pax_global_header00006660000000000000000000000064150071574520014520gustar00rootroot0000000000000052 comment=be84c79b45ccc266bf2aa08b05bce701252fd42e ga-5.9.2/000077500000000000000000000000001500715745200121245ustar00rootroot00000000000000ga-5.9.2/.github/000077500000000000000000000000001500715745200134645ustar00rootroot00000000000000ga-5.9.2/.github/workflows/000077500000000000000000000000001500715745200155215ustar00rootroot00000000000000ga-5.9.2/.github/workflows/github_actions.yml000066400000000000000000000175351500715745200212610ustar00rootroot00000000000000name: GlobalArrays_CI on: push: pull_request: release: schedule: - cron: '0 0 * * SUN' repository_dispatch: types: [backend_automation] workflow_dispatch: jobs: build: if: "!contains(github.event.head_commit.message, 'ci skip')" runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest] experimental: [false] mpi_impl: - openmpi - mpich armci_network: - mpi3 - mpi-ts - ofi - armci - mpi-pr f77: - gfortran-12 cc: - clang - gcc include: - os: ubuntu-latest experimental: true mpi_impl: mpich armci_network: mpi-ts f77: gfortran-11 cc: gcc-11 - os: ubuntu-22.04 experimental: true mpi_impl: mpich armci_network: mpi-pr f77: gfortran-10 cc: gcc-10 use_cmake: "Y" - os: ubuntu-latest experimental: true mpi_impl: intel armci_network: sockets config_opts: --enable-i4 f77: ifort cc: icx oneapi: /opt/intel/oneapi - os: ubuntu-latest experimental: true mpi_impl: openmpi armci_network: mpi-ts config_opts: --enable-i4 --without-blas --enable-cxx --disable-f77 f77: gfortran cc: gcc - os: macos-13 experimental: true mpi_impl: mpich armci_network: mpi-pr f77: ifort cc: icc config_opts: LIBS=-lifcore oneapi: /Users/runner/apps/oneapi - os: ubuntu-22.04 experimental: true mpi_impl: mpich armci_network: mpi-pr f77: gfortran cc: gcc use_sicm: "Y" - os: ubuntu-22.04 experimental: true mpi_impl: mpich armci_network: mpi-ts config_opts: "--disable-f77 --enable-cxx" f77: gfortran-10 cc: gcc-10 - os: macos-13 experimental: true mpi_impl: mpich armci_network: mpi-ts config_opts: "--disable-static --enable-shared" f77: gfortran-13 cc: clang - os: macos-13 experimental: true use_cmake: "Y" mpi_impl: mpich armci_network: mpi-pr f77: gfortran-13 cc: clang - os: macos-latest experimental: true use_cmake: "N" mpi_impl: mpich armci_network: mpi-pr f77: gfortran-14 cc: gcc-14 - os: ubuntu-22.04 experimental: true use_cmake: "Y" mpi_impl: openmpi armci_network: mpi-pr f77: gfortran cc: gcc - os: ubuntu-22.04 experimental: true use_cmake: "Y" mpi_impl: openmpi armci_network: mpi-ts f77: gfortran cc: gcc - os: ubuntu-22.04 experimental: true mpi_impl: intel armci_network: mpi-ts f77: ifort cc: gcc oneapi: /opt/intel/oneapi exclude: - armci_network: ofi os: macos-latest - armci_network: mpi-pr mpi_impl: openmpi - armci_network: mpi3 mpi_impl: openmpi - armci_network: ofi mpi_impl: openmpi fail-fast: false env: MPI_IMPL: ${{ matrix.mpi_impl }} PORT: ${{ matrix.armci_network }} ARMCI_NETWORK: ${{ matrix.armci_network }} F77: ${{ matrix.f77 }} CC: ${{ matrix.cc }} CONFIG_OPTS: ${{ matrix.config_opts}} USE_CMAKE: ${{ matrix.use_cmake }} USE_SICM: ${{ matrix.use_sicm }} continue-on-error: ${{ matrix.experimental }} steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 40 - name: Cache install steps id: ga-cache-install uses: actions/cache@v4 env: cache-name: cache-install-steps with: # cache files are stored in `~/apps` on Linux/macOS path: | apps key: ${{ runner.os }}-${{ matrix.mpi_impl}}-${{ matrix.armci_network}}-${{ matrix.cc }}-${{ matrix.f77 }}-${{ matrix.config_opts}}-ga-apps-v006 - name: install_packages run: | case "${{ matrix.os }}" in ubuntu*|jessie|stretch|buster) sudo apt-get update -q -y echo F77 is "$F77" if [[ "$F77" =~ gfortran-[0-9][0-9] ]] || [[ "$CC" =~ gcc-[0-9][0-9] ]]; then if [[ "$CC" =~ gcc-[0-9][0-9] ]]; then version=$(echo "$CC" | cut -d - -f 2 ) fi if [[ "$F77" =~ gfortran-[0-9][0-9] ]]; then version=$(echo "$F77" | cut -d - -f 2 ) fi sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test && sudo apt-get -y install gcc-$version gfortran-$version g++-$version fi sudo apt-get install -q -y gfortran ;; macos*) brew update brew install coreutils automake || true ;; esac if [[ "$F77" == "ifort" ]] || [[ "$CC" == "icc" ]] || [[ "$CC" == "icx" ]]; then ./travis/install-intel.sh; source ${{ matrix.oneapi }}/setvars.sh --force; fi echo F77 is `which "$F77"` echo F77 compiler version `"$F77" -v` - name: before_install if: steps.ga-cache-install.outputs.cache-hit != 'true' run: | pwd ls -lart mkdir -p $GITHUB_WORKSPACE/apps AUTO_CC="$CC" if [[ "$F77" == "ifort" ]] || [[ "$CC" == "icc" ]] || [[ "$CC" == "icx" ]]; then source ${{ matrix.oneapi }}/setvars.sh --force; AUTO_CC=cc; fi CC="$AUTO_CC" sh ./travis/install-autotools.sh $GITHUB_WORKSPACE/apps - name: install run: | export CI_ROOT=$GITHUB_WORKSPACE/apps if [[ "$F77" == "ifort" ]] || [[ "$CC" == "icc" ]] || [[ "$CC" == "icx" ]]; then source ${{ matrix.oneapi }}/setvars.sh --force; fi ./travis/install-mpi.sh $CI_ROOT $MPI_IMPL if [[ "$PORT" == "ofi" ]]; then ./travis/install-libfabric.sh $CI_ROOT; else true; fi if [[ "$PORT" == "armci" ]]; then ./travis/install-armci-mpi.sh $CI_ROOT; else true; fi if [[ "$USE_SICM" == "Y" ]]; then ./travis/install-sicm.sh $HOME/no_cache; else true; fi - name: compile and test global arrays if: ${{ success() }} run: | if [[ "$F77" == "ifort" ]] || [[ "$CC" == "icc" ]] || [[ "$CC" == "icx" ]] ; then source ${{ matrix.oneapi }}/setvars.sh --force; fi ./travis/build-run.sh $GITHUB_WORKSPACE/apps $PORT $MPI_IMPL $USE_CMAKE $F77 - name: after_failure if: ${{ failure() }} run: | pwd ls echo '***** cat config.log ****' find . -name config.log -exec cat {} ";" || true echo '***** cat global.fh *****' find . -name "global.fh" -exec cat {} ";" || true echo '***** global.fh* location *****' find ../.. -name "global.fh*" || true echo '***** cat global.fh.in *****' find ../.. -name "global.fh.in" -exec cat {} ";" || true cat ./test-suite.log || true echo '***** CMakeOutput.log *****' find . -name CMakeOutput.log -exec cat {} ";" || true echo '***** CMakeError.log ******' find . -name CMakeError.log -exec cat {} ";" || true echo '***********' cat /Users/runner/work/ga/ga/build/CMakeFiles/CMakeOutput.log || true echo '***********' cat /Users/runner/work/ga/ga/build/CMakeFiles/CMakeError.log || true ga-5.9.2/.gitignore000066400000000000000000000011601500715745200141120ustar00rootroot00000000000000# Compiled Object files *.slo *.lo *.o *.obj # Compiled Dynamic libraries *.so *.dylib *.dll # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app .cproject .project .settings Makefile.in aclocal.m4 autom4te.cache configure armci/config.h.in comex/config.h.in config.h.in log.00* org.eclipse.cdt.core.prefs org.eclipse.cdt.ui.prefs .autotools .externalToolBuilders/org.eclipse.cdt.autotools.core.genmakebuilderV2.launch compile config.guess config.sub depcomp install-sh missing lt~obsolete.m4 ltversion.m4 ltsugar.m4 ltoptions.m4 ltmain.sh libtool.m4 config.h.in~ *~ autotools bld* build-aux ga-5.9.2/.travis.yml000066400000000000000000000027051500715745200142410ustar00rootroot00000000000000language: c os: - linux - osx compiler: - clang - gcc osx_image: xcode10.2 addons: apt: packages: - gfortran homebrew: packages: - gcc - coreutils update: true cache: directories: - $HOME/travis jobs: allow_failures: - env: MPI_IMPL=openmpi env: # - MPI_IMPL=mpich # PORT=sockets # - MPI_IMPL=mpich # PORT=mpi-ts # - MPI_IMPL=mpich # PORT=mpi-mt # - MPI_IMPL=mpich # PORT=mpi-pt - MPI_IMPL=mpich PORT=mpi-pr - MPI_IMPL=mpich PORT=mpi3 - MPI_IMPL=openmpi PORT=mpi-ts - MPI_IMPL=mpich PORT=mpi-ts CONFIG_OPTS="--disable-f77 --enable-cxx" - MPI_IMPL=mpich PORT=mpi-ts CONFIG_OPTS="--disable-static --enable-shared" - MPI_IMPL=mpich PORT=ofi - MPI_IMPL=mpich PORT=armci - MPI_IMPL=openmpi PORT=armci before_install: - export TRAVIS_ROOT=$HOME/travis - mkdir -p $TRAVIS_ROOT - sh ./travis/install-autotools.sh $TRAVIS_ROOT install: - ./travis/install-mpi.sh $TRAVIS_ROOT $MPI_IMPL - if [[ "$PORT" == "ofi" ]]; then sh ./travis/install-libfabric.sh $TRAVIS_ROOT; fi - if [[ "$PORT" == "armci" ]]; then ./travis/install-armci-mpi.sh $TRAVIS_ROOT; fi - if [[ ( "$PORT" == "mpi-pr" ) && ( "$TRAVIS_OS_NAME" == "linux" ) ]]; then ./travis/install-sicm.sh $HOME/no_cache; fi script: - sh ./travis/build-run.sh $TRAVIS_ROOT $PORT $MPI_IMPL after_failure: - find . -name config.log -exec cat {} ";" - cat ./test-suite.log ga-5.9.2/CHANGELOG.md000066400000000000000000000707761500715745200137560ustar00rootroot00000000000000# Change Log The format is based on [Keep a Changelog](http://keepachangelog.com/). All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). This project follows the [Gitflow Workflow model](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow). ## [Unreleased] The Unreleased section will be empty for tagged releases. Unreleased functionality appears in the develop branch. ## [5.9.1] - Changed - Updated versions of blaspp, lapackpp - Fixed - Fixed a bug in the fortran interface for the nga_rand function - Removed minor memory leak from EAF_Delete function ## [5.9.1] - Changed - Updated OS's for continuous integration - Converted ARMCI_Error to use const char* instead of char* - Fixed - Data type mismatch in EAF fixed - Fixed group sync in sketch algorithm - Fixed some mismatches between long and int64_t data types - Added build system fixes for gcc version 14 ## [5.9] - Known Bugs - The MPI RMA port still does poorly with older releases of MPI. Currently, OpenMPI 5.0.x and MPICH 4.2.x appear to do well. - Added - GA can be initialized from an MPI communicator. This enables instances of GA to be created from non-GA programs and run independently on a subset of processors - An option to use System V memory instead of POSIX shared memory was added to the MPI progress ranks runtime. This option was added to get around a memory limitation with POSIX shared memory only allows a maximum of about half the available memory to used as shared memory. System V memory does not have this limit, but this implementation does not appear to be reliable and should be avoided if large memory usage is not needed - Substantial support for sparse 2D arrays. This includes operations such as matrix-vector and matrix-matrix multiplies, sparse-dense conversion and operations on the diagonals - Changed - Updated compiler settings in CMake build if Fujitsu compilers are detected. Other general improvements have also been made in CMake build. - Removed all instances of ga_malloc from GA source code. This frees GA from needing to initialize MA before using GA library calls. - Modified progress ranks runtime to use hostnames instead of host IDs to identify ranks on same SMP node. This is a more robust implementation. - Fixed - Fixed gcc toolchain checks in CMake for clang build - Fixed tiled arrays so that they work with restricted arrays and fixed some additional bugs in block cyclic distributions - Removed several memory leaks - Older TCGMSG implementations removed from build and documentation. TCGMSG-MPI is still retained - A substantial amount of code and documentation for discontinued or deprecated systems was removed. Examples include Blue Gene P/L/Q, LAPI, PVM, CCA, VIA, Quadrics, Elan-3/4, Myrinet GM, Mellanox, Hitachi, DEC, etc. - Number of outstanding non-blocking messages in MPI RMA runtime modified so that non-blocking test passes ## [5.8.2] - Known Bugs - The MPI RMA port still shows spotty behavior and many tests in the test suite are failing for many MPI implementations. Currently, the Open MPI implementation in version 4.1.4 is working well and all tests are passing. - Added - Setting ARMCI_VERBOSE=1 at runtime will also dump configuration details for ComEx runtime - Changed - Updated compiler settings in CMake build if Fujitsu compilers are detected - Fixed - Fixed gcc toolchain checks in CMake for clang build - Fixed tiled arrays so that they work with restricted arrays and fixed some additional bugs in block cyclic distributions - Removed several memory leaks - Modified check on the number of processors that was being performed in the GA create process. Previously this check was failing since it was possible that the check was being performed before a process group had been assigned to global array. - Fixed some issues with hidden string length argument in fortran interface ## [5.8.1] - Known Bugs - Added - Added support in MA for CUDA managed memory. Provided by Jeff Hammond. - Added a GA_Deallocate function that deallocates memory but leaves GA in place. GA_Allocate can be called later on the handle. This can be used for memory management. - Changed - Fixed - Slurm conflict for free_buf symbol in DRA library. Fixed by Michael Klemm. - Deallocate GA_MPI_World_comm_dup in GA_Terminate. - Dependency of CMake build on C++ is configurable. - Improved CMake integration of linear algebra libraries ## [5.8] - 2020-09-30 - Known Bugs - The MPI RMA port remains unreliable for many MPI implementations. Open MPI still reports many failures in the test suit. Intel MPI is better but still reports several failures. It is recommended to use the latest MPI implementations available. - Added - Version function that can be used to report the current version, subversion and patch numbers of the current release - Overlay option for creating new GAs on top of existing GAs - The number of progress ranks per node in the progress ranks runtime is now configurable - Functions for duplicating process groups and returning a process group that only contains the calling process - 64-bit versions of block-cyclic data distribution functions to C interface - Non-blocking test function - Read-only property based on caching - GA name can be recovered from handle - Added profiling capabilities to the GA branch that automatically generates a log file in the running directory. This can be controlled with GAW_FILE_PREFIX environment variable to add a prefix for the log files and the GAW_FMT environment variable to create a CSV format or human readable format. The default format is human readable. - For autotools, add --enable-profile=1 in the configure line - For CMake add -DENABLE_PROFILING=ON - Changed - Non-blocking handle management was completely revamped. This simplifies implementation and removes some bugs. The number of outstanding non-blocking calls was increased to 256 - Modified internal function that computes rank of processors on the world communicator so that it does not use the MPI_Comm_translate_ranks function. This function is implemented with a loop that scales as the square of the number of processors and is very slow at large processor counts - modified internal iterators so that block cyclic data distributions work on processor groups - Improved CMake build - Modified ga_print_distribution so that it works on block-cyclic data distributions - Fixed - Fixed a non-blocking error that was showing up in nbtest.x ## [5.7] - 2018-03-30 - Known Bugs - Some combinations of MPI implementations with the MPI RMA and PR ports fail. Recommended to use latest MPI implementations available. - Added - Tiled data layout - Read-only property type using replication across SMP nodes - Changed - GA is now thread safe - MPI3 implementation based on MPI RMA now uses data types in MPI calls by default. This is higher performing but not as reliable as using multiple contiguous data transfers. The build can be configured to use contiguous transfers if data types are not working for your MPI implementation. - ComEx MPI-PR now uses MPI data types in strided put and get calls by default. To enable the old packed behavior, set the following environment variables to 0. - COMEX_ENABLE_PUT_DATATYPE - COMEX_ENABLE_GET_DATATYPE Additionally, the original packing implementation is faster for smaller messages. Two new environment variables control at which point the MPI data types are used. - COMEX_PUT_DATATYPE_THRESHOLD. Default 8192. - COMEX_GET_DATATYPE_THRESHOLD. Default 8192. - Fixed - Message sizes exceeding 2GB now work correctly - Mirrored Arrays now distributes data across SMP nodes for ComEx-based runtimes - Matrix multiply works for non-standard data layouts (may not be performant) - Closed Issues - \[#48] Message sizes exceeding 2GB may not work correctly ## [5.6.5] - 2018-03-29 - Known Bugs - [\#48] Message sizes exceeding 2GB may not work correctly - Added - Environment variables to control internal ComEx MPI-PR settings - COMEX_MAX_NB_OUTSTANDING. Default 8. The maximum number of concurrent non-blocking operations. - COMEX_STATIC_BUFFER_SIZE. Default 2097152 bytes. Some ComEx operations require a temporary buffer. Any message larger than this size will dynamically allocate and free a new buffer to hold the larger message. - COMEX_EAGER_THRESHOLD. Default -1. Small messages can be sent as part of other internal ComEx operations. Recommended to set this to less than or equal to the corresponding MPI eager/rendezvous threshold cutoff. - COMEX_ENABLE_PUT_SELF. Default 1 (on). Contiguous put will use memcpy when target is same as originator. - COMEX_ENABLE_GET_SELF. Default 1 (on). Contiguous get will use memcpy when target is same as originator. - COMEX_ENABLE_ACC_SELF. Default 1 (on). Contiguous acc will use memcpy when target is same as originator. - COMEX_ENABLE_PUT_SMP. Default 1 (on). Contiguous put will use memcpy when target is on the same host via shared memory. - COMEX_ENABLE_GET_SMP. Default 1 (on). Contiguous get will use memcpy when target is on the same host via shared memory. - COMEX_ENABLE_ACC_SMP. Default 1 (on). Contiguous acc will use memcpy when target is on the same host via shared memory. - COMEX_ENABLE_PUT_PACKED. Default 1 (on). Strided put will pack the data into a contiguous buffer. - COMEX_ENABLE_GET_PACKED. Default 1 (on). Strided get will pack the data into a contiguous buffer. - COMEX_ENABLE_ACC_PACKED. Default 1 (on). Strided acc will pack the data into a contiguous buffer. - COMEX_ENABLE_PUT_IOV. Default 1 (on). Vector put will pack the data into a contiguous buffer. - COMEX_ENABLE_GET_IOV. Default 1 (on). Vector get will pack the data into a contiguous buffer. - COMEX_ENABLE_ACC_IOV. Default 1 (on). Vector acc will pack the data into a contiguous buffer. - COMEX_MAX_MESSAGE_SIZE. Default INT_MAX. All use of MPI will keep buffers less than this size. Sometimes useful in conjunction with eager thresholds to force all use of MPI below the eager threshold. - armci-config and comex-config added - --blas_size - --use_blas - --network_ldflags - --network_libs - ga-config added - --blas_size - --scalapack_size - --use_blas - --use_lapack - --use_scalapack - --use_peigs - --use_elpa - --use_elpa_2015 - --use_elpa_2016 - --network_ldflags - --network_libs - Changed - Removed case statement from install-autotools.sh - Fixed - install-autotools.sh works on FreeBSD - patch locally built m4 for OSX High Sierra - Closed Issues Requests - Scalapack with 8-byte integers? [\#93] - Please clarify what is "peigs" library [\#96] - additional arguments for bin/ga-config describing the presence of Peigs and/or Scalapack interfaces [\#99] - additional arguments for bin/ga-config describing the integer size of the Blas library used [\#100] ## [5.6.4] - 2018-03-21 - Known Bugs - [\#48] Message sizes exceeding 2GB may not work correctly - Added - armci-config and comex-config scripts to install. - Changed - install-autotools.sh installs all autotools regardless of existing versions - configure tests needing mixed C/Fortran code now use C linker - Fixed - Test suite was broken when GA was cross-compiled - eliop FreeBSD patch from Debichem - Locally installed automake is patched to work with newer perl versions - MPI-PR increased limit on number of possible comex_malloc invocations - Closed Pull Requests - \[#92] eliop FreeBSD patch from Debian maintainers of the NWChem Package - Closed Issues Requests - \[#82] Fortran failure on theta - \[#88] Automake regex expression broken for Perl versions >=5.26.0 - \[#89] autogen fails on Mac 10.12 - \[#90] configure script fails when using clang-4/5 + gfortran 6.3 compilers on Linux - \[#95] comex/src-mpi-pr/comex.c:996: _generate_shm_name: Assertion 'snprintf_retval < (int)31' failed ## [5.6.3] - 2017-12-08 - Known Bugs - [\#48] Message sizes exceeding 2GB may not work correctly - Fixed - Critical bug, incorrect use of MPI_Comm_split() might prevent startup in the following ComEx ports. - MPI-PR - MPI-PT - MPI-MT ## [5.6.2] - 2017-09-29 - Known Bugs - [\#48] Message sizes exceeding 2GB may not work correctly - Fixed - Bug in MPI-PT comex_malloc(). - Revert ARMCI contiguous check due to regression. - ELPA updates. - ScaLAPACK updates, including case for large matrices. - ComEx OFI updates from Intel. - Improved configure tests for LAPACK. - Improved travis tests. - Closed Pull Requests - [\#87] fix for case for large matrices when nprocs0/(2**I) is always larger than 1 ## [5.6.1] - 2017-05-30 - Known Bugs - [\#48] Message sizes exceeding 2GB may not work correctly - Added - New ELPA 2015 and 2016 eigensolver interfaces - Changed - autogen.sh unconditionally runs install-autotools.sh - install-autotools.sh downloads latest config.guess and config.sub - Additional LAPACK symbols are now tested for during configure - Fixed - comex_fence_proc() fixed for MPI-MT, MPI-PT, MPI-PR ports - configure --disable-fortran now works again - ComEx openib port was missing comex_nbacc symbol - Added $(BLAS_LIBS) to libcomex LIBADD to capture BLAS library dependency - EISPACK no longer enabled by default; --enable-eispack now works correctly - Shared memory name limit on OSX is now followed - comex_unlock() race condition - install-autotools.sh properly updates $PATH during build - install-autotools.sh alternate download location when FTP is blocked - patches to generated configure scripts for -lnuma - CMake build did not install some fortran headers - TravisCI: don't fail when brew install is a no-op - Closed Pull Requests - [\#34] Fix installation of autotools if not present - [\#53] new ELPA 2016 eigensolver 2stage interface - [\#54] new ELPA 2016 eigensolver interface for the Hotfix/5.6.1 branch - [\#55] curl for download when wget not installed - [\#58] comex/ofi: max_bytes_in_atomic may not fit in int - Closed Issues - [\#1] Incorporating GAMESS Patch - [\#5] Compiler error with --with-ofi - [\#9] Adding documentation for GA compilation on Windows - [\#25] CMake not building MA fortran wrappers - [\#30] Disable Fortran not working - [\#33] GA 5.6 release - autotools are downloaded and built even when latest versions exist - [\#38] #ifdef ENABLE_EISPACK should be #if ENABLE_EISPACK - [\#39] libcomex missing optional BLAS dependency - [\#41] develop branch and m4 version on cascade - [\#44] Comex OpenIB missing library symbol - [\#49] autogen.sh fails when only automake needs to be built - [\#50] install-autotools.sh on osx might choke if no timeout tool - [\#56] comex_fence_proc() is no-op in MT, PT, PR - [\#57] process groups sometimes fail for MPI-PT port ## [5.6] - 2017-04-04 - Added - Port for MPI-3 one-sided (--with-mpi3). - CMake build. - More complete test coverage. - Changed - Initial shared library versioning. - Fixed - Updates to ComEx/OFI provided by Intel. - ComEx/MPI-PR added uid and pid to shmem name. - Closed Pull Requests - [\#6] Comex/OFI: updated initialization of OmniPath provider - [\#10] comex/ofi: fixed EP initialization - [\#11] COMEX/OFI: added readme file for comex/ofi provider ## 5.5 - 2016-08 - Added - Port for libfabric (--with-ofi) via ComEx. This adds native support for Intel Omnipath. - Fixed - Numerous bug fixes. ## 5.4 - 2016-04 - Fixed - Numerous bug fixes. - Performed license/copyright audit of source files. - Removed - BGML and DCMF ports. Use MPI multithreading instead. ## 5.4b - 2015-05 - Added - Port for MPI progress ranks (--with-mpi-pr) via ComEx. - Port for MPI multithreading with progress threads (--with-mpi-pt) via ComEx. - Port for MPI multithreading (--with-mpi-mt) via ComEx. - Changed - Default network interface from SOCKETS to MPI two-sided (--with-mpi-ts) via ComEx. - Improved - ScaLAPACK and ELPA integration. - Removed - Replaced EISPACK with LAPACK/BLAS. ## 5.3 - 2014-04 - Fixed - Bug where incorrect BLAS integer size was used in ComEx. - Incompatibilities between this and the 5.2 release with respect to NWChem. - Testing - Validated this software with the NWChem 6.3 sources. ## 5.3b - 2013-12 - Added - Port for Portals4 (configure --with-portals4). When linking to the Portals4 reference implementation, it is highly recommended that the ummunotify driver is installed. Otherwise, memory registration errors and/or dropped messages may occur. This behavior can be verified using the PTL_DEBUG=1 and PTL_LOG_LEVEL=2 Portals4 environment variables. - ARMCI profiling to ComEx. - Changed - autotool scripts now latest versions. ## 5.2 - 2012-08 - Added - The Communications Runtime for Extreme Scale (ComEx) software to the GA release bundle. ComEx provides the following features to GA: - Port for Cray XE and XC (configure --with-dmapp). - Port for MPI-1 two-sided (configure --with-mpi-ts). - Support for externally linkable ARMCI (configure --with-armci=...) - Ability for users to select a different IB device using the ARMCI_OPENIB_DEVICE environment variable. - Ability for users to specify upper bound on ARMCI shared memory. Added ARMCI_DEFAULT_SHMMAX_UBOUND which is set and parsed at configure time. - Changed - How users link their applications. You now need "-lga -larmci" since libarmci is possibly an external dependency (see above). - Fixed - Support for Intel/QLogic IB thanks to patches from Dean Luick, Intel. - Improved - BLAS library parsing for ACML and MKL ('-mkl'). - ScaLAPACK integration thanks to Edo Apra. ## 5.1.1 - 2012-10 - Added - A wrapper for fsync to SF library. - MA_ACCESS_INDEX_TYPE to ma library. - Missing Python C sources. - Changed - Atomic operations. - Fixed - Numerous bugs for compilation on IBM AIX, as well as IBM xl compilers. - Many warnings reported by Intel compilers. - Integer overflow for indexing large arrays during accumulate. - Bug in GA_Zgemm64. - Ghosts hanging. - Removed - A few debugging print statements from pario. ## 5.1 - 2012-02 - Added - Unified "NGA" prefix for all functions. - New profiling interface and weak symbol interposition support. - Support for struct data types using the new NGA_Register_type(), NGA_Get_field() and NGA_Put_field() functions. - ga-config for 3rd party software to query compilation flags used for GA. - Global Arrays in NumPy (GAiN) interface for a NumPy work-alike with a Global Arrays backend. - GA_MPI_Comm() and other functions to retrieve MPI_Comm object associated with a GA processor group. - MPI-MT (MPI_THREAD_MULTIPLE) port for use when a native port is not available. - armci_msg_finalize() to abstract the message passing function required for application termination. - Ability for EAF_Open() to use MA memory operations instead of I/O operations. - Changed - ARMCI directory structure. - NGA_Add_patch() algorithm to use less memory. - tascel to no longer be part of the top-level configure (must be installed separately). - Python base module from "ga" to "ga4py" since we now have the submodules ga4py.ga and ga4py.gain. - autotools build to use autoconf-2.68 and libtool-2.4.2. - ARMCI Fortran sources to use `integer*4` type rather than an integer size compiler flag. - Fixed - Numerous configure and source bugs with our ScaLAPACK integration. - Bug in NGA_Matmul_patch(). - Numerous configure bugs. - Numerous Makefile bugs. - Support for large files. - Improved - Internal code, reducing the amount of dereferenced pointers. - Restriction on calling GA_Initalize() before messaging layer -- GA_Initalize(), ARMCI_Init(), MPI_Init()/tcg_pbegin() can be called in any order. - Removed - Deprecated interconnects Myrinet GM, VIA, Mellanox Verbs API, Quadrics Elan-3, Quadrics Elan-4. - Vampir support. - KSR and PARAGON support. ## 5.0.3 - 2012-02 - Added - Support for Cray compilers. - Fixed - Shared library linking. - A few *critical* bugs in GA_Duplicate(). - Bugs in strided get/put/acc routines. - Bugs in GPC support. - Numerous compilation warnings. - Numerous valgrind warnings. - Numerous configure bugs. - Numerous Makefile bugs. - Numerous bugs in Python interface. - Bug in GA_Patch_enum(). - Bug in TCGMSG-MPI nxtval(). - Latency reporting in perf.x benchmark. - Fortran ordering bug in NGA_Scatter(), NGA_Scatter_acc(), and NGA_Gather(). - Improved - BGP configure. - TCGMSG-MPI build. - Test suite. - Numerous inefficiencies in Python interface. ## 5.0.2 - 2011-03 - Added - Added support for Sun Studio compilers. - Added support for AMD Open64 compilers. - Changed - ARMCI RMW interface now uses void pointer. - GA_Patch_enum() now uses void pointer. - Fixed - Bugs in processor groups code. - Numerous compilation warnings. - Numerous configure bugs. - Numerous Makefile bugs. - Bug in GA_Unpack(). - Bug in GA_Dgemm() concerning transpose. - Numerous bugs in Python interface. - Improved - ga_scan_copy() and ga_scan_add(). ## 5.0.1 - 2011-01 - Fixed - Numerous configure bugs. - Numerous Makefile bugs. - Numerous bugs in test suite. - Atomics bug. - Numerous tascel bugs. - Bug in single complex matrix multiply. - Bug in destruction of mutexes. - Bug in process group collectives. - Bug in GA_Terminate(). - Improved - Configure for NEC and HPUX. ## 5.0 - 2010-11 - Now built using GNU autotools (autoconf,automake,libtool) - Restricted arrays (see user manual) - ARMCI runtime enhancements - On-demand connection management - Improved scalability for fence - New Python interface - Task Scheduling Library (tascel) ## 5.0b - 2010-07 - Now built using GNU autotools (autoconf,automake,libtool) ## 4.3 - 2010-05 - Optimized portals port to scale upto 200K procs - Optimized OpenIB port - BlueGene/P - Support for Sparse Data Operations - (See GA user manual - Chapter 11 for more details) ## 4.2 - 2009-07 - Support for several new platforms - Optimized portals port for Cray XT5 - BlueGene/P - Optimized OpenIB port - Support for Sparse Data Operations - (See GA user manual - Chapter 11 for more details) ## 4.1 - 2008-05 - Support for several new platforms - Cray XT4 - BlueGene/L, BlueGene/P - OpenIB network - Optimized one-sided non-blocking operations - New networks. i.e. ARMCI_NETWORK - OPENIB - PORTALS - MPI-SPAWN (one-sided communication thru' MPI2 Dynamic Process management and Send/Recv) ## 4.0 - 2006-04 - Support for multilevel parallelism: processor group awareness - GA_Dgemm matrix multiplication based on SRUMMA - Support for large arrays (a terabyte Global Array now possible) - Optimized one-sided non-blocking operations - Supports various platforms (Crays, IBM SPs, SGI Altix, ...) and interconnects (Myrinet, Quadrics, Infiniband, ...) [Unreleased]: https://github.com/GlobalArrays/ga/compare/v5.7...develop [5.7]: https://github.com/GlobalArrays/ga/compare/v5.6.5...v5.7 [5.6.5]: https://github.com/GlobalArrays/ga/compare/v5.6.4...v5.6.5 [5.6.4]: https://github.com/GlobalArrays/ga/compare/v5.6.3...v5.6.4 [5.6.3]: https://github.com/GlobalArrays/ga/compare/v5.6.2...v5.6.3 [5.6.2]: https://github.com/GlobalArrays/ga/compare/v5.6.1...v5.6.2 [5.6.1]: https://github.com/GlobalArrays/ga/compare/v5.6...v5.6.1 [5.6]: https://github.com/GlobalArrays/ga/releases/tag/v5.6 [\#100]: https://github.com/GlobalArrays/ga/issues/100 [\#99]: https://github.com/GlobalArrays/ga/issues/99 [\#98]: https://github.com/GlobalArrays/ga/issues/98 [\#97]: https://github.com/GlobalArrays/ga/issues/97 [\#96]: https://github.com/GlobalArrays/ga/issues/96 [\#95]: https://github.com/GlobalArrays/ga/issues/95 [\#94]: https://github.com/GlobalArrays/ga/issues/94 [\#93]: https://github.com/GlobalArrays/ga/issues/93 [\#92]: https://github.com/GlobalArrays/ga/pull/92 [\#91]: https://github.com/GlobalArrays/ga/pull/91 [\#90]: https://github.com/GlobalArrays/ga/issues/90 [\#89]: https://github.com/GlobalArrays/ga/issues/89 [\#88]: https://github.com/GlobalArrays/ga/issues/88 [\#87]: https://github.com/GlobalArrays/ga/pull/87 [\#86]: https://github.com/GlobalArrays/ga/issues/86 [\#85]: https://github.com/GlobalArrays/ga/issues/85 [\#84]: https://github.com/GlobalArrays/ga/issues/84 [\#83]: https://github.com/GlobalArrays/ga/issues/83 [\#82]: https://github.com/GlobalArrays/ga/issues/82 [\#81]: https://github.com/GlobalArrays/ga/pull/81 [\#80]: https://github.com/GlobalArrays/ga/pull/80 [\#79]: https://github.com/GlobalArrays/ga/pull/79 [\#78]: https://github.com/GlobalArrays/ga/pull/78 [\#77]: https://github.com/GlobalArrays/ga/pull/77 [\#76]: https://github.com/GlobalArrays/ga/pull/76 [\#75]: https://github.com/GlobalArrays/ga/pull/75 [\#74]: https://github.com/GlobalArrays/ga/pull/74 [\#73]: https://github.com/GlobalArrays/ga/pull/73 [\#72]: https://github.com/GlobalArrays/ga/pull/72 [\#71]: https://github.com/GlobalArrays/ga/pull/71 [\#70]: https://github.com/GlobalArrays/ga/pull/70 [\#69]: https://github.com/GlobalArrays/ga/pull/69 [\#68]: https://github.com/GlobalArrays/ga/pull/68 [\#67]: https://github.com/GlobalArrays/ga/pull/67 [\#66]: https://github.com/GlobalArrays/ga/pull/66 [\#65]: https://github.com/GlobalArrays/ga/pull/65 [\#64]: https://github.com/GlobalArrays/ga/issues/64 [\#63]: https://github.com/GlobalArrays/ga/pull/63 [\#62]: https://github.com/GlobalArrays/ga/pull/62 [\#61]: https://github.com/GlobalArrays/ga/issues/61 [\#60]: https://github.com/GlobalArrays/ga/pull/60 [\#59]: https://github.com/GlobalArrays/ga/pull/59 [\#58]: https://github.com/GlobalArrays/ga/pull/58 [\#57]: https://github.com/GlobalArrays/ga/issues/57 [\#56]: https://github.com/GlobalArrays/ga/issues/56 [\#55]: https://github.com/GlobalArrays/ga/pull/55 [\#54]: https://github.com/GlobalArrays/ga/pull/54 [\#53]: https://github.com/GlobalArrays/ga/pull/53 [\#52]: https://github.com/GlobalArrays/ga/issues/52 [\#51]: https://github.com/GlobalArrays/ga/issues/51 [\#50]: https://github.com/GlobalArrays/ga/issues/50 [\#49]: https://github.com/GlobalArrays/ga/issues/49 [\#48]: https://github.com/GlobalArrays/ga/issues/48 [\#47]: https://github.com/GlobalArrays/ga/issues/47 [\#46]: https://github.com/GlobalArrays/ga/issues/46 [\#45]: https://github.com/GlobalArrays/ga/issues/45 [\#44]: https://github.com/GlobalArrays/ga/issues/44 [\#43]: https://github.com/GlobalArrays/ga/issues/43 [\#42]: https://github.com/GlobalArrays/ga/issues/42 [\#41]: https://github.com/GlobalArrays/ga/issues/41 [\#40]: https://github.com/GlobalArrays/ga/issues/40 [\#39]: https://github.com/GlobalArrays/ga/issues/39 [\#38]: https://github.com/GlobalArrays/ga/issues/38 [\#37]: https://github.com/GlobalArrays/ga/pull/37 [\#36]: https://github.com/GlobalArrays/ga/issues/36 [\#35]: https://github.com/GlobalArrays/ga/issues/35 [\#34]: https://github.com/GlobalArrays/ga/pull/34 [\#33]: https://github.com/GlobalArrays/ga/issues/33 [\#32]: https://github.com/GlobalArrays/ga/issues/32 [\#31]: https://github.com/GlobalArrays/ga/issues/31 [\#30]: https://github.com/GlobalArrays/ga/issues/30 [\#29]: https://github.com/GlobalArrays/ga/issues/29 [\#28]: https://github.com/GlobalArrays/ga/issues/28 [\#27]: https://github.com/GlobalArrays/ga/issues/27 [\#26]: https://github.com/GlobalArrays/ga/issues/26 [\#25]: https://github.com/GlobalArrays/ga/issues/25 [\#24]: https://github.com/GlobalArrays/ga/issues/24 [\#23]: https://github.com/GlobalArrays/ga/issues/23 [\#22]: https://github.com/GlobalArrays/ga/issues/22 [\#21]: https://github.com/GlobalArrays/ga/issues/21 [\#20]: https://github.com/GlobalArrays/ga/issues/20 [\#19]: https://github.com/GlobalArrays/ga/issues/19 [\#18]: https://github.com/GlobalArrays/ga/issues/18 [\#17]: https://github.com/GlobalArrays/ga/issues/17 [\#16]: https://github.com/GlobalArrays/ga/issues/16 [\#15]: https://github.com/GlobalArrays/ga/issues/15 [\#14]: https://github.com/GlobalArrays/ga/issues/14 [\#13]: https://github.com/GlobalArrays/ga/issues/13 [\#12]: https://github.com/GlobalArrays/ga/issues/12 [\#11]: https://github.com/GlobalArrays/ga/pull/11 [\#10]: https://github.com/GlobalArrays/ga/pull/10 [\#9]: https://github.com/GlobalArrays/ga/issues/9 [\#8]: https://github.com/GlobalArrays/ga/issues/8 [\#7]: https://github.com/GlobalArrays/ga/issues/7 [\#6]: https://github.com/GlobalArrays/ga/pull/6 [\#5]: https://github.com/GlobalArrays/ga/issues/5 [\#4]: https://github.com/GlobalArrays/ga/pull/4 [\#3]: https://github.com/GlobalArrays/ga/issues/3 [\#2]: https://github.com/GlobalArrays/ga/issues/2 [\#1]: https://github.com/GlobalArrays/ga/issues/1 ga-5.9.2/CMakeLists.txt000066400000000000000000000423471500715745200146760ustar00rootroot00000000000000# # module: CMakeLists.txt # author: Bruce Palmer # description: CMake build for GA. Only MPI-based runtimes are supported. # # DISCLAIMER # # This material was prepared as an account of work sponsored by an # agency of the United States Government. Neither the United States # Government nor the United States Department of Energy, nor Battelle, # nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR # ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, # COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, # SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT # INFRINGE PRIVATELY OWNED RIGHTS. # # # ACKNOWLEDGMENT # # This software and its documentation were produced with United States # Government support under Contract Number DE-AC06-76RLO-1830 awarded by # the United States Department of Energy. The United States Government # retains a paid-up non-exclusive, irrevocable worldwide license to # reproduce, prepare derivative works, perform publicly and display # publicly by or for the US Government, including the right to # distribute to other US Government contractors. # # -*- mode: cmake -*- # ------------------------------------------------------------- # file: CMakeLists.txt # ------------------------------------------------------------- cmake_minimum_required (VERSION 3.18.0 FATAL_ERROR) project (GlobalArrays VERSION 5.8.0 LANGUAGES C) # Append local CMake directory list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}/cmake") list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}/cmake/linalg-modules") message(STATUS "CMAKE_MODULE_PATH: ${CMAKE_MODULE_PATH}") include(ga-utils) ga_option(ENABLE_CXX ON) ga_option(ENABLE_FORTRAN ON) ga_option(ENABLE_COVERAGE OFF) ga_option(CMAKE_CXX_EXTENSIONS OFF) set(NOFORT 1) set(ENABLE_F77 0) if(ENABLE_CXX) enable_language(CXX) endif() if (ENABLE_FORTRAN) enable_language(Fortran) set(NOFORT 0) set(ENABLE_F77 1) endif() ga_option(CMAKE_BUILD_TYPE Release) ga_option(LINALG_VENDOR BLIS) ga_option(ENABLE_TESTS ON) ga_option(ENABLE_PROFILING OFF) ga_option(ENABLE_SYSV OFF) #Options for user provided LinAlg libraries ga_option(ENABLE_BLAS OFF) ga_option(ENABLE_SCALAPACK OFF) ga_option(ENABLE_EISPACK OFF) ga_option(ENABLE_DPCPP OFF) ga_option(ENABLE_DEV_MODE OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_NO_SYSTEM_FROM_IMPORTED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) # set(CMAKE_LINK_DEPENDS_NO_SHARED ON) include(${PROJECT_SOURCE_DIR}/cmake/ga-compiler-options.cmake) if(ENABLE_BLAS) if(NOT ENABLE_FORTRAN) message(FATAL_ERROR "ENABLE_BLAS=ON needs ENABLE_FORTRAN=ON") endif() endif() if(ENABLE_FORTRAN) if(MSVC) message(FATAL_ERROR "MSVC build needs ENABLE_FORTRAN=OFF") endif() find_program(ga_m4_cmd NAMES gm4 m4) if(NOT ga_m4_cmd) message(FATAL_ERROR "m4 command not found. GNU M4 is required.") endif() endif() if(ENABLE_PROFILING) if(CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang") message(FATAL_ERROR "GA profiling is currently not supported with Clang compilers. Please set ENABLE_PROFILING=OFF") endif() endif() # GA Runtime options: MPI runtime used to build GA ga_option(GA_RUNTIME MPI_2SIDED) if (GA_RUNTIME STREQUAL MPI_PROGRESS_RANK) set (MPI_PR TRUE CACHE BOOL "use MPI progress ranks protocol for communication") elseif (GA_RUNTIME STREQUAL MPI_RMA) set (MPI3 TRUE CACHE BOOL "use MPI RMA protocols for communication") elseif (GA_RUNTIME STREQUAL MPI_MULTITHREADED) set (MPI_MT TRUE CACHE BOOL "use MPI multi-threading protocols for communication") elseif (GA_RUNTIME STREQUAL MPI_PROGRESS_THREAD) set (MPI_PT TRUE CACHE BOOL "use MPI progress thread protocols for communication") elseif (GA_RUNTIME STREQUAL MPI_2SIDED) set (MPI_TS TRUE CACHE BOOL "use MPI 2-sided protocol for communication") else() message(STATUS "GA_RUNTIME provided \"${GA_RUNTIME}\" is not supported") endif() option (MSG_COMMS_MPI "Using MPI runtime for communication" ON) option (ENABLE_ARMCI_MEM_OPTION "User option for managing memory" ON) option (ENABLE_CUDA_MEM "User option for CUDA memory" OFF) option (F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS "Fortran/C interface property" ON) message(STATUS "Checking MPI ...") find_package (MPI REQUIRED) set(GA_EXTRA_LIBS ${GA_EXTRA_LIBS} MPI::MPI_C) if(ENABLE_COVERAGE) add_compile_options(--coverage) list(APPEND GA_EXTRA_LIBS --coverage) endif() if(ENABLE_FORTRAN) #macos fix for clang+gfortran build #linking MPI::MPI_Fortran adds the flag to clang compile line if (CMAKE_Fortran_COMPILER_ID MATCHES "GNU") get_target_property(_mpif_ico MPI::MPI_Fortran INTERFACE_COMPILE_OPTIONS) if(_mpif_ico) string(REPLACE "-fallow-argument-mismatch" "" _mpif_ico ${_mpif_ico}) separate_arguments(_mpif_ico) set_target_properties(MPI::MPI_Fortran PROPERTIES INTERFACE_COMPILE_OPTIONS "${_mpif_ico}") endif() endif() list(APPEND GA_EXTRA_LIBS MPI::MPI_Fortran) endif() include(${PROJECT_SOURCE_DIR}/cmake/ga-checks.cmake) if (ENABLE_FORTRAN) include( FortranCInterface ) FortranCInterface_HEADER(${CMAKE_CURRENT_BINARY_DIR}/f2c_cmake.ignore MACRO_NAMESPACE F77_FUNC_) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/f2c_cmake.h COMMAND ${CMAKE_COMMAND} -D INPUT:PATH="${CMAKE_CURRENT_BINARY_DIR}/f2c_cmake.ignore" -D OUTPUT:PATH="${CMAKE_CURRENT_BINARY_DIR}/f2c_cmake.h" -P ${PROJECT_SOURCE_DIR}/tools/config_fh_from_h.cmake DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/f2c_cmake.ignore ) add_custom_target( GenerateF2C_CH ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/f2c_cmake.h ) set_source_files_properties( ${CMAKE_CURRENT_BINARY_DIR}/f2c_cmake.h PROPERTIES GENERATED TRUE ) else() CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/f2c_dummy.h.in ${CMAKE_CURRENT_BINARY_DIR}/f2c_cmake.h ) endif() # set some flags for MS Visual C Compiler if (MSVC) add_definitions(/D NOMINMAX /D _CRT_SECURE_NO_WARNINGS /D _CRT_NONSTDC_NO_WARNINGS) endif() # hardwire various GA configuration parameters. Use convention that parameters # are defined and set to 0 if not used set (CYGWIN 0) if (CMAKE_SYSTEM_NAME STREQUAL Linux) set(LINUX 1) set(LINUX64 1) endif() set(USE_I8 OFF) if (CMAKE_SIZEOF_VOID_P EQUAL 8) set(USE_I8 ON) set(BLAS_SIZE 8) endif() option (ENABLE_I8 "Use 8 byte Fortran integers" ${USE_I8}) message(STATUS "void size: ${CMAKE_SIZEOF_VOID_P}, USE_I8: ${USE_I8}, ENABLE_I8: ${ENABLE_I8}") include(${PROJECT_SOURCE_DIR}/cmake/ga-linalg.cmake) # hardwire memcpy and strchr since these have been standard for years set (HAVE_MEMCPY 1) set (HAVE_STRCHR 1) # miscellaneous hardwired parameters (mostly not used) set (ENABLE_CHECKPOINT 0) set (GA_PROFILING 0) set (ENABLE_TRACE 0) set (STATS 1) set (USE_MALLOC 0) if(ENABLE_PROFILING) set (GA_PROFILING 1) endif() if(USE_CRAYSHASTA) set(__CRAYXE 1) # list(APPEND GA_EXTRA_LIBS pmi2) endif() # hardwire ARMCI configuration options set (HAVE_ARMCI_GROUP_COMM 1) set (HAVE_ARMCI_GROUP_COMM_MEMBER 0) set (HAVE_ARMCI_INITIALIZED 1) # suppress any checks to see if test codes run. Only check for compilation. # use for cross-compilation situations option (CHECK_COMPILATION_ONLY "Check compilation only" OFF) if (ENABLE_FORTRAN) message(STATUS "CMAKE_Fortran_COMPILER: ${CMAKE_Fortran_COMPILER}") message(STATUS "CMAKE_Fortran_COMPILER_ID: ${CMAKE_Fortran_COMPILER_ID}") set (F90_MODULE ) set (F77_GETARG GETARG) set (F77_GETARG_ARGS "i,s") set (F77_GETARG_DECLS "") set (F77_IARGC IARGC) set (F77_FLUSH flush) set (HAVE_F77_FLUSH 1) if (CMAKE_Fortran_COMPILER_ID MATCHES "GNU") message(STATUS "Using GNU Fortran compiler settings") set (F77_GETARG_DECLS "intrinsic GETARG") set (FORTRAN_I8_FLAG -fdefault-integer-8) set (FORTRAN_I4_FLAG "") elseif (CMAKE_Fortran_COMPILER_ID MATCHES "Flang") message(STATUS "Using Flang compiler settings for flang, flang-new, armflang") set (F77_GETARG_DECLS "external GETARG") set (FORTRAN_I8_FLAG -fdefault-integer-8) set (FORTRAN_I4_FLAG "") elseif (CMAKE_Fortran_COMPILER_ID MATCHES "Intel") message(STATUS "Using Intel Fortran compiler settings") set (F77_GETARG_DECLS "external GETARG") set (FORTRAN_I8_FLAG "-i8 -integer-size 64") set (FORTRAN_I4_FLAG "-i4 -integer-size 32") elseif (CMAKE_Fortran_COMPILER_ID MATCHES "PGI") message(STATUS "Using PGI Fortran compiler settings") set (FORTRAN_I8_FLAG -i8) set (FORTRAN_I4_FLAG -i4) elseif (CMAKE_Fortran_COMPILER_ID MATCHES "XL") message(STATUS "Using IBM XL Fortran compiler settings") set (FORTRAN_I8_FLAG -qintsize=8) set (FORTRAN_I4_FLAG -qintsize=4) elseif (CMAKE_Fortran_COMPILER_ID MATCHES "Fujitsu") message(STATUS "Fujitsu Fortran compiler settings") set (FORTRAN_I8_FLAG "-CcdII8 -CcdLL8") set (FORTRAN_I4_FLAG "") elseif (CMAKE_Fortran_COMPILER_ID MATCHES "Cray") message(STATUS "Cray Fortran compiler settings") set (FORTRAN_I8_FLAG "-default64") set (FORTRAN_I4_FLAG "") endif() else() # need to set these variable even if only compiling C/C++ set (F77_GETARG GETARG) set (F77_GETARG_ARGS "i,s") set (F77_GETARG_DECLS "external GETARG") set (F77_IARGC IARGC) set (FORTRAN_I8_FLAG -i8) set (FORTRAN_I4_FLAG -i4) endif() # check_fortran_compiler_flag("-fallow-argument-mismatch" _fam) # ga_is_valid(_fam _fam_flag_exists) # if(_fam_flag_exists) # set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -fallow-argument-mismatch") # endif() if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL "10.1") set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -fallow-argument-mismatch") endif() if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL "11.1") set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -fno-tree-slp-vectorize") endif() endif() if(CMAKE_C_COMPILER_ID STREQUAL "GNU") if(CMAKE_C_COMPILER_VERSION VERSION_GREATER "14.0") add_compile_options(-std=gnu17) endif() if(ENABLE_CXX) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_compile_options(-fno-aggressive-loop-optimizations) endif() else() add_compile_options(-fno-aggressive-loop-optimizations) endif() elseif (CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -fno-aggressive-loop-optimizations") endif() # Hardwire these settings. No way to check for Fortran integer size in CMake # (that we can find) set (CM_SIZEOF_F77_DOUBLE 8) set (CM_SIZEOF_F77_REAL 4) set (CM_SIZEOF_F77_INTEGER 4) set (F2C_INTEGER_C_TYPE int) set (F2C_REAL_C_TYPE float) set (F2C_DOUBLE_PRECISION_C_TYPE double) if (ENABLE_I8) set (CM_SIZEOF_F77_INTEGER 8) set (F2C_INTEGER_C_TYPE long) set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${FORTRAN_I8_FLAG}") message(STATUS "Fortran flags: ${CMAKE_Fortran_FLAGS}") endif() set (C_POINTER_AS_INTEGER int) if (CMAKE_SIZEOF_VOID_P EQUAL 8) set (C_POINTER_AS_INTEGER long) if (CMAKE_SYSTEM_NAME STREQUAL "Linux") set(NOUSE_MMAP 1) endif() set(GA_ACCESS_INDEX_TYPE integer*8) else() set(GA_ACCESS_INDEX_TYPE integer*4) endif() # set (CM_BLAS_SIZE ${CM_SIZEOF_F77_INTEGER}) # ------------------------------------------------------------- # Create include files from templates # ------------------------------------------------------------- CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h ) CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/farg.h.in ${CMAKE_CURRENT_BINARY_DIR}/gaf2c/farg.h ) CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/typesf2c.h.in ${CMAKE_CURRENT_BINARY_DIR}/gaf2c/typesf2c.h ) CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/matypes.h.in ${CMAKE_CURRENT_BINARY_DIR}/ma/matypes.h ) CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/mafdecls.fh.in ${CMAKE_CURRENT_BINARY_DIR}/ma/mafdecls.fh ) CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/global/src/global.fh.in ${CMAKE_CURRENT_BINARY_DIR}/global/src/global.fh ) if (ENABLE_FORTRAN) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/config.fh COMMAND ${CMAKE_COMMAND} -D INPUT:PATH="${CMAKE_CURRENT_BINARY_DIR}/config.h" -D OUTPUT:PATH="${CMAKE_CURRENT_BINARY_DIR}/config.fh" -P ${PROJECT_SOURCE_DIR}/tools/config_fh_from_h.cmake DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/config.h DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/config.h ) add_custom_target( GenerateConfigFH ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/config.fh ) set_source_files_properties( ${CMAKE_CURRENT_BINARY_DIR}/config.fh PROPERTIES GENERATED TRUE ) endif() include_directories(AFTER ${MPI_INCLUDE_PATH}) add_definitions (-DHAVE_CONFIG_H -DOMPI_SKIP_MPICXX) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) list(APPEND GA_EXTRA_LIBS ${CMAKE_THREAD_LIBS_INIT}) if(NOT ENABLE_FORTRAN OR BUILD_SHARED_LIBS) if(NOT MSVC) list(APPEND GA_EXTRA_LIBS m) endif() endif() if(NOT "${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Darwin") if(NOT MSVC) list(APPEND GA_EXTRA_LIBS rt) endif() endif() if(ENABLE_BLAS) list(APPEND GA_EXTRA_LIBS ${linalg_lib}) add_compile_options(${BLAS_COMPILE_OPTIONS}) add_compile_definitions(${BLAS_COMPILE_DEFINITIONS}) #include_directories(${BLAS_INCLUDE_DIRS}) endif() if(ENABLE_FORTRAN) set(_CHK_FORTRAN_COMPILERS "GNU" "Intel" "LLVMFlang" "Flang") list(FIND _CHK_FORTRAN_COMPILERS ${CMAKE_Fortran_COMPILER_ID} index) if(index GREATER -1) find_package( StandardFortran ) list(APPEND GA_EXTRA_LIBS ${STANDARDFORTRAN_LIBRARIES}) endif() endif() if(ENABLE_PROFILING) set(ga_profiler $) endif() if(ENABLE_BLAS) include_directories(LinAlg/lapack+blas) else() set(ga_ref_blas $) endif() add_library(ga $ $ $ $ $ $ $ $ ${ga_ref_blas} ${ga_profiler} ) add_library(GlobalArrays::ga ALIAS ga) set (CMAKE_REQUIRED_LIBRARIES lapack blas) target_link_libraries(ga PUBLIC ${GA_EXTRA_LIBS}) target_include_directories(ga INTERFACE $ $ ) if(ENABLE_BLAS) target_include_directories(ga INTERFACE $ ) target_compile_options(ga INTERFACE $ ) target_compile_definitions(ga INTERFACE $ ) endif() if(DEFINED GA_GCC_TOOLCHAIN_FLAG) target_compile_options(ga PUBLIC $<$:${GA_GCC_TOOLCHAIN_FLAG}>) endif() if(ENABLE_TESTS) # turn testing on enable_testing() endif() # ------------------------------------------------------------- # Subdirectories # ------------------------------------------------------------- add_subdirectory(global/src) add_subdirectory(comex) if(ENABLE_CXX) add_subdirectory(ga++) endif() add_subdirectory(ma) add_subdirectory(pario) add_subdirectory(gaf2c) if(NOT ENABLE_BLAS) add_subdirectory(LinAlg/lapack+blas) endif() if(ENABLE_PROFILING) add_subdirectory(tools) endif() if(ENABLE_TESTS) add_subdirectory(global/testing) add_subdirectory(global/examples) endif() foreach (bip ${GA_HEADER_PATHS}) target_include_directories(ga INTERFACE $ ) endforeach() # -------------------------------------- # Global Arrays installation # -------------------------------------- include(GNUInstallDirs) set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/GlobalArrays) #dra eaf elio sf install(TARGETS ma ga_src EXPORT globalarrays-targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) install(TARGETS ga EXPORT globalarrays-targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) #Export the targets to a script install(EXPORT globalarrays-targets FILE globalarrays-targets.cmake NAMESPACE GlobalArrays:: DESTINATION ${INSTALL_CONFIGDIR} ) #Create a ConfigVersion.cmake file include(CMakePackageConfigHelpers) write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/globalarrays-config-version.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY AnyNewerVersion ) configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/cmake/globalarrays-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/globalarrays-config.cmake INSTALL_DESTINATION ${INSTALL_CONFIGDIR} PATH_VARS CMAKE_INSTALL_INCLUDEDIR ) #Install the config, configversion files install(FILES ${CMAKE_CURRENT_BINARY_DIR}/globalarrays-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/globalarrays-config-version.cmake DESTINATION ${INSTALL_CONFIGDIR} ) if(ENABLE_BLAS) install_linalg_modules (INSTALL_CONFIGDIR) #FIXME: Not sure why this file is not installed via ExternalProject build of GA. install(FILES ${CMAKE_CURRENT_LIST_DIR}/cmake/linalg-modules/FindILP64.cmake ${CMAKE_CURRENT_LIST_DIR}/cmake/linalg-modules/FindStandardFortran.cmake DESTINATION ${INSTALL_CONFIGDIR}/linalg-cmake-modules ) endif() list(REMOVE_AT CMAKE_MODULE_PATH 0) list(REMOVE_AT CMAKE_MODULE_PATH 0) # Export build tree export(EXPORT globalarrays-targets NAMESPACE GlobalArrays:: FILE "${PROJECT_BINARY_DIR}/globalarrays-targets.cmake") ga-5.9.2/DISCLAIMER000066400000000000000000000024711500715745200134670ustar00rootroot00000000000000This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor Battelle, nor any of their employees, nor any jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any information, apparatus, product, software, or process disclosed, or represents that its use would not infringe privately owned rights. Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, or favoring by the United States Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or any agency thereof. PACIFIC NORTHWEST NATIONAL LABORATORY operated by BATTELLE for the UNITED STATES DEPARTMENT OF ENERGY under Contract DE-AC05-76RL01830 ga-5.9.2/LICENSE000066400000000000000000000006751500715745200131410ustar00rootroot00000000000000Copyright 2022 Battelle Memorial Institute. Contributed under the BSD-3-Clause License https://opensource.org/licenses/BSD-3-Clause By contributing computer software code to the GlobalArrays project I certify that the contribution is being made under the BSD-3-Clause license (https://opensource.org/licenses/BSD-3-Clause) and that I have all rights and permissions necessary to do so, including authorization from my employer if applicable. ga-5.9.2/LinAlg/000077500000000000000000000000001500715745200132725ustar00rootroot00000000000000ga-5.9.2/LinAlg/lapack+blas/000077500000000000000000000000001500715745200154425ustar00rootroot00000000000000ga-5.9.2/LinAlg/lapack+blas/CMakeLists.txt000066400000000000000000000065541500715745200202140ustar00rootroot00000000000000# # module: CMakeLists.txt # author: Bruce Palmer # description: implements a primative CMake build that can be used to build # GA on Windows-based systems. Only MPI-based runtimes are # supported. # # DISCLAIMER # # This material was prepared as an account of work sponsored by an # agency of the United States Government. Neither the United States # Government nor the United States Department of Energy, nor Battelle, # nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR # ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, # COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, # SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT # INFRINGE PRIVATELY OWNED RIGHTS. # # # ACKNOWLEDGMENT # # This software and its documentation were produced with United States # Government support under Contract Number DE-AC06-76RLO-1830 awarded by # the United States Department of Energy. The United States Government # retains a paid-up non-exclusive, irrevocable worldwide license to # reproduce, prepare derivative works, perform publicly and display # publicly by or for the US Government, including the right to # distribute to other US Government contractors. # # -*- mode: cmake -*- # ------------------------------------------------------------- # file: CMakeLists.txt # ------------------------------------------------------------- # ------------------------------------------------------------- # LinAlg header installation # ------------------------------------------------------------- set(LINALG_HEADERS xgemm.h ) # ------------------------------------------------------------- # LINALG library installation # ------------------------------------------------------------- if (ENABLE_FORTRAN) set(FORTRAN_FILES gal_cgemm.f gal_daxpy.f gal_dcabs1.f gal_dcopy.f gal_ddot.f gal_dgemm.f gal_dgemv.f gal_dger.f gal_dgetf2.f gal_dgetrf.f gal_dgetrs.f gal_disnan.f gal_dlacpy.f gal_dlae2.f gal_dlaev2.f gal_dlaisnan.f gal_dlamch.f gal_dlanst.f gal_dlansy.f gal_dlapy2.f gal_dlarfb.f gal_dlarf.f gal_dlarfg.f gal_dlarft.f gal_dlartg.f gal_dlascl.f gal_dlaset.f gal_dlasr.f gal_dlasrt.f gal_dlassq.f gal_dlaswp.f gal_dlatrd.f gal_dnrm2.f gal_dorg2l.f gal_dorg2r.f gal_dorgql.f gal_dorgqr.f gal_dorgtr.f gal_dpotf2.f gal_dpotrf.f gal_dscal.f gal_dsteqr.f gal_dsterf.f gal_dswap.f gal_dsyev.f gal_dsygs2.f gal_dsygst.f gal_dsygv.f gal_dsymm.f gal_dsymv.f gal_dsyr2.f gal_dsyr2k.f gal_dsyrk.f gal_dsytd2.f gal_dsytrd.f gal_dtrmm.f gal_dtrmv.f gal_dtrsm.f gal_dtrsv.f gal_idamax.f gal_ieeeck.f gal_iladlc.f gal_iladlr.f gal_ilaenv.f gal_iparmq.f gal_lsame.f gal_sgemm.f gal_xerbla.f gal_zaxpy.f gal_zgemm.f ) endif() add_library(linalg OBJECT xgemm.c ${FORTRAN_FILES} ) target_include_directories(linalg BEFORE PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_BINARY_DIR}) ga-5.9.2/LinAlg/lapack+blas/README000066400000000000000000000004001500715745200163140ustar00rootroot00000000000000This directory contains a subset of LAPACK and BLAS subroutines. They have been prefixed with "gal_" so as not to conflict with any external BLAS/LAPACK libraries. These sources are unconditionally compiled so long as a valid Fortran compiler is detected. ga-5.9.2/LinAlg/lapack+blas/dummy.c000066400000000000000000000003011500715745200167330ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* dummy routine to make linalg.a legitimate when we do not compile lapack or blas */ void __dummy_linalg(int *a, int *b) { if(a) *a=*b; } ga-5.9.2/LinAlg/lapack+blas/gal_cgemm.f000066400000000000000000000273211500715745200175310ustar00rootroot00000000000000*> \brief \b CGEMM * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE CGEMM(TRANSA,TRANSB,M,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) * * .. Scalar Arguments .. * COMPLEX ALPHA,BETA * INTEGER K,LDA,LDB,LDC,M,N * CHARACTER TRANSA,TRANSB * .. * .. Array Arguments .. * COMPLEX A(LDA,*),B(LDB,*),C(LDC,*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> CGEMM performs one of the matrix-matrix operations *> *> C := alpha*op( A )*op( B ) + beta*C, *> *> where op( X ) is one of *> *> op( X ) = X or op( X ) = X**T or op( X ) = X**H, *> *> alpha and beta are scalars, and A, B and C are matrices, with op( A ) *> an m by k matrix, op( B ) a k by n matrix and C an m by n matrix. *> \endverbatim * * Arguments: * ========== * *> \param[in] TRANSA *> \verbatim *> TRANSA is CHARACTER*1 *> On entry, TRANSA specifies the form of op( A ) to be used in *> the matrix multiplication as follows: *> *> TRANSA = 'N' or 'n', op( A ) = A. *> *> TRANSA = 'T' or 't', op( A ) = A**T. *> *> TRANSA = 'C' or 'c', op( A ) = A**H. *> \endverbatim *> *> \param[in] TRANSB *> \verbatim *> TRANSB is CHARACTER*1 *> On entry, TRANSB specifies the form of op( B ) to be used in *> the matrix multiplication as follows: *> *> TRANSB = 'N' or 'n', op( B ) = B. *> *> TRANSB = 'T' or 't', op( B ) = B**T. *> *> TRANSB = 'C' or 'c', op( B ) = B**H. *> \endverbatim *> *> \param[in] M *> \verbatim *> M is INTEGER *> On entry, M specifies the number of rows of the matrix *> op( A ) and of the matrix C. M must be at least zero. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the number of columns of the matrix *> op( B ) and the number of columns of the matrix C. N must be *> at least zero. *> \endverbatim *> *> \param[in] K *> \verbatim *> K is INTEGER *> On entry, K specifies the number of columns of the matrix *> op( A ) and the number of rows of the matrix op( B ). K must *> be at least zero. *> \endverbatim *> *> \param[in] ALPHA *> \verbatim *> ALPHA is COMPLEX *> On entry, ALPHA specifies the scalar alpha. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is COMPLEX array of DIMENSION ( LDA, ka ), where ka is *> k when TRANSA = 'N' or 'n', and is m otherwise. *> Before entry with TRANSA = 'N' or 'n', the leading m by k *> part of the array A must contain the matrix A, otherwise *> the leading k by m part of the array A must contain the *> matrix A. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. When TRANSA = 'N' or 'n' then *> LDA must be at least max( 1, m ), otherwise LDA must be at *> least max( 1, k ). *> \endverbatim *> *> \param[in] B *> \verbatim *> B is COMPLEX array of DIMENSION ( LDB, kb ), where kb is *> n when TRANSB = 'N' or 'n', and is k otherwise. *> Before entry with TRANSB = 'N' or 'n', the leading k by n *> part of the array B must contain the matrix B, otherwise *> the leading n by k part of the array B must contain the *> matrix B. *> \endverbatim *> *> \param[in] LDB *> \verbatim *> LDB is INTEGER *> On entry, LDB specifies the first dimension of B as declared *> in the calling (sub) program. When TRANSB = 'N' or 'n' then *> LDB must be at least max( 1, k ), otherwise LDB must be at *> least max( 1, n ). *> \endverbatim *> *> \param[in] BETA *> \verbatim *> BETA is COMPLEX *> On entry, BETA specifies the scalar beta. When BETA is *> supplied as zero then C need not be set on input. *> \endverbatim *> *> \param[in,out] C *> \verbatim *> C is COMPLEX array of DIMENSION ( LDC, n ). *> Before entry, the leading m by n part of the array C must *> contain the matrix C, except when beta is zero, in which *> case C need not be set on entry. *> On exit, the array C is overwritten by the m by n matrix *> ( alpha*op( A )*op( B ) + beta*C ). *> \endverbatim *> *> \param[in] LDC *> \verbatim *> LDC is INTEGER *> On entry, LDC specifies the first dimension of C as declared *> in the calling (sub) program. LDC must be at least *> max( 1, m ). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup complex_blas_level3 * *> \par Further Details: * ===================== *> *> \verbatim *> *> Level 3 Blas routine. *> *> -- Written on 8-February-1989. *> Jack Dongarra, Argonne National Laboratory. *> Iain Duff, AERE Harwell. *> Jeremy Du Croz, Numerical Algorithms Group Ltd. *> Sven Hammarling, Numerical Algorithms Group Ltd. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_CGEMM(TRANSA,TRANSB,M,N,K, $ALPHA,A,LDA,B,LDB,BETA,C,LDC) * * -- Reference BLAS level3 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. COMPLEXALPHA,BETA INTEGER K,LDA,LDB,LDC,M,N CHARACTERTRANSA,TRANSB * .. * .. Array Arguments .. COMPLEXA(LDA,*),B(LDB,*),C(LDC,*) * .. * * ===================================================================== * * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC CONJG,MAX * .. * .. Local Scalars .. COMPLEXTEMP INTEGER I,INFO,J,L,NCOLA,NROWA,NROWB LOGICAL CONJA,CONJB,NOTA,NOTB * .. * .. Parameters .. COMPLEXONE PARAMETER (ONE=(1.0E+0,0.0E+0)) COMPLEXZERO PARAMETER (ZERO=(0.0E+0,0.0E+0)) * .. * * Set NOTA and NOTB as true if A and B respectively are not * conjugated or transposed, set CONJA and CONJB as true if A and * B respectively are to be transposed but not conjugated and set * NROWA, NCOLA and NROWB as the number of rows and columns of A * and the number of rows of B respectively. * NOTA=GAL_LSAME(TRANSA,'N') NOTB=GAL_LSAME(TRANSB,'N') CONJA=GAL_LSAME(TRANSA,'C') CONJB=GAL_LSAME(TRANSB,'C') IF(NOTA)THEN NROWA=M NCOLA=K ELSE NROWA=K NCOLA=M END IF IF(NOTB)THEN NROWB=K ELSE NROWB=N END IF * * Test the input parameters. * INFO=0 IF((.NOT.NOTA).AND.(.NOT.CONJA).AND. +(.NOT.GAL_LSAME(TRANSA,'T')))THEN INFO=1 ELSE IF((.NOT.NOTB).AND.(.NOT.CONJB).AND. +(.NOT.GAL_LSAME(TRANSB,'T')))THEN INFO=2 ELSE IF(M.LT.0)THEN INFO=3 ELSE IF(N.LT.0)THEN INFO=4 ELSE IF(K.LT.0)THEN INFO=5 ELSE IF(LDA.LT.MAX(1,NROWA))THEN INFO=8 ELSE IF(LDB.LT.MAX(1,NROWB))THEN INFO=10 ELSE IF(LDC.LT.MAX(1,M))THEN INFO=13 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_CGEMM',INFO) RETURN END IF * * Quick return if possible. * IF((M.EQ.0).OR.(N.EQ.0).OR. +(((ALPHA.EQ.ZERO).OR.(K.EQ.0)).AND.(BETA.EQ.ONE)))RETURN * * And when alpha.eq.zero. * IF(ALPHA.EQ.ZERO)THEN IF(BETA.EQ.ZERO)THEN DO 20J=1,N DO 10I=1,M C(I,J)=ZERO 10 CONTINUE 20 CONTINUE ELSE DO 40J=1,N DO 30I=1,M C(I,J)=BETA*C(I,J) 30 CONTINUE 40 CONTINUE END IF RETURN END IF * * Start the operations. * IF(NOTB)THEN IF(NOTA)THEN * * Form C := alpha*A*B + beta*C. * DO 90J=1,N IF(BETA.EQ.ZERO)THEN DO 50I=1,M C(I,J)=ZERO 50 CONTINUE ELSE IF(BETA.NE.ONE)THEN DO 60I=1,M C(I,J)=BETA*C(I,J) 60 CONTINUE END IF DO 80L=1,K IF(B(L,J).NE.ZERO)THEN TEMP=ALPHA*B(L,J) DO 70I=1,M C(I,J)=C(I,J)+TEMP*A(I,L) 70 CONTINUE END IF 80 CONTINUE 90 CONTINUE ELSE IF(CONJA)THEN * * Form C := alpha*A**H*B + beta*C. * DO 120J=1,N DO 110I=1,M TEMP=ZERO DO 100L=1,K TEMP=TEMP+CONJG(A(L,I))*B(L,J) 100 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 110 CONTINUE 120 CONTINUE ELSE * * Form C := alpha*A**T*B + beta*C * DO 150J=1,N DO 140I=1,M TEMP=ZERO DO 130L=1,K TEMP=TEMP+A(L,I)*B(L,J) 130 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 140 CONTINUE 150 CONTINUE END IF ELSE IF(NOTA)THEN IF(CONJB)THEN * * Form C := alpha*A*B**H + beta*C. * DO 200J=1,N IF(BETA.EQ.ZERO)THEN DO 160I=1,M C(I,J)=ZERO 160 CONTINUE ELSE IF(BETA.NE.ONE)THEN DO 170I=1,M C(I,J)=BETA*C(I,J) 170 CONTINUE END IF DO 190L=1,K IF(B(J,L).NE.ZERO)THEN TEMP=ALPHA*CONJG(B(J,L)) DO 180I=1,M C(I,J)=C(I,J)+TEMP*A(I,L) 180 CONTINUE END IF 190 CONTINUE 200 CONTINUE ELSE * * Form C := alpha*A*B**T + beta*C * DO 250J=1,N IF(BETA.EQ.ZERO)THEN DO 210I=1,M C(I,J)=ZERO 210 CONTINUE ELSE IF(BETA.NE.ONE)THEN DO 220I=1,M C(I,J)=BETA*C(I,J) 220 CONTINUE END IF DO 240L=1,K IF(B(J,L).NE.ZERO)THEN TEMP=ALPHA*B(J,L) DO 230I=1,M C(I,J)=C(I,J)+TEMP*A(I,L) 230 CONTINUE END IF 240 CONTINUE 250 CONTINUE END IF ELSE IF(CONJA)THEN IF(CONJB)THEN * * Form C := alpha*A**H*B**H + beta*C. * DO 280J=1,N DO 270I=1,M TEMP=ZERO DO 260L=1,K TEMP=TEMP+CONJG(A(L,I))*CONJG(B(J,L)) 260 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 270 CONTINUE 280 CONTINUE ELSE * * Form C := alpha*A**H*B**T + beta*C * DO 310J=1,N DO 300I=1,M TEMP=ZERO DO 290L=1,K TEMP=TEMP+CONJG(A(L,I))*B(J,L) 290 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 300 CONTINUE 310 CONTINUE END IF ELSE IF(CONJB)THEN * * Form C := alpha*A**T*B**H + beta*C * DO 340J=1,N DO 330I=1,M TEMP=ZERO DO 320L=1,K TEMP=TEMP+A(L,I)*CONJG(B(J,L)) 320 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 330 CONTINUE 340 CONTINUE ELSE * * Form C := alpha*A**T*B**T + beta*C * DO 370J=1,N DO 360I=1,M TEMP=ZERO DO 350L=1,K TEMP=TEMP+A(L,I)*B(J,L) 350 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 360 CONTINUE 370 CONTINUE END IF END IF * RETURN * * End of CGEMM . * END ga-5.9.2/LinAlg/lapack+blas/gal_daxpy.f000066400000000000000000000047421500715745200175700ustar00rootroot00000000000000*> \brief \b DAXPY * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DAXPY(N,DA,DX,INCX,DY,INCY) * * .. Scalar Arguments .. * DOUBLE PRECISION DA * INTEGER INCX,INCY,N * .. * .. Array Arguments .. * DOUBLE PRECISION DX(*),DY(*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DAXPY constant times a vector plus a vector. *> uses unrolled loops for increments equal to one. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level1 * *> \par Further Details: * ===================== *> *> \verbatim *> *> jack dongarra, linpack, 3/11/78. *> modified 12/3/93, array(1) declarations changed to array(*) *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DAXPY(N,DA,DX,INCX,DY,INCY) * * -- Reference BLAS level1 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. DOUBLE PRECISION DA INTEGER INCX,INCY,N * .. * .. Array Arguments .. DOUBLE PRECISION DX(*),DY(*) * .. * * ===================================================================== * * .. Local Scalars .. INTEGER I,IX,IY,M,MP1 * .. * .. Intrinsic Functions .. INTRINSIC MOD * .. IF(N.LE.0)RETURN IF(DA.EQ.0.0D0)RETURN IF(INCX.EQ.1.AND.INCY.EQ.1)THEN * * code for both increments equal to 1 * * * clean-up loop * M=MOD(N,4) IF(M.NE.0)THEN DO I=1,M DY(I)=DY(I)+DA*DX(I) END DO END IF IF(N.LT.4)RETURN MP1=M+1 DO I=MP1,N,4 DY(I)=DY(I)+DA*DX(I) DY(I+1)=DY(I+1)+DA*DX(I+1) DY(I+2)=DY(I+2)+DA*DX(I+2) DY(I+3)=DY(I+3)+DA*DX(I+3) END DO ELSE * * code for unequal increments or equal increments * not equal to 1 * IX=1 IY=1 IF(INCX.LT.0)IX=(-N+1)*INCX+1 IF(INCY.LT.0)IY=(-N+1)*INCY+1 DO I=1,N DY(IY)=DY(IY)+DA*DX(IX) IX=IX+INCX IY=IY+INCY END DO END IF RETURN END ga-5.9.2/LinAlg/lapack+blas/gal_dcabs1.f000066400000000000000000000006441500715745200175750ustar00rootroot00000000000000 DOUBLE PRECISION FUNCTION GAL_DCABS1(Z) * .. Scalar Arguments .. DOUBLE COMPLEX Z * .. * .. * Purpose * ======= * * GAL_DCABS1 computes absolute value of a double complex number * * ===================================================================== * * .. Intrinsic Functions .. INTRINSIC ABS,DBLE,DIMAG * GAL_DCABS1 = ABS(DBLE(Z)) + ABS(DIMAG(Z)) RETURN END ga-5.9.2/LinAlg/lapack+blas/gal_dcopy.f000066400000000000000000000046141500715745200175570ustar00rootroot00000000000000*> \brief \b DCOPY * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DCOPY(N,DX,INCX,DY,INCY) * * .. Scalar Arguments .. * INTEGER INCX,INCY,N * .. * .. Array Arguments .. * DOUBLE PRECISION DX(*),DY(*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DCOPY copies a vector, x, to a vector, y. *> uses unrolled loops for increments equal to one. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level1 * *> \par Further Details: * ===================== *> *> \verbatim *> *> jack dongarra, linpack, 3/11/78. *> modified 12/3/93, array(1) declarations changed to array(*) *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DCOPY(N,DX,INCX,DY,INCY) * * -- Reference BLAS level1 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. INTEGER INCX,INCY,N * .. * .. Array Arguments .. DOUBLE PRECISION DX(*),DY(*) * .. * * ===================================================================== * * .. Local Scalars .. INTEGER I,IX,IY,M,MP1 * .. * .. Intrinsic Functions .. INTRINSIC MOD * .. IF(N.LE.0)RETURN IF(INCX.EQ.1.AND.INCY.EQ.1)THEN * * code for both increments equal to 1 * * * clean-up loop * M=MOD(N,7) IF(M.NE.0)THEN DO I=1,M DY(I)=DX(I) END DO IF(N.LT.7)RETURN END IF MP1=M+1 DO I=MP1,N,7 DY(I)=DX(I) DY(I+1)=DX(I+1) DY(I+2)=DX(I+2) DY(I+3)=DX(I+3) DY(I+4)=DX(I+4) DY(I+5)=DX(I+5) DY(I+6)=DX(I+6) END DO ELSE * * code for unequal increments or equal increments * not equal to 1 * IX=1 IY=1 IF(INCX.LT.0)IX=(-N+1)*INCX+1 IF(INCY.LT.0)IY=(-N+1)*INCY+1 DO I=1,N DY(IY)=DX(IX) IX=IX+INCX IY=IY+INCY END DO END IF RETURN END ga-5.9.2/LinAlg/lapack+blas/gal_ddot.f000066400000000000000000000050251500715745200173700ustar00rootroot00000000000000*> \brief \b DDOT * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * DOUBLE PRECISION FUNCTION DDOT(N,DX,INCX,DY,INCY) * * .. Scalar Arguments .. * INTEGER INCX,INCY,N * .. * .. Array Arguments .. * DOUBLE PRECISION DX(*),DY(*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DDOT forms the dot product of two vectors. *> uses unrolled loops for increments equal to one. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level1 * *> \par Further Details: * ===================== *> *> \verbatim *> *> jack dongarra, linpack, 3/11/78. *> modified 12/3/93, array(1) declarations changed to array(*) *> \endverbatim *> * ===================================================================== DOUBLE PRECISION FUNCTION GAL_DDOT(N,DX,INCX,DY,INCY) * * -- Reference BLAS level1 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. INTEGER INCX,INCY,N * .. * .. Array Arguments .. DOUBLE PRECISION DX(*),DY(*) * .. * * ===================================================================== * * .. Local Scalars .. DOUBLE PRECISION DTEMP INTEGER I,IX,IY,M,MP1 * .. * .. Intrinsic Functions .. INTRINSIC MOD * .. GAL_DDOT=0.0D0 DTEMP=0.0D0 IF(N.LE.0)RETURN IF(INCX.EQ.1.AND.INCY.EQ.1)THEN * * code for both increments equal to 1 * * * clean-up loop * M=MOD(N,5) IF(M.NE.0)THEN DO I=1,M DTEMP=DTEMP+DX(I)*DY(I) END DO IF(N.LT.5)THEN GAL_DDOT=DTEMP RETURN END IF END IF MP1=M+1 DO I=MP1,N,5 DTEMP=DTEMP+DX(I)*DY(I)+DX(I+1)*DY(I+1)+ $DX(I+2)*DY(I+2)+DX(I+3)*DY(I+3)+DX(I+4)*DY(I+4) END DO ELSE * * code for unequal increments or equal increments * not equal to 1 * IX=1 IY=1 IF(INCX.LT.0)IX=(-N+1)*INCX+1 IF(INCY.LT.0)IY=(-N+1)*INCY+1 DO I=1,N DTEMP=DTEMP+DX(IX)*DY(IY) IX=IX+INCX IY=IY+INCY END DO END IF GAL_DDOT=DTEMP RETURN END ga-5.9.2/LinAlg/lapack+blas/gal_dgemm.f000066400000000000000000000233511500715745200175310ustar00rootroot00000000000000*> \brief \b DGEMM * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DGEMM(TRANSA,TRANSB,M,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) * * .. Scalar Arguments .. * DOUBLE PRECISION ALPHA,BETA * INTEGER K,LDA,LDB,LDC,M,N * CHARACTER TRANSA,TRANSB * .. * .. Array Arguments .. * DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DGEMM performs one of the matrix-matrix operations *> *> C := alpha*op( A )*op( B ) + beta*C, *> *> where op( X ) is one of *> *> op( X ) = X or op( X ) = X**T, *> *> alpha and beta are scalars, and A, B and C are matrices, with op( A ) *> an m by k matrix, op( B ) a k by n matrix and C an m by n matrix. *> \endverbatim * * Arguments: * ========== * *> \param[in] TRANSA *> \verbatim *> TRANSA is CHARACTER*1 *> On entry, TRANSA specifies the form of op( A ) to be used in *> the matrix multiplication as follows: *> *> TRANSA = 'N' or 'n', op( A ) = A. *> *> TRANSA = 'T' or 't', op( A ) = A**T. *> *> TRANSA = 'C' or 'c', op( A ) = A**T. *> \endverbatim *> *> \param[in] TRANSB *> \verbatim *> TRANSB is CHARACTER*1 *> On entry, TRANSB specifies the form of op( B ) to be used in *> the matrix multiplication as follows: *> *> TRANSB = 'N' or 'n', op( B ) = B. *> *> TRANSB = 'T' or 't', op( B ) = B**T. *> *> TRANSB = 'C' or 'c', op( B ) = B**T. *> \endverbatim *> *> \param[in] M *> \verbatim *> M is INTEGER *> On entry, M specifies the number of rows of the matrix *> op( A ) and of the matrix C. M must be at least zero. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the number of columns of the matrix *> op( B ) and the number of columns of the matrix C. N must be *> at least zero. *> \endverbatim *> *> \param[in] K *> \verbatim *> K is INTEGER *> On entry, K specifies the number of columns of the matrix *> op( A ) and the number of rows of the matrix op( B ). K must *> be at least zero. *> \endverbatim *> *> \param[in] ALPHA *> \verbatim *> ALPHA is DOUBLE PRECISION. *> On entry, ALPHA specifies the scalar alpha. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array of DIMENSION ( LDA, ka ), where ka is *> k when TRANSA = 'N' or 'n', and is m otherwise. *> Before entry with TRANSA = 'N' or 'n', the leading m by k *> part of the array A must contain the matrix A, otherwise *> the leading k by m part of the array A must contain the *> matrix A. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. When TRANSA = 'N' or 'n' then *> LDA must be at least max( 1, m ), otherwise LDA must be at *> least max( 1, k ). *> \endverbatim *> *> \param[in] B *> \verbatim *> B is DOUBLE PRECISION array of DIMENSION ( LDB, kb ), where kb is *> n when TRANSB = 'N' or 'n', and is k otherwise. *> Before entry with TRANSB = 'N' or 'n', the leading k by n *> part of the array B must contain the matrix B, otherwise *> the leading n by k part of the array B must contain the *> matrix B. *> \endverbatim *> *> \param[in] LDB *> \verbatim *> LDB is INTEGER *> On entry, LDB specifies the first dimension of B as declared *> in the calling (sub) program. When TRANSB = 'N' or 'n' then *> LDB must be at least max( 1, k ), otherwise LDB must be at *> least max( 1, n ). *> \endverbatim *> *> \param[in] BETA *> \verbatim *> BETA is DOUBLE PRECISION. *> On entry, BETA specifies the scalar beta. When BETA is *> supplied as zero then C need not be set on input. *> \endverbatim *> *> \param[in,out] C *> \verbatim *> C is DOUBLE PRECISION array of DIMENSION ( LDC, n ). *> Before entry, the leading m by n part of the array C must *> contain the matrix C, except when beta is zero, in which *> case C need not be set on entry. *> On exit, the array C is overwritten by the m by n matrix *> ( alpha*op( A )*op( B ) + beta*C ). *> \endverbatim *> *> \param[in] LDC *> \verbatim *> LDC is INTEGER *> On entry, LDC specifies the first dimension of C as declared *> in the calling (sub) program. LDC must be at least *> max( 1, m ). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level3 * *> \par Further Details: * ===================== *> *> \verbatim *> *> Level 3 Blas routine. *> *> -- Written on 8-February-1989. *> Jack Dongarra, Argonne National Laboratory. *> Iain Duff, AERE Harwell. *> Jeremy Du Croz, Numerical Algorithms Group Ltd. *> Sven Hammarling, Numerical Algorithms Group Ltd. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DGEMM(TRANSA,TRANSB,M,N,K, $ALPHA,A,LDA,B,LDB,BETA,C,LDC) * * -- Reference BLAS level3 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. DOUBLE PRECISION ALPHA,BETA INTEGER K,LDA,LDB,LDC,M,N CHARACTERTRANSA,TRANSB * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) * .. * * ===================================================================== * * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Local Scalars .. DOUBLE PRECISION TEMP INTEGER I,INFO,J,L,NCOLA,NROWA,NROWB LOGICAL NOTA,NOTB * .. * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * * Set NOTA and NOTB as true if A and B respectively are not * transposed and set NROWA, NCOLA and NROWB as the number of rows * and columns of A and the number of rows of B respectively. * NOTA=GAL_LSAME(TRANSA,'N') NOTB=GAL_LSAME(TRANSB,'N') IF(NOTA)THEN NROWA=M NCOLA=K ELSE NROWA=K NCOLA=M END IF IF(NOTB)THEN NROWB=K ELSE NROWB=N END IF * * Test the input parameters. * INFO=0 IF((.NOT.NOTA).AND.(.NOT.GAL_LSAME(TRANSA,'C')).AND. +(.NOT.GAL_LSAME(TRANSA,'T')))THEN INFO=1 ELSE IF((.NOT.NOTB).AND.(.NOT.GAL_LSAME(TRANSB,'C')).AND. +(.NOT.GAL_LSAME(TRANSB,'T')))THEN INFO=2 ELSE IF(M.LT.0)THEN INFO=3 ELSE IF(N.LT.0)THEN INFO=4 ELSE IF(K.LT.0)THEN INFO=5 ELSE IF(LDA.LT.MAX(1,NROWA))THEN INFO=8 ELSE IF(LDB.LT.MAX(1,NROWB))THEN INFO=10 ELSE IF(LDC.LT.MAX(1,M))THEN INFO=13 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DGEMM',INFO) RETURN END IF * * Quick return if possible. * IF((M.EQ.0).OR.(N.EQ.0).OR. +(((ALPHA.EQ.ZERO).OR.(K.EQ.0)).AND.(BETA.EQ.ONE)))RETURN * * And if alpha.eq.zero. * IF(ALPHA.EQ.ZERO)THEN IF(BETA.EQ.ZERO)THEN DO 20J=1,N DO 10I=1,M C(I,J)=ZERO 10 CONTINUE 20 CONTINUE ELSE DO 40J=1,N DO 30I=1,M C(I,J)=BETA*C(I,J) 30 CONTINUE 40 CONTINUE END IF RETURN END IF * * Start the operations. * IF(NOTB)THEN IF(NOTA)THEN * * Form C := alpha*A*B + beta*C. * DO 90J=1,N IF(BETA.EQ.ZERO)THEN DO 50I=1,M C(I,J)=ZERO 50 CONTINUE ELSE IF(BETA.NE.ONE)THEN DO 60I=1,M C(I,J)=BETA*C(I,J) 60 CONTINUE END IF DO 80L=1,K IF(B(L,J).NE.ZERO)THEN TEMP=ALPHA*B(L,J) DO 70I=1,M C(I,J)=C(I,J)+TEMP*A(I,L) 70 CONTINUE END IF 80 CONTINUE 90 CONTINUE ELSE * * Form C := alpha*A**T*B + beta*C * DO 120J=1,N DO 110I=1,M TEMP=ZERO DO 100L=1,K TEMP=TEMP+A(L,I)*B(L,J) 100 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 110 CONTINUE 120 CONTINUE END IF ELSE IF(NOTA)THEN * * Form C := alpha*A*B**T + beta*C * DO 170J=1,N IF(BETA.EQ.ZERO)THEN DO 130I=1,M C(I,J)=ZERO 130 CONTINUE ELSE IF(BETA.NE.ONE)THEN DO 140I=1,M C(I,J)=BETA*C(I,J) 140 CONTINUE END IF DO 160L=1,K IF(B(J,L).NE.ZERO)THEN TEMP=ALPHA*B(J,L) DO 150I=1,M C(I,J)=C(I,J)+TEMP*A(I,L) 150 CONTINUE END IF 160 CONTINUE 170 CONTINUE ELSE * * Form C := alpha*A**T*B**T + beta*C * DO 200J=1,N DO 190I=1,M TEMP=ZERO DO 180L=1,K TEMP=TEMP+A(L,I)*B(J,L) 180 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 190 CONTINUE 200 CONTINUE END IF END IF * RETURN * * End of DGEMM . * END ga-5.9.2/LinAlg/lapack+blas/gal_dgemv.f000066400000000000000000000174201500715745200175420ustar00rootroot00000000000000*> \brief \b DGEMV * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DGEMV(TRANS,M,N,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) * * .. Scalar Arguments .. * DOUBLE PRECISION ALPHA,BETA * INTEGER INCX,INCY,LDA,M,N * CHARACTER TRANS * .. * .. Array Arguments .. * DOUBLE PRECISION A(LDA,*),X(*),Y(*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DGEMV performs one of the matrix-vector operations *> *> y := alpha*A*x + beta*y, or y := alpha*A**T*x + beta*y, *> *> where alpha and beta are scalars, x and y are vectors and A is an *> m by n matrix. *> \endverbatim * * Arguments: * ========== * *> \param[in] TRANS *> \verbatim *> TRANS is CHARACTER*1 *> On entry, TRANS specifies the operation to be performed as *> follows: *> *> TRANS = 'N' or 'n' y := alpha*A*x + beta*y. *> *> TRANS = 'T' or 't' y := alpha*A**T*x + beta*y. *> *> TRANS = 'C' or 'c' y := alpha*A**T*x + beta*y. *> \endverbatim *> *> \param[in] M *> \verbatim *> M is INTEGER *> On entry, M specifies the number of rows of the matrix A. *> M must be at least zero. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the number of columns of the matrix A. *> N must be at least zero. *> \endverbatim *> *> \param[in] ALPHA *> \verbatim *> ALPHA is DOUBLE PRECISION. *> On entry, ALPHA specifies the scalar alpha. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array of DIMENSION ( LDA, n ). *> Before entry, the leading m by n part of the array A must *> contain the matrix of coefficients. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. LDA must be at least *> max( 1, m ). *> \endverbatim *> *> \param[in] X *> \verbatim *> X is DOUBLE PRECISION array of DIMENSION at least *> ( 1 + ( n - 1 )*abs( INCX ) ) when TRANS = 'N' or 'n' *> and at least *> ( 1 + ( m - 1 )*abs( INCX ) ) otherwise. *> Before entry, the incremented array X must contain the *> vector x. *> \endverbatim *> *> \param[in] INCX *> \verbatim *> INCX is INTEGER *> On entry, INCX specifies the increment for the elements of *> X. INCX must not be zero. *> \endverbatim *> *> \param[in] BETA *> \verbatim *> BETA is DOUBLE PRECISION. *> On entry, BETA specifies the scalar beta. When BETA is *> supplied as zero then Y need not be set on input. *> \endverbatim *> *> \param[in,out] Y *> \verbatim *> Y is DOUBLE PRECISION array of DIMENSION at least *> ( 1 + ( m - 1 )*abs( INCY ) ) when TRANS = 'N' or 'n' *> and at least *> ( 1 + ( n - 1 )*abs( INCY ) ) otherwise. *> Before entry with BETA non-zero, the incremented array Y *> must contain the vector y. On exit, Y is overwritten by the *> updated vector y. *> \endverbatim *> *> \param[in] INCY *> \verbatim *> INCY is INTEGER *> On entry, INCY specifies the increment for the elements of *> Y. INCY must not be zero. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level2 * *> \par Further Details: * ===================== *> *> \verbatim *> *> Level 2 Blas routine. *> The vector and matrix arguments are not referenced when N = 0, or M = 0 *> *> -- Written on 22-October-1986. *> Jack Dongarra, Argonne National Lab. *> Jeremy Du Croz, Nag Central Office. *> Sven Hammarling, Nag Central Office. *> Richard Hanson, Sandia National Labs. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DGEMV(TRANS,M,N,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) * * -- Reference BLAS level2 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. DOUBLE PRECISION ALPHA,BETA INTEGER INCX,INCY,LDA,M,N CHARACTERTRANS * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),X(*),Y(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * .. Local Scalars .. DOUBLE PRECISION TEMP INTEGER I,INFO,IX,IY,J,JX,JY,KX,KY,LENX,LENY * .. * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * * Test the input parameters. * INFO=0 IF(.NOT.GAL_LSAME(TRANS,'N').AND..NOT.GAL_LSAME(TRANS,'T').AND. +.NOT.GAL_LSAME(TRANS,'C'))THEN INFO=1 ELSE IF(M.LT.0)THEN INFO=2 ELSE IF(N.LT.0)THEN INFO=3 ELSE IF(LDA.LT.MAX(1,M))THEN INFO=6 ELSE IF(INCX.EQ.0)THEN INFO=8 ELSE IF(INCY.EQ.0)THEN INFO=11 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DGEMV',INFO) RETURN END IF * * Quick return if possible. * IF((M.EQ.0).OR.(N.EQ.0).OR. +((ALPHA.EQ.ZERO).AND.(BETA.EQ.ONE)))RETURN * * Set LENX and LENY, the lengths of the vectors x and y, and set * up the start points in X and Y. * IF(GAL_LSAME(TRANS,'N'))THEN LENX=N LENY=M ELSE LENX=M LENY=N END IF IF(INCX.GT.0)THEN KX=1 ELSE KX=1-(LENX-1)*INCX END IF IF(INCY.GT.0)THEN KY=1 ELSE KY=1-(LENY-1)*INCY END IF * * Start the operations. In this version the elements of A are * accessed sequentially with one pass through A. * * First form y := beta*y. * IF(BETA.NE.ONE)THEN IF(INCY.EQ.1)THEN IF(BETA.EQ.ZERO)THEN DO 10I=1,LENY Y(I)=ZERO 10 CONTINUE ELSE DO 20I=1,LENY Y(I)=BETA*Y(I) 20 CONTINUE END IF ELSE IY=KY IF(BETA.EQ.ZERO)THEN DO 30I=1,LENY Y(IY)=ZERO IY=IY+INCY 30 CONTINUE ELSE DO 40I=1,LENY Y(IY)=BETA*Y(IY) IY=IY+INCY 40 CONTINUE END IF END IF END IF IF(ALPHA.EQ.ZERO)RETURN IF(GAL_LSAME(TRANS,'N'))THEN * * Form y := alpha*A*x + y. * JX=KX IF(INCY.EQ.1)THEN DO 60J=1,N IF(X(JX).NE.ZERO)THEN TEMP=ALPHA*X(JX) DO 50I=1,M Y(I)=Y(I)+TEMP*A(I,J) 50 CONTINUE END IF JX=JX+INCX 60 CONTINUE ELSE DO 80J=1,N IF(X(JX).NE.ZERO)THEN TEMP=ALPHA*X(JX) IY=KY DO 70I=1,M Y(IY)=Y(IY)+TEMP*A(I,J) IY=IY+INCY 70 CONTINUE END IF JX=JX+INCX 80 CONTINUE END IF ELSE * * Form y := alpha*A**T*x + y. * JY=KY IF(INCX.EQ.1)THEN DO 100J=1,N TEMP=ZERO DO 90I=1,M TEMP=TEMP+A(I,J)*X(I) 90 CONTINUE Y(JY)=Y(JY)+ALPHA*TEMP JY=JY+INCY 100 CONTINUE ELSE DO 120J=1,N TEMP=ZERO IX=KX DO 110I=1,M TEMP=TEMP+A(I,J)*X(IX) IX=IX+INCX 110 CONTINUE Y(JY)=Y(JY)+ALPHA*TEMP JY=JY+INCY 120 CONTINUE END IF END IF * RETURN * * End of DGEMV . * END ga-5.9.2/LinAlg/lapack+blas/gal_dger.f000066400000000000000000000124011500715745200173530ustar00rootroot00000000000000*> \brief \b DGER * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DGER(M,N,ALPHA,X,INCX,Y,INCY,A,LDA) * * .. Scalar Arguments .. * DOUBLE PRECISION ALPHA * INTEGER INCX,INCY,LDA,M,N * .. * .. Array Arguments .. * DOUBLE PRECISION A(LDA,*),X(*),Y(*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DGER performs the rank 1 operation *> *> A := alpha*x*y**T + A, *> *> where alpha is a scalar, x is an m element vector, y is an n element *> vector and A is an m by n matrix. *> \endverbatim * * Arguments: * ========== * *> \param[in] M *> \verbatim *> M is INTEGER *> On entry, M specifies the number of rows of the matrix A. *> M must be at least zero. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the number of columns of the matrix A. *> N must be at least zero. *> \endverbatim *> *> \param[in] ALPHA *> \verbatim *> ALPHA is DOUBLE PRECISION. *> On entry, ALPHA specifies the scalar alpha. *> \endverbatim *> *> \param[in] X *> \verbatim *> X is DOUBLE PRECISION array of dimension at least *> ( 1 + ( m - 1 )*abs( INCX ) ). *> Before entry, the incremented array X must contain the m *> element vector x. *> \endverbatim *> *> \param[in] INCX *> \verbatim *> INCX is INTEGER *> On entry, INCX specifies the increment for the elements of *> X. INCX must not be zero. *> \endverbatim *> *> \param[in] Y *> \verbatim *> Y is DOUBLE PRECISION array of dimension at least *> ( 1 + ( n - 1 )*abs( INCY ) ). *> Before entry, the incremented array Y must contain the n *> element vector y. *> \endverbatim *> *> \param[in] INCY *> \verbatim *> INCY is INTEGER *> On entry, INCY specifies the increment for the elements of *> Y. INCY must not be zero. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is DOUBLE PRECISION array of DIMENSION ( LDA, n ). *> Before entry, the leading m by n part of the array A must *> contain the matrix of coefficients. On exit, A is *> overwritten by the updated matrix. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. LDA must be at least *> max( 1, m ). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level2 * *> \par Further Details: * ===================== *> *> \verbatim *> *> Level 2 Blas routine. *> *> -- Written on 22-October-1986. *> Jack Dongarra, Argonne National Lab. *> Jeremy Du Croz, Nag Central Office. *> Sven Hammarling, Nag Central Office. *> Richard Hanson, Sandia National Labs. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DGER(M,N,ALPHA,X,INCX,Y,INCY,A,LDA) * * -- Reference BLAS level2 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. DOUBLE PRECISION ALPHA INTEGER INCX,INCY,LDA,M,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),X(*),Y(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO PARAMETER (ZERO=0.0D+0) * .. * .. Local Scalars .. DOUBLE PRECISION TEMP INTEGER I,INFO,IX,J,JY,KX * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * * Test the input parameters. * INFO=0 IF(M.LT.0)THEN INFO=1 ELSE IF(N.LT.0)THEN INFO=2 ELSE IF(INCX.EQ.0)THEN INFO=5 ELSE IF(INCY.EQ.0)THEN INFO=7 ELSE IF(LDA.LT.MAX(1,M))THEN INFO=9 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DGER',INFO) RETURN END IF * * Quick return if possible. * IF((M.EQ.0).OR.(N.EQ.0).OR.(ALPHA.EQ.ZERO))RETURN * * Start the operations. In this version the elements of A are * accessed sequentially with one pass through A. * IF(INCY.GT.0)THEN JY=1 ELSE JY=1-(N-1)*INCY END IF IF(INCX.EQ.1)THEN DO 20J=1,N IF(Y(JY).NE.ZERO)THEN TEMP=ALPHA*Y(JY) DO 10I=1,M A(I,J)=A(I,J)+X(I)*TEMP 10 CONTINUE END IF JY=JY+INCY 20 CONTINUE ELSE IF(INCX.GT.0)THEN KX=1 ELSE KX=1-(M-1)*INCX END IF DO 40J=1,N IF(Y(JY).NE.ZERO)THEN TEMP=ALPHA*Y(JY) IX=KX DO 30I=1,M A(I,J)=A(I,J)+X(IX)*TEMP IX=IX+INCX 30 CONTINUE END IF JY=JY+INCY 40 CONTINUE END IF * RETURN * * End of DGER . * END ga-5.9.2/LinAlg/lapack+blas/gal_dgetf2.f000066400000000000000000000126211500715745200176110ustar00rootroot00000000000000*> \brief \b DGETF2 computes the LU factorization of a general m-by-n matrix using partial pivoting with row interchanges (unblocked algorithm). * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DGETF2 + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DGETF2( M, N, A, LDA, IPIV, INFO ) * * .. Scalar Arguments .. * INTEGER INFO, LDA, M, N * .. * .. Array Arguments .. * INTEGER IPIV( * ) * DOUBLE PRECISION A( LDA, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DGETF2 computes an LU factorization of a general m-by-n matrix A *> using partial pivoting with row interchanges. *> *> The factorization has the form *> A = P * L * U *> where P is a permutation matrix, L is lower triangular with unit *> diagonal elements (lower trapezoidal if m > n), and U is upper *> triangular (upper trapezoidal if m < n). *> *> This is the right-looking Level 2 BLAS version of the algorithm. *> \endverbatim * * Arguments: * ========== * *> \param[in] M *> \verbatim *> M is INTEGER *> The number of rows of the matrix A. M >= 0. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix A. N >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> On entry, the m by n matrix to be factored. *> On exit, the factors L and U from the factorization *> A = P*L*U; the unit diagonal elements of L are not stored. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,M). *> \endverbatim *> *> \param[out] IPIV *> \verbatim *> IPIV is INTEGER array, dimension (min(M,N)) *> The pivot indices; for 1 <= i <= min(M,N), row i of the *> matrix was interchanged with row IPIV(i). *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -k, the k-th argument had an illegal value *> > 0: if INFO = k, U(k,k) is exactly zero. The factorization *> has been completed, but the factor U is exactly *> singular, and division by zero will occur if it is used *> to solve a system of equations. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup doubleGEcomputational * * ===================================================================== SUBROUTINE GAL_DGETF2(M,N,A,LDA,IPIV,INFO) * * -- LAPACK computational routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. INTEGER INFO,LDA,M,N * .. * .. Array Arguments .. INTEGER IPIV(*) DOUBLE PRECISION A(LDA,*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * .. Local Scalars .. DOUBLE PRECISION SFMIN INTEGER I,J,JP * .. * .. External Functions .. DOUBLE PRECISION GAL_DLAMCH INTEGER GAL_IDAMAX EXTERNAL GAL_DLAMCH,GAL_IDAMAX * .. * .. External Subroutines .. EXTERNAL GAL_DGER,GAL_DSCAL,GAL_DSWAP,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX,MIN * .. * .. Executable Statements .. * * Test the input parameters. * INFO=0 IF(M.LT.0)THEN INFO=-1 ELSE IF(N.LT.0)THEN INFO=-2 ELSE IF(LDA.LT.MAX(1,M))THEN INFO=-4 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DGETF2',-INFO) RETURN END IF * * Quick return if possible * IF(M.EQ.0.OR.N.EQ.0) $RETURN * * Compute machine safe minimum * SFMIN=GAL_DLAMCH('S') * DO 10J=1,MIN(M,N) * * Find pivot and test for singularity. * JP=J-1+GAL_IDAMAX(M-J+1,A(J,J),1) IPIV(J)=JP IF(A(JP,J).NE.ZERO)THEN * * Apply the interchange to columns 1:N. * IF(JP.NE.J) $CALL GAL_DSWAP(N,A(J,1),LDA,A(JP,1),LDA) * * Compute elements J+1:M of J-th column. * IF(J.LT.M)THEN IF(ABS(A(J,J)).GE.SFMIN)THEN CALL GAL_DSCAL(M-J,ONE/A(J,J),A(J+1,J),1) ELSE DO 20I=1,M-J A(J+I,J)=A(J+I,J)/A(J,J) 20 CONTINUE END IF END IF * ELSE IF(INFO.EQ.0)THEN * INFO=J END IF * IF(J.LT.MIN(M,N))THEN * * Update trailing submatrix. * CALL GAL_DGER(M-J,N-J,-ONE,A(J+1,J),1,A(J,J+1),LDA, $A(J+1,J+1),LDA) END IF 10 CONTINUE RETURN * * End of DGETF2 * END ga-5.9.2/LinAlg/lapack+blas/gal_dgetrf.f000066400000000000000000000132701500715745200177120ustar00rootroot00000000000000*> \brief \b DGETRF * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DGETRF + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DGETRF( M, N, A, LDA, IPIV, INFO ) * * .. Scalar Arguments .. * INTEGER INFO, LDA, M, N * .. * .. Array Arguments .. * INTEGER IPIV( * ) * DOUBLE PRECISION A( LDA, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DGETRF computes an LU factorization of a general M-by-N matrix A *> using partial pivoting with row interchanges. *> *> The factorization has the form *> A = P * L * U *> where P is a permutation matrix, L is lower triangular with unit *> diagonal elements (lower trapezoidal if m > n), and U is upper *> triangular (upper trapezoidal if m < n). *> *> This is the right-looking Level 3 BLAS version of the algorithm. *> \endverbatim * * Arguments: * ========== * *> \param[in] M *> \verbatim *> M is INTEGER *> The number of rows of the matrix A. M >= 0. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix A. N >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> On entry, the M-by-N matrix to be factored. *> On exit, the factors L and U from the factorization *> A = P*L*U; the unit diagonal elements of L are not stored. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,M). *> \endverbatim *> *> \param[out] IPIV *> \verbatim *> IPIV is INTEGER array, dimension (min(M,N)) *> The pivot indices; for 1 <= i <= min(M,N), row i of the *> matrix was interchanged with row IPIV(i). *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument had an illegal value *> > 0: if INFO = i, U(i,i) is exactly zero. The factorization *> has been completed, but the factor U is exactly *> singular, and division by zero will occur if it is used *> to solve a system of equations. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup doubleGEcomputational * * ===================================================================== SUBROUTINE GAL_DGETRF(M,N,A,LDA,IPIV,INFO) * * -- LAPACK computational routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. INTEGER INFO,LDA,M,N * .. * .. Array Arguments .. INTEGER IPIV(*) DOUBLE PRECISION A(LDA,*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE PARAMETER (ONE=1.0D+0) * .. * .. Local Scalars .. INTEGER I,IINFO,J,JB,NB * .. * .. External Subroutines .. EXTERNAL GAL_DGEMM,GAL_DGETF2,GAL_DLASWP,GAL_DTRSM,GAL_XERBLA * .. * .. External Functions .. INTEGER GAL_ILAENV EXTERNAL GAL_ILAENV * .. * .. Intrinsic Functions .. INTRINSIC MAX,MIN * .. * .. Executable Statements .. * * Test the input parameters. * INFO=0 IF(M.LT.0)THEN INFO=-1 ELSE IF(N.LT.0)THEN INFO=-2 ELSE IF(LDA.LT.MAX(1,M))THEN INFO=-4 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DGETRF',-INFO) RETURN END IF * * Quick return if possible * IF(M.EQ.0.OR.N.EQ.0) $RETURN * * Determine the block size for this environment. * NB=GAL_ILAENV(1,'GAL_DGETRF','',M,N,-1,-1) IF(NB.LE.1.OR.NB.GE.MIN(M,N))THEN * * Use unblocked code. * CALL GAL_DGETF2(M,N,A,LDA,IPIV,INFO) ELSE * * Use blocked code. * DO 20J=1,MIN(M,N),NB JB=MIN(MIN(M,N)-J+1,NB) * * Factor diagonal and subdiagonal blocks and test for exact * singularity. * CALL GAL_DGETF2(M-J+1,JB,A(J,J),LDA,IPIV(J),IINFO) * * Adjust INFO and the pivot indices. * IF(INFO.EQ.0.AND.IINFO.GT.0) $INFO=IINFO+J-1 DO 10I=J,MIN(M,J+JB-1) IPIV(I)=J-1+IPIV(I) 10 CONTINUE * * Apply interchanges to columns 1:J-1. * CALL GAL_DLASWP(J-1,A,LDA,J,J+JB-1,IPIV,1) * IF(J+JB.LE.N)THEN * * Apply interchanges to columns J+JB:N. * CALL GAL_DLASWP(N-J-JB+1,A(1,J+JB),LDA,J,J+JB-1, $IPIV,1) * * Compute block row of U. * CALL GAL_DTRSM('LEFT','LOWER','NOTRANSPOSE','UNIT',JB, $N-J-JB+1,ONE,A(J,J),LDA,A(J,J+JB), $LDA) IF(J+JB.LE.M)THEN * * Update trailing submatrix. * CALL GAL_DGEMM('NOTRANSPOSE','NOTRANSPOSE',M-J-JB+1, $N-J-JB+1,JB,-ONE,A(J+JB,J),LDA, $A(J,J+JB),LDA,ONE,A(J+JB,J+JB), $LDA) END IF END IF 20 CONTINUE END IF RETURN * * End of DGETRF * END ga-5.9.2/LinAlg/lapack+blas/gal_dgetrs.f000066400000000000000000000131501500715745200177240ustar00rootroot00000000000000*> \brief \b DGETRS * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DGETRS + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DGETRS( TRANS, N, NRHS, A, LDA, IPIV, B, LDB, INFO ) * * .. Scalar Arguments .. * CHARACTER TRANS * INTEGER INFO, LDA, LDB, N, NRHS * .. * .. Array Arguments .. * INTEGER IPIV( * ) * DOUBLE PRECISION A( LDA, * ), B( LDB, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DGETRS solves a system of linear equations *> A * X = B or A**T * X = B *> with a general N-by-N matrix A using the LU factorization computed *> by DGETRF. *> \endverbatim * * Arguments: * ========== * *> \param[in] TRANS *> \verbatim *> TRANS is CHARACTER*1 *> Specifies the form of the system of equations: *> = 'N': A * X = B (No transpose) *> = 'T': A**T* X = B (Transpose) *> = 'C': A**T* X = B (Conjugate transpose = Transpose) *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrix A. N >= 0. *> \endverbatim *> *> \param[in] NRHS *> \verbatim *> NRHS is INTEGER *> The number of right hand sides, i.e., the number of columns *> of the matrix B. NRHS >= 0. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> The factors L and U from the factorization A = P*L*U *> as computed by DGETRF. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,N). *> \endverbatim *> *> \param[in] IPIV *> \verbatim *> IPIV is INTEGER array, dimension (N) *> The pivot indices from DGETRF; for 1<=i<=N, row i of the *> matrix was interchanged with row IPIV(i). *> \endverbatim *> *> \param[in,out] B *> \verbatim *> B is DOUBLE PRECISION array, dimension (LDB,NRHS) *> On entry, the right hand side matrix B. *> On exit, the solution matrix X. *> \endverbatim *> *> \param[in] LDB *> \verbatim *> LDB is INTEGER *> The leading dimension of the array B. LDB >= max(1,N). *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument had an illegal value *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup doubleGEcomputational * * ===================================================================== SUBROUTINE GAL_DGETRS(TRANS,N,NRHS,A,LDA,IPIV,B,LDB,INFO) * * -- LAPACK computational routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. CHARACTERTRANS INTEGER INFO,LDA,LDB,N,NRHS * .. * .. Array Arguments .. INTEGER IPIV(*) DOUBLE PRECISION A(LDA,*),B(LDB,*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE PARAMETER (ONE=1.0D+0) * .. * .. Local Scalars .. LOGICAL NOTRAN * .. * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_DLASWP,GAL_DTRSM,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Executable Statements .. * * Test the input parameters. * INFO=0 NOTRAN=GAL_LSAME(TRANS,'N') IF(.NOT.NOTRAN.AND..NOT.GAL_LSAME(TRANS,'T').AND..NOT. $GAL_LSAME(TRANS,'C'))THEN INFO=-1 ELSE IF(N.LT.0)THEN INFO=-2 ELSE IF(NRHS.LT.0)THEN INFO=-3 ELSE IF(LDA.LT.MAX(1,N))THEN INFO=-5 ELSE IF(LDB.LT.MAX(1,N))THEN INFO=-8 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DGETRS',-INFO) RETURN END IF * * Quick return if possible * IF(N.EQ.0.OR.NRHS.EQ.0) $RETURN * IF(NOTRAN)THEN * * Solve A * X = B. * * Apply row interchanges to the right hand sides. * CALL GAL_DLASWP(NRHS,B,LDB,1,N,IPIV,1) * * Solve L*X = B, overwriting B with X. * CALL GAL_DTRSM('LEFT','LOWER','NOTRANSPOSE','UNIT',N,NRHS, $ONE,A,LDA,B,LDB) * * Solve U*X = B, overwriting B with X. * CALL GAL_DTRSM('LEFT','UPPER','NOTRANSPOSE','NON-UNIT',N, $NRHS,ONE,A,LDA,B,LDB) ELSE * * Solve A**T * X = B. * * Solve U**T *X = B, overwriting B with X. * CALL GAL_DTRSM('LEFT','UPPER','TRANSPOSE','NON-UNIT',N,NRHS, $ONE,A,LDA,B,LDB) * * Solve L**T *X = B, overwriting B with X. * CALL GAL_DTRSM('LEFT','LOWER','TRANSPOSE','UNIT',N,NRHS,ONE, $A,LDA,B,LDB) * * Apply row interchanges to the solution vectors. * CALL GAL_DLASWP(NRHS,B,LDB,1,N,IPIV,-1) END IF * RETURN * * End of DGETRS * END ga-5.9.2/LinAlg/lapack+blas/gal_disnan.f000066400000000000000000000037511500715745200177160ustar00rootroot00000000000000*> \brief \b DISNAN tests input for NaN. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DISNAN + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * LOGICAL FUNCTION DISNAN( DIN ) * * .. Scalar Arguments .. * DOUBLE PRECISION DIN * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DISNAN returns .TRUE. if its argument is NaN, and .FALSE. *> otherwise. To be replaced by the Fortran 2003 intrinsic in the *> future. *> \endverbatim * * Arguments: * ========== * *> \param[in] DIN *> \verbatim *> DIN is DOUBLE PRECISION *> Input to test for NaN. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERauxiliary * * ===================================================================== LOGICAL FUNCTION GAL_DISNAN(DIN) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. DOUBLE PRECISION DIN * .. * * ===================================================================== * * .. External Functions .. LOGICAL GAL_DLAISNAN EXTERNAL GAL_DLAISNAN * .. * .. Executable Statements .. GAL_DISNAN=GAL_DLAISNAN(DIN,DIN) RETURN END ga-5.9.2/LinAlg/lapack+blas/gal_dlacpy.f000066400000000000000000000075111500715745200177140ustar00rootroot00000000000000*> \brief \b DLACPY copies all or part of one two-dimensional array to another. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLACPY + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLACPY( UPLO, M, N, A, LDA, B, LDB ) * * .. Scalar Arguments .. * CHARACTER UPLO * INTEGER LDA, LDB, M, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), B( LDB, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLACPY copies all or part of a two-dimensional matrix A to another *> matrix B. *> \endverbatim * * Arguments: * ========== * *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> Specifies the part of the matrix A to be copied to B. *> = 'U': Upper triangular part *> = 'L': Lower triangular part *> Otherwise: All of the matrix A *> \endverbatim *> *> \param[in] M *> \verbatim *> M is INTEGER *> The number of rows of the matrix A. M >= 0. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix A. N >= 0. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> The m by n matrix A. If UPLO = 'U', only the upper triangle *> or trapezoid is accessed; if UPLO = 'L', only the lower *> triangle or trapezoid is accessed. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,M). *> \endverbatim *> *> \param[out] B *> \verbatim *> B is DOUBLE PRECISION array, dimension (LDB,N) *> On exit, B = A in the locations specified by UPLO. *> \endverbatim *> *> \param[in] LDB *> \verbatim *> LDB is INTEGER *> The leading dimension of the array B. LDB >= max(1,M). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERauxiliary * * ===================================================================== SUBROUTINE GAL_DLACPY(UPLO,M,N,A,LDA,B,LDB) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. CHARACTERUPLO INTEGER LDA,LDB,M,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),B(LDB,*) * .. * * ===================================================================== * * .. Local Scalars .. INTEGER I,J * .. * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. Intrinsic Functions .. INTRINSIC MIN * .. * .. Executable Statements .. * IF(GAL_LSAME(UPLO,'U'))THEN DO 20J=1,N DO 10I=1,MIN(J,M) B(I,J)=A(I,J) 10 CONTINUE 20 CONTINUE ELSE IF(GAL_LSAME(UPLO,'L'))THEN DO 40J=1,N DO 30I=J,M B(I,J)=A(I,J) 30 CONTINUE 40 CONTINUE ELSE DO 60J=1,N DO 50I=1,M B(I,J)=A(I,J) 50 CONTINUE 60 CONTINUE END IF RETURN * * End of DLACPY * END ga-5.9.2/LinAlg/lapack+blas/gal_dlae2.f000066400000000000000000000107751500715745200174350ustar00rootroot00000000000000*> \brief \b DLAE2 computes the eigenvalues of a 2-by-2 symmetric matrix. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLAE2 + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLAE2( A, B, C, RT1, RT2 ) * * .. Scalar Arguments .. * DOUBLE PRECISION A, B, C, RT1, RT2 * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLAE2 computes the eigenvalues of a 2-by-2 symmetric matrix *> [ A B ] *> [ B C ]. *> On return, RT1 is the eigenvalue of larger absolute value, and RT2 *> is the eigenvalue of smaller absolute value. *> \endverbatim * * Arguments: * ========== * *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION *> The (1,1) element of the 2-by-2 matrix. *> \endverbatim *> *> \param[in] B *> \verbatim *> B is DOUBLE PRECISION *> The (1,2) and (2,1) elements of the 2-by-2 matrix. *> \endverbatim *> *> \param[in] C *> \verbatim *> C is DOUBLE PRECISION *> The (2,2) element of the 2-by-2 matrix. *> \endverbatim *> *> \param[out] RT1 *> \verbatim *> RT1 is DOUBLE PRECISION *> The eigenvalue of larger absolute value. *> \endverbatim *> *> \param[out] RT2 *> \verbatim *> RT2 is DOUBLE PRECISION *> The eigenvalue of smaller absolute value. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERauxiliary * *> \par Further Details: * ===================== *> *> \verbatim *> *> RT1 is accurate to a few ulps barring over/underflow. *> *> RT2 may be inaccurate if there is massive cancellation in the *> determinant A*C-B*B; higher precision or correctly rounded or *> correctly truncated arithmetic would be needed to compute RT2 *> accurately in all cases. *> *> Overflow is possible only if RT1 is within a factor of 5 of overflow. *> Underflow is harmless if the input data is 0 or exceeds *> underflow_threshold / macheps. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DLAE2(A,B,C,RT1,RT2) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. DOUBLE PRECISION A,B,C,RT1,RT2 * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE PARAMETER (ONE=1.0D0) DOUBLE PRECISION TWO PARAMETER (TWO=2.0D0) DOUBLE PRECISION ZERO PARAMETER (ZERO=0.0D0) DOUBLE PRECISION HALF PARAMETER (HALF=0.5D0) * .. * .. Local Scalars .. DOUBLE PRECISION AB,ACMN,ACMX,ADF,DF,RT,SM,TB * .. * .. Intrinsic Functions .. INTRINSIC ABS,SQRT * .. * .. Executable Statements .. * * Compute the eigenvalues * SM=A+C DF=A-C ADF=ABS(DF) TB=B+B AB=ABS(TB) IF(ABS(A).GT.ABS(C))THEN ACMX=A ACMN=C ELSE ACMX=C ACMN=A END IF IF(ADF.GT.AB)THEN RT=ADF*SQRT(ONE+(AB/ADF)**2) ELSE IF(ADF.LT.AB)THEN RT=AB*SQRT(ONE+(ADF/AB)**2) ELSE * * Includes case AB=ADF=0 * RT=AB*SQRT(TWO) END IF IF(SM.LT.ZERO)THEN RT1=HALF*(SM-RT) * * Order of execution important. * To get fully accurate smaller eigenvalue, * next line needs to be executed in higher precision. * RT2=(ACMX/RT1)*ACMN-(B/RT1)*B ELSE IF(SM.GT.ZERO)THEN RT1=HALF*(SM+RT) * * Order of execution important. * To get fully accurate smaller eigenvalue, * next line needs to be executed in higher precision. * RT2=(ACMX/RT1)*ACMN-(B/RT1)*B ELSE * * Includes case RT1 = RT2 = 0 * RT1=HALF*RT RT2=-HALF*RT END IF RETURN * * End of DLAE2 * END ga-5.9.2/LinAlg/lapack+blas/gal_dlaev2.f000066400000000000000000000132711500715745200176150ustar00rootroot00000000000000*> \brief \b DLAEV2 computes the eigenvalues and eigenvectors of a 2-by-2 symmetric/Hermitian matrix. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLAEV2 + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLAEV2( A, B, C, RT1, RT2, CS1, SN1 ) * * .. Scalar Arguments .. * DOUBLE PRECISION A, B, C, CS1, RT1, RT2, SN1 * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLAEV2 computes the eigendecomposition of a 2-by-2 symmetric matrix *> [ A B ] *> [ B C ]. *> On return, RT1 is the eigenvalue of larger absolute value, RT2 is the *> eigenvalue of smaller absolute value, and (CS1,SN1) is the unit right *> eigenvector for RT1, giving the decomposition *> *> [ CS1 SN1 ] [ A B ] [ CS1 -SN1 ] = [ RT1 0 ] *> [-SN1 CS1 ] [ B C ] [ SN1 CS1 ] [ 0 RT2 ]. *> \endverbatim * * Arguments: * ========== * *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION *> The (1,1) element of the 2-by-2 matrix. *> \endverbatim *> *> \param[in] B *> \verbatim *> B is DOUBLE PRECISION *> The (1,2) element and the conjugate of the (2,1) element of *> the 2-by-2 matrix. *> \endverbatim *> *> \param[in] C *> \verbatim *> C is DOUBLE PRECISION *> The (2,2) element of the 2-by-2 matrix. *> \endverbatim *> *> \param[out] RT1 *> \verbatim *> RT1 is DOUBLE PRECISION *> The eigenvalue of larger absolute value. *> \endverbatim *> *> \param[out] RT2 *> \verbatim *> RT2 is DOUBLE PRECISION *> The eigenvalue of smaller absolute value. *> \endverbatim *> *> \param[out] CS1 *> \verbatim *> CS1 is DOUBLE PRECISION *> \endverbatim *> *> \param[out] SN1 *> \verbatim *> SN1 is DOUBLE PRECISION *> The vector (CS1, SN1) is a unit right eigenvector for RT1. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERauxiliary * *> \par Further Details: * ===================== *> *> \verbatim *> *> RT1 is accurate to a few ulps barring over/underflow. *> *> RT2 may be inaccurate if there is massive cancellation in the *> determinant A*C-B*B; higher precision or correctly rounded or *> correctly truncated arithmetic would be needed to compute RT2 *> accurately in all cases. *> *> CS1 and SN1 are accurate to a few ulps barring over/underflow. *> *> Overflow is possible only if RT1 is within a factor of 5 of overflow. *> Underflow is harmless if the input data is 0 or exceeds *> underflow_threshold / macheps. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DLAEV2(A,B,C,RT1,RT2,CS1,SN1) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. DOUBLE PRECISION A,B,C,CS1,RT1,RT2,SN1 * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE PARAMETER (ONE=1.0D0) DOUBLE PRECISION TWO PARAMETER (TWO=2.0D0) DOUBLE PRECISION ZERO PARAMETER (ZERO=0.0D0) DOUBLE PRECISION HALF PARAMETER (HALF=0.5D0) * .. * .. Local Scalars .. INTEGER SGN1,SGN2 DOUBLE PRECISION AB,ACMN,ACMX,ACS,ADF,CS,CT,DF,RT,SM, $TB,TN * .. * .. Intrinsic Functions .. INTRINSIC ABS,SQRT * .. * .. Executable Statements .. * * Compute the eigenvalues * SM=A+C DF=A-C ADF=ABS(DF) TB=B+B AB=ABS(TB) IF(ABS(A).GT.ABS(C))THEN ACMX=A ACMN=C ELSE ACMX=C ACMN=A END IF IF(ADF.GT.AB)THEN RT=ADF*SQRT(ONE+(AB/ADF)**2) ELSE IF(ADF.LT.AB)THEN RT=AB*SQRT(ONE+(ADF/AB)**2) ELSE * * Includes case AB=ADF=0 * RT=AB*SQRT(TWO) END IF IF(SM.LT.ZERO)THEN RT1=HALF*(SM-RT) SGN1=-1 * * Order of execution important. * To get fully accurate smaller eigenvalue, * next line needs to be executed in higher precision. * RT2=(ACMX/RT1)*ACMN-(B/RT1)*B ELSE IF(SM.GT.ZERO)THEN RT1=HALF*(SM+RT) SGN1=1 * * Order of execution important. * To get fully accurate smaller eigenvalue, * next line needs to be executed in higher precision. * RT2=(ACMX/RT1)*ACMN-(B/RT1)*B ELSE * * Includes case RT1 = RT2 = 0 * RT1=HALF*RT RT2=-HALF*RT SGN1=1 END IF * * Compute the eigenvector * IF(DF.GE.ZERO)THEN CS=DF+RT SGN2=1 ELSE CS=DF-RT SGN2=-1 END IF ACS=ABS(CS) IF(ACS.GT.AB)THEN CT=-TB/CS SN1=ONE/SQRT(ONE+CT*CT) CS1=CT*SN1 ELSE IF(AB.EQ.ZERO)THEN CS1=ONE SN1=ZERO ELSE TN=-CS/TB CS1=ONE/SQRT(ONE+TN*TN) SN1=TN*CS1 END IF END IF IF(SGN1.EQ.SGN2)THEN TN=CS1 CS1=-SN1 SN1=TN END IF RETURN * * End of DLAEV2 * END ga-5.9.2/LinAlg/lapack+blas/gal_dlaisnan.f000066400000000000000000000050641500715745200202320ustar00rootroot00000000000000*> \brief \b DLAISNAN tests input for NaN by comparing two arguments for inequality. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLAISNAN + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * LOGICAL FUNCTION DLAISNAN( DIN1, DIN2 ) * * .. Scalar Arguments .. * DOUBLE PRECISION DIN1, DIN2 * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> This routine is not for general use. It exists solely to avoid *> over-optimization in DISNAN. *> *> DLAISNAN checks for NaNs by comparing its two arguments for *> inequality. NaN is the only floating-point value where NaN != NaN *> returns .TRUE. To check for NaNs, pass the same variable as both *> arguments. *> *> A compiler must assume that the two arguments are *> not the same variable, and the test will not be optimized away. *> Interprocedural or whole-program optimization may delete this *> test. The ISNAN functions will be replaced by the correct *> Fortran 03 intrinsic once the intrinsic is widely available. *> \endverbatim * * Arguments: * ========== * *> \param[in] DIN1 *> \verbatim *> DIN1 is DOUBLE PRECISION *> \endverbatim *> *> \param[in] DIN2 *> \verbatim *> DIN2 is DOUBLE PRECISION *> Two numbers to compare for inequality. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERauxiliary * * ===================================================================== LOGICAL FUNCTION GAL_DLAISNAN(DIN1,DIN2) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. DOUBLE PRECISION DIN1,DIN2 * .. * * ===================================================================== * * .. Executable Statements .. GAL_DLAISNAN=(DIN1.NE.DIN2) RETURN END ga-5.9.2/LinAlg/lapack+blas/gal_dlamch.f000066400000000000000000000120061500715745200176630ustar00rootroot00000000000000*> \brief \b DLAMCH * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * DOUBLE PRECISION FUNCTION DLAMCH( CMACH ) * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLAMCH determines double precision machine parameters. *> \endverbatim * * Arguments: * ========== * *> \param[in] CMACH *> \verbatim *> Specifies the value to be returned by DLAMCH: *> = 'E' or 'e', DLAMCH := eps *> = 'S' or 's , DLAMCH := sfmin *> = 'B' or 'b', DLAMCH := base *> = 'P' or 'p', DLAMCH := eps*base *> = 'N' or 'n', DLAMCH := t *> = 'R' or 'r', DLAMCH := rnd *> = 'M' or 'm', DLAMCH := emin *> = 'U' or 'u', DLAMCH := rmin *> = 'L' or 'l', DLAMCH := emax *> = 'O' or 'o', DLAMCH := rmax *> where *> eps = relative machine precision *> sfmin = safe minimum, such that 1/sfmin does not overflow *> base = base of the machine *> prec = eps*base *> t = number of (base) digits in the mantissa *> rnd = 1.0 when rounding occurs in addition, 0.0 otherwise *> emin = minimum exponent before (gradual) underflow *> rmin = underflow threshold - base**(emin-1) *> emax = largest exponent before overflow *> rmax = overflow threshold - (base**emax)*(1-eps) *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup auxOTHERauxiliary * * ===================================================================== DOUBLE PRECISION FUNCTION GAL_DLAMCH(CMACH) * * -- LAPACK auxiliary routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. CHARACTERCMACH * .. * * .. Scalar Arguments .. DOUBLE PRECISION A,B * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * .. Local Scalars .. DOUBLE PRECISION RND,EPS,SFMIN,SMALL,RMACH * .. * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. Intrinsic Functions .. INTRINSIC DIGITS,EPSILON,HUGE,MAXEXPONENT, $MINEXPONENT,RADIX,TINY * .. * .. Executable Statements .. * * * Assume rounding, not chopping. Always. * RND=ONE * IF(ONE.EQ.RND)THEN EPS=EPSILON(ZERO)*0.5 ELSE EPS=EPSILON(ZERO) END IF * IF(GAL_LSAME(CMACH,'E'))THEN RMACH=EPS ELSE IF(GAL_LSAME(CMACH,'S'))THEN SFMIN=TINY(ZERO) SMALL=ONE/HUGE(ZERO) IF(SMALL.GE.SFMIN)THEN * * Use SMALL plus a bit, to avoid the possibility of rounding * causing overflow when computing 1/sfmin. * SFMIN=SMALL*(ONE+EPS) END IF RMACH=SFMIN ELSE IF(GAL_LSAME(CMACH,'B'))THEN RMACH=RADIX(ZERO) ELSE IF(GAL_LSAME(CMACH,'P'))THEN RMACH=EPS*RADIX(ZERO) ELSE IF(GAL_LSAME(CMACH,'N'))THEN RMACH=DIGITS(ZERO) ELSE IF(GAL_LSAME(CMACH,'R'))THEN RMACH=RND ELSE IF(GAL_LSAME(CMACH,'M'))THEN RMACH=MINEXPONENT(ZERO) ELSE IF(GAL_LSAME(CMACH,'U'))THEN RMACH=TINY(ZERO) ELSE IF(GAL_LSAME(CMACH,'L'))THEN RMACH=MAXEXPONENT(ZERO) ELSE IF(GAL_LSAME(CMACH,'O'))THEN RMACH=HUGE(ZERO) ELSE RMACH=ZERO END IF * GAL_DLAMCH=RMACH RETURN * * End of DLAMCH * END ************************************************************************ *> \brief \b DLAMC3 *> \details *> \b Purpose: *> \verbatim *> DLAMC3 is intended to force A and B to be stored prior to doing *> the addition of A and B , for use in situations where optimizers *> might hold one of these in a register. *> \endverbatim *> \author LAPACK is a software package provided by Univ. of Tennessee, Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd.. *> \date November 2011 *> \ingroup auxOTHERauxiliary *> *> \param[in] A *> \verbatim *> A is a DOUBLE PRECISION *> \endverbatim *> *> \param[in] B *> \verbatim *> B is a DOUBLE PRECISION *> The values A and B. *> \endverbatim *> DOUBLE PRECISION FUNCTION GAL_DLAMC3(A,B) * * -- LAPACK auxiliary routine (version 3.4.0) -- * Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd.. * November 2010 * * .. Scalar Arguments .. DOUBLE PRECISION A,B * .. * ===================================================================== * * .. Executable Statements .. * GAL_DLAMC3=A+B * RETURN * * End of DLAMC3 * END * ************************************************************************ ga-5.9.2/LinAlg/lapack+blas/gal_dlanst.f000066400000000000000000000116051500715745200177240ustar00rootroot00000000000000*> \brief \b DLANST returns the value of the 1-norm, or the Frobenius norm, or the infinity norm, or the element of largest absolute value of a real symmetric tridiagonal matrix. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLANST + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * DOUBLE PRECISION FUNCTION DLANST( NORM, N, D, E ) * * .. Scalar Arguments .. * CHARACTER NORM * INTEGER N * .. * .. Array Arguments .. * DOUBLE PRECISION D( * ), E( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLANST returns the value of the one norm, or the Frobenius norm, or *> the infinity norm, or the element of largest absolute value of a *> real symmetric tridiagonal matrix A. *> \endverbatim *> *> \return DLANST *> \verbatim *> *> DLANST = ( max(abs(A(i,j))), NORM = 'M' or 'm' *> ( *> ( norm1(A), NORM = '1', 'O' or 'o' *> ( *> ( normI(A), NORM = 'I' or 'i' *> ( *> ( normF(A), NORM = 'F', 'f', 'E' or 'e' *> *> where norm1 denotes the one norm of a matrix (maximum column sum), *> normI denotes the infinity norm of a matrix (maximum row sum) and *> normF denotes the Frobenius norm of a matrix (square root of sum of *> squares). Note that max(abs(A(i,j))) is not a consistent matrix norm. *> \endverbatim * * Arguments: * ========== * *> \param[in] NORM *> \verbatim *> NORM is CHARACTER*1 *> Specifies the value to be returned in DLANST as described *> above. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrix A. N >= 0. When N = 0, DLANST is *> set to zero. *> \endverbatim *> *> \param[in] D *> \verbatim *> D is DOUBLE PRECISION array, dimension (N) *> The diagonal elements of A. *> \endverbatim *> *> \param[in] E *> \verbatim *> E is DOUBLE PRECISION array, dimension (N-1) *> The (n-1) sub-diagonal or super-diagonal elements of A. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERauxiliary * * ===================================================================== DOUBLE PRECISION FUNCTION GAL_DLANST(NORM,N,D,E) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. CHARACTERNORM INTEGER N * .. * .. Array Arguments .. DOUBLE PRECISION D(*),E(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * .. Local Scalars .. INTEGER I DOUBLE PRECISION ANORM,SCALE,SUM * .. * .. External Functions .. LOGICAL GAL_LSAME,GAL_DISNAN EXTERNAL GAL_LSAME,GAL_DISNAN * .. * .. External Subroutines .. EXTERNAL GAL_DLASSQ * .. * .. Intrinsic Functions .. INTRINSIC ABS,SQRT * .. * .. Executable Statements .. * IF(N.LE.0)THEN ANORM=ZERO ELSE IF(GAL_LSAME(NORM,'M'))THEN * * Find max(abs(A(i,j))). * ANORM=ABS(D(N)) DO 10I=1,N-1 SUM=ABS(D(I)) IF(ANORM.LT.SUM.OR.GAL_DISNAN(SUM))ANORM=SUM SUM=ABS(E(I)) IF(ANORM.LT.SUM.OR.GAL_DISNAN(SUM))ANORM=SUM 10 CONTINUE ELSE IF(GAL_LSAME(NORM,'O').OR.NORM.EQ.'1'.OR. $GAL_LSAME(NORM,'I'))THEN * * Find norm1(A). * IF(N.EQ.1)THEN ANORM=ABS(D(1)) ELSE ANORM=ABS(D(1))+ABS(E(1)) SUM=ABS(E(N-1))+ABS(D(N)) IF(ANORM.LT.SUM.OR.GAL_DISNAN(SUM))ANORM=SUM DO 20I=2,N-1 SUM=ABS(D(I))+ABS(E(I))+ABS(E(I-1)) IF(ANORM.LT.SUM.OR.GAL_DISNAN(SUM))ANORM=SUM 20 CONTINUE END IF ELSE IF((GAL_LSAME(NORM,'F')).OR.(GAL_LSAME(NORM,'E')))THEN * * Find normF(A). * SCALE=ZERO SUM=ONE IF(N.GT.1)THEN CALL GAL_DLASSQ(N-1,E,1,SCALE,SUM) SUM=2*SUM END IF CALL GAL_DLASSQ(N,D,1,SCALE,SUM) ANORM=SCALE*SQRT(SUM) END IF * GAL_DLANST=ANORM RETURN * * End of DLANST * END ga-5.9.2/LinAlg/lapack+blas/gal_dlansy.f000066400000000000000000000147771500715745200177460ustar00rootroot00000000000000*> \brief \b DLANSY returns the value of the 1-norm, or the Frobenius norm, or the infinity norm, or the element of largest absolute value of a real symmetric matrix. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLANSY + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * DOUBLE PRECISION FUNCTION DLANSY( NORM, UPLO, N, A, LDA, WORK ) * * .. Scalar Arguments .. * CHARACTER NORM, UPLO * INTEGER LDA, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), WORK( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLANSY returns the value of the one norm, or the Frobenius norm, or *> the infinity norm, or the element of largest absolute value of a *> real symmetric matrix A. *> \endverbatim *> *> \return DLANSY *> \verbatim *> *> DLANSY = ( max(abs(A(i,j))), NORM = 'M' or 'm' *> ( *> ( norm1(A), NORM = '1', 'O' or 'o' *> ( *> ( normI(A), NORM = 'I' or 'i' *> ( *> ( normF(A), NORM = 'F', 'f', 'E' or 'e' *> *> where norm1 denotes the one norm of a matrix (maximum column sum), *> normI denotes the infinity norm of a matrix (maximum row sum) and *> normF denotes the Frobenius norm of a matrix (square root of sum of *> squares). Note that max(abs(A(i,j))) is not a consistent matrix norm. *> \endverbatim * * Arguments: * ========== * *> \param[in] NORM *> \verbatim *> NORM is CHARACTER*1 *> Specifies the value to be returned in DLANSY as described *> above. *> \endverbatim *> *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> Specifies whether the upper or lower triangular part of the *> symmetric matrix A is to be referenced. *> = 'U': Upper triangular part of A is referenced *> = 'L': Lower triangular part of A is referenced *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrix A. N >= 0. When N = 0, DLANSY is *> set to zero. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> 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. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(N,1). *> \endverbatim *> *> \param[out] WORK *> \verbatim *> WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)), *> where LWORK >= N when NORM = 'I' or '1' or 'O'; otherwise, *> WORK is not referenced. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup doubleSYauxiliary * * ===================================================================== DOUBLE PRECISION FUNCTION GAL_DLANSY(NORM,UPLO,N,A,LDA,WORK) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. CHARACTERNORM,UPLO INTEGER LDA,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),WORK(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * .. Local Scalars .. INTEGER I,J DOUBLE PRECISION ABSA,SCALE,SUM,VALUE * .. * .. External Subroutines .. EXTERNAL GAL_DLASSQ * .. * .. External Functions .. LOGICAL GAL_LSAME,GAL_DISNAN EXTERNAL GAL_LSAME,GAL_DISNAN * .. * .. Intrinsic Functions .. INTRINSIC ABS,SQRT * .. * .. Executable Statements .. * IF(N.EQ.0)THEN VALUE=ZERO ELSE IF(GAL_LSAME(NORM,'M'))THEN * * Find max(abs(A(i,j))). * VALUE=ZERO IF(GAL_LSAME(UPLO,'U'))THEN DO 20J=1,N DO 10I=1,J SUM=ABS(A(I,J)) IF(VALUE.LT.SUM.OR.GAL_DISNAN(SUM))VALUE=SUM 10 CONTINUE 20 CONTINUE ELSE DO 40J=1,N DO 30I=J,N SUM=ABS(A(I,J)) IF(VALUE.LT.SUM.OR.GAL_DISNAN(SUM))VALUE=SUM 30 CONTINUE 40 CONTINUE END IF ELSE IF((GAL_LSAME(NORM,'I')).OR.(GAL_LSAME(NORM,'O')).OR. $(NORM.EQ.'1'))THEN * * Find normI(A) ( = norm1(A), since A is symmetric). * VALUE=ZERO IF(GAL_LSAME(UPLO,'U'))THEN DO 60J=1,N SUM=ZERO DO 50I=1,J-1 ABSA=ABS(A(I,J)) SUM=SUM+ABSA WORK(I)=WORK(I)+ABSA 50 CONTINUE WORK(J)=SUM+ABS(A(J,J)) 60 CONTINUE DO 70I=1,N SUM=WORK(I) IF(VALUE.LT.SUM.OR.GAL_DISNAN(SUM))VALUE=SUM 70 CONTINUE ELSE DO 80I=1,N WORK(I)=ZERO 80 CONTINUE DO 100J=1,N SUM=WORK(J)+ABS(A(J,J)) DO 90I=J+1,N ABSA=ABS(A(I,J)) SUM=SUM+ABSA WORK(I)=WORK(I)+ABSA 90 CONTINUE IF(VALUE.LT.SUM.OR.GAL_DISNAN(SUM))VALUE=SUM 100 CONTINUE END IF ELSE IF((GAL_LSAME(NORM,'F')).OR.(GAL_LSAME(NORM,'E')))THEN * * Find normF(A). * SCALE=ZERO SUM=ONE IF(GAL_LSAME(UPLO,'U'))THEN DO 110J=2,N CALL GAL_DLASSQ(J-1,A(1,J),1,SCALE,SUM) 110 CONTINUE ELSE DO 120J=1,N-1 CALL GAL_DLASSQ(N-J,A(J+1,J),1,SCALE,SUM) 120 CONTINUE END IF SUM=2*SUM CALL GAL_DLASSQ(N,A,LDA+1,SCALE,SUM) VALUE=SCALE*SQRT(SUM) END IF * GAL_DLANSY=VALUE RETURN * * End of DLANSY * END ga-5.9.2/LinAlg/lapack+blas/gal_dlapy2.f000066400000000000000000000046351500715745200176370ustar00rootroot00000000000000*> \brief \b DLAPY2 returns sqrt(x2+y2). * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLAPY2 + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * DOUBLE PRECISION FUNCTION DLAPY2( X, Y ) * * .. Scalar Arguments .. * DOUBLE PRECISION X, Y * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLAPY2 returns sqrt(x**2+y**2), taking care not to cause unnecessary *> overflow. *> \endverbatim * * Arguments: * ========== * *> \param[in] X *> \verbatim *> X is DOUBLE PRECISION *> \endverbatim *> *> \param[in] Y *> \verbatim *> Y is DOUBLE PRECISION *> X and Y specify the values x and y. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERauxiliary * * ===================================================================== DOUBLE PRECISION FUNCTION GAL_DLAPY2(X,Y) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. DOUBLE PRECISION X,Y * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO PARAMETER (ZERO=0.0D0) DOUBLE PRECISION ONE PARAMETER (ONE=1.0D0) * .. * .. Local Scalars .. DOUBLE PRECISION W,XABS,YABS,Z * .. * .. Intrinsic Functions .. INTRINSIC ABS,MAX,MIN,SQRT * .. * .. Executable Statements .. * XABS=ABS(X) YABS=ABS(Y) W=MAX(XABS,YABS) Z=MIN(XABS,YABS) IF(Z.EQ.ZERO)THEN GAL_DLAPY2=W ELSE GAL_DLAPY2=W*SQRT(ONE+(Z/W)**2) END IF RETURN * * End of DLAPY2 * END ga-5.9.2/LinAlg/lapack+blas/gal_dlarf.f000066400000000000000000000133621500715745200175310ustar00rootroot00000000000000*> \brief \b DLARF applies an elementary reflector to a general rectangular matrix. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLARF + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLARF( SIDE, M, N, V, INCV, TAU, C, LDC, WORK ) * * .. Scalar Arguments .. * CHARACTER SIDE * INTEGER INCV, LDC, M, N * DOUBLE PRECISION TAU * .. * .. Array Arguments .. * DOUBLE PRECISION C( LDC, * ), V( * ), WORK( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLARF applies a real elementary reflector H to a real m by n matrix *> C, from either the left or the right. H is represented in the form *> *> H = I - tau * v * v**T *> *> where tau is a real scalar and v is a real vector. *> *> If tau = 0, then H is taken to be the unit matrix. *> \endverbatim * * Arguments: * ========== * *> \param[in] SIDE *> \verbatim *> SIDE is CHARACTER*1 *> = 'L': form H * C *> = 'R': form C * H *> \endverbatim *> *> \param[in] M *> \verbatim *> M is INTEGER *> The number of rows of the matrix C. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix C. *> \endverbatim *> *> \param[in] V *> \verbatim *> V is DOUBLE PRECISION array, dimension *> (1 + (M-1)*abs(INCV)) if SIDE = 'L' *> or (1 + (N-1)*abs(INCV)) if SIDE = 'R' *> The vector v in the representation of H. V is not used if *> TAU = 0. *> \endverbatim *> *> \param[in] INCV *> \verbatim *> INCV is INTEGER *> The increment between elements of v. INCV <> 0. *> \endverbatim *> *> \param[in] TAU *> \verbatim *> TAU is DOUBLE PRECISION *> The value tau in the representation of H. *> \endverbatim *> *> \param[in,out] C *> \verbatim *> C is DOUBLE PRECISION array, dimension (LDC,N) *> On entry, the m by n matrix C. *> On exit, C is overwritten by the matrix H * C if SIDE = 'L', *> or C * H if SIDE = 'R'. *> \endverbatim *> *> \param[in] LDC *> \verbatim *> LDC is INTEGER *> The leading dimension of the array C. LDC >= max(1,M). *> \endverbatim *> *> \param[out] WORK *> \verbatim *> WORK is DOUBLE PRECISION array, dimension *> (N) if SIDE = 'L' *> or (M) if SIDE = 'R' *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup doubleOTHERauxiliary * * ===================================================================== SUBROUTINE GAL_DLARF(SIDE,M,N,V,INCV,TAU,C,LDC,WORK) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. CHARACTERSIDE INTEGER INCV,LDC,M,N DOUBLE PRECISION TAU * .. * .. Array Arguments .. DOUBLE PRECISION C(LDC,*),V(*),WORK(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * .. Local Scalars .. LOGICAL APPLYLEFT INTEGER I,LASTV,LASTC * .. * .. External Subroutines .. EXTERNAL GAL_DGEMV,GAL_DGER * .. * .. External Functions .. LOGICAL GAL_LSAME INTEGER GAL_ILADLR,GAL_ILADLC EXTERNAL GAL_LSAME,GAL_ILADLR,GAL_ILADLC * .. * .. Executable Statements .. * APPLYLEFT=GAL_LSAME(SIDE,'L') LASTV=0 LASTC=0 IF(TAU.NE.ZERO)THEN ! SETUPVARIABLESFORSCANNINGV.LASTVBEGINSPOINTINGTO THEEND ! OFV. IF(APPLYLEFT)THEN LASTV=M ELSE LASTV=N END IF IF(INCV.GT.0)THEN I=1+(LASTV-1)*INCV ELSE I=1 END IF ! LOOKFORTHELASTNON-ZEROROWINV. DO WHILE(LASTV.GT.0.AND.V(I).EQ.ZERO) LASTV=LASTV-1 I=I-INCV END DO IF(APPLYLEFT)THEN ! SCANFORTHELASTNON-ZEROCOLUMNINC(1:LASTV,:). LASTC=GAL_ILADLC(LASTV,N,C,LDC) ELSE ! SCANFORTHELASTNON-ZEROROWINC(:,1:LASTV). LASTC=GAL_ILADLR(M,LASTV,C,LDC) END IF END IF ! NOTETHATLASTC.EQ.0RENDERSTHEBLASOPERATIONSNULL;NOSPECIAL ! CASEISNEEDEDATTHISLEVEL. IF(APPLYLEFT)THEN * * Form H * C * IF(LASTV.GT.0)THEN * * w(1:lastc,1) := C(1:lastv,1:lastc)**T * v(1:lastv,1) * CALL GAL_DGEMV('TRANSPOSE',LASTV,LASTC,ONE,C,LDC,V,INCV, $ZERO,WORK,1) * * C(1:lastv,1:lastc) := C(...) - v(1:lastv,1) * w(1:lastc,1)**T * CALL GAL_DGER(LASTV,LASTC,-TAU,V,INCV,WORK,1,C,LDC) END IF ELSE * * Form C * H * IF(LASTV.GT.0)THEN * * w(1:lastc,1) := C(1:lastc,1:lastv) * v(1:lastv,1) * CALL GAL_DGEMV('NOTRANSPOSE',LASTC,LASTV,ONE,C,LDC, $V,INCV,ZERO,WORK,1) * * C(1:lastc,1:lastv) := C(...) - w(1:lastc,1) * v(1:lastv,1)**T * CALL GAL_DGER(LASTC,LASTV,-TAU,WORK,1,V,INCV,C,LDC) END IF END IF RETURN * * End of DLARF * END ga-5.9.2/LinAlg/lapack+blas/gal_dlarfb.f000066400000000000000000000425231500715745200176740ustar00rootroot00000000000000*> \brief \b DLARFB applies a block reflector or its transpose to a general rectangular matrix. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLARFB + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLARFB( SIDE, TRANS, DIRECT, STOREV, M, N, K, V, LDV, * T, LDT, C, LDC, WORK, LDWORK ) * * .. Scalar Arguments .. * CHARACTER DIRECT, SIDE, STOREV, TRANS * INTEGER K, LDC, LDT, LDV, LDWORK, M, N * .. * .. Array Arguments .. * DOUBLE PRECISION C( LDC, * ), T( LDT, * ), V( LDV, * ), * $ WORK( LDWORK, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLARFB applies a real block reflector H or its transpose H**T to a *> real m by n matrix C, from either the left or the right. *> \endverbatim * * Arguments: * ========== * *> \param[in] SIDE *> \verbatim *> SIDE is CHARACTER*1 *> = 'L': apply H or H**T from the Left *> = 'R': apply H or H**T from the Right *> \endverbatim *> *> \param[in] TRANS *> \verbatim *> TRANS is CHARACTER*1 *> = 'N': apply H (No transpose) *> = 'T': apply H**T (Transpose) *> \endverbatim *> *> \param[in] DIRECT *> \verbatim *> DIRECT is CHARACTER*1 *> Indicates how H is formed from a product of elementary *> reflectors *> = 'F': H = H(1) H(2) . . . H(k) (Forward) *> = 'B': H = H(k) . . . H(2) H(1) (Backward) *> \endverbatim *> *> \param[in] STOREV *> \verbatim *> STOREV is CHARACTER*1 *> Indicates how the vectors which define the elementary *> reflectors are stored: *> = 'C': Columnwise *> = 'R': Rowwise *> \endverbatim *> *> \param[in] M *> \verbatim *> M is INTEGER *> The number of rows of the matrix C. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix C. *> \endverbatim *> *> \param[in] K *> \verbatim *> K is INTEGER *> The order of the matrix T (= the number of elementary *> reflectors whose product defines the block reflector). *> \endverbatim *> *> \param[in] V *> \verbatim *> V is DOUBLE PRECISION array, dimension *> (LDV,K) if STOREV = 'C' *> (LDV,M) if STOREV = 'R' and SIDE = 'L' *> (LDV,N) if STOREV = 'R' and SIDE = 'R' *> The matrix V. See Further Details. *> \endverbatim *> *> \param[in] LDV *> \verbatim *> LDV is INTEGER *> The leading dimension of the array V. *> If STOREV = 'C' and SIDE = 'L', LDV >= max(1,M); *> if STOREV = 'C' and SIDE = 'R', LDV >= max(1,N); *> if STOREV = 'R', LDV >= K. *> \endverbatim *> *> \param[in] T *> \verbatim *> T is DOUBLE PRECISION array, dimension (LDT,K) *> The triangular k by k matrix T in the representation of the *> block reflector. *> \endverbatim *> *> \param[in] LDT *> \verbatim *> LDT is INTEGER *> The leading dimension of the array T. LDT >= K. *> \endverbatim *> *> \param[in,out] C *> \verbatim *> C is DOUBLE PRECISION array, dimension (LDC,N) *> On entry, the m by n matrix C. *> On exit, C is overwritten by H*C or H**T*C or C*H or C*H**T. *> \endverbatim *> *> \param[in] LDC *> \verbatim *> LDC is INTEGER *> The leading dimension of the array C. LDC >= max(1,M). *> \endverbatim *> *> \param[out] WORK *> \verbatim *> WORK is DOUBLE PRECISION array, dimension (LDWORK,K) *> \endverbatim *> *> \param[in] LDWORK *> \verbatim *> LDWORK is INTEGER *> The leading dimension of the array WORK. *> If SIDE = 'L', LDWORK >= max(1,N); *> if SIDE = 'R', LDWORK >= max(1,M). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date June 2013 * *> \ingroup doubleOTHERauxiliary * *> \par Further Details: * ===================== *> *> \verbatim *> *> The shape of the matrix V and the storage of the vectors which define *> the H(i) is best illustrated by the following example with n = 5 and *> k = 3. The elements equal to 1 are not stored; the corresponding *> array elements are modified but restored on exit. The rest of the *> array is not used. *> *> DIRECT = 'F' and STOREV = 'C': DIRECT = 'F' and STOREV = 'R': *> *> V = ( 1 ) V = ( 1 v1 v1 v1 v1 ) *> ( v1 1 ) ( 1 v2 v2 v2 ) *> ( v1 v2 1 ) ( 1 v3 v3 ) *> ( v1 v2 v3 ) *> ( v1 v2 v3 ) *> *> DIRECT = 'B' and STOREV = 'C': DIRECT = 'B' and STOREV = 'R': *> *> V = ( v1 v2 v3 ) V = ( v1 v1 1 ) *> ( v1 v2 v3 ) ( v2 v2 v2 1 ) *> ( 1 v2 v3 ) ( v3 v3 v3 v3 1 ) *> ( 1 v3 ) *> ( 1 ) *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DLARFB(SIDE,TRANS,DIRECT,STOREV,M,N,K,V,LDV, $T,LDT,C,LDC,WORK,LDWORK) * * -- LAPACK auxiliary routine (version 3.5.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * June 2013 * * .. Scalar Arguments .. CHARACTERDIRECT,SIDE,STOREV,TRANS INTEGER K,LDC,LDT,LDV,LDWORK,M,N * .. * .. Array Arguments .. DOUBLE PRECISION C(LDC,*),T(LDT,*),V(LDV,*), $WORK(LDWORK,*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE PARAMETER (ONE=1.0D+0) * .. * .. Local Scalars .. CHARACTERTRANST INTEGER I,J * .. * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_DCOPY,GAL_DGEMM,GAL_DTRMM * .. * .. Executable Statements .. * * Quick return if possible * IF(M.LE.0.OR.N.LE.0) $RETURN * IF(GAL_LSAME(TRANS,'N'))THEN TRANST='T' ELSE TRANST='N' END IF * IF(GAL_LSAME(STOREV,'C'))THEN * IF(GAL_LSAME(DIRECT,'F'))THEN * * Let V = ( V1 ) (first K rows) * ( V2 ) * where V1 is unit lower triangular. * IF(GAL_LSAME(SIDE,'L'))THEN * * Form H * C or H**T * C where C = ( C1 ) * ( C2 ) * * W := C**T * V = (C1**T * V1 + C2**T * V2) (stored in WORK) * * W := C1**T * DO 10J=1,K CALL GAL_DCOPY(N,C(J,1),LDC,WORK(1,J),1) 10 CONTINUE * * W := W * V1 * CALL GAL_DTRMM('RIGHT','LOWER','NOTRANSPOSE','UNIT',N, $K,ONE,V,LDV,WORK,LDWORK) IF(M.GT.K)THEN * * W := W + C2**T * V2 * CALL GAL_DGEMM('TRANSPOSE','NOTRANSPOSE',N,K,M-K, $ONE,C(K+1,1),LDC,V(K+1,1),LDV, $ONE,WORK,LDWORK) END IF * * W := W * T**T or W * T * CALL GAL_DTRMM('RIGHT','UPPER',TRANST,'NON-UNIT',N,K, $ONE,T,LDT,WORK,LDWORK) * * C := C - V * W**T * IF(M.GT.K)THEN * * C2 := C2 - V2 * W**T * CALL GAL_DGEMM('NOTRANSPOSE','TRANSPOSE',M-K,N,K, $-ONE,V(K+1,1),LDV,WORK,LDWORK,ONE, $C(K+1,1),LDC) END IF * * W := W * V1**T * CALL GAL_DTRMM('RIGHT','LOWER','TRANSPOSE','UNIT',N,K, $ONE,V,LDV,WORK,LDWORK) * * C1 := C1 - W**T * DO 30J=1,K DO 20I=1,N C(J,I)=C(J,I)-WORK(I,J) 20 CONTINUE 30 CONTINUE * ELSE IF(GAL_LSAME(SIDE,'R'))THEN * * Form C * H or C * H**T where C = ( C1 C2 ) * * W := C * V = (C1*V1 + C2*V2) (stored in WORK) * * W := C1 * DO 40J=1,K CALL GAL_DCOPY(M,C(1,J),1,WORK(1,J),1) 40 CONTINUE * * W := W * V1 * CALL GAL_DTRMM('RIGHT','LOWER','NOTRANSPOSE','UNIT',M, $K,ONE,V,LDV,WORK,LDWORK) IF(N.GT.K)THEN * * W := W + C2 * V2 * CALL GAL_DGEMM('NOTRANSPOSE','NOTRANSPOSE',M,K,N-K, $ONE,C(1,K+1),LDC,V(K+1,1),LDV, $ONE,WORK,LDWORK) END IF * * W := W * T or W * T**T * CALL GAL_DTRMM('RIGHT','UPPER',TRANS,'NON-UNIT',M,K, $ONE,T,LDT,WORK,LDWORK) * * C := C - W * V**T * IF(N.GT.K)THEN * * C2 := C2 - W * V2**T * CALL GAL_DGEMM('NOTRANSPOSE','TRANSPOSE',M,N-K,K, $-ONE,WORK,LDWORK,V(K+1,1),LDV,ONE, $C(1,K+1),LDC) END IF * * W := W * V1**T * CALL GAL_DTRMM('RIGHT','LOWER','TRANSPOSE','UNIT',M,K, $ONE,V,LDV,WORK,LDWORK) * * C1 := C1 - W * DO 60J=1,K DO 50I=1,M C(I,J)=C(I,J)-WORK(I,J) 50 CONTINUE 60 CONTINUE END IF * ELSE * * Let V = ( V1 ) * ( V2 ) (last K rows) * where V2 is unit upper triangular. * IF(GAL_LSAME(SIDE,'L'))THEN * * Form H * C or H**T * C where C = ( C1 ) * ( C2 ) * * W := C**T * V = (C1**T * V1 + C2**T * V2) (stored in WORK) * * W := C2**T * DO 70J=1,K CALL GAL_DCOPY(N,C(M-K+J,1),LDC,WORK(1,J),1) 70 CONTINUE * * W := W * V2 * CALL GAL_DTRMM('RIGHT','UPPER','NOTRANSPOSE','UNIT',N, $K,ONE,V(M-K+1,1),LDV,WORK,LDWORK) IF(M.GT.K)THEN * * W := W + C1**T * V1 * CALL GAL_DGEMM('TRANSPOSE','NOTRANSPOSE',N,K,M-K, $ONE,C,LDC,V,LDV,ONE,WORK,LDWORK) END IF * * W := W * T**T or W * T * CALL GAL_DTRMM('RIGHT','LOWER',TRANST,'NON-UNIT',N,K, $ONE,T,LDT,WORK,LDWORK) * * C := C - V * W**T * IF(M.GT.K)THEN * * C1 := C1 - V1 * W**T * CALL GAL_DGEMM('NOTRANSPOSE','TRANSPOSE',M-K,N,K, $-ONE,V,LDV,WORK,LDWORK,ONE,C,LDC) END IF * * W := W * V2**T * CALL GAL_DTRMM('RIGHT','UPPER','TRANSPOSE','UNIT',N,K, $ONE,V(M-K+1,1),LDV,WORK,LDWORK) * * C2 := C2 - W**T * DO 90J=1,K DO 80I=1,N C(M-K+J,I)=C(M-K+J,I)-WORK(I,J) 80 CONTINUE 90 CONTINUE * ELSE IF(GAL_LSAME(SIDE,'R'))THEN * * Form C * H or C * H**T where C = ( C1 C2 ) * * W := C * V = (C1*V1 + C2*V2) (stored in WORK) * * W := C2 * DO 100J=1,K CALL GAL_DCOPY(M,C(1,N-K+J),1,WORK(1,J),1) 100 CONTINUE * * W := W * V2 * CALL GAL_DTRMM('RIGHT','UPPER','NOTRANSPOSE','UNIT',M, $K,ONE,V(N-K+1,1),LDV,WORK,LDWORK) IF(N.GT.K)THEN * * W := W + C1 * V1 * CALL GAL_DGEMM('NOTRANSPOSE','NOTRANSPOSE',M,K,N-K, $ONE,C,LDC,V,LDV,ONE,WORK,LDWORK) END IF * * W := W * T or W * T**T * CALL GAL_DTRMM('RIGHT','LOWER',TRANS,'NON-UNIT',M,K, $ONE,T,LDT,WORK,LDWORK) * * C := C - W * V**T * IF(N.GT.K)THEN * * C1 := C1 - W * V1**T * CALL GAL_DGEMM('NOTRANSPOSE','TRANSPOSE',M,N-K,K, $-ONE,WORK,LDWORK,V,LDV,ONE,C,LDC) END IF * * W := W * V2**T * CALL GAL_DTRMM('RIGHT','UPPER','TRANSPOSE','UNIT',M,K, $ONE,V(N-K+1,1),LDV,WORK,LDWORK) * * C2 := C2 - W * DO 120J=1,K DO 110I=1,M C(I,N-K+J)=C(I,N-K+J)-WORK(I,J) 110 CONTINUE 120 CONTINUE END IF END IF * ELSE IF(GAL_LSAME(STOREV,'R'))THEN * IF(GAL_LSAME(DIRECT,'F'))THEN * * Let V = ( V1 V2 ) (V1: first K columns) * where V1 is unit upper triangular. * IF(GAL_LSAME(SIDE,'L'))THEN * * Form H * C or H**T * C where C = ( C1 ) * ( C2 ) * * W := C**T * V**T = (C1**T * V1**T + C2**T * V2**T) (stored in WORK) * * W := C1**T * DO 130J=1,K CALL GAL_DCOPY(N,C(J,1),LDC,WORK(1,J),1) 130 CONTINUE * * W := W * V1**T * CALL GAL_DTRMM('RIGHT','UPPER','TRANSPOSE','UNIT',N,K, $ONE,V,LDV,WORK,LDWORK) IF(M.GT.K)THEN * * W := W + C2**T * V2**T * CALL GAL_DGEMM('TRANSPOSE','TRANSPOSE',N,K,M-K,ONE, $C(K+1,1),LDC,V(1,K+1),LDV,ONE, $WORK,LDWORK) END IF * * W := W * T**T or W * T * CALL GAL_DTRMM('RIGHT','UPPER',TRANST,'NON-UNIT',N,K, $ONE,T,LDT,WORK,LDWORK) * * C := C - V**T * W**T * IF(M.GT.K)THEN * * C2 := C2 - V2**T * W**T * CALL GAL_DGEMM('TRANSPOSE','TRANSPOSE',M-K,N,K,-ONE, $V(1,K+1),LDV,WORK,LDWORK,ONE, $C(K+1,1),LDC) END IF * * W := W * V1 * CALL GAL_DTRMM('RIGHT','UPPER','NOTRANSPOSE','UNIT',N, $K,ONE,V,LDV,WORK,LDWORK) * * C1 := C1 - W**T * DO 150J=1,K DO 140I=1,N C(J,I)=C(J,I)-WORK(I,J) 140 CONTINUE 150 CONTINUE * ELSE IF(GAL_LSAME(SIDE,'R'))THEN * * Form C * H or C * H**T where C = ( C1 C2 ) * * W := C * V**T = (C1*V1**T + C2*V2**T) (stored in WORK) * * W := C1 * DO 160J=1,K CALL GAL_DCOPY(M,C(1,J),1,WORK(1,J),1) 160 CONTINUE * * W := W * V1**T * CALL GAL_DTRMM('RIGHT','UPPER','TRANSPOSE','UNIT',M,K, $ONE,V,LDV,WORK,LDWORK) IF(N.GT.K)THEN * * W := W + C2 * V2**T * CALL GAL_DGEMM('NOTRANSPOSE','TRANSPOSE',M,K,N-K, $ONE,C(1,K+1),LDC,V(1,K+1),LDV, $ONE,WORK,LDWORK) END IF * * W := W * T or W * T**T * CALL GAL_DTRMM('RIGHT','UPPER',TRANS,'NON-UNIT',M,K, $ONE,T,LDT,WORK,LDWORK) * * C := C - W * V * IF(N.GT.K)THEN * * C2 := C2 - W * V2 * CALL GAL_DGEMM('NOTRANSPOSE','NOTRANSPOSE',M,N-K,K, $-ONE,WORK,LDWORK,V(1,K+1),LDV,ONE, $C(1,K+1),LDC) END IF * * W := W * V1 * CALL GAL_DTRMM('RIGHT','UPPER','NOTRANSPOSE','UNIT',M, $K,ONE,V,LDV,WORK,LDWORK) * * C1 := C1 - W * DO 180J=1,K DO 170I=1,M C(I,J)=C(I,J)-WORK(I,J) 170 CONTINUE 180 CONTINUE * END IF * ELSE * * Let V = ( V1 V2 ) (V2: last K columns) * where V2 is unit lower triangular. * IF(GAL_LSAME(SIDE,'L'))THEN * * Form H * C or H**T * C where C = ( C1 ) * ( C2 ) * * W := C**T * V**T = (C1**T * V1**T + C2**T * V2**T) (stored in WORK) * * W := C2**T * DO 190J=1,K CALL GAL_DCOPY(N,C(M-K+J,1),LDC,WORK(1,J),1) 190 CONTINUE * * W := W * V2**T * CALL GAL_DTRMM('RIGHT','LOWER','TRANSPOSE','UNIT',N,K, $ONE,V(1,M-K+1),LDV,WORK,LDWORK) IF(M.GT.K)THEN * * W := W + C1**T * V1**T * CALL GAL_DGEMM('TRANSPOSE','TRANSPOSE',N,K,M-K,ONE, $C,LDC,V,LDV,ONE,WORK,LDWORK) END IF * * W := W * T**T or W * T * CALL GAL_DTRMM('RIGHT','LOWER',TRANST,'NON-UNIT',N,K, $ONE,T,LDT,WORK,LDWORK) * * C := C - V**T * W**T * IF(M.GT.K)THEN * * C1 := C1 - V1**T * W**T * CALL GAL_DGEMM('TRANSPOSE','TRANSPOSE',M-K,N,K,-ONE, $V,LDV,WORK,LDWORK,ONE,C,LDC) END IF * * W := W * V2 * CALL GAL_DTRMM('RIGHT','LOWER','NOTRANSPOSE','UNIT',N, $K,ONE,V(1,M-K+1),LDV,WORK,LDWORK) * * C2 := C2 - W**T * DO 210J=1,K DO 200I=1,N C(M-K+J,I)=C(M-K+J,I)-WORK(I,J) 200 CONTINUE 210 CONTINUE * ELSE IF(GAL_LSAME(SIDE,'R'))THEN * * Form C * H or C * H' where C = ( C1 C2 ) * * W := C * V**T = (C1*V1**T + C2*V2**T) (stored in WORK) * * W := C2 * DO 220J=1,K CALL GAL_DCOPY(M,C(1,N-K+J),1,WORK(1,J),1) 220 CONTINUE * * W := W * V2**T * CALL GAL_DTRMM('RIGHT','LOWER','TRANSPOSE','UNIT',M,K, $ONE,V(1,N-K+1),LDV,WORK,LDWORK) IF(N.GT.K)THEN * * W := W + C1 * V1**T * CALL GAL_DGEMM('NOTRANSPOSE','TRANSPOSE',M,K,N-K, $ONE,C,LDC,V,LDV,ONE,WORK,LDWORK) END IF * * W := W * T or W * T**T * CALL GAL_DTRMM('RIGHT','LOWER',TRANS,'NON-UNIT',M,K, $ONE,T,LDT,WORK,LDWORK) * * C := C - W * V * IF(N.GT.K)THEN * * C1 := C1 - W * V1 * CALL GAL_DGEMM('NOTRANSPOSE','NOTRANSPOSE',M,N-K,K, $-ONE,WORK,LDWORK,V,LDV,ONE,C,LDC) END IF * * W := W * V2 * CALL GAL_DTRMM('RIGHT','LOWER','NOTRANSPOSE','UNIT',M, $K,ONE,V(1,N-K+1),LDV,WORK,LDWORK) * * C1 := C1 - W * DO 240J=1,K DO 230I=1,M C(I,N-K+J)=C(I,N-K+J)-WORK(I,J) 230 CONTINUE 240 CONTINUE * END IF * END IF END IF * RETURN * * End of DLARFB * END ga-5.9.2/LinAlg/lapack+blas/gal_dlarfg.f000066400000000000000000000112401500715745200176710ustar00rootroot00000000000000*> \brief \b DLARFG generates an elementary reflector (Householder matrix). * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLARFG + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLARFG( N, ALPHA, X, INCX, TAU ) * * .. Scalar Arguments .. * INTEGER INCX, N * DOUBLE PRECISION ALPHA, TAU * .. * .. Array Arguments .. * DOUBLE PRECISION X( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLARFG generates a real elementary reflector H of order n, such *> that *> *> H * ( alpha ) = ( beta ), H**T * H = I. *> ( x ) ( 0 ) *> *> where alpha and beta are scalars, and x is an (n-1)-element real *> vector. H is represented in the form *> *> H = I - tau * ( 1 ) * ( 1 v**T ) , *> ( v ) *> *> where tau is a real scalar and v is a real (n-1)-element *> vector. *> *> If the elements of x are all zero, then tau = 0 and H is taken to be *> the unit matrix. *> *> Otherwise 1 <= tau <= 2. *> \endverbatim * * Arguments: * ========== * *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the elementary reflector. *> \endverbatim *> *> \param[in,out] ALPHA *> \verbatim *> ALPHA is DOUBLE PRECISION *> On entry, the value alpha. *> On exit, it is overwritten with the value beta. *> \endverbatim *> *> \param[in,out] X *> \verbatim *> X is DOUBLE PRECISION array, dimension *> (1+(N-2)*abs(INCX)) *> On entry, the vector x. *> On exit, it is overwritten with the vector v. *> \endverbatim *> *> \param[in] INCX *> \verbatim *> INCX is INTEGER *> The increment between elements of X. INCX > 0. *> \endverbatim *> *> \param[out] TAU *> \verbatim *> TAU is DOUBLE PRECISION *> The value tau. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup doubleOTHERauxiliary * * ===================================================================== SUBROUTINE GAL_DLARFG(N,ALPHA,X,INCX,TAU) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. INTEGER INCX,N DOUBLE PRECISION ALPHA,TAU * .. * .. Array Arguments .. DOUBLE PRECISION X(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * .. Local Scalars .. INTEGER J,KNT DOUBLE PRECISION BETA,RSAFMN,SAFMIN,XNORM * .. * .. External Functions .. DOUBLE PRECISION GAL_DLAMCH,GAL_DLAPY2,GAL_DNRM2 EXTERNAL GAL_DLAMCH,GAL_DLAPY2,GAL_DNRM2 * .. * .. Intrinsic Functions .. INTRINSIC ABS,SIGN * .. * .. External Subroutines .. EXTERNAL GAL_DSCAL * .. * .. Executable Statements .. * IF(N.LE.1)THEN TAU=ZERO RETURN END IF * XNORM=GAL_DNRM2(N-1,X,INCX) * IF(XNORM.EQ.ZERO)THEN * * H = I * TAU=ZERO ELSE * * general case * BETA=-SIGN(GAL_DLAPY2(ALPHA,XNORM),ALPHA) SAFMIN=GAL_DLAMCH('S')/GAL_DLAMCH('E') KNT=0 IF(ABS(BETA).LT.SAFMIN)THEN * * XNORM, BETA may be inaccurate; scale X and recompute them * RSAFMN=ONE/SAFMIN 10 CONTINUE KNT=KNT+1 CALL GAL_DSCAL(N-1,RSAFMN,X,INCX) BETA=BETA*RSAFMN ALPHA=ALPHA*RSAFMN IF(ABS(BETA).LT.SAFMIN) $GO TO 10 * * New BETA is at most 1, at least SAFMIN * XNORM=GAL_DNRM2(N-1,X,INCX) BETA=-SIGN(GAL_DLAPY2(ALPHA,XNORM),ALPHA) END IF TAU=(BETA-ALPHA)/BETA CALL GAL_DSCAL(N-1,ONE/(ALPHA-BETA),X,INCX) * * If ALPHA is subnormal, it may lose relative accuracy * DO 20J=1,KNT BETA=BETA*SAFMIN 20 CONTINUE ALPHA=BETA END IF * RETURN * * End of DLARFG * END ga-5.9.2/LinAlg/lapack+blas/gal_dlarft.f000066400000000000000000000212341500715745200177120ustar00rootroot00000000000000*> \brief \b DLARFT forms the triangular factor T of a block reflector H = I - vtvH * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLARFT + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLARFT( DIRECT, STOREV, N, K, V, LDV, TAU, T, LDT ) * * .. Scalar Arguments .. * CHARACTER DIRECT, STOREV * INTEGER K, LDT, LDV, N * .. * .. Array Arguments .. * DOUBLE PRECISION T( LDT, * ), TAU( * ), V( LDV, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLARFT forms the triangular factor T of a real block reflector H *> of order n, which is defined as a product of k elementary reflectors. *> *> If DIRECT = 'F', H = H(1) H(2) . . . H(k) and T is upper triangular; *> *> If DIRECT = 'B', H = H(k) . . . H(2) H(1) and T is lower triangular. *> *> If STOREV = 'C', the vector which defines the elementary reflector *> H(i) is stored in the i-th column of the array V, and *> *> H = I - V * T * V**T *> *> If STOREV = 'R', the vector which defines the elementary reflector *> H(i) is stored in the i-th row of the array V, and *> *> H = I - V**T * T * V *> \endverbatim * * Arguments: * ========== * *> \param[in] DIRECT *> \verbatim *> DIRECT is CHARACTER*1 *> Specifies the order in which the elementary reflectors are *> multiplied to form the block reflector: *> = 'F': H = H(1) H(2) . . . H(k) (Forward) *> = 'B': H = H(k) . . . H(2) H(1) (Backward) *> \endverbatim *> *> \param[in] STOREV *> \verbatim *> STOREV is CHARACTER*1 *> Specifies how the vectors which define the elementary *> reflectors are stored (see also Further Details): *> = 'C': columnwise *> = 'R': rowwise *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the block reflector H. N >= 0. *> \endverbatim *> *> \param[in] K *> \verbatim *> K is INTEGER *> The order of the triangular factor T (= the number of *> elementary reflectors). K >= 1. *> \endverbatim *> *> \param[in] V *> \verbatim *> V is DOUBLE PRECISION array, dimension *> (LDV,K) if STOREV = 'C' *> (LDV,N) if STOREV = 'R' *> The matrix V. See further details. *> \endverbatim *> *> \param[in] LDV *> \verbatim *> LDV is INTEGER *> The leading dimension of the array V. *> If STOREV = 'C', LDV >= max(1,N); if STOREV = 'R', LDV >= K. *> \endverbatim *> *> \param[in] TAU *> \verbatim *> TAU is DOUBLE PRECISION array, dimension (K) *> TAU(i) must contain the scalar factor of the elementary *> reflector H(i). *> \endverbatim *> *> \param[out] T *> \verbatim *> T is DOUBLE PRECISION array, dimension (LDT,K) *> The k by k triangular factor T of the block reflector. *> If DIRECT = 'F', T is upper triangular; if DIRECT = 'B', T is *> lower triangular. The rest of the array is not used. *> \endverbatim *> *> \param[in] LDT *> \verbatim *> LDT is INTEGER *> The leading dimension of the array T. LDT >= K. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup doubleOTHERauxiliary * *> \par Further Details: * ===================== *> *> \verbatim *> *> The shape of the matrix V and the storage of the vectors which define *> the H(i) is best illustrated by the following example with n = 5 and *> k = 3. The elements equal to 1 are not stored. *> *> DIRECT = 'F' and STOREV = 'C': DIRECT = 'F' and STOREV = 'R': *> *> V = ( 1 ) V = ( 1 v1 v1 v1 v1 ) *> ( v1 1 ) ( 1 v2 v2 v2 ) *> ( v1 v2 1 ) ( 1 v3 v3 ) *> ( v1 v2 v3 ) *> ( v1 v2 v3 ) *> *> DIRECT = 'B' and STOREV = 'C': DIRECT = 'B' and STOREV = 'R': *> *> V = ( v1 v2 v3 ) V = ( v1 v1 1 ) *> ( v1 v2 v3 ) ( v2 v2 v2 1 ) *> ( 1 v2 v3 ) ( v3 v3 v3 v3 1 ) *> ( 1 v3 ) *> ( 1 ) *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DLARFT(DIRECT,STOREV,N,K,V,LDV,TAU,T,LDT) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. CHARACTERDIRECT,STOREV INTEGER K,LDT,LDV,N * .. * .. Array Arguments .. DOUBLE PRECISION T(LDT,*),TAU(*),V(LDV,*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * .. Local Scalars .. INTEGER I,J,PREVLASTV,LASTV * .. * .. External Subroutines .. EXTERNAL GAL_DGEMV,GAL_DTRMV * .. * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. Executable Statements .. * * Quick return if possible * IF(N.EQ.0) $RETURN * IF(GAL_LSAME(DIRECT,'F'))THEN PREVLASTV=N DO I=1,K PREVLASTV=MAX(I,PREVLASTV) IF(TAU(I).EQ.ZERO)THEN * * H(i) = I * DO J=1,I T(J,I)=ZERO END DO ELSE * * general case * IF(GAL_LSAME(STOREV,'C'))THEN * Skip any trailing zeros. DO LASTV=N,I+1,-1 IF(V(LASTV,I).NE.ZERO)EXIT END DO DO J=1,I-1 T(J,I)=-TAU(I)*V(I,J) END DO J=MIN(LASTV,PREVLASTV) * * T(1:i-1,i) := - tau(i) * V(i:j,1:i-1)**T * V(i:j,i) * CALL GAL_DGEMV('TRANSPOSE',J-I,I-1,-TAU(I), $V(I+1,1),LDV,V(I+1,I),1,ONE, $T(1,I),1) ELSE * Skip any trailing zeros. DO LASTV=N,I+1,-1 IF(V(I,LASTV).NE.ZERO)EXIT END DO DO J=1,I-1 T(J,I)=-TAU(I)*V(J,I) END DO J=MIN(LASTV,PREVLASTV) * * T(1:i-1,i) := - tau(i) * V(1:i-1,i:j) * V(i,i:j)**T * CALL GAL_DGEMV('NOTRANSPOSE',I-1,J-I,-TAU(I), $V(1,I+1),LDV,V(I,I+1),LDV,ONE, $T(1,I),1) END IF * * T(1:i-1,i) := T(1:i-1,1:i-1) * T(1:i-1,i) * CALL GAL_DTRMV('UPPER','NOTRANSPOSE','NON-UNIT',I-1,T, $LDT,T(1,I),1) T(I,I)=TAU(I) IF(I.GT.1)THEN PREVLASTV=MAX(PREVLASTV,LASTV) ELSE PREVLASTV=LASTV END IF END IF END DO ELSE PREVLASTV=1 DO I=K,1,-1 IF(TAU(I).EQ.ZERO)THEN * * H(i) = I * DO J=I,K T(J,I)=ZERO END DO ELSE * * general case * IF(I.LT.K)THEN IF(GAL_LSAME(STOREV,'C'))THEN * Skip any leading zeros. DO LASTV=1,I-1 IF(V(LASTV,I).NE.ZERO)EXIT END DO DO J=I+1,K T(J,I)=-TAU(I)*V(N-K+I,J) END DO J=MAX(LASTV,PREVLASTV) * * T(i+1:k,i) = -tau(i) * V(j:n-k+i,i+1:k)**T * V(j:n-k+i,i) * CALL GAL_DGEMV('TRANSPOSE',N-K+I-J,K-I,-TAU(I), $V(J,I+1),LDV,V(J,I),1,ONE, $T(I+1,I),1) ELSE * Skip any leading zeros. DO LASTV=1,I-1 IF(V(I,LASTV).NE.ZERO)EXIT END DO DO J=I+1,K T(J,I)=-TAU(I)*V(J,N-K+I) END DO J=MAX(LASTV,PREVLASTV) * * T(i+1:k,i) = -tau(i) * V(i+1:k,j:n-k+i) * V(i,j:n-k+i)**T * CALL GAL_DGEMV('NOTRANSPOSE',K-I,N-K+I-J, $-TAU(I),V(I+1,J),LDV,V(I,J),LDV, $ONE,T(I+1,I),1) END IF * * T(i+1:k,i) := T(i+1:k,i+1:k) * T(i+1:k,i) * CALL GAL_DTRMV('LOWER','NOTRANSPOSE','NON-UNIT',K-I, $T(I+1,I+1),LDT,T(I+1,I),1) IF(I.GT.1)THEN PREVLASTV=MIN(PREVLASTV,LASTV) ELSE PREVLASTV=LASTV END IF END IF T(I,I)=TAU(I) END IF END DO END IF RETURN * * End of DLARFT * END ga-5.9.2/LinAlg/lapack+blas/gal_dlartg.f000066400000000000000000000116211500715745200177120ustar00rootroot00000000000000*> \brief \b DLARTG generates a plane rotation with real cosine and real sine. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLARTG + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLARTG( F, G, CS, SN, R ) * * .. Scalar Arguments .. * DOUBLE PRECISION CS, F, G, R, SN * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLARTG generate a plane rotation so that *> *> [ CS SN ] . [ F ] = [ R ] where CS**2 + SN**2 = 1. *> [ -SN CS ] [ G ] [ 0 ] *> *> This is a slower, more accurate version of the BLAS1 routine DROTG, *> with the following other differences: *> F and G are unchanged on return. *> If G=0, then CS=1 and SN=0. *> If F=0 and (G .ne. 0), then CS=0 and SN=1 without doing any *> floating point operations (saves work in DBDSQR when *> there are zeros on the diagonal). *> *> If F exceeds G in magnitude, CS will be positive. *> \endverbatim * * Arguments: * ========== * *> \param[in] F *> \verbatim *> F is DOUBLE PRECISION *> The first component of vector to be rotated. *> \endverbatim *> *> \param[in] G *> \verbatim *> G is DOUBLE PRECISION *> The second component of vector to be rotated. *> \endverbatim *> *> \param[out] CS *> \verbatim *> CS is DOUBLE PRECISION *> The cosine of the rotation. *> \endverbatim *> *> \param[out] SN *> \verbatim *> SN is DOUBLE PRECISION *> The sine of the rotation. *> \endverbatim *> *> \param[out] R *> \verbatim *> R is DOUBLE PRECISION *> The nonzero component of the rotated vector. *> *> This version has a few statements commented out for thread safety *> (machine parameters are computed on each entry). 10 feb 03, SJH. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERauxiliary * * ===================================================================== SUBROUTINE GAL_DLARTG(F,G,CS,SN,R) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. DOUBLE PRECISION CS,F,G,R,SN * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO PARAMETER (ZERO=0.0D0) DOUBLE PRECISION ONE PARAMETER (ONE=1.0D0) DOUBLE PRECISION TWO PARAMETER (TWO=2.0D0) * .. * .. Local Scalars .. * LOGICAL FIRST INTEGER COUNT,I DOUBLE PRECISION EPS,F1,G1,SAFMIN,SAFMN2,SAFMX2,SCALE * .. * .. External Functions .. DOUBLE PRECISION GAL_DLAMCH EXTERNAL GAL_DLAMCH * .. * .. Intrinsic Functions .. INTRINSIC ABS,INT,LOG,MAX,SQRT * .. * .. Save statement .. * SAVE FIRST, SAFMX2, SAFMIN, SAFMN2 * .. * .. Data statements .. * DATA FIRST / .TRUE. / * .. * .. Executable Statements .. * * IF( FIRST ) THEN SAFMIN=GAL_DLAMCH('S') EPS=GAL_DLAMCH('E') SAFMN2=GAL_DLAMCH('B')**INT(LOG(SAFMIN/EPS)/ $LOG(GAL_DLAMCH('B'))/TWO) SAFMX2=ONE/SAFMN2 * FIRST = .FALSE. * END IF IF(G.EQ.ZERO)THEN CS=ONE SN=ZERO R=F ELSE IF(F.EQ.ZERO)THEN CS=ZERO SN=ONE R=G ELSE F1=F G1=G SCALE=MAX(ABS(F1),ABS(G1)) IF(SCALE.GE.SAFMX2)THEN COUNT=0 10 CONTINUE COUNT=COUNT+1 F1=F1*SAFMN2 G1=G1*SAFMN2 SCALE=MAX(ABS(F1),ABS(G1)) IF(SCALE.GE.SAFMX2) $GO TO 10 R=SQRT(F1**2+G1**2) CS=F1/R SN=G1/R DO 20I=1,COUNT R=R*SAFMX2 20 CONTINUE ELSE IF(SCALE.LE.SAFMN2)THEN COUNT=0 30 CONTINUE COUNT=COUNT+1 F1=F1*SAFMX2 G1=G1*SAFMX2 SCALE=MAX(ABS(F1),ABS(G1)) IF(SCALE.LE.SAFMN2) $GO TO 30 R=SQRT(F1**2+G1**2) CS=F1/R SN=G1/R DO 40I=1,COUNT R=R*SAFMN2 40 CONTINUE ELSE R=SQRT(F1**2+G1**2) CS=F1/R SN=G1/R END IF IF(ABS(F).GT.ABS(G).AND.CS.LT.ZERO)THEN CS=-CS SN=-SN R=-R END IF END IF RETURN * * End of DLARTG * END ga-5.9.2/LinAlg/lapack+blas/gal_dlascl.f000066400000000000000000000212501500715745200176760ustar00rootroot00000000000000*> \brief \b DLASCL multiplies a general rectangular matrix by a real scalar defined as cto/cfrom. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLASCL + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLASCL( TYPE, KL, KU, CFROM, CTO, M, N, A, LDA, INFO ) * * .. Scalar Arguments .. * CHARACTER TYPE * INTEGER INFO, KL, KU, LDA, M, N * DOUBLE PRECISION CFROM, CTO * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLASCL multiplies the M by N real matrix A by the real scalar *> CTO/CFROM. This is done without over/underflow as long as the final *> result CTO*A(I,J)/CFROM does not over/underflow. TYPE specifies that *> A may be full, upper triangular, lower triangular, upper Hessenberg, *> or banded. *> \endverbatim * * Arguments: * ========== * *> \param[in] TYPE *> \verbatim *> TYPE is CHARACTER*1 *> TYPE indices the storage type of the input matrix. *> = 'G': A is a full matrix. *> = 'L': A is a lower triangular matrix. *> = 'U': A is an upper triangular matrix. *> = 'H': A is an upper Hessenberg matrix. *> = 'B': A is a symmetric band matrix with lower bandwidth KL *> and upper bandwidth KU and with the only the lower *> half stored. *> = 'Q': A is a symmetric band matrix with lower bandwidth KL *> and upper bandwidth KU and with the only the upper *> half stored. *> = 'Z': A is a band matrix with lower bandwidth KL and upper *> bandwidth KU. See DGBTRF for storage details. *> \endverbatim *> *> \param[in] KL *> \verbatim *> KL is INTEGER *> The lower bandwidth of A. Referenced only if TYPE = 'B', *> 'Q' or 'Z'. *> \endverbatim *> *> \param[in] KU *> \verbatim *> KU is INTEGER *> The upper bandwidth of A. Referenced only if TYPE = 'B', *> 'Q' or 'Z'. *> \endverbatim *> *> \param[in] CFROM *> \verbatim *> CFROM is DOUBLE PRECISION *> \endverbatim *> *> \param[in] CTO *> \verbatim *> CTO is DOUBLE PRECISION *> *> The matrix A is multiplied by CTO/CFROM. A(I,J) is computed *> without over/underflow if the final result CTO*A(I,J)/CFROM *> can be represented without over/underflow. CFROM must be *> nonzero. *> \endverbatim *> *> \param[in] M *> \verbatim *> M is INTEGER *> The number of rows of the matrix A. M >= 0. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix A. N >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> The matrix to be multiplied by CTO/CFROM. See TYPE for the *> storage type. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,M). *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> 0 - successful exit *> <0 - if INFO = -i, the i-th argument had an illegal value. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERauxiliary * * ===================================================================== SUBROUTINE GAL_DLASCL(TYPE,KL,KU,CFROM,CTO,M,N,A,LDA,INFO) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. CHARACTERTYPE INTEGER INFO,KL,KU,LDA,M,N DOUBLE PRECISION CFROM,CTO * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO,ONE PARAMETER (ZERO=0.0D0,ONE=1.0D0) * .. * .. Local Scalars .. LOGICAL DONE INTEGER I,ITYPE,J,K1,K2,K3,K4 DOUBLE PRECISION BIGNUM,CFROM1,CFROMC,CTO1,CTOC,MUL,SMLNUM * .. * .. External Functions .. LOGICAL GAL_LSAME,GAL_DISNAN DOUBLE PRECISION GAL_DLAMCH EXTERNAL GAL_LSAME,GAL_DLAMCH,GAL_DISNAN * .. * .. Intrinsic Functions .. INTRINSIC ABS,MAX,MIN * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Executable Statements .. * * Test the input arguments * INFO=0 * IF(GAL_LSAME(TYPE,'G'))THEN ITYPE=0 ELSE IF(GAL_LSAME(TYPE,'L'))THEN ITYPE=1 ELSE IF(GAL_LSAME(TYPE,'U'))THEN ITYPE=2 ELSE IF(GAL_LSAME(TYPE,'H'))THEN ITYPE=3 ELSE IF(GAL_LSAME(TYPE,'B'))THEN ITYPE=4 ELSE IF(GAL_LSAME(TYPE,'Q'))THEN ITYPE=5 ELSE IF(GAL_LSAME(TYPE,'Z'))THEN ITYPE=6 ELSE ITYPE=-1 END IF * IF(ITYPE.EQ.-1)THEN INFO=-1 ELSE IF(CFROM.EQ.ZERO.OR.GAL_DISNAN(CFROM))THEN INFO=-4 ELSE IF(GAL_DISNAN(CTO))THEN INFO=-5 ELSE IF(M.LT.0)THEN INFO=-6 ELSE IF(N.LT.0.OR.(ITYPE.EQ.4.AND.N.NE.M).OR. $(ITYPE.EQ.5.AND.N.NE.M))THEN INFO=-7 ELSE IF(ITYPE.LE.3.AND.LDA.LT.MAX(1,M))THEN INFO=-9 ELSE IF(ITYPE.GE.4)THEN IF(KL.LT.0.OR.KL.GT.MAX(M-1,0))THEN INFO=-2 ELSE IF(KU.LT.0.OR.KU.GT.MAX(N-1,0).OR. $((ITYPE.EQ.4.OR.ITYPE.EQ.5).AND.KL.NE.KU)) $THEN INFO=-3 ELSE IF((ITYPE.EQ.4.AND.LDA.LT.KL+1).OR. $(ITYPE.EQ.5.AND.LDA.LT.KU+1).OR. $(ITYPE.EQ.6.AND.LDA.LT.2*KL+KU+1))THEN INFO=-9 END IF END IF * IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DLASCL',-INFO) RETURN END IF * * Quick return if possible * IF(N.EQ.0.OR.M.EQ.0) $RETURN * * Get machine parameters * SMLNUM=GAL_DLAMCH('S') BIGNUM=ONE/SMLNUM * CFROMC=CFROM CTOC=CTO * 10 CONTINUE CFROM1=CFROMC*SMLNUM IF(CFROM1.EQ.CFROMC)THEN ! CFROMCISANINF.MULTIPLYBYACORRECTLYSIGNEDZEROFOR ! FINITECTOC,ORANANIFCTOCISINFINITE. MUL=CTOC/CFROMC DONE=.TRUE. CTO1=CTOC ELSE CTO1=CTOC/BIGNUM IF(CTO1.EQ.CTOC)THEN ! CTOCISEITHER0ORANINF.INBOTHCASES,CTOCITSELF ! SERVESASTHECORRECTMULTIPLICATIONFACTOR. MUL=CTOC DONE=.TRUE. CFROMC=ONE ELSE IF(ABS(CFROM1).GT.ABS(CTOC).AND.CTOC.NE.ZERO)THEN MUL=SMLNUM DONE=.FALSE. CFROMC=CFROM1 ELSE IF(ABS(CTO1).GT.ABS(CFROMC))THEN MUL=BIGNUM DONE=.FALSE. CTOC=CTO1 ELSE MUL=CTOC/CFROMC DONE=.TRUE. END IF END IF * IF(ITYPE.EQ.0)THEN * * Full matrix * DO 30J=1,N DO 20I=1,M A(I,J)=A(I,J)*MUL 20 CONTINUE 30 CONTINUE * ELSE IF(ITYPE.EQ.1)THEN * * Lower triangular matrix * DO 50J=1,N DO 40I=J,M A(I,J)=A(I,J)*MUL 40 CONTINUE 50 CONTINUE * ELSE IF(ITYPE.EQ.2)THEN * * Upper triangular matrix * DO 70J=1,N DO 60I=1,MIN(J,M) A(I,J)=A(I,J)*MUL 60 CONTINUE 70 CONTINUE * ELSE IF(ITYPE.EQ.3)THEN * * Upper Hessenberg matrix * DO 90J=1,N DO 80I=1,MIN(J+1,M) A(I,J)=A(I,J)*MUL 80 CONTINUE 90 CONTINUE * ELSE IF(ITYPE.EQ.4)THEN * * Lower half of a symmetric band matrix * K3=KL+1 K4=N+1 DO 110J=1,N DO 100I=1,MIN(K3,K4-J) A(I,J)=A(I,J)*MUL 100 CONTINUE 110 CONTINUE * ELSE IF(ITYPE.EQ.5)THEN * * Upper half of a symmetric band matrix * K1=KU+2 K3=KU+1 DO 130J=1,N DO 120I=MAX(K1-J,1),K3 A(I,J)=A(I,J)*MUL 120 CONTINUE 130 CONTINUE * ELSE IF(ITYPE.EQ.6)THEN * * Band matrix * K1=KL+KU+2 K2=KL+1 K3=2*KL+KU+1 K4=KL+KU+1+M DO 150J=1,N DO 140I=MAX(K1-J,K2),MIN(K3,K4-J) A(I,J)=A(I,J)*MUL 140 CONTINUE 150 CONTINUE * END IF * IF(.NOT.DONE) $GO TO 10 * RETURN * * End of DLASCL * END ga-5.9.2/LinAlg/lapack+blas/gal_dlaset.f000066400000000000000000000111761500715745200177160ustar00rootroot00000000000000*> \brief \b DLASET initializes the off-diagonal elements and the diagonal elements of a matrix to given values. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLASET + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLASET( UPLO, M, N, ALPHA, BETA, A, LDA ) * * .. Scalar Arguments .. * CHARACTER UPLO * INTEGER LDA, M, N * DOUBLE PRECISION ALPHA, BETA * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLASET initializes an m-by-n matrix A to BETA on the diagonal and *> ALPHA on the offdiagonals. *> \endverbatim * * Arguments: * ========== * *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> Specifies the part of the matrix A to be set. *> = 'U': Upper triangular part is set; the strictly lower *> triangular part of A is not changed. *> = 'L': Lower triangular part is set; the strictly upper *> triangular part of A is not changed. *> Otherwise: All of the matrix A is set. *> \endverbatim *> *> \param[in] M *> \verbatim *> M is INTEGER *> The number of rows of the matrix A. M >= 0. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix A. N >= 0. *> \endverbatim *> *> \param[in] ALPHA *> \verbatim *> ALPHA is DOUBLE PRECISION *> The constant to which the offdiagonal elements are to be set. *> \endverbatim *> *> \param[in] BETA *> \verbatim *> BETA is DOUBLE PRECISION *> The constant to which the diagonal elements are to be set. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> On exit, the leading m-by-n submatrix of A is set as follows: *> *> if UPLO = 'U', A(i,j) = ALPHA, 1<=i<=j-1, 1<=j<=n, *> if UPLO = 'L', A(i,j) = ALPHA, j+1<=i<=m, 1<=j<=n, *> otherwise, A(i,j) = ALPHA, 1<=i<=m, 1<=j<=n, i.ne.j, *> *> and, for all UPLO, A(i,i) = BETA, 1<=i<=min(m,n). *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,M). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERauxiliary * * ===================================================================== SUBROUTINE GAL_DLASET(UPLO,M,N,ALPHA,BETA,A,LDA) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. CHARACTERUPLO INTEGER LDA,M,N DOUBLE PRECISION ALPHA,BETA * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*) * .. * * ===================================================================== * * .. Local Scalars .. INTEGER I,J * .. * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. Intrinsic Functions .. INTRINSIC MIN * .. * .. Executable Statements .. * IF(GAL_LSAME(UPLO,'U'))THEN * * Set the strictly upper triangular or trapezoidal part of the * array to ALPHA. * DO 20J=2,N DO 10I=1,MIN(J-1,M) A(I,J)=ALPHA 10 CONTINUE 20 CONTINUE * ELSE IF(GAL_LSAME(UPLO,'L'))THEN * * Set the strictly lower triangular or trapezoidal part of the * array to ALPHA. * DO 40J=1,MIN(M,N) DO 30I=J+1,M A(I,J)=ALPHA 30 CONTINUE 40 CONTINUE * ELSE * * Set the leading m-by-n submatrix to ALPHA. * DO 60J=1,N DO 50I=1,M A(I,J)=ALPHA 50 CONTINUE 60 CONTINUE END IF * * Set the first min(M,N) diagonal elements to BETA. * DO 70I=1,MIN(M,N) A(I,I)=BETA 70 CONTINUE * RETURN * * End of DLASET * END ga-5.9.2/LinAlg/lapack+blas/gal_dlasr.f000066400000000000000000000300071500715745200175410ustar00rootroot00000000000000*> \brief \b DLASR applies a sequence of plane rotations to a general rectangular matrix. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLASR + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLASR( SIDE, PIVOT, DIRECT, M, N, C, S, A, LDA ) * * .. Scalar Arguments .. * CHARACTER DIRECT, PIVOT, SIDE * INTEGER LDA, M, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), C( * ), S( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLASR applies a sequence of plane rotations to a real matrix A, *> from either the left or the right. *> *> When SIDE = 'L', the transformation takes the form *> *> A := P*A *> *> and when SIDE = 'R', the transformation takes the form *> *> A := A*P**T *> *> where P is an orthogonal matrix consisting of a sequence of z plane *> rotations, with z = M when SIDE = 'L' and z = N when SIDE = 'R', *> and P**T is the transpose of P. *> *> When DIRECT = 'F' (Forward sequence), then *> *> P = P(z-1) * ... * P(2) * P(1) *> *> and when DIRECT = 'B' (Backward sequence), then *> *> P = P(1) * P(2) * ... * P(z-1) *> *> where P(k) is a plane rotation matrix defined by the 2-by-2 rotation *> *> R(k) = ( c(k) s(k) ) *> = ( -s(k) c(k) ). *> *> When PIVOT = 'V' (Variable pivot), the rotation is performed *> for the plane (k,k+1), i.e., P(k) has the form *> *> P(k) = ( 1 ) *> ( ... ) *> ( 1 ) *> ( c(k) s(k) ) *> ( -s(k) c(k) ) *> ( 1 ) *> ( ... ) *> ( 1 ) *> *> where R(k) appears as a rank-2 modification to the identity matrix in *> rows and columns k and k+1. *> *> When PIVOT = 'T' (Top pivot), the rotation is performed for the *> plane (1,k+1), so P(k) has the form *> *> P(k) = ( c(k) s(k) ) *> ( 1 ) *> ( ... ) *> ( 1 ) *> ( -s(k) c(k) ) *> ( 1 ) *> ( ... ) *> ( 1 ) *> *> where R(k) appears in rows and columns 1 and k+1. *> *> Similarly, when PIVOT = 'B' (Bottom pivot), the rotation is *> performed for the plane (k,z), giving P(k) the form *> *> P(k) = ( 1 ) *> ( ... ) *> ( 1 ) *> ( c(k) s(k) ) *> ( 1 ) *> ( ... ) *> ( 1 ) *> ( -s(k) c(k) ) *> *> where R(k) appears in rows and columns k and z. The rotations are *> performed without ever forming P(k) explicitly. *> \endverbatim * * Arguments: * ========== * *> \param[in] SIDE *> \verbatim *> SIDE is CHARACTER*1 *> Specifies whether the plane rotation matrix P is applied to *> A on the left or the right. *> = 'L': Left, compute A := P*A *> = 'R': Right, compute A:= A*P**T *> \endverbatim *> *> \param[in] PIVOT *> \verbatim *> PIVOT is CHARACTER*1 *> Specifies the plane for which P(k) is a plane rotation *> matrix. *> = 'V': Variable pivot, the plane (k,k+1) *> = 'T': Top pivot, the plane (1,k+1) *> = 'B': Bottom pivot, the plane (k,z) *> \endverbatim *> *> \param[in] DIRECT *> \verbatim *> DIRECT is CHARACTER*1 *> Specifies whether P is a forward or backward sequence of *> plane rotations. *> = 'F': Forward, P = P(z-1)*...*P(2)*P(1) *> = 'B': Backward, P = P(1)*P(2)*...*P(z-1) *> \endverbatim *> *> \param[in] M *> \verbatim *> M is INTEGER *> The number of rows of the matrix A. If m <= 1, an immediate *> return is effected. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix A. If n <= 1, an *> immediate return is effected. *> \endverbatim *> *> \param[in] C *> \verbatim *> C is DOUBLE PRECISION array, dimension *> (M-1) if SIDE = 'L' *> (N-1) if SIDE = 'R' *> The cosines c(k) of the plane rotations. *> \endverbatim *> *> \param[in] S *> \verbatim *> S is DOUBLE PRECISION array, dimension *> (M-1) if SIDE = 'L' *> (N-1) if SIDE = 'R' *> The sines s(k) of the plane rotations. The 2-by-2 plane *> rotation part of the matrix P(k), R(k), has the form *> R(k) = ( c(k) s(k) ) *> ( -s(k) c(k) ). *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> The M-by-N matrix A. On exit, A is overwritten by P*A if *> SIDE = 'R' or by A*P**T if SIDE = 'L'. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,M). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERauxiliary * * ===================================================================== SUBROUTINE GAL_DLASR(SIDE,PIVOT,DIRECT,M,N,C,S,A,LDA) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. CHARACTERDIRECT,PIVOT,SIDE INTEGER LDA,M,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),C(*),S(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * .. Local Scalars .. INTEGER I,INFO,J DOUBLE PRECISION CTEMP,STEMP,TEMP * .. * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Executable Statements .. * * Test the input parameters * INFO=0 IF(.NOT.(GAL_LSAME(SIDE,'L').OR.GAL_LSAME(SIDE,'R')))THEN INFO=1 ELSE IF(.NOT.(GAL_LSAME(PIVOT,'V').OR.GAL_LSAME(PIVOT, $'T').OR.GAL_LSAME(PIVOT,'B')))THEN INFO=2 ELSE IF(.NOT.(GAL_LSAME(DIRECT,'F').OR.GAL_LSAME(DIRECT,'B'))) $THEN INFO=3 ELSE IF(M.LT.0)THEN INFO=4 ELSE IF(N.LT.0)THEN INFO=5 ELSE IF(LDA.LT.MAX(1,M))THEN INFO=9 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DLASR',INFO) RETURN END IF * * Quick return if possible * IF((M.EQ.0).OR.(N.EQ.0)) $RETURN IF(GAL_LSAME(SIDE,'L'))THEN * * Form P * A * IF(GAL_LSAME(PIVOT,'V'))THEN IF(GAL_LSAME(DIRECT,'F'))THEN DO 20J=1,M-1 CTEMP=C(J) STEMP=S(J) IF((CTEMP.NE.ONE).OR.(STEMP.NE.ZERO))THEN DO 10I=1,N TEMP=A(J+1,I) A(J+1,I)=CTEMP*TEMP-STEMP*A(J,I) A(J,I)=STEMP*TEMP+CTEMP*A(J,I) 10 CONTINUE END IF 20 CONTINUE ELSE IF(GAL_LSAME(DIRECT,'B'))THEN DO 40J=M-1,1,-1 CTEMP=C(J) STEMP=S(J) IF((CTEMP.NE.ONE).OR.(STEMP.NE.ZERO))THEN DO 30I=1,N TEMP=A(J+1,I) A(J+1,I)=CTEMP*TEMP-STEMP*A(J,I) A(J,I)=STEMP*TEMP+CTEMP*A(J,I) 30 CONTINUE END IF 40 CONTINUE END IF ELSE IF(GAL_LSAME(PIVOT,'T'))THEN IF(GAL_LSAME(DIRECT,'F'))THEN DO 60J=2,M CTEMP=C(J-1) STEMP=S(J-1) IF((CTEMP.NE.ONE).OR.(STEMP.NE.ZERO))THEN DO 50I=1,N TEMP=A(J,I) A(J,I)=CTEMP*TEMP-STEMP*A(1,I) A(1,I)=STEMP*TEMP+CTEMP*A(1,I) 50 CONTINUE END IF 60 CONTINUE ELSE IF(GAL_LSAME(DIRECT,'B'))THEN DO 80J=M,2,-1 CTEMP=C(J-1) STEMP=S(J-1) IF((CTEMP.NE.ONE).OR.(STEMP.NE.ZERO))THEN DO 70I=1,N TEMP=A(J,I) A(J,I)=CTEMP*TEMP-STEMP*A(1,I) A(1,I)=STEMP*TEMP+CTEMP*A(1,I) 70 CONTINUE END IF 80 CONTINUE END IF ELSE IF(GAL_LSAME(PIVOT,'B'))THEN IF(GAL_LSAME(DIRECT,'F'))THEN DO 100J=1,M-1 CTEMP=C(J) STEMP=S(J) IF((CTEMP.NE.ONE).OR.(STEMP.NE.ZERO))THEN DO 90I=1,N TEMP=A(J,I) A(J,I)=STEMP*A(M,I)+CTEMP*TEMP A(M,I)=CTEMP*A(M,I)-STEMP*TEMP 90 CONTINUE END IF 100 CONTINUE ELSE IF(GAL_LSAME(DIRECT,'B'))THEN DO 120J=M-1,1,-1 CTEMP=C(J) STEMP=S(J) IF((CTEMP.NE.ONE).OR.(STEMP.NE.ZERO))THEN DO 110I=1,N TEMP=A(J,I) A(J,I)=STEMP*A(M,I)+CTEMP*TEMP A(M,I)=CTEMP*A(M,I)-STEMP*TEMP 110 CONTINUE END IF 120 CONTINUE END IF END IF ELSE IF(GAL_LSAME(SIDE,'R'))THEN * * Form A * P**T * IF(GAL_LSAME(PIVOT,'V'))THEN IF(GAL_LSAME(DIRECT,'F'))THEN DO 140J=1,N-1 CTEMP=C(J) STEMP=S(J) IF((CTEMP.NE.ONE).OR.(STEMP.NE.ZERO))THEN DO 130I=1,M TEMP=A(I,J+1) A(I,J+1)=CTEMP*TEMP-STEMP*A(I,J) A(I,J)=STEMP*TEMP+CTEMP*A(I,J) 130 CONTINUE END IF 140 CONTINUE ELSE IF(GAL_LSAME(DIRECT,'B'))THEN DO 160J=N-1,1,-1 CTEMP=C(J) STEMP=S(J) IF((CTEMP.NE.ONE).OR.(STEMP.NE.ZERO))THEN DO 150I=1,M TEMP=A(I,J+1) A(I,J+1)=CTEMP*TEMP-STEMP*A(I,J) A(I,J)=STEMP*TEMP+CTEMP*A(I,J) 150 CONTINUE END IF 160 CONTINUE END IF ELSE IF(GAL_LSAME(PIVOT,'T'))THEN IF(GAL_LSAME(DIRECT,'F'))THEN DO 180J=2,N CTEMP=C(J-1) STEMP=S(J-1) IF((CTEMP.NE.ONE).OR.(STEMP.NE.ZERO))THEN DO 170I=1,M TEMP=A(I,J) A(I,J)=CTEMP*TEMP-STEMP*A(I,1) A(I,1)=STEMP*TEMP+CTEMP*A(I,1) 170 CONTINUE END IF 180 CONTINUE ELSE IF(GAL_LSAME(DIRECT,'B'))THEN DO 200J=N,2,-1 CTEMP=C(J-1) STEMP=S(J-1) IF((CTEMP.NE.ONE).OR.(STEMP.NE.ZERO))THEN DO 190I=1,M TEMP=A(I,J) A(I,J)=CTEMP*TEMP-STEMP*A(I,1) A(I,1)=STEMP*TEMP+CTEMP*A(I,1) 190 CONTINUE END IF 200 CONTINUE END IF ELSE IF(GAL_LSAME(PIVOT,'B'))THEN IF(GAL_LSAME(DIRECT,'F'))THEN DO 220J=1,N-1 CTEMP=C(J) STEMP=S(J) IF((CTEMP.NE.ONE).OR.(STEMP.NE.ZERO))THEN DO 210I=1,M TEMP=A(I,J) A(I,J)=STEMP*A(I,N)+CTEMP*TEMP A(I,N)=CTEMP*A(I,N)-STEMP*TEMP 210 CONTINUE END IF 220 CONTINUE ELSE IF(GAL_LSAME(DIRECT,'B'))THEN DO 240J=N-1,1,-1 CTEMP=C(J) STEMP=S(J) IF((CTEMP.NE.ONE).OR.(STEMP.NE.ZERO))THEN DO 230I=1,M TEMP=A(I,J) A(I,J)=STEMP*A(I,N)+CTEMP*TEMP A(I,N)=CTEMP*A(I,N)-STEMP*TEMP 230 CONTINUE END IF 240 CONTINUE END IF END IF END IF * RETURN * * End of DLASR * END ga-5.9.2/LinAlg/lapack+blas/gal_dlasrt.f000066400000000000000000000145671500715745200177420ustar00rootroot00000000000000*> \brief \b DLASRT sorts numbers in increasing or decreasing order. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLASRT + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLASRT( ID, N, D, INFO ) * * .. Scalar Arguments .. * CHARACTER ID * INTEGER INFO, N * .. * .. Array Arguments .. * DOUBLE PRECISION D( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> Sort the numbers in D in increasing order (if ID = 'I') or *> in decreasing order (if ID = 'D' ). *> *> Use Quick Sort, reverting to Insertion sort on arrays of *> size <= 20. Dimension of STACK limits N to about 2**32. *> \endverbatim * * Arguments: * ========== * *> \param[in] ID *> \verbatim *> ID is CHARACTER*1 *> = 'I': sort D in increasing order; *> = 'D': sort D in decreasing order. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The length of the array D. *> \endverbatim *> *> \param[in,out] D *> \verbatim *> D is DOUBLE PRECISION array, dimension (N) *> On entry, the array to be sorted. *> On exit, D has been sorted into increasing order *> (D(1) <= ... <= D(N) ) or into decreasing order *> (D(1) >= ... >= D(N) ), depending on ID. *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument had an illegal value *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERcomputational * * ===================================================================== SUBROUTINE GAL_DLASRT(ID,N,D,INFO) * * -- LAPACK computational routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. CHARACTERID INTEGER INFO,N * .. * .. Array Arguments .. DOUBLE PRECISION D(*) * .. * * ===================================================================== * * .. Parameters .. INTEGER SELECT PARAMETER (SELECT=20) * .. * .. Local Scalars .. INTEGER DIR,ENDD,I,J,START,STKPNT DOUBLE PRECISION D1,D2,D3,DMNMX,TMP * .. * .. Local Arrays .. INTEGER STACK(2,32) * .. * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Executable Statements .. * * Test the input paramters. * INFO=0 DIR=-1 IF(GAL_LSAME(ID,'D'))THEN DIR=0 ELSE IF(GAL_LSAME(ID,'I'))THEN DIR=1 END IF IF(DIR.EQ.-1)THEN INFO=-1 ELSE IF(N.LT.0)THEN INFO=-2 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DLASRT',-INFO) RETURN END IF * * Quick return if possible * IF(N.LE.1) $RETURN * STKPNT=1 STACK(1,1)=1 STACK(2,1)=N 10 CONTINUE START=STACK(1,STKPNT) ENDD=STACK(2,STKPNT) STKPNT=STKPNT-1 IF(ENDD-START.LE.SELECT.AND.ENDD-START.GT.0)THEN * * Do Insertion sort on D( START:ENDD ) * IF(DIR.EQ.0)THEN * * Sort into decreasing order * DO 30I=START+1,ENDD DO 20J=I,START+1,-1 IF(D(J).GT.D(J-1))THEN DMNMX=D(J) D(J)=D(J-1) D(J-1)=DMNMX ELSE GO TO 30 END IF 20 CONTINUE 30 CONTINUE * ELSE * * Sort into increasing order * DO 50I=START+1,ENDD DO 40J=I,START+1,-1 IF(D(J).LT.D(J-1))THEN DMNMX=D(J) D(J)=D(J-1) D(J-1)=DMNMX ELSE GO TO 50 END IF 40 CONTINUE 50 CONTINUE * END IF * ELSE IF(ENDD-START.GT.SELECT)THEN * * Partition D( START:ENDD ) and stack parts, largest one first * * Choose partition entry as median of 3 * D1=D(START) D2=D(ENDD) I=(START+ENDD)/2 D3=D(I) IF(D1.LT.D2)THEN IF(D3.LT.D1)THEN DMNMX=D1 ELSE IF(D3.LT.D2)THEN DMNMX=D3 ELSE DMNMX=D2 END IF ELSE IF(D3.LT.D2)THEN DMNMX=D2 ELSE IF(D3.LT.D1)THEN DMNMX=D3 ELSE DMNMX=D1 END IF END IF * IF(DIR.EQ.0)THEN * * Sort into decreasing order * I=START-1 J=ENDD+1 60 CONTINUE 70 CONTINUE J=J-1 IF(D(J).LT.DMNMX) $GO TO 70 80 CONTINUE I=I+1 IF(D(I).GT.DMNMX) $GO TO 80 IF(I.LT.J)THEN TMP=D(I) D(I)=D(J) D(J)=TMP GO TO 60 END IF IF(J-START.GT.ENDD-J-1)THEN STKPNT=STKPNT+1 STACK(1,STKPNT)=START STACK(2,STKPNT)=J STKPNT=STKPNT+1 STACK(1,STKPNT)=J+1 STACK(2,STKPNT)=ENDD ELSE STKPNT=STKPNT+1 STACK(1,STKPNT)=J+1 STACK(2,STKPNT)=ENDD STKPNT=STKPNT+1 STACK(1,STKPNT)=START STACK(2,STKPNT)=J END IF ELSE * * Sort into increasing order * I=START-1 J=ENDD+1 90 CONTINUE 100 CONTINUE J=J-1 IF(D(J).GT.DMNMX) $GO TO 100 110 CONTINUE I=I+1 IF(D(I).LT.DMNMX) $GO TO 110 IF(I.LT.J)THEN TMP=D(I) D(I)=D(J) D(J)=TMP GO TO 90 END IF IF(J-START.GT.ENDD-J-1)THEN STKPNT=STKPNT+1 STACK(1,STKPNT)=START STACK(2,STKPNT)=J STKPNT=STKPNT+1 STACK(1,STKPNT)=J+1 STACK(2,STKPNT)=ENDD ELSE STKPNT=STKPNT+1 STACK(1,STKPNT)=J+1 STACK(2,STKPNT)=ENDD STKPNT=STKPNT+1 STACK(1,STKPNT)=START STACK(2,STKPNT)=J END IF END IF END IF IF(STKPNT.GT.0) $GO TO 10 RETURN * * End of DLASRT * END ga-5.9.2/LinAlg/lapack+blas/gal_dlassq.f000066400000000000000000000101021500715745200177150ustar00rootroot00000000000000*> \brief \b DLASSQ updates a sum of squares represented in scaled form. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLASSQ + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLASSQ( N, X, INCX, SCALE, SUMSQ ) * * .. Scalar Arguments .. * INTEGER INCX, N * DOUBLE PRECISION SCALE, SUMSQ * .. * .. Array Arguments .. * DOUBLE PRECISION X( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLASSQ returns the values scl and smsq such that *> *> ( scl**2 )*smsq = x( 1 )**2 +...+ x( n )**2 + ( scale**2 )*sumsq, *> *> where x( i ) = X( 1 + ( i - 1 )*INCX ). The value of sumsq is *> assumed to be non-negative and scl returns the value *> *> scl = max( scale, abs( x( i ) ) ). *> *> scale and sumsq must be supplied in SCALE and SUMSQ and *> scl and smsq are overwritten on SCALE and SUMSQ respectively. *> *> The routine makes only one pass through the vector x. *> \endverbatim * * Arguments: * ========== * *> \param[in] N *> \verbatim *> N is INTEGER *> The number of elements to be used from the vector X. *> \endverbatim *> *> \param[in] X *> \verbatim *> X is DOUBLE PRECISION array, dimension (N) *> The vector for which a scaled sum of squares is computed. *> x( i ) = X( 1 + ( i - 1 )*INCX ), 1 <= i <= n. *> \endverbatim *> *> \param[in] INCX *> \verbatim *> INCX is INTEGER *> The increment between successive values of the vector X. *> INCX > 0. *> \endverbatim *> *> \param[in,out] SCALE *> \verbatim *> SCALE is DOUBLE PRECISION *> On entry, the value scale in the equation above. *> On exit, SCALE is overwritten with scl , the scaling factor *> for the sum of squares. *> \endverbatim *> *> \param[in,out] SUMSQ *> \verbatim *> SUMSQ is DOUBLE PRECISION *> On entry, the value sumsq in the equation above. *> On exit, SUMSQ is overwritten with smsq , the basic sum of *> squares from which scl has been factored out. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERauxiliary * * ===================================================================== SUBROUTINE GAL_DLASSQ(N,X,INCX,SCALE,SUMSQ) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. INTEGER INCX,N DOUBLE PRECISION SCALE,SUMSQ * .. * .. Array Arguments .. DOUBLE PRECISION X(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO PARAMETER (ZERO=0.0D+0) * .. * .. Local Scalars .. INTEGER IX DOUBLE PRECISION ABSXI * .. * .. External Functions .. LOGICAL GAL_DISNAN EXTERNAL GAL_DISNAN * .. * .. Intrinsic Functions .. INTRINSIC ABS * .. * .. Executable Statements .. * IF(N.GT.0)THEN DO 10IX=1,1+(N-1)*INCX,INCX ABSXI=ABS(X(IX)) IF(ABSXI.GT.ZERO.OR.GAL_DISNAN(ABSXI))THEN IF(SCALE.LT.ABSXI)THEN SUMSQ=1+SUMSQ*(SCALE/ABSXI)**2 SCALE=ABSXI ELSE SUMSQ=SUMSQ+(ABSXI/SCALE)**2 END IF END IF 10 CONTINUE END IF RETURN * * End of DLASSQ * END ga-5.9.2/LinAlg/lapack+blas/gal_dlaswp.f000066400000000000000000000110171500715745200177260ustar00rootroot00000000000000*> \brief \b DLASWP performs a series of row interchanges on a general rectangular matrix. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLASWP + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLASWP( N, A, LDA, K1, K2, IPIV, INCX ) * * .. Scalar Arguments .. * INTEGER INCX, K1, K2, LDA, N * .. * .. Array Arguments .. * INTEGER IPIV( * ) * DOUBLE PRECISION A( LDA, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLASWP performs a series of row interchanges on the matrix A. *> One row interchange is initiated for each of rows K1 through K2 of A. *> \endverbatim * * Arguments: * ========== * *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix A. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> On entry, the matrix of column dimension N to which the row *> interchanges will be applied. *> On exit, the permuted matrix. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. *> \endverbatim *> *> \param[in] K1 *> \verbatim *> K1 is INTEGER *> The first element of IPIV for which a row interchange will *> be done. *> \endverbatim *> *> \param[in] K2 *> \verbatim *> K2 is INTEGER *> The last element of IPIV for which a row interchange will *> be done. *> \endverbatim *> *> \param[in] IPIV *> \verbatim *> IPIV is INTEGER array, dimension (K2*abs(INCX)) *> The vector of pivot indices. Only the elements in positions *> K1 through K2 of IPIV are accessed. *> IPIV(K) = L implies rows K and L are to be interchanged. *> \endverbatim *> *> \param[in] INCX *> \verbatim *> INCX is INTEGER *> The increment between successive values of IPIV. If IPIV *> is negative, the pivots are applied in reverse order. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup doubleOTHERauxiliary * *> \par Further Details: * ===================== *> *> \verbatim *> *> Modified by *> R. C. Whaley, Computer Science Dept., Univ. of Tenn., Knoxville, USA *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DLASWP(N,A,LDA,K1,K2,IPIV,INCX) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. INTEGER INCX,K1,K2,LDA,N * .. * .. Array Arguments .. INTEGER IPIV(*) DOUBLE PRECISION A(LDA,*) * .. * * ===================================================================== * * .. Local Scalars .. INTEGER I,I1,I2,INC,IP,IX,IX0,J,K,N32 DOUBLE PRECISION TEMP * .. * .. Executable Statements .. * * Interchange row I with row IPIV(I) for each of rows K1 through K2. * IF(INCX.GT.0)THEN IX0=K1 I1=K1 I2=K2 INC=1 ELSE IF(INCX.LT.0)THEN IX0=1+(1-K2)*INCX I1=K2 I2=K1 INC=-1 ELSE RETURN END IF * N32=(N/32)*32 IF(N32.NE.0)THEN DO 30J=1,N32,32 IX=IX0 DO 20I=I1,I2,INC IP=IPIV(IX) IF(IP.NE.I)THEN DO 10K=J,J+31 TEMP=A(I,K) A(I,K)=A(IP,K) A(IP,K)=TEMP 10 CONTINUE END IF IX=IX+INCX 20 CONTINUE 30 CONTINUE END IF IF(N32.NE.N)THEN N32=N32+1 IX=IX0 DO 50I=I1,I2,INC IP=IPIV(IX) IF(IP.NE.I)THEN DO 40K=N32,N TEMP=A(I,K) A(I,K)=A(IP,K) A(IP,K)=TEMP 40 CONTINUE END IF IX=IX+INCX 50 CONTINUE END IF * RETURN * * End of DLASWP * END ga-5.9.2/LinAlg/lapack+blas/gal_dlatrd.f000066400000000000000000000243621500715745200177150ustar00rootroot00000000000000*> \brief \b DLATRD reduces the first nb rows and columns of a symmetric/Hermitian matrix A to real tridiagonal form by an orthogonal similarity transformation. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DLATRD + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DLATRD( UPLO, N, NB, A, LDA, E, TAU, W, LDW ) * * .. Scalar Arguments .. * CHARACTER UPLO * INTEGER LDA, LDW, N, NB * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), E( * ), TAU( * ), W( LDW, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DLATRD reduces NB rows and columns of a real symmetric matrix A to *> symmetric tridiagonal form by an orthogonal similarity *> transformation Q**T * A * Q, and returns the matrices V and W which are *> needed to apply the transformation to the unreduced part of A. *> *> If UPLO = 'U', DLATRD reduces the last NB rows and columns of a *> matrix, of which the upper triangle is supplied; *> if UPLO = 'L', DLATRD reduces the first NB rows and columns of a *> matrix, of which the lower triangle is supplied. *> *> This is an auxiliary routine called by DSYTRD. *> \endverbatim * * Arguments: * ========== * *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> Specifies whether the upper or lower triangular part of the *> symmetric matrix A is stored: *> = 'U': Upper triangular *> = 'L': Lower triangular *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrix A. *> \endverbatim *> *> \param[in] NB *> \verbatim *> NB is INTEGER *> The number of rows and columns to be reduced. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is 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 UPLO = 'U', the last NB columns have been reduced to *> tridiagonal form, with the diagonal elements overwriting *> the diagonal elements of A; the elements above the diagonal *> with the array TAU, represent the orthogonal matrix Q as a *> product of elementary reflectors; *> if UPLO = 'L', the first NB columns have been reduced to *> tridiagonal form, with the diagonal elements overwriting *> the diagonal elements of A; the elements below the diagonal *> with the array TAU, represent the orthogonal matrix Q as a *> product of elementary reflectors. *> See Further Details. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= (1,N). *> \endverbatim *> *> \param[out] E *> \verbatim *> E is DOUBLE PRECISION array, dimension (N-1) *> If UPLO = 'U', E(n-nb:n-1) contains the superdiagonal *> elements of the last NB columns of the reduced matrix; *> if UPLO = 'L', E(1:nb) contains the subdiagonal elements of *> the first NB columns of the reduced matrix. *> \endverbatim *> *> \param[out] TAU *> \verbatim *> TAU is DOUBLE PRECISION array, dimension (N-1) *> The scalar factors of the elementary reflectors, stored in *> TAU(n-nb:n-1) if UPLO = 'U', and in TAU(1:nb) if UPLO = 'L'. *> See Further Details. *> \endverbatim *> *> \param[out] W *> \verbatim *> W is DOUBLE PRECISION array, dimension (LDW,NB) *> The n-by-nb matrix W required to update the unreduced part *> of A. *> \endverbatim *> *> \param[in] LDW *> \verbatim *> LDW is INTEGER *> The leading dimension of the array W. LDW >= max(1,N). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup doubleOTHERauxiliary * *> \par Further Details: * ===================== *> *> \verbatim *> *> If UPLO = 'U', the matrix Q is represented as a product of elementary *> reflectors *> *> Q = H(n) H(n-1) . . . H(n-nb+1). *> *> Each H(i) has the form *> *> H(i) = I - tau * v * v**T *> *> where tau is a real scalar, and v is a real vector with *> v(i:n) = 0 and v(i-1) = 1; v(1:i-1) is stored on exit in A(1:i-1,i), *> and tau in TAU(i-1). *> *> If UPLO = 'L', the matrix Q is represented as a product of elementary *> reflectors *> *> Q = H(1) H(2) . . . H(nb). *> *> Each H(i) has the form *> *> H(i) = I - tau * v * v**T *> *> where tau is a real scalar, and v is a real vector with *> v(1:i) = 0 and v(i+1) = 1; v(i+1:n) is stored on exit in A(i+1:n,i), *> and tau in TAU(i). *> *> The elements of the vectors v together form the n-by-nb matrix V *> which is needed, with W, to apply the transformation to the unreduced *> part of the matrix, using a symmetric rank-2k update of the form: *> A := A - V*W**T - W*V**T. *> *> The contents of A on exit are illustrated by the following examples *> with n = 5 and nb = 2: *> *> if UPLO = 'U': if UPLO = 'L': *> *> ( a a a v4 v5 ) ( d ) *> ( a a v4 v5 ) ( 1 d ) *> ( a 1 v5 ) ( v1 1 a ) *> ( d 1 ) ( v1 v2 a a ) *> ( d ) ( v1 v2 a a a ) *> *> where d denotes a diagonal element of the reduced matrix, a denotes *> an element of the original matrix that is unchanged, and vi denotes *> an element of the vector defining H(i). *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DLATRD(UPLO,N,NB,A,LDA,E,TAU,W,LDW) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. CHARACTERUPLO INTEGER LDA,LDW,N,NB * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),E(*),TAU(*),W(LDW,*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO,ONE,HALF PARAMETER (ZERO=0.0D+0,ONE=1.0D+0,HALF=0.5D+0) * .. * .. Local Scalars .. INTEGER I,IW DOUBLE PRECISION ALPHA * .. * .. External Subroutines .. EXTERNAL GAL_DAXPY,GAL_DGEMV,GAL_DLARFG,GAL_DSCAL,GAL_DSYMV * .. * .. External Functions .. LOGICAL GAL_LSAME DOUBLE PRECISION GAL_DDOT EXTERNAL GAL_LSAME,GAL_DDOT * .. * .. Intrinsic Functions .. INTRINSIC MIN * .. * .. Executable Statements .. * * Quick return if possible * IF(N.LE.0) $RETURN * IF(GAL_LSAME(UPLO,'U'))THEN * * Reduce last NB columns of upper triangle * DO 10I=N,N-NB+1,-1 IW=I-N+NB IF(I.LT.N)THEN * * Update A(1:i,i) * CALL GAL_DGEMV('NOTRANSPOSE',I,N-I,-ONE,A(1,I+1), $LDA,W(I,IW+1),LDW,ONE,A(1,I),1) CALL GAL_DGEMV('NOTRANSPOSE',I,N-I,-ONE,W(1,IW+1), $LDW,A(I,I+1),LDA,ONE,A(1,I),1) END IF IF(I.GT.1)THEN * * Generate elementary reflector H(i) to annihilate * A(1:i-2,i) * CALL GAL_DLARFG(I-1,A(I-1,I),A(1,I),1,TAU(I-1)) E(I-1)=A(I-1,I) A(I-1,I)=ONE * * Compute W(1:i-1,i) * CALL GAL_DSYMV('UPPER',I-1,ONE,A,LDA,A(1,I),1, $ZERO,W(1,IW),1) IF(I.LT.N)THEN CALL GAL_DGEMV('TRANSPOSE',I-1,N-I,ONE,W(1,IW+1), $LDW,A(1,I),1,ZERO,W(I+1,IW),1) CALL GAL_DGEMV('NOTRANSPOSE',I-1,N-I,-ONE, $A(1,I+1),LDA,W(I+1,IW),1,ONE, $W(1,IW),1) CALL GAL_DGEMV('TRANSPOSE',I-1,N-I,ONE,A(1,I+1), $LDA,A(1,I),1,ZERO,W(I+1,IW),1) CALL GAL_DGEMV('NOTRANSPOSE',I-1,N-I,-ONE, $W(1,IW+1),LDW,W(I+1,IW),1,ONE, $W(1,IW),1) END IF CALL GAL_DSCAL(I-1,TAU(I-1),W(1,IW),1) ALPHA=-HALF*TAU(I-1)*GAL_DDOT(I-1,W(1,IW),1, $A(1,I),1) CALL GAL_DAXPY(I-1,ALPHA,A(1,I),1,W(1,IW),1) END IF * 10 CONTINUE ELSE * * Reduce first NB columns of lower triangle * DO 20I=1,NB * * Update A(i:n,i) * CALL GAL_DGEMV('NOTRANSPOSE',N-I+1,I-1,-ONE,A(I,1), $LDA,W(I,1),LDW,ONE,A(I,I),1) CALL GAL_DGEMV('NOTRANSPOSE',N-I+1,I-1,-ONE,W(I,1), $LDW,A(I,1),LDA,ONE,A(I,I),1) IF(I.LT.N)THEN * * Generate elementary reflector H(i) to annihilate * A(i+2:n,i) * CALL GAL_DLARFG(N-I,A(I+1,I),A(MIN(I+2,N),I),1, $TAU(I)) E(I)=A(I+1,I) A(I+1,I)=ONE * * Compute W(i+1:n,i) * CALL GAL_DSYMV('LOWER',N-I,ONE,A(I+1,I+1),LDA, $A(I+1,I),1,ZERO,W(I+1,I),1) CALL GAL_DGEMV('TRANSPOSE',N-I,I-1,ONE,W(I+1,1),LDW, $A(I+1,I),1,ZERO,W(1,I),1) CALL GAL_DGEMV('NOTRANSPOSE',N-I,I-1,-ONE,A(I+1,1), $LDA,W(1,I),1,ONE,W(I+1,I),1) CALL GAL_DGEMV('TRANSPOSE',N-I,I-1,ONE,A(I+1,1),LDA, $A(I+1,I),1,ZERO,W(1,I),1) CALL GAL_DGEMV('NOTRANSPOSE',N-I,I-1,-ONE,W(I+1,1), $LDW,W(1,I),1,ONE,W(I+1,I),1) CALL GAL_DSCAL(N-I,TAU(I),W(I+1,I),1) ALPHA=-HALF*TAU(I)*GAL_DDOT(N-I,W(I+1,I),1, $A(I+1,I),1) CALL GAL_DAXPY(N-I,ALPHA,A(I+1,I),1,W(I+1,I),1) END IF * 20 CONTINUE END IF * RETURN * * End of DLATRD * END ga-5.9.2/LinAlg/lapack+blas/gal_dnrm2.f000066400000000000000000000047101500715745200174600ustar00rootroot00000000000000*> \brief \b DNRM2 * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * DOUBLE PRECISION FUNCTION DNRM2(N,X,INCX) * * .. Scalar Arguments .. * INTEGER INCX,N * .. * .. Array Arguments .. * DOUBLE PRECISION X(*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DNRM2 returns the euclidean norm of a vector via the function *> name, so that *> *> DNRM2 := sqrt( x'*x ) *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level1 * *> \par Further Details: * ===================== *> *> \verbatim *> *> -- This version written on 25-October-1982. *> Modified on 14-October-1993 to inline the call to DLASSQ. *> Sven Hammarling, Nag Ltd. *> \endverbatim *> * ===================================================================== DOUBLE PRECISION FUNCTION GAL_DNRM2(N,X,INCX) * * -- Reference BLAS level1 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. INTEGER INCX,N * .. * .. Array Arguments .. DOUBLE PRECISION X(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * .. Local Scalars .. DOUBLE PRECISION ABSXI,NORM,SCALE,SSQ INTEGER IX * .. * .. Intrinsic Functions .. INTRINSIC ABS,SQRT * .. IF(N.LT.1.OR.INCX.LT.1)THEN NORM=ZERO ELSE IF(N.EQ.1)THEN NORM=ABS(X(1)) ELSE SCALE=ZERO SSQ=ONE * The following loop is equivalent to this call to the LAPACK * auxiliary routine: * CALL DLASSQ( N, X, INCX, SCALE, SSQ ) * DO 10IX=1,1+(N-1)*INCX,INCX IF(X(IX).NE.ZERO)THEN ABSXI=ABS(X(IX)) IF(SCALE.LT.ABSXI)THEN SSQ=ONE+SSQ*(SCALE/ABSXI)**2 SCALE=ABSXI ELSE SSQ=SSQ+(ABSXI/SCALE)**2 END IF END IF 10 CONTINUE NORM=SCALE*SQRT(SSQ) END IF * GAL_DNRM2=NORM RETURN * * End of DNRM2. * END ga-5.9.2/LinAlg/lapack+blas/gal_dorg2l.f000066400000000000000000000116321500715745200176300ustar00rootroot00000000000000*> \brief \b DORG2L generates all or part of the orthogonal matrix Q from a QL factorization determined by sgeqlf (unblocked algorithm). * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DORG2L + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DORG2L( M, N, K, A, LDA, TAU, WORK, INFO ) * * .. Scalar Arguments .. * INTEGER INFO, K, LDA, M, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DORG2L generates an m by n real matrix Q with orthonormal columns, *> which is defined as the last n columns of a product of k elementary *> reflectors of order m *> *> Q = H(k) . . . H(2) H(1) *> *> as returned by DGEQLF. *> \endverbatim * * Arguments: * ========== * *> \param[in] M *> \verbatim *> M is INTEGER *> The number of rows of the matrix Q. M >= 0. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix Q. M >= N >= 0. *> \endverbatim *> *> \param[in] K *> \verbatim *> K is INTEGER *> The number of elementary reflectors whose product defines the *> matrix Q. N >= K >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> On entry, the (n-k+i)-th column must contain the vector which *> defines the elementary reflector H(i), for i = 1,2,...,k, as *> returned by DGEQLF in the last k columns of its array *> argument A. *> On exit, the m by n matrix Q. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The first dimension of the array A. LDA >= max(1,M). *> \endverbatim *> *> \param[in] TAU *> \verbatim *> TAU is DOUBLE PRECISION array, dimension (K) *> TAU(i) must contain the scalar factor of the elementary *> reflector H(i), as returned by DGEQLF. *> \endverbatim *> *> \param[out] WORK *> \verbatim *> WORK is DOUBLE PRECISION array, dimension (N) *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument has an illegal value *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup doubleOTHERcomputational * * ===================================================================== SUBROUTINE GAL_DORG2L(M,N,K,A,LDA,TAU,WORK,INFO) * * -- LAPACK computational routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. INTEGER INFO,K,LDA,M,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),TAU(*),WORK(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * .. Local Scalars .. INTEGER I,II,J,L * .. * .. External Subroutines .. EXTERNAL GAL_DLARF,GAL_DSCAL,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Executable Statements .. * * Test the input arguments * INFO=0 IF(M.LT.0)THEN INFO=-1 ELSE IF(N.LT.0.OR.N.GT.M)THEN INFO=-2 ELSE IF(K.LT.0.OR.K.GT.N)THEN INFO=-3 ELSE IF(LDA.LT.MAX(1,M))THEN INFO=-5 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DORG2L',-INFO) RETURN END IF * * Quick return if possible * IF(N.LE.0) $RETURN * * Initialise columns 1:n-k to columns of the unit matrix * DO 20J=1,N-K DO 10L=1,M A(L,J)=ZERO 10 CONTINUE A(M-N+J,J)=ONE 20 CONTINUE * DO 40I=1,K II=N-K+I * * Apply H(i) to A(1:m-k+i,1:n-k+i) from the left * A(M-N+II,II)=ONE CALL GAL_DLARF('LEFT',M-N+II,II-1,A(1,II),1,TAU(I),A, $LDA,WORK) CALL GAL_DSCAL(M-N+II-1,-TAU(I),A(1,II),1) A(M-N+II,II)=ONE-TAU(I) * * Set A(m-k+i+1:m,n-k+i) to zero * DO 30L=M-N+II+1,M A(L,II)=ZERO 30 CONTINUE 40 CONTINUE RETURN * * End of DORG2L * END ga-5.9.2/LinAlg/lapack+blas/gal_dorg2r.f000066400000000000000000000116241500715745200176370ustar00rootroot00000000000000*> \brief \b DORG2R generates all or part of the orthogonal matrix Q from a QR factorization determined by sgeqrf (unblocked algorithm). * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DORG2R + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DORG2R( M, N, K, A, LDA, TAU, WORK, INFO ) * * .. Scalar Arguments .. * INTEGER INFO, K, LDA, M, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DORG2R generates an m by n real matrix Q with orthonormal columns, *> which is defined as the first n columns of a product of k elementary *> reflectors of order m *> *> Q = H(1) H(2) . . . H(k) *> *> as returned by DGEQRF. *> \endverbatim * * Arguments: * ========== * *> \param[in] M *> \verbatim *> M is INTEGER *> The number of rows of the matrix Q. M >= 0. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix Q. M >= N >= 0. *> \endverbatim *> *> \param[in] K *> \verbatim *> K is INTEGER *> The number of elementary reflectors whose product defines the *> matrix Q. N >= K >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> On entry, the i-th column must contain the vector which *> defines the elementary reflector H(i), for i = 1,2,...,k, as *> returned by DGEQRF in the first k columns of its array *> argument A. *> On exit, the m-by-n matrix Q. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The first dimension of the array A. LDA >= max(1,M). *> \endverbatim *> *> \param[in] TAU *> \verbatim *> TAU is DOUBLE PRECISION array, dimension (K) *> TAU(i) must contain the scalar factor of the elementary *> reflector H(i), as returned by DGEQRF. *> \endverbatim *> *> \param[out] WORK *> \verbatim *> WORK is DOUBLE PRECISION array, dimension (N) *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument has an illegal value *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup doubleOTHERcomputational * * ===================================================================== SUBROUTINE GAL_DORG2R(M,N,K,A,LDA,TAU,WORK,INFO) * * -- LAPACK computational routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. INTEGER INFO,K,LDA,M,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),TAU(*),WORK(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * .. Local Scalars .. INTEGER I,J,L * .. * .. External Subroutines .. EXTERNAL GAL_DLARF,GAL_DSCAL,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Executable Statements .. * * Test the input arguments * INFO=0 IF(M.LT.0)THEN INFO=-1 ELSE IF(N.LT.0.OR.N.GT.M)THEN INFO=-2 ELSE IF(K.LT.0.OR.K.GT.N)THEN INFO=-3 ELSE IF(LDA.LT.MAX(1,M))THEN INFO=-5 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DORG2R',-INFO) RETURN END IF * * Quick return if possible * IF(N.LE.0) $RETURN * * Initialise columns k+1:n to columns of the unit matrix * DO 20J=K+1,N DO 10L=1,M A(L,J)=ZERO 10 CONTINUE A(J,J)=ONE 20 CONTINUE * DO 40I=K,1,-1 * * Apply H(i) to A(i:m,i:n) from the left * IF(I.LT.N)THEN A(I,I)=ONE CALL GAL_DLARF('LEFT',M-I+1,N-I,A(I,I),1,TAU(I), $A(I,I+1),LDA,WORK) END IF IF(I.LT.M) $CALL GAL_DSCAL(M-I,-TAU(I),A(I+1,I),1) A(I,I)=ONE-TAU(I) * * Set A(1:i-1,i) to zero * DO 30L=1,I-1 A(L,I)=ZERO 30 CONTINUE 40 CONTINUE RETURN * * End of DORG2R * END ga-5.9.2/LinAlg/lapack+blas/gal_dorgql.f000066400000000000000000000165351500715745200177360ustar00rootroot00000000000000*> \brief \b DORGQL * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DORGQL + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DORGQL( M, N, K, A, LDA, TAU, WORK, LWORK, INFO ) * * .. Scalar Arguments .. * INTEGER INFO, K, LDA, LWORK, M, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DORGQL generates an M-by-N real matrix Q with orthonormal columns, *> which is defined as the last N columns of a product of K elementary *> reflectors of order M *> *> Q = H(k) . . . H(2) H(1) *> *> as returned by DGEQLF. *> \endverbatim * * Arguments: * ========== * *> \param[in] M *> \verbatim *> M is INTEGER *> The number of rows of the matrix Q. M >= 0. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix Q. M >= N >= 0. *> \endverbatim *> *> \param[in] K *> \verbatim *> K is INTEGER *> The number of elementary reflectors whose product defines the *> matrix Q. N >= K >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> On entry, the (n-k+i)-th column must contain the vector which *> defines the elementary reflector H(i), for i = 1,2,...,k, as *> returned by DGEQLF in the last k columns of its array *> argument A. *> On exit, the M-by-N matrix Q. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The first dimension of the array A. LDA >= max(1,M). *> \endverbatim *> *> \param[in] TAU *> \verbatim *> TAU is DOUBLE PRECISION array, dimension (K) *> TAU(i) must contain the scalar factor of the elementary *> reflector H(i), as returned by DGEQLF. *> \endverbatim *> *> \param[out] WORK *> \verbatim *> WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)) *> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. *> \endverbatim *> *> \param[in] LWORK *> \verbatim *> LWORK is INTEGER *> The dimension of the array WORK. LWORK >= max(1,N). *> For optimum performance LWORK >= N*NB, where NB is the *> optimal blocksize. *> *> If LWORK = -1, then a workspace query is assumed; the routine *> only calculates the optimal size of the WORK array, returns *> this value as the first entry of the WORK array, and no error *> message related to LWORK is issued by XERBLA. *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument has an illegal value *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup doubleOTHERcomputational * * ===================================================================== SUBROUTINE GAL_DORGQL(M,N,K,A,LDA,TAU,WORK,LWORK,INFO) * * -- LAPACK computational routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. INTEGER INFO,K,LDA,LWORK,M,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),TAU(*),WORK(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO PARAMETER (ZERO=0.0D+0) * .. * .. Local Scalars .. LOGICAL LQUERY INTEGER I,IB,IINFO,IWS,J,KK,L,LDWORK,LWKOPT, $NB,NBMIN,NX * .. * .. External Subroutines .. EXTERNAL GAL_DLARFB,GAL_DLARFT,GAL_DORG2L,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX,MIN * .. * .. External Functions .. INTEGER GAL_ILAENV EXTERNAL GAL_ILAENV * .. * .. Executable Statements .. * * Test the input arguments * INFO=0 LQUERY=(LWORK.EQ.-1) IF(M.LT.0)THEN INFO=-1 ELSE IF(N.LT.0.OR.N.GT.M)THEN INFO=-2 ELSE IF(K.LT.0.OR.K.GT.N)THEN INFO=-3 ELSE IF(LDA.LT.MAX(1,M))THEN INFO=-5 END IF * IF(INFO.EQ.0)THEN IF(N.EQ.0)THEN LWKOPT=1 ELSE NB=GAL_ILAENV(1,'GAL_DORGQL','',M,N,K,-1) LWKOPT=N*NB END IF WORK(1)=LWKOPT * IF(LWORK.LT.MAX(1,N).AND..NOT.LQUERY)THEN INFO=-8 END IF END IF * IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DORGQL',-INFO) RETURN ELSE IF(LQUERY)THEN RETURN END IF * * Quick return if possible * IF(N.LE.0)THEN RETURN END IF * NBMIN=2 NX=0 IWS=N IF(NB.GT.1.AND.NB.LT.K)THEN * * Determine when to cross over from blocked to unblocked code. * NX=MAX(0,GAL_ILAENV(3,'GAL_DORGQL','',M,N,K,-1)) IF(NX.LT.K)THEN * * Determine if workspace is large enough for blocked code. * LDWORK=N IWS=LDWORK*NB IF(LWORK.LT.IWS)THEN * * Not enough workspace to use optimal NB: reduce NB and * determine the minimum value of NB. * NB=LWORK/LDWORK NBMIN=MAX(2,GAL_ILAENV(2,'GAL_DORGQL','',M,N,K,-1)) END IF END IF END IF * IF(NB.GE.NBMIN.AND.NB.LT.K.AND.NX.LT.K)THEN * * Use blocked code after the first block. * The last kk columns are handled by the block method. * KK=MIN(K,((K-NX+NB-1)/NB)*NB) * * Set A(m-kk+1:m,1:n-kk) to zero. * DO 20J=1,N-KK DO 10I=M-KK+1,M A(I,J)=ZERO 10 CONTINUE 20 CONTINUE ELSE KK=0 END IF * * Use unblocked code for the first or only block. * CALL GAL_DORG2L(M-KK,N-KK,K-KK,A,LDA,TAU,WORK,IINFO) * IF(KK.GT.0)THEN * * Use blocked code * DO 50I=K-KK+1,K,NB IB=MIN(NB,K-I+1) IF(N-K+I.GT.1)THEN * * Form the triangular factor of the block reflector * H = H(i+ib-1) . . . H(i+1) H(i) * CALL GAL_DLARFT('BACKWARD','COLUMNWISE',M-K+I+IB-1,IB, $A(1,N-K+I),LDA,TAU(I),WORK,LDWORK) * * Apply H to A(1:m-k+i+ib-1,1:n-k+i-1) from the left * CALL GAL_DLARFB('LEFT','NOTRANSPOSE','BACKWARD', $'COLUMNWISE',M-K+I+IB-1,N-K+I-1,IB, $A(1,N-K+I),LDA,WORK,LDWORK,A,LDA, $WORK(IB+1),LDWORK) END IF * * Apply H to rows 1:m-k+i+ib-1 of current block * CALL GAL_DORG2L(M-K+I+IB-1,IB,IB,A(1,N-K+I),LDA, $TAU(I),WORK,IINFO) * * Set rows m-k+i+ib:m of current block to zero * DO 40J=N-K+I,N-K+I+IB-1 DO 30L=M-K+I+IB,M A(L,J)=ZERO 30 CONTINUE 40 CONTINUE 50 CONTINUE END IF * WORK(1)=IWS RETURN * * End of DORGQL * END ga-5.9.2/LinAlg/lapack+blas/gal_dorgqr.f000066400000000000000000000163631500715745200177430ustar00rootroot00000000000000*> \brief \b DORGQR * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DORGQR + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DORGQR( M, N, K, A, LDA, TAU, WORK, LWORK, INFO ) * * .. Scalar Arguments .. * INTEGER INFO, K, LDA, LWORK, M, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DORGQR generates an M-by-N real matrix Q with orthonormal columns, *> which is defined as the first N columns of a product of K elementary *> reflectors of order M *> *> Q = H(1) H(2) . . . H(k) *> *> as returned by DGEQRF. *> \endverbatim * * Arguments: * ========== * *> \param[in] M *> \verbatim *> M is INTEGER *> The number of rows of the matrix Q. M >= 0. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix Q. M >= N >= 0. *> \endverbatim *> *> \param[in] K *> \verbatim *> K is INTEGER *> The number of elementary reflectors whose product defines the *> matrix Q. N >= K >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> On entry, the i-th column must contain the vector which *> defines the elementary reflector H(i), for i = 1,2,...,k, as *> returned by DGEQRF in the first k columns of its array *> argument A. *> On exit, the M-by-N matrix Q. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The first dimension of the array A. LDA >= max(1,M). *> \endverbatim *> *> \param[in] TAU *> \verbatim *> TAU is DOUBLE PRECISION array, dimension (K) *> TAU(i) must contain the scalar factor of the elementary *> reflector H(i), as returned by DGEQRF. *> \endverbatim *> *> \param[out] WORK *> \verbatim *> WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)) *> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. *> \endverbatim *> *> \param[in] LWORK *> \verbatim *> LWORK is INTEGER *> The dimension of the array WORK. LWORK >= max(1,N). *> For optimum performance LWORK >= N*NB, where NB is the *> optimal blocksize. *> *> If LWORK = -1, then a workspace query is assumed; the routine *> only calculates the optimal size of the WORK array, returns *> this value as the first entry of the WORK array, and no error *> message related to LWORK is issued by XERBLA. *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument has an illegal value *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup doubleOTHERcomputational * * ===================================================================== SUBROUTINE GAL_DORGQR(M,N,K,A,LDA,TAU,WORK,LWORK,INFO) * * -- LAPACK computational routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. INTEGER INFO,K,LDA,LWORK,M,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),TAU(*),WORK(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO PARAMETER (ZERO=0.0D+0) * .. * .. Local Scalars .. LOGICAL LQUERY INTEGER I,IB,IINFO,IWS,J,KI,KK,L,LDWORK, $LWKOPT,NB,NBMIN,NX * .. * .. External Subroutines .. EXTERNAL GAL_DLARFB,GAL_DLARFT,GAL_DORG2R,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX,MIN * .. * .. External Functions .. INTEGER GAL_ILAENV EXTERNAL GAL_ILAENV * .. * .. Executable Statements .. * * Test the input arguments * INFO=0 NB=GAL_ILAENV(1,'GAL_DORGQR','',M,N,K,-1) LWKOPT=MAX(1,N)*NB WORK(1)=LWKOPT LQUERY=(LWORK.EQ.-1) IF(M.LT.0)THEN INFO=-1 ELSE IF(N.LT.0.OR.N.GT.M)THEN INFO=-2 ELSE IF(K.LT.0.OR.K.GT.N)THEN INFO=-3 ELSE IF(LDA.LT.MAX(1,M))THEN INFO=-5 ELSE IF(LWORK.LT.MAX(1,N).AND..NOT.LQUERY)THEN INFO=-8 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DORGQR',-INFO) RETURN ELSE IF(LQUERY)THEN RETURN END IF * * Quick return if possible * IF(N.LE.0)THEN WORK(1)=1 RETURN END IF * NBMIN=2 NX=0 IWS=N IF(NB.GT.1.AND.NB.LT.K)THEN * * Determine when to cross over from blocked to unblocked code. * NX=MAX(0,GAL_ILAENV(3,'GAL_DORGQR','',M,N,K,-1)) IF(NX.LT.K)THEN * * Determine if workspace is large enough for blocked code. * LDWORK=N IWS=LDWORK*NB IF(LWORK.LT.IWS)THEN * * Not enough workspace to use optimal NB: reduce NB and * determine the minimum value of NB. * NB=LWORK/LDWORK NBMIN=MAX(2,GAL_ILAENV(2,'GAL_DORGQR','',M,N,K,-1)) END IF END IF END IF * IF(NB.GE.NBMIN.AND.NB.LT.K.AND.NX.LT.K)THEN * * Use blocked code after the last block. * The first kk columns are handled by the block method. * KI=((K-NX-1)/NB)*NB KK=MIN(K,KI+NB) * * Set A(1:kk,kk+1:n) to zero. * DO 20J=KK+1,N DO 10I=1,KK A(I,J)=ZERO 10 CONTINUE 20 CONTINUE ELSE KK=0 END IF * * Use unblocked code for the last or only block. * IF(KK.LT.N) $CALL GAL_DORG2R(M-KK,N-KK,K-KK,A(KK+1,KK+1),LDA, $TAU(KK+1),WORK,IINFO) * IF(KK.GT.0)THEN * * Use blocked code * DO 50I=KI+1,1,-NB IB=MIN(NB,K-I+1) IF(I+IB.LE.N)THEN * * Form the triangular factor of the block reflector * H = H(i) H(i+1) . . . H(i+ib-1) * CALL GAL_DLARFT('FORWARD','COLUMNWISE',M-I+1,IB, $A(I,I),LDA,TAU(I),WORK,LDWORK) * * Apply H to A(i:m,i+ib:n) from the left * CALL GAL_DLARFB('LEFT','NOTRANSPOSE','FORWARD', $'COLUMNWISE',M-I+1,N-I-IB+1,IB, $A(I,I),LDA,WORK,LDWORK,A(I,I+IB), $LDA,WORK(IB+1),LDWORK) END IF * * Apply H to rows i:m of current block * CALL GAL_DORG2R(M-I+1,IB,IB,A(I,I),LDA,TAU(I),WORK, $IINFO) * * Set rows 1:i-1 of current block to zero * DO 40J=I,I+IB-1 DO 30L=1,I-1 A(L,J)=ZERO 30 CONTINUE 40 CONTINUE 50 CONTINUE END IF * WORK(1)=IWS RETURN * * End of DORGQR * END ga-5.9.2/LinAlg/lapack+blas/gal_dorgtr.f000066400000000000000000000147231500715745200177440ustar00rootroot00000000000000*> \brief \b DORGTR * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DORGTR + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DORGTR( UPLO, N, A, LDA, TAU, WORK, LWORK, INFO ) * * .. Scalar Arguments .. * CHARACTER UPLO * INTEGER INFO, LDA, LWORK, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), TAU( * ), WORK( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DORGTR generates a real orthogonal matrix Q which is defined as the *> product of n-1 elementary reflectors of order N, as returned by *> DSYTRD: *> *> if UPLO = 'U', Q = H(n-1) . . . H(2) H(1), *> *> if UPLO = 'L', Q = H(1) H(2) . . . H(n-1). *> \endverbatim * * Arguments: * ========== * *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> = 'U': Upper triangle of A contains elementary reflectors *> from DSYTRD; *> = 'L': Lower triangle of A contains elementary reflectors *> from DSYTRD. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrix Q. N >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> On entry, the vectors which define the elementary reflectors, *> as returned by DSYTRD. *> On exit, the N-by-N orthogonal matrix Q. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,N). *> \endverbatim *> *> \param[in] TAU *> \verbatim *> TAU is DOUBLE PRECISION array, dimension (N-1) *> TAU(i) must contain the scalar factor of the elementary *> reflector H(i), as returned by DSYTRD. *> \endverbatim *> *> \param[out] WORK *> \verbatim *> WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)) *> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. *> \endverbatim *> *> \param[in] LWORK *> \verbatim *> LWORK is INTEGER *> The dimension of the array WORK. LWORK >= max(1,N-1). *> For optimum performance LWORK >= (N-1)*NB, where NB is *> the optimal blocksize. *> *> If LWORK = -1, then a workspace query is assumed; the routine *> only calculates the optimal size of the WORK array, returns *> this value as the first entry of the WORK array, and no error *> message related to LWORK is issued by XERBLA. *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument had an illegal value *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup doubleOTHERcomputational * * ===================================================================== SUBROUTINE GAL_DORGTR(UPLO,N,A,LDA,TAU,WORK,LWORK,INFO) * * -- LAPACK computational routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. CHARACTERUPLO INTEGER INFO,LDA,LWORK,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),TAU(*),WORK(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO,ONE PARAMETER (ZERO=0.0D+0,ONE=1.0D+0) * .. * .. Local Scalars .. LOGICAL LQUERY,UPPER INTEGER I,IINFO,J,LWKOPT,NB * .. * .. External Functions .. LOGICAL GAL_LSAME INTEGER GAL_ILAENV EXTERNAL GAL_LSAME,GAL_ILAENV * .. * .. External Subroutines .. EXTERNAL GAL_DORGQL,GAL_DORGQR,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Executable Statements .. * * Test the input arguments * INFO=0 LQUERY=(LWORK.EQ.-1) UPPER=GAL_LSAME(UPLO,'U') IF(.NOT.UPPER.AND..NOT.GAL_LSAME(UPLO,'L'))THEN INFO=-1 ELSE IF(N.LT.0)THEN INFO=-2 ELSE IF(LDA.LT.MAX(1,N))THEN INFO=-4 ELSE IF(LWORK.LT.MAX(1,N-1).AND..NOT.LQUERY)THEN INFO=-7 END IF * IF(INFO.EQ.0)THEN IF(UPPER)THEN NB=GAL_ILAENV(1,'GAL_DORGQL','',N-1,N-1,N-1,-1) ELSE NB=GAL_ILAENV(1,'GAL_DORGQR','',N-1,N-1,N-1,-1) END IF LWKOPT=MAX(1,N-1)*NB WORK(1)=LWKOPT END IF * IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DORGTR',-INFO) RETURN ELSE IF(LQUERY)THEN RETURN END IF * * Quick return if possible * IF(N.EQ.0)THEN WORK(1)=1 RETURN END IF * IF(UPPER)THEN * * Q was determined by a call to DSYTRD with UPLO = 'U' * * Shift the vectors which define the elementary reflectors one * column to the left, and set the last row and column of Q to * those of the unit matrix * DO 20J=1,N-1 DO 10I=1,J-1 A(I,J)=A(I,J+1) 10 CONTINUE A(N,J)=ZERO 20 CONTINUE DO 30I=1,N-1 A(I,N)=ZERO 30 CONTINUE A(N,N)=ONE * * Generate Q(1:n-1,1:n-1) * CALL GAL_DORGQL(N-1,N-1,N-1,A,LDA,TAU,WORK,LWORK,IINFO) * ELSE * * Q was determined by a call to DSYTRD with UPLO = 'L'. * * Shift the vectors which define the elementary reflectors one * column to the right, and set the first row and column of Q to * those of the unit matrix * DO 50J=N,2,-1 A(1,J)=ZERO DO 40I=J+1,N A(I,J)=A(I,J-1) 40 CONTINUE 50 CONTINUE A(1,1)=ONE DO 60I=2,N A(I,1)=ZERO 60 CONTINUE IF(N.GT.1)THEN * * Generate Q(2:n,2:n) * CALL GAL_DORGQR(N-1,N-1,N-1,A(2,2),LDA,TAU,WORK, $LWORK,IINFO) END IF END IF WORK(1)=LWKOPT RETURN * * End of DORGTR * END ga-5.9.2/LinAlg/lapack+blas/gal_dpotf2.f000066400000000000000000000137301500715745200176360ustar00rootroot00000000000000*> \brief \b DPOTF2 computes the Cholesky factorization of a symmetric/Hermitian positive definite matrix (unblocked algorithm). * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DPOTF2 + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DPOTF2( UPLO, N, A, LDA, INFO ) * * .. Scalar Arguments .. * CHARACTER UPLO * INTEGER INFO, LDA, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DPOTF2 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 unblocked version of the algorithm, calling Level 2 BLAS. *> \endverbatim * * Arguments: * ========== * *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> Specifies whether the upper or lower triangular part of the *> symmetric matrix A is stored. *> = 'U': Upper triangular *> = 'L': Lower triangular *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrix A. N >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is 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. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,N). *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -k, the k-th argument had an illegal value *> > 0: if INFO = k, the leading minor of order k is not *> positive definite, and the factorization could not be *> completed. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup doublePOcomputational * * ===================================================================== SUBROUTINE GAL_DPOTF2(UPLO,N,A,LDA,INFO) * * -- LAPACK computational routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. CHARACTERUPLO INTEGER INFO,LDA,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * .. Local Scalars .. LOGICAL UPPER INTEGER J DOUBLE PRECISION AJJ * .. * .. External Functions .. LOGICAL GAL_LSAME,GAL_DISNAN DOUBLE PRECISION GAL_DDOT EXTERNAL GAL_LSAME,GAL_DDOT,GAL_DISNAN * .. * .. External Subroutines .. EXTERNAL GAL_DGEMV,GAL_DSCAL,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX,SQRT * .. * .. Executable Statements .. * * Test the input parameters. * INFO=0 UPPER=GAL_LSAME(UPLO,'U') IF(.NOT.UPPER.AND..NOT.GAL_LSAME(UPLO,'L'))THEN INFO=-1 ELSE IF(N.LT.0)THEN INFO=-2 ELSE IF(LDA.LT.MAX(1,N))THEN INFO=-4 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DPOTF2',-INFO) RETURN END IF * * Quick return if possible * IF(N.EQ.0) $RETURN * IF(UPPER)THEN * * Compute the Cholesky factorization A = U**T *U. * DO 10J=1,N * * Compute U(J,J) and test for non-positive-definiteness. * AJJ=A(J,J)-GAL_DDOT(J-1,A(1,J),1,A(1,J),1) IF(AJJ.LE.ZERO.OR.GAL_DISNAN(AJJ))THEN A(J,J)=AJJ GO TO 30 END IF AJJ=SQRT(AJJ) A(J,J)=AJJ * * Compute elements J+1:N of row J. * IF(J.LT.N)THEN CALL GAL_DGEMV('TRANSPOSE',J-1,N-J,-ONE,A(1,J+1), $LDA,A(1,J),1,ONE,A(J,J+1),LDA) CALL GAL_DSCAL(N-J,ONE/AJJ,A(J,J+1),LDA) END IF 10 CONTINUE ELSE * * Compute the Cholesky factorization A = L*L**T. * DO 20J=1,N * * Compute L(J,J) and test for non-positive-definiteness. * AJJ=A(J,J)-GAL_DDOT(J-1,A(J,1),LDA,A(J,1), $LDA) IF(AJJ.LE.ZERO.OR.GAL_DISNAN(AJJ))THEN A(J,J)=AJJ GO TO 30 END IF AJJ=SQRT(AJJ) A(J,J)=AJJ * * Compute elements J+1:N of column J. * IF(J.LT.N)THEN CALL GAL_DGEMV('NOTRANSPOSE',N-J,J-1,-ONE,A(J+1,1), $LDA,A(J,1),LDA,ONE,A(J+1,J),1) CALL GAL_DSCAL(N-J,ONE/AJJ,A(J+1,J),1) END IF 20 CONTINUE END IF GO TO 40 * 30 CONTINUE INFO=J * 40 CONTINUE RETURN * * End of DPOTF2 * END ga-5.9.2/LinAlg/lapack+blas/gal_dpotrf.f000066400000000000000000000145421500715745200177400ustar00rootroot00000000000000*> \brief \b DPOTRF * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DPOTRF + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DPOTRF( UPLO, N, A, LDA, INFO ) * * .. Scalar Arguments .. * CHARACTER UPLO * INTEGER INFO, LDA, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> 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. *> \endverbatim * * Arguments: * ========== * *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> = 'U': Upper triangle of A is stored; *> = 'L': Lower triangle of A is stored. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrix A. N >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is 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. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,N). *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is 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. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup doublePOcomputational * * ===================================================================== SUBROUTINE GAL_DPOTRF(UPLO,N,A,LDA,INFO) * * -- LAPACK computational routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. CHARACTERUPLO INTEGER INFO,LDA,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE PARAMETER (ONE=1.0D+0) * .. * .. Local Scalars .. LOGICAL UPPER INTEGER J,JB,NB * .. * .. External Functions .. LOGICAL GAL_LSAME INTEGER GAL_ILAENV EXTERNAL GAL_LSAME,GAL_ILAENV * .. * .. External Subroutines .. EXTERNAL GAL_DGEMM,GAL_DPOTF2,GAL_DSYRK,GAL_DTRSM,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX,MIN * .. * .. Executable Statements .. * * Test the input parameters. * INFO=0 UPPER=GAL_LSAME(UPLO,'U') IF(.NOT.UPPER.AND..NOT.GAL_LSAME(UPLO,'L'))THEN INFO=-1 ELSE IF(N.LT.0)THEN INFO=-2 ELSE IF(LDA.LT.MAX(1,N))THEN INFO=-4 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DPOTRF',-INFO) RETURN END IF * * Quick return if possible * IF(N.EQ.0) $RETURN * * Determine the block size for this environment. * NB=GAL_ILAENV(1,'GAL_DPOTRF',UPLO,N,-1,-1,-1) IF(NB.LE.1.OR.NB.GE.N)THEN * * Use unblocked code. * CALL GAL_DPOTF2(UPLO,N,A,LDA,INFO) ELSE * * Use blocked code. * IF(UPPER)THEN * * Compute the Cholesky factorization A = U**T*U. * DO 10J=1,N,NB * * Update and factorize the current diagonal block and test * for non-positive-definiteness. * JB=MIN(NB,N-J+1) CALL GAL_DSYRK('UPPER','TRANSPOSE',JB,J-1,-ONE, $A(1,J),LDA,ONE,A(J,J),LDA) CALL GAL_DPOTF2('UPPER',JB,A(J,J),LDA,INFO) IF(INFO.NE.0) $GO TO 30 IF(J+JB.LE.N)THEN * * Compute the current block row. * CALL GAL_DGEMM('TRANSPOSE','NOTRANSPOSE',JB,N-J-JB+1, $J-1,-ONE,A(1,J),LDA,A(1,J+JB), $LDA,ONE,A(J,J+JB),LDA) CALL GAL_DTRSM('LEFT','UPPER','TRANSPOSE','NON-UNIT', $JB,N-J-JB+1,ONE,A(J,J),LDA, $A(J,J+JB),LDA) END IF 10 CONTINUE * ELSE * * Compute the Cholesky factorization A = L*L**T. * DO 20J=1,N,NB * * Update and factorize the current diagonal block and test * for non-positive-definiteness. * JB=MIN(NB,N-J+1) CALL GAL_DSYRK('LOWER','NOTRANSPOSE',JB,J-1,-ONE, $A(J,1),LDA,ONE,A(J,J),LDA) CALL GAL_DPOTF2('LOWER',JB,A(J,J),LDA,INFO) IF(INFO.NE.0) $GO TO 30 IF(J+JB.LE.N)THEN * * Compute the current block column. * CALL GAL_DGEMM('NOTRANSPOSE','TRANSPOSE',N-J-JB+1,JB, $J-1,-ONE,A(J+JB,1),LDA,A(J,1), $LDA,ONE,A(J+JB,J),LDA) CALL GAL_DTRSM('RIGHT','LOWER','TRANSPOSE','NON-UNIT', $N-J-JB+1,JB,ONE,A(J,J),LDA, $A(J+JB,J),LDA) END IF 20 CONTINUE END IF END IF GO TO 40 * 30 CONTINUE INFO=INFO+J-1 * 40 CONTINUE RETURN * * End of DPOTRF * END ga-5.9.2/LinAlg/lapack+blas/gal_dscal.f000066400000000000000000000044371500715745200175320ustar00rootroot00000000000000*> \brief \b DSCAL * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DSCAL(N,DA,DX,INCX) * * .. Scalar Arguments .. * DOUBLE PRECISION DA * INTEGER INCX,N * .. * .. Array Arguments .. * DOUBLE PRECISION DX(*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DSCAL scales a vector by a constant. *> uses unrolled loops for increment equal to one. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level1 * *> \par Further Details: * ===================== *> *> \verbatim *> *> jack dongarra, linpack, 3/11/78. *> modified 3/93 to return if incx .le. 0. *> modified 12/3/93, array(1) declarations changed to array(*) *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DSCAL(N,DA,DX,INCX) * * -- Reference BLAS level1 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. DOUBLE PRECISION DA INTEGER INCX,N * .. * .. Array Arguments .. DOUBLE PRECISION DX(*) * .. * * ===================================================================== * * .. Local Scalars .. INTEGER I,M,MP1,NINCX * .. * .. Intrinsic Functions .. INTRINSIC MOD * .. IF(N.LE.0.OR.INCX.LE.0)RETURN IF(INCX.EQ.1)THEN * * code for increment equal to 1 * * * clean-up loop * M=MOD(N,5) IF(M.NE.0)THEN DO I=1,M DX(I)=DA*DX(I) END DO IF(N.LT.5)RETURN END IF MP1=M+1 DO I=MP1,N,5 DX(I)=DA*DX(I) DX(I+1)=DA*DX(I+1) DX(I+2)=DA*DX(I+2) DX(I+3)=DA*DX(I+3) DX(I+4)=DA*DX(I+4) END DO ELSE * * code for increment not equal to 1 * NINCX=N*INCX DO I=1,NINCX,INCX DX(I)=DA*DX(I) END DO END IF RETURN END ga-5.9.2/LinAlg/lapack+blas/gal_dsteqr.f000066400000000000000000000317131500715745200177430ustar00rootroot00000000000000*> \brief \b DSTEQR * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DSTEQR + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DSTEQR( COMPZ, N, D, E, Z, LDZ, WORK, INFO ) * * .. Scalar Arguments .. * CHARACTER COMPZ * INTEGER INFO, LDZ, N * .. * .. Array Arguments .. * DOUBLE PRECISION D( * ), E( * ), WORK( * ), Z( LDZ, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DSTEQR computes all eigenvalues and, optionally, eigenvectors of a *> symmetric tridiagonal matrix using the implicit QL or QR method. *> The eigenvectors of a full or band symmetric matrix can also be found *> if DSYTRD or DSPTRD or DSBTRD has been used to reduce this matrix to *> tridiagonal form. *> \endverbatim * * Arguments: * ========== * *> \param[in] COMPZ *> \verbatim *> COMPZ is CHARACTER*1 *> = 'N': Compute eigenvalues only. *> = 'V': Compute eigenvalues and eigenvectors of the original *> symmetric matrix. On entry, Z must contain the *> orthogonal matrix used to reduce the original matrix *> to tridiagonal form. *> = 'I': Compute eigenvalues and eigenvectors of the *> tridiagonal matrix. Z is initialized to the identity *> matrix. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrix. N >= 0. *> \endverbatim *> *> \param[in,out] D *> \verbatim *> D is DOUBLE PRECISION array, dimension (N) *> On entry, the diagonal elements of the tridiagonal matrix. *> On exit, if INFO = 0, the eigenvalues in ascending order. *> \endverbatim *> *> \param[in,out] E *> \verbatim *> E is DOUBLE PRECISION array, dimension (N-1) *> On entry, the (n-1) subdiagonal elements of the tridiagonal *> matrix. *> On exit, E has been destroyed. *> \endverbatim *> *> \param[in,out] Z *> \verbatim *> Z is DOUBLE PRECISION array, dimension (LDZ, N) *> On entry, if COMPZ = 'V', then Z contains the orthogonal *> matrix used in the reduction to tridiagonal form. *> On exit, if INFO = 0, then if COMPZ = 'V', Z contains the *> orthonormal eigenvectors of the original symmetric matrix, *> and if COMPZ = 'I', Z contains the orthonormal eigenvectors *> of the symmetric tridiagonal matrix. *> If COMPZ = 'N', then Z is not referenced. *> \endverbatim *> *> \param[in] LDZ *> \verbatim *> LDZ is INTEGER *> The leading dimension of the array Z. LDZ >= 1, and if *> eigenvectors are desired, then LDZ >= max(1,N). *> \endverbatim *> *> \param[out] WORK *> \verbatim *> WORK is DOUBLE PRECISION array, dimension (max(1,2*N-2)) *> If COMPZ = 'N', then WORK is not referenced. *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument had an illegal value *> > 0: the algorithm has failed to find all the eigenvalues in *> a total of 30*N iterations; if INFO = i, then i *> elements of E have not converged to zero; on exit, D *> and E contain the elements of a symmetric tridiagonal *> matrix which is orthogonally similar to the original *> matrix. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup auxOTHERcomputational * * ===================================================================== SUBROUTINE GAL_DSTEQR(COMPZ,N,D,E,Z,LDZ,WORK,INFO) * * -- LAPACK computational routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. CHARACTERCOMPZ INTEGER INFO,LDZ,N * .. * .. Array Arguments .. DOUBLE PRECISION D(*),E(*),WORK(*),Z(LDZ,*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO,ONE,TWO,THREE PARAMETER (ZERO=0.0D0,ONE=1.0D0,TWO=2.0D0, $THREE=3.0D0) INTEGER MAXIT PARAMETER (MAXIT=30) * .. * .. Local Scalars .. INTEGER I,ICOMPZ,II,ISCALE,J,JTOT,K,L,L1,LEND, $LENDM1,LENDP1,LENDSV,LM1,LSV,M,MM,MM1, $NM1,NMAXIT DOUBLE PRECISION ANORM,B,C,EPS,EPS2,F,G,P,R,RT1,RT2, $S,SAFMAX,SAFMIN,SSFMAX,SSFMIN,TST * .. * .. External Functions .. LOGICAL GAL_LSAME DOUBLE PRECISION GAL_DLAMCH,GAL_DLANST,GAL_DLAPY2 EXTERNAL GAL_LSAME,GAL_DLAMCH,GAL_DLANST,GAL_DLAPY2 * .. * .. External Subroutines .. EXTERNAL GAL_DLAE2,GAL_DLAEV2,GAL_DLARTG, $GAL_DLASCL,GAL_DLASET,GAL_DLASR, $GAL_DLASRT,GAL_DSWAP,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC ABS,MAX,SIGN,SQRT * .. * .. Executable Statements .. * * Test the input parameters. * INFO=0 * IF(GAL_LSAME(COMPZ,'N'))THEN ICOMPZ=0 ELSE IF(GAL_LSAME(COMPZ,'V'))THEN ICOMPZ=1 ELSE IF(GAL_LSAME(COMPZ,'I'))THEN ICOMPZ=2 ELSE ICOMPZ=-1 END IF IF(ICOMPZ.LT.0)THEN INFO=-1 ELSE IF(N.LT.0)THEN INFO=-2 ELSE IF((LDZ.LT.1).OR.(ICOMPZ.GT.0.AND.LDZ.LT.MAX(1, $N)))THEN INFO=-6 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DSTEQR',-INFO) RETURN END IF * * Quick return if possible * IF(N.EQ.0) $RETURN * IF(N.EQ.1)THEN IF(ICOMPZ.EQ.2) $Z(1,1)=ONE RETURN END IF * * Determine the unit roundoff and over/underflow thresholds. * EPS=GAL_DLAMCH('E') EPS2=EPS**2 SAFMIN=GAL_DLAMCH('S') SAFMAX=ONE/SAFMIN SSFMAX=SQRT(SAFMAX)/THREE SSFMIN=SQRT(SAFMIN)/EPS2 * * Compute the eigenvalues and eigenvectors of the tridiagonal * matrix. * IF(ICOMPZ.EQ.2) $CALL GAL_DLASET('FULL',N,N,ZERO,ONE,Z,LDZ) * NMAXIT=N*MAXIT JTOT=0 * * Determine where the matrix splits and choose QL or QR iteration * for each block, according to whether top or bottom diagonal * element is smaller. * L1=1 NM1=N-1 * 10 CONTINUE IF(L1.GT.N) $GO TO 160 IF(L1.GT.1) $E(L1-1)=ZERO IF(L1.LE.NM1)THEN DO 20M=L1,NM1 TST=ABS(E(M)) IF(TST.EQ.ZERO) $GO TO 30 IF(TST.LE.(SQRT(ABS(D(M)))*SQRT(ABS(D(M+ $1))))*EPS)THEN E(M)=ZERO GO TO 30 END IF 20 CONTINUE END IF M=N * 30 CONTINUE L=L1 LSV=L LEND=M LENDSV=LEND L1=M+1 IF(LEND.EQ.L) $GO TO 10 * * Scale submatrix in rows and columns L to LEND * ANORM=GAL_DLANST('M',LEND-L+1,D(L),E(L)) ISCALE=0 IF(ANORM.EQ.ZERO) $GO TO 10 IF(ANORM.GT.SSFMAX)THEN ISCALE=1 CALL GAL_DLASCL('G',0,0,ANORM,SSFMAX,LEND-L+1,1,D(L),N, $INFO) CALL GAL_DLASCL('G',0,0,ANORM,SSFMAX,LEND-L,1,E(L),N, $INFO) ELSE IF(ANORM.LT.SSFMIN)THEN ISCALE=2 CALL GAL_DLASCL('G',0,0,ANORM,SSFMIN,LEND-L+1,1,D(L),N, $INFO) CALL GAL_DLASCL('G',0,0,ANORM,SSFMIN,LEND-L,1,E(L),N, $INFO) END IF * * Choose between QL and QR iteration * IF(ABS(D(LEND)).LT.ABS(D(L)))THEN LEND=LSV L=LENDSV END IF * IF(LEND.GT.L)THEN * * QL Iteration * * Look for small subdiagonal element. * 40 CONTINUE IF(L.NE.LEND)THEN LENDM1=LEND-1 DO 50M=L,LENDM1 TST=ABS(E(M))**2 IF(TST.LE.(EPS2*ABS(D(M)))*ABS(D(M+1))+ $SAFMIN)GOTO 60 50 CONTINUE END IF * M=LEND * 60 CONTINUE IF(M.LT.LEND) $E(M)=ZERO P=D(L) IF(M.EQ.L) $GO TO 80 * * If remaining matrix is 2-by-2, use DLAE2 or SLAEV2 * to compute its eigensystem. * IF(M.EQ.L+1)THEN IF(ICOMPZ.GT.0)THEN CALL GAL_DLAEV2(D(L),E(L),D(L+1),RT1,RT2,C,S) WORK(L)=C WORK(N-1+L)=S CALL GAL_DLASR('R','V','B',N,2,WORK(L), $WORK(N-1+L),Z(1,L),LDZ) ELSE CALL GAL_DLAE2(D(L),E(L),D(L+1),RT1,RT2) END IF D(L)=RT1 D(L+1)=RT2 E(L)=ZERO L=L+2 IF(L.LE.LEND) $GO TO 40 GO TO 140 END IF * IF(JTOT.EQ.NMAXIT) $GO TO 140 JTOT=JTOT+1 * * Form shift. * G=(D(L+1)-P)/(TWO*E(L)) R=GAL_DLAPY2(G,ONE) G=D(M)-P+(E(L)/(G+SIGN(R,G))) * S=ONE C=ONE P=ZERO * * Inner loop * MM1=M-1 DO 70I=MM1,L,-1 F=S*E(I) B=C*E(I) CALL GAL_DLARTG(G,F,C,S,R) IF(I.NE.M-1) $E(I+1)=R G=D(I+1)-P R=(D(I)-G)*S+TWO*C*B P=S*R D(I+1)=G+P G=C*R-B * * If eigenvectors are desired, then save rotations. * IF(ICOMPZ.GT.0)THEN WORK(I)=C WORK(N-1+I)=-S END IF * 70 CONTINUE * * If eigenvectors are desired, then apply saved rotations. * IF(ICOMPZ.GT.0)THEN MM=M-L+1 CALL GAL_DLASR('R','V','B',N,MM,WORK(L),WORK(N-1+L), $Z(1,L),LDZ) END IF * D(L)=D(L)-P E(L)=G GO TO 40 * * Eigenvalue found. * 80 CONTINUE D(L)=P * L=L+1 IF(L.LE.LEND) $GO TO 40 GO TO 140 * ELSE * * QR Iteration * * Look for small superdiagonal element. * 90 CONTINUE IF(L.NE.LEND)THEN LENDP1=LEND+1 DO 100M=L,LENDP1,-1 TST=ABS(E(M-1))**2 IF(TST.LE.(EPS2*ABS(D(M)))*ABS(D(M-1))+ $SAFMIN)GOTO 110 100 CONTINUE END IF * M=LEND * 110 CONTINUE IF(M.GT.LEND) $E(M-1)=ZERO P=D(L) IF(M.EQ.L) $GO TO 130 * * If remaining matrix is 2-by-2, use DLAE2 or SLAEV2 * to compute its eigensystem. * IF(M.EQ.L-1)THEN IF(ICOMPZ.GT.0)THEN CALL GAL_DLAEV2(D(L-1),E(L-1),D(L),RT1,RT2,C,S) WORK(M)=C WORK(N-1+M)=S CALL GAL_DLASR('R','V','F',N,2,WORK(M), $WORK(N-1+M),Z(1,L-1),LDZ) ELSE CALL GAL_DLAE2(D(L-1),E(L-1),D(L),RT1,RT2) END IF D(L-1)=RT1 D(L)=RT2 E(L-1)=ZERO L=L-2 IF(L.GE.LEND) $GO TO 90 GO TO 140 END IF * IF(JTOT.EQ.NMAXIT) $GO TO 140 JTOT=JTOT+1 * * Form shift. * G=(D(L-1)-P)/(TWO*E(L-1)) R=GAL_DLAPY2(G,ONE) G=D(M)-P+(E(L-1)/(G+SIGN(R,G))) * S=ONE C=ONE P=ZERO * * Inner loop * LM1=L-1 DO 120I=M,LM1 F=S*E(I) B=C*E(I) CALL GAL_DLARTG(G,F,C,S,R) IF(I.NE.M) $E(I-1)=R G=D(I)-P R=(D(I+1)-G)*S+TWO*C*B P=S*R D(I)=G+P G=C*R-B * * If eigenvectors are desired, then save rotations. * IF(ICOMPZ.GT.0)THEN WORK(I)=C WORK(N-1+I)=S END IF * 120 CONTINUE * * If eigenvectors are desired, then apply saved rotations. * IF(ICOMPZ.GT.0)THEN MM=L-M+1 CALL GAL_DLASR('R','V','F',N,MM,WORK(M),WORK(N-1+M), $Z(1,M),LDZ) END IF * D(L)=D(L)-P E(LM1)=G GO TO 90 * * Eigenvalue found. * 130 CONTINUE D(L)=P * L=L-1 IF(L.GE.LEND) $GO TO 90 GO TO 140 * END IF * * Undo scaling if necessary * 140 CONTINUE IF(ISCALE.EQ.1)THEN CALL GAL_DLASCL('G',0,0,SSFMAX,ANORM,LENDSV-LSV+1,1, $D(LSV),N,INFO) CALL GAL_DLASCL('G',0,0,SSFMAX,ANORM,LENDSV-LSV,1,E(LSV), $N,INFO) ELSE IF(ISCALE.EQ.2)THEN CALL GAL_DLASCL('G',0,0,SSFMIN,ANORM,LENDSV-LSV+1,1, $D(LSV),N,INFO) CALL GAL_DLASCL('G',0,0,SSFMIN,ANORM,LENDSV-LSV,1,E(LSV), $N,INFO) END IF * * Check for no convergence to an eigenvalue after a total * of N*MAXIT iterations. * IF(JTOT.LT.NMAXIT) $GO TO 10 DO 150I=1,N-1 IF(E(I).NE.ZERO) $INFO=INFO+1 150 CONTINUE GO TO 190 * * Order eigenvalues and eigenvectors. * 160 CONTINUE IF(ICOMPZ.EQ.0)THEN * * Use Quick Sort * CALL GAL_DLASRT('I',N,D,INFO) * ELSE * * Use Selection Sort to minimize swaps of eigenvectors * DO 180II=2,N I=II-1 K=I P=D(I) DO 170J=II,N IF(D(J).LT.P)THEN K=J P=D(J) END IF 170 CONTINUE IF(K.NE.I)THEN D(K)=D(I) D(I)=P CALL GAL_DSWAP(N,Z(1,I),1,Z(1,K),1) END IF 180 CONTINUE END IF * 190 CONTINUE RETURN * * End of DSTEQR * END ga-5.9.2/LinAlg/lapack+blas/gal_dsterf.f000066400000000000000000000216101500715745200177230ustar00rootroot00000000000000*> \brief \b DSTERF * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DSTERF + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DSTERF( N, D, E, INFO ) * * .. Scalar Arguments .. * INTEGER INFO, N * .. * .. Array Arguments .. * DOUBLE PRECISION D( * ), E( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DSTERF computes all eigenvalues of a symmetric tridiagonal matrix *> using the Pal-Walker-Kahan variant of the QL or QR algorithm. *> \endverbatim * * Arguments: * ========== * *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrix. N >= 0. *> \endverbatim *> *> \param[in,out] D *> \verbatim *> D is DOUBLE PRECISION array, dimension (N) *> On entry, the n diagonal elements of the tridiagonal matrix. *> On exit, if INFO = 0, the eigenvalues in ascending order. *> \endverbatim *> *> \param[in,out] E *> \verbatim *> E is DOUBLE PRECISION array, dimension (N-1) *> On entry, the (n-1) subdiagonal elements of the tridiagonal *> matrix. *> On exit, E has been destroyed. *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument had an illegal value *> > 0: the algorithm failed to find all of the eigenvalues in *> a total of 30*N iterations; if INFO = i, then i *> elements of E have not converged to zero. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup auxOTHERcomputational * * ===================================================================== SUBROUTINE GAL_DSTERF(N,D,E,INFO) * * -- LAPACK computational routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. INTEGER INFO,N * .. * .. Array Arguments .. DOUBLE PRECISION D(*),E(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO,ONE,TWO,THREE PARAMETER (ZERO=0.0D0,ONE=1.0D0,TWO=2.0D0, $THREE=3.0D0) INTEGER MAXIT PARAMETER (MAXIT=30) * .. * .. Local Scalars .. INTEGER I,ISCALE,JTOT,L,L1,LEND,LENDSV,LSV,M, $NMAXIT DOUBLE PRECISION ALPHA,ANORM,BB,C,EPS,EPS2,GAMMA,OLDC, $OLDGAM,P,R,RT1,RT2,RTE,S,SAFMAX,SAFMIN, $SIGMA,SSFMAX,SSFMIN,RMAX * .. * .. External Functions .. DOUBLE PRECISION GAL_DLAMCH,GAL_DLANST,GAL_DLAPY2 EXTERNAL GAL_DLAMCH,GAL_DLANST,GAL_DLAPY2 * .. * .. External Subroutines .. EXTERNAL GAL_DLAE2,GAL_DLASCL,GAL_DLASRT,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC ABS,SIGN,SQRT * .. * .. Executable Statements .. * * Test the input parameters. * INFO=0 * * Quick return if possible * IF(N.LT.0)THEN INFO=-1 CALL GAL_XERBLA('GAL_DSTERF',-INFO) RETURN END IF IF(N.LE.1) $RETURN * * Determine the unit roundoff for this environment. * EPS=GAL_DLAMCH('E') EPS2=EPS**2 SAFMIN=GAL_DLAMCH('S') SAFMAX=ONE/SAFMIN SSFMAX=SQRT(SAFMAX)/THREE SSFMIN=SQRT(SAFMIN)/EPS2 RMAX=GAL_DLAMCH('O') * * Compute the eigenvalues of the tridiagonal matrix. * NMAXIT=N*MAXIT SIGMA=ZERO JTOT=0 * * Determine where the matrix splits and choose QL or QR iteration * for each block, according to whether top or bottom diagonal * element is smaller. * L1=1 * 10 CONTINUE IF(L1.GT.N) $GO TO 170 IF(L1.GT.1) $E(L1-1)=ZERO DO 20M=L1,N-1 IF(ABS(E(M)).LE.(SQRT(ABS(D(M)))*SQRT(ABS(D(M+ $1))))*EPS)THEN E(M)=ZERO GO TO 30 END IF 20 CONTINUE M=N * 30 CONTINUE L=L1 LSV=L LEND=M LENDSV=LEND L1=M+1 IF(LEND.EQ.L) $GO TO 10 * * Scale submatrix in rows and columns L to LEND * ANORM=GAL_DLANST('M',LEND-L+1,D(L),E(L)) ISCALE=0 IF(ANORM.EQ.ZERO) $GO TO 10 IF((ANORM.GT.SSFMAX))THEN ISCALE=1 CALL GAL_DLASCL('G',0,0,ANORM,SSFMAX,LEND-L+1,1,D(L),N, $INFO) CALL GAL_DLASCL('G',0,0,ANORM,SSFMAX,LEND-L,1,E(L),N, $INFO) ELSE IF(ANORM.LT.SSFMIN)THEN ISCALE=2 CALL GAL_DLASCL('G',0,0,ANORM,SSFMIN,LEND-L+1,1,D(L),N, $INFO) CALL GAL_DLASCL('G',0,0,ANORM,SSFMIN,LEND-L,1,E(L),N, $INFO) END IF * DO 40I=L,LEND-1 E(I)=E(I)**2 40 CONTINUE * * Choose between QL and QR iteration * IF(ABS(D(LEND)).LT.ABS(D(L)))THEN LEND=LSV L=LENDSV END IF * IF(LEND.GE.L)THEN * * QL Iteration * * Look for small subdiagonal element. * 50 CONTINUE IF(L.NE.LEND)THEN DO 60M=L,LEND-1 IF(ABS(E(M)).LE.EPS2*ABS(D(M)*D(M+1))) $GO TO 70 60 CONTINUE END IF M=LEND * 70 CONTINUE IF(M.LT.LEND) $E(M)=ZERO P=D(L) IF(M.EQ.L) $GO TO 90 * * If remaining matrix is 2 by 2, use DLAE2 to compute its * eigenvalues. * IF(M.EQ.L+1)THEN RTE=SQRT(E(L)) CALL GAL_DLAE2(D(L),RTE,D(L+1),RT1,RT2) D(L)=RT1 D(L+1)=RT2 E(L)=ZERO L=L+2 IF(L.LE.LEND) $GO TO 50 GO TO 150 END IF * IF(JTOT.EQ.NMAXIT) $GO TO 150 JTOT=JTOT+1 * * Form shift. * RTE=SQRT(E(L)) SIGMA=(D(L+1)-P)/(TWO*RTE) R=GAL_DLAPY2(SIGMA,ONE) SIGMA=P-(RTE/(SIGMA+SIGN(R,SIGMA))) * C=ONE S=ZERO GAMMA=D(M)-SIGMA P=GAMMA*GAMMA * * Inner loop * DO 80I=M-1,L,-1 BB=E(I) R=P+BB IF(I.NE.M-1) $E(I+1)=S*R OLDC=C C=P/R S=BB/R OLDGAM=GAMMA ALPHA=D(I) GAMMA=C*(ALPHA-SIGMA)-S*OLDGAM D(I+1)=OLDGAM+(ALPHA-GAMMA) IF(C.NE.ZERO)THEN P=(GAMMA*GAMMA)/C ELSE P=OLDC*BB END IF 80 CONTINUE * E(L)=S*P D(L)=SIGMA+GAMMA GO TO 50 * * Eigenvalue found. * 90 CONTINUE D(L)=P * L=L+1 IF(L.LE.LEND) $GO TO 50 GO TO 150 * ELSE * * QR Iteration * * Look for small superdiagonal element. * 100 CONTINUE DO 110M=L,LEND+1,-1 IF(ABS(E(M-1)).LE.EPS2*ABS(D(M)*D(M-1))) $GO TO 120 110 CONTINUE M=LEND * 120 CONTINUE IF(M.GT.LEND) $E(M-1)=ZERO P=D(L) IF(M.EQ.L) $GO TO 140 * * If remaining matrix is 2 by 2, use DLAE2 to compute its * eigenvalues. * IF(M.EQ.L-1)THEN RTE=SQRT(E(L-1)) CALL GAL_DLAE2(D(L),RTE,D(L-1),RT1,RT2) D(L)=RT1 D(L-1)=RT2 E(L-1)=ZERO L=L-2 IF(L.GE.LEND) $GO TO 100 GO TO 150 END IF * IF(JTOT.EQ.NMAXIT) $GO TO 150 JTOT=JTOT+1 * * Form shift. * RTE=SQRT(E(L-1)) SIGMA=(D(L-1)-P)/(TWO*RTE) R=GAL_DLAPY2(SIGMA,ONE) SIGMA=P-(RTE/(SIGMA+SIGN(R,SIGMA))) * C=ONE S=ZERO GAMMA=D(M)-SIGMA P=GAMMA*GAMMA * * Inner loop * DO 130I=M,L-1 BB=E(I) R=P+BB IF(I.NE.M) $E(I-1)=S*R OLDC=C C=P/R S=BB/R OLDGAM=GAMMA ALPHA=D(I+1) GAMMA=C*(ALPHA-SIGMA)-S*OLDGAM D(I)=OLDGAM+(ALPHA-GAMMA) IF(C.NE.ZERO)THEN P=(GAMMA*GAMMA)/C ELSE P=OLDC*BB END IF 130 CONTINUE * E(L-1)=S*P D(L)=SIGMA+GAMMA GO TO 100 * * Eigenvalue found. * 140 CONTINUE D(L)=P * L=L-1 IF(L.GE.LEND) $GO TO 100 GO TO 150 * END IF * * Undo scaling if necessary * 150 CONTINUE IF(ISCALE.EQ.1) $CALL GAL_DLASCL('G',0,0,SSFMAX,ANORM,LENDSV-LSV+1,1, $D(LSV),N,INFO) IF(ISCALE.EQ.2) $CALL GAL_DLASCL('G',0,0,SSFMIN,ANORM,LENDSV-LSV+1,1, $D(LSV),N,INFO) * * Check for no convergence to an eigenvalue after a total * of N*MAXIT iterations. * IF(JTOT.LT.NMAXIT) $GO TO 10 DO 160I=1,N-1 IF(E(I).NE.ZERO) $INFO=INFO+1 160 CONTINUE GO TO 180 * * Sort eigenvalues in increasing order. * 170 CONTINUE CALL GAL_DLASRT('I',N,D,INFO) * 180 CONTINUE RETURN * * End of DSTERF * END ga-5.9.2/LinAlg/lapack+blas/gal_dswap.f000066400000000000000000000047701500715745200175620ustar00rootroot00000000000000*> \brief \b DSWAP * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DSWAP(N,DX,INCX,DY,INCY) * * .. Scalar Arguments .. * INTEGER INCX,INCY,N * .. * .. Array Arguments .. * DOUBLE PRECISION DX(*),DY(*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> interchanges two vectors. *> uses unrolled loops for increments equal one. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level1 * *> \par Further Details: * ===================== *> *> \verbatim *> *> jack dongarra, linpack, 3/11/78. *> modified 12/3/93, array(1) declarations changed to array(*) *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DSWAP(N,DX,INCX,DY,INCY) * * -- Reference BLAS level1 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. INTEGER INCX,INCY,N * .. * .. Array Arguments .. DOUBLE PRECISION DX(*),DY(*) * .. * * ===================================================================== * * .. Local Scalars .. DOUBLE PRECISION DTEMP INTEGER I,IX,IY,M,MP1 * .. * .. Intrinsic Functions .. INTRINSIC MOD * .. IF(N.LE.0)RETURN IF(INCX.EQ.1.AND.INCY.EQ.1)THEN * * code for both increments equal to 1 * * * clean-up loop * M=MOD(N,3) IF(M.NE.0)THEN DO I=1,M DTEMP=DX(I) DX(I)=DY(I) DY(I)=DTEMP END DO IF(N.LT.3)RETURN END IF MP1=M+1 DO I=MP1,N,3 DTEMP=DX(I) DX(I)=DY(I) DY(I)=DTEMP DTEMP=DX(I+1) DX(I+1)=DY(I+1) DY(I+1)=DTEMP DTEMP=DX(I+2) DX(I+2)=DY(I+2) DY(I+2)=DTEMP END DO ELSE * * code for unequal increments or equal increments not equal * to 1 * IX=1 IY=1 IF(INCX.LT.0)IX=(-N+1)*INCX+1 IF(INCY.LT.0)IY=(-N+1)*INCY+1 DO I=1,N DTEMP=DX(IX) DX(IX)=DY(IY) DY(IY)=DTEMP IX=IX+INCX IY=IY+INCY END DO END IF RETURN END ga-5.9.2/LinAlg/lapack+blas/gal_dsyev.f000066400000000000000000000174031500715745200175730ustar00rootroot00000000000000*> \brief DSYEV computes the eigenvalues and, optionally, the left and/or right eigenvectors for SY matrices * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DSYEV + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DSYEV( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, INFO ) * * .. Scalar Arguments .. * CHARACTER JOBZ, UPLO * INTEGER INFO, LDA, LWORK, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), W( * ), WORK( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DSYEV computes all eigenvalues and, optionally, eigenvectors of a *> real symmetric matrix A. *> \endverbatim * * Arguments: * ========== * *> \param[in] JOBZ *> \verbatim *> JOBZ is CHARACTER*1 *> = 'N': Compute eigenvalues only; *> = 'V': Compute eigenvalues and eigenvectors. *> \endverbatim *> *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> = 'U': Upper triangle of A is stored; *> = 'L': Lower triangle of A is stored. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrix A. N >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is 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. If UPLO = 'L', *> the leading N-by-N lower triangular part of A contains *> the lower triangular part of the matrix A. *> On exit, if JOBZ = 'V', then if INFO = 0, A contains the *> orthonormal eigenvectors of the matrix A. *> If JOBZ = 'N', then on exit the lower triangle (if UPLO='L') *> or the upper triangle (if UPLO='U') of A, including the *> diagonal, is destroyed. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,N). *> \endverbatim *> *> \param[out] W *> \verbatim *> W is DOUBLE PRECISION array, dimension (N) *> If INFO = 0, the eigenvalues in ascending order. *> \endverbatim *> *> \param[out] WORK *> \verbatim *> WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)) *> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. *> \endverbatim *> *> \param[in] LWORK *> \verbatim *> LWORK is INTEGER *> The length of the array WORK. LWORK >= max(1,3*N-1). *> For optimal efficiency, LWORK >= (NB+2)*N, *> where NB is the blocksize for DSYTRD returned by ILAENV. *> *> If LWORK = -1, then a workspace query is assumed; the routine *> only calculates the optimal size of the WORK array, returns *> this value as the first entry of the WORK array, and no error *> message related to LWORK is issued by XERBLA. *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument had an illegal value *> > 0: if INFO = i, the algorithm failed to converge; i *> off-diagonal elements of an intermediate tridiagonal *> form did not converge to zero. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup doubleSYeigen * * ===================================================================== SUBROUTINE GAL_DSYEV(JOBZ,UPLO,N,A,LDA,W,WORK,LWORK,INFO) * * -- LAPACK driver routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. CHARACTERJOBZ,UPLO INTEGER INFO,LDA,LWORK,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),W(*),WORK(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO,ONE PARAMETER (ZERO=0.0D0,ONE=1.0D0) * .. * .. Local Scalars .. LOGICAL LOWER,LQUERY,WANTZ INTEGER IINFO,IMAX,INDE,INDTAU,INDWRK,ISCALE, $LLWORK,LWKOPT,NB DOUBLE PRECISION ANRM,BIGNUM,EPS,RMAX,RMIN,SAFMIN,SIGMA, $SMLNUM * .. * .. External Functions .. LOGICAL GAL_LSAME INTEGER GAL_ILAENV DOUBLE PRECISION GAL_DLAMCH,GAL_DLANSY EXTERNAL GAL_LSAME,GAL_ILAENV,GAL_DLAMCH,GAL_DLANSY * .. * .. External Subroutines .. EXTERNAL GAL_DLASCL,GAL_DORGTR,GAL_DSCAL, $GAL_DSTEQR,GAL_DSTERF,GAL_DSYTRD, $GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX,SQRT * .. * .. Executable Statements .. * * Test the input parameters. * WANTZ=GAL_LSAME(JOBZ,'V') LOWER=GAL_LSAME(UPLO,'L') LQUERY=(LWORK.EQ.-1) * INFO=0 IF(.NOT.(WANTZ.OR.GAL_LSAME(JOBZ,'N')))THEN INFO=-1 ELSE IF(.NOT.(LOWER.OR.GAL_LSAME(UPLO,'U')))THEN INFO=-2 ELSE IF(N.LT.0)THEN INFO=-3 ELSE IF(LDA.LT.MAX(1,N))THEN INFO=-5 END IF * IF(INFO.EQ.0)THEN NB=GAL_ILAENV(1,'GAL_DSYTRD',UPLO,N,-1,-1,-1) LWKOPT=MAX(1,(NB+2)*N) WORK(1)=LWKOPT * IF(LWORK.LT.MAX(1,3*N-1).AND..NOT.LQUERY) $INFO=-8 END IF * IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DSYEV',-INFO) RETURN ELSE IF(LQUERY)THEN RETURN END IF * * Quick return if possible * IF(N.EQ.0)THEN RETURN END IF * IF(N.EQ.1)THEN W(1)=A(1,1) WORK(1)=2 IF(WANTZ) $A(1,1)=ONE RETURN END IF * * Get machine constants. * SAFMIN=GAL_DLAMCH('SAFEMINIMUM') EPS=GAL_DLAMCH('PRECISION') SMLNUM=SAFMIN/EPS BIGNUM=ONE/SMLNUM RMIN=SQRT(SMLNUM) RMAX=SQRT(BIGNUM) * * Scale matrix to allowable range, if necessary. * ANRM=GAL_DLANSY('M',UPLO,N,A,LDA,WORK) ISCALE=0 IF(ANRM.GT.ZERO.AND.ANRM.LT.RMIN)THEN ISCALE=1 SIGMA=RMIN/ANRM ELSE IF(ANRM.GT.RMAX)THEN ISCALE=1 SIGMA=RMAX/ANRM END IF IF(ISCALE.EQ.1) $CALL GAL_DLASCL(UPLO,0,0,ONE,SIGMA,N,N,A,LDA,INFO) * * Call DSYTRD to reduce symmetric matrix to tridiagonal form. * INDE=1 INDTAU=INDE+N INDWRK=INDTAU+N LLWORK=LWORK-INDWRK+1 CALL GAL_DSYTRD(UPLO,N,A,LDA,W,WORK(INDE),WORK(INDTAU), $WORK(INDWRK),LLWORK,IINFO) * * For eigenvalues only, call DSTERF. For eigenvectors, first call * DORGTR to generate the orthogonal matrix, then call DSTEQR. * IF(.NOT.WANTZ)THEN CALL GAL_DSTERF(N,W,WORK(INDE),INFO) ELSE CALL GAL_DORGTR(UPLO,N,A,LDA,WORK(INDTAU),WORK(INDWRK), $LLWORK,IINFO) CALL GAL_DSTEQR(JOBZ,N,W,WORK(INDE),A,LDA,WORK(INDTAU), $INFO) END IF * * If matrix was scaled, then rescale eigenvalues appropriately. * IF(ISCALE.EQ.1)THEN IF(INFO.EQ.0)THEN IMAX=N ELSE IMAX=INFO-1 END IF CALL GAL_DSCAL(IMAX,ONE/SIGMA,W,1) END IF * * Set WORK(1) to optimal workspace size. * WORK(1)=LWKOPT * RETURN * * End of DSYEV * END ga-5.9.2/LinAlg/lapack+blas/gal_dsygs2.f000066400000000000000000000171041500715745200176520ustar00rootroot00000000000000*> \brief \b DSYGS2 reduces a symmetric definite generalized eigenproblem to standard form, using the factorization results obtained from spotrf (unblocked algorithm). * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DSYGS2 + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DSYGS2( ITYPE, UPLO, N, A, LDA, B, LDB, INFO ) * * .. Scalar Arguments .. * CHARACTER UPLO * INTEGER INFO, ITYPE, LDA, LDB, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), B( LDB, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DSYGS2 reduces a real symmetric-definite generalized eigenproblem *> to standard form. *> *> If ITYPE = 1, the problem is A*x = lambda*B*x, *> and A is overwritten by inv(U**T)*A*inv(U) or inv(L)*A*inv(L**T) *> *> If ITYPE = 2 or 3, the problem is A*B*x = lambda*x or *> B*A*x = lambda*x, and A is overwritten by U*A*U**T or L**T *A*L. *> *> B must have been previously factorized as U**T *U or L*L**T by DPOTRF. *> \endverbatim * * Arguments: * ========== * *> \param[in] ITYPE *> \verbatim *> ITYPE is INTEGER *> = 1: compute inv(U**T)*A*inv(U) or inv(L)*A*inv(L**T); *> = 2 or 3: compute U*A*U**T or L**T *A*L. *> \endverbatim *> *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> Specifies whether the upper or lower triangular part of the *> symmetric matrix A is stored, and how B has been factorized. *> = 'U': Upper triangular *> = 'L': Lower triangular *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrices A and B. N >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is 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 transformed matrix, stored in the *> same format as A. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,N). *> \endverbatim *> *> \param[in] B *> \verbatim *> B is DOUBLE PRECISION array, dimension (LDB,N) *> The triangular factor from the Cholesky factorization of B, *> as returned by DPOTRF. *> \endverbatim *> *> \param[in] LDB *> \verbatim *> LDB is INTEGER *> The leading dimension of the array B. LDB >= max(1,N). *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit. *> < 0: if INFO = -i, the i-th argument had an illegal value. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup doubleSYcomputational * * ===================================================================== SUBROUTINE GAL_DSYGS2(ITYPE,UPLO,N,A,LDA,B,LDB,INFO) * * -- LAPACK computational routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. CHARACTERUPLO INTEGER INFO,ITYPE,LDA,LDB,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),B(LDB,*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,HALF PARAMETER (ONE=1.0D0,HALF=0.5D0) * .. * .. Local Scalars .. LOGICAL UPPER INTEGER K DOUBLE PRECISION AKK,BKK,CT * .. * .. External Subroutines .. EXTERNAL GAL_DAXPY,GAL_DSCAL, $GAL_DSYR2,GAL_DTRMV,GAL_DTRSV,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. Executable Statements .. * * Test the input parameters. * INFO=0 UPPER=GAL_LSAME(UPLO,'U') IF(ITYPE.LT.1.OR.ITYPE.GT.3)THEN INFO=-1 ELSE IF(.NOT.UPPER.AND..NOT.GAL_LSAME(UPLO,'L'))THEN INFO=-2 ELSE IF(N.LT.0)THEN INFO=-3 ELSE IF(LDA.LT.MAX(1,N))THEN INFO=-5 ELSE IF(LDB.LT.MAX(1,N))THEN INFO=-7 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DSYGS2',-INFO) RETURN END IF * IF(ITYPE.EQ.1)THEN IF(UPPER)THEN * * Compute inv(U**T)*A*inv(U) * DO 10K=1,N * * Update the upper triangle of A(k:n,k:n) * AKK=A(K,K) BKK=B(K,K) AKK=AKK/BKK**2 A(K,K)=AKK IF(K.LT.N)THEN CALL GAL_DSCAL(N-K,ONE/BKK,A(K,K+1),LDA) CT=-HALF*AKK CALL GAL_DAXPY(N-K,CT,B(K,K+1),LDB,A(K,K+1), $LDA) CALL GAL_DSYR2(UPLO,N-K,-ONE,A(K,K+1),LDA, $B(K,K+1),LDB,A(K+1,K+1),LDA) CALL GAL_DAXPY(N-K,CT,B(K,K+1),LDB,A(K,K+1), $LDA) CALL GAL_DTRSV(UPLO,'TRANSPOSE','NON-UNIT',N-K, $B(K+1,K+1),LDB,A(K,K+1),LDA) END IF 10 CONTINUE ELSE * * Compute inv(L)*A*inv(L**T) * DO 20K=1,N * * Update the lower triangle of A(k:n,k:n) * AKK=A(K,K) BKK=B(K,K) AKK=AKK/BKK**2 A(K,K)=AKK IF(K.LT.N)THEN CALL GAL_DSCAL(N-K,ONE/BKK,A(K+1,K),1) CT=-HALF*AKK CALL GAL_DAXPY(N-K,CT,B(K+1,K),1,A(K+1,K),1) CALL GAL_DSYR2(UPLO,N-K,-ONE,A(K+1,K),1, $B(K+1,K),1,A(K+1,K+1),LDA) CALL GAL_DAXPY(N-K,CT,B(K+1,K),1,A(K+1,K),1) CALL GAL_DTRSV(UPLO,'NOTRANSPOSE','NON-UNIT',N-K, $B(K+1,K+1),LDB,A(K+1,K),1) END IF 20 CONTINUE END IF ELSE IF(UPPER)THEN * * Compute U*A*U**T * DO 30K=1,N * * Update the upper triangle of A(1:k,1:k) * AKK=A(K,K) BKK=B(K,K) CALL GAL_DTRMV(UPLO,'NOTRANSPOSE','NON-UNIT',K-1,B, $LDB,A(1,K),1) CT=HALF*AKK CALL GAL_DAXPY(K-1,CT,B(1,K),1,A(1,K),1) CALL GAL_DSYR2(UPLO,K-1,ONE,A(1,K),1,B(1,K),1, $A,LDA) CALL GAL_DAXPY(K-1,CT,B(1,K),1,A(1,K),1) CALL GAL_DSCAL(K-1,BKK,A(1,K),1) A(K,K)=AKK*BKK**2 30 CONTINUE ELSE * * Compute L**T *A*L * DO 40K=1,N * * Update the lower triangle of A(1:k,1:k) * AKK=A(K,K) BKK=B(K,K) CALL GAL_DTRMV(UPLO,'TRANSPOSE','NON-UNIT',K-1,B,LDB, $A(K,1),LDA) CT=HALF*AKK CALL GAL_DAXPY(K-1,CT,B(K,1),LDB,A(K,1),LDA) CALL GAL_DSYR2(UPLO,K-1,ONE,A(K,1),LDA,B(K,1), $LDB,A,LDA) CALL GAL_DAXPY(K-1,CT,B(K,1),LDB,A(K,1),LDA) CALL GAL_DSCAL(K-1,BKK,A(K,1),LDA) A(K,K)=AKK*BKK**2 40 CONTINUE END IF END IF RETURN * * End of DSYGS2 * END ga-5.9.2/LinAlg/lapack+blas/gal_dsygst.f000066400000000000000000000213251500715745200177540ustar00rootroot00000000000000*> \brief \b DSYGST * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DSYGST + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DSYGST( ITYPE, UPLO, N, A, LDA, B, LDB, INFO ) * * .. Scalar Arguments .. * CHARACTER UPLO * INTEGER INFO, ITYPE, LDA, LDB, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), B( LDB, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DSYGST reduces a real symmetric-definite generalized eigenproblem *> to standard form. *> *> If ITYPE = 1, the problem is A*x = lambda*B*x, *> and A is overwritten by inv(U**T)*A*inv(U) or inv(L)*A*inv(L**T) *> *> If ITYPE = 2 or 3, the problem is A*B*x = lambda*x or *> B*A*x = lambda*x, and A is overwritten by U*A*U**T or L**T*A*L. *> *> B must have been previously factorized as U**T*U or L*L**T by DPOTRF. *> \endverbatim * * Arguments: * ========== * *> \param[in] ITYPE *> \verbatim *> ITYPE is INTEGER *> = 1: compute inv(U**T)*A*inv(U) or inv(L)*A*inv(L**T); *> = 2 or 3: compute U*A*U**T or L**T*A*L. *> \endverbatim *> *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> = 'U': Upper triangle of A is stored and B is factored as *> U**T*U; *> = 'L': Lower triangle of A is stored and B is factored as *> L*L**T. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrices A and B. N >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is 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 transformed matrix, stored in the *> same format as A. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,N). *> \endverbatim *> *> \param[in] B *> \verbatim *> B is DOUBLE PRECISION array, dimension (LDB,N) *> The triangular factor from the Cholesky factorization of B, *> as returned by DPOTRF. *> \endverbatim *> *> \param[in] LDB *> \verbatim *> LDB is INTEGER *> The leading dimension of the array B. LDB >= max(1,N). *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument had an illegal value *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup doubleSYcomputational * * ===================================================================== SUBROUTINE GAL_DSYGST(ITYPE,UPLO,N,A,LDA,B,LDB,INFO) * * -- LAPACK computational routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. CHARACTERUPLO INTEGER INFO,ITYPE,LDA,LDB,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),B(LDB,*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,HALF PARAMETER (ONE=1.0D0,HALF=0.5D0) * .. * .. Local Scalars .. LOGICAL UPPER INTEGER K,KB,NB * .. * .. External Subroutines .. EXTERNAL GAL_DSYGS2,GAL_DSYMM, $GAL_DSYR2K,GAL_DTRMM,GAL_DTRSM,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX,MIN * .. * .. External Functions .. LOGICAL GAL_LSAME INTEGER GAL_ILAENV EXTERNAL GAL_LSAME,GAL_ILAENV * .. * .. Executable Statements .. * * Test the input parameters. * INFO=0 UPPER=GAL_LSAME(UPLO,'U') IF(ITYPE.LT.1.OR.ITYPE.GT.3)THEN INFO=-1 ELSE IF(.NOT.UPPER.AND..NOT.GAL_LSAME(UPLO,'L'))THEN INFO=-2 ELSE IF(N.LT.0)THEN INFO=-3 ELSE IF(LDA.LT.MAX(1,N))THEN INFO=-5 ELSE IF(LDB.LT.MAX(1,N))THEN INFO=-7 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DSYGST',-INFO) RETURN END IF * * Quick return if possible * IF(N.EQ.0) $RETURN * * Determine the block size for this environment. * NB=GAL_ILAENV(1,'GAL_DSYGST',UPLO,N,-1,-1,-1) * IF(NB.LE.1.OR.NB.GE.N)THEN * * Use unblocked code * CALL GAL_DSYGS2(ITYPE,UPLO,N,A,LDA,B,LDB,INFO) ELSE * * Use blocked code * IF(ITYPE.EQ.1)THEN IF(UPPER)THEN * * Compute inv(U**T)*A*inv(U) * DO 10K=1,N,NB KB=MIN(N-K+1,NB) * * Update the upper triangle of A(k:n,k:n) * CALL GAL_DSYGS2(ITYPE,UPLO,KB,A(K,K),LDA, $B(K,K),LDB,INFO) IF(K+KB.LE.N)THEN CALL GAL_DTRSM('LEFT',UPLO,'TRANSPOSE','NON-UNIT', $KB,N-K-KB+1,ONE,B(K,K),LDB, $A(K,K+KB),LDA) CALL GAL_DSYMM('LEFT',UPLO,KB,N-K-KB+1,-HALF, $A(K,K),LDA,B(K,K+KB),LDB,ONE, $A(K,K+KB),LDA) CALL GAL_DSYR2K(UPLO,'TRANSPOSE',N-K-KB+1,KB,-ONE, $A(K,K+KB),LDA,B(K,K+KB),LDB, $ONE,A(K+KB,K+KB),LDA) CALL GAL_DSYMM('LEFT',UPLO,KB,N-K-KB+1,-HALF, $A(K,K),LDA,B(K,K+KB),LDB,ONE, $A(K,K+KB),LDA) CALL GAL_DTRSM('RIGHT',UPLO,'NOTRANSPOSE', $'NON-UNIT',KB,N-K-KB+1,ONE, $B(K+KB,K+KB),LDB,A(K,K+KB), $LDA) END IF 10 CONTINUE ELSE * * Compute inv(L)*A*inv(L**T) * DO 20K=1,N,NB KB=MIN(N-K+1,NB) * * Update the lower triangle of A(k:n,k:n) * CALL GAL_DSYGS2(ITYPE,UPLO,KB,A(K,K),LDA, $B(K,K),LDB,INFO) IF(K+KB.LE.N)THEN CALL GAL_DTRSM('RIGHT',UPLO,'TRANSPOSE','NON-UNIT', $N-K-KB+1,KB,ONE,B(K,K),LDB, $A(K+KB,K),LDA) CALL GAL_DSYMM('RIGHT',UPLO,N-K-KB+1,KB,-HALF, $A(K,K),LDA,B(K+KB,K),LDB,ONE, $A(K+KB,K),LDA) CALL GAL_DSYR2K(UPLO,'NOTRANSPOSE',N-K-KB+1,KB, $-ONE,A(K+KB,K),LDA,B(K+KB,K), $LDB,ONE,A(K+KB,K+KB),LDA) CALL GAL_DSYMM('RIGHT',UPLO,N-K-KB+1,KB,-HALF, $A(K,K),LDA,B(K+KB,K),LDB,ONE, $A(K+KB,K),LDA) CALL GAL_DTRSM('LEFT',UPLO,'NOTRANSPOSE', $'NON-UNIT',N-K-KB+1,KB,ONE, $B(K+KB,K+KB),LDB,A(K+KB,K), $LDA) END IF 20 CONTINUE END IF ELSE IF(UPPER)THEN * * Compute U*A*U**T * DO 30K=1,N,NB KB=MIN(N-K+1,NB) * * Update the upper triangle of A(1:k+kb-1,1:k+kb-1) * CALL GAL_DTRMM('LEFT',UPLO,'NOTRANSPOSE','NON-UNIT', $K-1,KB,ONE,B,LDB,A(1,K),LDA) CALL GAL_DSYMM('RIGHT',UPLO,K-1,KB,HALF,A(K,K), $LDA,B(1,K),LDB,ONE,A(1,K),LDA) CALL GAL_DSYR2K(UPLO,'NOTRANSPOSE',K-1,KB,ONE, $A(1,K),LDA,B(1,K),LDB,ONE,A, $LDA) CALL GAL_DSYMM('RIGHT',UPLO,K-1,KB,HALF,A(K,K), $LDA,B(1,K),LDB,ONE,A(1,K),LDA) CALL GAL_DTRMM('RIGHT',UPLO,'TRANSPOSE','NON-UNIT', $K-1,KB,ONE,B(K,K),LDB,A(1,K), $LDA) CALL GAL_DSYGS2(ITYPE,UPLO,KB,A(K,K),LDA, $B(K,K),LDB,INFO) 30 CONTINUE ELSE * * Compute L**T*A*L * DO 40K=1,N,NB KB=MIN(N-K+1,NB) * * Update the lower triangle of A(1:k+kb-1,1:k+kb-1) * CALL GAL_DTRMM('RIGHT',UPLO,'NOTRANSPOSE','NON-UNIT', $KB,K-1,ONE,B,LDB,A(K,1),LDA) CALL GAL_DSYMM('LEFT',UPLO,KB,K-1,HALF,A(K,K), $LDA,B(K,1),LDB,ONE,A(K,1),LDA) CALL GAL_DSYR2K(UPLO,'TRANSPOSE',K-1,KB,ONE, $A(K,1),LDA,B(K,1),LDB,ONE,A, $LDA) CALL GAL_DSYMM('LEFT',UPLO,KB,K-1,HALF,A(K,K), $LDA,B(K,1),LDB,ONE,A(K,1),LDA) CALL GAL_DTRMM('LEFT',UPLO,'TRANSPOSE','NON-UNIT',KB, $K-1,ONE,B(K,K),LDB,A(K,1),LDA) CALL GAL_DSYGS2(ITYPE,UPLO,KB,A(K,K),LDA, $B(K,K),LDB,INFO) 40 CONTINUE END IF END IF END IF RETURN * * End of DSYGST * END ga-5.9.2/LinAlg/lapack+blas/gal_dsygv.f000066400000000000000000000216441500715745200175770ustar00rootroot00000000000000*> \brief \b DSYGST * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DSYGV + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DSYGV( ITYPE, JOBZ, UPLO, N, A, LDA, B, LDB, W, WORK, * LWORK, INFO ) * * .. Scalar Arguments .. * CHARACTER JOBZ, UPLO * INTEGER INFO, ITYPE, LDA, LDB, LWORK, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), B( LDB, * ), W( * ), WORK( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DSYGV computes all the eigenvalues, and optionally, the eigenvectors *> of a real generalized symmetric-definite eigenproblem, of the form *> A*x=(lambda)*B*x, A*Bx=(lambda)*x, or B*A*x=(lambda)*x. *> Here A and B are assumed to be symmetric and B is also *> positive definite. *> \endverbatim * * Arguments: * ========== * *> \param[in] ITYPE *> \verbatim *> ITYPE is INTEGER *> Specifies the problem type to be solved: *> = 1: A*x = (lambda)*B*x *> = 2: A*B*x = (lambda)*x *> = 3: B*A*x = (lambda)*x *> \endverbatim *> *> \param[in] JOBZ *> \verbatim *> JOBZ is CHARACTER*1 *> = 'N': Compute eigenvalues only; *> = 'V': Compute eigenvalues and eigenvectors. *> \endverbatim *> *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> = 'U': Upper triangles of A and B are stored; *> = 'L': Lower triangles of A and B are stored. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrices A and B. N >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is 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. If UPLO = 'L', *> the leading N-by-N lower triangular part of A contains *> the lower triangular part of the matrix A. *> *> On exit, if JOBZ = 'V', then if INFO = 0, A contains the *> matrix Z of eigenvectors. The eigenvectors are normalized *> as follows: *> if ITYPE = 1 or 2, Z**T*B*Z = I; *> if ITYPE = 3, Z**T*inv(B)*Z = I. *> If JOBZ = 'N', then on exit the upper triangle (if UPLO='U') *> or the lower triangle (if UPLO='L') of A, including the *> diagonal, is destroyed. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,N). *> \endverbatim *> *> \param[in,out] B *> \verbatim *> B is DOUBLE PRECISION array, dimension (LDB, N) *> On entry, the symmetric positive definite matrix B. *> If UPLO = 'U', the leading N-by-N upper triangular part of B *> contains the upper triangular part of the matrix B. *> If UPLO = 'L', the leading N-by-N lower triangular part of B *> contains the lower triangular part of the matrix B. *> *> On exit, if INFO <= N, the part of B containing the matrix is *> overwritten by the triangular factor U or L from the Cholesky *> factorization B = U**T*U or B = L*L**T. *> \endverbatim *> *> \param[in] LDB *> \verbatim *> LDB is INTEGER *> The leading dimension of the array B. LDB >= max(1,N). *> \endverbatim *> *> \param[out] W *> \verbatim *> W is DOUBLE PRECISION array, dimension (N) *> If INFO = 0, the eigenvalues in ascending order. *> \endverbatim *> *> \param[out] WORK *> \verbatim *> WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)) *> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. *> \endverbatim *> *> \param[in] LWORK *> \verbatim *> LWORK is INTEGER *> The length of the array WORK. LWORK >= max(1,3*N-1). *> For optimal efficiency, LWORK >= (NB+2)*N, *> where NB is the blocksize for DSYTRD returned by ILAENV. *> *> If LWORK = -1, then a workspace query is assumed; the routine *> only calculates the optimal size of the WORK array, returns *> this value as the first entry of the WORK array, and no error *> message related to LWORK is issued by XERBLA. *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument had an illegal value *> > 0: DPOTRF or DSYEV returned an error code: *> <= N: if INFO = i, DSYEV failed to converge; *> i off-diagonal elements of an intermediate *> tridiagonal form did not converge to zero; *> > N: if INFO = N + i, for 1 <= i <= N, then the leading *> minor of order i of B is not positive definite. *> The factorization of B could not be completed and *> no eigenvalues or eigenvectors were computed. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup doubleSYeigen * * ===================================================================== SUBROUTINE GAL_DSYGV(ITYPE,JOBZ,UPLO,N,A,LDA,B,LDB,W,WORK, $LWORK,INFO) * * -- LAPACK driver routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. CHARACTERJOBZ,UPLO INTEGER INFO,ITYPE,LDA,LDB,LWORK,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),B(LDB,*),W(*),WORK(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE PARAMETER (ONE=1.0D+0) * .. * .. Local Scalars .. LOGICAL LQUERY,UPPER,WANTZ CHARACTERTRANS INTEGER LWKMIN,LWKOPT,NB,NEIG * .. * .. External Functions .. LOGICAL GAL_LSAME INTEGER GAL_ILAENV EXTERNAL GAL_LSAME,GAL_ILAENV * .. * .. External Subroutines .. EXTERNAL GAL_DPOTRF,GAL_DSYEV, $GAL_DSYGST,GAL_DTRMM,GAL_DTRSM,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Executable Statements .. * * Test the input parameters. * WANTZ=GAL_LSAME(JOBZ,'V') UPPER=GAL_LSAME(UPLO,'U') LQUERY=(LWORK.EQ.-1) * INFO=0 IF(ITYPE.LT.1.OR.ITYPE.GT.3)THEN INFO=-1 ELSE IF(.NOT.(WANTZ.OR.GAL_LSAME(JOBZ,'N')))THEN INFO=-2 ELSE IF(.NOT.(UPPER.OR.GAL_LSAME(UPLO,'L')))THEN INFO=-3 ELSE IF(N.LT.0)THEN INFO=-4 ELSE IF(LDA.LT.MAX(1,N))THEN INFO=-6 ELSE IF(LDB.LT.MAX(1,N))THEN INFO=-8 END IF * IF(INFO.EQ.0)THEN LWKMIN=MAX(1,3*N-1) NB=GAL_ILAENV(1,'DSYTRD',UPLO,N,-1,-1,-1) LWKOPT=MAX(LWKMIN,(NB+2)*N) WORK(1)=LWKOPT * IF(LWORK.LT.LWKMIN.AND..NOT.LQUERY)THEN INFO=-11 END IF END IF * IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DSYGV',-INFO) RETURN ELSE IF(LQUERY)THEN RETURN END IF * * Quick return if possible * IF(N.EQ.0) $RETURN * * Form a Cholesky factorization of B. * CALL GAL_DPOTRF(UPLO,N,B,LDB,INFO) IF(INFO.NE.0)THEN INFO=N+INFO RETURN END IF * * Transform problem to standard eigenvalue problem and solve. * CALL GAL_DSYGST(ITYPE,UPLO,N,A,LDA,B,LDB,INFO) CALL GAL_DSYEV(JOBZ,UPLO,N,A,LDA,W,WORK,LWORK,INFO) * IF(WANTZ)THEN * * Backtransform eigenvectors to the original problem. * NEIG=N IF(INFO.GT.0) $NEIG=INFO-1 IF(ITYPE.EQ.1.OR.ITYPE.EQ.2)THEN * * For A*x=(lambda)*B*x and A*B*x=(lambda)*x; * backtransform eigenvectors: x = inv(L)**T*y or inv(U)*y * IF(UPPER)THEN TRANS='N' ELSE TRANS='T' END IF * CALL GAL_DTRSM('LEFT',UPLO,TRANS,'NON-UNIT',N,NEIG,ONE, $B,LDB,A,LDA) * ELSE IF(ITYPE.EQ.3)THEN * * For B*A*x=(lambda)*x; * backtransform eigenvectors: x = L*y or U**T*y * IF(UPPER)THEN TRANS='T' ELSE TRANS='N' END IF * CALL GAL_DTRMM('LEFT',UPLO,TRANS,'NON-UNIT',N,NEIG,ONE, $B,LDB,A,LDA) END IF END IF * WORK(1)=LWKOPT RETURN * * End of DSYGV * END ga-5.9.2/LinAlg/lapack+blas/gal_dsymm.f000066400000000000000000000233731500715745200175750ustar00rootroot00000000000000*> \brief \b DSYMM * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DSYMM(SIDE,UPLO,M,N,ALPHA,A,LDA,B,LDB,BETA,C,LDC) * * .. Scalar Arguments .. * DOUBLE PRECISION ALPHA,BETA * INTEGER LDA,LDB,LDC,M,N * CHARACTER SIDE,UPLO * .. * .. Array Arguments .. * DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DSYMM performs one of the matrix-matrix operations *> *> C := alpha*A*B + beta*C, *> *> or *> *> C := alpha*B*A + beta*C, *> *> where alpha and beta are scalars, A is a symmetric matrix and B and *> C are m by n matrices. *> \endverbatim * * Arguments: * ========== * *> \param[in] SIDE *> \verbatim *> SIDE is CHARACTER*1 *> On entry, SIDE specifies whether the symmetric matrix A *> appears on the left or right in the operation as follows: *> *> SIDE = 'L' or 'l' C := alpha*A*B + beta*C, *> *> SIDE = 'R' or 'r' C := alpha*B*A + beta*C, *> \endverbatim *> *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> On entry, UPLO specifies whether the upper or lower *> triangular part of the symmetric matrix A is to be *> referenced as follows: *> *> UPLO = 'U' or 'u' Only the upper triangular part of the *> symmetric matrix is to be referenced. *> *> UPLO = 'L' or 'l' Only the lower triangular part of the *> symmetric matrix is to be referenced. *> \endverbatim *> *> \param[in] M *> \verbatim *> M is INTEGER *> On entry, M specifies the number of rows of the matrix C. *> M must be at least zero. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the number of columns of the matrix C. *> N must be at least zero. *> \endverbatim *> *> \param[in] ALPHA *> \verbatim *> ALPHA is DOUBLE PRECISION. *> On entry, ALPHA specifies the scalar alpha. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array of DIMENSION ( LDA, ka ), where ka is *> m when SIDE = 'L' or 'l' and is n otherwise. *> Before entry with SIDE = 'L' or 'l', the m by m part of *> the array A must contain the symmetric matrix, such that *> when UPLO = 'U' or 'u', the leading m by m upper triangular *> part of the array A must contain the upper triangular part *> of the symmetric matrix and the strictly lower triangular *> part of A is not referenced, and when UPLO = 'L' or 'l', *> the leading m by m lower triangular part of the array A *> must contain the lower triangular part of the symmetric *> matrix and the strictly upper triangular part of A is not *> referenced. *> Before entry with SIDE = 'R' or 'r', the n by n part of *> the array A must contain the symmetric matrix, such that *> when UPLO = 'U' or 'u', the leading n by n upper triangular *> part of the array A must contain the upper triangular part *> of the symmetric matrix and the strictly lower triangular *> part of A is not referenced, and when UPLO = 'L' or 'l', *> the leading n by n lower triangular part of the array A *> must contain the lower triangular part of the symmetric *> matrix and the strictly upper triangular part of A is not *> referenced. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. When SIDE = 'L' or 'l' then *> LDA must be at least max( 1, m ), otherwise LDA must be at *> least max( 1, n ). *> \endverbatim *> *> \param[in] B *> \verbatim *> B is DOUBLE PRECISION array of DIMENSION ( LDB, n ). *> Before entry, the leading m by n part of the array B must *> contain the matrix B. *> \endverbatim *> *> \param[in] LDB *> \verbatim *> LDB is INTEGER *> On entry, LDB specifies the first dimension of B as declared *> in the calling (sub) program. LDB must be at least *> max( 1, m ). *> \endverbatim *> *> \param[in] BETA *> \verbatim *> BETA is DOUBLE PRECISION. *> On entry, BETA specifies the scalar beta. When BETA is *> supplied as zero then C need not be set on input. *> \endverbatim *> *> \param[in,out] C *> \verbatim *> C is DOUBLE PRECISION array of DIMENSION ( LDC, n ). *> Before entry, the leading m by n part of the array C must *> contain the matrix C, except when beta is zero, in which *> case C need not be set on entry. *> On exit, the array C is overwritten by the m by n updated *> matrix. *> \endverbatim *> *> \param[in] LDC *> \verbatim *> LDC is INTEGER *> On entry, LDC specifies the first dimension of C as declared *> in the calling (sub) program. LDC must be at least *> max( 1, m ). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level3 * *> \par Further Details: * ===================== *> *> \verbatim *> *> Level 3 Blas routine. *> *> -- Written on 8-February-1989. *> Jack Dongarra, Argonne National Laboratory. *> Iain Duff, AERE Harwell. *> Jeremy Du Croz, Numerical Algorithms Group Ltd. *> Sven Hammarling, Numerical Algorithms Group Ltd. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DSYMM(SIDE,UPLO,M,N,ALPHA,A,LDA,B,LDB,BETA,C,LDC) * * -- Reference BLAS level3 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. DOUBLE PRECISION ALPHA,BETA INTEGER LDA,LDB,LDC,M,N CHARACTERSIDE,UPLO * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) * .. * * ===================================================================== * * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Local Scalars .. DOUBLE PRECISION TEMP1,TEMP2 INTEGER I,INFO,J,K,NROWA LOGICAL UPPER * .. * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * * Set NROWA as the number of rows of A. * IF(GAL_LSAME(SIDE,'L'))THEN NROWA=M ELSE NROWA=N END IF UPPER=GAL_LSAME(UPLO,'U') * * Test the input parameters. * INFO=0 IF((.NOT.GAL_LSAME(SIDE,'L')).AND.(.NOT.GAL_LSAME(SIDE,'R')))THEN INFO=1 ELSE IF((.NOT.UPPER).AND.(.NOT.GAL_LSAME(UPLO,'L')))THEN INFO=2 ELSE IF(M.LT.0)THEN INFO=3 ELSE IF(N.LT.0)THEN INFO=4 ELSE IF(LDA.LT.MAX(1,NROWA))THEN INFO=7 ELSE IF(LDB.LT.MAX(1,M))THEN INFO=9 ELSE IF(LDC.LT.MAX(1,M))THEN INFO=12 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DSYMM',INFO) RETURN END IF * * Quick return if possible. * IF((M.EQ.0).OR.(N.EQ.0).OR. +((ALPHA.EQ.ZERO).AND.(BETA.EQ.ONE)))RETURN * * And when alpha.eq.zero. * IF(ALPHA.EQ.ZERO)THEN IF(BETA.EQ.ZERO)THEN DO 20J=1,N DO 10I=1,M C(I,J)=ZERO 10 CONTINUE 20 CONTINUE ELSE DO 40J=1,N DO 30I=1,M C(I,J)=BETA*C(I,J) 30 CONTINUE 40 CONTINUE END IF RETURN END IF * * Start the operations. * IF(GAL_LSAME(SIDE,'L'))THEN * * Form C := alpha*A*B + beta*C. * IF(UPPER)THEN DO 70J=1,N DO 60I=1,M TEMP1=ALPHA*B(I,J) TEMP2=ZERO DO 50K=1,I-1 C(K,J)=C(K,J)+TEMP1*A(K,I) TEMP2=TEMP2+B(K,J)*A(K,I) 50 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=TEMP1*A(I,I)+ALPHA*TEMP2 ELSE C(I,J)=BETA*C(I,J)+TEMP1*A(I,I)+ +ALPHA*TEMP2 END IF 60 CONTINUE 70 CONTINUE ELSE DO 100J=1,N DO 90I=M,1,-1 TEMP1=ALPHA*B(I,J) TEMP2=ZERO DO 80K=I+1,M C(K,J)=C(K,J)+TEMP1*A(K,I) TEMP2=TEMP2+B(K,J)*A(K,I) 80 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=TEMP1*A(I,I)+ALPHA*TEMP2 ELSE C(I,J)=BETA*C(I,J)+TEMP1*A(I,I)+ +ALPHA*TEMP2 END IF 90 CONTINUE 100 CONTINUE END IF ELSE * * Form C := alpha*B*A + beta*C. * DO 170J=1,N TEMP1=ALPHA*A(J,J) IF(BETA.EQ.ZERO)THEN DO 110I=1,M C(I,J)=TEMP1*B(I,J) 110 CONTINUE ELSE DO 120I=1,M C(I,J)=BETA*C(I,J)+TEMP1*B(I,J) 120 CONTINUE END IF DO 140K=1,J-1 IF(UPPER)THEN TEMP1=ALPHA*A(K,J) ELSE TEMP1=ALPHA*A(J,K) END IF DO 130I=1,M C(I,J)=C(I,J)+TEMP1*B(I,K) 130 CONTINUE 140 CONTINUE DO 160K=J+1,N IF(UPPER)THEN TEMP1=ALPHA*A(J,K) ELSE TEMP1=ALPHA*A(K,J) END IF DO 150I=1,M C(I,J)=C(I,J)+TEMP1*B(I,K) 150 CONTINUE 160 CONTINUE 170 CONTINUE END IF * RETURN * * End of DSYMM . * END ga-5.9.2/LinAlg/lapack+blas/gal_dsymv.f000066400000000000000000000200431500715745200175750ustar00rootroot00000000000000*> \brief \b DSYMV * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DSYMV(UPLO,N,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) * * .. Scalar Arguments .. * DOUBLE PRECISION ALPHA,BETA * INTEGER INCX,INCY,LDA,N * CHARACTER UPLO * .. * .. Array Arguments .. * DOUBLE PRECISION A(LDA,*),X(*),Y(*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DSYMV performs the matrix-vector operation *> *> y := alpha*A*x + beta*y, *> *> where alpha and beta are scalars, x and y are n element vectors and *> A is an n by n symmetric matrix. *> \endverbatim * * Arguments: * ========== * *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> On entry, UPLO specifies whether the upper or lower *> triangular part of the array A is to be referenced as *> follows: *> *> UPLO = 'U' or 'u' Only the upper triangular part of A *> is to be referenced. *> *> UPLO = 'L' or 'l' Only the lower triangular part of A *> is to be referenced. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the order of the matrix A. *> N must be at least zero. *> \endverbatim *> *> \param[in] ALPHA *> \verbatim *> ALPHA is DOUBLE PRECISION. *> On entry, ALPHA specifies the scalar alpha. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array of DIMENSION ( LDA, n ). *> Before entry with UPLO = 'U' or 'u', the leading n by n *> upper triangular part of the array A must contain the upper *> triangular part of the symmetric matrix and the strictly *> lower triangular part of A is not referenced. *> Before entry with UPLO = 'L' or 'l', the leading n by n *> lower triangular part of the array A must contain the lower *> triangular part of the symmetric matrix and the strictly *> upper triangular part of A is not referenced. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. LDA must be at least *> max( 1, n ). *> \endverbatim *> *> \param[in] X *> \verbatim *> X is DOUBLE PRECISION array of dimension at least *> ( 1 + ( n - 1 )*abs( INCX ) ). *> Before entry, the incremented array X must contain the n *> element vector x. *> \endverbatim *> *> \param[in] INCX *> \verbatim *> INCX is INTEGER *> On entry, INCX specifies the increment for the elements of *> X. INCX must not be zero. *> \endverbatim *> *> \param[in] BETA *> \verbatim *> BETA is DOUBLE PRECISION. *> On entry, BETA specifies the scalar beta. When BETA is *> supplied as zero then Y need not be set on input. *> \endverbatim *> *> \param[in,out] Y *> \verbatim *> Y is DOUBLE PRECISION array of dimension at least *> ( 1 + ( n - 1 )*abs( INCY ) ). *> Before entry, the incremented array Y must contain the n *> element vector y. On exit, Y is overwritten by the updated *> vector y. *> \endverbatim *> *> \param[in] INCY *> \verbatim *> INCY is INTEGER *> On entry, INCY specifies the increment for the elements of *> Y. INCY must not be zero. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level2 * *> \par Further Details: * ===================== *> *> \verbatim *> *> Level 2 Blas routine. *> The vector and matrix arguments are not referenced when N = 0, or M = 0 *> *> -- Written on 22-October-1986. *> Jack Dongarra, Argonne National Lab. *> Jeremy Du Croz, Nag Central Office. *> Sven Hammarling, Nag Central Office. *> Richard Hanson, Sandia National Labs. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DSYMV(UPLO,N,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) * * -- Reference BLAS level2 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. DOUBLE PRECISION ALPHA,BETA INTEGER INCX,INCY,LDA,N CHARACTERUPLO * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),X(*),Y(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * .. Local Scalars .. DOUBLE PRECISION TEMP1,TEMP2 INTEGER I,INFO,IX,IY,J,JX,JY,KX,KY * .. * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * * Test the input parameters. * INFO=0 IF(.NOT.GAL_LSAME(UPLO,'U').AND..NOT.GAL_LSAME(UPLO,'L'))THEN INFO=1 ELSE IF(N.LT.0)THEN INFO=2 ELSE IF(LDA.LT.MAX(1,N))THEN INFO=5 ELSE IF(INCX.EQ.0)THEN INFO=7 ELSE IF(INCY.EQ.0)THEN INFO=10 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DSYMV',INFO) RETURN END IF * * Quick return if possible. * IF((N.EQ.0).OR.((ALPHA.EQ.ZERO).AND.(BETA.EQ.ONE)))RETURN * * Set up the start points in X and Y. * IF(INCX.GT.0)THEN KX=1 ELSE KX=1-(N-1)*INCX END IF IF(INCY.GT.0)THEN KY=1 ELSE KY=1-(N-1)*INCY END IF * * Start the operations. In this version the elements of A are * accessed sequentially with one pass through the triangular part * of A. * * First form y := beta*y. * IF(BETA.NE.ONE)THEN IF(INCY.EQ.1)THEN IF(BETA.EQ.ZERO)THEN DO 10I=1,N Y(I)=ZERO 10 CONTINUE ELSE DO 20I=1,N Y(I)=BETA*Y(I) 20 CONTINUE END IF ELSE IY=KY IF(BETA.EQ.ZERO)THEN DO 30I=1,N Y(IY)=ZERO IY=IY+INCY 30 CONTINUE ELSE DO 40I=1,N Y(IY)=BETA*Y(IY) IY=IY+INCY 40 CONTINUE END IF END IF END IF IF(ALPHA.EQ.ZERO)RETURN IF(GAL_LSAME(UPLO,'U'))THEN * * Form y when A is stored in upper triangle. * IF((INCX.EQ.1).AND.(INCY.EQ.1))THEN DO 60J=1,N TEMP1=ALPHA*X(J) TEMP2=ZERO DO 50I=1,J-1 Y(I)=Y(I)+TEMP1*A(I,J) TEMP2=TEMP2+A(I,J)*X(I) 50 CONTINUE Y(J)=Y(J)+TEMP1*A(J,J)+ALPHA*TEMP2 60 CONTINUE ELSE JX=KX JY=KY DO 80J=1,N TEMP1=ALPHA*X(JX) TEMP2=ZERO IX=KX IY=KY DO 70I=1,J-1 Y(IY)=Y(IY)+TEMP1*A(I,J) TEMP2=TEMP2+A(I,J)*X(IX) IX=IX+INCX IY=IY+INCY 70 CONTINUE Y(JY)=Y(JY)+TEMP1*A(J,J)+ALPHA*TEMP2 JX=JX+INCX JY=JY+INCY 80 CONTINUE END IF ELSE * * Form y when A is stored in lower triangle. * IF((INCX.EQ.1).AND.(INCY.EQ.1))THEN DO 100J=1,N TEMP1=ALPHA*X(J) TEMP2=ZERO Y(J)=Y(J)+TEMP1*A(J,J) DO 90I=J+1,N Y(I)=Y(I)+TEMP1*A(I,J) TEMP2=TEMP2+A(I,J)*X(I) 90 CONTINUE Y(J)=Y(J)+ALPHA*TEMP2 100 CONTINUE ELSE JX=KX JY=KY DO 120J=1,N TEMP1=ALPHA*X(JX) TEMP2=ZERO Y(JY)=Y(JY)+TEMP1*A(J,J) IX=JX IY=JY DO 110I=J+1,N IX=IX+INCX IY=IY+INCY Y(IY)=Y(IY)+TEMP1*A(I,J) TEMP2=TEMP2+A(I,J)*X(IX) 110 CONTINUE Y(JY)=Y(JY)+ALPHA*TEMP2 JX=JX+INCX JY=JY+INCY 120 CONTINUE END IF END IF * RETURN * * End of DSYMV . * END ga-5.9.2/LinAlg/lapack+blas/gal_dsyr2.f000066400000000000000000000167561500715745200175160ustar00rootroot00000000000000*> \brief \b DSYR2 * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DSYR2(UPLO,N,ALPHA,X,INCX,Y,INCY,A,LDA) * * .. Scalar Arguments .. * DOUBLE PRECISION ALPHA * INTEGER INCX,INCY,LDA,N * CHARACTER UPLO * .. * .. Array Arguments .. * DOUBLE PRECISION A(LDA,*),X(*),Y(*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DSYR2 performs the symmetric rank 2 operation *> *> A := alpha*x*y**T + alpha*y*x**T + A, *> *> where alpha is a scalar, x and y are n element vectors and A is an n *> by n symmetric matrix. *> \endverbatim * * Arguments: * ========== * *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> On entry, UPLO specifies whether the upper or lower *> triangular part of the array A is to be referenced as *> follows: *> *> UPLO = 'U' or 'u' Only the upper triangular part of A *> is to be referenced. *> *> UPLO = 'L' or 'l' Only the lower triangular part of A *> is to be referenced. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the order of the matrix A. *> N must be at least zero. *> \endverbatim *> *> \param[in] ALPHA *> \verbatim *> ALPHA is DOUBLE PRECISION. *> On entry, ALPHA specifies the scalar alpha. *> \endverbatim *> *> \param[in] X *> \verbatim *> X is DOUBLE PRECISION array of dimension at least *> ( 1 + ( n - 1 )*abs( INCX ) ). *> Before entry, the incremented array X must contain the n *> element vector x. *> \endverbatim *> *> \param[in] INCX *> \verbatim *> INCX is INTEGER *> On entry, INCX specifies the increment for the elements of *> X. INCX must not be zero. *> \endverbatim *> *> \param[in] Y *> \verbatim *> Y is DOUBLE PRECISION array of dimension at least *> ( 1 + ( n - 1 )*abs( INCY ) ). *> Before entry, the incremented array Y must contain the n *> element vector y. *> \endverbatim *> *> \param[in] INCY *> \verbatim *> INCY is INTEGER *> On entry, INCY specifies the increment for the elements of *> Y. INCY must not be zero. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is DOUBLE PRECISION array of DIMENSION ( LDA, n ). *> Before entry with UPLO = 'U' or 'u', the leading n by n *> upper triangular part of the array A must contain the upper *> triangular part of the symmetric matrix and the strictly *> lower triangular part of A is not referenced. On exit, the *> upper triangular part of the array A is overwritten by the *> upper triangular part of the updated matrix. *> Before entry with UPLO = 'L' or 'l', the leading n by n *> lower triangular part of the array A must contain the lower *> triangular part of the symmetric matrix and the strictly *> upper triangular part of A is not referenced. On exit, the *> lower triangular part of the array A is overwritten by the *> lower triangular part of the updated matrix. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. LDA must be at least *> max( 1, n ). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level2 * *> \par Further Details: * ===================== *> *> \verbatim *> *> Level 2 Blas routine. *> *> -- Written on 22-October-1986. *> Jack Dongarra, Argonne National Lab. *> Jeremy Du Croz, Nag Central Office. *> Sven Hammarling, Nag Central Office. *> Richard Hanson, Sandia National Labs. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DSYR2(UPLO,N,ALPHA,X,INCX,Y,INCY,A,LDA) * * -- Reference BLAS level2 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. DOUBLE PRECISION ALPHA INTEGER INCX,INCY,LDA,N CHARACTERUPLO * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),X(*),Y(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO PARAMETER (ZERO=0.0D+0) * .. * .. Local Scalars .. DOUBLE PRECISION TEMP1,TEMP2 INTEGER I,INFO,IX,IY,J,JX,JY,KX,KY * .. * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * * Test the input parameters. * INFO=0 IF(.NOT.GAL_LSAME(UPLO,'U').AND..NOT.GAL_LSAME(UPLO,'L'))THEN INFO=1 ELSE IF(N.LT.0)THEN INFO=2 ELSE IF(INCX.EQ.0)THEN INFO=5 ELSE IF(INCY.EQ.0)THEN INFO=7 ELSE IF(LDA.LT.MAX(1,N))THEN INFO=9 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DSYR2',INFO) RETURN END IF * * Quick return if possible. * IF((N.EQ.0).OR.(ALPHA.EQ.ZERO))RETURN * * Set up the start points in X and Y if the increments are not both * unity. * IF((INCX.NE.1).OR.(INCY.NE.1))THEN IF(INCX.GT.0)THEN KX=1 ELSE KX=1-(N-1)*INCX END IF IF(INCY.GT.0)THEN KY=1 ELSE KY=1-(N-1)*INCY END IF JX=KX JY=KY END IF * * Start the operations. In this version the elements of A are * accessed sequentially with one pass through the triangular part * of A. * IF(GAL_LSAME(UPLO,'U'))THEN * * Form A when A is stored in the upper triangle. * IF((INCX.EQ.1).AND.(INCY.EQ.1))THEN DO 20J=1,N IF((X(J).NE.ZERO).OR.(Y(J).NE.ZERO))THEN TEMP1=ALPHA*Y(J) TEMP2=ALPHA*X(J) DO 10I=1,J A(I,J)=A(I,J)+X(I)*TEMP1+Y(I)*TEMP2 10 CONTINUE END IF 20 CONTINUE ELSE DO 40J=1,N IF((X(JX).NE.ZERO).OR.(Y(JY).NE.ZERO))THEN TEMP1=ALPHA*Y(JY) TEMP2=ALPHA*X(JX) IX=KX IY=KY DO 30I=1,J A(I,J)=A(I,J)+X(IX)*TEMP1+Y(IY)*TEMP2 IX=IX+INCX IY=IY+INCY 30 CONTINUE END IF JX=JX+INCX JY=JY+INCY 40 CONTINUE END IF ELSE * * Form A when A is stored in the lower triangle. * IF((INCX.EQ.1).AND.(INCY.EQ.1))THEN DO 60J=1,N IF((X(J).NE.ZERO).OR.(Y(J).NE.ZERO))THEN TEMP1=ALPHA*Y(J) TEMP2=ALPHA*X(J) DO 50I=J,N A(I,J)=A(I,J)+X(I)*TEMP1+Y(I)*TEMP2 50 CONTINUE END IF 60 CONTINUE ELSE DO 80J=1,N IF((X(JX).NE.ZERO).OR.(Y(JY).NE.ZERO))THEN TEMP1=ALPHA*Y(JY) TEMP2=ALPHA*X(JX) IX=JX IY=JY DO 70I=J,N A(I,J)=A(I,J)+X(IX)*TEMP1+Y(IY)*TEMP2 IX=IX+INCX IY=IY+INCY 70 CONTINUE END IF JX=JX+INCX JY=JY+INCY 80 CONTINUE END IF END IF * RETURN * * End of DSYR2 . * END ga-5.9.2/LinAlg/lapack+blas/gal_dsyr2k.f000066400000000000000000000246551500715745200176660ustar00rootroot00000000000000*> \brief \b DSYR2K * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DSYR2K(UPLO,TRANS,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) * * .. Scalar Arguments .. * DOUBLE PRECISION ALPHA,BETA * INTEGER K,LDA,LDB,LDC,N * CHARACTER TRANS,UPLO * .. * .. Array Arguments .. * DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DSYR2K performs one of the symmetric rank 2k operations *> *> C := alpha*A*B**T + alpha*B*A**T + beta*C, *> *> or *> *> C := alpha*A**T*B + alpha*B**T*A + beta*C, *> *> where alpha and beta are scalars, C is an n by n symmetric matrix *> and A and B are n by k matrices in the first case and k by n *> matrices in the second case. *> \endverbatim * * Arguments: * ========== * *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> On entry, UPLO specifies whether the upper or lower *> triangular part of the array C is to be referenced as *> follows: *> *> UPLO = 'U' or 'u' Only the upper triangular part of C *> is to be referenced. *> *> UPLO = 'L' or 'l' Only the lower triangular part of C *> is to be referenced. *> \endverbatim *> *> \param[in] TRANS *> \verbatim *> TRANS is CHARACTER*1 *> On entry, TRANS specifies the operation to be performed as *> follows: *> *> TRANS = 'N' or 'n' C := alpha*A*B**T + alpha*B*A**T + *> beta*C. *> *> TRANS = 'T' or 't' C := alpha*A**T*B + alpha*B**T*A + *> beta*C. *> *> TRANS = 'C' or 'c' C := alpha*A**T*B + alpha*B**T*A + *> beta*C. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the order of the matrix C. N must be *> at least zero. *> \endverbatim *> *> \param[in] K *> \verbatim *> K is INTEGER *> On entry with TRANS = 'N' or 'n', K specifies the number *> of columns of the matrices A and B, and on entry with *> TRANS = 'T' or 't' or 'C' or 'c', K specifies the number *> of rows of the matrices A and B. K must be at least zero. *> \endverbatim *> *> \param[in] ALPHA *> \verbatim *> ALPHA is DOUBLE PRECISION. *> On entry, ALPHA specifies the scalar alpha. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array of DIMENSION ( LDA, ka ), where ka is *> k when TRANS = 'N' or 'n', and is n otherwise. *> Before entry with TRANS = 'N' or 'n', the leading n by k *> part of the array A must contain the matrix A, otherwise *> the leading k by n part of the array A must contain the *> matrix A. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. When TRANS = 'N' or 'n' *> then LDA must be at least max( 1, n ), otherwise LDA must *> be at least max( 1, k ). *> \endverbatim *> *> \param[in] B *> \verbatim *> B is DOUBLE PRECISION array of DIMENSION ( LDB, kb ), where kb is *> k when TRANS = 'N' or 'n', and is n otherwise. *> Before entry with TRANS = 'N' or 'n', the leading n by k *> part of the array B must contain the matrix B, otherwise *> the leading k by n part of the array B must contain the *> matrix B. *> \endverbatim *> *> \param[in] LDB *> \verbatim *> LDB is INTEGER *> On entry, LDB specifies the first dimension of B as declared *> in the calling (sub) program. When TRANS = 'N' or 'n' *> then LDB must be at least max( 1, n ), otherwise LDB must *> be at least max( 1, k ). *> \endverbatim *> *> \param[in] BETA *> \verbatim *> BETA is DOUBLE PRECISION. *> On entry, BETA specifies the scalar beta. *> \endverbatim *> *> \param[in,out] C *> \verbatim *> C is DOUBLE PRECISION array of DIMENSION ( LDC, n ). *> Before entry with UPLO = 'U' or 'u', the leading n by n *> upper triangular part of the array C must contain the upper *> triangular part of the symmetric matrix and the strictly *> lower triangular part of C is not referenced. On exit, the *> upper triangular part of the array C is overwritten by the *> upper triangular part of the updated matrix. *> Before entry with UPLO = 'L' or 'l', the leading n by n *> lower triangular part of the array C must contain the lower *> triangular part of the symmetric matrix and the strictly *> upper triangular part of C is not referenced. On exit, the *> lower triangular part of the array C is overwritten by the *> lower triangular part of the updated matrix. *> \endverbatim *> *> \param[in] LDC *> \verbatim *> LDC is INTEGER *> On entry, LDC specifies the first dimension of C as declared *> in the calling (sub) program. LDC must be at least *> max( 1, n ). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level3 * *> \par Further Details: * ===================== *> *> \verbatim *> *> Level 3 Blas routine. *> *> *> -- Written on 8-February-1989. *> Jack Dongarra, Argonne National Laboratory. *> Iain Duff, AERE Harwell. *> Jeremy Du Croz, Numerical Algorithms Group Ltd. *> Sven Hammarling, Numerical Algorithms Group Ltd. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DSYR2K(UPLO,TRANS,N,K, $ALPHA,A,LDA,B,LDB,BETA,C,LDC) * * -- Reference BLAS level3 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. DOUBLE PRECISION ALPHA,BETA INTEGER K,LDA,LDB,LDC,N CHARACTERTRANS,UPLO * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) * .. * * ===================================================================== * * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Local Scalars .. DOUBLE PRECISION TEMP1,TEMP2 INTEGER I,INFO,J,L,NROWA LOGICAL UPPER * .. * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * * Test the input parameters. * IF(GAL_LSAME(TRANS,'N'))THEN NROWA=N ELSE NROWA=K END IF UPPER=GAL_LSAME(UPLO,'U') * INFO=0 IF((.NOT.UPPER).AND.(.NOT.GAL_LSAME(UPLO,'L')))THEN INFO=1 ELSE IF((.NOT.GAL_LSAME(TRANS,'N')).AND. +(.NOT.GAL_LSAME(TRANS,'T')).AND. +(.NOT.GAL_LSAME(TRANS,'C')))THEN INFO=2 ELSE IF(N.LT.0)THEN INFO=3 ELSE IF(K.LT.0)THEN INFO=4 ELSE IF(LDA.LT.MAX(1,NROWA))THEN INFO=7 ELSE IF(LDB.LT.MAX(1,NROWA))THEN INFO=9 ELSE IF(LDC.LT.MAX(1,N))THEN INFO=12 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DSYR2K',INFO) RETURN END IF * * Quick return if possible. * IF((N.EQ.0).OR.(((ALPHA.EQ.ZERO).OR. +(K.EQ.0)).AND.(BETA.EQ.ONE)))RETURN * * And when alpha.eq.zero. * IF(ALPHA.EQ.ZERO)THEN IF(UPPER)THEN IF(BETA.EQ.ZERO)THEN DO 20J=1,N DO 10I=1,J C(I,J)=ZERO 10 CONTINUE 20 CONTINUE ELSE DO 40J=1,N DO 30I=1,J C(I,J)=BETA*C(I,J) 30 CONTINUE 40 CONTINUE END IF ELSE IF(BETA.EQ.ZERO)THEN DO 60J=1,N DO 50I=J,N C(I,J)=ZERO 50 CONTINUE 60 CONTINUE ELSE DO 80J=1,N DO 70I=J,N C(I,J)=BETA*C(I,J) 70 CONTINUE 80 CONTINUE END IF END IF RETURN END IF * * Start the operations. * IF(GAL_LSAME(TRANS,'N'))THEN * * Form C := alpha*A*B**T + alpha*B*A**T + C. * IF(UPPER)THEN DO 130J=1,N IF(BETA.EQ.ZERO)THEN DO 90I=1,J C(I,J)=ZERO 90 CONTINUE ELSE IF(BETA.NE.ONE)THEN DO 100I=1,J C(I,J)=BETA*C(I,J) 100 CONTINUE END IF DO 120L=1,K IF((A(J,L).NE.ZERO).OR.(B(J,L).NE.ZERO))THEN TEMP1=ALPHA*B(J,L) TEMP2=ALPHA*A(J,L) DO 110I=1,J C(I,J)=C(I,J)+A(I,L)*TEMP1+ +B(I,L)*TEMP2 110 CONTINUE END IF 120 CONTINUE 130 CONTINUE ELSE DO 180J=1,N IF(BETA.EQ.ZERO)THEN DO 140I=J,N C(I,J)=ZERO 140 CONTINUE ELSE IF(BETA.NE.ONE)THEN DO 150I=J,N C(I,J)=BETA*C(I,J) 150 CONTINUE END IF DO 170L=1,K IF((A(J,L).NE.ZERO).OR.(B(J,L).NE.ZERO))THEN TEMP1=ALPHA*B(J,L) TEMP2=ALPHA*A(J,L) DO 160I=J,N C(I,J)=C(I,J)+A(I,L)*TEMP1+ +B(I,L)*TEMP2 160 CONTINUE END IF 170 CONTINUE 180 CONTINUE END IF ELSE * * Form C := alpha*A**T*B + alpha*B**T*A + C. * IF(UPPER)THEN DO 210J=1,N DO 200I=1,J TEMP1=ZERO TEMP2=ZERO DO 190L=1,K TEMP1=TEMP1+A(L,I)*B(L,J) TEMP2=TEMP2+B(L,I)*A(L,J) 190 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP1+ALPHA*TEMP2 ELSE C(I,J)=BETA*C(I,J)+ALPHA*TEMP1+ +ALPHA*TEMP2 END IF 200 CONTINUE 210 CONTINUE ELSE DO 240J=1,N DO 230I=J,N TEMP1=ZERO TEMP2=ZERO DO 220L=1,K TEMP1=TEMP1+A(L,I)*B(L,J) TEMP2=TEMP2+B(L,I)*A(L,J) 220 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP1+ALPHA*TEMP2 ELSE C(I,J)=BETA*C(I,J)+ALPHA*TEMP1+ +ALPHA*TEMP2 END IF 230 CONTINUE 240 CONTINUE END IF END IF * RETURN * * End of DSYR2K. * END ga-5.9.2/LinAlg/lapack+blas/gal_dsyrk.f000066400000000000000000000220231500715745200175670ustar00rootroot00000000000000*> \brief \b DSYRK * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DSYRK(UPLO,TRANS,N,K,ALPHA,A,LDA,BETA,C,LDC) * * .. Scalar Arguments .. * DOUBLE PRECISION ALPHA,BETA * INTEGER K,LDA,LDC,N * CHARACTER TRANS,UPLO * .. * .. Array Arguments .. * DOUBLE PRECISION A(LDA,*),C(LDC,*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DSYRK performs one of the symmetric rank k operations *> *> C := alpha*A*A**T + beta*C, *> *> or *> *> C := alpha*A**T*A + beta*C, *> *> where alpha and beta are scalars, C is an n by n symmetric matrix *> and A is an n by k matrix in the first case and a k by n matrix *> in the second case. *> \endverbatim * * Arguments: * ========== * *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> On entry, UPLO specifies whether the upper or lower *> triangular part of the array C is to be referenced as *> follows: *> *> UPLO = 'U' or 'u' Only the upper triangular part of C *> is to be referenced. *> *> UPLO = 'L' or 'l' Only the lower triangular part of C *> is to be referenced. *> \endverbatim *> *> \param[in] TRANS *> \verbatim *> TRANS is CHARACTER*1 *> On entry, TRANS specifies the operation to be performed as *> follows: *> *> TRANS = 'N' or 'n' C := alpha*A*A**T + beta*C. *> *> TRANS = 'T' or 't' C := alpha*A**T*A + beta*C. *> *> TRANS = 'C' or 'c' C := alpha*A**T*A + beta*C. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the order of the matrix C. N must be *> at least zero. *> \endverbatim *> *> \param[in] K *> \verbatim *> K is INTEGER *> On entry with TRANS = 'N' or 'n', K specifies the number *> of columns of the matrix A, and on entry with *> TRANS = 'T' or 't' or 'C' or 'c', K specifies the number *> of rows of the matrix A. K must be at least zero. *> \endverbatim *> *> \param[in] ALPHA *> \verbatim *> ALPHA is DOUBLE PRECISION. *> On entry, ALPHA specifies the scalar alpha. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array of DIMENSION ( LDA, ka ), where ka is *> k when TRANS = 'N' or 'n', and is n otherwise. *> Before entry with TRANS = 'N' or 'n', the leading n by k *> part of the array A must contain the matrix A, otherwise *> the leading k by n part of the array A must contain the *> matrix A. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. When TRANS = 'N' or 'n' *> then LDA must be at least max( 1, n ), otherwise LDA must *> be at least max( 1, k ). *> \endverbatim *> *> \param[in] BETA *> \verbatim *> BETA is DOUBLE PRECISION. *> On entry, BETA specifies the scalar beta. *> \endverbatim *> *> \param[in,out] C *> \verbatim *> C is DOUBLE PRECISION array of DIMENSION ( LDC, n ). *> Before entry with UPLO = 'U' or 'u', the leading n by n *> upper triangular part of the array C must contain the upper *> triangular part of the symmetric matrix and the strictly *> lower triangular part of C is not referenced. On exit, the *> upper triangular part of the array C is overwritten by the *> upper triangular part of the updated matrix. *> Before entry with UPLO = 'L' or 'l', the leading n by n *> lower triangular part of the array C must contain the lower *> triangular part of the symmetric matrix and the strictly *> upper triangular part of C is not referenced. On exit, the *> lower triangular part of the array C is overwritten by the *> lower triangular part of the updated matrix. *> \endverbatim *> *> \param[in] LDC *> \verbatim *> LDC is INTEGER *> On entry, LDC specifies the first dimension of C as declared *> in the calling (sub) program. LDC must be at least *> max( 1, n ). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level3 * *> \par Further Details: * ===================== *> *> \verbatim *> *> Level 3 Blas routine. *> *> -- Written on 8-February-1989. *> Jack Dongarra, Argonne National Laboratory. *> Iain Duff, AERE Harwell. *> Jeremy Du Croz, Numerical Algorithms Group Ltd. *> Sven Hammarling, Numerical Algorithms Group Ltd. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DSYRK(UPLO,TRANS,N,K,ALPHA,A,LDA,BETA,C,LDC) * * -- Reference BLAS level3 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. DOUBLE PRECISION ALPHA,BETA INTEGER K,LDA,LDC,N CHARACTERTRANS,UPLO * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),C(LDC,*) * .. * * ===================================================================== * * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Local Scalars .. DOUBLE PRECISION TEMP INTEGER I,INFO,J,L,NROWA LOGICAL UPPER * .. * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * * Test the input parameters. * IF(GAL_LSAME(TRANS,'N'))THEN NROWA=N ELSE NROWA=K END IF UPPER=GAL_LSAME(UPLO,'U') * INFO=0 IF((.NOT.UPPER).AND.(.NOT.GAL_LSAME(UPLO,'L')))THEN INFO=1 ELSE IF((.NOT.GAL_LSAME(TRANS,'N')).AND. +(.NOT.GAL_LSAME(TRANS,'T')).AND. +(.NOT.GAL_LSAME(TRANS,'C')))THEN INFO=2 ELSE IF(N.LT.0)THEN INFO=3 ELSE IF(K.LT.0)THEN INFO=4 ELSE IF(LDA.LT.MAX(1,NROWA))THEN INFO=7 ELSE IF(LDC.LT.MAX(1,N))THEN INFO=10 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DSYRK',INFO) RETURN END IF * * Quick return if possible. * IF((N.EQ.0).OR.(((ALPHA.EQ.ZERO).OR. +(K.EQ.0)).AND.(BETA.EQ.ONE)))RETURN * * And when alpha.eq.zero. * IF(ALPHA.EQ.ZERO)THEN IF(UPPER)THEN IF(BETA.EQ.ZERO)THEN DO 20J=1,N DO 10I=1,J C(I,J)=ZERO 10 CONTINUE 20 CONTINUE ELSE DO 40J=1,N DO 30I=1,J C(I,J)=BETA*C(I,J) 30 CONTINUE 40 CONTINUE END IF ELSE IF(BETA.EQ.ZERO)THEN DO 60J=1,N DO 50I=J,N C(I,J)=ZERO 50 CONTINUE 60 CONTINUE ELSE DO 80J=1,N DO 70I=J,N C(I,J)=BETA*C(I,J) 70 CONTINUE 80 CONTINUE END IF END IF RETURN END IF * * Start the operations. * IF(GAL_LSAME(TRANS,'N'))THEN * * Form C := alpha*A*A**T + beta*C. * IF(UPPER)THEN DO 130J=1,N IF(BETA.EQ.ZERO)THEN DO 90I=1,J C(I,J)=ZERO 90 CONTINUE ELSE IF(BETA.NE.ONE)THEN DO 100I=1,J C(I,J)=BETA*C(I,J) 100 CONTINUE END IF DO 120L=1,K IF(A(J,L).NE.ZERO)THEN TEMP=ALPHA*A(J,L) DO 110I=1,J C(I,J)=C(I,J)+TEMP*A(I,L) 110 CONTINUE END IF 120 CONTINUE 130 CONTINUE ELSE DO 180J=1,N IF(BETA.EQ.ZERO)THEN DO 140I=J,N C(I,J)=ZERO 140 CONTINUE ELSE IF(BETA.NE.ONE)THEN DO 150I=J,N C(I,J)=BETA*C(I,J) 150 CONTINUE END IF DO 170L=1,K IF(A(J,L).NE.ZERO)THEN TEMP=ALPHA*A(J,L) DO 160I=J,N C(I,J)=C(I,J)+TEMP*A(I,L) 160 CONTINUE END IF 170 CONTINUE 180 CONTINUE END IF ELSE * * Form C := alpha*A**T*A + beta*C. * IF(UPPER)THEN DO 210J=1,N DO 200I=1,J TEMP=ZERO DO 190L=1,K TEMP=TEMP+A(L,I)*A(L,J) 190 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 200 CONTINUE 210 CONTINUE ELSE DO 240J=1,N DO 230I=J,N TEMP=ZERO DO 220L=1,K TEMP=TEMP+A(L,I)*A(L,J) 220 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 230 CONTINUE 240 CONTINUE END IF END IF * RETURN * * End of DSYRK . * END ga-5.9.2/LinAlg/lapack+blas/gal_dsytd2.f000066400000000000000000000217631500715745200176560ustar00rootroot00000000000000*> \brief \b DSYTD2 reduces a symmetric matrix to real symmetric tridiagonal form by an orthogonal similarity transformation (unblocked algorithm). * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DSYTD2 + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DSYTD2( UPLO, N, A, LDA, D, E, TAU, INFO ) * * .. Scalar Arguments .. * CHARACTER UPLO * INTEGER INFO, LDA, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), D( * ), E( * ), TAU( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DSYTD2 reduces a real symmetric matrix A to symmetric tridiagonal *> form T by an orthogonal similarity transformation: Q**T * A * Q = T. *> \endverbatim * * Arguments: * ========== * *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> Specifies whether the upper or lower triangular part of the *> symmetric matrix A is stored: *> = 'U': Upper triangular *> = 'L': Lower triangular *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrix A. N >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is 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 UPLO = 'U', the diagonal and first superdiagonal *> of A are overwritten by the corresponding elements of the *> tridiagonal matrix T, and the elements above the first *> superdiagonal, with the array TAU, represent the orthogonal *> matrix Q as a product of elementary reflectors; if UPLO *> = 'L', the diagonal and first subdiagonal of A are over- *> written by the corresponding elements of the tridiagonal *> matrix T, and the elements below the first subdiagonal, with *> the array TAU, represent the orthogonal matrix Q as a product *> of elementary reflectors. See Further Details. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,N). *> \endverbatim *> *> \param[out] D *> \verbatim *> D is DOUBLE PRECISION array, dimension (N) *> The diagonal elements of the tridiagonal matrix T: *> D(i) = A(i,i). *> \endverbatim *> *> \param[out] E *> \verbatim *> E is DOUBLE PRECISION array, dimension (N-1) *> The off-diagonal elements of the tridiagonal matrix T: *> E(i) = A(i,i+1) if UPLO = 'U', E(i) = A(i+1,i) if UPLO = 'L'. *> \endverbatim *> *> \param[out] TAU *> \verbatim *> TAU is DOUBLE PRECISION array, dimension (N-1) *> The scalar factors of the elementary reflectors (see Further *> Details). *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument had an illegal value. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup doubleSYcomputational * *> \par Further Details: * ===================== *> *> \verbatim *> *> If UPLO = 'U', the matrix Q is represented as a product of elementary *> reflectors *> *> Q = H(n-1) . . . H(2) H(1). *> *> Each H(i) has the form *> *> H(i) = I - tau * v * v**T *> *> where tau is a real scalar, and v is a real vector with *> v(i+1:n) = 0 and v(i) = 1; v(1:i-1) is stored on exit in *> A(1:i-1,i+1), and tau in TAU(i). *> *> If UPLO = 'L', the matrix Q is represented as a product of elementary *> reflectors *> *> Q = H(1) H(2) . . . H(n-1). *> *> Each H(i) has the form *> *> H(i) = I - tau * v * v**T *> *> where tau is a real scalar, and v is a real vector with *> v(1:i) = 0 and v(i+1) = 1; v(i+2:n) is stored on exit in A(i+2:n,i), *> and tau in TAU(i). *> *> The contents of A on exit are illustrated by the following examples *> with n = 5: *> *> if UPLO = 'U': if UPLO = 'L': *> *> ( d e v2 v3 v4 ) ( d ) *> ( d e v3 v4 ) ( e d ) *> ( d e v4 ) ( v1 e d ) *> ( d e ) ( v1 v2 e d ) *> ( d ) ( v1 v2 v3 e d ) *> *> where d and e denote diagonal and off-diagonal elements of T, and vi *> denotes an element of the vector defining H(i). *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DSYTD2(UPLO,N,A,LDA,D,E,TAU,INFO) * * -- LAPACK computational routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. CHARACTERUPLO INTEGER INFO,LDA,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),D(*),E(*),TAU(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE,ZERO,HALF PARAMETER (ONE=1.0D0,ZERO=0.0D0, $HALF=1.0D0/2.0D0) * .. * .. Local Scalars .. LOGICAL UPPER INTEGER I DOUBLE PRECISION ALPHA,TAUI * .. * .. External Subroutines .. EXTERNAL GAL_DAXPY,GAL_DLARFG,GAL_DSYMV,GAL_DSYR2,GAL_XERBLA * .. * .. External Functions .. LOGICAL GAL_LSAME DOUBLE PRECISION GAL_DDOT EXTERNAL GAL_LSAME,GAL_DDOT * .. * .. Intrinsic Functions .. INTRINSIC MAX,MIN * .. * .. Executable Statements .. * * Test the input parameters * INFO=0 UPPER=GAL_LSAME(UPLO,'U') IF(.NOT.UPPER.AND..NOT.GAL_LSAME(UPLO,'L'))THEN INFO=-1 ELSE IF(N.LT.0)THEN INFO=-2 ELSE IF(LDA.LT.MAX(1,N))THEN INFO=-4 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DSYTD2',-INFO) RETURN END IF * * Quick return if possible * IF(N.LE.0) $RETURN * IF(UPPER)THEN * * Reduce the upper triangle of A * DO 10I=N-1,1,-1 * * Generate elementary reflector H(i) = I - tau * v * v**T * to annihilate A(1:i-1,i+1) * CALL GAL_DLARFG(I,A(I,I+1),A(1,I+1),1,TAUI) E(I)=A(I,I+1) * IF(TAUI.NE.ZERO)THEN * * Apply H(i) from both sides to A(1:i,1:i) * A(I,I+1)=ONE * * Compute x := tau * A * v storing x in TAU(1:i) * CALL GAL_DSYMV(UPLO,I,TAUI,A,LDA,A(1,I+1),1,ZERO, $TAU,1) * * Compute w := x - 1/2 * tau * (x**T * v) * v * ALPHA=-HALF*TAUI*GAL_DDOT(I,TAU,1,A(1,I+1),1) CALL GAL_DAXPY(I,ALPHA,A(1,I+1),1,TAU,1) * * Apply the transformation as a rank-2 update: * A := A - v * w**T - w * v**T * CALL GAL_DSYR2(UPLO,I,-ONE,A(1,I+1),1,TAU,1,A, $LDA) * A(I,I+1)=E(I) END IF D(I+1)=A(I+1,I+1) TAU(I)=TAUI 10 CONTINUE D(1)=A(1,1) ELSE * * Reduce the lower triangle of A * DO 20I=1,N-1 * * Generate elementary reflector H(i) = I - tau * v * v**T * to annihilate A(i+2:n,i) * CALL GAL_DLARFG(N-I,A(I+1,I),A(MIN(I+2,N),I),1, $TAUI) E(I)=A(I+1,I) * IF(TAUI.NE.ZERO)THEN * * Apply H(i) from both sides to A(i+1:n,i+1:n) * A(I+1,I)=ONE * * Compute x := tau * A * v storing y in TAU(i:n-1) * CALL GAL_DSYMV(UPLO,N-I,TAUI,A(I+1,I+1),LDA, $A(I+1,I),1,ZERO,TAU(I),1) * * Compute w := x - 1/2 * tau * (x**T * v) * v * ALPHA=-HALF*TAUI*GAL_DDOT(N-I,TAU(I),1,A(I+1,I), $1) CALL GAL_DAXPY(N-I,ALPHA,A(I+1,I),1,TAU(I),1) * * Apply the transformation as a rank-2 update: * A := A - v * w**T - w * v**T * CALL GAL_DSYR2(UPLO,N-I,-ONE,A(I+1,I),1,TAU(I),1, $A(I+1,I+1),LDA) * A(I+1,I)=E(I) END IF D(I)=A(I,I) TAU(I)=TAUI 20 CONTINUE D(N)=A(N,N) END IF * RETURN * * End of DSYTD2 * END ga-5.9.2/LinAlg/lapack+blas/gal_dsytrd.f000066400000000000000000000250451500715745200177530ustar00rootroot00000000000000*> \brief \b DSYTRD * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download DSYTRD + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE DSYTRD( UPLO, N, A, LDA, D, E, TAU, WORK, LWORK, INFO ) * * .. Scalar Arguments .. * CHARACTER UPLO * INTEGER INFO, LDA, LWORK, N * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ), D( * ), E( * ), TAU( * ), * $ WORK( * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DSYTRD reduces a real symmetric matrix A to real symmetric *> tridiagonal form T by an orthogonal similarity transformation: *> Q**T * A * Q = T. *> \endverbatim * * Arguments: * ========== * *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> = 'U': Upper triangle of A is stored; *> = 'L': Lower triangle of A is stored. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The order of the matrix A. N >= 0. *> \endverbatim *> *> \param[in,out] A *> \verbatim *> A is 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 UPLO = 'U', the diagonal and first superdiagonal *> of A are overwritten by the corresponding elements of the *> tridiagonal matrix T, and the elements above the first *> superdiagonal, with the array TAU, represent the orthogonal *> matrix Q as a product of elementary reflectors; if UPLO *> = 'L', the diagonal and first subdiagonal of A are over- *> written by the corresponding elements of the tridiagonal *> matrix T, and the elements below the first subdiagonal, with *> the array TAU, represent the orthogonal matrix Q as a product *> of elementary reflectors. See Further Details. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,N). *> \endverbatim *> *> \param[out] D *> \verbatim *> D is DOUBLE PRECISION array, dimension (N) *> The diagonal elements of the tridiagonal matrix T: *> D(i) = A(i,i). *> \endverbatim *> *> \param[out] E *> \verbatim *> E is DOUBLE PRECISION array, dimension (N-1) *> The off-diagonal elements of the tridiagonal matrix T: *> E(i) = A(i,i+1) if UPLO = 'U', E(i) = A(i+1,i) if UPLO = 'L'. *> \endverbatim *> *> \param[out] TAU *> \verbatim *> TAU is DOUBLE PRECISION array, dimension (N-1) *> The scalar factors of the elementary reflectors (see Further *> Details). *> \endverbatim *> *> \param[out] WORK *> \verbatim *> WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)) *> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. *> \endverbatim *> *> \param[in] LWORK *> \verbatim *> LWORK is INTEGER *> The dimension of the array WORK. LWORK >= 1. *> For optimum performance LWORK >= N*NB, where NB is the *> optimal blocksize. *> *> If LWORK = -1, then a workspace query is assumed; the routine *> only calculates the optimal size of the WORK array, returns *> this value as the first entry of the WORK array, and no error *> message related to LWORK is issued by XERBLA. *> \endverbatim *> *> \param[out] INFO *> \verbatim *> INFO is INTEGER *> = 0: successful exit *> < 0: if INFO = -i, the i-th argument had an illegal value *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup doubleSYcomputational * *> \par Further Details: * ===================== *> *> \verbatim *> *> If UPLO = 'U', the matrix Q is represented as a product of elementary *> reflectors *> *> Q = H(n-1) . . . H(2) H(1). *> *> Each H(i) has the form *> *> H(i) = I - tau * v * v**T *> *> where tau is a real scalar, and v is a real vector with *> v(i+1:n) = 0 and v(i) = 1; v(1:i-1) is stored on exit in *> A(1:i-1,i+1), and tau in TAU(i). *> *> If UPLO = 'L', the matrix Q is represented as a product of elementary *> reflectors *> *> Q = H(1) H(2) . . . H(n-1). *> *> Each H(i) has the form *> *> H(i) = I - tau * v * v**T *> *> where tau is a real scalar, and v is a real vector with *> v(1:i) = 0 and v(i+1) = 1; v(i+2:n) is stored on exit in A(i+2:n,i), *> and tau in TAU(i). *> *> The contents of A on exit are illustrated by the following examples *> with n = 5: *> *> if UPLO = 'U': if UPLO = 'L': *> *> ( d e v2 v3 v4 ) ( d ) *> ( d e v3 v4 ) ( e d ) *> ( d e v4 ) ( v1 e d ) *> ( d e ) ( v1 v2 e d ) *> ( d ) ( v1 v2 v3 e d ) *> *> where d and e denote diagonal and off-diagonal elements of T, and vi *> denotes an element of the vector defining H(i). *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DSYTRD(UPLO,N,A,LDA,D,E,TAU,WORK,LWORK,INFO) * * -- LAPACK computational routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. CHARACTERUPLO INTEGER INFO,LDA,LWORK,N * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),D(*),E(*),TAU(*), $WORK(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ONE PARAMETER (ONE=1.0D+0) * .. * .. Local Scalars .. LOGICAL LQUERY,UPPER INTEGER I,IINFO,IWS,J,KK,LDWORK,LWKOPT,NB, $NBMIN,NX * .. * .. External Subroutines .. EXTERNAL GAL_DLATRD,GAL_DSYR2K,GAL_DSYTD2,GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. External Functions .. LOGICAL GAL_LSAME INTEGER GAL_ILAENV EXTERNAL GAL_LSAME,GAL_ILAENV * .. * .. Executable Statements .. * * Test the input parameters * INFO=0 UPPER=GAL_LSAME(UPLO,'U') LQUERY=(LWORK.EQ.-1) IF(.NOT.UPPER.AND..NOT.GAL_LSAME(UPLO,'L'))THEN INFO=-1 ELSE IF(N.LT.0)THEN INFO=-2 ELSE IF(LDA.LT.MAX(1,N))THEN INFO=-4 ELSE IF(LWORK.LT.1.AND..NOT.LQUERY)THEN INFO=-9 END IF * IF(INFO.EQ.0)THEN * * Determine the block size. * NB=GAL_ILAENV(1,'GAL_DSYTRD',UPLO,N,-1,-1,-1) LWKOPT=N*NB WORK(1)=LWKOPT END IF * IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DSYTRD',-INFO) RETURN ELSE IF(LQUERY)THEN RETURN END IF * * Quick return if possible * IF(N.EQ.0)THEN WORK(1)=1 RETURN END IF * NX=N IWS=1 IF(NB.GT.1.AND.NB.LT.N)THEN * * Determine when to cross over from blocked to unblocked code * (last block is always handled by unblocked code). * NX=MAX(NB,GAL_ILAENV(3,'GAL_DSYTRD',UPLO,N,-1,-1,-1)) IF(NX.LT.N)THEN * * Determine if workspace is large enough for blocked code. * LDWORK=N IWS=LDWORK*NB IF(LWORK.LT.IWS)THEN * * Not enough workspace to use optimal NB: determine the * minimum value of NB, and reduce NB or force use of * unblocked code by setting NX = N. * NB=MAX(LWORK/LDWORK,1) NBMIN=GAL_ILAENV(2,'GAL_DSYTRD',UPLO,N,-1,-1,-1) IF(NB.LT.NBMIN) $NX=N END IF ELSE NX=N END IF ELSE NB=1 END IF * IF(UPPER)THEN * * Reduce the upper triangle of A. * Columns 1:kk are handled by the unblocked method. * KK=N-((N-NX+NB-1)/NB)*NB DO 20I=N-NB+1,KK+1,-NB * * Reduce columns i:i+nb-1 to tridiagonal form and form the * matrix W which is needed to update the unreduced part of * the matrix * CALL GAL_DLATRD(UPLO,I+NB-1,NB,A,LDA,E,TAU,WORK, $LDWORK) * * Update the unreduced submatrix A(1:i-1,1:i-1), using an * update of the form: A := A - V*W**T - W*V**T * CALL GAL_DSYR2K(UPLO,'NOTRANSPOSE',I-1,NB,-ONE,A(1,I), $LDA,WORK,LDWORK,ONE,A,LDA) * * Copy superdiagonal elements back into A, and diagonal * elements into D * DO 10J=I,I+NB-1 A(J-1,J)=E(J-1) D(J)=A(J,J) 10 CONTINUE 20 CONTINUE * * Use unblocked code to reduce the last or only block * CALL GAL_DSYTD2(UPLO,KK,A,LDA,D,E,TAU,IINFO) ELSE * * Reduce the lower triangle of A * DO 40I=1,N-NX,NB * * Reduce columns i:i+nb-1 to tridiagonal form and form the * matrix W which is needed to update the unreduced part of * the matrix * CALL GAL_DLATRD(UPLO,N-I+1,NB,A(I,I),LDA,E(I), $TAU(I),WORK,LDWORK) * * Update the unreduced submatrix A(i+ib:n,i+ib:n), using * an update of the form: A := A - V*W**T - W*V**T * CALL GAL_DSYR2K(UPLO,'NOTRANSPOSE',N-I-NB+1,NB,-ONE, $A(I+NB,I),LDA,WORK(NB+1),LDWORK,ONE, $A(I+NB,I+NB),LDA) * * Copy subdiagonal elements back into A, and diagonal * elements into D * DO 30J=I,I+NB-1 A(J+1,J)=E(J) D(J)=A(J,J) 30 CONTINUE 40 CONTINUE * * Use unblocked code to reduce the last or only block * CALL GAL_DSYTD2(UPLO,N-I+1,A(I,I),LDA,D(I),E(I), $TAU(I),IINFO) END IF * WORK(1)=LWKOPT RETURN * * End of DSYTRD * END ga-5.9.2/LinAlg/lapack+blas/gal_dtrmm.f000066400000000000000000000241211500715745200175570ustar00rootroot00000000000000*> \brief \b DTRMM * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DTRMM(SIDE,UPLO,TRANSA,DIAG,M,N,ALPHA,A,LDA,B,LDB) * * .. Scalar Arguments .. * DOUBLE PRECISION ALPHA * INTEGER LDA,LDB,M,N * CHARACTER DIAG,SIDE,TRANSA,UPLO * .. * .. Array Arguments .. * DOUBLE PRECISION A(LDA,*),B(LDB,*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DTRMM performs one of the matrix-matrix operations *> *> B := alpha*op( A )*B, or B := alpha*B*op( A ), *> *> where alpha is a scalar, B is an m by n matrix, A is a unit, or *> non-unit, upper or lower triangular matrix and op( A ) is one of *> *> op( A ) = A or op( A ) = A**T. *> \endverbatim * * Arguments: * ========== * *> \param[in] SIDE *> \verbatim *> SIDE is CHARACTER*1 *> On entry, SIDE specifies whether op( A ) multiplies B from *> the left or right as follows: *> *> SIDE = 'L' or 'l' B := alpha*op( A )*B. *> *> SIDE = 'R' or 'r' B := alpha*B*op( A ). *> \endverbatim *> *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> On entry, UPLO specifies whether the matrix A is an upper or *> lower triangular matrix as follows: *> *> UPLO = 'U' or 'u' A is an upper triangular matrix. *> *> UPLO = 'L' or 'l' A is a lower triangular matrix. *> \endverbatim *> *> \param[in] TRANSA *> \verbatim *> TRANSA is CHARACTER*1 *> On entry, TRANSA specifies the form of op( A ) to be used in *> the matrix multiplication as follows: *> *> TRANSA = 'N' or 'n' op( A ) = A. *> *> TRANSA = 'T' or 't' op( A ) = A**T. *> *> TRANSA = 'C' or 'c' op( A ) = A**T. *> \endverbatim *> *> \param[in] DIAG *> \verbatim *> DIAG is CHARACTER*1 *> On entry, DIAG specifies whether or not A is unit triangular *> as follows: *> *> DIAG = 'U' or 'u' A is assumed to be unit triangular. *> *> DIAG = 'N' or 'n' A is not assumed to be unit *> triangular. *> \endverbatim *> *> \param[in] M *> \verbatim *> M is INTEGER *> On entry, M specifies the number of rows of B. M must be at *> least zero. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the number of columns of B. N must be *> at least zero. *> \endverbatim *> *> \param[in] ALPHA *> \verbatim *> ALPHA is DOUBLE PRECISION. *> On entry, ALPHA specifies the scalar alpha. When alpha is *> zero then A is not referenced and B need not be set before *> entry. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array of DIMENSION ( LDA, k ), where k is m *> when SIDE = 'L' or 'l' and is n when SIDE = 'R' or 'r'. *> Before entry with UPLO = 'U' or 'u', the leading k by k *> upper triangular part of the array A must contain the upper *> triangular matrix and the strictly lower triangular part of *> A is not referenced. *> Before entry with UPLO = 'L' or 'l', the leading k by k *> lower triangular part of the array A must contain the lower *> triangular matrix and the strictly upper triangular part of *> A is not referenced. *> Note that when DIAG = 'U' or 'u', the diagonal elements of *> A are not referenced either, but are assumed to be unity. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. When SIDE = 'L' or 'l' then *> LDA must be at least max( 1, m ), when SIDE = 'R' or 'r' *> then LDA must be at least max( 1, n ). *> \endverbatim *> *> \param[in,out] B *> \verbatim *> B is DOUBLE PRECISION array of DIMENSION ( LDB, n ). *> Before entry, the leading m by n part of the array B must *> contain the matrix B, and on exit is overwritten by the *> transformed matrix. *> \endverbatim *> *> \param[in] LDB *> \verbatim *> LDB is INTEGER *> On entry, LDB specifies the first dimension of B as declared *> in the calling (sub) program. LDB must be at least *> max( 1, m ). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level3 * *> \par Further Details: * ===================== *> *> \verbatim *> *> Level 3 Blas routine. *> *> -- Written on 8-February-1989. *> Jack Dongarra, Argonne National Laboratory. *> Iain Duff, AERE Harwell. *> Jeremy Du Croz, Numerical Algorithms Group Ltd. *> Sven Hammarling, Numerical Algorithms Group Ltd. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DTRMM(SIDE,UPLO,TRANSA,DIAG,M,N,ALPHA,A,LDA,B,LDB) * * -- Reference BLAS level3 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. DOUBLE PRECISION ALPHA INTEGER LDA,LDB,M,N CHARACTERDIAG,SIDE,TRANSA,UPLO * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),B(LDB,*) * .. * * ===================================================================== * * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Local Scalars .. DOUBLE PRECISION TEMP INTEGER I,INFO,J,K,NROWA LOGICAL LSIDE,NOUNIT,UPPER * .. * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * * Test the input parameters. * LSIDE=GAL_LSAME(SIDE,'L') IF(LSIDE)THEN NROWA=M ELSE NROWA=N END IF NOUNIT=GAL_LSAME(DIAG,'N') UPPER=GAL_LSAME(UPLO,'U') * INFO=0 IF((.NOT.LSIDE).AND.(.NOT.GAL_LSAME(SIDE,'R')))THEN INFO=1 ELSE IF((.NOT.UPPER).AND.(.NOT.GAL_LSAME(UPLO,'L')))THEN INFO=2 ELSE IF((.NOT.GAL_LSAME(TRANSA,'N')).AND. +(.NOT.GAL_LSAME(TRANSA,'T')).AND. +(.NOT.GAL_LSAME(TRANSA,'C')))THEN INFO=3 ELSE IF((.NOT.GAL_LSAME(DIAG,'U')).AND. +(.NOT.GAL_LSAME(DIAG,'N')))THEN INFO=4 ELSE IF(M.LT.0)THEN INFO=5 ELSE IF(N.LT.0)THEN INFO=6 ELSE IF(LDA.LT.MAX(1,NROWA))THEN INFO=9 ELSE IF(LDB.LT.MAX(1,M))THEN INFO=11 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DTRMM',INFO) RETURN END IF * * Quick return if possible. * IF(M.EQ.0.OR.N.EQ.0)RETURN * * And when alpha.eq.zero. * IF(ALPHA.EQ.ZERO)THEN DO 20J=1,N DO 10I=1,M B(I,J)=ZERO 10 CONTINUE 20 CONTINUE RETURN END IF * * Start the operations. * IF(LSIDE)THEN IF(GAL_LSAME(TRANSA,'N'))THEN * * Form B := alpha*A*B. * IF(UPPER)THEN DO 50J=1,N DO 40K=1,M IF(B(K,J).NE.ZERO)THEN TEMP=ALPHA*B(K,J) DO 30I=1,K-1 B(I,J)=B(I,J)+TEMP*A(I,K) 30 CONTINUE IF(NOUNIT)TEMP=TEMP*A(K,K) B(K,J)=TEMP END IF 40 CONTINUE 50 CONTINUE ELSE DO 80J=1,N DO 70K=M,1,-1 IF(B(K,J).NE.ZERO)THEN TEMP=ALPHA*B(K,J) B(K,J)=TEMP IF(NOUNIT)B(K,J)=B(K,J)*A(K,K) DO 60I=K+1,M B(I,J)=B(I,J)+TEMP*A(I,K) 60 CONTINUE END IF 70 CONTINUE 80 CONTINUE END IF ELSE * * Form B := alpha*A**T*B. * IF(UPPER)THEN DO 110J=1,N DO 100I=M,1,-1 TEMP=B(I,J) IF(NOUNIT)TEMP=TEMP*A(I,I) DO 90K=1,I-1 TEMP=TEMP+A(K,I)*B(K,J) 90 CONTINUE B(I,J)=ALPHA*TEMP 100 CONTINUE 110 CONTINUE ELSE DO 140J=1,N DO 130I=1,M TEMP=B(I,J) IF(NOUNIT)TEMP=TEMP*A(I,I) DO 120K=I+1,M TEMP=TEMP+A(K,I)*B(K,J) 120 CONTINUE B(I,J)=ALPHA*TEMP 130 CONTINUE 140 CONTINUE END IF END IF ELSE IF(GAL_LSAME(TRANSA,'N'))THEN * * Form B := alpha*B*A. * IF(UPPER)THEN DO 180J=N,1,-1 TEMP=ALPHA IF(NOUNIT)TEMP=TEMP*A(J,J) DO 150I=1,M B(I,J)=TEMP*B(I,J) 150 CONTINUE DO 170K=1,J-1 IF(A(K,J).NE.ZERO)THEN TEMP=ALPHA*A(K,J) DO 160I=1,M B(I,J)=B(I,J)+TEMP*B(I,K) 160 CONTINUE END IF 170 CONTINUE 180 CONTINUE ELSE DO 220J=1,N TEMP=ALPHA IF(NOUNIT)TEMP=TEMP*A(J,J) DO 190I=1,M B(I,J)=TEMP*B(I,J) 190 CONTINUE DO 210K=J+1,N IF(A(K,J).NE.ZERO)THEN TEMP=ALPHA*A(K,J) DO 200I=1,M B(I,J)=B(I,J)+TEMP*B(I,K) 200 CONTINUE END IF 210 CONTINUE 220 CONTINUE END IF ELSE * * Form B := alpha*B*A**T. * IF(UPPER)THEN DO 260K=1,N DO 240J=1,K-1 IF(A(J,K).NE.ZERO)THEN TEMP=ALPHA*A(J,K) DO 230I=1,M B(I,J)=B(I,J)+TEMP*B(I,K) 230 CONTINUE END IF 240 CONTINUE TEMP=ALPHA IF(NOUNIT)TEMP=TEMP*A(K,K) IF(TEMP.NE.ONE)THEN DO 250I=1,M B(I,K)=TEMP*B(I,K) 250 CONTINUE END IF 260 CONTINUE ELSE DO 300K=N,1,-1 DO 280J=K+1,N IF(A(J,K).NE.ZERO)THEN TEMP=ALPHA*A(J,K) DO 270I=1,M B(I,J)=B(I,J)+TEMP*B(I,K) 270 CONTINUE END IF 280 CONTINUE TEMP=ALPHA IF(NOUNIT)TEMP=TEMP*A(K,K) IF(TEMP.NE.ONE)THEN DO 290I=1,M B(I,K)=TEMP*B(I,K) 290 CONTINUE END IF 300 CONTINUE END IF END IF END IF * RETURN * * End of DTRMM . * END ga-5.9.2/LinAlg/lapack+blas/gal_dtrmv.f000066400000000000000000000202531500715745200175720ustar00rootroot00000000000000*> \brief \b DTRMV * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DTRMV(UPLO,TRANS,DIAG,N,A,LDA,X,INCX) * * .. Scalar Arguments .. * INTEGER INCX,LDA,N * CHARACTER DIAG,TRANS,UPLO * .. * .. Array Arguments .. * DOUBLE PRECISION A(LDA,*),X(*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DTRMV performs one of the matrix-vector operations *> *> x := A*x, or x := A**T*x, *> *> where x is an n element vector and A is an n by n unit, or non-unit, *> upper or lower triangular matrix. *> \endverbatim * * Arguments: * ========== * *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> On entry, UPLO specifies whether the matrix is an upper or *> lower triangular matrix as follows: *> *> UPLO = 'U' or 'u' A is an upper triangular matrix. *> *> UPLO = 'L' or 'l' A is a lower triangular matrix. *> \endverbatim *> *> \param[in] TRANS *> \verbatim *> TRANS is CHARACTER*1 *> On entry, TRANS specifies the operation to be performed as *> follows: *> *> TRANS = 'N' or 'n' x := A*x. *> *> TRANS = 'T' or 't' x := A**T*x. *> *> TRANS = 'C' or 'c' x := A**T*x. *> \endverbatim *> *> \param[in] DIAG *> \verbatim *> DIAG is CHARACTER*1 *> On entry, DIAG specifies whether or not A is unit *> triangular as follows: *> *> DIAG = 'U' or 'u' A is assumed to be unit triangular. *> *> DIAG = 'N' or 'n' A is not assumed to be unit *> triangular. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the order of the matrix A. *> N must be at least zero. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array of DIMENSION ( LDA, n ). *> Before entry with UPLO = 'U' or 'u', the leading n by n *> upper triangular part of the array A must contain the upper *> triangular matrix and the strictly lower triangular part of *> A is not referenced. *> Before entry with UPLO = 'L' or 'l', the leading n by n *> lower triangular part of the array A must contain the lower *> triangular matrix and the strictly upper triangular part of *> A is not referenced. *> Note that when DIAG = 'U' or 'u', the diagonal elements of *> A are not referenced either, but are assumed to be unity. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. LDA must be at least *> max( 1, n ). *> \endverbatim *> *> \param[in,out] X *> \verbatim *> X is DOUBLE PRECISION array of dimension at least *> ( 1 + ( n - 1 )*abs( INCX ) ). *> Before entry, the incremented array X must contain the n *> element vector x. On exit, X is overwritten with the *> tranformed vector x. *> \endverbatim *> *> \param[in] INCX *> \verbatim *> INCX is INTEGER *> On entry, INCX specifies the increment for the elements of *> X. INCX must not be zero. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level2 * *> \par Further Details: * ===================== *> *> \verbatim *> *> Level 2 Blas routine. *> The vector and matrix arguments are not referenced when N = 0, or M = 0 *> *> -- Written on 22-October-1986. *> Jack Dongarra, Argonne National Lab. *> Jeremy Du Croz, Nag Central Office. *> Sven Hammarling, Nag Central Office. *> Richard Hanson, Sandia National Labs. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DTRMV(UPLO,TRANS,DIAG,N,A,LDA,X,INCX) * * -- Reference BLAS level2 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. INTEGER INCX,LDA,N CHARACTERDIAG,TRANS,UPLO * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),X(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO PARAMETER (ZERO=0.0D+0) * .. * .. Local Scalars .. DOUBLE PRECISION TEMP INTEGER I,INFO,IX,J,JX,KX LOGICAL NOUNIT * .. * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * * Test the input parameters. * INFO=0 IF(.NOT.GAL_LSAME(UPLO,'U').AND..NOT.GAL_LSAME(UPLO,'L'))THEN INFO=1 ELSE IF(.NOT.GAL_LSAME(TRANS,'N').AND. +.NOT.GAL_LSAME(TRANS,'T').AND. +.NOT.GAL_LSAME(TRANS,'C'))THEN INFO=2 ELSE IF(.NOT.GAL_LSAME(DIAG,'U').AND. +.NOT.GAL_LSAME(DIAG,'N'))THEN INFO=3 ELSE IF(N.LT.0)THEN INFO=4 ELSE IF(LDA.LT.MAX(1,N))THEN INFO=6 ELSE IF(INCX.EQ.0)THEN INFO=8 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DTRMV',INFO) RETURN END IF * * Quick return if possible. * IF(N.EQ.0)RETURN * NOUNIT=GAL_LSAME(DIAG,'N') * * Set up the start point in X if the increment is not unity. This * will be ( N - 1 )*INCX too small for descending loops. * IF(INCX.LE.0)THEN KX=1-(N-1)*INCX ELSE IF(INCX.NE.1)THEN KX=1 END IF * * Start the operations. In this version the elements of A are * accessed sequentially with one pass through A. * IF(GAL_LSAME(TRANS,'N'))THEN * * Form x := A*x. * IF(GAL_LSAME(UPLO,'U'))THEN IF(INCX.EQ.1)THEN DO 20J=1,N IF(X(J).NE.ZERO)THEN TEMP=X(J) DO 10I=1,J-1 X(I)=X(I)+TEMP*A(I,J) 10 CONTINUE IF(NOUNIT)X(J)=X(J)*A(J,J) END IF 20 CONTINUE ELSE JX=KX DO 40J=1,N IF(X(JX).NE.ZERO)THEN TEMP=X(JX) IX=KX DO 30I=1,J-1 X(IX)=X(IX)+TEMP*A(I,J) IX=IX+INCX 30 CONTINUE IF(NOUNIT)X(JX)=X(JX)*A(J,J) END IF JX=JX+INCX 40 CONTINUE END IF ELSE IF(INCX.EQ.1)THEN DO 60J=N,1,-1 IF(X(J).NE.ZERO)THEN TEMP=X(J) DO 50I=N,J+1,-1 X(I)=X(I)+TEMP*A(I,J) 50 CONTINUE IF(NOUNIT)X(J)=X(J)*A(J,J) END IF 60 CONTINUE ELSE KX=KX+(N-1)*INCX JX=KX DO 80J=N,1,-1 IF(X(JX).NE.ZERO)THEN TEMP=X(JX) IX=KX DO 70I=N,J+1,-1 X(IX)=X(IX)+TEMP*A(I,J) IX=IX-INCX 70 CONTINUE IF(NOUNIT)X(JX)=X(JX)*A(J,J) END IF JX=JX-INCX 80 CONTINUE END IF END IF ELSE * * Form x := A**T*x. * IF(GAL_LSAME(UPLO,'U'))THEN IF(INCX.EQ.1)THEN DO 100J=N,1,-1 TEMP=X(J) IF(NOUNIT)TEMP=TEMP*A(J,J) DO 90I=J-1,1,-1 TEMP=TEMP+A(I,J)*X(I) 90 CONTINUE X(J)=TEMP 100 CONTINUE ELSE JX=KX+(N-1)*INCX DO 120J=N,1,-1 TEMP=X(JX) IX=JX IF(NOUNIT)TEMP=TEMP*A(J,J) DO 110I=J-1,1,-1 IX=IX-INCX TEMP=TEMP+A(I,J)*X(IX) 110 CONTINUE X(JX)=TEMP JX=JX-INCX 120 CONTINUE END IF ELSE IF(INCX.EQ.1)THEN DO 140J=1,N TEMP=X(J) IF(NOUNIT)TEMP=TEMP*A(J,J) DO 130I=J+1,N TEMP=TEMP+A(I,J)*X(I) 130 CONTINUE X(J)=TEMP 140 CONTINUE ELSE JX=KX DO 160J=1,N TEMP=X(JX) IX=JX IF(NOUNIT)TEMP=TEMP*A(J,J) DO 150I=J+1,N IX=IX+INCX TEMP=TEMP+A(I,J)*X(IX) 150 CONTINUE X(JX)=TEMP JX=JX+INCX 160 CONTINUE END IF END IF END IF * RETURN * * End of DTRMV . * END ga-5.9.2/LinAlg/lapack+blas/gal_dtrsm.f000066400000000000000000000250721500715745200175730ustar00rootroot00000000000000*> \brief \b DTRSM * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DTRSM(SIDE,UPLO,TRANSA,DIAG,M,N,ALPHA,A,LDA,B,LDB) * * .. Scalar Arguments .. * DOUBLE PRECISION ALPHA * INTEGER LDA,LDB,M,N * CHARACTER DIAG,SIDE,TRANSA,UPLO * .. * .. Array Arguments .. * DOUBLE PRECISION A(LDA,*),B(LDB,*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DTRSM solves one of the matrix equations *> *> op( A )*X = alpha*B, or X*op( A ) = alpha*B, *> *> where alpha is a scalar, X and B are m by n matrices, A is a unit, or *> non-unit, upper or lower triangular matrix and op( A ) is one of *> *> op( A ) = A or op( A ) = A**T. *> *> The matrix X is overwritten on B. *> \endverbatim * * Arguments: * ========== * *> \param[in] SIDE *> \verbatim *> SIDE is CHARACTER*1 *> On entry, SIDE specifies whether op( A ) appears on the left *> or right of X as follows: *> *> SIDE = 'L' or 'l' op( A )*X = alpha*B. *> *> SIDE = 'R' or 'r' X*op( A ) = alpha*B. *> \endverbatim *> *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> On entry, UPLO specifies whether the matrix A is an upper or *> lower triangular matrix as follows: *> *> UPLO = 'U' or 'u' A is an upper triangular matrix. *> *> UPLO = 'L' or 'l' A is a lower triangular matrix. *> \endverbatim *> *> \param[in] TRANSA *> \verbatim *> TRANSA is CHARACTER*1 *> On entry, TRANSA specifies the form of op( A ) to be used in *> the matrix multiplication as follows: *> *> TRANSA = 'N' or 'n' op( A ) = A. *> *> TRANSA = 'T' or 't' op( A ) = A**T. *> *> TRANSA = 'C' or 'c' op( A ) = A**T. *> \endverbatim *> *> \param[in] DIAG *> \verbatim *> DIAG is CHARACTER*1 *> On entry, DIAG specifies whether or not A is unit triangular *> as follows: *> *> DIAG = 'U' or 'u' A is assumed to be unit triangular. *> *> DIAG = 'N' or 'n' A is not assumed to be unit *> triangular. *> \endverbatim *> *> \param[in] M *> \verbatim *> M is INTEGER *> On entry, M specifies the number of rows of B. M must be at *> least zero. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the number of columns of B. N must be *> at least zero. *> \endverbatim *> *> \param[in] ALPHA *> \verbatim *> ALPHA is DOUBLE PRECISION. *> On entry, ALPHA specifies the scalar alpha. When alpha is *> zero then A is not referenced and B need not be set before *> entry. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array of DIMENSION ( LDA, k ), *> where k is m when SIDE = 'L' or 'l' *> and k is n when SIDE = 'R' or 'r'. *> Before entry with UPLO = 'U' or 'u', the leading k by k *> upper triangular part of the array A must contain the upper *> triangular matrix and the strictly lower triangular part of *> A is not referenced. *> Before entry with UPLO = 'L' or 'l', the leading k by k *> lower triangular part of the array A must contain the lower *> triangular matrix and the strictly upper triangular part of *> A is not referenced. *> Note that when DIAG = 'U' or 'u', the diagonal elements of *> A are not referenced either, but are assumed to be unity. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. When SIDE = 'L' or 'l' then *> LDA must be at least max( 1, m ), when SIDE = 'R' or 'r' *> then LDA must be at least max( 1, n ). *> \endverbatim *> *> \param[in,out] B *> \verbatim *> B is DOUBLE PRECISION array of DIMENSION ( LDB, n ). *> Before entry, the leading m by n part of the array B must *> contain the right-hand side matrix B, and on exit is *> overwritten by the solution matrix X. *> \endverbatim *> *> \param[in] LDB *> \verbatim *> LDB is INTEGER *> On entry, LDB specifies the first dimension of B as declared *> in the calling (sub) program. LDB must be at least *> max( 1, m ). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level3 * *> \par Further Details: * ===================== *> *> \verbatim *> *> Level 3 Blas routine. *> *> *> -- Written on 8-February-1989. *> Jack Dongarra, Argonne National Laboratory. *> Iain Duff, AERE Harwell. *> Jeremy Du Croz, Numerical Algorithms Group Ltd. *> Sven Hammarling, Numerical Algorithms Group Ltd. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_DTRSM(SIDE,UPLO,TRANSA,DIAG,M,N,ALPHA,A,LDA,B,LDB) * * -- Reference BLAS level3 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. DOUBLE PRECISION ALPHA INTEGER LDA,LDB,M,N CHARACTERDIAG,SIDE,TRANSA,UPLO * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),B(LDB,*) * .. * * ===================================================================== * * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Local Scalars .. DOUBLE PRECISION TEMP INTEGER I,INFO,J,K,NROWA LOGICAL LSIDE,NOUNIT,UPPER * .. * .. Parameters .. DOUBLE PRECISION ONE,ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) * .. * * Test the input parameters. * LSIDE=GAL_LSAME(SIDE,'L') IF(LSIDE)THEN NROWA=M ELSE NROWA=N END IF NOUNIT=GAL_LSAME(DIAG,'N') UPPER=GAL_LSAME(UPLO,'U') * INFO=0 IF((.NOT.LSIDE).AND.(.NOT.GAL_LSAME(SIDE,'R')))THEN INFO=1 ELSE IF((.NOT.UPPER).AND.(.NOT.GAL_LSAME(UPLO,'L')))THEN INFO=2 ELSE IF((.NOT.GAL_LSAME(TRANSA,'N')).AND. +(.NOT.GAL_LSAME(TRANSA,'T')).AND. +(.NOT.GAL_LSAME(TRANSA,'C')))THEN INFO=3 ELSE IF((.NOT.GAL_LSAME(DIAG,'U')).AND. +(.NOT.GAL_LSAME(DIAG,'N')))THEN INFO=4 ELSE IF(M.LT.0)THEN INFO=5 ELSE IF(N.LT.0)THEN INFO=6 ELSE IF(LDA.LT.MAX(1,NROWA))THEN INFO=9 ELSE IF(LDB.LT.MAX(1,M))THEN INFO=11 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('DTRSM',INFO) RETURN END IF * * Quick return if possible. * IF(M.EQ.0.OR.N.EQ.0)RETURN * * And when alpha.eq.zero. * IF(ALPHA.EQ.ZERO)THEN DO 20J=1,N DO 10I=1,M B(I,J)=ZERO 10 CONTINUE 20 CONTINUE RETURN END IF * * Start the operations. * IF(LSIDE)THEN IF(GAL_LSAME(TRANSA,'N'))THEN * * Form B := alpha*inv( A )*B. * IF(UPPER)THEN DO 60J=1,N IF(ALPHA.NE.ONE)THEN DO 30I=1,M B(I,J)=ALPHA*B(I,J) 30 CONTINUE END IF DO 50K=M,1,-1 IF(B(K,J).NE.ZERO)THEN IF(NOUNIT)B(K,J)=B(K,J)/A(K,K) DO 40I=1,K-1 B(I,J)=B(I,J)-B(K,J)*A(I,K) 40 CONTINUE END IF 50 CONTINUE 60 CONTINUE ELSE DO 100J=1,N IF(ALPHA.NE.ONE)THEN DO 70I=1,M B(I,J)=ALPHA*B(I,J) 70 CONTINUE END IF DO 90K=1,M IF(B(K,J).NE.ZERO)THEN IF(NOUNIT)B(K,J)=B(K,J)/A(K,K) DO 80I=K+1,M B(I,J)=B(I,J)-B(K,J)*A(I,K) 80 CONTINUE END IF 90 CONTINUE 100 CONTINUE END IF ELSE * * Form B := alpha*inv( A**T )*B. * IF(UPPER)THEN DO 130J=1,N DO 120I=1,M TEMP=ALPHA*B(I,J) DO 110K=1,I-1 TEMP=TEMP-A(K,I)*B(K,J) 110 CONTINUE IF(NOUNIT)TEMP=TEMP/A(I,I) B(I,J)=TEMP 120 CONTINUE 130 CONTINUE ELSE DO 160J=1,N DO 150I=M,1,-1 TEMP=ALPHA*B(I,J) DO 140K=I+1,M TEMP=TEMP-A(K,I)*B(K,J) 140 CONTINUE IF(NOUNIT)TEMP=TEMP/A(I,I) B(I,J)=TEMP 150 CONTINUE 160 CONTINUE END IF END IF ELSE IF(GAL_LSAME(TRANSA,'N'))THEN * * Form B := alpha*B*inv( A ). * IF(UPPER)THEN DO 210J=1,N IF(ALPHA.NE.ONE)THEN DO 170I=1,M B(I,J)=ALPHA*B(I,J) 170 CONTINUE END IF DO 190K=1,J-1 IF(A(K,J).NE.ZERO)THEN DO 180I=1,M B(I,J)=B(I,J)-A(K,J)*B(I,K) 180 CONTINUE END IF 190 CONTINUE IF(NOUNIT)THEN TEMP=ONE/A(J,J) DO 200I=1,M B(I,J)=TEMP*B(I,J) 200 CONTINUE END IF 210 CONTINUE ELSE DO 260J=N,1,-1 IF(ALPHA.NE.ONE)THEN DO 220I=1,M B(I,J)=ALPHA*B(I,J) 220 CONTINUE END IF DO 240K=J+1,N IF(A(K,J).NE.ZERO)THEN DO 230I=1,M B(I,J)=B(I,J)-A(K,J)*B(I,K) 230 CONTINUE END IF 240 CONTINUE IF(NOUNIT)THEN TEMP=ONE/A(J,J) DO 250I=1,M B(I,J)=TEMP*B(I,J) 250 CONTINUE END IF 260 CONTINUE END IF ELSE * * Form B := alpha*B*inv( A**T ). * IF(UPPER)THEN DO 310K=N,1,-1 IF(NOUNIT)THEN TEMP=ONE/A(K,K) DO 270I=1,M B(I,K)=TEMP*B(I,K) 270 CONTINUE END IF DO 290J=1,K-1 IF(A(J,K).NE.ZERO)THEN TEMP=A(J,K) DO 280I=1,M B(I,J)=B(I,J)-TEMP*B(I,K) 280 CONTINUE END IF 290 CONTINUE IF(ALPHA.NE.ONE)THEN DO 300I=1,M B(I,K)=ALPHA*B(I,K) 300 CONTINUE END IF 310 CONTINUE ELSE DO 360K=1,N IF(NOUNIT)THEN TEMP=ONE/A(K,K) DO 320I=1,M B(I,K)=TEMP*B(I,K) 320 CONTINUE END IF DO 340J=K+1,N IF(A(J,K).NE.ZERO)THEN TEMP=A(J,K) DO 330I=1,M B(I,J)=B(I,J)-TEMP*B(I,K) 330 CONTINUE END IF 340 CONTINUE IF(ALPHA.NE.ONE)THEN DO 350I=1,M B(I,K)=ALPHA*B(I,K) 350 CONTINUE END IF 360 CONTINUE END IF END IF END IF * RETURN * * End of DTRSM . * END ga-5.9.2/LinAlg/lapack+blas/gal_dtrsv.f000066400000000000000000000202501500715745200175750ustar00rootroot00000000000000*> \brief \b DTRSV * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE DTRSV(UPLO,TRANS,DIAG,N,A,LDA,X,INCX) * * .. Scalar Arguments .. * INTEGER INCX,LDA,N * CHARACTER DIAG,TRANS,UPLO * .. * .. Array Arguments .. * DOUBLE PRECISION A(LDA,*),X(*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> DTRSV solves one of the systems of equations *> *> A*x = b, or A**T*x = b, *> *> where b and x are n element vectors and A is an n by n unit, or *> non-unit, upper or lower triangular matrix. *> *> No test for singularity or near-singularity is included in this *> routine. Such tests must be performed before calling this routine. *> \endverbatim * * Arguments: * ========== * *> \param[in] UPLO *> \verbatim *> UPLO is CHARACTER*1 *> On entry, UPLO specifies whether the matrix is an upper or *> lower triangular matrix as follows: *> *> UPLO = 'U' or 'u' A is an upper triangular matrix. *> *> UPLO = 'L' or 'l' A is a lower triangular matrix. *> \endverbatim *> *> \param[in] TRANS *> \verbatim *> TRANS is CHARACTER*1 *> On entry, TRANS specifies the equations to be solved as *> follows: *> *> TRANS = 'N' or 'n' A*x = b. *> *> TRANS = 'T' or 't' A**T*x = b. *> *> TRANS = 'C' or 'c' A**T*x = b. *> \endverbatim *> *> \param[in] DIAG *> \verbatim *> DIAG is CHARACTER*1 *> On entry, DIAG specifies whether or not A is unit *> triangular as follows: *> *> DIAG = 'U' or 'u' A is assumed to be unit triangular. *> *> DIAG = 'N' or 'n' A is not assumed to be unit *> triangular. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the order of the matrix A. *> N must be at least zero. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array of DIMENSION ( LDA, n ). *> Before entry with UPLO = 'U' or 'u', the leading n by n *> upper triangular part of the array A must contain the upper *> triangular matrix and the strictly lower triangular part of *> A is not referenced. *> Before entry with UPLO = 'L' or 'l', the leading n by n *> lower triangular part of the array A must contain the lower *> triangular matrix and the strictly upper triangular part of *> A is not referenced. *> Note that when DIAG = 'U' or 'u', the diagonal elements of *> A are not referenced either, but are assumed to be unity. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. LDA must be at least *> max( 1, n ). *> \endverbatim *> *> \param[in,out] X *> \verbatim *> X is DOUBLE PRECISION array of dimension at least *> ( 1 + ( n - 1 )*abs( INCX ) ). *> Before entry, the incremented array X must contain the n *> element right-hand side vector b. On exit, X is overwritten *> with the solution vector x. *> \endverbatim *> *> \param[in] INCX *> \verbatim *> INCX is INTEGER *> On entry, INCX specifies the increment for the elements of *> X. INCX must not be zero. *> *> Level 2 Blas routine. *> *> -- Written on 22-October-1986. *> Jack Dongarra, Argonne National Lab. *> Jeremy Du Croz, Nag Central Office. *> Sven Hammarling, Nag Central Office. *> Richard Hanson, Sandia National Labs. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup double_blas_level1 * * ===================================================================== SUBROUTINE GAL_DTRSV(UPLO,TRANS,DIAG,N,A,LDA,X,INCX) * * -- Reference BLAS level1 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. INTEGER INCX,LDA,N CHARACTERDIAG,TRANS,UPLO * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*),X(*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO PARAMETER (ZERO=0.0D+0) * .. * .. Local Scalars .. DOUBLE PRECISION TEMP INTEGER I,INFO,IX,J,JX,KX LOGICAL NOUNIT * .. * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * * Test the input parameters. * INFO=0 IF(.NOT.GAL_LSAME(UPLO,'U').AND..NOT.GAL_LSAME(UPLO,'L'))THEN INFO=1 ELSE IF(.NOT.GAL_LSAME(TRANS,'N').AND. +.NOT.GAL_LSAME(TRANS,'T').AND. +.NOT.GAL_LSAME(TRANS,'C'))THEN INFO=2 ELSE IF(.NOT.GAL_LSAME(DIAG,'U').AND. +.NOT.GAL_LSAME(DIAG,'N'))THEN INFO=3 ELSE IF(N.LT.0)THEN INFO=4 ELSE IF(LDA.LT.MAX(1,N))THEN INFO=6 ELSE IF(INCX.EQ.0)THEN INFO=8 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_DTRSV',INFO) RETURN END IF * * Quick return if possible. * IF(N.EQ.0)RETURN * NOUNIT=GAL_LSAME(DIAG,'N') * * Set up the start point in X if the increment is not unity. This * will be ( N - 1 )*INCX too small for descending loops. * IF(INCX.LE.0)THEN KX=1-(N-1)*INCX ELSE IF(INCX.NE.1)THEN KX=1 END IF * * Start the operations. In this version the elements of A are * accessed sequentially with one pass through A. * IF(GAL_LSAME(TRANS,'N'))THEN * * Form x := inv( A )*x. * IF(GAL_LSAME(UPLO,'U'))THEN IF(INCX.EQ.1)THEN DO 20J=N,1,-1 IF(X(J).NE.ZERO)THEN IF(NOUNIT)X(J)=X(J)/A(J,J) TEMP=X(J) DO 10I=J-1,1,-1 X(I)=X(I)-TEMP*A(I,J) 10 CONTINUE END IF 20 CONTINUE ELSE JX=KX+(N-1)*INCX DO 40J=N,1,-1 IF(X(JX).NE.ZERO)THEN IF(NOUNIT)X(JX)=X(JX)/A(J,J) TEMP=X(JX) IX=JX DO 30I=J-1,1,-1 IX=IX-INCX X(IX)=X(IX)-TEMP*A(I,J) 30 CONTINUE END IF JX=JX-INCX 40 CONTINUE END IF ELSE IF(INCX.EQ.1)THEN DO 60J=1,N IF(X(J).NE.ZERO)THEN IF(NOUNIT)X(J)=X(J)/A(J,J) TEMP=X(J) DO 50I=J+1,N X(I)=X(I)-TEMP*A(I,J) 50 CONTINUE END IF 60 CONTINUE ELSE JX=KX DO 80J=1,N IF(X(JX).NE.ZERO)THEN IF(NOUNIT)X(JX)=X(JX)/A(J,J) TEMP=X(JX) IX=JX DO 70I=J+1,N IX=IX+INCX X(IX)=X(IX)-TEMP*A(I,J) 70 CONTINUE END IF JX=JX+INCX 80 CONTINUE END IF END IF ELSE * * Form x := inv( A**T )*x. * IF(GAL_LSAME(UPLO,'U'))THEN IF(INCX.EQ.1)THEN DO 100J=1,N TEMP=X(J) DO 90I=1,J-1 TEMP=TEMP-A(I,J)*X(I) 90 CONTINUE IF(NOUNIT)TEMP=TEMP/A(J,J) X(J)=TEMP 100 CONTINUE ELSE JX=KX DO 120J=1,N TEMP=X(JX) IX=KX DO 110I=1,J-1 TEMP=TEMP-A(I,J)*X(IX) IX=IX+INCX 110 CONTINUE IF(NOUNIT)TEMP=TEMP/A(J,J) X(JX)=TEMP JX=JX+INCX 120 CONTINUE END IF ELSE IF(INCX.EQ.1)THEN DO 140J=N,1,-1 TEMP=X(J) DO 130I=N,J+1,-1 TEMP=TEMP-A(I,J)*X(I) 130 CONTINUE IF(NOUNIT)TEMP=TEMP/A(J,J) X(J)=TEMP 140 CONTINUE ELSE KX=KX+(N-1)*INCX JX=KX DO 160J=N,1,-1 TEMP=X(JX) IX=KX DO 150I=N,J+1,-1 TEMP=TEMP-A(I,J)*X(IX) IX=IX-INCX 150 CONTINUE IF(NOUNIT)TEMP=TEMP/A(J,J) X(JX)=TEMP JX=JX-INCX 160 CONTINUE END IF END IF END IF * RETURN * * End of DTRSV . * END ga-5.9.2/LinAlg/lapack+blas/gal_idamax.f000066400000000000000000000043301500715745200176770ustar00rootroot00000000000000*> \brief \b IDAMAX * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * INTEGER FUNCTION IDAMAX(N,DX,INCX) * * .. Scalar Arguments .. * INTEGER INCX,N * .. * .. Array Arguments .. * DOUBLE PRECISION DX(*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> IDAMAX finds the index of element having max. absolute value. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup aux_blas * *> \par Further Details: * ===================== *> *> \verbatim *> *> jack dongarra, linpack, 3/11/78. *> modified 3/93 to return if incx .le. 0. *> modified 12/3/93, array(1) declarations changed to array(*) *> \endverbatim *> * ===================================================================== INTEGER FUNCTION GAL_IDAMAX(N,DX,INCX) * * -- Reference BLAS level1 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. INTEGER INCX,N * .. * .. Array Arguments .. DOUBLE PRECISION DX(*) * .. * * ===================================================================== * * .. Local Scalars .. DOUBLE PRECISION DMAX INTEGER I,IX * .. * .. Intrinsic Functions .. INTRINSIC DABS * .. GAL_IDAMAX=0 IF(N.LT.1.OR.INCX.LE.0)RETURN GAL_IDAMAX=1 IF(N.EQ.1)RETURN IF(INCX.EQ.1)THEN * * code for increment equal to 1 * DMAX=DABS(DX(1)) DO I=2,N IF(DABS(DX(I)).GT.DMAX)THEN GAL_IDAMAX=I DMAX=DABS(DX(I)) END IF END DO ELSE * * code for increment not equal to 1 * IX=1 DMAX=DABS(DX(1)) IX=IX+INCX DO I=2,N IF(DABS(DX(IX)).GT.DMAX)THEN GAL_IDAMAX=I DMAX=DABS(DX(IX)) END IF IX=IX+INCX END DO END IF RETURN END ga-5.9.2/LinAlg/lapack+blas/gal_ieeeck.f000066400000000000000000000104051500715745200176610ustar00rootroot00000000000000*> \brief \b IEEECK * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download IEEECK + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * INTEGER FUNCTION IEEECK( ISPEC, ZERO, ONE ) * * .. Scalar Arguments .. * INTEGER ISPEC * REAL ONE, ZERO * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> IEEECK is called from the ILAENV to verify that Infinity and *> possibly NaN arithmetic is safe (i.e. will not trap). *> \endverbatim * * Arguments: * ========== * *> \param[in] ISPEC *> \verbatim *> ISPEC is INTEGER *> Specifies whether to test just for inifinity arithmetic *> or whether to test for infinity and NaN arithmetic. *> = 0: Verify infinity arithmetic only. *> = 1: Verify infinity and NaN arithmetic. *> \endverbatim *> *> \param[in] ZERO *> \verbatim *> ZERO is REAL *> Must contain the value 0.0 *> This is passed to prevent the compiler from optimizing *> away this code. *> \endverbatim *> *> \param[in] ONE *> \verbatim *> ONE is REAL *> Must contain the value 1.0 *> This is passed to prevent the compiler from optimizing *> away this code. *> *> RETURN VALUE: INTEGER *> = 0: Arithmetic failed to produce the correct answers *> = 1: Arithmetic produced the correct answers *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup auxOTHERauxiliary * * ===================================================================== INTEGER FUNCTION GAL_IEEECK(ISPEC,ZERO,ONE) * * -- LAPACK auxiliary routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. INTEGER ISPEC REALONE,ZERO * .. * * ===================================================================== * * .. Local Scalars .. REALNAN1,NAN2,NAN3,NAN4,NAN5,NAN6,NEGINF, $NEGZRO,NEWZRO,POSINF * .. * .. Executable Statements .. GAL_IEEECK=1 * POSINF=ONE/ZERO IF(POSINF.LE.ONE)THEN GAL_IEEECK=0 RETURN END IF * NEGINF=-ONE/ZERO IF(NEGINF.GE.ZERO)THEN GAL_IEEECK=0 RETURN END IF * NEGZRO=ONE/(NEGINF+ONE) IF(NEGZRO.NE.ZERO)THEN GAL_IEEECK=0 RETURN END IF * NEGINF=ONE/NEGZRO IF(NEGINF.GE.ZERO)THEN GAL_IEEECK=0 RETURN END IF * NEWZRO=NEGZRO+ZERO IF(NEWZRO.NE.ZERO)THEN GAL_IEEECK=0 RETURN END IF * POSINF=ONE/NEWZRO IF(POSINF.LE.ONE)THEN GAL_IEEECK=0 RETURN END IF * NEGINF=NEGINF*POSINF IF(NEGINF.GE.ZERO)THEN GAL_IEEECK=0 RETURN END IF * POSINF=POSINF*POSINF IF(POSINF.LE.ONE)THEN GAL_IEEECK=0 RETURN END IF * * * * * Return if we were only asked to check infinity arithmetic * IF(ISPEC.EQ.0) $RETURN * NAN1=POSINF+NEGINF * NAN2=POSINF/NEGINF * NAN3=POSINF/POSINF * NAN4=POSINF*ZERO * NAN5=NEGINF*NEGZRO * NAN6=NAN5*ZERO * IF(NAN1.EQ.NAN1)THEN GAL_IEEECK=0 RETURN END IF * IF(NAN2.EQ.NAN2)THEN GAL_IEEECK=0 RETURN END IF * IF(NAN3.EQ.NAN3)THEN GAL_IEEECK=0 RETURN END IF * IF(NAN4.EQ.NAN4)THEN GAL_IEEECK=0 RETURN END IF * IF(NAN5.EQ.NAN5)THEN GAL_IEEECK=0 RETURN END IF * IF(NAN6.EQ.NAN6)THEN GAL_IEEECK=0 RETURN END IF * RETURN END ga-5.9.2/LinAlg/lapack+blas/gal_iladlc.f000066400000000000000000000055721500715745200176750ustar00rootroot00000000000000*> \brief \b ILADLC scans a matrix for its last non-zero column. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download ILADLC + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * INTEGER FUNCTION ILADLC( M, N, A, LDA ) * * .. Scalar Arguments .. * INTEGER M, N, LDA * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> ILADLC scans A for its last non-zero column. *> \endverbatim * * Arguments: * ========== * *> \param[in] M *> \verbatim *> M is INTEGER *> The number of rows of the matrix A. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix A. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> The m by n matrix A. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,M). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERauxiliary * * ===================================================================== INTEGER FUNCTION GAL_ILADLC(M,N,A,LDA) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. INTEGER M,N,LDA * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO PARAMETER (ZERO=0.0D+0) * .. * .. Local Scalars .. INTEGER I * .. * .. Executable Statements .. * * Quick test for the common case where one corner is non-zero. IF(N.EQ.0)THEN GAL_ILADLC=N ELSE IF(A(1,N).NE.ZERO.OR.A(M,N).NE.ZERO)THEN GAL_ILADLC=N ELSE * Now scan each column from the end, returning with the first non-zero. DO GAL_ILADLC=N,1,-1 DO I=1,M IF(A(I,GAL_ILADLC).NE.ZERO)RETURN END DO END DO END IF RETURN END ga-5.9.2/LinAlg/lapack+blas/gal_iladlr.f000066400000000000000000000056411500715745200177110ustar00rootroot00000000000000*> \brief \b ILADLR scans a matrix for its last non-zero row. * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download ILADLR + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * INTEGER FUNCTION ILADLR( M, N, A, LDA ) * * .. Scalar Arguments .. * INTEGER M, N, LDA * .. * .. Array Arguments .. * DOUBLE PRECISION A( LDA, * ) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> ILADLR scans A for its last non-zero row. *> \endverbatim * * Arguments: * ========== * *> \param[in] M *> \verbatim *> M is INTEGER *> The number of rows of the matrix A. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> The number of columns of the matrix A. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is DOUBLE PRECISION array, dimension (LDA,N) *> The m by n matrix A. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> The leading dimension of the array A. LDA >= max(1,M). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date September 2012 * *> \ingroup auxOTHERauxiliary * * ===================================================================== INTEGER FUNCTION GAL_ILADLR(M,N,A,LDA) * * -- LAPACK auxiliary routine (version 3.4.2) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * September 2012 * * .. Scalar Arguments .. INTEGER M,N,LDA * .. * .. Array Arguments .. DOUBLE PRECISION A(LDA,*) * .. * * ===================================================================== * * .. Parameters .. DOUBLE PRECISION ZERO PARAMETER (ZERO=0.0D+0) * .. * .. Local Scalars .. INTEGER I,J * .. * .. Executable Statements .. * * Quick test for the common case where one corner is non-zero. IF(M.EQ.0)THEN GAL_ILADLR=M ELSE IF(A(M,1).NE.ZERO.OR.A(M,N).NE.ZERO)THEN GAL_ILADLR=M ELSE * Scan up each column tracking the last zero row seen. GAL_ILADLR=0 DO J=1,N I=M DO WHILE((A(MAX(I,1),J).EQ.ZERO).AND.(I.GE.1)) I=I-1 ENDDO GAL_ILADLR=MAX(GAL_ILADLR,I) END DO END IF RETURN END ga-5.9.2/LinAlg/lapack+blas/gal_ilaenv.f000066400000000000000000000365221500715745200177220ustar00rootroot00000000000000*> \brief \b ILAENV * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download ILAENV + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * INTEGER FUNCTION ILAENV( ISPEC, NAME, OPTS, N1, N2, N3, N4 ) * * .. Scalar Arguments .. * CHARACTER*( * ) NAME, OPTS * INTEGER ISPEC, N1, N2, N3, N4 * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> ILAENV is called from the LAPACK routines to choose problem-dependent *> parameters for the local environment. See ISPEC for a description of *> the parameters. *> *> ILAENV returns an INTEGER *> if ILAENV >= 0: ILAENV returns the value of the parameter specified by ISPEC *> if ILAENV < 0: if ILAENV = -k, the k-th argument had an illegal value. *> *> This version provides a set of parameters which should give good, *> but not optimal, performance on many of the currently available *> computers. Users are encouraged to modify this subroutine to set *> the tuning parameters for their particular machine using the option *> and problem size information in the arguments. *> *> This routine will not function correctly if it is converted to all *> lower case. Converting it to all upper case is allowed. *> \endverbatim * * Arguments: * ========== * *> \param[in] ISPEC *> \verbatim *> ISPEC is INTEGER *> Specifies the parameter to be returned as the value of *> ILAENV. *> = 1: the optimal blocksize; if this value is 1, an unblocked *> algorithm will give the best performance. *> = 2: the minimum block size for which the block routine *> should be used; if the usable block size is less than *> this value, an unblocked routine should be used. *> = 3: the crossover point (in a block routine, for N less *> than this value, an unblocked routine should be used) *> = 4: the number of shifts, used in the nonsymmetric *> eigenvalue routines (DEPRECATED) *> = 5: the minimum column dimension for blocking to be used; *> rectangular blocks must have dimension at least k by m, *> where k is given by ILAENV(2,...) and m by ILAENV(5,...) *> = 6: the crossover point for the SVD (when reducing an m by n *> matrix to bidiagonal form, if max(m,n)/min(m,n) exceeds *> this value, a QR factorization is used first to reduce *> the matrix to a triangular form.) *> = 7: the number of processors *> = 8: the crossover point for the multishift QR method *> for nonsymmetric eigenvalue problems (DEPRECATED) *> = 9: maximum size of the subproblems at the bottom of the *> computation tree in the divide-and-conquer algorithm *> (used by xGELSD and xGESDD) *> =10: ieee NaN arithmetic can be trusted not to trap *> =11: infinity arithmetic can be trusted not to trap *> 12 <= ISPEC <= 16: *> xHSEQR or one of its subroutines, *> see IPARMQ for detailed explanation *> \endverbatim *> *> \param[in] NAME *> \verbatim *> NAME is CHARACTER*(*) *> The name of the calling subroutine, in either upper case or *> lower case. *> \endverbatim *> *> \param[in] OPTS *> \verbatim *> OPTS is CHARACTER*(*) *> The character options to the subroutine NAME, concatenated *> into a single character string. For example, UPLO = 'U', *> TRANS = 'T', and DIAG = 'N' for a triangular routine would *> be specified as OPTS = 'UTN'. *> \endverbatim *> *> \param[in] N1 *> \verbatim *> N1 is INTEGER *> \endverbatim *> *> \param[in] N2 *> \verbatim *> N2 is INTEGER *> \endverbatim *> *> \param[in] N3 *> \verbatim *> N3 is INTEGER *> \endverbatim *> *> \param[in] N4 *> \verbatim *> N4 is INTEGER *> Problem dimensions for the subroutine NAME; these may not all *> be required. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup auxOTHERauxiliary * *> \par Further Details: * ===================== *> *> \verbatim *> *> The following conventions have been used when calling ILAENV from the *> LAPACK routines: *> 1) OPTS is a concatenation of all of the character options to *> subroutine NAME, in the same order that they appear in the *> argument list for NAME, even if they are not used in determining *> the value of the parameter specified by ISPEC. *> 2) The problem dimensions N1, N2, N3, N4 are specified in the order *> that they appear in the argument list for NAME. N1 is used *> first, N2 second, and so on, and unused problem dimensions are *> passed a value of -1. *> 3) The parameter value returned by ILAENV is checked for validity in *> the calling subroutine. For example, ILAENV is used to retrieve *> the optimal blocksize for STRTRI as follows: *> *> NB = ILAENV( 1, 'STRTRI', UPLO // DIAG, N, -1, -1, -1 ) *> IF( NB.LE.1 ) NB = MAX( 1, N ) *> \endverbatim *> * ===================================================================== INTEGER FUNCTION GAL_ILAENV(ISPEC,NAME,OPTS,N1,N2,N3,N4) * * -- LAPACK auxiliary routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. CHARACTER*(*)NAME,OPTS INTEGER ISPEC,N1,N2,N3,N4 * .. * * ===================================================================== * * .. Local Scalars .. INTEGER I,IC,IZ,NB,NBMIN,NX LOGICAL CNAME,SNAME CHARACTERC1*1,C2*2,C4*2,C3*3,SUBNAM*6 * .. * .. Intrinsic Functions .. INTRINSIC CHAR,ICHAR,INT,MIN,REAL * .. * .. External Functions .. INTEGER GAL_IEEECK,GAL_IPARMQ EXTERNAL GAL_IEEECK,GAL_IPARMQ * .. * .. Executable Statements .. * GO TO (10,10,10,80,90,100,110,120, $130,140,150,160,160,160,160,160)ISPEC * * Invalid value for ISPEC * GAL_ILAENV=-1 RETURN * 10 CONTINUE * * Convert NAME to upper case if the first character is lower case. * GAL_ILAENV=1 SUBNAM=NAME IC=ICHAR(SUBNAM(1:1)) IZ=ICHAR('Z') IF(IZ.EQ.90.OR.IZ.EQ.122)THEN * * ASCII character set * IF(IC.GE.97.AND.IC.LE.122)THEN SUBNAM(1:1)=CHAR(IC-32) DO 20I=2,6 IC=ICHAR(SUBNAM(I:I)) IF(IC.GE.97.AND.IC.LE.122) $SUBNAM(I:I)=CHAR(IC-32) 20 CONTINUE END IF * ELSE IF(IZ.EQ.233.OR.IZ.EQ.169)THEN * * EBCDIC character set * IF((IC.GE.129.AND.IC.LE.137).OR. $(IC.GE.145.AND.IC.LE.153).OR. $(IC.GE.162.AND.IC.LE.169))THEN SUBNAM(1:1)=CHAR(IC+64) DO 30I=2,6 IC=ICHAR(SUBNAM(I:I)) IF((IC.GE.129.AND.IC.LE.137).OR. $(IC.GE.145.AND.IC.LE.153).OR. $(IC.GE.162.AND.IC.LE.169))SUBNAM(I: $I)=CHAR(IC+64) 30 CONTINUE END IF * ELSE IF(IZ.EQ.218.OR.IZ.EQ.250)THEN * * Prime machines: ASCII+128 * IF(IC.GE.225.AND.IC.LE.250)THEN SUBNAM(1:1)=CHAR(IC-32) DO 40I=2,6 IC=ICHAR(SUBNAM(I:I)) IF(IC.GE.225.AND.IC.LE.250) $SUBNAM(I:I)=CHAR(IC-32) 40 CONTINUE END IF END IF * C1=SUBNAM(1:1) SNAME=C1.EQ.'S'.OR.C1.EQ.'D' CNAME=C1.EQ.'C'.OR.C1.EQ.'Z' IF(.NOT.(CNAME.OR.SNAME)) $RETURN C2=SUBNAM(2:3) C3=SUBNAM(4:6) C4=C3(2:3) * GO TO (50,60,70)ISPEC * 50 CONTINUE * * ISPEC = 1: block size * * In these examples, separate code is provided for setting NB for * real and complex. We assume that NB will take the same value in * single or double precision. * NB=1 * IF(C2.EQ.'GE')THEN IF(C3.EQ.'TRF')THEN IF(SNAME)THEN NB=64 ELSE NB=64 END IF ELSE IF(C3.EQ.'QRF'.OR.C3.EQ.'RQF'.OR.C3.EQ.'LQF'.OR. $C3.EQ.'QLF')THEN IF(SNAME)THEN NB=32 ELSE NB=32 END IF ELSE IF(C3.EQ.'HRD')THEN IF(SNAME)THEN NB=32 ELSE NB=32 END IF ELSE IF(C3.EQ.'BRD')THEN IF(SNAME)THEN NB=32 ELSE NB=32 END IF ELSE IF(C3.EQ.'TRI')THEN IF(SNAME)THEN NB=64 ELSE NB=64 END IF END IF ELSE IF(C2.EQ.'PO')THEN IF(C3.EQ.'TRF')THEN IF(SNAME)THEN NB=64 ELSE NB=64 END IF END IF ELSE IF(C2.EQ.'SY')THEN IF(C3.EQ.'TRF')THEN IF(SNAME)THEN NB=64 ELSE NB=64 END IF ELSE IF(SNAME.AND.C3.EQ.'TRD')THEN NB=32 ELSE IF(SNAME.AND.C3.EQ.'GST')THEN NB=64 END IF ELSE IF(CNAME.AND.C2.EQ.'HE')THEN IF(C3.EQ.'TRF')THEN NB=64 ELSE IF(C3.EQ.'TRD')THEN NB=32 ELSE IF(C3.EQ.'GST')THEN NB=64 END IF ELSE IF(SNAME.AND.C2.EQ.'OR')THEN IF(C3(1:1).EQ.'G')THEN IF(C4.EQ.'QR'.OR.C4.EQ.'RQ'.OR.C4.EQ.'LQ'.OR.C4.EQ. $'QL'.OR.C4.EQ.'HR'.OR.C4.EQ.'TR'.OR.C4.EQ.'BR') $THEN NB=32 END IF ELSE IF(C3(1:1).EQ.'M')THEN IF(C4.EQ.'QR'.OR.C4.EQ.'RQ'.OR.C4.EQ.'LQ'.OR.C4.EQ. $'QL'.OR.C4.EQ.'HR'.OR.C4.EQ.'TR'.OR.C4.EQ.'BR') $THEN NB=32 END IF END IF ELSE IF(CNAME.AND.C2.EQ.'UN')THEN IF(C3(1:1).EQ.'G')THEN IF(C4.EQ.'QR'.OR.C4.EQ.'RQ'.OR.C4.EQ.'LQ'.OR.C4.EQ. $'QL'.OR.C4.EQ.'HR'.OR.C4.EQ.'TR'.OR.C4.EQ.'BR') $THEN NB=32 END IF ELSE IF(C3(1:1).EQ.'M')THEN IF(C4.EQ.'QR'.OR.C4.EQ.'RQ'.OR.C4.EQ.'LQ'.OR.C4.EQ. $'QL'.OR.C4.EQ.'HR'.OR.C4.EQ.'TR'.OR.C4.EQ.'BR') $THEN NB=32 END IF END IF ELSE IF(C2.EQ.'GB')THEN IF(C3.EQ.'TRF')THEN IF(SNAME)THEN IF(N4.LE.64)THEN NB=1 ELSE NB=32 END IF ELSE IF(N4.LE.64)THEN NB=1 ELSE NB=32 END IF END IF END IF ELSE IF(C2.EQ.'PB')THEN IF(C3.EQ.'TRF')THEN IF(SNAME)THEN IF(N2.LE.64)THEN NB=1 ELSE NB=32 END IF ELSE IF(N2.LE.64)THEN NB=1 ELSE NB=32 END IF END IF END IF ELSE IF(C2.EQ.'TR')THEN IF(C3.EQ.'TRI')THEN IF(SNAME)THEN NB=64 ELSE NB=64 END IF END IF ELSE IF(C2.EQ.'LA')THEN IF(C3.EQ.'UUM')THEN IF(SNAME)THEN NB=64 ELSE NB=64 END IF END IF ELSE IF(SNAME.AND.C2.EQ.'ST')THEN IF(C3.EQ.'EBZ')THEN NB=1 END IF END IF GAL_ILAENV=NB RETURN * 60 CONTINUE * * ISPEC = 2: minimum block size * NBMIN=2 IF(C2.EQ.'GE')THEN IF(C3.EQ.'QRF'.OR.C3.EQ.'RQF'.OR.C3.EQ.'LQF'.OR.C3.EQ. $'QLF')THEN IF(SNAME)THEN NBMIN=2 ELSE NBMIN=2 END IF ELSE IF(C3.EQ.'HRD')THEN IF(SNAME)THEN NBMIN=2 ELSE NBMIN=2 END IF ELSE IF(C3.EQ.'BRD')THEN IF(SNAME)THEN NBMIN=2 ELSE NBMIN=2 END IF ELSE IF(C3.EQ.'TRI')THEN IF(SNAME)THEN NBMIN=2 ELSE NBMIN=2 END IF END IF ELSE IF(C2.EQ.'SY')THEN IF(C3.EQ.'TRF')THEN IF(SNAME)THEN NBMIN=8 ELSE NBMIN=8 END IF ELSE IF(SNAME.AND.C3.EQ.'TRD')THEN NBMIN=2 END IF ELSE IF(CNAME.AND.C2.EQ.'HE')THEN IF(C3.EQ.'TRD')THEN NBMIN=2 END IF ELSE IF(SNAME.AND.C2.EQ.'OR')THEN IF(C3(1:1).EQ.'G')THEN IF(C4.EQ.'QR'.OR.C4.EQ.'RQ'.OR.C4.EQ.'LQ'.OR.C4.EQ. $'QL'.OR.C4.EQ.'HR'.OR.C4.EQ.'TR'.OR.C4.EQ.'BR') $THEN NBMIN=2 END IF ELSE IF(C3(1:1).EQ.'M')THEN IF(C4.EQ.'QR'.OR.C4.EQ.'RQ'.OR.C4.EQ.'LQ'.OR.C4.EQ. $'QL'.OR.C4.EQ.'HR'.OR.C4.EQ.'TR'.OR.C4.EQ.'BR') $THEN NBMIN=2 END IF END IF ELSE IF(CNAME.AND.C2.EQ.'UN')THEN IF(C3(1:1).EQ.'G')THEN IF(C4.EQ.'QR'.OR.C4.EQ.'RQ'.OR.C4.EQ.'LQ'.OR.C4.EQ. $'QL'.OR.C4.EQ.'HR'.OR.C4.EQ.'TR'.OR.C4.EQ.'BR') $THEN NBMIN=2 END IF ELSE IF(C3(1:1).EQ.'M')THEN IF(C4.EQ.'QR'.OR.C4.EQ.'RQ'.OR.C4.EQ.'LQ'.OR.C4.EQ. $'QL'.OR.C4.EQ.'HR'.OR.C4.EQ.'TR'.OR.C4.EQ.'BR') $THEN NBMIN=2 END IF END IF END IF GAL_ILAENV=NBMIN RETURN * 70 CONTINUE * * ISPEC = 3: crossover point * NX=0 IF(C2.EQ.'GE')THEN IF(C3.EQ.'QRF'.OR.C3.EQ.'RQF'.OR.C3.EQ.'LQF'.OR.C3.EQ. $'QLF')THEN IF(SNAME)THEN NX=128 ELSE NX=128 END IF ELSE IF(C3.EQ.'HRD')THEN IF(SNAME)THEN NX=128 ELSE NX=128 END IF ELSE IF(C3.EQ.'BRD')THEN IF(SNAME)THEN NX=128 ELSE NX=128 END IF END IF ELSE IF(C2.EQ.'SY')THEN IF(SNAME.AND.C3.EQ.'TRD')THEN NX=32 END IF ELSE IF(CNAME.AND.C2.EQ.'HE')THEN IF(C3.EQ.'TRD')THEN NX=32 END IF ELSE IF(SNAME.AND.C2.EQ.'OR')THEN IF(C3(1:1).EQ.'G')THEN IF(C4.EQ.'QR'.OR.C4.EQ.'RQ'.OR.C4.EQ.'LQ'.OR.C4.EQ. $'QL'.OR.C4.EQ.'HR'.OR.C4.EQ.'TR'.OR.C4.EQ.'BR') $THEN NX=128 END IF END IF ELSE IF(CNAME.AND.C2.EQ.'UN')THEN IF(C3(1:1).EQ.'G')THEN IF(C4.EQ.'QR'.OR.C4.EQ.'RQ'.OR.C4.EQ.'LQ'.OR.C4.EQ. $'QL'.OR.C4.EQ.'HR'.OR.C4.EQ.'TR'.OR.C4.EQ.'BR') $THEN NX=128 END IF END IF END IF GAL_ILAENV=NX RETURN * 80 CONTINUE * * ISPEC = 4: number of shifts (used by xHSEQR) * GAL_ILAENV=6 RETURN * 90 CONTINUE * * ISPEC = 5: minimum column dimension (not used) * GAL_ILAENV=2 RETURN * 100 CONTINUE * * ISPEC = 6: crossover point for SVD (used by xGELSS and xGESVD) * GAL_ILAENV=INT(REAL(MIN(N1,N2))*1.6E0) RETURN * 110 CONTINUE * * ISPEC = 7: number of processors (not used) * GAL_ILAENV=1 RETURN * 120 CONTINUE * * ISPEC = 8: crossover point for multishift (used by xHSEQR) * GAL_ILAENV=50 RETURN * 130 CONTINUE * * ISPEC = 9: maximum size of the subproblems at the bottom of the * computation tree in the divide-and-conquer algorithm * (used by xGELSD and xGESDD) * GAL_ILAENV=25 RETURN * 140 CONTINUE * * ISPEC = 10: ieee NaN arithmetic can be trusted not to trap * * ILAENV = 0 GAL_ILAENV=1 IF(GAL_ILAENV.EQ.1)THEN GAL_ILAENV=GAL_IEEECK(1,0.0,1.0) END IF RETURN * 150 CONTINUE * * ISPEC = 11: infinity arithmetic can be trusted not to trap * * ILAENV = 0 GAL_ILAENV=1 IF(GAL_ILAENV.EQ.1)THEN GAL_ILAENV=GAL_IEEECK(0,0.0,1.0) END IF RETURN * 160 CONTINUE * * 12 <= ISPEC <= 16: xHSEQR or one of its subroutines. * GAL_ILAENV=GAL_IPARMQ(ISPEC,NAME,OPTS,N1,N2,N3,N4) RETURN * * End of ILAENV * END ga-5.9.2/LinAlg/lapack+blas/gal_iparmq.f000066400000000000000000000256301500715745200177330ustar00rootroot00000000000000*> \brief \b IPARMQ * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download IPARMQ + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * INTEGER FUNCTION IPARMQ( ISPEC, NAME, OPTS, N, ILO, IHI, LWORK ) * * .. Scalar Arguments .. * INTEGER IHI, ILO, ISPEC, LWORK, N * CHARACTER NAME*( * ), OPTS*( * ) * * *> \par Purpose: * ============= *> *> \verbatim *> *> This program sets problem and machine dependent parameters *> useful for xHSEQR and its subroutines. It is called whenever *> ILAENV is called with 12 <= ISPEC <= 16 *> \endverbatim * * Arguments: * ========== * *> \param[in] ISPEC *> \verbatim *> ISPEC is integer scalar *> ISPEC specifies which tunable parameter IPARMQ should *> return. *> *> ISPEC=12: (INMIN) Matrices of order nmin or less *> are sent directly to xLAHQR, the implicit *> double shift QR algorithm. NMIN must be *> at least 11. *> *> ISPEC=13: (INWIN) Size of the deflation window. *> This is best set greater than or equal to *> the number of simultaneous shifts NS. *> Larger matrices benefit from larger deflation *> windows. *> *> ISPEC=14: (INIBL) Determines when to stop nibbling and *> invest in an (expensive) multi-shift QR sweep. *> If the aggressive early deflation subroutine *> finds LD converged eigenvalues from an order *> NW deflation window and LD.GT.(NW*NIBBLE)/100, *> then the next QR sweep is skipped and early *> deflation is applied immediately to the *> remaining active diagonal block. Setting *> IPARMQ(ISPEC=14) = 0 causes TTQRE to skip a *> multi-shift QR sweep whenever early deflation *> finds a converged eigenvalue. Setting *> IPARMQ(ISPEC=14) greater than or equal to 100 *> prevents TTQRE from skipping a multi-shift *> QR sweep. *> *> ISPEC=15: (NSHFTS) The number of simultaneous shifts in *> a multi-shift QR iteration. *> *> ISPEC=16: (IACC22) IPARMQ is set to 0, 1 or 2 with the *> following meanings. *> 0: During the multi-shift QR sweep, *> xLAQR5 does not accumulate reflections and *> does not use matrix-matrix multiply to *> update the far-from-diagonal matrix *> entries. *> 1: During the multi-shift QR sweep, *> xLAQR5 and/or xLAQRaccumulates reflections and uses *> matrix-matrix multiply to update the *> far-from-diagonal matrix entries. *> 2: During the multi-shift QR sweep. *> xLAQR5 accumulates reflections and takes *> advantage of 2-by-2 block structure during *> matrix-matrix multiplies. *> (If xTRMM is slower than xGEMM, then *> IPARMQ(ISPEC=16)=1 may be more efficient than *> IPARMQ(ISPEC=16)=2 despite the greater level of *> arithmetic work implied by the latter choice.) *> \endverbatim *> *> \param[in] NAME *> \verbatim *> NAME is character string *> Name of the calling subroutine *> \endverbatim *> *> \param[in] OPTS *> \verbatim *> OPTS is character string *> This is a concatenation of the string arguments to *> TTQRE. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is integer scalar *> N is the order of the Hessenberg matrix H. *> \endverbatim *> *> \param[in] ILO *> \verbatim *> ILO is INTEGER *> \endverbatim *> *> \param[in] IHI *> \verbatim *> IHI is INTEGER *> It is assumed that H is already upper triangular *> in rows and columns 1:ILO-1 and IHI+1:N. *> \endverbatim *> *> \param[in] LWORK *> \verbatim *> LWORK is integer scalar *> The amount of workspace available. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup auxOTHERauxiliary * *> \par Further Details: * ===================== *> *> \verbatim *> *> Little is known about how best to choose these parameters. *> It is possible to use different values of the parameters *> for each of CHSEQR, DHSEQR, SHSEQR and ZHSEQR. *> *> It is probably best to choose different parameters for *> different matrices and different parameters at different *> times during the iteration, but this has not been *> implemented --- yet. *> *> *> The best choices of most of the parameters depend *> in an ill-understood way on the relative execution *> rate of xLAQR3 and xLAQR5 and on the nature of each *> particular eigenvalue problem. Experiment may be the *> only practical way to determine which choices are most *> effective. *> *> Following is a list of default values supplied by IPARMQ. *> These defaults may be adjusted in order to attain better *> performance in any particular computational environment. *> *> IPARMQ(ISPEC=12) The xLAHQR vs xLAQR0 crossover point. *> Default: 75. (Must be at least 11.) *> *> IPARMQ(ISPEC=13) Recommended deflation window size. *> This depends on ILO, IHI and NS, the *> number of simultaneous shifts returned *> by IPARMQ(ISPEC=15). The default for *> (IHI-ILO+1).LE.500 is NS. The default *> for (IHI-ILO+1).GT.500 is 3*NS/2. *> *> IPARMQ(ISPEC=14) Nibble crossover point. Default: 14. *> *> IPARMQ(ISPEC=15) Number of simultaneous shifts, NS. *> a multi-shift QR iteration. *> *> If IHI-ILO+1 is ... *> *> greater than ...but less ... the *> or equal to ... than default is *> *> 0 30 NS = 2+ *> 30 60 NS = 4+ *> 60 150 NS = 10 *> 150 590 NS = ** *> 590 3000 NS = 64 *> 3000 6000 NS = 128 *> 6000 infinity NS = 256 *> *> (+) By default matrices of this order are *> passed to the implicit double shift routine *> xLAHQR. See IPARMQ(ISPEC=12) above. These *> values of NS are used only in case of a rare *> xLAHQR failure. *> *> (**) The asterisks (**) indicate an ad-hoc *> function increasing from 10 to 64. *> *> IPARMQ(ISPEC=16) Select structured matrix multiply. *> (See ISPEC=16 above for details.) *> Default: 3. *> \endverbatim *> * ===================================================================== INTEGER FUNCTION GAL_IPARMQ(ISPEC,NAME,OPTS,N,ILO,IHI,LWORK) * * -- LAPACK auxiliary routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. INTEGER IHI,ILO,ISPEC,LWORK,N CHARACTERNAME*(*),OPTS*(*) * * ================================================================ * .. Parameters .. INTEGER INMIN,INWIN,INIBL,ISHFTS,IACC22 PARAMETER (INMIN=12,INWIN=13,INIBL=14, $ISHFTS=15,IACC22=16) INTEGER NMIN,K22MIN,KACMIN,NIBBLE,KNWSWP PARAMETER (NMIN=75,K22MIN=14,KACMIN=14, $NIBBLE=14,KNWSWP=500) REALTWO PARAMETER (TWO=2.0) * .. * .. Local Scalars .. INTEGER NH,NS * .. * .. Intrinsic Functions .. INTRINSIC LOG,MAX,MOD,NINT,REAL * .. * .. Executable Statements .. IF((ISPEC.EQ.ISHFTS).OR.(ISPEC.EQ.INWIN).OR. $(ISPEC.EQ.IACC22))THEN * * ==== Set the number simultaneous shifts ==== * NH=IHI-ILO+1 NS=2 IF(NH.GE.30) $NS=4 IF(NH.GE.60) $NS=10 IF(NH.GE.150) $NS=MAX(10,NH/NINT(LOG(REAL(NH))/LOG(TWO))) IF(NH.GE.590) $NS=64 IF(NH.GE.3000) $NS=128 IF(NH.GE.6000) $NS=256 NS=MAX(2,NS-MOD(NS,2)) END IF * IF(ISPEC.EQ.INMIN)THEN * * * ===== Matrices of order smaller than NMIN get sent * . to xLAHQR, the classic double shift algorithm. * . This must be at least 11. ==== * GAL_IPARMQ=NMIN * ELSE IF(ISPEC.EQ.INIBL)THEN * * ==== INIBL: skip a multi-shift qr iteration and * . whenever aggressive early deflation finds * . at least (NIBBLE*(window size)/100) deflations. ==== * GAL_IPARMQ=NIBBLE * ELSE IF(ISPEC.EQ.ISHFTS)THEN * * ==== NSHFTS: The number of simultaneous shifts ===== * GAL_IPARMQ=NS * ELSE IF(ISPEC.EQ.INWIN)THEN * * ==== NW: deflation window size. ==== * IF(NH.LE.KNWSWP)THEN GAL_IPARMQ=NS ELSE GAL_IPARMQ=3*NS/2 END IF * ELSE IF(ISPEC.EQ.IACC22)THEN * * ==== IACC22: Whether to accumulate reflections * . before updating the far-from-diagonal elements * . and whether to use 2-by-2 block structure while * . doing it. A small amount of work could be saved * . by making this choice dependent also upon the * . NH=IHI-ILO+1. * GAL_IPARMQ=0 IF(NS.GE.KACMIN) $GAL_IPARMQ=1 IF(NS.GE.K22MIN) $GAL_IPARMQ=2 * ELSE * ===== invalid value of ispec ===== GAL_IPARMQ=-1 * END IF * * ==== End of IPARMQ ==== * END ga-5.9.2/LinAlg/lapack+blas/gal_lsame.f000066400000000000000000000057121500715745200175420ustar00rootroot00000000000000*> \brief \b LSAME * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * LOGICAL FUNCTION LSAME(CA,CB) * * .. Scalar Arguments .. * CHARACTER CA,CB * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> LSAME returns .TRUE. if CA is the same letter as CB regardless of *> case. *> \endverbatim * * Arguments: * ========== * *> \param[in] CA *> \verbatim *> CA is CHARACTER*1 *> \endverbatim *> *> \param[in] CB *> \verbatim *> CB is CHARACTER*1 *> CA and CB specify the single characters to be compared. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup aux_blas * * ===================================================================== LOGICAL FUNCTION GAL_LSAME(CA,CB) * * -- Reference BLAS level1 routine (version 3.1) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. CHARACTERCA,CB * .. * * ===================================================================== * * .. Intrinsic Functions .. INTRINSIC ICHAR * .. * .. Local Scalars .. INTEGER INTA,INTB,ZCODE * .. * * Test if the characters are equal * GAL_LSAME=CA.EQ.CB IF(GAL_LSAME)RETURN * * Now test for equivalence if both characters are alphabetic. * ZCODE=ICHAR('Z') * * Use 'Z' rather than 'A' so that ASCII can be detected on Prime * machines, on which ICHAR returns a value with bit 8 set. * ICHAR('A') on Prime machines returns 193 which is the same as * ICHAR('A') on an EBCDIC machine. * INTA=ICHAR(CA) INTB=ICHAR(CB) * IF(ZCODE.EQ.90.OR.ZCODE.EQ.122)THEN * * ASCII is assumed - ZCODE is the ASCII code of either lower or * upper case 'Z'. * IF(INTA.GE.97.AND.INTA.LE.122)INTA=INTA-32 IF(INTB.GE.97.AND.INTB.LE.122)INTB=INTB-32 * ELSE IF(ZCODE.EQ.233.OR.ZCODE.EQ.169)THEN * * EBCDIC is assumed - ZCODE is the EBCDIC code of either lower or * upper case 'Z'. * IF(INTA.GE.129.AND.INTA.LE.137.OR. +INTA.GE.145.AND.INTA.LE.153.OR. +INTA.GE.162.AND.INTA.LE.169)INTA=INTA+64 IF(INTB.GE.129.AND.INTB.LE.137.OR. +INTB.GE.145.AND.INTB.LE.153.OR. +INTB.GE.162.AND.INTB.LE.169)INTB=INTB+64 * ELSE IF(ZCODE.EQ.218.OR.ZCODE.EQ.250)THEN * * ASCII is assumed, on Prime machines - ZCODE is the ASCII code * plus 128 of either lower or upper case 'Z'. * IF(INTA.GE.225.AND.INTA.LE.250)INTA=INTA-32 IF(INTB.GE.225.AND.INTB.LE.250)INTB=INTB-32 END IF GAL_LSAME=INTA.EQ.INTB * * RETURN * * End of LSAME * END ga-5.9.2/LinAlg/lapack+blas/gal_sgemm.f000066400000000000000000000231371500715745200175520ustar00rootroot00000000000000*> \brief \b SGEMM * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE SGEMM(TRANSA,TRANSB,M,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) * * .. Scalar Arguments .. * REAL ALPHA,BETA * INTEGER K,LDA,LDB,LDC,M,N * CHARACTER TRANSA,TRANSB * .. * .. Array Arguments .. * REAL A(LDA,*),B(LDB,*),C(LDC,*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> SGEMM performs one of the matrix-matrix operations *> *> C := alpha*op( A )*op( B ) + beta*C, *> *> where op( X ) is one of *> *> op( X ) = X or op( X ) = X**T, *> *> alpha and beta are scalars, and A, B and C are matrices, with op( A ) *> an m by k matrix, op( B ) a k by n matrix and C an m by n matrix. *> \endverbatim * * Arguments: * ========== * *> \param[in] TRANSA *> \verbatim *> TRANSA is CHARACTER*1 *> On entry, TRANSA specifies the form of op( A ) to be used in *> the matrix multiplication as follows: *> *> TRANSA = 'N' or 'n', op( A ) = A. *> *> TRANSA = 'T' or 't', op( A ) = A**T. *> *> TRANSA = 'C' or 'c', op( A ) = A**T. *> \endverbatim *> *> \param[in] TRANSB *> \verbatim *> TRANSB is CHARACTER*1 *> On entry, TRANSB specifies the form of op( B ) to be used in *> the matrix multiplication as follows: *> *> TRANSB = 'N' or 'n', op( B ) = B. *> *> TRANSB = 'T' or 't', op( B ) = B**T. *> *> TRANSB = 'C' or 'c', op( B ) = B**T. *> \endverbatim *> *> \param[in] M *> \verbatim *> M is INTEGER *> On entry, M specifies the number of rows of the matrix *> op( A ) and of the matrix C. M must be at least zero. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the number of columns of the matrix *> op( B ) and the number of columns of the matrix C. N must be *> at least zero. *> \endverbatim *> *> \param[in] K *> \verbatim *> K is INTEGER *> On entry, K specifies the number of columns of the matrix *> op( A ) and the number of rows of the matrix op( B ). K must *> be at least zero. *> \endverbatim *> *> \param[in] ALPHA *> \verbatim *> ALPHA is REAL *> On entry, ALPHA specifies the scalar alpha. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is REAL array of DIMENSION ( LDA, ka ), where ka is *> k when TRANSA = 'N' or 'n', and is m otherwise. *> Before entry with TRANSA = 'N' or 'n', the leading m by k *> part of the array A must contain the matrix A, otherwise *> the leading k by m part of the array A must contain the *> matrix A. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. When TRANSA = 'N' or 'n' then *> LDA must be at least max( 1, m ), otherwise LDA must be at *> least max( 1, k ). *> \endverbatim *> *> \param[in] B *> \verbatim *> B is REAL array of DIMENSION ( LDB, kb ), where kb is *> n when TRANSB = 'N' or 'n', and is k otherwise. *> Before entry with TRANSB = 'N' or 'n', the leading k by n *> part of the array B must contain the matrix B, otherwise *> the leading n by k part of the array B must contain the *> matrix B. *> \endverbatim *> *> \param[in] LDB *> \verbatim *> LDB is INTEGER *> On entry, LDB specifies the first dimension of B as declared *> in the calling (sub) program. When TRANSB = 'N' or 'n' then *> LDB must be at least max( 1, k ), otherwise LDB must be at *> least max( 1, n ). *> \endverbatim *> *> \param[in] BETA *> \verbatim *> BETA is REAL *> On entry, BETA specifies the scalar beta. When BETA is *> supplied as zero then C need not be set on input. *> \endverbatim *> *> \param[in,out] C *> \verbatim *> C is REAL array of DIMENSION ( LDC, n ). *> Before entry, the leading m by n part of the array C must *> contain the matrix C, except when beta is zero, in which *> case C need not be set on entry. *> On exit, the array C is overwritten by the m by n matrix *> ( alpha*op( A )*op( B ) + beta*C ). *> \endverbatim *> *> \param[in] LDC *> \verbatim *> LDC is INTEGER *> On entry, LDC specifies the first dimension of C as declared *> in the calling (sub) program. LDC must be at least *> max( 1, m ). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup single_blas_level3 * *> \par Further Details: * ===================== *> *> \verbatim *> *> Level 3 Blas routine. *> *> -- Written on 8-February-1989. *> Jack Dongarra, Argonne National Laboratory. *> Iain Duff, AERE Harwell. *> Jeremy Du Croz, Numerical Algorithms Group Ltd. *> Sven Hammarling, Numerical Algorithms Group Ltd. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_SGEMM(TRANSA,TRANSB,M,N,K, $ALPHA,A,LDA,B,LDB,BETA,C,LDC) * * -- Reference BLAS level3 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. REALALPHA,BETA INTEGER K,LDA,LDB,LDC,M,N CHARACTERTRANSA,TRANSB * .. * .. Array Arguments .. REALA(LDA,*),B(LDB,*),C(LDC,*) * .. * * ===================================================================== * * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Local Scalars .. REALTEMP INTEGER I,INFO,J,L,NCOLA,NROWA,NROWB LOGICAL NOTA,NOTB * .. * .. Parameters .. REALONE,ZERO PARAMETER (ONE=1.0E+0,ZERO=0.0E+0) * .. * * Set NOTA and NOTB as true if A and B respectively are not * transposed and set NROWA, NCOLA and NROWB as the number of rows * and columns of A and the number of rows of B respectively. * NOTA=GAL_LSAME(TRANSA,'N') NOTB=GAL_LSAME(TRANSB,'N') IF(NOTA)THEN NROWA=M NCOLA=K ELSE NROWA=K NCOLA=M END IF IF(NOTB)THEN NROWB=K ELSE NROWB=N END IF * * Test the input parameters. * INFO=0 IF((.NOT.NOTA).AND.(.NOT.GAL_LSAME(TRANSA,'C')).AND. +(.NOT.GAL_LSAME(TRANSA,'T')))THEN INFO=1 ELSE IF((.NOT.NOTB).AND.(.NOT.GAL_LSAME(TRANSB,'C')).AND. +(.NOT.GAL_LSAME(TRANSB,'T')))THEN INFO=2 ELSE IF(M.LT.0)THEN INFO=3 ELSE IF(N.LT.0)THEN INFO=4 ELSE IF(K.LT.0)THEN INFO=5 ELSE IF(LDA.LT.MAX(1,NROWA))THEN INFO=8 ELSE IF(LDB.LT.MAX(1,NROWB))THEN INFO=10 ELSE IF(LDC.LT.MAX(1,M))THEN INFO=13 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_SGEMM',INFO) RETURN END IF * * Quick return if possible. * IF((M.EQ.0).OR.(N.EQ.0).OR. +(((ALPHA.EQ.ZERO).OR.(K.EQ.0)).AND.(BETA.EQ.ONE)))RETURN * * And if alpha.eq.zero. * IF(ALPHA.EQ.ZERO)THEN IF(BETA.EQ.ZERO)THEN DO 20J=1,N DO 10I=1,M C(I,J)=ZERO 10 CONTINUE 20 CONTINUE ELSE DO 40J=1,N DO 30I=1,M C(I,J)=BETA*C(I,J) 30 CONTINUE 40 CONTINUE END IF RETURN END IF * * Start the operations. * IF(NOTB)THEN IF(NOTA)THEN * * Form C := alpha*A*B + beta*C. * DO 90J=1,N IF(BETA.EQ.ZERO)THEN DO 50I=1,M C(I,J)=ZERO 50 CONTINUE ELSE IF(BETA.NE.ONE)THEN DO 60I=1,M C(I,J)=BETA*C(I,J) 60 CONTINUE END IF DO 80L=1,K IF(B(L,J).NE.ZERO)THEN TEMP=ALPHA*B(L,J) DO 70I=1,M C(I,J)=C(I,J)+TEMP*A(I,L) 70 CONTINUE END IF 80 CONTINUE 90 CONTINUE ELSE * * Form C := alpha*A**T*B + beta*C * DO 120J=1,N DO 110I=1,M TEMP=ZERO DO 100L=1,K TEMP=TEMP+A(L,I)*B(L,J) 100 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 110 CONTINUE 120 CONTINUE END IF ELSE IF(NOTA)THEN * * Form C := alpha*A*B**T + beta*C * DO 170J=1,N IF(BETA.EQ.ZERO)THEN DO 130I=1,M C(I,J)=ZERO 130 CONTINUE ELSE IF(BETA.NE.ONE)THEN DO 140I=1,M C(I,J)=BETA*C(I,J) 140 CONTINUE END IF DO 160L=1,K IF(B(J,L).NE.ZERO)THEN TEMP=ALPHA*B(J,L) DO 150I=1,M C(I,J)=C(I,J)+TEMP*A(I,L) 150 CONTINUE END IF 160 CONTINUE 170 CONTINUE ELSE * * Form C := alpha*A**T*B**T + beta*C * DO 200J=1,N DO 190I=1,M TEMP=ZERO DO 180L=1,K TEMP=TEMP+A(L,I)*B(J,L) 180 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 190 CONTINUE 200 CONTINUE END IF END IF * RETURN * * End of SGEMM . * END ga-5.9.2/LinAlg/lapack+blas/gal_xerbla.f000066400000000000000000000047641500715745200177240ustar00rootroot00000000000000*> \brief \b XERBLA * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * *> \htmlonly *> Download XERBLA + dependencies *> *> [TGZ] *> *> [ZIP] *> *> [TXT] *> \endhtmlonly * * Definition: * =========== * * SUBROUTINE XERBLA( SRNAME, INFO ) * * .. Scalar Arguments .. * CHARACTER*(*) SRNAME * INTEGER INFO * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> XERBLA is an error handler for the LAPACK routines. *> It is called by an LAPACK routine if an input parameter has an *> invalid value. A message is printed and execution stops. *> *> Installers may consider modifying the STOP statement in order to *> call system-specific exception-handling facilities. *> \endverbatim * * Arguments: * ========== * *> \param[in] SRNAME *> \verbatim *> SRNAME is CHARACTER*(*) *> The name of the routine which called XERBLA. *> \endverbatim *> *> \param[in] INFO *> \verbatim *> INFO is INTEGER *> The position of the invalid parameter in the parameter list *> of the calling routine. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup auxOTHERauxiliary * * ===================================================================== SUBROUTINE GAL_XERBLA(SRNAME,INFO) * * -- LAPACK auxiliary routine (version 3.4.0) -- * -- LAPACK is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. CHARACTER*(*)SRNAME INTEGER INFO * .. * * ===================================================================== * * .. Intrinsic Functions .. INTRINSIC LEN_TRIM * .. * .. Executable Statements .. * WRITE(*,FMT=9999)SRNAME(1:LEN_TRIM(SRNAME)),INFO * STOP * 9999 FORMAT('**ONENTRYTO ',A,'PARAMETER NUMBER',I2,'HAD', $'ANILLEGALVALUE') * * End of XERBLA * END ga-5.9.2/LinAlg/lapack+blas/gal_zaxpy.f000066400000000000000000000042501500715745200176100ustar00rootroot00000000000000*> \brief \b ZAXPY * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE ZAXPY(N,ZA,ZX,INCX,ZY,INCY) * * .. Scalar Arguments .. * COMPLEX*16 ZA * INTEGER INCX,INCY,N * .. * .. Array Arguments .. * COMPLEX*16 ZX(*),ZY(*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> ZAXPY constant times a vector plus a vector. *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup complex16_blas_level1 * *> \par Further Details: * ===================== *> *> \verbatim *> *> jack dongarra, 3/11/78. *> modified 12/3/93, array(1) declarations changed to array(*) *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_ZAXPY(N,ZA,ZX,INCX,ZY,INCY) * * -- Reference BLAS level1 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. COMPLEX*16ZA INTEGER INCX,INCY,N * .. * .. Array Arguments .. COMPLEX*16ZX(*),ZY(*) * .. * * ===================================================================== * * .. Local Scalars .. INTEGER I,IX,IY * .. * .. External Functions .. DOUBLE PRECISION GAL_DCABS1 EXTERNAL GAL_DCABS1 * .. IF(N.LE.0)RETURN IF(GAL_DCABS1(ZA).EQ.0.0D0)RETURN IF(INCX.EQ.1.AND.INCY.EQ.1)THEN * * code for both increments equal to 1 * DO I=1,N ZY(I)=ZY(I)+ZA*ZX(I) END DO ELSE * * code for unequal increments or equal increments * not equal to 1 * IX=1 IY=1 IF(INCX.LT.0)IX=(-N+1)*INCX+1 IF(INCY.LT.0)IY=(-N+1)*INCY+1 DO I=1,N ZY(IY)=ZY(IY)+ZA*ZX(IX) IX=IX+INCX IY=IY+INCY END DO END IF * RETURN END ga-5.9.2/LinAlg/lapack+blas/gal_zgemm.f000066400000000000000000000273761500715745200175720ustar00rootroot00000000000000*> \brief \b ZGEMM * * =========== DOCUMENTATION =========== * * Online html documentation available at * http://www.netlib.org/lapack/explore-html/ * * Definition: * =========== * * SUBROUTINE ZGEMM(TRANSA,TRANSB,M,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) * * .. Scalar Arguments .. * COMPLEX*16 ALPHA,BETA * INTEGER K,LDA,LDB,LDC,M,N * CHARACTER TRANSA,TRANSB * .. * .. Array Arguments .. * COMPLEX*16 A(LDA,*),B(LDB,*),C(LDC,*) * .. * * *> \par Purpose: * ============= *> *> \verbatim *> *> ZGEMM performs one of the matrix-matrix operations *> *> C := alpha*op( A )*op( B ) + beta*C, *> *> where op( X ) is one of *> *> op( X ) = X or op( X ) = X**T or op( X ) = X**H, *> *> alpha and beta are scalars, and A, B and C are matrices, with op( A ) *> an m by k matrix, op( B ) a k by n matrix and C an m by n matrix. *> \endverbatim * * Arguments: * ========== * *> \param[in] TRANSA *> \verbatim *> TRANSA is CHARACTER*1 *> On entry, TRANSA specifies the form of op( A ) to be used in *> the matrix multiplication as follows: *> *> TRANSA = 'N' or 'n', op( A ) = A. *> *> TRANSA = 'T' or 't', op( A ) = A**T. *> *> TRANSA = 'C' or 'c', op( A ) = A**H. *> \endverbatim *> *> \param[in] TRANSB *> \verbatim *> TRANSB is CHARACTER*1 *> On entry, TRANSB specifies the form of op( B ) to be used in *> the matrix multiplication as follows: *> *> TRANSB = 'N' or 'n', op( B ) = B. *> *> TRANSB = 'T' or 't', op( B ) = B**T. *> *> TRANSB = 'C' or 'c', op( B ) = B**H. *> \endverbatim *> *> \param[in] M *> \verbatim *> M is INTEGER *> On entry, M specifies the number of rows of the matrix *> op( A ) and of the matrix C. M must be at least zero. *> \endverbatim *> *> \param[in] N *> \verbatim *> N is INTEGER *> On entry, N specifies the number of columns of the matrix *> op( B ) and the number of columns of the matrix C. N must be *> at least zero. *> \endverbatim *> *> \param[in] K *> \verbatim *> K is INTEGER *> On entry, K specifies the number of columns of the matrix *> op( A ) and the number of rows of the matrix op( B ). K must *> be at least zero. *> \endverbatim *> *> \param[in] ALPHA *> \verbatim *> ALPHA is COMPLEX*16 *> On entry, ALPHA specifies the scalar alpha. *> \endverbatim *> *> \param[in] A *> \verbatim *> A is COMPLEX*16 array of DIMENSION ( LDA, ka ), where ka is *> k when TRANSA = 'N' or 'n', and is m otherwise. *> Before entry with TRANSA = 'N' or 'n', the leading m by k *> part of the array A must contain the matrix A, otherwise *> the leading k by m part of the array A must contain the *> matrix A. *> \endverbatim *> *> \param[in] LDA *> \verbatim *> LDA is INTEGER *> On entry, LDA specifies the first dimension of A as declared *> in the calling (sub) program. When TRANSA = 'N' or 'n' then *> LDA must be at least max( 1, m ), otherwise LDA must be at *> least max( 1, k ). *> \endverbatim *> *> \param[in] B *> \verbatim *> B is COMPLEX*16 array of DIMENSION ( LDB, kb ), where kb is *> n when TRANSB = 'N' or 'n', and is k otherwise. *> Before entry with TRANSB = 'N' or 'n', the leading k by n *> part of the array B must contain the matrix B, otherwise *> the leading n by k part of the array B must contain the *> matrix B. *> \endverbatim *> *> \param[in] LDB *> \verbatim *> LDB is INTEGER *> On entry, LDB specifies the first dimension of B as declared *> in the calling (sub) program. When TRANSB = 'N' or 'n' then *> LDB must be at least max( 1, k ), otherwise LDB must be at *> least max( 1, n ). *> \endverbatim *> *> \param[in] BETA *> \verbatim *> BETA is COMPLEX*16 *> On entry, BETA specifies the scalar beta. When BETA is *> supplied as zero then C need not be set on input. *> \endverbatim *> *> \param[in,out] C *> \verbatim *> C is COMPLEX*16 array of DIMENSION ( LDC, n ). *> Before entry, the leading m by n part of the array C must *> contain the matrix C, except when beta is zero, in which *> case C need not be set on entry. *> On exit, the array C is overwritten by the m by n matrix *> ( alpha*op( A )*op( B ) + beta*C ). *> \endverbatim *> *> \param[in] LDC *> \verbatim *> LDC is INTEGER *> On entry, LDC specifies the first dimension of C as declared *> in the calling (sub) program. LDC must be at least *> max( 1, m ). *> \endverbatim * * Authors: * ======== * *> \author Univ. of Tennessee *> \author Univ. of California Berkeley *> \author Univ. of Colorado Denver *> \author NAG Ltd. * *> \date November 2011 * *> \ingroup complex16_blas_level3 * *> \par Further Details: * ===================== *> *> \verbatim *> *> Level 3 Blas routine. *> *> -- Written on 8-February-1989. *> Jack Dongarra, Argonne National Laboratory. *> Iain Duff, AERE Harwell. *> Jeremy Du Croz, Numerical Algorithms Group Ltd. *> Sven Hammarling, Numerical Algorithms Group Ltd. *> \endverbatim *> * ===================================================================== SUBROUTINE GAL_ZGEMM(TRANSA,TRANSB,M,N,K, $ALPHA,A,LDA,B,LDB,BETA,C,LDC) * * -- Reference BLAS level3 routine (version 3.4.0) -- * -- Reference BLAS is a software package provided by Univ. of Tennessee, -- * -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- * November 2011 * * .. Scalar Arguments .. COMPLEX*16ALPHA,BETA INTEGER K,LDA,LDB,LDC,M,N CHARACTERTRANSA,TRANSB * .. * .. Array Arguments .. COMPLEX*16A(LDA,*),B(LDB,*),C(LDC,*) * .. * * ===================================================================== * * .. External Functions .. LOGICAL GAL_LSAME EXTERNAL GAL_LSAME * .. * .. External Subroutines .. EXTERNAL GAL_XERBLA * .. * .. Intrinsic Functions .. INTRINSIC DCONJG,MAX * .. * .. Local Scalars .. COMPLEX*16TEMP INTEGER I,INFO,J,L,NCOLA,NROWA,NROWB LOGICAL CONJA,CONJB,NOTA,NOTB * .. * .. Parameters .. COMPLEX*16ONE PARAMETER (ONE=(1.0D+0,0.0D+0)) COMPLEX*16ZERO PARAMETER (ZERO=(0.0D+0,0.0D+0)) * .. * * Set NOTA and NOTB as true if A and B respectively are not * conjugated or transposed, set CONJA and CONJB as true if A and * B respectively are to be transposed but not conjugated and set * NROWA, NCOLA and NROWB as the number of rows and columns of A * and the number of rows of B respectively. * NOTA=GAL_LSAME(TRANSA,'N') NOTB=GAL_LSAME(TRANSB,'N') CONJA=GAL_LSAME(TRANSA,'C') CONJB=GAL_LSAME(TRANSB,'C') IF(NOTA)THEN NROWA=M NCOLA=K ELSE NROWA=K NCOLA=M END IF IF(NOTB)THEN NROWB=K ELSE NROWB=N END IF * * Test the input parameters. * INFO=0 IF((.NOT.NOTA).AND.(.NOT.CONJA).AND. +(.NOT.GAL_LSAME(TRANSA,'T')))THEN INFO=1 ELSE IF((.NOT.NOTB).AND.(.NOT.CONJB).AND. +(.NOT.GAL_LSAME(TRANSB,'T')))THEN INFO=2 ELSE IF(M.LT.0)THEN INFO=3 ELSE IF(N.LT.0)THEN INFO=4 ELSE IF(K.LT.0)THEN INFO=5 ELSE IF(LDA.LT.MAX(1,NROWA))THEN INFO=8 ELSE IF(LDB.LT.MAX(1,NROWB))THEN INFO=10 ELSE IF(LDC.LT.MAX(1,M))THEN INFO=13 END IF IF(INFO.NE.0)THEN CALL GAL_XERBLA('GAL_ZGEMM',INFO) RETURN END IF * * Quick return if possible. * IF((M.EQ.0).OR.(N.EQ.0).OR. +(((ALPHA.EQ.ZERO).OR.(K.EQ.0)).AND.(BETA.EQ.ONE)))RETURN * * And when alpha.eq.zero. * IF(ALPHA.EQ.ZERO)THEN IF(BETA.EQ.ZERO)THEN DO 20J=1,N DO 10I=1,M C(I,J)=ZERO 10 CONTINUE 20 CONTINUE ELSE DO 40J=1,N DO 30I=1,M C(I,J)=BETA*C(I,J) 30 CONTINUE 40 CONTINUE END IF RETURN END IF * * Start the operations. * IF(NOTB)THEN IF(NOTA)THEN * * Form C := alpha*A*B + beta*C. * DO 90J=1,N IF(BETA.EQ.ZERO)THEN DO 50I=1,M C(I,J)=ZERO 50 CONTINUE ELSE IF(BETA.NE.ONE)THEN DO 60I=1,M C(I,J)=BETA*C(I,J) 60 CONTINUE END IF DO 80L=1,K IF(B(L,J).NE.ZERO)THEN TEMP=ALPHA*B(L,J) DO 70I=1,M C(I,J)=C(I,J)+TEMP*A(I,L) 70 CONTINUE END IF 80 CONTINUE 90 CONTINUE ELSE IF(CONJA)THEN * * Form C := alpha*A**H*B + beta*C. * DO 120J=1,N DO 110I=1,M TEMP=ZERO DO 100L=1,K TEMP=TEMP+DCONJG(A(L,I))*B(L,J) 100 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 110 CONTINUE 120 CONTINUE ELSE * * Form C := alpha*A**T*B + beta*C * DO 150J=1,N DO 140I=1,M TEMP=ZERO DO 130L=1,K TEMP=TEMP+A(L,I)*B(L,J) 130 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 140 CONTINUE 150 CONTINUE END IF ELSE IF(NOTA)THEN IF(CONJB)THEN * * Form C := alpha*A*B**H + beta*C. * DO 200J=1,N IF(BETA.EQ.ZERO)THEN DO 160I=1,M C(I,J)=ZERO 160 CONTINUE ELSE IF(BETA.NE.ONE)THEN DO 170I=1,M C(I,J)=BETA*C(I,J) 170 CONTINUE END IF DO 190L=1,K IF(B(J,L).NE.ZERO)THEN TEMP=ALPHA*DCONJG(B(J,L)) DO 180I=1,M C(I,J)=C(I,J)+TEMP*A(I,L) 180 CONTINUE END IF 190 CONTINUE 200 CONTINUE ELSE * * Form C := alpha*A*B**T + beta*C * DO 250J=1,N IF(BETA.EQ.ZERO)THEN DO 210I=1,M C(I,J)=ZERO 210 CONTINUE ELSE IF(BETA.NE.ONE)THEN DO 220I=1,M C(I,J)=BETA*C(I,J) 220 CONTINUE END IF DO 240L=1,K IF(B(J,L).NE.ZERO)THEN TEMP=ALPHA*B(J,L) DO 230I=1,M C(I,J)=C(I,J)+TEMP*A(I,L) 230 CONTINUE END IF 240 CONTINUE 250 CONTINUE END IF ELSE IF(CONJA)THEN IF(CONJB)THEN * * Form C := alpha*A**H*B**H + beta*C. * DO 280J=1,N DO 270I=1,M TEMP=ZERO DO 260L=1,K TEMP=TEMP+DCONJG(A(L,I))*DCONJG(B(J,L)) 260 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 270 CONTINUE 280 CONTINUE ELSE * * Form C := alpha*A**H*B**T + beta*C * DO 310J=1,N DO 300I=1,M TEMP=ZERO DO 290L=1,K TEMP=TEMP+DCONJG(A(L,I))*B(J,L) 290 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 300 CONTINUE 310 CONTINUE END IF ELSE IF(CONJB)THEN * * Form C := alpha*A**T*B**H + beta*C * DO 340J=1,N DO 330I=1,M TEMP=ZERO DO 320L=1,K TEMP=TEMP+A(L,I)*DCONJG(B(J,L)) 320 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 330 CONTINUE 340 CONTINUE ELSE * * Form C := alpha*A**T*B**T + beta*C * DO 370J=1,N DO 360I=1,M TEMP=ZERO DO 350L=1,K TEMP=TEMP+A(L,I)*B(J,L) 350 CONTINUE IF(BETA.EQ.ZERO)THEN C(I,J)=ALPHA*TEMP ELSE C(I,J)=ALPHA*TEMP+BETA*C(I,J) END IF 360 CONTINUE 370 CONTINUE END IF END IF * RETURN * * End of ZGEMM . * END ga-5.9.2/LinAlg/lapack+blas/galinalg.fh000066400000000000000000000011571500715745200175430ustar00rootroot00000000000000#ifndef GALINALG_FH_ #define GALINALG_FH_ #if HAVE_BLAS #else #define sgemm gal_sgemm #define SGEMM GAL_SGEMM #define dgemm gal_dgemm #define DGEMM GAL_DGEMM #define zgemm gal_zgemm #define ZGEMM GAL_ZGEMM #define cgemm gal_cgemm #define CGEMM GAL_CGEMM #endif #if HAVE_LAPACK #else #define dgetrf gal_dgetrf #define DGETRF GAL_DGETRF #define dsyev gal_dsyev #define DSYEV GAL_DSYEV #define dsygv gal_dsygv #define DSYGV GAL_DSYGV #define dtrsm gal_dtrsm #define DTRSM GAL_DTRSM #define lsame gal_lsame #define LSAME GAL_LSAME #define xerbla gal_xerbla #define XERBLA GAL_XERBLA #endif #endif /* GALINALG_FH_ */ ga-5.9.2/LinAlg/lapack+blas/galinalg.h000066400000000000000000000267701500715745200174050ustar00rootroot00000000000000#ifndef GALINALG_H_ #define GALINALG_H_ #define sgemm_ F77_FUNC(sgemm, SGEMM) #define dgemm_ F77_FUNC(dgemm, DGEMM) #define zgemm_ F77_FUNC(zgemm, ZGEMM) #define cgemm_ F77_FUNC(cgemm, CGEMM) #define dgetrf_ F77_FUNC(dgetrf,DGETRF) #define dgetrs_ F77_FUNC(dgetrs,DGETRS) #define dtrsm_ F77_FUNC(dtrsm, DTRSM) #define gal_sgemm_ F77_FUNC_(gal_sgemm, GAL_SGEMM) #define gal_dgemm_ F77_FUNC_(gal_dgemm, GAL_DGEMM) #define gal_zgemm_ F77_FUNC_(gal_zgemm, GAL_ZGEMM) #define gal_cgemm_ F77_FUNC_(gal_cgemm, GAL_CGEMM) #define gal_dgetrf_ F77_FUNC_(gal_dgetrf,GAL_DGETRF) #define gal_dgetrs_ F77_FUNC_(gal_dgetrs,GAL_DGETRS) #define gal_dtrsm_ F77_FUNC_(gal_dtrsm, GAL_DTRSM) #if HAVE_BLAS || ENABLE_F77 # if BLAS_SIZE == SIZEOF_SHORT # define BlasInt short # elif BLAS_SIZE == SIZEOF_INT # define BlasInt int # elif BLAS_SIZE == SIZEOF_LONG # define BlasInt long # elif BLAS_SIZE == SIZEOF_LONG_LONG # define BlasInt long long # endif #else # define BlasInt int #endif #if HAVE_BLAS # if defined(F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS) extern void sgemm_(char *TRANSA, char *TRANSB, BlasInt *M, BlasInt *N, BlasInt *K, Real *ALPHA, Real *A, BlasInt *LDA, Real *B, BlasInt *LDB, Real *BETA, Real *C, BlasInt *LDC, int alen, int blen); extern void dgemm_(char *TRANSA, char *TRANSB, BlasInt *M, BlasInt *N, BlasInt *K, DoublePrecision *ALPHA, DoublePrecision *A, BlasInt *LDA, DoublePrecision *B, BlasInt *LDB, DoublePrecision *BETA, DoublePrecision *C, BlasInt *LDC, int alen, int blen); extern void zgemm_(char *TRANSA, char *TRANSB, BlasInt *M, BlasInt *N, BlasInt *K, DoubleComplex *ALPHA, DoubleComplex *A, BlasInt *LDA, DoubleComplex *B, BlasInt *LDB, DoubleComplex *BETA, DoubleComplex *C, BlasInt *LDC, int alen, int blen); extern void cgemm_(char *TRANSA, char *TRANSB, BlasInt *M, BlasInt *N, BlasInt *K, SingleComplex *ALPHA, SingleComplex *A, BlasInt *LDA, SingleComplex *B, BlasInt *LDB, SingleComplex *BETA, SingleComplex *C, BlasInt *LDC, int alen, int blen); # else extern void sgemm_(char *TRANSA, int alen, char *TRANSB, int blen, BlasInt *M, BlasInt *N, BlasInt *K, Real *ALPHA, Real *A, BlasInt *LDA, Real *B, BlasInt *LDB, Real *BETA, Real *C, BlasInt *LDC); extern void dgemm_(char *TRANSA, int alen, char *TRANSB, int blen, BlasInt *M, BlasInt *N, BlasInt *K, DoublePrecision *ALPHA, DoublePrecision *A, BlasInt *LDA, DoublePrecision *B, BlasInt *LDB, DoublePrecision *BETA, DoublePrecision *C, BlasInt *LDC); extern void zgemm_(char *TRANSA, int alen, char *TRANSB, int blen, BlasInt *M, BlasInt *N, BlasInt *K, DoubleComplex *ALPHA, DoubleComplex *A, BlasInt *LDA, DoubleComplex *B, BlasInt *LDB, DoubleComplex *BETA, DoubleComplex *C, BlasInt *LDC); extern void cgemm_(char *TRANSA, int alen, char *TRANSB, int blen, BlasInt *M, BlasInt *N, BlasInt *K, SingleComplex *ALPHA, SingleComplex *A, BlasInt *LDA, SingleComplex *B, BlasInt *LDB, SingleComplex *BETA, SingleComplex *C, BlasInt *LDC); # endif #elif ENABLE_F77 # if defined(F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS) extern void gal_sgemm_(char *TRANSA, char *TRANSB, BlasInt *M, BlasInt *N, BlasInt *K, Real *ALPHA, Real *A, BlasInt *LDA, Real *B, BlasInt *LDB, Real *BETA, Real *C, BlasInt *LDC, int alen, int blen); extern void gal_dgemm_(char *TRANSA, char *TRANSB, BlasInt *M, BlasInt *N, BlasInt *K, DoublePrecision *ALPHA, DoublePrecision *A, BlasInt *LDA, DoublePrecision *B, BlasInt *LDB, DoublePrecision *BETA, DoublePrecision *C, BlasInt *LDC, int alen, int blen); extern void gal_zgemm_(char *TRANSA, char *TRANSB, BlasInt *M, BlasInt *N, BlasInt *K, DoubleComplex *ALPHA, DoubleComplex *A, BlasInt *LDA, DoubleComplex *B, BlasInt *LDB, DoubleComplex *BETA, DoubleComplex *C, BlasInt *LDC, int alen, int blen); extern void gal_cgemm_(char *TRANSA, char *TRANSB, BlasInt *M, BlasInt *N, BlasInt *K, SingleComplex *ALPHA, SingleComplex *A, BlasInt *LDA, SingleComplex *B, BlasInt *LDB, SingleComplex *BETA, SingleComplex *C, BlasInt *LDC, int alen, int blen); # else extern void gal_sgemm_(char *TRANSA, int alen, char *TRANSB, int blen, BlasInt *M, BlasInt *N, BlasInt *K, Real *ALPHA, Real *A, BlasInt *LDA, Real *B, BlasInt *LDB, Real *BETA, Real *C, BlasInt *LDC); extern void gal_dgemm_(char *TRANSA, int alen, char *TRANSB, int blen, BlasInt *M, BlasInt *N, BlasInt *K, DoublePrecision *ALPHA, DoublePrecision *A, BlasInt *LDA, DoublePrecision *B, BlasInt *LDB, DoublePrecision *BETA, DoublePrecision *C, BlasInt *LDC); extern void gal_zgemm_(char *TRANSA, int alen, char *TRANSB, int blen, BlasInt *M, BlasInt *N, BlasInt *K, DoubleComplex *ALPHA, DoubleComplex *A, BlasInt *LDA, DoubleComplex *B, BlasInt *LDB, DoubleComplex *BETA, DoubleComplex *C, BlasInt *LDC); extern void gal_cgemm_(char *TRANSA, int alen, char *TRANSB, int blen, BlasInt *M, BlasInt *N, BlasInt *K, SingleComplex *ALPHA, SingleComplex *A, BlasInt *LDA, SingleComplex *B, BlasInt *LDB, SingleComplex *BETA, SingleComplex *C, BlasInt *LDC); # endif #else # include "xgemm.h" #endif /* HAVE_BLAS */ #if HAVE_LAPACK # if defined(F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS) extern void dtrsm_(char *side, char *uplo, char *transa, char *diag, BlasInt *m, BlasInt *n, DoublePrecision *alpha, DoublePrecision *a, BlasInt *lda, DoublePrecision *b, BlasInt *ldb, int, int, int, int ); extern void dgetrs_(char *trans, BlasInt *n, BlasInt *nrhs, DoublePrecision *a, BlasInt *lda, BlasInt *ipiv, DoublePrecision *b, BlasInt *ldb, BlasInt *info, int len ); # else extern void dtrsm_(char *side, int, char *uplo, int, char *transa, int, char *diag, int, BlasInt *m, BlasInt *n, DoublePrecision *alpha, DoublePrecision *a, BlasInt *lda, DoublePrecision *b, BlasInt *ldb ); extern void dgetrs_(char *trans, int len, BlasInt *n, BlasInt *nrhs, DoublePrecision *a, BlasInt *lda, BlasInt *ipiv, DoublePrecision *b, BlasInt *ldb, BlasInt *info ); # endif extern void dgetrf_( BlasInt *m, BlasInt *n, DoublePrecision *a, BlasInt *ld, BlasInt *ipiv, BlasInt *info ); #elif ENABLE_F77 # if defined(F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS) extern void gal_dtrsm_(char *side, char *uplo, char *transa, char *diag, BlasInt *m, BlasInt *n, DoublePrecision *alpha, DoublePrecision *a, BlasInt *lda, DoublePrecision *b, BlasInt *ldb, int, int, int, int ); extern void gal_dgetrs_(char *trans, BlasInt *n, BlasInt *nrhs, DoublePrecision *a, BlasInt *lda, BlasInt *ipiv, DoublePrecision *b, BlasInt *ldb, BlasInt *info, int len ); # else extern void gal_dtrsm_(char *side, int, char *uplo, int, char *transa, int, char *diag, int, BlasInt *m, BlasInt *n, DoublePrecision *alpha, DoublePrecision *a, BlasInt *lda, DoublePrecision *b, BlasInt *ldb ); extern void gal_dgetrs_(char *trans, int len, BlasInt *n, BlasInt *nrhs, DoublePrecision *a, BlasInt *lda, BlasInt *ipiv, DoublePrecision *b, BlasInt *ldb, BlasInt *info ); # endif extern void gal_dgetrf_( BlasInt *m, BlasInt *n, DoublePrecision *a, BlasInt *ld, BlasInt *ipiv, BlasInt *info ); #else #endif /* HAVE_LAPACK */ #if HAVE_BLAS # if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS # define BLAS_SGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ sgemm_(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc, 1, 1) # define BLAS_DGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ dgemm_(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc, 1, 1) # define BLAS_ZGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ zgemm_(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc, 1, 1) # define BLAS_CGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ cgemm_(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc, 1, 1) # else # define BLAS_SGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ sgemm_(ta, 1, tb, 1, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) # define BLAS_DGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ dgemm_(ta, 1, tb, 1, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) # define BLAS_ZGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ zgemm_(ta, 1, tb, 1, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) # define BLAS_CGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ cgemm_(ta, 1, tb, 1, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) # endif #elif ENABLE_F77 # if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS # define BLAS_SGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ gal_sgemm_(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc, 1, 1) # define BLAS_DGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ gal_dgemm_(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc, 1, 1) # define BLAS_ZGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ gal_zgemm_(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc, 1, 1) # define BLAS_CGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ gal_cgemm_(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc, 1, 1) # else # define BLAS_SGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ gal_sgemm_(ta, 1, tb, 1, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) # define BLAS_DGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ gal_dgemm_(ta, 1, tb, 1, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) # define BLAS_ZGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ gal_zgemm_(ta, 1, tb, 1, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) # define BLAS_CGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ gal_cgemm_(ta, 1, tb, 1, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) # endif #else # define BLAS_SGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ xb_sgemm(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) # define BLAS_DGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ xb_dgemm(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) # define BLAS_ZGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ xb_zgemm(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) # define BLAS_CGEMM(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) \ xb_cgemm(ta, tb, i, j, k, alpha, a, lda, b, ldb, beta, c, ldc) #endif #if HAVE_LAPACK # if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS # define LAPACK_DTRSM(side, uplo, transa, diag, m, n, alpha, a, lda, b, ldb) \ dtrsm_(side, uplo, transa, diag, m, n, alpha, a, lda, b, ldb, 1, 1, 1, 1) # define LAPACK_DGETRS(trans, n, nrhs, a, lda, ipiv, b, ldb, info) \ dgetrs_(trans, n, nrhs, a, lda, ipiv, b, ldb, info, 1) # else # define LAPACK_DTRSM(side, 1, uplo, 1, transa, 1, diag, 1, m, n, alpha, a, lda, b, ldb) \ dtrsm_(side, uplo, transa, diag, m, n, alpha, a, lda, b, ldb) # define LAPACK_DGETRS(trans, n, nrhs, a, lda, ipiv, b, ldb, info) \ dgetrs_(trans, 1, n, nrhs, a, lda, ipiv, b, ldb, info) # endif # define LAPACK_DGETRF dgetrf_ #elif ENABLE_F77 # if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS # define LAPACK_DTRSM(side, uplo, transa, diag, m, n, alpha, a, lda, b, ldb) \ gal_dtrsm_(side, uplo, transa, diag, m, n, alpha, a, lda, b, ldb, 1, 1, 1, 1) # define LAPACK_DGETRS(trans, n, nrhs, a, lda, ipiv, b, ldb, info) \ gal_dgetrs_(trans, n, nrhs, a, lda, ipiv, b, ldb, info, 1) # else # define LAPACK_DTRSM(side, 1, uplo, 1, transa, 1, diag, 1, m, n, alpha, a, lda, b, ldb) \ gal_dtrsm_(side, uplo, transa, diag, m, n, alpha, a, lda, b, ldb) # define LAPACK_DGETRS(trans, n, nrhs, a, lda, ipiv, b, ldb, info) \ gal_dgetrs_(trans, 1, n, nrhs, a, lda, ipiv, b, ldb, info) # endif # define LAPACK_DGETRF gal_dgetrf_ #elif NOFORT #endif #endif /* GALINALG_H_ */ ga-5.9.2/LinAlg/lapack+blas/xgemm.c000066400000000000000000000752011500715745200167300ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "xgemm.h" #define COLMAJOR 'c' #define ROWMAJOR 'r' #define MDEBUG 0 void xb_sgemm (char *transa, char *transb, int *M, int *N, int *K, float *alpha, const float *a, int *p_lda, const float *b, int *p_ldb, float *beta, float *c, int *p_ldc) /* * Purpose * ======= * * This routine computes the matrix product: * * C <- alpha * op(A) * op(B) + beta * C . * * where op(M) represents either M, M transpose, * or M conjugate transpose. * * Arguments * ========= * * order (input) enum blas_order_type * Storage of input matrices A, B, and C. * * transa (input) enum blas_trans_type * Operation to be done on matrix A before multiplication. * Can be no operation, transposition, or conjugate transposition. * * transb (input) enum blas_trans_type * Operation to be done on matrix B before multiplication. * Can be no operation, transposition, or conjugate transposition. * * m n k (input) int * The dimensions of matrices A, B, and C. * Matrix C is m-by-n matrix. * Matrix A is m-by-k if A is not transposed, * k-by-m otherwise. * Matrix B is k-by-n if B is not transposed, * n-by-k otherwise. * * alpha (input) float * * a (input) const float* * matrix A. * * lda (input) int * leading dimension of A. * * b (input) const float* * matrix B * * ldb (input) int * leading dimension of B. * * beta (input) float * * c (input/output) float* * matrix C * * ldc (input) int * leading dimension of C. * */ { /* Integer Index Variables */ int i, j, h; int ai, bj, ci; int aih, bhj, cij; /* Index into matrices a, b, c during multiply */ int incai, incaih; /* Index increments for matrix a */ int incbj, incbhj; /* Index increments for matrix b */ int incci, inccij; /* Index increments for matrix c */ /* Input Matrices */ const float *a_i = a; const float *b_i = b; /* Output Matrix */ float *c_i = c; /* Input Scalars */ float alpha_i = *alpha; float beta_i = *beta; int m=*M, n=*N, k=*K; int lda=*p_lda, ldb=*p_ldb, ldc=*p_ldc; /* Temporary Floating-Point Variables */ float a_elem; float b_elem; float c_elem; float prod; float sum; float tmp1; float tmp2; char order = COLMAJOR; /* For the time being, it is always COLMAJOR. Eventually, it might change. */ #if MDEBUG const float *A = a, *B =b, *C = c, *C1 = c; printf("In Sgemm\n"); printf("m=%d, n=%d, k=%d\n", m, n, k); printf("alpha=%f, beta=%f\n", alpha_i, beta_i); printf("\n"); /* for(i=0; i $@ ############################################################################## # compat # # Although the compat directory houses replacements for missing or erroneous # standard C functions and such sources are conditionally compiled based on # results from configure tests, without the "random" implementation the # m4-generated tests always fail for scatter and copy_patch. libga_la_SOURCES += compat/random.c ############################################################################## # ma # libga_la_SOURCES += ma/error.c libga_la_SOURCES += ma/f2c.c libga_la_SOURCES += ma/ma.c libga_la_SOURCES += ma/ma.h libga_la_SOURCES += ma/memcpy.h libga_la_SOURCES += ma/string-util.c libga_la_SOURCES += ma/table.c if ENABLE_F77 libga_la_SOURCES += ma/maf.F endif include_HEADERS += ma/macdecls.h include_HEADERS += ma/macommon.h include_HEADERS += ma/maf2c.fh include_HEADERS += ma/mafdecls.fh include_HEADERS += ma/matypes.h noinst_HEADERS += ma/scope.h noinst_HEADERS += ma/string-util.h noinst_HEADERS += ma/table.h noinst_HEADERS += ma/error.h AM_CPPFLAGS += -I$(top_build_prefix)ma AM_CPPFLAGS += -I$(top_srcdir)/ma check_PROGRAMS += ma/testc check_PROGRAMS += ma/test-coalesce check_PROGRAMS += ma/test-inquire if ENABLE_F77 check_PROGRAMS += ma/testf endif MA_SERIAL_TESTS = MA_SERIAL_TESTS_XFAIL = MA_PARALLEL_TESTS = MA_PARALLEL_TESTS_XFAIL = MA_TESTS = $(MA_SERIAL_TESTS) $(MA_PARALLEL_TESTS) MA_TESTS_XFAIL = $(MA_SERIAL_TESTS_XFAIL) $(MA_PARALLEL_TESTS_XFAIL) #MA_SERIAL_TESTS += ma/testc$(EXEEXT) # iteractive prompt MA_SERIAL_TESTS += ma/test-coalesce$(EXEEXT) MA_SERIAL_TESTS += ma/test-inquire$(EXEEXT) if ENABLE_F77 MA_SERIAL_TESTS += ma/testf$(EXEEXT) MA_SERIAL_TESTS_XFAIL += ma/testf$(EXEEXT) endif ma_testf_SOURCES = ma/testf.F ma_testc_SOURCES = ma/testc.c ma_test_coalesce_SOURCES = ma/test-coalesce.c ma_test_inquire_SOURCES = ma/test-inquire.c ############################################################################## # LinAlg/lapack+blas # # Since we gave all linalg routines a gal_ prefix, we can unconditionally # compile our internal linalg routines. # if ENABLE_F77 libga_la_SOURCES += LinAlg/lapack+blas/gal_cgemm.f libga_la_SOURCES += LinAlg/lapack+blas/gal_daxpy.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dcabs1.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dcopy.f libga_la_SOURCES += LinAlg/lapack+blas/gal_ddot.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dgemm.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dgemv.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dger.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dgetf2.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dgetrf.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dgetrs.f libga_la_SOURCES += LinAlg/lapack+blas/gal_disnan.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlacpy.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlae2.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlaev2.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlaisnan.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlamch.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlanst.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlansy.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlapy2.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlarfb.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlarf.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlarfg.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlarft.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlartg.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlascl.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlaset.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlasr.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlasrt.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlassq.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlaswp.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dlatrd.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dnrm2.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dorg2l.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dorg2r.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dorgql.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dorgqr.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dorgtr.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dpotf2.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dpotrf.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dscal.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dsteqr.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dsterf.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dswap.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dsyev.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dsygs2.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dsygst.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dsygv.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dsymm.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dsymv.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dsyr2.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dsyr2k.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dsyrk.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dsytd2.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dsytrd.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dtrmm.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dtrmv.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dtrsm.f libga_la_SOURCES += LinAlg/lapack+blas/gal_dtrsv.f libga_la_SOURCES += LinAlg/lapack+blas/gal_idamax.f libga_la_SOURCES += LinAlg/lapack+blas/gal_ieeeck.f libga_la_SOURCES += LinAlg/lapack+blas/gal_iladlc.f libga_la_SOURCES += LinAlg/lapack+blas/gal_iladlr.f libga_la_SOURCES += LinAlg/lapack+blas/gal_ilaenv.f libga_la_SOURCES += LinAlg/lapack+blas/gal_iparmq.f libga_la_SOURCES += LinAlg/lapack+blas/gal_lsame.f libga_la_SOURCES += LinAlg/lapack+blas/gal_sgemm.f libga_la_SOURCES += LinAlg/lapack+blas/gal_xerbla.f libga_la_SOURCES += LinAlg/lapack+blas/gal_zaxpy.f libga_la_SOURCES += LinAlg/lapack+blas/gal_zgemm.f endif libga_la_SOURCES += LinAlg/lapack+blas/xgemm.c libga_la_SOURCES += LinAlg/lapack+blas/xgemm.h libga_la_SOURCES += LinAlg/lapack+blas/galinalg.h libga_la_SOURCES += LinAlg/lapack+blas/galinalg.fh AM_CPPFLAGS += -I$(top_srcdir)/LinAlg/lapack+blas EXTRA_DIST += LinAlg/lapack+blas/README ############################################################################## # global # EXTRA_DIST += global/README ############################################################################## # tools # # The following to support tracing, profiling, etc. # if ENABLE_PROFILING if HAVE_SYS_WEAK_ALIAS_PRAGMA lib_LTLIBRARIES += libwapi.la libwapi_la_SOURCES = libwapi_la_SOURCES += tools/ga-wapi.c if ENABLE_GPARRAYS libwapi_la_SOURCES += tools/gp-wapi.c endif else nodist_libga_la_SOURCES += tools/ga-wapi.c if ENABLE_GPARRAYS nodist_libga_la_SOURCES += tools/gp-wapi.c endif endif endif if ENABLE_GPARRAYS EXTRA_DIST += tools/gpwapigen.py endif EXTRA_DIST += tools/wapigen.py EXTRA_DIST += tools/wapigen_counts.py EXTRA_DIST += tools/wapigen_trace.py ############################################################################## # global/src # PAPI_H = $(top_srcdir)/global/src/ga-papi.h global/src/ga-wapi.h: $(PAPI_H) @-rm -f $@ $(SED_V) \ $(SED) -e "s/PAPI/WAPI/g" \ -e "s/pnga_/wnga_/g" \ -e "/typedef/d" \ $(PAPI_H) > $@ global/src/ga-wapidefs.h: $(PAPI_H) @-rm -f $@ $(SED_V) \ $(SED) -e "s/PAPI/WAPIDEFS/g" \ -e "s/^.*p\(nga_.*\)(.*$$/#define w\1 p\1/g" \ -e "/^ /d" \ -e "/^\/\*/d" \ -e "/^ \*/d" \ -e "/typedef/d" \ $(PAPI_H) > $@ BUILT_SOURCES += global/src/ga-wapi.h BUILT_SOURCES += global/src/ga-wapidefs.h CLEANFILES += global/src/ga-wapi.h CLEANFILES += global/src/ga-wapidefs.h libga_la_SOURCES += global/src/abstract_ops.h libga_la_SOURCES += global/src/base.c libga_la_SOURCES += global/src/base.h libga_la_SOURCES += global/src/ga_iterator.h libga_la_SOURCES += global/src/ga_sparse.array.h libga_la_SOURCES += global/src/capi.c libga_la_SOURCES += global/src/cnames.h libga_la_SOURCES += global/src/collect.c libga_la_SOURCES += global/src/datatypes.c libga_la_SOURCES += global/src/decomp.c libga_la_SOURCES += global/src/diag.fh libga_la_SOURCES += global/src/DP.c libga_la_SOURCES += global/src/elem_alg.c libga_la_SOURCES += global/src/fapi.c libga_la_SOURCES += global/src/ga_ckpt.h libga_la_SOURCES += global/src/gaconfig.h libga_la_SOURCES += global/src/ga_diag_seqc.c libga_la_SOURCES += global/src/ga_malloc.c libga_la_SOURCES += global/src/ga_profile.h libga_la_SOURCES += global/src/ga_solve_seq.c libga_la_SOURCES += global/src/ga_symmetr.c libga_la_SOURCES += global/src/ga_trace.c libga_la_SOURCES += global/src/ghosts.c libga_la_SOURCES += global/src/global.h libga_la_SOURCES += global/src/global.nalg.c libga_la_SOURCES += global/src/global.npatch.c libga_la_SOURCES += global/src/global.periodic.c libga_la_SOURCES += global/src/globalp.h libga_la_SOURCES += global/src/global.util.c libga_la_SOURCES += global/src/hsort.scat.c libga_la_SOURCES += global/src/iterator.c libga_la_SOURCES += global/src/sparse.array.c libga_la_SOURCES += global/src/sketch.c libga_la_SOURCES += global/src/mtwister.h libga_la_SOURCES += global/src/mtwister.c libga_la_SOURCES += global/src/matmul.c libga_la_SOURCES += global/src/matmul.h libga_la_SOURCES += global/src/matrix.c libga_la_SOURCES += global/src/nbutil.c libga_la_SOURCES += global/src/onesided.c libga_la_SOURCES += global/src/peigstubs.c libga_la_SOURCES += global/src/scalapack.fh libga_la_SOURCES += global/src/sclstubs.c libga_la_SOURCES += global/src/select.c libga_la_SOURCES += global/src/sparse.c libga_la_SOURCES += global/src/thread-safe.c libga_la_SOURCES += global/src/types.xh libga_la_SOURCES += global/src/types2.xh nodist_libga_la_SOURCES += global/src/ga-wapidefs.h if ENABLE_F77 libga_la_SOURCES += global/src/complex.F libga_la_SOURCES += global/src/ga_diag_seq.F if ENABLE_EISPACK libga_la_SOURCES += global/src/rsg.F endif if ENABLE_PEIGS libga_la_SOURCES += global/src/ga_diag.F endif # ENABLE_PEIGS if HAVE_SCALAPACK libga_la_SOURCES += global/src/scalapack.F endif # HAVE_SCALAPACK endif # ENABLE_F77 if ENABLE_CHECKPOINT libga_la_SOURCES += global/src/ga_ckpt.c endif include_HEADERS += global/src/gacommon.h include_HEADERS += global/src/ga.h include_HEADERS += global/src/global.fh include_HEADERS += global/src/ga-papi.h if MSG_COMMS_MPI include_HEADERS += global/src/ga-mpi.h include_HEADERS += global/src/ga-mpi.fh endif nodist_include_HEADERS += global/src/ga-wapi.h AM_CPPFLAGS += -I$(top_build_prefix)global/src AM_CPPFLAGS += -I$(top_srcdir)/global/src ############################################################################## # global/testing # AM_CPPFLAGS += -I$(top_srcdir)/global/testing check_PROGRAMS += global/testing/big check_PROGRAMS += global/testing/rand check_PROGRAMS += global/testing/elempatch check_PROGRAMS += global/testing/tiled_irreg_test check_PROGRAMS += global/testing/gatscat check_PROGRAMS += global/testing/comm_init check_PROGRAMS += global/testing/getmem check_PROGRAMS += global/testing/mtest check_PROGRAMS += global/testing/mulmatpatchc check_PROGRAMS += global/testing/normc check_PROGRAMS += global/testing/matrixc check_PROGRAMS += global/testing/ntestc check_PROGRAMS += global/testing/nbtestc check_PROGRAMS += global/testing/ntestfc check_PROGRAMS += global/testing/packc check_PROGRAMS += global/testing/patch_enumc check_PROGRAMS += global/testing/perf2 check_PROGRAMS += global/testing/print check_PROGRAMS += global/testing/scan_addc check_PROGRAMS += global/testing/scan_copyc check_PROGRAMS += global/testing/sprsmatvec check_PROGRAMS += global/testing/testabstract_ops check_PROGRAMS += global/testing/testsparse check_PROGRAMS += global/testing/laplace check_PROGRAMS += global/testing/sprs_test check_PROGRAMS += global/testing/kaczmarz check_PROGRAMS += global/testing/j_iter #check_PROGRAMS += global/testing/gs_ran check_PROGRAMS += global/testing/testc check_PROGRAMS += global/testing/testmatmultc check_PROGRAMS += global/testing/testmult check_PROGRAMS += global/testing/testmultrect check_PROGRAMS += global/testing/gemmtest check_PROGRAMS += global/testing/thread_perf_contig check_PROGRAMS += global/testing/thread_perf_strided check_PROGRAMS += global/testing/threadsafec check_PROGRAMS += global/testing/read_only check_PROGRAMS += global/testing/cache_test check_PROGRAMS += global/testing/unpackc if ENABLE_F77 check_PROGRAMS += global/testing/bin check_PROGRAMS += global/testing/blktest check_PROGRAMS += global/testing/d2test check_PROGRAMS += global/testing/g2test check_PROGRAMS += global/testing/g3test check_PROGRAMS += global/testing/ga_lu check_PROGRAMS += global/testing/ga_shift check_PROGRAMS += global/testing/ghosts check_PROGRAMS += global/testing/jacobi check_PROGRAMS += global/testing/mir_perf2 check_PROGRAMS += global/testing/mmatrix check_PROGRAMS += global/testing/mulmatpatch check_PROGRAMS += global/testing/nbtest check_PROGRAMS += global/testing/nb2test check_PROGRAMS += global/testing/ndim check_PROGRAMS += global/testing/patch check_PROGRAMS += global/testing/patch2 check_PROGRAMS += global/testing/patch_enumf check_PROGRAMS += global/testing/perfmod check_PROGRAMS += global/testing/perform check_PROGRAMS += global/testing/perf check_PROGRAMS += global/testing/pg2testmatmult check_PROGRAMS += global/testing/pg2test check_PROGRAMS += global/testing/pgtestmatmult check_PROGRAMS += global/testing/pgtest check_PROGRAMS += global/testing/random check_PROGRAMS += global/testing/scan check_PROGRAMS += global/testing/simple_groups check_PROGRAMS += global/testing/sparse check_PROGRAMS += global/testing/sprsmatmult check_PROGRAMS += global/testing/stride check_PROGRAMS += global/testing/testeig check_PROGRAMS += global/testing/testmatmult check_PROGRAMS += global/testing/testsolve check_PROGRAMS += global/testing/test check_PROGRAMS += global/testing/overlay check_PROGRAMS += global/testing/test_mirrored check_PROGRAMS += global/testing/types_test check_PROGRAMS += global/testing/field_test if HAVE_SCALAPACK check_PROGRAMS += global/testing/testspd endif # HAVE_SCALAPACK if MSG_COMMS_MPI check_PROGRAMS += global/testing/simple_groups_comm endif else # !ENABLE_F77 if HAVE_LAPACK check_PROGRAMS += global/testing/ga_lu endif endif # ENABLE_F77 if MSG_COMMS_MPI check_PROGRAMS += global/testing/ga-mpi check_PROGRAMS += global/testing/lock check_PROGRAMS += global/testing/simple_groups_commc endif if SYSV check_PROGRAMS += global/testing/ipc_clean endif # TODO somehow unit tests depend on MPI -- need to fix if ENABLE_UNIT_TESTS if MSG_COMMS_MPI check_PROGRAMS += global/testing/unit-tests/ga_abs_value check_PROGRAMS += global/testing/unit-tests/ga_acc check_PROGRAMS += global/testing/unit-tests/ga_add check_PROGRAMS += global/testing/unit-tests/ga_add_constant check_PROGRAMS += global/testing/unit-tests/ga_add_constantpatch check_PROGRAMS += global/testing/unit-tests/ga_add_diagonal check_PROGRAMS += global/testing/unit-tests/ga_add_patch check_PROGRAMS += global/testing/unit-tests/ga_copy2 check_PROGRAMS += global/testing/unit-tests/ga_copy3 check_PROGRAMS += global/testing/unit-tests/ga_copy check_PROGRAMS += global/testing/unit-tests/ga_copypatch2 check_PROGRAMS += global/testing/unit-tests/ga_copypatch check_PROGRAMS += global/testing/unit-tests/ga_create1 check_PROGRAMS += global/testing/unit-tests/ga_create2 check_PROGRAMS += global/testing/unit-tests/ga_create3 check_PROGRAMS += global/testing/unit-tests/ga_create check_PROGRAMS += global/testing/unit-tests/ga_create_handle check_PROGRAMS += global/testing/unit-tests/ga_create_irreg2 check_PROGRAMS += global/testing/unit-tests/ga_create_irreg3 check_PROGRAMS += global/testing/unit-tests/ga_create_irreg check_PROGRAMS += global/testing/unit-tests/ga_destroy check_PROGRAMS += global/testing/unit-tests/ga_dgop check_PROGRAMS += global/testing/unit-tests/ga_dot check_PROGRAMS += global/testing/unit-tests/ga_duplicate check_PROGRAMS += global/testing/unit-tests/ga_elem_divide check_PROGRAMS += global/testing/unit-tests/ga_elem_dividepatch check_PROGRAMS += global/testing/unit-tests/ga_elem_maximum check_PROGRAMS += global/testing/unit-tests/ga_elem_maximumpatch check_PROGRAMS += global/testing/unit-tests/ga_elem_minimum check_PROGRAMS += global/testing/unit-tests/ga_elem_minimumpatch check_PROGRAMS += global/testing/unit-tests/ga_elem_multiply check_PROGRAMS += global/testing/unit-tests/ga_elem_multiplypatch check_PROGRAMS += global/testing/unit-tests/ga_fill check_PROGRAMS += global/testing/unit-tests/ga_fillpatch1 check_PROGRAMS += global/testing/unit-tests/ga_fillpatch check_PROGRAMS += global/testing/unit-tests/ga_gather2 check_PROGRAMS += global/testing/unit-tests/ga_gather3 check_PROGRAMS += global/testing/unit-tests/ga_gather check_PROGRAMS += global/testing/unit-tests/ga_get_blockinfo check_PROGRAMS += global/testing/unit-tests/ga_get check_PROGRAMS += global/testing/unit-tests/ga_get_diagonal check_PROGRAMS += global/testing/unit-tests/ga_igop2 check_PROGRAMS += global/testing/unit-tests/ga_igop check_PROGRAMS += global/testing/unit-tests/ga_inquire check_PROGRAMS += global/testing/unit-tests/ga_intialize check_PROGRAMS += global/testing/unit-tests/ga_lgop check_PROGRAMS += global/testing/unit-tests/ga_median check_PROGRAMS += global/testing/unit-tests/ga_ndim2 check_PROGRAMS += global/testing/unit-tests/ga_ndim check_PROGRAMS += global/testing/unit-tests/ga_nnodes check_PROGRAMS += global/testing/unit-tests/ga_nodeid check_PROGRAMS += global/testing/unit-tests/ga_pgroup_create2 check_PROGRAMS += global/testing/unit-tests/ga_pgroup_create3 check_PROGRAMS += global/testing/unit-tests/ga_pgroup_create4 check_PROGRAMS += global/testing/unit-tests/ga_pgroup_create5 check_PROGRAMS += global/testing/unit-tests/ga_pgroup_create check_PROGRAMS += global/testing/unit-tests/ga_pgroup_destroy2 check_PROGRAMS += global/testing/unit-tests/ga_pgroup_destroy check_PROGRAMS += global/testing/unit-tests/ga_pgroup_nnodes_nodeid check_PROGRAMS += global/testing/unit-tests/ga_pgroup_setdefault check_PROGRAMS += global/testing/unit-tests/ga_put2 check_PROGRAMS += global/testing/unit-tests/ga_put check_PROGRAMS += global/testing/unit-tests/ga_scale2 check_PROGRAMS += global/testing/unit-tests/ga_scale check_PROGRAMS += global/testing/unit-tests/ga_scale_cols check_PROGRAMS += global/testing/unit-tests/ga_scale_patch check_PROGRAMS += global/testing/unit-tests/ga_scale_rows check_PROGRAMS += global/testing/unit-tests/ga_scatter check_PROGRAMS += global/testing/unit-tests/ga_set_data check_PROGRAMS += global/testing/unit-tests/ga_set_diagonal check_PROGRAMS += global/testing/unit-tests/ga_set_restricted check_PROGRAMS += global/testing/unit-tests/ga_solve check_PROGRAMS += global/testing/unit-tests/ga_sync check_PROGRAMS += global/testing/unit-tests/ga_transpose2 check_PROGRAMS += global/testing/unit-tests/ga_transpose3 check_PROGRAMS += global/testing/unit-tests/ga_transpose check_PROGRAMS += global/testing/unit-tests/ga_zero check_PROGRAMS += global/testing/unit-tests/ga_zerodiagonal check_PROGRAMS += global/testing/unit-tests/ga_zeropatch2 check_PROGRAMS += global/testing/unit-tests/ga_zeropatch endif endif GLOBAL_SERIAL_TESTS = GLOBAL_SERIAL_TESTS_XFAIL = GLOBAL_PARALLEL_TESTS = GLOBAL_PARALLEL_TESTS_XFAIL = GLOBAL_THREADED_TESTS = GLOBAL_TESTS = $(GLOBAL_SERIAL_TESTS) $(GLOBAL_PARALLEL_TESTS) GLOBAL_TESTS_XFAIL = $(GLOBAL_SERIAL_TESTS_XFAIL) $(GLOBAL_PARALLEL_TESTS_XFAIL) TRAVIS_TESTS = if ENABLE_F77 TRAVIS_TESTS += global/testing/test$(EXEEXT) else TRAVIS_TESTS += global/testing/testc$(EXEEXT) endif #GLOBAL_PARALLEL_TESTS += global/testing/big$(EXEEXT) # needs lots of memory GLOBAL_PARALLEL_TESTS += global/testing/elempatch$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/tiled_irreg_test$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/gatscat$(EXEEXT) # broken GLOBAL_PARALLEL_TESTS += global/testing/comm_init$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/getmem$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/rand$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/mtest$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/mulmatpatchc$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/normc$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/matrixc$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/ntestc$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/nbtestc$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/ntestfc$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/packc$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/patch_enumc$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/print$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/scan_addc$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/scan_copyc$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/testc$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/laplace$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/sprs_test$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/kaczmarz$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/j_iter$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/gs_ran$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/testmatmultc$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/testmult$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/testmultrect$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/gemmtest$(EXEEXT) GLOBAL_THREADED_TESTS += global/testing/thread_perf_contig$(EXEEXT) GLOBAL_THREADED_TESTS += global/testing/thread_perf_strided$(EXEEXT) GLOBAL_THREADED_TESTS += global/testing/threadsafec$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/read_only$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/cache_test$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/unpackc$(EXEEXT) if ENABLE_F77 GLOBAL_PARALLEL_TESTS += global/testing/bin$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/blktest$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/d2test$(EXEEXT) # needs input file GLOBAL_PARALLEL_TESTS += global/testing/g2test$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/g3test$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/ga_lu$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/ga_shift$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/ghosts$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/jacobi$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/mir_perf2$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/mmatrix$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/mulmatpatch$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/nbtest$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/nb2test$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/ndim$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/patch$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/patch2$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/patch_enumf$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/perfmod$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/perform$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/perf$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/perf2$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/pg2testmatmult$(EXEEXT) # needs 8 procs exactly GLOBAL_PARALLEL_TESTS += global/testing/pg2test$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/pgtestmatmult$(EXEEXT) # needs 8 procs exactly GLOBAL_PARALLEL_TESTS += global/testing/pgtest$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/random$(EXEEXT) # takes too long GLOBAL_PARALLEL_TESTS += global/testing/scan$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/simple_groups$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/sparse$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/sprsmatmult$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/stride$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/testeig$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/testmatmult$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/testsolve$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/test$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/overlay$(EXEEXT) if HAVE_SCALAPACK GLOBAL_PARALLEL_TESTS += global/testing/testspd$(EXEEXT) endif if MSG_COMMS_MPI GLOBAL_PARALLEL_TESTS += global/testing/simple_groups_comm$(EXEEXT) endif else if HAVE_LAPACK GLOBAL_PARALLEL_TESTS += global/testing/ga_lu$(EXEEXT) endif endif if MSG_COMMS_MPI GLOBAL_PARALLEL_TESTS += global/testing/ga-mpi$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/lock$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/simple_groups_commc$(EXEEXT) endif if SYSV #GLOBAL_PARALLEL_TESTS += global/testing/ipc_clean$(EXEEXT) endif #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_abs_value$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_acc$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_add$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_add_constant$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_add_constantpatch$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_add_diagonal$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_add_patch$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_copy2$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_copy3$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_copy$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_copypatch2$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_copypatch$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_create1$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_create2$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_create3$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_create$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_create_handle$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_create_irreg2$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_create_irreg3$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_create_irreg$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_destroy$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_dgop$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_dot$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_duplicate$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_elem_divide$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_elem_dividepatch$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_elem_maximum$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_elem_maximumpatch$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_elem_minimum$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_elem_minimumpatch$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_elem_multiply$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_elem_multiplypatch$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_fill$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_fillpatch1$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_fillpatch$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_gather2$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_gather3$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_gather$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_get_blockinfo$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_get$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_get_diagonal$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_igop2$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_igop$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_inquire$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_intialize$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_lgop$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_median$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_ndim2$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_ndim$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_nnodes$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_nodeid$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_pgroup_create2$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_pgroup_create3$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_pgroup_create4$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_pgroup_create5$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_pgroup_create$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_pgroup_destroy2$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_pgroup_destroy$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_pgroup_nnodes_nodeid$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_pgroup_setdefault$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_put2$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_put$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_scale2$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_scale$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_scale_cols$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_scale_patch$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_scale_rows$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_scatter$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_set_data$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_set_diagonal$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_set_restricted$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_solve$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_sync$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_transpose2$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_transpose3$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_transpose$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_zero$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_zerodiagonal$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_zeropatch2$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/testing/unit-tests/ga_zeropatch$(EXEEXT) if HAVE_M4 GLOBAL_TESTING_M4_DEPS = GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_GA_FILL.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_main.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_NGA_ACC.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_NGA_ADD_PATCH.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_NGA_COPY_PATCH.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_NGA_DOT_PATCH.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_NGA_FILL_PATCH.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_NGA_GATHER.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_NGA_GET.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_NGA_PERIODIC_ACC.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_NGA_PERIODIC_GET.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_NGA_PERIODIC_PUT.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_NGA_PUT.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_NGA_SCALE_PATCH.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_NGA_SCATTER_ACC.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_NGA_SCATTER.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_util_comm.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ndim_util.src GLOBAL_TESTING_M4_DEPS += global/testing/ngatest_src/ngatest.def GLOBAL_TESTING_M4_DEPS += global/testing/nga-onesided.m4 GLOBAL_TESTING_M4_DEPS += global/testing/nga-patch.m4 GLOBAL_TESTING_M4_DEPS += global/testing/nga-periodic.m4 GLOBAL_TESTING_M4_DEPS += global/testing/nga-scatter.m4 GLOBAL_TESTING_M4_DEPS += global/testing/ngatest.m4 GLOBAL_TESTING_M4_DEPS += global/testing/nga-util.m4 EXTRA_DIST += $(GLOBAL_TESTING_M4_DEPS) EXTRA_DIST += global/testing/README global/testing/nga-onesided.F: $(GLOBAL_TESTING_M4_DEPS) global/testing/nga-patch.F: $(GLOBAL_TESTING_M4_DEPS) global/testing/nga-periodic.F: $(GLOBAL_TESTING_M4_DEPS) global/testing/nga-scatter.F: $(GLOBAL_TESTING_M4_DEPS) global/testing/nga-util.F: $(GLOBAL_TESTING_M4_DEPS) global/testing/ngatest.F: $(GLOBAL_TESTING_M4_DEPS) CLEANFILES += global/testing/nga-onesided.F CLEANFILES += global/testing/nga-patch.F CLEANFILES += global/testing/nga-periodic.F CLEANFILES += global/testing/nga-scatter.F CLEANFILES += global/testing/nga-util.F CLEANFILES += global/testing/ngatest.F if ENABLE_F77 check_PROGRAMS += global/testing/nga-onesided check_PROGRAMS += global/testing/nga-patch check_PROGRAMS += global/testing/nga-periodic check_PROGRAMS += global/testing/nga-scatter check_PROGRAMS += global/testing/nga-util check_PROGRAMS += global/testing/ngatest endif if ENABLE_F77 GLOBAL_PARALLEL_TESTS += global/testing/nga-onesided$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/nga-patch$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/nga-periodic$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/nga-scatter$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/nga-scatter$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/nga-util$(EXEEXT) GLOBAL_PARALLEL_TESTS += global/testing/ngatest$(EXEEXT) endif .m4.F: $(M4_V) \ $(M4) -I$(top_srcdir)/global/testing $< > $@ endif if ENABLE_F77 gtsrcf = gtsrcf += global/testing/testutil.fh gtsrcf += global/testing/ffflush.F gtsrcf += global/testing/util.c testblassrc = global/testing/testblas.F endif global_testing_big_SOURCES = global/testing/big.c global_testing_bin_SOURCES = global/testing/bin.F $(gtsrcf) global_testing_blktest_SOURCES = global/testing/blktest.F $(gtsrcf) global_testing_d2test_SOURCES = global/testing/d2test.F $(gtsrcf) global_testing_elempatch_SOURCES = global/testing/elempatch.c global_testing_tiled_irreg_test_SOURCES = global/testing/tiled_irreg_test.c global_testing_field_test_SOURCES = global/testing/field-test.F $(gtsrcf) global_testing_g2test_SOURCES = global/testing/g2test.F $(gtsrcf) global_testing_g3test_SOURCES = global/testing/g3test.F $(gtsrcf) global_testing_ga_lu_SOURCES = global/testing/ga_lu.c global_testing_ga_mpi_SOURCES = global/testing/ga-mpi.c global_testing_ga_shift_SOURCES = global/testing/ga_shift.F $(gtsrcf) global_testing_gatscat_SOURCES = global/testing/gatscat.c global_testing_comm_init_SOURCES = global/testing/comm_init.c global_testing_getmem_SOURCES = global/testing/getmem.c global_testing_ghosts_SOURCES = global/testing/ghosts.F $(gtsrcf) global_testing_ipc_clean_SOURCES = global/testing/ipc.clean.c global_testing_jacobi_SOURCES = global/testing/jacobi.F $(gtsrcf) global_testing_lock_SOURCES = global/testing/lock.c global_testing_mir_perf2_SOURCES = global/testing/mir_perf2.F $(gtsrcf) global_testing_mmatrix_SOURCES = global/testing/mmatrix.F $(gtsrcf) global_testing_mtest_SOURCES = global/testing/mtest.c global_testing_mulmatpatch_SOURCES = global/testing/mulmatpatch.F $(gtsrcf) $(testblassrc) global_testing_mulmatpatchc_SOURCES = global/testing/mulmatpatchc.c global_testing_nbtest_SOURCES = global/testing/nbtest.F $(gtsrcf) global_testing_nb2test_SOURCES = global/testing/nb2test.F $(gtsrcf) global_testing_ndim_SOURCES = global/testing/ndim.F $(gtsrcf) global_testing_normc_SOURCES = global/testing/normc.c global_testing_matrixc_SOURCES = global/testing/matrixc.c global_testing_ntestc_SOURCES = global/testing/ntestc.c global_testing_nbtestc_SOURCES = global/testing/nbtestc.c global_testing_ntestfc_SOURCES = global/testing/ntestfc.c global_testing_packc_SOURCES = global/testing/packc.c global_testing_patch_SOURCES = global/testing/patch.F $(gtsrcf) $(testblassrc) global_testing_patch2_SOURCES = global/testing/patch2.F $(gtsrcf) global_testing_patch_enumc_SOURCES = global/testing/patch_enumc.c global_testing_patch_enumf_SOURCES = global/testing/patch_enumf.F $(gtsrcf) global_testing_perf_SOURCES = global/testing/perf.F $(gtsrcf) global_testing_perf2_SOURCES = global/testing/perf2.c global_testing_perfmod_SOURCES = global/testing/perfmod.F $(gtsrcf) global_testing_perform_SOURCES = global/testing/perform.F $(gtsrcf) global_testing_pg2test_SOURCES = global/testing/pg2test.F $(gtsrcf) global_testing_pg2testmatmult_SOURCES = global/testing/pg2testmatmult.F $(gtsrcf) $(testblassrc) global_testing_pgtest_SOURCES = global/testing/pgtest.F $(gtsrcf) global_testing_pgtestmatmult_SOURCES = global/testing/pgtestmatmult.F $(gtsrcf) global_testing_print_SOURCES = global/testing/print.c global_testing_random_SOURCES = global/testing/random.F $(gtsrcf) global_testing_scan_SOURCES = global/testing/scan.F $(gtsrcf) global_testing_rand_SOURCES = global/testing/rand.c global_testing_scan_addc_SOURCES = global/testing/scan_addc.c global_testing_scan_copyc_SOURCES = global/testing/scan_copyc.c global_testing_sprsmatvec_SOURCES = global/testing/sprsmatvec.c global_testing_testsparse_SOURCES = global/testing/testsparse.c global_testing_laplace_SOURCES = global/testing/laplace.c global_testing_sprs_test_SOURCES = global/testing/sprs_test.c global_testing_kaczmarz_SOURCES = global/testing/kaczmarz.c global_testing_j_iter_SOURCES = global/testing/j_iter.c #global_testing_gs_ran_SOURCES = global/testing/gs_ran.c global_testing_simple_groups_SOURCES = global/testing/simple_groups.F $(gtsrcf) global_testing_simple_groups_comm_SOURCES = global/testing/simple_groups_comm.F $(gtsrcf) global_testing_simple_groups_commc_SOURCES = global/testing/simple_groups_commc.c global_testing_sparse_SOURCES = global/testing/sparse.F $(gtsrcf) global_testing_sprsmatmult_SOURCES = global/testing/sprsmatmult.F $(gtsrcf) global_testing_stride_SOURCES = global/testing/stride.F $(gtsrcf) global_testing_testabstract_ops_SOURCES = global/testing/testabstract_ops.c global_testing_test_SOURCES = global/testing/test.F $(gtsrcf) global_testing_test_mirrored_SOURCES = global/testing/test.F $(gtsrcf) global_testing_overlay_SOURCES = global/testing/overlay.F $(gtsrcf) global_testing_testc_SOURCES = global/testing/testc.c global_testing_testeig_SOURCES = global/testing/testeig.F $(gtsrcf) global_testing_testmatmult_SOURCES = global/testing/testmatmult.F $(gtsrcf) global_testing_testmatmultc_SOURCES = global/testing/testmatmultc.c global_testing_testmult_SOURCES = global/testing/testmult.c global_testing_testmultrect_SOURCES = global/testing/testmultrect.c global_testing_gemmtest_SOURCES = global/testing/gemmtest.c global_testing_testsolve_SOURCES = global/testing/testsolve.F $(gtsrcf) global_testing_testspd_SOURCES = global/testing/testspd.F $(gtsrcf) global_testing_thread_perf_contig_SOURCES = global/testing/thread_perf_contig.c global_testing_thread_perf_strided_SOURCES = global/testing/thread_perf_strided.c global_testing_threadsafec_SOURCES = global/testing/threadsafec.c global_testing_types_test_SOURCES = global/testing/types-test.F $(gtsrcf) global_testing_unpackc_SOURCES = global/testing/unpackc.c global_testing_cache_test_SOURCES = global/testing/cache_test.c nodist_global_testing_nga_onesided_SOURCES = global/testing/nga-onesided.F $(gtsrcf) nodist_global_testing_nga_patch_SOURCES = global/testing/nga-patch.F $(gtsrcf) nodist_global_testing_nga_periodic_SOURCES = global/testing/nga-periodic.F $(gtsrcf) nodist_global_testing_nga_scatter_SOURCES = global/testing/nga-scatter.F $(gtsrcf) nodist_global_testing_nga_util_SOURCES = global/testing/nga-util.F $(gtsrcf) nodist_global_testing_ngatest_SOURCES = global/testing/ngatest.F $(gtsrcf) UNIT_TEST_EXTRA_SRC = UNIT_TEST_EXTRA_SRC += global/testing/unit-tests/mock.c UNIT_TEST_EXTRA_SRC += global/testing/unit-tests/mock.h global_testing_unit_tests_ga_abs_value_SOURCES = global/testing/unit-tests/ga_abs_value.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_acc_SOURCES = global/testing/unit-tests/ga_acc.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_add_SOURCES = global/testing/unit-tests/ga_add.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_add_constant_SOURCES = global/testing/unit-tests/ga_add_constant.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_add_constantpatch_SOURCES = global/testing/unit-tests/ga_add_constantpatch.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_add_diagonal_SOURCES = global/testing/unit-tests/ga_add_diagonal.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_add_patch_SOURCES = global/testing/unit-tests/ga_add_patch.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_copy2_SOURCES = global/testing/unit-tests/ga_copy2.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_copy3_SOURCES = global/testing/unit-tests/ga_copy3.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_copy_SOURCES = global/testing/unit-tests/ga_copy.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_copypatch2_SOURCES = global/testing/unit-tests/ga_copypatch2.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_copypatch_SOURCES = global/testing/unit-tests/ga_copypatch.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_create1_SOURCES = global/testing/unit-tests/ga_create1.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_create2_SOURCES = global/testing/unit-tests/ga_create2.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_create3_SOURCES = global/testing/unit-tests/ga_create3.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_create_SOURCES = global/testing/unit-tests/ga_create.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_create_handle_SOURCES = global/testing/unit-tests/ga_create_handle.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_create_irreg2_SOURCES = global/testing/unit-tests/ga_create_irreg2.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_create_irreg3_SOURCES = global/testing/unit-tests/ga_create_irreg3.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_create_irreg_SOURCES = global/testing/unit-tests/ga_create_irreg.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_destroy_SOURCES = global/testing/unit-tests/ga_destroy.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_dgop_SOURCES = global/testing/unit-tests/ga_dgop.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_dot_SOURCES = global/testing/unit-tests/ga_dot.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_duplicate_SOURCES = global/testing/unit-tests/ga_duplicate.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_elem_divide_SOURCES = global/testing/unit-tests/ga_elem_divide.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_elem_dividepatch_SOURCES = global/testing/unit-tests/ga_elem_dividepatch.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_elem_maximum_SOURCES = global/testing/unit-tests/ga_elem_maximum.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_elem_maximumpatch_SOURCES = global/testing/unit-tests/ga_elem_maximumpatch.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_elem_minimum_SOURCES = global/testing/unit-tests/ga_elem_minimum.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_elem_minimumpatch_SOURCES = global/testing/unit-tests/ga_elem_minimumpatch.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_elem_multiply_SOURCES = global/testing/unit-tests/ga_elem_multiply.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_elem_multiplypatch_SOURCES = global/testing/unit-tests/ga_elem_multiplypatch.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_fill_SOURCES = global/testing/unit-tests/ga_fill.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_fillpatch1_SOURCES = global/testing/unit-tests/ga_fillpatch1.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_fillpatch_SOURCES = global/testing/unit-tests/ga_fillpatch.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_gather2_SOURCES = global/testing/unit-tests/ga_gather2.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_gather3_SOURCES = global/testing/unit-tests/ga_gather3.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_gather_SOURCES = global/testing/unit-tests/ga_gather.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_get_blockinfo_SOURCES = global/testing/unit-tests/ga_get_blockinfo.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_get_SOURCES = global/testing/unit-tests/ga_get.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_get_diagonal_SOURCES = global/testing/unit-tests/ga_get_diagonal.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_igop2_SOURCES = global/testing/unit-tests/ga_igop2.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_igop_SOURCES = global/testing/unit-tests/ga_igop.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_inquire_SOURCES = global/testing/unit-tests/ga_inquire.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_intialize_SOURCES = global/testing/unit-tests/ga_intialize.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_lgop_SOURCES = global/testing/unit-tests/ga_lgop.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_median_SOURCES = global/testing/unit-tests/ga_median.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_ndim2_SOURCES = global/testing/unit-tests/ga_ndim2.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_ndim_SOURCES = global/testing/unit-tests/ga_ndim.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_nnodes_SOURCES = global/testing/unit-tests/ga_nnodes.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_nodeid_SOURCES = global/testing/unit-tests/ga_nodeid.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_pgroup_create2_SOURCES = global/testing/unit-tests/ga_pgroup_create2.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_pgroup_create3_SOURCES = global/testing/unit-tests/ga_pgroup_create3.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_pgroup_create4_SOURCES = global/testing/unit-tests/ga_pgroup_create4.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_pgroup_create5_SOURCES = global/testing/unit-tests/ga_pgroup_create5.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_pgroup_create_SOURCES = global/testing/unit-tests/ga_pgroup_create.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_pgroup_destroy2_SOURCES = global/testing/unit-tests/ga_pgroup_destroy2.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_pgroup_destroy_SOURCES = global/testing/unit-tests/ga_pgroup_destroy.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_pgroup_nnodes_nodeid_SOURCES = global/testing/unit-tests/ga_pgroup_nnodes_nodeid.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_pgroup_setdefault_SOURCES = global/testing/unit-tests/ga_pgroup_setdefault.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_put2_SOURCES = global/testing/unit-tests/ga_put2.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_put_SOURCES = global/testing/unit-tests/ga_put.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_scale2_SOURCES = global/testing/unit-tests/ga_scale2.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_scale_SOURCES = global/testing/unit-tests/ga_scale.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_scale_cols_SOURCES = global/testing/unit-tests/ga_scale_cols.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_scale_patch_SOURCES = global/testing/unit-tests/ga_scale_patch.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_scale_rows_SOURCES = global/testing/unit-tests/ga_scale_rows.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_scatter_SOURCES = global/testing/unit-tests/ga_scatter.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_set_data_SOURCES = global/testing/unit-tests/ga_set_data.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_set_diagonal_SOURCES = global/testing/unit-tests/ga_set_diagonal.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_set_restricted_SOURCES = global/testing/unit-tests/ga_set_restricted.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_solve_SOURCES = global/testing/unit-tests/ga_solve.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_sync_SOURCES = global/testing/unit-tests/ga_sync.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_transpose2_SOURCES = global/testing/unit-tests/ga_transpose2.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_transpose3_SOURCES = global/testing/unit-tests/ga_transpose3.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_transpose_SOURCES = global/testing/unit-tests/ga_transpose.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_zero_SOURCES = global/testing/unit-tests/ga_zero.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_zerodiagonal_SOURCES = global/testing/unit-tests/ga_zerodiagonal.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_zeropatch2_SOURCES = global/testing/unit-tests/ga_zeropatch2.c $(UNIT_TEST_EXTRA_SRC) global_testing_unit_tests_ga_zeropatch_SOURCES = global/testing/unit-tests/ga_zeropatch.c $(UNIT_TEST_EXTRA_SRC) global_testing_testabstract_ops_CFLAGS = $(AM_CFLAGS) $(OPENMP_CFLAGS) global_testing_thread_perf_contig_CFLAGS = $(AM_CFLAGS) $(OPENMP_CFLAGS) global_testing_thread_perf_strided_CFLAGS = $(AM_CFLAGS) $(OPENMP_CFLAGS) global_testing_threadsafec_CFLAGS = $(AM_CFLAGS) $(OPENMP_CFLAGS) global_testing_read_only_CFLAGS = $(AM_CFLAGS) $(OPENMP_CFLAGS) global_testing_testabstract_ops_CPPFLAGS = $(AM_CPPFLAGS) $(OPENMP_CFLAGS) global_testing_thread_perf_contig_CPPFLAGS = $(AM_CPPFLAGS) $(OPENMP_CFLAGS) global_testing_thread_perf_strided_CPPFLAGS = $(AM_CPPFLAGS) $(OPENMP_CFLAGS) global_testing_threadsafec_CPPFLAGS = $(AM_CPPFLAGS) $(OPENMP_CFLAGS) global_testing_read_only_CPPFLAGS = $(AM_CPPFLAGS) $(OPENMP_CFLAGS) global_testing_testabstract_ops_LDFLAGS = $(AM_LDFLAGS) $(OPENMP_CFLAGS) global_testing_thread_perf_contig_LDFLAGS = $(AM_LDFLAGS) $(OPENMP_CFLAGS) global_testing_thread_perf_strided_LDFLAGS= $(AM_LDFLAGS) $(OPENMP_CFLAGS) global_testing_threadsafec_LDFLAGS = $(AM_LDFLAGS) $(OPENMP_CFLAGS) global_testing_read_only_LDFLAGS = $(AM_LDFLAGS) $(OPENMP_CFLAGS) if F77_INTEL_NO_INLINE global_testing_nga_scatter_FFLAGS = $(AM_FFLAGS) -fno-inline global_testing_nga_onesided_FFLAGS = $(AM_FFLAGS) -fno-inline else global_testing_nga_scatter_FFLAGS = $(AM_FFLAGS) global_testing_nga_onesided_FFLAGS = $(AM_FFLAGS) endif global_testing_test_mirrored_CPPFLAGS = $(AM_CPPFLAGS) -DMIRROR=1 ############################################################################## # global/examples # check_PROGRAMS += global/examples/lennard-jones/lennard if ENABLE_F77 check_PROGRAMS += global/examples/boltzmann/boltz check_PROGRAMS += global/examples/conjugate_gradient/ga_cg check_PROGRAMS += global/examples/md_cluster/grp_sim check_PROGRAMS += global/examples/scf/scf endif GLOBAL_PARALLEL_TESTS += global/examples/lennard-jones/lennard$(EXEEXT) if ENABLE_F77 GLOBAL_PARALLEL_TESTS += global/examples/boltzmann/boltz$(EXEEXT) #GLOBAL_PARALLEL_TESTS += global/examples/conjugate_gradient/ga_cg$(EXEEXT) # needs input #GLOBAL_PARALLEL_TESTS += global/examples/md_cluster/grp_sim$(EXEEXT) # needs input #GLOBAL_PARALLEL_TESTS += global/examples/scf/scf$(EXEEXT) # needs input endif global_examples_boltzmann_boltz_SOURCES = \ global/examples/boltzmann/boltzmann.F \ global/examples/boltzmann/common \ global/examples/boltzmann/equil.F \ global/examples/boltzmann/get_patch.F \ global/examples/boltzmann/initpar.F \ global/examples/boltzmann/main.F \ global/examples/boltzmann/printdat.F \ global/examples/boltzmann/properties.F \ global/examples/boltzmann/setup.F \ global/examples/boltzmann/timestep.F \ global/examples/boltzmann/vorticity.F global_examples_boltzmann_boltz_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/global/examples/boltzmann global_examples_conjugate_gradient_ga_cg_SOURCES = \ global/examples/conjugate_gradient/computeloops.F \ global/examples/conjugate_gradient/finclude.h \ global/examples/conjugate_gradient/ga_cg.c \ global/examples/conjugate_gradient/matvecmul.c \ global/examples/conjugate_gradient/read_input.c global_examples_conjugate_gradient_ga_cg_LDFLAGS = $(AM_LDFLAGS) $(FLD_NOMAIN) global_examples_lennard_jones_lennard_SOURCES = \ global/examples/lennard-jones/lennard.c global_examples_md_cluster_grp_sim_SOURCES = \ global/examples/md_cluster/cell_list.F \ global/examples/md_cluster/cl_sim.F \ global/examples/md_cluster/cluster.F \ global/examples/md_cluster/common.fh \ global/examples/md_cluster/estats.F \ global/examples/md_cluster/factor.F \ global/examples/md_cluster/force.F \ global/examples/md_cluster/gather.F \ global/examples/md_cluster/grp_sim.F \ global/examples/md_cluster/hash.F \ global/examples/md_cluster/heapsort.F \ global/examples/md_cluster/kin.F \ global/examples/md_cluster/mdinit.F \ global/examples/md_cluster/mdout.F \ global/examples/md_cluster/mdstep.F \ global/examples/md_cluster/newcfg.F \ global/examples/md_cluster/nextc.F \ global/examples/md_cluster/pairs.F \ global/examples/md_cluster/ran3.F \ global/examples/md_cluster/rdpar.F \ global/examples/md_cluster/scatter.F \ global/examples/md_cluster/shuffle.F \ global/examples/md_cluster/sort.F \ global/examples/md_cluster/tstats.F \ global/examples/md_cluster/update.F global_examples_scf_scf_SOURCES = \ global/examples/scf/cscf.h \ global/examples/scf/input.F \ global/examples/scf/integ.F \ global/examples/scf/output.F \ global/examples/scf/scf.F \ global/examples/scf/timer.F EXTRA_DIST += global/examples/boltzmann/README EXTRA_DIST += global/examples/lennard-jones/README EXTRA_DIST += global/examples/md_cluster/atom.inp EXTRA_DIST += global/examples/scf/be16.inpt EXTRA_DIST += global/examples/scf/be.inpt EXTRA_DIST += global/examples/scf/ft-scf.F EXTRA_DIST += global/examples/scf/README ############################################################################## # global/X # if ENABLE_XREGION if NO_X else bin_PROGRAMS += global/X/xregion global_X_xregion_SOURCES = \ global/X/xregion_colormap.c \ global/X/xregion_dialog.c \ global/X/xregion_display.c \ global/X/xregion_fileio.c \ global/X/xregion_main.c \ global/X/xregion_overview.c \ global/X/xregion_pixregion.c \ global/X/xregion_scrollbars.c \ global/X/xregion_util.c \ global/X/xregion_view.c \ global/X/xregion_xcmap.c global_X_xregion_CPPFLAGS = $(X_CPPFLAGS) global_X_xregion_LDFLAGS = $(X_LDFLAGS) global_X_xregion_LDADD = -lXaw -lXmu -lXt -lX11 -lXau -lXdmcp -lXext endif endif EXTRA_DIST += global/X/README ############################################################################## # global/trace # bin_PROGRAMS += global/trace/adjust bin_PROGRAMS += global/trace/collisions global_trace_adjust_SOURCES = global/trace/adjust.c global_trace_adjust_LDFLAGS = global_trace_adjust_LDADD = global_trace_collisions_SOURCES = global/trace/collisions.c global_trace_collisions_LDFLAGS = global_trace_collisions_LDADD = EXTRA_DIST += global/trace/README EXTRA_DIST += global/trace/test.f ############################################################################## # global/doc # EXTRA_DIST += global/doc/README EXTRA_DIST += global/doc/Supercomputing94.pdf EXTRA_DIST += global/doc/user.pdf ############################################################################## # pario # libga_la_SOURCES += pario/dra/buffers.c libga_la_SOURCES += pario/dra/buffers.h libga_la_SOURCES += pario/dra/capi.c libga_la_SOURCES += pario/dra/disk.arrays.c libga_la_SOURCES += pario/dra/disk.param.c libga_la_SOURCES += pario/dra/draf2c.h libga_la_SOURCES += pario/dra/drap.h libga_la_SOURCES += pario/dra/env.c libga_la_SOURCES += pario/dra/fortran.c libga_la_SOURCES += pario/dra/patch.util.c libga_la_SOURCES += pario/eaf/eaf.c libga_la_SOURCES += pario/eaf/eaf_f2c.c libga_la_SOURCES += pario/eaf/eafP.h libga_la_SOURCES += pario/elio/elio.c libga_la_SOURCES += pario/elio/eliop.h libga_la_SOURCES += pario/elio/pablo.h libga_la_SOURCES += pario/elio/stat.c libga_la_SOURCES += pario/sf/sf_capi.c libga_la_SOURCES += pario/sf/sff2c.h libga_la_SOURCES += pario/sf/sf_fortran.c libga_la_SOURCES += pario/sf/shared.files.c include_HEADERS += pario/dra/dra.fh include_HEADERS += pario/dra/dra.h include_HEADERS += pario/eaf/eaf.fh include_HEADERS += pario/eaf/eaf.h include_HEADERS += pario/elio/chemio.h include_HEADERS += pario/elio/elio.h include_HEADERS += pario/sf/coms.h include_HEADERS += pario/sf/sf.fh include_HEADERS += pario/sf/sf.h AM_CPPFLAGS += -I$(top_srcdir)/pario/dra AM_CPPFLAGS += -I$(top_srcdir)/pario/eaf AM_CPPFLAGS += -I$(top_srcdir)/pario/elio AM_CPPFLAGS += -I$(top_srcdir)/pario/sf EXTRA_DIST += pario/dra/dbg_read.c EXTRA_DIST += pario/dra/dbg_write.c EXTRA_DIST += pario/dra/dra2arviz.c EXTRA_DIST += pario/dra/global.unsup.c if ENABLE_F77 check_PROGRAMS += pario/dra/big check_PROGRAMS += pario/dra/dra_mxm check_PROGRAMS += pario/dra/ntest check_PROGRAMS += pario/dra/perf check_PROGRAMS += pario/dra/perf2 check_PROGRAMS += pario/dra/perf3 check_PROGRAMS += pario/dra/test check_PROGRAMS += pario/dra/test_mxm check_PROGRAMS += pario/dra/time_mxm check_PROGRAMS += pario/eaf/test check_PROGRAMS += pario/sf/test endif check_PROGRAMS += pario/dra/bign check_PROGRAMS += pario/dra/dbg_read check_PROGRAMS += pario/dra/dbg_write check_PROGRAMS += pario/dra/dra2arviz check_PROGRAMS += pario/dra/ntestc check_PROGRAMS += pario/dra/perfn check_PROGRAMS += pario/dra/rate PARIO_SERIAL_TESTS = PARIO_SERIAL_TESTS_XFAIL = PARIO_PARALLEL_TESTS = PARIO_PARALLEL_TESTS_XFAIL = PARIO_TESTS = $(PARIO_SERIAL_TESTS) $(PARIO_PARALLEL_TESTS) PARIO_TESTS_XFAIL = $(PARIO_SERIAL_TESTS_XFAIL) $(PARIO_PARALLEL_TESTS_XFAIL) PARIO_PARALLEL_TESTS += pario/dra/bign$(EXEEXT) #PARIO_PARALLEL_TESTS += pario/dra/dbg_read$(EXEEXT) # barely compiles, wrong test #PARIO_PARALLEL_TESTS += pario/dra/dbg_write$(EXEEXT) # barely compiles, wrong test #PARIO_PARALLEL_TESTS += pario/dra/dra2arviz$(EXEEXT) # not a test? PARIO_PARALLEL_TESTS += pario/dra/ntestc$(EXEEXT) PARIO_PARALLEL_TESTS += pario/dra/perfn$(EXEEXT) PARIO_PARALLEL_TESTS += pario/dra/rate$(EXEEXT) dtsrc = dtsrc += pario/dra/ffflush.F dtsrc += pario/dra/util.c pario_dra_big_SOURCES = pario/dra/big.F $(dtsrc) pario_dra_bign_SOURCES = pario/dra/bign.c pario_dra_dbg_read_SOURCES = pario/dra/dbg_read.c pario_dra_dbg_write_SOURCES = pario/dra/dbg_write.c pario_dra_dra2arviz_SOURCES = pario/dra/dra2arviz.c pario_dra_dra_mxm_SOURCES = pario/dra/dra_mxm.F $(dtsrc) pario_dra_ntest_SOURCES = pario/dra/ntest.F $(dtsrc) pario_dra_ntestc_SOURCES = pario/dra/ntestc.c pario_dra_perf_SOURCES = pario/dra/perf.F $(dtsrc) pario_dra_perf2_SOURCES = pario/dra/perf2.F $(dtsrc) pario_dra_perf3_SOURCES = pario/dra/perf3.F $(dtsrc) pario_dra_perfn_SOURCES = pario/dra/perfn.c pario_dra_rate_SOURCES = pario/dra/rate.c pario_dra_test_SOURCES = pario/dra/test.F $(dtsrc) pario_dra_test_mxm_SOURCES = pario/dra/test_mxm.F $(dtsrc) pario_dra_time_mxm_SOURCES = pario/dra/time_mxm.F $(dtsrc) pario_eaf_test_SOURCES = pario/eaf/test.F $(dtsrc) pario_sf_test_SOURCES = pario/sf/test.F $(dtsrc) EXTRA_DIST += pario/dra/README ############################################################################## # ga++/src # if CXX_BINDINGS lib_LTLIBRARIES += libga++.la libga___la_SOURCES = libga___la_SOURCES += ga++/src/GAServices.cc libga___la_SOURCES += ga++/src/GlobalArray.cc libga___la_SOURCES += ga++/src/PGroup.cc libga___la_SOURCES += ga++/src/init_term.cc libga___la_SOURCES += ga++/src/overload.cc libga___la_SOURCES += ga++/src/services.cc libga___la_LIBADD = libga.la include_HEADERS += ga++/src/ga++.h include_HEADERS += ga++/src/GAServices.h include_HEADERS += ga++/src/GlobalArray.h include_HEADERS += ga++/src/init_term.h include_HEADERS += ga++/src/PGroup.h include_HEADERS += ga++/src/services.h AM_CPPFLAGS += -I$(top_srcdir)/ga++/src endif # CXX_BINDINGS EXTRA_DIST += ga++/README ############################################################################## # ga++/testing # if CXX_BINDINGS check_PROGRAMS += ga++/testing/elempatch check_PROGRAMS += ga++/testing/mtest check_PROGRAMS += ga++/testing/ntestc check_PROGRAMS += ga++/testing/testc check_PROGRAMS += ga++/testing/testmult check_PROGRAMS += ga++/testing/create_irreg_test check_PROGRAMS += ga++/testing/threadsafecpp CXX_SERIAL_TESTS = CXX_SERIAL_TESTS_XFAIL = CXX_PARALLEL_TESTS = CXX_PARALLEL_TESTS_XFAIL = CXX_TESTS = $(CXX_SERIAL_TESTS) $(CXX_PARALLEL_TESTS) CXX_TESTS_XFAIL = $(CXX_SERIAL_TESTS_XFAIL) $(CXX_PARALLEL_TESTS_XFAIL) CXX_PARALLEL_TESTS += ga++/testing/elempatch$(EXEEXT) CXX_PARALLEL_TESTS += ga++/testing/mtest$(EXEEXT) CXX_PARALLEL_TESTS += ga++/testing/ntestc$(EXEEXT) CXX_PARALLEL_TESTS += ga++/testing/testc$(EXEEXT) CXX_PARALLEL_TESTS += ga++/testing/testmult$(EXEEXT) CXX_PARALLEL_TESTS += ga++/testing/create_irreg_test$(EXEEXT) CXX_PARALLEL_TESTS += ga++/testing/threadsafecpp$(EXEEXT) ga___testing_elempatch_SOURCES = ga++/testing/elempatch.cc ga___testing_mtest_SOURCES = ga++/testing/mtest.cc ga___testing_ntestc_SOURCES = ga++/testing/ntestc.cc ga___testing_testc_SOURCES = ga++/testing/testc.cc ga___testing_testmult_SOURCES = ga++/testing/testmult.cc ga___testing_create_irreg_test_SOURCES = ga++/testing/create_irreg_test.cc ga___testing_threadsafecpp_SOURCES = ga++/testing/thread-safe.cc ga___testing_threadsafecpp_CXXFLAGS = $(AM_CXXFLAGS) $(OPENMP_CXXFLAGS) ga___testing_threadsafecpp_CPPFLAGS = $(AM_CPPFLAGS) $(OPENMP_CXXFLAGS) ga___testing_threadsafecpp_LDFLAGS = $(AM_LDFLAGS) $(OPENMP_CXXFLAGS) ga___testing_elempatch_LDADD = libga++.la ga___testing_mtest_LDADD = libga++.la ga___testing_ntestc_LDADD = libga++.la ga___testing_testc_LDADD = libga++.la ga___testing_testmult_LDADD = libga++.la ga___testing_create_irreg_test_LDADD = libga++.la endif # CXX_BINDINGS ############################################################################## # gparrays # if ENABLE_GPARRAYS GPPAPI_H = $(top_srcdir)/gparrays/src/gp-papi.h gparrays/src/gp-wapi.h: $(GPPAPI_H) @-rm -f $@ $(SED_V) \ $(SED) -e "s/GPPAPI/GPWAPI/g" \ -e "s/pgp_/wgp_/g" \ -e "/typedef/d" \ $(GPPAPI_H) > $@ gparrays/src/gp-wapidefs.h: $(GPPAPI_H) @-rm -f $@ $(SED_V) \ $(SED) -e "s/GPPAPI/GPWAPIDEFS/g" \ -e "s/^.*p\(gp_.*\)(.*$$/#define w\1 p\1/g" \ -e "/^ /d" \ -e "/^\/\*/d" \ -e "/^ \*/d" \ -e "/typedef/d" \ -e "/include/d" \ $(GPPAPI_H) > $@ BUILT_SOURCES += gparrays/src/gp-wapi.h BUILT_SOURCES += gparrays/src/gp-wapidefs.h CLEANFILES += gparrays/src/gp-wapi.h CLEANFILES += gparrays/src/gp-wapidefs.h libga_la_SOURCES += gparrays/src/gpbase.c libga_la_SOURCES += gparrays/src/gpbase.h libga_la_SOURCES += gparrays/src/gpcapi.c libga_la_SOURCES += gparrays/src/gpfapi.c libga_la_SOURCES += gparrays/src/gponesided.c libga_la_SOURCES += gparrays/src/gp-papi.h nodist_libga_la_SOURCES += gparrays/src/gp-wapidefs.h include_HEADERS += gparrays/src/gp.h include_HEADERS += gparrays/src/gp-papi.h nodist_include_HEADERS += gparrays/src/gp-wapi.h if ENABLE_F77 check_PROGRAMS += gparrays/testing/test endif check_PROGRAMS += gparrays/testing/testc check_PROGRAMS += gparrays/testing/transpose gparrays_testing_test_SOURCES = gparrays/testing/test.F gparrays_testing_testc_SOURCES = gparrays/testing/testc.c gparrays_testing_transpose_SOURCES = gparrays/testing/transpose.c AM_CPPFLAGS += -I$(top_build_prefix)gparrays/src AM_CPPFLAGS += -I$(top_srcdir)/gparrays/src endif # ENABLE_GPARRAYS ############################################################################## # gaf2c # libga_la_SOURCES += gaf2c/gaf2c.c libga_la_SOURCES += gaf2c/drand.c if ENABLE_F77 libga_la_SOURCES += gaf2c/farg.F endif include_HEADERS += gaf2c/typesf2c.h include_HEADERS += gaf2c/farg.h AM_CPPFLAGS += -I$(top_build_prefix)gaf2c AM_CPPFLAGS += -I$(top_srcdir)/gaf2c if ENABLE_F77 check_PROGRAMS += gaf2c/testarg endif GAF2C_SERIAL_TESTS = GAF2C_SERIAL_TESTS_XFAIL = GAF2C_PARALLEL_TESTS = GAF2C_PARALLEL_TESTS_XFAIL = GAF2C_TESTS = $(GAF2C_SERIAL_TESTS) $(GAF2C_PARALLEL_TESTS) GAF2C_TESTS_XFAIL = $(GAF2C_SERIAL_TESTS_XFAIL) $(GAF2C_PARALLEL_TESTS_XFAIL) if ENABLE_F77 GAF2C_SERIAL_TESTS += gaf2c/testarg$(EXEEXT) endif gaf2c_testarg_SOURCES = gaf2c/testarg.c gaf2c/testargf.f ############################################################################## # tcgmsg # libga_la_SOURCES += tcgmsg/capi.c libga_la_SOURCES += tcgmsg/fapi.c include_HEADERS += tcgmsg/tcgmsg.fh include_HEADERS += tcgmsg/tcgmsg.h include_HEADERS += tcgmsg/tcgmsg-mpi/msgtypesc.h include_HEADERS += tcgmsg/tcgmsg-mpi/msgtypesf.h AM_CPPFLAGS += -I$(top_srcdir)/tcgmsg ############################################################################## # tcgmsg/examples # # NOTE: While we don't build the tcgmsg examples, we include everything in the # distribution anyway. # EXTRA_DIST += tcgmsg/examples/blkdat120lin.f EXTRA_DIST += tcgmsg/examples/blkdat15.f EXTRA_DIST += tcgmsg/examples/blkdat240lin.f EXTRA_DIST += tcgmsg/examples/blkdat30.f EXTRA_DIST += tcgmsg/examples/blkdat60.f EXTRA_DIST += tcgmsg/examples/blkdat60lin.f EXTRA_DIST += tcgmsg/examples/cscf120lin.h EXTRA_DIST += tcgmsg/examples/cscf15.h EXTRA_DIST += tcgmsg/examples/cscf240lin.h EXTRA_DIST += tcgmsg/examples/cscf30.h EXTRA_DIST += tcgmsg/examples/cscf60.h EXTRA_DIST += tcgmsg/examples/cscf60lin.h EXTRA_DIST += tcgmsg/examples/cscf.h EXTRA_DIST += tcgmsg/examples/daxpy1.s EXTRA_DIST += tcgmsg/examples/daxpy.f EXTRA_DIST += tcgmsg/examples/ddot.f EXTRA_DIST += tcgmsg/examples/demo.proto EXTRA_DIST += tcgmsg/examples/diagon.f EXTRA_DIST += tcgmsg/examples/dscal.f EXTRA_DIST += tcgmsg/examples/fexit.f.proto EXTRA_DIST += tcgmsg/examples/getmem.c EXTRA_DIST += tcgmsg/examples/grid.c EXTRA_DIST += tcgmsg/examples/ieeetrap.c EXTRA_DIST += tcgmsg/examples/integ.f EXTRA_DIST += tcgmsg/examples/jacobi.f EXTRA_DIST += tcgmsg/examples/Makefile.proto EXTRA_DIST += tcgmsg/examples/mc.f EXTRA_DIST += tcgmsg/examples/mc.input EXTRA_DIST += tcgmsg/examples/md.f EXTRA_DIST += tcgmsg/examples/mxv_daxpy1.f EXTRA_DIST += tcgmsg/examples/mxv_dgemv.f EXTRA_DIST += tcgmsg/examples/mxv_fortran.f EXTRA_DIST += tcgmsg/examples/output.f EXTRA_DIST += tcgmsg/examples/prtri.f EXTRA_DIST += tcgmsg/examples/random.c EXTRA_DIST += tcgmsg/examples/README EXTRA_DIST += tcgmsg/examples/runit EXTRA_DIST += tcgmsg/examples/runit.grid EXTRA_DIST += tcgmsg/examples/scfblas.f EXTRA_DIST += tcgmsg/examples/scf.f EXTRA_DIST += tcgmsg/examples/timer.f EXTRA_DIST += tcgmsg/examples/trace.out EXTRA_DIST += tcgmsg/examples/xpix.shar ############################################################################## # tcgmsg/tcgmsg-mpi # if MSG_COMMS_MPI libga_la_SOURCES += tcgmsg/tcgmsg-mpi/checkbyte.c libga_la_SOURCES += tcgmsg/tcgmsg-mpi/clustercheck.c libga_la_SOURCES += tcgmsg/tcgmsg-mpi/collect.c libga_la_SOURCES += tcgmsg/tcgmsg-mpi/drand48.c libga_la_SOURCES += tcgmsg/tcgmsg-mpi/evlog.c libga_la_SOURCES += tcgmsg/tcgmsg-mpi/evlog.h libga_la_SOURCES += tcgmsg/tcgmsg-mpi/evon.c libga_la_SOURCES += tcgmsg/tcgmsg-mpi/llog.c libga_la_SOURCES += tcgmsg/tcgmsg-mpi/misc.c libga_la_SOURCES += tcgmsg/tcgmsg-mpi/niceftn.c libga_la_SOURCES += tcgmsg/tcgmsg-mpi/nxtval-armci.c libga_la_SOURCES += tcgmsg/tcgmsg-mpi/p2p.c libga_la_SOURCES += tcgmsg/tcgmsg-mpi/pbeginf.c libga_la_SOURCES += tcgmsg/tcgmsg-mpi/pfilecopy.c libga_la_SOURCES += tcgmsg/tcgmsg-mpi/sizeof.c libga_la_SOURCES += tcgmsg/tcgmsg-mpi/sndrcv.h libga_la_SOURCES += tcgmsg/tcgmsg-mpi/srftoc.h libga_la_SOURCES += tcgmsg/tcgmsg-mpi/tcgmsgP.h AM_CPPFLAGS += -I$(top_srcdir)/tcgmsg/tcgmsg-mpi check_PROGRAMS += tcgmsg/tests/test_asyn check_PROGRAMS += tcgmsg/tests/test if ENABLE_F77 check_PROGRAMS += tcgmsg/tests/testf endif #TCGMSG_PARALLEL_TESTS += tcgmsg/tests/test_asyn$(EXEEXT) # useless test #TCGMSG_PARALLEL_TESTS += tcgmsg/tests/test$(EXEEXT) # requires user input #if ENABLE_F77 #TCGMSG_PARALLEL_TESTS += tcgmsg/tests/testf$(EXEEXT) #endif tcgmsg_tests_test_asyn_SOURCES = tcgmsg/tests/test_asyn.c tcgmsg_tests_testf_SOURCES = tcgmsg/tests/testf.F tcgmsg_tests_test_SOURCES = tcgmsg/tests/test.c endif # MSG_COMMS_MPI EXTRA_DIST += tcgmsg/tcgmsg-mpi/README ############################################################################## # armci if ARMCI_NETWORK_ARMCI else if ARMCI_SRC_DIR_COMEX MAYBE_COMEX = comex else MAYBE_ARMCI = armci endif endif SUBDIRS = $(MAYBE_COMEX) $(MAYBE_ARMCI) . if ARMCI_NETWORK_ARMCI else if ARMCI_SRC_DIR_COMEX comex/libarmci.la: ($(am__cd) comex && $(MAKE) $(AM_MAKEFLAGS) all) || exit 1; else armci/libarmci.la: ($(am__cd) armci && $(MAKE) $(AM_MAKEFLAGS) all) || exit 1; endif endif if ARMCI_SRC_DIR_COMEX AM_CPPFLAGS += -I$(top_srcdir)/comex/src-armci else if ARMCI_NETWORK_ARMCI else AM_CPPFLAGS += -I$(top_srcdir)/armci/src/include endif endif # As of ga-5-2 libarmci is no longer rolled up into libga. libga_la_LIBADD += $(ELPA_LIBS) libga_la_LIBADD += $(SCALAPACK_LIBS) libga_la_LIBADD += $(LAPACK_LIBS) libga_la_LIBADD += $(BLAS_LIBS) if WITH_SICM libga_la_LIBADD += $(SICM_LIBS) endif if ARMCI_NETWORK_ARMCI libga_la_LIBADD += $(ARMCI_NETWORK_LIBS) libga_la_LIBADD += $(GA_MP_LIBS) else if ARMCI_SRC_DIR_COMEX libga_la_LIBADD += comex/libarmci.la else libga_la_LIBADD += armci/libarmci.la endif endif libga_la_LIBADD += $(MAYBE_FLIBS) # if --disable-f77 is used, we must override linker choice if ENABLE_F77 libga_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=F77 $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(F77LD) $(AM_FFLAGS) $(FFLAGS) \ $(libga_la_LDFLAGS) $(LDFLAGS) -o $@ else libga_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(libga_la_LDFLAGS) $(LDFLAGS) -o $@ endif ############################################################################## # the end # .PHONY: checkprogs checkprogs-recursive checkprogs-am checkprogs: checkprogs-recursive checkprogs-am: $(check_PROGRAMS) GA_RECURSIVE_TARGETS = checkprogs-recursive # COPIED FROM MAKEFILE GENERATED BY AUTOMAKE 1.11.1 # Modified to use our own recursive targets. $(GA_RECURSIVE_TARGETS): all @fail= failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" # END COPY # support verbose/silent make rules for additional programs # m4 M4_V = $(M4__v_$(V)) M4__v_ = $(M4__v_$(AM_DEFAULT_VERBOSITY)) M4__v_0 = @echo " M4 " $@; # sed SED_V = $(SED__v_$(V)) SED__v_ = $(SED__v_$(AM_DEFAULT_VERBOSITY)) SED__v_0 = @echo " SED " $@; # cp CP = cp CP_V = $(CP__v_$(V)) CP__v_ = $(CP__v_$(AM_DEFAULT_VERBOSITY)) CP__v_0 = @echo " CP " $@; .PHONY: pfiles clean-pfiles ############################################################################## # test suite # # Tests were broken up into groups depending on the piece of the distribution # which they tested. # # Some tests were commented out either because they required an input file or # they were failing for unknown reasons and we didn't want to further debug. # Some tests were based on old versions of GA which no longer applied but we # kept anyway (see pario). # # We leave pario tests out of the main set of tests because not all systems # can handle the disk requirements. # SERIAL_TESTS = SERIAL_TESTS += $(MA_SERIAL_TESTS) SERIAL_TESTS += $(GLOBAL_SERIAL_TESTS) SERIAL_TESTS += $(CXX_SERIAL_TESTS) SERIAL_TESTS_XFAIL = SERIAL_TESTS_XFAIL += $(MA_SERIAL_TESTS_XFAIL) SERIAL_TESTS_XFAIL += $(GLOBAL_SERIAL_TESTS_XFAIL) SERIAL_TESTS_XFAIL += $(CXX_SERIAL_TESTS_XFAIL) PARALLEL_TESTS = PARALLEL_TESTS += $(MA_PARALLEL_TESTS) PARALLEL_TESTS += $(GLOBAL_PARALLEL_TESTS) PARALLEL_TESTS += $(CXX_PARALLEL_TESTS) PARALLEL_TESTS += $(GLOBAL_THREADED_TESTS) PARALLEL_TESTS_XFAIL = PARALLEL_TESTS_XFAIL += $(MA_PARALLEL_TESTS_XFAIL) PARALLEL_TESTS_XFAIL += $(GLOBAL_PARALLEL_TESTS_XFAIL) PARALLEL_TESTS_XFAIL += $(CXX_PARALLEL_TESTS_XFAIL) TESTS = TESTS += $(SERIAL_TESTS) TESTS += $(PARALLEL_TESTS) XFAIL_TESTS = XFAIL_TESTS += $(SERIAL_TESTS_XFAIL) XFAIL_TESTS += $(PARALLEL_TESTS_XFAIL) if CROSS_COMPILING LOG_COMPILER = \ maybe_mpiexec=`if echo "$(SERIAL_TESTS)" | $(GREP) "$$p" > /dev/null; then echo "$(MPIEXEC)" | $(SED) 's/%NP%/1/'; else echo "$(MPIEXEC)" | $(SED) 's/%NP%/$(NPROCS)/'; fi`; eval $$maybe_mpiexec else if ARMCI_NETWORK_MPI_PR NPROCS1=$(shell expr $(NPROCS) + 1) LOG_COMPILER = \ maybe_mpiexec=`if echo "$(SERIAL_TESTS)" | $(GREP) "$$p" > /dev/null; then echo ""; else if echo "$(MPIEXEC)" | $(GREP) "%NP%" > /dev/null; then echo "$(MPIEXEC)" | $(SED) 's/%NP%/$(NPROCS1)/'; else echo "$(MPIEXEC)"; fi; fi`; eval $$maybe_mpiexec else LOG_COMPILER = \ maybe_mpiexec=`if echo "$(SERIAL_TESTS)" | $(GREP) "$$p" > /dev/null; then echo ""; else if echo "$(MPIEXEC)" | $(GREP) "%NP%" > /dev/null; then echo "$(MPIEXEC)" | $(SED) 's/%NP%/$(NPROCS)/'; else echo "$(MPIEXEC)"; fi; fi`; eval $$maybe_mpiexec endif # COMEX_NETWORK_MPI_PR endif # CROSS_COMPILING .PHONY: check-travis check-travis: $(all-am) $(check_LTLIBRARIES) $(TRAVIS_TESTS) $(MAYBE_PFILES) $(MAKE) $(AM_MAKEFLAGS) TESTS="$(TRAVIS_TESTS)" check-TESTS .PHONY: check-threaded check-threaded: $(all-am) $(check_LTLIBRARIES) $(GLOBAL_THREADED_TESTS) $(MAYBE_PFILES) $(MAKE) $(AM_MAKEFLAGS) TESTS="$(GLOBAL_THREADED_TESTS)" check-TESTS .PHONY: check-ga check-ga: $(all-am) $(check_LTLIBRARIES) $(check_PROGRAMS) $(MAYBE_PFILES) $(MAKE) $(AM_MAKEFLAGS) check-TESTS .PHONY: recheck-ga recheck-ga: $(all-am) $(check_LTLIBRARIES) $(check_PROGRAMS) $(MAYBE_PFILES) $(MAKE) $(AM_MAKEFLAGS) recheck .PHONY: check-ma check-ma: $(all-am) $(check_LTLIBRARIES) $(MA_TESTS) $(MAYBE_PFILES) $(MAKE) $(AM_MAKEFLAGS) TESTS="$(MA_TESTS)" XFAIL_TESTS="$(MA_TESTS_XFAIL)" check-TESTS .PHONY: check-global check-global: $(all-am) $(check_LTLIBRARIES) $(GLOBAL_TESTS) $(MAYBE_PFILES) $(MAKE) $(AM_MAKEFLAGS) TESTS="$(GLOBAL_TESTS)" XFAIL_TESTS="$(GLOBAL_TESTS_XFAIL)" check-TESTS .PHONY: check-pario check-pario: $(all-am) $(check_LTLIBRARIES) $(PARIO_TESTS) $(MAYBE_PFILES) $(MAKE) $(AM_MAKEFLAGS) TESTS="$(PARIO_TESTS)" XFAIL_TESTS="$(PARIO_TESTS_XFAIL)" check-TESTS .PHONY: check-ga++ if CXX_BINDINGS check-ga++: $(all-am) $(check_LTLIBRARIES) $(CXX_TESTS) $(MAYBE_PFILES) $(MAKE) $(AM_MAKEFLAGS) TESTS="$(CXX_TESTS)" XFAIL_TESTS="$(CXX_TESTS_XFAIL)" check-TESTS endif # Because pario tests are not listed in the TESTS variable, the log targets # are not generated by automake. Do them manually here. pario/dra/bign.log: pario/dra/bign$(EXEEXT) @p='pario/dra/bign$(EXEEXT)'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post) pario/dra/ntestc.log: pario/dra/ntestc$(EXEEXT) @p='pario/dra/ntestc$(EXEEXT)'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post) pario/dra/perfn.log: pario/dra/perfn$(EXEEXT) @p='pario/dra/perfn$(EXEEXT)'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post) pario/dra/rate.log: pario/dra/rate$(EXEEXT) @p='pario/dra/rate$(EXEEXT)'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post) ga-5.9.2/README.md000066400000000000000000000064621500715745200134130ustar00rootroot00000000000000# GLOBAL ARRAYS [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](LICENSE) [![Documentation Status](https://readthedocs.org/projects/globalarrays/badge/?version=latest)](https://globalarrays.readthedocs.io/en/latest/?badge=latest) [![GitHub Downloads](https://img.shields.io/github/downloads/GlobalArrays/ga/total)](https://github.com/GlobalArrays/ga/releases) [![CI](https://github.com/GlobalArrays/ga/actions/workflows/github_actions.yml/badge.svg)](https://github.com/GlobalArrays/ga/actions?query=workflow:GlobalArrays_CI) ## Table of Contents * [ACKNOWLEDGMENTS](#acknowledgment) * [ABOUT THIS SOFTWARE](#about-this-software) * [WHERE IS THE DOCUMENTATION?](#where-is-the-documentation) * [QUESTIONS/HELP/SUPPORT/BUG-REPORT](#questionshelpsupportbug-report) ## ACKNOWLEDGMENTS This software and its documentation were produced with United States Government support under Contract Number DE-AC06-76RLO-1830 awarded by the United States Department of Energy. The United States Government retains a paid-up non-exclusive, irrevocable worldwide license to reproduce, prepare derivative works, perform publicly and display publicly by or for the US Government, including the right to distribute to other US Government contractors. The most recent source of funding for development of GA is the [Exascale Computing Project](https://exascaleproject.org). ## ABOUT THIS SOFTWARE More information about Global Arrays can be found at the webpage [https://globalarrays.github.io](https://globalarrays.github.io). Global Arrays is a portable Non-Uniform Memory Access (NUMA) shared-memory programming environment for distributed and shared memory computers. It augments the message-passing model by providing a shared-memory like access to distributed dense arrays. This is also known as the Partitioned Global Address Space (PGAS) model. This library contains the Global Arrays (GA), Communications Runtime for Exascale (ComEx) run-time library, Aggregate Remote Memory Copy Interface (ARMCI) run-time library, Memory Allocator (MA), parallel I/O libraries (DRA,EAF,SF), TCGMSG, and TCGMSG-MPI packages bundled together. ARMCI provides one-sided remote memory operations used by GA. ComEx is a successor to ARMCI and provides an ARMCI-compatible interface. New parallel runtime development takes place within ComEx including the MPI-only runtimes. DRA (Disk Resident Arrays) is a parallel I/O library that maintains dense two-dimensional arrays on disk. SF (Shared Files) is a parallel I/O library that allows noncollective I/O to a parallel file. EAF (Exclusive Access Files) is parallel I/O library that supports I/O to private files. TCGMSG is a simple, efficient, but obsolete message-passing library. TCGMSG-MPI is a TCGMSG interface implementation on top of MPI and ARMCI. MA is a dynamic memory allocator/manager for Fortran and C programs. GA++ is a C++ binding for global arrays. ## WHERE IS THE DOCUMENTATION? The [GA manual](https://globalarrays.readthedocs.io) contains all the documentation. The API reference can be found [here](https://globalarrays.github.io/userinterface.html) ## QUESTIONS/HELP/SUPPORT/BUG-REPORT Please submit issues to our [GitHub issue tracker](https://github.com/GlobalArrays/ga/issues). We use Google Groups to host [our discussion forum](https://groups.google.com/forum/#!forum/hpctools). ga-5.9.2/armci/000077500000000000000000000000001500715745200132175ustar00rootroot00000000000000ga-5.9.2/armci/INSTALL000066400000000000000000000271361500715745200142610ustar00rootroot00000000000000Installation Instructions ************************* Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== Briefly, the shell commands `./configure; make; make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 6. Often, you can also type `make uninstall' to remove the installed files again. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple `-arch' options to the compiler but only a single `-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the `lipo' tool if you have problems. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put `/usr/ucb' early in your `PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in `/usr/bin'. So, if you need `/usr/ucb' in your `PATH', put it _after_ `/usr/bin'. On Haiku, software installed for all users goes in `/boot/common', not `/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf bug. Until the bug is fixed you can use this workaround: CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of all of the options to `configure', and exit. `--help=short' `--help=recursive' Print a summary of the options unique to this package's `configure', and exit. The `short' variant lists options used only in the top level, while the `recursive' variant lists options also present in any nested packages. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--prefix=DIR' Use DIR as the installation prefix. *Note Installation Names:: for more details, including other options available for fine-tuning the installation locations. `--no-create' `-n' Run the configure checks, but stop before creating any output files. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. ga-5.9.2/armci/Makefile.am000066400000000000000000000567001500715745200152630ustar00rootroot00000000000000############################################################################## # Makefile.am for ARMCI. # # Rationale: # This Makefile.am follows many of the suggestions outlined in the paper # "Recursive Make Considered Harmful". We do not use Automake's # 'include' feature (instead preferring a single, large Makefile.am). # # Additional targets: # Besides the traditional make targets supplied by Automake, we have added the # "examples" and "checkprogs" targets to build example programs and test # programs, respectively. # # Notes: # In general, each subdirectory has a corresponding section of this # Makefile.am with the notable exception being the many examples getting # rolled up into the examples section. # # The usual aclocal nonsense to get include paths right. ACLOCAL_AMFLAGS = -I m4 -I ../m4 -I ../comex/m4 # All public headers, installed programs, test programs, and example programs # are listed in these variables. Appended to throughout. These are the # automake variables used. include_HEADERS = bin_PROGRAMS = bin_SCRIPTS = check_PROGRAMS = check_LTLIBRARIES = lib_LTLIBRARIES = EXTRA_DIST = README BUILT_SOURCES = MOSTLYCLEANFILES = CLEANFILES = DISTCLEANFILES = MAINTAINERCLEANFILES = AM_FFLAGS = AM_CFLAGS = AM_CXXFLAGS = AM_CPPFLAGS = AM_LDFLAGS = LDADD = if ARMCI_NETWORK_EXTERNAL else lib_LTLIBRARIES += libarmci.la endif libarmci_la_SOURCES = nodist_libarmci_la_SOURCES = libarmci_la_LIBADD = if ARMCI_NETWORK_COMEX ARMCI_NETWORK_CPPFLAGS += -I$(abs_srcdir)/../comex/src-armci ARMCI_NETWORK_LDFLAGS += -L$(abs_builddir)/../comex ARMCI_NETWORK_LIBS += $(abs_builddir)/../comex/libarmci.la endif AM_FFLAGS += $(ARMCI_FOPT) AM_FFLAGS += $(GA_F_WARN) AM_FFLAGS += $(FFLAG_INT) AM_CFLAGS += $(ARMCI_COPT) AM_CFLAGS += $(GA_C_WARN) AM_CXXFLAGS += $(ARMCI_CXXOPT) AM_CXXFLAGS += $(GA_CXX_WARN) AM_CPPFLAGS += $(GA_MP_CPPFLAGS) AM_CPPFLAGS += $(ARMCI_NETWORK_CPPFLAGS) if MSG_COMMS_MPI else AM_CPPFLAGS += -I$(top_srcdir)/tcgmsg endif LDFLAGS_BASE = LDFLAGS_BASE += $(GA_MP_LDFLAGS) LDFLAGS_BASE += $(ARMCI_NETWORK_LDFLAGS) AM_LDFLAGS += $(LDFLAGS_BASE) if ARMCI_NETWORK_EXTERNAL LDADD += $(ARMCI_NETWORK_LIBS) LDADD += $(GA_MP_LIBS) else LDADD += libarmci.la endif if ARMCI_NETWORK_EXTERNAL else libarmci_la_LIBADD += $(GA_MP_LIBS) libarmci_la_LIBADD += $(ARMCI_NETWORK_LIBS) endif ############################################################################## # compiler and linker flags # # Important for external tools wanting to know how to link to ARMCI. SED_NORMALIZE_WHITESPACE = $(SED) 's/ [ ]*/ /g;s/" /"/g;s/ "/"/g' .PHONY: flags flags: @echo ' =========================================================================== ' @echo ' Suggested compiler/linker options are as follows.' @echo ' ARMCI libraries are installed in $(libdir)' @echo ' ARMCI headers are installed in $(includedir)' @echo ' ' @echo ' CPPFLAGS="$(GA_MP_CPPFLAGS) $(ARMCI_NETWORK_CPPFLAGS) -I$(includedir)"' | $(SED_NORMALIZE_WHITESPACE) @echo ' ' @echo ' LDFLAGS="$(GA_MP_LDFLAGS) $(ARMCI_NETWORK_LDFLAGS) -L$(libdir)"' | $(SED_NORMALIZE_WHITESPACE) @echo ' ' @echo ' For C/C++ Programs: ' @echo ' LIBS="-larmci $(GA_MP_LIBS) $(ARMCI_NETWORK_LIBS)"' | $(SED_NORMALIZE_WHITESPACE) @echo ' =========================================================================== ' bin_SCRIPTS += tools/armci-config CLEANFILES += $(bin_SCRIPTS) ############################################################################## # src # libarmci_la_SOURCES += src/collectives/message.c libarmci_la_SOURCES += src/common/aggregate.c libarmci_la_SOURCES += src/common/armci.c libarmci_la_SOURCES += src/common/ccopy.c libarmci_la_SOURCES += src/common/clusterinfo.c libarmci_la_SOURCES += src/common/pack.c libarmci_la_SOURCES += src/common/iterator.c libarmci_la_SOURCES += src/include/iterator.h libarmci_la_SOURCES += src/ft/armci_chkpt.h libarmci_la_SOURCES += src/ft/armci_storage.h libarmci_la_SOURCES += src/include/acc.h libarmci_la_SOURCES += src/include/armcip.h libarmci_la_SOURCES += src/include/asm-ppc.h libarmci_la_SOURCES += src/include/atomics-i386.h libarmci_la_SOURCES += src/include/copy.h libarmci_la_SOURCES += src/include/kr_malloc.h libarmci_la_SOURCES += src/include/locks.h libarmci_la_SOURCES += src/include/memlock.h libarmci_la_SOURCES += src/include/request.h libarmci_la_SOURCES += src/include/semaphores.h libarmci_la_SOURCES += src/include/shmalloc.h libarmci_la_SOURCES += src/include/armci_shmem.h libarmci_la_SOURCES += src/include/shmlimit.h libarmci_la_SOURCES += src/include/signaltrap.h libarmci_la_SOURCES += src/include/spinlock.h libarmci_la_SOURCES += src/include/utils.h libarmci_la_SOURCES += src/locks/locks.c libarmci_la_SOURCES += src/locks/memlock.c libarmci_la_SOURCES += src/locks/mutex.c libarmci_la_SOURCES += src/locks/semaphores.c libarmci_la_SOURCES += src/memory/kr_malloc.c libarmci_la_SOURCES += src/memory/memory.c libarmci_la_SOURCES += src/progress/fence.c libarmci_la_SOURCES += src/progress/wait.c libarmci_la_SOURCES += src/xfer/caccumulate.c libarmci_la_SOURCES += src/xfer/rmw.c libarmci_la_SOURCES += src/xfer/strided.c libarmci_la_SOURCES += src/xfer/vector.c if MSG_COMMS_MPI libarmci_la_SOURCES += src/common/groups.c endif if ARMCI_NETWORK_MPI_MT AM_CPPFLAGS += -I$(top_srcdir)/src/devices/mpi-mt libarmci_la_SOURCES += src/common/ds-shared.c libarmci_la_SOURCES += src/common/request.c libarmci_la_SOURCES += src/common/spawn.c libarmci_la_SOURCES += src/devices/mpi-mt/mpi2_client.c libarmci_la_SOURCES += src/devices/mpi-mt/mpi2.h libarmci_la_SOURCES += src/devices/mpi-mt/mpi2_server.c libarmci_la_SOURCES += src/memory/buffers.c endif if ARMCI_NETWORK_MPI_SPAWN AM_CPPFLAGS += -I$(top_srcdir)/src/devices/mpi-spawn libarmci_la_SOURCES += src/common/ds-shared.c libarmci_la_SOURCES += src/common/request.c libarmci_la_SOURCES += src/devices/mpi-spawn/mpi2_client.c libarmci_la_SOURCES += src/devices/mpi-spawn/mpi2.h libarmci_la_SOURCES += src/devices/mpi-spawn/mpi2_server.c libarmci_la_SOURCES += src/memory/buffers.c endif if ARMCI_NETWORK_OPENIB AM_CPPFLAGS += -I$(top_srcdir)/src/devices/openib libarmci_la_SOURCES += src/common/async.c libarmci_la_SOURCES += src/common/ds-shared.c libarmci_la_SOURCES += src/common/regions.c libarmci_la_SOURCES += src/common/request.c libarmci_la_SOURCES += src/common/spawn.c libarmci_la_SOURCES += src/devices/openib/armci-vapi.h libarmci_la_SOURCES += src/devices/openib/cbuf.c libarmci_la_SOURCES += src/devices/openib/cbuf.h libarmci_la_SOURCES += src/devices/openib/openib.c libarmci_la_SOURCES += src/devices/openib/pendbufs.c libarmci_la_SOURCES += src/devices/openib/pendbufs.h libarmci_la_SOURCES += src/devices/openib/rtinfo.c libarmci_la_SOURCES += src/memory/buffers.c endif if ARMCI_NETWORK_SOCKETS AM_CPPFLAGS += -I$(top_srcdir)/src/devices/sockets libarmci_la_SOURCES += src/common/async.c libarmci_la_SOURCES += src/common/ds-shared.c libarmci_la_SOURCES += src/common/request.c libarmci_la_SOURCES += src/common/spawn.c libarmci_la_SOURCES += src/devices/sockets/dataserv.c libarmci_la_SOURCES += src/devices/sockets/sockets.c libarmci_la_SOURCES += src/devices/sockets/sockets.h libarmci_la_SOURCES += src/memory/buffers.c endif if ENABLE_CHECKPOINT libarmci_la_SOURCES += src/ft/armci_chkpt.c libarmci_la_SOURCES += src/ft/armci_storage.c endif if ARMCI_ENABLE_GPC_CALLS libarmci_la_SOURCES += src/common/gpc.c endif if MACX libarmci_la_SOURCES += src/common/signaltrap.c libarmci_la_SOURCES += src/memory/shmalloc.c libarmci_la_SOURCES += src/memory/winshmem.c AM_CPPFLAGS += -DSHMEM -DMMAP else if CYGWIN libarmci_la_SOURCES += src/common/signaltrap.c libarmci_la_SOURCES += src/memory/shmalloc.c libarmci_la_SOURCES += src/memory/winshmem.c AM_CPPFLAGS += -DSHMEM -DMMAP else if SYSV libarmci_la_SOURCES += src/common/signaltrap.c libarmci_la_SOURCES += src/memory/shmem.c libarmci_la_SOURCES += src/memory/shmlimit.c endif endif endif if THREAD_SAFE libarmci_la_SOURCES += src/common/utils.c libarmci_la_SOURCES += src/util/threads.c endif include_HEADERS += src/include/armci.h include_HEADERS += src/include/gpc.h include_HEADERS += src/include/message.h AM_CPPFLAGS += -I$(top_srcdir)/src/include ############################################################################## # profiling # if ENABLE_PROFILING if HAVE_SYS_WEAK_ALIAS_PRAGMA lib_LTLIBRARIES += libarmci_prof.la libarmci_prof_la_SOURCES = libarmci_prof_la_SOURCES += tools/armci_prof.c libarmci_la_SOURCES += src/common/capi.c else # HAVE_SYS_WEAK_ALIAS_PRAGMA libarmci_la_SOURCES += tools/armci_prof.c endif # HAVE_SYS_WEAK_ALIAS_PRAGMA else # ENABLE_PROFILING libarmci_la_SOURCES += src/common/capi.c endif include_HEADERS += src/include/parmci.h ############################################################################## # testing # check_PROGRAMS += testing/msgcheck check_PROGRAMS += testing/perf check_PROGRAMS += testing/perf2 check_PROGRAMS += testing/perf_aggr check_PROGRAMS += testing/perf_nb check_PROGRAMS += testing/perf_strided check_PROGRAMS += testing/shmclean check_PROGRAMS += testing/shmtest check_PROGRAMS += testing/simplelock check_PROGRAMS += testing/test check_PROGRAMS += testing/test2 if HAVE_ARMCI_NOTIFY check_PROGRAMS += testing/testnotify endif if ENABLE_CHECKPOINT check_PROGRAMS += testing/fttest endif if ARMCI_ENABLE_GPC_CALLS check_PROGRAMS += testing/gpctest endif if MSG_COMMS_MPI check_PROGRAMS += testing/simple check_PROGRAMS += testing/test_groups if SYSV if ARMCI_NETWORK_EXTERNAL else check_PROGRAMS += testing/ipctest endif endif endif if THREAD_SAFE check_PROGRAMS += testing/test_mt endif if HAVE_ARMCI_STRIDE_INFO_INIT check_PROGRAMS += testing/testitr endif atsrc = if HAVE_ARMCI_MSG_INIT else atsrc += testing/rpl_armci_msg_init.c endif if HAVE_ARMCI_MSG_FINALIZE else atsrc += testing/rpl_armci_msg_finalize.c endif ARMCI_SERIAL_TESTS = ARMCI_SERIAL_TESTS_XFAIL = ARMCI_PARALLEL_TESTS = ARMCI_PARALLEL_TESTS_XFAIL = ARMCI_TESTS = $(ARMCI_SERIAL_TESTS) $(ARMCI_PARALLEL_TESTS) ARMCI_TESTS_XFAIL = $(ARMCI_SERIAL_TESTS_XFAIL) $(ARMCI_PARALLEL_TESTS_XFAIL) ARMCI_PARALLEL_TESTS += testing/msgcheck$(EXEEXT) ARMCI_PARALLEL_TESTS += testing/perf$(EXEEXT) #ARMCI_PARALLEL_TESTS += testing/perf2(EXEEXT) # only a benchmark ARMCI_PARALLEL_TESTS += testing/perf_aggr$(EXEEXT) #ARMCI_PARALLEL_TESTS += testing/perf_nb$(EXEEXT) # needs bug fixes #ARMCI_SERIAL_TESTS += testing/shmclean$(EXEEXT) # not sure we need this #ARMCI_SERIAL_TESTS += testing/shmtest$(EXEEXT) # not sure we need this ARMCI_PARALLEL_TESTS += testing/simplelock$(EXEEXT) ARMCI_PARALLEL_TESTS += testing/test$(EXEEXT) ARMCI_PARALLEL_TESTS += testing/test2$(EXEEXT) if HAVE_ARMCI_NOTIFY ARMCI_PARALLEL_TESTS += testing/testnotify$(EXEEXT) endif if ENABLE_CHECKPOINT ARMCI_PARALLEL_TESTS += testing/fttest$(EXEEXT) endif if ARMCI_ENABLE_GPC_CALLS ARMCI_PARALLEL_TESTS += testing/gpctest$(EXEEXT) endif if MSG_COMMS_MPI ARMCI_PARALLEL_TESTS += testing/simple$(EXEEXT) ARMCI_PARALLEL_TESTS += testing/test_groups$(EXEEXT) if SYSV if ARMCI_NETWORK_EXTERNAL else #ARMCI_PARALLEL_TESTS += testing/ipctest$(EXEEXT) # needs bug fixes endif endif endif if THREAD_SAFE ARMCI_PARALLEL_TESTS += testing/test_mt$(EXEEXT) endif if HAVE_ARMCI_STRIDE_INFO_INIT ARMCI_SERIAL_TESTS += testing/testitr$(EXEEXT) endif testing_fttest_SOURCES = testing/fttest.c $(atsrc) testing_gpctest_SOURCES = testing/gpctest.c $(atsrc) testing_ipctest_SOURCES = testing/ipctest.c $(atsrc) testing_msgcheck_SOURCES = testing/msgcheck.c $(atsrc) testing_perf_aggr_SOURCES = testing/perf_aggr.c $(atsrc) testing_perf_nb_SOURCES = testing/perf_nb.c $(atsrc) testing_perf_strided_SOURCES= testing/perf_strided.c $(atsrc) testing_perf_SOURCES = testing/perf.c $(atsrc) testing_perf2_SOURCES = testing/perf2.c $(atsrc) testing_shmclean_SOURCES = testing/shmclean.c $(atsrc) testing_shmtest_SOURCES = testing/shmtest.c $(atsrc) testing_simple_SOURCES = testing/simple.c $(atsrc) testing_simplelock_SOURCES = testing/simplelock.c $(atsrc) testing_test2_SOURCES = testing/test2.c $(atsrc) testing_test_groups_SOURCES = testing/test_groups.c $(atsrc) testing_testitr_SOURCES = testing/testitr.c $(atsrc) testing_test_mt_SOURCES = testing/test_mt.c $(atsrc) testing_testnotify_SOURCES = testing/testnotify.c $(atsrc) testing_test_SOURCES = testing/test.c $(atsrc) testing_testitr_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src/include ############################################################################## # examples # check_PROGRAMS += examples/benchmarks/cg/armci_sharedmemory/cg check_PROGRAMS += examples/benchmarks/lu/armci_blocking/lu-b-bc check_PROGRAMS += examples/benchmarks/lu/armci_blocking/lu-block check_PROGRAMS += examples/benchmarks/lu/armci_blocking/lu check_PROGRAMS += examples/benchmarks/lu/armci_nonblocking/lu_nb_get check_PROGRAMS += examples/benchmarks/lu/armci_nonblocking/lu_nb_put check_PROGRAMS += examples/benchmarks/lu/pthreads/lu-thread check_PROGRAMS += examples/features/aggregation/simple/simple check_PROGRAMS += examples/features/aggregation/sparse_matvecmul/sparse_matvecmul check_PROGRAMS += examples/features/non-blocking/simple/simple check_PROGRAMS += examples/features/symmetric_memory/simple/simple check_PROGRAMS += examples/simpleApps/transp1D-c if HAVE_ARMCI_NOTIFY check_PROGRAMS += examples/features/concurrency/simple/comdegree check_PROGRAMS += examples/features/notification/simple/testnotify endif if ARMCI_ENABLE_GPC_CALLS check_PROGRAMS += examples/features/gpc/hashtable/test_hashtable if HAVE_BLAS if HAVE_LAPACK check_PROGRAMS += examples/features/proc_affinity/computation_impact/computation_impact endif # HAVE_LAPACK endif # HAVE_BLAS check_PROGRAMS += examples/features/proc_affinity/simple/simple endif # ARMCI_ENABLE_GPC_CALLS if MSG_COMMS_MPI check_PROGRAMS += examples/features/concurrency/multidma/multidma check_PROGRAMS += examples/features/non-blocking/overlap/overlap endif if RANDOM_ACCESS check_PROGRAMS += examples/benchmarks/RandomAccess/simple/simple check_PROGRAMS += examples/benchmarks/RandomAccess/smp_bucket/smp_bucket endif if THREAD_SAFE check_PROGRAMS += examples/benchmarks/lu/armci_multithreaded/lu-block-th endif #ARMCI_PARALLEL_TESTS += examples/benchmarks/cg/armci_sharedmemory/cg$(EXEEXT) # needs input file ARMCI_PARALLEL_TESTS += examples/benchmarks/lu/armci_blocking/lu-b-bc$(EXEEXT) ARMCI_PARALLEL_TESTS += examples/benchmarks/lu/armci_blocking/lu-block$(EXEEXT) ARMCI_PARALLEL_TESTS += examples/benchmarks/lu/armci_blocking/lu$(EXEEXT) ARMCI_PARALLEL_TESTS += examples/benchmarks/lu/armci_nonblocking/lu_nb_get$(EXEEXT) ARMCI_PARALLEL_TESTS += examples/benchmarks/lu/armci_nonblocking/lu_nb_put$(EXEEXT) ARMCI_PARALLEL_TESTS += examples/benchmarks/lu/pthreads/lu-thread$(EXEEXT) ARMCI_PARALLEL_TESTS += examples/features/aggregation/simple/simple$(EXEEXT) #ARMCI_PARALLEL_TESTS += examples/features/aggregation/sparse_matvecmul/sparse_matvecmul$(EXEEXT) # needs input file ARMCI_PARALLEL_TESTS += examples/features/non-blocking/simple/simple$(EXEEXT) if HAVE_ARMCI_NOTIFY ARMCI_PARALLEL_TESTS += examples/features/concurrency/simple/comdegree$(EXEEXT) ARMCI_PARALLEL_TESTS += examples/features/notification/simple/testnotify$(EXEEXT) endif ARMCI_PARALLEL_TESTS += examples/features/symmetric_memory/simple/simple$(EXEEXT) ARMCI_PARALLEL_TESTS += examples/simpleApps/transp1D-c$(EXEEXT) if ARMCI_ENABLE_GPC_CALLS ARMCI_PARALLEL_TESTS += examples/features/gpc/hashtable/test_hashtable$(EXEEXT) if HAVE_BLAS if HAVE_LAPACK ARMCI_PARALLEL_TESTS += examples/features/proc_affinity/computation_impact/computation_impact$(EXEEXT) endif # HAVE_LAPACK endif # HAVE_BLAS ARMCI_PARALLEL_TESTS += examples/features/proc_affinity/simple/simple$(EXEEXT) endif # ARMCI_ENABLE_GPC_CALLS if MSG_COMMS_MPI #ARMCI_PARALLEL_TESTS += examples/features/concurrency/multidma/multidma$(EXEEXT) # needs bug fixes #ARMCI_PARALLEL_TESTS += examples/features/non-blocking/overlap/overlap$(EXEEXT) # needs bug fixes endif if RANDOM_ACCESS ARMCI_PARALLEL_TESTS += examples/benchmarks/RandomAccess/simple/simple$(EXEEXT) ARMCI_PARALLEL_TESTS += examples/benchmarks/RandomAccess/smp_bucket/smp_bucket$(EXEEXT) endif if THREAD_SAFE ARMCI_PARALLEL_TESTS += examples/benchmarks/lu/armci_multithreaded/lu-block-th$(EXEEXT) endif examples_benchmarks_cg_armci_sharedmemory_cg_SOURCES = \ examples/benchmarks/cg/armci_sharedmemory/cg.c \ examples/benchmarks/cg/armci_sharedmemory/compute.c \ examples/benchmarks/cg/armci_sharedmemory/read_input.c \ examples/benchmarks/cg/armci_sharedmemory/timing.c $(atsrc) examples_benchmarks_lu_armci_blocking_lu_SOURCES = \ examples/benchmarks/lu/armci_blocking/lu.c \ examples/benchmarks/lu/armci_blocking/timing.c $(atsrc) examples_benchmarks_lu_armci_blocking_lu_block_SOURCES = \ examples/benchmarks/lu/armci_blocking/lu-block.c \ examples/benchmarks/lu/armci_blocking/timing.c $(atsrc) examples_benchmarks_lu_armci_blocking_lu_b_bc_SOURCES = \ examples/benchmarks/lu/armci_blocking/lu-b-bc.c \ examples/benchmarks/lu/armci_blocking/timing.c $(atsrc) examples_benchmarks_lu_armci_multithreaded_lu_block_th_SOURCES = \ examples/benchmarks/lu/armci_multithreaded/lu-block-th.c \ examples/benchmarks/lu/armci_multithreaded/timing.c $(atsrc) examples_benchmarks_lu_armci_nonblocking_lu_nb_put_SOURCES = \ examples/benchmarks/lu/armci_nonblocking/lu_nb_put.c \ examples/benchmarks/lu/armci_nonblocking/timing.c $(atsrc) examples_benchmarks_lu_armci_nonblocking_lu_nb_get_SOURCES = \ examples/benchmarks/lu/armci_nonblocking/lu_nb_get.c \ examples/benchmarks/lu/armci_nonblocking/timing.c $(atsrc) examples_benchmarks_lu_pthreads_lu_thread_SOURCES = \ examples/benchmarks/lu/pthreads/barrier.c \ examples/benchmarks/lu/pthreads/barrier.h \ examples/benchmarks/lu/pthreads/errors.h \ examples/benchmarks/lu/pthreads/lu-thread.c \ examples/benchmarks/lu/pthreads/timing.c $(atsrc) examples_benchmarks_lu_pthreads_lu_thread_LDADD = -lpthread examples_benchmarks_RandomAccess_simple_simple_SOURCES = \ examples/benchmarks/RandomAccess/simple/simple.c \ examples/benchmarks/RandomAccess/timing.c $(atsrc) examples_benchmarks_RandomAccess_smp_bucket_smp_bucket_SOURCES = \ examples/benchmarks/RandomAccess/smp_bucket/smp_bucket.c \ examples/benchmarks/RandomAccess/timing.c $(atsrc) examples_features_aggregation_simple_simple_SOURCES = \ examples/features/aggregation/simple/simple.c $(atsrc) examples_features_aggregation_sparse_matvecmul_sparse_matvecmul_SOURCES = \ examples/features/aggregation/sparse_matvecmul/sparse_matvecmul.c $(atsrc) examples_features_concurrency_multidma_multidma_SOURCES = \ examples/features/concurrency/multidma/multidma.c $(atsrc) examples_features_concurrency_simple_comdegree_SOURCES = \ examples/features/concurrency/simple/comdegree.c $(atsrc) examples_features_gpc_hashtable_test_hashtable_SOURCES = \ examples/features/gpc/hashtable/DistHashmap.cc \ examples/features/gpc/hashtable/DistHashmap.h \ examples/features/gpc/hashtable/GPCHashmap.cc \ examples/features/gpc/hashtable/GPCHashmap.h \ examples/features/gpc/hashtable/GPCHashmapHandler.cc \ examples/features/gpc/hashtable/Hash_common.h \ examples/features/gpc/hashtable/HashFunctions.cc \ examples/features/gpc/hashtable/hash_map.h \ examples/features/gpc/hashtable/HashUtil.cc \ examples/features/gpc/hashtable/test_hashtable.cc \ examples/features/gpc/hashtable/Util.h $(atsrc) examples_features_gpc_hashtable_test_hashtable_LDFLAGS = $(LDFLAGS_BASE) examples_features_gpc_hashtable_test_hashtable_LDADD = $(LDADD) examples_features_non_blocking_overlap_overlap_SOURCES = \ examples/features/non-blocking/overlap/overlap.c $(atsrc) examples_features_non_blocking_simple_simple_SOURCES = \ examples/features/non-blocking/simple/simple.c $(atsrc) examples_features_notification_simple_testnotify_SOURCES = \ examples/features/notification/simple/testnotify.c $(atsrc) examples_features_proc_affinity_computation_impact_computation_impact_SOURCES = \ examples/features/proc_affinity/computation_impact/computation_impact.c $(atsrc) examples_features_proc_affinity_computation_impact_computation_impact_LDFLAGS = \ $(AM_LDFLAGS) \ $(LAPACK_LDFLAGS) \ $(BLAS_LDFLAGS) examples_features_proc_affinity_computation_impact_computation_impact_LDADD = \ $(LDADD) \ $(LAPACK_LIBS) \ $(BLAS_LIBS) examples_features_proc_affinity_simple_simple_SOURCES = \ examples/features/proc_affinity/simple/simple.c $(atsrc) examples_features_symmetric_memory_simple_simple_SOURCES = \ examples/features/symmetric_memory/simple/simple.c $(atsrc) examples_simpleApps_transp1D_c_SOURCES = \ examples/simpleApps/transp1D-c.c $(atsrc) EXTRA_DIST += examples/benchmarks/lu/README EXTRA_DIST += examples/features/aggregation/README EXTRA_DIST += examples/features/concurrency/README EXTRA_DIST += examples/features/gpc/hashtable/README EXTRA_DIST += examples/features/non-blocking/README EXTRA_DIST += examples/features/notification/README EXTRA_DIST += examples/features/proc_affinity/README EXTRA_DIST += examples/README ############################################################################## # the end # .PHONY: checkprogs checkprogs: $(check_PROGRAMS) # support verbose/silent make rules for additional programs # sed SED_V = $(SED__v_$(V)) SED__v_ = $(SED__v_$(AM_DEFAULT_VERBOSITY)) SED__v_0 = @echo " SED " $@; # ARMCI_TAS_AS ARMCI_TAS_AS_V = $(ARMCI_TAS_AS__v_$(V)) ARMCI_TAS_AS__v_ = $(ARMCI_TAS_AS__v_$(AM_DEFAULT_VERBOSITY)) ARMCI_TAS_AS__v_0 = @echo " GCC " $@; # ARMCI_X86COPY_AS ARMCI_X86COPY_AS_V = $(ARMCI_X86COPY_AS__v_$(V)) ARMCI_X86COPY_AS__v_ = $(ARMCI_X86COPY_AS__v_$(AM_DEFAULT_VERBOSITY)) ARMCI_X86COPY_AS__v_0 = @echo " GCC " $@; .PHONY: pfiles clean-pfiles ############################################################################## # test suite # # Some tests were commented out either because they required an input file or # they were failing for unknown reasons and we didn't want to further debug. # SERIAL_TESTS = SERIAL_TESTS += $(GAF2C_SERIAL_TESTS) SERIAL_TESTS += $(TCGMSG_SERIAL_TESTS) SERIAL_TESTS += $(ARMCI_SERIAL_TESTS) SERIAL_TESTS_XFAIL = SERIAL_TESTS_XFAIL += $(GAF2C_SERIAL_TESTS_XFAIL) SERIAL_TESTS_XFAIL += $(TCGMSG_SERIAL_TESTS_XFAIL) SERIAL_TESTS_XFAIL += $(ARMCI_SERIAL_TESTS_XFAIL) PARALLEL_TESTS = PARALLEL_TESTS += $(GAF2C_PARALLEL_TESTS) PARALLEL_TESTS += $(TCGMSG_PARALLEL_TESTS) PARALLEL_TESTS += $(ARMCI_PARALLEL_TESTS) PARALLEL_TESTS_XFAIL = PARALLEL_TESTS_XFAIL += $(GAF2C_PARALLEL_TESTS_XFAIL) PARALLEL_TESTS_XFAIL += $(TCGMSG_PARALLEL_TESTS_XFAIL) PARALLEL_TESTS_XFAIL += $(ARMCI_PARALLEL_TESTS_XFAIL) TESTS = TESTS += $(SERIAL_TESTS) TESTS += $(PARALLEL_TESTS) XFAIL_TESTS = XFAIL_TESTS += $(SERIAL_TESTS_XFAIL) XFAIL_TESTS += $(PARALLEL_TESTS_XFAIL) if CROSS_COMPILING maybe_mpiexec=`if echo "$(SERIAL_TESTS)" | $(GREP) "$$p" > /dev/null; then echo "$(MPIEXEC)" | $(SED) 's/%NP%/1/'; else echo "$(MPIEXEC)" | $(SED) 's/%NP%/$(NPROCS)/'; fi`; eval $$maybe_mpiexec else LOG_COMPILER = \ maybe_mpiexec=`if echo "$(SERIAL_TESTS)" | $(GREP) "$$p" > /dev/null; then echo ""; else if echo "$(MPIEXEC)" | $(GREP) "%NP%" > /dev/null; then echo "$(MPIEXEC)" | $(SED) 's/%NP%/$(NPROCS)/'; else echo "$(MPIEXEC)"; fi; fi`; eval $$maybe_mpiexec endif # CROSS_COMPILING .PHONY: check-gaf2c check-gaf2c: $(all-am) $(check_LTLIBRARIES) $(GAF2C_TESTS) $(MAYBE_PFILES) $(MAKE) $(AM_MAKEFLAGS) TESTS="$(GAF2C_TESTS)" XFAIL_TESTS="$(GAF2C_TESTS_XFAIL)" check-TESTS .PHONY: check-tcgmsg check-tcgmsg: $(all-am) $(check_LTLIBRARIES) $(TCGMSG_TESTS) $(MAYBE_PFILES) $(MAKE) $(AM_MAKEFLAGS) TESTS="$(TCGMSG_TESTS)" XFAIL_TESTS="$(TCGMSG_TESTS_XFAIL)" check-TESTS .PHONY: check-armci check-armci: $(all-am) $(check_LTLIBRARIES) $(ARMCI_TESTS) $(MAYBE_PFILES) $(MAKE) $(AM_MAKEFLAGS) TESTS="$(ARMCI_TESTS)" XFAIL_TESTS="$(ARMCI_TESTS_XFAIL)" check-TESTS ga-5.9.2/armci/NEWS000066400000000000000000000000001500715745200137040ustar00rootroot00000000000000ga-5.9.2/armci/README000066400000000000000000000252721500715745200141070ustar00rootroot00000000000000Aggregate Remote Memory Copy Interface (ARMCI) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DISCLAIMER ========== This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor Battelle, nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS. ACKNOWLEDGMENT ============== This software and its documentation were produced with United States Government support under Contract Number DE-AC06-76RLO-1830 awarded by the United States Department of Energy. The United States Government retains a paid-up non-exclusive, irrevocable worldwide license to reproduce, prepare derivative works, perform publicly and display publicly by or for the US Government, including the right to distribute to other US Government contractors. FOR THE IMPATIENT ================= The command:: ./configure && make && make install should compile the static ARMCI library (libarmci.a) to use sockets and install headers and libraries to /usr/local/include and /usr/local/lib, respectively. Please refer to the INSTALL file for generic build instructions. That is a good place to start if you are new to using "configure; make; make install" types of builds. Detailed instructions are covered later in this file. QUESTIONS/HELP/SUPPORT/BUG-REPORT ================================= bug reports: https://github.com/GlobalArrays/ga/issues website: https://hpc.pnl.gov/armci/ ABOUT THIS SOFTWARE =================== This document lists the platforms supported by ARMCI and operating system configuration/settings for these platform. Additional limited documentation is available at ./doc/armci.pdf. Test programs test.c and perf.c are in ./testing directory. SPLASH LU benchmark it in ./examples directory. Index ----- 1. Supported Platforms 2. General Settings 4. Building ARMCI on IBM. 5. Building ARMCI on CRAY. 6. Building ARMCI on other platforms 7. Platform specific issues/tuning Supported Platforms ------------------- - leadership class machines: Cray XT/XE/XK/XC, IBM Blue Gene/Q. - shared-memory systems: SUN Solaris, IBM, Linux - clusters of workstations (InfiniBand, sockets) configure options ----------------- ARMCI should be run with MPI. PVM and TCGMSG message-passing libraries are no longer supported. ARMCI has been tested with MPI vendor implementations in addition to MPICH and WMPI(NT). ARMCI has been tested with TCGMSG by developers of the NWChem package on many platforms. GNU make is REQUIRED on Unix. For command line build on Windows, microsoft nmake instead of GNU make should be used. Historically, the TARGET environment variable needed to be set. This variable is now detected automatically by configure. It also detects whether the system is a 64-bit platform. Historically, the MSG_COMMS environment variable needed to be set. This variable is obsolete. Instead, options are passed to configure. Read on for details. There are many options available when configuring ARMCI. Although configure can be safely run within this distributions' root folder, we recommend performing an out-of-source (aka VPATH) build. This will cleanly separate the generated Makefiles and compiled object files and libraries from the source code. This will allow, for example, one build using sockets versus another build using OpenIB for the communication layer to use the same source tree e.g.:: mkdir bld_mpi_sockets && cd bld_mpi_sockets && ../configure mkdir bld_mpi_openib && cd bld_mpi_openib && ../configure --with-openib Regardless of your choice to perform a VPATH build, the following should hopefully elucidate the myriad options to configure. Only the options requiring additional details are documented here. ./configure --help will certainly list more options in addition to limited documentation. For most of the external software packages an optional argument is allowed (represented as ARG below.) **ARG can be omitted** or can be one or more whitespace-separated directories, linker or preprocessor directives. For example:: --with-mpi="/path/to/mpi -lmylib -I/mydir" --with-mpi=/path/to/mpi/base --with-mpi=-lmpich The messaging libraries supported include MPI. If you omit their respective --with-* option, MPI is the default. --with-mpi=ARG Select MPI as the messaging library (default). If you omit ARG, we attempt to locate the MPI compiler wrappers. If you supply anything for ARG, we will parse ARG as indicated above. The ARMCI_NETWORK environment variable is now also obsolete. Instead use one of the following configure options. Our ability to automatically locate required headers libraries is currently inadequate. Therefore, you will likely need to specify the optional ARG pointing to the necessary directories and/or libraries. sockets is the default ARMCI network if nothing else is specified. --with-mpi-spawn=ARG select armci network as MPI-2 dynamic process mgmt --with-openib=ARG select armci network as InfiniBand OpenIB --with-sockets=ARG select armci network as Ethernet TCP/IP (default) --enable-autodetect attempt to locate ARMCI network besides sockets SOCKETS is the assumed default for clusters connected with Ethernet. This protocol might also work on other networks however, the performance might be sub-optimal. Cross-Compilation Issues ------------------------ Certain platforms cross-compile from a login node for a compute node, or one might choose to cross-compile for other reasons. Cross-compiling requires the use of the --host option to configure which indicates to configure that certain run-time tests should not be executed. See INSTALL for details on use of the --host option. Two of our target platforms are known to require cross-compilation, Cray XT and IBM Blue Gene. Cray XT +++++++ It has been noted that configure still succeeds without the use of the --host flag. If you experience problems without --host, we recommend:: configure --host=x86_64-unknown-linux-gnu And if that doesn't work (cross-compilation is not detected) you must then *force* cross-compilation using both --host and --build together:: configure --host=x86_64-unknown-linux-gnu --build=x86_64-unknown-linux-gnu Compiler Selection ------------------ Unless otherwise noted you can try to overwrite the default compiler names detected by configure by defining F77, CC, and CXX for Fortran (77), C, and C++ compilers, respectively. Or when using the MPI compilers MPIF77, MPICC, and MPICXX for MPI Fortran (77), C, and C++ compilers, respectively:: configure F77=f90 CC=gcc configure MPIF77=mpif90 MPICC=mpicc Although you can change the compiler at make-time it will likely fail. Many platform-specific compiler flags are detected at configure-time based on the compiler selection. If changing compilers, we recommend rerunning configure as above. After Configuration ------------------- By this point we assume you have successfully run configure either from the base distribution directory or from a separate build directory (aka VPATH build.) You are now ready to run 'make'. You can optionally run parallel make using the "-j" option which significantly speeds up the build. If using the MPI compiler wrappers, occasionally using "-j" will cause build failures because the MPI compiler wrapper creates a temporary symlink to the mpif.h header. In that case, you won't be able to use the "-j" option. Further, the influential environment variables used at configure-time can be overridden at make-time in case problems are encountered. For example:: ./configure CFLAGS=-Wimplicit ... make CFLAGS="-Wimplicit -g -O0" One particularly influential make variable is "V" which controls the verbosity of the make output. This variable corresponds to the --dis/enable-silent-riles configure-time option, but I often prefer the make-time variable:: make V=0 (configure --enable-silent-rules) make V=1 (configure --disable-silent-rules) Test Programs ------------- Running "make checkprogs" will build most test and example programs. Note that not all tests are built -- some tests depend on certain features being detected or enabled during configure. These programs are not intented to be examples of good ARMCI coding practices because they often include private headers. However, they help us debug or time our ARMCI library. Test Suite ++++++++++ Running "make check" will build most test and example programs (See "make checkprogs" notes above) in addition to running the test suite. The test suite runs both the serial and parallel tests. The test suite must know how to launch the parallel tests via the MPIEXEC variable. Please read your MPI flavor's documentation on how to launch. For example, the following is the command to launch the test suite when compiled with OpenMPI:: make check MPIEXEC="mpiexec -np 4" All tests have a per-test log file containing the output of the test. So if the test is testing/test.x, the log file would be testing/test.log. The output of failed tests is collected in the top-level log summary test-suite.log. Building on other platforms --------------------------- On other platforms, only setting required is the TARGET environment environment variable. Optionally, MSG_COMMS and related environment can be set as described in the General Settings section. Platform specific issues/tuning ------------------------------- The Linux kernel has traditionally fairly small limit for the shared memory segment size (SHMMAX). In kernels 2.2.x it is 32MB on Intel, 16MB on Sun Ultra, and 4MB on Alpha processors. There are two ways to increase this limit: - rebuild the kernel after changing SHMMAX in /usr/src/linux/include/asm-i386/shmparam.h, for example, setting SHMMAX as 0x8000000 (128MB) - A system admin can increase the limit without rebuilding the kernel, for example:: echo "134217728" >/proc/sys/kernel/shmmax SUN +++ Solaris by default provides only 1MB limit for the largest shared memory segment. You need to increase this value to do any useful work with ARMCI. For example to make SHMMAX= 2GB, add either of the lines to /etc/system:: set shmsys:shminfo_shmmax=0x80000000 /* hexidecimal */ set shmsys:shminfo_shmmax=2147483648 /* decimal */ After rebooting, you should be able to take advantage of the increased shared memory limits. HP-UX +++++ In most HP-UX/11 installations, the default limit for the largest shared memory segment is 64MB. A system administrator should be able to. ga-5.9.2/armci/configure.ac000066400000000000000000000276031500715745200155150ustar00rootroot00000000000000# Process this file with autoconf to produce a configure script. ############################################################################### # Init autoconf ############################################################################### AC_PREREQ([2.67]) AC_INIT([Aggregate Remote Memory Copy Interface (ARMCI)], [1.5], [https://github.com/GlobalArrays/ga/issues], [armci], [https://hpc.pnl.gov/armci/]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([src/common/armci.c]) ############################################################################### # Must occur before automake init ############################################################################### GA_TARGET GA_CROSS_COMPILING ############################################################################### # Init automake ############################################################################### AM_INIT_AUTOMAKE([color-tests foreign parallel-tests silent-rules subdir-objects]) # Don't emit "rebuild rules" for configure, Makefile.ins, etc. AM_MAINTAINER_MODE ############################################################################### # Misc. information and package setup. ############################################################################### GA_WITH_HELP ARMCI_TOP_BUILDDIR="`pwd`" cd "$srcdir" ARMCI_TOP_SRCDIR="`pwd`" cd "$ARMCI_TOP_BUILDDIR" AS_IF([test "$ARMCI_TOP_BUILDDIR" != "$ARMCI_TOP_SRCDIR"], [AC_MSG_NOTICE([Detected VPATH build])]) # Determine messaging library up front because if MPI is desired we use the # MPI compiler wrappers instead of the standard compilers. GA_MSG_COMMS ARMCI_ENABLE_GPC ARMCI_ENABLE_GROUP ARMCI_ENABLE_PROFILING ARMCI_SHMMAX GA_DISABLE_MPI_TESTS GA_ENABLE_WARNINGS GA_ENABLE_CHECKPOINT GA_ENABLE_OPT GA_ENABLE_TRACE GA_THREAD_SAFE ######################################### # C compiler ######################################### AC_MSG_NOTICE AC_MSG_NOTICE([C compiler]) AC_MSG_NOTICE ga_save_CFLAGS="$CFLAGS" GA_PROG_MPICC CFLAGS="$ga_save_CFLAGS" AC_USE_SYSTEM_EXTENSIONS GA_COMPILER_VENDOR GA_WARN_FLAGS ga_save_CFLAGS="$CFLAGS" CFLAGS="$ga_cv_c_warning_flags $CFLAGS" AS_IF([test x$with_mpi_wrappers = xyes], [GA_MPI_UNWRAP], [GA_ARG_PARSE([with_mpi], [GA_MP_LIBS], [GA_MP_LDFLAGS], [GA_MP_CPPFLAGS])]) AS_IF([test "x$with_mpi" != xno], [ AS_CASE([$enable_mpi_tests], [yes],[GA_MPICC_TEST_LINK], [no], [GA_MPICC_TEST_COMPILE])]) # Hack to append .x to executables. AC_SUBST([EXEEXT], [.x$EXEEXT]) GA_TARGET64 # Establish the underlying network infrastructure (SOCKETS, OPENIB, etc) GA_ARMCI_NETWORK # Checks for C header files. AC_HEADER_ASSERT AC_HEADER_DIRENT AC_HEADER_STDBOOL AC_HEADER_STDC AC_HEADER_SYS_WAIT GA_CHECK_HEADERS([assert.h c_asm.h errno.h fcntl.h float.h malloc.h math.h memory.h mpp/shmem.h netdb.h netinet/in.h netinet/tcp.h process.h setjmp.h signal.h stdarg.h stdint.h stdio.h stdlib.h string.h strings.h sys/types.h sys/atomic_op.h sys/errno.h sys/file.h sys/ipc.h sys/mman.h sys/param.h sys/sem.h sys/shm.h sys/socket.h sys/stat.h sys/syscall.h sys/systemcfg.h sys/time.h sys/uio.h sys/wait.h time.h unistd.h windows.h winsock.h rpc/rpc.h rpc/types.h rpc/xdr.h], [], [], [@%:@ifdef HAVE_RPC_TYPES_H @%:@include @%:@endif @%:@ifdef HAVE_SYS_TYPES_H @%:@include @%:@endif @%:@ifdef HAVE_SYS_IPC_H @%:@include @%:@endif]) # Checks for C typedefs, structures, and compiler characteristics. AC_C_CONST AC_C_INLINE AC_C_RESTRICT AC_C_VOLATILE AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_INT64_T AC_TYPE_OFF_T AC_TYPE_PID_T AC_TYPE_SIGNAL AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_TYPE_UINT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_CHECK_TYPES([ptrdiff_t]) GA_FUNCTION GA_DISABLE_SYS_WEAK_ALIAS GA_SYS_WEAK_ALIAS GA_UNION_SEMUN # Checks for C type sizes. AC_CHECK_SIZEOF([void*]) AC_CHECK_SIZEOF([char]) AC_CHECK_SIZEOF([short]) AC_CHECK_SIZEOF([int]) AC_CHECK_SIZEOF([long]) AC_CHECK_SIZEOF([long long]) AC_CHECK_SIZEOF([float]) AC_CHECK_SIZEOF([double]) GA_C_POINTER_AS_INTEGER # Checks for C library functions. AC_FUNC_FORK AC_FUNC_MMAP AC_FUNC_SELECT_ARGTYPES AS_IF([test "x$ga_msg_comms" = xTCGMSG], [AC_SEARCH_LIBS([xdr_array], [rpclib], [], [], [])]) AC_SEARCH_LIBS([floor], [m], [have_floor=1], [have_floor=0]) AC_DEFINE_UNQUOTED([HAVE_FLOOR], [$have_floor], [Define to 1 if you have the 'floor' function.]) AC_SEARCH_LIBS([pow], [m], [have_pow=1], [have_pow=0]) AC_DEFINE_UNQUOTED([HAVE_POW], [$have_pow], [Define to 1 if you have the 'pow' function.]) AC_SEARCH_LIBS([sqrt], [m], [have_sqrt=1], [have_sqrt=0]) AC_DEFINE_UNQUOTED([HAVE_SQRT], [$have_sqrt], [Define to 1 if you have the 'sqrt' function.]) GA_CHECK_FUNCS([bzero fastbcopy ftruncate gethostbyname getpagesize gettimeofday memset munmap select socket strchr strdup strerror strstr strtol _lock_try _acquire_lock xdr_char]) GA_CHECK_FUNCS([pthread_getconcurrency pthread_setconcurrency]) # Checks for C libraries. ARMCI_C_OPT CFLAGS="$ga_save_CFLAGS" ######################################### # C++ compiler ######################################### AC_MSG_NOTICE AS_IF([test "x$enable_cxx" = xyes], [AC_MSG_NOTICE([C++ compiler])], [AC_MSG_NOTICE([C++ compiler (disabled, but some tests still required)])]) AC_MSG_NOTICE AC_LANG_PUSH([C++]) # GA_PROG_MPICXX is required to silence complaints that C++ source exists # even if C++ support has been disabled. ga_save_CXXFLAGS="$CXXFLAGS" GA_PROG_MPICXX CXXFLAGS="$ga_save_CXXFLAGS" AS_IF([test x$with_mpi_wrappers = xyes], [GA_MPI_UNWRAP]) AS_IF([test "x$enable_cxx" = xyes], [ GA_COMPILER_VENDOR GA_WARN_FLAGS ga_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$ga_cv_cxx_warning_flags $CXXFLAGS" AS_IF([test "x$with_mpi" != xno], [GA_MPICXX_TEST]) # Checks for C++ header files. # Checks for C++ typedefs, structures, and compiler characteristics. # Checks for C++ type sizes. # Checks for C++ library functions. ARMCI_CXX_OPT CXXFLAGS="$ga_save_CXXFLAGS" ]) AC_LANG_POP([C++]) ######################################### # Assembler ######################################### AC_MSG_NOTICE AC_MSG_NOTICE([Assembler]) AC_MSG_NOTICE AM_PROG_AS ARMCI_AS_NO_I386ASM ############################################################################### # BLAS/LAPACK are used in one test program. We don't locate these libraries due # to many of them requiring Fortran. An enthusiastic user of the lone test # program can still build it if they are highly motivated -- but the build will # not be automated. # # The ARMCI tests are still supported when building a compatible # libarmci from the comex sources. Depending on how comex is configured, # this may add BLAS as a dependency. The COMEX_BLAS test will add the # HAVE_BLAS automake condition, however HAVE_LAPACK will remain forced # to false in order to disable automatically building the lone test as # described above. ############################################################################### COMEX_BLAS AM_CONDITIONAL([HAVE_LAPACK], [test 0 = 1]) ############################################################################### # Checks for programs. ############################################################################### AC_MSG_NOTICE AC_MSG_NOTICE([Checks for additional programs]) AC_MSG_NOTICE AC_PROG_GREP AC_PROG_SED ############################################################################### # Libtool setup -- no compiler/linker tests after this ############################################################################### AC_MSG_NOTICE AC_MSG_NOTICE([Libtool setup]) AC_MSG_NOTICE # temporarily restore unwrapped compilers # this works around a bug where libtool sadly relies on matching compiler # names in order to determine features (Fortran only, I think) # libtool doesn't recognize MPI compiler names, nor should it AS_IF([test x$with_mpi_wrappers = xyes], [GA_MPI_UNWRAP_PUSH]) GA_AR LT_INIT([disable-shared]) # and now that that's over, put the MPI compilers back # also, the above hack incorrectly sets the base compiler as the linker AS_IF([test x$with_mpi_wrappers = xyes], [GA_MPI_UNWRAP_POP compiler="$CC" LTCC="$CC" lt_save_CC="$CC" compiler_DEFAULT="$CC" compiler_CXX="$CXX" compiler_F77="$F77"]) ############################################################################### # Remaining setup -- some tests, some individual components ############################################################################### AC_MSG_NOTICE AC_MSG_NOTICE([Miscellaneous remaining setup]) AC_MSG_NOTICE # Establish some preprocessor symbols. ARMCI_SETUP # Set up TCGMSG. TCGMSG_REMOTE_SHELL TCGMSG_ENABLE_TIMINGS # TODO not sure, but thus far required for 'dist' target at least AM_CONDITIONAL([RANDOM_ACCESS], [test x != x]) # always false # Whether to use PIC for the inline GCC code. AM_CONDITIONAL([ENABLE_SHARED], [test x$enable_shared = xyes]) AS_IF([test "x$ga_cv_target" = xLINUX64], [AS_CASE([$host_cpu], [x86_64|ppc64], [AC_DEFINE([NEED_MEM_SYNC], [1], [Creates memfenc macro])])]) AS_IF([test "x$host_cpu" = xPWR4], [AC_DEFINE([NEED_MEM_SYNC], [1], [Creates memfenc macro])]) ############################################################################### # Test suite setup ############################################################################### AC_ARG_VAR([NPROCS], [number of procs to use for parallel tests (default 4)]) AS_IF([test "x$NPROCS" = x], [NPROCS=4]) AC_SUBST([NPROCS]) AC_ARG_VAR([MPIEXEC], [how to run parallel tests if built with MPI e.g. "mpiexec -np %NP%"]) AS_CASE([$ga_msg_comms], [MPI], [AS_IF([test "x$MPIEXEC" = x], [AC_PATH_PROGS([MPIEXEC], [mpirun mpiexec]) MPIEXEC="$MPIEXEC -n %NP%"])]) AC_SUBST([MPIEXEC]) AC_SUBST([TCGEXEC]) ############################################################################### # The End ############################################################################### AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([tools/armci-config], [chmod +x tools/armci-config]) AC_OUTPUT # Report on what we found. AC_MSG_NOTICE([]) AC_MSG_NOTICE([**************************************************************]) AC_MSG_NOTICE([ $PACKAGE_NAME configured as follows:]) AC_MSG_NOTICE([**************************************************************]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ TARGET=$ga_cv_target]) AC_MSG_NOTICE([ MSG_COMMS=$ga_msg_comms]) AC_MSG_NOTICE([ GA_MP_LIBS=$GA_MP_LIBS]) AC_MSG_NOTICE([ GA_MP_LDFLAGS=$GA_MP_LDFLAGS]) AC_MSG_NOTICE([ GA_MP_CPPFLAGS=$GA_MP_CPPFLAGS]) AC_MSG_NOTICE([ ARMCI_NETWORK=$ga_armci_network]) AC_MSG_NOTICE([ ARMCI_NETWORK_LDFLAGS=$ARMCI_NETWORK_LDFLAGS]) AC_MSG_NOTICE([ ARMCI_NETWORK_LIBS=$ARMCI_NETWORK_LIBS]) AC_MSG_NOTICE([ARMCI_NETWORK_CPPFLAGS=$ARMCI_NETWORK_CPPFLAGS]) AC_MSG_NOTICE([ CC=$CC]) AS_IF([test "x$with_mpi_wrappers" = xyes], [ AC_MSG_NOTICE([ unwrapped CC=$ga_cv_mpic_naked]) ]) AC_MSG_NOTICE([ CFLAGS=$CFLAGS]) AC_MSG_NOTICE([ ARMCI_COPT=$ARMCI_COPT]) AS_IF([test "x$enable_cxx" = xyes], [ AC_MSG_NOTICE([ CXX=$CXX]) AS_IF([test "x$with_mpi_wrappers" = xyes], [ AC_MSG_NOTICE([ unwrapped CXX=$ga_cv_mpicxx_naked]) ]) AC_MSG_NOTICE([ CXXFLAGS=$CXXFLAGS]) AC_MSG_NOTICE([ ARMCI_CXXOPT=$ARMCI_CXXOPT]) ]) AC_MSG_NOTICE([ CPP=$CPP]) AC_MSG_NOTICE([ CPPFLAGS=$CPPFLAGS]) AC_MSG_NOTICE([ LDFLAGS=$LDFLAGS]) AC_MSG_NOTICE([ LIBS=$LIBS]) AC_MSG_NOTICE([ FLIBS=$FLIBS]) AC_MSG_NOTICE([ AR=$AR]) AC_MSG_NOTICE([ AR_FLAGS=$AR_FLAGS]) AC_MSG_NOTICE([ CCAS=$CCAS]) AC_MSG_NOTICE([ CCAS_FLAGS=$CCAS_FLAGS]) AC_MSG_NOTICE([ DEFS=$DEFS]) AC_MSG_NOTICE([ SHELL=$SHELL]) AS_CASE([$ga_msg_comms], [MPI], [ AC_MSG_NOTICE([ MPIEXEC=$MPIEXEC]) ]) AC_MSG_NOTICE([ NPROCS=$NPROCS]) AC_MSG_NOTICE([]) ga-5.9.2/armci/doc/000077500000000000000000000000001500715745200137645ustar00rootroot00000000000000ga-5.9.2/armci/doc/armci.pdf000066400000000000000000002670041500715745200155630ustar00rootroot00000000000000%PDF-1.2 %âãÏÓ 2 0 obj << /D [1 0 R /XYZ null 766 null] >> endobj 3 0 obj << /D [1 0 R /XYZ null 53 null] >> endobj 4 0 obj << /D [1 0 R /XYZ null 727 null] >> endobj 5 0 obj << /D [1 0 R /XYZ null 700 null] >> endobj 6 0 obj << /D [1 0 R /XYZ null 684 null] >> endobj 7 0 obj << /D [1 0 R /XYZ null 668 null] >> endobj 8 0 obj << /D [1 0 R /XYZ null 652 null] >> endobj 9 0 obj << /D [1 0 R /XYZ null 638 null] >> endobj 10 0 obj << /D [1 0 R /XYZ null 609 null] >> endobj 11 0 obj << /D [1 0 R /XYZ null 588 null] >> endobj 12 0 obj << /D [1 0 R /XYZ null 308 null] >> endobj 13 0 obj << /CreationDate (D:191001102164319) /Producer (\376\377\000A\000c\000r\000o\000b\000a\000t\000 \000D\000i\000s\000t\000i\000l\000l\000e\000r\000 \0003\000.\0000\0002) /Title (Untitled Document) /Creator (FrameMaker 5.5P4f) >> endobj 14 0 obj << /D [1 0 R /XYZ null null null] >> endobj 15 0 obj << /D [1 0 R /XYZ null null null] >> endobj 16 0 obj << /I << /Title (A) >> /F 17 0 R >> endobj 18 0 obj << /Length 4648 /Filter /FlateDecode >> stream H‰œWKÛȾðè#X4ß½y7Hb#ö›I.ëzÈ–ÄX"Šò¬ö×§ª¾jJšq `Ä®®ª®÷ãLJׯÞþ)1‰yؾ~ÕDMibú“,΢¢2Y5•y8¾~›aÿùo‰ÙøôÐÊÿ§×¯‚$|øsJ•S’EM*¬ð•ÄqTU¦Jâ¨LÀ,Šã´þÎKáôkðî—?½ÿÁ¼3ᦎÊàs˜ÇQŒaåÁnÒ4Ê‚%$š*°aÕÁ£\œy· “8Ø…›Šæ0#j·³‹3¿¸c¸)¢D¹äã=dÆán’¨ ôåŸÆ‰ùyàûŒ—°¢£T ·³­ ÿùðá΢0§I‹8ªSÖY”Á˜¢t•ÀˆQœÄ¹êÿz4aÑÃMF¬g~5Ná&']ûpÓˆÔDž$™·LÑC’7úaLHWYð3k:·à¶ÜÑ?*ýýSJ™ée Ü•qû‹ÚØûZ¾ÒœÄ/LI*WÙêë2¹ú:ËT×vbê¾Ò£Ið‰ Ÿ’’#ë³cK‰Š`RðÖX‡Î|ÀWϾ œ8É 4æÃ¢®îÈʈ‚¯¤ØÎ›UĤôÞˆ“J%ü¦9ÆñiÙ@¿Ç–؆){]O­`˜OcXþ¼ìŸœ@N‡M°˜O`±ô@ä¿Úü¯ÀyTJ1ÎVkz©½SS¥Qܘ¢I£ä&®âòjìÊÖÇqé¿… ©,êä •Xн>¤%ò¯ƒm¦Œ÷•c¯ vs(¡4ž‡îEœßUŽ&¡* ‡kÄ™·nUøT7çOEñX²ÂüÇhBA`qEy}èwøÞãOî .Q8þ£lÂáȧ†#^HQ€$ðÍõàšV‰&Ö;[Áý‰Ã4 Ì€sPÓ"È¥å„_ Lånñðá òºƒ}¯€Ó¢˜½èùˆ×Ï^ o”™¢Þó¾Šñ/x>7*zºÞØÙ©Ð"­ s÷ÌÇkŸº«?8¥÷ÆVé•M¢ƒD©cªþ÷áZõ¯i¶½¥?:s K6r&原2§Ü‘¿#$™ù˦œÑ”\)ûíÉ”æ5—ü\*•Ô c‰á†Ã;?7ôbî…ôŠÏH½÷¬5ÙjË9W³Yf;ð‹[6%•ÆÂÊI¯È6ƒœ—~wÆÍ™)Í#•¬¯rþU''+/è48PàÚ'06ËV).$÷=/¾?ÔÚH…ÍÑG•\-ÊžäëIþkEyøƒ ¸ä×€ZE'G%EÍ·8.hv½ïœˆ›Áøbi1È£¸Î *ÞãEê=¾vùcÈeÕ…,¾[ùž._89`»nY©§%9íôðô¨:<^ »D™äîKø¼B‘¬y§7RéôÁRÕ¾Üsr\DØëȆ_ÄìkøP°Ú"¦àˆÙˇx® ”~Ø™ (ªpqç"#Q—r®ñµ"sTÔ9úö|°Ëb¹ (Ïx¿Ý‹âvŸw⤂%"x/°VïÉÙN^0Ë(àiTÙ}¢–ÔóúE¦fëxRç¾eOaƒâÊÅd‹ «¹ŒJ ZQÆÒ°ä¥T$9á°„¹¤5…Q!áJèOÜ}¨T-{ìûÿ-¤@q™wƒðoÑ2/f¸hyÂã¸tüŠ÷Ì*¦±äW–}k©!ƒƒ—‡ÜÉG»Ee˜Ý¿ÏïV†Î JÞꯒQoâÓy<ß1ïÀÂZ³Ü1“ÂCX[HïfÿK°õj­Y+Õ³^ išª{ÏQ¿É\l¥·å(Ú ‡’愌v-Ï#r=ÂG/¶¤ pÛDàšÈi©5G w–¾ÃW¯îŽw/rŒ€HÙÄ IRESa£mØ¿bTE»U‚|¸8…¿æ;èÏL–=èÜ-¡òRÈJ7ï.Zõ¾#è‰êаá–¨æM0Þ*º½iíÉ)ä yQª64s”ø«MU>µ6)L™[5;¥˜ä¶j¡¼O÷±á‡­$®ý´Õs8åƒRIèý…â ]QÞŽÇ#l0hb(„gl 8]𳸣¡„‘¦ êý°cžÎ¶ æ®CìÐ!é,Âć©G®ÝrüW·‚tv‘A?ƒ‚…zŽsÇ€‡“';󂇫£Ã•؃(ŒóÀâôX0žÙê:™&Ta¬Öže–ÑYO§Å“úŽòhµ˜ŸdŽÔÐê(…ñK ê§$ÌVÞ¸ò‚N½~‡é:…ÝÔ{×íTÆuülÏ·ß: R§ªÎK‰{éup¥ycéý¼|2÷C悱{a®b˜>WeK‰Ùˆ=PjÔË ¥2±ÖY_S¹Ö‰˜:ûÕ ª¬ðrTΟÊl§;§Ùµñ7~¥Ðëu)ë¸Öc=ãÆÊñ…QçÝç÷¤¦]d†Ë0P(ð”‡}`…’xf²Üµ«À_ËœÀú+7}Í ”òh‘åDÖDcu<ã±Å@RjƒÂu¦³l®­§Q©e }Ÿ“Œ¼Åû€×¹¿½£VÍþú`/PëŒ)ˆI¯AÎPÑ=ïLÙuÿlüäoøèQ×ê‘}Tz¡ÖЩÅb¢¡ò_­zžhõšh'ˆG€NºÎá°øÈ‹X9"ù;hêëºv ‹œ>¸†?—Ó疱뗲Sêáfƒ¤¬{â©.¿nk7RÑÖ¥aÿGÛC¯zÞç+§› U0<'¿ñé•&éšÿýïî¦øM·GßÙ|¿ÅIšhl¥ì-W}êbZ}õ½­áM­AñØe:­ê-ò½Ÿ(CÒš7?¶ó‹ŽÅ©1ƒ£2¶×§´ýhrÝ‘ŒvŠß¸-ZH0Ü&ùšc?ôG¨ïxÚDó<>úg¨™3Ã;7S”¬]†A»g•§ôFIsß–±ê¹Û}ð‰¥¡ÑC7¸¯TJh¡”Å×) MÀü†'X|y·DîL×ËÙíò¿vSn»dÁÑXy`ë”ÿ´JPÝK4-X-GHÃòE#ƒu;…ž²ÖaêÍK/¼Ø4Aöœ¡‰®?G0¤R†ÁáZÊzàQ<‹E]õ¸¥™_Aç3–ºEW¶[&Åzôåvž±³§eÓéöDFco§¾±u šuÍ !ÑUÆ H;wX‘™ãÂK}ðb”à"`=,²ÎòžÄ\Þ„©ê¨ÞWñ29ñXʲ`¡¨t•üª"…kzq5½Š Äa±;Hä±°n•«…I.w/[zeÔì#Í vÁÕÎî^Zös®Çùž!Þœàdª;ýó‚²n£Mé{Êï/÷·/º¢Înë–vßãv‡öúvo‘¿Ê¦K–L°ôA±Iõ„ý¬½V©‘½Ð«ÄmU:ÑHî”ýåî-Ýz™M¤Œö~0“î~”œ=Uœ7fvGÈq;Z ÖŠ˜G7¿´€þbÕTÞïÈŽetz–0•ßê$Yûw-mgƒà—‰È¹5›ÎnOŠ(©\Éø ™UÁÚËÆó‘é‡`]Ïã|; â$ÿ¥ºe{¥Öj‹”ø;Ô® Øm8py¡¿;ð˜E™X!|kÆìûP6“SÉ0ž]E´Cë¤G•J¤q,óén*ò¯‚ݶ z/ÐØ£XI(‰"utÓ´Pøâæ@S+‰$²$UCß™÷fIJu^lírfvvvwÞ{zFXgt«:B‰IHQâ?GdyŠíÔ–‰àžìE|dN˜žuì`è@SÒ_Aõoœ5‡Œa¤FmÍE2Ƈ~\Ê[Wêìí›ã0Ë‹CAª&íÙ7Úgðùu_o ¸Æ$mfþ Þbò“(˜Îˆ!n“‘Y^,‚JÚÂVjÆP¶cŽü†£w¬¢ÀÕ‡d攬%ÑÔ}ùõ»¤c­o’P{*'¥¦A8]È_³ØMCyêÍc)×ó(OÔfõ¤yðK}íFb€}Yä>ÈØMéNšRK–  6ÓFº@»rÌø0&ƒz‡ñTá£Èo0ÃBÏ ä ØÊš£Ï‹í…D¦ +Om±SeÍv kÿCaŒ~Í­´ä¨F®›ÐUŒH¢Ï "j µÙK¶ùÎóH]ýsÀ¥Ør-ŠÈ 3©Uw¶PGæ3hW°b/Ü}ÔöA¸oWô»Ï´[¼*^/‚V ™| •¸\ííóÃúÓº\? ï’Ù3¿D~åÈezÒÑ"<]dß°½S’Yv8ØîûÊÞ`A: OK;úãD±Rsœ¿"¤£ûüûÊUø `DÚm'àô%ÔÀÀ)¹£þõ3EÈš Rgı±¼ s(- {ÔÖ[.’~=XHîåî{\÷‡Ç§u È©r2÷<ÅϯwŽaÏôbîs--br*ñõᇋíÔ\Û÷;ß²é§}ÓGÊ[:Ö?–99ž%žÁ±od @üBµŸåýSÄ^i1&*òpæÁ˜¤üZdAµžd¿€ÅûÎ0M ´—J{ô܉Þi}ôx/H}áb2ÅtÁyÎA(:šÜÌ„Ø}-”¬Úpi'pϧ±¢õê^×ó îr*Ù›·ì¯cð*Û¤µžÚë«0)P§¥“ò·¼^v˜—A›‘­*ž¢¶HÍWõˆu%‹”eWúp¤çp9`¶GûvXÉA2ênL¸¬6Á;]UáBà à쮯a¾Ç÷N…ØZ™]¨-ý“ÀIN% È+P-"»Y*2ÈJ ê±,ZÛ†¥ewé†q³äVmaÅ ‹SÞ2½F†aŠ|•wŠjHlNz:|2•gŠ8‚™Í˜­ÄÚô¡3÷âÛW/:*ÆSøØ•¾p®ÄEkx¤'pôD\²q*&ý'(}êa·ÐÝœ•n ôiª´ ÀÚ¥7ŽS)â;¤ó›nòö/ûg<µb!‰Hh…ºfGP{I®AïÕÝD|=3À6îÐ)nxååsüÞXë•Ò[ìo±ÌZÆä@Ìœyʓӥ¥[b´%¾ˆÖñy5×gy)ŒÇ´»£›'ì™äÆÙúåÕ:Ùu¾Œ ù OÊÊ2&Xâ‚Ý&3Žõmã!ö±µ·?­îo$’hÛÀàÍk<}<ø§/ÿUS[ˆ:9lp¼µÿû ŠÜ’‘yHe6›á¼Õ2FŸ„'ÄD}¡|‰$1µ@¬R„b7;¹A*IÐ=/ú\c¶€˜4ÏJ­o‰Õ‰µÊ{ü›`êD3TòºÐ2ÓªdÃ#ùQ³ƒ®Ñ0ÓÑjBÿéwôŸ\éÀöôÜ"t;9’Ó&3]´1±cê.7=xÆÈ>zú:꺓!Ó„³"n?\0ùGvÚnÑø7ãž5noŸ]:è·çlÆq#í cÁn:ot<ö5KéôŸì écmèé¶BßæÁ²¤ûQŠ´è21‡{”ŠîlM©Ú2ä^Ñ1»6-˜ÕŽïB×jgÚ2o:µEnFÚM ÇŠª"4•˜ME?“ݸ¬¨ƒ³Yh™G9mVacBº }W§ƒ6Ao]E‹e2óÝpÎ[šMX‹‰…4Cõ®NÔ1ë´k75ú•¦^<ædj-†„è]]Át¦uŸyé§ò¬¶€ëérl¥‰GD$¯À>Æ4QæZ4J`yãÔbôi¦¥$6•Ö¬ÞÚ«eïËëYiÚVÒ°-µmÇ8¿¤KÂ\u‹¿Â …ÆE×¾ˆmÞ–DÖ¸ÏëOë;÷›6”Ñ_r‰Îömýx(^tòÎékƒ Öè=ªd´~S>þ¸D™Wú¬S%œ°ûôåçŸþ˜÷  endstream endobj 19 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F2 21 0 R /F3 22 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 26 0 obj << /D [25 0 R /XYZ null 730 null] >> endobj 27 0 obj << /D [25 0 R /XYZ null 580 null] >> endobj 28 0 obj << /D [25 0 R /XYZ null 339 null] >> endobj 29 0 obj << /D [25 0 R /XYZ null 279 null] >> endobj 30 0 obj << /D [25 0 R /XYZ null 211 null] >> endobj 31 0 obj << /D [25 0 R /XYZ null 193 null] >> endobj 32 0 obj << /D [25 0 R /XYZ null 146 null] >> endobj 33 0 obj << /D [25 0 R /XYZ null null null] >> endobj 35 0 obj << /Length 4997 /Filter /FlateDecode >> stream H‰„WKoãF¾0ÿ¡äÆbćHq÷äõ&Y0Ƙ1ö2“C‹jIÜáCK‘ãèßoU}Õ’èI°ØÝÕõ~|ýÏ—÷ï~ü96±yÙ½WFen–ô'é2V…IWQY˜—öý»¥Ùõ/Ÿb³?ñꥒÿ¯ïßIøòß÷ïIeiZ˜|-ãļü‹¢å2)„vAßq\ÈÏÁË!\GEàÌý~/_C¸ˆ£Á]µÜÙêí­™•Šk[šýòâÊŠÜZM““Î| ºÞ¸£®+QþOÝÌÇ5e¨rgþÑ;TÆ^ÕÊ Oyû}>:g>*/»¸tìŸÃ˜] œ3ðd[…¹~¼ó¨=J»r[sTŒÔï=RØæáÕÔ8ýtcº#ì‚1äÆ38Û6uçd0×-ÀU#G®¥idj­Åb{¡Uô Å(9–ÝzôðTŠOà £BIkî¿å³¸zésЪ´ÊEûÒ‘ó²¼Bºµw&}«3 ³ÐPçBàžÊ6SG‡Ökè“°>¬GPɀ犷8|$ãžÝrL:5Ž;AZ¸dCjWgyÄT!cÀ%Zpõgdûƒw½H>PâpÎ|rÂ$PH\¹–€‘Ÿl½—ãÃxQô5äš±ºÚØ(¥lùà \™%V =ýûÔÆƒW†×ä·jÁ%ê×Fݽ÷ó_ ñ›fû¸1HJ¸¿dÒm°àÔnzY9-ý«¤µXìéöKÉÏ B³ì—îòà±mÎB*h1ãWΊs¬w|!ÖÇ ·x ¾(72­ @Øéµ ç{*ðR2FŒW­ïôcìÁä ÉÖÒBi tÒëyLØrFf®HsP'xc¿õî}úZ·Õ—¶Ý:Yíÿ²/³µ¢¾ÒƒÃ(ÞÔûIa«€ˆ‘%<Õ+ЖÆqŠFù¨G=PðÐ.°V„bWÕ;ß· T×jåÁ¥øû¶] ~$¤/78/ipwŠÍYG×òdWfä]Îú8a¶ýÑcñÙkJÎÞ2ÀöµN½05ç7ÃüòÐLã›QÎ H™Ïö+·‚-5sËs‡Ê—^‡Ì°äÁÍÓ7“'a"=ûúmªÉŒ¢½ç[VöF‡%¬ô?Í€ÅA40¶Â:@·“’€ãW~±òê§/É]ôoÎhp2þç Ù«Ü â#nhb¤9ÚáVt%7&ØÊ¯ Gò>#âÁPLv_$C 7U,Ô‚qß½}‡–Þóqâû ‡ºDN‚G£J¥&J¼×H¹Ê²‰k1A^+×w“ì4€_ŽMއeÄ€%÷ïÏHªÎ2yœ®ù¹9Bf/»T³1÷RªÙX·j!ÜJW=wäLºþZ"Ww£TÜtuöÓè­H¥!Ê¥Û Ë/á?Ì¡SÆ/ôª Ó"J¥¯z#ÉÛ¹àêÃüP- ˜` œÔ{[Óac4g:÷F«|E½³ú–Þƒ³¥‰QêñÌÇ\2VrJsw‘<‘hœa{FiC¤¦@}– ¸vØëƒ¸y%uÌóºDûÆpðÔÊ|‹Ú‡g2Š¢Íù)Án¡ 7:z<ù©Â©D‰TJ“WÄÉ÷ÿƒƒgP¼RÛâ'Ùw–º-Á3Üy=8?>¨ÇÑp‡u·¿²6`då¤uf¦?wº?D éIë›Ê¸¹J£¸ug>P ^Á Gw—ž½ ü-þ?îQO» kÍ–ú~ÊÃJ*ˆ¦ÛÀóüF‡âV‡\Ûv9Éuzʼnº3 )ª›-[ÄŽŠŒ-¡²Ôä`¯Öôqæ§àüa íœö°Bñr- ˜¡÷Ž{"ÅÁݪDÍnî¶åñ•{ùž×Ÿ4¨4O5 -JŠÞOò8«QRh,ZK(ØÿMõà¶h"³6f˜ËàŒ%;šb˜¾S¶ZªèmÕ±©lÈô)©rŽÓeò–ZÑvÁ³”dm­—Æá朡‘6‘O}ë´hýûn&Dx‘®Ûi+í@ÍÝ7V z™Œ¯ÃmF{'æiz™¯9 ‰ù}vÙcò†pKäTbµ9ðÎLDBÓX$ÓcHj[þœßÓÕV€g4…T ,¼ìZr¶÷ìO4V¤æŒUsÏ—ÆÛZð>í×"·ò½@ ³cªÌË‘öLèå8ç0ñºã[·ÆÜðA£Êð}ðú*J…)“0£áGSQÅÏôÌô*Íq¤ÔÅkcÝÌ:xg’3T-¿°hÅ®«•’õ|[Ù…®/¡ä—Ä‘S‚mý?áUÓã8rCïòê(m·%ëÃÊm²› ú0É`·\d«ÆVF–Y§÷×/ùK¶;³4ÐVU±HVùø‚1Cˆžð…€Ü Ä t…Ð.@?! >­HÙ{EÍRªóÆêª„r { è^ÁeS³6 ==Є\”òP ů†V®”½hiÄ×ÀõÉqÌ\X¯tœ_0AŸ¥Ú.Ýë¡5ÏëÏ„’«‘i¢¾Þ½?3=侃ùbZŽƒÉl蛡Z`¿¼ÇcžIjHªÞ–ÀÆ;ÐPi-ü’*ýÿtó€v~÷!Väý¤Àò¢j›ݠdš­ -‹±G@ÙÜ%µ‡nTÄ”ÄÍJ΂Tj¡B™™oÝ ÅÕ¡ãÏW*ócWŸØµ°„o"0k½œ,¢hOKÊû©ADiïÒÃÐïå~š”!ã_Øä݇õõ«èãwròv°e½ª>8çè”݃7_{®N5¸olU:UIù­·«°ŽK›Œ¬Ǹ`MÁWÞ»ãýÔº{jw—®]k—ÖÁ9”nñv!çtNߥ{Ò܇=®‘€ë‰>m?/o Y±ÊfÚ®ºaûÚ›Wk€Ì¹*tFÖ×ÐEN±©ëÙØØ-YßC%ƒrFÎÃéTœ†pмֻo红%kÝ”®9ç+"ûi1»kÅ‹ ïwë Í+î?Z/º–‚®EuÛÕÛnf#+Ônoû“œ±X7$XT!4™ko„>íÓ»Ìo9(UÛï: å ¥ ÊR”)–òœR²tÔ@cEN´8©¼·á¦¾mÚX¥-I1¥µ}ÞûéIó€äYŠÞZÂ[\Æ3…ϵ¬EO<”øõ<˜°¼É(ÁY3n¹ùüCê$?K–¦n%òS¦ËUå’*[j}>†*0×êÕz®Õ?Jª.*ÍÕXñÙ}_kȇ·_9Æ*úi/;›GaºŒžÃsp"1'„KpÕ Ž”„%ñtæ I5ûë~-È£ï0zq–åðÊá3Æòœÿ[ïû 4ªr6)‚Ešé·ìv[€Aa Íû€S\‰hÈ;5[ÌO$ý,nðá„ÀÆpªÐ\ô÷d4âdÓ£|pWç 9àóB=ÎÔíàX˵~é^4*q7ÓgƒUô™³<Ûh¦+w)JÁð±žÂÒpOk~<åÉ6¤!J¥=š†èwc+Ѽ½ŸSŒÆ°ßiUMp­ ˆÔ=g„»Èµ_88¦Š´‚’7® — §¡®ÇL”ž¹¦ÖY :µof4‡4ITè-Î+Pœp¥õj“çΆ±¯Š’eüúo ÚuÈÞÁêÒÙ ¤CˆþÕ÷Ú‹”Km*µu-yö/qŽº xѸ—ç¿“ª}•TÏ’ìæÓ}žäÉ&¿3ycÒeX‚¼yÉÊdzZ×Ê DwãÀŸQ!tñȬ8wžÜÞVÉez$Œ$j5Gt±EÓ'”µí•ùLž*4’ •Ãò¼[ló½Gï¦öÄ-ûg/!¹à#C§ipBˆ îÈqÕt¤]Æ Jñôí/žo`Avªiöš#™RÕ"8b˜æ[d6Øs¼Ï†´´GIV›=ÊqnDñ£ï®ågë§«bR=n­ß€ \:¶ñMzÚ]Ýõ „Þ–©å8L Ûa¤$øTÂa_&ešýÿÅÂN»Ð)T´ Õ71KhK t´;PnÔ:$éešÜåÞ  ZÛÆ~þÛË?\ˆä<™P\.j5çYZ²Úþ3Ò2‘VŒ²4±J«M××÷ù.Ó\ª’åo=¿KÖE& s•o‹xµ6³×“¶—>Ö„ý†¹¬Ê’»ô[Ïé·.ì9EO¥G‹ôÎô¸™\›E{} l!8¬<~Ä;ƒp*ÒÙúð9Pš¡í!‰(5qÓ÷§8 Îôæ ¡]ÕQ—>dD'Pf„ƒîêDã~ÜíjÌÇ©ík+oêgÓ˜eo¦)åá6¿Ÿb0S[ðîíq—4Ñ*f®ô‹Ø%} »òÿI±b.8IRÜ7ʾ:ßï§ȽÒNÝ,QïŽ4JÓ7×*½à¸@¬ŽW`ñè Ù*é/ÇAqÑÏ…GŒï´Ÿmqª}Ì^š,N˜]ºŸnó°á„d˜µéo©¬‡G6D¯\T~¬OôëêÍÓÎÝü½å¬(…uöª9ïô/¯üïF0\5 endstream endobj 36 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F2 21 0 R /F3 22 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 38 0 obj << /D [37 0 R /XYZ null 524 null] >> endobj 39 0 obj << /D [37 0 R /XYZ null 511 null] >> endobj 40 0 obj << /D [37 0 R /XYZ null 498 null] >> endobj 41 0 obj << /D [37 0 R /XYZ null 486 null] >> endobj 42 0 obj << /D [37 0 R /XYZ null 473 null] >> endobj 43 0 obj << /D [37 0 R /XYZ null 461 null] >> endobj 44 0 obj << /D [37 0 R /XYZ null 442 null] >> endobj 45 0 obj << /D [37 0 R /XYZ null 405 null] >> endobj 46 0 obj << /D [37 0 R /XYZ null 387 null] >> endobj 47 0 obj << /D [37 0 R /XYZ null 344 null] >> endobj 48 0 obj << /D [37 0 R /XYZ null 587 null] >> endobj 49 0 obj << /D [37 0 R /XYZ null 91 null] >> endobj 50 0 obj << /D [37 0 R /XYZ null null null] >> endobj 34 0 obj << /P 25 0 R /R [63 63 549 729] /V 17 0 R /N 51 0 R >> endobj 52 0 obj << /Length 6752 /Filter /FlateDecode >> stream H‰„Wێܸ}7àࣴpË)Šbü”d× ²ƒ¼Ì. ¹[Ý£ /³½ã ÿžºQ*õŒcð´ŽÈbÕ©SUÔŸn^¿zû¾2•¹Ù¿~‹Ø˜þÑWºÂã|ƒ¹9½~Uš¬þËÏ•9<àÓÍ–þÿòúUæò›¿~µ±UQ;Œ/‹èbkn~„EYZO‹7ðÛº†¶ÜfÃy{|ÌÛÂg»|Ge½yÈ7MQeÛnšúÑtg~kô·ËË"dS¾iáÏAý˜û¶¨³ÂÜÜõf!p{Ò娇¯ýÎ|xûwó™ñít!¤½{~-ާ<‚éN”rJa8 ê)–u€ŠLa4"Œ{Jät7ðÔF«Ö'%`Ø©ðHuv^Ð!ŠìLPòsº£Gö¬ÍÌVv ý«Ñ캩£ú"î)Ê*»lá5ª4-ºØö±Òî5UŠ*9€7—íóBôNí©íÜLJ0&1ú² „eOlbS»Âµ ./7¨ÂÌIådÿ]ާwù!ÿcÁŠ»†Ã"ÿU š!·ÐCδàoj€PÁý¸Ï± fݶÿÃu±ŠàO¬|û­jÅ:øã?ÿöçÿÁIþ×/Ù"ø“Ù=l±JÆÛ_ßPÑÀO¬\ËÀýxÙþ’¿&œMD4e%§ßÜ(-Æœ[ $ y ŒŒÐ!\<‹áÃ#-;õp\Nš@+/¤;î„‘:!ƒ—=#†—Ò®¯PyŽäw-_Ô¾m$ùEÖ-æ 5 ±ã¨Ó³€òŠuW€LfÑé©UP'R  ÚWÕãAu“Ò~ê¶wÈy÷Eáæšéý‰:‚ãE!zPÕŒ–퀔BÄù%9*zÞÏ‹±™xàYÀ Ùƒá„óI¿ú£ìiÃÛ¨õˆÉ>·50{ÀÜðÛkk\0Ûzáuy¸•wyª¡¯pTV Í‘ t\`öãþœ¨ñç¶;¦’ÄîødØ/¸k‘!È Á‘ô!G̯Ö=vËH½3›RÓ ÜtDïI{×=4Uk+­¡fi:Ît7 ~¾Ó{h—ˆfã@Ä!Àäµ(ÑkÛ™MÚrëÚW^ÔAÜø¥ KîC[ ÅëÑ•[P€¥k¿ÓåñN4p?æ›QÍ7£„ug= ¶‡Óð•F਺Œ2ŠÉ€i×KŸÇ‹Xs‘fÊÙAïx€ðø³ešf/³‡ zP½ä‰Ì‰ÊR#s<‡¦þ¼KA4§aYƒBpéá+¶•†ê¦ÉŒXœÈ) Ãè½I²gòþ¿êGêH6ûíqYáàþíIý¢µ-=ˆVë§y‘‘Ubj+rPîgÖ,^"1ø]/Ô4VæÂv«ß“1¤‹ Ò}2ƒ*ƒNÄpa>±¡Â|8Ã"Ù5Ñ®¾ÛÑipè€/·r žHWphâpï…`»Æ:âµ¶ÎÖõ• ÷÷Tºx§&H ¯Ër½½ù ÅÖűô&/Uê­¼#(ÊÔ2‰\ ±v^8z"ÏÜ=v»”89c¦™)"/W&G^1-ɲOþ.ÝH¼<“t°èzb‚Á “ù(‰Ùöà ‰h'p¨Ê"Ô?Pf¹Bð“¦ÃmÔ·S€öœ(®Õ™t5áç ÷…F7ƖɰÏqû(#Y†ñáÄ•KSέ¥çןÔ.¸ÂEŸ'˜A|- ¨2x:ÌMDð‰ïÍxéþ"†U-v#ùäù ¾T+3ø?À7¢3Z¦æ„ôà €¾:Í‘ï4ÐÀ@wõ 4 Xü#XÓ4EPW1×>ØRü³êH€E6Ê:­€Sh1¡¬’¨\`m:¬€R°¥)]Š…OiÊÏàµcêX|\ø h}´ˆ/| ‚b ®ë…­§>–Éuf]”ßF±aáF¿šcY¶Ø¦.ʨN±ài + ð ;Û˜vÝ‚§d4g´ö¨Â·Áᯅ €»…Bj n!6ª4$ jÀëT*€“-§,r@Ç‚ŒxºHJbYD'Ñ.²‚% …(ÙÆÌ§Aêåx]@k`•9$£s.@ëW@)ÃcK®;G®§D⣲3…%»^ÎÙÂzâ#¸DatDaS]‰BTé4øuE% ÔlÑÉžVŸ‚úXØ1”Ô3@tʱ  }PÑ¢N™dáCÄVƒDËœb½4:• X*j•9 pUÎ[°K¯N©šƒÓ€g 9†íÂêX°¡”•ŠVnnJN3†€­V@‹Œ-$+ 5¶°®¹¬š#éciŸtÊ"4âi™J,E[±`Àqç/ç¾N|,:ÆPÚ)|âtÑúñ€õ+ ân–(T@Zá¸Ö³QÇ=3%*Q¥N¶XrÊ"ql‘”¸¾ˆN‚[d)á/ÂÆ­ § `Ö—ò¼HE¯KLÍ:•£Ìeh¸Íøw¿yê¢ëmEsÙúPq‰Bˆ–\/At;l¥­Ô5·•ÒKãQ@¢”²Nêæ°Je¯’-@­OA}”^9†í>º+ ·eú$)'CÅÍ ÆPü-sŠõbu*5À%Ãm•9 Ìej}ªlº¶¦S këÔ $Çœ«¸ø¹E$h‰¹`ǹµš1ìttkOœ*ÀϽpUb XuKùXú”%û °Ú±EAâº8¸Etþ"K!•,Ñ2…‹´ç‘”Äÿ¬€^dÏUpó‚¾Û´-ž.5ð_Ýš±_½†‘^o^z¯¶olHomUÑ[¸™½d<½õ¿hü…íÚxøm|Ù¸¼ööÆŸoWÆ·Þœ^»úeã/lׯ[ºË­ê¹qy­Ã~é½Ú®ÇêÚµÕfy]†o¾]Ýa£jè;Ž$å°ÞšÆÎC™’ÙÀ•N*;®à¯#WGNLúöqžZYãZFý|‚fG@˜o ‘¾'ý\þà@…ë©T{Mì!ð?Ϋ¦GŽÜ†Þ ø?ô±;Àlôý‘cì!×ÌÍXÛ½pàJŒñAþ}H>RRU×6X`ÝóJ¢(Šä{,èy¬x8¥Á†‰îår>þÆ] ÍŽ7Ë©âôú÷£oÛ1Šg»Øû’—ûiì™FŠèy=“žºÝˆ•ZžmÂ0!rÿ4/K JüZMT‚M0ûDi^Ýa€#y4Ž&RšHò Ç¢E“õ­È½ Ç«1@¿J³¶~?bÛÙ®Ž{·¢Ag@ܳчÍtËJG7âr·¹„ú3]{èØÒ”ÊîÇ]d7¸€‰È¤\pPnÄwÍŸÚ½wm'»‚“û®v% £I Îàhf¡¢mwëL ˆ³qI¨¢+x‘à H:H¢@Åîj¦¡Çt·˜ÙaÛé®$Õ¨€Ô°­`3Ó¸Ü4 !¹®5 1„qå£d†°= µV4œ÷#¶îªüÜb´øzµ&Fצ’íhgíÖÜ!£8)”QHùˆÝ0;ÊÛNwuÉDjº xÐ_í‹/гð Jô¢—)¯P3¯ª™=ÌÄ¡ˆÌD}í;®ìrÆMª’«³]Q¸­”2¸7€½êì({l;Ý%‰XJªÑ+efV¤&‚Nñt p! x¾$h¤=¸tÔv,2µðƒklª[T*ï¢êº°VÏ€&£>.VRØÛAê 9=ê[iÒ·ZÔ¬õÖâX.Ú’ñžB€œâLqÙå†z¼‹_*í$wɳ›ï}ÛŽQ<ÛÅÞ‹³q¶*^”Qlè1|¸øÛLîÕjbbAÃô«”à^Þ«Ja @R%eè¡É}Yž¬DmØÂQxhJ;è$Å’¾|Mê±Ôàz,mNI!Šo*É…tЀG™ÎCT´ ¾ PFËKE‚CCS‡™TP£Y³Œù„[)Y1S­yemÜ‹¶S3IâPÛ8(A¶©0C†D¦—Qy‘!ÕG®…R¬)âÅhyÉh¡™`ýåÇ@êKK™Ô}¡å­Ò+4 ÓŒ=¢ˆœ2«§¼H$*äPXifbÛ鮕Œ­ºŠ³°²UÁ&m” (ÁjÙ9/389:U•Öî¿bÛé.äñ¿ªzW‡<9.z ½h"-€¶« Ò«ZÙM`Ö|À(Ð¥é{8 ƒô]öÈwâ—ã÷¨Î7ãHªµž¨R³Úõ&0ƒB’hߪê˜ç–ÀWëòÎï]Q–‰°ô½ÆÛï»Ñ7IÇ@ƒ«ñÔîý¸k;Ý…®M€Õd^äFómÀK«7E9ƒôͲ AœwÊ‘9CŸM@S5.îíÌHãv‡¦êbî–ñîü ûq×v¶«è°aT1ÀΙEÌ +·1ì– 96ɧ¡‡íM ÑŒ†ËlËg‘dQo¨Cpf…lf0®2PÀ²¥±Pâk%Aß-0 ºot¬Û¦#À× Î¼×-Áë«ÑåØã) ús9Vï’†Øx¸ f¢`’ul Kìw¾mÇ(žíâ!©kì9½C°'¬à:¼ÇÃ÷AðC¡2…§fŒ­ž &…gˆ7¦o€P¸[è$CðäjJV1#SÀ@Ì#þ %@WÑA²JIíæ’ì—yt‡m§»¤Ù—ÔiŠÚf`!Í ï™šª88×ÐxW“å…z˜ÚíH-45c»,‡§d“bG“I6;0À¢yC€Ø‹7;l;ÙeCTü·9v0[“k’×i )Â3vKH¢?Î?ÀØÆ‡´bN;l;ÝU$¢Ñȉn”qÑ‚6WÅ1ëÀÕ‹iöÐ%ñI#jk h<1ëì ¤—%ì1jZÇ(š€#'<]ô¬„Ç%`rgB‰GgÝŸ«Ž«·/$"½¤wf¼JhS™Æ‚l Õ8® ‹1~ »ÊfZRµºÇ¶Ó]èþ³‘Qý8ôñ¥‘%)² %»Î`gg›ŒƒEÀhd~~ÿ®²hqôŸüð¹Ò™Ïd™rß¹zyþ„ŸôÏÿyÿîÃõòãõÓí‰bxýøý„ëëÇ ¯ÜíɧŸòõ—Û/Ï“Cri{º<ÿåý»'6‚Øýpýq{ªl€v‘úº~úø]~¥ëÛø|àoOtë‹ýý!¨}OUW|ºðçJ÷åÄñ¦|þñv£Le äWº~ÇŸ¯ò'&ʾŠ÷½W³tƒDùDÅ™Z£¿×’WKêß2·ÿË’]•â[ä½ä‡ïÒÇr•@Þk0þÄ.|úryù'cŸ »|æ__ÈràÇÛWùðòöU6É—›×ïü‹1ò]Öпo_pÐåE ë?/0ö_²þcdÆš~­b,‹œ¤|É7òäõG+#ZOÞq¼8ë¼Û…û¯”Øþò+ý|¿fæJÙMÍ´]ž(hmhtwXGåY‰¦ËªäC•èƒVùÒ;/Òïüa~?1°, ON °‘ÈŒÜðMz€wUÜ%¢;¸»¬ãÓ¨ÛNKîú2Ü¥sž³[¾ŸX˜»;43R‚‘+ÁKä<Íì2<¦Ö}ðxYÆ>œxì¡óŠ]Ìó‚ÏË÷“ýó»ù»ÛÏÒ7V“¬Ù¨ûÛ  =uR× ðQVh‡oBú¾˜ˆMº¶'dÒVįPUøÑ—>TyæJeÀô²P*ml!ÚñuhÇ&Ò—b6NiB Ä!cõr!ÿÆ]àã ÉV;ß¶cÏvñ–Š ûU/óÛ³îË{^Tã˜2”M!¹³M¢—½Ô÷|[€ª/NüÅËuŘ!ÞaÛÙ®âáo3½l@×´—YB$cf hZ,¡ŠP™» •½[ôØÚ1,§0NiÒÜg XÝaÛÙ.$ê.˜¡ëÅvyxc@Õ!K'~H°NïW5Ç îÇEÛ©Qß ¨åD‚Ì–à=°¢·dÀ#]³Ž•l&9rPšóÓÛNwI 3 ÅKOc ÏQ#“.¶¨A,b@y9»%s¿Äf‡m§»"ò×Yÿ$ eÙÆ¥08i÷Ùé5k—4CJÃÊ6—Àý³¥ƒûÛNw!¢žÇ$„®Û³æˆ0x@/ v©ìÑ ƒE4ƒ¾Úï²ä‚oÝìfi¸m§f$ãUÌ)кË@Å›µ 3À×&@9 T!b*M Ä®[n¹Ã¶Ó]]·¶1 $ê:ÞëóE«$—T©  ¡ êã[‰E©âñú*:Pýñçtñ—çûƒ¶…T×z³ÑŠL²´eûýíõöünÌ?ùB±!ÉýËãO cË[­JOÏ«4£À7òPUuƒmTvt’Xs…—¨­6:ôÖ œ¢|€vÁÙ:OÉUír°Ð/‹Or¡jYŒÛÖY'{Œ 6ñ„IC‘õõ!åy³ZôÅF@Êÿ讒ݸaz/ÐðÑ.àÔZlÉzÍ¡×öÁ ™f&À,-ú÷%ER¢lç” MqyÅG&+*ݹÐÖ"¶r,(eú"8C޳6LlTe¨4¨ZÅW fÍÐGlœYC‚i\DƒªUl¬#¥ŒA0V˜dÁË2¶‘Æ9K£O¬:ˈnTLòýBñ—+œfô wþLa¤ÏýÖwu¼7Vʼní¹hÊÑQä³4eûûývº> ¥Ó¬KO̘æåQ ¹GÖ¦RáVÆ}”¸‘BëÙÐ.sN¶á¥2âe Eà¥)@`ÈÇ{®0ÊíÒ‹h·lÃf/Ë\lbC(]%\^”,Å–îFô”Üà›[a˜Ù*€ !ÑtÓ ±´);,((…tå Ž0Ë}•²ó‹ZˆF†Ml ØK©8 (A ç8À¨© _y í–‹3»í\, !uXPÕBc´U‹V?…! ²UˆÌÖµ` £×1(PÒrø¤¤Fì+^m'éx †í.mŸ]õm£QažcÑ\@°T£šÒ¨ÀÍ×¼â:©Ÿ_¼Xð¸ÿ°ƒa¡–ýe„~ Rœ2p|nRï‹“ú¾>¯¾§B,ÏÛ9ù·ÆÉŸÓP`ÁH“—‹ÈG€"iOzÑ@2UeƒöÉJ`É ß9 n8ö‚ ¸ˆFNÚÆ*ÒôÀ£€/•dëÕ¯dƒ¥ Ä#\¬æ@œŒð’®á¤Å·ç˜QbAÁÑ@⬠sñJ†ZƒFx¶ÁÁÕ‚™¼§p46d4±­0c% áÙÆF¤4lÝ”÷LÎvR#¼ŠíC¤ñ;p±äA@‚ŒðbRðyRWx=Âé3‡±1Â×Çû0ª8}`ª!k¬L¤d×EAêúÈ7QŽÀ~B•ô‘IPÖˆLèŠ æ/Z`É Ýwxé ÊËÔÒ£¢Á„®ØXEʤ(r‹älcé™Z¶®¤Bˆz&[ ÜÀ=£Ò-=#Z6rÏJ$P8æ»*GÌ$Hs†JƒªUlpp•`&/ÆiMÌ6z#û›ˆU«ØXGJÛÀ-’³ ¥gêØ>FÚFî™l5rGd@"÷Œ2Yzã/WxÕ3ò™ÂX÷ÌÆñÞÇíå1ÚóTõ3ñ5¨”cÞ‹ æåz~{Þ?íÎg!À@‚Å—ÿnš"m¢¸ª®¿Þ^{gtƒ;ý0Á 7TD¶^?¥­·Çß~ÿýü顽·oÝx7¶¯·sÔݵûÆ~K’ægú|MÿœIëyÿÜÁB3µÍË;ë»;Ò9)œÛ“Â3=îO—.²·÷ R{"Ý]ËQ{èú -éÒÜýcO—ýo²­ŽŽrôÒõžŽÜ.ô?¼6÷\ßJ<œÇîÔÒ/rÊNØèíÚ=þúÐz–pml 64¼à ÕèÀPRy:xE]ûtØÿÙ.Í÷Æ9¤Jÿ˲sU endstream endobj 53 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F3 22 0 R /F4 54 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 56 0 obj << /D [55 0 R /XYZ null 497 null] >> endobj 57 0 obj << /D [55 0 R /XYZ null 484 null] >> endobj 58 0 obj << /D [55 0 R /XYZ null 471 null] >> endobj 59 0 obj << /D [55 0 R /XYZ null 459 null] >> endobj 60 0 obj << /D [55 0 R /XYZ null 446 null] >> endobj 61 0 obj << /D [55 0 R /XYZ null 434 null] >> endobj 62 0 obj << /D [55 0 R /XYZ null 415 null] >> endobj 63 0 obj << /D [55 0 R /XYZ null 379 null] >> endobj 64 0 obj << /D [55 0 R /XYZ null 350 null] >> endobj 65 0 obj << /D [55 0 R /XYZ null 307 null] >> endobj 66 0 obj << /D [55 0 R /XYZ null 270 null] >> endobj 67 0 obj << /D [55 0 R /XYZ null 251 null] >> endobj 68 0 obj << /D [55 0 R /XYZ null 155 null] >> endobj 69 0 obj << /D [55 0 R /XYZ null 137 null] >> endobj 70 0 obj << /D [55 0 R /XYZ null 121 null] >> endobj 71 0 obj << /D [55 0 R /XYZ null 104 null] >> endobj 72 0 obj << /D [55 0 R /XYZ null 563 null] >> endobj 73 0 obj << /D [55 0 R /XYZ null null null] >> endobj 51 0 obj << /P 37 0 R /R [63 63 549 729] /V 34 0 R /N 74 0 R >> endobj 75 0 obj << /Length 9368 /Filter /FlateDecode >> stream H‰ŒW[¯Û¸~ÿÀ§‚LmEuí>%iR¤Ø´EÖÅ>œ²,û¨•-G’“œýõ-Ù>Ín‹äÜ9óqæõêù³—גּjµ}þ¬ŠT…ðŸ>\è‚$S. ŠL­öÏŸ…jÔûŪ݀«UE¿=¦c³úJŠEÒ2²AìœSq$Iš¨Õ_6Ã(&¶;­Ô×®Ù¨C_ÝéãØÿd>¯þŽ"—6ˆÒÄÓD¹Æ')7Q6‡Q cßlj¤¼oë¯u;ü_¹D ú‡©‘ë¾ìû; ÌŸ¨ múãÜ׺«îtïû‚éÏÖ³MWCò4K§HZ¾ø º…;ýÎX¸-Ý™<°ºW„Àéïfé`·Üi¿5N× õ­1Ë–ãí*³âó²VƒY¦A¢G FÛ6LµízS¹Þ£L«ËÑó•Ê›ìÄd0%M] é„&/ÑÐT.ÿxB.}å%Ä(Ë"á '‹ÉI+NªîX÷ƆA êÁ‰\D }PãC‰a=ÒRÎêÁ¤ð£6åXª±šZE@WÉ)x›™¾ubPØèI'zãÊT·ÞdEâfÞ«¯ö"ƒ”Ú`†hYu`Tª{ r®ѶD«Ú,™ Î5ø‚g1\×–i™±m;csðçÛ’Zº,°.v”Ea榻)À™˜Ôv‡9Z÷[ˆ”.«ú/Þ=_ãC…*€¹P.É…àŠ„XYD/sT¯>ë@î‡7ïïÿuù¤±ø œy{!µ(•´PÞ‡š…Ôœò6ÓP -ÔÅ>WÑBî2 r*>ëØk¸·®úd°ž†>qÉE†j/¡J|¨²(·VX- …?ŠàV(å2MÙEw”bI©²çãÝiÕ×eB ‚4B¢ÊW_>ªŽÄnñª ­„¨1x«Ÿ¬4›\$égK´(Ñ}ÏméÃu¶º ´ÑEíEg¯½@½-+ô9“{°”?¥ê¶FÿàŽõ‘(ê ÑŠ,&bTØ r<)F%†hu[n×_NeÛ>.i%4ÍoõF1#¡[¬w¤ñÕŠL1¨£³­Ô@â]œÝÔ€óù ?7ðÒ`¶HJV“½®nšzsÞÙöxºçb }´¸Y•-ó«}½ïèº3¬n'¬NLðÔ‰¤°êU)‡OõL4ÊòH6 ¶-1qñ-¶9x£íÀ¶ßC¶y#P]-A´+ «–pCÙèsF¹t* óC½Â×(×MIúá &i¢ßCpéü„H„ñ*ô±ì1EÆåô06»“€·%’©+âYz§ü~Â[4^aÁå+t6¹ˆÎ×ãÑ@öhiŒ/îBYI=cÁ;ýaǦ-×xÜt¡d:5Òþƒ‘w ×"VµP7SomR¿YÊoSê“æo|ÀïPU,áõ©r¸ Eäqìæ×z´øÕ‡¤»ç¯#ÔÉ7W‰—”Bw7EÕeçx%Éô`ÃÝd«Ò?GÐÚ§J1š^ÈпÙƒAç”ÿ}ƒim1è(ì®Ü[C¡p§Uª.Òº„ÄÐMËaBú#\{lg‰r½aªÚŠºG`Kø”Ð9et-u©HÉ uü•Žêjd1Quþœ@O…Ì[ ›ÄÊD1>•)$Äñô½:¾Þ¥cˆd“y q=Ç(0KQ Ö|Ç=…áì.í•ìѱ½}ÙåRðñ±I~Ý»‡r/¯>Îrj+~¨ÿñïŸæçz¶üÓaý8Öð*‡Hº0¡Vòÿ°Ó]úÖ³¢(|R½kv\:'þé8RCX:ƒ°_5m+dÐ(0´”ü¶ŽõÀO6µR)u¾{ ºãJ,´¬:(ÌR½m)_·‰F*Y‡:oo.ŒÆÎ›Ì–ìèølºxÂÚ¿ iÄNb{6Ì.=Lp’ëáìþÙg>Žßº £à2á+®7Ûƒ÷l¶•øÛ±Ö,*ËчÎÇÖPÿµñÊÄM%bâP‰ßìÌZ,`˜Rï÷q¡/9穦¶\MuÿT‰–Êq‘û5+óŠËbä:l˜ó€]—,0/¹:¥T¹p˜ù@°ÂûÂY¶Ø1sID&IáÇ׿¯&ް èÛÍ*ÿµÜbÃÐóþå?gˆâ1dë¿à""š‚ª󱣄¿}ýe u]“@»~©bºøúÈ·°z!QÏ}ÔãÜGß׈qÓ2ç·Ê‚Œª†„ßÐŽÚÔL‡H—q|b} 3™Ùb xLЖ{‰4r0/øÉ⼫ś€aŽZ¾œ½©×ü„—5Úžö¬„ŸoìÁlÄ#^Žô"¢à`×XÕQüj o±r1DR+ábGpÈÈEÆælß¾d@αüЉö‘”þ(TW.Î-èy0¤ä.@͆~A%•©csð%\n˜„„'º"l=_:ËœT“}4³¤ÔƒeL2ªh]0ø+ôê ©£Ö3+ÓÒóc1‰.$Aªm÷±ï½Q#‘àË̶¯ù¼êöGÚ8ÀκʹǬ˜ýTÞqd'bqr”Y+¥$)øŽ±‰ÆÑ)ƒËȇ硎æ;ß“¦4Ô¾A÷†sHJ¨µ¤¶ý@«Zmâ¤ã+ÀŸŽ÷ˆs Ddð±8!N$ñ*FpžiW¼† [—ê`A\ ‡çêëE›è,İ’Ø5“ 3…ø o‰1 Œ ƒ|¯çöå… Ùvî¨@l1È8«î˜¶öG¨èp¡hOFÌ*Ro§f±¢C 5D"“k‘6J ‚›nV €‚$+¦ÃIGûö{ !C¬ÄÏ»iðÍc˜G¦6žª1´…obaBÁ6eAàhÙÃ;j¾A·¥;ñö <Ñ›†Ž óxw@7 F;ìTå¨4XG-¨70;Xjoûò‘õ),ŸœzE$ɇsc9ð‡@5ÆŒéÝ ä>‹>2]Oöu•å¿5Ù0×½àRÂöf ‹O6ƒúGƒT¡ U¡¬‹ aSÅÁmìý,u§7 5è<­[üîløù. ?ÿü´­^¾PmWAúa‡VÒî ^¼ôÓC¦) Ò%Þ±®Ԧ#ѯQìï:R€¢ûzßµ³†Iv)Û¶œ*ÏÎ6—!–Tbó¿·UDY1Ë(™¯à3‹}B­LZP)cŸW³”9‹PjlÍŸ­±Ô}Ò‚F»Xÿ—y¤uc بQ«…t%ÓåB9ÞùND)/Ü7¨a¤QŸV/¥§JZ¯’'Û„ OiæÔ–$. éé_ÜX+/I]çQ3Gœžƒ2OžW”6³ H1½æóiqŽ_ù‚äþ²¸U®;—ÏÂ/¹ª_«+MÚ"Ëæã«;+òzŽ3ê¬ Ì­–W­*êe¦õBãèk¡âOfAB%´,'Ú÷ïP6»=Üß^û0Q‚)˜%×5DÓç0Í`7Ix™É¶ˆ—Íå¡ ÝtØl“¿ÀŽGÿã½Zzä8nðÝ€ÿCŸÉ®»ºªúaÀ‡ÄI€8ˆ_tÙhg%3»öÌ*vþ}ø,’Ý=+]’Ëî4‹üø(’EâßGØ2¾&‰G‰™Ÿ¬”éEƯgjùÌ èôlp( É‹mNþ=½pîû <.ÖÄùÞ³‚[hù *ºÕÚCD'*ù²›ØXÛóïð9½¬P±àßOÌÌ~£»f)~]„çôºÒ ‚G÷ì2 } ŸL>óŽF¿¥Gþå'H îcËr;×n\à9›º/4dïm™¡Õ|ý¦ÙkSêÆiÆs:a&=y;ß‘·ó9íÉwïáO.È4¥å6å®BÛž»þ'l}`¬¥«e£kž1QƱà90“žçâÎwäõ¼¢;ò DÎèˆO ä_ŒÅÂç0Ñy8®Ô’å¸äÛqü 0ì2 „刄L *ðzt&Ì"‚ÕX¥*2Ìž‹‚f¸•‰PhBè«'FÊ„ቄ¹©­úº²4 Fóe£\·íÄ„¬Ï=Âa\”€;ÂÊÂH¬P”8óŠK zÚEޤ!0H&HþŒõ… ù…j–Šør㈺L¶ªj€@ ƒ&ÇD*côI ‚¡„¦…AÖ¸¤™‚\Ø23nÁBÎDLZ›B¸’*óïä£#0s"Â\W„Å畟v•¯¤¢f†™M±ýðcˆZ´7å Ò%½P½áÂ{î-;)”4ÂÊo)Ôµ¥ÐPC%N\VFÙI+^ꮼa“oFÂå‡ ÆÇÕ*¼ãK¨f#X²S±1á¯ç|9Û($½f¦ú×’kë?mô´Š’ÉuGh2Iè5‘õ[óm’Ò4¶ï–Å;tWZQú­%O&]ŒÖú$)®õ5qŒ[ À÷\ã÷Æ &Ì=³¨£ð]–ÝoîkÖe±-xÚéðÝšþŸ~Ú¬6š_ˈ-JÂ~hsv™uü+N¸8¯Á­À n)¸,ž°]à÷wtÖý«3®˜# bÄÑïþCOø=¼ú‹Í#î;ƒa<#ѧó#Àç_M n°­XgÚ4:þ<î÷ô 6Ž`…%V—%ü8ü—;êžÏw¬ùw¤úê +øqË!¬a8æßÙÏßÙ¦LD8¼ƒýˆ-êž Äò ÿü³¬¬ð(ÂŽ+ëL£¶îIÆù»óB¯*ófFKeÕ= ¯xQu˜õ¾hjOÉ&ø¹m–ðÊfæ¿a'ßø‹Ëc[!Z†Ó81yUìþø#ø0¼ú[÷‰é8ÌÃø}éϯi­û…þ‰,,{øõ|8_`ó¹õÏûÜ’ØŒ‹,>b.Œ(Ek&r1f÷ öu .ÖÜQ‘~‚ÄH‘0Å~ûºãÕèNl`Ð{f–3q¬{“Y z¾tßÃr‹r}`øí< ÚåVèöËóù#$<칪o¡MÉõd 4èqœ0[–r….x´:û¥Ê]äñ¢¶ý"ŸO¯qúˆ©„;ŸñF*-37#mAÎ úö-\¼ ã“ðŸ‰üN„xez¦•i¥à…üôøö5kªg–ûnx#€EÞ[ÿúI ¬Pírü½ Ù…[OÎw¿Q9=5gÈ’{:»¸îWèȾ&e)Øä Ì$4Rl¾?Ó=~ýUÍôÊÕ>ã[A+ 0¸ý‚€¦.,–£akÑçµÂ2]Q’½¿Sßã¨kUA%ÐÌõ™žxe€¦>V>a&=y;ß‘×ó‰w·üD/ÏÔ']ž&zûxf-”©;$Ìma`S ÈêšY $ȼ „I³ƒÙ³&èÚHEÍNIÌ) [p k§pï N!!8…„àÔÙ²v*Ztm¤¢æc»É Ä›B¼©4noŠÇXwS<»›ÊeuS@ØÜÔž5ñ¦ÖRQóQkÉæFÏ ,5ò3VÑ·?ü3uï.Ý·?¦îòî1Hd¬«÷•<ºª2X ¡ÂÞ|ny ×›{#<ŸµVQ¡ùØ´17ËštÓšÍ xؘK"™ì]› ¹F)K©%ë×˰*ñ"¬,T ,_`«²ÿ;¨œT®R8Å]ÝrŠ»ºå´³ZZéϢ߀h¿æS,÷`´U†+wÎq+Ü+Ú ÙöK|]b­,ü¢ïÆåˆ)ËÿÌxú¡{Ü•{Ñ<áÛ…vŒ3í‘Ë(û“xé9šÈ¸¬ãáÔPoða½#ü‰‚ ø¼ï!>l±#¬3Ãâ WùêB€B%Hc&º#°ǵZ‡á8¨‰hÄ‚ZʦƒaÑt³tœb$”¢IÞ`iÕ…®+ gYèrYÃYz¼æèy ÅÚ¹iVÀ©©ÀâÕ; +äE *=a"uVP ¥¨5Y9)ÆHAILZò‚m"ZšBzõLíCˆF ÅXaVRÃl[YÏw6.â@o©Õدx@ßІO—œUfK`²Ç åûv¾#oç[y­Öc/°- ý Xd®Ö¤ÅY2_\Rו£×| ãa…;¦."®r4Ü€!¸AyŠ; ä¬:N¡4HJ©t[sPj‰/Öìl"j§×bîl¹¹Ëš`îR+Ɇ8ç®a¯y´1ow EQ¨øg~ú‡e`+1€K¥c\Ð$ç€Ð¶™™gl>Ý Û±&\®T©CB§Oô 7€ß…¬„opHÎù»$ú憯üdùq…wô*ØS¯cïUð¹A²?Ç•|ƒ4«x»Z¸( ͧ¥§Ö|‚ïœð7Ó|R~õ ¾Ç%žþQUŽ‹óQ ›ð=$烞«AþxÍ‹µÕkÈ5„{\x ³Ç¿ðz /E’ÖZqºñzÔ–=.J0 -{¦q7s¸0ËjWb\{ÌC á¦ì‡ uÅaZT³··×ÙÊÆûT3ÅÇ\®U¤¹ „Õh"Íå€q ¸æ²H™Ë‚ëæ—Aç4Åe7GyPUäÍÛó°peÏC{#ä•´ Á;LAÚmGj·á1Ží‰[®Îp W§ˆÁÍSÀ Çf¯)WÆÍˈ SuÚÍ~›§ê¬§ló˜\S]H@ÈY ¦EæAÏ4‹õ†«ƒu] w¦È8r˜sƒ¢`ÍNþGUZ uP›UP³›€0„]Øç ú<¨µƒ¾ÍŸ§`Áˆ”Áˆ¢M‰Ù*d¥KÌ ÛƒŽ ±ÄÜäëAõO:üêΕÖód¢s[BÒtl}R0¦‡i±âžkèNÍ<ƒ™ò w;2“²fîÈ»*Tya˜Ü(lžç rSÔD$•ƒÕ\yL¢9–ìa¬ˆÄ6«)mú‚‹»ö:E{0Lã88{—Ò®z'R›X†8 h¿¯ÅŠB¤¬ªIQiU=ªGVÕbŠUµa¼˜èÑž¢€ëš_èö”š¾ÛÓVé»=qX·÷jžl‰} ø¤#²íj”‰¶XÈ.Ú·pŠHß>éš¡^‹½_ò\%içb^³×¤ W4».ôÿzÄš¹¬"SnŒs8{#„)ÇáµÄHY›yZúM*F -RmÑ÷»0¼os‰œÄ‡ç:¦<ú oç;òv®»L”ϼ^Û£R™èüÊ"•2/q×6)w¾·J¥<‘÷mbGBŸÜâ‘ò·£Æ¡«Rv»F=EmÛP˜¶n¨¢¶ 4Ž–™Cq½y›µ*•Aw?ñ¯P…9ÿJ¬pþêÎiÐcS”üƨ¸æ²Ó8lðóÇklìÝàîÀ@±NVS>üõz’ô°¸$[IÛñnŽÍcÜ ‘–[$„= a½m"íæ1.¸QË1h¶K»ÑlÁSŽv)ÃpÍÞmÒ-\sæðÂã•9¼ð\d/<‘™;*Ò^x¶ óºÊ„É\sxI±†Gs8`¯{´q`ƒ»†é±áfœÛÑ3 z\o ·èåÔsªŽHçëcLU>.0HŒ›sÿxp%÷•}ÿË|µãHÛÐÜ€ï0±ñ–~%)Þ Øpà,0Q÷ÆÞ°(þ%Õx’5úùøIQð +©R¦˜µ¦@ÄÈ{yº.¬×¾ öŽùéD À<ÏY!`ž8À1K¼Á@ó@³sÐò“ÂxšˆCbi(á²§à}{0MÞ‰W €ø¯,›÷_¨“i\0wa*ÑÌtÐúÔjhÔøûh|Ñò–‘ªj^£OÇîª޽ÜxIÍHU©8X ç¿TÌn¾P%`2¯Àq0 V¢zfy§e 0òªÀäåý$•yäP}­®|¡VF¡«3ï ¦{‰«A^ /ZÞ2cŽ\yÇüG˜«Ï] ê°ú=uµ¯­ùer˜ÏZ"¶mÀ5N)Õ­Øå ÜXôÚÔ7nûÏÍjƒRb d¬îa°2“Ù¤I¨âi©nt´yÁïÁ¹¾²¾Í”¼´n‰O€VŽÞÿàÜéûDÚnž×8 =åžkY®9fÞ¸– µ| ixbÜYpïÅDÅÍ]ÀL·¨Ìõû€ØprÅò‚+ùÂzã°èé \áèÊÖ(‡>²¤ï©#ð€\ò:Šc¹œù:ï&b}Ü9懽Ñ|>í1Û-ëÀÕÍsñ+½š%ITxò¤/gH¶&Ö’­‰ É2#¼5yæµîmkb,v‡z#@)Н\8Š%>–pJà§\–ÅìK…4¾¸·ro/‘øJØÞ^‡6¼Mß½MÒU¿{›€ö7oóùXc| HÜSÄ“¾Ä{Šàž"¸Z ŽÏs¼žØüÝx7š1õt0ìE7®‹.åÀóàTtæó©èR®Ü´Â.wcšTKÈ ¸3p¤/gHÏ€hô ÈæŠ%ä óZ÷ö+q®#ß‚mª4Æ@·M%_Á¾ÕøéK …jã#^ow·ñ±„Äç8^Olþn¼;Í|î¸h¿y˜ä†Eÿp¡Ãç[Ö†±ßmûžL?Bɵò‚k`®‡6/±Ôꀄ·*ï½¹ÒÄæÍ8×€3± iàewVÜ s ä5×ÌÒ“ŽWeãi"‰—BŸ <ïÛÌÀ5Ç< pwzuÀÎ -|ÝΜÚ7‰g}YI"H „Röx¨ŸPŸØ××€æß ex=ÓÆ+.Õî• ru³$ÄŠ,íž,' P–ë’òòú ­˜Õyså µr1[¼¼_0ÝãÇ€d÷Æß'ã«–·LÇ2ר¹ÁJû¶C6 ùþ°õG¾=l}‰¼¼-"¨šoJ©ôânלwÃ^”ö½é2‘¿ .2 ÖÀÌtCI·+Ùõ³Uh,¬ÞÓ¹é`ÝÏÍ‘¾O¤ƒ &3k =’/Ê=ײ\s´njÙRË;M¸ðĸ³àÞËŠ›»€”L«J´ý@²/Ý£+ŽwºQˆÃÎ!a%€+]ÙåÐG–ô}$µØ¶ ®/©qkçæV¸<ª4¤Çƒ–pU×ï§ 0‡ÆÛ .\ÅlH9,Oº]øD…W&Oúr†dCb-ÙØ,."Á’ç`^ëÞ¶æQ1úy#pu_\ž ôjãc‰o1,îiö¥B0ñjÀqy ˆ„ì8^OXx4w6o©CÝÔ9¤Ën¨§úMÙé÷cÙñÕ Að–c€«!²K:«È±ðõb%ÜãÅXÖc!=²¬éc‰²\l>ŸÖß½k6{ûÛÕá²;F–øjæR¿d/íkÙ‘–ÆG¼ß\5ˆ„Äç8^Ïlþn¼+M£PgÅö^…-,ÔǪ+cV¶`ªnÑÖϧ¢+¡ûÎÀÍ‚2 µ>^ϸÞ2xÂÁ§€Þì˹"§Ä4rJlhHtçŠdÓHÜÎ9Go…-Ûˆ¶:-ûVs4€`³À~`$.0«HJË"‘–Æd!M ñjJ†e×v"!;Ž×SD[Þ•&À]o\ËûE›¼7y0Ow+0¬^*QgN,Ðy“üÛØ˜Žk°Ïò]6póYOà¦0Þ `¼ÁõÆñ‡† `³lñ’.ªÝ¯•÷ísI¦ œ°¤.ÓbµyoÒTçÆ8³øyJ#æ¿Û)1:ÔÉH ú>[cW 7ŠÙ#›¹P£û› xàÒÂ#m£’óÇé{7×ü[m³ÎR±Ü‡]”ÝÄÏè¦û>&|³T˜壎S-ŸÇiŠØµˆ jEWÍ~µ™·{ÎùeÈgP/òyW—ïsFíê×=gWïP9C´û-Ÿ[œÚeîÊóËÐîQÕËÜVðû®®ŸïpRosI®×·ÏHÆpö¤†KþðZ«áÚ>½#¯ù®„Ò‹äšï[èå9€!=}0)ë¯'o[›–·ìh èšLP”Ôb±A U]ü)™ àèù%9Ý£Zxôf±µiyË/9É^mn§'¥•D'5€üIA5Þö¤LõãI¥!7zR ÑÖ“Ú½YlmZÞ2ÒüGڃ¸p¦Fþ÷ÇïýË/¿þ+|üöÇÇ/ÿüö»ÓˆÐP7Lž$%ÿOKk»îóg¼¢ò¯u÷3IÇýO‡§Î ÈÕ=NIÆ7Í‘…8nO•4]^=¦ZçñÍxz eoi±nˆ¶„Ÿð²Êä*gb,-Ó/Xè¦{©Ðµ{©ø¤£û‰ì+QMR‡¸nÛõÞkmíz*uíßóÊœžÍïÑ/±mÖ¼ÇßFß+‹Åy#Â’m=þ\q^ŠG[¯PacuŽË­vÏ.àBÁ ÏÍ|?èë÷ZúÐRx‰—:iÖ]çKix†s„Ù+Y¥^+à6Ï¿·K,ÀøZg ðTh$`.3 ŽŒnZhSe˜d b|2 À8^«YÃa$„TUˆÔs @=½ ï€GOÇÁõd³E€ËggÀG;<•íS,ãå…ê³ð]xŠ\]hÖ¢?ŸŒ×#š'ÂlÍ Ði£“€¨UEχ3w³YVqÜwe Pá(©·’ôµ÷ôrUqÛ³_.?”Í1ѬZ"¡¾-Þ㙵†+°$p¨F„Ùš6#O•gȵ¥«µ@ÍTÐ;Âïòý ¯ßÍTP}S ‡@„Ujf$bG“˜Çé %ARd98ÓaὯ…wž—%„×q¯3Ú2KöW„ñòv ¹;QHðñZ ¦È3’xcŸp\d †$’ ¦Ø§èÙ߃{ïF“ ·1ÍÝP×ò‘åY¦9Š©¿×Êáy±ˆ o œ,âÕ9Ê2¾óZo.êx™JõÇy(ÐØqfûK@Xf~àA9Ž—ãÕ€HK"^s1„ÞG–ƒy­7§€Ò\UnÜßfZÉæ•æBvžEöÄq¼dLtÇËãYx/KDs‰ô•סÜËtÁ{£Þls3 €ß>4n>•ÈŸrp8tO /ïÂËsQxU‚x‡†£Æ÷pþ¬þ«fs8nÞæ.ÁeÔl,¡Ÿåо¼ä~;ãœÑRšÜ—ËP;Äwƒ¹Ûò_h—ùn‰/3Ø%ì,Wè…o\]Œ®åvÚe-¸© Æ7-ö ãn‘y¸^èY¼I³ÈP‚€É›¹ÈT%væàV kE-‡jË›´´¼É–·J(o¨KyŸ":`xÏ4µx}§ Š\>’‡¶†íyE‚Ê•„´½&ðRý*/KdÓ^Ê¡¼*txñrm‡ã, ;çªj‡ã”ˆËËÁ G^NƒÔ7m„Ѭˆ3ÿaÝ˃ˆåÐaN³›¶ 6Íû´ô 2Sl[ÿIC”Œ·²„Ýî%l”0aÀ‡-&ìa‹ÇÏaÑòa@ÂþÿY­Á‰Q cŒ—Qáxü?RHX*ð#j&:ɛ™Ó‚K¼ËÈüÏŒÃöd`~çá%BÃ0˜DÎKøoße¯Û0 á=@Þ!£=¤Ð_y-Ú¥³· SÛ¡C—6ï%“"í4Èè$ÑÔ'Eû‰.ÔÎGGz\ÁK“¼7"pj4eɵÑñ¡´Œ„¬yË8ïÐ6Sg½bdK`K™æ„“!´™¥5 ÷–H·¯4T Z¼YÚ* 0së7o?ýè €”ïu—lÍ3i̶1GÉÖ(SŒ9Ð6æ@Û˜ƒêmÄ3æ(gkióüdÍ!ù-'ýÆ%oÌÑRúךHm'kM öOÊ!zz¯õ "0—k½ÔÁ…-YÁK“¼7"pj$LQÑ É·Œ„¤yË8ïÐ6ÓXíÚW,‚‚l l)Ów2„A¢"³°âôÞ*s”|ß*æh½ž.ëŽçc¿é0µzËÕÓ3Ó-箹ù}¿» ¿·Ÿ¯ÑãaÃÇxÄÕ;|ލG‡‹k­ëxßö;<'ç3>HŸýÏ/¦z3õ©ü¢n­H endstream endobj 76 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F3 22 0 R /F4 54 0 R >> /Pattern << /P1 77 0 R /P2 78 0 R /P3 79 0 R >> /ExtGState << /GS1 23 0 R >> /ColorSpace << /CS1 80 0 R >> >> endobj 80 0 obj [/Pattern /DeviceRGB ] endobj 82 0 obj << /D [81 0 R /XYZ null 730 null] >> endobj 83 0 obj << /D [81 0 R /XYZ null 714 null] >> endobj 84 0 obj << /D [81 0 R /XYZ null 698 null] >> endobj 85 0 obj << /D [81 0 R /XYZ null 683 null] >> endobj 86 0 obj << /D [81 0 R /XYZ null 667 null] >> endobj 87 0 obj << /D [81 0 R /XYZ null 651 null] >> endobj 88 0 obj << /D [81 0 R /XYZ null 636 null] >> endobj 89 0 obj << /D [81 0 R /XYZ null 620 null] >> endobj 90 0 obj << /D [81 0 R /XYZ null 594 null] >> endobj 91 0 obj << /D [81 0 R /XYZ null 575 null] >> endobj 92 0 obj << /D [81 0 R /XYZ null 550 null] >> endobj 93 0 obj << /D [81 0 R /XYZ null 470 null] >> endobj 94 0 obj << /D [81 0 R /XYZ null 445 null] >> endobj 95 0 obj << /D [81 0 R /XYZ null 312 null] >> endobj 96 0 obj << /D [81 0 R /XYZ null 179 null] >> endobj 97 0 obj << /D [81 0 R /XYZ null 136 null] >> endobj 98 0 obj << /D [81 0 R /XYZ null 111 null] >> endobj 99 0 obj << /D [81 0 R /XYZ null null null] >> endobj 74 0 obj << /P 55 0 R /R [63 63 549 729] /V 51 0 R /N 100 0 R >> endobj 101 0 obj << /Length 4597 /Filter /FlateDecode >> stream H‰”W[oÜ6~ÿÀ§•f‘Ôu‹}p¤ð¢Íí¼Ù…!k豚™ÑTÒÔ™þú=7jd ¸¹Øâáá¹óœ—oß|øl”QËû·oª¸ÊUéÃ%.Π岸*ÔrûöM¢ÖÀýïF­\-úùøö΢åï()I kâÔ9§ò2NL–©å'à“ĦtL+ùCç*VªŒîJÆÅ.¨Dîk=ôus»{õõ¯‹ëä7øÇTõºÑF½S6O{}¯dÿ|xý¶üïÜO›Å FvÍsäϵV}¹D<.¶Ñ¢ˆ ]G™þ½‹ð³WíŽ>Ô¥ ²ƒçyœ§A4»úî9¹pEl0$ e^Ë”¦AådóÙ‡üúævûvÅk[÷¯Áûwò9´ùîþF¯ºÃÝÆ3 Ãò!0ìÛ;ß«î^ÖwÇÑêž]¯ÆD¡BN£ ™ŸŠùI0]Í­V«a yúÈTÎåÉ‘.îïT fÍEc„Pô‹BÙ–Ûš©â¼C·Éa&On?“Œ>“Üt‡ÝȲr¡ŠÀ™(ÞøžêITÎ7'¤4ɪ©¤*+AÚ¢…±q®·wòáûh‘ÇVCäã.2ilô1JâRXx•öÀÁæ &wr:œiºPÆ(‹SÝò±µÿ0¨U-²ÆðqV¿ ßå/pjbg_“¨¡äHÜ¢dºòÓÕŸø_ÑüP÷4 Eh!u! ð!„.²^æ )—Ñ"ü‰%Üí…ÕÇN¤F¤AÊŸÉ .41´øc}˜N ðQ„[¼*I'çá.ã,h¾pHg›¯¹ÜŒÿÓoäÞBôÍë®Ö¾ïì"Ä‹ÍØÄ•qØ‹;q“ÀjÈ×úêÓ“³/¥òÙûm7z’,íg}|ð½Wuß×Gaþ¼Ø>œÔã,È Êj®:„åÃg‚g‰~GVq戙j&qçýâ—Ÿ õ%h¾âþ¿½° R ¾u=¶ÝNl1I\‰ ú-Y§'¼ê–”Â2-Ÿê1‚ÌBÒÔ¢f ©Iu : än1R±ï¡z+¸§|flic²åï¦t•MÎb´‚M ·}¡$b.D,NÊ4=¿W×zí±m`Óy¦ÎÄ6I«Ù´œ¢jŒ ½Mu^‘=_*¼†ØÆ0–LÙѶïN„`:"$¨$ «ŸÆf±k!ß3¯aÕ[\T â–^É.T_ge[ÌÁÊ$¶IKÝx(Ráô¬s EbþpÙì$l²‘"ù0ÛàÜ /1|Aa : °t0¹©7@IJf%çfdæ“0m‚ýõF(Ϙ;ßC¸œe 'Užcw(“ÂR\N5mM%9FÔç L‚„ R/íŽÖ5;ã§£5¹a©‹â¡X]PGœµ@7!¦Ì%é¬À¸$õú0W﬋ÂÎ8™ì4Ø%B-V¨EˆiO÷™P}xë@Þußóç"5#lÔXž‘9÷¾ï¶PCš ѦÃÓÚÓ¶ë, ”d…<9$u ™¬¸fñtEV-ÜÈGwè±^K*5vXÎ,f •%ÅAqBÕ’ËsxžËtj‚¥ !ê=â (Œ!ƒ_,Z±GÝTm VÚˆ-’‚+Þ÷Ÿjé'ÎD¼£sZÇŒ»›(– , Oâ×.l.îøcƒãµ g2å«È^3ŠÇAiý 12ú ož±ú‘h-ŸfíÂVd™?«8Àÿ¹«ÊY!×Ü—»³’3yYÌ8í„ìÊ4t{\ 6ô|Ûü}doµß …ìs;Ú¶æûI¥ÿŒ,µ($žê¤m1÷øáÕ݆I 1~…j¢%¼BFß‹¶º>”®6ÎOHgõpº3‰ QSjÁˆ»DJºVЩy}« W –]Y‡ê×Áu‘?¿á !·Ålê¸*Œ¨DÄ•©þã|î¸@È,òÙ4Í“)ò£ŒºO)&`ß+ž¬»±¥{VNŽíh8@¥`õ8šÉÃŽvîÙ]ºó;Ä Œ„ÕŒæ+‘Dﯹϩž¹< ñ?~® ž"cO‘ya¤™‹³bšæéé1aËcMÀE£Ç)ºŒ«& .¬Ã}”²¨™µ}%”Hm ÝtRn‚î, ÃþjA¤kÎ4¡uËs^Ú<ºÙâ·¡"qÿ¸Vt—ܦñŽÐrRðä=& ë¦9ð.ÎãJãBoj㢀åÇ$‹Íii,Y‘¨ /*¦â„7X¡äK¬–b¬Øó¢}ª‚ê¡Ý¶›ºW£ì+±ƒlß+Ù°ú[„ƒ[öd…«y6~Ót‹¨Ÿ‚θ ×Ãè)òXk8iSBC”t€¥à’Máz”—¯ZžÐ¥¦‰bÅ’J¾FóyF¨J 9"&!LÛ¿E3R&IªÚ”2G¬©U=Ê>ßÂt«6rþÀgö̶"bÍ:GO¸Ç ÊKåYŒ[ÁYÙ£Æ q ÒêX]áq‡ÚÏÀfÊ.m#ælhÚ×!5…’Ć|ZEGؼ~Ä‘Ð<*°¼V§¬óŸi”ÕÏröé=ÊD‰Øµ.¡®*p;ú‹ö]2ñö rð$ÔןAÆ™¿Ó"¤ËMö¾ë¾0r~½¤ô|È1üXšô ÉøAE±¦¬„’½ß«lÖ€z±`èÀ¾•pîø€·n»g¢ðVéo€rVꌟ$?9CJ€^À¾Äyäý= GLgîÜI6–_ŠÛú{½å_yµÇ ¡~[³Aƒ%A ‰æ@q'D ænad]B†¨\±ßÊNë%Í–›ò8Ÿ_2‘öÜq›VöpÚeú<>ðé¶áñ†¶4w‹^$ ê±ÝlÏÔÉ¥>Ž<0•ŒVé‹7DÊAÆÕæâ fÑsôºƒèÓÓ'A-ï/;ävn×,<‘VJ/‰…e«™¸'¾ÿs^-;nAð ÿ0G ˆ Ÿ¢oÆÚŽ xÀØœ -EEŒ%Q (¯í¯OWWIIk$ð‰ÃytÏô³J—€S1µ±-@X¼µ\,K… Ç y9X†Kmbºí"'‡’~Ф[ Â}äÂNïV±×À^/?‚‚“û»wvƒZͰ¼åÂ}’æ£äL£À§ Á¥±51ól?Eùnì·FIß°Ì´Åä©ÕZ£+õ—«‚ ¾ËÒd¤3öƒ’жöC‰Z]‚¯E¿¨\÷„Œ·ÀðhuH2d¡k6®9ê¸A£¬OÛbŠÕM£Kf±°O¿L.Òh R›¼yÔ™ßWÝO8N2ÂŒÖÉ#'ãè²Xêé°øB=zã,yPÞ '5³êJ„ÄV…Ì(dE3yýR?ä}å)?>GBH®_ä™™…Q‹þm$Íýr±´ãjÊ8±H:=t»Š,®TGפ€ ª£¾CD"ó"bÅ©¸\5©+·3Az@<뙄«P00Ø?ÖϺÖâlu±r\9š\EÙ¨¿µ„¾”ý5Q(P (‚ýïªå௠"±›\dQ˜AjxÇk©ôJWö’è'%«»t7}øç"&æÑ")žÕ˜ž3iotä˸ïÊ4·Z´:fI6ÏÒhy“#‘÷æ’/ÓxA·zÐÅ3‘æSÍ„ñ¬Ü„„âIøTšÔaµsîy3o¸çtf¬8<§ºKd'µÔZ7î29©ÃÑ º­‘ÚÍ–¬[1ŒÓbëA§\¹²Á£~* ô³OÖ±×\ï“Z vÂÌÙ$†ÙK1‘ NŠ¡¹&«£Óp)B p‹üéE9esè4z FOÌr ¡ÐßµÕ¾/Wz9‡º°´ŸýÌ'Ÿ¦é3Ü'¸Ojom¶à‚\%eá–o¹ê, kNlK‡aP <È+J™—KE¿ÀzÕšøs×ðD)%»6‰ìW9ú•E%}v)–õŒe=Ö²® Õ ºÝ©Ä,¨µsádýµë“;ÃVâ ÛfçºpÑ ÿûU<å& ç>J~: f=¬×”#Èìx+ÌÖÿÊdŠþ_ô/’!"²!úõÁjïDâ_â>“Á‘Ó§ÕÓQà‹³Xª*â Œq ÙÕG\ÖãSÍÔr6Z†ZºK \¯#ŒµúçpúèLKôRé’ä†ö Õ?e‚±ö§¬ý2§”NNd”°YŽP£õûŽÂ)€Ž7pʯ “öŒsģܩê,_?üúË¿&z  endstream endobj 102 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F2 21 0 R /F3 22 0 R /F4 54 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 104 0 obj << /D [103 0 R /XYZ null 729 null] >> endobj 105 0 obj << /D [103 0 R /XYZ null 711 null] >> endobj 106 0 obj << /D [103 0 R /XYZ null 609 null] >> endobj 107 0 obj << /D [103 0 R /XYZ null 591 null] >> endobj 108 0 obj << /D [103 0 R /XYZ null 399 null] >> endobj 109 0 obj << /D [103 0 R /XYZ null 377 null] >> endobj 110 0 obj << /D [103 0 R /XYZ null 364 null] >> endobj 111 0 obj << /D [103 0 R /XYZ null 352 null] >> endobj 112 0 obj << /D [103 0 R /XYZ null 340 null] >> endobj 113 0 obj << /D [103 0 R /XYZ null 328 null] >> endobj 114 0 obj << /D [103 0 R /XYZ null 316 null] >> endobj 115 0 obj << /D [103 0 R /XYZ null 298 null] >> endobj 116 0 obj << /D [103 0 R /XYZ null 280 null] >> endobj 117 0 obj << /D [103 0 R /XYZ null 268 null] >> endobj 118 0 obj << /D [103 0 R /XYZ null 244 null] >> endobj 119 0 obj << /D [103 0 R /XYZ null 232 null] >> endobj 120 0 obj << /D [103 0 R /XYZ null 221 null] >> endobj 121 0 obj << /D [103 0 R /XYZ null 210 null] >> endobj 122 0 obj << /D [103 0 R /XYZ null 199 null] >> endobj 123 0 obj << /D [103 0 R /XYZ null 188 null] >> endobj 124 0 obj << /D [103 0 R /XYZ null 176 null] >> endobj 125 0 obj << /D [103 0 R /XYZ null 165 null] >> endobj 126 0 obj << /D [103 0 R /XYZ null 153 null] >> endobj 127 0 obj << /D [103 0 R /XYZ null 140 null] >> endobj 128 0 obj << /D [103 0 R /XYZ null 128 null] >> endobj 129 0 obj << /D [103 0 R /XYZ null 116 null] >> endobj 130 0 obj << /D [103 0 R /XYZ null 104 null] >> endobj 131 0 obj << /D [103 0 R /XYZ null 92 null] >> endobj 132 0 obj << /D [103 0 R /XYZ null null null] >> endobj 100 0 obj << /P 81 0 R /R [63 63 549 729] /V 74 0 R /N 133 0 R >> endobj 134 0 obj << /Length 4470 /Filter /FlateDecode >> stream H‰ŒW]Ûº}ÿÀGªˆU‘’(éq›6í] È]´7ÅBkÓ¶YRe9¹þ÷/ʲ½7(X‹äp8Μ9ó§ç÷ïþøÉ(£ž·ïßUqåTô‘&iœ*ÍãªPχ÷ïµé¿þbÔç5ýÿñþvÑóP“M&‰+Kªø«°qR©ÂØØå¬+N’Ì‘Š~)úU#›è~7F•öÇ£ª»ŠVÆè>2&¶š–7~l¢"®t·‹þýü÷ß¿C•๮ÊbS\ÎÍÙô8±ÖɱÿŠÊ¸Ð{8)εïT‡ÚØè×6ÊãL÷ëo\%× » §rp[SàŸÿÌÇ8CÇèá4‰w‚pâJ4IƒEi¡úÈÅ©¢U—ÚÑ .¡ë©éy¦£eµ–áüaƒtë'ŒV%|ˆVn¦"ºÍ´GB{µášo<Õ"°g2}T¯"ïoŽhØ€¼¯ö^•Å&5‰6¯ÖuÛ6ëÚ±¬˜*÷âË®ƒÕGuÀyÐ̆ðíú«-gÖó™^äÄÊ'µ§épV'—DÍÄ/ˆ7HØP&E:?еáQœ1ò(„‰Õžìtpê¢ÁÁa |M{V5Ä-˜ áiÑâï,Çgz£¦^,ÇU`@ Ÿ¬Bm<LM‡oÌÅ$G©Õó¾±ð[ßÅŸ© žóePq˜ÿª[ºp®×5¸&×ímÊ@nk.;ó¤ 1ÈÏí4½¿z Ðg%¿M¦1"qºcÑ,”üy¸3dm~Ê}'vîZÔ€± ©‡¿ºÆãЗêÞîÒîb·«R±{­ 00(: €Pv$ZÎð,1‰îFNîd‡ê·tÉL«‰—ödŒlSýIeõÈ—êjë6"%êI»¾‡Éç–Ø!.N/!R¯¤¬–’!VóÊÍ2@,BîŽõJB=ƒ¼AÌ» u#Û!2Ù.AHwðîÔ«WõzßøÈfà›ï´î!úS½¡ï5¥)EW™æä‡€' Ô.Z¥$êAàã ÅÏÓG G°ºÔ/,óÉwk¯úQ=|áÕ7¥Úöf:<Ÿ°öñ€7Ú_H ˆ“íaÏÅz·lrÏ‹$ø ³/Ÿo øFcF¹FÙäÅwGöÿó$â3W ¨ì郞0DjnZŠY€®:|µwq“B&™ê­T(>ß œëÊs9 ¡9nÈ7#Qž ±gz't=€À&”„´žvð{ÅRƒH­›md \°!Ѓ]£?Dì_zÂɋ۬¾AW!œ> XÖT=‘ú[¼Œ™HœäòÞ9ÅIu'†ãR›ÞCzÄ®ªî’CŠ3ùwF˜K!•S‡Bµ,[jª Qɱ ÖJì#¾Ð×-Ž¥°A ©1ÕW.®¶Œv,ûª”eb~?<èÑ~ !6’tAJðM’L‰£Üv\o©LÉÜÙ p©&T !øÐn :v¨èÓrêÇ 8*™ðHÅ, v{$Á1ºUžW`—Ø·eEˆºç9âAŽëIOdàò^ô!}üsž›WPîÍ-ßdžJë“?ô`µIÉ! æyqÑJJ&!ÿ 7/ ¼ÓùD[Í'å§È8b?%޳àO8rÜÊÒ¡îèCQ¾Ž SæO¼,#%C–=Rhî1ÝÀ‡#>€:ÐuitÆEÈTÙ^*CJhq`5þÎi¤˜ÓøûÈŠBªàþ• f]Ù7úÿÒøÄ"Íè @xëÄÙÅNP›¸0ð4¨tm¯¶7]`Š wÕs.¾6 Õ/`Ø€ £ßP "Ÿ£âSið 0Y4$fžçu”\–‰$]ˆi#äF³–›^Ôî‚x=µx©œœ{YgÝ í4EHF^ÚxÑ Í–jfžOõÄzxOO“_Š:3ІZ_ Ÿ+SÉуçÈ*BdËòüÈ&ó¥.…”¨Xþ¿ÔNrv¨J^Tjº¹½ñj{а˜NðÎdSN6ǰ`pF•T\ ­-"ޏx`ŠpíHõ£ ÁuHзäÚ’\ÂÔ!cBƒ¡ÆÒ¾嬛ææÙTˆ Õl<ïŸÂ9 JÆÄüñ7rn¦ã'ÏŠ2TøAdýò^™.ü&fŸ/uÍßó4¾Mèe…´K\sõÒVÀg‘0{øò/—Á±#NP&x?.s 2¶ HfIà>õ9r¡?£Ê@ÅšP¬¦**ZŽe3àyz ‘žˆä û§f‚Î ?¿ÓrØþe‹äÀ=­>Ñ,(âu¢MvÍV82‚DÏÌ"‰ ‘å'ÞzŒ.Š "ÈÂNàécðÅ.CÃK¡+I¾üQè@¡ ŸqrøÍ¥¯ê×—;LÖ™²ôÀgx9™ ŠwWO0õ7‰VÌ5ĦᡡH(RòoÜÌ<#[Ï1^ÃÏ´î&©}„¸Ʀ»Ùô@@N!׿¡~H›ËEQ[6Ü,’{šô‹>@2òšQ~¬;†DÙéGæ¾Å…ûRå\ö˜<Õ žžhnBõ¢}âW×®MgÒ=gP⃩ËYñ°Ã®²Ä u\7qă)B^‰…ψKápð¦W}÷,?ÉöÓ0ðôfö(PI•®B•fC-4¾òOd+z^’-}léy'V[2‹&c)æDÐ äç/ü‹¨ª™hbžØV(Ò-)ÞÑß Pä86”]¨/T…ÎJ^å¼|".2þjùeû“„ðz._5CfˆýË™eçÍ4ûÏ{ºcIÊ.½•IJ¡ÛÇ=R¯Ò8äá–ÏÙ¸(Óô–r›ÜÎèåãkìjÁ`ÁµŒ5Üøû!H±’Îä{Itn×Âp[äÍ·œ(ï °ó‘(”Yks”ßЮzܨ4™‹x² 퉾L§K¯àñð;oZ¨§•Kj&Î|õÛ(cM aRC pçÓJD†a¸m ȈŸ™ËS§ÔMÄ 0KiÒ0|7,`Iž÷Íb%NYs÷¹ñqp† •€±+QüUØ8©TZÂ] ä«Ë#•Vô†¨òqRý–¾þÇ|Õô¶mÑ{þ‡=JA-k¹ü›SÐ$…/I‘=0h‰’UH¤@­ ¸Eÿ{çÍÌŠ¤>lõ–‹M-—Ëá›73ï;øJ"¿€žDgQ û pŽ‘ò”_Íú愇ã7óëÆN?ÁxŠÜŽS r'‚¹âe(qT Ýzõ·¨Î”îÞê½]é÷²¾ ¡ÄkÓü˜ãƒ÷óÜ^îî*è•¿'žœ çóg¹ÀÐ?P²Üˆbf~~‚/=Œ>5#ø»›€žþÕÅ‘ê:vñiQñ„šRk :\ðÂ8^‡Úgžà¾š°•LŸ´5œ}WÞÏÄËOPõ Ͼë¼XµÇžÂD î¦k ßë{µBb¯wÆ>Ä”¶ë‰U×`ŽsêDIj0gÒùK>¯ZÅÿ LÉZ¦8ÿ³ >'Œ4s!ˆ„}mÆŒ¡›èLøBù˜ÊýUxtÁkß‘Nð·Ç4Ê”) §ŠõtÆzÚ:d’s+LÑhti.' Æ‹Í$r}ùŽýÌJ”wµ¤ôèÇt£v•!ý¨c=AãTրݎúïqKv=ªDÃ\[Ú˜Žÿ5!ûYLt ­ÞÊà¡ZŠÌÙJ9Öù^þ¯ªeÈv‘=¢-I3c"âù†é8'©Í‡Þʾ¯æy‘žžùZßFCJ¯6È(üÞå®9±y|\ÝJ &#¦?ïÉÕmf«‡åª~~ðæÝ|×ÌЦùÎÐÅú¬ N¼°mêÙX6…? ö÷²’DRCÀ¶æDe›HD=éî–Qf¶gSó:kZj0·*ÙÊ6Ö7…ü܉ÙYÈéecæ…/ŒþlÀFê‰1ž,ͺ¦Â0²Pr,›ºy‘“k}LkB³€Å¸¿”`È3LXZËÙ°¬p¬ˆÊ±1§€ÕAHAÔ …q ÷ÌP2€H Å;+I[Š‚Æˆa“ee;ÄÁòj½à…iÊlô%HÁ—Jë9Û2çõ°ìW• ŠEø 13GdB§±“S6<—ð™¿äc¡'òÆé0‹Å áè­zùuB´\¤Ž±‰ƒÐ‰l26u¡'ø—-ƲŒ\˜oö3oþ‘Ó,‡I‘Ã$Wižë•ì›w¨¥­o¨žÞËö¾ÀŠ&~ñ˜\¦å“ñÑCdQo8®„£sbX)ã0S¾×Ò™*£Ô$ óZ®h<ÚV1ç=󦔛òfæ€\2tŽ1e1{⩈ð€…ZnØ·–•?ä‡T¤¬9mN9—Š>¼ó¿ÞÎaçáín¸ Þnx£V,¥á{+:Ðæ‹Þ„0Ý] 4×Ô|®í]ê%Õ9"CB—x'aY*¹§hÞŠÅ%½FGÄ5`“µÛØ€™z½Àl_|¹{ßöù øv6\‡o'¢ ô ˨³–ÕÒ+å´9ƒ—˜G&fn"fޤ¦rûM˜2zè<'[°„™¯"Öu±Î†ëkàVŸºdy€ì‹èoRNrpóÈÜ!üµ˜Gz5/‚'¸±¬S– ½±ñˆÕKŠñħlÔÒéi•.{iÁ—Á•ÞkÉd¢ÕŸ€û/I€{)ï'F;c®-Ò >4ÃØ –{žÅ¢Wƃµí$½©KH[õª…%“ÄHzélÞ£äèYÍ{WѤk­¢‰;“Û†Ém> ñš±æ$_¨ý²x œ±† f@ºÀRª€>—Ãý@–hŠ…îqé]ä0Ï|ãiÂçyé&Œ~÷¤Q[½˜ýqˆÏ3sÐÙMܤ2°'<)èÊcÉø:H‰’ŒÃÆdXŽ Ó°—H–ìÞ$ }¹K%¬¿z-ŸíCÂ×·³µ… §'Iý‚QŒŠ$!úûºÜpóJØRÀ8±Ôsá¼›ÚÅDÆ"ëx†¬ÁEv4NIÄœ.V13€Ö—îtöãQîÙÿúø~ó­¼"eä‡xá#¨CMÊo2”²@‘OÓŸúo÷.ó® endstream endobj 135 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F2 21 0 R /F3 22 0 R /F4 54 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 137 0 obj << /D [136 0 R /XYZ null 730 null] >> endobj 138 0 obj << /D [136 0 R /XYZ null 718 null] >> endobj 139 0 obj << /D [136 0 R /XYZ null 706 null] >> endobj 140 0 obj << /D [136 0 R /XYZ null 694 null] >> endobj 141 0 obj << /D [136 0 R /XYZ null 681 null] >> endobj 142 0 obj << /D [136 0 R /XYZ null 657 null] >> endobj 143 0 obj << /D [136 0 R /XYZ null 646 null] >> endobj 144 0 obj << /D [136 0 R /XYZ null 635 null] >> endobj 145 0 obj << /D [136 0 R /XYZ null 623 null] >> endobj 146 0 obj << /D [136 0 R /XYZ null 612 null] >> endobj 147 0 obj << /D [136 0 R /XYZ null 601 null] >> endobj 148 0 obj << /D [136 0 R /XYZ null 590 null] >> endobj 149 0 obj << /D [136 0 R /XYZ null 578 null] >> endobj 150 0 obj << /D [136 0 R /XYZ null 566 null] >> endobj 151 0 obj << /D [136 0 R /XYZ null 554 null] >> endobj 152 0 obj << /D [136 0 R /XYZ null 541 null] >> endobj 153 0 obj << /D [136 0 R /XYZ null 529 null] >> endobj 154 0 obj << /D [136 0 R /XYZ null 517 null] >> endobj 155 0 obj << /D [136 0 R /XYZ null 505 null] >> endobj 156 0 obj << /D [136 0 R /XYZ null 493 null] >> endobj 157 0 obj << /D [136 0 R /XYZ null 475 null] >> endobj 158 0 obj << /D [136 0 R /XYZ null 456 null] >> endobj 159 0 obj << /D [136 0 R /XYZ null 432 null] >> endobj 160 0 obj << /D [136 0 R /XYZ null 408 null] >> endobj 161 0 obj << /D [136 0 R /XYZ null 396 null] >> endobj 162 0 obj << /D [136 0 R /XYZ null 384 null] >> endobj 163 0 obj << /D [136 0 R /XYZ null 371 null] >> endobj 164 0 obj << /D [136 0 R /XYZ null 359 null] >> endobj 165 0 obj << /D [136 0 R /XYZ null 347 null] >> endobj 166 0 obj << /D [136 0 R /XYZ null 323 null] >> endobj 167 0 obj << /D [136 0 R /XYZ null 311 null] >> endobj 168 0 obj << /D [136 0 R /XYZ null 299 null] >> endobj 169 0 obj << /D [136 0 R /XYZ null 286 null] >> endobj 170 0 obj << /D [136 0 R /XYZ null 274 null] >> endobj 171 0 obj << /D [136 0 R /XYZ null 262 null] >> endobj 172 0 obj << /D [136 0 R /XYZ null 250 null] >> endobj 173 0 obj << /D [136 0 R /XYZ null 238 null] >> endobj 174 0 obj << /D [136 0 R /XYZ null 214 null] >> endobj 175 0 obj << /D [136 0 R /XYZ null 189 null] >> endobj 176 0 obj << /D [136 0 R /XYZ null 177 null] >> endobj 177 0 obj << /D [136 0 R /XYZ null 165 null] >> endobj 178 0 obj << /D [136 0 R /XYZ null 153 null] >> endobj 179 0 obj << /D [136 0 R /XYZ null 141 null] >> endobj 180 0 obj << /D [136 0 R /XYZ null 128 null] >> endobj 181 0 obj << /D [136 0 R /XYZ null 104 null] >> endobj 182 0 obj << /D [136 0 R /XYZ null 92 null] >> endobj 183 0 obj << /D [136 0 R /XYZ null null null] >> endobj 133 0 obj << /P 103 0 R /R [63 63 549 729] /V 100 0 R /N 184 0 R >> endobj 185 0 obj << /Length 3828 /Filter /FlateDecode >> stream H‰¤WÛnÜ8}ࣤQ¢n3O™x&È“Nc^ìÀ%º­ÝnÉ«V{à öß·.dKêûì€[¢Šdñ°êÔ©_æ¯_½ûM %æ¯_å2ODô‘ŒSÅ2OÅ|õúU `ýñ›‹5¾ÍKúÿçëW^êÏÿ‰+i»Ò,TRGQ$’D¦Y‹ùØÊ 5M»ñê¦ï¯ÿðéîó½¯Bïë¦ÿãÖ+ºUYß-êöù®oªuWÞ]÷V 5<Ü- Ú6<ðÔµ%?½éÌ¿o}ÿûüèÑ,”ZÇ9í9Þè#M¾l›æ-Ún·q‹¿û-²'T2 ÒHÌ” U’oÏeö|_7~.3¯óg~žè¥]û© =ó“[ÌÁÉ$Õ9OËÐ ž°ºË d¦#ílÊ”oƒˆ®áÆûhÓù*Ú+ü@æÞ²ög™Œ½¦ò3™zâÓ»/âÙx6eßv¢¥ñ'ÉÈ3]Ñ×mæýcÑ‹¾+øuíÏ ¼^Ýt¢*úBØW8¦›vçLp¦á—WÖ^oH6nK³ö©Ü’‚ 9¼j»ÞþÖc«vCï]in}Ñ·b{Í} èmAÀÝñöÊb‰‡O¼ì8‡Q#°f w†YÜ¿ôf} ßÑüÃøŽ .ÂwìÑ‘ðs”iÑÛ\ŒÁc‘÷HòÓXô˦gaÊ´ ãƒ19€ÅшUò$bÃRG\†Ø0a†8%ѲÜAöÙŸaÝ@¦†àëꞈ38?AÀ(º§ªàOÀÝ@(109†,@;S\¨vˆ7¥ÀÆ®ÖØáž ø8¸Ì¼qZj î@u¬Ü­.ùy·\L!Qƒœ ’ÔÉ«÷¯#o"¼† Ì:_bM]¼ñH±s ^xï̵>V/ V³A½è–§{T¡ ²÷>n¡ù®xÞ…K ‘xñYÀøÂGØ)n=‚âåhãØ£0Âø9pÆ}øÝG´q–J¹ñJx&e—¡BRc žAybaÀS%“beHJdE¦ –Züjü2Œ§8T+šüÿl˜E‰ÌâóAºÚÒÞœºÂÉÔ?/¹ÈpH u÷øË.fèDÇåK³"¢ŠY¥Æd‚§%y±lÔØ~±½k_¯pãÄ«@ìR Õ!ƒ[E¹ÕaÁÈàZñ…?Ûf·âéâ[¼#‹/âÙW‰¦ì[6°+1XÙ!*±ø=éAp ¥õ6Âô!³¢½µlxdýÀk·6;V°cn9ûJÛ Öž÷±ÇÞîZŸ?”ÅÒ.As­=¶}Û.£DcyºÝKÀ2ËÚz¼°Od±uƒ¾ÍøŠƒI$ÒUe9VwØv-s:̾µàM†›4.bp „²e2ÈÄ ôNÇã/l)xÊr \OXÕ².6ï—mù/¦æV<ñ–ý¹Â%°…ÚÃgíðaâ;…ÒdË´Á¡ÒŠ e,̰JL…+¶Ô€à·þ&¸dö>†®³r±7þZ4¥-°X„ïýèC°/Çt]J¨D{àLtÝ,„ü ­¸Õðk«V—KÂ+Ö(¥/S#[˜®ÌºG"£ABá/½@*¨f ‡ÓÈÂÇô­°+$kÏ~ÆØBR^ $eBC扟7üÓ³šÁi×ü¾8IÐhNáoÄÐ0õÿŠ¡+{×¢Óˆ;P#B¶:;©RÈ»|;ô¥4v”$¿{úy¡ÿlêLÎbVËX¤Äqå=pÅ#Š4¿øÈKCÀÑÄš›†šÌ6Eù¨äJü XwMÝ@*÷{I•¥ã»Ñ£³Ýßw<Ö(óâ±Ç£–'p-éÉÈ{$îÌÉó uö âÞ8 J6djšM±ÝEÂL9™iü…þÛ9†—bÁÁìcWLf*•pˆ|¯ñ ²%ú˜… °rm«û†ø£}YC¬PV‘)%¤G!j;™|e¹BQÑ\÷Ñ©´©ø…˜f’²$‰œ¼Um5–<A‚?“Z€ÙËyl¬¼=•Ç“ÎFël§QŒ°st8…‹%eôÄÒ y+4eì+dr§'Q= ƒ!vüT30~yÞúað¥ãb_‰†ûº ò¨b· F¹˜»\¼6ýÆç¶n†M^ÃÄ!ž ~Xnø:”Œâø ºI.“qYòéÚèNì/+CTÙVblJ$sΘÕ9ÒÉfy6âTê|OÜx-H²ŽóÐXn̉ì-ò?Ö·_"jít¢\¾Œoâ²%°ìê{Ãß×Iñƒ™èÌÒl„ÙöÖÃ¥Õ3¥¥úoçÕÒ›6„ï•úöU øvoiSU9T©ÒÜhT-ØK€#g)ùõùfl,jš x×»ëÝٙÙ<y¶¼—·ß¿\ÿþVšþÐîT#ͱÕ½1DÁdÅØÝ‘ƒ£¦àâ˜M”Ka–v³9Ð+^>J ‹Èø¥À+%œÙè‡hžÙB>€t•‚÷ûÀäYO¶™£árþ›‚Ôü´ÃÙs©cXÂfÇ^s>¸¬‡q4X‹á#­æÔ=~RרƒmÁ”î ëϱ0í©çÑ^ÔÐz-ó—Ñ×0›?NV×1ÄÖ²5îÒæ9k;ÜÑ@†‘ÎãNäbnyã‰è¼Œr6ðªi±ñÂ×Uf]¸>ÐâtÌ:9ßÖ´x»­áè4ʉ¢c=¯‘BÔÂ=moPÁÍ·vçË’ bÁÈ‘úRdZ8œ( ÇDàè Ç8á($åN§QLÿ Ü ñçäUD:29ƒ)%\… +(zˆóGQ8Ðâ $÷¢‰úÖC–•†T75Y.&î½ÂJ \%¯‘Fky& ”2:Ñ¢e{“+$äjWGç?’«µÀÛ“ëJ“€ÁqÚª˜¥ç(du:©"H©)ÙÞ, õ¥tP~ñß ~e¨Ò)¶rMí%¾ò´\lO8•’*ygg>ƧpžD O¢tKt¾\:Ü]ØåE%ƒ+‹‡Ÿ ™)¤ãS„ ÒgÍ»‰g›OîvLô2™%Í7túÄët#¦öhšaç)¹fQx6ÄŒ (C· Ö ‹wºo %ú_ð«s Yž‡;WlEO/£`6¦Cd]òNäí#»¬`m•]©ÞöüÉKmˆ‘5¹2¦x9gçPìu†'J·9æHk`L.ÏåpOØ3¡ïÈïC>%”¼ß\_v¦¤é“6GpñJª<ÿQÆ­ù½¹::ò6ÉïîDÔ<@ÁäšMÁ˜ ž‡Aâ;9R¢µΔv¹bàš2(ÖòTJXr‘+˜÷Ô‡±qÔ,RÖÙ§k´5ü¼M ¿·t·(¤†!ËΡÜiÈ2NÅ:-#ò“…ºüšë+’eò\©ªg½¨'üz÷þÝ_yö0Á endstream endobj 186 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F3 22 0 R /F4 54 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 188 0 obj << /D [187 0 R /XYZ null 730 null] >> endobj 189 0 obj << /D [187 0 R /XYZ null 718 null] >> endobj 190 0 obj << /D [187 0 R /XYZ null 706 null] >> endobj 191 0 obj << /D [187 0 R /XYZ null 694 null] >> endobj 192 0 obj << /D [187 0 R /XYZ null 675 null] >> endobj 193 0 obj << /D [187 0 R /XYZ null 657 null] >> endobj 194 0 obj << /D [187 0 R /XYZ null 645 null] >> endobj 195 0 obj << /D [187 0 R /XYZ null 621 null] >> endobj 196 0 obj << /D [187 0 R /XYZ null 609 null] >> endobj 197 0 obj << /D [187 0 R /XYZ null 596 null] >> endobj 198 0 obj << /D [187 0 R /XYZ null 584 null] >> endobj 199 0 obj << /D [187 0 R /XYZ null 572 null] >> endobj 200 0 obj << /D [187 0 R /XYZ null 560 null] >> endobj 201 0 obj << /D [187 0 R /XYZ null 548 null] >> endobj 202 0 obj << /D [187 0 R /XYZ null 536 null] >> endobj 203 0 obj << /D [187 0 R /XYZ null 524 null] >> endobj 204 0 obj << /D [187 0 R /XYZ null 511 null] >> endobj 205 0 obj << /D [187 0 R /XYZ null 499 null] >> endobj 206 0 obj << /D [187 0 R /XYZ null 475 null] >> endobj 207 0 obj << /D [187 0 R /XYZ null 463 null] >> endobj 208 0 obj << /D [187 0 R /XYZ null 451 null] >> endobj 209 0 obj << /D [187 0 R /XYZ null 438 null] >> endobj 210 0 obj << /D [187 0 R /XYZ null 426 null] >> endobj 211 0 obj << /D [187 0 R /XYZ null 414 null] >> endobj 212 0 obj << /D [187 0 R /XYZ null 402 null] >> endobj 213 0 obj << /D [187 0 R /XYZ null 390 null] >> endobj 214 0 obj << /D [187 0 R /XYZ null 378 null] >> endobj 215 0 obj << /D [187 0 R /XYZ null 359 null] >> endobj 216 0 obj << /D [187 0 R /XYZ null 341 null] >> endobj 217 0 obj << /D [187 0 R /XYZ null 317 null] >> endobj 218 0 obj << /D [187 0 R /XYZ null 293 null] >> endobj 219 0 obj << /D [187 0 R /XYZ null 281 null] >> endobj 220 0 obj << /D [187 0 R /XYZ null 270 null] >> endobj 221 0 obj << /D [187 0 R /XYZ null 259 null] >> endobj 222 0 obj << /D [187 0 R /XYZ null 248 null] >> endobj 223 0 obj << /D [187 0 R /XYZ null 237 null] >> endobj 224 0 obj << /D [187 0 R /XYZ null 225 null] >> endobj 225 0 obj << /D [187 0 R /XYZ null 214 null] >> endobj 226 0 obj << /D [187 0 R /XYZ null 201 null] >> endobj 227 0 obj << /D [187 0 R /XYZ null 189 null] >> endobj 228 0 obj << /D [187 0 R /XYZ null 177 null] >> endobj 229 0 obj << /D [187 0 R /XYZ null 165 null] >> endobj 230 0 obj << /D [187 0 R /XYZ null 153 null] >> endobj 231 0 obj << /D [187 0 R /XYZ null 141 null] >> endobj 232 0 obj << /D [187 0 R /XYZ null 116 null] >> endobj 233 0 obj << /D [187 0 R /XYZ null 104 null] >> endobj 234 0 obj << /D [187 0 R /XYZ null 92 null] >> endobj 235 0 obj << /D [187 0 R /XYZ null null null] >> endobj 184 0 obj << /P 136 0 R /R [63 63 549 729] /V 133 0 R /N 236 0 R >> endobj 237 0 obj << /Length 2996 /Filter /FlateDecode >> stream H‰ìWmÛ¸þ ÿÅt­%ê-‡ØÛí9$é!1ú%[,´¶ìuk[†,çn¯èï33”-y½¶{rAÐXSäp8|æíáÃçϾûÑ(£†“çÏr?OT€ÿ<ˆ‚ÈSÅ~žªáâù³@M!ýú£QÓ5} Gü÷—çϼLÿAš"§i`r? •d~`âX ¯!êAó®óœ÷~ò>”ÍF'~îÕzú©·ÔƒØ7žú\È`¾‘ßò¥ÎüÄSúïßè0Ûš-6+G>Hã›ÈYL§y¿•uÅö/†ñcyºÍÀݦÅ%ð“ ´ØÖ^# åòt£ÌÝB}ÜŒFåZÇ0QþNô ôCo3÷OXœd9`êX8Us_ÖÚD~DhÌ7¥(:xŽ’3.î.µø Ž |ëÕ•¨Q5ä©WêîQ7ÞLàŠQ=»+eMÍ–ª¹1U—ó²‘R-«Æ‰ßh=0p@ b*±‰àú6‹Ø,މxgW–:»¬ÍA’{WÆÏ¼Õƒ’aê­ÜoI¡{u!¿XΰJU-ØZMHLªp¥ˆ"Ž¿ÔHöµj—{ ¦,½q«­Ô†g×ø²ä!mBoìLìÛQìD‚4R%‹ŒÝ¹ÅöÜ?[6êòû«7·?oHysã­ëÑ…¯› u÷t/Ôª®F7z›Ñ£B“äÛ¢ŒøäAaÃj±oÅÕZ§ˆW¤Ùžµ‘Ÿ¤6oÃGrJ= ±ÌFÝ‹¢]ªocl¨£„AævLÙâð5åx„œ1‘“"øa6ÝÈpC¡„pM¡&5ÉU Š‹ÀuD¿qgƒÜ£ó \_þÿî(¸Qáô|×—ÿ#\”†çìï$p%_;˜†ÛD!‹¦´5«¥U1ôq/3ABÝúr4Ú,Pu™¤ó‘\­X¬¬)&—\°6\Þ30>^šr]§…i¹,ùZ1ÕuoN1mÑF©ÅÿÆWAF¿ùî¯ê³©–£¦ªÁyxG%‡Ö‹¢ÙwåŽ f†šü)z9"kF»á¹qÑÍê¼PŸ«ÙX½XƒŽà£¨£Ùí‰ÙÏ·z1F;½-êúBñ¿6@~Mwà(ÚÕ¾—Ë Eç&·È¶Èæé#NðåÈ-1Ú6S‡Õkç)ø‰°ŸÏÈU¹sRÔqRäœTñüÊ1O mî ÑT:¡7¸¢kþ 뛕蔓(ZQØvD‘Dï¥æ«E¹sêw §7ÊT¡ØÚ’u,*®°™°\Z¯¤(¤žüuIDÃfv„::ïK™Ñï …÷ÇûZÖw¨üvè^÷ár¨a^®qÜêÔP§VÚ5Å•,–òõÈí{¥,Lppz ”Q”¥”•ð’z3jÔ¿D›„°u¬(D’2q¿jjÄþ÷‹_–ñ±8Ýšnñkí s»/°•{s»Þ†§ &qÏL †2Jª]£‰D:öYH•×®ºâÈ•kêjÅ"Q£Êb$唫ƒj7 ŸæÞ”"-yy„|&!_Ø¢÷ÄчŒôxwÊž€·#p¼ó„­p¸_3J–!̽™¼y–:'Ö!>–ª+õˆæTÛÄA '視%ôÕ8 ¨Å÷; Y”&§8f!ýû°#C?;° fÔ]׺™÷Åw·ÿ |;çáÛ±è‰ð¶M]bê:8ÞC ÅàS‘G ÿNšäƒôŶ͟„Éä‡yN,‰Fj]GÛ©z±ŽÀyˆí6HÞµ¦¼…ì=ã8±[oqÇ.Žî_s5fÛž”¼$õ›¸…, ‹À¥7™hA¼á-·Ú–nº‘|ŠD†AÖy=uÀý7Ñ WHÅ÷ûý¢‰ d“'~¶ ¢ÈºØ¾¬µ¼)±XXË ã¥†ýæ$A7™í[úÉc$6¢Oa¹½¿óux©Sp*‘{ŠÙ¼¸x皡ǃßtáwŕ̻6¸¦ò l‰èH`ÂGýÖaÆ´6ÔôÒÔ1rE+òdö–êNâ½y?TAô•f\gš„–²ÜðÇ…f¢~ùw¾ãjõ¼¹e]ꈞM‡Žxûþµ¼RsVU±ôrzÑ%%ZHݰ½Š1Ý«“æ« ®:Žfo ®ÜûQSŸ{«³ØR9AbàØÉ\µF¢àM ÕP~\ ‘`<ÕSål=}^Ýʦk¡h?ðä[hË<è:%)ïÄ:ªä3z×뺊ÚQ[#ãíõ$²aRåË]jK\øJe—âvîg†”lbº_Éₘa,zU…Ä«•µ = I®$uïœ"I[pÿTâdïÄÄ»n|Ká`ôJÓ_ eZ¯XÁFN¹“Ÿ9ÊÊx¼¸X¹éR¼ó+Ïžª8&Jü ›Ç?_¤LÞÞ†_Îz¨šSò6…?Ò1jB¶£ènŸxÜÅA¯yZ¸Qt©iJ•'Ëw†Ô\öqƒiun¹Q–Ò+¡«iKùVí¥n´j*uWªb4ÚÉ%7/šrL‹ðÂ{¶R®ÜR@&}ò «‰6º×ÖÀ Þã‹Zûî•›‹Œìd€?$ù 0î±³<âûÞÎóòaÇJ‚ô?ƒÅ¼_)¸Ã™›”ZoÜæ¤æ‚3³)¨¾0õ6aÛÌ<°jH¶ÁÕÀæ¹6Ÿ¡&khÈè›ffæ 56»UäɸK`ü›_€«cDenfóyP*¤öÎ% PÁj¤–@Eñ‡‰&Ã;² ª¸XÁ$骑Ê:XƒÜÂ3Ũ$@…¬´„HA©%¥ÀÙÔ•Òy +€ÅHY"„‘S ¡m pl—k/““ÿ endstream endobj 238 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F3 22 0 R /F4 54 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 240 0 obj << /D [239 0 R /XYZ null 730 null] >> endobj 241 0 obj << /D [239 0 R /XYZ null 718 null] >> endobj 242 0 obj << /D [239 0 R /XYZ null 706 null] >> endobj 243 0 obj << /D [239 0 R /XYZ null 694 null] >> endobj 244 0 obj << /D [239 0 R /XYZ null 682 null] >> endobj 245 0 obj << /D [239 0 R /XYZ null 670 null] >> endobj 246 0 obj << /D [239 0 R /XYZ null 652 null] >> endobj 247 0 obj << /D [239 0 R /XYZ null 634 null] >> endobj 248 0 obj << /D [239 0 R /XYZ null 598 null] >> endobj 249 0 obj << /D [239 0 R /XYZ null 574 null] >> endobj 250 0 obj << /D [239 0 R /XYZ null 562 null] >> endobj 251 0 obj << /D [239 0 R /XYZ null 550 null] >> endobj 252 0 obj << /D [239 0 R /XYZ null 538 null] >> endobj 253 0 obj << /D [239 0 R /XYZ null 526 null] >> endobj 254 0 obj << /D [239 0 R /XYZ null 513 null] >> endobj 255 0 obj << /D [239 0 R /XYZ null 501 null] >> endobj 256 0 obj << /D [239 0 R /XYZ null 489 null] >> endobj 257 0 obj << /D [239 0 R /XYZ null 477 null] >> endobj 258 0 obj << /D [239 0 R /XYZ null 465 null] >> endobj 259 0 obj << /D [239 0 R /XYZ null 453 null] >> endobj 260 0 obj << /D [239 0 R /XYZ null 429 null] >> endobj 261 0 obj << /D [239 0 R /XYZ null 417 null] >> endobj 262 0 obj << /D [239 0 R /XYZ null 405 null] >> endobj 263 0 obj << /D [239 0 R /XYZ null 393 null] >> endobj 264 0 obj << /D [239 0 R /XYZ null 381 null] >> endobj 265 0 obj << /D [239 0 R /XYZ null 369 null] >> endobj 266 0 obj << /D [239 0 R /XYZ null 351 null] >> endobj 267 0 obj << /D [239 0 R /XYZ null 333 null] >> endobj 268 0 obj << /D [239 0 R /XYZ null 321 null] >> endobj 269 0 obj << /D [239 0 R /XYZ null 297 null] >> endobj 270 0 obj << /D [239 0 R /XYZ null 285 null] >> endobj 271 0 obj << /D [239 0 R /XYZ null 273 null] >> endobj 272 0 obj << /D [239 0 R /XYZ null 261 null] >> endobj 273 0 obj << /D [239 0 R /XYZ null 249 null] >> endobj 274 0 obj << /D [239 0 R /XYZ null 237 null] >> endobj 275 0 obj << /D [239 0 R /XYZ null 224 null] >> endobj 276 0 obj << /D [239 0 R /XYZ null 212 null] >> endobj 277 0 obj << /D [239 0 R /XYZ null 200 null] >> endobj 278 0 obj << /D [239 0 R /XYZ null 188 null] >> endobj 279 0 obj << /D [239 0 R /XYZ null 176 null] >> endobj 280 0 obj << /D [239 0 R /XYZ null 158 null] >> endobj 281 0 obj << /D [239 0 R /XYZ null 140 null] >> endobj 282 0 obj << /D [239 0 R /XYZ null 128 null] >> endobj 283 0 obj << /D [239 0 R /XYZ null 104 null] >> endobj 284 0 obj << /D [239 0 R /XYZ null 92 null] >> endobj 285 0 obj << /D [239 0 R /XYZ null null null] >> endobj 236 0 obj << /P 187 0 R /R [63 63 549 729] /V 184 0 R /N 286 0 R >> endobj 287 0 obj << /Length 3522 /Filter /FlateDecode >> stream H‰œW[oÛ:~/ÐÿÀG*눺«Àp“ölmO‘û’†"ÓŽveË+ÓîIýÎ…´|imïi˜"‡äpæ›ofÞ^¿úíƒJŒ¦¯_~‘ŠþÓ "?ÉD”øE&Fóׯ1éß¿*1[áר¢¿ß_¿’…7ú7ž»“ø¡’È ‘©ÈøùCw-Iï߆ɡäÀêÔ ü4cØ0ºÅ CÖ†ANе܃lͳî<ÔLŠMÙ¬5ôóô‡\ð€¬@äð® üXv­ˆªx¹ŸIí~!Å£œÀS_ɪ«Ÿ4¯‰z!Ì3‹‰N7ºd-­±âž7P pdˆŠ}•d‘KÅ¡Ê{ã=CÔ #†÷ŸnîÆŸŸ<ÊaUýë‘f'¥)ÍËR_‹M[OÄÕª*ø(»yUg([·›±W“UWË®»¬¼0 ‹X€bat32ãF/®~,»¶âÑU§ÿ ïØ7ì@…~¥™€#Ã8Üê°¨z°Ð#R°E6¨ ý±ö 8wîe`ûÑ”F‹Ç¡\ÒÀœŸÊÒÔv~Á¢ß·æ©•7È@¶^Ж™ÏÐ@“¨•1:ÃZÓÇ„/Ý`Hç‰)¯·´b…çÞ `”æÐ‘ÊÏt£ý=ëÆa…úT_O:?®ø eÇKÓ±'øn°`„{î{èã•éꉗ¸©{øæŽ™¬L~89AQ^¨ÚõÂà¦þ¢ÑÌÒÞè†LÙãPa­9X~à"åTZlÍ1*ä—µW°UcøYÒG»„R¿9´->‹ ½txþ/DøKSÏëz"8,‡>ÅwÙP¾ûí±ñÂc¹2m0Äù%:?",jžKÓÎAwÑ DƒÛ^xy½¤_p,Pà©`¬–st1×s¾¡{¡åv*Jaɇ9g"ø¬Ž9¤­,©ð_ë$æXí!°¦~”ŒY@¸D]rP×@” l<¥’ÆHQ(æù2ö•Gûg£/c{Á°óâHÎУ±œë…Yq½ñ”êü%ù'1@ Ø!ÿIáÀ´Aq‚ó÷÷~¿„ùƒ´'¦Ø1ÿÐËB¹ñ"t|Y7å¾ –tp–ĉ,ÑàÃR˜°}ЏÀyÍó+`NJ¾…pàG £€-7 uœH Ä‚À ^‚ŠÜ C"ɳcÌ¥©¼û<¶ë[¬[{(´àeC×%¯á=íüÚ©í9wcrÓЋ?»âãçßÅà­h訖¤³kû #è¶oQj÷-9l¤·(Ü0}òìíUȆÅG/OäÀQ÷N/k!< ’¶‚W ”{‡G©Ÿ7cÞt‹/ä;šü'Nx2æ ?±x•¦;®{ª#d$=y$ÛG1¬ávL`HzÒЋ‘¼nx‘×h)qs_È’¨ Q~Ë‹s êTp"þÉkר3!âqŸìAp%nWg ƒSyë®üˆ(N”à_D0ç2<`Í·<ñOã·óÓâ|i§5ûäOš=WÁÅÄÃn')…ýºÜÜßpQ䆪wJ±-:ñ1Eݕ쀕9Ñ#ƒ`¹FÓÈz)<ˆ¿ ;V‰¤›ö Nø›«R¦q-®HRØ©ŽR¤º³–Èã}KpR^2™î›ío½È&?¥ñµe2ÆŒ!+MÙ.·äPv@þ3zm9™tÌ%zåj(íÔR7šsR¢ê)‡a1©àƒö?5mõNk­Xò•æ¬}2,\Žì³röá’䔕ö¸ 9?«öÑH;iÌTºú¿|±‰ÊkGå‚àzÁfýÝÕráÒ3öOXKäò…ë,~aœ¨€X‹Žc_èÓ !ÄLˆÅ6ð9ü³5v—À+ ÜáË™éV¯v«…-ÄÊ”W vy•„('.ŠŠ »ìjŽ•0-/d–<^óá ·Q–´–g‘çî=l„ÿCý֋놸¯ÒCã$;ÍI*í„(NlþCO‹ ¢Ê2NŽ5…‡)š„jb?seK.@2»´2ØAa(.H¨Òö:°¶‹¼TQ{¥q¢gmÁnµËçÔœàó½ —qW²Ã].ËŠw² V±tôBì¤dM“?4FNë²zÓð>^‚âc¯€–wáÕΠÍvy!ÞyÛCðí@ã{¹dWãbGãm ¶%’ÏĹižc¢Gž´+ûI†$´X°èüIÿ¦È¸Ì¨4ÿBíÍÇ‹–&§¶2à· TæÃ#Š£ª,èë—ÐY¶ÂF6qà4õŒ‹×5ÿð¢ýX•u6 #,{ÔI׆€¥K9¡½nÑžËi˜Çˆ[ðòAÆ˜Žœ|GÎV¹³èWî¡Ó¶§ÿ¶I]Šÿ½΢•†[›&¡óýˆãêÙ£²ÃW@Õb¹ñTâ&ÑV˜¸ÉÌЕØC‘v<ªÙ0ŽPÚ·:k‰ 8ˆÓe×V'ÂtOþ²0uo½×s®( P[º%yš"Z‰;ž¸E †ÎuŸÄþÀU'úOðPÅaìÄbábñ^›5@‡Á“1ž álJ4kþ…-'Ÿ¶n”¥~¸Ûšùtí¯­»/YiÒ£*È·¥ÉºBRçøÀ¿S¦“usQû*;* d ¥\ÇñÖhÖúDtìòk«÷®’jíº .™´ga1±eRÕÕOš××Í3‹‰N7ºd ÌÖXq„ –d¾c@è!Ó8P` éÂ5P–2j3}d«cð^»"e€ˆƒŽ‡ŠÞÖ.ðYtÊk/´Ù ~ïø·6ú(·`ªˆ¤¹*âÃãœP/ µ¯wãû92Á÷Gšj—×bÓÖqµ„Šz;îôüZàú†ŠuðÙµÀè„Hû^ˆrk‚/kê^Hý*/üh1¯„ O*6‹ ça†½8‹‚¨GAf_K½hL=g¹®^¤ÁÍ¥iç X-¤Èš/$E KQ ôY+B,k"Ôv‚9% ï®1çèN·n{¡Éñ0K‚ÅÄ÷Ú<»Œži7v»ˆL4I¸í#¸W[¼É#g:F=\ÏTó1V íaQ×ãa‚nwnÇÀ€îŠ Ê²½ÜbÉì’hJÉßîQ[)ÄWoñâi›ðM!U–‡ WæE´‘ý››–o^xH³ãû³< ÏÄý£Ç}¥á¾¨·{"û°žskùcîÆÝ‹hxTARà ü½å‚Øilö° ‚¥ÝíáêY½(pCR§ŠTƒn˜2 ‘_#¹v'–Ž€?¿ºû8Fuœ‹¢ØqØyq$gnTšgÌ7Xæ©Þ/}… éO¼’rصKw.6!bÃ"Ú‹Í‹MÚû+v€zY,É\™,ë¦|jÀ¢KʺÃüŒ¦†€íMh€ðˆiTåÀ0¼Ç$ÿé¸snÿð~„Ÿÿ ÏH?ßÚ…áí­¼¥ú’:J{Ùÿ˜¯š§zGâ?ä˜"m•É×$$`9¬Ø•X-GÔM¢¥"mª~üül§“i›mà€8µñx<ÉûùÙZ#Œ¬,Q3‰ ×>šÆÍWmw÷_oqݵ• ë/ürӞɇD¯ÍÇ[=ކ߾Sšãµ>PBÌ?0U7”&|¯:H>-ï?íñÝŸGü!î@5÷º‚žCtÊäN™ÊGZWË#š!ŽOå  «ßRòE®=Ä}{:j1ª2£›ˆ).#£’.¨ÂÙ~²OƒûãMÔ9(Žô©cGnu†ÚwŽÑäšj2evŠñÔ8¾ÿ4ŒC ô(À&a³-]¢b+ÐB[)uZ‰ í.`þU‡Sw>WN ƒ€WA*’y6”–¡èÈq”¼ “P2%sD鉾à9Ô`>’¹ÓЬjΑ0x–G”$F-ÙP­5;ÂK@Ôöˆ&‚h$¥ Ñ¥äùÒªn¤,³S´æÀ×g5ßÚš8–Ïs7¸‰[¡»±Ò-ÁDj%MIöÞZ‰Q×dž+0Í¥®Ž<ѧ±KòÌ%y¦§3©@Èotå[©à,|æ*ãJ +¡« Ûå–­ä-‘òcóZ\Þ‚¹¨ÕÒM²r^YÝó¼I—¸X³¹óRƒ¼°±µÑ[ÉR€_À;w÷ŠîÇïUÝÿŠ1\W(ÿ«rÞSÿNÎ÷=üãìÆ-+)BéAoþ%íf§üŠzѶ,_ XnÓL?ù¿•.Ì!¾Úd‘nG©‹ôuPœ3k¨è¢Ò S=W46ÅÄÂ~Ù%‘EU”DCéÚ««­Âa(ÒÄ#mWMñQ3-¤4ªê@™‘#kéô4”§v!‰â&D'õŠÜÚað Åh)H 4Ì/%’Q“ô˜† kŠ}°ÔßiÊ7µ$õ^IÛ@e¨£Fæ©*šó<#u¦õH-;-I©âB­ÿØà6²3Õ;Õõì5™òœvO€EÈÓV, Ru+qi…Ê5“¨s^i‚Z7îIßC•ž+ó Jú¯”ùwÛlNÓÓdêöÜÿ#æþôøöÍoUÇê endstream endobj 288 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F3 22 0 R /F4 54 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 290 0 obj << /D [289 0 R /XYZ null 730 null] >> endobj 291 0 obj << /D [289 0 R /XYZ null 718 null] >> endobj 292 0 obj << /D [289 0 R /XYZ null 688 null] >> endobj 293 0 obj << /D [289 0 R /XYZ null 670 null] >> endobj 294 0 obj << /D [289 0 R /XYZ null 658 null] >> endobj 295 0 obj << /D [289 0 R /XYZ null 622 null] >> endobj 296 0 obj << /D [289 0 R /XYZ null 610 null] >> endobj 297 0 obj << /D [289 0 R /XYZ null 598 null] >> endobj 298 0 obj << /D [289 0 R /XYZ null 586 null] >> endobj 299 0 obj << /D [289 0 R /XYZ null 574 null] >> endobj 300 0 obj << /D [289 0 R /XYZ null 562 null] >> endobj 301 0 obj << /D [289 0 R /XYZ null 550 null] >> endobj 302 0 obj << /D [289 0 R /XYZ null 538 null] >> endobj 303 0 obj << /D [289 0 R /XYZ null 526 null] >> endobj 304 0 obj << /D [289 0 R /XYZ null 513 null] >> endobj 305 0 obj << /D [289 0 R /XYZ null 501 null] >> endobj 306 0 obj << /D [289 0 R /XYZ null 489 null] >> endobj 307 0 obj << /D [289 0 R /XYZ null 477 null] >> endobj 308 0 obj << /D [289 0 R /XYZ null 465 null] >> endobj 309 0 obj << /D [289 0 R /XYZ null 453 null] >> endobj 310 0 obj << /D [289 0 R /XYZ null 441 null] >> endobj 311 0 obj << /D [289 0 R /XYZ null 429 null] >> endobj 312 0 obj << /D [289 0 R /XYZ null 417 null] >> endobj 313 0 obj << /D [289 0 R /XYZ null 405 null] >> endobj 314 0 obj << /D [289 0 R /XYZ null 393 null] >> endobj 315 0 obj << /D [289 0 R /XYZ null 381 null] >> endobj 316 0 obj << /D [289 0 R /XYZ null 369 null] >> endobj 317 0 obj << /D [289 0 R /XYZ null 345 null] >> endobj 318 0 obj << /D [289 0 R /XYZ null 333 null] >> endobj 319 0 obj << /D [289 0 R /XYZ null 321 null] >> endobj 320 0 obj << /D [289 0 R /XYZ null 303 null] >> endobj 321 0 obj << /D [289 0 R /XYZ null 285 null] >> endobj 322 0 obj << /D [289 0 R /XYZ null 273 null] >> endobj 323 0 obj << /D [289 0 R /XYZ null 249 null] >> endobj 324 0 obj << /D [289 0 R /XYZ null 237 null] >> endobj 325 0 obj << /D [289 0 R /XYZ null 225 null] >> endobj 326 0 obj << /D [289 0 R /XYZ null 212 null] >> endobj 327 0 obj << /D [289 0 R /XYZ null 200 null] >> endobj 328 0 obj << /D [289 0 R /XYZ null 188 null] >> endobj 329 0 obj << /D [289 0 R /XYZ null 176 null] >> endobj 330 0 obj << /D [289 0 R /XYZ null 164 null] >> endobj 331 0 obj << /D [289 0 R /XYZ null 152 null] >> endobj 332 0 obj << /D [289 0 R /XYZ null 140 null] >> endobj 333 0 obj << /D [289 0 R /XYZ null 128 null] >> endobj 334 0 obj << /D [289 0 R /XYZ null 116 null] >> endobj 335 0 obj << /D [289 0 R /XYZ null 104 null] >> endobj 336 0 obj << /D [289 0 R /XYZ null 92 null] >> endobj 337 0 obj << /D [289 0 R /XYZ null null null] >> endobj 286 0 obj << /P 239 0 R /R [63 63 549 729] /V 236 0 R /N 338 0 R >> endobj 339 0 obj << /Length 2891 /Filter /FlateDecode >> stream H‰ÔWmoÛFþ ÿa?.ƒ#ËårùRà>¤y9äPE¢ûEѶ®©’”ç×߼쒢$[n.ER°HîììÎÌ3ÏÌü´xþ쇷J(±¸zþ,òD„ðG:TAh„6AžŠÅæù³P\ƒô¿>(qÝÃ[†‰X”ÏŸIz‹ÿ¢¢Ø*ò#¨,Ô"É‚P#¯yCÓŽP,îž?û(×Í ^¾¿xõî·—µ§"Y¿­š²º”—ž÷ëâߨQ[*ÈÂT _‘JòQŸÎHßGùËÎ˃LvžÃÏ–^ÚÞKƒHV?:eîz:HÒ8;I Û ¬ Îa`ÒL;8*T$èãs®¬?y~$./[/ ”,=¸b*÷|p›ì=)ø‘7$R‰²¨ëµýÖзk–[þíàâp~)«¾÷ü íH¸áïúEmÕÔbpÚí¶•‚«õC{¹ÂkoïÐì>ðt±ŽÓ=Ëe¼ÝA`ãàö¨1:Q–Gv y(œ‘¯¤h;ëâ)¦aÍ\¼· 1G.˲DëS¹Ùñ{Mßµ*qx¨0s|¤È¤rËâ@DÃk1¬[VÔÐr‘„”,½A ÞgUpŠÝXy*Ák+0ÜJÚ¥}óù:~ÈÐyN˜ ã|òMh“ñ[+k 2œßzuÅ8Æ ú Ül„ôXÙÏ×=à "ÚаÈ~`r¸;`PöJY tD7¢lo”ŒÊí}òt…I»@ð%€§P≔RžJ`å¼Fpå-þ"öЇ”„ࢃ¯úPîè§¥=M˜“S‚g*ÏÆ«=RCõÛf7TŸªþ’VËv× ò‡‹Å_Í ^~wdF'«²9¯Ð'ø³»®ÑѰ à1òÖƒdÄàƒ‡Q¢êµ aahEÙ¡ù¢÷0Ï+/„½CAW’DÚ+±Cb¤¸2d ¦o acµ´c¶¡€%Š&ZÉ\|?ìJ¦¾Dö®‹A6ßÕÁ¹ÇñüƮ޷@û@ªCy[±¢ÓfLJž`@: o¨¶Å²kí@ë_Jô {)WHˆØçwëeÅkP3™>ALtU]½íÄ›v°âÐÐ\pä_š v&QG*;ßø¼ÆÂÍ_×ÞOÏm»^}—MωªüŠ{ê–Jž«_®éÑÜôh×ôèyÓüU¬l E²ã¯÷¼¶_@é‹rµjªéŠ—V͈ÕÐÇF˜âž@ìØžRkÊеa:âˆ9ŒafAÇy$†5I!t±Àdy86øß&jÏã\z¶ÛÇÉ8<Ç/gJˆ]¾Æn—£üYV7 Xxà$Yóþ+$Ô€Ö=ì±KÀ8yMgt,qx@€Òba·9;4ÛÁžEFIu:‹!0T6ô b•Ø¢ÒnèKÁÊ /D’ÕÜ¥áRU5¢(ÿ gàbëÈ;„f.aˆ;J1è#l„Žãj Àí ´I*hc÷ë…M¯ÀJŽü‡QŒú«øO+¸‹ùü··áïÅ|a|`èæ›ÉÌç+÷‹ ÄJ#ÜÌÔH{¬‚ªkÀo#‹«1HPXeâ›––)·syOçÄLeÍk0½ðûšß!)zX¹fÎ Í%ÃH˜™Êãó“çEgã´Äç ±º¿2'/ï¡Qü.GÐm2Ž 4E¦v5ãšòšº4=@ÝÖqr› [»¡¡±ù¾·´_ ¢,¸ðm­™«Ö¬°a…ȼ%+ox~Íi~MÝÁLåW/•dÆ;·ƒo{o›7ÜVv (èlŸ MQ¬N0qncÖz>Îrg˜‘sæq»gTv¢-ÿJt)ƒš±ÀÐ=Lsñ§€'ºzñK»æ6xàÎ[hÛ‹¢ëŠ{/ɧ¼ü¦(olOÞ²ó!€ qm ÊnžÅÐòŒ]ùˆ7à!›¶»§e¨Ä¶dsAò£ïXu©g…ç&Ò(LüG9úˆgžæÁS3èÂM’Œt ñÏÏh]Á6õjƯe1#ÝPºaîK]>Íse‰iš1Þc9°oò0…£´9B©E;!?ÏíÕßWÃÎCä¥4ù˜â¶à‡zÇ¿@M5VG²#Hfüç bù`æòO @4 sø°++Øšñ˜û ñ‘»úf ‰ßØU¨©ãF ¼Qïl19mƤä ôÆÒV×Úà¸Õ>‰^ÊUÅè*»õÒfÝp†!ölá#ªM;Xq¨P€„ô8yž:¡Låðm‡ ¬*.†âE±ZuUÿ}ÖÀ]Ç+&cîjÛP¸*h¸ WÍqLíVqÕQf£Ó1c)bç%¬Þݬ˼Cü$T‹R¬*‰ YfI?÷ÁTç*0àW 3Bz¦c§XÛòˆæ˜UaF˜€El ùk˜©x¿“ý( &|¹¾,‹¦­ç³(œ*Y4V²- žj’¡šÄ¿P™02v*„Hþoàü•V Ê=`ßA*(ˆH䀥sA)Üä5Ô€²!¡F«FóAU[˜ë_þ¢(ˆâ×Üս䔾3{áëÂËÀÃø× endstream endobj 340 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F3 22 0 R /F4 54 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 342 0 obj << /D [341 0 R /XYZ null 730 null] >> endobj 343 0 obj << /D [341 0 R /XYZ null 712 null] >> endobj 344 0 obj << /D [341 0 R /XYZ null 700 null] >> endobj 345 0 obj << /D [341 0 R /XYZ null 676 null] >> endobj 346 0 obj << /D [341 0 R /XYZ null 664 null] >> endobj 347 0 obj << /D [341 0 R /XYZ null 652 null] >> endobj 348 0 obj << /D [341 0 R /XYZ null 628 null] >> endobj 349 0 obj << /D [341 0 R /XYZ null 616 null] >> endobj 350 0 obj << /D [341 0 R /XYZ null 604 null] >> endobj 351 0 obj << /D [341 0 R /XYZ null 579 null] >> endobj 352 0 obj << /D [341 0 R /XYZ null 557 null] >> endobj 353 0 obj << /D [341 0 R /XYZ null 539 null] >> endobj 354 0 obj << /D [341 0 R /XYZ null 497 null] >> endobj 355 0 obj << /D [341 0 R /XYZ null 479 null] >> endobj 356 0 obj << /D [341 0 R /XYZ null 461 null] >> endobj 357 0 obj << /D [341 0 R /XYZ null 431 null] >> endobj 358 0 obj << /D [341 0 R /XYZ null 413 null] >> endobj 359 0 obj << /D [341 0 R /XYZ null null null] >> endobj 360 0 obj << /D [341 0 R /XYZ null null null] >> endobj 338 0 obj << /P 289 0 R /R [63 63 549 729] /V 286 0 R /N 361 0 R >> endobj 370 0 obj << /Length 3140 /Filter /FlateDecode >> stream H‰ÌWKsÛ8¾»Êÿ—­"¦D>@úæx<‰=ѬËöäoMÑ%q#‘ŠJ6ÿ~ûEQ’ãdö¶¥*~~Ýxóx~öó¯Fõ8??˃?ó­ òÌå*Í‚ÐXõø SE†È|üáûËùÙGÏè<0ðïÃ/Ð~’Ä0¿Ò.ˆ½UU4» ¨¢™ÉÇsÓÒW·.Vª¯ºµ6¡Wk?ƒµ†vоný¯Ç[T*¥L…Î)Ç(ß ec*q>·õL]ÞO¯nþ¼ÒÆŠOÞ“øE‡ü"ägÃxäeÄï£w·Y2¯Ó~ Æ&íô²^uq*\¤˜„̆­©È˜£W Eiâ£9S‘ÿ,D^¥…ßêî­¶ÚOáb…*ÎÀ¸_é_ÉÏdÒëy¬‰¼·D²ëÊŠ™ª',æ^¡í3¯þDzо‡~›í"‘Õ¥öA{o ä^«zÆw€Ù}»_Ÿ¢ç›èø¥ö ßë4pÞôŠ¿a‘TY4ê™å!–jÉ⋬3šÔÍ" ^7½ª÷&"×„Í çb0‹ÅM&¯öp_5äÜO+s–µGYÁ€¸b1öI°­nÑ^1žr£c°A"Ù89ð=F0…Ùs5×Ès"‡h‹ é,ç‡Ø±A…ö༤ìF¤ &Ø%_€„)i–Ù‡¯Fjƒ³#<"R8³8Z+4Zø9XÃx9Q+çœd±LhE¯ °ÀDpx¡!ÉF ¦ŒG4ÊE¡‰öÅÏ_U‰&‘pX¡œº ^ó´ñÅø9MМñPÅ@üSg‚h@Cxzwóçå³6ãmןÊ{Œ©q VrGlÐnLèHtxÒªnH‡’¤Ì!Ã0–7Gë:OU]‡Qƒ Àê1?ÉÀÁ©~ä‡v¬Ã<ûxMеݓW.‹Ný´®¶ÛbQM@¨^•í¬ú¿ÆÄdæ0ÆBÙ¡Ð\µèyƒÁyþ¬ pàˆø‚hf ˜s”ïp(J¬Náu ‡h¥à•UÝSÀ ~B€Ë sB¤WCÈ:ðU¢R(¨'!´¯;CRü¾FQ ºGG¬@ûxÔÞ ÚCýÄ29{Uª(ÉûM©0°Y¤Ëçv1öB"Éj)"{‚2¥Œ˜ÖªîRpBà þä^¿Eè ¥^0p‹ë\`†Îã´x5i³œ’ö¥nÛ~&»ûb‰¥T=QL`˜m=šzF`üª<&‰H}CÀ`’¨û¾<&ƒ=晜Œ’ŒR@•É‚©Æ¯êÆl q«*©…BT±–1âz„›jM5?Ž >£æíjÕêÈÜ|¡zœP½užœ{Ö×¾rËÐÅrSC4}…ntÞ¢"ƒâ‡°œ¢ d–s&y +ÑAS9„Öe§cì';ê*ÖUÓo¹?¸ÐÐ]©Æ=vM’çƒYý1¥>z g¨^x÷È?Ö&'ŒÂõü5wáØjØ¡ÕPSB_Žþ ÑlÚÙðíÜQ--ïdÊBŸj›¹#m“Aݲ•z8û›ÚŽŒþ7mョÙcÖ5D„ÁRÑʆ7x•¶àÍ}”Ø¡öX’ gI Ôy4jfÑì¾âFeŽŒ1hcìZd±)åcûý”;„£ƒ;†ÇP J'‡uñ°³ù˜¬fß¿c °7„8}Ã5² Ô£ôHÑÁØâÕ»-8Vj¾0Ý÷ÄÞj%yöM1Ír¯®(6EÄ{ߥÇÞ;m½éõTýÁ,+ìÞ\õd­Å^"óxC½ÝÕ3¨ýW$$ ™ð•ø«û¡­EY…QÑ•Kn°›2˜ ©ðáwËgulbè|:Qü‘ç1/:>z¹¥9¿àb7"ÀX\ŒµbG ‰Ov4aŠê^ µN^ÒBÉӛтY{¸„<»ÓÆ‘õcïSÝL4•z…9áMõ¾Ø‘OræÆt§DoøÖ]=TSÈ}Lñ[ Þ Øô=Èj­·uÑ4"dŒMi•r¦n¹-A:ˆ¸Ì»cQE ‡Ô+>ÑÉ–þ?ë+Iw@'ŒO4b¸ Í%·0Jذ-º9sk»5ç+ ¡>衜Yt=+ÄÛ+Ó×:¿£îrZ”à®ïqd¦’ï(8îèMóazÁ^sШG¨¤¦„ÄðV‘ üR÷KÅ߀rv”¤ DhÙ®q¶ãĝӄt†Ç¢p@—@k´©4½8ºyKFuØ+¢3 Ð_ŠÍ‹§YÝ\žÙ–‰Òw˜»1ÖÁ¶dn×hL¨RËžFN•‡›Ë©&s\Ñc¬E æDS ‹ ]ÒjJoÿ`ôaN]0ø@"SfÈ…  qBP“bïIµ<‡)ª•ºƒ7&oËÄ%•A”®&ІN-xš®Hf~”ŒuÅt½p¯¡ÕÀš\—èeG`†ûë Û1?y}B‡ðâ /ÃĺÓ'#„{,dÉ{¬—;AßäKJ b÷ˆœˆ<œŽ4à€$‰‡àgvITÜ-þRËðàÝìh½‡¨uØnív|(˜¨›†ÉªZ3æ  ˆÏ÷Äšºª ÏÆù)Hcw)4í–J};ïÕ‘ã}tбÄÇ ‡‹•ºá³ 󇬚óBñu)ŒËeÝWe¿ë*õ°F‘ZÄd¸½€wQ3‘ýK@OHž’;±Ùüñ§×ËÞùläß„‡˜¶¿jƒï©–HºÝšŒC…Bß^¨k.ãÿ´h¶I Ù«ç`eÔÌâ®Djfñù°)ŽÖk.œ |êVR|ç<€q&ê@=jgÐÁTó˜¼a¬9¾¢ªØo·<ݱ˜+©œ&£‘”* ;±˜³× Ù›hG Z “µ(¯«@fËàI‹‰ºßSÝbÙéºzÛ2mC$ÌSGxH«3ZÅÊž…;õ[FÏ@¨Q±÷5è/ûUÑ!f 9f,Ã@ûvê™eZAŒuÐA8¶êR±8BÚð°£5™0{\ëÖàҵΨîÑvÇUQ–•°ÝBLÄìÑ{ÁkÅz] g^ù~A‹Þfxs¬áè ’9Á`. ZòGv„€âÙ’Mâox ‰7Я¡ÞhS[Šy¦¨ŒÑ¼R‘7Ö>xYÍ(ìX’ _¢pè4¹'Mp1‰oò‘ ¸.¥Ö‡b;SÊ!R‡o¸AgííI€DTu/IÃC#¼ö=4Á€ò.åN„µ£³›«ù.65T_õvhY ÙŸ ùÝl9]—²&ªÈ°N¢ÄÏû.ÿÂÜ÷as€Q%’Ù|ÅŠ’´ƒt²&‹ö³pÿǯ–¥„¡ºwÆè’. ThKÙùÀÑ g\—‡R-бuþÞääÄZŒ+¸÷ž$'¹·yȹö­™9½ÐóŒü&ƒ½‡ÉYZÀ$VeÔôûÆò376aPpçþ{Ý̈3÷Ãy³CYžº³qIëFÜgæìŸ,?Nº-á‹Ì¤Š$žå…ÙÝèF™I,Ô6¥¢­JØVi5Æðr:5È!×k€*‚J4îKì͹÷3BB@këµ ŒŽOºhnKJ.Ãx†!<6 œ§ ãu2ÒAU. Ö;¥Ä‰ÚT´mj;¤÷† \<<ÝÛ¬ã…ico’GA[Ób!ô`%½€nã™è{j¶^Â,³æ8u*À‘¸© -ò§(Zß+º±³²D×ÊjCe ÆŸàÕV=—Ï—ªqÜ=ª¼FT°°ô©&'¾ª*éöoA4W6‰¯Ã±Çž\¿!i4ûSyשeàTÚ Ô·KÞµÝzP(µoÌp­[$ñDDNÚ¶6 ó(:%ûäÚ‘ì4ÑÕ¨9é™TRô*šhÿ ¸(ŒÔBØÇg+Wm°ê‡«hݱç¿áÅóåÅ77ŒÂ? endstream endobj 371 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F2 21 0 R /F3 22 0 R /F4 54 0 R /F5 372 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 375 0 obj << /ProcSet [/PDF /ImageB ] /ExtGState << /GS1 23 0 R >> >> endobj 77 0 obj << /Type /Pattern /PatternType 1 /Resources 375 0 R /Matrix [0.96 0 0 -0.96 9.45 773.85] /PaintType 1 /TilingType 1 /BBox [0 0 1 1] /XStep 1 /YStep 1 /Length 102 /Filter /FlateDecode >> stream H‰2T0¢t^.}÷`C…ôb^.°.P4•—+ ÄA’B^.C0GD*$çòr9yµ†+XI0éà¬`¤=}JŠJFxº(00üÿ¼\®@å¼\aè› endstream endobj 376 0 obj << /ProcSet [/PDF /ImageB ] /ExtGState << /GS1 23 0 R >> >> endobj 78 0 obj << /Type /Pattern /PatternType 1 /Resources 376 0 R /Matrix [0.96 0 0 -0.96 9.45 773.85] /PaintType 1 /TilingType 1 /BBox [0 0 1 1] /XStep 1 /YStep 1 /Length 99 /Filter /FlateDecode >> stream H‰2T0¢t^.}÷`C…ôb^.°.P4•—+ ÄA’B^.C0GD*$çòr9yµ†+XI0éà¬`¤=}JŠJFxº(C/—+Py /@€Ež7 endstream endobj 377 0 obj << /ProcSet [/PDF /ImageB ] /ExtGState << /GS1 23 0 R >> >> endobj 79 0 obj << /Type /Pattern /PatternType 1 /Resources 377 0 R /Matrix [0.96 0 0 -0.96 9.45 773.85] /PaintType 1 /TilingType 1 /BBox [0 0 1 1] /XStep 1 /YStep 1 /Length 104 /Filter /FlateDecode >> stream H‰2T0¢t^.}÷`C…ôb^.°.P4•—+ ÄA’B^.C0GD*$çòr9yµ†+XI0éà¬`¤=}JŠJFxº(|¨°‘ão?ü—˨<— Àdô› endstream endobj 378 0 obj << /Type /Halftone /HalftoneType 1 /HalftoneName (Default) /Frequency 60 /Angle 45 /SpotFunction /Round >> endobj 23 0 obj << /Type /ExtGState /SA false /OP false /HT /Default >> endobj 20 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont /Times-Roman >> endobj 21 0 obj << /Type /Font /Subtype /Type1 /Name /F2 /BaseFont /Helvetica-Bold >> endobj 22 0 obj << /Type /Font /Subtype /Type1 /Name /F3 /BaseFont /Times-Italic >> endobj 54 0 obj << /Type /Font /Subtype /Type1 /Name /F4 /BaseFont /Courier >> endobj 372 0 obj << /Type /Font /Subtype /Type1 /Name /F5 /Encoding 379 0 R /BaseFont /Times-Roman >> endobj 379 0 obj << /Type /Encoding /Differences [ 0/grave/acute/circumflex/tilde/macron/breve/dotaccent/dieresis /ring/cedilla/hungarumlaut/ogonek/caron/dotlessi/fi/fl /Lslash/lslash/Zcaron/zcaron/minus 39/quotesingle 96/grave 130/quotesinglbase /florin/quotedblbase/ellipsis/dagger/daggerdbl/circumflex/perthousand/Scaron /guilsinglleft/OE 145/quoteleft/quoteright/quotedblleft/quotedblright/bullet/endash /emdash/tilde/trademark/scaron/guilsinglright/oe 159/Ydieresis 164/currency 166/brokenbar 168/dieresis/copyright/ordfeminine 172/logicalnot/hyphen/registered/macron /degree/plusminus/twosuperior/threesuperior/acute/mu 183/periodcentered/cedilla /onesuperior/ordmasculine 188/onequarter/onehalf/threequarters 192/Agrave/Aacute/Acircumflex /Atilde/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex /Edieresis/Igrave/Iacute/Icircumflex/Idieresis/Eth/Ntilde/Ograve /Oacute/Ocircumflex/Otilde/Odieresis/multiply/Oslash/Ugrave/Uacute /Ucircumflex/Udieresis/Yacute/Thorn/germandbls/agrave/aacute/acircumflex /atilde/adieresis/aring/ae/ccedilla/egrave/eacute/ecircumflex /edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve /oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute /ucircumflex/udieresis/yacute/thorn/ydieresis ] >> endobj 1 0 obj << /Type /Page /Parent 24 0 R /Resources 19 0 R /Contents 18 0 R /CropBox [0 0 612 791] /B [17 0 R] >> endobj 25 0 obj << /Type /Page /Parent 24 0 R /Resources 36 0 R /Contents 35 0 R /CropBox [0 0 612 791] /B [34 0 R] >> endobj 37 0 obj << /Type /Page /Parent 24 0 R /Resources 53 0 R /Contents 52 0 R /CropBox [0 0 612 791] /B [51 0 R] >> endobj 55 0 obj << /Type /Page /Parent 24 0 R /Resources 76 0 R /Contents 75 0 R /CropBox [0 0 612 791] /B [74 0 R] >> endobj 81 0 obj << /Type /Page /Parent 24 0 R /Resources 102 0 R /Contents 101 0 R /CropBox [0 0 612 791] /B [100 0 R] >> endobj 103 0 obj << /Type /Page /Parent 24 0 R /Resources 135 0 R /Contents 134 0 R /CropBox [0 0 612 791] /B [133 0 R] >> endobj 136 0 obj << /Type /Page /Parent 24 0 R /Resources 186 0 R /Contents 185 0 R /CropBox [0 0 612 791] /B [184 0 R] >> endobj 187 0 obj << /Type /Page /Parent 24 0 R /Resources 238 0 R /Contents 237 0 R /CropBox [0 0 612 791] /B [236 0 R] >> endobj 239 0 obj << /Type /Page /Parent 24 0 R /Resources 288 0 R /Contents 287 0 R /CropBox [0 0 612 791] /B [286 0 R] >> endobj 289 0 obj << /Type /Page /Parent 24 0 R /Resources 340 0 R /Contents 339 0 R /CropBox [0 0 612 791] /B [338 0 R] >> endobj 341 0 obj << /Type /Page /Parent 374 0 R /Resources 371 0 R /Contents 370 0 R /CropBox [0 0 612 791] /B [361 0 R] >> endobj 24 0 obj << /Type /Pages /Kids [1 0 R 25 0 R 37 0 R 55 0 R 81 0 R 103 0 R 136 0 R 187 0 R 239 0 R 289 0 R] /Count 10 /Parent 373 0 R >> endobj 374 0 obj << /Type /Pages /Kids [341 0 R] /Count 1 /Parent 373 0 R >> endobj 373 0 obj << /Type /Pages /Kids [24 0 R 374 0 R ] /Count 11 /MediaBox [0 0 612 792] >> endobj 380 0 obj << /Count 8 /First 362 0 R /Last 362 0 R >> endobj 362 0 obj << /Title (ARMCI: A Portable Aggregate Remote Memory Copy Interface) /Dest [1 0 R /XYZ null 737 null] /Parent 380 0 R /First 363 0 R /Last 369 0 R /Count 7 >> endobj 363 0 obj << /Title (Motivation and Background) /Dest [1 0 R /XYZ null 617 null] /Parent 362 0 R /Next 364 0 R >> endobj 364 0 obj << /Title (ARMCI Data Structures) /Dest [25 0 R /XYZ null 219 null] /Parent 362 0 R /Prev 363 0 R /Next 365 0 R >> endobj 365 0 obj << /Title (ARMCI Operations) /Dest [81 0 R /XYZ null 602 null] /Parent 362 0 R /Prev 364 0 R /Next 369 0 R /First 366 0 R /Last 368 0 R /Count 3 >> endobj 366 0 obj << /Title (Progress and ordering) /Dest [103 0 R /XYZ null 737 null] /Parent 365 0 R /Next 367 0 R >> endobj 367 0 obj << /Title (Memory allocation) /Dest [103 0 R /XYZ null 617 null] /Parent 365 0 R /Prev 366 0 R /Next 368 0 R >> endobj 368 0 obj << /Title (List of Operations) /Dest [103 0 R /XYZ null 406 null] /Parent 365 0 R /Prev 367 0 R >> endobj 369 0 obj << /Title (References) /Dest [341 0 R /XYZ null 587 null] /Parent 362 0 R /Prev 365 0 R >> endobj 361 0 obj << /P 341 0 R /R [63 63 549 729] /V 338 0 R /N 17 0 R >> endobj 17 0 obj << /T 16 0 R /P 1 0 R /R [63 63 549 729] /V 361 0 R /N 34 0 R >> endobj 381 0 obj [ 16 0 R ] endobj 382 0 obj << /Limits [(F) (G1000493)] /Names [(F) 15 0 R (G1000018) 174 0 R (G1000019) 175 0 R (G1000020) 176 0 R (G1000021) 177 0 R (G1000022) 178 0 R (G1000023) 179 0 R (G1000024) 180 0 R (G1000038) 217 0 R (G1000039) 218 0 R (G1000040) 219 0 R (G1000041) 220 0 R (G1000042) 221 0 R (G1000043) 222 0 R (G1000044) 223 0 R (G1000045) 224 0 R (G1000046) 225 0 R (G1000047) 226 0 R (G1000048) 232 0 R (G1000049) 233 0 R (G1000054) 227 0 R (G1000055) 228 0 R (G1000056) 229 0 R (G1000059) 231 0 R (G1000066) 230 0 R (G1000073) 234 0 R (G1000084) 182 0 R (G1000085) 188 0 R (G1000097) 167 0 R (G1000098) 168 0 R (G1000106) 152 0 R (G1000115) 128 0 R (G1000116) 129 0 R (G1000128) 248 0 R (G1000136) 249 0 R (G1000137) 250 0 R (G1000138) 251 0 R (G1000139) 252 0 R (G1000140) 253 0 R (G1000141) 254 0 R (G1000145) 262 0 R (G1000189) 255 0 R (G1000190) 256 0 R (G1000191) 257 0 R (G1000192) 258 0 R (G1000193) 259 0 R (G1000194) 261 0 R (G1000216) 260 0 R (G1000225) 181 0 R (G1000232) 166 0 R (G1000239) 344 0 R (G1000288) 323 0 R (G1000289) 324 0 R (G1000290) 325 0 R (G1000300) 322 0 R (G1000345) 346 0 R (G1000382) 347 0 R (G1000425) 348 0 R (G1000426) 349 0 R (G1000427) 350 0 R (G1000448) 331 0 R (G1000449) 332 0 R (G1000450) 333 0 R (G1000493) 291 0 R] >> endobj 383 0 obj << /Limits [(G1000504) (G1000923)] /Names [(G1000504) 281 0 R (G1000506) 290 0 R (G1000510) 282 0 R (G1000511) 283 0 R (G1000512) 284 0 R (G1000543) 279 0 R (G1000560) 268 0 R (G1000561) 269 0 R (G1000562) 270 0 R (G1000571) 271 0 R (G1000572) 272 0 R (G1000575) 275 0 R (G1000579) 276 0 R (G1000583) 277 0 R (G1000590) 278 0 R (G1000687) 70 0 R (G1000688) 112 0 R (G1000689) 139 0 R (G1000690) 137 0 R (G1000692) 156 0 R (G1000693) 171 0 R (G1000694) 172 0 R (G1000695) 191 0 R (G1000696) 242 0 R (G1000697) 265 0 R (G1000789) 215 0 R (G1000801) 204 0 R (G1000806) 203 0 R (G1000814) 193 0 R (G1000815) 194 0 R (G1000823) 195 0 R (G1000824) 196 0 R (G1000826) 197 0 R (G1000827) 198 0 R (G1000830) 199 0 R (G1000831) 200 0 R (G1000851) 214 0 R (G1000853) 205 0 R (G1000854) 206 0 R (G1000855) 207 0 R (G1000856) 208 0 R (G1000857) 209 0 R (G1000858) 210 0 R (G1000859) 211 0 R (G1000860) 212 0 R (G1000861) 213 0 R (G1000864) 345 0 R (G1000875) 201 0 R (G1000876) 202 0 R (G1000880) 240 0 R (G1000881) 241 0 R (G1000885) 130 0 R (G1000886) 131 0 R (G1000894) 153 0 R (G1000902) 154 0 R (G1000903) 155 0 R (G1000907) 169 0 R (G1000908) 170 0 R (G1000912) 189 0 R (G1000913) 190 0 R (G1000917) 263 0 R (G1000918) 264 0 R (G1000921) 329 0 R (G1000923) 327 0 R] >> endobj 384 0 obj << /Limits [(G1000924) (G997567)] /Names [(G1000924) 328 0 R (G1000936) 326 0 R (G1000943) 334 0 R (G1000944) 335 0 R (G1000945) 336 0 R (G1000961) 245 0 R (G1000981) 244 0 R (G1000990) 7 0 R (G1001002) 5 0 R (G1001005) 9 0 R (G1001022) 273 0 R (G1001023) 274 0 R (G1001046) 92 0 R (G1001056) 93 0 R (G1001064) 94 0 R (G1001067) 97 0 R (G1001075) 98 0 R (G1001098) 320 0 R (G1001104) 293 0 R (G1001107) 296 0 R (G1001108) 297 0 R (G1001109) 298 0 R (G1001110) 299 0 R (G1001111) 300 0 R (G1001126) 294 0 R (G1001127) 295 0 R (G1001134) 302 0 R (G1001135) 303 0 R (G1001136) 304 0 R (G1001139) 301 0 R (G1001145) 305 0 R (G1001146) 306 0 R (G1001147) 307 0 R (G1001153) 309 0 R (G1001154) 310 0 R (G1001155) 311 0 R (G1001162) 308 0 R (G1001180) 312 0 R (G1001181) 313 0 R (G1001193) 315 0 R (G1001194) 316 0 R (G1001195) 317 0 R (G1001196) 318 0 R (G1001197) 319 0 R (G1001205) 314 0 R (G997335) 3 0 R (G997338) 2 0 R (G997347) 4 0 R (G997358) 12 0 R (G997360) 28 0 R (G997371) 30 0 R (G997372) 31 0 R (G997378) 32 0 R (G997382) 38 0 R (G997383) 39 0 R (G997384) 40 0 R (G997385) 41 0 R (G997386) 42 0 R (G997387) 43 0 R (G997388) 47 0 R (G997390) 56 0 R (G997391) 57 0 R (G997394) 61 0 R (G997567) 59 0 R] >> endobj 385 0 obj << /Limits [(G997568) (G999785)] /Names [(G997568) 60 0 R (G997571) 62 0 R (G997581) 64 0 R (G997603) 157 0 R (G997604) 280 0 R (G997639) 115 0 R (G997658) 158 0 R (G997663) 192 0 R (G997665) 173 0 R (G997672) 140 0 R (G997679) 116 0 R (G997689) 105 0 R (G997698) 292 0 R (G997702) 321 0 R (G997703) 330 0 R (G997704) 107 0 R (G997712) 109 0 R (G997713) 110 0 R (G997714) 113 0 R (G997823) 48 0 R (G997952) 49 0 R (G997963) 8 0 R (G997984) 106 0 R (G997988) 108 0 R (G998097) 6 0 R (G998684) 67 0 R (G998823) 72 0 R (G998824) 90 0 R (G998865) 10 0 R (G998866) 11 0 R (G998964) 58 0 R (G999004) 343 0 R (G999010) 342 0 R (G999023) 65 0 R (G999024) 66 0 R (G999033) 44 0 R (G999039) 63 0 R (G999055) 45 0 R (G999056) 46 0 R (G999098) 91 0 R (G999101) 104 0 R (G999157) 247 0 R (G999215) 95 0 R (G999218) 351 0 R (G999268) 266 0 R (G999271) 246 0 R (G999281) 216 0 R (G999282) 243 0 R (G999296) 267 0 R (G999324) 26 0 R (G999328) 352 0 R (G999329) 29 0 R (G999352) 27 0 R (G999365) 355 0 R (G999412) 353 0 R (G999415) 354 0 R (G999422) 356 0 R (G999437) 357 0 R (G999450) 358 0 R (G999568) 96 0 R (G999780) 68 0 R (G999781) 69 0 R (G999782) 71 0 R (G999785) 82 0 R] >> endobj 386 0 obj << /Limits [(G999786) (P.9)] /Names [(G999786) 83 0 R (G999787) 84 0 R (G999788) 85 0 R (G999789) 86 0 R (G999793) 88 0 R (G999794) 89 0 R (G999798) 87 0 R (G999824) 111 0 R (G999827) 114 0 R (G999851) 149 0 R (G999869) 117 0 R (G999870) 125 0 R (G999871) 126 0 R (G999872) 138 0 R (G999878) 127 0 R (G999883) 118 0 R (G999891) 124 0 R (G999896) 119 0 R (G999897) 120 0 R (G999902) 121 0 R (G999908) 122 0 R (G999914) 123 0 R (G999922) 141 0 R (G999923) 142 0 R (G999924) 143 0 R (G999925) 144 0 R (G999926) 145 0 R (G999927) 146 0 R (G999928) 147 0 R (G999929) 148 0 R (G999935) 150 0 R (G999936) 151 0 R (G999957) 159 0 R (G999965) 160 0 R (G999966) 161 0 R (G999967) 162 0 R (G999977) 163 0 R (G999981) 164 0 R (G999985) 165 0 R (L) 360 0 R (P.1) 14 0 R (P.10) 337 0 R (P.11) 359 0 R (P.2) 33 0 R (P.3) 50 0 R (P.4) 73 0 R (P.5) 99 0 R (P.6) 132 0 R (P.7) 183 0 R (P.8) 235 0 R (P.9) 285 0 R] >> endobj 387 0 obj << /Kids [382 0 R 383 0 R 384 0 R 385 0 R 386 0 R] >> endobj 388 0 obj << /Dests 387 0 R >> endobj 389 0 obj << /Type /Catalog /Pages 373 0 R /Outlines 380 0 R /Threads 381 0 R /Names 388 0 R /OpenAction [1 0 R /XYZ null null null] /PageMode /UseOutlines >> endobj xref 0 390 0000000000 65535 f 0000076306 00000 n 0000000017 00000 n 0000000073 00000 n 0000000128 00000 n 0000000184 00000 n 0000000240 00000 n 0000000296 00000 n 0000000352 00000 n 0000000408 00000 n 0000000464 00000 n 0000000521 00000 n 0000000578 00000 n 0000000635 00000 n 0000000886 00000 n 0000000944 00000 n 0000001002 00000 n 0000079378 00000 n 0000001062 00000 n 0000005791 00000 n 0000074551 00000 n 0000074642 00000 n 0000074736 00000 n 0000074471 00000 n 0000077752 00000 n 0000076434 00000 n 0000005923 00000 n 0000005981 00000 n 0000006039 00000 n 0000006097 00000 n 0000006155 00000 n 0000006213 00000 n 0000006271 00000 n 0000006329 00000 n 0000012352 00000 n 0000006388 00000 n 0000011466 00000 n 0000076563 00000 n 0000011598 00000 n 0000011656 00000 n 0000011714 00000 n 0000011772 00000 n 0000011830 00000 n 0000011888 00000 n 0000011946 00000 n 0000012004 00000 n 0000012062 00000 n 0000012120 00000 n 0000012178 00000 n 0000012236 00000 n 0000012293 00000 n 0000020441 00000 n 0000012431 00000 n 0000019264 00000 n 0000074828 00000 n 0000076692 00000 n 0000019396 00000 n 0000019454 00000 n 0000019512 00000 n 0000019570 00000 n 0000019628 00000 n 0000019686 00000 n 0000019744 00000 n 0000019802 00000 n 0000019860 00000 n 0000019918 00000 n 0000019976 00000 n 0000020034 00000 n 0000020092 00000 n 0000020150 00000 n 0000020208 00000 n 0000020266 00000 n 0000020324 00000 n 0000020382 00000 n 0000031274 00000 n 0000020520 00000 n 0000029969 00000 n 0000073155 00000 n 0000073578 00000 n 0000073997 00000 n 0000030187 00000 n 0000076821 00000 n 0000030229 00000 n 0000030287 00000 n 0000030345 00000 n 0000030403 00000 n 0000030461 00000 n 0000030519 00000 n 0000030577 00000 n 0000030635 00000 n 0000030693 00000 n 0000030751 00000 n 0000030809 00000 n 0000030867 00000 n 0000030925 00000 n 0000030983 00000 n 0000031041 00000 n 0000031099 00000 n 0000031157 00000 n 0000031215 00000 n 0000037918 00000 n 0000031354 00000 n 0000036033 00000 n 0000076953 00000 n 0000036178 00000 n 0000036238 00000 n 0000036298 00000 n 0000036358 00000 n 0000036418 00000 n 0000036478 00000 n 0000036538 00000 n 0000036598 00000 n 0000036658 00000 n 0000036718 00000 n 0000036778 00000 n 0000036838 00000 n 0000036898 00000 n 0000036958 00000 n 0000037018 00000 n 0000037078 00000 n 0000037138 00000 n 0000037198 00000 n 0000037258 00000 n 0000037318 00000 n 0000037378 00000 n 0000037438 00000 n 0000037498 00000 n 0000037558 00000 n 0000037618 00000 n 0000037678 00000 n 0000037738 00000 n 0000037798 00000 n 0000037857 00000 n 0000045516 00000 n 0000037999 00000 n 0000042551 00000 n 0000077086 00000 n 0000042696 00000 n 0000042756 00000 n 0000042816 00000 n 0000042876 00000 n 0000042936 00000 n 0000042996 00000 n 0000043056 00000 n 0000043116 00000 n 0000043176 00000 n 0000043236 00000 n 0000043296 00000 n 0000043356 00000 n 0000043416 00000 n 0000043476 00000 n 0000043536 00000 n 0000043596 00000 n 0000043656 00000 n 0000043716 00000 n 0000043776 00000 n 0000043836 00000 n 0000043896 00000 n 0000043956 00000 n 0000044016 00000 n 0000044076 00000 n 0000044136 00000 n 0000044196 00000 n 0000044256 00000 n 0000044316 00000 n 0000044376 00000 n 0000044436 00000 n 0000044496 00000 n 0000044556 00000 n 0000044616 00000 n 0000044676 00000 n 0000044736 00000 n 0000044796 00000 n 0000044856 00000 n 0000044916 00000 n 0000044976 00000 n 0000045036 00000 n 0000045096 00000 n 0000045156 00000 n 0000045216 00000 n 0000045276 00000 n 0000045336 00000 n 0000045396 00000 n 0000045455 00000 n 0000052522 00000 n 0000045599 00000 n 0000049509 00000 n 0000077219 00000 n 0000049642 00000 n 0000049702 00000 n 0000049762 00000 n 0000049822 00000 n 0000049882 00000 n 0000049942 00000 n 0000050002 00000 n 0000050062 00000 n 0000050122 00000 n 0000050182 00000 n 0000050242 00000 n 0000050302 00000 n 0000050362 00000 n 0000050422 00000 n 0000050482 00000 n 0000050542 00000 n 0000050602 00000 n 0000050662 00000 n 0000050722 00000 n 0000050782 00000 n 0000050842 00000 n 0000050902 00000 n 0000050962 00000 n 0000051022 00000 n 0000051082 00000 n 0000051142 00000 n 0000051202 00000 n 0000051262 00000 n 0000051322 00000 n 0000051382 00000 n 0000051442 00000 n 0000051502 00000 n 0000051562 00000 n 0000051622 00000 n 0000051682 00000 n 0000051742 00000 n 0000051802 00000 n 0000051862 00000 n 0000051922 00000 n 0000051982 00000 n 0000052042 00000 n 0000052102 00000 n 0000052162 00000 n 0000052222 00000 n 0000052282 00000 n 0000052342 00000 n 0000052402 00000 n 0000052461 00000 n 0000058576 00000 n 0000052605 00000 n 0000055683 00000 n 0000077352 00000 n 0000055816 00000 n 0000055876 00000 n 0000055936 00000 n 0000055996 00000 n 0000056056 00000 n 0000056116 00000 n 0000056176 00000 n 0000056236 00000 n 0000056296 00000 n 0000056356 00000 n 0000056416 00000 n 0000056476 00000 n 0000056536 00000 n 0000056596 00000 n 0000056656 00000 n 0000056716 00000 n 0000056776 00000 n 0000056836 00000 n 0000056896 00000 n 0000056956 00000 n 0000057016 00000 n 0000057076 00000 n 0000057136 00000 n 0000057196 00000 n 0000057256 00000 n 0000057316 00000 n 0000057376 00000 n 0000057436 00000 n 0000057496 00000 n 0000057556 00000 n 0000057616 00000 n 0000057676 00000 n 0000057736 00000 n 0000057796 00000 n 0000057856 00000 n 0000057916 00000 n 0000057976 00000 n 0000058036 00000 n 0000058096 00000 n 0000058156 00000 n 0000058216 00000 n 0000058276 00000 n 0000058336 00000 n 0000058396 00000 n 0000058456 00000 n 0000058515 00000 n 0000065276 00000 n 0000058659 00000 n 0000062263 00000 n 0000077485 00000 n 0000062396 00000 n 0000062456 00000 n 0000062516 00000 n 0000062576 00000 n 0000062636 00000 n 0000062696 00000 n 0000062756 00000 n 0000062816 00000 n 0000062876 00000 n 0000062936 00000 n 0000062996 00000 n 0000063056 00000 n 0000063116 00000 n 0000063176 00000 n 0000063236 00000 n 0000063296 00000 n 0000063356 00000 n 0000063416 00000 n 0000063476 00000 n 0000063536 00000 n 0000063596 00000 n 0000063656 00000 n 0000063716 00000 n 0000063776 00000 n 0000063836 00000 n 0000063896 00000 n 0000063956 00000 n 0000064016 00000 n 0000064076 00000 n 0000064136 00000 n 0000064196 00000 n 0000064256 00000 n 0000064316 00000 n 0000064376 00000 n 0000064436 00000 n 0000064496 00000 n 0000064556 00000 n 0000064616 00000 n 0000064676 00000 n 0000064736 00000 n 0000064796 00000 n 0000064856 00000 n 0000064916 00000 n 0000064976 00000 n 0000065036 00000 n 0000065096 00000 n 0000065156 00000 n 0000065215 00000 n 0000069607 00000 n 0000065359 00000 n 0000068332 00000 n 0000077618 00000 n 0000068465 00000 n 0000068525 00000 n 0000068585 00000 n 0000068645 00000 n 0000068705 00000 n 0000068765 00000 n 0000068825 00000 n 0000068885 00000 n 0000068945 00000 n 0000069005 00000 n 0000069065 00000 n 0000069125 00000 n 0000069185 00000 n 0000069245 00000 n 0000069305 00000 n 0000069365 00000 n 0000069425 00000 n 0000069485 00000 n 0000069546 00000 n 0000079296 00000 n 0000078158 00000 n 0000078344 00000 n 0000078473 00000 n 0000078614 00000 n 0000078791 00000 n 0000078918 00000 n 0000079056 00000 n 0000079180 00000 n 0000069690 00000 n 0000072912 00000 n 0000074915 00000 n 0000077988 00000 n 0000077903 00000 n 0000073070 00000 n 0000073493 00000 n 0000073912 00000 n 0000074337 00000 n 0000075026 00000 n 0000078090 00000 n 0000079468 00000 n 0000079499 00000 n 0000080777 00000 n 0000082069 00000 n 0000083309 00000 n 0000084505 00000 n 0000085433 00000 n 0000085509 00000 n 0000085552 00000 n trailer << /Size 390 /Root 389 0 R /Info 13 0 R /ID [] >> startxref 85729 %%EOF ga-5.9.2/armci/examples/000077500000000000000000000000001500715745200150355ustar00rootroot00000000000000ga-5.9.2/armci/examples/README000066400000000000000000000014031500715745200157130ustar00rootroot00000000000000ARMCI EXAMPLES ~~~~~~~~~~~~~~ Introduction ============ The benchmarks directory has many ARMCI benchmarks. The features directory has examples and notes on each of the features listed below: 1. aggregation 2. gpc 3. non-blocking 4. proc_affinity 5. concurrency 6. notification 7. symmetric_memory Compiling ========= The examples directory is built by the top-level Makefile after a successful "configure" run. You can run "make checkprogs" to build the test programs and the example programs together, or "make examples" to build only the example programs. Remember, run "make" from the top level, not from the testing or examples directories! Execution ========= Any example that expects an input also has a README in its directory explaining how to use it. ga-5.9.2/armci/examples/benchmarks/000077500000000000000000000000001500715745200171525ustar00rootroot00000000000000ga-5.9.2/armci/examples/benchmarks/RandomAccess/000077500000000000000000000000001500715745200215145ustar00rootroot00000000000000ga-5.9.2/armci/examples/benchmarks/RandomAccess/ra_common.h000066400000000000000000000015601500715745200236410ustar00rootroot00000000000000/** @file * $ID:$ */ #ifndef RA_COMMON_H_ #define RA_COMMON_H_ #if SIZEOF_LONG == 8 # define POLY 0x0000000000000007UL # define PERIOD 1317624576693539401L typedef unsigned long u64Int; typedef long s64Int; # define FSTR64 "%ld" # define FSTRU64 "%lu" # define ZERO64B 0L #elif SIZEOF_LONG_LONG == 8 # define POLY 0x0000000000000007ULL # define PERIOD 1317624576693539401LL typedef unsigned long long u64Int; typedef long long s64Int; # define FSTR64 "%lld" # define FSTRU64 "%llu" # define ZERO64B 0LL #else # error long nor long long are 8 bytes in size #endif /* Macros for timing */ #define CPUSEC() (HPL_timer_cputime()) #define RTSEC() (MPI_Wtime()) #define MAX_TOTAL_PENDING_UPDATES 1024 #define LOCAL_BUFFER_SIZE MAX_TOTAL_PENDING_UPDATES #define MAX_OUTSTANDING_HANDLES 64 extern u64Int **HPCC_Table; #endif /* RA_COMMON_H_ */ ga-5.9.2/armci/examples/benchmarks/RandomAccess/simple/000077500000000000000000000000001500715745200230055ustar00rootroot00000000000000ga-5.9.2/armci/examples/benchmarks/RandomAccess/simple/simple.c000066400000000000000000000174021500715745200244460ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_STAT_H # include #endif #if HAVE_FCNTL_H # include #endif #include "armci.h" #include "message.h" #include "../ra_common.h" #define DEBUG 0 static int me, nproc; static u64Int procnumupdates,myglobalstart,globaltablelen,mytablelen,mintablesize,*procglobalstart; static int bigtables,Remainder; static int my_free_handle; FILE *fd; u64Int **HPCC_Table; struct vector_dscr_t{ int active; int dstproc; u64Int **xmitbuffer; struct vector_dscr_t *next; armci_giov_t a_v; }; struct vector_dscr_t *vec_dscr; static struct vector_dscr_t curdscr_val; struct vector_dscr_t *curdscr_ptr = &curdscr_val; static armci_hdl_t* _get_next_handle() { my_free_handle++; my_free_handle %= MAX_OUTSTANDING_HANDLES; } /* from hpcc RandomAccess/utility.c */ static u64Int HPCC_starts(s64Int n) { int i,j; u64Int temp,ran,m2[64]; while (n < 0) n += PERIOD; while (n > PERIOD) n -= PERIOD; if (n == 0) return 0x1; temp = 0x1; for (i=0; i<64; i++) { m2[i] = temp; temp = (temp << 1) ^ ((s64Int) temp < 0 ? POLY : 0); temp = (temp << 1) ^ ((s64Int) temp < 0 ? POLY : 0); } for (i=62; i>=0; i--) if ((n >> i) & 1) break; ran = 0x2; while (i > 0) { temp = 0; for (j=0; j<64; j++) if ((ran >> j) & 1) temp ^= m2[j]; ran = temp; i -= 1; if ((n >> i) & 1) ran = (ran << 1) ^ ((s64Int) ran < 0 ? POLY : 0); } return ran; } static void xmitvector(){ struct vector_dscr_t *tmp=curdscr_val.next, *tmp1; u64Int myscale=0; curdscr_ptr = &curdscr_val; while(tmp!=NULL){ if(tmp->dstproc!=me) ARMCI_NbAccV(ARMCI_ACC_RA,&myscale,&tmp->a_v,1,tmp->dstproc,NULL); tmp=tmp->next; } tmp=curdscr_val.next; while(tmp!=NULL){ if(tmp->dstproc==me) ARMCI_AccV(ARMCI_ACC_RA,&myscale,&tmp->a_v,1,tmp->dstproc); tmp=tmp->next; } tmp=curdscr_val.next; while(tmp!=NULL){ tmp->active=0; tmp->a_v.ptr_array_len=0; tmp1=tmp->next; tmp->next=NULL; tmp = tmp1; } } static void addtovector(u64Int ran){ u64Int offset; int proc; struct vector_dscr_t *tmp; offset = ran & (globaltablelen-1); if(offset < bigtables) proc=offset/(mintablesize+1); else proc=(offset-Remainder)/mintablesize; tmp =&vec_dscr[proc]; if(!tmp->active){ tmp->active = 1; tmp->next = NULL; curdscr_ptr->next = tmp; curdscr_ptr = tmp; } *(u64Int *)tmp->a_v.src_ptr_array[tmp->a_v.ptr_array_len]=ran; tmp->a_v.dst_ptr_array[tmp->a_v.ptr_array_len]=(void *)(HPCC_Table[proc]+(offset-procglobalstart[proc])); tmp->a_v.ptr_array_len+=1; } void HPCCRandom_Access() { int i; u64Int ran; ran = HPCC_starts (4 * myglobalstart); for(i=0;i simple.x inpfile\n"); fflush(stdout); } ARMCI_Finalize(); armci_msg_finalize(); return 0; } globaltablelen = atoi(argv[1]); mintablesize = globaltablelen/nproc; Remainder = globaltablelen - mintablesize*nproc; bigtables = (mintablesize+1)*Remainder; if(me #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_STAT_H # include #endif #if HAVE_FCNTL_H # include #endif #include "armci.h" #include "message.h" #include "../ra_common.h" #define DEBUG 0 static int me, nproc,nsmp; static u64Int procnumupdates,myglobalstart,globaltablelen,mytablelen,mintablesize,*procglobalstart; static int bigtables,Remainder; static int *armcismpid; static int my_free_handle; FILE *fd; u64Int **HPCC_Table; struct vector_dscr_t{ int active; int dstproc; int dstsmp; u64Int **xmitbuffer; struct vector_dscr_t *next; armci_giov_t a_v; }; struct vector_dscr_t *vec_dscr; static struct vector_dscr_t curdscr_val; struct vector_dscr_t *curdscr_ptr = &curdscr_val; static armci_hdl_t* _get_next_handle() { my_free_handle++; my_free_handle %= MAX_OUTSTANDING_HANDLES; } /* from hpcc RandomAccess/utility.c */ static u64Int HPCC_starts(s64Int n) { int i,j; u64Int temp,ran,m2[64]; while (n < 0) n += PERIOD; while (n > PERIOD) n -= PERIOD; if (n == 0) return 0x1; temp = 0x1; for (i=0; i<64; i++) { m2[i] = temp; temp = (temp << 1) ^ ((s64Int) temp < 0 ? POLY : 0); temp = (temp << 1) ^ ((s64Int) temp < 0 ? POLY : 0); } for (i=62; i>=0; i--) if ((n >> i) & 1) break; ran = 0x2; while (i > 0) { temp = 0; for (j=0; j<64; j++) if ((ran >> j) & 1) temp ^= m2[j]; ran = temp; i -= 1; if ((n >> i) & 1) ran = (ran << 1) ^ ((s64Int) ran < 0 ? POLY : 0); } return ran; } static void xmitsmpvector(){ struct vector_dscr_t *tmp=curdscr_val.next, *tmp1; int mysmpid = armcismpid[me]; u64Int myscale=0; curdscr_ptr = &curdscr_val; #if DEBUG printf("\n%d:in xmitsmpvector dstproc=%d dstsmp=%d mysmp=%d\n",me,tmp->dstproc,tmp->dstsmp,mysmpid);fflush(stdout); #endif while(tmp!=NULL){ if(tmp->dstsmp!=mysmpid) ARMCI_NbAccV(ARMCI_ACC_RA,&myscale,&tmp->a_v,1,tmp->dstproc,NULL); tmp=tmp->next; } tmp=curdscr_val.next; #if DEBUG printf("\n%d:in xmitsmpvector1 dstproc=%d\n",me,tmp->dstproc);fflush(stdout); #endif while(tmp!=NULL){ if(tmp->dstsmp==mysmpid) ARMCI_AccV(ARMCI_ACC_RA,&myscale,&tmp->a_v,1,tmp->dstproc); tmp=tmp->next; } tmp=curdscr_val.next; #if DEBUG printf("\n%d:in xmitsmpvector2 dstproc=%d\n",me,tmp->dstproc);fflush(stdout); #endif while(tmp!=NULL){ tmp->active=0; tmp->a_v.ptr_array_len=0; tmp1=tmp->next; tmp->next=NULL; tmp = tmp1; } #if DEBUG printf("\n%d:in xmitsmpvector done\n",me);fflush(stdout); #endif } static void addtosmpvector(u64Int ran){ u64Int offset; int proc,smpid; struct vector_dscr_t *tmp; offset = ran & (globaltablelen-1); if(offset < bigtables) proc=offset/(mintablesize+1); else proc=(offset-Remainder)/mintablesize; /*if(vec_dscr[]);*/ smpid = armcismpid[proc]; #if DEBUG printf("\n%d:adding ran=%d offset=%d proc=%d smpid=%d\n",me,ran,offset,proc,smpid);fflush(stdout); #endif tmp =&vec_dscr[smpid]; if(!tmp->active){ tmp->active = 1; tmp->next = NULL; curdscr_ptr->next = tmp; curdscr_ptr = tmp; } *(u64Int *)tmp->a_v.src_ptr_array[tmp->a_v.ptr_array_len]=ran; tmp->a_v.dst_ptr_array[tmp->a_v.ptr_array_len]=(void *)(HPCC_Table[proc]+(offset-procglobalstart[proc])); tmp->a_v.ptr_array_len+=1; #if DEBUG printf("\n%d:added ran=%d smpid=%d ptrarrlen=%d\n",me,ran,smpid,tmp->a_v.ptr_array_len);fflush(stdout); #endif } void HPCCRandom_Access() { int i; u64Int ran; ran = HPCC_starts (4 * myglobalstart); for(i=0;i simple.x inpfile\n"); fflush(stdout); } ARMCI_Finalize(); armci_msg_finalize(); return 0; } globaltablelen = atoi(argv[1]); mintablesize = globaltablelen/nproc; Remainder = globaltablelen - mintablesize*nproc; bigtables = (mintablesize+1)*Remainder; if(me #endif #if HAVE_SYS_ERRNO_H # include #endif #if HAVE_SYS_TIME_H # include #endif /* Timing routines that use standard Unix gettingofday() */ static struct timezone tz; static struct timeval start_time, finish_time; /* Start measuring a time delay */ void start_timer(void) { gettimeofday( &start_time, &tz); } /* Retunrn elapsed time in milliseconds */ double elapsed_time(void) { gettimeofday( &finish_time, &tz); return(1000.0*(finish_time.tv_sec - start_time.tv_sec) + (finish_time.tv_usec - start_time.tv_usec)/1000.0 ); } /* Return the stopping time in milliseconds */ double stop_time(void) { gettimeofday( &finish_time, &tz); return(1000.0*finish_time.tv_sec + finish_time.tv_usec/1000.0); } ga-5.9.2/armci/examples/benchmarks/cg/000077500000000000000000000000001500715745200175435ustar00rootroot00000000000000ga-5.9.2/armci/examples/benchmarks/cg/armci_sharedmemory/000077500000000000000000000000001500715745200234155ustar00rootroot00000000000000ga-5.9.2/armci/examples/benchmarks/cg/armci_sharedmemory/cg.c000066400000000000000000000166001500715745200241550ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** @file * $Id: cg.c,v 1.1.2.2 2007-07-05 15:29:56 vinod Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_STAT_H # include #endif #if HAVE_FCNTL_H # include #endif #if HAVE_STRING_H # include #endif #include "armci.h" #include "message.h" #define PRINT_VEC_ int na,nz; double *bvec,*dvec,*svec,*dmvec,*m_dvec,*amat,*xvec,*axvec,*rvec,*qvec; int *ridx,*cidx; int me, nproc; int myfirstrow=0,mylastrow=0; double epsilon=1e-4; double time_get=0; static int niter; void read_and_create(int,char **); void computeminverser(double *,double *, double *); void computeminverse(double *,double *, int *,int *); void finalize_arrays(int); extern void acg_matvecmul(double *,double *,double *,int *,int *); extern void acg_addvec(double *,double *,double *,double *, double *); extern void acg_2addvec(double *,double *, double *,double *, double *, double *, double *,double *, double *, double *, int *, int *); extern double acg_ddot(double *,double *); void conjugate_gradient(int nit,int dopreconditioning) { int i; double d_one=1.0,d_negone=-1.0; double delta0=0.0,deltaold=0.0,deltanew=0.0; double alpha=0.0,negalpha,beta,dtransposeq; acg_matvecmul(amat,xvec,axvec,ridx,cidx); /* compute Ax */ #ifdef PRINT_VEC acg_printvec("axvec",axvec); acg_printvec("bvec",bvec); #endif /* TODO original call had too many arguments */ /* acg_addvec(&d_one,bvec,&d_negone,axvec,rvec,ridx,cidx); */ /* r=b-Ax */ acg_addvec(&d_one,bvec,&d_negone,axvec,rvec); /* r=b-Ax */ #ifdef PRINT_VEC acg_printvec("rvec",rvec); if(me==0)for(i=0;i(epsilon*epsilon*delta0);i++){ acg_matvecmul(amat,dvec,qvec,ridx,cidx); /* q = ad */ dtransposeq=acg_ddot(dvec,qvec); /* compute d_transpose.q */ alpha = deltanew/dtransposeq; /* deltanew/(d_transpose.q) */ #if 0 if(i>0 && i%50==0){ /* compute Ax*/ acg_matvecmul(amat,xvec,axvec,ridx,cidx); /* x = x+ alpha.d*/ /* r=b-Ax*/ acg_2addvec(&d_one,xvec,&alpha,dvec,xvec,&d_one,bvec, &d_negone,axvec,rvec,ridx,cidx); } else #endif { negalpha = 0.0-alpha; /* x = x+ alpha.d*/ /* r=r-alpha.q*/ acg_2addvec(&d_one,xvec,&alpha,dvec,xvec,&d_one,rvec, &negalpha,qvec,rvec,ridx,cidx); } if(dopreconditioning) computeminverser(dmvec,rvec,svec); deltaold = deltanew; /* deltaold = deltanew*/ if(dopreconditioning) deltanew = acg_ddot(svec,rvec); /* deltanew = r_transpose.r*/ else deltanew = acg_ddot(rvec,rvec); /* deltanew = r_transpose.r*/ beta = deltanew/deltaold; /* beta = deltanew/deltaold*/ if(dopreconditioning) { /* too many aguments in function call */ /* acg_addvec(&d_one,svec,&beta,dvec,dvec,ridx,cidx); */ /* d = s + beta.d */ acg_addvec(&d_one,svec,&beta,dvec,dvec); } else { /* too many aguments in function call */ /* acg_addvec(&d_one,rvec,&beta,dvec,dvec,ridx,cidx); */ /* d = r + beta.d */ acg_addvec(&d_one,rvec,&beta,dvec,dvec); } #ifdef PRINT_VEC acg_printvec("xvec",xvec); #endif /* acg_printvec("xvec",xvec); */ } if(me==0)printf("\n\tIteration:%d\tBeta:%0.4f\tDelta:%f\n", i,beta,deltanew); niter = i; } void initialize_arrays(int dpc) { int i; for(i=0;i cg.x na nz file"); printf("\n\n where:"); printf("\n\tna is array dimention (only square arrays supported)"); printf("\n\tnz is number of non-zeros"); printf("\n\tfile is either the input file or the word random"); printf("\n\t use the word random if you to use random input"); printf("\n\t input should be in row compressed format"); printf("\n\t file should have matrix a followed by row, col & b (Ax=b)"); printf("\n\t if file also has na and nz, pass them as 0's and the"); printf("\n\t program will read them from the file"); printf("\n\nexample usages are:"); printf("\n\tmpirun -np 4 ./ga_cg.x 5000 80000 /home/me/myinput.dat"); printf("\n\tor"); printf("\n\tmpirun -np 4 ./ga_cg.x 5000 80000 random\n\n"); fflush(stdout); } ARMCI_Finalize(); armci_msg_finalize(); return 0; } read_and_create(argc,argv); if(me==0)printf("\nWarmup and initialization run"); #if 0 initialize_arrays(dopreconditioning); conjugate_gradient(1,dopreconditioning); time_get =0.0; #endif if(me==0)printf("\n\nStarting Conjugate Gradient ...."); initialize_arrays(dopreconditioning); time0=armci_timer(); conjugate_gradient(30000/*2*/,dopreconditioning); time1=armci_timer(); acg_matvecmul(amat,xvec,axvec,ridx,cidx); if(me==0)printf("\n%d:in %d iterations time to solution=%f-%f ax and b in cg_output.out\n",me,niter,(time1-time0),time_get); acg_matvecmul(amat,xvec,axvec,ridx,cidx); if(me==0){ int i; fd = fopen("cg_output.out", "w"); for(i=0;i<=na;i++) fprintf(fd,"\n%d:%s[%d]=%f %s[%d]=%f",me,"bvec",i,bvec[i],"axvec",i,axvec[i]); fflush(stdout); fclose(fd); } finalize_arrays(dopreconditioning); armci_msg_barrier(); if(me==0)printf("Terminating ..\n"); ARMCI_Finalize(); armci_msg_finalize(); return 0; } ga-5.9.2/armci/examples/benchmarks/cg/armci_sharedmemory/compute.c000066400000000000000000000057521500715745200252460ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #include "armci.h" #include "message.h" extern int na,nz; extern int me, nproc; extern int myfirstrow,mylastrow; void computeminverse(double *minvptr,double *aptr,int *rowptr,int *colptr) { int i,j; for(i=myfirstrow;i<=mylastrow;i++){ for(j=rowptr[i];j=i){ if(colptr[j]==i){ /*printf("\n%d:i=%d j=%d aptr=%f",me,i,j,aptr[j]);*/ minvptr[i]=10.0/aptr[j]; } if(colptr[j]>i){ minvptr[i]=0.0; /*printf("\n%d:l=%d i=%d mycolptr[j]=%d",me,j,i,colptr[j]);*/ } break; } } } /*armci_msg_barrier();*/ } void computeminverser(double *minvptr,double *rvecptr,double *minvrptr) { int i; for(i=myfirstrow;i<=mylastrow;i++) minvrptr[i]=minvptr[i]*rvecptr[i]; /*armci_msg_barrier();*/ } void acg_printvec2(char *v, double *vec, char *v1, double *vec1) { int i; for(i=myfirstrow;i<=mylastrow;i++) printf("\n%d:%s[%d]=%f %s[%d]=%f",me,v,i,vec[i],v1,i,vec1[i]); fflush(stdout); armci_msg_barrier(); } void acg_printvec(char *v, double *vec) { int i; for(i=myfirstrow;i<=mylastrow;i++) printf("\n%d:%s[%d]=%f",me,v,i,vec[i]); fflush(stdout); armci_msg_barrier(); } double acg_ddot(double *vec1,double *vec2) { int i; double dt=0.0; for(i=myfirstrow;i<=mylastrow;i++) dt+=(vec1[i]*vec2[i]); armci_msg_dgop(&dt,1,"+"); /*armci_msg_barrier();*/ return(dt); } void acg_zero(double *vec1) { int i; for(i=myfirstrow;i<=mylastrow;i++) vec1[i]=0.0; armci_msg_barrier(); } void acg_addvec(double *pscale1,double *vec1,double *pscale2,double *vec2, double *result) { int i; double scale1=*pscale1,scale2=*pscale2; for(i=myfirstrow;i<=mylastrow;i++) result[i]=(scale1*vec1[i]+scale2*vec2[i]); /*armci_msg_barrier();*/ } void acg_2addvec(double *pscale1a,double *vec1a, double *pscale2a,double *vec2a, double *resulta, double *pscale1b, double *vec1b,double *pscale2b, double *vec2b, double *resultb, int *rowptr, int *colptr) { int i; double scale1a=*pscale1a,scale2a=*pscale2a, scale1b=*pscale1b,scale2b=*pscale2b; for(i=myfirstrow;i<=mylastrow;i++){ resulta[i]=vec1a[i]*scale1a+vec2a[i]*scale2a; resultb[i]=vec1b[i]*scale1b+vec2b[i]*scale2b; } /*armci_msg_barrier();*/ } void acg_matvecmul(double *aptr,double *vec, double *result,int *rowptr, int *colptr) { int i,j; double tmprowsum=0.0; ARMCI_Barrier(); for(i=myfirstrow;i<=mylastrow;i++){ for(j=rowptr[i];j #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_STAT_H # include #endif #if HAVE_FCNTL_H # include #endif #if HAVE_STDIO_H # include #endif void printsparse_(int *irow, int *icol, double *a, double *b, int n,int nz) { int fd; int i,max,j; fd = open("matrix.bin",O_CREAT|O_WRONLY,0777); if(fd<=0) { printf("open failed"); return;} write (fd, &n, sizeof(int)); write (fd, &nz, sizeof(int)); write (fd, a, sizeof(double)*(nz)); write (fd, irow, sizeof(int)*(n)); write (fd, icol, sizeof(int)*(nz)); write (fd, b, sizeof(double)*(n)); close(fd); printf("dumped sparse matrix: dim = %d %d nonzeros\n", n, nz); } int main(int argc, char **argv) { int NZROW=8,NZSQ=64; int i,k,irow[NZROW+1],icol[NZSQ+4],n=NZROW,nz=NZSQ; double a[NZSQ], b[NZROW], xvec[NZROW]; for(i=0;i×9@Á»)Ïg?ÛâÀÉh¦²bmÁ¨žªÂÀƒð»Ù¹ò@á^#йX@=݃ã$@ã"’iZ@ÿÒ‡Äge@3·ÖKö6@WçD>´¦@úȃpÀ<þ¥W"ÁÜò¸À›TïÀºÏ?‰âΕÁ’ß 1þ@éáÖÍßôÀNâLÚÄÀÀ–RŒñÇ@uZ×µˆ®AVÈ€Z‡#á@Zˆæ‡Ág¥Hü²—À]×|8WCðÀ±ØÉåŃÃ@%m‚…&Ä)@K›­ÂÄõ?î¶Š-a†+@‹4±•N6@•äŒeæí@,X£ò{áÀVo¿SÐ A²%Œ²€™ÛÀ é'äÖXú@~m¬ø4ì@æZ/[^• Áe}sÄÁÄÀ i‰þذÁJýü8¯çÀ¿Ðž”A¶?Λ”ÁÂ@¬4\\ÑòÀ1¼\x€°@¹IŸª(¹ÀÖDœ²¼e@ª¦Dú|ÀÃbá_M@ÏÏ~Uü)e@+ÁCï™Ly@ç©çà"S@ß}†jN@ÇkwÜz¶<@%êMif€|ÀÓRº³¼Ã€@ñ³•ë³Á‰À´œ@¤‡‘@þ]dZéæˆÀÇcp¡@”!@°¿MjÀ¦n«Ó|ä]@>Ë@Ž»3@ÀOÜD“ÁIÀnì«PŽÀŒ&‡q‚@úQ‘ž‰À@Š‘—ž§@ˆÜ‚xœœÀË’CN wp@¹IŸª(¹À·ÆÌ!âÛ@MÚ¥¿zp|ÀLq•‘Ö`’@ÐÏ~Uü)eÀÃbá_M@ç©çà"SÀ+ÁCï™Ly@´Nƒ0`Î<@ÞbŽ#i1+@ˆqÆ*,Þ‰@ f©ÓÖ¢À4ÔÒ* H—@BÆEýµ°À$o<Ð<³ÀEôA Ôq@óÔ» ˆÀ|'úv¹IÀ-L/;Ê`@ aÇfC;@…©SrPبÀ ªæÆ„•¨@v­¢5#ÇÀpºŽÍ™°~@Áµ·Õž®À_M™<µæ·AH#q¼­*A­3¬+ãÁij ®Ÿ¼òÀ&¤·h͇ùÀk–¸Sò5àÀÀüÑùvì·Á’!ü ÁÔuGŒíApÌžßõ@ªþå-#AZX;œ˜Ý@¡ª´¼AªæbÅå@O#0±É3ôÀÅèÐÞâ/ËÀ4‹gòëæƒ@e›+Iôsi@H#q¼­*AY·#ʈקAÛ5 ø&{ÀR5ÔÂ4 nÀwÚo?¥@wÀc‹évÀ¸œ|Ã>+ä@Ý{?lCò§Áƪ6…( ÁýO-WFÉ AZX;œ˜ÝÀªþå-#AªæbÅåÀ¡ª´¼A€WhO\À˜ðœûÂcÀýö=rTi@.„2P@Ÿ±•¥@Ý9Ž€À’ÀM;Ðöx@BóÖe•ÀÈ"É犚@ÿØ ÚÇ*@¶ úÛÍ9@äm Š»SÀ\„âeÀºbÓ{1h@±{„nü˜\@þl!ÐmÆ?@XœrÀY7ADÿ=\@™(H¯ëQ]@6h+î‚ü®¿½wˆè‚tÀ<%nk@¶„rîîÀާôÔ·@g@¡•É€Fª£ÀPk#dŽP^@74p‘À\ÝJ~•,À¨fQÕ7Î`@3GÍ)óƒU@ØÙ*c»S~À+he¨¢ðš@®ßw¬(L·ÀÜ5É”@@uAZFn"@¦§‚¤ìh'AAuÆ¡û†å@$íøµ{8Á¾)Iò8Á¼õ’m2¾@ÆÇ$â‰@ö¬ö}Çæ@¤’[ý­d¹@Ó³$A÷X¬@±õöVx@—kÁêöÀIAÆIeå®ÀR…HC,Ø@-À€Lƒµ@uÂ+’Ô@0À 9ù¹@¬ü!ù¯ðÂ@Bþ4»D©@¬ü!ù¯ðÂ@Bþ4»D©@eTàŒ«_Þ@GÎbÚ¡žº@¯¿þXö8Á@Xm ¼W†@”¹ÅŒsCÑ@ ê e r®@ö)EDZû@q 7ˆuá@»åž´g#A—XîŽB AAuÆ¡û†å@©õE­õ`A©2uR&˜Á²ÿ팕KÁ~WEvpµ@@ÆìfIæ‡@¯Écƒ±À~#ªRÚ°ÀäæéÕV­¦@”iµ¬V?„@«\VM„Ù¢@-L”rˆ@›|-G–¬@Cà>‚Å ‰@#'ÉÏ{? @“'ì1“§|@Bë3^¾É@ã+žn°@’2;æLCò@ÚËXYódÛ@÷USç¿×§Aã@ÐçpÝÁë¼7R"ä…@¼™îù†°«@"U'%ú^wÀu§zˆž|À h–æÅe@Hð—'p5ì@-]:=ÞnÒ@Ÿ"‡ûÿ«ð@@´I›^Ì@äáÓÛñ!¤@¿Ro1ø@·SPØAÎÀ0ìûž¿‹@ÈäfÍ¥£À°ñàÖry@\§9ì´@Ó|'RU½X@ø0lϨÁþ›Áê¾Ro1øÀãáÓÛñ!¤@ƒPTÜO‡ç@Þbª†÷øÁ@•è˜M»ê@HÊЈ‡ÁÐ|'RU½XÀ\§9ì´@¡GX #˜Áòy‚œ•Î@ÄíydhÁY:sLQ@=í—¬É@Hã!§³>ì@s[×>ÆÀP³J­×‚@Zle¹TY@4‘4ÆQïÀyÇ‹én_Ã@Ç ¤ÍC´Àoåñ¬målÀ‰‹Ûü[^‡@½X²_äl@&NÜMeÄÀ¬“.Øz£@ÆyØo_¨Î@çÛ°]À¸AÐ{³8µ@S™ñÜ%TÀÃäŠÔU‘À›]»3]gÀs[×>ÆÀ›ˆ¢qŠò@K+†åxY@b½e311@ª3 ÿg¿@LÒ_’€òÀ†_XŽlÀ"Ÿ,Ü—±ÀÒÒƒŒl@ƒ<9 û¥Q@öeòªÊ7¢@o5ŠBpÐÀèÛ°]@ÆyØo_¨Î@S™ñÜ%T@·AÐ{³8µ@ôLòüIogÀŠþ"ø²•?À`;ëˆÍ#˜Aº.2ˆ³AI@Þ-@z'Ñ™CÀPW0•„ý—Á锢)Ä^÷Àì¹ñRéÜÀÁï§ +@òi"™æÂEÀÁJ?ö‚y Áb'KÜw2ñÀ—$£¥ýÀª;T£Ò´ÔÀ_ÄÇ1Ö÷ÀÒšä@ÓÀ?TÎ-· 0@ü…Ï” ÚUÀEüL«\õÀÎ ’û‘ÓÀ»»4é\ÇP@YV¤nMÚkÀº.2ˆ³A8ÉH©á±A®ö¡­ê¯øÀp€÷•÷A¬œ©áŸÝÆÀ²j:K“Éñ@têÆ@ÍÀ»–mÔž¸õ@RþµÌDÀ!á¬Fðk@õK¸…Ê-ÇÀ×4WAíf]H?«·@\âz ô±Á”~ >TÀñ/kf3ˆwÀ†Ñ,¦žÇEÀš¸©¡†a@Øê-¦ºÈÀº\CuÝóï@€NëcvÀaTϺ햄Àoù³EYÀ‰"ÉŒƒ"À‡ýæÔoÐLÀ;ø¦`ÀË–‹¿SÀL^˜/.èz@&.Z†ËÖËÀ‡Ì T<æ@³ûš;vg@95¦”“ÀÜ=Z·å rÀ›¸g‡°ö@(Q)r6¨Aßž ¬!ÁhëóWîâ–@fÍ™³ÌP}@ã¶°X?3¨ÁFةޘAQ÷rˆNÀóDXAF£@ò³t/h]@&+fûîfB@²àà±À=àéP~õv@ãßp‘AkÿæÈð@¦§ó§ŒpÀr ;÷û˜@ê)?¥Àäy?ó®˜@÷2öËÀ;!!YC»@諽ë#Ñ A_4e´)õ@ßž ¬!Áiº›ñÇ‘¨A(ÝÛÕÖa}@¦5W‹Ñb@âàVRªœ@Á͸lS¨Áæ¦ö7ï@¡”Ë;‚Á qpzB@…ÇŠ^ '@Žv®P‰Ù@×^Z 3ôÀ SÅnû„@èou2s‡\@¯iÄÇ›á@Êñ<º'– ÁÌDh[aø@•Ñ|M‰?Á_¦cÆ}FAmÍÅÜ|Ô/Á²¢‰ñ@‚6DwB×b@¯ž¢Ç¼¦A˜®r\âÀ70´9·Zz@?E. !J@þÿļ¦Áôp4ÁÑâ@ÑשutTOÀ&nÆ’&eB@ý ®;^@Ù¬™-¦¹G@ÖÚ“µ¾r@í°¹Pl. @ò‹ôë…ÀÄS»Æ,@‰× Á©1‘@h å­Y/8@"J–¥s™À×s#BÀ˜®r\âÀötA‘õ °AßË?¬*J@ŠZãkñ@ä°…Ì%аÁµú=¥‚wÁ@iV2¥lAñÀôNàæ­K¶@ #ú0‘}ìÀ€¸ÁGI=@Çj¸ËÖ?O ’¨Ä@<–UX.úÀç\—Qa@ÙRƒçB@ÆC±ÄQÆ@mI=ºàöÀ[¡Ò®)Â@ú^ÙаW‚À8¡À¶m/˜À$>;ìñ@nyø%&¸Àå¥Рâu@ðE›ƒFõÀÛ‰}?MÀ®k¦ce'ÀBÝ£FØ´fÀðX1·N@f‡Âv%@Àú^ÙаW‚À”çÍä Å@2=¦ó>8@ùeo½¡ÀQ1DÓÍÿ@Å«·»À5Ì£ÇÔÀYJ‚¨ˆZ€ÀG¨C¹Ê4@WùÕ8ÏÀe‡Âv%@@ðX1·N@®ö¡­ê¯øÀÕâTëEÐ@y^B¢(~@"c‚+À@ùèá¼Ñ•À†k*­'@†˜ÂèkRÀÅBï—F=±@T±ŒŸÍáZ@QHsüΪ@Å–C%/foÀ¦ÏñJJ.@UÄèÅ}VÀÐáR±,(@“ÈÞPa €À+à5¹h©ÀI)IËÊø@AØÚ3'{À“[ëã\Î¿Žž#¸#A„À^ÎŒ ^UÀFƒyp›)@(vÊñŠPÀÉx“2«àÀò»G]ÚR•@0‚¢Kóµ@Uâ–\¶gÀݧ´zÓ,@£V’&GÀp€÷•÷Ay^B¢(~@½s²¬ ~Ì@ùèá¼Ñ•@"c‚+À@œ>í¶aüFÀüMg€áq@M±ŒŸÍáZÀÅBï—F=±@Ä–C%/fo@PHsüΪ@Lu°9 hMÀ¿Îq‰ÖÕu@¢!k0øLGÀýMlã +Ÿ@¢ðÐ×aÀl©‰EÁÀ×´9ŽàÀSâ–\¶g@0‚¢Kóµ@¤ í=üKÀQg‹é¾@o–ªTÂ’@Äb–F3£@Tt^%„@ù—¼X>ÃÀdÙ3ëk”Àˆ”t(@ÏÙ©ã×?À½ñŠÙuÀ³i­R]`@o–ªTÂ’@2V~ñ؆³@‚t^%„ÀÃb–F3£@˜ÿ¤`ˆ2”À`’<ЩâºÀ‘«Ó¡Í?À *ô¡U@ oys_`@>ÁWw4„Àt7õ¢›•@J¼(qjjÀŒ¢F  @g×â–œÄ$ÀŽÆØ_ÿZaÀ10¥4Pgc@Œ`á­f•Àâ²Þ =@<ú h­™^@Ä¿½tÙ¾B@J¼(qjjÀDèúp×'Ÿ@^£æc$©$Àئ ¾@@V~ñýê8c@_í¼R‚À4]FÄR=@S¿‚ ±–À r3_¸dC@lìR²Â'@Ói*ò!Ï¥Ax®n¨ ý@$xH ÁŽXJÝâÀ© ݬmíø@…ò§ŠlÃÀêŸt#}@x†¶Þ @­x7Y’~@ßV(q„ @+Ùš¦!]@ïBåÔkÙ@Œ„TÈįv@¡à;¡ÊÐ@o97í!¬&ÁgzæÌb­óÀ¾ÓæÞ¬¥ÁCr/—ÁÙU¬ÿÌÀ¢ûï}@¦ËF$@h==M4ÁÙrû:vÊÀ¯†Èaf@õç-vLá?x®n¨ ý@ÂQ\ù}J®AØtNÑ|«ÃÀŠ~õúùöÀ…ò§ŠlÃ@© ݬmíø@h†¶Þ ÀêŸt#}@æV(q„ À­x7Y’~@òBåÔkÙÀ*Ùš¦!]@§à;¡ÊÐÀŒ„TÈįv@Õ”¹1Â~Á2Kܹ¶øAªeG¨IsÀ*¸ÓÙ¯·Àï÷­’žþÛÀa8Ðp¶u Aù CšÁyô÷É”E"Aü)5À5$AFFOu%è°ÁªYOíAMÇÀ[ <‘2zå@nκ³þ¶ßÀï³ÎÓ4äü@lg5äòìÀÎ 1Q‰ AF—úìçÐóÀM4èËèæ!AÓÄ“jòòÀþGÇ’î!A„¶sG+?_À¤¼é¬ÚmÀÐo9°3H@Ô¿\ëyð?c 9pEqÀP ç´ôR8Àç-vLá¿®†Èaf@ÉY¯Öô-©@ù R•ñØ\@CÕlëŸt@|? e'”@@Óû}zï’@Hš¯vû?ÛÞDVg˜@C|K5Ze@ !+L_Àûçó¦Ù¦É¿ófÓÆgR@€Tr"xiÀÕñLàI{ ÀÐ=›‚…U@zï0 k–Àqk´yv=cÀw·_S5¥À3ijú‡ÛnÀù R•ñØ\@ŽÓÖR¡@(&ó¦4A@Ò:JáDª @÷Gš¯vû¿Óû}zï’@ÜHxØL\d@Á?i=ÃÐ1@ ©¼h@;Ð^ÀTr"xi@ófÓÆgR@W¼Â;åôR@³ÏÛ ѦÀqk´yv=cÀjë–”7ƒ0À°³ÜJÿmÀF›ë)ÕW{Àë¼7R"ä…@™B O/hR@ƒ§G§¬¨AnŸÒÿèì§Áº8ó ´~„@)¹ë7ÜñV@ÃXæœV Á)¹ë7ÜñVÀ!„âôÝ‘Á‹Œc<¹ªrÀæë)h V @Ê÷6ÑZ!l@Ð.µ(÷±AÚå’íó±ÁmÛ¶m[ÌA´i¦F<°@ùÌ}[ˆÇà@B!„€ß@iÖ9t<¥@—•mµËÀ›@Ê™ý N¢@DQEQ`A˃ šž“@~ýôׯ@€À# [´@¡éö³×@Ià¬æU›@£K?Üe@R!÷fð?1kv5°úgÀgBs#mð?%jûÌ@$„B:@'2Ï€]À$„B:ÀB˜ëx%Q@¼™îù†°«@"U'%ú^w@S÷æƒú»Ae`¾v©Ê@B ¨F©çêÀÒ>Yó…‚©ÀáqU5{Ý@гJ—ÉvÀ#îùÎÈ#±@køÆ|æš}@+ü¬¤4ýÀðLŸÉÀ¢ÀÎ~ˆŸûðÀ˜‘êFQžÀ4þn¡Á@4þn¡‘@‹—›øÜÀä+Ž‹~éÀÕ\¿=Ñ@»s!6c’ÀoódŸææ Á1G‹2Û¸¨À"U'%ú^wÀ¼™îù†°«@e`¾v©Ê@ßÉ9þØbA$x‹˜‹§ÀaifÊ SfÀгJ—Év@áqU5{Ý@køÆ|æš}À#îùÎÈ#±@T¿Èg„™Àù¿n3póÀˆå$º@°ÀÙÍ3¢/ÜÀ4þn¡‘À4þn¡Á@tüÄRš ¦À׋³·3(Ù@»s!6c’@Õ\¿=Ñ@­[{¯Ò´Ào}%ö÷ÁB ¨F©çêÀ$x‹˜‹§ÀøêšgƒØ—Aª–2+EŽ@þÿÿÿƒ×—Áõq»Ì@µ ËΔ‹@MØS:Ɇ¶@6¶‹Ž·@ó¢pù< u@f¸¼DìÇ@[oëâI„‰@Ò>Yó…‚©ÀaifÊ SfÀª–2+EŽ@ëoÍr„×§A¢cv‹§@C«|€ZÇ—@mÛ¶m[ÌAÕ‹Oòì§Á—Sðr îÓ@©ªªªª Ú@¨EÈ_³›ð@) þ¤Üù¨@ÃÙ©ñ¢@,zVרµ@æ¾ø!½@¨tdçþ@KFÁÅ“·@]tÑE¾ë@ DpìÒ8Ð@ͱF" n´@æ?.®´@¨tdçî@™Óà΋²@…ļ!©ø@™X'£=‹@}Aˆ±&J@'Þ‘{Ä[u@Þ*¨ÙUv@,æ‰,4@ŠFÑz®†@ù¬Í“1H@Ë~pžQ^ä@´¡ƒ|É+@~œ}!"€@WÁ›&³3F@foFé1.z@õ³¸QAá:@¾%svÂ6§ÀØVË0LSS@%ïÀš£L†@*¨ò}¥%8@BüË ûµÀã(Ò8ñP@©7û±<ƒ@Xðèb_RÀÜ"\9˜°À–&3 'hÀLNÇHÐÀíåƒ$h@!—‚Ÿe8@^1iyÀIN’Î…?žÀR]‰É2^@kÛ”ØCC@¦In—¦wÀéÔ|PK¹ÀaI" :,iÀ uO'Š˜½@À°¨ç?[ÀûΕ’®ž@†ÓçÌ;m@¢váœÉÀÛa¨o0@5´ÆÞzN¥À$xH ÁØtNÑ|«ÃÀCÕlëŸt@(&ó¦4A@oýF^[–A ÚèÍ@¥Æ‡©KP@ªÃú$ªocÀ6û(§‹¬“@ÏÛC2ú6a@3ðÈ8Áü•Á ÙªeAaY±Ö¥@<;_«±tmÀ•OMÿ@áÁ}ðŒK®äÀ¸‘º=jÓÁ‡’Vj?ÏåÀC¢–5I]ùÀ 6õžqÐÀn`5=V±¢@ɨ{ná$À1‘%°!'@tJðÆ\À|ìBvìßÁ²N§À³æàÀYLBJ݉@ÎbÔù ¡V@-ÂŽâKáø@_yƒd@°ãàÅœ0@Ž^ºÖWb@ºx .@8™\ M‚Á&³îÈÎÀŽXJÝâÀŠ~õúùöÀ|? e'”@@Ò:JáDª @ ÚèÍ@U‰—mþ£Adzj3ÀÀâ`"—@hðL¢_@;R®+@€ÙNmîµ@&£ø¥Á<;_«±tm@aY±Ö¥@_ µÂÞâ@ÓâÌÕ˜Á¸åo*¾ËÀþ#íö@´ Ÿ\ñþòÀµ™èœ®AvÝ@Êmä@ _`-šÁÍ„†hº÷½À Ä$îdØ@j8±IÎÀPêDÍöö@IiuÕÀéx)ëVvÿ@? ØÿÌ@IxoöööÀ2ƒa§bÉÀã%×/ñò@ͨ{ná$@n`5=V±¢@_R Œˆ[À¾¦–c’‘@sí¿”Þ@J0Wî¢ÁP br'T@&Ê„¢!@A«JcÃ@µà¥«Š[/@¯ž§K®âù?A»´½/–,@®ô A5h÷?URöëóÇÀ‡ðPù|þâ@‡ñPî:Ë@" øi‚Á_ïàa¡ÀÀ󶬵7é@·?Õ×§Aɧ]é²ûÀþÿÿÿƒ×§Á5GQñ+û@ ¦Ègf @ÿÊñ|êtÀÝ3g„ùÑÀJý"¢‘@«¬ð=2·@óL^¦q‘ö?ɧ]é²ûÀ!Ì_m÷±A­U—ojì±ÁÿÊñ|êt@ ¦Ègf @]&(ržû@-¯Ž ¯F&Á!L^¦q‘ö¿«¬ð=2·@õ }-Þ·A)³¬iåQ@»Íð6ÃKÆ@$ˆ #ŒÛ·Á ¯X;æií@;»tBW Ö@ÍQÌÂ@ìÊ‘‰O¥@•ª)²—~@—)âƒGkk@¹J`DßmÁ!¼Ä­=ãÀ°ÍN£Ž¥Y@춦“G@ÜÄŒ!.¯òÀÌN<Àµ¬ÔÀ)³¬iåQ@¥¹²Òä§A ¯X;æiíÀÌZ©)Þ§Át®¿PÂá`@‰$”8ãBN@lÛA7úâ@þÿ8ÜWÁï­B"N<@ ª*É‘~)@M‰µ|¡¤Ô@Ö ?AЫòÀ´yd€/ó@lÿJ‡à“@7C¥}[H‡@Á‚Ï`ÉE@,@#¢ã¿ÚÀã=ÝnáÀ¨† —zïÕÀø–˜%¤é'ÀnzíŒÜv@o3ýæÝÿ9—¤«~s…@1Ë&TC@ ¹6Ì¢À;=ðì.P,À©èg ú”@¬Z½ö?mû¶n´Œ@³×ç"%'XÀMì_oEµÀ, W¢5Y@˶v‰å±À0 ô+ø¿½Ÿ¤qR½…@Ù.óV_ W@±{ÿÖ0Æ„@ôò¼¡»ôÃÀ]@_˜wÀÎg„àzk@Xž°9)7ÀûLŒîĵÀHmÜ[– ÀeˆeÄìŠ@f›‘Ù‰W@-\ý}(@cY_@¼Â?ôM¾ÀìYGEñWaÀlÿJ‡à“@e?b«ð@±é¹ÓIF@ü¬]Nó]t@Ù1®r¦À¤.Àý± ÑÀ¼Àžb@0j ߘÚÕÀÛn3ýæÝÃ?ozíŒÜv@!}«jR@êá´—@š¾Ð]…Y3À;ÿ~Ó À1¬Z½ö¿©èg ú”@³×ç"%'X@mû¶n´Œ@þÖ ÊYÀ“ M/µÀŒÚ2’¡É¿r¯üGÑ ±ÀI’$Ž„bR@XÜ5¶~#@µ*鯅‘Q@&ú{iyÀs‡Ù8Tr¹ÀXž°9)7@Îg„àzk@ìQƒHd_NÀóMÏØÐ³Àža7ÎYV@ìªjúç#@ùÝ¿ø¨X@ç诂*@fˆ/ZIùgÀ”7 œwÓ·Àp¶È“»×—AtŽ.Þ“}@þÿÿÿƒ×—Áb­¹ó˜ ƒ@0ÈQ ³ÀÉ s½Àrq£E^"†@xðGJ œW@tŽ.Þ“}@¤K®ÃSûO@¡Ä¥=Ô[T@$­)œNG„À”dLVÑáRÀ°Voé'W@ö©£;!)@»>×9@ÁVÈ€Z‡#á@ÎVµ×—AüÑTÂdÀìÅ>¶&s@ØÛ$ñ! @¥`¬Ò›Ú—Á@L® &Çä@ëk·z»¿@9RŽ\þ¢@»j\Jj2@ äþU“ÞA£Eo¶™óâÀlÓÈwñ&v@§&‚?þ³@»)Ïg?ÛâÀZˆæ‡ÁüÑTÂdÀ±U§¨×§AØÛ$ñ! ÀìÅ>¶&s@þÿÿÿƒ×§Á¾j\Jj2À9RŽ\þ¢@£Eo¶™óâ@ äþU“ÞA«&‚?þ³ÀkÓÈwñ&v@«¿¢;‹×§A±ºÞ:Àh r+Ç-(ÀLÑ5¸Å@þÿÿÿƒ×§ÁØ;êY¤§ÅÀy®)Èc"^@›H½ª† ß?~»)èÐdÀ’ÀDNå5À…"Ê$ÿB#@òª0xÔ¥:À ð~ Û^Àᡪ#·i_@tÝ5®{Àõ¼ð6P²ÏG@çòÕR6ôTÀ` &SKwÀB.îû⿱ºÞ:À.Ó\’×—Aé,±q³"@@É»›ýÜÀþÿÿÿƒ×—Á©7$šçÜ@»H½ª† ß¿y®)Èc"^@_pÏÝ5Ày`n eWÀñ‹bSµ9ÀL‡Ìø~ÈQ@VwOÜß_@-ä½…2qÀohƒS¶¢¿ #NþzÀfÂÎ=gUÀ¿òµî–Õb@€ªíÎÜ?‰]]ôHFwÀ$íøµ{8Á©2uR&˜Á!¢ ° ³XAòBý)AÁ­ð´Ò¶@Gs ø:@!ß’ÕÉ› Á´!`îòÖÀºÍð6ÃKæ@Lš4iÒ¸Á ¯€¾ž@ÖþÞZx@j|ìiÆ1Ú@6^uNÉcÀº\×Qy@œOàß 7ÀžS½0´@&q¬˜ÒŒÀ©±¥O™›â@êBvjšñõÀBЮpÉ-ÓÀ­7`@ųh %Û?PX8?`“c@'õgÏa‡ÀõU½2§@;.lÄÛNgÀáð‘öÀ'káÚ™§ÖÀ”þÕÀ{zt)—e@Ë©¢JˆÀŸ»ýƒïÀ¼¨ ÒÖÉÀl¼Õ¦& Á |Ÿ›ËÁ+9å ¾MÁ1FXÁ¾)Iò8Á²ÿ팕KÁòBý)AÚ7 šr´ÐÀèVhwv3ÀÀA¹*¾ú×ÁÑx±Æ5öÀC Bx÷òÀéNz aAHÁ´¡ƒ|É+@")Ž Dá@ÚêWÿ bÀÍ…S:P`@ú×2P®–À´žðu@<"‡µ–¿ôµ­ ÿœ@V•žN„,@<ϧ\Á@WäÀÙVÝ À°Æˆ#‚@™2ÆåAkSÀë#û²À?I’ëyO@ßEuž=‘@£Gma]ÀҽߖJÄÀˆ\xu˜ƒÀѾóÝ¥S@=ÞúƒÀ‹É›SŸ Àtãÿ¸´NÀO·oÉ1cÅÀôš‹Ö&²@ ôoòGš…À[ÆÑÇâQ@‘æQ–éˆÀaô´Ú°ÛÈÀa§”Š¡ÍjÀ~œ}!"€@ÚêWÿ bÀÚåa¡¸Öá@6£É’Àc·Ij@ÕÉ@ê"‡µ–?´žðu@N•žN„,Àôµ­ ÿœ@VäÀÙVÝ @<ϧ\Á@™2ÆåAkS@°Æˆ#‚@YýõY:C@7-šµð7´À£Gma]@ßEuž=‘@?@I"¹eŒÀíüí»U¬À×~¥»ü†Àšw(ûR¶@õcŽ>ñ3@zÔ£LÍžÀ‚v Qyë¬@ke;€qêÀXÇi2G¹@0ÅZ&Ïí„À§’G¬©&½@ÀÊ%-qÀŸ!ºÎÛ_ÄÀ”ûÌš–@Gƒ {X<@=Ñ—´®²’À[÷¦;U&‘ÀñIÇp³[@-äÓK‹…À“Ý$ö>vÀ—…Á!>_‡@Gƒ {X<@8£=ÕЖ@[÷¦;U&‘Àüœn¸šuÀc‹+ ÛyÀÛÓ°&¤@´à¤+ƒ}@Ô1ûl$¥§À¬ªªªª ê@`ÕoËZ³?´RÂJÇ›­@Ð 1–¦î•@¹S´p„Û@Š΀Pr¿@-¯Ýâ<,öÀfŸµ‡qrÃÀmÞ§¸d°@NàÒ! ¯”@l¥7¦õm@`ÕoËZ³?w aHº ê@>°F–‰E‘@G¦Žm–y@+†¾Á À@{â"KX¢@X7–ƒÐZÅÀ ã¡ù uëÀ¢ÔØÃ, “@[ËüŒ!x@~ KöyUb@P³J­×‚@K+†åxY@8C™¥%«@LÝ® A°‚Àúôi“ÀiÉ“ðñA|@#µÔ‘Ê@¦Àö‚£ãÅU@¸ò"4*@:Žé× DÀZle¹TY@b½e311@LÝ® A°‚ÀÊ¿˜’µ@8È|¾³Û€@ÌzD")¦À'?0›€ÃÛ¿ (¦Àï&s )EÀ 9âø0`@1ËqÇחAp­)8T:æÀþÿÿÿƒ×—Áßè"X:æ@‹¡ %)‚À¤!@lR—-ÀoÓLYEu@Ÿ!@lR—-@p­)8T:æÀðöà§Aáxè ܧÁc›zÁF-æ@É 0®;$ÁÂ$^íZ@-CÊ6~(@²E9"(Aí‡8´^²óÀ¤Ô(cxz@¬äƦý•Àç›9+ÄÀbI¿ 5„¦À °r¼Á¬X"i¶ºô@t÷¼Zt@ííØÐÈÑ?í‡8´^²óÀ·"–3¾"A‹oÏí–À_B\g$í±@yqçÄ`¥ÀÀå«K À»ÿâi·ô@#Jì=Ô"ÁwííØÐÈÑ¿t÷¼Zt@(sxL£á±AŸŠ :PBÀ®È¢–·˜@B!„€ß@¨EÈ_³›ð@ üZÓ?±@Û¹v`NÜ@ºŠ<ºæ±Á÷Ö”f±@ÊLï·w¤@>#[©vmµ@P–“pÍ»@jVpøBšÔ@Ö†æ–@ái«€É@ýÞqoÎÎ@ÃyP«Ä“@e €³“@c! YÈ:õ@߸®¡É@˜tj,Ø@a¶V~´òTÀaÚîÎN@7YA{I@ÅÔ(]™¢7ÀŸŠ :PBÀK˜qn…×§Aþÿÿÿƒ×§ÁHqÅþ'N@ ±… 0pÀRÍs‰¯7ÀÌésaS@‡ˆì“×@{dqßE9¼Àñ-ðkÀÚ:ÀÈ;„@w°$oEµB@¸‹þ^ÀKÂ}¬nÅÀï<©'•@Û%˜}ÅÆÀ?sM wŒ@Q$QyUª0ÀŽzmyc“@^qîýH’ÀÊÄ7¢w,”@%%Íþí‚À°À ?‹z@âmÐé-ôQ@A(Næ}{ÀäÈòäǪ™À>X±ÿ1.–@íëd ÑŸ@oÀ†sw@{dqßE9¼ÀY5Tßó@Œyhi ø’@9³Òc[v«Àl7Û¢ËZÀ»J 7Ô†u@& …¶×›@&^ÒïÔÀ”Ö“Þƒ –@Uæ 3aÓÀeüPó#`@MÚ³ ®ÂÀ¦#«×In@`ÏÀ‰ßrfúÓ÷ÀW,Ò¦á±A±é«¨¡XÀku¤e:¨@ÃÙ©ñ¢@ÊLï·w¤@‰ií?Nâ±Á…·X3±@úNF‰@³@k œ*Kn‘@¤¥HÄ@åyžçyÍ@NФÌÉÀ‚µþ¼X@,ƒÐíÌc@"‡±(ÎÝ¿±é«¨¡XÀ›—È•“‘@˜ 8ƒÚƒX@;½óI¤”Àú†±(ÎÝ?,ƒÐíÌc@ÛŸx»5ª@?´h¥T?@**¿öT@û%ºb·µB@jæŸf¨À°iÙþÓߥ¿]5‹ÕG³@—•mµËÀ›@,zVרµ@>#[©vmµ@…·X3±@UmMã—Á2ëhóô@aÙqcQ£”@ËOz~ÀL @lÛ¶mÛæ›@ÅqÇq ë@F®:™Ø@Ö†æ–@!à_[¾O˜@ÁFîrÖ¡@qžÞÇ9úKÀLpB¹°‚@Ç]9¼AóPÀopnBºZ¯À¢'P@É®æ´`v@OÌìÔóþ¿ aïú–L@›Nöùh|@qžÞÇ9úK@ÂFîrÖ¡@Ç]9¼AóP@LpB¹°‚@\ˆ ´ÕÛfÀšp5踫À9ÌìÔóþ?žÉ®æ´`v@¥Æ‡©KP@dzj3À0aœ~àõ×@‡¹©êw‚ÀŠm¤« t@ÇÀYJßJ@…ƒâY‡¥@½–Dmû¿M@EU×¾øè›@%ŽŸo§½@§¬Ï‡âÃÀhB{[]€@Y‚èÏ!TL@uØî÷¯tÀ$ÞÀÇŠ@íLš¸,ùa@ß7u "µµÀµl§Š-€@R†MZ/öÅÀ%â£Õ^uŠÀ„¹0ðëDÀ™”ö?HBdÀ…ÖÅð?]TÿÝ$Àu²˜Ãv¼u@ø.Þ,öLc@ªÃú$ªocÀÀâ`"—@‡¹©êw‚ÀZ2==-Þ@¿YìÑAA@pŽ…Ý @ÄáC·ÛFÀ.TÀj!~@Üað.©h@”ºk?˜PK@h‹6­ÿŽ@ãZÆ:?ÍÀqA¼Xw†xÀó^¿»îè¡@¿©ŸèÄ W@ËË®;'ï.@DòÓD܃hÀ9-ª/¯zÃÀl¢_V»tÀÓ™‹zšðÀÀ;Âú±^Â{@<¿áä¡ÀMŒL´ûSÀЂ3ŒoÛˆ@°£ëŠ´B@„`ûŸû›0@Óû}zï’@÷Gš¯vû¿{{þž×§Ah0÷pìgÀþÿÿÿƒ×§Á86™DsZ@€ôÁÖθ$ÀcÉUÒš”²Àµ‰×åN³f@òŠQ›»9Ø¿…ëÎÓ3 -@Hš¯vû?Óû}zï’@h0÷pìgÀøþBNÝ×—Aþÿÿÿƒ×—ÁôÁÖθ$@86™DsZ@Úíîp½c@žkó~ê ¹À…ëÎÓ3 -@iƒ}l‚À8» ­„×—As˜÷ zÀþÿÿÿƒ×—Áw¯Œ]•IÀ8\ç5ž~@[Î&1 @¢ÀŒÕ†fRÀs˜÷ zÀ©·%G£×§ADQEQ`A¨tdçþ@ƃٹ_•¢@…ļ!©È@jVpøBšÔ@§yZ’?–@aÙqcQ£”@SV.ýOâš@gI "ä§Á–WÈ1`º@´¯má¬Î­@;»tBW Ö@â?HÕ—@[lp±ÁÕé@‹*ù¶~@e ¯’a²À«Ö饤RÀ'ªgŽÀ†@"Uœô :„@oÇQL@ÕrBIlÀv=*7¹@•PÂW3@ è ŒÄ@ŠÑÿ+Ž€À÷4’6WÁAÀŠB5‚A0*@¢d®:O@÷4’6WÁA@ÕrBI|@znÓ‘O@²Yñù¼c@!D7Ð&Û‡ÀAóÙ3¾3@þ½«E½>@V7@æ;@{5ú°Ê4@²Yñù¼cÀcQ ðì¢n@‘Zˆö“À@Ÿ ;/{À¾/ƒib§À:Æó”8@efuƒæB@¸ïýY”^À>CZº½“'@¦åy'žRÀ259°ƒ´ÀnÔ`"'T@Ÿ ;/{À‹‘˜üàÄ@$¦+ŠÄ„@Ç(I¸À—ðkÖÎçkÀVð’Ô’†@àr3ÁƒÖQÀÂZ ñ+|@(á ý@P@?9H#^9¶ÀhëóWîâ–@(ÝÛÕÖa}@øUÀç¼.AÔ[c @W¥…Âé@ws5©’B!À`¨¸É@§Õ‘÷Û0ÀpIoÊŠU-À1$›Þ×hF@g|ƸŸhbÀš–“ÅÅ¥)@òYˆtDF@F 9(+@Ù•¦No7½À4:ôiM™¢ÀVçê†ã`À à<ꂈ4ã4<À¬j¦¯ûV@BXÙª'À2•ªT,bqÀ€„.ÁfÍ™³ÌP}@¦5W‹Ñb@Ô[c @˜Õª´Š>Av,p~h=!À4ƒ¡»ÐJ<@€„.A†ƒ‡ÅD3ÀêYZJ@‰ŠêÑH@ÿÚ;W¤ZbÀqÁÓ’Ÿ)@“±W0z'gÀ>‹ÿk.+@?ø%y“@¢v;죗¢ÀCñãÖ«‡À[yš2›ìÀ¿­ÓDn Ö`ÀëÄ­Ž¥ùV@·„ïs¸rÀwƒ$Ç*_qÀבW„ÇãcÀÿÿÿÿXãFÁWÁ›&³3F@ —z‘2A·}DÈÀ*›ñþ“T1ÁVYxéÚÚ@«ƒGA@RˆÆ» RdÀè0€·ŒlßÀD§½XŠ ÄÀÌÙId.ÏÀ òæD~µÀÞѧ÷oÌ@ú Ä{?ž@ èÇ&d­@ÉÔsZ‡ƒ@-Þ,XO´éÀ&TŒiœÀ¿Š+Ô:lj@'ùÑzq@Û=pÔ“f@=œ d–tÀfoFé1.z@·}DÈÀptìÕybAAµ8ÖÏ@Z;pOAÁ{•D½Kê¦ÀÊÏ'ίòÊ@0ôÒÝìÁ@VW¬ÞÀ\%Í7y:¶@V‚¼Í0ÞÏÀú Ä{?žÀÞѧ÷oÌ@ÉÔsZ‡ƒÀ èÇ&d­@&TŒiœ@Øá¬MéÀguQ9r@~ËÅ¥ï¶X@vm™çðÍÀÓ÷EܯÚ@puOåôï@œ»ÁØÖ´À]É>ìŸêÀCNbÅÀ@DÒ™U¹ØÀ‹¬ß4Ö±À‡‹dbƒ¿@#^yÅDh¡@ùƒQF9·¸@J}ª$©ÉƒÀœ»ÁØÖ´À©HùÅâ±Aœ,B=†Ùœ@˃ šž“@KFÁÅ“·@E6xnRÒ@²~ CV¬@Ö†æ–@¯ËÀ„sä±ÁwÄqcÜ@uŒâ\ÇHÑ@˜tj,ø@üß\~o”@ ¹¸cã @ÎCuÂI¡@õf¾OG»@?t 8eñÀ9¼†‚?±Àäú¡+àÃÀr$(Á&M¡@]`(ƒ@K}ª$©Éƒ@ùƒQF9·¸@ü®…ßÙ§A]‘a׺Áþÿÿÿƒ×§ÁeIv›×A_4tî9ù›Àqoj.F~’@m.|‹.ÞîÀKë§;Ê@]‘a׺ÁªD€¬<²AX媾»ü”@æ?.®´@ð|zÜ\Ç@_"*IÅ5§@e €³“@˜tj,ø@A_ÐôµÖ@¯ùiZÈ@ÜR¢3 ²Á/'Üòy–‘@¼ Õ6樛@Ù¿¨–°Oœ@i¦n-væ@8(H³>†ÁmÇ7QÕAIy].^DÁ%Ê›õpD¨AxCÝ|ÑÀ¶N »TT$@¡#¾š™eÀê“$d6@õ´‡g(vÀækjÔ{vÀÜo ñ“t ÁÝ1•jàÀ½éþ >Vý@ö®oŠOÏÈ@+†T!@D2´ ìÕaÀÃ.®™0¨Áq•§!œÚÀYZZZšA´´´´´Šå@M­µyí¢zÀê¨i”!Áœ¥°]ËÒàÀÁ ž¦A©£¬ô*oä@VCÿÛ¦è$Á°ôH›ùúãÀfE³D¾Aß5Aëåâ@xCÝ|ÑÀk½Œ1ײA¶N »TTdÀ¡#¾š™¥@Ú{Þ±ìuÀ0áÔ®:²µ@ýÞR5µ¶@ö•I…\Ûà@jª}rÒó Áö®oŠOÏÈÀ½éþ >Vý@+†TaÀD2´ ìÕ¡@q•§!œÚ@‡a)q ²Á´´´´´ŠåÀYZZZšAM­µyí¢º@ ‘óà§‚á@ÍÕƒ+X"Á©£¬ô*oäÀÁ ž¦Awžò­øã@UlàJæ$Áß5AëåâÀfE³D¾AÔƒRÔÁ×§AùmǶ<Áþÿÿÿƒ×§Á*ÃȰ³CAKª‘fñ]øÀÍùØAŒ•½ÀÖ<›@Pƒ„ÀGw®*Gr@ÙŠìb P@E¾t0Z@§ÿ8R”ö@ˆÓÂ®Ž»@ùmǶ<Áq¼ê õ±Aég·ûDë±ÁÏÌÐnA“xõ‹!Á²ÐSä@‘bÑ*+Á#E¾t0ZÀÙŠìb P@…ÓÂ®Ž»À¦ÿ8R”ö@/Rý°ß·@0½–ÏÙm9´_ÀIô±íM°@W¶=1RÀÎ3ZªaçpÀ¢K‹R h”ÀŧUkþŠ@ç=±”˼7@,3 ykÀ~1i«„²À\vô“‡#пIÊaɼ=@`†3öì#qÀ0½)¦ñ@GÞ¸@+vÀ¥V d@›cÄÑ`æ€ÀqÂM."MÀ>ñr;E@á߀©  @¯/ðú2}@3ôòN½À¿þzÙˆfÀZW¸™@p+=ÍMÔh‚²À*qç;lÀ‰f…E @áqU5{Ý@гJ—Év@¹5 %tð@ÃóúÀYÒÀ`)ÛXÀÇ­mÐKÊÀB¢¾q.%´@¥ÖŽ…¬Í@$8,]£G¼À [ Ì)@¯¡ѽŒjÀù?ót8ŽîÀ ùà´VDÔ@£™Œ¸ë@öZÈÀÖ;Íè>ÕÒ@84õ¡oÀa‘Ç@g7ûÀlFÑKŸ%Ë@гJ—ÉvÀáqU5{Ý@ÃóúÀYÒÀŠU=JÙ¼ AÃU†bKÒÀ@[r/ácFž@žƒþ¿ÖéÀ%8,]£G¼@¦ÖŽ…¬Í@ìéÔ%ÊhÀ^iÛf>ƒ©@ FšÄ¸ÂÀ؃z¤‘óÀöZÈ@¤™Œ¸ë@84õ¡o@Ö;Íè>ÕÒ@)]ÎT3ï¸@õ~8¸ÅÊ Á"c‚+À@ùèá¼Ñ•@Ú8ÁªŒ@óBüLÀ®3ý¾zŸò@¡€ýod·”@sÉYœ½I@_}VÇ-’ À2+ªÎ0~c@ff§*$À×;¬2jy@§QoF<:ÀÃ_4Ïì"9@¡{ãÕ€òù¿Ú8Áª\@óBüÀ9Ýœ yu@ šWצBÀËd±Üpž0@ º*O®'ñ¿éñâ›ìæW@D+‡O¬À55w¥¼:d@qGbýÊá$ÀÎó²¤6g@´E²Áõ'À5ømý b@³ï¾³¶"À”c³(KÎ @`¹êÆYá¿›{uݲR@ˆ³GMÀ˜‡Ò 2@3ÆÅ§:ó¿~áÖÞy@oíÕè9ÀRsô¾@£Vü@ðÝ¿½p†í@£|áciÜÝ¿©Nœ ò½À3ÌiëQ§k@œ!kDưg@X·òit(ÀÏt´W(õ¦@Ä™u"|@:Äuy¢@Â/y›©‚_@ƒ³JbÖÀr> –½ŸÀB‘l&s­@r´î7#(v@:(Ð6j;ôÀ°.¹‹ø,†À–‹{fòÏ@‘ÒATÐAƒÀùèá¼Ñ•À"c‚+À@óBüL@Ú8ÁªŒ@¡€ýod·”@³[[*Ù§A_}VÇ-’ @sÉYœ½I@ff§*$@2+ªÎ0~c@§QoF<:@×;¬2jy@¡{ãÕ€òù?Ã_4Ïì"9@óBü@Ú8Áª\@ šWצB@9Ýœ yu@ º*O®'ñ?Ëd±Üpž0@D+‡O¬@éñâ›ìæW@qGbýÊá$@55w¥¼:d@´E²Áõ'@Îó²¤6g@³ï¾³¶"@5ømý b@`¹êÆYá?”c³(KÎ @ˆ³GM@›{uݲR@3ÆÅ§:ó?˜‡Ò 2@oíÕè9@~áÖÞy@£Vü@ðÝ?Rsô¾@£|áciÜÝ?½p†í@ÝÏelÀNó“–½ÀX·òit(@·›ó„‚×§ÁÄ™u"|ÀÏt´W(õ¦@Ã/y›©‚_À:Äuy¢@‚OÖÁ Ÿ@uB“Å:ÖÀr´î7#(vÀB‘l&s­@êišLXžÀ¹%‰íÀ’ÒATÐAƒ@–‹{fòÏ@¶N »TT$@¶N »TTdÀPÍ9åË×—AÚVLí¸òíÀþÿÿÿƒ×—ÁéÙN`œí@‡u$·âo´@¼€·Ã"¿\@ƒ_& #ÃÀ¦Ž¦g/@FO6d(@)¼°Oú\ÀøK=#f£uÀúîºx†’IÀ£3K Tn’@¡#¾š™eÀ¡#¾š™¥@ÚVLí¸òíÀôµMœŽ¨Açž*Ìó§Á`à9açu@¥ï=®MÏ@V BT(õÞ@Lï‹ÞzÁ;´yš”ê]ÀŸëÇ«ôÉž@· q…œý¶@9ÊÉáÜ@ü0õ¬FÁÛÞDVg˜@ÜHxØL\d@6û(§‹¬“@hðL¢_@Ä4„D×äã@ŠVžî¡@G F!>nÀ³œèþ¼$€@Wèj“Œ-ç?;ëS*¯CXÀ{­?òzé@§¶ß©,¦¶@¾r[1cóÀQZ¶9î¾À†™S7œÁ—À‡Jýa«´bÀ +îÌÀ#‚} kjÀC|K5Ze@Á?i=ÃÐ1@ÏÛC2ú6a@;R®+@ŠVžî¡@Ê1þn–Õ@[g,GïÀ‘Ï. tVÀàK±‘Ç@£+„jµ@ýÐÊ%*ƒ@ŽzѽÀÞÿlôÑqÃÀ‡Jýa«´bÀÒ†56u-À2?Õ·ŽÀÞœ²Oô·ÀáúÄ\{ü§Aë8}åe@Hª ö@á@~ýôׯ@]tÑE¾ë@wÄqc Aë%]H¨@JÉp3¯¦@ái«€É@ËOz~ÀL @ÿ÷rÞ–g¦@–WÈ1`º@wÄqcÜ@ n¹Dø§Á}ß6›Ð§á@Ž»G7¼¹@) þ¤ÜùØ@A_ÐôµÖ@?Ë⟼Æ@ßàžœ@’Õ­K@‘®¢ ¸@E.b%zÉ@Æþs<*P@%ÑOñÄ@J°¤ÒQÀU±±y+•¹@®•ís¨xÀƒ#zVå’#ÁM†”œútáÀë8}åe@9áú¤ó±A}ß6›Ð§áÀj)ê±ÁÅþs<*PÀE.b%zÉ@J°¤ÒQ@œ%ÑOñÄ@®•ís¨x@U±±y+•¹@ wÚ™øtá@°³r@ã’#ÁFE]”2 ˜AVbÊÂ`É@ŽS¿ÉÌñ—Á;·ñÊeÁVbÊÂ`ÉÀVbÊÂ`É@Oo€’×§A|h¬éØ@k œ*Kn‘@lÛ¶mÛæ›@TÇ* W›@VbÊÂ`ÉÀ_ï¥&Ù§ÁÒ–ý¯Ðý¦@‘ &ð;Þ@ŽIéAȦ¦@€ê¼É='žÞÀÁÿ1ðX„®@hihp.¸Ë?Áÿ1ðX„®À‘fhp.¸Ë¿hihp.¸Ë?(ýZ;~®@hihp.¸Ë¿+—ú9|®ÀQ„EȳڧA«4å:Áþÿÿÿƒ×§Á˜†ífyàAnXzèÔX@ã]I‹;@NÇÊòt’Àf€ˆ¡Óºh@´C†¶8†@¡3áùwh@É›"gùÀ3[è¤NÃ@«4å:ÁˆhúVL@¸A‰ öâ ¸ÁЬЄô@—.¬ºc­¥@ЬЄô@ º,Á«4å:AÙÉ­!ñúIÁê“$d6@Ú{Þ±ìuÀuÄ‹e·ß@ЍY·tqxÀ Zñ†À»?;]ʈÕÀ õoÖwu@CæÇ2ú7ƒÀnB?¸ÂÀ[VÖrEl®@…¬S âJ@›ža]Àøb²ÃFR@øb²ÃF’À—ðyŸµL»@Ážän“ÓqÀÝA8!}ÑÃÀˆgµlq@+ÛcØ(î @êE”i¨Àõ´‡g(vÀ0áÔ®:²µ@ЍY·tqxÀs EÜΈã@;qÖÆÛÈÅ@¿â hÄ;uÀ[tTÍÝÚÀ Ï{ƹžÄ@s ~©@õÅ/ƒj—òÀoÆ9}Ö׌ÀÁ³a=ÓÐ@»Š¢ì›“À»Š¢ì›Ó@Ážän“Óq@—ðyŸµL»@L½ BäöqÀ(}µŒÃÀF”i¨@+ÛcØ(î @ñ.LjÛ@§íÆÄ åŽ@l ÄÎ-<@±wŠ´æÉH@Ä¡üY¡Õg@õ¼î)UÜÀQ¸BD¶ªC@™ô&rÃKA@ñ.Lzû@§íÆÄ åŽ@l ÄÎ-<@±wŠ´æÉH@õ‹"¹OFäÀÄ¡üY¡Õg@8õÌ@r8ñÀQ¸BD¶ªC@™ô&rÃKA@Éh¦²bmÁg¥Hü²—ÀìÅ>¶&s@ØÛ$ñ! ÀÈU]:ö˜A_Œ†ÄBê@ŸŸ‘Äú—ÁPlåì²ÿÀ(ѯ½¿ÊÀc%‘Ѧë)@þÇXXÀR­`qÆ‚@|¢…pð¹l@È7¹~½r@?ÖÚÜ™\@ų“k_,@·q—5WîCÀˆ W´ ÁÜ(3!ÑxÀoû))ÖUÀ;úü8y ÁÃÈDÝÀ“Ø­c§}@B·2ðœê!Àß¶‹ÊãÌñ@@ plÕŒÂÀø—ñBa@pn}!ŽmJ@Am~w¢LQ@fmÀkՉѿ¨žªÂÀ]×|8WCðÀØÛ$ñ! @ìÅ>¶&s@_Œ†ÄBê@¸‘š¤š×—AÊ R+%êÀþÿÿÿƒ×—ÁÇÂE#^PQÀw0õ¨á«eÀ1'æÆ)XÀ¯j«¨x†@ÍPkÌã¼CÀ·Œ«Tk”r@·Xÿf@pRÀ‡–;·gÀî…R¶*[„@÷Âë#ìÚT@ÁÛ›¹N¯§hÁoT¤ÄÏÎ-AÎ]ÎE´a>A¸°7 0¤@¾&7.A½Ï+ÃwâbA¾&7.Á³N>¸6™@àJ«Uå 3À—Õ ³(Â@ÓÉö®  @=«ÆbW³ã@=] ûÅ@à~ ­ü|µÀmwu—`¤ŸÀýºzeØâ-ÁQo"¥hÁ¸°7 0¤@KÖ­à8>A½¾&7.A¾&7.AÏ+ÃwâbAµéÝ´ê3À 8=›RmI@ÓÉö®  À—Õ ³(Â@=] ûÅÀ<«ÆbW³ã@OZ Ãù€À“ŸÓÑ'rrÀt'n%c’jÁ]·8a±ü-A¾&7.A½óZ¾:÷ûFA¦£8Þ¼Äè?Ï+ÃwâbA¾&7.Á"Ìt«ÒŒ^@=Ì`ul¯?—Õ ³(Â@ÓÉö®  @=«ÆbW³ã@=] ûÅ@H'íà¢ÕÀ¸ÐÐÝÕ´À }R·ü-Áækïn_’jÁ½¾&7.A¦£8Þ¼Äè?ž|ñðûFA¾&7.AÏ+ÃwâbA\ÿË`ul¯¿"Ìt«ÒŒ^@ÓÉö®  À—Õ ³(Â@=] ûÅÀ<«ÆbW³ã@aØ•¤¾Ô´@ÉÀ.¯¢ÕÀ*›ñþ“T1Áµ8ÖÏ@w'îÒáF0ACeÞnÞÀ©=ßuÀ^9xC J{@†w-›'Û@‰\êzÂ@—Æ…E>*ˆ@HGúR¤lî?¨7=Ýs@2_¸Nf@¥4ö öÌ@@òˆ:4þ´@€ÈøÄÜ@ì¯ Ÿ©±uÀ;ß×$¨ÀÍ„Ž‚þãeÀVYxéÚÚ@Z;pOAÁCeÞnÞÀ(NšNà@A’¨ªö"¨@„ƒë7ÎÀ‰\êzÂÀ†w-›'Û@]GúR¤lî¿—Æ…E>*ˆ@® ÎÇ=If@?-Ïâ%Y@Aòˆ:4þ´À¥4ö öÌ@ì¯ Ÿ©±u@€ÈøÄÜ@ˆÊÄ­ 6Í@]_*úI&ÝÀÆ7²Zäç@<`l>£1·À\öÊrfÞÚÀ8æS"Ÿ@9}‘Rþ®@TKð£1·ÀÙÛ>ê›ñ@Z+º¶“£@ù¨››†”áÀüSKðÜ÷11~À–ë—½ÀÙ=asyT@{™—)a€@ÖŽˆÜ7V@,£Y'YÀ—¢¬…‡úSÀn·±"j@5Cg\PÀü^yÉB@i ³ˆ­K@D‡'šÎbÀ,£Y'YÀêÄñL„s@N—–‡j@÷wd@À-˜–Ç•B@ÖwebÀR§2[™bÀâ7íÒã‹w@wi¹4H³Á¬ñÏ€Èv@ÁHª ö@á@ÈzéêøH˜@Ï+ÃwâbA¾&7.AÏ+ÃwâbA¾&7.A™1j‡Üã±A³óå[ˆ@Iùì{Íi§À·›ƒÝÀ\.Ü´ñ?“7þކjð?{~µÊ9”@rlaˆn—ç?ò½³6ð?S™Ëd@Û··´œÀr*q§ÅMñÀänöÝ*@ÐS0ˆ²;¨Àü/žß”À„Š „E†@¶ßõÏ¥µä?×ÐŒ+â2å?W?jU h@®l*°1ö@'ú=&%Ý@!Sx^øAå±³A¬ñÏ€Èv@Aðám“BªÁ¢cv‹§@œ,B=†Ùœ@X媾»ü”@¾&7.ÁÏ+ÃwâbA¾&7.ÁÏ+ÃwâbAú¤„×§A£ÎV”}§?˧’—w @w²‹¿´·§ÀÃ@Oõñ?DB•*\»?:ø‘êÞ¹é?¢aÙMGÂ@~{ºI_¬¸?œÔ ¸â¶²?¾.¿2•Å?S>Tî¾ÆÌ?Û\¨Ý‚‡@í¥òy<áœÀA‡Ô×Ù#À‡‡~à?ÍܤðúPã?B/¸D•ý”À²VìÆY(ÿ?@E`¥ÌÁ?E×N°?ËVü¨°°?7Æ8»ž\@'ú=&%ÝÀ®l*°1ö@å±³Á!Sx^øA®È¢–·˜@Ø“Úi&@ku¤e:¨@ í>‹ÕG³@sÉYœ½I@_}VÇ-’ @M!п@`!MøÁU#@×@ClF»@u˜Û e@²DàÈúÃá?ªø 9@d®‰Ð©˜ÀhÏŒ¬Ñ?f€`¸¨À+ùàÑ›J³À.Õ¡"Ž1-@Hã¿o‘$@àYoߪaÀ?Ð$KÆ>ü?Â’»Ò„§À­¥É­Ò¿ÀM1F¥¦E¼?Pbš]2¼?Ð!ü¹øUÀ_}VÇ-’ À†: zó¡â?‡ UNê @C«|€ZÇ—@Ø“Úi&@þÿÿÿƒ×—Á_}VÇ-’ ÀsÉYœ½I@|h¬éØ@£ÎV”}§?Íö![„×—A¢Õ~S¦@û`ò7èÝ—À –òkaøã?~)ðèü?÷*ÂIbé@Õ¸dí`Ð?3åÚÐ8ø?œ|zá…@^Ï&p@Ùßñ~‘á @8)ih„+É?OþޱƒÂü?¼4#uñØÀ"⧪ûf@¤‰C“™£Àþ]‡¼ÆÀdÈÀ…æ?C§Á(’B´ÀQmD|Ü«#@ƒAceòi%@ff§*$@gŸÂ3Lô×ÀÁí§äâó?D—Ûi›À8c›vË›?_r\‚ðsœ?M™Ë. ÔâÀÁí§äâó¿Üɋܥ @Vg¤bnÿ Aá­ìsHÊA¢cv‹§@þÿÿÿƒ×—Á¨EÈ_³›ð@Ÿõ‰8!V@ÃÙ©ñ¢@,zVרµ@×;¬2jy@§QoF<:@]tÑE¾ë@ DpìÒ8Ð@ͱF" n´@Iùì{Íi§Àu˜Û e@–—QuF@ÇÈá‰×—AˆjC0ra@Áœbæ ý?à'á·Ä9@H‰¤<Ê¥ðÀ€š<af@¸Î§mqÙ¢À›¤ëšÂŵÀ;“†¦RF@RàjÃ_À#øÎï9˜2@_ªažkáëÀä¤ ½O›@ûØY”7ÐÀ¨%Ÿ÷mb´À ¢ä-@Ý“/5 …À§QoF<:À£fÛ‡@g"@P­½}g¦ð?r6IJ ñ?ÄØblÊêC@mÛ¶m[ÌAÕ‹Oòì§ÁWVõ”¾W@) þ¤Üù¨@¨tdçþ@KFÁÅ“·@æ?.®´@§QoF<:À×;¬2jy@w²‹¿´·§Àû`ò7èÝ—À•0ckôÁhš5 ’¨A‘®uÌ>ÔÀ•zÚË×? éአ%ÚÀÀŽ× ¯³ðÀØÉÎHö¹ÀÜÎ(ÿ£À~Õ*ãëµÀ~d&éÀ<½ÀIK{9Á!ˆ¬‹Û©ÇÀrO¸o6ïëÀ-«pe\ý?õýT¶5IÐÀVkئm´À vë&¶-ÄÀ§QoF<:@èìèýwïÀsðpå( @.ß‚±2²Àù‹¥G\Ì?[SÒÍ?Øð“âÊËøÀsðpå( ÀDÄe„$@åyžçyÝ@þÿÿÿƒ×§Á üZÓ?±@¤k¬)ã@Ã_4Ïì"9@¡{ãÕ€òù?wÄqc A0N¡¡oƒ @Á!Â!ÔÊ@·›ƒÝÀ²DàÈúÃá?+Äâݯ=@ˆjC0ra@•sˆ×§A†¹f*@s[.Pª‹!@«qÚ'È+±À–«ÈÒ?“Ã0ýZí?4/@—Íà‚_$@å¾’|1@’Ù‘<ËP@•6ÅW… Á‡KƶZ{Æ?{ƒ­ßk Àš°Œ¼ÊÀÂ7cFˆ·J@²œû¶ð®,À¡{ãÕ€òù¿éí#^å?áìVª‡ @]äÍæŽp@ÞðžÉC©0@´i¦F<°@—Sðr îÓ@5GQñ+û@­U—ojì±Á¤k¬)ã@ƃٹ_•¢@E6xnRÒ@ð|zÜ\Ç@¡{ãÕ€òù¿Ã_4Ïì"9@Ã@Oõñ? –òkaøã?øój™Ž'°À‘®uÌ>ÔÀ§’4¥á±AšÒgø’ð? Õ„ŸHk@öêžá³3@2ͼÇì?éïz£;ñ?g+ÑaÛ@)ˆ6ŒÉS@ 0s jf¢À®Ìn(«XÒÀ‘Îæ1?€À…´¤— ½?yµÿ ’@æS1½$3@/5l p]ÇÀ¡{ãÕ€òù?…é- '{?Àgáùu(Îð?!„\2Áã?}íNž8ä?›-ä•8@5GQñ+ûÀ§]«.ë%Aþÿÿÿƒ×—Áë%]H¨@&®yyšÙ”@\.Ü´ñ?nÉY­í×?Áœbæ ý?†¹f*@j>)›„×—Aø–4þã×¶?‹ íÒÚ?–~¥Iýi°?éCÞøÆ¶?PLÖfÊ?[[šXaïï?+"Y§ ¨Àøû} ÄGÉ?†[3èÌÚ”À¯šì£¡‹æ?„ã¯<Ü×?ÞV˜Š¼ƒÀë¦E5É!>ÀkŽõÑÇ8È?²~ CV¬@_"*IÅ5§@elÐæ3@DB•*\»?Y³HèQ§?•zÚË×?šÒgø’ð?EÐ.xmQ@¥ž×2·?V|ðS[¬À÷ó¸M4@êÁ´9Ï2ð?"cQ•6§À¯OD¯~´´?‚É*¨:7ÀÓÇì>\g@¬%Ç3ÿ†ÀÓÇì>\gÀ©ßQfd„@’nPV±€&@¥`¬Ò›Ú—ÁÜ>Ú¤´Ý—AhíõgöÙÌ?Cl ÎäÀGîWýd°ú?XŸ{Ôõ?rõ«ÜEó¿À_SÚ™ÀXŸ{Ôõ?_†vé-+õ?š*)>@_SÚ™@þÿÿÿƒ×§ÁŽßQ„×§AbÝ\úøå¿þÿÿÿƒ×§Áþÿÿÿƒ×§Aþÿÿÿƒ×—Á»È„×—AÕ-N»ÈÀþÿÿÿƒ×—ÁÛ¹v`NÜ@6æ‹ys8@Ú8Áª\@óBü@JÉp3¯¦@<°öå¤@“7þކjð?ªø 9@’L…‡@à'á·Ä9@s[.Pª‹!@ø–4þã×¶?YSÖ„×—AÙwV¨ÀfÜÀÂû ¸1oû?ÄĦÊÿ @X ‹)@8[zñœ#@`%܇!@¶Š¬&ìõ÷?( Ä諭¦À»9¬1½â?DIÛyÁÕ¤Àù7¢¿çô?ÄJáõÚÌó?’+ ÿfÀóBüÀ $Îð:@µä˜ÅW:«?­2õ"ÿÞ«?d£°7d&@ùÌ}[ˆÇà@©ªªªª Ú@ßè"X:æ@áxè ܧÁ$€!5:@@ÆVó\Ç@…ļ!©È@óBüÀÚ8Áª\@:ø‘êÞ¹é?~)ðèü?(‰i[ÕàÀ éአ%ÚÀ Õ„ŸHk@5Oë…„×§A›€yœú>@¾ôkyL‘ÇÀñÉ4Iü @åP¬Ø:Ã@õ´Æ¬+ @±Öš˜Ï•ÈÀ¹û—@ä'@2îŠîÎã5@¡w/*j8à?óМš1@ ÆéYÒxù?k?&Í•ù?óBü@úîÿ]÷'fÀê&HSmmë?@ ò’­zÀ9EìiI@ßè"X:æÀWŽã¡“Aê&HSmmë¿¢™K|Î]@ºŠ<ºæ±ÁõëT…g^@ÊLï·w¤@>#[©vmµ@9Ýœ yu@ šWצB@ái«€É@ýÞqoÎÎ@ÃyP«Ä“@{~µÊ9”@d®‰Ð©˜ÀMR"˜ßÀH‰¤<Ê¥ðÀ«qÚ'È+±À‹ íÒÚ?ÙwV¨ÀfÜÀÚSôJÖë±AQ¸}ʱÀmõkÉr´Àÿ?…/blÅÀÆWþ¸»À¸’@°¦ÔÀÇüå–À¼Ù_dܨÙÀ/)ýŒæ@\ñh½YÙÞÀ“›ºqíÅ£À— c»³“À>óËŒõÀ šWצBÀ2"Öé<·ÉÀÈHh)R¯Î?š×v4áhÏ?W¯T |;ØÀB!„€ß@¨EÈ_³›ð@þÿÿÿƒ×§Áì'N~a@÷Ö”f±@jVpøBšÔ@Ö†æ–@e €³“@ šWצBÀ9Ýœ yu@¢aÙMGÂ@÷*ÂIbé@F`©÷$ßÀÀŽ× ¯³ðÀöêžá³3@¥ž×2·?›€yœú>@‰ÞÊ…×§Aß,w~ÿ°Àø;¯ -@3¢ t&:@.ÌHx…Œ?@ÎUÚ=á‰ÔÀ*6¾³ÀÕ–ÀeË$ í¡I@j”úo+@¢ƒ‰·•ˆP@ŠÅ  ²@ÕH}v¤“À šWצB@Ñ:fæì7ŽÀâBÙäQå!@6°œóþÉGÀTÀ½÷@¦«?S Oó}M¬?:Úi‡© \@âBÙäQå!Àã ô"ê/=@÷Ö”f±@†¬Å ñ @þÿÿÿƒ×—ÁËd±Üpž0@ º*O®'ñ?hÏŒ¬Ñ?`O…¸5þ?€š<af@–«ÈÒ?Âû ¸1oû?Q¸}ʱÀí®„×—AGW‘ðß?$äÙ­~ì?Þ5÷~Ùëð?Ø(ð»=¸÷?á[È$†¹?ÍÓæÄãàë?ôØóó3³?´žùpÕm@K?ܚ铵?ÂÉîUƒµ?“ æê;À º*O®'ñ¿BéðnWJâ?z„A·Üü?iÖ9t<¥@) þ¤Üù¨@†¬Å ñ @TÞ: â±Á§yZ’?–@ º*O®'ñ¿Ëd±Üpž0@~{ºI_¬¸?Õ¸dí`Ð?ÊOµNÙµÀØÉÎHö¹À2ͼÇì?¾ôkyL‘ÇÀß,w~ÿ°ÀÈ K}â±A?“ž˜˜Ý?®¼¥³ì?_î‚-ñ?– ƒW:¦ÀíßßR¤¬Ð?vòdë9@ôØóó3³?zŸNÖϼ@YÃóx#É?\á”ÉÈ? º*O®'ñ?‰‰#¯šm¤À"Ä;†­Ë?ªë>™ÀÊLï·w¤@£-]Ð4@‰ií?Nâ±Á…·X3±@éñâ›ìæW@D+‡O¬@¤¥HÄ@f€`¸¨Àj¹v+N@¸Î§mqÙ¢À“Ã0ýZí?ÄĦÊÿ @mõkÉr´ÀGW‘ðß?Ur™ùâ±AU$½Ní3ÁÀÝØKok0³ÀÛ´üv@ŽTZÏ¥ÓÍ?åéFG­@N¯ 2m‘ÀÁ`{“óMÔÀ×äšVø¼É?Ão÷À)«É?,hk|hÍÀD+‡O¬À….í½lé?@z¤9G@ÃÙ©ñ¢@£-]Ð4@D+‡O¬Àéñâ›ìæW@k œ*Kn‘@œÔ ¸â¶²?3åÚÐ8ø?îóÆÉŠÄ@ÜÎ(ÿ£Àéïz£;ñ?ñÉ4Iü @ø;¯ -@?“ž˜˜Ý? B6á^@Øä±´ø @Ñë }Á@žÄ´]Ò¶@ÚËÔÈTÒÕ?dðš:}á@åüC ›o‘ÀÊþˆ‚ØP!@c¶K VÝÒ?]Ƈ¯Ö?DZµÒ?D+‡O¬@™ ܬõeÀø®s÷¡¼?cîýß[@KPÕÈ 75AÌÊy³E§Àæ8ŽãX15Á‹Ÿ7(E§@é—]•Ë–À´Ž€K“¿ÌÊy³E§À·ùºLå5A]©B€yˆ5ÁÌÊy³E§@?x§ä5×À>#[©vmµ@3ã5XA@…·X3±@UmMã—Á55w¥¼:d@qGbýÊá$@ËOz~ÀL @ÅqÇq ë@rlaˆn—ç?+ùàÑ›J³ÀÂÄRm¢›À›¤ëšÂŵÀ4/@–~¥Iýi°?X ‹)@ÿ?…/blÅÀ$äÙ­~ì?U$½Ní3ÁÀß=îï—A_”8ƒñôÀ•í3-‰”À £70îBð?yºyS°Àçþë›ÀÉô=]2ûÀ>ÿ×ndì?Ò{CÐê?9ÏÙçØÀqGbýÊá$À]¨É¬ßæ–À•ùl†£?_£3Ö¤?^ó½OJ.˜À—•mµËÀ›@,zVרµ@œ$È&¹£B@aÙqcQ£”@qGbýÊá$À55w¥¼:d@lÛ¶mÛæ›@¾.¿2•Å?œ|zá…@å«5mc››À~Õ*ãëµÀg+ÑaÛ@åP¬Ø:Ã@3¢ t&:@®¼¥³ì?Øä±´ø @`啜¦{G@2‹zE@âŠÍz”À˜Jå8€æ?+˜dä9t@çÌ9 ¸è›À‘á¸Hâ?qGbýÊá$@‹ª˜¾¤rÀ}׎Yúï?BÆ©æÀÞAeuúï,@}׎Yúï¿Å2b˜Å @P–“pÍ»@ÇÙ¹H6D@úNF‰@³@2ëhóô@þÿÿÿƒ×§ÁÎó²¤6g@´E²Áõ'@ÿ÷rÞ–g¦@sssssï÷@ò½³6ð?.Õ¡"Ž1-@ È¨þ'0@;“†¦RF@—Íà‚_$@éCÞøÆ¶?8[zñœ#@ÆWþ¸»ÀÞ5÷~Ùëð?ÝØKok0³À_”8ƒñôÀÌbŒ>†×§Aö2`g¿*@0‘O};õ?ÕóY.oj¦À¢”úª÷@U‹¦¦øÀK— þdò?ƒïÔbÙOñ?¼êÖ²ùpÀ´E²Áõ'À˜aä@$o%Qcäª?´i2䇫?ÈÛ+ú0@Ê™ý N¢@æ¾ø!½@±PYiE@þÿÿÿƒ×—ÁSV.ýOâš@´E²Áõ'ÀÎó²¤6g@TÇ* W›@S>Tî¾ÆÌ?^Ï&p@uÕ4AH7¢À~d&éÀ<½À)ˆ6ŒÉS@õ´Æ¬+ @.ÌHx…Œ?@_î‚-ñ?Ñë }Á@2‹zE@%P]I‡×—A7©|l+°šÀ ÷øFpë?þ<ј÷@ô]“ •X›À½û˜”eÀj)!üêæ?í/aÚÕUá?%±æ?´E²Áõ'@}9^MiuÀ¯Mœuºò?6§­qvyÀ]˜¸³Ý1@¯Mœuºò¿ÕÆõˆ‹@jVpøBšÔ@?Àü6’?@aÙqcQ£”@þÿÿÿƒ×—Á5ømý b@³ï¾³¶"@–WÈ1`º@´¯má¬Î­@S™Ëd@Hã¿o‘$@i §•½©~ÀRàjÃ_Àå¾’|1@PLÖfÊ?`%܇!@¸’@°¦ÔÀØ(ð»=¸÷?Û´üv@•í3-‰”Àö2`g¿*@’ï8×—Ažüâ¶Ë‰@žÛ\¡xzºÀd•GJé?Ðæ¸~†À­À¯ÇÚ`ý@ºâ¡ª¹@ßHoÀ³ï¾³¶"À%–AKÂ@@¹”f­œ¨¿?è°LŸ4À?'(ÿºÛ/ÀDQEQ`A¨tdçþ@kZ‚+Ú@@§yZ’?–@gI "ä§Á³ï¾³¶"À5ømý b@Û\¨Ý‚‡@Ùßñ~‘á @}·ÀñKsÁIK{9Á 0s jf¢À±Öš˜Ï•ÈÀÎUÚ=á‰ÔÀ– ƒW:¦ÀžÄ´]Ò¶@âŠÍz”À7©|l+°šÀ~T'BÒð§As0«žêÚ#@óktºÀƒ+ 7•ñä?Fð‹Gá—­ÀTÛ|a*€@e5T@³ï¾³¶"@K;à¾íIÖÀ4 pߣð?# ä—À€[tŠžØéÀ4 pߣð¿,R¦# @œ,B=†Ùœ@Ö†æ–@·Px`Dý?”c³(KÎ @`¹êÆYá?wÄqcÜ@uŒâ\ÇHÑ@Û··´œÀàYoߪaÀ?-No1Ÿ²@#øÎï9˜2@’Ù‘<ËP@[[šXaïï?¶Š¬&ìõ÷?Çüå–Àá[È$†¹?ŽTZÏ¥ÓÍ? £70îBð?0‘O};õ?žüâ¶Ë‰@‰Ù62^l@A¶Œ†ÜÀG+LQLj£?-?>îªU@ Ñ¥LÑÀÈYµéagÀ;Žb‰l%À`¹êÆYá¿K×J™žÈ?ò[®rß?HÁ·ºÑ¾ß?,ä* O@˃ šž“@KFÁÅ“·@·Px`Dý?¯ËÀ„sä±Á˜tj,ø@`¹êÆYá¿”c³(KÎ @ÍQÒ³¥é?í¥òy<áœÀ8)ih„+É?>¤‰C“™£À!ˆ¬‹Û©ÇÀ®Ìn(«XÒÀV|ðS[¬À¹û—@ä'@*6¾³ÀÕ–ÀíßßR¤¬Ð?ÚËÔÈTÒÕ?˜Jå8€æ? ÷øFpë?s0«žêÚ#@cqŽFç±Aã]è%jÜÀG+LQLj£?­Ž(`*þ?ŘøuVOÑÀ/!E$õ<Á`¹êÆYá?ì*U}¸¡”À¯3 ’®8Ó?oƒÄóç Àxê‘ ¦@8£fHI‡¡ÀªËXÅÝ@xê‘ ¦Àv›ØQ•G:@Hª ö@á@ái«€É@¬ H0@ËOz~ÀL @›{uݲR@ˆ³GM@ n¹Dø§Á}ß6›Ð§áÀŽ»G7¼¹@) þ¤ÜùØ@r*q§ÅMñÀÐ$KÆ>ü?7ËÀ–ðÆÀ_ªažkáëÀ•6ÅW… Á+"Y§ ¨À( Ä諭¦À¼Ù_dܨÙÀÍÓæÄãàë?åéFG­@yºyS°ÀÕóY.oj¦ÀžÛ\¡xzºÀA¶Œ†ÜÀwähuô§AZ7–(£ â?òä‹p¯.ÉÀ1MðÀ@ éÀþ©ßÞvÐÖÀ !:ÇÀˆ³GMÀˆIŒ¨÷Ëÿ?ÄçübËÀœÀ|=ù¼¬nÀ£0`‡$¸Àx!<ÔNQ"A}ß6›Ð§á@~ýôׯ@]tÑE¾ë@¬ H0@–WÈ1`º@wÄqcÜ@A_ÐôµÖ@ˆ³GMÀ›{uݲR@}ß6›Ð§á@j)ê±Á­ì¥ºå?A‡Ô×Ù#ÀOþޱƒÂü?þ]‡¼ÆÀrO¸o6ïëÀ‘Îæ1?€À÷ó¸M4@2îŠîÎã5@eË$ í¡I@vòdë9@dðš:}á@+˜dä9t@þ<ј÷@óktºÀã]è%jÜÀ )•¥á±A$`T‹šÕ?£¯ª£+1@æœüè†òþ¿Os}€Ã¸ÖÀˆ³GM@:k±ªð!“ÀCÇu @fξMÄ@é¿jíß$@T(Ü/–õ?˜†ífyàA‰ öâ ¸Á§íÆÄ åŽ@À(òd¼Ü·A¬KN-@§?ž`uT @spÙôÀjÃ~¡›¥ÀõPï7ÕôÀ]@Làôy@fξMÄ@ôež,ŸA˜†ífyàÁÙÍò$òaHAƒð»Ù¹ò@±ØÉåŃÃ@¼æ£µV G@@L® &Çä@oÇQL@l ÄÎ-<@ŸŸ‘Äú—ÁÊ R+%êÀCl ÎäÀFprø*&@ÝËš/…×—AiVháÕ-@¹VÂf=|@ã0±PiÀ8ÏjöQß.ÀÆ--“9ÀƒsÜ×—î¿„ È£‚î¿ U6WX0À:JjÀp QÁzÿ@O ip˜Ê@ó=yJæÔú?„ È£‚î?xåv'T]@8ÏjöQß.@¸né_hÐA"WO¨Ó@ A”3²|;<Ý@¬ñ‡ª'@:Jj@}¥œÕ»Ó@znÓ‘O@l ÄÎ-<@þÿÿÿƒ×—ÁÕ-N»ÈÀ¬KN-@&€å„×—AÍ9¿þ¿Ì@xE™8h+ÀýA|üÛ£Í?ÏNÈ1“jó?©Ši™>À`†hÆ÷Ðç?:Jj@OÕšZ¥Å/ÀÍ9¿þ¿ÌÀzOòò[R@:JjÀ¬ñ‡ª'@ýÞqoÎÎ@xóÕgÚU@¤¥HÄ@ÅqÇq ë@~áÖÞy@oíÕè9@Ž»G7¼¹@Ó{³Â¹é—ÁänöÝ*@­¥É­Ò¿ÀxÑé±ÞT´ÀûØY”7ÐÀ{ƒ­ßk Àøû} ÄGÉ?DIÛyÁÕ¤À\ñh½YÙÞÀ´žùpÕm@Á`{“óMÔÀÉô=]2ûÀU‹¦¦øÀÐæ¸~†À­À-?>îªU@òä‹p¯.ÉÀ¥þvµ §Àht™Lü—A&»fc@¥þý¥-@ø ‰óùîÀoíÕè9Àöµ¦!­ÀoÊ•.÷!¾?nj$j/ؾ?|zk^±À€À# [´@ DpìÒ8Ð@E37H'W@´¯má¬Î­@oíÕè9À~áÖÞy@Ò–ý¯Ðý¦@þÿÿÿƒ×§Á‡‡~à?"⧪ûf@C§Á(’B´ÀõýT¶5IÐÀyµÿ ’@óМš1@¢ƒ‰·•ˆP@zŸNÖϼ@Êþˆ‚ØP!@‘áÿ×ndì?K— þdò?¯ÇÚ`ý@ Ñ¥LÑÀ1MðÀ@ éÀRkš@ÈÀ ?&»fc@¶³Hä±A$²Õõ]ÈÀ¸ÏAË‘À£Vü@ðÝ¿–•E^í?Å?°¹·‚úÝ?[â$§Ë¯Þ?Rj6WÄ@Æñ+RÒ AÏ%´BãÖ@ͱF" n´@•%î Aù?uŒâ\ÇHÑ@¯ùiZÈ@£Vü@ðÝ¿Rsô¾@Ï%´BãÖ@úÆŠd1Þ§ÁÍܤðúPã?íÊV A$ˆ #ŒÛ·Á ¯X;æiíÀ;»tBW Ö@SYõÓNÆÀÂNŸ)îØ·A®c”Ã;æÀˆ3Án#ì.@Ç,y®ÙÎÂÀ¨é!Ü&K¥Àø¢…ç·JAüä•ã@H7Ë{uœò@æ7¹¬ ¨Ô@ ¯X;æií@ÌZ©)Þ§Á2{˜‘«Ùô¿ùͦ„×§A˜@]iÜaÀ}!`¤ý¿žó|Z ³¿üä•ãÀø¢…ç·JAæ7¹¬ ¨ÔÀH7Ë{uœò@;»tBW Ö@:ÇNàâì§Á3‰™öxëÀMš4iÒ¸ð@¤­/45@–ð÷bž”ÍÀ®c”Ã;æÀ§AÏ9Gß§A¶ñpN„ÆÁÍ©-‚¬µÀ—x>SÊA:ÒŠc¼¶@Ya,óK|ÍÀå´ð2ÞRA\­­ !Ö@ËÌ™AK›$âþoÛ@C£4¢XOÀ˜@]iÜaÀ©}VK•yL@™ŠËátGÀÏhÍãcªÓ¿j_F‡æÂÀMš4iÒ¸ð@9zúðå§ÁÇ“k¬¸`ÞÀÛ[ Ÿ^¹$@ˆ3Án#ì.@¶ñpN„ÆÁÅ…{²ò§A¸ ¸_@Î3Ee´#Á‘ &ð;Þ@›68Ñ&û@‹ÍËZþ—ÁM…• r—@]Ƈ¯Ö?ÅÏ0ø°á?í/aÚÕUá?”5ÝéEÞÀ„Bý¡Þ'í?™ŠËátGÀ qêÆ—×—A5Ãs€¹ì?€S½Ñë}òÀaþft96A7ÆõfÊQáÀÛ¦Ìñ/ˆAX媾»ü”@e €³“@Î)xCM0ù?þÿÿÿƒ×§Á½p†í@£|áciÜÝ?A_ÐôµÖ@¯ùiZÈ@ü/žß”ÀPbš]2¼?Ó&˜ 3 @ ¢ä-@Â7cFˆ·J@¯šì£¡‹æ?ÄJáõÚÌó?— c»³“ÀÂÉîUƒµ?Ão÷À)«É?Ò{CÐê?ƒïÔbÙOñ?ºâ¡ª¹@ÈYµéagÀþ©ßÞvÐÖÀÊ$¶Ì­µ ?¥þý¥-@$²Õõ]ÈÀã㮣…×§A_¦rˆð"À£|áciÜÝ¿Í*æø.Å?4®$ªrBÛ?}¼feÍÙ?2ž§ýò›@æ?.®´@Î)xCM0ù?˜tj,ø@eIv›×AÜR¢3 ²Á£|áciÜÝ¿½p†í@ȽN å?B/¸D•ý”À)ufÿ‘Å?ƒAceòi%@ vë&¶-ÄÀ/5l p]ÇÀ"cQ•6§Àk?&Í•ù?ÕH}v¤“À\á”ÉÈ?DZµÒ?üšC>¸Hâ?%±æ?e5T@/!E$õ<ÁOs}€Ã¸ÖÀÊ$¶Ì­µ ?+µjaïø?ÿG9až[ÈÀ(ÿЗæ±A£|áciÜÝ?ÏZÒ\“À‘ÀŸàäbÎ?æÌ š¬›Àí=EêÓF@4ÈꯜÀ1V!ñq@í=EêÓFÀ_½ì˜„5@ÄRk÷CàåÀ)ˆaÐ9¶A¾&7ÁÏ+ÃwâBAÍQÌÂ@þÿÿÿƒ×§ÁÆËs¼<§µ@UóV@ÿþ?Ç,y®ÙÎÂÀÍ©-‚¬µÀ¸ ¸_@-,„×§AE¯JfÈñ?*ÃȰ³CAég·ûDë±Á}!`¤ý¿ÏhÍãcªÓ¿^ò(£á±A™Ž*éFÁ*VýÜAHÒx³öãÀcªD™…íAc! YÈ:õ@q žšÀóBüL@åyžçyÍ@F®:™Ø@©Nœ ò½ÀÝÏelÀ?Ë⟼Æ@þÿÿÿ„î@¼ˆŸÆ)¢‘@„Š „E†@Ð!ü¹øUÀ_}VÇ-’ @†|Â3º[pÀff§*$@Ý“/5 …À§QoF<:@²œû¶ð®,À¡{ãÕ€òù?„ã¯<Ü×?’+ ÿfÀóBü@>óËŒõÀ šWצB@“ æê;À º*O®'ñ?,hk|hÍÀD+‡O¬@9ÏÙçØÀqGbýÊá$@¼êÖ²ùpÀ´E²Áõ'@ßHoÀ³ï¾³¶"@;Žb‰l%À`¹êÆYá? !:ÇÀˆ³GM@투Y½@À3ÆÅ§:ó?ø ‰óùîÀoíÕè9@¸ÏAË‘À£Vü@ðÝ?_¦rˆð"À£|áciÜÝ?H³ ³ïƒÌ@u`VD¸@Ú¾Ì5,@çic’JË?œ¸Àä›ïË?~vUQtçtÀX·òit(@¡éö³×@¨tdçî@óBüLÀÒΕœÀuÅ!EN5¤@;»tBW Ö@üß\~o”@/'Üòy–‘@3ÌiëQ§k@Nó“–½ÀŽIéAȦ¦@²VìÆY(ÿ?_}VÇ-’ À'ô’éKWÀff§*$ÀgŸÂ3Lô×À§QoF<:ÀèìèýwïÀ¡{ãÕ€òù¿…é- '{?À¯OD¯~´´?óBüÀúîÿ]÷'fÀ šWצBÀÑ:fæì7ŽÀ º*O®'ñ¿‰‰#¯šm¤ÀD+‡O¬À™ ܬõeÀqGbýÊá$À‹ª˜¾¤rÀ´E²Áõ'À}9^MiuÀ³ï¾³¶"ÀK;à¾íIÖÀ`¹êÆYá¿ì*U}¸¡”Àˆ³GMÀ:k±ª’ #x @~6›z—@÷˜èÑ€Z`ÀGNAåñ&@¥Ç¥ãMIÀ>ó/,@”УÞ@ù&UvV†CÀ«í8v E@}T6SgøR@Ç©Fùø#@#›Ï-CLkÀa»bþãau@ÌŸ`b#H;@­|‘Å6ä$@Å>?Üe@!D7Ð&Û‡À±wŠ´æÉH@§?ž`uT @xE™8h+À’º š&ÀN.{œ@»]2øÌÜ-Àöœàob`SÀ1pY«A^QÀjôJýKÁú?­|‘Å6ä$@õ[®EEVÀ’º š&@|ßµJ=ΊÀ­|‘Å6ä$ÀÌŸ`b#H;@ T”…y7@v=*7¹@XŸ{Ôõ?¾àÏn¬ŠQÀ¹VÂf=|@>’ #x @6c<#ÝÉ@µ†j„I@@ÉD‹ûXÀþ7[7×…ÀoÈN¦#™@ÑæPÑãÀ•G?Fòï¿v=3l]À2»€vEªÀAxhÝFü?•G?Fòï?1ucAYòM@þ7[7×…@­î|7%¸ÉÀ€ï¼‚é@2»€vEª@R!÷fð?AóÙ3¾3@ЬЄô@õ‹"¹OFäÀspÙôÀýA|üÛ£Í?~6›z—@»]2øÌÜ-Àµ†j„I@-ŠÔ›Œäã@öÕè5â?ƒÉ‘õÚ+Ë@'} þ·Ô?2»€vEª@ñÙhßÖÀ~6›z—ÀêNKþ@µ†j„IÀû†všÙ‰ÉÀ2»€vEªÀ‚é@¤ÙèÓiÀëk·z»¿@•PÂW3@Ä¡üY¡Õg@rõ«ÜEó¿ÀpÕ? ”¦ Àã0±PiÀ÷˜èÑ€Z`À@ÉD‹ûXÀ-Þåô Mµ@K áêÖr@ß3[V £sÀYý7xD!aÀa×GµŒzÀíå~É|-@ù± ([!!@a×GµŒz@¡”YHÀ¬À“¶‡öÚrÀM~âÒ'ÖPÀ1kv5°úgÀþ½«E½>@—.¬ºc­¥@Ä¡üY¡Õg@_SÚ™ÀbÝ\úøå¿T(Ü/–õ¿jÃ~¡›¥À8ÏjöQß.ÀÏNÈ1“jó?GNAåñ&@öœàob`SÀþ7[7×…ÀöÕè5â?K áêÖr@®%¨˜Ûv™@þ7[7×…À$Bn3bjÀ‡M°E­ÀºßF Š À¶!Þ¸  @®è§òufÀßi!3è –Àüiót5À؃$Ï@@Ð+ï æ@‰TÊc¾T³À¶";BGõåÀµ®Laß¶Â@Ê1Bþl“@< P–Ѧ¬ÀßWÄÓœzt@O*°fÙg‹ÀõÊà“Ò¢@QíÑ;=À†ä ƒw1³ÀÓCÝ‚MÀ‰TÊc¾T³Àì/…Á ï@=—íÏáÃ@EŠMèv%óÀ"öZ~Êî«Àqh*TË@S¨Ç9[W“À¼™4oâ©@míÑ;=@õÊà“Ò¢@‚\£ÖhDÀÁf>¸²À-vÅ|žúÁŠ»‹ü¦XeÀ—x>SÊAº*"/>½¡@¬q“¤í8?@ãMÕýv@¨Œ……O@1cU/D|@0êRS@£UŸöì¶Àr5%hôˆÀ:ÒŠc¼¶@¬q“¤í8?@R·+õŠ@)Ö™  "@‡ÁNxÁõ?b ÅG##@h9‹+*ú?ìÊ‘‰O¥@ÛiÄ÷èÍ@ÛÚLì· A$“X½Eªò@M͘?°Z@«yyê@¨é!Ü&K¥ÀYa,óK|ÍÀÎ3Ee´#Á€S½Ñë}òÀE¯JfÈñ?â¾ÒD­fà@5)v?Bv}ÀAçG!úaäÀ<üï•eVœÀ2Oïâ¦A@\iTÛÝ{@g²w¯G2@ªg¥U¤@Â9Ù¦Â@=üï•eVœ@ëÞ;ÑÝT@N_Ó_7&,@4ùK¶Ø€}@’-$]·IM@æBÿ¾Ý:@áV0uÙoÀ·d!‘EÄ…À›‘›»ÕVÀI•7t“u‚@zÕ6wÁžó|Z ³¿j_F‡æÂÀaþft96A5)v?Bv}ÀíãAåŠLá@+.Øài˜À‡¢¬ »ßÀv8ÛÉp@É2:”^/ï?üî¯QF@±“ÂÎî?HÉû¹¤i˜@Ê›RUŒr@õ¾^–µ+@´g㸱°@>f¿pØ–S@X’`¢=r#@€þm²\À>Cà r›@êÁ]Û%[ÀɃ¾4­MÀ*ß»J‚y7@ è ŒÄ@õ¼î)UÜÀXŸ{Ôõ?…žê>ð!“ÀÆ--“9À¥Ç¥ãMIÀoÈN¦#™@ß3[V £sÀþ7[7×…À4Ü2^PçÝ@A%% ?žGÀUM#*6ýï¿f˜yð®ÂFÀÌ#çvë°À'?;7å!ü?UM#*6ýï?1ucAYòM@þ7[7×…@kÚ0†<@Ì#çvë°@gBs#mð?V7@æ;@ЬЄô@8õÌ@r8ñÀõPï7ÕôÀ©Ši™>À>ó/,@1pY«A^QÀƒÉ‘õÚ+Ë@$Bn3bjÀB´$’wì@,Üè»®sEÀÌ#çvë°@GNí›»›FÀ>ó/,Ààø é˜@Ì#çvë°ÀkÚ0†<@߸®¡É@2ÇsE×W@Ö†æ–@/µ ­@†: zó¡â?8L³°@Áí§äâó?£fÛ‡@g"@sðpå( @éí#^å? $Îð:@ê&HSmmë?2"Öé<·ÉÀâBÙäQå!@BéðnWJâ?….í½lé?]¨É¬ßæ–À}׎Yúï?˜aä@¯Mœuºò?%–AKÂ@@4 pߣð?K×J™žÈ?ˆIŒ¨÷Ëÿ?99©VÐ[Á?öµ¦!­ÀY#Ñ&õH@–•E^í?Å?Í*æø.Å?Ú¾Ì5,@Ú,jOÖ¼a@†c£lÛÀݚŋ½~ @éMîÚFõ?X1{’ûgÀᤔU¦ƒ@ze$1x2J@%óJ3ÅeÀIà¬æU›@™Óà΋²@ìv—mdL‡Àâ?HÕ—@@E`¥ÌÁ?¸]Uø±?D—Ûi›À.ß‚±2²Àgáùu(Îð?@ ò’­zÀ6°œóþÉGÀ"Ä;†­Ë?ø®s÷¡¼?BÆ©æÀ6§­qvyÀ# ä—À¯3 ’®8Ó?8$ÈOm@‘6j+ÀoO~+áÎ?ŸàäbÎ?]Ë4i˜ @†c£lÛÀáiò(ÔX¥@’ yIkø¿sçØBU2‡@޲ ¦ý¢À³ÖX÷\eÀk/šëÏÀ@ÞZáEh:@ŠÑÿ+Ž€ÀQ¸BD¶ªC@_†vé-+õ?CÇu @ƒsÜ×—УÞ@ÑæPÑãÀYý7xD!aÀ‡M°E­ÀA%% ?žGÀ27u;¾è†@ÎwÁfPG@åÖ.éÊ:JÀËüíÚŒ• ÀÛeÚ §]Àx0EXGÀÚ|'oÎN@‡M°E­@—Â0i0¨5@ËüíÚŒ• @%jûÌ@÷4’6WÁAÀQ¸BD¶ªC@]@Làôy@„ È£‚î¿`†hÆ÷Ðç?jôJýKÁú?•G?Fòï¿'} þ·Ô?a×GµŒzÀºßF Š ÀUM#*6ýï¿,Üè»®sEÀÎwÁfPG@aˆsõHÇF@¾ß¢µkû@¹Ú^®­>ÀÀ¤¼x9Åö>BÀËüíÚŒ• À—Â0i0¨5@ßàžœ@¶ßõÏ¥µä?´¶îAkË?P­½}g¦ð?áìVª‡ @ÞV˜Š¼ƒÀµä˜ÅW:«?ÈHh)R¯Î?•ùl†£?$o%Qcäª?¹”f­œ¨¿?ò[®rß?ÄçübËÀœÀoÊ•.÷!¾?°¹·‚úÝ?4®$ªrBÛ?çic’JË?7°ÁÏÒ@Ô*ç$êÀ@+S0M‰”¡?¿÷Þ¼?1Y‰ž_ÓÀIà—QÉÁÀ‰Û<‚@íîa_ºt@ ¹¸cã @¼ Õ6樛@ÌìË-ç‹qÀE×N°?8c›vË›?ù‹¥G\Ì?!„\2Áã?‚É*¨:7ÀTÀ½÷@¦«?oƒÄóç ÀrÁóSÅð?¬«Ù²tNã?æÌ š¬›Àêsßo­¨?Ô*ç$êÀ@·˜6W´¹Ï@O‹]¹$ëC@Xé{±ôÛÁÀWÂoø5ÆÏÀ~З’>~@žàÅh0q@’Õ­K@¸/Áh<:À÷ŸrPT^@×ÐŒ+â2å?¼–lÌ?r6IJ ñ?]äÍæŽp@ë¦E5É!>ÀÓÇì>\g@­2õ"ÿÞ«?š×v4áhÏ?_£3Ö¤?´i2䇫?è°LŸ4À?HÁ·ºÑ¾ß?xê‘ ¦@|=ù¼¬nÀE6°`@nj$j/ؾ?[â$§Ë¯Þ?}¼feÍÙ?í=EêÓF@œ¸Àä›ïË?+S0M‰”¡?I3sÙuÑ@Oxrm,ÀÀ¯`ˆŽ½?à«Û OPÒÀÜópÁ»¿@šŒâ”@.ÀS’uÀÎCuÂI¡@Ù¿¨–°Oœ@.™Áí.š€ÀËVü¨°°?_r\‚ðsœ?[SÒÍ?}íNž8ä?¬%Ç3ÿ†ÀS Oó}M¬?8£fHI‡¡À[iðÒî7ÀFz\¢BÃã?4ÈꯜÀ‚,޳B©?O‹]¹$ëC@Oxrm,ÀÀ?Cíhæ@ ƃ½)À@ßw›\jeæÀ.ÀS’u@šŒâ”@˜tj,Ø@j°ÖÚ D@!à_[¾O˜@œ!kDưg@X·òit(@‘®¢ ¸@Ç™ßöSh±@W?jU h@‡ UNê @ÐÍóݪyó?ÄØblÊêC@ÞðžÉC©0@kŽõÑÇ8È?d£°7d&@W¯T |;ØÀz„A·Üü?@z¤9G@^ó½OJ.˜ÀÈÛ+ú0@'(ÿºÛ/À,ä* O@£0`‡$¸À• ö1:ð?|zk^±ÀRj6WÄ@2ž§ýò›@~vUQtçtÀX·òit(Àݚŋ½~ @¿÷Þ¼?¯`ˆŽ½?V½¼ö$úR@£K™Àcîýß[@ÞAeuúï,@]˜¸³Ý1@€[tŠžØéÀªËXÅÝ@ òA™C"Q@–6=+o^ë?Ø`eC@3`Sx£v@1V!ñq@X·òit(@U3¢æpÀéMîÚFõ?’ yIkø¿w»“…×§AéMîÚFõ¿|<|O/Y@"ÚÌzehWÀ$„B:@ŠB5‚A0*@™ô&rÃKA@fξMÄ@ U6WX0À:Jj@ù&UvV†CÀ­|‘Å6ä$@v=3l]À2»€vEª@íå~É|-@f˜yð®ÂFÀÌ#çvë°@åÖ.éÊ:JÀ¾ß¢µkû@WGøš@p_*CΞ?î’‹Sù't@›i!@1&à2Jã›ÀíŒêA¤¼MÀ$„B:À'2Ï€]À{5ú°Ê4@™ô&rÃKA@fξMÄ@:JjÀOÕšZ¥Å/À«í8v E@õ[®EEVÀ2»€vEªÀñÙhßÖÀ¶!Þ¸  @Ì#çvë°ÀGNí›»›FÀËüíÚŒ• À¹Ú^®­>Àp_*CΞ?6Ch[iš@Xèó ˆ£Àœm¿3Ës@ƒó­€½-Àæí*‘1½6@LCÿ5÷©M@tU ûÃá›À !+L_À ©¼ÁFîrÖ¡@qžÞÇ9úK@;·ñÊeÁ€ê¼üS¿ÉHAØ·ÈiÐÕÈ@ðïó¦Ù¦É? ¸»6¦@Z¸Ïÿ:pÀ-5¥_aÑÀ¾/)r·-c@4Ÿkh›@¯Î&á!«A@ûçó¦Ù¦É¿h@;Ð^ÀqžÞÇ9úKÀÂFîrÖ¡@VbÊÂ`ÉÀÉ='žÞÀVbÊÂ`É@ðïó¦Ù¦É?©–YŠdÔÈ@[¸Ïÿ:p@ ¸»6¦@nÚ´¨ eÀæj!0ôÐÀ±Î&á!«AÀ4Ÿkh›@•ª)²—~@t®¿PÂá`@…¦÷B“œ@Ç\ôRë†À#©Ò-£À¨ÝYø§Âw@Ú±ÏGo@wCU¬›,\@&ÿúý‡ˆ‘À°8ÒãP¡RÀ½™Lw0Pn@cáÖÅ&@@±ï*%Ù]…@xUT'\@IøìuÌQ@Чód @@¹hæ D@u¥œÎ/@—)âƒGkk@‰$”8ãBN@Ç\ôRë†À±»Ô$¨@†Èr÷1{r@6—ɧÀ{^' \@žéeˆi@I@ÿˆ¥QdwÀÊóÜBtÀ ÇÞu×I[@oÒK^¬Ô@VêG™ í¶aüFÀÖŒ=¯Î ¶@&Ö!"”ÀÑ<›+ü?—ËgæôÔ$ÀݲQÙ:ö?Û›&j^¼MÀš›Ýq! À‚÷ÙNùOÇ@Þ+Pnu·÷?õsú¸ö¤À¯p)領À ¥¢ƒl¡@^Øn[`e@÷'^I3À3]÷7³ú?ý3¨ESÀ²j:K“Éñ@†˜ÂèkRÀüMg€áq@&Ö!"”À-é ‘?ËÁ@ØwÓ%À¡$7O‹4P@A±yóJ!ÀžÉpý¶!w@Ëj$¶I@¼H˜}ó$òÀÊ-´s"ÀÍ,ì¦ÖG@8÷|EÞ @ZÆ'5—ÆÀ>Wï²ØÀ`wå6"¨@ë9Í<Å$ÀB7_LΖ@@è|!iïäæ@UjÍ´³ÀøÈ93kÅ`À ̯g†@¿R,Ì@Yán°û£@B ž5ÖíÀ'ð#.=Í›@UjÍ´³ÀµÁÖÜ÷dò@ù.ä€÷‡@ ¿°»M¯Àøzyš·ù¢@äý®Ú {@é› àäœ@‹çN?%ƒñÀ`¨¸É@†ƒ‡ÅD3À ºÝX|@»ïºv0ÀaÔ¶H¶ø@‚‡â‘5?@>б]¨@À$¦Ì—$:X@ Ý0aäQÀuó/§+6À‡~§‘~ÀÃýîÆëžQÀ§Õ‘÷Û0ÀêYZJ@vMé/r+ÀÃaa P…F@‚‡â‘5?@"7•šò{@ÐÐ^ ÆÆV@1GpÀuó/§+6ÀàX×{ ÀºV Ÿâ QÀ®–0 *qÀQ*?•{L¹@%ÈÀ³7‡@“/¹R$ÏÀäÿ^@:®À.­zbÙ°˜@6ž£c©ØE@ƒ!„íÕÇ@…”»¡=«@Ý•¥”c@u‰~z“H@R÷'ŸâÀd@§À =@”ífÅ“²À5.b))~À%ÈÀ³7‡@éÉyxÊ£@8ª'}_¥ÀQX @ÜÀi¤@Nq@bÚƒa @ò1^+)µ @ðïý /ƒ@Âë.á@s;@yå[Þ:!@¦]¼(=@[Ž5¶]@‹»½ô}Ài¹¿+ÈŸÀpIoÊŠU-À‰ŠêÑH@z¾kÖïtWÀ¦Ìº#k@>б]¨@ÀÐÐ^ ÆÆV@·K…Ml¢U@"8À/UWfÀ"ÑòuÇ¢K@¿ðû*fÀ1$›Þ×hF@ÿÚ;W¤ZbÀ1B­Ç0‚@AçG!úaäÀ+.Øài˜À@æ tïë@Í=·5„°®@ò˸¸;ÎÀka¯ ê0¢À–Ï?ßµC'@Uj‘¬ÙÚBÀ<üï•eVœÀ‡¢¬ »ßÀÍ=·5„°®@$HÝDPˆá@ø«kZÖ  Àúå¬5¬ÀtíäÄžNÀ¨ {üÐh@4‘4ÆQïÀª3 ÿg¿@P#Ò\îùø@‘éw~‰ÁÀkµoóŠÔâÀÙu/Ñ[@yÇ‹én_Ã@LÒ_’€òÀ‘éw~‰ÁÀN#lsºêû@óe#_ÀrönÔâÀ¤Ô(cxz@‹oÏí–À“/¹R$ÏÀ8ª'}_¥À:”o[Ó1AøÉ}vÿ{Ý@']êTSðÀ1[‘i–Àá¦UݸÅ4@Lâ*ï¢ÞœÀéÅüxÀ‘Ÿö4–a@IŠ‘à<9@u§zˆž|ÀT#ð_š´Ò@õ³¸QAá:@.­zbÙ°˜@i¤@Nq@']êTSðÀHœ½•.ÐÀ´é9·ÑLÀ@?9c=iÀ[w[Âär@‰ø †D­?—9våX]o@ë\Ê¥j¡„?@•2S’ë@na^ñÜ‚Ï@^ ƒ%v¦†@‰ÿcÅml@.ýiĈ@ bd} Î`@qÄTÄ@GïÍœS8À¤Q5m›Kb@†HqÉ:©?Y̬¯â z@ö¦²f¬/í¿êd†ªn#"@7/Æ3u}UÀ墪{ò—$@3ü]ÙfWÀ†S7rÆi@ƒA¶‘ä.À—A¬û€ÒÀ&чK,©@V²ÿ—µ#<À (5³Ìb@]@°§4ìç? h–æÅe@ÂÁö>Á¾%svÂ6§À6ž£c©ØE@bÚƒa @1[‘i–À×­ ~kzÍÀ?9c=iÀÕÿdÒ{Î@¬ö †D­¿[w[Âär@"^Ê¥j¡„¿–9våX]o@Tè©3Ve˜@… y‡á{@1²Vˆt 4@~*( W'@ ðù¿=5@gّټ @GïÍœS8@qÄTÄ@¼HqÉ:©¿¥Q5m›Kb@覲f¬/í?Y̬¯â z@x,^PƒQÀ }ë›À¿„@…*)ê'âSÀ:S]¡D˜†@ƒA¶‘ä.@†S7rÆi@9ÚÔ‹£ÝAkäõNØ6ÀK{:N+k@f@°§4ì翟(5³Ìb@‹¡ %)‚Àc›zÁF-æ@ßè"X:æÀ)oе¦–@3èX@Îѱ -v@„<9ÂŒ0À_]xPIÝ‹ÀiÌs%íŸT@Ÿžwy^2@ ÂYŽmðLÀ¤!@lR—-ÀÉ 0®;$ÁWŽã¡“A3èX@:da”@¹q°j]À鑲OÿÊw@<ï—Q+:|@­Ž„h£¥Àó†ŸÉ>øyÀªP†PaÍ”@Ç ¤ÍC´À†_XŽlÀÎѱ -v@¹q°j]À,2K/‘¸@,â@6%r@ßEQºÙÀ’À¥°pi'v@̧Ov;W@¼6l¿›rÀoåñ¬målÀ"Ÿ,Ü—±À„<9ÂŒ0À鑲OÿÊw@,â@6%r@€[òø›8µ@íM4…0p@2´äÞ©µ¡À™©üj'ÊrÀÑò Ž@‰‹Ûü[^‡@ÒÒƒŒl@oÓLYEu@Â$^íZ@_]xPIÝ‹À<ï—Q+:|@ßEQºÙÀ’ÀíM4…0p@\Â*ûJBÁ@à…Cþ$£Àѽò?B.@lCwˆ#t@ê:u:MÖª@äcÑɺ@”Ù,NÇÈÀûþ©8Î,@½X²_äl@ƒ<9 û¥Q@Ÿ!@lR—-@-CÊ6~(@iÌs%íŸT@­Ž„h£¥À¥°pi'v@2´äÞ©µ¡Àà…Cþ$£Àöv•A˜Ñ@^‡?:3t@8a$u4$Y@"’ówÀ@a‹w©ât@í±§@w@öå ÃYÊÀhÀNœoˆ¿@`©ž@HzSA’»ÀæÃƒAÀI¥Fr±À€&‰»#\@`©ž@ãf!Äâµ@¾¨}î{‰À4¬Äв±ÀÆG…%\À†©ì°À© ݬmíø@…ò§ŠlÃ@3ðÈ8Áü•Á€ÙNmîµ@Šm¤« t@¿YìÑAA@2Oïâ¦A@v8ÛÉp@‰ß’ •Aö]¯ÖÖÿÁîß–“Žig@+žºÔÿˆD@tF§w@÷ÉwLCZ@&Õ#Aò}uÙæä@p®:Oß“~ÀJg¼§‡\@·Û¾­dgÍÀwW˜´Ú‰ŒÀKCDÏZ(f@«³<"æ½=@nƯzÓ@A;W䱸æ@GeWsaî™ÀÇ&ÎTŒÀ®ðÿœ3{™@vCÕáfº@„íJ0=À#2¸$Ïe@\YxŒî-@=C[wC‰@‡·t 8ª…ÀO|¸¾`@›ïûGâø@.´.ÁbçÏ@lâAp¹ “ÀhçÖ=5XÀÒ³wµg@C€¤ð¬D@ZIåú$¿@ØåU`›z•@­‹ç?öûQ@ãÇ9M+ð?@Y®µ‡Š'x@°ùGâgúG@)6ª•¹yA¥´*_˜æà@< •O:ÀôzÂEÊÛT@dm°öAÒ”Z×î|Î@0–IKŽzyÀµEøI@…ò§ŠlÃÀ© ݬmíø@ ÙªeA&£ø¥ÁÇÀYJßJ@pŽ…Ý @\iTÛÝ{@É2:”^/ï?ö]¯ÖÖÿÁËmK:&¨AÔr°,‘_?@g#Ž„@ëH‹þÏñN@¼o‹^˜1@ò}uÙæäÀ&Õ#AY–ñÚêÍ@Bmˆ„øÀÿDp§»ªô@3Yò ÁŸxU6%±=@5CL™pí@;W䱸æÀnƯzÓ@AsB2áP¿@Ò´ˆ"^bÛÀãìV“M@„_]1–Br@ýŽ_BÑÐ@{Vã–9ùÀcÆ%œé@ŒÁ,# ©è?z[™Ï48×@Ù sÊñxÁ.´.ÁbçÏÀ›ïûGâø@ó²&ÒË@Ð6žUõÀ%ýo¤=@§$Y@ž ç)§_•@í®>‰{m@”‘=”v(@@ɧ—+f@ÞÓ%@P@»E$tæ@¥´*_˜æàÀ)6ª•¹yA"ïnbžÊ@è ±.åÀÒ”Z×î|ÎÀdm°öArX\ÌúYÂ@$nb&iÈëÀ«ƒGA@{•D½Kê¦À©=ßuÀ’¨ªö"¨@óíFë+À@DóʆÀ¬¯„?„ž@ÙˆËRÃÀSÀÊêÄòÇÀÅGÌ{e‡@æŠz§@¥I‡x ÀàÞ}a%E@B%—gÙRÀRˆÆ» RdÀÊÏ'ίòÊ@^9xC J{@„ƒë7ÎÀDóʆÀ®·Çé)ˆÆ@ÖˆËRÃÀS@¬¯„?„ž@º"É ÌÍ@Ýô£ÍÀœI‡x @æŠz§@þc(DÞhÀW–úàG*v@…ƒâY‡¥@ÄáC·ÛFÀîß–“Žig@Ôr°,‘_?@–‡et™P£@ðÐ!/dÀAþ6øßJ@Š>Y‡r@$7D@ßè&SÞûT@]qq—ûªÀ”{Ò¥E"tÀòªXÓâ²À½H+Æ3‰@²¥¢ ‚`i@ 0É‘ˆV@½–Dmû¿M@.TÀj!~@+žºÔÿˆD@g#Ž„@ðÐ!/dÀÔ¢[þvµ@X¥I{ž”l@d½2n@P@¯4·sl[@øôz ¶g2@Ól—÷­zÀäÊõߥ˜À¥$òŸ‹@îÓÀõ²±À4ú’W$BF@ÐÅ=3©Ã3@·BŽ¡} À@…ç™?šÀM¯ž·’þÇÀáR‚’n&@¢Ò~Oº•@ã1·9òNsÀ4=ȈÎ,–@Åj‰¢F¶sÀ!8%ΈE‰@ã!Y’ŠJÀK…Ñ‚ïç£@ >¿gÀ‡]ÀqµÕÀôy!AQ—¤@Nž NÛ™@hæk4ö¡ÀÃ)R7M‹@Œ±WuÒ J@ê:—„ºé¥@MhsµA‡yÀ-.ꫤ@÷Óé'™€ÀÕWÊšgß–@ÛÑ™8òÅgÀ9÷|<$§@Fð W¿g@K…Ñ‚ïç£@*R;9Ðn•À~G|nƒÀ×Àhæk4ö¡@Nž NÛ™@Œ±WuÒ JÀÃ)R7M‹@LhsµA‡y@ê:—„ºé¥@öÓé'™€@..ꫤ@ÝÑ™8òÅg@ÕWÊšgß–@Gð W@ÉT"¼¥XÀnäw?±0@Š,½ ¤XÀÚ:ÀÈ;„@9³Òc[v«ÀóÍù@õÀÇî×%dΰ@£ð~‚ˆ2Š@~Vä›…¾­ÀÝëð3]Àå°ô¬æ…@Á^‚aÀe Ò?‰@«ÇŸVýÀmþÕÚ“v@.±ªm¢¨!À»]}ýlÈv@Àã_fбHÀ‘žØ÷Pçc@ žTâ³XÀ ^!ƒÍs@Y–b’‰èGÀ¯ÜΊ¥q@ØVË0LSS@M¯ž·’þÇÀÿš­·cÐp@…Ãê$z‘õ@1nW™%°‘À烻ZþÅÀÖ±Rö&_”@Ú¸ MÇÀ/ªzTŠ•@”aûþ¥¹¶À„h†¬¼q@÷‘·ÝCK@ÿk»™sÀq”ØÔÙÓÀÔF‘÷e:@=÷ƒXC@¦«0¿]*mÀ8À6‰e@b„ìÏŸ€ÀIOQs·À¶Ä>yx½€À€Ñ¹†_ô¼@çà¶÷Hq¡@Ø7$ÞÀR·nW[¯¯ÀˆŠt¬s•@i/¦cÇÊa@%ïÀš£L†@áR‚’n&@‡›?baÏÀ1nW™%°‘À6]ûo<÷@^ðë£ä¹“ÀìY R6öÇÀØ_ }¤Š•À÷}ñseÇÀs=šßÃk@u”Cç2¼ÀÌ2Áú~sÀ1ç2‚¡@¥¾t^õJ‚@Që¡æ ès@k‚¡@jY‡r@d½2n@P@À_A*”g‘@<ä’R¼@ã·\2fÀ/¬ÇõeÕ£À$–^]%‰q@éqW³‰G@jd€@;€¡ÀôœÁ¼R³ÀÒ«9w\@é C.¯FI@aY±Ö¥@<;_«±tm@’or~ t¦Àã·\2fÀjgj÷Â@ãý€<ÔõpÀ%=í×ÌÀ±»KŒJ€À^Ýú¥7q˜@L Ì{@ójÿÊæ«@öÜÖq@<;_«±tmÀaY±Ö¥@rŒŸ‡:UÀ/¬ÇõeÕ£Àãý€<ÔõpÀÝÞ¤Æ@ù軺±i€@ËW‚j!ÓÌÀ¢=}]möu@žÍí"úX@÷ÜÖqÀójÿÊæ«@êŸt#}@h†¶Þ À•OMÿ@áÁ_ µÂÞâ@&Õ#Aò}uÙæäÀ%=í×ÌÀù軺±i€@üź×zá@´§>oñ°dÀC[Ó–_¶ž@{¶'ó?¥ä¢ÍF}@dÓºð²»9@AÖ©¼NNª@ý½²”Q@&ðo˜ÐÀ)Ÿ[EQÀíºÖ¤Ê–@¹‰¦u@ Ï0î-;˜@\ªÊÉác@qEFê+†@úÖûN¼üÀx†¶Þ @êŸt#}@}ðŒK®äÀÓâÌÕ˜Áò}uÙæä@&Õ#A±»KŒJ€ÀËW‚j!ÓÌÀ´§>oñ°dÀy$ðÑw×á@¡¶'ó¿C[Ó–_¶ž@eÓºð²»9À¤ä¢ÍF}@šÞ‡0r u@Èüù @‹“ºŒmÀ[¬àBÇÀ¾‰¦uÀíºÖ¤Ê–@MÙó$‹bc@ÇC4C©Ï/@öÖûN¼ü@qEFê+†@§‹}.–p†@«¬%À50@&F‚7wyÀE–v´G0À.Þ‰?À`𼽇Žl(n@ =Ýr}Òš?«¬%À50@—šÒ˜ö„@Ëã/.“'0Àéþ½FƒvÀd4FdÊ¿ÿrA7">ÀúEÝr}Òš¿½‡Žl(n@¸åo*¾ËÀp®:Oß“~ÀY–ñÚêÍ@gtžvî¡@¤—Åä¼£n@mu9þˆ@飋ðCŸg@SÑKݺ'@˪’ÛøIQÀ= [*Ò·ò?æ Û~CyÀá˜mª˜ê@Qe¤ÿä_,Àz I = @7 ¸Í¦3ÀN†}V~µÿ?0Õ©õ­©'ÀÇÅ›”1‹¤ÀØ öPyÀˆï`ëý?A‰´KºÀÈSZcÆô?’‡:O@;Àþ#íö@Jg¼§‡\@Bmˆ„øÀ¤—Åä¼£n@wuQ»M4’@m3‹[Ìe@ýÉåì™D@·# [SÀd¤Í–74|@ˆà_{ ‰ÀíæÙ6Û8@:|ZPÞÛ.À+ÝC¼æ$W@²Ã…ßf5À_ê›r`@ÐbÆØ&Ý)À© TÔMS@*œñ]á¡wÀI²ëãŒÀª'íƒ/g(ÀŒþ{u–ZC@ç¯Jò ÀC%ÀjqyI@§¬Ï‡âÃÀh‹6­ÿŽ@cµ×ˆm·@CÅïý¢À—×NÛ( wÀ鼨G·a @{rÜ×áš@sd-lÀ>£[^ @Á =zýRÀFADz¯Q@~€ÊÀµrÀîP±ýG„@šýÆâyX@—Ã¥Sg):@Qß"jcÀhB{[]€@ãZÆ:?ÍÀCÅïý¢Àæ/”®«Ô@ñ"”n»¡@‡8â&7 ÈÀsd-l@{rÜ×áš@Á =zýR@?£[^ @IzOyÕÓyÀ¬;5oŽS›@šýÆâyXÀîP±ýG„@j†fûÝcÀ¤¡ÝéZŒ@Y‚èÏ!TL@qA¼Xw†xÀmu9þˆ@m3‹[Ìe@—×NÛ( wÀñ"”n»¡@™¢Ø™AŠ¢@]Ôp/BÝ«À¼ˆ¦  ¥À"®üQñÙ@Þ‰Ö4âTÀÇi>£¶þ~@uØî÷¯tÀó^¿»îè¡@飋ðCŸg@ýÉåì™D@鼨G·a @‡8â&7 ÈÀ]Ôp/BÝ«À(.äÓ@Õü;)~Z’@»{=J½ÀÒÎù¢³R{@5T5>°F¤Àw°$oEµB@l7Û¢ËZÀø§ŸLcŽ•À£ð~‚ˆ2Š@9Ýsç•@àà¯=ÐŒÀ”X«©?ÉMÀCå5 ßg@¸‹þ^À»J 7Ô†u@ù_®M@~Vä›…¾­Ààà¯=ÐŒÀø I5¸Ø¯@wI‡Êäg@|"¼Ó2&ƒÀã¶°X?3¨ÁâàVRªœ@½{EP¨AqÀ‡ÒöA½½§õA[@É÷G¼À…„ÀFˆtç3ûƒ@ìCeÕKøò¿8žNLÓyF@k–á*—aÀå)Ì–ñA@¨éKkÀn ’yØÁ'ç26Ã+ðÀsLöÇã®N@Z&FÍì)wÀl©fi8e@ÊpT-VË€ÀàÕÌ "·@ÄêÓœF}›À&pñj!ÁØ]„.NõÀFةޘAÁ͸lS¨ÁqÀ‡ÒöA…ýEÈÓ¨AW …–õîÀATHA AþCeÕKøò?Eˆtç3ûƒ@âêr]õ>ÙÀÎ Ù¡)Âó@ry„f´ƒlÀ­©"›;ç•@Úᨛ“À窣a®À?â|;áÀ·Œö AÀ Ð—$µ÷À¢a'ä5ÝAn’omæÁÄ“ü –à.Aœ-Ï@Dc˜À‡9*ÑËš²Àã(Ò8ñP@lÌÆ³pW Apö ýêÖ@•í²ý*3ÏÀ Äá=DxÀýÐän¨ÒÀÓÆ‹†uÀ$»œ–‡Äb@þulWFŠÀÞp‘Ø¢iÔÀ•fsQ&· ÀphØÜ§×Às%KH[y§ÀúŽV;WŠ@ÄshÀ;è@QsK–ÌÜÀϘ•÷ ±ÀtÔfŽN˜ÓÀÍ—@ «À’Çò?2áÀ¤K>·À¯ñÇ{çÀ„<̉>¦Àí§ØüÆ<®@èœÀwêdÀ©7û±<ƒ@pö ýêÖ@—ž’óæ@>±A G¶¡À(0fRKÀ×ÎxîM¡Àß-O{x]§À]t•ÛAÀè¢7~Á¶@–³—\¢À霕‚—±ÀQ\†¤ÀG\¤i›Š¼À²shÀ;èÀúŽV;WŠ@€öÁ°Å«ÀàNÎNÙ·À”ÓQúø§ÀUJ¾º‡€ÀW ôdàØ®À‚ïÿ6½ÀèÉàÞªœÀÊ m{ƒÞÀÍ’þð€À²yèÊ‘6·@o $l\üÀð2Ç$¼ÒÀÕcjÄ^zû@ŽÖbW_²Ò@ #¨zWE¯ÀÑäKö˜åE@aE ƒ’¤@hçºÜh@ÿÐÜQ‰’­@Š–“c»4%À9_i+ÓÀ"·1“Ô–©ÀŽÖbW_²Ò@?PûÏbܬ@|þ ÔY¡6@ÞºÐÇk°Àû}ƒóH|@)ØÀ\øï?†–“c»4%@ÿÐÜQ‰’­@ÅBï—F=±@M±ŒŸÍáZÀÏt´W(õ¦@Ä™u"|À•í²ý*3ÏÀ>±A G¶¡ÀV®„Zh‰®@ŒSM$ÀÌgÀöD!Èâ ’@8•cþæÝ3@2*ü§Ü “@Aè3ÑÍ]ÀÜ#¹F·¼š@dÊ“Y-ßn@EËÐ…_—@Z/¬º%p@û ãŽ'ËÇÀ*0pÄ@ÂDÞÙü—@ϲ nl@(ù(Ÿ'*–@½í¼k’n@´ÒDF%¥@@Z9eïž@n¶ú·‡<Ÿ@:žÑQ©]À‹,ôÐ „¬@ñcÄl#G„@T±ŒŸÍáZ@ÅBï—F=±@Ä™u"|@Ït´W(õ¦@ Äá=DxÀ(0fRKÀŒSM$ÀÌgÀ½å­“ÐD¨@Ýúá'3<@®ËÓRgçÞ?@è3ÑÍ]@2*ü§Ü “@V8þªËD@kbê@ÅHÛÙv-B@ÅšÔ=@{ðå4uÀ¬È l‹ÊÀíŸß¬7¨B@üÙgó@®úæB;=A@[ÕËA7Ç@2àÄ4rP@רui+@<žÑQ©]@m¶ú·‡<Ÿ@PÍö¾Î-V@1©‹/@Xðèb_RÀ—9våX]o@"^Ê¥j¡„¿oOj*9x@‡ä´÷²9­?ýÐän¨ÒÀ×ÎxîM¡ÀöD!Èâ ’@Ýúá'3<@-çðïÕº@<&ÅET@39‹,›@¾Aä]0o@†xkC}œ—@µ«)O1Pp@ŸvU§¼œ’@µä³Ìšš”·Ñ@ð½—§®|ÛÀl­ë/T›ÐÀÌ'‹Á£É~ÀMà#iª½¦@­3¬+ãÁÛ5 ø&{Àáyv¿æÀð½—§®|ÛÀÜöì"ç@Ùu]÷ËÛ@µ¸¯™/µa@ÿ‚f‚‰F@oïRÂA¼ëCDD¾m@hZ뺮SL@e0€-B\@˜ì7_ß 3@ij ®Ÿ¼òÀR5ÔÂ4 nÀð½—§®|ÛÀl­ë/T›ÐÀÙu]÷ËÛ@å‘ÔÆÐ@‰,ÿ“£¥S@˜Û¡Ó[9@ÇÚ&Zð—ò@yk‘€`@i9Úm?@‘b;nZO@>Å$áÚ!%@¿R,Ì@øzyš·ù¢@!8%ΈE‰@ ã!Y’ŠJ@”aûþ¥¹¶Às=šßÃk@úç• ™œ‚@ªÙ¡“`øVÀã²þ‚@ã*/ÓtWÀÇ@^t¨Ó@kôCr|ÀåUŒ(PàÀØ cÄ• À"ñ_Clã@Xs¾,%/#À苾¥õv@k¨]="G@?G,ª±”@LN“~`@Yán°û£@äý®Ú {@ã!Y’ŠJÀ!8%ΈE‰@„h†¬¼q@u”Cç2¼À©Ù¡“`øV@úç• ™œ‚@ã*/ÓtW@ã²þ‚@kôCr|À82(§Ö@)ÏeJ(Œ¡À=WÈ%°ÔÀPs¾,%/#@"ñ_Clã@k¨]="GÀ苾¥õv@KN“~`À?G,ª±”@r:o[Ï@‡lTPzr¡@ ˜½éõ¾Àòý÷éñ‰ÀÄx¶ÃÍ2¿À±Ózµ–À‡lTPzr¡@ ýuäP¾@V‘[».¬‰Àñå/|¶C³ÀdûBÝ–À§•å¦À ˜½éõ¾ÀV‘[».¬‰À몎ÐÕ@zÀæ*‡³¥@QÓÂßœ©@Ýå髉@‹^Ê>×À×›`6c´À! ®é.»@а<Ï£@òý÷éñ‰Àñå/|¶C³ÀzÀæ*‡³¥@ÿ›÷†Ì@òý÷éñ‰@{m9 ½7j@"ä ´]´À?᜙¢÷ÄÀÊ-=š£@ð î@©zj–.A;’Ý>ÔæÀ)¿1çÁ‹@@F{€3µÀ-a;Ž£gþÀäê·yj Ì@ŒÞŸ9ï0ÐÀ€z`‚×wâ@;’Ý>ÔæÀ0fyÄ A†‹ù8J¦œÀ•Ji$•âÅ@Ómpæ}ÐÁ@ë_ЈHÁúë½µQEã@É9ú &´õÀç›9+ÄÀyqçÄ`¥Àѽò?B.@^‡?:3t@h¡„ÅP¦@ŽUƒÓÂ%g@CÕsú#šÁ@©¾Wµ¤@ت÷ñ@²Ð¥7’t@O´À¥ÂÓªÀ; ŠÌ]}‡ÀbI¿ 5„¦ÀÀå«K ÀlCwˆ#t@8a$u4$Y@ŽUƒÓÂ%g@4EAº¢@‡&_®×¥@®‡¯N²‰@ßÛ:+3‰t@ Ï$WnšY@Y¹ˆ8m‡Àq¡1¦“ÀÊ—ˆ¹]å@Ò[ J8’À,á·Ä5ƒ×ÀAtðGÃ'Àô³wL=8ÓÀ…j9Ò­K @Ò[ J8’À2rîÍè@&s|è3gÀ>P3‹ÒÀ}gC65¡@ˆ ¦Š!ƒÝÀ,á·Ä5ƒ×À&s|è3gÀ”ã׬—ÙÐ@ô¯Ê-Hg…@ÌãfÇEʾ@`¨*ë3M@Áµ›Ÿ5Àž” ™s's@AtðGÃ'À>P3‹ÒÀô¯Ê-Hg…@nåÂ!*,É@`¨*ë3MÀÌãfÇEʾ@ž” ™s's@w}…ËÖ›À¿Î ãf@Ü £I¸ì4@¿Î ãfÀÜ £I¸ì4ÀÜ £I¸ì4@à/§pÒ±Ú?Ü £I¸ì4Àà/§pÒ±Ú¿QHsüΪ@Ä–C%/fo@:Äuy¢@Ã/y›©‚_À2*ü§Ü “@@è3ÑÍ]@¿Î ãfÀÜ £I¸ì4À¾ÄÈã:§@Ê5˜»È¼UÀ'¬Ç2ó{1Àš´BrÀÑd@Ø’^ÄÀKߟ3ÌwÀ—ãœ.P]˜@Þ­´5¯N@Å–C%/foÀPHsüΪ@Â/y›©‚_@:Äuy¢@Aè3ÑÍ]À2*ü§Ü “@Ü £I¸ì4Àà/§pÒ±Ú¿Ê5˜»È¼UÀ^o+˜Qí£@ó`Ú¨µØd@ëv9´Ò˜ÀºKëqÚn@ေCdÂÀÝ­´5¯NÀ˜ãœ.P]˜@xgéUs{r@èåÔ§ßÅ À³ò¤c@ϯ˜ü£ü¿µ2tZ‡bwÀÅ/ëÏì˜÷?nÚ¸7ácRÀ‰gõ*Ò}!@èåÔ§ßÅ ÀêK¬Ùàs@ϯ˜ü£ü?²ò¤c@ñN‡‹èšú¿‹7vYwÀɹ²9ˆ– @_V²µ+XÀ_ ]_Ü@P:÷.Û¡?” 2ÐK\@Ì…-x·jt@Q¿ÿ>¥æÀÀ¶c1ƒÀ€a:AEÖÀ€ôO‰ÐŒ”¿ÜlÁ7•@ÔL§«Ò÷q@P:÷.Û¡?vÇ*å4_Ü@ì‰æ¬‘xZ@`€‰åZ„@@KÑãvfnÀ€íÔ0A¦¸À ZP‰ÐŒ”¿¨ç€`+EÖÀa,/F¶)a@Le¸þh=@&¤·h͇ùÀwÚo?¥@wÀµ¸¯™/µa@‰,ÿ“£¥S@íŽÕ&ßÃ@²K{•`ƒ{@ jåW1ù@†ùYI[f@å‹gÿJE@‹âñn—=U@,}»í¡,@nK˜ð¤ÁÀ­-¨BuiÀk–¸Sò5àÀc‹évÀÿ‚f‚‰F@˜Û¡Ó[9@²K{•`ƒ{@'Ê2»!Á@}Ï© à@¬ªšÏ.tL@Þ÷='Q+@²”0’;@4ˆò€8@G‰º•UiÀ´æ@õɆÀÀ$»œ–‡Äb@]t•ÛAÀ –ûÜ®"Aܤ¡Vül¾@Ñ EQÜ âÀ‚f` É @]5³¨@¯cGäaºaÀ)Há. â@>ìQF0ÓšÀÛ †òBõüÀ¶ÊÓ¤ë:¦ÀZW¨· z@b²µs£À£ö§ãŠed@­á~üï'‹À°FâNÒ@%xSêþ£‹ÀþulWFŠÀè¢7~Á¶@ܤ¡Vül¾@âzçåõ@Ú¹Û:»»ª@L\Z®r¡çÀ®cGäaºa@]5³¨@lË¡A` ©À‘X91ÇÂ@ˆˆ×ÛßO³ÀÒîþ3Å÷À¿Öu¶<¢À¼Œ,Ën³Ê@}ŒQØ(ŽŒÀ’7¿0[³@&xSêþ£‹@°FâNÒ@¾/ƒib§À$¦+ŠÄ„@“U©I‹ž@(ÏŸUOŒÀ<ÏPIÀ"]ìu@s|ª^˜@ô¶Ê”2îÀ\¡ÿâ ¼?@† ZÍÎÂYÀ:Æó”8@Ç(I¸À(ÏŸUOŒÀå|˜î(¸@L‰£¹4|@¡M&oÈÀï¶Ê”2î@s|ª^˜@)!+WÄ«YÀ§Øø|ºÖt@efuƒæB@—ðkÖÎçkÀ<ÏPIÀL‰£¹4|@õ(ŽaF‹@ädŸ™yöyÀ@`ÝËWWvÀšß¯é™jg@¸ïýY”^ÀVð’Ô’†@"]ìu@¡M&oÈÀädŸ™yöyÀ€Þ­ÓV @y?›;^g@*ðïÅeUŒÀ #¨zWE¯À|þ ÔY¡6@ž[^=)3ó@)ÊX4»\ÔÀl”¨t‘\@§þó(„ÀªDæ%@òÀ õF_øÔ@ÑäKö˜åE@ÞºÐÇk°À)ÊX4»\ÔÀXB ¶PÀÙ,-wÐ>x@!V о_•Àe±:³lÜÀ¿îH’§NÀOGDïÖOh@ÍnzRIEÀ3•g- p@-ïáû”@^Æp«`j@ó»ølްP@À¡³Ê?ÔjÀ[M`B—À©4§}§’bÀ /Ö ¬lŠÀ‹Züel¨b@ê ôÏïfŒ@ãàŸkpÝ@^Æp«`j@b¦žØJÆ@´"®rüjÀ[u°…@-dOë}ÓaÀ¡ž Œ$aÀ\ѯ¯WËb@N'›'”À*áŸkpÝÀê ôÏïfŒ@Ú9·ºòÑá@V¬X¦ ^Å@6¹ Ø2@ª¸,÷FLÀ”[v3áÀ¡'¤ž…ÄÀ§KRJèE—À:äýÜd…wÀV¬X¦ ^Å@"ÅNÚW!ª@þ¬Mi^cMÀg$¿þó g@¸¾iïŠÄÀCK帾¥¨À#þ³+ÑvÀ^rKë­awÀB ž5ÖíÀé› àäœ@÷‘·ÝCK@Ì2Áú~sÀùØ–ÊVW@Ì'‹Á£É~ÀåUŒ(PàÀ)ÏeJ(Œ¡À1‡8bð ÷@amVdËÄ’@'ð#.=Í›@‹çN?%ƒñÀÿk»™sÀ1ç2]ŽçÀòYˆtDF@>‹ÿk.+@)C¡óQeÐÀ4 }ßñ@"ÑòuÇ¢K@/&zÛ/­fÀÃuPLWÀ$ß^D‹š4À6¹ Ø2@þ¬Mi^cMÀ:‰£Ð@§¼U_F³ÀÄ8_×ðiÀnâj„@Ib&/e8ZÀÍ/+ìì?ÀìZaÈC@ ­ B.H'@F 9(+@?ø%y“@óÐØ+‹À±@›eËnòBÝÀ¿ðû*fÀö£´b>0‚@ÐÄY•†4ÀÕ»I‰ç2?Àª¸,÷FLÀg$¿þó g@§¼U_F³À!ØØìÞ@hÞ_Y„@š)aa‡ À#h¥ûä?ÀÅ'ØuÉj#À¸~6M8'@­‚üQ @­x7Y’~@æV(q„ À¸‘º=jÓÁvÝ@Êmä@ß7u "µµÀDòÓD܃hÀnƯzÓ@A;W䱸æÀC[Ó–_¶ž@¡¶'ó¿{rÜ×áš@sd-l@¦w©º7¸@ñõ ŸŠÀ£¬ßn»·~@OÔÙ©O³:@WBP|¡3”@Ø"öÕÄ|^@7‰7zkx@‚Â1¶\@Ý‚ |b8À4Jušätn@ȯȨé—@šž tä@ð¾àC‡@DeUÀï ÀßV(q„ @­x7Y’~@‡’Vj?ÏåÀ _`-šÁµl§Š-€@9-ª/¯zÃÀ;W䱸æ@nƯzÓ@A{¶'ó?C[Ó–_¶ž@sd-lÀ{rÜ×áš@ñõ ŸŠÀ¿O±—ìÑ@MÔÙ©O³:À£¬ßn»·~@Ø"öÕÄ|^ÀWBP|¡3”@ƒÂ1¶\À7‰7zkx@V—´@Y@s@ú5üIî§ÀŸž täÀȯȨé—@MeUÀï @ð¾àC‡@2(!Äêq@‹Ÿ¼ßÛjÀkã,HÂ?À»8ŒõZY@U‹°ØÖSq@9•l6© Ò?¨r£R=¡€ÀçÑÝ­·Z@‹Ÿ¼ßÛjÀÌLiç/‹@ëèT` ÛX@Œ&W ¥ØsÀz•l6© Ò¿U‹°ØÖSq@ÂÂZ%X[@ã>e>¨î‰ÀKª‘fñ]øÀÏÌÐnA™Ž*éFÁ•¢µ¿Û\A˶—ðê!2ÁÐ- §+šN@ÊÐ7®mÀ|/1Gµy\Áû Ó5 2AÍùØAŒ•½À“xõ‹!Á*VýÜA˶—ðê!2Á^UgLÅ/cAÜŠx¬±ZhÀ¿ò{2‡@‰ófRC@2A«ÐµcÁæ8ŽãX15Á%kNö15A™z̪ˮH@ÔG¦cÀ™z̪ˮHÀ‹Ÿ7(E§@]©B€yˆ5Á™z̪ˮH@Æbdh15A‹Ÿ7(E§À0sq‚<ÉÕ@™z̪ˮHÀísB©/ÀœTs`Bâ@X'F5ý·e@®B<êZàÀœ 0GxhuÀÙô˜+¡" @XÒ¸êûFu@ß …âO·À@àYØqeÀX'F5ý·e@·>j;EÃá@œÓÏcuÀÝêž̾ÞÀÌ›vƒu@Êmö8¥^L@ÌUõ­`÷eÀé‚OW³ÀÞp‘Ø¢iÔÀ–³—\¢ÀÜ#¹F·¼š@V8þªËD@39‹,›@¢"¸ÿš=@Ñ EQÜ âÀÚ¹Û:»»ª@ÄHß³ëé@Srfõ—áÀ}mi¡@çÅ%?Zx@!Ø£ÍR@)çýzÀù.4)Õ¼àÀBâuÄÉ[À@§ÐSPƒ @³¾gÇÆv@)ƒ8®¯@÷/÷fAŠ@€îâ|ˆlÕ@ðŒmÎÞ>®@‘îV½Ñ£@Wmĉ3tÑ@‹Ap26"¥@×xõ®9¬@•fsQ&· À霕‚—±ÀdÊ“Y-ßn@kbê@¾Aä]0o@þ@Øþ@‚f` É @L\Z®r¡çÀSrfõ—áÀ@»´êD ADQ¸t@g.•hÔÇK@Ã.¡ÛyÀŸaÀõç÷¡@kæÉLÂ@7š ]½ïÀîO§s@le]ºqLJ@›6Hz0‚@[xWËP^@Ý ›®@õö»%Ÿš…@oY{Ô…Ï@,Kµ¢?ËõÀÀ!®æ¤¬´@¯Ùy“hÓÀHzSA’»À¾¨}î{‰ÀÄx¶ÃÍ2¿ÀdûBÝ–ÀQÓÂßœ©@òý÷éñ‰@.o+(d˜Ç@]éLÁ"Æ—@g„èi.œÀ—‰2mÀmà4“ÆM‰@&‰»#\ÀŒh^øXt‡@ ‚u1m@æÃƒAÀ4¬Äв±À±Ózµ–À§•å¦ÀÝå髉@{m9 ½7j@]éLÁ"Æ—@œÄ„N¹:¼@r<±ÁðlÀ**tp„‘À€&‰»#\@mà4“ÆM‰@r<±Áðl@ÊmlÄR@g„èi.œÀr<±ÁðlÀZáÊ4™„¨@¯CYøw@„@M>­ÿê”Àj§ˆl zÀ—‰2mÀ**tp„‘À¯CYøw@„@]sI®;ˆ“@“„¦m«çyÀéuŸö'`Àùü ìçÔ@Ûb3Õí¹@Œl1›F¥Àž(A2nÑ„ÀÙ˜:Ãèv_@.Ž¥C}9@e|ðRb‘âÀ±š0è9ÒÇÀÊî¶3¡ÄÒ@'U®…7¸@Ûb3Õí¹@6Ý[U¥@Œ]2ñ_…À×"è2%ˆÀ;‹=áìC@T¯’# @úY*^—¬ÇÀô I_®À´!…Ǥï·@úö£âž@û©mZ¢²À@Ö=[ÜûjÀ©–¤’#’@¬¥[^+hv@ˆ9Ö@#tu}Á¹@SˆßvÞÀj¢0kxP½À%ãÑ ­‘Àâsµ8ˆ@Ö=[ÜûjÀÆíT’äÁ@¨Û„1pwv@wû¬ÔrÁ[@ˆŒK¥X»@ŸÎ.‚kçŸ@P÷Óì¾ÀqÌRÑ¿Àæ×ð%xˆ@£­-ŸvCªÀQ÷rˆNÀæ¦ö7ï@I¥Fr±ÀÆG…%\À½½§õA[@W …–õîÀmà4“ÆM‰@€&‰»#\@r+žêÒÜ™@à =ÛøCR 0@ÑY=MIÀí<'&ç5@Až[T‰`À€Ýgñ"N@FÆÖÂúgÀŠ}Óc¼Ji@Cµj‚€ŸƒÀóDXAF£@¡”Ë;‚Á€&‰»#\@†©ì°ÀÉ÷G¼À…„ÀATHA A&‰»#\Àmà4“ÆM‰@à =r+žêÒà™@ÚUéžÜ(XÀÛÅlƒèr@ «„`©}`À#CãP'æˆ@iÓìý¯vÀ¿/´i ’@ðˆX³ß “À …µšuŒ­@&NÜMeÄÀöeòªÊ7¢@ «Ýr-â@Ÿqpw/ŠuÀ./Í>¨óÙÀ²‘ xP ŸÀ¬“.Øz£@o5ŠBpÐÀŸqpw/ŠuÀÔ0²„ŠGâ@y…@éÁÈ À‚Ô³ØöÔÀ°ÍN£Ž¥Y@ï­B"N<@Ú±ÏGo@{^' \@^ûRöf’À1ý{ÄŽ8~ÀÅÌbü-÷Â@T ±ñä¨@=ÎL‹¹ZÁÀŒÊv°6¦À춦“G@ ª*É‘~)@wCU¬›,\@žéeˆi@I@Òþ¦QzÀŽÖÓèFnÀT ±ñä¨@Ü$¨Tyt‘@FÏJ¨òá¦Àe·ˆ\,JÀ&F‚7wyÀËã/.“'0Àó»ølްP@´"®rüjÀæ–!Dz›@ ãåX qÀ‘²#'–ÀiÅfäo@E–v´G0Àéþ½FƒvÀÀ¡³Ê?ÔjÀ[u°…@ ãåX qÀû¦Âäë£@c- …¨{@§Ø'±Àˆ¦ÀŒÊ…ņž@yO´pƒ@ŒÊ…ņŽÀyO´psÀŒÊ…ņŽÀyO´psÀyO´pƒ@ÿ£—¨Âh@yO´psÀÿ£—¨ÂXÀyO´psÀÿ£—¨ÂXÀÍ„†hº÷½ÀGeWsaî™ÀsB2áP¿@= [*Ò·ò?ˆà_{ ‰ÀPú‰ìíR@XÁ šÊ-CÀAŒo} ÓADáo&±–@GÀÌV¹Á}CÕáfºÀ ÏXYõûô?Ž6ðyÀ 2VxUý?ôô†½Ì%Àç¸^l–ñ?íüæÝ?ÀñEØð?Y= £LR À>š‰¼Â ç?:F²ëžRÀ Ä$îdØ@Ç&ÎTŒÀÒ´ˆ"^bÛÀæ Û~CyÀíæÙ6Û8@TüæNm5À@”PïR9_@Dáo&±–@â>;Š:©A’êØà¼NÀùßek¤ÁÈ>âßÀ,Þ"ÓOŸ9@‡†_i†±À[zÚUÓ¾A@ü- ¢ ÀÂ÷[à^5@tCßÓ Àhuzm%@MöƒÂÀ¯€€yº3,@®ðÿœ3{™@ãìV“M@ŒÊ…ņŽÀyO´psÀGÀÌV¹Á’êØà¼NÀ‚ÒfM«Aù4÷()m@ŒÊ…ņn@yO´pS@vCÕáfº@„_]1–Br@yO´psÀÿ£—¨ÂXÀ}CÕáfºÀùßek¤Áù4÷()m@¢Õü¡AyO´pS@ÿ£—¨Â8@K…Ñ‚ïç£@ >¿g@q”ØÔÙÓÀ ð}”à€@¡ Š¢`@"Š­¶öqÀ`2¦zû@Ûì¯ìúkqÀ"ñ_Clã@Ps¾,%/#@÷¹€:y»@:ô_Rú†”Àžj¿Ñ’c@쟺&[ÀTÖHB71pÀ&ömÕF˜@Êk]îëù,@ø$ŽùHÙTÀ_M¤Ó<’@ê×´»q„c@F>P¢›¯@ÊR>|@ >¿gÀK…Ñ‚ïç£@ÔF‘÷e:@FíÆË¼°×À"Š­¶öq@¡ Š¢`@Ûì¯ìúkq@`2¦zû@Xs¾,%/#À"ñ_Clã@:ô_Rú†”ÀË@°ÊÌ@WMÉIÀúVwÔ ¶@pšÃñi˜@NL`–¡LÂÀ@±Š×UÀP\n@é×´»q„cÀ`M¤Ó<’@ÊR>|ÀG>P¢›¯@têÆ@ÍÀ¦ÏñJJ.@Lu°9 hMÀÑ<›+ü?ØwÓ%Àžj¿Ñ’c@WMÉIÀ?or³ë@†÷KØ~ÁÀ‹ÐøMÎÍëÀ'c’ ÝÄ@@oˆpü?¸xô“gSÀ©iz|j–ZÀ@SBUp›Í@8švWþ?ª[J0š#ÀÝR;X>a@ËWYЈÀGlŒ&@fî¨DHÀ»–mÔž¸õ@UÄèÅ}VÀ¿Îq‰ÖÕu@—ËgæôÔ$À¡$7O‹4P@쟺&[ÀúVwÔ ¶@†÷KØ~ÁÀ}¿R ‹›ú@\ÂE¼ëÄ@Mc‡ýÀ„Tþ%À¿fû™T?|@÷÷gî†b@¢0]ÛöÀ§"˜‡&ÀêzÛhHM@TLüX„݉ÀØÎ Cœ²@Àט—á\)Àã…—âBD@=÷ƒXC@ ™²ë1mÀTÖHB71pÀpšÃñi˜@‹ÐøMÎÍëÀ\ÂE¼ëÄ@í2ì@§bŒÜ¨ÉÀô ‰ºo×gÀvBWf'‘@¦«0¿]*mÀ¤JÑL–@&ömÕF˜@NL`–¡LÂÀ'c’ ÝÄ@Mc‡ýÀ§bŒÜ¨ÉÀ±G´ÚvSAJ{»±½‘@ c:x‡¹Àá^#йX@%m‚…&Ä)@þÿÿÿƒ×—ÁPlåì²ÿÀÇÂE#^PQÀÓ!{@P_Æç‹R@p QÁzÿ@B?H„Ø—A`å$'€ÀاHî°¤@E  r¿)L@Ú%W€aÛ’À´âpB›qlÀm°?ªro@€dmË;l9@]SpÆti@("AëÚ9@ä‚69¶À«@—•w‹WdÀBÀš?lÕÀñžãf ó†@$}O3f@0­êéýq@à°ö§ãLC@=݃ã$@K›­ÂÄõ?éÙN`œí@çž*Ìó§Á(ѯ½¿ÊÀw0õ¨á«eÀ…à—ØæxB@ê§W›;A@O ip˜Ê@`å$'€Àn¼ï=اAßOˆ×,l@‹AˆµÃ,@9$`£}À7jáe<¬@ÒÉÿ7åh5@+ø±C#O@w† ôT1@6U ~š@¥S(Ø(ßÀ NÀ@®A˜•w‹Wd@ä‚69¶À«@:¿®0Ü@ŽÑž±üçÝÀ„~³uèÜÀm¶ýæÐA"£l8‹Á2@õýTðœf>@U…ON@†É{Ó@Kî?ÌZ·¡ÀÑjýXd‘x@P£¢¼îp@`í5¦Û+…ÀfèòôZ+™ÀSyÎR8|@ÔKÅÀ3£a@ KóÁbКÀ”6ÙÕU±À2ÇsE×WÀÁí§äâó¿sðpå( Àê&HSmmë¿âBÙäQå!À}׎Yú￯Mœuºò¿4 pߣð¿Y#Ñ&õHÀX1{’ûgÀsçØBU2‡@éMîÚFõ¿, ‹n›•¢@-Ç‘˜úˆÀÏO6Ñt£Àôl¿t\T@a»¾{w÷r@ÀäÔðs'W@p‚ìps@Üɋܥ @DÄe„$@¢™K|Î]@ã ô"ê/=@Å2b˜Å @ÕÆõˆ‹@,R¦# @Y¹»Š @ᤔU¦ƒ@޲ ¦ý¢À|<|O/Y@-Ç‘˜úˆÀÍ _RB²@k÷РgÑS@Ôš£3R¥À—IÃ@ÊÒV@äE ¶ÁÜ;@ze$1x2J@³ÖX÷\eÀÑjýXd‘x@C£Ñbl@<ÀÄUé‘À%Ø5¼aÀ%óJ3ÅeÀk/šëÏÀ@P£Ñbl@±d˜§ £@J3¬`ÀçüsÛÜ-‡Àj8±IÎÀ„íJ0=ÀýŽ_BÑÐ@á˜mª˜ê@:|ZPÞÛ.À†&âßÀäAëïg:c@ž†ò©¬DÀÕÏ¡GJj @†ê¡ß6ÀµXÚÕ!Æ@ÃЇ*ÀøÌ²i”åbÀü O)°=8@ÓÄh#Å@e¼ =³™ÀŠR®FJ÷?e¬àvœ!ÀPêDÍöö@#2¸$Ïe@{Vã–9ùÀQe¤ÿä_,À+ÝC¼æ$W@ä\ ­ÀóSÀ»x±«¹}@Ž6ðyÀ,Þ"ÓOŸ9@ž†ò©¬DÀ³Ç Ì·"x@ß[¹µ·6À‹äo¹ç…`@…ÇÀ2©*ÀV’ÜßMåS@ü O)°=8@³&Bæ×rÀ<§/µ')ÀLÍÈmFóC@ç}ëµw!À—Q²jBJ@ìÛë¦K8u@êÐÖÍïŽÀ²ExœÑgÀö§N"¹DZ@&r_3ûžbÀ^<Æ„^¨‹@êÐÖÍïŽÀº.JdÕ¦·@ÂKd6Z@„–ÓÆèˆÀ§«L©‹@‹.°Š´À²ExœÑgÀÂKd6Z@ép0h;Ü‹@4̃N™ÀLƒû“§1@«(Ì›8ZÀh?o¢UÀèûmü +@X"ÃÛ¶b†À^×Á᪙@ö§N"¹DZ@„–ÓÆèˆÀ4̃N™À‰ª¹ L®@ö§N"¹DZÀ½ÃNÀОFV…˜À$Eý6ÿX@_€Ä>Ÿ@ÐЭžƒr@@Ú³^CÞœ@ï´»ès@E´vwHŠ«@:–c<ó†@x¶µ!ñ‘²@ÈG…n)iŠ@z'Ñ™CÀ!á¬Fðk@s%KH[y§ÀG\¤i›Š¼ÀZ/¬º%p@ÅšÔ=@µ«)O1Pp@o¶A¦à@çÅ%?Zx@g.•hÔÇK@+ÿRÌ>ÃNÀņ³E´Á@»µ—¬£Z@µ ë.Š À~%©A÷•u@6ž|øJ•I@?hDòs@÷†U5‚K@Ü…ŽËƒ@ĉíý¶_@IÄxX©‰@ x"Y?b@®B<êZàÀœÓÏcuÀŸ~êß@bP¾³Ð‡@ùuZ‚g)n@|JåH/@Al4í.µÀÛ(dWÈgÀÓuŒó œ@DU‘B pÀÙÐÍHèu@pp¦Ñ[¨I@Ç×Ç ®@¦¢Çפ–Àœ 0GxhuÀÝêž̾ÞÀbP¾³Ð‡@œ÷K#W°Û@|JåH/ÀúuZ‚g)n@U?d+£S‚ÀåÎéŒô¨ÀDU‘B p@ÓuŒó œ@pp¦Ñ[¨IÀÙÐÍHèu@x£Çפ–@Ç×Ç ®@Fˆtç3ûƒ@þCeÕKøò?÷Ïp_ÈÖ@ð24œ&å¤@ÆÈ¬ êÔÀ´oÉašÀÒ¤› ãh@_ …º†Àý+_H›¥À:}n›pÀìCeÕKøò¿Eˆtç3ûƒ@ð24œ&å¤@Xg÷^"=¾@š¯æ7=“À@qñÈ\pÆÀž»´HBª’ÀÊãLê°@a…^“>XpÀaÕcÙü“ÀŒl1›F¥ÀŒ]2ñ_…ÀK_™f‡‡–@èÇ|áÏl@Å Pc3ËlÀÓêÜÏF@̮̊x—@qõÄ{ï}@uèXÂ1E#@-Å[­\>Àž(A2nÑ„À×"è2%ˆÀèÇ|áÏl@äe‘QÕ‡@€KÔ@_òIÙŽoÀ»¤Î¸}@îL£4þób@ˆZR¡ÉGÀÄ¢Ú½b@k.µDz%ƒ@ cÜà:DoÀ§µpd¶F@¹ö§/ÑjÀ°„Õæl@„0ê_ˆÀ2e„»]£‡Ày2“~¡ê“@Ö1ê,ä«_ÀMø1o¿>À cÜà:DoÀ葪@·FP×ÕœTÀiãñzVx@þE§@†yÀ1Å][–@°iªˆ@Kƒ'qAª¤À{p!>ÀÁ¥ :÷UÀþÿļ¦Áé—]•Ë–ÀÌÊy³E§@.Þ‰?Àd4FdÊ¿‹Ÿ7(E§ÀÌ‹p‹Ô¼¦AÊl eêÝ(ÀoùiˆäÈD@˜ºOµ¿Z¼ÔB§`Àt€©î(@ôp4ÁÑâ@ä°…Ì%аÁ´Ž€K“¿?x§ä5×À`ð¼ÿrA7">À0sq‚<ÉÕ@Êl eêÝ(Àc‰óˆ¨œ±A ˜ºOµ?oùiˆäÈD@©>Á‚l½(@Ïtƒ©eÀ÷SŸ›l>ÂÀSU\Fò@WBdÕ9·À/¼ý€¾í@_ölW´ÅÀDçr\ÉÈû@Žo¨V·ÇÀ|HÇváâ÷@#@çzµÆ@+Þ-÷Ÿ›@¬Š{L¦‘@žybÊQnv@؃X èéÈÀ›¨mÅ´ À+Þ-÷Ÿ›@ËÆF©º@¹6¨,‘v@K¬¸V£®\@íõ|+¢ ÀòªA{~wºÀ”[v3áÀ¸¾iïŠÄÀ`¨_ÁSOâ@Í(LºÃ@˜áÒÕî­Àp)½…–™è¹3•@ýÎÈåNz@¡'¤ž…ÄÀCK帾¥¨ÀÍ(LºÃ@Á“Œëκ@‰a¹§±¿ƒ²BŒ}î­À…}Eqy@0ŽÅ4_@‰,ÎÞùð@ f„úÿåÅ?SÃÌÇÑàæÀ ¹R:†@+ŸëC¾ÒÀŸ¼”e¡:†À f„úÿåÅ?Õƒz¼îð@„ðì‚7;†ÀBû½àæÀ5Hí"Ù9†@å/ÍÁ+¾ÒÀõK¸…Ê-ÇÀÐáR±,(@¢!k0øLGÀݲQÙ:ö?A±yóJ!À©–¤’#’@¨Û„1pwv@@oˆpü?„Tþ%ÀU¿vÙÓ[ØÀ£ß<˜bΑÀf=(âã@@y®Ê‚@¨@ÝÉd•2!ÅÀö”q¼Ç@¹úµ{“½@AÒÔz²@¡@CFÌÊ› ø?þî&ñfÀž’­’vp@["Y.»ŠÀšý°ÈÀ¥qËçÁ«ÀÜ™sû?Ó7 µ žÀ×4WA“ÈÞPa €ÀýMlã +Ÿ@Û›&j^¼MÀžÉpý¶!w@¬¥[^+hv@wû¬ÔrÁ[@¸xô“gSÀ¿fû™T?|@w5§:¿µg@6›¶ÔÀ@y®Ê‚@¨@¯Ó lº×@þ=5ÈïÀâÁªÌâ‹ÁÍØZßûD¢@DR?/{P…@˜­Ð[PÀŒ>ÈÄÆt@0ÃQȱŠÀEÔóŽŽ¥@ ð![™ŠªÀÁúûìlk–À¹ß†¾RÀÜ(Hœêl@PW0•„ý—Áíf]H?«·@+à5¹h©À¢ðÐ×aÀš›Ýq! ÀËj$¶I@©iz|j–ZÀ÷÷gî†b@4œE…²@zà6Îr–@ÝÉd•2!ÅÀþ=5ÈïÀs*ýµ Ø—A@.Ž¥C}9@T¯’# @ÓêÜÏF@_òIÙŽoÀT,Aƒ¾jSÀ@‹Œ‹Âà’@6ô3—®G@i.­¶åŽÀe|ðRb‘âÀúY*^—¬ÇÀ̮̊x—@»¤Î¸}@sWûô¦Ð Aò àImÑ@ 'Cvi Áqî± ²À4,"kšm|@a<\S^–À±š0è9ÒÇÀô I_®ÀqõÄ{ï}@îL£4þób@ò àImÑ@K÷$º0µAôµ)©›m²ÀÒéí ËÁ¯my$–À—{ÎæU±@g²w¯G2@üî¯QF@\YxŒî-@cÆ%œé@oùiˆäÈD@ ˜ºOµ?Z<†b¹W@\“*2@væD^Àç¦Y[Ê߯¿¼€Pü°A@•—jn°Ý@¶³·"“i@©®êTÕÔ8@rkkÒÃqÀI–SÒZDÀªg¥U¤@±“ÂÎî?=C[wC‰@ŒÁ,# ©è?˜ºOµ¿oùiˆäÈD@\“*2@F8ÙZìOT@¦˜ºOµ?|²8TR^À¼êÒtké%@p˜œ7ý?Qp|P6ûN@ËwòQÁ@…ÐQÖ¿VÀšrì/ûv)ÀÛ%˜}ÅÆÀ”Ö“Þƒ –@C¦5PÍÝG@Á^‚aÀ9goa@Õ§§‰À®÷Ïl(äŽ@$\Dy‰f@\(6¬Æ@dØÜÿÎú”@¿çÙž¿=@ħéáãyÀÙa{L¯$@=æ¬Ò¯zÀÅ)Œÿ¹ìL@£¢é!PgÀò¼½øL@³ÚÏBb«tÀYÃ5¤„@04Íi@ª¶É×ãÀM¡4xˆÀ?sM wŒ@Uæ 3aÓÀüùì qÀe Ò?‰@|ػ̔C‰À]⌙¯ò²@³Ë©6”5—@—±>¢¼îp@dØÜÿÎú”@Nárìl¹@þPUœm@ÀGPÕ°—£@F¹‡,ŽNÀ„ûVäÿµ£@ñûØyf]uÀe85˜G8‘@xƒ]C¯tÀƒ„(шž@Ƹ[u@|SYi’bs@ËŠe6@„@6:Ø^E,K@Zµ?±/Š„À7YA{I@RÍs‰¯7ÀgC­…È™À“–œ¥­â¿;ž ÷OÀ6:Ø^E,K@Tµ©f>j @5H€Å=ÀÅÔ(]™¢7ÀÌésaS@ÜÞ†^Ç¥?–ÿnè™À<ÿúš®J@Zµ?±/Š„À5H€Å=À ‡ã#¡@ò³t/h]@ qpzB@Ö<›@Pƒ„À²ÐSä@HÒx³öãÀÐ- §+šN@ÜŠx¬±ZhÀ3tØy{†@J–ú{LMÀßBwæ8“lÀ—å Õ(:FÀ&+fûîfB@…ÇŠ^ '@Gw®*Gr@‘bÑ*+ÁcªD™…íAÊÐ7®mÀ¿ò{2‡@J–ú{LMÀp]?÷ Œ@ñ;zECÀÐm·)F[ÀÇßY–;®@%ye“Žpœ@w³g<Ð@e,ƒ{°@·á€ÙÛÒÀO±ù‡u´Àí\Z ’Àž×)‰À¤x7?SÁž@Ñ…²¯,•Àe,ƒ{°@ݯ©\»@úÈû·FŒ¶ÀKy-AL´ÀJ4-ùyÀzÕ²:3Z|Àƒ!„íÕÇ@ò1^+)µ @á¦UÝTÀ­_o82÷@4¦GJü|@Ê{Âcc¼UÀ&ÿ«ùɉoÀói"™æÂE@ßè• )€j@ÝPŸaº P@v±,®o©\@ä3M"(4@:Ì]Oþ W@Yiqš²¶2@„ÈÝŒKžT@𵯺r*3@ì¹ñRéÜÀñ/kf3ˆwÀªô”ÎIÍÜ@Ê{Âcc¼UÀâ(˜ïùi†@LÜXjÒ¼E@HG`(…«wÀèU17ÕsP@žòôo¥4@™3SËA@ºý ÷@aªG^•œ<@Îâ]s<@û,â™9@q xR,Ì@‹^Ê>×À"ä ´]´À¬ùáwñµå@¢Ë¸Žæ½@CheÑ-ÔÀUâ¯å£À×›`6c´À?᜙¢÷ÄÀ¢Ë¸Žæ½@ÆÕþ`1³Ø@U_°°£À߃PnÌÀ)¿1çÁ‹@†‹ù8J¦œÀQøÙúÞñÄ@Ψ µ¶À7ÔN¼¬ÀØ\ëê‘T’@Œ5 ±Ù™@ʤßÿcõ¿ »†÷áÂÀ+èUùÝ›¸@@F{€3µÀ•Ji$•âÅ@Ψ µ¶ÀŽ˜Ïr“{å@EüpŸ’@'Uv*›cÄÀ¥ßÿcõ?Œ5 ±Ù™@aÒ ë6HÃ@ØÛŒã#ªæÀ3;vr´„´À`í5¦Û+…ÀSÃÌÇÑàæÀ„ðì‚7;†À_0] ­ã@úQ"ÝÌço@VÀlÌ¢@Ó!çmcz@ÔhÉ*ŠÂ@¹R:†@oá“ ¾„ÀfèòôZ+™À ¹R:†@Bû½àæÀúQ"ÝÌço@/°06vþâ@ã™–¨/ˆy@ŠTЉ R@¹R:†ÀÔhÉ*ŠÂ@l”¨t‘\@+ °ÑÝ/†ÀÄUövíî @0Ÿ~à&8@‘£§6§„˜ÀÖkDÁQ…PÀz"93†À¯ ô%€‡@§þó(„À §§Šë¯@0Ÿ~à&8@‚ßš¦‹Ÿ@‰ðøœìQÀ;(Û[ŽZ’À¾A™Ü7˜…@-AÃ}FA³ÀsëubÁc@ I؉۳:@.¦lDu@§¸Ú@±GÀâ±›1¦Jy@b¥´š› l@Þ̺)øÈŠÀ]‚Ê<³á*Ëì¿ÎÐ ø…\X@öû¬ãÀÁ«~¥!j@"4E‚*>@툲ÕýŒj@27âO<@woV®r$:@9<­¶L@§¸Ú@±GÀ(>š•D¿‚@ð¬,UQ@&&ÎÚù8C@ÅaÝŸSÀÄm,^p{ÀwcÚĈí?ԃعtm@ów?J›&À&õcF¡T@ygœÏ"@»ŠI¥K’šÀfÙü+DõT@×X LÈ@˜Ì>á*Ëì?Y¼(á P@ôû¬ã@ÍÐ ø…\X@$4E‚*>ÀÁ«~¥!j@ 27âO<À툲ÕýŒj@I{÷ÞÁj@€ àèºB@â±›1¦Jy@ð¬,UQ@1 ußnu«@\~òÓ@îcÛ6–ªÀ%zÊ(jÀ1$ÓæªÒ†À¥ÝsŠ`Às#ëÚ½¬]@j2>¸Å4@b¥´š› l@&&ÎÚù8C@\~òÓ@æ¨Ýcõ @fZl•j"”ÀCdJœJý™ÀÓV[|Nh`ÀHlˆíë¶ÀR÷'ŸâÀd@¦]¼(=@$® …½ÀLâ*ï¢ÞœÀ.ýiĈ@ ðù¿=5@$Ú2éè,·@Nùðåw|š@áƒr Í S@Ë_Ê5å7@Þ̺)øÈŠÀÅaÝŸSÀîcÛ6–ªÀfZl•j"”À±‰´Yö³±@¾f Õ–@%‚U “@î%š„1ÀÐ*Ð0u@ ‰jä?§À =@[Ž5¶]@ö®úîFx•ÀéÅüxÀ bd} Î`@gّټ @ÿ p 9@HÂíSŠr@n…ÓAR§*@(ù¨ º@]‚Ê<³ÙÀÛøCR 0@ÚUéžÜ(XÀ30<Í2À@|U­÷í¤@ç%{æ~"@!Ÿ»ÂÞDKÀ§B)«ÑØ8@9ðŽþKÅSÀv³SâÚT@ß*¨<.pÀ=àéP~õv@×^Z 3ôÀ4:ôiM™¢ÀCñãÖ«‡Àk–á*—aÀÎ Ù¡)Âó@ÑY=MIÀÛÅlƒèr@|U­÷í¤@ 4ç"@bÕE<À 2s€We@Ûšm9rSÀŒ ¸âUòn@ÿ;çjRpÀ¡þû]ÀS‰@ý¹Nn°¡®@ð°|‹F8“@ÁpáÚ+ù@ï5œY¶@wº^-ÕÀyÆ*?ˤÀ=|Œè&±@í‚æ§ºF†@Ž.µ~ìõÀê+ºýµŠ³Àì¡ÏrŸÕ†@t ‚þ®§l@ï5œY¶@ß©Pðñ@jEã¡€°¡Àêç€iÃÀŠÂÚ"‰m‰@Èà ›`@ò˜ù@Á‰³Àz Ÿ¾3ïÀÆyØo_¨Î@èÛ°]@kµoóŠÔâÀóe#_ÀŸƒgü’Àdô¨·´Æ¿ågk5¦„×@0ª@W_Õ?çÛ°]ÀÆyØo_¨Î@Ùu/Ñ[@rönÔâÀºû¨·´Æ¿à_0ù’À0ª@W_Õ?—,çÙS„×@Áï§ +@†Ñ,¦žÇEÀ%%Íþí‚À§ÍmØôŠ@ÈCÆ=1@Àã_fбHÀ‚è_~µH@aîˆrÀÅ)Œÿ¹ìL@ñûØyf]uÀæ~ü7@¸ëO ´bÀ §ÖßRâ @ æ‰Q$GcÀ&ÿ«ùɉoÀLÜXjÒ¼E@3tL_h°…@R™«n×P@w»õãí:4@£Ž^Ý]Àòi"™æÂEÀš¸©¡†a@°À ?‹z@NûU]üb®ÀîãCêÊKÀ‘žØ÷Pçc@¼÷päGêcÀ™s©Ökß@£¢é!PgÀe85˜G8‘@åYÞ«jæÀ¤Ô,8&~@£¬¾Ë(À(Jp‹X@ói"™æÂE@HG`(…«wÀR™«n×P@rêf,,@n¹»¼)NPÀM6›Üx@˜xЕÜVµÀ&Ûvª}À-a;Ž£gþÀÓmpæ}ÐÁ@ÂÅùiLMNAï?i¤«}@ìÄNìXMÁ¹½Qß„#B±@T hŸÐÁÀh¸íhª}À )qÝQ¦Àäê·yj Ì@ë_ЈHÁï?i¤«}@®×R5HNAµ;±³¿;±»XMÁäê·yj ÌÀ‰4)ŽóÜ@7ÔN¼¬ÀEüpŸ’@Ճعtm@wcÚĈí?ì‘çUÃ@̡йŠÀÿ©'‚'@U|¤ËWS@¥Ï ÍÀ«ùÞ¦ûœw@â+¶Ã@š­@Ò†#´“ø€À=—ŠÔ¯z@ƺ׿ˆCÀu¸b“PIY@=¢y©äPƒÀâ€ØD„@Z •ÛH-8Àø:«l^¹•@¡lbAB²i@ÈRjK–@åÃÓ}>h@Ø\ëê‘T’@'Uv*›cÄÀcÚĈí¿ÔƒØ¹tm@̡йŠÀÎ;Ü^ªÌ@U|¤ËWSÀÿ©'‚'@ªs¶z¹Bd@ñÐŽ¡¸ ÐÀÒ†#´“ø€@ã+¶Ã@š­@¾º×¿ˆC@=—ŠÔ¯z@pÎÓ¦‚À-­·}¬@Y •ÛH-8@â€ØD„@¡lbAB²iÀø:«l^¹•@åÃÓ}>hÀÇRjK–@Øê-¦ºÈÀFƒyp›)@–‘«éÛHÀÞ+Pnu·÷?Ê-´s"À8švWþ?§"˜‡&ÀCFÌÊ› ø?˜­Ð[PÀá uTí±6ÀNÊÓCk É@ìÄNìXMÁµ;±³¿à‡²†XMAóï­*"’0À)68&àü?ÁÁ¸ù(Àº\CuÝóï@(vÊñŠPÀÇãÙ.lp@õsú¸ö¤ÀÍ,ì¦ÖG@ª[J0š#ÀêzÛhHM@þî&ñfÀŒ>ÈÄÆt@óI| S]@ï6.ðÀ¹½;±»XMÁóï­*"’0À§ß>0XMAk¬Æ` §"À7n’ÙËÌ=@&õcF¡T@ów?J›&Àÿ©'‚'@U|¤ËWSÀ^A/áâÛ@ýƒÙò‰Žh@]‹Hޏ°À/÷K¾˜)z@{Šý1uM”@2KN,wÀ+c@[U§b@+Û˜“6Àì¯Ë:l@NUyú ;DÀ'z¿C¨À;)  ¾TÀ¢»;ù @Ñ4°/Ô @Kêi¼Ÿ@¡Ó/Èÿœ)@7¯²€@â³I>h%ý¿ów?J›&@&õcF¡T@U|¤ËWS@ÿ©'‚'@ýƒÙò‰Žh@Bc×%5*•@Ñmšðm„ÀѦЯ­À2KN,w@{Šý1uM”@+Û˜“6@+c@[U§b@OUyú ;D@ë¯Ë:l@Átm:iŒLÀGâ€õ¶¦À×4°/Ô À¢»;ù @»Ó/Èÿœ)ÀKêi¼Ÿ@(´I>h%ý?7¯²€@>CZº½“'@àr3ÁƒÖQÀƒ_Ž|©Ï@ËIi®í³@]5³¨@®cGäaºa@Œ5 ±Ù™@¥ßÿcõ?<’ òžš—ÀygœÏ"@%‚U “@Àa)y31À¥Ï ÍÀªs¶z¹Bd@]‹Hޏ°ÀÑmšðm„Àç‡þ ä€ó@PljYlzÀ¾|ô[fàÀÈ"soA©@3æJoöV¦Àäú88‚ÔV@€yÓ¶À ðBçt¡{@„g•¢ÿˆk@àAó¹…Q@€9Šç ÉÙÀì ®Œð·À­3ÂW-OÇÀÚcÄž À¦åy'žRÀÂZ ñ+|@ ægÅs|´@¯"¹<’à™@¯cGäaºaÀ]5³¨@ʤßÿcõ¿Œ5 ±Ù™@þ ñalJ@»ŠI¥K’šÀî%š„1Àƒ¯¬è¾åX@«ùÞ¦ûœw@ñÐŽ¡¸ ÐÀ/÷K¾˜)z@ѦЯ­ÀPljYlzÀG«ªëD|ô@¾£ùwW©À¿9•¥ÈfàÀv 9‘Ó5X@dÑu«À‚~l“g‘@jIgVÂÀ¹½¶†œwQ@xñ\N$;6@:ºâ›ÞϪÀª/ŒŒÉÀ <ý-)GÀןœ4‡¾À–&3 'hÀqÄTÄ@GïÍœS8@Ä^ôdl–@ZÁêìB@ŸvU§¼œ’@¶ä³Ìš6@|]Ò$ª@zöŠÔçî@LNÇHÐÀGïÍœS8ÀqÄTÄ@YÁêìBÀÅ^ôdl–@µä³Ìš6À›˜¼O†@{öŠÔçîÀ|]Ò$ª@Z¼ÔB§`À©>Á‚l½(@væD^À¦˜ºOµ?,\÷Gå±o@x ›V*“(Àt€©î(@Ïtƒ©eÀç¦Y[Ê߯¿|²8TR^Àx ›V*“(ÀèFÇòr@§KRJèE—À#þ³+ÑvÀÄ8_×ðiÀhÞ_Y„@…–™è¹3•@…}Eqy@êðk6%Au@ÐWfü§…À:äýÜd…wÀ^rKë­awÀnâj„@š)aa‡ ÀýÎÈåNz@0ŽÅ4_@ÐWfü§…À9D\Àóô¡@259°ƒ´À(á ý@P@s|ª^˜@ï¶Ê”2î@X¼(á P@˜Ì>á*Ëì?=—ŠÔ¯z@¾º×¿ˆC@+c@[U§b@+Û˜“6@3æJoöV¦Àv 9‘Ó5X@kgŒ¼¢$@‘_E ä-aÀ,±ÒçmÆ®@¿ñÃt­tdÀALt0f@h^óÅ0ÀÅ”·Ÿw@O~†²æM@4yÑ6*x@Ÿ~Ê YL@nÔ`"'T@?9H#^9¶Àô¶Ê”2îÀs|ª^˜@¨Ì>á*Ëì¿Y¼(á P@ƺ׿ˆCÀ=—ŠÔ¯z@+Û˜“6À+c@[U§b@äú88‚ÔV@dÑu«À’_E ä-a@kgŒ¼¢$@¿ñÃt­tdÀ·¹F>³@h^óÅ0@ALt0f@O~†²æMÀÅ”·Ÿw@ ~Ê YLÀ4yÑ6*x@ŽÆØ_ÿZaÀV~ñýê8c@–Ï?ßµC'@tíäÄžNÀ ïÂ|ÖGÀÓŸ£çúS@Žòò1'c@|­GU tZÀK–¼w=5@šÌßqo¤PÀ10¥4Pgc@_í¼R‚ÀUj‘¬ÙÚBÀ¨ {üÐh@)23Î)L@‹ θrÀ|­GU tZÀ„b¿Ó|@Ï–GW QÀì×ÊÖj@‡]ÀqµÕÀ*R;9Ðn•À8À6‰e@ކŒ#["†À!Ø£ÍR@Ã.¡ÛyÀ¯ªi”Ð@•WãŒpµ@V¿Õûˆ¶AÈgE²ÊßœÀEª:ÃÍÀ«2CÆÙ‰ž@ùR«½55R@Á©ws¥˜{Àl‹[Ðö™@«ýÈf@P õb@o«†ÈÂMˆÀ™ÏÅýqÒÀwj›ÐͶŸ@…WªÀ¢×ÓÀE½/ó£@#çæI×jL@¡3ö“[xÀÈ !*ÃÀ–¨ÎõŽ}}@ |½VlqÖÀ;ÿ–ª^hœ@cQ:SoµêÀ§:×À_óÀÀôy!AQ—¤@~G|nƒÀ×Àb„ìÏŸ€À+ÏÁy¡@)çýzÀŸaÀõç÷¡@àê{†H…´@ÖãO³`\›@ÈgE²ÊßœÀÃAŒ@A ŸïÀ†žÀÐ)e5ô6ÍÀÞ¡“î&¾zÀªàË€D¤@¬ýÈfÀl‹[Ðö™@_^t•ŠÀ¹°bžq±@ÄÄyy„ø„@"á5 ÚÀ%hý€À]e[’(äÛÀ剿»~À3âPÃéWª@ XŠ÷ÎäQ@iw;6âÅÀH:ûäåƒ@2}ô.éÜÀUºÐêáŸÀUôj)ãÀíåƒ$h@Nž NÛ™@hæk4ö¡@Eª:ÃÍÀ ŸïÀ†žÀ[H°±NÌ@žøEËmv…@c.!Î9š@Kzdº@@@°ÿËRÉÀ¨þ'àõÀLã‡IOí™@BØÐo@€ÂcZÙ˜@›AZ®c@–sßù"ðŠ@Å_}GX;b@°! ÌfY›@¥êŲ±5q@ÑàiN~À¤@ÞùFÀ·ëˆ@!—‚Ÿe8@hæk4ö¡ÀNž NÛ™@«2CÆÙ‰ž@Ð)e5ô6ÍÀžøEËmv…@f£O^}Á@Tz¼z€j@§"ßãç@æ¡âœoŒÀIí?F±ÀBØÐoÀKã‡IOí™@›AZ®cÀ€ÂcZÙ˜@Å_}GX;bÀ–sßù"ðŠ@¥êŲ±5qÀ°! ÌfY›@ÞùFÀ·ëˆÀÑàiN~À¤@ Ý0aäQÀuó/§+6ÀµÌCŸt~@-«£6@mÕ?SûyÀ@Üa@TLüX„݉Àô ‰ºo×gÀJ{»±½‘@x$A= Aßè• )€j@èU17ÕsP@w'bì@_Uí)PÀA.*Ôp@w—ÂîœG@ ÔÑ´ñk@QUÆ®„ùE@C…›u6h@ïâ°vpF@b'KÜw2ñÀaTϺ햄Àø$ŽùHÙTÀP\n@ËWYЈÀØÎ Cœ²@vBWf'‘@ c:x‡¹À‚·8ñ@ÝPŸaº P@žòôo¥4@_Uí)PÀc3Zž"@5”Ú%U@¯Y©¾ -@ŠebQúP@\¶ËSy“+@Î8ÒÏüaN@Ãüž >,@Ã)R7M‹@Œ±WuÒ JÀ‚Þͯ¿>@ žTâ³XÀIOQs·À8Ûs%ùˆ@õÁ %íƒ@£ Œ?ZfÀ‹ö¿òôƒ@‹+îïzÑfÀ”X«©?ÉMÀwI‡Êäg@苾¥õv@k¨]="GÀ_M¤Ó<’@é×´»q„cÀžm6ò˜øŠ@®[9¸/–iÀÒÂ*È•@~Û(ûö?ÀŒ±WuÒ J@Ã)R7M‹@ÉT"¼¥XÀ ^!ƒÍs@¶Ä>yx½€ÀÐ'U#“j¸À£ Œ?Zf@öÁ %íƒ@‹+îïzÑf@‹ö¿òôƒ@Cå5 ßg@|"¼Ó2&ƒÀk¨]="G@苾¥õv@ê×´»q„c@`M¤Ó<’@®[9¸/–iÀ€x9E˜Ó–@‡Û(ûö?@ÓÂ*È•@” 2ÐK\@ì‰æ¬‘xZ@h\4$@´}Zq<À7ÀjœO[ÀûXÈ/~y<@!Èñª5ŸÀV_84 giÀÙ ç€Z»|@¿4Š»ˆUX@Ì…-x·jt@`€‰åZ„@@´}Zq<À7Ö˜Fm#‚@ûXÈ/~y<@zËOƒÙógÀmûáýa}À.–$ E|ÀÙÉäXía@~©ô ^>@kã,HÂ?ÀëèT` ÛX@p'^4”ï1@ø^!è¡LÀöw'â¤+@±kuVEÀ»8ŒõZY@Œ&W ¥ØsÀø^!è¡LÀì¶q©úf@~ˆïFÀhÇ]"Ù`@†`‡c@)·FÎÚ÷yÀF‹¸S@­xÔ²gÓnÀ÷x=µÆžhÀoþ‡'ȃ@gê&07@ÀÂbãOF=@)·FÎÚ÷yÀò[ÑŽs”@T]—¢ƒ£nÀœðî*¶ˆ@£d®aRƒ@ÉO+F ŸÀjä¬Ì&E@ÉÔ:š?\À¯p)領À8÷|EÞ @õ¼p ‹}@¼9sµ1SÀùR«½55R@Þ¡“î&¾zÀÔ’<ï¾@YÂÅÌv¬À™º& /§ÀТ§î¡@ ¥¢ƒl¡@ZÆ'5—ÆÀ½9sµ1S@ö¼p ‹}@Á©ws¥˜{ÀªàË€D¤@YÂÅÌv¬À‚›XxKÕ@sñÌÓ ×›@JöÏøÅüÉÀÉx“2«àÀ mäGFË•Àƒ³JbÖÀ‚OÖÁ Ÿ@û ãŽ'ËÇÀ{ðå4uÀØ’^ÄÀºKëqÚn@£ 0Ý÷@kÌ™4%xÀè]7L)@/(Ô¯ÕeÀ~D<ƒ §ÑÀëyPLŠs@ò»G]ÚR•@U>´9ŽàÀr> –½ŸÀuB“Å:ÖÀ*0pÄ@¬È l‹ÊÀKߟ3ÌwÀေCdÂÀkÌ™4%xÀ™õƒ}Jø@–ÿ÷p?!EÀ1­JMâš@!EqÝ£áx@ƒCmm•ÕÀå)Ì–ñA@ry„f´ƒlÀž²Üò†@Qy ½voÀ²>Nø|öFÀJƒŠ$)b@R‘éx…¢†ÀŸb®‘cçt@¨éKkÀ­©"›;ç•@Qy ½voÀå¥õeîâ›@WLõ¸%*b@%±dí;»|À)÷‚*qnt@T<_áL¥ÀŒ`á­f•À4]FÄR=@K–¼w=5@Ï–GW QÀˆsq—@é˜øéÈS@:0¨@dÀë¡rÍ„$À5Ëy±/@ĹrymÀ™Ú%/\@ÏÓþãÀFÏ*¤çé@¥pú;*Àâ²Þ =@S¿‚ ±–ÀšÌßqo¤PÀì×ÊÖj@é˜øéÈS@owºdý“@Ô>9ëó$À’Û¥hF`À-š¡kÀ改X’5@¼KƒšÀíÂxwÍ59@ãQ3+œ+ÀM,LPDDE@ÀüÑùvì·Á¸œ|Ã>+ä@oïRÂAÇÚ&Zð—ò@ jåW1ù@}Ï© à@·Í­ò€ò·AXìvÇ•ˆ@V.u2…°ÁÝ¡`á%öÀ\‰}TaÁÝñ"Ó˜éÜÀär´]8N@ǾgŽ2@L$nP÷Á6˜s]ùÚåÀØÒ¡!ô@g¯†ÍáË@’!ü ÁÝ{?lCò§ÁXìvÇ•ˆ@—µúZb ¨A sr….Øÿ@¡wš¾ !Á²5ñ “éÜ@Žök›OaÁ ;€U˜‹2@B|ï¶@ ¥K!Æå@æM­¡aïÁÔuGŒíAƪ6…( Á¼ëCDD¾m@yk‘€`@†ùYI[f@¬ªšÏ.tL@V.u2…°Á sr….Øÿ@L÷z[ ”Ã@›ß ‹@ù'Α2@=ñ¹ÚΔRÀNÚñÁžÎŠ@«ŽÏ!]K@æ’¯Öa@ÂÀÆ |¼š¶…@õ5;?m2Àe,z?»ÿ@òyáõ7¾ÐÀqY8`{s@îm²Á[/鿜2Dc•¾À{Ù<þ\@qþöv“¦À®R϶‰v@uÆêƒ>$N@>âAo'¨¢À&žlÎýÀH³u q¥Ñ@¶W“ªFc@Yju¶œ À¼ídˆq\@–|IjÜ­à?÷eÆ¢â?¡å!~«ÝÀéOQÕAjñ?ûÚ6K&ÀÖØ"—yó?áÄði*#À&nÆ’&eB@iV2¥lAñÀIN’Î…?žÀ†HqÉ:©?¥Q5m›Kb@ë¢f«iE¡?nœæjBl@…pJR|¬?ÜØ_’Ïtg@SU\Fò@õ5;?m2@Æ |¼š¶…@òyáõ7¾ÐÀBElÎAým²Á[/é?pY8`{s@{Ù<þ\@Q»ÊÄCËÍÀ®R϶‰v@xõáø¶ÀÚÃŒ§ØP{ÀµÓ¼cèÐ@§¾=`š³Ï@º@”\_<ÁWju¶œ @¶W“ªFc@Ž|Ijܭ࿼ídˆq\@\&UKUeÀ“¡üŽG@Ñ1×0!À`{Ç›V@¸æ+|¼È"Àl¹á£9ëR@¸AÐ{³8µ@S™ñÜ%T@ê:u:MÖª@"’ówÀ@ت÷ñ@ßÛ:+3‰t@./Í>¨óÙÀy…@éÁÈ ÀÍ,îΦèÞ@œ£„=ß´@®¬¼kèKÍÀÐyUÛD²ÀS™ñÜ%TÀ·AÐ{³8µ@äcÑɺ@a‹w©ât@²Ð¥7’t@ Ï$WnšY@²‘ xP ŸÀ‚Ô³ØöÔÀœ£„=ß´@@EÕ¸ Ï@ãŒkDC²Àðy ,ùÅ–ÀIiuÕÀëÞ;ÑÝT@õ¾^–µ+@‡·t 8ª…Àz[™Ï48×@z I = @²Ã…ßf5Àñ_îX=s2@³UoâZÀ 2VxUý?‡†_i†±ÀÕÏ¡GJj @ß[¹µ·6À¼€Pü°A@¼êÒtké%@ô虩:“@6äx=¶]@¸l<>e§@ä¦þàe2À,QöžŒ@§ÂDX\@îé®ìB@Y8–MÌr"À§R©šž ™À““!ùÖgÀéx)ëVvÿ@N_Ó_7&,@´g㸱°@O|¸¾`@Ù sÊñxÁ7 ¸Í¦3À_ê›r`@bª¾õ£[ÀV¯b/¨"„@ôô†½Ì%À[zÚUÓ¾A@†ê¡ß6À‹äo¹ç…`@•—jn°Ý@p˜œ7ý?6äx=¶]@§‘Ìá€@Û¾äøv2À¹[@Û>µ6iBc@¿ŽÕ.l3@F4ˆýk1À#À¡[¢K@dƒMÏNlÀþÐrÀ¬¯„?„ž@ÖˆËRÃÀS@ò ™«ƒô¤@Çšôçè[ÀúíyýqqÀ?¾3)…WF@ýzÏX1¸ÀŒ¹™¯í»TÀâµÇ.œ´œ@¾§Ç­Æ¼Q@ÙˆËRÃÀSÀ¬¯„?„ž@Çšôçè[ÀEká—ã©@Øsqóã8F@9Ü»5–€À8Dºýÿ$k@ä18ÙZ¬¹À¼§Ç­Æ¼QÀâµÇ.œ´œ@Vçê†ã`À[yš2›ìÀ¿úíyýqqÀØsqóã8F@ZœØ¬FÅz@°õxt¢6@<ñ”Ä’ 9À0 çÔ {k@j•ÎÉB@2;™9¾Ìv@<Ó1…Ï‘2À)ò +f@TÑZ~Ý$ÀïBåÔkÙ@*Ùš¦!]@ 6õžqÐÀIxoöööÀköp1ÖÀ#%é‰ß±gÀ\­­ !Ö@¨Œ……O@‡ÁNxÁõ?.´.ÁbçÏ@›ïûGâø@dÓºð²»9@¤ä¢ÍF}@OÔÙ©O³:@£¬ßn»·~@'î¶0ÀE|¥tŸ‘ÀÈi‚©³A@×Üäç¹Ý–@]£[^ @Á =zýR@ÒCéRúK&@LÝJh«>PÀøcßék°Àl"µ͇wÀWBP|¡3”@Ø"öÕÄ|^Àç¸^l–ñ?ü- ¢ ÀµXÚÕ!Æ@…ÇÀ2©*À¸l<>e§@Û¾äøv2À¯/+†‚Û@Ÿ¨!®@5¦ž ?ü€@ÊëÔ6¿4ZÀ)±_x@FúÚ!‹+€ÀÐêë§© ÀØ"öÕÄ|^@WBP|¡3”@íüæÝ?ÀÂ÷[à^5@ÃЇ*ÀV’ÜßMåS@ä¦þàe2À¹[@Ÿ¨!®@G< n›FÈ@ªíÌÿvÀ­YØú{•@-º—Ò¾â¿)±_Ÿo&—@»êæPïô\À&©×Àèc?Û{À´4-Ù*Î@Æü#©‡`@‡#1WØz”@$¿òD;â@ɨ{ná$Àn`5=V±¢@öÜÖq@ójÿÊæ«@)Ÿ[EQÀ[¬àBÇÀpeêÇÏ €ÀY´íµY½ÀãÁ0Õ¯—@ÑšLÌU Í@»êæPïô\@eZ>Ÿo&—@h}[kÌoÀAÊòBO‡ÓÀÊü#©‡`Àµ4-Ù*Î@¿òD;âÀ†#1WØz”@è0€·ŒlßÀ0ôÒÝìÁ@†w-›'Û@‰\êzÂÀÉB8Ó—Ñ·@œ,MÙщ@åOæT›ˆ@ú8&$®l À¦&µ‚ú‡@&MИœópÀTìö<¡©ÀSü’­Þœœ@D§½XŠ ÄÀVW¬ÞÀ‰\êzÂ@†w-›'Û@œ,MÙщ@,+³ö`\¯@ÝðËÓ&YÀ<Æl‰Æº@&MИœóp@¦&µ‚ú‡@Öjô˜ªP‡@w¼„TvÀÀÒ¤› ãh@ž»´HBª’ÀØô“·ÉƒÀUS ¥@Hj ‘Ýà@Ù¿èZ¿–˜Àw&/M§àÀ Ÿ2»“÷¿_ …º†ÀÊãLê°@D£^Îî¡@Š å¹K™ÃÀÙ¿èZ¿–˜À3orÜ€pã@øA=ÎϱI§àÀãßp‘A SÅnû„@n ’yØÁÚᨛ“Àý+_H›¥Àa…^“>XpÀÈyaG1€”@ M× —Uk@e…oÂÇ¿¹@hèM•ånr@W©ää,t @.vÉ·´t@kÿæÈð@èou2s‡\@'ç26Ã+ðÀ窣a®À:}n›pÀaÕcÙü“ÀÈÈ9dÎk@0Û{íÞ_B@hèM•ånr@üèÙíû'³@° ˜:_v@X.}ßK'L@¼¼u‡„@jÛô’}r@=ÎL‹¹ZÁÀFÏJ¨òá¦À£Hàm+À@“ P¤@(JŒžhÒ1Às\Û  À6³vLüFj@§eV‹ùªW@ŒÊv°6¦Àe·ˆ\,JÀ“ P¤@Ü–%¹)kŠ@s\Û  ÀWX¹ü¿—$£¥ýÀoù³EYÀúŽV;WŠ@²shÀ;èÀОFV…˜À»µ—¬£Z@yŒŽ®uý@v±,®o©\@™3SËA@A.*Ôp@5”Ú%U@&zF¸JÀ‹@“ÚÊÍdÀIÉ!\ÝC]@³õý^MÄ7@ˆëÅ/Z@tÃãNW8@ª;T£Ò´ÔÀ‰"ÉŒƒ"ÀÄshÀ;è@úŽV;WŠ@$Eý6ÿX@µ ë.Š ÀMÖÔünªÔ@ä3M"(4@ºý ÷@w—ÂîœG@¯Y©¾ -@“ÚÊÍdÀnŸ'Ä_œ@Cá·|‡4@LXCµ¬@ÚpÑ^2@bh-W7@ŒÞŸ9ï0ÐÀúë½µQEã@ »†÷áÂÀaÒ ë6HÃ@Qß„#B±@äê·yj ÌÀu¸b“PIY@pÎÓ¦‚À‰Êˆ#8Õ@èHËççÉàÀ€z`‚×wâ@É9ú &´õÀ+èUùÝ›¸@ØÛŒã#ªæÀT hŸÐÁÀ‰4)ŽóÜ@=¢y©äPƒÀ-­·}¬@èHËççÉàÀ ¿½Ã•èø@QsK–ÌÜÀ€öÁ°Å«ÀÂDÞÙü—@íŸß¬7¨B@Ú«¬ï;˜@¿ŽuÕŽ:@)Há. â@lË¡A` ©Àù.4)Õ¼àÀkæÉLÂ@_€Ä>Ÿ@~%©A÷•u@P õb@_^t•ŠÀtseÀ ð@ÆùM˜Â¶ÀLpÉ5¡@>‹Éë-ot@(È…ÿBD¬@!|ïÔ7އ@BÖäRAµ@ ±XKð¢Àf$cìNÒÀÔÊÀ3³@Ϙ•÷ ±ÀàNÎNÙ·Àϲ nl@üÙgó@Eœ¿4иl@7–ªày@>ìQF0ÓšÀ‘X91ÇÂ@BâuÄÉ[À@7š ]½ïÀÐЭžƒr@6ž|øJ•I@o«†ÈÂMˆÀ¹°bžq±@ÆùM˜Â¶À·™†‘Îõ@‚U*wðŽq@\úµ’ê7H@€Ý³'À€@Ñ*qÖìê[@Æþ:ŒŽÀ¸ô«c¢‰Ã@CAÔw®¬@­ûjÂäÀS‰ÖœÀŸ.~¼–@a»¾{w÷r@—IÃ@ÊÒV@<ÀÄUé‘ÀJ3¬`Àž’­’vp@0ÃQȱŠÀéeþ½¹Õ¢@(õÀ}£;€À{x†î½,–@·ŠHõ\·ÀÀäÔðs'W@äE ¶ÁÜ;@%Ø5¼aÀçüsÛÜ-‡À["Y.»ŠÀEÔóŽŽ¥@(õÀ}£;€Àþ…R¬º®@ Vdt:ŠóÀîØJOImØÀ·á€ÙÛÒÀúÈû·FŒ¶ÀЯ8îÜ"ø@¯&C¾w±Ý@"©0æS~@Kœ½2¸w@8²©Óü¾ØÀï÷¼rqï¾ÀO±ù‡u´ÀKy-AL´À¯&C¾w±Ý@v|OËZÉ@Dî׉se@bU.øÆ`@tÔfŽN˜ÓÀ”ÓQúø§À(ù(Ÿ'*–@®úæB;=A@ýÃprd–@ÂÈäÒ!Š8@§ÐSPƒ @îO§s@@Ú³^CÞœ@?hDòs@‘£§6§„˜À‰ðøœìQÀLpÉ5¡@‚U*wðŽq@I:Æ¿‡²@Ue'ÚÖ…@~fVÓPª@¾ªrîÃ…@‹¦`¤…œ±@-ç¥Ç ‰@ F::•œt@ëm1óhÿ?Í—@ «ÀUJ¾º‡€À½í¼k’n@[ÕËA7Ç@È]¤KÒân@»âþAì@³¾gÇÆv@le]ºqLJ@ï´»ès@÷†U5‚K@ÖkDÁQ…PÀ;(Û[ŽZ’À>‹Éë-ot@\úµ’ê7H@Ue'ÚÖ…@T„Õ·u±@À‘@‚@rU•k^@¯í¦Jˆ@fÔöá&Fa@æm1óhÿ¿ F::•œt@Ò³wµg@%ýo¤=@ÇÅ›”1‹¤À*œñ]á¡wÀFADz¯Q@IzOyÕÓyÀ¼ˆ¦  ¥ÀÕü;)~Z’@ÖkƒŽ>ð³@& Ôz!kÀ‰kë³6}@@ì–wyhÀC€¤ð¬D@§$Y@Ø öPyÀI²ëãŒÀ~€ÊÀµrÀ¬;5oŽS›@"®üQñÙ@»{=J½À& Ôz!kÀ&EÅÌe϶@~§¥à1raÀÜ®‡æŸä‰@ꂈ4ã4<ÀëÄ­Ž¥ùV@‡~§‘~ÀºV Ÿâ QÀ®ÇÚßÔ@¦³…|s5À¬j¦¯ûV@·„ïs¸rÀÃýîÆëžQÀ®–0 *qÀ¦³…|s5À]p–‘ðì@øÌ²i”åbÀü O)°=8@h?o¢UÀˆVÿ0 +@ö1oóê.c@xÓÚãBÀü O)°=8@³&Bæ×rÀèûmü +@Ð)Ç^õöSÀxÓÚãBÀÉÆ–+w@Hð—'p5ì@úôi“À8È|¾³Û€@ „(9ŸÜ”@va…°Á–xÀ@Út@?@2ç;é>gÀX›µ„º¹C@T‡jÈ @ók‡Íµ ìÀéAÊkŸ‚_Àß!cê]@8½pb9@½ºÔ+¿Œ€@bJ=aÈG]@ÎþÜ×ÌÆF@ü]Òl.@-]:=ÞnÒ@iÉ“ðñA|@ÌzD")¦Àva…°Á–xÀ¬{ÛV£@3ÍÊ«jçÔ {k@]ñ¹ÚΔR@¤þ1–à…@"ì67bÀØVN7Äõ@@ž,‚RzýZÀ1Ìú´Lq@ÈY{ôÕB@d¢mlmx\@”yÙ{ÐvÀZX;œ˜Ý@ªþå-#Ar ;÷û˜@Êñ<º'– ÁZ&FÍì)wÀ·Œö AAž[T‰`À#CãP'æˆ@!Ÿ»ÂÞDKÀ 2s€We@Ýñ"Ó˜éÜÀŽök›OaÁ=ñ¹ÚΔRÀù'Α2@"ì67bÀ™)î÷@¨UKÿ~›iÀ½–Ѐ3`„@ÇY{ôÕBÀ0Ìú´Lq@ÂÑ—€H~…À'§II­ @Êî¶3¡ÄÒ@´!…Ǥï·@¬Š{L¦‘@¹6¨,‘v@ 'Cvi Áôµ)©›m²À—øˆ}(A'|ñKÊV§À¤Qñ«tÀU-ü9“@'U®…7¸@úö£âž@žybÊQnv@K¬¸V£®\@qî± ²ÀÒéí ËÁ'|ñKÊV§ÀrÕ D.Aý$±¶’@DN*˵“´À^Øn[`e@>Wï²ØÀê:—„ºé¥@LhsµA‡y@™ÏÅýqÒÀÄÄyy„ø„@Lã‡IOí™@BØÐoÀ™º& /§ÀsñÌÓ ×›@ÒâÛ¶@ã Z½Šc¤ÀAw©»k¤@¯™ç±aÀÕLœET_–@h$ÈÀ•6@3=£Jk¬¦@éöCú<&@d<§…’±@/Â[)»ƒ@÷'^I3À`wå6"¨@MhsµA‡yÀê:—„ºé¥@wj›ÐͶŸ@"á5 ÚÀBØÐo@Kã‡IOí™@Т§î¡@JöÏøÅüÉÀã Z½Šc¤À‘ÖjÔ@¯™ç±a@Aw©»k¤@k$ÈÀ•6ÀÕLœET_–@ÛöCú<&À2=£Jk¬¦@.Â[)»ƒÀd<§…’±@½‡Žl(n@úEÝr}Òš¿ /Ö ¬lŠÀ\ѯ¯WËb@‘²#'–Àc- …¨{@s"Þfj@XÅåü5bP@ibþìKœ@£PÄ8 }†À =Ýr}Òš?½‡Žl(n@‹Züel¨b@N'›'”ÀiÅfäo@§Ø'±Àˆ¦À‹Œ·@ TP@‚lÚC4@£PÄ8 }†À§ƒ%ñ„®@_ÄÇ1Ö÷À‡ýæÔoÐLÀ:æcÈ2¯÷@:Ì]Oþ W@aªG^•œ<@ ÔÑ´ñk@ŠebQúP@IÉ!\ÝC]@Cá·|‡4@¡|\"Ö‹h@Tæ7y­|€À†sŠÐ•TÀ´f=V |@‘Æz²b U@|sëb¶‘3@Òšä@ÓÀ;ø¦`ÀbÇ‚P <Ó@Yiqš²¶2@Îâ]s<@QUÆ®„ùE@\¶ËSy“+@³õý^MÄ7@LXCµ¬@Tæ7y­|€À¨øîB4ݦ@ÃGÜ\ƒ{@°•òuJ¾¢Àš•Õ1@)¯ÌüÈ@DŸ<6ËÀyK?g°—³À’Çò?2áÀW ôdàØ®À´ÒDF%¥@2àÄ4rP@êxf:»\¥@nZŒX!iG@)ƒ8®¯@›6Hz0‚@E´vwHŠ«@Ü…ŽËƒ@(È…ÿBD¬@€Ý³'À€@~fVÓPª@À‘@‚@uïËÔ@yK?g°—³@yêÍÀ@bΛo#å—@yK?g°—³À)êž—6œÀ¤K>·À‚ïÿ6½À@Z9eïž@רui+@ºª°FÍ@öttF‚#@÷/÷fAŠ@[xWËP^@:–c<ó†@ĉíý¶_@!|ïÔ7އ@Ñ*qÖìê[@¾ªrîÃ…@rU•k^@yK?g°—³@â4nœ†À@ڕʆ…œ@Q¬‡és@-.ꫤ@öÓé'™€@…WªÀ¢×ÓÀ%hý€À€ÂcZÙ˜@›AZ®cÀAw©»k¤@¯™ç±a@Ò$VîÍÃ@§;$˜ Àu §3F ³À#ä—Ì @ÑΆÃö?•@¯ÁcåyW@GEÄö›Œ¥@r¸jZˆÌc@3Ÿ/.”°@3j^ªë‰@÷Óé'™€À..ꫤ@E½/ó£@]e[’(äÛÀ›AZ®c@€ÂcZÙ˜@¯™ç±aÀAw©»k¤@§;$˜ ÀM2YøwaÙ@ã7(.á7’@nÙ"Æ©ÖÈÀ¯ÁcåyWÀÑΆÃö?•@r¸jZˆÌcÀGEÄö›Œ¥@4j^ªë‰À4Ÿ/.”°@?TÎ-· 0@Ë–‹¿SÀ#çæI×jL@剿»~Àè]7L)@–ÿ÷p?!EÀ†sŠÐ•TÀÃGÜ\ƒ{@u §3F ³Àã7(.á7’@}üï4Û¬·@µõ•sСÀØQºßi’ÀEðB)ŸQ@ü…Ï” ÚUÀL^˜/.èz@¡3ö“[xÀ3âPÃéWª@/(Ô¯ÕeÀ1­JMâš@´f=V |@°•òuJ¾¢À#ä—Ì @nÙ"Æ©ÖÈÀµõ•sСÀŠ—,Ž«®Ê@Xôlcoi@»ùùo1æ®À0‚¢Kóµ@Sâ–\¶g@B‘l&s­@r´î7#(vÀn¶ú·‡<Ÿ@<žÑQ©]@—ãœ.P]˜@Ý­´5¯NÀ~D<ƒ §ÑÀ!EqÝ£áx@ØQºßi’ÀXôlcoi@¯œND•¥¸@tñz³¥­~ÀUâ–\¶gÀ0‚¢Kóµ@r´î7#(v@B‘l&s­@:žÑQ©]Àm¶ú·‡<Ÿ@Þ­´5¯N@˜ãœ.P]˜@ëyPLŠs@ƒCmm•ÕÀEðB)ŸQ@»ùùo1æ®Àtñz³¥­~ÀLš*ÇrÉ@Û †òBõüÀˆˆ×ÛßO³À€îâ|ˆlÕ@Ý ›®@ÎÐ ø…\X@ôû¬ã@â€ØD„@Y •ÛH-8@ì¯Ë:l@OUyú ;D@€yÓ¶À‚~l“g‘@ã¾r÷˜@£òºÆdÀALt0f@h^óÅ0@íæ›CpxA#gjRŒ’@Ê2PEÓÞ@¶ÉøZ@«Ú7oéÀlDC›mÿ“À¶ÊÓ¤ë:¦ÀÒîþ3Å÷ÀðŒmÎÞ>®@õö»%Ÿš…@öû¬ãÀÍÐ ø…\X@Z •ÛH-8Àâ€ØD„@NUyú ;DÀë¯Ë:l@ ðBçt¡{@jIgVÂÀ£òºÆd@ã¾r÷˜@h^óÅ0ÀALt0f@#gjRŒ’@«ŸŽÑ\A·ÉøZÀË2PEÓÞ@Éìȶ¢¤À$,òTOãÀIb&/e8ZÀ#h¥ûä?À¢ûT~±cq@»LN1ñ‹Q@6M»˜L†À"rµ½^UÀ}¡“ó±åv@òRf2þV@91)Å«=@‰ yT@ßD_ö°€1@Í/+ìì?ÀÅ'ØuÉj#À»LN1ñ‹Q@–5‰Q@í­úºs_iÀV{JqRÀφÿ.<í[@(‡Ë <@¾ÂQú"@Œ–F¶Cø8@Ó©¿«ãX@t÷¼Zt@wííØÐÈÑ¿Ÿžwy^2@ó†ŸÉ>øyÀ̧Ov;W@™©üj'ÊrÀ”Ù,NÇÈÀí±§@w@O´À¥ÂÓªÀY¹ˆ8m‡À®¬¼kèKÍÀãŒkDC²À \º &—Ý@yš]‰¶@ííØÐÈÑ?t÷¼Zt@ ÂYŽmðLÀªP†PaÍ”@¼6l¿›rÀÑò Ž@ûþ©8Î,@öå ÃYÊÀ; ŠÌ]}‡Àq¡1¦“ÀÐyUÛD²Àðy ,ùÅ–Àyš]‰¶@†SnÚH‰Ê@ê)?¥ÀÌDh[aø@ÙŠìb P@#E¾t0ZÀl©fi8e@À Ð—$µ÷À€Ýgñ"N@iÓìý¯vÀßBwæ8“lÀñ;zECÀ§B)«ÑØ8@Ûšm9rSÀØVN7Äõ@@¨UKÿ~›iÀÒÿæ ‘´¡@Æ«'“Rvw@@“9õ•s@.ÁÝjªcŽÀäy?ó®˜@•Ñ|M‰?ÁE¾t0Z@ÙŠìb P@ÊpT-VË€À¢a'ä5ÝAFÆÖÂúgÀ¿/´i ’@—å Õ(:FÀÐm·)F[À9ðŽþKÅSÀŒ ¸âUòn@ž,‚RzýZÀ½–Ѐ3`„@Æ«'“Rvw@üsôéJ—’@÷hƒ’*ÀxœU‰E.¨@<ñ”Ä’ 9ÀêM£Y ´¡¿é{.Âë+U@5H‚LÖ«2@3"އMÀ5H‚LÖ«2À0 <Ü•~§„9À5H‚LÖ«2@Ò¨~0ÙC@@5H‚LÖ«2Àž¡q~œÀÕWÊšgß–@ÝÑ™8òÅg@Áµ›Ÿ5Àž” ™s's@È !*ÃÀ XŠ÷ÎäQ@–sßù"ðŠ@Å_}GX;bÀÕLœET_–@k$ÈÀ•6ÀÑΆÃö?•@¯ÁcåyWÀFsœ‘ þœ@ãÞ`MçõÀÈãï´i—@•Ý9Í1ÀÔ›`'W¢@GÒï‰fKr@ÛÑ™8òÅgÀÕWÊšgß–@ž” ™s's@w}…ËÖ›À–¨ÎõŽ}}@iw;6âÅÀÅ_}GX;b@–sßù"ðŠ@h$ÈÀ•6@ÕLœET_–@¯ÁcåyW@ÑΆÃö?•@ãÞ`MçõÀ¶ºÖb‘¯@Ý9Í1@Èãï´i—@GÒï‰fKrÀÔ›`'W¢@7ÀjœO[ÀûXÈ/~y<@ gm>8˜]@‘»ZÄÑãLÀJ7½¢ÞD"À ®¸E=@ûXÈ/~y<@zËOƒÙógÀ‘»ZÄÑãLÀ+[2êq@&íX%N=@A€ÂÁyWÀ„¹0ðëDÀ;Âú±^Â{@òªXÓâ²À¥$òŸ‹@îP±ýG„@šýÆâyXÀ7‰7zkx@ƒÂ1¶\À5¦ž ?ü€@ªíÌÿvÀKCiÆh¡@2ûVS¯n‡À™”ö?HBdÀ<¿áä¡À½H+Æ3‰@îÓÀõ²±ÀšýÆâyX@îP±ýG„@‚Â1¶\@7‰7zkx@ÊëÔ6¿4ZÀ­YØú{•@2ûVS¯n‡ÀßýÕP!±@Œh^øXt‡@r<±Áðl@M>­ÿê”À“„¦m«çyÀF‹¸S@T]—¢ƒ£nÀŽ=ýk»@:Ëz“jr@¾2µÍÄn¯À:}€Ø¥&À•t5ø[@Àq$èàåóm@Ûhê°8(£À™_OÄó‘ZÀ ‚u1m@ÊmlÄR@j§ˆl zÀéuŸö'`À­xÔ²gÓnÀœðî*¶ˆ@:Ëz“jr@c„8¼@€Ù¼Ô{pe¨õ­ÀÍx‹Qóm@3Ô_§œÀ<ö,ZÀýOüò]» Àœ2Dc•¾À{Ù<þ\@¾2µÍÄn¯À€Ù¼C¼6ɬÐâ@GÔ¬ªÏCÀˆÅå_9ÚÀ€2¡b?Ø¿{Ù<þ\@Q»ÊÄCËÍÀ:}€Ø¥&ÀÔ{pe¨õ­ÀGÔ¬ªÏCÀòµ¹Bæ@z2¡b?Ø¿øðÞP˜ ÚÀâmÐé-ôQ@YëÖ€zÀê ôÏïfŒ@*áŸkpÝÀ¾ÿ%s<ßÀ=êÓ¡Ê—Àwº^-ÕÀjEã¡€°¡À¨¨âá(ó@21v ¦½@@l'8FàÀ÷Üñ›),ªÀÀïTBv½@çPh2:½á?A(Næ}{À0¥æÀÀKÑãvfnÀ§µpd¶F@·FP×ÕœTÀ!Èñª5ŸÀmûáýa}À†ª`ô¶^É@ø“DÜy(‘@~>€úô‹`@9ÍKS®|Àè uáDt¤À 283’Žc@¶c1ƒÀ€íÔ0A¦¸À¹ö§/ÑjÀiãñzVx@V_84 giÀ.–$ E|Àø“DÜy(‘@Ë6ö½@vb(È}‰ƒÀÝÝ6 î @–¢#Jüw@±ÌÊdM©À<ú h­™^@ r3_¸dC@:0¨@dÀÔ>9ëó$Àär´]8N@ ;€U˜‹2@;gÞ{ Kp@mÛÖ+ÛÁaÀ œ2ÑZÀU:m"cz%@j潡ðÂ.À±¢Éæ…H@fvNÅ dÀΠ t—÷A@Ä¿½tÙ¾B@lìR²Â'@ë¡rÍ„$À’Û¥hF`ÀǾgŽ2@B|ï¶@mÛÖ+ÛÁaÀŸÍz[§â„@/I± 9Î#@·q„ÿžaÀ±Íü*gH@ðºs9tcÀç˜a FÎB@mêé‹ß‡pÀ¡ª´¼AªæbÅåÀ5Ëy±/@-š¡kÀL$nP÷Á ¥K!Æå@NÚñÁžÎŠ@Œ«ŽÏ!]KÀ1Ìú´Lq@ÇY{ôÕBÀ œ2ÑZÀ/I± 9Î#@«€0î Š@3û H&X@,`׆M ÿ?O–«Î\ÄÀyäî @ƒ¯†V’ä$ÀªæbÅå@¡ª´¼AĹrymÀ改X’5@6˜s]ùÚåÀæM­¡aïÁ«ŽÏ!]K@NÚñÁžÎŠ@ÈY{ôÕB@0Ìú´Lq@U:m"cz%@·q„ÿžaÀ3û H&X@Cd÷èæ@`²xr¼YÀ‚‰Ý84@ÔD¬%&À•S~®A@ÔG¦cÀ™z̪ˮHÀ™Ú%/\@¼KƒšÀj潡ðÂ.À±Íü*gH@,`׆M ÿ?`²xr¼YÀ¥¶cCò„d@¬À ÍØ,;@à˜\«n9@O¼žÂ_þ(À™z̪ˮHÀísB©/ÀÏÓþãÀíÂxwÍ59@±¢Éæ…H@ðºs9tcÀO–«Î\ÄÀ‚‰Ý84@¬À ÍØ,;@Ê%¿·zÍU@¬œÂ&câ)ÀNn±ìûïC@'z¿C¨ÀÁtm:iŒLÀ„g•¢ÿˆk@¹½¶†œwQ@NÁœeÍÒ@ûQa*®Àãgnvÿ÷ÏÀÆb…b®@;)  ¾TÀGâ€õ¶¦ÀàAó¹…Q@xñ\N$;6@ûQa*®À…6l|*Ý@Iz¡À$D®@:VÞbYÚÀZIåú$¿@ž ç)§_•@ú ä*ýáÀ!V о_•ÀÓ`ÏÛuÀ£f!Xnve@)±_‰{m@ÖåU`›z•Àe±:³lÜÀüapeGRe@'dàg,ÔŸÀ)º—Ò¾â?)±_ÀBÌxÍí#†ÀPü]†‹p@3*í#ËPÀZGòÈ%É࿚WÇN¬ß>@©ª-q“@kÀ“:\A@ a;eç^@,3z×’~D@7<Ÿ×£ÔdÀ,3z×’~DÀ3*í#ËPÀË#õ¤`£@jû›JÑB@@’P¥šèÀ“:\A@¼ô¹#zÀ¯Ò‚;ÓC@Ä÷kñ)@ÑÉ]-—CÀïÏ,èhwMÀ1‘%°!'@_R Œˆ[À…ÖÅð?MŒL´ûSÀÝ‚ |b8ÀV—´@Y@s@ZGòÈ%Éà¿jû›JÑB@@¥“Là Œ@m)=ŒZÀËÃ0šŒ½‹ÀmÕ>…í™FÀtJðÆ\À¾¦–c’‘@]TÿÝ$ÀЂ3ŒoÛˆ@4Jušätn@ú5üIî§ÀšWÇN¬ß>@’P¥šèÀm)=ŒZÀ`ø8ªäª@šÕ5ãFÀcQ:µ« |ÀSˆßvÞÀP÷Óì¾ÀÌè¨dyœ@Øí©1m@šý°ÈÀ ð![™ŠªÀ ]_ýL@Öß`DXf7@np"î†×ÁÖ-(k&÷À´‹ààQAéäoFåù@u)¶tØ(d@mÝ”-€Àj¢0kxP½ÀqÌRÑ¿Àìv“v¸\@û~'t‡e@¥qËçÁ«ÀÁúûìlk–À&.`dµUs@SÂG™è@‘«3²G÷À~眑ú(ÛÀéäoFåù@OuÁðBá@"d8¾NF€Àƒmlvìš@T›|°l‰¥Àû}ƒóH|ÀaE ƒ’¤@û}ƒóH|@%ãÑ ­‘Àæ×ð%xˆ@1Ä®±tÐÀ ƒÅŽ廿u)¶tØ(d@"d8¾NF€ÀÏâ /Ñ@Ì9–Å_oÀhçºÜhÀ@ÀÇ"_ÀhçºÜh@)ØÀ\øï?âsµ8ˆ@£­-ŸvCªÀ0HÅŽ廿=›ÏPHÐÀmÝ”-€Àƒmlvìš@Ì9–Å_oÀÝ÷¾îñäÑ@‡u$·âo´@`à9açu@­qù³_@fTV e«5@]SpÆti@w† ôT1@qþöv“¦À®R϶‰v@Én¢Þ,ˆ@z¿yäs0@g¼!Å'›Àf‹óèY[ÀˆÅå_9ÚÀz2¡b?Ø¿o˜Ì^R@Ù$ý!$´@Ÿ2N‡Oê@d¿–Æ@ïSÀX˜c„­ÜÀ5z”9Ý<ƒÀiuðÕÏw@²©‹@@¼€·Ã"¿\@¥ï=®MÏ@ØÆ’)0@Gˆ'DX@("AëÚ9@6U ~š@®R϶‰v@xõáø¶Àôún·ßX@Öõvèµ@âh1ΦjÀq3·? RÀ€2¡b?Ø¿øðÞP˜ ÚÀ”¶“‚¨"@:& T+î?d¿–Æ@ïSÀnü\$»³å@:Ì6ê!ÂwÀ´¤Ä—Ä™ÇÀ ¸a/H@*ó 1]@ƒ_& #ÃÀV BT(õÞ@¥S(Ø(ßÀX˜c„­ÜÀ:Ì6ê!ÂwÀ×`–â„ã@åÇwŸ‡@md33kV"@´šfæÑ{cÀ¦Ž¦g/@Lï‹ÞzÁ NÀ@®A5z”9Ý<ƒÀ´¤Ä—Ä™ÇÀåÇwŸ‡@!Y8®Ð@ a3saÀ3—fjÐ-¢@•t5ø[@ÀÍx‹Qóm@ÖJ4Sܰ@ñƒ¯—ÉòmÀ3kZ|N¼°À`-½q$èàåóm@3Ô_§œÀñƒ¯—ÉòmÀȰOa½·@ðГġ¿ó5©1¼°ÀäÈòäǪ™Àeû€ù®˜@näw?±0@Y–b’‰èGÀÿƲ2ìG@?æñqÀÙ ‘SÁ@Ž.߃3¾¥@ò¼½øL@xƒ]C¯tÀ¤†ôYƒÿ?]R[s˜bÀʯÀ׿î @A(wÁªbÀ=|Œè&±@ŠÂÚ"‰m‰@w»õãí:4@n¹»¼)NPÀ@l'8FàÀÚÐqÄ¡²Àé´r5Õ@A½cW]˃@>X±ÿ1.–@V)]dd$ÅÀŠ,½ ¤XÀ¯ÜΊ¥q@6èÀú*¨qÀQ\!x@|š@_t`£d¡–@2º(ð#f|@³ÚÏBb«tÀƒ„(шž@Â{4¶‘ö&Àž»Š@vï{îâZ5ÀòœB‹@í‚æ§ºF†@Èà ›`@£Ž^Ý]ÀM6›Üx@÷Üñ›),ªÀÜßÔLŸŠÊÀA½cW]˃@€4EÑ@¯ñÇ{çÀèÉàÞªœÀ‹,ôÐ „¬@PÍö¾Î-V@O?*Ϭ@ñ”: :’O@ZW¨· z@¿Öu¶<¢À‘îV½Ñ£@oY{Ô…Ï@x¶µ!ñ‘²@IÄxX©‰@BÖäRAµ@Æþ:ŒŽÀ‹¦`¤…œ±@¯í¦Jˆ@yêÍÀ@ڕʆ…œ@ÀH`bÌ@±f^r#ÃÊÀ›êí¬d@C„‹›öªŠÀ„<̉>¦ÀÊ m{ƒÞÀñcÄl#G„@1©‹/@O,‚•w|„@BÃy\s&@b²µs£À¼Œ,Ën³Ê@Wmĉ3tÑ@,Kµ¢?ËõÀÈG…n)iŠ@ x"Y?b@ ±XKð¢À¸ô«c¢‰Ã@-ç¥Ç ‰@fÔöá&Fa@bΛo#å—@Q¬‡és@±f^r#ÃÊÀ‡ƒqñEö@¼’ÉaTÀh%ûO†³@9÷|<$§@Gð W£¶þ~@5T5>°F¤Àì–wyhÀÜ®‡æŸä‰@“:\A@¼ô¹#zÀ¶È*McqfÀðfÛü“@ÿÐÜQ‰’­@†–“c»4%@ªDæ%@òÀ9%kVGÕ@z"93†À¾A™Ü7˜…@ F::•œt@æm1óhÿ¿ bW¹5kñ@ËïÅ$áÚ!%@,}»í¡,@4ˆò€8@† ZÍÎÂYÀ§Øø|ºÖt@šß¯é™jg@*ðïÅeUŒÀ¥ÝsŠ`ÀHlˆíë¶À ‰jä?Ð*Ð0u@@Ü<Ô­¼eùyÀg¯†ÍáË@Nø|öFÀWLõ¸%*b@⤀~•@IƒŠ$)bÀì2P˜ËÆ”ÀtËÑ(‹ ¿JƒŠ$)b@%±dí;»|ÀIƒŠ$)bÀPµ:Ð,õ›@½‰h‚’`Æ”À-ìQø(&ÁŒzpÄHÁÁ«~¥!j@$4E‚*>Àø:«l^¹•@¡lbAB²iÀ¢»;ù @×4°/Ô À€9Šç ÉÙÀ:ºâ›ÞϪÀD’œ#°©@W‘·Y/‘ŽÀÅ”·Ÿw@O~†²æMÀÊ2PEÓÞ@·ÉøZÀ6[Í»&AóIŒAràA?O Ü÷“@½äNF~*À‘Ëìæ’LÁq }enCÁ"4E‚*>@Á«~¥!j@¡lbAB²i@ø:«l^¹•@Ñ4°/Ô @¢»;ù @ì ®Œð·Àª/ŒŒÉÀW‘·Y/‘Ž@C’œ#°©@O~†²æM@Å”·Ÿw@¶ÉøZ@Ë2PEÓÞ@óIŒAràAÄ"þHt–A´äNF~*@?O Ü÷“@EüL«\õÀ³ûš;vg@€Ñ¹†_ô¼@=Â*š] @¾µæ81õ@„ÈÝŒKžT@û,â™9@C…›u6h@Î8ÒÏüaN@ˆëÅ/Z@ÚpÑ^2@‘Æz²b U@š•Õ1@?¶Tâ@mºÂ᥺@$Æ›^àæÀFø#%ÂÀÎ ’û‘ÓÀ95¦”“Àçà¶÷Hq¡@¬x¸¨¶·ƒ@R¶„H³Ó@𵯺r*3@q xR,Ì@ïâ°vpF@Ãüž >,@tÃãNW8@bh-W7@|sëb¶‘3@)¯ÌüÈ@mºÂ᥺@­ÇnÞ\AÑ@¦§£d'ÂÀ76¥‡µÐÀ»»4é\ÇP@Ü=Z·å rÀg;EÔ§@’h€ü\ÀØ7$ÞÀV€"¸<˜nÀñb-Ä´'¡@B~ÖEëÀIø™>‚¡@¦¾t^õJ‚À?G,ª±”@KN“~`ÀF>P¢›¯@ÊR>|ÀÒÂ*È•@‡Û(ûö?@$Æ›^àæÀ¦§£d'ÂÀwþ°fî@îÀ,\èÎÆ@YV¤nMÚkÀ›¸g‡°ö@h€ü\@f;EÔ§@R·nW[¯¯À4@<½®ØÀB~ÖEë@ñb-Ä´'¡@¥¾t^õJ‚@Iø™>‚¡@LN“~`@?G,ª±”@ÊR>|@G>P¢›¯@~Û(ûö?ÀÓÂ*È•@Fø#%ÂÀ76¥‡µÐÀîÀ,\èÎÆ@<~ÐLãÙ@¼®žJ²@/tÉ8@ϸmXÃíÀ”WãŒpµÀcQ:SoµêÀUºÐêáŸÀÑàiN~À¤@ÞùFÀ·ëˆÀd<§…’±@.Â[)»ƒÀ3Ÿ/.”°@4j^ªë‰ÀÔ›`'W¢@GÒï‰fKrÀܳZ'¢Ž²@Þbî^EB„ÀœßJi§“ö@RåIË€Å@JtÉ8À¼®žJ²@žCnn…´À,*cŽæÀ§:×À_óÀÀUôj)ãÀÞùFÀ·ëˆ@ÑàiN~À¤@/Â[)»ƒ@d<§…’±@3j^ªë‰@4Ÿ/.”°@GÒï‰fKr@Ô›`'W¢@Þbî^EB„@ܳZ'¢Ž²@RåIË€Å@KÒH½î@! ®é.»@Ê-=š£@uèXÂ1E#@ˆZR¡ÉGÀYñ<à)À‰À6ô3—®G@CheÑ-ÔÀU_°°£ÀuÆêƒ>$N@ÚÃŒ§ØP{Àw&/M§àÀøA=3kZ|N¼°ÀðГġ¿ì2P˜ËÆ”À½þ{ƒ³JÃë@ÕcãíPÀtTí|Üò¤ÀÁïÒ|@@а<Ï£@ð î@-Å[­\>ÀÄ¢Ú½b@1Å[­\>@i.­¶åŽÀUâ¯å£À߃PnÌÀ>âAo'¨¢ÀµÓ¼cèÐ@ Ÿ2»“÷¿ÎϱI§àÀ`-½ó5©1¼°ÀtËÑ(‹ ¿‰h‚’`Æ”ÀÕcãíPÀ›)ˆd*í@áËέ©2£@WZ z`ÖÀ&žlÎýÀ§¾=`š³Ï@tTí|Üò¤ÀáËέ©2£@ÉPe°ý@KrÁþr"ÒÀH³u q¥Ñ@º@”\_<ÁÁïÒ|@@WZ z`ÖÀKrÁþr"ÒÀ4ØÄ nA—Æ…E>*ˆ@]GúR¤lî¿¥vz9â¡£@bÞŒÕy@ÊêÄòÇÀº"É ÌÍ@ýzÏX1¸À8Dºýÿ$k@fxé¼m'@#è1)ÒPÀL®ìØŸÛ@+ Å®[*“À´Ã9ÅËÊÀ4Ôjž%Z@æñG”4j‚@솈…9ÿ?HGúR¤lî?—Æ…E>*ˆ@bÞŒÕyÀ¥vz9â¡£@ÅGÌ{e‡@Ýô£ÍÀŒ¹™¯í»TÀä18ÙZ¬¹ÀÈ+¹ÂÄPÀ(T6m‚ux@+ Å®[*“À/úÎM'à@e jG@â¤4ÙÖÏÀ‡ˆ…9ÿ¿æñG”4j‚@4‹gòëæƒ@ýö=rTi@nK˜ð¤ÁÀG‰º•UiÀKêi¼Ÿ@»Ó/Èÿœ)Àãgnvÿ÷ÏÀIz¡À$D®@«~)Žþ>Ö@vö’Uí*®Àe›+Iôsi@.„2P@­-¨BuiÀ´æ@õɆÀÀ¡Ó/Èÿœ)@Kêi¼Ÿ@Æb…b®@:VÞbYÚÀvö’Uí*®À]˜`Nà@u²˜Ãv¼u@°£ëŠ´B@­‹ç?öûQ@”‘=”v(@²¥¢ ‚`i@4ú’W$BF@ýÅs»y@Ò«9w\@Àr˜Zh@ÁÃ&i@@|„ކÊÅ—ÀÊÀƒ}¸iÀ×tÞ7qƒ@N¨¨#j²"@VõÑãÀsÀ1:]òaFÀø.Þ,öLc@„`ûŸû›0@ãÇ9M+ð?@@ɧ—+f@ 0É‘ˆV@ÐÅ=3©Ã3@*ö:Y½9f@é C.¯FI@ùÏü³lSU@ØÉ™4 ,@Ö9 €¡ÀíÇMHã®}ÀN¨¨#j²"@›œÿ;ƃ@RÒwðzkEÀÐRæ8,ømÀ÷2öËÀ_¦cÆ}FABXÙª'Àwƒ$Ç*_qÀàÕÌ "·@n’omæÁŠ}Óc¼Ji@ðˆX³ß “Àv³SâÚT@ÿ;çjRpÀd¢mlmx\@ÂÑ—€H~…À@“9õ•s@÷hƒ’*ÀŸÜÿмHË@»Qpñ\R¥À;!!YC»@mÍÅÜ|Ô/Á2•ªT,bqÀבW„ÇãcÀÄêÓœF}›ÀÄ“ü –à.ACµj‚€ŸƒÀ …µšuŒ­@ß*¨<.pÀ¡þû]ÀS‰@”yÙ{ÐvÀ'§II­ @.ÁÝjªcŽÀxœU‰E.¨@»Qpñ\R¥Àò£Õ@&ÿúý‡ˆ‘Àÿˆ¥QdwÀr%1‘ÍD@~í=Æó5WÀä‚69¶À«@˜•w‹Wd@¾ÚÓí!lŸ@àýšÂã]@®x‡^¯Yï@È‹êNѾ@‰•ׇˆgÑÀó°¬hâ}Àƒ³¥ çÀ Y½‹å°µÀ;:ãÈ£À¥¦2¼ÒF‹À³u(µœR@ÉKÿm«cÀYÀEÕï “ÀÃCä5Ñ|À°8ÒãP¡RÀÊóÜBtÀ4H—áWÀr‰$ö–Èi@—•w‹WdÀä‚69¶À«@àýšÂã]À¿ÚÓí!lŸ@È‹êNѾ@´üñtܳÓ@SîEvˆŽÀ,GšÿåpÄÀÉ9‚!(¶ÀžéÙ!²$ÌÀÂKó‡pÀ„ã‹üøN†À:Kûð7¨dÀôÀÃ{¯Ôu@SEEÏ“–gÀI/…=Á_À$Výï`‰@é@1W]@·‰iQ)3@ç èÝ9(@Ÿº’gèÓ@­`ü(c@âb–‘*¥@ÃäŠÔU‘ÀôLòüIogÀ#µÔ‘Ê@¦À'?0›€ÃÛ¿@Út@?@3ÍÊ«jgÀ-¤Gʘł@©’ r@îV› [­¡@諽ë#Ñ A²¢‰ñ@&pñj!Áœ-Ï@Dc˜ÀR‘éx…¢†À)÷‚*qnt@W©ää,t @° ˜:_v@Ã|ß½½º@–€9½ë‡TÀ_4e´)õ@‚6DwB×b@Ø]„.NõÀ‡9*ÑËš²ÀŸb®‘cçt@T<_áL¥À.vÉ·´t@X.}ßK'L@–€9½ë‡TÀ4ˆ¹Ê‰q¼@±ï*%Ù]…@VêG™ f¿pØ–S@Y®µ‡Š'x@ÞÓ%@P@¶³·"“i@Qp|P6ûN@,QöžŒ@Û>µ6iBc@uZ,;mÄ@BèòÄR@y_>ºñB8ÀYÓâ‚r‡x@:/òD;ÈÀ|= /¾VŠÀ’-$]·IM@X’`¢=r#@°ùGâgúG@»E$tæ@©®êTÕÔ8@ËwòQÁ@§ÂDX\@¿ŽÕ.l3@BèòÄR@ZI· ƒÇ@½­ZÔÃm@n¼*ß®Àö`±æWÀ+hדf¿ÀŒ„TÈįv@§à;¡ÊÐÀ|ìBvìßÁsí¿”Þ@sw¹¸JÀ=¬oë|á@7ÆõfÊQáÀæBÿ¾Ý:@€þm²\À)6ª•¹yA¥´*_˜æàÀíºÖ¤Ê–@¾‰¦uÀȯȨé—@Ÿž täÀ2;™9¾Ìv@<Ó1…Ï‘2@y_>ºñB8À½­ZÔÃm@¾c Ìú¡@™·DºsÀÈú[ò2C@€òªCvqÀʬü¥}3@µÓ黲¡cÀ¡à;¡ÊÐ@Œ„TÈįv@²N§À³æàÀJ0Wî¢ÁÛv¯]–Ó~@Þ{ÖH»ÁÛ¦Ìñ/ˆAáV0uÙoÀ>Cà r›@¥´*_˜æà@)6ª•¹yA¹‰¦u@íºÖ¤Ê–@šž tä@ȯȨé—@<Ó1…Ï‘2À2;™9¾Ìv@YÓâ‚r‡x@n¼*ß®À™·DºsÀï‘§µ“¶@‚òªCvq@Èú[ò2C@*gàF ´sÀÀ:Ïð Ù£@€„.Á€„.Á€„>AÿÿÿÿXãFÁÿÿÿÿw„>Án @ÈSAíëd ÑŸ@ªóGC6u@+rtýÞ§ÀªóGC6uÀ+ŸëC¾ÒÀ5Hí"Ù9†@ÔhÉ*ŠÂ@¹R:†ÀŽ.µ~ìõÀò˜ù@Á‰³ÀÀïTBv½@cMh2:½á¿’@Ã&¹ö@`± 9г@oÀ†sw@?«³ÀDgO@±WÉåwÀDj>]ŽçÀŸ¼”e¡:†Àå/ÍÁ+¾ÒÀ¹R:†@ÔhÉ*ŠÂ@ê+ºýµŠ³Àz Ÿ¾3ïÀçPh2:½á?ÀïTBv½@`± 9г@Ðw˜æŠfð@{ds”@ ýtð÷ŸŸÀ5Ðò7sа@?÷eá’@o×?C6%´ÀÚF¥ÙŠÀ^Ýœé|ÀG×ü"o š@ ýtð÷ŸŸÀaóûÕÄu¿@óµ 2–’@BÞæ ‰Þt@#iWBŠÀ>€Ã¢ÀªëΚ@®‹Œf¸|·ÀÁ­ð´Ò¶@\s ø:ÀúîðpMÁ@¼õIJe@1sÓ37€²ÀIÞEý©íÀBŒ¯vÃÀQQLjÀGs ø:@Á­ð´Ò¶@¼õIJe@m9AP3¬@BŽlù‘À>Ô‘¬ gšÀôj^á ‰ÀÉÁõ|Q¾À¼õ’m2¾@Âr‘=ÖZû@%d¤’´>ÒÀˆÓ,íªüÀšÌ h~À9_¾¯oá@ŠÇÑ1¬Õ±@íÂn:¶tÀA×E)žú‘Àð‘y®izÅ@ݵ-¶÷e¹@.´ÂQ9‘…@Ås{iÍöÀêPO\À ‘±=øÐ@0NÉ^œ@ ‘±=øÐ@0NÉ^œ@GV‘‡ÜÎ@4#¿ªª”@(ªgÙïö`@Ubû¤¤î—ÀÆÇ$â‰@%d¤’´>ÒÀ˜R«ægA;Ñ3~Zœr@¤!ÝX•:ýÀùˆÌ!;²@ØÔð ƒ@I›|[M§@úS‹v‹Ã@ ÙÊle÷Àå”wÅ…@pšFz|R@.H›ÚGdŸÀ"ÕL:òÀòbf0E@àù‡•Ph@òbf0E@àù‡•Ph@†n3†ásš@-6I’(a@lŸ”ÔÍ“ÀΚ9yìÊ@I-E¢âe£@†Prl¹ƒ@{&Õ°×ã@G«ehëOx@þù.©{ÀcûìæUÀèÓH¯²é§À‹¢5¿w$À†Prl¹ƒ@~¢ñ‹ÿ0@ç½W·¹Èx@ðÀ8cåb@˜_ÒMO˜[Àãòa hÀ‡ãd¿ªŒÀùÎÑZ²ÀÖDœ²¼e@MÚ¥¿zp|À…ŠgälêeAP+);POÁ£7è}ÃâÀÄÐk®Ðï@ý¾Ã‡Ôo×Àïí‡â@¿ð@´« =€SË@ÈúÉzu¾àÀ€ã²úCnâÀ Áì ùñ@¹².™£•Æ@’Ö(v@Zщ‡ýë³@TrÎøHÀÊx#hŸ ÁÌ3T‡¨Ax‡É–hÇÀ¿Ó²æå@Ôc"L{c@rlˆªæ~À}DŽ1éÒ@Á_û¡tÔåÀ®¯mƒSaeÁOÀŠKAª¦Dú|ÀLq•‘Ö`’@P+);POÁáœE>uAxPÉ)Ôò@ âRz& Á–¹Ç3n±í@J’”òR ÁÆ€Ø- ~äÀ+ø.8°ù@FI;˜1¦ô@š4<Ò Á˜Ö(vÀ¹².™£•Æ@'TrÎøH@Zщ‡ýë³@‰™ØÒëåA ugk40Á>/5.çbá@QvïIDÁ ×c[€ÀüDUlé™@»fUÊ]ëÀЇ|yW_AÊ( &\KAžJŒýq¾sÁ£7è}ÃâÀxPÉ)Ôò@d7Z^ã@Ë8rhÛ‘¢ÀPź§ß£Ò@ú›€‡®G@B 4øåw@ÜÃ÷ , ”Ààu™nB@8ã}Æ,N–À°¢âîú˜æÀùÖåÍ<—¢@|‘÷š ¢@RJy¸Àd3¹Sâ`l@r„EЊÀzkõÕ@›ÐwðÀÄÐk®Ðï@ âRz& ÁË8rhÛ‘¢ÀE/-Ñõéå@Ýù›€‡®GÀPź§ß£Ò@·ì|‰øÀ*Á £½!©@º‘-ª¸¤•Àóœíøù«@B¤ßÕÔŒ¢@çØõ×$éÀ$żJ¢¶À1\¦c\-Î@|ˆŠpËÀ›É/Ϧ @Ug!Nd‰ëÀ ™: §APź§ß£Ò@Ýù›€‡®GÀÙ@™\AØu& á@˸4ê5¶u@¥œäøÛ¿Pªv‹åª@¸D¯U.(@³VÒ ’Áí¸Yc" áÀ +ÒõçºÀ®=ãu pe@²ØAÚÝ}@¬v·O¿ëfÀú›€‡®G@Pź§ß£Ò@Øu& á@Â~NC“ð@¦œäøÛ?ʸ4ê5¶u@~D¯U.(ÀPªv‹åª@ÖklõáÀí•ÕÍx˜ôÀ1¤•‘«weÀå> ÑgæºÀ¬v·O¿ëf@²ØAÚÝ}@MW—?p¼@[ù<É'Ï@w¿]pí¡À[ù<É'ÏÀqSŒƒ&¶Àɾ"Àdc‰@ÌW'g…@0l…žÀ™›À[ù<É'Ï@žúa8«@[ù<É'ÏÀª«pÉxÀIY5Pj­…@à¶vCW¨¼ÀS)¹ I¾™À+ÍŸðÿ˜°@Ãbá_M@ÐÏ~Uü)eÀw¿]pí¡À[ù<É'ÏÀ‹´¢ÜTÀ@ÈÊå%Àȸ¬¬Ö¡@[d7°-ÏD@‚µÜoDÂÀ• K"x™@ÏÏ~Uü)e@Ãbá_M@[ù<É'ÏÀª«pÉxÀÈÊå%ÀöðO{BîÅ@[d7°-ÏDÀȸ¬¬Ö¡@ßA¿¬À{•@„“dz‘ËÀ+ÁCï™Ly@ç©çà"SÀý¾Ã‡Ôo×À–¹Ç3n±í@B 4øåw@·ì|‰øÀȸ¬¬Ö¡@[d7°-ÏDÀÎ*1$¼@xx²ˆP§À­.`·Pz@_ß°TïÞÀ¿{qÙL›@*ý ;3²ÀRIKçÙve@Ц?„Àk7‹‚òµÀ1¤‹ƒ@ïϳό›Ð@å·7SéèÀç©çà"S@+ÁCï™Ly@ïí‡â@¿ð@J’”òR ÁÜÃ÷ , ”À*Á £½!©@[d7°-ÏD@ȸ¬¬Ö¡@xx²ˆP§À¨„PRˆÏ@ÎQo=ä•ÀH¡¶6K¬@Ôn~qä¶ÀÇ“¨ì…Î@ÑvHc©ÿÀü´†ÈôÖ @6–3N©x@Àè½ÖPÓ»À :'4ÚëÀŠ+@§ãA?XÍÈ8Ô@Þ$¤\z-iÀµÎÝȱ¼À&G4·¨±@ãwšÑÀ¯JOã òg@5ävt±³@Ùæ’:_ü1@¢ñtÞãî À–5’±ÊŸŒ@I÷@ºÞB@ç‰bþà/fÀ}Ô„‹@j €xŸ­À|/Œ*ÿZ@™…Úu?U}À/ÍDXÃX@<ÏéézÛoÀ5f¶`“q@fE•‚v{‡ÀÞ$¤\z-iÀøvlH&ÌÔ@ƒµmúŒ¨@;ò÷é°ÐÀâUBø¡ûgÀd›É­ÕÑÀ®æ’:_ü1À6ävt±³@Rÿ³S¼ñ‚@ì¤,Hb²ÀÎd‰P÷Š^ÀÃÅØd²ô@ÁKˆŸÀnÕmbõÁ@§ ”Þ+ÙuÀòì› 2½—@ e$>¯tlÀkƒìøÒM‚@N½ @V2„À— z¾èûš@M9D ™t@Ÿö¥!ŠìƒÀ`¶ß>ïjÀ:k9Äœ@?™j­Áß@ô¯ÿ†S"@­{ œ€ƒÀ‡eIÎAP@Ÿö¥!ŠìƒÀ²ZA…a›@ÈJH(hš@”lŒ‚š—Àô¯ÿ†S"À?™j­Áß@¸TŽ€ÛT@Ùûß”a¬ˆÀB®ˆdGªÌ@¡s§Ö¦°À:ìwnª·Á@Oëšå}nÀâªÁ-lÀÌÛ²qJªš@T§´´'c±@¥›–3ޤ ÀÜo"ÃSÆ@'ZÃ)£NrÀÛµ;ò@T·HZ„À'­ìóøöÀíڗ·@àgpÞ»Á@ıC‚š<“ÀU³Ã_Ý@ŽÍ=’ŠÀ§aÀtÅ@™5éS‡tÀ¡s§Ö¦°À'Ü'6eæ@Jëšå}n@;ìwnª·Á@©3‡±(aš@Þ4©löÈÀ£›–3ޤ @U§´´'c±@'ZÃ)£Nr@Üo"ÃSÆ@T·HZ„@Ûµ;ò@YÉ_Ù¯ÀjòÙ úgûÀıC‚š<“@àgpÞ»Á@ŽÍ=’Š@U³Ã_Ý@™5éS‡t@¦aÀtÅ@OÖÂykÁ©@¬3cêŒÀ²Ži¶×d\@PÀi°#DfÀCè½Y&¨Ànú˯ k†@ ”lÌÃñsÀŒâV°}6@¬3cêŒÀ±VÊ¢†÷­@™1®–fÀœdk©3¶q@íã7qh†@æÍªâ„­ÀÕ,}áa?9@×.üÂÿTuÀ®A$&.×@¯,~_fÄ­@9¢Ü.=™@"7–[—7À&N èÚi×ÀGÞ|õ±À9"Ðf’q±@`oFëÖ‚Ÿ@otȸyª@0‚ªJM/*@‹°CpÂÀÄÖˆ•W²”À¯,~_fÄ­@ï81  Ê@"7–[—7@8¢Ü.=™@œßv¬ÛhÀÐCsDÞÑÀ`oFëÖ‚ŸÀ9"Ðf’q±@>‚ªJM/*Àotȸyª@ßñt™ÀùG†lÀ²ÀFN“ÎÐÒ@`’L†*À›ƒ–6wÞFÀ[áÔn¸€1ÀrÆ®o³OFÀu+¡ð û?kOw- C?Àš¿•´AL@Ç»_Ç{kŒÀ©¾µã.@1w$Š;ÀÈúfÛGt@¥±,c;€Àev¥fKqR@0*A¨rgÀî¦*¬»aÒÀ¦ ¤—´<™@`’L†*ÀŽ.©ƒ#Ò@[áÔn¸€1À#6Š¥ÊÀu+¡ð û?ö.Çu4VÀ¯@‰¶0L@%äå|NYÀ¢¹­@¸«G´í:ÀTÊc(²ªH@oÁFýð5•À¦Ù<,ñù @Ìf°×ÌIsÀÏ1bÐ…ˆ@$Û23dš@xØdi—ØÀŸ"‡ûÿ«ð@X›µ„º¹C@°¸!u„Ç)@Wž£ÇØè¤@mÆûût?z@Ë´Í n´À~b4µ®J;À/뻸r"@@¯s¤&“ [ÀN6I0øðÀ ŘÚoÀA’5[1‹À@äWð…D@bb•>®a@r€ÕRæ>@KÅEÎýæƒ@>Y$ë…V@xšr;7ìJ@c­OPÝú1@kË€ýÀ¨f;Ø?YÀ>œ¯ÀDÌE@àOL©ñrÀ_ÂÄÚüþ8@¶Ÿ«@]À@´I›^Ì@T‡jÈ @×)WÈï@mÆûût?z@œ@ ²­“@Óצ²?,À"Ï·£—ŽÀ%“ÖäÑ[ÀS*ΤÒQw@™&¢o¨ßÌÀ»©\¼rcÀ~Î?, @xF°p†ZÀѬ¤× >@&>·@‚ÇFPîW@ª:ÛZ#{h@Gœˆr è&@ÿÔ¹m˜@qö¯´[À‘UÝ’{bÀàOL©ñr@>œ¯ÀDÌE@¦—ª^ºUÀoØ‘1L+y@À5CyD§@ åF´æ‚@è@ceˆ¦À€Î1 ³xÀš—u’T½‚@¸mÙ½±ÎH@ÀÜaMaÀ Q ò6åÀ´èî³uõ0@Ó<éðÒ})x@vÛYɵ´@@º‘¦Á ÀÔyJÝ@P@ƒ¦T])t7@ß}†jN@´Nƒ0`Î<@ë>´¤"“@@2¢æk<@ÄëÓZ}@Ú̸ _áfÀ“,èÊW›Àì?ôî½jn@‚?À«œ‰ÀEû*§ h[À”²òÀÑG@&Ñ.¼b À„VI Gœf@2úŠÜÅ?ÇkwÜz¶<@ÞbŽ#i1+@@2¢æk<@Ù–ÞWe’@Ú̸ _áf@ÄëÓZ}@Z« žFQaÀÏ6:þ$®œÀ‹Vëj[Ào9¶Ú÷ƒÀ&Ñ.¼b @”²òÀÑG@yúŠÜÅ¿„VI Gœf@}o„ßü¼@ùï_³ƒ@Æ0•^βÀúº ‰f@mk!7þV@m;÷QA¼zÀÈÖ˜Wò¥Àa4^‹òwÀùï_³ƒ@E›w¢˜±@X"seh@'%ާöåµÀÍzÁ fŽzÀôc]áž@Ïs?ˆ yÀTÈg»P‹À—¢¬…‡úSÀN—–‡j@ÌãVÁó…©@ÆJ±{Á“À³ô”Õ µ@¨åj³~j3ÀŽ·_+7¸„@Ñü(Všò¿7ìÕ#®À<­¨Š•Ð@n·±"j@÷wd@ÀÆJ±{Á“ÀgŒgf⺷@¢nÞ?0k3ÀyOûÄbI@Üü(Všò?·_+7¸„@TƒúÅÉ@#ÉŽc¸ÀR!jN Ë@ ÷Ñ/²@,ÝH~}ÖÀJ{Y7?¾ÀB³*cE¤@œ3gžÒ@“S/Ç-MÆ@ML$’±@jðî¢@À5´@µ+Џaf@÷¡á^.%Q@b~C2­ÀÀâÌ¥Ëþ«ÀþÂpê Q‘@ÿJ¨Ý{•{@ ÷Ñ/²@JNŸ'n?¯@JÀ×£&¿ÀÇ"Š;dÿ³À9Ǻ—’Ä@*Nc'Õ{@²tÎ’cݰ@‘=Š(“š@’M¹¢Œ@[å^B¯Æj@ɦÏììP@µˆÝ]î9@Ù¿‰Ê «Àî/ÝÁ+f–À»Ɉ³0z@Í/Üd@,ÝH~}ÖÀJÀ×£&¿ÀUö‹ÚAàá3øWZô@KÔp·ØaÁúL#ÇŠðÀŒÝE(wÚÀu²‚Êq±À¦Œé¸Ho™@,ƒéÉ]p@£øfI…Y@oš7‚Ñ@Ù³`Ý!m@w]QmašÀÛ5ó(BƒÀNŠeª*hž@Ãõ B'‰@ç´½»±ÃÀÔð“®)¬ÀAЋB#cD@œŽk=Ò¿%=äj7@®²;aòMÀJ{Y7?¾ÀÇ"Š;dÿ³Ààá3øWZô@œR³?\ný@HœÞj¢ïÀ(lïýÀ·7k²YS¾ÀoV!­À'ÀÀÏ‘‰÷Èw@½ï¦ÿœN@Ž™Æf7@æ³`Ý!mÀoš7‚Ñ@Ƙ‚">”‚ÀÌ·{¯0sÀilͳˆþ†@|³}%}s@ä<žRGªÀ&†•‘ý®¡ÀÑk=Ò?AЋB#cD@Ô#z²XúLÀT­ÆjÈÌb@B³*cE¤@9Ǻ—’Ä@-Ö>GœÈ@%š½B1/¥@Õ —¢ñÄÀî,“­e˜ÀׯA¦äx‡@oÿ8‘cÆÀ§í7oU¨À±ÛÆë;_¸@ï±¾ŸÚ‹@œ3gžÒ@*Nc'Õ{@%š½B1/¥@Eš=“¶@,‚ RУÀíhÙ„Ç@’ÀÞÆq•@e@oÖäè ¦ÀËœ±¾ý³Àt—pßLè•@;öm| i@u;ü»@~˜$©VÀQ%çô;%˜@샯%Ń@žã\´Ìx@*GÑ}–²¥À{zîd'ÒµÀãw+’º)“ÀS$½äû«À~³@œY«@~˜$©VÀû;I-3£¼@í[‘·äƒ@|«Ú¾ßIp@úcv…+ðÀ{—Ç&2º@ØFˆnœG“À¡è©Ô9¨Àf¤C?¯™@†WËâ¼ÞÅÀ“S/Ç-MÆ@²tÎ’cݰ@KÔp·ØaÁHœÞj¢ïÀQ%çô;%˜@í[‘·äƒ@gÇuAéö®cyê@‘,ç…Ì@ž †N¬ª@ÅÆ]p!éÝÀdãd­Ž-‹ÀÝÒùG?Ü@Ó ÑÔy@·;"æº@MzǤ@ML$’±@‘=Š(“š@úL#ÇŠðÀ(lïýÀ샯%Ń@|«Ú¾ßIp@éö®cyê@5XßÞƒÿAÖ¶y¶@­ipà•@¶Õ3¹ûxÀã.ФݧÜÀØ™Kõ[‘z@øŽ' Zd@b„/®)ޤ@ï{f_@äáÓÛñ!¤@ê¾Ro1øÀ#îùÎÈ#±@køÆ|æš}ÀC¡Ñš˜ÔÎ@ ” é~ŒÀEm™>íÖÕÀ Ö6¾n´@²É—²×à¸@z¿•RÇZ@‘]ÙŒ”@žïVi¿À…êü²3k@¿Ro1ø@ãáÓÛñ!¤@køÆ|æš}@#îùÎÈ#±@ ” é~ŒÀ(œùÅ‚q×@±´°õž!r@“ÁçÛÀu¿•RÇZÀ²É—²×à¸@|¸Â:2pb@¡öKéY@P¡%ÕÁÀö¬ö}Çæ@~WEvpµ@!ß’ÕÉ› Á  mpLçÀÒç9“ÂXA-‡5Ci±Í@^éŠÈ^ÁëÅØšO|ÏÀÚ•óÂb¶ª@‚3Ç›³Ù‡@sߛ₃Ë@P#pþ€äž@Òª£Î44¦@ˆAü{´ËŒ@n”,FÖ°@K‡äSƒ@Ñ_žp§#£@BÞ3và€@0ƵÌSÎ@pë~ú,[³@aÅçZBƒõ@Ô-Äq"à@¤’[ý­d¹@@ÆìfIæ‡@´!`îòÖÀ´mƒ”ÜÄæÀ-‡5Ci±Í@´Ÿ®Óx‡AOJ8=éÏÀÞ:*1ÁšÝk[Ç}@HáÄ[–Z@ò‡ê$Y@ç#‘eÞyp@=¥º~Àx@%k¶Ñ `@Ï“OåÄ‚@y÷‹?s`@²ýyÑèUu@t¦Šž@ÐR@è7ã‚úæ @ªõ,kÍ“…@ñ#a=ûÇ@ÀôÚnü±@c½»`Ô–@b¿Î®jBŸÀÿ>òîiÀ]0g8—AŸ@s/uÝ"´“Àj¬»óÌ¿b¿Î®jBŸÀ•ŠÛÅÔ@b¿Î®jBŸ@;~@ƒþˆÓÀ ñ¼6гËñ«“ÀµÎÝȱ¼ÀƒµmúŒ¨@»OH®AÖŽ¥-¼ÔÀÉÛ›@Ó·ÿLgxiÀ$L Õ„@oàmt¶MRÀñx+âþyw@ÚAç8‹ða@1e¿×±ã ÁC¿4¾Àfâ}À;@ì@îctúÑ@¦Øñ”éÊ@‘’Š‹Ò·‹À@Ö”\S¹›Àüuо¾(—@¸¡Á‰w¸ÀGÛ µ½¾@&G4·¨±@;ò÷é°ÐÀÖŽ¥-¼ÔÀP6°vè AÒ·ÿLgxi@ÉÛ›@oàmt¶MR@$L Õ„@gÕ¾ ¢?b@vYóB«ãK@¦Ð5UuöÂÀ {«zˆëÁD1ãSÔÉ@RÉ ¡M<¯@­‹#.½j‚@(5޳j¹@J©ÿݧ¡@He»ÀB~»;bÃ@꛹úŽÝÀ“9}þ+×@¯ Ücóݰ@OõÓͳ9×À˦Y ³Àxúnè¶ÂÀIÃF‚ÊÀÖ2_c&Ì©@Ÿë0Ý2ÀzJ‰œ1Z·@:•xš@¯ Ücóݰ@ž Æí˜Â@}æ,í³ÀJo 5Ý®½À(Ûé5a‘ÀBÛLê:¶À›ë0Ý2@Ö2_c&Ì©@ø‘ùM7š@œš\¿â}@üäÿ~ Û©@C[6ɯDg@ä+ n­{@ž¦R˜™š@ô¦ÊòUgx@¢@~Ô$.ºÀ‘Ç߃šÀó‘¦Ã¹gš@…°Þô:…@C[6ɯDg@,|¾•üA¶@z´JF3-¤Ààe2íÜ…@ÙËçËd@2ûõ±˜ÀYFÚš{Ä«À¶ù«Zð³…@gt'sq@;JÓȉ!A—ºË’ÔRâ@XµeéNÁ°»Ä,j¶ÔÀ6sMÔüsð@3Γ¦™u¼@X;„¬OÃÁ¬Jþ0³ ×Àä+ n­{@z´JF3-¤À—ºË’ÔRâ@cèyÏ# A>^\¶ÔÀ#qC†­ Áyb}¿üßÀ@íÌßi0@ä+ n­{Àø^çÏÄ@['K_ØÀ\›¼ýÿ£§ÀˆÓ,íªüÀ;Ñ3~Zœr@{CÖ¯A—7V/¨Ô@8õ»dçÁ<Î8¶ÑÀ+ž•š‡ÕÌ@•€M>Ï™@úŸ ûM¤p@øÓjº×¸¢Àû7?ï[5q@úkt앤ÀšÌ h~À¤!ÝX•:ýÀ—7V/¨Ô@è¦ÑZ:A—µ`@ÒÀ¥S£§ÙçÀ-°!=wž@z}N˃îj@ô ²64ŸÀ¸&Ä>JÑ@{DK0" À:%­\ÒÒ@9¢Ü.=™@"7–[—7@oÑüà/dØ@q°ŠœŸó®@ïlUÓÌÔÀT,EÒR«ÀºT¸¬´ÀOx‡ „~À"7–[—7À8¢Ü.=™@q°ŠœŸó®@YFÓPÌ@é H]ŽR«ÀA7·?^ÈÀÓ»I{À w g¬À­äÂâ€É@>¿ÃÃ:@Ñ?D£f©vw@j¹²²ŽÀÐé#—uv@é ТÐ'a@ÓüZ’N{@*wD %&žÀ;ÈÌ-þƒÈÀÆíPžHׯ@Ÿù¯J®p@íy°¡>’ÀÖÏŒHažÀ …¼¨…º`À>¿ÃÃ:@Ñ?)³ˆ0€É@ú‹›dâ1ŽÀäCp1À£@V¼ÑáAa@ŒCÅ·à[J@&©謑‘À`Æë.f³@biøˆ°¹¥@&`¨¾tÕÀò½6`w…ÀÁßó‘z§@Xû"B>²aÀ{Êÿ÷z•™Àü?rÐAØ]@JK-aº‹Àü?rÐAØ]ÀJK-aº‹@JK-aº‹ÀavMïù@JK-aº‹@U“yï¹À]X¥ã¯&AŽ'VBD…×@’ ¨ÌÄ@hŒ6£ãtÁ¼ˆð5ÙÀ G΢§"Õ@ò¼û‚W¡@æ]SÙ`PÁ@?ÃýÀç€PÀ>]ÜwáÀD'Pf(v ÀŽ'VBD…×@¡R‰ö.AL¹œÆ|™@¹ rôÐÚÀ»é(ÿíÁ^¼†È}¡@r‘?(´l@@ÃýÀç€P@ç]SÙ`PÁ@² á«æ@Ûì€: váÀ²Ži¶×d\@™1®–fÀãÀÜ‘‰@?ë‹Ú3ar@:TNXîsÀ±ã땊eiÀ-ðÏó yÀ^A†mT@]ƒE”J@l–ˆç*â@@k¤hc×5@0‹ƒ–``À\¯ÿ›´ÌqÀ:¹¾bð›Y@PÀi°#DfÀœdk©3¶q@?ë‹Ú3ar@b€,‰ÂC@'‘˜QngÀÖÇ´@/Û]À°¯¦—²tY@¶¾qú¦þ|ÀÑXG bã@@È1÷)­ƒ5@[%ýñVLÀÍÜdWëÊt@y7ÿ¬À“Ê\DÒJÀ¿ •ˆt¼×@Ózì‰àá´@ ÍjQ°°âÀ¸ãŽð{\ÁÀH¨îÐn©@óòxîe@z¼w×Ó‡À•wNÖ€[¶@ü«¸²!z™@ÕRX™°ƒœÀ]Ó®ÙvÓ‡@§õÊ4ô%º@v† ª;â@Ózì‰àá´@«Œë¾½¦@|e(Ñâ£ÁÀ¾f×DËb ÀÈBøøvŸ‡@ á`ì‡Àͨj_þ©@S ÔHõ•@jZ!A¸ x@ á`ì‡@úßí wp³ÀÝþ@œã­˜@޵%×q4|@††Jí@P>iXj¬@Ó6T7Òßc@E±ü\ÖK{ÀK,—¸…èÀ%ªiXj¬@+n—·\~è@çÕpšÜ…ÀÿÂâÿžž@þÁ»î ™@…RR.@NìÀ§#W ^ä˜ÀÈÕ`m‚[¾ÀØ zÜi©À7‰ywº(Ç@œ~32+s@+¸ŠÁ‡(œ@³€;KÀ@Lõ•t–¡@ þv¸öªÀáôF(éÀƒm2Ê;‚@Ÿ Òçë™é?ÐD’­³ü´ÀÖö˜à;šÀLõ•t–¡@¬¬„&›@£él»OêÀ·—©^ƒú›À! Òçë™é¿ƒm2Ê;‚@†¯·Ž4šÀ´ÍrÊØ`€ÀãwšÑÀâUBø¡ûgÀÉÛ›@Ò·ÿLgxi@ì6Ì?(ç@x?Kþ±Ð?.ƒ\ÃãÀËËaü ÀJ½îSNz@~_(„@ºž° =‹»@wztàSØ @¯JOã òg@d›É­ÕÑÀÓ·ÿLgxiÀÉÛ›@x?Kþ±Ð?jñnñŒç@€Øn¹ Àp€o”¤àÀ„_(„ÀJ½îSNz@±øó²Ý @ RÍ´f „@Jš± 'úÀW›©ÙÜFvÀÒSƒX «nÀØZÌi7ÔNÀAÉ+hã<ƒ@¤ÿb@ò\ø@תZ@·²^UþC@maäÏ«]@LÊõ±>ãC@­ìàÆ=Ée@a,ýÁž©P@£ÚˆeÊs@ÙÁDgç]@1ÌOšøõtÀ¦³«ùlÀ°N÷ý3OÀÜ1Êæ_œTÀ¤ÿb@Sï¶ËÔŸk@(¬kiHB@é§=“³i+@R>øc›WD@4 E+@ŸùÇyeßM@²£¾ÎíØ6@ÿ»$5"[@ª‚2Þv€D@°½ÞW¯-AÁçN¸í@;Š „†öÀô©‰Ñê)Áûƒ–îœCëÀ¢ÄŠÕÜñ¡@G^Ãy%õ@;=ppðœ@‚3³hÀh½ö^@îÀjšÿäqø²ÀÁçN¸í@Q}æ»AJIE[ÀÀóóhŒ*¤ÅÀÙ.4µÓ3çÀF$8ØqÁP^Ãy%õÀ¢ÄŠÕÜñ¡@‚3³h@;=ppðœ@|*õs(´Àþ˜Uº¬vËÀ+ü¬¤4ýÀT¿Èg„™Àõq»Ì@™X'£=‹@E.b%zÉ@Åþs<*PÀ;Š „†öÀJIE[ÀÀ×òés A4ݽ׹6…@Ç·ÓyhÎ@Ð/—Qðââ@’Ú^ÎD|Àؘ³î@ >³ÜĽ@TŠ“!Ñ×@ÓŽOŒ˜À“_5Ek©Áb /õÀàSô„ÌÏ@ßôC1@'öu›Y%à@) Jè8¡@GKD™y?¼@”cª:Ї@ðLŸÉÀ¢Àù¿n3póÀµ ËΔ‹@}Aˆ±&J@Æþs<*P@E.b%zÉ@óóhŒ*¤ÅÀ4ݽ׹6…@ùßÑÏA8Å?Ë1@Ú^ÎD|@Ð/—Qðââ@ÓŽOŒ˜@TŠ“!Ñ×@_ä~þõ³À¢Æ·F>þÀ 憎@nzCGUL@¯†¹4Ÿ@{ë茈`@ ¦Ègf @ÿÊñ|êt@è@ceˆ¦ÀG³x«æv‡À§ó²ƒDy·@}j‡–ô‹@™¹&¼ÀqŠ„tš†Àx¡~ u@Ó¯îÓ²Y^@V^9»^£@ãolJx@qþšcÉŠÀ>"‘»þ’pÀÿÊñ|êtÀ ¦Ègf @€Î1 ³xÀûvþ2¾†ƒÀ}j‡–ô‹@6¬¼À® ª@•“¹—S¢z@š‚P$ ¼À<¯9S,Z@·s¡lÍB@ãolJxÀV^9»^£@ùø=¢ŠàaÀK(‡ÂµeÀÝ3g„ùÑÀ]&(ržû@5GQñ+ûÀš—u’T½‚@p§ÕZ˜7g@™¹&¼À•“¹—S¢z@2l^ê@_×ç/q•@cjÔãÝ¡@Cx?:I¤x@ˆÀdÈ?ÞÀó}^˜.á¦À|e/ÜMs@YN¿%Û[@Jý"¢‘@-¯Ž ¯F&Á§]«.ë%AqŠ„tš†Àš‚P$ ¼À_×ç/q•@!Hc?"zæ@ 6{ÊÑv@‹Ñ¢†¤yO@½2ÝDæ À³§ðë•ÎÀ‚³ÉÚÐAµ@PÙDÉŸš?Çù}í3‹@®BéI,«Õ¿Zu¢Åøê©ÀÒdMÄH•@ñ…fêëv@=ó¬…ïH•À—êå0½«À°Nki­uÑ?PÙDÉŸš?ŽºA€’Aµ@¹BéI,«Õ?Çù}í3‹@q{°"ó¡@®¡þ^SÀÀq{°"ó¡ÀqG‚Kàµ@îÜ2Þ(U׿T/åÐT¼«ÀÜÄŒ!.¯òÀM‰µ|¡¤Ô@H7Ë{uœò@æ7¹¬ ¨ÔÀárñ-<]@þEKøÅ¿ÄëÓZ}@Ú̸ _áf@SrdMÁ³×@Y`Õµaõ°@~ѬðXKÚÀ΀h’²À4¼yxÄw@•¦·ƒYfá?UO€ð&è•@nÍ|˜F‘n@ÌN<Àµ¬ÔÀÖ ?AЫòÀæ7¹¬ ¨Ô@H7Ë{uœò@ÇýEKøÅ?árñ-<]@Ú̸ _áfÀÄëÓZ}@Y`Õµaõ°@‡îQ‘¹ Ã@zéòð1r®Àó—vïíSÈÀœ¦·ƒYfá¿5¼yxÄw@mÍ|˜F‘nÀUO€ð&è•@“,èÊW›ÀZ« žFQaÀ~ѬðXKÚÀzéòð1r®À›]æ.BDÞ@É¥•ö b°@}Ö¿ô;@'Ìܳ#$@ÄŠÃAe@¤ÀÞ<€=¢¿è38²Ï@‚ÀÐË¥k¼ÀO·7*žÀ'¿š.“WdÀì?ôî½jn@Ï6:þ$®œÀ΀h’²Àó—vïíSÈÀÉ¥•ö b°@ÿ‹÷ëïQÐ@ò4V^@ÿh4L@SÂÞ<€=¢?ÄŠÃAe@¤)L³ÖCÀÛãU|<€ÀâÕ’¤Åu@vFÛ?q±@¨7=Ýs@® ÎÇ=If@•Ä|ìîLŠ@Û½–üviÀþÇPWXLq@!wM¤.Ú?ö\o==UÀÜÞ2Ã'Cj@¡ŸQykŒ‹À8œ_gÀ2_¸Nf@?-Ïâ%Y@Û½–üviÀ½-Øèñ ‘@vM¤.Ú¿þÇPWXLq@»+év¿Xj@˜bëjø[‰ÀWäÝæc#gÀÂ7ÂD¶}„ÀºÍð6ÃKæ@¥g–žYºýÀîw©Y:eAè*Ê&J°éÀ˜äF, ,Á4¾Üe¥aÐ@Yd*¬˜ÁU6$ =ALš4iÒ¸Á¼Íð6ÃKAè*Ê&J°éÀðÃç\#A†G}Ì@êFBÁ¥¬›ÃcA‡©ª‘(Áog&#üÖ@êwçHvËÀ79  3T§À²Kj>ýW§@§qÁŽ{eÀ.¢Wdwx@ïÓ$~„@™Ûä2°:@Hñ0Çñ÷@ªG"Å«òL@~[Ý´_©ÀRµçrƒ*˜@Nô1HÚ¿ÀuÁ ¼@ 3NfÀœ_E:ã€@³TødÅÀÞuø[°š@i¹!©g2@Ù›E¾òKÀ90ƒã[i@§ÉUÆ9ÿsÀêwçHvËÀ…˜ÎAº„ð@ç¤êûwÁÈÀÄÔøÃÀz€@£é©4Ñ·™ÀœÛä2°:ÀïÓ$~„@–oÀà­l@¥rOò‰v(@f5{YâÝ•@°ŸÞ¼~ÂÀÈüm¿‘Ä@fœHuÚÀù¯e‡@o! ¨¤¡À¥"Ë‚A’@+¨ÿ<[ÏÀOŸè]¦ýKÀ@î–@e@ˆ¹'šmg{À‡Í¿œ…@7C¥}[H‡@±é¹ÓIF@79  3T§À‚ÖºÖÞ»@÷òþC>@LùhzÊ&‡ÀøEË›¦^ÀI›d×Ε}@³4ËFL:@q¼‡Ò@ÐæFì/]HÀlY³`Α@GôŽR^ÀçQ°Ò¹›¡ÀùÞI¥5ÏS@ÎÖD)œQ'@‰üS× ¿UÀ‹p" $¦—ÀœÿN•êy@8]EhÇ›ÀÁø‚¶T`ÀŸëu1C-nÀ¢–<øT@nbæAÀ±Œ æ¨%aÀÁ‚Ï`ÉE@ü¬]Nó]t@ækjÔ{vÀýÞR5µ¶@ Zñ†À;qÖÆÛÈÅ@²Kj>ýW§@ç¤êûwÁÈÀ÷òþC>@1b~èÝÏÜ@ý Çm¯ @œúj™äãÀx„ùÃ[Àm"Ë^u@EráN‘wÀ!Ðí3œ@Z¢Î"…H˜À›T2Ú=ª@øEË›¦^À3(” r3ÀZëNe(H@‰&Fè#p@Öàñ(ïdÀæ–¶šãö@Ï&nK1RˆÀBíC©Ç¬@“QÌÕyHÀun°§±¼r@GôŽR^@lY³`Α@¯:å“WtdÀ”çÅH,À¢-CÅYVÀ?Š·ƒ „@‹¾í[xq@VÀïH ²À"p¹ÂÞbÀX]$)sÀ“QÌÕyH@ÈÖ¾ê‡À˜gøÜ.j@s…”­¸C@Üo ñ“t Áö•I…\Ûà@»?;]ʈÕÀ¿â hÄ;uÀý Çm¯ @iÛ“™‘TØ@¹b…£ä•ÀÁÛ(¹* A='þçÇ‚àÀòaOƒu„§@ù™{+åÀÄÚz.ø«@;Xhˆ%ÀÝ1•jàÀjª}rÒó Á õoÖwu@[tTÍÝÚÀœúj™äãÀ¹b…£ä•ÀŠÑõÞÐô@='þçÇ‚à@ÁÛ(¹* Aš{+å@ñaOƒu„§@>Xhˆ%@ÄÚz.ø«@«äx”S É@Ë.?vÝþ©@n¼»®dĨÀS†LH,À¢³¬¬À^s Ë4™ÀžVñB¾tÀÀ6/-9¤ˆÀ’ tþà”ƒ@߆:*1'[@î4g_ÊQœ@¬@¹1æ?Ë.?vÝþ©@³éžñÚ²@¨Šr?'FÀ3ÿæÛYŠÀCTíM•ÀÐA©º‚À>º«Â»’ÀÞÔ[‡µ„´À IÑ?¯Pq@qXDë§H@\@¹1æ¿î4g_ÊQœ@¨Bs‡Œ¹@~î—7¹º}@Åχu·ºˆ@vPöv}¬ëÀx¼ÄþŒÀÀ¯®ÿ”µÀ9d¯âsÀuŽéà£À~î—7¹º}ÀÏ®:Y/E¤@¢±ì¢£[r@@¹¤?¦@TP¡ás@%C(Fó'Ú@œ†žÂì´@¿·ˆ#¶yž@aÒˆgªk@ŠéÿTòsÅ@·ËzÌD˜”@AK„zÇ@ß`$ÓâT@~î—7¹º}@ÛQü¿²ª@Ó8bÇF?ˆÀTP¡ás@À~bàSµÀ~î—7¹º}À¨É4v,WÀTP¡ásÀ@¹¤?¦@Åχu·ºˆ@†}Ecf+å@“*…#»–@¡ˆ/(ç?îÀ7`aogÀÀƒÖJŧ×Àlâ «oÑ‘@e&$Ae&$AiyÅ¡)Áš:+wÁêMua¬¤@8‡¹r@3m¿­Ú@ý fIWµ@I  |tºµ@:€¤Éöa‘@äTñÛŸ@E|~?7l@.FP@5í¹-ú`@¾¾J’àB@~b4µ®J;À"Ï·£—ŽÀÄÄcYjkÀ|…á§_@ç¿ÙGWÀh¹—Õ¥Àϙū¯gi@¾Ð®›‹ÑR@.‡Bó @@òÕ¸¨>P@YjJ ,øO@ ¶&hþ“2@Tk±IB±C@oIrqå%@·SPØAÎÀƒPTÜO‡ç@Î~ˆŸûðÀˆå$º@°ÀMØS:Ɇ¶@'Þ‘{Ä[u@êd†ªn#"@x,^PƒQÀEm™>íÖÕÀ±´°õž!r@Ç·ÓyhÎ@8Å?Ë1@48’ÏUÂú@e ÑØ …@—ë@ܧ:@æÅIz§ŽmÀ|¥¢Ñ5æÀýƒà2_¦À"U'%ú^çÀ`öüª´¤@R›+c1@A²Ävlâ@Ÿ˜EÅQÀ”: ­î¸@豞{ãv@ÀÛ œQÉ@wû!·‹@0ìûž¿‹@Þbª†÷øÁ˜‘êFQžÀÙÍ3¢/ÜÀ7/Æ3u}UÀ }ë›À¿„@ Ö6¾n´@“ÁçÛÀe ÑØ …@>a»ù@L{Ò9ÑnÀ%#fk‚¡@1Á²ÆÛpÀ!«ì8Í-àÀ¼™îù†°A–R›+c1Àaöüª´¤@ºô.b ´QÀMHOå …@\öÊrfÞÚÀZ+º¶“£@ì ›®àÍà@‡„Ô@ì‰FÔ9•@fFɱt@@=·>A@uWÆß“£À6ðÒú°1ÁÀzŽ\Ðð“”À8æS"Ÿ@ù¨››†”áÀ‡„Ô@nù†ô’_Þ@ýê(fgw@Ì»&JV@¯¥ù?"ŸÀ¯Qa•©Á@Mú@ $h•Àï¯C톰ÀìZaÈC@¸~6M8'@6M»˜L†Àí­úºs_iÀ©s@‚4|Æ@ƒó˜è½Àˆ@³â96Á‘ÇÀŠAPììÖ{Àä··Õ8Û–À &VYàtÀ"ÞEÂY–Àó‘°ÕªpÀd©:-£+¡@ô¤Hàän@Z×ÏöG¾@!ÞWø;Éh@ ­ B.H'@­‚üQ @"rµ½^UÀV{JqRÀƒó˜è½Àˆ@²·#ĺ@VòCüÜNÀaªUÒ]…ÃÀaÑæŒ:PÀ–©Æ°¡|ÀlŸûEbÀgÕÕ}eògÀô¤HàänÀd©:-£+¡@!ÞWø;ÉhÀZ×ÏöG¾@}¡“ó±åv@φÿ.<í[@³â96Á‘ÇÀVòCüÜNÀË/¥âB×@\Šû]ï”{@ÞŒLˆ@Ç’Oso@KÞŠYäs…@|)â $Wb@›Ïå:¢UºÀÁ-ç¶4{ÀßÛ7›D²ºÀKYt {ÀŠAPììÖ{ÀaªUÒ]…ÃÀ\Šû]ï”{@æ b㥧Ò@ºƒwiUÎQ@‹³ÿ½è³À §$0_ÆPÀ…¢ñxD±À:TNXîsÀ'‘˜QngÀ‚¢ÛÑ}@_4”xeHh@Né®$DgP@Ñ ý+ìêD@ÕÝÓ/[ø‘À03¥|ò<@=¡ÿh@X8ã™^B\Àw‹îA~q@j—"‰ w&@’ÏWúäæA@"ŒÒ‰HÁ¿“¨ dNO@ ë4² 8@±ã땊eiÀÖÇ´@/Û]À_4”xeHh@Ü©Þ)r@€ ìçD@WЂˆ³§:@൶C×eÀŸŠ¶rnçÀU8ã™^B\@=¡ÿh@h—"‰ w&Àw‹îA~q@"ŒÒ‰HÁ?‘ÏWúäæA@`ÐöÖOòC@Þš)à¾.@u%õ¨ž¢Ê@&…¡7ù"£@üX'> 4¹@(Ê ‰Å¥@RJâjQmÑÀF«÷Ó‹§²ÀW+­@ˆ¦À¬›ñ)Û£@_>ýwq@bŒ×-‚’ÀîQˆç‰ˆv@Ü]€ßÝšÀ&…¡7ù"£@¤žßysÀ@šW&£è£@¬-óŸ2—@ñ‰RXp²ÀvxÂ:¿À•¯èH˜—@ÔmhÉ·%ÃÀN„>y‰ÀÀ0"ר«@'W‚Ñ{Àþ¢Ìù[¦³@¡w*ÖÎïø@`‡O°hOíÀ´îôPƒùÀÏN©•]è@X–SO>Ì@^½>i@™²â\>¢ÇÀŽúåbÃ@`‡O°hOíÀ6¥àR2¨Aûä|—kè@Çóú&v.Áe½>iÀX–SO>Ì@Î}&©óÃ@?Kè K—ãÀHRá¬êÄ@WIÉ8À*ذÔWæš@ÔU•X"·v@?”ñ ÷Š¡ÀW€)Ô;²}ÀßbV¼5žÍÀ‚ÔÓ0q‹@Eµ¶¯Kt³@á÷¡üö>‘@WIÉ8ÀýÃ^§®Ò@¥žðv@>Ç;d_S@E9·ë}À9©r‡SYÀcy.¢µëŒ@5ö­0;ãÒÀq%•´@£¤9¼Dm@<>³fA"Sö› ³À@4ç„ØÀ¢ãôÚpfÊ@Ö2l@‡M‘i^‚ÀŠN–ŒBu¬@þU§ÀÃÀP(÷ÈôÜÁ‡ày•rÂÀ{Mˆ†Å{@ÂáËέ‚ŸÀ"Sö› ³À@6^…a›þAÙ`‹É×@šæ,†E÷À»fÅ>@“ÀÛ)U䙩@cÄ\G—mÓÀް¶ê@©øÅ€EÂÀ–GF’YÑýÀùoÄ®õ¢ÀG0ËEƒÅ@áÉE\}‚”@žeîK^rp@4];'“ÀÞ® ErpÀ‰Ž³š¿`ÀøíV­M;À.Ž»•¾ÎF@’ Ï ;@žeîK^rp@'á»{Ç‹@àOk–qpÀ>òà ‰ÀåçëÚæ:ÀRËT`Z¤YÀþ‹²Ý\Ú:@ÃGSÒµÖ/@Mé9óÉ=Ä@VîN––›@¶ÆÉÉÀ™ ÜôÊ ÀÐ23rü£@­1Nýw@VîN––›@rýh“Ír@×H“l ¡ÀÍKò†QãvÀÿ 9 z@i9%Î/YP@ÀEuV2ø’@ßjNÒõk@ã/ñ¶OE@Îý,‡.}]ÀË–oÔ//h@{í\¶ƒcQ@yy§lë„ÀhkyóÁZnÀ Œ[¿eˆÀæÄ¦ ©áP@ßjNÒõk@B7ˆž8<…@NE¼â¼]À·Mü “t@~ØlâœQ@ *_A¯R9@IÓôX.]nÀß x¹fVÀgF%çñP@<aÒ<†À¯®ÿ”µÀTP¡ás@°$Ȱ"°Æ@"Jȇ96…À±šB±L¸ÀVJÞļŒv@9d¯âsÀÀ~bàSµÀ"Jȇ96…ÀŒ1#ìdÌ@¯fzß@'@›D˜ÛÁÀ»AÍÈ,Ê´@)kò¡žP@gÛ±+b@h¡]m”‹…Àù–"§ÀÒ·CfߎÀý@ïãê}«ÀüWq&•@+Ûœ&Í@Ov¡p@)kò¡žP@˜©=0ŠÁ¹@,DNÄ…À°†ÿ ©@;ñÙÌ@‘ÀÞɲŒK'wÀ…s£øï”@‘"ü”™ÂÀ`¤õÜÙTw@¨ö1òX@4ç„ØÀÙ`‹É×@“€—5á]+A&øFZ˜ôÁÝHï}ÀŠ@©š€ò­À™¡/thÇÀ£Ê|ãÑ@õœjÏ}w)Á„[<à&AýŠ“ØîÎ@ÉÆ<:“F@Aœ.€0®m@¯]$³NƒÀ­ökƒ¶¤äÀFå±P(xð@¢ãôÚpfÊ@šæ,†E÷À&øFZ˜ôÁ·KW¨å=Atº€gZ®À×äÍ’IâÐ@ :ë¬ç«Ó@ 0æ9ìÀ˜æ %>A÷;÷s^8Á³Æ<:“FÀýŠ“ØîÎ@ŒÌµZ†UƒÀG8Ìn'™@4`£ ¬|ç@€n‚ò{ó Á(ÏT¼wàœ@Á!ÎY-”À;"äP’ÀpyF]WQ†@æþ*À}@KlDÖ …SÀ"]*Šq¼À õE£6‡@uÍ=µÃ?œ@¨f`ws‰rÀÄ=¤Š!nYÀÍQÉ—]gƒ@„ŸTY„ò¡@~òåBãÀZC}›pQs@’M¼’ $"À²ÿ>Ô™n@u\ÉV`KrÀSèú‘|H˜@‘ƒ(<8úTÀÁ!ÎY-”ÀÐKpv Ã@6’<€à…@ÇifóŒµÀKlDÖ …S@æþ*À}@Óno[Ý•hÀïeÁÀ¨f`ws‰r@uÍ=µÃ?œ@»­Ð •ƒ@Ësþ,â­À‘òåBã@„ŸTY„ò¡@M¼’ $"@YC}›pQs@J^ÏmŒ!rÀKkóÒä @ƒ(<8úT@Sèú‘|H˜@ƒÖJŧ×ÀM· qg³@Ô‹çL_¥@£ž…2ÈA>N÷¬»uðÀ0m=5ÐÀ-LJxgKó@{Õ•ä"ÞÁA]¦.§@ËGˆ*k³ìÀr÷¢)غ@)|®ðM6Ã@œê‡Þ¼/±À°/Xo‹;È@ꕼ‘êÀ&6k€¼?Ö@ZG~dœie@Ùý*K±«@´orI¯ŽÀU,=·Ú~²@i%ÂÛ¯Œ@ðË=bŽ(˜@ Wà‹ åÆÀÊÉ£AUlÃ@KÇBM4ÂÀlâ «oÑ‘@M â²ÅQÖÀ´ª¢»È%›@>N÷¬»uðÀ’ƒÅäY"AfÞFsŽñ@ÐËj°æÁ¶Uº"”3§À×U7éÝÁŽî¡ðàºÀˆºÚűìÀœê‡Þ¼/±@)|®ðM6Ã@ꕼ‘ê@°/Xo‹;È@_G~dœieÀ&6k€¼?Ö@´orI¯Ž@Ùý*K±«@f1$s‹~§@ѽ+?W8‚@™G•ï+ºÀN^ˆWyÍè@ÔRWÛí¸]ÀÑcKªÖ¥à@5Ðò7sа@óµ 2–’@F I=Týð@}³¸â\HÆ@¾ì_€>òìÀõC.PœÒÀÀgÂ[oJ¡@> ü¹¯;ƒ@õøF˜ÊÚÀ +gO½Àþ•RíɸÄ@êÿ”=­¦@?÷eá’@BÞæ ‰Þt@}³¸â\HÆ@ÊýÛHõÞ@}òûÛÀÀ8¸m9ÞÀAŽ5bsÜ‚@þP‡VKûd@–g÷{:½ÀÇtŸ*5ùŸÀýZÔ ›¦@¹EO\ý¼ˆ@вÇ_ a¨@S‘èM‹À5a9i cÇÀ^¶Ò_tÏ¡À¡JᘌÀ>°¶­ k€@ìlÎë´@g3XeR™@í%fÁ,±@4ðáå·|@S‘èM‹Àõ \FIá¶@[F"¨h¢¢ÀgwF`—W¯À¥‘+I©E@8ԖפÀÅqÖT8—@gñ–Å|@ÇüD㛓@ =ÿ:äyq@ þv¸öªÀ£él»OêÀWO¥ûßèª@Ñ…kU•ÝlÀ&pVý@b@ ‰Þöå }À eƒnÛ˜À,ȼc‘@bŠ 8gh•@û¬ÈÂz@$5c{~iT@6TsÌ-oÀáôF(éÀ·—©^ƒú›ÀÑ…kU•ÝlÀêÉpcÉ °@4•€BÁ0}À¨ùðMJ—@ÐùhвG‘@uY[VႲÀÌå6¥z@⋟OB§`@àO”$RpÀ}6œÿíˆ@EŠ#ÇU7 @½ûì?ªHs@©¼ÙY® À…Åz;§OkÀ©KkBv‘@3^Ýä_þ?ƒ]´7”À±ƒ[>ž`À»#.{]^@¡@¼û2@‘ÿÁÎ$š\@O×–H[1@½ûì?ªHs@o¤šê9¦•@vÝ@ôÚÀaÀP¹T¸U›À¢^Ýä_þ¿©KkBv‘@éú3N‘`À½»tÀ£@¼û2À»#.{]^@Q×–H[1À‘ÿÁÎ$š\@1Y‰ž_ÓÀXé{±ôÛÁÀNYH’ª'Ô@7ˤÇɦÂ@ô"ÑQP@·ã˜™bƒB@"Ø£ ‹Àö/¥ˆ­{ÀIà—QÉÁÀWÂoø5ÆÏÀ7ˤÇɦÂ@q ~·>HÐ@Ÿ®U€OŒB@Ó™ˆ– 5@× ÔX̘‚ÀÏfçÊ@†zÀòRf2þV@(‡Ë <@ä··Õ8Û–ÀaÑæŒ:PÀÞŒLˆ@_yÕÿ¬“œ@Î$fm1{@Çÿõ0‹e@ ¾LTkB@E9OÕ\ Àª{êeçƒÀpÇ£0 U„@-ùzQÉ;i@91)Å«=@¾ÂQú"@ &VYàtÀ–©Æ°¡|ÀÇ’Oso@Î$fm1{@™ñT¦M’@Î)5`ÌK@¬ÃZ¿òÃ'@ôÁièƒÀ£ŽG›û¶hÀUÕˆÛ?i@«M(UO@‰ yT@Œ–F¶Cø8@"ÞEÂY–ÀlŸûEbÀKÞŠYäs…@Çÿõ0‹e@Î)5`ÌK@ ~ï'Ú@è«9A]­@§`äªôðÀ÷º GéÈÏÀýøñãÇå@'Mš4i’È@ßD_ö°€1@Ó©¿«ãX@ó‘°ÕªpÀgÕÕ}eògÀ|)â $Wb@ ¾LTkB@¬ÃZ¿òÃ'@è«9A]­@i ¨á4Í@uRÕqØÞÏÀµÀŽžeÒÀ„ *T¨È@EŒ1bĬ@M;Ðöx@µ¼]‚¢”–À§`äªôðÀuRÕqØÞÏÀKW7€#£A°8$®'Žñ@p…°6lÁ!2pêÀBóÖe•Àw¾oÔZ³@÷º GéÈÏÀµÀŽžeÒÀ°8$®'Žñ@3Ú¢"ÄA0:íRæ~êÀ3­éËÁ´RÂJÇ›­@>°F–‰E‘@/뻸r"@@%“ÖäÑ[ÀüòÕf2*”Àç¿ÙGWÀ‡ÏýÀ¤¾µ@®ˆŽiøV–@ŸÈ¥²˜u¹@$œƒ®@Nâròi ÏÀnfH”VÖ°Àjønž*VŽ@srÅR#s@ Q¼ù¦]@Ð 1–¦î•@G¦Žm–y@¯s¤&“ [ÀS*ΤÒQw@ÀÉCˆ/bÀh¹—Õ¥À®ˆŽiøV–@a ̬K @YãgÙÛ¢@eøÞ¿Š…@ÞÜT&å[µÀFp&Î ¤À¬bóE·xv@õð7¼AZ\@ÃÀ¦ŽŠE@¹S´p„Û@+†¾Á À@ŸÈ¥²˜u¹@YãgÙÛ¢@©yœëA’´æÚkä@S7—RR8Œ@z㢵ÒÀs¬C_I¢ÀÁõ:7pÄqÀÞDÚIŒ#ÁäzV­½äéÀ—Qï'=1¼@)¿ œÉ¡@ΠÜ,s‹@Š΀Pr¿@{â"KX¢@$œƒ®@eøÞ¿Š…@’´æÚkä@,cºl%YÈ@ªz㢵Ò@T7—RR8Œ@O6Ko¥qrÀV?À!Ày—À´N°æÄqêÀrŒÐ3´—ÍÀVœ?ò" @ ÿIivS„@¢nŽÅÌân@ ¯€¾ž@×þÞZxÀ½id;Rœ÷@›eÕ(Ù¾òÀ´l„9<‚»@A»Ï”·¥À–¯qkÁ·@ oÎË´‹ÅÀ 1OGÔT÷ÀÖ¸€>‹ó@F´Z©{î¤@)–Úñ d€À{œÍåR°@„˜Äs‘ÀO¥Ë4ÙÀ]”›¬tË@ÅÛÌ5q=•@-Ç;š™‡Àà®@Ñ-•@£ÉÖ=ˆ‡ÀÖþÞZx@ ¯€¾ž@›eÕ(Ù¾òÀ‘ËFm3LAB»Ï”·¥@´l„9<‚»@Xh»{ü#ÄÀ£^÷\DÒ@‘ë¦Eqó@ݽ æT–Á)–Úñ d€@F´Z©{î¤@…˜Äs‘@{œÍåR°@#2nü6/’@„G¶æ“ áÀ-Ç;š™‡@ÅÛÌ5q=•@£ÉÖ=ˆ‡@à®@Ñ-•@j|ìiÆ1Ú@(^uNÉc@^éŠÈ^ÁOJ8=éÏÀw«t„*ÒApyÌ5üÑ@ÿHægÑÀ§}î z¡À6^uNÉcÀk|ìiÆ1Ú@ëÅØšO|ÏÀÞ:*1ÁpyÌ5üÑ@ ;›U/ AHÓÒ Û³ À? ´Ê±¡ÀkÊ7–J©Ú@Z¶g»Ý|À]Û^Ô9EZÀ¾“ 8¾Þ|@kc\ÚÀ(Z½Z¶g»Ý|ÀâE_ÄŠÜ@x  Û7Þ|@iЏйŸÀgÅÎ0Ÿ¿ól?%ýŽÚÀÈäfÍ¥£À@•è˜M»ê@墪{ò—$@…*)ê'âSÀ—ë@ܧ:@L{Ò9ÑnÀd©:-£+¡@ô¤HàänÀ›Ïå:¢UºÀºƒwiUÎQ@ ƒ6²¶@©#Ú]ºf@£w֙ΈêÀêJaÙùö @Ϧi -TÀ+‹hàꈕ@k.¹ Y‘&À°ñàÖry@HÊЈ‡Á3ü]ÙfWÀ:S]¡D˜†@æÅIz§ŽmÀ%#fk‚¡@ô¤Hàän@d©:-£+¡@Á-ç¶4{À‹³ÿ½è³À©#Ú]ºf@âj•ˆyå¯@ó‡¹Q'AÄ`413GSÀH&Õá\í†@h.¹ Y‘&@+‹hàꈕ@IøìuÌQ@(ÖY¾Ë@@;:ãÈ£ÀÂKó‡pÀbÑóUH~@ç èÝ9(@='eOX•@·€= l@€ÁfÉØœ@·¿¹}À‚ÀÕš|’”À”!…ë˜@"ÄIÈÓT@á˜:ÛÅ?@Чód @@îx²xß,@¥¦2¼ÒF‹À„ã‹üøN†ÀHiwâHk@Ÿº’gèÓ@y; ð];ƒ@½®`ŽBWY@·¿¹}À‚À¡æyŠ$¦@v4t ]×@¿EC媠¡À!›iB@ú²R¼£ ,@5a9i cÇÀ[F"¨h¢¢À0(£°Q¯AÖ-†Ïg/Ö@JpÚªä¦@÷ètàƒ@biEñ,X@Þ{ÁnýtÀ¥24ò¿Ñ¹@ÆyW_¢ÅSÀÕÕ_+¬ôÀ&G=¬=ËÀÊD$ÍÅ@èÀ”˜_@%Ô¹À^¶Ò_tÏ¡ÀgwF`—W¯ÀÖ-†Ïg/Ö@³§|â8ªí@Å‘(]pSÀR„þ§^üqÀûg,²|¾Ò?+ïåI@` â!¬D@¨ÿCk–c@Oõ›áV*Ì?$´ŠQ®9=@®÷e0ßÄJ@NæÅôŽ4@<‡Ú‹¶t@¼¸¢“Ø~`@¦Œé¸Ho™@Ï‘‰÷Èw@cjÔãÝ¡@ 6{ÊÑv@G¡ŒMæ¹ÀJ®l¼ {À o)ýÎ@)%}D=˜@3(Ö1a@ÞãÿœHJ@÷dö¾è‰@U·ì{ùU@¡!Ȭ½¢À‡±î*ânÀAQ¡&È “@®=¡›%’d@ëÇ^‰¼·ÀÇpxJÔÀœ•÷ˆÀÀ‰@-_Ü#¬`@Hb i¯‰@Ë‹ýèËÄÀ®ŽÓ¤o”ÀÛF×,r¡Ÿ@Ýl&Øíp@Cx?:I¤x@‹Ñ¢†¤yO@ý‡AI¦uÀ~‚ö¶·ŠÀ)%}D=˜@5¦èœž@Ç¡ZRÎ`@LqjÉë,@^¼zÆGÔoÀ^ÆÀ;Àp¼TÊ¢vi@ œ¯:@¡ô‚°åØŠÀÖõõ˜ãaÀþº?6a´`@~tí ¡5@t®˜—!©`@*Ô#VB+–À¼ª‰ z–Àªœ8¼e„t@Bæ×HöE@5ävt±³@®æ’:_ü1À.ƒ\ÃãÀ€Øn¹ ÀÔ/´Õ£«ê@7u“’n­·@Z]PUÒÀðÙ¬}®ÀÙæ’:_ü1@6ävt±³@ËËaü Àp€o”¤àÀ7u“’n­·@íÏæ‰æá@²DÐËt‚®Àj3¬½À7úmg>Ðä@ž™zæ¬&¡@“Úz ÐÀMsìrºûa@ÚòÌô€ÙÀûðP9F¢Àž™zæ¬&¡@ÞmSìqà@!D þaÀè#À#e ÐÀ[¥XJÌ À»³¤—XÞÐÀ%êMif€|ÀˆqÆ*,Þ‰@´« =€SË@Æ€Ø- ~äÀFÄC¸ˆ…@ „¹•LÀZðÄ&À D@Tìe»8ZÀéçrLúcÀ§Û0’—í(@ÆÑHmAR:ÀN$ü£ƒC@2E/"qYÀØlÌ‹JR@u)7qÀZR_;/m!@ó);)ÈSÀQ›^Gw+p@\ìÞµ ŒƒÀté}›˜ÌÀÝÿ$¿Då@ÓRº³¼Ã€@ f©ÓÖ¢ÀÈúÉzu¾àÀ+ø.8°ù@ „¹•LÀ½JAà ¦@ ø;ü7)ZÀTÃïãq@Öm¬h¾z@`m%úD@ÀÌå%[Á-Q@»ÿ¶ÛxYÀ’%˜ Ôšp@g´H,àgÀö&O&¢x†@j A:¿6À(:ÍW}Øh@/–™úßЃÀêbÃ@fô—@ïûî ©…á@Å™êòyúÀ#gmIŒ¢@o#=*ƒÏÀ( Ž•½ƒÀk_Çeñƒx@¿}’™„e@´Ã™¦sPÀêpi©è‚ÀOqÔ¶˜@o#=*ƒÏÀÇÐ]:ÓÊ´@+y 8DFp@Ž/iql@•À´Ã™¦sP@¿}’™„e@é¨Ð!¹¶˜@tJ쯑&°Àî~¾D˜¯°@sQ¡C‡‹‡@+Ÿôê_ÿ}ÀF@Ï“qWÀÏ$]¥×­ÀgoÞÄëÀ²k]Ÿˆ0<@¥ p;ñvUÀsQ¡C‡‹‡@ ©²‚z¡@ÙNìÙWÀ†sC¥šßkÀŸuHœ¤åÀz$˜È¡Àŧ)UUÀh‡p°U>p@M㋸Mé@ÎJÙ£B¤À¯i¬è)Ñ@;Æ­=ThÀÍϯ\ÃÀ%hÝ D@1$› ˆ íÀŒ“„„J¥@ÎJÙ£B¤ÀÈ_“¦Q›î@>Æ­=Th@¯i¬è)Ñ@´-4ö7DÀI…9Xh\ÃÀ ºÕ¨Wà¢@50 ,ñÀüX'> 4¹@šW&£è£@ÛGoõ@YNþÙsïâÀáÓSùmÎ@·Xty¶@"!è qÐ@úe‘G7®@õáö¾Ç@›­“êæ-¥@Ù-ÈÂtºÒÀ³2‰,¬¶@ž¯O!ô¯þÀƒ¼uàÅÑ@UÇMè¹@(Ê ‰Å¥@¬-óŸ2—@YNþÙsïâÀ8I™ÊdAîJ(ÊÎU¸@ó½zbî¡@ZKÙw©º@,)´UVl˜@ãÒR…«²@wûb#,‘@3ÄÑ(ä·@&ŒÄh/ÞÀáÐéCòãÂ@-b¤QóZÁ"ípÉN¥@løªdõÄ@Öc·¾Ç¿ÀÐùjƒÿp˜À›æêˆ‚,°@2™©¹DçÁÀ¬´„Úp7¯@Öc·¾Ç¿Àâ¾WUîÜ@öoŒ•a°@ðˆÎC!®ÅÀ6Ì®cSÌ®@!\\pXÒÀ©¼ÙY® ÀvÝ@ôÚÀaÀô"ÑQP@Ÿ®U€OŒB@˜È䥕Þ@‰¿þ!¶‘`@BŒß›ôàÀ6øÿò l@&= Vgu@"\gÀxê‘ ¦ÀE6°`Àí=EêÓFÀà«Û OPÒÀ ƃ½)À@²ÍÕµùÓ@pm…Û¾À£Ñ¼ÕÊœÀ}Š´ ûŸUÀŒ$2µð:`@Ge®¼B@šÓj|âz@©ßQfd„@v›ØQ•G:@ýÓ'§C6@_½ì˜„5@ÜópÁ»¿@ßw›\jeæÀpm…Û¾ÀšN”Â’æ@Y3(lãOXÀ=,hî*×—ÀMyŽ£œB@ÙìâÕ$@S7—RR8Œ@ªz㢵Ò@•Õrwyg±@4bú—/õ‹ÀÜ|2É4³¥À¤A„È‹@"üXºÒ)¤ÀÀ+½z㢵ÒÀT7—RR8Œ@4bú—/õ‹ÀTq™OÇ»@ã¶È!Œ@½D®Äá8µÀ@>®}]q¡¿ ºÉ–)¤ÀsÎ&Fè@5æ)úm·@r×ó¸ërÆÀÚÿIK$/ˆ@¶*Îgh@:R5C§ŠÀ^:ÂâÀÒ«iƒVȶÀ5æ)úm·@ÇÇ_t1_Ý@ [M‡@aÕKsxÁËÀö2jX6Ö„ÀšÒÄt…Á¦@ê“Rf·À{Ì·VÒÀs¬C_I¢ÀO6Ko¥qrÀÜ|2É4³¥Àã¶È!Œ@§;þ³@5)¤heæ‚ÀÁõ:7pÄqÀV?À!Ày—À¤A„È‹@½D®Äá8µÀ5)¤heæ‚ÀŸ‚IÙ»@§qÁŽ{eÀÄÔøÃÀz€@x„ùÃ[À‹eO›¶y@Rsú~‚‘À9 `7C@œu®µ6I]ÀWÕ-†” ,@ ±D¹PÀT¾ÿèÝåL@è4ox:_À<°õJ“é@''*´ô3À$µ/Ÿ`ñ<@¤Â„³áaÀ©âEýxÀš°ñTcE’@.¢Wdwx@£é©4Ñ·™Àm"Ë^u@Rsú~‚‘À¹âö~sêª@}þ¨).]Àã)œ4TW²@áv)ŒöðÀ7Å‘D8ÃÀGbBÁÀÏŸ5ôŸ@[.œÒ>Ì@#ðóÜ]+©@TKðW²@^É`«›…ã@Éé“_ÃÀ‹™L…ÛÀéË  ’1ž@ìÿ=6˜ÐÀqQãX„=©@`¢9ë°†@ÌÙId.ÏÀ\%Í7y:¶@¥4ö öÌ@Aòˆ:4þ´À€a:AEÖÀ ZP‰ÐŒ”¿åOæT›ˆ@ÝðËÓ&YÀá%7ÉZ»Àøê[[´zŠÀ;8C@8÷@LÕ€ÇÉÀuM—à@áÀ9¦þ*#®ÀÚùbŸÜ@9ˆ‚n©Ñ¬@ì\Ž«èåÀLSº|ׯÌ@XëCãÏÀ@ø E<œ¿‹ßeQµ¡@_<4Úlh@ òæD~µÀV‚¼Í0ÞÏÀ@òˆ:4þ´@¥4ö öÌ@€ôO‰ÐŒ”¿¨ç€`+EÖÀú8&$®l À<Æl‰Æº@“Ðé¿‘ŽÀ“o2çÎ(]ÀLÕ€ÇÉÀÜ–“îƒÖA9¦þ*#®@S:J(`@áÀ9ˆ‚n©Ñ¬ÀÚùbŸÜ@Îwm…ßÎ@ÄO[X{äÿÀpJ½ß¯Ð›0ãÏÀå :ã'€s@Á¶an»å:@aI" :,iÀÞѧ÷oÌ@ú Ä{?žÀuM—à@áÀ9¦þ*#®@ ‘qmnø0A»|Ð'Üð@ì«­ª,¼ð@«´€)3žž@£v«¤Ð1ÁË;bb°ÁñÀ uO'Š˜½@ú Ä{?ž@Þѧ÷oÌ@9¦þ*#®ÀS:J(`@áÀ»|Ð'Üð@ç„ð ^"A¨´€)3žžÀí«­ª,¼ð@ëÕ(í3îïÀÓDFH¡$Á$~¸z»ç¸@=-Q*×°‹À»õ1˜Î!³À‘ÞNMÒå‹@ÓvÀŽ@‚®Ë1w~ÀBê!èNªÀ­á­£êYÀV0·½;Ä‚@åÄ2±°r@°_qÍl>Z@=-Q*×°‹ÀÄòŽjÔ”À@5îýæF}‹@ê¯DC9d»Àd®Ë1w~@ÓvÀŽ@Û Á@1tÀ;_æ’ÚZ£À©ÏtÎ)k@q¥· Z@y‰óö;þB@OõÓͳ9×À}æ,í³Àý Ü„WŒá@øšš— 1À@T™Êwö½ÇÀÓ•»µªÀ˦Y ³ÀJo 5Ý®½Àøšš— 1À@#/Uˆ²À@²Õ.sƒªÀ #x½¨Àº\×Qy@´ó$òãœÀIîá(LqÝ@Vѱ°ßêÅÀ cûGV“ÀšÂ[’.n@Y”/þÝÀþ ,ÉŽ„Í@Èû3¾³@á8=ýÕ¡ÀœOàß 7Ào"PäªÀ@Vѱ°ßêÅÀí b=kó@šÂ[’.n@šPœ‡%¢ÀÔz2¦J¬Í@øZÎ3Ï÷ÀV:8Ï(J¢À¡Aém“Ä@°„Õæl@þE§@†yÀ~>€úô‹`@vb(È}‰ƒÀDb6¶Ø@¨²s¾•±À$A[k A&_:[,òÀ+„vJ³ÉÀ6  qÏæÎ@ ³»ëÛ@‚¶ÿ¢Y¨@S«)ùÒÜÁ&õlþJÍí@„0ê_ˆÀ1Å][–@9ÍKS®|ÀÝÝ6 î @¨²s¾•±@Db6¶Ø@&_:[,òÀb_¹¸½ºA~…€:Ð@0Ï ¦ñòïÀ€¶ÿ¢Y¨À ³»ëÛ@(¾ë4)ì@Ã,žÂ'ÁÇ2tÒ-í@rv´NsÁÀƒ¼#>s“ãÀ=¦Š%QÃ@æ‰ßá­ÔÀGg%‘€ž¬@+ÐC/ˆ@ eåÁˆx¢À»Ç*0V!‡@>ÏÑ[S¡Àrv´NsÁÀéž]5[¥ô@ÄŠÛsŸ=Ã@¢P zðÀ:GV2­@ºLdaL—ÞÀn †“‹f¢À½O þN¼@WÀÓô¡À1êtDRY»@ƒ¼#>s“ãÀÄŠÛsŸ=Ã@Ê3G‡èÝ@ 1¹“-¹ËÀú¤¦’×>Å@×2—þ´D@ê´X|@ÔU”¯‡B•ÀU›W|ö&ÀªŒ Q¶@=¦Š%QÃ@¢P zðÀ 1¹“-¹ËÀß}Ž ñ@Å2—þ´DÀû¤¦’×>Å@NÍŠáB–•Àú()²0°@èÊ¿Æ _¶@öµ²œ[ùÐÀæ‰ßá­ÔÀ:GV2­@ú¤¦’×>Å@Å2—þ´DÀë²¼íÝã@7³ZíóÃÀm.³ÅëÝÀ·ólÀpv¹@Gg%‘€ž¬@ºLdaL—ÞÀ×2—þ´D@û¤¦’×>Å@7³ZíóÃÀFÍ¥bYÈð@e„ë•[m¹@’úàH¨”çÀ«$G¥ßEä@TkÚñTÑÀõ)‹–Ah@ÆÖ¾þ¹›R@Ä×´ìô•@t'UÐë¹ÀÀó×L§º²ÀÕªÄtÀ W·s¶âÀp¢Ay Ø@TkÚñTÑÀ{6/7Ó¯ó@‘ÀÀ®˜R@‹uaˆ<@U,§q¬ÀŠ_?”2ÊÐ@Єd–âêtÀ#è²–įÀ Á˜­L#Õ@§ÖTæöÀ2¡í³o@«F]ÍÎT@“YîÌF@ù>±¶Õ cÀ” Ð`ÑÐ~ÀšŸVc"-ƒ@!ÊÕ+Àg@¸ÚF¹„ÀC8ÿ8ÈmÀa­9ÕcÑ]@«F]ÍÎT@ª»6«9×v@3’òvåÓ\ÀÈZ&ýÀÿx@‰õ‰er@)•2–ÀM7ôtÀ’~r§t’@©¼m¥vP@«§Ær“f€À{&Õ°×ã@ç½W·¹Èx@¸’A[:AÁ@ˆÖznÞh @p0Ôò‚À_@µu®ìA@Þ;•JÂÀtéÉ£ÀG«ehëOx@ðÀ8cåb@ˆÖznÞh @n:°@\[aNÆtB@ÀòMC{…À_Áô®¼£ÀÉzeE¬À@– €_f@ óW˜;ç[À÷[>ƒ›ätÀ¾·Ê×ûRÀ9¾_T@3#½C=6?@8d´ÑWEÀ óW˜;ç[@9oúÛ¦]@ß¡b‰§WF@ óW˜;ç[Àýç®gñÒz@+·ò–îRÀ’(à™§dÀâò©+?@›©wˆà'@ óW˜;ç[@áÊÂ>rÀãaìØ’GF@Æ# ‰1@ÜlÁ7•@a,/F¶)a@2e„»]£‡À°iªˆ@Ù ç€Z»|@ÙÉäXía@è uáDt¤À–¢#Jüw@+„vJ³ÉÀ~…€:Ð@9=†ðøÆ@ì)áÑ'ã¼ÀXáz7§@<ù…ݧ(ÅÀÔL§«Ò÷q@Le¸þh=@y2“~¡ê“@Kƒ'qAª¤À¿4Š»ˆUX@~©ô ^>@ 283’Žc@±ÌÊdM©À6  qÏæÎ@0Ï ¦ñòïÀì)áÑ'ã¼ÀjÓ².Œtà@*¡Œ§3ÄÀg>–YoVâ@]É>ìŸêÀõf¾OG»@:ÌžËBuï@‚§+ÓKÀ½ÀµÉ»–=ž@—(­TÀj@`Ò¼¡l©@^çé òŠ@ŸÖ^”ÕÀ•_œéËc¥À–Ý1H²@&jN‡tç›@EAë°»r›@5UpI!Ï@CNbÅÀ@?t 8eñÀ‚§+ÓKÀ½À¿AQÿÐó@¼è?¶€@êaŠ4‘M@»|<ÀŒ@Pnr%Èm@C˜¬’¶ ®À‘债p(ÅÀ³UéËØ4”@áêØe×~@«:˜¨cV~@üd›&”a@DÒ™U¹ØÀ9¼†‚?±À²Õ`L¾ð@évÑ”Å@ÿ;ùzË@g”4îÈTÀBƒ W¯êÀµÐ¼`OF¹À‹¬ß4Ö±Àäú¡+àÃÀévÑ”Å@o ¨A)Ü@f”4îÈT@ÿ;ùzË@Ö#¬íÞ¹ÀÙW‘e?ößÀÝHï}ÀŠ@tº€gZ®Àáv)ŒöðÀÉé“_ÃÀcž#ºù?ú@¢/öjÌ@ßÿÏéIÓt@ˆêß%º–Àv…ÿƒ'ãÀá‘øÄ»„@©š€ò­À×äÍ’IâÐ@7Å‘D8ÃÀ‹™L…ÛÀ¢/öjÌ@kS¿a׿@Ý¿bÿe—À0h‚>m¸@DmýŒ}‚@¿[”LˆäÀ³N>¸6™@µéÝ´ê3À"Ìt«ÒŒ^@\ÿË`ul¯¿,ƒéÉ]p@½ï¦ÿœN@Òñ¡W…À>‘(]pSÀ3(Ö1a@µG°Eد­@„Ód¾M0(ÀÏd`Ä€ÀQ¢¥|ÖVC@ÅÓ®ðL9¤À¿£«¨Mú;@'çÙ“%D€Àd–¦|2ÀàJ«Uå 3À 8=›RmI@=Ì`ul¯?"Ìt«ÒŒ^@£øfI…Y@Ž™Æf7@z[Æ€$cÀR„þ§^üqÀÞãÿœHJ@„Ód¾M0(À&=¨øqà­@ÓŸJÙYP@Ä}m•p„ÀÒøɳ®<ÀqÒµ5¤Àü=²¾À{ãèt[è~Àr×ó¸ërÆÀ [M‡@,7-—ÐÂ@Ê ±8»¡ÀØ+»pέÀäcðG¾ ™@uÖÁá+¶@]¬ªƒc¼SÀÚÿIK$/ˆ@aÕKsxÁËÀÊ ±8»¡À:q­:¿Ñ@ãh‚¥#–@^5Ý•™ÒÂÀ^¬ªƒc¼S@uÖÁá+¶@ —b˜Æ±Ah,ü}Äê@û1Géö•}ÀâÛS˜.ã©@T®òÁ(¤ÀÐ=ÞIWï ÁKÈŒå£Á·†ï AoÁéðr÷@h,ü}Äê@®TÓ ríè@âÛS˜.ã©@‡>øÀ¦ÖÀèöZ‹Œ’¿0;€ö~(¤ÀÌÅpܹÎÁy1²y5ðÀq;l÷@a W…ä@ƒ«1×ÿKÖ@U×€;*¢@‰Ó³?QÖÀ$)ºü‘½‰ÀÿœƒÕkk‚À%Ñàîí´x@· ç‹ñÃc@ôOCW2h@/c8h0ŒÀ_Êä:FˆÀÖ„z‹4TlÀJ:ѵ7j@€áÂâQ@U×€;*¢@‘;›|«áÇ@aÖó8௕À…ŸÕ n¿ÒÀ‚n.²¦@…¤îóÍbU@ƒXö×A@”°|èøŒÀV+§WYà°@ÑÇÿAÚW`Àd–ˆ9²kÀ=Ö! Í™F@øãa¤M¥-@‰Ó³?QÖÀaÖó8௕À44òýïÙ@£l7Œ!å…ÀH½.v†À|¿ñaÊˉ@k𛍧€¦ÀÎHp¯Þ@©As¤‚Š@«'~áóf@$)ºü‘½‰À…ŸÕ n¿ÒÀ£l7Œ!å…À¿ù„á@ñSà&Ö&’@š¹…%ÐÀì¼ZŽ+ù”@p¡ ^ÜcÁÀ¼æìoÀª-×Er˜¶@Øoð°qÁf@ŒÎ[¯¬³C@_žbÚµ•„Àí“Çà®@H½.v†ÀñSà&Ö&’@q† ¸FÊØ@ :Ü?ñÒØÀ•Mùïgs@{í½u'·­À¦Àâ–ö¡ÀƒÂrv´Ø@~l¸+WCÖÀõB}¶›¥@ÿœƒÕkk‚À‚n.²¦@|¿ñaÊˉ@š¹…%ÐÀ :Ü?ñÒØÀÎVnbùA&µ^öhå«À°‚ \¤[å@÷‰¸1zmÀ†,Éæ/‘@‚´äÄ}Ö@Fñžš$TÁY:ÎçeG·@Ú1¾Ç˜]ìÀ( Ž•½ƒÀ+y 8DFp@Gå/«c:Œ@g‰¡äKÀŸõîÞqwÀ´Ã™¦sP@í…’ªÝY@¾"'QçpÀ+vÀ¥V d@k_Çeñƒx@Ž/iql@•Àg‰¡äKÀßJ‘i–@´Ã™¦sPÀ ,LÆciÀQ}JyêpÀLï!V†@–ÏÙm9´_À¿}’™„e@´Ã™¦sP@ŸõîÞqwÀ´Ã™¦sPÀ¥á&qÈ›t@›cÄÑ`æ€À´Ã™¦sPÀ¿}’™„e@´Ã™¦sP@ ,LÆciÀ3¦×f•w”@ïlUÓÌÔÀé H]ŽR«ÀÐùjƒÿp˜ÀöoŒ•a°@¡#ÔÜÑÉÕ@ä3þVM–‰@yÿ¶%B@S¶Ê¬—ÀT,EÒR«ÀA7·?^ÈÀ›æêˆ‚,°@ðˆÎC!®ÅÀä3þVM–‰@ÙÄ7ÌÓ@·[EŒ×–ÀÞOøá¢U¯@c%‘Ѧë)@1'æÆ)XÀy¬ÙN´K¡@þl ·Â‡ÀíQΪÏGÀ™nžÒ¿8v@1}ÊEjž@ÎWû‚ÒLÀ“ë§2ò“_À–é"в¡ÀLÝ©Ë d…@þÇXXÀ¯j«¨x†@þl ·Â‡À .}¢`¾@ó¿õ‡ 0v@Zæmaµ¤Àu‚{)ÅxLÀ¾MòÛÍz@ÂæÕ]@î%û0ˆu@µƒ£¼&¼À%Ñàîí´x@…¤îóÍbU@SÌÒ\)A"9 A‘gªûð(ÁöÍ/¤r5 ÁRö‡1ìÒÀód°µ="¼ÀÛ±&ŽN@š?ñ<ˆd@· ç‹ñÃc@ƒXö×A@"9 AØæ®Ô¼°Al²¨áÀ/ Á®ÍdÊörÁªÓ½Ø E½ÀSi”oª¯ÀIޏq@ i@â2ôcÛlP@w¯Œ]•IÀ‹*ù¶~@Iô±íM°@qÂM."MÀmEWÐP@í-ª@„À¥ùçºsXÀq&¡º ŒEÀ­œ6/¶Y~@ë×)+!@2Q8˜SÀWG’‚%@>PAOËXÀ8\ç5ž~@e ¯’a²ÀW¶=1RÀ>ñr;E@í-ª@„À⯘¯á¸@#ã í’ýŒ@¼’'•3“y@íì‘"C²Àùw³îZTÀ0ß „B;‡@P}´½€YÀ=‚Ì¡he@áÓSùmÎ@îJ(ÊÎU¸@8MS¾|Û@ ­iT¯@ ƒl´ÄÁ²@-G‘.‘@–9 ÛDª@¿Ÿ²In)ˆ@ó|å‡ò4ÅÀ÷w¬8)†ÀýšGôÚäÀi­~Š©EÆÀ?U›ô@·Xty¶@ó½zbî¡@ ­iT¯@øé@¾ï%Ò@°˜AMJ¤›@^vRy@ÛR5‘([“@¨ž”¯Íq@û M73ŸŠÀ[xƒ^ SÁÀ-I&˜*vÈÀgh}È-ÍÈÀëJ³éÆ…@-¯Ýâ<,öÀX7–ƒÐZÅÀ¡u8ŒžK…@ϙū¯gi@Nâròi ÏÀÞÜT&å[µÀÞDÚIŒ#Á´N°æÄqêÀP%M9AÕááN’ñ@FªÉgâ ÍÀ$‰oÝŠ«±ÀÁ€Ìà67—ÀK—Rà+@š—7 BÀ™ü‡~c3@v/¥¿VÀfŸµ‡qrÃÀ ã¡ù uëÀ˜–yŒo@¾Ð®›‹ÑR@nfH”VÖ°ÀFp&Î ¤ÀäzV­½äéÀrŒÐ3´—ÍÀÕááN’ñ@—Eœ4ò@Âóè¹î|­À±ì¨îLšÀѵÚ6Õ¾À£…F•.BÀv WñKÄW@`{ȳò„IÀÅÁñm@\§9ì´@Ð|'RU½XÀ4þn¡Á@4þn¡‘ÀmÞ§¸d°@¢ÔØÃ, “@²É—²×à¸@u¿•RÇZÀ|¥¢Ñ5æÀ1Á²ÆÛpÀjønž*VŽ@¬bóE·xv@—Qï'=1¼@Vœ?ò" @FªÉgâ ÍÀÂóè¹î|­À»L†êݽÚ@6æ‹ys˜@<ÙhÝ?1u@[ èÏç`@Ó|'RU½X@\§9ì´@4þn¡‘@4þn¡Á@z¿•RÇZ@²É—²×à¸@ýƒà2_¦À!«ì8Í-àÀ6æ‹ys˜@±gEX•7É@=Ñ—´®²’À[÷¦;U&‘ÀgÛ±+b@,DNÄ…À!‚?9•@ÓSû¶÷Âx@D± TrÀ »õâe„•@[÷¦;U&‘Àüœn¸šuÀh¡]m”‹…À°†ÿ ©@ÓSû¶÷Âx@iüAuTî°@ó–„a»•@¤‘Ýbƒ¹À7ÙY;óúÀûïWk8–à@—Õ ³(Â@ÓÉö®  À—Õ ³(Â@ÓÉö®  À®l*°1ö@'ú=&%ÝÀ7âþ9Ÿ@i[Y ÄY]@´a¿q¥ÑžÀFÃ/¯¶sÀ;.¬_y@ªòFßE1@cFX;Ñ|@–霨d@* ŒuàÀ÷NjqÎ÷úÀÓÉö®  @—Õ ³(Â@ÓÉö®  @—Õ ³(Â@'ú=&%Ý@®l*°1ö@i[Y ÄY]@²™\çš@ži›é” sÀæ‚à]+“À«òFßE1À;.¬_y@ ÂPæ™»d@kAÀýÛ¹M@»õ1˜Î!³À5îýæF}‹@çF÷Éb»@Aâefð…ÀŤ#i$ÛµÀÉvdÐôr’À\HsÅ(°Š@Þ-ºªÜxõ¿Õ_=¥@5(þØù‹Ž@‘ÞNMÒå‹@ê¯DC9d»ÀAâefð…À“Ö–/7¬À@¾6¾áZ’À#‰eÄG¥À.ºªÜxõ?\HsÅ(°Š@Ô-'%Ý@[Ӛﺦu@jóÛŒlh@jóÛŒlhÀká5ÁH zÀçŲ7˜²b@ã¦DÛˆ@"R2ÂU@n߯õŽÀiâΞx¶CÀWE¥ê\@ÃdÛlÉÅð¿zÇX<ãƒt@ùÚGzj À¶=¥Ÿçí~À¶=¥Ÿçí~@÷x"ì–2|@ò»4X½ƒƒÀ"R2ÂU@rÜh?54‚@%×¼áàGÀ}¿ÙIŒÀËdÛlÉÅð?WE¥ê\@ôÚGzj @zÇX<ãƒt@éÀ{siAHG\IÇà@·dÔº¡7Ð@£h·uº@”­œÜ¥@ËkvÒ>"Ž@†[Û†íóÀÿ©,!VqàÀ¨A&&/»@€•»[¯œ@ݬÊ>„JêÀB•ÑÿÔ¿Àæß1¸´ÇÀó!Ÿ…B+”ÀAÞ µïáÀëYçå­Àf;#ÇAAµ@÷ßp~lp@™ûDä†TA@ jéé»@ë’)wêž@€…"åü¤@T@bЈ @6²šéj’À”Š"µ_AÀHG\IÇà@<ªÒœ{kî@[¸V–ª×º@/ÀDÕå¥@o¿6Á=ƒ@„ЂOšTj@¶€ê’”}àÀ•Û³t-7ËÀñ,÷ ™˜@æþ”\;y@Z‡ÎÁvÁ¿ÀNBƒqÏÙÀO¢LÛd+”À#«a)Z¹ÀÇÊjˆ°®Àž°˜ÑcÒÀtÕk’’@˜ûDä†TAÀ÷ßp~lp@=2Œœtœž@ì»Ô0ô€@¦6y>ÃV‚@Ë/®i¸l@˜ûDä†TA@iW6ÎÀ·dÔº¡7Ð@[¸V–ª×º@r‹øiËfë@ºß£¾ÀÑ øuÈÀûª÷ñ¡{§@ÓMdƒþ£îÀ+šâ«¢¿¦À7E /ê£@Ø‘Qº:s‹@£h·uº@/ÀDÕå¥@ºß£¾À±ò9¿ñ@ C0C•§¦@Ü\²ÔXÑÑÀتhŠM“¦Àê±^çÂëÀBÁ"U@i]³¬mÔv@”­œÜ¥@o¿6Á=ƒ@Ñ øuÈÀ C0C•§¦@$JªÀz\• Ï´›@›mµ‡²[‹@rŸy–©çt@ËkvÒ>"Ž@„ЂOšTj@ûª÷ñ¡{§@Ü\²ÔXÑÑÀFLJ‚¦@Ír*mbÂ@oŒ pX®À* IM™Â@‹eÃÈ@Fmc™ÈÄi@ÙåÐ@²ÀFÅ­.¿“À8–¯Iƒ@¬j‡]ßÚr@œéå¥Ð\@†[Û†íóÀ¶€ê’”}àÀÓMdƒþ£îÀتhŠM“¦À5ßÙxá<™@oŒ pX®Ày U¡mA›ëìLÌã@ÿ©,!VqàÀ•Û³t-7ËÀ+šâ«¢¿¦Àê±^çÂëÀÏÊï®À* IM™Â@›ëìLÌã@Ó‡ghêí@¨A&&/»@ñ,÷ ™˜@ãn Ùóö¡@‹eÃÈ@¼hˆNê@År饭Á@M¸ð$GüåÀQ*\èáïÄÀHÄ©Kw±@Lak"?¡@‡) ![[Š@cÇ¥ÌØ¬ÙÀ²}ƒFõšÀ»D*i“ž@Oú…à;k@€•»[¯œ@æþ”\;y@ˆ¶Ý求@Fmc™ÈÄi@År饭Á@v¤±t¬oÔ@Á>Z×áÆÀ'bÒ‹ÃÉ¥ÀûDó  -’@ë+˜ò@‹34„£mk@Ž‹ˆmá/”À6g'ïeÔÀQú…à;kÀ»D*i“ž@ݬÊ>„JêÀZ‡ÎÁvÁ¿ÀA ŸðC ð@C_V¥.øÄ@–NÒ‰(ÇÀm’¬]¤ÀB•ÑÿÔ¿ÀNBƒqÏÙÀC_V¥.øÄ@2ÄJ9]åÜ@ÿø.òº6¤À¶ÁH¨Õ­¨Àæß1¸´ÇÀO¢LÛd+”À¿²»© É@øÓn]J ’@}åŸÒÂ{‹@ÊúI¤l@ÒÞmÿ Àê#ïœ9WÀ,œf#˃@ó!Ÿ…B+”À#«a)Z¹ÀøÓn]J ’@(ÚQM¢*¾@[Àn]õîh@ÌP×øûI@«‘ê1ÜiÀû¶ %”À%AÎýôa@žS½0´@&q¬˜ÒŒ@0m=5ÐÀfÞFsŽñ@´l„9<‚»@B»Ï”·¥@yµ^­oè@…Š`¸÷Àd(jS´y±@È1Ö¸M¤Ž@»ä/A‘²@AhãB7Î@ÿÀsÝÈD»@d}v¹p$”@Æ'xîAÈÅ@ VÞœºó”@£¦Ù&Ï}íÀÇÈø'€>É@$(1ÈlQ¯@0z›ù…€ÀÏ üçb:¯@úöÌæÑy€ÀœNš^ÓÀh6Îqh³@&q¬˜ÒŒÀžS½0´@-LJxgKó@ÐËj°æÁA»Ï”·¥À´l„9<‚»@…Š`¸÷À&lªžäÕACä¥Ò‡„@²î¹×ÿa@IîB>Ð…@Ù;üY¾c@d}v¹p$”ÀÁsÝÈD»@ VÞœºó”ÀÆ'xîAÈÅ@vjBœíÑ@­xM8*Á0z›ù…€@$(1ÈlQ¯@úöÌæÑy€@Ï üçb:¯@A̓"(­@ˆ¡ãúѬåÀ˸4ê5¶u@¦œäøÛ?›ƒ–6wÞFÀ[áÔn¸€1À™ëÈ9«r@?Á£ë¾S@ñ—pŠŒ@‰<×ô‘H@B!·[ñ¡™ÀDìâµÉ1À³ÇnÑ µ_@(í+«>HÀ¥œäøۿʸ4ê5¶u@[áÔn¸€1À#6Š¥ÊÀ?Á£ë¾S@ô#$×^@b<×ô‘HÀñ—pŠŒ@oR[mNZÀ&¨¾¸.|—À(í+«>H@³ÇnÑ µ_@Ðé#—uv@V¼ÑáAa@0pú’¿[@– L¤E@n߯õŽÀ%×¼áàGÀTþS/ëª@ïNFNÀw4o((@˜uÞ¢“?ÀŠF¾ŒÀŠ+¢¼a@’‘HÀPßì)£À`räjYÊ`Àé ТÐ'a@ŒCÅ·à[J@ò%œ…ÚE@~Ö&X 1@iâΞx¶CÀ}¿ÙIŒÀïNFNÀ0û‘+qù«@M«ÆÝ?À¥œéù²—T@ AµT8W@ö…q(ŠÀìêçõ__@ñXÌb§aÀaþÔ:Wš À"!è qÐ@ZKÙw©º@ ƒl´ÄÁ²@°˜AMJ¤›@g,_E¤ŠÇ@ĺHø»š@0z#¹@–ze¢LŠ@hb'Ç££@æ)Jê¿+3ÖìêŒÇÀp‡â~U>ÀÀ­ ÅãßÀ¼ 7ËÄÀP@-¯0 @úe‘G7®@,)´UVl˜@-G‘.‘@^vRy@ĺHø»š@o;¼@xˆ³@Ë2ņd‰Š@„ÓF›§@P æ)Jê?hb'Ç££@þÈÊ+Á€À ƒÞZ<‚ÃÀ»b(C6½ÀŒld?: £Àdmo‘P©}@d_Ö@>A€iî[ †5Á¦ËoS:óÀr†žzUã@éÔç²*±@iH½…Ë ÁEÒ@¤Ø4A]²âšÓ@ôºÌ½´@€iî[ †5Á‚ Qu‚`A£{jøÓè@q—.žÈ³ÁéBÕwg”@F¹ì™D«4A[!Òæ_Á(zؾqu°@ÍB“À˜Þ‘@`¶ß>ïjÀÈJH(hš@æÒ&†@…>AÁÈï[fT$ALh$“ò6@%Îá¯ÁüMÀ€„>ÁWUUUUX$Á:k9Äœ@”lŒ‚š—ÀÁÈï[fT$A”H—I AQMqà"NÀóÿòj›c@WUUUUX$ÁÉqÇq Áñ³•ë³Á‰À4ÔÒ* H—@€ã²úCnâÀFI;˜1¦ô@àu™nB@º‘-ª¸¤•À­.`·Pz@ÎQo=ä•ÀZðÄ&À D@ ø;ü7)ZÀiËÔ ù¥Ã@¶ÌØ÷æ-Àœˆ0kûhÀ sæ*,/@2å©)u@À磫Tÿ¶£@‹ÚäpTIºÀ╸} gH@,ÑÕ–¨Ð_Àõ/#ü7u@¡V7Zâ“À2Ãt³Ê%@¼µgÍWÀ:ÔÐsƒü×@+ŸÜ–býñÀ´œ@¤‡‘@BÆEýµ°À Áì ùñ@š4<Ò Á8ã}Æ,N–Àóœíøù«@_ß°TïÞÀH¡¶6K¬@Tìe»8ZÀTÃïãq@¶ÌØ÷æ-Àˆ§v@"È@¨­t«W€@í¼×[dDÀ‰6-!ˆU@2-ÔŒ{¹À#8³«üÐ@pkó4í_À,ΚÑöÏt@߯hÖÔƒ‹À2­{†ëÈ©@ÊÅùêÌ‚<ÀÿÄ-¢3$o@ÎàóRïÀÚè6>@AŠÃ5˜ýš0A‚Nò"xåA.¤tZºª£@óÕöXàOÀùXÀ‘_0ÁáÞêã6wÁ±8)SMPÑÀù¤ÀY»À‚Nò"xåAÈ&q2>AéÕöXàO@.¤tZºª£@AyãtÁ‹56c8Áb þ,.c¼À¾n˜ïl¦À^alÞ=æ@·O®Íß”@ —Aì­ÙÀ-°þýŠ”À®0+—ÐÍÒÀ``Þ¿Á•>@·O®Íß”@•à=BC;ã@©c+F4–“ÀF´?ã¨ÓÀ¼»àáª>Àý†µÍÒÀ:ìwnª·Á@Jëšå}n@5ºä×Z@õ7ïÒ0À¢Ä9zæ A(·+yzqÀðÖ¨k*ýÀ’—O^x­À^[ÕÞ]¶@W1£ ¢a@ãb(dC½Ì@Âõ”jû)@…J¬8¸@<»ã…Àî…Z…ÁÈ5%šÈ¯}@ï0âz°äÆ@æòÚj@è“À#nñ£œ¼â@Á&ÛPsMÀSmÙ:IÎà@m»/‚à­@%ÎTç²u@=Khî rªÀ§ž™}ÒwÀÞd¶¢kñÀЙâµF›·@àüç žË@fTDOþGÀOëšå}nÀ;ìwnª·Á@Æá5Ò |ŒÀ´Z'Ia¿@(·+yzqÀj “"u Aï¡¿kíJ®À ] 1ÝôÀY1£ ¢aÀ^[ÕÞ]¶@áõ”jû)Àãb(dC½Ì@<»ã…@…J¬8¸@Ó¤ªs1Ó§À6ëþV8™ÁæòÚj@è“@ï0âz°äÆ@¬&ÛPsM@#nñ£œ¼â@m»/‚à­@±Ò4œ¬Žz@›Tz…S§À„­ÊmÜ@Šžžà›©@ø¢îZOíµ@T)ü!XþÀbTDOþG@àüç žË@½ã<ªsi@&0¹¼ åÀ¸mÙ½±ÎH@‘_àjDaÀ´a¿q¥ÑžÀži›é” sÀChÉŸ¦@õ?ìÌÚq@¹Ö cb’À1Ø ÊTd@)0¹¼ å@¾ã<ªsi@ÀÜaMaÀ‡Â>})x@FÃ/¯¶sÀæ‚à]+“Àõ?ìÌÚq@Óræ×c;¡@%ý}hZNd@!ç6µŒ˜ÀYTç«è·±@ êWø\sÀL?ÞÙñg™@õŠ—„W@C °å@:Z÷üeÀC ‚²Øª@Þœ!žKo@gKðÇìÛÀF<_J$n@4°F ;Ü¡@îîØ@9|Àì{Á侨@[$NøòU@ÝÝP¢“9¦ÀÈ#½óÅ-Uþ…º@èš­ e@óÅ-Uþ…º@èš­ e@ êWø\sÀXFtÓš·@õŠ—„WÀL?ÞÙñg™@:Z÷üe@C °å@Þœ!žKoÀC ‚²Øª@’o¦w@Àб’ÜÀîîØ@9|@4°F ;Ü¡@ù#NøòUÀì{Á侨@¦ ü]¿oò—H9¦Àëš­ eÀóÅ-Uþ…º@ëš­ eÀóÅ-Uþ…º@€•„ÿqWÀÆ®Gnr¤@Àã/ñ¶OE@NE¼â¼]À°{pmk”o@’'Ñø¤yÀê|ªñŸ]iÀ…‰aÛ“@Æ®Gnr¤@ÀÃ%l/ÿ'ÀÎý,‡.}]À·Mü “t@’'Ñø¤yÀNmð&^“@o®2ühŒ@qè—+R˜ÀPªv‹åª@~D¯U.(ÀqSŒƒ&¶ÀIY5Pj­…@ñ—pŠŒ@b<×ô‘HÀï@==[Ñ@$£«@å¸ÀÀ(€ÅJLÑÀúzcYÀ@žšÌšš“@h/>Rõg~À¸D¯U.(@Pªv‹åª@ɾ"Àdc‰@à¶vCW¨¼À‰<×ô‘H@ñ—pŠŒ@$£«@å¸ÀÀî7¤Âá@þmuN¼@ßμêàÀh/>Rõg~@žšÌšš“@E9OÕ\ ÀôÁièƒÀ&å⚣±@øÆÅ å•@Ú{ðØ>£Àü‹Bú×á‡Àª{êeçƒÀ£ŽG›û¶hÀøÆÅ å•@Ññæ‡-{@GˆÜâ‡Àþvš2¤mÀxúnè¶ÂÀ(Ûé5a‘ÀÔVä¹Ñ@V4]ÐTpÀ1C=FoÁÀŽ0ºPÓv•@IÃF‚ÊÀBÛLê:¶ÀV4]ÐTpÀ°ó9¥©Ò@ËØ“¶ß”@>ýƒ5ÊÀ.¤tZºª£@éÕöXàO@í]j‚ãvA0‹Õ„^Ál1ž´Ûo|À dAã÷ú£ÀöþgWOQÀ+A¥o`À¿-koµó-ÀAbÖ¾U†S@#”“Òáß=@ÿÿÿÿ_ãvÁdffæ„^AóÕöXàOÀ.¤tZºª£@0‹Õ„^Áñ†n‘™*AdŒRÚ<[@ð¡Ä/‡d“À„ÚÇ^&mK@cGiÅÚ£À…ÄÝKBÀ®"Ñú-‡u@¿-koµó-Àª¢ŒnÂWÀ#”“Òáß=@¨WbõÚ&@pN{Ú€.CÀ9-.¯m@ѯ$ Uÿ@ÀÅØ{ÑÃ*j@dffæ„^A233ó‡*Ál1ž´Ûo|ÀdŒRÚ<[@åöA4¤¤@crÔ¬ðuÀPG7žÅŽ@Ë­­‹·À ÿC X¢À\s·òGY@e}Ur@Z—W\ÑG@·lyJke@" Ùó;@?Œ1ôæ Š@J™$š°Àq’¬î©TÀqe¦C£@·›>¹w@^eù=ŸÀòŽ<›Z;`ÀFüߟV$l@õfoW/&>@àÑxµ8 @vVJ†¬R@ë1y–¡~@°­ªKP@%žÛG¶0r@ /*ŽP@ULF{s@y^QbŒI@ð¡Ä/‡d“ÀcrÔ¬ðuÀ>7ƒç ´·@°­­‹·@QG7žÅŽ@|:»Jl@­ë‰B5(®À³AûçkG@ìºv@¼¢^礮;@Š% `"@kl˜‡Ó`@E×­­¿~}À"oÆ3ù”ÀN ¸¥'s@qñÒN@C>xðReÀ™.•ŠÀPŽÀø™x/B@*h¾„{@göœ˜'V@ãv™Þ¢d'@;ûh Ç}S@>\ç™+Ý$@ /*ŽPÀ&žÛG¶0r@( ¾i´GI@s¿-_k‚ @&pVý@b@4•€BÁ0}ÀpÇ£0 U„@UÕˆÛ?i@Ú{ðØ>£ÀGˆÜâ‡Àâ¦ü©ý @GbKKè‚‚À Eˆ;!1†ÀDö8¡@)={Ii@F>ÇQPƒÀ ‰Þöå }À¨ùðMJ—@-ùzQÉ;i@«M(UO@ü‹Bú×á‡Àþvš2¤mÀGbKKè‚‚À{¦Uƒ'®@Ñ&Áûд¡@ð&劰ë»ÀÄ&f-„ÀOÐßiÑž@ãO^4Â@´ž(ó¦@Ç—OÑ̯@ÍH±;¢ç‘@Åã˜<úÃÀ‡ÝÝü&®ªÀ­ ç«ã§Àî?Ùóßâ„À´ž(ó¦@«áÕgAÒš@«³¿¹á—@¶‰:z@»˜ñ¸;•¬ÀuÌå “À>#0M›‡Àÿ?ôS¤¤ŒÀÖ2_c&Ì©@›ë0Ý2@1C=FoÁÀËØ“¶ß”@§S™ëÊ#º@ûO!¨¡ñ±À¼š9Cî®Àþ¼(MƒM©@Ÿë0Ý2ÀÖ2_c&Ì©@ª'T_º/iÀO€6R#œ@Ž0ºPÓv•@>ýƒ5ÊÀûO!¨¡ñ±Àåœb@+ÛÔ@ºÑ†U}œw@lÁðÕBí²Àâ]”M©@ü9”+0ÃÀÊ{¹K@ fÀ÷µƒÚWš@+ž•š‡ÕÌ@-°!=wž@l'6ÜÀ§Râf"ŸÀºÑ†U}œw@QD”ëQÒ@û8m׈Àë¯ûtåµ°Àµ·»9@•€M>Ï™@z}N˃îj@I/¾‚0S—À¾ î}ÏÀlÁðÕBí²Àû8m׈À=Àñ$Þ@`v£Q߃@YÛóJ!ÄÀÝû¡õæ·@àͰ/i›À'’õ6¿ÀQÒØç6&@¸¹ä'··ÀPÃxä<›@àͰ/i›Ài2êA‡GÃ@ÛG…׃%&@(3ð`=ÀPÃxä<›@f žb8ÃÀƒm2Ê;‚@! Òçë™é¿ eƒnÛ˜ÀÐùhвG‘@ Eˆ;!1†ÀÑ&Áûд¡@“`k•I¢@ß‹“?›´ÀT@d!zƒÀ³5´Þ À@Ÿ Òçë™é?ƒm2Ê;‚@,ȼc‘@uY[VႲÀDö8¡@ð&劰ë»Àß‹“?›´Ànélù£ÕÐ@…È´÷÷\ž@dAàd0·Àù–"§À;ñÙÌ@‘ÀL?ÞÙñg™@õŠ—„WÀá›ú¨X€¼@Šmmú8Ž@-ÞE®¸ª@B~®ÿ®qÀ®oø] åª@ݧTè¸I@+Qr.‘ÝÀÖ¿/pÌ÷™@%h¡@Ÿê&f3‚ÀÅf½é~¨@M _úVfÀ‘ú‹bŸº@Dêïð¶hkÀ‘ú‹bŸº@Dêïð¶hkÀÒ·CfߎÀÞɲŒK'wÀõŠ—„W@L?ÞÙñg™@Šmmú8Ž@…täÀ´@B~®ÿ®q@.ÞE®¸ª@ݧTè¸IÀ®oø] åª@|o¹Àï—ÀH<,ÞÀŸê&f3‚@%h¡@N _úVf@Åf½é~¨@?êïð¶hk@‘ú‹bŸº@?êïð¶hk@‘ú‹bŸº@’ ¨ÌÄ@L¹œÆ|™@ ÍjQ°°âÀ|e(Ñâ£ÁÀUD“{ô@“¯΋Ð@jMÌø¦8ÔÀÇitب“ÀaFÞ½Õ@CnhnÝzh@¯ha€·ÖÀ¦Ç˜Õ9B¡ÀfÛ†ÃÀ‘”ã$ôV§ÀM †/ îÆ@‰DuŸ2±ÀØ u³Õ“@@ëô”†o@ãŒðu™=‘À*wD %&žÀ`Æë.f³@²áä]{˜ÀºÄq‹°@0#`üÔ>ÃÀ³BF@õ5ç@…H¤€ºÉ@uWõˆEéÀ¨oîÇ+”@»&[úÂÀùpŒ<¢g‘À‘›9rY ³@nyϵA¯ñ·P×<«@jìUwç˜øÀUÙ#²±À¶ú¶PJÍÁJ;ñH$†@ãçÅkûî@Õ‡i!d@¯ñ·P×<«@6…-&WAk÷}]'o¤À1—©¦ñÀi$Æ…e.†À(Ò†gÍÁì‡i!dÀãçÅkûî@R­`qÆ‚@}Ö¿ô;@ò4V^@ÞÕ‡p^¦@§\RØPPšÀEç¬@T°Ñ‰¤v@»£ñ=^ÀשöC@ a@´ÚÒ•²À ‘»ù•@È=¶Þ¼T{@.°—ìd@|¢…pð¹l@'Ìܳ#$@ÿh4L@§\RØPPšÀ·¹knXº@!b^Ó³v@!'TsfRa@G¼œ»A"Z@q[êÈŸ£|Àùx¦'å[‰@Mc…“B\¹ÀVIèd@~L¥zP@AÞ µïáÀÇÊjˆ°®À7E /ê£@BÁ"U@Á3Òÿ£sÉÀÙåÐ@²ÀM¸ð$GüåÀÁ>Z×áÆÀ#¦úGîû@‰’Rã*Ô@ÅeéåÿÎÓÀX•aTxyÀÀM./ªjM¦Ààe•W¦¶@wuð ’—@Î3ZªaçpÀá߀©  @¥ùçºsXÀ#ã í’ýŒ@ëYçå­Àž°˜ÑcÒÀØ‘Qº:s‹@i]³¬mÔv@^mEÏ>JªÀFÅ­.¿“ÀQ*\èáïÄÀ'bÒ‹ÃÉ¥À‰’Rã*Ô@÷’i‹“à@’•Ù£¤Àh!œ(ÓÀôý3€eÛ–ÀÕ"säÉÇ—Àwuð ’—@ûÙÖcU‡x@‚õ?ÛérÀåèÞ#–¥@PÓ`T³±wÀ¶º¶gP«@[Î&1 @«Ö饤RÀ¢K‹R h”À¯/ðú2}@q&¡º ŒEÀ¼’'•3“y@f;#ÇAAµ@tÕk’’@z\• Ï´›@8–¯Iƒ@HÄ©Kw±@ûDó  -’@ÅeéåÿÎÓÀ’•Ù£¤À-·ð’õË@pðÞëE%°À¡2ÃYI™š@µHrûS„@¼]}Jl”@†ú$Øo¬Àó¸ uDKŠ@<TÍ>@Œe€=ãqÀòÀn~ÚdÀ }aw}­À¤Z&m¶@¢ÀŒÕ†fRÀ'ªgŽÀ†@ŧUkþŠ@3ôòN½À­œ6/¶Y~@íì‘"C²Àh!œ(ÓÀpðÞëE%°Àà6Rèëî@îÙ<úäa@W«31æBŽ@7«2ÈÀ‚›5ÁqÀž˜‹¬Øž¤@ÞhÜ ˜@ @Zþ` @LèëAB•×Àö/¡åñ Àƒ‡þ‘”©wÀò\ø@תZ@(¬kiHB@,K{(²ñœ@çB@…v@åÏK8–&ŸÀ—"%ó‡_yÀŠYØκw@hë0ÇGà`@¾Ä„iü”`@qÿh“:F@¶``ÈYh@mœÂŸR@;ö½qv@ËÐ.wJ¶`@÷BY\ xÀÓò»¡L½aÀ·²^UþC@é§=“³i+@çB@…v@n£kw ÍŽ@ó(æØuhyÀJV^ã BÀc¿o%ó`@KP!AÔH@Ìð>õÕÜH@Zrz)ª0@§’`qeAR@ûÅì«ì;@oNµè•`@÷’«ºÅI@–¯qkÁ·@Xh»{ü#ÄÀ²´Î$Ç×@‰Œ°´—ÈÀ²júëå`ºÀÿå4=­¸–@ ,ƒuõĸÀ9ùV¾@øú­ÛßïÕÀ”Ü×ñ†ÂÐ@¿W¼Šq¢@Í(~:l@$C‘#¦—@È|åd@—q¬é0 ƒÀŠƒrE@‘@„Ðø7–ƒ@«VJs9 Q@Eá zZ“@Eá zZ³À oÎË´‹ÅÀ£^÷\DÒ@‰Œ°´—ÈÀU ¯¶Eé@д‚j|@3”Ú³ÅÀ®ëFžœûº@ve¸$éßÀLÃR§Ô@Ü›É?èÀÍ(~:lÀ¿W¼Šq¢@È|ådÀ$C‘#¦—@7ùX$ï@·¨¾Z30©À«VJs9 QÀ„Ðø7–ƒ@°Kr4µÀ°Kr4Õ@ÐD’­³ü´À†¯·Ž4šÀbŠ 8gh•@Ìå6¥z@+ÐC/ˆ@n †“‹f¢Àm.³ÅëÝÀe„ë•[m¹@¹[Sbc¦â@zæ ki–Àå{L"Õá±Àüß½ô–ÀíÂn:¶tÀI›|[M§@Öö˜à;šÀ´ÍrÊØ`€Àû¬ÈÂz@⋟OB§`@ eåÁˆx¢À½O þN¼@·ólÀpv¹@’úàH¨”çÀzæ ki–ÀS%ò‰çÒä@VB¬i΃@°¢.PèÜ»Àüß½ô–Àe Âç[v}À¾Áé"™ærÀäU®,ᩪ@A×E)žú‘ÀúS‹v‹Ã@úŸ ûM¤p@ô ²64ŸÀVB¬i΃@Ýj]§¦ç@0†ƒç­¼À“SÅ9c@€õàKm–À8™Ý’¶2Ï@PZ¹ì f–@͆ïÀß UN”Àð‘y®izÅ@ ÙÊle÷ÀøÓjº×¸¢À¸&Ä>JÑ@°¢.PèÜ»À0†ƒç­¼À n·•ý@ý=? •À/ýrô:É@A£²w–@fˆiIÁ_@Ê8„e8'”ÀÄiì‚Ú èÀÓ³$A÷X¬@ݵ-¶÷e¹@å”wÅ…@²júëå`ºÀд‚j|@,ùú€9äë@sc ßÃ@ÎÜEýÝîèÀ¯Ý+/ïÀúÛëBçÀÏ® }©ÀÂ&-ÉDÜ¿@Fx«.¡Š@Â&-ÉDÜ¿@Fx«.¡Š@Øí¥”ø¼@ÔúIÁÊ‚@®ÿ&^É÷Ò@ZEgZö–›@ŠÌËà%\³Àd¤ +õ\XÀ±õöVx@.´ÂQ9‘…@pšFz|R@ÿå4=­¸–@3”Ú³ÅÀsc ßÃ@Xºè*×Ûë@WßÌÊrÀ½lM¢À“@ƒ0·qÖ ßÀ0‡‚[wªÀõôµ…“€ËÀÞ¹Æu’ë`À¡×s°Vy@]±áWþ ‹@×ÜwÞãœV@]±áWþ ‹@×ÜwÞãœV@ðonèà™ˆ@`ƒz‡8êO@ºw~öv@µâþ›³e@JÍ5X@–ð1S†V³Àí?(…ÆÒ@ ý§€§?ˆ çÓÀ´‹”þþ“ÀÞ䉶@ ò íàþ“@–§5 Z·´ÀàIONS¿ ý§€§?ìîytÆÒ@7q„B£“ÀÁfÑØl¿ËÀSw$£“@Ó{DÆq@&=a$ K9·´À ,ƒuõĸÀ®ëFžœûº@WßÌÊrÀÀ¿¢çýA‘ÈY- ðÀ¹D©Õ@6Íuf,Àoô°J@BbüycÀToÌ`ޤÁ\ ¯:­sí@9ùV¾@ve¸$éßÀ½lM¢À“@‘ÈY- ðÀU¹Ø¿Ô”&A j"Š£Ài[~æÑ¸±@Õ2MK lÀ25„G\V…@§j¿ë®í@ò"83È%Á 1OGÔT÷À‘ë¦Eqó@øú­ÛßïÕÀLÃR§Ô@ƒ‹ó@ݽ æT–Á”Ü×ñ†ÂÐ@Ü›É?èÀ¸Ë 8– ¥@6Íuf,Ài[~æÑ¸±@'§œö%öÀìJÄx~ A`Ü$'–\~ÀNTà$Þ»–@4ÌÐ]my³ÀÉ·nâ†Á@’ļSÓ•Ø@ÛGý7>ËÓ?Œn¨dsªÀPÏG£<‡„@ߥé׋@ˆðŧ—tm@‚¤ÿkV&¶À¨$‘@«V|»ó‚@sP[ùD™À†Å““IBÑÀ–÷CNGwÀÛGý7>ËÓ?€ âÁÚ@Fâqv¶qpÀaS3^ºÁÀ,5òt±PÀËüB†œ$ƒ@,5òt±PÀËüB†œ$ƒ@Hä<ÆÑZÀ°à Ðº!‰@Š @ìÿêg@6QeÓMI@L[~s.¢@ˆáÈ$ôLÃÀÿf1ž<œÀšË¾¹Ò²@ {•KfSÀF-Ì­á†@ z]_KÀˆ¶¡ÒôoÐÀ»'À‹vTÀ[X*%‡ ˆ@÷dö¾è‰@Ç¡ZRÎ`@Œn¨dsªÀFâqv¶qpÀ8Öo1¹@¢‚ìëï4 À6îFýèû@ÀxT“–Nt@þg&~3ÀVbˆuž˜w@ë&W†è‰@œ,õb[Àq[Öm²À¼×' u@XÎbΓ@Ýìˆëc@ø¡s5Š@VÆÁÚ—q@ܹ3!eUÀ7ù(e¡/‰Àâ«“­ðˆ@ÆC_P›@KØÍ‚Þvw@׊"+ðˆ@]€Îq%`@²AÁº w:@Ð(QÌ&oÀ#É,`߈@0£·óöY¼Àq¾+ŒÿvÀ jó£A¡ž@G@p‘ºdp@)[ÜJhÌ’@ú¤­Yˆ_?ÀU·ì{ùU@LqjÉë,@PÏG£<‡„@aS3^ºÁÀ¢‚ìëï4 ÀPÃ,'À,Þ@¬T;]ûv@d·¸ uz«Àß¹ÛeEz@ ·œV‡±±Àœ,õb[@ë&W†è‰@d–Gàm@4ùa‹½ÀÎ!tß`@g¨ÿ_å0@VÆÁÚ—qÀø¡s5Š@ý)³ÄB؈@UtW¥ˆ@è’Dã1/ÁÀKØÍ‚ÞvwÀÆC_P›@Êîo&§&U@NQâMáb+@wä”7oÀˇz_¢@ôØ iU@=á{¥+!jÀÿø4 €Ì´ÀOkUÁvúi@ËÅ3qÎÎ;@ü¤­Yˆ_?@)[ÜJhÌ’@5Cg\PÀ-˜–Ç•B@rÆ®o³OFÀu+¡ð û?³ô”Õ µ@¢nÞ?0k3À"WåV¢`Y@ë‘‚¥{2Àü^yÉB@ÖwebÀu+¡ð û?ö.Çu4VÀ¨åj³~j3ÀyOûÄbI@ë‘‚¥{2ÀÖØ”Jg@²ÉJRÒÌ‘@Ó±%z Ës@¢ñtÞãî ÀRÿ³S¼ñ‚@$L Õ„@oàmt¶MR@óòxîe@ á`ì‡ÀJ½îSNz@„_(„À.1JÒT·±@ DÖÙŠ*ÁÀÇ9_Ì媺Àó¯À˜|À5¤yÐ"<•@„/zî—Š@ø`¯èlm@KâVÏf‚@ú±óo@))Ïj#Gn@öºîaƒÕP@–5’±ÊŸŒ@ì¤,Hb²Àoàmt¶MRÀ$L Õ„@z¼w×Ó‡Àͨj_þ©@~_(„@J½îSNz@ DÖÙŠ*ÁÀû9¹~{ã@£ÿÛ.ÃQÀ“ߌ¼Õ±À¢³µ´lr@‰ôPgif@¶Äý¹I@J‚pM_@Œìô¤ìG@F;»ÏÀ@.°SÀhwáÀ-ýìba@, h [’J@+Ÿôê_ÿ}ÀÙNìÙWÀ9 `7C@}þ¨).]Àgû¶—œN@ `†®V<À¼5ÑÔÀ׊f5úUÀ¼8ÖÓf@ü¦ N3@º¤çãjw@¾L=»Ññ^@xCª¿yÀ¯=Ç=ç*]@` „@Ñ‹˜ü±&Q@F@Ï“qWÀ†sC¥šßkÀœu®µ6I]Àã)œ4Tí2Wµ^@\›JD@º²˜üF]@ÎIƒü°†ÀÑ‹˜ü±&QÀ` „@½éþ >Vý@ö®oŠOÏÈÀ¼5ÑÔÀ׊f5úU@9 À³GÁ@€óÑ£rƒ÷? 0†åXâýÀÔ¸Á3>¼È@Àk ¤Š0€@58»Ó.ò¿6 ˜ª.å›@_‚w“×g@6@ínÔw@¶fn©Ú%@0ÁÂÀÒ#v@Áÿ<)#@üËõRÀÀ:°æBpÀö®oŠOÏÈ@½éþ >Vý@׊f5úUÀwø¿»qÀ€óÑ£rƒ÷?´²ÍI+ÃÀ@¬Áwb™¼ÈÀˆhpíáýÀ@8»Ó.ò?Àk ¤Š0€@_‚w“×g@Ó¹²ýº`4@µfn©Ú%À6@ínÔw@Àÿ<)#À/ÁÂÀÒ#v@F¥ØÑH°^ÀsiŠðɳ¹À,@#¢ã¿ÚÀÙ1®r¦ÀÁÿ1ðX„®Àhihp.¸Ë¿,5òt±PÀ6îFýèû@À¬T;]ûv@¤­ì"g|Ò@"“,ø¿@-¡ ©¹³@;MAUqkJÀÓš¶"ûT"@I¨¿«ªWQÀ?gåõ8‘ƒ@:®0_Oð¿±a,¬Û‘@-†ÜæÃÀŸèbïŠ@•árrÖã¿«I^Æ@›Ó P”OÀéþ;wÑš@õ·ímqÀÓµÝk…“@«/daCÀ"+#Õ=@M/Þƒ.¦PÀx£”óߨ›@Ç×…(­R Àã=ÝnáÀ¤.Àý± ÑÀ‘fhp.¸Ë¿+—ú9|®ÀËüB†œ$ƒ@xT“–Nt@d·¸ uz«À"“,ø¿@©ÆX•¯Â@;MAUqkJÀ"‹õvŽ­µ@>ÄTE,ëUÀx_"L¼„@\®0_Oð?>gåõ8‘ƒ@.†ÜæÃ@±a,¬Û‘@TárrÖã?ŸèbïŠ@ l<øPÀÃÔMYá‚@ ¸ímq@éþ;wÑš@²/daC@ÓµÝk…“@WÔ2¹.âPÀx†~9èƒ@Ï×…(­R @x£”óߨ›@¨† —zïÕÀ¼Àžb@,5òt±PÀþg&~3Àß¹ÛeEz@-¡ ©¹³@;MAUqkJÀá,í!¹Ä@æÝæOZÀÓš¶"ûT"@I¨¿«ªWQÀ?gåõ8‘ƒ@:®0_Oð¿±a,¬Û‘@-†ÜæÃÀŸèbïŠ@•árrÖã¿«I^Æ@›Ó P”OÀéþ;wÑš@õ·ímqÀÓµÝk…“@«/daCÀ"+#Õ=@M/Þƒ.¦PÀx£”óߨ›@Ç×…(­R Àø–˜%¤é'À0j ߘÚÕÀËüB†œ$ƒ@Vbˆuž˜w@ ·œV‡±±À;MAUqkJÀ"‹õvŽ­µ@æÝæOZÀéw6“÷¨Æ@>ÄTE,ëUÀx_"L¼„@\®0_Oð?>gåõ8‘ƒ@.†ÜæÃ@±a,¬Û‘@TárrÖã?ŸèbïŠ@ l<øPÀÃÔMYá‚@ ¸ímq@éþ;wÑš@²/daC@ÓµÝk…“@WÔ2¹.âPÀx†~9èƒ@Ï×…(­R @x£”óߨ›@Ë–oÔ//h@~ØlâœQ@àÞn DÅ@A^¶ÛðÊÚÀàÔmWd“Àå}¯ó%rsÀŸâÒsF8ÃÀ z{Û@{í\¶ƒcQ@ *_A¯R9@A^¶ÛðÊÚÀЬ3™ó@õm…ÞˆdsÀ¦1=ežœ~À z{Û@Ì]œÿóÀó–Ð/¿Ù@³úÓ e¢mÀ¿7y¯Mƒ®À—h\ˆp@avNÚùÁ@«b{9u& @™”5¸@[”S„“…„@Äï”õ\HÄ@zªÜ‡f¤°@(âxÂì@ø¬ÌÄt­Î@@ûücûÀô¼×Ò³/ÖÀ0 +ƒ´@³úÓ e¢mÀ8!:P{Ú@nðâu6j@xWCº2²ÀUchD=þ¤@:ÿˆØLPƒ@3:n‰óœ@¡|°“ Šh@½U‡lA¨@Ö®›uéæ“@$£oc2Ñ@î%ˆáW²@œ¡˜Dû+ÚÀ VšŽÜÀö4Ñ.”˜@;"äP’À6’<€à…@ˬºlÀ@©uôVuä“Àhxp–SÔ…@ÛzA: TÀW™Z'>;@¬Ë±:ZÉdÀv#x+ÆÀ%§ÈL.‘@÷ú!5”c„@iÜ’'‚ï@{‚¹L{¼’@N±%ï`¾:Àâ¹4¼DSš@3ΓãèdpÀpyF]WQ†@ÇifóŒµÀ©uôVuä“À~f*CŸÏ@}˸pqWÀ`f~¸Š*“@12\tµeÀM®Åæs@"ÿž÷•ð€@6u“ÐÀvÜ’'‚ïÀ÷ú!5”c„@Q±%ï`¾:@{‚¹L{¼’@3Γãèdp@â¹4¼DSš@î ¬÷:Á@€ðDÅ+Ö¸À¡¼„ŠU`i@sµßLƒÀ„va9x=‰@ ÿ§9ù£À‰¸Ýƒ°~@sTáŠ÷F—À=B½Õ‚žÆÀé[ê –‘Ï@LZïy"šÀŠÈ׿ Ñ{@‰5áþ L•ÀrĸEÞb@¼Žœ+€ÀL‰“~À?iÆ5Ò÷“@¡ñ˜ fH@ì} Ñ|À ªªËÏW‘@ã2æÿQãÀ€ðDÅ+Ö¸À ¾¹½¿Õ@0޵ÚÜ•À3¼¢Ï˜@Iøè®wjŸÀ~‚{³Ç·@ÏKµLm“ÀYù¬@ÿ(™knËÉ@ábæ"êÂæÀ2x­jÀC°@ ØÀø¶O‘ÀÎJç,‚ª@2[‚={vÀ’äÙ~ ”@àλPg“@½ËŠ¥PX©Às‚f^^À - n‘@Uô &–¥À+·W6=™¸@õáö¾Ç@ãÒR…«²@–9 ÛDª@ÛR5‘([“@0z#¹@Ë2ņd‰Š@Ù´ƒ+yÚ@pvn4ªd†@¨Ù®·‡v§@Ü–õ64@yÞRŠÒ7ÕÀðS/ô¬†Àž>ˆ»Ì¯ÞÀhAR‚ϺÀH c<¬–@›­“êæ-¥@wûb#,‘@¿Ÿ²In)ˆ@¨ž”¯Íq@–ze¢LŠ@„ÓF›§@pvn4ªd†@Nò¼ÿo•×@ –õ64À©Ù®·‡v§@96k†Àú¥pNøSÒÀË<ëe€7²À}{îßÃÔÈÀ‰LáÛt@Òqý«±½À†Õèi‚ ÀI÷@ºÞB@Îd‰P÷Š^Àñx+âþyw@gÕ¾ ¢?b@Ç9_Ì媺À£ÿÛ.ÃQÀ¥¦'|‡©á@kŽïàμ@ yä@#p½ÀFÈ3+›åZ@¢oþ·JxÀ®+Q³×à¸À\ìJ8¦ŒÀ>º] ù°À- Péü˜—ÀÈÇ<Ö6V`@êâ‹÷"ÀÀoΈ±ÀMÉÖ×ï—À±ÇŒ>su@ÝoÍÄ e`@‘Û?œu›À)\⦵ˆ~Àç‰bþà/fÀÃÅØd²ô@ÚAç8‹ða@vYóB«ãK@ó¯À˜|À“ߌ¼Õ±ÀkŽïàμ@–Ge3+5Á@cp¶=šÀAóðg¬ÀûÊ´7&NxÀŒëCTûò•@’à=tˆ„ÀMƒ^ñvI©ÀøÑŽÀÄDÁ‰ÃˆÀåvåy^5ƒÀÌyéÞ¤@ÍÇ3ö<À¹JÕQ_‚ŠÀÝoÍÄ e`@h¶ýéýI@m‘t_˜ñ—@éåi¨ëŸz@nzíŒÜv@Ûn3ýæÝÃ? G΢§"Õ@^¼†È}¡@aFÞ½Õ@Ü]ÞÓœæÞÀ›m¤·¤À5¤yÐ"<•@¢³µ´lr@ yä@#p½Àcp¶=šÀß±ùÈ$Å@«YxòÎy@ÒÅ*XCâ‘@òö®ioÊs@'.›p»Àˆ@aç·\ër@ʼtz“‹@­$xLu@áÄÉ i q@é˜\{ð™<À¿DXõÉ%˜À]޽½ø;@o3ýæÝÿozíŒÜv@ò¼û‚W¡@r‘?(´l@CnhnÝzh@0«› ¢¦ÀløƒKð°ÀAóðg¬À«YxòÎy@fŽJõíÀ@é˜\{ð™<@áÄÉ i q@f±>2µ¨<À$¶äˆy ˜ÀÕ —¢ñÄÀ,‚ RУÀžã\´Ìx@úcv…+ðÀë&W†è‰@œ,õb[@÷:"õ@gö.±³³¯À²8&*n·ÆÀ@ŒiØ»œlÀÖIˆu6?˜@1qv„@òmñÀ3ÁœÎûûº@¾œ¼ö¥‘@ä§«‚Ö]@î,“­e˜ÀíhÙ„Ç@’À*GÑ}–²¥À{—Ç&2º@œ,õb[Àë&W†è‰@gö.±³³¯À†Â'ÓXû@¦šL.Äa‹À…+:§>p¥ÀÝ6$^„Ëu@g=žžb@'ÞzM{àÁ@œ¹ŠüÀä§«‚Ö]À¾œ¼ö¥‘@ׯA¦äx‡@ÞÆq•@e@Hä<ÆÑZÀq[Öm²Àd–Gàm@Óš¶"ûT"@>ÄTE,ëUÀÓš¶"ûT"@>ÄTE,ëUÀ²8&*n·ÆÀ¦šL.Äa‹Àÿ8¶$­Ï@´.åñq@…œ9Á`6&@—Oˆ3ZÀ}:)ŸNš¹@ý‹ ÿ¢B@‡Õín'@Ȥ% [Ày@´®æ0¼À.O[§,pÀ°à Ðº!‰@¼×' u@4ùa‹½ÀI¨¿«ªWQÀx_"L¼„@I¨¿«ªWQÀx_"L¼„@@ŒiØ»œlÀ…+:§>p¥À´.åñq@ÌvÛpTÀÇ@äQ´YqUÀceÙûȈ@QÉ[X*VÀ†?_¬["Š@ò.FŒHâTÀîÝb|*ìµÀ©KkBv‘@¢^Ýä_þ¿BŒß›ôàÀÒõ»¢õOlÀļŸò'ê@]Rl$Ê©£@BWà6–\‹@‰æ°øt‡`@Q±eáʼn@™z‘©Úë^@:•"e$'ÕÀÚÊüáà£À3^Ýä_þ?©KkBv‘@6øÿò l@{<|¥íóàÀ]Rl$Ê©£@S›,ªÓxç@‰æ°øt‡`ÀCWà6–\‹@šz‘©Úë^ÀQ±eáʼn@_",n£ÀÉ)Ð}”ÏÀÈ"É犚@ÿØ ÚÇ*À ]ØŒ=ø@¬ × u¼@œ“¬E"¼ÀýWOÉœN„@nÛ”åöÀäi›Ò@¾ÀÿØ ÚÇ*@È"É犚@¬ × u¼@¶ñ0@žò@b¹Zâ4@, Bµ ûÁÀXD-DuG¾ÀÊTÚaöÈðÀž¦R˜™š@àe2íÜ…@ÙîösNá@ÿ£øÉñEÇ@R…øàyôðÀ×^0êŸs×À.ÁÝ䉭á@´lP‰¹Ç@å=<# ¬@Íï<1ly’@q–ˆYxTÂÀ…N±ùQ1¢À­äyét@Šw¾y ¯‡@ô¦ÊòUgx@ÙËçËd@ÿ£øÉñEÇ@âÃjB©¸@Ïé)DÙÀ°ÉùIyÁÀD„Y ðWÊ@cµg'±@Óâ,Fð”@Üçs]ć{@ åXÊ.xšÀNCO¦þÉ£ÀÍš”D{@Ìç”+mºe@¡¼„ŠU`i@0޵ÚÜ•ÀR…øàyôðÀÏé)DÙÀîf÷Ÿw A m’^ä@U¨­Ilsg@:Å÷AÀÀTêˆ@“ƒ\@üt<Ί uÀ6ù SääÁu8ÓÕÐÉÀáû×EÃÀÕ”'^s@­¦ :1HxÀQ 5>ØY@m˜ÉsÀÞéISÈ@@4fê|Q ^Àƒâ¨Q«&@j 7iŽZÀܦÓp@•†¨«\‚ÀsµßLƒÀ3¼¢Ï˜@×^0êŸs×À°ÉùIyÁÀ m’^ä@Éž 0Ó´û@MÓûÕÀ ƒpXd›@©"ʯuÀ³y)är@Š ýÅç)ÍÀ©†¨MùÀHuŽòþèà?5LæÅÀà³çÎw’@þÆd¨sÀǵP*\Ž@×õ©ä†YÀÜÏà~Úv@<ï¹Ê=AÀ|V\(‰Ês@·>RÄ‚ˆÀúÕPî›@}Ô„‹@ÁKˆŸÀ1e¿×±ã Á¦Ð5UuöÂÀµÉ»–=ž@¼è?¶€@2_€Õ’Ao‘À‡±¬Ù@. 1|æUþÀgÅXzê$¥@oÐmwk¢­@­N9ãNh@ÖÔEKï¤@\²€I´ÂÀ„ ufåÀǨ%T½À–ðÀ„{@ÉíÞ³‘ÀçSQ1º@åQrnü¨“À!7ÂPþŸ@‹i Oʉ‚@j €xŸ­ÀnÕmbõÁ@C¿4¾À {«zˆëÁ—(­TÀj@êaŠ4‘M@o‘À‡±¬Ù@ìvãâ%®A5Ý1¯RÒ¾@®”§lQðÿÀb8L7z@™Åí”È[@pj£ÀÀKܽÉÞ@zå½Fw ÀÀ ÖàSô’À%ŽÒ`žÀÌå Ê[³@l’Îÿ³Àò'ÍûüÌ@£ºpGMl@Ô¿&÷2fP@œ“¬E"¼Àb¹Zâ4@dRs–@Ó?HŽÀíkæÓëYÀß]üÑ7 w@ˆçá" ì¶@*û5X~ËH@ýWOÉœN„@, Bµ ûÁÀÓ?HŽÀAÊ£¿¡7²@£µ™V´ w@˜g"»±”Àû5X~ËHÀˆçá" ì¶@¹².™£•Æ@˜Ö(vÀZ''õquç@-ëk°ÜäÌÀú:lZÔÊÀÌ“ }1Œ@¢¬)ìUëÀTQ÷ÏË@cG€üÂ@óŠžKæ@’Ö(v@¹².™£•Æ@-ëk°ÜäÌÀöQŽüûò@Ø@ fƒqh@ÊVØž@ÌÀgüL(ÊÔË@½ Eë5¦ôÀÿ‰žKæÀcG€üÂ@Í…S:P`@6£É’À¿W¼Šq¢@Í(~:lÀÎÜEýÝîèÀV\‘AµšÀ 8’c 2 A «Y…À@Ow'‰È²@ÿ`³ö‡ùH@y nâq@å…Ê}¡¢ÀÉΨ7p@¹ÑbN/ŸÀî'øÆ¾0Á³ÃB>Ô®@ ” ÖÕ§¡ÀÇgcAŸ@t\E7é9@b¤^0·qÖ ßÀ «Y…À@M’îÓ‚¯þ@ý`³ö‡ùHÀOw'‰È²@G PW#¨À›wö žÙ@TèfŒ¦À4/ðã…pÕ@=|ÂfÆ¿ª@©`¢os Á+MÆFØ@ÿs\E7é9ÀÇgcAŸ@m¤ÀúWiÅóþÛ@ïÓ$~„@œÛä2°:Àß#.Á´@ AD‘t@0³EŸ Àõ_¹ìcÀ¨Æoë@Ä6 o"%kÀÒ_Ùê,&·À‘› ({hP@5Øz“@‹øy«Íið?™Ûä2°:@ïÓ$~„@ AD‘t@Ïýo±7‹­@À´»*fÀ£zqðþº–ÀÄ6 o"%k@¨Æoë@ÄŸ¾¥Q¬xÀ$§ÊºL…´ÀBøy«Íið¿5Øz“@Hñ0Çñ÷@–oÀà­l@*ذÔWæš@¥žðv@ #ö³:Û@ϬÍ1SÉ;@ÿɶuÚÀÚ½&«ëƒÀÅ­ËÍ.¬À÷†NK…–*@ªG"Å«òL@¥rOò‰v(@ÔU•X"·v@>Ç;d_S@ϬÍ1SÉ;@  ¿¹Ú@îzöGJWÀ¥yãÙØÀàšÏ{?vÀ 6á'ÙZŸÀ~[Ý´_©Àf5{YâÝ•@EráN‘wÀWÕ-†” ,@:Ú²ðxSEÀ0³EŸ ÀÀ´»*fÀ;LŠqï°@TLW{‘Á?Ä"ëç‡h@íݵX‚zÀ›sÛ)Ì%5@2Sv)%PÀ³§‚¬‘X@e àÞÇ}Àãí`/2†@f®åëÍb@Rµçrƒ*˜@°ŸÞ¼~ÂÀ!Ðí3œ@ ±D¹PÀî'’”oi@õ_¹ìcÀ£zqðþº–ÀTLW{‘Á?A2üBï°@Ö†`ŽAÀ»^xŸ@X¨ž8YÀbÌCMAs@~Ò›5M}Àbe/7V¡@·üÆwTTf@‡KfVêB@?”ñ ÷Š¡ÀE9·ë}ÀÿɶuÚÀîzöGJWÀ†àé;õÚ@[-MðšQŠ@@OÛZ›@Y3 A\æpÀW€)Ô;²}À9©r‡SYÀÚ½&«ëƒÀ¥yãÙØÀ[-MðšQŠ@v{&\B×@X3 A\æp@@OÛZ›@Nô1HÚ¿ÀÈüm¿‘Ä@Z¢Î"…H˜ÀT¾ÿèÝåL@Î×vùeÀÄ"ëç‡h@Ö†`ŽAÀ”4®k;‘¾@âgÉ€Ó·À¶v³ËeÊU@žPûÒ¢pÀš#VkçPy@ÝeØõÀ)SFEå>@È!RvCHÀz€XKüƒÀœe¾¿ |@uÁ ¼@fœHuÚÀ›T2Ú=ª@è4ox:_À:Ô¡ ¿w@íݵX‚zÀ»^xŸ@âgÉ€Ó·À3l×I^Ñ@1÷¶DŒgÀLÖGú@›ª/ºŽ[‹ÀV~ƒÅ/°@|<`é\HÀuŽBÁ!S@$8-Ó½º@¢“B“ÀZщ‡ýë³@'TrÎøH@°¢âîú˜æÀB¤ßÕÔŒ¢@³VÒ ’ÁÖklõáÀÔ°NÙÞ˜A%¿/”ÙÓß@TrÎøHÀZщ‡ýë³@ùÖåÍ<—¢@çØõ×$éÀí¸Yc" áÀí•ÕÍx˜ôÀ%¿/”ÙÓß@xàÔ ìÿ@ñIÇp³[@c‹+ ÛyÀkE­—Üð@87 ¾\h@ÎáI@ 1®À­|¬¢œˆÀ]P'ÌâïÀéážïAÛŽ@-äÓK‹…ÀÛÓ°&¤@87 ¾\h@¯}vÜ»¾ð@Ÿ2^€·w@?·s<‰ªÀ„èyÑÅ\@ÿÖZ‹ðÀnXzèÔX@’xßQÖÍd@d.ˆ*#?@<,°KÆ,@"òœ 6Àfz ZÔ™]ÀÉ[¼%~ûq@ªÌi¤9v‚Àk‡':²é]À<Œ/§@¹4üì}óÀ´e’£…‚%8@·€»¢(ZÀ`ªÕ`ºþ@Óiü¶ì)&ÀhÈ<ÄÏw@àzª„_7Z@èT²TíDÀÏA„®×½0Àã]I‹;@d.ˆ*#?@ßœjZ4Z@T¡¼*»6À‘„ëÒA@±Ò¨"›d@}]ÐOJ}À6ý„Ð}bÀzde —VÀ½¸¶ †yÀ $(Äñ@þk^¢§WÀ‰`šv:¡l@ ‡)8CÀ¹bR‚'ªd@ÓŒ>þçÙÀ1ÐØÇ=‚1@cË>‹hZ@T××£1=@'ÐÑË0À0Å¢¼o=Àê´X|@NÍŠáB–•À„va9x=‰@Iøè®wjŸÀU¨­Ilsg@MÓûÕÀi?r¶+è@¬ðð'w¡@¯¢ \f\|@l•¶%Ô‚•À98xøQžÚÀU1OUv™@ÛÊw;«(ÙÀûpn’ÊT½@cÌÔ&˜ÀÒj ¼´y@È5ûù_®“À•…Èí±`@Æú ã}ÀÖ?Ây,ŒF@ý”bÎáyÀ{fY”ð@˜ŒgqC¢ÀÔU”¯‡B•Àú()²0°@ ÿ§9ù£À~‚{³Ç·@:Å÷AÀÀ ƒpXd›@¬ðð'w¡@Î͆Cè8å@Ï%&¸w•Àp¦6]H°@é•~¦™@:ò£ÜÞÀïI›tº@‹X *––æÀ!²?LH²@£’3@u“ÀˆøÆ/ŠË­@lääDyÀÉ‚›˜hŸ–@‹^¸ÿ6aÀŸÊÇ]—“@³yeC¨À×4r离@Ö2l@»fÅ>@“À™¡/thÇÀ :ë¬ç«Ó@ >½.`¸@‰€×^HS§À±fG´èV@‡SÐÊ%ÔxÀ¼Óõ£[Ÿ´@#r[ÌÀKxú¦Ñô„ÀB»Ê“á@7#_' „@ôg”ŸÕ¦À‡M‘i^‚ÀÛ)U䙩@£Ê|ãÑ@ 0æ9ìÀ‰€×^HS§À–J*³€!É@ºß-œ¥¶lÀØÍÙ'½&@w>k ÞÊÀÑñºy¦xâ@º’ÑŠãF›@‘šKX-r³À‘ˆÌ58šÀðË$ãÆ¿½@ì‰FÔ9•@ýê(fgw@æþ*À}@KlDÖ …S@±fG´èV@ºß-œ¥¶lÀ‰¨OøåÄ@*[Ú­Á?üOgM*óËÀ~SÛ•S؆ÀÇMf`G§@K—Äy Ь@uF”qw°‚@,×n´+w­ÀÔá3–¼€ÀTð,û='@’2@”´P@òalÚýs@1“c—ÎJ@•°¨ª¨£@ßt¸¸$(q@‚1z¶±ÀJôßï“J@fFɱt@Ì»&JV@KlDÖ …SÀæþ*À}@‡SÐÊ%ÔxÀØÍÙ'½&@*[Ú­Á?V‰ÈÖÙ\Ã@}SÛ•S؆@ÕÒÅ’SdÊÀÇMf`G§@uF”qw°‚ÀK—Äy Ь@B¡^Ä) dÀðQlk9£À’2@”´PÀTð,û='@1“c—ÎJÀòalÚýs@ßt¸¸$(qÀ•°¨ª¨£@…¿ð,õ@¹›hEgµÀÀ°¨ç?[À´žðu@ê"‡µ–? èÇ&d­@ÉÔsZ‡ƒÀ"]*Šq¼ÀÓno[Ý•hÀì«­ª,¼ð@¨´€)3žžÀhxp–SÔ…@}˸pqWÀüOgM*óËÀ}SÛ•S؆@Y)X±Ž» Aó,*d dÀ—uµƒGãäÀ®”Øý½¯@æð8z\.@æ,Çì*WÀc¯”"B%Áð­p<¼¡‹ÀLd#ظƒ@ÜæwRlÈb@pFf‚Ÿ€@Ù¶œ.áÓQÀ•n@™õ‡ÀÀvd·yX^À¶JnÅ¢¸À·ô³oÀVŒ?yæ_@Wû9,–?fV‚j“Nm@“§e¡ÎÀõΓŽqÏåÀHüÊ;¶çÀÊϪzŠ@{òð¾z‰g@&©ñ"¾‘t@Ó \;šÐJÀûΕ’®ž@<"‡µ–¿´žðu@ÉÔsZ‡ƒ@ èÇ&d­@`)ÛXÀÃU†bKÒÀ@ õE£6‡@ïeÁÀ«´€)3žž@í«­ª,¼ð@ÛzA: TÀ`f~¸Š*“@~SÛ•S؆ÀÕÒÅ’SdÊÀó,*d dÀ>[OA°LÔ±S©À,r5.›óÀ˜õ6a"WÀ‘³D-æ¦@ñã~VÁ%‹@`—E"ÁÜæwRlÈbÀLd#ظƒ@Ù¶œ.áÓQ@pFf‚Ÿ€@R}P5@r@­ÐñÁÁÀ øYÀn@`Ü a–¸À&Wû9,–¿VŒ?yæ_@.í÷©IpÀ÷Á¨j-ð@“§e¡Î@gV‚j“Nm@Bš1} ›@F(˜ªþÉåÀ{òð¾z‰gÀÊϪzŠ@Ó \;šÐJ@&©ñ"¾‘t@+†T!@+†TaÀÇ­mÐKÊÀ[r/ácFž@FO6d(@;´yš”ê]ÀuÍ=µÃ?œ@¨f`ws‰r@ÇMf`G§@—uµƒGãäÀ°LÔ±S©À±ºÓ—ùÙ@É~öFtyœÀh;‘Ü[Ë@_4^œ ¿¡@¥b5ûД@ü@96’¹o@¢º›û’@nèÒØ&¿h@fxv(rrÀ­²’=¼&šÀã¡æÙŽ|@, è_ßi@Âi„ IwÀ_Á³¸ªÂ@È!;ÄJ@æZª}jJ@'ˆ¨ZOÔÀD2´ ìÕaÀD2´ ìÕ¡@B¢¾q.%´@žƒþ¿ÖéÀ)¼°Oú\ÀŸëÇ«ôÉž@¨f`ws‰rÀuÍ=µÃ?œ@ÇMf`G§@®”Øý½¯@,r5.›óÀÉ~öFtyœÀ4’\Ãõ@_4^œ ¿¡Àh;‘Ü[Ë@ü@96’¹oÀ¥b5ûД@nèÒØ&¿hÀ¢º›û’@^>1>ãû²@=”™ñèTŒ@a sd˜ÔÀëØö(¦Ü¨À¡)Þ$/`¶@È!;ÄJÀ_Á³¸ªÂ@Y³ÁT‰À×òAªÌ@WÅö{ÊØÀpÂßðI«@@=·>A@¯¥ù?"ŸÀGbBÁÀéË  ’1ž@ßÿÏéIÓt@Ý¿bÿe—Àm_ã£]à@KHʯ ‡žÀø É6·X®@ôÁ%1.“ãÀuWÆß“£À¯Qa•©Á@ÏŸ5ôŸ@ìÿ=6˜ÐÀˆêß%º–À0h‚>m¸@KHʯ ‡žÀ|qìNgä@¶ úÛÍ9@Ž^œ)ˆSÀ3"އMÀ5H‚LÖ«2À<ê\ƒg@øˆZÍë€wÀfƒ©ø3[cÀÿ–r96Š}@äm Š»SÀQKê²Nßm@5H‚LÖ«2Àž¡q~œÀøˆZÍë€wÀÁ¼"ÿ;ù’@õÈŠ¸‹š}@;7î+Þ––À\„âeÀs¦ Ol@fƒ©ø3[cÀõÈŠ¸‹š}@W° ¿+r@LKÔ-¹ÀÏêßcç@³ü¼Ê.Àí/¬~µ›@³ ¡•³:À–Èï;ª@çt”uH¨5ÀF®ƒžAB@ò+ðJü8ÀG·“–@*†ÍÐHß-ÀKèü®3 @4‰äWO=ÀºbÓ{1h@´lô®ÇˆÀÿ–r96Š}@;7î+Þ––ÀLKÔ-¹À !5œð³œ@ê#­GóÛ)À¢nªNG@ÿ½ Li6ÀHµGá 6T@eõþW-2Àã›™ dP@Ôxj ,Ù Àþ¶ú ‰éR@¸ ¯ö)Àë'íbrœF@£8EBþ†8À|*È}/V@•wNÖ€[¶@S ÔHõ•@¯ha€·ÖÀÀÑêbS­—Àc+¨r¦J™@PL(žÖ¬î@“i½÷õ=À@R›#Šò8ëÀ•xtÃÄÀ#ÕŽFìXµ@òå—£—@f†±O-lª@â+]í|2Ž@ü«¸²!z™@jZ!A¸ x@¦Ç˜Õ9B¡ÀK[Ð^öÎÀw®¹ùÒ|@“i½÷õ=À@- ­~ŸVá@»ˆ/MˆäÄÀŸ@{oAÔÀÚä\ü—@8tz@£Ú‚Ž@Ë3&“”4q@fÛ†jÀ@_¬˜µ¸‚Àh\±çÌþ~@‡á-Ëаg@\J‰›r7@ˆéOj@ê!Ôë–p@ž-¨øqrR@ø`¯èlm@¶Äý¹I@\ìJ8¦ŒÀMƒ^ñvI©Àòö®ioÊs@òå—£—@8tz@wB{øƒ‘Àtq­Ò³N¾À_¬˜µ¸‚À6÷̹/Ä@Ô¿æo&a@,·®7J@g‡(õg c@|†»&M@åÏK8–&ŸÀó(æØuhyÀjÅ¢+ôާ@;—&—Ïì€@ÎÓs„VÏF@ŽØå¥ñ6^ÀN’:\­œ’ÀЄá³x@heüsÉ@@áY1ûïaVÀ9k‡Æ­1@‚ѼxÜFÀÛ-8Ø @ê,Ô!ãå5Àd;ÔLûE@ÈPmÙæKÀîÍM1*@(Öû»÷`Àå÷‡”l)@vùDwŒNÀ—"%ó‡_yÀJV^ã BÀ;—&—Ïì€@*iˆ+omš@vïÚÕb `À£3óÌ>u@¨ççmIu@š,¤ßÒ ¡ÀþÏù´šWÀÉRMñxo@i“}οHÀ9çÁªž`@WºŽåx¯7Àr%ÓƒÊN@o54tŸè^ÀðÉÔc@ŠGÈ#55À|0AÈÛw@󲻿æßAÀÑ!aB;ze@ßbV¼5žÍÀcy.¢µëŒ@*jüØ@DÔÌõ‡@?ÇýZÄÀ:”§IpšÀ‚ÔÓ0q‹@5ö­0;ãÒÀDÔÌõ‡@à)”nÏöÕ@fÔŽO#³™ÀD>ᚨÀÎÓs„VÏF@vïÚÕb `ÀÏ™h$óW¡@q=ëÓA@Su8l DžÀN*tØLŒ@©a¾«H‘Àõò ¿©ô@Àߥ‘2_ßO@@aÌ”?eÀ_¥ˆ“ž@@÷æ*ýç³UÀ&XŸ3iû/@ÆZ.ÑÉDÀC`›Sô€@í¸LÓº¿›MÛ%ÞT@Û ÆYå|ZÀÝù”½á¢,@ŒÌ£úžpÀ©¾ŸvÄ"8@°$ïÜ,]ÀŽØå¥ñ6^À£3óÌ>u@q=ëÓA@ã.fv’» @L©hah8‡@ è×{¹®¯À?Š^~AÀ.ˆ9,À>°[ÄeÀÿê$[j%|@lqýß¹VÀ³a¬€¿l@’«Û¡.EÀrE"l‰[@ê·LÓºÂ?B`›Sô€@©é®CY¤kÀ8ñÅ‹q@äkW÷BÀF`YBV…@yÓ´øOÀÙÆˆ!5s@Ÿ9B‡H@" +::ò_Àl:ÒÓ"q†@oà^<ËÍf@œ—ø#–E|ÀŸ`‚<ÙþS@Ç—­sÀî­À ×`À_ÑX9Á\Àa)ij¹r@oà^<ËÍf@H þÍw@%jþ~åÉP@3°ƒßƒÀÒ¬±y!Ò`ÀY%ý5ÊLÀŸö±‰¤ƒ@¡!Ȭ½¢À^¼zÆGÔoÀþþÅ_·à|@‘4|Fá›H@¶JÙÇǾ‚@ýƒçÛY@±E²±³§ƒ@Ua3Y@¢`÷ÝÕ¼P@‡±î*ânÀ^ÆÀ;À‘4|Fá›H@Zmyç—ø@$‘tzÊòO@Âu棋&@Ÿm—â¿P@M`€j©º%@¯i¬è)Ñ@>Æ­=Th@ÎáI@ 1®ÀŸ2^€·w@Ÿ¸·ó@ h–ŠžXh@çà.‡÷À5Î-zWhÀÆ3![·›§@½+ûÀ«ñÀ;Æ­=ThÀ¯i¬è)Ñ@­|¬¢œˆÀ?·s<‰ªÀ h–ŠžXh@ÛU[†ëò@Lñô©bSh@ê §~÷À¾+ûÀ«ñ@Ç3![·›§@otȸyª@>‚ªJM/*À;ÈÌ-þƒÈÀbiøˆ°¹¥@Ù‘äœÂæÃÀ•(âÏQ@¨é¸dGª¨@‹'• –@‘”ã$ôV§À…H¤€ºÉ@˜ŸñÅ`xÕ@^ó|"VÕÀ‰rÔDr@Ø u³Õ“ÀÇ2W)2¯ÀwQöŸj®@ÎýðþŒ@ÜŽ¢¼„  À0‚ªJM/*@otȸyª@ÆíPžHׯ@&`¨¾tÕÀ;‹úSª,¶@̬븨ÓÀ‹'• –À¨é¸dGª¨@M †/ îÆ@uWõˆEéÀ^ó|"VÕÀêƒf ¼÷@è"éÿ+”À–Þ:Ñ æµ@T ý|ç@G]ì³ÐÀ¢¼„  @ÎýðþŒ@“Úz ÐÀ!D þaÀÍϯ\ÃÀ´-4ö7DÀ‰DuŸ2±À¨oîÇ+”@‰rÔDr@è"éÿ+”À›ÈéÅÒ@ ÑcI⛚?Ê|}^¾éº@†¾þQüa@ƒ"ÏW“´@"hÝ D@†lûFãŠÀMsìrºûa@è#À#e ÐÀ%hÝ D@I…9Xh\ÃÀØ u³Õ“@»&[úÂÀØ u³Õ“À–Þ:Ñ æµ@ ÑcI⛚?Ádˆ´Ò@†¾þQüaÀË|}^¾éº@"hÝ DÀƒ"ÏW“´@³Smä‰ÀLùhzÊ&‡ÀøEË›¦^ÀÄ=¤Š!nYÀ»­Ð •ƒ@W™Z'>;@12\tµeÀæð8z\.@˜õ6a"WÀöëžI,‰@B*à>nÀøEË›¦^À3(” r3ÀÍQÉ—]gƒ@Ësþ,â­À¬Ë±:ZÉdÀM®Åæs@æ,Çì*WÀ‘³D-æ¦@B*à>nÀ"¶ecÜi¡@±{„nü˜\@ž§Dôµ?@!É•Ò`Œ@&Jw´}rÄçxÀ–²`8¨[À(¼þF0@åHgEMÀþl!ÐmÆ?@æÎ+{ù!@&Jw´}À‚GÍ xMÀSS-H€j@ƒ]´7”Àéú3N‘`À&= Vgu@êú3N‘`@¨¡wí‡ ‘@Îr¦ÌI@{™YÓ4tZ@~~`^=@2ÿ„ô`yÀç] —èbÀø ±´ÐA@€G WK+@Øj\F6b`@’‰c9I@±ƒ[>ž`À½»tÀ"À”ÞÛäR0ÖÀÝ–XÑ͇@"C¢ `p@Ò£àYˆÀ-°þýŠ”ÀF´?ã¨ÓÀ%»(@‚ÍÕðœá@ÊA}uÌœ>@Ä7Ávx¹@=·±Iï‘@+Ô€ˆuÚÀ>·±Iï‘À³l³ n²@†ÓçÌ;m@-Þ,XO´éÀ&TŒiœ@€ÈøÄÜ@ì¯ Ÿ©±u@†S7rÆi@ƒA¶‘ä.@Që¡æ ès@jPÝÝÎó?ö8¨\ƒÇ@­,¶¯nžƒÀ„ ²(Îc@«ýcQ·@¢váœÉÀ&TŒiœÀØá¬MéÀì¯ Ÿ©±uÀ€ÈøÄÜ@ƒA¶‘ä.À†S7rÆi@køÀ¦ÖÀž¬ç•Ñ1¨À¾FŒðsé@9È.8ä©À1ß´.ÚÀ0–CöôF¢¿ñ£à*D¤À–pÆÚ_ítÀæh&t:|À¿Y¿¬ã©@ê9µÌ3p‹@ q@÷™]@®§5F@ ¹6Ì¢Àš¾Ð]…Y3Àÿ>òîiÀb¿Î®jBŸ@æ]SÙ`PÁ@@ÃýÀç€P@?gåõ8‘ƒ@\®0_Oð??gåõ8‘ƒ@\®0_Oð?áŒ%¬zȨÀJ»“#è@.}¼´`ŽÀ¾•8ðÁáÀ ƒqúëµPÀ™¥I ¼a@]Ýᦿ7=¬1|€Z@°Ç²¡X{¸?W3Zö¡j@’ì˜;G §?ÕŽäÆÂíb@¡kŽsÖ•¿©á9„âwk@ü^¹B»õ©¿Ó㼄æÑÀl‚X§ï¬À;=ðì.P,À;ÿ~Ó À]0g8—AŸ@;~@ƒþˆÓÀ?ÃýÀç€PÀç]SÙ`PÁ@:®0_Oð¿>gåõ8‘ƒ@:®0_Oð¿>gåõ8‘ƒ@.}¼´`ŽÀLåu9ºÊë@6ÃýÀç€P@ÿZv#çÀáÀAÝá¦?™¥I ¼a@XDz¡X{¸¿7=¬1|€Z@ìì˜;G §¿X3Zö¡j@¨¡kŽsÖ•?ÕŽäÆÂíb@Å^¹B»õ©?©á9„âwk@öâE¸ À]ËÖü3ÂÀ{Õ•ä"ÞÁ¶Uº"”3§Àhb'Ç££@P æ)Jê?¨Ù®·‡v§@ –õ64À$ãðr A×YÕ'ªÀ7ˆ §å¦ÈÀZM½?ƒ9©@• tëyEÝÀkÿV„¥œ@‚¼¦ˆf8èÀpoÀ€]¼“@A]¦.§@×U7éÝÁæ)Jê¿hb'Ç££@Ü–õ64@©Ù®·‡v§@×YÕ'ªÀW4BDÃrAZM½?ƒ9©ÀL¬Ò™ÅÀ2Lyw„°ƒÀ<äÜd§¬ßÀ(̵N”°@ç19¸ÍëÀËGˆ*k³ìÀŽî¡ðàºÀd(jS´y±@Cä¥Ò‡„@«fÒP*ù@KËi_@i@“)®Ã$»À.LxÈ\§@ äCvü¹±@œÌÕ_ @(˜L*Z¡]@~¤b‰¯vÀ±ŒPч¼À6–.×·@à”ß§ê ãÀ;y+mâónÀr÷¢)غ@ˆºÚűìÀÈ1Ö¸M¤Ž@²î¹×ÿa@KËi_@i@w©'`÷@.LxÈ\§À@üžBk¾Àé^Q@©.²ø!l@}õ¹z…vÀŸ_ö1>‘@SÒàV`ÅkÀ6[d§/ÀÀÕ2”½ô»À+¯ìè>ßÕÀôµ­ ÿœ@N•žN„,À)|®ðM6Ã@œê‡Þ¼/±@7ˆ §å¦ÈÀZM½?ƒ9©À“)®Ã$»À.LxÈ\§À–Q 0p-A§"žêÆçÙÀ±<2üïÌÁEõ-sFå@“î ig@/µ1ÒáSÀÿé,)SF–@\™t@#’\¸Ö£@#„m{ sŠ@·Œ«ÕÆ´@ðE$ïþ‚ÀµºXöIßÀ •­µé¤@¿¨:‚Q®…@z@ˆ‘F†l@ã]•à§Å@ EPåC@@j§ÖM¢@Y¹CJ8ÓÀ3¯´b;™@¡§} óy@V•žN„,@ôµ­ ÿœ@œê‡Þ¼/±À)|®ðM6Ã@ZM½?ƒ9©@L¬Ò™ÅÀ.LxÈ\§@@üžBk¾À§"žêÆçÙÀ.…IÔ A¬ÇŽgç@Ñ(A‰Uý$Á:X®îYþR@ØV@tu@\™tÀÿé,)SF–@#„m{ sŠÀ#’\¸Ö£@ðE$ïþ‚@·Œ«ÕÆ´@è»´(5?•@D1 x¼%èÀz@ˆ‘F†lÀ¿¨:‚Q®…@EPåCÀã]•à§Å@ÐDQŒCàÖÀ¯§Ô\FÔA¡§} óyÀ3¯´b;™@oó`”àl@ÑÝô%ƒÀ©a¾«H‘À?Š^~AÀ,ö‘½¶p@€Ç ow?Jz\‚½@Óûá—lŒÀ¶i%í¾ºÀ¨Å/˜ÑM˜@VÔj[b$ƒÀEUþxÍU™@õò ¿©ô@À.ˆ9,À ¦ ow¿,ö‘½¶p@Óûá—lŒÀ°y(%¶™Â@SãmH(P˜@ùs™tšGÄÀ݈*Ÿ³•@Ga²É< _@;»^ÑF•À6ÚQì \P@AeÌaH{@»óó#zP(À¢w”eü|ÀÕ§Ó~Ø0fÀGa²É< _@¸@åpL2‘@ –.¾;G@Ö7üè–ÀÁóó#zP(@AeÌaH{@b$•ÚfÀ"˜ý QÀÈ7¹~½r@Eç¬@!b^Ó³v@;»^ÑF•À –.¾;G@+½í¬@½R-—4&z@š"¤'49®ÀÞT†Þ‡ŽÀ;Ÿ¨j,be@2޾œß^P@CdP¶Gk@¥ž´&âT@?ÖÚÜ™\@T°Ñ‰¤v@!'TsfRa@6ÚQì \P@Ö7üè–À½R-—4&z@„ æ¤@Wjˆ†Hj‘À¦wZJ°2•ÀLi_¹Y²Q@óf;Až;@‹­ˆD7ÑT@¼\š6ß?@1sÓ37€²ÀBŽlù‘Àå{L"Õá±Àüß½ô–Àán©®zRÀ@. 8jÇ¡@ŠhÆ·è@Ù®ùCes@IÞEý©íÀ>Ô‘¬ gšÀüß½ô–Àe Âç[v}À. 8jÇ¡@Hõl1þ @£N…s@ž^\¶ÔÀjìUwç˜øÀk÷}]'o¤ÀhÙ'MôÛAjt†3ÄØ@V¸š°tÉä@‰»‘FP2‹À°»Ä,j¶ÔÀ#qC†­ ÁUÙ#²±À1—©¦ñÀjt†3ÄØ@|,À@7NA„»‘FP2‹@V¸š°tÉä@6ðÒú°1ÁÀMú@ $h•À,×n´+w­ÀB¡^Ä) dÀLd#ظƒ@ÜæwRlÈbÀLE¤CíÅ@_ðŽëæmš@P¿A!²m†@5ɺ£%ÀzŽ\Ðð“”Àï¯C톰ÀÔá3–¼€ÀðQlk9£ÀÜæwRlÈb@Ld#ظƒ@_ðŽëæmš@ P@"U'%ú^çÀ¼™îù†°A£w֙ΈêÀó‡¹Q'AB•¥sºA²:Ǧ5Ù—ALãå’mÀD,僑@ç]áE6tÀ"A*ÞÆ­@Mn [Ž|ÎÀr(lì AMíàöÍ3tÀ6GÒ^ÁQ·Àž½ºO‹!’@…°¨¸ÚÀfýH§»tÀ·¦ûÍ©fÀã!.§›UQ@¢Ãî °8@g8bV(@å¬êØ @ìãÌ0R;TÀQŽÈܯQ‘@n¼»®dĨÀ¨Šr?'FÀMzA:g7¶@& ³ói†@æPŽé\“‚@n3åñ‰Tp@¿àrº¶x‡@ Sa®ue@¤Ç#¨ûõ²À îÍCvÀÄ*†"úŽ@‹ÞùÈðn@S†LH,À3ÿæÛYŠÀ& ³ói†@¾âþ¯®@›á Eùlp@¦2!A\á\@6¯ÃBÁt@VIH¤ÖùR@Fl¯pÞ‰=À0’-ô²°À‹ÞùÈðnÀÄ*†"úŽ@ Q ò6åÀvÛYɵ´@@x¡~ u@<¯9S,Z@cFX;Ñ|@ ÂPæ™»d@¹Ö cb’À%ý}hZNd@ÞuV¨Ø•@~ç‰w$}À´èî³uõ0@º‘¦Á ÀÓ¯îÓ²Y^@·s¡lÍB@–霨d@kAÀýÛ¹M@1Ø ÊTd@!ç6µŒ˜À~ç‰w$}ÀÌo)pîÄ£@¸F@]jµ@úžfUà€@à~ ­ü|µÀOZ Ãù€À+ïåI@ûg,²|¾Ò?Ïd`Ä€ÀÓŸJÙYP@é ÙÉMek@߇ÓWÅ+HÀŽ á`s@†bÃÁO*À‚…E×½æL@³òô»0þ¸¿‡UÐy0—Ÿ@ wsaÀEi@mwu—`¤ŸÀ“ŸÓÑ'rrÀh,²|¾Ò¿+ïåI@Q¢¥|ÖVC@Ä}m•p„À߇ÓWÅ+HÀ\刓t@ŒbÃÁO*@Ž á`s@¡òô»0þ¸?‚…E×½æL@yy§lë„ÀIÓôX.]nÀàÔmWd“Àõm…ÞˆdsÀgi2®Î§›@ápßm@@Áçïa@g]¨­ó+I@hkyóÁZnÀß x¹fVÀå}¯ó%rsÀ¦1=ežœ~Àápßm@ BçhV}@é“Úo`“I@Ì^T2@Ã.®™0¨Áq•§!œÚ@ÁÛ(¹* A='þçÇ‚à@PG7žÅŽ@°­­‹·@¼8ÖÓf@ü¦ N3À 0†åXâýÀ¬Áwb™¼ÈÀ÷ã­½F¨Aái/¨¬QÀ4ŒªpŠÁŒþš qyåÀ_Îëü¥E!A_Îëü¥Eá@ºVÇ“@ -n™LÀãñÛy„Á ÏnuäÀHÈc½„~@‹&$‡jFP@›M׆D‹$Au~ ýã@dþ®Q`‰@¤¿Ì"Áè 3WÊãÀñÜz@ìKŠ@=no~jâ[@‘¡`Åø€@•Áë&H_@*oÑ„­ €@½õë®§Nâ?q•§!œÚÀ‡a)q ²Á='þçÇ‚àÀÁÛ(¹* AË­­‹·ÀQG7žÅŽ@ü¦ N3@¼8ÖÓf@Ô¸Á3>¼È@ˆhpíáýÀái/¨¬QÀ³‚éñì²A:rÒ0¨å@¶ññ™Ú¥Á_Îëü¥EáÀ_Îëü¥E!A -n™L@ºVÇ“@ö0][)uä@Á]‘.9„ÁFC¨¶@@P@°6o\šU!@u~ ýãÀ›M׆D‹$Aøøª ([@ÎcǻŹâ@—{i@ÊóÁn)i\@ý|/ï ³-@•Áë&H_À’¡`Åø€@Ðõë®§Nâ¿*oÑ„­ €@Cè½Y&¨Àíã7qh†@-ðÏó yÀ°¯¦—²tY@Né®$DgP@€ ìçD@H9ÙE@¨@FrݱŒÀb6ƒèßbo@‚‘UVìÎö?èÙõKúaV@_”mô@A@nú˯ k†@æÍªâ„­À^A†mT@¶¾qú¦þ|ÀÑ ý+ìêD@WЂˆ³§:@FrݱŒÀ7LôãYή@q‘UVìÎö¿b6ƒèßbo@œŽäŠL@Ì÷@mÿ5@oš7‚Ñ@æ³`Ý!mÀ{zîd'ÒµÀØFˆnœG“ÀÅÆ]p!éÝÀ¶Õ3¹ûxÀ‘‹¾ bÑ@· @h #@\,ÖîCŒ@v{⸀û?Ù³`Ý!m@oš7‚Ñ@ãw+’º)“À¡è©Ô9¨Àdãd­Ž-‹Àã.ФݧÜÀ· @h #@î²)gË@]{⸀û¿\,ÖîCŒ@YZZZšA´´´´´ŠåÀ…ÄÝKBÀ ÿC X¢À|:»Jl@Àk ¤Š0€@@8»Ó.ò?4ŒªpŠÁ:rÒ0¨å@’\«L>kÁ@ŠÌŽg\rÀû™2Õ×®@ ;Íp4CÀc ˜ÃMß@õ1ÄŠýÕ@Àúûµ#•@Ñhh|¨8D@i&{hc£“@ò5·8/gB@â¨?¡•ÀÞÍLR‚€'@YïÞèÝÊ9@YYæ£~ìbÀ´´´´´Šå@YZZZšA®"Ñú-‡u@\s·òGY@­ë‰B5(®À58»Ó.ò¿Àk ¤Š0€@Œþš qyåÀ¶ññ™Ú¥ÁŠÌŽg\rÀO«º ç(Æ@†]oñ MÀÎ-‚‡Éxv@#g¬O¼IÀX€~DÏs@Ñhh|¨8DÀŽúûµ#•@ò5·8/gBÀi&{hc£“@AƒÍã“Þs@’îeÑ3žÀyM1ÚRÀJä1sé©{@‘gªûð(Ál²¨áÀ/ Áߥé׋@Š @ìÿêg@<îwm¼|(A9…$Éo A2À!Ç–>Î@õÌç82¸@3©œÞb—Àg;(*ÿLnÀöÍ/¤r5 Á®ÍdÊörÁˆðŧ—tm@6QeÓMI@9…$Éo A…°ˆÇQA¢øHï?è¸@èÆ òÿì£@±×rKëqÀöÆINÆ}À±<2üïÌÁ¬ÇŽgç@Óß ‘NAEöýÉõÀøJ 2ÐÁáb(lt¤â@Eõ-sFå@Ñ(A‰Uý$ÁEöýÉõÀ ‚Æò‰@2AE¾ˆMŒä@8BþÁšŒâ”@.ÀS’u@£Ñ¼ÕÊœÀY3(lãOXÀ{™YÓ4tZ@ÀaŸ4ÐGD@L|vøC•@œÚÂ× ¹p@Ù†‡<~’À“à ¸ÜsÀZbyz;L@À¼q›? 5@ka|u¯öi@kí™üS@.ÀS’uÀšŒâ”@}Š´ ûŸUÀ=,hî*×—À~~`^=@+g?¦Úƒ&@œÚÂ× ¹p@AÛ© Ho}@k÷1sfqÀ}›.°ËpÀº8õ»ÏW/@ƒU=@Dæ"¡ÓL@szèZ06@XœrÀ16X•j®^@Ïêßcç@ê#­GóÛ)ÀñÜð'ÖŒ@–,¡±5vM@>W½eIl„ÀYH›}ÛQÀ"›½/@¾îaS6ÀÒ”äEüß@*~í›ì1Àm±½µ l@ì *ÈÁ­4À-ÔoL]t @®!À(¹(ÀG”LÂzÑ@ÿ¶fBûA8ÀY7ADÿ=\@ “?(Ë †À³ü¼Ê.À¢nªNG@–,¡±5vM@5ó¯ŠN³†@vºF4—éQÀ {PëyÀÁÂk=6ÀЊDx ëS@Yš…±áé1ÀuFó+É'P@ød³™¤š À´°‘ÃX£R@X)(žÆ¾(À@T\Á‡HF@˜ Ï÷+8ÀãÿD„'ÝU@C °å@:Z÷üe@-ÞE®¸ª@B~®ÿ®q@Ç}ú6îÐÐ@„Þ½<&jÀñLÂÕ_N¯@xP–¹ï“„@?: ×"½À”ž]K @, ކãÀq|§Šâd©ÀN$PîV¥@ Ü+å\øqÀ‘{ ÊñѬ@8ÉU‡‰ u@TW“¿T@k¯€aˆý‡Àq‚`™¿@#.¯ÜŒ@q‚`™¿@#.¯ÜŒ@:Z÷üeÀC °å@B~®ÿ®qÀ.ÞE®¸ª@„Þ½<&jÀ÷\³óÈœÚ@yP–¹ï“„ÀñLÂÕ_N¯@ˆóA»Î @ØUÌY†aÚÀþ¿Þ¡»µ¥@RÏÐ&qâÀ Ü+å\øq@N$PîV¥@9ÉU‡‰ uÀ‘{ ÊñѬ@}**ˆÀ ÓNݼ@$.¯ÜŒÀq‚`™¿@$.¯ÜŒÀq‚`™¿@NàÒ! ¯”@[ËüŒ!x@Çù}í3‹@¹BéI,«Õ?srÅR#s@õð7¼AZ\@)¿ œÉ¡@ ÿIivS„@$‰oÝŠ«±À±ì¨îLšÀ<ÙhÝ?1u@w4o((@M«ÆÝ?À®Á‹…jÔ˜@eTÈýGZyÀá,v‰ÆšdÀþ’'<_Xl@¶¿¥„ŸHÀ›o§ŸÀ?ÑÒœ¶e@l¥7¦õm@~ KöyUb@®BéI,«Õ¿Çù}í3‹@ Q¼ù¦]@ÃÀ¦ŽŠE@ΠÜ,s‹@¢nŽÅÌân@Á€Ìà67—ÀѵÚ6Õ¾À[ èÏç`@˜uÞ¢“?À¥œéù²—T@eTÈýGZyÀÐPz†-L¥@²9è½j@'v£4XƒˆÀ†ŠƒÜ`@Jy§øj'X@ÏØÁW£À±sékXf@öq“º×áMÀ< 6NXhÀ¸æE¾õÀ”P€Ñ-Q@$å7>;²E@øîEoÒJÀ0ø‹Zd”6@öq“º×áMÀ¨‡…í\m@HÜ3?cÀl¹á=´gÀȯ2 ««E@˜Ñ®j£;@n{Ž„…6@IÓ¢9RÀâªÁ-lÀ©3‡±(aš@ãaÀ´µ@@ht™†À1 ü"‚@‘Üâý4—SÀÛÛ)O\Ÿ¼ÀÅ¢lÉh½S@¯×>P½W@Ôîù…";…ÀŠÓù‚\v—@ýys=gºö¿ÌÛ²qJªš@Þ4©löÈÀ@ht™†ÀÐVÅk(É@‘Üâý4—S@1 ü"‚@¿èËlÎTÀbõ!lT—¼ÀŽçuåË…Àwñ‡÷´@5zs=gºö?‰Óù‚\v—@–Gm¡áœ@ÅMŒ×G@™ý°˜q „ÀivàCkÀÔ²ãØ†˜ÀXêA že@>-x:DØw@²)@OAÀÅMŒ×G@ªlBªÍÉš@ivàCkÀ-£:†Ö¿QÀ¢>Û¿Y d@ÍÐ÷ôÔžŸÀÏ)@OA@?-x:DØw@ž¤~üZš(A–gÄÈÁÉí×åÙ‹@ߢLÛW½ÀÁ§®U¬ÁŸ"Ójeã@þ—:ÐM–Á,ÑÃS´ù@–gÄÈÁØáͱ>A³ÕþFÂÀbâJê§Aó@„ë'GMá@§éà¹pz(ÁïqÄ3û@ß$Ú÷®¨3Á+A¥o`À¿-koµó-À­†¶Ö@¦@ *þÃqF@AËì0ù°|ÀôA-ßLÌ=@¨WbõÚ6@#”“ÒáßMÀ¿-koµó-Àª¢ŒnÂWÀ *þÃqF@™Š1ÿõ}@ kàXÖ=@‰°“.uk€À#”“ÒáßMÀAbÖ¾U†c@Å,A§ Àö­“w«x@w+Û6Œc@™F*ŠË¬xÀ¡PU±ÿ–@É®gyȵ;@¼ÌŽ´~ŽZ@Òro $QD@€Ø™8¡À-WÛm ±yÀdOK4|@ë­oª½v@=UnxïyY@Î9ís÷óX@…ºÿeC@XöE89è@êEUi@¯“¯„9‚y@ÖõiÙw,œÀ¯“¯„9‚yÀhá‰@É®gyȵ;@¡ºî@°@:Úƒ'4XD@f½³Øÿ /@µ‡å“LgˆÀÔ£ÀÂ.tÀ¥ýåÐ¥‰e@¡¼ø³Âì`@MAÞ$V„C@ ~0–´C@¹+U€*:-@A¬FKév@—@ßÂ?hS@›ÄQ\$£@€ú•¸˜‘À‰"Tý²Àq, þK@žŽÀOÒ¥Àñ¼z˜uf@âiˆ‘j’@RÛÃÕ9¯Ù¿zeÊšùte@'UW˜rP@€ú•¸˜‘À—HzPP©³@[ÇZ¨Š?@Þö¬XJd‘Àx’4Í>@ íeýÂè³À¨ÚÃÕ9¯Ù?âiˆ‘j’@K6aß§DK@ 7üîöæ4@<,°KÆ,@T¡¼*»6À< 6NXhÀHÜ3?cÀ)ö¿[Òr@Dc–:b@놵 %†À´Ù<ÝwŠ@½zUK &@ÁÕ'/lñ0À|é!ýŽ#j@·9J_3€Àû4ª‚–U@{W¸ cwÀ¸õ¿pc.@hrû¡µÐCÀ¶t|F÷!@0%¥áK0,À"òœ 6À‘„ëÒA@¸æE¾õÀl¹á=´gÀDc–:b@Õ€¤çå€z@Ò‰Þu0y@pI'&Ï›À8Ó½;H1ÀŽ\⢒:@;þšú~tÀÏ…ëNh‰@AD}Aqí`ÀG_òV‚@uÐiÔ7ÀÖ–÷…7O@:D1ÈJ5,ÀJ±@!!6@—kÁêöÀ¯Écƒ±À©±¥O™›â@¯Écƒ±@Ås{iÍöÀ.H›ÚGdŸÀû7?ï[5q@{DK0" À$C‘#¦—@È|ådÀ“SÅ9c@ý=? •ÀúÛëBçÀ0‡‚[wªÀOw'‰È²@ý`³ö‡ùHÀé,LÖ:Aˆyd6à@Jô-Ñž°@\Üš>´{ÀõæL²´€þÀ©¢ÑxE!ÄÀaxH«¦…ýÀqò|K¢½À¼¯VÇíõÀzfs¿³¶À?ñU_®ù“@¿æÏ¼Í @IAÆIeå®À~#ªRÚ°ÀêPO\À"ÕL:òÀúkt앤À:%­\ÒÒ@È|åd@$C‘#¦—@€õàKm–À/ýrô:É@Ï® }©Àõôµ…“€ËÀÿ`³ö‡ùH@Ow'‰È²@ˆyd6à@üAÂP ATn*s‡È{@÷ìN —’°@) 12&;ÂÀ© á1µlÖÀ+Æ¡$ Ô¹À •¸·1íçÀž4mO¼Þ¹Àxr;ÒÀÌæÏ¼Í À?ñU_®ù“@.žµ½üýŽÀÓƒ½ty@œ—ø#–E|À%jþ~åÉP@9ä}°ñД@?'ó—xP€À[ ±u#÷[@vδ §îG@öÒÞ¥x@Aúª½˜ÀŸ`‚<ÙþS@3°ƒßƒÀ?'ó—xP€ÀŠÃi[¦+£@ÊxnàÚG@¼¼«%j4@´îôPƒùÀûä|—kè@‰¸Ýƒ°~@ÏKµLm“ÀTêˆ@“ƒ\@©"ʯuÀ¯¢ \f\|@Ï%&¸w•ÀçMp«¦…þ@«ÏÑ[S¡À1êtDRY»@ªŒ Q¶@öµ²œ[ùÐÀU1OUv™@:ò£ÜÞÀÞ‹›‘æw½ÀÍ÷@ãXä@DúG£S•£@Y ËJ߉@ 3NfÀù¯e‡@Öàñ(ïdÀ<°õJ“é@~UVñ2À›sÛ)Ì%5@X¨ž8YÀ¶v³ËeÊU@1÷¶DŒgÀ™ý°˜q „ÀivàCkÀ­¯oî¹6…@Îv¦äc@̵-ÇÓE@ öï_ÓiÀœ_E:ã€@o! ¨¤¡Àæ–¶šãö@''*´ô3ÀŠÒÞìL@2Sv)%PÀbÌCMAs@žPûÒ¢pÀLÖGú@ivàCkÀ-£:†Ö¿QÀÎv¦äc@¬›äÕ ßn@dJZs©`À‡Ø*s·ƒ@]ƒE”J@ÑXG bã@@ÕÝÓ/[ø‘À൶C×eÀ<“ÈéqÐ@{i_sø~@IIÖÎç*ÃÀJ”Té{±‚@¿cSZ1áo@!ÖR+`ÀÁݨá`‡¶ÀŸž&W‹À¥ôHóœzÀ¶VíOÐ,KÀl–ˆç*â@@È1÷)­ƒ5@03¥|ò<@ŸŠ¶rnçÀ{i_sø~@8ÞœŠlÐ@}Ÿ*'²·‚À…ɲâ)ÃÀÖR+`@¿cSZ1áo@¸øEhÈ´3@^‚++HµÀ'+þc;@–¶ß!8yÀÓvÀŽ@d®Ë1w~@Ť#i$ÛµÀ¾6¾áZ’À¢s°¥Ô@A––í‚À @tÐòÆRc@Œ\Yƒ.kyÀl`‚¹ w@òU¢ÙŽÀ6Ù¤„ÊV@ ÒT"~’mÀ=òî³·ÐÀ h3!ÛÀ›@¿JÛ÷“‘À‚®Ë1w~ÀÓvÀŽ@ÉvdÐôr’À#‰eÄG¥ÀA––í‚À @åùýUzÏ@»áƒsÊ6zÀj»ž%¶‡‘@–«=^ËÀÅö¥F¥@µŒJxœooÀÇvoŠe„@ŽÀQn…å†@¥7*G>ÕÀWôHÚ>¨@¢³¬¬ÀCTíM•ÀæPŽé\“‚@›á Eùlp@Øœÿ¬’f³@õÌ?2ä‰@ÝßÄ-,›’@ø>æq@Õ¬û$¨@ïeÛÜÌ•@aÐÔ ¸ÀÞ§öð•À^s Ë4™ÀÐA©º‚Àn3åñ‰Tp@¦2!A\á\@õÌ?2ä‰@œi1s‹íª@í³²Šg[€@±A Ù÷è]@;“¹|)8•@8ñÕÿž‚@)ÕÔ=M8•ÀõG|¸óº¬ÀðÖ¨k*ýÀï¡¿kíJ®À,·È{lc AÕv&¨éà½@[©ü§©Õ|@ ¸¨LøÑ°À€ÛÒ¥Ð@®s+óÃWDÀ‹Ö–ñÄ*Á¨ú,YA~@óTôfFËÑ@GŽ8¿=W@’—O^x­À ] 1ÝôÀÕv&¨éà½@ ©WœAB~n/8°À#Ï=V7ìâ@—s+óÃWD@€ÛÒ¥Ð@‘˜Ô1Æ{t@@\¥ ê°Á;Ž8¿=WÀóTôfFËÑ@"üXºÒ)¤À@>®}]q¡¿¿7y¯Mƒ®Ànðâu6j@± š{S§«@žže ªmÀ0&8î̧@ç|\È;@À+½ ºÉ–)¤À—h\ˆp@xWCº2²Àžže ªmÀ3´[ŠÊŰ@Ü|\È;À0&8î̧@?™j­Áß@ô¯ÿ†S"Àfz ZÔ™]À±Ò¨"›d@”P€Ñ-Q@ȯ2 ««E@놵 %†ÀÒ‰Þu0y@Þ¯zͪÒ@S€Öé»?ÃÀÉÚù‡?WÀÎ0Dëáb@þ $áçtÍÀQmå1„·@ 1åæ$«ÀP2žS¦@Ôƒ‡°@…À¢£—B·b@Ûÿ6oPãb@rüÄhè¹p@r”—¥Y@ÍJ²ñ@©ƒ4ÿa$ÀD‚ƒ©²’C@-€~ís9@ô¯ÿ†S"@?™j­Áß@É[¼%~ûq@}]ÐOJ}À$å7>;²E@˜Ñ®j£;@´Ù<ÝwŠ@pI'&Ï›ÀS€Öé»?ÃÀ›­¨¤4tä@ ÍÕôáMn@8Ä»J–OwÀR]A»ÿã¸@ãÌѳàÎÛÀøÙÚÔ›@©7€¨‘‡ÄÀWCÏÑ|ÅZ@¯„/,ê/ŒÀ>#Æ_÷L@ãOol­¦Y@·43äsªC@¤§ç;²ñ#Àž¬áU/@® ­W.@þ/Ô^„#@kOw- C?À¯@‰¶0L@£Øè"“Ës@(òÇ É`À¬c€b×qÀ¾M¡]v S@š¿•´AL@%äå|NYÀ(òÇ É`ÀívÏDL]~@„EµqS@›·¤Ÿ:xÀ‰"Tý²À[ÇZ¨Š?@]CJ·í“@Ö®Ÿëh@ÃXtO‘Š)@ņúþ:5À2*ÒK6æO@ŠWâ¤dÀ"\\ª~û'@°G³ÕO?À?NA|ÅtÀ8aŽ é*Àq, þK@Þö¬XJd‘ÀÖ®Ÿëh@Ï•›.py‡@8$ Mú¨@À”,·¡G²K@ˆ}/ßiÀîÄê=€@œýzFWI?ÀâÕáÚ‘lT@ë²¥Cø#$@É£¨‹”UrÀR…HC,Ø@äæéÕV­¦@êBvjšñõÀQ k ô*ØÀÚ•óÂb¶ª@šÝk[Ç}@¶*Îgh@ö2jX6Ö„ÀØ+»pέÀãh‚¥#–@`¨‘¼_޵@@Bóÿo‡Àï@'4b|—@5è¢[Ou~@üï<§ Ï¡@±òG7@õV[ ‰>”@ß'‘AçÙq@jÖ§ À@Úkg2Cy¤@Vx³r8Áæ@?šVêÑ@-À€Lƒµ@”iµ¬V?„@BЮpÉ-ÓÀzÕÇéUþ¼À‚3Ç›³Ù‡@HáÄ[–Z@:R5C§ŠÀšÒÄt…Á¦@äcðG¾ ™@^5Ý•™ÒÂÀ@Bóÿo‡ÀF•ÄÜ×À@‹žG3øt@œá¬Hâ1[@ø‡Z˜LÍ@0¥¼:ß[@6²€Cr@Äëð«ŠàO@õº«Â»’À¿àrº¶x‡@6¯ÃBÁt@ÝßÄ-,›’@í³²Šg[€@¬áNäôË@¡4av{“@.ªz}.–¾À€˜øx<ŒÀ6/-9¤ˆÀÞÔ[‡µ„´À Sa®ue@VIH¤ÖùR@ø>æq@±A Ù÷è]@¡4av{“@ñ%¶ßÁ@?­u’1ŒÀþ„ï]û¯ÀNÇÊòt’ÀªÌi¤9v‚À6ý„Ð}bÀ°±Ñ˜â¥¶@tÿ¯«< |@1A¹Ÿ¼¶¯À–M˜x…fÀiö$Ï¿@@Ã%©¤+^Àf€ˆ¡Óºh@ º,Áôež,ŸAk‡':²é]Àzde —VÀtÿ¯«< |@šVÿ¯(O©@51…0@ÁNtבªÀ¯†Àl^Àêž’;Tf{@òª&RzÀgŽFó®ÿ°@aÙ]<¾¿„@¥¢Pˆ~g@KâVÏf‚@J‚pM_@>º] ù°ÀøÑŽÀ'.›p»Àˆ@h\±çÌþ~@Ô¿æo&a@?¬„ùýŸ¦@:JB·~„@à ÉäNÔw@E©_fŽ5b@èn³ÍÜ™ÀØëÏYhpÀ:+~Äf@ò”2Ø«?Óè±Ü·o@[‰Ø•„¢Q@ú±óÒÀåá…ÛÚP@õìåà¦s×À'%ÒÊìþ’À»N`š·Õt@[J¨À@Fù0ƒZäÀd«âÂä«@‰4¶äò7¶@Yn$0k\@¬Z½ö?©èg ú”@WäÀÙVÝ À<ϧ\Á@CæÇ2ú7ƒÀ Ï{ƹžÄ@{í½u'·­À°‚ \¤[å@/µ1ÒáSÀØV@tu@d02\~D@˪þ¢ù@C^êÎ÷ý§@|¹ý[èòÀâCÉ/°@pJâtçÀ f v«p@]³â?¯[¥@N@[õï6QÀó^">¬:ÒÀƒJ5CÀˆò˜ùÒ@^°í9½–À^°í9½Ö@õ r$’“À§š»½Ý ÉÀß³X6÷¦ÀùuÖÚ@{í½u'·­@oòHClÇôÀVn$0k\À‰4¶äò7¶@¥ÖŽ…¬Í@%8,]£G¼@%ÑOñÄ@J°¤ÒQ@nB?¸ÂÀs ~©@Ð/—Qðââ@Ú^ÎD|@d‡˜Ý“@×ÀC^êÎ÷ý§@2 ² ÉxAÒ¸!=âÐÀˆò˜ùÒ@ƒJ5CÀ4Ý»Hœ2ÁèØïæz´µ@S¤à$ÔÁ7c:XÜ¢ÀÆ÷c«¿ò@ü ÉÚ¥‹@$8,]£G¼À¦ÖŽ…¬Í@J°¤ÒQÀœ%ÑOñÄ@[VÖrEl®@õÅ/ƒj—òÀ’Ú^ÎD|ÀÐ/—Qðââ@|¹ý[èòÀÒ¸!=âÐÀX3DqØZ AƒJ5C@ˆò˜ùÒ@±¥Ù#´@̳ǣ' ÁxŽ»[ñÎ@ Nù-ÚfÁü ÉÚ¥‹ÀÆ÷c«¿ò@Æ0•^βÀX"seh@A;à_s¸@D–õDVþ”ÀŠF@Ç-6¤À¡PGJV¶Œ@àKcVØ‘@DØ`þX·l@úº ‰f@'%ާöåµÀD–õDVþ”Àu6a}×Ç@ÝLð†)ŸŒ@¢•ªeù¹À©ÃJ‚Šæn@@ ÛšÍÜH@÷ßp~lp@˜ûDä†TAÀC{iê¯ÔÌ@1œ°œctŸ@~6‡6c´ËÀ5‡Nóð˜ÀèSêðÓÀ7¥Ö9S®eÀ·”7Ï€î—@Šhå`ÓÊj@½•º«Ùs@¸ÐænE\@9´(OšÀìrZHÂÁ€À™ûDä†TA@÷ßp~lp@1œ°œctŸ@^ÿ~Èh¬@Ò(š‰àÀœÀùëâbç¨ÀØÁ0#AyaÀëèÓ¥žÆ}À,ì€u3k@·û×àÒs>@8UïB\@èÃÞœy D@e× ÝræxÀÓ* BŠsÀ™(H¯ëQ]@tg+î‚ü®?>W½eIl„ÀvºF4—éQÀ¤|A§Ýæ£@”¾³Äº»À,!Qìòä‘@"vшK{°À¯øt!Ô¨¨ÀÜCöðÁ>Æ@6h+î‚ü®¿™(H¯ëQ]@YH›}ÛQÀ {PëyÀ”¾³Äº»À×vÿŒAÙ@OŸ æ¯ÀäAj ZaÍ@9ùÌÀ¢úÅ@Dõ€¿ÓãÀ°Æˆ#‚@™2ÆåAkS@¦ËoS:óÀ£{jøÓè@pFf‚Ÿ€@Ù¶œ.áÓQ@,!Qìòä‘@OŸ æ¯ÀíÛi¥*A¦`YÙÆ@_ uäÆÀºt1Š‘ŸbÀÃЉT·À@$.OÎþéÀ& NêúíÀmEÏ–Ç»À™2ÆåAkSÀ°Æˆ#‚@r†žzUã@q—.žÈ³ÁÙ¶œ.áÓQÀpFf‚Ÿ€@"vшK{°ÀäAj ZaÍ@¦`YÙÆ@T~Aj1Bö@yãøÑ>TÀÑ…oR™¦ÀþuÉhgãÀü0.ž AOR'…c¿²À8ñóEÒáÀë#û²ÀYýõY:C@ZC}›pQs@M¼’ $"@éÔç²*±@éBÕwg”@Tð,û='@’2@”´PÀ•n@™õ‡ÀÀR}P5@r@¥b5ûД@ü@96’¹oÀj·ù7g´¢@,éúÖ¿vP@_ uäÆÀyãøÑ>TÀÁ»myx¹@zj9ØDyaÀCÛ}Hôw@[vÊ YÞ$ÀÁÒÕg‡õi@ [=^¡}@˜~¶á0¯@›pTGýÝ@á0ù»X™@ìú»ãlõCÀ æD nn@t9ÈÎGÍ¿ý$R¾’@¿&ªÇ?À®;”õU@—$hˆðÄ9À?I’ëyO@7-šµð7´À’M¼’ $"ÀYC}›pQs@’2@”´P@Tð,û='@vd·yX^À­ÐñÁÁÀü@96’¹o@¥b5ûД@+éúÖ¿vPÀk·ù7g´¢@ºt1Š‘ŸbÀÑ…oR™¦Àzj9ØDyaÀAÁèø5Ù½@\vÊ YÞ$@BÛ}Hôw@Ÿ[=^¡}ÀÂÒÕg‡õi@îú»ãlõC@á0ù»X™@29ÈÎGÍ? æD nn@¿&ªÇ?@ý$R¾’@˜$hˆðÄ9@®;”õU@ý@ïãê}«À…s£øï”@D± TrÀó–„a»•@%7“%kÈ­@”Û¦|kU¥ÀüWq&•@‘"ü”™ÂÀ »õâe„•@¤‘Ýbƒ¹À”Û¦|kU¥ÀŽ®Yäy[Ï@»ä/A‘²@IîB>Ð…@ˆ çÓÀ7q„B£“À äCvü¹±@é^Q@£|²TEá@»Èœ!Ñ£@õEçç~ØÀlc%2Ñ8§ÀAhãB7Î@Ù;üY¾c@´‹”þþ“ÀÁfÑØl¿ËÀœÌÕ_ @©.²ø!l@»Èœ!Ñ£@ñ§fW3 Ø@PÛÞm>ªÀ[~ ûÅÀÓ6T7Òßc@çÕpšÜ…ÀBê!èNªÀÛ Á@1tÀ\HsÅ(°Š@.ºªÜxõ?,£ÍD¸Žº@nõsÏn’ƒ@8ðͽ1·ÀSúŽòCõuÀm@yk”ÀäêYÍDÔˆ@J+,†h[@RcT,úxÀ~It2pÍ–@ÇXÞ?ch@æºÊ%¢’@–-ÅH‡c@E±ü\ÖK{ÀÿÂâÿžž@­á­£êYÀ;_æ’ÚZ£ÀÞ-ºªÜxõ¿\HsÅ(°Š@nõsÏn’ƒ@¹žÍª@.õÆÒ4që¿q¦h‰Õ`°Àë>Æ@®ðeC™¤©À$úðÂfÒrÀÓ_tkö&‘@ÇXÞ?chÀ~It2pÍ–@–-ÅH‡cÀæºÊ%¢’@T§´´'c±@£›–3ޤ @AQ¡&È “@p¼TÊ¢vi@V0·½;Ä‚@©ÏtÎ)k@^[ÕÞ]¶@Y1£ ¢aÀXÎbΓ@Î!tß`@8ðͽ1·À.õÆÒ4që¿À´*hAóÖ@q4¸÷vÀcuî(w@óë{+Ü`@²Ù’ÒŸ/¼@Vöø;…leÀoá0â°§@Lq¤¨VyÀ¾-¢&òíÀjƒ]©æ¡@¨úÒcXd¶@ú”E¾Ùò‡À:¦ÏCý_Ò@PgÏŸ?Ç~À¡¹owä’@Á÷Gæ>vh@äÚÜÍ6»@·—¼‚MKhÀÑ|ª¾×’@°îMmd’ÐÀ+»ª¤ÐÀmǽP4§@Ÿ¸˜¿¥Öx@¥›–3ޤ ÀU§´´'c±@®=¡›%’d@ œ¯:@W1£ ¢a@^[ÕÞ]¶@Ýìˆëc@g¨ÿ_å0@SúŽòCõuÀq¦h‰Õ`°Àq4¸÷vÀ¾/ ÑØ@Wöø;…le@²Ù’ÒŸ/¼@Lq¤¨Vy@oá0â°§@À¶»å˜À ÜÓi€ïÀú”E¾Ùò‡@¨úÒcXd¶@NgÏŸ?Ç~@:¦ÏCý_Ò@,s»0ÉÌc@ó"a £9@º—¼‚MKh@åÚÜÍ6»@¹6Wûs¿c@“êH‹À\îr-)ÂÀäâ°u¨Qx@)0Ú¬J@RJâjQmÑÀñ‰RXp²ÀÙ-ÈÂtºÒÀ3ÄÑ(ä·@Y}.ºÞ`à@-#gÝ›¼@ÂëkÊE0«@üj  +ÄÀÀF«÷Ó‹§²ÀvxÂ:¿À³2‰,¬¶@&ŒÄh/ÞÀ-#gÝ›¼@™†©"ßÑ@AZÚß)PÀÀ-M šÔ@K,—¸…èÀþÁ»î ™@åÄ2±°r@q¥· Z@C ‚²Øª@Þœ!žKoÀ®oø] åª@ݧTè¸IÀñLÂÕ_N¯@yP–¹ï“„Àm@yk”Àë>Æ@cuî(w@x!¹çÝJê@òÚ÷4Ý´ÂÀéÁ½á ¶@¹³ôúZ¢@°vÛžíÀ:±y%¸¬Á@ïÑ|u޲@ÖáJšä\”À#ŧ°‰â¹@›Eá<9Ò}Àr\øÌ@hÚâ(æ1…Àr\øÌ@hÚâ(æ1…À%ªZ@y‰óö;þB@Þœ!žKo@C ‚²Øª@ݧTè¸I@®oø] åª@xP–¹ï“„@ñLÂÕ_N¯@äêYÍDÔˆ@®ðeC™¤©Àóë{+Ü`@òÚ÷4Ý´ÂÀKýÉ(¢hõ@·ö}ä”@ÍÖtÞ̯€@/zn[»‹”@݆ʒHóÀÖáJšä\”@ïÑ|u޲@œEá<9Ò}@$ŧ°‰â¹@gÚâ(æ1…@r\øÌ@gÚâ(æ1…@r\øÌ@àd«p3ÉÀ§#W ^ä˜ÀéÁ½á ¶@·ö}ä”@Õ[öÛÈ@ ?J¥EeÊ?’q1k±·ÀQ\F£@ú—ÒºqÀÒ¾¶ªx¬H@|Zû£½4kÀ"Ø:]-q€@oM]¡™Àµ¬MgAðŸÀÈÕ`m‚[¾À¹³ôúZ¢@ÍÖtÞ̯€@ ?J¥EeÊ?mwÙÛÈ@fr@Ó>=¡@¦•5ûÌÀ¢Ô²–lY“@iæ‰ëtíjÀŸÎlñ°@áK«Z«ñ¡Àߌ’I¸ø»@ Œ[¿eˆÀgF%çñP@ê|ªñŸ]iÀo®2ühŒ@@Áçïa@é“Úo`“I@SZ^+YŠ@øçðÛÏB…ÀæÄ¦ ©áP@<aÒ<†À…‰aÛ“@qè—+R˜Àg]¨­ó+I@Ì^T2@øçðÛÏB…ÀCù­Vg£@A’5[1‹À~Î?, @ÿ·g‘¿k@YjJ ,øO@JpÚªä¦@ÅP@ ¶&hþ“2@÷ètàƒ@yiÀîa@˜–QYs¡kÀD,僑@ TPš¬û†ÀLð“nïǵ@‚^±Ý_=}@Æ'VŸ¡Àl1]­é@@F?/1h"@¸Z ¶˜8qÀuÿ;áB¬ÀôOCW2h@”°|èøŒÀk𛍧€¦Àì¼ZŽ+ù”@÷‰¸1zmÀùMêa":±@lŸn½2?X@Ô…íšÀŸZOØ~BqÀ/c8h0ŒÀV+§WYà°@ÎHp¯Þ@p¡ ^ÜcÁÀ†,Éæ/‘@lŸn½2?X@Ï4ËSÑ,¯@ñlV˜¸)qÀ7‰Dk­À<Œ/§@½¸¶ †yÀ½zUK &@8Ó½;H1ÀÉÚù‡?WÀ ÍÕôáMn@oŸè±`]@£À©ê¢±UÀŸ óÊðdF@q`=&uÂ[À©ðÁpË~2@ Òd\ TÀ‰ßÏ“ù @œ(îú À’¯ ûfüYÀÃ?Zµg‰P@¹4üì}óÀ $(Äñ@ÁÕ'/lñ0ÀŽ\⢒:@Î0Dëáb@8Ä»J–OwÀ£À©ê¢±UÀ&é¾ä3 h@—â7QÀö5VzùVe@oø ˜o<À$GÍ :Î^@¶Õ—À»Âv[×*@V.9{¬pP@J-'¾¡dÀavNÚùÁ@UchD=þ¤@³ÛN„* A× ¤´åÁæ@ǧ†LñÍÁµRt8»äÀ½²É«óK¸@és³½Š˜„@ÿ)žœ[Ä@óí÷âdz°@ËâÐ1Ýì@È߬¼ÎÉÎ@ ›\ò¨³üÀÔ¢á½ãÚÀÛZŠº ´@«b{9u& @:ÿˆØLPƒ@× ¤´åÁæ@ÑKybJ¡Ó@µRt8»äÀî1õ¾ØÂÀ…R¯[Z–@ÌÆì½òb@zÊr8º¢@ý”Êo»Ž@œUŽÊ@ØkQCS¬@Ã¥uz¾EØÀ?1™g>ëÎÀ%Lóú’@­7`@³ ³h %Û¿˜äF, ,Á†G}Ì@F´Z©{î¤@)–Úñ d€@ÿÀsÝÈD»@d}v¹p$”À ›ãy/ï@9IÌôòÌÀâbL“•¯@Ánæ|¬ÙoÀ Ô¯ÖÀ„ÃÅßÃ:’@BÍŒŠ«•@s§V |À y£™™›•@Ž¢ †O |À +èñó@[ °ÎÄ’@ųh %Û?­7`@4¾Üe¥aÐ@êFBÁ)–Úñ d€ÀF´Z©{î¤@d}v¹p$”@ÁsÝÈD»@9IÌôòÌÀÜ5óZßù@¿næ|¬Ùo@ãbL“•¯@í{ýÞÛ¤À÷ÉtÀƨÔÀs§V |@BÍŒŠ«•@Ž¢ †O |@ y£™™›•@Y °ÎÄ’À +èñó@žž†ï‘@EßI¦øIÀ˜évÕö. @çaGe5À‰ðÌ´®â3@ä.LÃJÀI…!@­BÕØ 46ÀœiÅåŸÙŠÀ¿óQèMa@™§.{‰RtÀ&Gìþ©V@Ùúö¹sTÀEßI¦øIÀWûÑ»“@@o#5ÀBó¤“K@YVC¸òIÀ-Ç]R#a@ú«FCÐ06Àµ)ºüøL@cði—4Pa@qŒÈ\;’À®€üšE!@ÇYò…sÀôü¯j@Œ$2µð:`@MyŽ£œB@2ÿ„ô`yÀ ¾ÅËtcÀÙ†‡<~’Àk÷1sfqÀŲrX_}¢@$¼iô„ƒ@p‘¢$ÛlÀQ>²òz´ @ùŽç†Àn›É&O«cÀ­T|ÿ;@€4¬"6.RÀGe®¼B@ÙìâÕ$@ç] —èbÀég…Ý^¹KÀ“à ¸ÜsÀ}›.°ËpÀ$¼iô„ƒ@ð꣌ø$†@c'x+$#@}ŠË¬¿|mÀ°Ý¨Õ`À­¾3å²uÀp²ôƒFRÀ¯û"|þ»g@zJ‰œ1Z·@ø‘ùM7š@T™Êwö½ÇÀ²Õ.sƒªÀÇ—OÑ̯@«³¿¹á—@úgýù;Iá@„„Ò€ºÏ»@qä°\=àÀÞA¾¸~ÔºÀ:•xš@œš\¿â}@Ó•»µªÀ #x½¨ÀÍH±;¢ç‘@¶‰:z@„„Ò€ºÏ»@4¬¥'ÍõÇ@[Ð%Z÷‰¹Àf7ŠÌÖÇÀÅã˜<úÃÀ»˜ñ¸;•¬Àqä°\=àÀ[Ð%Z÷‰¹À‰?€¢·ä@³ÖQ‰TÃ@lÉ2KÀ}@Ëát ¡·r@‡ÝÝü&®ªÀuÌå “ÀÞA¾¸~ÔºÀf7ŠÌÖÇÀ³ÖQ‰TÃ@ô®Ê@ª³8ù'x@&Ю‰¹j[@þÇPWXLq@vM¤.Ú¿ÃPd`ì/Ÿ@xÕ…žQ†K@[ö¢V®ÀcEýåO+ÀzY!{÷r@‰ !Ý\@Œ¨ß8%…ÀòÍæäcÀ!wM¤.Ú?þÇPWXLq@xÕ…žQ†K@J™nÓ½@ñåïÉs©*@°ýh‹¬À>álºa@rÓk1J@²›!‡¯SjÀí ý|÷ÜtÀ cûGV“ÀšÂ[’.n@ǧ†LñÍÁµRt8»äÀŽžÂÛôAòöŠE8ä@šÂ[’.n@šPœ‡%¢ÀµRt8»äÀî1õ¾ØÂÀòöŠE8ä@£¹Ö bÇ@PX8?`“c@–ÞÜ03†À¯„?ÏþT×@N §–€ÆÀŒS}9¶œÃÀjœLJôC»@µ{Êa©Œ@ß•åG°À„R¦+&ÍÀž~ÏpQÂ@'õgÏa‡À4­xA„ª@N §–€ÆÀ˜1,Óí@y ».Œõ¹@_ÝÞÔbÖÀ%Aƒî(˜ÀJ©S î»@t%Ieû»@4¤Sá¡òæÀfH‚¹ Aü>aûß»@Ð1&Áy•|è߸ÀõD"sX’È@ѵé H]Àí¨yïâÀsbj\ÛX@áSŒµÓdpÀu_‰œŸÇQ@Q·«ƒ‡9†Àü>aûß»@ˆòùÚø@{!2Ú·À <¯v õÀÓµé H]@õD"sX’È@Ö$ ¸}ÀþÍ>²¡âÀËxñ,j¥@³wŠ7ä†ÀŸíßD¼@ŒS}9¶œÃÀy ».Œõ¹@´ç›¨^ç@¨†çjÓÛÅÀn4zÄ…ýÀÞ© ã§=ËÀ˜•¦–•ô@ÄÒÓÂÒsÕ@|Ÿ7Z®¸†@*©Pϳ#«ÀjœLJôC»@_ÝÞÔbÖÀ¨†çjÓÛÅÀLÿ·³ãó@žûq:õ$ÉÀ+¿TÁ®"ðÀø„°÷„àÑ@çžæž³@¯nyÛ“À²ûµ{Ƕ@™”5¸@3:n‰óœ@½²É«óK¸@…R¯[Z–@Ð1&Á{!2Ú·À|~\gnA¨òU²@Šä†¼@ÎY«§@qA?*çã@ðg2Ø:Å@18hÔÍôÀb¿Ýa´ÑÀ©Óƒ†Er¬@·«`aRÓÑ@ö9Œ¼§°›@[”S„“…„@¡|°“ Šh@és³½Š˜„@ÌÆì½òb@y•|è߸À <¯v õÀ¨òU²@A/´`¤&ù@éý/ìˇ@¶|€†s@aYtÝß°@|0ÿ‘@õa’/¹¼Àv¢é9uVÒÀ© ) x@Z( Lt @F„¼Á_i@Zu¢Åøê©Àq{°"ó¡@Äï”õ\HÄ@½U‡lA¨@ÿ)žœ[Ä@zÊr8º¢@Šä†¼@éý/ìˇ@ ˜DPjCß@^ÅÝ—¬À˜j‘ÀÁ¬ð@’OÎ3WÉÑ@AT—Á?1œ’ÖÀÀCõÕ·@ÒdMÄH•@®¡þ^SÀÀzªÜ‡f¤°@Ö®›uéæ“@óí÷âdz°@ý”Êo»Ž@ÎY«§@¶|€†s@^ÅÝ—¬ÀW+ɧŽä@~µ)²#]Û@‡°_&0½@ºÅ®×cãÀV‘p¶Z`åÀŠe? Ž£@(âxÂì@$£oc2Ñ@ËâÐ1Ýì@œUŽÊ@µ{Êa©Œ@%Aƒî(˜Àn4zÄ…ýÀžûq:õ$ÉÀqA?*çã@aYtÝß°@˜j‘ÀÁ¬ð@~µ)²#]Û@yþw‡JÒ4A}~¾Ò"AÐT\0´û6Á9¯…~‚"ÁD:Û!›åà@ø¬ÌÄt­Î@î%ˆáW²@È߬¼ÎÉÎ@ØkQCS¬@ß•åG°ÀJ©S î»@Þ© ã§=ËÀ+¿TÁ®"ðÀðg2Ø:Å@|0ÿ‘@’OÎ3WÉÑ@‡°_&0½@}~¾Ò"A2¬¤‚èA#®оJÁ…‚ÐßcùÀžµÇÎúÂ@T®òÁ(¤ÀèöZ‹Œ’¿#I`Ä0âÀ@\-ãéK6z@QÊ"¤zÁÀ¡tË¢1ŠÀ ³õŽŠ¦@Y}-K-z@Ð=0;€ö~(¤À\-ãéK6z@Ëö4ÛóD¶@é3n†ì²‰À\F lÔ¨Àv:ù"/y@b3:œz?M@`Ò¼¡l©@»|<ÀŒ@oÐmwk¢­@b8L7z@Žð»âÀ9È.8ä©À¾Vâ.ñ@ä['EÃ@GŒ¢ ;éÀ“aé„bdÈÀüqMcêÁ@…Ô`FX«@Zi‹uäåª@•›R§é+@^çé òŠ@Pnr%Èm@­N9ãNh@™Åí”È[@Iæé0¹©À1ß´.ÚÀä['EÃ@?BvØ.Já@}SïÁî?ÇÀæ–ÝÍÃÀ‰;ÈFÉü¢@WhCûŒ@ŽîroÞŒ@òcô«„p@'’õ6¿ÀÛG…׃%&@heüsÉ@@þÏù´šWÀߥ‘2_ßO@>°[ÄeÀ`ù`¥¯U„À…•œŠ@!€_®àùz@u¿*¯aÀè87µLv8@¥6âC¹ñOÀMQ¨ƒ‰'@ešœ'+™>ÀÈnÊ·N@Ït‰2d~SÀ3Qg'%@Y;ŒµgÀ ‹xAÃ1@¨7 ÞÏWUÀQÒØç6&@(3ð`=ÀáY1ûïaVÀÉRMñxo@@aÌ”?eÀÿê$[j%|@P@v„†@•ö¥«`¤Àu¿*¯aÀødP-¶<‡@›Ð$ÎÝNPÀo$ì×ÐKe@làb?ÀšÅfT@0Ÿ1zdÀÀ› îÚýi@›Á‰|‰<ÀÎù¬ºœ@¼ªö¯GÀŒŸ (ul@Fö—phØ@°E¢ÚG}Ê?Vvÿ#çv®@­ášµn_@ÇÑ"ã¥@“•/ß´qÀd¥6pàÀbT!ì\éa@J£Š¼Ëž@‡t`ÏÙ‚$@FExäÍiÀ°E¢ÚG}Ê?x5¿ô›Ø@¬ášµn_ÀVvÿ#çv®@“•/ß´q@ÇÑ"ã¥@†F»FÕpbÀÇzÛújàÀzt`ÏÙ‚$ÀJ£Š¼Ëž@Ï$]¥×­ÀŸuHœ¤åÀº¤çãjw@+>í2Wµ^@Û(LüÏ%¬@¶N|aÿzx@ÜÐ- ’uÀYÆ,B™Ú¿‚[#‚¶€f@ãÜþòµrM@goÞÄëÀz$˜È¡À¾L=»Ññ^@\›JD@¶N|aÿzx@F°í6ºç£@ëæAó¿ž|•‘ätÀˆökL’M@Ú5yìOV3@‡‹dbƒ¿@r$(Á&M¡@Bƒ W¯êÀÖ#¬íÞ¹ÀÖÔEKï¤@pj£ÀÀgÖ‘ÖïÀѱջPÌ@-] sþ©ú@^ ·˜À#^yÅDh¡@]`(ƒ@µÐ¼`OF¹ÀÙW‘e?ößÀ\²€I´ÂÀKܽÉÞ@9Ç’Z­ýÍ@4èj‚ñûÀ^ ·˜Àó¢rdü@¶šÁÆD¹r@¤„€ƒEK@ÓU¼,ÚYÀ Ç<¼‚ŒÿlÀù†º2¨PÀbϨ€cèA@Ô$yëÁ÷#@¤„€ƒEK@›+þ^ÏÔ]@€}·WŽ ¿MÜyÇÒYÀ©(?=¡PÀki3âë1ÀS2ž?Šç#@vÁd¥µ1@³TødÅÀ¥"Ë‚A’@Ï&nK1RˆÀ$µ/Ÿ`ñ<@hœaÎ6VÀÅ­ËÍ.¬ÀàšÏ{?vÀ³§‚¬‘X@~Ò›5M}À@OÛZ›@X3 A\æp@š#VkçPy@›ª/ºŽ[‹À̵-ÇÓE@dJZs©`Àt?ET¢éÇ@Øýw®ì“@Þuø[°š@+¨ÿ<[ÏÀBíC©Ç¬@¤Â„³áaÀ®’A' z@÷†NK…–*@ 6á'ÙZŸÀe àÞÇ}Àbe/7V¡@Y3 A\æpÀ@OÛZ›@ÝeØõÀV~ƒÅ/°@ öï_ÓiÀ‡Ø*s·ƒ@Øýw®ì“@bËwt´@9k‡Æ­1@i“}οHÀ_¥ˆ“ž@@lqýß¹VÀIÞÙ–4cÀ Œ7cN}@è87µLv8@›Ð$ÎÝNPÀÊ3 Ÿô}G@ºËÔEǦ^Àª¸YÏÑ‹@wÖ''÷è/Às&xYµ9@›\¦.J[PÀ؀Ȓ@@9;ZŠRTDÀÙ9•îªCkÉÕD@`ˆ²`NPÀè&¬®Ue@¼Û )êTÀÓ…bµ%ŒZ@Àº(úyO^@ÐS4ÉsÀcŸ Æ^³,À§gOå$p@ížxª08À»ß]@Äb–F3£@‚t^%„Àpõ…Ý‹@7ïõ°Lý`@“l¯(Ä@·Zhµ§Œ¦À£Æ•Ì3·À ;éÿ*ë’@áøœX‘ÀW*5.{’z@8(K)¨‘¹Àq™u³†‘@Tt^%„@Ãb–F3£@ …–…EV\@l ]ròPp@·Zhµ§Œ¦Àu‹¯„tÐ@8ѵºXÚ•@ŒÎICóÁÀ.­åH $@.º´ˆG Àäàö¥ät@ŒWkk.ÀÀÃXtO‘Š)@8$ Mú¨@À˜évÕö. @@o#5ÀòÈå=;”@Þ­ž¿Ïq@¦—ëx]‹À‡ÇNsøIiÀk?I°÷3@ådKS3JÀÛ]¹þU1@ì¶òT¢FÀºÛlÒ hÀ’ ƒÜ9FÀcø½‰@ÄqÀIËDWµzc@‹¼&–T‰TÀņúþ:5À”,·¡G²K@çaGe5ÀBó¤“K@Þ­ž¿Ïq@Imæj÷̆@ÂDùk¥kÀ·3º VC{À7‹waJÀâ„Ëða@GCÈtBÀݬinÄX@`âÌdö°@;¶ïÙøfÀåQlåì>P@ú1³*ЄÀzÌÎuÍj@¯ÝÌ—ö§ÀàÙ{:Ä¥¿v EýÙ©@%Ë4D'\@uÏuÃ2jÀ%Ë4D'\À 4½yÏ™vö§À%Ë4D'\@´wîIœq¨@%Ë4D'\À+ 2DANÀþ]dZéæˆÀíQΪÏGÀó¿õ‡ 0v@e}Ur@³AûçkG@žíã ²^’@ú«QIŸlÀ Óþi@‡µaÂKY@@ ªv¬yuŽ@õãü“5²À˫߮ÜeÀöhËU¡@xêU0¨up@×Y?Ã1¢A@G‡²ó0@͵Udž_À‰Ô6\‡áv@5˜Y¦âM@Çcp¡@$o<Ð<³ÀéçrLúcÀÖm¬h¾z@™nžÒ¿8v@Zæmaµ¤Àœˆ0kûhÀ¨­t«W€@Z—W\ÑG@ìºv@ú«QIŸlÀìq ‡¬@­ÀØ“OÀ†‘G¿“g`@›+uShÀðP£ØŠ¶@Ã>"eýJ@@çKKBC@ìo®ç‘Îc@Å®¹çé…ÀIñ¹òjwÀæ¸EN¦‹v@qåLÈ$hE@¦˜¦Bï@¸Ð︼ÌvÀ4ˆ´Sfu•@wU иEÀNzj#ݹw@nbַĤ_ÀHº ºl@ò©<öÂM@âƒ<|o#@6sMÔüsð@yb}¿üßÀ@#pk âgÅÀâCÉ/°@-0¯zœA°öKNýÍÀÅ2Äÿ Á±û½›Ý @3Γ¦™u¼@íÌßi0@ýmû ¢°@pJâtçÀ°öKNýÍÀCà+ ”ÂAbb¸­Pª@¨á;£hÅ Á¨ß<Œ„ÀH*Äkƒ 8ÀéÐØÂEŠ@'öÔ&J`]À!gë‚mž@.8ù_/î?C¸?"Rñ‘À¹ør—a@”¢j‰Ì‚8Àâ©aX}pÀ'öÔ&J`]ÀÓ.¨ÌÞ9“@ù7ù_/î¿!gë‚mž@È÷¼­Þa@‡U«× N™ÀAbÖ¾U†S@#”“Òáß=@AËì0ù°|À kàXÖ=@¼ÌŽ´~ŽZ@:Úƒ'4XD@ýíüÛÐg@Å sº¤Ó]À(âçæ’ªÀáÊâ‘ÉN~À㼇x«„@Ü… +>€@G¡µL»b@á‰át‚Xb@ºˆ„ª L@|É€òü•@¨§½¨  r@#”“Òáß=@¨WbõÚ&@ôA-ßLÌ=@‰°“.uk€ÀÒro $QD@f½³Øÿ /@Å sº¤Ó]Àô²-²¦Ú’@¯_â À£÷V*ŒÀó©â»g o@qþŠíZÚh@ &ŨL@T=Š:L@ÒUi§„u5@«w£÷bÒ€@5Flˆ†\@‚¤ÿkV&¶ÀL[~s.¢@ø¡s5Š@VÆÁÚ—qÀ'î@ûY7æQ£ÈÀ`Ûs½GíÀ?™Y¾,Å@›h¬§þ‰ @mdŸPƒÔmÀ¨$‘@ˆáÈ$ôLÃÀVÆÁÚ—q@ø¡s5Š@ûY7æQ£ÈÀ‚å†?€ö@¸Íá¾ÀzÅ@]‘RÔuÏôÀndŸPƒÔm@›h¬§þ‰ @«V|»ó‚@ÿf1ž<œÀtÐòÆRc@»áƒsÊ6zÀ`Ûs½GíÀ¸Íá¾ÀzÅ@VŠŽ¶ãð@@`tò WËÀ°ëÂ÷bÄÀHî¯l ¹@¹K3™ázA@¥]²UðdWÀNTw0º©„@¾:l âšÀmVoH!@»F.Ül6ÀÈtx²ÜmÀß` ¬‡@‹QS)Ö¨ZÀsP[ùD™ÀšË¾¹Ò²@Œ\Yƒ.kyÀj»ž%¶‡‘@?™Y¾,Å@]‘RÔuÏôÀ@`tò WËÀˆkÏëõ@)’*·´@][þ/ÓÆÄÀŒ5û`WÀÉPùIo@6íÑ;ƒÀIJ°uߘ@‚ëµ­‚7À¦>Ê-þM@¯«2ð÷ƒ@*{¢‘èŸÀª=•6úÓq@)SFEå>@|<`é\HÀø ¬h¤+ƒ@uÙãi³g@>cÙzâ‡À ë$,¬bÀñJ²AOrÀî¨Ü †ø @Kj§wîw@á!_¬Å¸¿È!RvCHÀuŽBÁ!S@uÙãi³g@Ù£UÃD{@‹½uz±bÀˆù¬|Í´‚ÀÁ„èï?´ @xˆjÓìŽrÀ&Ý!_¬Å¸?Kj§wîw@½wˆè‚tÀežâð>nk@í/¬~µ›@ÿ½ Li6Àð¹>rÄçxÀî#b›/¬[À"›½/@ÁÂk=6À‹**FW…@e°g½tP@H ³—9!@`ü»4™?À~3®m{î@@˜íëAÀX7ë AË@Y)ó=m5À‚ Ú>'@,ŠœƒýEÀ<%À¾îaS6ÀЊDx ëS@e°g½tP@óèëFB}@ŠwÈî_?Àûz:Ä\@ÏëÁèöË,À¶[˜Ûƒ)`@ïeAu5À»h½»RS@'qoµòõDÀ Ú¾Ÿõb@°/Xo‹;È@ꕼ‘ê@+3ÖìêŒÇÀþÈÊ+Á€ÀyÞRŠÒ7ÕÀ96k†À• tëyEÝÀ2Lyw„°ƒÀÿé,)SF–@\™tÀ7”¦zä@1!¤–Ffƒ@@Âìö&…³@ÐØOpŸ}@Þã{ØX§@ž”¢1ëHø?ꕼ‘êÀ°/Xo‹;È@p‡â~U>À ƒÞZ<‚ÃÀðS/ô¬†Àú¥pNøSÒÀkÿV„¥œ@<äÜd§¬ßÀ\™t@ÿé,)SF–@1!¤–Ffƒ@ ãÕö:ã@ÑØOpŸ}À@Âìö&…³@¸”¢1ëHø¿Þã{ØX§@&6k€¼?Ö@_G~dœieÀ.ÁÝ䉭á@D„Y ðWÊ@6ù SääÁŠ ýÅç)ÍÀ‚¼¦ˆf8èÀ(̵N”°@#’\¸Ö£@#„m{ sŠÀ@Âìö&…³@ÑØOpŸ}ÀÑö´•ü@ŒK®ƒ…¥Àç·¢i¸(±@P˜"–@ZG~dœie@&6k€¼?Ö@´lP‰¹Ç@cµg'±@u8ÓÕÐÉÀ©†¨MùÀpoÀ€]¼“@ç19¸ÍëÀ#„m{ sŠ@#’\¸Ö£@ÐØOpŸ}@@Âìö&…³@ŒK®ƒ…¥À¿f÷Ð`þ@‡l’‰ÀX–@\9ØÇºa}@”!@°¿MjÀEôA Ôq@‚?À«œ‰À‹Vëj[ÀÄŠÃAe@SÂÞ<€=¢?§Û0’—í(@`m%úD@À sæ*,/@í¼×[dDÀ­ÀØ“OÀŽ)#ý‘ˆ@"üËÚ†“b@¹ìqZ.@È:"ÉCÀ6ß«õ s<@ŒJ)Ø¡ÆZÀ4oéê¿ @墓îš=À¦n«Ó|ä]@óÔ» ˆÀEû*§ h[Ào9¶Ú÷ƒÀ¤ÀÞ<€=¢¿ÄŠÃAe@ÆÑHmAR:ÀÌå%[Á-Q@2å©)u@À‰6-!ˆU@†‘G¿“g`@"üËÚ†“b@<¼ÀÈí€@æÂƒI$@Àjý¼ñ8äT@Ø NÀ­лEl@0¶»ižÀzuXšƒBO@ßEuž=‘@£Gma]@X–SO>Ì@e½>iÀ=B½Õ‚žÆÀÿ(™knËÉ@å=<# ¬@Óâ,Fð”@áû×EÃÀHuŽòþèà?ÛÊw;«(ÙÀïI›tº@(˜L*Z¡]@}õ¹z…vÀ·Œ«ÕÆ´@ðE$ïþ‚@FæpÌ\ØÀ·Gìž6ù¸@e÷ëtý_½@DúG£S•£@Õ¬û$¨@;“¹|)8•@]³â?¯[¥@ f v«p@ç·¢i¸(±@‡l’‰ÀX–@tì0D×ò@Xû\™Í·íÀº ãSοØÀbWU°Ñì·@‘߈ƒ(ºÀJÛ˜a¥Ñ·@©á¸Î/À˜O¾£@¾t‹ÏŸ(g@J´Bå±3@0æ;ì2Nr@ãdËé‡ÀqšðPX5¯Àf¶"¸˜±”@Ut•ÓY¨@hz»ÚÉJ‡@»H?4¤ÐÀc¦*0©Ä@|+×v4¸¹@·3Àð»;ˆ@£Gma]ÀßEuž=‘@^½>i@X–SO>Ì@é[ê –‘Ï@ábæ"êÂæÀÍï<1ly’@Üçs]ć{@Õ”'^s@5LæÅÀûpn’ÊT½@‹X *––æÀ~¤b‰¯vÀŸ_ö1>‘@ðE$ïþ‚À·Œ«ÕÆ´@zJÛ ¹@„#Íò´úäÀCX8Ug£@Y ËJ߉@ïeÛÜÌ•@8ñÕÿž‚@ f v«pÀ]³â?¯[¥@P˜"–@\9ØÇºa}@Xû\™Í·íÀjž'b© Au•pæIž›@ø›Éu€¡åÀˆ¬—o¹@Uü<±É×Àñ70PúÊ¡@ðd‡r“ÀÀÕ~nSÐ,4@‡:€S(@,ŽJrÒȆÀâ£ÀY×Â@Ö2ìÎ9v@d3%Ì»Àsª>±6u@,®%”“JT@©[¾â@Î@Y¯rL±JçÀ·3Àð»;ˆÀ|+×v4¸¹@ҽߖJÄÀ?@I"¹eŒÀy nâq@G PW#¨ÀµºXöIßÀè»´(5?•@úé"\>ÒÀN@[õï6QÀCÛ}Hôw@\vÊ YÞ$@º ãSοØÀu•pæIž›@z/…ò—+þ@Üéý ¶P´ÀÕ7{\fze@%[ ËÊÛ”À/±ç„n@*O6a«@‚n8Ñ0ãÀŠ÷§~ÉÄ@²WÄ\ž—Àxœ‘Žc@ðY ½<›ÀÚSØzmq@Šɽ(EË¿ˆ\xu˜ƒÀíüí»U¬ÀLZïy"šÀ2x­jÀC°@­¦ :1HxÀà³çÎw’@å…Ê}¡¢À›wö žÙ@cÌÔ&˜À!²?LH²@ •­µé¤@D1 x¼%èÀÞhÒÓ·]À‰ÏÿE¦@åá…ÛÚP@ó^">¬:ÒÀ[vÊ YÞ$ÀBÛ}Hôw@bWU°Ñì·@ø›Éu€¡åÀÜéý ¶P´ÀÀ09wAÓÛAíöŠÀMl¦ ñ`¤@†õ½ëœË–ÀgÝ)G#Æ@²EyHqÀbß7³2òŽ@ÙÊxK¿XWÀüBå¡ÌŠ@*O6a«À/±ç„n@½…¹xZ˜ À13n›%é²@ÄtâÔ›Ã@,\Z’®Áˆ|.É@7º`Á”Àñs*_èÌ@hɽ(EË?ÚSØzmq@>Ë@Ž»3@|'úv¹IÀõ)‹–Ah@‘ÀÀ®˜R@÷[>ƒ›ätÀ+·ò–îRÀø¸0°l’@Ú=˜Â§Ær@"Ù—ÐQsÀD;Æ$ôÝ$ÀÃê hl‡ÀcR3€©mÀÀOÜD“ÁIÀ-L/;Ê`@ÆÖ¾þ¹›R@‹uaˆ<@¾·Ê×ûRÀ’(à™§dÀÚ=˜Â§Ær@äV{ ã@¤µZB Ž$À¯/HbùÜqÀîf×ùú£mÀ§†HÊ:¸kÀ™²â\>¢ÇÀÎ}&©óÃ@—q¬é0 ƒÀ7ùX$ï@Þ¹Æu’ë`Àoô°J@Õ2MK lÀ5öÕ/ã¼p@`Ü$'–\~Àæ$ÎUw©¢@œÝj«ƒ°½ÀXLnÌú‰Ã@?¿Òû’¥ÀŽúåbÃ@?Kè K—ãÀŠƒrE@‘@·¨¾Z30©À¡×s°Vy@BbüycÀ25„G\V…@I²µúƉÀNTà$Þ»–@Äñ޸޼Àæ~úˆ‹·Ö@?¿Òû’¥ÀÖÏÄ™U!Ñ@Êx#hŸ Á‰™ØÒëåA|‘÷š ¢@$żJ¢¶À¿{qÙL›@Ôn~qä¶À磫Tÿ¶£@2-ÔŒ{¹Àú:lZÔÊÀØ@ fƒqh@1Û:íý@ KÜÀ|LT+ƒ5@ïÔ:,žT®ÀtçÎ;ù@—-[¶ìÏÁÌ3T‡¨A ugk40ÁRJy¸À1\¦c\-Î@*ý ;3²ÀÇ“¨ì…Î@‹ÚäpTIºÀ#8³«üÐ@Ì“ }1Œ@ÊVØž@ÌÀ KÜÀ:›èTÊAüep䮜¥ÀKãÑri8Ä@Nš4iÒ¸ÁuçÎ;)Anì«PŽÀ aÇfC;@N$ü£ƒC@»ÿ¶ÛxYÀ9¾_T@âò©+?@╸} gH@pkó4í_À›+uShÀ¹ìqZ.@æÂƒI$@À"Ù—ÐQsÀ¤µZB Ž$À'ÈùfŒ@0÷)Î3¯YÀ•‚x§*õS@ráó€Ÿ>@=D‚RQEV@¢©‰/òõtÀ¸1uË7%@î Rñ,WÀŒ&‡q‚@…©SrPبÀ2E/"qYÀ’%˜ Ôšp@3#½C=6?@›©wˆà'@,ÑÕ–¨Ð_À,ΚÑöÏt@ðP£ØŠ¶@È:"ÉCÀjý¼ñ8äT@D;Æ$ôÝ$À¯/HbùÜqÀ0÷)Î3¯YÀæ­‘Ç‘@“:‹í“>@+‰Q?u'@<Áf+ mÀhòœìS‹@·wÆ©;ÀÔ|-['7n@>cÙzâ‡À‹½uz±bÀk†Ú3`²@„“ØQe[@D‹Õ¡ƒO@|a]Ì]D@°ºçHa²À¨ÑuM{@¯ÔɃ}$…@,“…¯ À ë$,¬bÀˆù¬|Í´‚À„“ØQe[@¸¯‡|Eú±@­÷ 9mD@%ê>r׌9@Œ'ÉtÐÀª´áBa²ÀO“…¯ @¯ÔɃ}$…@Ùý*K±«@´orI¯Ž@ŠÈ׿ Ñ{@ ØÀø¶O‘ÀQ 5>ØY@þÆd¨sÀÒj ¼´y@£’3@u“À±ŒPч¼ÀSÒàV`ÅkÀ¿¨:‚Q®…@z@ˆ‘F†lÀRèÏ”‡Ao@Éïô´‡À‘߈ƒ(ºÀˆ¬—o¹@ÓÛAíöŠÀåp°eC¸@n®“•B™†ÀmÁ 7eR@×HÁ*xpÀÍ,GcÙ8@ vT (†lÀ—·#Û©@‚*í^´ ”À&¦»v;4š@ƒŸ]ÿteÀ´orI¯ŽÀÙý*K±«@‰5áþ L•ÀÎJç,‚ª@m˜ÉsÀǵP*\Ž@È5ûù_®“ÀˆøÆ/ŠË­@6–.×·@6[d§/ÀÀz@ˆ‘F†l@¿¨:‚Q®…@Þ-ïÍ+î‡À$ô•‡‹&¢@JÛ˜a¥Ñ·@Uü<±É×ÀMl¦ ñ`¤@n®“•B™†À8¾€õ¿@.ÈoGü*lÀ‰÷wéÇ7‰@LÞspSÀpª¬¨¶Ö…@±Ž_ò ›À)[1Ò®@ƒŸ]ÿte@%¦»v;4š@²ÿ>Ô™n@J^ÏmŒ!rÀ·lyJke@¼¢^礮;@v#x+ÆÀ"ÿž÷•ð€@òalÚýs@1“c—ÎJÀ¶JnÅ¢¸À øYÀn@¢º›û’@nèÒØ&¿hÀfÎ àɘ@óOÝÝÎó¿ÁÒÕg‡õi@Ÿ[=^¡}À Óþi@Ã>"eýJ@@9’œ´j¾Õ@»´Ú Àå_7Åøÿ@Õ7GW;\°À~~¦‰øÈhÀõ›.}”@ðïEnÿã§ÀÕ²é 3^BÀÈä-tc@W yc×4@ñZÿíÞP@¯ZËŸ¡KÀ³ÅR®×±Àuâ¤@Á{@Aý> k@WÿG4©A@Ç»_Ç{kŒÀ¢¹­@u\ÉV`KrÀKkóÒä @" Ùó;@Š% `"@%§ÈL.‘@6u“ÐÀ1“c—ÎJ@òalÚýs@·ô³oÀ`Ü a–¸ÀnèÒØ&¿h@¢º›û’@>PÝÝÎó?fÎ àɘ@ [=^¡}@ÂÒÕg‡õi@‡µaÂKY@@çKKBC@»´Ú À uÏÂØ@¦Àü'ˆ}W@“°-xÀ·¸qqm¿–ÀF\ý¼j@kÀUQkŸA@z%j’ͧÀ,-YçEc9@¯)®ÃÔ2 @°ZËŸ¡K@ñZÿíÞP@ÙKôT=…rÀ]˜3ît¥}@\¬—Ç„×PÀ)5³²’ie@fzýjè‰@P”GÚ´•ÁÀ÷¢ˆ^j¥A@°\ÇÀÄ7Ávx¹@ÊA}uÌœ>@“å=^Ù‡Avñ”ݒׇAZßøz’:ˆÁ£ ¨$ë‡Á·U.¸ÕB!A½"JÀ"žû@~î—7¹º}À¨É4v,WÀe&$ASMv¯ZFç@``Þ¿Á•>@ý†µÍÒÀÆA}uÌœ>ÀÄ7Ávx¹@vñ”ݒׇA¦N¯áÙ‡AZßøz’:ˆÁ£ ¨$ë‡Á·U.¸ÕB!A½"JÀ"žû@iyÅ¡)Á¥‡ÀÊÛôéÀaÕÕ(½Ó•@U,=·Ú~²@f1$s‹~§@Zßøz’:ˆÁZßøz’:ˆÁD‘¥.¡ˆAWC£NˆA\ÓrœCÖ!Á¿´p›ªãûÀš:+wÁÑýcGÌçÀÈfÊ9Yíp@i%ÂÛ¯Œ@ѽ+?W8‚@£ ¨$ë‡Á£ ¨$ë‡ÁWC£NˆAu92{ˆA°á¤BK!Á¼½lcëþÀ4];'“ÀàOk–qpÀ¸¹ä'··ÀPÃxä<›@Û-8Ø @WºŽåx¯7À&XŸ3iû/@’«Û¡.EÀåá[(¬VÀq„X|‡0j@MQ¨ƒ‰'@làb?Àª¸YÏÑ‹@ùø´é0À°hÕ›bý»@¦ÊÆK?—À;u½%Ò.@ae²^Š3À–|¹]±%@(¬p‰gÊGÀÒ`JÎáÒ@ˆ+…–j5ÀÞ® ErpÀ>òà ‰ÀPÃxä<›@f žb8ÃÀê,Ô!ãå5Àr%ÓƒÊN@ÆZ.ÑÉDÀrE"l‰[@f›Õ%Uf@µ®CV8½Àešœ'+™>ÀšÅfT@wÖ''÷è/À>ªCkÉÕD@¦ÊÆK?—À&ÆÏ]Æ@H(œŠÀÍ%»K’DÀe{ÍmI@]ˆ×ùÌ}Àiy™†í^@xÇ%¿+'Àÿž—F]×K@6 ˜ª.å›@_‚w“×g@ÂŽ1¬@c9êéûX@.¾i>Ü»6@†rS%eÀÞÁŽ'ZNÀŒ¶}çk|@)xòÿâë´ÀÇ¡S®áÀ_‚w“×g@Ó¹²ýº`4@H(œŠÀc9êéûX@*Χ!§@UeÒReÀªgœ:™“@C\RÈš!|@°@ë•rÝt/@«Î.ò S@k*‹¨fÑ@]º¬KƱ@˜’E—غx@¨ÿCk–c@` â!¬D@ÅÓ®ðL9¤ÀÒøɳ®<ÀŽ á`s@ŒbÃÁO*@3j¨N­Ø@" É+ÄoºÀ}žpi;×À~SEJ?¿@MÀ¡Ö…•f@Rmõ$\r@îöhoœÅ|@œèÞ@“Àa â!¬DÀ¨ÿCk–c@¿£«¨Mú;@qÒµ5¤À†bÃÁO*ÀŽ á`s@" É+ÄoºÀ·‡®\ä@ÔŠïû§N¿@7ö|«¿äÀQmõ$\rÀMÀ¡Ö…•f@Üoë!{“ÀS@…‚ª@ñ…fêëv@q{°"ó¡À@ûücûÀœ¡˜Dû+ÚÀ0&8î̧@Ü|\È;À ›\ò¨³üÀÃ¥uz¾EØÀõD"sX’È@Óµé H]@˜•¦–•ô@ø„°÷„àÑ@18hÔÍôÀõa’/¹¼ÀAT—ÁºÅ®×cãÀÐT\0´û6Á#®оJÁ¶¯»6û+=A÷$51²UA:›üŠOìÀ=ó¬…ïH•ÀqG‚Kàµ@ô¼×Ò³/ÖÀ VšŽÜÀç|\È;@0&8î̧@Ô¢á½ãÚÀ?1™g>ëÎÀѵé H]ÀõD"sX’È@ÄÒÓÂÒsÕ@çžæž³@b¿Ýa´ÑÀv¢é9uVÒÀ?1œ’ÖÀV‘p¶Z`åÀ9¯…~‚"Á…‚ÐßcùÀ÷$51²UA7æâŽxAê8db,ÍÀ3èBd`&¸Àô©‰Ñê)ÁÙ.4µÓ3çÀؘ³î@0 +ƒ´@ö4Ñ.”˜@ÛZŠº ´@%Lóú’@©Óƒ†Er¬@© ) x@ÀCõÕ·@Še? Ž£@D:Û!›åà@žµÇÎúÂ@:›üŠOìÀê8db,ÍÀ£qØqf &AMŠæi=ã@3z9á]¹r@ÖM¿óä£À"2‹k4ÒnÀE“2ehÀ56W›íf³@”&^qþ;Q@ÞVÖw%6…À=ó Âe@²ûÀzì–À9“:ž ä@°zÛƒB²°@u7q}¸ÒwÀðpa7Ä­@ûƒ–îœCëÀF$8ØqÁ >³ÜĽ@3èBd`&¸ÀMŠæi=ã@Æ:”éAUE×åÈ¥À誹4t%×@yù@’ùí¡@TUâµìp³@VÒg¦ðtåÀžHk/ „Àé®E5Ä­¸@ìÿ¸‘“P™ÀëŸÐ À«Ê@%¯m³@ðWäž¼0€@ÛæVߺ®@Ç e ¦.ãÀù—¼X>ÃÀ˜ÿ¤`ˆ2”À£Æ•Ì3·À8ѵºXÚ•@cŠwnSÎ@êk~fýåˆ@oRxIv‡a@Ig0~6fÀÛÐ e[Y_@í “§†ÀdÙ3ëk”À`’<ЩâºÀ ;éÿ*ë’@ŒÎICóÁÀêk~fýåˆ@ŽÃóŠ·¾Ê@ºÆ`JˆwÀrN¬J}@Æ—õµq«tÀ[õüXÊß@?: ×"½ÀˆóA»Î @†·®ô¡“²@ß°uÊ.ܵÀ× ¨æ¥¨@ž§ ±Ÿ@eõhj1ëV@¾2Åý¥ŒÀiP$× Ü|ÀLQá¸]e±@”ž]K @ØUÌY†aÚÀß°uÊ.ܵÀœ}M‚¤7ì@ˆ§ ±ŸÀ× ¨æ¥¨@ÆÏ? `‹ÀÜ!pÇÁ@¸Ã 02B±@°ÃpTëÍäÀ[©ü§©Õ|@B~n/8°À3z9á]¹r@UE×åÈ¥À´0¿sKæü@` rG!ÜÉÀDÀ€ ˜õÀÎdæÃÂõÀá‚ï?â@øÒ®W¢Àp@þ¦Qž¤À!B@–ßÀ qúB½@ZÌšW&…@ÖÏÍpeH¶À‰'¸Û•@]6 + ÎÀ ¸¨LøÑ°À#Ï=V7ìâ@ÖM¿óä£À誹4t%×@` rG!ÜÉÀ’€56fW AHÌŠñÔ¿@SåXoô›ß@í%®h™Á(À)}¬Ì¡ÀYvnÁ6èÕ@T¿Dƒgݽ@Ð'ÄøÀ¿Æx½x¶ÀÓ¬ÚÇë¬ç@ª3™¸8ÇÀ §Òýíÿ@žŽÀOÒ¥Àx’4Í>@2*ÒK6æO@ˆ}/ßiÀÁr·@£š›ßQ9ŠÀ<üNÇ©ÀºÖÃÕ9¯Ù?ñ¼z˜uf@ íeýÂè³ÀŠWâ¤dÀîÄê=€@£š›ßQ9ŠÀ¶z ľ@2³1šß‰à¿Ø/á´Æ©ÀŽ·_+7¸„@Üü(Všò?Ä×´ìô•@U,§q¬À“YîÌF@3’òvåÓ\À?Œ1ôæ Š@kl˜‡Ó`@¨WbõÚ6@#”“ÒáßMÀ€Ø™8¡Àµ‡å“LgˆÀ ªv¬yuŽ@ìo®ç‘Îc@(âçæ’ªÀ¯_â Àå_7Åøÿ@¦Àü'ˆ}W@¨ÙÜÿöò@µ77îT±Ä@|\î%0ÜÀ•Æ6é¡„À^[ÚìSè¸@ÇÂÊH¦‡@d_ÕVY@#¦¡¬T!ÄÀ,#x?›Àñ`WN»®ÀÜê— ª‘À>WöBTWäÀ¼œªù„< À­eD!p@öü9&Qxe@Ñü(Všò¿·_+7¸„@t'UÐë¹ÀŠ_?”2ÊÐ@ù>±¶Õ cÀÈZ&ýÀÿx@#”“ÒáßMÀAbÖ¾U†c@-WÛm ±yÀÔ£ÀÂ.tÀáÊâ‘ÉN~À£÷V*ŒÀµ77îT±Ä@ýÑ‹˜”SÖ@bÜS¢þ£À°‡Îª¥À˜È4Ä'çŠÀ$e9Þ[V±Àœ›\Ë[ƒÀÚÅGÒy“À"œôÆçš’@Ø ®hÚÞÀq¼‡Ò@“QÌÕyHÀpN{Ú€.CÀJ™$š°ÀE×­­¿~}À÷ú!5”c„@vÜ’'‚ïÀVŒ?yæ_@&Wû9,–¿û™2Õ×®@†]oñ MÀdOK4|@¥ýåÐ¥‰e@õãü“5²ÀÅ®¹çé…À㼇x«„@ó©â»g o@Õ7GW;\°À“°-xÀ|\î%0ÜÀbÜS¢þ£Àò× ¥½ô@ë•k_§@I¢âóåÀºTb<î@m,m‰¦AÀŒÌØDîk@vdúJlÀ,B›?Ù­ÀÀø)i@~ÀL+Y2¡@';Da7Ôƒ@9öÄñkƒ@ÌÚɱm@pXÜG·@­$ ®·“@­V‡[šs@#Ô6Å•IÀˆx#ûEµÀŒ{¤LIJ„Àjc1ª‰FÀM­µyí¢zÀM­µyí¢º@øK=#f£uÀ· q…œý¶@ÐæFì/]HÀun°§±¼r@9-.¯m@q’¬î©TÀ"oÆ3ù”ÀiÜ’'‚ï@÷ú!5”c„@Wû9,–?VŒ?yæ_@fxv(rrÀ^>1>ãû²@ ;Íp4CÀÎ-‚‡Éxv@˫߮ÜeÀIñ¹òjwÀ~~¦‰øÈhÀ·¸qqm¿–À•Æ6é¡„À°‡Îª¥Àë•k_§@ÿZ¨Í¦yà@Ú³z4{ÀÊ·ÞhæÀplHR¾AÀ’c%ŒoPk@vdúJl@‹ÌØDîk@òTU¢KfÀÁŒh›9ÃlÀ#Ô6Å•I@­V‡[šs@T¦•(Ö`Ào*K%l –ÀÐE.fAq@ê¨i”!Á ‘óà§‚á@ [ Ì)@ìéÔ%ÊhÀúîºx†’IÀ9ÊÉáÜ@„~³uèÜÀmd33kV"@ a3saÀòaOƒu„§@š{+å@qe¦C£@N ¸¥'s@.í÷©IpÀ­²’=¼&šÀ=”™ñèTŒ@_Îëü¥E!A_Îëü¥EáÀöhËU¡@æ¸EN¦‹v@õ›.}”@F\ý¼j@^[ÚìSè¸@I¢âóåÀÚ³z4{À_Hg´—à@êJTè¤ÀnçB¯_ëš@9ËHóåÖl@Žº? á­@´ âsV¾ÀÇlóµ¢@=ÅÏ*Cpx@œ¥°]ËÒàÀÍÕƒ+X"Á¯¡ѽŒjÀ^iÛf>ƒ©@£3K Tn’@ü0õ¬FÁm¶ýæÐA´šfæÑ{cÀ3—fjÐ-¢@ù™{+åÀñaOƒu„§@÷Á¨j-ð@ã¡æÙŽ|@a sd˜ÔÀ_Îëü¥Eá@_Îëü¥E!AÊ·ÞhæÀêJTè¤ÀS»˜È>Ãö@Ä âsV¾@Žº? á­@¿Š+Ô:lj@guQ9r@Tìö<¡©ÀÖjô˜ªP‡@ì\Ž«èåÀÎwm…ßÎ@† Tüç@p¥ñ$sÐÀ'ùÑzq@~ËÅ¥ï¶X@Sü’­Þœœ@w¼„TvÀÀLSº|ׯÌ@ÄO[X{äÿÀp¥ñ$sÐÀúÊP†öA=¡ÿh@U8ã™^B\@IIÖÎç*ÃÀ}Ÿ*'²·‚À¨{¹,À@”ù'¨²£?0¼¢Þp’À`=xÕ÷vÍš›@àÎ üo{@"¹:8bl@Ù\,YG@X8ã™^B\À=¡ÿh@J”Té{±‚@…ɲâ)ÃÀ”ù'¨²£?Àê:Ÿ,À@¨’ù'¨²£¿s½ ¢‘ÀàÎ üo{ÀxÕ÷vÍš›@Ø\,YGÀ"¹:8bl@º8ó ´~„@~6‡6c´ËÀÒ(š‰àÀœÀ½7Ä{Ñ@$IØKçn@‘<Ÿ»g‘}@ÿDdµ«9e@è^ùrµÀûÒ#Þ=³{À|°JÂR/„@éYêªÑ½l@)¹ë7ÜñV@5‡Nóð˜Àùëâbç¨À$IØKçn@IÅ­G*ü²@Åbæ ½ÍP@ 80¹ù8@‹A¶ßÒ€ÀR$Í÷"›Àƒ1Ôã=ñV@ÆÏ`&~U@@” Ð`ÑÐ~À‰õ‰er@þè$QÓ@0¸b éµÀ_µÂt”@M#ÛöزÀÌ×x±ý7ÔÀÃo§ ½Ã@|ª«›V@´¿ÅoÚ@@šŸVc"-ƒ@)•2–À0¸b éµÀUN(Aâ@ p⯲Àú^9ÅwöÐ@x Ê[wÃ@Ä<Ø­iëéÀ« £‹»H@Ýäü¤ì2@þù.©{À˜_ÒMO˜[À_µÂt”@ p⯲À¹4ÿˆ¸$ã@@6Âùx¡?'4e‰û¶ãÀõ]F뮲@zùáÞÀq@™_ÒMO˜[@cûìæUÀãòa hÀM#ÛöزÀú^9ÅwöÐ@@6Âùx¡?kÜ t±$ã@M#Ûöز@¤ÛrŒëÀ«kܽäU@—!çÛA@­{ œ€ƒÀ¸TŽ€ÛT@Lh$“ò6@QMqà"NÀ´e’£òìÀ}òûÛÀÀW`Ðjï@Ýûr¾.Ã@7 ÏðªX@Çä'rg9vÀÚF¥ÙŠÀ>€Ã¢ÀõC.PœÒÀÀ8¸m9ÞÀÝûr¾.Ã@Ÿ’¢ZIß@LOM"¾PvÀJïšÛ”@êpi©è‚Àé¨Ð!¹¶˜@í…’ªÝY@Q}JyêpÀ€…"åü¤@¦6y>ÃV‚@›mµ‡²[‹@¬j‡]ßÚr@Lak"?¡@ë+˜ò@X•aTxyÀÀôý3€eÛ–À¡2ÃYI™š@ô‡ã–@VÌ—Ù|”ÀOqÔ¶˜@tJ쯑&°À¾"'QçpÀLï!V†@T@bЈ @Ë/®i¸l@rŸy–©çt@œéå¥Ð\@‡) ![[Š@‹34„£mk@M./ªjM¦ÀÕ"säÉÇ—ÀµHrûS„@VÌ—Ù|”À°øN›&±@gÂ[oJ¡@AŽ5bsÜ‚@H€W¿îįÀÂnÙÅ¿”‘À%j'¿š²@ค5œ “@jýêó¢À“Ú€ Þ‘ÀëâKZRÿŽÀS¦¼>Ña‚@ôýÞgÕÈ@\ËcЭaÀ> ü¹¯;ƒ@þP‡VKûd@ÂnÙÅ¿”‘ÀeF1aiusÀค5œ “@4f¥éÚ…@é8š.ñ‘ÀS*>¶n–@d -£r@. ÇoX£À\ËcЭa@ôýÞgÕÈ@cÇ¥ÌØ¬ÙÀŽ‹ˆmá/”Ààe•W¦¶@wuð ’—@IÛ1×@´±Ê-Ey@«ŒõÛÓµ‰@7 Åü8Ëj@Iɹu.±À·™TJŠÀ²}ƒFõšÀ6g'ïeÔÀwuð ’—@ûÙÖcU‡x@´±Ê-Ey@æ~ÓÁœ Ö@éúXhÁs@ÓóêcÅ–T@7ºî¨àyÀaGÇ“¥À6²šéj’À˜ûDä†TA@èSêðÓÀØÁ0#AyaÀ‘<Ÿ»g‘}@Åbæ ½ÍP@–In—@D„*Å? §‡nr Z@=F±ŽõB@”Š"µ_AÀiW6ÎÀ7¥Ö9S®eÀëèÓ¥žÆ}ÀÿDdµ«9e@ 80¹ù8@D„*Å?)*!: m—@ïòê)C@Ö'í p7+@õøF˜ÊÚÀ–g÷{:½Àjýêó¢Àé8š.ñ‘Àݸ¹µØò@‚ÝáW­¡Ï@Aóè›çÀ ƒ€òºÀŽéKu £@ÁúãT!Ø„À +gO½ÀÇtŸ*5ùŸÀ“Ú€ Þ‘ÀS*>¶n–@‚ÝáW­¡Ï@c>õ~Ô@Eµ‹©Í ÀÀ‚v½ BÖÀÁúãT!Ø„@ŽéKu £@W+­@ˆ¦À•¯èH˜—@¢Œ»§Wå@Àÿà¾ÛöÒÀîXÔi×ÄØ@6j‰pä4¶@cåîM¹ÊÀÆrˆÛW‰¼@ R<£éÀTâ+ñv7³@¬›ñ)Û£@ÔmhÉ·%ÃÀÀÿà¾ÛöÒÀ×Ï´þ@ø- Æ´@±Ì¤fùŸ’@lÒ*ýyʼ@â kÎoåÀR^]ÿ?]°@¡—µûÛqñÀÛµ;ò@T·HZ„@Üï¬Wk‡@Ø zÜi©À+Ûœ&Í@`¤õÜÙTw@…J¬8¸@<»ã…@gKðÇìÛÀ’o¦w@+Qr.‘ÝÀ|o¹Àï—À, ކãÀþ¿Þ¡»µ¥@J+,†h[@$úðÂfÒrÀoá0â°§@Lq¤¨Vy@°vÛžíÀ/zn[»‹”@ë| …¾@ýØ•ÊÁŠ@× ¨æ¥¨@ˆ§ ±ŸÀîXÔi×ÄØ@ø- Æ´@r!Š­!Abrà×LÌÀ@G÷Ï`²’ùÀ0_¡@)ÁÝI䙸@B`Þ Ì¬@y'~´e@’@çÓ@}€Ü:¡@ÌÏ’RäðÀÖ?Ù Í@J1®‡W½@N»µˆ@íä aUÁ…ð÷ÀÀQ¾ìn€âÁiÅÇ·ZËÀT·HZ„ÀÛµ;ò@2N´ÉÀ ¥À7‰ywº(Ç@Ov¡p@¨ö1òX@<»ã…À…J¬8¸@F<_J$n@Àб’ÜÀÖ¿/pÌ÷™@H<,ÞÀq|§Šâd©ÀRÏÐ&qâÀRcT,úxÀÓ_tkö&‘@Lq¤¨VyÀoá0â°§@:±y%¸¬Á@݆ʒHóÀýØ•ÊÁŠÀë| …¾@ž§ ±Ÿ@× ¨æ¥¨@6j‰pä4¶@±Ì¤fùŸ’@brà×LÌÀ@8ö¢-K A0_¡À„W;€Þ÷À)ÁÝI䙸@w'~´eÀB`Þ Ì¬@}€Ü:¡À’@çÓ@¨cŠ\X“@³Ý½rBòÀN»µˆÀJ1®‡W½@Nû;ˆªÀN¡ZÖÄjüÀ£ˆC‡s¼ÁÀ>IÐÅ%ÏÁU±±y+•¹@®•ís¨x@'­ìóøöÀYÉ_Ù¯ÀTŠ“!Ñ×@ÓŽOŒ˜@̇“$ È@°”iM–@î…Z…ÁÓ¤ªs1Ó§À4°F ;Ü¡@îîØ@9|@%h¡@Ÿê&f3‚@N$PîV¥@ Ü+å\øq@¯×>P½W@ŽçuåË…À€ÛÒ¥Ð@—s+óÃWD@ˆò˜ùÒ@ƒJ5C@~It2pÍ–@ÇXÞ?chÀ¾-¢&òíÀÀ¶»å˜ÀïÑ|u޲@ÖáJšä\”@Á%µýÿÁàõxð™ÀG÷Ï`²’ùÀ0_¡ÀÉ–ë^×™3Ao B ;£À‰éÞQ'5Á3h2^Øp½@Ë$&ôÚ@Wêv[Ÿ cÀwÏq_ÅÁ§Šé$d†²ÀžtŸt7±@ æköS‹@+àJ`È@êôw´]W@¼ô˜^>Æ@Z%ynå’@ÿžMNûÁ?p]®’¥À["…˜ÛÂ@Õé' ¼— @["…˜ÛÂ@Õé' ¼— @q±eg÷í¡@A#®f:À®•ís¨xÀU±±y+•¹@íڗ·@jòÙ úgûÀÓŽOŒ˜ÀTŠ“!Ñ×@›Ñvë–•@!(#ƒc@È5%šÈ¯}@6ëþV8™ÁîîØ@9|À4°F ;Ü¡@Ÿê&f3‚À%h¡@ Ü+å\øqÀN$PîV¥@Ôîù…";…Àwñ‡÷´@®s+óÃWDÀ€ÛÒ¥Ð@ƒJ5CÀˆò˜ùÒ@ÇXÞ?ch@~It2pÍ–@jƒ]©æ¡@ ÜÓi€ïÀÖáJšä\”ÀïÑ|u޲@„OŒq¥@½ØõϘÁ0_¡@„W;€Þ÷Ào B ;£À×v¹-cÛ3A2h2^Øp½À#­ëq»³ÁOêv[Ÿ c@Ë$&ôÚ@”Ôª õ±±@ëT“d¦Á æköS‹ÀžtŸt7±@êôw´]WÀ+àJ`È@ýxÿº’@¤…Ì@Ó_@pÜÊ¡zHq@­$WM¢„ÁÕé' ¼— À["…˜ÛÂ@Õé' ¼— À["…˜ÛÂ@I#®f:@r±eg÷í¡@ƒ#zVå’#Á wÚ™øtá@…¬S âJ@oÆ9}Ö׌Àx!<ÔNQ"A}ß6›Ð§áÀàgpÞ»Á@ıC‚š<“@“_5Ek©Á_ä~þõ³Àï0âz°äÆ@æòÚj@è“@ƒJ5CÀ4Ý»Hœ2Á±¥Ù#´@¨úÒcXd¶@ú”E¾Ùò‡@¬¾v§¦ØÌ@Jæþí6J™@)ÁÝI䙸@‰éÞQ'5Á2h2^Øp½ÀgiA)Þ¨'”@c3õ1î^@c3õ1îžÀözÒœ»Ïâ@®ûØaŰ@ú¬»Ë@O´œÌ«Z—@M†”œútáÀ°³r@ã’#Á›ža]ÀÁ³a=ÓÐ@}ß6›Ð§á@x!<ÔNQ"AıC‚š<“ÀàgpÞ»Á@b /õÀ¢Æ·F>þÀæòÚj@è“Àï0âz°äÆ@ˆò˜ùÒ@èØïæz´µ@̳ǣ' Áú”E¾Ùò‡À¨úÒcXd¶@Jæþí6J™À¬¾v§¦ØÌ@)ÁÝI䙸@3h2^Øp½@#­ëq»³Á)Þ¨'”@2¶p|Aú]¦yò ¢Àú]¦yò â@®ûØaŰÀözÒœ»Ïâ@O´œÌ«Z—Àú¬»Ë@õU½2§@;.lÄÛNg@sߛ₃Ë@ò‡ê$Y@{œÍåR°@…˜Äs‘@ÿHægÑÀHÓÒ Û³ ÀÆ'xîAÈÅ@ VÞœºó”ÀâbL“•¯@¿næ|¬Ùo@+—µ… Ö@¿•ì§^#œ@ŽW7IæÀ.‘¦c+Š‚ÀËÖ¬úúu¡@BƒÊlÎäÀx¿·"i¡@rLꢤ×À;.lÄÛNgÀõU½2§@P#pþ€äž@ç#‘eÞyp@„˜Äs‘À{œÍåR°@§}î z¡À? ´Ê±¡À VÞœºó”@Æ'xîAÈÅ@Ánæ|¬ÙoÀãbL“•¯@¿•ì§^#œ@£É~•ŽÍ@Å;br£À"flOŠãÀBƒÊlÎä@ËÖ¬úúu¡@rLꢤ×@x¿·"i¡@uÂ+’Ô@«\VM„Ù¢@áð‘öÀ¯Àƒ?/ÖÀÒª£Î44¦@=¥º~Àx@ðË=bŽ(˜@™G•ï+ºÀO¥Ë4ÙÀ#2nü6/’@£¦Ù&Ï}íÀvjBœíÑ@1FöZ?ø¦@4ÌÐ]my³ÀJô-Ñž°@Tn*s‡È{@ï@'4b|—@‹žG3øt@ Ô¯ÖÀí{ýÞÛ¤ÀŽW7IæÀÅ;br£À¹þû`o§Aî F$x±Àqîðä!\ÐÀÁç`Ž ¡@tŠ×¸ÆÀDÜwФ@&ŽäÃG›@[°Öíƒòy@E‰¦WÔÓ@‰Ç1­m@âãÚöh©º@ÑÒ€ ¥¡@Õeä êâ@½W˜V_Ì@úÌg@ayNQÔŸ“À0À 9ù¹@-L”rˆ@'káÚ™§ÖÀŸˆ­ÿ×ÀˆAü{´ËŒ@%k¶Ñ `@ Wà‹ åÆÀN^ˆWyÍè@]”›¬tË@„G¶æ“ áÀÇÈø'€>É@­xM8*ÁÅØ9¬´ÀÉ·nâ†Á@\Üš>´{À÷ìN —’°@5è¢[Ou~@œá¬Hâ1[@„ÃÅßÃ:’@÷ÉtÀƨÔÀ.‘¦c+Š‚À"flOŠãÀî F$x±Àþ„¶›´Â AŒ5„NŸ™¤À\ÔÊÀ5qôô¡À?(îb²ÑÀ,4‰°2ƒ@[69ŒAÓ`@ú«a·Òu@S@ÄïÚI¡@gŸ †@‡¶3 †‡È@äÈ&˜¤e²@Ü (AdÙ•À.$E« ™Â@¬ü!ù¯ðÂ@”…‚%8@ ‡)8CÀû4ª‚–U@AD}Aqí`À 1åæ$«ÀøÙÚÔ›@©ðÁpË~2@oø ˜o<Àö™ãîKïu@…!ÊÊ 0‹À–ìebû§@ægRú <ƒÀºÌ¡k8&†Àþ ·ÈqÀ1`ú"j@`¦0%w@óÿ᤾a@M0uöªK@G1HœA@·€»¢(ZÀ¹bR‚'ªd@{W¸ cwÀG_òV‚@P2žS¦@©7€¨‘‡ÄÀ Òd\ TÀ$GÍ :Î^@v‘ví<×ÀQ¤1®t­@ægRú <ƒÀ´“LlÖµ@=lr넳pÀ©¨ru8‰n@·Z^3PF@…eoÂS@îßâCL>@¬éc'@˜@Ÿù¯J®p@ò½6`w…ÀÚòÌô€ÙÀ[¥XJÌ À×-@6k@€ðc|c¸À@ëô”†o@ùpŒ<¢g‘ÀÇ2W)2¯ÀT ý|ç@Ê|}^¾éº@†¾þQüaÀËÔéÆâóÕ@c™AÄ—@íy°¡>’ÀÁßó‘z§@ûðP9F¢À»³¤—XÞÐÀSÕ1Vc¡À O Ьa£@ãŒðu™=‘À‘›9rY ³@wQöŸj®@G]ì³ÐÀ†¾þQüa@Ë|}^¾éº@c™AÄ—@ ,™ Ð@1$› ˆ íÀ ºÕ¨Wà¢@çà.‡÷ÀLñô©bSh@ƒ"ÏW“´@"hÝ DÀ„ÃØ$9-A×$H­L¤ÀŒ“„„J¥@50 ,ñÀ5Î-zWhÀê §~÷À"hÝ D@ƒ"ÏW“´@×$H­L¤ÀrŠ†Ç €A8d´ÑWEÀ óW˜;ç[@b) !›É…@ŠTà£#ƒ@ãðC)t„À¾û–ìQ]À óW˜;ç[@áÊÂ>rÀŠTà£#ƒ@Öð0l~…@êø•ÒmO]Àså®L3­xÀi¹!©g2@OŸè]¦ýKÀ©âEýxÀ7x&¸C’@xCª¿yÀº²˜üF]@th5Ü <ˆ@gH¡Ö{8“ÀÙ›E¾òKÀ@î–@e@š°ñTcE’@*ØIVݽ«À¯=Ç=ç*]@ÎIƒü°†ÀgH¡Ö{8“À:î¢÷: °@˜Dçü@è±×Jú¿hÑòOè@ÂU©YUCÐ@ûÖªªÐßÀÕ\R yÈÀþÇ ßÑÀEHõ§ª°Àž±×Jú?˜Dçü@ÂU©YUCÐ@Cþñ,ƒ)É@ {ðîwÈÀ~ÁiÒ²ÀðƼ%°ÀbeeŸßÁÀ·›>¹w@qñÒN@`ªÕ`ºþ@ÓŒ>þçÙÀ¸õ¿pc.@uÐiÔ7À¿cSZ1áo@ÖR+`@Ôƒ‡°@…ÀWCÏÑ|ÅZ@‰ßÏ“ù @¶Õ—ÀJèNÍkàN@N=ê%#cÀºÌ¡k8&†À=lr넳pÀù¾A9Ë@ÙJ^àt+š@*è„ûz{¹ÀBðÉu¢tÀH°ØÛ¾ÀmJ<¡Ÿ‹À?;WéÌ@§#45ýa@†Û§}ÙÀZy?“©&|Àw´nv@,Áøós¥_@Óiü¶ì)&À1ÐØÇ=‚1@hrû¡µÐCÀÖ–÷…7O@!ÖR+`À¿cSZ1áo@¢£—B·b@¯„/,ê/ŒÀœ(îú À»Âv[×*@x¸©…4"dÀ¬ õx@þ ·ÈqÀ©¨ru8‰n@ÙJ^àt+š@‡LIS?º@¾YLåì‡À3áÉtÅ_‡Àz”8h5MyÀ9u9-ö µÀG¬¡—ÝfÀT2ÒÒìM€Àѯ$ Uÿ@À^eù=ŸÀC>xðReÀºVÇ“@ -n™L@c ˜ÃMß@#g¬O¼IÀÛÿ6oPãb@>#Æ_÷L@Vvÿ#çv®@¬ášµn_ÀºTb<î@plHR¾AÀ1`ú"j@·Z^3PF@*è„ûz{¹À¾YLåì‡Àfœêàõ¹Õ@fáð”Læ›@#5Fì²”@ÅFã½@?üAu–rÄÀ[þ'¿#"‰À¼[·©Ï8h@¯Óª Ž_@éYX¬:‹@î™p%]À4õ‡·¡ÇÀ³'I;{À°¹âOÐ8ƒ@)Ü©¡0ÀÅØ{ÑÃ*j@òŽ<›Z;`À™.•ŠÀPŽÀ -n™LÀºVÇ“@õ1ÄŠýÕ@ÀX€~DÏs@­ášµn_@Vvÿ#çv®@m,m‰¦AÀ’c%ŒoPk@BðÉu¢tÀ3áÉtÅ_‡Àfáð”Læ›@‰Ã‚ö7€µ@™ÁxvÒ%…ÀGò‰F€±Àî™p%]@éYX¬:‹@!Æ Ó,%ŠÀXXüÓ ºÀ'Ü©¡0@°¹âOÐ8ƒ@ù?ót8ŽîÀ FšÄ¸ÂÀøb²ÃFR@»Š¢ì›“À, è_ßi@ëØö(¦Ü¨À^°í9½–ÀS¤à$ÔÁxŽ»[ñÎ@c3õ1î^@ú]¦yò ¢À†Fñ[A'(gfEš£À”ÉYÅW Á5t´FíÇÆ@²ïÿÓJx@T§B•½Õ»À ùà´VDÔ@؃z¤‘óÀøb²ÃF’À»Š¢ì›Ó@Âi„ IwÀ¡)Þ$/`¶@^°í9½Ö@7c:XÜ¢À Nù-ÚfÁc3õ1îžÀú]¦yò â@'(gfEš£ÀÎë%ÍPÑA‹^ Ùn߯ÀÚKáqEC ÁôSñÿñÜ…À30ïŸ* É@»#.{]^@£@¼û2ÀDìé‘–À06Ó˜#S@BWà6–\‹@‰æ°øt‡`ÀŽ™,J[ž}@WŠe…NÕc@s~úìV@ïzØ+6¸¿wù‚»FbÀ€u±FUÀ¡@¼û2@»#.{]^@2ëV©.lÀC¿¹µ”À‰æ°øt‡`@CWà6–\‹@WŠe…NÕc@Ÿ(f#Þq@ÄzØ+6¸?s~úìV@ :l»ÀUÀVÓõÖ«áGÀ´C†¶8†@hÈ<ÄÏw@cË>‹hZ@1A¹Ÿ¼¶¯À51…0@ì<¬W¨@’bj¢=ZpÀ¿KëOkY@bVÞá<@Û­*;kÀ,AVL×[@¡3áùwh@àzª„_7Z@T××£1=@–M˜x…fÀÁNtבªÀ’bj¢=ZpÀ‡&“Èç®@¾Û59<@!öä{Q @mÌŸòd=\@Ôe êW„‚Àø ±´ÐA@'øÆÌP+@Zbyz;L@º8õ»ÏW/@p‘¢$ÛlÀc'x+$#@\2”8+Ä¡@qéoo[BaÀ·0E¦,|Q@Å (»~ë:@³üO‹:¡À‡4¬"6.R@€G WK+@[ô•{ì@À¼q›? 5@ƒU=@Q>²òz´ @}ŠË¬¿|mÀqéoo[BaÀçmRÁ\&¤@髆ËÉ:@€¬œâž$@Óýª"C+R@°¹’²|¢ÀrĸEÞb@2[‚={vÀÞéISÈ@@×õ©ä†YÀíkæÓëYÀ£µ™V´ w@•…Èí±`@lääDyÀ‡×v–KT@ðFIBqÉnÀ©á¸Î/Àñ70PúÊ¡@²EyHqÀmÁ 7eR@.ÈoGü*lÀ*oØV ~@^&¢Þ€Àqa‘š" @ß/ƒ¼w…RÀHˆ¥Î-ðf@“wQ€#zÀ¼Žœ+€À’äÙ~ ”@4fê|Q ^ÀÜÏà~Úv@ß]üÑ7 w@˜g"»±”ÀÆú ã}ÀÉ‚›˜hŸ–@ÊΊt+rÀCô¸e‹@˜O¾£@ðd‡r“ÀÀbß7³2òŽ@×HÁ*xpÀ‰÷wéÇ7‰@^&¢Þ€À¾Â¶¡@ðxvä<Àýq$å”p@æÂí/‰„Àtèõ‹¿f—@Å ›D¤À0–CöôF¢¿†k)\¡ç@ÛïùŒÍÀ@mSáÍ@«pÌOFnáÀ´¾|ñ%ÕíÀ䉜àÃuÚ@t¡CöôF¢¿ñ£à*D¤ÀÛïùŒÍÀ@‹Ý?ÙÞ@F¥8S]âÀ}Jkálõ@fÝ`=âSÜ@!ž7#LüÀÀó×L§º²ÀЄd–âêtÀ9oúÛ¦]@ãaìØ’GF@Ãê hl‡Àîf×ùú£mÀ•‚x§*õS@“:‹í“>@³VõÚüç @&£Ís–~@/šÙZÅߨ@ð\ø¢ð1@ÕªÄtÀ#è²–įÀß¡b‰§WF@Æ# ‰1@cR3€©mÀ§†HÊ:¸kÀráó€Ÿ>@+‰Q?u'@&£Ís–~@FÆy¡ãÏ@Ö\ø¢ð1À/šÙZÅߨ@þ•RíɸÄ@ýZÔ ›¦@¼]}Jl”@îÙ<úäa@ëâKZRÿŽÀd -£r@Aóè›çÀEµ‹©Í ÀÀóMÊ\ÔÛå@Œ‰8°w°@§n³¬¼ÀÝ¥FÊVÊ„@êÿ”=­¦@¹EO\ý¼ˆ@S¦¼>Ña‚@. ÇoX£À ƒ€òºÀ‚v½ BÖÀŒ‰8°w°@p1VPU Ý@˜í|Y•C‰ÀmðŒ´Àç=±”˼7@¿þzÙˆfÀë×)+!@ùw³îZTÀ‚õ?ÛérÀ†ú$Øo¬ÀW«31æBŽ@ôýÞgÕÈ@\ËcЭa@ŽéKu £@ÁúãT!Ø„@§n³¬¼À˜í|Y•C‰À4„ÒÑ¿@Ù_ÖXÀÞ:¦Éí¢@@<:™-sÀ,3 ykÀZW¸™@2Q8˜SÀ0ß „B;‡@åèÞ#–¥@ó¸ uDKŠ@7«2ÈÀ\ËcЭaÀôýÞgÕÈ@ÁúãT!Ø„ÀŽéKu £@Ý¥FÊVÊ„@mðŒ´ÀÙ_ÖXÀßK’p¥rº@øíè»ürÀÆJžXã¥@ OÁ‚Å ‰@ê®é>þÕÀ¦2b@TØÀK‡äSƒ@y÷‹?s`@þ ,ÉŽ„Í@øZÎ3Ï÷À±òG7@0¥¼:ß[@[°Öíƒòy@[69ŒAÓ`@€Þßl(†@ÇÅ8}©c@µºg_¦YÕÀ¶+–ó-ÇAý9…ª@b6—{…«ÖÀ(•”»±]v@Ž)îáÚ¸S@ï¦k•ó·¡@sº“ž†@W•м#É@ÀÁïgÍÚ²@ +ÒõçºÀ1¤•‘«weÀÌW'g…@S)¹ I¾™ÀB!·[ñ¡™ÀoR[mNZÀ(€ÅJLÑÀþmuN¼@¬ÒÝöÌ)Û@ ꨼²—·ÀŒ`CûÀ1´ÿ-Õr†@®=ãu pe@å> ÑgæºÀ0l…žÀ™›À+ÍŸðÿ˜°@DìâµÉ1À&¨¾¸.|—ÀúzcYÀ@ßμêàÀ ꨼²—·ÀñìŠ#ä@1´ÿ-Õr†Àå"dž6£À²ØAÚÝ}@¬v·O¿ëf@³ÇnÑ µ_@(í+«>H@žšÌšš“@h/>Rõg~@{‚¹L{¼’@Q±%ï`¾:@fV‚j“Nm@“§e¡Î@ðïEnÿã§ÀkÀUQkŸA@ŒÌØDîk@vdúJl@Œ`CûÀ1´ÿ-Õr†À­XKÃû“@ÐîSÀ¬v·O¿ëfÀ²ØAÚÝ}@(í+«>HÀ³ÇnÑ µ_@h/>Rõg~ÀžšÌšš“@N±%ï`¾:À{‚¹L{¼’@“§e¡ÎÀgV‚j“Nm@Õ²é 3^BÀz%j’ͧÀvdúJlÀ‹ÌØDîk@1´ÿ-Õr†@å"dž6£ÀÐî<ž8“¡9<–@Œàú[ƒ>S@tÀ–0Ñ‚@”ÞÛäR0ÖÀ=·±Iï‘@B`Þ Ì¬@w'~´eÀOÁ÷žÚ@„sšar;¡À »NŠÁ¿ÀÅâÖy*“@Ý–XÑ͇@+Ô€ˆuÚÀy'~´e@B`Þ Ì¬@„sšar;¡Àe%Ò{6”á@{™¿€™Ž@Ú>IGkÈÀí¨yïâÀÖ$ ¸}À·«`aRÓÑ@Z( Lt @eõhj1ëV@ÆÏ? `‹Àé!¡Z,Ô@?AÛ2αÀ¥ÚÔU¨˜À´S%fÐÃ}@öC(ú uÀÇ «þ©@sbj\ÛX@þÍ>²¡âÀö9Œ¼§°›@F„¼Á_i@"2‹k4ÒnÀyù@’ùí¡@¾2Åý¥ŒÀÜ!pÇÁ@DÀ€ ˜õÀHÌŠñÔ¿@?AÛ2αÀŸ¡Þ–¬•ñ@܈©q>|¨@Ob„=‹'âÀ+ŒÓ„a“kÀBVø<ø @Þ$ô¦/hÀjËåéV²@½Ú™.¹&¬@››ÂK;eáÀ^Ç Mæü‘À"’ñ©¼»È@Éí×åÙ‹@³ÕþFÂÀE“2ehÀTUâµìp³@ÎdæÃÂõÀSåXoô›ß@Ë$&ôÚ@Oêv[Ÿ c@¥ÚÔU¨˜À܈©q>|¨@VÍ¢aŠ A:¤ö¼ùÀiIº'-êàÀÁÉ pÆìÂ@ ¦^H²ÞÀÉ^¡Ý…YÛ@ì´í}7ÁðŒ®8á@ߢLÛW½ÀbâJê§Aó@áSŒµÓdpÀËxñ,j¥@56W›íf³@VÒg¦ðtåÀá‚ï?â@í%®h™ÁWêv[Ÿ cÀË$&ôÚ@´S%fÐÃ}@Ob„=‹'âÀ:¤ö¼ùÀ*ý¦šÿ{7A*óN˜wÃ@W~ìh9ÎÿÀ`äµÀïŠÜ@XŠÂ±ç¶Á§ÿ1aù½jÀÈ?¿Ü»¶ @:åh¬RÞ@iÍ&ŸÂ&Á!ÊÕ+Àg@M7ôtÀÌ×x±ý7ÔÀx Ê[wÃ@'4e‰û¶ãÀM#Ûöز@BÊõ¡ó»í@N®ÀáÉËÀ¸ÚF¹„À’~r§t’@Ão§ ½Ã@Ä<Ø­iëéÀõ]F뮲@¤ÛrŒëÀN®ÀáÉËÀ\qƒûqú@rüÄhè¹p@ãOol­¦Y@`¦0%w@…eoÂS@H°ØÛ¾Àz”8h5MyÀ#5Fì²”@„-°Šüµ@ÈYŒs£?™CHUsu@ñøÍ[¬äk@r”—¥Y@·43äsªC@óÿ᤾a@îßâCL>@mJ<¡Ÿ‹À9u9-ö µÀÅFã½@ÈYŒs£?‘J@ñʵ@:šêIÜq`@¨ @$„bU@U³Ã_Ý@ŽÍ=’Š@#nñ£œ¼â@¬&ÛPsM@:¦ÏCý_Ò@NgÏŸ?Ç~@Z‰a¼sœç@ sco~:]@”&^qþ;Q@žHk/ „ÀøÒ®W¢Àp@(À)}¬Ì¡À’@çÓ@}€Ü:¡ÀwÏq_ÅÁ”Ôª õ±±@özÒœ»Ïâ@®ûØaŰÀ+ŒÓ„a“kÀiIº'-êàÀ*óN˜wÃ@³¹×ïˆ$Abqô¼ÝpœÀ3øÑwc@¥x Š‚”Àîϵ‡°æ@¼Ê= Ò—UÀµš5Ràt@¹´ q©«ÀŽÍ=’ŠÀU³Ã_Ý@Á&ÛPsMÀ#nñ£œ¼â@PgÏŸ?Ç~À:¦ÏCý_Ò@ sco~:]ÀZ‰a¼sœç@ÞVÖw%6…Àé®E5Ä­¸@þ¦Qž¤ÀYvnÁ6èÕ@}€Ü:¡@’@çÓ@§Šé$d†²ÀëT“d¦Á®ûØaŰ@özÒœ»Ïâ@BVø<ø @ÁÉ pÆìÂ@W~ìh9ÎÿÀbqô¼ÝpœÀ– !Žt9AÝXǯõ—ÀÄ2Ù3>É@¼Ê= Ò—U@îϵ‡°æ@—*O¨ÀÕGòl–á@>]ÜwáÀ² á«æ@Ò›à•·çÑ@4Â2fP}RÀ¾•8ðÁáÀ6ÃýÀç€P@­•Eê@(‚1B(Øñ?D'Pf(v ÀÛì€: váÀ*Â2fP}R@Ò›à•·çÑ@ ƒqúëµPÀÿZv#çÀáÀ(‚1B(Øñ?p¿$¨øBê@@¹¤?¦@TP¡ásÀ±šB±L¸À¯fzß@'@°Ÿ0˜À¨ ½Ú;¸6 9³@¤®,ˆø6…ÀTP¡ás@@¹¤?¦@VJÞļŒv@›D˜ÛÁÀt7cP54›¿éûÖ\Ï/˜À¤®,ˆø6…ÀŒÃ}–•Â@J\{šÀ¯¿þXö8Á@GV‘‡ÜÎ@†n3†ásš@%C(Fó'Ú@3m¿­Ú@AèXÔLÁ=·Öµö#ÁØí¥”ø¼@ðonèà™ˆ@¼¯VÇíõÀž4mO¼Þ¹À¸þ@bµÝõ@b¶"qŸÍÃ@G5U[Ó@xeÇNÅ- @G5U[Ó@xeÇNÅ- @åoÁLEAÿ˜£tÛí Aß©ÛPôoð@Sú Où×½@í]Ø-N$Ar;»JU7æ@£+3'+SA“Û9žá@Xm ¼W†@4#¿ªª”@-6I’(a@œ†žÂì´@ý fIWµ@xš G'Áüñ½ï%ðÿÀÔúIÁÊ‚@`ƒz‡8êO@zfs¿³¶Àxr;ÒÀÇ˵*~Ñ@Ñ#Ñe¯Ÿ@Ží7Š™@ILx¤ýd@Ží7Š™@ILx¤ýd@J\{šÀÿ˜£tÛí A×—Sž‰ìÿ@ÌùºLÊ@Üa¦úß—@бF¾qƒò@õ•bÕÝÅÁ@¶ï(ìˆBô@,öi 0¼@‰Û<‚@~З’>~@‘ÿÁÎ$š\@Q×–H[1À"Ø£ ‹À× ÔX̘‚À´7h+“À ¿eÆ«P@Q±eáʼn@šz‘©Úë^Às~úìV@ÄzØ+6¸?ÃU¾a.@JÒ yU+g@íîa_ºt@žàÅh0q@O×–H[1@‘ÿÁÎ$š\@ö/¥ˆ­{ÀÏfçÊ@†zÀ-7Ž…RãjÀ©ÚšI½’À™z‘©Úë^@Q±eáʼn@ïzØ+6¸¿s~úìV@JÒ yU+g@†^¶ÎD[q@90ƒã[i@ˆ¹'šmg{Àz€XKüƒÀ$8-Ó½º@ñJ²AOrÀÁ„èï?´ @D‹Õ¡ƒO@­÷ 9mD@ƒ8p®Iµ„@ØZXë(fÀ§ÉUÆ9ÿsÀ‡Í¿œ…@œe¾¿ |@¢“B“Àî¨Ü †ø @xˆjÓìŽrÀ|a]Ì]D@%ê>r׌9@ØZXë(fÀñNÒrç‰@iH½…Ë ÁF¹ì™D«4AÃЉT·À@þuÉhgãÀ™èo3Ï A\Í0C 4ÁEàB€}~Àä¼EÒ@¤Ø4A[!Òæ_Á$.OÎþéÀü0.ž A\Í0C 4Á…nUàŠ^AÂòà-ò™¿æÛ-‘~À¢d®:O@ó=yJæÔú?AxhÝFü?ù± ([!!@'?;7å!ü?ÛeÚ §]ÀÀ¤¼î’‹Sù't@Xèó ˆ£Àp0Ôò‚À\[aNÆtB@ÍJ²ñ@¤§ç;²ñ#À’¯ ûfüYÀV.9{¬pP@{vöuL´@¾GÕ©oEY@ƒî_4~@…Ó¿,¤bâ¿'*¬5‡ÀC_üdNRÀ¬²Ÿ|®‹À%õåûNRp@]=ÂAag¬À–þº&€wÀ÷4’6WÁA@„ È£‚î?•G?Fòï?a×GµŒz@UM#*6ýï?x0EXGÀx9Åö>BÀ›i!@œm¿3Ës@_@µu®ìA@ÀòMC{…À©ƒ4ÿa$Àž¬áU/@Ã?Zµg‰P@J-'¾¡dÀ¾GÕ©oEY@ð`»Ž²£¯@\Ô¿,¤bâ?ƒî_4~@M òoþTÀo‚ ˜÷À%õåûNRp@xðÃÛ–À³Ü¶wÀwÀBybwíCžÀùïGªKý@ų“k_,@ÍPkÌã¼CÀš*)>@é¿jíß$@xåv'T]@}T6SgøR@1ucAYòM@¡”YHÀ¬À®è§òufÀ1ucAYòM@Ú|'oÎN@1}ÊEjž@u‚{)ÅxLÀFüߟV$l@ø™x/B@xêU0¨up@qåLÈ$hE@Èä-tc@,-YçEc9@ÇÂÊH¦‡@,B›?Ù­ÀòTU¢KfÀnçB¯_ëš@èî3,.ª@A7ck*g@Ê]¯ÂkËIÀ“×qøhÀ^Ðô½ \e@Ú;4ñÄq@ZõàÂG4G@M~âÒ'ÖP@·q—5WîCÀ·Œ«Tk”r@_SÚ™@T(Ü/–õ?8ÏjöQß.@Ç©Fùø#@þ7[7×…@“¶‡öÚrÀßi!3è –Àþ7[7×…@‡M°E­@ÎWû‚ÒLÀ¾MòÛÍz@õfoW/&>@*h¾„{@×Y?Ã1¢A@¦˜¦Bï@W yc×4@¯)®ÃÔ2 @d_ÕVY@Àø)i@~ÀÁŒh›9ÃlÀ9ËHóåÖl@A7ck*g@î¦Ûž@æºÅ®ùGx@UÞ~#8C^@R Ñ.ìì–À &ÐiC@“F<ë÷Û@^Ýœé|ÀªëΚ@mk!7þV@ÍzÁ fŽzÀ‘]ÙŒ”@|¸Â:2pb@¡JᘌÀ¥‘+I©E@biEñ,X@;ú-bÇuÀŠF@Ç-6¤ÀÝLð†)ŸŒ@7 ÏðªX@LOM"¾PvÀ[ 7"³š´@²°CA[ŸÀÓÁ[Ш¦À|¸Â:2pbÀG×ü"o š@®‹Œf¸|·Àm;÷QA¼zÀôc]áž@>°¶­ k€@8ԖפÀÞ{ÁnýtÀ…yeÊž’@¡PGJV¶Œ@¢•ªeù¹ÀÇä'rg9vÀJïšÛ”@²°CA[ŸÀAÅÁ!€HÈ@&Þœ8™ÀžïVi¿À¡öKéY@`öüª´¤@–R›+c1ÀÓÁ[Ш¦À¬yϲWÀ@¯8[»ÌUÀ…êü²3k@P¡%ÕÁÀR›+c1@aöüª´¤@|¸Â:2pbÀ&Þœ8™À¯8[»ÌUÀtÅB&„¾@ñÕ ý½ÀW>Ü÷11~ÀæŠz§@œI‡x @âµÇ.œ´œ@¼§Ç­Æ¼QÀ´Ã9ÅËÊÀe jG@—BØDQÑ@Ðߢ4æÀ¬‰¨Q“À¾]MüS@ׯÑ{À+~@–ë—½À¥I‡x ÀæŠz§@¾§Ç­Æ¼Q@âµÇ.œ´œ@4Ôjž%Z@â¤4ÙÖÏÀÐߢ4æÀ ¥­ óÕÓ@(ÈyZ.ÿSÀVubNP“À$5c{~iT@àO”$RpÀ)={Ii@Ä&f-„À¼š9Cî®Àâ]”M©@T@d!zƒÀ…È´÷÷\ž@u”Â7ü•@ùãmðT³°À6TsÌ-oÀ}6œÿíˆ@F>ÇQPƒÀOÐßiÑž@þ¼(MƒM©@ü9”+0ÃÀ³5´Þ À@dAàd0·ÀùãmðT³°À·FOÚ_É@ì{Á侨@ù#NøòUÀÅf½é~¨@N _úVf@"C¢ `p@>·±Iï‘À‘{ ÊñѬ@9ÉU‡‰ uÀ#ŧ°‰â¹@œEá<9Ò}@ÌÏ’RäðÀ¨cŠ\X“@žtŸt7±@ æköS‹À »NŠÁ¿À{™¿€™Ž@‰Ê Z ,Ý@×û¦Éì’ÀF5vˆ’É@ù¶†±'t@F5vˆ’É@ù¶†±'t@[$NøòU@ì{Á侨@M _úVfÀÅf½é~¨@Ò£àYˆÀ³l³ n²@8ÉU‡‰ u@‘{ ÊñѬ@›Eá<9Ò}À$ŧ°‰â¹@Ö?Ù Í@³Ý½rBòÀ æköS‹@žtŸt7±@ÅâÖy*“@Ú>IGkÈÀ×û¦Éì’À ±.ýQá@ô¶†±'tÀE5vˆ’É@ô¶†±'tÀE5vˆ’É@¦—ëx]‹ÀÂDùk¥kÀéý§68…„@·®Qgw]@íœHt=V@“âlÁJ@zq­HB`@¾1§daêH@‡ÇNsøIiÀ·3º VC{À·®Qgw]@òr(ç9Äx@Ào%"&A@çþëä‹¡4@îP†ŸñI@õ`ÿíD63@6 [Q³’À–pÆÚ_ítÀ‰ðÌ´®â3@YVC¸òIÀk?I°÷3@7‹waJÀ¨åÓí{1¢@/Úü±;t@0Ýp?zû4@Ru’œeKÀOÝ–?þ¢’Àþw2f5w@Kh|Êö;iÀ;~vèátÀæh&t:|Àä.LÃJÀ-Ç]R#a@ådKS3JÀâ„Ëða@/Úü±;t@'øm}¡"™@鳞Ÿ5ˆKÀßÖøÍ$ùa@Òæät?e@Žª˜»l Àj~ìn<Ž€@’ tþà”ƒ@ IÑ?¯Pq@¤Ç#¨ûõ²ÀFl¯pÞ‰=À·¨Å†¿@˜)Öñ|av@Œs¦zN0­À~¢=—ë‚À߆:*1'[@qXDë§H@ îÍCvÀ0’-ô²°À˜)Öñ|av@€µ’ò*¼@»t[†Ì [À+´Ð0h¨Àjg)ÜÜn@ÜĆ·:T@_Êä:FˆÀÑÇÿAÚW`ÀRö‡1ìÒÀªÓ½Ø E½À2À!Ç–>Î@¢øHï?è¸@4SÉÙU¸@ õS¹|˜@ö0bT>œÀ§™ÅèyÀùG¹FÓ=T@I–î&ûˆ:@Ö„z‹4TlÀd–ˆ9²kÀód°µ="¼ÀSi”oª¯ÀõÌç82¸@èÆ òÿì£@ õS¹|˜@¶¥áNè¢@.dÇ ˆÕyÀLÔ<ÎyŒ†À„,âuñAØ—ŠÑž{ð@t¥WMˆÁ¨)MGr¢©À„8TD!»äÀܲ)*]ïÀØ—ŠÑž{ð@Y Äu¤A] ~]ïÀx´tÍJÁ¶)MGr¢©À5¡#êÀšÕÀ”²òÀÑG@&Ñ.¼b @4¼yxÄw@œ¦·ƒYfá¿è38²Ï@‚À¤)L³ÖCÀ»£ñ=^ÀG¼œ»A"Z@Xš£ªf@=RÂO‚ø?(,u‰E@@2‚(PEUÀæ~pò¤O@àõ gã%@&Ñ.¼b À”²òÀÑG@•¦·ƒYfá?5¼yxÄw@ÐË¥k¼ÀÛãU|<€ÀשöC@ a@q[êÈŸ£|À=RÂO‚ø?c"=1f@¿c[¥Æ6]À°„ s@àõ gã%Àå~pò¤O@SmÙ:IÎà@m»/‚à­@‹Ö–ñÄ*Á‘˜Ô1Æ{t@!B@–ßÀT¿Dƒgݽ@ó{‘LõÓÿ@›ûE—7 ÇÀm»/‚à­@±Ò4œ¬Žz@¨ú,YA~@@\¥ ê°Á qúB½@Ð'ÄøÀ›ûE—7 ÇÀ jµíp AúQ‘ž‰À ªæÆ„•¨@x‡É–hÇÀ>/5.çbá@d3¹Sâ`l@|ˆŠpËÀRIKçÙve@ÑvHc©ÿÀØlÌ‹JR@g´H,àgÀõ/#ü7u@߯hÖÔƒ‹À¸Ð︼ÌvÀ6ß«õ s<@Ø NÀ|LT+ƒ5@üep䮜¥À=D‚RQEV@<Áf+ mÀù C"<†@~E­ÿ5²¤À‘Ô½v]ã3@Ò¨¹eÀªö Ê’¸Ã@ÿq/Ü”ÝÀ@Š‘—ž§@v­¢5#ÇÀ¿Ó²æå@QvïIDÁr„EЊÀ›É/Ϧ @Ц?„Àü´†ÈôÖ @u)7qÀö&O&¢x†@¡V7Zâ“À2­{†ëÈ©@4ˆ´Sfu•@ŒJ)Ø¡ÆZÀ­лEl@ïÔ:,žT®ÀKãÑri8Ä@¢©‰/òõtÀhòœìS‹@~E­ÿ5²¤Àªra«XrÃ@ÈÐ~ß·RÀçòýøúq„@Ð…_ÒysâÀ·H»6­û@]²âšÓ@(zؾqu°@¯øt!Ô¨¨À9ùÌÀ¢úÅ@& NêúíÀOR'…c¿²À˜~¶á0¯@™—>­žä@GY¸œÕÄÀôºÌ½´@ÍB“À˜Þ‘@ÜCöðÁ>Æ@Dõ€¿ÓãÀmEÏ–Ç»À8ñóEÒáÀ›pTGýÝ@GY¸œÕÄÀ¿^fÚ‹ò@¢@~Ô$.ºÀ2ûõ±˜Àä+ n­{Àq–ˆYxTÂÀ åXÊ.xšÀÙL øá&×@t!}â·@ÞÅ¥XrĽÀ‰¿ï)¡À‘Ç߃šÀYFÚš{Ä«Àø^çÏÄ@…N±ùQ1¢ÀNCO¦þÉ£Àt!}â·@)jüài¿@³Wú9]ÀT5sâ¦ÀØj\F6b`@&„\ïÜI@ka|u¯öi@Dæ"¡ÓL@ùŽç†À°Ý¨Õ`À0¼¢Þp’À¨’ù'¨²£¿·0E¦,|Q@髆ËÉ:@ù~´Ìf”@h‰Á¢™£?’‰c9I@çÅ7vV3@kí™üS@szèZ06@n›É&O«cÀ­¾3å²uÀ`=s½ ¢‘ÀÅ (»~ë:@€¬œâž$@h‰Á¢™£?4#bf”@ÈÖ˜Wò¥ÀÏs?ˆ yÀ"A*ÞÆ­@ç]áE6tÀàKcVØ‘@©ÃJ‚Šæn@cI~æ“Oð@c­½½-c@ã4-óÀ{¹>3ï¾z@ÍÑÅ„Â@UgÖ¤¢½ZÀa4^‹òwÀTÈg»P‹Àè]áE6t@"A*ÞÆ­@DØ`þX·l@@ ÛšÍÜH@c­½½-c@µ~{å!ð@‘÷ïžÇzÀÑÓÄnä,óÀcgÖ¤¢½Z@ÍÑÅ„Â@Ô²ãØ†˜À¢>Û¿Y d@¨¢<]çB£@éZ–ÈeÀm⌭ëý‹À]ÿÞÅFû @XêA že@ÍÐ÷ôÔžŸÀéZ–ÈeÀrÌå©Ï¦@áÛ£L]µÀàyè¥û‹À™U©¶,Å@èP6ÉÙ±ÀYŸMDØâ_@è2bˆ:A‚Àd¹@üª‘Àæ™w¾³@­’UúÆ8ÃÀá[c/áÀUÀèP6ÉÙ±ÀÉ«*á?ÃÜ@g3þø–ÑÀW/g¤@l ù²³@yzÛˆ¾ÖÀí<-{VÀ91ÎàxÂÀ‹°CpÂÀßñt™ÀºT¸¬´ÀÓ»I{Àê“8¢@9Ù4bIV‹@Rîõ@þ×Ç@w¡âþ`N’@ÄÖˆ•W²”ÀùG†lÀ²ÀOx‡ „~À w g¬ÀÄ&=÷ „@k½mÃòGn@w¡âþ`N’@!ƒIGÀ@}åŸÒÂ{‹@[Àn]õîh@«ŒõÛÓµ‰@éúXhÁs@–h¨©þ°@€Ûﮂ‰µ?<2àJæË±À‘e?tðhÀ?)ŠûËøŒ@ShÉ*¡ÀêúXhÁsÀÊúI¤l@ÌP×øûI@7 Åü8Ëj@ÓóêcÅ–T@€Ûﮂ‰µ?‡4 XY°@k "•k}ÀyÝ‘áá¥À¾(Òa1n@Š$KkÌjÀ(¹ÚB‚ÖÀó‘¦Ã¹gš@¶ù«Zð³…@L‰“~ÀàλPg“@­äyét@Íš”D{@0æ;ì2Nr@,ŽJrÒȆÀÞÅ¥XrĽÀ³Wú9]À”®Gc–‘°@ïUäÏzðm@…°Þô:…@gt'sq@?iÆ5Ò÷“@½ËŠ¥PX©ÀŠw¾y ¯‡@Ìç”+mºe@ãdËé‡Àâ£ÀY×Â@‰¿ï)¡ÀT5sâ¦ÀïUäÏzðm@úÑ6ûÑ­@ýÐÍìvù?\ÝJ~•,À¡ñ˜ fH@s‚f^^Àƒâ¨Q«&@<ï¹Ê=AÀÖ?Ây,ŒF@‹^¸ÿ6aÀ fj;@›ÎèFËTÀqšðPX5¯ÀÖ2ìÎ9v@ÙÊxK¿XWÀÍ,GcÙ8@LÞspSÀqa‘š" @ðxvä<ÀRˆÉ)J¦ÀSÑl‘ pÀgÌå‰FA7: ®Õ@b†™D´wÁ‰ºMcÕÀ±II.]üN@!úLš§aÀ-½Ulñ-À¨fQÕ7Î`@ì} Ñ|À - n‘@j 7iŽZÀ|V\(‰Ês@ý”bÎáyÀŸÊÇ]—“@/Cø¶TxoÀ¯b}­…Þ‡@f¶"¸˜±”@d3%Ì»ÀüBå¡ÌŠ@ vT (†lÀpª¬¨¶Ö…@ß/ƒ¼w…RÀýq$å”p@ mðÕr‚ÀÛ)æÙHÀ7: ®Õ@dé¹PÑ´è@4ÙzoWÔÀ›ÎstBèÀæm”ãºÈÀšM¤þC”@\ñÓ³Ùi@ßwÆ yœÀ%ÎTç²u@›Tz…S§À¶ú¶PJÍÁi$Æ…e.†ÀV¸š°tÉä@„»‘FP2‹@ê¡«·AÃ2-0ò?6Þ‚<‡À†Ó•‡²Á€ï°Xí µ@(N*Ûx€ŸÀ M¶ÂYÑ@=Khî rªÀ„­ÊmÜ@J;ñH$†@(Ò†gÍÁ‰»‘FP2‹ÀV¸š°tÉä@Ã2-0ò?q«]AÑ6‘¾Q¼@üœßж¹µ@üN<ÁÃXæœV ÁVg¤bnÿ A·”7Ï€î—@,ì€u3k@è^ùrµÀ‹A¶ßÒ€À‰DŒßc"¶@ÿ ¬;K t@­‹°pç@)¹ë7ÜñVÀ]²˜ŽR ÁVg¤bnÿ AŠhå`ÓÊj@·û×àÒs>@ûÒ#Þ=³{ÀR$Í÷"›Àÿ ¬;K t@^¤vš©@­‹°pç@òÏ æ³ˆœÀòy‚œ•Î@!„âôÝ‘Áá­ìsHÊA&чK,©@käõNØ6ÀA²Ävlâ@ºô.b ´QÀêJaÙùö @Ä`413GSÀ<TÍ>@‚›5ÁqÀMn [Ž|ÎÀ­‹°pç@Å  ÄFºÓ@BÛ‡p@E6;ÛÑ^À±Ç$MºÀ[ÿ/Þ@YÀRZ£J`@ÄíydhÁ‹Œc<¹ªrÀk9#ã· Áá­ìsHÊAV²ÿ—µ#<ÀK{:N+k@Ÿ˜EÅQÀMHOå …@Ϧi -TÀH&Õá\í†@Œe€=ãqÀž˜‹¬Øž¤@r(lì A­‹°pç@BÛ‡p@—ºm”üâ»@Ç.¸¥Þå‘@æpÏj@¢Í7m§¶ÀÞ*nÒH Aß!cê]@ÈŽl¶dŒC@bb•>®a@Ѭ¤× >@¥24ò¿Ñ¹@ÆyW_¢ÅS@ë²ýa ÁMíàöÍ3tÀã4-óÀ‘÷ïžÇzÀËRúŽ|hù@3¶‚Ùf@nW;«@¦¹åù¹>z@²;|Xjd@ôæfî¬DK@BL ÐÝØÀœ*̪ŽJÀ?ð:etå@8½pb9@\è&– @r€ÕRæ>@&>·@ÆyW_¢ÅSÀ¥24ò¿Ñ¹@3ï¾z@ÑÓÄnä,óÀ3¶‚Ùf@ëúôñ4ù@÷päO,y@¼Ô¼DV@§‘ŸyRA@q´êUÿ"'@J÷GoJ@ì°ošÝØÀ iɾù+A½ºÔ+¿Œ€@¡øóï¡e@KÅEÎýæƒ@‚ÇFPîW@8ø­“ ¤,Áž½ºO‹!’@eKÚÅ€pÀ‚^±Ý_=}@nW;«@÷päO,y@‰oÐQÞæÑ@ Ð6£À½Xq8š–†@*ÜS¹¦+n@êS Gh1@¤œÂñÕ^TÀ‰µ£Ùc¿AbJ=aÈG]@·ªá¬#"C@>Y$ë…V@ª:ÛZ#{h@U.­—Á…°¨¸ÚÀjÆZ¹ï}@Æ'VŸ¡À¦¹åù¹>z@¼Ô¼DV@ Ð6£ÀkŒ³¼Þ3Ü@§‹ aûc@×}¶òu°J@¸-ÏrŒXSÀˆ±Ûê£v@|/Œ*ÿZ@§ ”Þ+ÙuÀÕRX™°ƒœÀ á`ì‡@ËöÝÕ×ÀF;»ÏÀ@ÈÇ<Ö6V`@åvåy^5ƒÀ÷IâyfP«@`C£ÿ\¿À™…Úu?U}Àòì› 2½—@]Ó®ÙvÓ‡@úßí wp³ÀU/.ô7À@.°SÀhwáÀêâ‹÷"ÀÀÌyéÞ¤@`C£ÿ\¿À‡`·•©Ùá@{zt)—e@Á À+¢¢ˆÀÈû3¾³@V:8Ï(J¢À!ž ,°ïµÀ㆟‰¹ †ÀˆØŠêbòÀÀý9…ª@1Iki@–Ê@”N+¦{@Ë©¢JˆÀ: 8qö¶«@á8=ýÕ¡À¡Aém“Ä@Þßl(†ÀÞPA 5¤À ‘acú©@b6—{…«ÖÀ”N+¦{@©@ÿ÷ãÆ@èÓH¯²é§À‡ãd¿ªŒÀÞ;•JÂÀ_Áô®¼£ÀzùáÞÀq@«kܽäU@ƒî_4~@\Ô¿,¤bâ?5ŠQ ‹Æ@·Š¾“6ª@‹¢5¿w$ÀùÎÑZ²ÀtéÉ£ÀÉzeE¬À™_ÒMO˜[@—!çÛA@…Ó¿,¤b⿃î_4~@·Š¾“6ª@s—³âį@ùƒQF9·¸@K}ª$©Éƒ@¦Øñ”éÊ@­‹#.½j‚@ŸÖ^”ÕÀC˜¬’¶ ®À„ ufåÀzå½Fw ÀÀ#ô$óqEÈ@¿Y¿¬ã©@ÐלÑKÄÃ@1j^Œmç§@GŒ¢ ;éÀ}SïÁî?ÇÀ0…YªÃ„ABHæµÄUà@@•Ù!ô¼ðÀç9Ô.NÇÀó ‚ÑŒ[ÑÀ w§ßˆ´ÀJ}ª$©ÉƒÀùƒQF9·¸@‘’Š‹Ò·‹À(5޳j¹@•_œéËc¥À‘债p(ÅÀǨ%T½À ÖàSô’ÀKæé0¹©@ê9µÌ3p‹@Læ€tj|‘@ð]ÝW%u@“aé„bdÈÀæ–ÝÍÃÀBHæµÄUà@áº}‹b|è@~ ‘ÀÀô@³–ÉãÀÀôŒXôÀ/«½ÿœ7—Àã"’iZ@î¶Š-a†+@y®)Èc"^@»H½ª† ß¿ˆ W´ Á·Xÿf@pRÀ¸né_hÐA$}O3f@"£l8‹Á2@´ÚÒ•²Àùx¦'å[‰@AeÌaH{@Áóó#zP(@š"¤'49®ÀWjˆ†Hj‘À(,u‰E@@¿c[¥Æ6]ÀAÐ{ËÖÅ@Iw®} ˆ@ÈÕxìK8s@"²eæ"žD@ŽËg¡•ÀšÞ%ÖÄwÀ›H½ª† ß?y®)Èc"^@Ü(3!ÑxÀ‡–;·gÀ ‘»ù•@Mc…“B\¹À»óó#zP(ÀAeÌaH{@ÞT†Þ‡ŽÀ¦wZJ°2•À2‚(PEUÀ°„ s@Iw®} ˆ@€ZLzå½@®ô±Ø €tÀÎ×Nì×À}ÀÞIWï ÁÌÅpܹÎÁmSáÍ@F¥8S]âÀdT9æò6AØGTøä™ö@“gÃmÆ1Á®³e32ø@KÈŒå£Áy1²y5ðÀ«pÌOFnáÀ}Jkálõ@ØGTøä™ö@¼¶ÆÞY4A/"I e÷@"+»ÐS­4ÁÇ—­sÀÒ¬±y!Ò`ÀØ ·ë»ÂÀ†"ÏQ–ÀÄ*†"úŽ@‹ÞùÈðnÀ[ ±u#÷[@ÊxnàÚG@íåwtMñ†@оÿµ²X@Œs¦zN0­À»t[†Ì [À>Nô· ŒÉ@móät§˜@ª¤¢Ü]F¢Àоÿµ²XÀgHnd„¢@ŸŸ8sr@î­À ×`ÀY%ý5ÊLÀÑSxÜK–Àtg˜ÆÍ²À‹ÞùÈðn@Ä*†"úŽ@vδ §îG@¼¼«%j4@оÿµ²XÀíåwtMñ†@~¢=—ë‚À+´Ð0h¨Àmóät§˜@ ŠûŽŠÀ@оÿµ²X@~#Œ,B¢Àr¬ [r@éh(ŒÔ[T@)Æ›„k Àx.z$:ÃXÀª¤¢Ü]F¢Àоÿµ²X@j5á;ñX±@p]¤»$„Ð?»åÛ1¢X@0©’ÌØc Àоÿµ²XÀ~#Œ,B¢Àp]¤»$„Ð?« ûjëP±@ý8¼X˜b”À å61/g}Àmû¶n´Œ@³×ç"%'X@maäÏ«]@R>øc›WD@lY³`Α@GôŽR^@Eµ¶¯Kt³@q%•´@¾Ä„iü”`@Ìð>õÕÜH@?ÇýZÄÀfÔŽO#³™À>-x:DØw@Ï)@OA@uÏuÃ2jÀ%Ë4D'\ÀûÖªªÐßÀ {ðîwÈÀm⌭ëý‹ÀáÛ£L]µÀgÕ1O×Ýã@ªìý¶¢OÆ@/ìgÈ6ãÀÀ}öíØ™ãC@4 E+@GôŽR^ÀlY³`Α@á÷¡üö>‘@£¤9¼Dm@qÿh“:F@Zrz)ª0@:”§IpšÀD>ᚨÀ²)@OAÀ?-x:DØw@%Ë4D'\À+ 2DANÀÕ\R yÈÀ~ÁiÒ²À]ÿÞÅFû @àyè¥û‹Àªìý¶¢OÆ@1(FjÑ@}öíØ™€@qþŠíZÚh@#¦¡¬T!ÄÀ˜È4Ä'çŠÀL+Y2¡@|ª«›V@« £‹»H@€hˆ¿ÁF¡@ ˆ<Õõ‡À =æG'†~@ù+èS¬Ug@Ž^pÈJ²@DµØ.ýŽ@a­9ÕcÑ]@«§Ær“f€À=UnxïyY@MAÞ$V„C@G¡µL»b@ &ŨL@,#x?›À$e9Þ[V±À';Da7Ôƒ@´¿ÅoÚ@@Ýäü¤ì2@ ˆ<Õõ‡ÀÖþÞn›±@ ­ü4u™a@nƒs^èJ@1†âö•@'À†eÞq@s&xYµ9@`ˆ²`NPÀ¬D”@Ï‚p’cÀüfLVÔfÀÙhÌH߉¹¿gmàQl³‘ÀE±ÿ,¿0k@›\¦.J[PÀè&¬®Ue@Ï‚p’cÀ<·7tT]™@Y@Øïé{©?D_¸'§fÀš³DÉŸ0k@:È(çŠH™ÀÁ§®U¬Á„ë'GMá@=ó Âe@ìÿ¸‘“P™ÀZÌšW&…@¿Æx½x¶ÀÞ$ô¦/hÀ ¦^H²ÞÀ`äµÀïŠÜ@3øÑwc@ÝXǯõ—ÀÂ}KA/ô/¥ÀµÓÀ7gÔ¬÷§@:p­¡'wÙÀ¼Ä ¦e‰@NAÇ(âuÁÀŸ"Ójeã@§éà¹pz(Á²ûÀzì–ÀëŸÐ À«Ê@ÖÏÍpeH¶ÀÓ¬ÚÇë¬ç@jËåéV²@É^¡Ý…YÛ@XŠÂ±ç¶Á¥x Š‚”ÀÄ2Ù3>É@/ô/¥ÀµÓÀ¡ì‡Ó?$AG0ÇÖußÀ¦Ñ2¶A'FO„óÁºÀ;€öjWeò@"\\ª~û'@œýzFWI?ÀI…!@ú«FCÐ06ÀÛ]¹þU1@GCÈtBÀ0Ýp?zû4@鳞Ÿ5ˆKÀ°Hq àªU@nýûå6’EÀe|¡$ʧBÀ92a35V@¦ÂJú [À~E‘û5g@£¬Ÿœ…”UÀ°G³ÕO?ÀâÕáÚ‘lT@­BÕØ 46Àµ)ºüøL@ì¶òT¢FÀݬinÄX@Ru’œeKÀßÖøÍ$ùa@nýûå6’EÀî\L±ùf@Ö¡^V[[X@úè*%+ÿlÀ{JKGqÒX@tÚ§@,XÀƒÎ&ÿí,l@€„>ÁWUUUUX$Áÿÿÿÿ_ãvÁdffæ„^Aÿÿÿÿ§ËxAº»»;uù[ÁWUUUUX$ÁÉqÇq Ádffæ„^A233ó‡*Áº»»;uù[Á©Oúd¨EAÁ ž¦A©£¬ô*oäÀàÑxµ8 @göœ˜'V@6@ínÔw@µfn©Ú%ÀãñÛy„Áö0][)uä@úûµ#•@Ñhh|¨8DÀ?;WéÌ@?üAu–rÄÀ™ÁxvÒ%…À²äqªèÆ@úC£î§•y@twbåíŒ@ÐQDU€â¿T ì®í?•@N墿f@©£¬ô*oä@Á ž¦AvVJ†¬R@ãv™Þ¢d'@¶fn©Ú%@6@ínÔw@ ÏnuäÀÁ]‘.9„ÁÑhh|¨8D@Žúûµ#•@§#45ýa@[þ'¿#"‰ÀGò‰F€±ÀúC£î§•y@ü,’.ðÀ@ÍQDU€â?uwbåíŒ@G`pf@Dwìy8@ÖÏŒHažÀXû"B>²aÀzÇX<ãƒt@ôÚGzj @Pßì)£ÀñXÌb§aÀÎýðþŒ@¢¼„  @ÔZJˆâUp@§UšÏÔ?ûú‘¦@ÍeÀN5q@ …¼¨…º`À{Êÿ÷z•™ÀùÚGzj ÀzÇX<ãƒt@`räjYÊ`ÀaþÔ:Wš ÀÜŽ¢¼„  ÀÎýðþŒ@ÅUšÏÔ¿ÔZJˆâUp@ÍeÀN5q@Cp¿ Ç¡@I  |tºµ@À†¬™½Ù@ó|å‡ò4ÅÀû M73ŸŠÀÝÝP¢“9¦À¦ ü]¿·U.¸ÕB!A·U.¸ÕB!A\ÓrœCÖ!Á°á¤BK!ÁZ„ëHêÖ@Á±aÔcˆ@ŠíÆ´«@PΩÝÔQ@:€¤Éöa‘@Ì )üõj@÷w¬8)†À[xƒ^ SÁÀÈ#½oò—H9¦À½"JÀ"žû@½"JÀ"žû@¿´p›ªãûÀ¼½lcëþÀÁ±aÔcˆ@˜À'—¥ùÔ@PΩÝÔQÀŠíÆ´«@ž¯O!ô¯þÀáÐéCòãÂ@ýšGôÚäÀ-I&˜*vÈÀÀ­ ÅãßÀ»b(C6½Àž>ˆ»Ì¯ÞÀË<ëe€7²ÀÂëkÊE0«@AZÚß)PÀÀÞã{ØX§@¸”¢1ëHø¿ŠíÆ´«@PΩÝÔQÀ>‡eä´Ó As¨‚KÂÞÖ@ ëõ1Ó·ÊÀ/ïô†`lÀzÏá°SBŸ@ƒ¼uàÅÑ@-b¤QóZÁi­~Š©EÆÀgh}È-ÍÈÀ¼ 7ËÄÀŒld?: £À§ž™}ÒwÀŠžžà›©@hAR‚ϺÀ}{îßÃÔÈÀüj  +ÄÀÀ-M šÔ@ž”¢1ëHø?Þã{ØX§@6Þ‚<‡ÀÑ6‘¾Q¼@PΩÝÔQ@ŠíÆ´«@s¨‚KÂÞÖ@˜`4ϽAÅcvÉ4¦ÀW\‹¼?ÍÀ¿·ˆ#¶yž@äTñÛŸ@ï LÌ"ÌÁST Ó†ÑÀUÇMè¹@"ípÉN¥@?U›ô@ëJ³éÆ…@P@-¯0 @dmo‘P©}@Þd¶¢kñÀø¢îZOíµ@ãçÅkûî@ì‡i!dÀH c<¬–@‰LáÛt@óTôfFËÑ@;Ž8¿=WÀ?G~z¹@Ë]rЇ@+àJ`È@êôw´]WÀß©ÛPôoð@ÌùºLÊ@†Ó•‡²Áüœßж¹µ@ ëõ1Ó·ÊÀÅcvÉ4¦À"£Ï²B[A=àa»ÅÌÀH¯8X£öÚ@L±Tˆâ©@{ §ŽÊÝ@ÆJ&±Ï†¤@aÒˆgªk@E|~?7l@¯CÚ„ûÈÀLRLŒò$áÀЙâµF›·@T)ü!XþÀÕ‡i!d@ãçÅkûî@GŽ8¿=W@óTôfFËÑ@©AÇ ‡@ Ù¦ß6òT@êôw´]W@+àJ`È@Sú Où×½@Üa¦úß—@€ï°Xí µ@üN<ÁW\‹¼?ÍÀ=àa»ÅÌÀ äj5ŠßA Û9z¨@v9]“w@¹¸ §Wɪ@_õ@¿çÝfŽhÀ3GÍ)óƒU@G·“–@¸ ¯ö)Àò®sÊ@7@c çî÷TÀ-ÔoL]t @X)(žÆ¾(ÀX7ë AË@ïeAu5ÀaK~wmL@㎦ˆg1À¤#„TEã@ArYC;4Àro“8ž>@umF@-K@RˆàF_ @]ð³fâ7À+?òÒ­ŠÀ‡´þöHI@šÄí™S@ØÙ*c»S~À*†ÍÐHß-Àë'íbrœF@îvÆjjëTÀÏ=6 ƒÜr@®!À(¹(À@T\Á‡HF@Y)ó=m5À»h½»RS@Y=éÌç`1ÀÚX½ÜXO@äÉ ® ÀýqŶÔR@umF@-K@f½%— š‰@<èÛ"s7Àd«ðø5U@åx  BI@Ûq}Ÿ^íÀSèú‘|H˜@ƒ(<8úT@•°¨ª¨£@ßt¸¸$(qÀõΓŽqÏåÀBš1} ›@_Á³¸ªÂ@È!;ÄJÀö8¨\ƒÇ@¬,¶¯nžƒ@á0ù»X™@îú»ãlõC@ñZÿíÞP@°ZËŸ¡K@ÐÌSþCçÏ@hr)-Ô[Î?‘ƒ(<8úTÀSèú‘|H˜@ßt¸¸$(q@•°¨ª¨£@HüÊ;¶çÀF(˜ªþÉåÀÈ!;ÄJ@_Á³¸ªÂ@­,¶¯nžƒÀö8¨\ƒÇ@ìú»ãlõCÀá0ù»X™@¯ZËŸ¡KÀñZÿíÞP@hr)-Ô[Î?+Eý}åÏ@ö\o==UÀ»+év¿Xj@[ö¢V®ÀñåïÉs©*@…Ù‘‡,h¡@vÛ›ìaÀíL'æ‚g@iŒ*vä+À¯ÁzÑD@…ôŒ­¡‰RÀÜÞ2Ã'Cj@˜bëjø[‰ÀcEýåO+À°ýh‹¬ÀvÛ›ìaÀNÍÌ´¢@iŒ*vä+@íL'æ‚g@Eë¿ýRÀ­Ñ š†`@Xõv¶J§Àíâ›xhczÀ­ìàÆ=Ée@ŸùÇyeßM@¶``ÈYh@§’`qeAR@þÇ ßÑÀðƼ%°À`ŒläE·´@^X/…û³Ÿ@BjàE[Í@/ ÞëÞò @12ä^ú‚@lйä]Nk@Ì—µ.nî{ÀjíwŒëžÀa,ýÁž©P@²£¾ÎíØ6@mœÂŸR@ûÅì«ì;@EHõ§ª°ÀbeeŸßÁÀ¿w5ÒmÓŸ@ív‡WqZˆ@/ ÞëÞò @ÜÀ)¾ÄóÃ@—R]#¤k@(D]jâT@ˆÜ‚xœœÀpºŽÍ™°~@Ôc"L{c@ ×c[€À‚µÜoDÂÀßA¿¬À{•@k7‹‚òµÀ6–3N©x@ZR_;/m!@j A:¿6À2Ãt³Ê%@ÊÅùêÌ‚<ÀwU иEÀ4oéê¿ @0¶»ižÀ¸1uË7%@·wÆ©;À‘Ô½v]ã3@ÈÐ~ß·RÀ0nè4Ð@Ç`’½˜ÀË’CN wp@Áµ·Õž®Àrlˆªæ~ÀüDUlé™@• K"x™@„“dz‘ËÀ1¤‹ƒ@Àè½ÖPÓ»Àó);)ÈSÀ(:ÍW}Øh@¼µgÍWÀÿÄ-¢3$o@Nzj#ݹw@墓îš=ÀzuXšƒBO@î Rñ,WÀÔ|-['7n@Ò¨¹eÀçòýøúq„@Ç`’½˜À*Y:W4Õ@”¹ÅŒsCÑ@#'ÉÏ{? @Ÿ»ýƒïÀ$>šr´ÐÀÑ_žp§#£@²ýyÑèUu@õV[ ‰>”@6²€Cr@„R¦+&ÍÀt%Ieû»@|Ÿ7Z®¸†@¯nyÛ“ÀE‰¦WÔÓ@ú«a·Òu@öø"ú)…™@(•”»±]v@ñD¤ïÍ@„ÀJ7ý¹Àø%?aXû¶@ö—K§V@÷Ìt{ºMà@q3/¹—tÈ@ ê e r®@“'ì1“§|@¼¨ ÒÖÉÀèVhwv3ÀÀBÞ3và€@t¦Šž@ÐR@ß'‘AçÙq@Äëð«ŠàO@ž~ÏpQÂ@4¤Sá¡òæÀ*©Pϳ#«À²ûµ{Ƕ@‰Ç1­m@S@Ÿ‚~߀v@Ž)îáÚ¸S@„ÀJ7ý¹À‘d¢ä™;æ@üœý²ÞC”@w[  ÐÞy@$Pb²À¼@¼É…¥@˶v‰å±ÀŒÚ2’¡É¿ŸèbïŠ@TárrÖã?ŸèbïŠ@TárrÖã?7=¬1|€Z@XDz¡X{¸¿õìåà¦s×Àõ r$’“À#vV‘Þ/h@N]ï$οerV\PýØ@qXŸ”“@oýçI)r@.÷ w;«É¿)59ˆÑÐi@!Û¥‹ ’Ë¿7ŽD*»r@¼O{òžºÕ¿0 ô+ø¿r¯üGÑ ±À•árrÖ㿟èbïŠ@•árrÖ㿟èbïŠ@°Ç²¡X{¸?7=¬1|€Z@'%ÒÊìþ’À§š»½Ý ÉÀN]ï$Î?#vV‘Þ/h@qXŸ”“@”,~MIÌ@ ÷ w;«É?oýçI)r@ Û¥‹ ’Ë?)59ˆÑÐi@±O{òžºÕ?7ŽD*»r@X;„¬OÃÁ['K_ØÀ»N`š·Õt@ß³X6÷¦ÀÅ2Äÿ Ább¸­Pª@ûcÊÜAVÕŽBãó×@¬Jþ0³ ×À\›¼ýÿ£§À[J¨ÀùuÖÚ@±û½›Ý @¨á;£hÅ ÁVÕŽBãó×@Bòû§Ò A%g ½S¨‡Àt’ŽxñœÀ.dÇ ˆÕyÀÈ£Ssûœ@¼ Õo@Á· @TÀ²Âg±Ã€À€áÂâQ@øãa¤M¥-@š?ñ<ˆd@â2ôcÛlP@§™ÅèyÀLÔ<ÎyŒ†À¼ Õo@:][aýe’@¼æìoÀ¦Àâ–ö¡À‚´äÄ}Ö@@Fù0ƒZäÀ{í½u'·­@#Ân^S1å@r[poa×ÀðXüÏ|@ôB}¶›¥Àª-×Er˜¶@ƒÂrv´Ø@Fñžš$TÁd«âÂä«@oòHClÇôÀr[poa×Àsòª’:ATN_9µ´À~õTÖß@+¸ŠÁ‡(œ@œ~32+s@’q1k±·Àfr@Ó>=¡@”"w–ä@¶ø^¸ÀÑÀéšžQŽZ‹ÀFÆQ‰à @IிǕ?@òn“W™…\ÀRÿ»¢ðÀnôܧ¬Kœ@Érg"nuäÀr®·D•Â@t¾«™Ñ¨¶@T²5ú³¢@~32+sÀ+¸ŠÁ‡(œ@ܹ3!eUÀý)³ÄB؈@Q\F£@¦•5ûÌÀ¶ø^¸ÀÑÀ  „²ä5ú@ˆ#"sv@yK·YÉÀÕ¬9~ÑiaÀ5®?ùr@•¸JJT™@a™cò›½À©0˜T=ÊÈ@G°“s!%óÀ™{V@UzѸÿ‡@K";Û/qÀ› Â:¤@ {•KfSÀ7ù(e¡/‰ÀUtW¥ˆ@«I^Æ@ l<øPÀ«I^Æ@ l<øPÀ…œ9Á`6&@äQ´YqUÀ¼ô˜^>Æ@ýxÿº’@éšžQŽZ‹Àˆ#"sv@ØMGO½ÂÐ@Ò’…cŽÀ‰7s“¶EÚÀùÓßÛK¬À.µŽ!@ã“Êec,TÀF-Ì­á†@â«“­ðˆ@è’Dã1/ÁÀ›Ó P”OÀÃÔMYá‚@›Ó P”OÀÃÔMYá‚@—Oˆ3ZÀceÙûȈ@ú—ÒºqÀ¢Ô²–lY“@Z%ynå’@¤…Ì@Ó_@FÆQ‰à @yK·YÉÀÒ’…cŽÀ ü™ýûÞ@¤ô6’cÀ¯Gžg¦ü„@ø|Pú]™À Ù‘{ų@Á¼ÞŠsn…À23fkÔÀá´›I.TÀðîjTˇ@†Å““IBÑÀ z]_KÀÆC_P›@KØÍ‚ÞvwÀ3©œÞb—À±×rKëqÀ›h¬§þ‰ @ndŸPƒÔm@R’‡HäÍ@/Žþ0á}@–÷CNGwÀˆ¶¡ÒôoÐÀKØÍ‚Þvw@ÆC_P›@g;(*ÿLnÀöÆINÆ}ÀmdŸPƒÔmÀ›h¬§þ‰ @/Žþ0á}@Nq‰zqBÊ@‰Ž³š¿`ÀåçëÚæ:Àw×uqôRn@dölõo W@Ý‘„­iŠ[Àjü«:¹SPÀøíV­M;ÀRËT`Z¤YÀdölõo W@Çád²ˆÅa@æ:ŠœGPÀ.SÌ gMCÀö)EDZû@Bë3^¾É@l¼Õ¦& ÁA¹*¾ú×ÁBŒ¯vÃÀôj^á ‰À0ƵÌSÎ@è7ã‚úæ @ŠhÆ·è@£N…s@jÖ§ À@õ‰»Ø@»åž´g#A’2;æLCò@+9å ¾MÁC Bx÷òÀaÅçZBƒõ@ñ#a=ûÇ@Yd*¬˜Á¥¬›ÃcAVx³r8Áæ@Þ†²øQÄ@ +èñó@Y °ÎÄ’ÀÕeä êâ@‡¶3 †‡È@Ùah!W¯ì@W•м#É@÷Ìt{ºMà@$Pb²À¼@ÞInðÔ A¹Zä)}ð@š¯h¾Â“GA>Ù+˜² Á—XîŽB AÚËXYódÛ@1FXÁéNz aAHÁÔ-Äq"à@ÀôÚnü±@U6$ =A‡©ª‘(Á?šVêÑ@KÊ u¢y®@[ °ÎÄ’@ +èñó@½W˜V_Ì@äÈ&˜¤e²@bIYƒÕ@ÀÁïgÍÚ²@q3/¹—tÈ@¼É…¥@e·Ò ´_ó@ˆÖ>‰»Ø@>Ù+˜² Á;F_;5MAŠVe¸y}À+he¨¢ðš@ýøñãÇå@„ *T¨È@p…°6lÁ0:íRæ~êÀKèü®3 @£8EBþ†8ÀG”LÂzÑ@˜ Ï÷+8À‚ Ú>'@'qoµòõDÀÍìDoîÙ"@‘ž¬pFAÀâ~º%y@dWÑÐCÀRˆàF_ @<èÛ"s7À(Y'áÐA‰G(Í”ã@ìÍÔâ%z™@®ßw¬(L·À'Mš4i’È@EŒ1bĬ@!2pêÀ3­éËÁ4‰äWO=À|*È}/V@ÿ¶fBûA8ÀãÿD„'ÝU@,ŠœƒýEÀ Ú¾Ÿõb@ ÈM* AÀ× £lvÁ^@…Órëœ/ÀfÎçé³½a@]ð³fâ7Àd«ðø5U@‰G(Í”ã@ül¼ÚOATW“¿T@}**ˆÀu_‰œŸÇQ@³wŠ7ä†ÀiP$× Ü|À¸Ã 02B±@öC(ú uÀ½Ú™.¹&¬@§ÿ1aù½jÀWS±‘!9„@ØJ=JѸÀk¯€aˆý‡À ÓNݼ@Q·«ƒ‡9†ÀŸíßD¼@LQá¸]e±@°ÃpTëÍäÀÇ «þ©@››ÂK;eáÀÈ?¿Ü»¶ @ØJ=JѸÀ+µ=Soøí@›ð7òÜ]@Ü5É”@@¦€ïô™ÀÉÓðŒåqÀgHnd„¢@r¬ [r@+?òÒ­ŠÀåx  BI@ÀWàžÊ”@FP£Úg·VÀìëôÇÚ˜@@uAZFn"@_,á˜rÀITøâë…ÀŸŸ8sr@éh(ŒÔ[T@‡´þöHI@Ûq}Ÿ^íÀFP£Úg·VÀåÿRÓa™@°ëÂ÷bÄÀ)’*·´@"§®„‡Í@š –o»À̇‹! µÀîüvN#'œ@E7œP20Š@q?vjYTÀHî¯l ¹@][þ/ÓÆÄÀš –o»À†%$ª¼Ï@˜ê{¶’€@’ F$1¹Àq?vjYT@E7œP20Š@·†ï Aq;l÷@´¾|ñ%ÕíÀfÝ`=âSÜ@“gÃmÆ1Á/"I e÷@RmZL®.AýHb§ ó ÁoÁéðr÷@a W…ä@䉜àÃuÚ@!ž7#LüÀ®³e32ø@"+»ÐS­4ÁýHb§ ó Á¼·nóyÑ5A·ÇæªD«@wÍåøëfÀŠN–ŒBu¬@cÄ\G—mÓÀõœjÏ}w)Á˜æ %>A ³»ëÛ@€¶ÿ¢Y¨À¼Óõ£[Ÿ´@w>k ÞÊÀ£)Ï7„)AÍk‡ÕZÉ Á|èiEáÀ:¿®¨§´µ@ °äzOÄ@Q ùh çÀvÍåøëf@·ÇæªD«@þU§ÀÃÀް¶ê@„[<à&A÷;÷s^8Á‚¶ÿ¢Y¨@ ³»ëÛ@#r[ÌÀÑñºy¦xâ@Ík‡ÕZÉ ÁÄÈ$5AñÒÛœ°¥Àr좣ïâÀÐrúHíÛÀo¯tlÀ@Ö”\S¹›ÀJ©ÿݧ¡@®»¸4Pq„@-ýìba@oΈ±ÀÍÇ3ö<Àʼtz“‹@–ðÀ„{@%ŽÒ`žÀ\J‰›r7@g‡(õg c@à ÉäNÔw@ÿî2ë§6b@>ýù ~‘¢@U¡ºn&‚@ýl ¬UÛa@L UšÛwÀ­_õ‹-q@} U –S@<ÏéézÛoÀkƒìøÒM‚@üuо¾(—@He»ÀõS%>o@, h [’J@MÉÖ×ï—À¹JÕQ_‚ŠÀ­$xLu@ÉíÞ³‘ÀÌå Ê[³@ˆéOj@|†»&M@E©_fŽ5b@QÕ#òÕK@U¡ºn&‚@©ò^Ù‘@¹>¤ÓõøvÀ¦>:Y±Ž@^Õ)(@ó@ÎþÜ×ÌÆF@¹î¬ÖMÄ-@xšr;7ìJ@Gœˆr è&@ÛĒҖóÀfýH§»tÀ½•º«Ùs@8UïB\@²;|Xjd@§‘ŸyRA@½Xq8š–†@§‹ aûc@Øq.Jh²¥@è*ß[É5…@­§èT ¥ÀÈïùiG…À‡UûÂd¶Ù@ü]Òl.@þ‡á@c­OPÝú1@ÿÔ¹m˜@ˆ¡è4ˆ%ÚÀ·¦ûÍ©fÀ¸ÐænE\@èÃÞœy D@ôæfî¬DK@q´êUÿ"'@*ÜS¹¦+n@×}¶òu°J@è*ß[É5…@GàLlé‘@ ± °¸"…ÀW)JGñÀÖ1ê,ä«_À{p!>À©¾µã.@¸«G´í:À¬c€b×qÀ„EµqS@ìÿmÜÑx@ 5õå4ÀMø1o¿>ÀÁ¥ :÷UÀ1w$Š;ÀTÊc(²ªH@¾M¡]v S@›·¤Ÿ:xÀ 5õå4Àr7-)b}z@ÈúfÛGt@oÁFýð5•ÀKj§wîw@&Ý!_¬Å¸?°ºçHa²ÀŒ'ÉtÐÀÙKôT=…rÀò¿`D—iµ@à'¥s("eÀâý1| 8@2|±ÖhNÀGv>}â—Àø¡¢…@¥±,c;€À¦Ù<,ñù @á!_¬Å¸¿Kj§wîw@¨ÑuM{@ª´áBa²À]˜3ît¥}@à'¥s("eÀ*L3è5¶@”TA¶ =CÀHåå—uX@9[v‡‹†@Ñ"ˆ ɯÀ¼‚ŒÿlÀ©(?=¡PÀ8.r@CM®z™œR@×Áÿ1yzLÀŽÝós1$Àù†º2¨PÀki3âë1ÀCM®z™œR@Bs1%"ëD@R2ž?Šç#À` ]0çÌ7ÀP(÷ÈôÜÁ©øÅ€EÂÀýŠ“ØîÎ@³Æ<:“FÀXëCãÏÀpJ½•Ñp~;ìA«¢O\Â@‡ày•rÂÀ–GF’YÑýÀÉÆ<:“F@ýŠ“ØîÎ@@ø E<œ¿ß¯Ð›0ãÏÀ«¢O\Â@ÁWÁcîïý@zeÊšùte@K6aß§DK@?NA|ÅtÀë²¥Cø#$@ºÛlÒ hÀ`âÌdö°@íœHt=V@Ào%"&A@e|¡$ʧBÀÖ¡^V[[X@„ó³bÅ‚p@ÏK{Ý~œjÀ]Á+©]îB@Â`û®-@'UW˜rP@ 7üîöæ4@8aŽ é*ÀÉ£¨‹”UrÀ’ ƒÜ9FÀ;¶ïÙøfÀ“âlÁJ@çþëä‹¡4@92a35V@úè*%+ÿlÀÏK{Ý~œjÀìVi“p”„@;Ž¥jÆ6@ƒGu5Us!@i ³ˆ­K@R§2[™bÀ7ìÕ#®ÀTƒúÅÉ@Î9ís÷óX@ ~0–´C@á‰át‚Xb@T=Š:L@ñ`WN»®Àœ›\Ë[ƒÀ9öÄñkƒ@ =æG'†~@ ­ü4u™a@eû±Á³@\çh3dˉÀågÓ ©”@%|LC!€q@D‡'šÎbÀâ7íÒã‹w@<­¨Š•Ð@#ÉŽc¸À…ºÿeC@¹+U€*:-@ºˆ„ª L@ÒUi§„u5@Üê— ª‘ÀÚÅGÒy“ÀÌÚɱm@ù+èS¬Ug@nƒs^èJ@\çh3dˉÀO¿ß¢ æº@úþ¿oœ–@@ÔÕÁZ@Ut•ÓY¨@sª>±6u@ÃÖá&Ñ¢£@‰ø²p@b†™D´wÁ4ÙzoWÔÀlR¿gƒÿ@*ý…·Ê¿Ó@hz»ÚÉJ‡@,®%”“JT@š1<~BÈ‚@ÍÙ´3òO@‰ºMcÕÀ›ÎstBèÀ*ý…·Ê¿Ó@ë§UN0è@²Yñù¼cÀÍ9¿þ¿ÌÀ#›Ï-CLkÀ’º š&@~6›z—Àüiót5À>ó/,Àƒó­€½-ÀøîEoÒJÀn{Ž„…6@¶t|F÷!@:D1ÈJ5,ÀÀåì°AWp@»Qþä§éuÀcQ ðì¢n@zOòò[R@a»bþãau@|ßµJ=ΊÀêNKþ@؃$Ï@@àø é˜@æí*‘1½6@0ø‹Zd”6@IÓ¢9RÀ0%¥áK0,ÀJ±@!!6@»Qþä§éuÀx€öîº^‚@VCÿÛ¦è$Áwžò­øã@­î|7%¸ÉÀµ†j„IÀÄÚz.ø«@>Xhˆ%@›M׆D‹$Au~ ýãÀŽº? á­@Ä âsV¾@3xq„OÑ@Äfm¶écÀŒà¸—r"ÀBgѳ=c@°ôH›ùúãÀUlàJæ$Á€ï¼û†všÙ‰ÉÀ;Xhˆ%ÀÄÚz.ø«@u~ ýã@›M׆D‹$A´ âsV¾ÀŽº? á­@Äfm¶écÀ²s±ÿýÓ@Åfm¶éc@ŒAá…ŤÀ¿KëOkY@¾Û59<@m'GXY‡†@Ù*Ž•öÝD@mÕ?SûyÀ@Ü<[LI"„nyÀ¾ívôxQÀbVÞá<@!öä{Q @Ù*Ž•öÝD@á?èìL‚@ø+ÎG<¢¿£¬ÝÿEùyÀvèbyû¤QÀuiê@µHfÀzY!{÷r@>álºa@!gë‚mž@ù7ù_/î¿òrHcuœÍ@ÓC2"’@F`^‡$‹Àk¾ïSø ÇÀD‡õW8æ?0¾°*]ëªÀû#d«ÚY”À‰ !Ý\@rÓk1J@.8ù_/î?!gë‚mž@ÓC2"’@—¡’™kÆ@*5ù_/î¿ImÔ-† ÇÀìKDáêâ“À–ô“4q~À‹É›SŸ ÀõcŽ>ñ3@ü?rÐAØ]ÀJK-aº‹@ÎÖD)œQ'@¢-CÅYVÀ æD nn@29ÈÎGÍ?/±ç„n@*O6a«ÀF`^‡$‹À´_¬pÑ™@Œ]¦ ‹ÀåUB,ëô†@ppQ:3ÀuïS°‚@/AQÏü´/À€¦üÓsÀåþõŠñb@õMꟹOÀtãÿ¸´NÀzÔ£LÍžÀJK-aº‹@U“yï¹À‰üS× ¿UÀ?Š·ƒ „@t9ÈÎGÍ¿ æD nn@*O6a«@/±ç„n@Œ]¦ ‹À, F’»@qpQ:3@åUB,ëô†@.AQÏü´/@uïS°‚@‰üS× ¿U@DuÐÄp–ÀD1¶ó•}@(¼þF0@‚GÍ xMÀC¸?"Rñ‘ÀÈ÷¼­Þa@k¾ïSø ÇÀ*5ù_/î¿3W8‰ÿÖÉ@Ú—7b ¸TÀåHgEMÀSS-H€j@¹ør—a@‡U«× N™ÀD‡õW8æ?ImÔ-† ÇÀÚ—7b ¸TÀdU™@ý_Ê@Œ¨ß8%…À²›!‡¯SjÀíL'æ‚g@iŒ*vä+@0¾°*]ëªÀìKDáêâ“À„3†JšÃ®@û’4`õ–@òÍæäcÀí ý|÷ÜtÀiŒ*vä+ÀíL'æ‚g@û#d«ÚY”À–ô“4q~Àû’4`õ–@0PPô´ƒ@±ÇŒ>su@ÝoÍÄ e`@èn³ÍÜ™ÀàÛ ¶HdpÀ<§›Pû™@xXí0˜l`@ýK²²ƒÀF Aç!PÀÏ'âÐk™[@äMÕZáNF@ø¢›EuÕ0@e7œd˜_@+­ejSÒG@ÝoÍÄ e`@h¶ýéýI@ØëÏYhpÀôr2( ²ÀxXí0˜l`@ìc>Õb•@c¡U¼ZÀÑž3¢UtwÀBpÇ/E@AÂÓ›Ë1@¦K¾²mÁ@ ²%Õ+ÏG@¼=#u092@³u(µœR@:Kûð7¨dÀ„VI Gœf@yúŠÜÅ¿UO€ð&è•@mÍ|˜F‘nÀO·7*žÀâÕ’¤Åu@Õš|’”Àv4t ]×@æ~pò¤O@àõ gã%À¢:/Ø—@¦¼U‰Ð*ÀÉKÿm«cÀôÀÃ{¯Ôu@2úŠÜÅ?„VI Gœf@nÍ|˜F‘n@UO€ð&è•@'¿š.“WdÀvWöBTWäÀ"œôÆçš’@pXÜG·@/šÙZÅߨ@Ö\ø¢ð1ÀŽ^pÈJ²@1†âö•@ågÓ ©”@úþ¿oœ–@r]|±¿€î@¢¤[KýØÀp¢Ay Ø@§ÖTæöÀêEUi@—@ßÂ?hS@¨§½¨  r@5Flˆ†\@¼œªù„< ÀØ ®hÚÞÀ­$ ®·“@ð\ø¢ð1@/šÙZÅߨ@DµØ.ýŽ@'À†eÞq@%|LC!€q@@ÔÕÁZ@¢¤[KýØÀ‹2ŒÚ/±ý@~1i«„²Àp+=ŠÓù‚\v—@5zs=gºö?k\ÈóÙ'@á0ãç´PÀ½Z¨Pv̱ÀIæ5‘9ZÀ‘@–é"в¡Àî%û0ˆu@G‡²ó0@nbַĤ_À“×qøhÀUÞ~#8C^@ÈÕxìK8s@&…“QDššÀcÂ~Oô“p@Éj‡!î³@Jþùªn¿À3·ÖKö6@•äŒeæí@ÃÈDÝÀ¤ÓõøvÀª„p&ßç@x„ˆŠ†À!6yW³@®µLü'j–@fE•‚v{‡À— z¾èûš@GÛ µ½¾@꛹úŽÝÀ&jN‡tç›@áêØe×~@åQrnü¨“Àò'ÍûüÌ@…Ô`FX«@WhCûŒ@ç9Ô.NÇÀô@³–ÉãÀL UšÛwÀ¦>:Y±Ž@x„ˆŠ†À·KølÂè@¸jvƒ…@³éƒ@_>ýwq@N„>y‰À]Û^Ô9EZÀx  Û7Þ|@cåîM¹ÊÀlÒ*ýyʼ@7êa4$£ÀlŒRBqÀwIˆ’^”Í@ྴZ¦À}÷œÙbl‰@xN±´ÿO®ÀbŒ×-‚’ÀÀ0"ר«@¾“ 8¾Þ|@iЏйŸÀÆrˆÛW‰¼@â kÎoåÀ=²“ŒqÀÔ$x›”ÀྴZ¦À°cÚ@ä°þAÊïªÀÃÕW§óÐ@ÒÞmÿ À«‘ê1ÜiÀ<2àJæË±Àk "•k}À¢mݧÍÂ@÷™† ×,…@;Ž®pߦÀê#ïœ9WÀû¶ %”ÀòÀn~ÚdÀÞhÜ ˜@‘e?tðhÀyÝ‘áá¥ÀE6;ÛÑ^ÀÇ.¸¥Þå‘@÷™† ×,…@íSÔš,µ@Ö"Ä'çÅWÀã{Só b¯ÀÙ)ª±’„@Y:sLQ@æë)h V @IÊaɼ=@*qç;lÀWG’‚%@P}´½€YÀ,œf#˃@%AÎýôa@PÓ`T³±wÀ }aw}­À @Zþ` @Þ:¦Éí¢@@øíè»ürÀ?)ŠûËøŒ@¾(Òa1n@±Ç$MºÀæpÏj@;Ž®pߦÀÖ"Ä'çÅWÀŒíhÔžÁ@ ­Ê6#–ÀXª}¶Q@=í—¬É@Ê÷6ÑZ!l@`†3öì#qÀ‰f…E @>PAOËXÀ=‚Ì¡he@¶º¶gP«@¤Z&m¶@LèëAB•×À<:™-sÀÆJžXã¥@[ÿ/Þ@YÀ¢Í7m§¶Àã{Só b¯À ­Ê6#–À—R).‰Ø@B˜ëx%Q@$„B:À¬ñ‡ª'@:JjÀÌŸ`b#H;@­|‘Å6ä$À‚é@2»€vEªÀkÚ0†<@Ì#çvë°À—Â0i0¨5@ËüíÚŒ• À1&à2Jã›ÀLCÿ5÷©M@'*¬5‡ÀM òoþTÀ¨‹¢@¡œ~‡»¯S@$„B:@B˜ëx%Q@:Jj@¬ñ‡ª'@­|‘Å6ä$@ÌŸ`b#H;@2»€vEª@‚é@Ì#çvë°@kÚ0†<@ËüíÚŒ• @—Â0i0¨5@íŒêA¤¼MÀtU ûÃá›ÀC_üdNRÀo‚ ˜÷À¡œ~‡»¯S@åIz7T¡@ ªªËÏW‘@Uô &–¥ÀܦÓp@·>RÄ‚ˆÀ{fY”ð@³yeC¨À´\ÓÆ|ƒ@ RÖ6·ÀaÐÔ ¸À)ÕÔ=M8•À»H?4¤ÐÀ©[¾â@Î@½…¹xZ˜ À—·#Û©@±Ž_ò ›ÀHˆ¥Î-ðf@æÂí/‰„À±II.]üN@æm”ãºÈÀi]L¶éÒ@ºú]¢©Àã2æÿQãÀ+·W6=™¸@•†¨«\‚ÀúÕPî›@˜ŒgqC¢À×4r离@JÓ¢á4–À³4n‡Ö×°@Þ§öð•ÀõG|¸óº¬Àc¦*0©Ä@Y¯rL±JçÀ13n›%é²@‚*í^´ ”À)[1Ò®@“wQ€#zÀtèõ‹¿f—@!úLš§aÀšM¤þC”@ºú]¢©À_4Á¥šÙÓ@²k]Ÿˆ0<@ŧ)UUÀÜÐ- ’uÀëæAó¿öÆ·ÇÓž @gëŒÈuûx@cá)EgœÀ£¹±)“sÀ¥ p;ñvUÀh‡p°U>p@YÆ,B™Ú¿ž|•‘ätÀgëŒÈuûx@úx?šT5@ X`5—sÀ”ˆ)MfŽÀµ+Џaf@ɦÏììP@w]QmašÀƘ‚">”‚ÀÝÒùG?Ü@Ø™Kõ[‘z@Æòtw, l@®÷e0ßÄJ@N¨Ñ‡>öw@û T¥A@“¹?ží.Z@qÌ]‰ÚD@zæ"ܸ^2@aÃLk5HÀî)ÅÚ sÀ?¬‹wÎV3@÷¡á^.%Q@µˆÝ]î9@Û5ó(BƒÀÌ·{¯0sÀÓ ÑÔy@øŽ' Zd@ð¤W¨íU@NæÅôŽ4@û T¥A@ÇÈ@6rs@å# )²D@¬\c)ó/@z}EõnHÀT…¿êr^_@qb7L J3@еÕÔ®@©`¢os Á EPåC@ã]•à§Å@Yn$0k\@‰4¶äò7¶@¿&ªÇ?Àý$R¾’@·3Àð»;ˆ@|+×v4¸¹@Š÷§~ÉÄ@,\Z’®Á•[§Lãô¾@Éø¿×bñÀppQ:3ÀåUB,ëô†@€æfóèáÀ$~EX(Á#Aw¼ë_ÎÃöÀ”°ö£Aµ@G¼%Ä$²úÀÏk¯à!nø?éJà§%¦@‹—›øÜÀtüÄRš ¦À6¶‹Ž·@Þ*¨ÙUv@£™Œ¸ë@öZÈ@àSô„ÌÏ@ 憎@”: ­î¸@Æ÷c«¿ò@ü ÉÚ¥‹À”ÉYÅW Á‹^ Ùn߯ÀÏQiYFAô äL± ¡À‘)_j+Ð@‘)_j+À»]ÙøœõóÀàC)WœÅ³@ä+Ž‹~éÀ׋³·3(Ù@ó¢pù< u@,æ‰,4@ ôoòGš…ÀXÇi2G¹@öZÈÀ¤™Œ¸ë@ßôC1@nzCGUL@豞{ãv@ ” ÖÕ§¡À+MÆFØ@ü ÉÚ¥‹@Æ÷c«¿ò@²WÄ\ž—Àˆ|.É@ºKŸÃ&š•ÀŸd¹ úÄ@5t´FíÇÆ@ÚKáqEC Á6#¥TÀ»@w¼ë_ÎÃöÀô äL± ¡À—Ä8;¥ A‘)_j+@‘)_j+Ð@[V½•Ó°¤@°vÍûwÁglR%úª“À´„ ýeË@NTw0º©„@6íÑ;ƒÀIிǕ?@Õ¬9~ÑiaÀ̇‹! µÀ˜ê{¶’€@ wj÷³‡º@¯èK-qû`À "¶ŽƒµžÀ²Ñ«“úJv@¾:l âšÀIJ°uߘ@òn“W™…\À5®?ùr@îüvN#'œ@’ F$1¹À¯èK-qû`À‘´únZL¼@¨Ž¿Ð{e@Uj°q”¦Àb~C2­ÀÀÙ¿‰Ê «ÀNŠeª*hž@ilͳˆþ†@oÿ8‘cÆÀoÖäè ¦ÀÖIˆu6?˜@Ý6$^„Ëu@¦a¿ÏŸÐ@°¯ÜP´@âÌ¥Ëþ«Àî/ÝÁ+f–ÀÃõ B'‰@|³}%}s@§í7oU¨ÀËœ±¾ý³À1qv„@g=žžb@°¯ÜP´@Ónl$Ö·@}DŽ1éÒ@»fUÊ]ëÀQ›^Gw+p@/–™úßЃÀ¢¬)ìUëÀgüL(ÊÔË@À»…½±ð@Û·Ð&¨oÍÀ¦äDMlÞÀ6v2e]Ìë@Á_û¡tÔåÀЇ|yW_A\ìÞµ ŒƒÀêbÃ@fô—@TQ÷ÏË@½ Eë5¦ôÀÛ·Ð&¨oÍÀ§ñø@ÓÁò‘Šæ@kíVÚ FÁ®¯mƒSaeÁÊ( &\KAzkõÕ@Ug!Nd‰ëÀïϳό›Ð@ :'4ÚëÀté}›˜ÌÀïûî ©…á@:ÔÐsƒü×@ÎàóRïÀcG€üÂ@ÿ‰žKæÀtçÎ;ù@Nš4iÒ¸Áªö Ê’¸Ã@Ð…_ÒysâÀ¦äDMlÞÀÓÁò‘Šæ@Ô œ‘eA³µË]ïAHÁOÀŠKAžJŒýq¾sÁ›ÐwðÀ ™: §Aå·7SéèÀŠ+@§ãAÝÿ$¿Då@Å™êòyúÀ+ŸÜ–býñÀÚè6>@AóŠžKæ@cG€üÂ@—-[¶ìÏÁuçÎ;)Aÿq/Ü”ÝÀ·H»6­û@6v2e]Ìë@kíVÚ FÁ³µË]ïAHÁÄeÝ3Ä”rA¬²Ÿ|®‹À%õåûNRp@YŸMDØâ_@g3þø–ÑÀp›`UÁ¥@”°½ÉŠ„À‚7ºŸÀØ­ÃCÑ«@%õåûNRp@xðÃÛ–Àè2bˆ:A‚ÀW/g¤@”°½ÉŠ„ÀŸq #œ³@éè,ÔŽ@?™ba¸À]=ÂAag¬À³Ü¶wÀwÀ{o¶@S\Ê@"cvð£€w@$àEðzBÃÀF#YyMŸ¿–þº&€wÀBybwíCžÀ"cvð£€w@¯¹ÛkF Ç@ ž"YyMŸ¿ÿTmÙWBÃÀd¹@üª‘Àl ù²³@‚7ºŸÀéè,ÔŽ@Ò³¨©jÿ¦@9í‰úæµ³ÀJlxµÆj@Y`SèŽÀæ™w¾³@yzÛˆ¾ÖÀØ­ÃCÑ«@?™ba¸À9í‰úæµ³ÀÙƒOŸè×@AË<,%íÀ v.ÅòŰ@î4g_ÊQœ@\@¹1æ¿.ªz}.–¾À?­u’1ŒÀòÜ å»·@HЬ‰6Œ@¬@¹1æ?î4g_ÊQœ@€˜øx<ŒÀþ„ï]û¯ÀHЬ‰6Œ@gáVÓ¡@«¬ð=2·@!L^¦q‘ö¿V^9»^£@ãolJxÀˆÀdÈ?ÞÀ½2ÝDæ À˜¨á½‰@ëÇ^‰¼·À¡ô‚°åØŠÀ¶JÙÇǾ‚@$‘tzÊòO@â¿Ñ9½ÓÙ@w%»ÂYȨ@q1lb„‰@?¿;ì8`@óL^¦q‘ö?«¬ð=2·@ãolJx@V^9»^£@ó}^˜.á¦À³§ðë•ÎÀÓ$wã•a@ÇpxJÔÀÖõõ˜ãaÀýƒçÛY@Âu棋&@w%»ÂYȨ@zó€ ªè¼@Zä_½™a@ÝÓqÔ6@»D*i“ž@Qú…à;kÀIɹu.±À7ºî¨àyÀShÉ*¡ÀŠ$KkÌjÀãa{ï4C²@™˜_4@Oú…à;k@»D*i“ž@·™TJŠÀaGÇ“¥ÀêúXhÁsÀ(¹ÚB‚ÖÀ™˜_4@ÞžAö¡@a§ò¬ãÀÆ^ ðtw´@‹ßeQµ¡@å :ã'€s@S«)ùÒÜÁ(¾ë4)ì@Xáz7§@*¡Œ§3ÄÀ|èiEáÀñÒÛœ°¥À™ f³[BAHìŃèÀákPçr¶Àí_§#í”âÀ_<4Úlh@Á¶an»å:@&õlþJÍí@Ã,žÂ'Á<ù…ݧ(ÅÀg>–YoVâ@:¿®¨§´µ@r좣ïâÀHìŃèÀÏ]ðçA ”lÌÃñsÀÕ,}áa?9@b6ƒèßbo@q‘UVìÎö¿­kÚÅÉ¿ª@Ìj^ß7À‘ ÕJ¿7ªÀŠß.‚¦¿ŒâV°}6@×.üÂÿTuÀ‚‘UVìÎö?b6ƒèßbo@Ìj^ß7ÀJ!n6ìª@ˆdß.‚¦¿¯gŽ€x7ªÀ.Ž»•¾ÎF@þ‹²Ý\Ú:@­§¬4TÒL@÷ƒþJÙc@@dþ®Q`‰@øøª ([@‚[#‚¶€f@ˆökL’M@wù‚»FbÀ :l»ÀUÀÝ‘„­iŠ[Àæ:ŠœGPÀ¢ŸRNxR}@U»ñ¹æFO@cá)EgœÀ X`5—sÀ‘ ÕJ¿7ªÀˆdß.‚¦¿õþ1 Ô(·@þºï^½~u@¼£ø­l¦Àà às]qÀÒ*¤&œD‰@Ö¬b4ËZ@’ Ï ;@ÃGSÒµÖ/@ãúˆ ä^@@ýCüö{ž2@ãÜþòµrM@Ú5yìOV3@€u±FUÀVÓõÖ«áGÀjü«:¹SPÀ.SÌ gMCÀ£¹±)“sÀ”ˆ)MfŽÀŠß.‚¦¿¯gŽ€x7ªÀþºï^½~u@îòT(Bÿ°@½Ÿ¤qR½…@I’$Ž„bR@rq£E^"†@°Voé'W@fE³D¾Aß5AëåâÀ0ÁÂÀÒ#v@Àÿ<)#À¤¿Ì"ÁÎcǻŹâ@i&{hc£“@ò5·8/gBÀtwbåíŒ@ÍQDU€â?Éy¯¡}xn@,N¸ž§ÀŒà$nüžvÀ¼£ø­l¦Àt®d|È@{•1ÏJ’@bŽŒ‰@¿¸«–²½ÀÑÎLZŠÀâCzйs@º“XÊäY\@A{˜çV‘@_x|#ïàb@Ù.óV_ W@XÜ5¶~#@xðGJ œW@ö©£;!)@ß5Aëåâ@fE³D¾AÁÿ<)#@/ÁÂÀÒ#v@è 3WÊãÀ—{i@ÊóÁò5·8/gB@i&{hc£“@ÐQDU€â¿uwbåíŒ@ÐæC@@@!Âv¿¹tÀâëÛMíCÀà às]qÀ{•1ÏJ’@õ@7óŽ@д³UˆZ@\Wúx{}‹ÀpÛú=kXÀ‹Íô¬`@›Äú€%.@ÝD<ûÇžb@N¶9ÃÂ4@ev¥fKqR@Ìf°×ÌIsÀ\¬—Ç„×PÀâý1| 8@”TA¶ =CÀà}(¾]@ ­ãŠ8ìÿ¿ð>o±bÀèzãjÉ¡{@+?Ú€¡_cÀ%dg{?FÀyˆJ§ ïU@âj¬)¾2@0*A¨rgÀÏ1bÐ…ˆ@)5³²’ie@2|±ÖhNÀHåå—uX@ ­ãŠ8ìÿ¿Òf" ‡^@"î±ù,Gq@#e}3’ÀÚ$•~»­MÀ¿®4 1À%öíTÍ@@ñì„`¶@Õ\¿=Ñ@»s!6c’@Ö;Íè>ÕÒ@84õ¡o@¢ÄŠÕÜñ¡@P^Ãy%õÀAÿ’T‘J@)9|\D/}ÀZ×ÏöG¾@!ÞWø;ÉhÀßÛ7›D²ºÀ §$0_ÆPÀ+‹hàꈕ@h.¹ Y‘&@Ê{¹K@ fÀë¯ûtåµ°À`v£Q߃@„Ðø7–ƒ@«VJs9 QÀŠÌËà%\³ÀJÍ5X@ÇgcAŸ@ÿs\E7é9À?ñU_®ù“@ÌæÏ¼Í À‘)_j+Ð@‘)_j+@Ùÿç;vÔÕ@J³Ž6­v@ùžâÕ€¥ïÀ+yÖ:!©À„s›©¡Û‹@tÄ"ÚWÀ(•¹£ µÀ.Éîæ°š6À»s!6c’ÀÕ\¿=Ñ@84õ¡oÀÖ;Íè>ÕÒ@G^Ãy%õ@¢ÄŠÕÜñ¡@µ·»9À)>FÛ?q±@!ÞWø;Éh@Z×ÏöG¾@KYt {À…¢ñxD±Àk.¹ Y‘&À+‹hàꈕ@÷µƒÚWš@µ·»9@YÛóJ!ÄÀ«VJs9 Q@„Ðø7–ƒ@d¤ +õ\XÀ–ð1S†V³Àt\E7é9@ÇgcAŸ@¿æÏ¼Í @?ñU_®ù“@‘)_j+À‘)_j+Ð@J³Ž6­v@G©¸û ŒÝ@+yÖ:!©@ ®Á%‹òÀtÄ"ÚW@„s›©¡Û‹@2T­X+]kÀŒ0¸xÝŒ°ÀoódŸææ Á­[{¯Ò´Àf¸¼DìÇ@ŠFÑz®†@a‘Ç@g7ûÀ)]ÎT3ï¸@'öu›Y%à@¯†¹4Ÿ@ÀÛ œQÉ@æZª}jJ@Y³ÁT‰À²ïÿÓJx@ôSñÿñÜ…À»]ÙøœõóÀ[V½•Ó°¤@ùžâÕ€¥ïÀ+yÖ:!©@·ü¥BéAÏU¹äHÀÀ1G‹2Û¸¨Ào}%ö÷Á[oëâI„‰@ù¬Í“1H@lFÑKŸ%Ë@õ~8¸ÅÊ Á) Jè8¡@{ë茈`@wû!·‹@'ˆ¨ZOÔÀ×òAªÌ@T§B•½Õ»À30ïŸ* É@àC)WœÅ³@°vÍûwÁ+yÖ:!©À ®Á%‹òÀÏU¹äHÀÀ,‘«ôGÌ!A§õÊ4ô%º@Ýþ@œã­˜@ºž° =‹»@±øó²Ý @Z]PUÒÀ²DÐËt‚®ÀEAë°»r›@«:˜¨cV~@¼´Ð0jÓÀ\1Î[HòÀæ/¾:v”@!7ÂPþŸ@£ºpGMl@f†±O-lª@£Ú‚Ž@ ³õŽŠ¦@v:ù"/y@Zi‹uäåª@ŽîroÞŒ@F=³"T¶Àw:ù"/yÀó ‚ÑŒ[ÑÀÀôŒXôÀ!6yW³@¸jvƒ…@öQÑãœÛ@,À¢Oöµ@v† ª;â@޵%×q4|@wztàSØ @ RÍ´f „@ðÙ¬}®Àj3¬½À5UpI!Ï@üd›&”a@D<\\0¶Àü\YDwq•ÀÍöþüç€@‹i Oʉ‚@Ô¿&÷2fP@â+]í|2Ž@Ë3&“”4q@Y}-K-z@b3:œz?M@•›R§é+@òcô«„p@:4÷Õ¢-zÀ¶ÝÇßh’¦À w§ßˆ´À/«½ÿœ7—À®µLü'j–@³éƒ@,À¢Oöµ@wÀÆòÄ@4 ZÅ“Àx ½’ÏWúäæA@"ŒÒ‰HÁ?Ð23rü£@ÿ 9 z@yÓ ¥¯š¬ÀftèP2xmÀ¥ôHóœzÀ'+þc;@­T|ÿ;@p²ôƒFRÀÇÑ"ã¥@“•/ß´q@"¹:8bl@Ø\,YGÀéYX¬:‹@î™p%]@³üO‹:¡ÀÓýª"C+R@°/¿ô]@ÛV}ép@mÕ?SûyÀø+ÎG<¢¿|øvºd¾¸@ :ëúdÀÝÿQzµÀ§BRòí{ÀAF÷_Ó™{@å0†úw}G@˜›Ãc—–¿n`Û)“À"ŒÒ‰HÁ¿‘ÏWúäæA@­1Nýw@i9%Î/YP@Œ“4ݶPiÀÏMš Í¢À¶VíOÐ,KÀ–¶ß!8yÀ€4¬"6.RÀ¯û"|þ»g@“•/ß´qÀÇÑ"ã¥@Ù\,YG@"¹:8bl@î™p%]ÀéYX¬:‹@‡4¬"6.R@°¹’²|¢À×V}épÀ°/¿ô]@@Ü<£¬ÝÿEùyÀ :ëúdÀF÷C½@§BRòí{@é¯sÎuþ´Àå0†úw}GÀAF÷_Ó™{@ë1y–¡~@;ûh Ç}S@d¥6pàÀ†F»FÕpbÀw´nv@4õ‡·¡ÇÀ!Æ Ó,%ŠÀT ì®í?•@G`pf@ÝÿQzµÀ§BRòí{@ôµfõ=é@yìÖ;Žlt@0½µ‚¬Àmÿo¢MDÀ°­ªKP@>\ç™+Ý$@bT!ì\éa@ÇzÛújàÀ,Áøós¥_@³'I;{ÀXXüÓ ºÀN墿f@Dwìy8@§BRòí{Àé¯sÎuþ´ÀyìÖ;Žlt@“XTÌè@Y\‰…‘ÜC@·Cr¬ÀkË€ýÀqö¯´[À5í¹-ú`@Tk±IB±C@ñ%™ä›L@g8bV(@m† è¦|\@l1]­é@@<¹­”‚2p@Z•²P.@«ì•pQÀ«4™Ø @¨f;Ø?YÀ‘UÝ’{bÀ¾¾J’àB@oIrqå%@ævýž‹Î/@å¬êØ @£Ü»̬?@F?/1h"@Z•²P.@ œ¾†œ¯ÀDÌE@àOL©ñr@;=ppðœ@‚3³h@^:ÂâÀê“Rf·ÀuÖÁá+¶@^¬ªƒc¼S@­ ç«ã§À>#0M›‡ÀlÉ2KÀ}@ª³8ù'x@„s›©¡Û‹@tÄ"ÚW@«ì•pQÀL€:à?ðÌO*Uá@¥?š¬ƒ¸@‚H³FÉ«ÀB ÊLZrÀàOL©ñrÀ>œ¯ÀDÌE@‚3³hÀ;=ppðœ@Ò«iƒVȶÀ{Ì·VÒÀ]¬ªƒc¼SÀuÖÁá+¶@î?Ùóßâ„Àÿ?ôS¤¤ŒÀËát ¡·r@&Ю‰¹j[@tÄ"ÚWÀ„s›©¡Û‹@«4™Ø @ 3š¤CJWÀ¥?š¬ƒ¸@¿RϺ†¯Ì@B ÊLZr@îBÀªªÀh½ö^@îÀ|*õs(´ÀGKD™y?¼@9“:ž ä@%¯m³@(•¹£ µÀ2T­X+]kÀ‚H³FÉ«ÀB ÊLZr@&ïbœ&Ö@ÀÀh1 {a@jšÿäqø²Àþ˜Uº¬vËÀ”cª:Ї@°zÛƒB²°@ðWäž¼0€@.Éîæ°š6ÀŒ0¸xÝŒ°ÀB ÊLZrÀîBÀªªÀÀÀh1 {a@a÷'J-oÔ@ìlÎë´@ÅqÖT8—@ÕÕ_+¬ôÀ³¡C~±’ÉÀQŽÈܯQ‘@ìãÌ0R;TÀÍÑÅ„Â@cgÖ¤¢½Z@BL ÐÝØÀJ÷GoJ@“+‹ô@m߸v@-Á@g°@í]Ø-N$AбF¾qƒò@H¯8X£öÚ@ Û9z¨@Æ#¯„A~ê~ýž8ÒÀiæ÷álÅAÀ¤B/(æÌ@·ËzÌD˜”@~È‹p•@Èy úÏ òÀÜÏ GŒøÁY¹CJ8ÓÀ¯§Ô\FÔAáb(lt¤â@8BþÁ˜ì„ˆ7±@ë•rÝt/@r;»JU7æ@õ•bÕÝÅÁ@L±Tˆâ©@v9]“w@~ê~ýž8ÒÀ-Œg~/#Aƒ•‡rºðÓ@·`¦;¾›@±{ÿÖ0Æ„@µ*鯅‘Q@œiÅåŸÙŠÀcði—4Pa@bϨ€cèA@S2ž?Šç#@üfLVÔfÀY@Øïé{©?×Áÿ1yzLÀR2ž?Šç#ÀbŽŒ‰@д³UˆZ@‹¹šõ«—@-ll¥~iÀÅÁ8+Oª°Àmä|‡ÑzÀˆ&¸¬eù`@vhÌH߉¹¿Ó·¹n4[e@¬N Ò]P@CV]5/Ž@ûÝ(Tš[@kŸ·¬¸Ç@w\1S b@¿óQèMa@qŒÈ\;’ÀÔ$yëÁ÷#@vÁd¥µ1@ÙhÌH߉¹¿D_¸'§fÀŽÝós1$À` ]0çÌ7À-ll¥~iÀÎÕlÝüË’@¿gÌH߉¹?ˆ&¸¬eù`@ôG¬˜jbP@gY;Ó9@ôò¼¡»ôÃÀ&ú{iyÀéþ;wÑš@ ¸ímq@éþ;wÑš@ ¸ímq@W3Zö¡j@ìì˜;G §¿ñÜz@ìKŠ@n)i\@.¾i>Ü»6@UeÒReÀÓ•7lNx@ 0“¢X;Ê¿oýçI)r@ ÷ w;«É?Ãܑеb~@x1´¥4P@Ò*¤&œD‰@¿¸«–²½À\Wúx{}‹ÀÅÁ8+Oª°ÀezwwuÞÛ@™Ž•%¸Ÿ¤@÷3¬mñy@½º.^¿Â¿·¨«¹À,Í#È¿#‡ÀþaSdy½À’ÞåƒÀ Ë´‚ÓÒ‚@‚kÉGÑ¿]@_˜wÀs‡Ù8Tr¹Àõ·ímqÀéþ;wÑš@õ·ímqÀéþ;wÑš@’ì˜;G §?X3Zö¡j@=no~jâ[@ý|/ï ³-@†rS%eÀªgœ:™“@|0“¢X;Ê?Ó•7lNx@.÷ w;«É¿oýçI)r@ˆÏH„4P@\Ý€ó&/!@Ö¬b4ËZ@ÑÎLZŠÀpÛú=kXÀmä|‡ÑzÀ™Ž•%¸Ÿ¤@·4tpª<§@ëº.^¿Â?÷3¬mñy@uwrµ…Àf·½XÙSÀ g‚¡{À‘.Zfc¦ÀkÉGÑ?¡Ë´‚ÓÒ‚@œ•÷ˆÀÀ‰@þº?6a´`@_žbÚµ•„À~l¸+WCÖÀY:ÎçeG·@׊"+ðˆ@Êîo&§&U@¡¹owä’@,s»0ÉÌc@ðXüÏ|@TN_9µ´À\ PKåa×@l(Ó‚. `@³¼`û¸ˆ@ô~Š ¼À('½Eð‡Àl)÷»ùqž@‰ÐklKp@-_Ü#¬`@~tí ¡5@í“Çà®@õB}¶›¥@Ú1¾Ç˜]ìÀ]€Îq%`@NQâMáb+@Á÷Gæ>vh@ó"a £9@ôB}¶›¥À~õTÖß@l(Ó‚. `@x/ç¸É Ö@²ó&``@`Ç_ÀÿtŒ—@ÀÓõìŒÏµs@d©†G&E@þÂpê Q‘@»Ɉ³0z@ç´½»±ÃÀä<žRGªÀ·;"æº@b„/®)ޤ@îÛýaÁ%–@<‡Ú‹¶t@6Ù¤„ÊV@µŒJxœooÀmVoH!@‚ëµ­‚7ÀÚlHTLö4@þ< „ö LÀ“¹?ží.Z@å# )²D@¥:¿³ÅSŠ@aÝ á``Àíì6ÍW£gÀËŽÞF>}@–b‹`øOÀÿJ¨Ý{•{@Í/Üd@Ôð“®)¬À&†•‘ý®¡ÀMzǤ@ï{f_@a¼£@¼¸¢“Ø~`@ ÒT"~’mÀÇvoŠe„@»F.Ül6À¦>Ê-þM@VL-øõ2KÀ ‹ËÆl3b@qÌ]‰ÚD@¬\c)ó/@aÝ á``À ‘Îà•‘@Zÿ x@ 4¤Ç‡„“À4ºóÁ½d@Ò¾¶ªx¬H@iæ‰ëtíjÀRÿ»¢ðÀ•¸JJT™@¤ô6’cÀE7œP20Š@q?vjYT@ "¶ŽƒµžÀ¨Ž¿Ð{e@Â\Ôù«f›@¤Ú~’”wƒÀ²Ï°¦q@--j=Öƒ‹À|Zû£½4kÀŸÎlñ°@nôܧ¬Kœ@a™cò›½À¯Gžg¦ü„@q?vjYTÀE7œP20Š@²Ñ«“úJv@Uj°q”¦À¤Ú~’”wƒÀOq°æ°@A\§§rv“À9yÉÁV®@"Ø:]-q€@áK«Z«ñ¡ÀÉrg"nuäÀ©0˜T=ÊÈ@ø|Pú]™ÀÃD¿„÷~a@Â$o¬‚À²Ï°¦q@A\§§rv“ÀDQó/Åè@Ï~V—2²Àþ¶jÈÃÀ„¡­¶¾¡ÀoM]¡™Àߌ’I¸ø»@r®·D•Â@G°“s!%óÀ Ù‘{ų@cÎ"Íp2uÀ–ˆ,Œ®Ÿ–@--j=Öƒ‹À9yÉÁV®@Ï~V—2²Àqƒ¸ð@¢ïìÃã/ŸÀ º˜¶Wî³Àt¾«™Ñ¨¶@™{V@Ý*È&¯ÀÂ$o¬‚@þ¶jÈÃÀ¢ïìÃã/ŸÀ6ןÁ¿@„Û×á¢ß|ÀT²5ú³¢@UzѸÿ‡@0G²éÎ0u@vòûɆ®´À„¡­¶¾¡À º˜¶Wî³À„Û×á¢ß|ÀP_ƒ1šÎÂ@Aœ.€0®m@ŒÌµZ†UƒÀKxú¦Ñô„Àº’ÑŠãF›@‚1z¶±À…¿ð,õ@ÊϪzŠ@{òð¾z‰gÀP¿A!²m†@)ɺ£%@J«»¶.•ª@3Å9»ÞЗÀ¯]$³NƒÀG8Ìn'™@B»Ê“á@‘šKX-r³ÀJôßï“J@¹›hEgµÀ{òð¾z‰g@ÊϪzŠ@5ɺ£%ÀP¿A!²m†@3Å9»ÞЗÀc\ªs¼@ˆ”t(@‘«Ó¡Í?ÀÎg„àzk@Xž°9)7@áÄÉ i q@é˜\{ð™<@:+~Äf@ì’2Ø«¿ýK²²ƒÀc¡U¼ZÀzæ"ܸ^2@z}EõnHÀ˜ªWÊl±Ÿ@®ºë!@l@ß{ƒðO’À ›†Á”áIÀ$8+!cµmÀ½v«Vo2À¿ —cÎ+…ÀMkÓ›3ÀÏÙ©ã×?À *ô¡U@Xž°9)7ÀÎg„àzk@é˜\{ð™<ÀáÄÉ i q@ò”2Ø«?:+~Äf@F Aç!PÀÑž3¢UtwÀaÃLk5HÀT…¿êr^_@®ºë!@l@1»¹¨Cs@bCM!ÍUÀ¿Ÿ) hz„ÀJfÁ]½@BCÖ®ñJgÀVÙ&nAJ@´Y¿–1ñ‚ÀûLŒîĵÀìQƒHd_NÀÓµÝk…“@²/daC@ÓµÝk…“@²/daC@¿DXõÉ%˜Àf±>2µ¨<ÀÕŽäÆÂíb@¨¡kŽsÖ•?d¿jÖ}Fq@ÿoù’Ĥ¨¿)59ˆÑÐi@ Û¥‹ ’Ë?Ï'âÐk™[@BpÇ/E@÷3¬mñy@ëº.^¿Â?ß{ƒðO’ÀbCM!ÍUÀ4h^0ù­@ªÇ!–¥š?@óÃL;¼P@aþÚÍA9@… ùõ0Yg@Ë@msãÞQ@ÔžAÐÁz@ÛC„ Õ´£¿HmÜ[– ÀóMÏØÐ³À«/daCÀÓµÝk…“@«/daCÀÓµÝk…“@]޽½ø;@$¶äˆy ˜À¡kŽsÖ•¿ÕŽäÆÂíb@!pù’Ĥ¨?d¿jÖ}Fq@!Û¥‹ ’Ë¿)59ˆÑÐi@½º.^¿Â¿÷3¬mñy@ ›†Á”áIÀ¿Ÿ) hz„ÀªÇ!–¥š?@ŒÜEþBª@¢B„ Õ´£?ÔžAÐÁz@î¦*¬»aÒÀ$Û23dš@â¹4¼DSš@3Γãèdp@&©ñ"¾‘t@Ó \;šÐJ@¯ÔɃ}$…@O“…¯ @³ÅR®×±Àfzýjè‰@­V‡[šs@#Ô6Å•I@tÀ–0Ñ‚@Œàú[ƒ>S@Gv>}â—À9[v‡‹†@ð>o±bÀ"î±ù,Gq@Jí¯`öÔ@ôÃÒDÊ®À¦ ¤—´<™@xØdi—ØÀ3ΓãèdpÀâ¹4¼DSš@Ó \;šÐJÀ&©ñ"¾‘t@,“…¯ À¯ÔɃ}$…@uâ¤@Á{@P”GÚ´•ÁÀ#Ô6Å•IÀ­V‡[šs@Œàú[ƒ>SÀtÀ–0Ñ‚@ø¡¢…@Ñ"ˆ ɯÀèzãjÉ¡{@#e}3’ÀôÃÒDÊ®ÀÝ¡w½)€á@½ñŠÙuÀ oys_`@äMÕZáNF@AÂÓ›Ë1@$8+!cµmÀJfÁ]½@óÃL;¼P@”äôM’y@9T{eÄáfÀ:3‡GßR@b[½¢ã<@³i­R]`@>ÁWw4„Àø¢›EuÕ0@¦K¾²mÁ@½v«Vo2ÀBCÖ®ñJgÀaþÚÍA9@9T{eÄáfÀ/ý¸H‰@êÆ8ùi{<@o¨ aÈÌ%@Ù9•îP@zq­HB`@îP†ŸñI@OÝ–?þ¢’ÀÒæät?e@¦ÂJú [À{JKGqÒX@]Á+©]îB@;Ž¥jÆ6@Ó·¹n4[e@ôG¬˜jbP@Qa ž»“@¾3¡áôkÀ(Ípo`B|@_±üj]@®§5F@&Gìþ©V@ÇYò…sÀIËDWµzc@ú1³*ЄÀ¾1§daêH@õ`ÿíD63@þw2f5w@Žª˜»l À~E‘û5g@tÚ§@,XÀÂ`û®-@ƒGu5Us!@¬N Ò]P@gY;Ó9@¾3¡áôkÀñ˜ï†ß¹@Y²Ï+Ú4¦ÀeˆeÄìŠ@ža7ÎYV@_4tî9ù›Ài¦n-væ@ÄRk÷CàåÀâCzйs@‹Íô¬`@CV]5/Ž@·¨«¹Àuwrµ…ÀøÏ‡ùJ°@]F&ýÅÂV@àrO°•@…'?í2­f@³ì\½]¹[@ïZ–ÚS‘Àf›‘Ù‰W@ìªjúç#@qoj.F~’@8(H³>†Á)ˆaÐ9¶AÙúö¹sTÀôü¯j@‹¼&–T‰TÀzÌÎuÍj@Kh|Êö;iÀj~ìn<Ž€@£¬Ÿœ…”UÀƒÎ&ÿí,l@º“XÊäY\@›Äú€%.@ûÝ(Tš[@,Í#È¿#‡Àf·½XÙSÀ(Ípo`B|@Y²Ï+Ú4¦À]F&ýÅÂV@·4·^N³@±Á¡5Sc@œšéùÚp4@‘¬€&eQÀÚk8ß’Ã@[ÆÑÇâQ@0ÅZ&Ïí„À(ªgÙïö`@lŸ”ÔÍ“À¾Áé"™ærÀ͆ïÀÊ8„e8'”Àb¤^ªÀ&¦»v;4š@ƒŸ]ÿte@úÌg@Ü (AdÙ•ÀQ`Vî›ï@âN`䈼@KÇBM4ÂÀÑcKªÖ¥à@h6Îqh³@ˆ¡ãúѬåÀ ò íàþ“@Ó{DÆq@;y+mâónÀ+¯ìè>ßÕÀ¡§} óy@3¯´b;™@lc%2Ñ8§À[~ ûÅÀƒŸ]ÿteÀ%¦»v;4š@ayNQÔŸ“À.$E« ™Â@âN`䈼@BöìÌkÝ@kc\ÚÀgÅÎ0Ÿ¿óÅ-Uþ…º@ëš­ eÀ‘ú‹bŸº@?êïð¶hk@q‚`™¿@$.¯ÜŒÀr\øÌ@gÚâ(æ1…@íä aUÁNû;ˆªÀ["…˜ÛÂ@Õé' ¼— ÀF5vˆ’É@ô¶†±'tÀ,}ð4!ô@¯ðѧ˜·@5šp¦#¤Û@¨\=(Z½ól?%ýŽÚÀèš­ e@óÅ-Uþ…º@Dêïð¶hkÀ‘ú‹bŸº@#.¯ÜŒ@q‚`™¿@hÚâ(æ1…Àr\øÌ@…ð÷ÀÀN¡ZÖÄjüÀÕé' ¼— @["…˜ÛÂ@ù¶†±'t@E5vˆ’É@¯ðѧ˜·@†X+–ÒÂë@¨\=4šp¦#¤Û@îQˆç‰ˆv@'W‚Ñ{ÀóÅ-Uþ…º@ëš­ eÀ‘ú‹bŸº@?êïð¶hk@q‚`™¿@$.¯ÜŒÀr\øÌ@gÚâ(æ1…@ R<£éÀR^]ÿ?]°@Q¾ìn€âÁ£ˆC‡s¼ÁÀ["…˜ÛÂ@Õé' ¼— ÀF5vˆ’É@ô¶†±'tÀ}÷œÙbl‰@ä°þAÊïªÀ5šp¦#¤Û@¨\=ê¢.&÷AºNÜhƒÇ@Ü]€ßÝšÀþ¢Ìù[¦³@èš­ e@óÅ-Uþ…º@Dêïð¶hkÀ‘ú‹bŸº@#.¯ÜŒ@q‚`™¿@hÚâ(æ1…Àr\øÌ@Tâ+ñv7³@¡—µûÛqñÀiÅÇ·ZËÀ>IÐÅ%ÏÁÕé' ¼— @["…˜ÛÂ@ù¶†±'t@E5vˆ’É@xN±´ÿO®ÀÃÕW§óÐ@¨\=4šp¦#¤Û@ºNÜhƒÇ@t´=¥ú@þ—:ÐM–ÁïqÄ3û@t¥WMˆÁ] ~]ïÀ7gÔ¬÷§@G0ÇÖußÀ×§–‘%AuX ‰VóÀQüÁk™<Û@Œ"çÁ ]ï@,ÑÃS´ù@ß$Ú÷®¨3Á¨)MGr¢©Àx´tÍJÁ:p­¡'wÙÀ¦Ñ2¶AuX ‰VóÀó™\bY°4A¶)MGr¢©@„ p§Á„½@Eá zZ“@°Kr4µÀ–§5 Z·´À&=ToÌ`ޤÁ§j¿ë®í@8{«äAºÜÒ hëÀEá zZ³À°Kr4Õ@àIONS¿a$ K9·´À\ ¯:­sí@ò"83È%ÁºÜÒ hëÀgÓ;ÜH%A%=äj7@Ô#z²XúLÀHb i¯‰@t®˜—!©`@Õ_=¥@Ô-'%Ý@#É,`߈@ôØ iU@=òî³·ÐÀŽÀQn…å†@Ñ|ª¾×’@¹6Wûs¿c@Ètx²ÜmÀ¯«2ð÷ƒ@îöhoœÅ|@Üoë!{“À+óL<ΑÀc.®­ð§@³¼`û¸ˆ@²ó&``@íì6ÍW£gÀZÿ x@¹†£\tÑ@Rûч·À†W´fõ¾À;,«t8´ˆ@•èpqy]ž@a½zs@p@®²;aòMÀT­ÆjÈÌb@5(þØù‹Ž@[Ӛﺦu@ h3!ÛÀ›@¥7*G>ÕÀß` ¬‡@*{¢‘èŸÀœèÞ@“ÀS@…‚ª@²þs³žª@™rß„ÁÀËŽÞF>}@ 4¤Ç‡„“ÀRûч·À“gA{ /á@ÈðÃϪ³À]E4Y”¼Š@Ë‹ýèËÄÀ*Ô#VB+–À»'À‹vTÀ0£·óöY¼À=á{¥+!jÀ"+#Õ=@WÔ2¹.âPÀ"+#Õ=@WÔ2¹.âPÀ‡Õín'@QÉ[X*VÀ±E²±³§ƒ@Ÿm—â¿P@æºÊ%¢’@–-ÅH‡cÀ°îMmd’ÐÀ“êH‹Àq±eg÷í¡@I#®f:@.µŽ!@á´›I.TÀq1lb„‰@Zä_½™a@ô~Š ¼À`Ç_À†W´fõ¾Àäzÿ -ÿð@»Âþ ™Ä´@ÄóøÇŸÝÀtÞmWb°–Àž®p³@ÿ<Õ:,ºWa@®ŽÓ¤o”À¼ª‰ z–À[X*%‡ ˆ@q¾+ŒÿvÀÿø4 €Ì´ÀM/Þƒ.¦PÀx†~9èƒ@M/Þƒ.¦PÀx†~9èƒ@Ȥ% [À†?_¬["Š@Ua3Y@M`€j©º%@¿JÛ÷“‘ÀWôHÚ>¨@–-ÅH‡c@æºÊ%¢’@+»ª¤ÐÀ\îr-)ÂÀ‹QS)Ö¨ZÀª=•6úÓq@A#®f:Àr±eg÷í¡@ã“Êec,TÀðîjTˇ@¿OˆØ*pÀk 5£…@?¿;ì8`@ÝÓqÔ6@('½Eð‡ÀÿtŒ—@À–b‹`øOÀ4ºóÁ½d@;,«t8´ˆ@ÈðÃϪ³À»Âþ ™Ä´@ñ’w…aØ@ƒE”²Oc“ÀH6†ýª/ÓÀÿ<ž®p³@u7q}¸ÒwÀÛæVߺ®@‰'¸Û•@ª3™¸8ÇÀ^Ç Mæü‘Àì´í}7Á:åh¬RÞ@µš5Ràt@—*O¨À¼Ä ¦e‰@'FO„óÁºÀo ¡HÝA«+À›@ÇÀðpa7Ä­@Ç e ¦.ãÀ]6 + ÎÀ §Òýíÿ@"’ñ©¼»È@ðŒ®8á@iÍ&ŸÂ&Á¹´ q©«ÀÕGòl–á@NAÇ(âuÁÀ;€öjWeò@«+À›@ÇÀ½º  8 A—êå0½«ÀîÜ2Þ(U׿™ü‡~c3@`{ȳò„IÀ›o§ŸÀJy§øj'X@&ï¥TZ1@ÓNr"Ù®FÀ+ω{ž£µ@¬ã×y³»?°Nki­uÑ?T/åÐT¼«Àv/¥¿VÀÅÁñm@?ÑÒœ¶e@ÏØÁW£À;\÷•*\TÀ”©$ &j@¬ã×y³»?³‹Êš¢µ@­’UúÆ8ÃÀí<-{VÀ$àEðzBÃÀ ž"YyMŸ¿JlxµÆj@AË<,%íÀÔà\ŠÓ@ÝŠ!^@á[c/áÀUÀ91ÎàxÂÀF#YyMŸ¿ÿTmÙWBÃÀY`SèŽÀ v.ÅòŰ@ÝŠ!^@¡ÑƒdXÍ@-\ý}(@ùÝ¿ø¨X@ÞÁŽ'ZNÀC\RÈš!|@A{˜çV‘@ÝD<ûÇžb@kŸ·¬¸Ç@þaSdy½À g‚¡{ÀàrO°•@±Á¡5Sc@QŸ²j¨@§gc-öyÀcY_@ç诂*@Œ¶}çk|@vÀ´à¤+ƒ}@]P'ÌâïÀ„èyÑÅ\@Æ3![·›§@¾+ûÀ«ñ@܆H/+–î@å2(j$’À—…Á!>_‡@Ô1ûl$¥§ÀéážïAÛŽ@ÿÖZ‹ðÀ½+ûÀ«ñÀÇ3![·›§@å2(j$’ÀY„²C‹ð@{Mˆ†Å{@ùoÄ®õ¢À­ökƒ¶¤äÀ4`£ ¬|ç@[.œÒ>Ì@qQãX„=©@v…ÿƒ'ãÀDmýŒ}‚@7#_' „@‘ˆÌ58šÀ °äzOÄ@ÐrúHíÛÀâos@0ë@aª–\:ÂÒÀÂáËέ‚ŸÀG0ËEƒÅ@Få±P(xð@€n‚ò{ó Á#ðóÜ]+©@`¢9ë°†@á‘øÄ»„@¿[”LˆäÀôg”ŸÕ¦ÀðË$ãÆ¿½@Q ùh çÀo6À¼ídˆq\@Ž|IjÜ­à¿ÃSáÉt@¾"¡%XÀ„ ²(Îc@­ýcQ·À®;”õU@˜$hˆðÄ9@ÚSØzmq@hɽ(EË?uïS°‚@.AQÏü´/@éJà§%¦@Ïk¯à!nø?;óŶO*À@!ÀÿglŒn@5´ÆÞzN¥Àa§”Š¡ÍjÀŸ!ºÎÛ_ÄÀ]@°§4ìç?Ÿ(5³Ìb@`ðvÇñ?}ö*ýÒ m@†Bä•_lî?‡V!Rh@yòcI>6@›˜¼O†@–|IjÜ­à?¼ídˆq\@Á"¡%X@ÃSáÉt@«ýcQ·@… ²(Îc@—$hˆðÄ9À®;”õU@Šɽ(EË¿ÚSØzmq@/AQÏü´/ÀuïS°‚@®k¯à!nø¿éJà§%¦@!ÀÿglŒn@ Vnd·@_ÂÄÚüþ8@¦—ª^ºUÀí%fÁ,±@ÇüD㛓@ÊD$ÍÅ@èÀUMa«B¾ÀPDFJî²À¸Z ¶˜8qÀêS Gh1@¸-ÏrŒXSÀgïAG×@ýDYƒ@r½@·ù‚~ÀhP­`b+`@Žfh|Õ|É¿•“ m6 |@5ãn9@ÀM@o97í!¬&ÁªeG¨IsÀYLBJ݉@P br'T@ËÃ0šŒ½‹ÀšÕ5ãFÀ?¿G˜ž#ÅÀý=$ÄØ±À]?Ù­J«Å@«móÉ•H@ŠZø¸Š@J¦Çv q@µ¸ºq.õ©@* ¼‹@ ­49L£@ÕÖç?{0r@²Ž½ …í™FÀcQ:µ« |Àc+¾ñ¡@ðÉîÑÌ(ÅÀ«móÉ•H@§å„2 Ä@J¦Çv qÀŠZø¸Š@* ¼‹Àµ¸ºq.õ©@ÕÖç?{0rÀ ­49L£@ÜÜ¥œ”˜ó@ZÆÏ²_@T¿**@iÒîå\@À¬@Õʨ'@7<Ÿ×£ÔdÀÑÉ]-—CÀÙYûü°±ÀEpãv|@ŠZø¸Š@J¦Çv qÀð“ÕQ•@¹ÛE¦s@ðÝ VŽJ–@8TæošhlÀ–çÀmf@XU[)niÀ,3z×’~DÀïÏ,èhwMÀ³§Žn†ŽÀê] –u®ÀJ¦Çv q@ŠZø¸Š@¹ÛE¦s@ Åu›“×€@7Tæošhl@ðÝ VŽJ–@XU[)ni@–çÀmf@~»)èÐdÀ_pÏÝ5À~»)èÐd@’ÀDNå5@’ÀDNå5Ày`n eWÀ’ÀDNå5@Ü¥mÐ…>W@ï÷­’žþÛÀgÔ±hUUI@kSÂyy@[Àµà¡ùÏ]æÀ7õõŠÕޝÀµ¸ºq.õ©@* ¼‹ÀðÝ VŽJ–@7Tæošhl@ñA^âD[ü@Ü¡Âe°Ì@,ÒÄáB¯@}ÍL_5À`Àÿ ‹“ÚÖÀ¤i?·"‡£À©@›¿ %Z@ÜæÑ—^¾gÀþƚмÀ,ïZ/ÝäÞ@i+oz‘©@Ï´o)³!@R–ez+À@zÂÒq?‘@ ¼è+ÀûØÌpäÀÔkÝTÔ±Àq¯œk0›$@:¼’‘ÅBÀÁû,Ï2@<ÓÙ=QÀ&ÃÁ·[ðÁÀƒ¹”¬¡À—ÉÝI¯@ƒÓ*í8Ÿ@‘üâÀ9@÷¯XCgÀÈ-ö zÉÀ/F¶ÆbðÀa8Ðp¶u AR‰¾.ÇñwÀ2[9 þÁ‰@XA28 ë±À¶px¡ê%ÔÀ* ¼‹@µ¸ºq.õ©@8TæošhlÀðÝ VŽJ–@Ü¡Âe°Ì@ܺã„7Ø@~ÍL_5À`@,ÒÄáB¯@Êî÷R<™ÀªrÂÀŠ#C¶ˆÀÛmÔ$ q–@ÓMdTŠ@–¨Ã MÁ¼´o)³!Ài+oz‘©@¬”‚!{‘@¦¯ñVs¥b@,4È®ž<ÀÓEAaZ@õ¡C)ú³ÀÞ¡ä€ÎéÀÀ1i’­úySÀ-xæþ¾q@% (®ÇaÀŒ_O ÝK€@ªÜŒ½³È•ÀŸ(•ð¾‘À„Ó*í8ŸÀ—ÉÝI¯@Q]’p¬VhÀeQáߨü•@ÁpZÏæ À`¡o&|ï‘@3¼°°ÁÂÀDŽÿ™¨Î¿i‹™¦Š•ÄÀ5”÷`¹ów@ ­49L£@ÕÖç?{0rÀ–çÀmf@XU[)ni@,ÒÄáB¯@~ÍL_5À`@dPíùü¶È@ú<,z·×ƒÀ-» V@ÀÖå¼Èsm@Ä~ÿ™¨Î¿ŸA?MÁÂÀrMÞ{@œtå•O¨ÊÀÕÖç?{0r@ ­49L£@XU[)niÀ–çÀmf@}ÍL_5À`À,ÒÄáB¯@ú<,z·×ƒÀ'8O‘ÅÐ@º=“^tm@æŠÈ:šÀ$Ò -I¨@BûVŸ‚¬€@ÿ ‹“ÚÖÀÊî÷R<™Às;ïìèVì@¢jØçî£À%,9‰ÄŽŽ@X\$C*`@»Eó-qÏÀ¾Qêá÷ ›@©x†ø£­@UM›€@ý4œÛ‘@€[+¯.s@0bknÜÀ ¡M ;7‡@9½§5˜@i[õ{8¢i@'Ã1Mz@v&†ªR@¤i?·"‡£ÀªrÂÀ¢jØçî£ÀÚ…¶›ysð@ôû¾è‹`@Õ+9Žœ1@Ò¡¸|›8hÀçºEÁ,ÕÀ¯1ê,Å €@3 ^ëéYQ@¨ç`Vc@Ù•ðõÆD@#f Ë­@Kãu¯kÚáÀ€\DZÞ7j@€f2¯Â;@…"Ê$ÿB#@ñ‹bSµ9Àh4l”Àè9•6õ@*Õ3ÿ4õÀTŽé¡z€°@Õq*‹¤þxÀý0²™Às—ÀØ=‘48¸C%–Àö‹F¯ÿx@òª0xÔ¥:ÀL‡Ìø~ÈQ@¦b‡}œ:@º[}¢Í Ág>íÊV AÕq*‹¤þxÀp>~‹„b´@è_W̃C¿E ˆqUs—Àö‹F¯ÿx@zP¶1ˆ×¢À='@º±@®eN…2fÀg¦#ËF¬g@eç:C-<@eý%9k ™À’Išæ‰b@ÓµmPk¨Àu–Mê?®eN…2fÀÛZ~Yd´@cç:C-<Àh¦#ËF¬g@]@5Wài@Fõ« ±E¡À{À}çsìí¿Þt :j¨ÀX"ÃÛ¶b†À‡Ä#ò™@g¦#ËF¬g@cç:C-<À» E7¯Š@˜ þÚ{IšÀû©•¸#otÀ4Ǧ7Z@^×Á᪙@Æ\¹ ¬Àeç:C-<@h¦#ËF¬g@˜ þÚ{IšÀÑ.˜L®@‡3é…ˆúG@•\$¾F4}Àù CšÁZÂÖˆ÷4‚@@Yò•“À©@›¿ %Z@Š#C¶ˆÀQȧxÚ1´@àI=ŽPlžºÀæ²À®AÁ ?ÃE@“×4ÄßdÀ’Œcì|ž]@ÀõB,vûzÀ܆"- k@J&+_iȈÀL圯r@&Ï厸 ÀY1_Žå±q@þ`,B9¿ Àyô÷É”E"AYŸ©éˆÀåèR»yÉ¡@ÜæÑ—^¾gÀÛmÔ$ q–@àI=QȧxÚ1´@—ÀŸ§V§@íÊ–ÜÛÁ$Á[3ä'oÃSÀ–=L7r@ g÷ æjÀÈü°üˆ@éÙŒxÀ¯]ÌÀ–@o p–΀ÀR(³ ý]®@Òw7á€À×µ–üj®@hP­`b+`@ëfh|Õ|É?HŒ1Å}f×@Go5¿@‘üв4¿h@¢} Êìì÷? ‘7ñûÞÀŽ&bFÈÀØ”æJÝþ@Öõø*B±@ÙÂE CV{ÀÇm "3@Žfh|Õ|É¿hP­`b+`@Go5¿@¦²ß`ÅaË@Ù} Êìì÷¿‘üв4¿h@Ð3¾0ÈÀ3 •5éÏÀ\ý¸ðz±@`Ÿ„L-£@gö§Î–6@îZGm*ï|À ð~ Û^ÀVwOÜß_@•“ mÑ‚Àî.¢ëÌœŽ@ÆsÃc»`¡À‚0Àó k˜@bÏuý‚@Í£ä˜fd@.|ØcÝ>ÀÀ%è% Z¯@€tñž2c@{¨Ÿåj@´@´ÿ¸ž%lAŠHìF¤ÃÌ@¥#í Äÿ@§÷›Ê@FFOu%è°ÁúüÈ–.Aй¸EÁú¯”[›Sk@Ö <7ôÂB@,ïZ/ÝäÞ@–¨Ã MÁX\$C*`@Õ+9Žœ1@æ²À®AíÊ–ÜÛÁ$ÁáòÚÐì*Á$6„Ògà²Aû¯©o¶lÊ@flVÕ[èÀ¥ÄÊ­p@lð&B@þºÄ“\ce@™÷†\xG@ÌüH“:öá@.µWuIgÁ•lyoVð@Nî_ü+ÁPÒ‹rT@›‹•8ˆ•5@7¬ôæ6ö@qµ³O>N$ÁüDEƒõ@1©ü­U$ÁnkÝáɧ½@˜¹[³›@øQ^–pÀh#&‰z-À¨´i3x@Ø6ékâ¿Å;^d‹„À9LaB TÀbõTJ´|@~ƒM!MS@N™·$@­™?ÓCáBÀa¿Š-gŠÁÀ0%Û´R`ÀÅ€0 Â@ ¤yf£5@G—)žbw@¯L_ ’Ža@˜¹[³›@ .«Æ¦Dº@*dKð‰aÀ÷@ãɘnÀUØ6ékâ?¨´i3x@׿IÓnüeÀ%”¯àÐÀ"$Hs׎y@.F×é¢/Q@dö£„øBÀ_­²Ia@ÆÜf^a¨Àxêè^Ã:ºÀ›x‡¢¹Ø@(ÃS_WD3@àáדÒt@Õg»D_@êSµSAÈ™¡Ó¾í&A¥ëêå+?Áe œýî¡@½šZZ“¹@ý“OO¿ÂŠ@}$0¬Û¡@ŠE)•ãŠ@9ý}p¥ŒRÁ–7 ³ 'ÁÈ™¡Ó¾í&A‡šOå~ÑFAæ„:ÜÍÁÀÔ·q$mÿÀRã )X?œ@ñi¸o!n@a‘¯H]„@0‡&xgFn@bæ­¹&Á<ÇÈí×EÁb¼ç¼*Ï@|â4nO¸Àm<ùˆþæ¥@q<Õ'¨YÀ)Tµ˜çÐÀs_ãoOų@c± þXª–ÀK`¦Á“@|â4nO¸Àç0Ä>€wÝ@s<Õ'¨Y@n<ùˆþæ¥@e@5ÇWâ´@ja·ö”ÛÀ+i3Ê0ˆ@AÃ×nÂ}²Ài+oz‘©@¼´o)³!À-» V@Àº=“^tm@»Eó-qÏÀÒ¡¸|›8hÀ9?rÖá¶@ÏÆ–V wÀâÜ”ÇbX»@u’\Ž@Ï´o)³!@i+oz‘©@Öå¼Èsm@æŠÈ:šÀ¾Qêá÷ ›@çºEÁ,ÕÀÏÆ–V wÀ,ê&tÉ@Žu’\ŽÀâÜ”ÇbX»@EB¯Ç¦=Ö@±€Ü±¹@¿ÿ3¬­ÖÀì$$•vʹÀ$^/¡S|@ÓR\bN@±€Ü±¹@Ï239\@Õ’ý‘ºÀƒö‡)Ú³À’¤B"b@ÓÚI•=è5@R–ez+À@¬”‚!{‘@¥ëêå+?Áæ„:ÜÍÁÀm<ùˆþæ¥@s<Õ'¨Y@9iOŒßÚó@R˜Yì¬Ä@‡,°ËàéÀÖ¯>ê!·ÀÎкæ)ùÄÀìƒÁt° À¿M\~û@·É…YÚ´@zÂÒq?‘@¦¯ñVs¥b@e œýî¡@Ô·q$mÿÀq<Õ'¨YÀn<ùˆþæ¥@R˜Yì¬Ä@¿Ò$Ü@ÎvªrC£½ÀýµÍ¯bÃÀ˜òG ÌâÀÃ4R|qf¶À·É…YÚ´À¿M\~û@yôXþ*š@B¶ÚB(o~À ‘7ñûÞÀÐ3¾0ÈÀ}¹uDôñ@ NêhÐ@<‹êuè;åÀª„,°`·®ÀA¶ÚB(o~@yôXþ*š@Ž&bFÈÀ3 •5éÏÀ NêhÐ@ꃑ¼0¿é@h³xþ²Àufê –âÀËJgóµ@f¯Úç VšÀ¿4RãaÀ­'¿4‰@%ypLšjµÀ7%ᔋ@f¯Úç VšÀš8!Š'ÁÍ@­'¿4‰@©õ8Ôr²À7%ᔋ@#VŠc½‡ÄÀtÝ5®{ÀohƒS¶¢¿¹hæ D@“Í’3 2@YÀEÕï “ÀSEEÏ“–gÀí¼ùq@­`ü(c@-9­õ[!ˆ@ºF”Ë_@"ÄIÈÓT@!›iB@çV+»H~@ÈòûV^£?õ¼ #NþzÀu¥œÎ/@¾Eݾ¤¡@ÃCä5Ñ|ÀI/…=Á_À›ûû5u[@âb–‘*¥@óæ‰#/s@¡‚Ýþ I@á˜:ÛÅ?@ú²R¼£ ,@ÈòûV^£?Ä÷ôÇ«E~@Т„D¼ú§@pcQ,ž?Т„D¼ú§À¨.=pcQ,ž?ÑædäNü§@pcQ,ž¿ò¨1”…ú§À,X£ò{áÀI¥ŠV¸ü9ÀT\ÖÏà<@ý0²™Às—Àè_W̃C¿¿4RãaÀ­'¿4‰@üú%å¦tµ@÷V¦xòšÀJJR\ä›&@Zx×@¡ÎDÀÓ½îbNÀ§ÊiÏkã@»!?@ºÆ+@fá)ýÉ8ÀékÜ­Ø83@(Ãöf]À^S<{ѲÀÍLƒ}—u@Î/Ggf—@A$ì×H@Vo¿SÐ AŽu¨T.Ÿ<@¹SÙñFNÀØ=E ˆqUs—À­'¿4‰@©õ8Ôr²À÷V¦xòšÀ?;X2u­É@’4*{°PÀ3»³,ú·n@ Oòp@XÃæ ¼ Á(ù4£Ìq8ÀJ‹ànÊÐE@ñ”×òå`\ÀNÛ¤ª‚³…@‹…H „x@)T›/h ·À‡ñé Ô—{@0ŽHÉGb@XO£g`y£À¶s¤©º€@¢O‰Ät&@Lý û.HÀØ”æJÝþ@\ý¸ðz±@<‹êuè;åÀh³xþ²ÀœX¿†›™â@ø É¿ÁÖà?=wÏÏj{ÀlgÖD¥ÀÒ ˆ,*HÀëX#“j@Öõø*B±@`Ÿ„L-£@ª„,°`·®Àufê –âÀø É¿ÁÖà?H90'v™â@ªYOíAMÇÀ«Hv~-5@ôg§·ü®FÀ ¼è+ÀÓEAaZ@“×4ÄßdÀ–=L7r@N4mÙ¾w@flVÕ[èÀHÝ–b8À›Ò‚ªÓd@`pº¨3ž/À/¤vÓžÍL@›?ºBÜ<À†£€’tZ@(šãBÀžØViàq@ºdñŸI2@T='¥jaÀgÁuž ¹@ƒ âa3‘@ûØÌpäÀõ¡C)ú³À©x†ø£­@¯1ê,Å €@’D‘ð…Ÿ@¥ÄÊ­p@½šZZ“¹@Rã )X?œ@‡,°ËàéÀÎvªrC£½À(tð }ð@e‰_ó~º@äç5ÆX¨@ v«ZT’@ò7†­‹k¢@`léá×Ƀ@í_ª¦yù¨@‚V¥Ýqz@v[%飋@k±Øäh˜b@ÔkÝTÔ±ÀÞ¡ä€ÎéÀÀUM›€@3 ^ëéYQ@ Òßß q@lð&B@ý“OO¿ÂŠ@ñi¸o!n@Ö¯>ê!·ÀýµÍ¯bÃÀe‰_ó~º@Œ×ˆÓ_Ñ@®L­Jøy@»9<ƒ-c@êluãés@»D\–dU@ŸèuÝÿz@x·_r–L@h«ÉÜ›ƒ@þºÄ“\ce@øQ^–pÀ*dKð‰aÀúYâøoÙŠ@Žß t*šTÀ¬1Ð|Žø8@Øz”Ð"@¯p œ–À”Ôôž «`À° o&h@µû…¼oe@þöb&ªƒe@™÷†\xG@h#&‰z-À÷@ãɘnÀŽß t*šTÀ>r4šÃ‘@Ùïé@E@7!ÊØ¡‹@¿kOýËË`ÀSs ÛMÀÞÙˆ¸JJ@$¥b‹iG@nκ³þ¶ßÀ¦€g8,³L@ÌÀ¿á”ß^Àq¯œk0›$@1i’­úySÀ’Œcì|ž]@ g÷ æjÀ¶Ëû5>Ñ‚ÀÌüH“:öá@¨´i3x@UØ6ékâ?{ ´ä&@`pº¨3ž/ÀÿéAC¯Å@€ÇƒYÉž?…ŽÊŸ¨ÅÀWAÇ㣔ò¿çî þN5@6TpÞhˆSÀªÃaA,=@P¯œYÁZjÀÀ;¨nä;@8ß±fjÀï³ÎÓ4äü@p»—\%jÀ)…Tø|@:¼’‘ÅBÀ-xæþ¾q@ÀõB,vûzÀÈü°üˆ@î.¢ëÌœŽ@.µWuIgÁØ6ék⿨´i3x@-­CXþ?/À/¤vÓžÍL@€ÇƒYÉž?Œå:“Å@øÝ6ékâ?‰•Ø*×§ÅÀtÓn]iSÀj}AP@Ëq@Ï.œIC“ZÀÖÝ&!ˆ@Ô¡˜;áhYÀßÕsg ˆ@WçD>´¦@²%Œ²€™ÛÀJJR\ä›&@’4*{°PÀS'au´®¾@óÄÜÈ}oªÀ+Çra$îÂÀóÖ‡WsÀª·½áœõeÀ˜òóJåùÞ@:L…¤à»ŒÀÛ[±i<<ª@òè•i .@1Á˜›kWÀúȃpÀ é'äÖXú@Zx×@¡ÎDÀ3»³,ú·n@óÄÜÈ}oªÀ>s|Ø¥<Ñ@¼Ð …xòk@‰ …h½ÀOÍ’éNˆd@­¦IŠüÀB2ã,<ª@Ÿöû(ôÇÀà sò’/LÀþg¹Êu@<þ¥W"Á~m¬ø4ì@9RŽ\þ¢@¾j\Jj2À“Ø­c§}@?·2ðœê!@+Çra$îÂÀ¼Ð …xòk@?I*‡Û@¥kY#çQ@çrè@°!A·èG9rïÀŽùõU&@ O´Å` @Üò¸À›TïÀæZ/[^• Á»j\Jj2@9RŽ\þ¢@B·2ðœê!À“Ø­c§}@óÖ‡WsÀ‰ …h½À¥kY#çQ@sP-x²Ò×@·èG9rï@çrè@°!A O´Å` ÀŽùõU&@Т„D¼ú§ÀpcQ,ž¿Ü5ÙZ²@Õêv{9åj@ÏùÊÛær™ÀäVÛ=äjÀ¨.=ò¨1”…ú§ÀÕêv{9åj@å[¦ïé¯@×êv{9åjÀ[lO%œ5ŒÀºÏ?‰âΕÁe}sÄÁÄÀ äþU“ÞA£Eo¶™óâ@ß¶‹ÊãÌñ@@ plÕŒÂ@Ó½îbNÀ Oòp@ª·½áœõeÀOÍ’éNˆd@çrè@°!A·èG9rï@4Â?N‹•AQ·í*Ø›þÀRbE’xINÀ ÀÀ7¬ôæ6ö@c± þXª–À+i3Ê0ˆ@wEûo%@(N$ÁK`¦Á“@AÃ×nÂ}²À4Fª]SÀϸêØq@P¯œYÁZjÀÖÝ&!ˆ@V”o‚!âÀôÄ:ÜJ£@ƒîP#UxÀM…_`# –@•׆«¬q@rSäµÔl¸@4€üÞ;}ÀèxÎaÍ­@ÓÄ“jòòÀFÙÑÑP%a@ò¦<¹qrÀrï–Ùxv¤@y>6 |@È-ö zÉÀÁpZÏæ À9½§5˜@€\DZÞ7j@Y1_Žå±q@Òw7á€À€tñž2c@üDEƒõ@çŽßb`~$@ýÿ>šãBÀí_ª¦yù¨@ŸèuÝÿz@À;¨nä;@Ô¡˜;áhYÀç¯þËuI@“véD¥VgÀÄa‰Ž@5‚ƒh¥)p@¾ä;mQ@4€üÞ;}À)B úV¯@“¸RÐËb+@þGÇ’î!Aì¿o},:À%g9 Æt¡@KÁžªu@5ãn9@ÀM@/F¶ÆbðÀ`¡o&|ï‘@i[õ{8¢i@€f2¯Â;@þ`,B9¿ À×µ–üj®@{¨Ÿåj@´@1©ü­U$Á­oReSÀžØViàq@‚V¥Ýqz@x·_r–L@8ß±fjÀßÕsg ˆ@Ÿ/:© xÀ§«ÊE“–@x¢… /Ü_@Àk¸} A@XOG~€ÀèxÎaÍ­@“¸RÐËb+@¨ÁLYc®@Ž™å°@¢«I~Г@ªTH¨§ÀÂO §¨¥‘ÀÁŽý%ïÀßJ¹6SaÀ¢«I~Г@Ù“ló @ F¬\mÀó†*ñxÀ©,ëÔ›kÀtº:î΃Àa¿Š-gŠÁÀÆÜf^a¨À° o&h@ÞÙˆ¸JJ@Ðu—Œu§g@¯g%ÒQ@!ÕÀ$•ÀBjªq­SlÀ65ÝüŠXÊ@ÌZ/X&¬@>Ä$FT£À ;AF¯å2À€ÿH°À\’ä¯~vÀ0%Û´R`Àxêè^Ã:ºÀµû…¼oe@$¥b‹iG@7 ââØe@ÊL ø]¼O@ò)ÆS©€ÀºQðÁOhVÀÌZ/X&¬@YÃ[¡üäÀ@moWš,‘ÀâÉ¡æ™ÀvÀ/ìzÀP*E’–ðpÀéáÖÍßôÀ¶?Λ”ÁÂ@Cr/—Á„¶sG+?_À_yƒd@µà¥«Š[/@lÓÈwñ&v@«&‚?þ³ÀAm~w¢LQ@PmÀkÕ‰Ñ?zyþäc@#òÜoÉ À³`â忎À£Ád!üÐ>À|ºv|›’@ZÆÏ²_@´ÿ¸ž%lAÅ€0 Â@›x‡¢¹Ø@ŽùõU&@ O´Å` À&<´™ô@?ʯ¯gÍÄÀ¹TUÏBu@.€Ûm¦^L@>Ä$FT£ÀmoWš,‘ÀÌu½«°@ø Íç_p@i©/ªE/q@G‘HÎY@ o¹òͬ@ë*ºÛJ44@ÎÔ¡vªÀ_Âä­ãÙpÀNâLÚÄÀ¬4\\ÑòÀÙU¬ÿÌÀ¤¼é¬ÚmÀ°ãàÅœ0@¯ž§K®âù?§&‚?þ³@kÓÈwñ&v@fmÀkՉѿBm~w¢LQ@þ"òÜoÉ @{yþäc@ãÉOC<ÊBÀBÌxÍí#†À¯„‘¶ùæ]@T¿**@ŠHìF¤ÃÌ@ ¤yf£5@(ÃS_WD3@ O´Å` @ŽùõU&@?ʯ¯gÍÄ@&<´™ô@;êH<@0’þHPò? ;AF¯å2ÀâÉ¡æ™Àø Íç_p@}¼w´9š¢@‡J}À/@k·Ñ’¨@ŒDeïBfX@«.`Æœ@^¥ìdÀuÀÆã鿳;À¿ÿ3¬­ÖÀÕ’ý‘ºÀ>®Ž™çà@å “zÑ´@z¹î CÆÀÀ/Ü©]4•@ì$$•vʹÀƒö‡)Ú³Àå “zÑ´@µ²^ÙèfÑ@"h$ðã“@ªØjmÖÏÀG—)žbw@àáדÒt@]íCQçÀíBs'SÊ3@¡Á,šFeÀs¢BçéOÀªTH¨§À F¬\mÀ€ÿH°ÀvÀ/ìzÀi©/ªE/q@‡J}À/@ZöŸ# ´è@Ã$[9ë}Ž@‰Q©ˆáBˆ@É“k‘oj@¯L_ ’Ža@Õg»D_@!ý*F@(èÈ^çÀs¢BçéOÀØ9–qmï7ÀÂO §¨¥‘Àó†*ñxÀ\’ä¯~vÀP*E’–ðpÀG‘HÎY@k·Ñ’¨@Ã$[9ë}Ž@nÞÏÅášç@¶ðXCûr@àHíl=¸S@‘48¸C%–Àö‹F¯ÿx@Î/Ggf—@‡ñé Ô—{@ž"*¨ÀN+óÅÃ6ŽÀTѦO˜v§@pd×ÚX_@ö‹F¯ÿx@zP¶1ˆ×¢ÀA$ì×H@0ŽHÉGb@ªDt+~ëÀ³ŠHtÀpd×ÚX_@™ûä ”3¤@¢ûï}@Ðo9°3H@$^/¡S|@’¤B"b@{j]¬kPSÀºdñŸI2@Z$¨©Ù¥Àœq-Ö‡ÀÁŽý%ïÀ©,ëÔ›kÀ o¹òͬ@ŒDeïBfX@z¹î CÆÀ"h$ðã“@‰Q©ˆáBˆ@¶ðXCûr@®]I½îÍ@Ø“sÛÂ\‡Àò‘.úp¤À,`ñ¶bÀ¦ËF$@Ô¿\ëyð?ÓR\bN@ÓÚI•=è5@ºdñŸI2@T='¥jaÀœq-Ö‡Àì’ÎjÀßJ¹6SaÀtº:î΃Àë*ºÛJ44@«.`Æœ@À/Ü©]4•@ªØjmÖÏÀÉ“k‘oj@àHíl=¸S@Ø“sÛÂ\‡Àµä¿­²Ñ@…±¿åñ<ÀŸÓw ¬“Àh==M4Ác 9pEqÀŽ^ºÖWb@A»´½/–,@óxˆä‚@iÒîå\@¥#í Äÿ@ÎÔ¡vªÀ^¥ìdÀuÀò‘.úp¤À…±¿åñ<À®qœÄÖ½@mc¦”}O€@Ùrû:vÊÀP ç´ôR8Àºx .@®ô A5h÷?I|na [@À¬@Õʨ'@§÷›Ê@_Âä­ãÙpÀÆã鿳;À,`ñ¶bÀŸÓw ¬“Àmc¦”}O€@\g,’•R“@**¿öT@ÝI7»B@8 ÇEÛ‘u@"õrîÏ'eeÀû%ºb·µB@¶°àp1@"õrîÏBƒ×ðÿ@²× ¤´¹À@îR>þ ?çÀðPóÕ6¤‹@έ?cÉc@ÌF(¤@ò¯OnùºÀbþ ?çÀfî°0©«ÀÉkj4Žà@òN¢—èI­À’CÀ'¡ýáÀÅn9™ä@Y×À»à@Ãèd5C`ŒÀíHçp¥ü¢@m×з†ˆ@­á;^ŠùkÀëV4žZ@¸B+ Å„wÀ%Ö;)Ƥ’À|G©Ìkâ¢@4ågI!b @Þ„rÊùrÀi­£l±r@.«‚„•ÀÁÌjú@@þ²³Ÿ/@U’çQèxD@ÖÇi × 3@œ —UOŸ`ÀŒ‚lŒ …GÀg<›]çwD@ê¿èwÀéAßVï:@üâß êLÀ­á;^ŠùkÀ¥üºzh#’@Hÿ©·?“lÀ&Ì,y?‰@º›ùl÷U“@5ØJwœ®¶ÀÁj‘R:‹ÀG´¾‚æbƒ@Ý.(<ZƒÀÕIÝ"°Í¡@žnÖ•/@²¥&k@Zy!j 3@ç#òµ!@Ô_šmªˆGÀŠÄÓØ.ÕJÀÇñ1Ù0ùUÀd¯ˆΪ‰@V ¯XêLÀÀШR _@G F!>nÀÛ"ð>–Õ@2 p±éâ@Ë«tŠO@À?{ħOƵ@÷ñN'Œ…À½WruPĵÀa¤RR}j@³œèþ¼$€@[g,GïÀ IÁ¥Œ­qÀØñD±@÷ñN'Œ…À¸Psžæ§ö@@þÓEa||@Ñù.ÁxàÀWèj“Œ-ç?‘Ï. tVÀ#ðÂEz0ÀèO!(îÔo@½WruPĵÀ@þÓEa||@VNÔµ@\çµà•.ƒÀ;ëS*¯CXÀàK±‘Ç@Ww×âW/@F“a/¾Àa¤RR}j@Ñù.ÁxàÀ\çµà•.ƒÀ Æü§ŽÌÛ@® ÷¼@lØqDüœ…ÀöPak´Àïi¸á\%“@U~,ž‚VžÀqûþ~½­€ÀlØqDüœ…Àº"•ÐÇÂ@ïi¸á\%“@·XÈÑu~ÂÀqûþ~½­€Àö”ϰVbÀÈîÅAól@Hx}€Àñ'²@ÉSOd:@•jp‘Xd@)ŠA¢®‚ÀiúÊgNMÀÉSOd:À’ }_ÉÕ´ÀL&ó"^ž@R§vësýÀ?Ñ9rS-PÀwÉ+tdøQ@m^©NcÀß‹ Àoq@"M’·s¼‚Àû"1‚À NiÁ¥‰”@ÉSOd:@¢1mMù±@IÈlëWyÀÀ*Ê’D—@ÉSOd:ÀHbiÄ'À#>˜Ó’@0Ï÷ÊjÂÀã“à· )Õ¿ÏTèÔ%d@¡ê#b”afÀ%©©?Ü x@J&WÝe·…ÀúRÃU—@įO…€˜M@zLLÀ—Û`À•jp‘Xd@IÈlëWyÀô@¡E/“@è‚Nøû½…ÀÓ=±aöŸÀ¶ÀŽwhJ”@ñý.n^¡?OJ>i©‰0À‰X ˜xÀ¼C2 b@2Ù‘÷^2@KïYœÝ¼CÀBE¬œ€@NLã—ƒóù?‹ ³FÓQ@UÈïhe'cÀþ!£í,kÀ[*=C]õ~@)ŠA¢®‚ÀÀ*Ê’D—@è‚Nøû½…À±òè•!¬@ûs.™½{š@›ì~`ìÿ´À¹Fê¤å¿¿×÷æ^N@ ßà”Ædb@ éôÆ—Àa v`ÞPÀ4C8þ©b@¹Lã—ƒóù¿BE¬œ€@`’n^pÀ>ÛÁßj–@%qÄ3`ûÌ@*Шø¡» À‡`\oHÈÀ0ÞÊ3øè¡@¶ÍHc›CÀià ²cÕbÀ;æz ¹õœÀˆ𽣿*Шø¡» Àç›à¨ËØ@0ÞÊ3øè¡@³!ivòÖÀià ²cÕbÀDÆÆ“²‹DÀx½WÞš1õœÀNФÌÉÀ˜ 8ƒÚƒX@â„êaW“@Gºô7vTÀpvbîÉqÀ' K(0À‚µþ¼X@;½óI¤”ÀGºô7vTÀ€›ùj휗@í,}61À‹RÉ™ˆlÀöPak´Àïi¸á\%“@iúÊgNMÀÉSOd:Ày«éqhÅ@?õWÇ4ht@•.·õ=9±À´ ß병“À±#toéù‰@ ù_%mMl@:»aù^ÀÚÁƒš`†C@ëårŸ†§À3ÚC²s–ŠÀ®i%ý)@*·Ñ„s@ïi¸á\%“@·XÈÑu~ÂÀÉSOd:ÀHbiÄ'À?õWÇ4ht@ùÄÉ1`¾Ã@´ ß병“À¬aï;vÀC B¤;Pl@¥™OTÙN@ÚÁƒš`†C@ÿEgÀÑP*^壉À͉PŽÛúlÀK›b-Mžq@QúÛfFU@mmÁ°¾Ê@ãíóYÃŽ²@𮵢LË@D“=ÌøÐ@dN{¹8b§@·¯¶xOz@$¸­óm€µÀm—Ä EŸÀHϦåhØÀ%4ëÁ¨xÕÀ¢öEö‚j+ÀLó1?;c=@9½³FÁbÀãicÈ•@¤~™dÔ@•—-ééL¼@D“=ÌøÐ@suìþl¸á@SÌå†è@"W ’ˆ®@ÜJ¬_ ú£À¥o̤YÛŒÀ¨§µÈœâÀ*¾©à3_àÀKó1?;c=@O';€OÀ=>óÔ¼•@ôŒ@ Š?ÉÀ8âéÇðÑÀ?ái6Fz@qæ¾ÚüSu@ìò½¾?L/ÀëV4žZ@Hÿ©·?“lÀa–†ÚÚÉ@æ ú±V'‡À›ò¼0–h¹ÀC IÒóŸ@µÎK#t @¥´A±µ|qÀÁEi°tq@Kż¨1ÀyüQ!^p@€"ucL@¢8ûõÂ@ab£ôcj@)âõjÒC@†Ö%'wÀ¬iôá,:@rË0rÜÿKÀ^PwFr”@0*X§ÓÀåò½¾?L/@qæ¾ÚüSu@¸B+ Å„wÀ&Ì,y?‰@æ ú±V'‡À›¥RiýÝÍ@W®†ò˜@Ö¢ª uÉÀ†œ(ü#)ÀëååæŽ@5_Ï~¹ØŽÀEŸb“ü`¬@"ucLÀyüQ!^p@ü`b£ôcjÀ¢8ûõÂ@»ÎøKƒaÀÎÂITôt”@*ôW-² WÀ2UE?½h@H0pÒ°R@¨EKµÝQ@LpB¹°‚@Ç]9¼AóP@ ¸»6¦@[¸Ïÿ:p@%Ö;)Ƥ’Àº›ùl÷U“@dN{¹8b§@SÌå†è@›ò¼0–h¹ÀW®†ò˜@ +\Ô2ä@çjC£»À€ÄH«8²Àf-šŽ’@ñ:öÎÁŽáÀ£ÖÁÃf•Àò„ɧYü|@üæNþéåI@iÝD/uC™ÀdßnZG¼£@ç|r‹ígÀW"fgÔs@«EKµÝQÀH0pÒ°R@Ç]9¼AóPÀLpB¹°‚@Z¸Ïÿ:pÀ ¸»6¦@|G©Ìkâ¢@5ØJwœ®¶À·¯¶xOz@"W ’ˆ®@C IÒóŸ@Ö¢ª uÉÀçjC£»ÀèǽA'!ò@“6™vý}@¢<±ê\ÈÀÈ )Ý=Àõ5ðÖ¼¯áÀýæNþéåIÀñ„ɧYü|@M[£÷Æ>–@2". LÌÀ¯°ä4ò‚@6žËyUê”ÀopnBºZ¯À\ˆ ´ÕÛfÀ-5¥_aÑÀnÚ´¨ eÀ4ågI!b @Áj‘R:‹ÀµÎK#t @†œ(ü#)À€ÄH«8²À“6™vý}@i<ÛˆÆ\Ü@i’ô0MI@µÚ‰˜ðD#@»´ùüMºAÀtòÓ0\]¦À¤'WŸ¶LUÀ’ wDáõ?亮ÜŽ)ÀíižoÝÊì? 0@)‡èþ¿¢'P@šp5踫À¾/)r·-c@æj!0ôÐÀÞ„rÊùrÀG´¾‚æbƒ@¥´A±µ|qÀëååæŽ@f-šŽ’@¢<±ê\ÈÀi’ô0MI@ꡆO•Ý@~Ç~»g¯‡ÀИ1UÊ¥@4¥›OŽT@ÝIµKê(¦À¯à 'ääZÀcpAãCj@±îÁ²QÀÝr¯fèþb@i­£l±r@Ý.(<ZƒÀ$¸­óm€µÀÜJ¬_ ú£ÀÁEi°tq@5_Ï~¹ØŽÀñ:öÎÁŽáÀÈ )Ý=ÀµÚ‰˜ðD#@~Ç~»g¯‡ÀAcàã@ibQp{ÿ·@§áˆWŽØZ@ŒQ<Û[Àuò¿å©Q@EWâü1öbÀ.«‚„•ÀÕIÝ"°Í¡@m—Ä EŸÀ¥o̤YÛŒÀKż¨1ÀEŸb“ü`¬@£ÖÁÃf•Àõ5ðÖ¼¯áÀ»´ùüMºAÀИ1UÊ¥@ibQp{ÿ·@5êßÙB~×@ eÀ²xÀpñœ¼ Ù¬@ÒÔº $@pÀœÜq@¼ÆÊU®Q´@# \û&ožÀDJN©ýÓ@waÑn$sŽÀ:%¯¦ß½@ÚˆÂPDwÀÁŠÙ¼uÀÎF¸þë6†@?6]«m>@–* ËÖÕ[ÀCßSÀ"àÀ ¾¦Jø¦@ØO¾.€P@p­ã‚xîmÀ# \û&ožÀ±“[Æ™Ä@yaÑn$sŽ@DJN©ýÓ@؈ÂPDw@:%¯¦ß½@¦²}bI@É%¨n帡À£e‰²éHÀàu¾ª Òt@ï˽~" o@,O‘Ó[%âÀyOX`¯hÀhÍ2‡c†@PÂSÒù¶“À:5AjBœsÀU~,ž‚VžÀqûþ~½­€À‡`\oHÈÀ0ÞÊ3øè¡@•.·õ=9±À´ ß병“À†2¸·!á@\:öÓv¢@]š7p@Ö¸T´ÃÒT@_»ò$ÇÍÏÀ“(3˜–À¦ÀBÏÂ’­¥e@ç*býjG@©>À¼£p@S»ž3Y"R@­Ñí é@Gß²~4c@$Ž*žšsÀ" µ+¯ÉfÀqûþ~½­€Àö”ϰVbÀ0ÞÊ3øè¡@³!ivòÖÀ´ ß병“À¬aï;vÀ\:öÓv¢@úWoÿ>ÂÝ@IF%û ™Q@•ÊofS™6@Õ>õÖ¦¦À·Qò®Gó¸ÀU³¶¾3UG@Š42èH=)@MâŠ+pïQ@(Á׋3@P·ãG¹Oc@uZ¨-µD@ófÓÆgR@Tr"xi@86™DsZ@ôÁÖθ$@ck%£vn‘À¨øÒ2—‹@’ }_ÉÕ´À#>˜Ó’@Ó=±aöŸÀûs.™½{š@is$VhÂ@ªˆíž0¶À2B¾µƒhÀyî 0n¾^@žšZ¹·÷e@yª~òQ@"Dwšf¿8@¨Ë~qýVÀ¸ÕZß.@‘Õª9%u~@(Ô¸#+ýÀµTÓª±ª„@Ô7Øøh@–=Æ·$4À€Tr"xiÀófÓÆgR@€ôÁÖθ$À86™DsZ@]ïâ¸.µ@&ÒsÈ¿§ÀL&ó"^ž@0Ï÷ÊjÂÀ¶ÀŽwhJ”@›ì~`ìÿ´Àªˆíž0¶À‹|ÑÈÕ@=32@oïqùq7À»ἇHS@¸~¶í>@ϰô¦Q¹VÀ8v7(u@ÍAôÐö€@ )P‘ù„À¶¨¥-SŒ@s‰_ëõΫÀ-D€¢ŒÅ(À”₯`¤E@ÕñLàI{ ÀW¼Â;åôR@cÉUÒš”²ÀÚíîp½c@üUŒ4ж¨?ÄÇ"Óˆ'¼¿R§vësýÀ?ã“à· )Õ¿ñý.n^¡?¹Fê¤å¿¿2B¾µƒhÀ=32@öŸ:"Ò’»@üdHÊ GoÀ‰BÑ–}®Ž?ðZå­n{ ¿þ¬¸±1Å­?Ø%Â;ý¿¿Ð=›‚…U@³ÏÛ ѦÀµ‰×åN³f@žkó~ê ¹Àßž¾ˆ7Àµ]‡¤ÎJ@Ñ9rS-PÀÏTèÔ%d@OJ>i©‰0À×÷æ^N@yî 0n¾^@oïqùq7ÀüdHÊ GoÀ ´v‚ £Â@¿Y¤6À¬B&D c/@·–÷‚X<À‚ˆ¦]uN@i¶CǪ@ðPóÕ6¤‹@ÎþàC"äÀòN¢—èI­ÀpZ­P?ALïó¡Jij@ÓÄ÷Åþ¼>Áùn9™ä@`°%ÀË*ƒ@έ?cÉc@Tx¯&Ú´œ@’CÀ'¡ýáÀLïó¡Jij@ÖòçöK?AAg€­´Ë¤Àùa’ÍÓ¼>ÁKJØ(ô}Ï@å˹lpØ¥Àó!Û'rþ¨ÀÀѼGà”l”ÄÀæË¹lpØ¥@EýJÌ“r¤À®ÖnȰê‡@ªsŠ&4Wû?wÄd3[)ÀˆîL®bh@W‘ ?w„À5u‚Fè Q@ºÄðáô [À¥-O6¨ìBÀ§¢“š¹Á@å˹lpØ¥À>º9èj×@ÀѼó!Û'rþ¨ÀæË¹lpØ¥@Sæn$öÑÀãl’û|Ò”@²¬¨Î/¸ÀÓX)¾,À×øê"‰c0@—3;ƒ–ŽÀ»Þ¯‘ ¬©@Œ…âwívÀîd‘ÕT@ÓX)¾,@¦¾’JÀý ®;^@ôNàæ­K¶@É®æ´`v@9ÌìÔóþ?4Ÿkh›@±Î&á!«AÀWBdÕ9·À÷eÆ¢â?\&UKUeÀò„ɧYü|@ýæNþéåIÀtòÓ0\]¦À4¥›OŽT@ó!Û'rþ¨ÀÀѼP ­@ @b†2ç@\h4éq@Ž(Ž®¨ @7Ý´ãrŽƒÀ½(óü~(BÀn¬¡É9è?Ö™1…DfÀÙ¬™-¦¹G@ #ú0‘}ìÀOÌìÔóþ¿žÉ®æ´`v@¯Î&á!«A@4Ÿkh›@/¼ý€¾í@¡å!~«ÝÀ“¡üŽG@üæNþéåI@ñ„ɧYü|@¤'WŸ¶LUÀÝIµKê(¦ÀÀѼó!Û'rþ¨À @b†2ç@a üÚW­@hïî¡6A@/ÔԫʃÚ?ñ£è¹QÀ„¾'FÀ‡IJÀÛ ª @?O@Gà”l”ÄÀæË¹lpØ¥@‹$‰6h Å@¢8bªñ¾ªÀ­¥ZƒJyÀ<Ò;RŒ#…@?¥dž]F@£ãà)VÉ@`ûfIfÀÇDºÜJJÀæË¹lpØ¥@Sæn$öÑÀ¢8bªñ¾ªÀ×>ž\M&Ó@Ò»Îôæ#…@M—C®d«‘À0$9.•<@Ç_J×ý?#Ю[tSÀ‹xu`ÞUÀdkžpýR@f‘Þ òíE@EýJÌ“r¤Àãl’û|Ò”@Øx1_Rb´@ Bnªß¹ÀýcÖ•ÀþïHø®±@OvÆ"”Àã]íU)€†@ÝšŠé°;H@Ö|²÷û;@®ÖnȰê‡@²¬¨Î/¸À Bnªß¹ÀLjº?G×@!ñ»…ΰ@‰çV6ÌÀyËpÎÑŠ—@á–ƒã<©ÀòŠQ›»9Ø¿…ëÎÓ3 -@E6Ýo†tÀ‡šäS]ZÀžšZ¹·÷e@»ἇHS@²z-† c@És³Ð+@…ëÎÓ3 -@iƒ}l‚ÀbÜûµXÀåÍ g\ _Àyª~òQ@¸~¶í>@És³Ð+@#/0­Ð…@URöëóÇÀ]—µÀ^{M@¬ZÍöñÎ@< •O:À"ïnbžÊ@ˆï`ëý?ª'íƒ/g(À¼¦q–– %@¿îH’§NÀñEØð?tCßÓ ÀÓÄh#Å@<§/µ')Àîé®ìB@F4ˆýk1ÀÓ8’?tü?Ø·—Zú$Àú?³WгjÀ‡±ˆÅ„"@]š7p@IF%û ™Q@ªsŠ&4Wû?ÓX)¾,Àö^Y w1m@ìWN°‚:À#©<ÔqÀ ¼‹õ«SÀ˜“ö1GÀ¸Ò<}X!u@˜.»®SåVÀ±Ï•ªB9À¸zbÉÊ3¥¿I€W¦%GÀ-;0 %@ÚÁÅE@Àªb^6Ã6 ÀB1¿~õ5@ÁÌjú@@žnÖ•/@x º~˜7‡@jY,Ë"3a@*aD`Î#@5TÒâ]#AÀÅUÂY†J@«h- ê8@«´ÿ[)qÀƒÀêïŸó_Àò;¦qŽtÀûár(GÀ+ IÔËBf@†Úõ¥ÿ²¿@‘ÒsæyÀúÒ M1>@þ²³Ÿ/@²¥&k@jY,Ë"3a@É]ûì¿ö@ {5ÀJÑlR@= â´8@¸OP¶‹ù&@­Òek´ô_À¼{ÀMÀÁÕ~—P-GÀû‚?å‘YoÀ~Ûõ¥ÿ²?* IÔËBf@ØÝê1@‚ï) å?Æ,™@8¡À¶m/˜À2=¦ó>8@U‹°ØÖSq@z•l6© Ò¿‚xàfÉä@RÞA#ˆfÌÀC’BÜ”àÀ›Â¬Ï@¦&Ý»\´@:Q·"¬}À¡ÏQ?Ô4·À*,8Ù 'ŒÀx(3HŸ|g@S@!|²K6@bÛ{WÑDq@—þ|ÁÂP@ÙFctÀ—EV½8ãYÀ$þ%yª´·À‡?UÛ ?@$>;ìñ@ùeo½¡À9•l6© Ò?U‹°ØÖSq@RÞA#ˆfÌÀ›õaí~ó@jj•áFÍ@P·]“3¼ñÀ6Q·"¬}@¦&Ý»\´@ã7Æ=á’À•eamÎú¯ÀS@!|²K6Àx(3HŸ|g@–þ|ÁÂPÀbÛ{WÑDq@ùþbzZÀÒ$ÍÓr¦„À³mÀ£¤@ÀWõ=­·ÀeZ>Ÿo&—@»êæPïô\@Y×À»à@Ån9™ä@:%¯¦ß½@؈ÂPDw@ÓÄ÷Åþ¼>ÁAg€­´Ë¤Àñà˜À¢ŒqÄkÂÀ×Pƒû ©>A+Ü ë"ß©ÀPHX‰)¬âÀÆÛÝf脦Àùzï M+Ü@ôÉÆÏ݈£@΃7ηá¢@ ±‡âáñh@›ê•« áÜÀÑ¡²®œBÆ@Åx¼8bÀwzìÚ‡:@»êæPïô\ÀeZ>Ÿo&—@Æn9™äÀY×À»à@ÚˆÂPDwÀ:%¯¦ß½@ùn9™ä@ùa’ÍÓ¼>Á€p'w´ÀÓCáà@+Ü ë"ß©À›¬2áÄ>AGb™Ž«z¥@‡«ôZQCáÀõÉÆÏ݈£Àùzï M+Ü@ ±‡âáñhÀ΃7ηá¢@BE0/- ¾@ÏØ Ó¢ðÀ³{Ü™q:@W¼Zè¼À{­?òzé@£+„jµ@ Ï0î-;˜@MÙó$‹bc@íK˜n÷š@[ùAºB@&©×Àh}[kÌoÀPHX‰)¬âÀGb™Ž«z¥@Ó¯¢câd'Aã6Rˆ<_à@s&†v9&ÁDE¼Ô^äÀH«wq°ÞÀ·¯€Q_@§¶ß©,¦¶@ýÐÊ%*ƒ@\ªÊÉác@ÇC4C©Ï/@ŽØ™¾Z f@&Ç È0’ @èc?Û{ÀAÊòBO‡ÓÀÆÛÝf脦À‡«ôZQCáÀã6Rˆ<_à@UDRý#SA …@y“åáÀRC[À™Á U0 `Ês@˜“êöMàÀšr᪖À‹ •àÊg@r”ÿ^sA@5—ÆžO]"ÀyüQ!^p@"ucLÀ5u‚Fè Q@Œ…âwívÀOvÆ"”ÀyËpÎÑŠ—@³m»z„#s@¼¸TÀ)üíÿ)‘@ÿ­çÑMLÀc9ˆ*ây@RÒBS,hÀ“ý[R¼tÀd‘“þy•À5—ÆžO]"@r”ÿ^sA@€"ucL@yüQ!^p@ºÄðáô [Àîd‘ÕT@ã]íU)€†@á–ƒã<©ÀO™“Qáþ}À>å?Æ,™@ÿ­çÑMLÀFbµD½‘@RÒBS,h@c9ˆ*ây@¾r[1cóÀŽzѽÀ´4-Ù*Î@Êü#©‡`Àùzï M+Ü@õÉÆÏ݈£Às&†v9&Á …@y“åáÀ‡§ˆtÁ‚&A~®ÖI’Ìæ@3òsõïØ@Ñ€ 1fZÀQZ¶9î¾ÀÞÿlôÑqÃÀÆü#©‡`@µ4-Ù*Î@ôÉÆÏ݈£@ùzï M+Ü@DE¼Ô^äÀRC[À™Á~®ÖI’Ìæ@•e@É{AØ€ 1fZ@3òsõïØ@ßWÄÓœzt@S¨Ç9[W“Àù¢†ÀÌF(¤@]S õ—Jm@Ãèd5C`ŒÀÉ\Þ.Hd@Lq€\${ÀO*°fÙg‹À¼™4oâ©@ê²ÊO¼Iž@ò¯OnùºÀ`®¬øp™ƒÀíHçp¥ü¢@Lq€\${Àê‡lE)’@C’BÜ”àÀjj•áFÍ@`9¥ôÅLã@<½4„ŠãÍÀ¿ÿä©?þ¹ÀAQW‰¼Î{@¥é› 0ƒ@¶¾¿KõJ`Àá7*µÌ}@ÜѸ\*(@›Â¬Ï@P·]“3¼ñÀ<½4„ŠãÍÀå¯q]öò@^bý$ {…ÀüÒ—w§ø)@ÒC/ZÄ£t@Ëáï¬Û¹M@å¥Рâu@Å«·»À*,8Ù 'ŒÀ•eamÎú¯Àg³áÉPyƒ@«x`FÚÆÄ@?—w§ø)À›Ÿ\*›Ýk@Êáï¬Û¹MÀÒC/ZÄ£t@ðE›ƒFõÀ5Ì£ÇÔÀ,ƒÐíÌc@ú†±(ÎÝ?¹0ßq_òLÀ,lP i(ÀpvbîÉqÀí,}61Àx(3HŸ|g@S@!|²K6À›Ÿ\*›Ýk@?—w§ø)À†¿mp r@S>ü -M@ ÀuŽM@w½9?”~@Û‰}?MÀYJ‚¨ˆZ€À"‡±(ÎÝ¿,ƒÐíÌc@²œ_®ÔS(À”AÖ_¤­BÀ' K(0À‹RÉ™ˆlÀS@!|²K6@x(3HŸ|g@>—w§ø)@›Ÿ\*›Ýk@S>ü -M@Ñ0a$d@x½9?”~À ÀuŽM@_»ò$ÇÍÏÀÕ>õÖ¦¦À#©<ÔqÀ˜.»®SåVÀði…4.Ð@t†ß‡ó]§@“(3˜–À¦À·Qò®Gó¸À ¼‹õ«SÀ±Ï•ªB9Àt†ß‡ó]§@`ÆJ:¤ ¹@ŽpE3Rà@”¦ÁëWAr@ÁŠÙ¼uÀ¦²}bI@ž<=¥CW´@¼:žaРÀ'a!GeˆÀùÄ2ª²@Úaîd(ŒU@k@_qoÀHdF_}´ÀÅÿݲŽ@–GÉRn›r@êÑÁ$© T@ÎF¸þë6†@É%¨n帡À¼:žaРÀ“ºšvFÚË@"ÛïƒìøF@»ÀII·Ê¥ÀcQÝpÀ¾ÇJ¥·„‡@OؾÅ/.•@—Ɔ¯TÃÀzï0 k–Àqk´yv=cÀ†™S7œÁ—À‡Jýa«´bÀ‡#1WØz”@¿òD;âÀ?6]«m>@£e‰²éHÀ΃7ηá¢@ ±‡âáñhÀH«wq°ÞÀ U0 `Ês@3òsõïØ@Ø€ 1fZ@'a!GeˆÀ"ÛïƒìøF@*%S*þ¶@îCöiBVS@7èæF~­$@,‹BFÁBÀqk´yv=cÀjë–”7ƒ0À‡Jýa«´bÀÒ†56u-À$¿òD;â@†#1WØz”@–* ËÖÕ[Ààu¾ª Òt@ ±‡âáñh@΃7ηá¢@·¯€Q_@˜“êöMàÀÑ€ 1fZÀ3òsõïØ@ùÄ2ª²@»ÀII·Ê¥ÀîCöiBVS@>-|dм³@K}¡n|À]›€zÉ™@CßSÀ"àÀï˽~" o@¨¼\aAâÁ?ªNãïͦÀ›ê•« áÜÀBE0/- ¾@Úaîd(ŒU@cQÝpÀTHšQ<³An9*4³À ¾¦Jø¦@,O‘Ó[%âÀÏÌ^âÕèÁÀkÇ™2qÁÑ¡²®œBÆ@ÏØ Ó¢ðÀk@_qoÀ¾ÇJ¥·„‡@n9*4³À+¬Lˆ8šA‰X ˜xÀ ßà”Ædb@±#toéù‰@C B¤;Pl@"Dwšf¿8@ϰô¦Q¹VÀ4ƒbP:a¯@Æ‹³Ä!q@Îàg¿.÷´À$J(ØõÀ.:¼Ö”v—@9€¤ù:U|@¼C2 b@ éôÆ—À ù_%mMl@¥™OTÙN@¨Ë~qýVÀ8v7(u@Æ‹³Ä!q@.JA„î¨@n?uúÏŽÀ˜¢*þk À¢mõ{y@hçäüÅÞ^@‹±7ÐCÁ^>`ÏÀm‘CWd”@ jÏÁVÀHϦåhØÀ¨§µÈœâÀ¢8ûõÂ@ü`b£ôcjÀc9ˆ*ây@RÒBS,h@ööZÙA¼žUÈšê@†“PÓ½ßÀ‰ßrfúÓ÷À jÏÁV@m‘CWd”@%4ëÁ¨xÕÀ*¾©à3_àÀab£ôcj@¢8ûõÂ@RÒBS,hÀc9ˆ*ây@¼žUÈšê@Ãþ’wæý@VôªóÕ#:@ã4ܤ^ÇMÀwÉ+tdøQ@¡ê#b”afÀ2Ù‘÷^2@a v`ÞPÀ¸ÕZß.@ÍAôÐö€@‰BÑ–}®Ž?¿Y¤6À*aD`Î#@ {5Àæ50–Vl@Ë>žµ@kÀUƒÍ‡qÀdÉHÿ¯ÀUÀVú]Ö¹hUÀpžD9X@íä UµLÀÔ‡dŒþ_@m^©NcÀ%©©?Ü x@KïYœÝ¼CÀ4C8þ©b@‘Õª9%u~@ )P‘ù„ÀðZå­n{ ¿¬B&D c/@5TÒâ]#AÀJÑlR@Ë>žµ@kÀå6«e>¨î‰À±kuVEÀhÇ]"Ù`@—EV½8ãYÀÒ$ÍÓr¦„À SÇkoC@¨ÓÙ‹¶.•@•×û™žZ@Xã–. ð¿VõÑãÀsÀRÒwðzkEÀ˜“ö1GÀ¸zbÉÊ3¥¿ãÑ‘O\&p@v!æèðàE@`ã–. ð?–×û™žZ@1:]òaFÀÐRæ8,ømÀ¸Òi@kA\zñw>@?€«_,@Áã ÷GQ@yÊ©±½@@Œ‚lŒ …GÀŠÄÓØ.ÕJÀƒÀêïŸó_À¼{ÀMÀ<˜Ý!¿_RÀîvìø[ÀdÓ°™ü>i@¨Òö•i@Xˆ­”Ö`,@0UVîZm@`„¤ @@b ²èð-@$þ%yª´·À³mÀ£¤@Àá7*µÌ}@àѸ\*(À¤ñ‰¹q¹ªÀ{u}?;‹e@'GüTW~@%î¡Ï ]À‚G¤Ê†©À@ÏA±ìö@‡?UÛ ?@Wõ=­·ÀÜѸ\*(@à7*µÌ}@:÷³»JdÀ[\¹æ «À%î¡Ï ]@'GüTW~@ÏA±ìö@è9©ðÀ@g<›]çwD@Çñ1Ù0ùUÀ9½³FÁbÀ=>óÔ¼•@)âõjÒC@»ÎøKƒaÀiÝD/uC™ÀM[£÷Æ>–@’ wDáõ?¯à 'ääZÀ§áˆWŽØZ@ eÀ²xÀ¨Aw¢'~˜@^¢tÒŠ ÀÅŠdˆ$@c’B·Ú‡5Àê¿èwÀd¯ˆΪ‰@ãicÈ•@ôŒ@ Š?ÉÀ†Ö%'wÀÎÂITôt”@dßnZG¼£@2". LÌÀ亮ÜŽ)ÀcpAãCj@ŒQ<Û[Àpñœ¼ Ù¬@^¢tÒŠ Àv.#°ýÓ@4ã [ªmWÀWYi„j&i@ðX1·N@e‡Âv%@@`ûfIfÀ#Ю[tSÀyä bRpÀMLÉZ;CÀ n=w@¢[U@f‡Âv%@ÀðX1·N@ÇDºÜJJÀ‹xu`ÞUÀ¥t ¯”—Ù?¦dÜA.ÈdÀ¢[U@zý (1Wh@‹p" $¦—À‹¾í[xq@¨Æoë@Ä6 o"%k@€¦üÓsÀ‰üS× ¿U@Ü\©à‘÷©@9'f±ÀÛÁ+´îuªÀÆv‘ß &iÀ€l«#‹@p¸Qûg@œÿN•êy@VÀïH ²ÀÄ6 o"%kÀ¨Æoë@åþõŠñb@DuÐÄp–À9'f±ÀÛÆ†"Ã@¤bày@B®×*# ¶Àp¸QûgÀ€l«#‹@É›"gùÀ«4å:A˜†ífyàÁòª&RzÀJ£Š¼Ëž@zt`ÏÙ‚$À°¹âOÐ8ƒ@'Ü©¡0@AF÷_Ó™{@å0†úw}GÀ0½µ‚¬ÀY\‰…‘ÜC@žªOðù@3[è¤NÃÀ3[è¤NÃ@ÙÉ­!ñúIÁÙÍò$òaHAgŽFó®ÿ°@‡t`ÏÙ‚$@J£Š¼Ëž@)Ü©¡0À°¹âOÐ8ƒ@å0†úw}G@AF÷_Ó™{@mÿo¢MDÀ·Cr¬À3[è¤NÃÀkÀíÝ A§ÿ8R”ö@…ÓÂ®Ž»À|/1Gµy\Á‰ófRC@2AFÏ*¤çé@ãQ3+œ+ÀfvNÅ dÀç˜a FÎB@yäî @ÔD¬%&Àà˜\«n9@¬œÂ&câ)ÀL-‰\Aö©wÄ$2ÁˆÓÂ®Ž»@¦ÿ8R”ö@û Ó5 2A«ÐµcÁ¥pú;*ÀM,LPDDE@Π t—÷A@mêé‹ß‡pÀƒ¯†V’ä$À•S~®A@O¼žÂ_þ(ÀNn±ìûïC@ö©wÄ$2ÁfÓbAÅx¼8bÀ³{Ü™q:@Þ?í?”Í@GÓîå+NÀųy¢hÝÀ²Ú”7ó»§À¸Aøç…Í@oúÙR @wzìÚ‡:@W¼Zè¼ÀGÓîå+NÀµÚ,ZzÍ@ñn{{å@ögïnFÖÀoúÙR À¹Aøç…Í@õÊà“Ò¢@míÑ;=@ųy¢hÝÀñn{{å@ÌÓãÿ ú@µQvT“Ž@š`}yMóÀ¦‹GS ÀQíÑ;=ÀõÊà“Ò¢@²Ú”7ó»§ÀögïnFÖÀµQvT“Ž@µ-EŠVHø@ 7x8,Q @6;D;MóÀk¤hc×5@[%ýñVLÀ%žÛG¶0r@ /*ŽPÀ‘¡`Åø€@•Áë&H_Àâ¨?¡•ÀAƒÍã“Þs@]­2Ÿ@çdl+†ÀGÍQ~‹¿–À'œÞJ„@0‹ƒ–``ÀÍÜdWëÊt@ /*ŽP@&žÛG¶0r@•Áë&H_@’¡`Åø€@ÞÍLR‚€'@’îeÑ3žÀçdl+†ÀˆXjþËZµ@çh÷'„@ÍÄ4«aZ²À\¯ÿ›´ÌqÀy7ÿ¬À“¨ dNO@`ÐöÖOòC@èÙõKúaV@œŽäŠL@YïÞèÝÊ9@yM1ÚRÀGÍQ~‹¿–Àçh÷'„@Þžhúj˜@˜[ D˜„À:¹¾bð›Y@“Ê\DÒJÀ ë4² 8@Þš)à¾.@_”mô@A@Ì÷@mÿ5@YYæ£~ìbÀJä1sé©{@'œÞJ„@ÍÄ4«aZ²À˜[ D˜„À°iS­!£²@¼Â?ôM¾Àfˆ/ZIùgÀ8]EhÇ›À"p¹ÂÞbÀx£”óߨ›@Ï×…(­R @x£”óߨ›@Ï×…(­R @îÍM1*@ŠGÈ#55ÀÝù”½á¢,@äkW÷BÀ®µw"Y ŸÀÂd{Ö¹d@©á9„âwk@Å^¹B»õ©?3Qg'%@›Á‰|‰<À£lWtoú@cŸ Æ^³,À–|¹]±%@]ˆ×ùÌ}ÀñÌN©y@Ó‡iü[L¡¿7ŽD*»r@±O{òžºÕ?Ý"‘»þ’pÀK(‡ÂµeÀYN¿%Û[@ z{Û@Ì]œÿóÀ+ ðÜœóÚÀQð.pq ó@BE¬œ€@¹Lã—ƒóù¿ëårŸ†§ÀÑP*^壉ÀÎàg¿.÷´Àn?uúÏŽÀN?TòbéË@ egÏT¨@Îë ,¹À¬k;mÇ”ÀNLã—ƒóù?BE¬œ€@3ÚC²s–ŠÀ͉PŽÛúlÀ$J(ØõÀ˜¢*þk À egÏT¨@ò™Î·|°@é°Àéfœ“Àai§ Áã¢À¶ÍHc›CÀià ²cÕbÀ®i%ý)@K›b-Mžq@.:¼Ö”v—@¢mõ{y@Îë ,¹Àé°Àéfœ“ÀCuoØol±@Âs©-W†@ià ²cÕbÀDÆÆ“²‹DÀ*·Ñ„s@QúÛfFU@9€¤ù:U|@hçäüÅÞ^@¬k;mÇ”Àai§ Áã¢ÀÂs©-W†@5jUêm”¡@ò;¦qŽtÀÁÕ~—P-GÀkA\zñw>@Xˆ­”Ö`,@HÈ;Û]¨@ñ·ïpà|À´¬ týg¦À —SS…}@ÒÙ¼áîG@Z—‘ÈC6@ûár(GÀû‚?å‘YoÀ?€«_,@0UVîZm@ñ·ïpà|Ànª5¼q®@Ï£„Èç}}@×§ÍðÞ¬À¦Tw¢I6@^0Hç»$@éAßVï:@V ¯XêLÀ¬iôá,:@*ôW-² WÀç|r‹ígÀ¯°ä4ò‚@íižoÝÊì?±îÁ²QÀuò¿å©Q@ÒÔº $@pÀ+ IÔËBf@~Ûõ¥ÿ²?ÅŠdˆ$@4ã [ªmWÀ´¬ týg¦ÀÏ£„Èç}}@d+ k@÷¢ˆ^j¥A@­eD!p@ˆx#ûEµÀT¦•(Ö`ÀÇlóµ¢@Ú;4ñÄq@ &ÐiC@)ÿL š–@Âè”ãèeÀ%eàéÖxÀìb# ÈÇS@¢–<øT@ÈÖ¾ê‡Ày^QbŒI@s¿-_k‚ @5˜Y¦âM@âƒ<|o#@WÿG4©A@°\ÇŽh*_ÕÀ+?Ú€¡_cÀÚ$•~»­MÀÄl ÁsrFÀ›—P9>9†@kF€ÝÀ_@ÐÙUaÀWu4¶É6À%dg{?FÀ¿®4 1ÀÑuy3zEj@kF€ÝÀ_@&‰ o¥!q@y¹~Ì¡TÀnncºÊ1}ÀyˆJ§ ïU@%öíTÍ@@%eàéÖxÀÄl ÁsrF@ÐÙUaÀy¹~Ì¡TÀTé 9ͱŠ@Ëj!2.@nbæAÀ˜gøÜ.j@jc1ª‰FÀÐE.fAq@âj¬)¾2@ñì„`¶@ìb# ÈÇS@s¨úà–ÀWu4¶É6ÀnncºÊ1}ÀËj!2.@¿É8•w…–@†ä ƒw1³À‚\£ÖhDÀ‘6úY…ž@b¸²ÀbhÀ7¯²€@(´I>h%ý?­3ÂW-OÇÀ <ý-)GÀ|]Ò$ª@{öŠÔçîÀ4yÑ6*x@ ~Ê YLÀ«Ú7oéÀÉìȶ¢¤À?O Ü÷“@´äNF~*@+{5‡â@ƒ{øû§@%xSêþ£‹À°FâNÒ@27âO<@툲ÕýŒj@åÃÓ}>h@ÇRjK–@â³I>h%ý¿7¯²€@ÚcÄž Àןœ4‡¾ÀzöŠÔçî@|]Ò$ª@Ÿ~Ê YL@4yÑ6*x@lDC›mÿ“À$,òTOãÀ½äNF~*À?O Ü÷“@ƒ{øû§@éz|/j¡Ô@Ò_Ùê,&·ÀÄŸ¾¥Q¬xÀãí`/2†@·üÆwTTf@ÛÁ+´îuªÀ¤bày@ÇÝ%É@"ylö”ÔjÀã"Oذ°ÀfMBr=@±Œ æ¨%aÀs…”­¸C@‘› ({hP@$§ÊºL…´Àf®åëÍb@‡KfVêB@õMꟹOÀD1¶ó•}@Æv‘ß &iÀB®×*# ¶À"ylö”ÔjÀw~e‘e|Ì@“¢£›¸y@ífòût´ÀVh*Ã.nÀǼ5Øz“@Bøy«Íið¿€l«#‹@p¸QûgÀã"Oذ°À“¢£›¸y@-h ïE¾¢@‡c6¬×jÀ­R¸(í«¿üð¦Š)nÀ‹øy«Íið?5Øz“@p¸Qûg@€l«#‹@fMBr=@ífòût´À‡c6¬×jÀc‘,iFª@+ÛcØ(î @F”i¨@Œà¸—r"ÀÅfm¶éc@ËHw×.ØÍ@xúQñEƒÀÝ«ÊÕ[ªÕÀêè/DY‚@· ÖG6‹²@Bgѳ=cÀêE”i¨À+ÛcØ(î @Bgѳ=c@ŒAá…ŤÀxúQñEƒ@ÌHw×.ØÍ@1뽇î‚ÀO>Žh*_ÕÀBgѳ=cÀ¸”ö ‹¼@` „@Ñ‹˜ü±&QÀüËõRÀÀF¥ØÑH°^À*oÑ„­ €@Ðõë®§Nâ¿)xòÿâë´ÀÇ¡S®áÀGÂí‡È@m­ÜNÙ‡@Ñ‹˜ü±&Q@` „@:°æBpÀsiŠðɳ¹À½õë®§Nâ?*oÑ„­ €@Ç¡S®áÀ¼Œ|‘NÀm­ÜNÙ‡@è ‚Lzvµ@bK· »Ah•„í  Ï@„8TD!»äÀ¶)MGr¢©ÀQüÁk™<Û@¶)MGr¢©@MÚÈω!AÏM¾U ß@€y Æ&Áp#r ¸çÀh•„í  Ï@j;\° –@ܲ)*]ïÀ5¡#êÀšÕÀŒ"çÁ ]ï@„ p§Á„½@ÏM¾U ß@áÅ{Ì Añ»}¦¸çÀ“¶™õjÁAK„zÇ@2v‹ŠñÇ@?— ¼üê2Áã0 ·ŒÁ‚a äH ã@]º¬KƱ@£+3'+SA¶ï(ìˆBô@{ §ŽÊÝ@¹¸ §Wɪ@iæ÷álÅAƒ•‡rºðÓ@€y Æ&Áñ»}¦¸çÀ8Ǩî3AaÝr´Äó@ß`$ÓâT@†ëâU¨@'óËiVöÀ5ö¬õ mÁ‰|'ON«@˜’E—غx@“Û9žá@,öi 0¼@ÆJ&±Ï†¤@_õÀ¼£p@MâŠ+pïQ@ßz,[ž”@³#^a@Öëˆ×Z}Õ@/Äù wz@ÙR_3ô½ÐÀŒ6ÖkIÀÑmBA˜ƒ @‰º…Rî¸@UN5þ°“À»®Þžõd¬ÀS»ž3Y"R@(Á׋3@d´d5a@çý©R]B@/Äù wz@ÙøYXÅÔ@ÂcÚXI@øxñŒÒ½ÐÀÏ”žQÿ@)Å‘pPc@IôGƒS£@Ü:‚áfIÀ;æz ¹õœÀx½ÙR_3ô½ÐÀÂcÚXI@´ŽÓí"Ð@8B®È³?Ù:‚áfI@IôGƒS£@ˆ𽣿WÞš1õœÀŒ6ÖkIÀøxñŒÒ½ÐÀ8B®È³?¬¤  #Ð@›5È^ÕÀ6ÚËè`…°À­Ñí é@P·ãG¹Oc@²i“ÃÜ{•@*êwD=w@ÑmBA˜ƒ @Ï”žQÿ@^‰eÕNgÑ@m{‡^n¨@àC\wÕx°ÀÃK”LŒ„¼ÀGß²~4c@uZ¨-µD@ë}r“Jw@ž¶g”ðX@‰º…Rî¸@)Å‘pPc@m{‡^n¨@¸‰œoµ\»@]5…@š”^n@@·Ú°:QÀê=^+p‡hÀx>'eeÀa;²;ÙêSÀš”^n@|^|ãnNq@ÌÊ£]Y@ÄLÆ$ðälÀß‹ Àoq@J&WÝe·…À‹ ³FÓQ@`’n^pÀ(Ô¸#+ýÀ¶¨¥-SŒ@þ¬¸±1Å­?·–÷‚X<ÀUƒÍ‡qÀ½C…1SÀâ‰bÞ0›@ŸcÚ×Ôy@²sÊ@ðò¡=";Àì©OAkÀ©¶/ÛÁßj–@µTÓª±ª„@s‰_ëõΫÀØ%Â;ý¿¿‚ˆ¦]uN@dÉHÿ¯ÀUÀ0)PzžcÀŸcÚ×Ôy@Ñ×aU@Iê¯0À—È^'M@Ô7Øøh@-D€¢ŒÅ(À@‘ÒsæyÀØÝê1@Vú]Ö¹hUÀ+/q\føV@Áã ÷GQ@`„¤ @@ÒÙ¼áîG@¦Tw¢I6@²sÊ@Iê¯0Àý¨Û:Œw@m?ÆVÇ`À–=Æ·$4À”₯`¤E@úÒ M1>@‚ï) 'wª©ø@ç0†8²;‡À`ò¿пãÀ"J–¥s™ÀÆC±ÄQÆ@–‹{fòÏ@’ÒATÐAƒ@ˆŠt¬s•@i/¦cÇÊaÀ2èæÛÊÃÀŠ6øD*†ÀŽo¨V·ÇÀÖØ"—yó?¸æ+|¼È"Àn¬¡É9è?‡IJÀVlc‡£ö?U™¦ùú,ÀÏýÖºƒ‘æÀç0†8²;‡ÀtO ñ”ã@bô¹ì‘@×s#BÀmI=ºàöÀ‘ÒATÐAƒÀ–‹{fòÏ@i/¦cÇÊa@ˆŠt¬s•@cð¬|€À±Ù³OŸ¨À|HÇváâ÷@áÄði*#Àl¹á£9ëR@Ö™1…DfÀÛ ª @?O@_O\Í&À-ñ/`/0]@Æýÿû"QWÀ`ò¿пãÀbô¹ì‘@O ˆ³m˜Ù@=M]o‡§ÁÛõ7Us‘±Ñãõ9Oew‰•¡¿Ýçñû/Yk}§Ñé-a•¶ê(2FZ€¦°ºÌÞô 6b ¨°¼ÈÒÜäìö,Xn„оì&8Phr|¢Èâü  6 X l € Š ” ® È Ý ò  : X v ’ ® º Æ Ø ê þ  J ‚ ’ ¢ ´ Æ ö & : N R V h z ¦ ¸ Ê ê   D j ˆ ¦ ² ¾ Î Þ ö ,:JZjzŒž¦®¼ÊÒÚâê,n¬ê6‚Ò"nºî"8NTZ¢ê8†¾ö2ntzÄZ¦ì2zÂb ÞüHvÀ N’¨¾Òæ<^¨òn¾ÜúBd†’ž¨²Öú>z¶Ôò$VˆºFb~Žž°ÂÒâæêþ  " . : H V ` j r z € † š ® Ó ø !!!$!4!D!J!P!†!¼!Ê!Ø!è!ø!"4"H"\"w"’"¨"¾"Ý"ü" ##(#4#L#d#l#t#Œ#¤#´#Ä#Ð#Ü#ä#ì#$$1$J$T$^$z$–$·$Ø$Þ$ä$ì$ô$ü$%% %4%H%N%T%^%h%p%x%„%%–%œ%¤%¬%°%´%Ä%Ô%Ü%ä%î%ø%&&&&8&B&L&T&\&d&l&‚&˜&¢&¬&´&¼&Æ&Ð&à&ð&''&'8'R'l't'|'†''˜' '¨'°'Ê'ä'ò'(( (( (*(4(F(X(^(d(n(x(€(ˆ(Ž(”(ª(À(Ê(Ô(ê()),)6)@)`)€)Œ)˜)¢)¬)È)ä)î)ø)*$***0*:*D*Z*p*~*Œ*–* *ª*´*¾*È*Þ*ô*ú*+++++6+P+p++œ+¨+°+¸+Â+Ì+Ü+ì+,, ,(,0,8,@,H,T,`,h,p,~,Œ,œ,¬,´,¼,Ê,Ø,ê,ü,- -0-@-F-L-V-`-j-t-|-„-œ-´-¾-È-Ü-ð-ø-. ...$.4.D.N.X.`.h.|..š.¤.º.Ð.â.ô. / />/\//¢/¨/®/¶/¾/Ô/ê/ô/þ/0:0O0d0j0p0„0˜0¬0À0Ê0Ô0Ú0à0è0ð0ú011 1(101>1L1^1p1€11³1Ö1â1î12"2,262@2J2V2b2|2–2¡2¬2Ã2Ú23.363>3F3N3`3r3~3Š3’3š3¦3²3º3Â3Ô3æ3ð3ú34*444>4F4N4d4z4†4’4˜4ž4¤4ª4¼4Î4è45 5>5T5j5t5~5’5¦5°5º5Ê5Ú5î566&646B6P6^6t6Š6–6¢6°6¾6Ð6â6è6î67777*767D7R7Z7b7p7~7„7Š7–7¢7°7¾7Ð7â7î7ú78 888.8>8H8R8^8j8x8†8’8ž8´8Ê8Ô8Þ8ä8ê899,9B9\9v9€9Š9”9ž9´9Ê9à9ö9: :::(:6:H:Z:b:j:p:v:Š:ž:°:Â:Ö:ê:ü:;";6;<;B;R;b;l;v;†;–;¦;¶;Ê;Þ;ö;<<<(<2<B<R<`<n<ˆ<¢<¨<®<¼<Ê<Ò<Ú<â<ê<=="=*=D=^=p=‚==ž=¦=®=¸=Â=Ö=ê=þ=>>">6>J>R>Z>f>r>„>–>°>Ê>Ö>â>ð>þ>???"?2?B?Z?r?~?Š?”?ž?®?¾?Ì?Ú?ð?@ @@&@:@D@N@X@b@n@z@†@’@š@¢@°@¾@Â@Æ@Ò@Þ@ì@ú@AA"A.A6A>AJAVAdArA~AŠA¤A¾AÌAÚAêAúABBB.BEDEJETE^EnE~E’E¦EÀEÚEæEòEüEFFF*F6F>FFFTFbFpF~F†FŽFžF®F¾FÎFâFöFþFG GG$G6GDGRG`GnGŠG¦GÀGÚGâGêGðGöG H"H*H2H:HBHJHRHbHrHxH~HH¢H´HÆHÒHÞHðHI III"I(I.I@IRI\IfI|I’II¨I´IÀIÆIÌIÖIàIîIüIJJJ$J,J4J>JHJRJ\JdJlJvJ€JŽJœJªJ¸JÀJÈJÒJÜJîJKKKK$K2K@KNK\KjKxK‚KŒK”KœK¤K¬K¸KÄKÎKØKæKôKLL"L4LHL\LdLlL|LŒL–L L¬L¸LÒLìLöLMM M(M0M@MPMVM\MfMpMˆM M¬M¸MÈMØMèMøMN NNN6NPNXN`NfNlNŠN¨N²N¼NÐNäNìNôNO OOOO$O8OLOlOŒO˜O¤O¬O´OÂOÐOÚOäOêOðOúOPP,P`L`^`p``°`Ä`Ø`à`è`ö`aaa(a8aXaxa~a„aŽa˜aªa¼aàab bb0bLbZbhbpbxbˆb˜b¢b¬b¼bÌbÜbìbcc"c0c>cLcVc`chcpczc„cŠccšc¤c°c¼cÆcÐcâcôcdd&d8d@dHdVdddxdŒd˜d¤d®d¸dÂdÌdÔdÜdîdee,e8eDeVehenete–e¸eÀeÈeÐeØeêeüeff$f8fBfLf^fpf€ff f°fÄfØfg4g^gˆg”g g®g¼gÌgÜgòghhh8hTh~h¨h¶hÄhÐhÜhôh ii$iDidiri€i–i¬iÌiìiöij jj&j8j@jHjjjŒj¾jðjk8k@kHkTk`klkxk‚kŒk–k k²kÄkÊkÐkäkøkll"l4l‹L‹Z‹b‹j‹p‹v‹„‹’‹–‹š‹°‹Æ‹Ð‹Ú‹ð‹ŒŒ.Œ:ŒFŒ^ŒvŒ„Œ’Œ Œ®Œ´ŒºŒÌŒÞŒäŒêŒöŒ &0:FR^jpvަ¸ÊÖâèîúŽŽ6ŽRŽnŽtŽzŽˆŽ–ŽºŽÞŽäŽêŽúŽ .BP^fn~Žš¦²¾ÌÚæò"*2:@FVfxВ𠦶ÆÔâø‘&‘>‘R‘f‘v‘†‘”‘¢‘¶‘Ê‘â‘ú‘ ’’&’.’>’N’f’~’ˆ’’’œ’¦’®’¶’Ø’ú’ ““"“*“4“>“R“f“|“’“¢“²““ғޓê“ò“ú“”””*”<”N”X”b”r”‚”ˆ”ޔ𔦔º”Δؔâ”î”ú”••$•6•<•B•L•V•d•r•‚•’•𕢕ª•²•Æ•Ú•à•æ•ò•þ•––"–2–:–B–N–Z–j–z–ˆ–––ž–¦–®–¶–Â–Î–Ú–æ– —.—D—Z—j—z—„—Ž—˜—¢—¬—¶—À—Ê—Ü—î—˜˜,˜>˜F˜N˜V˜^˜j˜v˜€˜Š˜–˜¢˜¶˜Ê˜Ø˜æ˜ð˜ú˜™™™"™,™6™H™Z™f™r™z™‚™Œ™–™œ™¢™²™Â™Ð™Þ™æ™î™ š&š0š:šNšbšžŸ ¡¬­²³´µÌÍÎÏØÙâã*+RS !,-TUžŸ ¡¬­²³´µÌÍÎÏØÙâã*+RS !,-TUVW”•¨©ª«êëtu()VW”•¨©ª«êëtu()¦§VW~PQØÙúûVW¦§VW~PQØÙúûVW¼½˜™¼½˜™ <=ŒÔÕ¬­ÐÑRS"#./0145@AXY <=ŒÔÕ¬­ÐÑRS"#./0145@AXY  $%ÊËJKÎÏâãèéî﨩üý  $%ÊËJKÎÏâãèéî﨩üý TUlm<=>?z{¨©ÊËèé TUlm<=>?z{¨©ÊËèé°±²³ÜÝÞßþÿ>?~ÜÝhijk¼½ôõ°±²³ÜÝÞßþÿ>?~ÜÝhijk¼½ôõ@A67†‡TUno@ABCDEno„…þÿ@A67†‡TUno@ABCDEno„…þÿ)*-.tu !45€¼½&'&'ÂÃÄÅÆÇ*+fg)*-.tu !45€¼½&'&'ÂÃÄÅÆÇ*+fgHI !š›  BCòóHI !š›  BCòó  ¨©¼½ÎÏÐÑòó*+bc‚ƒˆ‰ÌÍÜÝÞß  ¨©¼½ÎÏÐÑòó*+bc‚ƒˆ‰ÌÍÜÝÞßjkDE˜™âã ^_z{’“ìíôõjkDE˜™âã ^_z{’“ìíôõ01ÆÇ@A˜™¶·¸¹. / 0 1 01ÆÇ@A˜™¶·¸¹. / 0 1 ¨©º»¼½ÐÑâ㨩º»¼½ÐÑâã z{  JKfg¨©ÎÏÐÑîïðñ67Š‹ÌÍ z{  JKfg¨©ÎÏÐÑîïðñ67Š‹ÌÍ!"Èɶ·¼½!"Èɶ·¼½#$"#:;¦§#$"#:;¦§%&23$%89„…LMúûîïôõúû  &',-FGPQRSXY`abcØÙ%&23$%89„…LMúûîïôõúû  &',-FGPQRSXY`abcØÙ'(23bc~‘’“ÂÃØÙ'(23bc~‘’“ÂÃØÙ)*°±²³´µ¼½¾¿ÀÁÆÇÈÉÊËÌÍÎÏÖ׿çö÷þÿÀÁÂÃfg)*°±²³´µ¼½¾¿ÀÁÆÇÈÉÊËÌÍÎÏÖ׿çö÷þÿÀÁÂÃfg+,¸¹ÒÓÔÕèéêëìíôõøùŽhi+,¸¹ÒÓÔÕèéêëìíôõøùŽhi-./0xy45Z[€&'rs’“”•-./0xy45Z[€&'rs’“”•-./0¬­®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÖרÙâãæçö÷þÿZ[€rs”•-./0¬­®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÖרÙâãæçö÷þÿZ[€rs”•1BClm./23FGLM&'@ANOPQêëÖ×èé%&'(23`a~$%6789<=vw„…¢£¶·DELMRSXY´µúûôõ  XYbc ¡ØÙ, - %&'(23`a~$%6789<=vw„…¢£¶·DELMRSXY´µúûôõ  XYbc ¡ØÙ, - 45´µ\]^_„…45´µ\]^_„…67ÚÛÜÝÞßäåòóœbc67ÚÛÜÝÞßäåòóœbc89rsš›œª«   vwØÙÚÛŽ¦§¨©¶·¸¹ÈÉâãòó89rsš›œª«   vwØÙÚÛŽ¦§¨©¶·¸¹ÈÉâãòó:;¶·"#$%Ž:;¶·"#$%Ž <=Œ¸¹ÔÕìí0145XY <=Œ¸¹ÔÕìí0145XY>?š›º»ÚÛÐÑøù  89JK>?š›º»ÚÛÐÑøù  89JK@A67no¶·¸¹ðñ>?TUnoœª«>?@ABCDEnoÊËþÿ@A67no¶·¸¹ðñ>?TUnoœª«>?@ABCDEnoÊËþÿ1BC¾¿Ö×vw€‚ƒäåæç&'BCpqrsÊËèé1BC¾¿Ö×vw€‚ƒäåæç&'BCpqrsÊËèéDE()ÌÍäåDE()ÌÍäåFG²³´µ$%&'BCFG²³´µ$%&'BCHItuòó&'HItuòó&'JK¼½ !JK¼½ !LM^_èé‘LM^_èé‘NO®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÖרÙâãæçö÷þÿÞßàáNO®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÖרÙâãæçö÷þÿÞßàáPQ,-BC€ÚÛîïðñ ¡ÀÁþÿPQ,-BC€ÚÛîïðñ ¡ÀÁþÿRSHI¸¹RSHI¸¹TUz{®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷þÿ²³TUz{®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷þÿ²³VW°±²³¼½¾¿ÀÁÊËæçþÿVW°±²³¼½¾¿ÀÁÊËæçþÿXYjk‚ƒ„…†‡œ ¡®¯ÈÉXYjk‚ƒ„…†‡œ ¡®¯ÈÉZ[®¯²³¾¿ÂÃÆÇÈÉÐÑÖ׿ç~¼½Z[®¯²³¾¿ÂÃÆÇÈÉÐÑÖ׿ç~¼½\]®¯deÌÍ$ % \]®¯deÌÍ$ % ^_®¯°±²³¾¿ÂÃÆÇÈÉÊËÎÏÐÑÖ׿çö÷þÿ†‡ˆ‰˜™^_®¯°±²³¾¿ÂÃÆÇÈÉÊËÎÏÐÑÖ׿çö÷þÿ†‡ˆ‰˜™23`a$%()45>?@A~„…RSš›´µêë23`a$%()45>?@A~„…RSš›´µêë'(bcÈÉ‘’“žŸ'(bcÈÉ‘’“žŸde°±²³´µ¼½¾¿ÀÁÆÇÈÉÊËÎÏÖ׿çö÷þÿ !|}de°±²³´µ¼½¾¿ÀÁÆÇÈÉÊËÎÏÖ׿çö÷þÿ !|}fgÔÕèéêëìíôõøùŒ:;fgÔÕèéêëìíôõøùŒ:;hipqrs !hipqrs !jkŽœNO‚ƒ HIpqìíüýjkŽœNO‚ƒ HIpqìíüý1lm¢£&'Z[èéêëÖ×\]1lm¢£&'Z[èéêëÖ×\]no¬­°±²³´µ¶·¾¿ÌÍÎÏØÙâãæçúûüýÀÁÎÏno¬­°±²³´µ¶·¾¿ÌÍÎÏØÙâãæçúûüýÀÁÎÏpq¬­²³´µ¶·¾¿ÌÍÎÏØÙâãæçúûüýÈÉ  pq¬­²³´µ¶·¾¿ÌÍÎÏØÙâãæçúûüýÈÉ  rs|}ˆ‰rstu˜™ØÙ0167æç<=Žrs|}ˆ‰rstu˜™ØÙ0167æç<=Žtuä刉âã’“èétuä刉âã’“èévw !z{|}fgNOfgvw !z{|}fgNOfg-.xyÖרÙxyVWrs’“”•-.xyÖרÙxyVWrs’“”• TUz{®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçþÿJKfg67Š‹. / 0 1  TUz{®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçþÿJKfg67Š‹. / 0 1 rs|}¬­º»¼½ØÙrs|}¬­º»¼½ØÙ'(23~rstu¬­°±ÂÃØÙ'(23~rstu¬­°±ÂÃØÙ€¬­°±²³´µ¶·¼½¾¿ÆÇÈÉÊËÌÍÎÏÖרÙâãæçúûüýþÿZ[xy:;<=€¬­°±²³´µ¶·¼½¾¿ÆÇÈÉÊËÌÍÎÏÖרÙâãæçúûüýþÿZ[xy:;<=‚ƒ®¯ÂÃÆÇÈÉÐÑÖ×àáæç‚ƒ®¯ÂÃÆÇÈÉÐÑÖ×àáæç„…𛄅𛆇ÒÓêëìíôõÎÏrsZ[æç†‡ÒÓêëìíôõÎÏrsZ[æçrsˆ‰rstuvwxy<=VW      rsˆ‰rstuvwxy<=VW      Š‹ÒÓÔÕèéêëìíôõøùŠ‹ÒÓÔÕèéêëìíôõøù <=ŒÔÕ¬­xyŽÐÑPQRS0145JKXY <=ŒÔÕ¬­xyŽÐÑPQRS0145JKXYjkŽ‚ƒjkŽ‚ƒ‘®¯°±²³´µ¼½¾¿ÂÃÆÇÈÉÊËÎÏÐÑÖ׿çö÷þÿrstu‘®¯°±²³´µ¼½¾¿ÂÃÆÇÈÉÊËÎÏÐÑÖ׿çö÷þÿrstu’“ÚÛÜÝÞßàáäåðñòó¬­LMvwxy°±º»ðñ’“ÚÛÜÝÞßàáäåðñòó¬­LMvwxy°±º»ðñ”•ÐÑÞßàáòóúû”•ÐÑÞßàáòóúû–—¶·ÌÍÎÏâãúûüýÜÝ–—¶·ÌÍÎÏâãúûüýÜݘ™¬­²³´µ¶·¾¿ÌÍÎÏØÙâãæç  ˜™¬­²³´µ¶·¾¿ÌÍÎÏØÙâãæç  >?š›ÚÛ>?š›ÚÛjkœäåjk¢£ÔÕüýjkœäåjk¢£ÔÕüýžŸ ¡¬­  *+ !,-žŸ ¡¬­  *+ !,-žŸ ¡¬­  *+ !TUžŸ ¡¬­  *+ !TUlm¢£&'Z[æçlmèé\]lm¢£&'Z[æçlmèé\]¤¥‚ƒæçÚÛ¤¥‚ƒæçÚÛ¦§VWòóVW ¡¦§VWòóVW ¡¨©æç”•^_¨©æç”•^_ª«()’“67ª«()’“67/0nopq€˜™žŸ ¡¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÖרÙâãæçö÷úûüýþÿ*+ !/0nopq€˜™žŸ ¡¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÖרÙâãæçö÷úûüýþÿ*+ !/0NOTUZ[\]^_z{‚ƒ‘¬­®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙàáâãæçö÷þÿ/0NOTUZ[\]^_z{‚ƒ‘¬­®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙàáâãæçö÷þÿ)*/0NOTUVW^_denoz{€‘¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ²³ÀÁÂÃ)*/0NOTUVW^_denoz{€‘¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ²³ÀÁÂÃ)*/0NOTUVWZ[^_denopqz{€‘˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ²³)*/0NOTUVWZ[^_denopqz{€‘˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ²³)*/045NOTUdenopqz{€‘˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ^_)*/045NOTUdenopqz{€‘˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ^_:;nopq€–—˜™¬­°±²³´µ¶·¼½¾¿ÆÇÈÉÊËÌÍÎÏÖרÙâãæçúûüýþÿÜÝ:;nopq€–—˜™¬­°±²³´µ¶·¼½¾¿ÆÇÈÉÊËÌÍÎÏÖרÙâãæçúûüýþÿÜÝ+,<=¸¹ÒÓÔÕèéêëìíôõøùŽ+,<=¸¹ÒÓÔÕèéêëìíôõøùŽ>?º»ÔÕ>?º»ÔÕ)*/0JKNOTUVWdez{€‘¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ²³)*/0JKNOTUVWdez{€‘¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ²³)*/0NOTUVWZ[^_denopqz{€‘˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ²³)*/0NOTUVWZ[^_denopqz{€‘˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ²³)*/0NOTUVWdez{¬­®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷þÿ)*/0NOTUVWdez{¬­®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷þÿ/0NOTUZ[^_z{‚ƒ‘¬­®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙàáâãæçö÷þÿ/0NOTUZ[^_z{‚ƒ‘¬­®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙàáâãæçö÷þÿÄÅŠ‹ÆÇÄÅŠ‹ÆÇ)*/0NOTUZ[^_dez{€‚ƒ‘¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙàáâãæçö÷úûüýþÿ²³)*/0NOTUZ[^_dez{€‚ƒ‘¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙàáâãæçö÷úûüýþÿ²³)*/0NOTUZ[^_bcdez{€‚ƒ‘¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙàáâãæçö÷úûüýþÿ²³)*/0NOTUZ[^_bcdez{€‚ƒ‘¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙàáâãæçö÷úûüýþÿ²³)*/0NOTUVW^_dez{€‘¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ²³)*/0NOTUVW^_dez{€‘¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ²³)*/0NOTUnopqz{€–—˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿÜÝ)*/0NOTUnopqz{€–—˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿÜÝ)*/0NOTU^_denopqz{€‘–—˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿÜÝ<=)*/0NOTU^_denopqz{€‘–—˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿÜÝ<=TUZ[^_z{‚ƒ‘”•®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙÞßàáâãæçòóö÷þÿTUZ[^_z{‚ƒ‘”•®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙÞßàáâãæçòóö÷þÿ+,†‡Š‹¸¹ÒÓÔÕèéêëìíôõøùrsŽæç+,†‡Š‹¸¹ÒÓÔÕèéêëìíôõøùrsŽæç +,<=fgŠ‹Œ¸¹º»ÒÓÔÕèéêëìíôõøù¬­ŒŽÐÑ:;RShi +,<=fgŠ‹Œ¸¹º»ÒÓÔÕèéêëìíôõøù¬­ŒŽÐÑ:;RShi)*/0NOTUZ[^_dez{€‚ƒ‘¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙàáâãæçö÷úûüýþÿ²³)*/0NOTUZ[^_dez{€‚ƒ‘¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙàáâãæçö÷úûüýþÿ²³/0NOTUnopqz{€˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ  /0NOTUnopqz{€˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ  67>?’“š›ÚÛÜÝÞßàáäåòó67>?’“š›ÚÛÜÝÞßàáäåòó67’“ÚÛÜÝÞßàáäåòóbc67’“ÚÛÜÝÞßàáäåòóbc67’“”•ÐÑÚÛÜÝÞßàáäåðñòóLMxy67’“”•ÐÑÚÛÜÝÞßàáäåðñòóLMxy‚ƒ’“”•®¯ÂÃÆÇÈÉÐÑÖ×ÚÛÜÝÞßàáäåæçòóúû‚ƒ’“”•®¯ÂÃÆÇÈÉÐÑÖ×ÚÛÜÝÞßàáäåæçòóúû/0NOTUnopqz{€–—˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿÜÝÈÉ  /0NOTUnopqz{€–—˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿÜÝÈÉ  67tu’“ÚÛÜÝÞßàáäåòóˆ‰âã67tu’“ÚÛÜÝÞßàáäåòóˆ‰âã)*/0NOTUVWZ[^_denopqz{€‚ƒ‘˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙàáâãæçö÷úûüýþÿ)*/0NOTUVWZ[^_denopqz{€‚ƒ‘˜™¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙàáâãæçö÷úûüýþÿ+,fgŠ‹¸¹ÒÓÔÕèéêëìíôõøùŽ:;hi+,fgŠ‹¸¹ÒÓÔÕèéêëìíôõøùŽ:;hi+,fg†‡Š‹¸¹ÒÓÔÕèéêëìíôõøùŒŽ:;<=hi+,fg†‡Š‹¸¹ÒÓÔÕèéêëìíôõøùŒŽ:;<=hi+,<=fg†‡Š‹¸¹ÒÓÔÕèéêëìíôõøùŒŽ:;+,<=fg†‡Š‹¸¹ÒÓÔÕèéêëìíôõøùŒŽ:;îïhino²³ìí îïhino²³ìí ’“ÞßðñLMxy’“ÞßðñLMxy67’“”•ÐÑÚÛÜÝÞßàáäåòó$%ØÙDEøùúû, - 67’“”•ÐÑÚÛÜÝÞßàáäåòó$%ØÙDEøùúû, - +,fg†‡Š‹¸¹ÒÓÔÕèéêëìíôõøùŒŽ:;hi+,fg†‡Š‹¸¹ÒÓÔÕèéêëìíôõøùŒŽ:;hi)*/0NOTU^_de‘¬­®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷þÿ²³´µ)*/0NOTU^_de‘¬­®¯°±²³´µ¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷þÿ²³´µ+,fgŠ‹¸¹ÒÓÔÕèéêëìíôõøùŒŽhi+,fgŠ‹¸¹ÒÓÔÕèéêëìíôõøùŒŽhinopq€–—¬­°±²³´µ¶·¼½¾¿ÆÇÈÉÊËÌÍÎÏÖרÙâãæçúûüýþÿª«†‡nopq€–—¬­°±²³´µ¶·¼½¾¿ÆÇÈÉÊËÌÍÎÏÖרÙâãæçúûüýþÿª«†‡nopq€–—¬­°±²³´µ¶·¼½¾¿ÆÇÈÉÊËÌÍÎÏÖרÙâãæçúûüýþÿÜÝ<=nopq€–—¬­°±²³´µ¶·¼½¾¿ÆÇÈÉÊËÌÍÎÏÖרÙâãæçúûüýþÿÜÝ<=)*/0NOTUVW^_dez{€‘¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ²³)*/0NOTUVW^_dez{€‘¬­®¯°±²³´µ¶·¼½¾¿ÀÁÂÃÆÇÈÉÊËÌÍÎÏÐÑÖרÙâãæçö÷úûüýþÿ²³+,fgŠ‹ÒÓÔÕèéêëìíôõøùŒ:;hi+,fgŠ‹ÒÓÔÕèéêëìíôõøùŒ:;hi'(^_‚ƒÐц‡ˆ‰˜™'(^_‚ƒÐц‡ˆ‰˜™67œîïðñö÷¾¿67œîïðñö÷¾¿67ÜÝœ`aîïbc67ÜÝœ`aîïbc „… „…  ¨©ÎÏÐÑ45~ÌÍ  ¨©ÎÏÐÑ45~ÌÍ  RSVW|}  RSVW|}jkŽ()pqjkŽ()pqæçèéæçèéjkŽ‚ƒjkŽ‚ƒòó"#òó"#    LMæçèéüýþÿLMæçèéüýþÿ123LMæçèé@APQ€¼½&'ÂÃèé123LMæçèé@APQ€¼½&'ÂÃèéJK¼½ !‘JK¼½ !‘ !‘ !‘JK !^_BC‘JK !^_BC‘"#‘˜™"#‘˜™%&23`aòó$%()4589<=vw~„…¢£¤¥¶·ØÙDELMRSno®¯êëøùúû ¡ØÙ, - %&23`aòó$%()4589<=vw~„…¢£¤¥¶·ØÙDELMRSno®¯êëøùúû ¡ØÙ, - lm¢£&'FGæç”•\]lm¢£&'FGæç”•\]`a$%()45~RSš›êë`a$%()45~RSš›êë*+./0123VW¦§$%&',-~†‡–—ÄÅÞßàá*+./0123VW¦§$%&',-~†‡–—ÄÅÞßàáPQ,-BC€ÚÛîïðñ,-ÀÁPQ,-BC€ÚÛîïðñ,-ÀÁ1*+./0123VW|}¦§ª«$%,-ÜÝÞß0 1 1*+./0123VW|}¦§ª«$%,-ÜÝÞß0 1 *+./0123VW¦§,-Þß. / 0 1 *+./0123VW¦§,-Þß. / 0 1 1*+./0123LMVW¦§,-@APQÞßèé1*+./0123LMVW¦§,-@APQÞßèé`a$%()4567~RSêë`a$%()4567~RSêë23456789RSXY23456789RSXY%&23$%6789„…LMVWXYúû¬­ØÙ%&23$%6789„…LMVWXYúû¬­ØÙ:;žŸÆÇ€:;žŸÆÇ€23$%<=@Avw¢£¶·DERSno ¡, - 23$%<=@Avw¢£¶·DERSno ¡, - `a>?@A„…RSnoš›ÆÇ`a>?@A„…RSnoš›ÆÇ`a<=>?@AnoÆÇ`a<=>?@AnoÆÇPQ,-BC,-PQ,-BC,-DE˜™ÀÁ 89^_z{’“ìíôõDE˜™ÀÁ 89^_z{’“ìíôõ1FGJKLMno޼½bcfglm„…ÂÃÄÅ1FGJKLMno޼½bcfglm„…ÂÃÄÅRSHItu¸¹ÈÉRSHItu¸¹ÈÉ z{FGJKLMfg޼½67fglm„…Š‹Âà z{FGJKLMfg޼½67fglm„…Š‹ÂÃ123FGJKLM޼½@APQfglm„…ÂÃèé123FGJKLM޼½@APQfglm„…ÂÃèéjkNO‚ƒjkNO‚ƒPQêëÚÛPQêëÚÛ  RSTU|}  RSTU|} RSTUlm<=>?ÊË RSTUlm<=>?ÊË  *+./0123VW|}¦§,-Þß  *+./0123VW|}¦§,-ÞßXYZ[‘XYZ[‘XYZ[‘ôõâãXYZ[‘ôõâã\]ö÷de\]ö÷deLM !^_èéBC‘LM !^_èéBC‘`abcNO`abcNO`abcNO–—`abcNO–—defgdefg z{JKdefg67Š‹ z{JKdefg67Š‹hivwÒÓÔÕhivwÒÓÔÕjk./¤¥èéjk./¤¥èé TUlm<=>?ÊËèé TUlm<=>?ÊËèéFGnoŽfgŒÂÃÄÅ  FGnoŽfgŒÂÃÄÅ  hipqrs !ÊËhipqrs !ÊËhipqrsÊËhipqrsÊËHItuúûÈÉHItuúûÈÉ23$%<=vw¢£¶·DERS®¯ ¡, - 23$%<=vw¢£¶·DERS®¯ ¡, - xyžŸÒÓ€ ¡xyžŸÒÓ€ ¡z{‚ƒÊËz{‚ƒÊË  ./RSVW|}  ./RSVW|}`a$%()45~RS®¯êë`a$%()45~RS®¯êëPQ,-€ÚÛîïðñÀÁþÿPQ,-€ÚÛîïðñÀÁþÿjkŽNOz{‚ƒŽ„…jkŽNOz{‚ƒŽ„…%&23`a$%89>?„…LMRSš›´µúûØÙ%&23`a$%89>?„…LMRSš›´µúûØÙ†‡01¨©ÔÕ†‡01¨©ÔÕtuä刉âãèétuä刉âãèéÄÅŠ‹ÆÇª«ÄÅŠ‹ÆÇª«Œ¾¿vwö÷Œ¾¿vwö÷FGJKLMno޼½$%fglm„…ŒÂÃÄÅFGJKLMno޼½$%fglm„…ŒÂÃÄÅ"#XYZ[‘’“˜™œ"#XYZ[‘’“˜™œ‘’“œ‘’“œ”•ÂÃÔÕÖ×|}”•ÂÃÔÕÖ×|}–—Î϶·¸¹–—Î϶·¸¹"#DE‘˜™ z{’“ìí"#DE‘˜™ z{’“ìíš›BCš›BC67œ`a67œ`a:;xyžŸ€:;xyžŸ€ ¡¤¥JK ¡¤¥JK23$%<=vw¢£¤¥¶·DERS ¡, - 23$%<=vw¢£¤¥¶·DERS ¡, - $% ¡¢£¤¥JK$% ¡¢£¤¥JK*+./0123VW¦§¨©ª«*+,-Þß*+./0123VW¦§¨©ª«*+,-Þß  ¦§¨©ª«ÎÏÐÑ*+ÌÍ  ¦§¨©ª«ÎÏÐÑ*+ÌÍ./¦§¨©ª«*+./¦§¨©ª«*+ |}Œ’“ÔÕ¬­vwxy°±º»¼½îïðñÐÑRS |}Œ’“ÔÕ¬­vwxy°±º»¼½îïðñÐÑRS®¯´µÎÏÐÑhi¶·®¯´µÎÏÐÑhi¶·°±ÚÛøùÌÍÎϰ±ÚÛøùÌÍÎÏTU°±²³¼½¾¿ÆÇÈÉÊËÖ×ö÷þÿ²³´µhiTU°±²³¼½¾¿ÆÇÈÉÊËÖ×ö÷þÿ²³´µhiö÷®¯²³´µhiö÷®¯²³´µhi23$%<=vw¢£¶·DERSrs ¡, - 23$%<=vw¢£¶·DERSrs ¡, - ¸¹º»JK¸¹º»JK¸¹º»JKrs¸¹º»JKrsFGJKLM޼½bcfglm„…ÂÃFGJKLM޼½bcfglm„…ÂÃŒ¾¿ìívwxyÒÓö÷Œ¾¿ìívwxyÒÓö÷DEÀÁTU\]^_DEÀÁTU\]^_”•ÂÃÔÕÖ×â㔕ÂÃÔÕÖ×âãÄŤ¥òó,-ÄŤ¥òó,-ÄÅ:;Š‹ÆÇØÙ@A˜™¸¹0 1 ÄÅ:;Š‹ÆÇØÙ@A˜™¸¹0 1 ÈÉ|}Ö×ÈÉ|}Ö×z{ÊËæçz{ÊËæçÌÍøùþÿÌÍøùþÿ  –—¨©®¯ÎÏÐÑhi¶·ÌÍ  –—¨©®¯ÎÏÐÑhi¶·ÌÍ  ¨©®¯ÎÏÐÑòó*+45bc‚ƒ¶·ÌÍÜÝ  ¨©®¯ÎÏÐÑòó*+45bc‚ƒ¶·ÌÍÜÝxyÒÓ  € ¡ÀÁxyÒÓ  € ¡ÀÁ”•ÂÃÔÕâ㔕ÂÃÔÕâ㔕ÂÃÖ×|}Ö×”•ÂÃÖ×|}Ö×òó$%ÆÇØÙDEøù, - òó$%ÆÇØÙDEøù, - PQ,-€°±ÚÛîïðñÀÁÌÍÎÏPQ,-€°±ÚÛîïðñÀÁÌÍÎÏÜÝÞßàḹÜÝÞßàḹNOÜÝÞßàáNOÜÝÞßàáNOÜÝÞßàáNOÜÝÞßàátuä刉âã’“tuä刉âã’“œäåjkÔÕœäåjkÔÕÊËæçèéÊËæçèéLM^_æçèéLM^_æçèéPQêë$%àáPQêë$%à᾿ìívwxyÒÓ$%¾¿ìívwxyÒÓ$% PQ,-€ÚÛîïðñÀÁ PQ,-€ÚÛîïðñÀÁ PQ,-€ÚÛîïðñÀÁ PQ,-€ÚÛîïðñÀÁÐÑòó*+bc‚ƒÜÝÐÑòó*+bc‚ƒÜÝZ[ôõâãZ[ôõâã\]ö÷de\]ö÷de°±ÌÍøùÎÏþÿ°±ÌÍøùÎÏþÿtuúûlmÈÉtuúûlmÈÉüýþÿ !ŒÚÛ  üýþÿ !ŒÚÛ  üýþÿÊËüýþÿÊËæçèéüýþÿÊËæçèéüýþÿÊË    #$òó"##$òó"#–—Î϶·–—Î϶·jkDE˜™ z{’“ìíjkDE˜™ z{’“ìíÒÓ   ¡ÀÁþÿÒÓ   ¡ÀÁþÿ    PQ,-€ÚÛîïðñòóÀÁPQ,-€ÚÛîïðñòóÀÁVW\]deVW\]deö÷üý !deŒÚÛ  ö÷üý !deŒÚÛ    ¨©ÎÏÐÑÌÍ  ¨©ÎÏÐÑÌÍüý !Œ¬­ÚÛèé  üý !Œ¬­ÚÛèé  hiPQnoö÷üý !Œ¬­ÚÛ  hiPQnoö÷üý !Œ¬­ÚÛ  123LMfgüý !@APQŒÚÛèé  123LMfgüý !@APQŒÚÛèé  ÆÇØÙÆÇØÙz{‚ƒÊËz{‚ƒÊËhipqüý !ŒÚÛ  hipqüý !ŒÚÛ  #$"#:;#$"#:;*+./Žêë$%&'45PQfg~†‡ˆ‰–—ÄÅàá*+./Žêë$%&'45PQfg~†‡ˆ‰–—ÄÅàá1*+$%&'NOPQ~†‡–—ÄÅàá1*+$%&'NOPQ~†‡–—ÄÅàá()ÊË()Ê˦§¨©ª«ÐÑòó*+bc‚ƒÜݦ§¨©ª«ÐÑòó*+bc‚ƒÜÝ*+,-./0123BCVW¦§,-Þß*+,-./0123BCVW¦§,-Þßjk./˜™¤¥jk./˜™¤¥†‡01ÔÕ†‡01ÔÕ23œÐÑÔÕ23œÐÑÔÕ  ÐÑ$%45~  ÐÑ$%45~ z{JKfg67ˆ‰Š‹ z{JKfg67ˆ‰Š‹DE89ØÙôõDE89ØÙôõ#$"#:;¦§¨©ª«èé#$"#:;¦§¨©ª«èé TUlm<=>?z{¦§¨©ÊË TUlm<=>?z{¦§¨©ÊË TUlm<=>?z{¨©ÊË TUlm<=>?z{¨©ÊË123LMÆÇ@APQžŸº»âãäåè阙¸¹0 1 123LMÆÇ@APQžŸº»âãäåè阙¸¹0 1  !^_š›BC‘ !^_š›BC‘23òó$%<=vw¢£¶·ØÙDERSøù ¡, - 23òó$%<=vw¢£¶·ØÙDERSøù ¡, - &'FGHIæç”•&'FGHIæç”•jkFGHI”•æçjkFGHI”•æç ¡¤¥¸¹º»JKLM ¡¤¥¸¹º»JKLM%&23’“Þßðñ$%89„…JKLMxyúûØÙ%&23’“Þßðñ$%89„…JKLMxyúûØÙ1`abc&'NOPQ1`abc&'NOPQ123LM$%&'@ANOPQèé123LM$%&'@ANOPQèé23`a$%()4567<=>?vw~„…¢£¶·DERS𛮝êë ¡Ö×, - 23`a$%()4567<=>?vw~„…¢£¶·DERS𛮝êë ¡Ö×, - ÀÁTU\]^_ÀÁTU\]^_89VWXY¬­89VWXY¬­236789VWXYª«¬­°±ÂÃ236789VWXYª«¬­°±ÂÃlm¢£Z[èélm¢£Z[èéÀÁTU\]âãÀÁTU\]âãDEÀÁTU^_ôõDEÀÁTU^_ôõœ`aÔÕœ`aÔÕFG¼½ÐÑòó*+bc‚ƒÜÝFG¼½ÐÑòó*+bc‚ƒÜÝ\]ö÷de\]ö÷deFGJKLMno޼½$%fglm„…ÂÃÄÅFGJKLMno޼½$%fglm„…ÂÃÄÅ®¯²³´µÎÏhi®¯²³´µÎÏhiœäåjkÔÕœäåjkÔÕFGJKLM޼½úûfglm„…ÂÃÈÉFGJKLM޼½úûfglm„…ÂÃÈÉ$%<=>?@AnoÆÇ$%<=>?@AnoÆÇjkpqjkpq¶·º»rs¶·º»rsHItuòó !&'ÄÅÆÇ*+HItuòó !&'ÄÅÆÇ*+’“hiŒ¬­¾¿ìívwxy°±º»ÒÓðñ$%’“hiŒ¬­¾¿ìívwxy°±º»ÒÓðñ$%’“Þßðñ¬­¾¿ìíLMvwxy°±º»ÒÓîïðñXY’“Þßðñ¬­¾¿ìíLMvwxy°±º»ÒÓîïðñXY DE˜™ <=>?z{’“¨©ìí DE˜™ <=>?z{’“¨©ì픕ÈÉÖ×|}Ö×”•ÈÉÖ×|}Ö×  *+$%&'45~†‡–—ÄÅàá  *+$%&'45~†‡–—ÄÅàá:;xyžŸÒÓ€:;xyžŸÒÓ€ÐÑòó*+bc‚ƒˆ‰ÜÝÐÑòó*+bc‚ƒˆ‰ÜÝ FGJKLM޼½fglm„…Âà FGJKLM޼½fglm„…ÂÃ*+$%&'~†‡ˆ‰–—ÄÅàá*+$%&'~†‡ˆ‰–—ÄÅàá$%67‚ƒ†‡ˆ‰Š‹$%67‚ƒ†‡ˆ‰Š‹ z{JKfg67ˆ‰Š‹ z{JKfg67ˆ‰Š‹noŽüý !ŒÚÛ  noŽüý !ŒÚÛ  ‚ƒŽ„…†‡¬­®¯‚ƒŽ„…†‡¬­®¯LM !^_BC‘LM !^_BC‘tuDE˜™âã z{’“ìítuDE˜™âã z{’“ìíHI”•ÜÝHI”•ÜÝ*+bc$%&'~†‡–—ÄÅàá*+bc$%&'~†‡–—ÄÅàá./˜™¢£./˜™¢£`a()>?„…RSš›`a()>?„…RS𛑒“23œžŸ¾¿ÐÑ‘’“23œžŸ¾¿ÐÑ@AœžŸº»@AœžŸº»PQxyÒÓ   ¡ÀÁþÿPQxyÒÓ   ¡ÀÁþÿœ˜™¢£œ˜™¢£jkÄÅ./¤¥òójkÄÅ./¤¥òó#$:;<=¦§¨©ª«èé#$:;<=¦§¨©ª«èé :;<=>?z{¦§¨©ª«èé :;<=>?z{¦§¨©ª«è銋:;¦§¨©ª«è銋:;¦§¨©ª«è鬭è鬭èé$%vw~RS®¯$%vw~RS®¯’“¬­vwxy°±º»ðñXY’“¬­vwxy°±º»ðñXY²³´µÆÇòóö÷²³´µÆÇòóö÷23`a„…²³´µôõ23`a„…²³´µôõ–—®¯ÎÏÐѶ·¸¹–—®¯ÎÏÐѶ·¸¹RSHI–—Üݶ·¸¹RSHI–—Üݶ·¸¹|}’“¬­@AvwxyžŸ°±º»¼½ðñ|}’“¬­@AvwxyžŸ°±º»¼½ðñ|}¬­º»¼½|}¬­º»¼½œ¾¿â㜾¿âãPQ,-€ÒÓÚÛîïðñ   ¡ÀÁPQ,-€ÒÓÚÛîïðñ   ¡ÀÁFGJKLMno޼½fglm„…ÂÃÄÅFGJKLMno޼½fglm„…ÂÃÄÅ*+FGnoŽ$%&'fg~†‡–—ÂÃÄÅàá*+FGnoŽ$%&'fg~†‡–—ÂÃÄÅàá>?@Ano²³ÆÇ>?@Ano²³ÆÇHItuúûlmÈÉHItuúûlmÈÉ TUlmpqrsþÿ()<=>?ÊË TUlmpqrsþÿ()<=>?ÊË  ¨©°±ÎÏÐÑÚÛÌÍÎÏ  ¨©°±ÎÏÐÑÚÛÌÍÎϰ±ÚÛøùÌÍÎϰ±ÚÛøùÌÍÎÏ23œÐÑÔÕ23œÐÑÔÕhi¾¿ìívwxyÒÓÔÕhi¾¿ìívwxyÒÓÔÕœhiäå23`ajkÐÑÒÓÔÕœhiäå23`ajkÐÑÒÓÔÕÈÉÖ×|}Ö×ÈÉÖ×|}Ö×89ØÙâã89ØÙâãPQüý !ŒÚÛ  PQüý !ŒÚÛ  ./ÐÑòó*+bc‚ƒÜÝÞß./ÐÑòó*+bc‚ƒÜÝÞß*+./0123VW¦§,-ÜÝÞß*+./0123VW¦§,-ÜÝÞß*+êë$%&'~†‡–—ÄÅàá*+êë$%&'~†‡–—ÄÅàáZ[ÂÃÔÕôõ@A\]¾¿ØÙâãäåZ[ÂÃÔÕôõ@A\]¾¿ØÙâãäå@Aâãäå@Aâãä墣¨©&'FGHIæç”•^_¢£¨©&'FGHIæç”•^_ lm¬­èé lm¬­èé`a$%()45~RSêëÖ×`a$%()45~RSêëÖ×jkDE˜™ z{’“ìíjkDE˜™ z{’“ìí¬­xyîïðñö÷¾¿JK¬­xyîïðñö÷¾¿JK’“¬­vwxy°±º»îïðñö÷¾¿’“¬­vwxy°±º»îïðñö÷¾¿HItuòóHItuòóDE89^_ôõDE89^_ôõŒ¾¿îïðñö÷¾¿Œ¾¿îïðñö÷¾¿òó$%ØÙDEøùúû, - òó$%ØÙDEøùúû, - %&23”•àáòó$%89„…LMøùúûØÙ, - %&23”•àáòó$%89„…LMøùúûØÙ, - jkœüýjkœüýPQ€ÌÍøù   ¡þÿPQ€ÌÍøù   ¡þÿ¢£*+‘¢£*+‘@A@ABCjk‚ƒ„…†‡TUBCDE„…ÊËBCjk‚ƒ„…†‡TUBCDE„…ÊËþÿÌÍþÿÌÍ    ÊËJK¼½ÊËìí¨©üýxyz{    ÊËJK¼½ÊËìí¨©üýxyz{     JKÊËìí¨©z{     JKÊËìí¨©z{    @AXYÊËpqrs    @AXYÊËpqrsXYpqXYpq üý üý    JKìí¨©üýz{    JKìí¨©üýz{:;TUÆÇ”•¨©¸¹ÈÉ()`a:;TUÆÇ”•¨©¸¹ÈÉ()`aHIhiHIhiPQFGŠ‹þÿ89:;<=~ÌÍPQFGŠ‹þÿ89:;<=~ÌÍLM23Š‹LM23Š‹DEØÙÚÛøù¶·DEØÙÚÛøù¶·@A’“jkôõ,-./‘º»@A’“jkôõ,-./‘º»tu !~²³&'”•ÄÅÆÇ*+œžŸêëtu !~²³&'”•ÄÅÆÇ*+œžŸêë"#\]^_RS*+øù"#\]^_RS*+øù $%bcdeâ㤥JK $%bcdeâ㤥JK&'z{‘°±&'z{‘°±ª«()’“67ª«()’“67*+,-./23ÂÃnovw¬­*+,-./23ÂÃnovw¬­*+,-23ÂÃÄÅ  45novw¬­ÀÁØÙ*+,-23ÂÃÄÅ  45novw¬­ÀÁØÙ*+./¬­®¯vwÂÃ*+./¬­®¯vwÂÃ0123¬­45ÂÃ0123¬­45ÂÃ*+,-0123ÂÃ45no¬­*+,-0123ÂÃ45no¬­-.45€&'‘’“-.45€&'‘’“@A67¸¹no>?@Anoþÿ@A67¸¹no>?@Anoþÿ89    89    :;TU”•¨©¸¹ÎÏ()`a:;TU”•¨©¸¹ÎÏ()`a<=îï\]fg¢£<=îï\]fg¢£>?@A´µ¬­º»>?@A´µ¬­º»>?@A"#ÐѬ­>?@A"#ÐѬ­BCjkhi„…TUBCjkhi„…TUDE¶·DE¶·FGØÙBCtuøùHIèéFGØÙBCtuøùHIèéHIBCHIBCJKpqrsª«  €JKpqrsª«  €LMˆ‰23^_îïðñLMˆ‰23^_îïðñNOpqrs”•àáÈÉ–—NOpqrs”•àáÈÉ–—PQˆ‰Ž‘89 PQˆ‰Ž‘89 RS¦§lm‚ƒRS¦§lm‚ƒ:;TUÆÇ”•–—:;TUÆÇ”•–—¦§VW~ØÙúûVW¦§VW~ØÙúûVWXYZ[’“žŸ ¡XYZ[’“žŸ ¡-./0€XYZ[€xy:;<=rs”• ¡-./0€XYZ[€xy:;<=rs”• ¡45"#\]^_*+„…øù45"#\]^_*+„…øù45´µ"#\]^_ÄÅ„…øù45´µ"#\]^_ÄÅ„…øù`aBC²³Þß`aBC²³Þß67ÜÝ$%bcde¤¥JK67ÜÝ$%bcde¤¥JK$%bcdexyâ㤥JK$%bcdexyâ㤥JKfgèéòóˆ‰fgèéòóˆ‰hiÌÍ  hiÌÍ  BCjkfghi’“BCjkfghi’“¢£lm¦§øù\]¢£lm¦§øù\]@Anoœ@AnoœpqrsäåÀÁÂÃÄÅÈÉ\]ÄÅNOˆ‰pqrsäåÀÁÂÃÄÅÈÉ\]ÄÅNOˆ‰89rsˆ‰pqrstuäåÄÅÈÉüý \]ÄÅØÙÚÛBCäåòó    89rsˆ‰pqrstuäåÄÅÈÉüý \]ÄÅØÙÚÛBCäåòó    rsˆ‰rstu01<=rsˆ‰rstu01<=vw()bcpqžŸ‚ƒvw()bcpqžŸ‚ƒxyz{|}˜™ö÷þÿ‚ƒ„…î拉  xyz{|}˜™ö÷þÿ‚ƒ„…î拉  xyz{|} ¡ö÷øùþÿ„…êëî拉  xyz{|} ¡ö÷øùþÿ„…êëî拉  xyz{|} ¡PQøùþÿ:;„…¾¿ìíî拉    xyz{|} ¡PQøùþÿ:;„…¾¿ìíî拉    !~²³$%&'”•œ !~²³$%&'”•œ-./045Z[€¼½&'&'’“ÂÃrs”•-./045Z[€¼½&'&'’“ÂÃrs”•¤¥‚ƒÔÕÚÛ$%¤¥‚ƒÔÕÚÛ$%‚ƒŽ„…†‡¬­®¯¼½’“‚ƒŽ„…†‡¬­®¯¼½’“Ž„…†‡¬­®¯¼½’“Ž„…†‡¬­®¯¼½’“LMˆ‰23^_ðñ˜™ðñLMˆ‰23^_ðñ˜™ðñŠ‹ÒÓŒ67bcÒÓŠ‹ÒÓŒ67bcÒÓŒXYäåêëŒXYäåêëŽÂÃÆÇîïØÙŽÂÃÆÇîïØÙ‘œÒÓ23æç‘œÒÓ23æç’“úûŒ’“úûŒ”•˜™”•˜™–—VWžŸ./’“–—VWžŸ./’“xy˜™‚ƒxy˜™‚ƒš›()no„…89š›()no„…89‘œ ÒÓ23´µæç‘œ ÒÓ23´µæçžŸ¢£ÔÕÖרÙüý‚ƒôõö÷žŸ¢£ÔÕÖרÙüý‚ƒôõö÷z{|} ¡>?Þßàáòóøù@AÎÏz{|} ¡>?Þßàáòóøù@AÎÏ¢£*+./45de¢£*+./45de¤¥ÀÁ‘¢£ê뤥ÀÁ‘¢£êëRS¦§bclm‚ƒ–—RS¦§bclm‚ƒ–—¨©Ö×°±XY†‡¨©Ö×°±XY†‡úûª«Ö׆‡úûª«Ö׆‡Ž„…†‡¬­®¯Z[bcŽ„…†‡¬­®¯Z[bcŽ„…†‡¬­®¯°±Ž„…†‡¬­®¯°±®¯°±®¯°±FG !~²³´µ$%&'BCFG !~²³´µ$%&'BCFG²³´µÞßâã$%&'BCFG²³´µÞßâã$%&'BC@A¶·>?€Œœ>?@ABCDE@A¶·>?€Œœ>?@ABCDE@A67¸¹>?@A67¸¹>?º»bcÐѺ»bcÐÑ€„…†‡¼½&'ÂÃ’“€„…†‡¼½&'ÂÃ’“îïðñö÷¾¿JKîïðñö÷¾¿JK¤¥ÀÁ”•‘ÄÅ¢£ê뤥ÀÁ”•‘ÄÅ¢£êë*+,-23ÂÃÄÅ  ôõ,-TUno„…¬­ÚÛ*+,-23ÂÃÄÅ  ôõ,-TUno„…¬­ÚÛ,-^_ÂÃÄÅ  ‘ôõŠ‹„…ª«ØÙÚÛö÷,-^_ÂÃÄÅ  ‘ôõŠ‹„…ª«ØÙÚÛö÷TUÆÇ–—TUÆÇ–—ÈÉúûHIÈÉúûHI  ÊËJKÎÏâãî﨩üýxyz{  ÊËJKÎÏâãî﨩üýxyz{ÌÍ,-ÌÍ,-ÎÏ–—¾¿lmÎÏ–—¾¿lmÐÑö÷úûJKÐÑö÷úûJKŠ‹ÒÓ"#DE¦§ŒìíîÒÓ"#DE¦§ŒìíîïÔÕ !ÔÕ !¨©ª«Ö×°±XY†‡"#Œ¨©ª«Ö×°±XY†‡"#ŒFGØÙÚÛ./tuøùHI¶·FGØÙÚÛ./tuøùHI¶·ØÙÚÛ./BCøùØÙÚÛ./BCøù–—¶·ÌÍÎÏâãüýÜÝ<= ¡–—¶·ÌÍÎÏâãüýÜÝ<= ¡´µÞßâãfg´µÞßâãfgàá  nožŸàá  nožŸ´µÞßâã´µÞßâãpqrsäå–—ÄÅÈÉ\]ÄÅNOpqrsäå–—ÄÅÈÉ\]ÄÅNO¤¥æç ÚÛæç¤¥æç ÚÛæçlm¢£jkZ[fgèéêë23ˆ‰lm¢£jkZ[fgèéêë23ˆ‰1lmèéêëÖ×1lmèéêëÖ×ìí,-`aˆ‰Š‹Žìí,-`aˆ‰Š‹Ž<=î<=î@Aðñ¨©noÊË@Aðñ¨©noÊËÄŤ¥fgòóˆ‰ÄŤ¥fgòóˆ‰ôõö÷øù‚ƒZ[ôõö÷øù‚ƒZ[ôõö÷øùÐÑZ[ôõö÷øùÐÑZ[ôõö÷øù‚ƒôõö÷øù‚ƒúûèébcLMúûèébcLMüýz{ÜÝüýz{ÜÝþÿŒÌÍþÿŒÌÍèéîïLMbcèéîïLMbcjkÄÅ./¤¥òóˆ‰jkÄÅ./¤¥òóˆ‰no¸¹¸¹ÎÏ`a–—no¸¹¸¹ÎÏ`a–—noÀÁnoÀÁœæç ÚÛæçœæç ÚÛæçžŸ ¡,-ÂÃÄÅ  ,-TUžŸ ¡,-ÂÃÄÅ  ,-TUàá  nožŸàá  nožŸ  ¶·ÒÓ  ¶·ÒÓ–— ¡–— ¡–—&'ª«–—&'ª«vw–—ª«vw–—ª«vwÌÍ,-vwÌÍ,-vwÌÍvwÌÍDEÔÕ !DEÔÕ !ŒÎÏŽPQRSŒÎÏŽPQRS89 ¡89 ¡devw !z{|}fgfgdevw !z{|}fgfgÒÓ"#DE¦§êëìíîïÒÓ"#DE¦§êëìíîïFG~²³´µ$%&'BC"#ÞßFG~²³´µ$%&'BC"#Þß-.FG45€²³´µ$%&'BC-.FG45€²³´µ$%&'BCDEš›()„…DEš›()„…žŸ ¡¬­*+RS !*+žŸ ¡¬­*+RS !*+ìí,-`aˆ‰ØÙìí,-`aˆ‰ØÙØÙÚÛ./BC"#èéØÙÚÛ./BC"#èé0123456789:;<=z{|}|}(),-230123456789:;<=z{|}|}(),-2301234567z{01234567z{0123456789z{|},-0123456789z{|},-0123456701234567014589z{|},-01†‡014589z{|},-01†‡01:;()01:;()01<=¸¹defg01<=¸¹defg@A ¡¶·>?†‡œ>?@ABCDEÎÏ@A ¡¶·>?†‡œ>?@ABCDEÎÏ  @AXYpqrs  @AXYpqrsFGÚÛ./BCBC"#$%èéFGÚÛ./BCBC"#$%èéÒÓ"#DE¦§ÞßìíîïÒÓ"#DE¦§ÞßìíîïFG€‚ƒŠ‹ª«FG€‚ƒŠ‹ª«HIäåHIäå    ÊËJKÎÏâãìíî﨩üýz{    ÊËJKÎÏâãìíî﨩üýz{LM^_æçèéLM^_æçèéNOö÷NOö÷|}PQdeŠ‹þÿ89:;<=~¦§¾¿ìíîïÌÍ|}PQdeŠ‹þÿ89:;<=~¦§¾¿ìíîïÌÍ"#*+RS*+"#*+RS*+TUno@AŽ89:;˜™êëÐÑÒÓTUno@AŽ89:;˜™êëÐÑÒÓ–—VW’“–—VW’“  @AXYpqrs  @AXYpqrs¬­Z[bc¬­Z[bc<=\]fg<=\]fgLM^_`aæç67LMÔÕTUäåLM^_`aæç67LMÔÕTUäå^_`a0167ÎÏôõRSTUŽæçš›îï  ^_`a0167ÎÏôõRSTUŽæçš›îï  ¦§¬­Z[bclm–—¦§¬­Z[bclm–—de¢£¤¥žŸde¢£¤¥žŸ<=jk\]fghi–—’“<=jk\]fghi–—’“BCjkfghi’“BCjkfghi’“jkº»úûjkº»úûRS¦§bclm–—RS¦§bclm–—š›TUno@AŽ89:;˜™ÐÑÒÓš›TUno@AŽ89:;˜™ÐÑÒÓJKNOpqrsª«àáâã–—JKNOpqrsª«àáâã–—JKNOpqrsª«àဖ—JKNOpqrsª«àဖ—FGØÙtuøùúûHIFGØÙtuøùúûHIvw"#¾¿îïvw"#¾¿îïŒdexy¤¥ÐÑJKŒdexy¤¥ÐÑJKvw !01234589z{|},-01fgfgvw !01234589z{|},-01fgfgdevw !014589z{|},-defgÂÃdefgdevw !014589z{|},-defgÂÃdefgVW~ìí ØÙúûVWVW~ìí ØÙúûVW¶·€†‡Š‹Œ¾¿TUêë’“Ö×¶·€†‡Š‹Œ¾¿TUêë’“Ö×RS¦§ôõøù‚ƒ„…ÊËRS¦§ôõøù‚ƒ„…ÊËBC‚ƒ„…TUpqÊËBC‚ƒ„…TUpqÊË€†‡Š‹Œ¾¿TUêëBCDE„…pq’“€†‡Š‹Œ¾¿TUêëBCDE„…pq’“ˆ‰†‡ÎÏÖ׈‰†‡ÎÏÖ×€†‡Š‹ŒêëÖ×€†‡Š‹ŒêëÖ×¶·€†‡Š‹Œêë@A¶·€†‡Š‹Œêë@AŽ‘š›œ®¯89Ö×ØÙ    ÚÛŽ‘š›œ®¯89Ö×ØÙ    ÚÛÄÅŽ‘š›œ¬­®¯Š‹Ö×     ª«ÌÍØÙÚÛö÷  ÄÅŽ‘š›œ¬­®¯Š‹Ö×     ª«ÌÍØÙÚÛö÷  ª«()’“ª«()’“:;NOTU”•¨©ª«êëtuÈÉ():;NOTU”•¨©ª«êëtuÈÉ()ÎÏäå–—˜™01¾¿NO  ÎÏäå–—˜™01¾¿NO  rs–—˜™0167üýæçŽ  rs–—˜™0167üýæçŽ  89„…Ž‘š›œ®¯  ÚÛ  ¨©¸¹ÚÛòó89„…Ž‘š›œ®¯  ÚÛ  ¨©¸¹ÚÛòó89Ž‘š›œ®¯  ÚÛ  ¨©¸¹ÚÛòó89Ž‘š›œ®¯  ÚÛ  ¨©¸¹ÚÛòó–—žŸ./øù–—žŸ./øù ¡fgš›°±²³´µ ¡fgš›°±²³´µžŸ¢£Ö×üýôõrsº»žŸ¢£Ö×üýôõrsº»¤¥¶·ÐÑXYäåæçòó^_º»¼½jk¤¥¶·ÐÑXYäåæçòó^_º»¼½jkÒÓ"#DE¦§ÞßìíîïÒÓ"#DE¦§Þßìíîï:;”•¨©ª«âãêëtuÈÉ()HI:;”•¨©ª«âãêëtuÈÉ()HI89JKpqrs”•¨©ª«êëtu()¶·¸¹89JKpqrs”•¨©ª«êëtu()¶·¸¹./01‘¬­®¯vwÂà  ./01‘¬­®¯vwÂà  ./Ž‘š›œ¬­®¯  ÂÃÚÛ  ./Ž‘š›œ¬­®¯  ÂÃÚÛ  ¨©Ö×°±XY†‡"#¨©Ö×°±XY†‡"#²³º»hi²³º»hi>?´µ¶·àáä嬭º»>?´µ¶·àáä嬭º»¤¥´µ¶·ÐÑXYàáäåæçòó^_¼½jk¤¥´µ¶·ÐÑXYàáäåæçòó^_¼½jk:;¸¹¸¹ÀÁÎÏ()`a–—:;¸¹¸¹ÀÁÎÏ()`a–—²³º»^_hi²³º»^_hi ¼½ìíxyz{ ¼½ìíxyz{BC€†‡¾¿TUæç&'pqrs’“ÊËBC€†‡¾¿TUæç&'pqrs’“ÊËpqÀÁÄÅäå    pqÀÁÄÅäå    pqŽÂÃÆÇÄÅpqŽÂÃÆÇÄÅpqrsäåÀÁÄÅÈÉ\]ÄÅ  pqrsäåÀÁÄÅÈÉ\]ÄÅ  ŽÂÃÆÇÄÅŽÂÃÆÇÄÅpqrsäåÄÅÈÉ\]ÄÅÚÛˆ‰pqrsäåÄÅÈÉ\]ÄÅÚÛˆ‰     ÊË     ÊËDEÌÍö÷äåDEÌÍö÷ä冇ÎÏRShirs˜™FGRSZ[òó†‡ÎÏRShirs˜™FGRSZ[òóö÷¤¥¶·ÐÑXYZ[äåæçòó^_¼½jkö÷¤¥¶·ÐÑXYZ[äåæçòó^_¼½jk‘œÒÓÔÕ´µæç‘œÒÓÔÕ´µæç‚ƒžŸÒÓÔÕÖרÙ$%‚ƒôõö÷´µ‚ƒžŸÒÓÔÕÖרÙ$%‚ƒôõö÷´µ1BClmxyžŸê뢣ÔÕÖרÙüý$%€‚ƒôõrsö÷´µº»1BClmxyžŸê뢣ÔÕÖרÙüý$%€‚ƒôõrsö÷´µº»rsxy|}žŸÔÕÖ×ØÙ‚ƒôõVWö÷”•rsxy|}žŸÔÕÖ×ØÙ‚ƒôõVWö÷”•¤¥‚ƒæç ÚÛ¤¥‚ƒæç ÚÛ”•ÜÝÞß”•ÜÝÞßÜÝÞß>?ÜÝhijkôõÜÝÞß>?ÜÝhijkôõNOpqrsàáâãêë–—NOpqrsàáâãêë–—pq¨©àáâãêëpq¨©àáâãêëäåèéNOäåèéNOLM^_æçèéLM^_æçèéLMäåæçèéNOLMäåæçèéNO”•¨©ª«àáâãêëtu()”•¨©ª«àáâãêëtu()~ìíðñ º»ÆÇúûXYòóôõ~ìíðñ º»ÆÇúûXYòóôõŽîïØÙŽîïØÙìíðñ º»ÆÇúûVWXYòóôõìíðñ º»ÆÇúûVWXYòóôõ¦§òóVWÔÕ¦§òóVWÔÕÂÃÄÅôõ„…ÚÛÂÃÄÅôõ„…ÚÛÐÑÌÍö÷JKäåÐÑÌÍö÷JKäåFGØÙÚÛtuøùúûHIèéFGØÙÚÛtuøùúûHIèéÈÉÐÑtuøùúûHIJK* + ÈÉÐÑtuøùúûHIJK* + rsžŸ¢£Ö×üýrsžŸ¢£Ö×üýþÿÒÓÜÝDEþÿÒÓÜÝDE¨©Ö×<= ¡\]®¯¨©Ö×<= ¡\]®¯”•˜™”•˜™NOö÷tu˜™NOö÷tu˜™1lm¢£23LM@APQZ[žŸèéêëÔÕÖ×ØÙ‚ƒôõö÷èé1lm¢£23LM@APQZ[žŸèéêëÔÕÖ×ØÙ‚ƒôõö÷èé89rs~ìíðñ º»ÆÇÈÉúûXYòóôõ89rs~ìíðñ º»ÆÇÈÉúûXYòóôõ    ¸¹`aœÎÏÆÇ    ¸¹`aœÎÏÆÇ8989JKš›œ    €ÚÛ¨©¸¹òó  8989JKš›œ    €ÚÛ¨©¸¹òó   ¡DE¦§Þßàá ¡DE¦§Þßàá ¡>?†‡äåòóÎÏ ¡>?†‡äåòóÎÏBC ¡:;vwÞßàáäåæçòópq¤¥ÎÏBC ¡:;vwÞßàáäåæçòópq¤¥ÎÏðñ VWðñ VWÐÑPQÐÑPQŒxyÐÑPQJKŒxyÐÑPQJK‚ƒ‚ƒÔÕôõÔÕôõ:;¸¹ÀÁÎÏ:;¸¹ÀÁÎÏžŸ ¡¬­ÔÕ*+ !žŸ ¡¬­ÔÕ*+ !@Avw"#¾¿@Avw"#¾¿‚ƒÔÕÖ×$%´µ‚ƒÔÕÖ×$%´µHItu !~€¼½&'”•°±ÂÃÄÅÆÇ*+Z[œ¢£HItu !~€¼½&'”•°±ÂÃÄÅÆÇ*+Z[œ¢£vw()bcpqžŸÔÕvw()bcpqžŸÔÕ"#\]*+RS*+"#\]*+RS*+žŸÂà  ,-TUžŸÂà  ,-TU–—žŸ./’“–—žŸ./’“rstu`a–—˜™0167TUæç$%<=ŒŽ¨©îï  rstu`a–—˜™0167TUæç$%<=ŒŽ¨©îï  LMˆ‰23Š‹ðñLMˆ‰23Š‹ðñ,-012345ÂÃ,-012345ÂÃrs^_`a˜™0167TUæçŽîïðñrs^_`a˜™0167TUæçŽîïðñŽ89 ¡  Ž89 ¡  :;¤¥:;¤¥üýÜÝ<= ¡\]®¯üýÜÝ<= ¡\]®¯Þß>?~ÜÝhijkôõÞß>?~ÜÝhijkôõTUno@AŽ 89:;˜™ÐÑÒÓTUno@AŽ 89:;˜™ÐÑÒÓFG`a²³´µ$%&'BCBC"#$%ÞßFG`a²³´µ$%&'BCBC"#$%ÞßDERShi:;DERShi:;FG"#$%:;NOFG"#$%:;NOHI\]²³ØÙHI\]²³ØÙJKxyàáÔÕJKxyàáÔÕ^_LMÔÕ^_LMÔÕäåèéNOÔÕÜÝ67LMäåèéNOÔÕÜÝ67LMPQlm  "#45PQlm  "#45ÎÏDERShi˜™FGRS:;ÎÏDERShi˜™FGRS:;@ABC€„…†‡¾¿TU@ABCDE„…’“@ABC€„…†‡¾¿TU@ABCDE„…’“¦§òóVWÔÕ¦§òóVWÔÕŒ¤¥¶·ÐÑXYäåæçêëòó^_¼½jkŒ¤¥¶·ÐÑXYäåæçêëòó^_¼½jkôõö÷ÐÑZ[äåôõö÷ÐÑZ[äåpqrsäåÄÅÈÉHI\]ÄÅpqrsäåÄÅÈÉHI\]ÄÅLMˆ‰^_RSðñ˜™LMˆ‰^_RSðñ˜™ìí,-`aØÙ !¬­ØÙÚÛìí,-`aØÙ !¬­ØÙÚÛvw()bcpqäåjkvw()bcpqäåjkPQde  :;¦§îïPQde  :;¦§îïÞß ¡fgÞß ¡fgÎÏDERShi˜™FGRSTU|}ŒðñÎÏDERShi˜™FGRSTU|}Œðñjk,-jk,-PQlmÊË  âã45PQlmÊË  âã45@A67àá  no@Anoþÿ@A67àá  no@Anoþÿvw()bcpq‚ƒvw()bcpq‚ƒ†‡ÒÓÎÏrsZ[ÄÅæç†‡ÒÓÎÏrsZ[ÄÅæç”•¨©ª«êëtu()HI¶·”•¨©ª«êëtu()HI¶·89BCˆ‰vwxyÐÑäåæç<=VWpq89BCˆ‰vwxyÐÑäåæç<=VWpqxy€ˆ‰Z[vwxy:;<=VWrsxy€ˆ‰Z[vwxy:;<=VWrs&'z{‘°±&'z{‘°±01|}23ÀÁ*+Z[01|}23ÀÁ*+Z[>?~€ª«>?~€ª«BCFGÖ×~€‚ƒŠ‹ª«BCFGÖ×~€‚ƒŠ‹ª«BCžŸFGÔÕÖ×ØÙ€‚ƒæçôõª«ö÷BCpqèéBCžŸFGÔÕÖ×ØÙ€‚ƒæçôõª«ö÷BCpqèéš›()„…š›()„…>?ˆ‰†‡ÎÏ>?ˆ‰†‡ÎÏPQìí,-ˆ‰Š‹Ž89:;ÚÛPQìí,-ˆ‰Š‹Ž89:;ÚÛÄÅìíPQ‘ˆ‰Š‹Žþÿ89:;<=~ª«ÌÍØÙÚÛö÷ÄÅìíPQ‘ˆ‰Š‹Žþÿ89:;<=~ª«ÌÍØÙÚÛö÷Š‹ÒÓŒì튋ÒÓŒìíPQìíTUno@Aˆ‰Š‹Ž‘89:;˜™ÐÑÒÓPQìíTUno@Aˆ‰Š‹Ž‘89:;˜™ÐÑÒÓPQŽ‘   ®¯°±PQŽ‘   ®¯°±–—VW./’“–—VW./’“ !~ÀÁ&'”•ÆÇœêë !~ÀÁ&'”•ÆÇœêë–—&'–—&'ÎÏRShi˜™FGRSŒÎÏRShi˜™FGRSŒ ¡š›¨©°±²³´µ ¡š›¨©°±²³´µ@Ano¶·>?œ>?@ABCDE@Ano¶·>?œ>?@ABCDEžŸÊËœâ㦧ÆÇÈÉžŸÊËœâ㦧ÆÇÈÉÜÝ<= ¡\]®¯˜™ÜÝ<= ¡\]®¯˜™<=îïde¢£¤¥<=îïde¢£¤¥de¢£¤¥žŸde¢£¤¥žŸlm¦§øù@AFGlm¦§øù@AFGðñ𛍩ðñ𛍩@Aª«®¯´µþÿ@Aª«®¯´µþÿ¬­°±vwxy¬­°±vwxyª«®¯´µþÿª«®¯´µþÿ ¡š›¬­°±²³´µvw ¡š›¬­°±²³´µvw`a ¡š›°±²³´µ`a ¡š›°±²³´µ ¡š›ª«®¯°±²³´µ ¡š›ª«®¯°±²³´µ¶· !–—¶· !–—¸¹  ¸¹ÎÏ`a–—¸¹  ¸¹ÎÏ`a–—jkìíðñ º»ÆÇúûXYòóôõjkìíðñ º»ÆÇúûXYòóôõ¼½TU˜™š›æç¼½TU˜™š›æçÎÏ–—¾¿lmŒÎÏ–—¾¿lmŒno¸¹ÀÁno¸¹ÀÁÂÃ"#01¦§ÂÃ"#01¦§pqrsäåÂÃÄÅÆÇÈÉ\]ÄÅpqrsäåÂÃÄÅÆÇÈÉ\]ÄÅìíðñ º»ÆÇúûÞßXY¾¿òóôõìíðñ º»ÆÇúûÞßXY¾¿òóôõ!" ÈÉXYôõ!" ÈÉXYôõlmžŸÊËš›œâã45ÆÇÈÉlmžŸÊËš›œâã45ÆÇÈÉhiÌÍØÙhiÌÍØÙ ÊËJK`aÎÏâãîïôõލ©üýRS  ÊËJK`aÎÏâãîïôõލ©üýRS  @AvwÐÑ@AvwÐÑþÿÒÓ@ADEþÿÒÓ@ADE^_LMNOÔÕÜÝ67LM^_LMNOÔÕÜÝ67LMŽ‘Ö×ØÙ  Ž‘Ö×ØÙ  Ž`aÖ×ØÙ !tu¬­ØÙÚÛŽ`aÖ×ØÙ !tu¬­ØÙÚÛÈÉÚÛðñˆ‰./ÈÉÚÛðñˆ‰./Þßþÿ>?ÜÝhijkôõÞßþÿ>?ÜÝhijkôõ ¡DE¦§Þßàáìí ¡DE¦§Þßàáìí ¡´µ¶·Þßàáäå ¡´µ¶·Þßàáäå $%deÊËJKÎÏâãî﨩üý $%deÊËJKÎÏâãî﨩üýBCŒ¤¥´µ¶·ÐÑXYZ[bcvwàáäåæçòó^_jkº»¼½89jkpqBCŒ¤¥´µ¶·ÐÑXYZ[bcvwàáäåæçòó^_jkº»¼½89jkpqBC¤¥¶·¾¿ÐÑXYvw‚ƒäåæçòó&'^_¼½BCjkpqrsÊËèéBC¤¥¶·¾¿ÐÑXYvw‚ƒäåæçòó&'^_¼½BCjkpqrsÊËèé úûèéîïbc úûèéîïbcŒ€†‡Š‹ŒXYê대†‡Š‹ŒXYêë   JK¼½ìí¨©z{   JK¼½ìí¨©z{ ÊËJKÎÏâãèéîïbc¨©üý ÊËJKÎÏâãèéîïbc¨©üýÚÛðñˆ‰./º»ÚÛðñˆ‰./º» ¡¤¥¶·ÐÑXYäåæçòó^_¼½jkÎÏ ¡¤¥¶·ÐÑXYäåæçòó^_¼½jkÎÏžŸ`a¢£ÔÕÖ×ØÙ‚ƒÎÏôõrsŽö÷./‘º»  žŸ`a¢£ÔÕÖ×ØÙ‚ƒÎÏôõrsŽö÷./‘º»  xyz{NOö÷øùêëxyz{NOö÷øùêëz{|} ¡ö÷øùêëz{|} ¡ö÷øùêë’“jkìíðñ º»ÆÇúûüýXYòóôõ’“jkìíðñ º»ÆÇúûüýXYòóôõ˜™úûüý¨©âã  ˜™úûüý¨©âã  xyz{|}PQŠ‹þÿ89:;<=~„…î拉ÌÍ  xyz{|}PQŠ‹þÿ89:;<=~„…î拉ÌÍ  Âà  ,- !TUØÙÂà  ,- !TUØÙ`a ¡fg𛬭®¯°±²³´µ`a ¡fg𛬭®¯°±²³´µXYZ[ ¡š›°±²³´µ  vwxy~àá ¡ÜÝXYZ[ ¡š›°±²³´µ  vwxy~àá ¡ÜÝ!"ÈÉXYôõ!"ÈÉXYôõ@A 89vw@A 89vwde  vwxy~¦§àáÜÝde  vwxy~¦§àáÜÝPQlm  "#PQlm  "#()úûüý`aLMNOÎÏÔÕôõŽÜÝ67LM  ()úûüý`aLMNOÎÏÔÕôõŽÜÝ67LM  rs|}rs^_`a¢£ÖרÙ67NOÎÏÔÕôõTUrsŽÜÝ67LMº»  rs|}rs^_`a¢£ÖרÙ67NOÎÏÔÕôõTUrsŽÜÝ67LMº»  rsxy|}¬­¼½tu`aÖרÙ01ÎÏôõŽ<=  rsxy|}¬­¼½tu`aÖרÙ01ÎÏôõŽ<=  lmZ[èélmZ[è鈉^_®¯ðñ˜™ˆ‰^_®¯ðñ˜™)*|}23ÀÁZ[)*|}23ÀÁZ[üýz{ÜÝüýz{ÜÝz{ÌÍz{ÌÍHIÎÏRShi˜™FGRSHIÎÏRShi˜™FGRS¶· !–—¶· !–—$%./BCBCFG"#$%èéNOÞß$%./BCBCFG"#$%èéNOÞßBCBCFG"#$%NOBCBCFG"#$%NOBC¾¿æç&'ÔÕÖ×pqrsÊËBC¾¿æç&'ÔÕÖ×pqrsÊË01:;()./01:;()./¢£*+‘¢£*+‘ÌÍ014589z{|},-ÌÍ014589z{|},-¢£()./45defg¢£()./45defg89z{01¸¹†‡89z{01¸¹†‡01|}23Z[01|}23Z[¢£./45defg¢£./45defgŠ‹6789bcÒÓŠ‹6789bcÒÓPQš›PQTUno@Aˆ‰Š‹Žþÿ 6789:;<=tu~˜™ÌÍÐÑÒÓPQš›PQTUno@Aˆ‰Š‹Žþÿ 6789:;<=tu~˜™ÌÍÐÑÒÓ€Z[|}PQTUno@AFGdexyˆ‰Š‹Žþÿ89:;<=xy~˜™îï  ÌÍÐÑÒÓÚÛ€Z[|}PQTUno@AFGdexyˆ‰Š‹Žþÿ89:;<=xy~˜™îï  ÌÍÐÑÒÓÚÛ€ˆ‰ÎÏZ[PQvwxyŠ‹þÿ89:;<=VW~ÌÍ€ˆ‰ÎÏZ[PQvwxyŠ‹þÿ89:;<=VW~ÌÍ@A67¶·¸¹>?œ>?@ABCDE@A67¶·¸¹>?œ>?@ABCDE@A67 ¡¶·>?ŒTUnoœ>?@ABCDEnoþÿÎÏ@A67 ¡¶·>?ŒTUnoœ>?@ABCDEnoþÿÎÏ@A¶·>?†‡TUœ>?@ABCDE„…@A¶·>?†‡TUœ>?@ABCDE„…@A¶·>?†‡TUœ>?@ABCDE„…@A¶·>?†‡TUœ>?@ABCDE„…ÎÏRShi˜™FGRSTU|}ðñÎÏRShi˜™FGRSTU|}ðñFGÈÉØÙtuøùúûHIFGÈÉØÙtuøùúûHIÐÑö÷úûJKÐÑö÷úûJKLMPQLMPQpqäå–—NOpqäå–—NOPQØÙúûPQØÙúû`aÎÏRS^_hi˜™FGRSTU|}æçðñš›`aÎÏRS^_hi˜™FGRSTU|}æçðñš›^_`a0167hi¼½FGRSTU|}æçðñ˜™š›æç^_`a0167hi¼½FGRSTU|}æçðñ˜™š›æçxyˆ‰ØÙvwxy<=VWrs”•xyˆ‰ØÙvwxy<=VWrs”•¨©Ö×°±XY†‡Œ¨©Ö×°±XY†‡Œ†‡ÎÏrsZ[>?Äņ‡ÎÏrsZ[>?ÄÅ<= ¡\]®¯˜™<= ¡\]®¯˜™¤¥¶·º»ÐÑXYäåæçòó^_¼½jk¤¥¶·º»ÐÑXYäåæçòó^_¼½jk  `aÒÓ  `aÒÓúûèéîïbcLMúûèéîïbcLM¢£|}./45defg¢£|}./45defgvw !z{|}./45defgfgvw !z{|}./45defgfg²³º»Þß>?ÜÝhijkôõ²³º»Þß>?ÜÝhijkôõÞß>?ÜÝäåhijk¼½ôõ89Þß>?ÜÝäåhijk¼½ôõ89lmno‚ƒÊËlmno‚ƒÊË@A67ðñno@AlmnoÊËþÿ@A67ðñno@AlmnoÊËþÿ  @AXYpqrs  @AXYpqrs  @AXY¢£Ö×ôõpqrsº»  @AXY¢£Ö×ôõpqrsº»89tu˜™89tu˜™¬­°±   vwxy~àáÜݬ­°±   vwxy~àáÜÝJK¬­  :;vwxy~àáÜÝJK¬­  :;vwxy~àáÜÝüýz{üýz{hiFGRSTU|}ðñhiFGRSTU|}ðñPQŠ‹þÿ  89:;<=vwxy~àáÌÍÜÝPQŠ‹þÿ  89:;<=vwxy~àáÌÍÜÝJKrs  €JKrs  €xy˜™lm‚ƒ„…xy˜™lm‚ƒ„…xyz{|}†‡TUþÿBCDE‚ƒ„…î拉  xyz{|}†‡TUþÿBCDE‚ƒ„…î拉  úû¨©ª«Ö×°±XY†‡úû¨©ª«Ö×°±XY†‡pqÈÉÚÛðñˆ‰pqÈÉÚÛðñˆ‰FG€Š‹^_FG€Š‹^_fgÔÕêëìíôõøùþÿhi˜™ŒÌÍhi|}~fgÔÕêëìíôõøùþÿhi˜™ŒÌÍhi|}~+,Œ¸¹ÒÓÔÕèéêëìíôõøù`aÎÏôõŽPQRS  +,Œ¸¹ÒÓÔÕèéêëìíôõøù`aÎÏôõŽPQRS  &'45¤¥ÀÁz{*+‘’“&'45¤¥ÀÁz{*+‘’“45€‘’“45€‘’“¨©&'FGæç”•^_¨©&'FGæç”•^_¦§bcfglm–—¦§bcfglm–—TUno@AŽ89:;tu˜™ÐÑÒÓTUno@AŽ89:;tu˜™ÐÑÒÓÊËš›45ÆÇÊËš›45ÆÇ  žŸÊËœâãÆÇÈÉ  žŸÊËœâãÆÇÈÉvw()žŸÔÕvw()žŸÔÕ¦§89 ¡¦§89 ¡¢£ÔÕ  ¢£ÔÕ  $%bcdexy¤¥ÐÑJK$%bcdexy¤¥ÐÑJKPQde  ¦§PQde  ¦§    ÊËJKÎÏâãìíî﨩üýz{    ÊËJKÎÏâãìíî﨩üýz{FG~€‚ƒª«FG~€‚ƒª«>?@A´µ¬­º»>?@A´µ¬­º»<= ¡\]®¯<= ¡\]®¯&'&'z{°±ÄÅ¢£&'&'z{°±ÄÅ¢£HI²³ØÙHI²³ØÙ´µ|}€àá´µ|}€àáDEØÙ¶·DEØÙ¶·<=01¸¹defg†‡<=01¸¹defg†‡>?¤¥´µä嬭º»>?¤¥´µä嬭º»¤¥¶·ÐÑXYäåæçòó^_jk¼½89jk¤¥¶·ÐÑXYäåæçòó^_jk¼½89jk|}PQvw"#¾¿ìíîï|}PQvw"#¾¿ìíîï)*°±|}ÀÁÂÃ)*°±|}ÀÁÂÃ)*°±€¼½|}&'ÀÁÂÃdefg)*°±€¼½|}&'ÀÁÂÃdefgtu !ÀÁ&'°±ÄÅÆÇ*+¢£tu !ÀÁ&'°±ÄÅÆÇ*+¢£tu !&'”•ÄÅÆÇ*+êëtu !&'”•ÄÅÆÇ*+êëNO”•¨©ÈÉNO”•¨©ÈÉ@AðñlmnoÊË@AðñlmnoÊËþÿŒÌÍþÿŒÌÍno:;¸¹  ¸¹ÎÏ`a–—no:;¸¹  ¸¹ÎÏ`a–— >?ŒÔÕ¬­xy¤¥ÐÑRSJK >?ŒÔÕ¬­xy¤¥ÐÑRSJK`aÒÓ`aÒÓòó()VW&'žŸÔÕÖ×òó()VW&'žŸÔÕÖ×&'ÔÕÖ×&'ÔÕÖ×89VWrsŽ~îïHIÌÍPQ²³ØÙÚÛúûVW89VWrsŽ~îïHIÌÍPQ²³ØÙÚÛúûVW89rsš›œ  ØÙÚÛ¨©¸¹òó89rsš›œ  ØÙÚÛ¨©¸¹òóüýNOÔÕÜÝ67LMüýNOÔÕÜÝ67LMÆÇÞߦ§¾¿ÆÇÞߦ§¾¿JK  vwxy~àáÔÕÜÝJK  vwxy~àáÔÕÜÝlmžŸÊËœâã45ÆÇÈÉlmžŸÊËœâã45ÆÇÈÉHI^_äåHI^_äårs`a˜™0167RSTUæçŽš›rs`a˜™0167RSTUæçŽš›FG./BCøù"#èéFG./BCøù"#èéz{"#TUö÷øùêëìíz{"#TUö÷øùêëìí|}ÒÓ"#DEPQ¦§ŒÞß¾¿êëìíîï|}ÒÓ"#DEPQ¦§ŒÞß¾¿êëìíîïxyz{|}ÒÓ"#DEPQvw¦§deþÿ:;„…¾¿ìíî拉  xyz{|}ÒÓ"#DEPQvw¦§deþÿ:;„…¾¿ìíî拉  ˆ‰^_hiFGRSTU|}ðñ˜™ˆ‰^_hiFGRSTU|}ðñ˜™ÎÏòóÎÏòóÞß>?ÜÝhijkôõÞß>?ÜÝhijkôõžŸÔÕÖ×ØÙ‚ƒôõö÷žŸÔÕÖ×ØÙ‚ƒôõö÷lm¦§øùFG\]lm¦§øùFG\]VW~PQØÙúûVWVW~PQØÙúûVW  ÊËJKÎÏâãî﨩üý  ÊËJKÎÏâãî﨩üý@A67noª«®¯@Anoþÿ@A67noª«®¯@Anoþÿ89š›œ  vwÚÛ¨©¸¹òó89š›œ  vwÚÛ¨©¸¹òó@AvwÐÑ@AvwÐѦ§ ¡¦§ ¡vwª«vwª«PQ‘‘   tu®¯°±²³ÌÍPQ‘‘   tu®¯°±²³ÌÍŽ‘š›œ®¯‘:;   ®¯°±ÌÍÚÛŽ‘š›œ®¯‘:;   ®¯°±ÌÍÚÛŽ‘89Ö×  Ž‘89Ö×  ’“Œ’“Œ@A67no@Anoþÿ@A67no@Anoþÿ@A67nonoœ@Anoþÿ@A67nonoœ@Anoþÿ®¯°±Þß>?ÜÝhijkôõ®¯°±Þß>?ÜÝhijkôõ@A¬­ vwxy@A¬­ vwxyÔÕôõÔÕôõØÙtu®¯ØÙtu®¯`aÒÓ`aÒÓfg‘œòóÒÓˆ‰æçfg‘œòóÒÓˆ‰æç`aØÙ !¬­ØÙÚÛ`aØÙ !¬­ØÙÚÛ:;Ö×°±PQÂà  "#$%Ž:;Ö×°±PQÂà  "#$%Ž:;01"#$%ŒŽ¨©:;01"#$%ŒŽ¨©–—&'bc°±²³–—&'bc°±²³:;”•¨©ª«¸¹êëtu()`a:;”•¨©ª«¸¹êëtu()`atu !&'|}ÄÅÆÇ*+Z[tu !&'|}ÄÅÆÇ*+Z[ÄÅjk,-ÄÅjk,-ÚÛðñôõ./‘º»ÚÛðñôõ./‘º»ÂÃ01¦§ÂÃ01¦§‘œèé23‘œèé23PQlmÊËš›âã45ÆÇPQlmÊËš›âã45ÆÇª«()NOÔÕÜÝ67LMª«()NOÔÕÜÝ67LMäåjk¼½89äåjk¼½89fgÔÕèéêëìíôõDERS:;fgÔÕèéêëìíôõDERS:;rsêëtu01<=  rsêëtu01<=  Z[>?˜™ÄÅZ[>?˜™ÄŦ§ÒÓ@ABCDEFG¦§ÒÓ@ABCDEFGBCHIrs‚ƒæç@ABCpqèéäå  BCHIrs‚ƒæç@ABCpqèéäå  þÿÒÓ@ADEþÿÒÓ@ADE¦§øù@AFG¦§øù@AFG¨©tuHI¶·¸¹¼½ÀÁ¨©tuHI¶·¸¹¼½ÀÁîï$%bcde¾¿¤¥JKîï$%bcde¾¿¤¥JKúûNOÔÕbcÜÝ67LMúûNOÔÕbcÜÝ67LMvwFG"#$%NOvwFG"#$%NOŒLMŽPQRSŒLMŽPQRS ŒÔÕ¬­ÎÏŽÐÑPQRS ŒÔÕ¬­ÎÏŽÐÑPQRS ¡Âà  ,-TU ¡Âà  ,-TUVW~ðñØÙúûVWVW~ðñØÙúûVWìíðñ º»ÆÇÈÉúûXYòóôõìíðñ º»ÆÇÈÉúûXYòóôõ&'|}23*+Z[&'|}23*+Z[lm¢£&'lmøù\]lm¢£&'lmøù\]¨©æçŠ‹”•^_¨©æçŠ‹”•^_:;¸¹¸¹ÎÏ()`a–—:;¸¹¸¹ÎÏ()`a–—Š‹º»67&'bcÒÓŠ‹º»67&'bcÒÓ<=|}¸¹ÂÃdefg<=|}¸¹ÂÃdefg)*vw !<=z{|}fg¸¹ÂÃdefg)*vw !<=z{|}fg¸¹ÂÃdefg+,ÔÕèéêëôõøùŒhi+,ÔÕèéêëôõøùŒhi¤¥¶·ÐÑXYbcäåæçòó^_¼½jk¤¥¶·ÐÑXYbcäåæçòó^_¼½jkÎϾ¿lmŒÎϾ¿lmŒ*+,-23ÂÃno¬­¶·ÀÁ*+,-23ÂÃno¬­¶·ÀÁBC„…†‡¾¿vw‚ƒäåæç&'BCpqrsÊËèéBC„…†‡¾¿vw‚ƒäåæç&'BCpqrsÊËèé-./0BCxyZ[€¾¿xyæç&'VWpqrs’“”•ÊË-./0BCxyZ[€¾¿xyæç&'VWpqrs’“”•ÊËØÙ tu®¯ØÙ tu®¯*+,-./¬­vw*+,-./¬­vw Ê˼½xyz{ Ê˼½xyz{   ÊËJK¼½ìí¨©xyz{   ÊËJK¼½ìí¨©xyz{Œ´µ|}€Œ´µ|}€Œ~àáŒ~àá´µ|}€àá´µ|}€àávwpq‚ƒvwpq‚ƒ45\]^_ÂÃÄÅôõ„…ÚÛ45\]^_ÂÃÄÅôõ„…ÚÛ8901¸¹†‡8901¸¹†‡fgèéòóˆ‰fgèéòóˆ‰23Š‹Œ23Š‹Œ’“Ö×01¾¿XY$%lmŠ‹ŒŽ¨©’“Ö×01¾¿XY$%lmŠ‹ŒŽ¨©89:;rs˜™0167æç"#$%ŒŽ¦§¨©ÈÉâã89:;rs˜™0167æç"#$%ŒŽ¦§¨©ÈÉâãôõ./‘º» ôõ./‘º» -.xyXYjk„…†‡¼½fghi€†‡¾¿TUrs’“”•žŸ ¡-.xyXYjk„…†‡¼½fghi€†‡¾¿TUrs’“”•žŸ ¡-./0xyZ[€ØÙVWrs’“”•-./0xyZ[€ØÙVWrs’“”•NOTUÆÇpqrs¸¹àá¶·¸¹ !ÎÏ`a–—NOTUÆÇpqrs¸¹àá¶·¸¹ !ÎÏ`a–—ˆ‰”•^_ ¡¼½TU\]ðñ>?˜™š›æçˆ‰”•^_ ¡¼½TU\]ðñ>?˜™š›æç`a¼½RSTUæç˜™š›æç`a¼½RSTUæç˜™š›æç !~&'”•œžŸ !~&'”•œžŸ !XYàá  de¤¥’“œžŸ ¡ !XYàá  de¤¥’“œžŸ ¡XYZ[’“žŸ ¡XYZ[’“žŸ ¡¤¥ÀÁ&'°±ÄÅ¢£ê뤥ÀÁ&'°±ÄÅ¢£êëxyz{|}:;þÿ„…î拉  xyz{|}:;þÿ„…î拉  89žŸÂÃÞß01ަ§¨©¾¿ÆÇÈÉâã89žŸÂÃÞß01ަ§¨©¾¿ÆÇÈÉâã89š›œ  01üýÚÛ$%ŒŽ¦§¨©¸¹ÈÉâãòó89š›œ  01üýÚÛ$%ŒŽ¦§¨©¸¹ÈÉâãòóÄőЋª«ØÙÚÛö÷ÄőЋª«ØÙÚÛö÷*+,-23ÂÃ`aØÙ !no¬­ØÙÚÛ*+,-23ÂÃ`aØÙ !no¬­ØÙÚÛ‘   tu®¯°±‘   tu®¯°±‘   &'®¯°±²³‘   &'®¯°±²³ &'°±²³ &'°±²³œÒÓÔÕÖ×$%´µœÒÓÔÕÖ×$%´µ!"89ª«tuHIno¶·¸¹¼½ÀÁ!"89ª«tuHIno¶·¸¹¼½ÀÁ89š›œª«  ÚÛHI¨©¶·¸¹¼½ÀÁòó89š›œª«  ÚÛHI¨©¶·¸¹¼½ÀÁòó¢£Ö×ðñôõrs./‘º»¢£Ö×ðñôõrs./‘º»!"HI¶·¸¹¼½ÀÁ!"HI¶·¸¹¼½ÀÁÆÇÞߦ§¾¿ÆÇÞߦ§¾¿,-HIno¶·¸¹¼½ÀÁ,-HIno¶·¸¹¼½ÀÁ./01¬­®¯45ÂÃ./01¬­®¯45ÂÃrsZ[>?ÄÅrsZ[>?ÄÅ  žŸÊËš›œâã45¦§ÆÇÈÉ  žŸÊËš›œâã45¦§ÆÇÈÉ89pqâ㞟ÊËœâ㎦§¨©ÆÇÈÉâã  89pqâ㞟ÊËœâ㎦§¨©ÆÇÈÉâã  BC‚ƒ„…¾¿æç&'pqrsÊËBC‚ƒ„…¾¿æç&'pqrsÊËPQ‘Š‹þÿ89:;<=~   ÌÍPQ‘Š‹þÿ89:;<=~   ÌÍ ¡>?ˆ‰†‡òó@AÎÏ ¡>?ˆ‰†‡òó@AÎϺ»TUno@AŽ89:;˜™ÐÑÒÓº»TUno@AŽ89:;˜™ÐÑÒÓŠ‹TUno@AŽ6789:;˜™bcÐÑÒÓŠ‹TUno@AŽ6789:;˜™bcÐÑÒÓJK¢£àáÔÕ  JK¢£àáÔÕ  €ˆ‰Š‹Ö×€ˆ‰Š‹Ö×,-ÄÅ,-‘`aŠ‹ØÙ !ª«¬­ØÙÚÛö÷,-ÄÅ,-‘`aŠ‹ØÙ !ª«¬­ØÙÚÛö÷ÂÃÄÅŽ‘š›œ®¯ôõ`aˆ‰Š‹ØÙ:;   !„…ª«¬­ØÙÚÛö÷* + ÂÃÄÅŽ‘š›œ®¯ôõ`aˆ‰Š‹ØÙ:;   !„…ª«¬­ØÙÚÛö÷* +   vwxy~àáÜÝ  vwxy~àáÜÝ`a$%BC"#Þß`a$%BC"#Þß´µ~€àá´µ~€àá89üýަ§¨©ÈÉâã89üýަ§¨©ÈÉâãDEÌÍö÷äåDEÌÍö÷ä呜æç ÒÓæç‘œæç ÒÓæç1BC23LM@APQ‚ƒæçBCpqèé1BC23LM@APQ‚ƒæçBCpqèé !¤¥ÀÁ”•ÆÇ¢£êë !¤¥ÀÁ”•ÆÇ¢£êëìí   "#89ìí   "#89%&îïúû  &',-FGPQRS%&îïúû  &',-FGPQRSðñüýðñüý²³òóôõö÷úûüýþÿ  ()HIRS²³òóôõö÷úûüýþÿ  ()HIRS%&23´µòóôõö÷úûüý  XYbc%&23´µòóôõö÷úûüý  XYbc²³òóôõö÷úûüý²³òóôõö÷úûüý>?øù>?øù%&îïòóôõö÷úûüýþÿ  &'(),-FGHINOPQRS%&îïòóôõö÷úûüýþÿ  &'(),-FGHINOPQRSðñòóôõö÷úûüýðñòóôõö÷úûüýòóúûþÿ  ()HINORSòóúûþÿ  ()HINORS>?š›ÚÛ"#^_>?š›ÚÛ"#^_:;HI:;HIº»:;º»:;%&îïúû  &',-FGPQRS%&îïúû  &',-FGPQRSìí   $%89ìí   $%89>?ìí   89>?ìí   89%&23îïòóôõúûþÿ  &'()*+,-FGHIPQRSXYbc%&23îïòóôõúûþÿ  &'()*+,-FGHIPQRSXYbc*+,-<=>?FGVWXY\]*+,-<=>?FGVWXY\]()BCNO()BCNOBCPQBCPQúûüýþÿNOúûüýþÿNOZ[`aZ[`aúû()BCNOúû()BCNOìí $%ìí $%"#@A"#@A>?îïðñö÷¾¿>?îïðñö÷¾¿ !23 !23 ìí"#./4589@ALM^_ ìí"#./4589@ALM^_ìívw $%ìívw $%%&îïúû  &',-FGPQRS`a%&îïúû  &',-FGPQRS`aòóúûþÿ  ()BCHIRSòóúûþÿ  ()BCHIRS  *+<=FGVW  *+<=FGVW%&îïúû  &',-<=FGPQRS%&îïúû  &',-<=FGPQRS "#./014567@A "#./014567@A <=Œ./0145XY <=Œ./0145XY !2367 !2367 <=Œ"#./0145@AXY <=Œ"#./0145@AXY./2367./2367>?ìí   "#89>?ìí   "#89:;HI:;HI*+,-<=VW*+,-<=VW>?VWXY\]>?VWXY\] "#./45@A "#./45@A()BCPQ()BCPQDE\]`aDE\]`a%&îïúû  &'*+,-FGPQRS%&îïúû  &'*+,-FGPQRSòóúûþÿ  ():;HIRSòóúûþÿ  ():;HIRS>?ŒxyÐÑJK>?ŒxyÐÑJK"#LM^_"#LM^_úûþÿNOúûþÿNO%&îïúû  &',-BCFGPQRS%&îïúû  &',-BCFGPQRS%&îïòóúûþÿ  &'(),-FGHIPQRS%&îïòóúûþÿ  &'(),-FGHIPQRSTU\]`aTU\]`a*+<=>?VWXY\]*+<=>?VWXY\] %&23<=Œxy°±ôõ  0145>?VWXY\]`abc %&23<=Œxy°±ôõ  0145>?VWXY\]`abcZ[`aZ[`a>?DETUVWXY\]`a>?DETUVWXY\]`a"#LM^_"#LM^_%&&'DETUXYZ[\]`abc%&&'DETUXYZ[\]`abc%&23ôõ  XY`abc%&23ôõ  XY`abc\]de¼½$ % \]de¼½$ % fgxyz{‘’“žŸÊË& ' fgxyz{‘’“žŸÊË& ' îïhino”•²³ îïhino”•²³ XYjk„… ¡®¯ÈÉXYjk„… ¡®¯ÈÉlmŽÀÁÒÓ  ! " # lmŽÀÁÒÓ  ! " # îïhino”•ª«²³îïhino”•ª«²³pq„…†‡ˆ‰Š‹¢£ÎÏÜÝàá  pq„…†‡ˆ‰Š‹¢£ÎÏÜÝàá  ~‘rstu~‘rstu~‘rstu~‘rstuvw€Žvw€Žfgxyz{€‘’“ÊË& ' fgxyz{€‘’“ÊË& ' fgxyz{‘’“ÆÇÊËúû& ' fgxyz{‘’“ÆÇÊËúû& ' |}Žüý ! |}Žüý ! Z[~¼½Z[~¼½vwxy€ŽÆÇÎÏúûüývwxy€ŽÆÇÎÏúûüýXY‚ƒ†‡Š‹ÈÉÌÍàáXY‚ƒ†‡Š‹ÈÉÌÍàáXYjkpq„…†‡ˆ‰Š‹®¯ÈÉàá  XYjkpq„…†‡ˆ‰Š‹®¯ÈÉàá  XY^_pq‚ƒ„…†‡ˆ‰Š‹˜™àá  XY^_pq‚ƒ„…†‡ˆ‰Š‹˜™àá  ^_pq„…†‡ˆ‰Š‹˜™àá  ^_pq„…†‡ˆ‰Š‹˜™àá  pq‚ƒ„…†‡ˆ‰Š‹àá  pq‚ƒ„…†‡ˆ‰Š‹àá  Œ¤¥ª«ÀÁÂÃÄÅÒÓŒ¤¥ª«ÀÁÂÃÄÅÒÓlmvw|}€Ž ¡¾¿ÒÓ  " # lmvw|}€Ž ¡¾¿ÒÓ  " # '(bcfgxyz{‘’“žŸÆÇÊË& ' ( ) '(bcfgxyz{‘’“žŸÆÇÊË& ' ( ) '(bcfgxyz{‘’“ÊË& ' '(bcfgxyz{‘’“ÊË& ' hino”•ª«hino”•ª«–—˜™š›œ ¡¦§®¯ÚÛ–—˜™š›œ ¡¦§®¯ÚÛ^_ÆÇ@A†‡ˆ‰–—˜™¶·¸¹0 1 ^_ÆÇ@A†‡ˆ‰–—˜™¶·¸¹0 1 –—𛦧ÐÑâã–—š›¦§ÐÑâãXY–—œ¦§®¯XY–—œ¦§®¯bcfg‘žŸbcfg‘žŸ23XY$%<=vw¢£¶·DERSjkŽ–— ¡¾¿Ö×ÚÛ, - 23XY$%<=vw¢£¶·DERSjkŽ–— ¡¾¿Ö×ÚÛ, - pq¢£ÊËÎÏÜÝþÿ  ( ) pq¢£ÊËÎÏÜÝþÿ  ( ) Œ¤¥ª«ÄÅŒ¤¥ª«ÄÅ–—š›œ¦§®¯–—š›œ¦§®¯†‡¨©´µ¶·º»¼½ÐÑÔÕÞ߆‡¨©´µ¶·º»¼½ÐÑÔÕÞßXYnoŒ”•¤¥ª«¬­°±ÂÃÄÅêëXYnoŒ”•¤¥ª«¬­°±ÂÃÄÅêë~89VWXYª«¬­°±ÂÃ~89VWXYª«¬­°±ÂÃXYjk„…–—œ¦§®¯ÈÉXYjk„…–—œ¦§®¯ÈÉ~XYª«¬­°±ÂÃ~XYª«¬­°±ÂÃîïhino²³îïhino²³¨©´µ¶·¸¹Þߨ©´µ¶·¸¹Þߘ™¨©´µ¶·¸¹Þߘ™¨©´µ¶·¸¹Þ߯Ç@A˜™´µ¶·¸¹Þß0 1 ÆÇ@A˜™´µ¶·¸¹Þß0 1 ¨©º»¼½ÐѨ©º»¼½ÐÑZ[de~¨©º»¼½ÐÑZ[de~¨©º»¼½ÐÑŽ ¡¾¿Ž ¡¾¿lmŒÀÁÂÃÄÅÒÓlmŒÀÁÂÃÄÅÒÓ'(~XYŒª«¬­°±ÀÁÂÃÒÓ'(~XYŒª«¬­°±ÀÁÂÃÒÓŒ¤¥ª«ÀÁÄÅŒ¤¥ª«ÀÁÄÅz{€‘ÆÇúûüýz{€‘ÆÇúûüýXYjk‚ƒ„…®¯ÈÉXYjk‚ƒ„…®¯ÈÉfgxyz{‘’“¢£ÊË& ' ( ) fgxyz{‘’“¢£ÊË& ' ( ) \]‚ƒÌÍ\]‚ƒÌÍpq€¢£ÎÏÜÝpq€¢£ÎÏÜÝš›¨©º»¼½ÐÑâãš›¨©º»¼½ÐÑâãlmŒŽÀÁÂÃÒÓ  " # lmŒŽÀÁÂÃÒÓ  " # †‡01¨©ÔÕ†‡01¨©ÔÕRSêë ¡Ö×RSêë ¡Ö×%&'(23~$%89„…LMúûØÙ%&'(23~$%89„…LMúûØÙ–— ¡ÚÛ–— ¡ÚÛpq¢£ÎÏÜÝþÿ( ) pq¢£ÎÏÜÝþÿ( ) ¨©´µ¶·¸¹Þߨ©´µ¶·¸¹Þßpq‚ƒ„…†‡ˆ‰Š‹àá  pq‚ƒ„…†‡ˆ‰Š‹àá  š›ÐÑâãš›ÐÑâãrsÀÁBCäå    rsÀÁBCäå    †‡ÒÓrs¼½TU˜™š›æç†‡ÒÓrs¼½TU˜™š›æçtuˆ‰:;¦§¨©ª«èétuˆ‰:;¦§¨©ª«è骫êëìí ª«êëìí îïêëìí îïêëìí LM`a0167îïðñLM`a0167îïðñLMˆ‰2367îïðñLMˆ‰2367îïðñ89rsš›œìíðñ   º»ÆÇúûÚÛXY¨©¸¹òóôõ89rsš›œìíðñ   º»ÆÇúûÚÛXY¨©¸¹òóôõìíðñ º»ÆÇÈÉúûXYòóôõìíðñ º»ÆÇÈÉúûXYòóôõÄőЋª«ØÙÚÛö÷* + ÄőЋª«ØÙÚÛö÷* + "#\]^_žŸøù"#\]^_žŸøùz{€ÆÇúûüýz{€ÆÇúûüý|}€ÆÇúûüý|}€ÆÇúûüý¢£ÜÝþÿ  ( ) ¢£ÜÝþÿ  ( ) pq„…†‡ˆ‰Š‹¢£àáþÿ  pq„…†‡ˆ‰Š‹¢£àáþÿ  rs`aÎÏôõŽ   rs`aÎÏôõŽ   pqˆ‰âã89  ÈÉ      pqˆ‰âã89  ÈÉ      ˆ‰      ˆ‰      ‘   ‘   rs‘   rs‘   îïhiêëìí îïhiêëìí ˜™ØÙ‘¬­®¯  ˜™ØÙ‘¬­®¯  noüý !ŒÚÛ  noüý !ŒÚÛ  rsÀÁÄÅBCäå    rsÀÁÄÅBCäå    hiÀÁäå    hiÀÁäå    ˆ‰<=      ˆ‰<=      –—˜™01üý  –—˜™01üý  |}¢£ÔÕ    |}¢£ÔÕ    xyz{|}þÿ„…î拉    xyz{|}þÿ„…î拉    lmŽÒÓ  ! " # lmŽÒÓ  ! " # lm|}  ! lm|}  ! lmŽÒÓ  " # lmŽÒÓ  " # \]de$ % \]de$ % fgxyz{‘’“ÊË& ' ( ) fgxyz{‘’“ÊË& ' ( ) ‘¢£ÊËÜÝþÿ& ' ( ) ‘¢£ÊËÜÝþÿ& ' ( ) úûÚÛö÷* + úûÚÛö÷* + 23òó$%<=vw¢£¶·ØÙDERSøùúû ¡, - 23òó$%<=vw¢£¶·ØÙDERSøùúû ¡, - z{01. / 0 1 z{01. / 0 1 z{./01ÆÇ@A˜™¸¹. / 0 1 z{./01ÆÇ@A˜™¸¹. / 0 1 (œ0€™Ó@fÝ>+ 7§À,{3âB@Uü‰wdÌ$@giM¯Ý?@Ü*ü£ 5@~##K@n1Àmè?à!À˜¿>.¸Ú@èQ«ˆ´iA ¹SÔKÀ|¥ÛÄÛt@µUòuµøù@„TØ?µ¡Àô;\ÌÙC&À ¹ý‚ëÆX@‘ßHù¥@S›Gxœ@¯(‘ÝuÛ@sAº2§ÄÀ¶yòýS@¶¼±$j\@{”Qû{Û@©’å…–ä@¤ò3´§>Æ@qôRkÎ@Û¿Øã@©EobYA—+¿Ç–<À狻ąT@rþ‘g@ƒÄ$œÚ@w©ÌñÏ”@@µ¨7³Þ2@V‘óƒØ@V@Uó @a&–A“UlËœiA}iäÞ“òU@±ÑÙ:Ü$ÀJO“¢¶Àñs¯ ŽzÐÀO‹õIžÜ@ÀT™$@Uà&@ä'qò….Ó@ûû»¬—ß¡@€n‘î­²ªÀ<ó㡺ÿÞÀŠ Áì"•@Ñc§që@ '2m-À²7`õ~|˜@,Î#ŽÁ ^@˜/÷œÀAõ?Kœ®Ûëë@ÒoccÎd@›¬Z¬òÀp@ðpŸ¸‰Y@p…þXoS$@ \Á?.}Àìáx‡|ó«ÀS‹Õä*KÀZ¶¯üüh@Jî&wø@tÎz¾¶ÆÀVÉax;r@ù©Vò çtÀØ÷îׯp@ûº!´ÒD@´±7t>HfÀÉÁ˜üWÀLëI¼0bÀ]Îö2¯ØÀgY¤3ÝÃ@@vV‰ì?Žo@øÃý#©À@JƒCª2@=Pàï0Àïûò0rM¬Àiù? ¡É¾@„å±Â3À­öà‘áÜ^ÀÏÄRïÐic@61EZ¶3âÀ†û¹Æ> ¾Àªÿ¹¸aoÀ`Hv8Z:³À‡’ä#ÀtCÛ†ó?9vnýo¹MÀî·*u1X@`6›™@˜‡ç‚@Æú3ú&I£@2íeÏkfŒ@ŽYiÛ®K@/¦äéÓÀ…Y}áÖUjÀ†LUºD¤À—«µý#FAFèg5“ô@}3e׌{@(»È;¼£À¿‰Ú†Yn@!> [èLB@ Á¦çúˆ¢@ªk2Š#\d@„4.·YÀŸû˜È×Dâ@×{F¤4¶ú@Òÿ.ÊÆGÞ@=éÙŠÆâ5À_CÂ!`ë=À=½¬Ý…¦ßÀîκV»Ä@‡Ò½ßcAÀð2G‡³@âÓb‹ôf@3í¦Ê=D@е¡@¡3¸„tŽÀ!­ i¯]@eQç—k@•‰™¡ƒ–ÀxŠrccÛI@0ôbZ‘À]÷>ËQbÀ§<ÀoÒ1•Ào70ãŠTiÀ…Á’¢À;¥¤žÌˆÀÎQr“sZþ?žNjöpG@m¶!g³çæ?ƒEÈC-@R•©ˆ9@ÉYÿ[ºí?Úãqløí¿Y3Ö$:×Ó?”›ùI›ÐÀíM0½Sª@ֈϾÝY@‘³¾%ŠW¬@€g3kvD¶@óð`дtÐ@÷Ô/7-‘@¶<C” ß@T·‰ù@€@€rF¦»@€cøùQN@Ya&#y@#ä¶Ö±Àú±çÄÀC2äØzTf@±”H2`²@bÁ¬‹#~@—:›»À«@àçN‘øÀ_ìÆ¤–Æ@> ÛSt.`@z*V« Ø@rùë ³¬ÀÀ¾Út`oR#ÀHnÕkœ@¢‘ëÄV@€mUäÓ–¾À÷EçZ2ÀU‰5ÜœEâ@Ct"ù¾@²Pÿ;OØm@‡DØjAý²@¦aeü5ÀgŒÏâÔ@°ÛfȺÃß@ÖðžäfpÄÀM$1ò.E@¡Z¾––³Àóå6ŸKtX@wŠHª;ï@«îVËìÁ@Æâ³Ìjì»@ígÇ xÁV2¶”ÀÔ@|ãIð… À¢Ë—S§Ž@Çç•-^ÀS”0 ÛgÀnŽZO÷¶À(Ž›”‡:‡@`‘»f”)Ì@qmæþ,•ò?äC ÈÀ`×  ¿Àðè?S"ƒ@x²¾H˜l£@¯vb¨"xÀ§¤§â£@ëëÕ k—@lÞ¾mJч@ÚNç1$C ÀÕóû5Ž@D„4Gý‡¡Àf…ûpƒÀ³ {–miBÀ(hÄ)À„Eõ³–ð¿Àé$Ç_@?û{¨ ÀÜŽŽkr4À ó*È«ëV@øÑòð>Œ0ÀX(ˆZ¶@ÀÄ"‹©6Àj¿é(0@ºbɘßÞ)@cË4¸«û@ñrÔÅëê?±t™ÇÀø)°ÕgB@P†ßvÄnì¿xJxt´#À^>`¯åõ¿ƒÿ-I¯‘âÀŠoáP`åÀ³¿ëµ7å¿(§Uqêú?„¯¡ðê%QÀO´°_<î?sÒÿ¨’˜@3A¶jÃÒ+ÀGbK¨èÀj.ÀWçá7À”}º2!À˜{6Bjê<ÀŽö鱺À `µÂV@c\Þª1sKÀS:)þÿ€ À”±èÀ©ÕeéÀÐ*…$ÓÀ?qö #tò¿=Û6Á À²:3á”’OÀ}„›D(VÀÐÔ?Úº²1À°½T³TÀ©Ò‹yYÀ H9¨nÀÅNœÀôûlÀðFu ‰AÀuA×VçÙ¿ <)bb%@’:dVFBÀrîk¢"ÀWË÷}GÖ4@ŒÛ¾®&/@‚îÆlTÍ%À ØftÂ0À¼ÜÛõ´êÀÒŸ!E+ëaÁy ·â,;9@tkñ`sÓ6@YÇâÿr6A@´rS¿G¦0@á ¹­?@í#T6å W@ùKþÌã<Àõ€ÇD‘ü"@ÆÓ9ômZÀíAaB‰xÀ!Þ@ŽúÓ @ >ÿçv`@Òm*yžqÀ®íâr¡YÀ‡R5ã{Àn¥tuÜ=À†õ$¾JâGÀfžV+ÝlÀ†-”'œ@Ë@M«ãÒÀbŸ[‡Öçò?…·²ÂÓÀû'T1ˆ…Àâ®a6ž±@§Ö^~ªÏE@ØËþä™8À„)ÿ:LÃ)@cÙ0”;À"OSQœ@³¹3òÑÕà?£ûË0ÆÀ…µ>±AêÍÀ ké›ÐcÀ ß¯­Å¶E@¨˜Ñ®nÀõÁ£2ÚBÀ‚z`F@ù ‰Ž3PÀ b³âIiÀåfP ?¤XÀ¯Þ™ûèÀLöïà¶HÀÐ…nk!¾…ÀZïH¯yrÀñl›‚*åqÀh. ifÀµã¨¶f@+Jÿ‡}¿\@öÿŠ»o#@é~¦}„G@e6'ÍC)À<*é„À‚‹.&G@Ãô´ô:ñ1@ëß7XúÊd@@å @Àp 1] @H§ð»àÂÀ‡a‡µZ@PŠHH >À3o#6´,ZÀØ[ÇÂC8À4î–ƒ<9ÀàåÉ8øß¿hd;Þr"A@/åž?bß7À° ` ú?¹5ö"*3ï?öd ÌAgÀCáÄjYƒQÀ1ÔƒüQÔBÀÌ«ÃIå1À £ü2ZZS@·cWeQ@NÖ|ÙÂI%@E Db1QÀªW5Y™y À€¢¸mŸ @srи0¹QÀ’öÇ@@‘Êă_f¯À°¶z¬Ö@µÚ_ùÔMÀÁÝÅçVÿ¿3¡!Æ$y8À´u¸áx%À5Æöp¶WÀ]WÌ.ÉlA@€Ulîý8@kÙí¸FT¨¿I°mßEPÀ<í:-J@žÆŸëíQ@]:#Þ ÐUÀ&€tžéz@í¸bú:ÔÀÝÔîdMŠã?>|;…‰k@¨†žÆÃˆ@˜‰i|{†@òƒJî„‹ÀŒ:m6ÍXÀëüòtÞmeÀר.ß FÀSJÉûÖìX@‘!ŸˆÓyBÀt Mn6@‡MŠæï&@Ô]úM[1Àâ¹?0>À„8‡k¦µS@i7†p¥Ê:@è=Īץ6ÀÌ÷]T À·8QÖ•Š(À¢Î_EÀ…ÃgöÈ`@˜’å¸b@@¥K°7Ž©>Àx¶ÔXŒô&À"…'¼æ9!ÀêI¬A/ÀÖÊ#T«鿟ŒL =пs¾pÆE?„Àý1H_!4‘@Œè~P<÷o@ÚÿSb@_°fñÒû¿ê"³›ùP@`h@b÷0À³†î²±+S@TÅØ)§¦-@Ô·}¹;TSÀ dqÈf’úÀR·÷¥2ÞÀ}ÏUwáSÀxù{ìê>Àö(í(vÂÀØh‚×À$ÀÐs#¤#À}³K¥‚pÀß¶nÒa“$@×rÈžD± À" ™4:ˆÀÐ*`‹B/²@l˜0º«%@>Ø$[ø-!Àÿ “÷KÀ~ L¹Àåo÷]ûEÀ"w Þ…G3À,öòÍc[@óLFT@=o¯?‰<@ýW‡ú±I0@æSÊ*¯6(ÀÙ¹‹ÒÄæÀµa¯+ÀWw´0¢@â×pÖBƒãÀvóÕ‡ELZÁ‹F*!ö›8À=6o’6#@ ÷ôR;Šô¿1nüã4‘?Þ2ü®VGÀ§«/”—BÀBÖ¨TôÀS^¶Àkd@`YÃ¥œMÛÀ|5¦­®±äÀ­ÏÂNÈV2@îˆÁËŠ3@0LƒÈ.ÀÎL¹³¢ÀÐEŒ>ýº=@18 ®÷VCÀ®Ñ …:Ž?8îs LVÚ?-´=iÒ¥hÀçrK¹ƒN@¤–É­ueÀ­Âkü±5EÀ?-]Àíf9%.-Ày>37À!âßQeÀ÷´Œ=‹®ê¿õó´LIô¿Ívò“…gÀ ‘b¬û‚ý?_Ï«ÇTz@Ó•&pwçf@1$7w’,P@óÕJ/ËZgÀGŒåbJÀ±ÝMn WÀJÔ;̇0@ïîså->D@q>y­¦Œ8ÀZã–?–.O@ZÝœ'º‰À"îD®;z[@`,¸•—V8Àª:jy"ÀQ„½_YÀNÈݲ9ëQÀÜP‚(kFT@±I…ÒPÀ–¢«%£YA@%¢[¤¢+@]¡™"X&Àu÷M|l&Í?ÌUnöÃß@=Þl®‰˜'@ÛJÇU”%ÀseWÔBN'ÀÌy4Î@»ìÎ× @Ó'B$ù5"@üð¢ÅÿÀU뎭¤!A@›È ¼õÿ?KQüRATÀ9T„‚ï¢7Às‹wÙƒ!ò?;ÛTð#À‘·ÝÿnÅTÀ¼Û-B-À5ù•™ÃÒ(Àfyd ‚Å(ÀÕ*¨8²Œ3À¾5¼y©¼'@21³EÒqyÀÿK©á!a@©îÇP¹KÀC…‹ýÒV@ÿèK­Æk@èÅzÔ ŸIÀÙ¿ÿÔ!(ÀɪJh¹ÁA@Ý÷Ì!¶~xÀUe—ÐÀ®Ë£Ø9„À}¢û%@JðQ ~ÿ¿Ï@Ù(ãñ¿£)ÿ…d@,ÌfàÉ%À9b ¬A@ùq×DÀ³1Ô\Àžñ2êãËÿ¿,ãð4-ù[Àް×ÈxÀ+î¬ò¾4ÀXè%½‚¶\@#&·):¥Àãâ*ŒÚWã?Ë’¾ ÷QÀ5#a-‚+À 7t— yñ¿¦ùôÛ/6@Å),VÁ!@b¿dLO@Õ±°h?Òá?.€ýëý¿Kž UÚ¿™9c$6¹ù?› ÞQ€ó?¿ðR"@Œ5äM˜Ð$@Õ°°Æƒu@„tþJ«µ*ÀfÆÉ“aÀ.¹GIï?X¾§+¾ûÀ¶ÐíMÚâùÀÝÜ{󠘢@±}å¸âðVÀr¡þ,¼,6À ÔxXç-…@W8L#HžÀë >x¦ƒ0Àá·ƒ7±@â·UDØÀ€ÕØI+¹@†Í­·©P @ÝÏ÷íZœ @Š-uÖ¬ -À£}Öë!ÀŒÅDPËQÀÐ#;N#u?Àó5¨À@‰@ p ó³À¹kãå½È7À,³à=£L@ý;%Ð?}ÀÍÆô+ÜVoÀbö¨S|‘‡ÀcÜ?Ao­@øÕ'Ñ>Àäû6¿·"AÀ‘}d®‰¿UÀœ]T²hqÀñIABåY@cá0ž[@u÷ôŠLÀ®u­hzEÀg7on!a,@ºŸ„±>Àƒüž«èî5ÀcFdØ#À,ÏŠ·ì$:@£-ïÉ–  @ ³²àO5À¯•„yÖ’@‹iÝ>°£BÀqÌ·!@­Ë•-_ÀÿwSÍ@.¶‹ï&À[;à}µ À¨¯Ah/UÀ‹XÐÍÝ*:À|T:¦‚IÉ¿H6…ô¨Eó¿&":|'4Àî9¥½ÀKär7ÀŸ™yxÁÀWäpv7@Îj©j.4@ÚèI<[âBÀK“ÍwE4À¤àK,„*À±ô°ù À À)¦iSY'À¹ìäž}J@ÐZÕþÀªE!G^ÀöQkW 6@þÀV½c1Àå3 GÂñ¿=Ÿç¤…À$}kÎÝüˆ@Q±z_z~@{Îk‰0<@úxvͺMÀ°.ê°‘.QÀÈ“¿½4R@©¶\ $¢OÀ ºU@E@µ’­mP@ÀC]¦;ŠJIÀYhL€ ­Àÿ`Ïìýqj@LñPû¾êIÀÛVÕRHÀ¿±ôæ91À´ ©¥l*!À„Z!½l¤!@ämH$û@½ºBôOš@@…{HŒÂà\Ààû¨+Àç à WÀX¸3º/”nÀ<úaÄE¶c@tžf4a YÀBš>ö@Àc-Õ/òm0À)Tq ¾û@gBõ×Ùx]ÀHšì>VAÀÔÛÚ,J«P@$€^±2DVÀö Ÿƒâ{–@jwìÎÀH`åÐ"WÀ*=mUÁZ‚ÀÊ2C©^@}À2B@ÒA‡÷Ç"@£‰M2 ö?ämhp‹"À|Ç\7*y ÀËÐìR ÀOêÙÒôï4@îMXø6›ù?ôn_|]¦ÿ¿yÈ9 (ö@±œ­¶Ü·qÀéá>^•JHÀÀÚ:P‘@=LÚé]]@e(±TK@¥SÆ-N:À"à bÛ•À¡vÂÌÕ@R„¸£û1ÀÀ|Þ#ÛEÀÈ–tîר'À¾ªJ±JÀ^ùñ&¡ À¹ø·ÓÃ+@¤å“ÜÆ@T­?ï¥*[@íq„×pÀÛ÷Åýµ$@ƒÖbþ¹8@z-7ÜÆ®À:;’nÛå?)t¡™uYÀñ8vóä’7À´ñÜG–7À9&#ãÅ”ÀjîËB_@Sm¥h%@°Œgu`ñ?¾–„X,]Ú¿QÊýøV2À3#Ž|…Àõcç3I@ÜðëïÜ@ÖtUæ»x4@Ð9e±@h½iâ…ÊXÀˆáp4$}GÀWçy˜¤5À‰RFŒ|êÀå§¢3Ò3@ÜÄH´–«Ó?*¥ô•KM@ÛT<>Uy,@ì†óGg@À?7í×ÃØ1À儱àe@›¿;Ë“bS@\4’åH"À}³f€|4@¨AtX]W@ëç8_À˜é’lvÝ@öm¤‹{v@Géc~›4@ËÅŒïA#À‡ ÐL)ìb@\ÀsxÜ;`@÷ÚT›ÕXÀE$ŽutKÀJ^x†¡JÀôa+4ÀÛ‚ëÃQ @ü:.Ó¯:Å? #K-\0Ó?â)VÛÀ<<Ë÷1SÀ×À Îv=ÀBÍ$Ὶ±qŽ…¨ë?}"LnøÀÎ(š¶¿è~æu¼{[À¤ÕáœhËCÀ9Ìx]œ#ÀWø-Ð7@§?Zí Àse ‡¢ˆ#À‡ç²’G±Àc”²×tÿ¿ãNI¸¦@¶v¢ß:‹@^O- C«ÀôqCÉù‡Àl¸9­`U+À&ÄP¢´Ú"Àåp³;ÀZ’Þv’0À½ˆGòƒï?9´ä_%@ƒä–מW@±/Í¿¹“:@c;}I¤å[À"y|Ì×?À”™%5¡&8ÀZDÀp'@ÒüÀMc&À–zÃKšjÀœœZ™ mW@Ö¢·ÎV‚@Àóo¥Œùi Àº'ý›lÀIÆÊck€ÀøMŒZÝuÀíK´Á„:Àï5jªä2@û„uE.ÀT@ TYßurÀ‡±æv5¡ÀÀt'ª‘eD@+yT-òÇ@xæÆN»û÷?¾:<àH1@õ$²L@`8tž´/1ÀÎÍHÄr;=À°žF³|›Àc™sÏ÷õuÀ=d´«Š+@ÂH¬Ç†@T~""¡ÀøQE¹jwU@w‚¬—üE@Vºi n)@MrîBä€ À³4ªÑx_À™ÎNa±7QÀÕßÐçDâh@J.0Ⱥ@ÇT3£‹µ¿6% ±NSû¿ŠSZp†¾?ŠÅÃJ2ÞK@IýR©üQÀòž‡ÛN52@j¯gAsÝ-ÀøŽó.Æ0~ÀµMg òò`À°ú ‚2'À zÖ¡RÀ·5ªßù?Àd_rð1À¬iQ^F@5I?×éð¿mRŽ/ŠÈAÀ½÷aû2:Àœ·XU¸ÀㆄôJ@ÀF¦l"iæy@‚MŠƒ‚X@¦9Dé‹0@•‹4n @Kz‚µ)h@\ì ÞO@.r·Ž91P@QÅ„>{;:@W#ì«Ý?VQj ‘ÀH10Æ–Û?.=i1˜ë¿Qჺ!À4°¤ð^áÀ³+>ïx@A²„HD%Àþÿ9;áàû¾AÀΦ§KÑ;ÀÚÓo[ÈÂô?ùr³=é@u€¾º­ À-‹‡#ËÀzÞC1ô©3@PÉ=N%U1À¡i>Ë4@ìÁ™\¬Â=@ß±4Áœ\@ëàYg=:À©éD‡‡¿ë3ð?@;ˆWÏc•$À‡ré°i@Zhó‰%AÀH`ª‚ø¿O¸S#yÀQÑ ´)Ì@CõPI¤Ø@õžq€8F@@}ñ!#BÀ,Šò”¿Y@»BÁÉp©&ÀÊuÑ^ä¦HÀìÙ)xâè7ÀôÐ×7W!GÀàR®î¸?+À7,( .À[5#¸ø?sFF”çN@‡?MÂúìÀN(2á%I@ÒÞ‡f@¯—^¼DA@R‰Ì'¾J%@Pš£s^À!äÀHãDÀ œírŽCÀÚ1:>o4,À* íá„ûÝ¿nÿõwÀÀË4¬©2(Àø ˆV€Àgî5í¾Îh@»~Q@šçM÷–¡À#'ÓQñrÀ_Uÿ3³@"Þ¿ôR@ZÌ®ö™DÀÝí-¿O$,ÀRåûX±§@WøœÔhñQÀôWĺæßX@“]µj«I@;Q?æG_MÀ¬œ3ÀñÅ.ø›4@iãw‚®é¿ Ë[vVXÀÑß÷MÀgü›n{k@F¹ø¬Õ¨'@Ù•–•Ñ—@®^B£]­ý?{s]lò§.Àô`JWŸÀ À7Û=i Œ@ aÈRCô?"=Në,@À0[£|Yl=ÀdÅÃVñ&ÀƒJJq\Àm5¥ë!5À†m"ç&¶/À‘Øu¨B<À~‡øØ¯ À„÷žî?$%À( È£´ÜÀ¨vW^#@wD²;À5¿sÀ{äN@d¹‘&zyAÀ6ü‰ÎªeÀ܈Då9Àð-q¯¹b@¿A%§‚€@åšpÏÉÈc@çøxys2sÀGÎ"8Kù?œDøö5á?ÒšüÓ) ÀµO‚Uå“à¿øxò™Q@B®îµ,3@ð´Ð J¯P@©•=£U )Àµ ¼ þ~À9½ó3ehÀ rSŠá§¬?Ð鲺¾Ë⿜“ñôâH@ÀÖLbe—ý3À—#Ч-a@Ìb]cÀl+?ÕÿlZÀè »=rAÀ[5‚a6 À/õÞúuÀÛe2Îy`1À8M¶T!À3UV›& @€L¹äô׿Ö]§Ÿ´÷ÀµìKlÌø¿‡.é×Ïc@á¡g5ºü7@œuOáàE@Ejü=LÀmR„~>À­Ãš‘ljÀøekh cÀ7¢ +³áEÀüLì6mGÀ¤Î¥À{JrÞò¿XÀáÚ Y·>À’·FUYD@ðbÀæ6À9rV­2”T@IõáÕŽ(UÀxó) CÀ¥îýŸ¢ì¿jn×ýÒ@ˆÿÜÌœ"ð?Nñ9 të@%™ÇÛW @…¯s[ÀÚ™ ËÔ·ÀÐX5˜ÖÀ#&‘ŠðƒÀžPį0žF@‹ðIDWp)À<âý®&J@_ ‚ÜóB@å;®> @@€É!H@¨ ÿT$é^@°«”7oÀ¦ÇãÛÊä@)fÅljg @qðÐÆ².ÀVVH<0À›´?IìÚÀ?C‰ ‚ @""4—B@l¸â.[D @êŽÙ×,Àgd¡ž/[Àçeø£§=<@ßj”’eð$Àò ûˆ¯@†É‘!—Þ@‚”u:@Iˆ›¨"@G6¸Á¨ßç¿]s«}1DÁ?3E©ÕøW@JÚ,«=ù?÷0Ô#kÏ@‡zÖm›6Àý[dø7H@ßž›¬3\@À3­­ñŒ;A@ˆ\F \á0@å 8'Z¼BÀ1@¾±ÁH@ý[éÌ$@¯e7öü@Hï‚jU…T@é$ +J@¾HÐ0@¤ƒ”¶âÛ@ü›BÈÓc(@÷!1»±Äa@´–O*:S@øí¸u®wÀ'.j:"IÀ8ZÄTsÀJÀç @„×LÀóI¥Ž«(@!hÇÀþê{ ÌNy@ÅT “lþk@424)ÍÑ¿M¤ Ž$0ÀOâR)Ö½A@ÕKî}íÀ"¯â§Ež>@„ÁԱÖJ@ö@Z ç¿Ð*ÞhU?èƒ&Ì.ÀIZÙ±6À¡¶êôªÀ_KS’byë¿„Õ¾Ä)ú¿Ë¶ü#hÄ6@è D´ñ‘À1NÀSà‚Ó,‘ÀȰ»@ÀÁÓ!^Žå?dëÉso€=@½´Î«ù&Àv;µ‹Î­BÀQý’ºFY=@ bÓ¢Ÿ(À™^áU75ÀKFFS%«4À–bþÿ`bDÀØ–"YÐÃ9À h”«¢$@'®Þm%@ÖMÊ;@ʉ½´™¡'À¤pe+À¨Äz0zy=@NÏ*ï=5EÀ½¥±F€¥1Àü%œ×ÅÄ @‰ÔY¼™ÃÀ|·•‡¥/@2ÁÿÁ/%@¼ž÷xdk+@þxÏä@8ÓÕïë9ÀÞÚ)°EÀâ˜%‘j«aÀV"ÁG@?)DL @Ž8‚5šcþ¿®@_ Êê)@¨¤³I‡0@ÞÂú`í€ÀœSÁ-‡ÀW]ŠN @}*Ë EÝ#@DmbmÀ£¼¡á°æ?€üH-½7Ààú G&² À§eË”ÿ¿®ÝÆbÀÌ”7øï2Àð%ƒ~%`/ÀnV ˜@ÆXÀ@®íÎzRÀ'‹cƒ‰é/ÀŒje˜<Ï"ÀÁ¡£0=@{±üz:õ¿U 4¡HÀ6Iþ#¦¤0ÀG¦.­=@˰Û7"@ÁåÚ¯Ž@pf1|1&@¨¸Lo´Œq@Å’ ËÂ@%DbC­3—À<Ìäq@B¤8'%cOÀÉÃÕÌ7>ÀÂìÛB) @´íÒ¼Cã?±¶îd”! À{´Ñ¹«“&À¯âŽíî^ÀÄ”ËûJ(@ÀI¬Áa„(À[תší@-' 0Àà–ê">$ÀÊÞ|8ENÀ]ÄjŒÿ²DÀØP2hW@'“¼x:@ã~íüœ"@þ¡°¤ÀÝU˜92ÀóË]zR%ÀzR‡7GÀ_—g°†G@f=P$Õ;–¿–Õzº–ÛÙ?ÿV÷ò²ø¿¿PE)-ò¿€ƒ” ðþ˜¿òYÊ ´MÀ„|?ŠêO@4þLjÚ¿­7-8\?ÀaŒï”2ý=@ö8h¹‹Ì>@b2vBŽ>Àysa2ø<ð?ÍÅðó?®‚9sá?U¨×¾@"liN@¢eš<@ƒkJÄnBÀÊ:9*¯HÀ[‡g^šÀ0½Ö[uðœ@”7ÇWÖ?Ø5N©&u;@Þ’Ó«_›4@p,T \TÀ€ëMûr`@rýªlSÀâ• XUreÀ¹  &lÄoÀz­@q×ÇBÀà< Æž#M@³µDø)@Q·æ“æ]@XvŽO=,e@Ç#+ªDfÀƬ\o€_"Àløä0¨þ¿çÞ']òf Àf$rô/^Àlï½A'~Õ?Z.Ï•`ñÕ?Õ¡Ö©HÀnA HÀâ΋xߎ/À¥Ã :@É74@sZñD<3À0º5‰À^6)O·lvÀTò¾¾ÎaÀ„_¯Ì~@´X²~‚î @ š¨Mp*ÛÀ oM)¤ãÃ@/´v–{„ÀM:#¿6 À¸1J¿­fÿ¿‡ñ1Çû¿ 0,&4CÖ?“©õ–½Þ?KÁgGõb!ÀÌŽ&…… ö¿ïMÏèÞíÁ°8ÖŽ¬OôÀÍ£¿›¤"À™xWKñÀ¾Ü‹rI5Àµ4Þ%Àá€tIÊD@545~HM:À»;¡ZÃ~@¿‡/M—g@\ƒ¼‚´fÀÈ\çÈzaÀ¯jX0À"ÅñP‹uÀéŠÓ:À?ëûõ[¨FÀġ9g(A@YSÃZ Àˆ–ÎpŸÝ¿ž>µ9E@à;£´èÐ?>6óV°ÀXÜÍpc@ü9´@ 5À“`*Àl˜aaç|×?}ˆ €¸ð¿^£«t@~v6é= @œ¦ä¸:ˆpÀ”—¨dw@Cƒ³˜¯#À¾GˆÚ—À}ˆQ5Û( @Gå-]Roø?´xDÀµ0ÀÀ÷€añ&À0[D“ À¿uÔo&@ðõ>\þC@Ý,ùÅïh@({,Qß7fÀCd÷¼¤©YÀ·äÖA¹2ÀóL6‘þ6@×–ýÕÓR#ÀÆA‘@ŽÀí¥3ÍEÀOBå6OÂ"À%ÐÎT@oˆb”g`ÀqƒB•iCÀôæÜÅ !Àe¿M’Z@çñkÈ·D@Ø{rx±*!ÀßÅB¥ªÙ ÀxÜ¡ö>Àþ™ðLÀ¨hcè*@ß Œh/5À-fR¶Ï"ÀOJÌ©Às}¿Þi¶é¿Kªb×Í¿ž(þ·?£>Àx }Ûþ Àm”}`@ß%´Öå>@ä¨Ñ¡¬Ày0dÔÙ@ë rÿ¿i®UÉý¿A$øawD@?ƒè¸¾#@[¼6ƒ LÀK1õZÇê/À0[ j1ÜÀ–3b)0À\D •o€JÀˆñ© …y?ÀíÕñf´AÀ „¯†KKÀª»èåRWÀˆŒ¯' 3@ÂóqJ õ,À’Ôsf¯$@Á9^'Z‚b@'©‘®Ú¥Ì@¤Þ…–jHÀ€[TbRtÀ œ$=+UÀõò6ýc @ÑŒ”ÛÌÀ™F&™M@J;·î9'Àb÷4á"ÀÖŒ :7!Àóâ >šöGÀoàçþIÀ.ôßÈÀ°¹€À‚%=MÀ– O¢NÅ)@!è WïÂÜ¿h+'Õ)Zó¿2È:ºn@0À¸gå[i@ÕÝAÊö—?ºØÉrWà´¿9'¼|æ@<`öMf*À Cu+ç¿›¿2S­~ò¿4íi†=<@xžãÏcÑ@ªàÒå—@Pãçί1GÀý µ4X¾k@:ïª+$-m@jà[špÀ¬(Ë7!Ì¿înCJ¬ À|]7n®æ¿–eqSÏN@n"²`ó¿ØÏb·FNÀ¥Œj /³È?ö¨ØÓ‚ÀfÖOÓéò¿écªºÛ@è <í$u0À—iº\M@ä8§ö5 @ª¸’úÌu;@-”FO;@éü7ö~ê@LóD¯µéÀ©Óqÿ•KhÀf#gÔøoX@Š¥»ÀÕc@¦é¹l;wÀo×í½KÆ6Àh5Ya—œ%À´ê£527À ŠZ¹À†U?æüƒ?À4”6Ç9'1@=sÈG}@ñ ­ÀÂ@ÀÅ‹T*û?q¤þ2‡HÀb²4ØJu+À(>§_'À¢Ïô׬ã@ÀþbPŸð0@³2%ÍpHpÀqPu;$JpÀi­;ÐGél@†½ñá]µp@Rb^ªßÀ0–þÃ@ìß>>q‡'@y@ã˜B@ׄ(‰®{C@ð9Þ̶yÀ^í2ÔÓ‰,ÀNNEcú˜À—Û?nj |@ÒýÈ¥ž(^@â ò^«Àyr‹4ðGŽÀƒSËm ÆDÀY¨†‹³ÀS Y}? <@é™’5á¿W°!aÀÌlۼܷ)À««¨Un¤À ý¿ÓñÀTþ/¯ HÀ…2Ú9+¶CÀßO$GцÀãÔ¿B•€À³¼Æ4©­jÀKI Õ~@U/YqN@W$Ö&@É#VWtbð¿™E)þhÀŸ!Stkz1@ûW=—ÿ?Ê? hYpM@ o·,@·*®3Ù`Àᥑº‹B5ÀžþHs‚Ó>@Â]lQö?À” mGƒ=ÀAêfÀoÜõ㪾ؿËÓü « ÀàçvbSB@.Ï}® š@—$ QÀHµ“åqÀšÞä%@‹äòN¢@‘f±``,EÀñücù3ÀƒÈúŠ×¿7"$T´î?kãÀeøè!@ «m[pi@Ý;­®'ÀdýýÈ&ÀÀ¹­ží¿›c°G꿘ï )“]Àas‚~­HÀý¿×Ïô‰N@òÍõà")À(Y:Ð^PÀ|bF7÷.:ÀÖ*ȇsH@Ïã·Š¨@ňñ2•ÀÕp‘¥ªQÀª~m'Àøµõ¯ jó?…÷CšdÀ â`¢zØ.@à˜° —´@iïؾG$Àë^ñ2ec[À½ PÞ^;EÀ…kýªëÒ%@P8¬~ºÜ¿ËìŒVaL0À1z’zëä @F£?»Ó @ ³×oü«;Àz€ØA» @ñŠð†÷?›`åoÌZß?³1Ãù@±€6P>N5ÀHt…5Àë¶nƒÛ@’´ºQ—0@/2Ú‚­ófÀ‡Á+[ÚÊGÀPt—¿Š@tUb̉@¬‹H—͈ò¿ÞlžŽˆRú¿håßD¬¢@ ³à eÀÌPZDÉI@“üçhîˆ6@~ËÀ3C{*@e)eêÓ¨ÀDéàZTh2ÀÝ”™‡ ßÀ»¹×g;A@nÓîj7À¥D²=¨é8@Ùõ5ö(m1@3è9䀄鿆D+bö@aSðS°dGÀQÔr¥ô³EÀï”wFù¿µ(WAÎkÝ?{ÓïŸZÀG÷d÷žVô? éo¥rŠÀº‚0»S£Ì¿fqDm®(ÀùUàšµú¿4—,fÀätY´™/@Œ¥ê÷·mÀƒ6™è|VÀb¡ ¡µŽÀr¤‡±dUÀ{¡Ñ®„=@Ð’Ð[À$-Òsb¾A@?¹ÍöBÐÀ(OyàR–HÀ¨iºBx¡$ÀàòzXçýæ??e1¤ýïå¿Þî‹Ã×Xü?¬Z»$úb4@ÁbªL>7ÀrÝèã1Ð5À,ÐO4“7À"fF×AœÀ%WŠm¨À´i¡ ›Àùãv fƒÿ¿¹6!K À†/ ÇÅA-@À‰ãi3~@»Èëù=@A̹›fG@>ô2!8yRÀǯðkOÀoeÅ~ûá@¦ÒÌdqÛû¿³Þ ˆüT@Òa‘}‹@Ï: A=À]c—†@ÀO*ŸõXKÀ:É…ØGƒ@ÀÚ¾>5eó¿q¬š -0@jõ‚ý…DÀ+(3M,2À (?[K‡'@t­?^JJ#@¬:ü­³ÏGÀ°Ê`$5ÀoÁ³Õ84/Àß͘®©A@Ž63¾GÀÆYž´MW@à¢Dñ5@Ú¹)Ü?0@„x1eöå@ö Pà3ÅÀ@¬u’\?Àmw ûCB@)ÿÌ0„°\Àp‘¥ ?>@X% 'õ @çL8#¨t³?Pû¯YÀó.ÀÊávÔ%À M³àù#†@DÀŽØ¦C@RÚX ÀÍ©]ù—Áø¿/UÄt] ÀM û†±á”ÀUê¢ynUh@‚µi%±)@ Ççoá@ê?ßêZN ¿Lå=ñúþJ@Iíü*A+@?; ´]Àê™hþ›&@*nþI|]À @G‘ ÀÆ’ƒtMñ? WLˆ~›@pñø A·ð¿¡”ûï rí¿uF‚`#À²jϲÑ À ®g Ý'ÀÄeCVÅ 9Àq3i6 @³i~Jp@ì‡#·†J<ÀÈ19ñ¨ÀÇXesG)À{¸ŠÒÔ-À–ß û@Pß0A@Mö™NÀ`À´« É)¨#@ÀOÜͱ³YÀ±06A¬ËqÀ/Ê¡b%À¦ÁÙTÀ]ªí&‘|Àñ_¡Ï†Îr@Qa°¹ŽIB@Ðñÿí¢:@‰vÏŽŠYBÀtךX=À­·Ì mo7À…ur#þ%À‚dA7oðÀ\ftèÌ?„ÃM©f1cÀ5p×*ñHÀél F«‰ÀwÕ8‘†À“uÆ÷…Ø@À¼`(»˜1RÀ)+Œ“íºÝ¿egŸ@èf\Írî@‹úlá”"ÀKñ=ý6éÀdÔäêÀÀ)ÉŸHB^;@´«àgqZÀÁÞúš†vÀqã4 8VÀÖœùó3@ôúßlê"@Ñ_kþÌŸC@>BÁŸÖÆ5@Œ Z¢ï_>@øK«Ìâ}@M±öz(À¯HðRbí¿ UªpªÀÍ”5kr<@ã=!@g‹ö·‰ö?Ø †å®î?2P§çí¿Né@P󿟘tÓ3@Ú.tæJå?«7a qÎ?ø8æg£xu@üIäëT@m«†÷¿÷?¼·ëÍQŸ÷?‘œ`Û"ÀHŽL š ÀÊ\Ä·„HÀ0ç·JÀ±Ž9Ä€õ?³÷ùì<» À p®c“@©¶@®gÀ¨Šõ®œ"@S¸u ñ?S[4Fn#ÀÑF8̺³?h¡DèÔTÀAìϹ± 5À­TVfx#Àìƒy`Ê?ð~ïæ´ð¿£aÎ¥U•·?ˆ´t=l À—€ÓIýÀæYþTØëÀ#ëæ­t¯å?ÕãÙåðtRÀ»å@Àb.€ŠTÀRÖò Â5ÀRWKÔ€EÀ'ðò¥¦ÀÜÃ5.7ÀΈà²Ó @6¬1À¢"Àª´,™®ÈÀZ4!"šÀQÊó¾Ræ@Ù¹Õ´¤÷ù¿VÝ#áÂÆ÷¿Ò˜ƒÁë’)À×b›0ãSÀä91v˜(#ÀÓO”B·³À¨À_°0"ÀÛºb¼ˆÀ<8Wm1Àæ+FŒ4ÀÜÇÓt;ÀA÷üFâ0ÀH3MÚ¬rÀgØGº@'*'|‹'Àñ‘/é[îæ¿}ª‡~ Àüg¬ƒTÀwjšÛú¿kˆÑÃC&ÀAb91…ä? u'{Gñ¿›î4mJ/Ò?òùì•"dÀ¿â"~³Il@Ö˜ou4”¢@â4ÓÀ¤H¦Àn‚Bu©‚”ÀÞJ6§Ð:ä?¸Cà»öÙ @1¿SªcÒ´¿›šk)ÉÀë2¬Œ„ê)À¼vï’‚ù2ÀL塸ônÀHƒbèm@Mì6¹Z¡ÀòPL™£À«ál­nµ @)4odUà@ÀE^êÀ‰Kbÿ¿°£6þ?‘ѳ gj@L=œâ¸`ÀwPïŠËy7ÀU/ hAÀ$E'§àÀš&l©Æ\WÀ¸ âÓFKFÀNÄNÒ?À®Êô‘ÇÖ-À ©¶„zîA@/ Ç1‚2@Ìï2ÓPå?Œ¬r-ÌûÀ¬f›òÏ?yæ(Ò¾Œ¡?…§s„f»m@ÉfõAÑpÀBWTë ¸ÀªRºµ@G@*ýõË4:@o"E @§é‹ÅÓb!ÀjÅ‹ù!ÀFUãž=À^ÈUð/NJ@ÒXúí@-?ø<îó?2&:èž`@(õž–¸ú0@×qUIÀ9m,É`b@9c‚µWC@röþ #@&ÈZ)E*jÀY ‹‘:;BÀ»¬w:"@O@\"Ô…è¿]ÁsByÀç§^HµÀï,®ÜÍSÀM…xYædÅ¿QœÐܺ&@²âÌÎá@˜Aùí%ù¿`¨¼}d[ù¿Û:5×FÀj±E“}7;À—ó鯿 @§~² Á"À„±·M@ÀÓT‚Eº´#Àlí•ÕCÀ@›“G†ÉÒ¿·JXÏoÀ’Ý×4ÀböGÓRPS@sÎ¥ðO&3@ ’œwÞ?Ø7$.…á¿q”ã}íu*@ÖYò !@ÍsÓ${@õÎu"iÀÐ…RÔZ1@b7o¦‹Ü.À.ñ¼€'É Àx”Ïü6&À‡.ö¯@NCgråx ÀÙüÒT¬@Àfg0zp-À(&†C9h@3$ª®vÀéOT$^@›ßV„3wÀ(ZúËÛYÀw”±¶€·@¢×?7Á2Àɨ)y˜0@c÷ß2¾_Àö²ºëîŒAÀÃŒs‡«pÀl¼Àª¤I@IQM°”C|ÀQö7þ˜mÀL¨ RÆû8ÀsÕFôaÀÛ…nÂÃ×F@Þ‡2ÂqÈñ¿&º=ñd˜@}°ò8P@wErãDÂ2ÀóoÍU ÀÄí«‚™Àmú°;«ùÀK´(p—:ÀŒ£ðŒô¿•IÎhþ¨"@Ù+øgñpú?ð.Dg˜86Àè÷`VÜð#À3”I‚ÀøTŽ;ú'aÀ&²YœHÀŽH«kv7À>ÜKØ|Íø? „‡ ¹¾@„-Ð&Â=ÀÀpœS¤€nÑ@j—ˆÎHÞTÀ>6;;"_CÀ³¨Yo™<@Ph— ÆFÀdTq'ƒÀTòÄì?\@&ÔhF*5À”–1¡ý€D@ ë‡ h›ÀWXÉÞÑïÀË …I¼™Àú„3$,QÆ@‘°flþGQ@iØ)--d@=]Í¡¯ÃTÀ–¼Ç³A%Àú"ŸÄcX"@›oÄÙl@³¨GW%?À)ÜP’þ‰&ÀRâ*ù½ð¿û¤ÜMK @aÓ+'ÁÄÐÀ7·®ÅbÞ@ê%õSÀ,~ïH?À¿Þ¥ÚÉÏ@ÛÁ(’R@çÕ_îm×øÀ=p3XT™lÁ¬?T-Í6ÀÿwLy=ÀŠÈÄ®6oÀ?šUצbÀé×Oú@ÚŸ¥ þÀ~©úù+U:@p4êûg@GKÿa}IÀ(ÄFE+-ÀËtÀR/·NÀ˜sCZ(À»ÄL½4@ºúòÌ<@»Õ¹)ÑBÀŒé{²Ô*À¤¯ÛÊ60@m|ÛÒeÚç¿3ëÒh«@”Iz%Z#@‚ëûÚÀ}‡e½yÅ@=ÐoDf§B@JÝñëú#õ¿3@RlƒÀ$Ÿ›Õ/Ú¡@$];\@$MãÕ>¬,@ ½yùæ"@}0ÇaØ??AŸ7@šÀ>X$ò ¸@LÌ«f¥t˜ÀÎÀÂÛÁ\´@jÒa:ðò¦@r!]¸=¯ÛÀZÓÇ2ÜK2À·bÁ×—‚À4=0ù_ÚÀµ¾äí9^lÁ<û2$×Ë,@ BçÑä @:ŸeÁr#Àÿ‰ŠÛŒè!Àõs$jõ†@÷œ“ HÈ?­õµ"?Ü$À“«R£ â¿nŠF&BwE@èð©ÞÌÁ+@wŠ¥çÌc£ÀËA´Ö!Î@Ý2ôÜq?ÀòõŒw À;جÜLÀ4À]ªMÀÜE`M¨ÀÞáhmJÆ@½G`U4õ@s°@ë%ÈŒýì?@^¢¸~Ö?jžÝð8ÀA…Ì[ÀÀôm6#¤o@Žè$Ãè`@³ÒÞ±ÓܰÀÄ~ÑÈÝ@í5˜Ó®ÀxS%ë3ÙÝ@p¯·xh3Àc\Å"ÀzÇ1«[B@}®m±0ÀÌ#|¯:v@è¡ü©0°À&ÓKc0@ tcœ{»@r~Æ:FÀŪ<€(Àˆòï¾ö#À’Ò;Lé.ÀĹ´¯<è)¢ @ˆº‘êS®3À~’œ Ü;\ÀÆæ$ððî=À;޶ÕÀ]L²‰_,À#ƒœ"©8À—BéÊ0=Àsø]‰BÃf@§·Y# hÀq ‚ó‹!@°ù&µMMf@-/ l`ÀÄ—¨ èðL@Rðó@“6@$+¹#,œ@ÜüÐás@Ðô!”,X@,–¯%­¸"Àô”ì¶«@Òû‘E_öA@ŸSCc4À¡˜n¹JÁÀšæmNŰ€ÀZÑœ¸¾,@ý‹° H™A@Gþ¢Æ*oÀÓ÷_—á:@SOBÛÛ«bÀóF|M쓊ÀDu…•‰¡¢?´ úiÃ¸»Š¸¿BÀ6 mXKÀÝÓF#"ý?–—éék\À ™3C‘r@3þÕ¤ÀWš³¯ùÆ3ÀË'hQ9GÀ„fpÊÌô¿‘´íŠÀÙ%£ðçBÀƒµè°)ÀçJqõŠ|$@º˜Ë¾ÎNÀ<º£z¤Ï@Àè<]ÌÃBÀìÿV‘0wdÀß íŠ\ÀÅ'¯[0ÀnNR¸§«À9*z\d$AÀG¿úkfÀ6Ô ]…"ý?˜Ã?ß¿øøý{Hf.@Ò¬w–ÇBÀõk”’è—Ð?êሾn¶?Î Ý»HåÀ©,’!rÜ%Àš›®õVäÀü,<½lbC@áS?¼‡T@kêCýþ?Ô ¡äM°¿Ú ô í¤­¿"X‘4c®‡@¹@È)½À~&2øŽñ¿kc¤@¼ð¿ÒéRC] Àê¢h÷fÀVžäåìBÀ+UáÅoõ¿ŸÅBÿ¡ñ?ÂrºÏãü¿]¦:"¯y&ÀÅØç÷µLæ?zPú: ,@o§¥Ú@}üéD€@aì|fÄ\d@ž ð ðñlÀœõŠUÀ"€Q3`…@i 6Ò•À\ŒàÜQý¿´ÊÊ÷ÞÚ¿²Á£ÈV ÀúëèW%Qó?Œù7Ãë@»f@ñìÏ@@‘Ùm_<1•¿snæ‡ÎX@uâíêü`@, ¾ÅGÀˆ$ëÂw:À{v·—!@+\ji”*@ОœÏÝ@ôt뇖XÀz(w]•À Ý<©*7À “•‹·m@}y8,Äã@Àq](7­xmÀ“GMô# 2Àeà£Q QÀ™]å•yV<À+,‹¬“¿I@õy§[4@Ûr{­ºYR@%w˜±]J3@?œãð4¹¿¢¤‡a‡°À„0yõ¬ÀxDõ\3ê Àf‡Á«ö¨ø?mú=cMáû?VA(±H>ÀÅ Ý<Â4Àd ðm­­Àˆ[Ùj¤@²oÔ‹ÀR'°…&¹S@øÂ]“ir@k›:µNµ¢Àga-5.9.2/armci/examples/benchmarks/cg/armci_sharedmemory/read_input.c000066400000000000000000000135221500715745200257160ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_STAT_H # include #endif #if HAVE_FCNTL_H # include #endif #if HAVE_STRING_H # include #endif #include "armci.h" #include "message.h" extern int na; extern int nz; extern double *dmvec,*svec,*bvec,*dvec,*amat,*xvec,*axvec,*rvec,*qvec; extern int *ridx,*cidx; extern int me, nproc; extern int myfirstrow,mylastrow; static int *columnmap,*allfirstrow,*alllastrow; static FILE *fd; void generate_random_file(int naa,int nnz){ fd = fopen("randominput.dat", "w"); } void read_and_create(int argc, char **argv) { int ri,i; int tmp1,idealelementsperproc; void **amatptrs,**xvecptrs; na = atoi(argv[1]); nz = atoi(argv[2]); if(strncmp("random",argv[3],6)){ if(me==0){ fd = fopen(argv[3], "r"); if(fd==NULL)ARMCI_Error("unable to open given file",0); } } else{ if(na==0 || nz==0){ printf("\nERROR:exiting-no input file given and na or nz is 0"); fflush(stdout); ARMCI_Finalize(); armci_msg_finalize(); return; } if(me==0){ generate_random_file(na,nz); fd = fopen("randominput.dat", "r"); } } if(me==0){ if(na==0) fread(&na, sizeof(na), 1, fd); if(nz==0) fread(&nz, sizeof(nz), 1, fd); printf("\nReading CG input\n"); printf("Number of rows: %d\n", na); printf("Number of non-zeros: %d\n", nz); } armci_msg_bcast(&nz,sizeof(int),0); armci_msg_bcast(&na,sizeof(int),0); armci_msg_barrier(); amatptrs = (void **)malloc(sizeof(void *)*nproc); xvecptrs = (void **)malloc(sizeof(void *)*nproc); if(xvecptrs==NULL || amatptrs==NULL) ARMCI_Error("xvecptrs amatptrs malloc failed",sizeof(void *)*nproc); if(ARMCI_Malloc(amatptrs,((me==0)?(sizeof(double)*nz):0))) ARMCI_Error("amat malloc failed",sizeof(double)*nz); amat = (double *)amatptrs[0]; if(ARMCI_Malloc(amatptrs,((me==0)?(sizeof(int)*(nz+1)):0))) ARMCI_Error("icol malloc failed",sizeof(int)*(nz+1)); cidx = (int *)amatptrs[0]; ARMCI_Malloc(xvecptrs,((me==0)?(sizeof(int)*(na+1)):0)); /*+1 for end of last row*/ ridx = (int *)xvecptrs[0]; ARMCI_Malloc(xvecptrs,((me==0)?(sizeof(double)*(na+1)):0)); xvec = (double *)xvecptrs[0]; ARMCI_Malloc(xvecptrs,((me==0)?(sizeof(double)*(na+1)):0)); bvec = (double *)xvecptrs[0]; if(me==0){ for (i = 0; i < na + 1; i++) xvec[i] = 0.0; fread(amat, sizeof(double), nz, fd); fread(ridx, sizeof(int), (na+1), fd); ridx[na]=nz; fread(cidx, sizeof(int), (nz+1), fd); fread(bvec, sizeof(double), (na+1), fd); /* the c adjustment */ for (i = 0; i < na; i++) ridx[i] -= 1; for (i = 0; i < nz; i++) cidx[i] -= 1; } armci_msg_barrier(); /*acg_matvecmul(amat,xvec,bvec,ridx,cidx);*/ if(0){ for(i=0;i=idealelementsperproc){ if((elementsperproc-idealelementsperproc) > idealelementsperproc-(elementsperproc-(ridx[ri+1]-ridx[ri]))){ alllastrow[i] = ri-1; if((ri-1)<0)ARMCI_Error("run on a smaller processor count",0); /*tmp1--;*/ } else{ alllastrow[i] = ri; if(ri<0)ARMCI_Error("run on a smaller processor count",0); tmp1++; } elementsperproc=0; break; } } } alllastrow[nproc-1]=na-1; for(i=0;i #endif #if HAVE_SYS_ERRNO_H # include #endif #if HAVE_SYS_TIME_H # include #endif /* Timing routines that use standard Unix gettingofday() */ static struct timezone tz; static struct timeval start_time, finish_time; /* Start measuring a time delay */ void start_timer(void) { gettimeofday( &start_time, &tz); } /* Retunrn elapsed time in milliseconds */ double elapsed_time(void) { gettimeofday( &finish_time, &tz); return(1000.0*(finish_time.tv_sec - start_time.tv_sec) + (finish_time.tv_usec - start_time.tv_usec)/1000.0 ); } /* Return the stopping time in milliseconds */ double stop_time(void) { gettimeofday( &finish_time, &tz); return(1000.0*finish_time.tv_sec + finish_time.tv_usec/1000.0); } ga-5.9.2/armci/examples/benchmarks/lu/000077500000000000000000000000001500715745200175725ustar00rootroot00000000000000ga-5.9.2/armci/examples/benchmarks/lu/README000066400000000000000000000002041500715745200204460ustar00rootroot00000000000000This directory contains example programs using ARMCI At present time, SPLASH-2 LU factorization on top of ARMCI is included (lu.c). ga-5.9.2/armci/examples/benchmarks/lu/armci_blocking/000077500000000000000000000000001500715745200225355ustar00rootroot00000000000000ga-5.9.2/armci/examples/benchmarks/lu/armci_blocking/lu-b-bc.c000066400000000000000000000362651500715745200241360ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /************************************************** * LU factorization * * Armci Version * * Block and Cyclic distribution * **************************************************/ #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_UNISTD_H # include #endif #include "armci.h" #include "message.h" #define DEBUG_ #define MAXRAND 32767.0 #define DEFAULT_N 512 #define DEFAULT_B 16 /* global variables */ int n = DEFAULT_N; /* The size of the matrix */ int block_size = DEFAULT_B;/* Block dimension */ int nblocks; /* Number of blocks in each dimension */ int num_rows; /* Number of processors per row of processor grid */ int num_cols; /* Number of processors per col of processor grid */ double **a; /* a = lu; l and u both placed back in a */ int nproc, me = 0; int proc_bytes; int num; int nnodes; int doprint = 0; /* function declaration */ void lu(int, int, int); void lu0(double *,int, int); void bdiv(double *, double *, int, int, int, int); void bmodd(double *, double*, int, int, int, int); void bmod(double *, double *, double *, int, int, int, int, int, int); void daxpy(double *, double *, int, double); int block_owner(int, int); void init_array(); double touch_array(int, int); void print_block(); void print_array(int); void get_remote(double *, int, int); /* timing functions */ extern void start_timer(void); extern double elapsed_time(void); extern double stop_time(void); int main(int argc, char *argv[]) { int i, j; int ch; int edge; int size; /* ARMCI */ void **ptr; double **ptr_loc; armci_msg_init(&argc,&argv); nproc = armci_msg_nproc(); me = armci_msg_me(); while ((ch = getopt(argc, argv, "n:b:p:h")) != -1) { switch(ch) { case 'n': n = atoi(optarg); break; case 'b': block_size = atoi(optarg); break; case 'p': nproc = atoi(optarg); break; case 'h': { printf("Usage: LU, or \n"); printf(" LU -nMATRIXSIZE -bBLOCKSIZE -pNPROC\n"); armci_msg_barrier(); armci_msg_finalize(); exit(0); } } } if(me == 0) { printf("\n Blocked Dense LU Factorization\n"); printf(" %d by %d Matrix\n", n, n); printf(" %d Processors\n", nproc); printf(" %d by %d Element Blocks\n", block_size, block_size); printf("\n"); } /* num_rows = (int) sqrt((double) nproc); */ /* for (;;) { */ /* num_cols = nproc/num_rows; */ /* if (num_rows*num_cols == nproc) */ /* break; */ /* num_rows--; */ /* } */ nblocks = n/block_size; if (block_size * nblocks != n) { nblocks++; } nnodes = nproc / 4; if((nnodes * 4) != nproc) { num_cols = nproc - nnodes * 4; nnodes++; num_rows = 1; } else { num_cols = 2; num_rows = 2; } num = (nblocks * nblocks)/nnodes; if((num * nnodes) != (nblocks * nblocks)) num++; #ifdef DEBUG if(me == 0) for (i=0;i n) { kl = n; strK = kl - k; } else { strK = bs; } /* factor diagonal block */ diagowner = block_owner(K, K); if (diagowner == me) { A = a[K+K*nblocks]; lu0(A, strK, strK); } armci_msg_barrier(); /* divide column k by diagonal block */ if(block_owner(K, K) == me) D = a[K+K*nblocks]; else { D = buf1; get_remote(D, K, K); } for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } A = a[I+K*nblocks]; bdiv(A, D, strI, strK, strI, strK); } } /* modify row k by diagonal block */ for (j=kl, J=K+1; j n) { jl = n; strJ = jl - j; } else { strJ = bs; } A = a[K+J*nblocks]; bmodd(D, A, strK, strJ, strK, strK); } } armci_msg_barrier(); /* modify subsequent block columns */ for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } if(block_owner(I,K) == me) A = a[I+K*nblocks]; else { A = buf1; get_remote(A, I, K); } for (j=kl, J=K+1; j n) { jl = n; strJ= jl - j; } else { strJ = bs; } if (block_owner(I, J) == me) { /* parcel out blocks */ if(block_owner(K,J) == me) B = a[K+J*nblocks]; else { B = buf2; get_remote(B, K, J); } C = a[I+J*nblocks]; bmod(A, B, C, strI, strJ, strK, strI, strK, strI); } } } } free(buf1); free(buf2); } void get_remote(double *buf, int I, int J) { int proc_owner; int edge, size; proc_owner = block_owner(I, J); edge = n%block_size; if (edge == 0) { edge = block_size; } if ((I == nblocks-1) && (J == nblocks-1)) { size = edge*edge; } else if ((I == nblocks-1) || (J == nblocks-1)) { size = edge*block_size; } else { size = block_size*block_size; } size = size * sizeof(double); ARMCI_Get(a[I+J*nblocks], buf, size, proc_owner); } void lu0(double *a, int n, int stride) { int j; int k; /*int length;*/ double alpha; for (k=0; k #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_UNISTD_H # include #endif #include "armci.h" #include "message.h" #define DEBUG_ #define MAXRAND 32767.0 #define DEFAULT_N 1500 #define DEFAULT_B 16 /* global variables */ int n = DEFAULT_N; /* The size of the matrix */ int block_size = DEFAULT_B;/* Block dimension */ int nblocks; /* Number of blocks in each dimension */ int num_rows; /* Number of processors per row of processor grid */ int num_cols; /* Number of processors per col of processor grid */ double **a; /* a = lu; l and u both placed back in a */ int nproc, me = 0; int proc_bytes; int num; int doprint = 0; /* function declaration */ void lu(int, int, int); void lu0(double *,int, int); void bdiv(double *, double *, int, int, int, int); void bmodd(double *, double*, int, int, int, int); void bmod(double *, double *, double *, int, int, int, int, int, int); void daxpy(double *, double *, int, double); int block_owner(int, int); void init_array(); double touch_array(int, int); void print_block(); void print_array(int); void get_remote(double *, int, int); /* timing functions */ extern void start_timer(void); extern double elapsed_time(void); extern double stop_time(void); int main(int argc, char *argv[]) { int i, j; int ch; int edge; int size; /* ARMCI */ void **ptr; double **ptr_loc; armci_msg_init(&argc,&argv); nproc = armci_msg_nproc(); me = armci_msg_me(); while ((ch = getopt(argc, argv, "n:b:p:h")) != -1) { switch(ch) { case 'n': n = atoi(optarg); break; case 'b': block_size = atoi(optarg); break; case 'p': nproc = atoi(optarg); break; case 'h': { printf("Usage: LU, or \n"); printf(" LU -nMATRIXSIZE -bBLOCKSIZE -pNPROC\n"); armci_msg_barrier(); armci_msg_finalize(); exit(0); } } } if(me == 0) { printf("\n Blocked Dense LU Factorization\n"); printf(" %d by %d Matrix\n", n, n); printf(" %d Processors\n", nproc); printf(" %d by %d Element Blocks\n", block_size, block_size); printf("\n"); } num_rows = (int) sqrt((double) nproc); for (;;) { num_cols = nproc/num_rows; if (num_rows*num_cols == nproc) break; num_rows--; } nblocks = n/block_size; if (block_size * nblocks != n) { nblocks++; } num = (nblocks * nblocks)/nproc; if((num * nproc) != (nblocks * nblocks)) num++; edge = n%block_size; if (edge == 0) { edge = block_size; } #ifdef DEBUG if(me == 0) for (i=0;i n) { kl = n; strK = kl - k; } else { strK = bs; } /* factor diagonal block */ diagowner = block_owner(K, K); if (diagowner == me) { A = a[K+K*nblocks]; lu0(A, strK, strK); } armci_msg_barrier(); /* divide column k by diagonal block */ if(block_owner(K, K) == me) D = a[K+K*nblocks]; else { D = buf1; get_remote(D, K, K); } for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } A = a[I+K*nblocks]; bdiv(A, D, strI, strK, strI, strK); } } /* modify row k by diagonal block */ for (j=kl, J=K+1; j n) { jl = n; strJ = jl - j; } else { strJ = bs; } A = a[K+J*nblocks]; bmodd(D, A, strK, strJ, strK, strK); } } armci_msg_barrier(); /* modify subsequent block columns */ for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } if(block_owner(I,K) == me) A = a[I+K*nblocks]; else { A = buf1; get_remote(A, I, K); } for (j=kl, J=K+1; j n) { jl = n; strJ= jl - j; } else { strJ = bs; } if (block_owner(I, J) == me) { /* parcel out blocks */ if(block_owner(K,J) == me) B = a[K+J*nblocks]; else { B = buf2; get_remote(B, K, J); } C = a[I+J*nblocks]; bmod(A, B, C, strI, strJ, strK, strI, strK, strI); } } } } free(buf1); free(buf2); } void get_remote(double *buf, int I, int J) { int proc_owner; int edge, size; proc_owner = block_owner(I, J); edge = n%block_size; if (edge == 0) { edge = block_size; } if ((I == nblocks-1) && (J == nblocks-1)) { size = edge*edge; } else if ((I == nblocks-1) || (J == nblocks-1)) { size = edge*block_size; } else { size = block_size*block_size; } size = size * sizeof(double); ARMCI_Get(a[I+J*nblocks], buf, size, proc_owner); } void lu0(double *a, int n, int stride) { int j; int k; /*int length;*/ double alpha; for (k=0; k #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_UNISTD_H # include #endif #include "armci.h" #include "message.h" /*#define DEBUG*/ #define MAXRAND 32767.0 #define DEFAULT_N 1500 #define DEFAULT_B 16 /*#define MPI2_ONESIDED*/ /* global variables */ int n = DEFAULT_N; /* The size of the matrix */ int block_size = DEFAULT_B;/* Block dimension */ int nblocks; /* Number of blocks in each dimension */ int num_rows; /* Number of processors per row of processor grid */ int num_cols; /* Number of processors per col of processor grid */ double **a; /* a = lu; l and u both placed back in a */ int nproc, me = 0; int proc_bytes; int doprint = 0; double comm_time=0.0; int get_cntr=0; /* ARMCI */ void **ptr; #ifdef MPI2_ONESIDED MPI_Win win; #endif /* function declaration */ void lu(int, int, int); void lu0(double *,int, int); void bdiv(double *, double *, int, int, int, int); void bmodd(double *, double*, int, int, int, int); void bmod(double *, double *, double *, int, int, int, int, int, int); void daxpy(double *, double *, int, double); int block_owner(int, int); void init_array(); double touch_array(int, int); void print_block(); void print_array(int); void get_remote(double *, int, int); /* timing functions */ extern void start_timer(void); extern double elapsed_time(void); extern double stop_time(void); int main(int argc, char *argv[]) { int i, j; int ch; int edge; int size; int nloop=5; double **ptr_loc; armci_msg_init(&argc,&argv); nproc = armci_msg_nproc(); me = armci_msg_me(); while ((ch = getopt(argc, argv, "n:b:p:h")) != -1) { switch(ch) { case 'n': n = atoi(optarg); break; case 'b': block_size = atoi(optarg); break; case 'p': nproc = atoi(optarg); break; case 'h': { printf("Usage: LU, or \n"); printf(" LU -nMATRIXSIZE -bBLOCKSIZE -pNPROC\n"); armci_msg_barrier(); armci_msg_finalize(); exit(0); } } } if(me == 0) { printf("\n Blocked Dense LU Factorization\n"); printf(" %d by %d Matrix\n", n, n); printf(" %d Processors\n", nproc); printf(" %d by %d Element Blocks\n", block_size, block_size); printf("\n"); } num_rows = (int) sqrt((double) nproc); for (;;) { num_cols = nproc/num_rows; if (num_rows*num_cols == nproc) break; num_rows--; } nblocks = n/block_size; if (block_size * nblocks != n) { nblocks++; } edge = n%block_size; if (edge == 0) { edge = block_size; } #ifdef DEBUG if(me == 0) for (i=0;i n) { kl = n; strK = kl - k; } else { strK = bs; } /* factor diagonal block */ diagowner = block_owner(K, K); if (diagowner == me) { A = a[K+K*nblocks]; lu0(A, strK, strK); } armci_msg_barrier(); /* divide column k by diagonal block */ if(block_owner(K, K) == me) D = a[K+K*nblocks]; else { D = buf1; get_remote(D, K, K); } for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } A = a[I+K*nblocks]; bdiv(A, D, strI, strK, strI, strK); } } /* modify row k by diagonal block */ for (j=kl, J=K+1; j n) { jl = n; strJ = jl - j; } else { strJ = bs; } A = a[K+J*nblocks]; bmodd(D, A, strK, strJ, strK, strK); } } armci_msg_barrier(); /* modify subsequent block columns */ for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } if(block_owner(I,K) == me) A = a[I+K*nblocks]; else { A = buf1; get_remote(A, I, K); } for (j=kl, J=K+1; j n) { jl = n; strJ= jl - j; } else { strJ = bs; } if (block_owner(I, J) == me) { /* parcel out blocks */ if(block_owner(K,J) == me) B = a[K+J*nblocks]; else { B = buf2; get_remote(B, K, J); } C = a[I+J*nblocks]; bmod(A, B, C, strI, strJ, strK, strI, strK, strI); } } } } free(buf1); free(buf2); } void get_remote(double *buf, int I, int J) { int proc_owner; int edge, size; double t1; proc_owner = block_owner(I, J); edge = n%block_size; if (edge == 0) { edge = block_size; } if ((I == nblocks-1) && (J == nblocks-1)) { size = edge*edge; } else if ((I == nblocks-1) || (J == nblocks-1)) { size = edge*block_size; } else { size = block_size*block_size; } size = size * sizeof(double); t1 = armci_timer(); #ifdef MPI2_ONESIDED { int target_disp = ( ((char*)(a[I+J*nblocks])) - ((char*)(ptr[proc_owner])) ); if(target_disp<0) { printf("ERROR!: target disp is < 0, target_disp= %d\n", target_disp); MPI_Abort(MPI_COMM_WORLD, 1); } MPI_Win_lock(MPI_LOCK_EXCLUSIVE, proc_owner, 0, win); MPI_Get(buf, size, MPI_CHAR, proc_owner, target_disp, size, MPI_CHAR, win); MPI_Win_unlock(proc_owner, win); } #else ARMCI_Get(a[I+J*nblocks], buf, size, proc_owner); #endif comm_time += armci_timer() - t1; get_cntr++; } void lu0(double *a, int n, int stride) { int j; int k; /*int length;*/ double alpha; for (k=0; k #endif #if HAVE_SYS_ERRNO_H # include #endif #if HAVE_SYS_TIME_H # include #endif /** Timing routines that use standard Unix gettingofday() */ static struct timezone tz; static struct timeval start_time, finish_time; /** Start measuring a time delay */ void start_timer(void) { gettimeofday( &start_time, &tz); } /** Retunrn elapsed time in milliseconds */ double elapsed_time(void) { gettimeofday( &finish_time, &tz); return(1000.0*(finish_time.tv_sec - start_time.tv_sec) + (finish_time.tv_usec - start_time.tv_usec)/1000.0 ); } /** Return the stopping time in milliseconds */ double stop_time(void) { gettimeofday( &finish_time, &tz); return(1000.0*finish_time.tv_sec + finish_time.tv_usec/1000.0); } ga-5.9.2/armci/examples/benchmarks/lu/armci_multithreaded/000077500000000000000000000000001500715745200236005ustar00rootroot00000000000000ga-5.9.2/armci/examples/benchmarks/lu/armci_multithreaded/lu-block-th-nbget.c000066400000000000000000000507701500715745200271730ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /************************************************** * LU factorization * * Armci Version * * Block distribution * * Multi-threaded * **************************************************/ #define DEBUG #define DEBUG1_ #define DEBUG2_ #define DEBUG3_ #define USE_MUTEX_ #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STDARG_H # include #endif #include "armci.h" #include "utils.h" #include "message.h" #define MAXRAND 32767.0 #define DEFAULT_N 8 #define DEFAULT_B 2 #define MAX_THREADS 8 /* global variables */ int n = DEFAULT_N; /* The size of the matrix */ int block_size = DEFAULT_B;/* Block dimension */ int nblocks; /* Number of blocks in each dimension */ int num_rows; /* Number of processors per row of processor grid */ int num_cols; /* Number of processors per col of processor grid */ double **a; /* a = lu; l and u both placed back in a */ int nproc, th_per_p = 1, nthreads, me = 0, me_th[MAX_THREADS]; thread_t threads[MAX_THREADS]; int proc_bytes, thread_doubles[MAX_THREADS]; int num; int doprint = 1; int d = 0; /* delay */ thread_lock_t mutex; FILE *rep[MAX_THREADS]; char fname[] = "threadXX.log"; void report(int th_idx, char *fmt, ...) { #ifdef DEBUG3 va_list ap; va_start(ap, fmt); vfprintf(rep[th_idx], fmt, ap); va_end(ap); #endif } /* function declaration */ void *lu(void *); void lu0(double *,int, int); void bdiv(double *, double *, int, int, int, int); void bmodd(double *, double*, int, int, int, int); void bmod(double *, double *, double *, int, int, int, int, int, int); void daxpy(double *, double *, int, double); int block_owner(int, int); void init_array(); double touch_array(int, int); void print_array(int); void print_block_dbg(double *, const char *, int, int, int); void prefetch(double **A, double *buf, int I, int J, int th_idx, armci_hdl_t **hdlp); void get_remote(double *buf, int I, int J, armci_hdl_t **hdlp); int next_block(int th_idx, int bs, int kl, int ci, int cj, int cI, int cJ, int K, int *pI, int *pJ); /* timing functions */ extern void start_timer(void); extern double elapsed_time(void); extern double stop_time(void); main(int argc, char *argv[]) { int i, j, l; int ch; extern char *optarg; int edge; int size; int lu_arg[MAX_THREADS][3]; /* ARMCI */ void **ptr; double **ptr_loc; THREAD_LOCK_INIT(mutex); armci_msg_init(&argc,&argv); nproc = armci_msg_nproc(); me = armci_msg_me(); while ((ch = getopt(argc, argv, "n:b:p:t:d:h")) != -1) { switch(ch) { case 'n': n = atoi(optarg); break; case 'b': block_size = atoi(optarg); break; case 'p': nproc = atoi(optarg); break; case 't': th_per_p = atoi(optarg); break; case 'd': d = atoi(optarg); break; case 'h': { printf("Usage: LU, or \n"); printf(" LU -nMATRIXSIZE -bBLOCKSIZE -pNPROC -tTH_PER_P\n"); armci_msg_barrier(); armci_msg_finalize(); exit(0); } } } if(th_per_p>MAX_THREADS) { th_per_p=MAX_THREADS; if(me==0)printf("Warning: cannot run more than %d threads, adjust MAX_THREADS",MAX_THREADS); } if (d) { fprintf(stderr, "%d: %d\n", me, getpid()); sleep(d); } nthreads = th_per_p * nproc; if(me == 0) { printf("\n Blocked Dense LU Factorization\n"); printf(" %d by %d Matrix\n", n, n); printf(" %d Processors\n", nproc); printf(" %d thread(s) per processor, %d threads total\n", th_per_p, nthreads); printf(" %d by %d Element Blocks\n", block_size, block_size); printf("\n"); } num_rows = (int) sqrt((double) nthreads); for (;;) { num_cols = nthreads/num_rows; if (num_rows*num_cols == nthreads) break; num_rows--; } nblocks = n/block_size; if (block_size * nblocks != n) { nblocks++; } num = (nblocks * nblocks)/nthreads; if((num * nthreads) != (nblocks * nblocks)) num++; edge = n%block_size; if (edge == 0) { edge = block_size; } #ifdef DEBUG if(me == 0) for (i=0;i n) { kl = n; strK = kl - k; } else { strK = bs; } /* factor diagonal block */ diagowner = block_owner(K, K); if (diagowner == me_th[th_idx]) { A = a[K+K*nblocks]; lu0(A, strK, strK); } MT_BARRIER(); /* divide column k by diagonal block */ if(block_owner(K, K) == me_th[th_idx]) D = a[K+K*nblocks]; else { D = buf1; hdl1p = NULL; get_remote(D, K, K, &hdl1p); } for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } A = a[I+K*nblocks]; bdiv(A, D, strI, strK, strI, strK); } } /* modify row k by diagonal block */ for (j=kl, J=K+1; j n) { jl = n; strJ = jl - j; } else { strJ = bs; } A = a[K+J*nblocks]; bmodd(D, A, strK, strJ, strK, strK); } } MT_BARRIER(); /* prefetch (A1 and B1) */ AB = 0; /* 0 if using A1 and B1 (buf1 and buf2), 1 if A2 and B2 (buf3 and buf4) */ if (next_block(th_idx, bs, kl, kl, kl, K+1, K+1, K, &I, &J)) { report(th_idx, "ij: next %d,%d\n", I, J); /* next block to be computed (I,J) needs blocks A=(I,K) and J=(K,J) */ hdl1p = &hdl1; hdl2p = &hdl2; prefetch(&A1, buf1, I, K, th_idx, &hdl1p); prefetch(&B1, buf2, K, J, th_idx, &hdl2p); } else continue; /* modify subsequent block columns */ for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } for (j=kl, J=K+1; j n) { jl = n; strJ= jl - j; } else { strJ = bs; } if (block_owner(I, J) == me_th[th_idx]) { /* parcel out blocks */ report(th_idx, "ij: real %d,%d\n", I, J); /* wait for previously prefetched */ A = AB ? A2 : A1; B = AB ? B2 : B1; /* actual wait */ if (hdl1p) ARMCI_Wait(hdl1p); if (hdl2p) ARMCI_Wait(hdl2p); /* prefetch next A and B */ if (next_block(th_idx, bs, kl, i, j+bs, I, J+1, K, &pI, &pJ)) { report(th_idx, "ij: next %d,%d\n", pI, pJ); hdl1p = &hdl1; hdl2p = &hdl2; if (AB) { /* prefetch into A2 and B2 */ prefetch(&A1, buf1, pI, K, th_idx, &hdl1p); prefetch(&B1, buf2, K, pJ, th_idx, &hdl2p); } else { /* prefetch into A1 and B1 */ prefetch(&A2, buf3, pI, K, th_idx, &hdl1p); prefetch(&B2, buf4, K, pJ, th_idx, &hdl2p); } AB = AB ? 0 : 1; } C = a[I+J*nblocks]; bmod(A, B, C, strI, strJ, strK, strI, strK, strI); } } } } free(buf1); free(buf2); free(buf3); free(buf4); } void prefetch(double **A, double *buf, int I, int J, int th_idx, armci_hdl_t **hdlp) { if (block_owner(I,J) == me_th[th_idx]) { *A = a[I+J*nblocks]; *hdlp = NULL; /* local should not ARMCI_Wait */ } else { *A = buf; get_remote(*A, I, J, hdlp); } } void get_remote(double *buf, int I, int J, armci_hdl_t **hdlp) { int proc_owner; int edge, size; #ifdef USE_MUTEX THREAD_LOCK(mutex); #endif proc_owner = block_owner(I, J) / th_per_p; edge = n%block_size; if (edge == 0) { edge = block_size; } if ((I == nblocks-1) && (J == nblocks-1)) { size = edge*edge; } else if ((I == nblocks-1) || (J == nblocks-1)) { size = edge*block_size; } else { size = block_size*block_size; } size = size * sizeof(double); if (proc_owner == me) { memcpy(buf, a[I+J*nblocks], size); *hdlp = NULL; /* local should not ARMCI_Wait */ } else if (*hdlp) ARMCI_NbGet(a[I+J*nblocks], buf, size, proc_owner, *hdlp); else ARMCI_Get(a[I+J*nblocks], buf, size, proc_owner); #ifdef USE_MUTEX THREAD_UNLOCK(mutex); #endif } /* returns 1 if there is another block on current processor to be computed for some K * returns 0 otherwise; location of the block is stored in pI, pJ */ int next_block(int th_idx, int bs, int kl, int ci, int cj, int cI, int cJ, int K, int *pI, int *pJ) { int i, j, I, J; j = cj; J = cJ; for (i=ci, I=cI; i #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #include "armci.h" #include "utils.h" #include "message.h" #define MAXRAND 32767.0 #define DEFAULT_N 8 #define DEFAULT_B 2 #define MAX_THREADS 8 /* global variables */ int n = DEFAULT_N; /* The size of the matrix */ int block_size = DEFAULT_B;/* Block dimension */ int nblocks; /* Number of blocks in each dimension */ int num_rows; /* Number of processors per row of processor grid */ int num_cols; /* Number of processors per col of processor grid */ double **a; /* a = lu; l and u both placed back in a */ int nproc, th_per_p = 1, nthreads, me = 0, me_th[MAX_THREADS]; thread_t threads[MAX_THREADS]; int proc_bytes, thread_doubles[MAX_THREADS]; int num; int doprint = 1; int d = 0; /* delay */ thread_lock_t mutex; /* function declaration */ void *lu(void *); void lu0(double *,int, int); void bdiv(double *, double *, int, int, int, int); void bmodd(double *, double*, int, int, int, int); void bmod(double *, double *, double *, int, int, int, int, int, int); void daxpy(double *, double *, int, double); int block_owner(int, int); void init_array(); double touch_array(int, int); void print_array(int); void print_block_dbg(double *, const char *, int, int, int); void get_remote(double *, int, int); /* timing functions */ extern void start_timer(void); extern double elapsed_time(void); extern double stop_time(void); main(int argc, char *argv[]) { int i, j, l; int ch; extern char *optarg; int edge; int size; int lu_arg[MAX_THREADS][3]; /* ARMCI */ void **ptr; double **ptr_loc; THREAD_LOCK_INIT(mutex); armci_msg_init(&argc,&argv); nproc = armci_msg_nproc(); me = armci_msg_me(); while ((ch = getopt(argc, argv, "n:b:p:t:d:h")) != -1) { switch(ch) { case 'n': n = atoi(optarg); break; case 'b': block_size = atoi(optarg); break; case 'p': nproc = atoi(optarg); break; case 't': th_per_p = atoi(optarg); break; case 'd': d = atoi(optarg); break; case 'h': { printf("Usage: LU, or \n"); printf(" LU -nMATRIXSIZE -bBLOCKSIZE -pNPROC -tTH_PER_P\n"); armci_msg_barrier(); armci_msg_finalize(); exit(0); } } } if(th_per_p>MAX_THREADS) { th_per_p=MAX_THREADS; if(me==0)printf("Warning: cannot run more than %d threads, adjust MAX_THREADS",MAX_THREADS); } if (d) { fprintf(stderr, "%d: %d\n", me, getpid()); sleep(d); } nthreads = th_per_p * nproc; if(me == 0) { printf("\n Blocked Dense LU Factorization\n"); printf(" %d by %d Matrix\n", n, n); printf(" %d Processors\n", nproc); printf(" %d thread(s) per processor, %d threads total\n", th_per_p, nthreads); printf(" %d by %d Element Blocks\n", block_size, block_size); printf("\n"); } num_rows = (int) sqrt((double) nthreads); for (;;) { num_cols = nthreads/num_rows; if (num_rows*num_cols == nthreads) break; num_rows--; } nblocks = n/block_size; if (block_size * nblocks != n) { nblocks++; } num = (nblocks * nblocks)/nthreads; if((num * nthreads) != (nblocks * nblocks)) num++; edge = n%block_size; if (edge == 0) { edge = block_size; } #ifdef DEBUG if(me == 0) for (i=0;i n) { kl = n; strK = kl - k; } else { strK = bs; } /* factor diagonal block */ diagowner = block_owner(K, K); if (diagowner == me_th[th_idx]) { A = a[K+K*nblocks]; print_block_dbg(A, "th=%d, idx=%d: before lu0 a[%d]:\n", me_th[th_idx], th_idx, K+K*nblocks); lu0(A, strK, strK); } MT_BARRIER(); /* divide column k by diagonal block */ if(block_owner(K, K) == me_th[th_idx]) D = a[K+K*nblocks]; else { D = buf1; get_remote(D, K, K); } for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } A = a[I+K*nblocks]; bdiv(A, D, strI, strK, strI, strK); } } /* modify row k by diagonal block */ for (j=kl, J=K+1; j n) { jl = n; strJ = jl - j; } else { strJ = bs; } A = a[K+J*nblocks]; bmodd(D, A, strK, strJ, strK, strK); } } MT_BARRIER(); /* modify subsequent block columns */ for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } if(block_owner(I,K) == me_th[th_idx]) A = a[I+K*nblocks]; else { A = buf1; get_remote(A, I, K); } for (j=kl, J=K+1; j n) { jl = n; strJ= jl - j; } else { strJ = bs; } if (block_owner(I, J) == me_th[th_idx]) { /* parcel out blocks */ if(block_owner(K,J) == me_th[th_idx]) B = a[K+J*nblocks]; else { B = buf2; get_remote(B, K, J); } C = a[I+J*nblocks]; bmod(A, B, C, strI, strJ, strK, strI, strK, strI); } } } } free(buf1); free(buf2); return lu_arg; } void get_remote(double *buf, int I, int J) { int proc_owner; int edge, size; #ifdef USE_MUTEX THREAD_LOCK(mutex); #endif proc_owner = block_owner(I, J) / th_per_p; edge = n%block_size; if (edge == 0) { edge = block_size; } if ((I == nblocks-1) && (J == nblocks-1)) { size = edge*edge; } else if ((I == nblocks-1) || (J == nblocks-1)) { size = edge*block_size; } else { size = block_size*block_size; } size = size * sizeof(double); if (proc_owner == me) memcpy(buf, a[I+J*nblocks], size); else ARMCI_Get(a[I+J*nblocks], buf, size, proc_owner); #ifdef USE_MUTEX THREAD_UNLOCK(mutex); #endif } void lu0(double *a, int n, int stride) { int j; int k; int length; double alpha; for (k=0; k #endif #if HAVE_SYS_ERRNO_H # include #endif #if HAVE_SYS_TIME_H # include #endif /** Timing routines that use standard Unix gettingofday() */ static struct timezone tz; static struct timeval start_time, finish_time; /** Start measuring a time delay */ void start_timer(void) { gettimeofday( &start_time, &tz); } /** Retunrn elapsed time in milliseconds */ double elapsed_time(void) { gettimeofday( &finish_time, &tz); return(1000.0*(finish_time.tv_sec - start_time.tv_sec) + (finish_time.tv_usec - start_time.tv_usec)/1000.0 ); } /** Return the stopping time in milliseconds */ double stop_time(void) { gettimeofday( &finish_time, &tz); return(1000.0*finish_time.tv_sec + finish_time.tv_usec/1000.0); } ga-5.9.2/armci/examples/benchmarks/lu/armci_nonblocking/000077500000000000000000000000001500715745200232505ustar00rootroot00000000000000ga-5.9.2/armci/examples/benchmarks/lu/armci_nonblocking/lu_nb_get.c000066400000000000000000000357561500715745200253720ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /************************************************** * LU factorization * * Armci Version * **************************************************/ /***************** Non-blocking Version Pre-GETing ******************/ #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_STRING_H # include #endif #include "armci.h" #include "message.h" /* #define DEBUG */ #define MAXRAND 32767.0 #define DEFAULT_N 8 #define DEFAULT_B 2 #define MAXPROC 256 /* Maximum number of processors */ #define MAXBLOCK 2048 /* Maximum number of blocks in a row/column */ #define ANULL (armci_hdl_t *)NULL /* global variables */ int n = DEFAULT_N; /* The size of the matrix */ int block_size = DEFAULT_B;/* Block dimension */ int nblocks; /* Number of blocks in each dimension */ int num_rows; /* Number of processors per row of processor grid */ int num_cols; /* Number of processors per col of processor grid */ double **a; /* a = lu; l and u both placed back in a */ int nproc, me = 0; int proc_bytes; int doprint = 1; /* make it 1 to see the LU decomposition output */ /*funnction declaration */ void lu(int, int, int); void lu0(double *,int, int); void bdiv(double *, double *, int, int, int, int); void bmodd(double *, double*, int, int, int, int); void bmod(double *, double *, double *, int, int, int, int, int, int); void daxpy(double *, double *, int, double); int block_owner(int, int); void init_array(); double touch_array(int, int); void print_block(); void print_array(int); void get_remote(double *, int, int, armci_hdl_t *); /* timing functions */ extern void start_timer(void); extern double elapsed_time(void); extern double stop_time(void); int main(int argc, char *argv[]) { int i, j; int ch; int edge; int size; /* ARMCI */ void **ptr; double **ptr_loc; armci_msg_init(&argc,&argv); nproc = armci_msg_nproc(); me = armci_msg_me(); while ((ch = getopt(argc, argv, "n:b:p:h")) != -1) { switch(ch) { case 'n': n = atoi(optarg); break; case 'b': block_size = atoi(optarg); break; case 'p': nproc = atoi(optarg); break; case 'h': { printf("Usage: LU, or \n"); printf(" LU -nMATRIXSIZE -bBLOCKSIZE -pNPROC\n"); armci_msg_barrier(); armci_msg_finalize(); exit(0); } } } if(me == 0) { printf("\n Using pre_FETCHing \n"); printf("\n Blocked Dense LU Factorization\n"); printf(" %d by %d Matrix\n", n, n); printf(" %d Processors\n", nproc); printf(" %d by %d Element Blocks\n", block_size, block_size); printf("\n"); } num_rows = (int) sqrt((double) nproc); for (;;) { num_cols = nproc/num_rows; if (num_rows*num_cols == nproc) break; num_rows--; } nblocks = n/block_size; if (block_size * nblocks != n) { nblocks++; } edge = n%block_size; if (edge == 0) { edge = block_size; } #ifdef DEBUG if(me == 2) { for (i=0;i n) { kl = n; strK = kl - k; } else { strK = bs; } /* factor diagonal block */ diagowner = block_owner(K, K); if (diagowner == me) { A = a[K+K*nblocks]; lu0(A, strK, strK); } armci_msg_barrier(); /* divide column k by diagonal block */ if(block_owner(K, K) == me) D = a[K+K*nblocks]; else { D = dbuf; get_remote(D, K, K, NULL); } for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } A = a[I+K*nblocks]; bdiv(A, D, strI, strK, strI, strK); } } /* modify row k by diagonal block */ for (j=kl, J=K+1; j n) { jl = n; strJ = jl - j; } else { strJ = bs; } A = a[K+J*nblocks]; bmodd(D, A, strK, strJ, strK, strK); } } armci_msg_barrier(); /* modify subsequent block columns */ t1 = t2 = 0; memset(br, 0, sizeof(br)); memset(bc, 0, sizeof(bc)); for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } for (j=kl, J=K+1; j n) { jl = n; strJ= jl - j; } else { strJ = bs; } if (block_owner(I, J) == me) { /* parcel out blocks */ /* Pre-fetch next two blocks that will be required by me */ /* First, identify the next IJ-th block owned by me */ /* This caculation is for block-cyclic distribution */ r = I; c = J + num_cols; if (c >= nblocks) { r = I + num_rows; w = J - (K+1); if (w >= num_cols) c = w%num_cols + (K+1); else c = J; } /* This processor will need the blocks [r,K] and [K, c] next */ /* Now, pre-fetch blocks [r,K] and [K,c] using non-blocking gets*/ if (r < nblocks && c < nblocks) { if (!br[c] && block_owner(K, c) != me) { /* if this block has not been pre-fetched yet and if I already don't own it*/ if (hdl1 == NULL) {/* this is the first time, no previous non-blocking call */ get_remote(bufr[c], K, c, hdl1); } else { if (!ARMCI_Wait(hdl1)) {/* only if previous call with hdl1 returned, then fetch next block */ get_remote(bufr[c], K, c, hdl1); t1 = 1; } } } if (!bc[r] && block_owner(r, K) != me) { if (hdl2 == NULL) get_remote(bufc[r], r, K, hdl2); else { if (!ARMCI_Wait(hdl2)) { get_remote(bufc[r], r, K, hdl2); t2 = 1; } } } } /* end of if (r < nblocks && c < nblocks) */ if(block_owner(I,K) == me) A = a[I+K*nblocks]; else { if (!t1) get_remote(bufc[I], I, K, NULL); /* This is the first time, so make a blocking call */ A = bufc[I]; bc[I] = 1; } if(block_owner(K,J) == me) B = a[K+J*nblocks]; else { if (!t2) get_remote(bufr[J], K, J, NULL); /* This is the first time, so make a blocking call */ B = bufr[J]; br[J] = 1; } C = a[I+J*nblocks]; bmod(A, B, C, strI, strJ, strK, strI, strK, strI); } } } } ARMCI_Free_local(dbuf); ARMCI_Free_local(bufr); ARMCI_Free_local(bufc); } void get_remote(double *buf, int I, int J, armci_hdl_t *handle) { int proc_owner; int edge, size; int rc; proc_owner = block_owner(I, J); edge = n%block_size; if (edge == 0) { edge = block_size; } if ((I == nblocks-1) && (J == nblocks-1)) { size = edge*edge; } else if ((I == nblocks-1) || (J == nblocks-1)) { size = edge*block_size; } else { size = block_size*block_size; } size = size * sizeof(double); if (handle == NULL) {/* do a blocking get */ ARMCI_Get(a[I+J*nblocks], buf, size, proc_owner); } else { if ((rc = ARMCI_NbGet(a[I+J*nblocks], buf, size, proc_owner, handle))) /* do a non-blocking get */ ARMCI_Error("Error in ARMCI_NbGet", rc); } } void lu0(double *a, int n, int stride) { int j; int k; /*int length;*/ double alpha; for (k=0; k #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_STRING_H # include #endif #include "armci.h" #include "message.h" /*#define DEBUG*/ #define MAXRAND 32767.0 #define DEFAULT_N 8 #define DEFAULT_B 2 #define MAXPROC 256 /* Maximum number of processors */ /* global variables */ int n = DEFAULT_N; /* The size of the matrix */ int block_size = DEFAULT_B;/* Block dimension */ int nblocks; /* Number of blocks in each dimension */ int num_rows; /* Number of processors per row of processor grid */ int num_cols; /* Number of processors per col of processor grid */ double **a; /* a = lu; l and u both placed back in a */ int nproc, me = 0; int proc_bytes; int doprint = 0; /* make it 1 to see LU decomposition output */ /* Buffers for pre-fetched data */ double **bufr, **bufc; /* function declaration */ void lu(int, int, int); void lu0(double *,int, int); void bdiv(double *, double *, int, int, int, int); void bmodd(double *, double*, int, int, int, int); void bmod(double *, double *, double *, int, int, int, int, int, int); void daxpy(double *, double *, int, double); int block_owner(int, int); void init_array(); double touch_array(int, int); void print_block(); void print_array(int); void get_remote(double *, int, int); /* timing functions */ extern void start_timer(void); extern double elapsed_time(void); extern double stop_time(void); int main(int argc, char *argv[]) { int i, j; int ch; int edge; int size; /* ARMCI */ void **ptr; double **ptr_loc; void **bufr_g, **bufc_g; armci_msg_init(&argc,&argv); nproc = armci_msg_nproc(); me = armci_msg_me(); while ((ch = getopt(argc, argv, "n:b:p:h")) != -1) { switch(ch) { case 'n': n = atoi(optarg); break; case 'b': block_size = atoi(optarg); break; case 'p': nproc = atoi(optarg); break; case 'h': { printf("Usage: LU, or \n"); printf(" LU -nMATRIXSIZE -bBLOCKSIZE -pNPROC\n"); armci_msg_barrier(); armci_msg_finalize(); exit(0); } } } if(me == 0) { printf("\nUsing pre-PUTing\n"); printf("\n Blocked Dense LU Factorization\n"); printf(" %d by %d Matrix\n", n, n); printf(" %d Processors\n", nproc); printf(" %d by %d Element Blocks\n", block_size, block_size); printf("\n"); } num_rows = (int) sqrt((double) nproc); for (;;) { num_cols = nproc/num_rows; if (num_rows*num_cols == nproc) break; num_rows--; } nblocks = n/block_size; if (block_size * nblocks != n) { nblocks++; } edge = n%block_size; if (edge == 0) { edge = block_size; } #ifdef DEBUG if(me == 0) for (i=0;i n) { kl = n; strK = kl - k; } else { strK = bs; } /* factor diagonal block */ diagowner = block_owner(K, K); if (diagowner == me) { A = a[K+K*nblocks]; lu0(A, strK, strK); /* impl algo on this diag block */ } armci_msg_barrier(); /* divide column k by diagonal block */ if(block_owner(K, K) == me) D = a[K+K*nblocks]; else { D = dbuf; get_remote(D, K, K); } for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } A = a[I+K*nblocks]; bdiv(A, D, strI, strK, strI, strK); /* Pre-put this block to the block-owners of all blocks on the I-th row with a non-blocking put*/ memset (saved, 0, sizeof(saved)); for (m = K+1; m < nblocks; m++) { destp = block_owner (I, m); if (destp != me && !saved[destp]) { ARMCI_NbPut(A, bufc[destp*nblocks + I], strI*strK*sizeof(double), destp, NULL); saved[destp] = 1; } } } } /* end of for (i=k1, I=K+1...) */ /* modify row k by diagonal block */ for (j=kl, J=K+1; j n) { jl = n; strJ = jl - j; } else { strJ = bs; } A = a[K+J*nblocks]; bmodd(D, A, strK, strJ, strK, strK); /* Pre-put this block to the block-owners of all blocks on the J-th column with a non-blocking put*/ memset (saved, 0, sizeof(saved)); for (m = K+1; m < nblocks; m++) { destp = block_owner (m, J); if (destp != me && !saved[destp]) { ARMCI_NbPut(A, bufr[destp*nblocks + J], strK*strJ*sizeof(double), destp, NULL); saved[destp] = 1; } } } } ARMCI_WaitAll(); ARMCI_AllFence(); armci_msg_barrier(); /* modify subsequent block columns */ for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } for (j=kl, J=K+1; j n) { jl = n; strJ= jl - j; } else { strJ = bs; } if (block_owner(I, J) == me) { /* parcel out blocks */ if(block_owner(I,K) == me) A = a[I+K*nblocks]; else { A = bufc[me*nblocks+I]; } if(block_owner(K,J) == me) B = a[K+J*nblocks]; else B = bufr[me*nblocks + J]; C = a[I+J*nblocks]; bmod(A, B, C, strI, strJ, strK, strI, strK, strI); } } } } ARMCI_Free_local(dbuf); } void get_remote(double *buf, int I, int J) { int proc_owner; int edge, size; proc_owner = block_owner(I, J); edge = n%block_size; if (edge == 0) { edge = block_size; } if ((I == nblocks-1) && (J == nblocks-1)) { size = edge*edge; } else if ((I == nblocks-1) || (J == nblocks-1)) { size = edge*block_size; } else { size = block_size*block_size; } size = size * sizeof(double); ARMCI_Get(a[I+J*nblocks], buf, size, proc_owner); } /* Function lu0: implements the serial algo on one diag block (*a) */ void lu0(double *a, int n, int stride) { int j; int k; /*int length;*/ double alpha; for (k=0; k #endif #if HAVE_SYS_ERRNO_H # include #endif #if HAVE_SYS_TIME_H # include #endif /** Timing routines that use standard Unix gettingofday() */ static struct timezone tz; static struct timeval start_time, finish_time; /** Start measuring a time delay */ void start_timer(void) { gettimeofday( &start_time, &tz); } /** Retunrn elapsed time in milliseconds */ double elapsed_time(void) { gettimeofday( &finish_time, &tz); return(1000.0*(finish_time.tv_sec - start_time.tv_sec) + (finish_time.tv_usec - start_time.tv_usec)/1000.0 ); } /** Return the stopping time in milliseconds */ double stop_time(void) { gettimeofday( &finish_time, &tz); return(1000.0*finish_time.tv_sec + finish_time.tv_usec/1000.0); } ga-5.9.2/armci/examples/benchmarks/lu/pthreads/000077500000000000000000000000001500715745200214045ustar00rootroot00000000000000ga-5.9.2/armci/examples/benchmarks/lu/pthreads/barrier.c000066400000000000000000000103771500715745200232060ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** @file barrier.c * * This file implements the "barrier" synchronization construct. * * A barrier causes threads to wait until a set of threads has * all "reached" the barrier. The number of threads required is * set when the barrier is initialized, and cannot be changed * except by reinitializing. * * The barrier_init() and barrier_destroy() functions, * respectively, allow you to initialize and destroy the * barrier. * * The barrier_wait() function allows a thread to wait for a * barrier to be completed. One thread (the one that happens to * arrive last) will return from barrier_wait() with the status * -1 on success -- others will return with 0. The special * status makes it easy for the calling code to cause one thread * to do something in a serial region before entering another * parallel section of code. */ #include #include "errors.h" #include "barrier.h" /** * Initialize a barrier for use. */ int barrier_init (barrier_t *barrier, int count) { int status; barrier->threshold = barrier->counter = count; barrier->cycle = 0; status = pthread_mutex_init (&barrier->mutex, NULL); if (status != 0) return status; status = pthread_cond_init (&barrier->cv, NULL); if (status != 0) { pthread_mutex_destroy (&barrier->mutex); return status; } barrier->valid = BARRIER_VALID; return 0; } /** * Destroy a barrier when done using it. */ int barrier_destroy (barrier_t *barrier) { int status, status2; if (barrier->valid != BARRIER_VALID) return EINVAL; status = pthread_mutex_lock (&barrier->mutex); if (status != 0) return status; /* * Check whether any threads are known to be waiting; report * "BUSY" if so. */ if (barrier->counter != barrier->threshold) { pthread_mutex_unlock (&barrier->mutex); return EBUSY; } barrier->valid = 0; status = pthread_mutex_unlock (&barrier->mutex); if (status != 0) return status; /* * If unable to destroy either 1003.1c synchronization * object, return the error status. */ status = pthread_mutex_destroy (&barrier->mutex); status2 = pthread_cond_destroy (&barrier->cv); return (status == 0 ? status : status2); } /** * Wait for all members of a barrier to reach the barrier. When * the count (of remaining members) reaches 0, broadcast to wake * all threads waiting. */ int barrier_wait (barrier_t *barrier) { int status, cancel, tmp, cycle; if (barrier->valid != BARRIER_VALID) return EINVAL; status = pthread_mutex_lock (&barrier->mutex); if (status != 0) return status; cycle = barrier->cycle; /* Remember which cycle we're on */ if (--barrier->counter == 0) { barrier->cycle = !barrier->cycle; barrier->counter = barrier->threshold; status = pthread_cond_broadcast (&barrier->cv); /* * The last thread into the barrier will return status * -1 rather than 0, so that it can be used to perform * some special serial code following the barrier. */ if (status == 0) status = -1; } else { /* * Wait with cancellation disabled, because barrier_wait * should not be a cancellation point. */ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &cancel); /* * Wait until the barrier's cycle changes, which means * that it has been broadcast, and we don't want to wait * anymore. */ while (cycle == barrier->cycle) { status = pthread_cond_wait ( &barrier->cv, &barrier->mutex); if (status != 0) break; } pthread_setcancelstate (cancel, &tmp); } /* * Ignore an error in unlocking. It shouldn't happen, and * reporting it here would be misleading -- the barrier wait * completed, after all, whereas returning, for example, * EINVAL would imply the wait had failed. The next attempt * to use the barrier *will* return an error, or hang, due * to whatever happened to the mutex. */ pthread_mutex_unlock (&barrier->mutex); return status; /* error, -1 for waker, or 0 */ } ga-5.9.2/armci/examples/benchmarks/lu/pthreads/barrier.h000066400000000000000000000025211500715745200232030ustar00rootroot00000000000000/** @file barrier.h * * This header file describes the "barrier" synchronization * construct. The type barrier_t describes the full state of the * barrier including the POSIX 1003.1c synchronization objects * necessary. * * A barrier causes threads to wait until a set of threads has * all "reached" the barrier. The number of threads required is * set when the barrier is initialized, and cannot be changed * except by reinitializing. */ #include /** * Structure describing a barrier. */ typedef struct barrier_tag { pthread_mutex_t mutex; /**< Control access to barrier */ pthread_cond_t cv; /**< wait for barrier */ int valid; /**< set when valid */ int threshold; /**< number of threads required */ int counter; /**< current number of threads */ int cycle; /**< alternate wait cycles (0 or 1) */ } barrier_t; #define BARRIER_VALID 0xdbcafe /** * Support static initialization of barriers */ #define BARRIER_INITIALIZER(cnt) \ {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, \ BARRIER_VALID, cnt, cnt, 0} /** * Define barrier functions */ extern int barrier_init (barrier_t *barrier, int count); extern int barrier_destroy (barrier_t *barrier); extern int barrier_wait (barrier_t *barrier); ga-5.9.2/armci/examples/benchmarks/lu/pthreads/errors.h000066400000000000000000000030351500715745200230720ustar00rootroot00000000000000#ifndef __errors_h #define __errors_h #if HAVE_UNISTD_H # include #endif #if HAVE_ERRNO_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif /** * Define a macro that can be used for diagnostic output from * examples. When compiled -DDEBUG, it results in calling printf * with the specified argument list. When DEBUG is not defined, it * expands to nothing. */ #ifdef DEBUG # define DPRINTF(arg) printf arg #else # define DPRINTF(arg) #endif /** * NOTE: the "do {" ... "} while (0);" bracketing around the macros * allows the err_abort and errno_abort macros to be used as if they * were function calls, even in contexts where a trailing ";" would * generate a null statement. For example, * * if (status != 0) * err_abort (status, "message"); * else * return status; * * will not compile if err_abort is a macro ending with "}", because * C does not expect a ";" to follow the "}". Because C does expect * a ";" following the ")" in the do...while construct, err_abort and * errno_abort can be used as if they were function calls. */ #define err_abort(code,text) do { \ fprintf (stderr, "%s at \"%s\":%d: %s\n", \ text, __FILE__, __LINE__, strerror (code)); \ abort (); \ } while (0) #define errno_abort(text) do { \ fprintf (stderr, "%s at \"%s\":%d: %s\n", \ text, __FILE__, __LINE__, strerror (errno)); \ abort (); \ } while (0) #endif ga-5.9.2/armci/examples/benchmarks/lu/pthreads/lu-thread.c000066400000000000000000000467371500715745200234560ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** @file * Copyright (c) 1999 Pacific Northwest National Laboratory * All rights reserved. * * Author: Jialin Ju Account, PNNL * NAME * lu-thread.c * PURPOSE * Thread version of lu factorization * NOTES * * HISTORY * jju - Apr 19, 1999: Created. */ #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #include #include "barrier.h" #include "errors.h" #define MAXRAND 32767.0 #define DEFAULT_N 512 #define DEFAULT_P 1 #define DEFAULT_B 16 #define min(a,b) ((a) < (b) ? (a) : (b)) #define MAXTHR 16 barrier_t barrier; pthread_t thread[MAXTHR]; int global_id; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_attr_t attr; int num; /* timing functions */ extern void start_timer(void); extern double elapsed_time(void); extern double stop_time(void); int n = DEFAULT_N; /* The size of the matrix */ int P = DEFAULT_P; /* Number of processors */ int block_size = DEFAULT_B; /* Block dimension */ int nblocks; /* Number of blocks in each dimension */ int num_rows; /* Number of processors per row of processor grid */ int num_cols; /* Number of processors per col of processor grid */ double **a; /* a = lu; l and u both placed back in a */ double *rhs; int *proc_bytes; /* Bytes to malloc per processor to hold blocks of A*/ double **last_malloc; /* Starting point of last block of A */ int test_result = 0; /* Test result of factorization? */ int doprint = 0; /* Print out matrix values? */ int dostats = 0; /* Print out individual processor statistics? */ void *SlaveStart(); void OneSolve(int, int, double **, int, int); void lu0(double *,int, int); void bdiv(double *, double *, int, int, int, int); void bmodd(double *, double*, int, int, int, int); void bmod(double *, double *, double *, int, int, int, int, int, int); void daxpy(double *, double *, int, double); int BlockOwner(int, int); void lu(int, int, int, int); void InitA(double *); double TouchA(int, int); void PrintA(); void CheckResult(int, double **, double *); void printerr(char *); int main(int argc, char **argv) { int i, j; int ch; int MyNum=0; int proc_num; int edge; int size; int status; while ((ch = getopt(argc, argv, "n:p:b:toh")) != -1) { switch(ch) { case 'n': n = atoi(optarg); break; case 'p': P = atoi(optarg); break; case 'b': block_size = atoi(optarg); break; case 't': test_result = !test_result; break; case 'o': doprint = !doprint; break; case 'h': printf("Usage: LU \n\n"); printf("options:\n"); printf(" -nN : Decompose NxN matrix.\n"); printf(" -pP : P = number of processors.\n"); printf(" -bB : Use a block size of B.\n"); printf(" -t : Test output.\n"); printf(" -o : Print out matrix values.\n"); printf(" -h : Print out command line options.\n\n"); printf("Default: LU -n%1d -p%1d -b%1d\n", DEFAULT_N,DEFAULT_P,DEFAULT_B); exit(0); break; } } printf("\n"); printf("Blocked Dense LU Factorization\n"); printf(" %d by %d Matrix\n",n,n); printf(" %d Processors\n",P); printf(" %d by %d Element Blocks\n",block_size,block_size); printf("\n"); printf("\n"); num_rows = (int) sqrt((double) P); for (;;) { num_cols = P/num_rows; if (num_rows*num_cols == P) break; num_rows--; } nblocks = n/block_size; if (block_size * nblocks != n) { nblocks++; } num = (nblocks * nblocks)/P; if((num * P) != (nblocks * nblocks)) num++; edge = n%block_size; if (edge == 0) { edge = block_size; } proc_bytes = (int *) malloc(P*sizeof(int)); last_malloc = (double **) malloc(P*sizeof(double *)); for (i=0;iidlock); */ global_id = 0; pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); for (i=1; iidlock) */ status = pthread_mutex_lock(&mutex); if(status != 0) err_abort(status, "Lock mutex"); MyNum = global_id; global_id ++; /* UNLOCK(Global->idlock) */ status = pthread_mutex_unlock(&mutex); if(status != 0) err_abort(status, "Unlock mutex"); /* printf("binging..\n"); bindprocessor(BINDTHREAD, pthread_self(), MyNum); printf("binging in %d \n", sysconf(_SC_NPROCESSORS_CONF)); */ /* POSSIBLE ENHANCEMENT: Here is where one might pin processes to processors to avoid migration */ OneSolve(n, block_size, a, MyNum, dostats); return NULL; } void OneSolve(n, block_size, a, MyNum, dostats) double **a; int n; int block_size; int MyNum; int dostats; { int status; /* barrier to ensure all initialization is done */ /* BARRIER(Global->start, P); */ status = barrier_wait (&barrier); if (status > 0) err_abort (status, "Wait on barrier"); /* to remove cold-start misses, all processors touch their own data */ TouchA(block_size, MyNum); /* BARRIER(Global->start, P);*/ status = barrier_wait (&barrier); if (status > 0) err_abort (status, "Wait on barrier"); /* Starting the timer */ if(MyNum == 0) start_timer(); lu(n, block_size, MyNum, dostats); /* BARRIER(Global->start, P); */ status = barrier_wait (&barrier); if (status > 0) err_abort (status, "Wait on barrier"); /* Timer Stops here */ if(MyNum == 0) printf("\nRunning time = %f milliseconds.\n\n", elapsed_time()); } void lu0(a, n, stride) double *a; int n; int stride; { int j; int k; /*int length;*/ double alpha; for (k=0; k n) { kl = n; strK = kl - k; } else { strK = bs; } #ifdef DEBUG printf("k = %d, before factorization\n", k); PrintA(); #endif /* factor diagonal block */ diagowner = BlockOwner(K, K); if (diagowner == MyNum) { A = a[K+K*nblocks]; lu0(A, strK, strK); } #ifdef DEBUG printf("k = %d, after factorization\n", k); PrintA(); #endif /* BARRIER(Global->start, P); */ status = barrier_wait (&barrier); if (status > 0) err_abort (status, "Wait on barrier"); /* divide column k by diagonal block */ D = a[K+K*nblocks]; for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } A = a[I+K*nblocks]; bdiv(A, D, strI, strK, strI, strK); } } #ifdef DEBUG printf("k = %d, after divide column k by diagonal block\n", k); PrintA(); #endif /* modify row k by diagonal block */ for (j=kl, J=K+1; j n) { jl = n; strJ = jl - j; } else { strJ = bs; } A = a[K+J*nblocks]; bmodd(D, A, strK, strJ, strK, strK); } } #ifdef DEBUG printf("k = %d, after modify row k by diagonal block\n", k); PrintA(); #endif /* BARRIER(Global->start, P); */ status = barrier_wait (&barrier); if (status > 0) err_abort (status, "Wait on barrier"); /* modify subsequent block columns */ for (i=kl, I=K+1; i n) { il = n; strI = il - i; } else { strI = bs; } /*colowner = BlockOwner(I,K);*/ A = a[I+K*nblocks]; for (j=kl, J=K+1; j n) { jl = n; strJ= jl - j; } else { strJ = bs; } if (BlockOwner(I, J) == MyNum) { /* parcel out blocks */ B = a[K+J*nblocks]; C = a[I+J*nblocks]; bmod(A, B, C, strI, strJ, strK, strI, strK, strI); } } } #ifdef DEBUG printf("k = %d, after modify subsequent block columns\n", k); PrintA(); #endif } } void InitA(rhs) double *rhs; { int i, j; int ii, jj; int edge; int ibs; int jbs, skip; srand48((long) 1); edge = n%block_size; for (j=0; j=0; j--) { for (i=0; i 0.00001) { bogus = 1; max_diff = diff; } } if (bogus) { printf("TEST FAILED: (%.5f diff)\n", max_diff); } else { printf("TEST PASSED\n"); } free(y); } void printerr(s) char *s; { fprintf(stderr,"ERROR: %s\n",s); } ga-5.9.2/armci/examples/benchmarks/lu/pthreads/timing.c000066400000000000000000000023541500715745200230430ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** @file * Copyright (c) 1995 by PDCL Corporation. All Rights Reserved. * * NAME * timing.c * PURPOSE * Timing routines for calculating the execution time: * void start_timer(void); Set the timer. * double elapsed_time(void); Return the timing elapsed since * the timer has been set. * NOTES * Jialin Ju - Oct 16, 1995 Created. */ #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_ERRNO_H # include #endif #if HAVE_SYS_TIME_H # include #endif /** Timing routines that use standard Unix gettingofday() */ static struct timezone tz; static struct timeval start_time, finish_time; /** Start measuring a time delay */ void start_timer(void) { gettimeofday( &start_time, &tz); } /** Retunrn elapsed time in milliseconds */ double elapsed_time(void) { gettimeofday( &finish_time, &tz); return(1000.0*(finish_time.tv_sec - start_time.tv_sec) + (finish_time.tv_usec - start_time.tv_usec)/1000.0 ); } /** Return the stopping time in milliseconds */ double stop_time(void) { gettimeofday( &finish_time, &tz); return(1000.0*finish_time.tv_sec + finish_time.tv_usec/1000.0); } ga-5.9.2/armci/examples/examples_config.h000066400000000000000000000001501500715745200203450ustar00rootroot00000000000000ARMCI_HOME = ../../../../src ifndef TARGET error: @echo "TARGET machine not defined" exit endif ga-5.9.2/armci/examples/features/000077500000000000000000000000001500715745200166535ustar00rootroot00000000000000ga-5.9.2/armci/examples/features/aggregation/000077500000000000000000000000001500715745200211425ustar00rootroot00000000000000ga-5.9.2/armci/examples/features/aggregation/README000066400000000000000000000064201500715745200220240ustar00rootroot00000000000000Introduction ============ ARMCI has support for aggregating messages via its aggregate non-blocking handles. Any ARMCI non-blocking handle can be explicitly made as an aggregate handle and all subsequent messages that use the same handle are aggregated and sent as a single message. The details of the implementation can be found in the cluster 2003 paper from the ARMCI publications website. Description ============ The implicit aggregation of data transfers is implemented using the generalized I/O vector operations available in ARMCI. This interface enables the representation of a data transfer as a combination of multiple sets of equally sized contiguous data segments. When the first call involving aggregate nonblocking handle is executed, the library starts building a vector descriptor stored in one of the preallocated internal buffers. The actual data transfer takes place when the user calls wait operation or the buffer storing the vector descriptor fills up. Function Definitions ==================== ARMCI_SET_AGGREGATE_HANDLE (armci_hdl_t* handle) ------------------------------------------------ handle - Pointer to a desciptor associated with a particular non-blocking transfer. PURPOSE: Mark a handle as aggregate. This will allow ARMCI to combine nonblocking operations that use that particular handle and process them as a single operation. In the initial implementation only contiguous puts or gets could use aggregate handle. Specifying the same handle for a mix of put anmd get calls is not allowed i.e., only multiple put or only multiple get calls can use the same handle. ARMCI_UNSET_AGGREGATE_HANDLE (armci_hdl_t* handle) -------------------------------------------------- handle - Pointer to a desciptor associated with a particular non-blocking transfer. PURPOSE: Clears a handle that has been marked as aggregate. Sample Programs =============== 1. Sparse matrix-vector multiplication Sparse matrix-vector multiplication is one of the common computational kernels, for example in solving linear systems using conjugate gradient method. It is described as Ax = b, where A is an nxn nonsingular sparse matrix, b is an n-dimensional vector, and x is an n-dimensional vector of unknowns. In this benchmark, one of the sparse matrices (Figure 8a) from the Harwell-Boeing collection is used [9] to test the matrix-vector multiplication. The sparse matrix size is 41092 and has 1683902 (~.1%) nonzero elements. The experiments were conducted on the Linux cluster (dual node, 1GHz Itanium-2, Myrinet-2000 interconnect) at PNNL. Sparse matrix-vector multiplication was done with aggregation enabled and disabled. The sparse matrix and the vector are distributed among processors. Instead of gathering the entire vector, each process caches the vector elements corresponding to the non-zero element columns of its locally owned part of the matrix. When aggregation is enabled, all the get calls corresponding to a single processor are aggregated into a single request, thus reducing the overall latency and improving the data transfer rate. This sample program is in the sparse_matvecmul directory. There is an input required for this program. The method to obtain the input is given in the sparse_matvecmul directory in a README file ga-5.9.2/armci/examples/features/aggregation/simple/000077500000000000000000000000001500715745200224335ustar00rootroot00000000000000ga-5.9.2/armci/examples/features/aggregation/simple/simple.c000066400000000000000000000212301500715745200240660ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: simple.c,v 1.1.2.1 2007-06-20 17:41:40 vinod Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif #if HAVE_ASSERT_H # include #endif #include "armci.h" #include "message.h" #define DIM1 5 #define DIM2 3 #ifdef __sun /* Solaris has shared memory shortages in the default system configuration */ # define DIM3 6 # define DIM4 5 # define DIM5 4 #elif defined(__alpha__) # define DIM3 8 # define DIM4 5 # define DIM5 6 #else # define DIM3 8 # define DIM4 9 # define DIM5 7 #endif #define DIM6 3 #define DIM7 2 #define OFF 1 #define EDIM1 (DIM1+OFF) #define EDIM2 (DIM2+OFF) #define EDIM3 (DIM3+OFF) #define EDIM4 (DIM4+OFF) #define EDIM5 (DIM5+OFF) #define EDIM6 (DIM6+OFF) #define EDIM7 (DIM7+OFF) #define DIMS 4 #define MAXDIMS 7 #define MAX_DIM_VAL 50 #define LOOP 200 #define BASE 100. #define MAXPROC 128 #define TIMES 100 # define ELEMS 200 /***************************** macros ************************/ #define COPY(src, dst, bytes) memcpy((dst),(src),(bytes)) #define ARMCI_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define ARMCI_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define ARMCI_ABS(a) (((a) <0) ? -(a) : (a)) /***************************** global data *******************/ int me, nproc; void* work[MAXPROC]; /* work array for propagating addresses */ void create_array(void *a[], int elem_size, int ndim, int dims[]) { int bytes=elem_size, i, rc; assert(ndim<=MAXDIMS); for(i=0;i 0.1) { ARMCI_Error("aggregate put failed...1", 0); } } armci_msg_barrier(); if(!dryrun) { if(me==0) { printf("\n aggregate put ..O.K.\n"); fflush(stdout); } } if(me==0) { for(i=1; i 0.1) { ARMCI_Error("aggregate get failed...1", 0); } } } } armci_msg_barrier(); if(!dryrun) { if(me==0) { printf(" aggregate get ..O.K.\n"); fflush(stdout); } } ARMCI_AllFence(); armci_msg_barrier(); if(!dryrun)if(me==0){printf("O.K.\n"); fflush(stdout);} destroy_array((void **)ddst_put); destroy_array((void **)ddst_get); destroy_array((void **)dsrc); } int main(int argc, char* argv[]) { armci_msg_init(&argc, &argv); nproc = armci_msg_nproc(); me = armci_msg_me(); /* printf("nproc = %d, me = %d\n", nproc, me);*/ if(nproc>MAXPROC && me==0) ARMCI_Error("Test works for up to %d processors\n",MAXPROC); if(me==0){ printf("ARMCI test program (%d processes)\n",nproc); fflush(stdout); sleep(1); } ARMCI_Init(); if(me==0){ printf("\nAggregate put/get requests\n\n"); fflush(stdout); } test_aggregate(1); /* cold start */ test_aggregate(0); /* warm start */ ARMCI_AllFence(); armci_msg_barrier(); if(me==0){printf("\nSuccess!!\n"); fflush(stdout);} sleep(2); armci_msg_barrier(); ARMCI_Finalize(); armci_msg_finalize(); return(0); } ga-5.9.2/armci/examples/features/aggregation/sparse_matvecmul/000077500000000000000000000000001500715745200245145ustar00rootroot00000000000000ga-5.9.2/armci/examples/features/aggregation/sparse_matvecmul/sparse_matvecmul.c000066400000000000000000000302571500715745200302410ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id$ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif #if HAVE_ASSERT_H # include #endif #include "armci.h" #include "message.h" #define DIM1 5 #define DIM2 3 #ifdef __sun /* Solaris has shared memory shortages in the default system configuration */ # define DIM3 6 # define DIM4 5 # define DIM5 4 #elif defined(__alpha__) # define DIM3 8 # define DIM4 5 # define DIM5 6 #else # define DIM3 8 # define DIM4 9 # define DIM5 7 #endif #define DIM6 3 #define DIM7 2 #define OFF 1 #define EDIM1 (DIM1+OFF) #define EDIM2 (DIM2+OFF) #define EDIM3 (DIM3+OFF) #define EDIM4 (DIM4+OFF) #define EDIM5 (DIM5+OFF) #define EDIM6 (DIM6+OFF) #define EDIM7 (DIM7+OFF) #define DIMS 4 #define MAXDIMS 7 #define MAX_DIM_VAL 50 #define LOOP 200 #define BASE 100. #define MAXPROC 128 #define TIMES 100 # define ELEMS 200 /***************************** macros ************************/ #define COPY(src, dst, bytes) memcpy((dst),(src),(bytes)) #define ARMCI_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define ARMCI_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define ARMCI_ABS(a) (((a) <0) ? -(a) : (a)) #define ROW 65536 #define COL ROW /* square matrices only for the time being */ /***************************** global data *******************/ int me, nproc; short int fortran_indexing=0; static int proc_row_list[MAXPROC];/*no of rows owned by each process - accumulated*/ static int proc_nz_list[MAXPROC]; /*no of non-zeros owned by each process */ void create_array(void *a[], int elem_size, int ndim, int dims[]) { int bytes=elem_size, i, rc; assert(ndim<=MAXDIMS); for(i=0;i= local_nz_acc) { proc_row_list[proc_id++] = i+1; local_nz_acc = local_nz*(proc_id+1); if(proc_id == nproc-1) local_nz_acc = non_zero; if(me==0 && proc_id ROW) ARMCI_Error("order is greater than defined variable ROW", ROW); } len = sizeof(int); armci_msg_brdcst(n, len, 0); /* Broad cast number of non_zeros */ if(me==0) fscanf(fp, "%d", non_zero); armci_msg_brdcst(non_zero, len, 0); /* Broadcast row indices */ len = (*n+1)*sizeof(int); row_ind_tmp = (int *)malloc(len); if(me==0)for(i=0; i<*n+1; i++) { fscanf(fp, "%d", &row_ind_tmp[i]); if(fortran_indexing) --row_ind_tmp[i]; } armci_msg_brdcst(row_ind_tmp, len, 0); load_balance(*n, *non_zero, row_ind_tmp); /* find how much temporary storage is needed at the maximum */ if(me==0) { for(max=-1,j=0;j j) return (1); if (i < j) return (-1); return (0); } static int count = -1; static armci_hdl_t gHandle[MAXPROC]; static int prev_proc = -1; static void get_data(int n, int start, int end, double *vec_local, double **vec) { int i, j, rc, bytes, offset; int proc_start, proc_end, idx_start, idx_end; proc_start = proc_end = -1; for(i=0; istart) proc_start = i; if(proc_end<0 && proc_row_list[i]>end) proc_end = i; } if(proc_start<0 || proc_end<0) ARMCI_Error("Invalid Process Ids", -1); for(i=proc_start; i<=proc_end; i++) { if(i==proc_start) idx_start = start; else { if(i==0) idx_start=0; else idx_start = proc_row_list[i-1];} if(i==proc_end) idx_end = end; else idx_end = proc_row_list[i]-1; if(i!=prev_proc) { ++count; prev_proc = i; ARMCI_INIT_HANDLE(&gHandle[count]); ARMCI_SET_AGGREGATE_HANDLE(&gHandle[count]); } if(i==0) offset=0; else offset = proc_row_list[i-1]; if(i==me) { /* local */ for(j=idx_start; j<=idx_end; j++) vec_local[j] = vec[me][j-offset]; } else { /* remote */ bytes = (idx_end-idx_start+1)*sizeof(double); vec_local[idx_start] = -1; #if 0 if((rc=ARMCI_Get(&vec[i][idx_start-offset], &vec_local[idx_start], bytes, i))) #else if((rc=ARMCI_NbGet(&vec[i][idx_start-offset], &vec_local[idx_start], bytes, i, &gHandle[count]))) #endif ARMCI_Error("armci_nbget failed\n",rc); } } } static void sparse_multiply(int n, int non_zero, int *row_ind, int **col_ind, double **values, double **vec, double **svec) { int i, j, k, num_elements, offset, *tmp_indices; double start_time, comm_time, comp_time, v, vec_local[COL]; int start, end, prev, nrows, idx; #if 0 /* ---- Sequential Case ---- */ for(i=0; iprev+1) { end = prev; get_data(n, start, end, vec_local, vec); start = prev = tmp_indices[i]; } else prev = tmp_indices[i]; } get_data(n, start, prev, vec_local, vec); #if 1 if(count>=0) for(i=0; i<=count; i++) ARMCI_Wait(&gHandle[i]); #endif comm_time = armci_timer() - start_time; start_time = armci_timer(); /* Perform Matrix-Vector multiply and store the result in solution vector - "svec[]" */ if(me==0) { nrows = proc_row_list[me]; offset = row_ind[0]; } else { nrows = proc_row_list[me]-proc_row_list[me-1]; offset = row_ind[proc_row_list[me-1]]; } /* printf("%d: My total Work = %d\n", me, nrows); */ for(i=0; iMAXPROC && me==0) ARMCI_Error("Test works for up to %d processors\n",MAXPROC); if(me==0){ printf("ARMCI test program (%d processes)\n",nproc); fflush(stdout); sleep(1); } ARMCI_Init(); if(me==0){ printf("\n Performing Sparse Matrix-Vector Multiplication ...\n\n"); fflush(stdout); } test_sparse(); ARMCI_AllFence(); armci_msg_barrier(); if(me==0){printf("\nSuccess!!\n"); fflush(stdout);} sleep(2); armci_msg_barrier(); ARMCI_Finalize(); armci_msg_finalize(); return(0); } ga-5.9.2/armci/examples/features/concurrency/000077500000000000000000000000001500715745200212055ustar00rootroot00000000000000ga-5.9.2/armci/examples/features/concurrency/README000066400000000000000000000022271500715745200220700ustar00rootroot00000000000000High-performance networks have developed significantly over the past several years. Many of these networks are capable of remote direct memory access (RDMA) communication that involves movement of data between processor memories without memory copies or remote host processor involvement. Current high-speed networks deploy powerful communication processor(s) in the network interface card (NIC) to handle processing of multiple incoming and outgoing messages without interrupting the host processor. For example, the Quadrics Elan4 network interconnect can do two overlapping DMAs and allows multiple outstanding read/write transactions from and to the network interconnect. Switches used in the modern interconnects also have developed significantly in the last several years, facilitating multiple communication paths between network endpoints and hence roviding increased levels of concurrency and redundancy communication between network endpoints. As a result, the modern networks are very capable of handling simultaneous and concurrent data movements. ARMCI allows for its applications to utilize concurrency by its low-overhead implementation of one-sided calls ga-5.9.2/armci/examples/features/concurrency/multidma/000077500000000000000000000000001500715745200230215ustar00rootroot00000000000000ga-5.9.2/armci/examples/features/concurrency/multidma/multidma.c000066400000000000000000000302411500715745200250010ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STDARG_H # include #endif #ifdef WINDOWS_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_TIME_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_TIME_H # include #endif #if HAVE_ASSERT_H # include #endif #include #include "armci.h" #include "message.h" #define NDEBUG /*#define LOG2FILE*/ typedef int t_elem; /* type of an array element */ #define SIZE_ELEM sizeof(t_elem) /* # of simultaneous message for phase A*/ #define SML_MSGS (size < 9 ? size - 1 : 8) #define MIN_MSG_SIZE 8 #define MAX_MSG_SIZE (1024 * 1024) #define MSG_COUNT 20 static void ARMCI_ASSERT(int error_code) { if (error_code) { fprintf(stderr, "ARMCI error %d\n", error_code); pause(); ARMCI_Cleanup(); MPI_Abort(MPI_COMM_WORLD, error_code); } } #define FIX_TIME(t) if (t < 0.0) t = 0.0; int size, rank, second; #define ITERS 18 #define ITER_STEPS 20 double iterations_times[ITERS]; int iterations[ITERS]; int *p_srcs, *p_dsts; enum {CONT_PUT, CONT_GET, STRIDED_PUT, STRIDED_GET, STRIDED_ACC, VECTOR_PUT, VECTOR_GET, VECTOR_ACC}; #define OPS_COUNT (STRIDED_ACC + 1) #define NON_CONT(op) (op > CONT_GET) enum {NOWORK, TOTAL, OVERLAP}; #define STATS_COUNT (OVERLAP + 1) /* prints formatted numbered message with processor's rank */ int log_debug(const char *fmt, ...) { int r = 0; #ifndef NDEBUG static int log_counter = 1; va_list ap; va_start(ap, fmt); printf("%03d@%1d: ", log_counter++, rank); r = vprintf(fmt, ap); va_end(ap); #endif return r; } FILE *log_file = NULL; void start_logging(const char *fname) { #ifdef LOG2FILE char exe_name[255]; char log_path[255]; int i; char k; strcpy(exe_name, fname); if (exe_name[strlen(exe_name) - 2] == '.') /* remove .x */ exe_name[strlen(exe_name) - 2] = 0; if (exe_name[0] == '/') { /* full path given */ for (i = ((int)strlen(exe_name)) - 1, k = -1; i >= 0; i--) if (exe_name[i] == '/') { if (k == -1) k = i + 1; else { exe_name[i] = 0; break; } } log_debug("exe: path=%s, name=%s\n", exe_name, exe_name + k); sprintf(log_path, "%s/data/%s.dat", exe_name, exe_name + k); } else { /* only executable name */ sprintf(log_path, "../data/%s.dat", exe_name); } log_debug("log: %s\n", log_path); log_file = fopen(log_path, "w"); if (!log_file) { perror("cannot open log file"); abort(); } #else log_file = stderr; #endif } void finish_logging() { fclose(log_file); } /* prints formatted message to ../data/.dat */ int log_printf(const char *fmt, ...) { int r; va_list ap; va_start(ap, fmt); if (log_file) r = vfprintf(log_file, fmt, ap); else { fprintf(stderr, "warning: logging is not enabled for this process\n"); r = vfprintf(stderr, fmt, ap); } va_end(ap); return r; } struct stats { int size; /* size, including trailing space for arrays */ double put; double get; double *nb_put_a; double *nb_get_a; double *nb_put_b; double *nb_get_b; }; void benchmark(int msg_size, struct stats *st) { void **out_ptrs, **in_ptrs; double time_start, time_after_start, time_after_call, time_after_wait; double time2put, time2get; int i, j; armci_hdl_t handle; out_ptrs = malloc(sizeof(void*)*size); in_ptrs = malloc(sizeof(void*)*size); log_debug("testing message size %d bytes\n", msg_size); log_debug("barrier A\n"); armci_msg_barrier(); /* allocate buffers */ ARMCI_ASSERT(ARMCI_Malloc(out_ptrs, msg_size)); ARMCI_ASSERT(ARMCI_Malloc(in_ptrs, msg_size)); for (i = 0; i < msg_size; i++) { ((char *)out_ptrs[rank])[i] = (char)(rand() >> 24); ((char *)in_ptrs[rank])[i] = (char)(rand() >> 24); } /* warmup call */ log_debug("barrier B\n"); ARMCI_Barrier(); ARMCI_ASSERT(ARMCI_Put(in_ptrs[rank], out_ptrs[second], msg_size, second)); log_debug("barrier C: warmed up\n"); ARMCI_Barrier(); /* phase A */ if (!rank) { /* measure blocking Put 0 -> 1 */ time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_Put(out_ptrs[0], in_ptrs[1], msg_size, 1)); time_after_call = armci_timer(); time2put = time_after_call - time_after_start + time_start - time_after_start; log_debug("PutA(%d bytes) 0 -> 1: %.8f\n", msg_size, time2put); st->put = time2put; } log_debug("barrier D\n"); ARMCI_Barrier(); if (!rank) { /* measure blocking Get 0 <- 1 */ time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_Get(out_ptrs[1], in_ptrs[0], msg_size, 1)); time_after_call = armci_timer(); time2get = time_after_call - time_after_start + time_start - time_after_start; log_debug("GetA(%d bytes) 0 <- 1: %.8f\n", msg_size, time2get); st->get = time2get; } for (i = 1; i <= SML_MSGS; i++) { log_debug("barrier E\n"); ARMCI_Barrier(); if (!rank) { /* put i messages to procs 0..i-1 */ time_start = armci_timer(); time_after_start = armci_timer(); for (j = 1; j <= i; j++) ARMCI_ASSERT(ARMCI_NbPut(out_ptrs[0], in_ptrs[j], msg_size, j, NULL)); ARMCI_WaitAll(); time_after_wait = armci_timer(); time2put = time_after_wait - time_after_start + time_start - time_after_start; log_debug("NbPutA(%d bytes) 0 -> 1..%d: %.8f\n", msg_size, i, time2put); st->nb_put_a[i - 1] = time2put; } } for (i = 1; i <= SML_MSGS; i++) { log_debug("barrier F\n"); ARMCI_Barrier(); if (!rank) { /* get i messages from procs 0..i-1 */ time_start = armci_timer(); time_after_start = armci_timer(); for (j = 1; j <= i; j++) ARMCI_ASSERT(ARMCI_NbGet(out_ptrs[j], in_ptrs[0], msg_size, j, NULL)); ARMCI_WaitAll(); time_after_wait = armci_timer(); time2get = time_after_wait - time_after_start + time_start - time_after_start; log_debug("NbGetA(%d bytes) 0 <- 1..%d: %.8f\n", msg_size, i, time2get); st->nb_get_a[i - 1] = time2get; } } /* phase B */ log_debug("barrier G\n"); ARMCI_Barrier(); ARMCI_INIT_HANDLE(&handle); time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbPut(out_ptrs[rank], in_ptrs[second], msg_size, second, &handle)); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); time2put = time_after_wait - time_after_start + time_start - time_after_start; log_debug("NbPutB(%d bytes) %d -> %d: %.8f\n", msg_size, rank, second, time2put); st->nb_put_b[rank] = time2put; log_debug("barrier H\n"); ARMCI_Barrier(); ARMCI_INIT_HANDLE(&handle); time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbGet(out_ptrs[second], in_ptrs[rank], msg_size, second, &handle)); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); time2get = time_after_wait - time_after_start + time_start - time_after_start; log_debug("NbGetB(%d bytes) %d <- %d: %.8f\n", msg_size, rank, second, time2get); st->nb_get_b[rank] = time2get; log_debug("barrier I\n"); ARMCI_Barrier(); log_debug("finished benchmarking\n"); /* clean up */ ARMCI_Free(out_ptrs[rank]); ARMCI_Free(in_ptrs[rank]); free(out_ptrs); free(in_ptrs); } int main (int argc, char *argv[]) { int i, j, l; char buf[255]; int dist, pos, time_seed; struct stats *st; #ifdef USE_EXP2 int msg_sizes[MSG_COUNT]; #else int msg_sizes[] = {8, 16, 28, 52, 96, 180, 332, 616, 1144, 2124, 3952, 7344, 13652, 25384, 47196, 87748, 163144, 303332, 563972, 1048576}; #endif armci_msg_init(&argc, &argv); rank = armci_msg_me(); size = armci_msg_nproc(); assert((size & 1) ^ 1); /* works with even number of processors only */ log_debug("Message passing initialized\n"); if (!rank) start_logging(argv[0]); ARMCI_ASSERT(ARMCI_Init()); log_debug("ARMCI initialized\n"); /* inialize PRNG, use seed generated on processor 0 for uniform sequence */ time_seed = time(NULL); MPI_Bcast(&time_seed, 1, MPI_INT, 0, MPI_COMM_WORLD); srand(time_seed); rand(); log_debug("seed: %d\n", time_seed); /* generate random pairs of processors */ #define HALFSIZE (size / 2) p_srcs = malloc(sizeof(int) * size); assert(p_srcs); for (i = 0; i < size; i++) p_srcs[i] = -1; p_dsts = p_srcs + HALFSIZE; for (i = 0, j = size - 1, pos = 0; i < size; i++, j--) { dist = round((double)rand() * j / RAND_MAX + 1); /* random 1..j */ for (l = 0; l < dist; ) { pos = (pos + 1 == size) ? 0 : pos + 1; if ((p_srcs[pos] == -1) && (pos != i)) l++; } p_srcs[pos] = i; } for (i = 0, j = 0; i < HALFSIZE; i++) j += sprintf(buf + j, " %d->%d", p_srcs[i], p_dsts[i]); log_debug("random pairs:%s\n", buf); /* determines a pair/second for current processor */ for (i = 0; i < HALFSIZE; i++) { if (p_srcs[i] == rank) second = p_dsts[i]; if (p_dsts[i] == rank) second = p_srcs[i]; } log_debug("second: %d\n", second); /* allocate memory tatistics */ l = sizeof(struct stats) + 2 * sizeof(double) * (size + SML_MSGS); st = malloc(l); assert(st); st->size = l; st->nb_put_a = (double *)(((char *)st) + sizeof(struct stats)); st->nb_get_a = st->nb_put_a + SML_MSGS; st->nb_put_b = st->nb_get_a + SML_MSGS; st->nb_get_b = st->nb_put_b + size; for (i = 0; i < MSG_COUNT; i++) { benchmark(msg_sizes[i], st); if (rank != 0) { MPI_Gather(st->nb_put_b + rank, 1, MPI_DOUBLE, st->nb_put_b, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gather(st->nb_get_b + rank, 1, MPI_DOUBLE, st->nb_get_b, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); } if (!rank) { double min_put, max_put, mean_put; double min_get, max_get, mean_get; log_printf("msg_size = %d:\n", msg_sizes[i]); log_printf("Put = %.8f Get = %.8f\n", st->put, st->get); for (j = 0; j < SML_MSGS; j++) log_printf("NbPutA(0 -> 1..%d) = %.8f\n", j + 1, st->nb_put_a[j]); for (j = 0; j < SML_MSGS; j++) log_printf("NbGetA(0 -> 1..%d) = %.8f\n", j + 1, st->nb_get_a[j]); /* determine min, max and mean for phase B */ min_put = max_put = mean_put = st->nb_put_b[0]; min_get = max_get = mean_get = st->nb_get_b[0]; for (j = 1; j < size; j++) { if (min_put > st->nb_put_b[j]) min_put = st->nb_put_b[j]; if (max_put < st->nb_put_b[j]) max_put = st->nb_put_b[j]; mean_put += st->nb_put_b[j]; if (min_get > st->nb_get_b[j]) min_get = st->nb_get_b[j]; if (max_get < st->nb_get_b[j]) max_get = st->nb_get_b[j]; mean_get += st->nb_get_b[j]; } mean_put /= size; mean_get /= size; log_printf("NbPutB min = %.8f max = %.8f mean = %.8f\n", min_put, max_put, mean_put); log_printf("NbGetB min = %.8f max = %.8f mean = %.8f\n", min_get, max_get, mean_get); log_printf("\n"); } } /* clean up */ ARMCI_Finalize(); armci_msg_finalize(); free(st); free(p_srcs); if (!rank) finish_logging(); return 0; } ga-5.9.2/armci/examples/features/concurrency/simple/000077500000000000000000000000001500715745200224765ustar00rootroot00000000000000ga-5.9.2/armci/examples/features/concurrency/simple/comdegree.c000066400000000000000000000225311500715745200245770ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** * This test checks the networks ability to overlap data transfers. * It does it both for an optimitic case (with no other communication) and * a more realistic case. * --Vinod Tipparaju * --Pacific Northwest National Laboratory * --vinod@pnl.gov */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_ASSERT_H # include #endif #define DEBUG__ #include "armci.h" #include "message.h" /***************************** macros ************************/ #define COPY(src, dst, bytes) memcpy((dst),(src),(bytes)) #define ARMCI_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define ARMCI_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define ARMCI_ABS(a) (((a) <0) ? -(a) : (a)) /***************************** global data *******************/ int me, nproc; void create_array(void *a[], int size) { armci_size_t bytes=size; int rc; rc = ARMCI_Malloc(a, bytes); assert(rc==0); #ifdef DEBUG_ printf("%d after malloc ndim=%d b=%d ptr=%p\n",me,ndim,(int) bytes,a[me]); fflush(stdout); #endif assert(a[me]); bzero(a[me],bytes); } void destroy_array(void *ptr[]) { int check; armci_msg_barrier(); check = !ARMCI_Free(ptr[me]); assert(check); } #define LCC 13 #define MAXPROC 128 void test_get_multidma() { int i,j; void *b[MAXPROC], *a[MAXPROC]; int left = (me+nproc-1) % nproc; int right = (me+1) % nproc; /*int sendersright=0,sendersleft=0;*/ int loopcnt=10, itercount=5; double tt/*, t0[LCC]*/,t1[LCC],t2[LCC],t3[LCC]; armci_hdl_t hdl1,hdl2; for(i=0;i #include #include #include using std::cout; using std::endl; #define DEBUG 0 #ifdef WIN32 # include # define sleep(x) Sleep(1000*(x)) #else # include #endif #include "armci.h" #include "message.h" #define ARMCI_ENABLE_GPC_CALLS #include "gpc.h" #include "Hash_common.h" #include "DistHashmap.h" #include "Util.h" #define BUF_LIMIT 8192 // in bytes. Make sure this value can fit in an // integer variable extern int gpc_disthashmap_handler(int to, int from, void *hdr, int hlen, void *data, int dlen, void *rhdr, int rhlen, int *rhsize, void *rdata, int rdlen, int *rdsize, int rtype); extern void gpc_disthashmap_exec(int hash_op, char *buf, size_t bufsize, int proc, int gpc_handle); extern void gpc_disthashmap_exec_nb(int hash_op, char *buf, size_t bufsize, int proc, int gpc_handle, gpc_hdl_t *nbh); extern void gpc_disthashmap_exec_wait(gpc_hdl_t *nbh); extern int armci_master; short int DistHashmap::sm_initialized=0; DistHashmap::DistHashmap() : mGPCHandle(0), mGlobalIdMap(NULL), m_globalHashMapSize(0) { MP_MYID(&m_me); int nproc; MP_PROCS(&nproc); mIndex = new int[nproc]; for(int i=0; idestroy(); delete[] mIndex; int nproc; MP_PROCS(&nproc); for(int i=0; i BUF_LIMIT) ARMCI_Error("String length is greater than internal buffer. Increase BUF_LIMIT", 0); // if the buffer is full, trigger an insert into the GPC hashmap if(mIndex[dst_proc] + str.length() + padding > BUF_LIMIT) this->commit(dst_proc); // initalize the "number of strings" field in the buffer int *numstrings = (int*)mBuf[dst_proc]; if(mIndex[dst_proc] == 0) { *numstrings = 0; mIndex[dst_proc] += sizeof(int); } (*numstrings)++; // insert into the internal buffer int index = mIndex[dst_proc]; mIndex[dst_proc] += armci_hashmap_pack(&mBuf[dst_proc][index], str); } /** * Call commit() to complete the data insertion into the distributed hashmap * for consistency purposes. This is a collective call. */ void DistHashmap::commit() { int nproc; MP_PROCS(&nproc); for(int i=0; icommit(i); this->rehash(); } /** * Call commit() to complete the data insertion into the distributed hashmap * for consistency purposes (of a specified process). */ void DistHashmap::commit(int proc) { // complete the hashmap insertion if the buffer still has data if(mIndex[proc] > 0) { char *buf = &mBuf[proc][0]; int bufsize = mIndex[proc]; # if DEBUG printf("%d: Commit() to %d: numstrings=%d bufsize=%d\n", m_me, proc, *((int*)buf), bufsize);fflush(stdout); # endif memcpy(mTmpBuf, (const char*)buf, (size_t)bufsize); int tmpBufsize = bufsize; // insert into the distributed hashmap gpc_disthashmap_exec(HASHMAP_INSERT, buf, bufsize, proc, mGPCHandle); mIndex[proc] = 0; // verify the returned data if( *((int*)buf) != *((int*)mTmpBuf) ) ARMCI_Error("DistHashmap::commit() failed", 0); // i.e. save the global ids in the locally cached hashmap (mGlobalIdMap) int *globalTermIds = (int*) (buf + sizeof(int)); armci_hashmap_insert2(mGlobalIdMap, (const char*)mTmpBuf, tmpBufsize, globalTermIds, HASHMAP_INSERT); } } /** * This is a collective call to rehash the distributed hashmap */ void DistHashmap::rehash() { int nclus = armci_domain_count(ARMCI_DOMAIN_SMP); int clus_me = armci_domain_my_id(ARMCI_DOMAIN_SMP); // no need to rehash if #of clusters is <= 1 if(nclus <=1) { m_globalHashMapSize = mGlobalIdMap->size(); return; } // no GPC support for single node multiple processes if(nclus == 1) { int nproc; MP_PROCS(&nproc); if(nproc > 1) ARMCI_Error("DistHashmap::rehash(): no GPC support for single node multiple processes", 0); } int size=0; int *globalHashOffset = new int[nclus]; int *offsetMap = new int[nclus]; if(globalHashOffset == NULL || offsetMap == NULL) ARMCI_Error("DistHashmap::rehash() new alloc failed", 0); for(int i=0; ibegin(); iter!=mGlobalIdMap->end(); iter++) { termStr = (*iter).first; int nproc; MP_PROCS(&nproc); int dst_proc = armci_djb2_hash((unsigned char*)termStr.c_str(), nproc); int dst_clus = armci_domain_id(ARMCI_DOMAIN_SMP, dst_proc); (*iter).second += offsetMap[dst_clus]; } delete[] offsetMap; delete[] globalHashOffset; } /** * to check if a distributed hashmap exists * This is a local call */ bool DistHashmap::isCreated() { if (sm_initialized >0) return true; return false; } /** * This is a collective call */ void DistHashmap::print() { MP_BARRIER(); /* SMP master process destroys the hashmap */ if(m_me == armci_master) { gpc_disthashmap_exec(HASHMAP_PRINT, NULL, 0, m_me, mGPCHandle); } MP_BARRIER(); } /** * This is a local call */ void DistHashmap::print2() { printf("%d: Locally cached Hashmap[%d:%ld]\n", m_me, 1, mGlobalIdMap->size()); VocabIntMap::const_iterator iter; for(iter=mGlobalIdMap->begin(); iter != mGlobalIdMap->end(); iter++) { cout << iter->second << "\t: " << iter->first << endl; } cout << endl; } const VocabIntMap * DistHashmap:: getLocalMapPtr() { return mGlobalIdMap; } int DistHashmap:: getGlobalHashMapSize() { return m_globalHashMapSize; } /* TODO: 1. put the gpc exec functions in GPCHashmapHandler.cc as private methods in this class 2. for better performance, use ARMCI_Malloc instead of new for data transfer GPC buffers */ ga-5.9.2/armci/examples/features/gpc/hashtable/DistHashmap.h000066400000000000000000000042151500715745200237370ustar00rootroot00000000000000/* $Id: */ #ifndef DISTHASHMAP_H_ #define DISTHASHMAP_H_ #include using std::string; #define ARMCI_ENABLE_GPC_CALLS #include "gpc.h" #include "Hash_common.h" class DistHashmap { public: /** * Constructor */ DistHashmap(); /** * Default Destructor */ ~DistHashmap(); /** * creates a new distributed hashmap */ void create(); /** * destroys this distributed hashmap */ void destroy(); /** * str - string to be inserted into the distributed hashmap * size - size of strlen array * Insert() is just to ensure that there is a new entry to be inserted into * the distributed hash map, and this entry is marked in the local buffer * as "to be inserted". The reason is, it will be expensive to do the insert * for each and every new element. In order to avoid those latency costs, * insert aggregates the entries into a single buffer of size BUF_LIMIT and * only completes the insertion into the distributed hashh map, if and only * if this aggregate local buffer is full. In order to complete this * aggregation call (even though the buffer is not full), commit() can be * used to flush the local buffer into the distributed hash map. So it is a * good practice to call commit after all insert or whenever consistency is * required. */ void insert(string str); /** * Call commit() to complete the data insertion into the distributed * hashmap. */ void commit(); /** * Call commit() to complete the data insertion into the distributed * hashmap of a specified process. */ void commit(int proc); /** * prints this distributed hashmap */ void print(); void print2(); void rehash(); const VocabIntMap * getLocalMapPtr(); /** * returns the Global HashMap Size */ int getGlobalHashMapSize(); /** * returns true if a hashmap already exists */ static bool isCreated(); private: int mGPCHandle; int m_me; static short int sm_initialized; char **mBuf; char *mTmpBuf; /* temporary buffer */ int *mIndex; int m_globalHashMapSize; VocabIntMap *mGlobalIdMap; }; #endif /* DISTHASHMAP_H_ */ ga-5.9.2/armci/examples/features/gpc/hashtable/GPCHashmap.cc000066400000000000000000000045311500715745200236040ustar00rootroot00000000000000/* $Id: */ #include #include #include #include using std::string; using std::cout; using std::endl; #define DEBUG 0 #ifdef WIN32 # include # define sleep(x) Sleep(1000*(x)) #else # include #endif #include "armci.h" #define ARMCI_ENABLE_GPC_CALLS #include "gpc.h" #include "Hash_common.h" #include "GPCHashmap.h" #include "Util.h" short int GPCHashmap::sm_initialized=0; GPCHashmap::GPCHashmap() : mVocabMap(NULL) { } GPCHashmap::~GPCHashmap() { if(sm_initialized != 0) this->destroy(); } void GPCHashmap::create() { if(sm_initialized != 0) { ARMCI_Error("GPCHashmap::create(): Hashmap already exists. At a given time, only one distributed hashmap should exist. Multiple distributed hashmaps not yet supported", 0); } mVocabMap = new VocabIntMap(); sm_initialized=1; } void GPCHashmap::destroy() { if (mVocabMap != NULL) delete mVocabMap; sm_initialized=0; } /** * buf - character array * size - size of strlen array */ void GPCHashmap::insert(const char *buf, size_t bufsize) { armci_hashmap_insert(mVocabMap, buf, bufsize); #ifdef MEM_FENCE MEM_FENCE; #else ARMCI_Error("gpc_insert_handler: MEM_FENCE not defined", 0); #endif } /** * get the global term IDs */ void GPCHashmap::getGlobalIds(const char *buf, size_t bufsize, int *globalTermIds) { globalTermIds[0] = *((int*)buf); armci_hashmap_insert2(mVocabMap, buf, bufsize, &globalTermIds[1], HASHMAP_GET); #ifdef MEM_FENCE MEM_FENCE; #else ARMCI_Error("gpc_insert_handler: MEM_FENCE not defined", 0); #endif } /** * prints the hashmap in this server process */ void GPCHashmap::print() { int me; MP_MYID(&me); printf("%d: Hashmap[%d:%ld]\n", me, 1, mVocabMap->size()); VocabIntMap::const_iterator iter; for(iter=mVocabMap->begin(); iter != mVocabMap->end(); iter++) { cout << iter->second << "\t: " << iter->first << endl; } cout << endl; } // to check if a hashmap exists void GPCHashmap::rehash(int *size) { *size = (int) mVocabMap->size(); #ifdef MEM_FENCE MEM_FENCE; #else ARMCI_Error("gpc_insert_handler: MEM_FENCE not defined", 0); #endif } // to check if a hashmap exists bool GPCHashmap::isCreated() { if (sm_initialized >0) return true; return false; } ga-5.9.2/armci/examples/features/gpc/hashtable/GPCHashmap.h000066400000000000000000000021001500715745200234340ustar00rootroot00000000000000/* $Id: */ #ifndef GPCHASHMAP_H_ #define GPCHASHMAP_H_ #include using std::string; #include "Hash_common.h" class GPCHashmap { public: /** * Constructor */ GPCHashmap(); /** * Default Destructor */ ~GPCHashmap(); /** * creates a new hashmap (local) */ void create(); /** * destroys this hashmap */ void destroy(); /** * inserts elements into hashmap */ void insert(const char *buf, size_t size); /** * get the global term IDs */ void getGlobalIds(const char *buf, size_t bufsize, int *globalTermIds); /** * prints the local hashmap */ void print(); void rehash(int *size); /** * returns true if a hashmap already exists */ static bool isCreated(); private: VocabIntMap *mVocabMap; static short int sm_initialized; }; #endif /* GPCHASHMAP_H_ */ ga-5.9.2/armci/examples/features/gpc/hashtable/GPCHashmapHandler.cc000066400000000000000000000077031500715745200251060ustar00rootroot00000000000000/* $Id: */ #include #define DEBUG 0 #ifdef WIN32 # include # define sleep(x) Sleep(1000*(x)) #else # include #endif #include "armci.h" #define ARMCI_ENABLE_GPC_CALLS #include "gpc.h" #include "Hash_common.h" #include "GPCHashmap.h" static GPCHashmap *gGPCHashmap=NULL; #if DEBUG char* g_GPCops[6] = {"HASHMAP_CREATE", "HASHMAP_DESTROY", "HASHMAP_INSERT", "HASHMAP_PRINT", "HASHMAP_GET", "HASHMAP_REHASH"}; #endif int gpc_disthashmap_handler(int to, int from, void *hdr, int hlen, void *data, int dlen, void *rhdr, int rhlen, int *rhsize, void *rdata, int rdlen, int *rdsize, int rtype) { hash_hdr_t *lhdr; int rank; MP_MYID(&rank); lhdr = (hash_hdr_t*)ARMCI_Gpc_translate(hdr,to,from); const char *buf = (const char*)data; size_t bufsize = dlen; int hash_op = lhdr->hash_op; // for correctness ? *rhsize = sizeof(int); *rdsize = sizeof(int); #if DEBUG int me; MP_MYID(&me); printf("%d: In GPC handler: %s from=%d to=%d\n", me, g_GPCops[hash_op-HASHMAP_CREATE], from, to); fflush(stdout); #endif switch(hash_op) { case HASHMAP_CREATE: gGPCHashmap = new GPCHashmap(); gGPCHashmap->create(); break; case HASHMAP_DESTROY: gGPCHashmap->destroy(); if(gGPCHashmap != NULL) delete gGPCHashmap; break; case HASHMAP_INSERT: gGPCHashmap->insert(buf, bufsize); // piggy-back the globalIds gGPCHashmap->getGlobalIds(buf, bufsize, (int*)rdata); break; case HASHMAP_PRINT: gGPCHashmap->print(); break; case HASHMAP_REHASH: *rdsize = sizeof(int); gGPCHashmap->rehash((int*)rdata); break; default: ARMCI_Error("gpc_disthashmap_handler(): Invalid hashmap operation",0); } return GPC_DONE; } void gpc_disthashmap_exec_nb(int hash_op, char *buf, size_t bufsize, int proc, int gpc_handle, gpc_hdl_t *nbh) { int hlen; hash_hdr_t header; int rheader; #if DEBUG int me; MP_MYID(&me); printf("%d: Executing GPC: (%s) buf=%p bufsize=%ld proc=%d\n", me, g_GPCops[hash_op-HASHMAP_CREATE], buf, bufsize, proc); fflush(stdout); #endif header.hash_op = hash_op; header.buf = buf; header.bufsize = bufsize; hlen=sizeof(header); if(nbh!= NULL) ARMCI_Gpc_init_handle(nbh); #if 0 if(ARMCI_Gpc_exec(gpc_handle, proc, &header, hlen, buf, bufsize, &rheader, sizeof(int), &rdata, sizeof(int), nbh)) ARMCI_Error("gpc_disthashmap_exec_nb(): ARMCI_Gpc_exec failed", 0); #endif if(ARMCI_Gpc_exec(gpc_handle, proc, &header, hlen, buf, bufsize, &rheader, sizeof(int), buf, bufsize, nbh)) ARMCI_Error("gpc_disthashmap_exec_nb(): ARMCI_Gpc_exec failed", 0); } void gpc_disthashmap_exec_wait(gpc_hdl_t *nbh) { if(nbh!= NULL) ARMCI_Gpc_wait(nbh); } void gpc_disthashmap_exec(int hash_op, char *buf, size_t bufsize, int proc, int gpc_handle) { #if NON_BLOCKING // non blocking GPC is flaky gpc_hdl_t nbh; gpc_disthashmap_exec_nb(hash_op, buf, bufsize, proc, gpc_handle, &nbh); gpc_disthashmap_exec_wait(&nbh); #else gpc_disthashmap_exec_nb(hash_op, buf, bufsize, proc, gpc_handle, NULL); gpc_disthashmap_exec_wait(NULL); #endif /* int hlen; hash_hdr_t header; int rheader, rdata; header.hash_op = hash_op; header.buf = buf; header.bufsize = bufsize; hlen=sizeof(header); ARMCI_Gpc_init_handle(&nbh); if(ARMCI_Gpc_exec(gpc_handle, proc, &header, hlen, buf, bufsize, &rheader, sizeof(int), &rdata, sizeof(int), &nbh)) ARMCI_Error("gpc_disthashmap_exec(): ARMCI_Gpc_exec failed", 0); ARMCI_Gpc_wait(&nbh); */ } ga-5.9.2/armci/examples/features/gpc/hashtable/HashFunctions.cc000066400000000000000000000121651500715745200244470ustar00rootroot00000000000000/* $Id: */ #include #include #include /************************************************************ * Possible Hash Functions (using google search) * Hash Functions: As mentioned briefly in the previous section, there are multiple ways for constructing a hash function. Remember that hash function takes the data as input (often a string), and return s an integer in the range of possible indices into the hash table. Every hash function must do that, including the bad ones. So what makes for a good hash function? Characteristics of a Good Hash Function: There are four main characteristics of a good hash function: 1) The hash value is fully determined by the data being hashed. 2) The hash function uses all the input data. 3) The hash function "uniformly" distributes the data across the entire set of possible hash values. 4) The hash function generates very different hash values for similar strings. Let's examine why each of these is important: Rule 1: If something else besides the input data is used to determine the hash, then the hash value is not as dependent upon the input data, thus allowing for a worse distribution of the hash values. Rule 2: If the hash function doesn't use all the input data, then slight variations to the input data would cause an inappropriate number of similar hash values resulting in too many collisions. Rule 3: If the hash function does not uniformly distribute the data across the entire set of possible hash values, a large number of collisions will result, cutting down on the efficiency of the hash table. Rule 4: In real world applications, many data sets contain very similar data elements. We would like these data elements to still be distributable over a hash table. So let's take as an example the hash function used in the last section: */ int armci_hash(char *str, int table_size) { int sum; // Make sure a valid string passed in if (str==NULL) return -1; // Sum up all the characters in the string for( ; *str; str++) sum += *str; // Return the sum mod the table size return sum % table_size; } /* Which rules does it break and satisfy? Rule 1: Satisfies. The hash value is fully determined by the data being hashed. The hash value is just the sum of all the input characters. Rule 2: Satisfies. Every character is summed. Rule 3: Breaks. From looking at it, it isn't obvious that it doesn't uniformly distribute the strings, but if you were to analyze this function for a large input you would see certain statistical properties bad for a hash function. Rule 4: Breaks. Hash the string "bog". Now hash the string "gob". They're the same. Slight variations in the string should result in different hash values, but with this function they often don't. So this hash function isn't so good. It's a good introductory example but not so good in the long run. There are many possible ways to construct a better hash function (doing a web search will turn up hundreds) so we won't cover too many here except to present a few decent examples of hash functions: */ /* Peter Weinberger's */ int armci_hashpjw(char *s) { char *p; unsigned int h, g; h = 0; for(p=s; *p!='\0'; p++){ h = (h<<4) + *p; if (g = h&0xF0000000) { h ^= g>>24; h ^= g; } } return h % 211; } /* UNIX ELF hash * Published hash algorithm used in the UNIX ELF format for object files */ unsigned long armci_elf_hash(char *name) { unsigned long h = 0, g; while ( *name ) { h = ( h << 4 ) + *name++; if ( g = h & 0xF0000000 ) h ^= g >> 24; h &= ~g; } return h; } /* This algorithm was created for the sdbm (a reimplementation of ndbm) * database library and seems to work relatively well in scrambling bits */ unsigned long armci_sdbm_hash(unsigned char *str) { unsigned long hash = 0; int c; while (c = *str++) hash = c + (hash << 6) + (hash << 16) - hash; return hash; } /* djb2: * This algorithm was first reported by Dan Bernstein * many years ago in comp.lang.c */ unsigned long armci_djb2_hash(unsigned char *str, int total_procs) { unsigned long hash = 5381; int c; /** * Modified by Manoj: * while (c = *str++) hash = ((hash << 5) + hash) + c; */ while (c = *str++) hash = (((hash << 5) + hash) + c) % total_procs; return hash; } char armci_XORhash( char *key, int len) { char hash; int i; for (hash=0, i=0; i #include "Util.h" #include "armci.h" int armci_hashmap_pack(char *buf, string str) { int len = str.length(); // first copy the length of the string *((int*)buf) = len; // then copy the actual string int index = sizeof(int); char *dst = &buf[index]; strcpy(dst, str.c_str()); index += len; // offset "index" to integer-byte boundary int adjust = sizeof(int) - (index % sizeof(int)); index += adjust; return (int)index; } int armci_hashmap_unpack(const char *buf, string& str) { // first get the string length and the corresponding string int len = *((int*)buf); // then get the actual term string int index = sizeof(int); string termStr(&buf[index], len); str = termStr; index += len; // offset "index" to integer-byte boundary int adjust = sizeof(int) - (index % sizeof(int)); index += adjust; return (int)index; } void armci_hashmap_insert(VocabIntMap *vocabMap, const char *buf, size_t bufsize) { string termStr; size_t index=0; // get the number of strings to be inserted (i.e.first field in the // buffer) and increment the index accordingly. int numstrings = *((int*)buf); index += sizeof(int); #if DEBUG int me; MP_MYID(&me); printf("%d: armci_hashmap_insert(): numstrings=%d bufsize=%ld\n", me, numstrings, bufsize); fflush(stdout); #endif // unpack the buffer for(int i=0; i bufsize) ARMCI_Error("GPCHashmap::insert() failed. Buffer overflow.", 0); // add the term to the hashmap VocabIntMap::const_iterator iter = vocabMap->find(termStr); int termID = -1; if (iter != vocabMap->end()) { // term already in map termID = (*iter).second; } else { // new term. Add to vocab hashmap termID = vocabMap->size(); // starts with zero (*vocabMap)[termStr] = termID; } } } void armci_hashmap_insert2(VocabIntMap *vocabMap, const char *buf, size_t bufsize, int *globalTermIds, int op) { string termStr; size_t index=0; // get the number of strings to be inserted (i.e.first field in the // buffer) and increment the index accordingly. int numstrings = *((int*)buf); index += sizeof(int); // unpack the buffer for(int i=0; i bufsize) ARMCI_Error("armci_hashmap_insert2() failed. Buffer overflow.", 0); if(op==HASHMAP_INSERT) { // insert the term to the hashmap (*vocabMap)[termStr] = globalTermIds[i]; } else if(op==HASHMAP_GET){ // retrieve a term's global id from hashmap globalTermIds[i] = (*vocabMap)[termStr]; } else { ARMCI_Error("armci_hashmap_insert2() Invalid operation", 0); } } } ga-5.9.2/armci/examples/features/gpc/hashtable/Hash_common.h000066400000000000000000000016751500715745200237740ustar00rootroot00000000000000/* $Id: */ #ifndef HASH_COMMON_H #define HASH_COMMON_H #define HASHMAP_CREATE 101 #define HASHMAP_DESTROY 102 #define HASHMAP_INSERT 103 #define HASHMAP_PRINT 104 #define HASHMAP_GET 105 #define HASHMAP_REHASH 106 typedef struct hash_hdr { int hash_op; void*buf; size_t bufsize; }hash_hdr_t; #include using std::string; /* #include "UnicodeString.h" */ #define USE_MAP #ifdef USE_MAP # include typedef std::map VocabIntMap; #else /* USE_MAP */ # include "hash_map.h" using STL_HASHMAP_NAMESPACE::hash_map; using STL_HASHMAP_NAMESPACE::hash; struct hashStr { size_t operator()(const string& str) const { hash H; return H(str.c_str()); } }; typedef hash_map VocabIntMap; #endif /* USE_MAP */ extern unsigned long armci_djb2_hash(unsigned char *str, int total_procs); #endif /* HASH_COMMON_H */ ga-5.9.2/armci/examples/features/gpc/hashtable/README000066400000000000000000000000421500715745200222330ustar00rootroot00000000000000:Author: Manojkumar Krishnan PNNL ga-5.9.2/armci/examples/features/gpc/hashtable/Util.h000066400000000000000000000007741500715745200224550ustar00rootroot00000000000000#ifndef UTIL_H #define UTIL_H #include using std::string; #include "Hash_common.h" extern int armci_hashmap_pack(char *buf, string str); extern int armci_hashmap_unpack(const char *buf, string& str); extern void armci_hashmap_insert(VocabIntMap *vocabMap, const char *buf, size_t bufsize); extern void armci_hashmap_insert2(VocabIntMap *vocabMap, const char *buf, size_t bufsize, int *globalTermIds, int op); #endif /* UTIL_H */ ga-5.9.2/armci/examples/features/gpc/hashtable/hash_map.h000066400000000000000000000002421500715745200233060ustar00rootroot00000000000000/** @file * $Id: * header to include STL's hash_map */ #include #ifndef STL_HASHMAP_NAMESPACE # define STL_HASHMAP_NAMESPACE __gnu_cxx #endif ga-5.9.2/armci/examples/features/gpc/hashtable/sample.txt000066400000000000000000000002131500715745200233750ustar00rootroot00000000000000 The Global Arrays (GA) toolkit provides an efficient and portable "shared-memory" programming interface for distributed-memory computers. ga-5.9.2/armci/examples/features/gpc/hashtable/test_hashtable.cc000066400000000000000000000034111500715745200246570ustar00rootroot00000000000000/* $Id: */ #include #include #include #include using namespace std; #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif #include "mp3.h" #include "armci.h" #define ARMCI_ENABLE_GPC_CALLS #include "gpc.h" #include "Hash_common.h" #include "DistHashmap.h" int me, nproc; void test_distHashmap() { ifstream infile("sample.txt"); string str; // create a distributed hashmap if(me==0) { printf("Creating a distributed hashmap\n"); fflush(stdout);} DistHashmap *dist_hashmap = new DistHashmap(); dist_hashmap->create(); if(me==0) { printf("Distributed hashmap created. O.K.\n"); fflush(stdout);} // reads a word from the file and inserts it into the hashmap while(!infile.eof()) { infile >> str; dist_hashmap->insert(str); // if(me==0) { printf("%s\n", str.c_str()); fflush(stdout);} } dist_hashmap->commit(); dist_hashmap->print(); fflush(stdout); MP_BARRIER(); dist_hashmap->print2(); fflush(stdout); MP_BARRIER(); // delete the distributed hashmap dist_hashmap->destroy(); if(me==0) { printf("Distributed hashmap deleted. O.K.\n"); fflush(stdout);} infile.close(); } int main(int argc, char* argv[]) { MP_INIT(argc, argv); MP_PROCS(&nproc); MP_MYID(&me); if(me==0){ printf("ARMCI Distributed Hashmap test program (%d processes)\n",nproc); fflush(stdout); sleep(1); } ARMCI_Init(); if(me==0){ printf("\nDistributed Hashmap using ARMCI's GPC calls\n"); fflush(stdout); } MP_BARRIER(); test_distHashmap(); ARMCI_AllFence(); MP_BARRIER(); ARMCI_Finalize(); MP_FINALIZE(); return(0); } ga-5.9.2/armci/examples/features/non-blocking/000077500000000000000000000000001500715745200212335ustar00rootroot00000000000000ga-5.9.2/armci/examples/features/non-blocking/README000066400000000000000000000042121500715745200221120ustar00rootroot00000000000000Nonblocking operations initiate a communication call and then return control to the application. The user who wishes to exploit nonblocking communication as a technique for latency hiding by overlapping communication with computation implicitly assumes that progress in communication can be made in a purely computational phase of the program execution when no communication calls are made. All the non-blocking transfer functions are prototyped to work as transfers with both "explicit" and "implicit handle". It stores important information about the initiated data transfer. The descriptor is implemented as an abstract data type. This is motivated by a simpler implementation so that a data transfer descriptor can be stored and managed in the application rather in the ARMCI library space. If a NULL value is passed to the argument representing a handle (thus representing "implicit handle"), the function does an implicit handle non-blocking transfer. A request data structure embedded in the handle should not be copied in the application. Upon completion of the data transfer, handle can be reused. A handle can be used to represent multiple operations of the same type (i.e., all puts or all gets). Such handle is an aggregate handle. Underneath, ARMCI combines multiple requests and processes them as a single message (actually by calling ARMCI_PutV/GetV/AccV). An explict handle should be initialized using the following macro, before it is used in any non-blocking operation. It is initialized as follows: - ARMCI_INIT_HANDLE(armci_hdl_t* nb_handle) Nonblocking operations in ARMCI allow user ot initiate a one-sided call and then return control to the user program. The data transfer is completed locally by calling a wait operation. Waiting on a nonblocking put operation assures was injected into the network and the user buffer can be now reused. Both in case of blocking and nonblocking store operations, to access the modified data safely from other nodes programmer has to call an ARMCI_Fence call first. ARMCI_Fence completes data transfers on the remote side. Unlike the blocking operation, the nonblocking operations are NOT ordered. ga-5.9.2/armci/examples/features/non-blocking/overlap/000077500000000000000000000000001500715745200227035ustar00rootroot00000000000000ga-5.9.2/armci/examples/features/non-blocking/overlap/overlap.c000066400000000000000000000646621500715745200245350ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STDARG_H # include #endif #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_TIME_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_ASSERT_H # include #endif #include #include "armci.h" #include "message.h" extern double exp2(double); extern double round(double); extern double log2(double); #define NDEBUG /*#define LOG2FILE*/ typedef int t_elem; /* type of an array element */ #define SIZE_ELEM sizeof(t_elem) #define STRIDE_OFF (SIZE_ELEM * 4 - 1) #define MIN_MSG_SIZE 8 #define MAX_MSG_SIZE (1024 * 1024) #define MSG_COUNT 20 int armci_error_code; #define ARMCI_ASSERT(error_code) if ((armci_error_code = error_code)) { \ fprintf(stderr, "ARMCI error %d\n", armci_error_code);pause(); \ ARMCI_Cleanup(); MPI_Abort(MPI_COMM_WORLD, armci_error_code); } #define FIX_TIME(t) if (t < 0.0) t = 0.0; int size, rank, second; #define ITERS 18 #define ITER_STEPS 20 double iterations_times[ITERS]; int iterations[ITERS]; int *p_srcs, *p_dsts; enum {CONT_PUT, CONT_GET, STRIDED_PUT, STRIDED_GET, STRIDED_ACC, VECTOR_PUT, VECTOR_GET, VECTOR_ACC}; #define OPS_COUNT (STRIDED_ACC + 1) #define NON_CONT(op) (op > CONT_GET) enum {NOWORK, TOTAL, OVERLAP}; #define STATS_COUNT (OVERLAP + 1) /* prints formatted numbered message with processor's rank */ int log_debug(const char *fmt, ...) { int r = 0; #ifndef NDEBUG static int log_counter = 1; va_list ap; va_start(ap, fmt); printf("%03d@%1d: ", log_counter++, rank); r = vprintf(fmt, ap); va_end(ap); #endif return r; } FILE *log_file = NULL; void start_logging(const char *fname) { #ifdef LOG2FILE char exe_name[255]; char log_path[255]; int i; char k; strcpy(exe_name, fname); if (exe_name[strlen(exe_name) - 2] == '.') /* remove .x */ exe_name[strlen(exe_name) - 2] = 0; if (exe_name[0] == '/') { /* full path given */ for (i = ((int)strlen(exe_name)) - 1, k = -1; i >= 0; i--) if (exe_name[i] == '/') { if (k == -1) k = i + 1; else { exe_name[i] = 0; break; } } log_debug("exe: path=%s, name=%s\n", exe_name, exe_name + k); sprintf(log_path, "%s/data/%s.dat", exe_name, exe_name + k); } else { /* only executable name */ sprintf(log_path, "../data/%s.dat", exe_name); } log_debug("log: %s\n", log_path); log_file = fopen(log_path, "w"); if (!log_file) { perror("cannot open log file"); abort(); } #else log_file = stderr; #endif } void finish_logging() { fclose(log_file); } /* prints formatted message to ../data/.dat */ int log_printf(const char *fmt, ...) { va_list ap; int r; va_start(ap, fmt); if (log_file) r = vfprintf(log_file, fmt, ap); else { fprintf(stderr, "warning: logging is not enabled for this process\n"); r = vfprintf(stderr, fmt, ap); } va_end(ap); return r; } /* computes approximate time of n iterations for variable n */ void time_iterations() { double time_start, time_after_start, time_stop; int i, j, k, l; for (i = 0, j = 1; i < ITERS; i++, j *= 2) { time_start = armci_timer(); time_after_start = armci_timer(); for (l = 0, k = rand(); l < j; l++) k *= rand(); time_stop = armci_timer(); iterations_times[i] = time_stop - time_after_start + time_start - time_after_start; FIX_TIME(iterations_times[i]); iterations[i] = j; log_debug("it takes %.8f sec to iterate %d times\n", iterations_times[i], iterations[i]); } } /* computes useful overlap time for contiguous/vector/strided arrays * * op - operation * * msg_size - size of a message/ 1st dimension (bytes) * * size2 - not used for contiguous arrays * * - size of 2nd dimension for strided arrays (bytes) * * - # of vector segments for vectors * * returns pointer to static array of stats (STATS_COUNT doubles) * */ double * benchmark(int op, int msg_size, int size2) { static double stats[STATS_COUNT]; /* return statistics in static array */ void **array_ptrs; int stride_dist, block_sizes[2], scale = 2; int i=0, j=0, k=0, l=0, less=0, more=0; double time_start=0, time_after_start=0, time_after_call=0, time_after_work=0, time_after_wait=0; double time2call_nw=0, time2wait_nw = 1.0, time_total_nw=0; double time2call_fw, time2work_fw, time2wait_fw, time_total_fw; armci_hdl_t handle; array_ptrs = malloc(sizeof(void*)*size); log_debug("barrier O\n"); armci_msg_barrier(); /* initialize: obtain remote address and generate random array */ switch (op) { case CONT_PUT: case CONT_GET: ARMCI_ASSERT(ARMCI_Malloc(array_ptrs, msg_size)); for (i = 0; i < msg_size; i++) ((char *)array_ptrs[rank])[i] = (char)(rand() >> 24); break; /* 2D strided array of ints */ case STRIDED_PUT: case STRIDED_GET: case STRIDED_ACC: block_sizes[0] = msg_size; block_sizes[1] = size2; stride_dist = STRIDE_OFF + msg_size; log_debug("strided: dim1 = %d (%d bytes), dim2 = %d, stride = %d\n", msg_size / SIZE_ELEM, msg_size, size2, stride_dist); ARMCI_ASSERT(ARMCI_Malloc(array_ptrs, (size2 - 1) * stride_dist + msg_size)); for (i = 0; i < size2; i++) for (j = 0; j < (msg_size / ((int)SIZE_ELEM)); j++) { l = stride_dist * i + SIZE_ELEM * i; *(int *)((char *)array_ptrs[rank] + l) = rand(); } break; } /* warm up call */ log_debug("barrier A\n"); armci_msg_barrier(); if (second != -1) { log_debug("testing message size %d bytes\n", msg_size); switch (op) { case CONT_PUT: ARMCI_INIT_HANDLE(&handle); time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbPut(array_ptrs[rank], array_ptrs[second], msg_size, second, &handle)); time_after_call = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; case CONT_GET: ARMCI_INIT_HANDLE(&handle); time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbGet(array_ptrs[second], array_ptrs[rank], msg_size, second, &handle)); time_after_call = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; case STRIDED_PUT: ARMCI_INIT_HANDLE(&handle); time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbPutS(array_ptrs[rank], &stride_dist, array_ptrs[second], &stride_dist, block_sizes, 1, second, &handle)); time_after_call = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; case STRIDED_GET: ARMCI_INIT_HANDLE(&handle); time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbGetS(array_ptrs[second], &stride_dist, array_ptrs[rank], &stride_dist, block_sizes, 1, second, &handle)); time_after_call = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; case STRIDED_ACC: ARMCI_INIT_HANDLE(&handle); time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbAccS(ARMCI_ACC_INT, &scale, array_ptrs[rank], &stride_dist, array_ptrs[second], &stride_dist, block_sizes, 1, second, &handle)); time_after_call = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; } time2call_nw = time_after_call - time_after_start + time_start - time_after_start; time2wait_nw = time_after_wait - time_after_call + time_start - time_after_start; time_total_nw = time_after_wait - time_after_start + time_start - time_after_start; log_debug("time (warm up): %.8f call, %.8f wait, %.8f total\n", time2call_nw, time2wait_nw, time_total_nw); } log_debug("barrier B\n"); armci_msg_barrier(); if (second != -1) { /* no work */ ARMCI_INIT_HANDLE(&handle); switch (op) { case CONT_PUT: time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbPut(array_ptrs[rank], array_ptrs[second], msg_size, second, &handle)); time_after_call = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; case CONT_GET: time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbGet(array_ptrs[second], array_ptrs[rank], msg_size, second, &handle)); time_after_call = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; case STRIDED_PUT: time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbPutS(array_ptrs[rank], &stride_dist, array_ptrs[second], &stride_dist, block_sizes, 1, second, &handle)); time_after_call = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; case STRIDED_GET: time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbGetS(array_ptrs[second], &stride_dist, array_ptrs[rank], &stride_dist, block_sizes, 1, second, &handle)); time_after_call = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; case STRIDED_ACC: time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbAccS(ARMCI_ACC_INT, &scale, array_ptrs[rank], &stride_dist, array_ptrs[second], &stride_dist, block_sizes, 1, second, &handle)); time_after_call = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; } time2call_nw = time_after_call - time_after_start + time_start - time_after_start; FIX_TIME(time2call_nw); time2wait_nw = time_after_wait - time_after_call + time_start - time_after_start; FIX_TIME(time2wait_nw); time_total_nw = time_after_wait - time_after_start + time_start - time_after_start; FIX_TIME(time_total_nw); log_debug("time (no work): %.8f call, %.8f wait, %.8f total\n", time2call_nw, time2wait_nw, time_total_nw); } /* only perform tests if wait time is not 0 */ if (time2wait_nw > 0.0) { /* time2wait_nw is always 1.0 on seconds (receiving nodes) */ double overlaps[ITER_STEPS], totals[ITER_STEPS]; if (second != -1) { /* compute approximate range of iterations */ less = 0, more = iterations[ITERS - 1]; assert(time2wait_nw < iterations_times[ITERS - 1]); for (i = 0; i < ITERS; i++) if (time2wait_nw > iterations_times[i]) less = iterations[i]; else break; for (i = 0; i < ITERS; i++) if (time2wait_nw < iterations_times[ITERS - i - 1]) more = iterations[ITERS - i - 1]; else break; log_debug("wait time (%.8f) is between %d and %d iterations\n", time2wait_nw, less, more); } /* benchmark ITER_STEPS steps within computed range */ for (i = 0, j = less; i < ITER_STEPS; i++, j += (more - less) / (ITER_STEPS - 1)) { /* time noneblocking call with j interations of fake work */ log_debug("barrier C\n"); armci_msg_barrier(); if (second != -1) { ARMCI_INIT_HANDLE(&handle); switch (op) { case CONT_PUT: time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbPut(array_ptrs[rank], array_ptrs[second], msg_size, second, &handle)); time_after_call = armci_timer(); for (l = 0, k = rand(); l < j; l++) k *= rand(); time_after_work = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; case CONT_GET: time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbGet(array_ptrs[second], array_ptrs[rank], msg_size, second, &handle)); time_after_call = armci_timer(); for (l = 0, k = rand(); l < j; l++) k *= rand(); time_after_work = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; case STRIDED_PUT: time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbPutS(array_ptrs[rank], &stride_dist, array_ptrs[second], &stride_dist, block_sizes, 1, second, &handle)); time_after_call = armci_timer(); for (l = 0, k = rand(); l < j; l++) k *= rand(); time_after_work = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; case STRIDED_GET: time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbGetS(array_ptrs[second], &stride_dist, array_ptrs[rank], &stride_dist, block_sizes, 1, second, &handle)); time_after_call = armci_timer(); for (l = 0, k = rand(); l < j; l++) k *= rand(); time_after_work = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; case STRIDED_ACC: time_start = armci_timer(); time_after_start = armci_timer(); ARMCI_ASSERT(ARMCI_NbAccS(ARMCI_ACC_INT, &scale, array_ptrs[rank], &stride_dist, array_ptrs[second], &stride_dist, block_sizes, 1, second, &handle)); time_after_call = armci_timer(); for (l = 0, k = rand(); l < j; l++) k *= rand(); time_after_work = armci_timer(); ARMCI_ASSERT(ARMCI_Wait(&handle)); time_after_wait = armci_timer(); break; } time2call_fw = time_after_call - time_after_start + time_start - time_after_start; FIX_TIME(time2call_fw); time2work_fw = time_after_work - time_after_call + time_start - time_after_start; FIX_TIME(time2work_fw); time2wait_fw = time_after_wait - time_after_work + time_start - time_after_start; FIX_TIME(time2wait_fw); time_total_fw = time_after_wait - time_after_start + time_start - time_after_start; FIX_TIME(time_total_fw); log_debug("time (%d iters): %.8f call, %.8f work, " "%.8f wait %.8f total\n", j, time2call_fw, time2work_fw, time2wait_fw, time_total_fw); overlaps[i] = time2work_fw; totals[i] = time_total_fw; } } /* pick overlap with closest total (less or equal) */ if (second != -1) { double closest_total, closest_overlap; double smallest_total = totals[ITER_STEPS - 1], smallest_overlap = overlaps[ITER_STEPS - 1]; for (i = ITER_STEPS - 1; i >= 0; i--) { closest_total = totals[i]; closest_overlap = overlaps[i]; if (closest_total < smallest_total) { smallest_total = closest_total; smallest_overlap = closest_overlap; } if (closest_total <= time_total_nw) break; } if (closest_total > time_total_nw) { closest_total = smallest_total; closest_overlap = smallest_overlap; } stats[NOWORK] = time_total_nw; stats[TOTAL] = closest_total; stats[OVERLAP] = closest_overlap; } } else { if (second != -1) { for (i = 0; i < ITER_STEPS; i++) { log_debug("barrier C0\n"); armci_msg_barrier(); } stats[NOWORK] = time_total_nw; stats[TOTAL] = 0; stats[OVERLAP] = 0; } } ARMCI_ASSERT(ARMCI_Free(array_ptrs[rank])); free(array_ptrs); log_debug("barrier D\n"); armci_msg_barrier(); return stats; } int main (int argc, char *argv[]) { int i, j, k, l; double u; char buf[255]; int dist, pos, time_seed; int msg_sizes[MSG_COUNT], dim1_sizes[MSG_COUNT], dim2[MSG_COUNT], mul_elem; double *stats=NULL, *stats_all=NULL; double from_log = log2(MIN_MSG_SIZE); double to_log = log2(MAX_MSG_SIZE); double step_log = (to_log - from_log) / (MSG_COUNT - 1); armci_msg_init(&argc, &argv); rank = armci_msg_me(); size = armci_msg_nproc(); assert((size & 1) ^ 1); /* works with even number of processors only */ log_debug("Message passing initialized\n"); ARMCI_ASSERT(ARMCI_Init()); log_debug("ARMCI initialized\n"); if (!rank) start_logging(argv[0]); /* generate MSG_COUNT message sizes MIN_MSG_SIZE thru MAX_MSG_SIZE */ for (i = 0, u = from_log; i < MSG_COUNT; i++, u += step_log) { mul_elem = round(exp2(u)); msg_sizes[i] = mul_elem % ((int)SIZE_ELEM) ? (mul_elem / ((int)SIZE_ELEM) + 1) * ((int)SIZE_ELEM) : mul_elem; /* multiple of SIZE_ELEM */ } /* generate MSG_COUNT respective dim1 sizes and dim2 for strided */ for (i = 0; i < MSG_COUNT; i++) { mul_elem = msg_sizes[i] / SIZE_ELEM; mul_elem = sqrt(2.0 * mul_elem); dim1_sizes[i] = mul_elem * SIZE_ELEM; dim2[i] = mul_elem / 2; } /* print msg_sizes and appropriate derivatives (debug mode only) */ if (!rank) { log_debug("msg_sizes:\n"); for (i = 0; i < MSG_COUNT; i++) log_debug("cont: %d bytes | strided: %d bytes X %d\n", msg_sizes[i], dim1_sizes[i], dim2[i]); } /* inialize PRNG, use seed generated on processor 0 for uniform sequence */ time_seed = time(NULL); MPI_Bcast(&time_seed, 1, MPI_INT, 0, MPI_COMM_WORLD); srand(time_seed); rand(); log_debug("seed: %d\n", time_seed); /* generate random pairs of processors */ #define HALFSIZE (size / 2) p_srcs = malloc(sizeof(int) * size); assert(p_srcs); for (i = 0; i < size; i++) p_srcs[i] = -1; p_dsts = p_srcs + HALFSIZE; for (i = 0, j = size - 1, pos = 0; i < size; i++, j--) { dist = round((double)rand() * j / RAND_MAX + 1); /* random 1..j */ for (l = 0; l < dist; ) { pos = (pos + 1 == size) ? 0 : pos + 1; if ((p_srcs[pos] == -1) && (pos != i)) l++; } p_srcs[pos] = i; } for (i = 0, j = 0; i < HALFSIZE; i++) j += sprintf(buf + j, " %d->%d", p_srcs[i], p_dsts[i]); log_debug("random pairs:%s\n", buf); /* time interations: 1 thru ITERS */ time_iterations(); /* determine if processor initiates communication and where it sends to, * * -1 for second(receiver) */ second = -1; for (i = 0; i < HALFSIZE; i++) if (p_srcs[i] == rank) second = p_dsts[i]; log_debug("second: %d\n", second); /* allocate memory for statisticis */ #define MSG_OFF (STATS_COUNT * size) #define OPS_OFF (MSG_OFF * MSG_COUNT) stats_all = malloc(OPS_COUNT * OPS_OFF * sizeof(double)); assert(stats_all); for (i = 0; i < OPS_COUNT; i++) for (j = 0; j < MSG_COUNT; j++) { switch (i) { case CONT_PUT: case CONT_GET: stats = benchmark(i, msg_sizes[j], 0); log_debug("stats: %8d | %.8f | %.8f | %.8f | %.2f\n", msg_sizes[j], stats[NOWORK], stats[TOTAL], stats[OVERLAP], 100.0 * stats[OVERLAP] / stats[TOTAL]); break; case STRIDED_PUT: case STRIDED_GET: case STRIDED_ACC: stats = benchmark(i, dim1_sizes[j], dim2[j]); log_debug("stats: %8d | %.8f | %.8f | %.8f | %.2f\n", dim1_sizes[j] * dim2[j], stats[NOWORK], stats[TOTAL], stats[OVERLAP], 100.0 * stats[OVERLAP] / stats[TOTAL]); break; } MPI_Gather(stats, STATS_COUNT, MPI_DOUBLE, stats_all + i * OPS_OFF + j * MSG_OFF, STATS_COUNT, MPI_DOUBLE, 0, MPI_COMM_WORLD); } if (!rank) for (l = 0; l < HALFSIZE; l++) { /* interate thru pairs */ log_printf("for pair of processors %d -> %d:\n", p_srcs[l], p_dsts[l]); for (i = 0; i < OPS_COUNT; i++) { /* iterate thru operations */ switch (i) { case CONT_PUT: log_printf("ARMCI_NbPut\n"); break; case CONT_GET: log_printf("ARMCI_NbGet\n"); break; case STRIDED_PUT: log_printf("ARMCI_NbPutS\n"); break; case STRIDED_GET: log_printf("ARMCI_NbGetS\n"); break; case STRIDED_ACC: log_printf("ARMCI_NbAccS\n"); break; } log_printf("msg size | nowork | total | overlap |" " ratio\n"); log_printf("---------+------------+------------+------------+" "------\n"); for (j = 0; j < MSG_COUNT; j++) { /* iterate thru msg sizes */ k = i * OPS_OFF + j * MSG_OFF + p_srcs[l] * STATS_COUNT; log_printf("%8d | %.8f | %.8f | %.8f | %.2f\n", NON_CONT(i) ? dim1_sizes[j] * dim2[j]: msg_sizes[j], stats_all[k + NOWORK], stats_all[k + TOTAL], stats_all[k + OVERLAP], (stats_all[k + NOWORK] < stats_all[k + TOTAL]) || (stats_all[k + TOTAL] <= 0.0) ? 0 : 100.0 * stats_all[k + OVERLAP] / stats_all[k + TOTAL]); } log_printf("\n"); } } if (!rank) finish_logging(); ARMCI_Finalize(); armci_msg_finalize(); free(p_srcs); free(stats_all); return 0; } ga-5.9.2/armci/examples/features/non-blocking/simple/000077500000000000000000000000001500715745200225245ustar00rootroot00000000000000ga-5.9.2/armci/examples/features/non-blocking/simple/simple.c000066400000000000000000000032251500715745200241630ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id$ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_UNISTD_H # include #endif #include "armci.h" #include "message.h" int me,nprocs; int LOOP=10; int main(int argc, char **argv) { int i; double **myptrs; double t0,t1,tnbget=0,tnbwait=0,t2=0; armci_msg_init(&argc,&argv); nprocs = armci_msg_nproc(); if (nprocs==1) { fprintf(stderr,"You must use more than 1 process for this test. Exiting gently."); return 0; } me = armci_msg_me(); myptrs = (double **)malloc(sizeof(double *)*nprocs); ARMCI_Init(); ARMCI_Malloc((void **)myptrs,LOOP*sizeof(double)); armci_msg_barrier(); if(me==0){ for(i=0;i #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_UNISTD_H # include #endif #define DEBUG__ #include "armci.h" #include "message.h" #define DIM1 5 #define DIM2 3 #ifdef __sun /* Solaris has shared memory shortages in the default system configuration */ # define DIM3 6 # define DIM4 5 # define DIM5 4 #elif defined(__alpha__) # define DIM3 8 # define DIM4 5 # define DIM5 6 #else # define DIM3 8 # define DIM4 9 # define DIM5 7 #endif #define DIM6 3 #define DIM7 2 #define OFF 1 #define EDIM1 (DIM1+OFF) #define EDIM2 (DIM2+OFF) #define EDIM3 (DIM3+OFF) #define EDIM4 (DIM4+OFF) #define EDIM5 (DIM5+OFF) #define EDIM6 (DIM6+OFF) #define EDIM7 (DIM7+OFF) #define DIMS 4 #define MAXDIMS 7 #define MAX_DIM_VAL 50 #define LOOP 200 #define BASE 100. #define MAXPROC 128 #define TIMES 100 # define ELEMS 200 /***************************** macros ************************/ #define COPY(src, dst, bytes) memcpy((dst),(src),(bytes)) #define ARMCI_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define ARMCI_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define ARMCI_ABS(a) (((a) <0) ? -(a) : (a)) /***************************** global data *******************/ int me, nproc; void* work[MAXPROC]; /* work array for propagating addresses */ /*\ generate random range for a section of multidimensional array \*/ void get_range(int ndim, int dims[], int lo[], int hi[]) { int dim; for(dim=0; dim 0)? rand()%range : lo[dim]; new_lo[dim] = toss; new_hi[dim] = toss + diff -1; assert(new_hi[dim] < dims[dim]); assert(diff == (new_hi[dim] -new_lo[dim]+1)); } } /*\ print range of ndim dimensional array with two strings before and after \*/ void print_range(char *pre,int ndim, int lo[], int hi[], char* post) { int i; printf("%s[",pre); for(i=0;i1 takes a partial plane */ /* create shared and local arrays */ create_array(b, sizeof(double),ndim,dimsB); create_array(a, sizeof(double),ndim,dimsB); elems = get_elems(ndim, stride, dimsB, sizeof(double)); init((double*)a[me], ndim, elems, dimsB); for(i=0; i dimsB[i]) ? dimsB[i]-1: dimsB[i]-less; count[i]=hi[i]-lo[i]+1; } count[0]*=sizeof(double); for(i=0; i1)? dimsB[ndim-1] : 1; strl = (ndim>1)? ndim-2: 0; /* strides of the subpatch to transfer */ for(i=0;i #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_UNISTD_H # include #endif #include #include #include "armci.h" #include "gpc.h" typedef struct {double real,imag;} DoubleComplex; #define LOOP 100 int accloop; int gpcwork_daxpy; int gpcwork_dgemm; int gpcwork_ddot; int gpcwork_memcpy; int me,nprocs; int size=2100*1024*LOOP; int count=2100*1024; int stride_arr[2]={0,0}; double alpha=0.1; double c_alpha=0.1; char **myptrs; double t0,t1,t2,t3; double tmpbuf[LOOP*100]; double tmpbuf1[LOOP*100]; double tmpbuf2[LOOP*100000]; double tmpbuf3[LOOP*100000]; #define DGS 450 double dga[DGS][DGS]; double dgb[DGS][DGS]; double dgc[DGS][DGS]; int gpc_work_handler_daxpy(int to, int from, void *hdr, int hlen, void *data, int dlen, void *rhdr, int rhlen, int *rhsize, void *rdata, int rdlen, int *rdsize, int rtype) { int *rem; int tmp_loop; int i,j; int ONE=1; int N=DGS; rem = (int*)ARMCI_Gpc_translate(*(void**)hdr,to,from); tmp_loop = *rem; t2=MPI_Wtime(); for(j=0;j=2 procs\n"); MPI_Finalize(); exit(1); } right = (me+1)%nprocs; hlen=sizeof(header); bzero(rheader,100); rhlen = hlen; ARMCI_Init(); accloop=atoi(argv[1]); rem=accloop; myptrs = (char **)malloc(sizeof(char *)*nprocs); ARMCI_Malloc((void **)myptrs,size); MPI_Barrier(MPI_COMM_WORLD); gpcwork_memcpy = ARMCI_Gpc_register(gpc_work_handler_memcpy); gpcwork_ddot =ARMCI_Gpc_register(gpc_work_handler_ddot); gpcwork_daxpy = ARMCI_Gpc_register(gpc_work_handler_daxpy); gpcwork_dgemm = ARMCI_Gpc_register(gpc_work_handler_dgemm); MPI_Barrier(MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); ARMCI_Gpc_init_handle(&nbh); if(ARMCI_Gpc_exec(gpcwork_memcpy, right, &header, hlen, loc, sizeof(int), rheader, rhlen,loc, sizeof(int), &nbh)) fprintf(stderr,"ARMCI_Gpc_exec failed\n"); { int m,n,k; char notr='n'; DoubleComplex ZERO; usleep(100); ZERO.real=0.;ZERO.imag=0.; m=n=k=DGS; t0=MPI_Wtime(); #ifdef DGEMM_WORK for(j=0;j<4*15;j++){ c_alpha=c_alpha+j*rand(); dgemm_(¬r,¬r,&m,&n,&k,&c_alpha,c_dga,&m,c_dgb,&n,&ZERO,c_dgc,&k,1,1); } #elif IUNIT_WORK for(j=0;j<2*LOOP*100;j++){ for(i=0;i (10 9 8 7 6 5 4 3 2 1) */ #define TOTALELEMS 1007031 #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_WINDOWS_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_ASSERT_H # include #endif #include #include "armci.h" #include "message.h" void VERIFY(void **b_ptr, int *dims, int *map) { int i, j, length, icnt, ichk, lmin, lmax; int *buf, *b; void *src_ptr, *dst_ptr; int me, nprocs; /* Find local processor ID and number of processors */ me = armci_msg_me(); nprocs = armci_msg_nproc(); /* Process 0 verifies that inversion is correct. Start by allocating buffer and guarantee that it is big enough */ length = (int)(((double)dims[0])/((double)nprocs)) + 1; buf = (int*)malloc(length*sizeof(int)); if (me == 0) { icnt = 0; ichk = 0; for (i=0; i= map[i]) { pmin = i; } else { break; } } pmax = nprocs-1; for (i=nprocs-2; i>=0; i--) { if (max < map[i+1]) { pmax = i; } else { break; } } /* Loop over processors that will receive data and copy inverted data to processors */ for (i=pmin; i<=pmax; i++) { /* Find min and max indices owned by processor i */ lmin = map[i]; if (i min) { cmin = lmin; } else { cmin = min; } if (lmax < max) { cmax = lmax; } else { cmax = max; } /* Find offsets on source and destination processors */ src_offset = cmin - min; src_ptr = (void*)(buf + src_offset); dst_offset = cmin - lmin; dst_ptr = ((char*)b_ptr[i]) + sizeof(int)*dst_offset; /* Find length of data (in bytes) to be sent to processor i */ length = sizeof(int)*(cmax-cmin+1); /* Send data to processor */ ARMCI_Put(src_ptr, dst_ptr, length, i); } ARMCI_AllFence(); armci_msg_barrier(); free(buf); VERIFY(b_ptr, dims, map); free(map); armci_msg_barrier(); ARMCI_Free(a_ptr[me]); ARMCI_Free(b_ptr[me]); free(a_ptr); free(b_ptr); } int main(int argc, char **argv) { /* int heap=300000, stack=300000; */ int me, nprocs; /* Step1: Initialize Message Passing library */ armci_msg_init(&argc, &argv); /* Step2: Initialize ARMCI */ ARMCI_Init(); /* Step3: Initialize Memory Allocator (MA) */ /*bjp if(! MA_init(C_DBL, stack, heap) ) ARMCI_Error("MA_init failed",stack+heap); */ me = armci_msg_me(); nprocs = armci_msg_nproc(); if(me==0) { printf("\nUsing %d processes\n\n", nprocs); fflush(stdout); } TRANSPOSE1D(); if(me==0)printf("\nTerminating ..\n"); ARMCI_Finalize(); armci_msg_finalize(); return(0); } ga-5.9.2/armci/f90/000077500000000000000000000000001500715745200136155ustar00rootroot00000000000000ga-5.9.2/armci/f90/armci_mem_f90.f90000066400000000000000000001574151500715745200165610ustar00rootroot00000000000000module armci_mem_f90 use armci_types interface armci_malloc_fa module procedure armci_malloc_1di4 module procedure armci_malloc_2di4 module procedure armci_malloc_3di4 module procedure armci_malloc_4di4 module procedure armci_malloc_5di4 module procedure armci_malloc_6di4 module procedure armci_malloc_7di4 module procedure armci_malloc_1di8 module procedure armci_malloc_2di8 module procedure armci_malloc_3di8 module procedure armci_malloc_4di8 module procedure armci_malloc_5di8 module procedure armci_malloc_6di8 module procedure armci_malloc_7di8 module procedure armci_malloc_1dr4 module procedure armci_malloc_2dr4 module procedure armci_malloc_3dr4 module procedure armci_malloc_4dr4 module procedure armci_malloc_5dr4 module procedure armci_malloc_6dr4 module procedure armci_malloc_7dr4 module procedure armci_malloc_1dr8 module procedure armci_malloc_2dr8 module procedure armci_malloc_3dr8 module procedure armci_malloc_4dr8 module procedure armci_malloc_5dr8 module procedure armci_malloc_6dr8 module procedure armci_malloc_7dr8 module procedure armci_malloc_1dc4 module procedure armci_malloc_2dc4 module procedure armci_malloc_3dc4 module procedure armci_malloc_4dc4 module procedure armci_malloc_5dc4 module procedure armci_malloc_6dc4 module procedure armci_malloc_7dc4 module procedure armci_malloc_1dc8 module procedure armci_malloc_2dc8 module procedure armci_malloc_3dc8 module procedure armci_malloc_4dc8 module procedure armci_malloc_5dc8 module procedure armci_malloc_6dc8 module procedure armci_malloc_7dc8 end interface interface armci_free_fa module procedure armci_free_1di4 module procedure armci_free_2di4 module procedure armci_free_3di4 module procedure armci_free_4di4 module procedure armci_free_5di4 module procedure armci_free_6di4 module procedure armci_free_7di4 module procedure armci_free_1di8 module procedure armci_free_2di8 module procedure armci_free_3di8 module procedure armci_free_4di8 module procedure armci_free_5di8 module procedure armci_free_6di8 module procedure armci_free_7di8 module procedure armci_free_1dr4 module procedure armci_free_2dr4 module procedure armci_free_3dr4 module procedure armci_free_4dr4 module procedure armci_free_5dr4 module procedure armci_free_6dr4 module procedure armci_free_7dr4 module procedure armci_free_1dr8 module procedure armci_free_2dr8 module procedure armci_free_3dr8 module procedure armci_free_4dr8 module procedure armci_free_5dr8 module procedure armci_free_6dr8 module procedure armci_free_7dr8 module procedure armci_free_1dc4 module procedure armci_free_2dc4 module procedure armci_free_3dc4 module procedure armci_free_4dc4 module procedure armci_free_5dc4 module procedure armci_free_6dc4 module procedure armci_free_7dc4 module procedure armci_free_1dc8 module procedure armci_free_2dc8 module procedure armci_free_3dc8 module procedure armci_free_4dc8 module procedure armci_free_5dc8 module procedure armci_free_6dc8 module procedure armci_free_7dc8 end interface contains subroutine armci_malloc_1di4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind integer(kind=i4), dimension(:), pointer :: a integer, intent(in) :: rank, elemsize, lb(1), ub(1) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface integer(kind=i4), dimension(:), pointer :: a integer, dimension(1) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 4 integer :: rank = 1 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) if (rc .ne. 0) print *, "error armci_malloc_1di4: error = ", rc end subroutine armci_malloc_1di4 subroutine armci_malloc_2di4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind integer(kind=i4), dimension(:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(2), ub(2) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface integer(kind=i4), dimension(:,:), pointer :: a integer, dimension(2) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 4 integer :: rank = 2 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) if (rc .ne. 0) print *, "error armci_malloc_2di4: error = ", rc end subroutine armci_malloc_2di4 subroutine armci_malloc_3di4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind integer(kind=i4), dimension(:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(3), ub(3) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface integer(kind=i4), dimension(:,:,:), pointer :: a integer, dimension(3) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 4 integer :: rank = 3 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) if (rc .ne. 0) print *, "error armci_malloc_3di4: error = ", rc end subroutine armci_malloc_3di4 subroutine armci_malloc_4di4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind integer(kind=i4), dimension(:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(4), ub(4) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface integer(kind=i4), dimension(:,:,:,:), pointer :: a integer, dimension(4) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 4 integer :: rank = 4 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) if (rc .ne. 0) print *, "error armci_malloc_4di4: error = ", rc end subroutine armci_malloc_4di4 subroutine armci_malloc_5di4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind integer(kind=i4), dimension(:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(5), ub(5) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface integer(kind=i4), dimension(:,:,:,:,:), pointer :: a integer, dimension(5) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 4 integer :: rank = 5 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) if (rc .ne. 0) print *, "error armci_malloc_5di4: error = ", rc end subroutine armci_malloc_5di4 subroutine armci_malloc_6di4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind integer(kind=i4), dimension(:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(6), ub(6) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface integer(kind=i4), dimension(:,:,:,:,:,:), pointer :: a integer, dimension(6) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 4 integer :: rank = 6 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) if (rc .ne. 0) print *, "error armci_malloc_6di4: error = ", rc end subroutine armci_malloc_6di4 subroutine armci_malloc_7di4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind integer(kind=i4), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(7), ub(7) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface integer(kind=i4), dimension(:,:,:,:,:,:,:), pointer :: a integer, dimension(7) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 4 integer :: rank = 7 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) if (rc .ne. 0) print *, "error armci_malloc_7di4: error = ", rc end subroutine armci_malloc_7di4 subroutine armci_free_1di4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind integer(kind=i4), dimension(:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface integer(kind=i4), dimension(:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 1 call armci_free_farray(a, rank, rc) if (rc .ne. 0) print *, "error armci_free_1di4: error = ", rc end subroutine armci_free_1di4 subroutine armci_free_2di4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind integer(kind=i4), dimension(:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface integer(kind=i4), dimension(:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 2 call armci_free_farray(a, rank, rc) if (rc .ne. 0) print *, "error armci_free_2di4: error = ", rc end subroutine armci_free_2di4 subroutine armci_free_3di4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind integer(kind=i4), dimension(:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface integer(kind=i4), dimension(:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 3 call armci_free_farray(a, rank, rc) if (rc .ne. 0) print *, "error armci_free_3di4: error = ", rc end subroutine armci_free_3di4 subroutine armci_free_4di4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind integer(kind=i4), dimension(:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface integer(kind=i4), dimension(:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 4 call armci_free_farray(a, rank, rc) if (rc .ne. 0) print *, "error armci_free_4di4: error = ", rc end subroutine armci_free_4di4 subroutine armci_free_5di4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind integer(kind=i4), dimension(:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface integer(kind=i4), dimension(:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 5 call armci_free_farray(a, rank, rc) if (rc .ne. 0) print *, "error armci_free_5di4: error = ", rc end subroutine armci_free_5di4 subroutine armci_free_6di4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind integer(kind=i4), dimension(:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface integer(kind=i4), dimension(:,:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 6 call armci_free_farray(a, rank, rc) if (rc .ne. 0) print *, "error armci_free_6di4: error = ", rc end subroutine armci_free_6di4 subroutine armci_free_7di4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind integer(kind=i4), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface integer(kind=i4), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 7 call armci_free_farray(a, rank, rc) if (rc .ne. 0) print *, "error armci_free_7di4: error = ", rc end subroutine armci_free_7di4 subroutine armci_malloc_1di8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind integer(kind=i8), dimension(:), pointer :: a integer, intent(in) :: rank, elemsize, lb(1), ub(1) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface integer(kind=i8), dimension(:), pointer :: a integer, dimension(1) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 1 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) if (rc .ne. 0) print *, "error armci_malloc_1di8: error = ", rc end subroutine armci_malloc_1di8 subroutine armci_malloc_2di8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind integer(kind=i8), dimension(:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(2), ub(2) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface integer(kind=i8), dimension(:,:), pointer :: a integer, dimension(2) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 2 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) if (rc .ne. 0) print *, "error armci_malloc_2di8: error = ", rc end subroutine armci_malloc_2di8 subroutine armci_malloc_3di8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind integer(kind=i8), dimension(:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(3), ub(3) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface integer(kind=i8), dimension(:,:,:), pointer :: a integer, dimension(3) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 3 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) if (rc .ne. 0) print *, "error armci_malloc_3di8: error = ", rc end subroutine armci_malloc_3di8 subroutine armci_malloc_4di8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind integer(kind=i8), dimension(:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(4), ub(4) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface integer(kind=i8), dimension(:,:,:,:), pointer :: a integer, dimension(4) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 4 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) if (rc .ne. 0) print *, "error armci_malloc_4di8: error = ", rc end subroutine armci_malloc_4di8 subroutine armci_malloc_5di8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind integer(kind=i8), dimension(:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(5), ub(5) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface integer(kind=i8), dimension(:,:,:,:,:), pointer :: a integer, dimension(5) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 5 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) if (rc .ne. 0) print *, "error armci_malloc_5di8: error = ", rc end subroutine armci_malloc_5di8 subroutine armci_malloc_6di8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind integer(kind=i8), dimension(:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(6), ub(6) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface integer(kind=i8), dimension(:,:,:,:,:,:), pointer :: a integer, dimension(6) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 6 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_6di8: error = ", rc end subroutine armci_malloc_6di8 subroutine armci_malloc_7di8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind integer(kind=i8), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(7), ub(7) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface integer(kind=i8), dimension(:,:,:,:,:,:,:), pointer :: a integer, dimension(7) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 7 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_7di8: error = ", rc end subroutine armci_malloc_7di8 subroutine armci_free_1di8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind integer(kind=i8), dimension(:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface integer(kind=i8), dimension(:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 1 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_1di8: error = ", rc end subroutine armci_free_1di8 subroutine armci_free_2di8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind integer(kind=i8), dimension(:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface integer(kind=i8), dimension(:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 2 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_2di8: error = ", rc end subroutine armci_free_2di8 subroutine armci_free_3di8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind integer(kind=i8), dimension(:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface integer(kind=i8), dimension(:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 3 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_3di8: error = ", rc end subroutine armci_free_3di8 subroutine armci_free_4di8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind integer(kind=i8), dimension(:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface integer(kind=i8), dimension(:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 4 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_4di8: error = ", rc end subroutine armci_free_4di8 subroutine armci_free_5di8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind integer(kind=i8), dimension(:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface integer(kind=i8), dimension(:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 5 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_5di8: error = ", rc end subroutine armci_free_5di8 subroutine armci_free_6di8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind integer(kind=i8), dimension(:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface integer(kind=i8), dimension(:,:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 6 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_6di8: error = ", rc end subroutine armci_free_6di8 subroutine armci_free_7di8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind integer(kind=i8), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface integer(kind=i8), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 7 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_7di8: error = ", rc end subroutine armci_free_7di8 subroutine armci_malloc_1dr4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind real(kind=r4), dimension(:), pointer :: a integer, intent(in) :: rank, elemsize, lb(1), ub(1) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface real(kind=r4), dimension(:), pointer :: a integer, dimension(1) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 4 integer :: rank = 1 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_1dr4: error = ", rc end subroutine armci_malloc_1dr4 subroutine armci_malloc_2dr4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind real(kind=r4), dimension(:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(2), ub(2) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface real(kind=r4), dimension(:,:), pointer :: a integer, dimension(2) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 4 integer :: rank = 2 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_2dr4: error = ", rc end subroutine armci_malloc_2dr4 subroutine armci_malloc_3dr4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind real(kind=r4), dimension(:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(3), ub(3) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface real(kind=r4), dimension(:,:,:), pointer :: a integer, dimension(3) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 4 integer :: rank = 3 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_3dr4: error = ", rc end subroutine armci_malloc_3dr4 subroutine armci_malloc_4dr4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind real(kind=r4), dimension(:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(4), ub(4) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface real(kind=r4), dimension(:,:,:,:), pointer :: a integer, dimension(4) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 4 integer :: rank = 4 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_4dr4: error = ", rc end subroutine armci_malloc_4dr4 subroutine armci_malloc_5dr4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind real(kind=r4), dimension(:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(5), ub(5) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface real(kind=r4), dimension(:,:,:,:,:), pointer :: a integer, dimension(5) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 4 integer :: rank = 5 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_5dr4: error = ", rc end subroutine armci_malloc_5dr4 subroutine armci_malloc_6dr4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind real(kind=r4), dimension(:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(6), ub(6) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface real(kind=r4), dimension(:,:,:,:,:,:), pointer :: a integer, dimension(6) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 4 integer :: rank = 6 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_6dr4: error = ", rc end subroutine armci_malloc_6dr4 subroutine armci_malloc_7dr4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind real(kind=r4), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(7), ub(7) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface real(kind=r4), dimension(:,:,:,:,:,:,:), pointer :: a integer, dimension(7) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 4 integer :: rank = 7 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_7dr4: error = ", rc end subroutine armci_malloc_7dr4 subroutine armci_free_1dr4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind real(kind=r4), dimension(:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface real(kind=r4), dimension(:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 1 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_1dr4: error = ", rc end subroutine armci_free_1dr4 subroutine armci_free_2dr4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind real(kind=r4), dimension(:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface real(kind=r4), dimension(:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 2 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_2dr4: error = ", rc end subroutine armci_free_2dr4 subroutine armci_free_3dr4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind real(kind=r4), dimension(:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface real(kind=r4), dimension(:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 3 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_3dr4: error = ", rc end subroutine armci_free_3dr4 subroutine armci_free_4dr4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind real(kind=r4), dimension(:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface real(kind=r4), dimension(:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 4 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_4dr4: error = ", rc end subroutine armci_free_4dr4 subroutine armci_free_5dr4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind real(kind=r4), dimension(:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface real(kind=r4), dimension(:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 5 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_5dr4: error = ", rc end subroutine armci_free_5dr4 subroutine armci_free_6dr4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind real(kind=r4), dimension(:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface real(kind=r4), dimension(:,:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 6 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_6dr4: error = ", rc end subroutine armci_free_6dr4 subroutine armci_free_7dr4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind real(kind=r4), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface real(kind=r4), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 7 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_7dr4: error = ", rc end subroutine armci_free_7dr4 subroutine armci_malloc_1dr8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind real(kind=r8), dimension(:), pointer :: a integer, intent(in) :: rank, elemsize, lb(1), ub(1) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface real(kind=r8), dimension(:), pointer :: a integer, dimension(1) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 1 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_1dr8: error = ", rc end subroutine armci_malloc_1dr8 subroutine armci_malloc_2dr8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind real(kind=r8), dimension(:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(2), ub(2) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface real(kind=r8), dimension(:,:), pointer :: a integer, dimension(2) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 2 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_2dr8: error = ", rc end subroutine armci_malloc_2dr8 subroutine armci_malloc_3dr8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind real(kind=r8), dimension(:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(3), ub(3) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface real(kind=r8), dimension(:,:,:), pointer :: a integer, dimension(3) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 3 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_3dr8: error = ", rc end subroutine armci_malloc_3dr8 subroutine armci_malloc_4dr8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind real(kind=r8), dimension(:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(4), ub(4) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface real(kind=r8), dimension(:,:,:,:), pointer :: a integer, dimension(4) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 4 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_4dr8: error = ", rc end subroutine armci_malloc_4dr8 subroutine armci_malloc_5dr8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind real(kind=r8), dimension(:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(5), ub(5) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface real(kind=r8), dimension(:,:,:,:,:), pointer :: a integer, dimension(5) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 5 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_5dr8: error = ", rc end subroutine armci_malloc_5dr8 subroutine armci_malloc_6dr8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind real(kind=r8), dimension(:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(6), ub(6) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface real(kind=r8), dimension(:,:,:,:,:,:), pointer :: a integer, dimension(6) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 6 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_6dr8: error = ", rc end subroutine armci_malloc_6dr8 subroutine armci_malloc_7dr8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind real(kind=r8), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(7), ub(7) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface real(kind=r8), dimension(:,:,:,:,:,:,:), pointer :: a integer, dimension(7) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 7 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_7dr8: error = ", rc end subroutine armci_malloc_7dr8 subroutine armci_free_1dr8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind real(kind=r8), dimension(:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface real(kind=r8), dimension(:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 1 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_1dr8: error = ", rc end subroutine armci_free_1dr8 subroutine armci_free_2dr8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind real(kind=r8), dimension(:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface real(kind=r8), dimension(:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 2 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_2dr8: error = ", rc end subroutine armci_free_2dr8 subroutine armci_free_3dr8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind real(kind=r8), dimension(:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface real(kind=r8), dimension(:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 3 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_3dr8: error = ", rc end subroutine armci_free_3dr8 subroutine armci_free_4dr8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind real(kind=r8), dimension(:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface real(kind=r8), dimension(:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 4 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_4dr8: error = ", rc end subroutine armci_free_4dr8 subroutine armci_free_5dr8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind real(kind=r8), dimension(:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface real(kind=r8), dimension(:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 5 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_5dr8: error = ", rc end subroutine armci_free_5dr8 subroutine armci_free_6dr8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind real(kind=r8), dimension(:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface real(kind=r8), dimension(:,:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 6 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_6dr8: error = ", rc end subroutine armci_free_6dr8 subroutine armci_free_7dr8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind real(kind=r8), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface real(kind=r8), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 7 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_7dr8: error = ", rc end subroutine armci_free_7dr8 subroutine armci_malloc_1dc4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind complex(kind=c4), dimension(:), pointer :: a integer, intent(in) :: rank, elemsize, lb(1), ub(1) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface complex(kind=c4), dimension(:), pointer :: a integer, dimension(1) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 1 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_1dc4: error = ", rc end subroutine armci_malloc_1dc4 subroutine armci_malloc_2dc4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind complex(kind=c4), dimension(:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(2), ub(2) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface complex(kind=c4), dimension(:,:), pointer :: a integer, dimension(2) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 2 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_2dc4: error = ", rc end subroutine armci_malloc_2dc4 subroutine armci_malloc_3dc4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind complex(kind=c4), dimension(:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(3), ub(3) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface complex(kind=c4), dimension(:,:,:), pointer :: a integer, dimension(3) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 3 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_3dc4: error = ", rc end subroutine armci_malloc_3dc4 subroutine armci_malloc_4dc4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind complex(kind=c4), dimension(:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(4), ub(4) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface complex(kind=c4), dimension(:,:,:,:), pointer :: a integer, dimension(4) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 4 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_4dc4: error = ", rc end subroutine armci_malloc_4dc4 subroutine armci_malloc_5dc4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind complex(kind=c4), dimension(:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(5), ub(5) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface complex(kind=c4), dimension(:,:,:,:,:), pointer :: a integer, dimension(5) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 5 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_5dc4: error = ", rc end subroutine armci_malloc_5dc4 subroutine armci_malloc_6dc4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind complex(kind=c4), dimension(:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(6), ub(6) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface complex(kind=c4), dimension(:,:,:,:,:,:), pointer :: a integer, dimension(6) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 6 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_6dc4: error = ", rc end subroutine armci_malloc_6dc4 subroutine armci_malloc_7dc4(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind complex(kind=c4), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(7), ub(7) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface complex(kind=c4), dimension(:,:,:,:,:,:,:), pointer :: a integer, dimension(7) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 8 integer :: rank = 7 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_7dc4: error = ", rc end subroutine armci_malloc_7dc4 subroutine armci_free_1dc4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind complex(kind=c4), dimension(:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface complex(kind=c4), dimension(:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 1 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_1dc4: error = ", rc end subroutine armci_free_1dc4 subroutine armci_free_2dc4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind complex(kind=c4), dimension(:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface complex(kind=c4), dimension(:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 2 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_2dc4: error = ", rc end subroutine armci_free_2dc4 subroutine armci_free_3dc4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind complex(kind=c4), dimension(:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface complex(kind=c4), dimension(:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 3 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_3dc4: error = ", rc end subroutine armci_free_3dc4 subroutine armci_free_4dc4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind complex(kind=c4), dimension(:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface complex(kind=c4), dimension(:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 4 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_4dc4: error = ", rc end subroutine armci_free_4dc4 subroutine armci_free_5dc4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind complex(kind=c4), dimension(:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface complex(kind=c4), dimension(:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 5 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_5dc4: error = ", rc end subroutine armci_free_5dc4 subroutine armci_free_6dc4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind complex(kind=c4), dimension(:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface complex(kind=c4), dimension(:,:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 6 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_6dc4: error = ", rc end subroutine armci_free_6dc4 subroutine armci_free_7dc4(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind complex(kind=c4), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface complex(kind=c4), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 7 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_7dc4: error = ", rc end subroutine armci_free_7dc4 subroutine armci_malloc_1dc8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind complex(kind=c8), dimension(:), pointer :: a integer, intent(in) :: rank, elemsize, lb(1), ub(1) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface complex(kind=c8), dimension(:), pointer :: a integer, dimension(1) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 16 integer :: rank = 1 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_1dc8: error = ", rc end subroutine armci_malloc_1dc8 subroutine armci_malloc_2dc8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind complex(kind=c8), dimension(:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(2), ub(2) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface complex(kind=c8), dimension(:,:), pointer :: a integer, dimension(2) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 16 integer :: rank = 2 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_2dc8: error = ", rc end subroutine armci_malloc_2dc8 subroutine armci_malloc_3dc8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind complex(kind=c8), dimension(:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(3), ub(3) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface complex(kind=c8), dimension(:,:,:), pointer :: a integer, dimension(3) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 16 integer :: rank = 3 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_3dc8: error = ", rc end subroutine armci_malloc_3dc8 subroutine armci_malloc_4dc8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind complex(kind=c8), dimension(:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(4), ub(4) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface complex(kind=c8), dimension(:,:,:,:), pointer :: a integer, dimension(4) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 16 integer :: rank = 4 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_4dc8: error = ", rc end subroutine armci_malloc_4dc8 subroutine armci_malloc_5dc8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind complex(kind=c8), dimension(:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(5), ub(5) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface complex(kind=c8), dimension(:,:,:,:,:), pointer :: a integer, dimension(5) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 16 integer :: rank = 5 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_5dc8: error = ", rc end subroutine armci_malloc_5dc8 subroutine armci_malloc_6dc8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind complex(kind=c8), dimension(:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(6), ub(6) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface complex(kind=c8), dimension(:,:,:,:,:,:), pointer :: a integer, dimension(6) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 16 integer :: rank = 6 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_6dc8: error = ", rc end subroutine armci_malloc_6dc8 subroutine armci_malloc_7dc8(a, lb, ub, rc) use definekind implicit none interface subroutine armci_malloc_farray(a, rank, elemsize, lb, ub, rc) use definekind complex(kind=c8), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank, elemsize, lb(7), ub(7) integer, intent(out) :: rc end subroutine armci_malloc_farray end interface complex(kind=c8), dimension(:,:,:,:,:,:,:), pointer :: a integer, dimension(7) :: lb, ub integer, intent(out), optional :: rc integer :: elemsize = 16 integer :: rank = 7 call armci_malloc_farray(a, rank, elemsize, lb, ub, rc) ! if (rc .ne. 0) print *, "error armci_malloc_7dc8: error = ", rc end subroutine armci_malloc_7dc8 subroutine armci_free_1dc8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind complex(kind=c8), dimension(:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface complex(kind=c8), dimension(:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 1 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_1dc8: error = ", rc end subroutine armci_free_1dc8 subroutine armci_free_2dc8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind complex(kind=c8), dimension(:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface complex(kind=c8), dimension(:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 2 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_2dc8: error = ", rc end subroutine armci_free_2dc8 subroutine armci_free_3dc8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind complex(kind=c8), dimension(:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface complex(kind=c8), dimension(:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 3 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_3dc8: error = ", rc end subroutine armci_free_3dc8 subroutine armci_free_4dc8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind complex(kind=c8), dimension(:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface complex(kind=c8), dimension(:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 4 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_4dc8: error = ", rc end subroutine armci_free_4dc8 subroutine armci_free_5dc8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind complex(kind=c8), dimension(:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface complex(kind=c8), dimension(:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 5 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_5dc8: error = ", rc end subroutine armci_free_5dc8 subroutine armci_free_6dc8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind complex(kind=c8), dimension(:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface complex(kind=c8), dimension(:,:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 6 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_6dc8: error = ", rc end subroutine armci_free_6dc8 subroutine armci_free_7dc8(a, rc) use definekind implicit none interface subroutine armci_free_farray(a, rank, rc) use definekind complex(kind=c8), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(in) :: rank integer, intent(out) :: rc end subroutine armci_free_farray end interface complex(kind=c8), dimension(:,:,:,:,:,:,:), pointer :: a integer, intent(out), optional :: rc integer :: rank = 7 call armci_free_farray(a, rank, rc) ! if (rc .ne. 0) print *, "error armci_free_7dc8: error = ", rc end subroutine armci_free_7dc8 end module armci_mem_f90 ga-5.9.2/armci/f90/armci_mov_f90.f90000066400000000000000000002156601500715745200166010ustar00rootroot00000000000000module armci_mov_f90 use armci_types interface armci_put_fa module procedure armci_put_1di4 module procedure armci_put_2di4 module procedure armci_put_3di4 module procedure armci_put_4di4 module procedure armci_put_5di4 module procedure armci_put_6di4 module procedure armci_put_7di4 module procedure armci_put_1di8 module procedure armci_put_2di8 module procedure armci_put_3di8 module procedure armci_put_4di8 module procedure armci_put_5di8 module procedure armci_put_6di8 module procedure armci_put_7di8 module procedure armci_put_1dr4 module procedure armci_put_2dr4 module procedure armci_put_3dr4 module procedure armci_put_4dr4 module procedure armci_put_5dr4 module procedure armci_put_6dr4 module procedure armci_put_7dr4 module procedure armci_put_1dr8 module procedure armci_put_2dr8 module procedure armci_put_3dr8 module procedure armci_put_4dr8 module procedure armci_put_5dr8 module procedure armci_put_6dr8 module procedure armci_put_7dr8 module procedure armci_put_1dc4 module procedure armci_put_2dc4 module procedure armci_put_3dc4 module procedure armci_put_4dc4 module procedure armci_put_5dc4 module procedure armci_put_6dc4 module procedure armci_put_7dc4 module procedure armci_put_1dc8 module procedure armci_put_2dc8 module procedure armci_put_3dc8 module procedure armci_put_4dc8 module procedure armci_put_5dc8 module procedure armci_put_6dc8 module procedure armci_put_7dc8 end interface interface armci_get_fa module procedure armci_get_1di4 module procedure armci_get_2di4 module procedure armci_get_3di4 module procedure armci_get_4di4 module procedure armci_get_5di4 module procedure armci_get_6di4 module procedure armci_get_7di4 module procedure armci_get_1di8 module procedure armci_get_2di8 module procedure armci_get_3di8 module procedure armci_get_4di8 module procedure armci_get_5di8 module procedure armci_get_6di8 module procedure armci_get_7di8 module procedure armci_get_1dr4 module procedure armci_get_2dr4 module procedure armci_get_3dr4 module procedure armci_get_4dr4 module procedure armci_get_5dr4 module procedure armci_get_6dr4 module procedure armci_get_7dr4 module procedure armci_get_1dr8 module procedure armci_get_2dr8 module procedure armci_get_3dr8 module procedure armci_get_4dr8 module procedure armci_get_5dr8 module procedure armci_get_6dr8 module procedure armci_get_7dr8 module procedure armci_get_1dc4 module procedure armci_get_2dc4 module procedure armci_get_3dc4 module procedure armci_get_4dc4 module procedure armci_get_5dc4 module procedure armci_get_6dc4 module procedure armci_get_7dc4 module procedure armci_get_1dc8 module procedure armci_get_2dc8 module procedure armci_get_3dc8 module procedure armci_get_4dc8 module procedure armci_get_5dc8 module procedure armci_get_6dc8 module procedure armci_get_7dc8 end interface contains subroutine armci_put_1di4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface integer(kind=i4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_1di4 subroutine armci_put_2di4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface integer(kind=i4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_2di4 subroutine armci_put_3di4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface integer(kind=i4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_3di4 subroutine armci_put_4di4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface integer(kind=i4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_4di4 subroutine armci_put_5di4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface integer(kind=i4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_5di4 subroutine armci_put_6di4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface integer(kind=i4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_6di4 subroutine armci_put_7di4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface integer(kind=i4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_7di4 subroutine armci_put_1di8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface integer(kind=i8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_1di8 subroutine armci_put_2di8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface integer(kind=i8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_2di8 subroutine armci_put_3di8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface integer(kind=i8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_3di8 subroutine armci_put_4di8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface integer(kind=i8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_4di8 subroutine armci_put_5di8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface integer(kind=i8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_5di8 subroutine armci_put_6di8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface integer(kind=i8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_6di8 subroutine armci_put_7di8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface integer(kind=i8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_7di8 subroutine armci_put_1dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface real(kind=r4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_1dr4 subroutine armci_put_2dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface real(kind=r4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_2dr4 subroutine armci_put_3dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface real(kind=r4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_3dr4 subroutine armci_put_4dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface real(kind=r4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_4dr4 subroutine armci_put_5dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface real(kind=r4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_5dr4 subroutine armci_put_6dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface real(kind=r4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_6dr4 subroutine armci_put_7dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface real(kind=r4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_7dr4 subroutine armci_put_1dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface real(kind=r8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_1dr8 subroutine armci_put_2dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface real(kind=r8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_2dr8 subroutine armci_put_3dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface real(kind=r8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_3dr8 subroutine armci_put_4dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface real(kind=r8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_4dr8 subroutine armci_put_5dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface real(kind=r8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_5dr8 subroutine armci_put_6dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface real(kind=r8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_6dr8 subroutine armci_put_7dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface real(kind=r8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_7dr8 subroutine armci_put_1dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface complex(kind=c4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_1dc4 subroutine armci_put_2dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface complex(kind=c4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_2dc4 subroutine armci_put_3dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface complex(kind=c4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_3dc4 subroutine armci_put_4dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface complex(kind=c4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_4dc4 subroutine armci_put_5dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface complex(kind=c4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_5dc4 subroutine armci_put_6dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface complex(kind=c4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_6dc4 subroutine armci_put_7dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface complex(kind=c4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_7dc4 subroutine armci_put_1dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface complex(kind=c8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_1dc8 subroutine armci_put_2dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface complex(kind=c8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_2dc8 subroutine armci_put_3dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface complex(kind=c8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_3dc8 subroutine armci_put_4dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface complex(kind=c8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_4dc8 subroutine armci_put_5dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface complex(kind=c8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_5dc8 subroutine armci_put_6dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface complex(kind=c8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_6dc8 subroutine armci_put_7dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_put_farrays end interface complex(kind=c8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call armci_put_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_put_7dc8 !get subroutine armci_get_1di4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface integer(kind=i4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_1di4 subroutine armci_get_2di4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface integer(kind=i4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_2di4 subroutine armci_get_3di4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface integer(kind=i4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_3di4 subroutine armci_get_4di4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface integer(kind=i4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_4di4 subroutine armci_get_5di4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface integer(kind=i4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_5di4 subroutine armci_get_6di4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface integer(kind=i4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_6di4 subroutine armci_get_7di4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface integer(kind=i4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_7di4 subroutine armci_get_1di8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface integer(kind=i8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_1di8 subroutine armci_get_2di8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface integer(kind=i8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_2di8 subroutine armci_get_3di8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface integer(kind=i8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_3di8 subroutine armci_get_4di8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface integer(kind=i8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_4di8 subroutine armci_get_5di8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface integer(kind=i8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_5di8 subroutine armci_get_6di8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface integer(kind=i8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_6di8 subroutine armci_get_7di8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=i8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface integer(kind=i8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_7di8 subroutine armci_get_1dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface real(kind=r4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_1dr4 subroutine armci_get_2dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface real(kind=r4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_2dr4 subroutine armci_get_3dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface real(kind=r4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_3dr4 subroutine armci_get_4dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface real(kind=r4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_4dr4 subroutine armci_get_5dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface real(kind=r4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_5dr4 subroutine armci_get_6dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface real(kind=r4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_6dr4 subroutine armci_get_7dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface real(kind=r4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_7dr4 subroutine armci_get_1dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface real(kind=r8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_1dr8 subroutine armci_get_2dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface real(kind=r8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_2dr8 subroutine armci_get_3dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface real(kind=r8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_3dr8 subroutine armci_get_4dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface real(kind=r8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_4dr8 subroutine armci_get_5dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface real(kind=r8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_5dr8 subroutine armci_get_6dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface real(kind=r8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_6dr8 subroutine armci_get_7dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=r8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface real(kind=r8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_7dr8 subroutine armci_get_1dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface complex(kind=c4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_1dc4 subroutine armci_get_2dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface complex(kind=c4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_2dc4 subroutine armci_get_3dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface complex(kind=c4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_3dc4 subroutine armci_get_4dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface complex(kind=c4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_4dc4 subroutine armci_get_5dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface complex(kind=c4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_5dc4 subroutine armci_get_6dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface complex(kind=c4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_6dc4 subroutine armci_get_7dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface complex(kind=c4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_7dc4 subroutine armci_get_1dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface complex(kind=c8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_1dc8 subroutine armci_get_2dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface complex(kind=c8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_2dc8 subroutine armci_get_3dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface complex(kind=c8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_3dc8 subroutine armci_get_4dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface complex(kind=c8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_4dc8 subroutine armci_get_5dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface complex(kind=c8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_5dc8 subroutine armci_get_6dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface complex(kind=c8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_6dc8 subroutine armci_get_7dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind implicit none interface subroutine armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=c8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine armci_get_farrays end interface complex(kind=c8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call armci_get_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine armci_get_7dc8 end module armci_mov_f90 ga-5.9.2/armci/f90/armci_nbmov.f90000066400000000000000000002177641500715745200164520ustar00rootroot00000000000000module ARMCI_NbMov use armci_types interface ARMCI_NbPut_fa module procedure ARMCI_NbPut_1di4 module procedure ARMCI_NbPut_2di4 module procedure ARMCI_NbPut_3di4 module procedure ARMCI_NbPut_4di4 module procedure ARMCI_NbPut_5di4 module procedure ARMCI_NbPut_6di4 module procedure ARMCI_NbPut_7di4 module procedure ARMCI_NbPut_1di8 module procedure ARMCI_NbPut_2di8 module procedure ARMCI_NbPut_3di8 module procedure ARMCI_NbPut_4di8 module procedure ARMCI_NbPut_5di8 module procedure ARMCI_NbPut_6di8 module procedure ARMCI_NbPut_7di8 module procedure ARMCI_NbPut_1dr4 module procedure ARMCI_NbPut_2dr4 module procedure ARMCI_NbPut_3dr4 module procedure ARMCI_NbPut_4dr4 module procedure ARMCI_NbPut_5dr4 module procedure ARMCI_NbPut_6dr4 module procedure ARMCI_NbPut_7dr4 module procedure ARMCI_NbPut_1dr8 module procedure ARMCI_NbPut_2dr8 module procedure ARMCI_NbPut_3dr8 module procedure ARMCI_NbPut_4dr8 module procedure ARMCI_NbPut_5dr8 module procedure ARMCI_NbPut_6dr8 module procedure ARMCI_NbPut_7dr8 module procedure ARMCI_NbPut_1dc4 module procedure ARMCI_NbPut_2dc4 module procedure ARMCI_NbPut_3dc4 module procedure ARMCI_NbPut_4dc4 module procedure ARMCI_NbPut_5dc4 module procedure ARMCI_NbPut_6dc4 module procedure ARMCI_NbPut_7dc4 module procedure ARMCI_NbPut_1dc8 module procedure ARMCI_NbPut_2dc8 module procedure ARMCI_NbPut_3dc8 module procedure ARMCI_NbPut_4dc8 module procedure ARMCI_NbPut_5dc8 module procedure ARMCI_NbPut_6dc8 module procedure ARMCI_NbPut_7dc8 end interface interface ARMCI_NbGet_fa module procedure ARMCI_NbGet_1di4 module procedure ARMCI_NbGet_2di4 module procedure ARMCI_NbGet_3di4 module procedure ARMCI_NbGet_4di4 module procedure ARMCI_NbGet_5di4 module procedure ARMCI_NbGet_6di4 module procedure ARMCI_NbGet_7di4 module procedure ARMCI_NbGet_1di8 module procedure ARMCI_NbGet_2di8 module procedure ARMCI_NbGet_3di8 module procedure ARMCI_NbGet_4di8 module procedure ARMCI_NbGet_5di8 module procedure ARMCI_NbGet_6di8 module procedure ARMCI_NbGet_7di8 module procedure ARMCI_NbGet_1dr4 module procedure ARMCI_NbGet_2dr4 module procedure ARMCI_NbGet_3dr4 module procedure ARMCI_NbGet_4dr4 module procedure ARMCI_NbGet_5dr4 module procedure ARMCI_NbGet_6dr4 module procedure ARMCI_NbGet_7dr4 module procedure ARMCI_NbGet_1dr8 module procedure ARMCI_NbGet_2dr8 module procedure ARMCI_NbGet_3dr8 module procedure ARMCI_NbGet_4dr8 module procedure ARMCI_NbGet_5dr8 module procedure ARMCI_NbGet_6dr8 module procedure ARMCI_NbGet_7dr8 module procedure ARMCI_NbGet_1dc4 module procedure ARMCI_NbGet_2dc4 module procedure ARMCI_NbGet_3dc4 module procedure ARMCI_NbGet_4dc4 module procedure ARMCI_NbGet_5dc4 module procedure ARMCI_NbGet_6dc4 module procedure ARMCI_NbGet_7dc4 module procedure ARMCI_NbGet_1dc8 module procedure ARMCI_NbGet_2dc8 module procedure ARMCI_NbGet_3dc8 module procedure ARMCI_NbGet_4dc8 module procedure ARMCI_NbGet_5dc8 module procedure ARMCI_NbGet_6dc8 module procedure ARMCI_NbGet_7dc8 end interface contains subroutine ARMCI_NbPut_1di4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use ARMCI_types integer(kind=I4), dimension(:), pointer :: src, dst type(ARMCI_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface integer(kind=I4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_1di4 subroutine ARMCI_NbPut_2di4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface integer(kind=I4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_2di4 subroutine ARMCI_NbPut_3di4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface integer(kind=I4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_3di4 subroutine ARMCI_NbPut_4di4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface integer(kind=I4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_4di4 subroutine ARMCI_NbPut_5di4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface integer(kind=I4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_5di4 subroutine ARMCI_NbPut_6di4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface integer(kind=I4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_6di4 subroutine ARMCI_NbPut_7di4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface integer(kind=I4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_7di4 subroutine ARMCI_NbPut_1di8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface integer(kind=I8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_1di8 subroutine ARMCI_NbPut_2di8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface integer(kind=I8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_2di8 subroutine ARMCI_NbPut_3di8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface integer(kind=I8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_3di8 subroutine ARMCI_NbPut_4di8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface integer(kind=I8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_4di8 subroutine ARMCI_NbPut_5di8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface integer(kind=I8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_5di8 subroutine ARMCI_NbPut_6di8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface integer(kind=I8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_6di8 subroutine ARMCI_NbPut_7di8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface integer(kind=I8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_7di8 subroutine ARMCI_NbPut_1dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface real(kind=R4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_1dr4 subroutine ARMCI_NbPut_2dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface real(kind=R4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_2dr4 subroutine ARMCI_NbPut_3dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface real(kind=R4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_3dr4 subroutine ARMCI_NbPut_4dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface real(kind=R4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_4dr4 subroutine ARMCI_NbPut_5dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface real(kind=R4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_5dr4 subroutine ARMCI_NbPut_6dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface real(kind=R4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_6dr4 subroutine ARMCI_NbPut_7dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface real(kind=R4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_7dr4 subroutine ARMCI_NbPut_1dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface real(kind=R8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_1dr8 subroutine ARMCI_NbPut_2dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface real(kind=R8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_2dr8 subroutine ARMCI_NbPut_3dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface real(kind=R8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_3dr8 subroutine ARMCI_NbPut_4dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface real(kind=R8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_4dr8 subroutine ARMCI_NbPut_5dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface real(kind=R8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_5dr8 subroutine ARMCI_NbPut_6dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface real(kind=R8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_6dr8 subroutine ARMCI_NbPut_7dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface real(kind=R8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_7dr8 subroutine ARMCI_NbPut_1dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface complex(kind=C4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_1dc4 subroutine ARMCI_NbPut_2dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface complex(kind=C4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_2dc4 subroutine ARMCI_NbPut_3dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface complex(kind=C4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_3dc4 subroutine ARMCI_NbPut_4dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface complex(kind=C4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_4dc4 subroutine ARMCI_NbPut_5dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface complex(kind=C4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_5dc4 subroutine ARMCI_NbPut_6dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface complex(kind=C4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_6dc4 subroutine ARMCI_NbPut_7dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface complex(kind=C4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_7dc4 subroutine ARMCI_NbPut_1dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface complex(kind=C8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_1dc8 subroutine ARMCI_NbPut_2dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface complex(kind=C8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_2dc8 subroutine ARMCI_NbPut_3dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface complex(kind=C8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_3dc8 subroutine ARMCI_NbPut_4dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface complex(kind=C8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_4dc8 subroutine ARMCI_NbPut_5dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface complex(kind=C8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_5dc8 subroutine ARMCI_NbPut_6dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface complex(kind=C8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_6dc8 subroutine ARMCI_NbPut_7dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbPut_farrays end interface complex(kind=C8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call ARMCI_NbPut_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbPut_7dc8 !Get subroutine ARMCI_NbGet_1di4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface integer(kind=I4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_1di4 subroutine ARMCI_NbGet_2di4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface integer(kind=I4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_2di4 subroutine ARMCI_NbGet_3di4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface integer(kind=I4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_3di4 subroutine ARMCI_NbGet_4di4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface integer(kind=I4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_4di4 subroutine ARMCI_NbGet_5di4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface integer(kind=I4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_5di4 subroutine ARMCI_NbGet_6di4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface integer(kind=I4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_6di4 subroutine ARMCI_NbGet_7di4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface integer(kind=I4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_7di4 subroutine ARMCI_NbGet_1di8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface integer(kind=I8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_1di8 subroutine ARMCI_NbGet_2di8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface integer(kind=I8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_2di8 subroutine ARMCI_NbGet_3di8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface integer(kind=I8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_3di8 subroutine ARMCI_NbGet_4di8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface integer(kind=I8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_4di8 subroutine ARMCI_NbGet_5di8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface integer(kind=I8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_5di8 subroutine ARMCI_NbGet_6di8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface integer(kind=I8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_6di8 subroutine ARMCI_NbGet_7di8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types integer(kind=I8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface integer(kind=I8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_7di8 subroutine ARMCI_NbGet_1dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface real(kind=R4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_1dr4 subroutine ARMCI_NbGet_2dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface real(kind=R4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_2dr4 subroutine ARMCI_NbGet_3dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface real(kind=R4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_3dr4 subroutine ARMCI_NbGet_4dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface real(kind=R4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_4dr4 subroutine ARMCI_NbGet_5dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface real(kind=R4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_5dr4 subroutine ARMCI_NbGet_6dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface real(kind=R4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_6dr4 subroutine ARMCI_NbGet_7dr4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface real(kind=R4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_7dr4 subroutine ARMCI_NbGet_1dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface real(kind=R8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_1dr8 subroutine ARMCI_NbGet_2dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface real(kind=R8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_2dr8 subroutine ARMCI_NbGet_3dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface real(kind=R8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_3dr8 subroutine ARMCI_NbGet_4dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface real(kind=R8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_4dr8 subroutine ARMCI_NbGet_5dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface real(kind=R8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_5dr8 subroutine ARMCI_NbGet_6dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface real(kind=R8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_6dr8 subroutine ARMCI_NbGet_7dr8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types real(kind=R8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface real(kind=R8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_7dr8 subroutine ARMCI_NbGet_1dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface complex(kind=C4), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_1dc4 subroutine ARMCI_NbGet_2dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface complex(kind=C4), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_2dc4 subroutine ARMCI_NbGet_3dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface complex(kind=C4), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_3dc4 subroutine ARMCI_NbGet_4dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface complex(kind=C4), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_4dc4 subroutine ARMCI_NbGet_5dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface complex(kind=C4), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_5dc4 subroutine ARMCI_NbGet_6dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface complex(kind=C4), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_6dc4 subroutine ARMCI_NbGet_7dc4(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface complex(kind=C4), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_7dc4 subroutine ARMCI_NbGet_1dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface complex(kind=C8), dimension(:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 1 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_1dc8 subroutine ARMCI_NbGet_2dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface complex(kind=C8), dimension(:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 2 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_2dc8 subroutine ARMCI_NbGet_3dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface complex(kind=C8), dimension(:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 3 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_3dc8 subroutine ARMCI_NbGet_4dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface complex(kind=C8), dimension(:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 4 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_4dc8 subroutine ARMCI_NbGet_5dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface complex(kind=C8), dimension(:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 5 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_5dc8 subroutine ARMCI_NbGet_6dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface complex(kind=C8), dimension(:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 6 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_6dc8 subroutine ARMCI_NbGet_7dc8(src, src_slc, dst, dst_slc, proc, rc) use definekind ! implicit none interface subroutine ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) use definekind use armci_types complex(kind=C8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc, rank integer, intent(out) :: rc end subroutine ARMCI_NbGet_farrays end interface complex(kind=C8), dimension(:,:,:,:,:,:,:), pointer :: src, dst type(armci_slice), intent(in) :: src_slc, dst_slc integer, intent(in) :: proc integer, intent(out) :: rc integer :: rank rank = 7 call ARMCI_NbGet_farrays(src, src_slc, dst, dst_slc, proc, rank, rc) end subroutine ARMCI_NbGet_7dc8 end module ARMCI_NbMov ga-5.9.2/armci/f90/armci_types.f90000066400000000000000000000002311500715745200164500ustar00rootroot00000000000000module armci_types type armci_slice integer :: lo(7) integer :: hi(7) integer :: stride(7) end type armci_slice end module armci_types ga-5.9.2/armci/f90/armcif90.c000066400000000000000000000724741500715745200154110ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "CompilerCharacteristics.h" #include #include #include #include #include #include #define HIDDEN_DESC #if defined(F90_SYM_CASE_LOWER) # define ARMCI_Arr_init F90_SYMBOL( armci_arr_init ) # define ARMCI_Arr_finalize F90_SYMBOL( armci_arr_finalize ) # define ARMCI_Malloc_farray F90_SYMBOL( armci_malloc_farray ) # define ARMCI_Free_farray F90_SYMBOL( armci_free_farray ) # define ARMCI_Put_farrays F90_SYMBOL( armci_put_farrays ) # define ARMCI_NbPut_farrays F90_SYMBOL( armci_nbput_farrays ) # define ARMCI_Get_farrays F90_SYMBOL( armci_get_farrays ) # define ARMCI_NbGet_farrays F90_SYMBOL( armci_nbget_farrays ) # define ARMCI_Sync F90_SYMBOL( armci_sync ) # define ARMCI_Waitall_fa F90_SYMBOL( armci_waitall_fa ) # define ARMCI_Notify_fa F90_SYMBOL( armci_notify_fa ) # define ARMCI_NotifyWait_fa F90_SYMBOL( armci_notifywait_fa ) # define ARMCI_Malloc_fa F90_SYMBOL( armci_malloc_fa ) #endif /* F90_SYM_CASE_LOWER */ #if defined(F90_SYM_CASE_MIXED) # define ARMCI_Arr_init F90_SYMBOL( ARMCI_Arr_init ) # define ARMCI_Arr_finalize F90_SYMBOL( ARMCI_Arr_finalize ) # define ARMCI_Malloc_farray F90_SYMBOL( ARMCI_Malloc_farray ) # define ARMCI_Free_farray F90_SYMBOL( ARMCI_Free_farray ) # define ARMCI_Put_farrays F90_SYMBOL( ARMCI_Put_farrays ) # define ARMCI_NbPut_farrays F90_SYMBOL( ARMCI_NbPut_farrays ) # define ARMCI_Get_farrays F90_SYMBOL( ARMCI_Get_farrays ) # define ARMCI_NbGet_farrays F90_SYMBOL( ARMCI_NbGet_farrays ) # define ARMCI_Sync F90_SYMBOL( ARMCI_Sync ) # define ARMCI_Waitall_fa F90_SYMBOL( ARMCI_Waitall_fa ) # define ARMCI_Notify_fa F90_SYMBOL( ARMCI_Notify_fa ) # define ARMCI_NotifyWait_fa F90_SYMBOL( ARMCI_NotifyWait_fa ) #endif /* F90_SYM_CASE_MIXED */ #ifdef F90_SYM_CASE_UPPER # define ARMCI_Arr_init F90_SYMBOL( ARMCI_ARR_INIT ) # define ARMCI_Arr_finalize F90_SYMBOL( ARMCI_ARR_FINALIZE ) # define ARMCI_Malloc_farray F90_SYMBOL( ARMCI_MALLOC_FARRAY ) # define ARMCI_Free_farray F90_SYMBOL( ARMCI_FREE_FARRAY ) # define ARMCI_Put_farrays F90_SYMBOL( ARMCI_PUT_FARRAYS ) # define ARMCI_NbPut_farrays F90_SYMBOL( ARMCI_NBPUT_FARRAYS ) # define ARMCI_Get_farrays F90_SYMBOL( ARMCI_GET_FARRAYS ) # define ARMCI_NbGet_farrays F90_SYMBOL( ARMCI_NBGET_FARRAYS ) # define ARMCI_Sync F90_SYMBOL( ARMCI_SYNC ) # define ARMCI_Waitall_fa F90_SYMBOL( ARMCI_WAITALL_FA ) # define ARMCI_Notify_fa F90_SYMBOL( ARMCI_NOTIFY_FA ) # define ARMCI_NotifyWait_fa F90_SYMBOL( ARMCI_NOTIFYWAIT_FA ) #endif /* F90_SYM_CASE_UPPER */ #ifdef F90_INTEL # define GET_ADDRESS_FROM_FDSC(dsc) (((void**)(dsc))[2]) #else # define GET_ADDRESS_FROM_FDSC(dsc) (((void**)(dsc))[0]) #endif typedef struct { int lo[MAXDIM]; int hi[MAXDIM]; int stride[MAXDIM]; } array_slice_t; /* allows simple test to assure that allocated and freed addresses the same */ void* g_save_addr; /* structure containing function pointers to chasm lib, must be initialized */ F90_CompilerCharacteristics cc; extern int armci_nproc, armci_me; void ARMCI_Arr_init() { F90_SetCompilerCharacteristics(&cc, FORTRAN_COMPILER); ARMCI_Init(); } void ARMCI_Arr_finalize() { ARMCI_Finalize(); } void ARMCI_Waitall_fa() { ARMCI_WaitAll(); } void ARMCI_Notify_fa(int* proc) { int tempvar; tempvar = armci_notify(*proc); } void ARMCI_NotifyWait_fa(int* proc) { int rc,wc; rc = armci_notify_wait(*proc,&wc); } void armci_abort() { } void armci_abort_() { } void ARMCI_Sync() { ARMCI_Barrier(); } void ARMCI_Terminate() { } void ARMCI_Malloc_farray(void* dv, int* rank, int* elemsize, int lb_in[], int ub_in[], int* rc, void* dv_hidden) { long stride[MAXDIM]; long lo[MAXDIM]; unsigned long extent[MAXDIM]; void* dv_local; armci_arr_dsc_t *ad; int i; _array_create(&g_save_addr,*elemsize, *rank, lb_in, ub_in); if(!g_save_addr)ARMCI_Error("_array_create failed",*rank); /************************ chasm interface ***************************/ /* memory for dope vector */ dv_local = malloc( cc.getArrayDescSize(*rank) ); if(!dv_local) {*rc = -2; return;} ad = GET_ARR_DSC_FROM_ARRAY(g_save_addr); /* *rc = cc.setArrayDesc(dv_local, g_save_addr, *rank, F90_ArrayPointer, F90_Integer, elemsize, ad->lo, ad->extent, ad->stride); */ for(i=0; i< *rank; i++) lo[i] = (long)ad->lo[i]; for(i=0; i< *rank; i++) stride[i] = (long)ad->stride[i]; for(i=0; i< *rank; i++) extent[i] = (unsigned long) ad->extent[i]; *rc = cc.setArrayDesc(dv_local, g_save_addr, *rank, F90_ArrayPointer, F90_Integer, *elemsize, lo, extent, stride); if (*rc) { fprintf(stderr, "ERROR in setting array descriptor\n"); return; } /* copy dope vector to the stack memory */ *rc = cc.copyToArrayDescAndHidden(dv_local, *rank, F90_ArrayPointer, dv, dv_hidden); if (*rc) { fprintf(stderr, "ERROR in copying array descriptor\n"); return; } free(dv_local); } void ARMCI_Free_farray(void* dv, int* rank, int* rc, void* dv_hidden) { F90_CompilerCharacteristics cc; void* dv_local; void* addr; armci_arr_dsc_t *ad; *rc = 0; F90_SetCompilerCharacteristics(&cc, FORTRAN_COMPILER); dv_local = cc.createArrayDesc(dv, dv_hidden, *rank, F90_ArrayPointer); if (dv_local == 0) { fprintf(stderr, "ERROR in createArrayDesc\n"); *rc = -1; return; } addr = cc.getArrayBaseAddress(dv_local, *rank); if (addr == 0) { fprintf(stderr, "ERROR in getArrayBaseAddress rank=%d\n",*rank); *rc = -1; return; } _array_free(addr); } static long Index(int rank, int subscript[], int lo[], int extent[]) { long idx = 0, i, factor=1; for(i=0;ielemsize; ad_src_rank = ad_dst->rank; for (i=0;ilo, ad_src_lo, ad_src_extent)*ad_src_elemsize + (char*) addr_src; ptr_dst= Index(ad_dst->rank, dst_slc->lo, ad_dst->lo, ad_dst->extent)*ad_dst->elemsize + (char*) ad_dst->aptr[*proc]; /* The following attempts to deal with non-unit stride. */ /* Seems like we assume that src_slc->hi[i] >= src_slc->lo[i] and stride[i] > 0 for all i. One might wish to modify that assumption at some point. Should we check that the patches are the same size, does ARMCI_Puts handle that? */ if (ad_dst->rank != ad_src_rank) { fprintf(stderr, "ARMCI_Put_farrays: ERROR ranks of src and dst do not match\n"); *rc = -1; return; } src_first_stride_eq_1 = 1; src_unit_stride = 1; for(i=0; i< ad_src_rank; i++) { if (src_slc->stride[i] > 1) { if (i == 0) { src_first_stride_eq_1 = 0; } src_unit_stride = 0; src_count[i] = (int)((src_slc->hi[i] -src_slc->lo[i]+src_slc->stride[i])/src_slc->stride[i]); } else { src_count[i] = src_slc->hi[i] -src_slc->lo[i]+1; } } dst_first_stride_eq_1 = 1; dst_unit_stride = 1; for(i=0; i< ad_dst->rank; i++) { if (dst_slc->stride[i] > 1) { if (i == 0) { dst_first_stride_eq_1 = 0; } dst_unit_stride = 0; dst_count[i] = (int)((dst_slc->hi[i] -dst_slc->lo[i]+dst_slc->stride[i])/dst_slc->stride[i]); } else { dst_count[i] = dst_slc->hi[i] -dst_slc->lo[i]+1; } } /* Might want to check that src_count and dst_count match here?, perhaps that happens in ARMC_PutS but I think not. */ for (i=0;istride[i+1]; } src_count[0] *= ad_src_elemsize; } else { /* If first dimension strides are both 1, then we can till use ad_src->rank-1 levels. Just the strides need changing. */ if ((src_first_stride_eq_1 != 0) && (dst_first_stride_eq_1 != 0)) { mv_rank = ad_src_rank-1; src_count[0] *= ad_src_elemsize; for (i=0; i< mv_rank; i++) { src_stride[i] = ad_src_stride[i+1]*src_slc->stride[i+1]; } for (i=0; i< mv_rank; i++) { dst_stride[i] = ad_dst->stride[i+1]*dst_slc->stride[i+1]; } } else { mv_rank = ad_src_rank; /* Shift counts, right, set count[0] to elemsize. */ for (i=mv_rank;i>0;i--) { src_count[i] = src_count[i-1]; } src_count[0] = ad_src_elemsize; /* Adjust strides. */ for (i=0;istride[i]; } for (i=0;istride[i]*dst_slc->stride[i]; } } } #if 0 printf(" ARMCI_Put_farrays(src): lo = %d hi = %d stride = %d\n", src_slc->lo[0], src_slc->hi[0], src_slc->stride[0]); printf(" ARMCI_Put_farrays(dst): lo = %d hi = %d stride = %d on proc = %d\n", dst_slc->lo[0], dst_slc->hi[0], dst_slc->stride[0], *proc); #endif /* ARMCI_PutS(ptr_src, ad_src->stride+1, ptr_dst, ad_dst->stride+1, count, ad_src->rank-1, *proc); */ ARMCI_PutS(ptr_src, src_stride, ptr_dst, dst_stride, src_count, mv_rank, *proc); } ARMCI_NbPut_farrays(void* dv_src, array_slice_t* src_slc, void* dv_dst, array_slice_t* dst_slc, int* proc, int* rank, int* rc, void* dv_h_src, void* dv_h_dst) { void *addr_src, *addr_dst, *dv_l_src, *dv_l_dst; armci_arr_dsc_t *ad_dst,*ad_src; void *ptr_src, *ptr_dst; int i; int src_count[MAXDIM+1]; int dst_count[MAXDIM+1]; int src_stride[MAXDIM]; int dst_stride[MAXDIM]; int src_unit_stride, src_first_stride_eq_1; int dst_unit_stride, dst_first_stride_eq_1; int mv_rank; int ad_src_rank; int ad_src_elemsize; int ad_src_lo[MAXDIM]; int ad_src_extent[MAXDIM]; int ad_src_stride[MAXDIM]; *rc = 0; dv_l_src = cc.createArrayDesc(dv_src, dv_h_src, *rank, F90_ArrayPointer); if (dv_l_src == 0) { fprintf(stderr, "nb_ARMCI_Put_farrays: ERROR in createArrayDesc\n"); *rc = -1; return; } addr_src = cc.getArrayBaseAddress(dv_l_src, *rank); if (addr_src == 0) { fprintf(stderr, "%d:nb_ARMCI_Put_farrays: ERROR in getArrayBaseAddress src %d\n",armci_me,*rank); *rc = -1; return; } dv_l_dst = cc.createArrayDesc(dv_dst, dv_h_dst, *rank, F90_ArrayPointer); if (dv_l_dst == 0) { fprintf(stderr, "nb_ARMCI_Put_farrays: ERROR in createArrayDesc\n"); *rc = -1; return; } addr_dst = cc.getArrayBaseAddress(dv_l_dst, *rank); if (addr_dst == 0) { fprintf(stderr, "%d:nb_ARMCI_Put_farrays: ERROR in getArrayBaseAddress dst %d\n",armci_me,*rank); *rc = -1; return; } #if 0 addr_src = g_save_addr; assert(addr_src); addr_dst = g_save_add; assert(addr_dst); #endif /* ad_src = GET_ARR_DSC_FROM_ARRAY(addr_src); Need to replace ad_src_rank, ad_src_lo, ad_src_extent, ad_src_stride, and ad_src_elemsize with local stuff from the chasm interface. No way to get elemsize from chasm stuff, so for now take src_elemsize to be the same as dst_elemsize. */ ad_dst = GET_ARR_DSC_FROM_ARRAY(addr_dst); ad_src_elemsize = ad_dst->elemsize; ad_src_rank = ad_dst->rank; for (i=0;ilo, ad_src_lo, ad_src_extent)*ad_src_elemsize + (char*) addr_src; ptr_dst= Index(ad_dst->rank, dst_slc->lo, ad_dst->lo, ad_dst->extent)*ad_dst->elemsize + (char*) ad_dst->aptr[*proc]; /* The following attempts to deal with non-unit stride. */ /* Seems like we assume that src_slc->hi[i] >= src_slc->lo[i] and stride[i] > 0 for all i. One might wish to modify that assumption at some point. Should we check that the patches are the same size, does ARMCI_Puts handle that? */ if (ad_dst->rank != ad_src_rank) { fprintf(stderr, "nb_ARMCI_Put_farrays: ERROR ranks of src and dst do not match\n"); *rc = -1; return; } src_first_stride_eq_1 = 1; src_unit_stride = 1; for(i=0; i< ad_src_rank; i++) { if (src_slc->stride[i] > 1) { if (i == 0) { src_first_stride_eq_1 = 0; } src_unit_stride = 0; src_count[i] = (int)((src_slc->hi[i] -src_slc->lo[i]+src_slc->stride[i])/src_slc->stride[i]); } else { src_count[i] = src_slc->hi[i] -src_slc->lo[i]+1; } } dst_first_stride_eq_1 = 1; dst_unit_stride = 1; for(i=0; i< ad_dst->rank; i++) { if (dst_slc->stride[i] > 1) { if (i == 0) { dst_first_stride_eq_1 = 0; } dst_unit_stride = 0; dst_count[i] = (int)((dst_slc->hi[i] -dst_slc->lo[i]+dst_slc->stride[i])/dst_slc->stride[i]); } else { dst_count[i] = dst_slc->hi[i] -dst_slc->lo[i]+1; } } /* Might want to check that src_count and dst_count match here?, * perhaps that happens in ARMC_PutS but I think not. * */ for (i=0;istride[i+1]; } src_count[0] *= ad_src_elemsize; } else { /* If first dimension strides are both 1, then we * can till use ad_src->rank-1 levels. Just * the strides need changing. * */ if ((src_first_stride_eq_1 != 0) && (dst_first_stride_eq_1 != 0)) { mv_rank = ad_src_rank-1; src_count[0] *= ad_src_elemsize; for (i=0; i< mv_rank; i++) { src_stride[i] = ad_src_stride[i+1]*src_slc->stride[i+1]; } for (i=0; i< mv_rank; i++) { dst_stride[i] = ad_dst->stride[i+1]*dst_slc->stride[i+1]; } } else { mv_rank = ad_src_rank; /* Shift counts, right, set count[0] to elemsize. */ for (i=mv_rank;i>0;i--) { src_count[i] = src_count[i-1]; } src_count[0] = ad_src_elemsize; /* Adjust strides. */ for (i=0;istride[i]; } for (i=0;istride[i]*dst_slc->stride[i]; } } } #if 0 printf(" nb_ARMCI_Put_farrays(src): lo = %d hi = %d stride = %d\n", src_slc->lo[0], src_slc->hi[0], src_slc->stride[0]); printf(" nb_ARMCI_Put_farrays(dst): lo = %d hi = %d stride = %d on proc = %d\n", dst_slc->lo[0], dst_slc->hi[0], dst_slc->stride[0], *proc); #endif /* * ARMCI_PutS(ptr_src, ad_src->stride+1, ptr_dst, ad_dst->stride+1, count, ad_src->rank-1, *proc); * */ ARMCI_NbPutS(ptr_src, src_stride, ptr_dst, dst_stride, src_count, mv_rank, *proc,NULL); } void ARMCI_Get_farrays(void* dv_src, array_slice_t* src_slc, void* dv_dst, array_slice_t* dst_slc, int* proc, int* rank, int* rc, void* dv_h_src, void* dv_h_dst) { void *addr_src, *addr_dst, *dv_l_src, *dv_l_dst; armci_arr_dsc_t *ad_src, *ad_dst; void *ptr_src, *ptr_dst; int i; int src_count[MAXDIM+1]; int dst_count[MAXDIM+1]; int src_stride[MAXDIM]; int dst_stride[MAXDIM]; int src_unit_stride, src_first_stride_eq_1; int dst_unit_stride, dst_first_stride_eq_1; int mv_rank; int ad_dst_rank; int ad_dst_elemsize; int ad_dst_lo[MAXDIM]; int ad_dst_extent[MAXDIM]; int ad_dst_stride[MAXDIM]; *rc = 0; #ifdef HIDDEN_DESC dv_l_src = cc.createArrayDesc(dv_src, dv_h_src, *rank, F90_ArrayPointer); #else dv_l_src = dv_src; #endif if (dv_l_src == 0) { fprintf(stderr, "ARMCI_Get_farrays: ERROR in createArrayDesc\n"); *rc = -1; return; } addr_src = cc.getArrayBaseAddress(dv_l_src, *rank); if (addr_src == 0) { fprintf(stderr, "ARMCI_Get_farrays: ERROR in getArrayBaseAddress\n"); *rc = -1; return; } #ifdef HIDDEN_DESC dv_l_dst = cc.createArrayDesc(dv_dst, dv_h_dst, *rank, F90_ArrayPointer); #else dv_l_dst = dv_dst; #endif if (dv_l_dst == 0) { fprintf(stderr, "ARMCI_Get_farrays: ERROR in createArrayDesc\n"); *rc = -1; return; } addr_dst = cc.getArrayBaseAddress(dv_l_dst, *rank); if (addr_dst == 0) { fprintf(stderr, "ARMCI_Get_farrays: ERROR in getArrayBaseAddress\n"); *rc = -1; return; } #if 0 addr_src = g_save_addr; assert(addr_src); addr_dst = g_save_add; assert(addr_dst); #endif ad_src = GET_ARR_DSC_FROM_ARRAY(addr_src); /* ad_dst = GET_ARR_DSC_FROM_ARRAY(addr_dst); */ ad_dst_elemsize = ad_src->elemsize; ad_dst_rank = ad_src->rank; for (i=0;irank, src_slc->lo, ad_src->lo, ad_src->extent)*ad_src->elemsize + (char*) ad_src->aptr[*proc]; ptr_dst= Index(ad_dst_rank, dst_slc->lo, ad_dst_lo, ad_dst_extent)*ad_dst_elemsize + (char*) addr_dst; /* The following attempts to deal with non-unit stride. */ /* Seems like we assume that src_slc->hi[i] >= src_slc->lo[i] and stride[i] > 0 for all i. One might wish to modify that assumption at some point. Should we check that the patches are the same size, does ARMCI_Gets handle that? */ if (ad_dst_rank != ad_src->rank) { fprintf(stderr, "ARMCI_Get_farrays: ERROR ranks of src and dst do not match\n"); *rc = -1; return; } src_first_stride_eq_1 = 1; src_unit_stride = 1; for(i=0; i< ad_src->rank; i++) { if (src_slc->stride[i] > 1) { if (i == 0) { src_first_stride_eq_1 = 0; } src_unit_stride = 0; src_count[i] = (int)((src_slc->hi[i] -src_slc->lo[i]+src_slc->stride[i])/src_slc->stride[i]); } else { src_count[i] = src_slc->hi[i] -src_slc->lo[i]+1; } } dst_first_stride_eq_1 = 1; dst_unit_stride = 1; for(i=0; i< ad_dst_rank; i++) { if (dst_slc->stride[i] > 1) { if (i == 0) { dst_first_stride_eq_1 = 0; } dst_unit_stride = 0; dst_count[i] = (int)((dst_slc->hi[i] -dst_slc->lo[i]+dst_slc->stride[i])/dst_slc->stride[i]); } else { dst_count[i] = dst_slc->hi[i] -dst_slc->lo[i]+1; } } /* Might want to check that src_count and dst_count match here?, perhaps that happens in ARMC_PutS but I think not. */ for (i=0;irank;i++) { if (dst_count[i] != src_count[i]) { fprintf(stderr, "ARMCI_Get_farrays: ERROR counts of src and dst slices do not match\n"); *rc = -1; return; } } if ((src_unit_stride != 0) && (dst_unit_stride != 0)) { mv_rank = ad_src->rank-1; for (i=0; istride[i+1]; } for (i=0; ielemsize; } else { /* If first dimension strides are both 1, then we can till use ad_src->rank-1 levels. Just the strides need changing. */ if ((src_first_stride_eq_1 != 0) && (dst_first_stride_eq_1 != 0)) { mv_rank = ad_src->rank-1; src_count[0] *= ad_src->elemsize; for (i=0; i< mv_rank; i++) { src_stride[i] = ad_src->stride[i+1]*src_slc->stride[i+1]; } for (i=0; i< mv_rank; i++) { dst_stride[i] = ad_dst_stride[i+1]*dst_slc->stride[i+1]; } } else { mv_rank = ad_src->rank; /* Shift counts, right, set count[0] to elemsize. */ for (i=mv_rank;i>0;i--) { src_count[i] = src_count[i-1]; } src_count[0] = ad_src->elemsize; /* Adjust strides. */ for (i=0;istride[i]*src_slc->stride[i]; } for (i=0;istride[i]; } } } #if 0 printf(" ARMCI_Get_farrays(src): lo = %d hi = %d stride = %d\n", src_slc->lo[0], src_slc->hi[0], src_slc->stride[0]); printf(" ARMCI_Get_farrays(dst): lo = %d hi = %d stride = %d on proc = %d\n", dst_slc->lo[0], dst_slc->hi[0], dst_slc->stride[0], *proc); #endif /* ARMCI_GetS(ptr_src, ad_src->stride+1, ptr_dst, ad_dst->stride+1, count, ad_src->rank-1, *proc); */ ARMCI_GetS(ptr_src, src_stride, ptr_dst, dst_stride, src_count, mv_rank, *proc); } void ARMCI_NbGet_farrays(void* dv_src, array_slice_t* src_slc, void* dv_dst, array_slice_t* dst_slc, int* proc, int* rank, int* rc, void* dv_h_src, void* dv_h_dst) { void *addr_src, *addr_dst, *dv_l_src, *dv_l_dst; armci_arr_dsc_t *ad_src, *ad_dst; void *ptr_src, *ptr_dst; int i; int src_count[MAXDIM+1]; int dst_count[MAXDIM+1]; int src_stride[MAXDIM]; int dst_stride[MAXDIM]; int src_unit_stride, src_first_stride_eq_1; int dst_unit_stride, dst_first_stride_eq_1; int mv_rank; int ad_dst_rank; int ad_dst_elemsize; int ad_dst_lo[MAXDIM]; int ad_dst_extent[MAXDIM]; int ad_dst_stride[MAXDIM]; *rc = 0; dv_l_src = cc.createArrayDesc(dv_src, dv_h_src, *rank, F90_ArrayPointer); if (dv_l_src == 0) { fprintf(stderr, "ARMCI_Get_farrays: ERROR in createArrayDesc\n"); *rc = -1; return; } addr_src = cc.getArrayBaseAddress(dv_l_src, *rank); if (addr_src == 0) { fprintf(stderr, "ARMCI_Get_farrays: ERROR in getArrayBaseAddress\n"); *rc = -1; return; } #if 0 addr_src = g_save_addr; assert(addr_src); addr_dst = g_save_add; assert(addr_dst); #endif ad_src = GET_ARR_DSC_FROM_ARRAY(addr_src); /* * ad_dst = GET_ARR_DSC_FROM_ARRAY(addr_dst); * */ ad_dst_elemsize = ad_src->elemsize; ad_dst_rank = ad_src->rank; for (i=0;irank, src_slc->lo, ad_src->lo, ad_src->extent)*ad_src->elemsize + (char*) ad_src->aptr[*proc]; ptr_dst= Index(ad_dst_rank, dst_slc->lo, ad_dst_lo, ad_dst_extent)*ad_dst_elemsize + (char*) addr_dst; /* The following attempts to deal with non-unit stride. */ /* Seems like we assume that src_slc->hi[i] >= src_slc->lo[i] and * stride[i] > 0 for all i. One might wish to modify that * assumption at some point. Should we check that the patches * are the same size, does ARMCI_Gets handle that? * */ if (ad_dst_rank != ad_src->rank) { fprintf(stderr, "ARMCI_Get_farrays: ERROR ranks of src and dst do not match\n"); *rc = -1; return; } src_first_stride_eq_1 = 1; src_unit_stride = 1; for(i=0; i< ad_src->rank; i++) { if (src_slc->stride[i] > 1) { if (i == 0) { src_first_stride_eq_1 = 0; } src_unit_stride = 0; src_count[i] = (int)((src_slc->hi[i] -src_slc->lo[i]+src_slc->stride[i])/src_slc->stride[i]); } else { src_count[i] = src_slc->hi[i] -src_slc->lo[i]+1; } } dst_first_stride_eq_1 = 1; dst_unit_stride = 1; for(i=0; i< ad_dst_rank; i++) { if (dst_slc->stride[i] > 1) { if (i == 0) { dst_first_stride_eq_1 = 0; } dst_unit_stride = 0; dst_count[i] = (int)((dst_slc->hi[i] -dst_slc->lo[i]+dst_slc->stride[i])/dst_slc->stride[i]); } else { dst_count[i] = dst_slc->hi[i] -dst_slc->lo[i]+1; } } /* Might want to check that src_count and dst_count match here?, * perhaps that happens in ARMC_PutS but I think not. * */ for (i=0;irank;i++) { if (dst_count[i] != src_count[i]) { fprintf(stderr, "ARMCI_Get_farrays: ERROR counts of src and dst slices do not match\n"); *rc = -1; return; } } if ((src_unit_stride != 0) && (dst_unit_stride != 0)) { mv_rank = ad_src->rank-1; for (i=0; istride[i+1]; } for (i=0; ielemsize; } else { /* If first dimension strides are both 1, then we can till use ad_src->rank-1 levels. Just the strides need changing. */ if ((src_first_stride_eq_1 != 0) && (dst_first_stride_eq_1 != 0)) { mv_rank = ad_src->rank-1; src_count[0] *= ad_src->elemsize; for (i=0; i< mv_rank; i++) { src_stride[i] = ad_src->stride[i+1]*src_slc->stride[i+1]; } for (i=0; i< mv_rank; i++) { dst_stride[i] = ad_dst_stride[i+1]*dst_slc->stride[i+1]; } } else { mv_rank = ad_src->rank; /* Shift counts, right, set count[0] to elemsize. */ for (i=mv_rank;i>0;i--) { src_count[i] = src_count[i-1]; } src_count[0] = ad_src->elemsize; /* Adjust strides. */ for (i=0;istride[i]*src_slc->stride[i]; } for (i=0;istride[i]; } } } #if 0 printf(" ARMCI_Get_farrays(src): lo = %d hi = %d stride = %d\n", src_slc->lo[0], src_slc->hi[0], src_slc->stride[0]); printf(" ARMCI_Get_farrays(dst): lo = %d hi = %d stride = %d on proc = %d\n", dst_slc->lo[0], dst_slc->hi[0], dst_slc->stride[0], *proc); #endif /* * ARMCI_GetS(ptr_src, ad_src->stride+1, ptr_dst, ad_dst->stride+1, count, ad_src->rank-1, *proc); */ ARMCI_NbGetS(ptr_src, src_stride, ptr_dst, dst_stride, src_count, mv_rank, *proc,NULL); } ga-5.9.2/armci/f90/arraydesc.h000066400000000000000000000011361500715745200157440ustar00rootroot00000000000000#ifndef __ARMCIARR__ #define __ARMCIARR__ #define MAXDIM 7 typedef struct { int rank; int elemsize; int extent[MAXDIM]; int lo[MAXDIM]; int hi[MAXDIM]; int stride[MAXDIM]; void **aptr; void *amem; }armci_arr_dsc_t; #define HEADER_SIZE 256 #define GET_PTR_ARR_FROM_ARRAY(ptr) (((char*)ptr)-HEADER_SIZE -armci_nproc*sizeof(void*)) #define GET_ARR_DSC_FROM_ARRAY(ptr) (armci_arr_dsc_t*)(((char*)ptr)-HEADER_SIZE) typedef int index_size_t; extern void _array_create(void **pptr, int elemsize, int rank, index_size_t *lb, index_size_t *ub); extern void _array_free(void *ptr); #endif ga-5.9.2/armci/f90/arraywrap.c000066400000000000000000000060641500715745200157770ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /******************************************************************************************************* * This is a language (python,f90) independent layer of array management operations * Oerations defined here are called from language-specific layer to allocate & free memory * *******************************************************************************************************/ #include #include #include #include #include #include extern int armci_nproc, armci_me; /*\ allocates memory for array according to rank an lower/upper bounds * pptr contains address to user visible array data * lb can be specified as NULL - in that case ub contains shape \*/ void _array_create(void **pptr, int elemsize, int rank, index_size_t *lb, index_size_t *ub) { void **tmp; char *hptr; armci_arr_dsc_t *ad; void *gtemp; int* ia, i; int bytes=0, elems=1, apsize; *pptr = (void*)0; if(rank <1 || rank > MAXDIM) {fprintf(stderr,"%d: bad rank %d\n",armci_me,rank); return;} if(lb) for(i=0; i< rank; i++) elems *= 1 + ub[i] - lb[i]; else for(i=0; i< rank; i++) elems *= ub[i]; /* allocate pointer vector */ apsize = armci_nproc *sizeof(void*); tmp = (void*) malloc(apsize); if(!tmp) {fprintf(stderr,"%: malloc failed\n",armci_me); return;} bytes += apsize; /* allocate header */ bytes += HEADER_SIZE; /* allocate array memory */ bytes += elemsize * elems; if( ARMCI_Malloc(tmp,bytes)) { fprintf(stderr,"%: armci_malloc failed\n",armci_me); return;} /* set up the header */ hptr = ((char*)tmp[armci_me])+apsize; gtemp = ((char*)hptr)+HEADER_SIZE; #if DEBUG_ printf("%d: array memory %p tmp=%p\n", armci_me, gtemp,tmp[armci_me]); fflush(stdout); #endif /* store array descriptor */ ad = (armci_arr_dsc_t*) hptr; /* */ ad->aptr = tmp[armci_me]; /* store ptr to array of ptrs */ ad->elemsize = elemsize; ad->amem = gtemp; ad->rank = rank; if(lb){ for(i=0; i< rank; i++) ad->lo[i] = lb[i]; for(i=0; i< rank; i++) ad->hi[i] = ub[i]; for(i=0; i< rank; i++) ad->extent[i] = 1 + ub[i] - lb[i]; }else { for(i=0; i< rank; i++) ad->lo[i] = 0; /* C default for array lower bound */ for(i=0; i< rank; i++) ad->hi[i] = ub[i]-1; /* C default for array upper bound */ for(i=0; i< rank; i++) ad->extent[i] = ub[i]; } ad->stride[0]=elemsize; for(i=1; i< rank; i++) ad->stride[i] = ad->stride[i-1]*ad->extent[i-1]; /* adjust ptr array to point to the user f90 array rather than allocated memory */ for(i = 0; iaptr,apsize); *pptr = gtemp; free(tmp); } /*\ deallocates array corresponding to ptr \*/ void _array_free(void *ptr) { armci_arr_dsc_t *ad; ad = GET_ARR_DSC_FROM_ARRAY(ptr); if(ptr != ad->amem)ARMCI_Error("free - bad memory\n",0); ARMCI_Free(ad->aptr); #if DEBUG_ printf("%d: freed %p\n",armci_me, addr); fflush(stdout); #endif } ga-5.9.2/armci/f90/checkput.f90000066400000000000000000000574071500715745200157600ustar00rootroot00000000000000module checkput use definekind use armci_types interface check_b module procedure check_b_i4 module procedure check_b_i8 module procedure check_b_r4 module procedure check_b_r8 module procedure check_b_c4 module procedure check_b_c8 end interface contains subroutine check_b_i4(rank,b,dst_slice,src_slice,remote_proc,dlb,dub, & & slb,sub,joff,pass) implicit none integer rank integer(kind=i4), pointer :: b(:) type(armci_slice) :: dst_slice, src_slice integer remote_proc integer dlb(7),dub(7),slb(7),sub(7) integer joff integer pass integer me integer index integer dextent(7) integer sextent(7) integer dst_stride(7) integer src_stride(7) integer src_off(7),dst_off(7) integer src_lo(7),src_hi(7) integer dst_lo(7),dst_hi(7) integer i,m,i1,i2,i3,k4,i5,i6,i7 integer j1s,j2s,j3s,j4s,j5s,j6s,j7s integer j1d,j2d,j3d,j4d,j5d,j6d,j7d integer factor integer myunit integer jbase ! me = nodeid() ! myunit = 10+me ! write(myunit,*)me,'check_b_i4 rank = ',rank ! write(myunit,*)me,'check_b_i4 dst_slice = ',dst_slice ! write(myunit,*)me,'check_b_i4 src_slice = ',src_slice ! write(myunit,*)me,'check_b_i4 remote_proc = ',remote_proc ! write(myunit,*)me,'check_b_i4 dlb = ',dlb ! write(myunit,*)me,'check_b_i4 dub = ',dub ! write(myunit,*)me,'check_b_i4 slb = ',slb ! write(myunit,*)me,'check_b_i4 sub = ',sub ! write(myunit,*)me,'check_b_i4 joff = ',joff dst_lo(:) = 1 dst_hi(:) = 1 dst_stride(:) = 1 do m = 1,rank dst_lo(m) = dst_slice%lo(m) enddo do m = 1,rank dst_hi(m) = dst_slice%hi(m) enddo do m = 1,rank dst_stride(m) = dst_slice%stride(m) enddo src_lo(:) = 1 src_hi(:) = 1 src_stride(:) = 1 do m = 1,rank src_lo(m) = src_slice%lo(m) enddo do m = 1,rank src_hi(m) = src_slice%hi(m) enddo do m = 1,rank src_stride(m) = src_slice%stride(m) enddo do m = 1,rank sextent(m) = sub(m) - slb(m) + 1 enddo do m = 1,rank dextent(m) = dub(m) - dlb(m) + 1 enddo do m = rank+1,7 dextent(m) = 1 enddo do m = rank+1,7 sextent(m) = 1 enddo src_off(1) = 1 do m = 2,rank src_off(m) = src_off(m-1)*sextent(m-1) enddo dst_off(1) = 1 do m = 2,rank dst_off(m) = dst_off(m-1)*dextent(m-1) enddo do m = 1,rank src_off(m) = src_off(m) * src_slice%stride(m) enddo do m = 1,rank dst_off(m) = dst_off(m) * dst_slice%stride(m) enddo do m= rank+1,7 dst_off(m) = 0 enddo do m= rank+1,7 src_off(m) = 0 enddo j7d = 1 factor = 1 do m = 1,rank j7d = j7d + (dst_lo(m) - dlb(m))*factor factor = factor * dextent(1) enddo j7s = 1 factor = 1 do m = 1,rank j7s = j7s + (src_lo(m) - slb(m))*factor factor = factor * sextent(1) enddo ! write(myunit,*)me,'check_b_i4 j7d,j7s = ',j7d,j7s pass = 1 index = 0 jbase = joff -1 do i7 = dst_lo(7),dst_hi(7),dst_stride(7) j6d = j7d j6s = j7s do i6 = dst_lo(6),dst_hi(6),dst_stride(6) j5d = j6d j5s = j6s do i5 = dst_lo(5),dst_hi(5),dst_stride(5) j4d = j5d j4s = j5s do k4 = dst_lo(4),dst_hi(4),dst_stride(4) j3d = j4d j3s = j4s do i3 = dst_lo(3),dst_hi(3),dst_stride(3) j2d = j3d j2s = j3s do i2 = dst_lo(2),dst_hi(2),dst_stride(2) j1d = j2d j1s = j2s do i1 = dst_lo(1),dst_hi(1),dst_stride(1) if (b(j1d) .ne. jbase + j1s) then ! write(myunit,*)'b(',j1d,') = ',b(j1d),' jbase + j1s = ',jbase+j1s pass = 0 index = j1d endif j1d = j1d + dst_off(1) j1s = j1s + src_off(1) enddo j2d = j2d + dst_off(2) j2s = j2s + src_off(2) enddo j3d = j3d + dst_off(3) j3s = j3s + src_off(3) enddo j4d = j4d + dst_off(4) j4s = j4s + src_off(4) enddo j5d = j5d + dst_off(5) j5s = j5s + src_off(5) enddo j6d = j6d + dst_off(6) j6s = j6s + src_off(6) enddo j7d = j7d + dst_off(7) j7s = j7s + src_off(7) enddo return end subroutine check_b_i4 subroutine check_b_i8(rank,b,dst_slice,src_slice,remote_proc,dlb,dub, & & slb,sub,joff,pass) implicit none integer rank integer(kind=i8), pointer :: b(:) type(armci_slice) :: dst_slice, src_slice integer remote_proc integer dlb(7),dub(7),slb(7),sub(7) integer joff integer pass integer me integer index integer dextent(7) integer sextent(7) integer dst_stride(7) integer src_stride(7) integer src_off(7),dst_off(7) integer src_lo(7),src_hi(7) integer dst_lo(7),dst_hi(7) integer i,m,i1,i2,i3,k4,i5,i6,i7 integer(kind=i8) :: j1s,j2s,j3s,j4s,j5s,j6s,j7s integer(kind=i8) :: j1d,j2d,j3d,j4d,j5d,j6d,j7d integer factor integer myunit integer(kind=i8) :: jbase dst_lo(:) = 1 dst_hi(:) = 1 dst_stride(:) = 1 do m = 1,rank dst_lo(m) = dst_slice%lo(m) enddo do m = 1,rank dst_hi(m) = dst_slice%hi(m) enddo do m = 1,rank dst_stride(m) = dst_slice%stride(m) enddo src_lo(:) = 1 src_hi(:) = 1 src_stride(:) = 1 do m = 1,rank src_lo(m) = src_slice%lo(m) enddo do m = 1,rank src_hi(m) = src_slice%hi(m) enddo do m = 1,rank src_stride(m) = src_slice%stride(m) enddo do m = 1,rank sextent(m) = sub(m) - slb(m) + 1 enddo do m = 1,rank dextent(m) = dub(m) - dlb(m) + 1 enddo do m = rank+1,7 dextent(m) = 1 enddo do m = rank+1,7 sextent(m) = 1 enddo src_off(1) = 1 do m = 2,rank src_off(m) = src_off(m-1)*sextent(m-1) enddo dst_off(1) = 1 do m = 2,rank dst_off(m) = dst_off(m-1)*dextent(m-1) enddo do m = 1,rank src_off(m) = src_off(m) * src_slice%stride(m) enddo do m = 1,rank dst_off(m) = dst_off(m) * dst_slice%stride(m) enddo do m= rank+1,7 dst_off(m) = 0 enddo do m= rank+1,7 src_off(m) = 0 enddo j7d = 1 factor = 1 do m = 1,rank j7d = j7d + (dst_lo(m) - dlb(m))*factor factor = factor * dextent(1) enddo j7s = 1 factor = 1 do m = 1,rank j7s = j7s + (src_lo(m) - slb(m))*factor factor = factor * sextent(1) enddo pass = 1 index = 0 jbase = joff -1 do i7 = dst_lo(7),dst_hi(7),dst_stride(7) j6d = j7d j6s = j7s do i6 = dst_lo(6),dst_hi(6),dst_stride(6) j5d = j6d j5s = j6s do i5 = dst_lo(5),dst_hi(5),dst_stride(5) j4d = j5d j4s = j5s do k4 = dst_lo(4),dst_hi(4),dst_stride(4) j3d = j4d j3s = j4s do i3 = dst_lo(3),dst_hi(3),dst_stride(3) j2d = j3d j2s = j3s do i2 = dst_lo(2),dst_hi(2),dst_stride(2) j1d = j2d j1s = j2s do i1 = dst_lo(1),dst_hi(1),dst_stride(1) if (b(j1d) .ne. jbase + j1s) then print *,'checkb_i8 fail: j1d,b(j1d),jbase,j1s,jbase+j1s = ',& & j1d,b(j1d),jbase,j1s,jbase+j1s stop pass = 0 index = j1d endif j1d = j1d + dst_off(1) j1s = j1s + src_off(1) enddo j2d = j2d + dst_off(2) j2s = j2s + src_off(2) enddo j3d = j3d + dst_off(3) j3s = j3s + src_off(3) enddo j4d = j4d + dst_off(4) j4s = j4s + src_off(4) enddo j5d = j5d + dst_off(5) j5s = j5s + src_off(5) enddo j6d = j6d + dst_off(6) j6s = j6s + src_off(6) enddo j7d = j7d + dst_off(7) j7s = j7s + src_off(7) enddo return end subroutine check_b_i8 subroutine check_b_r4(rank,b,dst_slice,src_slice,remote_proc,dlb,dub, & & slb,sub,joff,pass) implicit none integer rank real(kind=r4), pointer :: b(:) type(armci_slice) :: dst_slice, src_slice integer remote_proc integer dlb(7),dub(7),slb(7),sub(7) integer joff integer pass integer me integer index integer dextent(7) integer sextent(7) integer dst_stride(7) integer src_stride(7) integer src_off(7),dst_off(7) integer src_lo(7),src_hi(7) integer dst_lo(7),dst_hi(7) integer i,m,i1,i2,i3,k4,i5,i6,i7 integer j1s,j2s,j3s,j4s,j5s,j6s,j7s integer j1d,j2d,j3d,j4d,j5d,j6d,j7d integer factor integer myunit integer jbase dst_lo(:) = 1 dst_hi(:) = 1 dst_stride(:) = 1 do m = 1,rank dst_lo(m) = dst_slice%lo(m) enddo do m = 1,rank dst_hi(m) = dst_slice%hi(m) enddo do m = 1,rank dst_stride(m) = dst_slice%stride(m) enddo src_lo(:) = 1 src_hi(:) = 1 src_stride(:) = 1 do m = 1,rank src_lo(m) = src_slice%lo(m) enddo do m = 1,rank src_hi(m) = src_slice%hi(m) enddo do m = 1,rank src_stride(m) = src_slice%stride(m) enddo do m = 1,rank sextent(m) = sub(m) - slb(m) + 1 enddo do m = 1,rank dextent(m) = dub(m) - dlb(m) + 1 enddo do m = rank+1,7 dextent(m) = 1 enddo do m = rank+1,7 sextent(m) = 1 enddo src_off(1) = 1 do m = 2,rank src_off(m) = src_off(m-1)*sextent(m-1) enddo dst_off(1) = 1 do m = 2,rank dst_off(m) = dst_off(m-1)*dextent(m-1) enddo do m = 1,rank src_off(m) = src_off(m) * src_slice%stride(m) enddo do m = 1,rank dst_off(m) = dst_off(m) * dst_slice%stride(m) enddo do m= rank+1,7 dst_off(m) = 0 enddo do m= rank+1,7 src_off(m) = 0 enddo j7d = 1 factor = 1 do m = 1,rank j7d = j7d + (dst_lo(m) - dlb(m))*factor factor = factor * dextent(1) enddo j7s = 1 factor = 1 do m = 1,rank j7s = j7s + (src_lo(m) - slb(m))*factor factor = factor * sextent(1) enddo pass = 1 index = 0 jbase = joff -1 do i7 = dst_lo(7),dst_hi(7),dst_stride(7) j6d = j7d j6s = j7s do i6 = dst_lo(6),dst_hi(6),dst_stride(6) j5d = j6d j5s = j6s do i5 = dst_lo(5),dst_hi(5),dst_stride(5) j4d = j5d j4s = j5s do k4 = dst_lo(4),dst_hi(4),dst_stride(4) j3d = j4d j3s = j4s do i3 = dst_lo(3),dst_hi(3),dst_stride(3) j2d = j3d j2s = j3s do i2 = dst_lo(2),dst_hi(2),dst_stride(2) j1d = j2d j1s = j2s do i1 = dst_lo(1),dst_hi(1),dst_stride(1) if (b(j1d) .ne. float(jbase + j1s)) then pass = 0 index = j1d endif j1d = j1d + dst_off(1) j1s = j1s + src_off(1) enddo j2d = j2d + dst_off(2) j2s = j2s + src_off(2) enddo j3d = j3d + dst_off(3) j3s = j3s + src_off(3) enddo j4d = j4d + dst_off(4) j4s = j4s + src_off(4) enddo j5d = j5d + dst_off(5) j5s = j5s + src_off(5) enddo j6d = j6d + dst_off(6) j6s = j6s + src_off(6) enddo j7d = j7d + dst_off(7) j7s = j7s + src_off(7) enddo return end subroutine check_b_r4 subroutine check_b_r8(rank,b,dst_slice,src_slice,remote_proc,dlb,dub, & & slb,sub,joff,pass) implicit none integer rank real(kind=r8), pointer :: b(:) type(armci_slice) :: dst_slice, src_slice integer remote_proc integer dlb(7),dub(7),slb(7),sub(7) integer joff integer pass integer me integer index integer dextent(7) integer sextent(7) integer dst_stride(7) integer src_stride(7) integer src_off(7),dst_off(7) integer src_lo(7),src_hi(7) integer dst_lo(7),dst_hi(7) integer i,m,i1,i2,i3,k4,i5,i6,i7 integer j1s,j2s,j3s,j4s,j5s,j6s,j7s integer j1d,j2d,j3d,j4d,j5d,j6d,j7d integer factor integer myunit integer jbase dst_lo(:) = 1 dst_hi(:) = 1 dst_stride(:) = 1 do m = 1,rank dst_lo(m) = dst_slice%lo(m) enddo do m = 1,rank dst_hi(m) = dst_slice%hi(m) enddo do m = 1,rank dst_stride(m) = dst_slice%stride(m) enddo src_lo(:) = 1 src_hi(:) = 1 src_stride(:) = 1 do m = 1,rank src_lo(m) = src_slice%lo(m) enddo do m = 1,rank src_hi(m) = src_slice%hi(m) enddo do m = 1,rank src_stride(m) = src_slice%stride(m) enddo do m = 1,rank sextent(m) = sub(m) - slb(m) + 1 enddo do m = 1,rank dextent(m) = dub(m) - dlb(m) + 1 enddo do m = rank+1,7 dextent(m) = 1 enddo do m = rank+1,7 sextent(m) = 1 enddo src_off(1) = 1 do m = 2,rank src_off(m) = src_off(m-1)*sextent(m-1) enddo dst_off(1) = 1 do m = 2,rank dst_off(m) = dst_off(m-1)*dextent(m-1) enddo do m = 1,rank src_off(m) = src_off(m) * src_slice%stride(m) enddo do m = 1,rank dst_off(m) = dst_off(m) * dst_slice%stride(m) enddo do m= rank+1,7 dst_off(m) = 0 enddo do m= rank+1,7 src_off(m) = 0 enddo j7d = 1 factor = 1 do m = 1,rank j7d = j7d + (dst_lo(m) - dlb(m))*factor factor = factor * dextent(1) enddo j7s = 1 factor = 1 do m = 1,rank j7s = j7s + (src_lo(m) - slb(m))*factor factor = factor * sextent(1) enddo pass = 1 index = 0 jbase = joff -1 do i7 = dst_lo(7),dst_hi(7),dst_stride(7) j6d = j7d j6s = j7s do i6 = dst_lo(6),dst_hi(6),dst_stride(6) j5d = j6d j5s = j6s do i5 = dst_lo(5),dst_hi(5),dst_stride(5) j4d = j5d j4s = j5s do k4 = dst_lo(4),dst_hi(4),dst_stride(4) j3d = j4d j3s = j4s do i3 = dst_lo(3),dst_hi(3),dst_stride(3) j2d = j3d j2s = j3s do i2 = dst_lo(2),dst_hi(2),dst_stride(2) j1d = j2d j1s = j2s do i1 = dst_lo(1),dst_hi(1),dst_stride(1) if (b(j1d) .ne. dble(jbase + j1s)) then pass = 0 index = j1d endif j1d = j1d + dst_off(1) j1s = j1s + src_off(1) enddo j2d = j2d + dst_off(2) j2s = j2s + src_off(2) enddo j3d = j3d + dst_off(3) j3s = j3s + src_off(3) enddo j4d = j4d + dst_off(4) j4s = j4s + src_off(4) enddo j5d = j5d + dst_off(5) j5s = j5s + src_off(5) enddo j6d = j6d + dst_off(6) j6s = j6s + src_off(6) enddo j7d = j7d + dst_off(7) j7s = j7s + src_off(7) enddo return end subroutine check_b_r8 subroutine check_b_c4(rank,b,dst_slice,src_slice,remote_proc,dlb,dub, & & slb,sub,joff,pass) implicit none integer rank complex(kind=c4), pointer :: b(:) type(armci_slice) :: dst_slice, src_slice integer remote_proc integer dlb(7),dub(7),slb(7),sub(7) integer joff integer pass integer me integer index integer dextent(7) integer sextent(7) integer dst_stride(7) integer src_stride(7) integer src_off(7),dst_off(7) integer src_lo(7),src_hi(7) integer dst_lo(7),dst_hi(7) integer i,m,i1,i2,i3,k4,i5,i6,i7 integer j1s,j2s,j3s,j4s,j5s,j6s,j7s integer j1d,j2d,j3d,j4d,j5d,j6d,j7d integer factor integer myunit integer jbase dst_lo(:) = 1 dst_hi(:) = 1 dst_stride(:) = 1 do m = 1,rank dst_lo(m) = dst_slice%lo(m) enddo do m = 1,rank dst_hi(m) = dst_slice%hi(m) enddo do m = 1,rank dst_stride(m) = dst_slice%stride(m) enddo src_lo(:) = 1 src_hi(:) = 1 src_stride(:) = 1 do m = 1,rank src_lo(m) = src_slice%lo(m) enddo do m = 1,rank src_hi(m) = src_slice%hi(m) enddo do m = 1,rank src_stride(m) = src_slice%stride(m) enddo do m = 1,rank sextent(m) = sub(m) - slb(m) + 1 enddo do m = 1,rank dextent(m) = dub(m) - dlb(m) + 1 enddo do m = rank+1,7 dextent(m) = 1 enddo do m = rank+1,7 sextent(m) = 1 enddo src_off(1) = 1 do m = 2,rank src_off(m) = src_off(m-1)*sextent(m-1) enddo dst_off(1) = 1 do m = 2,rank dst_off(m) = dst_off(m-1)*dextent(m-1) enddo do m = 1,rank src_off(m) = src_off(m) * src_slice%stride(m) enddo do m = 1,rank dst_off(m) = dst_off(m) * dst_slice%stride(m) enddo do m= rank+1,7 dst_off(m) = 0 enddo do m= rank+1,7 src_off(m) = 0 enddo j7d = 1 factor = 1 do m = 1,rank j7d = j7d + (dst_lo(m) - dlb(m))*factor factor = factor * dextent(1) enddo j7s = 1 factor = 1 do m = 1,rank j7s = j7s + (src_lo(m) - slb(m))*factor factor = factor * sextent(1) enddo pass = 1 index = 0 jbase = joff -1 do i7 = dst_lo(7),dst_hi(7),dst_stride(7) j6d = j7d j6s = j7s do i6 = dst_lo(6),dst_hi(6),dst_stride(6) j5d = j6d j5s = j6s do i5 = dst_lo(5),dst_hi(5),dst_stride(5) j4d = j5d j4s = j5s do k4 = dst_lo(4),dst_hi(4),dst_stride(4) j3d = j4d j3s = j4s do i3 = dst_lo(3),dst_hi(3),dst_stride(3) j2d = j3d j2s = j3s do i2 = dst_lo(2),dst_hi(2),dst_stride(2) j1d = j2d j1s = j2s do i1 = dst_lo(1),dst_hi(1),dst_stride(1) if (b(j1d) .ne. cmplx(jbase+j1s,jbase+j1s,c4)) then pass = 0 index = j1d endif j1d = j1d + dst_off(1) j1s = j1s + src_off(1) enddo j2d = j2d + dst_off(2) j2s = j2s + src_off(2) enddo j3d = j3d + dst_off(3) j3s = j3s + src_off(3) enddo j4d = j4d + dst_off(4) j4s = j4s + src_off(4) enddo j5d = j5d + dst_off(5) j5s = j5s + src_off(5) enddo j6d = j6d + dst_off(6) j6s = j6s + src_off(6) enddo j7d = j7d + dst_off(7) j7s = j7s + src_off(7) enddo return end subroutine check_b_c4 subroutine check_b_c8(rank,b,dst_slice,src_slice,remote_proc,dlb,dub, & & slb,sub,joff,pass) implicit none integer rank complex(kind=c8), pointer :: b(:) type(armci_slice) :: dst_slice, src_slice integer remote_proc integer dlb(7),dub(7),slb(7),sub(7) integer joff integer pass integer me integer index integer dextent(7) integer sextent(7) integer dst_stride(7) integer src_stride(7) integer src_off(7),dst_off(7) integer src_lo(7),src_hi(7) integer dst_lo(7),dst_hi(7) integer i,m,i1,i2,i3,k4,i5,i6,i7 integer j1s,j2s,j3s,j4s,j5s,j6s,j7s integer j1d,j2d,j3d,j4d,j5d,j6d,j7d integer factor integer myunit integer jbase dst_lo(:) = 1 dst_hi(:) = 1 dst_stride(:) = 1 do m = 1,rank dst_lo(m) = dst_slice%lo(m) enddo do m = 1,rank dst_hi(m) = dst_slice%hi(m) enddo do m = 1,rank dst_stride(m) = dst_slice%stride(m) enddo src_lo(:) = 1 src_hi(:) = 1 src_stride(:) = 1 do m = 1,rank src_lo(m) = src_slice%lo(m) enddo do m = 1,rank src_hi(m) = src_slice%hi(m) enddo do m = 1,rank src_stride(m) = src_slice%stride(m) enddo do m = 1,rank sextent(m) = sub(m) - slb(m) + 1 enddo do m = 1,rank dextent(m) = dub(m) - dlb(m) + 1 enddo do m = rank+1,7 dextent(m) = 1 enddo do m = rank+1,7 sextent(m) = 1 enddo src_off(1) = 1 do m = 2,rank src_off(m) = src_off(m-1)*sextent(m-1) enddo dst_off(1) = 1 do m = 2,rank dst_off(m) = dst_off(m-1)*dextent(m-1) enddo do m = 1,rank src_off(m) = src_off(m) * src_slice%stride(m) enddo do m = 1,rank dst_off(m) = dst_off(m) * dst_slice%stride(m) enddo do m= rank+1,7 dst_off(m) = 0 enddo do m= rank+1,7 src_off(m) = 0 enddo j7d = 1 factor = 1 do m = 1,rank j7d = j7d + (dst_lo(m) - dlb(m))*factor factor = factor * dextent(1) enddo j7s = 1 factor = 1 do m = 1,rank j7s = j7s + (src_lo(m) - slb(m))*factor factor = factor * sextent(1) enddo pass = 1 index = 0 jbase = joff -1 do i7 = dst_lo(7),dst_hi(7),dst_stride(7) j6d = j7d j6s = j7s do i6 = dst_lo(6),dst_hi(6),dst_stride(6) j5d = j6d j5s = j6s do i5 = dst_lo(5),dst_hi(5),dst_stride(5) j4d = j5d j4s = j5s do k4 = dst_lo(4),dst_hi(4),dst_stride(4) j3d = j4d j3s = j4s do i3 = dst_lo(3),dst_hi(3),dst_stride(3) j2d = j3d j2s = j3s do i2 = dst_lo(2),dst_hi(2),dst_stride(2) j1d = j2d j1s = j2s do i1 = dst_lo(1),dst_hi(1),dst_stride(1) if (b(j1d) .ne. cmplx(jbase+j1s,jbase+j1s,c8)) then pass = 0 index = j1d endif j1d = j1d + dst_off(1) j1s = j1s + src_off(1) enddo j2d = j2d + dst_off(2) j2s = j2s + src_off(2) enddo j3d = j3d + dst_off(3) j3s = j3s + src_off(3) enddo j4d = j4d + dst_off(4) j4s = j4s + src_off(4) enddo j5d = j5d + dst_off(5) j5s = j5s + src_off(5) enddo j6d = j6d + dst_off(6) j6s = j6s + src_off(6) enddo j7d = j7d + dst_off(7) j7s = j7s + src_off(7) enddo return end subroutine check_b_c8 end module checkput ga-5.9.2/armci/f90/definekind.f90000066400000000000000000000005511500715745200162360ustar00rootroot00000000000000module definekind integer, parameter :: I4 = SELECTED_INT_KIND(9) integer, parameter :: I8 = SELECTED_INT_KIND(16) integer, parameter :: R4 = SELECTED_REAL_KIND(5) integer, parameter :: R8 = SELECTED_REAL_KIND(12) integer, parameter :: C4 = SELECTED_REAL_KIND(5) integer, parameter :: C8 = SELECTED_REAL_KIND(12) end module definekind ga-5.9.2/armci/f90/definekind_cray.f90000066400000000000000000000003601500715745200172520ustar00rootroot00000000000000module definekind integer, parameter :: I4 = 4 integer, parameter :: I8 = 8 integer, parameter :: R4 = 4 integer, parameter :: R8 = 8 integer, parameter :: C4 = 4 integer, parameter :: C8 = 8 end module definekind ga-5.9.2/armci/f90/kinds.f90000066400000000000000000000010271500715745200152450ustar00rootroot00000000000000 program kinds INTEGER*4 I4 INTEGER*8 I8 I4 = 1 I8 = 1234567890123_8 write (*,'("The KIND for single precision is ",I2)') KIND(0.0) write (*,'("The KIND for double precision is ",I2)') KIND(0.0D0) write (*,'("The KIND for integer *4 is ",I2)') KIND(I4) write (*,'("The KIND for integer *8 is ",I2)') KIND(I8) write (*,'("The KIND for single complex is ",I2)') KIND((0.0,0.0)) write (*,'("The KIND for double precision is ",I2)') KIND((0.0D0,0.0D0)) stop end ga-5.9.2/armci/f90/testa.F90000066400000000000000000000042551500715745200152230ustar00rootroot00000000000000program main use definekind use armci_types use armci_mem_f90 use armci_mov_f90 use armci_nbmov use testa_init type(armci_slice) :: src_slice, dst_slice ! integer(kind=i4), pointer :: a1(:) integer :: remote_proc integer :: lb(7), ub(7), rc, i, j,me, nproc integer :: myunit integer :: m integer :: extent(7) integer :: asize(7) type(armci_slice) :: src_slice, dst_slice real(kind=8), dimension(:,:), pointer :: buff real(kind=8) :: time1,time2,time_array(28) integer chunk(28) data chunk /1,9,16,36,81,144,256,400,576,900,1600,2304,2704,4096,& 6084,8281,10816,16384,20164,29241,44100,65536,90000,124609,& 160000,193600,226576, 262144/ integer tbuffid,oldtbuffid #ifdef MPI integer :: ierr include 'mpif.h' call mpi_init(ierr) call mpi_comm_rank(mpi_comm_world,me,ierr) call mpi_comm_size(mpi_comm_world,nproc,ierr) #else call pbeginf() nproc = nnodes() me = nodeid() #endif remote_proc = nproc -1 -me call armci_arr_init() lb(:) = 1 ub(:) = 550*550 ub(2) = 2 call armci_malloc_fa(buff,lb,ub,ierr) tbuffid = 2 write (6,*) 'start' ! 1d if( me .eq. 0) then do m=1,28 src_slice%lo(1) = 1 src_slice%lo(2) = tbuffid src_slice%hi(1) = chunk(m) src_slice%hi(2) = tbuffid src_slice%stride(:)=2 dst_slice%lo(1) = 1 dst_slice%lo(2) = tbuffid dst_slice%hi(1) = chunk(m) dst_slice%hi(2) = tbuffid dst_slice%stride(:)=2 call armci_put_fa(buff,src_slice,buff,dst_slice,1,ierr) time1 = mpi_wtime() i = 50 ! if (m .gt. 13) i = 4 do k=1,i if(tbuffid .eq. 1) then tbuffid=2 else tbuffid=1 endif src_slice%lo(2) = tbuffid src_slice%hi(2) = tbuffid dst_slice%lo(2) = tbuffid dst_slice%hi(2) = tbuffid call armci_put_fa(buff,src_slice,buff,dst_slice,1,ierr) enddo time2 = mpi_wtime() time_array(m) = (time2 - time1)/i write (6,*) chunk(m),' ',time_array(m) enddo endif call mpi_barrier(mpi_comm_world,ierr) call armci_arr_finalize() #ifdef MPI call mpi_finalize(ierr) #else call pend() #endif end program main ga-5.9.2/armci/f90/testa_init.f90000066400000000000000000000215211500715745200163010ustar00rootroot00000000000000module testa_init use definekind interface init_7d module procedure init_7d_i4 module procedure init_7d_i8 module procedure init_7d_r4 module procedure init_7d_r8 module procedure init_7d_c4 module procedure init_7d_c8 end interface contains subroutine init_7d_i4(a,b,alb,aub,blb,bub,aoff,boff) integer(kind=i4), pointer :: a(:,:,:,:,:,:,:) integer(kind=i4), pointer :: b(:,:,:,:,:,:,:) integer alb(7),aub(7),blb(7),bub(7) integer aoff, boff integer i1,i2,i3,k4,i5,i6,i7 integer j j = aoff do i7 = alb(7),aub(7) do i6 = alb(6),aub(6) do i5 = alb(5),aub(5) do k4 = alb(4),aub(4) do i3 = alb(3),aub(3) do i2 = alb(2),aub(2) do i1 = alb(1),aub(1) a(i1,i2,i3,k4,i5,i6,i7) = j j = j + 1 enddo enddo enddo enddo enddo enddo enddo j = boff do i7 = blb(7),bub(7) do i6 = blb(6),bub(6) do i5 = blb(5),bub(5) do k4 = blb(4),bub(4) do i3 = blb(3),bub(3) do i2 = blb(2),bub(2) do i1 = blb(1),bub(1) b(i1,i2,i3,k4,i5,i6,i7) = j j = j + 1 enddo enddo enddo enddo enddo enddo enddo return end subroutine init_7d_i4 subroutine init_7d_i8(a,b,alb,aub,blb,bub,aoff,boff) integer(kind=i8), pointer :: a(:,:,:,:,:,:,:) integer(kind=i8), pointer :: b(:,:,:,:,:,:,:) integer alb(7),aub(7),blb(7),bub(7) integer aoff, boff integer i1,i2,i3,k4,i5,i6,i7 integer(kind=i8) :: j j = aoff do i7 = alb(7),aub(7) do i6 = alb(6),aub(6) do i5 = alb(5),aub(5) do k4 = alb(4),aub(4) do i3 = alb(3),aub(3) do i2 = alb(2),aub(2) do i1 = alb(1),aub(1) a(i1,i2,i3,k4,i5,i6,i7) = j j = j + 1 enddo enddo enddo enddo enddo enddo enddo j = boff do i7 = blb(7),bub(7) do i6 = blb(6),bub(6) do i5 = blb(5),bub(5) do k4 = blb(4),bub(4) do i3 = blb(3),bub(3) do i2 = blb(2),bub(2) do i1 = blb(1),bub(1) b(i1,i2,i3,k4,i5,i6,i7) = j j = j + 1 enddo enddo enddo enddo enddo enddo enddo return end subroutine init_7d_i8 subroutine init_7d_r4(a,b,alb,aub,blb,bub,aoff,boff) real(kind=r4), pointer :: a(:,:,:,:,:,:,:) real(kind=r4), pointer :: b(:,:,:,:,:,:,:) integer alb(7),aub(7),blb(7),bub(7) integer aoff, boff integer i1,i2,i3,k4,i5,i6,i7 integer(kind=i4) :: j j = aoff do i7 = alb(7),aub(7) do i6 = alb(6),aub(6) do i5 = alb(5),aub(5) do k4 = alb(4),aub(4) do i3 = alb(3),aub(3) do i2 = alb(2),aub(2) do i1 = alb(1),aub(1) a(i1,i2,i3,k4,i5,i6,i7) = float(j) j = j + 1 enddo enddo enddo enddo enddo enddo enddo j = boff do i7 = blb(7),bub(7) do i6 = blb(6),bub(6) do i5 = blb(5),bub(5) do k4 = blb(4),bub(4) do i3 = blb(3),bub(3) do i2 = blb(2),bub(2) do i1 = blb(1),bub(1) b(i1,i2,i3,k4,i5,i6,i7) = float(j) j = j + 1 enddo enddo enddo enddo enddo enddo enddo return end subroutine init_7d_r4 subroutine init_7d_r8(a,b,alb,aub,blb,bub,aoff,boff) real(kind=r8), pointer :: a(:,:,:,:,:,:,:) real(kind=r8), pointer :: b(:,:,:,:,:,:,:) integer alb(7),aub(7),blb(7),bub(7) integer aoff, boff integer i1,i2,i3,k4,i5,i6,i7 integer(kind=i4) :: j j = aoff do i7 = alb(7),aub(7) do i6 = alb(6),aub(6) do i5 = alb(5),aub(5) do k4 = alb(4),aub(4) do i3 = alb(3),aub(3) do i2 = alb(2),aub(2) do i1 = alb(1),aub(1) a(i1,i2,i3,k4,i5,i6,i7) = dble(j) j = j + 1 enddo enddo enddo enddo enddo enddo enddo j = boff do i7 = blb(7),bub(7) do i6 = blb(6),bub(6) do i5 = blb(5),bub(5) do k4 = blb(4),bub(4) do i3 = blb(3),bub(3) do i2 = blb(2),bub(2) do i1 = blb(1),bub(1) b(i1,i2,i3,k4,i5,i6,i7) = dble(j) j = j + 1 enddo enddo enddo enddo enddo enddo enddo return end subroutine init_7d_r8 subroutine init_7d_c4(a,b,alb,aub,blb,bub,aoff,boff) complex(kind=c4), pointer :: a(:,:,:,:,:,:,:) complex(kind=c4), pointer :: b(:,:,:,:,:,:,:) integer alb(7),aub(7),blb(7),bub(7) integer aoff, boff integer i1,i2,i3,k4,i5,i6,i7 integer(kind=i4) :: j j = aoff do i7 = alb(7),aub(7) do i6 = alb(6),aub(6) do i5 = alb(5),aub(5) do k4 = alb(4),aub(4) do i3 = alb(3),aub(3) do i2 = alb(2),aub(2) do i1 = alb(1),aub(1) a(i1,i2,i3,k4,i5,i6,i7) = cmplx(j,j,c4) j = j + 1 enddo enddo enddo enddo enddo enddo enddo j = boff do i7 = blb(7),bub(7) do i6 = blb(6),bub(6) do i5 = blb(5),bub(5) do k4 = blb(4),bub(4) do i3 = blb(3),bub(3) do i2 = blb(2),bub(2) do i1 = blb(1),bub(1) b(i1,i2,i3,k4,i5,i6,i7) = cmplx(j,j,c4) j = j + 1 enddo enddo enddo enddo enddo enddo enddo return end subroutine init_7d_c4 subroutine init_7d_c8(a,b,alb,aub,blb,bub,aoff,boff) complex(kind=c8), pointer :: a(:,:,:,:,:,:,:) complex(kind=c8), pointer :: b(:,:,:,:,:,:,:) integer alb(7),aub(7),blb(7),bub(7) integer aoff,boff integer i1,i2,i3,k4,i5,i6,i7 integer(kind=i4) :: j j = aoff do i7 = alb(7),aub(7) do i6 = alb(6),aub(6) do i5 = alb(5),aub(5) do k4 = alb(4),aub(4) do i3 = alb(3),aub(3) do i2 = alb(2),aub(2) do i1 = alb(1),aub(1) a(i1,i2,i3,k4,i5,i6,i7) = cmplx(j,j,c8) j = j + 1 enddo enddo enddo enddo enddo enddo enddo j = boff do i7 = blb(7),bub(7) do i6 = blb(6),bub(6) do i5 = blb(5),bub(5) do k4 = blb(4),bub(4) do i3 = blb(3),bub(3) do i2 = blb(2),bub(2) do i1 = blb(1),bub(1) b(i1,i2,i3,k4,i5,i6,i7) = cmplx(j,j,c8) j = j + 1 enddo enddo enddo enddo enddo enddo enddo return end subroutine init_7d_c8 end module testa_init ga-5.9.2/armci/f90/testanb.f90000066400000000000000000000017731500715745200156050ustar00rootroot00000000000000program main use definekind use armci_types use armci_mem_f90 use armci_nbmov use testa_init use checkput type(ARMCI_slice) :: src_slice, dst_slice ! integer(kind=I4), pointer :: a1(:) integer :: remote_proc integer :: lb(7), ub(7), rc, i, j,me, nproc integer :: myunit integer :: m integer :: extent(7) integer :: asize(7) call mpi_init(ierr) call mpi_comm_rank(mpi_comm_world, me, ierr) call mpi_comm_size(mpi_comm_world, nproc, ierr) remote_proc = nproc -1 -me myunit = 10+me call ARMCI_Arr_init() lb(:) = 1 ub(:) = 7 extent(:) = 1 extent(:) = extent(:) + ub(:) - lb(:) asize(1) = extent(1) do m= 2,7 asize(m) = asize(m-1)*extent(m) enddo call testanb_i4(nproc,me,remote_proc) call testanb_i8(nproc,me,remote_proc) call testanb_r4(nproc,me,remote_proc) call testanb_r8(nproc,me,remote_proc) call testanb_c4(nproc,me,remote_proc) call testanb_c8(nproc,me,remote_proc) call ARMCI_Arr_finalize() call mpi_finalize(ierr) end program main ga-5.9.2/armci/f90/testfa_type.f90000066400000000000000000002705021500715745200164720ustar00rootroot00000000000000subroutine testa_i4(nproc,me,remote_proc) use definekind use armci_types use armci_mem_f90 use armci_mov_f90 use testa_init use checkput implicit none type(armci_slice) :: src_slice, dst_slice integer :: nproc,me,remote_proc integer(kind=i4), pointer :: a7_i4(:,:,:,:,:,:,:), b7_i4(:,:,:,:,:,:,:) integer(kind=i4), pointer :: a6_i4(:,:,:,:,:,:), b6_i4(:,:,:,:,:,:) integer(kind=i4), pointer :: a5_i4(:,:,:,:,:), b5_i4(:,:,:,:,:) integer(kind=i4), pointer :: a4_i4(:,:,:,:), b4_i4(:,:,:,:) integer(kind=i4), pointer :: a3_i4(:,:,:), b3_i4(:,:,:) integer(kind=i4), pointer :: a2_i4(:,:), b2_i4(:,:) integer(kind=i4), pointer :: a1_i4(:), b1_i4(:) integer(kind=i4), pointer :: v_i4(:) integer :: lb(7), ub(7), rc, i, j integer :: pass integer :: myunit integer :: m integer :: rank integer :: joff integer :: vshape(1) integer :: extent(7) integer :: asize(7) integer :: score(7) integer :: vlb(1),vub(1) integer :: afirst,bfirst afirst = 1+me bfirst = afirst * afirst lb(:) = 1 ub(:) = 7 extent(:) = 1 extent(:) = extent(:) + ub(:) - lb(:) asize(1) = extent(1) do m= 2,7 asize(m) = asize(m-1)*extent(m) enddo ! test i4 flavor. vlb(1) = 1 vub(1) = asize(7) call armci_malloc_fa(v_i4,vlb,vub,rc) if (rc .ne. 0) then print *,' armci_malloc_fa for v_i4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a7_i4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a7_i4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b7_i4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b7_i4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a6_i4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a6_i4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b6_i4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b6_i4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a5_i4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a5_i4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b5_i4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b5_i4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a4_i4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a4_i4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b4_i4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b4_i4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a3_i4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a3_i4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b3_i4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b3_i4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a2_i4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a2_i4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b2_i4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b2_i4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a1_i4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a1_i4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b1_i4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b1_i4 failed rc = ',rc call armci_abort() endif ! let all processors get alloccated. call armci_sync() ! i4 allocations done, now loop over slices. ! we will test three flavors of slices, ! all unit stride, unit stride in the first dimension only, ! all non-unit stride (2). score(:) = 0 joff = remote_proc + 1 ! ! test put. ! do m = 1,3 ! initialize arrays. ! call init_7d_i4(a7_i4,b7_i4,lb,ub,lb,ub,afirst,bfirst) a6_i4(:,:,:,:,:,:) = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5_i4(:,:,:,:,:) = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4_i4(:,:,:,:) = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3_i4(:,:,:) = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2_i4(:,:) = a7_i4(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1_i4(:) = a7_i4(lb(1):ub(1),1,1,1,1,1,1) b6_i4(:,:,:,:,:,:) = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5_i4(:,:,:,:,:) = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4_i4(:,:,:,:) = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3_i4(:,:,:) = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2_i4(:,:) = b7_i4(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1_i4(:) = b7_i4(lb(1):ub(1),1,1,1,1,1,1) ! let all processors get initialized. call armci_sync() ! set up slice info for the put. ! ! for m = 1, the slice has all unit strides. ! for m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! for m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! rank 1. rank = 1 call armci_put_fa(a1_i4, src_slice, b1_i4, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 1d i4, m = ',m,' failed, rc = ',rc endif ! check that received b1 is the piece of the sent a1. ! call check_b(rank,b1_i4,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 1d i4, m = ',m,' failed' endif call armci_sync() ! rank 2. rank = 2 vshape(1) = asize(2) call armci_put_fa(a2_i4, src_slice, b2_i4, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 2d i4, m = ',m,' failed, rc = ',rc endif ! check that received b2 is the piece of the sent a2. ! ! call check_b(rank,reshape(b2_i4,vshape),dst_slice,src_slice,remote_proc, & ! call check_b(rank,b2_i4,dst_slice,src_slice,remote_proc, & v_i4(1:asize(2)) = reshape(b2_i4,vshape) call check_b(rank,v_i4,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 2d i4, m = ',m,' failed' endif call armci_sync() ! rank 3. rank = 3 vshape(1) = asize(3) call armci_put_fa(a3_i4, src_slice, b3_i4, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 3d i4, m = ',m,' failed, rc = ',rc endif ! check that received b3 is the piece of the sent a3. ! v_i4(1:asize(3)) = reshape(b3_i4,vshape) call check_b(rank,v_i4,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 3d i4, m = ',m,' failed' endif call armci_sync() ! rank 4. rank = 4 vshape(1) = asize(4) call armci_put_fa(a4_i4, src_slice, b4_i4, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 4d i4, m = ',m,' failed, rc = ',rc endif ! check that received b4 is the piece of the sent a4. ! v_i4(1:asize(4)) = reshape(b4_i4,vshape) call check_b(rank,v_i4,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 4d i4, m = ',m,' failed' endif call armci_sync() ! rank 5. rank = 5 vshape(1) = asize(5) call armci_put_fa(a5_i4, src_slice, b5_i4, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 5d i4, m = ',m,' failed, rc = ',rc endif ! check that received b5 is the piece of the sent a5. ! v_i4(1:asize(5)) = reshape(b5_i4,vshape) call check_b(rank,v_i4,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 4d i4, m = ',m,' failed' endif call armci_sync() ! rank 6. rank = 6 vshape(1) = asize(6) call armci_put_fa(a6_i4, src_slice, b6_i4, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 6d i4, m = ',m,' failed, rc = ',rc endif ! check that received b6 is the piece of the sent a6 ! v_i4(1:asize(6)) = reshape(b6_i4,vshape) call check_b(rank,v_i4,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 6d i4, m = ',m,' failed' endif call armci_sync() ! rank 7. rank = 7 vshape(1) = asize(7) call armci_put_fa(a7_i4, src_slice, b7_i4, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 7d i4, m = ',m,' failed, rc = ',rc endif ! check that received b7 is the piece of the sent a7 ! v_i4(1:asize(7)) = reshape(b7_i4,vshape) call check_b(rank,v_i4,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 7d i4, m = ',m,' failed' endif call armci_sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' armci_put_fa for i4, ',m,'d passed' endif enddo endif ! ! test get. ! score(:) = 0 joff = (remote_proc + 1)*(remote_proc+1) do m = 1,3 ! ! initialize arrays. ! call init_7d(a7_i4,b7_i4,lb,ub,lb,ub,afirst,bfirst) a6_i4 = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5_i4 = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4_i4 = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3_i4 = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2_i4 = a7_i4(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1_i4 = a7_i4(lb(1):ub(1),1,1,1,1,1,1) b6_i4 = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5_i4 = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4_i4 = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3_i4 = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2_i4 = b7_i4(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1_i4 = b7_i4(lb(1):ub(1),1,1,1,1,1,1) ! let all processors get initialized. call armci_sync() ! set up slice info for the get. ! ! for m = 1, the slice has all unit strides. ! for m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! for m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! ! rank 1 ! rank = 1 call armci_get_fa(b1_i4, dst_slice, a1_i4, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. call check_b(rank,a1_i4,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 1d i4, m = ',m,' failed' endif call armci_sync() ! ! rank 2 ! rank = 2 vshape(1) = asize(2) call armci_get_fa(b2_i4, dst_slice, a2_i4, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v_i4(1:asize(2)) = reshape(a2_i4,vshape) call check_b(rank,v_i4,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 2d i4, m = ',m,' failed' endif call armci_sync() ! ! rank 3 ! rank = 3 vshape(1) = asize(3) call armci_get_fa(b3_i4, dst_slice, a3_i4, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v_i4(1:asize(3)) = reshape(a3_i4,vshape) call check_b(rank,v_i4,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 3d i4, m = ',m,' failed' endif call armci_sync() ! ! rank 4 ! rank = 4 vshape(1) = asize(4) call armci_get_fa(b4_i4, dst_slice, a4_i4, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v_i4(1:asize(4)) = reshape(a4_i4,vshape) call check_b(rank,v_i4,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 4d i4, m = ',m,' failed' endif call armci_sync() ! ! rank 5 ! rank = 5 vshape(1) = asize(5) call armci_get_fa(b5_i4, dst_slice, a5_i4, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v_i4(1:asize(5)) = reshape(a5_i4,vshape) call check_b(rank,v_i4,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 5d i4, m = ',m,' failed' endif call armci_sync() ! ! rank 6 ! rank = 6 vshape(1) = asize(6) call armci_get_fa(b6_i4, dst_slice, a6_i4, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v_i4(1:asize(6)) = reshape(a6_i4,vshape) call check_b(rank,v_i4,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 6d i4, m = ',m,' failed' endif call armci_sync() ! ! rank 7 ! rank = 7 vshape(1) = asize(7) call armci_get_fa(b7_i4, dst_slice, a7_i4, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v_i4(1:asize(7)) = reshape(a7_i4,vshape) call check_b(rank,v_i4,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 7d i4, m = ',m,' failed' endif call armci_sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' armci_get_fa for i4, ',m,'d passed' endif enddo endif ! ! free v, a and b arrays. ! call armci_sync() call armci_free_fa(v_i4, rc) call armci_free_fa(a1_i4, rc) call armci_free_fa(b1_i4, rc) call armci_free_fa(a2_i4, rc) call armci_free_fa(b2_i4, rc) call armci_free_fa(a3_i4, rc) call armci_free_fa(b3_i4, rc) call armci_free_fa(a4_i4, rc) call armci_free_fa(b4_i4, rc) call armci_free_fa(a5_i4, rc) call armci_free_fa(b5_i4, rc) call armci_free_fa(a6_i4, rc) call armci_free_fa(b6_i4, rc) call armci_free_fa(a7_i4, rc) call armci_free_fa(b7_i4, rc) call armci_sync() return endsubroutine subroutine testa_i8(nproc,me,remote_proc) use definekind use armci_types use armci_mem_f90 use armci_mov_f90 use testa_init use checkput implicit none type(armci_slice) :: src_slice, dst_slice integer :: nproc,me,remote_proc integer(kind=i8), pointer :: a7_i8(:,:,:,:,:,:,:), b7_i8(:,:,:,:,:,:,:) integer(kind=i8), pointer :: a6_i8(:,:,:,:,:,:), b6_i8(:,:,:,:,:,:) integer(kind=i8), pointer :: a5_i8(:,:,:,:,:), b5_i8(:,:,:,:,:) integer(kind=i8), pointer :: a4_i8(:,:,:,:), b4_i8(:,:,:,:) integer(kind=i8), pointer :: a3_i8(:,:,:), b3_i8(:,:,:) integer(kind=i8), pointer :: a2_i8(:,:), b2_i8(:,:) integer(kind=i8), pointer :: a1_i8(:), b1_i8(:) integer(kind=i8), pointer :: v_i8(:) integer :: lb(7), ub(7), rc, i, j integer :: pass integer :: myunit integer :: m integer :: rank integer :: joff integer :: vshape(1) integer :: extent(7) integer :: asize(7) integer :: score(7) integer :: vlb(1),vub(1) integer :: afirst,bfirst lb(:) = 1 ub(:) = 7 extent(:) = 1 extent(:) = extent(:) + ub(:) - lb(:) asize(1) = extent(1) do m= 2,7 asize(m) = asize(m-1)*extent(m) enddo afirst = (1+me) bfirst = afirst * afirst ! test i8 flavor. vlb(1) = 1 vub(1) = asize(7) call armci_malloc_fa(v_i8,vlb,vub,rc) if (rc .ne. 0) then print *,' armci_malloc_fa for v_i8 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a7_i8, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a7_i8 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b7_i8, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b7_i8 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a6_i8, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a6_i8 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b6_i8, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b6_i8 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a5_i8, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a5_i8 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b5_i8, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b5_i8 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a4_i8, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a4_i8 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b4_i8, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b4_i8 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a3_i8, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a3_i8 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b3_i8, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b3_i8 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a2_i8, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a2_i8 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b2_i8, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b2_i8 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a1_i8, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a1_i8 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b1_i8, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b1_i8 failed rc = ',rc call armci_abort() endif ! let all processors get alloccated. call armci_sync() ! i8 allocations done, now loop over slices. ! we will test three flavors of slices, ! all unit stride, unit stride in the first dimension only, ! all non-unit stride (2). score(:) = 0 joff = remote_proc + 1 ! ! test put. ! do m = 1,3 ! initialize arrays. ! call init_7d(a7_i8,b7_i8,lb,ub,lb,ub,afirst,bfirst) a6_i8(:,:,:,:,:,:) = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5_i8(:,:,:,:,:) = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4_i8(:,:,:,:) = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3_i8(:,:,:) = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2_i8(:,:) = a7_i8(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1_i8(:) = a7_i8(lb(1):ub(1),1,1,1,1,1,1) b6_i8(:,:,:,:,:,:) = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5_i8(:,:,:,:,:) = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4_i8(:,:,:,:) = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3_i8(:,:,:) = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2_i8(:,:) = b7_i8(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1_i8(:) = b7_i8(lb(1):ub(1),1,1,1,1,1,1) ! let all processors get initialized. call armci_sync() ! set up slice info for the put. ! ! for m = 1, the slice has all unit strides. ! for m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! for m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! rank 1. rank = 1 call armci_put_fa(a1_i8, src_slice, b1_i8, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 1d i8, m = ',m,' failed, rc = ',rc endif ! check that received b1 is the piece of the sent a1. ! call check_b(rank,b1_i8,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 1d i8, m = ',m,' failed' endif call armci_sync() ! rank 2. rank = 2 vshape(1) = asize(2) call armci_put_fa(a2_i8, src_slice, b2_i8, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 2d i8, m = ',m,' failed, rc = ',rc endif ! check that received b2 is the piece of the sent a2. ! ! call check_b(rank,reshape(b2_i8,vshape),dst_slice,src_slice,remote_proc, & ! call check_b(rank,b2_i8,dst_slice,src_slice,remote_proc, & v_i8(1:asize(2)) = reshape(b2_i8,vshape) call check_b(rank,v_i8,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 2d i8, m = ',m,' failed' endif call armci_sync() ! rank 3. rank = 3 vshape(1) = asize(3) call armci_put_fa(a3_i8, src_slice, b3_i8, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 3d i8, m = ',m,' failed, rc = ',rc endif ! check that received b3 is the piece of the sent a3. ! v_i8(1:asize(3)) = reshape(b3_i8,vshape) call check_b(rank,v_i8,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 3d i8, m = ',m,' failed' endif call armci_sync() ! rank 4. rank = 4 vshape(1) = asize(4) call armci_put_fa(a4_i8, src_slice, b4_i8, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 4d i8, m = ',m,' failed, rc = ',rc endif ! check that received b4 is the piece of the sent a4. ! v_i8(1:asize(4)) = reshape(b4_i8,vshape) call check_b(rank,v_i8,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 4d i8, m = ',m,' failed' endif call armci_sync() ! rank 5. rank = 5 vshape(1) = asize(5) call armci_put_fa(a5_i8, src_slice, b5_i8, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 5d i8, m = ',m,' failed, rc = ',rc endif ! check that received b5 is the piece of the sent a5. ! v_i8(1:asize(5)) = reshape(b5_i8,vshape) call check_b(rank,v_i8,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 4d i8, m = ',m,' failed' endif call armci_sync() ! rank 6. rank = 6 vshape(1) = asize(6) call armci_put_fa(a6_i8, src_slice, b6_i8, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 6d i8, m = ',m,' failed, rc = ',rc endif ! check that received b6 is the piece of the sent a6 ! v_i8(1:asize(6)) = reshape(b6_i8,vshape) call check_b(rank,v_i8,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 6d i8, m = ',m,' failed' endif call armci_sync() ! rank 7. rank = 7 vshape(1) = asize(7) call armci_put_fa(a7_i8, src_slice, b7_i8, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 7d i8, m = ',m,' failed, rc = ',rc endif ! check that received b7 is the piece of the sent a7 ! v_i8(1:asize(7)) = reshape(b7_i8,vshape) call check_b(rank,v_i8,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 7d i8, m = ',m,' failed' endif call armci_sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' armci_put_fa for i8, ',m,'d passed' endif enddo endif ! ! test get. ! score(:) = 0 joff = (remote_proc + 1)*(remote_proc+1) do m = 1,3 ! ! initialize arrays. ! call init_7d(a7_i8,b7_i8,lb,ub,lb,ub,afirst,bfirst) a6_i8 = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5_i8 = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4_i8 = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3_i8 = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2_i8 = a7_i8(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1_i8 = a7_i8(lb(1):ub(1),1,1,1,1,1,1) b6_i8 = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5_i8 = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4_i8 = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3_i8 = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2_i8 = b7_i8(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1_i8 = b7_i8(lb(1):ub(1),1,1,1,1,1,1) ! let all processors get initialized. call armci_sync() ! set up slice info for the get. ! ! for m = 1, the slice has all unit strides. ! for m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! for m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! ! rank 1 ! rank = 1 call armci_get_fa(b1_i8, dst_slice, a1_i8, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. call check_b(rank,a1_i8,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 1d i8, m = ',m,' failed' endif call armci_sync() ! ! rank 2 ! rank = 2 vshape(1) = asize(2) call armci_get_fa(b2_i8, dst_slice, a2_i8, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v_i8(1:asize(2)) = reshape(a2_i8,vshape) call check_b(rank,v_i8,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 2d i8, m = ',m,' failed' endif call armci_sync() ! ! rank 3 ! rank = 3 vshape(1) = asize(3) call armci_get_fa(b3_i8, dst_slice, a3_i8, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v_i8(1:asize(3)) = reshape(a3_i8,vshape) call check_b(rank,v_i8,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 3d i8, m = ',m,' failed' endif call armci_sync() ! ! rank 4 ! rank = 4 vshape(1) = asize(4) call armci_get_fa(b4_i8, dst_slice, a4_i8, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v_i8(1:asize(4)) = reshape(a4_i8,vshape) call check_b(rank,v_i8,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 4d i8, m = ',m,' failed' endif call armci_sync() ! ! rank 5 ! rank = 5 vshape(1) = asize(5) call armci_get_fa(b5_i8, dst_slice, a5_i8, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v_i8(1:asize(5)) = reshape(a5_i8,vshape) call check_b(rank,v_i8,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 5d i8, m = ',m,' failed' endif call armci_sync() ! ! rank 6 ! rank = 6 vshape(1) = asize(6) call armci_get_fa(b6_i8, dst_slice, a6_i8, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v_i8(1:asize(6)) = reshape(a6_i8,vshape) call check_b(rank,v_i8,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 6d i8, m = ',m,' failed' endif call armci_sync() ! ! rank 7 ! rank = 7 vshape(1) = asize(7) call armci_get_fa(b7_i8, dst_slice, a7_i8, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v_i8(1:asize(7)) = reshape(a7_i8,vshape) call check_b(rank,v_i8,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 7d i8, m = ',m,' failed' endif call armci_sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' armci_get_fa for i8, ',m,'d passed' endif enddo endif ! ! free v, a and b arrays. ! call armci_sync() call armci_free_fa(v_i8, rc) call armci_free_fa(a1_i8, rc) call armci_free_fa(b1_i8, rc) call armci_free_fa(a2_i8, rc) call armci_free_fa(b2_i8, rc) call armci_free_fa(a3_i8, rc) call armci_free_fa(b3_i8, rc) call armci_free_fa(a4_i8, rc) call armci_free_fa(b4_i8, rc) call armci_free_fa(a5_i8, rc) call armci_free_fa(b5_i8, rc) call armci_free_fa(a6_i8, rc) call armci_free_fa(b6_i8, rc) call armci_free_fa(a7_i8, rc) call armci_free_fa(b7_i8, rc) call armci_sync() return endsubroutine subroutine testa_r4(nproc,me,remote_proc) use definekind use armci_types use armci_mem_f90 use armci_mov_f90 use testa_init use checkput implicit none type(armci_slice) :: src_slice, dst_slice integer :: nproc,me,remote_proc real(kind=r4), pointer :: a7(:,:,:,:,:,:,:), b7(:,:,:,:,:,:,:) real(kind=r4), pointer :: a6(:,:,:,:,:,:), b6(:,:,:,:,:,:) real(kind=r4), pointer :: a5(:,:,:,:,:), b5(:,:,:,:,:) real(kind=r4), pointer :: a4(:,:,:,:), b4(:,:,:,:) real(kind=r4), pointer :: a3(:,:,:), b3(:,:,:) real(kind=r4), pointer :: a2(:,:), b2(:,:) real(kind=r4), pointer :: a1(:), b1(:) real(kind=r4), pointer :: v(:) integer :: lb(7), ub(7), rc, i, j integer :: pass integer :: myunit integer :: m integer :: rank integer :: joff integer :: vshape(1) integer :: extent(7) integer :: asize(7) integer :: score(7) integer :: vlb(1),vub(1) integer :: afirst,bfirst afirst = 1+me bfirst = afirst*afirst lb(:) = 1 ub(:) = 7 extent(:) = 1 extent(:) = extent(:) + ub(:) - lb(:) asize(1) = extent(1) do m= 2,7 asize(m) = asize(m-1)*extent(m) enddo ! test r4 flavor. vlb(1) = 1 vub(1) = asize(7) call armci_malloc_fa(v,vlb,vub,rc) if (rc .ne. 0) then print *,' armci_malloc_fa for v failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a7, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a7 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b7, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b7 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a6, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a6 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b6, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b6 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a5, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a5 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b5, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b5 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a3, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a3 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b3, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b3 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a2, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a2 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b2, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b2 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a1, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a1 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b1, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b1 failed rc = ',rc call armci_abort() endif ! let all processors get alloccated. call armci_sync() ! r4 allocations done, now loop over slices. ! we will test three flavors of slices, ! all unit stride, unit stride in the first dimension only, ! all non-unit stride (2). score(:) = 0 joff = remote_proc + 1 ! ! test put. ! do m = 1,3 ! initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6(:,:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5(:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4(:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3(:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2(:,:) = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1(:) = a7(lb(1):ub(1),1,1,1,1,1,1) b6(:,:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5(:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4(:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3(:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2(:,:) = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1(:) = b7(lb(1):ub(1),1,1,1,1,1,1) ! let all processors get initialized. call armci_sync() ! set up slice info for the put. ! ! for m = 1, the slice has all unit strides. ! for m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! for m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! rank 1. rank = 1 call armci_put_fa(a1, src_slice, b1, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 1d r4, m = ',m,' failed, rc = ',rc endif ! check that received b1 is the piece of the sent a1. ! call check_b(rank,b1,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 1d r4, m = ',m,' failed' endif call armci_sync() ! rank 2. rank = 2 vshape(1) = asize(2) call armci_put_fa(a2, src_slice, b2, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 2d r4, m = ',m,' failed, rc = ',rc endif ! check that received b2 is the piece of the sent a2. ! ! call check_b(rank,reshape(b2,vshape),dst_slice,src_slice,remote_proc, & ! call check_b(rank,b2,dst_slice,src_slice,remote_proc, & v(1:asize(2)) = reshape(b2,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 2d r4, m = ',m,' failed' endif call armci_sync() ! rank 3. rank = 3 vshape(1) = asize(3) call armci_put_fa(a3, src_slice, b3, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 3d r4, m = ',m,' failed, rc = ',rc endif ! check that received b3 is the piece of the sent a3. ! v(1:asize(3)) = reshape(b3,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 3d r4, m = ',m,' failed' endif call armci_sync() ! rank 4. rank = 4 vshape(1) = asize(4) call armci_put_fa(a4, src_slice, b4, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 4d r4, m = ',m,' failed, rc = ',rc endif ! check that received b4 is the piece of the sent a4. ! v(1:asize(4)) = reshape(b4,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 4d r4, m = ',m,' failed' endif call armci_sync() ! rank 5. rank = 5 vshape(1) = asize(5) call armci_put_fa(a5, src_slice, b5, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 5d r4, m = ',m,' failed, rc = ',rc endif ! check that received b5 is the piece of the sent a5. ! v(1:asize(5)) = reshape(b5,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 4d r4, m = ',m,' failed' endif call armci_sync() ! rank 6. rank = 6 vshape(1) = asize(6) call armci_put_fa(a6, src_slice, b6, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 6d r4, m = ',m,' failed, rc = ',rc endif ! check that received b6 is the piece of the sent a6 ! v(1:asize(6)) = reshape(b6,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 6d r4, m = ',m,' failed' endif call armci_sync() ! rank 7. rank = 7 vshape(1) = asize(7) call armci_put_fa(a7, src_slice, b7, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 7d r4, m = ',m,' failed, rc = ',rc endif ! check that received b7 is the piece of the sent a7 ! v(1:asize(7)) = reshape(b7,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 7d r4, m = ',m,' failed' endif call armci_sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' armci_put_fa for r4, ',m,'d passed' endif enddo endif ! ! test get. ! score(:) = 0 joff = (remote_proc + 1)*(remote_proc+1) do m = 1,3 ! ! initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2 = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1 = a7(lb(1):ub(1),1,1,1,1,1,1) b6 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2 = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1 = b7(lb(1):ub(1),1,1,1,1,1,1) ! let all processors get initialized. call armci_sync() ! set up slice info for the get. ! ! for m = 1, the slice has all unit strides. ! for m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! for m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! ! rank 1 ! rank = 1 call armci_get_fa(b1, dst_slice, a1, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. call check_b(rank,a1,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 1d r4, m = ',m,' failed' endif call armci_sync() ! ! rank 2 ! rank = 2 vshape(1) = asize(2) call armci_get_fa(b2, dst_slice, a2, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(2)) = reshape(a2,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 2d r4, m = ',m,' failed' endif call armci_sync() ! ! rank 3 ! rank = 3 vshape(1) = asize(3) call armci_get_fa(b3, dst_slice, a3, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(3)) = reshape(a3,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 3d r4, m = ',m,' failed' endif call armci_sync() ! ! rank 4 ! rank = 4 vshape(1) = asize(4) call armci_get_fa(b4, dst_slice, a4, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(4)) = reshape(a4,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 4d r4, m = ',m,' failed' endif call armci_sync() ! ! rank 5 ! rank = 5 vshape(1) = asize(5) call armci_get_fa(b5, dst_slice, a5, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(5)) = reshape(a5,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 5d r4, m = ',m,' failed' endif call armci_sync() ! ! rank 6 ! rank = 6 vshape(1) = asize(6) call armci_get_fa(b6, dst_slice, a6, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(6)) = reshape(a6,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 6d r4, m = ',m,' failed' endif call armci_sync() ! ! rank 7 ! rank = 7 vshape(1) = asize(7) call armci_get_fa(b7, dst_slice, a7, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(7)) = reshape(a7,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 7d r4, m = ',m,' failed' endif call armci_sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' armci_get_fa for r4, ',m,'d passed' endif enddo endif ! ! free v, a and b arrays. ! call armci_sync() call armci_free_fa(v, rc) call armci_free_fa(a1, rc) call armci_free_fa(b1, rc) call armci_free_fa(a2, rc) call armci_free_fa(b2, rc) call armci_free_fa(a3, rc) call armci_free_fa(b3, rc) call armci_free_fa(a4, rc) call armci_free_fa(b4, rc) call armci_free_fa(a5, rc) call armci_free_fa(b5, rc) call armci_free_fa(a6, rc) call armci_free_fa(b6, rc) call armci_free_fa(a7, rc) call armci_free_fa(b7, rc) call armci_sync() return endsubroutine subroutine testa_r8(nproc,me,remote_proc) use definekind use armci_types use armci_mem_f90 use armci_mov_f90 use armci_nbmov use testa_init use checkput implicit none type(armci_slice) :: src_slice, dst_slice integer :: nproc,me,remote_proc real(kind=r8), pointer :: a7(:,:,:,:,:,:,:), b7(:,:,:,:,:,:,:) real(kind=r8), pointer :: a6(:,:,:,:,:,:), b6(:,:,:,:,:,:) real(kind=r8), pointer :: a5(:,:,:,:,:), b5(:,:,:,:,:) real(kind=r8), pointer :: a4(:,:,:,:), b4(:,:,:,:) real(kind=r8), pointer :: a3(:,:,:), b3(:,:,:) real(kind=r8), pointer :: a2(:,:), b2(:,:) real(kind=r8), pointer :: a1(:), b1(:) real(kind=r8), pointer :: v(:) integer :: lb(7), ub(7), rc, i, j integer :: pass integer :: myunit integer :: m integer :: rank integer :: joff integer :: vshape(1) integer :: extent(7) integer :: asize(7) integer :: score(7) integer :: vlb(1),vub(1) integer :: afirst,bfirst afirst = 1+me bfirst = afirst*afirst lb(:) = 1 ub(:) = 7 extent(:) = 1 extent(:) = extent(:) + ub(:) - lb(:) asize(1) = extent(1) do m= 2,7 asize(m) = asize(m-1)*extent(m) enddo ! test r8 flavor. vlb(1) = 1 vub(1) = asize(7) call armci_malloc_fa(v,vlb,vub,rc) if (rc .ne. 0) then print *,' armci_malloc_fa for v failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a7, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a7 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b7, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b7 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a6, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a6 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b6, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b6 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a5, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a5 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b5, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b5 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a3, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a3 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b3, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b3 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a2, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a2 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b2, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b2 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a1, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a1 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b1, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b1 failed rc = ',rc call armci_abort() endif ! let all processors get alloccated. call armci_sync() ! r8 allocations done, now loop over slices. ! we will test three flavors of slices, ! all unit stride, unit stride in the first dimension only, ! all non-unit stride (2). score(:) = 0 joff = remote_proc + 1 ! ! test put. ! do m = 1,3 ! initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6(:,:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5(:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4(:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3(:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2(:,:) = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1(:) = a7(lb(1):ub(1),1,1,1,1,1,1) b6(:,:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5(:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4(:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3(:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2(:,:) = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1(:) = b7(lb(1):ub(1),1,1,1,1,1,1) ! let all processors get initialized. call armci_sync() ! set up slice info for the put. ! ! for m = 1, the slice has all unit strides. ! for m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! for m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! rank 1. rank = 1 call armci_nbput_fa(a1, src_slice, b1, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 1d r8, m = ',m,' failed, rc = ',rc endif ! check that received b1 is the piece of the sent a1. ! call check_b(rank,b1,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 1d r8, m = ',m,' failed' endif call armci_sync() ! rank 2. rank = 2 vshape(1) = asize(2) call armci_put_fa(a2, src_slice, b2, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 2d r8, m = ',m,' failed, rc = ',rc endif ! check that received b2 is the piece of the sent a2. ! ! call check_b(rank,reshape(b2,vshape),dst_slice,src_slice,remote_proc, & ! call check_b(rank,b2,dst_slice,src_slice,remote_proc, & v(1:asize(2)) = reshape(b2,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 2d r8, m = ',m,' failed' endif call armci_sync() ! rank 3. rank = 3 vshape(1) = asize(3) call armci_put_fa(a3, src_slice, b3, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 3d r8, m = ',m,' failed, rc = ',rc endif ! check that received b3 is the piece of the sent a3. ! v(1:asize(3)) = reshape(b3,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 3d r8, m = ',m,' failed' endif call armci_sync() ! rank 4. rank = 4 vshape(1) = asize(4) call armci_put_fa(a4, src_slice, b4, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 4d r8, m = ',m,' failed, rc = ',rc endif ! check that received b4 is the piece of the sent a4. ! v(1:asize(4)) = reshape(b4,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 4d r8, m = ',m,' failed' endif call armci_sync() ! rank 5. rank = 5 vshape(1) = asize(5) call armci_put_fa(a5, src_slice, b5, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 5d r8, m = ',m,' failed, rc = ',rc endif ! check that received b5 is the piece of the sent a5. ! v(1:asize(5)) = reshape(b5,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 4d r8, m = ',m,' failed' endif call armci_sync() ! rank 6. rank = 6 vshape(1) = asize(6) call armci_put_fa(a6, src_slice, b6, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 6d r8, m = ',m,' failed, rc = ',rc endif ! check that received b6 is the piece of the sent a6 ! v(1:asize(6)) = reshape(b6,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 6d r8, m = ',m,' failed' endif call armci_sync() ! rank 7. rank = 7 vshape(1) = asize(7) call armci_put_fa(a7, src_slice, b7, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 7d r8, m = ',m,' failed, rc = ',rc endif ! check that received b7 is the piece of the sent a7 ! v(1:asize(7)) = reshape(b7,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 7d r8, m = ',m,' failed' endif call armci_sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' armci_put_fa for r8, ',m,'d passed' endif enddo endif ! ! test get. ! score(:) = 0 joff = (remote_proc + 1)*(remote_proc+1) do m = 1,3 ! ! initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2 = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1 = a7(lb(1):ub(1),1,1,1,1,1,1) b6 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2 = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1 = b7(lb(1):ub(1),1,1,1,1,1,1) ! let all processors get initialized. call armci_sync() ! set up slice info for the get. ! ! for m = 1, the slice has all unit strides. ! for m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! for m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! ! rank 1 ! rank = 1 call armci_get_fa(b1, dst_slice, a1, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. call check_b(rank,a1,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 1d r8, m = ',m,' failed' endif call armci_sync() ! ! rank 2 ! rank = 2 vshape(1) = asize(2) call armci_get_fa(b2, dst_slice, a2, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(2)) = reshape(a2,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 2d r8, m = ',m,' failed' endif call armci_sync() ! ! rank 3 ! rank = 3 vshape(1) = asize(3) call armci_get_fa(b3, dst_slice, a3, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(3)) = reshape(a3,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 3d r8, m = ',m,' failed' endif call armci_sync() ! ! rank 4 ! rank = 4 vshape(1) = asize(4) call armci_get_fa(b4, dst_slice, a4, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(4)) = reshape(a4,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 4d r8, m = ',m,' failed' endif call armci_sync() ! ! rank 5 ! rank = 5 vshape(1) = asize(5) call armci_get_fa(b5, dst_slice, a5, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(5)) = reshape(a5,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 5d r8, m = ',m,' failed' endif call armci_sync() ! ! rank 6 ! rank = 6 vshape(1) = asize(6) call armci_get_fa(b6, dst_slice, a6, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(6)) = reshape(a6,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 6d r8, m = ',m,' failed' endif call armci_sync() ! ! rank 7 ! rank = 7 vshape(1) = asize(7) call armci_get_fa(b7, dst_slice, a7, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(7)) = reshape(a7,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 7d r8, m = ',m,' failed' endif call armci_sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' armci_get_fa for r8, ',m,'d passed' endif enddo endif ! ! free v, a and b arrays. ! call armci_sync() call armci_free_fa(v, rc) call armci_free_fa(a1, rc) call armci_free_fa(b1, rc) call armci_free_fa(a2, rc) call armci_free_fa(b2, rc) call armci_free_fa(a3, rc) call armci_free_fa(b3, rc) call armci_free_fa(a4, rc) call armci_free_fa(b4, rc) call armci_free_fa(a5, rc) call armci_free_fa(b5, rc) call armci_free_fa(a6, rc) call armci_free_fa(b6, rc) call armci_free_fa(a7, rc) call armci_free_fa(b7, rc) call armci_sync() return endsubroutine subroutine testa_c4(nproc,me,remote_proc) use definekind use armci_types use armci_mem_f90 use armci_mov_f90 use testa_init use checkput implicit none type(armci_slice) :: src_slice, dst_slice integer :: nproc,me,remote_proc complex(kind=c4), pointer :: a7(:,:,:,:,:,:,:), b7(:,:,:,:,:,:,:) complex(kind=c4), pointer :: a6(:,:,:,:,:,:), b6(:,:,:,:,:,:) complex(kind=c4), pointer :: a5(:,:,:,:,:), b5(:,:,:,:,:) complex(kind=c4), pointer :: a4(:,:,:,:), b4(:,:,:,:) complex(kind=c4), pointer :: a3(:,:,:), b3(:,:,:) complex(kind=c4), pointer :: a2(:,:), b2(:,:) complex(kind=c4), pointer :: a1(:), b1(:) complex(kind=c4), pointer :: v(:) integer :: lb(7), ub(7), rc, i, j integer :: pass integer :: myunit integer :: m integer :: rank integer :: joff integer :: vshape(1) integer :: extent(7) integer :: asize(7) integer :: score(7) integer :: vlb(1),vub(1) integer :: afirst,bfirst afirst = 1+me bfirst = afirst*afirst lb(:) = 1 ub(:) = 7 extent(:) = 1 extent(:) = extent(:) + ub(:) - lb(:) asize(1) = extent(1) do m= 2,7 asize(m) = asize(m-1)*extent(m) enddo ! test c4 flavor. vlb(1) = 1 vub(1) = asize(7) call armci_malloc_fa(v,vlb,vub,rc) if (rc .ne. 0) then print *,' armci_malloc_fa for v failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a7, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a7 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b7, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b7 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a6, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a6 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b6, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b6 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a5, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a5 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b5, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b5 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a3, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a3 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b3, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b3 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a2, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a2 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b2, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b2 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a1, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a1 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b1, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b1 failed rc = ',rc call armci_abort() endif ! let all processors get alloccated. call armci_sync() ! c4 allocations done, now loop over slices. ! we will test three flavors of slices, ! all unit stride, unit stride in the first dimension only, ! all non-unit stride (2). score(:) = 0 joff = remote_proc + 1 ! ! test put. ! do m = 1,3 ! initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6(:,:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5(:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4(:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3(:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2(:,:) = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1(:) = a7(lb(1):ub(1),1,1,1,1,1,1) b6(:,:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5(:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4(:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3(:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2(:,:) = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1(:) = b7(lb(1):ub(1),1,1,1,1,1,1) ! let all processors get initialized. call armci_sync() ! set up slice info for the put. ! ! for m = 1, the slice has all unit strides. ! for m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! for m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! rank 1. rank = 1 call armci_put_fa(a1, src_slice, b1, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 1d c4, m = ',m,' failed, rc = ',rc endif ! check that received b1 is the piece of the sent a1. ! call check_b(rank,b1,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 1d c4, m = ',m,' failed' endif call armci_sync() ! rank 2. rank = 2 vshape(1) = asize(2) call armci_put_fa(a2, src_slice, b2, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 2d c4, m = ',m,' failed, rc = ',rc endif ! check that received b2 is the piece of the sent a2. ! ! call check_b(rank,reshape(b2,vshape),dst_slice,src_slice,remote_proc, & ! call check_b(rank,b2,dst_slice,src_slice,remote_proc, & v(1:asize(2)) = reshape(b2,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 2d c4, m = ',m,' failed' endif call armci_sync() ! rank 3. rank = 3 vshape(1) = asize(3) call armci_put_fa(a3, src_slice, b3, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 3d c4, m = ',m,' failed, rc = ',rc endif ! check that received b3 is the piece of the sent a3. ! v(1:asize(3)) = reshape(b3,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 3d c4, m = ',m,' failed' endif call armci_sync() ! rank 4. rank = 4 vshape(1) = asize(4) call armci_put_fa(a4, src_slice, b4, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 4d c4, m = ',m,' failed, rc = ',rc endif ! check that received b4 is the piece of the sent a4. ! v(1:asize(4)) = reshape(b4,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 4d c4, m = ',m,' failed' endif call armci_sync() ! rank 5. rank = 5 vshape(1) = asize(5) call armci_put_fa(a5, src_slice, b5, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 5d c4, m = ',m,' failed, rc = ',rc endif ! check that received b5 is the piece of the sent a5. ! v(1:asize(5)) = reshape(b5,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 4d c4, m = ',m,' failed' endif call armci_sync() ! rank 6. rank = 6 vshape(1) = asize(6) call armci_put_fa(a6, src_slice, b6, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 6d c4, m = ',m,' failed, rc = ',rc endif ! check that received b6 is the piece of the sent a6 ! v(1:asize(6)) = reshape(b6,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 6d c4, m = ',m,' failed' endif call armci_sync() ! rank 7. rank = 7 vshape(1) = asize(7) call armci_put_fa(a7, src_slice, b7, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 7d c4, m = ',m,' failed, rc = ',rc endif ! check that received b7 is the piece of the sent a7 ! v(1:asize(7)) = reshape(b7,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 7d c4, m = ',m,' failed' endif call armci_sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' armci_put_fa for c4, ',m,'d passed' endif enddo endif ! ! test get. ! score(:) = 0 joff = (remote_proc + 1)*(remote_proc+1) do m = 1,3 ! ! initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2 = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1 = a7(lb(1):ub(1),1,1,1,1,1,1) b6 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2 = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1 = b7(lb(1):ub(1),1,1,1,1,1,1) ! let all processors get initialized. call armci_sync() ! set up slice info for the get. ! ! for m = 1, the slice has all unit strides. ! for m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! for m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! ! rank 1 ! rank = 1 call armci_get_fa(b1, dst_slice, a1, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. call check_b(rank,a1,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 1d c4, m = ',m,' failed' endif call armci_sync() ! ! rank 2 ! rank = 2 vshape(1) = asize(2) call armci_get_fa(b2, dst_slice, a2, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(2)) = reshape(a2,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 2d c4, m = ',m,' failed' endif call armci_sync() ! ! rank 3 ! rank = 3 vshape(1) = asize(3) call armci_get_fa(b3, dst_slice, a3, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(3)) = reshape(a3,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 3d c4, m = ',m,' failed' endif call armci_sync() ! ! rank 4 ! rank = 4 vshape(1) = asize(4) call armci_get_fa(b4, dst_slice, a4, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(4)) = reshape(a4,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 4d c4, m = ',m,' failed' endif call armci_sync() ! ! rank 5 ! rank = 5 vshape(1) = asize(5) call armci_get_fa(b5, dst_slice, a5, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(5)) = reshape(a5,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 5d c4, m = ',m,' failed' endif call armci_sync() ! ! rank 6 ! rank = 6 vshape(1) = asize(6) call armci_get_fa(b6, dst_slice, a6, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(6)) = reshape(a6,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 6d c4, m = ',m,' failed' endif call armci_sync() ! ! rank 7 ! rank = 7 vshape(1) = asize(7) call armci_get_fa(b7, dst_slice, a7, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(7)) = reshape(a7,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 7d c4, m = ',m,' failed' endif call armci_sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' armci_get_fa for c4, ',m,'d passed' endif enddo endif ! ! free v, a and b arrays. ! call armci_sync() call armci_free_fa(v, rc) call armci_free_fa(a1, rc) call armci_free_fa(b1, rc) call armci_free_fa(a2, rc) call armci_free_fa(b2, rc) call armci_free_fa(a3, rc) call armci_free_fa(b3, rc) call armci_free_fa(a4, rc) call armci_free_fa(b4, rc) call armci_free_fa(a5, rc) call armci_free_fa(b5, rc) call armci_free_fa(a6, rc) call armci_free_fa(b6, rc) call armci_free_fa(a7, rc) call armci_free_fa(b7, rc) call armci_sync() return endsubroutine subroutine testa_c8(nproc,me,remote_proc) use definekind use armci_types use armci_mem_f90 use armci_mov_f90 use testa_init use checkput implicit none type(armci_slice) :: src_slice, dst_slice integer :: nproc,me,remote_proc complex(kind=c8), pointer :: a7(:,:,:,:,:,:,:), b7(:,:,:,:,:,:,:) complex(kind=c8), pointer :: a6(:,:,:,:,:,:), b6(:,:,:,:,:,:) complex(kind=c8), pointer :: a5(:,:,:,:,:), b5(:,:,:,:,:) complex(kind=c8), pointer :: a4(:,:,:,:), b4(:,:,:,:) complex(kind=c8), pointer :: a3(:,:,:), b3(:,:,:) complex(kind=c8), pointer :: a2(:,:), b2(:,:) complex(kind=c8), pointer :: a1(:), b1(:) complex(kind=c8), pointer :: v(:) integer :: lb(7), ub(7), rc, i, j integer :: pass integer :: myunit integer :: m integer :: rank integer :: joff integer :: vshape(1) integer :: extent(7) integer :: asize(7) integer :: score(7) integer :: vlb(1),vub(1) integer :: afirst,bfirst afirst = 1+me bfirst = afirst*afirst lb(:) = 1 ub(:) = 7 ub(7) = 3 extent(:) = 1 extent(:) = extent(:) + ub(:) - lb(:) asize(1) = extent(1) do m= 2,7 asize(m) = asize(m-1)*extent(m) enddo ! test c8 flavor. vlb(1) = 1 vub(1) = asize(7) call armci_malloc_fa(v,vlb,vub,rc) if (rc .ne. 0) then print *,' armci_malloc_fa for v failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a7, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a7 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b7, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b7 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a6, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a6 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b6, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b6 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a5, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a5 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b5, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b5 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b4, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b4 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a3, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a3 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b3, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b3 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a2, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a2 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b2, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b2 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(a1, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for a1 failed rc = ',rc call armci_abort() endif call armci_malloc_fa(b1, lb, ub, rc) if (rc .ne. 0) then print *,' armci_malloc_fa for b1 failed rc = ',rc call armci_abort() endif ! let all processors get alloccated. call armci_sync() ! c8 allocations done, now loop over slices. ! we will test three flavors of slices, ! all unit stride, unit stride in the first dimension only, ! all non-unit stride (2). score(:) = 0 joff = remote_proc + 1 ! ! test put. ! do m = 1,3 ! initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6(:,:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5(:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4(:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3(:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2(:,:) = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1(:) = a7(lb(1):ub(1),1,1,1,1,1,1) b6(:,:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5(:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4(:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3(:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2(:,:) = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1(:) = b7(lb(1):ub(1),1,1,1,1,1,1) ! let all processors get initialized. call armci_sync() ! set up slice info for the put. ! ! for m = 1, the slice has all unit strides. ! for m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! for m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%lo(7) = 1 src_slice%hi(7) = 2 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%lo(7) = 2 dst_slice%hi(7) = 3 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! rank 1. rank = 1 call armci_put_fa(a1, src_slice, b1, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 1d c8, m = ',m,' failed, rc = ',rc endif ! check that received b1 is the piece of the sent a1. ! call check_b(rank,b1,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 1d c8, m = ',m,' failed' endif call armci_sync() ! rank 2. rank = 2 vshape(1) = asize(2) call armci_put_fa(a2, src_slice, b2, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 2d c8, m = ',m,' failed, rc = ',rc endif ! check that received b2 is the piece of the sent a2. ! ! call check_b(rank,reshape(b2,vshape),dst_slice,src_slice,remote_proc, & ! call check_b(rank,b2,dst_slice,src_slice,remote_proc, & v(1:asize(2)) = reshape(b2,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 2d c8, m = ',m,' failed' endif call armci_sync() ! rank 3. rank = 3 vshape(1) = asize(3) call armci_put_fa(a3, src_slice, b3, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 3d c8, m = ',m,' failed, rc = ',rc endif ! check that received b3 is the piece of the sent a3. ! v(1:asize(3)) = reshape(b3,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 3d c8, m = ',m,' failed' endif call armci_sync() ! rank 4. rank = 4 vshape(1) = asize(4) call armci_put_fa(a4, src_slice, b4, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 4d c8, m = ',m,' failed, rc = ',rc endif ! check that received b4 is the piece of the sent a4. ! v(1:asize(4)) = reshape(b4,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 4d c8, m = ',m,' failed' endif call armci_sync() ! rank 5. rank = 5 vshape(1) = asize(5) call armci_put_fa(a5, src_slice, b5, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 5d c8, m = ',m,' failed, rc = ',rc endif ! check that received b5 is the piece of the sent a5. ! v(1:asize(5)) = reshape(b5,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 4d c8, m = ',m,' failed' endif call armci_sync() ! rank 6. rank = 6 vshape(1) = asize(6) call armci_put_fa(a6, src_slice, b6, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 6d c8, m = ',m,' failed, rc = ',rc endif ! check that received b6 is the piece of the sent a6 ! v(1:asize(6)) = reshape(b6,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 6d c8, m = ',m,' failed' endif call armci_sync() ! rank 7. rank = 7 vshape(1) = asize(7) call armci_put_fa(a7, src_slice, b7, dst_slice, remote_proc, rc) call armci_sync() if (rc .ne. 0) then print *,me,': armci_put_fa for 7d c8, m = ',m,' failed, rc = ',rc endif ! check that received b7 is the piece of the sent a7 ! v(1:asize(7)) = reshape(b7,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': armci_put_fa verify for 7d c8, m = ',m,' failed' endif call armci_sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' armci_put_fa for c8, ',m,'d passed' endif enddo endif ! ! test get. ! score(:) = 0 joff = (remote_proc + 1)*(remote_proc+1) do m = 1,3 ! ! initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2 = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1 = a7(lb(1):ub(1),1,1,1,1,1,1) b6 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2 = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1 = b7(lb(1):ub(1),1,1,1,1,1,1) ! let all processors get initialized. call armci_sync() ! set up slice info for the get. ! ! for m = 1, the slice has all unit strides. ! for m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! for m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%lo(7) = 1 src_slice%hi(7) = 2 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%lo(7) = 2 dst_slice%hi(7) = 3 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! ! rank 1 ! rank = 1 call armci_get_fa(b1, dst_slice, a1, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. call check_b(rank,a1,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 1d c8, m = ',m,' failed' endif call armci_sync() ! ! rank 2 ! rank = 2 vshape(1) = asize(2) call armci_get_fa(b2, dst_slice, a2, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(2)) = reshape(a2,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 2d c8, m = ',m,' failed' endif call armci_sync() ! ! rank 3 ! rank = 3 vshape(1) = asize(3) call armci_get_fa(b3, dst_slice, a3, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(3)) = reshape(a3,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 3d c8, m = ',m,' failed' endif call armci_sync() ! ! rank 4 ! rank = 4 vshape(1) = asize(4) call armci_get_fa(b4, dst_slice, a4, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(4)) = reshape(a4,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 4d c8, m = ',m,' failed' endif call armci_sync() ! ! rank 5 ! rank = 5 vshape(1) = asize(5) call armci_get_fa(b5, dst_slice, a5, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(5)) = reshape(a5,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 5d c8, m = ',m,' failed' endif call armci_sync() ! ! rank 6 ! rank = 6 vshape(1) = asize(6) call armci_get_fa(b6, dst_slice, a6, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(6)) = reshape(a6,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 6d c8, m = ',m,' failed' endif call armci_sync() ! ! rank 7 ! rank = 7 vshape(1) = asize(7) call armci_get_fa(b7, dst_slice, a7, src_slice, remote_proc, rc) call armci_sync() ! ! check that recieved a is the piece of b that was sent. v(1:asize(7)) = reshape(a7,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': armci_get_fa verify for 7d c8, m = ',m,' failed' endif call armci_sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' armci_get_fa for c8, ',m,'d passed' endif enddo endif ! ! free v, a and b arrays. ! call armci_sync() call armci_free_fa(v, rc) call armci_free_fa(a1, rc) call armci_free_fa(b1, rc) call armci_free_fa(a2, rc) call armci_free_fa(b2, rc) call armci_free_fa(a3, rc) call armci_free_fa(b3, rc) call armci_free_fa(a4, rc) call armci_free_fa(b4, rc) call armci_free_fa(a5, rc) call armci_free_fa(b5, rc) call armci_free_fa(a6, rc) call armci_free_fa(b6, rc) call armci_free_fa(a7, rc) call armci_free_fa(b7, rc) call armci_sync() return endsubroutine ga-5.9.2/armci/f90/testnbfa_type.f90000066400000000000000000002735721500715745200170240ustar00rootroot00000000000000subroutine testanb_i4(nproc,me,remote_proc) use definekind use armci_types use armci_mem_f90 use armci_nbmov use testa_init use checkput implicit none type(ARMCI_slice) :: src_slice, dst_slice integer :: nproc,me,remote_proc integer(kind=I4), pointer :: a7_i4(:,:,:,:,:,:,:), b7_i4(:,:,:,:,:,:,:) integer(kind=I4), pointer :: a6_i4(:,:,:,:,:,:), b6_i4(:,:,:,:,:,:) integer(kind=I4), pointer :: a5_i4(:,:,:,:,:), b5_i4(:,:,:,:,:) integer(kind=I4), pointer :: a4_i4(:,:,:,:), b4_i4(:,:,:,:) integer(kind=I4), pointer :: a3_i4(:,:,:), b3_i4(:,:,:) integer(kind=I4), pointer :: a2_i4(:,:), b2_i4(:,:) integer(kind=I4), pointer :: a1_i4(:), b1_i4(:) integer(kind=I4), pointer :: v_i4(:) integer :: lb(7), ub(7), rc, i, j integer :: pass integer :: myunit integer :: m integer :: rank integer :: joff integer :: vshape(1) integer :: extent(7) integer :: asize(7) integer :: score(7) integer :: vlb(1),vub(1) integer :: afirst,bfirst afirst = 1+me bfirst = afirst * afirst lb(:) = 1 ub(:) = 7 extent(:) = 1 extent(:) = extent(:) + ub(:) - lb(:) asize(1) = extent(1) do m= 2,7 asize(m) = asize(m-1)*extent(m) enddo ! Test I4 flavor. vlb(1) = 1 vub(1) = asize(7) call ARMCI_Malloc_fa(v_i4,vlb,vub,rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for v_i4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a7_i4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a7_i4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b7_i4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b7_i4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a6_i4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a6_i4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b6_i4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b6_i4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a5_i4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a5_i4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b5_i4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b5_i4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a4_i4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a4_i4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b4_i4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b4_i4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a3_i4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a3_i4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b3_i4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b3_i4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a2_i4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a2_i4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b2_i4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b2_i4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a1_i4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a1_i4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b1_i4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b1_i4 failed rc = ',rc stop endif ! Let all processors get allocated. call ARMCI_Sync() ! I4 allocations done, now loop over slices. ! We will test three flavors of slices, ! all unit stride, unit stride in the first dimension only, ! all non-unit stride (2). score(:) = 0 joff = remote_proc + 1 ! ! Test Put. ! do m = 1,3 ! Initialize arrays. ! call init_7d(a7_i4,b7_i4,lb,ub,lb,ub,afirst,bfirst) a6_i4(:,:,:,:,:,:) = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5_i4(:,:,:,:,:) = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4_i4(:,:,:,:) = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3_i4(:,:,:) = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2_i4(:,:) = a7_i4(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1_i4(:) = a7_i4(lb(1):ub(1),1,1,1,1,1,1) b6_i4(:,:,:,:,:,:) = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5_i4(:,:,:,:,:) = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4_i4(:,:,:,:) = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3_i4(:,:,:) = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2_i4(:,:) = b7_i4(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1_i4(:) = b7_i4(lb(1):ub(1),1,1,1,1,1,1) ! Let all processors get initialized. call ARMCI_Sync() ! Set up slice info for the put. ! ! For m = 1, the slice has all unit strides. ! For m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! For m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! Rank 1. rank = 1 call ARMCI_NbPut_fa(a1_i4, src_slice, b1_i4, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 1D I4, m = ',m,' failed, rc = ',rc endif ! Check that received b1 is the piece of the sent a1. ! call check_b(rank,b1_i4,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 1D I4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 2. rank = 2 vshape(1) = asize(2) call ARMCI_NbPut_fa(a2_i4, src_slice, b2_i4, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 2D I4, m = ',m,' failed, rc = ',rc endif ! Check that received b2 is the piece of the sent a2. ! ! call check_b(rank,reshape(b2_i4,vshape),dst_slice,src_slice,remote_proc, & ! call check_b(rank,b2_i4,dst_slice,src_slice,remote_proc, & v_i4(1:asize(2)) = reshape(b2_i4,vshape) call check_b(rank,v_i4,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 2D I4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 3. rank = 3 vshape(1) = asize(3) call ARMCI_NbPut_fa(a3_i4, src_slice, b3_i4, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 3D I4, m = ',m,' failed, rc = ',rc endif ! Check that received b3 is the piece of the sent a3. ! v_i4(1:asize(3)) = reshape(b3_i4,vshape) call check_b(rank,v_i4,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 3D I4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 4. rank = 4 vshape(1) = asize(4) call ARMCI_NbPut_fa(a4_i4, src_slice, b4_i4, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 4D I4, m = ',m,' failed, rc = ',rc endif ! Check that received b4 is the piece of the sent a4. ! v_i4(1:asize(4)) = reshape(b4_i4,vshape) call check_b(rank,v_i4,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 4D I4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 5. rank = 5 vshape(1) = asize(5) call ARMCI_NbPut_fa(a5_i4, src_slice, b5_i4, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 5D I4, m = ',m,' failed, rc = ',rc endif ! Check that received b5 is the piece of the sent a5. ! v_i4(1:asize(5)) = reshape(b5_i4,vshape) call check_b(rank,v_i4,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 4D I4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 6. rank = 6 vshape(1) = asize(6) call ARMCI_NbPut_fa(a6_i4, src_slice, b6_i4, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 6D I4, m = ',m,' failed, rc = ',rc endif ! Check that received b6 is the piece of the sent a6 ! v_i4(1:asize(6)) = reshape(b6_i4,vshape) call check_b(rank,v_i4,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 6D I4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 7. rank = 7 vshape(1) = asize(7) call ARMCI_NbPut_fa(a7_i4, src_slice, b7_i4, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 7D I4, m = ',m,' failed, rc = ',rc endif ! Check that received b7 is the piece of the sent a7 ! v_i4(1:asize(7)) = reshape(b7_i4,vshape) call check_b(rank,v_i4,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 7D I4, m = ',m,' failed' endif call ARMCI_Sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' ARMCI_NbPut_fa for I4, ',m,'D passed' endif enddo endif ! ! Test Get. ! score(:) = 0 joff = (remote_proc + 1)*(remote_proc+1) do m = 1,3 ! ! Initialize arrays. ! call init_7d(a7_i4,b7_i4,lb,ub,lb,ub,afirst,bfirst) a6_i4 = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5_i4 = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4_i4 = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3_i4 = a7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2_i4 = a7_i4(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1_i4 = a7_i4(lb(1):ub(1),1,1,1,1,1,1) b6_i4 = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5_i4 = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4_i4 = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3_i4 = b7_i4(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2_i4 = b7_i4(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1_i4 = b7_i4(lb(1):ub(1),1,1,1,1,1,1) ! Let all processors get initialized. call ARMCI_Sync() ! Set up slice info for the get. ! ! For m = 1, the slice has all unit strides. ! For m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! For m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! ! Rank 1 ! rank = 1 call ARMCI_NbGet_fa(b1_i4, dst_slice, a1_i4, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. call check_b(rank,a1_i4,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 1D I4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 2 ! rank = 2 vshape(1) = asize(2) call ARMCI_NbGet_fa(b2_i4, dst_slice, a2_i4, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v_i4(1:asize(2)) = reshape(a2_i4,vshape) call check_b(rank,v_i4,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 2D I4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 3 ! rank = 3 vshape(1) = asize(3) call ARMCI_NbGet_fa(b3_i4, dst_slice, a3_i4, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v_i4(1:asize(3)) = reshape(a3_i4,vshape) call check_b(rank,v_i4,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 3D I4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 4 ! rank = 4 vshape(1) = asize(4) call ARMCI_NbGet_fa(b4_i4, dst_slice, a4_i4, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v_i4(1:asize(4)) = reshape(a4_i4,vshape) call check_b(rank,v_i4,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 4D I4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 5 ! rank = 5 vshape(1) = asize(5) call ARMCI_NbGet_fa(b5_i4, dst_slice, a5_i4, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v_i4(1:asize(5)) = reshape(a5_i4,vshape) call check_b(rank,v_i4,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 5D I4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 6 ! rank = 6 vshape(1) = asize(6) call ARMCI_NbGet_fa(b6_i4, dst_slice, a6_i4, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v_i4(1:asize(6)) = reshape(a6_i4,vshape) call check_b(rank,v_i4,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 6D I4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 7 ! rank = 7 vshape(1) = asize(7) call ARMCI_NbGet_fa(b7_i4, dst_slice, a7_i4, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v_i4(1:asize(7)) = reshape(a7_i4,vshape) call check_b(rank,v_i4,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 7D I4, m = ',m,' failed' endif call ARMCI_Sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' ARMCI_NbGet_fa for I4, ',m,'D passed' endif enddo endif ! ! Free v, a and b arrays. ! call ARMCI_Sync() call ARMCI_Free_fa(v_i4, rc) call ARMCI_Free_fa(a1_i4, rc) call ARMCI_Free_fa(b1_i4, rc) call ARMCI_Free_fa(a2_i4, rc) call ARMCI_Free_fa(b2_i4, rc) call ARMCI_Free_fa(a3_i4, rc) call ARMCI_Free_fa(b3_i4, rc) call ARMCI_Free_fa(a4_i4, rc) call ARMCI_Free_fa(b4_i4, rc) call ARMCI_Free_fa(a5_i4, rc) call ARMCI_Free_fa(b5_i4, rc) call ARMCI_Free_fa(a6_i4, rc) call ARMCI_Free_fa(b6_i4, rc) call ARMCI_Free_fa(a7_i4, rc) call ARMCI_Free_fa(b7_i4, rc) call ARMCI_Sync() return endsubroutine subroutine testanb_i8(nproc,me,remote_proc) use definekind use armci_types use armci_mem_f90 use armci_nbmov use testa_init use checkput implicit none type(ARMCI_slice) :: src_slice, dst_slice integer :: nproc,me,remote_proc integer(kind=I8), pointer :: a7_i8(:,:,:,:,:,:,:), b7_i8(:,:,:,:,:,:,:) integer(kind=I8), pointer :: a6_i8(:,:,:,:,:,:), b6_i8(:,:,:,:,:,:) integer(kind=I8), pointer :: a5_i8(:,:,:,:,:), b5_i8(:,:,:,:,:) integer(kind=I8), pointer :: a4_i8(:,:,:,:), b4_i8(:,:,:,:) integer(kind=I8), pointer :: a3_i8(:,:,:), b3_i8(:,:,:) integer(kind=I8), pointer :: a2_i8(:,:), b2_i8(:,:) integer(kind=I8), pointer :: a1_i8(:), b1_i8(:) integer(kind=I8), pointer :: v_i8(:) integer :: lb(7), ub(7), rc, i, j integer :: pass integer :: myunit integer :: m integer :: rank integer :: joff integer :: vshape(1) integer :: extent(7) integer :: asize(7) integer :: score(7) integer :: vlb(1),vub(1) integer :: afirst,bfirst lb(:) = 1 ub(:) = 7 extent(:) = 1 extent(:) = extent(:) + ub(:) - lb(:) asize(1) = extent(1) do m= 2,7 asize(m) = asize(m-1)*extent(m) enddo afirst = (1+me) bfirst = afirst * afirst ! Test I8 flavor. vlb(1) = 1 vub(1) = asize(7) call ARMCI_Malloc_fa(v_i8,vlb,vub,rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for v_i8 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a7_i8, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a7_i8 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b7_i8, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b7_i8 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a6_i8, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a6_i8 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b6_i8, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b6_i8 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a5_i8, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a5_i8 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b5_i8, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b5_i8 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a4_i8, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a4_i8 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b4_i8, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b4_i8 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a3_i8, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a3_i8 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b3_i8, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b3_i8 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a2_i8, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a2_i8 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b2_i8, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b2_i8 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a1_i8, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a1_i8 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b1_i8, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b1_i8 failed rc = ',rc stop endif ! Let all processors get alloccated. call ARMCI_Sync() ! I8 allocations done, now loop over slices. ! We will test three flavors of slices, ! all unit stride, unit stride in the first dimension only, ! all non-unit stride (2). score(:) = 0 joff = remote_proc + 1 ! ! Test Put. ! do m = 1,3 ! Initialize arrays. ! call init_7d(a7_i8,b7_i8,lb,ub,lb,ub,afirst,bfirst) a6_i8(:,:,:,:,:,:) = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5_i8(:,:,:,:,:) = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4_i8(:,:,:,:) = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3_i8(:,:,:) = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2_i8(:,:) = a7_i8(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1_i8(:) = a7_i8(lb(1):ub(1),1,1,1,1,1,1) b6_i8(:,:,:,:,:,:) = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5_i8(:,:,:,:,:) = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4_i8(:,:,:,:) = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3_i8(:,:,:) = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2_i8(:,:) = b7_i8(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1_i8(:) = b7_i8(lb(1):ub(1),1,1,1,1,1,1) ! Let all processors get initialized. call ARMCI_Sync() ! Set up slice info for the put. ! ! For m = 1, the slice has all unit strides. ! For m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! For m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! Rank 1. rank = 1 call ARMCI_NbPut_fa(a1_i8, src_slice, b1_i8, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 1D I8, m = ',m,' failed, rc = ',rc endif ! Check that received b1 is the piece of the sent a1. ! call check_b(rank,b1_i8,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 1D I8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 2. rank = 2 vshape(1) = asize(2) call ARMCI_NbPut_fa(a2_i8, src_slice, b2_i8, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 2D I8, m = ',m,' failed, rc = ',rc endif ! Check that received b2 is the piece of the sent a2. ! ! call check_b(rank,reshape(b2_i8,vshape),dst_slice,src_slice,remote_proc, & ! call check_b(rank,b2_i8,dst_slice,src_slice,remote_proc, & v_i8(1:asize(2)) = reshape(b2_i8,vshape) call check_b(rank,v_i8,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 2D I8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 3. rank = 3 vshape(1) = asize(3) call ARMCI_NbPut_fa(a3_i8, src_slice, b3_i8, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 3D I8, m = ',m,' failed, rc = ',rc endif ! Check that received b3 is the piece of the sent a3. ! v_i8(1:asize(3)) = reshape(b3_i8,vshape) call check_b(rank,v_i8,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 3D I8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 4. rank = 4 vshape(1) = asize(4) call ARMCI_NbPut_fa(a4_i8, src_slice, b4_i8, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 4D I8, m = ',m,' failed, rc = ',rc endif ! Check that received b4 is the piece of the sent a4. ! v_i8(1:asize(4)) = reshape(b4_i8,vshape) call check_b(rank,v_i8,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 4D I8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 5. rank = 5 vshape(1) = asize(5) call ARMCI_NbPut_fa(a5_i8, src_slice, b5_i8, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 5D I8, m = ',m,' failed, rc = ',rc endif ! Check that received b5 is the piece of the sent a5. ! v_i8(1:asize(5)) = reshape(b5_i8,vshape) call check_b(rank,v_i8,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 4D I8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 6. rank = 6 vshape(1) = asize(6) call ARMCI_NbPut_fa(a6_i8, src_slice, b6_i8, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 6D I8, m = ',m,' failed, rc = ',rc endif ! Check that received b6 is the piece of the sent a6 ! v_i8(1:asize(6)) = reshape(b6_i8,vshape) call check_b(rank,v_i8,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 6D I8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 7. rank = 7 vshape(1) = asize(7) call ARMCI_NbPut_fa(a7_i8, src_slice, b7_i8, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 7D I8, m = ',m,' failed, rc = ',rc endif ! Check that received b7 is the piece of the sent a7 ! v_i8(1:asize(7)) = reshape(b7_i8,vshape) call check_b(rank,v_i8,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 7D I8, m = ',m,' failed' endif call ARMCI_Sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' ARMCI_NbPut_fa for I8, ',m,'D passed' endif enddo endif ! ! Test Get. ! score(:) = 0 joff = (remote_proc + 1)*(remote_proc+1) do m = 1,3 ! ! Initialize arrays. ! call init_7d(a7_i8,b7_i8,lb,ub,lb,ub,afirst,bfirst) a6_i8 = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5_i8 = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4_i8 = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3_i8 = a7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2_i8 = a7_i8(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1_i8 = a7_i8(lb(1):ub(1),1,1,1,1,1,1) b6_i8 = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5_i8 = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4_i8 = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3_i8 = b7_i8(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2_i8 = b7_i8(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1_i8 = b7_i8(lb(1):ub(1),1,1,1,1,1,1) ! Let all processors get initialized. call ARMCI_Sync() ! Set up slice info for the get. ! ! For m = 1, the slice has all unit strides. ! For m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! For m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! ! Rank 1 ! rank = 1 call ARMCI_NbGet_fa(b1_i8, dst_slice, a1_i8, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. call check_b(rank,a1_i8,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 1D I8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 2 ! rank = 2 vshape(1) = asize(2) call ARMCI_NbGet_fa(b2_i8, dst_slice, a2_i8, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v_i8(1:asize(2)) = reshape(a2_i8,vshape) call check_b(rank,v_i8,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 2D I8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 3 ! rank = 3 vshape(1) = asize(3) call ARMCI_NbGet_fa(b3_i8, dst_slice, a3_i8, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v_i8(1:asize(3)) = reshape(a3_i8,vshape) call check_b(rank,v_i8,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 3D I8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 4 ! rank = 4 vshape(1) = asize(4) call ARMCI_NbGet_fa(b4_i8, dst_slice, a4_i8, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v_i8(1:asize(4)) = reshape(a4_i8,vshape) call check_b(rank,v_i8,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 4D I8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 5 ! rank = 5 vshape(1) = asize(5) call ARMCI_NbGet_fa(b5_i8, dst_slice, a5_i8, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v_i8(1:asize(5)) = reshape(a5_i8,vshape) call check_b(rank,v_i8,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 5D I8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 6 ! rank = 6 vshape(1) = asize(6) call ARMCI_NbGet_fa(b6_i8, dst_slice, a6_i8, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v_i8(1:asize(6)) = reshape(a6_i8,vshape) call check_b(rank,v_i8,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 6D I8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 7 ! rank = 7 vshape(1) = asize(7) call ARMCI_NbGet_fa(b7_i8, dst_slice, a7_i8, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v_i8(1:asize(7)) = reshape(a7_i8,vshape) call check_b(rank,v_i8,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 7D I8, m = ',m,' failed' endif call ARMCI_Sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' ARMCI_NbGet_fa for I8, ',m,'D passed' endif enddo endif ! ! Free v, a and b arrays. ! call ARMCI_Sync() call ARMCI_Free_fa(v_i8, rc) call ARMCI_Free_fa(a1_i8, rc) call ARMCI_Free_fa(b1_i8, rc) call ARMCI_Free_fa(a2_i8, rc) call ARMCI_Free_fa(b2_i8, rc) call ARMCI_Free_fa(a3_i8, rc) call ARMCI_Free_fa(b3_i8, rc) call ARMCI_Free_fa(a4_i8, rc) call ARMCI_Free_fa(b4_i8, rc) call ARMCI_Free_fa(a5_i8, rc) call ARMCI_Free_fa(b5_i8, rc) call ARMCI_Free_fa(a6_i8, rc) call ARMCI_Free_fa(b6_i8, rc) call ARMCI_Free_fa(a7_i8, rc) call ARMCI_Free_fa(b7_i8, rc) call ARMCI_Sync() return endsubroutine subroutine testanb_r4(nproc,me,remote_proc) use definekind use armci_types use armci_mem_f90 use armci_nbmov use testa_init use checkput implicit none type(ARMCI_slice) :: src_slice, dst_slice integer :: nproc,me,remote_proc real(kind=R4), pointer :: a7(:,:,:,:,:,:,:), b7(:,:,:,:,:,:,:) real(kind=R4), pointer :: a6(:,:,:,:,:,:), b6(:,:,:,:,:,:) real(kind=R4), pointer :: a5(:,:,:,:,:), b5(:,:,:,:,:) real(kind=R4), pointer :: a4(:,:,:,:), b4(:,:,:,:) real(kind=R4), pointer :: a3(:,:,:), b3(:,:,:) real(kind=R4), pointer :: a2(:,:), b2(:,:) real(kind=R4), pointer :: a1(:), b1(:) real(kind=R4), pointer :: v(:) integer :: lb(7), ub(7), rc, i, j integer :: pass integer :: myunit integer :: m integer :: rank integer :: joff integer :: vshape(1) integer :: extent(7) integer :: asize(7) integer :: score(7) integer :: vlb(1),vub(1) integer :: afirst,bfirst afirst = 1+me bfirst = afirst*afirst lb(:) = 1 ub(:) = 7 extent(:) = 1 extent(:) = extent(:) + ub(:) - lb(:) asize(1) = extent(1) do m= 2,7 asize(m) = asize(m-1)*extent(m) enddo ! Test R4 flavor. vlb(1) = 1 vub(1) = asize(7) call ARMCI_Malloc_fa(v,vlb,vub,rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for v failed rc = ',rc stop endif call ARMCI_Malloc_fa(a7, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a7 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b7, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b7 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a6, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a6 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b6, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b6 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a5, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a5 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b5, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b5 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a3, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a3 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b3, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b3 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a2, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a2 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b2, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b2 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a1, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a1 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b1, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b1 failed rc = ',rc stop endif ! Let all processors get alloccated. call ARMCI_Sync() ! R4 allocations done, now loop over slices. ! We will test three flavors of slices, ! all unit stride, unit stride in the first dimension only, ! all non-unit stride (2). score(:) = 0 joff = remote_proc + 1 ! ! Test Put. ! do m = 1,3 ! Initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6(:,:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5(:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4(:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3(:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2(:,:) = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1(:) = a7(lb(1):ub(1),1,1,1,1,1,1) b6(:,:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5(:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4(:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3(:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2(:,:) = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1(:) = b7(lb(1):ub(1),1,1,1,1,1,1) ! Let all processors get initialized. call ARMCI_Sync() ! Set up slice info for the put. ! ! For m = 1, the slice has all unit strides. ! For m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! For m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! Rank 1. rank = 1 call ARMCI_NbPut_fa(a1, src_slice, b1, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 1D R4, m = ',m,' failed, rc = ',rc endif ! Check that received b1 is the piece of the sent a1. ! call check_b(rank,b1,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 1D R4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 2. rank = 2 vshape(1) = asize(2) call ARMCI_NbPut_fa(a2, src_slice, b2, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 2D R4, m = ',m,' failed, rc = ',rc endif ! Check that received b2 is the piece of the sent a2. ! ! call check_b(rank,reshape(b2,vshape),dst_slice,src_slice,remote_proc, & ! call check_b(rank,b2,dst_slice,src_slice,remote_proc, & v(1:asize(2)) = reshape(b2,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 2D R4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 3. rank = 3 vshape(1) = asize(3) call ARMCI_NbPut_fa(a3, src_slice, b3, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 3D R4, m = ',m,' failed, rc = ',rc endif ! Check that received b3 is the piece of the sent a3. ! v(1:asize(3)) = reshape(b3,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 3D R4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 4. rank = 4 vshape(1) = asize(4) call ARMCI_NbPut_fa(a4, src_slice, b4, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 4D R4, m = ',m,' failed, rc = ',rc endif ! Check that received b4 is the piece of the sent a4. ! v(1:asize(4)) = reshape(b4,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 4D R4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 5. rank = 5 vshape(1) = asize(5) call ARMCI_NbPut_fa(a5, src_slice, b5, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 5D R4, m = ',m,' failed, rc = ',rc endif ! Check that received b5 is the piece of the sent a5. ! v(1:asize(5)) = reshape(b5,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 4D R4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 6. rank = 6 vshape(1) = asize(6) call ARMCI_NbPut_fa(a6, src_slice, b6, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 6D R4, m = ',m,' failed, rc = ',rc endif ! Check that received b6 is the piece of the sent a6 ! v(1:asize(6)) = reshape(b6,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 6D R4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 7. rank = 7 vshape(1) = asize(7) call ARMCI_NbPut_fa(a7, src_slice, b7, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 7D R4, m = ',m,' failed, rc = ',rc endif ! Check that received b7 is the piece of the sent a7 ! v(1:asize(7)) = reshape(b7,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 7D R4, m = ',m,' failed' endif call ARMCI_Sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' ARMCI_NbPut_fa for R4, ',m,'D passed' endif enddo endif ! ! Test Get. ! score(:) = 0 joff = (remote_proc + 1)*(remote_proc+1) do m = 1,3 ! ! Initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2 = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1 = a7(lb(1):ub(1),1,1,1,1,1,1) b6 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2 = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1 = b7(lb(1):ub(1),1,1,1,1,1,1) ! Let all processors get initialized. call ARMCI_Sync() ! Set up slice info for the get. ! ! For m = 1, the slice has all unit strides. ! For m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! For m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! ! Rank 1 ! rank = 1 call ARMCI_NbGet_fa(b1, dst_slice, a1, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. call check_b(rank,a1,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 1D R4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 2 ! rank = 2 vshape(1) = asize(2) call ARMCI_NbGet_fa(b2, dst_slice, a2, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(2)) = reshape(a2,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 2D R4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 3 ! rank = 3 vshape(1) = asize(3) call ARMCI_NbGet_fa(b3, dst_slice, a3, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(3)) = reshape(a3,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 3D R4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 4 ! rank = 4 vshape(1) = asize(4) call ARMCI_NbGet_fa(b4, dst_slice, a4, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(4)) = reshape(a4,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 4D R4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 5 ! rank = 5 vshape(1) = asize(5) call ARMCI_NbGet_fa(b5, dst_slice, a5, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(5)) = reshape(a5,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 5D R4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 6 ! rank = 6 vshape(1) = asize(6) call ARMCI_NbGet_fa(b6, dst_slice, a6, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(6)) = reshape(a6,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 6D R4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 7 ! rank = 7 vshape(1) = asize(7) call ARMCI_NbGet_fa(b7, dst_slice, a7, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(7)) = reshape(a7,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 7D R4, m = ',m,' failed' endif call ARMCI_Sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' ARMCI_NbGet_fa for R4, ',m,'D passed' endif enddo endif ! ! Free v, a and b arrays. ! call ARMCI_Sync() call ARMCI_Free_fa(v, rc) call ARMCI_Free_fa(a1, rc) call ARMCI_Free_fa(b1, rc) call ARMCI_Free_fa(a2, rc) call ARMCI_Free_fa(b2, rc) call ARMCI_Free_fa(a3, rc) call ARMCI_Free_fa(b3, rc) call ARMCI_Free_fa(a4, rc) call ARMCI_Free_fa(b4, rc) call ARMCI_Free_fa(a5, rc) call ARMCI_Free_fa(b5, rc) call ARMCI_Free_fa(a6, rc) call ARMCI_Free_fa(b6, rc) call ARMCI_Free_fa(a7, rc) call ARMCI_Free_fa(b7, rc) call ARMCI_Sync() return endsubroutine subroutine testanb_r8(nproc,me,remote_proc) use definekind use armci_types use armci_mem_f90 use armci_nbmov use testa_init use checkput implicit none type(ARMCI_slice) :: src_slice, dst_slice integer :: nproc,me,remote_proc real(kind=R8), pointer :: a7(:,:,:,:,:,:,:), b7(:,:,:,:,:,:,:) real(kind=R8), pointer :: a6(:,:,:,:,:,:), b6(:,:,:,:,:,:) real(kind=R8), pointer :: a5(:,:,:,:,:), b5(:,:,:,:,:) real(kind=R8), pointer :: a4(:,:,:,:), b4(:,:,:,:) real(kind=R8), pointer :: a3(:,:,:), b3(:,:,:) real(kind=R8), pointer :: a2(:,:), b2(:,:) real(kind=R8), pointer :: a1(:), b1(:) real(kind=R8), pointer :: v(:) integer :: lb(7), ub(7), rc, i, j integer :: pass integer :: myunit integer :: m integer :: rank integer :: joff integer :: vshape(1) integer :: extent(7) integer :: asize(7) integer :: score(7) integer :: vlb(1),vub(1) integer :: afirst,bfirst afirst = 1+me bfirst = afirst*afirst lb(:) = 1 ub(:) = 7 extent(:) = 1 extent(:) = extent(:) + ub(:) - lb(:) asize(1) = extent(1) do m= 2,7 asize(m) = asize(m-1)*extent(m) enddo ! Test R8 flavor. vlb(1) = 1 vub(1) = asize(7) call ARMCI_Malloc_fa(v,vlb,vub,rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for v failed rc = ',rc stop endif call ARMCI_Malloc_fa(a7, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a7 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b7, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b7 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a6, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a6 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b6, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b6 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a5, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a5 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b5, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b5 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a3, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a3 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b3, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b3 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a2, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a2 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b2, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b2 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a1, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a1 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b1, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b1 failed rc = ',rc stop endif ! Let all processors get alloccated. call ARMCI_Sync() ! R8 allocations done, now loop over slices. ! We will test three flavors of slices, ! all unit stride, unit stride in the first dimension only, ! all non-unit stride (2). score(:) = 0 joff = remote_proc + 1 ! ! Test Put. ! do m = 1,3 ! Initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6(:,:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5(:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4(:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3(:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2(:,:) = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1(:) = a7(lb(1):ub(1),1,1,1,1,1,1) b6(:,:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5(:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4(:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3(:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2(:,:) = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1(:) = b7(lb(1):ub(1),1,1,1,1,1,1) ! Let all processors get initialized. call ARMCI_Sync() ! Set up slice info for the put. ! ! For m = 1, the slice has all unit strides. ! For m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! For m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! Rank 1. rank = 1 call ARMCI_NbPut_fa(a1, src_slice, b1, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 1D R8, m = ',m,' failed, rc = ',rc endif ! Check that received b1 is the piece of the sent a1. ! call check_b(rank,b1,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 1D R8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 2. rank = 2 vshape(1) = asize(2) call ARMCI_NbPut_fa(a2, src_slice, b2, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 2D R8, m = ',m,' failed, rc = ',rc endif ! Check that received b2 is the piece of the sent a2. ! ! call check_b(rank,reshape(b2,vshape),dst_slice,src_slice,remote_proc, & ! call check_b(rank,b2,dst_slice,src_slice,remote_proc, & v(1:asize(2)) = reshape(b2,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 2D R8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 3. rank = 3 vshape(1) = asize(3) call ARMCI_NbPut_fa(a3, src_slice, b3, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 3D R8, m = ',m,' failed, rc = ',rc endif ! Check that received b3 is the piece of the sent a3. ! v(1:asize(3)) = reshape(b3,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 3D R8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 4. rank = 4 vshape(1) = asize(4) call ARMCI_NbPut_fa(a4, src_slice, b4, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 4D R8, m = ',m,' failed, rc = ',rc endif ! Check that received b4 is the piece of the sent a4. ! v(1:asize(4)) = reshape(b4,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 4D R8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 5. rank = 5 vshape(1) = asize(5) call ARMCI_NbPut_fa(a5, src_slice, b5, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 5D R8, m = ',m,' failed, rc = ',rc endif ! Check that received b5 is the piece of the sent a5. ! v(1:asize(5)) = reshape(b5,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 4D R8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 6. rank = 6 vshape(1) = asize(6) call ARMCI_NbPut_fa(a6, src_slice, b6, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 6D R8, m = ',m,' failed, rc = ',rc endif ! Check that received b6 is the piece of the sent a6 ! v(1:asize(6)) = reshape(b6,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 6D R8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 7. rank = 7 vshape(1) = asize(7) call ARMCI_NbPut_fa(a7, src_slice, b7, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 7D R8, m = ',m,' failed, rc = ',rc endif ! Check that received b7 is the piece of the sent a7 ! v(1:asize(7)) = reshape(b7,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 7D R8, m = ',m,' failed' endif call ARMCI_Sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' ARMCI_NbPut_fa for R8, ',m,'D passed' endif enddo endif ! ! Test Get. ! score(:) = 0 joff = (remote_proc + 1)*(remote_proc+1) do m = 1,3 ! ! Initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2 = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1 = a7(lb(1):ub(1),1,1,1,1,1,1) b6 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2 = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1 = b7(lb(1):ub(1),1,1,1,1,1,1) ! Let all processors get initialized. call ARMCI_Sync() ! Set up slice info for the get. ! ! For m = 1, the slice has all unit strides. ! For m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! For m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! ! Rank 1 ! rank = 1 call ARMCI_NbGet_fa(b1, dst_slice, a1, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. call check_b(rank,a1,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 1D R8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 2 ! rank = 2 vshape(1) = asize(2) call ARMCI_NbGet_fa(b2, dst_slice, a2, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(2)) = reshape(a2,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 2D R8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 3 ! rank = 3 vshape(1) = asize(3) call ARMCI_NbGet_fa(b3, dst_slice, a3, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(3)) = reshape(a3,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 3D R8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 4 ! rank = 4 vshape(1) = asize(4) call ARMCI_NbGet_fa(b4, dst_slice, a4, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(4)) = reshape(a4,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 4D R8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 5 ! rank = 5 vshape(1) = asize(5) call ARMCI_NbGet_fa(b5, dst_slice, a5, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(5)) = reshape(a5,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 5D R8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 6 ! rank = 6 vshape(1) = asize(6) call ARMCI_NbGet_fa(b6, dst_slice, a6, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(6)) = reshape(a6,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 6D R8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 7 ! rank = 7 vshape(1) = asize(7) call ARMCI_NbGet_fa(b7, dst_slice, a7, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(7)) = reshape(a7,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 7D R8, m = ',m,' failed' endif call ARMCI_Sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' ARMCI_NbGet_fa for R8, ',m,'D passed' endif enddo endif ! ! Free v, a and b arrays. ! call ARMCI_Sync() call ARMCI_Free_fa(v, rc) call ARMCI_Free_fa(a1, rc) call ARMCI_Free_fa(b1, rc) call ARMCI_Free_fa(a2, rc) call ARMCI_Free_fa(b2, rc) call ARMCI_Free_fa(a3, rc) call ARMCI_Free_fa(b3, rc) call ARMCI_Free_fa(a4, rc) call ARMCI_Free_fa(b4, rc) call ARMCI_Free_fa(a5, rc) call ARMCI_Free_fa(b5, rc) call ARMCI_Free_fa(a6, rc) call ARMCI_Free_fa(b6, rc) call ARMCI_Free_fa(a7, rc) call ARMCI_Free_fa(b7, rc) call ARMCI_Sync() return endsubroutine subroutine testanb_c4(nproc,me,remote_proc) use definekind use armci_types use armci_mem_f90 use armci_nbmov use testa_init use checkput implicit none type(ARMCI_slice) :: src_slice, dst_slice integer :: nproc,me,remote_proc complex(kind=C4), pointer :: a7(:,:,:,:,:,:,:), b7(:,:,:,:,:,:,:) complex(kind=C4), pointer :: a6(:,:,:,:,:,:), b6(:,:,:,:,:,:) complex(kind=C4), pointer :: a5(:,:,:,:,:), b5(:,:,:,:,:) complex(kind=C4), pointer :: a4(:,:,:,:), b4(:,:,:,:) complex(kind=C4), pointer :: a3(:,:,:), b3(:,:,:) complex(kind=C4), pointer :: a2(:,:), b2(:,:) complex(kind=C4), pointer :: a1(:), b1(:) complex(kind=C4), pointer :: v(:) integer :: lb(7), ub(7), rc, i, j integer :: pass integer :: myunit integer :: m integer :: rank integer :: joff integer :: vshape(1) integer :: extent(7) integer :: asize(7) integer :: score(7) integer :: vlb(1),vub(1) integer :: afirst,bfirst afirst = 1+me bfirst = afirst*afirst lb(:) = 1 ub(:) = 7 extent(:) = 1 extent(:) = extent(:) + ub(:) - lb(:) asize(1) = extent(1) do m= 2,7 asize(m) = asize(m-1)*extent(m) enddo ! Test C4 flavor. vlb(1) = 1 vub(1) = asize(7) call ARMCI_Malloc_fa(v,vlb,vub,rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for v failed rc = ',rc stop endif call ARMCI_Malloc_fa(a7, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a7 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b7, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b7 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a6, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a6 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b6, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b6 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a5, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a5 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b5, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b5 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a3, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a3 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b3, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b3 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a2, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a2 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b2, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b2 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a1, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a1 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b1, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b1 failed rc = ',rc stop endif ! Let all processors get alloccated. call ARMCI_Sync() ! C4 allocations done, now loop over slices. ! We will test three flavors of slices, ! all unit stride, unit stride in the first dimension only, ! all non-unit stride (2). score(:) = 0 joff = remote_proc + 1 ! ! Test Put. ! do m = 1,3 ! Initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6(:,:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5(:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4(:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3(:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2(:,:) = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1(:) = a7(lb(1):ub(1),1,1,1,1,1,1) b6(:,:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5(:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4(:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3(:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2(:,:) = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1(:) = b7(lb(1):ub(1),1,1,1,1,1,1) ! Let all processors get initialized. call ARMCI_Sync() ! Set up slice info for the put. ! ! For m = 1, the slice has all unit strides. ! For m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! For m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! Rank 1. rank = 1 call ARMCI_NbPut_fa(a1, src_slice, b1, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 1D C4, m = ',m,' failed, rc = ',rc endif ! Check that received b1 is the piece of the sent a1. ! call check_b(rank,b1,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 1D C4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 2. rank = 2 vshape(1) = asize(2) call ARMCI_NbPut_fa(a2, src_slice, b2, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 2D C4, m = ',m,' failed, rc = ',rc endif ! Check that received b2 is the piece of the sent a2. ! ! call check_b(rank,reshape(b2,vshape),dst_slice,src_slice,remote_proc, & ! call check_b(rank,b2,dst_slice,src_slice,remote_proc, & v(1:asize(2)) = reshape(b2,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 2D C4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 3. rank = 3 vshape(1) = asize(3) call ARMCI_NbPut_fa(a3, src_slice, b3, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 3D C4, m = ',m,' failed, rc = ',rc endif ! Check that received b3 is the piece of the sent a3. ! v(1:asize(3)) = reshape(b3,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 3D C4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 4. rank = 4 vshape(1) = asize(4) call ARMCI_NbPut_fa(a4, src_slice, b4, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 4D C4, m = ',m,' failed, rc = ',rc endif ! Check that received b4 is the piece of the sent a4. ! v(1:asize(4)) = reshape(b4,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 4D C4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 5. rank = 5 vshape(1) = asize(5) call ARMCI_NbPut_fa(a5, src_slice, b5, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 5D C4, m = ',m,' failed, rc = ',rc endif ! Check that received b5 is the piece of the sent a5. ! v(1:asize(5)) = reshape(b5,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 4D C4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 6. rank = 6 vshape(1) = asize(6) call ARMCI_NbPut_fa(a6, src_slice, b6, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 6D C4, m = ',m,' failed, rc = ',rc endif ! Check that received b6 is the piece of the sent a6 ! v(1:asize(6)) = reshape(b6,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 6D C4, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 7. rank = 7 vshape(1) = asize(7) call ARMCI_NbPut_fa(a7, src_slice, b7, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 7D C4, m = ',m,' failed, rc = ',rc endif ! Check that received b7 is the piece of the sent a7 ! v(1:asize(7)) = reshape(b7,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 7D C4, m = ',m,' failed' endif call ARMCI_Sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' ARMCI_NbPut_fa for C4, ',m,'D passed' endif enddo endif ! ! Test Get. ! score(:) = 0 joff = (remote_proc + 1)*(remote_proc+1) do m = 1,3 ! ! Initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2 = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1 = a7(lb(1):ub(1),1,1,1,1,1,1) b6 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2 = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1 = b7(lb(1):ub(1),1,1,1,1,1,1) ! Let all processors get initialized. call ARMCI_Sync() ! Set up slice info for the get. ! ! For m = 1, the slice has all unit strides. ! For m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! For m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! ! Rank 1 ! rank = 1 call ARMCI_NbGet_fa(b1, dst_slice, a1, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. call check_b(rank,a1,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 1D C4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 2 ! rank = 2 vshape(1) = asize(2) call ARMCI_NbGet_fa(b2, dst_slice, a2, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(2)) = reshape(a2,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 2D C4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 3 ! rank = 3 vshape(1) = asize(3) call ARMCI_NbGet_fa(b3, dst_slice, a3, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(3)) = reshape(a3,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 3D C4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 4 ! rank = 4 vshape(1) = asize(4) call ARMCI_NbGet_fa(b4, dst_slice, a4, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(4)) = reshape(a4,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 4D C4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 5 ! rank = 5 vshape(1) = asize(5) call ARMCI_NbGet_fa(b5, dst_slice, a5, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(5)) = reshape(a5,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 5D C4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 6 ! rank = 6 vshape(1) = asize(6) call ARMCI_NbGet_fa(b6, dst_slice, a6, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(6)) = reshape(a6,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 6D C4, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 7 ! rank = 7 vshape(1) = asize(7) call ARMCI_NbGet_fa(b7, dst_slice, a7, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(7)) = reshape(a7,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 7D C4, m = ',m,' failed' endif call ARMCI_Sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' ARMCI_NbGet_fa for C4, ',m,'D passed' endif enddo endif ! ! Free v, a and b arrays. ! call ARMCI_Sync() call ARMCI_Free_fa(v, rc) call ARMCI_Free_fa(a1, rc) call ARMCI_Free_fa(b1, rc) call ARMCI_Free_fa(a2, rc) call ARMCI_Free_fa(b2, rc) call ARMCI_Free_fa(a3, rc) call ARMCI_Free_fa(b3, rc) call ARMCI_Free_fa(a4, rc) call ARMCI_Free_fa(b4, rc) call ARMCI_Free_fa(a5, rc) call ARMCI_Free_fa(b5, rc) call ARMCI_Free_fa(a6, rc) call ARMCI_Free_fa(b6, rc) call ARMCI_Free_fa(a7, rc) call ARMCI_Free_fa(b7, rc) call ARMCI_Sync() return endsubroutine subroutine testanb_c8(nproc,me,remote_proc) use definekind use armci_types use armci_mem_f90 use armci_nbmov use testa_init use checkput implicit none type(ARMCI_slice) :: src_slice, dst_slice integer :: nproc,me,remote_proc complex(kind=C8), pointer :: a7(:,:,:,:,:,:,:), b7(:,:,:,:,:,:,:) complex(kind=C8), pointer :: a6(:,:,:,:,:,:), b6(:,:,:,:,:,:) complex(kind=C8), pointer :: a5(:,:,:,:,:), b5(:,:,:,:,:) complex(kind=C8), pointer :: a4(:,:,:,:), b4(:,:,:,:) complex(kind=C8), pointer :: a3(:,:,:), b3(:,:,:) complex(kind=C8), pointer :: a2(:,:), b2(:,:) complex(kind=C8), pointer :: a1(:), b1(:) complex(kind=C8), pointer :: v(:) integer :: lb(7), ub(7), rc, i, j integer :: pass integer :: myunit integer :: m integer :: rank integer :: joff integer :: vshape(1) integer :: extent(7) integer :: asize(7) integer :: score(7) integer :: vlb(1),vub(1) integer :: afirst,bfirst afirst = 1+me bfirst = afirst*afirst lb(:) = 1 ub(:) = 7 ub(7) = 3 extent(:) = 1 extent(:) = extent(:) + ub(:) - lb(:) asize(1) = extent(1) do m= 2,7 asize(m) = asize(m-1)*extent(m) enddo ! Test C8 flavor. vlb(1) = 1 vub(1) = asize(7) call ARMCI_Malloc_fa(v,vlb,vub,rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for v failed rc = ',rc stop endif call ARMCI_Malloc_fa(a7, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a7 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b7, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b7 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a6, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a6 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b6, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b6 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a5, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a5 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b5, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b5 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b4, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b4 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a3, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a3 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b3, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b3 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a2, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a2 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b2, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b2 failed rc = ',rc stop endif call ARMCI_Malloc_fa(a1, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for a1 failed rc = ',rc stop endif call ARMCI_Malloc_fa(b1, lb, ub, rc) if (rc .ne. 0) then print *,' ARMCI_Malloc_fa for b1 failed rc = ',rc stop endif ! Let all processors get alloccated. call ARMCI_Sync() ! C8 allocations done, now loop over slices. ! We will test three flavors of slices, ! all unit stride, unit stride in the first dimension only, ! all non-unit stride (2). score(:) = 0 joff = remote_proc + 1 ! ! Test Put. ! do m = 1,3 ! Initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6(:,:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5(:,:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4(:,:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3(:,:,:) = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2(:,:) = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1(:) = a7(lb(1):ub(1),1,1,1,1,1,1) b6(:,:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5(:,:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4(:,:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3(:,:,:) = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2(:,:) = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1(:) = b7(lb(1):ub(1),1,1,1,1,1,1) ! Let all processors get initialized. call ARMCI_Sync() ! Set up slice info for the put. ! ! For m = 1, the slice has all unit strides. ! For m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! For m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%lo(7) = 1 src_slice%hi(7) = 2 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%lo(7) = 2 dst_slice%hi(7) = 3 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! Rank 1. rank = 1 call ARMCI_NbPut_fa(a1, src_slice, b1, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 1D C8, m = ',m,' failed, rc = ',rc endif ! Check that received b1 is the piece of the sent a1. ! call check_b(rank,b1,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 1D C8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 2. rank = 2 vshape(1) = asize(2) call ARMCI_NbPut_fa(a2, src_slice, b2, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 2D C8, m = ',m,' failed, rc = ',rc endif ! Check that received b2 is the piece of the sent a2. ! ! call check_b(rank,reshape(b2,vshape),dst_slice,src_slice,remote_proc, & ! call check_b(rank,b2,dst_slice,src_slice,remote_proc, & v(1:asize(2)) = reshape(b2,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 2D C8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 3. rank = 3 vshape(1) = asize(3) call ARMCI_NbPut_fa(a3, src_slice, b3, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 3D C8, m = ',m,' failed, rc = ',rc endif ! Check that received b3 is the piece of the sent a3. ! v(1:asize(3)) = reshape(b3,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 3D C8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 4. rank = 4 vshape(1) = asize(4) call ARMCI_NbPut_fa(a4, src_slice, b4, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 4D C8, m = ',m,' failed, rc = ',rc endif ! Check that received b4 is the piece of the sent a4. ! v(1:asize(4)) = reshape(b4,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 4D C8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 5. rank = 5 vshape(1) = asize(5) call ARMCI_NbPut_fa(a5, src_slice, b5, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 5D C8, m = ',m,' failed, rc = ',rc endif ! Check that received b5 is the piece of the sent a5. ! v(1:asize(5)) = reshape(b5,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 4D C8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 6. rank = 6 vshape(1) = asize(6) call ARMCI_NbPut_fa(a6, src_slice, b6, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 6D C8, m = ',m,' failed, rc = ',rc endif ! Check that received b6 is the piece of the sent a6 ! v(1:asize(6)) = reshape(b6,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 6D C8, m = ',m,' failed' endif call ARMCI_Sync() ! Rank 7. rank = 7 vshape(1) = asize(7) call ARMCI_NbPut_fa(a7, src_slice, b7, dst_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() if (rc .ne. 0) then print *,me,': ARMCI_NbPut_fa for 7D C8, m = ',m,' failed, rc = ',rc endif ! Check that received b7 is the piece of the sent a7 ! v(1:asize(7)) = reshape(b7,vshape) call check_b(rank,v,dst_slice,src_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbPut_fa verify for 7D C8, m = ',m,' failed' endif call ARMCI_Sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' ARMCI_NbPut_fa for C8, ',m,'D passed' endif enddo endif ! ! Test Get. ! score(:) = 0 joff = (remote_proc + 1)*(remote_proc+1) do m = 1,3 ! ! Initialize arrays. ! call init_7d(a7,b7,lb,ub,lb,ub,afirst,bfirst) a6 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) a5 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) a4 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) a3 = a7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) a2 = a7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) a1 = a7(lb(1):ub(1),1,1,1,1,1,1) b6 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),lb(6):ub(6),1) b5 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),lb(5):ub(5),1,1) b4 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),lb(4):ub(4),1,1,1) b3 = b7(lb(1):ub(1),lb(2):ub(2),lb(3):ub(3),1,1,1,1) b2 = b7(lb(1):ub(1),lb(2):ub(2),1,1,1,1,1) b1 = b7(lb(1):ub(1),1,1,1,1,1,1) ! Let all processors get initialized. call ARMCI_Sync() ! Set up slice info for the get. ! ! For m = 1, the slice has all unit strides. ! For m = 2, the first dimension has a unit stride and all other ! dimensions have a stride of 2. ! For m = 3, all dimensions have a stride of 2. ! src_slice%lo(:) = 2 src_slice%hi(:) = 6 src_slice%lo(7) = 1 src_slice%hi(7) = 2 src_slice%stride(:) = (m+2)/2 src_slice%stride(1) = (m+1)/2 dst_slice%lo(:) = 3 dst_slice%hi(:) = 7 dst_slice%lo(7) = 2 dst_slice%hi(7) = 3 dst_slice%stride(:) = (m+2)/2 dst_slice%stride(1) = (m+1)/2 ! ! Rank 1 ! rank = 1 call ARMCI_NbGet_fa(b1, dst_slice, a1, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. call check_b(rank,a1,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(1) = score(1) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 1D C8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 2 ! rank = 2 vshape(1) = asize(2) call ARMCI_NbGet_fa(b2, dst_slice, a2, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(2)) = reshape(a2,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(2) = score(2) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 2D C8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 3 ! rank = 3 vshape(1) = asize(3) call ARMCI_NbGet_fa(b3, dst_slice, a3, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(3)) = reshape(a3,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(3) = score(3) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 3D C8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 4 ! rank = 4 vshape(1) = asize(4) call ARMCI_NbGet_fa(b4, dst_slice, a4, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(4)) = reshape(a4,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(4) = score(4) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 4D C8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 5 ! rank = 5 vshape(1) = asize(5) call ARMCI_NbGet_fa(b5, dst_slice, a5, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(5)) = reshape(a5,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(5) = score(5) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 5D C8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 6 ! rank = 6 vshape(1) = asize(6) call ARMCI_NbGet_fa(b6, dst_slice, a6, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(6)) = reshape(a6,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(6) = score(6) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 6D C8, m = ',m,' failed' endif call ARMCI_Sync() ! ! Rank 7 ! rank = 7 vshape(1) = asize(7) call ARMCI_NbGet_fa(b7, dst_slice, a7, src_slice, remote_proc, rc) call ARMCI_Waitall_fa() call ARMCI_Sync() ! ! Check that recieved a is the piece of b that was sent. v(1:asize(7)) = reshape(a7,vshape) call check_b(rank,v,src_slice,dst_slice,remote_proc, & & lb,ub,lb,ub,joff,pass) score(7) = score(7) + pass if (pass .ne. 1) then print *,me,': ARMCI_NbGet_fa verify for 7D C8, m = ',m,' failed' endif call ARMCI_Sync() enddo if (me .eq. 0) then do m = 1,7 if (score(m) .eq. 3) then print *,' ARMCI_NbGet_fa for C8, ',m,'D passed' endif enddo endif ! ! Free v, a and b arrays. ! call ARMCI_Sync() call ARMCI_Free_fa(v, rc) call ARMCI_Free_fa(a1, rc) call ARMCI_Free_fa(b1, rc) call ARMCI_Free_fa(a2, rc) call ARMCI_Free_fa(b2, rc) call ARMCI_Free_fa(a3, rc) call ARMCI_Free_fa(b3, rc) call ARMCI_Free_fa(a4, rc) call ARMCI_Free_fa(b4, rc) call ARMCI_Free_fa(a5, rc) call ARMCI_Free_fa(b5, rc) call ARMCI_Free_fa(a6, rc) call ARMCI_Free_fa(b6, rc) call ARMCI_Free_fa(a7, rc) call ARMCI_Free_fa(b7, rc) call ARMCI_Sync() return endsubroutine ga-5.9.2/armci/m4/000077500000000000000000000000001500715745200135375ustar00rootroot00000000000000ga-5.9.2/armci/m4/armci_dummy.m4000066400000000000000000000002111500715745200163010ustar00rootroot00000000000000# ARMCI_DUMMY # ----------- # Placeholder so autoreconf completes in the absense of a complete m4 directory. AC_DEFUN([ARMCI_DUMMY], []) ga-5.9.2/armci/src/000077500000000000000000000000001500715745200140065ustar00rootroot00000000000000ga-5.9.2/armci/src/collectives/000077500000000000000000000000001500715745200163225ustar00rootroot00000000000000ga-5.9.2/armci/src/collectives/message.c000066400000000000000000001614401500715745200201200ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: message.c,v 1.58.6.4 2007-04-24 10:08:26 vinod Exp $ */ #if defined(TCGMSG) # include static void tcg_brdcst(long type, void *buf, long lenbuf, long originator) { long atype = type; long alenbuf = lenbuf; long aoriginator = originator; BRDCST_(&atype, buf, &alenbuf, &aoriginator); } static void tcg_synch(long type) { long atype = type; SYNCH_(&atype); } static void tcg_snd(long type, void *buf, long lenbuf, long node, long sync) { long atype = type; long alenbuf = lenbuf; long anode = node; long async = sync; SND_(&atype, buf, &alenbuf, &anode, &async); } static void tcg_rcv(long type, void *buf, long lenbuf, long *lenmes, long nodeselect, long *nodefrom, long sync) { long atype = type; long alenbuf = lenbuf; long anodeselect = nodeselect; long async = sync; RCV_(&atype, buf, &alenbuf, lenmes, &anodeselect, nodefrom, &async); } #else # ifndef MSG_COMMS_MPI # define MSG_COMMS_MPI # endif # include #endif #include "message.h" #include "armcip.h" #include "copy.h" #if HAVE_STDIO_H # include #endif #if HAVE_ASSERT_H # include #endif #ifdef _POSIX_PRIORITY_SCHEDULING # include #endif #include "armci.h" #include "acc.h" #define DEBUG_ 0 #if defined(SYSV) || defined(MMAP) ||defined (WIN32) # include "armci_shmem.h" #endif /* global operations are use buffer size of BUF_SIZE doubles */ #define BUF_SIZE (4*2048) #define INFO_BUF_SIZE (BUF_SIZE*sizeof(BUF_SIZE) - sizeof(double)) #undef EMPTY #define EMPTY 0 #define FULL 1 static double *work=NULL; static long *lwork = NULL; static long long *llwork = NULL; static int *iwork = NULL; static float *fwork = NULL; static int _armci_gop_init=0; /* tells us if we have a buffers allocated */ static int _armci_gop_shmem =0; /* tells us to use shared memory for gops */ extern void armci_util_wait_int(volatile int *, int , int ); static int empty=EMPTY,full=FULL; #if defined(SYSV) || defined(MMAP) || defined(WIN32) static void **ptr_arr=NULL; #endif typedef struct { union { volatile int flag; double dummy[16]; }a; union { volatile int flag; double dummy[16]; }b; double array[BUF_SIZE]; } bufstruct; static bufstruct *_gop_buffer; #define GOP_BUF(p) (_gop_buffer+((p)-armci_master)) /*\ macro to set a flag includes mem barrier to assure that flag is not set * before any outstanding writes complete \*/ #ifdef NEED_MEM_SYNC # ifdef AIX # define SET_SHM_FLAG(_flg,_val) _clear_lock((int *)(_flg),_val); # elif defined(MACX) # if defined(__GNUC__) # define SET_SHM_FLAG(_flg,_val)\ *(_flg)=(_val);__asm__ __volatile__ ("isync" ::: "memory") # endif # endif #endif #ifndef SET_SHM_FLAG # define SET_SHM_FLAG(_flg,_val) *(_flg)=_val; #endif /*\ * Variables/structures for use in Barrier and for Binomial tree \*/ #if HAVE_MATH_H # include #endif int barr_switch; static int LnB=0,powof2nodes,Lp2; typedef struct { volatile int flag1; double dum[16]; volatile int flag2; } barrier_struct; barrier_struct *_bar_buff; #define BAR_BUF(p) (_bar_buff+((p))) void **barr_snd_ptr,**barr_rcv_ptr; int _armci_barrier_init=0; int _armci_barrier_shmem=0; #if 0 /*\ * Tree generation code \*/ static void _dfs_bintree_parse(int *idlist, int index, int max, int *result) { int left = (int)2*index+1; int right = (int) 2*index+2; static int pos=0; int r_end,l_end; l_end=pos++; result[pos++]=idlist[index]; if(lefta.flag=EMPTY; /* initially buffer is empty */ GOP_BUF(armci_me)->b.flag=EMPTY; /* initially buffer is empty */ if(armci_me == armci_master ){ GOP_BUF(armci_clus_last+1)->a.flag=EMPTY;/*initially buffer is empty*/ GOP_BUF(armci_clus_last+2)->a.flag=EMPTY;/*initially buffer is empty*/ GOP_BUF(armci_clus_last+1)->b.flag=EMPTY;/*initially buffer is empty*/ GOP_BUF(armci_clus_last+2)->b.flag=EMPTY;/*initially buffer is empty*/ } _armci_gop_shmem = 1; } #endif /*stuff needed for barrier and binomial bcast/reduce*/ _armci_gop_init=1; } void armci_msg_gop_finalize() { #if defined(SYSV) || defined(MMAP) || defined(WIN32) if(ARMCI_Uses_shm()){ PARMCI_Free(ptr_arr[armci_me]); free(ptr_arr); } #endif _deallocate_mem_for_work(); } void cpu_yield() { #if defined(SYSV) || defined(MMAP) || defined(WIN32) #if defined(WIN32) Sleep(1); #elif defined(_POSIX_PRIORITY_SCHEDULING) sched_yield(); #else usleep(1); #endif #endif } /*\ busy wait * n represents number of time delay units * notused is useful to fool compiler by passing address of sensitive variable \*/ #define DUMMY_INIT 1.0001 double _armci_dummy_work=DUMMY_INIT; void armci_util_spin(int n, void *notused) { int i; for(i=0; i-1) _armci_dummy_work *=DUMMY_INIT; if(_armci_dummy_work>(double)armci_msg_nproc())_armci_dummy_work=DUMMY_INIT; } /***************************Barrier Code*************************************/ void armci_msg_barr_init(){ #if defined(SYSV) || defined(MMAP) || defined(WIN32) int size=sizeof(barrier_struct)*armci_clus_info[armci_clus_me].nslave; char *tmp; void **ptr_arr; barr_switch=0; /*First allocate space for flags*/ ptr_arr = (void**)malloc(armci_nproc*sizeof(void*)); if(armci_me==armci_master) size = size+128; else size=0; PARMCI_Malloc(ptr_arr, size); tmp = (char*)ptr_arr[armci_master]; size=2*sizeof(int); if(!tmp)armci_die("allocate barr shm failed",0); _bar_buff=(barrier_struct *)tmp; SET_SHM_FLAG(&(BAR_BUF(armci_me-armci_master)->flag1),empty); SET_SHM_FLAG(&(BAR_BUF(armci_me-armci_master)->flag2),empty); /*allocate memory to send/rcv data*/ barr_snd_ptr = (void **)malloc(sizeof(void *)*armci_nproc); barr_rcv_ptr = (void **)malloc(sizeof(void *)*armci_nproc); if(PARMCI_Malloc(barr_snd_ptr,size))armci_die("malloc barr_init failed",0); if(PARMCI_Malloc(barr_rcv_ptr,size))armci_die("malloc barr_init failed",0); if(barr_rcv_ptr[armci_me]==NULL || barr_snd_ptr[armci_me]==NULL) armci_die("problems in malloc barr_init",0); /*we have to figure if we have power of ,two nodes*/ powof2nodes=1; LnB = (int)floor(log(armci_nclus)/log(2))+1; if(pow(2,LnB-1)= root+nproc) left = -1; right = 2*index + 2 + root; if(right >= root+nproc)right = -1; }else if(scope ==SCOPE_MASTERS){ root = armci_clus_info[0].master; nproc = armci_nclus; if(armci_me != armci_master){up = -1; left = -1; right = -1; } else{ index = armci_clus_me - root; up = (index-1)/2 + root; up = ( up < root)? -1: armci_clus_info[up].master; left = 2*index + 1 + root; left = ( left >= root+nproc)? -1: armci_clus_info[left].master; right = 2*index + 2 + root; right =( right >= root+nproc)? -1: armci_clus_info[right].master; } }else{ root = 0; nproc = armci_nproc; index = armci_me - root; up = (index-1)/2 + root; if( up < root) up = -1; left = 2*index + 1 + root; if(left >= root+nproc) left = -1; right = 2*index + 2 + root; if(right >= root+nproc)right = -1; } *Up = up; *Left = left; *Right = right; *Root = root; } /*\ root broadcasts to everyone else \*/ void armci_msg_bcast_scope(int scope, void *buf, int len, int root) { int up, left, right, Root; if(!buf)armci_die("armci_msg_bcast: NULL pointer", len); armci_msg_bintree(scope, &Root, &up, &left, &right); if(root !=Root){ if(armci_me == root) armci_msg_snd(ARMCI_TAG, buf,len, Root); if(armci_me ==Root) armci_msg_rcv(ARMCI_TAG, buf, len, NULL, root); } /* printf("%d: scope=%d left=%d right=%d up=%d\n",armci_me, scope, left, right, up);*/ if(armci_me != Root && up!=-1) armci_msg_rcv(ARMCI_TAG, buf, len, NULL, up); if (left > -1) armci_msg_snd(ARMCI_TAG, buf, len, left); if (right > -1) armci_msg_snd(ARMCI_TAG, buf, len, right); } /*\ shared memory based broadcast for a single SMP node \*/ void armci_smp_bcast(void *x, int n , int root) { int ndo, len,i, bufsize = BUF_SIZE*sizeof(double); static int bufid=1; if(armci_clus_info[armci_clus_me].nslave<2) return; /* nothing to do */ if(!x)armci_die("armci_msg_bcast: NULL pointer", n); /* enable or balance pipeline for messages comparable to bufsize */ if((n>bufsize/2) && (n <(2*bufsize-64))){ bufsize = n/2; bufsize>>=3; bufsize<<=3; } while ((ndo = (n<=bufsize) ? n : bufsize)) { len = ndo; if(armci_me==root){ /* wait for the flag protecting the buffer to clear */ armci_util_wait_int(&(GOP_BUF(armci_clus_last+bufid)->a.flag),EMPTY,100); SET_SHM_FLAG(&(GOP_BUF(armci_clus_last+bufid)->a.flag),full); #if 0 for(i=armci_clus_first; i <= armci_clus_last; i++) if(i!=root)armci_util_wait_int(&GOP_BUF(i)->b.flag, EMPTY, 100); armci_copy(x,GOP_BUF(armci_clus_last+bufid+1)->array,len); for(i=armci_clus_first; i <= armci_clus_last; i++) if(i!=root) GOP_BUF(i)->b.flag=FULL; #else armci_copy(x,GOP_BUF(armci_clus_last+bufid)->array,len); for(i=armci_clus_first; i <= armci_clus_last; i++) if(i!=root){ armci_util_wait_int(&GOP_BUF(i)->b.flag, EMPTY, 100); SET_SHM_FLAG(&(GOP_BUF(i)->b.flag),full); } #endif }else{ armci_util_wait_int(&GOP_BUF(armci_me)->b.flag, FULL, 100); armci_copy(GOP_BUF(armci_clus_last+bufid)->array,x,len); SET_SHM_FLAG(&(GOP_BUF(armci_me)->b.flag),empty); } n -=ndo; x = len + (char*)x; bufid = (bufid)%2 +1; /* since root waited for everybody to check in the previous buffer is free*/ if(armci_me==root){ SET_SHM_FLAG(&(GOP_BUF(armci_clus_last+bufid)->a.flag),empty); } } } /*\ shared memory based broadcast for a single SMP node out of shmem buffer \*/ void armci_smp_buf_bcast(void *x, int n, int root, void *shmbuf ) { int i, nslave = armci_clus_info[armci_clus_me].nslave; if(nslave<2){ armci_copy(shmbuf,x,n); return; /* nothing to do */ } if(!x)armci_die("armci_msg_bcast: NULL pointer", n); if(!shmbuf)armci_die("armci_msg_bcast: NULL pointer", n); if(armci_me==root){ /* notify others that the data in buffer is ready */ for(i=armci_clus_first; i <= armci_clus_last; i++) if(i!=root){ armci_util_wait_int(&GOP_BUF(i)->b.flag, EMPTY, 100); GOP_BUF(i)->b.flag=FULL; } /* root also needs to copy */ armci_copy(shmbuf,x,n); /* wait until everybody is finished -- can reclaim buffer */ for(i=armci_clus_first; i <= armci_clus_last; i++) if(i!=root)armci_util_wait_int(&GOP_BUF(i)->b.flag, EMPTY,100000); }else{ /* spin until data in buffer is ready */ armci_util_wait_int(&GOP_BUF(armci_me)->b.flag , FULL, 100000); armci_copy(shmbuf,x,n); /* copy data */ GOP_BUF(armci_me)->b.flag = EMPTY; /* indicate we are done */ } } void _armci_msg_binomial_bcast(void *buf, int len, int root){ int Root = armci_master; int nslave = armci_clus_info[armci_clus_me].nslave; int i,next_node,next; /* int my_rank,root_rank,next_rank; */ /* inter-node operation between masters */ if(root !=armci_clus_info[0].master){ Root = armci_clus_info[0].master; if(armci_me == root) armci_msg_snd(ARMCI_TAG, buf,len, Root); if(armci_me ==Root) armci_msg_rcv(ARMCI_TAG, buf, len, NULL, root); root = Root; Root = armci_master; } if(armci_nclus>1 &&armci_me==armci_master){/*the internode bcast, first*/ /*first do the recv*/ int rcv_proc=armci_clus_me,flag=1,diff=1; if(armci_me!=root){ while(!(rcv_proc & flag)){ diff=diff<<1; flag=flag<<1; } rcv_proc = armci_clus_info[armci_clus_me-diff].master; armci_msg_rcv(ARMCI_TAG, buf,len,NULL,rcv_proc); /*printf("\n%d: recv from %d \n",armci_me,rcv_proc);fflush(stdout);*/ } else diff = Lp2; /*printf("\n%d: %d diff>>1 = %d\n",armci_me,Lp2,diff>>1);*/ for(i=diff>>1;i>=1;i=i>>1){ next=i^armci_clus_me; if(next>=0 && next1)armci_msg_bcast_scope(SCOPE_MASTERS, buf, len, root); else Root = root; /* intra-node operation */ #if 1 if(_armci_gop_shmem && nslave<33) armci_smp_bcast(buf, len, Root); else #endif armci_msg_bcast_scope(SCOPE_NODE, buf, len, Root); } #endif void armci_msg_brdcst(void* buffer, int len, int root) { if(!buffer)armci_die("armci_msg_brdcast: NULL pointer", len); #if defined(MSG_COMMS_MPI) MPI_Bcast(buffer, len, MPI_CHAR, root, ARMCI_COMM_WORLD); #else { long ttag=ARMCI_TAG, llen=len, rroot=root; tcg_brdcst(ttag, buffer, llen, rroot); } #endif } void armci_msg_snd(int tag, void* buffer, int len, int to) { #ifdef MSG_COMMS_MPI MPI_Send(buffer, len, MPI_CHAR, to, tag, ARMCI_COMM_WORLD); #else { long ttag=tag, llen=len, tto=to, block=1; tcg_snd(ttag, buffer, llen, tto, block); } #endif } /*\ receive message of specified tag from proc and get its len if msglen!=NULL \*/ void armci_msg_rcv(int tag, void* buffer, int buflen, int *msglen, int from) { #ifdef MSG_COMMS_MPI MPI_Status status; MPI_Recv(buffer, buflen, MPI_CHAR, from, tag, ARMCI_COMM_WORLD, &status); if(msglen) MPI_Get_count(&status, MPI_CHAR, msglen); #else long ttag=tag, llen=buflen, mlen, ffrom=from, sender, block=1; tcg_rcv(ttag, buffer, llen, &mlen, ffrom, &sender, block); if(msglen)*msglen = (int)mlen; #endif } int armci_msg_rcvany(int tag, void* buffer, int buflen, int *msglen) { #if defined(MSG_COMMS_MPI) int ierr; MPI_Status status; ierr = MPI_Recv(buffer, buflen, MPI_CHAR, MPI_ANY_SOURCE, tag, ARMCI_COMM_WORLD, &status); if(ierr != MPI_SUCCESS) armci_die("armci_msg_rcvany: Recv failed ", tag); if(msglen)if(MPI_SUCCESS!=MPI_Get_count(&status, MPI_CHAR, msglen)) armci_die("armci_msg_rcvany: count failed ", tag); return (int)status.MPI_SOURCE; # else long ttag=tag, llen=buflen, mlen, ffrom=-1, sender, block=1; tcg_rcv(ttag, buffer, llen, &mlen, ffrom, &sender, block); if(msglen)*msglen = (int)mlen; return (int)sender; # endif } /*\ cluster master broadcasts to everyone else in the same cluster \*/ void armci_msg_clus_brdcst(void *buf, int len) { int root, up, left, right; int tag=ARMCI_TAG, lenmes; armci_msg_bintree(SCOPE_NODE, &root, &up, &left, &right); if(armci_me != root) armci_msg_rcv(tag, buf, len, &lenmes, up); if (left > -1) armci_msg_snd(tag, buf, len, left); if (right > -1) armci_msg_snd(tag, buf, len, right); } /*\ reduce operation for long \*/ static void ldoop(int n, char *op, long *x, long* work) { if (strncmp(op,"+",1) == 0) while(n--) *x++ += *work++; else if (strncmp(op,"*",1) == 0) while(n--) *x++ *= *work++; else if (strncmp(op,"max",3) == 0) while(n--) { *x = ARMCI_MAX(*x, *work); x++; work++; } else if (strncmp(op,"min",3) == 0) while(n--) { *x = ARMCI_MIN(*x, *work); x++; work++; } else if (strncmp(op,"absmax",6) == 0) while(n--) { register long x1 = ARMCI_ABS(*x), x2 = ARMCI_ABS(*work); *x = ARMCI_MAX(x1, x2); x++; work++; } else if (strncmp(op,"absmin",6) == 0) while(n--) { register long x1 = ARMCI_ABS(*x), x2 = ARMCI_ABS(*work); *x = ARMCI_MIN(x1, x2); x++; work++; } else if (strncmp(op,"or",2) == 0) while(n--) { *x |= *work; x++; work++; } /* these are new */ else if ((strncmp(op, "&&", 2) == 0) || (strncmp(op, "land", 4) == 0)) { while(n--) { *x = *x && *work; x++; work++; } } else if ((strncmp(op, "||", 2) == 0) || (strncmp(op, "lor", 3) == 0)) { while(n--) { *x = *x || *work; x++; work++; } } else if ((strncmp(op, "&", 1) == 0) || (strncmp(op, "band", 4) == 0)) { while(n--) { *x &= *work; x++; work++; } } else if ((strncmp(op, "|", 1) == 0) || (strncmp(op, "bor", 3) == 0)) { while(n--) { *x |= *work; x++; work++; } } else armci_die("ldoop: unknown operation requested", n); } /*\ reduce operation for long x= op(work,work2) \*/ static void ldoop2(int n, char *op, long *x, long* work, long* work2) { if (strncmp(op,"+",1) == 0) while(n--) *x++ = *work++ + *work2++; else if (strncmp(op,"*",1) == 0) while(n--) *x++ = *work++ * *work2++; else if (strncmp(op,"max",3) == 0) while(n--) { *x = ARMCI_MAX(*work2, *work); x++; work++; work2++; } else if (strncmp(op,"min",3) == 0) while(n--) { *x = ARMCI_MIN(*work2, *work); x++; work++; work2++; } else if (strncmp(op,"absmax",6) == 0) while(n--) { register long x1 = ARMCI_ABS(*work), x2 = ARMCI_ABS(*work2); *x = ARMCI_MAX(x1, x2); x++; work++; work2++; } else if (strncmp(op,"absmin",6) == 0) while(n--) { register long x1 = ARMCI_ABS(*work), x2 = ARMCI_ABS(*work2); *x = ARMCI_MIN(x1, x2); x++; work++; work2++; } else if (strncmp(op,"or",2) == 0) while(n--) { *x = *work | *work2; x++; work++; work2++; } /* these are new */ else if ((strncmp(op, "&&", 2) == 0) || (strncmp(op, "land", 4) == 0)) { while(n--) { *x = *work && *work2; x++; work++; work2++; } } else if ((strncmp(op, "||", 2) == 0) || (strncmp(op, "lor", 3) == 0)) { while(n--) { *x = *work || *work2; x++; work++; work2++; } } else if ((strncmp(op, "&", 1) == 0) || (strncmp(op, "band", 4) == 0)) { while(n--) { *x = *work & *work2; x++; work++; work2++; } } else if ((strncmp(op, "|", 1) == 0) || (strncmp(op, "bor", 3) == 0)) { while(n--) { *x = *work | *work2; x++; work++; work2++; } } else armci_die("ldoop2: unknown operation requested", n); } /*\ reduce operation for long long \*/ static void lldoop(int n, char *op, long long *x, long long* work) { if (strncmp(op,"+",1) == 0) while(n--) *x++ += *work++; else if (strncmp(op,"*",1) == 0) while(n--) *x++ *= *work++; else if (strncmp(op,"max",3) == 0) while(n--) { *x = ARMCI_MAX(*x, *work); x++; work++; } else if (strncmp(op,"min",3) == 0) while(n--) { *x = ARMCI_MIN(*x, *work); x++; work++; } else if (strncmp(op,"absmax",6) == 0) while(n--) { register long long x1 = ARMCI_ABS(*x), x2 = ARMCI_ABS(*work); *x = ARMCI_MAX(x1, x2); x++; work++; } else if (strncmp(op,"absmin",6) == 0) while(n--) { register long long x1 = ARMCI_ABS(*x), x2 = ARMCI_ABS(*work); *x = ARMCI_MIN(x1, x2); x++; work++; } else if (strncmp(op,"or",2) == 0) while(n--) { *x |= *work; x++; work++; } /* these are new */ else if ((strncmp(op, "&&", 2) == 0) || (strncmp(op, "land", 4) == 0)) { while(n--) { *x = *x && *work; x++; work++; } } else if ((strncmp(op, "||", 2) == 0) || (strncmp(op, "lor", 3) == 0)) { while(n--) { *x = *x || *work; x++; work++; } } else if ((strncmp(op, "&", 1) == 0) || (strncmp(op, "band", 4) == 0)) { while(n--) { *x &= *work; x++; work++; } } else if ((strncmp(op, "|", 1) == 0) || (strncmp(op, "bor", 3) == 0)) { while(n--) { *x |= *work; x++; work++; } } else armci_die("lldoop: unknown operation requested", n); } /*\ reduce operation for long long x= op(work,work2) \*/ static void lldoop2(int n, char *op, long long *x, long long* work, long long* work2) { if (strncmp(op,"+",1) == 0) while(n--) *x++ = *work++ + *work2++; else if (strncmp(op,"*",1) == 0) while(n--) *x++ = *work++ * *work2++; else if (strncmp(op,"max",3) == 0) while(n--) { *x = ARMCI_MAX(*work2, *work); x++; work++; work2++; } else if (strncmp(op,"min",3) == 0) while(n--) { *x = ARMCI_MIN(*work2, *work); x++; work++; work2++; } else if (strncmp(op,"absmax",6) == 0) while(n--) { register long long x1 = ARMCI_ABS(*work), x2 = ARMCI_ABS(*work2); *x = ARMCI_MAX(x1, x2); x++; work++; work2++; } else if (strncmp(op,"absmin",6) == 0) while(n--) { register long long x1 = ARMCI_ABS(*work), x2 = ARMCI_ABS(*work2); *x = ARMCI_MIN(x1, x2); x++; work++; work2++; } else if (strncmp(op,"or",2) == 0) while(n--) { *x = *work | *work2; x++; work++; work2++; } /* these are new */ else if ((strncmp(op, "&&", 2) == 0) || (strncmp(op, "land", 4) == 0)) { while(n--) { *x = *work && *work2; x++; work++; work2++; } } else if ((strncmp(op, "||", 2) == 0) || (strncmp(op, "lor", 3) == 0)) { while(n--) { *x = *work || *work2; x++; work++; work2++; } } else if ((strncmp(op, "&", 1) == 0) || (strncmp(op, "band", 4) == 0)) { while(n--) { *x = *work & *work2; x++; work++; work2++; } } else if ((strncmp(op, "|", 1) == 0) || (strncmp(op, "bor", 3) == 0)) { while(n--) { *x = *work | *work2; x++; work++; work2++; } } else armci_die("ldoop2: unknown operation requested", n); } /*\ reduce operation for int \*/ static void idoop(int n, char *op, int *x, int* work) { if (strncmp(op,"+",1) == 0) while(n--) *x++ += *work++; else if (strncmp(op,"*",1) == 0) while(n--) *x++ *= *work++; else if (strncmp(op,"max",3) == 0) while(n--) { *x = ARMCI_MAX(*x, *work); x++; work++; } else if (strncmp(op,"min",3) == 0) while(n--) { *x = ARMCI_MIN(*x, *work); x++; work++; } else if (strncmp(op,"absmax",6) == 0) while(n--) { register int x1 = ARMCI_ABS(*x), x2 = ARMCI_ABS(*work); *x = ARMCI_MAX(x1, x2); x++; work++; } else if (strncmp(op,"absmin",6) == 0) while(n--) { register int x1 = ARMCI_ABS(*x), x2 = ARMCI_ABS(*work); *x = ARMCI_MIN(x1, x2); x++; work++; } else if (strncmp(op,"or",2) == 0) while(n--) { *x |= *work; x++; work++; } /* these are new */ else if ((strncmp(op, "&&", 2) == 0) || (strncmp(op, "land", 4) == 0)) { while(n--) { *x = *x && *work; x++; work++; } } else if ((strncmp(op, "||", 2) == 0) || (strncmp(op, "lor", 3) == 0)) { while(n--) { *x = *x || *work; x++; work++; } } else if ((strncmp(op, "&", 1) == 0) || (strncmp(op, "band", 4) == 0)) { while(n--) { *x &= *work; x++; work++; } } else if ((strncmp(op, "|", 1) == 0) || (strncmp(op, "bor", 3) == 0)) { while(n--) { *x |= *work; x++; work++; } } else armci_die("idoop: unknown operation requested", n); } /*\ reduce operation for int x= op(work,work2) \*/ static void idoop2(int n, char *op, int *x, int* work, int* work2) { if (strncmp(op,"+",1) == 0) while(n--) *x++ = *work++ + *work2++; else if (strncmp(op,"*",1) == 0) while(n--) *x++ = *work++ * *work2++; else if (strncmp(op,"max",3) == 0) while(n--) { *x = ARMCI_MAX(*work2, *work); x++; work++; work2++; } else if (strncmp(op,"min",3) == 0) while(n--) { *x = ARMCI_MIN(*work2, *work); x++; work++; work2++; } else if (strncmp(op,"absmax",6) == 0) while(n--) { register int x1 = ARMCI_ABS(*work), x2 = ARMCI_ABS(*work2); *x = ARMCI_MAX(x1, x2); x++; work++; work2++; } else if (strncmp(op,"absmin",6) == 0) while(n--) { register int x1 = ARMCI_ABS(*work), x2 = ARMCI_ABS(*work2); *x = ARMCI_MIN(x1, x2); x++; work++; work2++; } else if (strncmp(op,"or",2) == 0) while(n--) { *x = *work | *work2; x++; work++; work2++; } /* these are new */ else if ((strncmp(op, "&&", 2) == 0) || (strncmp(op, "land", 4) == 0)) { while(n--) { *x = *work && *work2; x++; work++; work2++; } } else if ((strncmp(op, "||", 2) == 0) || (strncmp(op, "lor", 3) == 0)) { while(n--) { *x = *work || *work2; x++; work++; work2++; } } else if ((strncmp(op, "&", 1) == 0) || (strncmp(op, "band", 4) == 0)) { while(n--) { *x = *work & *work2; x++; work++; work2++; } } else if ((strncmp(op, "|", 1) == 0) || (strncmp(op, "bor", 3) == 0)) { while(n--) { *x = *work | *work2; x++; work++; work2++; } } else armci_die("idoop2: unknown operation requested", n); } /*\ reduce operation for double \*/ static void ddoop(int n, char* op, double* x, double* work) { if (strncmp(op,"+",1) == 0){ if(n>63) FORT_DADD(&n,x,work); else while(n--) *x++ += *work++; }else if (strncmp(op,"*",1) == 0){ if(n>63) FORT_DMULT(&n,x,work); else while(n--) *x++ *= *work++; }else if (strncmp(op,"max",3) == 0) while(n--) { *x = ARMCI_MAX(*x, *work); x++; work++; } else if (strncmp(op,"min",3) == 0) while(n--) { *x = ARMCI_MIN(*x, *work); x++; work++; } else if (strncmp(op,"absmax",6) == 0) while(n--) { register double x1 = ARMCI_ABS(*x), x2 = ARMCI_ABS(*work); *x = ARMCI_MAX(x1, x2); x++; work++; } else if (strncmp(op,"absmin",6) == 0) while(n--) { register double x1 = ARMCI_ABS(*x), x2 = ARMCI_ABS(*work); *x = ARMCI_MIN(x1, x2); x++; work++; } else armci_die("ddoop: unknown operation requested", n); } /*\ reduce operation for double x= op(work,work2) \*/ static void ddoop2(int n, char *op, double *x, double* work, double* work2) { if (strncmp(op,"+",1) == 0){ if(n>63) FORT_DADD2(&n,x,work,work2); else while(n--) *x++ = *work++ + *work2++; }else if (strncmp(op,"*",1) == 0){ if(n>63) FORT_DMULT2(&n,x,work,work2); while(n--) *x++ = *work++ * *work2++; }else if (strncmp(op,"max",3) == 0) while(n--) { *x = ARMCI_MAX(*work2, *work); x++; work++; work2++; } else if (strncmp(op,"min",3) == 0) while(n--) { *x = ARMCI_MIN(*work2, *work); x++; work++; work2++; } else if (strncmp(op,"absmax",6) == 0) while(n--) { register double x1 = ARMCI_ABS(*work), x2 = ARMCI_ABS(*work2); *x = ARMCI_MAX(x1, x2); x++; work++; work2++; } else if (strncmp(op,"absmin",6) == 0) while(n--) { register double x1 = ARMCI_ABS(*work), x2 = ARMCI_ABS(*work2); *x = ARMCI_MIN(x1, x2); x++; work++; work2++; } else armci_die("ddoop2: unknown operation requested", n); } /*\ reduce operation for float \*/ static void fdoop(int n, char* op, float* x, float* work) { if (strncmp(op,"+",1) == 0) while(n--) *x++ += *work++; else if (strncmp(op,"*",1) == 0) while(n--) *x++ *= *work++; else if (strncmp(op,"max",3) == 0) while(n--) { *x = ARMCI_MAX(*x, *work); x++; work++; } else if (strncmp(op,"min",3) == 0) while(n--) { *x = ARMCI_MIN(*x, *work); x++; work++; } else if (strncmp(op,"absmax",6) == 0) while(n--) { register float x1 = ARMCI_ABS(*x), x2 = ARMCI_ABS(*work); *x = ARMCI_MAX(x1, x2); x++; work++; } else if (strncmp(op,"absmin",6) == 0) while(n--) { register float x1 = ARMCI_ABS(*x), x2 = ARMCI_ABS(*work); *x = ARMCI_MIN(x1, x2); x++; work++; } else armci_die("fdoop: unknown operation requested", n); } /*\ reduce operation for float x= op(work,work2) \*/ static void fdoop2(int n, char *op, float *x, float* work, float* work2) { if (strncmp(op,"+",1) == 0) while(n--) *x++ = *work++ + *work2++; else if (strncmp(op,"*",1) == 0) while(n--) *x++ = *work++ * *work2++; else if (strncmp(op,"max",3) == 0) while(n--) { *x = ARMCI_MAX(*work2, *work); x++; work++; work2++; } else if (strncmp(op,"min",3) == 0) while(n--) { *x = ARMCI_MIN(*work2, *work); x++; work++; work2++; } else if (strncmp(op,"absmax",6) == 0) while(n--) { register float x1 = ARMCI_ABS(*work), x2 = ARMCI_ABS(*work2); *x = ARMCI_MAX(x1, x2); x++; work++; work2++; } else if (strncmp(op,"absmin",6) == 0) while(n--) { register float x1 = ARMCI_ABS(*work), x2 = ARMCI_ABS(*work2); *x = ARMCI_MIN(x1, x2); x++; work++; work2++; } else armci_die("fdoop2: unknown operation requested", n); } /*\ combine array of longs/ints accross all processes \*/ void armci_msg_gop_scope(int scope, void *x, int n, char* op, int type) { int root, up, left, right, size; int tag=ARMCI_TAG; int ndo, len, lenmes, orign =n, ratio; void *origx =x; if(!x)armci_die("armci_msg_gop: NULL pointer", n); if(work==NULL)_allocate_mem_for_work(); { armci_msg_bintree(scope, &root, &up, &left, &right); if(type==ARMCI_INT) size = sizeof(int); else if(type==ARMCI_LONG) size = sizeof(long); else if(type==ARMCI_LONG_LONG) size = sizeof(long long); else if(type==ARMCI_FLOAT) size = sizeof(float); else size = sizeof(double); ratio = sizeof(double)/size; while ((ndo = (n<=BUF_SIZE*ratio) ? n : BUF_SIZE*ratio)) { len = lenmes = ndo*size; if (left > -1) { armci_msg_rcv(tag, lwork, len, &lenmes, left); if(type==ARMCI_INT) idoop(ndo, op, (int*)x, iwork); else if(type==ARMCI_LONG) ldoop(ndo, op, (long*)x, lwork); else if(type==ARMCI_LONG_LONG) lldoop(ndo, op,(long long*)x,llwork); else if(type==ARMCI_FLOAT) fdoop(ndo, op, (float*)x, fwork); else ddoop(ndo, op, (double*)x, work); } if (right > -1) { armci_msg_rcv(tag, lwork, len, &lenmes, right); if(type==ARMCI_INT) idoop(ndo, op, (int*)x, iwork); else if(type==ARMCI_LONG) ldoop(ndo, op, (long*)x, lwork); else if(type==ARMCI_LONG_LONG) lldoop(ndo, op,(long long*)x,llwork); else if(type==ARMCI_FLOAT) fdoop(ndo, op, (float*)x, fwork); else ddoop(ndo, op, (double*)x, work); } if (armci_me != root && up!=-1) armci_msg_snd(tag, x, len, up); n -=ndo; x = len + (char*)x; } /* Now, root broadcasts the result down the binary tree */ len = orign*size; armci_msg_bcast_scope(scope, origx, len, root); } } void armci_msg_reduce_scope(int scope, void *x, int n, char* op, int type) { int root, up, left, right, size; int tag=ARMCI_TAG; int ndo, len, lenmes, ratio; if(!x)armci_die("armci_msg_gop: NULL pointer", n); if(work==NULL)_allocate_mem_for_work(); armci_msg_bintree(scope, &root, &up, &left, &right); if(type==ARMCI_INT) size = sizeof(int); else if(type==ARMCI_LONG) size = sizeof(long); else if(type==ARMCI_LONG_LONG) size = sizeof(long long); else if(type==ARMCI_FLOAT) size = sizeof(float); else size = sizeof(double); ratio = sizeof(double)/size; while ((ndo = (n<=BUF_SIZE*ratio) ? n : BUF_SIZE*ratio)) { len = lenmes = ndo*size; if (left > -1) { armci_msg_rcv(tag, lwork, len, &lenmes, left); if(type==ARMCI_INT) idoop(ndo, op, (int*)x, iwork); else if(type==ARMCI_LONG) ldoop(ndo, op, (long*)x, lwork); else if(type==ARMCI_LONG_LONG) lldoop(ndo, op,(long long*)x,llwork); else if(type==ARMCI_FLOAT) fdoop(ndo, op, (float*)x, fwork); else ddoop(ndo, op, (double*)x, work); } if (right > -1) { armci_msg_rcv(tag, lwork, len, &lenmes, right); if(type==ARMCI_INT) idoop(ndo, op, (int*)x, iwork); else if(type==ARMCI_LONG) ldoop(ndo, op, (long*)x, lwork); else if(type==ARMCI_LONG_LONG) lldoop(ndo, op,(long long*)x,llwork); else if(type==ARMCI_FLOAT) fdoop(ndo, op, (float*)x, fwork); else ddoop(ndo, op, (double*)x, work); } if (armci_me != root && up!=-1) armci_msg_snd(tag, x, len, up); n -=ndo; x = len + (char*)x; } } static void gop(int type, int ndo, char* op, void *x, void *work) { if(type==ARMCI_INT) idoop(ndo, op, (int*)x, (int*)work); else if(type==ARMCI_LONG) ldoop(ndo, op, (long*)x, (long*)work); else if(type==ARMCI_LONG_LONG) lldoop(ndo, op, (long long*)x, (long long*)work); else if(type==ARMCI_FLOAT) fdoop(ndo, op, (float*)x, (float*)work); else ddoop(ndo, op, (double*)x, (double*)work); } static void gop2(int type, int ndo, char* op, void *x, void *work, void *work2) { #if 0 int size; if(type==ARMCI_INT) size = sizeof(int); else if(type==ARMCI_LONG) size = sizeof(long); else if(type==ARMCI_LONG_LONG) size = sizeof(long long); else if(type==ARMCI_FLOAT) size = sizeof(float); else size = sizeof(double); armci_copy(work2,x,ndo*size); if(type==ARMCI_INT) idoop(ndo, op, (int*)x, (int*)work); else if(type==ARMCI_LONG) ldoop(ndo, op, (long*)x, (long*)work); else if(type==ARMCI_LONG_LONG) lldoop(ndo, op, (long long*)x, (long long*)work); else if(type==ARMCI_FLOAT) fdoop(ndo, op, (float*)x, (float*)work); else ddoop(ndo, op, (double*)x, (double*)work); #else if(type==ARMCI_INT) idoop2(ndo, op, (int*)x, (int*)work, (int*)work2); else if(type==ARMCI_LONG)ldoop2(ndo,op,(long*)x,(long*)work,(long*)work2); else if(type==ARMCI_LONG_LONG) lldoop2(ndo,op,(long long*)x,(long long*)work,(long long*)work2); else if(type==ARMCI_FLOAT)fdoop2(ndo,op,(float*)x,(float*)work,(float*)work2); else ddoop2(ndo, op, (double*)x, (double*)work,(double*)work2); #endif } /*\ shared memory based reduction for a single SMP node \*/ static void armci_smp_reduce(void *x, int n, char* op, int type) { int root, up, left, right, size; int ndo, len, lenmes, ratio; int nslave = armci_clus_info[armci_clus_me].nslave; if(nslave<2) return; /* nothing to do */ if(!x)armci_die("armci_msg_gop: NULL pointer", n); armci_msg_bintree(SCOPE_NODE, &root, &up, &left, &right); if(type==ARMCI_INT) size = sizeof(int); else if(type==ARMCI_LONG) size = sizeof(long); else if(type==ARMCI_LONG_LONG) size = sizeof(long long); else if(type==ARMCI_FLOAT) size = sizeof(float); else size = sizeof(double); ratio = sizeof(double)/size; while ((ndo = (n<=BUF_SIZE*ratio) ? n : BUF_SIZE*ratio)) { len = lenmes = ndo*size; armci_util_wait_int(&GOP_BUF(armci_me)->a.flag, EMPTY, 100); #if 1 if(left<0 && right<0) armci_copy(x,GOP_BUF(armci_me)->array,len); /* version oblivious to the order of data arrival */ { int need_left = left >-1; int need_right = right >-1; int from, first =1, maxspin=100, count=0; bufstruct *b; while(need_left || need_right){ from =-1; if(need_left && GOP_BUF(left)->a.flag == FULL){ from =left; need_left =0; }else if(need_right && GOP_BUF(right)->a.flag == FULL) { from =right; need_right =0; } if(from != -1){ b = GOP_BUF(from); #if 1 if(armci_me == root) gop(type, ndo, op, x, b->array); else { if(first) gop2(type, ndo, op, GOP_BUF(armci_me)->array, b->array,x); else gop(type, ndo, op, GOP_BUF(armci_me)->array, b->array); } first =0; #else gop(type, ndo, op, GOP_BUF(armci_me)->array, b->array); #endif SET_SHM_FLAG(&( b->a.flag),empty); }else if((++count)array,len); /* this version requires a specific order of data arrival */ if (left >-1) { while(GOP_BUF(left)->a.flag != FULL) cpu_yield(); gop(type, ndo, op, GOP_BUF(armci_me)->array, GOP_BUF(left)->array); SET_SHM_FLAG(&( GOP_BUF(left)->a.flag),empty); } if (right >-1 ) { while(GOP_BUF(right)->a.flag != FULL) cpu_yield(); gop(type, ndo, op, GOP_BUF(armci_me)->array, GOP_BUF(right)->array); GOP_BUF(right)->a.flag = EMPTY; } #endif if (armci_me != root ) { SET_SHM_FLAG(&(GOP_BUF(armci_me)->a.flag),full); } #if 0 else /* NOTE: this copy can be eliminated in a cluster configuration */ armci_copy(GOP_BUF(armci_me)->array,x,len); #endif n -=ndo; x = len + (char*)x; } } void _armci_msg_binomial_reduce(void *x, int n, char* op, int type){ int root = armci_clus_info[0].master; int i,next_node,next; int size, ratio, ndo, lenmes,len; /* int my_rank,root_rank,next_rank; */ if(work==NULL)_allocate_mem_for_work(); if(armci_me!=armci_master)return; if(type==ARMCI_INT) size = sizeof(int); else if(type==ARMCI_LONG) size = sizeof(long); else if(type==ARMCI_LONG_LONG) size = sizeof(long long); else if(type==ARMCI_FLOAT) size = sizeof(float); else size = sizeof(double); ratio = sizeof(double)/size; while ((ndo = (n<=BUF_SIZE*ratio) ? n : BUF_SIZE*ratio)) { int snd_proc=armci_clus_me,flag=1,diff=1; len = lenmes = ndo*size; if(armci_me!=root){ while(!(snd_proc & flag)){ diff=diff<<1; flag=flag<<1; } snd_proc = armci_clus_info[armci_clus_me-diff].master; } else diff = Lp2; /*printf("\n%d: %d diff>>1 = %d\n",armci_me,Lp2,diff>>1);*/ for(i=diff>>1;i>=1;i=i>>1){ next=i^armci_clus_me; if(next>=0 && next1){ armci_msg_reduce_scope(SCOPE_MASTERS, x, n, op, type); } } static void armci_msg_gop2(void *x, int n, char* op, int type) { int size, root=0; if (work==NULL) { _allocate_mem_for_work(); } if (type==ARMCI_INT) { size = sizeof(int); } else if (type==ARMCI_LONG) { size = sizeof(long); } else if (type==ARMCI_LONG_LONG) { size = sizeof(long long); } else if (type==ARMCI_FLOAT) { size = sizeof(float); } else { size = sizeof(double); } armci_msg_reduce(x, n, op, type); armci_msg_bcast(x, size*n, root); } static void armci_sel(int type, char *op, void *x, void* work, int n) { int selected=0; switch (type) { case ARMCI_INT: if(strncmp(op,"min",3) == 0){ if(*(int*)x > *(int*)work) selected=1; }else if(*(int*)x < *(int*)work) selected=1; break; case ARMCI_LONG: if(strncmp(op,"min",3) == 0){ if(*(long*)x > *(long*)work) selected=1; }else if(*(long*)x < *(long*)work) selected=1; break; case ARMCI_LONG_LONG: if(strncmp(op,"min",3) == 0){ if(*(long long*)x > *(long long*)work) selected=1; }else if(*(long long*)x < *(long long*)work) selected=1; break; case ARMCI_FLOAT: if(strncmp(op,"min",3) == 0){ if(*(float*)x > *(float*)work) selected=1; }else if(*(float*)x < *(float*)work) selected=1; break; default: if(strncmp(op,"min",3) == 0){ if(*(double*)x > *(double*)work) selected=1; }else if(*(double*)x < *(double*)work) selected=1; } if(selected)armci_copy(work,x, n); } /*\ global for op with extra info \*/ void armci_msg_sel_scope(int scope, void *x, int n, char* op, int type, int contribute) { int root, up, left, right; int tag=ARMCI_TAG; int len, lenmes, min; min = (strncmp(op,"min",3) == 0); if(!min && (strncmp(op,"max",3) != 0)) armci_die("armci_msg_gop_info: operation not supported ", 0); if(!x)armci_die("armci_msg_gop_info: NULL pointer", n); if(n>((int)INFO_BUF_SIZE))armci_die("armci_msg_gop_info: info too large",n); len = lenmes = n; armci_msg_bintree(scope, &root, &up, &left, &right); if (left > -1) { /* receive into work if contributing otherwise into x */ if(contribute)armci_msg_rcv(tag, work, len, &lenmes, left); else armci_msg_rcv(tag, x, len, &lenmes, left); if(lenmes){ if(contribute) armci_sel(type, op, x, work, n); else contribute =1; /* now we got data to pass */ } } if (right > -1) { /* receive into work if contributing otherwise into x */ if(contribute) armci_msg_rcv(tag, work, len, &lenmes, right); else armci_msg_rcv(tag, x, len, &lenmes, right); if(lenmes){ if(contribute) armci_sel(type, op, x, work, n); else contribute =1; /* now we got data to pass */ } } if (armci_me != root){ if(contribute) armci_msg_snd(tag, x, len, up); else armci_msg_snd(tag, x, 0, up); /* send 0 bytes */ } /* Now, root broadcasts the result down the binary tree */ armci_msg_bcast_scope(scope, x, n, root); } /*\ combine array of longs/ints/doubles accross all processes \*/ void armci_msg_igop(int *x, int n, char* op) { armci_msg_gop2(x, n, op, ARMCI_INT); } void armci_msg_lgop(long *x, int n, char* op) { armci_msg_gop2(x, n, op, ARMCI_LONG); } void armci_msg_llgop(long long *x, int n, char* op) { armci_msg_gop2(x, n, op, ARMCI_LONG_LONG); } void armci_msg_fgop(float *x, int n, char* op) { armci_msg_gop2(x, n, op, ARMCI_FLOAT); } void armci_msg_dgop(double *x, int n, char* op) { armci_msg_gop2(x, n, op, ARMCI_DOUBLE); } /*\ add array of longs/ints within the same cluster node \*/ void armci_msg_clus_igop(int *x, int n, char* op) { armci_msg_gop_scope(SCOPE_NODE,x, n, op, ARMCI_INT); } void armci_msg_clus_lgop(long *x, int n, char* op) { armci_msg_gop_scope(SCOPE_NODE,x, n, op, ARMCI_LONG); } void armci_msg_clus_llgop(long long *x, int n, char* op) { armci_msg_gop_scope(SCOPE_NODE,x, n, op, ARMCI_LONG_LONG); } void armci_msg_clus_fgop(float *x, int n, char* op) { armci_msg_gop_scope(SCOPE_NODE,x, n, op, ARMCI_FLOAT); } void armci_msg_clus_dgop_scope(double *x, int n, char* op) { armci_msg_gop_scope(SCOPE_NODE,x, n, op, ARMCI_DOUBLE); } void armci_exchange_address(void *ptr_ar[], int n) { int ratio = sizeof(void*)/sizeof(int); /* armci_msg_lgop((long*)ptr_ar, n, "+"); */ if(DEBUG_)printf("%d: exchanging %ld ratio=%d\n",armci_me,(long)ptr_ar[armci_me],ratio); armci_msg_gop2(ptr_ar, n*ratio, "+",ARMCI_INT); } /** * ********************* Begin ARMCI Groups Code **************************** * NOTE: This part is MPI dependent (i.e. ifdef MSG_COMMS_MPI) */ #ifdef MSG_COMMS_MPI MPI_Comm armci_group_comm(ARMCI_Group *group) { #ifdef ARMCI_GROUP return MPI_COMM_NULL; #else ARMCI_iGroup *igroup = armci_get_igroup_from_group(group); return (MPI_Comm)igroup->icomm; #endif } void parmci_msg_group_barrier(ARMCI_Group *group) { ARMCI_iGroup *igroup = armci_get_igroup_from_group(group); #ifdef ARMCI_GROUP { int val=0; armci_msg_group_igop(&val, 1, "+", group); } #else MPI_Barrier((MPI_Comm)(igroup->icomm)); #endif } #ifdef ARMCI_GROUP extern void ARMCI_Bcast_(void *buffer, int len, int root, ARMCI_Group *group); #else extern void ARMCI_Bcast_(void *buffer, int len, int root, ARMCI_Comm comm); #endif void armci_grp_clus_brdcst(void *buf, int len, int grp_master, int grp_clus_nproc, ARMCI_Group *mastergroup) { ARMCI_iGroup *igroup = armci_get_igroup_from_group(mastergroup); int i, *pid_list, root=0; #ifdef ARMCI_GROUP ARMCI_Group group; #else MPI_Group group_world; MPI_Group group; MPI_Comm comm; #endif /* create a communicator for the processes with in a node */ pid_list = (int *)malloc(grp_clus_nproc*sizeof(int)); for(i=0; iicomm), &group_world); MPI_Group_incl(group_world, grp_clus_nproc, pid_list, &group); MPI_Comm_create((MPI_Comm)(igroup->icomm), (MPI_Group)group, (MPI_Comm*)&comm); /* Broadcast within the node (for this sub group of processes) */ ARMCI_Bcast_(buf, len, root, comm); free(pid_list); MPI_Comm_free(&comm); /* free the temporary communicator */ MPI_Group_free(&group); #endif } /* to avoid warning */ extern int ARMCI_Absolute_id(ARMCI_Group *group,int group_rank); void armci_msg_group_bintree(int scope, int* Root, int *Up, int *Left, int *Right, ARMCI_Group *group) { int root, up, left, right, index, nproc,grp_clus_me,grp_me,grp_master,grp_nproc; armci_grp_attr_t *grp_attr=ARMCI_Group_getattr(group); grp_me = grp_attr->grp_me; grp_clus_me = grp_attr->grp_clus_me; grp_master = grp_attr->grp_clus_info[grp_clus_me].master; ARMCI_Group_size(group, &grp_nproc); if(scope == SCOPE_NODE){ root = grp_attr->grp_clus_info[grp_clus_me].master; nproc = grp_attr->grp_clus_info[grp_clus_me].nslave; index = grp_me - root; up = (index-1)/2 + root; if( up < root) up = -1; left = 2*index + 1 + root; if(left >= root+nproc) left = -1; right = 2*index + 2 + root; if(right >= root+nproc)right = -1; }else if(scope ==SCOPE_MASTERS){ root = grp_attr->grp_clus_info[0].master; nproc = grp_attr->grp_nclus; if(grp_me != grp_master){up = -1; left = -1; right = -1; } else{ index = grp_clus_me - root; up = (index-1)/2 + root; up = ( up < root)? -1: grp_attr->grp_clus_info[up].master; left = 2*index + 1 + root; left =( left >= root+nproc)?-1:grp_attr->grp_clus_info[left].master; right= 2*index + 2 + root; right=( right>=root+nproc)?-1:grp_attr->grp_clus_info[right].master; } }else{ root = 0; nproc = grp_nproc; index = grp_me - root; up = (index-1)/2 + root; if( up < root) up = -1; left = 2*index + 1 + root; if(left >= root+nproc) left = -1; right = 2*index + 2 + root; if(right >= root+nproc)right = -1; } *Up = (up==-1)?up:ARMCI_Absolute_id(group,up); *Left = (left==-1)?left:ARMCI_Absolute_id(group,left); *Right = (right==-1)?right:ARMCI_Absolute_id(group,right); *Root = (root==-1)?root:ARMCI_Absolute_id(group,root); } void armci_msg_group_bcast_scope(int scope, void *buf, int len, int root, ARMCI_Group *group) { int up, left, right, Root; if(!buf)armci_die("armci_msg_bcast: NULL pointer", len); if(!group)armci_msg_bcast_scope(scope,buf,len,root); armci_msg_group_bintree(scope, &Root, &up, &left, &right,group); if(root !=Root){ if(armci_me == root) armci_msg_snd(ARMCI_TAG, buf,len, Root); if(armci_me ==Root) armci_msg_rcv(ARMCI_TAG, buf, len, NULL, root); } /* printf("%d: scope=%d left=%d right=%d up=%d\n",armci_me, scope, left, right, up);*/ if(armci_me != Root && up!=-1) armci_msg_rcv(ARMCI_TAG, buf, len, NULL, up); if (left > -1) armci_msg_snd(ARMCI_TAG, buf, len, left); if (right > -1) armci_msg_snd(ARMCI_TAG, buf, len, right); } void armci_msg_group_gop_scope(int scope, void *x, int n, char* op, int type, ARMCI_Group *group) { int root, up, left, right, size; int tag=ARMCI_TAG; int ndo, len, lenmes, orign =n, ratio; void *origx =x; if(!group)armci_msg_gop_scope(scope,x,n,op,type); if(!x)armci_die("armci_msg_gop: NULL pointer", n); if(work==NULL)_allocate_mem_for_work(); armci_msg_group_bintree(scope, &root, &up, &left, &right,group); if(type==ARMCI_INT) size = sizeof(int); else if(type==ARMCI_LONG) size = sizeof(long); else if(type==ARMCI_LONG_LONG) size = sizeof(long long); else if(type==ARMCI_FLOAT) size = sizeof(float); else size = sizeof(double); ratio = sizeof(double)/size; while ((ndo = (n<=BUF_SIZE*ratio) ? n : BUF_SIZE*ratio)) { len = lenmes = ndo*size; if (left > -1) { armci_msg_rcv(tag, lwork, len, &lenmes, left); if(type==ARMCI_INT) idoop(ndo, op, (int*)x, iwork); else if(type==ARMCI_LONG) ldoop(ndo, op, (long*)x, lwork); else if(type==ARMCI_LONG_LONG) lldoop(ndo, op, (long long*)x,llwork); else if(type==ARMCI_FLOAT) fdoop(ndo, op, (float*)x, fwork); else ddoop(ndo, op, (double*)x, work); } if (right > -1) { armci_msg_rcv(tag, lwork, len, &lenmes, right); if(type==ARMCI_INT) idoop(ndo, op, (int*)x, iwork); else if(type==ARMCI_LONG) ldoop(ndo, op, (long*)x, lwork); else if(type==ARMCI_LONG_LONG) lldoop(ndo, op,(long long*)x, llwork); else if(type==ARMCI_FLOAT) fdoop(ndo, op, (float*)x, fwork); else ddoop(ndo, op, (double*)x, work); } if (armci_me != root && up!=-1) armci_msg_snd(tag, x, len, up); n -=ndo; x = len + (char*)x; } /* Now, root broadcasts the result down the binary tree */ len = orign*size; armci_msg_group_bcast_scope(scope, origx, len, root,group); } void armci_exchange_address_grp(void *ptr_arr[], int n, ARMCI_Group *group) { int ratio = sizeof(void*)/sizeof(int); ARMCI_iGroup *igroup = armci_get_igroup_from_group(group); int grp_me = igroup->grp_attr.grp_me; if(DEBUG_){ printf("%d: exchanging %ld ratio=%d\n",armci_me, (long)ptr_arr[grp_me], ratio); } armci_msg_group_gop_scope(SCOPE_ALL, ptr_arr, n*ratio, "+", ARMCI_INT, group); } /*\ combine array of longs/ints/doubles accross all processes \*/ void armci_msg_group_igop(int *x, int n, char* op, ARMCI_Group *group) { armci_msg_group_gop_scope(SCOPE_ALL,x, n, op, ARMCI_INT,group); } void armci_msg_group_lgop(long *x, int n, char* op,ARMCI_Group *group) { armci_msg_group_gop_scope(SCOPE_ALL,x, n, op, ARMCI_LONG,group); } void armci_msg_group_llgop(long long *x, int n, char* op,ARMCI_Group *group) { armci_msg_group_gop_scope(SCOPE_ALL,x, n, op, ARMCI_LONG_LONG,group); } void armci_msg_group_fgop(float *x, int n, char* op,ARMCI_Group *group) { armci_msg_group_gop_scope(SCOPE_ALL,x, n, op, ARMCI_FLOAT,group); } void armci_msg_group_dgop(double *x, int n, char* op,ARMCI_Group *group) { armci_msg_group_gop_scope(SCOPE_ALL,x, n, op, ARMCI_DOUBLE,group); } # endif /* ifdef MSG_COMMS_MPI */ /*********************** End ARMCI Groups Code ****************************/ ga-5.9.2/armci/src/common/000077500000000000000000000000001500715745200152765ustar00rootroot00000000000000ga-5.9.2/armci/src/common/aggregate.c000066400000000000000000000271641500715745200174020ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** $Id: aggregate.c,v 1.6 2003-10-22 22:12:14 d3h325 Exp $ * Aggregate Put/Get requests */ #include "armcip.h" #if HAVE_STRING_H # include /* memcpy */ #endif #if HAVE_STDIO_H # include #endif #define _MAX_AGG_BUFFERS 32 /* Maximum # of aggregation buffers available*/ #define _MAX_AGG_BUFSIZE 2048 /* size of each buffer. should be < 2^15 */ #define _MAX_PTRS 256 /* < 2^15, as it is "short int" in agg_req_t */ #define _MAX_AGG_HANDLE _MAX_AGG_BUFFERS /* Max # of aggregation handles */ /* aggregate request handle */ typedef struct { unsigned int tag; /* non-blocking request tag */ short int proc; /* remote process id */ short int request_len ; /* number of requests */ short int ptr_array_len; /* pointer length for this request */ short int buf_pos_end; /* position of buffer (from right end) */ armci_giov_t *darr; /* giov vectors */ }agg_req_t; static agg_req_t *aggr[_MAX_AGG_HANDLE]; /* aggregate request handle */ /* data structure for dynamic buffer management */ typedef struct { int size; /* represents the size of the list (not linked list) */ int index[_MAX_AGG_HANDLE]; } agg_list_t; static agg_list_t ulist, alist;/*in-use & available aggr buffer index list*/ /* aggregation buffer */ static char agg_buf[_MAX_AGG_BUFFERS][_MAX_AGG_BUFSIZE]; /* aggregation buffer to store the pointers */ static void* agg_src_ptr[_MAX_AGG_BUFFERS][_MAX_PTRS]; static void* agg_dst_ptr[_MAX_AGG_BUFFERS][_MAX_PTRS]; /** * --------------------------------------------------------------------- * fill descriptor from this side (left to right) * ---> * _______________________________________________ * | | | |. . . . . . . . . . | | | | * |__|__|__|_____________________________|__|__|__| * * <--- * fill src and dst pointer (arrays) in this direction * (right to left) * * Once they are about to cross each other (implies buffer is full), * complete the data transfer. * --------------------------------------------------------------------- */ #define AGG_INIT_NB_HANDLE(op_type, p, nb_handle) \ if(nb_handle->proc < 0) { \ nb_handle->tag = GET_NEXT_NBTAG(); \ nb_handle->op = op_type; \ nb_handle->proc = p; \ nb_handle->bufid= NB_NONE; \ } \ else if(nb_handle->op != op_type) \ armci_die("ARMCI_NbXXX: AGG_INIT_NB_HANDLE(): Aggregate Failed, Invalid non-blocking handle", nb_handle->op); \ else if(nb_handle->proc != p) \ armci_die("ARMCI_NbXXX: AGG_INIT_NB_HANDLE(): Aggregate Failed, Invalid non-blocking handle", p) /* initialize/set the fields in the buffer*/ #define _armci_agg_set_buffer(index, tag, proc, len) { \ aggr[(index)]->tag = (tag); \ aggr[(index)]->proc = (proc); \ aggr[(index)]->request_len = (len); \ ulist.index[ulist.size++] = (index);/* add the new index to the in-use list and increment it's size*/ \ } /* get the index of the aggregation buffer to be used */ static int _armci_agg_get_bufferid(armci_ihdl_t nb_handle) { int i, index, proc = nb_handle->proc; unsigned int tag = nb_handle->tag; /* check if there is an entry for this handle in the existing list*/ for(i=ulist.size-1; i>=0; i--) { index = ulist.index[i]; if(aggr[index]->tag == tag && aggr[index]->proc == proc) return index; } /* else it is a new handle, so get a aggr buffer from either of the lists. ???? don't throw exception here */ if(ulist.size >= _MAX_AGG_BUFFERS && alist.size == 0) armci_die("_armci_agg_get_index: Too many outstanding aggregation requests\n", ulist.size); /*If there is a buffer in readily available list,use it*/ if(alist.size > 0) index = alist.index[--alist.size]; else { /* else use/get a buffer from the main list */ index = ulist.size; /* allocate memory for aggregate request handle */ aggr[index] = (agg_req_t *)agg_buf[index]; aggr[index]->request_len = 0; aggr[index]->ptr_array_len = 0; aggr[index]->buf_pos_end = _MAX_AGG_BUFSIZE; /* allocate memory for giov vector field in aggregate request handler */ aggr[index]->darr = (armci_giov_t *)(agg_buf[index]+sizeof(agg_req_t)); } _armci_agg_set_buffer(index, tag, proc, 0); return index; } static void _armci_agg_update_lists(int index) { int i; /* remove that index from the in-use list and bring the last element in the in-use list to the position of the removed one. */ for(i=0; irequest_len; /* index of giov descriptor */ bytes_remaining = aggr[index]->buf_pos_end - (sizeof(agg_req_t) + aggr[index]->request_len*sizeof(armci_giov_t)); /* extra bytes required to store registered put data */ if(is_registered_put) bytes_needed = bytes; /* if (byte-)sizes are equal, use previously created descriptor else get a new descriptor */ if( rid && bytes==aggr[index]->darr[rid-1].bytes) --rid; else { get_new_descr=1; bytes_needed += sizeof(armci_giov_t); } /* If buffer is full, then complete data transfer. After completion, if still ptr array_len is greater than maximum limit(_MAX_PTRS), then do it by parts. Determine new ptr_array_len that fits buffer */ if( (bytes_needed > bytes_remaining) || (_MAX_PTRS - aggr[index]->ptr_array_len < *ptr_array_len)) { armci_agg_complete(nb_handle, SET); rid = 0; get_new_descr=1; if(*ptr_array_len > _MAX_PTRS) *ptr_array_len = _MAX_PTRS; } /* if new descriptor, allocate memory for src_ptr & dst_ptr arrays */ if(get_new_descr) { int i = aggr[index]->ptr_array_len; aggr[index]->darr[rid].src_ptr_array = (void **)&agg_src_ptr[index][i]; aggr[index]->darr[rid].dst_ptr_array = (void **)&agg_dst_ptr[index][i]; aggr[index]->darr[rid].ptr_array_len = 0; aggr[index]->request_len++; } /* store registered put data */ if(is_registered_put) { aggr[index]->buf_pos_end -= bytes; memcpy(&((char *)aggr[index])[aggr[index]->buf_pos_end], *((char **)registered_put_data), bytes); *(char **)registered_put_data = (char *)&((char *)aggr[index])[aggr[index]->buf_pos_end]; } aggr[index]->ptr_array_len += *ptr_array_len; return (&aggr[index]->darr[rid]); } int armci_agg_save_descriptor(void *src, void *dst, int bytes, int proc, int op, int is_registered_put, armci_ihdl_t nb_handle) { int one=1, idx; armci_giov_t * darr; /* set up the handle if it is a new aggregation request */ AGG_INIT_NB_HANDLE(op, proc, nb_handle); darr = _armci_agg_get_descriptor(&one, bytes, nb_handle, is_registered_put, &src); idx = darr->ptr_array_len; darr->src_ptr_array[idx] = src; darr->dst_ptr_array[idx] = dst; darr->bytes = bytes; darr->ptr_array_len += 1; fflush(stdout); return 0; } int armci_agg_save_giov_descriptor(armci_giov_t dscr[], int len, int proc, int op, armci_ihdl_t nb_handle) { int i, j, k, idx, bytes, ptr_array_len; armci_giov_t * darr; /* set up the handle if it is a new aggregation request */ AGG_INIT_NB_HANDLE(op, proc, nb_handle); for(i=0; iptr_array_len; for(j=idx; jsrc_ptr_array[j] = dscr[i].src_ptr_array[k]; darr->dst_ptr_array[j] = dscr[i].dst_ptr_array[k]; } darr->bytes = dscr[i].bytes; darr->ptr_array_len += ptr_array_len; ptr_array_len = dscr[i].ptr_array_len - ptr_array_len; if(ptr_array_len <0) armci_die("agg_save_giov_descr failed", 0L); } while(k < darr[i].ptr_array_len); } return 0; } int armci_agg_save_strided_descriptor(void *src_ptr, int src_stride_arr[], void* dst_ptr, int dst_stride_arr[], int count[], int stride_levels, int proc, int op, armci_ihdl_t nb_handle) { int i, j, k, idx, ptr_array_len=1, total1D=1, num1D=0; int offset1, offset2, factor[MAX_STRIDE_LEVEL]; armci_giov_t * darr; /* set up the handle if it is a new aggregation request */ AGG_INIT_NB_HANDLE(op, proc, nb_handle); for(i=1; i<=stride_levels; i++) { total1D *= count[i]; factor[i-1]=0; } ptr_array_len = total1D; do { darr=_armci_agg_get_descriptor(&ptr_array_len,count[0],nb_handle,0,0); idx = darr->ptr_array_len; /* converting stride into giov vector */ for(i=idx; isrc_ptr_array[i] = (char *)src_ptr + offset1; darr->dst_ptr_array[i] = (char *)dst_ptr + offset2; ++factor[0]; ++num1D; for(j=1; jbytes = count[0]; darr->ptr_array_len += ptr_array_len; ptr_array_len = total1D - ptr_array_len; if(ptr_array_len <0) armci_die("agg_save_strided_descr failed", 0L); } while(num1D < total1D); return 0; } void armci_agg_complete(armci_ihdl_t nb_handle, int condition) { int i, index=0, rc; /* get the buffer index for this handle */ for(i=ulist.size-1; i>=0; i--) { index = ulist.index[i]; if(aggr[index]->tag == nb_handle->tag && aggr[index]->proc == nb_handle->proc) break; } if(i<0) return; /* implies this handle has no requests at all */ #if 0 printf("%d: Aggregation Complete to remote process %d (%d:%d requests)\n", armci_me, nb_handle->proc, index, aggr[index]->request_len); #endif /* complete the data transfer. NOTE: in some APIs, Non-blocking calls (followed by wait) performs better than blocking put/get */ if(aggr[index]->request_len) { switch(nb_handle->op) { case PUT: if((rc=PARMCI_PutV(aggr[index]->darr, aggr[index]->request_len, nb_handle->proc))) ARMCI_Error("armci_agg_complete: putv failed",rc); break; case GET: if((rc=PARMCI_GetV(aggr[index]->darr, aggr[index]->request_len, nb_handle->proc))) ARMCI_Error("armci_agg_complete: getv failed",rc); break; } } /* setting request length to zero, as the requests are completed */ aggr[index]->request_len = 0; aggr[index]->ptr_array_len = 0; aggr[index]->buf_pos_end = _MAX_AGG_BUFSIZE; /* If armci_agg_complete() is called PARMCI_Wait(), then unset nb_handle*/ if(condition==UNSET) { nb_handle->proc = -1; _armci_agg_update_lists(index); } } ga-5.9.2/armci/src/common/armci.c000066400000000000000000000516151500715745200165450ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* DISCLAIMER * * This material was prepared as an account of work sponsored by an * agency of the United States Government. Neither the United States * Government nor the United States Department of Energy, nor Battelle, * nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR * ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, * COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, * SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT * INFRINGE PRIVATELY OWNED RIGHTS. * * * ACKNOWLEDGMENT * * This software and its documentation were produced with United States * Government support under Contract Number DE-AC06-76RLO-1830 awarded by * the United States Department of Energy. The United States Government * retains a paid-up non-exclusive, irrevocable worldwide license to * reproduce, prepare derivative works, perform publicly and display * publicly by or for the US Government, including the right to * distribute to other US Government contractors. */ #define EXTERN /*#define PRINT_BT*/ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STDARG_H # include #endif #if HAVE_ERRNO_H # include #endif #include "armcip.h" #include "copy.h" #include "memlock.h" #include "armci_shmem.h" #include "signaltrap.h" /* global variables -- Initialized in PARMCI_Init() and never modified*/ int armci_me, armci_nproc; int armci_clus_me, armci_nclus, armci_master; int armci_clus_first, armci_clus_last; int _armci_initialized=0; int _armci_initialized_args=0; int _armci_terminating =0; int *_armci_argc=NULL; char ***_armci_argv=NULL; thread_id_t armci_usr_tid; #if !defined(THREAD_SAFE) double armci_internal_buffer[BUFSIZE_DBL]; #endif #if defined(SYSV) || defined(WIN32) || defined(MMAP) # include "locks.h" lockset_t lockid; #endif #ifdef ALLOW_PIN int* armci_prot_switch_fence=NULL; int armci_prot_switch_preproc = -1; int armci_prot_switch_preop = -1; #endif typedef struct{ int sent; int received; int waited; }armci_notify_t; armci_notify_t **_armci_notify_arr; void ARMCI_Cleanup() { #if (defined(SYSV) || defined(WIN32) || defined(MMAP)) Delete_All_Regions(); if(armci_nproc>1) DeleteLocks(lockid); /* in case of an error notify server that it is time to quit */ #if defined(DATA_SERVER) if(armci_nclus >1){ /* send quit request to server unless it is already dead */ armci_wait_for_server(); armci_transport_cleanup(); } #endif armci_finalize_fence(); #ifndef WIN32 ARMCI_RestoreSignals(); #endif #endif } void armci_notify_init() { int rc, bytes= sizeof(armci_notify_t)*armci_nproc; _armci_notify_arr= (armci_notify_t**)malloc(armci_nproc*sizeof(armci_notify_t*)); if(!_armci_notify_arr)armci_die("armci_notify_ini:malloc failed",armci_nproc); if((rc=PARMCI_Malloc((void **)_armci_notify_arr, bytes))) armci_die(" armci_notify_init: armci_malloc failed",bytes); bzero(_armci_notify_arr[armci_me], bytes); } static void armci_perror_msg() { char perr_str[80]; if(!errno) return; sprintf(perr_str,"Last System Error Message from Task %d:",armci_me); perror(perr_str); } extern int AR_caught_sigint; extern int AR_caught_sigterm; void armci_abort(int code) { armci_perror_msg(); ARMCI_Cleanup(); /* data server process cannot use message-passing library to abort * it simply exits, parent will get SIGCHLD and abort the program */ #if defined(DATA_SERVER) if(armci_me<0) _exit(1); else #endif armci_msg_abort(code); } /*For now, until no code requires a function pointer to ARMCI_Error (used by GA now).*/ void ARMCI_Error(const char *msg, int code) { armci_die(msg,code); } void armci_allocate_locks() { #if (defined(SYSV) || defined(WIN32) || defined(MMAP)) if(armci_nproc == 1)return; # if defined(SPINLOCK) || defined(PMUTEX) || defined(PSPIN) CreateInitLocks(NUM_LOCKS, &lockid); # else if(armci_master==armci_me)CreateInitLocks(NUM_LOCKS, &lockid); armci_msg_clus_brdcst(&lockid, sizeof(lockid)); if(armci_master != armci_me)InitLocks(NUM_LOCKS, lockid); # endif #endif } void ARMCI_Set_shm_limit(unsigned long shmemlimit) { #if (defined(SYSV) || defined(WIN32) || defined(MMAP)) #define EXTRASHM 1024 /* extra shmem used internally in ARMCI */ unsigned long limit; limit = shmemlimit+EXTRASHM; armci_set_shmem_limit_per_core(limit); #endif } /*\ allocate and initialize memory locking data structure \*/ void armci_init_memlock() { int bytes = MAX_SLOTS*sizeof(memlock_t); int rc, msize_per_proc=bytes; #ifdef MEMLOCK_SHMEM_FLAG /* last proc on node allocates memlock flag in shmem */ if(armci_clus_last == armci_me) bytes += sizeof(int); #endif memlock_table_array = malloc(armci_nproc*sizeof(void*)); if(!memlock_table_array) armci_die("malloc failed for ARMCI lock array",0); rc = PARMCI_Malloc(memlock_table_array, bytes); if(rc) armci_die("failed to allocate ARMCI memlock array",rc); armci_msg_barrier(); bzero(memlock_table_array[armci_me],bytes); #ifdef MEMLOCK_SHMEM_FLAG /* armci_use_memlock_table is a pointer to local memory variable=1 * we overwrite the pointer with address of shared memory variable * armci_use_memlock_table and initialize it >0 */ armci_use_memlock_table = (int*) (msize_per_proc + (char*) memlock_table_array[armci_clus_last]); /* printf("%d: last=%d bytes=%d ptr =(%d, %d)\n", armci_me,armci_clus_last,bytes,armci_use_memlock_table, memlock_table_array[armci_clus_last]); fflush(stdout); */ if(armci_clus_last == armci_me) *armci_use_memlock_table =1+armci_me; #endif armci_msg_barrier(); } extern void armci_region_shm_malloc(void *ptr_arr[], size_t bytes); #ifdef ENABLE_CHECKPOINT int armci_ft_spare_procs; void armci_set_spare_procs(int spare) { armci_ft_spare_procs = spare; } ARMCI_Group armci_ft_group; ARMCI_Group *ARMCI_Get_ft_group() { return(&armci_ft_group); } void armci_create_ft_group() { int i, list[MAX_PROC]; for(i=0;i0) return 0; dassertp(1,sizeof(armci_ireq_t) <= sizeof(armci_hdl_t), ("nb handle sizes: internal(%d) should be <= external(%d)\n", sizeof(armci_ireq_t), sizeof(armci_hdl_t))); /* let's hope that the message passing environment was initialized outside * of ARMCI such that passing NULL for argc/argv here is okay */ /* armci_msg_init(NULL, NULL); */ armci_msg_init_comm(comm); #ifdef MPI_SPAWN if(!_armci_initialized_args) armci_die("ARMCI is built w/ ARMCI_NETWORK=MPI-SPAWN. For this network " "setting, ARMCI must be initialized with PARMCI_Init_args() " "instead of PARMCI_Init(). Please replace PARMCI_Init() " " with PARMCI_Init_args(&argc, &argv) as in the API docs", 0L); #endif #if defined(MPI_MT) { int provided; MPI_Query_thread(&provided); if (provided == MPI_THREAD_SINGLE) { armci_die("ARMCI is built w/ ARMCI_NETWORK=MPI_MT but the " "provided MPI threading level is MPI_THREAD_SINGLE " " not MPI_THREAD_MULTIPLE", 1); } else if (provided == MPI_THREAD_FUNNELED) { armci_die("ARMCI is built w/ ARMCI_NETWORK=MPI_MT but the " "provided MPI threading level is MPI_THREAD_FUNNELED " " not MPI_THREAD_MULTIPLE", 1); } else if (provided == MPI_THREAD_SERIALIZED) { armci_die("ARMCI is built w/ ARMCI_NETWORK=MPI_MT but the " "provided MPI threading level is MPI_THREAD_SERIALIZED " " not MPI_THREAD_MULTIPLE", 1); } else if (provided == MPI_THREAD_MULTIPLE) { } } #endif armci_nproc = armci_msg_nproc(); armci_me = armci_msg_me(); armci_usr_tid = THREAD_ID_SELF(); /*remember the main user thread id */ #if defined(THREAD_SAFE) armci_init_threads(); th_idx = ARMCI_THREAD_IDX; if (th_idx) printf("WARNING: PARMCI_Init is called from thread %d, should be 0\n",th_idx); #endif armci_init_clusinfo(); #ifdef MSG_COMMS_MPI armci_group_init(); #endif armci_krmalloc_init_localmem(); #ifndef BLRTS /* trap signals to cleanup ARMCI system resources in case of crash */ if(armci_me == armci_master) { ARMCI_ParentTrapSignals(); } ARMCI_ChildrenTrapSignals(); #endif #if defined(SYSV) || defined(WIN32) || defined(MMAP) /* init shared/K&R memory */ if(ARMCI_Uses_shm() ) { armci_shmem_init(); } #endif #ifdef REGION_ALLOC { void* test_ptr_arr = malloc(sizeof(void *)*MAX_PROC); dassert(1,test_ptr_arr); PARMCI_Malloc(test_ptr_arr,256*1024*1024); PARMCI_Free(test_ptr_arr[armci_me]); free(test_ptr_arr); } #endif /* allocate locks: we need to do it before server is started */ armci_allocate_locks(); armci_init_fence(); #if ARMCI_ENABLE_GPC_CALLS gpc_init_signals(); #endif #ifdef ALLOW_PIN armci_prot_switch_fence = malloc(sizeof(int*)*armci_nproc); armci_prot_switch_preproc = -1; armci_prot_switch_preop = -1; #endif /* NOTE: FOR PROCESS-BASED DATA SERVER WE CANNOT call PARMCI_Malloc yet */ # if defined(DATA_SERVER) if(armci_nclus >1) armci_start_server(); # endif #if defined(VAPI) /* initialize registration of memory */ armci_region_init(); #endif armci_msg_barrier(); armci_init_memlock(); /* allocate data struct for locking memory areas */ armci_notify_init(); armci_msg_barrier(); armci_msg_gop_init(); _armci_initialized=1; #ifdef ENABLE_CHECKPOINT armci_init_checkpoint(armci_ft_spare_procs); #endif #ifdef MPI_MT _armci_test_connections(); #else uval = getenv("ARMCI_TEST_CONNECTIONS"); if(uval!=NULL) { _armci_test_connections(); } #endif #if MSG_COMMS_TCGMSGMPI install_nxtval(NULL, NULL); #endif return 0; } int PARMCI_Init() { return !_armci_init(MPI_COMM_WORLD); } int PARMCI_Init_mpi_comm(MPI_Comm comm) { return !_armci_init(comm); } /* ARMCI Finalize is called multiple times, if both GA and TCGMSG are used * */ void PARMCI_Finalize() { if(_armci_initialized <= 0 ) { return; } _armci_initialized = 0; _armci_terminating =1; _armci_initialized_args=0; _armci_argc = NULL; _armci_argv = NULL; armci_msg_barrier(); if(armci_me==armci_master) ARMCI_ParentRestoreSignals(); #if defined(DATA_SERVER) if(armci_nclus >1){ armci_wait_for_server(); armci_msg_barrier(); } #endif #ifdef ALLOW_PIN free(armci_prot_switch_fence); #endif armci_msg_gop_finalize(); ARMCI_Cleanup(); armci_msg_barrier(); #ifdef MSG_COMMS_MPI armci_group_finalize(); #endif #ifdef MSG_COMMS_MPI MPI_Comm_free(&ARMCI_COMM_WORLD); /*SK: free at last*/ #endif } /* Indicates whether ARMCI_Init or ARMCI_Init_args has been called. */ int PARMCI_Initialized() { return (_armci_initialized > 0) ? 1 : 0; } #if !(defined(SYSV) || defined(WIN32)) void ARMCI_Set_shmem_limit(unsigned long shmemlimit) { /* not applicable here * aborting would make user's life harder */ } #endif void ARMCI_Copy(void *src, void *dst, int n) { armci_copy(src,dst,n); } extern void cpu_yield(); void armci_util_wait_int(volatile int *p, int val, int maxspin) { int count=0; while(*p != val) if((++count)agg_flag = 1; ((armci_ihdl_t)(nb_handle))->proc = -1; } void ARMCI_UNSET_AGGREGATE_HANDLE(armci_hdl_t* nb_handle) { ((armci_ihdl_t)(nb_handle))->agg_flag = 0; ((armci_ihdl_t)(nb_handle))->proc = -1; } int parmci_notify(int proc) { armci_notify_t *pnotify = _armci_notify_arr[armci_me]+proc; pnotify->sent++; # ifdef MEM_FENCE if(SAMECLUSNODE(proc)) MEM_FENCE; # endif #ifdef OPENIB /* IB will optimze a simple Put by using RDMA. This can bypass non-RDMA * Puts and lead to incorrect behavour. Avoid that by using PutV, which * presently does not optimize to RDMA. * This workaround is sub-optimal for two reasons: * 1. This adds more overhead when there is may be no need. * 2. There is no guarantee that PutV will always be un-optimized. */ void *sp = &pnotify->sent; void *dp = &(_armci_notify_arr[proc]+armci_me)->received; armci_giov_t gv; gv.src_ptr_array = &sp; gv.dst_ptr_array = &dp; gv.ptr_array_len = 1; gv.bytes = sizeof(pnotify->sent); PARMCI_PutV(&gv, 1, proc); #else PARMCI_Put(&pnotify->sent,&(_armci_notify_arr[proc]+armci_me)->received, sizeof(pnotify->sent),proc); #endif /* OPENIB */ return(pnotify->sent); } /* blocks until received count becomes >= waited count * return received count and store waited count in *pval */ int parmci_notify_wait(int proc,int *pval) { int retval; long loop=0; armci_notify_t *pnotify = _armci_notify_arr[armci_me]+proc; pnotify->waited++; while( pnotify->waited > pnotify->received) { if(++loop == 1000) { loop=0;cpu_yield(); } armci_util_spin(loop, pnotify); } *pval = pnotify->waited; retval=pnotify->received; return retval; } long armci_util_long_getval(long* p) { return *p; } int armci_util_int_getval(int* p) { return *p; } #if ARMCI_ENABLE_GPC_CALLS int armci_gpc(int hndl, int proc, void *hdr, int hlen, void *data, int dlen, void *rhdr, int rhlen, void *rdata, int rdlen, armci_hdl_t* nbh) { armci_ihdl_t nb_handle = (armci_ihdl_t)nbh; armci_giov_t darr[2]; /* = {{&rhdr, &rhdr, 1, rhlen}, {&rdata, &rdata, 1, rdlen}};*/ gpc_send_t send; char *ptr; /* initialize giov */ darr[0].src_ptr_array = &rhdr; darr[0].dst_ptr_array = &rhdr; darr[0].ptr_array_len = 1; darr[0].bytes = rhlen; darr[1].src_ptr_array = &rdata; darr[1].dst_ptr_array = &rdata; darr[1].ptr_array_len = 1; darr[1].bytes = rdlen; /* if(hlen<0 || hlen>=ARMCI_Gpc_get_hlen()) */ /* return FAIL2; */ /* if(rhlen<0 || rhlen>=ARMCI_Gpc_get_hlen()) */ /* return FAIL2; */ /* if(dlen<0 || dlen>=ARMCI_Gpc_get_dlen()) */ /* return FAIL2; */ /* if(rdlen<0 || rdlen>=ARMCI_Gpc_get_dlen()) */ /* return FAIL2; */ if(hlen>0 && hdr==NULL) return FAIL3; if(rhlen>0 && rhdr==NULL) return FAIL3; if(dlen>0 && data==NULL) return FAIL3; if(rdlen>0 && rdata==NULL) return FAIL3; if(proc<0 || proc >= armci_nproc) return FAIL4; send.hndl = hndl; send.hlen = hlen; send.dlen = dlen; send.hdr = hdr; send.data = data; if(nb_handle){ nb_handle->tag = GET_NEXT_NBTAG(); nb_handle->op = GET; nb_handle->proc= proc; nb_handle->bufid=NB_NONE; } else { ORDER(GET,proc); /*ensure ordering */ nb_handle = NULL; } #if defined(VAPI) if(armci_rem_gpc(GET, darr, 2, &send, proc, 1, nb_handle)) #endif return FAIL2; return 0; } int armci_sameclusnode(int proc) { return SAMECLUSNODE(proc); } #endif void _armci_init_handle(armci_hdl_t *hdl) { ((double *)((hdl)->data))[0]=0; ((double *)((hdl)->data))[1]=0; } #ifdef CHANGE_SERVER_AFFINITY static inline int val_to_char(int v) { if (v >= 0 && v < 10) return '0' + v; else if (v >= 10 && v < 16) return ('a' - 10) + v; else return -1; } static const char *nexttoken(const char *q, int sep) { if (q) q = strchr(q, sep); if (q) q++; return q; } int cstr_to_cpuset(cpu_set_t * mask, const char *str) { const char *p, *q; q = str; CPU_ZERO(mask); while (p = q, q = nexttoken(q, ','), p) { unsigned int a; /* beginning of range */ unsigned int b; /* end of range */ unsigned int s; /* stride */ const char *c1, *c2; if (sscanf(p, "%u", &a) < 1) return 1; b = a; s = 1; c1 = nexttoken(p, '-'); c2 = nexttoken(p, ','); if (c1 != NULL && (c2 == NULL || c1 < c2)) { if (sscanf(c1, "%u", &b) < 1) return 1; c1 = nexttoken(c1, ':'); if (c1 != NULL && (c2 == NULL || c1 < c2)) if (sscanf(c1, "%u", &s) < 1) { return 1; } } if (!(a <= b)) return 1; while (a <= b) { CPU_SET(a, mask); a += s; } } return 0; } char *cpuset_to_cstr(cpu_set_t * mask, char *str) { int i; char *ptr = str; int entry_made = 0; for (i = 0; i < CPU_SETSIZE; i++) { if (CPU_ISSET(i, mask)) { int j; int run = 0; entry_made = 1; for (j = i + 1; j < CPU_SETSIZE; j++) { if (CPU_ISSET(j, mask)) run++; else break; } if (!run) sprintf(ptr, "%d,", i); else if (run == 1) { sprintf(ptr, "%d,%d,", i, i + 1); i++; } else { sprintf(ptr, "%d-%d,", i, i + run); i += run; } while (*ptr != 0) ptr++; } } ptr -= entry_made; *ptr = 0; return str; } char *cpuset_to_str(cpu_set_t * mask, char *str) { int base; char *ptr = str; char *ret = 0; for (base = CPU_SETSIZE - 4; base >= 0; base -= 4) { char val = 0; if (CPU_ISSET(base, mask)) val |= 1; if (CPU_ISSET(base + 1, mask)) val |= 2; if (CPU_ISSET(base + 2, mask)) val |= 4; if (CPU_ISSET(base + 3, mask)) val |= 8; if (!ret && val) ret = ptr; *ptr++ = val_to_char(val); } *ptr = 0; return ret ? ret : ptr - 1; } #endif static int in_error_cleanup=0; void derr_printf(const char *format, ...) { if(!in_error_cleanup) { #ifdef SYSV if((!AR_caught_sigterm && !AR_caught_sigint) || armci_me==0) #endif { va_list ap; va_start(ap, format); vprintf(format, ap); va_end(ap); } } } int dassertp_fail(const char *cond_string, const char *file, const char *func, unsigned int line, int code) { if(!in_error_cleanup) { /* JAD 02/23/2012 for applications, an exit/error code of 0 indicates * success, it is therefore wrong to call dassertp_fail with a zero value */ if (0 == code) { code = -1; } in_error_cleanup=1; #ifdef SYSV if((!AR_caught_sigterm && !AR_caught_sigint) || armci_me==0) #endif { printf("(rank:%d hostname:%s pid:%d):ARMCI DASSERT fail. %s:%s():%d cond:%s\n", armci_me,armci_clus_info[armci_clus_me].hostname, getpid(), file,func,line,cond_string); #if defined(PRINT_BT) backtrace_symbols_fd(bt, backtrace(bt, 100), 2); #endif } armci_abort(code); } return code; } ga-5.9.2/armci/src/common/async.c000066400000000000000000000073501500715745200165640ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: async.c,v 1.5 2002-12-18 18:25:33 vinod Exp $ */ /* data structures and interfaces for handling asynchronous requests */ #if HAVE_STDIO_H # include #endif #include "armcip.h" #define DEBUG_ 0 /* #define UBUF_LEN 496 */ #define MAX_PENDING_REQ 4 #define MAX_STRIDE_LEVELS 8 typedef struct { void *ptr; int stride_levels; int stride_arr[MAX_STRIDE_LEVELS]; int count[MAX_STRIDE_LEVELS]; }strided_dscr_t; typedef struct { int segments; int len; void *ptrs[60]; }vector_dscr_t; typedef struct { int reqid; /* request id */ int bufid; /* communication buffer id */ union { /* 8 bytes for alignment reason */ void *dscrbuf; double pad; }ptr; union { char buf[UBUF_LEN]; strided_dscr_t strided; vector_dscr_t vector; }dscr; }saved_dscr_t; static int cur_id=191; #define GET_REQ_ID cur_id++ #define REQ_TO_DSC_ID(reqid) ((reqid)>>8) static saved_dscr_t armci_pending_dscr[MAX_PENDING_REQ]; /*\ save a part of strided descriptor needed to complete request \*/ void _armci_asyn_save_dscr_strided(int id, void *ptr, int stride[], int count[], int levels) { strided_dscr_t *dscr; int i, dsc_id = REQ_TO_DSC_ID(id); dscr = &armci_pending_dscr[dsc_id].dscr.strided; dscr->stride_levels = levels; dscr->ptr =ptr; for(i=0;istride_arr[i]=stride[i]; for(i=0;icount[i]=count[i]; } /*\ for request stored in buf, save local strided descriptor and return req id \*/ int _armci_asyn_init_strided_get(void *buf, void *ptr,int levels, int stride[], int count[]) { int id, i; int dsc_id=_armci_buf_to_index(buf); strided_dscr_t *dscr; dscr = &armci_pending_dscr[dsc_id].dscr.strided; dscr->stride_levels = levels; dscr->ptr =ptr; for(i=0;istride_arr[i]=stride[i]; for(i=0;icount[i]=count[i]; /* convert bufid into req id returned to user */ id = dsc_id; id <<= 8; /* buffer id is in second byte */ cur_id = (cur_id+1)%255 +1; /* counter in LSB */ id += cur_id; armci_pending_dscr[dsc_id].reqid = id; if(DEBUG_){ printf("%d: init strided get: ptr=%p reqid=%d bufid=%d cid=%d levels=%d count[0]=%d\n", armci_me,ptr,id,dsc_id, cur_id, levels,count[0]); fflush(stdout); } return id; } void _armci_asyn_complete_strided_get(int dsc_id, void *buf) { request_header_t *msginfo = (request_header_t*) buf; strided_dscr_t *dscr; dscr = &armci_pending_dscr[dsc_id].dscr.strided; armci_pending_dscr[dsc_id].reqid = 0; if(DEBUG_){ printf("%d:complete_strided_get: ptr=%p bufid=%d levels=%d count[0]=%d\n", armci_me,dscr->ptr,dsc_id,dscr->stride_levels,dscr->count[0]); fflush(stdout); } armci_rcv_strided_data(msginfo->to, msginfo, msginfo->datalen, dscr->ptr, dscr->stride_levels, dscr->stride_arr,dscr->count); } #if 0 /*this function has been added in armci.c*/ int PARMCI_Wait(int req_id) { int dsc_id = REQ_TO_DSC_ID(req_id); void *buf; if(DEBUG_){ printf("%d: WAIT for req id=%d bufid=%d\n",armci_me,req_id,dsc_id); fflush(stdout); } buf = _armci_buf_ptr_from_id(dsc_id); if(dsc_id >MAX_PENDING_REQ) armci_die2("PARMCI_Wait: bad id",dsc_id,MAX_PENDING_REQ); /* when 0 it means the request was completed to get the buffer */ if(armci_pending_dscr[dsc_id].reqid == 0) return 0; /* return 1 if request id looks bad */ if(armci_pending_dscr[dsc_id].reqid < req_id) return 1; _armci_asyn_complete_strided_get(dsc_id,buf); FREE_SEND_BUFFER(buf); return 0; } #endif ga-5.9.2/armci/src/common/capi.c000066400000000000000000000273141500715745200163650ustar00rootroot00000000000000 #if HAVE_CONFIG_H # include "config.h" #endif #include #include "armci.h" #include "parmci.h" #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Acc #endif int ARMCI_Acc(int optype, void *scale, void *src, void *dst, int bytes, int proc) { return PARMCI_Acc(optype, scale, src, dst, bytes, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_AccS #endif int ARMCI_AccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { return PARMCI_AccS(optype, scale, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_AccV #endif int ARMCI_AccV(int op, void *scale, armci_giov_t *darr, int len, int proc) { return PARMCI_AccV(op, scale, darr, len, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_AllFence #endif void ARMCI_AllFence() { PARMCI_AllFence(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Barrier #endif void ARMCI_Barrier() { PARMCI_Barrier(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Create_mutexes #endif int ARMCI_Create_mutexes(int num) { return PARMCI_Create_mutexes(num); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Destroy_mutexes #endif int ARMCI_Destroy_mutexes() { return PARMCI_Destroy_mutexes(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Fence #endif void ARMCI_Fence(int proc) { PARMCI_Fence(proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GroupFence #endif void ARMCI_GroupFence(ARMCI_Group *group) { PARMCI_GroupFence(group); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Finalize #endif void ARMCI_Finalize() { PARMCI_Finalize(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Free #endif int ARMCI_Free(void *ptr) { return PARMCI_Free(ptr); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Free_memdev #endif int ARMCI_Free_memdev(void *ptr) { return PARMCI_Free_memdev(ptr); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Free_local #endif int ARMCI_Free_local(void *ptr) { return PARMCI_Free_local(ptr); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Get #endif int ARMCI_Get(void *src, void *dst, int bytes, int proc) { return PARMCI_Get(src, dst, bytes, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetS #endif int ARMCI_GetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { return PARMCI_GetS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetV #endif int ARMCI_GetV(armci_giov_t *darr, int len, int proc) { return PARMCI_GetV(darr, len, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetValueDouble #endif double ARMCI_GetValueDouble(void *src, int proc) { return PARMCI_GetValueDouble(src, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetValueFloat #endif float ARMCI_GetValueFloat(void *src, int proc) { return PARMCI_GetValueFloat(src, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetValueInt #endif int ARMCI_GetValueInt(void *src, int proc) { return PARMCI_GetValueInt(src, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetValueLong #endif long ARMCI_GetValueLong(void *src, int proc) { return PARMCI_GetValueLong(src, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Init #endif int ARMCI_Init() { return PARMCI_Init(); } #ifdef MSG_COMMS_MPI #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Init_mpi_comm #endif int ARMCI_Init_mpi_comm(MPI_Comm comm) { return PARMCI_Init_mpi_comm(comm); } #endif #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Init_args #endif int ARMCI_Init_args(int *argc, char ***argv) { return PARMCI_Init_args(argc, argv); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Initialized #endif int ARMCI_Initialized() { return PARMCI_Initialized(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Lock #endif void ARMCI_Lock(int mutex, int proc) { PARMCI_Lock(mutex, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Malloc #endif int ARMCI_Malloc(void **ptr_arr, armci_size_t bytes) { return PARMCI_Malloc(ptr_arr, bytes); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Malloc_memdev #endif int ARMCI_Malloc_memdev(void **ptr_arr, armci_size_t bytes, const char *device) { return PARMCI_Malloc(ptr_arr, bytes); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Malloc_local #endif void* ARMCI_Malloc_local(armci_size_t bytes) { return PARMCI_Malloc_local(bytes); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Memat #endif void* ARMCI_Memat(armci_meminfo_t *meminfo, long offset) { return PARMCI_Memat(meminfo, offset); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Memget #endif void ARMCI_Memget(size_t bytes, armci_meminfo_t *meminfo, int memflg) { PARMCI_Memget(bytes, meminfo, memflg); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbAccS #endif int ARMCI_NbAccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbAccS(optype, scale, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbAccV #endif int ARMCI_NbAccV(int op, void *scale, armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbAccV(op, scale, darr, len, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbGet #endif int ARMCI_NbGet(void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbGet(src, dst, bytes, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbGetS #endif int ARMCI_NbGetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbGetS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbGetV #endif int ARMCI_NbGetV(armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbGetV(darr, len, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPut #endif int ARMCI_NbPut(void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPut(src, dst, bytes, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutS #endif int ARMCI_NbPutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutV #endif int ARMCI_NbPutV(armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutV(darr, len, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutValueDouble #endif int ARMCI_NbPutValueDouble(double src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutValueDouble(src, dst, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutValueFloat #endif int ARMCI_NbPutValueFloat(float src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutValueFloat(src, dst, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutValueInt #endif int ARMCI_NbPutValueInt(int src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutValueInt(src, dst, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutValueLong #endif int ARMCI_NbPutValueLong(long src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutValueLong(src, dst, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Put #endif int ARMCI_Put(void *src, void *dst, int bytes, int proc) { return PARMCI_Put(src, dst, bytes, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutS #endif int ARMCI_PutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { return PARMCI_PutS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutS_flag #endif int ARMCI_PutS_flag(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { return PARMCI_PutS_flag(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, flag, val, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutS_flag_dir #endif int ARMCI_PutS_flag_dir(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { return PARMCI_PutS_flag_dir(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, flag, val, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutV #endif int ARMCI_PutV(armci_giov_t *darr, int len, int proc) { return PARMCI_PutV(darr, len, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutValueDouble #endif int ARMCI_PutValueDouble(double src, void *dst, int proc) { return PARMCI_PutValueDouble(src, dst, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutValueFloat #endif int ARMCI_PutValueFloat(float src, void *dst, int proc) { return PARMCI_PutValueFloat(src, dst, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutValueInt #endif int ARMCI_PutValueInt(int src, void *dst, int proc) { return PARMCI_PutValueInt(src, dst, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutValueLong #endif int ARMCI_PutValueLong(long src, void *dst, int proc) { return PARMCI_PutValueLong(src, dst, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Put_flag #endif int ARMCI_Put_flag(void *src, void *dst, int bytes, int *f, int v, int proc) { return PARMCI_Put_flag(src, dst, bytes, f, v, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Rmw #endif int ARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc) { return PARMCI_Rmw(op, ploc, prem, extra, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Test #endif int ARMCI_Test(armci_hdl_t *nb_handle) { return PARMCI_Test(nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Unlock #endif void ARMCI_Unlock(int mutex, int proc) { PARMCI_Unlock(mutex, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Wait #endif int ARMCI_Wait(armci_hdl_t *nb_handle) { return PARMCI_Wait(nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_WaitAll #endif int ARMCI_WaitAll() { return PARMCI_WaitAll(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_WaitProc #endif int ARMCI_WaitProc(int proc) { return PARMCI_WaitProc(proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak armci_msg_barrier #endif void armci_msg_barrier() { parmci_msg_barrier(); } #if MSG_COMMS_MPI #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak armci_msg_group_barrier #endif void armci_msg_group_barrier(ARMCI_Group *group) { parmci_msg_group_barrier(group); } #endif #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak armci_notify #endif int armci_notify(int proc) { return parmci_notify(proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak armci_notify_wait #endif int armci_notify_wait(int proc, int *pval) { return parmci_notify_wait(proc, pval); } ga-5.9.2/armci/src/common/ccopy.c000066400000000000000000000221351500715745200165620ustar00rootroot00000000000000/*************************************************************************** COPYRIGHT The following is a notice of limited availability of the code, and disclaimer which must be included in the prologue of the code and in all source listings of the code. Copyright Notice + 2009 University of Chicago Permission is hereby granted to use, reproduce, prepare derivative works, and to redistribute to others. This software was authored by: Jeff R. Hammond Leadership Computing Facility Argonne National Laboratory Argonne IL 60439 USA phone: (630) 252-5381 e-mail: jhammond@anl.gov GOVERNMENT LICENSE Portions of this material resulted from work developed under a U.S. Government Contract and are subject to the following license: the Government is granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable worldwide license in this computer software to reproduce, prepare derivative works, and perform publicly and display publicly. DISCLAIMER This computer code material was prepared, in part, as an account of work sponsored by an agency of the United States Government. Neither the United States, nor the University of Chicago, nor any of their employees, makes any warranty express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately owned rights. ***************************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "copy.h" /* ONE-DIMENSIONAL COPY OPERATIONS */ #if 0 subroutine dcopy1d_n(A, B, n) integer n,i double precision A(n), B(n) ccdir$ no_cache_alloc a,b do i = 1, n B(i) = A(i) end do end #endif void c_dcopy1d_n_(const double* const restrict A, double* const restrict B, const int* const restrict n) { int i; for ( i = 0 ; i < (*n) ; i++ ){ B[i] = A[i]; } return; } #if 0 subroutine dcopy1d_u(A, B, n) integer n,n1,i double precision A(n), B(n) double precision d1, d2, d3, d4 n1 = iand(max0(n,0),3) do i = 1, n1 B(i) = A(i) end do do i = n1+1, n, 4 d1 = a(i) d2 = a(i+1) d3 = a(i+2) d4 = a(i+3) b(i) = d1 b(i+1) = d2 b(i+2) = d3 b(i+3) = d4 end do end #endif void c_dcopy1d_u_(const double* const restrict A, double* const restrict B, const int* const restrict n) { int i; int m = (*n) - ((*n)%4); for ( i = 0 ; i < m ; i+=4 ){ B[i ] = A[i ]; B[i+1] = A[i+1]; B[i+2] = A[i+2]; B[i+3] = A[i+3]; } for ( i = m ; i < (*n) ; i++ ){ B[i] = A[i]; } return; } /* TWO-DIMENSIONAL COPY OPERATIONS */ #if 0 subroutine dcopy21(rows, cols, A, ald, buf, cur) integer rows, cols integer c, r, ald, cur double precision A(ald,*), buf(ald) cur = 0 do c = 1, cols do r = 1, rows cur = cur+1 buf(cur) = A(r,c) end do end do end #endif void c_dcopy21_(const int* const restrict rows, const int* const restrict cols, const double* const restrict A, const int* const restrict ald, double* const restrict buf, int* const restrict cur) { int r, c, i=0; for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < (*rows) ; r++ ){ buf[i++] = A[ c * (*ald) + r ]; } } (*cur) = i; return; } #if 0 subroutine dcopy12(rows, cols, A, ald, buf, cur) integer rows, cols integer c, r, ald, cur double precision A(ald,*), buf(ald) cur = 0 do c = 1, cols do r = 1, rows cur = cur+1 A(r,c) = buf(cur) end do end do end #endif void c_dcopy12_(const int* const restrict rows, const int* const restrict cols, double* const restrict A, const int* const restrict ald, const double* const restrict buf, int* const restrict cur) { int r, c, i=0; i = 0; for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < (*rows) ; r++ ){ A[ c * (*ald) + r ] = buf[i++]; } } (*cur) = i; return; } #if 0 subroutine dcopy2d_n(rows, cols, A, ald, B, bld) integer rows, cols integer c, r, ald, bld double precision A(ald,*), B(bld,*) do c = 1, cols do r = 1, rows B(r,c) = A(r,c) end do end do end #endif void c_dcopy2d_n_(const int* const restrict rows, const int* const restrict cols, const double* const restrict A, const int* const restrict ald, double* const restrict B, const int* const restrict bld) { int r, c; for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < (*rows) ; r++ ){ B[ c * (*bld) + r ] = A[ c * (*ald) + r ]; } } return; } #if 0 subroutine dcopy2d_u(rows, cols, A, ald, B, bld) integer rows, cols integer c, r, ald, bld double precision A(ald,*), B(bld,*) integer r1 double precision d1, d2, d3, d4 do c = 1, cols r1 = iand(max0(rows,0),3) do r = 1, r1 c$$$ b(r,c) = a(r,c) + b(r,c) * 0 b(r,c) = a(r,c) end do do r = r1 + 1, rows, 4 d1 = a(r,c) d2 = a(r+1,c) d3 = a(r+2,c) d4 = a(r+3,c) b(r,c) = d1 b(r+1,c) = d2 b(r+2,c) = d3 b(r+3,c) = d4 c$$$ b(r,c) = a(r,c) + b(r,c) * 0 c$$$ b(r+1,c) = a(r+1,c) + b(r+1,c) * 0 c$$$ b(r+2,c) = a(r+2,c) + b(r+2,c) * 0 c$$$ b(r+3,c) = a(r+3,c) + b(r+3,c) * 0 enddo enddo end #endif void c_dcopy2d_u_(const int* const restrict rows, const int* const restrict cols, const double* const restrict A, const int* const restrict ald, double* const restrict B, const int* const restrict bld) { int r, c; for ( c = 0 ; c < (*cols) ; c++ ){ int m = (*rows) - ((*rows)%4); for ( r = 0 ; r < m ; r+=4 ){ B[ c * (*bld) + r ] = A[ c * (*ald) + r ]; B[ c * (*bld) + r+1 ] = A[ c * (*ald) + r+1 ]; B[ c * (*bld) + r+2 ] = A[ c * (*ald) + r+2 ]; B[ c * (*bld) + r+3 ] = A[ c * (*ald) + r+3 ]; } for ( r = m ; r < (*rows) ; r++ ){ B[ c * (*bld) + r ] = A[ c * (*ald) + r ]; } } return; } /* THREE-DIMENSIONAL COPY OPERATIONS */ #if 0 subroutine dcopy31(rows, cols, planes, A, aldr, aldc, buf, cur) integer rows, cols, planes integer c, r, p, aldr, aldc, cur double precision A(aldr, aldc, *), buf(aldr) cur = 0 do p = 1, planes do c = 1, cols do r = 1, rows cur = cur+1 buf(cur) = A(r,c,p) end do end do end do end #endif void c_dcopy31_(const int* const restrict rows, const int* const restrict cols, const int* const restrict plns, const double* const restrict A, const int* const restrict aldr, const int* const restrict aldc, double* const restrict buf, int* const restrict cur) { int r, c, p, i=0; for ( p = 0 ; p < (*plns) ; p++ ){ for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < (*rows) ; r++ ){ buf[i++] = A[ p * (*aldc) * (*aldr) + c * (*aldr) + r ]; } } } (*cur) = i; return; } #if 0 subroutine dcopy13(rows, cols, planes, A, aldr, aldc, buf, cur) integer rows, cols, planes integer c, r, p, aldr, aldc, cur double precision A(aldr, aldc, *), buf(aldr) cur = 0 do p = 1, planes do c = 1, cols do r = 1, rows cur = cur+1 A(r,c,p) = buf(cur) end do end do end do end #endif void c_dcopy13_(const int* const restrict rows, const int* const restrict cols, const int* const restrict plns, double* const restrict A, const int* const restrict aldr, const int* const restrict aldc, const double* const restrict buf, int* const restrict cur) { int r, c, p, i=0; for ( p = 0 ; p < (*plns) ; p++ ){ for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < (*rows) ; r++ ){ A[ p * (*aldc) * (*aldr) + c * (*aldr) + r ] = buf[i++]; } } } (*cur) = i; return; } ga-5.9.2/armci/src/common/clusterinfo.c000066400000000000000000000310131500715745200177750ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /****************************************************************************** * file: clusterinfo.c * purpose: Determine cluster info i.e., number of machines and processes * running on each of them. * *******************************************************************************/ #if HAVE_STRING_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_UNISTD_H # include #elif HAVE_WINSOCK_H # include /* this is where gethostbyname is declared */ #endif #include "message.h" #include "armcip.h" /* NO_SHMEM enables to simulate cluster environment on a single workstation. * Must define NO_SHMMAX_SEARCH in shmem.c to prevent depleting shared memory * due to a gready shmem request by the master process on cluster node 0. */ #define DEBUG 0 #define MAX_HOSTNAME 80 #define CHECK_NODE_NAMES /* print info on how many cluster nodes detected */ #ifdef CLUSTER # define PRINT_CLUSTER_INFO 0 #else # define PRINT_CLUSTER_INFO 0 #endif #if defined(OPENIB) static const char *network_protocol="OpenIB Verbs API"; #elif defined(PM) static const char *network_protocol="Score PM"; #elif defined(MPI_MT) static const char *network_protocol="MPI-MT"; #elif defined(MPI_SPAWN) static const char *network_protocol="MPI-SPAWN"; #else static const char *network_protocol="TCP/IP Sockets"; #endif /*** stores cluster configuration. Initialized before user threads are created and then read-only ***/ armci_clus_t *armci_clus_info; # define GETHOSTNAME gethostname static char* merge_names(char *name) { int jump = 1, rem, to, from; int lenmes, lenbuf, curlen, totbuflen= armci_nproc*HOSTNAME_LEN; int len = strlen(name); char *work = malloc(totbuflen); if(!work)armci_die("armci: merge_names: malloc failed: ",totbuflen); strcpy(work, name); curlen = len+1; /* prefix tree merges names in the order of process numbering in log(P)time * result = name_1//name_2//...//name_P-1 */ do { jump *= 2; rem = armci_me%jump; if(rem){ to = armci_me - rem; armci_msg_snd(ARMCI_TAG, work, curlen, to); break; }else{ from = armci_me + jump/2; if(from < armci_nproc){ lenbuf = totbuflen - curlen; armci_msg_rcv(ARMCI_TAG, work+curlen, lenbuf, &lenmes, from); curlen += lenmes; } } }while (jump < armci_nproc); return(work); } static void process_hostlist(char *names) { #ifdef CLUSTER int i, cluster=0; char *s,*master; int len, root=0; /******** inspect list of machine names to determine locality ********/ if (armci_me==0){ /* first find out how many cluster nodes we got */ armci_nclus =1; s=master=names; for(i=1; i < armci_nproc; i++){ s += strlen(s)+1; if(strcmp(s,master)){ /* we found a new machine name on the list */ master = s; armci_nclus++; /*fprintf(stderr,"new name %s len =%d\n",master, strlen(master));*/ } } /* allocate memory */ armci_clus_info = (armci_clus_t*)malloc(armci_nclus*sizeof(armci_clus_t)); if(!armci_clus_info) armci_die("malloc failed for clusinfo",armci_nclus); /* fill the data structure -- go through the list again */ s=names; master="*-"; /* impossible hostname */ cluster =0; for(i=0; i < armci_nproc; i++){ if(strcmp(s,master)){ /* we found a new machine name on the list */ master = s; armci_clus_info[cluster].nslave=1; armci_clus_info[cluster].master=i; strcpy(armci_clus_info[cluster].hostname, master); #ifdef CHECK_NODE_NAMES /* need consecutive task id allocated on the same node * the current test only compares hostnames against first cluster */ if(cluster) if(!strcmp(master,armci_clus_info[0].hostname)){ /* we have seen that hostname before */ fprintf(stderr, "ARMCI supports block process mapping only\n"); armci_die("Cannot run: improper task to host mapping!",0); } #endif cluster++; } else{ /* the process is still on the same host */ armci_clus_info[cluster-1].nslave++; } s += strlen(s)+1; } if(armci_nclus != cluster) armci_die("inconsistency processing clusterinfo",armci_nclus); } /******** process 0 got all data ********/ /* now broadcast locality info struct to all processes * two steps are needed because of the unknown length of hostname list */ len = sizeof(int); armci_msg_brdcst(&armci_nclus, len, root); if(armci_me){ /* allocate memory */ armci_clus_info = (armci_clus_t*)malloc(armci_nclus*sizeof(armci_clus_t)); if(!armci_clus_info) armci_die("malloc failed for clusinfo",armci_nclus); } len = sizeof(armci_clus_t)*armci_nclus; armci_msg_brdcst(armci_clus_info, len, root); /******** all processes 0 got all data ********/ /* now determine current cluster node id by comparing me to master */ armci_clus_me = armci_nclus-1; for(i =0; i< armci_nclus-1; i++) if(armci_me < armci_clus_info[i+1].master){ armci_clus_me=i; break; } #else armci_clus_me=0; armci_nclus=1; armci_clus_info = (armci_clus_t*)malloc(armci_nclus*sizeof(armci_clus_t)); if(!armci_clus_info) armci_die("malloc failed for clusinfo",armci_nclus); strcpy(armci_clus_info[0].hostname, names); armci_clus_info[0].master=0; armci_clus_info[0].nslave=armci_nproc; #endif /* Starting process ID on my node */ armci_clus_first = armci_clus_info[armci_clus_me].master; /* Last process ID on my node */ armci_clus_last = armci_clus_first + armci_clus_info[armci_clus_me].nslave-1; } /*\ Substring Replacement: replace needle with nail in a haystack \*/ static char *substr_replace(char *haystack, char *needle, char *nail) { char *tmp, *pos, *first; size_t len=strlen(needle), nlen=strlen(nail),bytes; size_t left; pos = strstr(haystack,needle); if (pos ==NULL) return NULL; first= tmp = calloc(strlen(haystack)+nlen-len+1+1,1); if(first==NULL) return(NULL); bytes = pos - haystack; while(bytes){ *tmp = *haystack; tmp++; haystack++; bytes--;} while(nlen) { *tmp = *nail; tmp++; nail++; nlen--;} haystack += len; left = strlen(haystack); while(left>0){*tmp = *haystack; tmp++; haystack++; left --;} *tmp='\0'; return(first); } /*\ ARMCI_HOSTNAME_REPLACE contains "needle/nail" string to derive new hostname \*/ static char *new_hostname(char *host) { char *tmp, *needle, *nail; if((tmp =getenv("ARMCI_HOSTNAME_REPLACE"))){ needle = strdup(tmp); if(needle== NULL) return NULL; nail = strchr(needle,'/'); if(nail == NULL) return NULL; *nail = '\0'; nail++; if(nail == (needle+1)){ char* tmp1 = calloc(strlen(host)+strlen(nail)+1,1); if(tmp1 == NULL) return NULL; strcpy(tmp1,host); strcat(tmp1,nail); return tmp1; } return substr_replace(host,needle,nail); } else return NULL; } static void print_clus_info() { int i; if(PRINT_CLUSTER_INFO && armci_nclus > 1 && armci_me ==0){ #if defined(DATA_SERVER) || defined(SERVER_THREAD) printf("ARMCI configured for %d cluster nodes. Network protocol is '%s'.\n", armci_nclus, network_protocol); #else printf("ARMCI configured for %d cluster nodes\n", armci_nclus); #endif fflush(stdout); } if(armci_me==0 && DEBUG) for(i=0;i= MAX_HOSTNAME) armci_die("armci: hostname too long",strlen(tmp)); strcpy(name,tmp); printf("%d using %s hostname\n",armci_me, name); fflush(stdout); } len = strlen(name); #if ARMCI_ENABLE_GPC_CALLS /*a simple way to run as many servers as compute processes*/ enval = getenv("ARMCI_NSERV_EQ_NPROC"); if(enval != NULL){ sprintf(name+len,"n%d",getpid()); len = strlen(name); printf("\n%s\n",name); } #endif #ifdef HOSTNAME_TRUNCATE { /* in some cases (e.g.,SP) when name is used to determine * cluster structure but not to establish communication * we can truncate hostnames to save memory */ int i; limit = HOSTNAME_LEN-1; for(i=0; i",i+1); } if(len>limit)name[limit]='\0'; len =limit; } #else if(len >= HOSTNAME_LEN-1) armci_die("armci: gethostname overrun name string length",len); #endif #ifdef NO_SHMEM name[len]='0'+armci_me; name[len+1]='\0'; len++; #endif if(DEBUG) fprintf(stderr,"%d: %s len=%d\n",armci_me, name,(int)strlen(name)); #ifdef CLUSTER merged = merge_names(name); /* create hostname list */ process_hostlist(merged); /* compute cluster info */ free(merged); #else process_hostlist(name); /* compute cluster info */ #endif #if (defined(SYSV) || defined(WIN32) || defined(MMAP)) armci_set_shmem_limit_per_node(armci_clus_info[0].nslave); #endif armci_master = armci_clus_info[armci_clus_me].master; #ifdef NO_SHMEM int i; for(i=0;i= armci_nproc) armci_die("armci_clus_id: out of range",p); if (p < armci_clus_first){ from = 0; to = armci_clus_me; } else { from = armci_clus_me; to = armci_nclus; } found = to - 1; /* Binary search algorithm to be implemented, * sequential search for now */ for(c = from; c < to - 1; c++) if (p < armci_clus_info[c+1].master) { found = c; break; } return found; } /*\ return number of processes in the domain represented by id; id<0 means my node \*/ int armci_domain_nprocs(armci_domain_t domain, int id) { if(id >= armci_nclus) armci_die2("armci domain error",id,armci_nclus); /* This is an error condition */ if(id < 0) { fprintf(stderr,"[%d] Returned domain is invalid\n", armci_me); id = armci_clus_me; } return armci_clus_info[id].nslave; } /* return number of nodes in diven domain */ int armci_domain_count(armci_domain_t domain) { return armci_nclus; } /* return domain ID of the specified process */ int armci_domain_id(armci_domain_t domain, int glob_proc_id) { int id = glob_proc_id; if(id <0 || id >= armci_nproc) { armci_die2("armci domain error",id,armci_nproc); } return armci_clus_id(glob_proc_id); } /* return global ID of a process loc_proc_id in domain identified by id * armci_domain_nproc(id)< loc_proc_id >=0 */ int armci_domain_glob_proc_id(armci_domain_t domain, int id, int loc_proc_id) { if(id <0 || id >= armci_nclus) armci_die2("armci domain error",id,armci_nclus); if(loc_proc_id<0 || loc_proc_id>= armci_clus_info[id].nslave) armci_die2("armci domain proc error", loc_proc_id,armci_clus_info[id].nslave); return (armci_clus_info[id].master + loc_proc_id); } /* return ID of domain that the calling process belongs to */ int armci_domain_my_id(armci_domain_t domain) { return armci_clus_me; } /* Check whether the oricess is in the same domain */ int armci_domain_same_id (armci_domain_t domain, int proc) { int rc = SAMECLUSNODE(proc); return rc; } ga-5.9.2/armci/src/common/ds-shared.c000066400000000000000000001073611500715745200173240ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "armcip.h" #include "request.h" #include "message.h" #include "memlock.h" #include "copy.h" #include "gpc.h" #include "iterator.h" #if HAVE_STDIO_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_PROCESS_H # include #endif #if HAVE_UNISTD_H # include #endif #define DEBUG_ 0 #define DEBUG1 0 #ifndef SERV # define SERV 2 #endif #ifdef SOCKETS # define EQ_TAGS(a_, b_) ((a_) == (b_)) #else # define EQ_TAGS(a_, b_) !memcmp(&(a_), &(b_), sizeof(a_)) #endif int _armci_server_started=0; #if defined(SOCKETS) extern active_socks_t *_armci_active_socks; extern void armci_sock_send(int to, void *data, int len); #endif /**************************** pipelining for medium size msg ***********/ #ifdef PIPE_BUFSIZE static int pack_size(int len) { int oldlen = len; #define PIPE_ROUNDUP 512 #define PIPE_SHORT_ROUNDUP (1024) int n; if(len <4*PIPE_BUFSIZE){ len /=2; n = len%PIPE_SHORT_ROUNDUP; if(n)len += (PIPE_SHORT_ROUNDUP-n); } #if defined(VAPI) else if(len <25*PIPE_BUFSIZE){ len /=4; n = len%PIPE_SHORT_ROUNDUP; if(n)len += (PIPE_SHORT_ROUNDUP-n); } else if(len <41*PIPE_BUFSIZE){ len /=8; n = len%PIPE_SHORT_ROUNDUP; if(n)len += (PIPE_SHORT_ROUNDUP-n); } #else else if(len <32*PIPE_BUFSIZE){ len /=8; n = len%PIPE_SHORT_ROUNDUP; if(n)len += (PIPE_SHORT_ROUNDUP-n); } #endif else #if defined(VAPI) len = 8*4096; #else len = 64*1024-128; #endif #ifdef MAX_PIPELINE_CHUNKS if(oldlen/len > MAX_PIPELINE_CHUNKS-1){ len = oldlen/MAX_PIPELINE_CHUNKS; n = len%PIPE_SHORT_ROUNDUP; if(n)len += (PIPE_SHORT_ROUNDUP-n); } #endif return len; } #define PACK_SIZE1(_len) ((_len)datalen); arg.buf_posted = arg.buf = buf; arg.count = bufsize; arg.proc = (msginfo->operation==GET)?msginfo->to:msginfo->from; arg.op = msginfo->operation; armci_dispatch_strided(buf, stride_arr, count, strides, -1, -1, packsize, armcill_pipe_post_bufs,&arg); } void armci_pipe_receive_strided(request_header_t* msginfo, void *ptr, int stride_arr[], int count[], int strides) { buf_arg_t arg; int packsize = PACK_SIZE(msginfo->datalen); #if defined(VAPI) arg.buf_posted = msginfo->tag; #endif arg.buf = ptr; arg.count = 0; arg.proc = (msginfo->operation==GET)?msginfo->to:msginfo->from; arg.op = msginfo->operation; armci_dispatch_strided(ptr, stride_arr, count, strides, -1, -1, packsize, armcill_pipe_extract_data, &arg); } void armci_pipe_send_strided(request_header_t *msginfo, void *buf, int buflen, void *ptr, int *stride_arr,int count[],int strides) { buf_arg_t arg; int packsize = PACK_SIZE(msginfo->datalen); #if defined(VAPI) arg.buf_posted = msginfo->tag; #endif arg.buf = buf; arg.count = 0; arg.proc = (msginfo->operation==GET)?msginfo->from:msginfo->to; arg.op = msginfo->operation; armci_dispatch_strided(ptr, stride_arr, count, strides, -1, -1, packsize, armcill_pipe_send_chunk, &arg); } #endif /**************************** end of pipelining for medium size msg ***********/ #if defined(CLIENT_BUF_BYPASS) /**************** NOTE: for now this code can only handle contiguous data *****/ void armci_send_strided_data_bypass(int proc, request_header_t *msginfo, void *loc_buf, int msg_buflen, void *loc_ptr, int *loc_stride_arr, void *rem_ptr, int *rem_stride_arr, int *count, int stride_levels) { int armcill_server_wait_ack(int,int); if(DEBUG_){ printf("%d(s): strided(%d) get bypass from %d\n",armci_me,stride_levels, msginfo->from); fflush(stdout); } #ifdef VAPI if(stride_levels==0 && msginfo->pinned){ armci_send_contig_bypass(proc,msginfo,loc_ptr,rem_ptr,count[0]); return; } else { armci_die("***Contact Developers with machine/network info at hpctools@emsl.pnl.gov: bypass path wrongly invoked***",0); } #endif armci_pin_memory(loc_ptr, loc_stride_arr,count, stride_levels); /*wait until client ready*/ if(!armcill_server_wait_ack(msginfo->from,1)){ /*client was not able to pin memory, it will revert to default protocol hence, unpin the memory and leave. */ armci_unpin_memory(loc_ptr, loc_stride_arr,count, stride_levels); return; } armcill_server_put(msginfo->from,loc_ptr,rem_ptr,count[0]); armci_unpin_memory(loc_ptr, loc_stride_arr,count, stride_levels); if(DEBUG_){ printf("%d(s): strided(%d) get bypass done \n",armci_me,stride_levels); fflush(stdout); } } #endif /*\ client initialization \*/ void armci_client_code() { if(DEBUG_){ printf("in client after fork %d(%d)\n",armci_me,getpid()); fflush(stdout); } armci_client_connect_to_servers(); armci_msg_barrier(); if(DEBUG_){ printf("%d client connected to all %d servers\n",armci_me, armci_nclus-1); fflush(stdout); } } /*\ client sends request to server \*/ void armci_send_req(int proc, request_header_t* msginfo, int len) { int hdrlen = sizeof(request_header_t); int bytes; if(msginfo->operation == GET) { if(msginfo->format==VECTOR && msginfo->ehlen > 0) bytes = msginfo->dscrlen + hdrlen + msginfo->datalen; else bytes = msginfo->dscrlen + hdrlen; } else bytes = msginfo->bytes + hdrlen; if(DEBUG_){printf("%d: sending req %d (len=%d dscr=%d data=%d) to %d \n", armci_me, msginfo->operation, bytes,msginfo->dscrlen, msginfo->datalen,proc); fflush(stdout); } if(bytes > len)armci_die2("armci_send_req:buffer overflow",bytes,len); #ifdef PIPE_BUFSIZE if( # ifdef CLIENT_BUF_BYPASS (!msginfo->bypass) && # endif (msginfo->datalen>2*PIPE_MIN_BUFSIZE) && (msginfo->operation == GET) && (msginfo->format == STRIDED)){ char *buf = sizeof(void*) + (char*)(msginfo+1); int *ibuf = (int*)buf; int *strides =ibuf; int *stride_arr= ibuf +1; int *count = stride_arr + *strides; armci_pipe_prep_receive_strided(msginfo, buf, *strides, stride_arr, count, len-2**strides*sizeof(int)-sizeof(void*)); armci_pipe_send_req(proc,msginfo, bytes); }else #endif armci_send_req_msg(proc,msginfo, bytes); } /*\ client sends strided data + request to server \*/ void armci_send_strided(int proc, request_header_t *msginfo, char *bdata, void *ptr, int strides, int stride_arr[], int count[]) { int hdrlen = sizeof(request_header_t); int dscrlen = msginfo->dscrlen; int datalen = msginfo->datalen; int cluster = armci_clus_id(proc); int bytes; bytes = msginfo->bytes + hdrlen; if(DEBUG_){ printf("%d:sending strided %d to(%d,%d,%d) bytes=%d dslen=%d dlen=%d,\n", armci_me, msginfo->operation, msginfo->to, cluster, proc, bytes, dscrlen, datalen); fflush(stdout); } #if defined(SOCKETS) /* zero-copy optimization for large requests */ if(count[0] > TCP_PAYLOAD){ if(armci_send_req_msg_strided(proc, msginfo,ptr,strides, stride_arr, count))armci_die("armci_send_strided_req long: failed",0); return; /************** done **************/ } #elif defined(MPI_SPAWN_ZEROCOPY) /* zero-copy optimization for large requests */ if(msginfo->operation==PUT && msginfo->datalen==0 && count[0]>TCP_PAYLOAD){ if(armci_send_req_msg_strided(proc, msginfo,ptr,strides, stride_arr, count))armci_die("armci_send_strided_req long: failed",0); return; /************** done **************/ } #elif defined(PIPE_BUFSIZE___) #warning Network resource is only locked inside armci_send_req_msg, no common lock if((msginfo->datalen>2*PIPE_MIN_BUFSIZE) && (msginfo->operation == PUT)){ msginfo->bytes =0; /*** this tells server that we use pipelined put ****/ armci_send_req_msg(proc,msginfo, hdrlen+dscrlen); armci_pipe_send_strided(msginfo, bdata, datalen, ptr, stride_arr, count, strides); return; /************** done **************/ } #endif /* copy into a buffer before sending */ # ifdef SERV_BUF_IDX_T msginfo->inbuf = armcill_getbidx((msginfo->datalen+msginfo->dscrlen), proc, &msginfo->tag.ack); msginfo->tag.ack_ptr = &msginfo->tag.ack; # endif armci_write_strided(ptr, strides, stride_arr, count, bdata); if(armci_send_req_msg(proc,msginfo, bytes)) armci_die("armci_send_strided_req: failed",0); } #ifdef SOCKETS /* main handler to process responses from dataserver */ void armci_rcv_hdlr(request_header_t* msginfo) { int not_rcvd, my_id, nready, i, n, rc; msg_tag_t rcvd_id; BUF_INFO_T *info; n = MAX_BUFS + MAX_SMALL_BUFS; my_id = _armci_buf_to_bufinfo(msginfo)->bufid; not_rcvd = 1; while (not_rcvd) { THREAD_LOCK(armci_user_threads.net_lock); if (!_armci_buf_cmpld(my_id)) { /* my buffer has not been completed yet */ nready=armci_WaitSock(_armci_active_socks->socks,n,_armci_active_socks->ready); if (nready) { for (i = 0; i < n && nready; i++) { if (!_armci_active_socks->ready[i]) continue; /* not a ready sock */ nready--; /* receive data from socket _armci_active_socks->ready[i] * Note: socks[i] is the socket with incoming data HOWEVER * i is NOT necessarily the index of the associated buffer. * This is because armci_WaitSock will mark ALL entries for * the same socket as ready */ rc = armci_ReadFromSocket(_armci_active_socks->socks[i], &rcvd_id, sizeof(rcvd_id)); if(rc<0)armci_die("armci_rcv_strided_data: read tag failed",rc); /* receive response and process it */ msginfo = (request_header_t *)_armci_buf_ptr_from_id(rcvd_id); switch (msginfo->operation) { case PUT: case UNLOCK: armci_die("armci_rcv_hdlr: unexpected op",msginfo->operation); break; case GET: info = _armci_id_to_bufinfo(rcvd_id); armci_complete_req_buf(info, msginfo); break; case LOCK: case RMW: case ARMCI_SWAP: case ARMCI_SWAP_LONG: case ARMCI_FETCH_AND_ADD: case ARMCI_FETCH_AND_ADD_LONG: armci_rcv_data(msginfo->to, msginfo); break; case ACK: armci_rcv_data(NODE_SERVER(msginfo->to), msginfo); break; default: armci_die("armci_rcv_hdlr: unrecognized op",msginfo->operation); } /* clear this socket in active sockets */ _armci_active_socks->socks[i] = -1; THREAD_UNLOCK(armci_user_threads.net_lock); /* check if the data we received were sent to us */ if (rcvd_id == my_id) not_rcvd = 0; /* mark received buffer as completed */ _armci_buf_set_cmpld_idx(rcvd_id, 1); } if(nready)armci_die("armci_rcv_hdlr:nready in not consistent",nready); } else { /* timed out in select */ THREAD_UNLOCK(armci_user_threads.net_lock); cpu_yield(); } } else { /* buffer was completed by another thread */ THREAD_UNLOCK(armci_user_threads.net_lock); not_rcvd = 0; } } } #if 0 /* receives plain(contiguous) data from dataserver */ void armci_rcv_data_hdlr(int bufid) { request_header_t* msginfo; int proc, datalen; char *buf; /* obtain buffer and buffer info associated with this receive */ msginfo = (request_header_t *)_armci_buf_ptr_from_id(bufid); proc = msginfo->to; datalen = msginfo->datalen; if(datalen == 0) armci_die("armci_rcv_data_hdlr: no data to receive",datalen); if(datalen > (MSG_BUFLEN-sizeof(request_header_t)-sizeof(long))) armci_die("armci_rcv_data_hdlr: data overflowing rcv buffer",datalen); /* fills msginfo buffer */ buf = armci_ReadFromDirect(proc, msginfo, datalen); if ((char *)(msginfo+1) != buf) armci_die("armci_rcv_data_hdlr: buf != msginfo+1",datalen); } /* received strided data from dataserver */ void armci_rcv_strided_data_hdlr(int bufid) { BUF_INFO_T *info; char *dscr; request_header_t *msginfo; void *ptr; int proc, strides, *stride_arr, *count; char *databuf; /* obtain buffer and buffer info associated with this receive */ info = _armci_id_to_bufinfo(bufid); dscr = info->dscr; msginfo = (request_header_t *)_armci_buf_ptr_from_id(bufid); proc = msginfo->to; /* ptr, strides, stride_arr and count should be extracted from buf_info */ ptr = *(void**)dscr; dscr += sizeof(void*); strides = *(int*)dscr; dscr += sizeof(int); stride_arr = (int*)dscr; dscr += strides*sizeof(int); count = (int*)dscr; /* actual rcv: copied from old armci_rcv_strided_data */ /* zero-copy optimization for large requests */ if(count[0] > TCP_PAYLOAD){ armci_ReadStridedFromDirect(proc,msginfo,ptr,strides,stride_arr, count); return; /*********************** done ************************/ } databuf = armci_ReadFromDirect(proc,msginfo,msginfo->datalen); armci_read_strided(ptr, strides, stride_arr, count, databuf); } /* receives vector data from dataserver */ void armci_rcv_vector_data_hdlr(int bufid) { request_header_t* msginfo; int proc, datalen; char *buf; armci_giov_t *darr; /* obtain buffer and buffer info associated with this receive */ msginfo = (request_header_t *)_armci_buf_ptr_from_id(bufid); proc = msginfo->to; /*datalen = msginfo->datalen;*/ buf = (char *)(msginfo + 1); /* receive vector as cont block, data is in buf */ armci_rcv_data_hdlr(bufid); /* unpack vector */ /* armci_giov_t darr[], int len) */ armci_vector_from_buf(darr, len, buf); } #endif #endif /*\ client receives data from server \*/ char *armci_rcv_data(int proc, request_header_t* msginfo) { int datalen = msginfo->datalen; char *buf; if(DEBUG_) { printf("%d:armci_rcv_data: bytes= %d \n", armci_me, datalen); fflush(stdout); } if(datalen == 0) armci_die("armci_rcv_data: no data to receive",datalen); if(datalen > (((int)MSG_BUFLEN)-((int)sizeof(request_header_t))-((int)sizeof(long)))) armci_die("armci_rcv_data:data overflowing rcv buffer",datalen); buf = armci_ReadFromDirect(proc, msginfo, datalen); if(DEBUG_){ printf("%d:armci_rcv_data: got %d bytes \n",armci_me,datalen); fflush(stdout); } return(buf); } /*\ client receives vector data from server and unpacks to the right loc \*/ void armci_rcv_vector_data(int proc, request_header_t* msginfo, armci_giov_t darr[], int len) { char *buf = armci_rcv_data(proc, msginfo); armci_vector_from_buf(darr, len, buf); } /*\ client receives strided data from server \*/ #if 0 void armci_rcv_strided_data(int proc, request_header_t* msginfo, int datalen, void *ptr, int strides,int stride_arr[],int count[]) { extern BUF_INFO_T *_armci_tag_to_bufinfo(msg_tag_t tag); extern BUF_INFO_T *_armci_buf_to_bufinfo(void *buf); int not_received = 1; int sel, idx, rc, n=MAX_BUFS+MAX_SMALL_BUFS; char *databuf; msg_tag_t tag; BUF_INFO_T *info; char *dscr; void *loc_ptr; int stride_levels, *loc_stride_arr; request_header_t* buf; while (not_received) { THREAD_LOCK(armci_user_threads.net_lock); if (!_armci_buf_cmpld(msginfo)) { /* buffer not completed */ #ifdef SOCKETS sel=armci_WaitSock(_armci_active_socks->socks,n,_armci_active_socks->ready); #endif if (sel > 0) { /* pick a socket (should I check if sel > 1?) */ for(idx=0;idxready[idx])break; /* socks[idx] is the socket with incoming data HOWEVER idx is * NOT necessarily the index of the associated buffer. It is * because armci_WaitSock will mark ALL entries for the same * socket as ready */ /* read tag */ #ifdef SOCKETS rc=armci_ReadFromSocket(_armci_active_socks->socks[idx],&tag,sizeof(tag)); if(rc<0)armci_die("armci_rcv_strided_data: read tag failed",rc); #if 0 || defined(DTAG) idx = tag & DTAG; printf("DAG RCV: dtag=%ld,idx=%d,",tag,idx); tag >>= (sizeof(msg_id_t) * 8); printf("tag=%d,",tag); /* find proper buffer idx */ info = _armci_tag_to_bufinfo(tag); printf("idx(tag)=%d\n",info->bufid); fflush(stdout); if(info->bufid!=idx)armci_die("armci_rcv_strided_data: bad tag",tag); #else idx = tag; #endif #endif info = _armci_id_to_bufinfo(idx); dscr = info->dscr; /* network complete -- old armci_rcv_strided_data * ptr, strides, stride_arr and count should be extracted from buf_info */ ptr = *(void**)dscr; dscr += sizeof(void*); strides = *(int*)dscr; dscr += sizeof(int); stride_arr = (int*)dscr; dscr += strides*sizeof(int); count = (int*)dscr; /* find appropriate msginfo for received response */ buf = (request_header_t *)_armci_buf_ptr_from_id(idx); proc = buf->to; #ifdef CLIENT_BUF_BYPASS #error THIS PATH IS NOT UPDATED if(msginfo->bypass){ /* zero-copy protocol: get ACK and then unpin user buffer */ armci_rcv_strided_data_bypass(proc, msginfo, ptr, strides); armci_unpin_memory(ptr, stride_arr, count, strides); return; /* we are done */ } #endif #ifdef SOCKETS /* zero-copy optimization for large requests */ if (count[0] > TCP_PAYLOAD) armci_ReadStridedFromDirect(proc,buf,ptr,strides,stride_arr,count); else #elif defined(PIPE_BUFSIZE) if (buf->datalen > 2*PIPE_MIN_BUFSIZE) armci_pipe_receive_strided(buf, ptr, stride_arr, count, strides); else #endif { databuf = armci_ReadFromDirect(proc,buf,datalen); armci_read_strided(ptr, strides, stride_arr, count, databuf); } /* update active sockets */ _armci_active_socks->socks[idx] = -1; THREAD_UNLOCK(armci_user_threads.net_lock); /* check if the response we received was for our request */ if (EQ_TAGS(tag, _armci_buf_to_bufinfo(msginfo)->tag)) { /* if (tag == _armci_buf_to_bufinfo(buf)->tag) { _armci_buf_release(msginfo); released in armci_rem_strided */ not_received = 0; } else { _armci_buf_set_cmpld_idx(idx, 1); /* completed */ } } else { THREAD_UNLOCK(armci_user_threads.net_lock); #if 0 if(sel)armci_die("armci_rcv_strided_data: error in select",errno); else #endif cpu_yield(); /* timed out in select */ } } else { /* buffer was completed by another thread */ THREAD_UNLOCK(armci_user_threads.net_lock); /* _armci_buf_release(msginfo); released in armci_rem_strided */ not_received = 0; } } } #else void armci_rcv_strided_data(int proc, request_header_t* msginfo, int datalen, void *ptr, int strides,int stride_arr[],int count[]) { char *databuf; if(DEBUG_){ printf("%d: armci_rcv_strided_data: expecting datalen %d from %d\n", armci_me, datalen, proc); fflush(stdout); } #ifdef CLIENT_BUF_BYPASS if(msginfo->bypass){ /* zero-copy protocol: get ACK and then unpin user buffer */ armci_rcv_strided_data_bypass(proc, msginfo, ptr, strides); armci_unpin_memory(ptr, stride_arr, count, strides); return; /* we are done */ } #endif #if defined(SOCKETS) || defined(MPI_SPAWN_ZEROCOPY) /* zero-copy optimization for large requests */ if(count[0] > TCP_PAYLOAD){ armci_ReadStridedFromDirect(proc,msginfo,ptr,strides,stride_arr, count); return; /*********************** done ************************/ } #elif defined(PIPE_BUFSIZE) if(msginfo->datalen>2*PIPE_MIN_BUFSIZE){ armci_pipe_receive_strided(msginfo, ptr, stride_arr, count, strides); return; /*********************** done ************************/ } #endif #if !defined(GET_STRIDED_COPY_PIPELINED) databuf = armci_ReadFromDirect(proc,msginfo,datalen); armci_read_strided(ptr, strides, stride_arr, count, databuf); #else { int bytes_buf = 0, bytes_usr = 0, seg_off=0; stride_info_t sinfo; char *armci_ReadFromDirectSegment(int proc,request_header_t *msginfo, int datalen, int *bytes_buf); armci_stride_info_init(&sinfo,ptr,strides,stride_arr,count); do { databuf = armci_ReadFromDirectSegment(proc,msginfo,datalen,&bytes_buf); bytes_usr += armci_read_strided_inc(&sinfo,&databuf[bytes_usr],bytes_buf-bytes_usr, &seg_off); } while(bytes_bufdscrlen = 0; msginfo->from = armci_me; msginfo->to = SERVER_NODE(clus); msginfo->operation = ACK; msginfo->bytes =0; msginfo->datalen =sizeof(int); #ifdef SOCKETS msginfo->tag = BUF_TO_BUFINFO(msginfo)->bufid; #endif if(DEBUG_){ printf("%d(c):sending ACKreq to %d clus=%d\n",armci_me,msginfo->to,clus); fflush(stdout); } armci_send_req(armci_clus_info[clus].master, msginfo, bufsize); #ifdef SOCKETS armci_rcv_hdlr(msginfo); #else armci_rcv_data(armci_clus_info[clus].master, msginfo); /* receive ACK */ #endif assert(*(int*)(msginfo+1) == ACK); #ifdef VAPI assert(*(((int *)(msginfo+1))+1) == ARMCI_STAMP); #endif FREE_SEND_BUFFER(msginfo); } /***************************** server side *********************************/ static void armci_check_req(request_header_t *msginfo, int buflen) { if((msginfo->to != armci_me && msginfo->to < armci_master) || msginfo->to >= armci_master + armci_clus_info[armci_clus_me].nslave) armci_die("armci_rcv_req: invalid to", msginfo->to); #if 0 /* should be done in recv_req */ if(msginfo->operation != GET && msginfo->bytes > buflen) armci_die2("armci_rcv_req: message overflowing rcv buffer", msginfo->bytes,MSG_BUFLEN); #endif if(msginfo->dscrlen < 0) armci_die("armci_rcv_req: dscrlen < 0", msginfo->dscrlen); if(msginfo->datalen < 0) armci_die("armci_rcv_req: datalen < 0", msginfo->datalen); #ifndef PIPE_BUFSIZE if(msginfo->dscrlen > (int)msginfo->bytes) armci_die2("armci_rcv_req: dsclen > bytes", msginfo->dscrlen, msginfo->bytes); #endif } /*\ server response - send data to client \*/ void armci_send_data(request_header_t* msginfo, void *data) { int to = msginfo->from; #if defined(VAPI) /* if the data is in the pinned buffer: MessageRcvBuffer */ #if defined(PEND_BUFS) extern int armci_data_in_serv_buf(void *); if(armci_data_in_serv_buf(data)) #else if((data > (void *)MessageRcvBuffer) && (data < (void *)(MessageRcvBuffer + MSG_BUFLEN))) #endif /* write the message to the client */ armci_WriteToDirect(to, msginfo, data); else { /* copy the data to the MessageRcvBuffer */ char *buf = MessageRcvBuffer; # if defined(PEND_BUFS) fprintf(stderr, "%d:: op=%d len=%d ptr=%p working on unpinned memory. aborting!\n", armci_me, msginfo->operation,msginfo->datalen, data); assert(0); buf = NULL; /* extern char *armci_openib_get_msg_rcv_buf(int); */ /* buf = armci_openib_get_msg_rcv_buf(msginfo->from); */ # endif assert(buf != NULL); armci_copy(data, buf, msginfo->datalen); armci_WriteToDirect(to, msginfo, buf); } #else armci_WriteToDirect(to, msginfo, data); #endif } /*\ server sends strided data back to client \*/ void armci_send_strided_data(int proc, request_header_t *msginfo, char *bdata, void *ptr, int strides, int stride_arr[], int count[]) { int to = msginfo->from; if(DEBUG_){ printf("%d(server): sending datalen = %d to %d %p\n", armci_me, msginfo->datalen, to,ptr); fflush(stdout); } #if defined(SOCKETS) || defined(MPI_SPAWN_ZEROCOPY) /* zero-copy optimization for large requests */ if(count[0] > TCP_PAYLOAD){ armci_WriteStridedToDirect(to,msginfo,ptr, strides, stride_arr, count); return; /*********************** done ************************/ } #elif defined(PIPE_BUFSIZE) if(msginfo->datalen>2*PIPE_MIN_BUFSIZE) { armci_pipe_send_strided(msginfo, bdata, msginfo->datalen, ptr, stride_arr, count, strides); return; } #endif #if defined(GET_NO_SRV_COPY) { ARMCI_MEMHDL_T *mhloc=NULL;/*, *mhrem=NULL;*/ int nsegs, i; /* printf("%d(s): TRYING to use rdma contig to strided\n",armci_me); */ /* fflush(stdout); */ nsegs = 1; for(i=0;ioperation==GET && !msginfo->pinned && strides>=0 && get_armci_region_local_hndl(ptr,armci_clus_id(armci_me),&mhloc)) { /* printf("%d(s): using rdma contig to strided\n",armci_me); */ /* fflush(stdout); */ armci_server_rdma_strided_to_contig(ptr, stride_arr, count, strides, msginfo->tag.data_ptr, to, msginfo); return; } else { /* printf("%d(s): not taking rdma to contig path. mhloc=%p mhrem=%p\n",armci_me,mhloc,mhrem); */ } } #endif /* for small contiguous blocks copy into a buffer before sending */ armci_write_strided(ptr, strides, stride_arr, count, bdata); /* write the message to the client */ armci_WriteToDirect(to, msginfo, bdata); if(DEBUG_){ printf("%d(serv):sent len=%d to %d\n",armci_me,msginfo->datalen,to); fflush(stdout); } } /*\ server sends ACK to client \*/ void armci_server_ack(request_header_t* msginfo) { #if defined(PEND_BUFS) #else int ack=ACK; #endif if(DEBUG_){ printf("%d server: sending ACK to %d\n",armci_me,msginfo->from); fflush(stdout); } if(msginfo->datalen != sizeof(int)) armci_die("armci_server_ack: bad datalen=",msginfo->datalen); #if defined(PEND_BUFS) { /*Send from server known memory -- avoid extra buffers and copying on server*/ int *ack1 = (int *)(msginfo+1); /*msginfo is in some server buffer. I can overwrite the descriptor*/ *ack1 = ACK; assert(sizeof(request_header_t)+2*sizeof(int)from; if(DEBUG_){ printf("%d(serv):got %d request from %d\n",armci_me,msginfo->operation, from); fflush(stdout); } /*if(msginfo->operation==GET)fprintf(stderr,"GET request received with tag: %d\n",msginfo->tag);*/ switch(msginfo->operation){ case ACK: if(DEBUG_) { fprintf(stdout, "%d(server): got ACK request from %d\n", armci_me, msginfo->from); fflush(stdout); } #ifdef SOCKETS armci_sock_send(msginfo->from, &(msginfo->tag), sizeof(msg_tag_t)); #endif armci_server_ack(msginfo); break; case ATTACH: if(DEBUG_){ printf("%d(serv):got ATTACH request from%d\n",armci_me, from); fflush(stdout); } armci_server_ipc(msginfo, descr, buffer, buflen); break; #if defined(SOCKETS) || defined(MPI_SPAWN) || defined(MPI_MT) case QUIT: if(DEBUG_){ printf("%d(serv):got QUIT request from %d\n",armci_me, from); fflush(stdout); } armci_server_goodbye(msginfo); break; #endif case ARMCI_SWAP: case ARMCI_SWAP_LONG: case ARMCI_FETCH_AND_ADD: case ARMCI_FETCH_AND_ADD_LONG: armci_server_rmw(msginfo,descr,buffer); break; case LOCK: armci_server_lock(msginfo); break; case UNLOCK: armci_server_unlock(msginfo, descr); break; default: if(msginfo->format ==VECTOR) armci_server_vector(msginfo, descr, buffer, buflen); else if(msginfo->format ==STRIDED){ #if defined(VAPI) /* buffer bypass protocol */ if(msginfo->pinned == 1){ int armci_post_gather(void *, int *, int *,int, armci_vapi_memhndl_t *,int,int,int,void *); void * src_ptr; int stride_levels; int count[MAX_STRIDE_LEVEL]; int src_stride_arr[MAX_STRIDE_LEVEL]; int found; ARMCI_MEMHDL_T *mhandle; int i,num; if(DEBUG1){ printf("%d(s) : unpacking dscr\n",armci_me); fflush(stdout); } src_ptr = *(void**)descr; descr = (char*)descr + sizeof(void*); stride_levels = *(int*)descr; descr = (char*)descr + sizeof(int); for(i =0; ifrom,mytag,SERV,NULL ); mytag = (mytag+1)%MAX_PENDING; if(mytag==0)mytag=1; if(DEBUG1){ printf("%d(s) : finished posting %d gather\n", armci_me,num); fflush(stdout); } } else #endif armci_server(msginfo, descr, buffer, buflen); } else armci_die2("armci_data_serv: unknown format code", msginfo->format, msginfo->from); } } /*\ initialize connection and start server thread/processes \*/ void armci_start_server() { armci_init_connections(); #if defined(MPI_SPAWN) /* For MPI_SPAWN, this should be called by all processes */ armci_create_server_MPIprocess( ); #else if(armci_me == armci_master) { # ifdef SERVER_THREAD armci_create_server_thread( armci_server_code ); # else armci_create_server_process( armci_server_code ); # endif } #endif armci_client_code(); _armci_server_started=1; } void *armci_server_code(void *data) { #ifdef SERVER_THREAD #if (defined(VAPI)) && ARMCI_ENABLE_GPC_CALLS # ifdef PTHREADS extern pthread_t data_server; data_server = pthread_self(); # else armci_die("armci_server_code: threaded data servers not using pthreads not supported by gpc", 0); # endif #endif #endif if(DEBUG_) printf("%d: in server after creating thread.\n",armci_me); /* make initial contact with all the computing process */ armci_server_initial_connection(); if(DEBUG_) { printf("%d(server): connected to all computing processes\n",armci_me); fflush(stdout); } #if ARMCI_ENABLE_GPC_CALLS gpc_init(); #endif armci_call_data_server(); armci_transport_cleanup(); return(NULL); } /*\ request to QUIT sent by client \*/ void armci_serv_quit() { int bufsize = sizeof(request_header_t)+sizeof(int); int destproc; request_header_t *msginfo; destproc = SERVER_NODE(armci_clus_me); msginfo = (request_header_t*)GET_SEND_BUFFER(bufsize,QUIT,destproc); if(DEBUG_){ printf("%d master: sending quit request to server\n",armci_me); fflush(stdout); } msginfo->dscrlen = 0; msginfo->from = armci_me; msginfo->to = SERVER_NODE(armci_clus_me); msginfo->operation = QUIT; if(ACK_QUIT) msginfo->bytes = msginfo->datalen = sizeof(int); /* ACK */ else msginfo->bytes = msginfo->datalen = 0; /* no ACK */ armci_send_req(armci_master, msginfo, bufsize); if(ACK_QUIT){ int stat; stat = *(int*)armci_rcv_data(armci_master,msginfo); /* receive ACK */ if(stat != QUIT) armci_die("armci_serv_quit: wrong response from server", stat); FREE_SEND_BUFFER(msginfo); } } /*\ server action triggered by request to quit \*/ void armci_server_goodbye(request_header_t* msginfo) { int ack=QUIT; if(DEBUG_){ printf("%d server: terminating request by %d\n",armci_me,msginfo->from); fflush(stdout); } if(msginfo->datalen){ msginfo->datalen = -msginfo->datalen; if(msginfo->datalen != sizeof(int)) armci_die("armci_server_goodbye: bad datalen=",msginfo->datalen); armci_send_data(msginfo, &ack); } armci_transport_cleanup(); /* Finalizing data server process w.r.t. MPI is not portable */ _exit(0); } ga-5.9.2/armci/src/common/gpc.c000066400000000000000000000256021500715745200162200ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: gpc.c,v 1.7.4.4 2007-06-13 00:44:01 vinod Exp $ ***************************************************** Prototype of Global Procedure Calls. July/03 JN - shared memory version *************************************************************/ #if HAVE_STDIO_H # include #endif #include "armcip.h" #include "locks.h" #include "gpc.h" #define GPC_SLOTS 32 #define GPC_OFFSET -100 static void *_table[GPC_SLOTS]={ (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0}; /*\ callback functions must be registered -- user gets int handle back \*/ int ARMCI_Gpc_register( int (*func) ()) { int handle =-1, candidate = 0; PARMCI_Barrier(); do{ if(!_table[candidate]){ handle = candidate; _table[candidate]=func; } candidate++; }while(candidate < GPC_SLOTS && handle == -1); return(GPC_OFFSET-handle); } /*\ release/deassociate handle with previously registered callback function \*/ void ARMCI_Gpc_release(int handle) { int h = -handle + GPC_OFFSET; PARMCI_Barrier(); if(h<0 || h >= GPC_SLOTS) armci_die("ARMCI_Gpc_release: bad handle",h); _table[h] = (void*)0; } /*\ Send Request to Execute callback function in a global address space * Arguments: * f - handle to the callback function * p - remote processor * hdr - header data - used to pack extra args for callback (local buffer) * hlen - size of header data < ARMCI_GPC_HLEN * data - bulk data passed to callback (local buffer) * dlen - length of bulk data * rhdr - ptr to reply header (return args from callback) * rhlen - length of buffer to store reply header < ARMCI_GPC_HLEN * rdata - ptr to where reply data from callback should be stored (local buf) * rdlen - size of the buffer to store reply data * nbh - nonblocking handle * \*/ int ARMCI_Gpc_exec(int h, int p, void *hdr, int hlen, void *data, int dlen, void *rhdr, int rhlen, void *rdata, int rdlen, gpc_hdl_t* nbh) { int hnd = -h + GPC_OFFSET; int err = 0; armci_hdl_t *ahdl = (nbh ? &(nbh->ahdl): NULL); if(hnd <0 || hnd>= GPC_SLOTS) err += fprintf(stderr, "ARMCI_Gpc_exec: bad callback handle %d: %d\n",hnd,GPC_SLOTS); if(!_table[hnd]) err += fprintf(stderr, "ARMCI_Gpc_exec: NULL function %d",hnd); if(hlen<0 || hlen>=ARMCI_Gpc_get_hlen()) err += fprintf(stderr, "ARMCI_Gpc_exec: Invalid send header size %d %d\n", hlen, ARMCI_Gpc_get_hlen()); if(rhlen<0 || rhlen>=ARMCI_Gpc_get_hlen()) err += fprintf(stderr, "ARMCI_Gpc_exec: Invalid recv header size %d %d\n", rhlen, ARMCI_Gpc_get_hlen()); if(dlen<0 || dlen>=ARMCI_Gpc_get_dlen()) err += fprintf(stderr, "ARMCI_Gpc_exec: Invalid send data size %d %d\n", dlen, ARMCI_Gpc_get_dlen()); if(rdlen<0 || rdlen>=ARMCI_Gpc_get_dlen()) err += fprintf(stderr, "ARMCI_Gpc_exec: Invalid recv data size %d %d\n", rdlen, ARMCI_Gpc_get_dlen()); if(hlen>0 && hdr==NULL) err += fprintf(stderr, "ARMCI_Gpc_exec: Null send header for non-zero header size %d\n", hlen); if(rhlen>0 && rhdr==NULL) err += fprintf(stderr, "ARMCI_Gpc_exec: Null recv header for non-zero header size %d\n", rhlen); if(dlen>0 && data==NULL) err += fprintf(stderr, "ARMCI_Gpc_exec: Null send data for non-zero data size %d\n", dlen); if(rdlen>0 && rdata==NULL) err += fprintf(stderr, "ARMCI_Gpc_exec: Null recv data for non-zero header size %d\n", rdlen); if(p<0 || p >= armci_nproc) err += fprintf(stderr, "ARMCI_Gpc_exec: Invalid target processor id %d\n", p, armci_nproc); if(err) return FAIL; if(rhlen + rdlen == 0) armci_die("Zero reply header + data length not yet supported", 0); if(nbh) nbh->proc = p; #if 1 if(SAMECLUSNODE(p) && armci_nproc==1) { int rhsize, rdsize; int (*func)(); /* fprintf(stderr, "%d:: armci gpc exec. SAMECLUSNODE\n", armci_me); */ func = _table[hnd]; if(func(p, armci_me, hdr, hlen, data, dlen, rhdr, rhlen, &rhsize, rdata, rdlen, &rdsize, GPC_INIT) != GPC_DONE) { func(p, armci_me, hdr, hlen, data, dlen, rhdr, rhlen, &rhsize, rdata, rdlen, &rdsize, GPC_WAIT); } #ifndef VAPI PARMCI_Fence(p); #endif return 0; } #endif /* fprintf(stderr, "%d:: armci gpc exec. invoking armci gpc\n", armci_me); */ return armci_gpc(h, p, hdr, hlen, data, dlen, rhdr, rhlen, rdata, rdlen, ahdl); } /* func - handle to the function executed at each process in the chain callba- handle to the callback to be executed when hdr - header data used to pack extra args for callback (local buffer) hlen - size of header data < ARMCI_GPC_HLEN data - bulk data passed to callback (local buffer) dlen - length of bulk data rhdr - ptr to reply header (return args from callback) rhlen - length of buffer to store reply header < ARMCI_GPC_HLEN rdata - ptr to where reply data from callback should be stored (local buf) rdlen - size of the buffer to store reply data idlen - number of ID's idslst- list of id's in the chained GPC nbh - nonblocking handle which also acts as a context for each individual GPC Tree - the id of tree function used (default is 0=>binary, 1=>binomial, n=> user defined) */ int ARMCI_Gpc_chained_exec(int func, int callback, void *hdr, int hlen, void *data, int dlen, void *rhdr, int rhlen, void *rdata, int rdlen, int idlen, int *idlst, gpc_hdl_t* nbh, int TREE) { #if 0 int hnd = -func + GPC_OFFSET; int err = 0; armci_hdl_t *ahdl = (nbh ? &(nbh->ahdl): NULL); if(hnd <0 || hnd>= GPC_SLOTS) err += fprintf(stderr, "ARMCI_Gpc_exec: bad callback handle %d: %d\n",hnd,GPC_SLOTS); if(!_table[hnd]) err += fprintf(stderr, "ARMCI_Gpc_exec: NULL function %d",hnd); if(hlen<0 || hlen>=ARMCI_Gpc_get_hlen()) err += fprintf(stderr, "ARMCI_Gpc_exec: Invalid send header size %d %d\n", hlen, ARMCI_Gpc_get_hlen()); if(rhlen<0 || rhlen>=ARMCI_Gpc_get_hlen()) err += fprintf(stderr, "ARMCI_Gpc_exec: Invalid recv header size %d %d\n", rhlen, ARMCI_Gpc_get_hlen()); if(dlen<0 || dlen>=ARMCI_Gpc_get_dlen()) err += fprintf(stderr, "ARMCI_Gpc_exec: Invalid send data size %d %d\n", dlen, ARMCI_Gpc_get_dlen()); if(rdlen<0 || rdlen>=ARMCI_Gpc_get_dlen()) err += fprintf(stderr, "ARMCI_Gpc_exec: Invalid recv data size %d %d\n", rdlen, ARMCI_Gpc_get_dlen()); if(hlen>0 && hdr==NULL) err += fprintf(stderr, "ARMCI_Gpc_exec: Null send header for non-zero header size %d\n", hlen); if(rhlen>0 && rhdr==NULL) err += fprintf(stderr, "ARMCI_Gpc_exec: Null recv header for non-zero header size %d\n", rhlen); if(dlen>0 && data==NULL) err += fprintf(stderr, "ARMCI_Gpc_exec: Null send data for non-zero data size %d\n", dlen); if(rdlen>0 && rdata==NULL) err += fprintf(stderr, "ARMCI_Gpc_exec: Null recv data for non-zero header size %d\n", rdlen); if(p<0 || p >= armci_nproc) err += fprintf(stderr, "ARMCI_Gpc_exec: Invalid target processor id %d\n", p, armci_nproc); if(err) return FAIL; if(rhlen + rdlen == 0) armci_die("Zero reply header + data length not yet supported", 0); tree_id = armci_msg_generate_tree(idlst,idlen,id_tree,TREE); if(nbh) nbh->proc = p; #if 1 if(SAMECLUSNODE(p) && armci_nproc==1) { int rhsize, rdsize; int (*func)(); /* fprintf(stderr, "%d:: armci gpc exec. SAMECLUSNODE\n", armci_me); */ func = _table[hnd]; if(func(p, armci_me, hdr, hlen, data, dlen, rhdr, rhlen, &rhsize, rdata, rdlen, &rdsize, GPC_INIT) != GPC_DONE) { func(p, armci_me, hdr, hlen, data, dlen, rhdr, rhlen, &rhsize, rdata, rdlen, &rdsize, GPC_WAIT); } #ifndef VAPI PARMCI_Fence(p); #endif return 0; } #endif /* fprintf(stderr, "%d:: armci gpc exec. invoking armci gpc\n", armci_me); */ return armci_gpc(h, p, hdr, hlen, data, dlen, rhdr, rhlen, rdata, rdlen, ahdl); #endif } int armci_gpc_local_exec(int h, int to, int from, void *hdr, int hlen, void *data, int dlen, void *rhdr, int rhlen, void *rdata, int rdlen, int rtype) { int rhsize, rdsize; int (*func)(); int hnd = -h + GPC_OFFSET; if(hnd <0 || hnd>= GPC_SLOTS) armci_die2("armci_gpc_local_exec: bad callback handle",hnd,GPC_SLOTS); if(!_table[hnd]) armci_die("armci_gpc_local_exec: NULL function",hnd); func = _table[hnd]; if(!SAMECLUSNODE(to)) armci_die("armci_gpc_local_exec: GPC call to a different node received!", armci_me); /* func(to, from, hdr, hlen, data, dlen, rhdr, rhlen, &rhsize, */ /* rdata, rdlen, &rdsize); */ /* return 0; */ return func(to, from, hdr, hlen, data, dlen, rhdr, rhlen, &rhsize, rdata, rdlen, &rdsize, rtype); } /*\ * This is a template for the callback function * The arguments are passed as specified in ARMCI_Gpc_exec * In addition, * rhsize specifies the actual size of reply header data returned * rdsize specifies the actual size of reply data returned \*/ int example_func(int to, int from, void *hdr, int hlen, void *data, int dlen, void *rhdr, int rhlen, int *rhsize, void *rdata, int rdlen, int *rdsize, int rtype); /*\ * Translate pointer to memory on processor "proc" * to be used in a callback function send by processor "from" \*/ void * ARMCI_Gpc_translate(void *ptr, int proc, int from) { return ptr; } /*\ acquire lock in a callback function executed in context of processor "proc" \*/ void ARMCI_Gpc_lock(int proc) { #if defined(CLUSTER) int lock = (proc-armci_clus_info[armci_clus_id(proc)].master)%NUM_LOCKS; #else int lock = 0; #endif NATIVE_LOCK(lock,proc); } /*\ try acquire lock in a callback function to be executed in context of * processor "proc" * return value: 1 - success * 0 - failure (already locked by another thread) \*/ int ARMCI_Gpc_trylock(int proc) { armci_die("ARMCI_Gpc_trylock: not yet implemented",0); return 0; } /*\ release lock in a callback function executed in context of processor "proc" \*/ void ARMCI_Gpc_unlock(int proc) { #if defined(CLUSTER) int lock = (proc-armci_clus_info[armci_clus_id(proc)].master)%NUM_LOCKS; #else int lock = 0; #endif NATIVE_UNLOCK(lock,proc); } void ARMCI_Gpc_init_handle(gpc_hdl_t *nbh) { nbh->proc = armci_me; ARMCI_INIT_HANDLE(&nbh->ahdl); } void ARMCI_Gpc_wait(gpc_hdl_t *nbh) { if(SAMECLUSNODE(nbh->proc)) return; PARMCI_Wait(&nbh->ahdl); } void ARMCI_Gpc_test(gpc_hdl_t *nbh) { if(SAMECLUSNODE(nbh->proc)) return; PARMCI_Test(&nbh->ahdl); } #define ARMCI_GPC_HLEN 65536 #define ARMCI_GPC_DLEN 65536 int ARMCI_Gpc_get_hlen() { return ARMCI_GPC_HLEN; } int ARMCI_Gpc_get_dlen() { return ARMCI_GPC_DLEN; } ga-5.9.2/armci/src/common/groups.c000066400000000000000000000415651500715745200167740ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: groups.c,v 1.4.6.2 2007-08-15 08:37:16 manoj Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_ASSERT_H # include #endif #ifndef MSG_COMMS_MPI # define MSG_COMMS_MPI #endif #include "armcip.h" #include "message.h" #define DEBUG_ 0 MPI_Comm ARMCI_COMM_WORLD; /*dup of MPI_COMM_WORLD. Initialized first thing in ARMCI_Init*/ ARMCI_Group ARMCI_Default_Proc_Group = 0; ARMCI_Group ARMCI_World_Proc_Group = 0; typedef struct group_list_struct { ARMCI_Group group; ARMCI_iGroup igroup; struct group_list_struct *next; } group_list_t; group_list_t *group_list = NULL; ARMCI_iGroup* armci_get_igroup_from_group(ARMCI_Group *group) { group_list_t *current_group_list_item = group_list; assert(group_list != NULL); while (current_group_list_item != NULL) { if (current_group_list_item->group == *group) { return ¤t_group_list_item->igroup; } current_group_list_item = current_group_list_item->next; } armci_die("ARMCI_Group lookup failed", -1); return NULL; } static void armci_create_group_and_igroup(ARMCI_Group *group, ARMCI_iGroup **igroup) { group_list_t *new_group_list_item = NULL; group_list_t *last_group_list_item = NULL; /* create the new group in the linked list */ last_group_list_item = group_list; while (last_group_list_item->next != NULL) { last_group_list_item = last_group_list_item->next; } new_group_list_item = malloc(sizeof(group_list_t)); new_group_list_item->group = last_group_list_item->group + 1; new_group_list_item->next = NULL; *igroup = &new_group_list_item->igroup; *group = new_group_list_item->group; last_group_list_item->next = new_group_list_item; } #ifdef ARMCI_GROUP void ARMCI_Bcast_(void *buffer, int len, int root, ARMCI_Group *group) { armci_msg_group_bcast_scope(SCOPE_ALL, buffer, len, ARMCI_Absolute_id(group, root), group); } #else void ARMCI_Bcast_(void *buffer, int len, int root, ARMCI_Comm comm) { int result; MPI_Comm_compare(comm, ARMCI_COMM_WORLD, &result); if(result == MPI_IDENT) armci_msg_brdcst(buffer, len, root); else MPI_Bcast(buffer, len, MPI_BYTE, root, (MPI_Comm)comm); } #endif int ARMCI_Group_rank(ARMCI_Group *group, int *rank) { ARMCI_iGroup *igroup = armci_get_igroup_from_group(group); #ifdef ARMCI_GROUP if(!igroup) return MPI_ERR_GROUP; *rank = igroup->grp_attr.grp_me; return MPI_SUCCESS; #else return MPI_Group_rank((MPI_Group)(igroup->igroup), rank); #endif } void ARMCI_Group_size(ARMCI_Group *group, int *size) { ARMCI_iGroup *igroup = armci_get_igroup_from_group(group); #ifdef ARMCI_GROUP *size = igroup->grp_attr.nproc; #else MPI_Group_size((MPI_Group)(igroup->igroup), size); #endif } int ARMCI_Absolute_id(ARMCI_Group *group,int group_rank) { int abs_rank,status; ARMCI_iGroup *igroup = armci_get_igroup_from_group(group); #ifdef ARMCI_GROUP assert(group_rank < igroup->grp_attr.nproc); return igroup->grp_attr.proc_list[group_rank]; #else MPI_Group grp; status = MPI_Comm_group(ARMCI_COMM_WORLD,&grp); assert(MPI_SUCCESS == status); MPI_Group_translate_ranks(igroup->igroup,1,&group_rank,grp,&abs_rank); return(abs_rank); #endif } void ARMCI_Group_set_default(ARMCI_Group *group) { ARMCI_Default_Proc_Group = *group; } void ARMCI_Group_get_default(ARMCI_Group *group_out) { *group_out = ARMCI_Default_Proc_Group; } void ARMCI_Group_get_world(ARMCI_Group *group_out) { *group_out = ARMCI_World_Proc_Group; } static void get_group_clus_id(ARMCI_iGroup *igroup, int grp_nproc, int *grp_clus_id) { #ifdef ARMCI_GROUP int i; assert(grp_nproc<=igroup->grp_attr.nproc); for(i=0; igrp_attr.proc_list[i]); } #else int i, *ranks1, *ranks2; MPI_Group group2; /* Takes the list of processes from one group and attempts to determine * the corresponding ranks in a second group (here, ARMCI_COMM_WORLD) */ ranks1 = (int *)malloc(2*grp_nproc*sizeof(int)); ranks2 = ranks1 + grp_nproc; for(i=0; iigroup, grp_nproc, ranks1, group2, ranks2); /* get the clus_id of processes */ for(i=0; iicomm; #endif int grp_me, grp_nproc, grp_nclus, grp_clus_me; armci_clus_t *grp_clus_info=NULL; #ifdef CLUSTER int i, len, root=0; #endif #ifndef ARMCI_GROUP if(comm==MPI_COMM_NULL || igroup->igroup==MPI_GROUP_NULL) armci_die("group_process_list: NULL COMMUNICATOR",0); #endif ARMCI_Group_rank(group, &grp_me); ARMCI_Group_size(group, &grp_nproc); #ifdef CLUSTER # ifdef ARMCI_GROUP /*all processes construct the clus_info structure in parallel*/ grp_clus_info = group_construct_clusinfo(&grp_nclus, group); # else /* process 0 gets group cluster information: grp_nclus, grp_clus_info */ if(grp_me == 0) { grp_clus_info = group_construct_clusinfo(&grp_nclus, group); } /* process 0 broadcasts group cluster information */ len = sizeof(int); ARMCI_Bcast_(&grp_nclus, len, root, comm); if(grp_me){ /* allocate memory */ grp_clus_info = (armci_clus_t*)malloc(grp_nclus*sizeof(armci_clus_t)); if(!armci_clus_info)armci_die("malloc failed for clusinfo",armci_nclus); } len = sizeof(armci_clus_t)*grp_nclus; ARMCI_Bcast_(grp_clus_info, len, root, comm); # endif /* determine current group cluster node id by comparing me to master */ grp_clus_me = grp_nclus-1; for(i =0; i< grp_nclus-1; i++) { if(grp_me < grp_clus_info[i+1].master){ grp_clus_me=i; break; } } #else /* !CLUSTER */ grp_clus_me = 0; grp_nclus = 1; grp_clus_info = (armci_clus_t*)malloc(grp_nclus*sizeof(armci_clus_t)); if(!grp_clus_info)armci_die("malloc failed for clusinfo",grp_nclus); strcpy(grp_clus_info[0].hostname, armci_clus_info[0].hostname); grp_clus_info[0].master=0; grp_clus_info[0].nslave=grp_nproc; #endif /* CLUSTER */ #ifdef ARMCI_GROUP /*Set in ARMCI_Group_create. ARMCI_Group_rank is used before setting this field. So moving it there in the generic implementation.*/ #else grp_attr->grp_me = grp_me; #endif grp_attr->grp_clus_info = grp_clus_info; grp_attr->grp_nclus = grp_nclus; grp_attr->grp_clus_me = grp_clus_me; } /* attribute caching: group_cluster_information and memory_offset should be cached in group data structure */ static void armci_cache_attr(ARMCI_Group *group) { armci_grp_attr_t *grp_attr; ARMCI_iGroup *igroup = armci_get_igroup_from_group(group); /* allocate storage for the attribute content. Note: Attribute contents should be stored in persistent memory */ grp_attr = &(igroup->grp_attr); /* get group cluster information and grp_attr */ group_process_list(group, grp_attr); } armci_grp_attr_t *ARMCI_Group_getattr(ARMCI_Group *group) { ARMCI_iGroup *igroup = armci_get_igroup_from_group(group); return(&(igroup->grp_attr)); } static void armci_igroup_finalize(ARMCI_iGroup *igroup) { #ifdef ARMCI_GROUP int world_me, i; world_me = armci_msg_me(); for(i=0; igrp_attr.nproc; i++) { if(igroup->grp_attr.proc_list[i] == world_me) { break; } } if(i==igroup->grp_attr.nproc) { return; /*not in group to be freed*/ } assert(igroup); free(igroup->grp_attr.grp_clus_info); free(igroup->grp_attr.proc_list); igroup->grp_attr.nproc = 0; #else int rv; assert(igroup); /*the following was causing seg fault*/ /*free(igroup->grp_attr.grp_clus_info);*/ rv=MPI_Group_free(&(igroup->igroup)); if(rv != MPI_SUCCESS) armci_die("MPI_Group_free: Failed ",armci_me); if(igroup->icomm != MPI_COMM_NULL) { rv = MPI_Comm_free( (MPI_Comm*)&(igroup->icomm) ); if(rv != MPI_SUCCESS) armci_die("MPI_Comm_free: Failed ",armci_me); } #endif } void ARMCI_Group_free(ARMCI_Group *group) { group_list_t *current_group_list_item = group_list; group_list_t *previous_group_list_item = NULL; /* find the group to free */ while (current_group_list_item != NULL) { if (current_group_list_item->group == *group) { break; } previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; } /* make sure we found a group */ assert(current_group_list_item != NULL); /* remove the group from the linked list */ if (previous_group_list_item != NULL) { previous_group_list_item->next = current_group_list_item->next; } /* free the group */ armci_igroup_finalize(¤t_group_list_item->igroup); free(current_group_list_item); } /* Create a child group for to the given group. @param n IN #procs in this group (<= that in group_parent) @param pid_list IN The list of proc ids (w.r.t. group_parent) @param group_out OUT Handle to store the created group @param group_parent IN Parent group */ void ARMCI_Group_create_child(int n, int *pid_list, ARMCI_Group *group_out, ARMCI_Group *grp_parent) { int grp_me; ARMCI_iGroup *igroup = NULL; #ifdef ARMCI_GROUP int i, world_me, parent_grp_me; armci_grp_attr_t *grp_attr = NULL; #else int rv; ARMCI_iGroup *igroup_parent = NULL; MPI_Group *group_parent = NULL; MPI_Comm *comm_parent = NULL; #endif armci_create_group_and_igroup(group_out, &igroup); #ifdef ARMCI_GROUP grp_attr = &igroup->grp_attr; ARMCI_Group_rank(grp_parent, &parent_grp_me); for(i=0; inproc=0; grp_attr->proc_list = NULL; return; /*not in group to be created*/ } for(i=0; i pid_list[i+1]){ armci_die("ARMCI_Group_create: Process ids are not sorted ",armci_me); break; } } grp_attr->grp_clus_info = NULL; grp_attr->nproc = n; grp_attr->proc_list = (int *)malloc(n*sizeof(int)); assert(grp_attr->proc_list!=NULL); for(i=0; iproc_list[i] = ARMCI_Absolute_id(grp_parent,pid_list[i]); } world_me = armci_msg_me(); grp_attr->grp_me = grp_me = MPI_UNDEFINED; for(i=0; igrp_attr.proc_list[i] == world_me) { grp_attr->grp_me = grp_me = i; break; } } if(grp_me != MPI_UNDEFINED) armci_cache_attr(group_out); armci_msg_group_barrier(group_out); #else igroup_parent = armci_get_igroup_from_group(grp_parent); /* NOTE: default group is the parent group */ group_parent = &(igroup_parent->igroup); comm_parent = &(igroup_parent->icomm); rv=MPI_Group_incl(*group_parent, n, pid_list, &(igroup->igroup)); if(rv != MPI_SUCCESS) armci_die("MPI_Group_incl: Failed ",armci_me); { MPI_Comm comm, comm1, comm2; int lvl=1, local_ldr_pos; MPI_Group_rank((MPI_Group)(igroup->igroup), &grp_me); if(grp_me == MPI_UNDEFINED) { igroup->icomm = MPI_COMM_NULL; /*FIXME: keeping the group around for now*/ return; } assert(grp_me>=0); /*SK: sanity check for the following bitwise operations*/ MPI_Comm_dup(MPI_COMM_SELF, &comm); /*FIXME: can be optimized away*/ local_ldr_pos = grp_me; while(n> lvl) { int tag=0; int remote_ldr_pos = local_ldr_pos^lvl; if(remote_ldr_pos < n) { int remote_leader = pid_list[remote_ldr_pos]; MPI_Comm peer_comm = *comm_parent; int high = (local_ldr_posicomm = comm; MPI_Group_free(&igroup->igroup); /*cleanup temporary group*/ MPI_Comm_group(igroup->icomm, &igroup->igroup); /*the group associated with comm*/ igroup->grp_attr.grp_clus_info=NULL; /* processes belong to this group should cache attributes */ armci_cache_attr(group_out); } #endif } void ARMCI_Group_create(int n, int *pid_list, ARMCI_Group *group_out) { ARMCI_Group_create_child(n, pid_list, group_out, (ARMCI_Group *)&ARMCI_Default_Proc_Group); } void armci_group_init() { #ifdef ARMCI_GROUP int i; #else int grp_me; #endif ARMCI_iGroup *igroup; /* Initially, World group is the default group */ ARMCI_World_Proc_Group = 0; ARMCI_Default_Proc_Group = 0; /* create the head of the group linked list */ assert(group_list == NULL); group_list = malloc(sizeof(group_list_t)); group_list->group = ARMCI_World_Proc_Group; group_list->next = NULL; igroup = &group_list->igroup; #ifdef ARMCI_GROUP /*setup the world proc group*/ igroup->grp_attr.nproc = armci_msg_nproc(); igroup->grp_attr.grp_me = armci_msg_me(); igroup->grp_attr.proc_list = (int *)malloc(igroup->grp_attr.nproc*sizeof(int)); assert(igroup->grp_attr.proc_list != NULL); for(i=0; igrp_attr.nproc; i++) { igroup->grp_attr.proc_list[i] = i; } igroup->grp_attr.grp_clus_info = NULL; armci_cache_attr(&ARMCI_World_Proc_Group); #else /* save MPI world group and communicatior in ARMCI_World_Proc_Group */ igroup->icomm = ARMCI_COMM_WORLD; MPI_Comm_group(ARMCI_COMM_WORLD, &(igroup->igroup)); /* processes belong to this group should cache attributes */ MPI_Group_rank((MPI_Group)(igroup->igroup), &grp_me); if(grp_me != MPI_UNDEFINED) { armci_cache_attr(&ARMCI_World_Proc_Group); } #endif } void armci_group_finalize() { group_list_t *current_group_list_item = group_list; group_list_t *previous_group_list_item = NULL; /* don't free the world group (the list head) */ current_group_list_item = current_group_list_item->next; while (current_group_list_item != NULL) { previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; armci_igroup_finalize(&previous_group_list_item->igroup); free(previous_group_list_item); } } /* ISSUES: 1. Make sure ARMCI_Group_free frees the attribute data structures 2. replace malloc with, kr_malloc using local_context. */ ga-5.9.2/armci/src/common/iterator.c000066400000000000000000000104131500715745200172720ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** @file iterator.h * @author Sriram Krishnamoorthy * @brief Stride iterator. * An iterator for the stride descriptor to reuse common traversal * functionality. More functionality related to the strided * descriptor reusable across files will be extracted here as well. */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #include "iterator.h" /**Create a stride iterator. * @param base_ptr IN Starting pointer for stride descriptor * @param stride_levels IN #stride levels * @param stride_arr IN the strides (arr of size[stride_levels]) * @param seg_count IN #segments in each stride * level([stride_levels+1]) * @return Handle to stride iterator created */ void armci_stride_info_init(stride_info_t *sinfo, void *base_ptr, int stride_levels, const int *stride_arr, const int *seg_count) { int i; dassert(1,sinfo!=NULL); dassert(1,stride_levels>=0); dassert(1,stride_levels<=ARMCI_MAX_STRIDE_LEVEL); for(i=0; i= seg_count[0]); else dassert(1,stride_arr[i] >= stride_arr[i-1]*seg_count[i]); } sinfo->base_ptr= base_ptr; sinfo->stride_levels = stride_levels; for(i=0; istride_arr[i] = stride_arr[i]; } for(i=0; iseg_count[i] = seg_count[i]; } sinfo->size=1; for(i=1; isize *= sinfo->seg_count[i]; } dassert(1,sinfo->size>0); sinfo->pos=0; for(i=0; iitr[i] = 0; } } /**Destroy a stride iterator. * @param psitr IN/OUT Pointer to stride iterator * @return void */ void armci_stride_info_destroy(stride_info_t *sinfo) { } /**Size of the stride iterator. Defined as total #contiguous * segments in the stride iterator. * @param sitr IN Handle to stride iterator * @return Size of the stride iterator */ int armci_stride_info_size(stride_info_t *sinfo) { dassert(1,sinfo!=NULL); return sinfo->size; } /**Position of the stride iterator. Between 0 and (size-1), * inclusive. Position is the index of the contiguous segment * currently traversed by the iterator. * @param sitr IN Handle to stride descriptor * @return Position of the iterator */ int armci_stride_info_pos(stride_info_t *sinfo) { dassert(1,sinfo!=NULL); return sinfo->pos; } /**Move the iterator to the next position. Assumes position<=size. * @param sitr IN Handle to stride descriptor * @return void */ void armci_stride_info_next(stride_info_t *sinfo) { int i; dassert(1,sinfo!=NULL); dassert(1,sinfo->pos size); sinfo->pos += 1; if(sinfo->stride_levels>0) { sinfo->itr[0] += 1; for(i=0; istride_levels-1 && sinfo->itr[i]==sinfo->seg_count[i+1]; i++) { sinfo->itr[i] = 0; sinfo->itr[i+1] += 1; } dassert(1,sinfo->itr[i] <= sinfo->seg_count[i+1]); } } /**Get pointer to the contiguous segment currently being * traversed. This is the pointer to the user buffer. * @param sitr IN Handle to stride descriptor * @return pointer to current contiguous segment */ void *armci_stride_info_seg_ptr(stride_info_t *sinfo) { dassert(1,sinfo!=NULL); return sinfo->base_ptr + armci_stride_info_seg_off(sinfo); } /**Get the size of the current segment. * @param sitr IN Handle to stride descriptor * @return Size of the current segment */ int armci_stride_info_seg_size(stride_info_t *sinfo) { dassert(1,sinfo!=NULL); return sinfo->seg_count[0]; } /**Get the offset of the current segment with respect to the start of * the first segment (a.k.a src_ptr) * @param sitr IN Handle to stride descriptor * @return Offset of the current segment */ int armci_stride_info_seg_off(stride_info_t *sinfo) { int i; int off; dassert(1,sinfo!=NULL); off=0; for(i=0; istride_levels; i++) { off += sinfo->itr[i] * sinfo->stride_arr[i]; } return off; } /**Check if there are more segments to iterate over. (a.k.a * positionpossize; } ga-5.9.2/armci/src/common/pack.c000066400000000000000000000312401500715745200163600ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: pack.c,v 1.36.10.1 2006-12-14 13:24:37 manoj Exp $ */ #include "armcip.h" #if HAVE_STDIO_H # include #endif #if !defined(ACC_COPY) &&!defined(CYGNUS)&&!defined(CYGWIN) # define REMOTE_OP #endif #if defined(REMOTE_OP) # define OP_STRIDED armci_rem_strided #else # define OP_STRIDED(_a,_b,_c,_d,_e,_f,_g,_h,_i,_delete1,_j,_hdl)\ armci_op_strided(_a,_b,_c,_d,_e,_f,_g,_h,_i,_j,_hdl) #endif /*\ determine if patch fits in the ARMCI buffer, and if not * at which stride level (patch dim) need to decompose it * *fit_level is the value of stride level to perform packing at * *nb means number of elements of count[*fit_level] that fit in buf \*/ static void armci_fit_buffer(int count[], int stride_levels, int* fit_level, int *nb, int bufsize) { int bytes=1, sbytes=1; int level; /* find out at which stride level BUFFER becomes too small */ for(level=0; level<= stride_levels; level++){ sbytes = bytes; /* store #bytes at current level to save div cost later */ bytes *= count[level]; if(bufsize < bytes) break; } /* buffer big enough for entire patch */ if(bufsize >= bytes){ *fit_level = stride_levels; *nb = count[stride_levels]; return; } /* buffer too small */ switch (level){ case 0: /* smaller than a single column */ *fit_level = 0; *nb = bufsize; break; case -1: /* one column fits */ *fit_level = 0; *nb = sbytes; break; default: /* it could keep nb instances of (level-1)-dimensional patch */ *fit_level = level; *nb = bufsize/sbytes; } } /*\ The function decomposes a multi-dimensional patch so that it fits in the * internal ARMCI buffer. * It works by recursively reducing patch dimension until some portion of the * subpatch fits in the buffer. * The recursive process is controlled by "fit_level" and "nb" arguments, * which have to be set to -1 at the top-level of the recursion tree. * * Argument last and variable looplast are used to indicate to sending/packing * routine that we are dealing with the last portion of the request. * Due to the recursive nature of packing code, the algorithm is following: * if last=1 then internal for loop passes 1 for the last chunk * else it passes 0 * \*/ int armci_pack_strided(int op, void* scale, int proc, void *src_ptr, int src_stride_arr[], void* dst_ptr, int dst_stride_arr[], int count[], int stride_levels, ext_header_t *h, int fit_level, int nb, int last,armci_ihdl_t nb_handle) { int rc=0, bufsize=BUFSIZE,noswap=0; long sn; void *src, *dst; #ifdef REMOTE_OP int flag=0; #else int flag=1; #endif int b; static int call_count; #ifdef STRIDED_GET_BUFLEN if(op==GET)bufsize=STRIDED_GET_BUFLEN; #endif #if (defined(GM_) || defined(VAPI_)) /*we cant assume that the entire available buffer will be used for data, fact that the header and descriptor also go in the same buffer should be considered while packing. */ bufsize-=(sizeof(request_header_t)+(MAX_STRIDE_LEVEL+4)*sizeof(int)+2*sizeof(void *)); # if defined(PIPE_BUFSIZE) && defined(MAX_PIPELINE_CHUNKS) bufsize-=8*MAX_PIPELINE_CHUNKS; # endif #endif #ifdef BALANCE_FACTOR /* Added the following for balancing buffers */ if(op==PUT){ int bytes=1, i; for(i=0; i<= stride_levels; i++) bytes *= count[i]; if(bytes > bufsize && bytes/bufsize < 3 && bytes%bufsize < BALANCE_BUFSIZE){ /* bytes div bufsize - 1 is to increase the balence factor for 3 buffer case */ bufsize = bytes/ (bytes/bufsize - 1 + BALANCE_FACTOR); noswap = 1; /*** yuck: if set to 1, error in buffers.c ***/ } bytes = bufsize%8; bufsize -= bytes; } #endif /* determine decomposition of the patch to fit in the buffer */ if(fit_level<0){ armci_fit_buffer(count, stride_levels, &fit_level, &nb, bufsize); last = 1; } if(fit_level == stride_levels){ /* we can fit subpatch into the buffer */ int chunk = count[fit_level]; int dst_stride, src_stride; if(nb == chunk){ /* take shortcut when whole patch fits in the buffer */ if(h) h->last = last?1:0; if(nb_handle && call_count ){ nb_handle->bufid=NB_MULTI; call_count++; } return(OP_STRIDED(op, scale, proc, src_ptr, src_stride_arr, dst_ptr,dst_stride_arr,count,stride_levels,h,flag,nb_handle)); } if(fit_level){ dst_stride = dst_stride_arr[fit_level -1]; src_stride = src_stride_arr[fit_level -1]; }else{ dst_stride = src_stride = 1; } if(op == GET || noswap == 1) b =nb; else{ b = chunk%nb; if(b==0)b=nb; } /* put smallest piece first */ for(sn = 0; sn < chunk; ){ src = (char*)src_ptr + src_stride* sn; dst = (char*)dst_ptr + dst_stride* sn; count[fit_level] = ARMCI_MIN(b, chunk-sn); /*modify count for this level*/ if(h) h->last = (last && ((sn+b)>=chunk))? 1: 0 ; if(nb_handle)call_count++; rc = OP_STRIDED( op, scale, proc, src, src_stride_arr, dst,dst_stride_arr,count,fit_level,h,flag,nb_handle); if(rc) break; sn += b; b = nb; } count[fit_level] = chunk; /* restore original count */ } else { for(sn = 0; sn < count[stride_levels]; sn++){ int looplast =0; src = (char*)src_ptr + src_stride_arr[stride_levels -1]* sn; dst = (char*)dst_ptr + dst_stride_arr[stride_levels -1]* sn; if(last && (sn == count[stride_levels]-1)) looplast =1; rc = armci_pack_strided(op, scale, proc, src, src_stride_arr, dst, dst_stride_arr, count, stride_levels -1, h,fit_level, nb, looplast,nb_handle); if(rc) return rc; } } if(nb_handle && call_count ) nb_handle->bufid=NB_MULTI; return rc; } /*\ decompose strided data into chunks and call func on each chunk \*/ void armci_dispatch_strided(void *ptr, int stride_arr[], int count[], int strides, int fit_level, int nb, int bufsize, void (*fun)(void*,int*,int*,int,void*), void *arg) { int sn; # ifdef PIPE_MEDIUM_BUFSIZE_ int first_call=0; # endif void *ptr_upd; /* determine decomposition of the patch to fit in the buffer */ if(fit_level<0){ # ifdef PIPE_MEDIUM_BUFSIZE_ first_call=1; # endif armci_fit_buffer(count, strides, &fit_level, &nb, bufsize); } if(fit_level == strides){ /* we can fit subpatch into the buffer */ int chunk = count[fit_level]; int stride_upd; # ifdef PIPE_MEDIUM_BUFSIZE_ /* for first call we adjust nb for performance in medium request */ if(first_call && strides==0) if(chunk<2*bufsize && chunk>PIPE_MEDIUM_BUFSIZE) nb = PIPE_MEDIUM_BUFSIZE; # endif if(nb == chunk){ /* take shortcut when whole patch fits in the buffer */ fun(ptr, stride_arr, count, strides, arg); } if(fit_level) stride_upd = stride_arr[fit_level -1]; else stride_upd = 1; for(sn = 0; sn < chunk; sn += nb){ ptr_upd = (char*)ptr + stride_upd* sn; count[fit_level] = ARMCI_MIN(nb, chunk-sn); /*modify count for this level*/ fun(ptr_upd, stride_arr, count, fit_level, arg); } count[fit_level] = chunk; /* restore original count */ }else for(sn = 0; sn < count[strides]; sn++){ ptr_upd = (char*)ptr + stride_arr[strides -1]* sn; armci_dispatch_strided(ptr_upd, stride_arr, count, strides -1, fit_level, nb, bufsize, fun, arg); } } /* how much space is needed to move data + reduced descriptor ? */ int armci_vector_bytes( armci_giov_t darr[], int len) { int i, bytes=0; for(i=0; isrc_ptr_array=NULL; /* go through the sets looking for set to be split */ for(s=0;s((int)BUFSIZE1)){ split =(BUFSIZE1 -bytes-2*sizeof(int))/(darr[s].bytes +sizeof(void*)); if(split == 0) s--; /* no room available - do not split */ break; }else bytes+=csize; if(BUFSIZE1 -bytes < 64) break; /* stop here if almost full */ } if(s==len)s--; /* adjust loop counter should be < number of sets */ *nlen = s+1; if(split){ /* save the value to be overwritten only if "save" is not filled */ if(!save->src_ptr_array)*save= darr[s]; /* split the set: reduce # of elems, "extra" keeps info for rest of set*/ *extra = darr[s]; darr[s].ptr_array_len = split; extra->ptr_array_len -= split; extra->src_ptr_array = &extra->src_ptr_array[split]; extra->dst_ptr_array = &extra->dst_ptr_array[split]; } } static inline void armcip_init_giov_t(armci_giov_t *thing) { thing->src_ptr_array=NULL; thing->dst_ptr_array=NULL; thing->ptr_array_len=0; thing->bytes=0; } int armci_pack_vector(int op, void *scale, armci_giov_t darr[],int len, int proc,armci_ihdl_t nb_handle) { armci_giov_t extra; /* keeps data remainder of set to be processed in chunks */ armci_giov_t save; /* keeps original value of set to be processed in chunks */ armci_giov_t *ndarr; /* points to first array element to be processed now */ int rc=0, nlen, count=0; armcip_init_giov_t(&extra); armcip_init_giov_t(&save); ndarr = darr; save.src_ptr_array=NULL; /* indicates that save slot is empty */ while(len){ armci_split_dscr_array(ndarr, len, &extra, &nlen, &save); # if defined(REMOTE_OP) /* A problem will occur if len is 1 and nlen is 0. This corresponds to a * situation where the size of an individual element is found to exceed * BUFSIZE1. Treat this as a single transfer of contiguous data using * the standard PARMCI_Get/Put/Acc call */ if (len == 1 && nlen == 0) { if(ARMCI_ACC(op))rc=PARMCI_Acc(op, scale, ndarr[0].src_ptr_array[0], ndarr[0].dst_ptr_array[0],ndarr[0].bytes, proc); else if(op == GET)rc=PARMCI_Get(ndarr[0].src_ptr_array[0], ndarr[0].dst_ptr_array[0],ndarr[0].bytes, proc); else if(op == PUT)rc=PARMCI_Put(ndarr[0].src_ptr_array[0], ndarr[0].dst_ptr_array[0],ndarr[0].bytes, proc); else armci_die("Unknown op in armci_pack_vector",op); nlen = 1; } else { rc = armci_rem_vector(op, scale, ndarr,nlen,proc,0,nb_handle); } # else if(ARMCI_ACC(op))rc=armci_acc_vector(op,scale,ndarr,nlen,proc); else rc = armci_copy_vector(op,ndarr,nlen,proc); # endif if(rc) break; /* non-NULL pointer indicates that set was split */ if(extra.src_ptr_array){ if(nb_handle) { nb_handle->bufid = NB_MULTI; /*can be set multiple times here; but not reset here*/ } ndarr[nlen-1]=extra; /* set the pointer to remainder of last set */ nlen--; /* since last set not done in full need to process it again */ }else{ if(save.src_ptr_array){ ndarr[0]=save; save.src_ptr_array=NULL; /* indicates that save slot is empty */ } if(nlen == 0) armci_die("vector packetization problem:buffer too small",BUFSIZE1); } len -=nlen; ndarr +=nlen; count ++; } return rc; } ga-5.9.2/armci/src/common/regions.c000066400000000000000000000364531500715745200171230ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: regions.c,v 1.14.2.3 2007-10-05 16:51:51 manoj Exp $ interface to keep track of memory regions accross the cluster */ /* * armci_region_init - allocates list of regions, initialization * armci_region_register_shm - registers shared memory on the current node * armci_region_register_loc - registers local memory * armci_region_clus_record - stores info on pinned region on a given cluster node * armci_region_clus_found - returns 1/0 if specified memory is registered * armci_region_loc_found - same for local memory * armci_region_loc_both_found - returns 1 if local and remote are found, otherwise 0 * */ /*7/7/06 * REGIONS REQUIRE MEMHDL was for all networks like via, infiniband, etc.. * which had a handle associated with remote/local memory required for * rdma. Coincidentally all these networks also used a server thread. * so server_regions were allocated and enabled when REGIONS_REQUIRE_MEMHDL * was defined. */ #include "armcip.h" #if HAVE_STDLIB_H # include #endif #if HAVE_STDIO_H # include #endif #include "copy.h" /*this should match similar def in vapi.c and openib.c */ # define MAX_REGIONS 8 typedef struct { void *start; void *end; #ifdef REGIONS_REQUIRE_MEMHDL ARMCI_MEMHDL_T memhdl; #endif } armci_region_t; typedef struct { long n; armci_region_t list[MAX_REGIONS]; } armci_reglist_t; static int allow_pin=0; static armci_reglist_t *clus_regions; /* cluster shared/remote memory */ #ifdef REGIONS_REQUIRE_MEMHDL static armci_reglist_t *serv_regions; /* server regions */ #endif static armci_reglist_t loc_regions_arr; /* local memory */ static void *needs_pin_shmptr=NULL, *needs_pin_ptr=NULL; static size_t needs_pin_shmsize=0, needs_pin_size=0; #ifdef REGIONS_REQUIRE_MEMHDL extern int armci_pin_contig_hndl(void *ptr, size_t bytes, ARMCI_MEMHDL_T *memhdl); extern int armci_server_pin_contig(void *ptr, size_t bytes, ARMCI_MEMHDL_T *memhdl); #else extern int armci_pin_contig1(void *ptr, size_t bytes); #endif static void **exch_list=(void**)0; static char exch_loc[MAX_REGIONS]; static char exch_rem[MAX_REGIONS]; void armci_serv_register_req(void *start,long bytes, ARMCI_MEMHDL_T *reg_mem); static int armci_region_record(void *start, void *end, armci_reglist_t *reg) { int cur=reg->n; #ifdef DEBUG_ int i; for(i=0; in; i++) if(reg->start >= start && reg->end < start) armci_die("armci_region_record: already recorded",i); #endif if(reg->n >= MAX_REGIONS) return 0; (reg->list+cur)->start = start; (reg->list+cur)->end = end; reg->n++; return 1; } static void armci_region_register(void *start, long size, armci_reglist_t *reg) { ARMCI_PR_DBG("enter",0); if(reg->n >= MAX_REGIONS) return; if(armci_nclus<=1)return; #ifdef REGIONS_REQUIRE_MEMHDL if(!armci_pin_contig_hndl(start, size, &((reg->list+reg->n)->memhdl))){ printf("%d pin failed %p bytes=%ld\n",armci_me,start,size); fflush(stdout); return; } #else if(!armci_pin_contig1(start, size)){ printf("%d pin failed %p bytes=%ld\n",armci_me,start,size); fflush(stdout); return; } #endif (void) armci_region_record(start,((char*)start)+size,reg); } void armci_region_register_shm(void *start, long size) { if(allow_pin) armci_region_register(start, size, clus_regions+armci_clus_me); else{ needs_pin_shmptr = start; needs_pin_shmsize= size; } } void armci_region_register_loc(void *start, long size) { if(allow_pin)armci_region_register(start, size, &loc_regions_arr); else{ needs_pin_ptr = start; needs_pin_size= size; } #ifdef DEBUG_ if(allow_pin){ printf("\n%d:%d registered local %p bytes=%ld\n",armci_me,allow_pin,start,size); fflush(stdout); } #endif } void armci_region_clus_record(int node, void *start, long size) { if(node > armci_nclus || node <0 ) armci_die("armci_region_remote: bad node ",node); (void)armci_region_record(start,((char*)start)+size,clus_regions+node); } void armci_region_init() { ARMCI_PR_DBG("enter",0); allow_pin =1; clus_regions=(armci_reglist_t*)calloc(armci_nclus,sizeof(armci_reglist_t)); if(!clus_regions)armci_die("armci_region_init: calloc failed",armci_nclus); #ifdef REGIONS_REQUIRE_MEMHDL serv_regions=(armci_reglist_t*)calloc(armci_nclus,sizeof(armci_reglist_t)); if(!serv_regions)armci_die("armci_region_init: calloc failed",armci_nclus); #endif exch_list = (void**)calloc(2*armci_nclus, sizeof(void*)); if(!exch_list) armci_die("armci_region_init: calloc 2 failed",armci_nclus); bzero(exch_loc,sizeof(exch_loc)); bzero(exch_rem,sizeof(exch_rem)); if(needs_pin_ptr) armci_region_register_loc(needs_pin_ptr,needs_pin_size); if(needs_pin_shmptr) armci_region_register_shm(needs_pin_shmptr,needs_pin_shmsize); ARMCI_PR_DBG("exit",0); } void armci_region_destroy() { armci_reglist_t *reg = &loc_regions_arr; int i; #ifdef REGIONS_REQUIRE_MEMHDL ARMCI_MEMHDL_T *loc_memhdl; #endif if(!allow_pin) return; for(i=0; in; i++){ #ifdef REGIONS_REQUIRE_MEMHDL loc_memhdl=&((reg->list+i)->memhdl); armci_network_client_deregister_memory(loc_memhdl); #endif } reg=clus_regions+armci_clus_me; for(i=0; in; i++){ #ifdef REGIONS_REQUIRE_MEMHDL loc_memhdl=&((reg->list+i)->memhdl); armci_network_client_deregister_memory(loc_memhdl); #endif } } void armci_server_region_destroy() { armci_reglist_t *reg; int i; #ifdef REGIONS_REQUIRE_MEMHDL ARMCI_MEMHDL_T *loc_memhdl; ARMCI_PR_DBG("enter",0); reg=serv_regions+armci_clus_me; for(i=0; in; i++){ loc_memhdl=&((reg->list+i)->memhdl); armci_network_server_deregister_memory(loc_memhdl); } ARMCI_PR_DBG("exit",0); #endif } int armci_region_clus_found(int node, void *start, int size) { armci_reglist_t *reg=clus_regions+node; int i,found=-1; if(!allow_pin) return 0; if(node > armci_nclus || node <0 ) armci_die("armci_region_clus_found: bad node ",node); for(i=0; in; i++) if((reg->list+i)->start <= start && (reg->list+i)->end > start){ found=i; break; } return(found); } int armci_region_loc_found(void *start, int size) { armci_reglist_t *reg = &loc_regions_arr; int i,found=-1; ARMCI_PR_DBG("enter",0); if(!allow_pin) return 0; for(i=0; in; i++) if((reg->list+i)->start <= start && (reg->list+i)->end > start){ found=i; break; } #ifdef DEBUG if(found){ printf("%d: found loc %d n=%ld (%p,%p) %p\n",armci_me,found,reg->n, (reg->list)->start,(reg->list)->end, start); fflush(stdout); } #endif ARMCI_PR_DBG("exit",0); return(found); } #ifdef REGIONS_REQUIRE_MEMHDL int armci_region_both_found_hndl(void *loc, void *rem, int size, int node, ARMCI_MEMHDL_T **loc_memhdl,ARMCI_MEMHDL_T **rem_memhdl) { armci_reglist_t *reg = &loc_regions_arr; int i,found=0; if(!allow_pin) return 0; /* first scan for local */ for(i=0; in; i++){ if((reg->list+i)->start <= loc && (reg->list+i)->end > loc){ #if 0 printf("\n%d:loc found %d %p\n",armci_me,i,loc); #endif found=1; break; } #if 0 else { printf("\n%d: loc ptr=%p st=%p end=%p size=%d\n",armci_me,loc, (reg->list+i)->start,(reg->list+i)->end,size); fflush(stdout); } #endif } if(!found){ /* might be local shared */ reg=clus_regions+armci_clus_me; for(i=0; in; i++){ if((reg->list+i)->start <= loc && (reg->list+i)->end > loc){ found=1; break; } #if 0 else { printf("\n%d:clus ptr=%p st=%p end=%p size=%d\n",armci_me,loc, (reg->list+i)->start,(reg->list+i)->end,size); fflush(stdout); } #endif } } if(!found) return 0; else {*loc_memhdl=&((reg->list+i)->memhdl);} /* now check remote shared */ reg=serv_regions+node; for(i=0; in; i++){ if((reg->list+i)->start <= rem && (reg->list+i)->end > rem){ #if 0 printf("\n%d: serv found %d %p %p\n",armci_me,i,rem,(reg->list+i)->start); #endif found=2;break; } #if 0 else { printf("\n%d: serv ptr=%p st=%p end=%p size=%d nd=%d\n",armci_me,rem, (reg->list+i)->start,(reg->list+i)->end,size,node); fflush(stdout); } #endif } if(0){ if(found==2){printf("%d: found both %d %p\n", armci_me,node,(void*)*loc_memhdl); fflush(stdout); } } if(found==2){*rem_memhdl=&((reg->list+i)->memhdl); return 1;} else return 0; } int armci_region_remote_found_hndl(void *rem,int size, int node, ARMCI_MEMHDL_T **remhdl) { armci_reglist_t *reg = serv_regions+node; int i,found=0; for(i=0; in; i++) if((reg->list+i)->start <= rem && (reg->list+i)->end > rem){ found=1;break; } if(found==1){*remhdl=&((reg->list+i)->memhdl); return 1;} else return 0; } int get_armci_region_local_hndl(void *loc, int node,ARMCI_MEMHDL_T **loc_memhdl) { armci_reglist_t *reg = &loc_regions_arr; int i, found = 0; if(!allow_pin) {printf("inside get_armci_region_local_hndl : case allow_pin = 0\n"); return 0; } if(!found){ reg = serv_regions+armci_clus_me; for(i=0; in; i++){ if((reg->list+i)->start <= loc && (reg->list+i)->end >loc){ found =1; break; } #if 0 else { printf("\n%d: serv ptr=%p st=%p end=%p nd=%d nreg=%d\n",armci_me,loc, (reg->list+i)->start,(reg->list+i)->end,node,reg->n); fflush(stdout); } #endif } } if(found == 1){ *loc_memhdl = &((reg->list+i)->memhdl); if(0){ printf("%d(s) : found local %p\n",armci_me,(void*)*loc_memhdl); fflush(stdout); } return 1; } else return 0; } int armci_region_serv_found(int node,void *start,int size) { armci_reglist_t *reg=serv_regions+node; int i,found=-1; if(!allow_pin) return 0; if(node > armci_nclus || node <0 ) armci_die("armci_region_serv_found: bad node ",node); for(i=0; in; i++) if((reg->list+i)->start <= start && (reg->list+i)->end > start){found=i; break;} return(found); } #endif int armci_region_both_found(void *loc, void *rem, int size, int node) { armci_reglist_t *reg = &loc_regions_arr; int i,found=0; if(!allow_pin) return 0; /* first scan for local */ for(i=0; in; i++) if((reg->list+i)->start <= loc && (reg->list+i)->end > loc){found=1; break;} if(!found){ /* might be local shared */ reg=clus_regions+armci_clus_me; for(i=0; in; i++) if((reg->list+i)->start <= loc && (reg->list+i)->end > loc){found=1; break;} } if(!found) return 0; /* now check remote shared */ reg=clus_regions+node; for(i=0; in; i++) if((reg->list+i)->start <= rem && (reg->list+i)->end > rem){found=2;break;} #if 0 if(found==2){printf("%d: found both %d\n",armci_me,node); fflush(stdout); } #endif if(found==2) return 1; else return 0; } void armci_region_exchange(void *start, long size) { int found=0, i; armci_region_t *reg=0; #ifdef REGIONS_REQUIRE_MEMHDL ARMCI_MEMHDL_T *hdlarr; hdlarr = calloc(armci_nclus,sizeof(ARMCI_MEMHDL_T)); #endif if(!allow_pin)return; if(armci_nclus<=1)return; found=armci_region_clus_found(armci_clus_me, start,size); if(found>-1){ if(!exch_rem[found]){ reg = (clus_regions+armci_clus_me)->list+found; exch_rem[found]=1; } }else{ found= armci_region_loc_found(start,size); if(found>-1){ if(!exch_loc[found]){ reg = (&loc_regions_arr)->list+found; exch_loc[found]=1; } } } bzero(exch_list,2*armci_nclus*sizeof(void *)); if( reg && (armci_me == armci_master)){ exch_list[2*armci_clus_me] = reg->start; exch_list[2*armci_clus_me+1] = reg->end; #ifdef REGIONS_REQUIRE_MEMHDL armci_copy(®->memhdl,&hdlarr[armci_clus_me],sizeof(ARMCI_MEMHDL_T)); #endif } /* exchange info on new regions with other nodes */ armci_exchange_address(exch_list,2*armci_nclus); #ifdef REGIONS_REQUIRE_MEMHDL i = armci_nclus*sizeof(ARMCI_MEMHDL_T)/sizeof(int); armci_msg_gop_scope(SCOPE_ALL,hdlarr,i,"+",ARMCI_INT); #endif for(i=0; in); fflush(stdout); #endif #ifdef REGIONS_REQUIRE_MEMHDL armci_copy(&hdlarr[i],&(r->list+r->n)->memhdl,sizeof(ARMCI_MEMHDL_T)); #endif armci_region_record(exch_list[2*i],exch_list[2*i+1], r); } } } /*\ * for server thread to know which region is what in cases where there is one * process per node \*/ void armci_global_region_exchange(void *start, long size) { #ifdef REGIONS_REQUIRE_MEMHDL ARMCI_MEMHDL_T *hdlarr; hdlarr = calloc(armci_nclus,sizeof(ARMCI_MEMHDL_T)); #endif if(!allow_pin)return; if(armci_nclus<=1)return; armci_region_exchange(start,size); #ifdef REGIONS_REQUIRE_MEMHDL { int foundclus=0, foundserv=0, i,loc=0; armci_reglist_t *reglist=NULL,*clreglist=NULL; armci_region_t *reg=NULL; foundclus=armci_region_clus_found(armci_clus_me, start,size); foundserv=armci_region_serv_found(armci_clus_me, start,size); if(foundclus==-1){ foundclus = armci_region_loc_found(start,size); loc=1; } if(foundclus!=-1 && foundserv==-1){ reglist = (serv_regions+armci_clus_me); if(loc) clreglist = &(loc_regions_arr); else clreglist = (clus_regions+armci_clus_me); #if defined(DATA_SERVER) armci_serv_register_req((clreglist->list+foundclus)->start,((char *)(clreglist->list+foundclus)->end-(char *)((clreglist->list+foundclus)->start)),&((reglist->list+reglist->n)->memhdl)); #endif (void)armci_region_record((clreglist->list+foundclus)->start,(clreglist->list+foundclus)->end,reglist); #if DEBUG printf("\n%d:serv recording st=%p end=%p sz=%d from %d n=%d sz=%d\n",armci_me,(clreglist->list+foundclus)->start,(clreglist->list+foundclus)->end,(clreglist->list+foundclus)->end-(clreglist->list+foundclus)->start,armci_clus_me,reglist->n,sizeof(ARMCI_MEMHDL_T));fflush(stdout); #endif foundserv=armci_region_serv_found(armci_clus_me, start,size); reg = (serv_regions+armci_clus_me)->list+foundserv; } if(reg) armci_copy(®->memhdl,&hdlarr[armci_clus_me],sizeof(ARMCI_MEMHDL_T)); i = armci_nclus*sizeof(ARMCI_MEMHDL_T)/sizeof(int); armci_msg_gop_scope(SCOPE_ALL,hdlarr,i,"+",ARMCI_INT); for(i=0; ilist+r->n)->start){ #if 0 printf("\n%d:serv recording %p from %d n=%d \n",armci_me,(rc->list+r->n)->start,i,r->n);fflush(stdout); #endif armci_copy(&hdlarr[i],&(r->list+r->n)->memhdl,sizeof(ARMCI_MEMHDL_T)); armci_region_record((rc->list+r->n)->start,(rc->list+r->n)->end,r); } } } #endif } ga-5.9.2/armci/src/common/request.c000066400000000000000000001544251500715745200171450ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: request.c,v 1.74.2.11 2007-10-18 06:09:37 d3h325 Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_SIGNAL_H # include #endif #include "armcip.h" #include "request.h" #include "memlock.h" #include "armci_shmem.h" #include "copy.h" #include "gpc.h" #ifdef SOCKETS extern void armci_sock_send(int to, void *data, int len); #endif #define DEBUG_ 0 #if 0 # define MARK_ENTER(func_) { fprintf(stdout, "ENTERING %s\n", func_); fflush(stdout); } # define MARK_EXIT(func_) { fprintf(stdout, "EXITING %s\n", func_); fflush(stdout); } #else # define MARK_ENTER(func_) # define MARK_EXIT(func_) #endif #if 0 # define PRNDBG3(m,a1,a2,a3) \ fprintf(stderr,"DBG %d: " m,armci_me,a1,a2,a3);fflush(stderr) # define PRNDBG(m) PRNDBG3(m,0,0,0) # define PRNDBG1(m,a1) PRNDBG3(m,a1,0,0) # define PRNDBG2(m,a1,a2) PRNDBG3(m,a1,a2,0) #else # define PRNDBG(m) # define PRNDBG1(m,a1) # define PRNDBG2(m,a1,a2) # define PRNDBG3(m,a1,a2,a3) #endif #if !defined(VAPI) double _armci_rcv_buf[MSG_BUFLEN_DBL]; double _armci_snd_buf[MSG_BUFLEN_DBL]; char* MessageSndBuffer = (char*)_armci_snd_buf; char* MessageRcvBuffer = (char*)_armci_rcv_buf; #endif #define MAX_EHLEN 248 #define ADDBUF(buf,type,val) *(type*)(buf) = (val); (buf) += sizeof(type) #define GETBUF(buf,type,var) (var) = *(type*)(buf); (buf) += sizeof(type) #define ALLIGN8(buf){size_t _adr=(size_t)(buf); \ _adr>>=3; _adr<<=3; _adr+=8; (buf) = (char*)_adr; } #ifndef CLN # define CLN 1 #endif #ifndef SERV # define SERV 2 #endif /*******************Routines to handle completion descriptor******************/ /*\ *Following the the routines to fill a completion descriptor, if necessary *copy the data to destination based on completion descriptor *NOTE, THE FOLLOWING ROUTINES ARE FOR CLIENTS ONLY \*/ /*\Routine to complete a vector request, data is in buf and descriptor in dscr \*/ extern int armci_direct_vector_get(request_header_t *msginfo , armci_giov_t darr[], int len, int proc); static void armci_complete_vector_get(armci_giov_t darr[],int len,void *buf) { int proc; request_header_t *msginfo = (request_header_t*) buf; proc = msginfo->to; #if defined(USE_SOCKET_VECTOR_API) armci_direct_vector_get(msginfo, darr, len, proc); #else armci_rcv_vector_data(proc, msginfo, darr, len); #endif FREE_SEND_BUFFER(buf); } /*\ Routine called from buffers.c to complete a request for which the buffer was * used for, so that the buffer can be reused. \*/ void armci_complete_req_buf(BUF_INFO_T *info, void *buffer) { request_header_t *msginfo = (request_header_t*) buffer; if(info->protocol==0)return; else if(info->protocol==SDSCR_IN_PLACE){ char *dscr = info->dscr; void *loc_ptr; int stride_levels; int *loc_stride_arr,*count; loc_ptr = *(void**)dscr; dscr += sizeof(void*); stride_levels = *(int*)dscr; dscr += sizeof(int); loc_stride_arr = (int*)dscr; dscr += stride_levels*sizeof(int); count = (int*)dscr; if(0 || DEBUG_){ if(armci_me==0){ printf("\n%d:extracted loc_ptr=%p, stridelevels=%d\n",armci_me, loc_ptr,stride_levels); fflush(stdout); } } #if defined(ALLOW_PIN) if(msginfo->pinned && msginfo->bypass){ armci_rcv_strided_data_bypass_both(msginfo->to,msginfo,loc_ptr,count, stride_levels); } else #endif armci_rcv_strided_data(msginfo->to, msginfo, msginfo->datalen, loc_ptr, stride_levels,loc_stride_arr,count); FREE_SEND_BUFFER(msginfo); } else if(info->protocol==VDSCR_IN_PLACE || info->protocol==VDSCR_IN_PTR){ char *dscr; int len,i; if(info->protocol==VDSCR_IN_PLACE)dscr = info->dscr; else dscr = info->ptr.dscrbuf; GETBUF(dscr, long ,len); { armci_giov_t *darr; darr = (armci_giov_t *)malloc(sizeof(armci_giov_t)*len); if(!darr)armci_die("malloc in complete_req_buf failed",len); for(i = 0; i< len; i++){ int parlen, bytes; GETBUF(dscr, int, parlen); GETBUF(dscr, int, bytes); darr[i].ptr_array_len = parlen; darr[i].bytes = bytes; if(msginfo->operation==GET)darr[i].dst_ptr_array=(void **)dscr; else darr[i].src_ptr_array=(void **)dscr; dscr+=sizeof(void *)*parlen; } if (msginfo->operation==GET) armci_complete_vector_get(darr,len,buffer); } } else armci_die("armci_complete_req_buf,protocol val invalid",info->protocol); } /*\ save a part of strided descriptor needed to complete request \*/ void armci_save_strided_dscr(char **bptr, void *rem_ptr,int rem_stride_arr[], int count[], int stride_levels,int is_nb) { int i; char *bufptr=*bptr; BUF_INFO_T *info=NULL; MARK_ENTER("armci_save_strided_dscr"); if(is_nb){ info=BUF_TO_BUFINFO(*bptr); bufptr = (info->dscr); } *(void**)bufptr = rem_ptr; bufptr += sizeof(void*); *(int*)bufptr = stride_levels; bufptr += sizeof(int); for(i=0;idscr); if(armci_me==0) printf("\n%d:rem_ptr %p=%p stride_levels %d=%d\n",armci_me, *(void**)bufptr,rem_ptr, *(int*)(bufptr + sizeof(void*)),stride_levels); } /*remote_strided expects the pointer to point to the end of descr hence..*/ if(is_nb) info->protocol=SDSCR_IN_PLACE; else *bptr=bufptr; MARK_EXIT("armci_save_strided_dscr"); } /*\ save a part of vector descriptor needed to complete request \*/ void armci_save_vector_dscr(char **bptr,armci_giov_t darr[],int len, int op,int is_nb) { int i,size=sizeof(int); BUF_INFO_T *info; char *buf,*bufptr=*bptr; void *rem_ptr; if(is_nb){ for(i=0;idscr; info->protocol=VDSCR_IN_PLACE; } else { info->ptr.dscrbuf = (void *)malloc(size); buf = (char *)info->ptr.dscrbuf; info->protocol=VDSCR_IN_PTR; } } else buf=bufptr; ADDBUF(buf,long,len); /* number of sets */ for(i=0;ibufid to val, else set it to the id of the buf \*/ void armci_set_nbhandle_bufid(armci_ihdl_t nb_handle,char *buf,int val) { BUF_INFO_T *info; if(buf){ info = BUF_TO_BUFINFO(buf); val = info->bufid; } nb_handle->bufid = val; } /**************End--Routines to handle completion descriptor******************/ /*\ send request to server to LOCK MUTEX \*/ void armci_rem_lock(int mutex, int proc, int *ticket) { request_header_t *msginfo; int *ibuf; int bufsize = sizeof(request_header_t)+sizeof(int); msginfo = (request_header_t*)GET_SEND_BUFFER(bufsize,LOCK,proc); bzero(msginfo,sizeof(request_header_t)); msginfo->datalen = sizeof(int); msginfo->dscrlen = 0; msginfo->from = armci_me; msginfo->to = proc; msginfo->operation = LOCK; msginfo->format = mutex; msginfo->bytes = msginfo->datalen + msginfo->dscrlen; #ifdef SOCKETS msginfo->tag = BUF_TO_BUFINFO(msginfo)->bufid; #endif ibuf = (int*)(msginfo+1); *ibuf = mutex; armci_send_req(proc, msginfo, bufsize); /* receive ticket from server */ #ifdef SOCKETS armci_rcv_hdlr(msginfo); *ticket = *(int*)(msginfo + 1); #else *ticket = *(int*)armci_rcv_data(proc,msginfo); #endif FREE_SEND_BUFFER(msginfo); if(DEBUG_)fprintf(stderr,"%d receiving ticket %d\n",armci_me, *ticket); } void armci_server_lock(request_header_t *msginfo) { int *ibuf = (int*)(msginfo+1); int proc = msginfo->from; int mutex; int ticket; mutex = *(int*)ibuf; /* acquire lock on behalf of requesting process */ ticket = armci_server_lock_mutex(mutex, proc, msginfo->tag); if(ticket >-1){ /* got lock */ msginfo->datalen = sizeof(int); #ifdef SOCKETS armci_sock_send(msginfo->from, &(msginfo->tag), sizeof(msg_tag_t)); #endif armci_send_data(msginfo, &ticket); } } /*\ send request to server to UNLOCK MUTEX \*/ void armci_rem_unlock(int mutex, int proc, int ticket) { request_header_t *msginfo; int *ibuf; int bufsize = sizeof(request_header_t)+sizeof(ticket); msginfo = (request_header_t*)GET_SEND_BUFFER(bufsize,UNLOCK,proc); bzero(msginfo,sizeof(request_header_t)); msginfo->dscrlen = msginfo->bytes = sizeof(ticket); msginfo->datalen = 0; msginfo->from = armci_me; msginfo->to = proc; msginfo->operation = UNLOCK; msginfo->format = mutex; #ifdef SOCKETS msginfo->tag = BUF_TO_BUFINFO(msginfo)->bufid; #endif ibuf = (int*)(msginfo+1); *ibuf = ticket; if(DEBUG_)fprintf(stderr,"%d sending unlock\n",armci_me); armci_send_req(proc, msginfo, bufsize); } /*\ server unlocks mutex and passes lock to the next waiting process \*/ void armci_server_unlock(request_header_t *msginfo, char* dscr) { int ticket = *(int*)dscr; int mutex = msginfo->format; int proc = msginfo->to; int waiting; waiting = armci_server_unlock_mutex(mutex,proc,ticket,&msginfo->tag); if(waiting >-1){ /* -1 means that nobody is waiting */ ticket++; /* pass ticket to the waiting process */ msginfo->from = waiting; msginfo->datalen = sizeof(ticket); #ifdef SOCKETS armci_sock_send(msginfo->from, &(msginfo->tag), sizeof(msg_tag_t)); #endif armci_send_data(msginfo, &ticket); } } void armci_unlock_waiting_process(msg_tag_t tag, int proc, int ticket) { request_header_t header; request_header_t *msginfo = &header; msginfo->datalen = sizeof(int); msginfo->tag = tag; msginfo->from = proc; msginfo->to = armci_me; armci_send_data(msginfo, &ticket); } #ifdef REGIONS_REQUIRE_MEMHDL void armci_serv_register_req(void *ptr,long sz,ARMCI_MEMHDL_T *memhdl) { char *buf; int bufsize = sizeof(request_header_t)+sizeof(long)+sizeof(void *)+sizeof(ARMCI_MEMHDL_T); request_header_t *msginfo = (request_header_t*)GET_SEND_BUFFER(bufsize,REGISTER,armci_me); bzero(msginfo,sizeof(request_header_t)); msginfo->from = armci_me; msginfo->to = SERVER_NODE(armci_clus_me); msginfo->dscrlen = sizeof(long)+sizeof(void *); msginfo->datalen = sizeof(ARMCI_MEMHDL_T); msginfo->operation = REGISTER; msginfo->bytes = msginfo->dscrlen+ msginfo->datalen; msginfo->tag.ack = 0; buf = (char *)(msginfo+1); ADDBUF(buf,void*,ptr); ADDBUF(buf,long,sz); armci_send_req(armci_master, msginfo, bufsize); buf= armci_rcv_data(armci_master, msginfo); /* receive response */ armci_copy(buf,memhdl,sizeof(ARMCI_MEMHDL_T)); FREE_SEND_BUFFER(msginfo); if(DEBUG_){ printf("%d:client register req sent ptr=%p %d bytes\n",armci_me, buf,bufsize);fflush(stdout); } } #endif /*\ control message to the server, e.g.: ATTACH to shmem, return ptr etc. \*/ void armci_serv_attach_req(void *info, int ilen, long size, void* resp,int rlen) { char *buf; int bufsize = sizeof(request_header_t)+ilen + sizeof(long)+sizeof(rlen); request_header_t *msginfo = (request_header_t*)GET_SEND_BUFFER(bufsize,ATTACH,armci_me); bzero(msginfo,sizeof(request_header_t)); msginfo->from = armci_me; msginfo->to = SERVER_NODE(armci_clus_me); msginfo->dscrlen = ilen; msginfo->datalen = sizeof(long)+sizeof(rlen); msginfo->operation = ATTACH; msginfo->bytes = msginfo->dscrlen+ msginfo->datalen; armci_copy(info, msginfo +1, ilen); buf = ((char*)msginfo) + ilen + sizeof(request_header_t); *((long*)buf) =size; *(int*)(buf+ sizeof(long)) =rlen; armci_send_req(armci_master, msginfo, bufsize); if(rlen){ buf= armci_rcv_data(armci_master, msginfo); /* receive response */ armci_copy(buf, resp, rlen); FREE_SEND_BUFFER(msginfo); if(DEBUG_){printf("%d:client attaching got ptr=%p %d bytes\n",armci_me,buf,rlen); fflush(stdout); } } } /*\ server initializes its copy of the memory lock data structures \*/ static void server_alloc_memlock(void *ptr_myclus) { int i; /* for protection, set pointers for processes outside local node NULL */ memlock_table_array = calloc(armci_nproc,sizeof(void*)); if(!memlock_table_array) armci_die("malloc failed for ARMCI lock array",0); /* set pointers for processes on local cluster node * ptr_myclus - corresponds to the master process */ for(i=0; i< armci_clus_info[armci_clus_me].nslave; i++){ memlock_table_array[armci_master +i] = ((char*)ptr_myclus) + MAX_SLOTS*sizeof(memlock_t)*i; } /* set pointer to the use flag */ #ifdef MEMLOCK_SHMEM_FLAG armci_use_memlock_table = (int*) (MAX_SLOTS*sizeof(memlock_t) + (char*) memlock_table_array[armci_clus_last]); if(DEBUG_) fprintf(stderr,"server initialized memlock %p\n", (void*)armci_use_memlock_table); #endif } static int allocate_memlock=1; /*\ server actions triggered by client request to ATTACH \*/ void armci_server_ipc(request_header_t* msginfo, void* descr, void* buffer, int buflen) { double *ptr; long *idlist = (long*)descr; long size = *(long*)buffer; int rlen = *(int*)(sizeof(long)+(char*)buffer); if(size<0) armci_die("armci_server_ipc: size<0",(int)size); ptr=(double*)Attach_Shared_Region(idlist+1,size,idlist[0]); if(!ptr)armci_die("armci_server_ipc: failed to attach",0); /* provide data server with access to the memory lock data structures */ if(allocate_memlock){ allocate_memlock = 0; server_alloc_memlock(ptr); } if(size>0)armci_set_mem_offset(ptr); if(msginfo->datalen != sizeof(long)+sizeof(int)) armci_die("armci_server_ipc: bad msginfo->datalen ",msginfo->datalen); if(rlen==sizeof(ptr)){ #if defined(PEND_BUFS) /*memcpy(buffer, &ptr, sizeof(&ptr));*/ memcpy(buffer, &ptr, sizeof(void*)); armci_send_data(msginfo, buffer); #else armci_send_data(msginfo, &ptr); #endif }else armci_die("armci_server_ipc: bad rlen",rlen); } /*\ send RMW request to server \*/ void armci_rem_rmw(int op, int *ploc, int *prem, int extra, int proc) { request_header_t *msginfo; char *buf; void *buffer; int bufsize = sizeof(request_header_t)+sizeof(long)+sizeof(void*); msginfo = (request_header_t*)GET_SEND_BUFFER(bufsize,op,proc); bzero(msginfo,sizeof(request_header_t)); msginfo->dscrlen = sizeof(void*); msginfo->from = armci_me; msginfo->to = proc; msginfo->operation = op; msginfo->datalen = sizeof(long); #ifdef SOCKETS msginfo->tag = BUF_TO_BUFINFO(msginfo)->bufid; #endif buf = (char*)(msginfo+1); ADDBUF(buf, void*, prem); /* pointer is shipped as descriptor */ /* data field: extra argument in fetch&add and local value in swap */ if(op==ARMCI_SWAP){ ADDBUF(buf, int, *ploc); }else if(op==ARMCI_SWAP_LONG) { ADDBUF(buf, long, *((long*)ploc) ); msginfo->datalen = sizeof(long); }else { ADDBUF(buf, int, extra); } msginfo->bytes = msginfo->datalen+msginfo->dscrlen ; if(DEBUG_){ printf("%d sending RMW request %d to %d\n",armci_me,op,proc); fflush(stdout); } armci_send_req(proc, msginfo, bufsize); #ifdef SOCKETS armci_rcv_hdlr(msginfo); buffer = msginfo + 1; #else buffer = armci_rcv_data(proc,msginfo); /* receive response */ #endif if(op==ARMCI_FETCH_AND_ADD || op== ARMCI_SWAP) *ploc = *(int*)buffer; else *(long*)ploc = *(long*)buffer; FREE_SEND_BUFFER(msginfo); } /*\ server response to RMW \*/ void armci_server_rmw(request_header_t* msginfo,void* ptr, void* pextra) { long lold; int iold; void *pold=0; int op = msginfo->operation; if(DEBUG_){ printf("%d server: executing RMW from %d. op=%d pextra=%p\n",armci_me,msginfo->from, op, pextra); fflush(stdout); } if(msginfo->datalen != sizeof(long)) armci_die2("armci_server_rmw: bad datalen=",msginfo->datalen,op); /* for swap operations *pextra has the value to swap * for fetc&add it carries the increment argument */ switch(op){ case ARMCI_SWAP: iold = *(int*) pextra; case ARMCI_FETCH_AND_ADD: pold = &iold; break; case ARMCI_SWAP_LONG: lold = *(long*) pextra; case ARMCI_FETCH_AND_ADD_LONG: pold = &lold; break; default: armci_die("armci_server_rmw: bad operation code=",op); } armci_generic_rmw(op, pold, *(int**)ptr, *(int*) pextra, msginfo->to); #ifdef SOCKETS armci_sock_send(msginfo->from, &(msginfo->tag), sizeof(msg_tag_t)); #endif #if defined(PEND_BUFS) memcpy(pextra,pold,msginfo->datalen); armci_send_data(msginfo, pextra); /*Send from server-anointed buffers*/ #else armci_send_data(msginfo, pold); #endif } extern int armci_direct_vector_snd(request_header_t *msginfo , armci_giov_t darr[], int len, int proc); extern int armci_direct_vector(request_header_t *msginfo , armci_giov_t darr[], int len, int proc); int armci_rem_vector(int op, void *scale, armci_giov_t darr[],int len,int proc,int flag, armci_ihdl_t nb_handle) { char *buf,*buf0; request_header_t *msginfo; int bytes =0, s, slen=0; size_t adr; int bufsize = sizeof(request_header_t);/*,isnonblocking=0;*/ /*if(nb_handle)isnonblocking=1;*/ /* compute size of the buffer needed */ for(s=0; stag = BUF_TO_BUFINFO(buf)->bufid; #endif /* printf("%d:: rem_vector. len=%d. ptr_len[len-1]=%d bytes[len-1]=%d bufsize=%d\n", */ /* armci_me, len, darr[len-1].ptr_array_len, darr[len-1].bytes,bufsize); */ /* fflush(stdout); */ if(nb_handle){ INIT_SENDBUF_INFO(nb_handle,buf,op,proc); _armci_buf_set_tag(buf,nb_handle->tag,0); if(nb_handle->bufid == NB_NONE) armci_set_nbhandle_bufid(nb_handle,buf,0); } buf += sizeof(request_header_t); /* fill vector descriptor */ armci_save_vector_dscr(&buf,darr,len,op,0); /* align buf for doubles (8-bytes) before copying data */ adr = (size_t)buf; adr >>=3; adr <<=3; adr +=8; buf = (char*)adr; msginfo->ehlen = 0; /* fill message header */ msginfo->dscrlen = buf - buf0 - sizeof(request_header_t); msginfo->from = armci_me; msginfo->to = proc; msginfo->operation = op; msginfo->format = VECTOR; msginfo->datalen = bytes; /* put scale for accumulate */ switch(op){ case ARMCI_ACC_INT: *(int*)buf = *(int*)scale; slen= sizeof(int); break; case ARMCI_ACC_DCP: ((double*)buf)[0] = ((double*)scale)[0]; ((double*)buf)[1] = ((double*)scale)[1]; slen=2*sizeof(double);break; case ARMCI_ACC_DBL: *(double*)buf = *(double*)scale; slen = sizeof(double); break; case ARMCI_ACC_CPL: ((float*)buf)[0] = ((float*)scale)[0]; ((float*)buf)[1] = ((float*)scale)[1]; slen=2*sizeof(float);break; case ARMCI_ACC_FLT: *(float*)buf = *(float*)scale; slen = sizeof(float); break; default: slen=0; } buf += slen; msginfo->datalen += slen; msginfo->bytes = msginfo->datalen+msginfo->dscrlen; #ifdef USE_SOCKET_VECTOR_API if (flag && (op == GET || op == PUT)) { /*armci_direct_vector(msginfo,darr,len,proc);*/ armci_direct_vector_snd(msginfo,darr,len,proc); if (op == GET) { armci_save_vector_dscr(&buf0,darr,len,op,1); /*armci_complete_vector_get(darr,len,msginfo);*/ armci_rcv_hdlr(msginfo); } _armci_buf_set_cmpld(msginfo, 1); /* this may not be the best place */ return 0; } #endif /* for put and accumulate copy data into buffer */ if(op != GET){ /* fprintf(stderr,"sending %f\n",*(double*)darr[0].src_ptr_array[0]);*/ armci_vector_to_buf(darr, len, buf); } /* #ifdef VAPI */ /* else{ */ /* if(msginfo->dscrlen < (bytes - sizeof(int))) */ /* *(int*)(((char*)(msginfo+1))+(bytes-sizeof(int))) = ARMCI_STAMP; */ /* else */ /* *(int*)(((char*)(msginfo+1))+(msginfo->dscrlen+bytes-sizeof(int))) = ARMCI_STAMP; */ /* } */ /* #endif */ armci_send_req(proc, msginfo, bufsize); #ifdef KOT if (op == GET #ifndef SOCKETS && nb_handle #endif ) { armci_save_vector_dscr(&buf0,darr,len,op,1); #ifdef SOCKETS armci_rcv_hdlr(msginfo); #else armci_complete_vector_get(darr,len,msginfo); #endif } #ifdef SOCKETS _armci_buf_set_cmpld(msginfo, 1); /* this may not be the best place */ #endif #else if(nb_handle && op==GET)armci_save_vector_dscr(&buf0,darr,len,op,1); if(op == GET # if !defined(SOCKETS) && !defined(MPI_SPAWN) && !defined(MPI_MT) && !nb_handle # endif ){ #ifdef SOCKETS armci_rcv_hdlr(msginfo); #else armci_complete_vector_get(darr,len,msginfo); #endif } #if defined(SOCKETS) && !defined(NB_SOCKETS) _armci_buf_set_cmpld(msginfo, 1); /* this may not be the best place */ #endif #endif return 0; } #define CHUN_ (8*8096) #define CHUN 200000 /*\ client version of remote strided operation \*/ int armci_rem_strided(int op, void* scale, int proc, void *src_ptr, int src_stride_arr[], void* dst_ptr, int dst_stride_arr[], int count[], int stride_levels, ext_header_t *h, int flag,armci_ihdl_t nb_handle) { char *buf, *buf0; request_header_t *msginfo; int i, slen=0, bytes; void *rem_ptr; int *rem_stride_arr; int bufsize = sizeof(request_header_t); int ehlen =0; /* we send ext header only for last chunk */ #if 0 if(h) ehlen = h->len; #else if(h) if(h->last) ehlen = h->len; #endif if(ehlen>MAX_EHLEN || ehlen <0) armci_die2("armci_rem_strided ehlen out of range",MAX_EHLEN,ehlen); /* calculate size of the buffer needed */ for(i=0, bytes=1;i<=stride_levels;i++)bytes*=count[i]; bufsize += bytes+sizeof(void*)+2*sizeof(int)*(stride_levels+1) +ehlen +2*sizeof(double) + 16; /* +scale+alignment */ if (flag){ # ifdef CLIENT_BUF_BYPASS if(_armci_bypass) bufsize -=bytes; /* we are not sending data*/ # elif defined(USE_SOCKET_VECTOR_API) bufsize -=bytes; /* we are not sending data*/ bufsize += sizeof(struct iovec)*MAX_IOVEC; # else if(op==GET)bufsize -=bytes; # endif } buf = buf0= GET_SEND_BUFFER((bufsize),op,proc); msginfo = (request_header_t*)buf; bzero(msginfo,sizeof(request_header_t)); #ifdef SOCKETS #if 0 || defined(DTAG) msginfo->tag = nb_handle ? nb_handle->tag : GET_NEXT_NBTAG(); _armci_buf_set_tag(buf,msginfo->tag,0); printf("DTAG: tag=%ld,DTAG=%d,",msginfo->tag,DTAG); msginfo->tag &= DTAG; msginfo->tag <<= sizeof(msg_id_t) * 8; msginfo->tag |= BUF_TO_BUFINFO(buf)->bufid; printf("bufid=%hd,dtag=%ld\n",BUF_TO_BUFINFO(buf)->bufid,msginfo->tag); fflush(stdout); #else msginfo->tag = BUF_TO_BUFINFO(buf)->bufid; #endif #endif if(nb_handle) { /* INIT_SENDBUF_INFO(nb_handle,buf,op,proc); same as _armci_buf_set_tag, why here? */ _armci_buf_set_tag(buf,nb_handle->tag,0); if(nb_handle->bufid == NB_NONE) armci_set_nbhandle_bufid(nb_handle,buf,0); } if(op == GET){ rem_ptr = src_ptr; rem_stride_arr = src_stride_arr; }else{ rem_ptr = dst_ptr; rem_stride_arr = dst_stride_arr; } msginfo->datalen=bytes; #if defined(USE_SOCKET_VECTOR_API) || defined(MPI_SPAWN_ZEROCOPY) /*****for making put use readv/writev on sockets*****/ if(op==PUT && flag) msginfo->datalen=0; #endif /* fill strided descriptor */ buf += sizeof(request_header_t); /*this function fills the dscr into buf and also moves the buf ptr to the end of the dscr*/ armci_save_strided_dscr(&buf,rem_ptr,rem_stride_arr,count,stride_levels,0); # ifdef CLIENT_BUF_BYPASS if(flag && _armci_bypass){ /* to bypass the client MessageSnd buffer in get we need to add source pointer and stride info - server will put data directly there */ ADDBUF(buf,void*,dst_ptr); for(i=0;ibypass=1; msginfo->pinned=0; /* if set then pin is done before sending req*/ }else{ msginfo->bypass=0; msginfo->pinned=0; } # endif /* align buf for doubles (8-bytes) before copying data */ ALLIGN8(buf); /* fill message header */ msginfo->from = armci_me; msginfo->to = proc; msginfo->format = STRIDED; msginfo->operation = op; /* put scale for accumulate */ switch(op){ case ARMCI_ACC_INT: *(int*)buf = *(int*)scale; slen= sizeof(int); break; case ARMCI_ACC_DCP: ((double*)buf)[0] = ((double*)scale)[0]; ((double*)buf)[1] = ((double*)scale)[1]; slen=2*sizeof(double);break; case ARMCI_ACC_DBL: *(double*)buf = *(double*)scale; slen = sizeof(double); break; case ARMCI_ACC_CPL: ((float*)buf)[0] = ((float*)scale)[0]; ((float*)buf)[1] = ((float*)scale)[1]; slen=2*sizeof(float);break; case ARMCI_ACC_FLT: *(float*)buf = *(float*)scale; slen = sizeof(float); break; case ARMCI_ACC_LNG: *(long*)buf = *(long*)scale; slen = sizeof(long); break; default: slen=0; } /* if(ARMCI_ACC(op))printf("%d client len=%d alpha=%f data=%f,%f\n", armci_me, buf-(char*)msginfo,((double*)buf)[0],*((double*)src_ptr), ((double*)buf)[1]); */ buf += slen; /**** add extended header *******/ if(ehlen){ bcopy(h->exthdr,buf,ehlen); i = ehlen%8; ehlen += (8-i); /* make sure buffer is still alligned */ buf += ehlen; } msginfo->ehlen = ehlen; msginfo->dscrlen = buf - buf0 - sizeof(request_header_t); msginfo->bytes = msginfo->datalen+msginfo->dscrlen; if(op == GET){ /* #ifdef VAPI */ /* if(msginfo->dscrlen < (bytes - sizeof(int))) */ /* *(int*)(((char*)(msginfo+1))+(bytes-sizeof(int)))=ARMCI_STAMP; */ /* else */ /* *(int*)(((char*)(msginfo+1))+(msginfo->dscrlen+bytes-sizeof(int))) = */ /* ARMCI_STAMP; */ /* #endif */ # if defined(CLIENT_BUF_BYPASS) if(msginfo->bypass){ #ifdef MULTISTEP_PIN if(stride_levels==0 && !msginfo->pinned && count[0]>=400000){ int seq=1; armci_send_req(proc,msginfo,bufsize); for(i=0; i< bytes; i+=CHUN){ int len= ARMCI_MIN(CHUN,(bytes-i)); char *p = i +(char*)dst_ptr; armci_pin_contig(p, len); armci_client_send_ack(proc, seq); seq++; } }else #endif { int armci_pin_memory(void *,int *,int *,int); void armci_client_send_ack(int, int); if(!msginfo->pinned) armci_send_req(proc,msginfo,bufsize); if(!armci_pin_memory(dst_ptr,dst_stride_arr,count, stride_levels)){ armci_client_send_ack(proc, -1); armci_rcv_strided_data_bypass(proc,msginfo,dst_ptr,stride_levels); FREE_SEND_BUFFER(msginfo); return 1; /* failed:cannot do bypass */ } if(msginfo->pinned) armci_send_req(proc,msginfo,bufsize); else armci_client_send_ack(proc, 1); /*if(nb_handle) armci_save_strided_dscr(&buf,dst_ptr,dst_stride_arr,count, stride_levels,1);*/ } }else # endif { armci_send_req(proc, msginfo, bufsize); } #if !defined(MPI_SPAWN) && !defined(MPI_MT) armci_save_strided_dscr(&buf0,dst_ptr,dst_stride_arr,count, stride_levels,1); #endif #if defined(SOCKETS) # ifdef NB_SOCKETS if(!nb_handle) # endif { armci_rcv_hdlr(msginfo); FREE_SEND_BUFFER(msginfo); } #else # if !defined(MPI_SPAWN) && !defined(MPI_MT) if(!nb_handle) # endif { armci_rcv_strided_data(proc, msginfo, msginfo->datalen, dst_ptr, stride_levels, dst_stride_arr,count); FREE_SEND_BUFFER(msginfo); } #endif } else { /* for put and accumulate send data */ armci_send_strided(proc,msginfo, buf, src_ptr, stride_levels, src_stride_arr, count); #ifdef SOCKETS _armci_buf_set_cmpld(msginfo, 1); /* this may not be the best place */ #endif } return 0; } #if defined(ALLOW_PIN) && defined(VAPI) /*\ * two phase send \*/ int armci_two_phase_send(int proc,void *src_ptr,int src_stride_arr[], void *dst_ptr,int dst_stride_arr[],int count[], int stride_levels,void ** context_ptr,armci_ihdl_t nbhandle, ARMCI_MEMHDL_T *mhloc) { char *buf, *buf0; request_header_t *msginfo; int bytes, i; int ehlen = 0,nbtag=0; int *rem_ptr; int * rem_stride_arr; int bufsize = sizeof(request_header_t); void armci_post_gather(void *, int *, int *,int, armci_vapi_memhndl_t *,int,int,int,NB_CMPL_T *); bytes = 0; if(nbhandle)nbtag = nbhandle->tag; /*calculate the size of buffer needed */ bufsize += bytes+sizeof(void *) + 2*sizeof(int)*(stride_levels+1) + ehlen +2*sizeof(double) + 16; buf = buf0 = GET_SEND_BUFFER(bufsize,PUT,proc); msginfo = (request_header_t*)buf; buf += sizeof(request_header_t); bzero(msginfo, sizeof(request_header_t)); rem_ptr = dst_ptr; rem_stride_arr = dst_stride_arr; armci_save_strided_dscr(&buf,rem_ptr,rem_stride_arr,count,stride_levels,0); if(DEBUG_) { printf(" CLIENT :the dest_ptr is %p src is %p\n", (void*)rem_ptr,src_ptr); for(i =0; idatalen = 0; msginfo->bypass = 1; msginfo->pinned = 1; msginfo->from = armci_me; msginfo->to = proc; msginfo->format = STRIDED; msginfo->operation = PUT; msginfo->ehlen = 0; msginfo->dscrlen = buf - buf0 - sizeof(request_header_t); msginfo->bytes = msginfo->datalen + msginfo->dscrlen; /* I have not set msginfo->tag */ /* send the first phase request */ armci_send_req(proc, msginfo, bufsize); if(DEBUG_) { printf("%d:CLIENT : finished sending first put request \n",armci_me); fflush(stdout); } armci_wait_ack(buf0); if(DEBUG_) { printf("\n%d: client got ack about to post gather\n",armci_me); fflush(stdout); } /* the client is now in the second phase, in a loop creates the gather descr one at a time and posts them */ armci_post_gather(src_ptr,src_stride_arr,count,stride_levels, mhloc,proc,nbtag,CLN,&nbhandle->cmpl_info); if(DEBUG_) { printf("%d(c) : returned from armci_post_gather\n",armci_me); fflush(stdout); } if(nbhandle){ BUF_INFO_T *info=NULL; info=BUF_TO_BUFINFO(buf0); info->protocol=0; } FREE_SEND_BUFFER(msginfo); return 0; } int armci_two_phase_get(int proc, void*src_ptr, int src_stride_arr[], void*dst_ptr,int dst_stride_arr[], int count[], int stride_levels, void**context_ptr, armci_ihdl_t nb_handle, ARMCI_MEMHDL_T *mhloc) { char *buf, *buf0; request_header_t *msginfo; int bytes; int ehlen = 0,nbtag=0; int *rem_ptr; int num; int *rem_stride_arr; int bufsize = sizeof(request_header_t); extern void armci_post_scatter(void *,int *,int *,int, armci_vapi_memhndl_t *,int,int,int,NB_CMPL_T *); extern void armci_wait_for_blocking_scatter(); if(DEBUG_){ printf("%d(c):about to call armci_post_scatter, CLN value is %d\n", armci_me,CLN); fflush(stdout); } if(nb_handle)nbtag = nb_handle->tag; armci_post_scatter(dst_ptr, dst_stride_arr, count, stride_levels,mhloc, proc,nbtag,CLN,&nb_handle->cmpl_info); if(DEBUG_){ printf("\n%d: returned from armci_post_scatter %d\n",armci_me,num); fflush(stdout); } bytes = 0; bufsize += bytes+sizeof(void *) + 2*sizeof(int)*(stride_levels+1) + ehlen +2*sizeof(int) +2*sizeof(double) + 16; buf = buf0 = GET_SEND_BUFFER(bufsize,GET,proc); msginfo = (request_header_t *)buf; buf += sizeof(request_header_t); rem_ptr = src_ptr; rem_stride_arr = src_stride_arr; /*this call is to put the remote descriptor into the buffer to send*/ armci_save_strided_dscr(&buf,rem_ptr,rem_stride_arr,count,stride_levels,0); msginfo->datalen = 0; msginfo->bypass = 1; msginfo->pinned = 1; msginfo->from = armci_me; msginfo->to = proc; msginfo->format = STRIDED; msginfo->operation = GET; msginfo->ehlen = 0; msginfo->dscrlen = buf - buf0 - sizeof(request_header_t); msginfo->bytes = msginfo->datalen + msginfo->dscrlen; /* send the request asking the server to post gather */ armci_send_req(proc, msginfo, bufsize); if(DEBUG_){ printf("%d(c) : finished sending get request to server\n",armci_me); fflush(stdout); } if(nb_handle){ BUF_INFO_T *info=NULL; info=BUF_TO_BUFINFO(buf0); info->protocol=0; } else{ armci_wait_for_blocking_scatter(); FREE_SEND_BUFFER(msginfo); } if(DEBUG_){ printf("%d(c) : finished polling for scatter_recv\n",armci_me); fflush(stdout); } return 0; } #endif #if defined(ALLOW_PIN) && !defined(HAS_RDMA_GET) /*\ client version of remote strided get \*/ int armci_rem_get(int proc, void *src_ptr, int src_stride_arr[], void* dst_ptr, int dst_stride_arr[], int count[], int stride_levels, armci_ihdl_t nb_handle,void *mhloc,void *mhrem) { char *buf, *buf0; request_header_t *msginfo; int i, bytes; void *rem_ptr; int *rem_stride_arr; int bufsize = sizeof(request_header_t); /* calculate size of the buffer needed */ for(i=0, bytes=1;i<=stride_levels;i++)bytes*=count[i]; bufsize += sizeof(void*)+2*sizeof(int)*(stride_levels+1) +2*sizeof(double) + 16; /* +scale+alignment */ #ifdef VAPI /*need to send the rkey and lkey*/ /* lkey and rkey are unsigned its, but we cant trust it to stay like that*/ # ifdef OPENIB bufsize +=(sizeof(uint32_t)+sizeof(uint32_t)); # else bufsize +=(sizeof(VAPI_lkey_t)+sizeof(VAPI_rkey_t)); # endif #endif buf = buf0= GET_SEND_BUFFER(bufsize,GET,proc); if(nb_handle){ INIT_SENDBUF_INFO(nb_handle,buf,op,proc); _armci_buf_set_tag(buf,nb_handle->tag,0); if(nb_handle->bufid == NB_NONE) armci_set_nbhandle_bufid(nb_handle,buf,0); } msginfo = (request_header_t*)buf; buf += sizeof(request_header_t); rem_ptr = src_ptr; rem_stride_arr = src_stride_arr; /*this function fills the dscr into buf and also moves the buf ptr to the end of the dscr*/ armci_save_strided_dscr(&buf,rem_ptr,rem_stride_arr,count,stride_levels,0); /* to bypass the client MessageSnd buffer in get we need to add source pointer and stride info - server will put data directly there */ ADDBUF(buf,void*,dst_ptr); #ifdef VAPI #ifdef OPENIB ADDBUF(buf,uint32_t,((ARMCI_MEMHDL_T *)mhloc)->rkey); ADDBUF(buf,uint32_t,((ARMCI_MEMHDL_T *)mhrem)->lkey); #else ADDBUF(buf,VAPI_rkey_t,((ARMCI_MEMHDL_T *)mhloc)->rkey); ADDBUF(buf,VAPI_lkey_t,((ARMCI_MEMHDL_T *)mhrem)->lkey); #endif #endif for(i=0;ibypass=1; msginfo->pinned=1; msginfo->from = armci_me; msginfo->to = proc; msginfo->format = STRIDED; msginfo->operation = GET; msginfo->ehlen = 0; msginfo->datalen=0; msginfo->dscrlen = buf - buf0 - sizeof(request_header_t); msginfo->bytes = msginfo->dscrlen; #if defined(VAPI) /* prepare for set the stamp at the end of the user buffer */ if(count[0]dscrlen - msginfo->ehlen); #if 0 if(msginfo->ehlen)printf("%d:server from=%d len=%d: ptr=%p val=%d\n",armci_me,msginfo->from, msginfo->ehlen,h->ptr,h->val); fflush(stdout); #endif flag = (int*)(h->ptr); *flag = h->val; } void armci_server(request_header_t *msginfo, char *dscr, char* buf, int buflen) { int buf_stride_arr[MAX_STRIDE_LEVEL+1]; int *loc_stride_arr,slen; int *count, stride_levels; void *buf_ptr, *loc_ptr; void *scale; char *dscr_save = dscr; int rc, i,proc; # if defined(CLIENT_BUF_BYPASS) int *client_stride_arr=0; void *client_ptr=0; # endif /*return if using readv/socket for put*/ if(msginfo->operation==PUT && msginfo->datalen==0){ if(msginfo->ehlen) /* process extra header if available */ armci_process_extheader(msginfo, dscr, buf, buflen); return; } /* unpack descriptor record */ loc_ptr = *(void**)dscr; dscr += sizeof(void*); stride_levels = *(int*)dscr; dscr += sizeof(int); loc_stride_arr = (int*)dscr; dscr += stride_levels*sizeof(int); count = (int*)dscr; /* compute stride array for buffer */ buf_stride_arr[0]=count[0]; for(i=0; i< stride_levels; i++) buf_stride_arr[i+1]= buf_stride_arr[i]*count[i+1]; # if defined(CLIENT_BUF_BYPASS) if(msginfo->bypass){ dscr += (1+stride_levels)*sizeof(int); /* move past count */ GETBUF(dscr,void*,client_ptr); client_stride_arr = (int*)dscr; dscr += stride_levels*sizeof(int); } # endif /* get scale for accumulate, adjust buf to point to data */ switch(msginfo->operation){ case ARMCI_ACC_INT: slen = sizeof(int); break; case ARMCI_ACC_DCP: slen = 2*sizeof(double); break; case ARMCI_ACC_DBL: slen = sizeof(double); break; case ARMCI_ACC_CPL: slen = 2*sizeof(float); break; case ARMCI_ACC_FLT: slen = sizeof(float); break; case ARMCI_ACC_LNG: slen = sizeof(long); break; default: slen=0; } scale = dscr_save+ (msginfo->dscrlen - slen -msginfo->ehlen); /* if(ARMCI_ACC(msginfo->operation)) fprintf(stderr,"%d in server len=%d slen=%d alpha=%f data=%f\n", armci_me, msginfo->dscrlen, slen, *(double*)scale,*(double*)buf); */ buf_ptr = buf; /* data in buffer */ proc = msginfo->to; if(msginfo->operation == GET){ # if defined(CLIENT_BUF_BYPASS) || defined(VAPI) /* This path was not updated */ if(msginfo->bypass){ armci_send_strided_data_bypass(proc, msginfo, buf, buflen, loc_ptr, loc_stride_arr, client_ptr, client_stride_arr, count, stride_levels); }else # endif /* send tag followed by the actual message */ #ifdef SOCKETS armci_sock_send(msginfo->from, &(msginfo->tag), sizeof(msg_tag_t)); #endif armci_send_strided_data(proc, msginfo, buf, loc_ptr, stride_levels, loc_stride_arr, count); /* fprintf(stderr, "GET response sent with tag: %d\n, msginfo->tag", msginfo->tag); */ /* TBD:alternatively could include tag into strided message or pack */ } else{ #ifdef PIPE_BUFSIZE if((msginfo->bytes==0) && (msginfo->operation==PUT)){ armci_pipe_prep_receive_strided(msginfo,buf_ptr,stride_levels, loc_stride_arr, count, buflen); armci_pipe_receive_strided(msginfo,loc_ptr,loc_stride_arr, count, stride_levels); } else #endif { #if defined(PUT_NO_SRV_COPY) ARMCI_MEMHDL_T *mhloc; int nsegs; nsegs = 1; for(i=0; ioperation==PUT && msginfo->format==STRIDED && !msginfo->pinned && get_armci_region_local_hndl(loc_ptr,armci_clus_id(armci_me),&mhloc) && !msginfo->tag.imm_msg) { /*do nothing; data movement done done in pbuf_start_get().*/ } else #endif if((rc = armci_op_strided(msginfo->operation, scale, proc, buf_ptr, buf_stride_arr, loc_ptr, loc_stride_arr, count, stride_levels, 1,NULL))) armci_die("server_strided: op from buf failed",rc); } } if(msginfo->ehlen) /* process extra header if available */ armci_process_extheader(msginfo, dscr_save, buf, buflen); } #if ARMCI_ENABLE_GPC_CALLS && (defined(VAPI) || defined(SOCKETS)) static int gpc_call_process( request_header_t *msginfo, int len, char *dscr, char* buf, int buflen, char *sbuf); #endif void armci_server_vector( request_header_t *msginfo, char *dscr, char* buf, int buflen) { int proc; long len; void *scale; int i,s; char *sbuf = buf; if(msginfo->operation==PUT && msginfo->datalen==0)return;/*return if using readv/socket for put*/ /* unpack descriptor record */ GETBUF(dscr, long ,len); /* get scale for accumulate, adjust buf to point to data */ scale = buf; switch(msginfo->operation){ case ARMCI_ACC_INT: buf += sizeof(int); break; case ARMCI_ACC_DCP: buf += 2*sizeof(double); break; case ARMCI_ACC_DBL: buf += sizeof(double); break; case ARMCI_ACC_CPL: buf += 2*sizeof(float); break; case ARMCI_ACC_FLT: buf += sizeof(float); break; } proc = msginfo->to; /*fprintf(stderr,"scale=%f\n",*(double*)scale);*/ /* execute the operation */ switch(msginfo->operation) { case GET: /* fprintf(stderr, "%d:: Got a vector message!!\n", armci_me); */ if(msginfo->ehlen) { #if ARMCI_ENABLE_GPC_CALLS && defined(VAPI) gpc_call_process(msginfo, len, dscr, buf, buflen, sbuf); #else armci_die("Unexpected vector message with non-zero ehlen. GPC call?", msginfo->ehlen); #endif } else { for(i = 0; i< len; i++){ int parlen, bytes; void **ptr; GETBUF(dscr, int, parlen); GETBUF(dscr, int, bytes); /* fprintf(stderr,"len=%d bytes=%d parlen=%d\n",len,bytes,parlen);*/ ptr = (void**)dscr; dscr += parlen*sizeof(char*); for(s=0; s< parlen; s++){ armci_copy(ptr[s], buf, bytes); buf += bytes; } } /* send tag followed by the actual message */ #ifdef SOCKETS armci_sock_send(msginfo->from, &(msginfo->tag), sizeof(msg_tag_t)); #endif /* fprintf(stderr,"%d:: VECTOR GET. server sending buffer %p datalen=%d\n",armci_me, sbuf, msginfo->datalen); */ armci_send_data(msginfo, sbuf); } break; case PUT: /* fprintf(stderr,"received in buffer %f\n",*(double*)buf);*/ for(i = 0; i< len; i++){ int parlen, bytes; void **ptr; GETBUF(dscr, int, parlen); GETBUF(dscr, int, bytes); ptr = (void**)dscr; dscr += parlen*sizeof(char*); for(s=0; s< parlen; s++){ /* armci_copy(buf, ptr[s], bytes); */ bcopy(buf, ptr[s], (size_t)bytes); buf += bytes; } } break; default: /* this should be accumulate */ if(!ARMCI_ACC(msginfo->operation)) armci_die("v server: wrong op code",msginfo->operation); /* fprintf(stderr,"received first=%f last =%f in buffer\n",*/ /* *((double*)buf),((double*)buf)[99]);*/ for(i = 0; i< len; i++){ int parlen, bytes; void **ptr; GETBUF(dscr, int, parlen); GETBUF(dscr, int, bytes); ptr = (void**)dscr; dscr += parlen*sizeof(char*); armci_lockmem_scatter(ptr, parlen, bytes, proc); for(s=0; s< parlen; s++){ armci_acc_2D(msginfo->operation, scale, proc, buf, ptr[s], bytes, 1, bytes, bytes, 0); buf += bytes; } ARMCI_UNLOCKMEM(proc); } } } /**Routines for GPC calls**/ /**Server side routine to handle a GPC call request**/ /*===============Register this memory=====================*/ #if ARMCI_ENABLE_GPC_CALLS #if defined(VAPI) gpc_buf_t *gpc_req; #if defined(DATA_SERVER) && defined(SERVER_THREAD) # ifdef PTHREADS pthread_t data_server; # else # error Threading other than pthreads not yet implemented # endif #endif void block_thread_signal(int signo) { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); pthread_sigmask(SIG_BLOCK, &mask, NULL); } void unblock_thread_signal(int signo) { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); pthread_sigmask(SIG_UNBLOCK, &mask, NULL); } void gpc_init() { int i; for(i=0; ihndl, gpc_req[i].msginfo.to, gpc_req[i].msginfo.from, gcall->hdr, gcall->hlen, gcall->data, gcall->dlen, gcall->rhdr, gcall->rhlen, gcall->rdata, gcall->rdlen, GPC_PROBE) == GPC_DONE) { armci_send_data(&gpc_req[i].msginfo, gpc_req[i].reply); gpc_free_buf_handle(i); } } } void gpc_completion_handler(int sig) { if(sig != SIGUSR1) armci_die("gpc_completion_handler. Invoked with unexpected signal", sig); if(!pthread_equal(pthread_self(), data_server)) armci_die("Signal in a thread other than the data server!!!", sig); /* fprintf(stderr, "%d::SIGNAL\n", armci_me); */ gpc_completion_scan(); } void gpc_init_signals() { struct sigaction action; /*register signal handler**/ action.sa_handler = gpc_completion_handler; sigemptyset(&action.sa_mask); action.sa_flags = SA_RESTART; sigaction(SIGUSR1, &action, NULL); /*Block the GPC completion signal. data server unblocks it later for itself*/ /* block_thread_signal(SIGUSR1); */ #if 0 /*Mask this signal on main thread. Should only execute in the context of the data server.*/ if(pthread_self() == armci_usr_tid) { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); pthread_sigmask(SIG_BLOCK, &mask, NULL); } #endif } int gpc_get_buf_handle() { int i; gpc_call_t *gcall; for(i=0; ihndl, gpc_req[i].msginfo.to, gpc_req[i].msginfo.from, gcall->hdr, gcall->hlen, gcall->data, gcall->dlen, gcall->rhdr, gcall->rhlen, gcall->rdata, gcall->rdlen, GPC_WAIT) != GPC_DONE) armci_die("Wait on GPC call completion failed", 0); armci_send_data(&gpc_req[i].msginfo, gpc_req[i].reply); gpc_free_buf_handle(i); } gpc_req[i].active = 1; return i; } static int gpc_call_process( request_header_t *msginfo, int len, char *dscr, char* buf, int buflen, char *sbuf) { int h, hlen, dlen, rhlen, rdlen; int parlen; int rbuf; void *hdr, *data, *rhdr, *rdata; gpc_buf_t *gbuf; gpc_call_t *gcall; /*printf("%d:%s dscr=%p buf=%p \n",armci_me,FUNCTION_NAME,dscr,buf);fflush(stdout);*/ GETBUF(dscr, int, parlen); if(parlen != 1) armci_die("gpc_call_process: Invalid parlen in dscr", parlen); GETBUF(dscr, int, rhlen); dscr += parlen*sizeof(char*); GETBUF(dscr, int, parlen); if(parlen != 1) armci_die("gpc_call_process: Invalid parlen in dscr", parlen); GETBUF(dscr, int, rdlen); dscr += parlen*sizeof(char *); GETBUF(dscr, int, h); GETBUF(dscr, int, hlen); hdr = dscr; dscr += hlen; GETBUF(dscr, int, dlen); data = dscr; dscr += dlen; rhdr = buf; rdata = (char *)rhdr + rhlen; rbuf = gpc_get_buf_handle(); gbuf = &gpc_req[rbuf]; gbuf->msginfo = *msginfo; gbuf->call.hndl = h; gbuf->call.hlen = hlen; gbuf->call.dlen = dlen; gbuf->call.rhlen = rhlen; gbuf->call.rdlen = rdlen; gbuf->call.hdr = gbuf->send; gbuf->call.data = gbuf->send + hlen; bcopy(hdr, gbuf->call.hdr, gbuf->call.hlen); bcopy(data, gbuf->call.data, gbuf->call.dlen); gbuf->call.rhdr = gbuf->reply; gbuf->call.rdata = gbuf->reply + rhlen; gcall = &gbuf->call; if(armci_gpc_local_exec(gcall->hndl, gbuf->msginfo.to, gbuf->msginfo.from, gcall->hdr, gcall->hlen, gcall->data, gcall->dlen, gcall->rhdr, gcall->rhlen, gcall->rdata, gcall->rdlen, GPC_INIT) == GPC_DONE) { armci_send_data(msginfo, gbuf->reply); gpc_free_buf_handle(rbuf); } return 0; } /*Based on armci_rem_vector. On the server side, a vector GET request with a non-zero ehlen is a GPC call*/ int armci_rem_gpc(int op, armci_giov_t darr[],int len, gpc_send_t *send, int proc, int flag, armci_ihdl_t nb_handle) { char *buf, *buf0; request_header_t *msginfo; int bytes =0, s/*, slen=0*/; size_t adr; int bufsize = sizeof(request_header_t),isnonblocking=0; int send_len=0, i; void *ptr; if(nb_handle)isnonblocking=1; if(len != 2) armci_die("armci_rem_gpc: invalid len parameter", len); /* compute size of the buffer needed */ for(s=0; shlen + send->dlen; bufsize += bytes + sizeof(long) + send_len + 2*sizeof(double) + sizeof(double) + 8; /*+scale+allignment*/ #if defined(USE_SOCKET_VECTOR_API) if(flag){ int totaliovecs=MAX_IOVEC; /*if(op==PUT)*/bufsize-=bytes; buf = buf0= GET_SEND_BUFFER((bufsize+sizeof(struct iovec)*totaliovecs),op,proc); } else #endif { buf = buf0= GET_SEND_BUFFER(bufsize,op,proc); } msginfo = (request_header_t*)buf; bzero(msginfo,sizeof(request_header_t)); if(nb_handle){ INIT_SENDBUF_INFO(nb_handle,buf,op,proc); _armci_buf_set_tag(buf,nb_handle->tag,0); if(nb_handle->bufid == NB_NONE) armci_set_nbhandle_bufid(nb_handle,buf,0); } buf += sizeof(request_header_t); /* fill vector descriptor */ armci_save_vector_dscr(&buf,darr,len,op,0); /* add data to send*/ ptr = buf; ADDBUF(ptr, int, send->hndl); ADDBUF(ptr, int, send->hlen); bcopy(send->hdr, ptr, send->hlen); /* armci_copy? */ ptr += send->hlen; ADDBUF(ptr, int, send->dlen); bcopy(send->data, ptr, send->dlen); /* armci_copy? */ i = send_len%8; send_len += 8-i; buf += send_len; /*Dummy non-zero extended header to identify a GPC call*/ buf += sizeof(double); msginfo->ehlen = sizeof(double); /* fill message header */ msginfo->dscrlen = buf - buf0 - sizeof(request_header_t); msginfo->from = armci_me; msginfo->to = proc; msginfo->operation = op; msginfo->format = VECTOR; msginfo->datalen = bytes; msginfo->bytes = msginfo->datalen+msginfo->dscrlen; #if defined(USE_SOCKET_VECTOR_API) if(flag&&(op==GET||op==PUT)){ armci_direct_vector(msginfo,darr,len,proc); return 0; } #endif /* #ifdef VAPI */ /* if(op == GET) { */ /* if(msginfo->dscrlen < (bytes - sizeof(int))) */ /* *(int*)(((char*)(msginfo+1))+(bytes-sizeof(int))) = ARMCI_STAMP; */ /* else */ /* *(int*)(((char*)(msginfo+1))+(msginfo->dscrlen+bytes-sizeof(int))) = ARMCI_STAMP; */ /* } */ /* #endif */ armci_send_req(proc, msginfo, bufsize); if (nb_handle && op==GET) armci_save_vector_dscr(&buf0,darr,len,op,1); if (op == GET # if !defined(SOCKETS) && !nb_handle # endif ){ armci_complete_vector_get(darr,len,msginfo); } return 0; } #else /*Empty functions to let unsupported hardware smoothly pass through initialization*/ void gpc_init(void) {} void gpc_init_signals(void) {} #endif #endif ga-5.9.2/armci/src/common/signaltrap.c000066400000000000000000000247231500715745200176160ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: signaltrap.c,v 1.28 2005-05-13 19:06:40 vinod Exp $ */ /******************************************************\ * Signal handler functions for the following signals: * * SIGINT, SIGCHLD, SIGBUS, SIGFPE, SIGILL, * * SIGSEGV, SIGSYS, SIGTRAP, SIGHUP, SIGTERM * * Used to call armci_error that frees up IPC resources * \******************************************************/ #if HAVE_SIGNAL_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_WAIT_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_ERRNO_H # include #endif #include "armci.h" #include "armcip.h" #define PAUSE_ON_ERROR__ #define Error armci_die #if !defined(armci_die) extern void Error(); #endif # define SigType void #ifndef SIG_ERR # define SIG_ERR (SigType (*)())-1 #endif extern int armci_me; int AR_caught_sigint=0; int AR_caught_sigterm=0; int AR_caught_sigchld=0; int AR_caught_sigsegv=0; int AR_caught_sig=0; SigType (*SigChldOrig)(), (*SigIntOrig)(), (*SigHupOrig)(), (*SigTermOrig)(); SigType (*SigSegvOrig)(); /*********************** SIGINT *************************************/ SigType SigIntHandler(sig) int sig; { AR_caught_sigint = 1; AR_caught_sig= sig; Error("SigIntHandler: interrupt signal was caught",(int) sig); } void TrapSigInt() /* Trap the signal SIGINT so that we can propagate error conditions and also tidy up shared system resources in a manner not possible just by killing everyone */ { if ( (SigIntOrig = signal(SIGINT, SigIntHandler)) == SIG_ERR) Error("TrapSigInt: error from signal setting SIGINT",0); } void RestoreSigInt() /* Restore the original signal handler */ { if(AR_caught_sigint) SigIntOrig(SIGINT); if ( signal(SIGINT, SigIntOrig) == SIG_ERR) Error("RestoreSigInt: error from restoring signal SIGINT",0); } /*********************** SIGABORT *************************************/ SigType SigAbortHandler(sig) int sig; { AR_caught_sig= sig; Error("SigIntHandler: abort signal was caught: cleaning up",(int) sig); } void TrapSigAbort() /* Trap the signal SIGINT so that we can propagate error conditions and also tidy up shared system resources in a manner not possible just by killing everyone */ { if ( signal(SIGINT, SigAbortHandler) == SIG_ERR) Error("TrapSigAbort: error from signal setting SIGABORT",0); } /*********************** SIGCHLD *************************************/ SigType SigChldHandler(sig) int sig; { int status; #if defined(LINUX) pid_t ret; /* Trap signal as soon as possible to avoid race */ if ( (SigChldOrig = signal(SIGCHLD, SigChldHandler)) == SIG_ERR) Error("SigChldHandler: error from signal setting SIGCHLD",0); #endif # if defined(LINUX) ret = waitpid(0, &status, WNOHANG); if((ret == 0) || ((ret == -1) && (errno == ECHILD))) { return; } # else (void)wait(&status); # endif AR_caught_sigchld=1; AR_caught_sig= sig; Error("Child process terminated prematurely, status=",(int) status); } void TrapSigChld() /* Trap SIGCHLD so that can tell if children die unexpectedly. */ { if ( (SigChldOrig = signal(SIGCHLD, SigChldHandler)) == SIG_ERR) Error("TrapSigChld: error from signal setting SIGCHLD",0); } void RestoreSigChld() { if(AR_caught_sigchld) SigChldOrig(SIGCHLD); if (signal(SIGCHLD, SigChldOrig) == SIG_ERR) Error("RestoreSigChld: error from restoring signal SIGChld",0); } void RestoreSigChldDfl() { (void) signal(SIGCHLD, SIG_DFL); } /*********************** SIGBUS *************************************/ SigType SigBusHandler(sig) int sig; { AR_caught_sig= sig; #ifdef PAUSE_ON_ERROR fprintf(stderr,"%d(%d): Bus Error ... pausing\n", armci_me, getpid() );pause(); #endif Error("Bus error, status=",(int) sig); } void TrapSigBus() /* Trap SIGBUS */ { if ( signal(SIGBUS, SigBusHandler) == SIG_ERR) Error("TrapSigBus: error from signal setting SIGBUS", 0); } /*********************** SIGFPE *************************************/ SigType SigFpeHandler(sig) int sig; { AR_caught_sig= sig; #ifdef PAUSE_ON_ERROR fprintf(stderr,"%d(%s:%d): Sig FPE ... pausing\n", armci_me, armci_clus_info[armci_clus_me].hostname, getpid() );pause(); #endif Error("Floating Point Exception error, status=",(int) sig); } void TrapSigFpe() /* Trap SIGFPE */ { if ( signal(SIGFPE, SigFpeHandler) == SIG_ERR) Error("TrapSigFpe: error from signal setting SIGFPE", 0); } /*********************** SIGILL *************************************/ SigType SigIllHandler(sig) int sig; { AR_caught_sig= sig; Error("Illegal Instruction error, status=",(int) sig); } void TrapSigIll() /* Trap SIGILL */ { if ( signal(SIGILL, SigIllHandler) == SIG_ERR) Error("TrapSigIll: error from signal setting SIGILL", 0); } /*********************** SIGSEGV *************************************/ SigType SigSegvHandler(sig) int sig; { AR_caught_sig= sig; AR_caught_sigsegv=1; #ifdef PAUSE_ON_ERROR fprintf(stderr,"%d(%s:%d): Segmentation Violation ... pausing\n", armci_me, armci_clus_info[armci_clus_me].hostname, getpid() );pause(); #endif Error("Segmentation Violation error, status=",(int) sig); } #ifdef ENABLE_CHECKPOINT static void * signal_arr[100]; SigType SigSegvActionSa(int sig,siginfo_t *sinfo, void *ptr) { int (*func)(); AR_caught_sig= sig; AR_caught_sigsegv=1; func = signal_arr[sig]; /*printf("\n%d:in sigaction %p, %d\n",armci_me,sinfo->si_addr,sinfo->si_errno);fflush(stdout);*/ if(func(sinfo->si_addr,sinfo->si_errno,sinfo->si_fd)) Error("Segmentation Violation error, status=",(int) SIGSEGV); } void TrapSigSegvSigaction() { struct sigaction sa; sa.sa_sigaction = (void *)SigSegvActionSa; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGSEGV, &sa, NULL); } #endif void TrapSigSegv() /* Trap SIGSEGV */ { if ( (SigSegvOrig=signal(SIGSEGV, SigSegvHandler)) == SIG_ERR) Error("TrapSigSegv: error from signal setting SIGSEGV", 0); } void RestoreSigSegv() /* Restore the original signal handler */ { /* if(AR_caught_sigsegv) SigSegvOrig(SIGSEGV); */ #ifdef ENABLE_CHECKPOINT__ struct sigaction sa; sa.sa_handler = (void *)SigSegvOrig; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGSEGV, &sa, NULL); sigaction(SIGSEGV,&sa,NULL); #else if ( signal(SIGSEGV,SigSegvOrig) == SIG_ERR) Error("RestoreSigSegv: error from restoring signal SIGSEGV",0); #endif } /*********************** SIGSYS *************************************/ SigType SigSysHandler(sig) int sig; { AR_caught_sig= sig; Error("Bad Argument To System Call error, status=",(int) sig); } void TrapSigSys() /* Trap SIGSYS */ { #ifndef LINUX if ( signal(SIGSYS, SigSysHandler) == SIG_ERR) Error("TrapSigSys: error from signal setting SIGSYS", 0); #endif } /*********************** SIGTRAP *************************************/ SigType SigTrapHandler(sig) int sig; { AR_caught_sig= sig; Error("Trace Trap error, status=",(int) sig); } void TrapSigTrap() /* Trap SIGTRAP */ { if ( signal(SIGTRAP, SigTrapHandler) == SIG_ERR) Error("TrapSigTrap: error from signal setting SIGTRAP", 0); } /*********************** SIGHUP *************************************/ SigType SigHupHandler(sig) int sig; { AR_caught_sig= sig; Error("Hangup error, status=",(int) sig); } void TrapSigHup() /* Trap SIGHUP */ { if ( (SigHupOrig = signal(SIGHUP, SigHupHandler)) == SIG_ERR) Error("TrapSigHup: error from signal setting SIGHUP", 0); } void RestoreSigHup() /* Restore the original signal handler */ { if(AR_caught_sig== SIGHUP) SigHupOrig(SIGHUP); if ( signal(SIGHUP, SigHupOrig) == SIG_ERR) Error("RestoreSigHUP: error from restoring signal SIGHUP",0); } /*********************** SIGTERM *************************************/ SigType SigTermHandler(sig) int sig; { AR_caught_sigterm = 1; AR_caught_sig= sig; Error("Terminate signal was sent, status=",(int) sig); } void TrapSigTerm() /* Trap SIGTERM */ { if ( (SigTermOrig = signal(SIGTERM, SigTermHandler)) == SIG_ERR) Error("TrapSigTerm: error from signal setting SIGTERM", 0); } void RestoreSigTerm() /* Restore the original signal handler */ { if(AR_caught_sigterm && (SigTermOrig != SIG_DFL) ) SigTermOrig(SIGTERM); if ( signal(SIGTERM, SigTermOrig) == SIG_ERR) Error("RestoreSigTerm: error from restoring signal SIGTerm",0); } /*********************** SIGIOT *************************************/ #ifdef SIGIOT SigType SigIotHandler(sig) int sig; { AR_caught_sig= sig; Error("IOT signal was sent, status=",(int) sig); } void TrapSigIot() /* Trap SIGIOT */ { if ( signal(SIGIOT, SigIotHandler) == SIG_ERR) Error("TrapSigIot: error from signal setting SIGIOT", 0); } #endif /*********************** SIGCONT *************************************/ SigType SigContHandler(sig) int sig; { /* Error("Trace Cont error, status=",(int) sig);*/ AR_caught_sig= sig; } void TrapSigCont() /* Trap SIGCONT */ { if ( signal(SIGCONT, SigContHandler) == SIG_ERR) Error("TrapSigCont: error from signal setting SIGCONT", 0); } /*********************** SIGXCPU *************************************/ SigType SigXcpuHandler(sig) int sig; { AR_caught_sig= sig; Error("Terminate signal was sent, status=",(int) sig); } void TrapSigXcpu() /* Trap SIGXCPU */ { if ( signal(SIGXCPU, SigXcpuHandler) == SIG_ERR) Error("TrapSigXcpu: error from signal setting SIGXCPU", 0); } /******************* external API *********************************/ void ARMCI_ChildrenTrapSignals() { TrapSigBus(); TrapSigFpe(); TrapSigIll(); #ifdef ENABLE_CHECKPOINT TrapSigSegvSigaction(); #else TrapSigSegv(); #endif TrapSigSys(); TrapSigTrap(); TrapSigAbort(); TrapSigTerm(); TrapSigInt(); } void ARMCI_ParentTrapSignals() { TrapSigChld(); TrapSigHup(); } void ARMCI_RestoreSignals() { RestoreSigTerm(); RestoreSigInt(); RestoreSigSegv(); } void ARMCI_ParentRestoreSignals() { RestoreSigChld(); ARMCI_RestoreSignals(); RestoreSigHup(); } #ifdef ENABLE_CHECKPOINT /*user can register a function with 3 parameters, 1st offending address * 2nd err number and third file descriptor*/ void ARMCI_Register_Signal_Handler(int sig, void (*func)()) { signal_arr[sig]=func; } #endif ga-5.9.2/armci/src/common/spawn.c000066400000000000000000000055561500715745200166050ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* OS specific server process/thread creation and destruction * JN/03.25.2000 */ #if HAVE_STDIO_H # include #endif #if HAVE_ERRNO_H # include #endif #include "armcip.h" #ifdef WIN32 /************************** Windows threads **************************/ #if HAVE_WINDOWS_H # include #endif #if HAVE_PROCESS_H # include #endif thread_id_t armci_serv_tid; unsigned long armci_serv_handle; #ifndef NO_STDLIBC #define NEWTHREAD CreateThread #else #define NEWTHREAD _beginthreadex #endif unsigned __stdcall armci_wrap_func(void *arg) { void (*func)(void*); func = arg; /* boost the server thread priority be better responsiveness */ (void)SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST); func(NULL); return (unsigned)0; } void armci_create_server_thread ( void* (* func)(void*) ) { /* as we need to use std C rt library we cannot use CreateThread */ armci_serv_handle = NEWTHREAD(NULL, 0, armci_wrap_func, (void*)func, 0, &armci_serv_tid); if(!armci_serv_handle) armci_die("armci_create_server_thread: create failed",0); } void armci_terminate_server_thread() { /*int rc;*/ /* TerminateThread(armci_serv_handle,&rc);*/ } /****************************** PTHREADS *****************************/ #elif defined(PTHREADS) #include thread_id_t armci_serv_tid; void armci_create_server_thread ( void* (* func)(void*) ) { pthread_attr_t attr; int rc; if(pthread_attr_init(&attr)) armci_die("armci_create_server_thread: attr init failed",0); #if defined(AIX) pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); #endif rc = pthread_create(&armci_serv_tid, &attr, func, NULL); if(rc) armci_die("armci_create_server_thread: create failed",errno); pthread_attr_destroy(&attr); } void armci_terminate_server_thread() { if(pthread_join(armci_serv_tid,NULL)) armci_die("armci_terminate_server_thread: failed",0); } #else /**************************** Unix processes ******************************/ #if HAVE_UNISTD_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_WAIT_H # include #endif pid_t server_pid= (pid_t)0; void armci_create_server_process ( void* (* func)(void*) ) { pid_t pid; if ( (pid = fork() ) < 0) armci_die("fork failed", (int)pid); else if(pid == 0){ armci_me = SOFFSET - armci_me; /* server id derived from parent id */ func(NULL); } else server_pid = pid; } void armci_wait_server_process() { int stat; pid_t rc; if(!server_pid) return; rc = wait (&stat); if (rc != server_pid){ perror("ARMCI master: wait for child process (server) failed:"); } server_pid = (pid_t)0; } #endif ga-5.9.2/armci/src/common/timer.c000066400000000000000000000017651500715745200165730ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #ifdef WIN32 static double msec; /* reference for timer */ # include #else #if HAVE_SYS_TYPES_H # include #endif #if HAVE SYS_TIME_H # include #endif static unsigned firstsec=0; /* Reference for timer */ static unsigned firstusec=0; /* Reference for timer */ #endif static int first_call=1; double armci_timer() { #ifdef WIN32 double t0 = (double)GetCurrentTime(); if(first_call){ first_call=0; msec=t0; return 0.0; } t0 -=msec; if(t0<0.0)t0 += (double)0xffffffff; return 0.01*t0; #else double low, high; struct timeval tp; struct timezone tzp; (void) gettimeofday(&tp,&tzp); if (first_call) { firstsec = tp.tv_sec; firstusec = tp.tv_usec; first_call = 0; } low = (double)(tp.tv_usec>>1) - (double) (firstusec>>1); high = (double) (tp.tv_sec - firstsec); return high + 1.0e-6*(low+low); #endif } ga-5.9.2/armci/src/common/utils.c000066400000000000000000000151351500715745200166070ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* * A barrier causes threads to wait until a set of threads has * all "reached" the barrier. The number of threads required is * set when the barrier is initialized, and cannot be changed * except by reinitializing. * * The barrier_init() and barrier_destroy() functions, * respectively, allow you to initialize and destroy the * barrier. * * The barrier_wait() function allows a thread to wait for a * barrier to be completed. One thread (the one that happens to * arrive last) will return from barrier_wait() with the status * -1 on success -- others will return with 0. The special * status makes it easy for the calling code to cause one thread * to do something in a serial region before entering another * parallel section of code. */ #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_ERRNO_H # include #endif #if HAVE_SYS_TIME_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_ERRNO_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #include "utils.h" #define DEBUG_ int mt_size; /* number of processes: needed for collective mt ops */ int mt_tpp; /* number of threads used for collective ops */ thread_barrier_t mt_barrier; /* static barrier used for multi-threaded MT_BARRIER */ int armci_malloc_mt(void *ptr[], int bytes) { int rc, th_size, i, j; th_size = mt_size * mt_tpp; if (thread_barrier_wait(&mt_barrier)==-1) { rc = PARMCI_Malloc(ptr, bytes * mt_tpp); #ifdef DEBUG printf("bytes=%d\n", bytes); for (i = 0; i < mt_size; i++) printf("ptr[%d]=%p\n",i,ptr[i]); #endif /* at this point proc ptrs are at beggining of the list */ for (i = mt_size - 1; i >= 0; i--) for (j = mt_tpp - 1; j >= 0; j--) { #ifdef DEBUG printf("mt_size=%d,mt_tpp=%d,i=%d,j=%d,ptr[%d]=%p+%d\n", mt_size,mt_tpp,i,j,i*mt_tpp+j,ptr[i],j*bytes); fflush(stdout); #endif ptr[i * mt_tpp + j] = ((char*)ptr[i]) + j * bytes; } } thread_barrier_wait(&mt_barrier); return rc; } int armci_free_mt(void *ptr, int th_idx) { } #ifdef POSIX_THREADS /* * Initialize a barrier for use. */ int thread_barrier_init (thread_barrier_t *barrier, int count) { int status; barrier->threshold = barrier->counter = count; barrier->cycle = 0; status = pthread_mutex_init (&barrier->mutex, NULL); if (status != 0) return status; status = pthread_cond_init (&barrier->cv, NULL); if (status != 0) { pthread_mutex_destroy (&barrier->mutex); return status; } barrier->valid = BARRIER_VALID; return 0; } /* * Destroy a barrier when done using it. */ int thread_barrier_destroy (thread_barrier_t *barrier) { int status, status2; if (barrier->valid != BARRIER_VALID) return EINVAL; status = pthread_mutex_lock (&barrier->mutex); if (status != 0) return status; /* * Check whether any threads are known to be waiting; report * "BUSY" if so. */ if (barrier->counter != barrier->threshold) { pthread_mutex_unlock (&barrier->mutex); return EBUSY; } barrier->valid = 0; status = pthread_mutex_unlock (&barrier->mutex); if (status != 0) return status; /* * If unable to destroy either 1003.1c synchronization * object, return the error status. */ status = pthread_mutex_destroy (&barrier->mutex); status2 = pthread_cond_destroy (&barrier->cv); return (status == 0 ? status : status2); } /* * Wait for all members of a barrier to reach the barrier. When * the count (of remaining members) reaches 0, broadcast to wake * all threads waiting. */ int thread_barrier_wait (thread_barrier_t *barrier) { int status, cancel, tmp, cycle; if (barrier->valid != BARRIER_VALID) return EINVAL; status = pthread_mutex_lock (&barrier->mutex); if (status != 0) return status; cycle = barrier->cycle; /* Remember which cycle we're on */ if (--barrier->counter == 0) { barrier->cycle = !barrier->cycle; barrier->counter = barrier->threshold; status = pthread_cond_broadcast (&barrier->cv); /* * The last thread into the barrier will return status * -1 rather than 0, so that it can be used to perform * some special serial code following the barrier. */ if (status == 0) status = -1; } else { /* * Wait with cancellation disabled, because barrier_wait * should not be a cancellation point. */ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &cancel); /* * Wait until the barrier's cycle changes, which means * that it has been broadcast, and we don't want to wait * anymore. */ while (cycle == barrier->cycle) { status = pthread_cond_wait ( &barrier->cv, &barrier->mutex); if (status != 0) break; } pthread_setcancelstate (cancel, &tmp); } /* * Ignore an error in unlocking. It shouldn't happen, and * reporting it here would be misleading -- the barrier wait * completed, after all, whereas returning, for example, * EINVAL would imply the wait had failed. The next attempt * to use the barrier *will* return an error, or hang, due * to whatever happened to the mutex. */ pthread_mutex_unlock (&barrier->mutex); return status; /* error, -1 for waker, or 0 */ } #endif #if 0 /*** NAME timing.c PURPOSE Timing routines for calculating the execution time: void start_timer(void); Set the timer. double elapsed_time(void); Return the timing elapsed since the timer has been set. NOTES Jialin Ju - Oct 16, 1995 Created. ***/ /* Timing routines that use standard Unix gettingofday() */ static struct timezone tz; static struct timeval start_time, finish_time; /* Start measuring a time delay */ void start_timer(void) { gettimeofday( &start_time, &tz); } /* Retunrn elapsed time in milliseconds */ double elapsed_time(void) { gettimeofday( &finish_time, &tz); return(1000.0*(finish_time.tv_sec - start_time.tv_sec) + (finish_time.tv_usec - start_time.tv_usec)/1000.0 ); } /* Return the stopping time in milliseconds */ double stop_time(void) { gettimeofday( &finish_time, &tz); return(1000.0*finish_time.tv_sec + finish_time.tv_usec/1000.0); } #endif ga-5.9.2/armci/src/devices/000077500000000000000000000000001500715745200154305ustar00rootroot00000000000000ga-5.9.2/armci/src/devices/mpi-mt/000077500000000000000000000000001500715745200166335ustar00rootroot00000000000000ga-5.9.2/armci/src/devices/mpi-mt/mpi2.h000066400000000000000000000027461500715745200176640ustar00rootroot00000000000000#ifndef MPI2_H #define MPI2_H #include #define MPI_SPAWN_DEBUG 0 #undef MPI_SPAWN_ZEROCOPY /* enables zero-copy for large requests */ #ifdef MPI_SPAWN_ZEROCOPY # define MPI_USER_DEF_DATATYPE /* Enables MPI userdefined type for non-contig * data, if MPI_SPAWN_ZEROCOPY is enabled */ #endif /* uncomment MULTIPLE_BUFS macro definition to disable multiple buffers */ #undef MULTIPLE_BUFS #define ARMCI_MPI_SPAWN_INIT_TAG 1000 #define ARMCI_MPI_SPAWN_TAG 2000 #define ARMCI_MPI_SPAWN_DATA_TAG 3000 #define ARMCI_MPI_SPAWN_VDATA_TAG 4000 #define ARMCI_MPI_CLIENT2SERVER_TAG 4500 #define ARMCI_MPI_SERVER2CLIENT_TAG 5000 /* In case of multiple buffers, we use tags from 2001 to 2999 (999 tags * total) to ensure flow control at the server side */ #define ARMCI_MPI_SPAWN_TAG_BEGIN 2001 #define ARMCI_MPI_SPAWN_TAG_END 2999 #define GET_SEND_BUFFER _armci_buf_get #define FREE_SEND_BUFFER _armci_buf_release #define COMPLETE_HANDLE _armci_buf_complete_nb_request #define TEST_HANDLE _armci_buf_test_nb_request #define SEND 0 #define RECV 1 extern void armci_mpi_strided(int op, void *ptr, int stride_levels, int stride_arr[], int count[], int proc, MPI_Comm comm); extern void armci_mpi_strided2(int op, void *ptr, int stride_levels, int stride_arr[], int count[], int proc, MPI_Comm comm); #endif /* MPI2_H */ ga-5.9.2/armci/src/devices/mpi-mt/mpi2_client.c000066400000000000000000000205221500715745200212050ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** * MPI_SPAWN: ARMCI on top of MPI Multithreaded * Abhinav Vishnu */ #if HAVE_STDARG_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_STDLIB_H # include #endif #include "mpi.h" #include "mpi2.h" #include "armcip.h" #include "request.h" #include "armci_shmem.h" #include "locks.h" #include #define ARMCI_ROOT 0 /* root process */ /* Inter-communicators for communicating between clients and data servers */ MPI_Comm MPI_COMM_CLIENT2SERVER=MPI_COMM_NULL; static int armci_nserver=-1; static int *_armci_mpi_tag=NULL; extern char ***_armci_argv; extern int armci_get_shmem_info(char *addrp, int* shmid, long *shmoffset, size_t *shmsize); #if MPI_SPAWN_DEBUG void armci_mpi2_debug(int rank, const char *format, ...) { va_list arg; if(rank == armci_me) { va_start(arg, format); printf("%d: ", rank); vprintf(format, arg); va_end(arg); fflush(stdout); } } #else #define armci_mpi2_debug(x, ...) #endif #if MPI_SPAWN_DEBUG static inline int CHECK_Mpi (int status) { if(status != MPI_SUCCESS) { armci_mpi2_debug(armci_me, "MPI Check failed.\n"); armci_die("CHECK_Mpi failed.", 0); } } #else # define CHECK_Mpi(x) x #endif /************************************************************************** * Platform specific server code as required by the ARMCI s/w layer. (BEGIN) */ /* Create connections between clients and servers */ void armci_init_connections() { armci_mpi2_debug(0, "armci_init_connections\n"); _armci_buf_init(); /* CHECK: Is this correct ? */ CHECK_Mpi(MPI_Barrier(ARMCI_COMM_WORLD)); /* Abhinav Vishnu */ armci_create_server_MPIprocess(); armci_mpi2_debug(0, "armci_init_connections completed\n"); } void armci_wait_for_server() { armci_mpi2_debug(0, "armci_wait_for_server: wait for server to quit\n"); if (armci_me == armci_master) { armci_serv_quit(); } } void armci_client_connect_to_servers() { /* Abhinav Vishnu */ } /* NOTE: armci_mpi_strided and armci_mpi_strided2 are the only 2 functions * that are common to client and server part */ void armci_mpi_strided_c2s(int op, void *ptr, int stride_levels, int stride_arr[], int count[], int proc, MPI_Comm comm) { int i, j; long idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int bvalue[MAX_STRIDE_LEVEL], bunit[MAX_STRIDE_LEVEL]; MPI_Status status; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) n1dim *= count[i]; /* calculate the destination indices */ bvalue[0] = 0; bvalue[1] = 0; bunit[0] = 1; bunit[1] = 1; for(i=2; i<=stride_levels; i++) { bvalue[i] = 0; bunit[i] = bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) bvalue[j] = 0; } if(op == SEND) { CHECK_Mpi( MPI_Send(((char*)ptr)+idx, count[0], MPI_BYTE, proc, ARMCI_MPI_CLIENT2SERVER_TAG, comm) ); } else /* ( op == RECV) */ { CHECK_Mpi( MPI_Recv(((char*)ptr)+idx, count[0], MPI_BYTE, proc, ARMCI_MPI_SERVER2CLIENT_TAG, comm, &status) ); } } } /* This is the only function that is common to client and server part */ void armci_mpi_strided2(int op, void *ptr, int stride_levels, int stride_arr[], int count[], int proc, MPI_Comm comm) { /* Not supported */ assert(0); } /*\ client sends request message to server \*/ int armci_send_req_msg (int proc, void *buf, int bytes) { int clus_id = armci_clus_id(proc); int server ; /* Abhinav Vishnu */ server = armci_clus_info[clus_id].master; armci_mpi2_debug(armci_me, "armci_send_req_msg(): proc=%d, server=%d, " "buf=%p, bytes=%d\n", proc, server, buf, bytes); CHECK_Mpi( MPI_Send(buf, bytes, MPI_BYTE, server, ARMCI_MPI_CLIENT2SERVER_TAG, ARMCI_COMM_WORLD) ); armci_mpi2_debug(armci_me, "armci_send_req_msg(): send msg to server(%d), to" "fwd to client %d\n", server, proc); return 0; } /*\ client sends strided data + request to server \*/ int armci_send_req_msg_strided(int proc, request_header_t *msginfo,char *ptr, int strides, int stride_arr[], int count[]) { int server; int clus_id = armci_clus_id(proc); int bytes; /* Abhinav Vishnu */ server = armci_clus_info[clus_id].master; armci_mpi2_debug(armci_me, "armci_send_req_msg_strided: proc=%d server=%d " "bytes=%d (op=%d)\n", proc, server, msginfo->datalen, msginfo->operation); /* we write header + descriptor of strided data */ bytes = sizeof(request_header_t) + msginfo->dscrlen; armci_send_req_msg(proc, msginfo, bytes); { /* for larger blocks write directly thus avoiding memcopy */ armci_mpi_strided_c2s(SEND, ptr, strides, stride_arr, count, server, ARMCI_COMM_WORLD); } armci_mpi2_debug(armci_me, "armci_send_req_msg_strided(): send msg to " "server(%d), to fwd to client %d\n", server, proc); return 0; } /*\ client receives data from server \*/ char *armci_ReadFromDirect (int proc, request_header_t *msginfo, int len) { int server; int clus_id = armci_clus_id(proc); MPI_Status status; server = armci_clus_info[clus_id].master; armci_mpi2_debug(armci_me, "armci_ReadFromDirect: proc=%d, server=%d, " "msginfo=%p, bytes=%d (op=%d)\n", proc, server, msginfo, len, msginfo->operation); CHECK_Mpi( MPI_Recv(msginfo + 1, len, MPI_BYTE, server, ARMCI_MPI_SERVER2CLIENT_TAG, ARMCI_COMM_WORLD, &status) ); armci_mpi2_debug(armci_me, "recv msg from server(%d), fwd by client %d\n", server, proc); { int count; MPI_Get_count(&status, MPI_BYTE, &count); if (count != len) { armci_mpi2_debug(armci_me, "armci_ReadFromDirect: got %d bytes, " "expected %d bytes\n", count, len); armci_die("armci_ReadFromDirect: MPI_Recv failed.", count); } } return (char *) (msginfo+1); } /*\ client receives strided data from server \*/ void armci_ReadStridedFromDirect(int proc, request_header_t* msginfo, void *ptr, int strides, int stride_arr[], int count[]) { int server; int clus_id = armci_clus_id(proc); /* Abhinav Vishnu */ server = armci_clus_info[clus_id].master; armci_mpi2_debug(armci_me, "armci_ReadStridedFromDirect: proc=%d " "stride_levels=%d, server=%d bytes=%d (op=%d)\n", proc, strides, server, msginfo->datalen, msginfo->operation); { armci_mpi_strided_c2s(RECV, ptr, strides, stride_arr, count, server, ARMCI_COMM_WORLD); } } /** * Platform specific server code ENDs here. (END) **************************************************************************/ static void armci_gather_hostnames(char **hostname_arr) { /* Code must not flow through here */ assert(0); } /** * Create server processes. This is called in armci_start_server. * Must be called after armci_init_clusinfo(). */ void armci_create_server_MPIprocess () { int rank, size, flag, i; CHECK_Mpi(MPI_Initialized(&flag)); if (flag == 0) armci_die("ARMCI error: MPI_Init must be called before PARMCI_Init()",0); CHECK_Mpi(MPI_Comm_rank(ARMCI_COMM_WORLD, &rank)); CHECK_Mpi(MPI_Comm_size(ARMCI_COMM_WORLD, &size)); armci_nserver = armci_nclus; /* makesure all processes sync here. CHECK: does it ensure global sync ? */ CHECK_Mpi(MPI_Barrier(ARMCI_COMM_WORLD)); armci_mpi2_debug(0, "armci_create_server_MPIprocess: Servers spawned!\n"); } ga-5.9.2/armci/src/devices/mpi-mt/mpi2_server.c000066400000000000000000000232611500715745200212400ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include /** * mpi2_server.c: MPI_SPAWN Server Code * Manojkumar Krishnan */ #if HAVE_STDARG_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_STDLIB_H # include #endif #include "mpi.h" #include "armcip.h" #include "mpi2.h" #include "kr_malloc.h" #include "locks.h" /* Inter-communicators for communicating with clients */ /* Abhinav Vishnu */ void armci_mpi2_server_init_twosided(); MPI_Comm MPI_COMM_SERVER2CLIENT = MPI_COMM_NULL; static int armci_server_me=-1, armci_nserver=-1; static int armci_client_first=-1, armci_nclients=-1; extern Header *armci_get_shmem_ptr(int shmid, long shmoffset, size_t shmsize); #if MPI_SPAWN_DEBUG void armci_mpi2_server_debug(int rank, const char *format, ...) { va_list arg; if(rank == armci_server_me) { va_start(arg, format); printf("**** Server %d: ", armci_server_me); vprintf(format, arg); va_end(arg); fflush(stdout); } } #else # define armci_mpi2_server_debug(x, ...) #endif #if 1 static inline int CHECK_Mpi (int status) { if(status != MPI_SUCCESS) { armci_mpi2_server_debug(armci_me, "MPI Check failed.\n"); armci_die("CHECK_Mpi failed.", 0); } } #else # define CHECK_Mpi(x) x #endif void armci_mpi_strided_s2c(int op, void *ptr, int stride_levels, int stride_arr[], int count[], int proc, MPI_Comm comm) { int i, j; long idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int bvalue[MAX_STRIDE_LEVEL], bunit[MAX_STRIDE_LEVEL]; MPI_Status status; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) n1dim *= count[i]; /* calculate the destination indices */ bvalue[0] = 0; bvalue[1] = 0; bunit[0] = 1; bunit[1] = 1; for(i=2; i<=stride_levels; i++) { bvalue[i] = 0; bunit[i] = bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) bvalue[j] = 0; } if(op == SEND) { CHECK_Mpi( MPI_Send(((char*)ptr)+idx, count[0], MPI_BYTE, proc, ARMCI_MPI_SERVER2CLIENT_TAG, comm) ); } else /* ( op == RECV) */ { CHECK_Mpi( MPI_Recv(((char*)ptr)+idx, count[0], MPI_BYTE, proc, ARMCI_MPI_CLIENT2SERVER_TAG, comm, &status) ); } } } void check_comm() { int result; assert(SERVER_CONTEXT); assert(ARMCI_COMM_WORLD != MPI_COMM_NULL); } /************************************************************************** * Platform specific server code as required by the ARMCI s/w layer. (BEGIN) */ /* establish connections with client (i.e compute) processes */ void armci_server_initial_connection() { armci_mpi2_server_init_twosided(); armci_mpi2_server_debug(0, "armci_server_initial_connection\n"); } /* close all open connections, called before terminating/aborting */ void armci_transport_cleanup() { #if 0 /* armci_transport_cleanup is called by all procs (clients and servers). Therefore, only in server case we need to finalize MPI before exit. */ if(ARMCI_COMM_WORLD != MPI_COMM_NULL) { armci_mpi2_server_debug(0, "Calling MPI_Finalize\n"); MPI_Finalize(); exit(EXIT_SUCCESS); /* server termination */ } #endif } static void armci_mpi_rcv_strided_data(request_header_t *msginfo, void *vdscr, int from) { int bytes; void *ptr; char *dscr; int stride_levels, *stride_arr, *count; bytes = msginfo->dscrlen; dscr = (char*)(msginfo + 1); *(void**)vdscr = (void *)dscr; ptr = *(void**)dscr; dscr += sizeof(void*); stride_levels = *(int*)dscr; dscr += sizeof(int); stride_arr = (int*)dscr; dscr += stride_levels*sizeof(int); count = (int*)dscr; dscr += (stride_levels+1)*sizeof(int); check_comm(); { armci_mpi_strided_s2c(RECV, ptr, stride_levels, stride_arr, count, from, ARMCI_COMM_WORLD); } } static void armci_mpi_rcv_vector_data(request_header_t *msginfo, void *vdscr, int proc) { armci_die("armci_mpi_rcv_vector_data(): Not yet implemented!", 0); } /* server receives request */ void armci_rcv_req (void *mesg, void *phdr, void *pdescr, void *pdata, int *buflen) { request_header_t *msginfo = NULL; int hdrlen = sizeof(request_header_t); int p=-1; int bytes; check_comm(); MPI_Status status; msginfo = (request_header_t*) MessageRcvBuffer; p = * (int *) mesg; CHECK_Mpi( MPI_Recv(MessageRcvBuffer, MSG_BUFLEN, MPI_BYTE, p, ARMCI_MPI_CLIENT2SERVER_TAG, ARMCI_COMM_WORLD, &status) ); * (void **) phdr = msginfo; if( !(p >= 0 && p < armci_nproc) ) armci_die("armci_rcv_req: request from invalid client", p); armci_mpi2_server_debug(armci_server_me, "armci_rcv_req: op=%d mesg=%p, phdr=%p " "pdata=%p, buflen=%p, p=%d\n", msginfo->operation, mesg, phdr, pdata, buflen, p, MSG_BUFLEN); #ifdef MPI_SPAWN_ZEROCOPY assert(0); #endif *buflen = MSG_BUFLEN - hdrlen; if (msginfo->operation == GET) { bytes = msginfo->dscrlen; } else { bytes = msginfo->bytes; if (bytes > *buflen) armci_die2("armci_rcv_req: message overflowing rcv buf", msginfo->bytes, *buflen); } if (msginfo->bytes) { * (void **) pdescr = msginfo + 1; * (void **) pdata = msginfo->dscrlen + (char *) (msginfo+1); *buflen -= msginfo->dscrlen; if (msginfo->operation != GET && msginfo->datalen) { *buflen -= msginfo->datalen; } } else { * (void**) pdata = msginfo + 1; * (void**) pdescr = NULL; } if (msginfo->datalen > 0 && msginfo->operation != GET) { if (msginfo->datalen > (MSG_BUFLEN - hdrlen - msginfo->dscrlen)) { armci_die2("armci_rcv_req:data overflowing buffer", msginfo->dscrlen, msginfo->datalen); } *buflen -= msginfo->datalen; } } /* server sends data back to client */ void armci_WriteToDirect (int to, request_header_t *msginfo, void *data) { armci_mpi2_server_debug(armci_server_me, "armci_WriteToDirect: " "to=%d, msginfo=%p, data=%p, bytes=%d\n", to, msginfo, data, msginfo->datalen); if( !(to >= 0 && to < armci_nproc) ) armci_die("armci_WriteToDirect: send request to invalid client", to); check_comm(); CHECK_Mpi( MPI_Send(data, msginfo->datalen, MPI_BYTE, to, ARMCI_MPI_SERVER2CLIENT_TAG, ARMCI_COMM_WORLD) ); } /*\ server sends strided data back to client \*/ void armci_WriteStridedToDirect(int to, request_header_t* msginfo, void *ptr, int strides, int stride_arr[], int count[]) { armci_mpi2_server_debug(armci_server_me, "armci_WriteStridedToDirect: " "to=%d, stride_levels=%d, bytes=%d\n", to, strides, msginfo->datalen); check_comm(); { armci_mpi_strided_s2c(SEND, ptr, strides, stride_arr, count, to, ARMCI_COMM_WORLD); } } void armci_call_data_server() { int p=-1; MPI_Status status; armci_mpi2_server_debug(0, "armci_call_data_server(): Server main loop\n"); int result; check_comm(); /* server main loop; wait for and service requests until QUIT requested */ int flag = 0; for(;;) { #if 1 while (!flag) { CHECK_Mpi( MPI_Iprobe(MPI_ANY_SOURCE, ARMCI_MPI_CLIENT2SERVER_TAG, ARMCI_COMM_WORLD, &flag, &status) ); } #else CHECK_Mpi( MPI_Probe(MPI_ANY_SOURCE, ARMCI_MPI_CLIENT2SERVER_TAG, ARMCI_COMM_WORLD, &status) ); #endif flag = 0; p = status.MPI_SOURCE; assert((p>= 0) && (p < armci_nproc)); armci_mpi2_server_debug(armci_server_me, "Processing message from client %d\n", p); armci_data_server(&p); } } /** * Platform specific server code ENDs here. **************************************************************************/ static void emulate_armci_init_clusinfo() { assert(0); } static void emulate_armci_allocate_locks(long *shm_info) { assert(0); } /* Abhinav Vishnu */ void armci_mpi2_server_init_twosided() { int namelen, version, subversion; char processor_name[MPI_MAX_PROCESSOR_NAME]; long shm_info[3]; MPI_Status status; assert(ARMCI_COMM_WORLD != MPI_COMM_NULL); CHECK_Mpi(MPI_Comm_rank(ARMCI_COMM_WORLD, &armci_server_me)); armci_nserver = armci_nclus; CHECK_Mpi(MPI_Get_processor_name(processor_name, &namelen)); CHECK_Mpi(MPI_Get_version(&version, &subversion)); } void armci_mpi2_server_init() { assert(0); } void armci_mpi2_server() { assert(0); } void armci_comm_dup_server(MPI_Comm * comm) { /* Do nothing */ } ga-5.9.2/armci/src/devices/mpi-spawn/000077500000000000000000000000001500715745200173435ustar00rootroot00000000000000ga-5.9.2/armci/src/devices/mpi-spawn/mpi2.h000066400000000000000000000026241500715745200203670ustar00rootroot00000000000000#ifndef MPI2_H #define MPI2_H #include #define MPI_SPAWN_DEBUG 0 #define MPI_SPAWN_ZEROCOPY /* enables zero-copy for large requests */ #ifdef MPI_SPAWN_ZEROCOPY # define MPI_USER_DEF_DATATYPE /* Enables MPI userdefined type for non-contig * data, if MPI_SPAWN_ZEROCOPY is enabled */ #endif /* uncomment MULTIPLE_BUFS macro definition to disable multiple buffers */ #define MULTIPLE_BUFS #define ARMCI_MPI_SPAWN_INIT_TAG 1000 #define ARMCI_MPI_SPAWN_TAG 2000 #define ARMCI_MPI_SPAWN_DATA_TAG 3000 #define ARMCI_MPI_SPAWN_VDATA_TAG 4000 /* In case of multiple buffers, we use tags from 2001 to 2999 (999 tags * total) to ensure flow control at the server side */ #define ARMCI_MPI_SPAWN_TAG_BEGIN 2001 #define ARMCI_MPI_SPAWN_TAG_END 2999 #define GET_SEND_BUFFER _armci_buf_get #define FREE_SEND_BUFFER _armci_buf_release #define COMPLETE_HANDLE _armci_buf_complete_nb_request #define TEST_HANDLE _armci_buf_test_nb_request #define SEND 0 #define RECV 1 extern void armci_mpi_strided(int op, void *ptr, int stride_levels, int stride_arr[], int count[], int proc, MPI_Comm comm); extern void armci_mpi_strided2(int op, void *ptr, int stride_levels, int stride_arr[], int count[], int proc, MPI_Comm comm); #endif /* MPI2_H */ ga-5.9.2/armci/src/devices/mpi-spawn/mpi2_client.c000066400000000000000000000407131500715745200217210ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** * MPI_SPAWN: ARMCI on top of MPI. * Manojkumar Krishnan */ #if HAVE_STDARG_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_STDLIB_H # include #endif #include "mpi.h" #include "mpi2.h" #include "armcip.h" #include "request.h" #include "armci_shmem.h" #include "locks.h" #define ARMCI_ROOT 0 /* root process */ /* Inter-communicators for communicating between clients and data servers */ MPI_Comm MPI_COMM_CLIENT2SERVER=MPI_COMM_NULL; static int armci_nserver=-1; static int *_armci_mpi_tag=NULL; extern char ***_armci_argv; extern int armci_get_shmem_info(char *addrp, int* shmid, long *shmoffset, size_t *shmsize); #if MPI_SPAWN_DEBUG void armci_mpi2_debug(int rank, const char *format, ...) { va_list arg; if(rank == armci_me) { va_start(arg, format); printf("%d: ", rank); vprintf(format, arg); va_end(arg); fflush(stdout); } } #else #define armci_mpi2_debug(x, ...) #endif #if MPI_SPAWN_DEBUG static inline int CHECK_Mpi (int status) { if(status != MPI_SUCCESS) { armci_mpi2_debug(armci_me, "MPI Check failed.\n"); armci_die("CHECK_Mpi failed.", 0); } } #else # define CHECK_Mpi(x) x #endif /************************************************************************** * Platform specific server code as required by the ARMCI s/w layer. (BEGIN) */ /* Create connections between clients and servers */ void armci_init_connections() { armci_mpi2_debug(0, "armci_init_connections\n"); _armci_buf_init(); /* CHECK: Is this correct ? */ } void armci_wait_for_server() { armci_mpi2_debug(0, "armci_wait_for_server: wait for server to quit\n"); if (armci_me == armci_master) { armci_serv_quit(); } } void armci_client_connect_to_servers() { armci_mpi2_debug(0, "armci_client_connect_to_servers\n"); } /* NOTE: armci_mpi_strided and armci_mpi_strided2 are the only 2 functions * that are common to client and server part */ void armci_mpi_strided(int op, void *ptr, int stride_levels, int stride_arr[], int count[], int proc, MPI_Comm comm) { int i, j; long idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int bvalue[MAX_STRIDE_LEVEL], bunit[MAX_STRIDE_LEVEL]; MPI_Status status; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) n1dim *= count[i]; /* calculate the destination indices */ bvalue[0] = 0; bvalue[1] = 0; bunit[0] = 1; bunit[1] = 1; for(i=2; i<=stride_levels; i++) { bvalue[i] = 0; bunit[i] = bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) bvalue[j] = 0; } if(op == SEND) { CHECK_Mpi( MPI_Send(((char*)ptr)+idx, count[0], MPI_BYTE, proc, ARMCI_MPI_SPAWN_DATA_TAG, comm) ); } else /* ( op == RECV) */ { CHECK_Mpi( MPI_Recv(((char*)ptr)+idx, count[0], MPI_BYTE, proc, ARMCI_MPI_SPAWN_DATA_TAG, comm, &status) ); } } } /* This is the only function that is common to client and server part */ void armci_mpi_strided2(int op, void *ptr, int stride_levels, int stride_arr[], int count[], int proc, MPI_Comm comm) { int i, stride=1; MPI_Status status; MPI_Datatype type[MAX_STRIDE_LEVEL]; if(stride_levels == 0) { armci_mpi_strided(op, ptr, stride_levels, stride_arr, count, proc, comm); return; } /* convert stided data desciption to MPI type */ type[0] = MPI_BYTE; for(i=1; i<=stride_levels; i++) { stride *= stride_arr[i-1]; CHECK_Mpi( MPI_Type_hvector(count[i], count[i-1], stride, type[i-1], &type[i]) ); } CHECK_Mpi( MPI_Type_commit(&type[stride_levels]) ); if(op == SEND) { CHECK_Mpi( MPI_Send(ptr, 1, type[stride_levels], proc, ARMCI_MPI_SPAWN_VDATA_TAG, comm) ); } else /* ( op == RECV) */ { CHECK_Mpi( MPI_Recv(ptr, 1, type[stride_levels], proc, ARMCI_MPI_SPAWN_VDATA_TAG, comm, &status) ); } } /*\ client sends request message to server \*/ int armci_send_req_msg (int proc, void *buf, int bytes) { int server = armci_clus_id(proc); armci_mpi2_debug(armci_me, "armci_send_req_msg(): proc=%d, server=%d, " "buf=%p, bytes=%d\n", proc, server, buf, bytes); if( !(server >= 0 && server < armci_nserver) ) armci_die("armci_send_req_msg: Invalid server.", 0); #ifdef MULTIPLE_BUFS /** * Sequentially ordered tags to ensure flow control at the server side. * For example, a put followed by get from a client should be processed in * ORDER at the server side. If we don't have the flow control, the server * might process the get request first instead of put (and thus violating * ARMCI's ordering semantics. */ ((request_header_t*)buf)->tag = _armci_mpi_tag[server]; CHECK_Mpi( MPI_Send(buf, bytes, MPI_BYTE, server, ARMCI_MPI_SPAWN_TAG, MPI_COMM_CLIENT2SERVER) ); _armci_mpi_tag[server]++; if(_armci_mpi_tag[server] > ARMCI_MPI_SPAWN_TAG_END) _armci_mpi_tag[server] = ARMCI_MPI_SPAWN_TAG_BEGIN; #else CHECK_Mpi( MPI_Send(buf, bytes, MPI_BYTE, server, ARMCI_MPI_SPAWN_TAG, MPI_COMM_CLIENT2SERVER) ); #endif armci_mpi2_debug(armci_me, "armci_send_req_msg(): send msg to server(%d), to" "fwd to client %d\n", server, proc); return 0; } /*\ client sends strided data + request to server \*/ int armci_send_req_msg_strided(int proc, request_header_t *msginfo,char *ptr, int strides, int stride_arr[], int count[]) { int server = armci_clus_id(proc); int bytes; armci_mpi2_debug(armci_me, "armci_send_req_msg_strided: proc=%d server=%d " "bytes=%d (op=%d)\n", proc, server, msginfo->datalen, msginfo->operation); THREAD_LOCK(armci_user_threads.net_lock); /* we write header + descriptor of strided data */ bytes = sizeof(request_header_t) + msginfo->dscrlen; armci_send_req_msg(proc, msginfo, bytes); #ifdef MPI_USER_DEF_DATATYPE if(strides>0) { armci_mpi_strided2(SEND, ptr, strides, stride_arr, count, server, MPI_COMM_CLIENT2SERVER); } else #endif { /* for larger blocks write directly thus avoiding memcopy */ armci_mpi_strided(SEND, ptr, strides, stride_arr, count, server, MPI_COMM_CLIENT2SERVER); } THREAD_UNLOCK(armci_user_threads.net_lock); armci_mpi2_debug(armci_me, "armci_send_req_msg_strided(): send msg to " "server(%d), to fwd to client %d\n", server, proc); return 0; } /*\ client receives data from server \*/ char *armci_ReadFromDirect (int proc, request_header_t *msginfo, int len) { int server = armci_clus_id(proc); MPI_Status status; armci_mpi2_debug(armci_me, "armci_ReadFromDirect: proc=%d, server=%d, " "msginfo=%p, bytes=%d (op=%d)\n", proc, server, msginfo, len, msginfo->operation); if( !(server >= 0 && server < armci_nserver) ) armci_die("armci_ReadFromDirect: Invalid server.", 0); CHECK_Mpi( MPI_Recv(msginfo + 1, len, MPI_BYTE, server, ARMCI_MPI_SPAWN_TAG, MPI_COMM_CLIENT2SERVER, &status) ); armci_mpi2_debug(armci_me, "recv msg from server(%d), fwd by client %d\n", server, proc); #if MPI_SPAWN_DEBUG { int count; MPI_Get_count(&status, MPI_BYTE, &count); if (count != len) { armci_mpi2_debug(armci_me, "armci_ReadFromDirect: got %d bytes, " "expected %d bytes\n", count, len); armci_die("armci_ReadFromDirect: MPI_Recv failed.", count); } } #endif return (char *) (msginfo+1); } /*\ client receives strided data from server \*/ void armci_ReadStridedFromDirect(int proc, request_header_t* msginfo, void *ptr, int strides, int stride_arr[], int count[]) { int server=armci_clus_id(proc); armci_mpi2_debug(armci_me, "armci_ReadStridedFromDirect: proc=%d " "stride_levels=%d, server=%d bytes=%d (op=%d)\n", proc, strides, server, msginfo->datalen, msginfo->operation); if( !(server >= 0 && server < armci_nserver) ) armci_die("armci_ReadStridedFromDirect: Invalid server.", 0); #ifdef MPI_USER_DEF_DATATYPE if(strides > 0) { armci_mpi_strided2(RECV, ptr, strides, stride_arr, count, server, MPI_COMM_CLIENT2SERVER); } else #endif { armci_mpi_strided(RECV, ptr, strides, stride_arr, count, server, MPI_COMM_CLIENT2SERVER); } } /** * Platform specific server code ENDs here. (END) **************************************************************************/ static void armci_gather_hostnames(char **hostname_arr) { int i, j, k, namelen, is_master; char hostname[MPI_MAX_PROCESSOR_NAME], *hostnames=NULL; int *master_arr=NULL; master_arr = (int*) malloc(armci_nproc * sizeof(int)); hostnames = (char*) malloc(armci_nproc * MPI_MAX_PROCESSOR_NAME * sizeof(char)); if(hostnames==NULL || master_arr==NULL) { armci_die("armci_gather_hostnames: malloc failed.", 0); } MPI_Get_processor_name(hostname, &namelen); CHECK_Mpi( MPI_Allgather(hostname, MPI_MAX_PROCESSOR_NAME, MPI_CHAR, hostnames, MPI_MAX_PROCESSOR_NAME, MPI_CHAR, ARMCI_COMM_WORLD) ); if(armci_me == armci_master) { is_master = 1; } else { is_master = 0; } CHECK_Mpi(MPI_Allgather(&is_master, 1, MPI_INT, master_arr, 1, MPI_INT, ARMCI_COMM_WORLD)); { /* get only the hostname of armci master processes */ for(i=0,j=0,k=0; i=armci_nserver) armci_die("armci_gather_hostnames: Invalid masters.",0); strncpy(hostname_arr[j++], &hostnames[k], MPI_MAX_PROCESSOR_NAME); } k += MPI_MAX_PROCESSOR_NAME; } } free(hostnames); free(master_arr); } void select_server_program(char *server_program, int num_procs) { strcpy(server_program, (*_armci_argv)[0]); return; } static void armci_mpi2_spawn() { int i; char server_program[100]; char **command_arr=NULL, **hostname_arr=NULL, **nid_arr=NULL; int *size_arr=NULL; MPI_Info *info_arr; /* we need to start 1 data server process on each node. So a total of "armci_nclus" data servers */ armci_nserver = armci_nclus; select_server_program(server_program, armci_nserver); armci_mpi2_debug(0, "armci_mpi2_init(): Spawning %d data server processes " "running %s\n", armci_nserver, server_program); /* allocate necessary data structures */ { command_arr = (char**) malloc(armci_nserver * sizeof(char*)); size_arr = (int*) malloc(armci_nserver * sizeof(int)); info_arr = (MPI_Info*) malloc(armci_nserver * sizeof(MPI_Info)); hostname_arr = (char**) malloc(armci_nserver * sizeof(char*)); for(i=0; i #endif #if HAVE_STDIO_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_STDLIB_H # include #endif #include "mpi.h" #include "armcip.h" #include "mpi2.h" #include "kr_malloc.h" #include "locks.h" /* Inter-communicators for communicating with clients */ MPI_Comm MPI_COMM_SERVER2CLIENT=MPI_COMM_NULL; static int armci_server_me=-1, armci_nserver=-1; static int armci_client_first=-1, armci_nclients=-1; extern Header *armci_get_shmem_ptr(int shmid, long shmoffset, size_t shmsize); /* ====================== MUltiple Buffers Code ======================== */ #ifdef MULTIPLE_BUFS #define MPI2_MAX_BUFS 10 typedef struct req_waitlist { int reqid; int p; int tag; struct req_waitlist *next; } req_waitlist_t; static double _mpi2_rcv_buf[MPI2_MAX_BUFS][MSG_BUFLEN_DBL]; static MPI_Request _mpi_request[MPI2_MAX_BUFS]; static int *_next_tag=NULL; static int _reqid_ready=0; static req_waitlist_t *_req_waitlist_head = NULL; static req_waitlist_t *_req_waitlist_tail = NULL; /* increment the tag value to maintain flowcontrol */ #define INCR_TAG(p) \ { \ if(++_next_tag[p] > ARMCI_MPI_SPAWN_TAG_END) \ _next_tag[p] = ARMCI_MPI_SPAWN_TAG_BEGIN; \ } static int wlist_get_req(int *p, int *tag, int *reqid) { req_waitlist_t *itr, *prev=NULL; for(itr=_req_waitlist_head; itr != NULL; itr=itr->next) { if(itr->tag == _next_tag[itr->p]) { /* mark the request id as ready to be processed, and update the * tag order and waitlist */ *p = itr->p; *tag = itr->tag; *reqid = itr->reqid; INCR_TAG(*p); /* remove this request from the waiting list */ if(itr==_req_waitlist_head) _req_waitlist_head = itr->next; if(itr==_req_waitlist_tail) _req_waitlist_tail = prev; if(prev != NULL) prev->next = itr->next; free(itr); return 1; } prev = itr; } return 0; } static void wlist_add_req(int reqid, int p, int tag) { req_waitlist_t *node = (req_waitlist_t *) malloc(sizeof(req_waitlist_t)); node->reqid = reqid; node->p = p; node->tag = tag; node->next = NULL; if(_req_waitlist_head == NULL) { _req_waitlist_head = node; _req_waitlist_tail = node; return; } /* append the new request at the end of the list */ _req_waitlist_tail->next = node; _req_waitlist_tail = node; } #endif /* ==================== END: Multiple Buffers Code ====================== */ #if MPI_SPAWN_DEBUG void armci_mpi2_server_debug(int rank, const char *format, ...) { va_list arg; if(rank == armci_server_me) { va_start(arg, format); printf("**** Server %d: ", armci_server_me); vprintf(format, arg); va_end(arg); fflush(stdout); } } #else # define armci_mpi2_server_debug(x, ...) #endif #if MPI_SPAWN_DEBUG static inline int CHECK_Mpi (int status) { if(status != MPI_SUCCESS) { armci_mpi2_server_debug(armci_me, "MPI Check failed.\n"); armci_die("CHECK_Mpi failed.", 0); } } #else # define CHECK_Mpi(x) x #endif /************************************************************************** * Platform specific server code as required by the ARMCI s/w layer. (BEGIN) */ /* establish connections with client (i.e compute) processes */ void armci_server_initial_connection() { armci_mpi2_server_debug(0, "armci_server_initial_connection\n"); } /* close all open connections, called before terminating/aborting */ void armci_transport_cleanup() { /* armci_transport_cleanup is called by all procs (clients and servers). Therefore, only in server case we need to finalize MPI before exit. */ if(MPI_COMM_SERVER2CLIENT != MPI_COMM_NULL) { armci_mpi2_server_debug(0, "Calling MPI_Finalize\n"); MPI_Finalize(); exit(EXIT_SUCCESS); /* server termination */ } } static void armci_mpi_rcv_strided_data(request_header_t *msginfo, void *vdscr, int from) { int bytes; void *ptr; char *dscr; int stride_levels, *stride_arr, *count; bytes = msginfo->dscrlen; dscr = (char*)(msginfo + 1); *(void**)vdscr = (void *)dscr; ptr = *(void**)dscr; dscr += sizeof(void*); stride_levels = *(int*)dscr; dscr += sizeof(int); stride_arr = (int*)dscr; dscr += stride_levels*sizeof(int); count = (int*)dscr; dscr += (stride_levels+1)*sizeof(int); #ifdef MPI_USER_DEF_DATATYPE if(stride_levels>0) { armci_mpi_strided2(RECV, ptr, stride_levels, stride_arr, count, from, MPI_COMM_SERVER2CLIENT); } else #endif { armci_mpi_strided(RECV, ptr, stride_levels, stride_arr, count, from, MPI_COMM_SERVER2CLIENT); } } static void armci_mpi_rcv_vector_data(request_header_t *msginfo, void *vdscr, int proc) { armci_die("armci_mpi_rcv_vector_data(): Not yet implemented!", 0); } /* server receives request */ void armci_rcv_req (void *mesg, void *phdr, void *pdescr, void *pdata, int *buflen) { request_header_t *msginfo = NULL; int hdrlen = sizeof(request_header_t); int p=-1; int bytes; #if !defined(MULTIPLE_BUFS) MPI_Status status; msginfo = (request_header_t*) MessageRcvBuffer; p = * (int *) mesg; CHECK_Mpi( MPI_Recv(MessageRcvBuffer, MSG_BUFLEN, MPI_BYTE, p, ARMCI_MPI_SPAWN_TAG, MPI_COMM_SERVER2CLIENT, &status) ); #else int reqid = _reqid_ready;;/*get request id that is ready to be processed */ msginfo = (request_header_t*) _mpi2_rcv_buf[reqid]; p = * (int *) mesg; if(p != msginfo->from) armci_die("armci_rcv_req: invalid client", p); #endif * (void **) phdr = msginfo; if( !(p >= 0 && p < armci_nproc) ) armci_die("armci_rcv_req: request from invalid client", p); armci_mpi2_server_debug(armci_server_me, "armci_rcv_req: op=%d mesg=%p, phdr=%p " "pdata=%p, buflen=%p, p=%d\n", msginfo->operation, mesg, phdr, pdata, buflen, p, MSG_BUFLEN); #ifdef MPI_SPAWN_ZEROCOPY if(msginfo->operation==PUT && msginfo->datalen==0) { if(msginfo->format==STRIDED) { armci_mpi_rcv_strided_data(msginfo, pdescr, p); } if(msginfo->format==VECTOR) { armci_mpi_rcv_vector_data(msginfo, pdescr, p); } return; } #endif *buflen = MSG_BUFLEN - hdrlen; if (msginfo->operation == GET) { bytes = msginfo->dscrlen; } else { bytes = msginfo->bytes; if (bytes > *buflen) armci_die2("armci_rcv_req: message overflowing rcv buf", msginfo->bytes, *buflen); } #if MPI_SPAWN_DEBUG && !defined(MPI_SPAWN_ZEROCOPY) && 0 { int count; MPI_Get_count(&status, MPI_BYTE, &count); if (count != (bytes + hdrlen)) { armci_mpi2_server_debug(armci_server_me, "armci_rcv_req: " "got %d bytes, expected %d bytes\n", count, bytes + hdrlen); printf("%d: armci_rcv_req: got %d bytes, expected %d bytes (%d)\n", armci_me, count, bytes + hdrlen, msginfo->datalen); armci_die("armci_rcv_req: count check failed.\n", 0); } } #endif if (msginfo->bytes) { * (void **) pdescr = msginfo + 1; * (void **) pdata = msginfo->dscrlen + (char *) (msginfo+1); *buflen -= msginfo->dscrlen; if (msginfo->operation != GET && msginfo->datalen) { *buflen -= msginfo->datalen; } } else { * (void**) pdata = msginfo + 1; * (void**) pdescr = NULL; } if (msginfo->datalen > 0 && msginfo->operation != GET) { if (msginfo->datalen > (MSG_BUFLEN - hdrlen - msginfo->dscrlen)) { armci_die2("armci_rcv_req:data overflowing buffer", msginfo->dscrlen, msginfo->datalen); } *buflen -= msginfo->datalen; } } /* server sends data back to client */ void armci_WriteToDirect (int to, request_header_t *msginfo, void *data) { armci_mpi2_server_debug(armci_server_me, "armci_WriteToDirect: " "to=%d, msginfo=%p, data=%p, bytes=%d\n", to, msginfo, data, msginfo->datalen); if( !(to >= 0 && to < armci_nproc) ) armci_die("armci_WriteToDirect: send request to invalid client", to); CHECK_Mpi( MPI_Send(data, msginfo->datalen, MPI_BYTE, to, ARMCI_MPI_SPAWN_TAG, MPI_COMM_SERVER2CLIENT) ); } /*\ server sends strided data back to client \*/ void armci_WriteStridedToDirect(int to, request_header_t* msginfo, void *ptr, int strides, int stride_arr[], int count[]) { armci_mpi2_server_debug(armci_server_me, "armci_WriteStridedToDirect: " "to=%d, stride_levels=%d, bytes=%d\n", to, strides, msginfo->datalen); #ifdef MPI_USER_DEF_DATATYPE if(strides>0) { armci_mpi_strided2(SEND, ptr, strides, stride_arr, count, to, MPI_COMM_SERVER2CLIENT); } else #endif { armci_mpi_strided(SEND, ptr, strides, stride_arr, count, to, MPI_COMM_SERVER2CLIENT); } } void armci_call_data_server() { int p=-1; MPI_Status status; armci_mpi2_server_debug(0, "armci_call_data_server(): Server main loop\n"); #if !defined(MULTIPLE_BUFS) /* server main loop; wait for and service requests until QUIT requested */ for(;;) { CHECK_Mpi( MPI_Probe(MPI_ANY_SOURCE, ARMCI_MPI_SPAWN_TAG,MPI_COMM_SERVER2CLIENT, &status) ); p = status.MPI_SOURCE; armci_mpi2_server_debug(armci_server_me, "Processing message from client %d\n", p); armci_data_server(&p); } #else int i, tag, reqid, do_waitlist=0; /* server multiple bufs setup */ _req_waitlist_head = NULL; _req_waitlist_tail = NULL; /* Initialize "next tag" array, which manages flow control */ if( (_next_tag = (int*) malloc(armci_nproc*sizeof(int)) ) == NULL) armci_die("mpi2_server: _next_tag malloc failed", 0); for(i=0; itag; /* check if it is in or out of order request */ if(tag == _next_tag[p]) { INCR_TAG(p); } else { /* out of order req - enforce ordering by waitlisting this req */ wlist_add_req(reqid, p, tag); continue; } } /* mark the request id that is ready to processed */ _reqid_ready = reqid; /* server process the incoming (or waitlisted) request */ armci_data_server(&p); /* After completing the request (which also frees a buffer), server * posts a receive using this buffer */ CHECK_Mpi( MPI_Irecv(_mpi2_rcv_buf[reqid], MSG_BUFLEN, MPI_BYTE, MPI_ANY_SOURCE, ARMCI_MPI_SPAWN_TAG, MPI_COMM_SERVER2CLIENT, &_mpi_request[reqid]) ); } #endif } /** * Platform specific server code ENDs here. **************************************************************************/ static void emulate_armci_init_clusinfo() { int psize; MPI_Comm_remote_size(MPI_COMM_SERVER2CLIENT, &psize); /* server id (i.e. server's armci_me) is derived from node master's id. Similar to armci_create_server_process() to set SERVER_CONTEXT */ armci_me = SOFFSET - armci_client_first; armci_nproc = psize; armci_usr_tid = THREAD_ID_SELF(); /*remember the main user thread id */ armci_master = armci_client_first; /* ***** emulate armci_init_clusinfo() ***** */ armci_clus_me = armci_server_me; armci_nclus = armci_nserver; armci_clus_first = armci_clus_info[armci_clus_me].master; armci_clus_last = (armci_clus_first + armci_clus_info[armci_clus_me].nslave - 1); if(armci_clus_first != armci_client_first || armci_nclients != armci_clus_info[armci_clus_me].nslave) { armci_mpi2_server_debug(armci_server_me, "armci_clus_first=%d, armci_clus_last=%d\n", armci_clus_first, armci_clus_last); armci_die("mpi2_server: armci_clus_info is incorrect.", 0); } } static void emulate_armci_allocate_locks(long *shm_info) { int shmid = (int) shm_info[0]; long shmoffset = shm_info[1]; size_t shmsize = (size_t) shm_info[2]; _armci_int_mutexes = (PAD_LOCK_T*) armci_get_shmem_ptr(shmid, shmoffset, shmsize); } void armci_mpi2_server_init() { int namelen, version, subversion; char processor_name[MPI_MAX_PROCESSOR_NAME]; long shm_info[3]; MPI_Status status; MPI_Comm_rank(ARMCI_COMM_WORLD, &armci_server_me); MPI_Comm_size(ARMCI_COMM_WORLD, &armci_nserver); MPI_Get_processor_name(processor_name, &namelen); MPI_Get_version(&version, &subversion); armci_mpi2_server_debug(armci_server_me, "I'm %d of %d SERVERS running on %s (MPI %d.%d)\n", armci_server_me, armci_nserver, processor_name, version, subversion); /* get parent's groupinfo */ MPI_Comm_get_parent(&MPI_COMM_SERVER2CLIENT); if (MPI_COMM_SERVER2CLIENT == MPI_COMM_NULL) { armci_die("mpi2_server: Invalid spawn. No parent process found.\n",0); } /* receive my clients info */ { int msg[3]; MPI_Recv(msg, 3, MPI_INT, MPI_ANY_SOURCE, ARMCI_MPI_SPAWN_INIT_TAG, MPI_COMM_SERVER2CLIENT, &status); if(msg[0]-ARMCI_MPI_SPAWN_INIT_TAG != armci_server_me) { armci_die("mpi2_server: Recv failed", msg[0]); } armci_client_first = msg[1]; armci_nclients = msg[2]; armci_mpi2_server_debug(armci_server_me, "My clients are [%d-%d]\n", armci_client_first, armci_client_first+armci_nclients-1); } /********************************************************************** * Emulate PARMCI_Init(). * Spawned Data server processes emulate PARMCI_Init() to complete the * ARMCI Initalization process similar to clients */ armci_clus_info =(armci_clus_t*)malloc(armci_nserver*sizeof(armci_clus_t)); if(armci_clus_info == NULL) { armci_die("mpi2_server: armci_clus_info malloc failed", 0); } /* receive and emulate clus info, lock info */ MPI_Recv(armci_clus_info, armci_nserver*sizeof(armci_clus_t), MPI_BYTE, armci_client_first, ARMCI_MPI_SPAWN_INIT_TAG, MPI_COMM_SERVER2CLIENT, &status); MPI_Recv(shm_info, 3, MPI_LONG, armci_client_first, ARMCI_MPI_SPAWN_INIT_TAG, MPI_COMM_SERVER2CLIENT, &status); /* server setup clusinfo&locks, exactly as this node's armci master */ emulate_armci_init_clusinfo(); /* armci_init_clusinfo() in PARMCI_Init */ emulate_armci_allocate_locks(shm_info); /* armci_allocate_locks() */ /* Fence data structures should be initialized by server too. see * armci_generic_rmw (called by armci_server_rmw) */ armci_init_fence(); /** * End of PARMCI_Init() emulation. * *******************************************************************/ MPI_Barrier(ARMCI_COMM_WORLD); } void armci_mpi2_server() { armci_mpi2_server_init(); armci_server_code(NULL); /* Should never come here */ armci_die("mpi2_server: server died in an unexpected manner", 0); } ga-5.9.2/armci/src/devices/openib/000077500000000000000000000000001500715745200167045ustar00rootroot00000000000000ga-5.9.2/armci/src/devices/openib/armci-vapi.h000066400000000000000000000152041500715745200211070ustar00rootroot00000000000000/*$Id: armci-vapi.h,v 1.21.2.1 2007-03-06 19:50:24 vinod Exp $ */ #ifndef _VAPI_H #define _VAPI_H #ifdef OPENIB #include #endif #define DSCRID_NBDSCR 10000 #define MAX_RDMA_SIZE (8388608) #define DEFAULT_ADDR_LEN (8) /* format length of hcalid/qp_num.*/ #define DEFAULT_PORT (1) /*for vapi*/ #define DEFAULT_MTU (MTU1024) #define DEFAULT_PSN (0) #define DEFAULT_PKEY_IX (0) #define DEFAULT_P_KEY (0x0) #define DEFAULT_MIN_RNR_TIMER (5) #define DEFAULT_SERVICE_LEVEL (0) #define DEFAULT_TIME_OUT (5) #define DEFAULT_STATIC_RATE (2) #define DEFAULT_SRC_PATH_BITS (0) #define DEFAULT_RETRY_COUNT (1) #define DEFAULT_RNR_RETRY (1) #define DEFAULT_R_KEY (0x0) #define DEFAULT_L_KEY (0x0) #define DEFAULT_MAX_WQE 2048/*(1023)*/ typedef struct { void *data_ptr; /* pointer where the data should go */ long ack; /* header ack */ void *ack_ptr; /* pointer where the data should go */ #if defined(PEND_BUFS) unsigned int imm_msg:1; unsigned int data_len:31; #endif } msg_tag_t; typedef struct { #ifdef OPENIB struct ibv_send_wr sdscr; struct ibv_sge ssg_entry; struct ibv_recv_wr rdscr; struct ibv_sge rsg_entry; #endif } armci_vapi_field_t; typedef struct { #ifdef OPENIB uint32_t rkey; uint32_t lkey; struct ibv_mr *memhndl; #endif }armci_vapi_memhndl_t; extern char * armci_vapi_client_mem_alloc(int); typedef struct { int tag; int issg; #ifdef OPENIB struct ibv_send_wr sdescr; struct ibv_recv_wr rdescr; struct ibv_sge sg_entry[56]; /*ff:this has to be malloced*/ #endif int numofsends; int numofrecvs; int myindex; } sr_descr_t; typedef struct { int tag; int issg; #ifdef OPENIB struct ibv_recv_wr descr; struct ibv_send_wr sg_entry[56]; /*ff:this has to be malloced*/ #endif int numofrecvs; int myindex; } rdescr_t; #if defined(PEND_BUFS) #ifdef OPENIB /* typedef struct { */ /* struct ibv_recv_wr dscr; */ /* struct ibv_sge sg_entry; */ /* } IMMBUF_NW_T; */ /* typedef struct { */ /* struct ibv_sge sg_entry; */ /* struct ibv_recv_wr rdscr; */ /* struct ibv_send_wr sdscr; */ /* } PENDBUF_NW_T; */ #define IMMBUF_NW_T \ struct ibv_recv_wr dscr; \ struct ibv_sge sg_entry; \ int send_pending; #define PENDBUF_NW_T \ struct ibv_sge sg_entry; \ struct ibv_recv_wr rdscr; \ struct ibv_send_wr sdscr; #define IS_IMM_MSG(msginfo) ((msginfo).tag.imm_msg) void armci_complete_immbuf(void *vbuf); void armci_complete_pendbuf(void *buf); /*Note that start_put and start_get cannot report completion from within. They have to just return and report completion later. */ void armci_pbuf_start_put(void *src, void *dst, int bytes, int proc, int bufid); void armci_pbuf_start_get(void *msg_info, void *src, void *dst, int bytes, int proc, int bufid); #else #error "PEND_BUFS only implemented for OPENIB" #endif #endif void armci_client_nbcall_complete(sr_descr_t *,int,int); void armci_vapi_set_mark_buf_send_complete(int); #define ARMCI_MEMHDL_T armci_vapi_memhndl_t #define REGIONS_REQUIRE_MEMHDL #define PIPE_BUFSIZE__ (4096) #define PIPE_MIN_BUFSIZE 1024 #define PIPE_MEDIUM_BUFSIZE (2*1024) #define VBUF_DLEN 4*64*1023 #define MSG_BUFLEN_DBL ((VBUF_DLEN)>>3) #if defined(PEND_BUFS) #define IMM_BUF_NUM_DEFAULT 4 #define IMM_BUF_LEN_DEFAULT 2048 #define PENDING_BUF_NUM_DEFAULT 20 #define PENDING_BUF_LEN_DEFAULT (VBUF_DLEN) #endif #ifdef PIPE_BUFSIZE # define STRIDED_GET_BUFLEN_DBL 31*1024 # define STRIDED_GET_BUFLEN (STRIDED_GET_BUFLEN_DBL<<3) # define MAX_BUFLEN (STRIDED_GET_BUFLEN+EXTRA_MSG_BUFLEN) #else # define MAX_BUFLEN (MSG_BUFLEN+EXTRA_MSG_BUFLEN) #endif #define BALANCE_BUFFERS #ifdef BALANCE_BUFFERS # define BALANCE_FACTOR 1.6 /* # define BALANCE_FACTOR 2.0 */ # define BALANCE_BUFSIZE 25000 #endif #define BUF_EXTRA_FIELD_T armci_vapi_field_t #define GET_SEND_BUFFER _armci_buf_get #define FREE_SEND_BUFFER _armci_buf_release #ifdef OPENIB #define INIT_SEND_BUF(_field,_snd,_rcv) _snd=1;_rcv=1;memset(&((_field).sdscr),0,sizeof(struct ibv_send_wr));(_field).sdscr.wr_id=avail+1;armci_vapi_set_mark_buf_send_complete(avail+1) #endif #define BUF_ALLOCATE armci_vapi_client_mem_alloc #define CLEAR_SEND_BUF_FIELD(_field,_snd,_rcv,_to,_op) armci_vapi_complete_buf((armci_vapi_field_t *)(&(_field)),(_snd),(_rcv),(_to),(_op));_snd=0;_rcv=0;_to=0 #define TEST_SEND_BUF_FIELD(_field,_snd,_rcv,_to,_op,_pret) armci_vapi_test_buf((armci_vapi_field_t *)(&(_field)),(_snd),(_rcv),(_to),(_op),(_pret)) #define CLIENT_BUF_BYPASS 1 #define _armci_bypass 1 #define COMPLETE_HANDLE _armci_buf_complete_nb_request #if defined(ALLOW_PIN) # define NB_CMPL_T sr_descr_t* # define ARMCI_NB_WAIT(_cntr) if(_cntr)if(nb_handle->tag==(_cntr)->tag)\ armci_client_nbcall_complete(_cntr,nb_handle->tag,nb_handle->op); # define CLEAR_HNDL_FIELD(_x) _x=NULL #endif /* #define DIRECT_PUT_MIN_SIZE 8192 */ #define DIRECT_PUT_MIN_SIZE 0 #define LONG_GET_THRESHOLD 2147483648 #define LONG_GET_THRESHOLD_STRIDED 2147483648 #ifndef ARMCI_STAMP #error "ARMCI_STAMP used to define ARMCI_VAPI_COMPLETE not found!" #define ARMCI_VAPI_COMPLETE 1088451863 #else #define ARMCI_VAPI_COMPLETE ARMCI_STAMP #endif #define ARMCI_POST_SCATTER 1000000001 #define ARMCI_VAPI_CLEAR 0 #ifdef OPENIB /* #define VAPI_SGGET_MIN_COLUMN 2147483648 */ #define VAPI_SGPUT_MIN_COLUMN 2147483648 #define VAPI_SGGET_MIN_COLUMN 10 /* #define VAPI_SGPUT_MIN_COLUMN 720 */ #endif #define DSCRID_SCATTERCLIENT 70000 #define DSCRID_SCATTERCLIENT_END 70000+9999 #define MAX_PENDING 32 #define HAS_RDMA_GET #if defined(OPENIB) #if 1 #define PUT_NO_SRV_COPY /*server rdma-s from to client buffers (in buffers.c) to remote memory (bypassing servers intermediate buffers) when possible*/ #define GET_NO_SRV_COPY /*server rdma-s from remote memory to client buffers (in buffers.c) (bypassing server intermediate buffers) when possible*/ int no_srv_copy_nsegs_ulimit();/*max #contig segs for which we avoid server copy. Too many segments might overflow a queue*/ #endif #define GET_STRIDED_COPY_PIPELINED /*client copies data from buffers to user memory in segments rather than waiting for all the data*/ #define GET_STRIDED_COPY_PIPELINED_SIZE (1*1024) /*size of each segment*/ #endif #endif /* _VAPI_CONST_H */ extern void armci_wait_ack(char *buffer); extern void armci_complete_multi_sglist_sends(int proc); extern void armci_client_direct_send(int p,void *src_buf, void *dst_buf, int len,void** contextptr,int nbtag,ARMCI_MEMHDL_T *lochdl,ARMCI_MEMHDL_T *remhdl); ga-5.9.2/armci/src/devices/openib/cbuf.c000066400000000000000000000230501500715745200177670ustar00rootroot00000000000000/* * Copyright (C) 1999-2001 The Regents of the University of California * (through E.O. Lawrence Berkeley National Laboratory), subject to * approval by the U.S. Department of Energy. * * Use of this software is under license. The license agreement is included * in the file MVICH_LICENSE.TXT. * * Developed at Berkeley Lab as part of MVICH. * * Authors: Bill Saphir * Michael Welcome */ /* Copyright (c) 2002-2008, The Ohio State University. All rights * reserved. * * This file is part of the MVAPICH software package developed by the * team members of The Ohio State University's Network-Based Computing * Laboratory (NBCL), headed by Professor Dhabaleswar K. (DK) Panda. * * For detailed copyright and licensing information, please refer to the * copyright file COPYRIGHT_MVAPICH in the top level MPICH directory. * */ #define _XOPEN_SOURCE 600 #include "cbuf.h" #include /* * cbufs * * cbufs provide system buffers for VMPICH. They are analogous to mbufs * in BSD networking. * The primary motivation for cbufs is that implementing MPI on VIA * seems to requiring pre-posting a number of fixed-sized buffers. * These buffers must be registered (pinned). Life is easier if * they are all registered at once so there is only one memory * handle. We manage a fixed-size pool of cbufs that are * allocated and pinned when a progam starts up. We manage * the free cbuf list as a singly linked list. * * Two different ways to manage the free list as a singly-linked list. * 1. head and tail pointers. Add to tail, remove from head. * 2. only head pointer, treat as a stack. * * #1 Eliminates contention between adding to list and removing from list * Lock-free possible? * * #2 Has slightly less overhead when there is no contention, and is more * likely to produce a cbuf that is already in cache. * * Currently anticipate that most access near-term will be single-threaded, * so go with head only. (#2) */ /* head of list of allocated cbuf regions */ static cbuf_region *cbuf_region_head = NULL; /* * free_cbuf_head is the head of the free list */ static cbuf *free_cbuf_head = NULL; static int cbuf_n_allocated = 0; static long num_free_cbuf = 0; static long num_cbuf_get = 0; static long num_cbuf_free = 0; static pthread_spinlock_t cbuf_lock; int viadev_cbuf_max = -1; int viadev_cbuf_total_size = (2 * 1024); int viadev_cbuf_secondary_pool_size = 128; void init_cbuf_lock() { pthread_spin_init(&cbuf_lock, 0); } static void lock_cbuf() { pthread_spin_lock(&cbuf_lock); return; } static void unlock_cbuf() { pthread_spin_unlock(&cbuf_lock); return; } void dump_cbuf_region(cbuf_region * r) { } void dump_cbuf_regions() { cbuf_region *r = cbuf_region_head; while (r) { dump_cbuf_region(r); r = r->next; } } void deallocate_cbufs() { cbuf_region *r = cbuf_region_head; lock_cbuf(); while (r) { if (r->mem_handle != NULL) { /* free cbufs add it later */ } r = r->next; } unlock_cbuf(); } static void allocate_cbuf_region(int ncbufs) { struct cbuf_region *reg; void *mem; void *cbuf_dma_buffer; int i; cbuf *cur; int alignment_cbuf = 64; int alignment_dma; alignment_dma = getpagesize(); if (free_cbuf_head != NULL) { } if (ncbufs <= 0) { } /* are we limiting cbuf allocation? If so, make sure * we dont alloc more than allowed */ reg = (struct cbuf_region *) malloc(sizeof(struct cbuf_region)); if (NULL == reg) { } if(posix_memalign((void **) &mem, alignment_cbuf, ncbufs * sizeof(cbuf))) { } /* ALLOCATE THE DMA BUFFER */ if(posix_memalign((void **) &cbuf_dma_buffer, alignment_dma, ncbufs * viadev_cbuf_total_size)) { } memset(mem, 0, ncbufs * sizeof(cbuf)); memset(cbuf_dma_buffer, 0, ncbufs * viadev_cbuf_total_size); cbuf_n_allocated += ncbufs; num_free_cbuf += ncbufs; reg->malloc_start = mem; reg->malloc_buf_start = cbuf_dma_buffer; reg->malloc_end = (void *) ((char *) mem + ncbufs * sizeof(cbuf)); reg->malloc_buf_end = (void *) ((char *) cbuf_dma_buffer + ncbufs * viadev_cbuf_total_size); reg->count = ncbufs; free_cbuf_head = (cbuf *) ((aint_t) mem); reg->cbuf_head = free_cbuf_head; reg->mem_handle = armci_register_memory(cbuf_dma_buffer, ncbufs * viadev_cbuf_total_size); if (reg->mem_handle == NULL) { } /* init the free list */ for (i = 0; i < ncbufs - 1; i++) { cur = free_cbuf_head + i; cur->desc.next = free_cbuf_head + i + 1; cur->region = reg; #ifdef ADAPTIVE_RDMA_FAST_PATH #else cur->buffer = (unsigned char *) ((char *)(cbuf_dma_buffer) + (i * viadev_cbuf_total_size)); #endif } /* last one needs to be set to NULL */ cur = free_cbuf_head + ncbufs - 1; cur->desc.next = NULL; cur->region = reg; #ifdef ADAPTIVE_RDMA_FAST_PATH #else cur->buffer = (unsigned char *) ((char *)cbuf_dma_buffer + ((ncbufs - 1) * viadev_cbuf_total_size)); #endif /* thread region list */ reg->next = cbuf_region_head; cbuf_region_head = reg; } void allocate_cbufs(int ncbufs) { /* this function is only called by the init routines. * cache the nic handle and ptag for later cbuf_region allocations */ /* now allocate the first cbuf region */ allocate_cbuf_region(ncbufs); } /* * Get a cbuf off the free list */ cbuf *get_cbuf(void) { cbuf *v; lock_cbuf(); /* * It will often be possible for higher layers to recover * when no cbuf is available, but waiting for more descriptors * to complete. For now, just abort. */ if (NULL == free_cbuf_head) { allocate_cbuf_region(viadev_cbuf_secondary_pool_size); if (NULL == free_cbuf_head) { } } v = free_cbuf_head; num_free_cbuf--; num_cbuf_get++; /* this correctly handles removing from single entry free list */ free_cbuf_head = free_cbuf_head->desc.next; #ifdef ADAPTIVE_RDMA_FAST_PATH /* need to change this to RPUT_CBUF_FLAG or RGET_CBUF_FLAG later * if we are doing rput */ v->padding = NORMAL_CBUF_FLAG; #endif /* this is probably not the right place to initialize shandle to NULL. * Do it here for now because it will make sure it is always initialized. * Otherwise we would need to very carefully add the initialization in * a dozen other places, and probably miss one. */ v->shandle = NULL; v->ref_count = 0; v->len = 0; v->grank = -1; /* Make sure it is not inadvertantly used anywhere */ unlock_cbuf(); return (v); } /* * Put a cbuf back on the free list */ void release_cbuf(cbuf * v) { lock_cbuf(); /* note this correctly handles appending to empty free list */ assert(v != free_cbuf_head); v->desc.next = free_cbuf_head; #ifdef ADAPTIVE_RDMA_FAST_PATH #endif free_cbuf_head = v; num_free_cbuf++; num_cbuf_free++; unlock_cbuf(); } /* * fill in cbuf descriptor with all necessary info */ void cbuf_init_send(cbuf * v, unsigned long len) { v->desc.u.sr.next = NULL; v->desc.u.sr.send_flags = IBV_SEND_SIGNALED; v->desc.u.sr.opcode = IBV_WR_SEND; v->desc.u.sr.wr_id = (aint_t) v; v->desc.u.sr.num_sge = 1; v->desc.u.sr.sg_list = &(v->desc.sg_entry); v->desc.sg_entry.addr = (uintptr_t) v->buffer; v->desc.sg_entry.length = len; v->desc.sg_entry.lkey = v->region->mem_handle->lkey; } void cbuf_init_recv(cbuf * v, unsigned long len) { v->desc.u.rr.next = NULL; v->desc.u.rr.wr_id = (aint_t) v; v->desc.u.rr.num_sge = 1; v->desc.u.rr.sg_list = &(v->desc.sg_entry); v->desc.sg_entry.addr = (uintptr_t) v->buffer; v->desc.sg_entry.length = len; v->desc.sg_entry.lkey = v->region->mem_handle->lkey; #ifdef ADAPTIVE_RDMA_FAST_PATH v->padding = NORMAL_CBUF_FLAG; #endif } void cbuf_init_sendrecv(cbuf * v, unsigned long len) { } void cbuf_init_rput(cbuf * v, void *local_address, uint32_t lkey, void *remote_address, uint32_t rkey, int len) { v->desc.u.sr.next = NULL; v->desc.u.sr.send_flags = IBV_SEND_SIGNALED; v->desc.u.sr.opcode = IBV_WR_RDMA_WRITE; v->desc.u.sr.wr_id = (aint_t) v; v->desc.u.sr.num_sge = 1; v->desc.u.sr.sg_list = &(v->desc.sg_entry); v->desc.sg_entry.length = len; v->desc.sg_entry.lkey = lkey; v->desc.sg_entry.addr = (uintptr_t) local_address; v->desc.u.sr.wr.rdma.remote_addr = (uintptr_t) remote_address; v->desc.u.sr.wr.rdma.rkey = rkey; #ifdef ADAPTIVE_RDMA_FAST_PATH v->padding = RPUT_CBUF_FLAG; #endif } void cbuf_init_rget(cbuf * v, void *local_address, uint32_t lkey, void *remote_address, uint32_t rkey, int len) { v->desc.u.sr.next = NULL; v->desc.u.sr.send_flags = IBV_SEND_SIGNALED; v->desc.u.sr.opcode = IBV_WR_RDMA_READ; v->desc.u.sr.wr_id = (aint_t) v; v->desc.u.sr.num_sge = 1; v->desc.u.sr.sg_list = &(v->desc.sg_entry); v->desc.sg_entry.length = len; v->desc.sg_entry.lkey = lkey; v->desc.sg_entry.addr = (uintptr_t) local_address; v->desc.u.sr.wr.rdma.remote_addr = (uintptr_t) remote_address; v->desc.u.sr.wr.rdma.rkey = rkey; #ifdef ADAPTIVE_RDMA_FAST_PATH v->padding = RGET_CBUF_FLAG; #endif } /* * print out cbuf contents for debugging */ void dump_cbuf(char *msg, cbuf * v) { } #ifdef ADAPTIVE_RDMA_FAST_PATH #endif ga-5.9.2/armci/src/devices/openib/cbuf.h000066400000000000000000000122721500715745200200000ustar00rootroot00000000000000/* * Copyright (C) 1999-2001 The Regents of the University of California * (through E.O. Lawrence Berkeley National Laboratory), subject to * approval by the U.S. Department of Energy. * * Use of this software is under license. The license agreement is included * in the file MVICH_LICENSE.TXT. * * Developed at Berkeley Lab as part of MVICH. * * Authors: Bill Saphir * Michael Welcome */ /* Copyright (c) 2002-2008, The Ohio State University. All rights * reserved. * * This file is part of the MVAPICH software package developed by the * team members of The Ohio State University's Network-Based Computing * Laboratory (NBCL), headed by Professor Dhabaleswar K. (DK) Panda. * * For detailed copyright and licensing information, please refer to the * copyright file COPYRIGHT_MVAPICH in the top level MPICH directory. * */ #ifndef _CBUF_H #define _CBUF_H #include #include #include #include #include #define CBUF_FLAG_TYPE uint32_t #if (defined(RDMA_FAST_PATH) || defined(ADAPTIVE_RDMA_FAST_PATH)) #define CBUF_BUFFER_SIZE (viadev_cbuf_total_size - \ 2*sizeof(CBUF_FLAG_TYPE)) #else #define CBUF_BUFFER_SIZE (viadev_cbuf_total_size) #endif /* * brief justification for cbuf format: * descriptor must be aligned (64 bytes). * cbuf size must be multiple of this alignment to allow contiguous allocation * descriptor and buffer should be contiguous to allow via implementations that * optimize contiguous descriptor/data (? how likely ?) * need to be able to store send handle in cbuf so that we can mark sends * complete when communication completes. don't want to store * it in packet header because we don't always need to send over the network. * don't want to store at beginning or between desc and buffer (see above) so * store at end. */ #define QP_CON_REQ 1 #define QP_CON_ACK 2 #define QP_CON_BREAK_FROM_CLIENT 3 #define QP_CON_BREAK_FROM_SERVER 4 typedef struct { uint32_t src_rank; uint16_t lid; uint32_t qpnum; int msg_type; } armci_ud_rank; struct ibv_descriptor { union { struct ibv_recv_wr rr; struct ibv_send_wr sr; } u; struct ibv_sge sg_entry; void *next; }; typedef struct ibv_descriptor IBV_DESCRIPTOR; typedef struct cbuf { #if (defined(RDMA_FAST_PATH) || defined(ADAPTIVE_RDMA_FAST_PATH)) CBUF_FLAG_TYPE *head_flag; #endif unsigned char *buffer; #if (defined(RDMA_FAST_PATH) || defined(ADAPTIVE_RDMA_FAST_PATH)) int padding; #endif IBV_DESCRIPTOR desc; /* NULL shandle means not send or not complete. Non-null * means pointer to send handle that is now complete. Used * by viadev_process_send */ void *shandle; struct cbuf_region *region; int grank; uint16_t bytes_remaining; uint16_t len; unsigned char *data_start; uint16_t ref_count; } cbuf; /* one for head and one for tail */ #define CBUF_FAST_RDMA_EXTRA_BYTES (2*sizeof(CBUF_FLAG_TYPE)) #define FAST_RDMA_ALT_TAG 0x8000 #define FAST_RDMA_SIZE_MASK 0x7fff /* * Vbufs are allocated in blocks and threaded on a single free list. * * These data structures record information on all the cbuf * regions that have been allocated. They can be used for * error checking and to un-register and deallocate the regions * at program termination. * */ typedef struct cbuf_region { struct ibv_mr *mem_handle; /* memory handle for entire region */ void *malloc_start; /* used to free region later */ void *malloc_end; /* to bracket mem region */ void *malloc_buf_start; /* used to free DMA region later */ void *malloc_buf_end; /* bracket DMA region */ int count; /* number of cbufs in region */ struct cbuf *cbuf_head; /* first cbuf in region */ struct cbuf_region *next; /* thread cbuf regions */ } cbuf_region; /* ack. needed here after cbuf is defined. need to clean up header files * and dependencies. */ typedef unsigned long aint_t; extern int viadev_cbuf_max; extern int viadev_cbuf_total_size; extern int viadev_cbuf_secondary_pool_size; void dump_cbuf_regions(void); void dump_cbuf_region(cbuf_region * r); void allocate_cbufs(int ncbufs); void deallocate_cbufs(void); cbuf *get_cbuf(void); void release_cbuf(cbuf * v); void cbuf_init_send(cbuf * v, unsigned long len); void cbuf_init_recv(cbuf * v, unsigned long len); void cbuf_init_sendrecv(cbuf * v, unsigned long len); void cbuf_init_rput(cbuf * v, void *local_address, uint32_t local_memhandle, void *remote_address, uint32_t remote_memhandle_rkey, int nbytes); void cbuf_init_rget(cbuf * v, void *local_address, uint32_t local_memhandle, void *remote_address, uint32_t remote_memhandle_rkey, int nbytes); void init_cbuf_lock(); void dump_cbuf(char *msg, cbuf * v); struct ibv_mr * armci_register_memory(void *, int); /* * Macros for working with cbufs */ #define CBUF_BUFFER_START(v) (v->buffer) #define CBUF_DATA_SIZE(type) (CBUF_BUFFER_SIZE - sizeof(type)) #endif /* _CBUF_H */ ga-5.9.2/armci/src/devices/openib/openib.c000066400000000000000000004626251500715745200203430ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: openib.c,v 1.4.2.9 2007-10-18 06:08:03 d3h325 Exp $ * * File organized as follows */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #if HAVE_STDIO_H # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_STRING_H # include #endif #include #include "cbuf.h" #include "armcip.h" #include "copy.h" #include "request.h" #include "armci-vapi.h" #include "iterator.h" #define DEBUG_INIT 0 #define DEBUG_FINALIZE 0 #define DEBUG_SERVER 0 #define DEBUG_CLN 0 #define TIME_INIT 0 # define VAPIDEV_NAME "InfiniHost0" # define INVAL_HNDL 0xFFFFFFFF #define RNR_TIMER 12 /*Debug macros used to tune what is being tested -- mostly openib calls*/ #define DBG_INIT 1 #define DBG_POLL 1 #define DBG_ALL 1 #define QP_INACTIVE 5 #define QP_REQ_SENT 2 #define QP_ACK_RCVD 3 #define QP_ACTIVE 4 u_int32_t armci_max_num_sg_ent; u_int32_t armci_max_qp_ous_swr; u_int32_t armci_max_qp_ous_rwr; typedef struct { struct ibv_qp *qp; uint32_t sqpnum; /*we need to exchng qp nums,arr for that*/ uint16_t lid; uint16_t state; void *next; } armci_connect_t; armci_connect_t *CLN_con, *SRV_con; static uint32_t *SRV_rqpnums, *CLN_rqpnums; /*relevant rqp num arrs, to connect to svr and client*/ static uint32_t *CLN_rqpnumtmpbuf=NULL; /*temporary buf used during connection setup*/ /*\ * datastrucure for infinihost NIC \*/ typedef struct { uint16_t *lid_arr; /*we need to exchange lids, arr for that*/ struct ibv_context *handle; /*device context/handle*/ int maxtransfersize; struct ibv_device_attr attr; /*device properties*/ struct ibv_port_attr hca_port; /*mostly for getting lid*/ uint8_t active_port; struct ibv_pd *ptag; /*protection tag*/ const char *vendor; struct ibv_cq *scq; /*send completion queue*/ struct ibv_cq *rcq; /*recv completion queue*/ struct ibv_comp_channel *sch; /*send completion channel*/ struct ibv_comp_channel *rch; /*recv completion channel*/ void *scq_cntx; /*send context for completion queue*/ void *rcq_cntx; /*recv context for completion queue*/ int scv; /*send completion vector*/ int rcv; /*recv completion vector*/ } vapi_nic_t; typedef struct { armci_vapi_memhndl_t *prem_handle; /*address server to store memory handle*/ armci_vapi_memhndl_t handle; }ack_t; armci_vapi_memhndl_t *CLN_handle; armci_vapi_memhndl_t serv_memhandle, client_memhandle; armci_vapi_memhndl_t *handle_array; armci_vapi_memhndl_t *pinned_handle; static vapi_nic_t nic_arr[3]; static vapi_nic_t *SRV_nic= nic_arr; static vapi_nic_t *CLN_nic= nic_arr+1; static int armci_server_terminating; #define NONE -1 static int armci_ack_proc=NONE; static int armci_vapi_server_ready; static int armci_vapi_server_stage1=0; static int armci_vapi_client_stage1=0; static int armci_vapi_server_stage2=0; static int armci_vapi_client_ready; int _s=-1,_c=-1; static int armci_vapi_max_inline_size=-1; #define CLIENT_STAMP 101 #define SERV_STAMP 99 #define MAX_PROC_INLINE_SIZE 2048 static char * client_tail; static char * serv_tail; static ack_t *SRV_ack; #if defined(PEND_BUFS) typedef immbuf_t vapibuf_t; typedef pendbuf_t vapibuf_pend_t; #else typedef struct { struct ibv_recv_wr dscr; struct ibv_sge sg_entry; char buf[CBUF_DLEN]; } vapibuf_t; #endif typedef struct { struct ibv_send_wr snd_dscr; struct ibv_sge ssg_entry; struct ibv_recv_wr rcv_dscr; struct ibv_sge rsg_entry; char buf[VBUF_DLEN]; } vapibuf_ext_t; typedef struct { struct ibv_send_wr rmw_dscr; struct ibv_sge rmw_entry; } vapirmw_t; unsigned int armci_use_odcm = 0; unsigned int armci_use_lazy_break = 0; unsigned int armci_use_apm = 0; unsigned int armci_use_apm_test = 0; unsigned int armci_use_srq = 0; unsigned int armci_use_snft = 0; unsigned int armci_use_affinity = 0; unsigned int armci_srq_size = 4096; pthread_t armci_async_thread[4]; void async_thread_hca_events(void *ctx); void* async_thread_ud_events(void *ctx); void init_apm_lock(void); #if 0 static struct ibv_srq *create_srq(vapi_nic_t *nic); #endif void setup_ud_channel(void); void process_recv_completion_from_server(armci_ud_rank *h, cbuf *v); void process_recv_completion_from_client(armci_ud_rank *h, cbuf *v); struct ibv_srq *CLN_srq_hndl; struct ibv_srq *SRV_srq_hndl; void post_recv(void); struct Remote_Buf { char **buf; uint32_t *qp_num; uint16_t *lid; uint32_t *rkey; }; struct HCA { struct ibv_device *ib_dev; struct ibv_context *context; struct ibv_pd *pd; struct ibv_cq *cq; struct ibv_srq *srq_hndl; struct ibv_comp_channel *comp_channel; }; struct RC_Conn { struct ibv_qp **qp; uint16_t *lid; int *status; uint32_t *qp_num; struct ibv_ah **ud_ah; }; struct Remote_Buf rbuf; struct RC_Conn conn; struct HCA hca; void handle_network_fault(struct ibv_wc *pdscr); int process_recv_completion_from_client_flag; int total_active_conn_to_server, total_active_conn_to_client, total_breaks; void check_state_of_ib_connection(int a, int b); static vapibuf_t **serv_buf_arr; #if !defined(PEND_BUFS) /*These are typically used as spare buffers for communication. Since we do not wait on completion anymore, we need to ensure things work fine when these have in-flight messages. Disabled for now.*/ static vapibuf_t *spare_serv_buf, *spare_serv_bufptr; static vapibuf_ext_t *serv_buf; #endif static vapirmw_t rmw[64]; static int *flag_arr; /* flag indicates its receiving scatter data */ #define SERV 2 #define CLN 1 #define MAX_DESCR 2 typedef struct { int avail; struct ibv_qp *qp; struct ibv_recv_wr *descr; } descr_pool_t; static int* _gtmparr; static void* test_ptr; static int test_stride_arr[1]; static int test_count[2]; static int test_stride_levels; char *MessageRcvBuffer; extern void armci_util_wait_int(volatile int *,int,int); void armci_send_data_to_client(int proc, void *buf,int bytes,void *dbuf); void armci_server_register_region(void *,long,ARMCI_MEMHDL_T *); static descr_pool_t serv_descr_pool = {MAX_DESCR,NULL,NULL}; static descr_pool_t client_descr_pool = {MAX_DESCR,NULL,NULL}; /**Buffer (long[1] used to set msginfo->tag.ack_ptr in client-side. See usage in SERVER_SEND_ACK macro*/ static long *ack_buf; #define GET_DATA_PTR(buf) (sizeof(request_header_t) + (char*)buf) #define BUF_TO_SDESCR(buf) ((struct ibv_send_wr *)(&((armci_vapi_field_t *)((char *)(buf) - sizeof(armci_vapi_field_t)))->sdscr)) #define BUF_TO_RDESCR(buf) ((struct ibv_recv_wr *)(&((armci_vapi_field_t *)((char *)(buf) - sizeof(armci_vapi_field_t)))->rdscr)) #define BUF_TO_SSGLST(buf) ((struct ibv_sge *)(&((armci_vapi_field_t *)((char *)(buf) - sizeof(armci_vapi_field_t)))->ssg_entry)) #define BUF_TO_RSGLST(buf) ((struct ibv_sge *)(&((armci_vapi_field_t *)((char *)(buf) - sizeof(armci_vapi_field_t)))->rsg_entry)) #define BUF_TO_ECBUF(buf) (vapibuf_ext_t*)(((char*)buf) - (sizeof(struct ibv_send_wr)+sizeof(struct ibv_recv_wr)+2*sizeof(struct ibv_sge))) #define SERVER_SEND_ACK(p) do { \ assert(*ack_buf == ARMCI_STAMP); \ assert((p)>=0); \ armci_send_data_to_client((p),ack_buf, \ sizeof(long),msginfo->tag.ack_ptr); \ } while(0) /* #define SERVER_SEND_ACK(p) {assert(serv_buf!=NULL);assert(msginfo->from==(p));*((long *)serv_buf->buf)=ARMCI_STAMP;armci_send_data_to_client((p),serv_buf->buf,sizeof(long),msginfo->tag.ack_ptr);} */ #define SERVER_SEND_DATA(_SS_proc,_SS_src,_SS_dst,_SS_size) {armci_send_data_to_client(_SS_proc,_SS_src,_SS_size,_SS_dst);} #define SERVER_GET_DATA(_SG_proc,_SG_src,_SG_dst,_SG_size) {armci_get_data_from_client(_SG_proc,_SG_src,_SG_size,_SG_dst);} /*\ descriptors will have unique ID's for the wait on descriptor routine to * complete a descriptor and know where it came from \*/ #define NUMOFBUFFERS (MAX_BUFS+MAX_SMALL_BUFS) #define DSCRID_FROMBUFS 1 #define DSCRID_FROMBUFS_END (DSCRID_FROMBUFS+NUMOFBUFFERS) #define DSCRID_NBDSCR 10000 #define DSCRID_NBDSCR_END (10000+MAX_PENDING) #define DSCRID_SCATGAT 20000 #define DSCRID_SCATGAT_END 20000+MAX_PENDING #define DSCRID_RMW 30000 #define DSCRID_RMW_END 30000+9999 #if defined(PEND_BUFS) #define DSCRID_PENDBUF (40000) #define DSCRID_PENDBUF_END (DSCRID_PENDBUF + 2*PENDING_BUF_NUM+1) #define DSCRID_IMMBUF_RECV (200000) #define DSCRID_IMMBUF_RECV_END (600000) #define DSCRID_IMMBUF_RESP (600000) #define DSCRID_IMMBUF_RESP_END (1000000) #endif extern double MPI_Wtime(); static double inittime0=0,inittime1=0,inittime2=0,inittime4=0; static int mark_buf_send_complete[NUMOFBUFFERS+1]; static sr_descr_t armci_vapi_client_nbsdscr_array[MAX_PENDING]; static sr_descr_t armci_vapi_client_nbrdscr_array[MAX_PENDING]; static sr_descr_t armci_vapi_serv_nbsdscr_array[MAX_PENDING]; static sr_descr_t armci_vapi_serv_nbrdscr_array[MAX_PENDING]; void armci_server_transport_cleanup(); /********************FUNCTIONS TO CHECK OPENIB RETURN STATUS*******************/ void armci_check_status(int debug, int rc,char *msg) { dassertp(debug,rc==0,("%d: %s, rc=%d\n",armci_me,msg,rc)); /* if(debug)printf("%d:%s, rc = %d\n", armci_me,msg, rc); */ /* if(rc!=0)armci_die(msg,rc); */ } void armci_vapi_check_return(int debug, int ret, const char *ss) { } void armci_vapi_print_dscr_info(struct ibv_send_wr *sr, struct ibv_recv_wr *rr) { int i; if(rr){ printf("\n%d:print_dscr rr id=%ld sg_lst_len=%d", armci_me, rr->wr_id, rr->num_sge); for (i = 0; i < rr->num_sge; i++) { printf("\n\t:sg_entry=%d addr=%lu len=%d", i, rr->sg_list[i].addr, rr->sg_list[i].length); } fflush(stdout); } if(sr){ printf("\n%d:print_dscr sr id=%lu opcode=%d sg_lst_len=%d", armci_me, sr->wr_id, sr->opcode, sr->num_sge); for (i = 0; i < sr->num_sge; i++) { printf("\n\t:sg_entry=%d addr=%lu len=%d", i, sr->sg_list[i].addr, sr->sg_list[i].length); } fflush(stdout); } } /*****************END FUNCTIONS TO CHECK VAPI RETURN STATUS********************/ void armci_recv_complete(struct ibv_recv_wr *rcv_dscr, char *from, int numofrecvs) { int rc=0; struct ibv_wc pdscr1; struct ibv_wc *pdscr = &pdscr1; sr_descr_t *rdscr_arr; vapi_nic_t *nic; int debug,i; if(SERVER_CONTEXT){ rdscr_arr = armci_vapi_serv_nbrdscr_array; nic=CLN_nic; debug = DEBUG_SERVER; } else{ rdscr_arr = armci_vapi_client_nbrdscr_array; nic=SRV_nic; debug = DEBUG_CLN; } if(debug){ printf("\n%d%s:recv_complete called from %s id=%ld\n",armci_me, ((SERVER_CONTEXT)?"(s)":" "),from,rcv_dscr->wr_id);fflush(stdout); } for(i=0;ircq, 1, pdscr); } dassertp(DBG_POLL|DBG_ALL,rc>=0, ("%d: rc=%d id=%d status=%d (%d/%d)\n", armci_me,rc,pdscr->wr_id,pdscr->status,i,numofrecvs)); dassert1(1,pdscr->status==IBV_WC_SUCCESS,pdscr->status); if(debug){ if(pdscr->wr_id >= DSCRID_SCATGAT && pdscr->wr_id < DSCRID_SCATGAT_END) printf("\n%d:recv from %s complete id=%lu num=%d",armci_me, from,pdscr->wr_id,rdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].numofrecvs); } if(pdscr->wr_id >= DSCRID_SCATGAT && pdscr->wr_id < DSCRID_SCATGAT_END){ rdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].numofrecvs--; if(rdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].numofrecvs==0) rdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].tag=0; } else if(pdscr->wr_id == (DSCRID_SCATGAT + MAX_PENDING)){ /*this was from a blocking call, do nothing*/ continue; } else { armci_die("\nclient should be posting only one kind of recv",armci_me); } rc = 0; }while(pdscr->wr_id!=rcv_dscr->wr_id); rc = 0; } } void armci_vapi_set_mark_buf_send_complete(int id) { mark_buf_send_complete[id]=0; } void armci_send_complete(struct ibv_send_wr *snd_dscr, char *from,int numoftimes) { int rc=0; struct ibv_wc pdscr1; struct ibv_wc *pdscr = &pdscr1; sr_descr_t *sdscr_arr; vapi_nic_t *nic; int debug,i; pdscr1.status = IBV_WC_SUCCESS; /* bzero(&pdscr1, sizeof(pdscr1)); */ /* printf("%d: Waiting for send with wr_id=%d to complete\n", armci_me, snd_dscr->wr_id); */ /* fflush(stdout); */ if(SERVER_CONTEXT){ sdscr_arr = armci_vapi_serv_nbsdscr_array; nic=CLN_nic; debug = DEBUG_SERVER; } else{ sdscr_arr = armci_vapi_client_nbsdscr_array; nic=SRV_nic; debug = DEBUG_CLN; } if(debug) { printf("\n%d%s:send_complete called from %s id=%ld nt=%d\n",armci_me, ((SERVER_CONTEXT)?"(s)":" "),from,snd_dscr->wr_id,numoftimes); fflush(stdout); } for(i=0;ircq,1,pdscr); else #endif rc = ibv_poll_cq(nic->scq,1, pdscr); } dassertp(DBG_POLL|DBG_ALL,rc>=0, ("%d:rc=%d status=%d id=%d (%d/%d)",armci_me, rc,pdscr->status,(int)pdscr->wr_id,i,numoftimes)); dassert1(1,pdscr->status==IBV_WC_SUCCESS,pdscr->status); /* printf("%d: Obtained completion of wr_id=%d\n", armci_me, pdscr->wr_id); */ /* fflush(stdout); */ if(SERVER_CONTEXT){ if(debug)printf("%d:completed id %lu i=%d\n",armci_me,pdscr->wr_id,i); if(pdscr->wr_id >=DSCRID_SCATGAT && pdscr->wr_id < DSCRID_SCATGAT_END){ sdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].numofsends--; if(sdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].numofsends==0) sdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].tag=0; } else if(pdscr->wr_id >=armci_nproc && pdscr->wr_id < 2*armci_nproc){ /*its coming from send_data_to_client just return*/ } #if defined(PEND_BUFS) else if(pdscr->wr_id >= DSCRID_IMMBUF_RESP && pdscr->wr_id>DSCRID_IMMBUF_RESP_END) { /*send from server to client completed*/ } #endif else armci_die("server send complete got weird id",pdscr->wr_id); } else{ if(debug)printf("%d:completed id %lu i=%d\n",armci_me,pdscr->wr_id,i); if(pdscr->wr_id >=DSCRID_FROMBUFS && pdscr->wr_id < DSCRID_FROMBUFS_END) { /* printf("%d: marking send buffer %d as complete\n", armci_me, pdscr->wr_id);*/ mark_buf_send_complete[pdscr->wr_id]=1; } else if(pdscr->wr_id >=DSCRID_NBDSCR && pdscr->wr_id < DSCRID_NBDSCR_END){ sdscr_arr[pdscr->wr_id-DSCRID_NBDSCR].numofsends--; if(sdscr_arr[pdscr->wr_id-DSCRID_NBDSCR].numofsends==0) sdscr_arr[pdscr->wr_id-DSCRID_NBDSCR].tag=0; } else if(pdscr->wr_id >=DSCRID_SCATGAT && pdscr->wr_id < DSCRID_SCATGAT_END){ sdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].numofsends--; if(sdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].numofsends==0) sdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].tag=0; } else if(pdscr->wr_id == (DSCRID_SCATGAT + MAX_PENDING)){ /* printf("%d: completed a blocking scatgat descriptor\n", armci_me); */ /*this was from a blocking call, do nothing*/ continue; } else armci_die("client send complete got weird id",pdscr->wr_id); } rc = 0; }while(pdscr->wr_id!=snd_dscr->wr_id); rc = 0; } } void armci_dscrlist_recv_complete(int tag, char* from,sr_descr_t *dscr) { int i,nr; sr_descr_t *retdscr,*rdscr_arr; if(dscr == NULL){ if(SERVER_CONTEXT) rdscr_arr = armci_vapi_serv_nbrdscr_array; else rdscr_arr = armci_vapi_client_nbrdscr_array; for(i=0;inumofrecvs; armci_recv_complete(&(retdscr->rdescr),"(s)list_send_complete",nr); } void armci_dscrlist_send_complete(int tag,char *from, sr_descr_t *dscr) { int i,ns; sr_descr_t *retdscr,*sdscr_arr; if(dscr==NULL){ if(SERVER_CONTEXT) sdscr_arr = armci_vapi_serv_nbsdscr_array; else sdscr_arr = armci_vapi_client_nbsdscr_array; for(i=0;inumofsends; armci_send_complete(&(retdscr->sdescr),"dscrlist_send_complete",ns); } void armci_client_nbcall_complete(sr_descr_t *dscr, int tag, int op) { if(tag != dscr->tag) return; THREAD_LOCK(armci_user_threads.net_lock); if(op == GET){ if(dscr->issg){ if(dscr->numofrecvs>0) armci_dscrlist_recv_complete(tag,"armci_client_nbcall_complete recv", dscr); } else{ if(dscr->numofsends>0) armci_dscrlist_send_complete(tag,"armci_client_nbcall_complete send", dscr); } } if(op == PUT){ if(dscr->numofsends>0) armci_dscrlist_send_complete(tag,"armci_client_nbcall_complete send", dscr); } THREAD_UNLOCK(armci_user_threads.net_lock); } static int cur_serv_pend_descr; static int cur_client_pend_descr; sr_descr_t *armci_vapi_get_next_rdescr(int nbtag,int sg) { static int serverthreadavail=-1; /*client thread can't touch this*/ static int clientthreadavail=-1; /*server thread can't touch this*/ int avail,newavail; sr_descr_t *retdscr,*rdscr_arr; if(SERVER_CONTEXT){ rdscr_arr = armci_vapi_serv_nbrdscr_array; avail = serverthreadavail; /*printf("\n%d:serv thread avail=%d",armci_me,serverthreadavail);*/ } else{ rdscr_arr = armci_vapi_client_nbrdscr_array; avail = clientthreadavail; } if(avail==-1){ int i; for(i=0;irdescr,0,sizeof(struct ibv_recv_wr)); if(sg) retdscr->rdescr.wr_id = DSCRID_SCATGAT + avail; else{ retdscr->rdescr.wr_id = DSCRID_NBDSCR + avail; retdscr->numofrecvs=1; } newavail = (avail+1)%MAX_PENDING; if(SERVER_CONTEXT){ cur_serv_pend_descr = avail; serverthreadavail=newavail; } else{ cur_client_pend_descr = avail; clientthreadavail=newavail; } return(retdscr); } sr_descr_t *armci_vapi_get_next_sdescr(int nbtag,int sg) { static int serverthreadavail=-1; /*client thread can't touch this*/ static int clientthreadavail=-1; /*server thread can't touch this*/ int avail,newavail; sr_descr_t *retdscr,*sdscr_arr; if(SERVER_CONTEXT){ sdscr_arr = armci_vapi_serv_nbsdscr_array; avail = serverthreadavail; } else{ sdscr_arr = armci_vapi_client_nbsdscr_array; avail = clientthreadavail; } if(avail==-1){ /*first call*/ int i; for(i=0;isdescr,0,sizeof(struct ibv_recv_wr)); if(sg) retdscr->sdescr.wr_id = DSCRID_SCATGAT + avail; else{ retdscr->sdescr.wr_id = DSCRID_NBDSCR + avail; retdscr->numofsends=1; } newavail = (avail+1)%MAX_PENDING; if(SERVER_CONTEXT){ cur_serv_pend_descr = avail; serverthreadavail=newavail; } else{ cur_client_pend_descr = avail; clientthreadavail=newavail; } return(retdscr); } void armci_wait_for_server() { armci_server_terminating = 1; } /* ibv_create_qp does not use separate structure to return properties, seems it is all inside ibv_qp */ static void armci_create_qp(vapi_nic_t *nic, struct ibv_qp **qp) { struct ibv_qp_init_attr initattr; bzero(&initattr, sizeof(struct ibv_qp_init_attr)); *qp = NULL; initattr.cap.max_send_wr = armci_max_qp_ous_swr; initattr.cap.max_recv_wr = armci_max_qp_ous_rwr; initattr.cap.max_recv_sge = armci_max_num_sg_ent; initattr.cap.max_send_sge = armci_max_num_sg_ent; #if defined(PEND_BUFS) if(nic==CLN_nic) { initattr.send_cq = nic->rcq; initattr.recv_cq = nic->rcq; } else #endif { initattr.send_cq = nic->scq; initattr.recv_cq = nic->rcq; } initattr.qp_type = IBV_QPT_RC; *qp = ibv_create_qp(nic->ptag, &initattr); dassert(1,*qp!=NULL); /* The value of inline size should be dependent on number of processes * */ if (armci_nproc >= MAX_PROC_INLINE_SIZE) { armci_vapi_max_inline_size = -1; } else { armci_vapi_max_inline_size = initattr.cap.max_inline_data; } } int armci_openib_sl; int armci_openib_server_poll; void armci_openib_env_init() { char *value; if ((value = getenv("ARMCI_OPENIB_USE_SL")) != NULL){ armci_openib_sl = atoi(value); } else { armci_openib_sl = 0; } /* Don't enable server polling by default */ if ((value = getenv("ARMCI_OPENIB_SERVER_POLL")) != NULL){ armci_openib_server_poll = atoi(value); } else { armci_openib_server_poll = 0; } } static void armci_init_nic(vapi_nic_t *nic, int scq_entries, int rcq_entries) { int rc, ndevs, i; struct ibv_device **devs=NULL; if (nic == SRV_nic) { /* Initialize OpenIB runtime variables only once*/ armci_openib_env_init(); } bzero(nic,sizeof(vapi_nic_t)); nic->lid_arr = (uint16_t *)calloc(armci_nproc,sizeof(uint16_t)); dassert(1,nic->lid_arr!=NULL); devs = ibv_get_device_list(&ndevs); char *runtime_devname; int device_found = 0, device_id = 0; runtime_devname = getenv("ARMCI_OPENIB_DEVICE"); if (runtime_devname) { for (i = 0; i < ndevs; i++) { if (!strncmp(ibv_get_device_name(devs[i]), runtime_devname, 32)) { device_found = 1; device_id = i; break; } } } else { device_id = 0; device_found = 1; } assert(device_found); nic->handle = ibv_open_device(devs[device_id]); nic->maxtransfersize = MAX_RDMA_SIZE; nic->vendor = ibv_get_device_name(devs[device_id]); rc = ibv_query_device(nic->handle, &nic->attr); assert(0 == rc); int down_port_count_check = 0; for (i = 1; i <= 2; i++) { rc = ibv_query_port(nic->handle, (uint8_t)i, &nic->hca_port); assert(0 == rc); if (IBV_PORT_ACTIVE == nic->hca_port.state) { nic->active_port = i; break; } else { down_port_count_check++; } } /* Assert that the number of inactive ports is not equal to the number * of down ports on any adapter */ assert(down_port_count_check != 2); /*save the lid for doing a global exchange later */ nic->lid_arr[armci_me] = nic->hca_port.lid; /*allocate tag (protection domain) */ nic->ptag = ibv_alloc_pd(nic->handle); /* properties of scq and rcq required for the cq number, this also needs * to be globally exchanged */ nic->scv = 1; nic->rcv = 2; nic->scq = nic->rcq = NULL; if(scq_entries) { nic->sch = ibv_create_comp_channel(nic->handle); nic->scq = ibv_create_cq(nic->handle, 16000, nic->scq_cntx,nic->sch, 0); } if(rcq_entries) { nic->rch = ibv_create_comp_channel(nic->handle); nic->rcq = ibv_create_cq(nic->handle, 32768, nic->rcq_cntx,nic->rch, 0); } ibv_free_device_list(devs); armci_max_num_sg_ent = 29; armci_max_qp_ous_swr = 100; armci_max_qp_ous_rwr = 50; char *value; if ((value = getenv("ARMCI_USE_ODCM")) != NULL){ armci_use_odcm = atoi(value); } else { armci_use_odcm = 0; } armci_use_lazy_break = 0; if(armci_max_qp_ous_rwr + armci_max_qp_ous_swr>nic->attr.max_qp_wr){ armci_max_qp_ous_swr = nic->attr.max_qp_wr/16; armci_max_qp_ous_rwr = nic->attr.max_qp_wr - armci_max_qp_ous_swr; } if(armci_max_num_sg_ent >= nic->attr.max_sge){ armci_max_num_sg_ent = nic->attr.max_sge - 1; } } void armci_setaffinity(char *cpu_mapping) { long N_CPUs_online; cpu_set_t affinity_mask; unsigned long affinity_mask_len = sizeof(affinity_mask); char *tp; char *cp; char tp_str[8]; int cpu, i, j; if (!armci_use_affinity) return; /*Get number of CPU on machine */ if ((N_CPUs_online = sysconf(_SC_NPROCESSORS_ONLN)) < 1) { perror("sysconf"); } if (cpu_mapping) { tp = cpu_mapping; j = 0; while (*tp != '\0') { i = 0; cp = tp; while (*cp != '\0' && *cp != ',' && *cp != ':') { cp++; i++; } strncpy(tp_str, tp, i); tp_str[i] = '\0'; cpu = atoi(tp_str); if (j == armci_me - armci_master) { CPU_ZERO(&affinity_mask); CPU_SET(cpu, &affinity_mask); if (sched_setaffinity(0, affinity_mask_len, &affinity_mask)<0 ) { perror("sched_setaffinity"); } break; } tp = cp + 1; j++; } free(cpu_mapping); } else { CPU_ZERO(&affinity_mask); CPU_SET(((armci_me) - armci_master) %N_CPUs_online, &affinity_mask); if (sched_setaffinity(0,affinity_mask_len,&affinity_mask)<0 ) { perror("sched_setaffinity"); } } } /****************MEMORY ALLOCATION REGISTRATION DEREGISTRATION****************/ static char * serv_malloc_buf_base; #if ARMCI_ENABLE_GPC_CALLS extern gpc_buf_t *gpc_req; #endif void armci_server_alloc_bufs() { int bytes, total, extra =sizeof(struct ibv_recv_wr)*MAX_DESCR+SIXTYFOUR; int mhsize = armci_nproc*sizeof(armci_vapi_memhndl_t); /* ack */ char *tmp, *tmp0; int i; #if defined(PEND_BUFS) int clients = (IMM_BUF_NUM+1)*armci_nproc; #else int clients = armci_nproc; #endif /* allocate memory for the recv buffers-must be alligned on 64byte bnd */ /* note we add extra one to repost it for the client we are received req */ bytes = (clients+1)*sizeof(vapibuf_t)+sizeof(vapibuf_ext_t) + extra+ mhsize #if ARMCI_ENABLE_GPC_CALLS + MAX_GPC_REQ * sizeof(gpc_buf_t) #endif #if defined(PEND_BUFS) + (clients+1)*IMM_BUF_LEN + PENDING_BUF_NUM*(sizeof(vapibuf_pend_t)+PENDING_BUF_LEN) #endif + sizeof(long) + 7*SIXTYFOUR; total = bytes + SIXTYFOUR; if(total%4096!=0) total = total - (total%4096) + 4096; tmp0=tmp = malloc(total); serv_malloc_buf_base = tmp0; dassert1(1,tmp!=NULL,(int)total); /* stamp the last byte */ serv_tail= tmp + bytes+SIXTYFOUR-1; *serv_tail=SERV_STAMP; /* allocate memory for client memory handle to support put response * in dynamic memory registration protocols */ CLN_handle = (armci_vapi_memhndl_t*)tmp; memset(CLN_handle,0,mhsize); /* set it to zero */ tmp += mhsize; #if ARMCI_ENABLE_GPC_CALLS /* gpc_req memory*/ tmp += SIXTYFOUR - ((ssize_t)tmp % SIXTYFOUR); gpc_req = (gpc_buf_t *)tmp; tmp += MAX_GPC_REQ * sizeof(gpc_buf_t); #endif /* setup descriptor memory */ tmp += SIXTYFOUR - ((ssize_t)tmp % SIXTYFOUR); serv_descr_pool.descr= (struct ibv_recv_wr *)(tmp); tmp += extra; /* setup ack buffer*/ tmp += SIXTYFOUR - ((ssize_t)tmp % SIXTYFOUR); ack_buf = (long *)(tmp); *ack_buf=ARMCI_STAMP; tmp += sizeof(long); /* setup buffer pointers */ tmp += SIXTYFOUR - ((ssize_t)tmp % SIXTYFOUR); serv_buf_arr = (vapibuf_t **)malloc(sizeof(vapibuf_t*)*clients); for(i=0;ibuf = tmp + i*IMM_BUF_LEN; } tmp += clients*IMM_BUF_LEN; /*setup pending buffers*/ tmp += SIXTYFOUR - ((ssize_t)tmp % SIXTYFOUR); serv_pendbuf_arr = (vapibuf_pend_t *)(tmp); tmp=(char *)(serv_pendbuf_arr+PENDING_BUF_NUM); tmp += SIXTYFOUR - ((ssize_t)tmp % SIXTYFOUR); for(i=0; ibuf; #endif flag_arr = (int *)malloc(sizeof(int)*armci_nproc); for (i =0; iptag,(void*)CLN_nic->handle);fflush(stdout); } serv_memhandle.memhndl = ibv_reg_mr(CLN_nic->ptag, tmp0, total, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ); dassert1(1,serv_memhandle.memhndl!=NULL,total); serv_memhandle.lkey=serv_memhandle.memhndl->lkey; serv_memhandle.rkey=serv_memhandle.memhndl->rkey; /* exchange address of ack/memhandle flag on servers */ if(DEBUG_SERVER){ printf("%d(s):registered mem %p %dbytes mhandle=%p mharr starts%p\n", armci_me, tmp0, total, (void*)serv_memhandle.memhndl,(void*)CLN_handle); fflush(stdout); } } static char * client_malloc_buf_base; char * armci_vapi_client_mem_alloc(int size) { int mod, total; int extra = MAX_DESCR*sizeof(struct ibv_recv_wr)+SIXTYFOUR; char *tmp,*tmp0; /*we use the size passed by the armci_init_bufs routine instead of bytes*/ total = size + extra + 2*SIXTYFOUR; if(total%4096!=0) total = total - (total%4096) + 4096; tmp0 = tmp = malloc(total); dassert1(1,tmp!=NULL,total); client_malloc_buf_base = tmp; #if 0 /*SK: could this lead to a problem at ibv_reg_mr() because of unfixed 'total'?*/ if(ALIGN64ADD(tmp0))tmp0+=ALIGN64ADD(tmp0); #endif /* stamp the last byte */ client_tail= tmp + extra+ size +2*SIXTYFOUR-1; *client_tail=CLIENT_STAMP; /* we also have a place to store memhandle for zero-copy get */ pinned_handle =(armci_vapi_memhndl_t *) (tmp + extra+ size +SIXTYFOUR-16); mod = ((ssize_t)tmp)%SIXTYFOUR; client_descr_pool.descr= (struct ibv_recv_wr*)(tmp+SIXTYFOUR-mod); tmp += extra; client_memhandle.memhndl = ibv_reg_mr(SRV_nic->ptag, tmp0, total, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ); dassert(1,client_memhandle.memhndl!=NULL); client_memhandle.lkey = client_memhandle.memhndl->lkey; client_memhandle.rkey = client_memhandle.memhndl->rkey; handle_array[armci_me].lkey = client_memhandle.lkey; handle_array[armci_me].rkey = client_memhandle.rkey; handle_array[armci_me].memhndl = client_memhandle.memhndl; if(DEBUG_INIT){ printf("%d: registered client memory %p %dsize tmp=%p \n", armci_me,tmp0, total, tmp); fflush(stdout); } /*now that we have the handle array, we get every body elses RDMA handle*/ total = (sizeof(armci_vapi_memhndl_t)*armci_nproc)/sizeof(int); armci_msg_gop_scope(SCOPE_ALL,handle_array,total,"+",ARMCI_INT); return(tmp); } void armci_server_register_region(void *ptr,long bytes, ARMCI_MEMHDL_T *memhdl) { bzero(memhdl,sizeof(ARMCI_MEMHDL_T)); memhdl->memhndl = ibv_reg_mr(CLN_nic->ptag, ptr, bytes, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ); dassert(1,memhdl->memhndl!=NULL); memhdl->lkey=memhdl->memhndl->lkey; memhdl->rkey=memhdl->memhndl->rkey; if(DEBUG_SERVER){ printf("\n%d(s):registered lkey=%d rkey=%d ptr=%p end=%p %p\n",armci_me, memhdl->lkey,memhdl->rkey,ptr,(char *)ptr+bytes,(void*)memhdl); fflush(stdout); } } int armci_pin_contig_hndl(void *ptr, size_t bytes, ARMCI_MEMHDL_T *memhdl) { memhdl->memhndl = ibv_reg_mr(SRV_nic->ptag, ptr, bytes, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ); dassert(1,memhdl->memhndl!=NULL); memhdl->lkey=memhdl->memhndl->lkey; memhdl->rkey=memhdl->memhndl->rkey; if(0){ printf("\n%d:registered lkey=%d rkey=%d ptr=%p end=%p\n",armci_me, memhdl->lkey,memhdl->rkey,ptr,(char *)ptr+bytes);fflush(stdout); } return 1; } #if 1 void armci_network_client_deregister_memory(ARMCI_MEMHDL_T *mh) { int rc; rc = ibv_dereg_mr(mh->memhndl); dassert1(1,rc==0,rc); armci_vapi_check_return(DEBUG_FINALIZE,rc, "armci_network_client_deregister_memory:deregister_mr"); } void armci_network_server_deregister_memory(ARMCI_MEMHDL_T *mh) { int rc; return; /* ??? why ??? */ printf("\n%d:deregister ptr=%p",armci_me,(void*)mh);fflush(stdout); rc = ibv_dereg_mr(mh->memhndl); dassert1(1,rc==0,rc); armci_vapi_check_return(DEBUG_FINALIZE,rc, "armci_network_server_deregister_memory:deregister_mr"); } #else # define armci_network_client_deregister_memory(mh) \ armci_vapi_check_return(DEBUG_FINALIZE, \ ibv_dereg_mr(mh->memhndl), \ "armci_network_client_deregister_memory:deregister_mr") # define armci_network_server_deregister_memory(mh) \ armci_vapi_check_return(DEBUG_FINALIZE, \ ibv_dereg_mr(mh->memhndl), \ "armci_network_server_deregister_memory:deregister_mr") #endif void armci_set_serv_mh() { int s, ratio = sizeof(ack_t)/sizeof(int); /* first collect addrresses on all masters */ if(armci_me == armci_master){ SRV_ack[armci_clus_me].prem_handle=CLN_handle; SRV_ack[armci_clus_me].handle =serv_memhandle; armci_msg_gop_scope(SCOPE_MASTERS,SRV_ack,ratio*armci_nclus,"+", ARMCI_INT); } /* next master broadcasts the addresses within its node */ armci_msg_bcast_scope(SCOPE_NODE,SRV_ack,armci_nclus*sizeof(ack_t), armci_master); /* Finally save address corresponding to my id on each server */ for(s=0; s< armci_nclus; s++){ SRV_ack[s].prem_handle += armci_me; } } /**********END MEMORY ALLOCATION REGISTRATION AND DEREGISTRATION**************/ /*\ * init_connections, client_connect_to_servers -- client code * server_initial_connection, all_data_server -- server code \*/ void armci_init_connections() { int c,s; int sz; uint32_t *tmpbuf; int *tmparr; if(TIME_INIT)inittime0 = MPI_Wtime(); #if defined(PEND_BUFS) armci_pbuf_init_buffer_env(); #endif armci_setaffinity(NULL); /* initialize nic connection for qp numbers and lid's */ armci_init_nic(SRV_nic,1,1); for(c=0; c < NUMOFBUFFERS+1; c++) { mark_buf_send_complete[c]=1; } _gtmparr = (int *)calloc(armci_nproc,sizeof(int)); /*qp_numbers and lids need to be exchanged globally*/ tmparr = (int *)calloc(armci_nproc,sizeof(int)); tmparr[armci_me] = SRV_nic->lid_arr[armci_me]; sz = armci_nproc; armci_msg_gop_scope(SCOPE_ALL,tmparr,sz,"+",ARMCI_INT); for(c=0;clid_arr[c]=tmparr[c]; tmparr[c]=0; } /*SRV_con is for client to connect to servers */ SRV_con=(armci_connect_t *)malloc(sizeof(armci_connect_t)*armci_nclus); dassert1(1,SRV_con!=NULL,sizeof(armci_connect_t)*armci_nclus); bzero(SRV_con,sizeof(armci_connect_t)*armci_nclus); CLN_con=(armci_connect_t*)malloc(sizeof(armci_connect_t)*armci_nproc); dassert1(1,CLN_con!=NULL,sizeof(armci_connect_t)*armci_nproc); bzero(CLN_con,sizeof(armci_connect_t)*armci_nproc); /*every client creates a qp with every server other than the one on itself*/ SRV_rqpnums = (uint32_t*)malloc(sizeof(uint32_t)*armci_nproc); dassert(1,SRV_rqpnums); tmpbuf = (uint32_t*)calloc(armci_nproc,sizeof(uint32_t)); dassert(1,tmpbuf); sz = armci_nproc*(sizeof(uint32_t)/sizeof(int)); armci_vapi_max_inline_size = 0; if (!armci_use_odcm) { for(s = 0; s < armci_nclus; s++){ armci_connect_t *con = SRV_con + s; { armci_create_qp(SRV_nic,&con->qp); con->sqpnum = con->qp->qp_num; tmpbuf[armci_clus_info[s].master] = con->qp->qp_num; con->lid = SRV_nic->lid_arr[s]; } } MPI_Alltoall(tmpbuf,sizeof(uint32_t),MPI_CHAR,SRV_rqpnums, sizeof(uint32_t),MPI_CHAR,ARMCI_COMM_WORLD); free(tmpbuf); if(armci_me != armci_master) { free(SRV_rqpnums); SRV_rqpnums = NULL; } } else { for(s = 0; s < armci_nclus; s++){ armci_connect_t *con = SRV_con + s; con->state = QP_INACTIVE; } } SRV_ack = (ack_t*)calloc(armci_nclus, sizeof(ack_t)); dassert1(1,SRV_ack!=NULL,armci_nclus*sizeof(ack_t)); handle_array = (armci_vapi_memhndl_t *)calloc(sizeof(armci_vapi_memhndl_t), armci_nproc); dassert1(1,handle_array!=NULL,sizeof(armci_vapi_memhndl_t)*armci_nproc); if (armci_use_odcm) { setup_ud_channel(); } } static void vapi_connect_client() { int i, sz=0, c, rc; struct ibv_qp_attr qp_attr; enum ibv_qp_attr_mask qp_attr_mask; if (TIME_INIT) inittime0 = MPI_Wtime(); if (armci_me == armci_master) armci_util_wait_int(&armci_vapi_server_stage1, 1, 10); if (TIME_INIT) printf("\n%d:wait for server to get to stage 1 time for " "vapi_connect_client is %f", armci_me, (inittime1 = MPI_Wtime()) - inittime0); sz = armci_nproc; if (armci_me == armci_master) { armci_msg_gop_scope(SCOPE_MASTERS, _gtmparr, sz, "+", ARMCI_INT); for (c=0; clid_arr[c] = _gtmparr[c]; _gtmparr[c] = 0; } if (DEBUG_CLN) { printf("\n%d(svc): mylid = %d",armci_me,CLN_nic->lid_arr[armci_me]); fflush(stdout); } } armci_vapi_client_stage1 = 1; /* allocate and initialize connection structs */ sz = armci_nproc*sizeof(uint32_t)/sizeof(int); if (armci_me == armci_master) armci_util_wait_int(&armci_vapi_server_stage2, 1, 10); #if 0 for (c = 0; c < armci_nproc; c++){ armci_connect_t *con = CLN_con + c; if (armci_me != armci_master) { char *ptrr; int extra; ptrr = malloc(8 + sizeof(uint32_t) * armci_nproc); extra = ALIGNLONGADD(ptrr); ptrr = ptrr + extra; con->rqpnum = (uint32_t *)ptrr; bzero(con->rqpnum, sizeof(uint32_t) * armci_nproc); } armci_msg_gop_scope(SCOPE_ALL, con->rqpnum, sz, "+", ARMCI_INT); } #else CLN_rqpnums = (uint32_t*)malloc(sizeof(uint32_t)*armci_nproc); if (!armci_use_odcm) { if(armci_me != armci_master) { /*just has junk*/ CLN_rqpnumtmpbuf = (uint32_t*)malloc(sizeof(uint32_t)*armci_nproc); } dassert(1, CLN_rqpnumtmpbuf); MPI_Alltoall(CLN_rqpnumtmpbuf, sizeof(uint32_t), MPI_CHAR, CLN_rqpnums, sizeof(uint32_t), MPI_CHAR, ARMCI_COMM_WORLD); free(CLN_rqpnumtmpbuf); CLN_rqpnumtmpbuf=NULL; #endif if (TIME_INIT) printf("\n%d:wait for server tog et to stage 2 time for " "vapi_connect_client is %f", armci_me, (inittime2 = MPI_Wtime()) - inittime1); /*armci_set_serv_mh();*/ if (DEBUG_CLN) { printf("%d:all connections ready\n", armci_me); fflush(stdout); } /* For sanity */ memset(&qp_attr, 0, sizeof qp_attr); /* Modifying QP to INIT */ qp_attr_mask = IBV_QP_STATE | IBV_QP_PKEY_INDEX | IBV_QP_PORT | IBV_QP_ACCESS_FLAGS; qp_attr.qp_state = IBV_QPS_INIT; qp_attr.pkey_index = DEFAULT_PKEY_IX; qp_attr.port_num = SRV_nic->active_port; qp_attr.qp_access_flags = IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ; /* start from from server on my_node -1 */ for (i = 0; i < armci_nclus; i++) { armci_connect_t *con; con = SRV_con + i; rc = ibv_modify_qp(con->qp, &qp_attr, qp_attr_mask); dassertp(1,!rc,("%d: client RST->INIT i=%d rc=%d\n",armci_me,i,rc)); } if (TIME_INIT) printf("\n%d:to init time for vapi_connect_client is %f", armci_me, (inittime1 = MPI_Wtime()) - inittime2); qp_attr_mask = IBV_QP_STATE | IBV_QP_MAX_DEST_RD_ATOMIC | IBV_QP_PATH_MTU | IBV_QP_RQ_PSN | IBV_QP_MIN_RNR_TIMER; memset(&qp_attr, 0, sizeof qp_attr); qp_attr.qp_state = IBV_QPS_RTR; qp_attr.max_dest_rd_atomic = 4; qp_attr.path_mtu = IBV_MTU_1024; qp_attr.rq_psn = 0; qp_attr.min_rnr_timer = RNR_TIMER; /* AV: Adding the service level parameter */ qp_attr.ah_attr.sl = armci_openib_sl; for (i = 0; i < armci_nclus; i++) { armci_connect_t *con; #if 0 armci_connect_t *conS; #endif con = SRV_con + i; #if 0 conS = CLN_con + armci_me; #endif qp_attr_mask |= IBV_QP_AV | IBV_QP_DEST_QPN; #if 0 qp_attr.dest_qp_num = conS->rqpnum[armci_clus_info[i].master]; #else qp_attr.dest_qp_num = CLN_rqpnums[armci_clus_info[i].master]; #endif qp_attr.ah_attr.dlid = SRV_nic->lid_arr[armci_clus_info[i].master]; qp_attr.ah_attr.port_num = SRV_nic->active_port; qp_attr.ah_attr.sl = armci_openib_sl; rc = ibv_modify_qp(con->qp, &qp_attr, qp_attr_mask); dassertp(1,!rc,("%d: INIT->RTR client i=%d rc=%d\n",armci_me,i,rc)); } } /*to to to RTS, other side must be in RTR*/ armci_msg_barrier(); if (TIME_INIT) printf("\n%d:init to rtr time for vapi_connect_client is %f", armci_me, (inittime2 = MPI_Wtime()) - inittime1); armci_vapi_client_ready=1; if (!armci_use_odcm) { qp_attr_mask = IBV_QP_STATE | IBV_QP_SQ_PSN | IBV_QP_TIMEOUT | IBV_QP_RETRY_CNT | IBV_QP_RNR_RETRY | IBV_QP_MAX_QP_RD_ATOMIC; memset(&qp_attr, 0, sizeof qp_attr); qp_attr.qp_state = IBV_QPS_RTS; qp_attr.sq_psn = 0; qp_attr.timeout = 18; qp_attr.retry_cnt = 7; qp_attr.rnr_retry = 7; qp_attr.max_rd_atomic = 4; for (i = 0; i < armci_nclus; i++){ armci_connect_t *con; con = SRV_con + i; rc = ibv_modify_qp(con->qp, &qp_attr, qp_attr_mask); dassertp(1,!rc,("%d: client RTR->RTS i=%d rc=%d\n",armci_me,i,rc)); } if (TIME_INIT) printf("\n%d:rtr to rts time for vapi_connect_client is %f", armci_me, (inittime1 = MPI_Wtime()) - inittime2); free(CLN_rqpnums); CLN_rqpnums=NULL; } } void armci_client_connect_to_servers() { extern void armci_util_wait_int(volatile int *,int,int); if (TIME_INIT) inittime0 = MPI_Wtime(); _armci_buf_init(); vapi_connect_client(); if (armci_me == armci_master) armci_util_wait_int(&armci_vapi_server_ready,1,10); armci_msg_barrier(); if (DEBUG_CLN && armci_me == armci_master) { printf("\n%d:server_ready=%d\n",armci_me,armci_vapi_server_ready); fflush(stdout); } if (TIME_INIT) printf("\n%d:time for client_connect_to_s is %f", armci_me,MPI_Wtime()-inittime0); } void armci_init_vapibuf_recv(struct ibv_recv_wr *rd, struct ibv_sge *sg_entry, char *buf, int len, armci_vapi_memhndl_t *mhandle) { memset(rd,0,sizeof(struct ibv_recv_wr)); rd->next = NULL; rd->num_sge = 1; rd->sg_list = sg_entry; rd->wr_id = 0; sg_entry->lkey = mhandle->lkey; sg_entry->addr = (uint64_t)buf; sg_entry->length = len; } void armci_init_vapibuf_send(struct ibv_send_wr *sd, struct ibv_sge *sg_entry, char *buf, int len, armci_vapi_memhndl_t *mhandle) { sd->opcode = IBV_WR_SEND; sd->next = NULL; sd->send_flags = IBV_SEND_SIGNALED; sd->num_sge = 1; sd->sg_list = sg_entry; sg_entry->lkey = mhandle->lkey; sg_entry->addr = (uint64_t)buf; sg_entry->length = len; } static void armci_init_cbuf_srdma(struct ibv_send_wr *sd, struct ibv_sge *sg_entry, char *lbuf, char *rbuf, int len, armci_vapi_memhndl_t *lhandle, armci_vapi_memhndl_t *rhandle) { /* NOTE: sd->wr is a union, sr->wr.ud might conflict with sr->wr.rdma */ sd->opcode = IBV_WR_RDMA_WRITE; sd->send_flags = IBV_SEND_SIGNALED; sd->next = NULL; sd->num_sge = 1; sd->sg_list = sg_entry; if (rhandle) sd->wr.rdma.rkey = rhandle->rkey; sd->wr.rdma.remote_addr = (uint64_t)rbuf; if (lhandle) sg_entry->lkey = lhandle->lkey; sg_entry->addr = (uint64_t)lbuf; sg_entry->length = len; } static void armci_init_cbuf_rrdma(struct ibv_send_wr *sd, struct ibv_sge *sg_entry, char *lbuf, char *rbuf, int len, armci_vapi_memhndl_t *lhandle, armci_vapi_memhndl_t *rhandle) { sd->opcode = IBV_WR_RDMA_READ; sd->next = NULL; sd->send_flags = IBV_SEND_SIGNALED; sd->num_sge = 1; sd->sg_list = sg_entry; sd->wr.ud.remote_qkey = 0; if (rhandle) sd->wr.rdma.rkey = rhandle->rkey; sd->wr.rdma.remote_addr = (uint64_t)rbuf; if (lhandle) sg_entry->lkey = lhandle->lkey; sg_entry->addr = (uint64_t)lbuf; sg_entry->length = len; /* sd->wr is a union, sr->wr.ud might conflict with sr->wr.rdma */ } void armci_server_initial_connection() { int c, rc, i, j; struct ibv_qp_attr qp_attr; enum ibv_qp_attr_mask qp_attr_mask; struct ibv_recv_wr *bad_wr; if (TIME_INIT) inittime0 = MPI_Wtime(); if (DEBUG_SERVER) { printf("in server after fork %d (%d)\n",armci_me,getpid()); fflush(stdout); } #if defined(PEND_BUFS) && !defined(SERVER_THREAD) armci_pbuf_init_buffer_env(); #endif armci_init_nic(CLN_nic,1,1); if (!armci_openib_server_poll) { /* * Start a notify event request immediately after creation so * nothing is missed. */ rc = ibv_req_notify_cq(CLN_nic->rcq, 0); dassert1(1,rc==0,rc); } _gtmparr[armci_me] = CLN_nic->lid_arr[armci_me]; armci_vapi_server_stage1 = 1; armci_util_wait_int(&armci_vapi_client_stage1, 1, 10); CLN_rqpnumtmpbuf = (uint32_t*)malloc(sizeof(uint32_t)*armci_nproc); dassert(1, CLN_rqpnumtmpbuf); if (!armci_use_odcm) { for (c = 0; c < armci_nproc; c++) { armci_connect_t *con = CLN_con + c; armci_create_qp(CLN_nic, &con->qp); con->sqpnum = con->qp->qp_num; con->lid = CLN_nic->lid_arr[c]; CLN_rqpnumtmpbuf[c] = con->qp->qp_num; } } else { for (c = 0; c < armci_nproc; c++) { armci_connect_t *con = CLN_con + c; con->state = QP_INACTIVE; } } armci_vapi_server_stage2 = 1; if (!armci_use_odcm) { qp_attr_mask = IBV_QP_STATE | IBV_QP_PKEY_INDEX | IBV_QP_PORT | IBV_QP_ACCESS_FLAGS; memset(&qp_attr, 0, sizeof qp_attr); qp_attr.qp_state = IBV_QPS_INIT; qp_attr.pkey_index = DEFAULT_PKEY_IX; qp_attr.port_num = CLN_nic->active_port; qp_attr.qp_access_flags = IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ; for (c = 0; c < armci_nproc; c++) { armci_connect_t *con = CLN_con + c; rc = ibv_modify_qp(con->qp, &qp_attr, qp_attr_mask); dassertp(1,!rc,("%d: RTS->INIT server c=%d rc=%d\n",armci_me,c,rc)); } memset(&qp_attr, 0, sizeof qp_attr); qp_attr_mask = IBV_QP_STATE | IBV_QP_MAX_DEST_RD_ATOMIC | IBV_QP_PATH_MTU | IBV_QP_RQ_PSN | IBV_QP_MIN_RNR_TIMER; qp_attr.qp_state = IBV_QPS_RTR; qp_attr.path_mtu = IBV_MTU_1024; qp_attr.max_dest_rd_atomic = 4; qp_attr.min_rnr_timer = RNR_TIMER; qp_attr.rq_psn = 0; for(c = 0; c < armci_nproc; c++) { armci_connect_t *con = CLN_con + c; qp_attr_mask |= IBV_QP_DEST_QPN | IBV_QP_AV; qp_attr.dest_qp_num = SRV_rqpnums[c]; qp_attr.ah_attr.dlid = SRV_nic->lid_arr[c]; qp_attr.ah_attr.port_num = CLN_nic->active_port; rc = ibv_modify_qp(con->qp, &qp_attr, qp_attr_mask); dassertp(1,!rc,("%d: INIT->RTR server cln=%d rc=%d\n",armci_me,c,rc)); } } armci_util_wait_int(&armci_vapi_client_ready,1,10); memset(&qp_attr, 0, sizeof qp_attr); if (!armci_use_odcm) { qp_attr_mask = IBV_QP_STATE | IBV_QP_SQ_PSN | IBV_QP_TIMEOUT | IBV_QP_RETRY_CNT | IBV_QP_RNR_RETRY | IBV_QP_MAX_QP_RD_ATOMIC; qp_attr.qp_state = IBV_QPS_RTS; qp_attr.sq_psn = 0; qp_attr.timeout = 18; qp_attr.retry_cnt = 7; qp_attr.rnr_retry = 7; qp_attr.max_rd_atomic = 4; for (c = 0; c < armci_nproc; c++) { armci_connect_t *con = CLN_con + c; rc = ibv_modify_qp(con->qp, &qp_attr,qp_attr_mask); dassertp(1,!rc,("%d: server RTR->RTS cln=%d rc=%d\n",armci_me,c,rc)); } free(SRV_rqpnums); SRV_rqpnums = NULL; } armci_server_alloc_bufs(); if (!armci_use_odcm) { /* setup descriptors and post nonblocking receives */ #if defined(PEND_BUFS) assert(armci_nproc*(IMM_BUF_NUM+1)dscr, &cbuf->sg_entry, cbuf->buf, IMM_BUF_LEN, &serv_memhandle); /* we use index of the buffer to identify the buffer, this index is * returned with a call to ibv_poll_cq inside the ibv_wr */ cbuf->dscr.wr_id = i*(IMM_BUF_NUM+1)+j + DSCRID_IMMBUF_RECV; if (DEBUG_SERVER) { printf("\n%d(s):posted rr with lkey=%d",armci_me,cbuf->sg_entry.lkey); fflush(stdout); } rc = ibv_post_recv((CLN_con+i)->qp, &cbuf->dscr, &bad_wr); dassert1(1,rc==0,rc); } } #else for(i = 0; i < armci_nproc; i++) { vapibuf_t *cbuf; cbuf = serv_buf_arr[i]; armci_init_vapibuf_recv(&cbuf->dscr, &cbuf->sg_entry, cbuf->buf, CBUF_DLEN, &serv_memhandle); /* we use index of the buffer to identify the buffer, this index is * returned with a call to ibv_poll_cq inside the ibv_wr */ cbuf->dscr.wr_id = i+armci_nproc; if (DEBUG_SERVER) { printf("\n%d(s):posted rr with lkey=%d",armci_me,cbuf->sg_entry.lkey); fflush(stdout); } rc = ibv_post_recv((CLN_con+i)->qp, &cbuf->dscr, &bad_wr); dassert1(1,rc==0,rc); } #endif } if (TIME_INIT) printf("\n%d:post time for server_initial_conn is %f", armci_me, MPI_Wtime() - inittime4); armci_vapi_server_ready=1; if (DEBUG_SERVER) { printf("%d: server connected to all clients\n",armci_me); fflush(stdout); } if (TIME_INIT) printf("\n%d:time for server_initial_conn is %f", armci_me, MPI_Wtime() - inittime0); } static void armci_finalize_nic(vapi_nic_t *nic) { int ret; ret = ibv_destroy_cq(nic->scq); dassert1(1,ret==0,ret); armci_vapi_check_return(DEBUG_FINALIZE,ret,"armci_finalize_nic:destroy_scq"); ret = ibv_destroy_comp_channel(nic->sch); dassert1(1,ret==0,ret); armci_vapi_check_return(DEBUG_FINALIZE,ret,"armci_finalize_nic:destroy_sch"); ret = ibv_destroy_cq(nic->rcq); dassert1(1,ret==0,ret); armci_vapi_check_return(DEBUG_FINALIZE,ret,"armci_finalize_nic:destroy_rcq"); ret = ibv_destroy_comp_channel(nic->rch); dassert1(1,ret==0,ret); armci_vapi_check_return(DEBUG_FINALIZE,ret,"armci_finalize_nic:destroy_rch"); ret = ibv_close_device(nic->handle); dassert1(1,ret==0,ret); armci_vapi_check_return(DEBUG_FINALIZE,ret,"armci_finalize_nic:release_hca"); } void armci_server_transport_cleanup() { int s; int rc; /*first we have empty send/recv queues TBD*/ if(serv_malloc_buf_base){ rc = ibv_dereg_mr(serv_memhandle.memhndl); dassert1(1,rc==0,rc); armci_vapi_check_return(DEBUG_FINALIZE,rc, "armci_server_transport_cleanup:deregister_mr"); /*now free it*/ free(serv_malloc_buf_base); } /*now deregister all my regions from regionskk.c*/ armci_server_region_destroy(); if (CLN_con) { for (s = 0; s < armci_nproc; s++) { armci_connect_t *con = CLN_con + s; if (con->qp) { rc = ibv_destroy_qp(con->qp); armci_vapi_check_return(DEBUG_FINALIZE,rc, "armci_server_transport_cleanup:destroy_qp"); } #if 0 free(con->rqpnum); #endif } free(CLN_con); } armci_finalize_nic(CLN_nic); } void armci_transport_cleanup() { int s; int rc; /*first deregister buffers memory */ if (client_malloc_buf_base) { rc = ibv_dereg_mr(client_memhandle.memhndl); dassert1(1,rc==0,rc); armci_vapi_check_return(DEBUG_FINALIZE,rc,"armci_client_transport_cleanup:deregister_mr"); /*now free it*/ free(client_malloc_buf_base); } /*now deregister all my regions from regions.c*/ armci_region_destroy(); if (SRV_con) { for (s = 0; s < armci_nclus; s++) { armci_connect_t *con = SRV_con + s; if (con->qp) { rc = ibv_destroy_qp(con->qp); dassert1(1,rc==0,rc); armci_vapi_check_return(DEBUG_FINALIZE,rc,"armci_client_transport_cleanup:destroy_qp"); } #if 0 free(con->rqpnum); #endif } free(SRV_con); } armci_finalize_nic(SRV_nic); } /** Post an immediate buffer back for the client to send. */ static void _armci_pendbuf_post_immbuf(vapibuf_t *cbuf, int to) { int rc; struct ibv_recv_wr *bad_wr; #if defined(PEND_BUFS) assert(cbuf->dscr.wr_id == cbuf-serv_buf_arr[0]+DSCRID_IMMBUF_RECV); #endif rc = ibv_post_recv((CLN_con+to)->qp, &(cbuf->dscr), &bad_wr); dassert1(1,rc==0,rc); } #if defined(PEND_BUFS) #define DSCRID_TO_IMMBUFID(x) (x-DSCRID_IMMBUF_RECV) #else #define DSCRID_TO_IMMBUFID(x) ((x)-armci_nproc) #endif #if defined(PEND_BUFS) /**Obtain a message receive buffer to receive a message. Used in place * of MessageRcvBuffer. Should not be used. */ char *armci_openib_get_msg_rcv_buf(int proc) { armci_die("PEND_BUFS in OPENIB: MessageRcvBuffer not available. Should use the in-place buffers to receive data", proc); return NULL; } /** Check that the data is in a server allocated buffer. This is * guaranteed to be pinned. Ideally, this should always be true. Any * operation that request alternative support will have to fix this * function and possibly @armci_openib_get_msg_rcv_buf(). * @param br IN Buffer pointer being checked * @return 1 if it is a server-allocated buffer. 0 otherwise. */ int armci_data_in_serv_buf(void *br) { if(br>=(void *)serv_malloc_buf_base && br<(void *)serv_tail) return 1; if(DEBUG_SERVER) { printf("%d:: serv_bufs=%p<->%p. br=%p out of range\n", armci_me, serv_malloc_buf_base, serv_tail, br); fflush(stdout); } return 0; } #define PBUF_BUFID_TO_PUT_WRID(_pbufid) (DSCRID_PENDBUF+(_pbufid)*2) #define PBUF_BUFID_TO_GET_WRID(_pbufid) (DSCRID_PENDBUF+(_pbufid)*2+1) #define PBUF_WRID_TO_PBUFID(_id) (((_id)-DSCRID_PENDBUF)/2) #define PBUF_IS_GET_WRID(_id) (((_id)-DSCRID_PENDBUF)&1) #define PBUF_IS_PUT_WRID(_id) (!(((_id)-DSCRID_PENDBUF)&1)) /**Complete processing this immediate buffer. Parameters is void *, * since vapibuf_t*|immbuf_t* is not available in armci-vapi.h */ void armci_complete_immbuf(void *buf) { vapibuf_t *cbuf = (vapibuf_t*)buf; request_header_t *msginfo=(request_header_t*)cbuf->buf; #if SRI_CORRECT #error cbuf->send_pending = 0; #else _armci_pendbuf_post_immbuf(cbuf,msginfo->from); #endif armci_data_server(cbuf); if(msginfo->operation==PUT || ARMCI_ACC(msginfo->operation)) { SERVER_SEND_ACK(msginfo->from); } #if SRI_CORRECT if(!cbuf->send_pending) { _armci_pendbuf_post_immbuf(cbuf,msginfo->from); } #endif } /**Complete processing this pending buffer. Parameters is void *, * since vapibuf_t*|immbuf_t* is not available in armci-vapi.h. Note * that the pending buffer may not yet be available for reuse. This * will depend on the state of the pending buffer (which might have to * wait for a communication innitiated by armci_data_server() to * complete. */ void armci_complete_pendbuf(void *buf) { vapibuf_pend_t *pbuf = (vapibuf_pend_t *)buf; request_header_t *msginfo=(request_header_t*)pbuf->buf; assert(pbuf->vbuf); #if SRI_CORRECT pbuf->cbuf->send_pending=0; #else _armci_pendbuf_post_immbuf(pbuf->vbuf,msginfo->from); #endif armci_data_server(pbuf); if(msginfo->operation==PUT || ARMCI_ACC(msginfo->operation)) { SERVER_SEND_ACK(msginfo->from); } #if SRI_CORRECT #error assert(!pbuf->cbuf->send_pending); _armci_pendbuf_post_immbuf(pbuf->cbuf,msginfo->from); #endif } void _armci_get_data_from_client(int proc, struct ibv_send_wr *sdscr, int dscrid, struct ibv_sge *ssg_entry, void *rbuf, void *lbuf, int bytes) ; void _armci_send_data_to_client_pbuf(int proc, struct ibv_send_wr *sdscr, int dscrid, struct ibv_sge *ssg_entry, void *rbuf, void *lbuf, int bytes); int no_srv_copy_nsegs_ulimit() { return armci_max_qp_ous_swr*armci_max_num_sg_ent/10; } /** Initiate a get operation to progress a pending buffer. * @param msginfo Request header for any additional processing * @param src Pointer to src of data (remote for GET) * @param dst Pointer to dst * @param bytes #bytes to transfer * @param proc proc to transfer from(for get)/to(for put) * @param pbufid Index of pending buffer */ void armci_pbuf_start_get(void *msg_info, void *src, void *dst, int bytes, int proc, int pbufid) { struct ibv_send_wr sdscr; struct ibv_sge sg_entry; int wrid = PBUF_BUFID_TO_GET_WRID(pbufid); request_header_t *msginfo=(request_header_t *)msg_info; void armci_server_rdma_contig_to_strided(char *src_ptr, int proc, char *dst_ptr, int dst_stride_arr[], int seg_count[], int stride_levels, request_header_t *msginfo); #if defined(PUT_NO_SRV_COPY) if(msginfo->operation==PUT && msginfo->format==STRIDED && !msginfo->pinned && src==msginfo->tag.data_ptr) { char *loc_ptr, *rem_ptr; int stride_levels, *count; int *loc_stride_arr; char *dscr = (char *)(msginfo+1); ARMCI_MEMHDL_T *mhloc=NULL; int nsegs, i; /* unpack descriptor record */ loc_ptr = *(void**)dscr; dscr += sizeof(void*); stride_levels = *(int*)dscr; dscr += sizeof(int); loc_stride_arr = (int*)dscr; dscr += stride_levels*sizeof(int); count = (int*)dscr; rem_ptr = msginfo->tag.data_ptr; nsegs = 1; for(i=0; ifrom); if(nsegswr_id = dscrid; struct ibv_send_wr *bad_wr; rc = ibv_post_send((CLN_con+proc)->qp, sdscr, &bad_wr); dassert1(1,rc==0,rc); } void _armci_send_data_to_client_pbuf(int proc, struct ibv_send_wr *sdscr, int dscrid, struct ibv_sge *ssg_entry, void *rbuf, void *lbuf, int bytes) { int rc = 0; if(DEBUG_SERVER) { printf("\n%d(s):sending data to client %d at %p flag = %p bytes=%d\n", armci_me, proc,rbuf,(char *)rbuf+bytes-sizeof(int),bytes);fflush(stdout); } memset(sdscr,0,sizeof(struct ibv_send_wr)); armci_init_cbuf_srdma(sdscr,ssg_entry,lbuf,rbuf,bytes, &serv_memhandle,(handle_array+proc)); if(DEBUG_SERVER){ printf("\n%d(s):handle_array[%d]=%p dbuf=%p flag=%p bytes=%d\n",armci_me, proc,(void*)&handle_array[proc],(char *)rbuf, (char *)rbuf+bytes-sizeof(int),bytes); fflush(stdout); } sdscr->wr_id = dscrid; struct ibv_send_wr *bad_wr; rc = ibv_post_send((CLN_con+proc)->qp, sdscr, &bad_wr); dassert1(1,rc==0,rc); } #endif #define DATA_SERVER_YIELD_CPU void armci_call_data_server() { int rc = 0; int rc1 = 0; vapibuf_t *cbuf,*cbufs; request_header_t *msginfo,*msg; int c,i; static int mytag=1; #ifdef CHANGE_SERVER_AFFINITY int rrr,serverwcount=0; #else #ifdef DATA_SERVER_YIELD_CPU_ int serverwcount=0; #endif #endif #ifdef CHANGE_SERVER_AFFINITY cpu_set_t mycpuid,new_mask; char str[CPU_SETSIZE]; char cid[8]; extern char * cpuset_to_cstr(cpu_set_t *mask, char *str); int nslave=armci_clus_info[armci_clus_me].nslave; rrr=sched_getaffinity(0, sizeof(mycpuid), &mycpuid); #endif #if ARMCI_ENABLE_GPC_CALLS unblock_thread_signal(GPC_COMPLETION_SIGNAL); #endif #if defined(PEND_BUFS) armci_pendbuf_init(); #endif for (;;) { struct ibv_wc *pdscr=NULL; struct ibv_wc pdscr1; pdscr = &pdscr1; pdscr->status = IBV_WC_SUCCESS; rc = 0; #ifdef CHANGE_SERVER_AFFINITY static int ccc; serverwcount++; if(serverwcount==100){ serverwcount=0; ccc=(ccc+1)%nslave; sprintf (cid, "%d", ccc); rrr = cstr_to_cpuset(&new_mask,cid); if (sched_setaffinity(0, sizeof (new_mask), &new_mask)) { perror("sched_setaffinity"); printf("failed to set pid %d's affinity.\n", getpid()); } rrr=sched_getaffinity(0, sizeof(mycpuid), &mycpuid); if(rrr)perror("sched_getaffinity"); } #else #ifdef DATA_SERVER_YIELD_CPU_ serverwcount++; if(serverwcount==50){ serverwcount=0;usleep(1); } #endif #endif #if ARMCI_ENABLE_GPC_CALLS block_thread_signal(GPC_COMPLETION_SIGNAL); #endif bzero(pdscr, sizeof(*pdscr)); do { rc = ibv_poll_cq(CLN_nic->rcq, 1, pdscr); if (armci_server_terminating) { /* server is interrupted when clients terminate connections */ armci_server_transport_cleanup(); sleep(1); _exit(0); } if (rc == 0 && !armci_openib_server_poll) { /* wait for a notify event */ rc1 = ibv_get_cq_event(CLN_nic->rch,&CLN_nic->rcq,&CLN_nic->rcq_cntx); dassert1(1,rc1==0,rc1); ibv_ack_cq_events(CLN_nic->rcq, 1); /* re-arm notify event */ rc1 = ibv_req_notify_cq(CLN_nic->rcq, 0); dassert1(1,rc1==0,rc1); /* note: an event receive does not guarantee an actual completion */ continue; } } while (rc == 0); if(DEBUG_SERVER) { printf("\n%d:pdscr=%p %p %d %d %d %d\n",armci_me,(void*)pdscr,(void*)&pdscr1, pdscr->status,pdscr->opcode,pdscr->vendor_err, pdscr->src_qp); fflush(stdout); } dassertp(1,rc>=0,("%d: rc=%d id=%d status=%d", armci_me,rc,(int)pdscr->wr_id,pdscr->status)); dassert1(1,pdscr->status==IBV_WC_SUCCESS,pdscr->status); if (DEBUG_SERVER) { printf("%d(s) : NEW MESSAGE bytelen %d \n",armci_me,pdscr->byte_len); printf("%d(s) : NEW MESSAGE id is %ld \n",armci_me,pdscr->wr_id); fflush(stdout); } #if defined(PEND_BUFS) if(pdscr->wr_id>=DSCRID_IMMBUF_RESP && pdscr->wr_idwr_id - DSCRID_IMMBUF_RESP; if(id>=0 && idsend_pending==1); serv_buf_arr[id]->send_pending = 0; _armci_pendbuf_post_immbuf(serv_buf_arr[id],dest); } #endif continue; } if (pdscr->wr_id>=DSCRID_PENDBUF && pdscr->wr_idwr_id); /* printf("%d(s) : Progressing pending msg (something completed) pbufid=%d id=%ld byte_len=%d status=%d\n", armci_me, pbufid,pdscr->wr_id,pdscr->byte_len,done_status); */ /* fflush(stdout); */ if(PBUF_IS_GET_WRID(pdscr->wr_id)) armci_pendbuf_done_get(pbufid); else if(PBUF_IS_PUT_WRID(pdscr->wr_id)) armci_pendbuf_done_put(pbufid); else armci_die("Pending buffer op completed. But not PUT or GET!",pdscr->wr_id); continue; } #endif if (pdscr->wr_id >= DSCRID_SCATGAT && pdscr->wr_id < DSCRID_SCATGAT_END) { #if defined(PEND_BUFS) sr_descr_t *sdscr_arr; #else sr_descr_t *rdscr_arr; #endif if (DEBUG_SERVER) { printf("%d(s) : received SCATGAT DATA id = %ld, length = %d\n", armci_me,pdscr->wr_id, pdscr->byte_len); fflush(stdout); } #if defined(PEND_BUFS) sdscr_arr = armci_vapi_serv_nbsdscr_array; assert(sdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].numofsends>0); sdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].numofsends--; if(sdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].numofsends==0) sdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].tag=0; #else rdscr_arr = armci_vapi_serv_nbrdscr_array; rdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].numofrecvs--; if(rdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].numofrecvs==0) rdscr_arr[pdscr->wr_id-DSCRID_SCATGAT].tag=0; #endif continue; } #if defined(PEND_BUFS) assert(pdscr->wr_id>=DSCRID_IMMBUF_RECV && pdscr->wr_idwr_id)]; assert(cbuf->dscr.wr_id == pdscr->wr_id); msginfo = (request_header_t*)cbuf->buf; armci_ack_proc = c = msginfo->from; if (DEBUG_SERVER) { printf("%d(s) : request id is %ld operation is %d, length is %d from=%d cbuf->dscr.wr_id=%d\n", armci_me,pdscr->wr_id,msginfo->operation,pdscr->byte_len,msginfo->from, (int)cbuf->dscr.wr_id); fflush(stdout); } #if defined(PEND_BUFS) cbufs = cbuf; armci_init_vapibuf_recv(&cbufs->dscr, &cbufs->sg_entry,cbufs->buf, IMM_BUF_LEN, &serv_memhandle); cbufs->dscr.wr_id = pdscr->wr_id; #else cbufs = serv_buf_arr[pdscr->wr_id - armci_nproc] = spare_serv_buf; armci_init_vapibuf_recv(&cbufs->dscr, &cbufs->sg_entry,cbufs->buf, CBUF_DLEN, &serv_memhandle); cbufs->dscr.wr_id = c + armci_nproc; spare_serv_buf = cbuf; #endif if(DEBUG_SERVER) { printf("%d(s):Came out of poll id=%ld\n",armci_me,pdscr->wr_id); fflush(stdout); } if(msginfo->operation == PUT &&msginfo->pinned == 1){ int found, num; int stride_arr[MAX_STRIDE_LEVEL]; /*should be MAX_STRIDE_LEVELS*/ int count[MAX_STRIDE_LEVEL]; void *dest_ptr; int stride_levels; ARMCI_MEMHDL_T *loc_memhandle; void armci_post_scatter(void *,int *,int *,int, armci_vapi_memhndl_t *,int,int,int,sr_descr_t **); /*unpack decsriptor_record : should call a function instead */ msg = msginfo + 1; test_ptr = dest_ptr = *(void**)msg; msg = (request_header_t *) ((char*)msg + sizeof(void*)); test_stride_levels=stride_levels = *(int*)msg; msg = (request_header_t *) ((char*)msg + sizeof(int)); for(i =0; iwr_id)); if(DEBUG_SERVER) { printf("%d(s) : about to call armci_post_scatter\n",armci_me); fflush(stdout); } armci_post_scatter(dest_ptr, stride_arr, count, stride_levels, loc_memhandle,msginfo->from, mytag, SERV,NULL ); mytag = (mytag+1)%(MAX_PENDING); if(mytag==0)mytag=1; if(DEBUG_SERVER) { printf("%d(s) : finished posting %d scatter\n",armci_me,num); fflush(stdout); } _armci_pendbuf_post_immbuf(cbufs, msginfo->from); SERVER_SEND_ACK(msginfo->from); } else if(msginfo->operation == REGISTER){ if (DEBUG_SERVER) { printf("%d(s) : Register_op id is %d, comp_dscr_id is %ld\n", armci_me,msginfo->operation,pdscr->wr_id); fflush(stdout); } armci_server_register_region(*((void **)(msginfo+1)), *((long *)((char *)(msginfo+1)+sizeof(void *))), (ARMCI_MEMHDL_T *)(msginfo->tag.data_ptr)); _armci_pendbuf_post_immbuf(cbufs, msginfo->from); *(long *)(msginfo->tag.ack_ptr) = ARMCI_STAMP; continue; } else { if(DEBUG_SERVER) { printf("%d(s) : request is %ld about to call armci_data_server\n", armci_me, pdscr->wr_id); fflush(stdout); } #if defined(PEND_BUFS) armci_pendbuf_service_req(cbuf); #else _armci_pendbuf_post_immbuf(cbufs, msginfo->from); armci_data_server(cbuf); if((msginfo->operation == PUT) || ARMCI_ACC(msginfo->operation)) { /* for operations that do not send data back we can send ACK now */ SERVER_SEND_ACK(msginfo->from); if(DEBUG_SERVER){ printf("%d(s) : posted ack\n\n",armci_me); fflush(stdout); } } #endif } if (0) { printf("%d(s):Done processed request\n\n",armci_me); fflush(stdout); } #if ARMCI_ENABLE_GPC_CALLS unblock_thread_signal(GPC_COMPLETION_SIGNAL); #endif }/* end of for */ } void armci_vapi_complete_buf(armci_vapi_field_t *field,int snd,int rcv,int to,int op) { struct ibv_send_wr *snd_dscr; BUF_INFO_T *info; info = (BUF_INFO_T *)((char *)field-sizeof(BUF_INFO_T)); if(info->tag && op==GET)return; if(snd){ snd_dscr=&(field->sdscr); if(mark_buf_send_complete[snd_dscr->wr_id]==0) armci_send_complete(snd_dscr,"armci_vapi_complete_buf",1); } if(rcv){ int *last; long *flag; int loop = 0; request_header_t *msginfo = (request_header_t *)(field+1); flag = (long *)&msginfo->tag.ack; if(op==PUT || ARMCI_ACC(op)){ if(msginfo->bypass && msginfo->pinned && msginfo->format == STRIDED && op == PUT); else{ while(armci_util_long_getval(flag) != ARMCI_STAMP) { loop++; loop %=100000; if(loop==0){ } } } /* printf("%d: client complete_buf. op=%d loop=%d till *flag=ARMCI_STAMP\n", armci_me,op,loop); */ /* fflush(stdout); */ *flag = 0L; } else{ /*SK: I think we get here only for GET with result directly going to client's pinned memory. (info.tag==0 && op==GET)*/ last = (int *)((char *)msginfo+msginfo->datalen-sizeof(int)); while(armci_util_int_getval(last) == ARMCI_STAMP && armci_util_long_getval(flag) != ARMCI_STAMP){ loop++; loop %=100000; if(loop==0){ if(DEBUG_CLN){ printf("%d: client last(%p)=%d flag(%p)=%ld off=%d\n", armci_me,(void*)last,*last,(void*)flag,*flag,msginfo->datalen); fflush(stdout); } } } } } } void armci_vapi_test_buf(armci_vapi_field_t *field,int snd,int rcv,int to,int op, int *retval) { struct ibv_send_wr *snd_dscr; BUF_INFO_T *info; info = (BUF_INFO_T *)((char *)field-sizeof(BUF_INFO_T)); *retval = 0; if(info->tag && op==GET)return; if(snd){ snd_dscr=&(field->sdscr); if(mark_buf_send_complete[snd_dscr->wr_id]==0) { /* printf("%d: test buf. send not complete\n",armci_me); */ /* fflush(stdout); */ return; } } if(rcv){ int *last; long *flag; request_header_t *msginfo = (request_header_t *)(field+1); flag = (long *)&msginfo->tag.ack; if(op==PUT || ARMCI_ACC(op)){ if(msginfo->bypass && msginfo->pinned && msginfo->format == STRIDED && op == PUT) *retval=1; else{ if(armci_util_long_getval(flag) == ARMCI_STAMP) { *retval = 1; } } return; } else{ /*SK: I think we get here only for GET with result directly going to client's pinned memory. (info.tag==0 && op==GET)*/ last = (int *)((char *)msginfo+msginfo->datalen-sizeof(int)); if(armci_util_int_getval(last) != ARMCI_STAMP || armci_util_long_getval(flag) == ARMCI_STAMP){ *retval=1; } return; } } } static inline void armci_vapi_post_send(int isclient,int con_offset, struct ibv_send_wr *snd_dscr,char *from) { int rc = 0; /*vapi_nic_t *nic;*/ armci_connect_t *con; int total = 0; if(!isclient){ /*nic = CLN_nic;*/ con = CLN_con+con_offset; } else{ /*nic = SRV_nic;*/ con = SRV_con+con_offset; } if(DEBUG_CLN){ printf("vapi_post_send: snd_dscr->num_sge=%d, snd_dscr->sg_list->length=%d\n", snd_dscr->num_sge, snd_dscr->sg_list->length); fflush(stdout); } /* find the total length of all the segments */ total = snd_dscr->sg_list->length * snd_dscr->num_sge; if(DEBUG_CLN){ printf("%d(c) : total is %d\t, max_size is %d\n",armci_me,total, armci_vapi_max_inline_size); } struct ibv_send_wr *bad_wr; if (total > armci_vapi_max_inline_size) { rc = ibv_post_send(con->qp, snd_dscr, &bad_wr); } else { rc = ibv_post_send(con->qp, snd_dscr, &bad_wr); /* no corresponding call, using ibv_post_send rc = EVAPI_post_inline_sr(nic->handle,con->qp,snd_dscr);*/ } dassert1(1,rc==0,rc); } /** Send request to server. */ int armci_send_req_msg(int proc, void *buf, int bytes) { int cluster = armci_clus_id(proc); request_header_t *msginfo = (request_header_t *)buf; struct ibv_send_wr *snd_dscr; struct ibv_sge *ssg_lst; THREAD_LOCK(armci_user_threads.net_lock); check_state_of_ib_connection(proc, 0); snd_dscr = BUF_TO_SDESCR((char *)buf); ssg_lst = BUF_TO_SSGLST((char *)buf); /*Stamp end of buffers as needed*/ if(msginfo->operation == GET && !msginfo->pinned) { const int dscrlen = msginfo->dscrlen; const int datalen = msginfo->datalen; int *last; if(dscrlen < (datalen - sizeof(int))) last = (int*)(((char*)(msginfo+1))+(datalen-sizeof(int))); else last = (int*)(((char*)(msginfo+1))+(dscrlen+datalen-sizeof(int))); *last = ARMCI_STAMP; #ifdef GET_STRIDED_COPY_PIPELINED if(msginfo->format == STRIDED) { const int ssize = GET_STRIDED_COPY_PIPELINED_SIZE/sizeof(int); int *sfirst = (int*)(dscrlen+(char*)(msginfo+1))+ssize; /*stamping can start here*/ int *slast = last, *ptr; for(ptr=sfirst; ptroperation == ACK) { *(int *)(msginfo +1) = ARMCI_STAMP+1; *(((int *)(msginfo +1))+1) = ARMCI_STAMP+1; } #if defined(PEND_BUFS) if((msginfo->operation==PUT || ARMCI_ACC(msginfo->operation)) && bytes > IMM_BUF_LEN) { msginfo->tag.imm_msg=0; assert(sizeof(request_header_t)datalen, IMM_BUF_LEN); assert(bytes==IMM_BUF_LEN||(bytes==sizeof(*msginfo)+msginfo->dscrlen)); } else if(msginfo->operation==GET && !(msginfo->datalen+sizeof(request_header_t)+msginfo->dscrlentag.imm_msg=0; bytes = ARMCI_MIN(sizeof(request_header_t)+msginfo->dscrlen, IMM_BUF_LEN); } #if defined(PUT_NO_SRV_COPY) && 0 /*SK:disabled. Imm msgs are sent inline for latency reasons*/ else if(msginfo->operation==PUT && !msginfo->pinned && msginfo->format==STRIDED && msginfo->tag.data_len>=2048) { msginfo->tag.imm_msg = 0; assert(sizeof(request_header_t)datalen, IMM_BUF_LEN); assert(bytes==IMM_BUF_LEN||(bytes==sizeof(*msginfo)+msginfo->dscrlen)); } #endif else{ msginfo->tag.imm_msg=1; } /* printf("%d: send_req: op=%d bytes=%d data_len=%d imm=%d\n",*/ /* armci_me, msginfo->operation, bytes, msginfo->datalen,msginfo->tag.imm_msg);*/ /* fflush(stdout);*/ if(bytes<0 || bytes>IMM_BUF_LEN) { printf("%d(pid=%d): Trying to send too large a mesg. op=%d bytes=%d(max=%d) to=%d\n", armci_me, getpid(),msginfo->operation,bytes,IMM_BUF_LEN, proc); fflush(stdout); pause(); assert(bytes>=0); assert(bytes <= IMM_BUF_LEN); } _armci_buf_ensure_pend_outstanding_op_per_node(buf,cluster); /* printf("%d: send_req. ensured pend os per node. to=%d op=%d\n", armci_me, msginfo->to,msginfo->operation); */ /* fflush(stdout); */ #else _armci_buf_ensure_one_outstanding_op_per_node(buf,cluster); #endif if(msginfo->operation == PUT || ARMCI_ACC(msginfo->operation)){ #if defined(PEND_BUFS) if(!msginfo->tag.imm_msg){ msginfo->tag.data_ptr = (char *)(msginfo+1)+msginfo->dscrlen; msginfo->tag.data_len = msginfo->datalen; } else msginfo->tag.data_ptr = NULL; #else { msginfo->tag.data_ptr = (void *)&msginfo->tag.ack; } #endif } else { if(msginfo->operation == GET && !msginfo->bypass && msginfo->dscrlen >= (msginfo->datalen-sizeof(int))) msginfo->tag.data_ptr = (char *)(msginfo+1)+msginfo->dscrlen; else msginfo->tag.data_ptr = GET_DATA_PTR(buf); } /*this has to be reset so that we can wait on it see ReadFromDirect*/ msginfo->tag.ack = 0; msginfo->tag.ack_ptr = &(msginfo->tag.ack); if(DEBUG_CLN){ printf("%d:the ack_ptr is initialised to %p, ack->value is %ld\n", armci_me,msginfo->tag.ack_ptr,msginfo->tag.ack);fflush(stdout); } armci_init_vapibuf_send(snd_dscr, ssg_lst,buf, bytes, &client_memhandle); /* printf("%d: Sending req wr_id=%d to=%d\n",armci_me,snd_dscr->wr_id,proc);*/ /* fflush(stdout);*/ armci_vapi_post_send(1,cluster,snd_dscr,"send_req_msg:post_send"); THREAD_UNLOCK(armci_user_threads.net_lock); if(DEBUG_CLN){ printf("%d:client sent REQ=%d %d bytes serv=%d qp=%p id =%ld lkey=%d\n", armci_me,msginfo->operation,bytes,cluster, (void*)(SRV_con+cluster)->qp,snd_dscr->wr_id,ssg_lst->lkey); fflush(stdout); } return(0); } /*\ * client waits for first phase ack before posting gather desr \*/ void armci_wait_ack(char *buffer) { long *flag; request_header_t *msginfo = (request_header_t *)(buffer); flag = (long*)&msginfo->tag.ack; while(armci_util_long_getval(flag) != ARMCI_STAMP); flag = 0; } void armci_client_direct_send(int p,void *src_buf, void *dst_buf, int len,void** contextptr,int nbtag,ARMCI_MEMHDL_T *lochdl,ARMCI_MEMHDL_T *remhdl) { sr_descr_t *dirdscr; int clus = armci_clus_id(p); check_state_of_ib_connection(p, 0); THREAD_LOCK(armci_user_threads.net_lock); /*ID for the desr that comes from get_next_descr is already set*/ dirdscr = armci_vapi_get_next_sdescr(nbtag,0); if(nbtag)*contextptr = dirdscr; armci_init_cbuf_srdma(&dirdscr->sdescr,dirdscr->sg_entry,src_buf,dst_buf, len,lochdl,remhdl); armci_vapi_post_send(1,clus,&(dirdscr->sdescr), "client_direct_send:post_send"); /* the following unlock/lock ensures fairness (in case other threads are waiting on the lock) not required to work */ #if 1 THREAD_UNLOCK(armci_user_threads.net_lock); THREAD_LOCK(armci_user_threads.net_lock); #endif if(nbtag==0) armci_send_complete(&(dirdscr->sdescr),"armci_client_direct_send",1); THREAD_UNLOCK(armci_user_threads.net_lock); } /*\ RDMA get \*/ void armci_client_direct_get(int p, void *src_buf, void *dst_buf, int len, void** cptr,int nbtag,ARMCI_MEMHDL_T *lochdl, ARMCI_MEMHDL_T *remhdl) { int rc = 0; sr_descr_t *dirdscr; int clus = armci_clus_id(p); check_state_of_ib_connection(p, 0); struct ibv_send_wr *bad_wr; THREAD_LOCK(armci_user_threads.net_lock); /*ID for the desr that comes from get_next_descr is already set*/ dirdscr = armci_vapi_get_next_sdescr(nbtag,0); if(nbtag)*cptr = dirdscr; if(DEBUG_CLN){ printf("\n%d: in direct get lkey=%d rkey=%d\n",armci_me,lochdl->lkey, remhdl->rkey);fflush(stdout); } armci_init_cbuf_rrdma(&dirdscr->sdescr,dirdscr->sg_entry,dst_buf,src_buf, len,lochdl,remhdl); rc = ibv_post_send((SRV_con+clus)->qp, &(dirdscr->sdescr), &bad_wr); dassert1(1,rc==0,rc); /* unlock/lock to ensure fairness: allows others thread post before waiting for completion */ /*VT?check to see if this should be UNLOCK followed by lock*/ #if 1 THREAD_UNLOCK(armci_user_threads.net_lock); THREAD_LOCK(armci_user_threads.net_lock); #endif if(!nbtag){ armci_send_complete(&(dirdscr->sdescr),"armci_client_direct_get",1); } THREAD_UNLOCK(armci_user_threads.net_lock); } #define WQE_LIST_LENGTH 32 #define WQE_LIST_COUNT 1 /** Direct put into remote processor memory. Assumes that (and invoked * only when) the source buffers in user memory are pinned as well. * @param operation PUT/GET * @param src_ptr Source pointer for data * @param src_stride_arr Strides on the source array * @param dst_ptr Destination pointer to start writing to * @param seq_count[stride_levels+1] #els in each stride * level. seg_count[0] is contiguous bytes * @param proc Destimation process * @param cptr OUT Pointer to store the descriptor to wait on for completion * @param nbtag IN Non-blocking tag (non-blocking op if nbtag!=0) * @param lochdl IN Local memory handle/key (registered memory stuff) * @param remhdl IN Remote memory handle/key * */ #if 0 void armci_client_direct_rdma_strided(int operation, int proc, char *src_ptr, int src_stride_arr[], char *dst_ptr, int dst_stride_arr[], int seg_count[], int stride_levels, void **cptr, int nbtag, ARMCI_MEMHDL_T *lochdl, ARMCI_MEMHDL_T *remhdl) { int rc; sr_descr_t *dirdscr; const int clus = armci_clus_id(proc); struct ibv_send_wr *bad_wr; struct ibv_send_wr sdscr[WQE_LIST_COUNT][WQE_LIST_LENGTH]; struct ibv_sge sg_entry[WQE_LIST_COUNT][WQE_LIST_LENGTH]; int busy[WQE_LIST_COUNT], wait_count[WQE_LIST_COUNT],clst; int i, j, c, numposts; int idx[MAX_STRIDE_LEVEL]; THREAD_LOCK(armci_user_threads.net_lock); assert(stride_levels >= 0); assert(stride_levels<=MAX_STRIDE_LEVEL); /*ID for the desr that comes from get_next_descr is already set*/ dirdscr = armci_vapi_get_next_sdescr(nbtag,0); if(nbtag)*cptr = dirdscr; assert(dirdscr->tag == nbtag); if(DEBUG_CLN) { printf("\n%d: in direct rdma strided id=%d lkey=%ld rkey=%ld\n", armci_me,dirdscr->sdescr.wr_id,lochdl->lkey,remhdl->rkey);fflush(stdout); } for(c=0; csdescr.wr_id; sdscr[j][i].send_flags = 0; /*non-signalled*/ if(inumofsends=0; bzero(idx, stride_levels*sizeof(int)); int count = (numposts%WQE_LIST_LENGTH) ? (numposts%WQE_LIST_LENGTH):WQE_LIST_LENGTH; assert(count == ARMCI_MIN(count, numposts)); clst=0; for(i=0; i0); armci_send_complete(&dirdscr->sdescr,"client_direct_rdma_strided",wait_count[clst]); dirdscr->numofsends -= wait_count[clst]; busy[clst]=0; wait_count[clst]=0; } if(operation == PUT) { sg_entry[clst][j-i].addr = (uint64_t)(src_ptr + src_offset); sdscr[clst][j-i].wr.rdma.remote_addr = (uint64_t)(dst_ptr + dst_offset); } else if (operation == GET) { sg_entry[clst][j-i].addr = (uint64_t)(dst_ptr + dst_offset); sdscr[clst][j-i].wr.rdma.remote_addr = (uint64_t)(src_ptr + src_offset); } assert(sg_entry[clst][j-i].length == seg_count[0]); idx[0] += 1; for(c=0;cnumofsends += 1; wait_count[clst] = 1; /* armci_send_complete(&dirdscr->sdescr,"armci_client_direct_rdma_strided",count); */ if(count < WQE_LIST_LENGTH) { sdscr[clst][count-1].next=&sdscr[clst][count]; /*reset it*/ } sdscr[clst][count-1].send_flags=0; /*reset it*/ i += count; count = ARMCI_MIN(WQE_LIST_LENGTH,numposts-i); assert(count==0 || count==WQE_LIST_LENGTH); clst = (clst+1)%WQE_LIST_COUNT; } if(!nbtag) { armci_send_complete(&dirdscr->sdescr,"armci_client_direct_get",dirdscr->numofsends); dirdscr->numofsends = 0; dirdscr->tag = 0; } THREAD_UNLOCK(armci_user_threads.net_lock); } #else void armci_client_direct_rdma_strided(int operation, int proc, char *src_ptr, int src_stride_arr[], char *dst_ptr, int dst_stride_arr[], int seg_count[], int stride_levels, void **cptr, int nbtag, ARMCI_MEMHDL_T *lochdl, ARMCI_MEMHDL_T *remhdl) { int rc, i, j, c, busy[WQE_LIST_COUNT], clst, ctr; sr_descr_t *dirdscr; const int clus = armci_clus_id(proc); struct ibv_send_wr *bad_wr; struct ibv_send_wr sdscr[WQE_LIST_COUNT][WQE_LIST_LENGTH]; struct ibv_sge sg_entry[WQE_LIST_COUNT][WQE_LIST_LENGTH]; stride_info_t sinfo, dinfo; THREAD_LOCK(armci_user_threads.net_lock); assert(stride_levels >= 0); assert(stride_levels<=MAX_STRIDE_LEVEL); /*ID for the desr that comes from get_next_descr is already set*/ dirdscr = armci_vapi_get_next_sdescr(nbtag,0); if(nbtag)*cptr = dirdscr; assert(dirdscr->tag == nbtag); if(DEBUG_CLN) { printf("\n%d: in direct rdma strided id=%lu lkey=%u rkey=%u\n", armci_me,dirdscr->sdescr.wr_id,lochdl->lkey,remhdl->rkey);fflush(stdout); } /*initialize fixed values for descriptors*/ bzero(sdscr, WQE_LIST_COUNT*WQE_LIST_LENGTH*sizeof(struct ibv_send_wr)); bzero(sg_entry, WQE_LIST_COUNT*WQE_LIST_LENGTH*sizeof(struct ibv_sge)); for(j=0; jsdescr.wr_id; sdscr[j][i].send_flags = 0; /*non-signalled*/ if(inumofsends=0; clst=ctr=0; bzero(busy, sizeof(int)*WQE_LIST_COUNT); while(armci_stride_info_has_more(&sinfo)) { assert(armci_stride_info_has_more(&dinfo)); uint64_t saddr = (uint64_t)armci_stride_info_seg_ptr(&sinfo); uint64_t daddr = (uint64_t)armci_stride_info_seg_ptr(&dinfo); if(operation == PUT) { sg_entry[clst][ctr].addr = saddr; sdscr[clst][ctr].wr.rdma.remote_addr = daddr; } else if (operation == GET) { sg_entry[clst][ctr].addr = daddr; sdscr[clst][ctr].wr.rdma.remote_addr = saddr; } assert(sg_entry[clst][ctr].length == seg_count[0]); ctr+=1; armci_stride_info_next(&sinfo); armci_stride_info_next(&dinfo); if(ctr == WQE_LIST_LENGTH || !armci_stride_info_has_more(&sinfo)) { sdscr[clst][ctr-1].next=NULL; sdscr[clst][ctr-1].send_flags=IBV_SEND_SIGNALED; /*only the last one*/ for(c=0; cnumofsends += 1; if(ctrsdescr,"client_direct_rdma_strided",1); dirdscr->numofsends -= 1; busy[clst]=0; } } } armci_stride_info_destroy(&sinfo); armci_stride_info_destroy(&dinfo); if(!nbtag) { armci_send_complete(&dirdscr->sdescr,"armci_client_direct_get",dirdscr->numofsends); dirdscr->numofsends = 0; dirdscr->tag = 0; } THREAD_UNLOCK(armci_user_threads.net_lock); } #endif #if defined(PEND_BUFS) int armci_server_msginfo_to_pbuf_index(request_header_t *msginfo) { int index=-1, i; assert(!msginfo->tag.imm_msg); for(i = 0; ioperation == GET); assert(stride_levels >= 0); assert(stride_levels<=MAX_STRIDE_LEVEL); if(!get_armci_region_local_hndl(src_ptr,armci_clus_id(armci_me), &loc_memhdl)) { armci_die("rdma_strided_to_contig: failed to get local handle\n",0); } if(!msginfo->tag.imm_msg) { int index = armci_server_msginfo_to_pbuf_index(msginfo); assert(index>=0); wr_id = PBUF_BUFID_TO_PUT_WRID(index); } else { wr_id = DSCRID_IMMBUF_RESP_END-1-proc; } bzero(&sdscr1, sizeof(sdscr1)); sdscr1.wr_id = wr_id; if(DEBUG_CLN) { printf("\n%d: in rdma strided to contig id=%d lkey=%ld rkey=%ld\n", armci_me,wr_id,loc_memhdl->lkey,rem_memhdl->rkey); fflush(stdout); } /*initialize fixed values for descriptors*/ bzero(sdscr, WQE_LIST_COUNT*WQE_LIST_LENGTH*sizeof(struct ibv_send_wr)); bzero(sg_entry, WQE_LIST_COUNT*WQE_LIST_LENGTH*sizeof(struct ibv_sge)); for(j=0; jfrom); THREAD_UNLOCK(armci_user_threads.net_lock); } #else #define MAX_NUM_SGE 64 /*same as above, but uses gather rdma writes*/ void armci_server_rdma_strided_to_contig(char *src_ptr, int src_stride_arr[], int seg_count[], int stride_levels, char *dst_ptr, int proc, request_header_t *msginfo) { int rc, ctr, wr_id, bytes; struct ibv_send_wr *bad_wr, sdscr1, sdscr; struct ibv_sge sg_entry[MAX_NUM_SGE]; stride_info_t sinfo; uint64_t daddr; ARMCI_MEMHDL_T *loc_memhdl; ARMCI_MEMHDL_T *rem_memhdl = &handle_array[proc]; const int max_num_sge = ARMCI_MIN(MAX_NUM_SGE, armci_max_num_sg_ent); int numposts=0, numsegs=0; THREAD_LOCK(armci_user_threads.net_lock); assert(msginfo->operation == GET); assert(stride_levels >= 0); assert(stride_levels<=MAX_STRIDE_LEVEL); if(!get_armci_region_local_hndl(src_ptr,armci_clus_id(armci_me), &loc_memhdl)) { armci_die("rdma_strided_to_contig: failed to get local handle\n",0); } if(!msginfo->tag.imm_msg) { int index = armci_server_msginfo_to_pbuf_index(msginfo); assert(index>=0); wr_id = PBUF_BUFID_TO_PUT_WRID(index); } else { wr_id = DSCRID_IMMBUF_RESP_END-1-proc; } bzero(&sdscr1, sizeof(sdscr1)); sdscr1.wr_id = wr_id; if(DEBUG_CLN) { printf("\n%d: in rdma strided to contig id=%d lkey=%u rkey=%u\n", armci_me,wr_id,loc_memhdl->lkey,rem_memhdl->rkey); fflush(stdout); } /*initialize fixed values for descriptors*/ bzero(&sdscr, sizeof(sdscr)); bzero(sg_entry, max_num_sge*sizeof(struct ibv_sge)); armci_init_cbuf_srdma(&sdscr,&sg_entry[0],NULL,NULL,seg_count[0],loc_memhdl,rem_memhdl); sdscr.send_flags = 0; /*non-signalled*/ sdscr.num_sge = 0; /*set below in the loop*/ sdscr.wr_id = wr_id; for(ctr=0; ctrlkey; } /*post requests in a loop*/ armci_stride_info_init(&sinfo,src_ptr,stride_levels,src_stride_arr,seg_count); numposts = numsegs = 0; ctr=0; daddr = (uint64_t)dst_ptr; bytes=0; while(armci_stride_info_has_more(&sinfo)) { sg_entry[ctr].addr = (uint64_t)armci_stride_info_seg_ptr(&sinfo); assert(sg_entry[ctr].length == seg_count[0]); sdscr.num_sge += 1; bytes += seg_count[0]; ctr+=1; numsegs += 1; armci_stride_info_next(&sinfo); if(ctr == max_num_sge || !armci_stride_info_has_more(&sinfo)) { sdscr.wr.rdma.remote_addr = daddr; if(!armci_stride_info_has_more(&sinfo)) { sdscr.send_flags=IBV_SEND_SIGNALED; /*only the last one*/ } else { assert(sdscr.send_flags == 0); } assert(ctr == sdscr.num_sge); rc = ibv_post_send(CLN_con[proc].qp, &sdscr, &bad_wr); dassert1(1,rc==0,rc); numposts += 1; ctr=0; sdscr.num_sge = 0; daddr += bytes; bytes = 0; } } /* printf("%d(s): scatgat write numposts=%d numsegs=%d\n",armci_me,numposts,numsegs); */ armci_stride_info_destroy(&sinfo); assert(proc == msginfo->from); THREAD_UNLOCK(armci_user_threads.net_lock); } /*Directly read data from client buffers into remote memory. Data is contiguous in client-side. */ void armci_server_rdma_contig_to_strided(char *src_ptr, int proc, char *dst_ptr, int dst_stride_arr[], int seg_count[], int stride_levels, request_header_t *msginfo) { int rc, ctr, wr_id, bytes; struct ibv_send_wr *bad_wr, sdscr1, sdscr; struct ibv_sge sg_entry[MAX_NUM_SGE]; stride_info_t dinfo; uint64_t saddr; ARMCI_MEMHDL_T *loc_memhdl; ARMCI_MEMHDL_T *rem_memhdl = &handle_array[proc]; const int max_num_sge = ARMCI_MIN(MAX_NUM_SGE, armci_max_num_sg_ent); int numposts=0, numsegs=0; THREAD_LOCK(armci_user_threads.net_lock); assert(msginfo->operation == PUT); assert(stride_levels >= 0); assert(stride_levels<=MAX_STRIDE_LEVEL); if(!get_armci_region_local_hndl(dst_ptr,armci_clus_id(armci_me), &loc_memhdl)) { armci_die("rdma_strided_to_contig: failed to get local handle\n",0); } if(!msginfo->tag.imm_msg) { int index = armci_server_msginfo_to_pbuf_index(msginfo); assert(index>=0); wr_id = PBUF_BUFID_TO_GET_WRID(index); } else { wr_id = DSCRID_IMMBUF_RESP_END-1-proc; } bzero(&sdscr1, sizeof(sdscr1)); sdscr1.wr_id = wr_id; if(DEBUG_CLN) { printf("\n%d: in rdma strided to contig id=%d lkey=%u rkey=%u\n", armci_me,wr_id,loc_memhdl->lkey,rem_memhdl->rkey); fflush(stdout); } /*initialize fixed values for descriptors*/ bzero(&sdscr, sizeof(sdscr)); bzero(sg_entry, max_num_sge*sizeof(struct ibv_sge)); armci_init_cbuf_rrdma(&sdscr,&sg_entry[0],NULL,NULL,seg_count[0],loc_memhdl,rem_memhdl); sdscr.send_flags = 0; /*non-signalled*/ sdscr.num_sge = 0; /*set below in the loop*/ sdscr.wr_id = wr_id; for(ctr=0; ctrlkey; } /*post requests in a loop*/ armci_stride_info_init(&dinfo,dst_ptr,stride_levels,dst_stride_arr,seg_count); numposts = numsegs = 0; ctr=0; saddr = (uint64_t)src_ptr; bytes=0; while(armci_stride_info_has_more(&dinfo)) { sg_entry[ctr].addr = (uint64_t)armci_stride_info_seg_ptr(&dinfo); assert(sg_entry[ctr].length == seg_count[0]); sdscr.num_sge += 1; bytes += seg_count[0]; ctr+=1; numsegs += 1; armci_stride_info_next(&dinfo); if(ctr == max_num_sge || !armci_stride_info_has_more(&dinfo)) { sdscr.wr.rdma.remote_addr = saddr; if(!armci_stride_info_has_more(&dinfo)) { sdscr.send_flags=IBV_SEND_SIGNALED; /*only the last one*/ } else { assert(sdscr.send_flags == 0); } assert(ctr == sdscr.num_sge); rc = ibv_post_send(CLN_con[proc].qp, &sdscr, &bad_wr); dassert1(1,rc==0,rc); numposts += 1; ctr=0; sdscr.num_sge = 0; saddr += bytes; bytes = 0; } } /* printf("%d(s): scatgat write numposts=%d numsegs=%d\n",armci_me,numposts,numsegs); */ armci_stride_info_destroy(&dinfo); assert(proc == msginfo->from); THREAD_UNLOCK(armci_user_threads.net_lock); } #endif #endif char *armci_ReadFromDirect(int proc, request_header_t *msginfo, int len) { int cluster = armci_clus_id(proc); vapibuf_ext_t* ecbuf=BUF_TO_ECBUF(msginfo); char *dataptr = GET_DATA_PTR(ecbuf->buf); extern void armci_util_wait_int(volatile int *,int,int); if(DEBUG_CLN){ printf("%d(c):read direct %d qp=%p\n",armci_me, len,(void*)&(SRV_con+cluster)->qp); fflush(stdout); } if(mark_buf_send_complete[ecbuf->snd_dscr.wr_id]==0) armci_send_complete(&(ecbuf->snd_dscr),"armci_ReadFromDirect",1); if(!msginfo->bypass){ long *flag; int *last; int loop = 0; flag = &(msginfo->tag.ack); if(msginfo->operation==GET){ last = (int *)(dataptr+len-sizeof(int)); if(msginfo->dscrlen >= (len-sizeof(int))){ last = (int *)(dataptr+len+msginfo->dscrlen-sizeof(int)); dataptr+=msginfo->dscrlen; } if(DEBUG_CLN){ printf("\n%d:flagval=%d at ptr=%p ack=%ld dist=%d\n",armci_me,*last, (void*)last,*flag,len);fflush(stdout); } while(armci_util_int_getval(last) == ARMCI_STAMP && armci_util_long_getval(flag) != ARMCI_STAMP){ loop++; loop %=100000; if(loop==0){ if(DEBUG_CLN){ printf("%d: client last(%p)=%d flag(%p)=%ld off=%d\n", armci_me,(void*)last,*last,(void*)flag,*flag,msginfo->datalen); fflush(stdout); } } } *flag = 0L; } else if(msginfo->operation == REGISTER){ while(armci_util_long_getval(flag) != ARMCI_STAMP){ loop++; loop %=100000; if(loop==0){ if(DEBUG_CLN){ printf("%d: client flag(%p)=%ld off=%d\n", armci_me,(void*)flag,*flag,msginfo->datalen); fflush(stdout); } } } } else{ int *flg = (int *)(dataptr+len); while(armci_util_int_getval(flg) != ARMCI_STAMP){ loop++; loop %=100000; if(loop==0){ if(DEBUG_CLN){ printf("%d: client waiting (%p)=%d off=%d\n", armci_me,(void*)flg,*flg,len); fflush(stdout); } } } } } return dataptr; } #ifdef GET_STRIDED_COPY_PIPELINED /**Same as armci_ReadFromDirect, except reads partial segments * (identify by stamping done in armci_send_req_msg() and * returns. Note that the return value is the starting pointer of the * buffer containig the data. It is the same for all the segments * read for a message. * @param proc IN Read data corresponding to an earlier req to this proc * @param msginfo IN The request for which we are reading now * @param len IN #bytes in the total response * @param bytes_done OUT @bytes of the total response read so far (monotonic) * @return Starting pointer to the buffer containing the data */ char *armci_ReadFromDirectSegment(int proc, request_header_t *msginfo, int len, int *bytes_done) { int cluster = armci_clus_id(proc); vapibuf_ext_t* ecbuf=BUF_TO_ECBUF(msginfo); char *dataptr = GET_DATA_PTR(ecbuf->buf); extern void armci_util_wait_int(volatile int *,int,int); if(DEBUG_CLN){ printf("%d(c):read direct %d qp=%p\n",armci_me, len,(void*)&(SRV_con+cluster)->qp); fflush(stdout); } if(mark_buf_send_complete[ecbuf->snd_dscr.wr_id]==0) armci_send_complete(&(ecbuf->snd_dscr),"armci_ReadFromDirect",1); if(!msginfo->bypass){ long *flag; int *last; int loop = 0; flag = &(msginfo->tag.ack); if(msginfo->operation==GET){ last = (int *)(dataptr+len-sizeof(int)); if(msginfo->dscrlen >= (len-sizeof(int))){ last = (int *)(dataptr+len+msginfo->dscrlen-sizeof(int)); dataptr+=msginfo->dscrlen; } if(DEBUG_CLN){ printf("\n%d:flagval=%d at ptr=%p ack=%ld dist=%d\n",armci_me,*last, (void*)last,*flag,len);fflush(stdout); } while(armci_util_int_getval(last) == ARMCI_STAMP && armci_util_long_getval(flag) != ARMCI_STAMP){ loop++; loop %=100000; if(loop==0){ if(DEBUG_CLN){ printf("%d: client last(%p)=%d flag(%p)=%ld off=%d\n", armci_me,(void*)last,*last,(void*)flag,*flag,msginfo->datalen); fflush(stdout); } } { int ssize = GET_STRIDED_COPY_PIPELINED_SIZE/sizeof(int); int *sfirst = (int*)(msginfo->dscrlen+(char*)(msginfo+1))+ssize; /*stamping can start here*/ int *slast = last; int off = (((int *)(dataptr+*bytes_done)-sfirst+ssize)/ssize)*ssize; int *ptr = sfirst+off; dassert(1,off>=0); dassert(1,(char *)sfirst>dataptr); dassert(1,(char *)ptr>dataptr); if(ptr<=slast && armci_util_int_getval(ptr)!=ARMCI_STAMP) { *bytes_done = ((char*)ptr)-dataptr; return dataptr; } } } *flag = 0L; *bytes_done = len; return dataptr; } else if(msginfo->operation == REGISTER){ while(armci_util_long_getval(flag) != ARMCI_STAMP){ loop++; loop %=100000; if(loop==0){ if(DEBUG_CLN){ printf("%d: client flag(%p)=%ld off=%d\n", armci_me,(void*)flag,*flag,msginfo->datalen); fflush(stdout); } } } } else{ int *flg = (int *)(dataptr+len); while(armci_util_int_getval(flg) != ARMCI_STAMP){ loop++; loop %=100000; if(loop==0){ if(DEBUG_CLN){ printf("%d: client waiting (%p)=%d off=%d\n", armci_me,(void*)flg,*flg,len); fflush(stdout); } } } } } *bytes_done = len; return dataptr; } #endif /** * @param proc IN id of remote client to put to * @param buf IN local buf (has to be registered) */ void armci_send_data_to_client(int proc, void *buf, int bytes,void *dbuf) { int i, rc = 0; struct ibv_send_wr *bad_wr; struct ibv_send_wr sdscr; struct ibv_sge ssg_entry; if(DEBUG_SERVER){ printf("\n%d(s):sending data to client %d at %p flag = %p bytes=%d\n", armci_me, proc,dbuf,(char *)dbuf+bytes-sizeof(int),bytes);fflush(stdout); } memset(&sdscr,0,sizeof(struct ibv_send_wr)); memset(&ssg_entry,0,sizeof(ssg_entry)); armci_init_cbuf_srdma(&sdscr,&ssg_entry,buf,dbuf,bytes, &serv_memhandle,(handle_array+proc)); if(DEBUG_SERVER){ printf("\n%d(s):handle_array[%d]=%p dbuf=%p flag=%p bytes=%d\n",armci_me, proc,(void*)&handle_array[proc],(char *)dbuf, (char *)dbuf+bytes-sizeof(int),bytes); fflush(stdout); } #if defined(PEND_BUFS) for(i=proc*(IMM_BUF_NUM+1); i<(proc+1)*(IMM_BUF_NUM+1); i++) { if((char*)buf>= serv_buf_arr[i]->buf && (char*)bufbuf) break; } #if SRI_CORRECT if(i<(proc+1)*(IMM_BUF_NUM+1)) { /*Message from an immediate buffer*/ assert(serv_buf_arr[i]->send_pending==0); serv_buf_arr[i]->send_pending=1; sdscr.wr_id = DSCRID_IMMBUF_RESP+i; } else #endif { sdscr.wr_id = DSCRID_IMMBUF_RESP+armci_nproc*(IMM_BUF_NUM+1)+1; } /* #endif */ /* #if defined(PEND_BUFS) */ /* { */ /* static uint64_t ctr=DSCRID_IMMBUF_RESP; */ /* sdscr.wr_id = ctr; */ /* ctr = (ctr+1-DSCRID_IMMBUF_RESP)%(DSCRID_IMMBUF_RESP_END-DSCRID_IMMBUF_RESP)+DSCRID_IMMBUF_RESP; */ /* } */ #else sdscr.wr_id = proc+armci_nproc; #endif rc = ibv_post_send((CLN_con+proc)->qp, &sdscr, &bad_wr); dassert1(1,rc==0,rc); #if !defined(PEND_BUFS) armci_send_complete(&sdscr,"armci_send_data_to_client",1); #endif } void armci_WriteToDirect(int proc, request_header_t* msginfo, void *buf) { int bytes; int *last; ARMCI_PR_DBG("enter",0); bytes = (int)msginfo->datalen; if(DEBUG_SERVER){ printf("%d(s):write to direct sent %d to %d at %p\n",armci_me, bytes,proc,(char *)msginfo->tag.data_ptr); fflush(stdout); } if(msginfo->operation!=GET){ *(int *)((char *)buf+bytes)=ARMCI_STAMP; bytes+=sizeof(int); } #if defined(PEND_BUFS) if(!msginfo->tag.imm_msg) { int i; /* fprintf(stderr, "%d:: Not immediate mesg operated on\n", armci_me); */ assert(msginfo->operation == GET); /*nothing else uses this for now*/ /**This is a pending buf*/ vapibuf_pend_t *pbuf=NULL; int index; for(i = 0; idscrlen+bytessdscr, PBUF_BUFID_TO_PUT_WRID(index), &pbuf->sg_entry, msginfo->tag.data_ptr, buf, bytes); } else #endif { armci_send_data_to_client(proc,buf,bytes,msginfo->tag.data_ptr); } /*if(msginfo->dscrlen >= (bytes-sizeof(int))) last = (int*)(((char*)(buf)) + (msginfo->dscrlen+bytes - sizeof(int))); else*/ last = (int*)(((char*)(buf)) + (bytes - sizeof(int))); if(msginfo->operation==GET && *last == ARMCI_STAMP){ SERVER_SEND_ACK(msginfo->from); } armci_ack_proc=NONE; ARMCI_PR_DBG("exit",0); } #if defined(PEND_BUFS) void armci_rcv_req(void *mesg,void *phdr,void *pdescr,void *pdata,int *buflen) { request_header_t *msginfo = *(request_header_t**)mesg; *(void **)phdr = msginfo; if(msginfo->tag.imm_msg) *buflen = IMM_BUF_LEN - sizeof(request_header_t) - msginfo->dscrlen; else *buflen = PENDING_BUF_LEN - sizeof(request_header_t) - msginfo->dscrlen; *(void **)pdata = msginfo->dscrlen + (char *)(msginfo+1); if(msginfo->bytes) *(void **)pdescr = msginfo+1; else *(void **)pdescr = NULL; } #else void armci_rcv_req(void *mesg,void *phdr,void *pdescr,void *pdata,int *buflen) { vapibuf_t *cbuf = (vapibuf_t*)mesg; request_header_t *msginfo = (request_header_t *)cbuf->buf; *(void **)phdr = msginfo; ARMCI_PR_DBG("enter",msginfo->operation); if(DEBUG_SERVER){ printf("%d(server): got %d req (dscrlen=%d datalen=%d) from %d\n", armci_me, msginfo->operation, msginfo->dscrlen, msginfo->datalen, msginfo->from); fflush(stdout); } /* we leave room for msginfo on the client side */ *buflen = MSG_BUFLEN - sizeof(request_header_t); if(msginfo->bytes) { *(void **)pdescr = msginfo+1; if(msginfo->operation == GET) *(void **)pdata = MessageRcvBuffer; else *(void **)pdata = msginfo->dscrlen + (char*)(msginfo+1); }else { *(void**)pdescr = NULL; *(void**)pdata = MessageRcvBuffer; } ARMCI_PR_DBG("exit",msginfo->operation); } #endif static void posts_scatter_desc(sr_descr_t *pend_dscr,int proc,int type) { int rc; int cluster = armci_clus_id(proc); struct ibv_recv_wr *scat_dscr; struct ibv_recv_wr *bad_wr; scat_dscr = &pend_dscr->rdescr; /*armci_vapi_print_dscr_info(NULL,scat_dscr);*/ if((type==SERV && DEBUG_SERVER) || (type==CLN && DEBUG_CLN)){ printf("%d(%d) : inside posts scatter dscr, id is %lu\n", armci_me,type,scat_dscr->wr_id); fflush(stdout); } if(type == SERV) rc = ibv_post_recv((CLN_con + proc)->qp, scat_dscr, &bad_wr); else rc = ibv_post_recv((SRV_con+cluster)->qp, scat_dscr, &bad_wr); dassert1(1,rc==0,rc); if((type==SERV && DEBUG_SERVER) || (type==CLN && DEBUG_CLN) ) { printf("\n%d: list_length is %d, id is %ld\n", armci_me,scat_dscr->num_sge,scat_dscr->wr_id); fflush(stdout); } } /*\ * client calls from request.c * server calls from ds-shared.c \*/ static sr_descr_t client_blocking_scatter_dscr; void armci_post_scatter(void *dest_ptr, int dest_stride_arr[], int count[], int stride_levels, armci_vapi_memhndl_t *mhandle, int proc, int nbtag, int type, sr_descr_t **srd) { int i; int total_of_2D = 1; int index[MAX_STRIDE_LEVEL], unit[MAX_STRIDE_LEVEL]; int j,k,y; int num_xmit = 0, num_seg, max_seg, rem_seg,vecind; char* src, *src1; sr_descr_t *pend_dscr; struct ibv_sge *scat_sglist; struct ibv_recv_wr *scat_dscr; if((type==SERV && DEBUG_SERVER) || (type==CLN && DEBUG_CLN) ){ printf("%d(%d) : inside post_scatter %d\n",armci_me,type,nbtag); fflush(stdout); } max_seg = armci_max_num_sg_ent; THREAD_LOCK(armci_user_threads.net_lock); if(nbtag){ pend_dscr = armci_vapi_get_next_rdescr(nbtag,1); if(srd!=NULL)*srd=pend_dscr; } else{ pend_dscr = &client_blocking_scatter_dscr; pend_dscr->rdescr.wr_id=DSCRID_SCATGAT + MAX_PENDING; } /*pend_dscr->proc = proc;*/ pend_dscr->numofrecvs=0; scat_dscr = &pend_dscr->rdescr; scat_sglist = pend_dscr->sg_entry; /* scat_dscr->opcode = VAPI_RECEIVE; no ->opcode in ibv_recv_wr */ /* scat_dscr->comp_type = VAPI_SIGNALED; no ->comp_type in ibv_recv_wr */ scat_dscr->sg_list = scat_sglist; scat_dscr->num_sge = 0; index[2] = 0; unit[2] = 1; if(stride_levels > 1){ total_of_2D = count[2]; for(j=3; j<=stride_levels; j++){ index[j] = 0; unit[j] = unit[j-1]*count[j-1]; total_of_2D*=count[j]; } } num_xmit = total_of_2D*count[1]/max_seg; rem_seg = (total_of_2D*count[1])%max_seg; if(num_xmit == 0) num_xmit = 1; else if(rem_seg!= 0)num_xmit++; if ((type==SERV && DEBUG_SERVER) || (type==CLN && DEBUG_CLN) ) { printf("%d(%d):armci_post_scatter num_xmit = %d\t, rem_seg = %d\n", armci_me,type,num_xmit,rem_seg); fflush(stdout); } k=0; vecind = 0; if(rem_seg!=0 && k==(num_xmit-1))num_seg = rem_seg; else num_seg = max_seg; y=0; for(i=0;i= count[j]) index[j] =0; } src1 = src; for(j=0; jnumofrecvs++; /* the previous one has been posted, start off new*/ scat_dscr->num_sge = 0; y = 0; /* reuse the same scatter descriptor */ vecind=0;k++; if(rem_seg!=0 && k==(num_xmit-1))num_seg = rem_seg; } /* fill the scatter descriptor */ scat_sglist[y].addr = (uint64_t)src1; scat_sglist[y].lkey = mhandle->lkey; scat_sglist[y].length = count[0]; scat_dscr->num_sge++; src1 += dest_stride_arr[0]; y++; } if(vecind == num_seg){ posts_scatter_desc(pend_dscr,proc,type); pend_dscr->numofrecvs++; /* the previous one has been posted, start off new*/ scat_dscr->num_sge = 0; y =0 ; vecind = 0; k++; if(rem_seg!=0 && k==(num_xmit-1))num_seg=rem_seg; else num_seg = max_seg; } } THREAD_UNLOCK(armci_user_threads.net_lock); /* printf("%d(s): num scatters posted=%d\n", armci_me,pend_dscr->numofrecvs); */ if(!nbtag){ /*if blocking call wait_for_blocking_scatter to complete*/ } return; } void armci_wait_for_blocking_scatter() { sr_descr_t *pend_dscr=&client_blocking_scatter_dscr; armci_recv_complete(&pend_dscr->rdescr,"armci_post_scatter",pend_dscr->numofrecvs); } /*\ * function used by armci_post_gather to actually post the sctter list \*/ static void posts_gather_desc(sr_descr_t *pend_dscr,int proc,int type) { int rc; int cluster = armci_clus_id(proc); struct ibv_send_wr *gat_dscr; struct ibv_send_wr *bad_wr; THREAD_LOCK(armci_user_threads.net_lock); gat_dscr = &pend_dscr->sdescr; /*armci_vapi_print_dscr_info(gat_dscr,NULL);*/ if((type==SERV && DEBUG_SERVER) || (type==CLN && DEBUG_CLN)){ printf("%d: type(client=1)=%d inside posts gather dscr, id is %lu\n", armci_me,type,gat_dscr->wr_id); fflush(stdout); } rc = 0; if(type == CLN){ rc = ibv_post_send((SRV_con+cluster)->qp, gat_dscr, &bad_wr); } else{ rc = ibv_post_send((CLN_con + proc)->qp, gat_dscr, &bad_wr); } dassert1(1,rc==0,rc); THREAD_UNLOCK(armci_user_threads.net_lock); } /*\ * posts a bunch of gather descriptors \*/ static sr_descr_t client_blocking_gather_dscr; void armci_post_gather(void *src_ptr, int src_stride_arr[], int count[], int stride_levels, armci_vapi_memhndl_t *mhandle, int proc,int nbtag, int type, sr_descr_t **srd) { int i; int total_of_2D = 1; int index[MAX_STRIDE_LEVEL], unit[MAX_STRIDE_LEVEL]; int j,k,y; char *src, *src1; int num_xmit = 0, num_seg, max_seg, rem_seg,vecind; sr_descr_t *pend_dscr; struct ibv_sge *gat_sglist; struct ibv_send_wr *gat_dscr; if((type==SERV && DEBUG_SERVER) || (type==CLN && DEBUG_CLN)){ printf("%d(%d) : inside post_gather\n",armci_me,type); fflush(stdout); } max_seg = armci_max_num_sg_ent; if(nbtag){ pend_dscr = armci_vapi_get_next_sdescr(nbtag,1); if(srd!=NULL)*srd=pend_dscr; } else{ pend_dscr = &client_blocking_gather_dscr; pend_dscr->sdescr.wr_id=DSCRID_SCATGAT + MAX_PENDING; } pend_dscr->numofsends=0; gat_dscr = &pend_dscr->sdescr; gat_sglist = pend_dscr->sg_entry; gat_dscr->opcode = IBV_WR_SEND; gat_dscr->send_flags = IBV_SEND_SIGNALED; gat_dscr->sg_list = gat_sglist; gat_dscr->num_sge = 0; /* gat_dscr->send_flags = 0; */ index[2] = 0; unit[2] = 1; if(stride_levels > 1){ total_of_2D = count[2]; for(j=3; j<=stride_levels; j++){ index[j] = 0; unit[j] = unit[j-1]*count[j-1]; total_of_2D*=count[j]; } } num_xmit = total_of_2D*count[1]/max_seg; rem_seg = (total_of_2D*count[1])%max_seg; if(num_xmit == 0) num_xmit = 1; else if(rem_seg!= 0)num_xmit++; if((type==SERV && DEBUG_SERVER) || (type==CLN && DEBUG_CLN) ){ printf("%d(%d):armci_post_gather total_2D=%d, num_xmit=%d, rem_seg =%d, count[1] = %d\n",armci_me,type,total_of_2D, num_xmit,rem_seg,count[1]); fflush(stdout); } k=0; vecind = 0; if(rem_seg!=0 && k==(num_xmit-1))num_seg = rem_seg; else num_seg = max_seg; y=0; for(i=0;i= count[j]) index[j] =0; } src1 = src; for(j=0; jnumofsends++; /* the previous one has been posted, start off new*/ gat_dscr->num_sge = 0; y = 0; vecind=0;k++; if(rem_seg!=0 && k==(num_xmit-1))num_seg = rem_seg; } /* fill the gather descriptor */ gat_sglist[y].addr = (uint64_t)src1; gat_sglist[y].lkey = mhandle->lkey; gat_sglist[y].length = count[0]; gat_dscr->num_sge++; src1 += src_stride_arr[0]; y++; } if(vecind == num_seg){ posts_gather_desc(pend_dscr,proc,type); pend_dscr->numofsends++; if((type==SERV && DEBUG_SERVER) || (type==CLN && DEBUG_CLN) ){ printf("%d(%d)posts_gather_desc done\n",armci_me,type); fflush(stdout); } /* the previous one has been posted, start off new*/ gat_dscr->num_sge = 0; y = 0; vecind = 0; k++; if(rem_seg!=0 && k==(num_xmit-1))num_seg=rem_seg; else num_seg = max_seg; } } /* printf("%d: num gathers posted =%d\n",armci_me,pend_dscr->numofsends); */ if(!nbtag){ /*complete here*/ armci_send_complete(&pend_dscr->sdescr,"armci_post_gather",pend_dscr->numofsends); } return; } /***********************END SCATTER GATHER STUFF******************************/ /***********************SPECIAL SEND/RECV*************************************/ void armci_server_direct_send(int dst, char *src_buf, char *dst_buf, int len, uint32_t *lkey, uint32_t *rkey) { int rc = 0; struct ibv_wc *pdscr=NULL; struct ibv_wc pdscr1; struct ibv_send_wr sdscr; struct ibv_sge ssg_entry; pdscr = &pdscr1; if(DEBUG_SERVER){ printf("\n%d(s):sending dir data to client %d at %p bytes=%d last=%p\n", armci_me,dst,dst_buf,len,(dst_buf+len-4));fflush(stdout); } memset(&sdscr,0,sizeof(struct ibv_send_wr)); armci_init_cbuf_srdma(&sdscr,&ssg_entry,src_buf,dst_buf,len,NULL,NULL); sdscr.wr.rdma.rkey = *rkey; ssg_entry.lkey = *lkey; sdscr.wr_id = dst+armci_nproc; struct ibv_send_wr *bad_wr; rc = ibv_post_send((CLN_con+dst)->qp, &sdscr, &bad_wr); dassert1(1,rc==0,rc); while (rc == 0) { rc = ibv_poll_cq(CLN_nic->scq, 1, pdscr); } dassertp(1,rc>=0,("%d: rc=%d id=%d status=%d\n", armci_me,rc,(int)pdscr->wr_id,pdscr->status)); dassert1(1,pdscr->status==IBV_WC_SUCCESS,pdscr->status);; } void armci_send_contig_bypass(int proc, request_header_t *msginfo, void *src_ptr, void *rem_ptr, int bytes) { int *last; uint32_t *lkey=NULL; uint32_t *rkey; int dscrlen = msginfo->dscrlen; last = (int*)(((char*)(src_ptr)) + (bytes - sizeof(int))); dassertp(1,msginfo->pinned,("%d: not pinned proc=%d",armci_me,proc)); rkey = (uint32_t *)((char *)(msginfo+1)+dscrlen-(sizeof(uint32_t)+sizeof(uint32_t))); if(DEBUG_SERVER){ printf("%d(server): sending data bypass to %d (%p,%p) %d %d\n", armci_me, msginfo->from,src_ptr, rem_ptr,*lkey,*rkey); fflush(stdout); } armci_server_direct_send(msginfo->from,src_ptr,rem_ptr,bytes,lkey,rkey); if(*last == ARMCI_STAMP){ SERVER_SEND_ACK(msginfo->from); } } void armci_rcv_strided_data_bypass_both(int proc, request_header_t *msginfo, void *ptr, int *count, int stride_levels) { int datalen = msginfo->datalen; int *last; long *ack; int loop=0; if(DEBUG_CLN){ printf("%d:rcv_strided_data_both bypass from %d\n", armci_me, proc); fflush(stdout); } if(!stride_levels){ last = (int*)(((char*)(ptr)) + (count[0] -sizeof(int))); ack = (long *)&msginfo->tag; while(armci_util_int_getval(last) == ARMCI_STAMP && armci_util_long_getval(ack) != ARMCI_STAMP){ loop++; loop %=1000000; if(loop==0){ if(DEBUG_CLN){ printf("%d: client last(%p)=%d ack(%p)=%ld off=%d\n", armci_me,(void*)last,*last,(void*)ack,*ack,(int)((char*)last - (char*)ptr)); fflush(stdout); } } } } else { printf("\n%d:rcv_strided_data called, it should never be called\n",armci_me); armci_dscrlist_recv_complete(0,"armci_rcv_strided_data_bypass_both",NULL); } if(DEBUG_CLN){printf("%d:rcv_strided_data bypass both: %d bytes from %d\n", armci_me, datalen, proc); fflush(stdout); } } int armci_pin_memory(void *ptr, int stride_arr[], int count[], int strides) { fprintf(stderr, "[%d]:armci_pin_memory not implemented\n",armci_me); fflush(stderr); return 0; } void armci_client_send_ack(int proc, int n) { printf("\n%d:client_send_ack not implemented",armci_me);fflush(stdout); } void armci_rcv_strided_data_bypass(int proc, request_header_t* msginfo, void *ptr, int stride_levels) { printf("\n%d:armci_rcv_strided_data_bypass not implemented",armci_me); fflush(stdout); } void armci_unpin_memory(void *ptr, int stride_arr[], int count[], int strides) { printf("\n%d:armci_unpin_memory not implemented",armci_me);fflush(stdout); } int armcill_server_wait_ack(int proc, int n) { printf("\n%d:armcill_server_wait_ack not implemented",armci_me); fflush(stdout); return(0); } void armcill_server_put(int proc, void* s, void *d, int len) { printf("\n%d:armcill_server_put not implemented",armci_me);fflush(stdout); } /*\ * initialising the atomic send descriptor \*/ void armci_init_vapibuf_atomic(struct ibv_send_wr *sd, struct ibv_sge *sg, int op, int*ploc,int *prem, int extra, int id,ARMCI_MEMHDL_T *lhandle, ARMCI_MEMHDL_T *rhandle) { if (1) { printf("%d(c) : entered armci_init_vapibuf_atomic\n",armci_me); fflush(stdout); } memset(sd,0,sizeof(struct ibv_send_wr)); if (op == ARMCI_FETCH_AND_ADD_LONG ) { printf("%d(c) :setting opcode for snd dscr to FETCH_AND_ADD\n",armci_me); sd->opcode = IBV_WR_ATOMIC_FETCH_AND_ADD; sd->wr.atomic.compare_add = (uint64_t)extra; } else if(op == ARMCI_SWAP_LONG){ sd->opcode = IBV_WR_ATOMIC_CMP_AND_SWP; sd->wr.atomic.swap = (uint64_t)extra; } sd->send_flags = IBV_SEND_SIGNALED; sg->length = 8; /* 64 bit atomic*/ printf("--------\n"); sg->addr= (uint64_t)(void *)ploc; if(lhandle) sg->lkey = lhandle->lkey; sd->sg_list = sg; sd->num_sge = 1; sd->wr.atomic.remote_addr = (uint64_t)(void *)prem; if(rhandle) sd->wr.atomic.rkey = rhandle->rkey; /* how do we get the remote key */ sd->wr_id = DSCRID_RMW + armci_me; if(1){ printf("%d(c) : finished initialising atomic send desc id is %ld,armci_ime = %d\n",armci_me,sd->wr_id,armci_me); fflush(stdout); } } /*\ * using vapi remote atomic operations \*/ void client_rmw_complete(struct ibv_send_wr *snd_dscr, char *from) { int rc = 0; struct ibv_wc pdscr1; struct ibv_wc *pdscr=&pdscr1; printf("%d(c) : inside client_rmw_complete\n",armci_me); do { while (rc == 0) { rc = ibv_poll_cq(CLN_nic->scq, 1, pdscr); } dassertp(DBG_POLL|DBG_ALL,rc>=0, ("%d: rc=%d id=%d status=%d\n", armci_me,rc,pdscr->wr_id,pdscr->status)); dassert1(1,pdscr->status==IBV_WC_SUCCESS,pdscr->status); rc = 0; } while(pdscr->wr_id != snd_dscr->wr_id); } void armci_direct_rmw(int op, int*ploc, int *prem, int extra, int proc, ARMCI_MEMHDL_T *lhandle, ARMCI_MEMHDL_T *rhandle) { int rc = 0; struct ibv_send_wr *sd; struct ibv_sge *sg; armci_connect_t *con; con = CLN_con+proc; sd = &(rmw[armci_me].rmw_dscr); sg = &(rmw[armci_me].rmw_entry); if (1) { printf("%d(c) : about to call armci_init_vapibuf_atomic\n",armci_me); fflush(stdout); } armci_init_vapibuf_atomic(sd, sg, op,ploc,prem,extra,proc,lhandle,rhandle); if (1) { printf("%d(c) : finished armci_init_vapibuf_atomic\n",armci_me); fflush(stdout); } struct ibv_send_wr * bad_wr; rc = ibv_post_send(con->qp, sd, &bad_wr); dassert1(1,rc==0,rc); if (1) { printf("%d(c) : finished posting desc\n",armci_me); fflush(stdout); } /*armci_send_complete(sd,"send_remote_atomic");*/ client_rmw_complete(sd,"send_remote_atomic"); return; } struct node *dto_q = NULL; void process_con_break_from_client(armci_ud_rank *h, cbuf *v) { armci_connect_t *con = CLN_con + h->src_rank; cbuf *v1 = get_cbuf(); assert( v1 != NULL); v1->desc.u.sr.wr.ud.remote_qpn = rbuf.qp_num[h->src_rank]; v1->desc.u.sr.wr.ud.remote_qkey = 0; v1->desc.u.sr.wr.ud.ah = conn.ud_ah[h->src_rank]; armci_ud_rank *h1 = (armci_ud_rank *)CBUF_BUFFER_START(v1); h1->src_rank = armci_me; h1->qpnum = con->sqpnum; h1->msg_type = QP_CON_BREAK_FROM_SERVER; cbuf_init_send(v1, sizeof(armci_ud_rank)); /* Release the receiving cbuf */ release_cbuf(v); struct ibv_send_wr *send_wr; if(ibv_post_send(conn.qp[0], &(v1->desc.u.sr), &send_wr)) { fprintf(stderr, "Error posting send\n"); } } void process_con_break_from_server(armci_ud_rank *h, cbuf *v) { armci_connect_t *con = SRV_con + armci_clus_id(h->src_rank); con->state = QP_INACTIVE; release_cbuf(v); } void process_recv_completion_from_client(armci_ud_rank *h, cbuf *v) { struct ibv_qp_attr qp_attr; enum ibv_qp_attr_mask qp_attr_mask; struct ibv_recv_wr *bad_wr; int rc, j; static int qp_flag = 1; if (qp_flag == 1) { total_active_conn_to_client = 0; qp_flag = 0; } total_active_conn_to_client++; armci_connect_t *con = CLN_con + h->src_rank; if (con->state == QP_ACTIVE) { qp_attr_mask = IBV_QP_STATE; memset(&qp_attr, 0, sizeof qp_attr); qp_attr.qp_state = IBV_QPS_SQD; if (ibv_modify_qp(con->qp, &qp_attr, qp_attr_mask)) { fprintf(stdout," Error modifying QP\n"); fflush(stdout); } if (ibv_destroy_qp(con->qp)) { printf("Error destroying QP\n"); } total_active_conn_to_client--; } armci_create_qp(CLN_nic, &con->qp); con->sqpnum = con->qp->qp_num; con->lid = CLN_nic->lid_arr[h->src_rank]; CLN_rqpnumtmpbuf[h->src_rank] = con->qp->qp_num; qp_attr_mask = IBV_QP_STATE | IBV_QP_PKEY_INDEX | IBV_QP_PORT | IBV_QP_ACCESS_FLAGS; memset(&qp_attr, 0, sizeof qp_attr); qp_attr.qp_state = IBV_QPS_INIT; qp_attr.pkey_index = DEFAULT_PKEY_IX; qp_attr.port_num = CLN_nic->active_port; qp_attr.qp_access_flags = IBV_ACCESS_LOCAL_WRITE| IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ; rc = ibv_modify_qp(con->qp, &qp_attr, qp_attr_mask); memset(&qp_attr, 0, sizeof qp_attr); qp_attr_mask = IBV_QP_STATE | IBV_QP_MAX_DEST_RD_ATOMIC | IBV_QP_PATH_MTU | IBV_QP_RQ_PSN | IBV_QP_MIN_RNR_TIMER; qp_attr.qp_state = IBV_QPS_RTR; qp_attr.path_mtu = IBV_MTU_1024; /*MTU*/ qp_attr.max_dest_rd_atomic = 4; qp_attr.min_rnr_timer = RNR_TIMER; qp_attr.rq_psn = 0; /* Fill in the information from the header */ SRV_rqpnums[h->src_rank] = h->qpnum; qp_attr_mask |= IBV_QP_DEST_QPN | IBV_QP_AV; qp_attr.dest_qp_num = SRV_rqpnums[h->src_rank]; qp_attr.ah_attr.dlid = SRV_nic->lid_arr[h->src_rank]; qp_attr.ah_attr.port_num = CLN_nic->active_port; rc = ibv_modify_qp(con->qp, &qp_attr, qp_attr_mask); memset(&qp_attr, 0, sizeof qp_attr); qp_attr_mask = IBV_QP_STATE | IBV_QP_SQ_PSN | IBV_QP_TIMEOUT | IBV_QP_RETRY_CNT | IBV_QP_RNR_RETRY | IBV_QP_MAX_QP_RD_ATOMIC; qp_attr.qp_state = IBV_QPS_RTS; qp_attr.sq_psn = 0; qp_attr.timeout = 18; qp_attr.retry_cnt = 7; qp_attr.rnr_retry = 7; qp_attr.max_rd_atomic = 4; rc = ibv_modify_qp(con->qp, &qp_attr,qp_attr_mask); #if defined(PEND_BUFS) assert(armci_nproc*(IMM_BUF_NUM+1)src_rank*(IMM_BUF_NUM+1)+j]; armci_init_vapibuf_recv(&cbuf->dscr, &cbuf->sg_entry, cbuf->buf, IMM_BUF_LEN, &serv_memhandle); cbuf->dscr.wr_id = h->src_rank*(IMM_BUF_NUM+1)+j + DSCRID_IMMBUF_RECV; if (DEBUG_SERVER) { printf("\n%d(s):posted rr with lkey=%d",armci_me,cbuf->sg_entry.lkey); fflush(stdout); } if (armci_use_srq) { rc = ibv_post_srq_recv(CLN_srq_hndl, &cbuf->dscr, &bad_wr); } else { rc = ibv_post_recv((CLN_con + h->src_rank)->qp, &cbuf->dscr, &bad_wr); } dassert1(1,rc==0,rc); } #else int i; for(i = 0; i < armci_nproc; i++) { vapibuf_t *cbuf; cbuf = serv_buf_arr[h->src_rank]; armci_init_vapibuf_recv(&cbuf->dscr, &cbuf->sg_entry, cbuf->buf, CBUF_DLEN, &serv_memhandle); cbuf->dscr.wr_id = h->src_rank+armci_nproc; if (DEBUG_SERVER) { printf("\n%d(s):posted rr with lkey=%d",armci_me,cbuf->sg_entry.lkey); fflush(stdout); } if (armci_use_srq) { rc = ibv_post_srq_recv(CLN_srq_hndl, &cbuf->dscr, &bad_wr); } else { rc = ibv_post_recv((CLN_con+h->src_rank)->qp, &cbuf->dscr, &bad_wr); } dassert1(1,rc==0,rc); } #endif /* Now send back the information */ struct cbuf *v1 = get_cbuf(); assert( v1 != NULL); v1->desc.u.sr.wr.ud.remote_qpn = rbuf.qp_num[h->src_rank]; v1->desc.u.sr.wr.ud.remote_qkey = 0; v1->desc.u.sr.wr.ud.ah = conn.ud_ah[h->src_rank]; armci_ud_rank *h1 = (armci_ud_rank *)CBUF_BUFFER_START(v1); h1->src_rank = armci_me; h1->qpnum = con->sqpnum; h1->msg_type = 2; cbuf_init_send(v1, sizeof(armci_ud_rank)); /* Release the receiving cbuf */ release_cbuf(v); struct ibv_send_wr *send_wr; if(ibv_post_send(conn.qp[0], &(v1->desc.u.sr), &send_wr)) { fprintf(stderr, "Error posting send\n"); } con->state = QP_ACTIVE; } void progress_engine() { int ne; struct ibv_wc wc; void *cbuf_addr; cbuf *v; do { ne = ibv_poll_cq(hca.cq, 1, &wc); } while (ne < 1); /* Okay, got an entry, check for errors */ if(ne < 0) { fprintf(stderr,"Error Polling CQ\n"); } if(wc.status != IBV_WC_SUCCESS) { fprintf(stderr, "[%d] Failed status %d\n", armci_me, wc.status); } cbuf_addr = (void *) ((aint_t) wc.wr_id); assert(cbuf_addr != NULL); v = (cbuf *)cbuf_addr; armci_ud_rank *h = (armci_ud_rank *)(CBUF_BUFFER_START(v) + 40); if(IBV_WC_SEND == wc.opcode || IBV_WC_RDMA_WRITE == wc.opcode) { release_cbuf(v); /* Do nothing, just release the cbuf */ /* Send Completion */ } else if (IBV_WC_RECV == wc.opcode) { /* Recv completion */ post_recv(); /* Check if the message received is from a data server */ assert((h->msg_type == QP_CON_REQ) || (h->msg_type == QP_CON_ACK) || (h->msg_type == QP_CON_BREAK_FROM_CLIENT) || (h->msg_type == QP_CON_BREAK_FROM_SERVER)); if (h->msg_type == QP_CON_REQ) { process_recv_completion_from_client(h, v); } else if (h->msg_type == QP_CON_BREAK_FROM_CLIENT) { process_con_break_from_client(h, v); } else if (h->msg_type == QP_CON_BREAK_FROM_SERVER) { process_con_break_from_server(h, v); } else { process_recv_completion_from_server(h, v); } } else { fprintf(stderr, "Unknown opcode recv'd\n"); } } void* async_thread_ud_events(void *context) { while (1) { progress_engine(); } return NULL; } #if 0 static struct ibv_srq *create_srq(vapi_nic_t *nic) { struct ibv_srq_init_attr srq_init_attr; struct ibv_srq *srq_ptr = NULL; memset(&srq_init_attr, 0, sizeof(srq_init_attr)); srq_init_attr.srq_context = nic->handle; #ifdef PEND_BUFS srq_init_attr.attr.max_wr = armci_nproc * (IMM_BUF_NUM + 1) + 200; #else srq_init_attr.attr.max_wr = armci_nproc + 200; #endif srq_init_attr.attr.max_sge = 1; /* The limit value should be ignored during SRQ create */ srq_init_attr.attr.srq_limit = 30; srq_ptr = ibv_create_srq(nic->ptag, &srq_init_attr); return srq_ptr; } #endif int init_params(void) { conn.qp = (struct ibv_qp **) malloc(armci_nproc * sizeof(struct ibv_qp *)); conn.lid = (uint16_t *) malloc(armci_nproc * sizeof(int)); conn.qp_num = (uint32_t *) malloc(armci_nproc * sizeof(int)); conn.ud_ah = (struct ibv_ah **) malloc (armci_nproc * sizeof (struct ibv_ah *)); conn.status = (int *) malloc(armci_nproc * sizeof(int)); memset((void *)conn.status, 0, sizeof(int) * armci_nproc); rbuf.qp_num = (uint32_t *) malloc(armci_nproc * sizeof(int)); rbuf.lid = (uint16_t *) malloc(armci_nproc * sizeof(int)); rbuf.rkey = (uint32_t *) malloc(armci_nproc * sizeof(int)); rbuf.buf = (char **) malloc(armci_nproc * sizeof(char *)); assert(conn.qp && conn.lid && rbuf.qp_num && rbuf.lid && rbuf.rkey && rbuf.buf); return 0; } int open_hca(void) { struct ibv_device **dev_list; int num_hcas; dev_list = ibv_get_device_list(&num_hcas); hca.ib_dev = dev_list[0]; hca.context = ibv_open_device(hca.ib_dev); if(!hca.context) { fprintf(stderr,"Couldn't get context %s\n", ibv_get_device_name(hca.ib_dev)); return 1; } hca.pd = ibv_alloc_pd(hca.context); if(!hca.pd) { fprintf(stderr,"Couldn't get pd %s\n", ibv_get_device_name(hca.ib_dev)); return 1; } return 0; } int create_cq(void) { hca.cq = ibv_create_cq(hca.context, 16000, NULL, NULL, 0); if(!hca.cq) { fprintf(stderr, "Couldn't create CQ\n"); return 1; } if (armci_me == armci_master) { pthread_create(&armci_async_thread[1], NULL, &async_thread_ud_events, (void *) hca.context); } return 0; } int get_lid(void) { struct ibv_port_attr port_attr[2]; int i, j; for (j = 1; j <= 1; j++) { if (!ibv_query_port(hca.context, j, &port_attr[j - 1]) && (port_attr[j - 1].state == IBV_PORT_ACTIVE)) { for (i = 0; i < armci_nproc; i++) conn.lid[i] = port_attr[j - 1].lid; return 0; } } return 1; } int exch_addr(void) { MPI_Allgather((void *)conn.qp_num, sizeof(uint32_t), MPI_BYTE, (void *)rbuf.qp_num, sizeof(uint32_t), MPI_BYTE, ARMCI_COMM_WORLD); MPI_Alltoall((void *)conn.lid, sizeof(uint16_t), MPI_BYTE, (void *)rbuf.lid, sizeof(uint16_t), MPI_BYTE, ARMCI_COMM_WORLD); #ifdef DEBUG for (i = 0; i < nprocs; i++) { if (me == i) continue; fprintf(stdout,"[%d] Remote QP %d, Remote LID %u, Rkey %u, Lkey %u\n" " LBuf %p, RBuf %p\n", me, rbuf.qp_num[i], rbuf.lid[i], rbuf.rkey[i], lbuf.mr->lkey, lbuf.buf, rbuf.buf[i]); fflush(stdout); } #endif return 0; } int create_qp(void) { struct ibv_qp_attr qp_attr; memset(&qp_attr, 0, sizeof qp_attr); struct ibv_qp_init_attr attr = { .send_cq = hca.cq, .recv_cq = hca.cq, .cap = { .max_send_wr = 8192, .max_recv_wr = 8192, .max_send_sge = 1, .max_recv_sge = 1, .max_inline_data = 0 }, .qp_type = IBV_QPT_UD }; conn.qp[0] = ibv_create_qp(hca.pd, &attr); if(!conn.qp[0]) { fprintf(stderr,"Couldn't create QP\n"); return 1; } conn.qp_num[0] = conn.qp[0]->qp_num; qp_attr.qp_state = IBV_QPS_INIT; qp_attr.pkey_index = 0; qp_attr.port_num = 1; qp_attr.qkey = 0; if(ibv_modify_qp(conn.qp[0], &qp_attr, IBV_QP_STATE | IBV_QP_PKEY_INDEX | IBV_QP_PORT | IBV_QP_QKEY)) { fprintf(stderr,"Could not modify QP to INIT\n"); return 1; } #ifdef DEBUG fprintf(stdout,"[%d] Created QP %d, LID %d\n", me, conn.qp_num[0], conn.lid[0]); fflush(stdout); #endif return 0; } struct ibv_mr* armci_register_memory(void* buf, int len) { return (ibv_reg_mr(hca.pd, buf, len, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ)); } int connect_qp(void) { struct ibv_qp_attr attr; int i; memset(&attr, 0 , sizeof attr); attr.qp_state = IBV_QPS_RTR; if (ibv_modify_qp(conn.qp[0], &attr, IBV_QP_STATE)) { fprintf(stderr, "Failed to modify QP to RTR\n"); return 1; } attr.qp_state = IBV_QPS_RTS; attr.sq_psn = 0; if (ibv_modify_qp(conn.qp[0], &attr, IBV_QP_STATE | IBV_QP_SQ_PSN)) { fprintf(stderr, "Failed to modify QP to RTS\n"); return 1; } for (i = 0; i < armci_nproc; i++) { struct ibv_ah_attr ah_attr; memset(&ah_attr, 0, sizeof(ah_attr)); ah_attr.is_global = 0; ah_attr.dlid = rbuf.lid[i]; ah_attr.sl = 0; ah_attr.src_path_bits = 0; ah_attr.port_num = 1; conn.ud_ah[i] = ibv_create_ah(hca.pd, &ah_attr); if (!conn.ud_ah[i]) { fprintf(stderr, "Error creating address handles\n"); } } return 0; } int total_ud_recv_buffers = 4096; void post_recv() { cbuf *v = get_cbuf(); cbuf_init_recv(v, CBUF_BUFFER_SIZE); struct ibv_recv_wr *bad_wr; /* set id to be the global_rank */ v->grank = -1; if(ibv_post_recv(conn.qp[0], &(v->desc.u.rr), &bad_wr)) { fprintf(stderr," Error posting UD recv\n"); fflush(stderr); } } int post_buffers() { init_cbuf_lock(); allocate_cbufs(8192); int i; for (i = 0; i < total_ud_recv_buffers; i++) { post_recv(); } return 0; } void test_connectivity(void) { int i; struct ibv_send_wr *bad_wr; cbuf *v = NULL; for (i = 0; i < armci_nproc;i++) { if (armci_me == i) continue; int j; for (j = 0 ; j < 2; j++) { v = get_cbuf(); assert(v != NULL); v->desc.u.sr.wr.ud.remote_qpn = rbuf.qp_num[i]; v->desc.u.sr.wr.ud.remote_qkey = 0; v->desc.u.sr.wr.ud.ah = conn.ud_ah[i]; armci_ud_rank *h = (armci_ud_rank *)CBUF_BUFFER_START(v); h->src_rank = armci_me; cbuf_init_send(v, sizeof(armci_ud_rank)); if(ibv_post_send(conn.qp[0], &(v->desc.u.sr), &bad_wr)) { fprintf(stderr, "Error posting send\n"); } } } } #if 0 int recycle_dead_qp() { } #endif void handle_network_fault(struct ibv_wc *pdscr) { #if 0 recycle_dead_qp(); #endif } void setup_ud_channel() { if(init_params()) { } if(open_hca()) { } if(create_cq()) { } if(get_lid()) { } if(create_qp()) { } if (exch_addr()) { } if(connect_qp()) { } MPI_Barrier(ARMCI_COMM_WORLD); if(post_buffers()) { } MPI_Barrier(ARMCI_COMM_WORLD); } void process_recv_completion_from_server(armci_ud_rank *h, cbuf *v) { struct ibv_qp_attr qp_attr; enum ibv_qp_attr_mask qp_attr_mask; int rc; armci_connect_t *con = SRV_con + armci_clus_id(h->src_rank); qp_attr_mask = IBV_QP_STATE | IBV_QP_PKEY_INDEX | IBV_QP_PORT | IBV_QP_ACCESS_FLAGS; memset(&qp_attr, 0, sizeof qp_attr); qp_attr.qp_state = IBV_QPS_INIT; qp_attr.pkey_index = DEFAULT_PKEY_IX; qp_attr.port_num = SRV_nic->active_port; qp_attr.qp_access_flags = IBV_ACCESS_LOCAL_WRITE| IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ; rc = ibv_modify_qp(con->qp, &qp_attr, qp_attr_mask); assert(0 == rc); memset(&qp_attr, 0, sizeof qp_attr); qp_attr_mask = IBV_QP_STATE | IBV_QP_MAX_DEST_RD_ATOMIC | IBV_QP_PATH_MTU | IBV_QP_RQ_PSN | IBV_QP_MIN_RNR_TIMER; qp_attr.qp_state = IBV_QPS_RTR; qp_attr.path_mtu = IBV_MTU_1024; /*MTU*/ qp_attr.max_dest_rd_atomic = 4; qp_attr.min_rnr_timer = RNR_TIMER; qp_attr.rq_psn = 0; /* Fill in the information from the header */ CLN_rqpnums[h->src_rank] = h->qpnum; qp_attr_mask |= IBV_QP_DEST_QPN | IBV_QP_AV; qp_attr.dest_qp_num = CLN_rqpnums[h->src_rank]; qp_attr.ah_attr.dlid = SRV_nic->lid_arr[h->src_rank]; qp_attr.ah_attr.port_num = SRV_nic->active_port; rc = ibv_modify_qp(con->qp, &qp_attr, qp_attr_mask); assert(0 == rc); memset(&qp_attr, 0, sizeof qp_attr); qp_attr_mask = IBV_QP_STATE | IBV_QP_SQ_PSN | IBV_QP_TIMEOUT | IBV_QP_RETRY_CNT | IBV_QP_RNR_RETRY | IBV_QP_MAX_QP_RD_ATOMIC; qp_attr.qp_state = IBV_QPS_RTS; qp_attr.sq_psn = 0; qp_attr.timeout = 18; qp_attr.retry_cnt = 7; qp_attr.rnr_retry = 7; qp_attr.max_rd_atomic = 4; rc = ibv_modify_qp(con->qp, &qp_attr,qp_attr_mask); assert(0 == rc); release_cbuf(v); con->state = QP_ACTIVE; } struct con_q_t { struct armci_connect_t *head; void *next; }; struct con_q_t *con_q; #define MAX_CLIENT_TO_SERVER_CONN 2 armci_connect_t * dequeue_conn() { return NULL; } int get_max_client_to_server_conn() { static int env_read_flag = 0; static int max_client_to_server_conn = 0; if (!env_read_flag) { char *value; if ((value = getenv("ARMCI_MAX_CLIENT_TO_SERVER_FACTOR")) != NULL){ max_client_to_server_conn = armci_nclus/atoi(value); /* We need a minimum of 4 connections */ if (max_client_to_server_conn <= 1) max_client_to_server_conn = 2; if (armci_me == 0) { fprintf(stdout, "max_client_to_server_conn[%d]\n", max_client_to_server_conn); } } else { max_client_to_server_conn = armci_nclus / 2; if (max_client_to_server_conn <= 1) max_client_to_server_conn = 2; } } return max_client_to_server_conn; } int get_the_victim_connection() { return 0; } void break_a_connection_if_needed() { assert(!SERVER_CONTEXT); if (!armci_use_lazy_break) return; int max_client_to_server_conn = get_max_client_to_server_conn(); if ((total_active_conn_to_server >= max_client_to_server_conn) && (armci_me != armci_master)) { int proc, clus_id; do { proc = get_the_victim_connection(); /* Not enough on the queue */ if (proc == -1) return; clus_id = armci_clus_id(proc); } while ((armci_clus_me == clus_id) || ((SRV_con + clus_id)->state != QP_ACTIVE)); ARMCI_WaitAll(); ARMCI_Fence(proc); struct ibv_qp_attr qp_attr; enum ibv_qp_attr_mask qp_attr_mask; armci_connect_t *con = SRV_con + clus_id; dassert(1, con->state == QP_ACTIVE); qp_attr_mask = IBV_QP_STATE; memset(&qp_attr, 0, sizeof qp_attr); qp_attr.qp_state = IBV_QPS_SQD; if (ibv_modify_qp(con->qp, &qp_attr, qp_attr_mask)) { fprintf(stdout," Error modifying QP\n"); fflush(stdout); } cbuf *v = get_cbuf(); assert(v != NULL); v->desc.u.sr.wr.ud.remote_qpn = rbuf.qp_num[armci_clus_info[clus_id].master]; v->desc.u.sr.wr.ud.remote_qkey = 0; v->desc.u.sr.wr.ud.ah = conn.ud_ah[armci_clus_info[clus_id].master]; armci_ud_rank *h = (armci_ud_rank *)CBUF_BUFFER_START(v); h->src_rank = armci_me; h->qpnum = con->sqpnum; h->msg_type = QP_CON_BREAK_FROM_CLIENT; struct ibv_send_wr *bad_wr; cbuf_init_send(v, sizeof(armci_ud_rank)); if(ibv_post_send(conn.qp[0], &(v->desc.u.sr), &bad_wr)) { printf("Error posting send\n"); } while (con->state != QP_INACTIVE) { if (armci_me != armci_master) progress_engine(); } if (ibv_destroy_qp(con->qp)) { printf("Error destroying QP\n"); } } } void check_state_of_ib_connection(int proc, int force) { int clus_id; armci_connect_t *con; /* return if ARMCI does not use on demand connection management */ if (!armci_use_odcm) return; static int flag = 1; if (flag == 1) { total_active_conn_to_server = 0; total_breaks = 0; flag = 0; } /* Check clus id */ clus_id = armci_clus_id(proc); con = SRV_con + clus_id; assert(!SERVER_CONTEXT); if (con->state != QP_ACTIVE) { if (!force) break_a_connection_if_needed(); total_active_conn_to_server++; armci_create_qp(SRV_nic, &con->qp); con->sqpnum = con->qp->qp_num; con->lid = SRV_nic->lid_arr[clus_id]; cbuf *v = get_cbuf(); assert(v != NULL); v->desc.u.sr.wr.ud.remote_qpn = rbuf.qp_num[armci_clus_info[clus_id].master]; v->desc.u.sr.wr.ud.remote_qkey = 0; v->desc.u.sr.wr.ud.ah = conn.ud_ah[armci_clus_info[clus_id].master]; armci_ud_rank *h = (armci_ud_rank *)CBUF_BUFFER_START(v); h->src_rank = armci_me; h->qpnum = con->sqpnum; h->msg_type = QP_CON_REQ; struct ibv_send_wr *bad_wr; cbuf_init_send(v, sizeof(armci_ud_rank)); if(ibv_post_send(conn.qp[0], &(v->desc.u.sr), &bad_wr)) { printf("Error posting send\n"); } if (armci_me != armci_master) { while (con->state != QP_ACTIVE) { progress_engine(); } } else { while (con->state != QP_ACTIVE) { usleep(1); } } } } ga-5.9.2/armci/src/devices/openib/pendbufs.c000066400000000000000000000702771500715745200206730ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if defined(PEND_BUFS) #include "pendbufs.h" #include "armcip.h" #if HAVE_STDIO_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #define DEBUG_SERVER 0 /*-------------------Attributes-------------------------*/ /**Attributes to control buffer count and sizes. Implement this way to hide the global variables, and provide get/set methods.*/ #define NUM_ATTRIBUTES 4 #define ATTRIB_IMMBUF_LEN 0 #define ATTRIB_IMMBUF_NUM 1 #define ATTRIB_PNDBUF_LEN 2 #define ATTRIB_PNDBUF_NUM 3 /** List of hidden attributes and their operations. * @param attid IN Attribute id. Choose from the list above * @param gs IN Get(=0)/Set(=1) * @param v IN Value (used only when gs==1) * @return Value of the attribute on return */ static int att_ops(int attid, int gs, int v) { static not_first[NUM_ATTRIBUTES]; /*auto-init to zero*/ static val[NUM_ATTRIBUTES]; assert(attid>=0 && attidIMM_BUF_LEN*/ INIT=7, /**buf; proc_waitlist_t *info = &pbuf_proc_list_info[msginfo->from]; assert(msginfo->tag.imm_msg == 0); pbuf = _armci_serv_pendbuf_getbuf(); if(pbuf) { pbuf->status = INIT; pbuf->avail = 0; pbuf->vbuf = vbuf; memcpy(pbuf->buf, vbuf->buf, sizeof(request_header_t)+msginfo->dscrlen); /* pbuf_proc_list_info[msginfo->from].waiting_on=pbuf; */ pbuf->order_prev = info->order_tail; if(info->order_tail) info->order_tail->order_next = pbuf; info->order_tail = pbuf; if(!info->order_head) info->order_head = pbuf; } return pbuf; } /**Free a pending buffer * @param pbuf IN Pointer to Pending buffer to be freed * @return none */ static void _armci_serv_pendbuf_freebuf(pendbuf_t *pbuf){ const request_header_t *msginfo = (request_header_t *)pbuf->buf; proc_waitlist_t *info = &pbuf_proc_list_info[msginfo->from]; ARMCI_PR_DBG("enter",0); assert(pbuf != NULL); pbuf->avail=1; pbuf->status = -1; pbuf->vbuf = NULL; /* assert(info->waiting_on == pbuf); */ /* info->waiting_on = NULL; */ if(pbuf->order_prev) pbuf->order_prev->order_next = pbuf->order_next; if(pbuf->order_next) pbuf->order_next->order_prev = pbuf->order_prev; if(info->order_head == pbuf) { assert(pbuf->order_prev == NULL); info->order_head = pbuf->order_next; } if(info->order_tail == pbuf) { assert(pbuf->order_next == NULL); info->order_tail = pbuf->order_prev; } pbuf->order_prev = pbuf->order_next = NULL; /*not necessary here*/ _nPendBufsUsed -= 1; assert(_nPendBufsUsed>=0); ARMCI_PR_DBG("exit",0); } #if 0 /*Messages are processed in-place in immediate buffers or issued into pending buffers for progress in order (like ONE_PBUF_PER_MESG). This rule relaxes ONE_PBUF_PER_MESG by allowing ACCs to be processed in-place/issued without waiting for the prior reqs to complete*/ static int _can_progress_accnoorder(immbuf_t *vbuf) { const request_header_t *msginfo=(request_header_t*)vbuf->buf; const int proc = msginfo->from; const proc_waitlist_t *info = &pbuf_proc_list_info[proc]; int i, nwaiting_on, nacc; pendbuf_t *ptr; assert(_pbufOrder == ACC_NO_ORDER); if(!IS_IMM_MSG(*msginfo) && _nPendBufsUsed==PENDING_BUF_NUM) { /* printf("%d(s): op=%d from=%d datalen=%d waiting for pending buffers\n",armci_me,msginfo->operation,msginfo->from,msginfo->tag.data_len); */ /* fflush(stdout); */ return 0; /*This buffer needs a free pending buffer*/ } if(IS_IMM_MSG(*msginfo) && ARMCI_ACC(msginfo->operation)) { return 1; } if(info->immbuf_wlist_head && info->immbuf_wlist_head!=vbuf) { /* printf("%d(s): op=%d from=%d datalen=%d not queue head\n",armci_me,msginfo->operation,msginfo->from,msginfo->tag.data_len); */ /* fflush(stdout); */ return 0; /*in order issue*/ } if(!ARMCI_ACC(msginfo->operation)) { if(info->order_head) return 0; return 1; } int check = ARMCI_ACC(msginfo->operation); assert(check); for(ptr=info->order_head; ptr!=NULL; ptr=ptr->order_next) { request_header_t *m = (request_header_t *)ptr->buf; assert(m->from == msginfo->from); if(!ARMCI_ACC(m->operation)) break; } if(ptr != NULL) return 0; return 1; } static int _can_progress_putaccsplitorder(immbuf_t*vbuf) { if(!IS_IMM_MSG(*msginfo) && _nPendBufsUsed==PENDING_BUF_NUM) { return 0; /*This buffer needs a free pending buffer*/ } if(IS_IMM_MSG(*msginfo) && ARMCI_ACC(msginfo->operation)) { return 1; } if(info->immbuf_wlist_head && info->immbuf_wlist_head!=vbuf) { return 0; } if(msginfo->operation!=PUT && !ARMCI_ACC(msginfo->operation)) { if(info->order_head) return 0; return 1; } if(IS_IMM_MSG(*msginfo) && info->order_head) return 0; return 1; } #endif /** Implement ordering between messages. This function needs to be * implemented in conjunction with @_armci_serv_pendbuf_promote to * ensure ordered processing of messages. * @param vbuf IN Message in immediate buffer being checked * @return 1 if the message can be progressed (either in-place or * after copying to a pending buffer). 0 therwise. */ static int _armci_serv_pendbuf_can_progress(immbuf_t *vbuf) { const request_header_t *msginfo=(request_header_t*)vbuf->buf; const int proc = msginfo->from; const proc_waitlist_t *info = &pbuf_proc_list_info[proc]; if(_pbufOrder == ONE_PBUF_MESG) { /*Only one pending buffer used at any time*/ if(_nPendBufsUsed>0) return 0; return 1; } if(_pbufOrder == ONE_PBUF_MESG_PER_PROC) { /*Only one non-immediate mesg can be assigned to the pending buffers at any time*/ if(info->order_head || (info->immbuf_wlist_head && info->immbuf_wlist_head!=vbuf)) { return 0;/*other requests from this process remain*/ } if(!IS_IMM_MSG(*msginfo) && _nPendBufsUsed==PENDING_BUF_NUM) { return 0; /*This buffer needs a free pending buffer*/ } assert(info->n_pending == 0 || info->immbuf_wlist_head==vbuf); return 1; } if(_pbufOrder == ACC_NO_ORDER) { /*Messages are processed in-place in immediate buffers or issued into pending buffers for progress in order (like ONE_PBUF_PER_MESG). This rule relaxes ONE_PBUF_PER_MESG by allowing a sequence of ACCs to be processed in-place/issued without waiting for the prior ones to complete*/ int i, nwaiting_on, nacc; pendbuf_t *ptr; if(!IS_IMM_MSG(*msginfo) && _nPendBufsUsed==PENDING_BUF_NUM) { /* printf("%d(s): op=%d from=%d datalen=%d waiting for pending buffers\n",armci_me,msginfo->operation,msginfo->from,msginfo->tag.data_len); */ /* fflush(stdout); */ return 0; /*This buffer needs a free pending buffer*/ } #if 1 /*commented for now: it does work*/ if(IS_IMM_MSG(*msginfo) && ARMCI_ACC(msginfo->operation)) { return 1; } #endif if(info->immbuf_wlist_head && info->immbuf_wlist_head!=vbuf) { /* printf("%d(s): op=%d from=%d datalen=%d not queue head\n",armci_me,msginfo->operation,msginfo->from,msginfo->tag.data_len); */ /* fflush(stdout); */ return 0; /*in order issue*/ } if(!ARMCI_ACC(msginfo->operation)) { if(info->order_head) return 0; return 1; } assert(ARMCI_ACC(msginfo->operation)); for(ptr=info->order_head; ptr!=NULL; ptr=ptr->order_next) { request_header_t *m = (request_header_t *)ptr->buf; assert(m->from == msginfo->from); if(!ARMCI_ACC(m->operation)) break; } if(ptr != NULL) return 0; return 1; } if(_pbufOrder == PUTACC_SPLIT_ORDER) { if(!IS_IMM_MSG(*msginfo) && _nPendBufsUsed==PENDING_BUF_NUM) { return 0; /*This buffer needs a free pending buffer*/ } if(info->immbuf_wlist_head && info->immbuf_wlist_head!=vbuf) { return 0; } if(msginfo->operation!=PUT && !ARMCI_ACC(msginfo->operation)) { if(info->order_head) return 0; return 1; } if(IS_IMM_MSG(*msginfo) && ARMCI_ACC(msginfo->operation)) { return 1; } if(IS_IMM_MSG(*msginfo) && info->order_head) return 0; return 1; } if(_pbufOrder == GET_GET_REORDER) { if(!IS_IMM_MSG(*msginfo) && _nPendBufsUsed==PENDING_BUF_NUM) { return 0; /*This buffer needs a free pending buffer*/ } if(IS_IMM_MSG(*msginfo) && ARMCI_ACC(msginfo->operation)) { return 1; } if(info->immbuf_wlist_head && info->immbuf_wlist_head!=vbuf) { return 0; } if(msginfo->operation!=PUT && !ARMCI_ACC(msginfo->operation)) { if(info->order_tail) { request_header_t *m=(request_header_t*)info->order_tail->buf; if(msginfo->operation==GET && m->operation == GET) { /* printf("%d: Get Get progressing\n", armci_me); */ return 1; } return 0; } return 1; } if(IS_IMM_MSG(*msginfo) && info->order_head) return 0; return 1; } armci_die("Unknown pbuf ordering rule",_pbufOrder); return 0; } /** Goes through the set of immediate buffers waiting to be processed * and completed, and identifies a buffer that can be processed * now. Removes it from the list and returns it. Promote also * considers availability of pending buffers if need be. * @return Pointer to buffer that can be processed now. NULL if none exists. */ static immbuf_t* _armci_serv_pendbuf_promote() { immbuf_t *immbuf = NULL; proc_waitlist_t *info; ARMCI_PR_DBG("enter",0); assert(_nPendBufsUsed>=0); if(!pbuf_ordering_plist_head) { return NULL; /*nothing to promote*/ } info = pbuf_ordering_plist_head; do { if(info->immbuf_wlist_head==NULL) { printf("%d(s): Why is info->immbuf_wlist_head NULL\n", armci_me); fflush(stdout); pause(); } assert(info->immbuf_wlist_head!=NULL); assert(info->n_pending>0); if(_armci_serv_pendbuf_can_progress(info->immbuf_wlist_head)) { immbuf = info->immbuf_wlist_head; info->immbuf_wlist_head = immbuf->immbuf_list_next; info->n_pending -= 1; immbuf->immbuf_list_next = NULL; if(!info->immbuf_wlist_head) { assert(info->immbuf_wlist_tail == immbuf); info->immbuf_wlist_tail = NULL; /*remove this proc from proc list*/ info->prev->next = info->next; info->next->prev = info->prev; if(pbuf_ordering_plist_head == info) { pbuf_ordering_plist_head = (info->next==info)?NULL:info->next; } info->prev = info->next = NULL; } break; } info = info->next; } while(info != pbuf_ordering_plist_head); if(DEBUG_SERVER) if(immbuf) { request_header_t *msginfo=(request_header_t*)immbuf->buf; printf("%d:: promoting a buffer immbuf=%p op=%d from=%d n_pending=%d\n", armci_me,immbuf,msginfo->operation,msginfo->from,info->n_pending); fflush(stdout); } ARMCI_PR_DBG("exit",0); return immbuf; } /** Enqueue a message. It could be an immediate message that cannot * make progress or a non-immediate message that cannot make progress * either due to ordering constraints or lack of pending buffers. * @param vbuf IN Immediate buffer to be enqueud * @return Pending buffer into which the message was enqueued. NULL * if no pending buffer was allocated (which is always the case for * immediate messages) */ static pendbuf_t* _armci_serv_pendbuf_enqueue(immbuf_t *vbuf) { request_header_t *msginfo=(request_header_t *)vbuf->buf; int from = msginfo->from; pendbuf_t *pbuf; immbuf_t *ibuf; proc_waitlist_t *info = &pbuf_proc_list_info[msginfo->from]; ARMCI_PR_DBG("enter",0); /* printf("%d: Entered serv_pbuf_enqueue\n", armci_me); */ pbuf=NULL; if(msginfo->tag.imm_msg) { assert(!_armci_serv_pendbuf_can_progress(vbuf)); } else if(_armci_serv_pendbuf_can_progress(vbuf)) { pbuf = _armci_serv_pendbuf_assignbuf(vbuf); assert(pbuf != NULL); /*can_progress() should ensure this*/ } if(pbuf == NULL) { /* printf("%d(s):: Enqueing op=%d imm=%d from %d. n_pending=%d\n", armci_me, msginfo->operation, msginfo->tag.imm_msg, msginfo->from,info->n_pending); */ /* fflush(stdout); */ vbuf->immbuf_list_next = NULL; assert(info->n_pending < IMM_BUF_NUM); /*How another message now?*/ for(ibuf=info->immbuf_wlist_head;ibuf!=NULL;ibuf=ibuf->immbuf_list_next) { assert(vbuf != ibuf); /*enqueueing the same buffer twice*/ } info->n_pending += 1; if(!info->immbuf_wlist_head) { assert(!info->immbuf_wlist_tail); assert(!info->prev && !info->next); /*insert proc into proc list*/ if(!pbuf_ordering_plist_head) { pbuf_ordering_plist_head=info->next=info->prev=info; } else { info->next = pbuf_ordering_plist_head; info->prev = pbuf_ordering_plist_head->prev; pbuf_ordering_plist_head->prev->next = info; pbuf_ordering_plist_head->prev = info; } } /*insert vbuf into immbuf list for this proc*/ if(info->immbuf_wlist_tail) info->immbuf_wlist_tail->immbuf_list_next=vbuf; info->immbuf_wlist_tail = vbuf; if(!info->immbuf_wlist_head) info->immbuf_wlist_head = vbuf; } /* printf("%d: Leaving serv_pbuf_enqueue\n", armci_me); */ ARMCI_PR_DBG("exit",0); return pbuf; } /** Progress GET requests. * @param pbuf IN Pending buffer containing the GET request * @return none */ static void _armci_serv_pendbuf_progress_get(pendbuf_t *pbuf) { int index = (pbuf - serv_pendbuf_arr); request_header_t *msginfo = (request_header_t *)pbuf->buf; void *buffer =((char *)(msginfo+1)+msginfo->dscrlen); int *status = &pbuf->status; assert(sizeof(request_header_t)+msginfo->dscrlen+msginfo->datalendscrlen <= IMM_BUF_LEN) { /*Have the header and descriptor; go process*/ armci_complete_pendbuf(pbuf); if(msginfo->pinned) { *status = SEND_DATA_DONE; _armci_serv_pendbuf_freebuf(pbuf); } else { *status = SEND_DATA_PENDING; } } else { /*Need to get rest of descriptor*/ const int bytes = sizeof(request_header_t)+msginfo->dscrlen-IMM_BUF_LEN; #warning "PEND_BUFS: Abusing msginfo->tag.ack_ptr for GETS with large descriptors!" assert(msginfo->tag.ack_ptr != NULL); /*sanity check. Should point to tag.ack on the client side*/ void *lptr = ((char *)msginfo)+IMM_BUF_LEN; void *rptr = ((char *)msginfo->tag.ack_ptr) - (int)(&((request_header_t *)0)->tag.ack) + IMM_BUF_LEN; /* printf("%d(s):: GET getting rest of descriptor index=%d bytes=%d ptr=%p from=%d\n", */ /* armci_me,index,bytes,rptr,msginfo->from); */ /* fflush(stdout); */ assert(IMM_BUF_LEN+bytes < PENDING_BUF_LEN); armci_pbuf_start_get(msginfo,rptr,lptr,bytes,msginfo->from,index); *status = RECV_DSCR_PENDING; } break; case RECV_DSCR_PENDING: armci_die("call_data_server should set status to RECV_DSCR_DONE before calling progress",*status); break; case SEND_DATA_PENDING: armci_die("call_data_server should set status to SEND_DATA_DONE before calling progress",*status); break; case RECV_DSCR_DONE: /* printf("%d(s):: GET. Done recving descriptor index=%d op=%d datalen=%d from=%d\n", */ /* armci_me,index,msginfo->operation,msginfo->datalen,msginfo->from); */ /* fflush(stdout); */ armci_complete_pendbuf(pbuf); if(msginfo->pinned) { *status = SEND_DATA_DONE; _armci_serv_pendbuf_freebuf(pbuf); } else { *status = SEND_DATA_PENDING; } break; case SEND_DATA_DONE: _armci_serv_pendbuf_freebuf(pbuf); break; case RECV_DATA_PENDING: case RECV_DATA_DONE: default: armci_die("pendbuf_progress_get: invalid status", *status); } } /** Progress PUT/ACC requests. * @param pbuf IN Pending buffer containing the PUT/ACC request * @return none */ static void _armci_serv_pendbuf_progress_putacc(pendbuf_t *pbuf) { int index = (pbuf - serv_pendbuf_arr); request_header_t *msginfo = (request_header_t *)pbuf->buf; void *buffer =((char *)(msginfo+1))+msginfo->dscrlen; int *status = &pbuf->status; assert(msginfo->operation==PUT || ARMCI_ACC(msginfo->operation)); assert(sizeof(request_header_t)+msginfo->dscrlen+msginfo->datalenoperation,msginfo->from); */ /* fflush(stdout); */ if(sizeof(request_header_t)+msginfo->dscrlen <= IMM_BUF_LEN) { /*Have the header and descriptor; go process*/ assert(sizeof(request_header_t)+msginfo->dscrlen+msginfo->tag.data_len < PENDING_BUF_LEN); armci_pbuf_start_get(msginfo,msginfo->tag.data_ptr,buffer,msginfo->tag.data_len, msginfo->from, index); /* printf("%d(s): PUT/ACC getting data. pbuf_num=%d data_ptr=%p data_len=%d bytes=%d\n", armci_me,index,msginfo->tag.data_ptr, msginfo->tag.data_len,msginfo->bytes); */ *status = RECV_DATA_PENDING; } else { /*Need to get rest of descriptor*/ const int bytes = sizeof(request_header_t)+msginfo->dscrlen-IMM_BUF_LEN; #warning "PEND_BUFS: Abusing msginfo->tag.ack_ptr for GETS with large descriptors!" assert(msginfo->tag.ack_ptr != NULL); /*sanity check. Should point to tag.ack on the client side*/ void *lptr = ((char *)msginfo)+IMM_BUF_LEN; void *rptr = ((char *)msginfo->tag.ack_ptr) - (int)(&((request_header_t *)0)->tag.ack) + IMM_BUF_LEN; /* printf("%d(s):: PUT getting rest of descriptor index=%d bytes=%d ptr=%p from=%d\n", */ /* armci_me,index,bytes,rptr,msginfo->from); */ /* fflush(stdout); */ assert(IMM_BUF_LEN+bytes < PENDING_BUF_LEN); armci_pbuf_start_get(msginfo,rptr,lptr,bytes,msginfo->from,index); *status = RECV_DSCR_PENDING; } break; case RECV_DSCR_PENDING: armci_die("call_data_server should set status to RECV_DSCR_DONE before calling progress",*status); break; case RECV_DATA_PENDING: armci_die("call_data_server should set status to RECV_DONE before calling progress",*status); break; case RECV_DSCR_DONE: assert(sizeof(request_header_t)+msginfo->dscrlen+msginfo->tag.data_len < PENDING_BUF_LEN); armci_pbuf_start_get(msginfo,msginfo->tag.data_ptr,buffer,msginfo->tag.data_len, msginfo->from, index); /* printf("%d(s): PUT/ACC getting data. pbuf_num=%d data_ptr=%p data_len=%d bytes=%d\n", armci_me,index,msginfo->tag.data_ptr, msginfo->tag.data_len,msginfo->bytes); */ *status = RECV_DATA_PENDING; break; case RECV_DATA_DONE: /* printf("%d(s):: Done PUT/ACC with buf index=%d op=%d datalen=%d from=%d\n", */ /* armci_me,index,msginfo->operation,msginfo->datalen,msginfo->from); */ /* fflush(stdout); */ if(msginfo->operation == PUT && pbuf->order_prev!=NULL) { assert(pbuf->commit_me == 0); /*Why called so many times in thie state?*/ pbuf->commit_me = 1; break; } pbuf->commit_me = 0; armci_complete_pendbuf(pbuf); _armci_serv_pendbuf_freebuf(pbuf); break; case SEND_DATA_PENDING: case SEND_DATA_DONE: default: armci_die("pendbuf_progress_putacc: invalid status", *status); } } /** Make progress on processing a pending buffer. This function, also * ensures any other waiting messages get processed if they can * be. Thus, progress and eventual termination is guaranteed by this * function. * @param _pbuf IN Pending buffer to make progress on * @return none */ static void _armci_serv_pendbuf_progress(pendbuf_t *_pbuf){ request_header_t *msginfo = (request_header_t *)_pbuf->buf; immbuf_t *vbuf = _pbuf->vbuf; pendbuf_t *pbuf = _pbuf; assert(pbuf->vbuf!=NULL); do { if(vbuf && !IS_IMM_MSG(*msginfo)) { assert(pbuf->vbuf == vbuf); } /* printf("%d(s):: progressing op=%d imm=%d from=%d datalen=%d pbuf=%p vbuf=%p n_pending=%d\n", armci_me, */ /* msginfo->operation,msginfo->tag.imm_msg,msginfo->from,msginfo->datalen, pbuf,vbuf,pbuf_proc_list_info[msginfo->from].n_pending); */ /* fflush(stdout); */ if(IS_IMM_MSG(*msginfo)) { armci_complete_immbuf(vbuf); } else { /*non-immediate message*/ proc_waitlist_t* info = &pbuf_proc_list_info[msginfo->from]; do { assert(pbuf->vbuf == vbuf); if(msginfo->operation == PUT || ARMCI_ACC(msginfo->operation)) { _armci_serv_pendbuf_progress_putacc(pbuf); } else if (msginfo->operation == GET) { _armci_serv_pendbuf_progress_get(pbuf); } else { armci_die("pending buffer processing for this op not yet implemented", msginfo->operation); } pbuf = info->order_head; vbuf = pbuf ? pbuf->vbuf : NULL; } while(info->order_head && info->order_head->commit_me); } /* sleep(2); */ vbuf = _armci_serv_pendbuf_promote(); if(vbuf) { msginfo = (request_header_t *)vbuf->buf; if(!msginfo->tag.imm_msg) { pbuf = _armci_serv_pendbuf_assignbuf(vbuf); assert(pbuf != NULL); } } } while(vbuf != NULL); } /*----------------External functions--------------------*/ /** Initialize array of pending buffers * @return none */ void armci_pendbuf_init() { int i; ARMCI_PR_DBG("enter",0); /* bzero(serv_pendbuf_arr, sizeof(pendbuf_t)*PENDING_BUF_NUM); */ for(i=0; ibuf; bzero(pbuf, sizeof(pendbuf_t)); pbuf->buf = buf; pbuf->avail=1; } pbuf_ordering_plist_head=NULL; pbuf_proc_list_info = (proc_waitlist_t *)malloc(sizeof(proc_waitlist_t)*armci_nproc); assert(pbuf_proc_list_info != NULL); bzero(pbuf_proc_list_info, sizeof(proc_waitlist_t)*armci_nproc); ARMCI_PR_DBG("exit",0); } void armci_pendbuf_service_req(immbuf_t *immbuf) { pendbuf_t *pbuf; request_header_t *msginfo=(request_header_t*)immbuf->buf; if(IS_IMM_MSG(*msginfo) && _armci_serv_pendbuf_can_progress(immbuf)) { /* printf("%d: msg vbuf=%p op=%d from=%d imm=%d datalen=%d bytes=%d data_ptr=%p can progress. Completing it now!\n", */ /* armci_me, vbuf, msginfo->operation, msginfo->from, msginfo->tag.imm_msg,msginfo->datalen,msginfo->bytes,msginfo->tag.data_ptr); */ /* fflush(stdout); */ armci_complete_immbuf(immbuf); } else if((pbuf = _armci_serv_pendbuf_enqueue(immbuf))) { /* printf("%d: msg vbuf=%p op=%d from=%d imm=%d datalen=%d bytes=%d data_ptr=%p got pending buf. Progressing it!\n", */ /* armci_me, vbuf, msginfo->operation, msginfo->from, msginfo->tag.imm_msg,msginfo->datalen,msginfo->bytes,msginfo->tag.data_ptr); */ /* fflush(stdout); */ _armci_serv_pendbuf_progress(pbuf); } else { /* printf("%d: msg vbuf=%p op=%d from=%d imm=%d datalen=%d bytes=%d data_ptr=%p in waitlist!\n", armci_me, vbuf, msginfo->operation, msginfo->from, msginfo->tag.imm_msg,msginfo->datalen,msginfo->bytes,msginfo->tag.data_ptr); */ /* fflush(stdout); */ } } /**Network layer reporting to split buffers code that a put completed * on a pending buffer. * @param pbufid IN Pending buffer id (specified when starting a * put). * @return void */ void armci_pendbuf_done_put(int pbufid) { assert(pbufid>=0 && pbufid=0 && pbufidstatus) { case RECV_DSCR_PENDING: pbuf->status = RECV_DSCR_DONE; break; case RECV_DATA_PENDING: pbuf->status = RECV_DATA_DONE; break; default: armci_die("Reporting get done on buf with inappropriate status",pbufid); } _armci_serv_pendbuf_progress(pbuf); } #endif /*PEND_BUFS*/ ga-5.9.2/armci/src/devices/openib/pendbufs.h000066400000000000000000000047641500715745200206760ustar00rootroot00000000000000/** @file Split buffer implementation. * @author Sriram Krishnamoorthy * * Supports multiple short/immediate buffers posted per client and a * client-independent number of buffers to handle large messages. */ #ifndef _PENDBUFS_H_ #define _PENDBUFS_H_ #if defined(PEND_BUFS) #include "armcip.h" #include "request.h" #define PBUF_RGUARD_SIZE 256 /**The buf should be the first field in immbuf_t and pendbuf_t. For example, look at openib.c:armci_rcv_req and maybe other places*/ typedef struct immbuf_t { char *buf; /*immediate buffer[IMMBUF_LEN]*/ /* IMMBUF_NW_T fields; */ char rguard[PBUF_RGUARD_SIZE]; IMMBUF_NW_T struct immbuf_t *immbuf_list_next; } immbuf_t; typedef struct pendbuf_t { char *buf; /*pending buffer[PENDBUF_LEN]*/ /* PENDBUF_NW_T fields; */ char rguard[PBUF_RGUARD_SIZE]; PENDBUF_NW_T int status; /* #endif #if HAVE_STRING_H # include #endif /* determine number of CPUs on the current SMP node- Linux version for now */ int armci_getnumcpus(void) { int numproc = 0; FILE* fp; char line[80]; fp = fopen("/proc/cpuinfo","r"); if(fp == NULL) return -1; while(!feof(fp)){ fgets(line,80,fp); if(strncmp(line,"processor",9)==0) numproc++; } fclose(fp); return numproc; } ga-5.9.2/armci/src/devices/sockets/000077500000000000000000000000001500715745200171035ustar00rootroot00000000000000ga-5.9.2/armci/src/devices/sockets/dataserv.c000066400000000000000000000632741500715745200210740ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: dataserv.c,v 1.30.8.5 2007-07-02 05:18:13 d3p687 Exp $ */ #include "armcip.h" #include "request.h" #include "copy.h" #if HAVE_STDIO_H # include #endif #if HAVE_ERRNO_H # include #endif #if HAVE_MATH_H # include #endif #define DEBUG_ 0 #define DEBUG1 0 #define USE_VECTOR_FORMAT_ 1 active_socks_t *_armci_active_socks; extern int AR_ready_sigchld; int *SRV_sock; int *AR_port; int *CLN_sock; char *msg="hello from server"; static int *readylist=(int*)0; #define GETBUF(buf,type,var) (var) = *(type*)(buf); (buf) += sizeof(type) #if defined(USE_SOCKET_VECTOR_API) int armci_RecvVectorFromSocket(int sock,armci_giov_t darr[], int len, struct iovec *iov){ int i,j=0,k,num_xmit=0,lastiovlength,iovlength,n=0,max_iovec,totalsize=0; int totaliovs=0,dim1=0,dim2=0; struct iovec *saveiov=iov; max_iovec = MAX_IOVEC; for(i=0;i1){ total_of_2D = count[2]; for(j=3; j<=stride_levels; j++) { index[j] = 0; unit[j] = unit[j-1] * count[j-1]; total_of_2D *= count[j]; } } num_xmit = (total_of_2D*count[1])/max_iovec; lastiovlength = (total_of_2D*count[1])%max_iovec; if(num_xmit == 0) num_xmit = 1; else if(lastiovlength!=0)num_xmit++; k=0;vecind=0; if(lastiovlength!=0 && k==(num_xmit-1))iovlength=lastiovlength; else iovlength=max_iovec; for(i=0; i= count[j]) index[j] = 0; } dst1=dst; for(j=0;j1){ total_of_2D = count[2]; for(j=3; j<=stride_levels; j++) { index[j] = 0; unit[j] = unit[j-1] * count[j-1]; total_of_2D *= count[j]; } } num_xmit = total_of_2D*count[1]/max_iovec; lastiovlength = (total_of_2D*count[1])%max_iovec; if(num_xmit == 0) num_xmit = 1; else if(lastiovlength!=0)num_xmit++; k=0;vecind=0; if(lastiovlength!=0 && k==(num_xmit-1))iovlength=lastiovlength; else iovlength=max_iovec; for(i=0; i= count[j]) index[j] = 0; } src1=src; for(j=0;joperation==GET) bufsize = msginfo->dscrlen+sizeof(request_header_t); if(msginfo->operation==PUT){ msginfo->datalen=0; msginfo->bytes=msginfo->dscrlen; bufsize=msginfo->dscrlen+sizeof(request_header_t); } armci_send_req(proc, msginfo, bufsize); if(msginfo->operation==PUT){ bytes=armci_SendVectorToSocket(SRV_sock[armci_clus_id(proc)],darr,len, (struct iovec *)((char*)(msginfo+1)+msginfo->dscrlen) ); } return(bytes); } int armci_direct_vector_get(request_header_t *msginfo , armci_giov_t darr[], int len, int proc) { return armci_RecvVectorFromSocket(SRV_sock[armci_clus_id(proc)],darr,len, (struct iovec *)((char*)(msginfo+1)+msginfo->dscrlen) ); } int armci_direct_vector(request_header_t *msginfo , armci_giov_t darr[], int len, int proc){ int bufsize=0,bytes=0,s; for(s=0; soperation==GET) bufsize = msginfo->dscrlen+sizeof(request_header_t); if(msginfo->operation==PUT){ msginfo->datalen=0; msginfo->bytes=msginfo->dscrlen; bufsize=msginfo->dscrlen+sizeof(request_header_t); } armci_send_req(proc, msginfo, bufsize); if(msginfo->operation==GET){ bytes=armci_RecvVectorFromSocket(SRV_sock[armci_clus_id(proc)],darr,len, (struct iovec *)((char*)(msginfo+1)+msginfo->dscrlen) ); } if(msginfo->operation==PUT){ bytes=armci_SendVectorToSocket(SRV_sock[armci_clus_id(proc)],darr,len, (struct iovec *)((char*)(msginfo+1)+msginfo->dscrlen) ); } return(bytes); } #endif /*\ client sends request message to server \*/ int armci_send_req_msg(int proc, void *buf, int bytes) { int cluster = armci_clus_id(proc); request_header_t* msginfo = (request_header_t*)buf; int idx, rc; THREAD_LOCK(armci_user_threads.net_lock); /* mark sockets as active (only if reply is expected?) */ idx = _armci_buf_to_index(msginfo); _armci_active_socks->socks[idx] = SRV_sock[cluster]; rc = (armci_WriteToSocket(SRV_sock[cluster], buf, bytes) < 0); THREAD_UNLOCK(armci_user_threads.net_lock); return rc; } void armci_write_strided_sock(void *ptr, int stride_levels, int stride_arr[], int count[], int fd) { int i, j, stat; long idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int bvalue[MAX_STRIDE_LEVEL], bunit[MAX_STRIDE_LEVEL]; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) n1dim *= count[i]; /* calculate the destination indices */ bvalue[0] = 0; bvalue[1] = 0; bunit[0] = 1; bunit[1] = 1; for(i=2; i<=stride_levels; i++) { bvalue[i] = 0; bunit[i] = bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) bvalue[j] = 0; } /* memcpy(buf, ((char*)ptr)+idx, count[0]); */ /* buf += count[0]; */ stat = armci_WriteToSocket(fd, ((char*)ptr)+idx, count[0]); if(stat<0)armci_die("armci_write_strided_sock:write failed",stat); } } void armci_read_strided_sock(void *ptr, int stride_levels, int stride_arr[], int count[], int fd) { int i, j, stat; long idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int bvalue[MAX_STRIDE_LEVEL], bunit[MAX_STRIDE_LEVEL]; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) n1dim *= count[i]; /* calculate the destination indices */ bvalue[0] = 0; bvalue[1] = 0; bunit[0] = 1; bunit[1] = 1; for(i=2; i<=stride_levels; i++) { bvalue[i] = 0; bunit[i] = bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) bvalue[j] = 0; } /* memcpy(buf, ((char*)ptr)+idx, count[0]); */ /* buf += count[0]; */ stat = armci_ReadFromSocket(fd, ((char*)ptr)+idx, count[0]); if(stat<0)armci_die("armci_read_strided_sock:read failed",stat); } } /*\ client sends strided data + request to server \*/ int armci_send_req_msg_strided(int proc, request_header_t *msginfo,char *ptr, int strides, int stride_arr[], int count[]) { int cluster = armci_clus_id(proc); int stat, bytes; if(DEBUG_){ printf("%d:armci_send_req_msg_strided: op=%d to=%d bytes= %d \n",armci_me, msginfo->operation,proc,msginfo->datalen); fflush(stdout); } /* we write header + data descriptor */ bytes = sizeof(request_header_t) + msginfo->dscrlen; THREAD_LOCK(armci_user_threads.net_lock); stat = armci_WriteToSocket(SRV_sock[cluster], msginfo, bytes); if(stat<0)armci_die("armci_send_strided:write failed",stat); #if defined(USE_SOCKET_VECTOR_API) if(msginfo->operation==PUT && msginfo->datalen==0) armci_SendStridedToSocket( SRV_sock[cluster],ptr,stride_arr,count, strides,(struct iovec *)(msginfo+1) ); else #endif /* for larger blocks write directly to socket thus avoiding memcopy */ armci_write_strided_sock(ptr, strides,stride_arr,count,SRV_sock[cluster]); THREAD_UNLOCK(armci_user_threads.net_lock); return 0; } char *armci_ReadFromDirect(int proc, request_header_t * msginfo, int len) { int cluster=armci_clus_id(proc); int stat; if(DEBUG_){ printf("%d:armci_ReadFromDirect: from %d \n",armci_me,proc); fflush(stdout); } stat =armci_ReadFromSocket(SRV_sock[cluster],msginfo+1,len); if(stat<0)armci_die("armci_rcv_data: read failed",stat); return(char*)(msginfo+1); } /*\ client receives strided data from server \*/ void armci_ReadStridedFromDirect(int proc, request_header_t* msginfo, void *ptr, int strides, int stride_arr[], int count[]) { int cluster=armci_clus_id(proc); if(DEBUG_){ printf("%d:armci_ReadStridedFromDirect: from %d \n",armci_me,proc); fflush(stdout); } #if defined(USE_SOCKET_VECTOR_API) if(msginfo->operation==GET && strides > 0) armci_RecvStridedFromSocket( SRV_sock[cluster],ptr,stride_arr,count, strides,(struct iovec *)((char*)(msginfo+1)+msginfo->dscrlen)); else #endif armci_read_strided_sock(ptr, strides, stride_arr, count, SRV_sock[cluster]); } /*********************************** server side ***************************/ #if defined(USE_SOCKET_VECTOR_API) void armci_tcp_read_vector_data(request_header_t *msginfo,void *vdscr,int p){ int bytes,i,j,stat; void **ptr; char *dscr; long len; armci_giov_t *mydarr; bytes = msginfo->dscrlen; if(DEBUG1){ printf("\n in armci_tcp_read_vector_data reading bytes=%d infonext=%p\n", bytes,(void*)(msginfo+1));fflush(stdout); } stat = armci_ReadFromSocket(CLN_sock[p], (MessageRcvBuffer+sizeof(request_header_t)),bytes); if(stat<0)armci_die("armci_tcp_read_vector_data: read of data failed",stat); dscr=(MessageRcvBuffer+sizeof(request_header_t)); ptr=(void**)dscr; *(void**)vdscr=(void *)dscr; mydarr = (armci_giov_t *)(dscr+bytes); GETBUF(dscr, long ,len); if(len!=0){ for(i=0;idscrlen; if(DEBUG1){ printf("\n in armci tcp read strided data reading bytes=%d infonext=%p\n" ,bytes,(void*)(msginfo+1));fflush(stdout); } stat = armci_ReadFromSocket(CLN_sock[p], (MessageRcvBuffer+sizeof(request_header_t)),bytes); if(stat<0)armci_die("armci_tcp_read_strided_data:read of data failed",stat); dscr=(MessageRcvBuffer+sizeof(request_header_t)); *(void**)vdscr=(void *)dscr; ptr = *(void**)dscr; dscr += sizeof(void*); stride_levels = *(int*)dscr; dscr += sizeof(int); stride_arr = (int*)dscr; dscr += stride_levels*sizeof(int); count = (int*)dscr; dscr += (stride_levels+1)*sizeof(int); armci_RecvStridedFromSocket( CLN_sock[p],ptr,stride_arr,count,stride_levels, (struct iovec *)dscr); /*armci_RecvStridedFromSocket( CLN_sock[p],ptr,stride_arr,count, stride_levels,(struct iovec *)((char*)(msginfo+1)+msginfo->dscrlen) );*/ } #endif /*\ server receives request \*/ void armci_rcv_req(void *mesg, void *phdr, void *pdescr,void *pdata,int *buflen) { request_header_t *msginfo = (request_header_t*)MessageRcvBuffer; int hdrlen = sizeof(request_header_t); int stat, p = *(int*)mesg; int bytes; stat =armci_ReadFromSocket(CLN_sock[p],MessageRcvBuffer,hdrlen); if(stat<0) armci_die("armci_rcv_req: failed to receive header ",p); *(void**)phdr = msginfo; #if defined(USE_SOCKET_VECTOR_API) if(msginfo->operation == PUT && msginfo->datalen==0){ if(msginfo->format==STRIDED) armci_tcp_read_strided_data(msginfo,pdescr,p); if(msginfo->format==VECTOR){ armci_tcp_read_vector_data(msginfo,pdescr,p); } return; } #endif *buflen = MSG_BUFLEN - hdrlen; if (msginfo->operation == GET) bytes = msginfo->dscrlen; else{ bytes = msginfo->bytes; if(bytes >*buflen)armci_die2("armci_rcv_req: message overflowing rcv buf", msginfo->bytes,*buflen); } if(msginfo->bytes){ stat = armci_ReadFromSocket(CLN_sock[p],msginfo+1,bytes); if(stat<0)armci_die("armci_rcv_req: read of data failed",stat); *(void**)pdescr = msginfo+1; *(void**)pdata = msginfo->dscrlen + (char*)(msginfo+1); *buflen -= msginfo->dscrlen; if (msginfo->operation != GET) if(msginfo->datalen)*buflen -= msginfo->datalen; }else { *(void**)pdata = msginfo+1; *(void**)pdescr = NULL; } if(msginfo->datalen>0 && msginfo->operation != GET){ if(msginfo->datalen > ((int)MSG_BUFLEN) -((int)hdrlen) -msginfo->dscrlen) armci_die2("armci_rcv_req:data overflowing buffer", msginfo->dscrlen,msginfo->datalen); *buflen -= msginfo->datalen; } } /*\ send data back to client \*/ void armci_WriteToDirect(int to, request_header_t* msginfo, void *data) { int stat = armci_WriteToSocket(CLN_sock[to], data, msginfo->datalen); if(stat<0)armci_die("armci_WriteToDirect:write failed",stat); } /*\ server sends strided data back to client \*/ void armci_WriteStridedToDirect(int proc, request_header_t* msginfo, void *ptr, int strides, int stride_arr[], int count[]) { if(DEBUG_){ printf("%d:armci_WriteStridedToDirect:from %d\n",armci_me,proc); fflush(stdout); } #if defined(USE_SOCKET_VECTOR_API) if(msginfo->operation==GET && strides>0) armci_SendStridedToSocket(CLN_sock[proc],ptr,stride_arr,count,strides, (struct iovec *)((char*)(msginfo+1)+msginfo->dscrlen) ) ; else #endif armci_write_strided_sock(ptr, strides, stride_arr, count, CLN_sock[proc]); } /*\ server writes data to socket associated with process "to" \*/ void armci_sock_send(int to, void* data, int len) { int stat = armci_WriteToSocket(CLN_sock[to], data, len); if(stat<0)armci_die("armci_sock_send:write failed",stat); } /*\ close all open sockets, called before terminating/aborting \*/ void armci_transport_cleanup() { if(SERVER_CONTEXT){ if(readylist)free(readylist); armci_ShutdownAll(CLN_sock,armci_nproc); /*server */ }else armci_ShutdownAll(SRV_sock,armci_nclus); /*client */ } /*\ main routine for data server process in a cluster environment * the process is blocked (in select) until message arrives from * the clients and services the requests \*/ void armci_call_data_server() { int nready; int up=1; readylist = (int*)calloc(sizeof(int),armci_nproc); if(!readylist)armci_die("armci_data_server:could not allocate readylist",0); if(DEBUG_){ printf("%d server waiting for request\n",armci_me); fflush(stdout); sleep(1); } /* server main loop; wait for and service requests until QUIT requested */ for(;;){ int i, p; nready = armci_WaitSock(CLN_sock, armci_nproc, readylist); for(i = 0; i < armci_nproc; i++){ p = (up) ? i : armci_nproc -1 -i; if(!readylist[p])continue; armci_data_server(&p); nready--; if(nready==0) break; /* all sockets read */ } /* fairness attempt: each time process the list in a different direction*/ up = 1- up; /* switch directions for the next round */ if(nready) armci_die("armci_dataserv:readylist not consistent with nready",nready); } } extern int tcp_sendrcv_bufsize; void armci_determine_sock_buf_size(){ if(armci_nclus<=8)return; if(armci_nclus>=128){tcp_sendrcv_bufsize = 32768;return;} tcp_sendrcv_bufsize =(int)pow(2,(22-(int)(log(armci_nclus)/log(2)))); return; } /*\ Create Sockets for clients and servers \*/ void armci_init_connections() { int i,n,p,master = armci_clus_info[armci_clus_me].master; _armci_buf_init(); /* sockets for communication with data server */ SRV_sock = (int*) malloc(sizeof(int)*armci_nclus); if(!SRV_sock)armci_die("ARMCI cannot allocate SRV_sock",armci_nclus); /* array that will be used to exchange port info */ AR_port = (int*) calloc(armci_nproc * armci_nclus, sizeof(int)); if(!AR_port)armci_die("ARMCI cannot allocate AR_port",armci_nproc*armci_nclus); /* create active sockets list select */ if (!(_armci_active_socks = malloc(sizeof(active_socks_t)))) armci_die("dataserv.c, malloc _armci_active_socks failed",0); for(i=0,n=MAX_BUFS+MAX_SMALL_BUFS;isocks[i]=-1; /* create sockets for communication with each user process */ if(master==armci_me){ CLN_sock = (int*) malloc(sizeof(int)*armci_nproc); if(!CLN_sock)armci_die("ARMCI cannot allocate CLN_sock",armci_nproc); armci_determine_sock_buf_size(); for(p=0; p< armci_nproc; p++){ int off_port = armci_clus_me*armci_nproc; # ifdef SERVER_THREAD if(p >=armci_clus_first && p <= armci_clus_last) CLN_sock[p]=-1; else # endif armci_CreateSocketAndBind(CLN_sock + p, AR_port + p +off_port); } #ifdef SERVER_THREAD /* skip sockets associated with processes on the current node */ if(armci_clus_first>0) armci_ListenSockAll(CLN_sock, armci_clus_first); if(armci_clus_last< armci_nproc-1) armci_ListenSockAll(CLN_sock + armci_clus_last+1, armci_nproc-armci_clus_last-1); #else armci_ListenSockAll(CLN_sock, armci_nproc); #endif } } void armci_wait_for_server() { if(armci_me == armci_master){ #ifndef SERVER_THREAD RestoreSigChldDfl(); armci_serv_quit(); armci_wait_server_process(); #endif } } void armci_client_connect_to_servers() { int stat,c, nall; char str[100]; #ifndef SERVER_THREAD int p; /* master has to close all sockets -- they are used by server PROCESS */ if(armci_master==armci_me)for(p=0; p< armci_nproc; p++){ close(CLN_sock[p]); } #endif /* exchange port numbers with processes in all cluster nodes * save number of messages by using global sum -only masters contribute */ nall = armci_nclus*armci_nproc; armci_msg_igop(AR_port,nall,"+"); /*using port number create socket & connect to data server in each clus node*/ for(c=0; c< armci_nclus; c++){ int off_port = c*armci_nproc; #ifdef SERVER_THREAD /*no intra node socket connection with server thread*/ if(c == armci_clus_me) SRV_sock[c]=-1; else #endif SRV_sock[c] = armci_CreateSocketAndConnect(armci_clus_info[c].hostname, AR_port[off_port + armci_me]); if(DEBUG_ && SRV_sock[c]!=-1){ printf("%d: client connected to %s:%d\n",armci_me, armci_clus_info[c].hostname, AR_port[off_port + armci_me]); fflush(stdout); } } if(DEBUG_){ bzero(str,99); for(c=0; c< armci_nclus; c++)if(SRV_sock[c]!=-1){ stat =armci_ReadFromSocket(SRV_sock[c],str, sizeof(msg)+1); if(stat<0)armci_die("read failed",stat); printf("in client %d message was=%s from%d\n",armci_me,str,c); fflush(stdout); } } free(AR_port); /* we do not need the port numbers anymore */ } /*\ establish connections with compute processes \*/ void armci_server_initial_connection() { #ifdef SERVER_THREAD if(armci_clus_first>0) armci_AcceptSockAll(CLN_sock, armci_clus_first); if(armci_clus_last< armci_nproc-1) armci_AcceptSockAll(CLN_sock + armci_clus_last+1, armci_nproc-armci_clus_last-1); #else armci_AcceptSockAll(CLN_sock, armci_nproc); #endif if(DEBUG_){ int stat, p; printf("%d: server connected to all clients\n",armci_me); fflush(stdout); sleep(1); for(p=0; p #endif #if HAVE_STRING_H # include #endif #if HAVE_ERRNO_H # include #endif #if HAVE_SYS_WAIT_H # include #endif #if HAVE_SYS_TIME_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_SOCKET_H # include #endif /*# include */ /*moved to sockets.h*/ #if HAVE_NETINET_IN_H # include #endif #if HAVE_NETINET_TCP_H # include #endif #if HAVE_NETDB_H # include #endif #if HAVE_UNISTD_H # include # define CLOSE close #elif HAVE_WINSOCK_H # include # define bcopy(s1,s2,n) memcpy(s2,s1,n) # define sleep(x) Sleep(1000*(x)) # define CLOSE closesocket #endif #include "armcip.h" #include "sockets.h" typedef socklen_t soclen_t; /* portability of socklen_t definition is iffy - we need to avoid it !! #if defined(LINUX) && ( defined(_SOCKETBITS_H) || defined(__BITS_SOCKET_H)) #elif defined(AIX) typedef size_t socklen_t; #else typedef int socklen_t; #endif */ #ifndef MAX_STRIDE_LEVEL #define MAX_STRIDE_LEVEL 8 #endif extern int armci_me, armci_nproc,armci_nclus; int tcp_sendrcv_bufsize=131072; #define DEBUG_ 0 #define DEBUG1 0 #define CONNECT_TRIALS 4 #define MAX_INTR_NO_DATA 8 int armci_PollSocket(int sock) /* Poll the socket for available input. Return 1 if data is available, 0 otherwise. */ { fd_set ready; struct timeval timelimit; int nready; if (sock < 0) return 0; again: FD_ZERO(&ready); FD_SET(sock, &ready); timelimit.tv_sec = 0; timelimit.tv_usec = 0; nready = select(sock+1, &ready, (fd_set *) NULL, (fd_set *) NULL, &timelimit); if (nready < 0) { if (errno == EINTR) goto again; else armci_die("armci_PollSocket: error from select", sock); } return nready; } /*\ sleep in select until data appears on one of sockets * return number of sockets ready and indicate which ones are in ready array * allows <0 values in socklist array (ignores them) \*/ int armci_WaitSock(int *socklist, int num, int *ready) { int sock,maxsock=0; fd_set dset; int nready; if(num<0) armci_die("armci_WaitSock: num <0",num); again: FD_ZERO(&dset); maxsock=0; for(sock=0; sock maxsock)maxsock = socklist[sock]; if(socklist[sock] >0){ /* ignore fd=-1 on the list */ FD_SET(socklist[sock], &dset); } } nready = select(maxsock+1, &dset, (fd_set*)NULL, (fd_set*)NULL, NULL); if (nready < 0) { if (errno == EINTR){ fprintf(stderr,"%d:interrupted in select\n",armci_me); goto again; } else armci_die("armci_WaitSocket: error from select", sock); } for(sock=0; sock maxsock)maxsock = socklist[sock]; if(socklist[sock] >0){ /* ignore fd=-1 on the list */ FD_SET(socklist[sock], &dset); } } timelimit.tv_sec = 0; timelimit.tv_usec = lim; nready = select(maxsock+1, &dset, (fd_set*)NULL, (fd_set*)NULL, &timelimit); if (nready < 0) { if (errno == EINTR){ fprintf(stderr,"%d:interrupted in select\n",armci_me); goto again; } else armci_die("armci_WaitSocket: error from select", sock); } for(sock=0; sockp_proto; status = setsockopt(sock, level, TCP_NODELAY, optval, sizeof(int)); if (status != 0) armci_die("armci_TcpNoDelay: setsockopt failed", status); } void armci_ShutdownAll(int socklist[], int num) /* close all sockets discarding any pending data in either direction. */ { int i; for (i=0; i= 0) { (void) shutdown(socklist[i], 2); (void) CLOSE(socklist[i]); socklist[i]=-1; } } #if defined(USE_SOCKET_VECTOR_API) int _armci_tcp_writev(int sock, struct iovec *iovptr,int writeiovlength,int currentwritesize,struct iovec *iov){ int n=0; while(n!=currentwritesize){ int rc; rc=writev(sock,iovptr,writeiovlength); if(rc<0)perror("writev failed"); if(DEBUG1&&0)if(rciov_len)+templength>rc){ iovptr->iov_base=(char *)((*iovptr).iov_base)+(rc-templength); iovptr->iov_len-=(rc-templength); templength+=(rc-templength); } else { templength+=iovptr->iov_len; iovptr+=1; completediovs++; } } writeiovlength-=completediovs; if(writeiovlength<=0)writeiovlength=1; } } return(n); } int _armci_tcp_readv(int sock, struct iovec *iovptr,int readiovlength,int currentreadsize,struct iovec *iov){ int n=0; while(n!=currentreadsize){ int rc; rc=readv(sock,iovptr,readiovlength); if(rc<0)perror("readv failed"); if(DEBUG1&&0)if(rciov_len)+templength>rc){ iovptr->iov_base=(char *)((*iovptr).iov_base)+(rc-templength); iovptr->iov_len-=(rc-templength); templength+=(rc-templength); } else { templength+=iovptr->iov_len; iovptr+=1; completediovs++; } } readiovlength-=completediovs; if(readiovlength<=0)readiovlength=1; } } return(n); } int armci_ReadVFromSocket(int sock,struct iovec *iov, int iovlength, int totalsize) { struct iovec *iovptr; int i=0,num_xmit=1,lastiovoriglen=0,lastiovnewlen=0,lastiovindex=-1,n=0; int readiovlength,currentreadsize=totalsize,totalreadsofar=0,byteslefttoread=0; char *lastiovorigbase=NULL; iovptr=iov; if(totalsize>PACKET_SIZE){ while(totalreadsofar!=totalsize){ currentreadsize=0; if(lastiovindex>=0) iovptr=iov+lastiovindex; if(lastiovoriglen!=0){ iov[lastiovindex].iov_base = (lastiovorigbase+lastiovnewlen); iov[lastiovindex].iov_len=lastiovoriglen-lastiovnewlen; lastiovoriglen=0; } iovlength=0; if(totalsize-totalreadsofarbyteslefttoread){ lastiovoriglen=iov[i].iov_len;lastiovorigbase=(char *)iov[i].iov_base; lastiovindex=i; iov[i].iov_len=byteslefttoread-currentreadsize; currentreadsize+=iov[i].iov_len; lastiovnewlen=iov[i].iov_len; } else { currentreadsize+=iov[i].iov_len; i++; lastiovindex=i; iovlength++; } } if(lastiovoriglen>0)iovlength+=1; totalreadsofar+=currentreadsize; num_xmit++; readiovlength=iovlength; n=_armci_tcp_readv(sock,iovptr,readiovlength,currentreadsize,iov); if(DEBUG1){printf("\nFinished reading n=%d bytes iov of length %d \n",n,iovlength);fflush(stdout);} } } else { iovptr=iov; readiovlength=iovlength; n=0; n+=_armci_tcp_readv(sock,iovptr,readiovlength,currentreadsize,iov); if(DEBUG1){printf("\nFits in one packet Finished reading n=%d bytes iov of length %d \n",n,iovlength);fflush(stdout);} } return(n); } int armci_WriteVToSocket(int sock,struct iovec *iov, int iovlength, int totalsize){ int lastiovoriglen=0,lastiovnewlen=0,lastiovindex=-1,totalwritesofar=0,byteslefttowrite=0; struct iovec *iovptr; int i=0,num_xmit=0,n=0; int currentwritesize=totalsize,writeiovlength; char *lastiovorigbase=NULL; iovptr=iov; if(totalsize>PACKET_SIZE){ while(totalwritesofar!=totalsize){ currentwritesize=0; if(lastiovindex>=0) iovptr=iov+lastiovindex; if(lastiovoriglen!=0){ iov[lastiovindex].iov_base = (lastiovorigbase+lastiovnewlen); iov[lastiovindex].iov_len= lastiovoriglen-lastiovnewlen; lastiovoriglen=0; } iovlength=0; if(totalsize-totalwritesofarbyteslefttowrite){ lastiovoriglen=iov[i].iov_len;lastiovorigbase=(char *)iov[i].iov_base; lastiovindex=i; iov[i].iov_len=byteslefttowrite-currentwritesize; currentwritesize+=iov[i].iov_len;lastiovnewlen=iov[i].iov_len; } else { currentwritesize+=iov[i].iov_len; i++; lastiovindex=i; iovlength++; } } totalwritesofar+=currentwritesize; num_xmit++; if(lastiovoriglen>0)iovlength+=1; writeiovlength=iovlength; n=_armci_tcp_writev(sock,iovptr,writeiovlength,currentwritesize,iov); if(DEBUG1){printf("\nFinished writing n=%d iov of length %d \n",n,iovlength);fflush(stdout);} if(n!=currentwritesize)armci_die2("\n problems with writing using writev\n",n,currentwritesize); } } else { iovptr=iov; writeiovlength=iovlength; n=0; n= _armci_tcp_writev(sock,iovptr,writeiovlength,currentwritesize,iov); if(n<0)perror("write failed"); if(DEBUG1){printf("\nFits in one packet Finished writing n=%d iov of length %d \n",n,iovlength);fflush(stdout);} if(n!=currentwritesize)armci_die2("\n problems with writing using writev\n",n,currentwritesize); } return(n); } #endif /*for use_socket_vec_api*/ int armci_ReadFromSocket(int sock, void* buffer, int lenbuf) /* Read from the socket until we get all we want. */ { int nread, status, nintr=0; char *buf = (char*)buffer; status = lenbuf; while (lenbuf > 0) { again: nread = recv(sock, buf, lenbuf, 0); /* on linux 0 can be returned if socket is closed by sender */ if(nread < 0 || ((nread == 0) && errno ) ){ if (errno == EINTR){ if(DEBUG_){ fprintf(stderr,"%d:interrupted in recv\n",armci_me); } /* retry a few times if nread==0 */ if(nread==0) nintr++; else nintr=0; if(nintr>MAX_INTR_NO_DATA) return -1; /* the socket must be closed */ goto again; }else { if(DEBUG_){ (void) fprintf(stderr,"sock=%d, pid=%d, nread=%d, len=%d\n", sock, armci_me, nread, lenbuf); if(errno)perror("armci_ReadFromSocket: recv failed"); } status = -1; break; } } buf += nread; lenbuf -= nread; } return status; } int armci_WriteToSocket (int sock, void* buffer, int lenbuf) /* Write to the socket in packets of PACKET_SIZE bytes */ { int status = lenbuf; int nsent, len; char *buf = (char*)buffer; if(DEBUG_){ printf("%d armci_WriteToSocket sock=%d lenbuf=%d\n",armci_me,sock,lenbuf); fflush(stdout); } while (lenbuf > 0) { again: len = (lenbuf > PACKET_SIZE) ? PACKET_SIZE : lenbuf; nsent = send(sock, buf, len, 0); if (nsent < 0) { /* This is bad news */ if(errno ==EINTR){ if(DEBUG_){ fprintf(stderr,"%d:interrupted in socket send\n",armci_me); } goto again; }else{ if(DEBUG_){ (void) fprintf(stderr,"sock=%d, pid=%d, nsent=%d, len=%d\n", sock, armci_me, nsent, lenbuf); (void) fflush(stderr); } status = -1; break; } } buf += nsent; lenbuf -= nsent; } return status; } void armci_CreateSocketAndBind(int *sock, int *port) /* Create a socket, bind it to a wildcard internet name and return the info so that its port number may be advertised */ { soclen_t length; struct sockaddr_in server; int size = PACKET_SIZE; int on = 1; length = sizeof (struct sockaddr_in); /* Create socket */ if ( (*sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) armci_die("armci_CreateSocketAndBind: socket creation failed", *sock); if(setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof on) == -1) armci_die("armci_CreateSocketAndBind: error from setsockopt", -1); /* Increase size of socket buffers to improve long message performance and increase size of message that goes asynchronously */ if(setsockopt(*sock, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size))) armci_die("armci_CreateSocketAndBind: error setting SO_RCVBUF", size); if(setsockopt(*sock, SOL_SOCKET, SO_SNDBUF, (char *) &size, sizeof(size))) armci_die("armci_CreateSocketAndBind: error setting SO_SNDBUF", size); armci_TcpNoDelay(*sock); /* Name socket with wildcards */ server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = 0; if (bind(*sock, (struct sockaddr *) &server, length) < 0) armci_die("armci_CreateSocketAndBind: bind failed", 0); /* Find out port number etc. */ if (getsockname(*sock, (struct sockaddr *) &server, &length) < 0) armci_die("armci_CreateSocketAndBind: getsockname failed", 0); *port = ntohs(server.sin_port); } /*\ listen for socket connections \*/ void armci_ListenSockAll(int* socklist, int num) { int i; if(num<0)armci_die("armci_ListenSockAll invalid number of sockets",num); for(i=0; i< num; i++){ againlist: if (listen(socklist[i], num) < 0) { if (errno == EINTR) goto againlist; else armci_die("armci_ListenSockAll: listen failed", 0); } } if (DEBUG_) { (void) printf("process %d out of listen on %d sockets\n",armci_me,num); (void) fflush(stdout); } } void armci_tcp_get_sock_buf_size(int msgsock){ int buffer_orig=32768,r; r = -1; while ( r < 0 && (tcp_sendrcv_bufsize > buffer_orig) ) { r= setsockopt(msgsock, SOL_SOCKET, SO_SNDBUF, (char *) &tcp_sendrcv_bufsize, sizeof(tcp_sendrcv_bufsize)); r= setsockopt(msgsock, SOL_SOCKET, SO_RCVBUF, (char *) &tcp_sendrcv_bufsize, sizeof(tcp_sendrcv_bufsize)); if( r < 0 ) tcp_sendrcv_bufsize =(tcp_sendrcv_bufsize/2); } } /*\ accept connections on the specified sockets \*/ void armci_AcceptSockAll(int* socklist, int num) { fd_set ready, fdzero; struct timeval timelimit; int maxsock, msgsock, nready, num_accept=0; int i; if(num<0)armci_die("armci_AcceptSockAll invalid number of sockets",num); /* Use select to wait for someone to try and establish a connection so that we can add a short timeout to avoid hangs */ FD_ZERO(&fdzero); againsel: FD_ZERO(&ready); /* we negate socket number on the list to mark already connected */ maxsock=0; for(i=0; i maxsock)maxsock = socklist[i]; /* find largest value*/ if(socklist[i]>0) FD_SET(socklist[i], &ready); /* printf("%d: accepting socket%d=%d of %d\n",armci_me,i,socklist[i],num);*/ } timelimit.tv_sec = TIMEOUT_ACCEPT; timelimit.tv_usec = 0; nready = select(maxsock+1, &ready, (fd_set *) NULL, (fd_set *) NULL, &timelimit); /* error screening */ if ( (nready <= 0) && (errno == EINTR) ) goto againsel; else if (nready < 0) armci_die("armci_AcceptSockAll: error from select",nready); else if (nready == 0) armci_die("armci_AcceptSockAll:timeout waiting for connection",nready); /* if (bcmp(&ready,&fdzero,sizeof(fdzero)))*/ /* armci_die("armci_AcceptSockAll: out of select but not ready!",nready);*/ /* accept connection from newly contacted clients */ for(i=0; i< num; i++){ int sock = socklist[i]; if(sock<0) continue; /* accepted already */ if(!FD_ISSET(sock, &ready)) continue; /* not contacted yet */ againacc: msgsock = accept(sock, (struct sockaddr *) NULL, (soclen_t *) NULL); if(msgsock==0){ int msgsock2; msgsock2 = dup(msgsock); /*(void) CLOSE(msgsock);*/ msgsock = msgsock2; } if (msgsock == -1) { if (errno == EINTR) goto againacc; else armci_die("armci_AcceptSockAll: accept failed", msgsock); } if(DEBUG_){ (void) printf("process %d out of accept socket=%d\n",armci_me,msgsock); (void) fflush(stdout); } /* Increase size of socket buffers to improve long message performance and increase size of message that goes asynchronously */ armci_tcp_get_sock_buf_size(msgsock); armci_TcpNoDelay(msgsock); (void) CLOSE(sock); /* will not be needing this again */ socklist[i] = -msgsock; /* negate connected socket on the list */ num_accept++; } if(num_accept < num) goto againsel; for(i=0; i< num; i++) if(socklist[i]>=0) armci_die("armci_AcceptSockAll: not connected",socklist[i]); else socklist[i] = - socklist[i]; } int armci_ListenAndAccept(int sock) /* Listen and accept a connection on the specified socket which was created with CreateSocketAndBind */ { fd_set ready; struct timeval timelimit; int msgsock, nready; int size = PACKET_SIZE; againlist: if (listen(sock, 1) < 0) { if (errno == EINTR) goto againlist; else armci_die("armci_ListenAndAccept: listen failed", 0); } if (DEBUG_) { (void) printf("process %d out of listen on socket %d\n",armci_me,sock); (void) fflush(stdout); } /* Use select to wait for someone to try and establish a connection so that we can add a short timeout to avoid hangs */ againsel: FD_ZERO(&ready); FD_SET(sock, &ready); timelimit.tv_sec = TIMEOUT_ACCEPT; timelimit.tv_usec = 0; nready = select(sock+1, &ready, (fd_set *) NULL, (fd_set *) NULL, &timelimit); if ( (nready <= 0) && (errno == EINTR) ) goto againsel; else if (nready < 0) armci_die("armci_ListenAndAccept: error from select", nready); else if (nready == 0) armci_die("armci_ListenAndAccept: timeout waiting for connection", nready); if (!FD_ISSET(sock, &ready)) armci_die("armci_ListenAndAccept: out of select but not ready!", nready); againacc: msgsock = accept(sock, (struct sockaddr *) NULL, (soclen_t *) NULL); if (msgsock == -1) { if (errno == EINTR) goto againacc; else armci_die("armci_ListenAndAccept: accept failed", msgsock); } if (DEBUG_) { (void) printf("process %d out of accept on socket %d\n", armci_me,msgsock); (void) fflush(stdout); } /* Increase size of socket buffers to improve long message performance and increase size of message that goes asynchronously */ if(setsockopt(msgsock, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof size)) armci_die("armci_ListenAndAccept: error setting SO_RCVBUF", size); if(setsockopt(msgsock, SOL_SOCKET, SO_SNDBUF, (char *) &size, sizeof size)) armci_die("armci_ListenAndAccept: error setting SO_SNDBUF", size); armci_TcpNoDelay(sock); (void) CLOSE(sock); /* will not be needing this again */ return msgsock; } #if !defined(WIN32) struct hostent *gethostbyname(); #endif int armci_CreateSocketAndConnect(char *hostname, int port) /* Return the file descriptor of the socket which connects me to the remote process on hostname at port hostname = hostname of the remote process port = port number of remote socket */ { int sock, status; struct sockaddr_in server; struct hostent *hp; int on = 1; int trial; /* Create socket */ if ( (sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) { (void) fprintf(stderr,"trying to connect to host=%s, port=%d\n", hostname, port); armci_die("armci_CreateSocketAndConnect: socket failed", sock); } /* the following can be disabled */ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof on) == -1) armci_die("armci_CreateSocketAndConnect: error setting REUSEADDR", -1); /* Connect socket */ server.sin_family = AF_INET; hp = gethostbyname(hostname); if (hp == 0) { (void) fprintf(stderr,"trying to connect to host=%s, port=%d\n", hostname, port); armci_die("armci_CreateSocketAndConnect: gethostbyname failed", 0); } bcopy((char *) hp->h_addr, (char *) &server.sin_addr, hp->h_length); server.sin_port = htons((unsigned short) port); trial = 0; againcon: if ((status = connect(sock, (struct sockaddr *) &server, sizeof server)) < 0) { if (errno == EINTR) goto againcon; else if(trial>CONNECT_TRIALS){ (void) fprintf(stderr,"%d:trying connect to host=%s, port=%d t=%d %d\n", armci_me,hostname, port,trial,errno); perror("trying to connect:"); armci_die("armci_CreateSocketAndConnect: connect failed", status); }else { trial++; sleep(1); goto againcon; } } /* Increase size of socket buffers to improve long message performance and increase size of message that goes asynchronously */ armci_tcp_get_sock_buf_size(sock); armci_TcpNoDelay(sock); return sock; } ga-5.9.2/armci/src/devices/sockets/sockets.h000066400000000000000000000032121500715745200207250ustar00rootroot00000000000000#ifndef SOCKETS_H_ #define SOCKETS_H_ #include "armci.h" #ifndef WIN32 #define USE_SOCKET_VECTOR_API #endif #if defined(USE_SOCKET_VECTOR_API) # include #endif extern int tcp_sendrcv_bufsize; extern int armci_PollSocket(int sock); extern int armci_WaitSock(int *socklist, int num, int *ready); extern int armci_ReadFromSocket(int sock, void* buffer, int lenbuf); extern int armci_WriteToSocket (int sock, void* buffer, int lenbuf); #if defined(USE_SOCKET_VECTOR_API) extern int armci_RecvStridedFromSocket(int sock,void* buffer,int *str_arr,int *cnt,int str_level,struct iovec *iov); extern int armci_SendStridedToSocket(int sock,void* buffer,int *str_arr,int *cnt,int str_level,struct iovec *iov); extern int armci_RecvVectorFromSocket(int sock,armci_giov_t darr[], int len,struct iovec *iov); extern int armci_SendVectorToSocket(int sock,armci_giov_t darr[], int len,struct iovec *iov); extern int armci_ReadVFromSocket(int sock,struct iovec *iov, int iovlength, int totalsize); extern int armci_WriteVToSocket (int sock,struct iovec *iov, int iovlength, int totalsize); #endif extern void armci_ListenSockAll(int* socklist, int num); extern void armci_AcceptSockAll(int* socklist, int num); extern int armci_CreateSocketAndConnect(char *hostname, int port); extern void armci_ShutdownAll(int socklist[], int num); extern void armci_CreateSocketAndBind(int *sock, int *port); #define PACKET_SIZE tcp_sendrcv_bufsize #define TIMEOUT_ACCEPT 60 #define GET_SEND_BUFFER _armci_buf_get_clear_busy #define FREE_SEND_BUFFER _armci_buf_release #ifndef UIO_MAXIOV #define MAX_IOVEC 8 #else #define MAX_IOVEC (UIO_MAXIOV>100?100:UIO_MAXIOV) #endif #endif ga-5.9.2/armci/src/ft/000077500000000000000000000000001500715745200144175ustar00rootroot00000000000000ga-5.9.2/armci/src/ft/armci_chkpt.c000066400000000000000000000666351500715745200170670ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /*interfaces for checkpointing */ /* TODO * work on the case if pagenum==firstpage or lastpage when writing pages */ #if HAVE_STDIO_H # include #endif #if HAVE_SETJMP_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_SYSCALL_H # include #endif #if HAVE_SYS_MMAN_H # include #endif #if HAVE_SYS_PARAM_H # include #endif #if HAVE_SYS_WAIT_H # include #endif #if HAVE_SIGNAL_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_FCNTL_H # include #endif #if HAVE_ERRNO_H # include #endif #if HAVE_DIRENT_H # include #endif #if HAVE_STDARG_H # include #endif #include #include "armcip.h" #include "message.h" #include "armci_chkpt.h" #define DEBUG 0 #define DEBUG_ 1 /*\ * ----------CORE FUNCTIONS ----------- * armci_init_checkpoint() - the checkpointing code is initialized * armci_icheckpoint_init() - called when with first checkpoint * armci_icheckpoint_finalize() - called when done with chkpt * armci_icheckpoint() - called every time we checkpoint * armci_recover() - called to recoved * ----------SUPPORT FUNCTIONS ----------- * armci_ckpt_pgfh() - pagefault handler, to set the list of dirty pages * armci_monitor_addr() - monitors address to look for and pages to set readonly * armci_create_record() - create the record for local part of data being stored * armci_protect_pages() - to protect pages in the dirty page list * armci_storage_read() - reads record from storage * armci_storage_write() - writes into storage * armci_storage_fopen() - opens a file in storage * \*/ /*\ global variables \*/ static armci_storage_record_t armci_storage_record[1001]; static int isheap; static int number_of_records=1; static int next_available_rid=0; static int mypagesize; int **armci_rec_ind; static armci_page_info_t armci_dpage_info; static int checkpointing_initialized=0; static int armci_recovering=0; static char *armci_ckpt_bspBottom=NULL; /* return the current position (index) of the ckpt file */ #define CURR_FILE_POS(rid) armci_storage_record[rid].fileinfo.startindex #define UPDATE_FILE_POS(rid,size) \ armci_storage_record[rid].fileinfo.startindex += (size) void ARMCI_Ckpt_create_ds(armci_ckpt_ds_t *ckptds, int count) { armci_create_ckptds(ckptds,count); } int ARMCI_Ckpt_init(char *filename, ARMCI_Group *grp, int savestack, int saveheap, armci_ckpt_ds_t *ckptds) { int rid; rid = armci_icheckpoint_init(filename,grp,savestack,saveheap,ckptds); return(rid); } int ARMCI_Ckpt(int rid) { return(armci_icheckpoint(rid)); } void ARMCI_Ckpt_Recover(int rid, int iamreplacement) { armci_irecover(rid, iamreplacement); } void ARMCI_Ckpt_finalize(int rid) { armci_icheckpoint_finalize(rid); } /* ----------SUPPORT FUNCTIONS ----------- */ /* This function is called from ARMCI sigv and sigbus handler */ int armci_ckpt_pgfh(void *addr, int errno, int fd) { char *paddr; unsigned long pagenum; /*find the page number and the corresponding page aligned address*/ pagenum = (unsigned long)((long)addr/mypagesize); paddr = (char*)(pagenum*mypagesize); if(DEBUG)printf("%d:paddr=%p addr=%p %lu\n",armci_me,paddr,addr,pagenum); /*page is being touched change page permission to READ/WRITE*/ mprotect(paddr, mypagesize, PROT_READ | PROT_WRITE); /*mark pagenumber dirty in dirty page array*/ armci_dpage_info.touched_page_arr[armci_dpage_info.num_touched_pages++] = pagenum; if(pagenumarmci_dpage_info.lastpage) armci_dpage_info.lastpage = pagenum; #if 1 printf("%d: armci_ckpt_pgfh(): num_touched_pages = %ld\n",armci_me,armci_dpage_info.num_touched_pages);fflush(stdout); #endif return(0); } #if 0 printf("%d:pagenum=%d first=%d last=%d\n",armci_me,pagenum,armci_storage_record[i].firstpage,(armci_storage_record[i].firstpage+armci_storage_record[i].totalpages));fflush(stdout); #endif static int armci_create_record(ARMCI_Group *group, int count) { int recind; int relprocid; int rc; rc = ARMCI_Group_rank(group,&relprocid); /*create and broadcast new index in the records data structure*/ next_available_rid = 0; if(next_available_rid==0){ if(relprocid==0) ARMCI_Rmw(ARMCI_FETCH_AND_ADD,&recind,armci_rec_ind[0],1,0); } else recind=next_available_rid; armci_msg_group_bcast_scope(SCOPE_ALL,&recind,sizeof(int),0,group); if(recind>1001) armci_die("create_record, failure",recind); armci_storage_record[recind].pid = armci_me; armci_storage_record[recind].rid = recind; armci_storage_record[recind].rel_pid = relprocid; memcpy(&armci_storage_record[recind].group,group,sizeof(ARMCI_Group)); if(count!=0) armci_storage_record[recind].user_addr = (armci_monitor_address_t *)malloc(sizeof(armci_monitor_address_t)*count); armci_storage_record[recind].user_addr_count=count; if(next_available_rid!=0) next_available_rid = 0; else number_of_records++; return(recind); } static void armci_protect_pages(unsigned long startpagenum,unsigned long numpages) { char *addr; addr =(char *)((unsigned long)(startpagenum*mypagesize)); mprotect(addr, mypagesize*numpages,PROT_READ); if(DEBUG)printf("%d:protecting address %p %ld\n",armci_me,addr,mypagesize*numpages); } /* CHECK: This is a temporary function - remove later. Then make sure remove * the ifdef CHECKPOINT2 in armci_init_checkpoint(). Note this should be * called inside main(int argc, char **argv), I guess ... */ void armci_init_checkpoint2() { printf("%d:in armci init checkpoint2\n",armci_me);fflush(stdout); } /*\ ----------CORE FUNCTIONS ----------- \*/ /*called in armci init*/ int armci_init_checkpoint(int spare) { int rc; extern void ARMCI_Register_Signal_Handler(int sig, void (*func)()); extern void armci_create_ft_group(); #ifdef CHECKPOINT2 printf("%d:in armci init checkpoint\n",armci_me);fflush(stdout); #endif mypagesize = getpagesize(); if(checkpointing_initialized)return(0); /* malloc for record index */ armci_rec_ind = (int **)malloc(sizeof(int *)*armci_nproc); if(armci_me==0){ rc = ARMCI_Malloc((void **)armci_rec_ind, 2*sizeof(int)); armci_rec_ind[armci_me][0]=armci_rec_ind[armci_me][1]=1; } else rc = ARMCI_Malloc((void **)armci_rec_ind, 0); assert(rc==0); ARMCI_Register_Signal_Handler(SIGSEGV,(void *)armci_ckpt_pgfh); armci_dpage_info.touched_page_arr = (unsigned long *)malloc(sizeof(unsigned long)*100000); armci_dpage_info.num_touched_pages=armci_dpage_info.lastpage=0; armci_dpage_info.firstpage = 99999999; armci_create_ft_group(spare); checkpointing_initialized = 1; return(0); } void armci_create_ckptds(armci_ckpt_ds_t *ckptds, int count) { printf("%d:in armci_create_ckptds with count=%d\n",armci_me,count);fflush(stdout); ckptds->count=count; ckptds->ptr_arr=(void **)malloc(sizeof(void *)*(count+1)); ckptds->sz=(size_t *)malloc(sizeof(size_t)*(count+1)); ckptds->saveonce=(int *)calloc((count+1),sizeof(int)); if( ckptds->saveonce==NULL || ckptds->ptr_arr==NULL || ckptds->sz == NULL ) armci_die("malloc failed in armci_create_ckptds",count); } void armci_free_ckptds(armci_ckpt_ds_t *ckptds) { free(ckptds->ptr_arr); free(ckptds->sz); } static void armci_create_protect_pages(armci_monitor_address_t *addrds, void *ptr, unsigned long bytes,int callprotect) { unsigned long laddr; unsigned long totalpages; unsigned long j; addrds->ptr=(void *)(ptr); addrds->bytes = bytes; laddr = (unsigned long)(addrds->ptr); addrds->firstpage = (unsigned long)((unsigned long)laddr/mypagesize); if(laddr%mypagesize ==0){ totalpages = (int)(bytes/mypagesize); if(bytes%mypagesize)totalpages++; } else { int shift; shift = mypagesize - laddr%mypagesize; if(DEBUG);{ printf("%d:shift=%d bytes=%ld\n",armci_me,shift,bytes); fflush(stdout); } if(bytestotalpages = totalpages; addrds->num_touched_pages = totalpages; addrds->touched_page_arr = malloc(totalpages*sizeof(unsigned long)); if(addrds->touched_page_arr==NULL) armci_die("malloc failed in armci_icheckpoint_init",totalpages); addrds->touched_page_arr[0]=addrds->firstpage; for(j=1;jtouched_page_arr[j]=addrds->touched_page_arr[j-1]+1; } if(DEBUG);{ printf("%d: addrds=%p first=%lu total=%lu laddr=%lu (%p)\n",armci_me, addrds, addrds->firstpage, addrds->totalpages, laddr, (void*)laddr); fflush(stdout); } if(callprotect){ if(isheap) armci_protect_pages(addrds->firstpage+16,addrds->totalpages); else armci_protect_pages(addrds->firstpage,addrds->totalpages); } } /*called everytime a new checkpoint record is created*/ int armci_icheckpoint_init(char *filename,ARMCI_Group *grp, int savestack, int saveheap, armci_ckpt_ds_t *ckptds) { int rid; int i=0; char line[256],tmpfilename[100]; unsigned long databottom; FILE *fp; printf("%d:in armci ckpt init\n",armci_me);fflush(stdout); if(DEBUG && ckptds!=NULL) printf("%d:ckptdscount=%d\n",armci_me,ckptds->count); /*create the record*/ if(ckptds!=NULL) rid = armci_create_record(grp,ckptds->count); else rid = armci_create_record(grp,0); if(DEBUG) printf("%d:got rid = %d\n",armci_me,rid);fflush(stdout); armci_storage_record[rid].ckpt_heap = saveheap; armci_storage_record[rid].ckpt_stack = savestack; /*open the file for reading and writing*/ if(filename == NULL){ filename = (char *)malloc(sizeof(char)*(11+1+6+1+4)); if(filename==NULL)armci_die("alloc for filename failed",11+1+6+1+4); sprintf(filename,"%s","armci_chkpt_"); sprintf((filename+strlen(filename)),"%d",armci_me); sprintf((filename+strlen(filename)),"%s","_"); sprintf(filename+strlen(filename),"%d",rid); } armci_storage_record[rid].fileinfo.filename = malloc(sizeof(char)*strlen(filename)); if(NULL==armci_storage_record[rid].fileinfo.filename) armci_die("malloc failed for filename in ga_icheckpoint_init",0); strcpy(armci_storage_record[rid].fileinfo.filename,filename); armci_storage_record[rid].fileinfo.fd = armci_storage_fopen(filename); armci_storage_record[rid].fileinfo.startindex = 0; /* initialize */ if(DEBUG){printf("filename=%s\n",filename);fflush(stdout);} sprintf(tmpfilename, "/proc/%d/maps",getpid()); fp=fopen(tmpfilename, "r"); if (fp == NULL){ armci_die("couldnt find dseg base",getpid()); } if(armci_storage_record[rid].ckpt_stack){ unsigned long stackbottom; char *start,*end,*tmp; armci_monitor_address_t *addrds =&armci_storage_record[rid].stack_mon; do { fgets(line, 255, fp); } while ( line[0] != '6' ); sscanf(line, "%p", (void**)&databottom); printf("%p databot\n",(char *)databottom); # ifdef KERNEL_2_4 do { start = fgets(line, 255, fp); } while(start != NULL); # else /* KERNEL_2_6 */ { char tmpline[256]; do { strncpy(line, tmpline, 256); start = fgets(tmpline, 255, fp); } while(tmpline[0] == '6'); } # endif start = strstr(line, "-") + 1; if(DEBUG);{printf("stack top=%s\n",start);fflush(stdout);} end = strstr(line, " "); *end = 0; sscanf(start, "%p", (void**)&stackbottom); addrds->ptr = (void *)(stackbottom); } if(armci_storage_record[rid].ckpt_heap){ char *datatop; armci_monitor_address_t *addrds =&armci_storage_record[rid].heap_mon; datatop=(char *)sbrk(0); if(!armci_storage_record[rid].ckpt_stack){ do { fgets(line, 255, fp); } while ( line[0] != '6' ); sscanf(line, "%p", (void**)&databottom); } printf("%d:databot=%p datatop=%p %ld %p %p\n",armci_me,(void*)databottom,datatop,(unsigned long)((char *)datatop-(char *)databottom),&armci_storage_record[rid].jmp, &addrds); isheap = 1; printf("I'm here 1\n"); fflush(stdout); armci_create_protect_pages(addrds,(void *)databottom,(unsigned long)((char *)datatop-(char *)databottom),1); printf("I'm here 1a\n"); fflush(stdout); isheap = 0; mprotect(armci_storage_record,sizeof(armci_storage_record_t)*1000, PROT_READ | PROT_WRITE); mprotect(&armci_recovering,sizeof(int), PROT_READ | PROT_WRITE); mprotect(&armci_dpage_info,sizeof(armci_page_info_t), PROT_READ | PROT_WRITE); mprotect(armci_dpage_info.touched_page_arr,sizeof(unsigned long)*99999999, PROT_READ | PROT_WRITE); printf("I'm here 2\n"); fflush(stdout); } else { if(ckptds!=NULL) for(i=0;icount;i++){ armci_monitor_address_t *addrds =&armci_storage_record[rid].user_addr[i]; addrds->saveonce = ckptds->saveonce[i]; if(addrds->saveonce) armci_create_protect_pages(addrds,ckptds->ptr_arr[i],ckptds->sz[i],0); else armci_create_protect_pages(addrds,ckptds->ptr_arr[i],ckptds->sz[i],1); } } if(DEBUG){printf("%d:completed init\n",armci_me);fflush(stdout);} return(rid); } int armci_create_touchedpagearray(unsigned long *tpa,unsigned long firstpage, unsigned long totalpages){ unsigned long i,j=0; printf("In armci_create_touchedpagearray(): armci_dpage_info.num_touched_page=%ld\n", armci_dpage_info.num_touched_pages); tpa = (unsigned long *)malloc(sizeof(unsigned long)*armci_dpage_info.num_touched_pages); for(i=0;i=firstpage || armci_dpage_info.touched_page_arr[i] < (firstpage+totalpages)){ tpa[j]=armci_dpage_info.touched_page_arr[i]; j++; } } armci_dpage_info.num_touched_pages=0; return(j); } /* CHECK: put all the defines here in the armci_chpt.h header or in some * meaningful header*/ /* returns the stack head address. SP register contains this address. We save some EXTRA_SPACE as there may be some space used below the current SP */ #define EXTRA_STACK_SPACE (1 << 11) #define TMP_STACK_SIZE (1 << 12) /* CHECK: 4K is enough, I guess */ #ifndef JB_SP #define JB_SP 0 #endif #define EST_OFFSET 100 #define ARMCI_STACK_VERIFY 1234 #define STACK_TOP ((unsigned long)((unsigned long)(&dummy_first) - EST_OFFSET)) #if 0 static char* stack_head_addr() { jmp_buf tmp_j; setjmp(tmp_j); return ((char*) ((tmp_j->__jmpbuf[JB_SP] - EXTRA_STACK_SPACE) & (~PAGE_SIZE)) ); } /* to get the stack pointer */ unsigned long get_esp() { __asm__(" movl %esp,%eax "); } #endif void what_is_going_on() { int a; printf("what_is_going_on(): a=%p\n", &a); } static void armci_ckpt_write_stack(int rid) { int dummy_first=ARMCI_STACK_VERIFY; char *top=NULL; off_t ofs = 0; int dummy_last=ARMCI_STACK_VERIFY; /* top = stack_head_addr(); */ top = (char*)STACK_TOP; armci_storage_record[rid].stack_mon.bytes=(unsigned long)(armci_storage_record[rid].stack_mon.ptr)-((unsigned long)(top)); ofs=CURR_FILE_POS(rid); UPDATE_FILE_POS(rid, armci_storage_record[rid].stack_mon.bytes); armci_storage_record[rid].stack_mon.fileoffset = ofs; printf("%d: ptr = %p\n",armci_me,armci_storage_record[rid].stack_mon.ptr); printf("%d: Save stack: %p to %p (bytes=%ld : off=%ld)\n\n",armci_me, top, top+armci_storage_record[rid].stack_mon.bytes, armci_storage_record[rid].stack_mon.bytes, ofs);fflush(stdout); armci_storage_write_ptr(armci_storage_record[rid].fileinfo.fd,top, armci_storage_record[rid].stack_mon.bytes, armci_storage_record[rid].stack_mon.fileoffset); } static void armci_ckpt_write_heap(int rid) { int j; off_t ofs = 0; char *addr=NULL; armci_monitor_address_t *addrds =&armci_storage_record[rid].heap_mon; addr = sbrk(0); ofs=CURR_FILE_POS(rid); /* UPDATE_FILE_POS(rid, armci_storage_record[rid].heap_mon.bytes); */ armci_storage_record[rid].heap_mon.fileoffset = ofs; if(addr > (char *)armci_storage_record[rid].heap_mon.ptr){ /*this means change in data segment - save what ever is new and reset size*/ void *tmpaddr = addrds->ptr; void *tmpaddr1 = (void *)((char *)addrds->ptr+addrds->bytes); unsigned long firstpage = addrds->firstpage; unsigned long totalpages = addrds->totalpages; /*first save new pages*/ ofs=(off_t)(armci_storage_record[rid].heap_mon.fileoffset+totalpages*mypagesize); /*problem here - remove mallocs*/ isheap = 1; armci_create_protect_pages(addrds,tmpaddr1,(addr-(char *)tmpaddr1),1); isheap = 0; printf("%d: Hello 1\n", armci_me); armci_storage_write_pages(armci_storage_record[rid].fileinfo.fd,addrds->firstpage,addrds->touched_page_arr,addrds->num_touched_pages,mypagesize,ofs); /*now write the touched pages*/ addrds->ptr = tmpaddr; addrds->bytes = addr-(char *)tmpaddr; addrds->firstpage = firstpage; /*problem here if last data seg addr is not a page boundary*/ addrds->totalpages+=totalpages; } /*write touched pages since*/ ofs=(off_t)(armci_storage_record[rid].heap_mon.fileoffset); addrds->num_touched_pages = armci_create_touchedpagearray(addrds->touched_page_arr,addrds->firstpage,addrds->totalpages); printf("%d: Hello 2\n", armci_me); /*problem here - remove mallocs*/ if(addrds->num_touched_pages!=0) addrds->num_touched_pages = armci_create_touchedpagearray(addrds->touched_page_arr,addrds->firstpage,addrds->totalpages); printf("%d: Hello 3: %ld %p %ld %d %ld\n", armci_me,addrds->firstpage,addrds->touched_page_arr,addrds->num_touched_pages,mypagesize,ofs); armci_storage_write_pages(armci_storage_record[rid].fileinfo.fd,addrds->firstpage,addrds->touched_page_arr,addrds->num_touched_pages,mypagesize,ofs); printf("%d: Hello 3a\n", armci_me); for(j=0;jnum_touched_pages;j++){ addr =(char *)(addrds->touched_page_arr[j]*mypagesize); mprotect(addr, mypagesize,PROT_READ); } printf("%d: Hello 4\n", armci_me); } static void armci_ckpt_write_data(int rid) { int i,j; off_t ofs=0; char *addr=NULL; printf("%d: armci_ckpt_write_data(): Saving data ...\n", armci_me); for(i=0;ifileoffset = ofs; /*if(addrds->num_touched_pages!=0) CHECK this... */ addrds->num_touched_pages = armci_create_touchedpagearray(addrds->touched_page_arr,addrds->firstpage,addrds->totalpages); printf("%d: DATA:[i=%d] addrds=%p off=%ld size=%ld (#ofPages=%ld total=%ld pagesize=%d)\n", armci_me, i, addrds, addrds->fileoffset, addrds->num_touched_pages*mypagesize, addrds->num_touched_pages, addrds->totalpages, mypagesize); armci_storage_write_pages(armci_storage_record[rid].fileinfo.fd,addrds->firstpage,addrds->touched_page_arr,addrds->num_touched_pages,mypagesize,ofs); for(j=0;jnum_touched_pages;j++){ addr =(char *)(addrds->touched_page_arr[j]*mypagesize); mprotect(addr, mypagesize,PROT_READ); } /* CHECK: should I uncomment this ? check it out! bzero(addrds->touched_page_arr, sizeof(unsigned long)*addrds->num_touched_pages); addrds->num_touched_pages = 0; */ /* update the file offset */ UPDATE_FILE_POS(rid, addrds->bytes); } } static void armci_ckpt_write_rid(int rid) { } /*get the list of changed pages from touched_page_array and rewrite the * changed pages*/ int armci_icheckpoint(int rid) { int rc=ARMCI_CKPT; off_t ofs; if(DEBUG);{ printf("%d: in checkpoint rid=%d %p\n",armci_me,rid,&armci_recovering);fflush(stdout); } if(armci_storage_record[rid].ckpt_stack || armci_storage_record[rid].ckpt_heap) { if((armci_recovering=setjmp(armci_storage_record[rid].jmp))==0){ /* 1. file offsets */ armci_storage_record[rid].fileinfo.startindex = 0; ofs=CURR_FILE_POS(rid); UPDATE_FILE_POS(rid,sizeof(jmp_buf)); /* 1a. save jmp buffer env */ printf("%d: Save jmp_buf: %p to %p (bytes=%ld : off=%ld)\n\n",armci_me, &armci_storage_record[rid].jmp, &armci_storage_record[rid].jmp+armci_storage_record[rid].stack_mon.bytes, sizeof(jmp_buf), ofs);fflush(stdout); armci_storage_write_ptr(armci_storage_record[rid].fileinfo.fd, &armci_storage_record[rid].jmp, sizeof(jmp_buf), ofs); /* 2. save stack */ printf("%d: Saving stack\n", armci_me); if(armci_storage_record[rid].ckpt_stack){ armci_ckpt_write_stack(rid); } /* 3. save data segment (entire heap (or) user specified data ) */ if(armci_storage_record[rid].ckpt_heap) armci_ckpt_write_heap(rid); else armci_ckpt_write_data(rid); /* 4. CHECK: sync file system, thus data is flushed to disk */ /* armci_storage_fsync(armci_storage_record[rid].fileinfo.fd); */ /* 5. TODO: save the record index in the file. Caution: there are mallocs in the structure. beware. */ armci_ckpt_write_rid(rid); } else { /*long jump brings us here */ /* CHECK: open the ckpt files*/ printf("long jump brought us here. Performed Recovery..\n"); printf("address(rid)=%p address(rc)=%p\n", &rid, &rc); what_is_going_on(); printf("rid=%d address(rid)=%p\n", rid, &rid); rc = ARMCI_RESTART; } } else{ armci_ckpt_write_data(rid); } armci_msg_group_barrier(&armci_storage_record[rid].group); printf("%d: After Barrier\n", armci_me); return(rc); } static void armci_recover_memory(int rid) { int dummy; off_t ofs; size_t stacksize = armci_storage_record[rid].stack_mon.bytes; char *stacktop = (char*)((unsigned long)(armci_storage_record[rid].stack_mon.ptr) - stacksize); printf("armci_recover_stack(): check=%p ; rid=%d\n", &dummy, rid); /* call recursively until current stack is above saved stack */ if( (unsigned long)&dummy >= (unsigned long)(stacktop-EST_OFFSET) ) armci_recover_memory(rid); /* ------------------ recover stack segment ------------------- */ printf("%d: armci_recover_stack(): fp=%p size=%ld off=%ld stack: %p to %p\n", armci_me, armci_storage_record[rid].fileinfo.fd, stacksize, armci_storage_record[rid].stack_mon.fileoffset, stacktop, armci_storage_record[rid].stack_mon.ptr); armci_storage_read_ptr(armci_storage_record[rid].fileinfo.fd, stacktop, stacksize, armci_storage_record[rid].stack_mon.fileoffset); { /* verify stack recovery */ int dummy = *((int*)(stacktop+EST_OFFSET)); if(dummy != ARMCI_STACK_VERIFY) { printf("WARNING: armci_recover_stack FAILED: %d", dummy); armci_die("armci_recover_stack FAILED", dummy); } else if(DEBUG_) printf("%d: armci_recover_stack SUCCESS (%d)\n", armci_me, dummy); } ofs=0; /* jmp_buf is the first one to be stored in ckpt file, so ofs=0 */ printf("%d: armci_recover jmp_buf(): size=%ld off=%ld (%p to %p)\n", armci_me, sizeof(jmp_buf), ofs, &armci_storage_record[rid].jmp, (char*)(&armci_storage_record[rid].jmp)+sizeof(jmp_buf)); armci_storage_read_ptr(armci_storage_record[rid].fileinfo.fd, &armci_storage_record[rid].jmp, sizeof(jmp_buf), ofs); armci_msg_group_barrier(&armci_storage_record[rid].group); printf("%d: restoring original stack starts @ %p\n", armci_me, (void*)armci_storage_record[rid].jmp->__jmpbuf[JB_SP]); longjmp(armci_storage_record[rid].jmp,1);/*goto the restored stack*/ } static void armci_recover_stack(int rid, char *filename) { armci_recover_memory(rid); } static void armci_recover_heap(int rid, char *filename) { /* TODO: restore heap */ } static void armci_recover_data(int rid, char *filename) { int i,j; printf("%d: armci_recover_data(): rid=%d\n", armci_me, rid); for(i=0;iptr, addrds->bytes, addrds->fileoffset); armci_storage_read_pages(armci_storage_record[rid].fileinfo.fd, addrds->firstpage, addrds->touched_page_arr, addrds->num_touched_pages, mypagesize, addrds->fileoffset); } } int armci_irecover_NEW(int rid,int iamreplacement) { /* CHECK: do you need to fille rest of fileinfo like name, startindex? */ char *filename = armci_storage_record[rid].fileinfo.filename; sprintf(filename,"%s","armci_chkpt_"); sprintf((filename+strlen(filename)),"%d",armci_me); sprintf((filename+strlen(filename)),"%s","_"); sprintf(filename+strlen(filename),"%d",rid); printf("\n%d: Starting recovery...\n", armci_me); printf("%d: filename = %s\n", armci_me, filename); if(armci_storage_record[rid].ckpt_heap) { armci_recover_heap(rid,filename); } else { armci_recover_data(rid, filename); } /* stack should be the last thing recovered, as it calls longjmp() */ if(armci_storage_record[rid].ckpt_stack) armci_recover_stack(rid,filename); /*we should never come here things are hosed */ armci_die("recovery hosed",0); return(1); } int armci_irecover(int rid,int iamreplacement) { armci_irecover_NEW(rid, iamreplacement); return 1; } void armci_icheckpoint_finalize(int rid) { int i; armci_msg_group_barrier(&armci_storage_record[rid].group); for(i=0;itouched_page_arr); } free(armci_storage_record[rid].user_addr); free(armci_storage_record[rid].fileinfo.filename); armci_storage_fclose(armci_storage_record[rid].fileinfo.fd); next_available_rid = rid; } /* TODO 1. checkpoint shared memory and mmap regions 2. I/O file open/close, signals and other system specific stuff ? 3. memory leaks due to malloc() - free them */ ga-5.9.2/armci/src/ft/armci_chkpt.h000066400000000000000000000023061500715745200170550ustar00rootroot00000000000000 #ifndef _ARMCI_CHKPT_H #define _ARMCI_CHKPT_H #include "armci_storage.h" typedef struct{ void *ptr; size_t bytes; int saveonce; unsigned long *touched_page_arr; unsigned long num_touched_pages; unsigned long firstpage; unsigned long totalpages; unsigned long fileoffset; } armci_monitor_address_t; typedef struct{ FILE_DS fd; long startindex; int status; char *filename; } armci_file_info_t; typedef struct{ unsigned long num_touched_pages; unsigned long *touched_page_arr; unsigned long firstpage; unsigned long lastpage; }armci_page_info_t; typedef struct{ int rid; /*unique record id*/ int pid; /*id of the process*/ int rel_pid; /*group pid for the process*/ int tmp; /*for jmp_buf alignment*/ jmp_buf jmp; /*the jmp buffer for setjmp and longjmp*/ int ckpt_heap,ckpt_stack; armci_monitor_address_t stack_mon,heap_mon; armci_monitor_address_t *user_addr; int user_addr_count; armci_file_info_t fileinfo; ARMCI_Group group; } armci_storage_record_t; #endif /* _ARMCI_CHKPT_H */ ga-5.9.2/armci/src/ft/armci_storage.c000066400000000000000000000074171500715745200174130ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_SYSCALL_H # include #endif #if HAVE_SIGNAL_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_FCNTL_H # include #endif #if HAVE_ERRNO_H # include #endif #if HAVE_STDARG_H # include #endif #include "armci_storage.h" extern int armci_me; extern void armci_die(char *msg, int code); #define DEBUG 0 FILE_DS armci_storage_fopen(char *filename) { FILE_DS file_d; remove ( filename ); file_d = fopen(filename,"w+"); if(file_d==NULL) armci_die("armci_storage_fopen(): cannot open file",0); if(DEBUG); printf("\n%d:filed=%p %s\n",armci_me,file_d,filename); return(file_d); } FILE_DS armci_storage_fopen_RONLY(char *filename) { FILE_DS file_d; file_d = fopen(filename,"r"); if(file_d==NULL) armci_die("armci_storage_fopen_RONLY():cannot open file",0); if(DEBUG); printf("\n%d:filed=%p %s\n",armci_me,file_d,filename); return(file_d); } void armci_storage_fclose(FILE_DS filed) { int rc = fclose(filed); if(rc!=0)armci_die("armci_storage_fclose(): cannot close file",0); } int armci_storage_read_ptr(FILE_DS file_d,void *ptr,size_t size,off_t ofs) { int rc=0,orc=0,isize=size; if(isize<0) armci_die("armci_storage_read_ptr(): Invalid size(<0)", isize); rc = fseek(file_d,ofs,SEEK_SET); if(rc)armci_die("fseek failed in armci_storage_read_ptr",rc); while(orc!=isize){ printf("ptr=%p ofs=%ld size=%ld filed=%p\n", ptr,ofs,size,file_d); rc = fread(ptr,1,size,file_d); if(!rc) armci_die("armci_storage_read_ptr(): fread failed",0); orc+=rc; if(DEBUG); printf("\n%d:read %d so far of %d\n",armci_me,orc,isize); if(orc!=isize){ ptr+=rc; size-=rc; } } rc = fseek(file_d,0,SEEK_SET); if(rc)armci_die("fseek failed in armci_storage_read_ptr",rc); return 0; } int armci_storage_read_pages(FILE_DS file_d, unsigned long first_page, unsigned long *page_arr, unsigned long page_arr_sz,int pagesize, off_t ofs) { int i,rc=0; /*this can be heavily optimized*/ for(i=0;i #endif #if HAVE_SETJMP_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_SYSCALL_H # include #endif #if HAVE_SYS_MMAN_H # include #endif #if HAVE_SYS_PARAM_H # include #endif #if HAVE_SYS_WAIT_H # include #endif #if HAVE_SIGNAL_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_FCNTL_H # include #endif #if HAVE_ERRNO_H # include #endif #if HAVE_DIRENT_H # include #endif #if HAVE_STDARG_H # include #endif #define FILE_DS FILE * FILE_DS armci_storage_fopen(char *filename); FILE_DS armci_storage_fopen_RONLY(char *filename); void armci_storage_fclose(FILE_DS filed); int armci_storage_read_ptr(FILE_DS file_d,void *ptr,size_t size,off_t ofs); int armci_storage_read_pages(FILE_DS file_d, unsigned long first_page, unsigned long *page_arr, unsigned long page_arr_sz,int pagesize, off_t ofs); int armci_storage_write_ptr(FILE_DS file_d,void *ptr,size_t size,off_t ofs); int armci_storage_write_pages(FILE_DS file_d, unsigned long first_page, unsigned long *page_arr, unsigned long page_arr_sz,int pagesize, off_t ofs); ga-5.9.2/armci/src/include/000077500000000000000000000000001500715745200154315ustar00rootroot00000000000000ga-5.9.2/armci/src/include/acc.h000066400000000000000000000216471500715745200163420ustar00rootroot00000000000000#ifndef _ACC_H_ #define _ACC_H_ typedef struct { float real; float imag; } complex_t; typedef struct { double real; double imag; } dcomplex_t; void c_d_accumulate_1d_(const double* const restrict alpha, double* const restrict A, const double* const restrict B, const int* const restrict rows); void c_f_accumulate_1d_(const float* const restrict alpha, float* const restrict A, const float* const restrict B, const int* const restrict rows); void c_c_accumulate_1d_(const complex_t* const restrict alpha, complex_t* const restrict A, const complex_t* const restrict B, const int* const restrict rows); void c_z_accumulate_1d_(const dcomplex_t* const restrict alpha, dcomplex_t* const restrict A, const dcomplex_t* const restrict B, const int* const restrict rows); void c_i_accumulate_1d_(const int* const restrict alpha, int* const restrict A, const int* const restrict B, const int* const restrict rows); void c_l_accumulate_1d_(const long* const restrict alpha, long* const restrict A, const long* const restrict B, const int* const restrict rows); void c_ll_accumulate_1d_(const long long* const restrict alpha, long long* const restrict A, const long long* const restrict B, const int* const restrict rows); void c_d_accumulate_2d_(const double* const restrict alpha, const int* const restrict rows, const int* const restrict cols, double* const restrict A, const int* const restrict ald, const double* const restrict B, const int* const restrict bld); void c_f_accumulate_2d_(const float* const restrict alpha, const int* const restrict rows, const int* const restrict cols, float* const restrict A, const int* const restrict ald, const float* const restrict B, const int* const restrict bld); void c_c_accumulate_2d_(const complex_t* const restrict alpha, const int* const restrict rows, const int* const restrict cols, complex_t* const restrict A, const int* const restrict ald, const complex_t* const restrict B, const int* const restrict bld); void c_z_accumulate_2d_(const dcomplex_t* const restrict alpha, const int* const restrict rows, const int* const restrict cols, dcomplex_t* const restrict A, const int* const restrict ald, const dcomplex_t* const restrict B, const int* const restrict bld); void c_i_accumulate_2d_(const int* const restrict alpha, const int* const restrict rows, const int* const restrict cols, int* const restrict A, const int* const restrict ald, const int* const restrict B, const int* const restrict bld); void c_l_accumulate_2d_(const long* const restrict alpha, const int* const restrict rows, const int* const restrict cols, long* const restrict A, const int* const restrict ald, const long* const restrict B, const int* const restrict bld); void c_ll_accumulate_2d_(const long long* const restrict alpha, const int* const restrict rows, const int* const restrict cols, long long* const restrict A, const int* const restrict ald, const long long* const restrict B, const int* const restrict bld); void c_d_accumulate_2d_u_(const double* const restrict alpha, const int* const restrict rows, const int* const restrict cols, double* const restrict A, const int* const restrict ald, const double* const restrict B, const int* const restrict bld); void c_f_accumulate_2d_u_(const float* const restrict alpha, const int* const restrict rows, const int* const restrict cols, float* const restrict A, const int* const restrict ald, const float* const restrict B, const int* const restrict bld); void c_c_accumulate_2d_u_(const complex_t* const restrict alpha, const int* const restrict rows, const int* const restrict cols, complex_t* const restrict A, const int* const restrict ald, const complex_t* const restrict B, const int* const restrict bld); void c_z_accumulate_2d_u_(const dcomplex_t* const restrict alpha, const int* const restrict rows, const int* const restrict cols, dcomplex_t* const restrict A, const int* const restrict ald, const dcomplex_t* const restrict B, const int* const restrict bld); void c_i_accumulate_2d_u_(const int* const restrict alpha, const int* const restrict rows, const int* const restrict cols, int* const restrict A, const int* const restrict ald, const int* const restrict B, const int* const restrict bld); void c_l_accumulate_2d_u_(const long* const restrict alpha, const int* const restrict rows, const int* const restrict cols, long* const restrict A, const int* const restrict ald, const long* const restrict B, const int* const restrict bld); void c_ll_accumulate_2d_u_(const long long* const restrict alpha, const int* const restrict rows, const int* const restrict cols, long long* const restrict A, const int* const restrict ald, const long long* const restrict B, const int* const restrict bld); void c_dadd_(const int* const restrict n, double* const restrict x, const double* const restrict work); void c_dadd2_(const int* const restrict n, double* const restrict x, const double* const restrict work, const double* const restrict work2); void c_dmult_(const int* const restrict n, double* const restrict x, const double* const restrict work); void c_dmult2_(const int* const restrict n, double* const restrict x, const double* const restrict work, const double* const restrict work2); #define I_ACCUMULATE_1D c_i_accumulate_1d_ #define L_ACCUMULATE_1D c_l_accumulate_1d_ #define LL_ACCUMULATE_1D c_ll_accumulate_1d_ #define D_ACCUMULATE_1D c_d_accumulate_1d_ #define C_ACCUMULATE_1D c_c_accumulate_1d_ #define Z_ACCUMULATE_1D c_z_accumulate_1d_ #define F_ACCUMULATE_1D c_f_accumulate_1d_ #define I_ACCUMULATE_2D c_i_accumulate_2d_ #define L_ACCUMULATE_2D c_l_accumulate_2d_ #define LL_ACCUMULATE_2D c_ll_accumulate_2d_ #define D_ACCUMULATE_2D c_d_accumulate_2d_ #define C_ACCUMULATE_2D c_c_accumulate_2d_ #define Z_ACCUMULATE_2D c_z_accumulate_2d_ #define F_ACCUMULATE_2D c_f_accumulate_2d_ #define FORT_DADD c_dadd_ #define FORT_DADD2 c_dadd2_ #define FORT_DMULT c_dmult_ #define FORT_DMULT2 c_dmult2_ #endif /* _ACC_H_ */ ga-5.9.2/armci/src/include/armci.h000066400000000000000000000416731500715745200167100ustar00rootroot00000000000000/* ARMCI header file */ #ifndef _ARMCI_H #define _ARMCI_H /* for size_t */ #include #ifdef MSG_COMMS_MPI #include #endif #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif extern int armci_sameclusnode(int proc); typedef struct { void **src_ptr_array; void **dst_ptr_array; int ptr_array_len; int bytes; } armci_giov_t; typedef long armci_size_t; extern int armci_notify(int proc); extern int armci_notify_wait(int proc,int *pval); extern int ARMCI_Init(void); /* initialize ARMCI */ #ifdef MSG_COMMS_MPI extern int ARMCI_Init_mpi_comm(MPI_Comm comm); /* initialize ARMCI */ #endif extern int ARMCI_Init_args(int *argc, char ***argv); /* initialize ARMCI */ extern int ARMCI_Initialized(); extern void ARMCI_Barrier(void); /* ARMCI Barrier*/ extern int ARMCI_Put(void *src, void* dst, int bytes, int proc); extern int ARMCI_Put_flag(void *src, void* dst,int bytes,int *f,int v,int proc); /* On path for deprecation */ #define ARMCI_Put1(_s,_d,_b,_p) memcpy(_d,_s,_b), 0 extern int ARMCI_PutS( /* strided put */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc /* remote process(or) ID */ ); extern int ARMCI_PutS_flag_dir( /* put with flag that uses direct put */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of segments at each stride levels: count[0]=bytes*/ int stride_levels, /* number of stride levels */ int *flag, /* pointer to remote flag */ int val, /* value to set flag upon completion of data transfer */ int proc /* remote process(or) ID */ ); extern int ARMCI_PutS_flag( void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of segments at each stride levels: count[0]=bytes*/ int stride_levels, /* number of stride levels */ int *flag, /* pointer to remote flag */ int val, /* value to set flag upon completion of data transfer */ int proc /* remote process(or) ID */ ); extern int ARMCI_Acc(int optype, void *scale, void *src, void* dst, int bytes, int proc); extern int ARMCI_AccS( /* strided accumulate */ int optype, /* operation */ void *scale, /* scale factor x += scale*y */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc /* remote process(or) ID */ ); extern int ARMCI_Get(void *src, void* dst, int bytes, int proc); extern int ARMCI_GetS( /* strided get */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc /* remote process(or) ID */ ); extern int ARMCI_GetV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ); extern int ARMCI_PutV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ); extern int ARMCI_AccV( int op, /* operation code */ void *scale, /* scaling factor for accumulate */ armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ); extern int ARMCI_PutValueInt(int src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc /* remote process (or) ID */ ); extern int ARMCI_PutValueLong(long src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc /* remote process (or) ID */ ); extern int ARMCI_PutValueFloat(float src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc /* remote process (or) ID */ ); extern int ARMCI_PutValueDouble(double src,/* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc /* remote process (or) ID */ ); extern int ARMCI_GetValueInt(void *src, int proc); extern long ARMCI_GetValueLong(void *src, int proc); extern float ARMCI_GetValueFloat(void *src, int proc); extern double ARMCI_GetValueDouble(void *src, int proc); extern int ARMCI_Malloc(void* ptr_arr[], armci_size_t bytes); extern int ARMCI_Malloc_memdev(void* ptr_arr[], armci_size_t bytes, const char* device); extern int ARMCI_Free(void *ptr); extern int ARMCI_Free_memdev(void *ptr); extern void* ARMCI_Malloc_local(armci_size_t bytes); extern int ARMCI_Free_local(void *ptr); extern int ARMCI_Same_node(int proc); extern void ARMCI_Finalize(); /* terminate ARMCI */ extern void ARMCI_Error(const char *msg, int code); extern void ARMCI_Fence(int proc); extern void ARMCI_AllFence(void); extern int ARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc); extern void ARMCI_Cleanup(void); extern int ARMCI_Create_mutexes(int num); extern int ARMCI_Destroy_mutexes(void); extern void ARMCI_Lock(int mutex, int proc); extern void ARMCI_Unlock(int mutex, int proc); extern void ARMCI_Set_shm_limit(unsigned long shmemlimit); extern int ARMCI_Uses_shm(); extern void ARMCI_Copy(void *src, void *dst, int n); #define FAIL -1 #define FAIL2 -2 #define FAIL3 -3 #define FAIL4 -4 #define FAIL5 -5 #define FAIL6 -6 #define FAIL7 -7 #define FAIL8 -8 #define ARMCI_SWAP 10 #define ARMCI_SWAP_LONG 11 #define ARMCI_FETCH_AND_ADD 12 #define ARMCI_FETCH_AND_ADD_LONG 13 #define ARMCI_ACC_OFF 36 #define ARMCI_ACC_INT (ARMCI_ACC_OFF + 1) #define ARMCI_ACC_DBL (ARMCI_ACC_OFF + 2) #define ARMCI_ACC_FLT (ARMCI_ACC_OFF + 3) #define ARMCI_ACC_CPL (ARMCI_ACC_OFF + 4) #define ARMCI_ACC_DCP (ARMCI_ACC_OFF + 5) #define ARMCI_ACC_LNG (ARMCI_ACC_OFF + 6) #define ARMCI_MAX_STRIDE_LEVEL 8 /****************Error/termination macros************************/ /**Debug assert macro. To be used instead of assert for more user * informative and cleaner death. Also allows individualized * enabling/disabling of assert statements. * @param _enable Ignore this assertion if _enable==0 * @param _cond Condition to be evaluated (assert that it is true) * @param _plist Information to be printed using printf, should be * within parenthesis (eg., dassert(1,0,("%d:test n=%d\n",me,0)); * ). This is fed directly to printf. */ int dassertp_fail(const char *cond_string, const char *file, const char *func, unsigned int line, int code); void derr_printf(const char *format, ...); #undef dassertp #define dassertp(_enable,_cond,_plist) do { \ if((_enable) && !(_cond)) { \ derr_printf _plist; \ dassertp_fail(#_cond,__FILE__,FUNCTION_NAME,__LINE__,1); \ }} while(0) #undef dassertc #define dassertc(_enable,_cond,_plist,_code) do { \ if((_enable) && !(_cond)) { \ derr_printf _plist; \ dassertp_fail(#_cond,__FILE__,FUNCTION_NAME,__LINE__,_code); \ }} while(0) #undef dassert #define dassert(_enable,_cond) dassertp((_enable),(_cond),("")) #undef dassert1 #define dassert1(_enable,_cond,_ival) \ dassertp((_enable),(_cond),("%d: error ival=%d\n", \ armci_msg_me(),(int)(_ival))) #define armci_die(_msg,_code) dassertc(1,0, \ ("%d:%s: %d\n", armci_msg_me(),(_msg),(_code)),_code) #define armci_die2(_msg,_code1,_code2) dassertc(1,0, \ ("%d:%s: (%d,%d)\n",armci_msg_me(),(_msg),(_code1),(_code2)),_code1) /************ locality information **********************************************/ typedef int armci_domain_t; #define ARMCI_DOMAIN_SMP 0 /* SMP node domain for armci_domain_XXX calls */ extern int armci_domain_nprocs(armci_domain_t domain, int id); extern int armci_domain_id(armci_domain_t domain, int glob_proc_id); extern int armci_domain_glob_proc_id(armci_domain_t domain, int id, int loc_proc_id); extern int armci_domain_my_id(armci_domain_t domain); extern int armci_domain_count(armci_domain_t domain); extern int armci_domain_same_id(armci_domain_t domain, int proc); extern char *mp_group_name; /*********************stuff for non-blocking API******************************/ /*\ the request structure for non-blocking api. \*/ /*max of sizes for all platforms. Increase if any platform needs more*/ typedef struct{ int data[4]; /* tag, bufid, agg_flag, op, proc */ double dummy[72]; } armci_hdl_t; #define armci_req_t armci_hdl_t typedef int ARMCI_Group; extern void ARMCI_Group_create(int n, int *pid_list, ARMCI_Group *group_out); extern void ARMCI_Group_create_child(int n, int *pid_list, ARMCI_Group *group_out, ARMCI_Group *group_parent); extern void ARMCI_Group_free(ARMCI_Group *group); extern int ARMCI_Group_rank(ARMCI_Group *group, int *rank); extern void ARMCI_Group_size(ARMCI_Group *group, int *size); extern void ARMCI_Group_set_default(ARMCI_Group *group); extern void ARMCI_Group_get_default(ARMCI_Group *group_out); extern void ARMCI_Group_get_world(ARMCI_Group *group_out); extern void ARMCI_GroupFence(ARMCI_Group *group); extern int ARMCI_Absolute_id(ARMCI_Group *group, int group_rank); extern int ARMCI_Uses_shm_grp(ARMCI_Group *group); extern int ARMCI_Malloc_group(void *ptr_arr[], armci_size_t bytes,ARMCI_Group *group); extern int ARMCI_Malloc_group_memdev(void *ptr_arr[], armci_size_t bytes, ARMCI_Group *group, const char *device); extern int ARMCI_Free_group(void *ptr, ARMCI_Group *group); extern int ARMCI_NbPut(void *src, void* dst, int bytes, int proc,armci_hdl_t* nb_handle); extern int ARMCI_NbPutS( /* strided put */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbAccS( /* strided accumulate */ int optype, /* operation */ void *scale, /* scale factor x += scale*y */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbGet(void *src, void* dst, int bytes, int proc,armci_hdl_t* nb_handle); extern int ARMCI_NbGetS( /* strided get */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handler/*armci_non-blocking request handle*/ ); extern int ARMCI_NbGetV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbPutV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbAccV( int op, /* operation code */ void *scale, /* scaling factor for accumulate */ armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbPutValueInt(int src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc, /* remote process (or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle */ ); extern int ARMCI_NbPutValueLong(long src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc, /* remote process (or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle */ ); extern int ARMCI_NbPutValueFloat(float src,/* value in a register to put */ void *dst,/* dest starting addr to put data */ int proc, /* remote process (or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle */ ); extern int ARMCI_NbPutValueDouble(double src,/* value in a register to put */ void *dst,/* dest starting addr to put data*/ int proc, /* remote process (or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle */ ); extern int ARMCI_Wait(armci_hdl_t* nb_handle); /*non-blocking request handle*/ extern int ARMCI_Test(armci_hdl_t* nb_handle); /*non-blocking request handle*/ extern int ARMCI_WaitAll (void); extern int ARMCI_WaitProc (int proc); extern void ARMCI_SET_AGGREGATE_HANDLE(armci_hdl_t* nb_handle); extern void ARMCI_UNSET_AGGREGATE_HANDLE(armci_hdl_t* nb_handle); #define ARMCI_INIT_HANDLE(hdl) do {((double *)((hdl)->data))[0]=0; \ ((double *)((hdl)->data))[1]=0; }while(0) /* -------------- ARMCI Non-collective memory allocator ------------- */ typedef struct armci_meminfo_ds { char * armci_addr; /* remote address of the creator which can be used in ARMCI communication */ char *addr; /* local address of creator which can be used in to set SMP memoffset, armci_set_mem_offset() */ size_t size; /* size of remote pid's segment (bytes) */ int cpid; /* armci pid of creator */ long idlist[128]; } armci_meminfo_t; extern void ARMCI_Memget(size_t bytes, armci_meminfo_t *meminfo, int memflg); extern void* ARMCI_Memat(armci_meminfo_t *meminfo, long offset); extern void ARMCI_Memdt(armci_meminfo_t *meminfo, long offset); extern void ARMCI_Memctl(armci_meminfo_t *meminfo); /* ------------------------------------------------------------------ */ #if defined(__cplusplus) || defined(c_plusplus) } #endif #endif /* _ARMCI_H */ ga-5.9.2/armci/src/include/armci_shmem.h000066400000000000000000000011501500715745200200630ustar00rootroot00000000000000#ifndef _SHMEM_H_ #define _SHMEM_H_ extern void Set_Shmem_Limit(unsigned long shmemlimit); extern void Delete_All_Regions(); extern char* Create_Shared_Region(long idlist[], long size, long *offset); extern char* Attach_Shared_Region(long idlist[], long size, long offset); extern void Free_Shmem_Ptr(long id, long size, char* addr); extern long armci_shmem_reg_size(int i, long id); extern char* armci_shmem_reg_ptr(int i); #define POST_ALLOC_CHECK(temp,size) ; #define MAX_REGIONS 64 #if defined(WIN32) #define SHMIDLEN 3 #else #define SHMIDLEN (MAX_REGIONS + 2) #endif #define IDLOC (SHMIDLEN - 3) #endif ga-5.9.2/armci/src/include/armcip.h000066400000000000000000000336141500715745200170640ustar00rootroot00000000000000/* $Id: armcip.h,v 1.82.2.9 2007-08-29 17:32:31 manoj Exp $ */ /* armci private header file */ #ifndef _ARMCI_P_H #define _ARMCI_P_H #if HAVE_STDLIB_H # include #endif #include "armci.h" #include "parmci.h" #include "message.h" /*#define ARMCI_PR_DBG(__ARMCI_ST,__ARMCI_NU) \ printf("\n%d:%s:%d:%s:%s:%d",armci_me,__FILE__,__LINE__,FUNCTION_NAME,__ARMCI_ST,__ARMCI_NU)*/ #define ARMCI_PR_DBG(__ARMCI_ST,__ARMCI_NU) #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include # define sleep(x) Sleep(100*(x)) #endif #if (defined(SYSV) || defined(WIN32)|| defined(MMAP)) && !defined(NO_SHM) #define CLUSTER #ifdef SERVER_THREAD # define SERVER_NODE(c) (armci_clus_info[(c)].master); # define NODE_SERVER(c) (c); #else # define SOFFSET -10000 # define SERVER_NODE(c) ((int)(SOFFSET -armci_clus_info[(c)].master)); # define NODE_SERVER(c) ((int)(SOFFSET - c)) #endif #endif /**Symbol to stamp end of buffers in certain networks*/ #define ARMCI_STAMP 20080528 /*\GPC call stuff \*/ typedef struct { int hndl, hlen, dlen; void *hdr, *data; }gpc_send_t; /*\ Stuff for non-blocking API \*/ #define NB_MULTI -1 /*more than one armci buffer(buffers.c) used for nbcall*/ #define NB_NONE -2 /*no armci buffer(buffers.c) used for nbcall*/ extern unsigned int _armci_get_next_tag(); #define GET_NEXT_NBTAG _armci_get_next_tag #define ARMCI_MAX_IMPLICIT 15 typedef struct{ int len; int last; void *exthdr; } ext_header_t; typedef struct{ int val; void *ptr; } armci_flag_t; #if defined(PTHREADS) || defined(POSIX_THREADS) # include typedef pthread_t thread_id_t; # define THREAD_ID_SELF pthread_self #elif defined(WIN32) # include typedef DWORD thread_id_t; # define THREAD_ID_SELF GetCurrentThreadId #else typedef int thread_id_t; # define THREAD_ID_SELF() 1 #endif extern thread_id_t armci_usr_tid; extern thread_id_t armci_serv_tid; #ifdef SERVER_THREAD # define SERVER_CONTEXT (armci_serv_tid == THREAD_ID_SELF()) #else # define SERVER_CONTEXT (armci_me<0) #endif #if defined(CLUSTER) # include "request.h" #endif /* ------------------------ ARMCI threads support ------------------------- */ #define ARMCI_THREADS_LIMIT 32 #include "utils.h" #if defined(THREAD_SAFE) typedef struct { int max; /* max # of threads per proc */ int avail; /* next available position */ thread_id_t *ids; /* list of threads' ids */ thread_lock_t lock; /* general case lock */ thread_lock_t buf_lock; /* lock for buffer access */ thread_lock_t net_lock; /* lock for network accees */ } armci_user_threads_t; extern armci_user_threads_t armci_user_threads; extern void armci_init_threads(); extern void armci_finalize_threads(); extern int armci_thread_idx(); extern int armci_register_thread(thread_id_t id); #define ARMCI_THREAD_IDX armci_thread_idx() /* needs to be optimized */ #else # define ARMCI_THREAD_IDX 0 #endif /* ------------------------------------------------------------------------ */ /* min amount of data in strided request to be sent in single TCP/IP message*/ #if defined(SOCKETS) || defined(MPI_SPAWN_ZEROCOPY) # define TCP_PAYLOAD 128 # define LONG_GET_THRESHOLD TCP_PAYLOAD # define LONG_GET_THRESHOLD_STRIDED LONG_GET_THRESHOLD # define LONG_PUT_THRESHOLD 128 #endif #ifdef WIN32 # define bzero(a,len){\ int _i;\ char *_c = (char*)(a);\ for(_i=0; _i< (int)(len); _i++)_c[_i]=(char)0;\ } # define bcopy(a,b,len) memcpy(b,a,len) #else # if HAVE_STRINGS_H # include # endif #endif #ifndef FATR # if defined(WIN32) && !defined(__MINGW32__) # define FATR __stdcall # else # define FATR # endif #endif #define MAX_PROC 8096 #define MAX_STRIDE_LEVEL ARMCI_MAX_STRIDE_LEVEL /* msg tag ARMCI uses in collective ops */ #define ARMCI_TAG 30000 #ifndef EXTRA_MSG_BUFLEN_DBL # define RESERVED_BUFLEN ((sizeof(request_header_t)>>3)+3*MAX_STRIDE_LEVEL) #else # define RESERVED_BUFLEN ((sizeof(request_header_t)>>3)+3*MAX_STRIDE_LEVEL +\ EXTRA_MSG_BUFLEN_DBL) #endif /* packing algorithm for double complex numbers requires even number */ # ifdef MSG_BUFLEN_DBL # define BUFSIZE_DBL (MSG_BUFLEN_DBL - RESERVED_BUFLEN) # else # define BUFSIZE_DBL 32768 # endif # define BUFSIZE (BUFSIZE_DBL * sizeof(double)) /* note opcodes must be lower than ARMCI_ACC_OFF !!! */ #define PUT 1 #define GET 2 #define RMW 3 #define LOCK 4 #define UNLOCK 5 #define ACK 6 /* must fit in two bits, see msginfo->format in request.h */ #define STRIDED 1 #define VECTOR 2 extern int armci_me, armci_nproc; extern int _armci_initialized; #if !defined(THREAD_SAFE) extern double armci_internal_buffer[BUFSIZE_DBL]; #endif extern void armci_shmem_init(); extern void armci_krmalloc_init_localmem(); #if 0 extern void armci_die(char *msg, int code); extern void armci_die2(char *msg, int code1, int code2); #endif extern void armci_write_strided(void *ptr, int stride_levels, int stride_arr[], int count[], char *buf); extern void armci_read_strided(void *ptr, int stride_levels, int stride_arr[], int count[], char *buf); extern int armci_op_strided(int op, void* scale, int proc,void *src_ptr, int src_stride_arr[], void* dst_ptr, int dst_stride_arr[], int count[], int stride_levels, int lockit,armci_ihdl_t nb_handle); extern int armci_copy_vector(int op, /* operation code */ armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ); extern int armci_acc_vector(int op, /* operation code */ void *scale, /* scale factor */ armci_giov_t darr[],/* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ); extern int armci_pack_strided(int op, void* scale, int proc, void *src_ptr, int src_stride_arr[], void* dst_ptr, int dst_stride_arr[], int count[], int stride_levels, ext_header_t *hdr, int fit_level, int nb, int last,armci_ihdl_t nb_handle); extern int armci_pack_vector(int op, void *scale, armci_giov_t darr[],int len,int proc,armci_ihdl_t nb_handle); extern void armci_lockmem(void *pstart, void* pend, int proc); extern void armci_unlockmem(int proc); extern int armci_acc_copy_strided(int optype, void* scale, int proc, void* src_ptr, int src_stride_arr[], void* dst_ptr, int dst_stride_arr[], int count[], int stride_levels); extern void armci_vector_to_buf(armci_giov_t darr[], int len, void* buf); extern void armci_vector_from_buf(armci_giov_t darr[], int len, void* buf); extern void armci_init_fence(); extern void armci_finalize_fence(); #ifdef SOCKETS #ifdef SERVER_THREAD extern void armci_create_server_thread ( void* (* func)(void*) ); extern void armci_terminate_server_thread(); #else extern void armci_create_server_process ( void* (* func)(void*) ); extern void armci_wait_server_process(); extern void RestoreSigChldDfl(); #endif #endif #if defined(MPI_SPAWN) || defined(MPI_MT) extern void armci_create_server_MPIprocess (); #endif #define ARMCI_MAX(a,b) (((a)>(b))?(a):(b)) #define ARMCI_MIN(a,b) (((a)<(b))?(a):(b)) #define ARMCI_ABS(a) (((a) >= 0) ? (a) : (-(a))) #define ARMCI_ACC(op) ((((int)(op))-ARMCI_ACC_INT)>=0) #ifdef CLUSTER extern char *_armci_fence_arr; #ifdef THREAD_SAFE # define FENCE_ARR(p_) (_armci_fence_arr[ARMCI_THREAD_IDX*armci_nproc+p_]) #else # define FENCE_ARR(p_) (_armci_fence_arr[p_]) #endif # define SAMECLUSNODE(p)\ ( ((p) <= armci_clus_last) && ((p) >= armci_clus_first) ) #else # define SAMECLUSNODE(p) ((p)==armci_me) #endif #if defined(CLUSTER) # define ORDER(op_,proc_)\ if(!SAMECLUSNODE(proc_) && op_ != GET )FENCE_ARR(proc_)=1 # define UPDATE_FENCE_INFO(proc_) if(!SAMECLUSNODE(proc_))FENCE_ARR(proc_)=1 #else # define ORDER(op,proc) if(proc != armci_me) FENCE_NODE(proc) # define UPDATE_FENCE_INFO(proc_) #endif typedef struct { int ptr_array_len; int bytes; void **ptr_array; } armci_riov_t; /*\ consider up to HOSTNAME_LEN characters in host name * we can truncate names of the SP nodes since it is not used * to establish socket communication like on the networks of workstations * SP node names must be distinct within first HOSTNAME_LEN characters \*/ #if defined(AIX) # define HOSTNAME_TRUNCATE # define HOSTNAME_LEN 12 #else # define HOSTNAME_LEN 64 #endif typedef struct { int master; int nslave; char hostname[HOSTNAME_LEN]; } armci_clus_t; extern armci_clus_t *armci_clus_info; extern int armci_nclus, armci_clus_me, armci_master; extern int armci_clus_first, armci_clus_last; extern int armci_clus_id(int p); extern void armci_init_clusinfo(); extern void armci_set_mem_offset(void *ptr); extern int _armci_terminating; extern void armci_acc_2D(int op, void* scale, int proc, void *src_ptr, void *dst_ptr, int bytes, int cols, int src_stride, int dst_stride, int lockit); extern void armci_lockmem_scatter(void *ptr_array[], int len, int bytes, int p); extern void armci_generic_rmw(int op, void *ploc, void *prem, int extra, int p); extern unsigned long armci_max_region(); extern void armci_dispatch_strided(void *ptr, int stride_arr[], int count[], int strides, int fit_level, int nb, int bufsize, void (*fun)(void*,int*,int*,int,void*), void *arg); extern void armci_msg_gop_init(); extern void armci_msg_gop_finalize(); extern void armci_util_spin(int n, void *notused); #if defined(SYSV) || defined(WIN32) extern void armci_shmem_init(); extern void armci_set_shmem_limit_per_core(unsigned long shmemlimit); extern void armci_set_shmem_limit_per_node(int nslaves); extern void armci_set_shmem_limit(unsigned long shmemlimit); #endif #define ALIGN_PTR_LONG(type, x) if( ((long)(x)) % sizeof(long)) { long _y = (long)(x);\ if(sizeof(long)==8){_y>>=3; _y<<=3; }\ else { _y>>=2; _y<<=2; }\ _y += sizeof(long); (x) = (type*)_y; } #define SIXTYFOUR 64 #define ALIGN64ADD(buf) (SIXTYFOUR-(((ssize_t)(buf))%SIXTYFOUR)) #define ALIGNLONGADD(buf) ((((ssize_t)(buf))%sizeof(long))?(sizeof(long)-(((ssize_t)(buf))%sizeof(long))):0) #define SET 1 #define UNSET 0 extern int armci_agg_save_strided_descriptor(void *src_ptr, int src_stride_arr[], void* dst_ptr, int dst_stride_arr[], int count[], int stride_levels, int proc, int op, armci_ihdl_t nb_handle); extern int armci_agg_save_giov_descriptor(armci_giov_t darr[], int len, int proc, int op, armci_ihdl_t nb_handle); extern int armci_agg_save_descriptor(void *src, void *dst, int bytes, int proc, int op, int is_registered_put, armci_ihdl_t nb_handle); extern void armci_agg_complete(armci_ihdl_t nb_handle, int condition); extern armci_hdl_t *armci_set_implicit_handle (int op, int proc); extern int armci_getnumcpus(void); extern long armci_util_long_getval(long* p); extern int armci_util_int_getval(int* p); extern void armci_region_register_shm(void *start, long size); extern void armci_region_register_loc(void *start, long size); extern void armci_region_clus_record(int node, void *start, long size); extern void armci_region_init(); extern int armci_region_clus_found(int node, void *start, int size); extern int armci_region_loc_found(void *start, int size); extern int armci_region_both_found(void *loc, void *rem, int size, int node); #ifdef REGIONS_REQUIRE_MEMHDL extern int get_armci_region_local_hndl(void *loc, int node, ARMCI_MEMHDL_T **loc_memhdl); #endif extern void armci_region_exchange(void *start, long size); extern void cpu_yield(); #ifdef ALLOW_PIN extern void armci_global_region_exchange(void *, long); #endif /* -------------------- ARMCI Groups ---------------------- */ /* data structure that caches a group's attribute */ #ifdef MSG_COMMS_MPI typedef int ARMCI_Datatype; extern int ATTR_KEY; /* attribute key */ /* #define ARMCI_GROUP /\*Generic ARMCI implementation*\/ */ typedef struct { armci_clus_t *grp_clus_info; int grp_me; /* my group id */ int grp_nclus; /* number of cluster nodes */ int grp_clus_me; /* my cluster node id */ int mem_offset; /* memory offset */ #ifdef ARMCI_GROUP int nproc; /* #procs in this group*/ int *proc_list; /* Ids of procs in this group (w.r.t. MPI_COMM_WORLD)*/ #endif }armci_grp_attr_t; #include "mpi.h" /**dup of MPI_COMM_WORLD for internal MPI communication*/ extern MPI_Comm ARMCI_COMM_WORLD; typedef MPI_Comm ARMCI_Comm; typedef struct { #ifndef ARMCI_GROUP MPI_Comm icomm; MPI_Group igroup; #endif armci_grp_attr_t grp_attr; }ARMCI_iGroup; armci_grp_attr_t *ARMCI_Group_getattr(ARMCI_Group *grp); extern void armci_group_init(); extern void armci_group_finalize(); extern ARMCI_iGroup* armci_get_igroup_from_group(ARMCI_Group *group); #endif /* ifdef MSG_COMMS_MPI */ /* -------------------------------------------------------- */ /* ------------ ARMCI Chekcpointing/Recovery -------------- */ #ifdef ENABLE_CHECKPOINT extern int armci_init_checkpoint(); extern void armci_create_ckptds(armci_ckpt_ds_t *ckptds, int count); extern int armci_icheckpoint_init(char *filename, ARMCI_Group *grp, int savestack, int saveheap, armci_ckpt_ds_t *ckptds); extern int armci_icheckpoint(int rid); extern int armci_irecover(int rid,int iamreplacement); extern void armci_icheckpoint_finalize(int rid); #endif /* ifdef ENABLE_CHECKPOINT */ /* -------------------------------------------------------- */ #endif ga-5.9.2/armci/src/include/asm-ppc.h000066400000000000000000000067131500715745200171510ustar00rootroot00000000000000/** Atomic instructions for ppc. To be populated as need arises. * @author Sriram Krishnamoorthy */ #ifndef __ATOMICS_PPC__ #define __ATOMICS_PPC__ #if HAVE_ASSERT_H # include #endif #define v4b (volatile unsigned int *) /* sriram's original asm (didn't work) */ static inline void atomic_exchange(void *val, void *ptr, int size) { int ret; assert(size==4); assert(sizeof(unsigned int)==4); assert((((unsigned)ptr)&3)==0); /*make sure it is 4-byte aligned*/ asm volatile( "loop: lwarx %[ret],0,%[ptr] \n\t" /*Load & reserve*/ " stwcx. %[val],0,%[ptr] \n\t" /*Store if still reserved*/ " bne- loop \n\t" /*Loop otherwise*/ : [ret]"=&r"(ret) : [val]"r"(*v4b(val)), [ptr]"r"(v4b(ptr)) : "memory", "cc" ); *v4b(val) = ret; } /* adapted from "Synchronising C/C++ and POWER" by Sarkar et al, appearing in * PLDI'12 */ static inline void acquire_spinlock(void *lock) { int temp; int free = 0; int held = 1; asm volatile( "0: lwarx %0,0,%3 \n" /*load-reserve lock into temp*/ " cmpw %1,%0 \n" /*lock is free?*/ " bne- 0b \n" /*loop if lock not free*/ " stwcx. %2,0,%3 \n" /*store if still reserved*/ " bne- 0b \n" /*loop if lost reservation*/ " isync \n" /*import barrier*/ : "+r"(temp) : "r"(free), "r"(held), "r"(lock) : "cr0", "memory" ); } /* adapted from "Synchronising C/C++ and POWER" by Sarkar et al, appearing in * PLDI'12 */ static inline void release_spinlock(void *lock) { int free = 0; assert(sizeof(unsigned int)==4); assert((((unsigned)lock)&3)==0); /*make sure it is 4-byte aligned*/ /* TODO: lwsync might not be strong enough, consider 'sync' */ asm volatile( "lwsync \n" /*export barrier*/ "stw %0,0(%1) \n" /*normal store to release lock*/ : : "r"(free), "r"(lock) : "memory" ); } /* adapted from "Synchronising C/C++ and POWER" by Sarkar et al, appearing in * PLDI'12 */ static inline int fetch_and_add(void *addr, int inc) { int ret; int tmp; asm volatile( "0: lwarx %0,0,%2 \n" /*load-reserve addr into tmp*/ " mr %1,%0 \n" /*copy tmp into ret*/ " add %0,%3,%0 \n" /*add inc to addr, store in addr*/ " stwcx. %0,0,%2 \n" /*store if still reserved*/ " bne- 0b \n" /*loop if lost reservation*/ : "+r"(tmp), "+r"(ret) : "r"(addr), "r"(inc) ); } /* from http://www.ibm.com/developerworks/rational/library/inline-assembly-C-Cpp-guide/index.html */ /* this did NOT work */ static inline int acquireLock(int *lock){ int returnvalue = 0; int lockval; asm volatile ( "0: lwarx %0,0,%2 \n" //load lock and reserve " cmpwi 0,%0,0 \n" //compare the lock value to 0 " bne 1f \n" //not 0 then exit function " ori %0,%0,1 \n" //set the lock to 1 " stwcx. %0,0,%2 \n" //try to acquire the lock " bne 0b \n" //reservation lost, try again " sync \n" //import barrier " ori %1,%1,1 \n" //set the return value to true "1: \n" //didn't get lock, return false : "+r" (lockval), "+r" (returnvalue) : "r"(lock) //parameter lock is an address : "cr0" ); //cmpwi, stwcx both clobber cr0 return returnvalue; } #undef v4b #endif /* __ATOMICS_PPC__ */ ga-5.9.2/armci/src/include/atomics-i386.h000066400000000000000000000023161500715745200177320ustar00rootroot00000000000000/** Atomic instructions for i386. To be populated as need arises. * @author Sriram Krishnamoorthy */ #ifndef __ATOMICS_I386__ #define __ATOMICS_I386__ #if HAVE_ASSERT_H # include #endif #define v4b (volatile unsigned int *) static inline void atomic_exchange(void *val, void *ptr, int size) { assert(size == 4); __asm__ __volatile__ ("xchgl %0, %1" : "=r"(*v4b(val)), "+m"(*v4b(ptr)) : "0"(*v4b(val)) : "memory"); } /*SK: fixme. only available in 486+. This breaks i386 compatibility. atomic: *(type*)ploc = *(type*)prem; *(type*)prem += extra*/ static inline void atomic_fetch_and_add(void *prem, void *ploc, int extra, int size) { int _a_temp; assert(size == 4); #if 0 *(int*)ploc = __sync_fetch_and_add((int*)prem, extra); #elif 0 __asm__ __volatile__ ("movq %2, %%rax; \ lock xaddl %0, (%%rax);" : "=r"(_a_temp) : "0"(extra), "m"((int*)prem) : "memory", "rax"); *(int *)ploc = _a_temp; #else __asm__ __volatile__ ("lock xaddl %0, (%2)" : "=r"(_a_temp) : "0"(extra), "r"((int*)prem) : "memory"); *(int *)ploc = _a_temp; #endif } #undef v4b #endif /*__ATOMICS_I386__*/ ga-5.9.2/armci/src/include/copy.h000066400000000000000000000137701500715745200165640ustar00rootroot00000000000000/* $Id: copy.h,v 1.86.2.6 2007-08-29 17:32:32 manoj Exp $ */ #ifndef _COPY_H_ #define _COPY_H_ #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif # define MEMCPY #ifndef EXTERN # define EXTERN extern #endif #if defined(MEMCPY) && !defined(armci_copy) # define armci_copy(src,dst,n) memcpy((dst), (src), (n)) #endif #if defined(NEED_MEM_SYNC) # ifdef AIX # define MEM_FENCE {int _dummy=1; _clear_lock((int *)&_dummy,0); } # elif defined(LINUX) && defined(__GNUC__) && defined(__ppc__) # define MEM_FENCE \ __asm__ __volatile__ ("isync" : : : "memory"); # endif #endif #ifndef armci_copy # ifdef PTR_ALIGN # define armci_copy(src,dst,n) \ do if( ((n) < THRESH1D) || ((n)%ALIGN_SIZE) || \ ((unsigned long)(src)%ALIGN_SIZE) ||\ ((unsigned long)(dst)%ALIGN_SIZE)) memcpy((dst),(src),(n));\ else{ int _bytes=(n)/sizeof(double); DCOPY1D((double*)(src),(double*)(dst),&_bytes);}\ while (0) # else # define armci_copy(src,dst,n) \ do if( ((n) < THRESH1D) || ((n)%ALIGN_SIZE) ) memcpy((dst), (src), (n));\ else{ int _bytes=(n)/sizeof(double); DCOPY1D((double*)(src),(double*)(dst),&_bytes);}\ while (0) # endif #endif /****************************** 2D Copy *******************/ #ifndef MEMCPY # define DCopy2D(rows, cols, src_ptr, src_ld, dst_ptr, dst_ld){\ int rrows, ldd, lds, ccols;\ rrows = (rows);\ lds = (src_ld);\ ldd = (dst_ld);\ ccols = (cols);\ DCOPY2D(&rrows, &ccols, src_ptr, &lds,dst_ptr,&ldd);\ } #else # define DCopy2D(rows, cols, src_ptr, src_ld, dst_ptr, dst_ld){\ int j, nbytes = sizeof(double)* rows;\ char *ps=src_ptr, *pd=dst_ptr;\ for (j = 0; j < cols; j++){\ armci_copy(ps, pd, nbytes);\ ps += sizeof(double)* src_ld;\ pd += sizeof(double)* dst_ld;\ }\ } #endif # define ByteCopy2D(bytes, count, src_ptr, src_stride, dst_ptr,dst_stride){\ int _j;\ char *ps=src_ptr, *pd=dst_ptr;\ for (_j = 0; _j < count; _j++){\ armci_copy(ps, pd, bytes);\ ps += src_stride;\ pd += dst_stride;\ }\ } # define armci_put2D(proc,bytes,count,src_ptr,src_stride,dst_ptr,dst_stride){\ int _j;\ char *ps=src_ptr, *pd=dst_ptr;\ for (_j = 0; _j < count; _j++){\ armci_put(ps, pd, bytes, proc);\ ps += src_stride;\ pd += dst_stride;\ }\ } # define armci_get2D(proc,bytes,count,src_ptr,src_stride,dst_ptr,dst_stride){\ int _j;\ char *ps=src_ptr, *pd=dst_ptr;\ for (_j = 0; _j < count; _j++){\ armci_get(ps, pd, bytes, proc);\ ps += src_stride;\ pd += dst_stride;\ }\ } #define FENCE_NODE(p) #define UPDATE_FENCE_STATE(p, op, nissued) # define THRESH 32 # define THRESH1D 512 #define ALIGN_SIZE sizeof(double) /********* interface to C 1D and 2D memory copy functions ***********/ /* dcopy2d_u_ uses explicit unrolled loops to depth 4 */ void c_dcopy2d_n_(const int* const restrict rows, const int* const restrict cols, const double* const restrict A, const int* const restrict ald, double* const restrict B, const int* const restrict bld); void c_dcopy2d_u_(const int* const restrict rows, const int* const restrict cols, const double* const restrict A, const int* const restrict ald, double* const restrict B, const int* const restrict bld); void c_dcopy1d_n_(const double* const restrict A, double* const restrict B, const int* const restrict n); void c_dcopy1d_u_(const double* const restrict A, double* const restrict B, const int* const restrict n); void c_dcopy21_(const int* const restrict rows, const int* const restrict cols, const double* const restrict A, const int* const restrict ald, double* const restrict buf, int* const restrict cur); void c_dcopy12_(const int* const restrict rows, const int* const restrict cols, double* const restrict A, const int* const restrict ald, const double* const restrict buf, int* const restrict cur); void c_dcopy31_(const int* const restrict rows, const int* const restrict cols, const int* const restrict plns, const double* const restrict A, const int* const restrict aldr, const int* const restrict aldc, double* const restrict buf, int* const restrict cur); void c_dcopy13_(const int* const restrict rows, const int* const restrict cols, const int* const restrict plns, double* const restrict A, const int* const restrict aldr, const int* const restrict aldc, const double* const restrict buf, int* const restrict cur); #if defined(AIX) # define DCOPY2D c_dcopy2d_u_ # define DCOPY1D c_dcopy1d_u_ #elif defined(LINUX) || defined(WIN32) # define DCOPY2D c_dcopy2d_n_ # define DCOPY1D c_dcopy1d_n_ #else # define DCOPY2D c_dcopy2d_u_ # define DCOPY1D c_dcopy1d_u_ #endif #define DCOPY21 c_dcopy21_ #define DCOPY12 c_dcopy12_ #define DCOPY31 c_dcopy31_ #define DCOPY13 c_dcopy13_ /***************************** 1-Dimensional copy ************************/ #define armci_get(src,dst,n,p) armci_copy((src),(dst),(n)) #define armci_put(src,dst,n,p) armci_copy((src),(dst),(n)) #ifndef MEM_FENCE # define MEM_FENCE {} #endif #ifndef armci_copy_fence # define armci_copy_fence armci_copy #endif #endif ga-5.9.2/armci/src/include/gpc.h000066400000000000000000000020031500715745200163460ustar00rootroot00000000000000#ifndef __GPCDEF #define __GPCDEF #include "armci.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif #define GPC_INIT 1 #define GPC_PROBE 2 #define GPC_WAIT 3 #define GPC_DONE 4 #define GPC_PENDING 5 typedef struct { int proc; armci_hdl_t ahdl; }gpc_hdl_t; /* #define ARMCI_GPC_HLEN 1024 */ /* #define ARMCI_GPC_DLEN 1024*1024 */ extern int ARMCI_Gpc_register( int (*func) ()); extern void ARMCI_Gpc_release(int handle); extern void * ARMCI_Gpc_translate(void *ptr, int proc, int from); extern void ARMCI_Gpc_lock(int proc); extern void ARMCI_Gpc_unlock(int proc); extern int ARMCI_Gpc_trylock(int proc); extern int ARMCI_Gpc_exec(int h,int p, void *hdr, int hlen, void *data,int dlen, void *rhdr, int rhlen, void *rdata, int rdlen, gpc_hdl_t* nbh); extern int ARMCI_Get_gpc_hlen(); extern int ARMCI_Get_gpc_dlen(); extern void ARMCI_Gpc_init_handle(gpc_hdl_t *nbh); extern void ARMCI_Gpc_wait(gpc_hdl_t *nbh); #if defined(__cplusplus) || defined(c_plusplus) } #endif #endif ga-5.9.2/armci/src/include/iterator.h000066400000000000000000000026041500715745200174350ustar00rootroot00000000000000/** @file iterator.h * @author Sriram Krishnamoorthy * @brief Stride iterator. * An iterator for the stride descriptor to reuse common traversal * functionality. More functionality related to the strided * descriptor reusable across files will be extracted here as well. */ #ifndef _STRIDE_INFO_H_ #define _STRIDE_INFO_H_ #include "armci.h" /*for ARMCI_MAX_STRIDE_LEVEL and dassert*/ #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif typedef struct { char *base_ptr; int stride_levels; int stride_arr[ARMCI_MAX_STRIDE_LEVEL]; int seg_count[ARMCI_MAX_STRIDE_LEVEL+1]; int size, pos, itr[ARMCI_MAX_STRIDE_LEVEL]; } stride_info_t; void armci_stride_info_init(stride_info_t *sinfo, void *base_ptr, int stride_levels, const int *stride_arr, const int *seg_count); void armci_stride_info_destroy(stride_info_t *sinfo); int armci_stride_info_count(stride_info_t *sinfo); int armci_stride_info_pos(stride_info_t *sinfo); void armci_stride_info_next(stride_info_t *sinfo); void *armci_stride_info_seg_ptr(stride_info_t *sinfo); int armci_stride_info_seg_size(stride_info_t *sinfo); int armci_stride_info_seg_off(stride_info_t *sinfo); int armci_stride_info_size(stride_info_t *sinfo); int armci_stride_info_has_more(stride_info_t *sinfo); #if defined(__cplusplus) || defined(c_plusplus) } #endif #endif /*_STRIDE_INFO_H_*/ ga-5.9.2/armci/src/include/kr_malloc.h000066400000000000000000000062761500715745200175600ustar00rootroot00000000000000#ifndef KR_MALLOC_H /* K&R malloc */ #define KR_MALLOC_H #define LOG_ALIGN 6 #define ALIGNMENT (1 << LOG_ALIGN) #define KR_CTX_SHMEM 101 #define KR_CTX_LOCALMEM 102 union header{ struct { unsigned valid1; /* Token to check if is not overwritten */ union header *ptr; /* next block if on free list */ int shmid; /* next block's shared memory id */ long shmoffset; /* next block's shmem offset */ size_t shmsize; /* next block's shared memory segment size */ size_t size; /* size of this block*/ unsigned valid2; /* Another token acting as a guard */ } s; char align[ALIGNMENT]; /* Align to ALIGNMENT byte boundary */ }; typedef union header Header; typedef struct malloc_context { size_t usize; /* unit size in bytes */ size_t nalloc; /* No. of units of length ALIGNMENT */ size_t max_nalloc; /* Maximum no. of units that can get */ void * (*alloc_fptr)(); /* function pointer to memory alloc routine */ size_t total; /* Amount request from system in units */ long nchunk; /* No. of chunks of system memory */ long inuse; /* Amount in use in units */ long maxuse; /* Maximum value of inuse */ long nfrags; /* No. of fragments divided into */ long nmcalls; /* No. of calls to _armci_alloc() */ long nfcalls; /* No. of calls to memfree */ int ctx_type; /* context id. -1 represents ctx_local context. otherwise, it is ctx_shmem context. */ int shmid; /* first free block's (i.e.freep) shmem id */ long shmoffset; /* first free block's shmem offset */ size_t shmsize; /* first free block's shmem total size */ Header base; /* empty list to get started */ Header *freep; /* start of free list */ Header *usedp; /* start of used list */ } context_t; /* Memory required to store the shmem context in shared memory. This shmem context shuld be stored in shared memory and is stored in the first shared memory segment created (i.e.armci_krmalloc_init_ctxshmem) */ #define SHMEM_CTX_MEM (sizeof(context_t)+sizeof(void*)) #define SHMEM_CTX_UNITS ((SHMEM_CTX_MEM + sizeof(Header) - 1)>>LOG_ALIGN) + 1; extern void kr_malloc_init(size_t usize, /* unit size in bytes */ size_t nalloc, size_t max_nalloc, void * (*alloc_fptr)(), /* memory alloc routine */ int debug, context_t *ctx); /* Returns data aligned on a quad boundary. Even if the request size is zero it returns a non-zero pointer. */ extern char *kr_malloc(size_t size, context_t *ctx); /* Frees memory allocated by kr_malloc(). Ignores NULL pointers but must not be called twice for the same pointer or called with non-memalloc'ed pointers */ extern void kr_free(char *ptr, context_t *ctx); /* Print to standard output the usage statistics ... a wrapper for kr_malloc_stats(); */ extern void kr_malloc_print_stats(context_t *ctx); extern void kr_malloc_verify(context_t *ctx); #endif ga-5.9.2/armci/src/include/locks.h000066400000000000000000000047571500715745200167320ustar00rootroot00000000000000#ifndef _ARMCI_LOCKS_H_ #define _ARMCI_LOCKS_H_ #if HAVE_SYS_TYPES_H # include #endif #define MAX_LOCKS 1024 #define NUM_LOCKS MAX_LOCKS #if !(defined(PMUTEX) || defined(PSPIN) || defined(CYGNUS) ) # include "spinlock.h" #endif #if !(defined(PMUTEX) || defined(PSPIN) || defined(SPINLOCK)) # error cannot run #endif #if (defined(SPINLOCK) || defined(PMUTEX) || defined(PSPIN)) # include "armci_shmem.h" typedef struct { long off; long idlist[SHMIDLEN]; } lockset_t; extern lockset_t lockid; #endif #if defined(PMUTEX) # warning SPINLOCK: pthread_mutex_lock # include # include # define NAT_LOCK(x,p) pthread_mutex_lock(_armci_int_mutexes +x) # define NAT_UNLOCK(x,p) pthread_mutex_unlock(_armci_int_mutexes +x) # define LOCK_T pthread_mutex_t # define PAD_LOCK_T LOCK_T extern PAD_LOCK_T *_armci_int_mutexes; #elif defined(PSPIN) # warning SPINLOCK: pthread_spin_lock # include # include # define NAT_LOCK(x,p) pthread_spin_lock(_armci_int_mutexes +x) # define NAT_UNLOCK(x,p) pthread_spin_unlock(_armci_int_mutexes +x) # define LOCK_T pthread_spinlock_t # define PAD_LOCK_T LOCK_T extern PAD_LOCK_T *_armci_int_mutexes; #elif defined(SPINLOCK) # define NAT_LOCK(x,p) armci_acquire_spinlock((LOCK_T*)(_armci_int_mutexes+(x))) # define NAT_UNLOCK(x,p) armci_release_spinlock((LOCK_T*)(_armci_int_mutexes+(x))) extern PAD_LOCK_T *_armci_int_mutexes; #elif defined(WIN32) typedef int lockset_t; extern void setlock(int); extern void unsetlock(int); # define NAT_LOCK(x,p) setlock(x) # define NAT_UNLOCK(x,p) unsetlock(x) #elif defined(CYGNUS) typedef int lockset_t; # define NAT_LOCK(x,p) armci_die("does not run in parallel",0) # define NAT_UNLOCK(x,p) armci_die("does not run in parallel",0) #elif defined(SYSV) || defined(MACX) # include "semaphores.h" # undef NUM_LOCKS # define NUM_LOCKS ((MAX_LOCKS< SEMMSL) ? MAX_LOCKS:SEMMSL) # define NAT_LOCK(x,p) P_semaphore(x) # define NAT_UNLOCK(x,p) V_semaphore(x) # ifndef _LOCKS_C_ # define CreateInitLocks Sem_CreateInitLocks # define InitLocks Sem_InitLocks # define DeleteLocks Sem_DeleteLocks # endif #else # error #endif extern void CreateInitLocks(int num, lockset_t *id); extern void InitLocks(int num , lockset_t id); extern void DeleteLocks(lockset_t id); #define NATIVE_LOCK(x,p) if(armci_nproc>1) { NAT_LOCK(x,p); } #define NATIVE_UNLOCK(x,p) if(armci_nproc>1) { NAT_UNLOCK(x,p); } #endif /* _ARMCI_LOCKS_H_ */ ga-5.9.2/armci/src/include/memlock.h000066400000000000000000000016211500715745200172310ustar00rootroot00000000000000/* $Id: memlock.h,v 1.18 2004-09-21 17:26:23 manoj Exp $ */ #ifndef _MEMLOCK_H_ #define _MEMLOCK_H_ /* data structure for locking memory areas */ #define MAX_SLOTS 8 typedef struct{ void *start; void *end; } memlock_t; /* SGI Altix Stuff */ typedef struct { void *seg_addr; /* master's starting address of the segment */ size_t seg_size; size_t tile_size; size_t mem_offset; }armci_memoffset_t; extern void** memlock_table_array; extern int *armci_use_memlock_table; #if defined(PTHREADS) || defined(CYGWIN) || \ (defined(LINUX64) && defined(__GNUC__) && defined(__alpha__)) # define ARMCI_LOCKMEM armci_lockmem_ # define ARMCI_UNLOCKMEM armci_unlockmem_ #else # define ARMCI_LOCKMEM armci_lockmem # define ARMCI_UNLOCKMEM armci_unlockmem #endif extern void ARMCI_LOCKMEM(void *pstart, void *pend, int proc); extern void ARMCI_UNLOCKMEM(int proc); #define MEMLOCK_SHMEM_FLAG #endif ga-5.9.2/armci/src/include/message.h000066400000000000000000000065641500715745200172410ustar00rootroot00000000000000#ifndef _MESSAGE_H_ #define _MESSAGE_H_ #include "armci.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif #define ARMCI_INT -99 #define ARMCI_LONG -101 #define ARMCI_LONG_LONG -102 #define ARMCI_FLOAT -306 #define ARMCI_DOUBLE -307 #define SCOPE_ALL 333 #define SCOPE_NODE 337 #define SCOPE_MASTERS 339 #define armci_msg_sel(x,n,op,type,contribute)\ armci_msg_sel_scope(SCOPE_ALL,(x),(n),(op),(type),(contribute)) #if 0 #define armci_msg_bcast(buffer, len, root)\ armci_msg_bcast_scope(SCOPE_ALL, (buffer), (len), (root)) #else extern void armci_msg_bcast(void *buffer, int len, int root); #endif extern void armci_msg_sel_scope(int scope, void *x, int n, char* op, int type, int contribute); extern void armci_msg_bcast_scope(int scope, void* buffer, int len, int root); extern void armci_msg_brdcst(void* buffer, int len, int root); extern void armci_msg_snd(int tag, void* buffer, int len, int to); extern void armci_msg_rcv(int tag, void* buffer, int buflen, int *msglen, int from); extern int armci_msg_rcvany(int tag, void* buffer, int buflen, int *msglen); extern void armci_msg_reduce(void *x, int n, char *op, int type); extern void armci_msg_reduce_scope(int scope, void *x, int n, char *op, int type); extern void armci_msg_gop_scope(int scope, void *x, int n, char* op, int type); extern void armci_msg_igop(int *x, int n, char* op); extern void armci_msg_lgop(long *x, int n, char* op); extern void armci_msg_llgop(long long *x, int n, char* op); extern void armci_msg_fgop(float *x, int n, char* op); extern void armci_msg_dgop(double *x, int n, char* op); extern void armci_exchange_address(void *ptr_ar[], int n); extern void armci_msg_barrier(); extern void armci_msg_bintree(int scope, int* Root, int *Up, int *Left, int *Right); extern int armci_msg_me(); extern int armci_msg_nproc(); extern void armci_msg_abort(int code); extern void armci_msg_init(int *argc, char ***argv); #ifdef MSG_COMMS_MPI extern void armci_msg_init_comm(MPI_Comm comm); #endif extern void armci_msg_finalize(); extern double armci_timer(); extern void armci_msg_clus_brdcst(void *buf, int len); extern void armci_msg_clus_igop(int *x, int n, char* op); extern void armci_msg_clus_fgop(float *x, int n, char* op); extern void armci_msg_clus_lgop(long *x, int n, char* op); extern void armci_msg_clus_llgop(long long *x, int n, char* op); extern void armci_msg_clus_dgop(double *x, int n, char* op); extern void armci_msg_group_gop_scope(int scope, void *x, int n, char* op, int type, ARMCI_Group *group); extern void armci_msg_group_igop(int *x, int n, char* op,ARMCI_Group *group); extern void armci_msg_group_lgop(long *x, int n, char* op,ARMCI_Group *group); extern void armci_msg_group_llgop(long long *x, int n, char* op,ARMCI_Group *group); extern void armci_msg_group_fgop(float *x, int n, char* op,ARMCI_Group *group); extern void armci_msg_group_dgop(double *x, int n,char* op,ARMCI_Group *group); extern void armci_exchange_address_grp(void *ptr_arr[], int n, ARMCI_Group *group); extern void armci_msg_group_barrier(ARMCI_Group *group); extern void armci_msg_group_bcast_scope(int scope, void *buf, int len, int root, ARMCI_Group *group); extern void armci_grp_clus_brdcst(void *buf, int len, int grp_master, int grp_clus_nproc,ARMCI_Group *mastergroup); #if defined(__cplusplus) || defined(c_plusplus) } #endif #endif ga-5.9.2/armci/src/include/parmci.h000066400000000000000000000113061500715745200170560ustar00rootroot00000000000000#ifndef _PARMCI_H_ #define _PARMCI_H_ #include "armci.h" extern int PARMCI_Acc(int optype, void *scale, void *src, void* dst, int bytes, int proc); extern int PARMCI_AccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc); extern int PARMCI_AccV(int op, void *scale, armci_giov_t * darr, int len, int proc); extern void PARMCI_AllFence(); extern void PARMCI_Barrier(); extern int PARMCI_Create_mutexes(int num); extern int PARMCI_Destroy_mutexes(); extern void PARMCI_Fence(int proc); extern void PARMCI_GroupFence(ARMCI_Group *group); extern void PARMCI_Finalize(); extern int PARMCI_Free_local(void *ptr); extern int PARMCI_Free(void *ptr); extern int PARMCI_Free_memdev(void *ptr); extern int PARMCI_GetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc); extern double PARMCI_GetValueDouble(void *src, int proc); extern float PARMCI_GetValueFloat(void *src, int proc); extern int PARMCI_GetValueInt(void *src, int proc); extern long PARMCI_GetValueLong(void *src, int proc); extern int PARMCI_GetV(armci_giov_t * darr, int len, int proc); extern int PARMCI_Get(void *src, void *dst, int bytes, int proc); extern int PARMCI_Init(); #ifdef MSG_COMMS_MPI extern int PARMCI_Init_mpi_comm(MPI_Comm comm); #endif extern int PARMCI_Init_args(int *argc, char ***argv); extern int PARMCI_Initialized(); extern void PARMCI_Lock(int mutex, int proc); extern void* PARMCI_Malloc_local(armci_size_t bytes); extern int PARMCI_Malloc(void **ptr_arr, armci_size_t bytes); extern int PARMCI_Malloc_memdev(void **ptr_arr, armci_size_t bytes, const char *device); extern void* PARMCI_Memat(armci_meminfo_t * meminfo, long offset); extern void PARMCI_Memget(size_t bytes, armci_meminfo_t * meminfo, int memflg); extern int PARMCI_NbAccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbAccV(int op, void *scale, armci_giov_t * darr, int len, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbGetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbGetV(armci_giov_t * darr, int len, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbGet(void *src, void *dst, int bytes, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbPutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbPutValueDouble(double src, void *dst, int proc, armci_hdl_t* nb_handle); extern int PARMCI_NbPutValueFloat(float src, void *dst, int proc, armci_hdl_t* nb_handle); extern int PARMCI_NbPutValueInt(int src, void *dst, int proc, armci_hdl_t* nb_handle); extern int PARMCI_NbPutValueLong(long src, void *dst, int proc, armci_hdl_t* nb_handle); extern int PARMCI_NbPutV(armci_giov_t * darr, int len, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbPut(void *src, void *dst, int bytes, int proc, armci_hdl_t * nb_handle); extern int PARMCI_Put_flag(void *src, void *dst, int bytes, int *f, int v, int proc); extern int PARMCI_PutS_flag_dir(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc); extern int PARMCI_PutS_flag(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc); extern int PARMCI_PutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc); extern int PARMCI_PutValueDouble(double src, void *dst, int proc); extern int PARMCI_PutValueFloat(float src, void *dst, int proc); extern int PARMCI_PutValueInt(int src, void *dst, int proc); extern int PARMCI_PutValueLong(long src, void *dst, int proc); extern int PARMCI_PutV(armci_giov_t * darr, int len, int proc); extern int PARMCI_Put(void *src, void *dst, int bytes, int proc); extern int PARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc); extern int PARMCI_Test(armci_hdl_t * nb_handle); extern void PARMCI_Unlock(int mutex, int proc); extern int PARMCI_WaitAll(); extern int PARMCI_Wait(armci_hdl_t * nb_handle); extern int PARMCI_WaitProc(int proc); extern void parmci_msg_barrier(); extern void parmci_msg_group_barrier(ARMCI_Group *group); extern int parmci_notify(int proc); extern int parmci_notify_wait(int proc, int *pval); #endif /* _PARMCI_H_ */ ga-5.9.2/armci/src/include/request.h000066400000000000000000000301071500715745200172730ustar00rootroot00000000000000#ifndef _REQUEST_H_ #define _REQUEST_H_ /******** client buffer managment ops ****************************/ extern void _armci_buf_init(); extern char* _armci_buf_get(int size, int operation, int to); extern void _armci_buf_release(void *buf); extern int _armci_buf_to_index(void *buf); extern char* _armci_buf_ptr_from_id(int id); extern void _armci_buf_ensure_one_outstanding_op_per_node(void *buf, int node); #if defined(PEND_BUFS) extern void _armci_buf_ensure_pend_outstanding_op_per_node(void *buf, int node); #endif extern void _armci_buf_complete_nb_request(int bufid,unsigned int tag, int *retcode); extern void _armci_buf_test_nb_request(int bufid,unsigned int tag, int *retcode); extern void _armci_buf_set_tag(void *bufptr,unsigned int tag,short int protocol); extern void _armci_buf_clear_all(); extern char *_armci_buf_get_clear_busy(int size, int operation, int to); extern void _armci_buf_set_busy(void *buf, int state); extern void _armci_buf_set_busy_idx(int tbl_idx, int state); extern int _armci_buf_cmpld(int bufid); extern void _armci_buf_set_cmpld(void *buf, int state); extern void _armci_buf_set_cmpld_idx(int idx, int state); #if defined(VAPI) # include "armci-vapi.h" #elif defined(SOCKETS) # include "sockets.h" typedef long msg_tag_t; typedef unsigned short msg_id_t; # define DTAG_ ((1<<(sizeof(msg_id_t)*8))-1) # define NB_SOCKETS_ /* define NB_SOCKETS to allow non-blocking path */ #elif defined(MPI_SPAWN) || defined(MPI_MT) # include "mpi2.h" typedef long msg_tag_t; #else typedef long msg_tag_t; #endif #ifdef OPENIB # include "pendbufs.h" #endif #ifndef CLEAR_HNDL_FIELD # define CLEAR_HNDL_FIELD(_x) #endif #define ACK_QUIT 0 #define QUIT 33 #define ATTACH 34 #define REGISTER 35 /*\ the internal request structure for non-blocking api. \*/ typedef struct{ unsigned int tag; short int bufid; short int agg_flag; int op; int proc; #ifdef NB_CMPL_T NB_CMPL_T cmpl_info; #endif } armci_ireq_t; /*\ the internal request structure for non-blocking api. \*/ typedef armci_ireq_t* armci_ihdl_t; extern void armci_set_nbhandle_bufid(armci_ihdl_t nb_handle, char *buf, int val); extern void set_nbhandle(armci_ihdl_t *nbh, armci_hdl_t *nb_handle, int op, int proc); typedef struct { short int to; /* message recipient */ short int from; /* message sender */ unsigned int operation:8; /* operation code */ #if defined(CLIENT_BUF_BYPASS) unsigned int format:2; /* data format used */ unsigned int pinned:1; /* indicates if sender memory was pinned */ unsigned int bypass:1; /* indicate if bypass protocol used */ #else unsigned int format:4; /* data format used */ #endif unsigned int bytes:20; /* number of bytes requested */ int datalen; /* >0 in lapi means that data is included */ unsigned int ehlen:8; /* size of extra header and the end of descr */ signed int dscrlen:24; /* >0 in lapi means that descriptor is included */ msg_tag_t tag; /* message tag for response to this request, MUST BE LAST */ }request_header_t; /*******gpc call strctures*************/ #if HAVE_SIGNAL_H # include #endif #define MAX_GPC_REQ 1 #define MAX_GPC_REPLY_LEN (64*1024) #define MAX_GPC_SEND_LEN (64*1024) #define GPC_COMPLETION_SIGNAL SIGUSR1 typedef struct { int hndl; int hlen, dlen; void *hdr, *data; int rhlen, rdlen; void *rhdr, *rdata; } gpc_call_t; typedef struct { int active; /* int zombie; */ request_header_t msginfo; gpc_call_t call; char send[MAX_GPC_SEND_LEN]; char reply[MAX_GPC_REPLY_LEN]; } gpc_buf_t; /* gpc_buf_t *gpc_req; */ extern gpc_buf_t *gpc_req; extern void block_pthread_signal(int signo); extern void unblock_pthread_signal(int signo); /*******structures copied from async.c for storing cmpl dscr for nb req*******/ #define UBUF_LEN 112 typedef struct { unsigned int tag; /* request id*/ short int bufid; /* communication buffer id */ short int protocol; /* what does this buf hold?*/ union { void *dscrbuf; /*in case dscr below is not enough, do a*/ double pad; /*malloc, save pointer in dscrbuf and use it*/ }ptr; char dscr[UBUF_LEN]; /*place to store the dscr*/ }_buf_info_t; #define BUF_INFO_T _buf_info_t extern BUF_INFO_T *_armci_buf_to_bufinfo(void *buf); #define BUF_TO_BUFINFO _armci_buf_to_bufinfo void armci_complete_req_buf(BUF_INFO_T *info, void *buffer); extern BUF_INFO_T *_armci_id_to_bufinfo(int bufid); #if 0 && defined(DATA_SERVER) && defined(SOCKETS) #define MAX_BUFS 1 #define MAX_SMALL_BUFS 1 #else #define MAX_BUFS 15 #define MAX_SMALL_BUFS 16 #endif /* tracks sockets used for receiving responces from data server (GET) */ typedef struct { int socks[MAX_BUFS+MAX_SMALL_BUFS]; /* sock # or -1 if not used */ int ready[MAX_BUFS+MAX_SMALL_BUFS]; /* 1 - ready, 0 - not */ } active_socks_t; /*valid values for the element protocol in BUF_INFO_T*/ #define SDSCR_IN_PLACE 1 /*indicated that strided descriptor is in place*/ #define VDSCR_IN_PLACE 2 /*indicated that vector descriptor is in place*/ #define VDSCR_IN_PTR 3 /*indicates that the vector descriptor in allocated and pointer stored in dscrbuf */ /****************************************************************************/ #ifndef MSG_BUFLEN_DBL # define MSG_BUFLEN_DBL 50000 #endif #define MSG_BUFLEN sizeof(double)*MSG_BUFLEN_DBL extern char* MessageRcvBuffer; extern char* MessageSndBuffer; # ifdef SOCKETS # define GA_SEND_REPLY(tag, buf, len, p) armci_sock_send(p,buf,len) # else # define GA_SEND_REPLY(tag, buf, len, p) # endif #ifndef GET_SEND_BUFFER # define GET_SEND_BUFFER(_size,_op,_to) MessageSndBuffer #endif #ifndef FREE_SEND_BUFFER #define FREE_SEND_BUFFER(_ptr) #endif #ifndef INIT_SENDBUF_INFO #define INIT_SENDBUF_INFO(_hdl,_buf,_op,_proc) #endif typedef struct { char *buf; char* buf_posted; int count; int proc; int op; int extra; } buf_arg_t; /*includes for SERVER_LOCK*/ #if defined(SERVER_THREAD) extern void armci_rem_lock(int mutex, int proc, int *ticket); extern void armci_rem_unlock(int mutex, int proc, int ticket); extern void armci_unlock_waiting_process(msg_tag_t tag,int proc, int ticket); #endif #ifdef PIPE_BUFSIZE extern void armcill_pipe_post_bufs(void *ptr, int stride_arr[], int count[], int strides, void* argvoid); extern void armcill_pipe_extract_data(void *ptr,int stride_arr[],int count[], int strides, void* argvoid); extern void armcill_pipe_send_chunk(void *data, int stride_arr[],int count[], int strides, void* argvoid); #endif extern void armci_send_strided(int proc, request_header_t *msginfo, char *bdata, void *ptr, int strides, int stride_arr[], int count[]); extern void armci_rcv_hdlr(request_header_t* msginfo); extern char *armci_rcv_data(int proc, request_header_t *msginfo); extern void armci_rcv_strided_data_bypass(int proc, request_header_t *msginfo, void *ptr, int stride_levels); extern void armci_send_strided_data_bypass(int proc, request_header_t *msginfo, void *loc_buf, int msg_buflen, void *loc_ptr, int *loc_stride_arr, void *rem_ptr, int *rem_stride_arr, int *count, int stride_levels); extern void armci_rcv_strided_data(int proc, request_header_t* msginfo, int datalen, void *ptr, int strides,int stride_arr[],int count[]); extern void armci_send_strided_data(int proc, request_header_t *msginfo, char *bdata, void *ptr, int strides, int stride_arr[], int count[]); extern void armci_send_req(int proc, request_header_t* msginfo, int len); extern void armci_server_rmw(request_header_t* msginfo,void* ptr, void* pextra); extern int armci_rem_vector(int op, void *scale, armci_giov_t darr[],int len, int proc,int flag,armci_ihdl_t nb_handle); extern int armci_rem_strided(int op, void* scale, int proc, void *src_ptr, int src_stride_arr[], void* dst_ptr, int dst_stride_arr[], int count[], int stride_levels, ext_header_t *h, int lockit,armci_ihdl_t nb_handle); extern void armci_rem_rmw(int op, int *ploc, int *prem, int extra, int proc); extern void armci_rem_ack(int clus); extern void armci_server(request_header_t *msginfo, char *dscr, char* buf, int buflen); extern void armci_server_vector(request_header_t *msginfo, char *dscr, char* buf, int buflen); extern void armci_serv_attach_req(void *info, int ilen, long size, void* resp,int rlen); extern void armci_server_lock(request_header_t *msginfo); extern void armci_server_unlock(request_header_t *msginfo, char* dscr); extern void armci_create_server_thread ( void* (* func)(void*) ); extern int armci_server_lock_mutex(int mutex, int proc, msg_tag_t tag); extern void armci_send_data(request_header_t* msginfo, void *data); extern int armci_server_unlock_mutex(int mutex, int p, int tkt, msg_tag_t* tag); extern void armci_rcv_vector_data(int p, request_header_t* msginfo, armci_giov_t dr[], int len); extern void armci_wait_for_server(); extern void armci_start_server(); extern void armci_transport_cleanup(); extern int armci_send_req_msg(int proc, void *buf, int bytes); extern void armci_WriteToDirect(int proc, request_header_t* msginfo, void *buf); extern char *armci_ReadFromDirect(int proc, request_header_t *msginfo, int len); extern void armci_init_connections(); extern void *armci_server_code(void *data); extern void armci_rcv_req(void *mesg, void *phdr, void *pdescr, void *pdata, int *buflen); extern void armci_client_connect_to_servers(); extern void armci_data_server(void *mesg); extern void armci_server_initial_connection(); extern void armci_call_data_server(); #ifdef SOCKETS extern void armci_ReadStridedFromDirect(int proc, request_header_t* msginfo, void *ptr, int strides, int stride_arr[], int count[]); extern void armci_WriteStridedToDirect(int proc, request_header_t* msginfo, void *ptr, int strides, int stride_arr[], int count[]); extern void armci_serv_quit(); extern int armci_send_req_msg_strided(int proc, request_header_t *msginfo, char *ptr, int strides, int stride_arr[],int count[]); extern void armci_server_goodbye(request_header_t* msginfo); #endif #if defined(MPI_SPAWN) || defined(MPI_MT) extern void armci_serv_quit(); extern void armci_server_goodbye(request_header_t* msginfo); #endif extern void armci_server_ipc(request_header_t* msginfo, void* descr, void* buffer, int buflen); #ifdef PIPE_BUFSIZE extern void armci_pipe_prep_receive_strided(request_header_t *msginfo,char *buf, int strides, int stride_arr[], int count[], int bufsize); extern void armci_pipe_receive_strided(request_header_t* msginfo, void *ptr, int stride_arr[], int count[], int strides); extern void armci_pipe_send_req(int proc, void *buf, int bytes); #endif extern void armci_rcv_strided_data_bypass_both(int, request_header_t*,void*, int*, int); extern int armci_rem_get(int proc, void *src_ptr, int src_stride_arr[], void* dst_ptr, int dst_stride_arr[], int count[], int stride_levels, armci_ihdl_t nb_handle,void *mhloc,void *mhrem); #if defined(ALLOW_PIN) && defined(VAPI) extern int armci_two_phase_send(int proc,void *src_ptr,int src_stride_arr[], void *dst_ptr,int dst_stride_arr[],int count[], int stride_levels,void ** context_ptr,armci_ihdl_t nbhandle, ARMCI_MEMHDL_T *mhloc); extern int armci_two_phase_get(int proc, void*src_ptr, int src_stride_arr[], void*dst_ptr,int dst_stride_arr[], int count[], int stride_levels, void**context_ptr, armci_ihdl_t nbhandle, ARMCI_MEMHDL_T *mhloc); #endif #endif ga-5.9.2/armci/src/include/semaphores.h000066400000000000000000000021121500715745200177440ustar00rootroot00000000000000#ifndef _SEMAPHORES_H_ #define _SEMAPHORES_H_ #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_IPC_H # include #endif #if HAVE_SYS_SEM_H # include #endif #if !HAVE_UNION_SEMUN union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short int *array; /* array for GETALL, SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ }; #endif /* how many semaphores are available ? */ #ifndef SEMMSL # ifdef AIX # define SEMMSL 8094 # else # define SEMMSL 16 # endif #endif extern struct sembuf sops; extern int semaphoreID; int semop(); #define ALL_SEMS -1 #define _P_code -1 #define _V_code 1 #define P_semaphore(s) \ {\ sops.sem_num = (s);\ sops.sem_op = _P_code;\ sops.sem_flg = 0; \ semop(semaphoreID,&sops,1);\ } #define V_semaphore(s) \ {\ sops.sem_num = (s);\ sops.sem_op = _V_code;\ sops.sem_flg = 0; \ semop(semaphoreID,&sops,1);\ } typedef int lockset_t; #endif ga-5.9.2/armci/src/include/shmalloc.h000066400000000000000000000032571500715745200174130ustar00rootroot00000000000000#ifndef SHMALLOC_H #define SHMALLOC_H /* Returns data aligned on a quad boundary. Even if the request size is zero it returns a non-zero pointer. */ extern char *armci_shmalloc(size_t size); /* void shfree(char *pointer) Frees memory allocated by armci_shmalloc(). Ignores NULL pointers but must not be called twice for the same pointer or called with non-shmalloc'ed pointers */ extern void shfree(); /* void shmalloc_print_stats(); Print to standard output the usage statistics ... a wrapper for shmalloc_stats(); */ extern void shmalloc_print_stats(); /* void shmalloc_stats(size_t *total, long *nchunk, size_t *inuse, size_t *maxuse, long *nfrags, long *nmcalls, long *nfcalls) Returns the statistics about memory usage. total = total amount of memory got from system in bytes nchunk = total no. of chunks of memory got from the system inuse = amount of memory application is using in bytes maxuse = maximum value of inuse to dat nfrags = no. of fragments that memory is split into nmcalls= no. of calls to shmalloc nfcalls= no. of calls to shfree */ extern void shmalloc_stats(); /* void shmalloc_debug(int code) Enable debuging code = 0 ... no debugging code != 0 ... entire heap is verified on every call */ extern void shmalloc_debug(); /* Verify the heap. */ extern void shmalloc_verify(); /* size ... minimum size in bytes that chunks of data should be obtained from the system in. The default is 131072. maxsize ... maximum amount of memory that should be obtained from the system. The default is 12,582,912. */ extern void shmalloc_request(size_t size, size_t maxsize); #endif ga-5.9.2/armci/src/include/shmlimit.h000066400000000000000000000002751500715745200174340ustar00rootroot00000000000000#ifndef _SHMLIMIT_H_ #define _SHMLIMIT_H_ extern int armci_me, armci_master; extern void armci_die(char *, int); extern int armci_shmem_test(); extern int armci_child_shmem_init(); #endif ga-5.9.2/armci/src/include/signaltrap.h000066400000000000000000000005461500715745200177530ustar00rootroot00000000000000#ifndef _SIGNALTRAP_H_ #define _SIGNALTRAP_H_ #ifdef SYSV extern void ARMCI_ChildrenTrapSignals(); extern void ARMCI_ParentTrapSignals(); extern void ARMCI_ParentRestoreSignals(); extern void ARMCI_RestoreSignals(); #else # define ARMCI_ChildrenTrapSignals() # define ARMCI_ParentTrapSignals() # define ARMCI_ParentRestoreSignals() #endif #endif ga-5.9.2/armci/src/include/spinlock.h000066400000000000000000000062461500715745200174340ustar00rootroot00000000000000/** * @file spinlock.h * * This file attempts to implement spin locks for various platforms and/or CPU * instruction sets. */ #ifndef SPINLOCK_H #define SPINLOCK_H #define DEBUG_SPINLOCK 0 #define OPENPA 0 #if OPENPA # if DEBUG_SPINLOCK # warning SPINLOCK: openpa # endif # define SPINLOCK # include "opa_primitives.h" # define LOCK_T OPA_int_t # define TESTANDSET(x) OPA_swap_int((x), 1) # define MEMORY_BARRIER OPA_read_write_barrier #elif (defined(PPC) || defined(__PPC__) || defined(__PPC)) # if DEBUG_SPINLOCK # warning SPINLOCK: PPC # endif # define SPINLOCK # include "asm-ppc.h" //# define TESTANDSET testandset //# define TESTANDSET acquireLock # define armci_acquire_spinlock acquire_spinlock # define armci_release_spinlock release_spinlock # define MEMORY_BARRIER memory_barrier static int testandset(void *spinlock) { int v=1; atomic_exchange(&v,spinlock,sizeof(int)); return v; } static void memory_barrier() { __asm__ __volatile__ ("sync" : : : "memory"); } #elif defined(__i386__) || defined(__x86_64__) # if DEBUG_SPINLOCK # warning SPINLOCK: x86_64 # endif # define SPINLOCK # include "atomics-i386.h" static int testandset(void *spinlock) { int v=1; atomic_exchange(&v,spinlock,sizeof(int)); return v; } # define TESTANDSET testandset /*#elif defined(AIX)*/ #elif HAVE_SYS_ATOMIC_OP_H # if DEBUG_SPINLOCK # warning SPINLOCK: sys/atomic_op.h (AIX) # endif # include # define SPINLOCK # define TESTANDSET(x) (_check_lock((x), 0, 1)==TRUE) # define RELEASE_SPINLOCK(x) _clear_lock((x),0) #elif defined(MACX) #endif #ifdef SPINLOCK #if DEBUG_ # if HAVE_STDIO_H # include # endif #endif #if HAVE_UNISTD_H # include #endif #ifndef DBL_PAD # define DBL_PAD 16 #endif /* make sure that locks are not sharing the same cache line */ typedef struct{ double lock[DBL_PAD]; }pad_lock_t; #ifndef LOCK_T # define LOCK_T int #endif #define PAD_LOCK_T pad_lock_t static inline void armci_init_spinlock(LOCK_T *mutex) { #if OPENPA OPA_store_int(mutex, 0); #else *mutex =0; #endif } #ifdef TESTANDSET static inline void armci_acquire_spinlock(LOCK_T *mutex) { int loop=0, maxloop =10; while (TESTANDSET(mutex)){ loop++; if(loop==maxloop){ #if DEBUG_ extern int armci_me; printf("%d:spinlock sleeping\n",armci_me); fflush(stdout); #endif usleep(1); loop=0; } } } #ifdef RELEASE_SPINLOCK # ifdef MEMORY_BARRIER # define armci_release_spinlock(x) MEMORY_BARRIER(); RELEASE_SPINLOCK(x) # else # define armci_release_spinlock(x) RELEASE_SPINLOCK(x) # endif #else static inline void armci_release_spinlock(LOCK_T *mutex) { #ifdef MEMORY_BARRIER MEMORY_BARRIER(); #endif #if OPENPA OPA_store_int(mutex, 0); #else *mutex =0; #endif #ifdef MEMORY_BARRIER MEMORY_BARRIER (); #endif #if (defined(MACX)||defined(LINUX)) && defined(__GNUC__) && defined(__ppc__) __asm__ __volatile__ ("isync" : : : "memory"); #endif } #endif /* RELEASE_SPINLOCK */ #endif /* TESTANDSET */ #endif /* SPINLOCK */ #endif /* SPINLOCK_H */ ga-5.9.2/armci/src/include/utils.h000066400000000000000000000071001500715745200167400ustar00rootroot00000000000000/* $Id: utils.h,v 1.1.2.3 2007-07-02 05:35:31 d3p687 Exp $ * * primitives for transparent handling of multi-threading */ #ifndef UTILS_H #define UTILS_H /* * This header file describes the "barrier" synchronization * construct. The type barrier_t describes the full state of the * barrier including the POSIX 1003.1c synchronization objects * necessary. * * A barrier causes threads to wait until a set of threads has * all "reached" the barrier. The number of threads required is * set when the barrier is initialized, and cannot be changed * except by reinitializing. */ #ifdef THREAD_SAFE # ifdef POSIX_THREADS # include #if 1 typedef pthread_mutex_t thread_lock_t; # define THREAD_LOCK_INIT(x) pthread_mutex_init(&x,NULL) # define THREAD_LOCK_DESTROY(x) pthread_mutex_destroy(&x) # define THREAD_LOCK(x) pthread_mutex_lock(&x) # define THREAD_UNLOCK(x) pthread_mutex_unlock(&x) #else # include "spinlock.h" typedef LOCK_T thread_lock_t; # define THREAD_LOCK_INIT(x) armci_init_spinlock(&x) # define THREAD_LOCK_DESTROY(x) 0 # define THREAD_LOCK(x) armci_acquire_spinlock(&x) # define THREAD_UNLOCK(x) armci_release_spinlock(&x) #endif typedef pthread_t thread_t; # define THREAD_CREATE(th_,func_,arg_) pthread_create(th_,NULL,func_,arg_) # define THREAD_JOIN(th_,ret_) pthread_join(th_,ret_) /* structure describing a barrier */ typedef struct thread_barrier_tag { pthread_mutex_t mutex; /* Control access to barrier */ pthread_cond_t cv; /* wait for barrier */ int valid; /* set when valid */ int threshold; /* number of threads required */ int counter; /* current number of threads */ int cycle; /* alternate wait cycles (0 or 1) */ } thread_barrier_t; # define BARRIER_VALID 0xdbcafe /* support static initialization of barriers */ # define BARRIER_INITIALIZER(cnt) {\ PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER,\ BARRIER_VALID, cnt, cnt, 0} # else # error ONLY PTHREADS SUPPORT HAS BEEN IMPLEMENTED # endif # define TH2PROC(th_) (th_/mt_tpp) /* computes processor from thread id */ /* barrier functions */ int thread_barrier_init (thread_barrier_t *barrier, int count); int thread_barrier_destroy (thread_barrier_t *barrier); int thread_barrier_wait (thread_barrier_t *barrier); /* multi-threaded memory functions */ int armci_malloc_mt(void *ptr[], int bytes); int armci_free_mt(void *ptr, int th_idx); # define ARMCI_MALLOC_MT armci_malloc_mt # define ARMCI_FREE_MT armci_free_mt # define TH_INIT(p_,t_) mt_size=p_;mt_tpp=t_;\ thread_barrier_init(&mt_barrier,mt_tpp) # define TH_FINALIZE() thread_barrier_destroy(&mt_barrier) # define MT_BARRIER() if (thread_barrier_wait(&mt_barrier)==-1) armci_msg_barrier();\ thread_barrier_wait(&mt_barrier) extern int mt_size; extern int mt_tpp; extern thread_barrier_t mt_barrier; #else # define THREAD_LOCK_INIT(x) # define THREAD_LOCK_DESTROY(x) # define THREAD_LOCK(x) # define THREAD_UNLOCK(x) # define TH_INIT(p_,t_) # define TH_FINALIZE() # define MT_BARRIER armci_msg_barrier # define ARMCI_MALLOC_MT ARMCI_Malloc # define ARMCI_FREE_MT(p_,th_) ARMCI_Free(p_) #endif #endif/*UTILS_H*/ ga-5.9.2/armci/src/locks/000077500000000000000000000000001500715745200151215ustar00rootroot00000000000000ga-5.9.2/armci/src/locks/locks.c000066400000000000000000000100551500715745200164010ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: locks.c,v 1.15.6.1 2006-12-14 13:24:36 manoj Exp $ */ #define _LOCKS_C_ #include "armcip.h" #include "locks.h" #if HAVE_UNISTD_H # include #endif #if HAVE_STDIO_H # include #endif PAD_LOCK_T *_armci_int_mutexes; #if !defined(armci_die) extern void armci_die(char*,int); #endif #if defined(SPINLOCK) || defined(PMUTEX) || defined(PSPIN) void **ptr_arr; void CreateInitLocks(int num_locks, lockset_t *plockid) { int locks_per_proc, size; ptr_arr = (void**)malloc(armci_nproc*sizeof(void*)); locks_per_proc = (num_locks*armci_nclus)/armci_nproc + 1; size=locks_per_proc*sizeof(PAD_LOCK_T); PARMCI_Malloc(ptr_arr, size); _armci_int_mutexes = (PAD_LOCK_T*) ptr_arr[armci_master]; if(!_armci_int_mutexes) armci_die("Failed to create spinlocks",size); #ifdef PMUTEX if(armci_me == armci_master) { int i; pthread_mutexattr_t pshared; if(pthread_mutexattr_init(&pshared)) armci_die("armci_allocate_locks: could not init mutex attr",0); # ifndef LINUX if(pthread_mutexattr_setpshared(&pshared,PTHREAD_PROCESS_SHARED)) armci_die("armci_allocate_locks: could not set PROCESS_SHARED",0); # endif for(i=0; i< locks_per_proc*armci_clus_info[armci_clus_me].nslave; i++){ if(pthread_mutex_init(_armci_int_mutexes+i,&pshared)) armci_die("armci_allocate_locks: could not init mutex",i); } } #elif defined(PSPIN) if(armci_me == armci_master) { for(i=0; i< locks_per_proc*armci_clus_info[armci_clus_me].nslave; i++){ if(pthread_spin_init(_armci_int_mutexes+i,PTHREAD_PROCESS_SHARED)) armci_die("armci_allocate_locks: could not init mutex",i); } } #else bzero((char*)ptr_arr[armci_me],size); #endif } void InitLocks(int num_locks, lockset_t lockid) { /* what are you doing here ? All processes should've called CreateInitLocks(). Check preprocessor directtives and see lock allocation in armci_init */ armci_die("InitLocks(): what are you doing here ?",armci_me); } void DeleteLocks(lockset_t lockid) { _armci_int_mutexes = (PAD_LOCK_T*)0; } #elif defined(WIN32) /****************************** Windows NT ********************************/ #include #include HANDLE mutex_arr[NUM_LOCKS]; static int parent_pid; static int num_alloc_locks=0; void CreateInitLocks(int num_locks, lockset_t *lockid) { if(num_locks > NUM_LOCKS) armci_die("To many locks requested", num_locks); *lockid = parent_pid = _getpid(); InitLocks(num_locks, *lockid); } void InitLocks(int num_locks, lockset_t lockid) { int i; char lock_name[64]; for(i=0;inum_alloc_locks || mutex <0)armci_die("setlock: invalid",mutex); rc =WaitForSingleObject(mutex_arr[mutex],INFINITE); switch(rc) { case WAIT_OBJECT_0: /* OK */ break; case WAIT_ABANDONED: /*abandoned: some process crashed holding mutex? */ armci_die("setlock: mutex abandoned",mutex); default: /* some other problem */ fprintf(stderr,"WaitForSingleObject code=%d\n",rc); armci_die("setlock: failed",mutex); } } void unsetlock(int mutex) { if(mutex >num_alloc_locks || mutex <0)armci_die("unsetlock: invalid",mutex); if(ReleaseMutex(mutex_arr[mutex])==FALSE)armci_die("unsetlock: failed",mutex); } #else /*********************** every thing else *************************/ void CreateInitLocks(int num_locks, lockset_t *lockid) {} void InitLocks(int num_locks, lockset_t lockid) { } void DeleteLocks(lockset_t lockid) { } #endif ga-5.9.2/armci/src/locks/memlock.c000066400000000000000000000151271500715745200167220ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: memlock.c,v 1.24.2.3 2007-08-29 17:32:32 manoj Exp $ */ #include "armcip.h" #include "locks.h" #include "copy.h" #include "memlock.h" #if HAVE_STDIO_H # include #endif #define DEBUG_ 0 #define INVALID_VAL -9999999 #ifdef DATA_SERVER # define CORRECT_PTR #endif static size_t armci_mem_offset=0; /* We start by using table: assign address of local variable set to 1 * On shmem systems, this addres is overwritten by a shared memory location * when memlock array is allocated in armci_init * Therefore, any process within shmem node can reset armci_use_memlock_table * to "not used" when offset changes. Since the variable is in shmem, everybody * on that SMP node will see the change and use the same locking functions */ int init_use_memlock_table=1; int *armci_use_memlock_table=&init_use_memlock_table; static int locked_slot=INVALID_VAL; volatile double armci_dummy_work=0.; void **memlock_table_array; /* constants for cache line alignment */ # define CALGN 64 # define LOG_CALGN 6 #define ALIGN_ADDRESS(x) (char*)((((unsigned long)x) >> LOG_CALGN) << LOG_CALGN) static memlock_t table[MAX_SLOTS]; /*\ simple locking scheme that ignores addresses \*/ void armci_lockmem_(void *pstart, void *pend, int proc) { #if defined(CLUSTER) int lock = (proc-armci_clus_info[armci_clus_id(proc)].master)%NUM_LOCKS; #else int lock = 0; #endif if(DEBUG_){ printf("%d: armci_lockmem_ proc=%d lock=%d\n",armci_me,proc,lock); fflush(stdout); } NATIVE_LOCK(lock,proc); if(DEBUG_){ printf("%d: armci_lockmem_ done\n",armci_me); fflush(stdout); } } void armci_unlockmem_(int proc) { #if defined(CLUSTER) int lock = (proc-armci_clus_info[armci_clus_id(proc)].master)%NUM_LOCKS; #else int lock = 0; #endif NATIVE_UNLOCK(lock,proc); } /*\ idle for a time proportional to factor \*/ void armci_waitsome(int factor) { int i=factor*100000; if(factor <= 1) armci_dummy_work =0.; if(factor < 1) return; while(--i){ armci_dummy_work = armci_dummy_work + 1./(double)i; } } /*\ acquire exclusive LOCK to MEMORY area owned by process "proc" * . only one area can be locked at a time by the calling process * . must unlock it with armci_unlockmem \*/ void armci_lockmem(void *start, void *end, int proc) { register void* pstart, *pend; register int slot, avail=0; int turn=0, conflict=0; memlock_t *memlock_table; #if defined(CLUSTER) int lock = (proc-armci_clus_info[armci_clus_id(proc)].master)%NUM_LOCKS; #else int lock = 0; #endif #ifdef CORRECT_PTR if(! *armci_use_memlock_table){ /* if offset invalid, use dumb locking scheme ignoring addresses */ armci_lockmem_(start, end, proc); return; } /* when processes are attached to a shmem region at different addresses, * addresses written to memlock table must be adjusted to the node master */ if(armci_mem_offset){ start = armci_mem_offset + (char*)start; end = armci_mem_offset + (char*)end; } #endif if(DEBUG_){ printf("%d: calling armci_lockmem for %d range %p -%p\n", armci_me, proc, start,end); fflush(stdout); } memlock_table = (memlock_t*)memlock_table_array[proc]; #ifdef ALIGN_ADDRESS /* align address range on cache line boundary to avoid false sharing */ pstart = ALIGN_ADDRESS(start); pend = CALGN -1 + ALIGN_ADDRESS(end); #else pstart=start; pend =end; #endif while(1){ NATIVE_LOCK(lock,proc); armci_get(memlock_table, table, sizeof(table), proc); /* armci_copy(memlock_table, table, sizeof(table));*/ /* inspect the table */ conflict = 0; avail =-1; for(slot = 0; slot < MAX_SLOTS; slot ++){ /* nonzero starting address means the slot is occupied */ if(table[slot].start == NULL){ /* remember a free slot to store address range */ avail = slot; }else{ /*check for conflict: overlap between stored and current range*/ if( (pstart >= table[slot].start && pstart <= table[slot].end) || (pend >= table[slot].start && pend <= table[slot].end) ){ conflict = 1; break; } /* printf("%d: locking %ld-%ld (%d) conflict\n", armci_me, */ } } if(avail != -1 && !conflict) break; NATIVE_UNLOCK(lock,proc); armci_waitsome( ++turn ); } /* we got the memory lock: enter address into the table */ table[avail].start = pstart; table[avail].end = pend; armci_put(table+avail,memlock_table+avail,sizeof(memlock_t),proc); FENCE_NODE(proc); NATIVE_UNLOCK(lock,proc); locked_slot = avail; } /*\ release lock to the memory area locked by previous call to armci_lockemem \*/ void armci_unlockmem(int proc) { void *null[2] = {NULL,NULL}; memlock_t *memlock_table; #ifdef CORRECT_PTR if(! *armci_use_memlock_table){ /* if offset invalid, use dumb locking scheme ignoring addresses */ armci_unlockmem_(proc); return; } #endif #ifdef DEBUG if(locked_slot == INVALID_VAL) armci_die("armci_unlock: empty",0); if(locked_slot >= MAX_SLOTS || locked_slot <0) armci_die("armci_unlock: corrupted slot?",locked_slot); #endif memlock_table = (memlock_t*)memlock_table_array[proc]; armci_put(null,&memlock_table[locked_slot].start,2*sizeof(void*),proc); } /*\ based on address for set by master, determine correction for * memory addresses set in memlock table * if the correction/offset ever changes stop using memlock table locking \*/ void armci_set_mem_offset(void *ptr) { size_t off; static int first_time=1; volatile void *ref_ptr; /* do not care if memlock not used */ if(! *armci_use_memlock_table) return; if(!ptr) armci_die("armci_set_mem_offset : null ptr",0); ref_ptr = *(void**)ptr; off = (size_t)((char*)ref_ptr - (char*)ptr); if(first_time){ armci_mem_offset =off; first_time =0; if(DEBUG_){ printf("%d memlock offset=%ld ref=%p ptr=%p\n",armci_me, (long)armci_mem_offset, ref_ptr, ptr); fflush(stdout); } }else{ if(armci_mem_offset != off){ *armci_use_memlock_table =0; fprintf(stderr, "%d: WARNING:armci_set_mem_offset: offset changed %ld to %ld\n", armci_me, (long)armci_mem_offset, (long)off); fflush(stdout); } } } ga-5.9.2/armci/src/locks/mutex.c000066400000000000000000000245641500715745200164420ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: mutex.c,v 1.24.10.1 2006-12-21 23:50:48 manoj Exp $ */ #include "armcip.h" #include "copy.h" #include "request.h" #if HAVE_STDIO_H # include #endif #define DEBUG 0 #define MAX_LOCKS 32768 #define SPINMAX 1000 double _dummy_work_=0.; static int num_mutexes=0, *tickets; typedef struct { int mutex; int turn; msg_tag_t tag; } waiting_list_t; /* data structure to store info about blocked (waiting) process for mutex */ static waiting_list_t* blocked=(waiting_list_t*)0; typedef struct { int* token; int* turn; int* tickets; int count; } mutex_entry_t; void** mutex_mem_ar; mutex_entry_t *glob_mutex; int PARMCI_Create_mutexes(int num) { int rc,p, totcount; int *mutex_count; if (num < 0 || num > MAX_LOCKS) return(FAIL); if(num_mutexes) armci_die("mutexes already created",num_mutexes); if(armci_nproc == 1){ num_mutexes=1; return(0); } mutex_count = malloc(sizeof(int)*armci_nproc); dassert(1, mutex_count); /* local memory allocation for mutex arrays*/ mutex_mem_ar = (void*) malloc(armci_nproc*sizeof(void*)); if(!mutex_mem_ar) armci_die("PARMCI_Create_mutexes: malloc failed",0); glob_mutex = (void*)malloc(armci_nproc*sizeof(mutex_entry_t)); if(!glob_mutex){ free(mutex_count); free(mutex_mem_ar); armci_die("PARMCI_Create_mutexes: malloc 2 failed",0); } /* bzero(mutex_count,armci_nproc*sizeof(int));*/ bzero((char*)mutex_count,sizeof(int)*armci_nproc); /* find out how many mutexes everybody allocated */ mutex_count[armci_me]=num; armci_msg_igop(mutex_count, armci_nproc, "+"); for(p=totcount=0; p< armci_nproc; p++)totcount+=mutex_count[p]; tickets = calloc(totcount,sizeof(int)); if(!tickets) { free(glob_mutex); free(mutex_mem_ar); free(mutex_count); return(FAIL2); } /* we need memory for token and turn - 2 ints */ rc = PARMCI_Malloc(mutex_mem_ar,2*num*sizeof(int)); if(rc){ free(glob_mutex); free(mutex_mem_ar); free(tickets); free(mutex_count); return(FAIL3); } if(num)bzero((char*)mutex_mem_ar[armci_me],2*num*sizeof(int)); /* setup global mutex array */ for(p=totcount=0; p< armci_nproc; p++){ glob_mutex[p].token = mutex_mem_ar[p]; glob_mutex[p].turn = glob_mutex[p].token + mutex_count[p]; glob_mutex[p].count = mutex_count[p]; glob_mutex[p].tickets = tickets + totcount; totcount += mutex_count[p]; } num_mutexes= totcount; PARMCI_Barrier(); if(DEBUG) fprintf(stderr,"%d created (%d,%d) mutexes\n",armci_me,num,totcount); free(mutex_count); return(0); } void armci_serv_mutex_create() { int mem = armci_nproc*sizeof(waiting_list_t); blocked = (waiting_list_t*)malloc(mem); if(!blocked) armci_die("armci server:error allocating mutex memory ",0); } void armci_serv_mutex_close() { if(blocked) free(blocked ); blocked = (waiting_list_t*)0; } int PARMCI_Destroy_mutexes() { if(num_mutexes==0)armci_die("armci_destroy_mutexes: not created",0); if(armci_nproc == 1) return(0); armci_msg_barrier(); num_mutexes=0; # if defined(SERVER_LOCK) armci_serv_mutex_close(); # endif if(glob_mutex[armci_me].count)PARMCI_Free(glob_mutex[armci_me].token); free(tickets); free(glob_mutex); free(mutex_mem_ar); return(0); } static int register_in_mutex_queue(int id, int proc) { int *mutex_entry, ticket; if(glob_mutex[proc].count < id) armci_die2("armci:invalid mutex id",id, glob_mutex[proc].count); mutex_entry = glob_mutex[proc].token + id; PARMCI_Rmw(ARMCI_FETCH_AND_ADD, &ticket, mutex_entry, 1, proc); return ticket; } /*\ check if mutex is available by comparing turn and token * can only be called by a process on the same SMP node as proc \*/ static int armci_mutex_free(int mutex, int proc) { volatile int *mutex_ticket=glob_mutex[proc].turn + mutex; volatile int *turn = glob_mutex[proc].token +mutex; /* here we will put code to check if other processes on the node * are waiting for this mutex * lockinfo_node[me].ticket = mutex_ticket; * lockinfo_node[me].mutex = mutex; */ if(*mutex_ticket == *turn) return 1; else return 0; } static void armci_generic_lock(int mutex, int proc) { int i, myturn, factor=0, len=sizeof(int); int *mutex_ticket, next_in_line; mutex_ticket= glob_mutex[proc].turn + mutex; myturn = register_in_mutex_queue(mutex, proc); /* code to reduce cost of unlocking mutex on the same SMP node goes here * lockinfo_node[me].ticket = mutex_ticket; * lockinfo_node[me].mutex = mutex; */ _dummy_work_ = 0.; /* must be global to fool the compiler */ do { PARMCI_Get(mutex_ticket, &next_in_line, len, proc); if(next_in_line > myturn) armci_die2("armci: problem with tickets",myturn,next_in_line); /* apply a linear backoff delay before retrying */ for(i=0; i< SPINMAX * factor; i++) _dummy_work_ += 1.; factor += 1; }while (myturn != next_in_line); glob_mutex[proc].tickets[mutex] = myturn; /* save ticket value */ } static void armci_generic_unlock(int mutex, int proc) { int *mutex_ticket= glob_mutex[proc].turn + mutex; int *newval = glob_mutex[proc].tickets +mutex; int len=sizeof(int); /* update ticket for next process requesting this mutex */ (*newval) ++; /* write new ticket value stored previously in tickets */ PARMCI_Put(newval, mutex_ticket, len, proc); MEM_FENCE; } /*\ Acquire mutex for "proc" * -must be executed in hrecv/AM handler thread * -application thread must use generic_lock routine \*/ int armci_server_lock_mutex(int mutex, int proc, msg_tag_t tag) { int myturn; int *mutex_ticket, next_in_line, len=sizeof(int); int owner = armci_me; if(DEBUG)fprintf(stderr,"SLOCK=%d owner=%d p=%d m=%d\n", armci_me,owner, proc,mutex); mutex_ticket= glob_mutex[owner].turn + mutex; myturn = register_in_mutex_queue(mutex, owner); armci_copy(mutex_ticket, &next_in_line, len); if(next_in_line > myturn) armci_die2("armci-s: problem with tickets",myturn,next_in_line); if(next_in_line != myturn){ if(!blocked)armci_serv_mutex_create(); blocked[proc].mutex = mutex; blocked[proc].turn = myturn; blocked[proc].tag = tag; if(DEBUG) fprintf(stderr,"SLOCK=%d proc=%d blocked (%d,%d)\n", armci_me, proc, next_in_line,myturn); return -1; } else { if(DEBUG) fprintf(stderr,"SLOCK=%d proc=%d sending ticket (%d)\n", armci_me, proc, myturn); /* send ticket to requesting node */ /* GA_SEND_REPLY(tag, &myturn, sizeof(int), proc); */ return (myturn); } } /*\ Release mutex "id" held by proc * called from hrecv/AM handler AND application thread \*/ int armci_server_unlock_mutex(int mutex, int proc, int Ticket, msg_tag_t* ptag) { #define NOBODY -1 int owner = armci_me; int i, p=NOBODY, *mutex_ticket= glob_mutex[owner].turn + mutex; int len=sizeof(int); if(DEBUG) fprintf(stderr,"SUNLOCK=%d node=%d mutex=%d ticket=%d\n", armci_me,proc,mutex,Ticket); Ticket++; armci_copy(&Ticket, mutex_ticket, len); /* if mutex is free then nobody is reqistered in queue */ if(armci_mutex_free(mutex, proc)) return -1; /* search for the next process in queue waiting for this mutex */ for(i=0; i< armci_nproc; i++){ if(!blocked)break; /* not allocated yet - nobody is waiting */ if(DEBUG)fprintf(stderr,"SUNLOCK=%d node=%d list=(%d,%d)\n", armci_me, i, blocked[i].mutex, blocked[i].turn); if((blocked[i].mutex == mutex) && (blocked[i].turn == Ticket)){ p = i; break; } } /* send Ticket to a process waiting for mutex */ if(p != NOBODY){ if(p == armci_me)armci_die("server_unlock: cannot unlock self",0); else { if(DEBUG)fprintf(stderr,"SUNLOCK=%d node=%d unlock ticket=%d go=%d\n", armci_me, proc, Ticket, p); /* GA_SEND_REPLY(blocked[p].tag, &Ticket, sizeof(int), p); */ *ptag = blocked[p].tag; return p; } } return -1; /* nobody is waiting */ } void PARMCI_Lock(int mutex, int proc) { #if defined(SERVER_LOCK) int direct; #endif if(DEBUG)fprintf(stderr,"%d enter lock\n",armci_me); if(armci_nproc == 1) return; if(!num_mutexes) armci_die("armci_lock: create mutexes first",0); if(mutex > glob_mutex[proc].count) armci_die2("armci_lock: mutex not allocated", mutex, glob_mutex[proc].count); # if defined(SERVER_LOCK) direct=SAMECLUSNODE(proc); if(!direct) armci_rem_lock(mutex,proc, glob_mutex[proc].tickets + mutex ); else # endif armci_generic_lock(mutex,proc); if(DEBUG)fprintf(stderr,"%d leave lock\n",armci_me); } void PARMCI_Unlock(int mutex, int proc) { if(DEBUG)fprintf(stderr,"%d enter unlock\n",armci_me); if(armci_nproc == 1) return; if(!num_mutexes) armci_die("armci_lock: create mutexes first",0); if(mutex > glob_mutex[proc].count) armci_die2("armci_lock: mutex not allocated", mutex, glob_mutex[proc].count); # if defined(SERVER_LOCK) if(armci_nclus >1) { if(proc != armci_me) armci_rem_unlock(mutex, proc, glob_mutex[proc].tickets[mutex]); else { int ticket = glob_mutex[proc].tickets[mutex]; msg_tag_t tag; int waiting; waiting = armci_server_unlock_mutex(mutex, proc, ticket, &tag); if(waiting >-1) armci_unlock_waiting_process(tag, waiting, ++ticket); } } else # endif armci_generic_unlock(mutex, proc); if(DEBUG)fprintf(stderr,"%d leave unlock\n",armci_me); } ga-5.9.2/armci/src/locks/semaphores.c000066400000000000000000000037571500715745200174470ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: semaphores.c,v 1.12 2005-03-10 19:11:23 vinodtipparaju Exp $ */ #include "semaphores.h" #if HAVE_STDIO_H # include #endif #if HAVE_UNISTD_H # include #endif int num_sem_alloc=0; void perror(); #include "armcip.h" struct sembuf sops; int semaphoreID; int SemGet(num_sem) int num_sem; { semaphoreID = semget(IPC_PRIVATE,num_sem, IPC_CREAT | 0600); if(semaphoreID<0){ fprintf(stderr," Semaphore Allocation Failed \nsuggestions to fix the problem: \n"); fprintf(stderr," 1. run ipcs and ipcrm -s commands to clean any semaphore ids\n"); fprintf(stderr," 2. verify if constant SEMMSL defined in file semaphore.h is set correctly for your system\n"); fprintf(stderr," 3. recompile semaphore.c\n"); sleep(1); perror("Error message from failed semget:"); armci_die(" exiting ...", num_sem); } num_sem_alloc = num_sem; return(semaphoreID); } void SemInit(id,value) int id,value; { int i, semid, num_sem; union semun semctl_arg; semctl_arg.val = value; if(id == ALL_SEMS){ semid = 0; num_sem = num_sem_alloc;} else { semid = id; num_sem = 1;} for(i=0; i< num_sem; i++){ if( semctl(semaphoreID, semid, SETVAL,semctl_arg )<0){ perror((char*)0); armci_die("SemInit error",id); } semid++; } } /* release semaphore(s) */ void SemDel() { union semun dummy; /* this is only to avoid compiler whinning about the unitialized variable*/ dummy.val=0; (void) semctl(semaphoreID,0,IPC_RMID,dummy); } void Sem_CreateInitLocks(int num, lockset_t *id) { *id = SemGet(num); SemInit(ALL_SEMS,1); } void Sem_InitLocks(int num, lockset_t id) { semaphoreID = id; num_sem_alloc = num; } void Sem_DeleteLocks(lockset_t id) { union semun dummy; /* this is only to avoid compiler whinning about the unitialized variable*/ dummy.val=0; (void) semctl(id,0,IPC_RMID,dummy); } ga-5.9.2/armci/src/memory/000077500000000000000000000000001500715745200153165ustar00rootroot00000000000000ga-5.9.2/armci/src/memory/buffers.c000066400000000000000000001616371500715745200171340ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: buffers.c,v 1.29.6.9 2007-07-02 05:16:50 d3p687 Exp $ **/ #define SIXTYFOUR 64 #define DEBUG_ 0 #define DEBUG2_ 0 #define EXTRA_ERR_CHECK /**********************************************************************/ #if HAVE_STDLIB_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_ASSERT_H # include #endif #include "armcip.h" #include "request.h" #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include typedef unsigned long ssize_t; #endif #ifdef SOCKETS # define EQ_TAGS(a_, b_) ((a_) == (b_)) #else # define EQ_TAGS(a_, b_) !memcmp(&(a_), &(b_), sizeof(a_)) #endif #define ALIGN64ADD(buf) (SIXTYFOUR-(((ssize_t)(buf))%SIXTYFOUR)) /* the following symbols should be defined if needed in protocol specific header file: BUF_EXTRA_FIELD, BUF_ALLOCATE */ #ifndef BUF_ALLOCATE # define BUF_ALLOCATE malloc #endif #if defined(PEND_BUFS) #define SMALL_BUF_LEN 8192 #else #define SMALL_BUF_LEN 8192 #endif #ifndef MSG_BUFLEN_SMALL #define MSG_BUFLEN_SMALL (MSG_BUFLEN >>0) #endif #define LEFT_GUARD 11.11e11 #define RIGHT_GUARD 22.22e22 /* #define CLEAR_TABLE_SLOT(idx) *((int*)(_armci_buf_state->table+(idx))) =0 */ #define CLEAR_TABLE_SLOT(idx) (memset(_armci_buf_state->table+(idx),'\0',sizeof(buf_state_t)) /* we allow multiple buffers (up to 15) per single request * adjacent buffers can be coalesced into a large one */ #if 0 typedef struct { unsigned int op:8; /* pending operation code */ unsigned int snd:1; /* if 1 then buffer is used for sending request */ unsigned int rcv:1; /* if 1 then buffer is used for receiving data */ unsigned int async:1; /* if 1 then request is nonblocking */ unsigned int first:5; /* id of the 1st buffer in the set in same request */ unsigned int count:1; /* count is not used and is always 1 (or 0???) */ /*unsigned int count:4; \* how many buffers used for this request 8 possible */ unsigned int busy:1; /* if 1 buffer is used and cannot be completed */ unsigned int cmpl:1; /* set to 1 if buffer was completed and can be released */ unsigned int to:13; /* serv/proc to which request was sent, 8172 possible */ }buf_state_t; #else typedef struct { unsigned int op:8; /* pending operation code */ unsigned int snd:1; /* if 1 then buffer is used for sending request */ unsigned int rcv:1; /* if 1 then buffer is used for receiving data */ unsigned int async:1; /* if 1 then request is nonblocking */ unsigned int first:20; /* id of the 1st buffer in the set in same request */ unsigned int count:1; /* count is not used and is always 1 (or 0???) */ /*unsigned int count:4; \* how many buffers used for this request 8 possible */ unsigned int busy:1; /* if 1 buffer is used and cannot be completed */ unsigned int cmpl:1; /* set to 1 if buffer was completed and can be released */ unsigned int to:30; /* serv/proc to which request was sent, can handle pretty large counts (can be used for fields in the future) */ }buf_state_t; #endif #ifndef BUFID_PAD_T #define BUFID_PAD_T BUF_INFO_T #endif /* message send buffer data structure */ typedef struct { BUF_INFO_T id; # ifdef BUF_EXTRA_FIELD_T BUF_EXTRA_FIELD_T field; # endif char buffer[MSG_BUFLEN_SMALL]; } buf_ext_t; /* message send buffer data structure */ typedef struct { BUF_INFO_T id; # ifdef BUF_EXTRA_FIELD_T BUF_EXTRA_FIELD_T field; # endif char buffer[SMALL_BUF_LEN]; } buf_smext_t; /* we keep table and buffer pointer together for better locality */ typedef struct { double left_guard; /* stamp to verify if array was corrupted */ buf_state_t table[MAX_BUFS+MAX_SMALL_BUFS]; /*array with state of buffer */ buf_ext_t *buf; /* address of buffer pool */ buf_smext_t *smallbuf; /* address of the large buffer pool */ int avail; int smavail; int pad; double right_guard; /* stamp to verify if array was corrupted */ unsigned buf_bitmap; /* bitmaps to track available buffers: */ unsigned smbuf_bitmap;/* 1 - available, 0 - not available */ } reqbuf_pool_t; #ifndef BUF_EXTRA_FIELD_T # define SIZE_BUF_EXTRA_FIELD 0 # define BUF_TO_EBUF(buf) (buf_ext_t*)(((char*)buf) - sizeof(BUFID_PAD_T) -\ SIZE_BUF_EXTRA_FIELD) # define BUF_TO_SMEBUF(buf) (buf_smext_t*)(((char*)buf)- sizeof(BUFID_PAD_T) -\ SIZE_BUF_EXTRA_FIELD) #else # define BUF_TO_EBUF(buf) (buf_ext_t*)(((char*)buf) - sizeof(BUFID_PAD_T) -\ sizeof(BUF_EXTRA_FIELD_T)) # define BUF_TO_SMEBUF(buf) (buf_smext_t*)(((char*)buf)- sizeof(BUFID_PAD_T) -\ sizeof(BUF_EXTRA_FIELD_T)) #endif #define BUF_TO_BUFINDEX(buf) (BUF_TO_EBUF((buf)))->id.bufid #define BUF_TO_SMBUFINDEX(buf) (BUF_TO_SMEBUF((buf)))->id.bufid buf_ext_t *_armci_buffers; /* these are the actual buffers */ buf_smext_t *_armci_smbuffers; /* no, these are the actual buffers */ reqbuf_pool_t* _armci_buf_state; /* array that describes state of each buf */ extern active_socks_t *_armci_active_socks; /* returns bufinfo, given bufid */ BUF_INFO_T *_armci_id_to_bufinfo(int bufid) { if (bufid < 0 || bufid >= (MAX_BUFS+MAX_SMALL_BUFS)) armci_die2("_armci_id_to_bufinfo: bad id",bufid,MAX_BUFS); return bufid < MAX_BUFS ? &(_armci_buf_state->buf[bufid].id) : &(_armci_buf_state->smallbuf[bufid-MAX_BUFS].id); } #if (defined(THREAD_SAFE) || defined(SOCKETS)) /* check if buffer was completed and can be released */ int armci_test_network_complete() { int idx; for (idx=0;idxtable[idx].cmpl) break; return (idx == MAX_BUFS+MAX_SMALL_BUFS ? -1 : idx); } /*\ we allocate alligned buffer space * this operation can be implemented in platform specific files \*/ void _armci_buf_init() { char *tmp; int extra=0; int smallbuf_size = sizeof(buf_smext_t)*(MAX_SMALL_BUFS); tmp = (char *)BUF_ALLOCATE((MAX_BUFS*sizeof(buf_ext_t) + 64 + smallbuf_size)); extra= ALIGN64ADD(tmp); /* if(sizeof(buf_state_t) != sizeof(int)) */ /* armci_die("armci_buf_init size buf_state_t!=int",sizeof(buf_state_t)); */ dassert(1,MAX_BUFSbuffer, (void*)(_armci_buffers+MAX_BUFS), (long unsigned)MAX_BUFS*sizeof(buf_ext_t), extra); fflush(stdout); } /* now allocate state array */ tmp = calloc(1, sizeof(reqbuf_pool_t) + 64); if(!tmp)armci_die("_armci_buf_init calloc failed",0); extra= ALIGN64ADD(tmp); _armci_buf_state = (reqbuf_pool_t*)(tmp + extra); /* initialize it */ _armci_buf_state->left_guard = LEFT_GUARD; _armci_buf_state->right_guard = RIGHT_GUARD; _armci_buf_state->avail =0; _armci_buf_state->smavail =MAX_BUFS; _armci_buf_state->buf = _armci_buffers; _armci_buf_state->smallbuf = _armci_smbuffers; /* initialize bitmaps */ /* should not be more than sizeof(unsigned int) buffers */ if (MAX_BUFS > sizeof(unsigned)*8 && MAX_SMALL_BUFS > sizeof(unsigned)*8) armci_die("_armci_buf_init: cannot allocate this many buffers",0); _armci_buf_state->buf_bitmap = (1 << MAX_BUFS) - 1; _armci_buf_state->smbuf_bitmap = (1 << MAX_SMALL_BUFS) - 1; if(BUF_TO_EBUF(_armci_buf_state->buf[0].buffer)!=_armci_buf_state->buf) armci_die("buffers.c, internal structure alignment problem",0); } /*\ convert buffer pointer to index (in state array) \*/ int _armci_buf_to_index(void *buf) { int index; char *ptr = (char*)buf; if(DEBUG2_){ printf("%d: in _armci_buf_to_index %p\n",armci_me, buf); fflush(stdout); } if(buf > (void *)_armci_buffers && buf < (void *)(_armci_buffers+MAX_BUFS)){ index = BUF_TO_BUFINDEX(ptr); if((index >= MAX_BUFS)|| (index<0)) armci_die2("armci_buf_to_index: bad index:",index,MAX_BUFS); return(index); } else if(buf > (void *)_armci_smbuffers && buf < (void *)(_armci_smbuffers+MAX_SMALL_BUFS)){ index = BUF_TO_SMBUFINDEX(ptr); if((index >= MAX_BUFS+MAX_SMALL_BUFS)|| (indextable +idx; /* fprintf(stderr, "%d:: entered %s. called=%d\n", armci_me, FUNCTION_NAME); */ count = buf_state->count; if(DEBUG_ ) { printf("%d:buf_complete_index:%d op=%d first=%d count=%d called=%d\n", armci_me,idx,buf_state->op,buf_state->first,buf_state->count, called); fflush(stdout); } if(buf_state->first != (unsigned int)idx){ armci_die2("complete_buf_index:Inconsistent index:",idx,buf_state->first); } if(buf_state->async){ /* completion of strided get should release that buffer */ if(buf_state->op == GET); else armci_die2("buf_complete_index: async mode not avail for this op", buf_state->op,idx); } # ifdef BUF_EXTRA_FIELD_T else{ /* need to call platform specific function */ if(idx>=MAX_BUFS){ int relidx; relidx = idx-MAX_BUFS; CLEAR_SEND_BUF_FIELD(_armci_buf_state->smallbuf[relidx].field,buf_state->snd,buf_state->rcv,buf_state->to,buf_state->op); /*later, we might just need to do this for all operations, not just get*/ if(_armci_buf_state->smallbuf[relidx].id.tag!=0 &&(buf_state->op == GET)){ armci_complete_req_buf(&(_armci_buf_state->smallbuf[relidx].id), _armci_buf_state->smallbuf[relidx].buffer); } _armci_buf_state->smallbuf[relidx].id.tag=0; } else { CLEAR_SEND_BUF_FIELD(_armci_buf_state->buf[idx].field,buf_state->snd,buf_state->rcv,buf_state->to,buf_state->op); /*later, we might just need to do this for all operations, not just get*/ if(_armci_buf_state->buf[idx].id.tag!=0 &&(buf_state->op == GET)){ armci_complete_req_buf(&(_armci_buf_state->buf[idx].id), _armci_buf_state->buf[idx].buffer); } _armci_buf_state->buf[idx].id.tag=0; } } # endif /* clear table slots for all the buffers in the set for this request */ for(; count; count--, buf_state++) { /* *(int*)buf_state = 0; */ memset(buf_state,'\0',sizeof(buf_state_t)); } } /*\ test outstanding operation that uses the specified buffer for complete * It is important not to change the state of the buffer, the buffer has * to remain as it was, only completion has to be indicated \*/ int _armci_buf_test_index(int idx, int called) { int count,retval=0; buf_state_t *buf_state = _armci_buf_state->table +idx; count = buf_state->count; if(DEBUG_ ){ printf("%d:buf_test_index:%d op=%d first=%d count=%d called=%d\n", armci_me,idx,buf_state->op,buf_state->first,buf_state->count, called); fflush(stdout); } if(buf_state->first != (unsigned int)idx){ armci_die2("_buf_test_index:inconsistent index:",idx,buf_state->first); } # ifdef BUF_EXTRA_FIELD_T /* need to call platform specific function */ if(idx>=MAX_BUFS){ int relidx; relidx = idx-MAX_BUFS; /*printf("\n%d:relidx=%d \n",armci_me,relidx);fflush(stdout);*/ TEST_SEND_BUF_FIELD(_armci_buf_state->smallbuf[relidx].field,buf_state->snd,buf_state->rcv,buf_state->to,buf_state->op,&retval); } else { TEST_SEND_BUF_FIELD(_armci_buf_state->buf[idx].field,buf_state->snd,buf_state->rcv,buf_state->to,buf_state->op,&retval); } # endif if(DEBUG_ ){ printf("%d:buf_test_index:%d op=%d first=%d count=%d called=%d ret=%d\n", armci_me,idx,buf_state->op,buf_state->first,buf_state->count, called,retval); fflush(stdout); } return(retval); } /*\ make sure that there are no other pending operations to that smp node * this operation is called from platforms specific routine that sends * request * we could have accomplished the same in armci_buf_get but as Vinod * is pointing out, it is better to delay completing outstanding * calls to overlap memcpy for the current buffer with communication \*/ void _armci_buf_ensure_one_outstanding_op_per_node(void *buf, int node) { int i; int index =_armci_buf_to_index(buf); int this = _armci_buf_state->table[index].first; int nfirst, nlast; void _armci_buf_release_index(int i); nfirst=armci_clus_info[node].master; nlast = nfirst+armci_clus_info[node].nslave-1; if((_armci_buf_state->table[index].to<(unsigned int) nfirst) || (_armci_buf_state->table[index].to>(unsigned int) nlast)) armci_die2("_armci_buf_ensure_one_outstanding_op_per_node: bad to",node, (int)_armci_buf_state->table[index].to); for(i=0;itable +i; if((buf_state->to >= nfirst) && (buf_state->to<= (unsigned int) nlast)) if((buf_state->first != (unsigned int) this)&&(buf_state->first==(unsigned int) i) && buf_state->op){ _armci_buf_complete_index(i,0); _armci_buf_release_index(i); } } } /*\ same as above but for process \*/ void _armci_buf_ensure_one_outstanding_op_per_proc(void *buf, int proc) { int i; int index = _armci_buf_to_index(buf); int this = _armci_buf_state->table[index].first; if(_armci_buf_state->table[index].to !=(unsigned int) proc ) armci_die2("_armci_buf_ensure_one_outstanding_op_per_proc: bad to", proc, (int)_armci_buf_state->table[index].to); for(i=0;itable +i; if(buf_state->to == (unsigned int) proc) if((buf_state->first != (unsigned int) this)&&(buf_state->first==(unsigned int) i) && buf_state->op) _armci_buf_complete_index(i,0); } } #define HISTORY__ #ifdef HISTORY typedef struct{ int size; int op; int count; int id; } history_t; history_t history[100]; int h=0; void print_history() { int i; fflush(stdout); printf("%d records\n",h); for(i=0; ibuf[history[i].id].buffer, history[i].count, history[i].op); fflush(stdout); } #endif enum {CALL_GET, CALL_RELEASE} last_call_ = CALL_RELEASE; #if DEBUG3_ static int get_count_ = 0; static int release_count_ = 0; #endif /* release buffer, update free buffers bitmaps */ void _armci_buf_release_index(int tbl_idx) { int idx; #if DEBUG3_ release_count_++; if (last_call_ != CALL_GET) { printf("same call: trying to release %s buffer\n" " %d release calls to date\n", tbl_idx >= MAX_BUFS ? "small" : "regular", release_count_); } else last_call_ = CALL_RELEASE; int mark = !(tbl_idx < MAX_BUFS ? _armci_buf_state->buf_bitmap : _armci_buf_state->smbuf_bitmap); #endif if ((tbl_idx >= MAX_BUFS+MAX_SMALL_BUFS) || (tbl_idx < 0)) armci_die2("armci_buf_release: bad index:", tbl_idx, MAX_BUFS); THREAD_LOCK(armci_user_threads.buf_lock); if (tbl_idx < MAX_BUFS) { idx = tbl_idx; _armci_buf_state->buf_bitmap |= 1 << idx; _armci_buf_state->buf[idx].id.tag = 0; } else { idx = tbl_idx - MAX_BUFS; _armci_buf_state->smbuf_bitmap |= 1 << idx; _armci_buf_state->smallbuf[idx].id.tag = 0; } _armci_buf_state->table[tbl_idx].busy = 0; THREAD_UNLOCK(armci_user_threads.buf_lock); #if DEBUG3_ if (mark) printf("release of empty buffer pool, after: %d\n", tbl_idx < MAX_BUFS ? _armci_buf_state->buf_bitmap : _armci_buf_state->smbuf_bitmap); #endif } /*\ release buffer when it becomes free \*/ void _armci_buf_release(void *buf) { _armci_buf_release_index(_armci_buf_to_index(buf)); } /* call corresponding to GET_SEND_BUF */ char *_armci_buf_get(int size, int operation, int to) { int avail; /* needed by INIT_SEND_BUF for MELLANOX */ unsigned bitmap; int small = size < SMALL_BUF_LEN; int max_bufs = small ? MAX_SMALL_BUFS : MAX_BUFS; int idx = 0; /* same type buffers index: 0..{MAX_BUFS|MAX_SMALL_BUFS}-1 */ int tbl_idx; /* global index in table: 0..MAX_BUFS+MAX_SMALL_BUFS-1 */ int not_ready = 1; #if DEBUG3_ get_count_++; if (last_call_ != CALL_RELEASE) { printf("same call: trying to get %s buffer\n" " %d get calls to date, %d release calls to date, bitmap: %d\n", small ? "small" : "regular", get_count_, release_count_, small ? _armci_buf_state->smbuf_bitmap : _armci_buf_state->buf_bitmap); } else last_call_ = CALL_GET; /* debug hook: no buffers left */ if (!(small ? _armci_buf_state->smbuf_bitmap : _armci_buf_state->buf_bitmap)) bitmap = small ? _armci_buf_state->smbuf_bitmap : _armci_buf_state->buf_bitmap; #endif while (not_ready) { THREAD_LOCK(armci_user_threads.buf_lock); bitmap = small ? _armci_buf_state->smbuf_bitmap : _armci_buf_state->buf_bitmap; /* check if there are available buffers */ if (bitmap) { /* find available buffer in the bitmap */ for (idx = 0; idx < max_bufs; idx++) { if (bitmap & (1 << idx)) break; } if (idx >= max_bufs) armci_die("_armci_buf_get: buffer idx is out of the range",idx); /* mark buffer as taken in the bitmap and busy */ bitmap &= ~((unsigned)(1 << idx)); if (small) { tbl_idx = idx + MAX_BUFS; _armci_buf_state->smbuf_bitmap = bitmap; } else { tbl_idx = idx; _armci_buf_state->buf_bitmap = bitmap; } #if 0 _armci_buf_state->table[tbl_idx].busy = 1; #endif THREAD_UNLOCK(armci_user_threads.buf_lock); not_ready = 0; } else { THREAD_UNLOCK(armci_user_threads.buf_lock); /* try network complete */ #if defined(SOCKETS) tbl_idx = armci_test_network_complete(); #else /* all network should eventually use armci_test_network_complete */ tbl_idx = small ? _armci_buf_state->smavail : _armci_buf_state->avail; #endif avail = tbl_idx; if ((tbl_idx >= MAX_BUFS+MAX_SMALL_BUFS) || (tbl_idx < 0 && tbl_idx != -1)) armci_die2("_armci_buf_get: bad idx:", tbl_idx, MAX_BUFS); if (tbl_idx < 0) { /*printf("armci_test_network_complete returned -1\n");fflush(stdout);*/ cpu_yield(); } else { /* could complete a buffer */ /* ignore a busy buffer */ #if DEBUG3_ printf("armci_test_network_complete, gets:%d, releases:%d\n", get_count_, release_count_); #endif if (_armci_buf_state->table[tbl_idx].busy) { printf("BUFFER BUSY 1\n"); continue; } /* complete buffer */ _armci_buf_complete_index(tbl_idx, 0); /* tbl_idx < MAX_BUFS ^ small - 1 if completed compatible buffer */ if ((tbl_idx < MAX_BUFS) ^ small) { THREAD_LOCK(armci_user_threads.buf_lock); /* is this check really necessary ??? */ if (!_armci_buf_state->table[tbl_idx].busy) { #if 0 _armci_buf_state->table[tbl_idx].busy = 1; #endif not_ready = 0; #if DEBUG3_ release_count_++; printf("released in get, gets:%d, releases:%d\n", get_count_, release_count_); #endif } else { printf("BUFFER BUSY 2\n"); } THREAD_UNLOCK(armci_user_threads.buf_lock); idx = small ? tbl_idx - MAX_BUFS : tbl_idx; } else _armci_buf_release_index(tbl_idx); } } } /* initialize buffer */ _armci_buf_state->table[tbl_idx].op = operation; _armci_buf_state->table[tbl_idx].to = to; _armci_buf_state->table[tbl_idx].count = 1; _armci_buf_state->table[tbl_idx].first = tbl_idx; _armci_buf_state->table[tbl_idx].cmpl = 0; /* Note: tbl_idx is used in vapi vesrion of INIT_SEND_BUF */ if (small) { _armci_buf_state->smallbuf[idx].id.tag = 0; _armci_buf_state->smallbuf[idx].id.bufid = tbl_idx; _armci_buf_state->smallbuf[idx].id.protocol = 0; # ifdef BUF_EXTRA_FIELD_T INIT_SEND_BUF(_armci_buf_state->smallbuf[idx].field, _armci_buf_state->table[tbl_idx].snd, _armci_buf_state->table[tbl_idx].rcv); #endif } else { _armci_buf_state->buf[idx].id.tag = 0; _armci_buf_state->buf[idx].id.bufid = tbl_idx; _armci_buf_state->buf[idx].id.protocol = 0; # ifdef BUF_EXTRA_FIELD_T INIT_SEND_BUF(_armci_buf_state->buf[idx].field, _armci_buf_state->table[idx].snd, _armci_buf_state->table[idx].rcv); #endif } return small ? _armci_buf_state->smallbuf[idx].buffer : _armci_buf_state->buf[idx].buffer; } /*\ return pointer to buffer number id \*/ char *_armci_buf_ptr_from_id(int id) { if(id <0 || id >=(MAX_BUFS+MAX_SMALL_BUFS)) armci_die2("armci_buf_ptr_from_id: bad id",id,MAX_BUFS); if(id >=MAX_BUFS)return(_armci_buf_state->smallbuf[id-MAX_BUFS].buffer); return(_armci_buf_state->buf[id].buffer); } /*\function called from PARMCI_Wait to wait for non-blocking ops \*/ void _armci_buf_complete_nb_request(int bufid,unsigned int tag, int *retcode) { int i=0; #if 0 printf("\n%d:wait called with bufid=%d tag=%d \n",armci_me,bufid,tag); fflush(stdout); #endif if(bufid == NB_NONE) *retcode=0; else if(bufid == NB_MULTI) { for(i=0;ibuf[i].id.tag) _armci_buf_complete_index(i,1); } for(i=0;ismallbuf[i].id.tag) _armci_buf_complete_index(i+MAX_BUFS,1); } *retcode=0; } else { if(bufidbuf[bufid].id.tag) _armci_buf_complete_index(bufid,1); } else{ if(tag && tag==_armci_buf_state->smallbuf[bufid-MAX_BUFS].id.tag) _armci_buf_complete_index(bufid,1); } *retcode=0; } } /*\function called from PARMCI_Test to test completion of non-blocking ops \*/ void _armci_buf_test_nb_request(int bufid,unsigned int tag, int *retcode) { int i; if(bufid == NB_NONE) *retcode=0; else if(bufid == NB_MULTI) { for(i=0;ibuf[i].id.tag){ if(_armci_buf_test_index(i,1)){ *retcode=1; break; } } } for(i=0;ismallbuf[i].id.tag) if(_armci_buf_test_index(i+MAX_BUFS,1)){ *retcode=1; break; } } } else { if(bufidbuf[bufid].id.tag) *retcode = _armci_buf_test_index(bufid,1); } else{ if(tag && tag==_armci_buf_state->smallbuf[bufid-MAX_BUFS].id.tag) *retcode = _armci_buf_test_index(bufid,1); } } } /*\function to set the buffer tag and the protocol \*/ void _armci_buf_set_tag(void *bufptr,unsigned int tag,short int protocol) { int index = _armci_buf_to_index(bufptr); /*_armci_buf_state->table[index].async=1;*/ if(indexbuf[index].id.tag=tag; _armci_buf_state->buf[index].id.protocol=protocol; } else{ _armci_buf_state->smallbuf[index-MAX_BUFS].id.tag=tag; _armci_buf_state->smallbuf[index-MAX_BUFS].id.protocol=protocol; } } /*\function to return bufinfo, given buf ptr \*/ BUF_INFO_T *_armci_buf_to_bufinfo(void *buf){ if(buf > (void *)_armci_buffers && buf < (void *)(_armci_buffers+MAX_BUFS)){ return(&((BUF_TO_EBUF(buf))->id)); } else if(buf > (void *)_armci_smbuffers && buf < (void *)(_armci_smbuffers+MAX_SMALL_BUFS)){ return(&((BUF_TO_SMEBUF(buf))->id)); } else { armci_die("armci_buf_to_index: bad pointer",0); return(0); } } /*\function to clear all buffers \*/ void _armci_buf_clear_all() { int i; for(i=0;itable[i].op || _armci_buf_state->table[i].first) CLEAR_SEND_BUF_FIELD(_armci_buf_state->buf[i].field,_armci_buf_state->table[i].snd,_armci_buf_state->table[i].rcv,_armci_buf_state->table[i].to,_armci_buf_state->table[i].op); #endif } for(i=MAX_BUFS;itable[i].op || _armci_buf_state->table[i].first) CLEAR_SEND_BUF_FIELD(_armci_buf_state->smallbuf[i-MAX_BUFS].field,_armci_buf_state->table[i].snd,_armci_buf_state->table[i].rcv,_armci_buf_state->table[i].to,_armci_buf_state->table[i].op); #endif } } #ifdef VAPI /* this will work for vapi as there is no get pipeline enabled in vapi * with get pipeline, this will break very badly */ void _armci_buf_update_scatter_count(int id) { int i,num,last,first; for(i=0;itable[i].op==GET){ request_header_t *msginfo; msginfo = (request_header_t*)_armci_buf_state->buf[i].buffer; if(msginfo->pinned && msginfo->bypass && msginfo->format == STRIDED){ num = *(int *)((char *)msginfo+msginfo->bytes); last = *(int *)((char *)msginfo+msginfo->bytes+sizeof(int)); first = last - num+1; if(first < 0 )first+=DSCRID_SCATTERCLIENT_END-DSCRID_SCATTERCLIENT-1; if(id == first && num!=0){ *(int *)((char *)msginfo+msginfo->bytes) = (--num); return; } } } # endif } for(i=MAX_BUFS;itable[i].op==GET){ request_header_t *msginfo = (request_header_t*)_armci_buf_state->smallbuf[i-MAX_BUFS].buffer; if(msginfo->pinned && msginfo->bypass && msginfo->format == STRIDED){ num = *(int *)((char *)msginfo+msginfo->bytes); last = *(int *)((char *)msginfo+msginfo->bytes+sizeof(int)); first = last - num+1; if(first < 0 )first+=DSCRID_SCATTERCLIENT_END-DSCRID_SCATTERCLIENT-1; if(id == first && num!=0){ *(int *)((char *)msginfo+msginfo->bytes) = (--num); return; } } } # endif } } #endif #else /* (defined(THREAD_SAFE) || defined(SOCKETS)) */ /*\ we allocate alligned buffer space * this operation can be implemented in platform specific files \*/ void _armci_buf_init() { char *tmp; int extra=0; int smallbuf_size = sizeof(buf_smext_t)*(MAX_SMALL_BUFS); tmp = (char *)BUF_ALLOCATE((MAX_BUFS*sizeof(buf_ext_t) + 64 + smallbuf_size)); extra= ALIGN64ADD(tmp); #if 0 if(sizeof(buf_state_t) != sizeof(int)) armci_die("armci_buf_init size buf_state_t!=int",sizeof(buf_state_t)); #endif _armci_buffers = (buf_ext_t *) (tmp + extra); tmp = (char *)(_armci_buffers + MAX_BUFS); extra = ALIGN64ADD(tmp); _armci_smbuffers = (buf_smext_t *) (tmp + extra); if(DEBUG2_){ printf("%d:armci_init_bufs: pointer %p, before align ptr=%p bufptr=%p end of region is %p size=%lu extra=%d\n", armci_me,(void*)_armci_buffers,tmp,_armci_buffers->buffer,(void*)(_armci_buffers+MAX_BUFS), MAX_BUFS*sizeof(buf_ext_t),extra); fflush(stdout); } /* now allocate state array */ tmp = calloc(1, sizeof(reqbuf_pool_t) + 64); if(!tmp)armci_die("_armci_buf_init calloc failed",0); extra= ALIGN64ADD(tmp); _armci_buf_state = (reqbuf_pool_t*)(tmp + extra); /* initialize it */ _armci_buf_state->left_guard = LEFT_GUARD; _armci_buf_state->right_guard = RIGHT_GUARD; _armci_buf_state->avail =0; _armci_buf_state->smavail =MAX_BUFS; _armci_buf_state->buf = _armci_buffers; _armci_buf_state->smallbuf = _armci_smbuffers; if(BUF_TO_EBUF(_armci_buf_state->buf[0].buffer)!=_armci_buf_state->buf) armci_die("buffers.c, internal structure alignment problem",0); } /*\ convert buffer pointer to index (in state array) \*/ int _armci_buf_to_index(void *buf) { int index; char *ptr = (char*)buf; if(DEBUG2_){ printf("%d: in _armci_buf_to_index %p\n",armci_me, buf); fflush(stdout); } if(buf > (void *)_armci_buffers && buf < (void *)(_armci_buffers+MAX_BUFS)){ index = BUF_TO_BUFINDEX(ptr); if((index >= MAX_BUFS)|| (index<0)) armci_die2("armci_buf_to_index: bad index:",index,MAX_BUFS); return(index); } else if(buf > (void *)_armci_smbuffers && buf < (void *)(_armci_smbuffers+MAX_SMALL_BUFS)){ index = BUF_TO_SMBUFINDEX(ptr); if((index >= MAX_BUFS+MAX_SMALL_BUFS)|| (indextable +idx; /* printf("%d: buf_complete_index. idx=%d\n", armci_me, idx);*/ /* fflush(stdout);*/ /* if(buf_state->op==GET) { */ /* printf("%d: %s(): op is get\n",armci_me,FUNCTION_NAME); */ /* } */ count = buf_state->count; if(DEBUG_ ) { printf("%d:buf_complete_index:%d op=%d first=%d count=%d called=%d\n", armci_me,idx,buf_state->op,buf_state->first,buf_state->count, called); fflush(stdout); } if(buf_state->first != (unsigned int)idx){ armci_die2("complete_buf_index:inconsistent Index:",idx,buf_state->first); } if(buf_state->async){ /* completion of strided get should release that buffer */ if(buf_state->op == GET); else armci_die2("buf_complete_index: async mode not avail for this op", buf_state->op,idx); } # ifdef BUF_EXTRA_FIELD_T else{ /* need to call platform specific function */ if(idx>=MAX_BUFS){ int relidx; relidx = idx-MAX_BUFS; /* printf("%d:%s(): Calling clear_send_buf_field\n",armci_me,FUNCTION_NAME); */ CLEAR_SEND_BUF_FIELD(_armci_buf_state->smallbuf[relidx].field,buf_state->snd,buf_state->rcv,buf_state->to,buf_state->op); /*later, we might just need to do this for all operations, not just get*/ if(_armci_buf_state->smallbuf[relidx].id.tag!=0 &&(buf_state->op == GET)){ armci_complete_req_buf(&(_armci_buf_state->smallbuf[relidx].id), _armci_buf_state->smallbuf[relidx].buffer); } _armci_buf_state->smallbuf[relidx].id.tag=0; } else { CLEAR_SEND_BUF_FIELD(_armci_buf_state->buf[idx].field,buf_state->snd,buf_state->rcv,buf_state->to,buf_state->op); /*later, we might just need to do this for all operations, not just get*/ if(_armci_buf_state->buf[idx].id.tag!=0 &&(buf_state->op == GET)){ armci_complete_req_buf(&(_armci_buf_state->buf[idx].id), _armci_buf_state->buf[idx].buffer); } _armci_buf_state->buf[idx].id.tag=0; } } # endif /* clear table slots for all the buffers in the set for this request */ assert(count==1); for(; count; count--, buf_state++) { /* *(int*)buf_state = 0; */ memset(buf_state, '\0', sizeof(buf_state_t)); } } /*\ test outstanding operation that uses the specified buffer for complete * It is important not to change the state of the buffer, the buffer has * to remain as it was, only completion has to be indicated \*/ int _armci_buf_test_index(int idx, int called) { int retval=0; buf_state_t *buf_state = _armci_buf_state->table +idx; if(DEBUG_ ){ printf("%d:buf_test_index:%d op=%d first=%d count=%d called=%d\n", armci_me,idx,buf_state->op,buf_state->first,buf_state->count, called); fflush(stdout); } if(buf_state->first != (unsigned int)idx){ armci_die2("_buf_test_index:inconsistent index:",idx,buf_state->first); } # ifdef BUF_EXTRA_FIELD_T /* need to call platform specific function */ if(idx>=MAX_BUFS){ int relidx; relidx = idx-MAX_BUFS; /*printf("\n%d:relidx=%d \n",armci_me,relidx);fflush(stdout);*/ TEST_SEND_BUF_FIELD(_armci_buf_state->smallbuf[relidx].field,buf_state->snd,buf_state->rcv,buf_state->to,buf_state->op,&retval); } else { TEST_SEND_BUF_FIELD(_armci_buf_state->buf[idx].field,buf_state->snd,buf_state->rcv,buf_state->to,buf_state->op,&retval); } # endif if(DEBUG_ ){ printf("%d:buf_test_index:%d op=%d first=%d count=%d called=%d ret=%d\n", armci_me,idx,buf_state->op,buf_state->first,buf_state->count, called,retval); fflush(stdout); } return(retval); } /** an addition to the below operation to allow for multiple outstanding operations per server node */ #if defined(PEND_BUFS) /** Need to implement effective mechanisms to ensure a given number of outstanding operations. The key is to not wait for a recently sent message. Another issue might be priority between the large and small messages. This function will be improved as needed. X_BUFS+ */ void _armci_buf_ensure_pend_outstanding_op_per_node(void *buf, int node) { unsigned int i; int index =_armci_buf_to_index(buf); int this = _armci_buf_state->table[index].first; unsigned int nfirst, nlast; void _armci_buf_release_index(int i); int buf_pend_count=0; int max_pend_count=IMM_BUF_NUM; /* printf("%d: ensure_pend_os_per_node. idx=%d\n", armci_me, index);*/ /* fflush(stdout);*/ nfirst=armci_clus_info[node].master; nlast = nfirst+armci_clus_info[node].nslave-1; if((_armci_buf_state->table[index].to<(unsigned int) nfirst) || (_armci_buf_state->table[index].to>(unsigned int) nlast)) armci_die2("_armci_buf_ensure_pend_outstanding_op_per_node: bad to",node, (int)_armci_buf_state->table[index].to); #if 0 for(i=0;itable +i; if((buf_state->to >= nfirst) && (buf_state->to<= (unsigned int) nlast)) if((buf_state->first != (unsigned int) this)&&(buf_state->first==(unsigned int) i) && buf_state->op){ buf_pend_count++; if(buf_pend_count > IMM_BUF_NUM-1){ /* printf("%d: client. pend_os_per_node. completing buf index=%d\n", armci_me, i); */ /* fflush(stdout); */ _armci_buf_complete_index(i,0); _armci_buf_release_index(i); /* printf("%d: client. pend_os_per_node. done completing buf index=%d\n", armci_me, i); */ fflush(stdout); buf_pend_count--; } } } #else const int smavail=_armci_buf_state->smavail; const int avail = _armci_buf_state->avail; const int startsmall = (smavail-MAX_BUFS-1+MAX_SMALL_BUFS)%MAX_SMALL_BUFS+MAX_BUFS; const int start =(avail-1+MAX_BUFS)%MAX_BUFS; buf_pend_count=0; buf_state_t *bs; i = start; bs = &_armci_buf_state->table[i]; do { if((bs->to>=nfirst) && (bs->to<=nlast)) { if((bs->first!=this) && (bs->first==i) && bs->op) { #if defined(OPENIB) /*SK: not tested on other platforms*/ if(!_armci_buf_test_index(i,1)) { buf_pend_count++; } #else buf_pend_count++; #endif if(buf_pend_count > max_pend_count-1) { /* printf("%d:%s():complete largebuf %d\n",armci_me,FUNCTION_NAME,i); */ _armci_buf_complete_index(i,0); _armci_buf_release_index(i); buf_pend_count--; } } } i = (i-1+MAX_BUFS)%MAX_BUFS; bs = &_armci_buf_state->table[i]; } while(i != start); /* printf("%d: largebuf pend=%d\n",armci_me,buf_pend_count); */ i = startsmall; bs = &_armci_buf_state->table[i]; do { if((bs->to>=nfirst) && (bs->to<=nlast)) { if((bs->first!=this) && (bs->first==i) && bs->op) { #if defined(OPENIB) /*SK:not tested on other platforms*/ if(!_armci_buf_test_index(i,1)) { buf_pend_count++; } #else buf_pend_count++; #endif if(buf_pend_count > max_pend_count-1) { /* printf("%d:%s():complete smallbuf %d\n",armci_me,FUNCTION_NAME,i); */ _armci_buf_complete_index(i,0); _armci_buf_release_index(i); buf_pend_count--; } } } i = (i-MAX_BUFS-1+MAX_SMALL_BUFS)%MAX_SMALL_BUFS+MAX_BUFS; bs = &_armci_buf_state->table[i]; } while(i != startsmall); buf_pend_count=0; for(i=0; ito>=nfirst) && (bs->to<=nlast)) { if((bs->first!=this) && (bs->first==i) && bs->op) { buf_pend_count += 1; } } } assert(buf_pend_count <= max_pend_count); #endif } void _armci_buf_ensure_pend_outstanding_op_per_proc(void *buf, int proc) { int i; int index = _armci_buf_to_index(buf); int this = _armci_buf_state->table[index].first; void _armci_buf_release_index(int i); int buf_pend_count=0; if(_armci_buf_state->table[index].to !=(unsigned int) proc ) armci_die2("_armci_buf_ensure_one_outstanding_op_per_proc: bad to", proc, (int)_armci_buf_state->table[index].to); for(i=0;itable +i; if(buf_state->to == (unsigned int) proc) { if((buf_state->first != (unsigned int) this)&&(buf_state->first==(unsigned int) i) && buf_state->op){ buf_pend_count++; if(buf_pend_count > IMM_BUF_NUM-1){ _armci_buf_complete_index(i,0); _armci_buf_release_index(i); buf_pend_count--; } } } } } #endif /*\ make sure that there are no other pending operations to that smp node * this operation is called from platforms specific routine that sends * request * we could have accomplished the same in armci_buf_get but as Vinod * is pointing out, it is better to delay completing outstanding * calls to overlap memcpy for the current buffer with communication \*/ void _armci_buf_ensure_one_outstanding_op_per_node(void *buf, int node) { int i; int index =_armci_buf_to_index(buf); int this = _armci_buf_state->table[index].first; int nfirst, nlast; void _armci_buf_release_index(int i); nfirst=armci_clus_info[node].master; nlast = nfirst+armci_clus_info[node].nslave-1; if((_armci_buf_state->table[index].to<(unsigned int) nfirst) || (_armci_buf_state->table[index].to>(unsigned int) nlast)) armci_die2("_armci_buf_ensure_one_outstanding_op_per_node: bad to",node, (int)_armci_buf_state->table[index].to); for(i=0;itable +i; if((buf_state->to >= nfirst) && (buf_state->to<= (unsigned int) nlast)) { if((buf_state->first != (unsigned int) this)&&(buf_state->first==(unsigned int) i) && buf_state->op) { _armci_buf_complete_index(i,0); _armci_buf_release_index(i); } } } } /*\ same as above but for process \*/ void _armci_buf_ensure_one_outstanding_op_per_proc(void *buf, int proc) { int i; int index = _armci_buf_to_index(buf); int this = _armci_buf_state->table[index].first; void _armci_buf_release_index(int i); if(_armci_buf_state->table[index].to !=(unsigned int) proc ) armci_die2("_armci_buf_ensure_one_outstanding_op_per_proc: bad to", proc, (int)_armci_buf_state->table[index].to); for(i=0;itable +i; if(buf_state->to == (unsigned int) proc) { if((buf_state->first != (unsigned int) this)&&(buf_state->first==(unsigned int) i) && buf_state->op) { _armci_buf_complete_index(i,0); _armci_buf_release_index(i); } } } } #define HISTORY__ #ifdef HISTORY typedef struct{ int size; int op; int count; int id; } history_t; history_t history[100]; int h=0; void print_history() { int i; fflush(stdout); printf("%d records\n",h); for(i=0; ibuf[history[i].id].buffer, history[i].count, history[i].op); fflush(stdout); } #endif /*\ call corresponding to GET_SEND_BUF \*/ char *_armci_buf_get_small(int size, int operation, int to) { int avail=_armci_buf_state->smavail,i; /* int ous_in=0,ous_out; */ /* for(i=MAX_BUFS; itable[i].first) { */ /* assert(_armci_buf_state->table[i].op); */ /* } */ /* if(_armci_buf_state->table[i].op) { */ /* assert(_armci_buf_state->table[i].first==i); */ /* ous_in++; */ /* } */ /* } */ if((_armci_buf_state->table[avail].op || _armci_buf_state->table[avail].first)) { for(i=MAX_BUFS;itable[i].op && !_armci_buf_state->table[i].first) break; #if defined(OPENIB) /*not tested on other platforms*/ if(_armci_buf_test_index(i,1)) { /* ous_in-=1; */ break; } #endif } if(i<(MAX_SMALL_BUFS+MAX_BUFS))avail = i; else { /* printf("%d: no free smallbuf. complete index %d\n",armci_me,avail); */ _armci_buf_complete_index(avail,1); /* ous_in-=1; */ } } _armci_buf_state->table[avail].op = operation; _armci_buf_state->table[avail].to = to; _armci_buf_state->table[avail].count= 1; _armci_buf_state->table[avail].first = avail; _armci_buf_state->smallbuf[avail-MAX_BUFS].id.tag=0; _armci_buf_state->smallbuf[avail-MAX_BUFS].id.bufid= avail; _armci_buf_state->smallbuf[avail-MAX_BUFS].id.protocol=0; if(DEBUG_ || 0) { printf("%d:buf_get_sm1:size=%d max=%d got %d ptr=%p op=%d to=%d count=%d first=%d\n", armci_me,size,SMALL_BUF_LEN,avail, _armci_buf_state->smallbuf[avail-MAX_BUFS].buffer,operation,to, (int)_armci_buf_state->table[avail].count,(int)_armci_buf_state->table[avail].first); fflush(stdout); } # ifdef BUF_EXTRA_FIELD_T INIT_SEND_BUF(_armci_buf_state->smallbuf[avail-MAX_BUFS].field,_armci_buf_state->table[avail].snd,_armci_buf_state->table[avail].rcv); #endif _armci_buf_state->smavail = (avail+1-MAX_BUFS)%MAX_SMALL_BUFS + MAX_BUFS; if(DEBUG_ || 0) { printf("%d:buf_get_sm:size=%d max=%d got %d ptr=%p op=%d to=%d count=%d first=%d\n", armci_me,size,SMALL_BUF_LEN,avail, _armci_buf_state->smallbuf[avail-MAX_BUFS].buffer,operation,to, _armci_buf_state->table[avail].count,_armci_buf_state->table[avail].first); fflush(stdout); } /* ous_out=0; */ /* for(i=MAX_BUFS; itable[i].first) { */ /* assert(_armci_buf_state->table[i].op); */ /* } */ /* if(_armci_buf_state->table[i].op) { */ /* assert(_armci_buf_state->table[i].first==i); */ /* ous_out++; */ /* } */ /* } */ /* assert(ous_in+1 == ous_out); */ return(_armci_buf_state->smallbuf[avail-MAX_BUFS].buffer); } /*\ call corresponding to GET_SEND_BUF \*/ char *_armci_buf_get(int size, int operation, int to) { int avail=_armci_buf_state->avail; int count=1, i; /* int ous_in, ous_out; */ /*if small buffer, we go to another routine that gets smallbuf*/ if(size MSG_BUFLEN_SMALL) ){ double val = (double)size; /* use double due to a bug in gcc */ val /= MSG_BUFLEN_SMALL; count=(int)val; if(size%MSG_BUFLEN_SMALL) count++; assert(0); /*FOR NOW:Should never require multiple buffers; what else is pack.c for?*/ } /* start from 0 if there is not enough bufs available from here */ if((avail+count) > MAX_BUFS)avail = 0; /* ous_in=0; */ /* for(i=0; itable[i].first) { */ /* assert(_armci_buf_state->table[i].op); */ /* } */ /* if(_armci_buf_state->table[i].op) { */ /* assert(_armci_buf_state->table[i].first==i); */ /* ous_in++; */ /* } */ /* } */ /* avail should never point to buffer in a middle of a set of used bufs */ if(_armci_buf_state->table[avail].op && (_armci_buf_state->table[avail].first != (unsigned int) avail)){ sleep(1); printf("%d: inconsistent first. avail=%d count=%d first=%d size=%d\n", armci_me, avail, count, _armci_buf_state->table[avail].first, size); armci_die2("armci_buf_get: inconsistent first", avail, _armci_buf_state->table[avail].first); } #if 0 /* we need complete "count" number of buffers */ for(i=0;itable[cur].op && _armci_buf_state->table[cur].first==(unsigned int) cur) _armci_buf_complete_index(cur,1); } #else assert(count == 1); if(_armci_buf_state->table[avail].op || _armci_buf_state->table[avail].first) { for(i=0; itable[i].op && !_armci_buf_state->table[i].first) break; #if defined(OPENIB) if(_armci_buf_test_index(i,1)) { /* ous_in -= 1; */ break; } #endif } if(itable[avail+i].op = operation; _armci_buf_state->table[avail+i].to = to; _armci_buf_state->table[avail+i].count= count; _armci_buf_state->table[avail+i].first = avail; } _armci_buf_state->buf[avail].id.tag=0; _armci_buf_state->buf[avail].id.bufid=avail; _armci_buf_state->buf[avail].id.protocol=0; # ifdef BUF_EXTRA_FIELD_T INIT_SEND_BUF(_armci_buf_state->buf[avail].field,_armci_buf_state->table[avail].snd,_armci_buf_state->table[avail].rcv); #endif #ifdef HISTORY history[h].size=size; history[h].op=operation; history[h].count=count; history[h].id = avail; h++; #endif if(DEBUG_ || 0) { printf("%d:buf_get:size=%d max=%lu got %d ptr=%p count=%d op=%d to=%d\n", armci_me,size,MSG_BUFLEN_SMALL,avail, _armci_buf_state->buf[avail].buffer, count,operation,to); fflush(stdout); } /* select candidate buffer for next allocation request */ _armci_buf_state->avail = avail+count; _armci_buf_state->avail %= MAX_BUFS; /* ous_out=0; */ /* for(i=0; itable[i].first) { */ /* assert(_armci_buf_state->table[i].op); */ /* } */ /* if(_armci_buf_state->table[i].op) { */ /* assert(_armci_buf_state->table[i].first==i); */ /* ous_out++; */ /* } */ /* } */ /* assert(ous_in+1 == ous_out); */ return(_armci_buf_state->buf[avail].buffer); } void _armci_buf_release_index(int index) { int count; buf_state_t *buf_state = _armci_buf_state->table +index; char *_armci_buf_ptr_from_id(int id); if((index >= MAX_BUFS+MAX_SMALL_BUFS)|| (index<0)) armci_die2("armci_buf_release: bad index:",index,MAX_BUFS); count = _armci_buf_state->table[index].count; if(DEBUG_) { printf("%d:_armci_buf_release_index %d ptr=%p count=%d op=%d smavail=%d\n", armci_me,index,_armci_buf_ptr_from_id(index),count, _armci_buf_state->table[index].op,_armci_buf_state->smavail); fflush(stdout); } /* clear table slots for all the buffers in the set for this request */ for(; count; count--, buf_state++) { memset(buf_state, '\0', sizeof(buf_state_t)); /* *(int*)buf_state = 0; */ } if(index >= MAX_BUFS){ _armci_buf_state->smallbuf[index-MAX_BUFS].id.tag=0; #ifndef VAPI _armci_buf_state->smavail = index; #endif } else{ _armci_buf_state->buf[index].id.tag=0; #ifndef VAPI _armci_buf_state->avail = index; #endif } /* the current buffer is prime candidate to satisfy next buffer request */ } /*\ release buffer when it becomes free \*/ void _armci_buf_release(void *buf) { _armci_buf_release_index(_armci_buf_to_index(buf)); } /*\ return pointer to buffer number id \*/ char *_armci_buf_ptr_from_id(int id) { if(id <0 || id >=(MAX_BUFS+MAX_SMALL_BUFS)) armci_die2("armci_buf_ptr_from_id: bad id",id,MAX_BUFS); if(id >=MAX_BUFS)return(_armci_buf_state->smallbuf[id-MAX_BUFS].buffer); return(_armci_buf_state->buf[id].buffer); } /*\function called from PARMCI_Wait to wait for non-blocking ops \*/ void _armci_buf_complete_nb_request(int bufid,unsigned int tag, int *retcode) { int i=0; #if 0 printf("\n%d:wait called with bufid=%d tag=%d \n",armci_me,bufid,tag); fflush(stdout); #endif if(bufid == NB_NONE) *retcode=0; else if(bufid == NB_MULTI) { #if 0 for(i=0;ibuf[i].id.tag) _armci_buf_complete_index(i,1); } for(i=0;ismallbuf[i].id.tag) _armci_buf_complete_index(i+MAX_BUFS,1); } #else { const int smavail=_armci_buf_state->smavail; const int avail = _armci_buf_state->avail; const int startsmall = (smavail-MAX_BUFS+1+MAX_SMALL_BUFS)%MAX_SMALL_BUFS+MAX_BUFS; const int start =(avail+1+MAX_BUFS)%MAX_BUFS; i = start; do { if(tag && tag==_armci_buf_state->buf[i].id.tag) _armci_buf_complete_index(i,1); i = (i+1+MAX_BUFS)%MAX_BUFS; } while(i != start); i = startsmall; do { if(tag && tag==_armci_buf_state->smallbuf[i-MAX_BUFS].id.tag) _armci_buf_complete_index(i,1); i = (i-MAX_BUFS+1+MAX_SMALL_BUFS)%MAX_SMALL_BUFS+MAX_BUFS; } while(i != startsmall); } #endif *retcode=0; } else { if(bufidbuf[bufid].id.tag) _armci_buf_complete_index(bufid,1); } else{ if(tag && tag==_armci_buf_state->smallbuf[bufid-MAX_BUFS].id.tag) _armci_buf_complete_index(bufid,1); } *retcode=0; } } /*\function called from PARMCI_Test to test completion of non-blocking ops \*/ void _armci_buf_test_nb_request(int bufid,unsigned int tag, int *retcode) { int i; if(bufid == NB_NONE) *retcode=0; else if(bufid == NB_MULTI) { for(i=0;ibuf[i].id.tag){ if(_armci_buf_test_index(i,1)){ *retcode=1; break; } } } for(i=0;ismallbuf[i].id.tag) if(_armci_buf_test_index(i+MAX_BUFS,1)){ *retcode=1; break; } } } else { if(bufidbuf[bufid].id.tag) *retcode = _armci_buf_test_index(bufid,1); } else{ if(tag && tag==_armci_buf_state->smallbuf[bufid-MAX_BUFS].id.tag) *retcode = _armci_buf_test_index(bufid,1); } } } /*\function to set the buffer tag and the protocol \*/ void _armci_buf_set_tag(void *bufptr,unsigned int tag,short int protocol) { int index = _armci_buf_to_index(bufptr); /*_armci_buf_state->table[index].async=1;*/ if(indexbuf[index].id.tag=tag; _armci_buf_state->buf[index].id.protocol=protocol; } else{ _armci_buf_state->smallbuf[index-MAX_BUFS].id.tag=tag; _armci_buf_state->smallbuf[index-MAX_BUFS].id.protocol=protocol; } } /*\function to return bufinfo, given buf ptr \*/ BUF_INFO_T *_armci_buf_to_bufinfo(void *buf){ if(buf > (void *)_armci_buffers && buf < (void *)(_armci_buffers+MAX_BUFS)){ return(&((BUF_TO_EBUF(buf))->id)); } else if(buf > (void *)_armci_smbuffers && buf < (void *)(_armci_smbuffers+MAX_SMALL_BUFS)){ return(&((BUF_TO_SMEBUF(buf))->id)); } else { armci_die("armci_buf_to_index: bad pointer",0); return(0); } } /*\function to clear all buffers \*/ void _armci_buf_clear_all() { int i; for(i=0;itable[i].op || _armci_buf_state->table[i].first) CLEAR_SEND_BUF_FIELD(_armci_buf_state->buf[i].field,_armci_buf_state->table[i].snd,_armci_buf_state->table[i].rcv,_armci_buf_state->table[i].to,_armci_buf_state->table[i].op); #endif } for(i=MAX_BUFS;itable[i].op || _armci_buf_state->table[i].first) CLEAR_SEND_BUF_FIELD(_armci_buf_state->smallbuf[i-MAX_BUFS].field,_armci_buf_state->table[i].snd,_armci_buf_state->table[i].rcv,_armci_buf_state->table[i].to,_armci_buf_state->table[i].op); #endif } } #ifdef VAPI /* this will work for vapi as there is no get pipeline enabled in vapi * with get pipeline, this will break very badly */ void _armci_buf_update_scatter_count(int id) { int i,num,last,first; for(i=0;itable[i].op==GET){ request_header_t *msginfo; msginfo = (request_header_t*)_armci_buf_state->buf[i].buffer; if(msginfo->pinned && msginfo->bypass && msginfo->format == STRIDED){ num = *(int *)((char *)msginfo+msginfo->bytes); last = *(int *)((char *)msginfo+msginfo->bytes+sizeof(int)); first = last - num+1; if(first < 0 )first+=DSCRID_SCATTERCLIENT_END-DSCRID_SCATTERCLIENT-1; if(id == first && num!=0){ *(int *)((char *)msginfo+msginfo->bytes) = (--num); return; } } } # endif } for(i=MAX_BUFS;itable[i].op==GET){ request_header_t *msginfo = (request_header_t*)_armci_buf_state->smallbuf[i-MAX_BUFS].buffer; if(msginfo->pinned && msginfo->bypass && msginfo->format == STRIDED){ num = *(int *)((char *)msginfo+msginfo->bytes); last = *(int *)((char *)msginfo+msginfo->bytes+sizeof(int)); first = last - num+1; if(first < 0 )first+=DSCRID_SCATTERCLIENT_END-DSCRID_SCATTERCLIENT-1; if(id == first && num!=0){ *(int *)((char *)msginfo+msginfo->bytes) = (--num); return; } } } # endif } } #endif #endif /* function to return bufinfo, given buf tag */ BUF_INFO_T *_armci_tag_to_bufinfo(msg_tag_t tag) { int idx; for (idx=0; idx < MAX_BUFS; idx++) if (EQ_TAGS(_armci_buffers[idx].id.tag, tag)) break; if (idx == MAX_BUFS) {/* not found is regular buffers */ for (idx = 0; idx < MAX_SMALL_BUFS; idx++) if (EQ_TAGS(_armci_smbuffers[idx].id.tag, tag)) break; if (idx == MAX_SMALL_BUFS) /* not found at all */ #ifdef SOCKETS armci_die("_armci_tag_to_bufinfo: bad tag",tag); #else armci_die("_armci_tag_to_bufinfo: bad tag",0); #endif return &(_armci_smbuffers[idx].id); } else return &(_armci_buffers[idx].id); } /* inline primitives for buffer state management */ char *_armci_buf_get_clear_busy(int size, int operation, int to) { char *buf = _armci_buf_get(size, operation, to); _armci_buf_set_busy(buf, 0); return buf; } void _armci_buf_set_busy(void *buf, int state) { _armci_buf_state->table[_armci_buf_to_index(buf)].busy = state; } void _armci_buf_set_busy_idx(int idx, int state) { _armci_buf_state->table[idx].busy = state; } #if 0 int _armci_buf_cmpld(void *buf) { return _armci_buf_state->table[_armci_buf_to_index(buf)].cmpl; } #else int _armci_buf_cmpld(int bufid) { return _armci_buf_state->table[bufid].cmpl; } #endif void _armci_buf_set_cmpld(void *buf, int state) { _armci_buf_state->table[_armci_buf_to_index(buf)].cmpl = state; } void _armci_buf_set_cmpld_idx(int idx, int state) { _armci_buf_state->table[idx].cmpl = state; } ga-5.9.2/armci/src/memory/kr_malloc.c000066400000000000000000000411431500715745200174300ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: kr_malloc.c,v 1.24 2006-09-12 23:21:21 andriy Exp $ */ #if HAVE_STDIO_H # include #endif #include "kr_malloc.h" #include "armcip.h" /* for DEBUG purpose only. remove later */ #include "locks.h" #define DEBUG 0 /* Storage allocator basically copied from ANSI K&R and corrupted */ extern char *armci_allocate(); /* Used to get memory from the system */ #if !defined(armci_die) extern void armci_die(); #endif static char *kr_malloc_shmem(size_t nbytes, context_t *ctx); static void kr_free_shmem(char *ap, context_t *ctx); /** * DEFAULT_NALLOC: No. of units of length ALIGNMENT to get in every * request to the system for memory (8MB/64 => 128*1024units) * DEFAULT_MAX_NALLOC: Maximum number of units that can get i.e.1GB * (if unit size=64bytes, then max units=1024MB/64 = 16*1024*1024) */ #define DEFAULT_NALLOC (4*128*1024) #define DEFAULT_NALLOC_ALIGN 1024 #define DEFAULT_MAX_NALLOC (64*1024*1024*16) /* mutual exclusion defs go here */ #define LOCKED 100 #define UNLOCKED 101 static int lock_mode=UNLOCKED; /* enable locking only after armci is initailized as locks (and lock data structures) are initialized in PARMCI_Init */ #define LOCKIT(p) \ if(_armci_initialized && lock_mode==UNLOCKED) { \ NAT_LOCK(0,p); lock_mode=LOCKED; \ } #define UNLOCKIT(p) \ if(_armci_initialized && lock_mode==LOCKED) { \ NAT_UNLOCK(0,p); lock_mode=UNLOCKED; \ } static int do_verify = 0; /* Flag for automatic heap verification */ #define VALID1 0xaaaaaaaa /* For validity check on headers */ #define VALID2 0x55555555 #define USEDP 0 /* CHECK. By default anable this. */ static void kr_error(char *s, unsigned long i, context_t *ctx) { char string[256]; sprintf(string,"kr_malloc: %s %ld(0x%lx)\n", s, i, i); #if 0 kr_malloc_print_stats(ctx); #endif armci_die(string, i); } static Header *morecore(size_t nu, context_t *ctx) { char *cp; Header *up; #if DEBUG (void) printf("%d: morecore 1: Getting %ld more units of length %d nalloc=%d\n", armci_me, (long)nu, sizeof(Header),ctx->nalloc); (void) fflush(stdout); #endif if (ctx->total >= ctx->max_nalloc) { # if DEBUG armci_die("kr_malloc: morecore: maximum allocation reached",armci_me); # endif return (Header *) NULL; /* Enforce upper limit on core usage */ } #if 1 /* 07/03 ctx->nalloc is now the minimum # units we ask from OS */ nu = DEFAULT_NALLOC_ALIGN*((nu-1)/DEFAULT_NALLOC_ALIGN+1); if(nu < ctx->nalloc) nu = ctx->nalloc; #else nu = ctx->nalloc*((nu-1)/ctx->nalloc+1); /* nu must by a multiplicity of nalloc */ #endif #if DEBUG (void) printf("%d: morecore: Getting %ld more units of length %d\n", armci_me, (long)nu, sizeof(Header)); (void) fflush(stdout); #endif if ((cp =(char *)(*ctx->alloc_fptr)((size_t)nu * sizeof(Header))) == (char *)NULL) return (Header *) NULL; ctx->total += nu; /* Have just got nu more units */ ctx->nchunk++; /* One more chunk */ ctx->nfrags++; /* Currently one more frag */ ctx->inuse += nu; /* Inuse will be decremented by kr_free */ up = (Header *) cp; up->s.size = nu; up->s.valid1 = VALID1; up->s.valid2 = VALID2; /* Insert into linked list of blocks in use so that kr_free works ... for debug only */ up->s.ptr = ctx->usedp; ctx->usedp = up; kr_free((char *)(up+1), ctx); /* Try to join into the free list */ return ctx->freep; } void kr_malloc_init(size_t usize, /* unit size in bytes */ size_t nalloc, size_t max_nalloc, void * (*alloc_fptr)(), /* memory alloc routine */ int debug, context_t *ctx) { int scale; if(usize <= 0) usize = sizeof(Header); scale = usize>>LOG_ALIGN; if(scale<1)fprintf(stderr,"Error: kr_malloc_init !!!\n"); if(nalloc==0) nalloc = DEFAULT_NALLOC; if(max_nalloc==0) max_nalloc = DEFAULT_MAX_NALLOC; ctx->usize = sizeof(Header); ctx->nalloc = nalloc * scale; ctx->max_nalloc = max_nalloc * scale; ctx->alloc_fptr = alloc_fptr; ctx->freep = NULL; ctx->usedp = NULL; ctx->shmid = -1; ctx->shmoffset = 0; ctx->shmsize = 0; ctx->ctx_type = -1; do_verify = debug; } char *kr_malloc(size_t nbytes, context_t *ctx) { Header *p, *prevp; size_t nunits; char *return_ptr; #if !defined(SHMMAX_SEARCH_NO_FORK) if(ctx->ctx_type == KR_CTX_SHMEM) return kr_malloc_shmem(nbytes,ctx); #endif /* If first time in need to initialize the free list */ if ((prevp = ctx->freep) == NULL) { if (sizeof(Header) != ALIGNMENT) kr_error("Alignment is not valid", (unsigned long) ALIGNMENT, ctx); ctx->total = 0; /* Initialize statistics */ ctx->nchunk = 0; ctx->inuse = 0; ctx->nfrags = 0; ctx->maxuse = 0; ctx->nmcalls= 0; ctx->nfcalls= 0; /* Initialize linked list */ ctx->base.s.ptr = ctx->freep = prevp = &(ctx->base); ctx->base.s.size = 0; ctx->base.s.valid1 = VALID1; ctx->base.s.valid2 = VALID2; } ctx->nmcalls++; if (do_verify) kr_malloc_verify(ctx); /* Rather than divide make the alignment a known power of 2 */ nunits = ((nbytes + sizeof(Header) - 1)>>LOG_ALIGN) + 1; for (p=prevp->s.ptr; ; prevp = p, p = p->s.ptr) { if (p->s.size >= nunits) { /* Big enuf */ if (p->s.size == nunits) /* exact fit */ prevp->s.ptr = p->s.ptr; else { /* allocate tail end */ p->s.size -= nunits; p += p->s.size; p->s.size = nunits; p->s.valid1 = VALID1; p->s.valid2 = VALID2; ctx->nfrags++; /* Have just increased the fragmentation */ } /* Insert into linked list of blocks in use ... for debug only */ p->s.ptr = ctx->usedp; ctx->usedp = p; ctx->inuse += nunits; /* Record usage */ if (ctx->inuse > ctx->maxuse) ctx->maxuse = ctx->inuse; ctx->freep = prevp; return_ptr = (char *) (p+1); break; } if (p == ctx->freep) { /* wrapped around the free list */ if ((p = morecore(nunits, ctx)) == (Header *) NULL) { return_ptr = (char *) NULL; break; } } } return return_ptr; } void kr_free(char *ap, context_t *ctx) { Header *bp, *p, **up; #if !defined(SHMMAX_SEARCH_NO_FORK) if(ctx->ctx_type == KR_CTX_SHMEM) { kr_free_shmem(ap,ctx); return; } #endif ctx->nfcalls++; if (do_verify) kr_malloc_verify(ctx); /* only do something if pointer is not NULL */ if ( ap ) { bp = (Header *) ap - 1; /* Point to block header */ if (bp->s.valid1 != VALID1 || bp->s.valid2 != VALID2) kr_error("kr_free: pointer not from kr_malloc", (unsigned long) ap, ctx); ctx->inuse -= bp->s.size; /* Decrement memory ctx->usage */ /* Extract the block from the used linked list ... for debug only */ for (up=&(ctx->usedp); ; up = &((*up)->s.ptr)) { if (!*up) kr_error("kr_free: block not found in used list\n", (unsigned long) ap, ctx); if (*up == bp) { *up = bp->s.ptr; break; } } /* Join the memory back into the free linked list */ for (p=ctx->freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) if (p >= p->s.ptr && (bp > p || bp < p->s.ptr)) break; /* Freed block at start or end of arena */ if (bp + bp->s.size == p->s.ptr) {/* join to upper neighbour */ bp->s.size += p->s.ptr->s.size; bp->s.ptr = p->s.ptr->s.ptr; ctx->nfrags--; /* Lost a fragment */ } else bp->s.ptr = p->s.ptr; if (p + p->s.size == bp) { /* Join to lower neighbour */ p->s.size += bp->s.size; p->s.ptr = bp->s.ptr; ctx->nfrags--; /* Lost a fragment */ } else p->s.ptr = bp; ctx->freep = p; } /* end if on ap */ } /* Print to standard output the usage statistics. */ void kr_malloc_print_stats(context_t *ctx) { fflush(stderr); printf("\nkr_malloc statistics\n-------------------\n\n"); printf("Total memory from system ... %ld bytes\n", (long)(ctx->total*ctx->usize)); printf("Current memory usage ....... %ld bytes\n", (long)(ctx->inuse*ctx->usize)); printf("Maximum memory usage ....... %ld bytes\n", (long)(ctx->maxuse*ctx->usize)); printf("No. chunks from system ..... %ld\n", ctx->nchunk); printf("No. of fragments ........... %ld\n", ctx->nfrags); printf("No. of calls to kr_malloc ... %ld\n", ctx->nmcalls); printf("No. of calls to kr_free ..... %ld\n", ctx->nfcalls); printf("\n"); fflush(stdout); } /* Currently assumes that are working in a single region. */ void kr_malloc_verify(context_t *ctx) { Header *p; if(_armci_initialized && lock_mode==UNLOCKED) { LOCKIT(armci_master); lock_mode=LOCKED; } if ( ctx->freep ) { /* Check the used list */ for (p=ctx->usedp; p; p=p->s.ptr) { if (p->s.valid1 != VALID1 || p->s.valid2 != VALID2) kr_error("invalid header on usedlist", (unsigned long) p->s.valid1, ctx); if (p->s.size > ctx->total) kr_error("invalid size in header on usedlist", (unsigned long) p->s.size, ctx); } /* Check the free list */ p = ctx->base.s.ptr; while (p != &(ctx->base)) { if (p->s.valid1 != VALID1 || p->s.valid2 != VALID2) kr_error("invalid header on freelist", (unsigned long) p->s.valid1, ctx); if (p->s.size > ctx->total) kr_error("invalid size in header on freelist", (unsigned long) p->s.size, ctx); p = p->s.ptr; } } /* end if */ if(_armci_initialized && lock_mode==LOCKED) { UNLOCKIT(armci_master); lock_mode=UNLOCKED; } } /********************** BEGIN: kr_malloc for ctx_shmem *********************/ #if defined(SYSV) || defined(MMAP) #include "armci_shmem.h" extern int armci_get_shmem_info(char *addrp, int* shmid, long *shmoffset, size_t *shmsize); extern Header *armci_get_shmem_ptr(int shmid, long shmoffset, size_t shmsize); /* returns, address of the shared memory region based on shmid, offset. * (i.e. return_addr = stating address of shmid + offset) */ #define SHM_PTR(hdr) armci_get_shmem_ptr((hdr)->s.shmid, (hdr)->s.shmoffset, (hdr)->s.shmsize) /* * kr_malloc_shmem: memory allocator for shmem context (i.e ctx_shmem) */ static char *kr_malloc_shmem(size_t nbytes, context_t *ctx) { Header *p, *prevp; size_t nunits, prev_shmsize=0; char *return_ptr; int prev_shmid=-1; long prev_shmoffset=0; LOCKIT(armci_master); /* Rather than divide make the alignment a known power of 2 */ nunits = ((nbytes + sizeof(Header) - 1)>>LOG_ALIGN) + 1; /* If first time in need to initialize the free list */ if ((prevp = ctx->freep) == NULL) { if (sizeof(Header) != ALIGNMENT) kr_error("kr_malloc_shmem: Alignment is not valid", (unsigned long) ALIGNMENT, ctx); ctx->total = 0; /* Initialize statistics */ ctx->nchunk = ctx->inuse = ctx->maxuse = 0; ctx->nfrags = ctx->nmcalls = ctx->nfcalls = 0; /* Initialize linked list */ ctx->base.s.size = 0; ctx->base.s.shmid = -1; ctx->base.s.shmoffset = 0; ctx->base.s.shmsize = 0; ctx->base.s.valid1 = VALID1; ctx->base.s.valid2 = VALID2; if ((p = morecore(nunits, ctx)) == (Header *) NULL) return NULL; ctx->base.s.ptr = prevp = ctx->freep; /* CHECK */ } prev_shmid = ctx->shmid; prev_shmoffset = ctx->shmoffset; prev_shmsize = ctx->shmsize; prevp = ctx->freep = armci_get_shmem_ptr(ctx->shmid, ctx->shmoffset, ctx->shmsize); ctx->nmcalls++; if (do_verify) kr_malloc_verify(ctx); for (p=SHM_PTR(prevp); ; prevp = p, p = SHM_PTR(p)) { if (p->s.size >= nunits) { /* Big enuf */ if (p->s.size == nunits) { /* exact fit */ prevp->s.ptr = p->s.ptr; prevp->s.shmid = p->s.shmid; prevp->s.shmoffset = p->s.shmoffset; prevp->s.shmsize = p->s.shmsize; } else { /* allocate tail end */ p->s.size -= nunits; p += p->s.size; p->s.size = nunits; p->s.valid1 = VALID1; p->s.valid2 = VALID2; ctx->nfrags++; /* Have just increased the fragmentation */ } #if USEDP /* Insert into linked list of blocks in use ... for debug only */ p->s.ptr = ctx->usedp; ctx->usedp = p; #endif ctx->inuse += nunits; /* Record usage */ if (ctx->inuse > ctx->maxuse) ctx->maxuse = ctx->inuse; ctx->freep = prevp; ctx->shmid = prev_shmid; ctx->shmoffset = prev_shmoffset; ctx->shmsize = prev_shmsize; return_ptr = (char *) (p+1); break; } prev_shmid = prevp->s.shmid; prev_shmoffset = prevp->s.shmoffset; prev_shmsize = prevp->s.shmsize; if (p == ctx->freep) { /* wrapped around the free list */ if ((p = morecore(nunits, ctx)) == (Header *) NULL) { return_ptr = (char *) NULL; break; } prev_shmid = ctx->shmid; prev_shmoffset = ctx->shmoffset; prev_shmsize = ctx->shmsize; } } UNLOCKIT(armci_master); return return_ptr; } static void kr_free_shmem(char *ap, context_t *ctx) { Header *bp, *p, *nextp; #if USEDP Header **up; #endif int shmid=-1; long shmoffset=0; size_t shmsize=0; LOCKIT(armci_master); ctx->nfcalls++; if (do_verify) kr_malloc_verify(ctx); /* only do something if pointer is not NULL */ if ( ap ) { bp = (Header *) ap - 1; /* Point to block header */ if (bp->s.valid1 != VALID1 || bp->s.valid2 != VALID2) kr_error("kr_free_shmem: pointer not from kr_malloc", (unsigned long) ap, ctx); ctx->inuse -= bp->s.size; /* Decrement memory ctx->usage */ #if USEDP /* Extract the block from the used linked list ... for debug only */ for (up=&(ctx->usedp); ; up = &((*up)->s.ptr)) { if (!*up) kr_error("kr_free_shmem: block not found in used list\n", (unsigned long) ap, ctx); if (*up == bp) { *up = bp->s.ptr; break; } } #endif if(ctx->shmid==-1) { armci_get_shmem_info((char*)bp, &ctx->shmid, &ctx->shmoffset, &ctx->shmsize); ctx->base.s.shmid = ctx->shmid; ctx->base.s.shmsize = ctx->shmsize; ctx->base.s.shmoffset = ctx->shmoffset; p = ctx->freep = bp; p->s.ptr = bp; p->s.size-=SHMEM_CTX_UNITS; /*memory to store shmem info in context*/ p->s.shmid = ctx->shmid; p->s.shmsize = ctx->shmsize; p->s.shmoffset = ctx->shmoffset; UNLOCKIT(armci_master); return; } ctx->freep = armci_get_shmem_ptr(ctx->shmid, ctx->shmoffset, ctx->shmsize); shmid = ctx->shmid; shmoffset = ctx->shmoffset; shmsize = ctx->shmsize; /* Join the memory back into the free linked list */ p = ctx->freep; nextp = SHM_PTR(p); for ( ; !(bp > p && bp < nextp); p=nextp, nextp=SHM_PTR(p)) { if (p >= nextp && (bp > p || bp < nextp)) break; /* Freed block at start or end of arena */ nextp = SHM_PTR(p); shmid = p->s.shmid; shmoffset = p->s.shmoffset; shmsize = p->s.shmsize; } if (bp + bp->s.size == nextp) {/* join to upper neighbour */ bp->s.size += nextp->s.size; bp->s.ptr = nextp->s.ptr; ctx->nfrags--; /* Lost a fragment */ bp->s.shmid = nextp->s.shmid; bp->s.shmoffset = nextp->s.shmoffset; bp->s.shmsize = nextp->s.shmsize; } else { bp->s.ptr = nextp; bp->s.shmid = p->s.shmid; bp->s.shmoffset = p->s.shmoffset; bp->s.shmsize = p->s.shmsize; } if (p + p->s.size == bp) { /* Join to lower neighbour */ p->s.size += bp->s.size; p->s.ptr = bp->s.ptr; ctx->nfrags--; /* Lost a fragment */ p->s.shmid = bp->s.shmid; p->s.shmoffset = bp->s.shmoffset; p->s.shmsize = bp->s.shmsize; } else { p->s.ptr = bp; armci_get_shmem_info((char*)bp, &p->s.shmid, &p->s.shmoffset, &p->s.shmsize); } ctx->freep = p; ctx->shmid = shmid; ctx->shmoffset = shmoffset; ctx->shmsize = shmsize; } /* end if on ap */ UNLOCKIT(armci_master); } #else /* #ifdef SYSV */ /* What are doing here */ static char *kr_malloc_shmem(size_t nbytes, context_t *ctx) { armci_die("kr_malloc_shmem(): Invalid Function Call", 0L); } static void kr_free_shmem(char *ap, context_t *ctx) { armci_die("kr_free_shmem(): Invalid Function Call", 0L); } #endif /* #ifdef SYSV */ /********************** END: kr_malloc for ctx_shmem *********************/ /** issues: 1. do usage statistics only if debug/DEBUG is enabled */ ga-5.9.2/armci/src/memory/memory.c000066400000000000000000001004571500715745200170010ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: memory.c,v 1.56.2.3 2007-04-25 23:49:55 d3p687 Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_ASSERT_H # include #endif #include "armcip.h" #include "message.h" #include "kr_malloc.h" #define DEBUG_ 0 #define USE_MALLOC #define USE_SHMEM_ #define SHM_UNIT 1024 static context_t ctx_localmem; #if defined(ALLOW_PIN) static context_t ctx_mlocalmem; #endif #if defined(SYSV) || defined(WIN32) || defined(MMAP) #include "armci_shmem.h" #if !defined(SERVER_THREAD) ||\ defined(USE_SHMEM) #define RMA_NEEDS_SHMEM #endif #if defined(DATA_SERVER) && !defined(SERVER_THREAD) extern int _armci_server_started; #endif void kr_check_local() { #if 0 kr_malloc_print_stats(&ctx_localmem); #endif kr_malloc_verify(&ctx_localmem); } void armci_print_ptr(void **ptr_arr, int bytes, int size, void* myptr, int off) { int i; int nproc = armci_clus_info[armci_clus_me].nslave; ARMCI_PR_DBG("enter",0); for(i=0; i< armci_nproc; i++){ int j; if(armci_me ==i){ printf("%d master =%d nproc=%d off=%d\n",armci_me, armci_master,nproc, off); printf("%d:bytes=%d mptr=%p s=%d ",armci_me, bytes, myptr,size); for(j = 0; j< armci_nproc; j++)printf(" %p",ptr_arr[j]); printf("\n"); fflush(stdout); } armci_msg_barrier(); } ARMCI_PR_DBG("exit",0); } /*\ master exports its address of shmem region at the beggining of that region \*/ static void armci_master_exp_attached_ptr(void* ptr) { ARMCI_PR_DBG("enter",0); if(!ptr) armci_die("armci_master_exp_att_ptr: null ptr",0); *(volatile void**)ptr = ptr; ARMCI_PR_DBG("exit",0); } /*\ Collective Memory Allocation on shared memory systems \*/ void armci_shmem_malloc(void *ptr_arr[], armci_size_t bytes) { void *myptr=NULL, *ptr=NULL; long idlist[SHMIDLEN]; long size=0, offset=0; long *size_arr; void **ptr_ref_arr; int i,cn, len; int nproc = armci_clus_info[armci_clus_me].nslave; ARMCI_PR_DBG("enter",0); bzero((char*)ptr_arr,armci_nproc*sizeof(void*)); /* allocate work arrays */ size_arr = (long*)calloc(armci_nproc,sizeof(long)); if(!size_arr)armci_die("armci_malloc:calloc failed",armci_nproc); /* allocate arrays for cluster address translations */ # if defined(DATA_SERVER) len = armci_nclus; # else len = nproc; # endif ptr_ref_arr = calloc(len,sizeof(void*)); /* must be zero */ if(!ptr_ref_arr)armci_die("armci_malloc:calloc 2 failed",len); /* combine all memory requests into size_arr */ size_arr[armci_me] = bytes; armci_msg_lgop(size_arr, armci_nproc, "+"); /* determine aggregate request size on the cluster node */ for(i=0, size=0; i< nproc; i++) size += size_arr[i+armci_master]; /* master process creates shmem region and then others attach to it */ if(armci_me == armci_master ){ /* can malloc if there is no data server process and has 1 process/node*/ # ifndef RMA_NEEDS_SHMEM if(nproc == 1) myptr = kr_malloc(size, &ctx_localmem); else # endif myptr = Create_Shared_Region(idlist+1,size,idlist); if(!myptr && size>0 )armci_die("armci_malloc: could not create", (int)(size>>10)); /* place its address at begining of attached region for others to see */ if(size)armci_master_exp_attached_ptr(myptr); if(DEBUG_){ printf("%d:armci_malloc addr mptr=%p size=%ld\n",armci_me,myptr,size); fflush(stdout); } } /* broadcast shmem id to other processes on the same cluster node */ armci_msg_clus_brdcst(idlist, SHMIDLEN*sizeof(long)); if(armci_me != armci_master){ myptr=(double*)Attach_Shared_Region(idlist+1,size,idlist[0]); if(!myptr)armci_die("armci_malloc: could not attach", (int)(size>>10)); /* now every process in a SMP node needs to find out its offset * w.r.t. master - this offset is necessary to use memlock table */ if(size) armci_set_mem_offset(myptr); if(DEBUG_){ printf("%d:armci_malloc attached addr mptr=%p ref=%p size=%ld\n", armci_me,myptr, *(void**)myptr,size); fflush(stdout); } } # if defined(DATA_SERVER) /* get server reference address for every cluster node to perform * remote address translation for global address space */ if(armci_nclus>1){ if(armci_me == armci_master){ # ifdef SERVER_THREAD /* data server thread runs on master process */ ptr_ref_arr[armci_clus_me]=myptr; # else /* ask dataserver process to attach to the region and get * ptr*/ { if(_armci_server_started) { armci_serv_attach_req(idlist, SHMIDLEN*sizeof(long), size, &ptr, sizeof(void*)); ptr_ref_arr[armci_clus_me]= ptr; /* from server*/ } else /* server not yet started */ ptr_ref_arr[armci_clus_me]=myptr; } if(DEBUG_){ printf("%d:addresses server=%p myptr=%p\n",armci_me,ptr,myptr); fflush(stdout); } # endif } /* exchange ref addr of shared memory region on every cluster node*/ armci_exchange_address(ptr_ref_arr, armci_nclus); }else { ptr_ref_arr[armci_master] = myptr; } /* translate addresses for all cluster nodes */ for(cn = 0; cn < armci_nclus; cn++){ int master = armci_clus_info[cn].master; offset = 0; /* on local cluster node use myptr directly */ ptr = (armci_clus_me == cn) ? myptr: ptr_ref_arr[cn]; /* compute addresses pointing to the memory regions on cluster node*/ for(i=0; i< armci_clus_info[cn].nslave; i++){ /* NULL if request size is 0*/ ptr_arr[i+master] = (size_arr[i+master])? ((char*)ptr)+offset : NULL; offset += size_arr[i+master]; } } # else /* compute addresses for local cluster node */ offset =0; for(i=0; i< nproc; i++) { ptr_ref_arr[i] = (size_arr[i+armci_master])? ((char*)myptr)+offset : 0L; offset += size_arr[i+armci_master]; } /* exchange addreses with all other processes */ ptr_arr[armci_me] = (char*)ptr_ref_arr[armci_me-armci_master]; armci_exchange_address(ptr_arr, armci_nproc); /* overwrite entries for local cluster node with ptr_ref_arr */ bcopy((char*)ptr_ref_arr, (char*)(ptr_arr+armci_master), nproc*sizeof(void*)); /* armci_print_ptr(ptr_arr, bytes, size, myptr, offset);*/ # endif #ifdef ALLOW_PIN if(armci_nclus>1)armci_global_region_exchange(myptr, (long) size_arr[armci_me]); else #endif armci_msg_barrier(); /* free work arrays */ free(ptr_ref_arr); free(size_arr); ARMCI_PR_DBG("exit",0); } /******************************************************************** * Non-collective Memory Allocation on shared memory systems \*/ void armci_shmem_memget(armci_meminfo_t *meminfo, size_t size) { void *myptr=NULL; void *armci_ptr=NULL; /* legal ARCMIptr used in ARMCI data xfer ops */ long idlist[SHMIDLEN]; /* can malloc if there is no data server process & has 1 process/node*/ #ifndef RMA_NEEDS_SHMEM if( armci_clus_info[armci_clus_me].nslave == 1) myptr = kr_malloc(size, &ctx_localmem); else #endif myptr = Create_Shared_Region(idlist+1,size,idlist); if(!myptr && size>0 ) armci_die("armci_shmem_memget: create failed", (int)(size>>10)); if(DEBUG_) { printf("%d: armci_shmem_memget: addr=%p size=%ld %ld %ld \n", armci_me, myptr, (long)size, idlist[0], idlist[1]); fflush(stdout); } armci_ptr = myptr; #if defined(DATA_SERVER) /* get server reference address to perform * remote address translation for global address space */ if(armci_nclus>1) { # ifdef SERVER_THREAD /* data server thread runs on master process */ if(armci_me != armci_master) { armci_serv_attach_req(idlist, SHMIDLEN*sizeof(long), size, &armci_ptr, sizeof(void*)); } # else /* ask dataserver process to attach to region and get ptr*/ { if(_armci_server_started) { armci_serv_attach_req(idlist, SHMIDLEN*sizeof(long), size, &armci_ptr, sizeof(void*)); } } # endif } #endif /* fill the meminfo structure */ meminfo->armci_addr = armci_ptr; meminfo->addr = myptr; meminfo->size = size; meminfo->cpid = armci_me; bcopy(idlist, meminfo->idlist, SHMIDLEN*sizeof(long)); } void* armci_shmem_memat(armci_meminfo_t *meminfo) { void *ptr=NULL; long size = (long) meminfo->size; long *idlist = (long*) meminfo->idlist; if(SAMECLUSNODE(meminfo->cpid)) { /* Attach to the shared memory segment */ ptr=(double*)Attach_Shared_Region(idlist+1,size,idlist[0]); if(!ptr)armci_die("ARMCi_Memat: could not attach", (int)(size>>10)); /* CHECK: now every process in a SMP node needs to find out its offset * w.r.t. master - this offset is necessary to use memlock table */ if(size) armci_set_mem_offset(ptr); } else { ptr = meminfo->armci_addr; /* remote address */ } return ptr; } void armci_shmem_memctl(armci_meminfo_t *meminfo) { /* only the creator can delete the segment */ if(meminfo->cpid == armci_me) { void *ptr = meminfo->addr; #ifdef RMA_NEEDS_SHMEM Free_Shmem_Ptr(0,0,ptr); #else if(armci_clus_info[armci_clus_me].nslave>1) Free_Shmem_Ptr(0,0,ptr); else kr_free(ptr, &ctx_localmem); #endif } } /****** End: Non-collective memory allocation on shared memory systems *****/ #ifdef MSG_COMMS_MPI /******************************************************************** * Group Memory Allocation on shared memory systems for ARMCI Groups \*/ void armci_shmem_malloc_group(void *ptr_arr[], armci_size_t bytes, ARMCI_Group *group) { void *myptr=NULL, *ptr=NULL; long idlist[SHMIDLEN]; long size=0, offset=0; long *size_arr; void **ptr_ref_arr; int i,cn, len; /* int nproc = armci_clus_info[armci_clus_me].nslave; ? change ? */ int grp_me, grp_nproc, grp_nclus, grp_master, grp_clus_nproc, grp_clus_me; armci_grp_attr_t *grp_attr=ARMCI_Group_getattr(group); ARMCI_PR_DBG("enter",0); /* Get the group info: group size & group rank */ ARMCI_Group_size(group, &grp_nproc); ARMCI_Group_rank(group, &grp_me); if(grp_me == MPI_UNDEFINED) { /* check if the process is in this group */ armci_die("armci_malloc_group: process is not a member in this group", armci_me); } grp_nclus = grp_attr->grp_nclus; grp_clus_me = grp_attr->grp_clus_me; grp_master = grp_attr->grp_clus_info[grp_clus_me].master; grp_clus_nproc = grp_attr->grp_clus_info[grp_clus_me].nslave; bzero((char*)ptr_arr,grp_nproc*sizeof(void*)); /* allocate work arrays */ size_arr = (long*)calloc(grp_nproc,sizeof(long)); if(!size_arr)armci_die("armci_malloc_group:calloc failed",grp_nproc); /* allocate arrays for cluster address translations */ # if defined(DATA_SERVER) len = grp_nclus; # else len = grp_clus_nproc; # endif ptr_ref_arr = calloc(len,sizeof(void*)); /* must be zero */ if(!ptr_ref_arr)armci_die("armci_malloc_group:calloc 2 failed",len); /* combine all memory requests into size_arr */ size_arr[grp_me] = bytes; armci_msg_group_gop_scope(SCOPE_ALL, size_arr, grp_nproc, "+", ARMCI_LONG, group); /* determine aggregate request size on the cluster node */ for(i=0, size=0; i< grp_clus_nproc; i++) size += size_arr[i+grp_master]; /* master process creates shmem region and then others attach to it */ if(grp_me == grp_master ){ /* can malloc if there is no data server process and has 1 process/node*/ # ifndef RMA_NEEDS_SHMEM if( armci_clus_info[armci_clus_me].nslave == 1) myptr = kr_malloc(size, &ctx_localmem); else # endif myptr = Create_Shared_Region(idlist+1,size,idlist); if(!myptr && size>0 )armci_die("armci_malloc_group: could not create", (int)(size>>10)); /* place its address at begining of attached region for others to see */ if(size)armci_master_exp_attached_ptr(myptr); if(DEBUG_){ printf("%d:armci_malloc_group addr mptr=%p ref=%p size=%ld %ld %ld \n",armci_me,myptr,*(void**)myptr, size,idlist[0],idlist[1]); fflush(stdout); } } /* broadcast shmem id to other processes (in the same group) on the same cluster node */ armci_grp_clus_brdcst(idlist, SHMIDLEN*sizeof(long), grp_master, grp_clus_nproc, group); if(grp_me != grp_master){ myptr=(double*)Attach_Shared_Region(idlist+1,size,idlist[0]); if(!myptr)armci_die("armci_malloc_group: could not attach", (int)(size>>10)); /* now every process in a SMP node needs to find out its offset * w.r.t. master - this offset is necessary to use memlock table */ if(size) armci_set_mem_offset(myptr); if(DEBUG_){ printf("%d:armci_malloc_group attached addr mptr=%p ref=%p size=%ld\n", armci_me,myptr, *(void**)myptr,size); fflush(stdout); } } # if defined(DATA_SERVER) /* get server reference address for every cluster node in the group * to perform remote address translation for global address space */ if(grp_nclus>1){ if(grp_me == grp_master){ # ifdef SERVER_THREAD /* data server thread runs on master process */ if(ARMCI_Absolute_id(group,grp_master)!=armci_master){ /*printf("\n%d: grp_master=%d %ld %ld \n",armci_me,ARMCI_Absolute_id(group,grp_master),idlist[0],idlist[1]);*/ armci_serv_attach_req(idlist, SHMIDLEN*sizeof(long), size, &ptr, sizeof(void*)); ptr_ref_arr[grp_clus_me]= ptr; /* from server*/ } else ptr_ref_arr[grp_clus_me]=myptr; # else /* ask data server process to attach to the region and get ptr */ { if(_armci_server_started) { armci_serv_attach_req(idlist, SHMIDLEN*sizeof(long), size, &ptr, sizeof(void*)); ptr_ref_arr[grp_clus_me]= ptr; /* from server*/ } else /* server not yet started */ ptr_ref_arr[grp_clus_me]=myptr; } if(DEBUG_){ printf("%d:addresses server=%p myptr=%p\n",grp_me,ptr,myptr); fflush(stdout); } # endif } /* exchange ref addr of shared memory region on every cluster node*/ { int ratio = sizeof(void*)/sizeof(int); if(DEBUG_)printf("%d: exchanging %ld ratio=%d\n",armci_me, (long)ptr_arr[grp_me], ratio); armci_msg_group_gop_scope(SCOPE_ALL, ptr_ref_arr, grp_nclus*ratio, "+", ARMCI_INT, group); } }else { ptr_ref_arr[grp_master] = myptr; } /* translate addresses for all cluster nodes */ for(cn = 0; cn < grp_nclus; cn++){ int master = grp_attr->grp_clus_info[cn].master; offset = 0; /* on local cluster node use myptr directly */ ptr = (grp_clus_me == cn) ? myptr: ptr_ref_arr[cn]; /* compute addresses pointing to the memory regions on cluster node*/ for(i=0; i< grp_attr->grp_clus_info[cn].nslave; i++){ /* NULL if request size is 0*/ ptr_arr[i+master] =(size_arr[i+master])? ((char*)ptr)+offset: NULL; offset += size_arr[i+master]; } } # else /* compute addresses for local cluster node */ offset =0; for(i=0; i< grp_clus_nproc; i++) { ptr_ref_arr[i] = (size_arr[i+grp_master])? ((char*)myptr)+offset : 0L; offset += size_arr[i+grp_master]; } /* exchange addreses with all other processes */ ptr_arr[grp_me] = (char*)ptr_ref_arr[grp_me-grp_master]; armci_exchange_address_grp(ptr_arr, grp_nproc, group); /* overwrite entries for local cluster node with ptr_ref_arr */ bcopy((char*)ptr_ref_arr, (char*)(ptr_arr+grp_master), grp_clus_nproc*sizeof(void*)); # endif /* armci_print_ptr(ptr_arr, bytes, size, myptr, offset);*/ #ifdef ALLOW_PIN #if 0 /* ????? check ?????? */ armci_die("armci_malloc_group: Not yet implemented", 0); if(grp_nclus>1)armci_global_region_exchange(myptr, (long) size_arr[grp_me]); else #endif #endif armci_msg_group_barrier(group); /* free work arrays */ free(ptr_ref_arr); free(size_arr); ARMCI_PR_DBG("exit",0); } #endif /* ifdef MSG_COMMS_MPI */ #else void armci_shmem_malloc(void* ptr_arr[], int bytes) { armci_die("armci_shmem_malloc should never be called on this system",0); } void armci_shmem_memget(armci_meminfo_t *meminfo, size_t size) { armci_die("armci_shmem_memget should never be called on this system",0); } void* armci_shmem_memat(armci_meminfo_t *meminfo) { armci_die("armci_shmem_memat should never be called on this system",0); } void armci_shmem_memctl(armci_meminfo_t *meminfo) { armci_die("armci_shmem_memctl should never be called on this system",0); } # ifdef MSG_COMMS_MPI void armci_shmem_malloc_group(void *ptr_arr[], armci_size_t bytes, ARMCI_Group *group) { armci_die("armci_shmem_malloc_group should never be called on this system",0); } # endif #endif #ifdef ALLOW_PIN void *reg_malloc(size_t size) { char *ptr; ARMCI_PR_DBG("enter",0); ptr = malloc(size); armci_region_register_loc(ptr,size); ARMCI_PR_DBG("exit",0); return(ptr); } #endif /* public constructor to initialize the kr_malloc context */ void armci_krmalloc_init_localmem() { #if defined(ALLOW_PIN) kr_malloc_init(0, 0, 0, reg_malloc, 0, &ctx_localmem); kr_malloc_init(0, 0, 0, malloc, 0, &ctx_mlocalmem); ctx_mlocalmem.ctx_type = KR_CTX_LOCALMEM; #else kr_malloc_init(0, 0, 0, malloc, 0, &ctx_localmem); #endif ctx_localmem.ctx_type = KR_CTX_LOCALMEM; } /** * Local Memory Allocation and Free */ void *PARMCI_Malloc_local(armci_size_t bytes) { ARMCI_PR_DBG("enter",0); ARMCI_PR_DBG("exit",0); return (void *)kr_malloc((size_t)bytes, &ctx_localmem); } int PARMCI_Free_local(void *ptr) { ARMCI_PR_DBG("enter",0); kr_free((char *)ptr, &ctx_localmem); ARMCI_PR_DBG("exit",0); return 0; } #ifdef REGION_ALLOC static context_t ctx_region_shmem; static long *reg_pids=NULL; void armci_region_shm_malloc(void *ptr_arr[], size_t bytes) { long size=bytes; void *ptr; int i, peers=armci_clus_last-armci_clus_first+1; extern void* armci_region_getcore(size_t); extern int armci_region_register(int p, void **pinout, long pid, size_t bytes); if(!reg_pids){ kr_malloc_init(0,0,500*1024*1024, armci_region_getcore, 0, &ctx_region_shmem); reg_pids = (long*)calloc(peers,sizeof(long)); reg_pids[armci_me -armci_clus_first] = getpid(); armci_msg_gop_scope(SCOPE_NODE,reg_pids, peers,"+",ARMCI_LONG); } ptr=kr_malloc((size_t)size, &ctx_region_shmem); if(bytes) if(!ptr) armci_die("armci_region_shm_malloc: failed",bytes); bzero((char*)ptr_arr,armci_nproc*sizeof(void*)); ptr_arr[armci_me] = ptr; /* now combine individual addresses into a single array */ armci_exchange_address(ptr_arr, armci_nproc); for(i=0; igrp_clus_me; int i, peers=grp_attr->grp_clus_info[grp_clus_me].nslave; int grp_clus_first=grp_attr->grp_clus_info[grp_clus_me].master; extern void* armci_region_getcore(size_t); extern int armci_region_register(int p, void **pinout, long pid, size_t bytes); ARMCI_Group_rank(group, &grp_me); ARMCI_Group_size(group, &grp_nproc); ptr=kr_malloc((size_t)size, &ctx_region_shmem); if(bytes) if(!ptr) armci_die("armci_region_shm_malloc_grp: failed",bytes); bzero((char*)ptr_arr,grp_nproc*sizeof(void*)); ptr_arr[grp_me] = ptr; /* now combine individual addresses into a single array */ armci_exchange_address_grp(ptr_arr, grp_nproc, group); for(i=0; i 1) # endif if(ARMCI_Uses_shm()){ if(armci_me==armci_master){ # ifdef RMA_NEEDS_SHMEM Free_Shmem_Ptr(0,0,ptr); # else if(armci_clus_info[armci_clus_me].nslave>1) Free_Shmem_Ptr(0,0,ptr); else kr_free(ptr, &ctx_localmem); # endif } ptr = NULL; return 0; } # endif kr_free(ptr, &ctx_localmem); # endif /* REGION_ALLOC */ ptr = NULL; ARMCI_PR_DBG("exit",0); return 0; } int PARMCI_Free_memdev(void *ptr) { return PARMCI_Free(ptr); } int ARMCI_Uses_shm() { int uses=0; #if (defined(SYSV) || defined(WIN32) || defined(MMAP)) && !defined(NO_SHM) # ifdef RMA_NEEDS_SHMEM if(armci_nproc >1) uses= 1; /* always unless serial mode */ # else if(armci_nproc != armci_nclus)uses= 1; /* only when > 1 node used */ # endif #endif if(DEBUG_) fprintf(stderr,"%d:uses shmem %d\n",armci_me, uses); return uses; } #ifdef MSG_COMMS_MPI int ARMCI_Uses_shm_grp(ARMCI_Group *group) { int uses=0, grp_me, grp_nproc; /*int grp_nclus;*/ /*armci_grp_attr_t *grp_attr=ARMCI_Group_getattr(group);*/ ARMCI_PR_DBG("enter",0); ARMCI_Group_size(group, &grp_nproc); ARMCI_Group_rank(group, &grp_me); #if (defined(SYSV) || defined(WIN32) || defined(MMAP)) && !defined(NO_SHM) # ifdef RMA_NEEDS_SHMEM if(grp_nproc >1) uses= 1; /* always unless serial mode */ # else #if 0 grp_nclus = grp_attr->grp_nclus; if(grp_nproc != grp_nclus)uses= 1; /* only when > 1 node used */ #else if(armci_nproc != armci_nclus)uses= 1; /* only when > 1 node used */ #endif # endif #endif if(DEBUG_) fprintf(stderr,"%d (grp_id=%d):uses shmem %d\n",armci_me, grp_me, uses); ARMCI_PR_DBG("exit",0); return uses; } /*\ ************** Begin Group Collective Memory Allocation ****************** * returns array of pointers to blocks of memory allocated by everybody * Note: as the same shared memory region can be mapped at different locations * in each process address space, the array might hold different values * on every process. However, the addresses are legitimate * and can be used in the ARMCI data transfer operations. * ptr_arr[nproc] \*/ int ARMCI_Malloc_group(void *ptr_arr[], armci_size_t bytes, ARMCI_Group *group) { void *ptr; int grp_me, grp_nproc; ARMCI_PR_DBG("enter",0); ARMCI_Group_size(group, &grp_nproc); ARMCI_Group_rank(group, &grp_me); if(DEBUG_)fprintf(stderr,"%d (grp_id=%d) bytes in armci_malloc_group %d\n", armci_me, grp_me, (int)bytes); #ifdef REGION_ALLOC armci_region_shm_malloc_grp(ptr_arr, bytes, group); #else #ifdef USE_MALLOC if(grp_nproc == 1) { ptr = kr_malloc((size_t) bytes, &ctx_localmem); if(bytes) if(!ptr) armci_die("armci_malloc_group:malloc 1 failed",(int)bytes); ptr_arr[grp_me] = ptr; ARMCI_PR_DBG("exit",0); return (0); } #endif if( ARMCI_Uses_shm_grp(group) ) { armci_shmem_malloc_group(ptr_arr,bytes,group); } else { /* on distributed-memory systems just malloc & collect all addresses */ ptr = kr_malloc(bytes, &ctx_localmem); if(bytes) if(!ptr) armci_die("armci_malloc:malloc 2 failed",bytes); bzero((char*)ptr_arr,grp_nproc*sizeof(void*)); ptr_arr[grp_me] = ptr; /* now combine individual addresses into a single array */ armci_exchange_address_grp(ptr_arr, grp_nproc, group); # ifdef ALLOW_PIN # if 0 /* ????? check ?????? */ armci_die("armci_malloc_group: Not yet implemented", 0); armci_global_region_exchange(ptr, (long) bytes); # endif # endif } #endif ARMCI_PR_DBG("exit",0); return(0); } /*\ * Just a wrapper on ARMCI_Malloc_group to keep old code from breaking \*/ int ARMCI_Malloc_group_memdev(void *ptr_arr[], armci_size_t bytes, ARMCI_Group *group, const char *device) { return ARMCI_Malloc_group(ptr_arr, bytes, group); } /*\ shared memory is released to kr_malloc only on process 0 * with data server malloc cannot be used \*/ int ARMCI_Free_group(void *ptr, ARMCI_Group *group) { int grp_me, grp_nproc, grp_master, grp_clus_me; armci_grp_attr_t *grp_attr=ARMCI_Group_getattr(group); ARMCI_PR_DBG("enter",0); if(!ptr)return 1; ARMCI_Group_size(group, &grp_nproc); ARMCI_Group_rank(group, &grp_me); if(grp_me == MPI_UNDEFINED) { /* check if the process is in this group */ armci_die("armci_malloc_group: process is not a member in this group", armci_me); } /* get the group cluster info */ grp_clus_me = grp_attr->grp_clus_me; grp_master = grp_attr->grp_clus_info[grp_clus_me].master; #ifdef REGION_ALLOC kr_free(ptr, &ctx_region_shmem); #else # if (defined(SYSV) || defined(WIN32) || defined(MMAP)) && !defined(NO_SHM) # ifdef USE_MALLOC if(grp_nproc > 1) # endif if(ARMCI_Uses_shm_grp(group)){ if(grp_me == grp_master) { # ifdef RMA_NEEDS_SHMEM Free_Shmem_Ptr(0,0,ptr); # else if(armci_clus_info[armci_clus_me].nslave>1) Free_Shmem_Ptr(0,0,ptr); else kr_free(ptr, &ctx_localmem); # endif } ptr = NULL; ARMCI_PR_DBG("exit",0); return 0; } # endif kr_free(ptr, &ctx_localmem); #endif /* ifdef REGION_ALLOC */ ptr = NULL; ARMCI_PR_DBG("exit",0); return 0; } /* ***************** End Group Collective Memory Allocation ******************/ #endif /* ************** Begin Non-Collective Memory Allocation ****************** * Prototype similar to SysV shared memory. */ /** * CHECK: On Altix we are forced to use SysV as shmalloc is collective. We * may use a preallocated shmalloc memory, however, it may NOT still solve * our problem... * NOTE: "int memflg" option for future optimiztions. */ void PARMCI_Memget(size_t bytes, armci_meminfo_t *meminfo, int memflg) { void *myptr=NULL; void *armci_ptr=NULL; /* legal ARCMI ptr used in ARMCI data xfer ops*/ size_t size = bytes; if(size<=0) armci_die("PARMCI_Memget: size must be > 0", (int)size); if(meminfo==NULL) armci_die("PARMCI_Memget: Invalid arg #2 (NULL ptr)",0); if(memflg!=0) armci_die("PARMCI_Memget: Invalid memflg", memflg); if( !ARMCI_Uses_shm() ) { armci_ptr = myptr = kr_malloc(size, &ctx_localmem); if(size) if(!myptr) armci_die("PARMCI_Memget failed", (int)size); /* fill the meminfo structure */ meminfo->armci_addr = armci_ptr; meminfo->addr = myptr; meminfo->size = size; meminfo->cpid = armci_me; /* meminfo->attr = NULL; */ } else { armci_shmem_memget(meminfo, size); } #ifdef ALLOW_PIN # if 0 /* disabled for now. May not go thru' the fast zero-copy path */ if(armci_nclus>1) armci_global_region_exchange(meminfo->addr, (long)size_arr[armci_me]); # endif #endif if(DEBUG_){ printf("%d: PARMCI_Memget: addresses server=%p myptr=%p bytes=%ld\n", armci_me, meminfo->armci_addr, meminfo->addr, (long)bytes); fflush(stdout); } } void* PARMCI_Memat(armci_meminfo_t *meminfo, long offset) { void *ptr=NULL; if(meminfo==NULL) armci_die("PARMCI_Memat: Invalid arg #1 (NULL ptr)",0); if(meminfo->cpid==armci_me) { ptr = meminfo->addr; return ptr; } if( !ARMCI_Uses_shm()) { ptr = meminfo->addr; } else { ptr = armci_shmem_memat(meminfo); ptr = ((char*)ptr) + offset; } if(DEBUG_) { printf("%d:PARMCI_Memat: attached addr mptr=%p size=%ld\n", armci_me, ptr, (long)meminfo->size); fflush(stdout); } return ptr; } void ARMCI_Memdt(armci_meminfo_t *meminfo, long offset) { /** * Do nothing. May be we need to have reference counting in future. This * is to avoid the case of dangling pointers when the creator of shm * segment calls Memctl and other processes are still attached to this * segment */ } void ARMCI_Memctl(armci_meminfo_t *meminfo) { if(meminfo==NULL) armci_die("PARMCI_Memget: Invalid arg #2 (NULL ptr)",0); /* only the creator can delete the segment */ if(meminfo->cpid == armci_me) { if( !ARMCI_Uses_shm() ) { void *ptr = meminfo->addr; kr_free(ptr, &ctx_localmem); } else { armci_shmem_memctl(meminfo); } } meminfo->addr = NULL; meminfo->armci_addr = NULL; /* if(meminfo->attr!=NULL) free(meminfo->attr); */ } /* ***************** End Non-Collective Memory Allocation ******************/ ga-5.9.2/armci/src/memory/shmalloc.c000066400000000000000000000047721500715745200172760ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: shmalloc.c,v 1.10 2002-06-20 23:34:17 vinod Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STRING_H # include #endif #include "armcip.h" #include "message.h" #include "kr_malloc.h" static long *offset_arr; void armci_shmalloc_exchange_offsets(context_t *ctx_local) { void **ptr_arr; void *ptr; armci_size_t bytes = 128; int i; ptr_arr = (void**)malloc(armci_nproc*sizeof(void*)); offset_arr = (long*) malloc(armci_nproc*sizeof(long)); if(!ptr_arr || !offset_arr) armci_die("armci_shmalloc_get_offsets: malloc failed", 0); /* get memory with same size on all procs */ ptr = kr_malloc(bytes, ctx_local); if(!ptr) armci_die("armci_shmalloc_get_offsets: kr_malloc failed",bytes); bzero((char*)ptr_arr,armci_nproc*sizeof(void*)); ptr_arr[armci_me] = ptr; /* now combine individual addresses into a single array */ armci_exchange_address(ptr_arr, armci_nproc); /* identify offets */ for (i=0; i #endif #if HAVE_SYS_IPC_H # include #endif #if HAVE_SYS_SHM_H # include #endif #if HAVE_SYS_PARAM_H # include #endif #if HAVE_ERRNO_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #include "armci_shmem.h" #include "kr_malloc.h" #include "shmlimit.h" #include "message.h" #include "armcip.h" #define SHM_UNIT (1024) /* Need to determine the max shmem segment size. There are 2 alternatives: * 1. use predefined SHMMAX if available or set some reasonable values, or * 2. trial-and-error search for a max value (default) * case a) fork a process to determine shmmax size (more accurate) * case b) search w/o forking until success (less accurate) */ #if defined(VAPI) || defined(SOLARIS) # define SHMMAX_SEARCH_NO_FORK #endif #if defined(AIX) || defined(SHMMAX_SEARCH_NO_FORK) # define NO_SHMMAX_SEARCH #endif /* Limits for the largest shmem segment are in Kilobytes to avoid passing * Gigavalues to kr_malloc */ #define _SHMMAX 4*1024 #if defined(SUN)||defined(SOLARIS) # undef _SHMMAX # define _SHMMAX (1024) /* memory in KB */ #elif defined(AIX) # undef _SHMMAX # define _SHMMAX ((unsigned long)512*1024) #elif defined(__FreeBSD__) # undef _SHMMAX # define _SHMMAX ((unsigned long)3*1024) #elif defined(LINUX) # if !defined(SHMMAX) /* Red Hat does not define SHMMAX */ # undef _SHMMAX # if defined(__sparc__) || defined(__powerpc__) # define _SHMMAX ((unsigned long)16*1024) # elif defined(__alpha__) # define _SHMMAX ((unsigned long)4072) # else /* Intel */ # define _SHMMAX ((unsigned long)32*1024) # endif # endif #elif defined(SHMMAX) # undef _SHMMAX # define _SHMMAX (((unsigned long)SHMMAX)>>10) #endif static unsigned long MinShmem_per_core = 0; static unsigned long MaxShmem_per_core = 0; static unsigned long MinShmem = _SHMMAX; static unsigned long MaxShmem = MAX_REGIONS*_SHMMAX; static context_t ctx_shmem; /* kr_malloc context */ static context_t *ctx_shmem_global; /* kr_malloc context stored in shmem */ static int create_call=0; #ifdef SHMMAX_SEARCH_NO_FORK static char *ptr_search_no_fork = (char*)0; static int id_search_no_fork=0; #endif #ifdef LINUX #define CLEANUP_CMD(command) sprintf(command,"/usr/bin/ipcrm shm %d",id); #else #define CLEANUP_CMD(command) sprintf(command,"/usr/bin/ipcrm -m %d",id); #endif #endif /*\ A wrapper to shmget. Just to be sure that ID is not 0. \*/ static int armci_shmget(size_t size,char *from) { int id; id = shmget(IPC_PRIVATE, size, (IPC_CREAT | 00600)); /*attaching with id 0 somehow fails (Seen on pentium4+linux24+gm163) *so if id=0, shmget again. */ while(id==0){ /* free id=0 and get a new one */ if(shmctl((int)id,IPC_RMID,(struct shmid_ds *)NULL)) { fprintf(stderr,"id=%d \n",id); armci_die("allocate: failed to _delete_ shared region ",id); } id = shmget(IPC_PRIVATE, size, (IPC_CREAT | 00600)); } if(DEBUG_){ printf("\n%d:armci_shmget sz=%ld caller=%s id=%d\n",armci_me,(long)size, from,id); fflush(stdout); } return(id); } /*\ test is a shared memory region of a specified size can be allocated * return 0 (no) or 1 (yes) \*/ int armci_test_allocate(long size) { char *ptr; int id = armci_shmget((size_t)size,"armci_test_allocate"); if (id <0) return 0; /* attach to segment */ ptr = shmat(id, (char *) NULL, 0); /* delete segment id */ if(shmctl( id, IPC_RMID, (struct shmid_ds *)NULL)) fprintf(stderr,"failed to remove shm id=%d\n",id); /* test pointer */ if (((long)ptr) == -1L) return 0; else return 1; } #ifdef SHMMAX_SEARCH_NO_FORK /*\ try to allocate a shared memory region of a specified size; return pointer \*/ static int armci_shmalloc_try(long size) { char *ptr; int id = armci_shmget((size_t) size,"armci_shmalloc_try"); if (id <0) return 0; /* attach to segment */ ptr = shmat(id, (char *) NULL, 0); /* test pointer */ if (((long)ptr) == -1L) return 0; ptr_search_no_fork = ptr; id_search_no_fork = id; return 1; } #endif /* parameters that define range and granularity of search for shm segment size * UBOUND is chosen to be < 2GB to avoid overflowing on 32-bit systems * smaller PAGE gives more accurate results but with more search steps * LBOUND is set to minimum amount for our purposes * change UBOUND=512MB if you need larger arrays than 512 MB */ #define PAGE (16*65536L) #define LBOUND 1048576L #define UBOUND 512*LBOUND #define ARMCI_STRINGIFY(str) #str #define ARMCI_CONCAT(str) strL #ifndef ARMCI_DEFAULT_SHMMAX_UBOUND #define ARMCI_DEFAULT_SHMMAX_UBOUND 8192 #endif static long get_user_shmmax() { char *uval; int x=0; uval = getenv("ARMCI_DEFAULT_SHMMAX"); if(uval != NULL){ sscanf(uval,"%d",&x); if(x<1 || x> ARMCI_DEFAULT_SHMMAX_UBOUND){ fprintf(stderr, "incorrect ARMCI_DEFAULT_SHMMAX should be <1," ARMCI_STRINGIFY(ARMCI_DEFAULT_SHMMAX) ">mb and 2^N Found=%d\n",x); x=0; } } return ((long)x)*1048576L; /* return value in bytes */ } /*\ determine the max shmem segment size using bisection \*/ int armci_shmem_test() { long x; int i,rc; long upper_bound=UBOUND; long lower_bound=0; x = get_user_shmmax(); if(!x) x = upper_bound; else upper_bound =x; if(DEBUG_){printf("%d: x = %ld upper_bound=%ld\n",armci_me, x, upper_bound); fflush(stdout);} for(i=1;;i++){ long step; rc = armci_test_allocate(x); if(DEBUG_) printf("%d:test %d size=%ld bytes status=%d\n",armci_me,i,x,rc); if(rc){ lower_bound = x; step = (upper_bound -x)>>1; if(step < PAGE) break; x += step; }else{ upper_bound = x; step = (x-lower_bound)>>1; if(step>=20; x <<=20; } if(!lower_bound){ /* try if can get LBOUND - necessary if search starts from UBOUND */ lower_bound=LBOUND; rc = armci_test_allocate(lower_bound); if(!rc) return(0); } if(DEBUG_) printf("%ld bytes segment size, %d calls \n",lower_bound,i); return (int)( lower_bound>>20); /* return shmmax in mb */ } #ifdef SHMMAX_SEARCH_NO_FORK /*\ determine the max shmem segment size by halving \*/ static int armci_shmem_test_no_fork() { long x; int i,rc; long lower_bound=_SHMMAX*SHM_UNIT; #define UBOUND_SEARCH_NO_FORK (256*SHM_UNIT*SHM_UNIT) x = get_user_shmmax(); if(!x) x = UBOUND_SEARCH_NO_FORK; for(i=1;;i++){ rc = armci_shmalloc_try(x); if(DEBUG_) printf("%d:test by halving size=%ld bytes rc=%d\n",armci_me,x,rc); if(rc){ lower_bound = x; break; }else{ x >>= 1 ; if(x>20); /* return shmmax in mb */ } #endif /* Create shared region to store kr_malloc context in shared memory */ void armci_krmalloc_init_ctxshmem() { void *myptr=NULL; long idlist[SHMIDLEN]; long size; int offset = sizeof(void*)/sizeof(int); /* to store shared memory context and myptr */ size = SHMEM_CTX_MEM; if(armci_me == armci_master ){ myptr = Create_Shared_Region(idlist+1,size,idlist); if(!myptr && size>0 ) armci_die("armci_krmalloc_init_ctxshmem: could not create", (int)(size>>10)); if(size) *(volatile void**)myptr = myptr; if(DEBUG_){ printf("%d:armci_krmalloc_init_ctxshmem addr mptr=%p ref=%p size=%ld\n", armci_me, myptr, *(void**)myptr, size); fflush(stdout); } /* Bootstrapping: allocate storage for ctx_shmem_global. NOTE:there is offset,as master places its address at begining for others to see */ ctx_shmem_global = (context_t*) ( ((int*)myptr)+offset ); *ctx_shmem_global = ctx_shmem; /*master copies ctx into shared region */ } /* broadcast shmem id to other processes on the same cluster node */ armci_msg_clus_brdcst(idlist, SHMIDLEN*sizeof(long)); if(armci_me != armci_master){ myptr=(double*)Attach_Shared_Region(idlist+1,size,idlist[0]); if(!myptr)armci_die("armci_krmalloc_init_ctxshmem: could not attach", (int)(size>>10)); /* now every process in a SMP node needs to find out its offset * w.r.t. master - this offset is necessary to use memlock table */ if(size) armci_set_mem_offset(myptr); if(DEBUG_){ printf("%d:armci_krmalloc_init_ctxshmem attached addr mptr=%p ref=%p size=%ld\n", armci_me,myptr, *(void**)myptr,size); fflush(stdout); } /* store context info */ ctx_shmem_global = (context_t*) ( ((int*)myptr)+offset ); if(DEBUG_){ printf("%d:armci_krmalloc_init_ctxshmem: shmid=%d off=%ld size=%ld\n", armci_me, ctx_shmem_global->shmid, ctx_shmem_global->shmoffset, (long)ctx_shmem_global->shmsize); fflush(stdout); } } } void armci_shmem_init() { if(armci_me == armci_master){ #if !defined(NO_SHMMAX_SEARCH) || defined(SHMMAX_SEARCH_NO_FORK) # ifdef SHMMAX_SEARCH_NO_FORK int x = armci_shmem_test_no_fork(); # else int x = armci_child_shmem_init(); # endif if(x<1) armci_die("no usable amount of shared memory available: only got \n", (int)LBOUND); if(DEBUG_){ printf("%d:shmem_init: %d mbytes max segment size\n",armci_me,x);fflush(stdout);} MinShmem = (long)(x<<10); /* make sure it is in kb: mb <<10 */ MaxShmem = MAX_REGIONS*MinShmem; # ifdef REPORT_SHMMAX printf("%d using x=%d SHMMAX=%ldKB\n", armci_me,x, MinShmem); fflush(stdout); # endif #else /* nothing to do here - limits were given */ #endif } armci_krmalloc_init_ctxshmem(); if(DEBUG_)printf("%d: out of shmem_init\n",armci_me); } void armci_set_shmem_limit_per_node(int nslaves) { if (MaxShmem_per_core > 0) MaxShmem = nslaves*MaxShmem_per_core; if (MinShmem_per_core > 0) MinShmem = nslaves*MinShmem_per_core; } void armci_set_shmem_limit_per_core(unsigned long shmemlimit) { MaxShmem_per_core = (shmemlimit + SHM_UNIT - 1)/SHM_UNIT; MinShmem_per_core = (shmemlimit + SHM_UNIT - 1)/SHM_UNIT; } /*\ application can reset the upper limit (bytes) for memory allocation \*/ void armci_set_shmem_limit(unsigned long shmemlimit) { unsigned long kbytes; kbytes = (shmemlimit + SHM_UNIT -1)/SHM_UNIT; if(MaxShmem > kbytes) MaxShmem = kbytes; if(MinShmem > kbytes) MinShmem = kbytes; } static void shmem_errmsg(size_t size) { long sz=(long)size; printf("******************* ARMCI INFO ************************\n"); printf("The application attempted to allocate a shared memory segment "); printf("of %ld bytes in size. This might be in addition to segments ",sz); printf("that were allocated succesfully previously. "); printf("The current system configuration does not allow enough "); printf("shared memory to be allocated to the application.\n"); printf("This is most often caused by:\n1) system parameter SHMMAX "); printf("(largest shared memory segment) being too small or\n"); printf("2) insufficient swap space.\n"); printf("Please ask your system administrator to verify if SHMMAX "); printf("matches the amount of memory needed by your application and "); printf("the system has sufficient amount of swap space. "); printf("Most UNIX systems can be easily reconfigured "); printf("to allow larger shared memory segments,\n"); printf("see https://hpc.pnl.gov/globalarrays/support.shtml\n"); printf("In some cases, the problem might be caused by insufficient swap space.\n"); printf("*******************************************************\n"); } static struct shm_region_list{ char *addr; long id; long sz; long attached; }region_list[MAX_REGIONS]; static int alloc_regions=0; static long occup_blocks=0; /* Terminology * region - actual piece of shared memory allocated from OS * block - a part of allocated shmem that is given to the requesting process */ /* Now, the machines where shm segments are not glued together */ static int last_allocated=-1; unsigned long armci_max_region() { return MinShmem; } int find_regions(char *addrp, long* id, int *region) { int nreg, reg; if(last_allocated!=-1){ reg=last_allocated; last_allocated = -1; } else{ for(reg=-1,nreg=0;nreg= region_list[nreg].addr && addrp < (region_list[nreg].addr + region_list[nreg].sz)) { reg = nreg; break; } } if(reg == -1) armci_die("find_regions: failed to locate shared region", 0L); } *region = reg; *id = region_list[reg].id; return 1; } /* returns the shmem info based on the addr */ int armci_get_shmem_info(char *addrp, int* shmid, long *shmoffset, size_t *shmsize) { int region; long id; find_regions(addrp, &id, ®ion); *shmid = id; *shmoffset = (long)(addrp - region_list[region].addr); *shmsize = region_list[region].sz; return 1; } long armci_shm_reg_size(int i, long id) { if(i<0 || i>= MAX_REGIONS)armci_die("armci_shmem_reg_size: bad i",i); return region_list[i].sz; } void* armci_shm_reg_ptr(int i) { if(i<0 || i>= MAX_REGIONS)armci_die("armci_shmem_reg_ptr: bad i",i); return (void *)region_list[i].addr; } Header *armci_get_shmem_ptr(int shmid, long shmoffset, size_t shmsize) { /* returns, address of the shared memory region based on shmid, offset. * (i.e. return_addr = stating address of shmid + offset)*/ long idlist[SHMIDLEN]; Header *p = NULL; idlist[1] = (long)shmid; idlist[0] = shmoffset; idlist[IDLOC+1] = shmsize; /* CHECK : idlist in CreateShmem????*/ if(!(p=(Header*)Attach_Shared_Region(idlist+1, shmsize, idlist[0]))) armci_die("kr_malloc:could not attach",(int)(p->s.shmsize>>10)); #if DEBUG_ printf("%d: armci_get_shmem_ptr: %d %ld %ld %p\n", armci_me, idlist[1], idlist[0], shmsize, p); fflush(stdout); #endif return p; } char *Attach_Shared_Region(id, size, offset) long *id, offset, size; { int reg, found, shmflag=0; static char *temp; if(alloc_regions>=MAX_REGIONS) armci_die("Attach_Shared_Region: to many regions ",0); if(DEBUG_){ printf("%d:AttachSharedRegion %d:size=%ld id=%ld\n", armci_me, create_call++, size,*id); fflush(stdout); } /* under Linux we can get valid id=0 */ #ifndef LINUX if(!*id) armci_die("Attach_Shared_Region: shmem ID=0 ",(int)*id); #endif /* first time needs to initialize region_list structure */ if(!alloc_regions){ for(reg=0;reg= MAX_REGIONS) armci_die("Create_Shared_Region:allocate:too many regions allocated ",0); last_allocated = alloc_regions; #ifdef SHMMAX_SEARCH_NO_FORK if (ptr_search_no_fork){ temp = ptr_search_no_fork; id = id_search_no_fork; ptr_search_no_fork = (char*)0; /* do not look at it again */ }else #endif { if ( (id = armci_shmget(sz,"armci_allocate")) < 0 ) { fprintf(stderr,"id=%d size=%ld\n",id, size); shmem_errmsg(sz); armci_die("allocate: failed to create shared region ",id); } if ( (long)( (temp = shmat(id, pref_addr, shmflag))) == -1L){ char command[64]; CLEANUP_CMD(command); if(system(command) == -1) printf("Please clean shared memory (id=%d): see man ipcrm\n",id); armci_die("allocate: failed to attach to shared region id=",id); } if(DEBUG_){ printf("%d:allocate:attach:id=%d paddr=%p size=%ld\n",armci_me,id,temp,size); fflush(stdout); } #if !defined(AIX) /* delete segment id so that OS cleans it when all attached processes are gone */ if(shmctl( id, IPC_RMID, (struct shmid_ds *)NULL)) fprintf(stderr,"failed to remove shm id=%d\n",id); #endif } POST_ALLOC_CHECK(temp,sz); region_list[alloc_regions].addr = temp; region_list[alloc_regions].id = id; region_list[alloc_regions].attached=1; region_list[alloc_regions].sz=sz; alloc_regions++; if(DEBUG2_){ printf("%d:allocate:id=%d addr=%p size=%ld\n",armci_me,id,temp,size); fflush(stdout); } #ifdef ALLOW_PIN armci_region_register_shm(temp, size); #endif return (void*) (temp); } /******************** common code for the two versions *********************/ /*\ Allocate a block of shared memory - called by master process \*/ char *Create_Shared_Region(long *id, long size, long *offset) { char *temp; int reg, refreg=0,nreg; if(alloc_regions>=MAX_REGIONS) armci_die("Create_Shared_Region: to many regions ",0); if(DEBUG_){ printf("%d:CreateSharedRegion %d:size=%ld\n",armci_me,create_call++,size); fflush(stdout); } /*initialization: 1st allocation request */ if(!alloc_regions){ for(reg=0;reg #endif #if HAVE_SYS_WAIT_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_ERRNO_H # include #endif #if HAVE_SIGNAL_H # include #endif #include "shmlimit.h" #include "armci.h" #include "message.h" #define DEBUG_ 0 void (*armci_sig_chld_orig)(); static int status=0; int armci_shmlimit_caught_sigchld=0; static void SigChldHandler(sig) int sig; { #ifdef DISABLED int pid; pid = wait(&status); #endif armci_shmlimit_caught_sigchld=1; } static void TrapSigChld() { if ( (armci_sig_chld_orig = signal(SIGCHLD, SigChldHandler)) == SIG_ERR) armci_die("TrapSigChld: error from signal setting SIGCHLD",0); } static void RestoreSigChld() { if ( signal(SIGCHLD, armci_sig_chld_orig) == SIG_ERR) armci_die("Restore_SigChld: error from restoring signal SIGChld",0); } int armci_child_shmem_init() { pid_t pid; int x; #ifdef PIPE_AFTER_FORK_BUG int i; #endif int y; int fd[2]; int val; if(pipe(fd)==-1) armci_die("armci shmem_test pipe failed",0); TrapSigChld(); if ( (pid = fork() ) < 0) armci_die("armci shmem_test fork failed", (int)pid); else if(pid == 0){ x= armci_shmem_test(); #ifdef PIPE_AFTER_FORK_BUG /* due to a bug in OSF1 V4.0/1229/alpha first item written gets hosed*/ for(i=0;i<2;i++) #endif val=write(fd[1],&x,sizeof(int)); if(val < 0 || (size_t)val < sizeof(int)) armci_die("armci shmem_test: write failed",0); _exit(0); }else{ pid_t rc; #ifdef PIPE_AFTER_FORK_BUG /* due to a bug in OSF1 V4.0/1229/alpha first item read is garbage */ for(i=0;i<2;i++) #endif val=read(fd[0],&y,sizeof(int)); if(val < 0 || (size_t)val < sizeof(int)) armci_die("armci shmem_test: read failed",val); again: rc = wait (&status); if(rc == -1 && errno == EINTR) goto again; if (!WIFEXITED(status)) armci_die("ARMCI: child did not return rc",0); x = WEXITSTATUS(status); } /* restore previous signal handler */ RestoreSigChld(); close(fd[0]); close(fd[1]); #if DEBUG_ printf("%d:in parent: x=%d y=%d\n",armci_me,x,y);fflush(stdout);sleep(1); #endif return y; } ga-5.9.2/armci/src/memory/winshmem.c000066400000000000000000000367251500715745200173260ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: winshmem.c,v 1.20 2005-09-29 21:30:10 d3h325 Exp $ */ /* WIN32 & Posix SysV-like shared memory allocation and management * * * Interface: * ~~~~~~~~~ * char *Create_Shared_Region(long *idlist, long *size, long *offset) * . to be called by just one process. * . calls kr_malloc, malloc-like * memory allocator from K&R.kr_malloc inturn calls armci_allocate() that * does shmget() and shmat(). * . idlist might be just a pointer to integer or a true array in the * MULTIPLE_REGIONS versions (calling routine has to take cere of it) * char *Attach_Shared_Region(long *idlist, long *size, long *offset) * . called by any other process to attach to existing shmem region or * if attached just calculate the address based on the offset * . has to be called after shmem region was created * void Free_Shmem_Ptr(long id, long size, char* addr) * . called ONLY by the process that created shmem region (id) to return * pointer to kr_malloc (shmem is not destroyed) * long Delete_All_Regions() * . destroys all shared memory regions * . can be called by any process assuming that all processes attached * to alllocated shmem regions * . needs to by called by cleanup procedure(s) */ #define DEBUG 0 #define DEBUG0 0 #if HAVE_STDIO_H # include #endif #ifdef WIN32 # include # include # define GETPID _getpid #elif defined(MMAP) # if HAVE_FCNTL_H # include # endif # if HAVE_UNISTD_H # include # endif # if HAVE_SYS_STAT_H # include # endif # if HAVE_SYS_MMAN_H # include # endif typedef int HANDLE; typedef void* LPVOID; # define GETPID getpid #else # ifndef _POSIX_C_SOURCE # define _POSIX_C_SOURCE 199309L # endif # if HAVE_FCNTL_H # include # endif # if HAVE_UNISTD_H # include # endif # if HAVE_SYS_STAT_H # include # endif # if HAVE_SYS_MMAN_H # include # endif typedef int HANDLE; typedef void* LPVOID; # define GETPID getpid #endif #if HAVE_ASSERT_H # include #endif #include "kr_malloc.h" #include "armci_shmem.h" #include "armcip.h" #include "armci.h" #define SHM_UNIT (1024) /* default unit for shared memory allocation in KB! */ #if defined(WIN32) # define _SHMMAX 32678 #elif defined(MACX) # define _SHMMAX 64*1024 #else # define _SHMMAX 2*32678 #endif #define SET_MAPNAME(id) sprintf(map_fname,"/tmp/ARMCIshmem.%d.%d",parent_pid,(id)) /*********************** global data structures ****************************/ /* Terminology * region - actual piece of shared memory allocated from OS * block - a part of allocated shmem that is given to the requesting process */ /* array holds handles and addreses for each shmem region*/ static struct shm_region_list{ char *addr; HANDLE id; long size; }region_list[MAX_REGIONS]; static char map_fname[64]; static int alloc_regions=0; /* counter to identify mapping handle */ static int last_allocated=0; /* counter trailing alloc_regions by 0/1 */ /* Min and Max amount of aggregate memory that can be allocated */ static unsigned long MinShmem_per_core = 0; static unsigned long MaxShmem_per_core = 0; static unsigned long MinShmem = _SHMMAX; static unsigned long MaxShmem = MAX_REGIONS*_SHMMAX; static context_t ctx_winshmem; /* kr_malloc context */ static context_t *ctx_winshmem_global;/*for processor groups,kr_malloc context is stored in shared memory */ static int parent_pid=-1; /* process id of process 0 "parent" */ extern int armci_me; /* Create shared region to store kr_malloc context in shared memory */ void armci_krmalloc_init_ctxwinshmem() { void *myptr=NULL; long idlist[SHMIDLEN]; long size; int offset = sizeof(void*)/sizeof(int); /* to store shared memory context and myptr */ size = SHMEM_CTX_MEM; if(armci_me == armci_master ){ myptr = Create_Shared_Region(idlist+1,size,idlist); if(!myptr && size>0 ) armci_die("armci_krmalloc_init_ctxwinshmem: could not create", (int)(size>>10)); if(size) *(volatile void**)myptr = myptr; if(DEBUG){ printf("%d:armci_krmalloc_init_ctxwinshmem addr mptr=%p ref=%p size=%ld\n", armci_me, myptr, *(void**)myptr, size); fflush(stdout); } /* Bootstrapping: allocate storage for ctx_winshmem_global. NOTE:there is offset,as master places its addr at begining for others to see */ ctx_winshmem_global = (context_t*) ( ((int*)myptr)+offset ); *ctx_winshmem_global=ctx_winshmem;/*master copies ctx into shared rgn*/ } /* broadcast shmem id to other processes on the same cluster node */ armci_msg_clus_brdcst(idlist, SHMIDLEN*sizeof(long)); if(armci_me != armci_master){ myptr=(double*)Attach_Shared_Region(idlist+1,size,idlist[0]); if(!myptr)armci_die("armci_krmalloc_init_ctxwinshmem: could not attach", (int)(size>>10)); /* now every process in a SMP node needs to find out its offset * w.r.t. master - this offset is necessary to use memlock table */ if(size) armci_set_mem_offset(myptr); if(DEBUG){ printf("%d:armci_krmalloc_init_ctxwinshmem attached addr mptr=%p ref=%p size=%ld\n", armci_me,myptr, *(void**)myptr,size); fflush(stdout); } /* store context info */ ctx_winshmem_global = (context_t*) ( ((int*)myptr)+offset ); if(DEBUG){ printf("%d:armci_krmalloc_init_ctxwinshmem:shmid=%d off=%ld size=%ld\n", armci_me, ctx_winshmem_global->shmid,ctx_winshmem_global->shmoffset, (long)ctx_winshmem_global->shmsize); fflush(stdout); } } } /* not done here yet */ void armci_shmem_init() { armci_krmalloc_init_ctxwinshmem(); } unsigned long armci_max_region() { return MinShmem; } void armci_set_shmem_limit_per_node(int nslaves) { if (MaxShmem_per_core > 0) MaxShmem = nslaves*MaxShmem_per_core; if (MinShmem_per_core > 0) MinShmem = nslaves*MinShmem_per_core; } void armci_set_shmem_limit_per_core(unsigned long shmemlimit) { MaxShmem_per_core = (shmemlimit + SHM_UNIT - 1)/SHM_UNIT; MinShmem_per_core = (shmemlimit + SHM_UNIT - 1)/SHM_UNIT; } /*\ application can reset the upper limit for memory allocation \*/ void armci_set_shmem_limit(unsigned long shmemlimit) /* comes in bytes */ { unsigned long kbytes; kbytes = (shmemlimit + SHM_UNIT -1)/SHM_UNIT; if(MaxShmem > kbytes) MaxShmem = kbytes; if(MinShmem > kbytes) MinShmem = kbytes; } void Delete_All_Regions() { int reg; for(reg = 0; reg < alloc_regions; reg++){ if(region_list[reg].addr != (char*)0){ # if defined(WIN32) UnmapViewOfFile(region_list[reg].addr); CloseHandle(region_list[reg].id); # else munmap(region_list[reg].addr, region_list[reg].size); SET_MAPNAME(reg); (void)unlink(map_fname); # endif region_list[reg].addr = (char*)0; } } } /*\ only process that created shared region returns the pointer to kr_malloc \*/ void Free_Shmem_Ptr(long id, long size, char* addr) { kr_free(addr, ctx_winshmem_global); } char *armci_get_core_from_map_file(int exists, long size) { LPVOID ptr; HANDLE h_shm_map; SET_MAPNAME(alloc_regions); region_list[alloc_regions].addr = (char*)0; #if defined(WIN32) h_shm_map = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (int)size, map_fname); if(h_shm_map == NULL) return NULL; if(exists){ /* get an error code when mapping should exist */ if (GetLastError() != ERROR_ALREADY_EXISTS){ CloseHandle(h_shm_map); fprintf(stderr,"map handle does not exist (attach)\n"); return NULL; }else { /* OK */ } } else { /* problem if mapping it should not be there */ if (GetLastError() == ERROR_ALREADY_EXISTS){ CloseHandle(h_shm_map); fprintf(stderr,"map handle already exists (create)\n"); return NULL; } } /* now map file into process address space */ ptr = MapViewOfFile(h_shm_map, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0); if((char*)ptr == NULL){ CloseHandle(h_shm_map); h_shm_map = INVALID_HANDLE_VALUE; } #elif defined(MMAP) && !defined(MACX) if(exists){ if(size < MinShmem*SHM_UNIT) size = MinShmem*SHM_UNIT; h_shm_map = open(map_fname, O_RDWR, S_IRWXU); if(h_shm_map <0) return NULL; }else{ (void)unlink(map_fname); /* sanity cleanup */ h_shm_map = open(map_fname, O_CREAT|O_RDWR, S_IRWXU); if(h_shm_map <0) return NULL; if(ftruncate(h_shm_map,size) < 0) return NULL; } ptr = mmap((caddr_t)0, (size_t)size, PROT_READ|PROT_WRITE, MAP_SHARED, h_shm_map, 0); close(h_shm_map); h_shm_map = -1; #elif defined(MACX) if(exists){ if(size < MinShmem*SHM_UNIT) size = MinShmem*SHM_UNIT; h_shm_map = shm_open(map_fname, O_RDWR, S_IRWXU); if(h_shm_map == -1) return NULL; }else{ (void)shm_unlink(map_fname); /* sanity cleanup */ h_shm_map = shm_open(map_fname, O_CREAT|O_RDWR, S_IRWXU); if(h_shm_map<0) perror("open"); if(h_shm_map == -1) return NULL; if(ftruncate(h_shm_map,size) < 0){ perror("ftruncate"); return NULL; } } ptr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, h_shm_map, 0L); if((long)ptr <0){ perror("mmap"); return NULL; } close(h_shm_map); h_shm_map = -1; #else if(exists){ h_shm_map = shm_open(map_fname, O_RDWR, S_IRWXU); if(h_shm_map == -1) return NULL; }else{ (void)shm_unlink(map_fname); /* sanity cleanup */ h_shm_map = shm_open(map_fname, O_CREAT|O_RDWR, S_IRWXU); if(h_shm_map) perror("shm_open"); if(h_shm_map == -1) return NULL; if(ftruncate(h_shm_map,size) < 0) return NULL; } ptr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, h_shm_map, 0L); close(h_shm_map); h_shm_map = -1; #endif /* save file handle in the array to close it in the future */ region_list[alloc_regions].id = h_shm_map; if(DEBUG0){printf("%d: got ptr=%p bytes=%ld mmap\n",armci_me,ptr,size); fflush(stdout); } region_list[alloc_regions].addr = (char*)ptr; region_list[alloc_regions].size = size; return((char*)ptr); } int find_regions(char *addrp, long* id, int *region) { int nreg, reg; if(last_allocated!=-1){ reg=last_allocated; last_allocated = -1; } else{ for(reg=0,nreg=0;nreg addrp )break; reg = nreg; } } *region = reg; *id = reg; return 1; } /*\ function called by shared memory allocator (kr_malloc) \*/ char *armci_allocate(size_t size) { char *ptr; if(alloc_regions>= MAX_REGIONS) armci_die("max alloc regions exceeded", alloc_regions); ptr = armci_get_core_from_map_file( 0, (long)size); if(ptr !=NULL) alloc_regions++; if(DEBUG)printf("%d:got more core %lx %ld this was %d segment allocated\n",armci_me,(unsigned long)ptr, (long)size, alloc_regions); return ptr; } char* Create_Shared_Region(long idlist[], long size, long *offset) { char *temp; int reg; /*initialization */ if(!alloc_regions){ for(reg=0;reg= MAX_REGIONS)armci_die("armci_shmem_reg_size: bad i",i); if(region_list[i].id !=(HANDLE)id)armci_die("armci_shmem_reg_size id",(int)id); return region_list[i].size; } char* armci_shmem_reg_ptr(int i) { if(i<0 || i>= MAX_REGIONS)armci_die("armci_shmem_reg_ptr: bad i",i); return region_list[i].addr; } int armci_get_shmem_info(char *addrp, int* shmid, long *shmoffset, size_t *shmsize) { int region; long idlist[SHMIDLEN]; if(!find_regions(addrp,idlist,®ion)) armci_die("CreateSharedRegion: allocation inconsitent",0); *shmid = (int) (*idlist); *shmoffset = (long)(addrp - region_list[region].addr); *shmsize = region_list[region].size; #if DEBUG printf("%d: armci_get_shmem_info: shmid=%d offset=%ld size=%ld %p\n", armci_me, *shmid, *shmoffset, *shmsize, addrp); fflush(stdout); #endif return 1; } Header *armci_get_shmem_ptr(int shmid, long shmoffset, size_t shmsize) { /* returns, address of the shared memory region based on shmid, offset. * (i.e. return_addr = stating address of shmid + offset)*/ long idlist[SHMIDLEN]; Header *p = NULL; idlist[1] = (long)shmid; idlist[0] = shmoffset; idlist[IDLOC+1] = shmsize; if(!(p=(Header*)Attach_Shared_Region(idlist+1, shmsize, shmoffset))) armci_die("armci_get_shmem_ptr: could not attach", (int)(shmsize>>10)); #if DEBUG printf("%d: armci_get_shmem_ptr: shmid=%d offset=%ld size=%ld %p\n", armci_me, shmid, shmoffset, shmsize, p); fflush(stdout); #endif return p; } ga-5.9.2/armci/src/progress/000077500000000000000000000000001500715745200156525ustar00rootroot00000000000000ga-5.9.2/armci/src/progress/fence.c000066400000000000000000000031561500715745200171030ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "armcip.h" #include "armci.h" #include "copy.h" #if HAVE_STDIO_H # include #endif #if defined(TCGMSG) # include static void tcg_synch(long type) { long atype = type; SYNCH_(&atype); } #else # include #endif char *_armci_fence_arr; void armci_init_fence() { #if defined (DATA_SERVER) #if defined(THREAD_SAFE) _armci_fence_arr = calloc(armci_nproc*armci_user_threads.max,1); #else _armci_fence_arr=calloc(armci_nproc,1); #endif if(!_armci_fence_arr) armci_die("armci_init_fence: calloc failed",0); #endif } void armci_finalize_fence() { #if defined (DATA_SERVER) free(_armci_fence_arr); _armci_fence_arr = NULL; #endif } void PARMCI_Fence(int proc) { #if defined(DATA_SERVER) if(FENCE_ARR(proc) && (armci_nclus >1)){ int cluster = armci_clus_id(proc); int master = armci_clus_info[cluster].master; armci_rem_ack(cluster); bzero(&FENCE_ARR(master), armci_clus_info[cluster].nslave); } #else FENCE_NODE(proc); MEM_FENCE; #endif } void PARMCI_GroupFence(ARMCI_Group *group) { /* Stub to prevent compilation problems with Comex build */ } void PARMCI_AllFence() { #if defined(CLUSTER) int p; for(p = 0;p < armci_nproc; p++) { PARMCI_Fence(p); } #endif MEM_FENCE; } void PARMCI_Barrier() { if (armci_nproc==1) return; PARMCI_AllFence(); #ifdef MSG_COMMS_MPI MPI_Barrier(ARMCI_COMM_WORLD); #else { long type=ARMCI_TAG; tcg_synch(type); } #endif MEM_FENCE; } ga-5.9.2/armci/src/progress/wait.c000066400000000000000000000055771500715745200170000ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #define EXTERN #include "armcip.h" int PARMCI_Wait(armci_hdl_t* usr_hdl) { armci_ihdl_t nb_handle = (armci_ihdl_t)usr_hdl; int success=0; int direct = SAMECLUSNODE(nb_handle->proc); if(direct) { return(success); } if(nb_handle) { if(nb_handle->agg_flag) { armci_agg_complete(nb_handle, UNSET); return (success); } } if(nb_handle){ #ifdef ARMCI_NB_WAIT if(nb_handle->tag==0){ ARMCI_NB_WAIT(nb_handle->cmpl_info); return(success); } #if defined(ALLOW_PIN) if(nb_handle->tag!=0 && nb_handle->bufid==NB_NONE){ ARMCI_NB_WAIT(nb_handle->cmpl_info); return(success); } #endif #endif #ifdef COMPLETE_HANDLE COMPLETE_HANDLE(nb_handle->bufid,nb_handle->tag,(&success)); #endif } return(success); } /** * * implicit handle * */ static armci_hdl_t armci_nb_handle[ARMCI_MAX_IMPLICIT];/*implicit non-blocking handle*/ static char hdl_flag[ARMCI_MAX_IMPLICIT]; static int impcount=0; armci_hdl_t *armci_set_implicit_handle (int op, int proc) { armci_ihdl_t nbh; int i=impcount%ARMCI_MAX_IMPLICIT; if(hdl_flag[i]=='1') PARMCI_Wait(&armci_nb_handle[i]); nbh = (armci_ihdl_t)&armci_nb_handle[i]; nbh->tag = GET_NEXT_NBTAG(); nbh->op = op; nbh->proc = proc; nbh->bufid = NB_NONE; nbh->agg_flag = 0; hdl_flag[i]='1'; ++impcount; return &armci_nb_handle[i]; } /* wait for all non-blocking operations to finish */ int PARMCI_WaitAll (void) { int i; if(impcount) { for(i=0; iproc==proc) { PARMCI_Wait(&armci_nb_handle[i]); hdl_flag[i]='0'; } } } return 0; } int PARMCI_Test(armci_hdl_t *usr_hdl) { armci_ihdl_t nb_handle = (armci_ihdl_t)usr_hdl; int success=0; int direct=SAMECLUSNODE(nb_handle->proc); if(direct)return(success); if(nb_handle) { if(nb_handle->agg_flag) { armci_die("test for aggregate handle not yet implemented\n",0); } } if(nb_handle){ #ifdef ARMCI_NB_TEST if(nb_handle->tag==0){ ARMCI_NB_TEST(nb_handle->cmpl_info,&success); return(success); } #endif #ifdef TEST_HANDLE TEST_HANDLE(nb_handle->bufid,nb_handle->tag,(&success)); #endif } return(success); } ga-5.9.2/armci/src/util/000077500000000000000000000000001500715745200147635ustar00rootroot00000000000000ga-5.9.2/armci/src/util/armci_cpp000077500000000000000000000273331500715745200166560ustar00rootroot00000000000000#!/usr/bin/env perl # This script is designed to preprocess selected #if defined(), #ifdef # and #ifndef # statements in source code. The syntax for using this # script is # # ./armci_cpp SYMBOL1 SYMBOL2 SYMBOL3 .... SYMBOLN # # where SYMBOL refers to arguments that appear in the #if, #ifdef, # #ifndef statements. This script will process ALL files in the # directory in which it is run. This is done so that symbols defined # in include files can be added to the list of symbols used by the # script to parse the files. The files produced by the parser will have # a .new extension. The parser also produces a fairly large amount of # debugging output to standard IO. This can be ignored. # # An example invocation of armci_cpp is as follows: # # armci_cpp LINUX64 LINUX SYSV PTHREADS DATA_SERVER \ # SERVER_THREAD _REENTRANT VAPI ALLOW_PIN PEND_BUFS REF_THREAD_SAFE \ # MPI OPENIB # # Symbols that end in _H and _H_ are handled differently if they # appear in .h files. The code bracketed by these symbols is parsed if # the symbol exists, even though it is using the #ifndef syntax. # # Note that this parser will probably not produce compilable code. There # are a few instances where symbols are defined if a comparative # relation is satisfied (e.g. SYMBOL_A < SYMBOL_B) and this causes some # symbols to be left out of the symbol table and some parts of the code # to be incorrectly parsed. The parser will, however, give an rough idea # of what post process code will look like. # $numargs = @ARGV; if ($numargs == 0) { print "No environment settings specified\n"; print "Usage: ./armci_cpp SYMBOL1 SYMBOL2 SYMBOL3 .... SYMBOLN\n"; exit(0); } # Get environment settings and use these to file the symbol list %arg_symbols = {}; for ($i=0; $i<$numargs; $i++) { print "ARGV[$i]: $ARGV[$i]\n"; $arg_symbols{$ARGV[$i]} = 1; } # Get a list of all files in the directory opendir(MYDIR,"./"); @tfiles = readdir(MYDIR); closedir(MYDIR); $num_files = @tfiles; # Scan the include files for symbols for ($ifile=0; $ifile<$num_files; $ifile++) { if ($tfiles[$ifile] =~ /\.h$/ || $tfiles[$ifile] =~ /\.c$/) { $level = 0; %symbols = {}; %symbols = %arg_symbols; %header_symbols = {}; # Initialize file variable @state = (); $state[0] = 1; @save_cpp = (); $save_cpp[0] = 0; @once_true = (); $once_true[0] = 1; @parse_else = (); $parse_else[0] = 0; @ignore = (); $ignore[0] = 0; $newfile = ""; $continuation_cnt = 0; # $file_level = 0; @parsing_header = (); $parsing_header[0] = 0; # &parse_file($tfiles[$ifile]); # print "PRINTING NEWFILE:\n$newfile\n"; $filename = $tfiles[$ifile]; $filename .= "\.new"; open (NEW_HEADER,">$filename"); print NEW_HEADER ("$newfile"); close(NEW_HEADER); # foreach $symbol (keys %header_symbols) { print "(header symbols) \($symbol\):$header_symbols{$symbol}\n"; } foreach $symbol (keys %symbols) { print "(symbols) \($symbol\):$symbols{$symbol}\n"; } } } # Subroutine to reduce string to evaluatable expression sub reduce_expr { if ($state[$level] == 1) { print "Expression1: \($expr\)\n"; } $expr =~ s/\/\*.*\*\///g; $expr =~ s/undefined/\!/g; $expr =~ s/defined//g; if ($state[$level] == 1) { print "Expression2: \($expr\)\n"; } foreach $symbol (keys %header_symbols) { if ($symbol =~ /^\s*[a-zA-Z0-9\_]*\s*$/) { if ($expr =~ /[\s\!\(\|\&]+$symbol[\s\)\|\&]+/ || $expr =~ /\s+$symbol$/ || $expr =~ /$symbol $/ || $expr =~ /^$symbol$/) { print "Matching header_symbol: $symbol\n"; $expr =~ s/$symbol/1/; } } } foreach $symbol (keys %symbols) { if ($symbol =~ /ARMCI_STAMP/ ) { print "Found ARMCI_STAMP: \($symbol\)\n"; } if ($symbol =~ /^\s*[a-zA-Z0-9\_]*\s*$/) { if ($expr =~ /[\s\!\(\|\&]+$symbol[\s\)\|\&]+/ || $expr =~ /\s+$symbol$/ || $expr =~ /$symbol $/ || $expr =~ /^$symbol$/) { print "Matching symbol: $symbol\n"; $expr =~ s/$symbol/1/; } } } if ($state[$level] == 1) { print "Expression3: \($expr\)\n"; } # Set anything that hasn't been recognized to 0 $copy = $expr; # Replace delimiters etc. by blanks $copy =~ s/[&|\(\)]+/ /g; chomp($copy); @strings = (); @strings = split(/\s+/,$copy); # Loop through list of strings and replace anything that isn't a 1 $test_string = ""; for ($j=0; $j<@strings; $j++) { $test_string .= $strings[$j]; $test_string .= " "; if (!($strings[$j] =~ /\s*1\s+/ || $strings[$j] =~ /\s+1\s*/ || $strings[$j] =~ /^1$/)) { $tmp_string = $strings[$j]; if (!($tmp_string =~ /[\s+|\!]/ || $tmp_string =~ /\\/ || $tmp_string eq "")) { $expr =~ s/$tmp_string/0/; } } } # Get rid of continuation characters $expr =~ s/\\//g; if ($state[$level] == 1) { print "Expression4: \($expr\)\n"; } } # Subroutine to get the rest of the expression if a line continuation appears sub get_expr { my($local_expr,$filesize,$iline,@myfile) = @_; $expr = $local_expr; # See if line continues print "Local expr: $local_expr filesize: $filesize iline: $iline\n"; while ($line =~/\\\s*$/ && $iline<$filesize-1) { $iline++; $line = $myfile[$iline]; $expr .= $line; print "Current line: $line\n"; print "Continuation line: $expr\n"; } $continuation_cnt = $iline; # Remove continuation characters $expr =~ s/\\//; } # Subroutine to set parsing parameters sub set_pars_params { $level++; print "Level: $level\n"; if ($expr =~ /0 0/) { print "Bogus expression: $expr\n"; print "Filename: $tfiles[$ifile]\n"; } $chk = eval $expr; if ($chk) { print "Expression $expr : true\n"; } else { print "Expression $expr : false\n"; } if ($chk) { if ($state[$level-1] == 1) { $state[$level] = 1; $save_cpp[$level] = 0; } else { $state[$level] = 0; $save_cpp[$level] = 0; } } else { $state[$level] = 0; $save_cpp[$level] = 0; } $parse_else[$level] = 1; $ignore[$level] = 0; } # Subroutine to parse files sub parse_file($filename) { my ($filename) = @_; my (@file, $filesize, $iline); my ($comment); # my (@state, @save_cpp, @once_true); # my (@parse_else); # Get strings from file; print "Parsing file: $tfiles[$ifile]\n"; open(HEADER, $filename); @file =
; close(HEADER); $filesize = @file; $iline = 0; # Initialize parser contol variables $comment = 0; while ($iline<$filesize) { $line = $file[$iline]; # Check for comments if ($line =~ /\/\*/ && !($line =~/\*\//)) { $comment = 1; } if ($line =~ /\*\// && !($line =~ /\/\*/)) { $comment = 0; } # Check for #if constructs if ($line =~ /^\s*\#\s*if([\s\(])*def(ined)?(.*)/) { $tmp_string = $1; &get_expr($3,$filesize,$iline,@file); $iline = $continuation_cnt; print "ifdef expression: $3\n"; if ($tmp_string =~ /\(*/) { $tmp_string .= $expr; $expr = $tmp_string; } &reduce_expr; &set_pars_params; $chk = eval $expr; if ($chk) { $once_true[$level] = 1; } else { $once_true[$level] = 0; } print "ifdef: state[$level]: $state[$level]\n"; } elsif ($line =~ /^\s*\#\s*if\s+0/) { print "if 0: $line"; $level++; print "Level: $level\n"; $state[$level] = 0; $save_cpp[$level] = 0; $once_true[$level] = 0; $pars_else[$level] = 1; $ignore[$level] = 0; } elsif ($line =~ /^\s*\#\s*if\s+1/) { print "if 1: $line"; $level++; print "Level: $level\n"; if ($state[$level-1] == 1) { $state[$level] = 1; $save_cpp[$level] = 0; } else { $state[$level] = 0; $save_cpp[$level] = 0; } $once_true[$level] = 1; $pars_else[$level] = 1; $ignore[$level] = 0; } elsif ($line =~ /^\s*\#\s*if\s*\!\s*defined(.*)/) { &get_expr($1,$filesize,$iline,@file); $iline = $continuation_cnt; &reduce_expr; $expr = "!\($expr\)"; &set_pars_params; $chk = eval $expr; if ($chk) { $once_true[$level] = 1; } else { $once_true[$level] = 0; } } elsif ($line =~ /^\s*\#\s*if\s+/) { # Some other kind of conditional that will be ignored $level++; if ($state[$level-1] == 1) { $state[$level] = 1; $save_cpp[$level] = 1; $parse_else[$level] = 0; $newfile .= $line; $ignore[$level] = 1; } else { $state[$level] = 0; $save_cpp[$level] = 0; $ignore[$level] = 0; } } elsif ($line =~ /^\s*\#\s*ifndef\s+(.*)/) { # Check for header symbol if ($line =~ /\s+(\S+_H)\s*$/ || $line =~ /\s+(\S+_H_)\s*$/) { if (!defined($header_symbols{$1})) { $level++; print "Level: $level\n"; if ($state[$level-1] == 1) { $state[$level] = 1; $save_cpp[$level] = 1; $once_true[$level] = 1; $newfile .= $line; $header_symbols{$1} = 1; } else { $state[$level] = 0; $save_cpp[$level] = 0; $once_true[$level] = 0; } } else { $level++; $state[$level] = 0; $save_cpp[$level] = 1; $once_true[$level] = 1; } $pars_else[$level] = 1; $ignore[$level] = 0; } else { &get_expr($1,$filesize,$iline,@file); $iline = $continuation_cnt; &reduce_expr; $expr = "!\($expr\)"; &set_pars_params; $chk = eval $expr; if ($chk) { $once_true[$level] = 1; } else { $once_true[$level] = 0; } } } elsif ($line =~ /^\s*\#\s*elif\s+(.*)/ && $ignore[$level]==0) { &get_expr($1,$filesize,$iline,@file); $iline = $continuation_cnt; &reduce_expr; $chk = eval $expr; if ($chk) { if ($once_true[$level] == 0 && $state[$level-1] == 1) { $state[$level] = 1; $once_true[$level] = 1; } else { $state[$level] = 0; } } else { $state[$level] = 0; } } elsif ($line =~ /^\s*\#\s*else/ && $ignore[$level]==0) { if ($once_true[$level] == 0 && $state[$level-1] == 1) { $state[$level] = 1; $once_true[$level] = 1; } else { $state[$level] = 0; } } elsif ($line =~ /^\s*\#\s*endif/) { if ($state[$level] == 1 && $save_cpp[$level] == 1) { $newfile .= $line; } print "endif: $line"; $level--; print "Level: $level\n"; if ($level < 0) { exit(0); } } elsif ($line =~ /^\s*\#\s*define\s+(\S+)\s*(\S*)/) { if ($comment == 0) { $key = $1; $value = $2; $key =~ s/\(.*//; if (($ignore[$level] == 0 && $parsing_header[$file_level] == 1) || $state[$level] == 1) { if ($2 ne "") { $symbols{$key} = $value; print "new symbol key: $key value: $value\n"; } else { $symbols{$key} = 1; print "new symbol key: $key (no value)\n"; } } } if ($state[$level] == 1) { print "new definition: $line\n"; $newfile .= $line; } } elsif ($line =~ /^\s*\#\s*include\s+(\S+)/) { $include_file = $1; # Ignore include if it is a system level header file if (!($include_file =~ /\<.*\>/) && $include_file =~ /\.h\"\s*$/ && $state[$level] == 1) { $newfile .= $line; $include_file =~ s/\"//g; print "Adding contents of $include_file\n"; $level++; $ignore[$level] = 0; $state[$level] = 0; $file_level++; $parsing_header[$file_level] = 1; &parse_file($include_file); $file_level--; $level--; } elsif ( $state[$level] == 1) { $newfile .= $line; } } else { if ($state[$level] == 1) { $newfile .= $line; } } $iline++; } } ga-5.9.2/armci/src/util/threads.c000066400000000000000000000067371500715745200165760ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: threads.c,v 1.1.2.5 2007-08-28 21:29:46 manoj Exp $ */ #if 0 # define PRNDBG3(m,a1,a2,a3) \ fprintf(stderr,"DBG %d: " m,armci_me,a1,a2,a3);fflush(stderr) # define PRNDBG(m) PRNDBG3(m,0,0,0) # define PRNDBG1(m,a1) PRNDBG3(m,a1,0,0) # define PRNDBG2(m,a1,a2) PRNDBG3(m,a1,a2,0) #else # define PRNDBG(m) # define PRNDBG1(m,a1) # define PRNDBG2(m,a1,a2) # define PRNDBG3(m,a1,a2,a3) #endif #if HAVE_STDIO_H # include #endif #include "armcip.h" armci_user_threads_t armci_user_threads; void armci_init_threads() { int i, bytes; char *uval = getenv("ARMCI_MAX_THREADS"); armci_user_threads.max = 1; armci_user_threads.avail = 0; if (uval != NULL) sscanf(uval, "%d", &armci_user_threads.max); if (armci_user_threads.max < 1 || armci_user_threads.max > ARMCI_THREADS_LIMIT) { printf("Error: Only 1-%d threads are supported. ",ARMCI_THREADS_LIMIT); printf("Set ARMCI_MAX_THREADS appropriately\n"); fflush(stdout); armci_die("armci_init_threads: failed", 0); } bytes = sizeof(thread_id_t) * armci_user_threads.max; if ( !(armci_user_threads.ids = (thread_id_t*) malloc(bytes)) ) { armci_die("armci_init_threads: armci_user_threads.ids malloc failed", armci_user_threads.max); } memset(armci_user_threads.ids, 0, bytes); #if 0 /* spinlock has void return value */ if (THREAD_LOCK_INIT(armci_user_threads.lock) || THREAD_LOCK_INIT(armci_user_threads.buf_lock) || THREAD_LOCK_INIT(armci_user_threads.net_lock)) armci_die("armci_init_threads:locks initialization failed", 0); #else THREAD_LOCK_INIT(armci_user_threads.lock); THREAD_LOCK_INIT(armci_user_threads.buf_lock); THREAD_LOCK_INIT(armci_user_threads.net_lock); #endif #if 0 /* using one lock per socket for now, it might be feasible (and usefull) * to use two (one for sending and one for receiving) */ armci_user_threads.sock_locks = malloc(armci_nclus *sizeof(thread_lock_t)); for (i = 0; i < armci_nclus; i++) if (THREAD_LOCK_INIT(armci_user_threads.sock_locks[i])) armci_die("armci_init_threads:sock locks initialization failed", i); #endif } void armci_finalize_threads() { THREAD_LOCK_DESTROY(armci_user_threads.lock); THREAD_LOCK_DESTROY(armci_user_threads.net_lock); THREAD_LOCK_DESTROY(armci_user_threads.buf_lock); free(armci_user_threads.ids); } /* calling armci_thread_idx for every function that accesses thread-private data * might be expensive -- needs optiomization */ int armci_thread_idx() { int i, n = ARMCI_MIN(armci_user_threads.avail, armci_user_threads.max); thread_id_t id = THREAD_ID_SELF(); for (i = 0; i < n; i++) if (id == armci_user_threads.ids[i]) { /*PRNDBG2("thread id=%ld already registered, idx=%d\n", id, i);*/ return i; } /* see this thread for the first time */ return armci_register_thread(id); } int armci_register_thread(thread_id_t id) { int i; THREAD_LOCK(armci_user_threads.lock); i = armci_user_threads.avail; armci_user_threads.avail++; THREAD_UNLOCK(armci_user_threads.lock); if (i < armci_user_threads.max) armci_user_threads.ids[i] = id; else armci_die("armci_thread_idx: too many threads, adjust ARMCI_MAX_THREADS", armci_user_threads.avail); PRNDBG2("registered a new thread: idx=%d, id=%ld\n", i, id); return i; } ga-5.9.2/armci/src/xfer/000077500000000000000000000000001500715745200147525ustar00rootroot00000000000000ga-5.9.2/armci/src/xfer/caccumulate.c000066400000000000000000000576711500715745200174240ustar00rootroot00000000000000/*************************************************************************** COPYRIGHT The following is a notice of limited availability of the code, and disclaimer which must be included in the prologue of the code and in all source listings of the code. Copyright Notice + 2009 University of Chicago Permission is hereby granted to use, reproduce, prepare derivative works, and to redistribute to others. This software was authored by: Jeff R. Hammond Leadership Computing Facility Argonne National Laboratory Argonne IL 60439 USA phone: (630) 252-5381 e-mail: jhammond@anl.gov GOVERNMENT LICENSE Portions of this material resulted from work developed under a U.S. Government Contract and are subject to the following license: the Government is granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable worldwide license in this computer software to reproduce, prepare derivative works, and perform publicly and display publicly. DISCLAIMER This computer code material was prepared, in part, as an account of work sponsored by an agency of the United States Government. Neither the United States, nor the University of Chicago, nor any of their employees, makes any warranty express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness of any information, apparatus, product, or process disclosed, or represents that its use would not infringe privately owned rights. ***************************************************************************/ /*********************************************************************** * accumulate operation for the following datatypes: * real, double precision, complex, double complex, integer * * WARNING: This file must be compiled WITH optimization under AIX. * IBM fortran compilers generate bad code with -g option. * * Two versions of each routine are provided: * original and unrolled loops. * ***********************************************************************/ #if HAVE_CONFIG_H # include "config.h" #endif #include "acc.h" #if 0 subroutine d_accumulate_1d(alpha, A, B, rows) integer rows, r double precision A(*), B(*), alpha ccdir$ no_cache_alloc a,b do r = 1, rows A(r) = A(r)+ alpha*B(r) enddo end #endif void c_d_accumulate_1d_(const double* const restrict alpha, double* restrict A, const double* const restrict B, const int* const restrict rows) { int i; for ( i = 0 ; i < (*rows) ; i++ ){ A[i] += (*alpha) * B[i]; } return; } #if 0 subroutine d_accumulate_2d(alpha, rows, cols, A, ald, B, bld) integer rows, cols integer c, r, ald, bld double precision A(ald,*), B(bld,*), alpha ccdir$ no_cache_alloc a,b do c = 1, cols do r = 1, rows A(r,c) = A(r,c)+ alpha*B(r,c) enddo enddo end #endif void c_d_accumulate_2d_(const double* const alpha, const int* const rows, const int* const cols, double* restrict A, const int* const ald, const double* const B, const int* const bld) { int r, c; for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < (*rows) ; r++ ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; } } return; } #if 0 subroutine f_accumulate_1d(alpha, A, B, rows) integer rows, r real A(*), B(*), alpha do r = 1, rows A(r) = A(r)+ alpha*B(r) enddo end #endif void c_f_accumulate_1d_(const float* const restrict alpha, float* const restrict A, const float* const restrict B, const int* const restrict rows) { int i; for ( i = 0 ; i < (*rows) ; i++ ){ A[i] += (*alpha) * B[i]; } return; } #if 0 subroutine f_accumulate_2d(alpha, rows, cols, A, ald, B, bld) integer rows, cols integer c, r, ald, bld real A(ald,*), B(bld,*), alpha do c = 1, cols do r = 1, rows A(r,c) = A(r,c)+ alpha*B(r,c) enddo enddo end #endif void c_f_accumulate_2d_(const float* const alpha, const int* const rows, const int* const cols, float* restrict A, const int* const ald, const float* const B, const int* const bld) { int r, c; for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < (*rows) ; r++ ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; } } return; } #if 0 subroutine z_accumulate_1d(alpha, A, B, rows) integer rows, r double complex A(*), B(*), alpha do r = 1, rows A(r) = A(r)+ alpha*B(r) enddo end #endif void c_c_accumulate_1d_(const complex_t* const restrict alpha, complex_t* const restrict A, const complex_t* const restrict B, const int* const restrict rows) { int i; for ( i = 0 ; i < (*rows) ; i++ ){ A[i].real += (*alpha).real * B[i].real - (*alpha).imag * B[i].imag; A[i].imag += (*alpha).imag * B[i].real + (*alpha).real * B[i].imag; } return; } #if 0 subroutine z_accumulate_2d(alpha, rows, cols, A, ald, B, bld) integer rows, cols integer c, r, ald, bld double complex A(ald,*), B(bld,*), alpha do c = 1, cols do r = 1, rows A(r,c) = A(r,c)+ alpha*B(r,c) enddo enddo end #endif void c_c_accumulate_2d_(const complex_t* const alpha, const int* const rows, const int* const cols, complex_t* restrict A, const int* const ald, const complex_t* const B, const int* const bld) { int r, c; for ( c = 0 ; c < (*cols) ; c++ ) { for ( r = 0 ; r < (*rows) ; r++ ) { A[ c * (*ald) + r ].real += (*alpha).real * B[ c * (*bld) + r ].real - (*alpha).imag * B[ c * (*bld) + r ].imag; A[ c * (*ald) + r ].imag += (*alpha).imag * B[ c * (*bld) + r ].real + (*alpha).real * B[ c * (*bld) + r ].imag; } } return; } #if 0 subroutine c_accumulate_1d(alpha, A, B, rows) integer rows, r complex A(*), B(*), alpha do r = 1, rows A(r) = A(r)+ alpha*B(r) enddo end #endif void c_z_accumulate_1d_(const dcomplex_t* const restrict alpha, dcomplex_t* const restrict A, const dcomplex_t* const restrict B, const int* const restrict rows) { int i; for ( i = 0 ; i < (*rows) ; i++ ){ A[i].real += (*alpha).real * B[i].real - (*alpha).imag * B[i].imag; A[i].imag += (*alpha).imag * B[i].real + (*alpha).real * B[i].imag; } return; } #if 0 subroutine c_accumulate_2d(alpha, rows, cols, A, ald, B, bld) integer rows, cols integer c, r, ald, bld complex A(ald,*), B(bld,*), alpha do c = 1, cols do r = 1, rows A(r,c) = A(r,c)+ alpha*B(r,c) enddo enddo end #endif void c_z_accumulate_2d_(const dcomplex_t* const alpha, const int* const rows, const int* const cols, dcomplex_t* restrict A, const int* const ald, const dcomplex_t* const B, const int* const bld) { int r, c; for ( c = 0 ; c < (*cols) ; c++ ) { for ( r = 0 ; r < (*rows) ; r++ ) { A[ c * (*ald) + r ].real += (*alpha).real * B[ c * (*bld) + r ].real - (*alpha).imag * B[ c * (*bld) + r ].imag; A[ c * (*ald) + r ].imag += (*alpha).imag * B[ c * (*bld) + r ].real + (*alpha).real * B[ c * (*bld) + r ].imag; } } return; } #if 0 subroutine i_accumulate_2d(alpha, rows, cols, A, ald, B, bld) integer rows, cols integer c, r, ald, bld integer A(ald,*), B(bld,*), alpha do c = 1, cols do r = 1, rows A(r,c) = A(r,c)+ alpha*B(r,c) enddo enddo end #endif void c_i_accumulate_1d_(const int* const restrict alpha, int* const restrict A, const int* const restrict B, const int* const restrict rows) { int i; for ( i = 0 ; i < (*rows) ; i++ ){ A[i] += (*alpha) * B[i]; } return; } void c_l_accumulate_1d_(const long* const restrict alpha, long* const restrict A, const long* const restrict B, const int* const restrict rows) { int i; for ( i = 0 ; i < (*rows) ; i++ ){ A[i] += (*alpha) * B[i]; } return; } void c_ll_accumulate_1d_(const long long* const restrict alpha, long long* const restrict A, const long long* const restrict B, const int* const restrict rows) { int i; for ( i = 0 ; i < (*rows) ; i++ ){ A[i] += (*alpha) * B[i]; } return; } #if 0 subroutine i_accumulate_1d(alpha, A, B, rows) integer rows, r integer A(*), B(*), alpha do r = 1, rows A(r) = A(r)+ alpha*B(r) enddo end #endif void c_i_accumulate_2d_(const int* const alpha, const int* const rows, const int* const cols, int* restrict A, const int* const ald, const int* const B, const int* const bld) { int r, c; for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < (*rows) ; r++ ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; } } return; } void c_l_accumulate_2d_(const long* const alpha, const int* const rows, const int* const cols, long* restrict A, const int* const ald, const long* const B, const int* const bld) { int r, c; for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < (*rows) ; r++ ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; } } return; } void c_ll_accumulate_2d_(const long long* const alpha, const int* const rows, const int* const cols, long long* restrict A, const int* const ald, const long long* const B, const int* const bld) { int r, c; for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < (*rows) ; r++ ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; } } return; } #if 0 subroutine d_accumulate_2d_u(alpha, rows, cols, A, ald, B, bld) integer rows, cols integer c, r, ald, bld double precision A(ald,*), B(bld,*), alpha integer r1 doubleprecision d1, d2, d3, d4 do c = 1, cols r1 = iand(max0(rows,0),3) do r = 1, r1 a(r,c) = a(r,c) + alpha*b(r,c) end do do r = r1 + 1, rows, 4 d1 = a(r,c) + alpha*b(r,c) d2 = a(r+1,c) + alpha*b(r+1,c) d3 = a(r+2,c) + alpha*b(r+2,c) d4 = a(r+3,c) + alpha*b(r+3,c) a(r,c) = d1 a(r+1,c) = d2 a(r+2,c) = d3 a(r+3,c) = d4 enddo enddo end #endif void c_d_accumulate_2d_u_(const double* const alpha, const int* const rows, const int* const cols, double* restrict A, const int* const ald, const double* const B, const int* const bld) { int r, c; int m = (*rows) - ((*rows)%4); for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < m ; r+=4 ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; A[ c * (*ald) + r+1 ] += (*alpha) * B[ c * (*bld) + r+1 ]; A[ c * (*ald) + r+2 ] += (*alpha) * B[ c * (*bld) + r+2 ]; A[ c * (*ald) + r+3 ] += (*alpha) * B[ c * (*bld) + r+3 ]; } for ( r = m ; r < (*rows) ; r++ ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; } } return; } #if 0 subroutine f_accumulate_2d_u(alpha, rows, cols, A, ald, B, bld) integer rows, cols integer c, r, ald, bld real A(ald,*), B(bld,*), alpha integer r1 real d1, d2, d3, d4 do c = 1, cols r1 = iand(max0(rows,0),3) do r = 1, r1 a(r,c) = a(r,c) + alpha*b(r,c) end do do r = r1 + 1, rows, 4 d1 = a(r,c) + alpha*b(r,c) d2 = a(r+1,c) + alpha*b(r+1,c) d3 = a(r+2,c) + alpha*b(r+2,c) d4 = a(r+3,c) + alpha*b(r+3,c) a(r,c) = d1 a(r+1,c) = d2 a(r+2,c) = d3 a(r+3,c) = d4 enddo enddo end #endif void c_f_accumulate_2d_u_(const float* const alpha, const int* const rows, const int* const cols, float* restrict A, const int* const ald, const float* const B, const int* const bld) { int r, c; int m = (*rows) - ((*rows)%4); for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < m ; r+=4 ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; A[ c * (*ald) + r+1 ] += (*alpha) * B[ c * (*bld) + r+1 ]; A[ c * (*ald) + r+2 ] += (*alpha) * B[ c * (*bld) + r+2 ]; A[ c * (*ald) + r+3 ] += (*alpha) * B[ c * (*bld) + r+3 ]; } for ( r = m ; r < (*rows) ; r++ ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; } } return; } #if 0 subroutine z_accumulate_2d_u(alpha, rows, cols, A, ald, B, bld) integer rows, cols integer c, r, ald, bld double complex A(ald,*), B(bld,*), alpha integer r1 double complex x1, x2, x3, x4 do c = 1, cols r1 = iand(max0(rows,0),3) do r = 1, r1 a(r,c) = a(r,c) + alpha*b(r,c) end do do r = r1 + 1, rows, 4 x1 = a(r,c) + alpha*b(r,c) x2 = a(r+1,c) + alpha*b(r+1,c) x3 = a(r+2,c) + alpha*b(r+2,c) x4 = a(r+3,c) + alpha*b(r+3,c) a(r,c) = x1 a(r+1,c) = x2 a(r+2,c) = x3 a(r+3,c) = x4 enddo enddo end #endif void c_c_accumulate_2d_u_(const complex_t* const alpha, const int* const rows, const int* const cols, complex_t* restrict A, const int* const ald, const complex_t* const B, const int* const bld) { int r, c; int jA, jB; int m = (*rows) - ((*rows)%4); for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < m ; r+=4 ){ jA = c * (*ald) + r; jB = c * (*bld) + r; A[ jA ].real += (*alpha).real * B[ jB ].real - (*alpha).imag * B[ jB ].imag; A[ jA ].imag += (*alpha).imag * B[ jB ].real + (*alpha).real * B[ jB ].imag; A[ jA+1 ].real += (*alpha).real * B[ jB+1 ].real - (*alpha).imag * B[ jB+1 ].imag; A[ jA+1 ].imag += (*alpha).imag * B[ jB+1 ].real + (*alpha).real * B[ jB+1 ].imag; A[ jA+2 ].real += (*alpha).real * B[ jB+2 ].real - (*alpha).imag * B[ jB+2 ].imag; A[ jA+2 ].imag += (*alpha).imag * B[ jB+2 ].real + (*alpha).real * B[ jB+2 ].imag; A[ jA+3 ].real += (*alpha).real * B[ jB+3 ].real - (*alpha).imag * B[ jB+3 ].imag; A[ jA+3 ].imag += (*alpha).imag * B[ jB+3 ].real + (*alpha).real * B[ jB+3 ].imag; } for ( r = m ; r < (*rows) ; r++ ){ A[ c * (*ald) + r ].real += (*alpha).real * B[ c * (*bld) + r ].real - (*alpha).imag * B[ c * (*bld) + r ].imag; A[ c * (*ald) + r ].imag += (*alpha).imag * B[ c * (*bld) + r ].real + (*alpha).real * B[ c * (*bld) + r ].imag; } } return; } #if 0 subroutine c_accumulate_2d_u(alpha, rows, cols, A, ald, B, bld) integer rows, cols integer c, r, ald, bld complex A(ald,*), B(bld,*), alpha integer r1 complex x1, x2, x3, x4 do c = 1, cols r1 = iand(max0(rows,0),3) do r = 1, r1 a(r,c) = a(r,c) + alpha*b(r,c) end do do r = r1 + 1, rows, 4 x1 = a(r,c) + alpha*b(r,c) x2 = a(r+1,c) + alpha*b(r+1,c) x3 = a(r+2,c) + alpha*b(r+2,c) x4 = a(r+3,c) + alpha*b(r+3,c) a(r,c) = x1 a(r+1,c) = x2 a(r+2,c) = x3 a(r+3,c) = x4 enddo enddo end #endif void c_z_accumulate_2d_u_(const dcomplex_t* const alpha, const int* const rows, const int* const cols, dcomplex_t* restrict A, const int* const ald, const dcomplex_t* const B, const int* const bld) { int r, c; int jA, jB; int m = (*rows) - ((*rows)%4); for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < m ; r+=4 ){ jA = c * (*ald) + r; jB = c * (*bld) + r; A[ jA ].real += (*alpha).real * B[ jB ].real - (*alpha).imag * B[ jB ].imag; A[ jA ].imag += (*alpha).imag * B[ jB ].real + (*alpha).real * B[ jB ].imag; A[ jA+1 ].real += (*alpha).real * B[ jB+1 ].real - (*alpha).imag * B[ jB+1 ].imag; A[ jA+1 ].imag += (*alpha).imag * B[ jB+1 ].real + (*alpha).real * B[ jB+1 ].imag; A[ jA+2 ].real += (*alpha).real * B[ jB+2 ].real - (*alpha).imag * B[ jB+2 ].imag; A[ jA+2 ].imag += (*alpha).imag * B[ jB+2 ].real + (*alpha).real * B[ jB+2 ].imag; A[ jA+3 ].real += (*alpha).real * B[ jB+3 ].real - (*alpha).imag * B[ jB+3 ].imag; A[ jA+3 ].imag += (*alpha).imag * B[ jB+3 ].real + (*alpha).real * B[ jB+3 ].imag; } for ( r = m ; r < (*rows) ; r++ ){ A[ c * (*ald) + r ].real += (*alpha).real * B[ c * (*bld) + r ].real - (*alpha).imag * B[ c * (*bld) + r ].imag; A[ c * (*ald) + r ].imag += (*alpha).imag * B[ c * (*bld) + r ].real + (*alpha).real * B[ c * (*bld) + r ].imag; } } return; } #if 0 subroutine i_accumulate_2d_u(alpha, rows, cols, A, ald, B, bld) integer rows, cols integer c, r, ald, bld integer A(ald,*), B(bld,*), alpha integer r1, j2, j3, j4, j5 do c = 1, cols r1 = iand(max0(rows,0),3) do r = 1, r1 a(r,c) = a(r,c) + alpha*b(r,c) end do do r = r1 + 1, rows, 4 j2 = a(r,c) + alpha*b(r,c) j3 = a(r+1,c) + alpha*b(r+1,c) j4 = a(r+2,c) + alpha*b(r+2,c) j5 = a(r+3,c) + alpha*b(r+3,c) a(r,c) = j2 a(r+1,c) = j3 a(r+2,c) = j4 a(r+3,c) = j5 enddo enddo end #endif void c_i_accumulate_2d_u_(const int* const alpha, const int* const rows, const int* const cols, int* restrict A, const int* const ald, const int* const B, const int* const bld) { int r, c; int m = (*rows) - ((*rows)%4); for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < m ; r+=4 ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; A[ c * (*ald) + r+1 ] += (*alpha) * B[ c * (*bld) + r+1 ]; A[ c * (*ald) + r+2 ] += (*alpha) * B[ c * (*bld) + r+2 ]; A[ c * (*ald) + r+3 ] += (*alpha) * B[ c * (*bld) + r+3 ]; } for ( r = m ; r < (*rows) ; r++ ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; } } return; } void c_l_accumulate_2d_u_(const long* const alpha, const int* const rows, const int* const cols, long* restrict A, const int* const ald, const long* const B, const int* const bld) { int r, c; int m = (*rows) - ((*rows)%4); for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < m ; r+=4 ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; A[ c * (*ald) + r+1 ] += (*alpha) * B[ c * (*bld) + r+1 ]; A[ c * (*ald) + r+2 ] += (*alpha) * B[ c * (*bld) + r+2 ]; A[ c * (*ald) + r+3 ] += (*alpha) * B[ c * (*bld) + r+3 ]; } for ( r = m ; r < (*rows) ; r++ ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; } } return; } void c_ll_accumulate_2d_u_(const long long* const alpha, const int* const rows, const int* const cols, long long* restrict A, const int* const ald, const long long* const B, const int* const bld) { int r, c; int m = (*rows) - ((*rows)%4); for ( c = 0 ; c < (*cols) ; c++ ){ for ( r = 0 ; r < m ; r+=4 ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; A[ c * (*ald) + r+1 ] += (*alpha) * B[ c * (*bld) + r+1 ]; A[ c * (*ald) + r+2 ] += (*alpha) * B[ c * (*bld) + r+2 ]; A[ c * (*ald) + r+3 ] += (*alpha) * B[ c * (*bld) + r+3 ]; } for ( r = m ; r < (*rows) ; r++ ){ A[ c * (*ald) + r ] += (*alpha) * B[ c * (*bld) + r ]; } } return; } #if 0 c---------- operations used in armci gops -------------- c subroutine fort_dadd(n, x, work) integer n,i double precision x(n), work(n) do i= 1,n x(i) = x(i) + work(i) enddo end #endif void c_dadd_(const int* const restrict n, double* const restrict x, const double* const restrict work) { int i; for ( i = 0 ; i < (*n) ; i++ ){ x[i] += work[i]; } return; } #if 0 subroutine fort_dadd2(n, x, work, work2) integer n,i double precision x(n), work(n), work2(n) do i= 1,n x(i) = work(i) + work2(i) enddo end #endif void c_dadd2_(const int* const restrict n, double* const restrict x, const double* const restrict work, const double* const restrict work2) { int i; for ( i = 0 ; i < (*n) ; i++ ){ x[i] = work[i] + work2[i]; } return; } #if 0 subroutine fort_dmult(n, x, work) integer n,i double precision x(n), work(n) do i= 1,n x(i) = x(i) * work(i) enddo end #endif void c_dmult_(const int* const restrict n, double* const restrict x, const double* const restrict work) { int i; for ( i = 0 ; i < (*n) ; i++ ){ x[i] *= work[i]; } return; } #if 0 subroutine fort_dmult2(n, x, work,work2) integer n,i double precision x(n), work(n) do i= 1,n x(i) = work(i)*work2(i) enddo end #endif void c_dmult2_(const int* const restrict n, double* const restrict x, const double* const restrict work, const double* const restrict work2) { int i; for ( i = 0 ; i < (*n) ; i++ ){ x[i] = work[i] * work2[i]; } return; } ga-5.9.2/armci/src/xfer/rmw.c000066400000000000000000000053201500715745200157230ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "armcip.h" #include "locks.h" #include "copy.h" #if HAVE_STDIO_H # include #endif #if (defined(__i386__) || defined(__x86_64__)) && !defined(NO_I386ASM) # include "atomics-i386.h" #endif /* global scope to prevent compiler optimization of volatile code */ int _a_temp; long _a_ltemp; void armci_generic_rmw(int op, void *ploc, void *prem, int extra, int proc) { #if defined(CLUSTER) int lock = (proc-armci_clus_info[armci_clus_id(proc)].master)%NUM_LOCKS; #else int lock = 0; #endif NATIVE_LOCK(lock,proc); switch (op) { case ARMCI_FETCH_AND_ADD: #if (defined(__i386__) || defined(__x86_64__)) && !defined(NO_I386ASM) #if (defined(__GNUC__) || defined(__INTEL_COMPILER__) ||defined(__PGIC__)) && !defined(NO_I386ASM) if(SERVER_CONTEXT || armci_nclus == 1){ /* *(int*)ploc = __sync_fetch_and_add((int*)prem, extra); */ atomic_fetch_and_add(prem, ploc, extra, sizeof(int)); } else #endif #endif { armci_get(prem,ploc,sizeof(int),proc); _a_temp = *(int*)ploc + extra; armci_put(&_a_temp,prem,sizeof(int),proc); } break; case ARMCI_FETCH_AND_ADD_LONG: armci_get(prem,ploc,sizeof(long),proc); _a_ltemp = *(long*)ploc + extra; armci_put(&_a_ltemp,prem,sizeof(long),proc); break; case ARMCI_SWAP: #if (defined(__i386__) || defined(__x86_64__)) && !defined(NO_I386ASM) if(SERVER_CONTEXT || armci_nclus==1){ atomic_exchange(ploc, prem, sizeof(int)); } else #endif { armci_get(prem,&_a_temp,sizeof(int),proc); armci_put(ploc,prem,sizeof(int),proc); *(int*)ploc = _a_temp; } break; case ARMCI_SWAP_LONG: armci_get(prem,&_a_ltemp,sizeof(long),proc); armci_put(ploc,prem,sizeof(long),proc); *(long*)ploc = _a_ltemp; break; default: armci_die("rmw: operation not supported",op); } #ifdef VAPI if(!SERVER_CONTEXT) #endif PARMCI_Fence(proc); NATIVE_UNLOCK(lock,proc); } int PARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc) { #if defined(CLUSTER) && !defined(CYGWIN) if(!SAMECLUSNODE(proc)){ armci_rem_rmw(op, ploc, prem, extra, proc); return 0; } #endif #ifdef REGION_ALLOC if(SAMECLUSNODE(proc)) (void)armci_region_fixup(proc,&prem); #endif switch (op) { case ARMCI_FETCH_AND_ADD: case ARMCI_FETCH_AND_ADD_LONG: case ARMCI_SWAP: case ARMCI_SWAP_LONG: armci_generic_rmw(op, ploc, prem, extra, proc); break; default: armci_die("rmw: operation not supported",op); } return 0; } ga-5.9.2/armci/src/xfer/strided.c000066400000000000000000001217211500715745200165600ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: strided.c,v 1.117.2.6 2007-08-29 17:46:40 manoj Exp $ */ #include "armcip.h" #include "copy.h" #include "acc.h" #include "memlock.h" #include "armci.h" #include "iterator.h" #if HAVE_STDIO_H # include #endif #if HAVE_ASSERT_H # include #endif #define ARMCI_OP_2D(op, scale, proc, src, dst, bytes, count, src_stride, dst_stride,lockit) \ if(op == GET || op ==PUT) \ armci_copy_2D(op, proc, src, dst, bytes, count, src_stride,dst_stride); \ else if(count==1) armci_acc_1D(op, scale, proc, src, dst, bytes,lockit); \ else \ armci_acc_2D(op, scale, proc, src, dst, bytes, count, src_stride,dst_stride,lockit) /* macro supports run-time selection of request sending scheme */ #if defined(CLIENT_BUF_BYPASS) #define CAN_REQUEST_DIRECTLY _armci_bypass #else # define CAN_REQUEST_DIRECTLY 1 #endif #define BIGINT 2147483647 #define PREPROCESS_STRIDED(tmp_count) { \ tmp_count=0; \ if(stride_levels) \ for(;stride_levels;stride_levels--)if(count[stride_levels]>1)break; \ if((long) seg_count[1]* (long) seg_count[0] < BIGINT){ \ if(stride_levels&&(count[0]==src_stride_arr[0]&&count[0]==dst_stride_arr[0])){ \ tmp_count=seg_count[1]; \ count = seg_count+1; \ seg_count[1] = seg_count[0] * seg_count[1]; \ stride_levels --; \ src_stride_arr ++; dst_stride_arr++ ; \ } \ } \ } #define POSTPROCESS_STRIDED(tmp_count) if(tmp_count)seg_count[1]=tmp_count #define SERVER_GET 1 #define SERVER_NBGET 2 #define DIRECT_GET 3 #define DIRECT_NBGET 4 #define SERVER_PUT 5 #define SERVER_NBPUT 6 #define DIRECT_PUT 7 #define DIRECT_NBPUT 8 #ifdef ALLOW_PIN # define DO_FENCE(__proc,__prot) if(__prot==SERVER_GET); \ else if(__prot==SERVER_PUT); \ else if(__prot==DIRECT_GET || __prot==DIRECT_NBGET){ \ if(armci_prot_switch_fence[__proc]==SERVER_PUT) \ PARMCI_Fence(__proc); \ } \ else if(__prot==DIRECT_PUT || __prot==DIRECT_NBPUT){ \ if(armci_prot_switch_fence[__proc]==SERVER_PUT) \ PARMCI_Fence(__proc); \ } \ else {} \ armci_prot_switch_fence[__proc]=__prot #else # define DO_FENCE(__proc,__prot) #endif #ifndef REGIONS_REQUIRE_MEMHDL # define ARMCI_MEMHDL_T void #endif ARMCI_MEMHDL_T *mhloc=NULL,*mhrem=NULL; #ifdef REGIONS_REQUIRE_MEMHDL int armci_region_both_found_hndl(void *loc, void *rem, int size, int node, ARMCI_MEMHDL_T **loc_memhdl,ARMCI_MEMHDL_T **rem_memhdl); # define ARMCI_REGION_BOTH_FOUND(_s,_d,_b,_p) \ armci_region_both_found_hndl((_s),(_d),(_b),(_p),&mhloc,&mhrem) #else # define ARMCI_REGION_BOTH_FOUND(_s,_d,_b,_p) \ armci_region_both_found((_s),(_d),(_b),(_p)) #endif #ifdef HAS_RDMA_GET # ifdef REGIONS_REQUIRE_MEMHDL void armci_client_direct_get(int p, void *src_buf, void *dst_buf, int len, void** cptr,int nbtag,ARMCI_MEMHDL_T *lochdl,ARMCI_MEMHDL_T *remhdl); # else void armci_client_direct_get(int p, void *src_buf, void *dst_buf, int len, void** contextptr,int nbtag,void *mhdl,void *mhdl1); # endif # define ARMCI_NBREM_GET(_p,_s,_sst,_d,_dst,_cou,_lev,_hdl) \ armci_client_direct_get((_p),(_s),(_d),(_cou)[0],(void**)&((_hdl)->cmpl_info),(_hdl)->tag,(void *)mhloc,(void *)mhrem); \ # define ARMCI_REM_GET(_p,_s,_sst,_d,_dst,_cou,_lev,_hdl) \ armci_client_direct_get((_p),(_s),(_d),(_cou)[0],NULL,0,(void *)mhloc,(void *)mhrem) \ #else # define ARMCI_REM_GET(_p,_s,_sst,_d,_dst,_cou,_lev,_hdl) \ armci_rem_get((_p),(_s),(_sst),(_d),(_dst),(_cou),(_lev),(_hdl),(void *)mhloc,(void *)mhrem) # define ARMCI_NBREM_GET ARMCI_REM_GET #endif #ifdef ALLOW_PIN extern int* armci_prot_switch_fence; extern int armci_prot_switch_preproc; extern int armci_prot_switch_preop; #endif int armci_iwork[MAX_STRIDE_LEVEL]; /*\ 2-dimensional array copy \*/ static void armci_copy_2D(int op, int proc, void *src_ptr, void *dst_ptr, int bytes, int count, int src_stride, int dst_stride) { int shmem = SAMECLUSNODE(proc); if(shmem) { /* data is in local/shared memory -- can use memcpy */ if(count==1 && bytes 2) && lockit){ /* we need one lock operation only - must be done outside 2d acc */ armci_lockmem_patch(dst_ptr,dst_stride_arr, count, stride_levels, proc); unlockit=1; lockit =0; } /* if(proc!=armci_me) INTR_OFF;*/ switch (stride_levels) { case 0: /* 1D copy */ ARMCI_OP_2D(op, scale, proc, src_ptr, dst_ptr, count[0], 1, count[0], count[0], lockit); break; case 1: /* 2D op */ ARMCI_OP_2D(op, scale, proc, src_ptr, dst_ptr, count[0], count[1], src_stride_arr[0], dst_stride_arr[0], lockit); break; case 2: /* 3D op */ for (s2= 0; s2 < count[2]; s2++){ /* 2D copy */ ARMCI_OP_2D(op, scale, proc, src+s2*src_stride_arr[1], dst+s2*dst_stride_arr[1], count[0], count[1], src_stride_arr[0], dst_stride_arr[0], lockit ); } break; case 3: /* 4D op */ for(s3=0; s3< count[3]; s3++){ src = (char*)src_ptr + src_stride_arr[2]*s3; dst = (char*)dst_ptr + dst_stride_arr[2]*s3; for (s2= 0; s2 < count[2]; s2++){ /* 3D copy */ ARMCI_OP_2D(op, scale, proc, src+s2*src_stride_arr[1], dst+s2*dst_stride_arr[1], count[0], count[1],src_stride_arr[0], dst_stride_arr[0],lockit); } } break; default: /* N-dimensional */ { /* stride_levels is not the same as ndim. it is ndim-1 * For example a 10x10x10... array, suppose the datatype is byte * the stride_arr is 10, 10x10, 10x10x10 .... */ index[2] = 0; unit[2] = 1; total_of_2D = count[2]; for(j=3; j<=stride_levels; j++) { index[j] = 0; unit[j] = unit[j-1] * count[j-1]; total_of_2D *= count[j]; } for(i=0; i= count[j]) index[j] = 0; } ARMCI_OP_2D(op, scale, proc, src, dst, count[0], count[1], src_stride_arr[0], dst_stride_arr[0], lockit); } } } /* if(proc!=armci_me) INTR_ON;*/ /* __asm__ __volatile__ ("sfence":::"memory"); */ if(unlockit){ # if defined(ACC_COPY) FENCE_NODE(proc); # endif ARMCI_UNLOCKMEM(proc); /* release memory lock */ } return 0; } /**Internal puts function. Combines both blocking and non-blocking * variants. Any use of implicit handles should be outside of this * function. * @param src_ptr pointer to 1st segment at source * @param src_stride_arr array of strides at source * @param dst_ptr pointer to 1st segment at destination * @param dst_stride_arr array of strides at destination * @param seg_count number of segments at each stride levels: count[0]=bytes * @param stride_levels number of stride levels * @param proc remote process(or) ID * @param nbh non-blocking handle (NULL implies blocking call) * @param put_flag Flag to set after the PUT is remote complete (if any) * @return */ static int _armci_puts(void *src_ptr, int src_stride_arr[], void* dst_ptr, int dst_stride_arr[], int seg_count[], int stride_levels, int proc, armci_ihdl_t nbh, armci_flag_t *put_flag) { int *count=seg_count, tmp_count=0; int rc=0, direct=1; if(src_ptr == NULL || dst_ptr == NULL) return FAIL; if(count[0]<0)return FAIL3; if(stride_levels <0 || stride_levels > MAX_STRIDE_LEVEL) return FAIL4; if(proc<0)return FAIL5; PREPROCESS_STRIDED(tmp_count); # if defined(PACKPUT) direct=SAMECLUSNODE(proc); # endif /*PACKPUT*/ if(put_flag) dassert(1,nbh==NULL); if(!nbh) { ORDER(PUT,proc);/* ensure ordering */ } else {/* aggregate put */ if(nbh->agg_flag == SET) { if(!direct){ rc= armci_agg_save_strided_descriptor(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, PUT, nbh); POSTPROCESS_STRIDED(tmp_count); return(rc); } } else { /*ORDER(PUT,proc); ensure ordering */ UPDATE_FENCE_INFO(proc); /*set tag and op in the nb handle*/ nbh->tag = GET_NEXT_NBTAG(); nbh->op = PUT; nbh->proc= proc; nbh->bufid=NB_NONE; } } if(!direct){ # ifdef ALLOW_PIN /*if we can pin, we do*/ if(!stride_levels && ARMCI_REGION_BOTH_FOUND(src_ptr,dst_ptr,count[0],armci_clus_id(proc))){ if(nbh) { DO_FENCE(proc,DIRECT_NBPUT); armci_client_direct_send(proc, src_ptr, dst_ptr, count[0], (void **)(&nbh->cmpl_info), nbh->tag,mhloc,mhrem); } else { DO_FENCE(proc,DIRECT_PUT); armci_client_direct_send(proc,src_ptr,dst_ptr,count[0],NULL,0,mhloc,mhrem); } POSTPROCESS_STRIDED(tmp_count); if(put_flag) { PARMCI_Fence(proc); PARMCI_Put(&put_flag->val,put_flag->ptr,sizeof(int),proc); } return 0; } # if 0 && defined(VAPI) # if !defined(PEND_BUFS) if(stride_levels==1 && count[0]>VAPI_SGPUT_MIN_COLUMN && ARMCI_REGION_BOTH_FOUND(src_ptr,dst_ptr,count[0],armci_clus_id(proc))){ if(nbh) { DO_FENCE(proc,DIRECT_NBPUT); } else { DO_FENCE(proc,DIRECT_PUT); } /* printf("%d:Calling two phase send\n", armci_me); */ armci_two_phase_send(proc, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr,count,stride_levels,NULL,nbh,mhloc); if(put_flag) { PARMCI_Fence(proc); PARMCI_Put(&put_flag->val,put_flag->ptr,sizeof(int),proc); } return 0; } # else /*!PEND_BUFS*/ { int i, off; for(i=0; i0); if(i!=0) assert(src_stride_arr[i]>=src_stride_arr[i-1]*count[i]); } off = (stride_levels>0)? src_stride_arr[stride_levels-1]*count[stride_levels] : count[0]; mhloc=mhrem=NULL; if(ARMCI_REGION_BOTH_FOUND(src_ptr,dst_ptr,off,armci_clus_id(proc))) { assert(mhloc != NULL); assert(mhrem != NULL); if(nbh) { DO_FENCE(proc, DIRECT_NBPUT); armci_client_direct_rdma_strided(PUT,proc,src_ptr,src_stride_arr, dst_ptr,dst_stride_arr, count, stride_levels, (void**)&nbh->cmpl_info,nbh->tag, mhloc,mhrem); } else { DO_FENCE(proc, DIRECT_PUT); armci_client_direct_rdma_strided(PUT,proc,src_ptr,src_stride_arr, dst_ptr,dst_stride_arr, count, stride_levels,NULL,0, mhloc,mhrem); } if(put_flag) { PARMCI_Fence(proc); PARMCI_Put(&put_flag->val,put_flag->ptr,sizeof(int),proc); } return 0; } } # endif /*!PEND_BUFS*/ # endif /*VAPI*/ # endif /*ALLOW_PIN*/ } if(!direct){ if(nbh) { DO_FENCE(proc,SERVER_PUT); } else { DO_FENCE(proc,SERVER_NBPUT); } # if defined(DATA_SERVER) && defined(SOCKETS) && defined(USE_SOCKET_VECTOR_API) if(count[0]> LONG_PUT_THRESHOLD && stride_levels>0){ ext_header_t h, *hdr; h.exthdr = put_flag; h.len = sizeof(armci_flag_t); hdr = put_flag?&h:NULL; rc = armci_rem_strided(PUT, NULL, proc, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, hdr,1, nbh); } else # endif /*DATA_SERVER && SOCKETS && USE_SOCKET_VECTOR_API*/ { ext_header_t h,*hdr; h.exthdr = put_flag; h.len = sizeof(armci_flag_t); hdr = put_flag?&h:NULL; if(nbh) { nbh->tag =0; /* packed request is completed locally */ CLEAR_HNDL_FIELD(nbh->cmpl_info); } rc = armci_pack_strided(PUT,NULL,proc,src_ptr,src_stride_arr, dst_ptr,dst_stride_arr, count, stride_levels,hdr,-1,-1,-1,NULL); } } else { if(!nbh && stride_levels == 0) { armci_copy_2D(PUT, proc, src_ptr, dst_ptr, count[0], 1, count[0], count[0]); } else { rc = armci_op_strided( PUT, NULL, proc, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr,count,stride_levels, 0,nbh); } if(put_flag) { /*=>!nbh*/ PARMCI_Fence(proc); PARMCI_Put(&put_flag->val,put_flag->ptr,sizeof(int),proc); } } POSTPROCESS_STRIDED(tmp_count); if(rc) return FAIL6; else return 0; } int PARMCI_PutS( void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int seg_count[], /* number of segments at each stride levels: count[0]=bytes*/ int stride_levels, /* number of stride levels */ int proc /* remote process(or) ID */ ) { #if 1 return _armci_puts(src_ptr,src_stride_arr,dst_ptr,dst_stride_arr, seg_count,stride_levels,proc,NULL,NULL); #else armci_hdl_t nbh; ARMCI_INIT_HANDLE(&nbh); PARMCI_NbPutS(src_ptr,src_stride_arr,dst_ptr,dst_stride_arr,seg_count,stride_levels,proc,&nbh); PARMCI_Wait(&nbh); return 0; #endif } int PARMCI_PutS_flag_dir(void *src_ptr, int src_stride_arr[], void* dst_ptr, int dst_stride_arr[], int seg_count[], int stride_levels, int *flag, int val, int proc) { return PARMCI_PutS_flag(src_ptr, src_stride_arr,dst_ptr,dst_stride_arr, seg_count, stride_levels, flag, val, proc); } int PARMCI_PutS_flag(void *src_ptr, int src_stride_arr[], void* dst_ptr, int dst_stride_arr[], int seg_count[], int stride_levels, int *flag, int val, int proc) { armci_flag_t put_flag; put_flag.val=val; put_flag.ptr=flag; return _armci_puts(src_ptr,src_stride_arr,dst_ptr,dst_stride_arr,seg_count,stride_levels,proc,NULL,&put_flag); } int PARMCI_GetS( void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int seg_count[], /* number of segments at each stride levels: count[0]=bytes*/ int stride_levels, /* number of stride levels */ int proc /* remote process(or) ID */ ) { armci_hdl_t nbh; ORDER(GET,proc); ARMCI_INIT_HANDLE(&nbh); PARMCI_NbGetS(src_ptr,src_stride_arr,dst_ptr,dst_stride_arr,seg_count,stride_levels,proc,&nbh); PARMCI_Wait(&nbh); return 0; } /**Internal strided accumulate. Implicit handles should be used * outsise this function. * @param optype operation * @param scale scale factor x += scale*y * @param src_ptr pointer to 1st segment at source * @param src_stride_arr[] array of strides at source * @param dst_ptr 1st segment at destination * @param dst_stride_arr[] array of strides at destination * @param seg_count[] number of segments at each stride * levels: count[0]=bytes * @param stride_levels number of stride levels * @param proc remote process(or) ID * @param nbh armci non-blocking call handle * @return */ static int _armci_accs( int optype, void *scale, void *src_ptr, int src_stride_arr[], void* dst_ptr, int dst_stride_arr[], int seg_count[],int stride_levels, int proc, armci_ihdl_t nbh) { int rc, direct=1; int *count=seg_count, tmp_count=0; if(src_ptr == NULL || dst_ptr == NULL) return FAIL; if(src_stride_arr == NULL || dst_stride_arr ==NULL) return FAIL2; if(count[0]<0)return FAIL3; if(stride_levels <0 || stride_levels > MAX_STRIDE_LEVEL) return FAIL4; if(proc<0)return FAIL5; if(!nbh) { ORDER(optype,proc); } else { UPDATE_FENCE_INFO(proc); nbh->tag = GET_NEXT_NBTAG(); nbh->op = optype; nbh->proc= proc; nbh->bufid=NB_NONE; } PREPROCESS_STRIDED(tmp_count); direct=SAMECLUSNODE(proc); #if defined(ACC_COPY) if(armci_me != proc) direct=0; #endif /*ACC_COPY*/ if(direct) { rc = armci_op_strided(optype,scale, proc, src_ptr, src_stride_arr,dst_ptr, dst_stride_arr, count, stride_levels,1,NULL); } else { if(nbh) { DO_FENCE(proc,SERVER_NBPUT); } else { DO_FENCE(proc,SERVER_PUT); } rc = armci_pack_strided(optype,scale,proc,src_ptr, src_stride_arr,dst_ptr, dst_stride_arr,count,stride_levels,NULL,-1,-1,-1,nbh); } POSTPROCESS_STRIDED(tmp_count); if(rc) return FAIL6; else return 0; } int PARMCI_AccS( int optype, /* operation */ void *scale, /* scale factor x += scale*y */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int seg_count[], /* number of segments at each stride levels: count[0]=bytes*/ int stride_levels, /* number of stride levels */ int proc /* remote process(or) ID */ ) { return _armci_accs(optype,scale,src_ptr,src_stride_arr,dst_ptr, dst_stride_arr,seg_count,stride_levels,proc,NULL); } int PARMCI_Put(void *src, void* dst, int bytes, int proc) { int rc=0; rc = PARMCI_PutS(src, NULL, dst, NULL, &bytes, 0, proc); return rc; } int PARMCI_Acc(int optype, void *scale, void *src, void* dst, int bytes, int proc) { int rc=0; rc = PARMCI_AccS(optype, scale, src, NULL, dst, NULL, &bytes, 0, proc); return rc; } int PARMCI_Put_flag(void *src, void* dst,int bytes,int *f,int v,int proc) { return PARMCI_PutS_flag(src, NULL, dst, NULL, &bytes, 0, f, v, proc); } int PARMCI_Get(void *src, void* dst, int bytes, int proc) { int rc=0; rc = PARMCI_GetS(src, NULL, dst, NULL, &bytes, 0, proc); dassert(1,rc==0); return rc; } #define PACK1D 1 #if PACK1D # define armci_read_strided1 armci_read_strided # define armci_write_strided1 armci_write_strided #else # define armci_read_strided2 armci_read_strided # define armci_write_strided2 armci_write_strided #endif void armci_write_strided1(void *ptr, int stride_levels, int stride_arr[], int count[], char *buf) { const int seg_size = count[0]; int off=0; stride_info_t sinfo; armci_stride_info_init(&sinfo, ptr,stride_levels,stride_arr,count); while(armci_stride_info_has_more(&sinfo)) { char *sptr = armci_stride_info_seg_ptr(&sinfo); armci_copy(sptr,&buf[off],seg_size); off += seg_size; armci_stride_info_next(&sinfo); } armci_stride_info_destroy(&sinfo); } void armci_write_strided2(void *ptr, int stride_levels, int stride_arr[], int count[], char *buf) { int i, j; int total; /* number of 2 dim block */ int index[MAX_STRIDE_LEVEL], unit[MAX_STRIDE_LEVEL]; if(stride_levels == 0){ armci_copy( ptr, buf, count[0]); }else if (count[0]%ALIGN_SIZE || (unsigned long)ptr%ALIGN_SIZE ) armci_write_strided1(ptr,stride_levels, stride_arr,count,buf); else { int rows, ld, idx, ldd; char *src; rows = count[0]/8; ld = stride_arr[0]/8; switch(stride_levels){ case 1: DCOPY21(&rows, count+1, ptr, &ld, (void*)buf, &idx); break; case 2: #if 0 for(i=0; i< count[2]; i++){ DCOPY21(&rows, count+1, ptr, &ld, buf, &idx); ptr = ((char*)ptr)+stride_arr[1]; buf = (char*) ((double*)buf + idx); } #endif ldd = stride_arr[1]/stride_arr[0]; DCOPY31(&rows, count+1, count+2, ptr, &ld, &ldd, (void*)buf, &idx); break; default: index[2] = 0; unit[2] = 1; total = count[2]; for(j=3; j<=stride_levels; j++) { index[j] = 0; unit[j] = unit[j-1] * count[j-1]; total *= count[j]; } for(i=0; i= count[j]) index[j] = 0; } DCOPY21(&rows, count+1, (void*)src, &ld, (void*)buf, &idx); buf = (char*) ((double*)buf + idx); } } /*switch */ } /*else */ } void armci_read_strided1(void *ptr, int stride_levels, int stride_arr[], int count[], char *buf) { const int seg_size = count[0]; int off=0; stride_info_t sinfo; armci_stride_info_init(&sinfo,ptr,stride_levels,stride_arr,count); while(armci_stride_info_has_more(&sinfo)) { char *dptr = armci_stride_info_seg_ptr(&sinfo); armci_copy(&buf[off],dptr,seg_size); off += seg_size; armci_stride_info_next(&sinfo); } armci_stride_info_destroy(&sinfo); } void armci_read_strided2(void *ptr, int stride_levels, int stride_arr[], int count[], char *buf) { int i, j; int total; /* number of 2 dim block */ int index[MAX_STRIDE_LEVEL], unit[MAX_STRIDE_LEVEL]; if(stride_levels == 0){ armci_copy( buf, ptr, count[0]); }else if (count[0]%ALIGN_SIZE || (unsigned long)ptr%ALIGN_SIZE) armci_read_strided1(ptr,stride_levels, stride_arr,count,buf); else { int rows, ld, idx, ldd; char *src; rows = count[0]/8; ld = stride_arr[0]/8; switch(stride_levels){ case 1: DCOPY12(&rows, count+1, ptr, &ld, (void*)buf, &idx); break; case 2: #if 0 for(i=0; i< count[2]; i++){ DCOPY12(&rows, count+1, ptr, &ld, buf, &idx); ptr = ((char*)ptr)+stride_arr[1]; buf = (char*) ((double*)buf + idx); } #endif ldd = stride_arr[1]/stride_arr[0]; DCOPY13(&rows, count+1, count+2, ptr, &ld, &ldd, (void*)buf, &idx); break; default: index[2] = 0; unit[2] = 1; total = count[2]; for(j=3; j<=stride_levels; j++) { index[j] = 0; unit[j] = unit[j-1] * count[j-1]; total *= count[j]; } for(i=0; i= count[j]) index[j] = 0; } DCOPY12(&rows, count+1, (void*)src, &ld, (void*)buf, &idx); buf = (char*) ((double*)buf + idx); } } /*switch */ } /*else */ } /**Read data from buffer into the locations pointed to by the stride * iterator. The reading happens incrementally. The stride iterator is * traversed to copy as much data as possible in the buffer. When all * the data in buf is consumed the function returns with the number of * bytes consumed from the buffer. * @param sinfo Stride iterator * @param buf IN Pointer to data to be read into user memory * @param bytes IN #bytes available in buf for reading * @param seg_off INOUT Bytes of the current segment written in the * last call (on partial segment write). On return, this parameter * contains the bytes of the last segment written if it was partial. * @return #bytes read from buf into user memory. */ int armci_read_strided_inc(stride_info_t *sinfo, const char *buf,int bytes, int *seg_off) { int off=0; const int seg_size = armci_stride_info_seg_size(sinfo); dassert1(1,bytes>0,bytes); off=0; if(*seg_off) { char *sptr = (char*) &buf[off]; char *dptr = ((char*)armci_stride_info_seg_ptr(sinfo))+*seg_off; int size = ARMCI_MIN(seg_size-*seg_off,bytes); /* printf("%d:%s(): seg_size=%d,seg_off=%d,bytes=%d\n",armci_me,FUNCTION_NAME,seg_size,*seg_off,bytes); */ dassert(1,armci_stride_info_has_more(sinfo)); armci_copy(sptr,dptr,size); off += size; if(*seg_off+size == seg_size) { armci_stride_info_next(sinfo); } } while(bytes>off) { int size = ARMCI_MIN(seg_size, bytes-off); dassert(1,armci_stride_info_has_more(sinfo)); armci_copy(&buf[off],armci_stride_info_seg_ptr(sinfo),size); if(size==seg_size) armci_stride_info_next(sinfo); off += size; } dassertp(1,off==bytes,("%d:off=%d bytes=%d",armci_me,off,bytes)); *seg_off = (bytes + *seg_off) % seg_size; return bytes; } #define INIT_NB_HANDLE(nb,o,p) if(nb){ \ (nb)->tag = 0; \ (nb)->op = (o); (nb)->proc= (p); \ (nb)->bufid=NB_NONE;} \ else { (nb)=(armci_ihdl_t)armci_set_implicit_handle(o, p); (nb)->tag=0; } /*\Non-Blocking API \*/ int PARMCI_NbPutS( void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int seg_count[], /* number of segments at each stride levels: count[0]=bytes*/ int stride_levels, /* number of stride levels */ int proc, /* remote process(or) ID */ armci_hdl_t* usr_hdl /* armci non-blocking call handle*/ ) { if(!usr_hdl) usr_hdl = armci_set_implicit_handle(PUT, proc); return _armci_puts(src_ptr, src_stride_arr,dst_ptr,dst_stride_arr, seg_count,stride_levels,proc,(armci_ihdl_t)usr_hdl,NULL); } int PARMCI_NbGetS( void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int seg_count[], /* number of segments at each stride levels: byte_count[0]=bytes*/ int stride_levels, /* number of stride levels */ int proc, /* remote process(or) ID */ armci_hdl_t* usr_hdl /* armci non-blocking call handle*/ ) { armci_ihdl_t nb_handle = (armci_ihdl_t)usr_hdl; int rc=0,direct=1; int *count=seg_count, tmp_count=0; if(src_ptr == NULL || dst_ptr == NULL) return FAIL; if(seg_count[0]<0)return FAIL3; if(stride_levels <0 || stride_levels > MAX_STRIDE_LEVEL) return FAIL4; if(proc<0)return FAIL5; direct=SAMECLUSNODE(proc); PREPROCESS_STRIDED(tmp_count); /* aggregate get */ if(nb_handle && nb_handle->agg_flag == SET) { if(!direct){ rc= armci_agg_save_strided_descriptor(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, GET, nb_handle); POSTPROCESS_STRIDED(tmp_count); return(rc); } } else { /* ORDER(GET,proc); ensure ordering */ UPDATE_FENCE_INFO(proc); /*set tag and op in the nb handle*/ if(nb_handle){ nb_handle->tag = GET_NEXT_NBTAG(); nb_handle->op = GET; nb_handle->proc= proc; nb_handle->bufid=NB_NONE; } else nb_handle = (armci_ihdl_t)armci_set_implicit_handle(GET, proc); } if(!direct){ # ifdef ALLOW_PIN if(!stride_levels && ARMCI_REGION_BOTH_FOUND(dst_ptr,src_ptr,count[0],armci_clus_id(proc))){ DO_FENCE(proc,DIRECT_NBGET); ARMCI_NBREM_GET(proc, src_ptr,NULL,dst_ptr,NULL,count, 0, nb_handle); POSTPROCESS_STRIDED(tmp_count); return 0; } # endif } if(!direct){ DO_FENCE(proc,SERVER_NBGET); #if defined(DATA_SERVER) && (defined(SOCKETS) || defined(CLIENT_BUF_BYPASS) ) /* for larger strided or 1D reqests buffering can be avoided to send data * we can try to bypass the packetization step and send request directly */ rc = armci_pack_strided(GET, NULL, proc, src_ptr, src_stride_arr, dst_ptr,dst_stride_arr,count,stride_levels, NULL,-1,-1,-1,nb_handle); } else #else /* avoid LAPI_GetV */ if(stride_levels==1 && count[0]>320 && !direct) { ARMCI_REM_GET(proc,src_ptr,src_stride_arr,dst_ptr, dst_stride_arr, count, stride_levels, nb_handle); } else #endif { rc = armci_op_strided(GET, NULL, proc, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr,count, stride_levels,0,nb_handle); } POSTPROCESS_STRIDED(tmp_count); if(rc) return FAIL6; else return 0; } int PARMCI_NbAccS( int optype, /* operation */ void *scale, /* scale factor x += scale*y */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int seg_count[], /* number of segments at each stride levels: count[0]=bytes*/ int stride_levels, /* number of stride levels */ int proc, /* remote process(or) ID */ armci_hdl_t* usr_hdl /* armci non-blocking call handle*/ ) { if(!usr_hdl) usr_hdl = armci_set_implicit_handle(optype, proc); return _armci_accs(optype,scale,src_ptr,src_stride_arr,dst_ptr, dst_stride_arr,seg_count,stride_levels,proc,(armci_ihdl_t)usr_hdl); } #if !defined(ACC_COPY)&&!defined(CYGNUS)&&!defined(CYGWIN) # define REMOTE_OP #endif void set_nbhandle(armci_ihdl_t *nbh, armci_hdl_t *nb_handle, int op, int proc) { if(nb_handle) { *nbh=(armci_ihdl_t)nb_handle; } else { *nbh=(armci_ihdl_t)armci_set_implicit_handle(op, proc); } } int PARMCI_NbPut(void *src, void* dst, int bytes, int proc,armci_hdl_t* uhandle) { int rc; rc = PARMCI_NbPutS(src,NULL,dst,NULL,&bytes,0,proc,uhandle); return(rc); } int PARMCI_NbGet(void *src, void* dst, int bytes, int proc,armci_hdl_t* uhandle) { int rc; rc=PARMCI_NbGetS(src,NULL,dst,NULL,&bytes,0,proc,uhandle); return(rc); } static void _armci_op_value(int op, void *src, void *dst, int proc, int bytes, armci_hdl_t *usr_hdl) { int rc=0,pv=0; armci_ihdl_t nbh = (armci_ihdl_t)usr_hdl; if(!nbh) { ORDER(op,proc); /* ensure ordering */ }else { if(nbh->agg_flag == SET) { if(op==PUT) pv = 1; (void)armci_agg_save_descriptor(src,dst,bytes,proc,op,pv,nbh); return; } else { if(op==PUT) UPDATE_FENCE_INFO(proc); /*set tag and op in the nb handle*/ nbh->tag = GET_NEXT_NBTAG(); nbh->op = op; nbh->proc= proc; nbh->bufid=NB_NONE; } } #if defined(REMOTE_OP) rc = armci_rem_strided(op, NULL, proc, src, NULL, dst, NULL, &bytes, 0, NULL, 0, nbh); if(rc) armci_die("ARMCI_Value: armci_rem_strided incomplete", FAIL6); #else if(op==PUT) { UPDATE_FENCE_STATE(proc, PUT, 1); armci_put(src, dst, bytes, proc); } else { armci_get(src, dst, bytes, proc); } #endif } static void _armci_rem_value(int op, void *src, void *dst, int proc, int bytes) { _armci_op_value(op,src,dst,proc,bytes,NULL); } /* non-blocking remote value put/get operation */ static void _armci_nb_rem_value(int op, void *src, void *dst, int proc, int bytes, armci_hdl_t *usr_hdl) { if(!usr_hdl) usr_hdl = (armci_hdl_t*)armci_set_implicit_handle(op,proc); _armci_op_value(op,src,dst,proc,bytes,usr_hdl); } #define CHK_ERR(dst, proc) \ if(dst==NULL) armci_die("ARMCI_PutValue: NULL pointer passed",FAIL); \ if(proc<0) armci_die("ARMCI_PutValue: Invalid process rank", proc); #define CHK_ERR_GET(src, dst, proc, bytes) \ if(src==NULL || dst==NULL) armci_die("ARMCI_GetValue: NULL pointer passed",FAIL); \ if(proc<0) armci_die("ARMCI_GetValue: Invalid process rank", proc); \ if(bytes<0) armci_die("ARMCI_GetValue: Invalid size", bytes); /** * Register-Originated Put. */ int PARMCI_PutValueInt(int src, void *dst, int proc) { CHK_ERR(dst, proc); if( SAMECLUSNODE(proc) ) *(int *)dst = src; else _armci_rem_value(PUT, &src, dst, proc, sizeof(int)); return 0; } int PARMCI_PutValueLong(long src, void *dst, int proc) { CHK_ERR(dst, proc); if( SAMECLUSNODE(proc) ) *(long *)dst = src; else _armci_rem_value(PUT, &src, dst, proc, sizeof(long)); return 0; } int PARMCI_PutValueFloat(float src, void *dst, int proc) { CHK_ERR(dst, proc); if( SAMECLUSNODE(proc) ) *(float *)dst = src; else _armci_rem_value(PUT, &src, dst, proc, sizeof(float)); return 0; } int PARMCI_PutValueDouble(double src, void *dst, int proc) { CHK_ERR(dst, proc); if( SAMECLUSNODE(proc) ) *(double *)dst = src; else _armci_rem_value(PUT, &src, dst, proc, sizeof(double)); return 0; } /** * Non-Blocking register-originated put. */ int PARMCI_NbPutValueInt(int src, void *dst, int proc, armci_hdl_t* usr_hdl) { CHK_ERR(dst, proc); if( SAMECLUSNODE(proc) ) *(int *)dst = src; else _armci_nb_rem_value(PUT,&src,dst,proc,sizeof(int),usr_hdl); return 0; } int PARMCI_NbPutValueLong(long src, void *dst, int proc, armci_hdl_t* usr_hdl) { CHK_ERR(dst, proc); if( SAMECLUSNODE(proc) ) *(long *)dst = src; else _armci_nb_rem_value(PUT,&src,dst,proc,sizeof(long),usr_hdl); return 0; } int PARMCI_NbPutValueFloat(float src, void *dst, int proc, armci_hdl_t* usr_hdl) { CHK_ERR(dst, proc); if( SAMECLUSNODE(proc) ) *(float *)dst = src; else _armci_nb_rem_value(PUT,&src,dst,proc,sizeof(float),usr_hdl); return 0; } int PARMCI_NbPutValueDouble(double src, void *dst, int proc, armci_hdl_t* usr_hdl) { CHK_ERR(dst, proc); if( SAMECLUSNODE(proc) ) *(double *)dst = src; else _armci_nb_rem_value(PUT,&src,dst,proc,sizeof(double),usr_hdl); return 0; } /** * Register-Originated Get. */ int PARMCI_GetValueInt(void *src, int proc) { int dst; if (SAMECLUSNODE(proc)) return *(int *)src; else _armci_rem_value(GET, src, &dst, proc, sizeof(int)); return dst; } long PARMCI_GetValueLong(void *src, int proc) { long dst; if (SAMECLUSNODE(proc)) return *(long *)src; else _armci_rem_value(GET, src, &dst, proc, sizeof(long)); return dst; } float PARMCI_GetValueFloat(void *src, int proc) { float dst; if (SAMECLUSNODE(proc)) return *(float *)src; else _armci_rem_value(GET, src, &dst, proc, sizeof(float)); return dst; } double PARMCI_GetValueDouble(void *src, int proc) { double dst; if(SAMECLUSNODE(proc)) return *(double *)src; else _armci_rem_value(GET, src, &dst, proc, sizeof(double)); return dst; } ga-5.9.2/armci/src/xfer/vector.c000066400000000000000000000442671500715745200164350ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: vector.c,v 1.32.6.4 2007-08-29 17:32:32 manoj Exp $ */ #include "armcip.h" #include "copy.h" #include "acc.h" #include "memlock.h" #if HAVE_STDIO_H # include #endif #if HAVE_ASSERT_H # include #endif /* void I_ACCUMULATE(void* scale, int elems, void*src, void* dst) { int j; int *a=(int*)dst, *b=(int*)src; int alpha = *(int*)scale; for(j=0;j BUFSIZE/2){ /* for large segments use strided implementation */ for(j=0; j< dr.ptr_array_len; j++){ rc = armci_acc_copy_strided(op, scale,proc, dr.src_ptr_array[j], NULL, dr.dst_ptr_array[j],NULL, &dr.bytes, 0); if(rc)return(rc); } }else{ armci_giov_t dl; /*lock memory:should optimize it to lock only a chunk at a time*/ armci_lockmem_scatter(dr.dst_ptr_array, dr.ptr_array_len, dr.bytes, proc); /* copy as many blocks as possible into the local buffer */ dl.bytes = dr.bytes; nb = ARMCI_MIN(PWORKLEN,BUFSIZE/dr.bytes); for(j=0; j< dr.ptr_array_len; j+= nb){ int nblocks = ARMCI_MIN(nb, dr.ptr_array_len -j); int k; /* setup vector descriptor for remote memory copy to bring data into buffer*/ dl.ptr_array_len = nblocks; dl.src_ptr_array = dr.dst_ptr_array + j; /* GET destination becomes source for copy */ for(k=0; k< nblocks; k++) pwork[k] = k*dl.bytes + (char*)armci_internal_buffer; dl.dst_ptr_array = pwork; /* get data to the local buffer */ rc = armci_copy_vector(GET, &dl, 1, proc); if(rc){ ARMCI_UNLOCKMEM(proc); return(rc);} /* update source array for accumulate */ dl.src_ptr_array = dr.src_ptr_array +j; /* do scatter accumulate updating copy of data in buffer */ armci_scatter_acc(op, scale, dl, armci_me, 0); /* modify descriptor-now source becomes destination for PUT*/ dl.dst_ptr_array = dr.dst_ptr_array + j; dl.src_ptr_array = pwork; /* put data back */ rc = armci_copy_vector(PUT, &dl, 1, proc); FENCE_NODE(proc); if(rc){ ARMCI_UNLOCKMEM(proc); return(rc);} } ARMCI_UNLOCKMEM(proc); } }/*endfor*/ } #endif return 0; } int armci_copy_vector(int op, /* operation code */ armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ) { int i,s,shmem= SAMECLUSNODE(proc); if(shmem ){ /* local/shared memory copy */ for(i = 0; i< len; i++){ for( s=0; s< darr[i].ptr_array_len; s++){ armci_copy(darr[i].src_ptr_array[s],darr[i].dst_ptr_array[s],darr[i].bytes); } } }else { /* access through global address space */ /* March 19 - removed FENCE from here - it is in ORDER inside armci.c */ switch(op){ case PUT: for(i = 0; i< len; i++){ UPDATE_FENCE_STATE(proc, PUT, darr[i].ptr_array_len); for( s=0; s< darr[i].ptr_array_len; s++){ armci_put(darr[i].src_ptr_array[s],darr[i].dst_ptr_array[s], darr[i].bytes, proc); } } break; case GET: for(i = 0; i< len; i++){ for( s=0; s< darr[i].ptr_array_len; s++){ armci_get(darr[i].src_ptr_array[s],darr[i].dst_ptr_array[s], darr[i].bytes,proc); } } break; default: armci_die("armci_copy_vector: wrong optype",op); } } return 0; } void armci_vector_to_buf(armci_giov_t darr[], int len, void* buf) { int i,s; char *ptr = (char*)buf; for(i = 0; i< len; i++){ for( s=0; s< darr[i].ptr_array_len; s++){ armci_copy(darr[i].src_ptr_array[s],ptr,darr[i].bytes); ptr += darr[i].bytes; } } } void armci_vector_from_buf(armci_giov_t darr[], int len, void* buf) { int i,s; char *ptr = (char*)buf; for(i = 0; i< len; i++){ for( s=0; s< darr[i].ptr_array_len; s++){ armci_copy(ptr, darr[i].dst_ptr_array[s],darr[i].bytes); ptr += darr[i].bytes; } } } int PARMCI_PutV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ) { int rc=0, i,direct=1; #if defined(USE_SOCKET_VECTOR_API) int totvec=0; #endif if(len<1) return FAIL; for(i=0;i= armci_nproc)return FAIL5; ORDER(PUT,proc); /* ensure ordering */ direct=SAMECLUSNODE(proc); /* use direct protocol for remote access when performance is better */ if (direct) { rc = armci_copy_vector(PUT, darr, len, proc); } else { #if defined(DATA_SERVER) && defined(SOCKETS) && defined(USE_SOCKET_VECTOR_API) /* 500 is very conservative, the number here should be modified to be based on the size of send/recv buffer */ if(totvec<500) { rc = armci_rem_vector(PUT, NULL, darr, len, proc, 1,NULL); } else #endif { rc = armci_pack_vector(PUT, NULL, darr, len, proc,NULL); } } if (rc) { return FAIL6; } return 0; } int PARMCI_GetV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ) { int rc=0, i,direct=1; #if defined(USE_SOCKET_VECTOR_API) int totvec=0; #endif if(len<1) return FAIL; for(i=0;i= armci_nproc)return FAIL5; ORDER(GET,proc); /* ensure ordering */ direct=SAMECLUSNODE(proc); /* use direct protocol for remote access when performance is better */ if (direct) { rc = armci_copy_vector(GET, darr, len, proc); } else { #if defined(DATA_SERVER) && defined(SOCKETS) && defined(USE_SOCKET_VECTOR_API) /* 500 is very conservative, the number here should be modified to be based on the size of send/recv buffer*/ if (totvec<500) { rc = armci_rem_vector(GET, NULL, darr, len, proc,1,NULL); } else #endif { rc = armci_pack_vector(GET, NULL, darr, len, proc,NULL); } } if(rc) { return FAIL6; } return 0; } int PARMCI_AccV( int op, /* oeration code */ void *scale, /*scaling factor for accumulate */ armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ) { int rc=0, i,direct=1; if(len<1) return FAIL; for(i=0;i= armci_nproc)return FAIL5; ORDER(op,proc); /* ensure ordering */ direct=SAMECLUSNODE(proc); # if defined(ACC_COPY) if(armci_me != proc) direct=0; # endif if (direct) { rc = armci_acc_vector( op, scale, darr, len, proc); } else { rc = armci_pack_vector(op, scale, darr, len, proc,NULL); } if (rc) { return FAIL6; } return 0; } /*****************************************************************************/ /*\ Non-blocking vector API \*/ int PARMCI_NbPutV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc, /* remote process(or) ID */ armci_hdl_t* usr_hdl /*non-blocking request handle*/ ) { armci_ihdl_t nb_handle = (armci_ihdl_t)usr_hdl; int rc=0, i,direct=1; #if defined(USE_SOCKET_VECTOR_API) int totvec=0; #endif if(len<1) return FAIL; for(i=0;i= armci_nproc)return FAIL5; direct=SAMECLUSNODE(proc); /* aggregate put */ if(nb_handle && nb_handle->agg_flag == SET) { if(!direct) { rc=armci_agg_save_giov_descriptor(darr, len, proc, PUT, nb_handle); return rc; } } else { /*ORDER(PUT,proc); ensure ordering */ UPDATE_FENCE_INFO(proc); /*set tag and op in the nb handle*/ if(nb_handle){ nb_handle->tag = GET_NEXT_NBTAG(); nb_handle->op = PUT; nb_handle->proc= proc; nb_handle->bufid=NB_NONE; } else nb_handle = (armci_ihdl_t)armci_set_implicit_handle(PUT, proc); } if (direct) { rc = armci_copy_vector(PUT, darr, len, proc); } else{ #if defined(DATA_SERVER) && defined(SOCKETS) && defined(USE_SOCKET_VECTOR_API) /* 500 is very conservative, the number here should be modified to be based on the size of send/recv buffer */ if (totvec<500) { rc = armci_rem_vector(PUT, NULL, darr, len, proc, 1,nb_handle); } else #endif { rc = armci_pack_vector(PUT, NULL, darr, len, proc,nb_handle); } } if(rc) return FAIL6; else return 0; } int PARMCI_NbGetV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc, /* remote process(or) ID */ armci_hdl_t* usr_hdl /*non-blocking request handle*/ ) { armci_ihdl_t nb_handle = (armci_ihdl_t)usr_hdl; int rc=0, i,direct=1; #if defined(USE_SOCKET_VECTOR_API) int totvec=0; #endif if(len<1) return FAIL; for(i=0;i= armci_nproc)return FAIL5; direct=SAMECLUSNODE(proc); /* aggregate get */ if(nb_handle && nb_handle->agg_flag == SET) { if(!direct) { rc=armci_agg_save_giov_descriptor(darr, len, proc, GET, nb_handle); return rc; } } else { /* ORDER(GET,proc); ensure ordering */ if(nb_handle){ nb_handle->tag = GET_NEXT_NBTAG(); nb_handle->op = GET; nb_handle->proc= proc; nb_handle->bufid=NB_NONE; } else nb_handle = (armci_ihdl_t)armci_set_implicit_handle(GET, proc); } if (direct) { rc = armci_copy_vector(GET, darr, len, proc); } else{ #if defined(DATA_SERVER) && defined(SOCKETS) && defined(USE_SOCKET_VECTOR_API) /* 500 is very conservative, the number here should be modified to be based on the size of send/recv buffer */ if (totvec<500) { rc = armci_rem_vector(GET, NULL, darr, len, proc,1,nb_handle); } else #endif { rc = armci_pack_vector(GET, NULL, darr, len, proc,nb_handle); } } if(rc) return FAIL6; else return 0; } int PARMCI_NbAccV( int op, /* oeration code */ void *scale, /*scaling factor for accumulate */ armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc, /* remote process(or) ID */ armci_hdl_t* usr_hdl /*non-blocking request handle*/ ) { armci_ihdl_t nb_handle = (armci_ihdl_t)usr_hdl; int rc=0, i,direct=1; if(len<1) return FAIL; for(i=0;i= armci_nproc)return FAIL5; /* ORDER(op,proc); ensure ordering */ UPDATE_FENCE_INFO(proc); direct=SAMECLUSNODE(proc); if(nb_handle){ nb_handle->tag = GET_NEXT_NBTAG(); nb_handle->op = op; nb_handle->proc= proc; nb_handle->bufid=NB_NONE; } else nb_handle = (armci_ihdl_t)armci_set_implicit_handle(op, proc); # if defined(ACC_COPY) if(armci_me != proc) direct=0; # endif if (direct) { rc = armci_acc_vector( op, scale, darr, len, proc); } else { rc = armci_pack_vector(op, scale, darr, len, proc,nb_handle); } if (rc) { return FAIL6; } return 0; } /*****************************************************************************/ ga-5.9.2/armci/testing/000077500000000000000000000000001500715745200146745ustar00rootroot00000000000000ga-5.9.2/armci/testing/fttest.c000066400000000000000000000053321500715745200163540ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_ASSERT_H # include #endif #include "armci.h" #include "message.h" #define MAXPROCS 128 #define SIZE_ 1024 #define FAILURE_SIZE_ 512 int me, nproc; double *ptr_arr[MAXPROCS]; long size; void do_work(int sz) { int i; static int d = 1; for (i = 0; i < sz; i++) { *((double *)(ptr_arr[me]) + i) = i + 1.12 * d++; } } static double time_array[100], time_array1[100], t1; int main(int argc, char *argv[]) { int rc, i, j = 0, rid, ret; armci_ckpt_ds_t ckptds; ARMCI_Group grp; armci_msg_init(&argc, &argv); ARMCI_Init_args(&argc, &argv); nproc = armci_msg_nproc(); me = armci_msg_me(); if (me == 0) { if (nproc > MAXPROCS) { ARMCI_Error("nproc > MAXPROCS", nproc); } else { printf("ARMCI test program (%d processes)\n", nproc); fflush(stdout); sleep(1); } } armci_init_checkpoint2(); ARMCI_Group_get_world(&grp); size = SIZE_; rc = ARMCI_Malloc((void **)ptr_arr, size * 8); printf("ARMCI test program (%d processes)\n", nproc); fflush(stdout); for (size = 1; size <= SIZE_; size *= 2) { t1 = MPI_Wtime(); for (i = 0; i < 5; i++) { for (rc = 0; rc < 15; rc++) { do_work(size); } } time_array[j++] = MPI_Wtime() - t1; ARMCI_Barrier(); printf("%d:done for size %ld\n", me, size); fflush(stdout); } (void)ARMCI_Ckpt_create_ds(&ckptds, 1); ckptds.ptr_arr[0] = ptr_arr[me]; ckptds.sz[0] = SIZE_ * 8; rid = ARMCI_Ckpt_init(NULL, &grp, 1, 0, &ckptds); printf("%d: After ARMCI_Ckpt_init(): \n", me); j = 0; for (size = 128; size <= SIZE_; size *= 2) { int rc; int simulate_restart = 1; t1 = MPI_Wtime(); ret = ARMCI_Ckpt(rid); if (ret == ARMCI_CKPT) { printf("%d: Performed CHECKPOINT @ size=%ld\n", me, size); } else if (ret == ARMCI_RESTART) { simulate_restart = 0; printf("%d: Performed RESTART @ size=%ld\n", me, size); } for (i = 0; i < 5; i++) { for (rc = 0; rc < 15; rc++) if (i == 3 && rc == 10) { } do_work(size); } time_array1[j++] = MPI_Wtime() - t1; sleep(1); if (simulate_restart && size == FAILURE_SIZE_) { printf("%d: Simulating FAILURE @ size = %d\n", me, size); ARMCI_Restart_simulate(rid, 1); } printf("%d: DONE for size=%ld regular=%f withckpt=%f\n\n", me, size, time_array[j-1], time_array1[j-1]); fflush(stdout); } ARMCI_Ckpt_finalize(rid); printf("Before Finalize()\n"); ARMCI_Barrier(); ARMCI_Finalize(); armci_msg_finalize(); return(0); } ga-5.9.2/armci/testing/gpctest.c000066400000000000000000000072241500715745200165160ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: gpctest.c,v 1.2.4.1 2007-06-13 00:44:01 vinod Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_UNISTD_H # include #endif /*#define RMW*/ #include "armci.h" #include "gpc.h" #include "message.h" #define MAXPROC 128 # define ELEMS 200 #define LOOP 100 /***************************** global data *******************/ int me, nproc; void *work[MAXPROC]; /* work array for propagating addresses */ int hswap = 0; void gpc_swap(int *loc, int *rem, int p) { gpc_hdl_t nbh; char rheader[100]; int hlen, rhlen, rhsize; int rdsize; void *header = rem; extern int hswap; hlen = sizeof(header); bzero(rheader, 100); rhlen = hlen; ARMCI_Gpc_init_handle(&nbh); if (ARMCI_Gpc_exec(hswap, p, &header, hlen, loc, sizeof(int), rheader, rhlen, loc, sizeof(int), NULL/*&nbh*/)) { fprintf(stderr, "ARMCI_Gpc_exec failed\n"); } /*ARMCI_Gpc_wait(&nbh);*/ } int gpc_swap_handler(int to, int from, void *hdr, int hlen, void *data, int dlen, void *rhdr, int rhlen, int *rhsize, void *rdata, int rdlen, int *rdsize, int rtype) { int *rem; int tmp_swap; #ifdef DEBUG_ printf("executing swap handler from=%d to=%d\n"); fflush(stdout); #endif rem = (int *)ARMCI_Gpc_translate(*(void **)hdr, to, from); ARMCI_Gpc_lock(to); tmp_swap = *rem; *rem = *(int *)data; ARMCI_Gpc_unlock(to); *(int *)rdata = tmp_swap; *(int *)rhdr = tmp_swap; /* 2nd copy just for debug purposes */ *rhsize = sizeof(void *); *rdsize = sizeof(int); return GPC_DONE; } #define LOCKED -1 void test_swap() { int rc, bytes, i, val, whatever = -8999; int *arr[MAXPROC]; /* shared variable is located on processor 0 */ bytes = me == 0 ? sizeof(int) : 0; rc = ARMCI_Malloc((void **)arr, bytes); if (rc != 0) { ARMCI_Error("test_swap: ARMCI_Malloc failed", 0); } ARMCI_Barrier(); hswap = ARMCI_Gpc_register(gpc_swap_handler); if (me == 0) { *arr[0] = 0; /* initialization */ } ARMCI_Barrier(); for (i = 0; i < LOOP; i++) { val = LOCKED; do { #ifdef RMW rc = ARMCI_Rmw(ARMCI_SWAP, &val, arr[0], whatever, 0); if (rc != 0) { ARMCI_Error("test_swap: ARMCI_Rmw failed", 0); } #else gpc_swap(&val, arr[0], 0); #endif } while (val == LOCKED); val++; #ifdef RMW rc = ARMCI_Rmw(ARMCI_SWAP, &val, arr[0], whatever, 0); if (rc != 0) { ARMCI_Error("test_swap: ARMCI_Malloc failed", 0); } #else gpc_swap(&val, arr[0], 0); #endif } ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("The final value is %d, should be %d.\n\n", *arr[0], LOOP * nproc); fflush(stdout); if (*arr[0] != LOOP * nproc) { ARMCI_Error("failed ...", *arr[0]); } } ARMCI_Free(arr[me]); } int main(int argc, char *argv[]) { int ndim; armci_msg_init(&argc, &argv); ARMCI_Init_args(&argc, &argv); nproc = armci_msg_nproc(); me = armci_msg_me(); if (nproc > MAXPROC && me == 0) { ARMCI_Error("Test works for up to %d processors\n", MAXPROC); } if (me == 0) { printf("ARMCI Global Procedure Call test program (%d processes)\n", nproc); fflush(stdout); sleep(1); } if (me == 0) { #ifdef RMW printf("\nTesting atomic swap using ARMCI_Rmw\n"); #else printf("\nTesting atomic swap using GPC\n"); #endif fflush(stdout); } ARMCI_Barrier(); test_swap(); ARMCI_AllFence(); ARMCI_Barrier(); ARMCI_Finalize(); armci_msg_finalize(); return(0); } ga-5.9.2/armci/testing/ipctest.c000066400000000000000000000043671500715745200165250ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: ipctest.c,v 1.2 1999-07-28 00:47:55 d3h325 Exp $ */ #if HAVE_STDIO_H # include #endif #include #if HAVE_ASSERT_H # include #endif #include "armcip.h" #include "armci_shmem.h" #include "locks.h" #include "message.h" int me, nproc; void test() { double *a = NULL, start = 1., end = -1.; int len = 100; long size = len * sizeof(double); long idlist[SHMIDLEN]; int numlock = 10, i; lockset_t lockid; /* shared memory test */ if (me == 0) { printf("Test shared memory\n"); a = (double *)Create_Shared_Region(idlist + 1, size, idlist); assert(a); a[0] = start; a[len-1] = end; } MPI_Bcast(idlist, SHMIDLEN, MPI_LONG, 0, MPI_COMM_WORLD); if (me) { a = (double *)Attach_Shared_Region(idlist + 1, size, idlist[0]); assert(a); } if (me == nproc - 1) { printf("%d: start=%f end=%f\n", me, a[0], a[len-1]); if (a[0] == start && a[len-1] == end) { printf("Works!\n"); } } /*printf("%d: a=%x\n",me,a); */ MPI_Barrier(MPI_COMM_WORLD); /* allocate locks */ if (me == 0) { a[0] = 0.; CreateInitLocks(numlock, &lockid); printf("\nMutual exclusion test\n"); } MPI_Bcast(&lockid, sizeof(lockid), MPI_BYTE, 0, MPI_COMM_WORLD); if (me) { InitLocks(numlock, lockid); } /* mutual exclusion test: * everybody increments shared variable 1000 times */ # define TIMES 1000 MPI_Barrier(MPI_COMM_WORLD); for (i = 0; i < TIMES; i++) { NATIVE_LOCK(0, me); a[0]++; NATIVE_UNLOCK(0, me); } MPI_Barrier(MPI_COMM_WORLD); if (me == nproc - 1) { printf("value of shared variable =%f should be %f\n", a[0], 1.0 * nproc * TIMES); if (a[0] == 1.0 * nproc * TIMES) { printf("Works!\n\n"); } } /* cleanup of IPC resources */ if (me == 0) { DeleteLocks(lockid); Delete_All_Regions(); } MPI_Barrier(MPI_COMM_WORLD); } int main(int argc, char **argv) { armci_msg_init(&argc, &argv); ARMCI_Init_args(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &nproc); MPI_Comm_rank(MPI_COMM_WORLD, &me); if (me == 0) { printf("Testing IPCs (%d MPI processes)\n\n", nproc); } test(); ARMCI_Finalize(); armci_msg_finalize(); return 0; } ga-5.9.2/armci/testing/msgcheck.c000066400000000000000000000107361500715745200166330ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #include "armci.h" #include "message.h" int me, nproc; #define LOOP 20 void time_gop(double *test, int len) { int i; double t; t = armci_timer(); for (i = 0; i < LOOP; i++) { armci_msg_dgop(test, len, "+"); } t = armci_timer() - t; t /= LOOP; if (me == 0) { printf("Time per gop %f len=%d doubles\n", t, len); fflush(stdout); } } void time_reduce(double *test, int len) { int i; double t; t = armci_timer(); for (i = 0; i < LOOP; i++) { armci_msg_reduce(test, len, "+", ARMCI_DOUBLE); } t = armci_timer() - t; t /= LOOP; if (me == 0) { printf("Time per reduce %f len=%d doubles\n", t, len); fflush(stdout); } } void TestGlobals() { #define MAXLENG 256*1024 double *dtest; int *itest; long *ltest; int len; int ifrom = nproc - 1, lfrom = 1, dfrom = 1; if (me == 0) { printf("Global test ... broadcast and reduction for int, long, double\n----------\n"); fflush(stdout); } if (!(dtest = (double *) malloc((unsigned)(MAXLENG * sizeof(double))))) { ARMCI_Error("TestGlobals: failed to allocated dtest", MAXLENG); } if (!(ltest = (long *) malloc((unsigned)(MAXLENG * sizeof(long))))) { ARMCI_Error("TestGlobals: failed to allocated ltest", MAXLENG); } if (!(itest = (int *) malloc((unsigned)(MAXLENG * sizeof(int))))) { ARMCI_Error("TestGlobals: failed to allocated itest", MAXLENG); } for (len = 1; len < MAXLENG; len *= 2) { int ilen = len * sizeof(int); int dlen = len * sizeof(double); int llen = len * sizeof(long); int i; ifrom = (ifrom + 1) % nproc; lfrom = (lfrom + 1) % nproc; dfrom = (lfrom + 1) % nproc; #if 0 printf("%d:ifrom=%d lfrom=%d dfrom=%d\n", me, ifrom, lfrom, dfrom); fflush(stdout); #endif if (me == 0) { printf("Test length = %d ... ", len); fflush(stdout); } if (me == ifrom)for (i = 0; i < len; i++) { itest[i] = i; } else for (i = 0; i < len; i++) { itest[i] = 0; } if (me == lfrom)for (i = 0; i < len; i++) { ltest[i] = (long)i; } else for (i = 0; i < len; i++) { ltest[i] = 0L; } if (me == dfrom)for (i = 0; i < len; i++) { dtest[i] = (double)i; } else for (i = 0; i < len; i++) { dtest[i] = 0.0; } /* Test broadcast */ armci_msg_brdcst(itest, ilen, ifrom); armci_msg_brdcst(ltest, llen, lfrom); armci_msg_brdcst(dtest, dlen, dfrom); for (i = 0; i < len; i++) { if (itest[i] != i) { printf("int broadcast failed %d", itest[i]); ARMCI_Error("int broadcast failed %d", itest[i]); } if (ltest[i] != (long)i) { printf("long broadcast failed %ld", ltest[i]); ARMCI_Error("long broadcast failed %ld", ltest[i]); } if (dtest[i] != (double)i) { printf("double broadcast failed %f", dtest[i]); ARMCI_Error("double broadcast failed %f", dtest[i]); } } if (me == 0) { printf("broadcast OK ..."); fflush(stdout); } /* Test global sum */ for (i = 0; i < len; i++) { itest[i] = i * me; ltest[i] = (long) itest[i]; dtest[i] = (double) itest[i]; } armci_msg_igop(itest, len, "+"); armci_msg_lgop(ltest, len, "+"); armci_msg_dgop(dtest, len, "+"); for (i = 0; i < len; i++) { int iresult = i * nproc * (nproc - 1) / 2; if (itest[i] != iresult || ltest[i] != (long)iresult || dtest[i] != (double) iresult) { ARMCI_Error("TestGlobals: global sum failed", (int) i); } } if (me == 0) { printf("global sums OK\n"); fflush(stdout); } } /* now we get timing data */ time_gop(dtest, MAXLENG); time_reduce(dtest, MAXLENG); free((char *) itest); free((char *) ltest); free((char *) dtest); } int main(int argc, char **argv) { armci_msg_init(&argc, &argv); /* initialize ARMCI */ ARMCI_Init_args(&argc, &argv); me = armci_msg_me(); nproc = armci_msg_nproc(); if (nproc < 2) { if (me == 0) fprintf(stderr, "USAGE: 2 <= processes < %d\n", nproc); ARMCI_Barrier(); armci_msg_finalize(); exit(0); } if (me == 0) { printf("Test of ARMCI Wrappers to Basic Message Passing Operations\n"); fflush(stdout); } ARMCI_Barrier(); TestGlobals(); /* done */ ARMCI_Finalize(); armci_msg_finalize(); return(0); } ga-5.9.2/armci/testing/perf.c000066400000000000000000000415211500715745200157770ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* * Author: Jialin Ju, PNNL */ /* $Id: perf.c,v 1.21 2006-09-12 23:21:21 andriy Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif #if HAVE_MATH_H # include #endif #include "armci.h" #include "message.h" #ifndef ARMCI_MAX_STRIDE_LEVEL #define ARMCI_MAX_STRIDE_LEVEL 7 #endif #define SIZE 550 #define MAXPROC 8 #define CHUNK_NUM 28 #define FORCE_1D_ #ifndef ABS #define ARMCI_ABS(a) ((a)>0? (a): -(a)) #endif /* tells to use ARMCI_Malloc_local instead of plain malloc */ #define MALLOC_LOC int CHECK_RESULT = 0; int chunk[CHUNK_NUM] = {1, 3, 4, 6, 9, 12, 16, 20, 24, 30, 40, 48, 52, 64, 78, 91, 104, 128, 142, 171, 210, 256, 300, 353, 400, 440, 476, 512 }; char check_type[15]; int nproc, me; int warn_accuracy = 0; void fill_array(double *arr, int count, int which); void check_result(double *src_buf, double *dst_buf, int *stride, int *count, int stride_levels); void acc_array(double scale, double *array1, double *array2, int *stride, int *count, int stride_levels); static double _tt0 = 0.0; /*\ quick fix for inacurate timer \*/ double Timer() { #define DELTA 0.000001 double t = armci_timer(); if (t <= _tt0 + DELTA) { _tt0 += DELTA; } else { _tt0 = t; } return _tt0; } #define TIMER armci_timer double time_get(double *src_buf, double *dst_buf, int chunk, int loop, int proc, int levels) { int i, bal = 0; int stride[2]; int count[2]; int stride_levels = levels; double *tmp_buf = NULL, *tmp_buf_ptr = NULL; double start_time, stop_time, total_time = 0; stride[0] = SIZE * sizeof(double); count[0] = chunk * sizeof(double); count[1] = chunk; if (CHECK_RESULT) { tmp_buf = (double *)malloc(SIZE * SIZE * sizeof(double)); assert(tmp_buf != NULL); fill_array(tmp_buf, SIZE * SIZE, proc); tmp_buf_ptr = tmp_buf; } start_time = TIMER(); for (i = 0; i < loop; i++) { #ifdef FORCE_1D int j; if (levels > 0) { for (j = 0; j < count[1]; j++) { char *s = (char *) src_buf, *d = (char *)dst_buf; s += j * stride[0]; d += j * stride[0]; ARMCI_Get(src_buf, dst_buf, count[0], proc); } } else #endif if (levels) { ARMCI_GetS(src_buf, stride, dst_buf, stride, count, stride_levels, proc); } else { ARMCI_Get(src_buf, dst_buf, count[0], proc); } if (CHECK_RESULT) { sprintf(check_type, "ARMCI_GetS:"); check_result(tmp_buf_ptr, dst_buf, stride, count, stride_levels); } /* prepare next src and dst ptrs: avoid cache locality */ if (bal == 0) { src_buf += 128; dst_buf += 128; if (CHECK_RESULT) { tmp_buf_ptr += 128; } bal = 1; } else { src_buf -= 128; dst_buf -= 128; if (CHECK_RESULT) { tmp_buf_ptr -= 128; } bal = 0; } } stop_time = TIMER(); total_time = (stop_time - start_time); if (CHECK_RESULT) { free(tmp_buf); } if (total_time == 0.0) { total_time = 0.000001; /* workaround for inaccurate timers */ warn_accuracy++; } return(total_time / loop); } double time_put(double *src_buf, double *dst_buf, int chunk, int loop, int proc, int levels) { int i, bal = 0; int stride[2]; int count[2]; int stride_levels = levels; double *tmp_buf = NULL; double start_time, stop_time, total_time = 0; stride[0] = SIZE * sizeof(double); count[0] = chunk * sizeof(double); count[1] = chunk; if (CHECK_RESULT) { tmp_buf = (double *)malloc(SIZE * SIZE * sizeof(double)); assert(tmp_buf != NULL); } start_time = TIMER(); for (i = 0; i < loop; i++) { #ifdef FORCE_1D int j; if (levels > 0) { for (j = 0; j < count[1]; j++) { char *s = (char *) src_buf, *d = (char *)dst_buf; s += j * stride[0]; d += j * stride[0]; ARMCI_Put(src_buf, dst_buf, count[0], proc); } } else #endif if (levels) { ARMCI_PutS(src_buf, stride, dst_buf, stride, count, stride_levels, proc); } else { ARMCI_Put(src_buf, dst_buf, count[0], proc); } if (CHECK_RESULT) { ARMCI_GetS(dst_buf, stride, tmp_buf, stride, count, stride_levels, proc); sprintf(check_type, "ARMCI_PutS:"); check_result(tmp_buf, src_buf, stride, count, stride_levels); } /* prepare next src and dst ptrs: avoid cache locality */ if (bal == 0) { src_buf += 128; dst_buf += 128; bal = 1; } else { src_buf -= 128; dst_buf -= 128; bal = 0; } } stop_time = TIMER(); total_time = (stop_time - start_time); if (CHECK_RESULT) { free(tmp_buf); } if (total_time == 0.0) { total_time = 0.000001; /* workaround for inaccurate timers */ warn_accuracy++; } return(total_time / loop); } double time_acc(double *src_buf, double *dst_buf, int chunk, int loop, int proc, int levels) { int i, bal = 0; int stride[2]; int count[2]; int stride_levels = levels; double *before_buf = NULL, *after_buf = NULL; double start_time, stop_time, total_time = 0; stride[0] = SIZE * sizeof(double); count[0] = chunk * sizeof(double); count[1] = chunk; if (CHECK_RESULT) { before_buf = (double *)malloc(SIZE * SIZE * sizeof(double)); assert(before_buf != NULL); after_buf = (double *)malloc(SIZE * SIZE * sizeof(double)); assert(after_buf != NULL); } start_time = TIMER(); for (i = 0; i < loop; i++) { double scale = (double)i; if (CHECK_RESULT) { ARMCI_GetS(dst_buf, stride, before_buf, stride, count, stride_levels, proc); acc_array(scale, before_buf, src_buf, stride, count, stride_levels); } ARMCI_AccS(ARMCI_ACC_DBL, &scale, src_buf, stride, dst_buf, stride, count, stride_levels, proc); if (CHECK_RESULT) { ARMCI_GetS(dst_buf, stride, after_buf, stride, count, stride_levels, proc); sprintf(check_type, "ARMCI_AccS:"); check_result(after_buf, before_buf, stride, count, stride_levels); } /* prepare next src and dst ptrs: avoid cache locality */ if (bal == 0) { src_buf += 128; dst_buf += 128; bal = 1; } else { src_buf -= 128; dst_buf -= 128; bal = 0; } } stop_time = TIMER(); total_time = (stop_time - start_time); if (CHECK_RESULT) { free(before_buf); free(after_buf); } if (total_time == 0.0) { total_time = 0.000001; /* workaround for inaccurate timers */ warn_accuracy++; } return(total_time / loop); } void test_1D() { int i; int dst; int ierr; double *buf = NULL; void *ptr[MAXPROC], *get_ptr[MAXPROC]; /* memory allocation */ #ifdef MALLOC_LOC if (me == 0) { buf = (double *)ARMCI_Malloc_local(SIZE * SIZE * sizeof(double)); assert(buf != NULL); } #else if (me == 0) { buf = (double *)malloc(SIZE * SIZE * sizeof(double)); assert(buf != NULL); } #endif ierr = ARMCI_Malloc(ptr, (SIZE * SIZE * sizeof(double))); assert(ierr == 0); assert(ptr[me]); ierr = ARMCI_Malloc(get_ptr, (SIZE * SIZE * sizeof(double))); assert(ierr == 0); assert(get_ptr[me]); /* ARMCI - initialize the data window */ fill_array(ptr[me], SIZE * SIZE, me); fill_array(get_ptr[me], SIZE * SIZE, me); ARMCI_Barrier(); /* only the proc 0 does the work */ if (me == 0) { if (!CHECK_RESULT) { printf(" section get put"); printf(" acc\n"); printf("bytes loop usec MB/s usec MB/s"); printf(" usec MB/s\n"); printf("------- ------ -------- -------- -------- --------"); printf(" -------- --------\n"); fflush(stdout); } for (i = 0; i < CHUNK_NUM; i++) { int loop; int bytes = chunk[i] * chunk[i] * sizeof(double); double t_get = 0, t_put = 0, t_acc = 0; double latency_get, latency_put, latency_acc; double bandwidth_get, bandwidth_put, bandwidth_acc; loop = (SIZE * SIZE) / (chunk[i] * chunk[i]); loop = (int)sqrt((double)loop); if (loop < 2) { loop = 2; } for (dst = 1; dst < nproc; dst++) { /* strided get */ fill_array(buf, SIZE * SIZE, me * 10); t_get += time_get((double *)(get_ptr[dst]), (double *)buf, chunk[i] * chunk[i], loop, dst, 0); /* strided put */ fill_array(buf, SIZE * SIZE, me * 10); t_put += time_put((double *)buf, (double *)(ptr[dst]), chunk[i] * chunk[i], loop, dst, 0); /* strided acc */ fill_array(buf, SIZE * SIZE, me * 10); t_acc += time_acc((double *)buf, (double *)(ptr[dst]), chunk[i] * chunk[i], loop, dst, 0); } latency_get = t_get / (nproc - 1); latency_put = t_put / (nproc - 1); latency_acc = t_acc / (nproc - 1); bandwidth_get = (bytes * (nproc - 1) * 1e-6) / t_get; bandwidth_put = (bytes * (nproc - 1) * 1e-6) / t_put; bandwidth_acc = (bytes * (nproc - 1) * 1e-6) / t_acc; /* print */ if (!CHECK_RESULT) { printf("%d\t%d\t%.2e %.2e %.2e %.2e %.2e %.2e\n", bytes, loop, latency_get / 1e-6, bandwidth_get, latency_put / 1e-6, bandwidth_put, latency_acc / 1e-6, bandwidth_acc); } } } else { sleep(3); } ARMCI_AllFence(); ARMCI_Barrier(); /* cleanup */ ARMCI_Free(get_ptr[me]); ARMCI_Free(ptr[me]); #ifdef MALLOC_LOC if (me == 0) { ARMCI_Free_local(buf); } #else if (me == 0) { free(buf); } #endif } void test_2D() { int i; int dst; int ierr; double *buf = NULL; void *ptr[MAXPROC], *get_ptr[MAXPROC]; #ifdef MALLOC_LOC if (me == 0) { buf = (double *)ARMCI_Malloc_local(SIZE * SIZE * sizeof(double)); assert(buf != NULL); } #else if (me == 0) { buf = (double *)malloc(SIZE * SIZE * sizeof(double)); assert(buf != NULL); } #endif ierr = ARMCI_Malloc(ptr, (SIZE * SIZE * sizeof(double))); assert(ierr == 0); assert(ptr[me]); ierr = ARMCI_Malloc(get_ptr, (SIZE * SIZE * sizeof(double))); assert(ierr == 0); assert(get_ptr[me]); /* ARMCI - initialize the data window */ fill_array(ptr[me], SIZE * SIZE, me); fill_array(get_ptr[me], SIZE * SIZE, me); ARMCI_Barrier(); /* only the proc 0 doest the work */ /* print the title */ if (me == 0) { if (!CHECK_RESULT) { printf(" section get put"); printf(" acc\n"); printf("bytes loop usec MB/s usec MB/s"); printf(" usec MB/s\n"); printf("------- ------ -------- -------- -------- --------"); printf(" -------- --------\n"); fflush(stdout); } for (i = 0; i < CHUNK_NUM; i++) { int loop; int bytes = chunk[i] * chunk[i] * sizeof(double); double t_get = 0, t_put = 0, t_acc = 0; double latency_get, latency_put, latency_acc; double bandwidth_get, bandwidth_put, bandwidth_acc; loop = SIZE / chunk[i]; if (loop < 2) { loop = 2; } for (dst = 1; dst < nproc; dst++) { /* strided get */ fill_array(buf, SIZE * SIZE, me * 10); t_get += time_get((double *)(get_ptr[dst]), (double *)buf, chunk[i], loop, dst, 1); /* strided put */ fill_array(buf, SIZE * SIZE, me * 10); t_put += time_put((double *)buf, (double *)(ptr[dst]), chunk[i], loop, dst, 1); /* strided acc */ fill_array(buf, SIZE * SIZE, me * 10); t_acc += time_acc((double *)buf, (double *)(ptr[dst]), chunk[i], loop, dst, 1); } latency_get = t_get / (nproc - 1); latency_put = t_put / (nproc - 1); latency_acc = t_acc / (nproc - 1); bandwidth_get = (bytes * (nproc - 1) * 1e-6) / t_get; bandwidth_put = (bytes * (nproc - 1) * 1e-6) / t_put; bandwidth_acc = (bytes * (nproc - 1) * 1e-6) / t_acc; /* print */ if (!CHECK_RESULT) { printf("%d\t%d\t%.2e %.2e %.2e %.2e %.2e %.2e\n", bytes, loop, latency_get / 1e-6, bandwidth_get, latency_put / 1e-6, bandwidth_put, latency_acc / 1e-6, bandwidth_acc); } } } else { sleep(3); } ARMCI_AllFence(); ARMCI_Barrier(); /* cleanup */ ARMCI_Free(get_ptr[me]); ARMCI_Free(ptr[me]); #ifdef MALLOC_LOC if (me == 0) { ARMCI_Free_local(buf); } #else if (me == 0) { free(buf); } #endif } int main(int argc, char **argv) { armci_msg_init(&argc, &argv); /* initialize ARMCI */ ARMCI_Init_args(&argc, &argv); me = armci_msg_me(); nproc = armci_msg_nproc(); if (nproc < 2 || nproc > MAXPROC) { if (me == 0) fprintf(stderr, "USAGE: 2 <= processes < %d - got %d\n", MAXPROC, nproc); ARMCI_Barrier(); armci_msg_finalize(); /*MPI_Finalize();*/ exit(0); } if (!me) { printf("\n Performance of Basic Blocking Communication Operations\n"); } ARMCI_Barrier(); CHECK_RESULT = 1; test_1D(); CHECK_RESULT = 0; /* warmup run */ /* test 1 dimension array */ if (!me) { printf("\n\t\t\tContiguous Data Transfer\n"); } test_1D(); /* test 2 dimension array */ if (!me) { printf("\n\t\t\tStrided Data Transfer\n"); } test_2D(); ARMCI_Barrier(); if (me == 0) { if (warn_accuracy) { printf("\nWARNING: Your timer does not have sufficient accuracy for this test (%d)\n", warn_accuracy); } printf("\n\n------------ Now we test the same data transfer for correctness ----------\n"); fflush(stdout); } ARMCI_Barrier(); CHECK_RESULT = 1; if (!me) { printf("\n\t\t\tContiguous Data Transfer\n"); } test_1D(); if (me == 0) { printf("OK\n"); } ARMCI_Barrier(); if (!me) { printf("\n\t\t\tStrided Data Transfer\n"); } test_2D(); if (me == 0) { printf("OK\n\n\nTests Completed.\n"); } ARMCI_Barrier(); /* done */ ARMCI_Finalize(); armci_msg_finalize(); /*MPI_Finalize();*/ return(0); } void fill_array(double *arr, int count, int which) { int i; for (i = 0; i < count; i++) { arr[i] = i * 8.23 + which * 2.89; } } void check_result(double *src_buf, double *dst_buf, int *stride, int *count, int stride_levels) { int i, j, size; long idx; int n1dim; /* number of 1 dim block */ int bvalue[ARMCI_MAX_STRIDE_LEVEL], bunit[ARMCI_MAX_STRIDE_LEVEL]; /* number of n-element of the first dimension */ n1dim = 1; for (i = 1; i <= stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ bvalue[0] = 0; bvalue[1] = 0; bunit[0] = 1; bunit[1] = 1; for (i = 2; i <= stride_levels; i++) { bvalue[i] = 0; bunit[i] = bunit[i-1] * count[i-1]; } for (i = 0; i < n1dim; i++) { idx = 0; for (j = 1; j <= stride_levels; j++) { idx += bvalue[j] * stride[j-1]; if ((i + 1) % bunit[j] == 0) { bvalue[j]++; } if (bvalue[j] > (count[j] - 1)) { bvalue[j] = 0; } } size = count[0] / sizeof(double); for (j = 0; j < size; j++) if (ARMCI_ABS(((double *)((char *)src_buf + idx))[j] - ((double *)((char *)dst_buf + idx))[j]) > 0.000001) { fprintf(stdout, "Error:%s comparison failed: (%d) (%f :%f) %d\n", check_type, j, ((double *)((char *)src_buf + idx))[j], ((double *)((char *)dst_buf + idx))[j], count[0]); ARMCI_Error("failed", 0); } } } /* array1 = array1 + array2 * scale */ void acc_array(double scale, double *array1, double *array2, int *stride, int *count, int stride_levels) { int i, j, size; long idx; int n1dim; /* number of 1 dim block */ int bvalue[ARMCI_MAX_STRIDE_LEVEL], bunit[ARMCI_MAX_STRIDE_LEVEL]; /* number of n-element of the first dimension */ n1dim = 1; for (i = 1; i <= stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ bvalue[0] = 0; bvalue[1] = 0; bunit[0] = 1; bunit[1] = 1; for (i = 2; i <= stride_levels; i++) { bvalue[i] = 0; bunit[i] = bunit[i-1] * count[i-1]; } for (i = 0; i < n1dim; i++) { idx = 0; for (j = 1; j <= stride_levels; j++) { idx += bvalue[j] * stride[j-1]; if ((i + 1) % bunit[j] == 0) { bvalue[j]++; } if (bvalue[j] > (count[j] - 1)) { bvalue[j] = 0; } } size = count[0] / sizeof(double); for (j = 0; j < size; j++) ((double *)((char *)array1 + idx))[j] += ((double *)((char *)array2 + idx))[j] * scale; } } ga-5.9.2/armci/testing/perf2.c000066400000000000000000000071711500715745200160640ustar00rootroot00000000000000#include #include #include #include #include #include "armci.h" #include "message.h" static int me; static int nproc; #define PUT 0 #define GET 1 #define ACC 2 #define MAX_MESSAGE_SIZE 1024*1024 #define MEDIUM_MESSAGE_SIZE 8192 #define ITER_SMALL 100 #define ITER_LARGE 100 #define WARMUP 10 static void fill_array(double *arr, int count, int which); static void contig_test(size_t buffer_size, int op); double dclock() { struct timeval tv; gettimeofday(&tv, NULL); return(tv.tv_sec * 1.0e6 + (double)tv.tv_usec); } int main(int argc, char **argv) { armci_msg_init(&argc,&argv); ARMCI_Init_args(&argc, &argv); me = armci_msg_me(); nproc = armci_msg_nproc(); /* This test only works for two processes */ assert(nproc == 2); if (0 == me) { printf("msg size (bytes) avg time (us) avg b/w (MB/sec)\n"); } if (0 == me) { printf("#PNNL comex Put Test\n"); } contig_test(MAX_MESSAGE_SIZE, PUT); if (0 == me) { printf("#PNNL comex Get Test\n"); } contig_test(MAX_MESSAGE_SIZE, GET); if (0 == me) { printf("#PNNL comex Accumulate Test\n"); } contig_test(MAX_MESSAGE_SIZE, ACC); ARMCI_Finalize(); armci_msg_finalize(); return 0; } static void fill_array(double *arr, int count, int which) { int i; for (i = 0; i < count; i++) { arr[i] = i * 8.23 + which * 2.89; } } static void contig_test(size_t buffer_size, int op) { void **dst_ptr; void **put_buf; void **get_buf; double *times; size_t msg_size; int dst = 1; double scale = 1.0; dst_ptr = (void*)malloc(nproc * sizeof(void*)); put_buf = (void*)malloc(nproc * sizeof(void*)); get_buf = (void*)malloc(nproc * sizeof(void*)); times = (double*)malloc(nproc * sizeof(double)); ARMCI_Malloc(dst_ptr, buffer_size); ARMCI_Malloc(put_buf, buffer_size); ARMCI_Malloc(get_buf, buffer_size); /* initialize what we're putting */ fill_array((double*)put_buf[me], buffer_size/sizeof(double), me); for (msg_size = 16; msg_size <= buffer_size; msg_size *= 2) { int j; int iter = msg_size > MEDIUM_MESSAGE_SIZE ? ITER_LARGE : ITER_SMALL; double t_start, t_end; if (0 == me) { for (j= 0; j < iter + WARMUP; ++j) { if (WARMUP == j) { t_start = dclock(); } switch (op) { case PUT: ARMCI_Put(put_buf[me], dst_ptr[dst], msg_size, dst); break; case GET: ARMCI_Get(dst_ptr[dst], get_buf[me], msg_size, dst); break; case ACC: ARMCI_Acc(ARMCI_ACC_DBL, &scale, put_buf[me], dst_ptr[dst], msg_size, dst); break; default: ARMCI_Error("oops", 1); } } } /* calculate total time and average time */ t_end = dclock(); ARMCI_Barrier(); if (0 == me) { printf("%8lu\t\t%6.2f\t\t%10.2f\n", (unsigned long)msg_size, ((t_end - t_start))/iter, msg_size*iter/((t_end - t_start))); } } ARMCI_Free(dst_ptr[me]); ARMCI_Free(put_buf[me]); ARMCI_Free(get_buf[me]); free(dst_ptr); free(put_buf); free(get_buf); free(times); } ga-5.9.2/armci/testing/perf_aggr.c000066400000000000000000000220411500715745200167730ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: perf_aggr.c,v 1.7 2004-07-21 00:23:06 manoj Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif #include "armci.h" #include "message.h" #define DIM1 5 #define DIM2 3 #ifdef __sun /* Solaris has shared memory shortages in the default system configuration */ # define DIM3 6 # define DIM4 5 # define DIM5 4 #elif defined(__alpha__) # define DIM3 8 # define DIM4 5 # define DIM5 6 #else # define DIM3 8 # define DIM4 9 # define DIM5 7 #endif #define DIM6 3 #define DIM7 2 #define OFF 1 #define EDIM1 (DIM1+OFF) #define EDIM2 (DIM2+OFF) #define EDIM3 (DIM3+OFF) #define EDIM4 (DIM4+OFF) #define EDIM5 (DIM5+OFF) #define EDIM6 (DIM6+OFF) #define EDIM7 (DIM7+OFF) #define DIMS 4 #define MAXDIMS 7 #define MAX_DIM_VAL 50 #define LOOP 200 #define BASE 100. #define MAXPROC 128 #define TIMES 100 # define ELEMS 200 /***************************** macros ************************/ #define COPY(src, dst, bytes) memcpy((dst),(src),(bytes)) #define ARMCI_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define ARMCI_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define ARMCI_ABS(a) (((a) <0) ? -(a) : (a)) /***************************** global data *******************/ int me, nproc; void *work[MAXPROC]; /* work array for propagating addresses */ /*void create_array(void *a[], int elem_size, int ndim, int dims[])*/ void create_array(double *a[], int ndim, int dims[]) { int bytes = sizeof(double), i, rc; assert(ndim <= MAXDIMS); for (i = 0; i < ndim; i++) { bytes *= dims[i]; } rc = ARMCI_Malloc((void **)a, bytes); assert(rc == 0); assert(a[me]); } /*void destroy_array(void *ptr[])*/ void destroy_array(double *ptr[]) { int check; ARMCI_Barrier(); check = !ARMCI_Free(ptr[me]); assert(check); } #define MAXELEMS 1000 #define MAX_REQUESTS MAXELEMS void test_aggregate(int dryrun) { int i, j, rc, bytes, elems[2] = {MAXPROC, MAXELEMS}; double *ddst_put[MAXPROC]; double *ddst_get[MAXPROC]; double *dsrc[MAXPROC]; armci_hdl_t aggr_hdl_put[MAXPROC]; armci_hdl_t aggr_hdl_get[MAXPROC]; armci_hdl_t hdl_put[MAXELEMS]; armci_hdl_t hdl_get[MAXELEMS]; armci_giov_t darr; void *src_ptr[MAX_REQUESTS], *dst_ptr[MAX_REQUESTS]; int start = 0, end = 0; double start_time; create_array(ddst_put, 2, elems); create_array(ddst_get, 2, elems); create_array(dsrc, 1, &elems[1]); for (i = 0; i < elems[1]; i++) { dsrc[me][i] = i * 1.001 * (me + 1); } for (i = 0; i < elems[0]*elems[1]; i++) { ddst_put[me][i] = 0.0; ddst_get[me][i] = 0.0; } ARMCI_Barrier(); /* only proc 0 does the work */ if (me == 0) { if (!dryrun) { printf("Transferring %d doubles (Not an array of %d doubles)\n", MAXELEMS, MAXELEMS); } /* initializing non-blocking handles */ for (i = 0; i < elems[1]; i++) { ARMCI_INIT_HANDLE(&hdl_put[i]); } for (i = 0; i < elems[1]; i++) { ARMCI_INIT_HANDLE(&hdl_get[i]); } /* aggregate handles */ for (i = 0; i < nproc; i++) { ARMCI_INIT_HANDLE(&aggr_hdl_put[i]); } for (i = 0; i < nproc; i++) { ARMCI_INIT_HANDLE(&aggr_hdl_get[i]); } for (i = 0; i < nproc; i++) { ARMCI_SET_AGGREGATE_HANDLE(&aggr_hdl_put[i]); } for (i = 0; i < nproc; i++) { ARMCI_SET_AGGREGATE_HANDLE(&aggr_hdl_get[i]); } bytes = sizeof(double); /* **************** PUT **************** */ /* register put */ start_time = armci_timer(); start = 0; end = elems[1]; for (i = 1; i < nproc; i++) { for (j = start; j < end; j++) { ARMCI_NbPutValueDouble(dsrc[me][j], &ddst_put[i][me*elems[1] + j], i, &hdl_put[j]); } for (j = start; j < end; j++) { ARMCI_Wait(&hdl_put[j]); } } if (!dryrun) { printf("%d: Value Put time = %.2es\n", me, armci_timer() - start_time); } /* vector put */ start_time = armci_timer(); for (i = 1; i < nproc; i++) { for (j = start; j < end; j++) { src_ptr[j] = (void *)&dsrc[me][j]; dst_ptr[j] = (void *)&ddst_put[i][me*elems[1] + j]; } darr.src_ptr_array = src_ptr; darr.dst_ptr_array = dst_ptr; darr.bytes = sizeof(double); darr.ptr_array_len = elems[1]; if ((rc = ARMCI_NbPutV(&darr, 1, i, &hdl_put[i]))) { ARMCI_Error("armci_nbputv failed\n", rc); } } for (i = 1; i < nproc; i++) { ARMCI_Wait(&hdl_put[i]); } if (!dryrun) { printf("%d: Vector Put time = %.2es\n", me, armci_timer() - start_time); } /* regular put */ start_time = armci_timer(); for (i = 1; i < nproc; i++) { for (j = start; j < end; j++) { if ((rc = ARMCI_NbPut(&dsrc[me][j], &ddst_put[i][me*elems[1] + j], bytes, i, &hdl_put[j]))) { ARMCI_Error("armci_nbput failed\n", rc); } } for (j = start; j < end; j++) { ARMCI_Wait(&hdl_put[j]); } } if (!dryrun) { printf("%d: Regular Put time = %.2es\n", me, armci_timer() - start_time); } /* aggregate put */ start_time = armci_timer(); for (i = 1; i < nproc; i++) { for (j = start; j < end; j++) { if ((rc = ARMCI_NbPut(&dsrc[me][j], &ddst_put[i][me*elems[1] + j], bytes, i, &aggr_hdl_put[i]))) { ARMCI_Error("armci_nbput failed\n", rc); } } } for (i = 1; i < nproc; i++) { ARMCI_Wait(&aggr_hdl_put[i]); } if (!dryrun) { printf("%d: Aggregate Put time = %.2es\n\n", me, armci_timer() - start_time); } /* **************** GET **************** */ /* vector get */ start_time = armci_timer(); for (i = 1; i < nproc; i++) { for (j = start; j < end; j++) { src_ptr[j] = (void *)&dsrc[i][j]; dst_ptr[j] = (void *)&ddst_get[me][i*elems[1] + j]; } darr.src_ptr_array = src_ptr; darr.dst_ptr_array = dst_ptr; darr.bytes = sizeof(double); darr.ptr_array_len = elems[1]; if ((rc = ARMCI_NbGetV(&darr, 1, i, &hdl_get[i]))) { ARMCI_Error("armci_nbgetv failed\n", rc); } ARMCI_Wait(&hdl_get[i]); } if (!dryrun) { printf("%d: Vector Get time = %.2es\n", me, armci_timer() - start_time); } /* regular get */ start_time = armci_timer(); for (i = 1; i < nproc; i++) { for (j = start; j < end; j++) { if ((rc = ARMCI_NbGet(&dsrc[i][j], &ddst_get[me][i*elems[1] + j], bytes, i, &hdl_get[j]))) { ARMCI_Error("armci_nbget failed\n", rc); } } for (j = start; j < end; j++) { ARMCI_Wait(&hdl_get[j]); } } if (!dryrun) { printf("%d: Regular Get time = %.2es\n", me, armci_timer() - start_time); } /* aggregate get */ start_time = armci_timer(); for (i = 1; i < nproc; i++) { for (j = start; j < end; j++) { ARMCI_NbGet(&dsrc[i][j], &ddst_get[me][i*elems[1] + j], bytes, i, &aggr_hdl_get[i]); } } for (i = 1; i < nproc; i++) { ARMCI_Wait(&aggr_hdl_get[i]); } if (!dryrun) { printf("%d: Aggregate Get time = %.2es\n", me, armci_timer() - start_time); } } ARMCI_Barrier(); ARMCI_AllFence(); ARMCI_Barrier(); /* Verify */ if (!(me == 0)) for (j = 0; j < elems[1]; j++) { if (ARMCI_ABS(ddst_put[me][j] - j * 1.001) > 0.1) { ARMCI_Error("aggregate put failed...1", 0); } } ARMCI_Barrier(); if (!dryrun)if (me == 0) { printf("\n aggregate put ..O.K.\n"); } fflush(stdout); if (me == 0) { for (i = 1; i < nproc; i++) { for (j = 0; j < elems[1]; j++) { if (ARMCI_ABS(ddst_get[me][i*elems[1] + j] - j * 1.001 *(i + 1)) > 0.1) { ARMCI_Error("aggregate get failed...1", 0); } } } } ARMCI_Barrier(); if (!dryrun)if (me == 0) { printf(" aggregate get ..O.K.\n"); } fflush(stdout); ARMCI_AllFence(); ARMCI_Barrier(); if (!dryrun)if (me == 0) { printf("O.K.\n"); fflush(stdout); } destroy_array(ddst_put); destroy_array(ddst_get); destroy_array(dsrc); } int main(int argc, char *argv[]) { armci_msg_init(&argc, &argv); ARMCI_Init_args(&argc, &argv); nproc = armci_msg_nproc(); me = armci_msg_me(); /* printf("nproc = %d, me = %d\n", nproc, me);*/ if (nproc > MAXPROC && me == 0) { ARMCI_Error("Test works for up to %d processors\n", MAXPROC); } if (me == 0) { printf("ARMCI test program (%d processes)\n", nproc); fflush(stdout); sleep(1); } if (me == 0) { printf("\nAggregate put/get requests\n\n"); fflush(stdout); } test_aggregate(1); /* cold start */ test_aggregate(0); /* warm start */ ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nSuccess!!\n"); fflush(stdout); } sleep(2); ARMCI_Barrier(); ARMCI_Finalize(); armci_msg_finalize(); return(0); } ga-5.9.2/armci/testing/perf_nb.c000066400000000000000000000234011500715745200164530ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: perf_nb.c,v 1.3 2004-03-29 19:14:51 vinod Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif #include "armci.h" #include "message.h" #define DIM1 5 #define DIM2 3 #ifdef __sun /* Solaris has shared memory shortages in the default system configuration */ # define DIM3 6 # define DIM4 5 # define DIM5 4 #elif defined(__alpha__) # define DIM3 8 # define DIM4 5 # define DIM5 6 #else # define DIM3 8 # define DIM4 9 # define DIM5 7 #endif #define DIM6 3 #define DIM7 2 #define OFF 1 #define EDIM1 (DIM1+OFF) #define EDIM2 (DIM2+OFF) #define EDIM3 (DIM3+OFF) #define EDIM4 (DIM4+OFF) #define EDIM5 (DIM5+OFF) #define EDIM6 (DIM6+OFF) #define EDIM7 (DIM7+OFF) #define DIMS 4 #define MAXDIMS 7 #define MAX_DIM_VAL 50 #define LOOP 200 #define BASE 100. #define MAXPROC 8 #define TIMES 100 # define ELEMS 200 #define MAXELEMS 131072 /* 262144 */ #define MAX_REQUESTS MAXELEMS #define PUT 11 #define GET 22 #define ACC 33 int VERIFY = 1; /* verifies results */ int DEBUG = 0; /* if debug=1, dump extra messages */ /***************************** macros ************************/ #define COPY(src, dst, bytes) memcpy((dst),(src),(bytes)) #define ARMCI_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define ARMCI_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define ARMCI_ABS(a) (((a) <0) ? -(a) : (a)) /***************************** global data *******************/ int me, nproc; void *work[MAXPROC]; /* work array for propagating addresses */ double *ddst[MAXPROC]; /*void create_array(void *a[], int elem_size, int ndim, int dims[])*/ void create_array(double *a[], int ndim, int dims[]) { int bytes = sizeof(double), i, rc; assert(ndim <= MAXDIMS); for (i = 0; i < ndim; i++) { bytes *= dims[i]; } rc = ARMCI_Malloc((void **)a, bytes); assert(rc == 0); assert(a[me]); } /*void destroy_array(void *ptr[])*/ void destroy_array(double *ptr[]) { int check; ARMCI_Barrier(); check = !ARMCI_Free(ptr[me]); assert(check); } void verify_results(int op, int *elems) { int i, j; switch (op) { case PUT: if (!(me == 0)) for (j = 0; j < elems[1]; j++) { if (ARMCI_ABS(ddst[me][j] - j * 1.001) > 0.1) { ARMCI_Error("put failed...Invalid Value Obtained..1", 0); } } ARMCI_Barrier(); if (DEBUG) if (me == 0) { printf(" verifying put ..O.K.\n"); } break; case GET: if (me == 0) { for (i = 1; i < nproc; i++) { for (j = 0; j < elems[1]; j++) { if (ARMCI_ABS(ddst[me][i*elems[1] + j] - j * 1.001 *(i + 1)) > 0.1) { ARMCI_Error("get failed...Invalid Value Obtained..1", 0); } } } } ARMCI_Barrier(); if (DEBUG) if (me == 0) { printf(" verifying get ..O.K.\n\n"); } break; case ACC: if (me == 0) for (j = 0; j < elems[1]; j++) { /*printf("ddst[%d][%d] = %lf\n", me, j, ddst[me][j]); fflush(stdout); */ if (ARMCI_ABS(ddst[me][j] - (double)nproc) > 0.1) { ARMCI_Error("accumulate failed...Invalid Value Obtained..1", 0); } } ARMCI_Barrier(); if (DEBUG)if (me == 0) { printf(" verifying accumulate ..O.K.\n"); } break; default: ARMCI_Error("Invalid Operation", 0); } fflush(stdout); } void test_perf_nb(int dry_run) { int i, j, loop, rc, bytes, elems[2] = {MAXPROC, MAXELEMS}; int stride, k = 0, ntimes; double stime, t1, t2, t3, t4, t5, t6, t7, t8, t9; double *dsrc[MAXPROC], scale = 1.0; armci_hdl_t hdl_get, hdl_put, hdl_acc; create_array(ddst, 2, elems); create_array(dsrc, 1, &elems[1]); if (!dry_run)if (me == 0) { printf("\n\t\t\tRemote 1-D Array Section\n"); printf("section get nbget wait put nbput "); printf(" wait acc nbacc wait\n"); printf("------- -------- -------- -------- -------- --------"); printf(" -------- -------- -------- --------\n"); fflush(stdout); } for (loop = 1; loop <= MAXELEMS; loop *= 2, k++) { elems[1] = loop; ntimes = (int)sqrt((double)(MAXELEMS / elems[1])); if (ntimes < 1) { ntimes = 1; } /* -------------------------- SETUP --------------------------- */ /*initializing non-blocking handles,time,src & dst buffers*/ ARMCI_INIT_HANDLE(&hdl_put); ARMCI_INIT_HANDLE(&hdl_get); ARMCI_INIT_HANDLE(&hdl_acc); t1 = t2 = t3 = t4 = t5 = t6 = t7 = t8 = t9 = 0.0; for (i = 0; i < elems[1]; i++) { dsrc[me][i] = i * 1.001 * (me + 1); } for (i = 0; i < elems[0]*elems[1]; i++) { ddst[me][i] = 0.0; } ARMCI_Barrier(); /* bytes transfered */ bytes = sizeof(double) * elems[1]; ARMCI_Barrier(); /* -------------------------- PUT/GET -------------------------- */ if (me == 0) { for (i = 1; i < nproc; i++) { stime = armci_timer(); for (j = 0; j < ntimes; j++) if ((rc = ARMCI_Put(&dsrc[me][0], &ddst[i][me*elems[1]], bytes, i))) { ARMCI_Error("armci_nbput failed\n", rc); } t1 += armci_timer() - stime; } } ARMCI_Barrier(); ARMCI_AllFence(); ARMCI_Barrier(); if (VERIFY) { verify_results(PUT, elems); } for (i = 0; i < elems[0]*elems[1]; i++) { ddst[me][i] = 0.0; } ARMCI_Barrier(); if (me == 0) { for (i = 1; i < nproc; i++) { stime = armci_timer(); for (j = 0; j < ntimes; j++) if ((rc = ARMCI_Get(&dsrc[i][0], &ddst[me][i*elems[1]], bytes, i))) { printf("%d: armci_get. rc=%d\n", me, rc); fflush(stdout); ARMCI_Error("armci_nbget failed\n", rc); } t4 += armci_timer() - stime; } } ARMCI_Barrier(); ARMCI_AllFence(); ARMCI_Barrier(); if (VERIFY) { verify_results(GET, elems); } for (i = 0; i < elems[0]*elems[1]; i++) { ddst[me][i] = 0.0; } ARMCI_Barrier(); /* ------------------------ nb PUT/GET ------------------------- */ if (me == 0) { for (i = 1; i < nproc; i++) { for (j = 0; j < ntimes; j++) { stime = armci_timer(); if ((rc = ARMCI_NbPut(&dsrc[me][0], &ddst[i][me*elems[1]], bytes, i, &hdl_put))) { ARMCI_Error("armci_nbput failed\n", rc); } t2 += armci_timer() - stime; stime = armci_timer(); ARMCI_Wait(&hdl_put); t3 += armci_timer() - stime; } } } ARMCI_Barrier(); ARMCI_AllFence(); ARMCI_Barrier(); if (VERIFY) { verify_results(PUT, elems); } for (i = 0; i < elems[0]*elems[1]; i++) { ddst[me][i] = 0.0; } ARMCI_Barrier(); if (me == 0) { for (i = 1; i < nproc; i++) { for (j = 0; j < ntimes; j++) { stime = armci_timer(); if ((rc = ARMCI_NbGet(&dsrc[i][0], &ddst[me][i*elems[1]], bytes, i, &hdl_get))) { ARMCI_Error("armci_nbget failed\n", rc); } t5 += armci_timer() - stime; stime = armci_timer(); ARMCI_Wait(&hdl_get); t6 += armci_timer() - stime; } } } ARMCI_Barrier(); ARMCI_AllFence(); ARMCI_Barrier(); if (VERIFY) { verify_results(GET, elems); } for (i = 0; i < elems[0]*elems[1]; i++) { ddst[me][i] = 0.0; } ARMCI_Barrier(); /* ------------------------ Accumulate ------------------------- */ for (i = 0; i < elems[1]; i++) { dsrc[me][i] = 1.0; } ARMCI_Barrier(); stride = elems[1] * sizeof(double); scale = 1.0; for (j = 0; j < ntimes; j++) { stime = armci_timer(); if ((rc = ARMCI_AccS(ARMCI_ACC_DBL, &scale, &dsrc[me][0], &stride, &ddst[0][0], &stride, &bytes, 0, 0))) { ARMCI_Error("armci_acc failed\n", rc); } t7 += armci_timer() - stime; ARMCI_Barrier(); ARMCI_AllFence(); ARMCI_Barrier(); if (VERIFY) { verify_results(ACC, elems); } for (i = 0; i < elems[0]*elems[1]; i++) { ddst[me][i] = 0.0; } ARMCI_Barrier(); } /* print timings */ if (!dry_run) if (me == 0) printf("%d\t %.2e %.2e %.2e %.2e %.2e %.2e %.2e %.2e %.2e\n", bytes, t4 / ntimes, t5 / ntimes, t6 / ntimes, t1 / ntimes, t2 / ntimes, t3 / ntimes, t7 / ntimes, t8 / ntimes, t9 / ntimes); } ARMCI_AllFence(); ARMCI_Barrier(); if (!dry_run)if (me == 0) { printf("O.K.\n"); fflush(stdout); } destroy_array(ddst); destroy_array(dsrc); } int main(int argc, char *argv[]) { armci_msg_init(&argc, &argv); ARMCI_Init_args(&argc, &argv); nproc = armci_msg_nproc(); me = armci_msg_me(); if (nproc < 2 || nproc > MAXPROC) { if (me == 0) fprintf(stderr, "USAGE: 2 <= processes < %d - got %d\n", MAXPROC, nproc); ARMCI_Barrier(); armci_msg_finalize(); exit(0); } if (me == 0) { printf("ARMCI test program (%d processes)\n", nproc); fflush(stdout); sleep(1); } if (me == 0) { printf("\n put/get/acc requests (Time in secs)\n\n"); fflush(stdout); } test_perf_nb(1); test_perf_nb(0); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nSuccess!!\n"); fflush(stdout); } sleep(2); ARMCI_Barrier(); ARMCI_Finalize(); armci_msg_finalize(); return(0); } /* NOTE: ARMCI_NbAcc fails in opus for buffer sizes greater than 400Kb */ ga-5.9.2/armci/testing/perf_strided.c000066400000000000000000000106071500715745200175160ustar00rootroot00000000000000#include #include #include #include #include #include #include "armci.h" #include "message.h" static int me; static int nproc; #define PUTS 0 #define GETS 1 #define ACCS 2 #define MAX_MESSAGE_SIZE 1024*1024 #define MEDIUM_MESSAGE_SIZE 8192 #define ITER_SMALL 100 #define ITER_LARGE 10 #define WARMUP 2 static void fill_array(double *arr, int count, int which); static void strided_test(size_t buffer_size, int op); double dclock() { struct timeval tv; gettimeofday(&tv, NULL); return(tv.tv_sec * 1.0e6 + (double)tv.tv_usec); } int main(int argc, char **argv) { armci_msg_init(&argc,&argv); ARMCI_Init_args(&argc, &argv); me = armci_msg_me(); nproc = armci_msg_nproc(); /* This test only works for two processes */ assert(nproc == 2); if (0 == me) { printf("\n\n"); printf("msg size (bytes) avg time (us) avg b/w (MB/sec)\n"); } if (0 == me) { printf("\n\n"); printf("#PNNL armci Put Strided Test\n"); } strided_test(MAX_MESSAGE_SIZE, PUTS); if (0 == me) { printf("\n\n"); printf("#PNNL armci Get Strided Test\n"); } strided_test(MAX_MESSAGE_SIZE, GETS); if (0 == me) { printf("\n\n"); printf("#PNNL armci Accumulate Strided Test\n"); } strided_test(MAX_MESSAGE_SIZE, ACCS); ARMCI_Finalize(); armci_msg_finalize(); return 0; } static void fill_array(double *arr, int count, int which) { int i; for (i = 0; i < count; i++) { arr[i] = i; } } static void strided_test(size_t buffer_size, int op) { void **dst_ptr; void **put_buf; void **get_buf; double *times; size_t msg_size; int dst = 1; double scale = 1; /* Information for strided data transfer */ int levels = 1; int count[2]; int stride[1]; size_t xdim, ydim; dst_ptr = (void*)malloc(nproc * sizeof(void*)); put_buf = (void*)malloc(nproc * sizeof(void*)); get_buf = (void*)malloc(nproc * sizeof(void*)); times = (double*)malloc(nproc * sizeof(double)); ARMCI_Malloc(dst_ptr, buffer_size); ARMCI_Malloc(put_buf, buffer_size); ARMCI_Malloc(get_buf, buffer_size); /* initialize what we're putting */ fill_array((double*)put_buf[me], buffer_size/sizeof(double), me); for (msg_size = 16; msg_size <= buffer_size; msg_size *= 2) { int j; int iter = msg_size > MEDIUM_MESSAGE_SIZE ? ITER_LARGE : ITER_SMALL; for (xdim = 8; xdim <= msg_size; xdim *=2 ) { double t_start, t_end; ydim = msg_size / xdim; count[0] = xdim; count[1] = ydim; stride[0] = xdim; if (0 == me) { for (j= 0; j < iter + WARMUP; ++j) { if (WARMUP == j) { t_start = dclock(); } switch (op) { case PUTS: ARMCI_PutS(put_buf[me], stride, dst_ptr[dst], stride, count, levels, dst); break; case GETS: ARMCI_GetS(dst_ptr[dst], stride, get_buf[me], stride, count, levels, dst); break; case ACCS: ARMCI_AccS(ARMCI_ACC_DBL, (void *)&scale, put_buf[me], stride, dst_ptr[dst], stride, count, levels, dst); break; default: ARMCI_Error("oops", 1); } } } ARMCI_Barrier(); /* calculate total time and average time */ t_end = dclock(); if (0 == me) { printf("%8lu\t\t%6.2f\t\t%6.2f\t\t%lu\t\t%lu\n", (unsigned long)msg_size, ((t_end - t_start))/iter, msg_size*(nproc-1)*iter/((t_end - t_start)), (unsigned long)xdim, (unsigned long)ydim); } } } ARMCI_Free(dst_ptr[me]); ARMCI_Free(put_buf[me]); ARMCI_Free(get_buf[me]); free(dst_ptr); free(put_buf); free(get_buf); free(times); } ga-5.9.2/armci/testing/rpl_armci_msg_finalize.c000066400000000000000000000001651500715745200215410ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include void armci_msg_finalize() { MPI_Finalize(); } ga-5.9.2/armci/testing/rpl_armci_msg_init.c000066400000000000000000000005061500715745200207020ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include void armci_msg_init(int *argc, char ***argv) { int flag=0; MPI_Initialized(&flag); if (!flag) { #if 0 int provided; MPI_Init_thread(argc, argv, MPI_THREAD_MULTIPLE, &provided); #else MPI_Init(argc, argv); # endif } } ga-5.9.2/armci/testing/shmclean.c000066400000000000000000000021741500715745200166360ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* The program was written to address missing "ipcrm" command on Mac X * It probes a range of System V shared memory id from 0 to MAXID * and if exist, it attempts to delete them. */ #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_IPC_H # include #endif #if HAVE_SYS_SHM_H # include #endif #if HAVE_SYS_PARAM_H # include #endif #if HAVE_ERRNO_H # include #endif #if HAVE_STDIO_H # include #endif #define MAXID 1000000 int main(int argc, char **argv) { int i; for (i = 0; i < MAXID; i++) { #if SIZEOF_VOIDP == SIZEOF_INT int rc = (int) shmat(i, (char *) NULL, 0); #elif SIZEOF_VOIDP == SIZEOF_LONG long rc = (long) shmat(i, (char *) NULL, 0); #elif SIZEOF_VOIDP == SIZEOF_LONGLONG long long rc = (long long) shmat(i, (char *) NULL, 0); #endif if (rc < 0) { continue; } printf("found %d\n", i); shmdt((void *)rc); /* delete segment id */ if (shmctl(i, IPC_RMID, (struct shmid_ds *)NULL)) { printf("failed to remove shm id=%d\n", i); } } return 0; } ga-5.9.2/armci/testing/shmtest.c000066400000000000000000000047561500715745200165430ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: shmtest.c,v 1.6 2003-10-22 22:12:21 d3h325 Exp $ */ #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_IPC_H # include #endif #if HAVE_SYS_SHM_H # include #endif #if HAVE_SYS_PARAM_H # include #endif #if HAVE_ERRNO_H # include #endif #if HAVE_STDIO_H # include #endif int armci_test_allocate(long size) { char *ptr; long id = (long)shmget(IPC_PRIVATE, (size_t) size, (int)(IPC_CREAT | 00600)); if (id < 0L) { return 0; } #if 0 /* attach to segment */ ptr = shmat((int) id, (char *) NULL, 0); #else ptr = (char *) NULL; #endif /* delete segment id */ if (shmctl((int) id, IPC_RMID, (struct shmid_ds *)NULL)) { fprintf(stderr, "failed to remove shm id=%ld\n", id); } /* test pointer */ if (((long)ptr) == -1L) { return 0; } else { return 1; } } /* parameters that define range and granularity of search for shm segment size * UBOUND is chosen to be < 2GB to avoid overflowing on 32-bit systems * smaller PAGE gives more accurate results but with more search steps * LBOUND is set to amount that is considered insufficient for our purposes */ #define PAGE 131072L #define UBOUND 2*4096*PAGE #define LBOUND 4*PAGE int verbose = 1; /*\ determine the max shmem segment size using bisection \*/ void armci_shmem_init() { long x, i, y = 0L; long upper_bound = UBOUND; long lower_bound = 0; x = upper_bound; for (i = 1;; i++) { long step; int rc = armci_test_allocate(x); if (rc) { if (verbose) { printf("test %ld size=%ld bytes: success\n", i, x); } y = lower_bound = x; step = (upper_bound - x) >> 1; if (step < 16 * PAGE) { break; } x += step; } else { if (verbose) { printf("test %ld size=%ld bytes: failed\n", i, x); } upper_bound = x; step = (x - lower_bound) >> 1; if (step < PAGE || x < LBOUND) { break; } x -= step; } } if (verbose) { if (x < LBOUND) { printf("no usable amount (%ld bytes) of shared memory available\n", LBOUND); } else { printf("%ld bytes segment size, %ld calls \n", y, i); } } else { printf("%ld\n", y); } } int main(int argc, char **argv) { if (argc > 1) { verbose = 0; } if (verbose) { printf("Searching for max shared memory segment size (SHMMAX) using bisection\n"); } armci_shmem_init(); return 0; } ga-5.9.2/armci/testing/simple.c000066400000000000000000000037611500715745200163400ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /*$id$*/ #include #include #include #include "armci.h" #include "message.h" int me, nprocs; int LOOP = 10; int main(int argc, char **argv) { int k, i; double **myptrs[10]; double t0, t1, tget = 0, tnbget = 0, tput = 0, tnbput = 0, tnbwait = 0, t2 = 0; armci_msg_init(&argc, &argv); ARMCI_Init_args(&argc, &argv); ARMCI_Init_args(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &me); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); for (k = 0; k < 10; k++) { myptrs[k] = (double **)malloc(sizeof(double *) * nprocs); ARMCI_Malloc((void **)myptrs[k], 400000 * LOOP * sizeof(double)); for (i = 0; i < LOOP; i++) { myptrs[k][me][i] = me + 0.414; } MPI_Barrier(MPI_COMM_WORLD); for (i = 0; i < LOOP; i++) { ARMCI_Get(myptrs[k][(me+1)%nprocs] + i, myptrs[k][me] + i, sizeof(double), (me + 1) % nprocs); /*if(myptrs[k][me][i]!=0.414+(me+1)%nprocs)ARMCI_Error("errr",myptrs[k][me][i]);*/ } t0 = t1 = tget = tnbget = tput = tnbput = tnbwait = t2 = 0; t0 = MPI_Wtime(); for (i = 0; i < LOOP; i++) { ARMCI_Get(myptrs[k][(me+1)%nprocs] + i, myptrs[k][me] + i, sizeof(double), (me + 1) % nprocs); } t1 = MPI_Wtime(); printf("\nGet Latency=%f\n", 1e6 *(t1 - t0) / LOOP); fflush(stdout); t1 = t0 = 0; for (i = 0; i < LOOP; i++) { armci_hdl_t nbh; ARMCI_INIT_HANDLE(&nbh); t0 = MPI_Wtime(); ARMCI_NbGet(myptrs[k][(me+1)%nprocs] + i, myptrs[k][me] + i, sizeof(double), (me + 1) % nprocs, &nbh); t1 = MPI_Wtime(); ARMCI_Wait(&nbh); t2 = MPI_Wtime(); tnbget += (t1 - t0); tnbwait += (t2 - t1); } printf("\nNb Get Latency=%f Nb Wait=%f\n", 1e6 * tnbget / LOOP, 1e6 * tnbwait / LOOP); fflush(stdout); MPI_Barrier(MPI_COMM_WORLD); } for (k = 0; k < 10; k++) { ARMCI_Free(myptrs[k][me]); } MPI_Barrier(MPI_COMM_WORLD); ARMCI_Finalize(); ARMCI_Finalize(); armci_msg_finalize(); return 0; } ga-5.9.2/armci/testing/simplelock.c000066400000000000000000000023051500715745200172020ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_UNISTD_H # include #endif #include "armci.h" #include "message.h" int me, nproc; extern void armcill_lock(int, int); extern void armcill_unlock(int, int); void test_lock() { int i, mut; if (me == 0) { printf("\n"); } for (mut = 0; mut < 16; mut++) for (i = 0; i < nproc; i++) { #if FIXME_THESE_ARE_NOT_DEFINED_FOR_PORTALS armcill_lock(mut, i); armcill_unlock(mut, i); #endif ARMCI_Barrier(); if (me == 0) { printf("."); fflush(stdout); } ARMCI_Barrier(); } } int main(int argc, char *argv[]) { armci_msg_init(&argc, &argv); ARMCI_Init_args(&argc, &argv); nproc = armci_msg_nproc(); me = armci_msg_me(); if (me == 0) { printf("ARMCI test program for lock(%d processes)\n", nproc); fflush(stdout); sleep(1); } test_lock(); ARMCI_Barrier(); if (me == 0) { printf("test passed\n"); fflush(stdout); } sleep(2); ARMCI_Barrier(); ARMCI_Finalize(); armci_msg_finalize(); return(0); } ga-5.9.2/armci/testing/test.c000066400000000000000000001462041500715745200160260ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: test.c,v 1.43.6.6 2007-08-30 22:59:27 manoj Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif #include "armci.h" #include "message.h" #define MEMLOCK_TEST 0 #if MEMLOCK_TEST extern void armci_lockmem(void *, void *, int); extern void armci_unlockmem(void); #endif #define DIM1 5 #define DIM2 3 #ifdef __sun /* Solaris has shared memory shortages in the default system configuration */ # define DIM3 6 # define DIM4 5 # define DIM5 4 #elif defined(__alpha__) # define DIM3 8 # define DIM4 5 # define DIM5 6 #else # define DIM3 8 # define DIM4 9 # define DIM5 7 #endif #define DIM6 3 #define DIM7 2 #define OFF 1 #define EDIM1 (DIM1+OFF) #define EDIM2 (DIM2+OFF) #define EDIM3 (DIM3+OFF) #define EDIM4 (DIM4+OFF) #define EDIM5 (DIM5+OFF) #define EDIM6 (DIM6+OFF) #define EDIM7 (DIM7+OFF) #define DIMS 4 #define MAXDIMS 7 #define MAX_DIM_VAL 50 #define LOOP 200 #define BASE 100. #define MAXPROC 128 #define TIMES 100 # define ELEMS 200 /***************************** macros ************************/ #define COPY(src, dst, bytes) memcpy((dst),(src),(bytes)) #define ARMCI_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define ARMCI_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define ARMCI_ABS(a) (((a) <0) ? -(a) : (a)) /***************************** global data *******************/ int me, nproc; int work[MAXPROC]; /* work array for propagating addresses */ /*\ generate random range for a section of multidimensional array \*/ void get_range(int ndim, int dims[], int lo[], int hi[]) { int dim; for (dim = 0; dim < ndim; dim++) { int toss1, toss2; toss1 = rand() % dims[dim]; toss2 = rand() % dims[dim]; if (toss1 < toss2) { lo[dim] = toss1; hi[dim] = toss2; } else { hi[dim] = toss1; lo[dim] = toss2; } } } /*\ generates a new random range similar to the input range for an array with specified dimensions \*/ void new_range(int ndim, int dims[], int lo[], int hi[], int new_lo[], int new_hi[]) { int dim; for (dim = 0; dim < ndim; dim++) { int toss, range; int diff = hi[dim] - lo[dim] + 1; assert(diff <= dims[dim]); range = dims[dim] - diff; toss = (range > 0) ? rand() % range : lo[dim]; new_lo[dim] = toss; new_hi[dim] = toss + diff - 1; assert(new_hi[dim] < dims[dim]); assert(diff == (new_hi[dim] - new_lo[dim] + 1)); } } /*\ print range of ndim dimensional array with two strings before and after \*/ void print_range(char *pre, int ndim, int lo[], int hi[], char *post) { int i; printf("%s[", pre); for (i = 0; i < ndim; i++) { printf("%d:%d", lo[i], hi[i]); if (i == ndim - 1) { printf("] %s", post); } else { printf(","); } } } /*\ print subscript of ndim dimensional array with two strings before and after \*/ void print_subscript(char *pre, int ndim, int subscript[], char *post) { int i; printf("%s [", pre); for (i = 0; i < ndim; i++) { printf("%d", subscript[i]); if (i == ndim - 1) { printf("] %s", post); } else { printf(","); } } } /*\ print a section of a 2-D array of doubles \*/ void print_2D_double(double *a, int ld, int *lo, int *hi) { int i, j; for (i = lo[0]; i <= hi[0]; i++) { for (j = lo[1]; j <= hi[1]; j++) { printf("%13f ", a[ld*j+i]); } printf("\n"); } } /*\ initialize array: a[i,j,k,..]=i+100*j+10000*k+ ... \*/ void init(double *a, int ndim, int elems, int dims[]) { int idx[MAXDIMS]; int i, dim; for (i = 0; i < elems; i++) { int Index = i; double field, val; for (dim = 0; dim < ndim; dim++) { idx[dim] = Index % dims[dim]; Index /= dims[dim]; } field = 1.; val = 0.; for (dim = 0; dim < ndim; dim++) { val += field * idx[dim]; field *= BASE; } a[i] = val; /* printf("(%d,%d,%d)=%6.0f",idx[0],idx[1],idx[2],val); */ } } /*\ compute Index from subscript * assume that first subscript component changes first \*/ int Index(int ndim, int subscript[], int dims[]) { int idx = 0, i, factor = 1; for (i = 0; i < ndim; i++) { idx += subscript[i] * factor; factor *= dims[i]; } return idx; } void update_subscript(int ndim, int subscript[], int lo[], int hi[], int dims[]) { int i; for (i = 0; i < ndim; i++) { if (subscript[i] < hi[i]) { subscript[i]++; return; } subscript[i] = lo[i]; } } void compare_patches(double eps, int ndim, double *patch1, int lo1[], int hi1[], int dims1[], double *patch2, int lo2[], int hi2[], int dims2[]) { int i, j, elems = 1; int subscr1[MAXDIMS], subscr2[MAXDIMS]; double diff, max; int offset1, offset2; for (i = 0; i < ndim; i++) { /* count # of elements & verify consistency of both patches */ int diff = hi1[i] - lo1[i]; assert(diff == (hi2[i] - lo2[i])); assert(diff < dims1[i]); assert(diff < dims2[i]); elems *= diff + 1; subscr1[i] = lo1[i]; subscr2[i] = lo2[i]; } /* compare element values in both patches */ offset1 = Index(ndim, subscr1, dims1); offset2 = Index(ndim, subscr2, dims2); for (j = 0; j < elems; j++) { int idx1, idx2; idx1 = Index(ndim, subscr1, dims1); /* calculate element Index from a subscript */ idx2 = Index(ndim, subscr2, dims2); idx1 -= offset1; idx2 -= offset2; diff = patch1[idx1] - patch2[idx2]; max = ARMCI_MAX(ARMCI_ABS(patch1[idx1]), ARMCI_ABS(patch2[idx2])); if (max == 0. || max < eps) { max = 1.; } if (eps < ARMCI_ABS(diff) / max) { char msg[48]; sprintf(msg, "(proc=%d):%f", me, patch1[idx1]); print_subscript("ERROR: a", ndim, subscr1, msg); sprintf(msg, "%f\n", patch2[idx2]); print_subscript(" b", ndim, subscr2, msg); fflush(stdout); sleep(1); ARMCI_Error("Bailing out", 0); } { /* update subscript for the patches */ update_subscript(ndim, subscr1, lo1, hi1, dims1); update_subscript(ndim, subscr2, lo2, hi2, dims2); } } /* make sure we reached upper limit */ /*for(i=0;i= 100) { ARMCI_Error("increase MMAX", g_idx); } ARMCI_Memget(bytes, &meminfo[g_idx][me], 0); for (i = 0; i < nproc; i++) { armci_msg_brdcst(&meminfo[g_idx][i], sizeof(armci_meminfo_t), i); } for (i = 0; i < nproc; i++) { a[i] = ARMCI_Memat(&meminfo[g_idx][i], 0); } g_idx++; } #else rc = ARMCI_Malloc(a, bytes); assert(rc == 0); #endif assert(a[me]); } void destroy_array(void *ptr[]) { ARMCI_Barrier(); #if 0 int check = !ARMCI_Free(ptr[me]); assert(check); #endif } int loA[MAXDIMS], hiA[MAXDIMS]; int dimsA[MAXDIMS] = {DIM1, DIM2, DIM3, DIM4, DIM5, DIM6, DIM7}; int loB[MAXDIMS], hiB[MAXDIMS]; int dimsB[MAXDIMS] = {EDIM1, EDIM2, EDIM3, EDIM4, EDIM5, EDIM6, EDIM7}; int count[MAXDIMS]; int strideA[MAXDIMS], strideB[MAXDIMS]; int loC[MAXDIMS], hiC[MAXDIMS]; int idx[MAXDIMS] = {0, 0, 0, 0, 0, 0, 0}; void test_dim(int ndim) { int dim, elems; int i, j, proc; /* double a[DIM4][DIM3][DIM2][DIM1], b[EDIM4][EDIM3][EDIM2][EDIM1];*/ void *b[MAXPROC]; void *a, *c; elems = 1; strideA[0] = sizeof(double); strideB[0] = sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } elems *= dimsA[i]; } /* create shared and local arrays */ create_array(b, sizeof(double), ndim, dimsB); a = malloc(sizeof(double) * elems); assert(a); c = malloc(sizeof(double) * elems); assert(c); init(a, ndim, elems, dimsA); if (me == 0) { printf("--------array[%d", dimsA[0]); for (dim = 1; dim < ndim; dim++) { printf(",%d", dimsA[dim]); } printf("]--------\n"); } sleep(1); ARMCI_AllFence(); ARMCI_Barrier(); for (i = 0; i < LOOP; i++) { int idx1, idx2, idx3; get_range(ndim, dimsA, loA, hiA); new_range(ndim, dimsB, loA, hiA, loB, hiB); new_range(ndim, dimsA, loA, hiA, loC, hiC); proc = nproc - 1 - me; if (me == 0) { print_range("local", ndim, loA, hiA, "-> "); print_range("remote", ndim, loB, hiB, "-> "); print_range("local", ndim, loC, hiC, "\n"); } idx1 = Index(ndim, loA, dimsA); idx2 = Index(ndim, loB, dimsB); idx3 = Index(ndim, loC, dimsA); for (j = 0; j < ndim; j++) { count[j] = hiA[j] - loA[j] + 1; } count[0] *= sizeof(double); /* convert range to bytes at stride level zero */ (void)ARMCI_PutS((double *)a + idx1, strideA, (double *)b[proc] + idx2, strideB, count, ndim - 1, proc); /* sleep(1);*/ /* printf("%d: a=(%x,%f) b=(%x,%f)\n",me,idx1 + (double*)a,*(idx1 + (double*)a),idx2 + (double*)b,*(idx2 + (double*)b));*/ /* fflush(stdout);*/ /* sleep(1);*/ /* note that we do not need ARMCI_Fence here since * consectutive operations targeting the same process are ordered */ (void)ARMCI_GetS((double *)b[proc] + idx2, strideB, (double *)c + idx3, strideA, count, ndim - 1, proc); compare_patches(0., ndim, (double *)a + idx1, loA, hiA, dimsA, (double *)c + idx3, loC, hiC, dimsA); } free(c); destroy_array(b); free(a); } int nloA[MAXDIMS+1][MAXDIMS], nhiA[MAXDIMS+1][MAXDIMS]; int nloB[MAXDIMS+1][MAXDIMS], nhiB[MAXDIMS+1][MAXDIMS]; int nloC[MAXDIMS+1][MAXDIMS], nhiC[MAXDIMS+1][MAXDIMS]; int get_next_RRproc(int initialize, int ndim) { static int distance; int proc; if (initialize) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } if (nproc == 1) { distance = 0; } return(0); } /*send it to a different process everytime*/ proc = (me <= ((nproc % 2 == 0) ? ((nproc / 2) - 1) : (nproc / 2))) ? (me + distance) : (me - distance); if ((nproc % 2) != 0 && me == (nproc / 2)) { proc = me; } if (distance != 0) { if (me < (nproc / 2)) { distance++; if ((me + distance) >= nproc) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } distance -= me; } } else { distance--; if ((me - distance) >= (nproc / 2)) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } distance = distance + (me - distance); } } if (ndim != 1 && MAXDIMS > nproc && (ndim % (nproc / 2) == 0)) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } } } return(proc); } void test_nbdim() { int elems = 1, elems1 = 1; int i, j, proc, ndim, rc; void *b[MAXDIMS+1][MAXPROC]; void *a[MAXDIMS+1], *c[MAXDIMS+1]; armci_hdl_t hdl_put[MAXDIMS+1], hdl_get[MAXDIMS+1]; int idx1 = 0, idx2 = 0, idx3 = 0; /* create shared and local arrays */ for (ndim = 1; ndim <= MAXDIMS; ndim++) { elems1 *= dimsB[ndim-1]; elems *= dimsA[ndim-1]; rc = ARMCI_Malloc(b[ndim], sizeof(double) * elems1); assert(rc == 0); assert(b[ndim][me]); a[ndim] = malloc(sizeof(double) * elems); assert(a[ndim]); c[ndim] = malloc(sizeof(double) * elems); assert(c[ndim]); init(a[ndim], ndim, elems, dimsA); ARMCI_INIT_HANDLE(hdl_put + ndim); ARMCI_INIT_HANDLE(hdl_get + ndim); } ARMCI_AllFence(); ARMCI_Barrier(); (void)get_next_RRproc(1, 0); for (ndim = 1; ndim <= MAXDIMS; ndim++) { strideA[0] = sizeof(double); strideB[0] = sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } } proc = get_next_RRproc(0, ndim); get_range(ndim, dimsA, nloA[ndim], nhiA[ndim]); new_range(ndim, dimsB, nloA[ndim], nhiA[ndim], nloB[ndim], nhiB[ndim]); new_range(ndim, dimsA, nloA[ndim], nhiA[ndim], nloC[ndim], nhiC[ndim]); if (me == 0) { print_range("local", ndim, nloA[ndim], nhiA[ndim], "-> "); print_range("remote", ndim, nloB[ndim], nhiB[ndim], "-> "); print_range("local", ndim, nloC[ndim], nhiC[ndim], "\n"); fflush(stdout); sleep(1); } idx1 = Index(ndim, nloA[ndim], dimsA); idx2 = Index(ndim, nloB[ndim], dimsB); idx3 = Index(ndim, nloC[ndim], dimsA); for (j = 0; j < ndim; j++) { count[j] = nhiA[ndim][j] - nloA[ndim][j] + 1; } count[0] *= sizeof(double); if (ndim == 1) { (void)ARMCI_NbPut((double *)a[ndim] + idx1, (double *)b[ndim][proc] + idx2, count[0], proc, (hdl_put + ndim)); } else { (void)ARMCI_NbPutS((double *)a[ndim] + idx1, strideA, (double *)b[ndim][proc] + idx2, strideB, count, ndim - 1, proc, (hdl_put + ndim)); } } sleep(5); ARMCI_Barrier(); /*before we do gets, we have to make sure puts are complete on the remote processor*/ for (ndim = 1; ndim <= MAXDIMS; ndim++) { ARMCI_Wait(hdl_put + ndim); } ARMCI_Barrier(); ARMCI_AllFence(); (void)get_next_RRproc(1, 0); for (ndim = 1; ndim <= MAXDIMS; ndim++) { strideA[0] = sizeof(double); strideB[0] = sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } } /*send it to a different process everytime*/ proc = get_next_RRproc(0, ndim); idx1 = Index(ndim, nloA[ndim], dimsA); idx2 = Index(ndim, nloB[ndim], dimsB); idx3 = Index(ndim, nloC[ndim], dimsA); for (j = 0; j < ndim; j++) { count[j] = nhiA[ndim][j] - nloA[ndim][j] + 1; } count[0] *= sizeof(double); if (ndim == 1) { (void)ARMCI_NbGet((double *)b[ndim][proc] + idx2, (double *)c[ndim] + idx3, count[0], proc, (hdl_get + ndim)); } else { (void)ARMCI_NbGetS((double *)b[ndim][proc] + idx2, strideB, (double *)c[ndim] + idx3, strideA, count, ndim - 1, proc, (hdl_get + ndim)); } } ARMCI_Barrier(); if (me == 0) { printf("Now waiting for all non-blocking calls and verifying data...\n"); fflush(stdout); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { ARMCI_Wait(hdl_get + ndim); idx1 = Index(ndim, nloA[ndim], dimsA); idx2 = Index(ndim, nloB[ndim], dimsB); idx3 = Index(ndim, nloC[ndim], dimsA); compare_patches(0., ndim, (double *)a[ndim] + idx1, nloA[ndim], nhiA[ndim], dimsA, (double *)c[ndim] + idx3, nloC[ndim], nhiC[ndim], dimsA); } if (me == 0) { printf("OK\n"); fflush(stdout); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { destroy_array(b[ndim]); free(c[ndim]); free(a[ndim]); } } #define PTR_ARR_LEN 10 #define VLOOP 50 #define VEC_ELE_LEN 20 /*number of doubles in each dimention*/ #define GIOV_ARR_LEN 9 void verify_vector_data(double *data, int procs, int isput, int datalen) { double facto = 2.89; int i, j = 0, k = 0, kc = 0, dst = 0; if (isput) { facto = 1.89; } for (i = 0; i < datalen; i++) { if (dst != me) if (ARMCI_ABS((data[i] - (me + facto + dst)*((kc + 1)*(j % PTR_ARR_LEN + 1)))) > 0.001) { printf("\n%d:while verifying data of a op from proc=%d ", me, dst); printf("giov index=%d ptr_arr_index=%d \n :element index=%d", kc, (j % PTR_ARR_LEN), k); printf(" elem was supposed to be %f but is %f", (me + facto + dst)*((kc + 1)*(j % PTR_ARR_LEN + 1)) , data[i]); fflush(stdout); sleep(1); ARMCI_Error("vector non-blocking failed", 0); } k++; if (k == VEC_ELE_LEN) { j++; k = 0; if (j % PTR_ARR_LEN == 0) { kc++; if ((kc % GIOV_ARR_LEN) == 0) { kc = 0; dst++; } } } } } void test_vec_small() { double *getdst; double **putsrc; armci_giov_t dsc[MAXPROC*GIOV_ARR_LEN]; void **psrc; /*arrays of pointers to be used by giov_t*/ void **pdst; void *getsrc[MAXPROC]; /*to allocate mem via armci_malloc*/ void *putdst[MAXPROC]; /*to allocate mem via armci_malloc*/ armci_hdl_t hdl_put[MAXPROC], hdl_get[MAXPROC]; int i = 0, j = 0, k = 0, kc = 0, kcold = 0, rc, dstproc, dst = 0; int lenpergiov; lenpergiov = PTR_ARR_LEN * VEC_ELE_LEN; rc = ARMCI_Malloc(getsrc, sizeof(double) * nproc * GIOV_ARR_LEN * lenpergiov); assert(rc == 0); assert(getsrc[me]); rc = ARMCI_Malloc(putdst, sizeof(double) * nproc * GIOV_ARR_LEN * lenpergiov); assert(rc == 0); assert(putdst[me]); /*first malloc for getdst and putsrc, both are 2d arrays*/ getdst = (double *)malloc(sizeof(double) * nproc * GIOV_ARR_LEN * lenpergiov); putsrc = (double **)malloc(sizeof(double *) * nproc * GIOV_ARR_LEN * PTR_ARR_LEN); assert(getdst); assert(putsrc); for (i = 0; i < nproc * GIOV_ARR_LEN * PTR_ARR_LEN; i++) { putsrc[i] = (double *)malloc(sizeof(double) * VEC_ELE_LEN); assert(putsrc[i]); } /*allocating memory for psrc and pdst*/ psrc = (void **)malloc(sizeof(void *) * PTR_ARR_LEN * nproc * GIOV_ARR_LEN); pdst = (void **)malloc(sizeof(void *) * PTR_ARR_LEN * nproc * GIOV_ARR_LEN); assert(pdst); assert(psrc); for (i = 0; i < nproc * lenpergiov * GIOV_ARR_LEN; i++) { putsrc[j][k] = (me + 1.89 + dst) * ((kc + 1) * ((j % PTR_ARR_LEN) + 1)); ((double *)getsrc[me])[i] = (me + 2.89 + dst) * ((kc + 1) * (j % PTR_ARR_LEN + 1)); k++; if (k == VEC_ELE_LEN) { j++; k = 0; if ((j % PTR_ARR_LEN) == 0) { kc++; if ((kc % GIOV_ARR_LEN) == 0) { kc = 0; dst++; } } } } /*********************Testing NbPutV*********************************/ i = 0; j = 0; k = 0; kc = 0; dstproc = me; for (i = 0; i < nproc - 1; i++) { dstproc++; if (dstproc == nproc) { dstproc = 0; } for (j = 0; j < GIOV_ARR_LEN; j++) { kcold = kc; for (k = 0; k < PTR_ARR_LEN; k++, kc++) { double *ptr; psrc[kc] = (void *)putsrc[PTR_ARR_LEN*(dstproc*GIOV_ARR_LEN+j)+k]; ptr = (double *)putdst[dstproc]; pdst[kc] = (void *)(ptr + lenpergiov * (GIOV_ARR_LEN * me + j) + k * VEC_ELE_LEN); } dsc[j].bytes = VEC_ELE_LEN * sizeof(double); dsc[j].src_ptr_array = &psrc[kcold]; dsc[j].dst_ptr_array = &pdst[kcold]; dsc[j].ptr_array_len = PTR_ARR_LEN; } ARMCI_INIT_HANDLE(hdl_put + dstproc); if ((rc = ARMCI_NbPutV(dsc, GIOV_ARR_LEN, dstproc, hdl_put + dstproc))) { ARMCI_Error("putv failed", rc); } } if (me == 0) { printf("\n\tNow veryfying the vector put data for correctness"); } for (i = 0; i < nproc; i++)if (i != me) { ARMCI_Wait(hdl_put + i); } sleep(1); ARMCI_Barrier(); ARMCI_AllFence();/*every one syncs after put */ verify_vector_data((double *)putdst[me], nproc, 1, nproc * GIOV_ARR_LEN * lenpergiov); if (me == 0) { printf("\n\tPuts OK\n"); } /****************Done Testing NbPutV*********************************/ /*********************Testing NbGetV*********************************/ i = 0; j = 0; k = 0; kc = 0; dstproc = me; for (i = 0; i < nproc - 1; i++) { dstproc++; if (dstproc == nproc) { dstproc = 0; } for (j = 0; j < GIOV_ARR_LEN; j++) { kcold = kc; for (k = 0; k < PTR_ARR_LEN; k++, kc++) { double *ptr; ptr = getdst; pdst[kc] = (void *)(ptr + lenpergiov * (dstproc * GIOV_ARR_LEN + j) + k * VEC_ELE_LEN); ptr = (double *)(getsrc[dstproc]); psrc[kc] = (void *)(ptr + lenpergiov * (me * GIOV_ARR_LEN + j) + k * VEC_ELE_LEN); } dsc[j].bytes = VEC_ELE_LEN * sizeof(double); dsc[j].src_ptr_array = &psrc[kcold]; dsc[j].dst_ptr_array = &pdst[kcold]; dsc[j].ptr_array_len = PTR_ARR_LEN; } ARMCI_INIT_HANDLE(hdl_get + dstproc); if ((rc = ARMCI_NbGetV(dsc, GIOV_ARR_LEN, dstproc, hdl_get + dstproc))) { ARMCI_Error("putv failed", rc); } } if (me == 0) { printf("\n\tNow veryfying the vector get data for correctness"); } for (i = 0; i < nproc; i++)if (i != me) { ARMCI_Wait(hdl_get + i); } sleep(1); ARMCI_Barrier(); verify_vector_data((double *)getdst, nproc, 0, nproc * GIOV_ARR_LEN * lenpergiov); if (me == 0) { printf("\n\tGets OK\n"); } /****************Done Testing NbGetV*********************************/ free(pdst); free(psrc); free(getdst); for (i = 0; i < nproc * GIOV_ARR_LEN * PTR_ARR_LEN; i++) { free(putsrc[i]); } free(putsrc); } void GetPermutedProcList(int *ProcList) { int i, iswap, temp; if (nproc > MAXPROC) { ARMCI_Error("permute_proc: nproc to big ", nproc); } /* initialize list */ for (i = 0; i < nproc; i++) { ProcList[i] = i; } if (nproc == 1) { return; } /* every process generates different random sequence */ (void)srand((unsigned)me); /* list permutation generated by random swapping */ for (i = 0; i < nproc; i++) { iswap = (int)(rand() % nproc); temp = ProcList[iswap]; ProcList[iswap] = ProcList[i]; ProcList[i] = temp; } } /*\ Atomic Accumulate test: remote += alpha*local * Every process/or has its patch of array b updated TIMES*NPROC times. * The sequence of updates is random: everybody uses a randomly permuted list * and accumulate is non-collective (of-course) \*/ void test_acc(int ndim) { int dim, elems; int i, proc; void *b[MAXPROC]; void *a, *c; double alpha = 0.1, scale; int idx1, idx2; int *proclist = work; elems = 1; strideA[0] = sizeof(double); strideB[0] = sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } elems *= dimsA[i]; /* set up patch coordinates: same on every processor */ loA[i] = 0; hiA[i] = loA[i] + 1; loB[i] = dimsB[i] - 2; hiB[i] = loB[i] + 1; count[i] = hiA[i] - loA[i] + 1; } /* create shared and local arrays */ create_array(b, sizeof(double), ndim, dimsB); a = malloc(sizeof(double) * elems); assert(a); c = malloc(sizeof(double) * elems); assert(c); init(a, ndim, elems, dimsA); if (me == 0) { printf("--------array[%d", dimsA[0]); for (dim = 1; dim < ndim; dim++) { printf(",%d", dimsA[dim]); } printf("]--------\n"); } GetPermutedProcList(proclist); idx1 = Index(ndim, loA, dimsA); idx2 = Index(ndim, loB, dimsB); count[0] *= sizeof(double); /* convert range to bytes at stride level zero */ /* initialize all elements of array b to zero */ elems = 1; for (i = 0; i < ndim; i++) { elems *= dimsB[i]; } for (i = 0; i < elems; i++) { ((double *)b[me])[i] = 0.; } sleep(1); if (me == 0) { print_range("patch", ndim, loA, hiA, " -> "); print_range("patch", ndim, loB, hiB, "\n"); fflush(stdout); } ARMCI_AllFence(); ARMCI_Barrier(); for (i = 0; i < TIMES * nproc; i++) { proc = proclist[i%nproc]; (void)ARMCI_AccS(ARMCI_ACC_DBL, &alpha, (double *)a + idx1, strideA, (double *)b[proc] + idx2, strideB, count, ndim - 1, proc); } /* sleep(9);*/ ARMCI_AllFence(); ARMCI_Barrier(); /* copy my patch into local array c */ (void)ARMCI_GetS((double *)b[me] + idx2, strideB, (double *)c + idx1, strideA, count, ndim - 1, me); scale = alpha * TIMES * nproc; scale_patch(scale, ndim, (double *)a + idx1, loA, hiA, dimsA); compare_patches(.0001, ndim, (double *)a + idx1, loA, hiA, dimsA, (double *)c + idx1, loA, hiA, dimsA); ARMCI_Barrier(); if (0 == me) { printf(" OK\n\n"); fflush(stdout); } free(c); destroy_array(b); free(a); } /*************************** vector interface *********************************\ * tests vector interface for transfers of triangular sections of a 2-D array * ******************************************************************************/ void test_vector() { int dim, elems, ndim, cols, rows, mrc; int i, proc, loop; int rc; int idx1, idx3; void *b[MAXPROC]; void *a, *c; armci_giov_t dsc[MAX_DIM_VAL]; void *psrc[MAX_DIM_VAL]; void *pdst[MAX_DIM_VAL]; elems = 1; ndim = 2; for (i = 0; i < ndim; i++) { dimsA[i] = MAX_DIM_VAL; dimsB[i] = MAX_DIM_VAL + 1; elems *= dimsA[i]; } /* create shared and local arrays */ create_array(b, sizeof(double), ndim, dimsB); a = malloc(sizeof(double) * elems); assert(a); c = malloc(sizeof(double) * elems); assert(c); init(a, ndim, elems, dimsA); if (me == 0) { printf("--------array[%d", dimsA[0]); for (dim = 1; dim < ndim; dim++) { printf(",%d", dimsA[dim]); } printf("]--------\n"); } sleep(1); for (loop = 0; loop < LOOP; loop++) { get_range(ndim, dimsA, loA, hiA); new_range(ndim, dimsB, loA, hiA, loB, hiB); new_range(ndim, dimsA, loA, hiA, loC, hiC); proc = nproc - 1 - me; if (me == 0) { print_range("local", ndim, loA, hiA, "-> "); print_range("remote", ndim, loB, hiB, "-> "); print_range("local", ndim, loC, hiC, "\n"); } /* printf("array at source\n");*/ /* print_2D_double((double *)a, dimsA[0], loA, hiA);*/ cols = hiA[1] - loA[1] + 1; rows = hiA[0] - loA[0] + 1; mrc = ARMCI_MIN(cols, rows); /* generate a data descriptor for a lower-triangular patch */ for (i = 0; i < mrc; i++) { int ij[2]; int idx; ij[0] = loA[0] + i; ij[1] = loA[1] + i; idx = Index(ndim, ij, dimsA); psrc[i] = (double *)a + idx; ij[0] = loB[0] + i; ij[1] = loB[1] + i; idx = Index(ndim, ij, dimsB); pdst[i] = (double *)b[proc] + idx; dsc[i].bytes = (rows - i) * sizeof(double); dsc[i].src_ptr_array = &psrc[i]; dsc[i].dst_ptr_array = &pdst[i]; /* assume each element different in size (not true in rectangular patches) */ dsc[i].ptr_array_len = 1; } if ((rc = ARMCI_PutV(dsc, mrc, proc))) { ARMCI_Error("putv failed ", rc); } /* printf("array at destination\n");*/ /* print_2D_double((double *)b[proc], dimsB[0], loB, hiB);*/ /* generate a data descriptor for the upper-triangular patch */ /* there is one less element since diagonal is excluded */ for (i = 1; i < cols; i++) { int ij[2]; ij[0] = loA[0]; ij[1] = loA[1] + i; psrc[i-1] = (double *)a + Index(ndim, ij, dimsA); ij[0] = loB[0]; ij[1] = loB[1] + i; pdst[i-1] = (double *)b[proc] + Index(ndim, ij, dimsB); mrc = ARMCI_MIN(i, rows); dsc[i-1].bytes = mrc * sizeof(double); dsc[i-1].src_ptr_array = &psrc[i-1]; dsc[i-1].dst_ptr_array = &pdst[i-1]; /* assume each element different in size (not true in rectangular patches) */ dsc[i-1].ptr_array_len = 1; } if ((cols - 1))if ((rc = ARMCI_PutV(dsc, cols - 1, proc))) { ARMCI_Error("putv(2) failed ", rc); } /* we get back entire rectangular patch */ for (i = 0; i < cols; i++) { int ij[2]; ij[0] = loB[0]; ij[1] = loB[1] + i; psrc[i] = (double *)b[proc] + Index(ndim, ij, dimsB); ij[0] = loC[0]; ij[1] = loC[1] + i; pdst[i] = (double *)c + Index(ndim, ij, dimsA); } dsc[0].bytes = rows * sizeof(double); dsc[0].src_ptr_array = psrc; dsc[0].dst_ptr_array = pdst; dsc[0].ptr_array_len = cols; /* note that we do not need ARMCI_Fence here since * consecutive operations targeting the same process are ordered */ if ((rc = ARMCI_GetV(dsc, 1, proc))) { ARMCI_Error("getv failed ", rc); } idx1 = Index(ndim, loA, dimsA); idx3 = Index(ndim, loC, dimsA); compare_patches(0., ndim, (double *)a + idx1, loA, hiA, dimsA, (double *)c + idx3, loC, hiC, dimsA); } free(c); destroy_array(b); free(a); } /*\ Atomic Accumulate test for vector API: remote += alpha*local * Every process/or has its patch of array b updated TIMES*NPROC times. * The sequence of updates is random: everybody uses a randomly permuted list * and accumulate is non-collective (of-course) \*/ void test_vector_acc() { int dim, elems, bytes; int i, j, proc, rc, one = 1; void *b[MAXPROC]; void *psrc[ELEMS/2], *pdst[ELEMS/2]; void *a, *c; double alpha = 0.1, scale; int *proclist = work; armci_giov_t dsc; int check; elems = ELEMS; dim = 1; bytes = sizeof(double) * elems; /* create shared and local arrays */ create_array(b, sizeof(double), dim, &elems); a = malloc(bytes); assert(a); c = malloc(bytes); assert(c); init(a, dim, elems, &elems); if (me == 0) { printf("--------array[%d", elems); printf("]--------\n"); fflush(stdout); } GetPermutedProcList(proclist); /* initialize all elements of array b to zero */ for (i = 0; i < elems; i++) { ((double *)b[me])[i] = 0.; } sleep(1); dsc.bytes = sizeof(double); dsc.src_ptr_array = psrc; dsc.dst_ptr_array = pdst; dsc.ptr_array_len = elems / 2; ARMCI_Barrier(); for (i = 0; i < TIMES * nproc; i++) { /* proc=proclist[i%nproc];*/ proc = 0; /* accumulate even numbered elements */ for (j = 0; j < elems / 2; j++) { psrc[j] = 2 * j + (double *)a; pdst[j] = 2 * j + (double *)b[proc]; } if ((rc = ARMCI_AccV(ARMCI_ACC_DBL, &alpha, &dsc, 1, proc))) { ARMCI_Error("accumlate failed", rc); } /* for(j=0; j 0.1) { ARMCI_Error("Float register-originated put failed", 0); } if (ARMCI_ABS(fdst_get[i] - 100.01 *(i + 1)) > 0.1) { ARMCI_Error("Float register-originated get failed", 0); } } if (me == 0) { printf("OK\ndouble data type: "); } for (i = 0; i < elems; i++) { if (ARMCI_ABS(ddst[me][i] - 10.001 *(i + 1)) > 0.1) { ARMCI_Error("Double register-originated put failed", 0); } if (ARMCI_ABS(ddst_get[i] - 100.001 *(i + 1)) > 0.1) { ARMCI_Error("Double register-originated get failed", 0); } } if (me == 0) { printf("OK\n"); fflush(stdout); } ARMCI_AllFence(); ARMCI_Barrier(); destroy_array((void **)idst); destroy_array((void **)ldst); destroy_array((void **)fdst); destroy_array((void **)ddst); destroy_array((void **)isrc_get); destroy_array((void **)lsrc_get); destroy_array((void **)fsrc_get); destroy_array((void **)dsrc_get); } #define MAXELEMS 6400 #define NUMAGG 20 /* NUMAGG < MAXELEMS/10 */ #define MAX_REQUESTS 325 /* MAXELEMS/NUMAGG */ #define COUNT 50 void test_aggregate() { int i, j, k, rc, bytes, elems[2] = {MAXPROC, MAXELEMS}; double *ddst_put[MAXPROC]; double *ddst_get[MAXPROC]; double *dsrc[MAXPROC]; armci_hdl_t usr_hdl_put[MAXPROC]; armci_hdl_t usr_hdl_get[MAXPROC]; armci_giov_t darr; void *src_ptr[MAX_REQUESTS], *dst_ptr[MAX_REQUESTS]; int start = 0, end = 0; create_array((void **)ddst_put, sizeof(double), 2, elems); create_array((void **)ddst_get, sizeof(double), 2, elems); create_array((void **)dsrc, sizeof(double), 1, &elems[1]); for (i = 0; i < elems[1]; i++) { dsrc[me][i] = i * 1.001 * (me + 1); } for (i = 0; i < elems[0]*elems[1]; i++) { ddst_put[me][i] = 0.0; ddst_get[me][i] = 0.0; } ARMCI_Barrier(); for (i = 0; i < nproc; i++) { ARMCI_INIT_HANDLE(&usr_hdl_put[i]); } for (i = 0; i < nproc; i++) { ARMCI_INIT_HANDLE(&usr_hdl_get[i]); } for (i = 0; i < nproc; i++) { ARMCI_SET_AGGREGATE_HANDLE(&usr_hdl_put[i]); } for (i = 0; i < nproc; i++) { ARMCI_SET_AGGREGATE_HANDLE(&usr_hdl_get[i]); } /* Testing aggregate put */ for (i = 0; i < nproc; i++) { start = 0; end = COUNT * NUMAGG; for (j = start; j < end; j++) { bytes = sizeof(double); ARMCI_NbPutValueDouble(dsrc[me][j], &ddst_put[i][me*elems[1] + j], i, &usr_hdl_put[i]); } start = end; end = start + COUNT * NUMAGG; for (j = start, k = 0; j < end; j += NUMAGG, k++) { src_ptr[k] = (void *)&dsrc[me][j]; dst_ptr[k] = (void *)&ddst_put[i][me*elems[1] + j]; } darr.src_ptr_array = src_ptr; darr.dst_ptr_array = dst_ptr; darr.bytes = NUMAGG * sizeof(double); darr.ptr_array_len = k; if ((rc = ARMCI_NbPutV(&darr, 1, i, &usr_hdl_put[i]))) { ARMCI_Error("armci_nbputv failed\n", rc); } start = end; end = start + COUNT * NUMAGG; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbPutS(&dsrc[me][j], NULL, &ddst_put[i][me*elems[1] + j], NULL, &bytes, 0, i, &usr_hdl_put[i]))) { ARMCI_Error("armci_nbputs failed\n", rc); } } start = end; end = elems[1]; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbPut(&dsrc[me][j], &ddst_put[i][me*elems[1] + j], bytes, i, &usr_hdl_put[i]))) { ARMCI_Error("armci_nbput failed\n", rc); } } } for (i = 0; i < nproc; i++) { ARMCI_Wait(&usr_hdl_put[i]); } /* Testing aggregate get */ for (i = 0; i < nproc; i++) { start = 0; end = COUNT * NUMAGG; for (j = start, k = 0; j < end; j += NUMAGG, k++) { src_ptr[k] = (void *)&dsrc[i][j]; dst_ptr[k] = (void *)&ddst_get[me][i*elems[1] + j]; } darr.src_ptr_array = src_ptr; darr.dst_ptr_array = dst_ptr; darr.bytes = NUMAGG * sizeof(double); darr.ptr_array_len = k; if ((rc = ARMCI_NbGetV(&darr, 1, i, &usr_hdl_get[i]))) { ARMCI_Error("armci_nbgetv failed\n", rc); } start = end; end = start + COUNT * NUMAGG; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbGetS(&dsrc[i][j], NULL, &ddst_get[me][i*elems[1] + j], NULL, &bytes, 0, i, &usr_hdl_get[i]))) { ARMCI_Error("armci_nbputs failed\n", rc); } } start = end; end = elems[1]; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbGet(&dsrc[i][j], &ddst_get[me][i*elems[1] + j], bytes, i, &usr_hdl_get[i]))) { ARMCI_Error("armci_nbget failed\n", rc); } } } for (i = 0; i < nproc; i++) { ARMCI_Wait(&usr_hdl_get[i]); } ARMCI_Barrier(); ARMCI_AllFence(); ARMCI_Barrier(); for (i = 0; i < nproc; i++) { for (j = 0; j < elems[1]; j++) { if (ARMCI_ABS(ddst_put[me][i*elems[1] + j] - j * 1.001 *(i + 1)) > 0.1) { ARMCI_Error("aggregate put failed...1", 0); } } } ARMCI_Barrier(); if (me == 0) { printf(" aggregate put ..O.K.\n"); } fflush(stdout); for (i = 0; i < nproc; i++) { for (j = 0; j < elems[1]; j++) { if (ARMCI_ABS(ddst_get[me][i*elems[1] + j] - j * 1.001 *(i + 1)) > 0.1) { ARMCI_Error("aggregate get failed...1", 0); } } } ARMCI_Barrier(); if (me == 0) { printf(" aggregate get ..O.K.\n"); } fflush(stdout); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("O.K.\n"); fflush(stdout); } destroy_array((void **)ddst_put); destroy_array((void **)ddst_get); destroy_array((void **)dsrc); } void test_implicit() { int i, j, k, rc, bytes, elems[2] = {MAXPROC, MAXELEMS}; double *ddst_put[MAXPROC]; double *ddst_get[MAXPROC]; double *dsrc[MAXPROC]; armci_giov_t darr; void *src_ptr[MAX_REQUESTS], *dst_ptr[MAX_REQUESTS]; int start = 0, end = 0; armci_hdl_t usr_hdl[MAXPROC]; create_array((void **)ddst_put, sizeof(double), 2, elems); create_array((void **)ddst_get, sizeof(double), 2, elems); create_array((void **)dsrc, sizeof(double), 1, &elems[1]); for (i = 0; i < elems[1]; i++) { dsrc[me][i] = i * 1.001 * (me + 1); } for (i = 0; i < elems[0]*elems[1]; i++) { ddst_put[me][i] = 0.0; ddst_get[me][i] = 0.0; } ARMCI_Barrier(); for (i = 0; i < nproc; i++) { ARMCI_INIT_HANDLE(&usr_hdl[i]); } for (i = 0; i < nproc; i++) { start = 0; end = COUNT * NUMAGG; for (j = start; j < end; j++) { bytes = sizeof(double); ARMCI_NbPutValueDouble(dsrc[me][j], &ddst_put[i][me*elems[1] + j], i, NULL); } start = end; end = start + COUNT * NUMAGG; for (j = start, k = 0; j < end; j += NUMAGG, k++) { src_ptr[k] = (void *)&dsrc[me][j]; dst_ptr[k] = (void *)&ddst_put[i][me*elems[1] + j]; } darr.src_ptr_array = src_ptr; darr.dst_ptr_array = dst_ptr; darr.bytes = NUMAGG * sizeof(double); darr.ptr_array_len = k; if ((rc = ARMCI_NbPutV(&darr, 1, i, NULL))) { ARMCI_Error("armci_nbputv failed\n", rc); } start = end; end = start + COUNT * NUMAGG; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbPutS(&dsrc[me][j], NULL, &ddst_put[i][me*elems[1] + j], NULL, &bytes, 0, i, NULL))) { ARMCI_Error("armci_nbputs failed\n", rc); } } start = end; end = elems[1]; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbPut(&dsrc[me][j], &ddst_put[i][me*elems[1] + j], bytes, i, NULL))) { ARMCI_Error("armci_nbput failed\n", rc); } } } for (i = 0; i < nproc; i++) { start = 0; end = COUNT * NUMAGG; for (j = start, k = 0; j < end; j += NUMAGG, k++) { src_ptr[k] = (void *)&dsrc[i][j]; dst_ptr[k] = (void *)&ddst_get[me][i*elems[1] + j]; } darr.src_ptr_array = src_ptr; darr.dst_ptr_array = dst_ptr; darr.bytes = NUMAGG * sizeof(double); darr.ptr_array_len = k; if ((rc = ARMCI_NbGetV(&darr, 1, i, NULL))) { ARMCI_Error("armci_nbgetv failed\n", rc); } start = end; end = start + COUNT * NUMAGG; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbGetS(&dsrc[i][j], NULL, &ddst_get[me][i*elems[1] + j], NULL, &bytes, 0, i, NULL))) { ARMCI_Error("armci_nbputs failed\n", rc); } } start = end; end = elems[1]; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbGet(&dsrc[i][j], &ddst_get[me][i*elems[1] + j], bytes, i, NULL))) { ARMCI_Error("armci_nbget failed\n", rc); } } } ARMCI_WaitAll(); ARMCI_Barrier(); ARMCI_AllFence(); ARMCI_Barrier(); for (i = 0; i < nproc; i++) { for (j = 0; j < elems[1]; j++) { if (ARMCI_ABS(ddst_put[me][i*elems[1] + j] - j * 1.001 *(i + 1)) > 0.1) { ARMCI_Error("implicit handle(s) failed...(a)", 0); } } } ARMCI_Barrier(); for (i = 0; i < nproc; i++) { for (j = 0; j < elems[1]; j++) { if (ARMCI_ABS(ddst_get[me][i*elems[1] + j] - j * 1.001 *(i + 1)) > 0.1) { ARMCI_Error("implicit handles(s) failed...(b)", 0); } } } ARMCI_Barrier(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("O.K.\n\n"); fflush(stdout); } destroy_array((void **)ddst_put); destroy_array((void **)ddst_get); destroy_array((void **)dsrc); } int main(int argc, char *argv[]) { int ndim; armci_msg_init(&argc, &argv); ARMCI_Init_args(&argc, &argv); nproc = armci_msg_nproc(); me = armci_msg_me(); /* printf("nproc = %d, me = %d\n", nproc, me);*/ if (nproc > MAXPROC && me == 0) { ARMCI_Error("Test works for up to %d processors\n", MAXPROC); } if (me == 0) { printf("ARMCI test program (%d processes)\n", nproc); fflush(stdout); sleep(1); } /* if(me==1)armci_die("process 1 committing suicide",1); */ if (me == 0) { printf("\nTesting strided gets and puts\n"); printf("(Only std output for process 0 is printed)\n\n"); fflush(stdout); sleep(1); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { test_dim(ndim); } ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting non-blocking gets and puts\n"); fflush(stdout); sleep(1); } test_nbdim(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting non-blocking vector gets and puts\n"); fflush(stdout); sleep(1); } test_vec_small(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting atomic accumulate\n"); fflush(stdout); sleep(1); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { test_acc(ndim); } ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting Vector Interface using triangular patches of a 2-D array\n\n"); fflush(stdout); sleep(1); } test_vector(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting Accumulate with Vector Interface\n\n"); fflush(stdout); sleep(1); } test_vector_acc(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting atomic fetch&add\n"); printf("(Std Output for all processes is printed)\n\n"); fflush(stdout); sleep(1); } ARMCI_Barrier(); test_fetch_add(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting atomic swap\n"); fflush(stdout); } test_swap(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting register-originated put and get\n"); fflush(stdout); sleep(1); } test_rput(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting aggregate put/get requests\n"); fflush(stdout); } /** * Aggregate put/get requests cannot be tested for\ number of procs * greater than 32. (Current implementation of aggregate put/get * can use at the maximum of 32 handles (defined by macro * _MAX_AGG_BUFFERS in aggregate.c). This test case is written in * such a way that each process puts/gets data to all the other * processes, thus the number of aggregate handle used is equal to * the number of processes created. */ if (nproc > 32) { if (me == 0) { printf("\n WARNING: Aggregate put/get requests cannot be tested for number of procs greater than 32.\n\n"); fflush(stdout); } } else { test_aggregate(); } ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting implicit handles\n"); fflush(stdout); } test_implicit(); ARMCI_AllFence(); ARMCI_Barrier(); ARMCI_Barrier(); #if MEMLOCK_TEST test_memlock(); #endif ARMCI_Barrier(); if (me == 0) { printf("All tests passed\n"); fflush(stdout); } sleep(2); #ifdef NEWMALLOC { int i, j; for (i = 0; i < g_idx; i++) for (j = 0; j < nproc; j++) { ARMCI_Memdt(&meminfo[i][j], 0); } for (i = 0; i < g_idx; i++) { ARMCI_Memctl(&meminfo[i][me]); } } #endif ARMCI_Barrier(); ARMCI_Finalize(); armci_msg_finalize(); /*MPI_Finalize();*/ return(0); } ga-5.9.2/armci/testing/test2.c000066400000000000000000001014011500715745200160760ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: test2.c,v 1.1.4.1 2007-05-29 19:36:23 manoj Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_SYS_TIME_H # include #endif #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif /*#define ARMCI_INT -99 */ /*#define ARMCI_LONG -101 */ /*#define ARMCI_FLOAT -306 */ /*#define ARMCI_DOUBLE -307 */ #define FLOAT_EPS ((float) 1.0 / 4096) #define DOUBLE_EPS ((double) 1.0 / 16384) #include "armci.h" #include "message.h" #define DIM1 5 #define DIM2 3 #ifdef __sun /* Solaris has shared memory shortages in the default system configuration */ # define DIM3 6 # define DIM4 5 # define DIM5 4 #elif defined(__alpha__) # define DIM3 8 # define DIM4 5 # define DIM5 6 #else # define DIM3 8 # define DIM4 9 # define DIM5 7 #endif #define DIM6 3 #define DIM7 2 #define OFF 1 #define EDIM1 (DIM1+OFF) #define EDIM2 (DIM2+OFF) #define EDIM3 (DIM3+OFF) #define EDIM4 (DIM4+OFF) #define EDIM5 (DIM5+OFF) #define EDIM6 (DIM6+OFF) #define EDIM7 (DIM7+OFF) #define DIMS 4 #define MAXDIMS 7 #define MAX_DIM_VAL 50 #define LOOP 200 #define BASE 100. #define MAXPROC 1024 #define TIMES 100 # define ELEMS 200 typedef struct { float real; float imag; } cmpl_t; typedef struct { double real; double imag; } dcmpl_t; /***************************** macros ************************/ #define COPY(src, dst, bytes) memcpy((dst),(src),(bytes)) #define ARMCI_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define ARMCI_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define ARMCI_ABS(a) (((a) <0) ? -(a) : (a)) /***************************** global data *******************/ int me, nproc; void *work[MAXPROC]; /* work array for propagating addresses */ void create_array(void *a[], int elem_size, int ndim, int dims[]) { int bytes = elem_size, i, rc; assert(ndim <= MAXDIMS); for (i = 0; i < ndim; i++) { bytes *= dims[i]; } rc = ARMCI_Malloc(a, bytes); assert(rc == 0); assert(a[me]); } void destroy_array(void *ptr[]) { int check; ARMCI_Barrier(); check = !ARMCI_Free(ptr[me]); assert(check); } int loA[MAXDIMS], hiA[MAXDIMS]; int dimsA[MAXDIMS] = {DIM1, DIM2, DIM3, DIM4, DIM5, DIM6, DIM7}; int loB[MAXDIMS], hiB[MAXDIMS]; int dimsB[MAXDIMS] = {EDIM1, EDIM2, EDIM3, EDIM4, EDIM5, EDIM6, EDIM7}; int count[MAXDIMS]; int strideA[MAXDIMS], strideB[MAXDIMS]; int loC[MAXDIMS], hiC[MAXDIMS]; int idx[MAXDIMS] = {0, 0, 0, 0, 0, 0, 0}; void test_brdcst(int datatype) { void *a[6]; int len[6] = {1, 10, 100, 1000, 10000, 100000}; int datatype_size = 0; int i, j; switch (datatype) { case ARMCI_INT: datatype_size = sizeof(int); for (i = 0; i < 6; i++) { a[i] = malloc(len[i] * datatype_size); } for (i = 0; i < 6; i++) if (me == 0) for (j = 0; j < len[i]; j++) { ((int *) a[i])[j] = (int) j; } else { memset(a[i], 0x0, len[i] * datatype_size); } break; case ARMCI_LONG: datatype_size = sizeof(long); for (i = 0; i < 6; i++) { a[i] = malloc(len[i] * datatype_size); } for (i = 0; i < 6; i++) if (me == 0) for (j = 0; j < len[i]; j++) { ((long *) a[i])[j] = (long) j; } else { memset(a[i], 0x0, len[i] * datatype_size); } break; case ARMCI_FLOAT: datatype_size = sizeof(float); for (i = 0; i < 6; i++) { a[i] = malloc(len[i] * datatype_size); } for (i = 0; i < 6; i++) if (me == 0) for (j = 0; j < len[i]; j++) { ((float *) a[i])[j] = (float) j; } else { memset(a[i], 0x0, len[i] * datatype_size); } break; case ARMCI_DOUBLE: datatype_size = sizeof(double); for (i = 0; i < 6; i++) { a[i] = malloc(len[i] * datatype_size); } for (i = 0; i < 6; i++) if (me == 0) for (j = 0; j < len[i]; j++) { ((double *) a[i])[j] = (double) j; } else { memset(a[i], 0x0, len[i] * datatype_size); } break; default: break; } for (i = 0; i < 6; i++) { armci_msg_brdcst(a[i], len[i] * datatype_size, 0); } switch (datatype) { case ARMCI_INT: for (i = 0; i < 6; i++) for (j = 0; j < len[i]; j++) if (((int *) a[i])[j] != (int) j) { printf("ERROR a[%d][%d] = %d != %d\n", i, j, ((int *) a[i])[j], (int) j); ARMCI_Error("armci_brdcst failed (int)\n", 0); } break; case ARMCI_LONG: for (i = 0; i < 6; i++) for (j = 0; j < len[i]; j++) if (((long *) a[i])[j] != (long) j) { printf("ERROR a[%d][%d] = %ld != %ld\n", i, j, ((long *) a[i])[j], (long) j); ARMCI_Error("armci_brdcst failed (long)\n", 0); } break; case ARMCI_FLOAT: for (i = 0; i < 6; i++) for (j = 0; j < len[i]; j++) if (((float *) a[i])[j] != (float) j) { printf("ERROR a[%d][%d] = %f != %f\n", i, j, ((float *) a[i])[j], (float) j); ARMCI_Error("armci_brdcst failed (float)\n", 0); } break; case ARMCI_DOUBLE: for (i = 0; i < 6; i++) for (j = 0; j < len[i]; j++) if (((double *) a[i])[j] != (double) j) { printf("ERROR a[%d][%d] = %f != %f\n", i, j, ((double *) a[i])[j], (double) j); ARMCI_Error("armci_brdcst failed (double)\n", 0); } break; default: break; } for (i = 0; i < 6; i++) { free(a[i]); } } void test_gop2_or_reduce(const int datatype, char *op, const int reduce_test) { void *a[6]; int len[6] = {1, 10, 100, 1000, 10000, 100000}; int len_length = 3; int datatype_size = 0; int i, j; char *test_type; int verbose = 0; if (reduce_test == 0) { test_type = "gop2"; } else { test_type = "reduce"; } switch (datatype) { case ARMCI_INT: datatype_size = sizeof(int); for (i = 0; i < len_length; i++) { a[i] = malloc(len[i] * datatype_size); } for (i = 0; i < len_length; i++) for (j = 0; j < len[i]; j++) { ((int *) a[i])[j] = (int)(me + j) * (((me + j) % 2 == 0) ? 1 : -1); } for (i = 0; i < len_length; i++) { if (me == 0 && verbose != 0) { printf("testing %s %s message size = %d op = %s\n", test_type, "ARMCI_INT", len[i], op); } if (reduce_test == 0) { armci_msg_igop(a[i], len[i], op); } else { armci_msg_reduce(a[i], len[i], op, datatype); } } if (me == 0 || reduce_test == 0) for (i = 0; i < len_length; i++) { if (me == 0 && verbose != 0) { printf("checking %s %s message size = %d op = %s\n", test_type, "ARMCI_INT", len[i], op); } for (j = 0; j < len[i]; j++) if (strncmp(op, "+", 1) == 0) { int compare = 0; if (nproc % 2 == 0) { if (j % 2 == 0) { compare = -nproc / 2; } else { compare = nproc / 2; } } else { if (j % 2 == 0) { compare = j + nproc / 2; } else { compare = -(j + nproc / 2); } } if (((int *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %d != %d\n", test_type, "ARMCI_INT", op, i, j, ((int *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "*", 1) == 0) { int compare = 1; int k = 0; for (k = 0; k < nproc; k++) { compare *= (k + j) * (((k + j) % 2 == 0) ? 1 : -1); } if (((int *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %d != %d\n", test_type, "ARMCI_INT", op, i, j, ((int *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "min", 3) == 0) { int compare = -(j + nproc - 1); if (compare % 2 == 0 && nproc > 1) { compare = -(j + nproc - 2); } if (((int *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %d != %d\n", test_type, "ARMCI_INT", op, i, j, ((int *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "max", 3) == 0) { int compare = j + nproc - 1; if (compare % 2 != 0 && nproc > 1) { compare = j + nproc - 2; } if (((int *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %d != %d\n", test_type, "ARMCI_INT", op, i, j, ((int *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "absmax", 6) == 0) { int compare = j + nproc - 1; if (((int *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %d != %d\n", test_type, "ARMCI_INT", op, i, j, ((int *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "absmin", 6) == 0) { int compare = j; if (((int *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %d != %d\n", test_type, "ARMCI_INT", op, i, j, ((int *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "or", 2) == 0) { } } break; case ARMCI_LONG: datatype_size = sizeof(long); for (i = 0; i < len_length; i++) { a[i] = malloc(len[i] * datatype_size); } for (i = 0; i < len_length; i++) for (j = 0; j < len[i]; j++) { ((long *) a[i])[j] = (long)(me + j) * (((me + j) % 2 == 0) ? 1 : -1); } for (i = 0; i < len_length; i++) { if (me == 0 && verbose != 0) { printf("testing %s %s message size = %d op = %s\n", test_type, "ARMCI_LONG", len[i], op); } if (reduce_test == 0) { armci_msg_lgop(a[i], len[i], op); } else { armci_msg_reduce(a[i], len[i], op, datatype); } } if (me == 0 || reduce_test == 0) for (i = 0; i < len_length; i++) { if (me == 0 && verbose != 0) { printf("checking %s %s message size = %d op = %s\n", test_type, "ARMCI_LONG", len[i], op); } for (j = 0; j < len[i]; j++) if (strncmp(op, "+", 1) == 0) { int compare = 0; if (nproc % 2 == 0) { if (j % 2 == 0) { compare = -nproc / 2; } else { compare = nproc / 2; } } else { if (j % 2 == 0) { compare = j + nproc / 2; } else { compare = -(j + nproc / 2); } } if (((long *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %ld != %d\n", test_type, "ARMCI_LONG", op, i, j, ((long *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "*", 1) == 0) { int compare = 1; int k = 0; for (k = 0; k < nproc; k++) { compare *= (k + j) * (((k + j) % 2 == 0) ? 1 : -1); } if (((long *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %ld != %d\n", test_type, "ARMCI_LONG", op, i, j, ((long *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "min", 3) == 0) { int compare = -(j + nproc - 1); if (compare % 2 == 0 && nproc > 1) { compare = -(j + nproc - 2); } if (((long *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %ld != %d\n", test_type, "ARMCI_LONG", op, i, j, ((long *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "max", 3) == 0) { int compare = j + nproc - 1; if (compare % 2 != 0 && nproc > 1) { compare = j + nproc - 2; } if (((long *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %ld != %d\n", test_type, "ARMCI_LONG", op, i, j, ((long *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "absmax", 6) == 0) { int compare = j + nproc - 1; if (((long *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %ld != %d\n", test_type, "ARMCI_LONG", op, i, j, ((long *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "absmin", 6) == 0) { int compare = j; if (((long *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %ld != %d\n", test_type, "ARMCI_LONG", op, i, j, ((long *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "or", 2) == 0) { } } break; case ARMCI_FLOAT: datatype_size = sizeof(float); for (i = 0; i < len_length; i++) { a[i] = malloc(len[i] * datatype_size); } for (i = 0; i < len_length; i++) for (j = 0; j < len[i]; j++) { ((float *) a[i])[j] = (float)(me + j) * (((me + j) % 2 == 0) ? 1.0 / nproc : -1.0 / nproc); } for (i = 0; i < len_length; i++) { if (me == 0 && verbose != 0) { printf("testing %s ARMCI_FLOAT message size = %d op = %s\n", test_type, len[i], op); } if (reduce_test == 0) { armci_msg_fgop(a[i], len[i], op); } else { armci_msg_reduce(a[i], len[i], op, datatype); } } if (me == 0 || reduce_test == 0) for (i = 0; i < len_length; i++) { if (me == 0 && verbose != 0) { printf("checking %s ARMCI_FLOAT message size = %d op = %s\n", test_type, len[i], op); } for (j = 0; j < len[i]; j++) if (strncmp(op, "+", 1) == 0) { float compare = 0.0; if (nproc % 2 == 0) { if (j % 2 == 0) { compare = -(((int)nproc / 2) / (float) nproc); } else { compare = ((int)nproc / 2) / (float) nproc; } } else { if (j % 2 == 0) { compare = ((int) j + nproc / 2) / (float) nproc; } else { compare = -(((int) j + nproc / 2) / (float) nproc); } } if (ARMCI_ABS(((float *) a[i])[j] - compare) > ARMCI_ABS(compare) * FLOAT_EPS) { printf("ERROR %s %s %s a[%d][%d] = %f != %f\n", test_type, "ARMCI_FLOAT", op, i, j, ((float *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "*", 1) == 0) { float compare = 1.0; int k = 0; for (k = 0; k < nproc; k++) { compare *= ((float) k + j) / (float) nproc; } if ((nproc / 2) % 2 != 0) if (nproc % 2 != 0) if (j % 2 == 0) { compare *= -1.0; } if (ARMCI_ABS(((float *) a[i])[j] - compare) > ARMCI_ABS(compare) * FLOAT_EPS) { printf("ERROR %s %s %s a[%d][%d] = %f != %f\n", test_type, "ARMCI_FLOAT", op, i, j, ((float *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "min", 3) == 0) { float compare = -((float) j + nproc - 1) / nproc; if ((j + nproc - 1) % 2 == 0 && nproc > 1) { compare = -((float) j + nproc - 2) / nproc; } if (((float *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %f != %f\n", test_type, "ARMCI_FLOAT", op, i, j, ((float *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "max", 3) == 0) { float compare = ((float) j + nproc - 1) / nproc; if ((j + nproc - 1) % 2 != 0 && nproc > 1) { compare = ((float) j + nproc - 2) / nproc; } if (((float *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %f != %f\n", test_type, "ARMCI_FLOAT", op, i, j, ((float *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "absmax", 6) == 0) { float compare = ((float) j + nproc - 1) / nproc; if (((float *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %f != %f\n", test_type, "ARMCI_FLOAT", op, i, j, ((float *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "absmin", 6) == 0) { float compare = (float) j / nproc; if (((float *) a[i])[j] != compare) { printf("ERROR %s %s %s a[%d][%d] = %f != %f\n", test_type, "ARMCI_FLOAT", op, i, j, ((float *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } } break; case ARMCI_DOUBLE: datatype_size = sizeof(double); for (i = 0; i < len_length; i++) { a[i] = malloc(len[i] * datatype_size); } for (i = 0; i < len_length; i++) for (j = 0; j < len[i]; j++) { ((double *) a[i])[j] = (double)(me + j) * (((me + j) % 2 == 0) ? 1.0 / nproc : -1.0 / nproc); } for (i = 0; i < len_length; i++) { if (me == 0 && verbose != 0) { printf("testing %s ARMCI_DOUBLE message size = %d op = %s\n", test_type, len[i], op); } if (reduce_test == 0) { armci_msg_dgop(a[i], len[i], op); } else { armci_msg_reduce(a[i], len[i], op, datatype); } } if (me == 0 || reduce_test == 0) for (i = 0; i < len_length; i++) { if (me == 0 && verbose != 0) { printf("checking %s ARMCI_DOUBLE message size = %d op = %s\n", test_type, len[i], op); } for (j = 0; j < len[i]; j++) if (strncmp(op, "+", 1) == 0) { double compare = 0.0; if (nproc % 2 == 0) { if (j % 2 == 0) { compare = -(((int)nproc / 2) / (double) nproc); } else { compare = ((int)nproc / 2) / (double) nproc; } } else { if (j % 2 == 0) { compare = ((int) j + nproc / 2) / (double) nproc; } else { compare = -(((int) j + nproc / 2) / (double) nproc); } } if (ARMCI_ABS(((double *) a[i])[j] - compare) > ARMCI_ABS(compare) * DOUBLE_EPS) { printf("ERROR %s %s %s a[%d][%d] = %f != %f\n", test_type, "ARMCI_DOUBLE", op, i, j, ((double *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "*", 1) == 0) { double compare = 1.0; int k = 0; for (k = 0; k < nproc; k++) { compare *= ((float) k + j) / (float) nproc; } if ((nproc / 2) % 2 != 0) if (nproc % 2 != 0) if (j % 2 == 0) { compare *= -1.0; } if (ARMCI_ABS(((double *) a[i])[j] - compare) > ARMCI_ABS(compare) * DOUBLE_EPS) { printf("ERROR %s %s %s a[%d][%d] = %f != %f\n", test_type, "ARMCI_DOUBLE", op, i, j, ((double *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "min", 3) == 0) { double compare = -((double) j + nproc - 1) / nproc; if ((j + nproc - 1) % 2 == 0 && nproc > 1) { compare = -((double) j + nproc - 2) / nproc; } if (ARMCI_ABS(((double *) a[i])[j] - compare) > ARMCI_ABS(compare) * DOUBLE_EPS) { printf("ERROR %s %s %s a[%d][%d] = %f != %f\n", test_type, "ARMCI_DOUBLE", op, i, j, ((double *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "max", 3) == 0) { double compare = ((double) j + nproc - 1) / nproc; if ((j + nproc - 1) % 2 != 0 && nproc > 1) { compare = ((double) j + nproc - 2) / nproc; } if (ARMCI_ABS(((double *) a[i])[j] - compare) > ARMCI_ABS(compare) * DOUBLE_EPS) { printf("ERROR %s %s %s a[%d][%d] = %f != %f\n", test_type, "ARMCI_DOUBLE", op, i, j, ((double *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "absmax", 6) == 0) { double compare = ((double) j + nproc - 1) / nproc; if (ARMCI_ABS(((double *) a[i])[j] - compare) > ARMCI_ABS(compare) * DOUBLE_EPS) { printf("ERROR %s %s %s a[%d][%d] = %f != %f\n", test_type, "ARMCI_DOUBLE", op, i, j, ((double *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } else if (strncmp(op, "absmin", 6) == 0) { double compare = (double) j / nproc; if (ARMCI_ABS(((double *) a[i])[j] - compare) > ARMCI_ABS(compare) * DOUBLE_EPS) { printf("ERROR %s %s %s a[%d][%d] = %f != %f\n", test_type, "ARMCI_DOUBLE", op, i, j, ((double *) a[i])[j], compare); ARMCI_Error("test_gop2_or_reduce failed\n", 0); } } } break; default: break; } for (i = 0; i < len_length; i++) { free(a[i]); } } void test_collective(const int datatype) { char *op[7] = {"+", "*", "min", "max", "absmax", "absmin", "or"}; int i = 0; int num_tests = 7; if (datatype == ARMCI_DOUBLE || datatype == ARMCI_FLOAT) { num_tests = 6; } /* test armci_msg_brdcst */ test_brdcst(datatype); /* test armci_msg_gop2 */ for (i = 0; i < num_tests; i++) { test_gop2_or_reduce(datatype, op[i], 0); } /* test armci_msg_reduce */ for (i = 0; i < num_tests; i++) { test_gop2_or_reduce(datatype, op[i], 1); } ARMCI_Barrier(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("O.K.\n\n"); fflush(stdout); } } void test_acc_type(const int datatype) { int i = 0; int datatype_size = 0; void *scale; void *a; void *b[MAXPROC]; int elems = ELEMS; int dim = 1; int count = 0; int strideA = 0; int strideB = 0; switch (datatype) { case ARMCI_ACC_INT: datatype_size = sizeof(int); scale = malloc(datatype_size); *((int *) scale) = 1; a = malloc(elems * datatype_size); create_array((void **)b, datatype_size, dim, &elems); for (i = 0; i < elems; i++) { ((int *) a)[i] = i + me; ((int *) b[me])[i] = 0; } break; case ARMCI_ACC_LNG: datatype_size = sizeof(long); scale = malloc(datatype_size); *((long *) scale) = 1; a = malloc(elems * datatype_size); create_array((void **)b, datatype_size, dim, &elems); for (i = 0; i < elems; i++) { ((long *) a)[i] = i + me; ((long *) b[me])[i] = 0; } break; case ARMCI_ACC_FLT: datatype_size = sizeof(float); scale = malloc(datatype_size); *((float *) scale) = 1.0; a = malloc(elems * datatype_size); create_array((void **)b, datatype_size, dim, &elems); for (i = 0; i < elems; i++) { ((float *) a)[i] = (float) i + me; ((float *) b[me])[i] = 0.0; } break; case ARMCI_ACC_DBL: datatype_size = sizeof(double); scale = malloc(datatype_size); *((double *) scale) = 1.0; a = malloc(elems * datatype_size); create_array((void **)b, datatype_size, dim, &elems); for (i = 0; i < elems; i++) { ((double *) a)[i] = (double) i + me; ((double *) b[me])[i] = 0.0; } break; case ARMCI_ACC_CPL: datatype_size = sizeof(cmpl_t); scale = malloc(datatype_size); ((cmpl_t *) scale)->real = 2.0; ((cmpl_t *) scale)->imag = 1.0; a = malloc(elems * datatype_size); create_array((void **)b, datatype_size, dim, &elems); for (i = 0; i < elems; i++) { ((cmpl_t *) a)[i].real = ((float) i + me); ((cmpl_t *) a)[i].imag = ((float) i + me); ((cmpl_t *) b[me])[i].real = 0.0; ((cmpl_t *) b[me])[i].imag = 0.0; } break; case ARMCI_ACC_DCP: datatype_size = sizeof(dcmpl_t); scale = malloc(datatype_size); ((dcmpl_t *) scale)->real = 2.0; ((dcmpl_t *) scale)->imag = 1.0; a = malloc(elems * datatype_size); create_array((void **)b, datatype_size, dim, &elems); for (i = 0; i < elems; i++) { ((dcmpl_t *) a)[i].real = ((double) i + me); ((dcmpl_t *) a)[i].imag = ((double) i + me); ((dcmpl_t *) b[me])[i].real = 0.0; ((dcmpl_t *) b[me])[i].imag = 0.0; } break; default: return; break; } count = elems * datatype_size; strideA = elems * datatype_size; strideB = elems * datatype_size; ARMCI_AllFence(); ARMCI_Barrier(); for (i = 0; i < nproc; i++) { ARMCI_AccS(datatype, scale, a, &strideA, b[(me + i) % nproc], &strideB, &count, 0, (me + i) % nproc); } ARMCI_AllFence(); ARMCI_Barrier(); switch (datatype) { case ARMCI_ACC_INT: for (i = 0; i < elems; i++) { int compare = (i * nproc) + nproc / 2 * (nproc - 1); if (((int *)b[me])[i] != compare) { printf("ERROR accumulate ARMCI_ACC_INT [%d] = %d != %d\n", i, ((int *)b[me])[i], compare); ARMCI_Error("test_acc_type failed\n", 0); } } break; case ARMCI_ACC_LNG: for (i = 0; i < elems; i++) { long compare = (i * nproc) + nproc / 2 * (nproc - 1); if (((long *)b[me])[i] != compare) { printf("ERROR accumulate ARMCI_ACC_LNG [%d] = %d != %ld\n", i, ((int *)b[me])[i], compare); ARMCI_Error("test_acc_type failed\n", 0); } } break; case ARMCI_ACC_FLT: for (i = 0; i < elems; i++) { float compare = (float)((i * nproc) + nproc / 2 * (nproc - 1)); if (((float *)b[me])[i] != compare) { printf("ERROR accumulate ARMCI_ACC_FLT [%d] = %f != %f\n", i, ((float *)b[me])[i], compare); ARMCI_Error("test_acc_type failed\n", 0); } } break; case ARMCI_ACC_DBL: for (i = 0; i < elems; i++) { double compare = (double)((i * nproc) + nproc / 2 * (nproc - 1)); if (((double *)b[me])[i] != (double)((i * nproc) + nproc / 2 *(nproc - 1))) { printf("ERROR accumulate ARMCI_ACC_DBL [%d] = %f != %f \n", i, ((double *)b[me])[i], compare); ARMCI_Error("test_acc_type failed\n", 0); } } break; case ARMCI_ACC_CPL: for (i = 0; i < elems; i++) { float compare = (float)((i * nproc) + nproc / 2 * (nproc - 1)); if (((cmpl_t *)b[me])[i].real != compare && ((cmpl_t *)b[me])[i].imag != 3 * compare) { printf("ERROR accumulate ARMCI_ACC_CPL [%d] = %f + %fj != %f + %fj\n", i, ((cmpl_t *)b[me])[i].real, ((cmpl_t *)b[me])[i].imag, compare, 3 * compare); ARMCI_Error("test_acc_type failed\n", 0); } } break; case ARMCI_ACC_DCP: for (i = 0; i < elems; i++) { double compare = (double)((i * nproc) + nproc / 2 * (nproc - 1)); if (((dcmpl_t *)b[me])[i].real != compare && ((dcmpl_t *)b[me])[i].imag != 3 * compare) { printf("ERROR accumulate ARMCI_ACC_DCP [%d] = %f + %fj != %f + %fj\n", i, ((dcmpl_t *)b[me])[i].real, ((dcmpl_t *)b[me])[i].imag, compare, 3 * compare); ARMCI_Error("test_acc_type failed\n", 0); } } break; default: break; } ARMCI_Barrier(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("O.K.\n\n"); fflush(stdout); } destroy_array((void **)b); free(a); free(scale); } int main(int argc, char *argv[]) { int i; struct timeval start_time[14]; struct timeval stop_time[14]; /* char * test_name[14] = { "dim", "nbdim", "vec_small", "acc", "vector", "vector_acc", "fetch_add", "swap", "rput", "aggregate", "implicit", "memlock", "acc_type", "collective" }; int test_flags[14] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1 }; */ char *test_name[2] = { "acc_type", "collective" }; int test_flags[2] = { 1, 1 }; #define TEST_ACC_TYPE 0 #define TEST_COLLECTIVE 1 armci_msg_init(&argc, &argv); ARMCI_Init_args(&argc, &argv); nproc = armci_msg_nproc(); me = armci_msg_me(); if (nproc > MAXPROC && me == 0) { ARMCI_Error("Test works for up to %d processors\n", MAXPROC); } if (me == 0) { printf("ARMCI test program (%d processes)\n", nproc); fflush(stdout); sleep(1); } gettimeofday(&start_time[TEST_ACC_TYPE], NULL); if (test_flags[TEST_ACC_TYPE] == 1) { if (me == 0) { printf("\nTesting Accumulate Types\n"); fflush(stdout); } ARMCI_Barrier(); if (me == 0) { printf("Test Accumulate ARMCI_ACC_INT\n"); fflush(stdout); } test_acc_type(ARMCI_ACC_INT); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("Test Accumulate ARMCI_ACC_LNG\n"); fflush(stdout); } test_acc_type(ARMCI_ACC_LNG); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("Test Accumulate ARMCI_ACC_FLT\n"); fflush(stdout); } test_acc_type(ARMCI_ACC_FLT); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("Test Accumulate ARMCI_ACC_DBL\n"); fflush(stdout); } test_acc_type(ARMCI_ACC_DBL); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("Test Accumulate ARMCI_ACC_CPL\n"); fflush(stdout); } test_acc_type(ARMCI_ACC_CPL); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("Test Accumulate ARMCI_ACC_DCP\n"); fflush(stdout); } test_acc_type(ARMCI_ACC_DCP); ARMCI_AllFence(); ARMCI_Barrier(); } gettimeofday(&stop_time[TEST_ACC_TYPE], NULL); gettimeofday(&start_time[TEST_COLLECTIVE], NULL); if (test_flags[TEST_COLLECTIVE] == 1) { if (me == 0) { printf("\nTesting Collective Types\n"); fflush(stdout); } if (me == 0) { printf("Test Collective ARMCI_INT\n"); fflush(stdout); } ARMCI_Barrier(); test_collective(ARMCI_INT); ARMCI_Barrier(); if (me == 0) { printf("Test Collective ARMCI_LONG\n"); fflush(stdout); } ARMCI_Barrier(); test_collective(ARMCI_LONG); ARMCI_Barrier(); if (me == 0) { printf("Test Collective ARMCI_FLOAT\n"); fflush(stdout); } ARMCI_Barrier(); test_collective(ARMCI_FLOAT); ARMCI_Barrier(); if (me == 0) { printf("Test Collective ARMCI_DOUBLE\n"); fflush(stdout); } ARMCI_Barrier(); test_collective(ARMCI_DOUBLE); ARMCI_Barrier(); } gettimeofday(&stop_time[TEST_COLLECTIVE], NULL); if (me == 0) { printf("Accumulate and Collective tests passed\n"); fflush(stdout); } if (me == 0) { printf("Testcase runtime\n"); printf("Name,Time(seconds)\n"); for (i = 0; i < 2; i++) if (test_flags[i] == 1) { double time_spent = (stop_time[i].tv_sec - start_time[i].tv_sec) + ((double) stop_time[i].tv_usec - start_time[i].tv_usec) / 1E6; printf("%s,%.6f\n", test_name[i], time_spent); } } ARMCI_Barrier(); ARMCI_Finalize(); armci_msg_finalize(); return(0); } ga-5.9.2/armci/testing/test_groups.c000066400000000000000000000157011500715745200174220ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: test_groups.c,v 1.3 2004-11-22 20:29:53 manoj Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif #include "armci.h" #include "message.h" /*#include "armcip.h"*/ #define MAXDIMS 7 #define MAXPROC 128 #define MINPROC 4 /***************************** macros ************************/ #define COPY(src, dst, bytes) memcpy((dst),(src),(bytes)) #define ABS(a) (((a) >= 0) ? (a) : (-(a))) #define MIN(a,b) (((a)<(b)) ? (a) : (b)) /***************************** global data *******************/ int me, nproc; void *work[MAXPROC]; /* work array for propagating addresses */ void create_array(void *a[], int elem_size, int ndim, int dims[]) { int bytes = elem_size, i, rc; assert(ndim <= MAXDIMS); for (i = 0; i < ndim; i++) { bytes *= dims[i]; } rc = ARMCI_Malloc(a, bytes); assert(rc == 0); assert(a[me]); } void destroy_array(void *ptr[]) { int check; ARMCI_Barrier(); check = !ARMCI_Free(ptr[me]); assert(check); } #define GNUM_A 3 #define GNUM_B 2 #define ELEMS 10 /* to check if a process belongs to this group */ int chk_grp_membership(int rank, ARMCI_Group *grp, int *memberlist) { int i, grp_size; ARMCI_Group_size(grp, &grp_size); for (i = 0; i < grp_size; i++) if (rank == memberlist[i]) { return 1; } return 0; } void test_one_group(ARMCI_Group *group, int *pid_list) { int grp_me, grp_size; int i, j, src_proc, dst_proc; double *ddst_put[MAXPROC]; double dsrc[ELEMS]; int bytes, world_me; world_me = armci_msg_me(); ARMCI_Group_rank(group, &grp_me); ARMCI_Group_size(group, &grp_size); if (grp_me == 0) { printf("GROUP SIZE = %d\n", grp_size); } printf("%d:group rank = %d\n", me, grp_me); src_proc = 0; dst_proc = grp_size - 1; bytes = ELEMS * sizeof(double); ARMCI_Malloc_group((void **)ddst_put, bytes, group); for (i = 0; i < ELEMS; i++) { dsrc[i] = i * 1.001 * (grp_me + 1); } for (i = 0; i < ELEMS; i++) { ddst_put[grp_me][i] = -1.0; } armci_msg_group_barrier(group); if (grp_me == src_proc) { /* NOTE: make sure to specify absolute ids in ARMCI calls */ ARMCI_Put(dsrc, &ddst_put[dst_proc][0], bytes, ARMCI_Absolute_id(group, dst_proc)); } armci_msg_group_barrier(group); /* NOTE: make sure to specify absolute ids in ARMCI calls */ ARMCI_Fence(ARMCI_Absolute_id(group, dst_proc)); sleep(1); /* Verify*/ if (grp_me == dst_proc) { for (j = 0; j < ELEMS; j++) { if (ABS(ddst_put[grp_me][j] - j * 1.001 *(src_proc + 1)) > 0.1) { printf("\t%d: ddst_put[%d][%d] = %f and expected value is %f\n", me, grp_me, j, ddst_put[grp_me][j], j * 1.001 *(src_proc + 1)); ARMCI_Error("groups: armci put failed...1", 0); } } printf("\n%d(%d): Test O.K. Verified\n", dst_proc, world_me); } armci_msg_group_barrier(group); ARMCI_Free_group(ddst_put[grp_me], group); } void test_groups() { int pid_listA[MAXPROC] = {0, 1, 2}; int pid_listB[MAXPROC] = {1, 3}; ARMCI_Group groupA, groupB; ARMCI_Barrier(); ARMCI_Group_create(GNUM_A, pid_listA, &groupA); /* create group 1 */ ARMCI_Group_create(GNUM_B, pid_listB, &groupB); /* create group 2 */ /* ------------------------ GROUP A ------------------------- */ if (chk_grp_membership(me, &groupA, pid_listA)) { /* group A */ test_one_group(&groupA, pid_listA); } ARMCI_Barrier(); /* ------------------------ GROUP B ------------------------- */ if (chk_grp_membership(me, &groupB, pid_listB)) { /* group B */ test_one_group(&groupB, pid_listB); } ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("O.K.\n"); fflush(stdout); } } /** * Random permutation of 0..n-1 into an array. */ void random_permute(int *arr, int n) { int i, j; int *vtmp = (int *)malloc(n * sizeof(int)); assert(vtmp != NULL); for (i = 0; i < n; ++i) { vtmp[i] = -1; } for (i = 0; i < n; i++) { while (vtmp[j=(rand()%n)] != -1) /*no-op*/; assert(vtmp[j] == -1); vtmp[j] = 0; arr[i] = j; } free(vtmp); } int int_compare(const void *v1, const void *v2) { int i1 = *(int *)v1, i2 = *(int *)v2; if (i1 < i2) { return -1; } if (i1 > i2) { return +1; } return 0; } /** * Test routine for non-collective process group management. This test * should not be used with MPI process group implementation. */ #define GROUP_SIZE 2 #define MAX_GROUPS (MAXPROC/GROUP_SIZE) void test_groups_noncollective() { int *pid_lists[MAX_GROUPS]; int pids[MAXPROC]; int i, nprocs, world_me; ARMCI_Group group; int *my_pid_list = NULL, my_grp_size = 0; int ngrps; ARMCI_Barrier(); nprocs = armci_msg_nproc(); world_me = armci_msg_me(); random_permute(pids, nproc); ngrps = nprocs / GROUP_SIZE; for (i = 0; i < nprocs / GROUP_SIZE; i++) { pid_lists[i] = pids + (i * GROUP_SIZE); } for (i = 0; i < nprocs; i++) { if (pids[i] == world_me) { int grp_id = MIN(i / GROUP_SIZE, ngrps - 1); my_pid_list = pid_lists[grp_id]; if (grp_id == ngrps - 1) { my_grp_size = GROUP_SIZE + (nprocs % GROUP_SIZE); } else { my_grp_size = GROUP_SIZE; } } } qsort(my_pid_list, my_grp_size, sizeof(int), int_compare); ARMCI_Barrier(); /*now create all these disjoint groups and test them in parallel*/ ARMCI_Group_create(my_grp_size, my_pid_list, &group); test_one_group(&group, my_pid_list); ARMCI_Group_free(&group); ARMCI_AllFence(); ARMCI_Barrier(); if (world_me == 0) { printf("O.K.\n"); fflush(stdout); } } int main(int argc, char *argv[]) { armci_msg_init(&argc, &argv); ARMCI_Init_args(&argc, &argv); nproc = armci_msg_nproc(); me = armci_msg_me(); /* printf("nproc = %d, me = %d\n", nproc, me);*/ if (nproc < MINPROC) { if (0 == me) { printf("Test needs at least %d processors (%d used)\n", MINPROC, nproc); } ARMCI_Barrier(); armci_msg_finalize(); exit(0); } if (nproc > MAXPROC) { if (0 == me) { printf("Test works for up to %d processors (%d used)\n", MAXPROC, nproc); } ARMCI_Barrier(); armci_msg_finalize(); exit(0); } if (me == 0) { printf("ARMCI test program (%d processes)\n", nproc); fflush(stdout); sleep(1); } if (me == 0) { printf("\n Testing ARMCI Groups!\n\n"); fflush(stdout); } test_groups(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\n Collective groups: Success!!\n"); fflush(stdout); } sleep(2); #ifdef ARMCI_GROUP test_groups_noncollective(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\n Non-collective groups: Success!!\n"); fflush(stdout); } sleep(2); #endif ARMCI_Barrier(); ARMCI_Finalize(); armci_msg_finalize(); return(0); } ga-5.9.2/armci/testing/test_mt.c000066400000000000000000000401111500715745200165140ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: test_mt.c,v 1.1.2.1 2007-07-02 05:34:13 d3p687 Exp $ * test_mt.c * * Developed by Andriy Kot * Copyright (c) 2007 Pacific Northwest National Laboratory * * Changelog: * 2007-02-17 - created * */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STDARG_H # include #endif #if HAVE_ASSERT_H # include #endif #include "armci.h" #include "message.h" #include "utils.h" #define DEBUG /* note: a TBUFSIZE (per thread) buffer is used to print arrays */ #define TBUFSIZE 65535 /* prints debug information to files named test_mt.th<#th_rank>*/ /*#define LOG2FILE*/ #define NOTHREADS_ /* debug: does not spawn threads if set */ typedef double atype_t; /* type of the element of the array */ #define MAX_TPP 8 /* max treads per processor */ #define TPP 1//2 /* threads per processor */ #define ASIZE 4//5000 /* size of array */ #define ITERS 1//10 /* iterations per test */ enum {PUT = 101, GET, ACC}; int tpp = TPP; int asize = ASIZE; int iters = ITERS; int delay = 0; double scale = 2.0; #define THREAD_OFF 1000.0 #define ITER_OFF 10.0 #define ELEM_INC 0.0003 /* each ARMCI mem block that allocated consists of * th_size blocks, one per each thread (systemwide), which consist of * iters blocks, one per each iteration, which consist of * asize doubles (or atype_t) */ #define ASIZE_BYTES (asize*sizeof(atype_t)) #define ASIZExITERS (asize*iters) #define ASIZExITERS_BYTES (ASIZE_BYTES*iters) #define ASIZExITERSxTH (asize*iters*th_size) #define ASIZExITERSxTH_BYTES (ASIZE_BYTES*iters*th_size) /* p_ - pointer (points to area owned by particula thread) * th_ - thread section * it_ - iteration * i_ - element offset (0 for beginning of array) */ #define AELEM(p_,th_,it_,i_) ((atype_t *)p_)[th_*ASIZExITERS+it_*asize+i_] #define AELEM_VAL(th_,it_,i_) (THREAD_OFF*th_+ITER_OFF*it_+ELEM_INC*(i_+1)) int rank, size, th_size; int th_rank[MAX_TPP]; unsigned time_seed; int *pairs, *rnd_tgts, rnd_one; void **ptrs1, **ptrs2; FILE *dbg[MAX_TPP]; char fname[] = "test_mt.th000"; char cbuf[2048]; int cbufl; #define PRINTF0 if(!rank)printf #define PRINTF0T if(!TH_ME)printf #define RND(l_,h_) (l_+(int)(((double)h_)*rand()/(RAND_MAX+((double)l_)))) #define TH_ME (th_rank[th_idx]) void prndbg(int th_idx, char *fmt, ...) { #ifdef DEBUG va_list ap; va_start(ap, fmt); #ifdef LOG2FILE # define DSCR dbg[th_idx] #else # define DSCR stdout printf("%3d: ", TH_ME); #endif vfprintf(DSCR, fmt, ap); fflush(DSCR); va_end(ap); #endif } void usage() { if (!rank) { printf("Usage: test_mt, or \n"); printf(" test_mt -tTHREADS_PER_PROC -sARRAY_SIZE -iITERATIONS_COUNT\n"); } ARMCI_Barrier(); armci_msg_finalize(); exit(0); } void *thread_main(void *arg); void zero_array(int th_idx, void *ptr); void init_array(int th_idx, void *ptr); void print_array(int th_idx, char *msg, atype_t *array); void test_pairs(int th_idx); // deprecated? void test_PutGetAcc(int th_idx, int tgt, int *rmt, int rmt_cnt); void check_PutGetAcc(int th_idx, int rmt, int op, atype_t *array); int main(int argc, char *argv[]) { int ch; extern char *optarg; int i, j, r; thread_t threads[MAX_TPP]; /* init ARMCI */ armci_msg_init(&argc, &argv); ARMCI_Init_args(&argc, &argv); size = armci_msg_nproc(); rank = armci_msg_me(); while ((ch = getopt(argc, argv, "t:s:i:d:h")) != -1) { switch (ch) { case 't': /* # of threads */ tpp = atoi(optarg); if (tpp < 1 || tpp > MAX_TPP) { PRINTF0("\"%s\" is improper value for -t, should be a " "number between 1 and %d(MAX_TPP)\n", optarg, MAX_TPP); usage(); } break; case 'i': /* # of iterations */ iters = atoi(optarg); if (iters < 1) { PRINTF0("\"%s\" is improper value for -t, should be a " "number equal or larger than 1\n", optarg); usage(); } break; case 's': /* # of elements in the array */ asize = atoi(optarg); if (iters < 1) { PRINTF0("\"%s\" is improper value for -s, should be a " "number equal or larger than 1\n", optarg); usage(); } break; case 'd': delay = atoi(optarg); break; /* delay before start */ case 'h': usage(); break; /* print usage info */ } } #ifdef NOTHREADS tpp = 1; PRINTF0("Warning: NOTHREADS debug symbol is set -- running w/o threads\n"); #endif th_size = size * tpp; PRINTF0("\nTest of multi-threaded capabilities:\n" "%d threads per process (%d threads total),\n" "%d array elements of size %d,\n" "%d iteration(s)\n\n", tpp, th_size, asize, sizeof(atype_t), iters); if (delay) { printf("%d: %d\n", rank, getpid()); fflush(stdout); sleep(delay); ARMCI_Barrier(); } TH_INIT(size, tpp); for (i = 0; i < tpp; i++) { th_rank[i] = rank * tpp + i; } #if defined(DEBUG) && defined(LOG2FILE) for (i = 0; i < tpp; i++) { fname[10] = '0' + th_rank[i] / 100; fname[11] = '0' + th_rank[i] % 100 / 10; fname[12] = '0' + th_rank[i] % 10; dbg[i] = fopen(fname, "w"); } #endif for (i = 0; i < tpp; i++) { prndbg(i, "proc %d, thread %d(%d):\n", rank, i, th_rank[i]); } /* set global seed (to ensure same random sequence across procs) */ time_seed = (unsigned)time(NULL); armci_msg_brdcst(&time_seed, sizeof(time_seed), 0); srand(time_seed); rand(); prndbg(0, "seed = %u\n", time_seed); /* random pairs */ pairs = calloc(th_size, sizeof(int)); for (i = 0; i < th_size; i++) { pairs[i] = -1; } for (i = 0; i < th_size; i++) { if (pairs[i] != -1) { continue; } r = RND(0, th_size); while (i == r || pairs[r] != -1) { r = RND(0, th_size); } pairs[i] = r; pairs[r] = i; } for (i = 0, cbufl = 0; i < th_size; i++) cbufl += sprintf(cbuf + cbufl, " %d->%d|%d->%d", i, pairs[i], pairs[i], pairs[pairs[i]]); prndbg(0, "random pairs:%s\n", cbuf); /* random targets */ rnd_tgts = calloc(th_size, sizeof(int)); for (i = 0, cbufl = 0; i < th_size; i++) { rnd_tgts[i] = RND(0, th_size); if (rnd_tgts[i] == i) { i--; continue; } cbufl += sprintf(cbuf + cbufl, " %d", rnd_tgts[i]); } prndbg(0, "random targets:%s\n", cbuf); /* random one */ rnd_one = RND(0, th_size); prndbg(0, "random one = %d\n", rnd_one); ptrs1 = calloc(th_size, sizeof(void *)); assert(ptrs1); ptrs2 = calloc(th_size, sizeof(void *)); assert(ptrs2); #ifdef NOTHREADS thread_main((void *)(long)0); #else for (i = 0; i < tpp; i++) { THREAD_CREATE(threads + i, thread_main, (void *)(long)i); } for (i = 0; i < tpp; i++) { THREAD_JOIN(threads[i], NULL); } #endif ARMCI_Barrier(); PRINTF0("Tests Completed\n"); /* clean up */ #if defined(DEBUG) && defined(LOG2FILE) for (i = 0; i < tpp; i++) { fclose(dbg[i]); } #endif ARMCI_Finalize(); TH_FINALIZE(); armci_msg_finalize(); return 0; } void *thread_main(void *arg) { int th_idx, i; int tgt, *rmt, rmt_cnt; th_idx = (int)(long)arg; prndbg(th_idx, "thread %d(%d|%d) STARTED\n", TH_ME, rank, th_idx); int check = !ARMCI_MALLOC_MT(ptrs1, ASIZExITERSxTH_BYTES); assert(check); check = !ARMCI_MALLOC_MT(ptrs2, ASIZExITERSxTH_BYTES); assert(check); #if 0 for (i = 0, cbufl = 0; i < th_size; i++) { cbufl += sprintf(cbuf + cbufl, " %p", ptrs1[i]); } prndbg(th_idx, "ptrs1: %s\n", cbuf); for (i = 0, cbufl = 0; i < th_size; i++) { cbufl += sprintf(cbuf + cbufl, " %p", ptrs2[i]); } prndbg(th_idx, "ptrs2: %s\n", cbuf); #endif #if 0 init_array(th_idx, ptrs1[TH_ME]); init_array(th_idx, ptrs2[TH_ME]); #endif rmt = calloc(th_size, sizeof(int)); assert(rmt); PRINTF0T(" TESTING GET/PUT/ACC\n\n"); /* test pairs */ PRINTF0T("> Testing pair-wise communication pattern ...\n"); tgt = rmt[0] = pairs[TH_ME]; rmt_cnt = 1; test_PutGetAcc(th_idx, tgt, rmt, rmt_cnt); PRINTF0T(" pair-wise is OK\n\n"); //return NULL; // REMOVE WHEN DONE /* test random target */ PRINTF0T("> Testing random target communication pattern ...\n"); tgt = rnd_tgts[TH_ME]; for (i = 0, rmt_cnt = 0; i < th_size; i++) if (rnd_tgts[i] == TH_ME) { rmt[rmt_cnt++] = i; } test_PutGetAcc(th_idx, tgt, rmt, rmt_cnt); PRINTF0T(" random target is OK\n\n"); /* test all to one */ PRINTF0T("> Testing hotspot(all to one) communication pattern ...\n"); if (TH_ME == rnd_one) { tgt = -1; for (i = 0, rmt_cnt = 0; i < th_size; i++) if (i != TH_ME) { rmt[rmt_cnt++] = i; } } else { tgt = rnd_one; rmt_cnt = 0; } test_PutGetAcc(th_idx, tgt, rmt, rmt_cnt); PRINTF0T(" hotspot is OK\n\n"); free(rmt); } void zero_array(int th_idx, void *ptr) { int i, j, k; for (i = 0; i < th_size; i++)for (j = 0; j < iters; j++)for (k = 0; k < asize; k++) { AELEM(ptr, i, j, k) = 0.0; } } void init_array(int th_idx, void *ptr) { int i, j, k; for (i = 0; i < th_size; i++)for (j = 0; j < iters; j++)for (k = 0; k < asize; k++) { AELEM(ptr, i, j, k) = AELEM_VAL(TH_ME, j, k); } /*AELEM(ptr, i, j) = THREAD_OFF*TH_ME + ITER_OFF*i + ELEM_INC*(j+1);*/ print_array(th_idx, "initialized", ptr); #if 0 # if 1 for (i = 0, cbufl = 0; i < th_size; i++) { for (j = 0; j < iters; j++) { cbufl += sprintf(cbuf + cbufl, "(%d,%d)%p:", i, j, &(((atype_t *)ptr)[i*ASIZExITERS+j*asize])); for (k = 0; k < asize; k++) { cbufl += sprintf(cbuf + cbufl, " %.4f", ((atype_t *)ptr)[i*ASIZExITERS+j*asize+k]); } cbufl += sprintf(cbuf + cbufl, "\n"); } cbufl += sprintf(cbuf + cbufl, "\n"); } # else for (i = 0, cbufl = 0; i < (th_size * iters * asize); i++) { cbufl += sprintf(cbuf + cbufl, " %.4f", ((atype_t *)ptr)[i]); } # endif prndbg(th_idx, "initialized:\n%s\n", cbuf); #endif } void print_array(int th_idx, char *msg, atype_t *array) { #ifdef DEBUG int i, j, k, tbufl; char tbuf[TBUFSIZE]; if (ASIZExITERSxTH_BYTES > TBUFSIZE / 2) { prndbg(th_idx, "%s:\n%s\n", msg, "array is too big to print"); } for (i = 0, tbufl = 0; i < th_size; i++) { for (j = 0; j < iters; j++) { tbufl += sprintf(tbuf + tbufl, "(%d,%d)%p:", i, j, &AELEM(array, i, j, 0)); for (k = 0; k < asize; k++) { tbufl += sprintf(tbuf + tbufl, " %.4f", AELEM(array, i, j, k)); } tbufl += sprintf(tbuf + tbufl, "\n"); } tbufl += sprintf(tbuf + tbufl, "\n"); } prndbg(th_idx, "%s:\n%s\n", msg, tbuf); #endif } /* void print_array(int th_idx, char *msg, atype_t *array, int count) { #ifdef DEBUG int i; for (i = 0, cbufl = 0; i < count; i++) cbufl+=sprintf(cbuf+cbufl, " %.4f", array[i]); prndbg(th_idx, "%s:%s\n", msg, cbuf); #endif } */ int check_result(atype_t *array, int th) { int i, j, k, mismatch; for (i = 0, k = 0, mismatch; i < iters; i++) for (j = 0; j < asize; j++, k++) { if (array[k] != AELEM_VAL(th, i, j)) { printf("mismatch detected: th=%d, i=%d, j=%d, k=%d, elem=%d, array=%d\n", th, i, j, k, AELEM_VAL(th, i, j), array[k]); fflush(stdout); abort(); } } } void test_pairs(int th_idx) { int rem_th, rem_proc; int i, j; void *src, *dst; rem_th = pairs[TH_ME]; rem_proc = TH2PROC(rem_th); prndbg(th_idx, "test_pair: %d<->%d(%d)\n", TH_ME, rem_th, rem_proc); MT_BARRIER(); #if 0 print_array(th_idx, "before", &AELEM(ptrs2[TH_ME], rem_th, 0, 0), ASIZExITERS); #endif for (i = 0; i < iters; i++) { /* src - addr of my thread block on remote proc/thread */ src = &AELEM(ptrs1[rem_th], TH_ME, i, 0); /* src - addr of remote thread block on my proc/thread */ dst = &AELEM(ptrs2[TH_ME], rem_th, i, 0); /* get from my pair */ int check = !ARMCI_Get(src, dst, ASIZE_BYTES, rem_proc); assert(check); } MT_BARRIER(); #if 0 print_array(th_idx, "rcvd", &AELEM(ptrs2[TH_ME], rem_th, 0, 0), ASIZExITERS); #endif /* check results */ check_result(&AELEM(ptrs2[TH_ME], rem_th, 0, 0), rem_th); } /* test Put/Get/Acc sequence regardless of communication pattern * tgt -- remote target for put/get/acc (none if -1) * rmt -- list of remote thread that put/acc to here (correctness is cheked here) * rmt_cnt -- # of threads in rmt */ void test_PutGetAcc(int th_idx, int tgt, int *rmt, int rmt_cnt) { /* a - local thread, b - remote thread */ int a, b, b_proc, stride[2], count[2]; int i, j; void *src, *dst; #ifdef DEBUG for (i = 0, cbufl = 0; i < rmt_cnt; i++) { cbufl += sprintf(cbuf + cbufl, " %d", rmt[i]); } prndbg(th_idx, "test_PutGetAcc: put/acc to %d, get from %d, check put/acc from %s\n", tgt, tgt, rmt_cnt ? cbuf : "none"); #endif a = TH_ME; stride[0] = ASIZE_BYTES; count[0] = ASIZE_BYTES; count[1] = 1; /* init arrays */ init_array(th_idx, ptrs1[TH_ME]); init_array(th_idx, ptrs2[TH_ME]); MT_BARRIER(); /* put - put a.ptrs1[b] into b.ptrs2[a] */ if (tgt != -1) { b = tgt; b_proc = TH2PROC(b); for (i = 0; i < iters; i++) { src = &AELEM(ptrs1[a], b, i, 0); /* a.ptrs1[b] */ dst = &AELEM(ptrs2[b], a, i, 0); /* b.ptrs2[a] */ int check; // check = !ARMCI_Put(src, dst, ASIZE_BYTES, b_proc); // assert(check); check = !ARMCI_PutS(src, stride, dst, stride, count, 1, b_proc); assert(check); } ARMCI_Fence(b_proc); } MT_BARRIER(); print_array(th_idx, "PUT:ptrs1[TH_ME]", ptrs1[TH_ME]); print_array(th_idx, "PUT:ptrs2[TH_ME]", ptrs2[TH_ME]); MT_BARRIER(); /* chk put(s) from b(s): a.ptrs2[b] */ for (j = 0; j < rmt_cnt; j++) { b = rmt[j]; b_proc = TH2PROC(b); check_PutGetAcc(th_idx, b, PUT, &AELEM(ptrs2[a], b, 0, 0)); } //return; // REMOVE WHEN DONE /* init arrays */ init_array(th_idx, ptrs1[TH_ME]); init_array(th_idx, ptrs2[TH_ME]); MT_BARRIER(); /* get - get b.ptrs1[a] into a.ptrs2[b] */ if (tgt != -1) { b = tgt; b_proc = TH2PROC(b); for (i = 0; i < iters; i++) { src = &AELEM(ptrs1[b], a, i, 0); /* b.ptrs1[a] */ dst = &AELEM(ptrs2[a], b, i, 0); /* a.ptrs2[b] */ check = !ARMCI_GetS(src, stride, dst, stride, count, 1, b_proc); assert(check); } } print_array(th_idx, "GET:ptrs1[TH_ME]", ptrs1[TH_ME]); print_array(th_idx, "GET:ptrs2[TH_ME]", ptrs2[TH_ME]); MT_BARRIER(); /* chk get from b: a.ptrs2[b] */ if (tgt != -1) { check_PutGetAcc(th_idx, b, GET, &AELEM(ptrs2[a], b, 0, 0)); } #if 1 /* init arrays */ init_array(th_idx, ptrs1[TH_ME]); init_array(th_idx, ptrs2[TH_ME]); MT_BARRIER(); /* acc - acc a.ptrs1[b] * scale + b.ptrs2[a] into b.ptrs2[a] */ if (tgt != -1) { b = tgt; b_proc = TH2PROC(b); for (i = 0; i < iters; i++) { src = &AELEM(ptrs1[a], b, i, 0); /* a.ptrs1[b] */ dst = &AELEM(ptrs2[b], a, i, 0); /* b.ptrs2[a] */ check = !ARMCI_AccS(ARMCI_ACC_DBL, &scale, src, stride, dst, stride, count, 1, b_proc); assert(check); } ARMCI_Fence(b_proc); } MT_BARRIER(); print_array(th_idx, "ACC:ptrs1[TH_ME]", ptrs1[TH_ME]); print_array(th_idx, "ACC:ptrs2[TH_ME]", ptrs2[TH_ME]); MT_BARRIER(); /* chk acc(s) from b(s): a.ptrs2[b] */ for (j = 0; j < rmt_cnt; j++) { b = rmt[j]; b_proc = TH2PROC(b); check_PutGetAcc(th_idx, b, ACC, &AELEM(ptrs2[a], b, 0, 0)); } #endif MT_BARRIER(); } void check_PutGetAcc(int th_idx, int rmt, int op, atype_t *array) { int i, j, k; double expected; for (i = 0, k = 0; i < iters; i++) for (j = 0; j < asize; j++, k++) { expected = op == ACC ? AELEM_VAL(TH_ME, i, j) + scale * AELEM_VAL(rmt, i, j) : AELEM_VAL(rmt, i, j); if (array[k] != expected) { printf("mismatch detected: TM_ME=%d, rmt=%d, op=%d, i=%d, j=%d, " "k=%d, expected=%f, array=%f\n", TH_ME, rmt, op, i, j, k, expected, array[k]); fflush(stdout); sleep(5); abort(); } #if 0 if (array[k] != AELEM_VAL(th, i, j)) { printf("mismatch detected: th=%d, i=%d, j=%d, k=%d, elem=%d, array=%d\n", th, i, j, k, AELEM_VAL(th, i, j), array[k]); fflush(stdout); sleep(10); abort(); } #endif } } ga-5.9.2/armci/testing/testitr.c000066400000000000000000000037371500715745200165500ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_ASSERT_H # include #endif #include "iterator.h" int main(int argc, char **argv) { stride_info_t sinfo, dinfo; int a[10][10], b[11][11]; int asr[1] = {10 * sizeof(int)}; int bsr[1] = {11 * sizeof(int)}; int count[2] = {5 * sizeof(int), 5}; int i, j; for (i = 0; i < 10; i++) { for (j = 0; j < 10; j++) { a[i][j] = i * 10 + j; } } for (i = 0; i < 11; i++) { for (j = 0; j < 11; j++) { b[i][j] = 0; } } armci_stride_info_init(&sinfo, &a[2][3], 1, asr, count); armci_stride_info_init(&dinfo, &b[3][4], 1, bsr, count); assert(armci_stride_info_size(&sinfo) == 5); assert(armci_stride_info_size(&dinfo) == 5); assert(armci_stride_info_pos(&sinfo) == 0); assert(armci_stride_info_pos(&dinfo) == 0); while (armci_stride_info_has_more(&sinfo)) { int bytes; char *ap, *bp; assert(armci_stride_info_has_more(&dinfo)); bytes = armci_stride_info_seg_size(&sinfo); assert(bytes == armci_stride_info_seg_size(&dinfo)); ap = armci_stride_info_seg_ptr(&sinfo); bp = armci_stride_info_seg_ptr(&dinfo); memcpy(bp, ap, bytes); armci_stride_info_next(&sinfo); armci_stride_info_next(&dinfo); } armci_stride_info_destroy(&sinfo); armci_stride_info_destroy(&dinfo); #if 0 for (i = 0; i < 10; i++) { for (j = 0; j < 10; j++) { printf("%3d ", a[i][j]); } printf("\n"); } for (i = 0; i < 11; i++) { for (j = 0; j < 11; j++) { printf("%3d ", b[i][j]); } printf("\n"); } #endif for (i = 2; i < 2 + 5; i++) { for (j = 3; j < 3 + 5; j++) { if (a[i][j] != b[i+1][j+1]) { printf("a[%d][%d]=%d b[%d][%d]=%d\n", i, j, a[i][j], i, j, b[i][j]); printf("Test Failed\n"); return 0; } } } printf("Success\n"); return 0; } ga-5.9.2/armci/testing/testnotify.c000066400000000000000000000246261500715745200172620ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: testnotify.c,v 1.4 2003-08-21 22:51:12 d3h325 Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif #define DEBUG__ #include "armci.h" #include "message.h" #define DIM1 5 #define DIM2 3 #ifdef __sun /* Solaris has shared memory shortages in the default system configuration */ # define DIM3 6 # define DIM4 5 # define DIM5 4 #elif defined(__alpha__) # define DIM3 8 # define DIM4 5 # define DIM5 6 #else # define DIM3 8 # define DIM4 9 # define DIM5 7 #endif #define DIM6 3 #define DIM7 2 #define OFF 1 #define EDIM1 (DIM1+OFF) #define EDIM2 (DIM2+OFF) #define EDIM3 (DIM3+OFF) #define EDIM4 (DIM4+OFF) #define EDIM5 (DIM5+OFF) #define EDIM6 (DIM6+OFF) #define EDIM7 (DIM7+OFF) #define DIMS 4 #define MAXDIMS 7 #define MAX_DIM_VAL 50 #define LOOP 200 #define BASE 100. #define MAXPROC 128 #define TIMES 100 # define ELEMS 200 /***************************** macros ************************/ #define COPY(src, dst, bytes) memcpy((dst),(src),(bytes)) #define ARMCI_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define ARMCI_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define ARMCI_ABS(a) (((a) <0) ? -(a) : (a)) /***************************** global data *******************/ int me, nproc; void *work[MAXPROC]; /* work array for propagating addresses */ /*\ generate random range for a section of multidimensional array \*/ void get_range(int ndim, int dims[], int lo[], int hi[]) { int dim; for (dim = 0; dim < ndim; dim++) { int toss1, toss2; toss1 = rand() % dims[dim]; toss2 = rand() % dims[dim]; if (toss1 < toss2) { lo[dim] = toss1; hi[dim] = toss2; } else { hi[dim] = toss1; lo[dim] = toss2; } } } /*\ generates a new random range similar to the input range for an array with specified dimensions \*/ void new_range(int ndim, int dims[], int lo[], int hi[], int new_lo[], int new_hi[]) { int dim; for (dim = 0; dim < ndim; dim++) { int toss, range; int diff = hi[dim] - lo[dim] + 1; assert(diff <= dims[dim]); range = dims[dim] - diff; toss = (range > 0) ? rand() % range : lo[dim]; new_lo[dim] = toss; new_hi[dim] = toss + diff - 1; assert(new_hi[dim] < dims[dim]); assert(diff == (new_hi[dim] - new_lo[dim] + 1)); } } /*\ print range of ndim dimensional array with two strings before and after \*/ void print_range(char *pre, int ndim, int lo[], int hi[], char *post) { int i; printf("%s[", pre); for (i = 0; i < ndim; i++) { printf("%d:%d", lo[i], hi[i]); if (i == ndim - 1) { printf("] %s", post); } else { printf(","); } } } /*\ print subscript of ndim dimensional array with two strings before and after \*/ void print_subscript(char *pre, int ndim, int subscript[], char *post) { int i; printf("%s [", pre); for (i = 0; i < ndim; i++) { printf("%d", subscript[i]); if (i == ndim - 1) { printf("] %s", post); } else { printf(","); } } } /*\ print a section of a 2-D array of doubles \*/ void print_2D_double(double *a, int ld, int *lo, int *hi) { int i, j; for (i = lo[0]; i <= hi[0]; i++) { for (j = lo[1]; j <= hi[1]; j++) { printf("%13f ", a[ld*j+i]); } printf("\n"); } } /*\ initialize array: a[i,j,k,..]=i+100*j+10000*k+ ... \*/ void init(double *a, int ndim, int elems, int dims[]) { int idx[MAXDIMS]; int i, dim; for (i = 0; i < elems; i++) { int Index = i; double field, val; for (dim = 0; dim < ndim; dim++) { idx[dim] = Index % dims[dim]; Index /= dims[dim]; } field = 1.; val = 0.; for (dim = 0; dim < ndim; dim++) { val += field * idx[dim]; field *= BASE; } a[i] = val; /* printf("(%d,%d,%d)=%6.0f",idx[0],idx[1],idx[2],val); */ } } /*\ compute Index from subscript * assume that first subscript component changes first \*/ int Index(int ndim, int subscript[], int dims[]) { int idx = 0, i, factor = 1; for (i = 0; i < ndim; i++) { idx += subscript[i] * factor; factor *= dims[i]; } return idx; } void update_subscript(int ndim, int subscript[], int lo[], int hi[], int dims[]) { int i; for (i = 0; i < ndim; i++) { if (subscript[i] < hi[i]) { subscript[i]++; return; } subscript[i] = lo[i]; } } void compare_patches(double eps, int ndim, double *patch1, int lo1[], int hi1[], int dims1[], double *patch2, int lo2[], int hi2[], int dims2[]) { int i, j, elems = 1; int subscr1[MAXDIMS], subscr2[MAXDIMS]; double diff, max; int idx1, idx2, offset1, offset2; for (i = 0; i < ndim; i++) { /* count # of elements & verify consistency of both patches */ int diff = hi1[i] - lo1[i]; assert(diff == (hi2[i] - lo2[i])); assert(diff < dims1[i]); assert(diff < dims2[i]); elems *= diff + 1; subscr1[i] = lo1[i]; subscr2[i] = lo2[i]; } /* compare element values in both patches */ for (j = 0; j < elems; j++) { idx1 = Index(ndim, subscr1, dims1); /* calculate element Index from a subscript */ idx2 = Index(ndim, subscr2, dims2); if (j == 0) { offset1 = idx1; offset2 = idx2; } idx1 -= offset1; idx2 -= offset2; diff = patch1[idx1] - patch2[idx2]; max = ARMCI_MAX(ARMCI_ABS(patch1[idx1]), ARMCI_ABS(patch2[idx2])); if (max == 0. || max < eps) { max = 1.; } if (eps < ARMCI_ABS(diff) / max) { char msg[48]; sprintf(msg, "(proc=%d):%f", me, patch1[idx1]); print_subscript("ERROR: a", ndim, subscr1, msg); sprintf(msg, "%f\n", patch2[idx2]); print_subscript(" b", ndim, subscr2, msg); fflush(stdout); sleep(1); ARMCI_Error("Bailing out", 0); } { /* update subscript for the patches */ update_subscript(ndim, subscr1, lo1, hi1, dims1); update_subscript(ndim, subscr2, lo2, hi2, dims2); } } /* make sure we reached upper limit */ /*for(i=0;i1 takes a partial plane */ /* create shared and local arrays */ create_array(b, sizeof(double), ndim, dimsB); create_array(a, sizeof(double), ndim, dimsB); elems = get_elems(ndim, stride, dimsB, sizeof(double)); init((double *)a[me], ndim, elems, dimsB); for (i = 0; i < ndim; i++) { lo[i] = 0; hi[i] = (less > dimsB[i]) ? dimsB[i] - 1 : dimsB[i] - less; count[i] = hi[i] - lo[i] + 1; } count[0] *= sizeof(double); for (i = 0; i < ndim - 1; i++) { Idx *= dimsB[i]; } ARMCI_Barrier(); if (me == 0) { printf("--------array[%d", dimsB[0]); for (dim = 1; dim < ndim; dim++) { printf(",%d", dimsB[dim]); } printf("]--------\n"); fflush(stdout); } ARMCI_Barrier(); loopcnt = (ndim > 1) ? dimsB[ndim-1] : 1; strl = (ndim > 1) ? ndim - 2 : 0; /* strides of the subpatch to transfer */ for (i = 0; i < loopcnt; i++) { int wc; if (me == 0) { ARMCI_PutS((double *)a[me] + idx, stride, (double *)b[left] + idx, stride, count, strl, left); #if DEBUG_ printf("%d-%d: ps=%p pd=%p i=%d idx=%d count=%d\n", me, left, (double *) a[me] + idx, (double *)b[left] + idx, i, idx, count[0]); fflush(stdout); #endif (void)armci_notify(left); (void)armci_notify_wait(right, &wc); } else { (void)armci_notify_wait(right, &wc); ARMCI_PutS((double *)b[me] + idx, stride, (double *)b[left] + idx, stride, count, strl, left); #if DEBUG_ printf("%d: ps=%p pd=%p i=%d idx=%d count=%d\n", me, (double *)b[me] + idx, (double *)b[left] + idx, i, idx, count[0]); fflush(stdout); #endif (void)armci_notify(left); } idx += Idx; /* advance to the next slab */ } ARMCI_Barrier(); if (me == 0) { compare_patches(0., ndim, (double *)a[0], lo, hi, dimsB, (double *)b[0], lo, hi, dimsB); printf("OK\n"); } ARMCI_Barrier(); destroy_array(b); destroy_array(a); } int main(int argc, char *argv[]) { int ndim; armci_msg_init(&argc, &argv); ARMCI_Init_args(&argc, &argv); nproc = armci_msg_nproc(); me = armci_msg_me(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting armci_notify\n"); fflush(stdout); sleep(1); } ARMCI_Barrier(); for (ndim = 1; ndim <= MAXDIMS; ndim++) { test_notify(ndim); } ARMCI_Barrier(); ARMCI_Finalize(); armci_msg_finalize(); return(0); } ga-5.9.2/armci/testing/timer.h000066400000000000000000000037721500715745200161760ustar00rootroot00000000000000#ifndef ARMCI_TESTING_TIMER_H_ #define ARMCI_TESTING_TIMER_H_ #if (defined(__i386__) || defined(__x86_64__) || defined(__powerpc__)) # define HAVE_RDTSC 1 # if defined(__i386__) static __inline__ unsigned long long rdtsc(void) { unsigned long long int x; __asm__ volatile(".byte 0x0f, 0x31" : "=A"(x)); return x; } # elif defined(__x86_64__) static __inline__ unsigned long long rdtsc(void) { unsigned hi, lo; __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi)); return ((unsigned long long)lo) | (((unsigned long long)hi) << 32); } # elif defined(__powerpc__) static __inline__ unsigned long long rdtsc(void) { unsigned long long int result = 0; unsigned long int upper, lower, tmp; __asm__ volatile( "0: \n" "\tmftbu %0 \n" "\tmftb %1 \n" "\tmftbu %2 \n" "\tcmpw %2,%0 \n" "\tbne 0b \n" : "=r"(upper), "=r"(lower), "=r"(tmp) ); result = upper; result = result << 32; result = result | lower; return(result); } # endif #elif HAVE_SYS_TIME_H # include #elif HAVE_WINDOWS_H # include static LARGE_INTEGER frequency; #endif static unsigned long long timer_start() { #if HAVE_RDTSC return rdtsc(); #elif HAVE_SYS_TIME_H struct timeval timer; (void)gettimeofday(&timer, NULL); return timer.tv_sec * 1000000 + timer.tv_usec; #elif HAVE_WINDOWS_H LARGE_INTEGER timer; QueryPerformanceCounter(&timer); return timer.QuadPart * 1000 / frequency.QuadPart; #else return 0; #endif } static unsigned long long timer_end(unsigned long long begin) { return timer_start() - begin; } static void timer_init() { #if HAVE_RDTSC #elif HAVE_SYS_TIME_H #elif HAVE_WINDOWS_H QueryPerformanceFrequency(&frequency); #else #endif } static const char *timer_name() { #if HAVE_RDTSC return "rdtsc"; #elif HAVE_SYS_TIME_H return "gettimeofday"; #elif HAVE_WINDOWS_H return "windows QueryPerformanceCounter"; #else return "no timers"; #endif } #endif /* ARMCI_TESTING_TIMER_H_ */ ga-5.9.2/armci/tools/000077500000000000000000000000001500715745200143575ustar00rootroot00000000000000ga-5.9.2/armci/tools/armci-config.in000066400000000000000000000231621500715745200172510ustar00rootroot00000000000000#! /bin/sh # Generated from armci-config.in.m4sh by GNU Autoconf 2.69. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested="" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes break 2 fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset ## -------------------- ## ## Main body of script. ## ## -------------------- ## prefix="@prefix@" exec_prefix="@exec_prefix@" cc="@CC@" cppflags="@GA_MP_CPPFLAGS@ @ARMCI_NETWORK_CPPFLAGS@ @BLAS_CPPFLAGS@ -I@includedir@" network_cppflags="@ARMCI_NETWORK_CPPFLAGS@" cflags="" ldflags="@GA_MP_LDFLAGS@ @ARMCI_NETWORK_LDFLAGS@ @BLAS_LDFLAGS@ -L@libdir@" network_ldflags="@ARMCI_NETWORK_LDFLAGS@" libs="-larmci @GA_MP_LIBS@ @ARMCI_NETWORK_LIBS@ @BLAS_LIBS@ @LIBS@" network_libs="@ARMCI_NETWORK_LIBS@" version="@PACKAGE_VERSION@" blas_int=4 # so that this interface mirrors the other config scripts use_blas=0 # legacy ARMCI does not use BLAS usage="Usage: armci-config [OPTIONS]... With the exception of --version and --help, all other options can be combined or run exclusively. Output is echoed to stdout. Options: --cc --cppflags --cflags --ldflags --libs --network_cppflags --network_ldflags --network_libs --version --help " result= while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\(^=*\)='` ac_optarg=`expr "X$1" : 'X^=*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\(^=*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -help | --help | --hel | --he | -h ) $as_echo "$usage"; exit ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$version"; exit ;; -cc | --cc ) result="$result $cc" ;; -cppflags | --cppflags ) result="$result $cppflags" ;; -network_cppflags | --network_cppflags ) result="$result $network_cppflags" ;; -cflags | --cflags ) result="$result $cflags" ;; -ldflags | --ldflags ) result="$result $ldflags" ;; -network_ldflags | --network_ldflags ) result="$result $network_ldflags" ;; -libs | --libs ) result="$result $libs" ;; -network_libs | --network_libs ) result="$result $network_libs" ;; -blas_size | --blas_size ) result="$result $blas_size" ;; -use_blas | --use_blas ) result="$result $use_blas" ;; # This is an error. *) $as_echo "unrecognized option: \`$1' Try \`$0 --help' for more information."; exit ;; esac shift done $as_echo "$result" | sed 's/ */ /g;s/" /"/g;s/ "/"/g;s/^ *//;s/ *$//' ga-5.9.2/armci/tools/armci-config.in.m4sh000066400000000000000000000044671500715745200201320ustar00rootroot00000000000000dnl run "autom4te -l m4sh armci-config.in.m4sh > armci-config.in" AS_INIT prefix="@prefix@" exec_prefix="@exec_prefix@" cc="@CC@" cppflags="@GA_MP_CPPFLAGS@ @ARMCI_NETWORK_CPPFLAGS@ @BLAS_CPPFLAGS@ -I@includedir@" network_cppflags="@ARMCI_NETWORK_CPPFLAGS@" cflags="" ldflags="@GA_MP_LDFLAGS@ @ARMCI_NETWORK_LDFLAGS@ @BLAS_LDFLAGS@ -L@libdir@" network_ldflags="@ARMCI_NETWORK_LDFLAGS@" libs="-larmci @GA_MP_LIBS@ @ARMCI_NETWORK_LIBS@ @BLAS_LIBS@ @LIBS@" network_libs="@ARMCI_NETWORK_LIBS@" version="@PACKAGE_VERSION@" blas_int=4 # so that this interface mirrors the other config scripts use_blas=0 # legacy ARMCI does not use BLAS [usage="Usage: armci-config [OPTIONS]... With the exception of --version and --help, all other options can be combined or run exclusively. Output is echoed to stdout. Options: --cc --cppflags --cflags --ldflags --libs --network_cppflags --network_ldflags --network_libs --version --help "] result= while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -help | --help | --hel | --he | -h ) AS_ECHO(["$usage"]); exit ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) AS_ECHO(["$version"]); exit ;; -cc | --cc ) result="$result $cc" ;; -cppflags | --cppflags ) result="$result $cppflags" ;; -network_cppflags | --network_cppflags ) result="$result $network_cppflags" ;; -cflags | --cflags ) result="$result $cflags" ;; -ldflags | --ldflags ) result="$result $ldflags" ;; -network_ldflags | --network_ldflags ) result="$result $network_ldflags" ;; -libs | --libs ) result="$result $libs" ;; -network_libs | --network_libs ) result="$result $network_libs" ;; -blas_size | --blas_size ) result="$result $blas_size" ;; -use_blas | --use_blas ) result="$result $use_blas" ;; # This is an error. *) AS_ECHO(["unrecognized option: \`$1' Try \`$0 --help' for more information."]); exit ;; esac shift done AS_ECHO(["$result"]) | sed 's/ [ ]*/ /g;s/" /"/g;s/ "/"/g;s/^ [ ]*//;s/ [ ]*$//' ga-5.9.2/armci/tools/armci_prof.c000066400000000000000000000263161500715745200166540ustar00rootroot00000000000000 #if HAVE_CONFIG_H # include "config.h" #endif #include #include "armci.h" #include "parmci.h" #include "armci_profile.h" #include "armci_profile.c" int ARMCI_Acc(int optype, void *scale, void *src, void *dst, int bytes, int proc) { int ret; armci_profile_start_strided(&bytes, 0, proc, ARMCI_PROF_ACC); ret = PARMCI_Acc(optype, scale, src, dst, bytes, proc); armci_profile_stop_strided(ARMCI_PROF_ACC); return ret; } int ARMCI_AccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { int ret; armci_profile_start_strided(count, stride_levels, proc, ARMCI_PROF_ACCS); ret = PARMCI_AccS(optype, scale, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); armci_profile_stop_strided(ARMCI_PROF_ACCS); return ret; } int ARMCI_AccV(int op, void *scale, armci_giov_t *darr, int len, int proc) { int ret; armci_profile_start_vector(darr, len, proc, ARMCI_PROF_ACCV); ret = PARMCI_AccV(op, scale, darr, len, proc); armci_profile_stop_vector(ARMCI_PROF_ACCV); return ret; } void ARMCI_AllFence() { armci_profile_start(ARMCI_PROF_ALLFENCE); PARMCI_AllFence(); armci_profile_stop(ARMCI_PROF_ALLFENCE); } void ARMCI_Barrier() { armci_profile_start(ARMCI_PROF_BARRIER); PARMCI_Barrier(); armci_profile_stop(ARMCI_PROF_BARRIER); } int ARMCI_Create_mutexes(int num) { int ret; ret = PARMCI_Create_mutexes(num); return ret; } int ARMCI_Destroy_mutexes() { int ret; ret = PARMCI_Destroy_mutexes(); return ret; } void ARMCI_Fence(int proc) { if (!SAMECLUSNODE(proc)) armci_profile_start(ARMCI_PROF_FENCE); PARMCI_Fence(proc); if (!SAMECLUSNODE(proc)) armci_profile_stop(ARMCI_PROF_FENCE); } void ARMCI_Finalize() { armci_profile_terminate(); PARMCI_Finalize(); } int ARMCI_Free(void *ptr) { int ret; ret = PARMCI_Free(ptr); return ret; } int ARMCI_Free_local(void *ptr) { int ret; ret = PARMCI_Free_local(ptr); return ret; } int ARMCI_Get(void *src, void *dst, int bytes, int proc) { int ret; armci_profile_start_strided(&bytes, 0, proc, ARMCI_PROF_GET); ret = PARMCI_Get(src, dst, bytes, proc); armci_profile_stop_strided(ARMCI_PROF_GET); return ret; } int ARMCI_GetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { int ret; armci_profile_start_strided(count, stride_levels, proc, ARMCI_PROF_GETS); ret = PARMCI_GetS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); armci_profile_stop_strided(ARMCI_PROF_GETS); return ret; } int ARMCI_GetV(armci_giov_t *darr, int len, int proc) { int ret; armci_profile_start_vector(darr, len, proc, ARMCI_PROF_GETV); ret = PARMCI_GetV(darr, len, proc); armci_profile_stop_vector(ARMCI_PROF_GETV); return ret; } double ARMCI_GetValueDouble(void *src, int proc) { double ret; ret = PARMCI_GetValueDouble(src, proc); return ret; } float ARMCI_GetValueFloat(void *src, int proc) { float ret; ret = PARMCI_GetValueFloat(src, proc); return ret; } int ARMCI_GetValueInt(void *src, int proc) { int ret; ret = PARMCI_GetValueInt(src, proc); return ret; } long ARMCI_GetValueLong(void *src, int proc) { long ret; ret = PARMCI_GetValueLong(src, proc); return ret; } int ARMCI_Init() { int ret; ret = PARMCI_Init(); armci_profile_init(); return ret; } int ARMCI_Init_args(int *argc, char ***argv) { int ret; ret = PARMCI_Init_args(argc, argv); armci_profile_init(); return ret; } void ARMCI_Lock(int mutex, int proc) { PARMCI_Lock(mutex, proc); } int ARMCI_Malloc(void **ptr_arr, armci_size_t bytes) { int ret; ret = PARMCI_Malloc(ptr_arr, bytes); return ret; } void* ARMCI_Malloc_local(armci_size_t bytes) { void* ret; ret = PARMCI_Malloc_local(bytes); return ret; } void* ARMCI_Memat(armci_meminfo_t *meminfo, long offset) { void* ret; ret = PARMCI_Memat(meminfo, offset); return ret; } void ARMCI_Memget(size_t bytes, armci_meminfo_t *meminfo, int memflg) { PARMCI_Memget(bytes, meminfo, memflg); } int ARMCI_NbAccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { int ret; armci_profile_start_strided(count, stride_levels, proc, ARMCI_PROF_NBACCS); ret = PARMCI_NbAccS(optype, scale, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); armci_profile_stop_strided(ARMCI_PROF_NBACCS); return ret; } int ARMCI_NbAccV(int op, void *scale, armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { int ret; armci_profile_start_vector(darr, len, proc, ARMCI_PROF_NBACCV); ret = PARMCI_NbAccV(op, scale, darr, len, proc, nb_handle); armci_profile_stop_vector(ARMCI_PROF_NBACCV); return ret; } int ARMCI_NbGet(void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { int ret; armci_profile_start_strided(&bytes, 0, proc, ARMCI_PROF_NBGET); ret = PARMCI_NbGet(src, dst, bytes, proc, nb_handle); armci_profile_stop_strided(ARMCI_PROF_NBGET); return ret; } int ARMCI_NbGetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { int ret; armci_profile_start_strided(count, stride_levels, proc, ARMCI_PROF_NBGETS); ret = PARMCI_NbGetS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); armci_profile_stop_strided(ARMCI_PROF_NBGETS); return ret; } int ARMCI_NbGetV(armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { int ret; armci_profile_start_vector(darr, len, proc, ARMCI_PROF_NBGETV); ret = PARMCI_NbGetV(darr, len, proc, nb_handle); armci_profile_stop_vector(ARMCI_PROF_NBGETV); return ret; } int ARMCI_NbPut(void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { int ret; armci_profile_start_strided(&bytes, 0, proc, ARMCI_PROF_NBPUT); ret = PARMCI_NbPut(src, dst, bytes, proc, nb_handle); armci_profile_stop_strided(ARMCI_PROF_NBPUT); return ret; } int ARMCI_NbPutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { int ret; armci_profile_start_strided(count, stride_levels, proc, ARMCI_PROF_NBPUTS); ret = PARMCI_NbPutS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); armci_profile_stop_strided(ARMCI_PROF_NBPUTS); return ret; } int ARMCI_NbPutV(armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { int ret; armci_profile_start_vector(darr, len, proc, ARMCI_PROF_NBPUTV); ret = PARMCI_NbPutV(darr, len, proc, nb_handle); armci_profile_stop_vector(ARMCI_PROF_NBPUTV); return ret; } int ARMCI_NbPutValueDouble(double src, void *dst, int proc, armci_hdl_t *nb_handle) { int ret; ret = PARMCI_NbPutValueDouble(src, dst, proc, nb_handle); return ret; } int ARMCI_NbPutValueFloat(float src, void *dst, int proc, armci_hdl_t *nb_handle) { int ret; ret = PARMCI_NbPutValueFloat(src, dst, proc, nb_handle); return ret; } int ARMCI_NbPutValueInt(int src, void *dst, int proc, armci_hdl_t *nb_handle) { int ret; ret = PARMCI_NbPutValueInt(src, dst, proc, nb_handle); return ret; } int ARMCI_NbPutValueLong(long src, void *dst, int proc, armci_hdl_t *nb_handle) { int ret; ret = PARMCI_NbPutValueLong(src, dst, proc, nb_handle); return ret; } int ARMCI_Put(void *src, void *dst, int bytes, int proc) { int ret; armci_profile_start_strided(&bytes, 0, proc, ARMCI_PROF_PUT); ret = PARMCI_Put(src, dst, bytes, proc); armci_profile_stop_strided(ARMCI_PROF_PUT); return ret; } int ARMCI_PutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { int ret; armci_profile_start_strided(count, stride_levels, proc, ARMCI_PROF_PUTS); ret = PARMCI_PutS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); armci_profile_stop_strided(ARMCI_PROF_PUTS); return ret; } int ARMCI_PutS_flag(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { int ret; ret = PARMCI_PutS_flag(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, flag, val, proc); return ret; } int ARMCI_PutS_flag_dir(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { int ret; ret = PARMCI_PutS_flag_dir(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, flag, val, proc); return ret; } int ARMCI_PutV(armci_giov_t *darr, int len, int proc) { int ret; armci_profile_start_vector(darr, len, proc, ARMCI_PROF_PUTV); ret = PARMCI_PutV(darr, len, proc); armci_profile_stop_vector(ARMCI_PROF_PUTV); return ret; } int ARMCI_PutValueDouble(double src, void *dst, int proc) { int ret; ret = PARMCI_PutValueDouble(src, dst, proc); return ret; } int ARMCI_PutValueFloat(float src, void *dst, int proc) { int ret; ret = PARMCI_PutValueFloat(src, dst, proc); return ret; } int ARMCI_PutValueInt(int src, void *dst, int proc) { int ret; ret = PARMCI_PutValueInt(src, dst, proc); return ret; } int ARMCI_PutValueLong(long src, void *dst, int proc) { int ret; ret = PARMCI_PutValueLong(src, dst, proc); return ret; } int ARMCI_Put_flag(void *src, void *dst, int bytes, int *f, int v, int proc) { int ret; ret = PARMCI_Put_flag(src, dst, bytes, f, v, proc); return ret; } int ARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc) { int ret; armci_profile_start(ARMCI_PROF_RMW); ret = PARMCI_Rmw(op, ploc, prem, extra, proc); armci_profile_stop(ARMCI_PROF_RMW); return ret; } int ARMCI_Test(armci_hdl_t *nb_handle) { int ret; ret = PARMCI_Test(nb_handle); return ret; } void ARMCI_Unlock(int mutex, int proc) { PARMCI_Unlock(mutex, proc); } int ARMCI_Wait(armci_hdl_t *nb_handle) { int ret; armci_profile_start(ARMCI_PROF_WAIT); ret = PARMCI_Wait(nb_handle); armci_profile_stop(ARMCI_PROF_WAIT); return ret; } int ARMCI_WaitAll() { int ret; ret = PARMCI_WaitAll(); return ret; } int ARMCI_WaitProc(int proc) { int ret; ret = PARMCI_WaitProc(proc); return ret; } void armci_msg_barrier() { armci_profile_start(ARMCI_PROF_BARRIER); parmci_msg_barrier(); armci_profile_stop(ARMCI_PROF_BARRIER); } void armci_msg_group_barrier(ARMCI_Group *group) { armci_profile_start(ARMCI_PROF_BARRIER); parmci_msg_group_barrier(group); armci_profile_stop(ARMCI_PROF_BARRIER); } int armci_notify_wait(int proc, int *pval) { int ret; armci_profile_start(ARMCI_PROF_NOTIFY); ret = parmci_notify_wait(proc, pval); armci_profile_stop(ARMCI_PROF_NOTIFY); return ret; } ga-5.9.2/armci/tools/armci_profile.c000066400000000000000000000577671500715745200173640ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: armci_profile.c,v 1.8 2005-11-30 10:20:53 vinod Exp $ */ /** * Profiler can profile the following ARMCI Calls: * ARMCI_Get,ARMCI_Put,ARMCI_Acc,ARMCI_NbGet,ARMCI_NbPut,ARMCI_NbAcc, * ARMCI_GetS,ARMCI_PutS,ARMCI_AccS,ARMCI_NbGetS,ARMCI_NbPutS,ARMCI_NbAccS, * ARMCI_GetV,ARMCI_PutV,ARMCI_AccV,ARMCI_NbGetV,ARMCI_NbPutV,ARMCI_NbAccV, * ARMCI_Wait, armci_wait_notify * (NOTE: As armci_notify is same as ARMCI_Put, it is not profiled.) * * * Note #1: Right now, only process 0's profile is printed. * Each and every process saves its profile in the correspoding data struture. * Each process prints its profile to an output file armci_profile. * when armci_profile_terminate() is called (called in ARMCI_Finalize()). * * Note #2: By default profiler prints msg ranges 0 to 21. Example: range 10 * corresponds to message ranges from 1024 bytes to 2047 bytes. * Message ranges are in the power of 2. for ex: * ------------------------------------ * MSG_RANGE (r) BYTES (2^r to 2^(r+1)-1) * ------------------------------------ * 0 0-1 * 1 2-3 * 2 4-7 * ... ... * 10 1024-2047 bytes * ... ... * 20 1MB - (2MB-1) * 21 >= 2MB * ------------------------------------- * To increase the message range, set ARMCI_MAX_MSG_RANGE accordingly. * * Note #3: If Stride information needs to be printed, set ARMCI_PRINT_STRIDE. * Stride information is printed in armci_profile_terminate() for a various * selective message ranges and event types.Modify it according to your needs. * * Note #4: There is no profiling support for non-blocking operations yet!! */ #define DEBUG_ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_MATH_H # include #endif #include "armci.h" #include "armcip.h" #include "armci_profile.h" #ifndef MSG_COMMS_MPI # include "tcgmsg.h" # define MP_TIMER tcg_time #else # include "mpi.h" # define MP_TIMER MPI_Wtime #endif #define ARMCI_PRINT_STRIDE 1 #define ARMCI_MAX_MSG_RANGE 22 /* 0 to 21 */ #if ARMCI_PRINT_STRIDE # define STRIDE_COUNT 1000 # define ARMCI_MAX_DIM 7 typedef struct armci_stride { int stride_levels; int proc; int count[ARMCI_MAX_DIM]; double time; }armci_stride_t; typedef struct giov { int ptr_array_len; int bytes; }giov_t; typedef struct armci_vector { int vec_len; int proc; giov_t *giov; double time; }armci_vector_t; #endif #define ARMCI_EVENTS 24 char *gEventName[ARMCI_EVENTS]={ "GET", "PUT", "ACC", "STRIDED GET", "STRIDED PUT", "STRIDED ACC", "VECTOR GET", "VECTOR PUT", "VECTOR ACC", "NBGET", "NBPUT", "NBACC", "STRIDED NBGET", "STRIDED NBPUT", "STRIDED NBACC", "VECTOR NBGET", "VECTOR NBPUT", "VECTOR NBACC", "BARRIER","ARMCI_WAIT","NOTIFY_WAIT", "FENCE", "ALLFENCE", "RMW" }; typedef struct armci_profile { int count; /* number of times called */ double time; /* total execution time for "count" calls */ #if ARMCI_PRINT_STRIDE armci_stride_t *stride; armci_vector_t *vector; #endif }armci_profile_t; /* profile get/put/acc for various message ranges (i.e ARMCI_MAX_MSG_RANGE) */ static armci_profile_t ARMCI_PROF[ARMCI_EVENTS][ARMCI_MAX_MSG_RANGE]; /* Current event */ struct event_info { int is_set; int event_type; int range; double start_time; } gCURRENT_EVNT; static int strided_event(int e) { if (e==ARMCI_PROF_GETS || e==ARMCI_PROF_PUTS || e==ARMCI_PROF_ACCS || e==ARMCI_PROF_NBGETS || e==ARMCI_PROF_NBPUTS || e==ARMCI_PROF_NBACCS) return 1; return 0; } void armci_profile_init() { int i,j; if(armci_me==0) {printf("\nProfiling ARMCI - ON\n");fflush(stdout);} gCURRENT_EVNT.is_set = 0; for(i=0; i= ARMCI_MAX_DIM) armci_die("ARMCI_PROFILE: stride_levels >= ARMCI_MAX_DIM. Increase ARMCI_MAX_DIM.", armci_me); /* find the message range */ for(i=0; i<= stride_levels; i++) bytes *= count[i]; if(bytes<=0) range=0; else range = (int) (log((double)bytes)/log(2.0)); if(range>=ARMCI_MAX_MSG_RANGE-1) range = ARMCI_MAX_MSG_RANGE-1; /* set the curent event for timer */ status = armci_profile_set_event(event_type, range); if(status == ARMCI_EVENT_SET) { /* new event set */ /* profile update: i.e. update event count */ ARMCI_PROF[event_type][range].count++; # if ARMCI_PRINT_STRIDE if(strided_event(event_type)) { int idx = ARMCI_PROF[event_type][range].count-1; if(idx=ARMCI_MAX_MSG_RANGE-1) range = ARMCI_MAX_MSG_RANGE-1; /* set the curent event for timer */ status = armci_profile_set_event(event_type, range); if(status == ARMCI_EVENT_SET) { /* new event set */ /* profile update: i.e. update event count */ ARMCI_PROF[event_type][range].count++; # if ARMCI_PRINT_STRIDE { int idx = ARMCI_PROF[event_type][range].count-1; if(idx=%d)\n", 1<<(ARMCI_MAX_MSG_RANGE-1)); } } /* print profile of contiguous get/put/acc calls for every message range */ static void armci_print_contig(FILE *fp) { int i, nrange=ARMCI_MAX_MSG_RANGE; ARMCI_HDR1(fp); ARMCI_HDR3(fp); for(i=0; i< nrange; i++) { fprintf(fp, "%d\t %d\t %d\t %.2e %.2e %.2e ", ARMCI_PROF[ARMCI_PROF_GET][i].count, ARMCI_PROF[ARMCI_PROF_PUT][i].count, ARMCI_PROF[ARMCI_PROF_ACC][i].count, ARMCI_PROF[ARMCI_PROF_GET][i].time, ARMCI_PROF[ARMCI_PROF_PUT][i].time, ARMCI_PROF[ARMCI_PROF_ACC][i].time); if(i< nrange-1) fprintf(fp, "(%d-%d)\n", 1<=%d)\n", 1<<(ARMCI_MAX_MSG_RANGE-1)); } } /* This prints the number of non-contiguous get/put/acc/ calls for every message range */ static void armci_print_noncontig(FILE *fp) { int i, nget, nput, nacc, nrange=ARMCI_MAX_MSG_RANGE; double gtime, ptime, atime; ARMCI_HDR2(fp); ARMCI_HDR3(fp); for(i=0; i< nrange; i++) { nget = (ARMCI_PROF[ARMCI_PROF_GETS][i].count + ARMCI_PROF[ARMCI_PROF_GETV][i].count); nput = (ARMCI_PROF[ARMCI_PROF_PUTS][i].count + ARMCI_PROF[ARMCI_PROF_PUTV][i].count); nacc = (ARMCI_PROF[ARMCI_PROF_ACCS][i].count + ARMCI_PROF[ARMCI_PROF_ACCV][i].count); gtime = (ARMCI_PROF[ARMCI_PROF_GETS][i].time + ARMCI_PROF[ARMCI_PROF_GETV][i].time); ptime = (ARMCI_PROF[ARMCI_PROF_PUTS][i].time + ARMCI_PROF[ARMCI_PROF_PUTV][i].time); atime = (ARMCI_PROF[ARMCI_PROF_ACCS][i].time + ARMCI_PROF[ARMCI_PROF_ACCV][i].time); fprintf(fp, "%d\t %d\t %d\t %.2e %.2e %.2e ", nget, nput, nacc, gtime, ptime, atime); if (i< nrange-1) fprintf(fp, "(%d-%d)\n", 1<=%d)\n", 1<<(ARMCI_MAX_MSG_RANGE-1)); } } /* print profile of non-blocking contiguous get/put/acc calls for every message range */ static void armci_print_nbcontig(FILE *fp) { int i, nrange=ARMCI_MAX_MSG_RANGE; ARMCI_HDR6(fp); ARMCI_HDR8(fp); for(i=0; i< nrange; i++) { fprintf(fp, "%d\t %d\t %d\t %.2e %.2e %.2e ", ARMCI_PROF[ARMCI_PROF_NBGET][i].count, ARMCI_PROF[ARMCI_PROF_NBPUT][i].count, ARMCI_PROF[ARMCI_PROF_NBACC][i].count, ARMCI_PROF[ARMCI_PROF_NBGET][i].time, ARMCI_PROF[ARMCI_PROF_NBPUT][i].time, ARMCI_PROF[ARMCI_PROF_NBACC][i].time); if(i< nrange-1) fprintf(fp, "(%d-%d)\n", 1<=%d)\n", 1<<(ARMCI_MAX_MSG_RANGE-1)); } } /* This prints the number of non-blocking non-contiguous get/put/acc/ calls for every message range */ static void armci_print_nbnoncontig(FILE *fp) { int i, nget, nput, nacc, nrange=ARMCI_MAX_MSG_RANGE; double gtime, ptime, atime; ARMCI_HDR7(fp); ARMCI_HDR8(fp); for(i=0; i< nrange; i++) { nget = (ARMCI_PROF[ARMCI_PROF_NBGETS][i].count + ARMCI_PROF[ARMCI_PROF_NBGETV][i].count); nput = (ARMCI_PROF[ARMCI_PROF_NBPUTS][i].count + ARMCI_PROF[ARMCI_PROF_NBPUTV][i].count); nacc = (ARMCI_PROF[ARMCI_PROF_NBACCS][i].count + ARMCI_PROF[ARMCI_PROF_NBACCV][i].count); gtime = (ARMCI_PROF[ARMCI_PROF_NBGETS][i].time + ARMCI_PROF[ARMCI_PROF_NBGETV][i].time); ptime = (ARMCI_PROF[ARMCI_PROF_NBPUTS][i].time + ARMCI_PROF[ARMCI_PROF_NBPUTV][i].time); atime = (ARMCI_PROF[ARMCI_PROF_NBACCS][i].time + ARMCI_PROF[ARMCI_PROF_NBACCV][i].time); fprintf(fp, "%d\t %d\t %d\t %.2e %.2e %.2e ", nget, nput, nacc, gtime, ptime, atime); if (i< nrange-1) fprintf(fp, "(%d-%d)\n", 1<=%d)\n", 1<<(ARMCI_MAX_MSG_RANGE-1)); } } /* Profile of armci_notify_wait(), ARMCI_Wait() and ARMCI_Barrier() */ static void armci_print_misc(FILE *fp) { ARMCI_HDR9(fp); fprintf(fp, "#calls\t time\t EVENT\n\n"); fprintf(fp, "%d\t %.2e ARMCI_Wait()\n", ARMCI_PROF[ARMCI_PROF_WAIT][0].count, ARMCI_PROF[ARMCI_PROF_WAIT][0].time); fprintf(fp, "%d\t %.2e armci_notify_wait()\n", ARMCI_PROF[ARMCI_PROF_NOTIFY][0].count, ARMCI_PROF[ARMCI_PROF_NOTIFY][0].time); fprintf(fp, "%d\t %.2e ARMCI_Barrier()\n", ARMCI_PROF[ARMCI_PROF_BARRIER][0].count, ARMCI_PROF[ARMCI_PROF_BARRIER][0].time); fprintf(fp, "%d\t %.2e ARMCI_Fence()\n", ARMCI_PROF[ARMCI_PROF_FENCE][0].count, ARMCI_PROF[ARMCI_PROF_FENCE][0].time); fprintf(fp, "%d\t %.2e ARMCI_Allfence()\n", ARMCI_PROF[ARMCI_PROF_ALLFENCE][0].count, ARMCI_PROF[ARMCI_PROF_ALLFENCE][0].time); fprintf(fp, "%d\t %.2e ARMCI_Rmw()\n", ARMCI_PROF[ARMCI_PROF_RMW][0].count, ARMCI_PROF[ARMCI_PROF_RMW][0].time); } #if ARMCI_PRINT_STRIDE static void armci_print_warning_msg(FILE *fp, int range, int str_count) { fprintf(fp, "WARNING: In your program, total number of data transfers\n"); fprintf(fp, "for message range[%d - %d] is %d. This exceeds\n", 1< %d (in armci_profile.c)\n", str_count); } static void armci_print_stridedinfo(FILE *fp, int event, int range) { int i, j, stride_levels, str_count; double time=0.0; str_count = ARMCI_PROF[event][range].count; if(str_count <=0) return; if(str_count > STRIDE_COUNT) { armci_print_warning_msg(fp, range, str_count); str_count = STRIDE_COUNT; } fprintf(fp, "\n\nSTRIDE INFORMATION FOR MSG_RANGE %d-%d for EVENT: %s\n", 1< STRIDE_COUNT) { armci_print_warning_msg(fp, range, str_count); str_count = STRIDE_COUNT; } fprintf(fp, "\n\nVECTOR INFORMATION FOR MSG_RANGE %d-%d for EVENT: %s\n", 1< #include #include "parmci.h" #define TIME MPI_Wtime static double ARMCI_AccV_t; static double ARMCI_Barrier_t; static double ARMCI_AccS_t; static double ARMCI_Finalize_t; static double ARMCI_NbPut_t; static double ARMCI_GetValueInt_t; static double ARMCI_Put_flag_t; static double ARMCI_WaitAll_t; static double ARMCI_Malloc_local_t; static double ARMCI_Free_local_t; static double ARMCI_Get_t; static double ARMCI_PutValueFloat_t; static double ARMCI_NbAccV_t; static double ARMCI_GetValueFloat_t; static double ARMCI_Malloc_t; static double ARMCI_NbAccS_t; static double ARMCI_PutS_t; static double ARMCI_PutV_t; static double ARMCI_Destroy_mutexes_t; static double ARMCI_Free_t; static double ARMCI_Init_args_t; static double ARMCI_PutValueInt_t; static double ARMCI_Memget_t; static double ARMCI_AllFence_t; static double ARMCI_NbPutV_t; static double ARMCI_PutValueDouble_t; static double ARMCI_GetV_t; static double ARMCI_Test_t; static double ARMCI_GetS_t; static double ARMCI_Unlock_t; static double ARMCI_Fence_t; static double ARMCI_Create_mutexes_t; static double ARMCI_PutS_flag_t; static double ARMCI_WaitProc_t; static double ARMCI_Lock_t; static double ARMCI_GetValueDouble_t; static double ARMCI_NbGetV_t; static double ARMCI_Rmw_t; static double ARMCI_Init_t; static double ARMCI_NbGetS_t; static double ARMCI_NbGet_t; static double ARMCI_Put_t; static double ARMCI_NbPutS_t; static double ARMCI_PutS_flag_dir_t; static double ARMCI_Wait_t; static double ARMCI_GetValueLong_t; static double ARMCI_PutValueLong_t; static int ARMCI_AccV_c; static int ARMCI_Barrier_c; static int ARMCI_AccS_c; static int ARMCI_Finalize_c; static int ARMCI_NbPut_c; static int ARMCI_GetValueInt_c; static int ARMCI_Put_flag_c; static int ARMCI_WaitAll_c; static int ARMCI_Malloc_local_c; static int ARMCI_Free_local_c; static int ARMCI_Get_c; static int ARMCI_PutValueFloat_c; static int ARMCI_NbAccV_c; static int ARMCI_GetValueFloat_c; static int ARMCI_Malloc_c; static int ARMCI_NbAccS_c; static int ARMCI_PutS_c; static int ARMCI_PutV_c; static int ARMCI_Destroy_mutexes_c; static int ARMCI_Free_c; static int ARMCI_Init_args_c; static int ARMCI_PutValueInt_c; static int ARMCI_Memget_c; static int ARMCI_AllFence_c; static int ARMCI_NbPutV_c; static int ARMCI_PutValueDouble_c; static int ARMCI_GetV_c; static int ARMCI_Test_c; static int ARMCI_GetS_c; static int ARMCI_Unlock_c; static int ARMCI_Fence_c; static int ARMCI_Create_mutexes_c; static int ARMCI_PutS_flag_c; static int ARMCI_WaitProc_c; static int ARMCI_Lock_c; static int ARMCI_GetValueDouble_c; static int ARMCI_NbGetV_c; static int ARMCI_Rmw_c; static int ARMCI_Init_c; static int ARMCI_NbGetS_c; static int ARMCI_NbGet_c; static int ARMCI_Put_c; static int ARMCI_NbPutS_c; static int ARMCI_PutS_flag_dir_c; static int ARMCI_Wait_c; static int ARMCI_GetValueLong_c; static int ARMCI_PutValueLong_c; static double t; static int c; static int me; void ARMCI_Finalize() { MPI_Comm_rank(MPI_COMM_WORLD, &me); MPI_Reduce(&ARMCI_AccV_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_AccV_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_AccV,%d,%lf\n", ARMCI_AccV_c, ARMCI_AccV_t); } MPI_Reduce(&ARMCI_Barrier_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Barrier_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Barrier,%d,%lf\n", ARMCI_Barrier_c, ARMCI_Barrier_t); } MPI_Reduce(&ARMCI_AccS_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_AccS_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_AccS,%d,%lf\n", ARMCI_AccS_c, ARMCI_AccS_t); } MPI_Reduce(&ARMCI_NbPut_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_NbPut_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_NbPut,%d,%lf\n", ARMCI_NbPut_c, ARMCI_NbPut_t); } MPI_Reduce(&ARMCI_GetValueInt_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_GetValueInt_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_GetValueInt,%d,%lf\n", ARMCI_GetValueInt_c, ARMCI_GetValueInt_t); } MPI_Reduce(&ARMCI_Put_flag_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Put_flag_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Put_flag,%d,%lf\n", ARMCI_Put_flag_c, ARMCI_Put_flag_t); } MPI_Reduce(&ARMCI_NbGetS_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_NbGetS_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_NbGetS,%d,%lf\n", ARMCI_NbGetS_c, ARMCI_NbGetS_t); } MPI_Reduce(&ARMCI_Malloc_local_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Malloc_local_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Malloc_local,%d,%lf\n", ARMCI_Malloc_local_c, ARMCI_Malloc_local_t); } MPI_Reduce(&ARMCI_Free_local_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Free_local_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Free_local,%d,%lf\n", ARMCI_Free_local_c, ARMCI_Free_local_t); } MPI_Reduce(&ARMCI_Get_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Get_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Get,%d,%lf\n", ARMCI_Get_c, ARMCI_Get_t); } MPI_Reduce(&ARMCI_Put_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Put_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Put,%d,%lf\n", ARMCI_Put_c, ARMCI_Put_t); } MPI_Reduce(&ARMCI_Destroy_mutexes_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Destroy_mutexes_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Destroy_mutexes,%d,%lf\n", ARMCI_Destroy_mutexes_c, ARMCI_Destroy_mutexes_t); } MPI_Reduce(&ARMCI_GetS_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_GetS_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_GetS,%d,%lf\n", ARMCI_GetS_c, ARMCI_GetS_t); } MPI_Reduce(&ARMCI_NbAccV_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_NbAccV_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_NbAccV,%d,%lf\n", ARMCI_NbAccV_c, ARMCI_NbAccV_t); } MPI_Reduce(&ARMCI_GetValueFloat_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_GetValueFloat_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_GetValueFloat,%d,%lf\n", ARMCI_GetValueFloat_c, ARMCI_GetValueFloat_t); } MPI_Reduce(&ARMCI_Malloc_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Malloc_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Malloc,%d,%lf\n", ARMCI_Malloc_c, ARMCI_Malloc_t); } MPI_Reduce(&ARMCI_NbAccS_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_NbAccS_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_NbAccS,%d,%lf\n", ARMCI_NbAccS_c, ARMCI_NbAccS_t); } MPI_Reduce(&ARMCI_PutS_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_PutS_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_PutS,%d,%lf\n", ARMCI_PutS_c, ARMCI_PutS_t); } MPI_Reduce(&ARMCI_PutV_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_PutV_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_PutV,%d,%lf\n", ARMCI_PutV_c, ARMCI_PutV_t); } MPI_Reduce(&ARMCI_Free_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Free_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Free,%d,%lf\n", ARMCI_Free_c, ARMCI_Free_t); } MPI_Reduce(&ARMCI_Init_args_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Init_args_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Init_args,%d,%lf\n", ARMCI_Init_args_c, ARMCI_Init_args_t); } MPI_Reduce(&ARMCI_PutValueInt_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_PutValueInt_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_PutValueInt,%d,%lf\n", ARMCI_PutValueInt_c, ARMCI_PutValueInt_t); } MPI_Reduce(&ARMCI_Memget_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Memget_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Memget,%d,%lf\n", ARMCI_Memget_c, ARMCI_Memget_t); } MPI_Reduce(&ARMCI_AllFence_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_AllFence_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_AllFence,%d,%lf\n", ARMCI_AllFence_c, ARMCI_AllFence_t); } MPI_Reduce(&ARMCI_NbPutV_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_NbPutV_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_NbPutV,%d,%lf\n", ARMCI_NbPutV_c, ARMCI_NbPutV_t); } MPI_Reduce(&ARMCI_PutValueDouble_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_PutValueDouble_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_PutValueDouble,%d,%lf\n", ARMCI_PutValueDouble_c, ARMCI_PutValueDouble_t); } MPI_Reduce(&ARMCI_GetV_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_GetV_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_GetV,%d,%lf\n", ARMCI_GetV_c, ARMCI_GetV_t); } MPI_Reduce(&ARMCI_Test_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Test_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Test,%d,%lf\n", ARMCI_Test_c, ARMCI_Test_t); } MPI_Reduce(&ARMCI_Unlock_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Unlock_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Unlock,%d,%lf\n", ARMCI_Unlock_c, ARMCI_Unlock_t); } MPI_Reduce(&ARMCI_Fence_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Fence_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Fence,%d,%lf\n", ARMCI_Fence_c, ARMCI_Fence_t); } MPI_Reduce(&ARMCI_Create_mutexes_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Create_mutexes_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Create_mutexes,%d,%lf\n", ARMCI_Create_mutexes_c, ARMCI_Create_mutexes_t); } MPI_Reduce(&ARMCI_PutS_flag_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_PutS_flag_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_PutS_flag,%d,%lf\n", ARMCI_PutS_flag_c, ARMCI_PutS_flag_t); } MPI_Reduce(&ARMCI_WaitProc_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_WaitProc_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_WaitProc,%d,%lf\n", ARMCI_WaitProc_c, ARMCI_WaitProc_t); } MPI_Reduce(&ARMCI_Lock_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Lock_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Lock,%d,%lf\n", ARMCI_Lock_c, ARMCI_Lock_t); } MPI_Reduce(&ARMCI_GetValueDouble_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_GetValueDouble_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_GetValueDouble,%d,%lf\n", ARMCI_GetValueDouble_c, ARMCI_GetValueDouble_t); } MPI_Reduce(&ARMCI_NbGetV_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_NbGetV_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_NbGetV,%d,%lf\n", ARMCI_NbGetV_c, ARMCI_NbGetV_t); } MPI_Reduce(&ARMCI_Rmw_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Rmw_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Rmw,%d,%lf\n", ARMCI_Rmw_c, ARMCI_Rmw_t); } MPI_Reduce(&ARMCI_Init_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Init_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Init,%d,%lf\n", ARMCI_Init_c, ARMCI_Init_t); } MPI_Reduce(&ARMCI_WaitAll_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_WaitAll_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_WaitAll,%d,%lf\n", ARMCI_WaitAll_c, ARMCI_WaitAll_t); } MPI_Reduce(&ARMCI_NbGet_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_NbGet_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_NbGet,%d,%lf\n", ARMCI_NbGet_c, ARMCI_NbGet_t); } MPI_Reduce(&ARMCI_PutValueFloat_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_PutValueFloat_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_PutValueFloat,%d,%lf\n", ARMCI_PutValueFloat_c, ARMCI_PutValueFloat_t); } MPI_Reduce(&ARMCI_NbPutS_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_NbPutS_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_NbPutS,%d,%lf\n", ARMCI_NbPutS_c, ARMCI_NbPutS_t); } MPI_Reduce(&ARMCI_PutS_flag_dir_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_PutS_flag_dir_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_PutS_flag_dir,%d,%lf\n", ARMCI_PutS_flag_dir_c, ARMCI_PutS_flag_dir_t); } MPI_Reduce(&ARMCI_PutValueLong_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_PutValueLong_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_PutValueLong,%d,%lf\n", ARMCI_PutValueLong_c, ARMCI_PutValueLong_t); } MPI_Reduce(&ARMCI_Wait_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_Wait_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_Wait,%d,%lf\n", ARMCI_Wait_c, ARMCI_Wait_t); } MPI_Reduce(&ARMCI_GetValueLong_c, &c, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(&ARMCI_GetValueLong_t, &t, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if (me == 0) { printf("ARMCI_GetValueLong,%d,%lf\n", ARMCI_GetValueLong_c, ARMCI_GetValueLong_t); } PARMCI_Finalize(); } int ARMCI_AccV(int op, void *scale, armci_giov_t * darr, int len, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_AccV(op, scale, darr, len, proc); etime = TIME(); ARMCI_AccV_t += etime - stime; return rval; } void ARMCI_Barrier() { static double stime, etime; stime = TIME(); PARMCI_Barrier(); etime = TIME(); ARMCI_Barrier_t += etime - stime; } int ARMCI_AccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_AccS(optype, scale, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); etime = TIME(); ARMCI_AccS_t += etime - stime; return rval; } int ARMCI_NbPut(void *src, void *dst, int bytes, int proc, armci_hdl_t * nb_handle) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_NbPut(src, dst, bytes, proc, nb_handle); etime = TIME(); ARMCI_NbPut_t += etime - stime; return rval; } int ARMCI_GetValueInt(void *src, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_GetValueInt(src, proc); etime = TIME(); ARMCI_GetValueInt_t += etime - stime; return rval; } int ARMCI_Put_flag(void *src, void *dst, int bytes, int *f, int v, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_Put_flag(src, dst, bytes, f, v, proc); etime = TIME(); ARMCI_Put_flag_t += etime - stime; return rval; } int ARMCI_NbGetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t * nb_handle) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_NbGetS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); etime = TIME(); ARMCI_NbGetS_t += etime - stime; return rval; } void *ARMCI_Malloc_local(armci_size_t bytes) { void *rval; static double stime, etime; stime = TIME(); rval = PARMCI_Malloc_local(bytes); etime = TIME(); ARMCI_Malloc_local_t += etime - stime; return rval; } int ARMCI_Free_local(void *ptr) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_Free_local(ptr); etime = TIME(); ARMCI_Free_local_t += etime - stime; return rval; } int ARMCI_Get(void *src, void *dst, int bytes, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_Get(src, dst, bytes, proc); etime = TIME(); ARMCI_Get_t += etime - stime; return rval; } int ARMCI_Put(void *src, void *dst, int bytes, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_Put(src, dst, bytes, proc); etime = TIME(); ARMCI_Put_t += etime - stime; return rval; } int ARMCI_Destroy_mutexes() { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_Destroy_mutexes(); etime = TIME(); ARMCI_Destroy_mutexes_t += etime - stime; return rval; } int ARMCI_GetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_GetS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); etime = TIME(); ARMCI_GetS_t += etime - stime; return rval; } int ARMCI_NbAccV(int op, void *scale, armci_giov_t * darr, int len, int proc, armci_hdl_t * nb_handle) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_NbAccV(op, scale, darr, len, proc, nb_handle); etime = TIME(); ARMCI_NbAccV_t += etime - stime; return rval; } float ARMCI_GetValueFloat(void *src, int proc) { float rval; static double stime, etime; stime = TIME(); rval = PARMCI_GetValueFloat(src, proc); etime = TIME(); ARMCI_GetValueFloat_t += etime - stime; return rval; } int ARMCI_Malloc(void **ptr_arr, armci_size_t bytes) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_Malloc(ptr_arr, bytes); etime = TIME(); ARMCI_Malloc_t += etime - stime; return rval; } int ARMCI_NbAccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t * nb_handle) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_NbAccS(optype, scale, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); etime = TIME(); ARMCI_NbAccS_t += etime - stime; return rval; } int ARMCI_PutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_PutS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); etime = TIME(); ARMCI_PutS_t += etime - stime; return rval; } int ARMCI_PutV(armci_giov_t * darr, int len, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_PutV(darr, len, proc); etime = TIME(); ARMCI_PutV_t += etime - stime; return rval; } int ARMCI_Free(void *ptr) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_Free(ptr); etime = TIME(); ARMCI_Free_t += etime - stime; return rval; } int ARMCI_Init_args(int *argc, char ***argv) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_Init_args(argc, argv); etime = TIME(); ARMCI_Init_args_t += etime - stime; return rval; } int ARMCI_PutValueInt(int src, void *dst, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_PutValueInt(src, dst, proc); etime = TIME(); ARMCI_PutValueInt_t += etime - stime; return rval; } void ARMCI_Memget(size_t bytes, armci_meminfo_t * meminfo, int memflg) { static double stime, etime; stime = TIME(); PARMCI_Memget(bytes, meminfo, memflg); etime = TIME(); ARMCI_Memget_t += etime - stime; } void ARMCI_AllFence() { static double stime, etime; stime = TIME(); PARMCI_AllFence(); etime = TIME(); ARMCI_AllFence_t += etime - stime; } int ARMCI_NbPutV(armci_giov_t * darr, int len, int proc, armci_hdl_t * nb_handle) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_NbPutV(darr, len, proc, nb_handle); etime = TIME(); ARMCI_NbPutV_t += etime - stime; return rval; } int ARMCI_PutValueDouble(double src, void *dst, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_PutValueDouble(src, dst, proc); etime = TIME(); ARMCI_PutValueDouble_t += etime - stime; return rval; } int ARMCI_GetV(armci_giov_t * darr, int len, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_GetV(darr, len, proc); etime = TIME(); ARMCI_GetV_t += etime - stime; return rval; } int ARMCI_Test(armci_hdl_t * nb_handle) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_Test(nb_handle); etime = TIME(); ARMCI_Test_t += etime - stime; return rval; } void ARMCI_Unlock(int mutex, int proc) { static double stime, etime; stime = TIME(); PARMCI_Unlock(mutex, proc); etime = TIME(); ARMCI_Unlock_t += etime - stime; } void ARMCI_Fence(int proc) { static double stime, etime; stime = TIME(); PARMCI_Fence(proc); etime = TIME(); ARMCI_Fence_t += etime - stime; } int ARMCI_Create_mutexes(int num) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_Create_mutexes(num); etime = TIME(); ARMCI_Create_mutexes_t += etime - stime; return rval; } int ARMCI_PutS_flag(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_PutS_flag(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, flag, val, proc); etime = TIME(); ARMCI_PutS_flag_t += etime - stime; return rval; } int ARMCI_WaitProc(int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_WaitProc(proc); etime = TIME(); ARMCI_WaitProc_t += etime - stime; return rval; } void ARMCI_Lock(int mutex, int proc) { static double stime, etime; stime = TIME(); PARMCI_Lock(mutex, proc); etime = TIME(); ARMCI_Lock_t += etime - stime; } double ARMCI_GetValueDouble(void *src, int proc) { double rval; static double stime, etime; stime = TIME(); rval = PARMCI_GetValueDouble(src, proc); etime = TIME(); ARMCI_GetValueDouble_t += etime - stime; return rval; } int ARMCI_NbGetV(armci_giov_t * darr, int len, int proc, armci_hdl_t * nb_handle) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_NbGetV(darr, len, proc, nb_handle); etime = TIME(); ARMCI_NbGetV_t += etime - stime; return rval; } int ARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_Rmw(op, ploc, prem, extra, proc); etime = TIME(); ARMCI_Rmw_t += etime - stime; return rval; } int ARMCI_Init() { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_Init(); etime = TIME(); ARMCI_Init_t += etime - stime; return rval; } int ARMCI_WaitAll() { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_WaitAll(); etime = TIME(); ARMCI_WaitAll_t += etime - stime; return rval; } int ARMCI_NbGet(void *src, void *dst, int bytes, int proc, armci_hdl_t * nb_handle) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_NbGet(src, dst, bytes, proc, nb_handle); etime = TIME(); ARMCI_NbGet_t += etime - stime; return rval; } int ARMCI_PutValueFloat(float src, void *dst, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_PutValueFloat(src, dst, proc); etime = TIME(); ARMCI_PutValueFloat_t += etime - stime; return rval; } int ARMCI_NbPutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t * nb_handle) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_NbPutS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); etime = TIME(); ARMCI_NbPutS_t += etime - stime; return rval; } int ARMCI_PutS_flag_dir(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_PutS_flag_dir(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, flag, val, proc); etime = TIME(); ARMCI_PutS_flag_dir_t += etime - stime; return rval; } int ARMCI_PutValueLong(long src, void *dst, int proc) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_PutValueLong(src, dst, proc); etime = TIME(); ARMCI_PutValueLong_t += etime - stime; return rval; } int ARMCI_Wait(armci_hdl_t * nb_handle) { int rval; static double stime, etime; stime = TIME(); rval = PARMCI_Wait(nb_handle); etime = TIME(); ARMCI_Wait_t += etime - stime; return rval; } long ARMCI_GetValueLong(void *src, int proc) { long rval; static double stime, etime; stime = TIME(); rval = PARMCI_GetValueLong(src, proc); etime = TIME(); ARMCI_GetValueLong_t += etime - stime; return rval; } ga-5.9.2/armci/tools/capi_gen.py000077500000000000000000000103131500715745200164770ustar00rootroot00000000000000#!/usr/bin/env python '''Generate armci's capi.c source from the parmci.h header.''' import sys def get_signatures(header): # first, gather all function signatures from parmci.h aka argv[1] accumulating = False signatures = [] current_signature = '' EXTERN = 'extern' SEMICOLON = ';' for line in open(header): line = line.strip() # remove whitespace before and after line if not line: continue # skip blank lines if EXTERN in line and SEMICOLON in line: signatures.append(line) elif EXTERN in line: current_signature = line accumulating = True elif SEMICOLON in line and accumulating: current_signature += line signatures.append(current_signature) accumulating = False elif accumulating: current_signature += line return signatures class FunctionArgument(object): def __init__(self, signature): self.pointer = signature.count('*') self.array = '[' in signature signature = signature.replace('*','').strip() signature = signature.replace('[','').strip() signature = signature.replace(']','').strip() self.type,self.name = signature.split() def __str__(self): ret = self.type[:] ret += ' ' for p in range(self.pointer): ret += '*' ret += self.name if self.array: ret += '[]' return ret class Function(object): def __init__(self, signature): signature = signature.replace('extern','').strip() self.return_type,signature = signature.split(None,1) self.return_type = self.return_type.strip() signature = signature.strip() if '*' not in self.return_type and signature[0] == '*': # return type is void* not void self.return_type += '*' signature = signature[1:].strip() self.name,signature = signature.split('(',1) self.name = self.name.strip() signature = signature.replace(')','').strip() signature = signature.replace(';','').strip() self.args = [] if signature: for arg in signature.split(','): self.args.append(FunctionArgument(arg.strip())) def get_call(self, name=None): sig = '' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += arg.name sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def get_signature(self, name=None): sig = self.return_type[:] sig += ' ' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += str(arg) sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def __str__(self): return self.get_signature() if __name__ == '__main__': if len(sys.argv) != 2: print 'incorrect number of arguments' print 'usage: capi_gen.py > ' sys.exit(len(sys.argv)) # print headers print ''' #if HAVE_CONFIG_H # include "config.h" #endif #include #include "armci.h" #include "parmci.h" ''' functions = {} # parse signatures into the Function class for sig in get_signatures(sys.argv[1]): function = Function(sig) functions[function.name] = function # now process the functions for name in sorted(functions): func = functions[name] maybe_return = '' if '*' in func.return_type or 'void' not in func.return_type: maybe_return = 'return ' func = functions[name] new_name = None if 'PARMCI_' in name: new_name = name.replace('PARMCI_','ARMCI_') elif 'parmci_' in name: new_name = name.replace('parmci_','armci_') print ''' #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak %s #endif %s { %s%s; } ''' % (new_name, func.get_signature(new_name), maybe_return, func.get_call()) ga-5.9.2/armci/tools/prof_gen.py000077500000000000000000000202741500715745200165400ustar00rootroot00000000000000#!/usr/bin/env python '''Generate the armci_prof.c source from the parmci.h header.''' import sys name_to_event = { "ARMCI_Acc" : "ARMCI_PROF_ACC", "ARMCI_AccS" : "ARMCI_PROF_ACCS", "ARMCI_AccV" : "ARMCI_PROF_ACCV", "ARMCI_AllFence" : "ARMCI_PROF_ALLFENCE", "ARMCI_Barrier" : "ARMCI_PROF_BARRIER", "ARMCI_Fence" : "ARMCI_PROF_FENCE", "ARMCI_Get" : "ARMCI_PROF_GET", "ARMCI_GetS" : "ARMCI_PROF_GETS", "ARMCI_GetV" : "ARMCI_PROF_GETV", "ARMCI_NbAcc" : "ARMCI_PROF_NBACC", "ARMCI_NbAccS" : "ARMCI_PROF_NBACCS", "ARMCI_NbAccV" : "ARMCI_PROF_NBACCV", "ARMCI_NbGet" : "ARMCI_PROF_NBGET", "ARMCI_NbGetS" : "ARMCI_PROF_NBGETS", "ARMCI_NbGetV" : "ARMCI_PROF_NBGETV", "ARMCI_NbPut" : "ARMCI_PROF_NBPUT", "ARMCI_NbPutS" : "ARMCI_PROF_NBPUTS", "ARMCI_NbPutV" : "ARMCI_PROF_NBPUTV", "ARMCI_Put" : "ARMCI_PROF_PUT", "ARMCI_PutS" : "ARMCI_PROF_PUTS", "ARMCI_PutV" : "ARMCI_PROF_PUTV", "ARMCI_Rmw" : "ARMCI_PROF_RMW", "ARMCI_Wait" : "ARMCI_PROF_WAIT", "armci_msg_barrier" : "ARMCI_PROF_BARRIER", "armci_msg_group_barrier" : "ARMCI_PROF_BARRIER", "armci_notify_wait" : "ARMCI_PROF_NOTIFY", } def get_signatures(header): # first, gather all function signatures from parmci.h aka argv[1] accumulating = False signatures = [] current_signature = '' EXTERN = 'extern' SEMICOLON = ';' for line in open(header): line = line.strip() # remove whitespace before and after line if not line: continue # skip blank lines if EXTERN in line and SEMICOLON in line: signatures.append(line) elif EXTERN in line: current_signature = line accumulating = True elif SEMICOLON in line and accumulating: current_signature += line signatures.append(current_signature) accumulating = False elif accumulating: current_signature += line return signatures class FunctionArgument(object): def __init__(self, signature): self.pointer = signature.count('*') self.array = '[' in signature signature = signature.replace('*','').strip() signature = signature.replace('[','').strip() signature = signature.replace(']','').strip() self.type,self.name = signature.split() def __str__(self): ret = self.type[:] ret += ' ' for p in range(self.pointer): ret += '*' ret += self.name if self.array: ret += '[]' return ret class Function(object): def __init__(self, signature): signature = signature.replace('extern','').strip() self.return_type,signature = signature.split(None,1) self.return_type = self.return_type.strip() signature = signature.strip() if '*' not in self.return_type and signature[0] == '*': # return type is void* not void self.return_type += '*' signature = signature[1:].strip() self.name,signature = signature.split('(',1) self.name = self.name.strip() signature = signature.replace(')','').strip() signature = signature.replace(';','').strip() self.args = [] if signature: for arg in signature.split(','): self.args.append(FunctionArgument(arg.strip())) def get_call(self, name=None): sig = '' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += arg.name sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def get_signature(self, name=None): sig = self.return_type[:] sig += ' ' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += str(arg) sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def __str__(self): return self.get_signature() if __name__ == '__main__': if len(sys.argv) != 2: print 'incorrect number of arguments' print 'usage: prof_gen.py > ' sys.exit(len(sys.argv)) # print headers print ''' #if HAVE_CONFIG_H # include "config.h" #endif #include #include "armci.h" #include "parmci.h" #include "armci_profile.h" #include "armci_profile.c" ''' functions = {} # parse signatures into the Function class for sig in get_signatures(sys.argv[1]): function = Function(sig) functions[function.name] = function # now process the functions for name in sorted(functions): func = functions[name] maybe_declare = '' maybe_assign = '' maybe_return = '' if '*' in func.return_type or 'void' not in func.return_type: maybe_declare = '%s ret;' % func.return_type maybe_assign = 'ret = ' maybe_return = 'return ret;' func = functions[name] new_name = None if 'PARMCI_' in name: new_name = name.replace('PARMCI_','ARMCI_') elif 'parmci_' in name: new_name = name.replace('parmci_','armci_') if new_name in ['ARMCI_Init','ARMCI_Init_args']: print ''' %s { int ret; ret = %s; armci_profile_init(); return ret; } ''' % (func.get_signature(new_name), func.get_call()) elif new_name in ['ARMCI_Finalize']: print ''' %s { armci_profile_terminate(); %s; } ''' % (func.get_signature(new_name), func.get_call()) elif new_name in ['ARMCI_GetV', 'ARMCI_PutV', 'ARMCI_AccV', 'ARMCI_NbGetV', 'ARMCI_NbPutV', 'ARMCI_NbAccV']: print ''' %s { %s armci_profile_start_vector(darr, len, proc, %s); %s%s; armci_profile_stop_vector(%s); %s } ''' % (func.get_signature(new_name), maybe_declare, name_to_event[new_name], maybe_assign, func.get_call(), name_to_event[new_name], maybe_return) elif new_name in ['ARMCI_GetS', 'ARMCI_PutS', 'ARMCI_AccS', 'ARMCI_NbGetS', 'ARMCI_NbPutS', 'ARMCI_NbAccS']: print ''' %s { %s armci_profile_start_strided(count, stride_levels, proc, %s); %s%s; armci_profile_stop_strided(%s); %s } ''' % (func.get_signature(new_name), maybe_declare, name_to_event[new_name], maybe_assign, func.get_call(), name_to_event[new_name], maybe_return) elif new_name in ['ARMCI_Get', 'ARMCI_Put', 'ARMCI_Acc', 'ARMCI_NbGet', 'ARMCI_NbPut', 'ARMCI_NbAcc']: print ''' %s { %s armci_profile_start_strided(&bytes, 0, proc, %s); %s%s; armci_profile_stop_strided(%s); %s } ''' % (func.get_signature(new_name), maybe_declare, name_to_event[new_name], maybe_assign, func.get_call(), name_to_event[new_name], maybe_return) elif "ARMCI_Fence" in new_name: print ''' %s { if (!SAMECLUSNODE(proc)) armci_profile_start(ARMCI_PROF_FENCE); %s%s; if (!SAMECLUSNODE(proc)) armci_profile_stop(ARMCI_PROF_FENCE); } ''' % (func.get_signature(new_name), maybe_assign, func.get_call()) elif new_name in name_to_event: print ''' %s { %s armci_profile_start(%s); %s%s; armci_profile_stop(%s); %s } ''' % (func.get_signature(new_name), maybe_declare, name_to_event[new_name], maybe_assign, func.get_call(), name_to_event[new_name], maybe_return) else: print ''' %s { %s %s%s; %s } ''' % (func.get_signature(new_name), maybe_declare, maybe_assign, func.get_call(), maybe_return) ga-5.9.2/autogen.sh000077500000000000000000000040351500715745200141270ustar00rootroot00000000000000#! /bin/sh set -e set -x if [ -z "$1" ]; then AUTOTOOLS_DIR="`pwd`/autotools" else AUTOTOOLS_DIR="$1" fi # This is where updated Autotools will be for Linux. # We force the use of specific Autotools versions. sh ./travis/install-autotools.sh "$AUTOTOOLS_DIR" export PATH="${AUTOTOOLS_DIR}/bin":$PATH export M4="${AUTOTOOLS_DIR}/bin/m4" export AUTOCONF="${AUTOTOOLS_DIR}/bin/autoconf" export AUTOHEADER="${AUTOTOOLS_DIR}/bin/autoheader" export AUTOM4TE="${AUTOTOOLS_DIR}/bin/autom4te" export AUTORECONF="${AUTOTOOLS_DIR}/bin/autoreconf" export AUTOSCAN="${AUTOTOOLS_DIR}/bin/autoscan" export AUTOUPDATE="${AUTOTOOLS_DIR}/bin/autoupdate" export IFNAMES="${AUTOTOOLS_DIR}/bin/ifnames" export AUTOMAKE="${AUTOTOOLS_DIR}/bin/automake" export ACLOCAL="${AUTOTOOLS_DIR}/bin/aclocal" autoreconf -vif # patch to configure script for PGF90 and -lnuma for conffile in configure comex/configure armci/configure do # check whether patch is needed if grep lnuma $conffile > /dev/null then echo "patch already applied to $conffile" else echo "patching $conffile" # OSX sed doesn't do in-place easily, the following should work anywhere rm -f $conffile.tmp sed '/cmdline.*ignore/a\ ac_f77_v_output=`echo $ac_f77_v_output | sed "s/ -lnuma//g"`\ ' $conffile > $conffile.tmp mv $conffile.tmp $conffile # sed might change file permissions chmod ug+xr $conffile fi done # overwrite config.guess and config.sub with latest for dir in build-aux comex/build-aux armci/build-aux do cp $AUTOTOOLS_DIR/bin/config.guess $dir cp $AUTOTOOLS_DIR/bin/config.sub $dir # ensure these are executable scripts chmod ug+xr $dir/config.guess chmod ug+xr $dir/config.sub done # patch ltmain.sh for special intel -mkl flag for dir in build-aux comex/build-aux armci/build-aux do rm -f $dir/ltmain.sh.tmp sed 's/\([m6][t4]|\)/mkl*|-\1/' $dir/ltmain.sh > $dir/ltmain.sh.tmp mv $dir/ltmain.sh.tmp $dir/ltmain.sh # sed might change file permissions chmod ug+xr $dir/ltmain.sh done ga-5.9.2/benchmarks/000077500000000000000000000000001500715745200142415ustar00rootroot00000000000000ga-5.9.2/benchmarks/Makefile000066400000000000000000000042561500715745200157100ustar00rootroot00000000000000 GA_CONFIG := # e.g. /path/to/ga/install/bin/ga-config ifneq ($(GA_CONFIG),) CPPFLAGS := $(shell $(GA_CONFIG) --cppflags) FFLAGS := $(shell $(GA_CONFIG) --fflags) CFLAGS := $(shell $(GA_CONFIG) --cflags) LIBS := $(shell $(GA_CONFIG) --libs) FLIBS := $(shell $(GA_CONFIG) --flibs) LDFLAGS := $(shell $(GA_CONFIG) --ldflags) CC := $(shell $(GA_CONFIG) --cc) F77 := $(shell $(GA_CONFIG) --f77) endif dist: rm -rf ga-benchmarks mkdir ga-benchmarks cp -f ../global/testing/mp3.h ./ga-benchmarks/mp3.h cp -f ../global/testing/mp3.fh ./ga-benchmarks/mp3.fh cp -f ../global/testing/ga_shift.F ./ga-benchmarks/ga_shift.F cp -f ../global/testing/perf.F ./ga-benchmarks/ga_ptp.F cp -f ../global/testing/perf2.c ./ga-benchmarks/ga_perf.c cp -f ../armci/testing/perf.c ./ga-benchmarks/armci_perf.c cp -f ./Makefile ./ga-benchmarks/Makefile cp -f ./config.h ./ga-benchmarks/config.h cp -f ./config.fh ./ga-benchmarks/config.fh cp -f ./util.c ./ga-benchmarks/util.c cp -f ./testutil.fh ./ga-benchmarks/testutil.fh cp -f ./README ./ga-benchmarks/README sed -i '13,31d' ./ga-benchmarks/Makefile rm -f ga-benchmarks.tgz tar -czf ga-benchmarks.tgz ./ga-benchmarks .SUFFIXES: benchmarks: ga_shift.x ga_ptp.x ga_perf.x armci_perf.x ga_shift.x: ga_shift.o util.o $(F77) -o $@ $^ $(LDFLAGS) $(LIBS) ga_shift.o: ga_shift.F mp3.fh config.fh $(F77) -o $@ -c $< -DHAVE_CONFIG_H -I. $(CPPFLAGS) $(FFLAGS) ga_ptp.x: ga_ptp.o util.o $(F77) -o $@ $^ $(LDFLAGS) $(LIBS) ga_ptp.o: ga_ptp.F mp3.fh config.fh $(F77) -o $@ -c $< -DHAVE_CONFIG_H -I. $(CPPFLAGS) $(FFLAGS) ga_perf.x: ga_perf.o util.o $(CC) -o $@ $^ $(LDFLAGS) $(LIBS) $(FLIBS) ga_perf.o: ga_perf.c mp3.h config.h $(CC) -o $@ -c $< -DHAVE_CONFIG_H -I. $(CPPFLAGS) $(CFLAGS) armci_perf.x: armci_perf.o util.o $(CC) -o $@ $^ $(LDFLAGS) $(LIBS) $(FLIBS) armci_perf.o: armci_perf.c mp3.h config.h $(CC) -o $@ -c $< -DHAVE_CONFIG_H -I. $(CPPFLAGS) $(CFLAGS) util.o: util.c $(CC) -o $@ -c $< -DHAVE_CONFIG_H -I. $(CPPFLAGS) $(CFLAGS) config.h: config.fh: mp3.h: mp3.fh: clean: rm -f *.x *.o ga-5.9.2/benchmarks/README000066400000000000000000000031501500715745200151200ustar00rootroot00000000000000============================================= README for Global Arrays and ARMCI Benchmarks ============================================= How to Build the Benchmarks --------------------------- The Makefile assumes you have installed GA 5-1 or later which also installs the "ga-config" helper script. This script contains the various linker, compiler, and preprocessor flags used to build GA. You must specify the path to ga-config or else edit the Makefile to add the necessary flags manually. For example:: make GA_CONFIG=/path/to/ga/install/bin/ga-config make CPPFLAGS=-I/path/to/ga/install/include LDFLAGS=-L/path/to/ga/install/lib LIBS=-lga It is *much* easier to use the installed ga-config script! Directory Contents ------------------ -armci_perf.c ARMCI benchmark where proc 0 sequentially communicates with the other 1..P procs -config.fh a few necessary preprocessor symbols for Fortran -config.h a few necessary preprocessor symbols for C -ga_perf.c GA benchmark demonstrating the overhead added by the GA layer compared to the "armci_perf.c" benchmark -ga_ptp.F GA benchmark where all procs communicate -ga_shift.F GA benchmark using a shift algorithm -LICENSE legal information about these files -Makefile makefile for building these benchmarks -mp3.fh message passing init and termination symbols for Fortran -mp3.h message passing init and termination symbols for C -README this file -testutil.fh contains a few Fortran declarations for extra functions How to Run the Benchmarks ------------------------- TODO ga-5.9.2/benchmarks/README.first000066400000000000000000000015351500715745200162530ustar00rootroot00000000000000============ README.first ============ What is this for? ----------------- This directory is meant to stage some of our test programs into a gzipped tarball meant for benchmark testing on various platforms. Our tests depend on certain header files, preprocessor directives, and additional test-related functions which aren't available from a standard installation of GA. The files here are meant to fill that void. See the README for details about the contained files. Note that the README will describe these files as well as files which have not yet been staged. How to Generate the Benchmarks Tarball -------------------------------------- Simply run "make". The default make target will create a "ga-benchmarks" directory, copy various test sources as well as the sources found here, and remove the default staging make target from the copied Makefile. ga-5.9.2/benchmarks/config.fh000066400000000000000000000000161500715745200160220ustar00rootroot00000000000000#define MPI 1 ga-5.9.2/benchmarks/config.h000066400000000000000000000002151500715745200156550ustar00rootroot00000000000000#define HAVE_STDIO_H 1 #define HAVE_STDLIB_H 1 #define HAVE_ASSERT_H 1 #define HAVE_UNISTD_H 1 #define HAVE_MATH_H 1 #define MSG_COMMS_MPI 1 ga-5.9.2/benchmarks/testutil.fh000066400000000000000000000001571500715745200164400ustar00rootroot00000000000000 double precision util_timer integer util_mdtob external util_timer external util_mdtob ga-5.9.2/benchmarks/util.c000066400000000000000000000035571500715745200153740ustar00rootroot00000000000000#include #include #include "ga.h" #include "typesf2c.h" #define F77_SET_MA_USE_ARMCI_MEM(name) \ void name() \ { \ int retval; \ if((retval=setenv("MA_USE_ARMCI_MEM", "YES", 1)) != 0) \ GA_Error("setenv failed: insufficient space in the environment",1); \ } F77_SET_MA_USE_ARMCI_MEM(set_ma_use_armci_mem) F77_SET_MA_USE_ARMCI_MEM(set_ma_use_armci_mem_) F77_SET_MA_USE_ARMCI_MEM(set_ma_use_armci_mem__) F77_SET_MA_USE_ARMCI_MEM(SET_MA_USE_ARMCI_MEM) F77_SET_MA_USE_ARMCI_MEM(SET_MA_USE_ARMCI_MEM_) F77_SET_MA_USE_ARMCI_MEM(SET_MA_USE_ARMCI_MEM__) #define F77_UTIL_MDTOB(name) \ Integer name(Integer *n) \ { \ if (*n < 0) \ GA_Error("util_MDTOB_: negative argument",*n); \ return (Integer) (*n * sizeof(DoublePrecision)); \ } F77_UTIL_MDTOB(util_mdtob) F77_UTIL_MDTOB(util_mdtob_) F77_UTIL_MDTOB(util_mdtob__) F77_UTIL_MDTOB(UTIL_MDTOB) F77_UTIL_MDTOB(UTIL_MDTOB_) F77_UTIL_MDTOB(UTIL_MDTOB__) #define F77_UTIL_TIMER(name) \ double name() \ { \ return GA_Wtime(); \ } F77_UTIL_TIMER(util_timer) F77_UTIL_TIMER(util_timer_) F77_UTIL_TIMER(util_timer__) F77_UTIL_TIMER(UTIL_TIMER) F77_UTIL_TIMER(UTIL_TIMER_) F77_UTIL_TIMER(UTIL_TIMER__) #define F77_FLUSH(name) \ void name(Integer *unit) \ { \ fflush(stdout); \ fflush(stderr); \ } F77_FLUSH(ffflush) F77_FLUSH(ffflush_) F77_FLUSH(ffflush__) F77_FLUSH(FFFLUSH) F77_FLUSH(FFFLUSH_) F77_FLUSH(FFFLUSH__) ga-5.9.2/cmake/000077500000000000000000000000001500715745200132045ustar00rootroot00000000000000ga-5.9.2/cmake/config.h.in000066400000000000000000000051411500715745200152300ustar00rootroot00000000000000#include "f2c_cmake.h" #ifndef _GNU_SOURCE # define _GNU_SOURCE 1 #endif #cmakedefine01 HAVE_ASSERT_H #cmakedefine01 HAVE_LIMITS_H #cmakedefine01 HAVE_MALLOC_H #cmakedefine01 HAVE_MATH_H #cmakedefine01 HAVE_MEMCPY #cmakedefine01 HAVE_PAUSE #cmakedefine01 HAVE_STDDEF_H #cmakedefine01 HAVE_STDINT_H #cmakedefine01 HAVE_STDIO_H #cmakedefine01 HAVE_STDLIB_H #cmakedefine01 HAVE_STRCHR #cmakedefine01 HAVE_STRINGS_H #cmakedefine01 HAVE_STRING_H #cmakedefine01 HAVE_SYS_TYPES_H #cmakedefine01 HAVE_UNISTD_H #cmakedefine01 HAVE_WINDOWS_H #cmakedefine01 HAVE_BZERO #if !HAVE_BZERO #define bzero(b,len) (memset((b), '\0', (len)), (void) 0) #endif #cmakedefine01 ENABLE_F77 #cmakedefine01 NOFORT #cmakedefine NOUSE_MMAP #cmakedefine NDEBUG #cmakedefine CYGWIN #cmakedefine DECOSF #cmakedefine01 ENABLE_EISPACK #cmakedefine ENABLE_CHECKPOINT #define ENABLE_PROFILING ${GA_PROFILING} #cmakedefine ENABLE_TRACE #cmakedefine01 STATS #cmakedefine USE_MALLOC #cmakedefine01 HAVE_ARMCI_GROUP_COMM #cmakedefine01 HAVE_ARMCI_GROUP_COMM_MEMBER #cmakedefine01 HAVE_ARMCI_INITIALIZED #cmakedefine01 HAVE_SYS_WEAK_ALIAS_PRAGMA #cmakedefine MPI3 #cmakedefine MPI_MT #cmakedefine MPI_PR #cmakedefine MPI_PT #cmakedefine MPI_TS #cmakedefine01 MSG_COMMS_MPI #cmakedefine01 ENABLE_ARMCI_MEM_OPTION #cmakedefine ENABLE_CUDA_MEM #cmakedefine01 HAVE_BLAS #cmakedefine01 HAVE_LAPACK #cmakedefine01 F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS /*#define F77_FUNC(name,NAME) F77_FUNC_GLOBAL(name,NAME)*/ /*#define F77_FUNC_(name,NAME) F77_FUNC_GLOBAL_(name,NAME)*/ #define F77_FUNC(name,NAME) name ## _ #define F77_FUNC_(name,NAME) name ## _ #define FXX_MODULE ${F90_MODULE} #define F77_GETARG ${F77_GETARG} #define F77_GETARG_ARGS ${F77_GETARG_ARGS} #define F77_GETARG_DECL ${F77_GETARG_DECLS} #define F77_IARGC ${F77_IARGC} #define F77_FLUSH ${F77_FLUSH} #cmakedefine01 HAVE_F77_FLUSH #define SIZEOF_INT ${CM_SIZEOF_INT} #define SIZEOF_DOUBLE ${CM_SIZEOF_DOUBLE} #define SIZEOF_F77_DOUBLE_PRECISION ${CM_SIZEOF_F77_DOUBLE} #define SIZEOF_F77_REAL ${CM_SIZEOF_F77_REAL} #define SIZEOF_F77_INTEGER ${CM_SIZEOF_F77_INTEGER} #define SIZEOF_FLOAT ${CM_SIZEOF_FLOAT} #define SIZEOF_LONG ${CM_SIZEOF_LONG} #define SIZEOF_LONG_DOUBLE ${CM_SIZEOF_LONG_DOUBLE} #define SIZEOF_LONG_LONG ${CM_SIZEOF_LONG_LONG} #define SIZEOF_SHORT ${CM_SIZEOF_SHORT} #define SIZEOF_VOIDP ${CMAKE_SIZEOF_VOID_P} #define BLAS_SIZE ${BLAS_SIZE} /* #define BLAS_SIZE ${CM_BLAS_SIZE} */ #cmakedefine LINUX #cmakedefine LINUX64 #cmakedefine _FILE_OFFSET_BITS #cmakedefine _LARGEFILE_SOURCE #cmakedefine _LARGE_FILES #ifndef __cplusplus #cmakedefine inline ${inline} #endif #cmakedefine restrict ${restrict} ga-5.9.2/cmake/f2c_dummy.h.in000066400000000000000000000007411500715745200156510ustar00rootroot00000000000000/** @file * This is a dummy header file that is used if no Fortran compiler * is specified. These symbols are needed by the GA build even if * the Fortran interface is not being used. */ #ifndef F77_FUNC_HEADER_INCLUDED #define F77_FUNC_HEADER_INCLUDED /* Mangling for Fortran global symbols without underscores. */ #define F77_FUNC_GLOBAL(name,NAME) name##_ /* Mangling for Fortran global symbols with underscores. */ #define F77_FUNC_GLOBAL_(name,NAME) name##_ #endif ga-5.9.2/cmake/farg.h.in000066400000000000000000000010511500715745200146760ustar00rootroot00000000000000#ifndef FARG_H_ #define FARG_H_ #include "typesf2c.h" #if ENABLE_F77 #define F2C_GETARG F77_FUNC_(f2c_getarg,F2C_GETARG) #define F2C_IARGC F77_FUNC_(f2c_iargc,F2C_IARGC) #endif #define F2C_GETARG_ARGV_MAX 255 #define F2C_GETARG_ARGLEN_MAX 255 extern void F2C_GETARG(Integer*, char*, int); extern Integer F2C_IARGC(); extern void ga_c2fstring(char *cstring, char *fstring, int flength); extern void ga_f2cstring(char *fstring, int flength, char *cstring, int clength); extern void ga_f2c_get_cmd_args(int *argc, char ***argv); #endif /* FARG_H_ */ ga-5.9.2/cmake/ga-checks.cmake000066400000000000000000000060761500715745200160440ustar00rootroot00000000000000INCLUDE( CheckCSourceCompiles ) # Check for restrict keyword FOREACH( ac_kw __restrict __restrict__ _Restrict restrict ) CHECK_C_SOURCE_COMPILES( " typedef int * int_ptr; int foo (int_ptr ${ac_kw} ip) { return ip[0]; } int main() { int s[1]; int * ${ac_kw} t = s; t[0] = 0; return foo(t); } " HAVE_RESTRICT ) IF( HAVE_RESTRICT ) SET( ac_cv_c_restrict ${ac_kw} ) BREAK( ) ENDIF( ) ENDFOREACH( ) IF( HAVE_RESTRICT ) SET( restrict ${ac_cv_c_restrict} ) ELSE( ) SET( restrict " " ) ENDIF( ) # Check for inline keyword CHECK_C_SOURCE_COMPILES( " typedef int foo_t; static inline foo_t static_foo(){return 0;} foo_t foo(){return 0;} int main(int argc, char *argv[]){return 0;} " HAVE_INLINE_NATIVE ) IF( HAVE_INLINE_NATIVE ) ELSE ( ) FOREACH( ac_kw __inline__ __inline ) CHECK_C_SOURCE_COMPILES( " typedef int foo_t; static ${ac_kw} foo_t static_foo(){return 0;} foo_t foo(){return 0;} int main(int argc, char *argv[]){return 0;} " HAVE_INLINE ) IF( HAVE_INLINE ) SET( ac_cv_c_inline ${ac_kw} ) BREAK( ) ENDIF( ) ENDFOREACH( ) IF( HAVE_INLINE ) SET( inline ${ac_cv_c_inline} ) ELSE( ) SET( inline " " ) ENDIF( ) ENDIF( ) # check for availability of functions by seeing if small programs compile CHECK_C_SOURCE_COMPILES( " int main(int argc, char *argv[]) { pause(); return 0; } " HAVE_PAUSE ) # check for availability of long double C-type CHECK_C_SOURCE_COMPILES( " int main(int argc, char *argv[]) { long double x; return 0; } " HAVE_LONG_DOUBLE ) CHECK_C_SOURCE_COMPILES( " extern void weakf(int c); #pragma weak weakf = __weakf void __weakf(int c) {} int main(int argc, char **argv) { weakf(0); return(0); } " HAVE_SYS_WEAK_ALIAS_PRAGMA ) if (HAVE_LONG_DOUBLE) set(MA_LONG_DOUBLE "long double") else() set(MA_LONG_DOUBLE "struct {double dummy[2];}") endif() # check size of different variables include(CheckTypeSize) check_type_size("int" CM_SIZEOF_INT) check_type_size("double" CM_SIZEOF_DOUBLE) check_type_size("float" CM_SIZEOF_FLOAT) check_type_size("long" CM_SIZEOF_LONG) check_type_size("long double" CM_SIZEOF_LONG_DOUBLE) check_type_size("long long" CM_SIZEOF_LONG_LONG) check_type_size("short" CM_SIZEOF_SHORT) # check for standard C/C++ include files include(CheckIncludeFiles) check_include_files("assert.h" HAVE_ASSERT_H) check_include_files("limits.h" HAVE_LIMITS_H) check_include_files("linux/limits.h" HAVE_LINUX_LIMITS_H) check_include_files("malloc.h" HAVE_MALLOC_H) check_include_files("math.h" HAVE_MATH_H) check_include_files("stddef.h" HAVE_STDDEF_H) check_include_files("stdint.h" HAVE_STDINT_H) check_include_files("stdio.h" HAVE_STDIO_H) check_include_files("stdlib.h" HAVE_STDLIB_H) check_include_files("strings.h" HAVE_STRINGS_H) check_include_files("string.h" HAVE_STRING_H) check_include_files("sys/types.h" HAVE_SYS_TYPES_H) check_include_files("unistd.h" HAVE_UNISTD_H) check_include_files("windows.h" HAVE_WINDOWS_H) # check for certain functions include(CheckFunctionExists) check_function_exists("bzero" HAVE_BZERO) ga-5.9.2/cmake/ga-compiler-options.cmake000066400000000000000000000047401500715745200201030ustar00rootroot00000000000000 #Tmp Fix: segfaults with the presence of -DNDEBUG if(CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "IntelLLVM") if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RELEASE) set(__cmbt_upper RELEASE) else() string( TOUPPER ${CMAKE_BUILD_TYPE} __cmbt_upper ) endif() set(CMAKE_C_FLAGS_${__cmbt_upper} "-O3 -g") endif() if(CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILER_ID STREQUAL "IntelLLVM") if(NOT "${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Darwin") if(GCCROOT) set(__GA_GCC_INSTALL_PREFIX ${GCCROOT}) set(GA_GCC_TOOLCHAIN_FLAG "--gcc-toolchain=${GCCROOT}") else() get_filename_component(__GA_GCC_INSTALL_PREFIX "${CMAKE_Fortran_COMPILER}/../.." ABSOLUTE) if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") set(GA_GCC_TOOLCHAIN_FLAG "--gcc-toolchain=${__GA_GCC_INSTALL_PREFIX}") else() message(FATAL_ERROR "GCCROOT cmake option not set when using clang compilers. \ Please set a valid path to the GCC installation.") endif() endif() #Check GCC installation if(NOT (EXISTS ${__GA_GCC_INSTALL_PREFIX}/bin AND EXISTS ${__GA_GCC_INSTALL_PREFIX}/include AND EXISTS ${__GA_GCC_INSTALL_PREFIX}/lib) ) message(FATAL_ERROR "GCC installation path found ${__GA_GCC_INSTALL_PREFIX} seems to be incorrect. \ Please set the GCCROOT cmake option to the correct GCC installation prefix.") endif() message(STATUS "GA_GCC_TOOLCHAIN_FLAG: ${GA_GCC_TOOLCHAIN_FLAG}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${GA_GCC_TOOLCHAIN_FLAG}") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${GA_GCC_TOOLCHAIN_FLAG}") set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} ${GA_GCC_TOOLCHAIN_FLAG}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${GA_GCC_TOOLCHAIN_FLAG}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${GA_GCC_TOOLCHAIN_FLAG}") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${GA_GCC_TOOLCHAIN_FLAG}") endif() endif() if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") if(CMAKE_C_COMPILER_VERSION VERSION_EQUAL CMAKE_Fortran_COMPILER_VERSION) message(STATUS "Check GNU compiler versions.") else() message(STATUS "GNU C and Fortran compiler versions do not match") message(FATAL_ERROR "GNU Compiler versions provided: gcc: ${CMAKE_C_COMPILER_VERSION}, gfortran version: ${CMAKE_Fortran_COMPILER_VERSION}") endif() endif() ga-5.9.2/cmake/ga-linalg.cmake000066400000000000000000000231411500715745200160420ustar00rootroot00000000000000# # module: GlobalArrays.cmake # author: Bruce Palmer # description: Define utility functions. # # DISCLAIMER # # This material was prepared as an account of work sponsored by an # agency of the United States Government. Neither the United States # Government nor the United States Department of Energy, nor Battelle, # nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR # ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, # COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, # SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT # INFRINGE PRIVATELY OWNED RIGHTS. # # # ACKNOWLEDGMENT # # This software and its documentation were produced with United States # Government support under Contract Number DE-AC06-76RLO-1830 awarded by # the United States Department of Energy. The United States Government # retains a paid-up non-exclusive, irrevocable worldwide license to # reproduce, prepare derivative works, perform publicly and display # publicly by or for the US Government, including the right to # distribute to other US Government contractors. # function(ga_set_blasroot __blasvendor __blasvar) if("${LINALG_VENDOR}" STREQUAL "${__blasvendor}") set(__ebv_exists FALSE) ga_path_exists(${__blasvar} __bv_exists) if (DEFINED ENV{${__blasvar}}) set(__eblasvar $ENV{${__blasvar}}) ga_path_exists(__eblasvar __ebv_exists) if(__ebv_exists) set(${__blasvar} ${__eblasvar} PARENT_SCOPE) endif() endif() if(NOT __bv_exists AND NOT __ebv_exists) message(FATAL_ERROR "Could not find the following ${__blasvar} path: ${__eblasvar} ${${__blasvar}}") endif() endif() endfunction() set(linalg_lib ) if( "sycl" IN_LIST LINALG_OPTIONAL_COMPONENTS ) set(ENABLE_DPCPP ON) # elseif(ENABLE_DPCPP) # list(APPEND LINALG_OPTIONAL_COMPONENTS "sycl") endif() function(check_ga_blas_options) # ga_is_valid(${LINALG_VENDOR} _lav_set) ga_is_valid(LINALG_PREFIX _lap_set) ga_is_valid(BLAS_PREFIX _lbp_set) ga_is_valid(LAPACK_PREFIX _llp_set) ga_is_valid(ScaLAPACK_PREFIX _lsp_set) if(NOT (_lap_set OR _lbp_set OR _llp_set OR _lsp_set) ) message(FATAL_ERROR "ENABLE_BLAS=ON but the options \ to specify the root of the LinAlg libraries installation \ are not set. Please refer to README.md") endif() endfunction() #Check if provided paths are valid and export if (ENABLE_BLAS) check_ga_blas_options() ga_path_exists(LINALG_PREFIX __la_exists) if(NOT __la_exists) message(FATAL_ERROR "Could not find the following ${LINALG_VENDOR} installation path at: ${LINALG_PREFIX}") endif() endif() if ("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "arm64" AND "${LINALG_VENDOR}" STREQUAL "IntelMKL") message( FATAL_ERROR "IntelMKL is not supported for ARM architectures" ) endif() # check for numerical libraries. These should set variables BLAS_FOUND and # LAPACK_FOUND set(GA_BLAS_ILP64 OFF) if (ENABLE_BLAS) set(BLAS_PREFERENCE_LIST ${LINALG_VENDOR}) set(LAPACK_PREFERENCE_LIST ${LINALG_VENDOR}) set(ScaLAPACK_PREFERENCE_LIST ${LINALG_VENDOR}) set(LINALG_PREFER_STATIC ON) if(BUILD_SHARED_LIBS) set(LINALG_PREFER_STATIC OFF) endif() if(ENABLE_DPCPP) set(LINALG_THREAD_LAYER "sequential") endif() set(${LINALG_VENDOR}_PREFERS_STATIC ${LINALG_PREFER_STATIC}) set(ReferenceLAPACK_PREFERS_STATIC ${LINALG_PREFER_STATIC}) set(ReferenceScaLAPACK_PREFERS_STATIC ${LINALG_PREFER_STATIC}) set(BLAS_SIZE 4) set(${LINALG_VENDOR}_THREAD_LAYER ${LINALG_THREAD_LAYER}) set(BLAS_REQUIRED_COMPONENTS ${LINALG_REQUIRED_COMPONENTS}) set(LAPACK_REQUIRED_COMPONENTS ${LINALG_REQUIRED_COMPONENTS}) set(ScaLAPACK_REQUIRED_COMPONENTS ${LINALG_REQUIRED_COMPONENTS}) set(BLAS_OPTIONAL_COMPONENTS ${LINALG_OPTIONAL_COMPONENTS}) set(LAPACK_OPTIONAL_COMPONENTS ${LINALG_OPTIONAL_COMPONENTS}) set(ScaLAPACK_OPTIONAL_COMPONENTS ${LINALG_OPTIONAL_COMPONENTS}) set(use_openmp ON) set(_blis_essl_set OFF) if(${LINALG_VENDOR} MATCHES "BLIS" OR ${LINALG_VENDOR} MATCHES "IBMESSL") set(_blis_essl_set ON) endif() if("sequential" IN_LIST LINALG_THREAD_LAYER OR _blis_essl_set) set(use_openmp OFF) endif() if(_blis_essl_set OR ${LINALG_VENDOR} MATCHES "OpenBLAS") set(use_openmp OFF) # if(_blis_essl_set) #Assume openblas does not have lapack set(LAPACK_PREFERENCE_LIST ReferenceLAPACK) # endif() if(ENABLE_SCALAPACK) set(ScaLAPACK_PREFERENCE_LIST ReferenceScaLAPACK) endif() endif() if( "ilp64" IN_LIST LINALG_REQUIRED_COMPONENTS ) set(BLAS_SIZE 8) set(GA_BLAS_ILP64 ON) endif() if(NOT BLAS_PREFIX) set(BLAS_PREFIX ${LINALG_PREFIX}) endif() if(NOT LAPACK_PREFIX) set(LAPACK_PREFIX ${LINALG_PREFIX}) endif() if(NOT ScaLAPACK_PREFIX) set(ScaLAPACK_PREFIX ${LINALG_PREFIX}) endif() if(ENABLE_SCALAPACK) find_package(ScaLAPACK) if (ScaLAPACK_FOUND) set(HAVE_SCALAPACK 1) else() message(FATAL_ERROR "ENABLE_SCALAPACK=ON, but a ScaLAPACK library was not found") endif() endif() find_package(LAPACK) if (LAPACK_FOUND) set(HAVE_LAPACK 1) else() message(FATAL_ERROR "ENABLE_BLAS=ON, but a LAPACK library was not found") endif() find_package(BLAS) if (BLAS_FOUND) set(HAVE_BLAS 1) else() message(FATAL_ERROR "ENABLE_BLAS=ON, but a BLAS library was not found") endif() if(ENABLE_DPCPP) set(MKL_INTERFACE lp64) if(GA_BLAS_ILP64) set(MKL_INTERFACE ilp64) endif() find_package(MKL CONFIG REQUIRED PATHS ${LINALG_PREFIX} NO_DEFAULT_PATH) list(APPEND linalg_lib MKL::MKL_SYCL::BLAS) endif() if(ENABLE_CXX) set(BPP_GIT_TAG v2024.10.26) set(LPP_GIT_TAG v2024.10.26) set(SPP_GIT_TAG 6397f52cf11c0dfd82a79698ee198a2fce515d81) if(ENABLE_DEV_MODE) set(BPP_GIT_TAG master) set(LPP_GIT_TAG master) set(SPP_GIT_TAG master) endif() include(FetchContent) set( gpu_backend "none" CACHE STRING "GPU backend to use" FORCE) if(NOT TARGET blaspp) if(ENABLE_OFFLINE_BUILD) FetchContent_Declare( blaspp URL ${DEPS_LOCAL_PATH}/blaspp ) else() #set(BUILD_SHARED_LIBS ON CACHE BOOL "Build SHARED libraries" FORCE) FetchContent_Declare( blaspp GIT_REPOSITORY https://github.com/icl-utk-edu/blaspp.git GIT_TAG ${BPP_GIT_TAG} ) endif() FetchContent_MakeAvailable( blaspp ) endif() if(NOT TARGET lapackpp) if(ENABLE_OFFLINE_BUILD) FetchContent_Declare( lapackpp URL ${DEPS_LOCAL_PATH}/lapackpp ) else() FetchContent_Declare( lapackpp GIT_REPOSITORY https://github.com/icl-utk-edu/lapackpp.git GIT_TAG ${LPP_GIT_TAG} ) endif() FetchContent_MakeAvailable( lapackpp ) endif() if(ENABLE_SCALAPACK) if(NOT TARGET scalapackpp::scalapackpp) if(ENABLE_OFFLINE_BUILD) FetchContent_Declare( scalapackpp URL ${DEPS_LOCAL_PATH}/scalapackpp ) else() FetchContent_Declare( scalapackpp GIT_REPOSITORY https://github.com/wavefunction91/scalapackpp.git GIT_TAG ${SPP_GIT_TAG} ) endif() FetchContent_MakeAvailable( scalapackpp ) endif() endif() set(_la_cxx_blas blaspp) set(_la_cxx_lapack lapackpp) set(_la_cxx_scalapack scalapackpp::scalapackpp) endif() else() set(HAVE_BLAS 0) set(HAVE_LAPACK 0) set(HAVE_SCALAPACK 0) endif() if(ENABLE_DPCPP) set(USE_DPCPP ON) endif() if (ENABLE_SCALAPACK) set(SCALAPACK_I8 OFF) if( "ilp64" IN_LIST LINALG_REQUIRED_COMPONENTS ) set(SCALAPACK_I8 ON) endif() # add_definitions(-DHAVE_SCALAPACK) if (SCALAPACK_I8) add_definitions(-DSCALAPACK_I8) endif() endif() if (ENABLE_EISPACK) add_definitions(-DENABLE_EISPACK) endif() # if (ENABLE_FORTRAN) # add_definitions(-DENABLE_F77) # endif() message(STATUS "HAVE_BLAS: ${HAVE_BLAS}") message(STATUS "HAVE_LAPACK: ${HAVE_LAPACK}") message(STATUS "HAVE_SCALAPACK: ${HAVE_SCALAPACK}") if (HAVE_BLAS) if("${LINALG_VENDOR}" STREQUAL "IntelMKL") set(BLA_VENDOR_MKL ON ) set(BLA_LAPACK_INT "MKL_INT" ) set(BLA_LAPACK_COMPLEX8 "MKL_Complex8" ) set(BLA_LAPACK_COMPLEX16 "MKL_Complex16" ) elseif("${LINALG_VENDOR}" STREQUAL "IBMESSL") set(BLA_VENDOR_ESSL ON) set(BLA_LAPACK_INT "int32_t") if(GA_BLAS_ILP64) set(BLA_LAPACK_INT "int64_t") endif() set(BLA_LAPACK_COMPLEX8 "std::complex") set(BLA_LAPACK_COMPLEX16 "std::complex") elseif("${LINALG_VENDOR}" STREQUAL "BLIS") set(USE_BLIS ON) set(BLA_VENDOR_BLIS ON) set(BLA_LAPACK_INT "int32_t") if(GA_BLAS_ILP64) set(BLA_LAPACK_INT "int64_t") endif() set(BLA_LAPACK_COMPLEX8 "std::complex") set(BLA_LAPACK_COMPLEX16 "std::complex") endif() CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/ga_linalg.h.in ${CMAKE_CURRENT_BINARY_DIR}/ga_linalg.h ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ga_linalg.h DESTINATION include/ga ) list(APPEND linalg_lib BLAS::BLAS ${_la_cxx_blas}) message(STATUS "BLAS_LIBRARIES: ${BLAS_LIBRARIES}") endif() if (HAVE_LAPACK) list(APPEND linalg_lib LAPACK::LAPACK ${_la_cxx_lapack}) message(STATUS "LAPACK_LIBRARIES: ${LAPACK_LIBRARIES}") endif() if (HAVE_SCALAPACK) list(APPEND linalg_lib ScaLAPACK::ScaLAPACK ${_la_cxx_scalapack}) message(STATUS "ScaLAPACK_LIBRARIES: ${ScaLAPACK_LIBRARIES}") endif() ga-5.9.2/cmake/ga-utils.cmake000066400000000000000000000075661500715745200157510ustar00rootroot00000000000000# # module: GlobalArrays.cmake # author: Bruce Palmer # description: Define utility functions. # # DISCLAIMER # # This material was prepared as an account of work sponsored by an # agency of the United States Government. Neither the United States # Government nor the United States Department of Energy, nor Battelle, # nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR # ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, # COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, # SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT # INFRINGE PRIVATELY OWNED RIGHTS. # # # ACKNOWLEDGMENT # # This software and its documentation were produced with United States # Government support under Contract Number DE-AC06-76RLO-1830 awarded by # the United States Department of Energy. The United States Government # retains a paid-up non-exclusive, irrevocable worldwide license to # reproduce, prepare derivative works, perform publicly and display # publicly by or for the US Government, including the right to # distribute to other US Government contractors. # function(ga_is_numeric value) if ("${value}" MATCHES "^[0-9]+$") set(GA_TEST_IS_ARGV2_NUMERIC TRUE PARENT_SCOPE) else() set(GA_TEST_IS_ARGV2_NUMERIC FALSE PARENT_SCOPE) endif() endfunction() # ------------------------------------------------------------- # ga_add_parallel_test # ------------------------------------------------------------- function(ga_add_parallel_test test_name test_srcs) get_filename_component(_test_name_only "${test_name}" NAME) set(GA_TEST_NPROCS 4) if(MPI_PR) set(GA_TEST_NPROCS 5) endif() set(ga_test_ll C) if(DEFINED ARGV2) ga_is_numeric(${ARGV2}) if (GA_TEST_IS_ARGV2_NUMERIC) set(GA_TEST_NPROCS ${ARGV2}) else() set(ga_test_ll Fortran) endif() endif() if(DEFINED ARGV3) set(ga_test_ll Fortran) endif() set(__ga_mpiexec ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${GA_TEST_NPROCS}) if(GA_JOB_LAUNCH_CMD) set(__ga_mpiexec ${GA_JOB_LAUNCH_CMD}) if(GA_JOB_LAUNCH_ARGS) separate_arguments(GA_JOB_LAUNCH_ARGS) set(__ga_mpiexec ${__ga_mpiexec} ${GA_JOB_LAUNCH_ARGS}) else() set(__ga_mpiexec ${__ga_mpiexec} ${MPIEXEC_NUMPROC_FLAG} ${GA_TEST_NPROCS}) endif() endif() separate_arguments(test_srcs) set(__ga_test_exe "${_test_name_only}.x") add_executable (${__ga_test_exe} ${test_srcs}) target_link_libraries(${__ga_test_exe} ga) set_property(TARGET ${__ga_test_exe} PROPERTY LINKER_LANGUAGE ${ga_test_ll}) add_test(NAME ${test_name} COMMAND ${__ga_mpiexec} ${CMAKE_CURRENT_BINARY_DIR}/${__ga_test_exe}) endfunction(ga_add_parallel_test) function(ga_is_valid __variable __out) set(${__out} FALSE PARENT_SCOPE) if(DEFINED ${__variable} AND (NOT "${${__variable}}" STREQUAL "")) set(${__out} TRUE PARENT_SCOPE) endif() endfunction() # # Sets an option's value if the user doesn't supply one. # # Syntax: ga_option # - name: The name of the variable to store the option's value under, # e.g. CMAKE_BUILD_TYPE for the option containing the build's type # - value: The default value to set the variable to, e.g. to default to a # Debug build for the build type set value to Debug # function(ga_option name value) ga_is_valid(${name} was_set) if(was_set) message(STATUS "Value of ${name} was set by user to : ${${name}}") else() set(${name} ${value} PARENT_SCOPE) message(STATUS "Setting value of ${name} to default : ${value}") endif() endfunction() function(ga_path_exists __variable __out) ga_is_valid(${__variable} was_set) set(${__out} FALSE PARENT_SCOPE) if(NOT was_set) return() endif() get_filename_component(_fullpath "${${__variable}}" REALPATH) if(EXISTS ${_fullpath}) set(${__out} TRUE PARENT_SCOPE) endif() endfunction() ga-5.9.2/cmake/ga_linalg.h.in000066400000000000000000000012211500715745200156730ustar00rootroot00000000000000#pragma once #cmakedefine ENABLE_CXX #cmakedefine ENABLE_BLAS #cmakedefine BLA_VENDOR_MKL #cmakedefine BLA_VENDOR_ESSL #cmakedefine BLA_VENDOR_BLIS #cmakedefine USE_BLIS #cmakedefine USE_DPCPP #if defined(ENABLE_CXX) && defined(ENABLE_BLAS) #include "lapack.hh" #else #if defined(ENABLE_BLAS) #define BLA_LAPACK_INT ${BLA_LAPACK_INT} #define BLA_LAPACK_COMPLEX8 ${BLA_LAPACK_COMPLEX8} #define BLA_LAPACK_COMPLEX16 ${BLA_LAPACK_COMPLEX16} #if defined(BLA_VENDOR_MKL) #include "mkl.h" #elif defined(BLA_VENDOR_ESSL) #include "essl.h" #elif defined(BLA_VENDOR_BLIS) #include "blis/cblas.h" #endif #endif //ENABLE_BLAS #endif //ENABLE_CXX/BLAS ga-5.9.2/cmake/globalarrays-config.cmake.in000066400000000000000000000063721500715745200205500ustar00rootroot00000000000000cmake_minimum_required( VERSION 3.17 FATAL_ERROR) @PACKAGE_INIT@ set(ENABLE_BLAS @ENABLE_BLAS@) set(ENABLE_CXX @ENABLE_CXX@) set(ENABLE_FORTRAN @ENABLE_FORTRAN@) set(ENABLE_SCALAPACK @ENABLE_SCALAPACK@) set(BUILD_SHARED_LIBS @BUILD_SHARED_LIBS@) set(GA_MPI_LIBS MPI::MPI_C) if(ENABLE_FORTRAN) enable_language(Fortran) set(GA_MPI_LIBS MPI::MPI_Fortran) endif() if (NOT TARGET Threads::Threads) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) endif() if(ENABLE_BLAS) list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_LIST_DIR}") list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_LIST_DIR}/linalg-cmake-modules") set(LINALG_VENDOR @LINALG_VENDOR@) set(LINALG_PREFIX @LINALG_PREFIX@) set(BLAS_PREFERENCE_LIST @LINALG_VENDOR@) set(LAPACK_PREFERENCE_LIST @LAPACK_PREFERENCE_LIST@) set(ScaLAPACK_PREFERENCE_LIST @ScaLAPACK_PREFERENCE_LIST@) set(@LINALG_VENDOR@_PREFERS_STATIC @LINALG_PREFER_STATIC@) set(ReferenceLAPACK_PREFERS_STATIC @LINALG_PREFER_STATIC@) set(ReferenceScaLAPACK_PREFERS_STATIC @LINALG_PREFER_STATIC@) set(@LINALG_VENDOR@_THREAD_LAYER @LINALG_THREAD_LAYER@) set(BLAS_REQUIRED_COMPONENTS @LINALG_REQUIRED_COMPONENTS@) set(LAPACK_REQUIRED_COMPONENTS @LINALG_REQUIRED_COMPONENTS@) set(ScaLAPACK_REQUIRED_COMPONENTS @LINALG_REQUIRED_COMPONENTS@) set(BLAS_OPTIONAL_COMPONENTS @LINALG_OPTIONAL_COMPONENTS@) set(LAPACK_OPTIONAL_COMPONENTS @LINALG_OPTIONAL_COMPONENTS@) set(ScaLAPACK_OPTIONAL_COMPONENTS @LINALG_OPTIONAL_COMPONENTS@) set(BLAS_PREFIX @BLAS_PREFIX@) set(LAPACK_PREFIX @LAPACK_PREFIX@) set(ENABLE_DPCPP @ENABLE_DPCPP@) set(GA_BLAS_ILP64 @GA_BLAS_ILP64@) if(ENABLE_SCALAPACK) set(ScaLAPACK_PREFIX @ScaLAPACK_PREFIX@) if(NOT TARGET ScaLAPACK::ScaLAPACK) find_package(ScaLAPACK REQUIRED) endif() endif() if(NOT TARGET LAPACK::LAPACK) find_package(LAPACK REQUIRED) endif() if(NOT TARGET BLAS::BLAS) find_package(BLAS REQUIRED) endif() if(ENABLE_DPCPP) if(NOT TARGET MKL::MKL_SYCL::BLAS) set(MKL_INTERFACE lp64) if(GA_BLAS_ILP64) set(MKL_INTERFACE ilp64) endif() find_package(MKL CONFIG REQUIRED PATHS ${LINALG_PREFIX} NO_DEFAULT_PATH) endif() endif() if(ENABLE_CXX) get_filename_component(_ga_la_pp "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE) list(INSERT CMAKE_PREFIX_PATH 0 "${_ga_la_pp}") set(use_openmp @use_openmp@) if(NOT TARGET blaspp) find_package(blaspp REQUIRED) endif() if(NOT TARGET lapackpp) find_package(lapackpp REQUIRED) endif() if(ENABLE_SCALAPACK) if(NOT TARGET scalapackpp::scalapackpp) find_package(scalapackpp REQUIRED) endif() endif() list(REMOVE_AT CMAKE_PREFIX_PATH 0) list(REMOVE_AT CMAKE_MODULE_PATH 0) list(REMOVE_AT CMAKE_MODULE_PATH 0) endif() endif() if(NOT TARGET GlobalArrays::ga) include("${CMAKE_CURRENT_LIST_DIR}/globalarrays-targets.cmake") endif() if(NOT TARGET MPI::MPI_C) find_package(MPI REQUIRED) endif() if(NOT TARGET MPI::MPI_Fortran) find_package(MPI REQUIRED) endif() set(GlobalArrays_FOUND TRUE) set(GlobalArrays_LIBRARIES GlobalArrays::ga) set(GlobalArrays_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@/ga" "${MPI_C_INCLUDE_DIRS}") ga-5.9.2/cmake/linalg-modules/000077500000000000000000000000001500715745200161205ustar00rootroot00000000000000ga-5.9.2/cmake/linalg-modules/FindAccelerate.cmake000066400000000000000000000015751500715745200217630ustar00rootroot00000000000000include( CheckCCompilerFlag ) check_c_compiler_flag( "-framework Accelerate" COMPILER_RECOGNIZES_ACCELERATE ) if( COMPILER_RECOGNIZES_ACCELERATE ) set( Accelerate_LIBRARIES "-framework Accelerate" CACHE STRING "Accelerate Libraries" FORCE) set( Accelerate_lp64_FOUND TRUE ) set( Accelerate_ilp64_FOUND FALSE ) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args( Accelerate REQUIRED_VARS Accelerate_LIBRARIES HANDLE_COMPONENTS ) ga-5.9.2/cmake/linalg-modules/FindBLAS.cmake000066400000000000000000000116621500715745200204520ustar00rootroot00000000000000cmake_minimum_required( VERSION 3.17 ) # Require CMake 3.17+ include( CMakePushCheckState ) include( CheckLibraryExists ) include( CheckSymbolExists ) include( FindPackageHandleStandardArgs ) include( CMakeFindDependencyMacro ) include( ${CMAKE_CURRENT_LIST_DIR}/util/CommonFunctions.cmake ) include( ${CMAKE_CURRENT_LIST_DIR}/util/BLASUtilities.cmake ) include( ${CMAKE_CURRENT_LIST_DIR}/LinAlgModulesMacros.cmake ) # SANITY CHECK: Make sure only one integer interface is requested if( "ilp64" IN_LIST BLAS_FIND_COMPONENTS AND "lp64" IN_LIST BLAS_FIND_COMPONENTS ) message( FATAL_ERROR "BLAS cannot link to both ILP64 and LP64 interfaces" ) endif() # Get list of required / optional components foreach( _comp ${BLAS_FIND_COMPONENTS} ) if( BLAS_FIND_REQUIRED_${_comp} ) list( APPEND BLAS_REQUIRED_COMPONENTS ${_comp} ) else() list( APPEND BLAS_OPTIONAL_COMPONENTS ${_comp} ) endif() endforeach() emulate_kitware_linalg_modules( BLAS ) fill_out_prefix( BLAS ) if( NOT BLAS_PREFERENCE_LIST ) set( BLAS_PREFERENCE_LIST "IntelMKL" "IBMESSL" "BLIS" "OpenBLAS" "ReferenceBLAS" ) if( CMAKE_SYSTEM_NAME MATCHES "Darwin" ) list( PREPEND BLAS_PREFERENCE_LIST "Accelerate" ) endif() endif() if( NOT BLAS_LIBRARIES ) message( STATUS "BLAS_LIBRARIES Not Given: Will Perform Search" ) foreach( blas_type ${BLAS_PREFERENCE_LIST} ) copy_meta_data( BLAS ${blas_type} ) find_package( ${blas_type} COMPONENTS ${BLAS_REQUIRED_COMPONENTS} OPTIONAL_COMPONENTS ${BLAS_OPTIONAL_COMPONENTS} ) if( ${blas_type}_FOUND ) # Propagate Linker / Includes set( BLAS_VENDOR "${blas_type}" ) set( BLAS_LIBRARIES "${${blas_type}_LIBRARIES}" ) set( BLAS_COMPILE_DEFINITIONS "${${blas_type}_COMPILE_DEFINITIONS}" ) set( BLAS_INCLUDE_DIRS "${${blas_type}_INCLUDE_DIR}" ) set( BLAS_COMPILE_OPTIONS "${${blas_type}_COMPILE_OPTIONS}" ) # Generic Components #set( BLAS_headers_FOUND ${${blas_type}_headers_FOUND} ) set( BLAS_sycl_FOUND ${${blas_type}_sycl_FOUND} ) set( BLAS_blacs_FOUND ${${blas_type}_blacs_FOUND} ) set( BLAS_scalapack_FOUND ${${blas_type}_scalapack_FOUND} ) break() # Break from search loop endif() endforeach() else() find_linalg_dependencies( BLAS_LIBRARIES ) endif() # Handle implicit BLAS linkage if( BLAS_LIBRARIES MATCHES "[Ii][Mm][Pp][Ll][Ii][Cc][Ii][Tt]" ) unset( BLAS_LIBRARIES ) endif() # Check if DGEMM exists in proposed BLAS_LIBRARIES check_fortran_functions_exist( dgemm BLAS BLAS_LIBRARIES BLAS_LINK_OK BLAS_Fortran_LOWER BLAS_Fortran_UNDERSCORE ) # If BLAS linkage successful, check if it is ILP64/LP64 if( BLAS_LINK_OK ) set( _dgemm_name "dgemm" ) if( NOT BLAS_Fortran_LOWER ) string( TOUPPER "${_dgemm_name}" _dgemm_name ) endif() if( BLAS_Fortran_UNDERSCORE ) set( _dgemm_name "${_dgemm_name}_" ) endif() check_blas_int( BLAS_LIBRARIES ${_dgemm_name} BLAS_IS_LP64 ) if( BLAS_IS_LP64 ) set( BLAS_lp64_FOUND TRUE ) set( BLAS_ilp64_FOUND FALSE ) else() set( BLAS_lp64_FOUND FALSE ) set( BLAS_ilp64_FOUND TRUE ) find_dependency( ILP64 ) list( APPEND BLAS_COMPILE_OPTIONS "${ILP64_COMPILE_OPTIONS}" ) foreach ( lang C CXX Fortran ) if ( DEFINED ILP64_${lang}_COMPILE_OPTIONS ) list( APPEND BLAS_${lang}_COMPILE_OPTIONS "${ILP64_${lang}_COMPILE_OPTIONS}" ) endif() endforeach() endif() endif() find_package_handle_standard_args( BLAS REQUIRED_VARS BLAS_LINK_OK HANDLE_COMPONENTS ) # Cache variables if( BLAS_FOUND ) set( BLAS_VENDOR "${BLAS_VENDOR}" CACHE STRING "BLAS Vendor" FORCE ) set( BLAS_IS_LP64 "${BLAS_IS_LP64}" CACHE STRING "BLAS LP64 Flag" FORCE ) set( BLAS_LIBRARIES "${BLAS_LIBRARIES}" CACHE STRING "BLAS Libraries" FORCE ) set( BLAS_COMPILE_DEFINITIONS "${BLAS_COMPILE_DEFINITIONS}" CACHE STRING "BLAS Compile Definitions" FORCE ) set( BLAS_INCLUDE_DIRS "${BLAS_INCLUDE_DIRS}" CACHE STRING "BLAS Include Directories" FORCE ) set( BLAS_COMPILE_OPTIONS "${BLAS_COMPILE_OPTIONS}" CACHE STRING "BLAS Compile Options" FORCE ) foreach ( lang C CXX Fortran ) if ( DEFINED BLAS_${lang}_COMPILE_OPTIONS ) set( BLAS_${lang}_COMPILE_OPTIONS "${BLAS_${lang}_COMPILE_OPTIONS}" CACHE STRING "BLAS Compile Options for Language ${lang}" FORCE ) endif() endforeach() endif() if( BLAS_FOUND AND NOT TARGET BLAS::BLAS ) add_library( BLAS::BLAS INTERFACE IMPORTED ) set_target_properties( BLAS::BLAS PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${BLAS_INCLUDE_DIRS}" INTERFACE_COMPILE_OPTIONS "${BLAS_COMPILE_OPTIONS}" INTERFACE_COMPILE_DEFINITIONS "${BLAS_COMPILE_DEFINITIONS}" INTERFACE_LINK_LIBRARIES "${BLAS_LIBRARIES}" ) endif() ga-5.9.2/cmake/linalg-modules/FindBLIS.cmake000066400000000000000000000057371500715745200204700ustar00rootroot00000000000000# SANITY CHECK if( "ilp64" IN_LIST BLIS_FIND_COMPONENTS AND "lp64" IN_LIST BLIS_FIND_COMPONENTS ) message( FATAL_ERROR "BLIS cannot link to both ILP64 and LP64 interfaces" ) endif() if( BLIS_PREFERS_STATIC ) set( BLIS_LIBRARY_NAME "libblis.a" "libblis-mt.a" ) else() set( BLIS_LIBRARY_NAME "blis" "blis-mt") endif() find_library( BLIS_LIBRARIES NAMES ${BLIS_LIBRARY_NAME} HINTS ${BLIS_PREFIX} PATHS ${BLIS_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib lib64 lib32 DOC "BLIS Library" ) find_path( BLIS_INCLUDE_DIR NAMES blis.h blis/blis.h HINTS ${BLIS_PREFIX} PATHS ${BLIS_INCLUDE_DIR} PATH_SUFFIXES include DOC "BLIS header" ) if( BLIS_LIBRARIES ) if( NOT "m" IN_LIST BLIS_LIBRARIES ) list( APPEND BLIS_LIBRARIES "m") endif() endif() # check ILP64 if( BLIS_INCLUDE_DIR ) try_run( BLIS_USES_LP64 BLIS_TEST_COMPILES ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_LIST_DIR}/util/blis_int_size.c CMAKE_FLAGS -DINCLUDE_DIRECTORIES:STRING=${BLIS_INCLUDE_DIR} LINK_LIBRARIES ${BLIS_LIBRARIES} COMPILE_OUTPUT_VARIABLE _blis_idx_compile_output RUN_OUTPUT_VARIABLE _blis_idx_run_output ) if( NOT BLIS_TEST_COMPILES ) if( ${_blis_idx_compile_output} MATCHES "pthread_" ) if (NOT TARGET Threads::Threads) find_dependency(Threads) # Threads::Threads by default is not GLOBAL, so to allow users of LINALG_LIBRARIES to safely use it we need to make it global # more discussion here: https://gitlab.kitware.com/cmake/cmake/-/issues/17256 set_target_properties(Threads::Threads PROPERTIES IMPORTED_GLOBAL TRUE) endif(NOT TARGET Threads::Threads) list( APPEND BLIS_LIBRARIES Threads::Threads ) endif() if( ${_blis_idx_compile_output} MATCHES "omp_" ) find_dependency( OpenMP ) list( APPEND BLIS_LIBRARIES OpenMP::OpenMP_C ) endif() endif() try_run( BLIS_USES_LP64 BLIS_TEST_COMPILES ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_LIST_DIR}/util/blis_int_size.c CMAKE_FLAGS -DINCLUDE_DIRECTORIES:STRING=${BLIS_INCLUDE_DIR} LINK_LIBRARIES ${BLIS_LIBRARIES} COMPILE_OUTPUT_VARIABLE _blis_idx_compile_output RUN_OUTPUT_VARIABLE _blis_idx_run_output ) if( ${BLIS_USES_LP64} EQUAL 0 ) set( BLIS_USES_LP64 TRUE ) else() set( BLIS_USES_LP64 FALSE ) endif() ## Handle components if( BLIS_USES_LP64 ) set( BLIS_ilp64_FOUND FALSE ) set( BLIS_lp64_FOUND TRUE ) else() set( BLIS_ilp64_FOUND TRUE ) set( BLIS_lp64_FOUND FALSE ) endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args( BLIS REQUIRED_VARS BLIS_LIBRARIES BLIS_INCLUDE_DIR HANDLE_COMPONENTS ) #if( BLIS_FOUND AND NOT TARGET BLIS::BLIS ) # # add_library( BLIS::BLIS INTERFACE IMPORTED ) # set_target_properties( BLIS::BLIS PROPERTIES # INTERFACE_INCLUDE_DIRECTORIES "${BLIS_INCLUDE_DIR}" # INTERFACE_LINK_LIBRARIES "${BLIS_LIBRARIES}" # ) # #endif() ga-5.9.2/cmake/linalg-modules/FindFLAME.cmake000066400000000000000000000022531500715745200205510ustar00rootroot00000000000000# SANITY CHECK if( "ilp64" IN_LIST FLAME_FIND_COMPONENTS AND "lp64" IN_LIST FLAME_FIND_COMPONENTS ) message( FATAL_ERROR "FLAME cannot link to both ILP64 and LP64 interfaces" ) endif() if( FLAME_PREFERS_STATIC ) set( FLAME_LIBRARY_NAME "libflame.a" ) else() set( FLAME_LIBRARY_NAME "flame" ) endif() find_library( FLAME_LIBRARIES NAMES ${FLAME_LIBRARY_NAME} HINTS ${FLAME_PREFIX} PATHS ${FLAME_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib lib64 lib32 DOC "FLAME Library" ) find_path( FLAME_INCLUDE_DIR NAMES FLAME.h HINTS ${FLAME_PREFIX} PATHS ${FLAME_INCLUDE_DIR} PATH_SUFFIXES include DOC "FLAME header" ) #XXX Seems that FLAME is only LP64 set( FLAME_lp64_FOUND TRUE ) set( FLAME_ilp64_FOUND FALSE ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( FLAME REQUIRED_VARS FLAME_LIBRARIES FLAME_INCLUDE_DIR HANDLE_COMPONENTS ) #if( FLAME_FOUND AND NOT TARGET FLAME::FLAME ) # # add_library( FLAME::FLAME INTERFACE IMPORTED ) # set_target_properties( FLAME::FLAME PROPERTIES # INTERFACE_INCLUDE_DIRECTORIES "${FLAME_INCLUDE_DIR}" # INTERFACE_LINK_LIBRARIES "${FLAME_LIBRARIES}" # ) # #endif() ga-5.9.2/cmake/linalg-modules/FindIBMESSL.cmake000066400000000000000000000046341500715745200210300ustar00rootroot00000000000000if( "ilp64" IN_LIST IBMESSL_FIND_COMPONENTS AND "lp64" IN_LIST IBMESSL_FIND_COMPONENTS ) message( FATAL_ERROR "IBMESSL cannot link to both ILP64 and LP64 interfaces" ) endif() set( IBMESSL_LP64_SERIAL_LIBRARY_NAME "essl" ) set( IBMESSL_LP64_SMP_LIBRARY_NAME "esslsmp" ) set( IBMESSL_ILP64_SERIAL_LIBRARY_NAME "essl6464" ) set( IBMESSL_ILP64_SMP_LIBRARY_NAME "esslsmp6464" ) if( NOT IBMESSL_THREAD_LAYER ) set( IBMESSL_THREAD_LAYER "smp" ) endif() if( IBMESSL_THREAD_LAYER MATCHES "smp" ) set( IBMESSL_LP64_LIBRARY_NAME ${IBMESSL_LP64_SMP_LIBRARY_NAME} ) set( IBMESSL_ILP64_LIBRARY_NAME ${IBMESSL_ILP64_SMP_LIBRARY_NAME} ) else() set( IBMESSL_LP64_LIBRARY_NAME ${IBMESSL_LP64_SERIAL_LIBRARY_NAME} ) set( IBMESSL_ILP64_LIBRARY_NAME ${IBMESSL_ILP64_SERIAL_LIBRARY_NAME} ) endif() find_path( IBMESSL_INCLUDE_DIR NAMES essl.h HINTS ${IBMESSL_PREFIX} PATHS ${IBMESSL_INCLUDE_DIR} PATH_SUFFIXES include DOC "IBM(R) ESSL header" ) find_library( IBMESSL_LP64_LIBRARIES NAMES ${IBMESSL_LP64_LIBRARY_NAME} HINTS ${IBMESSL_PREFIX} PATHS ${IBMESSL_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib lib64 lib32 DOC "IBM(R) ESSL Library (LP64)" ) find_library( IBMESSL_ILP64_LIBRARIES NAMES ${IBMESSL_ILP64_LIBRARY_NAME} HINTS ${IBMESSL_PREFIX} PATHS ${IBMESSL_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib lib64 lib32 DOC "IBM(R) ESSL Library (ILP64)" ) # Components #if( IBMESSL_INCLUDE_DIR ) # set( IBMESSL_headers_FOUND TRUE ) #else() # set( IBMESSL_headers_FOUND FALSE ) #endif() if( IBMESSL_ILP64_LIBRARIES ) set( IBMESSL_ilp64_FOUND TRUE ) else() set( IBMESSL_ilp64_FOUND FALSE ) endif() if( IBMESSL_LP64_LIBRARIES ) set( IBMESSL_lp64_FOUND TRUE ) else() set( IBMESSL_lp64_FOUND FALSE ) endif() # LP64 Default if( "ilp64" IN_LIST IBMESSL_FIND_COMPONENTS ) set( IBMESSL_LIBRARIES "${IBMESSL_ILP64_LIBRARIES}" ) else() set( IBMESSL_LIBRARIES "${IBMESSL_LP64_LIBRARIES}" ) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args( IBMESSL REQUIRED_VARS IBMESSL_LIBRARIES IBMESSL_INCLUDE_DIR HANDLE_COMPONENTS ) #if( IBMESSL_FOUND AND NOT TARGET IBMESSL::essl ) # # add_library( IBMESSL::essl INTERFACE IMPORTED ) # set_target_properties( IBMESSL::essl PROPERTIES # INTERFACE_INCLUDE_DIRECTORIES "${IBMESSL_INCLUDE_DIR}" # INTERFACE_LINK_LIBRARIES "${IBMESSL_LIBRARIES}" # ) # #endif() ga-5.9.2/cmake/linalg-modules/FindILP64.cmake000066400000000000000000000026071500715745200205260ustar00rootroot00000000000000set( ILP64_FOUND TRUE CACHE BOOL "ILP64 Flags Found" FORCE ) set( ILP64_COMPILE_OPTIONS # Ensure 64-bit executables for GNU C,CXX,Fortran $<$,$>:-m64> # Make default integers 64-bit for Fortran $<$,$>:-i8> $<$,$>:-fdefault-integer-8> ) set( ILP64_COMPILE_OPTIONS "${ILP64_COMPILE_OPTIONS}" CACHE STRING "ILP64 compile options" FORCE ) foreach (lang C CXX Fortran) if ( NOT DEFINED CMAKE_${lang}_COMPILER_ID ) continue() endif() if ( CMAKE_${lang}_COMPILER_ID STREQUAL GNU ) list( APPEND ILP64_${lang}_COMPILE_OPTIONS -m64 ) endif() if ( lang STREQUAL Fortran ) if ( CMAKE_Fortran_COMPILER_ID STREQUAL Intel OR CMAKE_Fortran_COMPILER_ID STREQUAL PGI ) list( APPEND ILP64_${lang}_COMPILE_OPTIONS -i8 ) endif() if ( CMAKE_Fortran_COMPILER_ID STREQUAL GNU OR CMAKE_Fortran_COMPILER_ID STREQUAL Flang OR CMAKE_Fortran_COMPILER_ID STREQUAL LLVMFlang ) list( APPEND ILP64_${lang}_COMPILE_OPTIONS -fdefault-integer-8 ) endif() endif() set( ILP64_${lang}_COMPILE_OPTIONS "${ILP64_${lang}_COMPILE_OPTIONS}" CACHE STRING "ILP64 compile options for language ${lang}" FORCE ) endforeach() ga-5.9.2/cmake/linalg-modules/FindIntelMKL.cmake000066400000000000000000000370561500715745200213550ustar00rootroot00000000000000# FindIntelMKL.cmake # # Finds Intel(R) MKL # # The module will define the following variables: # # IntelMKL_FOUND - Found MKL installation # IntelMKL_INCLUDE_DIR - Location of MKL headers (mkl.h) # IntelMKL_LIBRARIES - MKL libraries # # The find behaviour of the module can be influenced by the following # # IntelMKL_PREFERS_STATIC - default OFF # IntelMKL_THREAD_LAYER - ( sequential, openmp, tbb ) default: openmp # IntelMKL_OMP_LIBRARY - ( Intel, GNU, PGI ) default: depends on compiler include( CMakeFindDependencyMacro ) # SANITY CHECK if( "ilp64" IN_LIST IntelMKL_FIND_COMPONENTS AND "lp64" IN_LIST IntelMKL_FIND_COMPONENTS ) message( FATAL_ERROR "IntelMKL cannot link to both ILP64 and LP64 interfaces" ) endif() if( "scalapack" IN_LIST IntelMKL_FIND_COMPONENTS AND NOT ("blacs" IN_LIST IntelMKL_FIND_COMPONENTS) ) list(APPEND IntelMKL_FIND_COMPONENTS "blacs" ) endif() if ("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "arm64") message( WARNING "IntelMKL is not supported for ARM architectures" ) endif() # MKL lib names if( IntelMKL_PREFERS_STATIC ) set( IntelMKL_LP64_LIBRARY_NAME "libmkl_intel_lp64.a" ) set( IntelMKL_ILP64_LIBRARY_NAME "libmkl_intel_ilp64.a" ) set( IntelMKL_SEQUENTIAL_LIBRARY_NAME "libmkl_sequential.a" ) set( IntelMKL_OMP_INTEL_LIBRARY_NAME "libmkl_intel_thread.a" ) set( IntelMKL_OMP_GNU_LIBRARY_NAME "libmkl_gnu_thread.a" ) set( IntelMKL_OMP_PGI_LIBRARY_NAME "libmkl_pgi_thread.a" ) set( IntelMKL_TBB_LIBRARY_NAME "libmkl_tbb_thread.a" ) set( IntelMKL_CORE_LIBRARY_NAME "libmkl_core.a" ) set( IntelMKL_SYCL_LIBRARY_NAME "libmkl_sycl.a" ) set( IntelMKL_LP64_ScaLAPACK_LIBRARY_NAME "libmkl_scalapack_lp64.a" ) set( IntelMKL_ILP64_ScaLAPACK_LIBRARY_NAME "libmkl_scalapack_ilp64.a" ) set( IntelMKL_LP64_INTELMPI_BLACS_LIBRARY_NAME "libmkl_blacs_intelmpi_lp64.a" ) set( IntelMKL_LP64_OPENMPI_BLACS_LIBRARY_NAME "libmkl_blacs_openmpi_lp64.a" ) set( IntelMKL_LP64_SGIMPT_BLACS_LIBRARY_NAME "libmkl_blacs_sgimpt_lp64.a" ) set( IntelMKL_ILP64_INTELMPI_BLACS_LIBRARY_NAME "libmkl_blacs_intelmpi_ilp64.a" ) set( IntelMKL_ILP64_OPENMPI_BLACS_LIBRARY_NAME "libmkl_blacs_openmpi_ilp64.a" ) set( IntelMKL_ILP64_SGIMPT_BLACS_LIBRARY_NAME "libmkl_blacs_sgimpt_ilp64.a" ) else() set( IntelMKL_LP64_LIBRARY_NAME "mkl_intel_lp64" ) set( IntelMKL_ILP64_LIBRARY_NAME "mkl_intel_ilp64" ) set( IntelMKL_SEQUENTIAL_LIBRARY_NAME "mkl_sequential" ) set( IntelMKL_OMP_INTEL_LIBRARY_NAME "mkl_intel_thread" ) set( IntelMKL_OMP_GNU_LIBRARY_NAME "mkl_gnu_thread" ) set( IntelMKL_OMP_PGI_LIBRARY_NAME "mkl_pgi_thread" ) set( IntelMKL_TBB_LIBRARY_NAME "mkl_tbb_thread" ) set( IntelMKL_CORE_LIBRARY_NAME "mkl_core" ) set( IntelMKL_SYCL_LIBRARY_NAME "mkl_sycl" ) set( IntelMKL_LP64_ScaLAPACK_LIBRARY_NAME "mkl_scalapack_lp64" ) set( IntelMKL_ILP64_ScaLAPACK_LIBRARY_NAME "mkl_scalapack_ilp64" ) set( IntelMKL_LP64_INTELMPI_BLACS_LIBRARY_NAME "mkl_blacs_intelmpi_lp64" ) set( IntelMKL_LP64_OPENMPI_BLACS_LIBRARY_NAME "mkl_blacs_openmpi_lp64" ) set( IntelMKL_LP64_SGIMPT_BLACS_LIBRARY_NAME "mkl_blacs_sgimpt_lp64" ) set( IntelMKL_ILP64_INTELMPI_BLACS_LIBRARY_NAME "mkl_blacs_intelmpi_ilp64" ) set( IntelMKL_ILP64_OPENMPI_BLACS_LIBRARY_NAME "mkl_blacs_openmpi_ilp64" ) set( IntelMKL_ILP64_SGIMPT_BLACS_LIBRARY_NAME "mkl_blacs_sgimpt_ilp64" ) endif() # Defaults if( NOT IntelMKL_THREAD_LAYER ) set( IntelMKL_THREAD_LAYER "openmp" ) endif() if( NOT IntelMKL_MPI_LIBRARY ) set( IntelMKL_MPI_LIBRARY "intelmpi" ) endif() if( NOT IntelMKL_PREFIX ) set( IntelMKL_PREFIX $ENV{MKLROOT} ) endif() # MKL Threads if( IntelMKL_THREAD_LAYER MATCHES "sequential" ) # Sequential set( IntelMKL_THREAD_LIBRARY_NAME ${IntelMKL_SEQUENTIAL_LIBRARY_NAME} ) elseif( IntelMKL_THREAD_LAYER MATCHES "tbb" ) # TBB set( IntelMKL_THREAD_LIBRARY_NAME ${IntelMKL_TBB_LIBRARY_NAME} ) else() # OpenMP if( NOT IntelMKL_OMP_LIBRARY ) include( ${CMAKE_CURRENT_LIST_DIR}/util/IntrospectOpenMP.cmake ) if( NOT TARGET OpenMP::OpenMP_C ) find_dependency( OpenMP ) endif() check_openmp_is_gnu( OpenMP::OpenMP_C OMP_IS_GNU ) if( OMP_IS_GNU ) set( IntelMKL_OMP_LIBRARY "GNU" ) elseif( CMAKE_C_COMPILER_ID MATCHES "Intel" ) set( IntelMKL_OMP_LIBRARY "Intel" ) elseif( CMAKE_C_COMPILER_ID MATCHES "PGI" ) set( IntelMKL_OMP_LIBRARY "PGI" ) else() message( WARNING "OpenMP Could Not Be Introspected -- Defauting to GNU for MKL" ) set( IntelMKL_OMP_LIBRARY "GNU" ) endif() endif() if( IntelMKL_OMP_LIBRARY MATCHES "Intel" ) set( IntelMKL_THREAD_LIBRARY_NAME ${IntelMKL_OMP_INTEL_LIBRARY_NAME} ) elseif( IntelMKL_OMP_LIBRARY MATCHES "PGI" ) set( IntelMKL_THREAD_LIBRARY_NAME ${IntelMKL_OMP_PGI_LIBRARY_NAME} ) else() # Default to GNU OpenMP set( IntelMKL_THREAD_LIBRARY_NAME ${IntelMKL_OMP_GNU_LIBRARY_NAME} ) endif() endif() # MKL MPI for BLACS if( "blacs" IN_LIST IntelMKL_FIND_COMPONENTS ) if( NOT TARGET MPI::MPI_C ) find_dependency( MPI ) endif() if( NOT IntelMPI_MPI_LIBRARY ) include( ${CMAKE_CURRENT_LIST_DIR}/util/IntrospectMPI.cmake ) get_mpi_vendor( MPI::MPI_C MPI_VENDOR ) if( MPI_VENDOR MATCHES "OPENMPI" ) set( IntelMKL_MPI_LIBRARY "openmpi" ) elseif( MPI_VENDOR MATCHES "SGIMPT" ) set( IntelMKL_MPI_LIBRARY "sgimpt" ) else() # Default to MPICH ABI set( IntelMKL_MPI_LIBRARY "mpich" ) endif() endif() if( IntelMKL_MPI_LIBRARY MATCHES "openmpi" ) set( IntelMKL_LP64_BLACS_LIBRARY_NAME ${IntelMKL_LP64_OPENMPI_BLACS_LIBRARY_NAME} ) set( IntelMKL_ILP64_BLACS_LIBRARY_NAME ${IntelMKL_ILP64_OPENMPI_BLACS_LIBRARY_NAME} ) elseif( IntelMKL_MPI_LIBRARY MATCHES "sgimpt" ) set( IntelMKL_LP64_BLACS_LIBRARY_NAME ${IntelMKL_LP64_SGIMPT_BLACS_LIBRARY_NAME} ) set( IntelMKL_ILP64_BLACS_LIBRARY_NAME ${IntelMKL_ILP64_SGIMPT_BLACS_LIBRARY_NAME} ) else() # Default to MPICH ABI set( IntelMKL_LP64_BLACS_LIBRARY_NAME ${IntelMKL_LP64_INTELMPI_BLACS_LIBRARY_NAME} ) set( IntelMKL_ILP64_BLACS_LIBRARY_NAME ${IntelMKL_ILP64_INTELMPI_BLACS_LIBRARY_NAME} ) endif() endif() # Header if( NOT IntelMKL_INCLUDE_DIR ) find_path( IntelMKL_INCLUDE_DIR NAMES mkl.h HINTS ${IntelMKL_PREFIX} PATH_SUFFIXES include DOC "Intel(R) MKL header" ) endif() find_library( IntelMKL_THREAD_LIBRARY NAMES ${IntelMKL_THREAD_LIBRARY_NAME} HINTS ${IntelMKL_PREFIX} PATHS ${IntelMKL_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib/intel64 lib/ia32 DOC "Intel(R) MKL THREAD Library" ) find_library( IntelMKL_CORE_LIBRARY NAMES ${IntelMKL_CORE_LIBRARY_NAME} HINTS ${IntelMKL_PREFIX} PATHS ${IntelMKL_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib/intel64 lib/ia32 DOC "Intel(R) MKL CORE Library" ) # Check version if( EXISTS ${IntelMKL_INCLUDE_DIR}/mkl_version.h ) set( version_pattern "^#define[\t ]+__INTEL_MKL(|_MINOR|_UPDATE)__[\t ]+([0-9\\.]+)$" ) file( STRINGS ${IntelMKL_INCLUDE_DIR}/mkl_version.h mkl_version REGEX ${version_pattern} ) foreach( match ${mkl_version} ) if(IntelMKL_VERSION_STRING) set(IntelMKL_VERSION_STRING "${IntelMKL_VERSION_STRING}.") endif() string(REGEX REPLACE ${version_pattern} "${IntelMKL_VERSION_STRING}\\2" IntelMKL_VERSION_STRING ${match} ) set(IntelMKL_VERSION_${CMAKE_MATCH_1} ${CMAKE_MATCH_2}) endforeach() unset( mkl_version ) unset( version_pattern ) endif() # Handle LP64 / ILP64 find_library( IntelMKL_ILP64_LIBRARY NAMES ${IntelMKL_ILP64_LIBRARY_NAME} HINTS ${IntelMKL_PREFIX} PATHS ${IntelMKL_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib/intel64 lib/ia32 DOC "Intel(R) ILP64 MKL Library" ) find_library( IntelMKL_LP64_LIBRARY NAMES ${IntelMKL_LP64_LIBRARY_NAME} HINTS ${IntelMKL_PREFIX} PATHS ${IntelMKL_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib/intel64 lib/ia32 DOC "Intel(R) LP64 MKL Library" ) if( IntelMKL_ILP64_LIBRARY ) set( IntelMKL_ilp64_FOUND TRUE ) else() set( IntelMKL_ilp64_FOUND FALSE ) endif() if( IntelMKL_LP64_LIBRARY ) set( IntelMKL_lp64_FOUND TRUE ) else() set( IntelMKL_lp64_FOUND FALSE ) endif() # SYCL if( "sycl" IN_LIST IntelMKL_FIND_COMPONENTS ) find_library( IntelMKL_SYCL_LIBRARY NAMES ${IntelMKL_SYCL_LIBRARY_NAME} HINTS ${IntelMKL_PREFIX} PATHS ${IntelMKL_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib/intel64 lib/ia32 DOC "Intel(R) MKL SYCL Library" ) endif() # BLACS / ScaLAPACK find_library( IntelMKL_ILP64_BLACS_LIBRARY NAMES ${IntelMKL_ILP64_BLACS_LIBRARY_NAME} HINTS ${IntelMKL_PREFIX} PATHS ${IntelMKL_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib/intel64 lib/ia32 DOC "Intel(R) ILP64 MKL BLACS Library" ) find_library( IntelMKL_LP64_BLACS_LIBRARY NAMES ${IntelMKL_LP64_BLACS_LIBRARY_NAME} HINTS ${IntelMKL_PREFIX} PATHS ${IntelMKL_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib/intel64 lib/ia32 DOC "Intel(R) LP64 MKL BLACS Library" ) find_library( IntelMKL_ILP64_ScaLAPACK_LIBRARY NAMES ${IntelMKL_ILP64_ScaLAPACK_LIBRARY_NAME} HINTS ${IntelMKL_PREFIX} PATHS ${IntelMKL_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib/intel64 lib/ia32 DOC "Intel(R) ILP64 MKL ScaLAPACK Library" ) find_library( IntelMKL_LP64_ScaLAPACK_LIBRARY NAMES ${IntelMKL_LP64_ScaLAPACK_LIBRARY_NAME} HINTS ${IntelMKL_PREFIX} PATHS ${IntelMKL_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib/intel64 lib/ia32 DOC "Intel(R) LP64 MKL ScaLAPACK Library" ) # Default to LP64 if( "ilp64" IN_LIST IntelMKL_FIND_COMPONENTS ) set( IntelMKL_COMPILE_DEFINITIONS "MKL_ILP64" ) set( IntelMKL_LIBRARY ${IntelMKL_ILP64_LIBRARY} ) if( IntelMKL_ILP64_BLACS_LIBRARY ) set( IntelMKL_BLACS_LIBRARY ${IntelMKL_ILP64_BLACS_LIBRARY} ) set( IntelMKL_blacs_FOUND TRUE ) endif() if( IntelMKL_ILP64_ScaLAPACK_LIBRARY ) set( IntelMKL_ScaLAPACK_LIBRARY ${IntelMKL_ILP64_ScaLAPACK_LIBRARY} ) set( IntelMKL_scalapack_FOUND TRUE ) endif() else() set( IntelMKL_LIBRARY ${IntelMKL_LP64_LIBRARY} ) if( IntelMKL_LP64_BLACS_LIBRARY ) set( IntelMKL_BLACS_LIBRARY ${IntelMKL_LP64_BLACS_LIBRARY} ) set( IntelMKL_blacs_FOUND TRUE ) endif() if( IntelMKL_LP64_ScaLAPACK_LIBRARY ) set( IntelMKL_ScaLAPACK_LIBRARY ${IntelMKL_LP64_ScaLAPACK_LIBRARY} ) set( IntelMKL_scalapack_FOUND TRUE ) endif() endif() if( IntelMKL_SYCL_LIBRARY ) set( IntelMKL_sycl_FOUND TRUE ) endif() # Check if found library is actually static if( IntelMKL_CORE_LIBRARY MATCHES ".+libmkl_core.a" ) set( IntelMKL_PREFERS_STATIC TRUE ) endif() if( IntelMKL_LIBRARY AND IntelMKL_THREAD_LIBRARY AND IntelMKL_CORE_LIBRARY ) set( IntelMKL_BLAS_LAPACK_LIBRARIES ${IntelMKL_LIBRARY} ${IntelMKL_THREAD_LIBRARY} ${IntelMKL_CORE_LIBRARY} ) if( "sycl" IN_LIST IntelMKL_FIND_COMPONENTS ) list( APPEND IntelMKL_BLAS_LAPACK_LIBRARIES ${IntelMKL_SYCL_LIBRARY} ) endif() if( "blacs" IN_LIST IntelMKL_FIND_COMPONENTS ) set( IntelMKL_BLACS_LIBRARIES ${IntelMKL_BLAS_LAPACK_LIBRARIES} ${IntelMKL_BLACS_LIBRARY} ) endif() if( IntelMKL_PREFERS_STATIC ) if(NOT "${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Darwin") list( PREPEND IntelMKL_BLAS_LAPACK_LIBRARIES "-Wl,--start-group" ) list( APPEND IntelMKL_BLAS_LAPACK_LIBRARIES "-Wl,--end-group" ) if( IntelMKL_BLACS_LIBRARIES ) list( PREPEND IntelMKL_BLACS_LIBRARIES "-Wl,--start-group" ) list( APPEND IntelMKL_BLACS_LIBRARIES "-Wl,--end-group" ) endif() endif() if( "scalapack" IN_LIST IntelMKL_FIND_COMPONENTS ) set( IntelMKL_ScaLAPACK_LIBRARIES ${IntelMKL_ScaLAPACK_LIBRARY} ${IntelMKL_BLACS_LIBRARIES} ) endif() else() if( "scalapack" IN_LIST IntelMKL_FIND_COMPONENTS ) set( IntelMKL_ScaLAPACK_LIBRARIES ${IntelMKL_ScaLAPACK_LIBRARY} ${IntelMKL_BLACS_LIBRARIES} ) endif() list( PREPEND IntelMKL_BLAS_LAPACK_LIBRARIES "-Wl,--no-as-needed" ) if( IntelMKL_BLACS_LIBRARIES ) list( PREPEND IntelMKL_BLACS_LIBRARIES "-Wl,--no-as-needed" ) endif() if( IntelMKL_BLACS_LIBRARIES ) list( PREPEND IntelMKL_ScaLAPACK_LIBRARIES "-Wl,--no-as-needed" ) endif() endif() if( IntelMKL_THREAD_LAYER MATCHES "openmp" ) list( APPEND IntelMKL_BLAS_LAPACK_LIBRARIES OpenMP::OpenMP_C ) if( IntelMKL_BLACS_LIBRARIES ) list( APPEND IntelMKL_BLACS_LIBRARIES OpenMP::OpenMP_C ) endif() if( IntelMKL_ScaLAPACK_LIBRARIES ) list( APPEND IntelMKL_ScaLAPACK_LIBRARIES OpenMP::OpenMP_C ) endif() elseif( IntelMKL_THREAD_LAYER MATCHES "tbb" ) if( NOT TARGET TBB::tbb ) find_dependency( TBB REQUIRED ) # TBB::tbb by default is not GLOBAL, so to allow users of LINALG_LIBRARIES to safely use it we need to make it global # more discussion here: https://gitlab.kitware.com/cmake/cmake/-/issues/17256 set_target_properties(TBB::tbb PROPERTIES IMPORTED_GLOBAL TRUE) endif() list( APPEND IntelMKL_BLAS_LAPACK_LIBRARIES TBB::tbb ) if( IntelMKL_BLACS_LIBRARIES ) list( APPEND IntelMKL_BLACS_LIBRARIES TBB::tbb ) endif() if( IntelMKL_ScaLAPACK_LIBRARIES ) list( APPEND IntelMKL_ScaLAPACK_LIBRARIES TBB::tbb ) endif() endif() if( NOT TARGET Threads::Threads ) find_dependency( Threads ) # Threads::Threads by default is not GLOBAL, so to allow users of LINALG_LIBRARIES to safely use it we need to make it global # more discussion here: https://gitlab.kitware.com/cmake/cmake/-/issues/17256 set_target_properties(Threads::Threads PROPERTIES IMPORTED_GLOBAL TRUE) endif() list( APPEND IntelMKL_BLAS_LAPACK_LIBRARIES "m" "dl" Threads::Threads ) if( IntelMKL_BLACS_LIBRARIES ) list( APPEND IntelMKL_BLACS_LIBRARIES "m" "dl" Threads::Threads MPI::MPI_C ) endif() if( IntelMKL_ScaLAPACK_LIBRARIES ) list( APPEND IntelMKL_ScaLAPACK_LIBRARIES "m" "dl" Threads::Threads MPI::MPI_C ) endif() if( IntelMKL_ScaLAPACK_LIBRARIES ) set( IntelMKL_LIBRARIES ${IntelMKL_ScaLAPACK_LIBRARIES} ) elseif( IntelMKL_BLACS_LIBRARIES ) set( IntelMKL_LIBRARIES ${IntelMKL_BLACS_LIBRARIES} ) else() set( IntelMKL_LIBRARIES ${IntelMKL_BLAS_LAPACK_LIBRARIES} ) endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args( IntelMKL REQUIRED_VARS IntelMKL_LIBRARIES IntelMKL_INCLUDE_DIR VERSION_VAR IntelMKL_VERSION_STRING HANDLE_COMPONENTS ) if( IntelMKL_FOUND ) if( IntelMKL_BLAS_LAPACK_LIBRARIES AND NOT TARGET IntelMKL::IntelMKL ) add_library( IntelMKL::IntelMKL INTERFACE IMPORTED ) set_target_properties( IntelMKL::IntelMKL PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${IntelMKL_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES "${IntelMKL_BLAS_LAPACK_LIBRARIES}" ) endif() if( IntelMKL_BLACS_LIBRARIES AND NOT TARGET IntelMKL::BLACS ) add_library( IntelMKL::BLACS INTERFACE IMPORTED ) set_target_properties( IntelMKL::BLACS PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${IntelMKL_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES "${IntelMKL_BLACS_LIBRARIES}" ) endif() if( IntelMKL_ScaLAPACK_LIBRARIES AND NOT TARGET IntelMKL::ScaLAPACK ) add_library( IntelMKL::ScaLAPACK INTERFACE IMPORTED ) set_target_properties( IntelMKL::ScaLAPACK PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${IntelMKL_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES "${IntelMKL_ScaLAPACK_LIBRARIES}" ) endif() endif() ga-5.9.2/cmake/linalg-modules/FindLAPACK.cmake000066400000000000000000000156021500715745200206620ustar00rootroot00000000000000cmake_minimum_required( VERSION 3.17 ) # Require CMake 3.17+ include( CMakePushCheckState ) include( CheckLibraryExists ) include( CheckSymbolExists ) include( CMakeFindDependencyMacro ) include( FindPackageHandleStandardArgs ) include( ${CMAKE_CURRENT_LIST_DIR}/util/CommonFunctions.cmake ) include( ${CMAKE_CURRENT_LIST_DIR}/util/LAPACKUtilities.cmake ) include( ${CMAKE_CURRENT_LIST_DIR}/LinAlgModulesMacros.cmake ) # SANITY CHECK if( "ilp64" IN_LIST LAPACK_FIND_COMPONENTS AND "lp64" IN_LIST LAPACK_FIND_COMPONENTS ) message( FATAL_ERROR "LAPACK cannot link to both ILP64 and LP64 interfaces" ) endif() # Get list of required / optional components foreach( _comp ${LAPACK_FIND_COMPONENTS} ) if( LAPACK_FIND_REQUIRED_${_comp} ) list( APPEND LAPACK_REQUIRED_COMPONENTS ${_comp} ) else() list( APPEND LAPACK_OPTIONAL_COMPONENTS ${_comp} ) endif() endforeach() emulate_kitware_linalg_modules( LAPACK ) fill_out_prefix( LAPACK ) if( NOT LAPACK_PREFERENCE_LIST ) set( LAPACK_PREFERENCE_LIST "ReferenceLAPACK" "FLAME" ) endif() if( NOT LAPACK_LIBRARIES ) # Find BLAS if( NOT TARGET BLAS::BLAS ) copy_meta_data( LAPACK BLAS ) find_dependency( BLAS COMPONENTS ${LAPACK_REQUIRED_COMPONENTS} OPTIONAL_COMPONENTS ${LAPACK_OPTIONAL_COMPONENTS} ) endif() # Check if BLAS contains a LAPACK linker message( STATUS "LAPACK_LIBRARIES Not Given: Checking for LAPACK in BLAS" ) set( LAPACK_LIBRARIES ${BLAS_LIBRARIES} ) set( LAPACK_INCLUDE_DIRS ${BLAS_INCLUDE_DIRS} ) set( LAPACK_COMPILE_DEFINITIONS ${BLAS_COMPILE_DEFINITIONS} ) # use dpstrf to check for full LAPACK API ... some implementations are incomplete (e.g. older OpenBLAS) # also need to handle several corner cases: # - OpenBLAS needs libgfortran only for some functions, dpstrf is not one of them, so check for dgesvd check_fortran_functions_exist( "dpstrf;dgesvd" LAPACK LAPACK_LIBRARIES BLAS_HAS_LAPACK LAPACK_Fortran_LOWER LAPACK_Fortran_UNDERSCORE ) # If BLAS has a full LAPACK Linker, propagate vars if( BLAS_HAS_LAPACK ) message( STATUS "BLAS Has A Full LAPACK Linker" ) set( LAPACK_VENDOR ${BLAS_VENDOR} ) set( LAPACK_IS_LP64 ${BLAS_IS_LP64} ) set( LAPACK_blacs_FOUND ${BLAS_blacs_FOUND} ) set( LAPACK_scalapack_FOUND ${BLAS_scalapack_FOUND} ) set( LAPACK_sycl_FOUND ${BLAS_sycl_FOUND} ) # Else find LAPACK installation consistent with BLAS else( BLAS_HAS_LAPACK ) # Ensure proper integer size if( BLAS_IS_LP64 AND (NOT "lp64" IN_LIST LAPACK_REQUIRED_COMPONENTS) ) list( APPEND LAPACK_REQUIRED_COMPONENTS "lp64" ) elseif( (NOT BLAS_IS_LP64) AND (NOT "ilp64" IN_LIST LAPACK_REQUIRED_COMPONENTS ) ) list( APPEND LAPACK_REQUIRED_COMPONENTS "ilp64" ) endif() message( STATUS "BLAS Does Not Have A Full LAPACK Linker -- Performing Search" ) foreach( lapack_type ${LAPACK_PREFERENCE_LIST} ) copy_meta_data( LAPACK ${lapack_type} ) find_package( ${lapack_type} COMPONENTS ${LAPACK_REQUIRED_COMPONENTS} OPTIONAL_COMPONENTS ${LAPACK_OPTIONAL_COMPONENTS} ) if( ${lapack_type}_FOUND ) # Propagate Linker / Includes set( LAPACK_VENDOR "${lapack_type}" ) list( PREPEND LAPACK_LIBRARIES ${${lapack_type}_LIBRARIES} ) list( PREPEND LAPACK_COMPILE_DEFINITIONS ${${lapack_type}_COMPILE_DEFINITIONS} ) list( PREPEND LAPACK_INCLUDE_DIR ${${lapack_type}_INCLUDE_DIR} ) # Generic Components #set( LAPACK_headers_FOUND ${${lapack_type}_headers_FOUND} ) set( LAPACK_blacs_FOUND ${${lapack_type}_blacs_FOUND} ) set( LAPACK_scalapack_FOUND ${${lapack_type}_scalapack_FOUND} ) set( LAPACK_sycl_FOUND ${${lapack_type}_sycl_FOUND} ) break() # Break from search loop endif() endforeach() endif( BLAS_HAS_LAPACK ) else() find_linalg_dependencies( LAPACK_LIBRARIES ) endif() # Handle implicit LAPACK linkage if( LAPACK_LIBRARIES MATCHES "[Ii][Mm][Pp][Ll][Ii][Cc][Ii][Tt]" ) unset( LAPACK_LIBRARIES ) endif() # Check for LAPACK Linker if( BLAS_HAS_LAPACK ) set( LAPACK_LINK_OK TRUE ) else() # see notes above the first invocation of check_fortran_functions_exist check_fortran_functions_exist( "dpstrf;dgesvd" LAPACK LAPACK_LIBRARIES LAPACK_LINK_OK LAPACK_Fortran_LOWER LAPACK_Fortran_UNDERSCORE ) endif() # If LAPACK linkage successful, check if it is ILP64/LP64 if( LAPACK_LINK_OK ) set( _dsyev_name "dsyev" ) if( NOT LAPACK_Fortran_LOWER ) string( TOUPPER "${_dsyev_name}" _dsyev_name ) endif() if( LAPACK_Fortran_UNDERSCORE ) set( _dsyev_name "${_dsyev_name}_" ) endif() check_lapack_int( LAPACK_LIBRARIES ${_dsyev_name} LAPACK_IS_LP64 ) if( LAPACK_IS_LP64 ) set( LAPACK_lp64_FOUND TRUE ) set( LAPACK_ilp64_FOUND FALSE ) else() set( LAPACK_lp64_FOUND FALSE ) set( LAPACK_ilp64_FOUND TRUE ) find_dependency( ILP64 ) list( APPEND LAPACK_COMPILE_OPTIONS "${ILP64_COMPILE_OPTIONS}" ) foreach ( lang C CXX Fortran ) if ( DEFINED ILP64_${lang}_COMPILE_OPTIONS ) list( APPEND LAPACK_${lang}_COMPILE_OPTIONS "${ILP64_${lang}_COMPILE_OPTIONS}" ) endif() endforeach() endif() else() # Unset everything for safety unset( LAPACK_LIBRARIES ) unset( LAPACK_COMPILE_DEFINITIONS ) endif() find_package_handle_standard_args( LAPACK REQUIRED_VARS LAPACK_LINK_OK HANDLE_COMPONENTS ) # Cache variables if( LAPACK_FOUND ) set( LAPACK_VENDOR "${LAPACK_VENDOR}" CACHE STRING "LAPACK Vendor" FORCE ) set( LAPACK_IS_LP64 "${LAPACK_IS_LP64}" CACHE STRING "LAPACK LP64 Flag" FORCE ) set( LAPACK_LIBRARIES "${LAPACK_LIBRARIES}" CACHE STRING "LAPACK Libraries" FORCE ) set( LAPACK_COMPILE_DEFINITIONS "${LAPACK_COMPILE_DEFINITIONS}" CACHE STRING "LAPACK Compile Definitions" FORCE ) set( LAPACK_INCLUDE_DIRS "${LAPACK_INCLUDE_DIRS}" CACHE STRING "LAPACK Include Directories" FORCE ) set( LAPACK_COMPILE_OPTIONS "${LAPACK_COMPILE_OPTIONS}" CACHE STRING "LAPACK Compile Options" FORCE ) foreach ( lang C CXX Fortran ) if ( DEFINED LAPACK_${lang}_COMPILE_OPTIONS ) set( LAPACK_${lang}_COMPILE_OPTIONS "${LAPACK_${lang}_COMPILE_OPTIONS}" CACHE STRING "LAPACK Compile Options for Language ${lang}" FORCE ) endif() endforeach() endif() if( LAPACK_FOUND AND NOT TARGET LAPACK::LAPACK ) add_library( LAPACK::LAPACK INTERFACE IMPORTED ) set_target_properties( LAPACK::LAPACK PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LAPACK_INCLUDE_DIRS}" INTERFACE_COMPILE_OPTIONS "${LAPACK_COMPILE_OPTIONS}" INTERFACE_COMPILE_DEFINITIONS "${LAPACK_COMPILE_DEFINITIONS}" INTERFACE_LINK_LIBRARIES "${LAPACK_LIBRARIES}" ) endif() ga-5.9.2/cmake/linalg-modules/FindOpenBLAS.cmake000066400000000000000000000044561500715745200212770ustar00rootroot00000000000000# SANITY CHECK if( "ilp64" IN_LIST OpenBLAS_FIND_COMPONENTS AND "lp64" IN_LIST OpenBLAS_FIND_COMPONENTS ) message( FATAL_ERROR "OpenBLAS cannot link to both ILP64 and LP64 interfaces" ) endif() if( OpenBLAS_PREFERS_STATIC ) set( OpenBLAS_LP64_LIBRARY_NAME "libopenblas.a" ) set( OpenBLAS_ILP64_LIBRARY_NAME "libopenblas_64.a" ) else() set( OpenBLAS_LP64_LIBRARY_NAME "openblas" ) set( OpenBLAS_ILP64_LIBRARY_NAME "openblas_64" ) endif() find_library( OpenBLAS_LIBRARIES NAMES ${OpenBLAS_LP64_LIBRARY_NAME} ${OpenBLAS_ILP64_LIBRARY_NAME} HINTS ${OpenBLAS_PREFIX} PATHS ${OpenBLAS_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib lib64 lib32 DOC "OpenBLAS Library" ) find_path( OpenBLAS_INCLUDE_DIR NAMES openblas_config.h HINTS ${OpenBLAS_PREFIX} PATHS ${OpenBLAS_INCLUDE_DIR} PATH_SUFFIXES include include/openblas include/openblas64 DOC "OpenBLAS header" ) #if( OpenBLAS_LIBRARY AND OpenBLAS_PREFERS_STATIC ) # include( CMakeFindDependency ) # find_package( Threads QUIET ) # set( OpenBLAS_LIBRARIES ${OpenBLAS_LIBRARY} Threads::Threads "m") #endif() # check ILP64 if( OpenBLAS_INCLUDE_DIR ) try_run( OpenBLAS_USES_LP64 _openblas_idx_test_compile_result ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_LIST_DIR}/util/openblas_int_size.c CMAKE_FLAGS -DINCLUDE_DIRECTORIES:STRING=${OpenBLAS_INCLUDE_DIR} COMPILE_OUTPUT_VARIABLE _openblas_idx_compile_output RUN_OUTPUT_VARIABLE _openblas_idx_run_output ) if( ${OpenBLAS_USES_LP64} EQUAL 0 ) set( OpenBLAS_USES_LP64 TRUE ) else() set( OpenBLAS_USES_LP64 FALSE ) endif() ## Handle components if( OpenBLAS_USES_LP64 ) set( OpenBLAS_ilp64_FOUND FALSE ) set( OpenBLAS_lp64_FOUND TRUE ) else() set( OpenBLAS_ilp64_FOUND TRUE ) set( OpenBLAS_lp64_FOUND FALSE ) endif() endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args( OpenBLAS REQUIRED_VARS OpenBLAS_LIBRARIES OpenBLAS_INCLUDE_DIR HANDLE_COMPONENTS ) #if( OpenBLAS_FOUND AND NOT TARGET OpenBLAS::OpenBLAS ) # # add_library( OpenBLAS::OpenBLAS INTERFACE IMPORTED ) # set_target_properties( OpenBLAS::OpenBLAS PROPERTIES # INTERFACE_INCLUDE_DIRECTORIES "${OpenBLAS_INCLUDE_DIR}" # INTERFACE_LINK_LIBRARIES "${OpenBLAS_LIBRARIES}" # ) # #endif() ga-5.9.2/cmake/linalg-modules/FindReferenceBLAS.cmake000066400000000000000000000036011500715745200222630ustar00rootroot00000000000000# SANITY CHECK if( "ilp64" IN_LIST ReferenceBLAS_FIND_COMPONENTS AND "lp64" IN_LIST ReferenceBLAS_FIND_COMPONENTS ) message( FATAL_ERROR "ReferenceBLAS cannot link to both ILP64 and LP64 interfaces" ) endif() if( ReferenceBLAS_PREFERS_STATIC ) set( ReferenceBLAS_LP64_LIBRARY_NAME "libblas.a" ) set( ReferenceBLAS_ILP64_LIBRARY_NAME "libblas64.a" ) else() set( ReferenceBLAS_LP64_LIBRARY_NAME "blas" ) set( ReferenceBLAS_ILP64_LIBRARY_NAME "blas64" ) endif() find_library( ReferenceBLAS_LP64_LIBRARIES NAMES ${ReferenceBLAS_LP64_LIBRARY_NAME} HINTS ${ReferenceBLAS_PREFIX} PATHS ${ReferenceBLAS_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib lib64 lib32 DOC "ReferenceBLAS LP64 Library" ) if( ReferenceBLAS_LP64_LIBRARIES ) set( ReferenceBLAS_lp64_FOUND TRUE ) else() set( ReferenceBLAS_lp64_FOUND FALSE ) endif() find_library( ReferenceBLAS_ILP64_LIBRARIES NAMES ${ReferenceBLAS_ILP64_LIBRARY_NAME} HINTS ${ReferenceBLAS_PREFIX} PATHS ${ReferenceBLAS_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib lib64 lib32 DOC "ReferenceBLAS ILP64 Library" ) if( ReferenceBLAS_ILP64_LIBRARIES ) set( ReferenceBLAS_ilp64_FOUND TRUE ) else() set( ReferenceBLAS_ilp64_FOUND FALSE ) endif() # Default to LP64 if( "ilp64" IN_LIST ReferenceBLAS_FIND_COMPONENTS ) set( ReferenceBLAS_LIBRARIES ${ReferenceBLAS_ILP64_LIBRARIES} ) else() set( ReferenceBLAS_LIBRARIES ${ReferenceBLAS_LP64_LIBRARIES} ) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args( ReferenceBLAS REQUIRED_VARS ReferenceBLAS_LIBRARIES HANDLE_COMPONENTS ) #if( ReferenceBLAS_FOUND AND NOT TARGET ReferenceBLAS::ReferenceBLAS ) # # add_library( ReferenceBLAS::ReferenceBLAS INTERFACE IMPORTED ) # set_target_properties( ReferenceBLAS::ReferenceBLAS PROPERTIES # INTERFACE_LINK_LIBRARIES "${ReferenceBLAS_LIBRARIES}" # ) # #endif() ga-5.9.2/cmake/linalg-modules/FindReferenceLAPACK.cmake000066400000000000000000000037261500715745200225050ustar00rootroot00000000000000# SANITY CHECK if( "ilp64" IN_LIST ReferenceLAPACK_FIND_COMPONENTS AND "lp64" IN_LIST ReferenceLAPACK_FIND_COMPONENTS ) message( FATAL_ERROR "ReferenceLAPACK cannot link to both ILP64 and LP64 interfaces" ) endif() if( ReferenceLAPACK_PREFERS_STATIC ) set( ReferenceLAPACK_LP64_LIBRARY_NAME "liblapack.a" ) set( ReferenceLAPACK_ILP64_LIBRARY_NAME "liblapack64.a" ) else() set( ReferenceLAPACK_LP64_LIBRARY_NAME "lapack" ) set( ReferenceLAPACK_ILP64_LIBRARY_NAME "lapack64" ) endif() find_library( ReferenceLAPACK_LP64_LIBRARIES NAMES ${ReferenceLAPACK_LP64_LIBRARY_NAME} HINTS ${ReferenceLAPACK_PREFIX} PATHS ${ReferenceLAPACK_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib lib64 lib32 DOC "ReferenceLAPACK LP64 Library" ) if( ReferenceLAPACK_LP64_LIBRARIES ) set( ReferenceLAPACK_lp64_FOUND TRUE ) else() set( ReferenceLAPACK_lp64_FOUND FALSE ) endif() find_library( ReferenceLAPACK_ILP64_LIBRARIES NAMES ${ReferenceLAPACK_ILP64_LIBRARY_NAME} HINTS ${ReferenceLAPACK_PREFIX} PATHS ${ReferenceLAPACK_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib lib64 lib32 DOC "ReferenceLAPACK ILP64 Library" ) if( ReferenceLAPACK_ILP64_LIBRARIES ) set( ReferenceLAPACK_ilp64_FOUND TRUE ) else() set( ReferenceLAPACK_ilp64_FOUND FALSE ) endif() # Default to LP64 if( "ilp64" IN_LIST ReferenceLAPACK_FIND_COMPONENTS ) set( ReferenceLAPACK_LIBRARIES ${ReferenceLAPACK_ILP64_LIBRARIES} ) else() set( ReferenceLAPACK_LIBRARIES ${ReferenceLAPACK_LP64_LIBRARIES} ) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args( ReferenceLAPACK REQUIRED_VARS ReferenceLAPACK_LIBRARIES HANDLE_COMPONENTS ) #if( ReferenceLAPACK_FOUND AND NOT TARGET ReferenceLAPACK::ReferenceLAPACK ) # # add_library( ReferenceLAPACK::ReferenceLAPACK INTERFACE IMPORTED ) # set_target_properties( ReferenceLAPACK::ReferenceLAPACK PROPERTIES # INTERFACE_LINK_LIBRARIES "${ReferenceLAPACK_LIBRARIES}" # ) # #endif() ga-5.9.2/cmake/linalg-modules/FindReferenceScaLAPACK.cmake000066400000000000000000000046101500715745200231250ustar00rootroot00000000000000# SANITY CHECK if( "ilp64" IN_LIST ReferenceScaLAPACK_FIND_COMPONENTS AND "lp64" IN_LIST ReferenceScaLAPACK_FIND_COMPONENTS ) message( FATAL_ERROR "ReferenceScaLAPACK cannot link to both ILP64 and LP64 interfaces" ) endif() if( "ilp64" IN_LIST ReferenceScaLAPACK_FIND_COMPONENTS ) message( FATAL_ERROR "ReferenceScaLAPACK ILP64 interface discovery is currently not supported" ) endif() if( NOT TARGET MPI::MPI_C ) enable_language(C) find_dependency( MPI ) endif() if( ReferenceScaLAPACK_PREFERS_STATIC ) set( ReferenceScaLAPACK_LP64_LIBRARY_NAME "libscalapack.a" ) set( ReferenceScaLAPACK_ILP64_LIBRARY_NAME "libscalapack64.a" ) else() set( ReferenceScaLAPACK_LP64_LIBRARY_NAME "scalapack" ) set( ReferenceScaLAPACK_ILP64_LIBRARY_NAME "scalapack64" ) endif() find_library( ReferenceScaLAPACK_LP64_LIBRARIES NAMES ${ReferenceScaLAPACK_LP64_LIBRARY_NAME} HINTS ${ReferenceScaLAPACK_PREFIX} PATHS ${ReferenceScaLAPACK_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib lib64 lib32 DOC "ReferenceScaLAPACK LP64 Library" ) if( ReferenceScaLAPACK_LP64_LIBRARIES ) set( ReferenceScaLAPACK_lp64_FOUND TRUE ) else() set( ReferenceScaLAPACK_lp64_FOUND FALSE ) endif() find_library( ReferenceScaLAPACK_ILP64_LIBRARIES NAMES ${ReferenceScaLAPACK_ILP64_LIBRARY_NAME} HINTS ${ReferenceScaLAPACK_PREFIX} PATHS ${ReferenceScaLAPACK_LIBRARY_DIR} ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES} PATH_SUFFIXES lib lib64 lib32 DOC "ReferenceScaLAPACK ILP64 Library" ) if( ReferenceScaLAPACK_ILP64_LIBRARIES ) set( ReferenceScaLAPACK_ilp64_FOUND TRUE ) else() set( ReferenceScaLAPACK_ilp64_FOUND FALSE ) endif() # Default to LP64 if( "ilp64" IN_LIST ReferenceScaLAPACK_FIND_COMPONENTS ) set( ReferenceScaLAPACK_LIBRARIES ${ReferenceScaLAPACK_ILP64_LIBRARIES} ) else() set( ReferenceScaLAPACK_LIBRARIES ${ReferenceScaLAPACK_LP64_LIBRARIES} ) endif() list( APPEND ReferenceScaLAPACK_LIBRARIES MPI::MPI_C ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( ReferenceScaLAPACK REQUIRED_VARS ReferenceScaLAPACK_LIBRARIES HANDLE_COMPONENTS ) #if( ReferenceScaLAPACK_FOUND AND NOT TARGET ReferenceScaLAPACK::ReferenceScaLAPACK ) # # add_library( ReferenceScaLAPACK::ReferenceScaLAPACK INTERFACE IMPORTED ) # set_target_properties( ReferenceScaLAPACK::ReferenceScaLAPACK PROPERTIES # INTERFACE_LINK_LIBRARIES "${ReferenceScaLAPACK_LIBRARIES}" # ) # #endif() ga-5.9.2/cmake/linalg-modules/FindScaLAPACK.cmake000066400000000000000000000145521500715745200213140ustar00rootroot00000000000000# FindScaLAPACK.cmake # # Finds the ScaLAPACK library. # # This module will define the following variables: # # ScaLAPACK_FOUND - System has found ScaLAPACK installation # ScaLAPACK_LIBRARIES - ScaLAPACK libraries # # This module will export the following targets if SCALAPACK_FOUND # # ScaLAPACK::ScaLAPACK # # Proper usage: # # project( TEST_FIND_SCALAPACK C ) # find_package( ScaLAPACK ) # # if( ScaLAPACK_FOUND ) # add_executable( test test.cxx ) # target_link_libraries( test ScaLAPACK::ScaLAPACK ) # endif() # # # This module will use the following variables to change # default behaviour if set # # ScaLAPACK_PREFIX # ScaLAPACK_LIBRARY_DIR # ScaLAPACK_LIBRARIES cmake_minimum_required( VERSION 3.17 ) # Require CMake 3.17+ include( CMakePushCheckState ) include( CheckLibraryExists ) include( CheckSymbolExists ) include( FindPackageHandleStandardArgs ) include( CMakeFindDependencyMacro ) include( ${CMAKE_CURRENT_LIST_DIR}/util/CommonFunctions.cmake ) include( ${CMAKE_CURRENT_LIST_DIR}/util/ScaLAPACKUtilities.cmake ) include( ${CMAKE_CURRENT_LIST_DIR}/LinAlgModulesMacros.cmake ) # SANITY CHECK if( "ilp64" IN_LIST ScaLAPACK_FIND_COMPONENTS AND "lp64" IN_LIST ScaLAPACK_FIND_COMPONENTS ) message( FATAL_ERROR "ScaLAPACK cannot link to both ILP64 and LP64 interfaces" ) endif() # Get list of required / optional components foreach( _comp ${ScaLAPACK_FIND_COMPONENTS} ) if( ScaLAPACK_FIND_REQUIRED_${_comp} ) list( APPEND ScaLAPACK_REQUIRED_COMPONENTS ${_comp} ) else() list( APPEND ScaLAPACK_OPTIONAL_COMPONENTS ${_comp} ) endif() endforeach() emulate_kitware_linalg_modules( ScaLAPACK ) fill_out_prefix( ScaLAPACK ) if( NOT ScaLAPACK_PREFERENCE_LIST ) set( ScaLAPACK_PREFERENCE_LIST "ReferenceScaLAPACK" ) endif() if( NOT ScaLAPACK_LIBRARIES ) # Find LAPACK if( NOT TARGET LAPACK::LAPACK ) copy_meta_data( ScaLAPACK LAPACK ) find_dependency( LAPACK COMPONENTS ${ScaLAPACK_REQUIRED_COMPONENTS} OPTIONAL_COMPONENTS ${ScaLAPACK_OPTIONAL_COMPONENTS} scalapack blacs ) endif() # Check if LAPACK contains ScaLAPACK linker (e.g. MKL) message( STATUS "ScaLAPACK_LIBRARIES Not Given: Checking for ScaLAPACK in LAPACK" ) set( ScaLAPACK_LIBRARIES ${LAPACK_LIBRARIES} ) set( ScaLAPACK_INCLUDE_DIR ${LAPACK_INCLUDE_DIR} ) set( ScaLAPACK_COMPILE_DEFINITIONS ${LAPACK_COMPILE_DEFINITIONS} ) check_pdpotrf_exists( ScaLAPACK_LIBRARIES LAPACK_HAS_ScaLAPACK ScaLAPACK_Fortran_LOWER ScaLAPACK_Fortran_UNDERSCORE ) # If LAPACK has a full ScaLAPACK Linker, propagate vars if( LAPACK_HAS_ScaLAPACK ) message( STATUS "LAPACK Has A Full ScaLAPACK Linker" ) set( ScaLAPACK_VENDOR ${LAPACK_VENDOR} ) set( ScaLAPACK_IS_LP64 ${LAPACK_IS_LP64} ) # Else find ScaLAPACK installation consistent with LAPACK else( LAPACK_HAS_ScaLAPACK ) # Ensure proper integer size if( LAPACK_IS_LP64 AND (NOT "lp64" IN_LIST ScaLAPACK_REQUIRED_COMPONENTS) ) list( APPEND ScaLAPACK_REQUIRED_COMPONENTS "lp64" ) elseif( (NOT LAPACK_IS_LP64) AND (NOT "ilp64" IN_LIST ScaLAPACK_REQUIRED_COMPONENTS ) ) list( APPEND ScaLAPACK_REQUIRED_COMPONENTS "ilp64" ) endif() message( STATUS "LAPACK Does Not Have A Full ScaLAPACK Linker -- Performing Search" ) foreach( scalapack_type ${ScaLAPACK_PREFERENCE_LIST} ) copy_meta_data( ScaLAPACK ${scalapack_type} ) find_package( ${scalapack_type} COMPONENTS ${ScaLAPACK_REQUIRED_COMPONENTS} OPTIONAL_COMPONENTS ${ScaLAPACK_OPTIONAL_COMPONENTS} ) if( ${scalapack_type}_FOUND ) # Propagate Linker / Includes set( ScaLAPACK_VENDOR "${scalapack_type}" ) list( PREPEND ScaLAPACK_LIBRARIES ${${scalapack_type}_LIBRARIES} ) list( PREPEND ScaLAPACK_COMPILE_DEFINITIONS ${${scalapack_type}_COMPILE_DEFINITIONS} ) list( PREPEND ScaLAPACK_INCLUDE_DIR ${${scalapack_type}_INCLUDE_DIR} ) break() # Break from search loop endif() endforeach() endif( LAPACK_HAS_ScaLAPACK ) else() find_linalg_dependencies( ScaLAPACK_LIBRARIES ) endif() # Handle implicit LAPACK linkage if( ScaLAPACK_LIBRARIES MATCHES "[Ii][Mm][Pp][Ll][Ii][Cc][Ii][Tt]" ) unset( ScaLAPACK_LIBRARIES ) endif() # Check for ScaLAPACK Linker if( LAPACK_HAS_ScaLAPACK ) set( ScaLAPACK_LINK_OK TRUE ) else() check_pdpotrf_exists( ScaLAPACK_LIBRARIES ScaLAPACK_LINK_OK ScaLAPACK_Fortran_LOWER ScaLAPACK_Fortran_UNDERSCORE ) endif() # If ScaLAPACK linkage sucessful, check if it is ILP64/LP64 if( ScaLAPACK_LINK_OK ) # TODO: This requires running an MPI program, pretty dangerous #set( _pdpotrf_name "pdpotrf" ) #if( NOT ScaLAPACK_Fortran_LOWER ) # string( TOUPPER "${_pdpotrf_name}" _pdpotrf_name ) #endif() #if( ScaLAPACK_Fortran_UNDERSCORE ) # set( _pdpotrf_name "${_pdpotrf_name}_" ) #endif() #check_scalapack_int( ScaLAPACK_LIBRARIES ${_pdpotrf_name} ScaLAPACK_IS_LP64 ) # XXX: Unless expressly told otherwise, assume ScaLAPACK is LP64 if( NOT DEFINED ScaLAPACK_IS_LP64 ) set( ScaLAPACK_IS_LP64 TRUE ) endif() if( ScaLAPACK_IS_LP64 ) set( ScaLAPACK_lp64_FOUND TRUE ) set( ScaLAPACK_ilp64_FOUND FALSE ) else() set( ScaLAPACK_lp64_FOUND FALSE ) set( ScaLAPACK_ilp64_FOUND TRUE ) find_dependency( ILP64 ) list( APPEND ScaLAPACK_COMPILE_OPTIONS "${ILP64_COMPILE_OPTIONS}" ) foreach ( lang C CXX Fortran ) if ( DEFINED ILP64_${lang}_COMPILE_OPTIONS ) list( APPEND ScaLAPACK_${lang}_COMPILE_OPTIONS "${ILP64_${lang}_COMPILE_OPTIONS}" ) endif() endforeach() endif() else() # Unset everything for safety unset( ScaLAPACK_LIBRARIES ) unset( ScaLAPACK_COMPILE_DEFINITIONS ) endif() find_package_handle_standard_args( ScaLAPACK REQUIRED_VARS ScaLAPACK_LINK_OK HANDLE_COMPONENTS ) # Cache variables set( ScaLAPACK_IS_LP64 "${ScaLAPACK_IS_LP64}" CACHE STRING "ScaLAPACK LP64 Flag" FORCE ) set( ScaLAPACK_LIBRARIES "${ScaLAPACK_LIBRARIES}" CACHE STRING "ScaLAPACK Libraries" FORCE ) if( ScaLAPACK_FOUND AND NOT TARGET ScaLAPACK::ScaLAPACK ) add_library( ScaLAPACK::ScaLAPACK INTERFACE IMPORTED ) set_target_properties( ScaLAPACK::ScaLAPACK PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${ScaLAPACK_COMPILE_DEFINITIONS}" INTERFACE_LINK_LIBRARIES "${ScaLAPACK_LIBRARIES}" ) endif() ga-5.9.2/cmake/linalg-modules/FindStandardFortran.cmake000066400000000000000000000041221500715745200230160ustar00rootroot00000000000000# # This module will locate the Fortran standard libraries so that a Fortran # object or library can be linked against using a C/C++ compiler. # # After calling this module the following will be set: # STANDARDFORTRAN_LIBRARIES : A list of libraries to link against # StandardFortran_FOUND : True if we found the standard Fortran libraries # # Implemenation note. CMake provides a variable # CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES which is a list of all libraries a # compiler implicitly links against. Unfortunately, at least for GNU, this # list includes a lot of extra libraries that we don't necessarily want to # link against (including both static and shared versions of libgcc). This is # why we've hardcoded the list per compiler. # enable_language(Fortran) include(FindPackageHandleStandardArgs) if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU") set(STANDARDFORTRAN_LIBS gfortran) elseif(CMAKE_Fortran_COMPILER_ID MATCHES "Intel") set(STANDARDFORTRAN_LIBS ifcore) elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "LLVMFlang") set(STANDARDFORTRAN_LIBS FortranRuntime FortranDecimal) elseif(CMAKE_Fortran_COMPILER_ID MATCHES "Flang") set(STANDARDFORTRAN_LIBS flang flangrti pgmath) #CMAKE_Fortran_COMPILER_ID does not give "ArmFlang" if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") set(STANDARDFORTRAN_LIBS armflang amath_a64fx) endif() else() message(FATAL_ERROR "${CMAKE_Fortran_COMPILER_ID} is not yet supported by this module.") endif() foreach(STANDARDFORTRAN_LIB ${STANDARDFORTRAN_LIBS}) set(STANDARDFORTRAN_LIB_NAMES lib${STANDARDFORTRAN_LIB}${CMAKE_SHARED_LIBRARY_SUFFIX} lib${STANDARDFORTRAN_LIB}.a) find_library(${STANDARDFORTRAN_LIB}_LIBRARY ${STANDARDFORTRAN_LIB_NAMES} HINTS ${CMAKE_Fortran_IMPLICIT_LINK_DIRECTORIES} ) list(APPEND STANDARDFORTRAN_LIBRARIES ${${STANDARDFORTRAN_LIB}_LIBRARY}) endforeach() find_package_handle_standard_args(StandardFortran DEFAULT_MSG STANDARDFORTRAN_LIBRARIES) set(STANDARDFORTRAN_FOUND ${StandardFortran_FOUND}) ga-5.9.2/cmake/linalg-modules/FindTBB.cmake000066400000000000000000000416241500715745200203410ustar00rootroot00000000000000# based https://github.com/Kitware/VTK/blob/master/CMake/FindTBB.cmake # # CHANGES: # - TBB_ROOT -> TBBROOT (tbb shell scripts set envvar TBBROOT) # - Find ThreadingBuildingBlocks include dirs and libraries # Use this module by invoking find_package with the form: # find_package(TBB # [REQUIRED] # Fail with error if TBB is not found # ) # # Once done, this will define # # TBB_FOUND - system has TBB # TBB_INCLUDE_DIRS - the TBB include directories # TBB_LIBRARIES - TBB libraries to be linked, doesn't include malloc or # malloc proxy # TBB::tbb - imported target for the TBB library # # TBB_VERSION_MAJOR - Major Product Version Number # TBB_VERSION_MINOR - Minor Product Version Number # TBB_INTERFACE_VERSION - Engineering Focused Version Number # TBB_COMPATIBLE_INTERFACE_VERSION - The oldest major interface version # still supported. This uses the engineering # focused interface version numbers. # # TBB_MALLOC_FOUND - system has TBB malloc library # TBB_MALLOC_INCLUDE_DIRS - the TBB malloc include directories # TBB_MALLOC_LIBRARIES - The TBB malloc libraries to be lined # TBB::malloc - imported target for the TBB malloc library # # TBB_MALLOC_PROXY_FOUND - system has TBB malloc proxy library # TBB_MALLOC_PROXY_INCLUDE_DIRS = the TBB malloc proxy include directories # TBB_MALLOC_PROXY_LIBRARIES - The TBB malloc proxy libraries to be lined # TBB::malloc_proxy - imported target for the TBB malloc proxy library # # # This module reads hints about search locations from variables: # ENV TBB_ARCH_PLATFORM - for eg. set it to "mic" for Xeon Phi builds # ENV TBBROOT or just TBBROOT - root directory of tbb installation # ENV TBB_BUILD_PREFIX - specifies the build prefix for user built tbb # libraries. Should be specified with ENV TBBROOT # and optionally... # ENV TBB_BUILD_DIR - if build directory is different than ${TBBROOT}/build # # # Modified by Robert Maynard from the original OGRE source # #------------------------------------------------------------------- # This file is part of the CMake build system for OGRE # (Object-oriented Graphics Rendering Engine) # For the latest info, see http://www.ogre3d.org/ # # The contents of this file are placed in the public domain. Feel # free to make use of it in any way you like. #------------------------------------------------------------------- # #============================================================================= # Copyright 2010-2012 Kitware, Inc. # Copyright 2012 Rolf Eike Beer # # 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 distribute this file outside of CMake, substitute the full # License text for the above reference.) #============================================================================= # FindTBB helper functions and macros # # Use TBBConfig.cmake if possible. enable_language(CXX) set(_tbb_find_quiet) if (TBB_FIND_QUIETLY) set(_tbb_find_quiet QUIET) endif () set(_tbb_find_components) set(_tbb_find_optional_components) foreach (_tbb_find_component IN LISTS TBB_FIND_COMPONENTS) if (TBB_FIND_REQUIRED_${_tbb_find_component}) list(APPEND _tbb_find_components "${_tbb_find_component}") else () list(APPEND _tbb_find_optional_components "${_tbb_find_component}") endif () endforeach () unset(_tbb_find_component) find_package(TBB CONFIG ${_tbb_find_quiet} COMPONENTS ${_tbb_find_components} OPTIONAL_COMPONENTS ${_tbb_find_optional_components}) unset(_tbb_find_quiet) unset(_tbb_find_components) unset(_tbb_find_optional_components) if (TBB_FOUND) return () endif () #==================================================== # Fix the library path in case it is a linker script #==================================================== function(tbb_extract_real_library library real_library) if(NOT UNIX OR NOT EXISTS ${library}) set(${real_library} "${library}" PARENT_SCOPE) return() endif() #Read in the first 4 bytes and see if they are the ELF magic number set(_elf_magic "7f454c46") file(READ ${library} _hex_data OFFSET 0 LIMIT 4 HEX) if(_hex_data STREQUAL _elf_magic) #we have opened a elf binary so this is what #we should link to set(${real_library} "${library}" PARENT_SCOPE) return() endif() file(READ ${library} _data OFFSET 0 LIMIT 1024) if("${_data}" MATCHES "INPUT \\(([^(]+)\\)") #extract out the .so name from REGEX MATCH command set(_proper_so_name "${CMAKE_MATCH_1}") #construct path to the real .so which is presumed to be in the same directory #as the input file get_filename_component(_so_dir "${library}" DIRECTORY) set(${real_library} "${_so_dir}/${_proper_so_name}" PARENT_SCOPE) else() #unable to determine what this library is so just hope everything works #and pass it unmodified. set(${real_library} "${library}" PARENT_SCOPE) endif() endfunction() #=============================================== # Do the final processing for the package find. #=============================================== macro(findpkg_finish PREFIX TARGET_NAME) if ((${PREFIX}_INCLUDE_DIR OR ${PREFIX}_IGNORE_HEADERS) AND ${PREFIX}_LIBRARY) set(${PREFIX}_FOUND TRUE) set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIR}) set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARY}) else () if (${PREFIX}_FIND_REQUIRED AND NOT ${PREFIX}_FIND_QUIETLY) message(FATAL_ERROR "Required library ${PREFIX} not found.") endif () endif () if (NOT TARGET "TBB::${TARGET_NAME}") if (${PREFIX}_LIBRARY_RELEASE) tbb_extract_real_library(${${PREFIX}_LIBRARY_RELEASE} real_release) endif () if (${PREFIX}_LIBRARY_DEBUG) tbb_extract_real_library(${${PREFIX}_LIBRARY_DEBUG} real_debug) endif () add_library(TBB::${TARGET_NAME} UNKNOWN IMPORTED) if(NOT ${PREFIX}_IGNORE_HEADERS) set_target_properties(TBB::${TARGET_NAME} PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${${PREFIX}_INCLUDE_DIR}") endif() if (${PREFIX}_LIBRARY_DEBUG AND ${PREFIX}_LIBRARY_RELEASE) set_target_properties(TBB::${TARGET_NAME} PROPERTIES IMPORTED_LOCATION "${real_release}" IMPORTED_LOCATION_DEBUG "${real_debug}" IMPORTED_LOCATION_RELEASE "${real_release}") elseif (${PREFIX}_LIBRARY_RELEASE) set_target_properties(TBB::${TARGET_NAME} PROPERTIES IMPORTED_LOCATION "${real_release}") elseif (${PREFIX}_LIBRARY_DEBUG) set_target_properties(TBB::${TARGET_NAME} PROPERTIES IMPORTED_LOCATION "${real_debug}") endif () # on non-Windows platforms need C++ library # tried IMPORTED_LINK_DEPENDENT_LIBRARIES # and IMPORTED_LINK_INTERFACE_LANGUAGES (and changing UNKNOWN -> SHARED/STATIC in add_library) # to pass this info, to no avail if (NOT (WIN32 AND MSVC)) if (USE_LIBCXX) set(std_cxx_lib_name c++) else (USE_LIBCXX) set(std_cxx_lib_name stdc++) endif (USE_LIBCXX) target_link_libraries(TBB::${TARGET_NAME} INTERFACE ${std_cxx_lib_name}) unset(std_cxx_lib_name) endif () endif () #mark the following variables as internal variables mark_as_advanced(${PREFIX}_INCLUDE_DIR ${PREFIX}_LIBRARY ${PREFIX}_LIBRARY_DEBUG ${PREFIX}_LIBRARY_RELEASE) endmacro() #=============================================== # Generate debug names from given release names #=============================================== macro(get_debug_names PREFIX) foreach(i ${${PREFIX}}) set(${PREFIX}_DEBUG ${${PREFIX}_DEBUG} ${i}d ${i}D ${i}_d ${i}_D ${i}_debug ${i}) endforeach() endmacro() #=============================================== # See if we have env vars to help us find tbb #=============================================== macro(getenv_path VAR) set(ENV_${VAR} $ENV{${VAR}}) # replace won't work if var is blank if (ENV_${VAR}) string( REGEX REPLACE "\\\\" "/" ENV_${VAR} ${ENV_${VAR}} ) endif () endmacro() #=============================================== # Couple a set of release AND debug libraries #=============================================== macro(make_library_set PREFIX) if (${PREFIX}_RELEASE AND ${PREFIX}_DEBUG) set(${PREFIX} optimized ${${PREFIX}_RELEASE} debug ${${PREFIX}_DEBUG}) elseif (${PREFIX}_RELEASE) set(${PREFIX} ${${PREFIX}_RELEASE}) elseif (${PREFIX}_DEBUG) set(${PREFIX} ${${PREFIX}_DEBUG}) endif () endmacro() #============================================================================= # Now to actually find TBB # # Get path, convert backslashes as ${ENV_${var}} getenv_path(TBBROOT) getenv_path(TBB_IGNORE_HEADERS) if(ENV_TBB_IGNORE_HEADERS AND NOT TBB_IGNORE_HEADERS) set( TBB_IGNORE_HEADERS ${ENV_TBB_IGNORE_HEADERS} ) endif() # initialize search paths set(TBB_PREFIX_PATH ${TBBROOT} ${ENV_TBBROOT}) set(TBB_INC_SEARCH_PATH "") set(TBB_LIB_SEARCH_PATH "") # If user built from sources set(TBB_BUILD_PREFIX $ENV{TBB_BUILD_PREFIX}) if (TBB_BUILD_PREFIX AND ENV_TBBROOT) getenv_path(TBB_BUILD_DIR) if (NOT ENV_TBB_BUILD_DIR) set(ENV_TBB_BUILD_DIR ${ENV_TBBROOT}/build) endif () # include directory under ${ENV_TBBROOT}/include list(APPEND TBB_LIB_SEARCH_PATH ${ENV_TBB_BUILD_DIR}/${TBB_BUILD_PREFIX}_release ${ENV_TBB_BUILD_DIR}/${TBB_BUILD_PREFIX}_debug) endif () # For Windows, let's assume that the user might be using the precompiled # TBB packages from the main website. These use a rather awkward directory # structure (at least for automatically finding the right files) depending # on platform and compiler, but we'll do our best to accommodate it. # Not adding the same effort for the precompiled linux builds, though. Those # have different versions for CC compiler versions and linux kernels which # will never adequately match the user's setup, so there is no feasible way # to detect the "best" version to use. The user will have to manually # select the right files. (Chances are the distributions are shipping their # custom version of tbb, anyway, so the problem is probably nonexistent.) if (WIN32 AND MSVC) set(COMPILER_PREFIX "vc7.1") if (MSVC_VERSION EQUAL 1400) set(COMPILER_PREFIX "vc8") elseif(MSVC_VERSION EQUAL 1500) set(COMPILER_PREFIX "vc9") elseif(MSVC_VERSION EQUAL 1600) set(COMPILER_PREFIX "vc10") elseif(MSVC_VERSION EQUAL 1700) set(COMPILER_PREFIX "vc11") elseif(MSVC_VERSION EQUAL 1800) set(COMPILER_PREFIX "vc12") elseif(MSVC_VERSION GREATER_EQUAL 1900) set(COMPILER_PREFIX "vc14") endif () # for each prefix path, add ia32/64\${COMPILER_PREFIX}\lib to the lib search path foreach (dir IN LISTS TBB_PREFIX_PATH) if (CMAKE_CL_64) list(APPEND TBB_LIB_SEARCH_PATH ${dir}/intel64/${COMPILER_PREFIX}/lib) list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/intel64/${COMPILER_PREFIX}) else () list(APPEND TBB_LIB_SEARCH_PATH ${dir}/ia32/${COMPILER_PREFIX}/lib) list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/ia32/${COMPILER_PREFIX}) endif () endforeach () endif () # For OS X binary distribution, choose libc++ based libraries for Mavericks (10.9) # and above and AppleClang if (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT CMAKE_SYSTEM_VERSION VERSION_LESS 13.0) set (USE_LIBCXX OFF) cmake_policy(GET CMP0025 POLICY_VAR) if (POLICY_VAR STREQUAL "NEW") if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") set (USE_LIBCXX ON) endif () else () if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set (USE_LIBCXX ON) endif () endif () if (USE_LIBCXX) foreach (dir IN LISTS TBB_PREFIX_PATH) list (APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/libc++ ${dir}/libc++/lib) endforeach () endif () endif () # check compiler ABI if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(COMPILER_PREFIX) if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8) list(APPEND COMPILER_PREFIX "gcc4.8") endif() if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7) list(APPEND COMPILER_PREFIX "gcc4.7") endif() if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4) list(APPEND COMPILER_PREFIX "gcc4.4") endif() list(APPEND COMPILER_PREFIX "gcc4.1") elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(COMPILER_PREFIX) if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.0) # Complete guess list(APPEND COMPILER_PREFIX "gcc4.8") endif() if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.6) list(APPEND COMPILER_PREFIX "gcc4.7") endif() list(APPEND COMPILER_PREFIX "gcc4.4") else() # Assume compatibility with 4.4 for other compilers list(APPEND COMPILER_PREFIX "gcc4.4") endif () # if platform architecture is explicitly specified set(TBB_ARCH_PLATFORM $ENV{TBB_ARCH_PLATFORM}) if (TBB_ARCH_PLATFORM) foreach (dir IN LISTS TBB_PREFIX_PATH) list(APPEND TBB_LIB_SEARCH_PATH ${dir}/${TBB_ARCH_PLATFORM}/lib) list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/${TBB_ARCH_PLATFORM}) endforeach () endif () foreach (dir IN LISTS TBB_PREFIX_PATH) foreach (prefix IN LISTS COMPILER_PREFIX) if (CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/intel64) list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/intel64/${prefix}) list(APPEND TBB_LIB_SEARCH_PATH ${dir}/intel64/lib) list(APPEND TBB_LIB_SEARCH_PATH ${dir}/intel64/${prefix}/lib) else () list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/ia32) list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib/ia32/${prefix}) list(APPEND TBB_LIB_SEARCH_PATH ${dir}/ia32/lib) list(APPEND TBB_LIB_SEARCH_PATH ${dir}/ia32/${prefix}/lib) endif () endforeach() endforeach () # add general search paths foreach (dir IN LISTS TBB_PREFIX_PATH) list(APPEND TBB_LIB_SEARCH_PATH ${dir}/lib ${dir}/Lib ${dir}/lib/tbb ${dir}/Libs) list(APPEND TBB_INC_SEARCH_PATH ${dir}/include ${dir}/Include ${dir}/include/tbb) endforeach () set(TBB_LIBRARY_NAMES tbb) get_debug_names(TBB_LIBRARY_NAMES) find_path(TBB_INCLUDE_DIR NAMES tbb/tbb.h PATHS ${TBB_INC_SEARCH_PATH}) find_library(TBB_LIBRARY_RELEASE NAMES ${TBB_LIBRARY_NAMES} PATHS ${TBB_LIB_SEARCH_PATH}) find_library(TBB_LIBRARY_DEBUG NAMES ${TBB_LIBRARY_NAMES_DEBUG} PATHS ${TBB_LIB_SEARCH_PATH}) make_library_set(TBB_LIBRARY) findpkg_finish(TBB tbb) #if we haven't found TBB no point on going any further if (NOT TBB_FOUND) return() endif () #============================================================================= # Look for TBB's malloc package set(TBB_MALLOC_LIBRARY_NAMES tbbmalloc) get_debug_names(TBB_MALLOC_LIBRARY_NAMES) find_path(TBB_MALLOC_INCLUDE_DIR NAMES tbb/tbb.h PATHS ${TBB_INC_SEARCH_PATH}) find_library(TBB_MALLOC_LIBRARY_RELEASE NAMES ${TBB_MALLOC_LIBRARY_NAMES} PATHS ${TBB_LIB_SEARCH_PATH}) find_library(TBB_MALLOC_LIBRARY_DEBUG NAMES ${TBB_MALLOC_LIBRARY_NAMES_DEBUG} PATHS ${TBB_LIB_SEARCH_PATH}) make_library_set(TBB_MALLOC_LIBRARY) findpkg_finish(TBB_MALLOC tbbmalloc) #============================================================================= # Look for TBB's malloc proxy package set(TBB_MALLOC_PROXY_LIBRARY_NAMES tbbmalloc_proxy) get_debug_names(TBB_MALLOC_PROXY_LIBRARY_NAMES) find_path(TBB_MALLOC_PROXY_INCLUDE_DIR NAMES tbb/tbbmalloc_proxy.h PATHS ${TBB_INC_SEARCH_PATH}) find_library(TBB_MALLOC_PROXY_LIBRARY_RELEASE NAMES ${TBB_MALLOC_PROXY_LIBRARY_NAMES} PATHS ${TBB_LIB_SEARCH_PATH}) find_library(TBB_MALLOC_PROXY_LIBRARY_DEBUG NAMES ${TBB_MALLOC_PROXY_LIBRARY_NAMES_DEBUG} PATHS ${TBB_LIB_SEARCH_PATH}) make_library_set(TBB_MALLOC_PROXY_LIBRARY) findpkg_finish(TBB_MALLOC_PROXY tbbmalloc_proxy) #============================================================================= #parse all the version numbers from tbb if(NOT TBB_IGNORE_HEADERS AND NOT TBB_VERSION) if (EXISTS "${TBB_INCLUDE_DIR}/oneapi/tbb/version.h") file(STRINGS "${TBB_INCLUDE_DIR}/oneapi/tbb/version.h" TBB_VERSION_CONTENTS REGEX "VERSION") else() #only read the start of the file file(STRINGS "${TBB_INCLUDE_DIR}/tbb/tbb_stddef.h" TBB_VERSION_CONTENTS REGEX "VERSION") endif() string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1" TBB_VERSION_MAJOR "${TBB_VERSION_CONTENTS}") string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1" TBB_VERSION_MINOR "${TBB_VERSION_CONTENTS}") string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" TBB_INTERFACE_VERSION "${TBB_VERSION_CONTENTS}") string(REGEX REPLACE ".*#define TBB_COMPATIBLE_INTERFACE_VERSION ([0-9]+).*" "\\1" TBB_COMPATIBLE_INTERFACE_VERSION "${TBB_VERSION_CONTENTS}") endif() ga-5.9.2/cmake/linalg-modules/LICENSE.txt000066400000000000000000000026751500715745200177550ustar00rootroot00000000000000Copyright (c) 2020 David Williams-Young Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ga-5.9.2/cmake/linalg-modules/LinAlgModulesMacros.cmake000066400000000000000000000037711500715745200227760ustar00rootroot00000000000000set( LINALG_MACROS_DIR ${CMAKE_CURRENT_LIST_DIR} ) macro( find_linalg_dependencies _libs ) include( CMakeFindDependencyMacro ) foreach( _lib ${${_libs}} ) if (${_lib} MATCHES "OpenMP") find_dependency(OpenMP) elseif (${_lib} MATCHES "Threads") if (NOT TARGET Threads::Threads) find_dependency(Threads) # Threads::Threads by default is not GLOBAL, so to allow users of LINALG_LIBRARIES to safely use it we need to make it global # more discussion here: https://gitlab.kitware.com/cmake/cmake/-/issues/17256 set_target_properties(Threads::Threads PROPERTIES IMPORTED_GLOBAL TRUE) endif(NOT TARGET Threads::Threads) elseif (${_lib} MATCHES "tbb") find_dependency(TBB) elseif (${_lib} MATCHES "MPI") find_dependency(MPI) endif() endforeach() endmacro() function( install_linalg_modules _dest_dir ) set( LINALG_FIND_MODULES FindBLAS.cmake FindBLIS.cmake FindIBMESSL.cmake FindIntelMKL.cmake FindLAPACK.cmake FindOpenBLAS.cmake FindReferenceBLAS.cmake FindReferenceLAPACK.cmake FindReferenceScaLAPACK.cmake FindScaLAPACK.cmake FindILP64.cmake FindTBB.cmake FindStandardFortran.cmake LinAlgModulesMacros.cmake ) set( LINALG_UTIL_FILES util/BLASUtilities.cmake util/blis_int_size.c util/func_check.c util/get_mpi_vendor.c util/ilp64_checker.c util/lapack_ilp64_checker.c util/openblas_int_size.c util/CommonFunctions.cmake util/IntrospectMPI.cmake util/IntrospectOpenMP.cmake util/LAPACKUtilities.cmake util/ScaLAPACKUtilities.cmake ) list( TRANSFORM LINALG_FIND_MODULES PREPEND ${LINALG_MACROS_DIR}/ ) list( TRANSFORM LINALG_UTIL_FILES PREPEND ${LINALG_MACROS_DIR}/ ) install( FILES ${LINALG_FIND_MODULES} ${LINALG_MACROS_DIR}/LICENSE.txt DESTINATION ${${_dest_dir}}/linalg-cmake-modules ) install( FILES ${LINALG_UTIL_FILES} DESTINATION ${${_dest_dir}}/linalg-cmake-modules/util ) endfunction() ga-5.9.2/cmake/linalg-modules/util/000077500000000000000000000000001500715745200170755ustar00rootroot00000000000000ga-5.9.2/cmake/linalg-modules/util/BLASUtilities.cmake000066400000000000000000000013011500715745200225070ustar00rootroot00000000000000set( BLAS_UTILITY_CMAKE_FILE_DIR ${CMAKE_CURRENT_LIST_DIR} ) function( check_blas_int _libs _dgemm_name _libs_are_lp64 ) try_run( _run_result _compile_result ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${BLAS_UTILITY_CMAKE_FILE_DIR}/ilp64_checker.c LINK_LIBRARIES ${${_libs}} COMPILE_DEFINITIONS "-DDGEMM_NAME=${_dgemm_name}" COMPILE_OUTPUT_VARIABLE _compile_output RUN_OUTPUT_VARIABLE _run_output ) if (NOT _compile_result) message(FATAL_ERROR "check_blas_int: try_run failed: _compile_output=${_compile_output}") endif() if( _run_output MATCHES "BLAS IS LP64" ) set( ${_libs_are_lp64} TRUE PARENT_SCOPE ) else() set( ${_libs_are_lp64} FALSE PARENT_SCOPE ) endif() endfunction() ga-5.9.2/cmake/linalg-modules/util/CommonFunctions.cmake000066400000000000000000000252621500715745200232270ustar00rootroot00000000000000#================================================================== # Copyright (c) 2018 The Regents of the University of California, # through Lawrence Berkeley National Laboratory. # # Author: David Williams-Young # # This file is part of cmake-modules. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # (1) Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # (2) Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # (3) Neither the name of the University of California, Lawrence Berkeley # National Laboratory, U.S. Dept. of Energy nor the names of its contributors may # be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # You are under no obligation whatsoever to provide any bug fixes, patches, or # upgrades to the features, functionality or performance of the source code # ("Enhancements") to anyone; however, if you choose to make your Enhancements # available either publicly, or directly to Lawrence Berkeley National # Laboratory, without imposing a separate written license agreement for such # Enhancements, then you hereby grant the following license: a non-exclusive, # royalty-free perpetual license to install, use, modify, prepare derivative # works, incorporate into other computer software, distribute, and sublicense # such enhancements or derivative works thereof, in binary and source code form. # #================================================================== set( COMMON_UTILITY_CMAKE_FILE_DIR ${CMAKE_CURRENT_LIST_DIR} ) function( emulate_kitware_linalg_modules name ) if( DEFINED BLA_STATIC AND NOT DEFINED ${name}_PREFERS_STATIC ) if( DEFINED CACHE{BLA_STATIC} ) set( ${name}_PREFERS_STATIC ${BLA_STATIC} CACHE BOOL "Use Static ${name} LIBRARIES" ) else() set( ${name}_PREFERS_STATIC ${BLA_STATIC} PARENT_SCOPE ) endif() endif() if( DEFINED BLA_VENDOR AND NOT DEFINED ${name}_PREFERENCE_LIST ) if( DEFINED CACHE{BLA_VENDOR} ) set( ${name}_PREFERENCE_LIST ${BLA_VENDOR} CACHE BOOL "Use Static ${name} LIBRARIES" ) else() set( ${name}_PREFERENCE_LIST ${BLA_VENDOR} PARENT_SCOPE ) endif() endif() endfunction() function( fill_out_prefix name ) #if( ${name}_PREFIX AND NOT ${name}_INCLUDE_DIR ) # set( ${name}_INCLUDE_DIR ${${name}_PREFIX}/include PARENT_SCOPE ) #endif() if( ${name}_PREFIX AND NOT ${name}_LIBRARY_DIR ) set( ${name}_LIBRARY_DIR "${${name}_PREFIX}/lib;${${name}_PREFIX}/lib32;${${name}_PREFIX}/lib64" PARENT_SCOPE ) endif() endfunction() function( copy_meta_data _src _dest ) #if( ${_src}_LIBRARIES AND NOT ${_dest}_LIBRARIES ) # set( ${_dest}_LIBRARIES ${${_src}_LIBRARIES} PARENT_SCOPE ) #endif() if( ${_src}_PREFIX AND NOT ${_dest}_PREFIX ) set( ${_dest}_PREFIX ${${_src}_PREFIX} PARENT_SCOPE ) endif() if( ${_src}_INCLUDE_DIR AND NOT ${_dest}_INCLUDE_DIR ) set( ${_dest}_INCLUDE_DIR ${${_src}_INCLUDE_DIR} PARENT_SCOPE ) endif() if( ${_src}_LIBRARY_DIR AND NOT ${_dest}_LIBRARY_DIR ) set( ${_dest}_LIBRARY_DIR ${${_src}_LIBRARY_DIR} PARENT_SCOPE ) endif() if( ${_src}_PREFERS_STATIC AND NOT ${_dest}_PREFERS_STATIC ) set( ${_dest}_PREFERS_STATIC ${${_src}_PREFERS_STATIC} PARENT_SCOPE ) endif() if( ${_src}_THREAD_LAYER AND NOT ${_dest}_THREAD_LAYER ) set( ${_dest}_THREAD_LAYER ${${_src}_THREAD_LAYER} PARENT_SCOPE ) endif() endfunction() function( get_true_target_property _out _target _property ) if( TARGET ${_target} ) get_property( _${_target}_imported TARGET ${_target} PROPERTY IMPORTED ) if( NOT ${_property} MATCHES "INTERFACE_LINK_LIBRARIES" ) get_property( _${_target}_property TARGET ${_target} PROPERTY ${_property} ) endif() if( _${_target}_imported ) #message( STATUS "${_target} is IMPORTED" ) get_property( _${_target}_link TARGET ${_target} PROPERTY INTERFACE_LINK_LIBRARIES ) foreach( _lib ${_${_target}_link} ) #message( STATUS "Checking ${_lib}") if( TARGET ${_lib} ) get_true_target_property( _${_lib}_property ${_lib} ${_property} ) #message( STATUS "${_lib} is a TARGET with ${_${_lib}_property}" ) if( _${_lib}_property ) list( APPEND _${_target}_property_imported ${_${_lib}_property} ) endif() elseif( ${_property} MATCHES "INTERFACE_LINK_LIBRARIES" ) list( APPEND _${_target}_property_imported ${_lib} ) endif() endforeach() if(_${_target}_property_imported) list(APPEND _${_target}_property ${_${_target}_property_imported} ) endif() set( ${_out} ${_${_target}_property} PARENT_SCOPE ) else() #message( STATUS "${_target} is NOT IMPORTED" ) #message( STATUS "Setting ${_out} to ${_${_target}_property} " ) set( ${_out} ${_${_target}_property} PARENT_SCOPE ) endif() endif() endfunction() function( check_function_exists_w_results _libs _func _output _result ) try_compile( ${_result} ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${COMMON_UTILITY_CMAKE_FILE_DIR}/func_check.c COMPILE_DEFINITIONS "-DFUNC_NAME=${_func}" LINK_LIBRARIES ${_libs} OUTPUT_VARIABLE ${_output} ) set( ${_output} "${${_output}}" PARENT_SCOPE ) set( ${_result} "${${_result}}" PARENT_SCOPE ) endfunction() function( append_possibly_missing_libs _linker_test __compile_output _orig_libs __new_libs ) set( _tmp_libs ) # Check for missing Fortran symbols if( ${__compile_output} MATCHES "fortran" OR ${__compile_output} MATCHES "Fortran" OR ${__compile_output} MATCHES "f90_" ) message( STATUS " * Missing Standard Fortran Libs - Adding to ${_linker_test} linker" ) # Check for Standard Fortran Libraries if(NOT STANDARDFORTRAN_LIBRARIES) include(CMakeFindDependencyMacro) find_dependency( StandardFortran ) endif() list( APPEND _tmp_libs "${STANDARDFORTRAN_LIBRARIES}" ) endif() if( ${__compile_output} MATCHES "omp_" ) message( STATUS " * Missing OpenMP - Adding to ${_linker_test} linker" ) if( NOT TARGET OpenMP::OpenMP_C ) find_dependency( OpenMP ) endif() list( APPEND _tmp_libs OpenMP::OpenMP_C ) endif() if( ${__compile_output} MATCHES "pthread_" ) message( STATUS " * Missing PThreads - Adding to ${_linker_test} linker" ) if( NOT TARGET Threads::Threads ) find_dependency( Threads ) # Threads::Threads by default is not GLOBAL, so to allow users of LINALG_LIBRARIES to safely use it we need to make it global # more discussion here: https://gitlab.kitware.com/cmake/cmake/-/issues/17256 set_target_properties(Threads::Threads PROPERTIES IMPORTED_GLOBAL TRUE) endif() list( APPEND _tmp_libs Threads::Threads ) endif() if( ${__compile_output} MATCHES "logf" OR ${__compile_output} MATCHES "sqrt" ) message( STATUS " * Missing LIBM - Adding to ${_linker_test} linker" ) list( APPEND _tmp_libs "m" ) endif() set( ${__new_libs} "${_tmp_libs}" PARENT_SCOPE ) endfunction() # _funcs = LIST of lowercase symbol name # _namespace = namespace (BLAS, LAPACK, etc.) function( check_fortran_functions_exist _funcs _namespace _libs _link_ok _uses_lower _uses_underscore ) set( ${_link_ok} FALSE ) set( ${_uses_lower} ) set( ${_uses_underscore} ) foreach( _uplo LOWER UPPER ) foreach( _under UNDERSCORE NO_UNDERSCORE ) set( _item ${_namespace}_${_uplo}_${_under} ) message( STATUS "Performing Test ${_item}" ) # ask linker for each symbol in _funcs, exit early if any fail foreach( _func IN LISTS _funcs) set( _${_func}_name_template "${_func}" ) string( TO${_uplo} ${_${_func}_name_template} _${_func}_name_uplo ) if( _under EQUAL "UNDERSCORE" ) set( _${_func}_name "${_${_func}_name_uplo}_" ) else() set( _${_func}_name "${_${_func}_name_uplo}_" ) endif() check_function_exists_w_results( "${${_libs}}" ${_${_func}_name} _compile_output _compile_result ) if( NOT _compile_result ) append_possibly_missing_libs( ${_namespace} _compile_output ${_libs} _new_libs ) list( APPEND ${_libs} ${_new_libs} ) set( ${_libs} ${${_libs}} PARENT_SCOPE ) # try linking again check_function_exists_w_results( "${${_libs}}" ${_${_func}_name} _compile_output _compile_result ) endif() unset( _${_func}_name_template ) unset( _${_func}_name_uplo ) file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "FUNCTION CHECK: ${_item}\n ${_compile_output}") if( _compile_result ) set( ${_link_ok} TRUE ) string( COMPARE EQUAL "${_uplo}" "LOWER" ${_uses_lower} ) string( COMPARE EQUAL "${_under}" "UNDERSCORE" ${_uses_underscore} ) else() break() # early exit foreach if linking failed for any symbol even with extra libs endif() endforeach() # _funcs if( ${${_link_ok}} ) message( STATUS "Performing Test ${_item} -- found" ) break() else () message( STATUS "Performing Test ${_item} -- not found" ) endif() endforeach() # underscore if( ${${_link_ok}} ) break() endif() endforeach() # lowerupper set( ${_link_ok} ${${_link_ok}} PARENT_SCOPE ) set( ${_uses_lower} ${${_uses_lower}} PARENT_SCOPE ) set( ${_uses_underscore} ${${_uses_underscore}} PARENT_SCOPE ) endfunction() ga-5.9.2/cmake/linalg-modules/util/IntrospectMPI.cmake000066400000000000000000000017751500715745200226110ustar00rootroot00000000000000set( MPI_UTILITY_CMAKE_FILE_DIR ${CMAKE_CURRENT_LIST_DIR} ) function( get_mpi_vendor _mpi_libs _mpi_vendor ) if( NOT TARGET ${_mpi_libs} ) set( _mpi_linker ${${_mpi_libs}} ) else() set( _mpi_linker ${_mpi_libs} ) endif() try_run( _run_result _compile_result ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${MPI_UTILITY_CMAKE_FILE_DIR}/get_mpi_vendor.c LINK_LIBRARIES ${_mpi_linker} COMPILE_OUTPUT_VARIABLE _compile_output RUN_OUTPUT_VARIABLE _run_output ) string( TOUPPER "${_run_output}" _run_output ) if( _run_output MATCHES "CRAY" ) set( ${_mpi_vendor} "CRAY" PARENT_SCOPE ) elseif( _run_output MATCHES "OPEN MPI" ) set( ${_mpi_vendor} "OPENMPI" PARENT_SCOPE ) elseif( _run_output MATCHES "OPENMPI" ) set( ${_mpi_vendor} "OPENMPI" PARENT_SCOPE ) elseif( _run_output MATCHES "MVAPICH" ) set( ${_mpi_vendor} "MVAPICH" PARENT_SCOPE ) elseif( _run_output MATCHES "MPICH" ) set( ${_mpi_vendor} "MPICH" PARENT_SCOPE ) else() set( ${_mpi_vendor} "UNKNOWN" PARENT_SCOPE ) endif() endfunction() ga-5.9.2/cmake/linalg-modules/util/IntrospectOpenMP.cmake000066400000000000000000000037261500715745200233200ustar00rootroot00000000000000set( OMP_UTILITY_CMAKE_FILE_DIR ${CMAKE_CURRENT_LIST_DIR} ) function( check_openmp_is_gnu _openmp_libs _openmp_is_gnu ) include( CMakePushCheckState ) include( CheckFunctionExists ) cmake_push_check_state( RESET ) if( NOT TARGET ${_openmp_libs} ) set( CMAKE_REQUIRED_LIBRARIES ${${_openmp_libs}} ) else() set( CMAKE_REQUIRED_LIBRARIES ${_openmp_libs} ) endif() set( CMAKE_REQUIRED_QUIET ON ) check_function_exists( "kmp_set_defaults" _openmp_is_not_gnu ) cmake_pop_check_state() message( STATUS "Checking if OpenMP is GNU" ) if( _openmp_is_not_gnu ) message( STATUS "Checking if OpenMP is GNU -- NO" ) set( ${_openmp_is_gnu} FALSE PARENT_SCOPE ) else() message( STATUS "Checking if OpenMP is GNU -- YES" ) set( ${_openmp_is_gnu} TRUE PARENT_SCOPE ) endif() endfunction() #function( check_openmp_is_intel _openmp_libs _openmp_is_intel ) # #include( CMakePushCheckState ) #include( CheckLibraryExists ) #include( CheckSymbolExists ) # # #cmake_push_check_state( RESET ) # #if( ${_openmp_libs} ) # set( CMAKE_REQUIRED_LIBRARIES ${${_openmp_libs}} ) #endif() #set( CMAKE_REQUIRED_QUIET ON ) # #check_library_exists( "" "kmp_set_defaults" "" ${_openmp_is_intel} ) # #if( ${_openmp_is_intel} ) # set( ${_openmp_is_intel} TRUE PARENT_SCOPE ) #else() # set( ${_openmp_is_intel} FALSE PARENT_SCOPE ) #endif() # #endfunction() # # # # # # #function( check_openmp_is_nv _openmp_libs _openmp_is_nv ) # #include( CMakePushCheckState ) #include( CheckLibraryExists ) #include( CheckSymbolExists ) # # #cmake_push_check_state( RESET ) # # # #if( ${_openmp_libs} ) # if( NOT TARGET ${_openmp_libs} ) # set( _openmp_libs ${${_openmp_libs}} ) # endif() # set( CMAKE_REQUIRED_LIBRARIES ${_openmp_libs} ) #endif() #set( CMAKE_REQUIRED_QUIET ON ) # #check_library_exists( "" "nvomp_set_memory_preferred_location_device" "" ${_openmp_is_nv} ) # #if( ${_openmp_is_nv} ) # set( ${_openmp_is_nv} TRUE PARENT_SCOPE ) #else() # set( ${_openmp_is_nv} FALSE PARENT_SCOPE ) #endif() # #endfunction() ga-5.9.2/cmake/linalg-modules/util/LAPACKUtilities.cmake000066400000000000000000000014711500715745200227310ustar00rootroot00000000000000set( LAPACK_UTILITY_CMAKE_FILE_DIR ${CMAKE_CURRENT_LIST_DIR} ) function( check_lapack_int _libs _dsyev_name _libs_are_lp64 ) try_run( _run_result _compile_result ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${LAPACK_UTILITY_CMAKE_FILE_DIR}/lapack_ilp64_checker.c LINK_LIBRARIES ${${_libs}} COMPILE_DEFINITIONS "-DDSYEV_NAME=${_dsyev_name}" COMPILE_OUTPUT_VARIABLE _compile_output RUN_OUTPUT_VARIABLE _run_output ) if (NOT _compile_result) message(FATAL_ERROR "check_lapack_int: try_run failed: _compile_output=${_compile_output}") endif() if( _run_output MATCHES "LAPACK IS LP64" ) set( ${_libs_are_lp64} TRUE PARENT_SCOPE ) else() set( ${_libs_are_lp64} FALSE PARENT_SCOPE ) endif() endfunction() ga-5.9.2/cmake/linalg-modules/util/ScaLAPACKUtilities.cmake000066400000000000000000000054141500715745200233610ustar00rootroot00000000000000set( ScaLAPACK_UTILITY_CMAKE_FILE_DIR ${CMAKE_CURRENT_LIST_DIR} ) function( check_pdpotrf_exists _libs _link_ok _uses_lower _uses_underscore ) include( ${ScaLAPACK_UTILITY_CMAKE_FILE_DIR}/CommonFunctions.cmake ) set( ${_link_ok} FALSE ) set( ${_uses_lower} ) set( ${_uses_underscore} ) foreach( _uplo LOWER UPPER ) set( _pdpotrf_name_template "pdpotrf" ) string( TO${_uplo} ${_pdpotrf_name_template} _pdpotrf_name_uplo ) foreach( _under UNDERSCORE NO_UNDERSCORE ) set( _item ScaLAPACK_${_uplo}_${_under} ) if( _under EQUAL "UNDERSCORE" ) set( _pdpotrf_name "${_pdpotrf_name_uplo}_" ) else() set( _pdpotrf_name "${_pdpotrf_name_uplo}_" ) endif() check_function_exists_w_results( "${${_libs}}" ${_pdpotrf_name} _compile_output _compile_result ) message( STATUS "Performing Test ${_item}" ) if( _compile_result ) message( STATUS "Performing Test ${_item} -- found" ) set( ${_link_ok} TRUE ) string( COMPARE EQUAL "${_uplo}" "LOWER" ${_uses_lower} ) string( COMPARE EQUAL "${_under}" "UNDERSCORE" ${_uses_underscore} ) break() else() append_possibly_missing_libs( ScaLAPACK _compile_output ${_libs} _new_libs ) list( APPEND ${_libs} ${_new_libs} ) set( ${_libs} ${${_libs}} PARENT_SCOPE ) # Recheck Compilation check_function_exists_w_results( "${${_libs}}" ${_pdpotrf_name} _compile_output _compile_result ) if( _compile_result ) message( STATUS "Performing Test ${_item} -- found" ) set( ${_link_ok} TRUE ) string( COMPARE EQUAL "${_uplo}" "LOWER" ${_uses_lower} ) string( COMPARE EQUAL "${_under}" "UNDERSCORE" ${_uses_underscore} ) break() else() message( STATUS "Performing Test ${_item} -- not found" ) endif() endif() endforeach() if( ${${_link_ok}} ) break() endif() unset( _pdpotrf_name_template ) unset( _pdpotrf_name_uplo ) endforeach() #cmake_pop_check_state() set( ${_link_ok} ${${_link_ok}} PARENT_SCOPE ) set( ${_uses_lower} ${${_uses_lower}} PARENT_SCOPE ) set( ${_uses_underscore} ${${_uses_underscore}} PARENT_SCOPE ) endfunction() #function( check_scalapack_int _libs _dsyev_name _libs_are_lp64 ) # #try_run( _run_result _compile_result ${CMAKE_CURRENT_BINARY_DIR} # SOURCES ${ScaLAPACK_UTILITY_CMAKE_FILE_DIR}/scalapack_ilp64_checker.c # LINK_LIBRARIES ${${_libs}} # COMPILE_DEFINITIONS "-DDSYEV_NAME=${_dsyev_name}" # COMPILE_OUTPUT_VARIABLE _compile_output # RUN_OUTPUT_VARIABLE _run_output #) # ##message( STATUS ${_run_result} ) # #if( ${_run_result} EQUAL 0 ) # set( ${_libs_are_lp64} TRUE PARENT_SCOPE ) #else() # set( ${_libs_are_lp64} FALSE PARENT_SCOPE ) #endif() # #endfunction() ga-5.9.2/cmake/linalg-modules/util/blis_int_size.c000066400000000000000000000040561500715745200221030ustar00rootroot00000000000000#if 0 //#define BLIS_PARAM_MACRO_DEFS_H //#define BLIS_OBJ_MACRO_DEFS_H //#define BLIS_MISC_MACRO_DEFS_H //#define BLIS_SCAL2RIS_MXN_H //#define BLIS_SCALRIS_MXN_UPLO_H //#define BLIS_ABSQR2_H //#define BLIS_ABVAL2S_H //#define BLIS_ADDS_H //#define BLIS_ADDJS_H //#define BLIS_ADD3S_H //#define BLIS_AXPBYS_H //#define BLIS_AXPBYJS_H //#define BLIS_AXPYS_H //#define BLIS_AXPYJS_H //#define BLIS_AXMYS_H //#define BLIS_CONJS_H //#define BLIS_COPYS_H //#define BLIS_COPYJS_H //#define BLIS_COPYCJS_H //#define BLIS_COPYNZS_H //#define BLIS_COPYJNZS_H //#define BLIS_ADDS_MXN_H #include //blis_version.c:(.text+0x304): undefined reference to `round' void* bli_thrcomm_bcast( dim_t inside_id, void* to_send, thrcomm_t* comm ) { } void bli_thrcomm_barrier( dim_t thread_id, thrcomm_t* comm ){ } void bli_thread_range_sub ( thrinfo_t* thread, dim_t n, dim_t bf, bool_t handle_edge_low, dim_t* start, dim_t* end ){ } BLIS_EXPORT_BLIS int bli_pthread_mutex_init ( bli_pthread_mutex_t* mutex, const bli_pthread_mutexattr_t* attr ){ } BLIS_EXPORT_BLIS int bli_pthread_mutex_destroy ( bli_pthread_mutex_t* mutex ){ } BLIS_EXPORT_BLIS int bli_pthread_mutex_lock ( bli_pthread_mutex_t* mutex ){ } BLIS_EXPORT_BLIS int bli_pthread_mutex_trylock ( bli_pthread_mutex_t* mutex ){ } BLIS_EXPORT_BLIS int bli_pthread_mutex_unlock ( bli_pthread_mutex_t* mutex ) { } void bli_abort(){ } void bli_obj_scalar_detach ( obj_t* a, obj_t* alpha ){ } bool_t bli_obj_imag_is_zero( obj_t* a ){ } double round( double x) {} int main() { int blis_int_size = BLIS_BLAS_INT_TYPE_SIZE; if( blis_int_size == 32 ) return 0; else return 1; } #else #if __has_include() #include #else #include #endif int main() { int blis_int_size = BLIS_BLAS_INT_TYPE_SIZE; if( blis_int_size == 32 ) return 0; else return 1; } #endif ga-5.9.2/cmake/linalg-modules/util/func_check.c000066400000000000000000000000741500715745200213320ustar00rootroot00000000000000void FUNC_NAME(); int main() { FUNC_NAME(); return 0; } ga-5.9.2/cmake/linalg-modules/util/get_mpi_vendor.c000066400000000000000000000003201500715745200222350ustar00rootroot00000000000000#include #include int main() { char LIB_NAME[MPI_MAX_LIBRARY_VERSION_STRING]; int result_len; MPI_Get_library_version(LIB_NAME, &result_len); printf("%s\n",LIB_NAME); return 0; } ga-5.9.2/cmake/linalg-modules/util/ilp64_checker.c000066400000000000000000000027271500715745200216730ustar00rootroot00000000000000#include #include #ifdef __cplusplus extern "C" { #endif void DGEMM_NAME(const char*, const char*, int32_t*,int32_t*,int32_t*, double*, double*, int32_t*, double*, int32_t*, double*, double*, int32_t*); #ifdef __cplusplus } #endif int main() { int32_t fake_two[2] = {2,1}; // Will be interpreted as 2 in LP64 and 2^32+2 in ILP64 int32_t true_two[2] = {2,0}; // Will be interpreted as 2 in both LP64 and ILP64 double A[4] = {1.,1.,1.,1.}; double B[4] = {1.,1.,1.,1.}; double C[4] = {0.,0.,0.,0.}; double zero = 0., one_d = 1.; int32_t* fake_two_ptr = fake_two; int32_t* true_two_ptr = true_two; // In an LP64 BLAS Library, the GEMM will be interpreted as N=M=K=LDA=LDB=LDA=2 // In an ILP64 BLAS Library, the GEMM will be interpreted as LDA=LDB=LDC=2 and M=N=K=LARGE // Because LDA < M, etc in ILP64, the GEMM fails by the standard // Valid for MKL, BLIS, OpenBLAS, NETLIB (Reference) BLAS, and ESSL DGEMM_NAME( "N","N", fake_two_ptr, fake_two_ptr, fake_two_ptr, &one_d, A, true_two_ptr, B, true_two_ptr, &zero, C, true_two_ptr ); // Print out result, will either be zero or it work print due to GEMM abort double sum = 0.; int i; for( i = 0; i < 4; ++i ) sum += C[i]; #if 0 if( ((int)sum) == 8 ) return 0; else return 1; #else if( ((int)sum) == 8 ) printf("XXXX BLAS IS LP64 XXXX\n" ); else printf("XXXX BLAS IS ILP64 XXXX\n"); #endif return 0; }; ga-5.9.2/cmake/linalg-modules/util/lapack_ilp64_checker.c000066400000000000000000000023711500715745200232010ustar00rootroot00000000000000#include #include #ifdef __cplusplus extern "C" { #endif void DSYEV_NAME(const char*, const char*, int32_t*, double*, int32_t*, double*, double*, int32_t*, int32_t*); #ifdef __cplusplus } #endif int main() { int32_t fake_two[2] = {2,1}; // Will be interpreted as 2 in LP64 and 2^32+2 in ILP64 int32_t true_two[2] = {2,0}; // Will be interpreted as 2 in both LP64 and ILP64 double A[4] = {1.,1.,1.,1.}; double W[2]; int32_t LWORK[2] = {-1,-1}; // Will be interpreted as -1 in both LP64 and ILP64 int32_t INFO[2]; // Will store the correct INFO in INFO[1] double WORK; int32_t* fake_two_ptr = fake_two; int32_t* true_two_ptr = true_two; // In an LP64 LAPACK Library, the SYEV will be interpreted as N=LDA=LDA=2 // In an ILP64 BLAS Library, the GEMM will be interpreted as LDA=2 and N=LARGE // Because LDA < N, etc in ILP64, the DSYEV fails by the standard // Valid for MKL, OpenBLAS, NETLIB (Reference) LAPACK, and ESSL DSYEV_NAME( "N","L", fake_two_ptr, A, true_two_ptr, W, &WORK, LWORK, INFO ); #if 0 if( INFO[0] == 0 ) return 0; else return -1; #else if( INFO[0] == 0 ) printf("XXXX LAPACK IS LP64 XXXX\n" ); else printf("XXXX LAPACK IS ILP64 XXXX\n"); #endif }; ga-5.9.2/cmake/linalg-modules/util/openblas_int_size.c000066400000000000000000000002421500715745200227460ustar00rootroot00000000000000#include int main() { int blis_int_size = sizeof(blasint)*8; if( blis_int_size == 32 ) return 0; else return 1; } ga-5.9.2/cmake/mafdecls.fh.in000066400000000000000000000106751500715745200157170ustar00rootroot00000000000000#ifndef _mafdecls_fh #define _mafdecls_fh ! ! $Id: mafdecls.fh,v 1.11 2002-09-14 05:40:30 d3g001 Exp $ ! ! ! Public header file for a portable dynamic memory allocator. ! ! This file may be included by internal and external FORTRAN files. ! #include "macommon.h" ! ! The guard ends here instead of at the end of the file because we only ! need the cpp constants (stuff above) defined once per FORTRAN file, ! but need the declarations (stuff below) to be defined each time this ! file is included in a FORTRAN file. ! #endif ! ! constants ! ! type declarations for datatype constants integer MT_BYTE ! byte integer MT_INT ! integer integer MT_LOG ! logical integer MT_REAL ! real integer MT_DBL ! double precision integer MT_SCPL ! single precision complex integer MT_DCPL ! double precision complex integer MT_F_FIRST ! first type integer MT_F_LAST ! last type ! parameter declarations for datatype constants parameter (MT_BYTE = MT_F_BYTE) parameter (MT_INT = MT_F_INT) parameter (MT_LOG = MT_F_LOG) parameter (MT_REAL = MT_F_REAL) parameter (MT_DBL = MT_F_DBL) parameter (MT_SCPL = MT_F_SCPL) parameter (MT_DCPL = MT_F_DCPL) parameter (MT_F_FIRST = MT_BYTE) parameter (MT_F_LAST = MT_DCPL) ! ! function types ! #ifndef MAF_INTERNAL logical MA_alloc_get logical MA_allocate_heap logical MA_chop_stack logical MA_free_heap logical MA_free_heap_piece logical MA_get_index logical MA_get_next_memhandle logical MA_get_numalign logical MA_init logical MA_initialized logical MA_init_memhandle_iterator integer MA_inquire_avail integer MA_inquire_heap integer MA_inquire_heap_check_stack integer MA_inquire_heap_no_partition integer MA_inquire_stack integer MA_inquire_stack_check_heap integer MA_inquire_stack_no_partition logical MA_pop_stack ! subroutine MA_print_stats logical MA_push_get logical MA_push_stack logical MA_set_auto_verify logical MA_set_error_print logical MA_set_hard_fail logical MA_set_numalign integer MA_sizeof integer MA_sizeof_overhead ! subroutine MA_summarize_allocated_blocks ! subroutine MA_trace logical MA_verify_allocator_stuff external MA_alloc_get external MA_allocate_heap external MA_chop_stack external MA_free_heap external MA_free_heap_piece external MA_get_index external MA_get_next_memhandle external MA_get_numalign external MA_init external MA_initialized external MA_init_memhandle_iterator external MA_inquire_avail external MA_inquire_heap external MA_inquire_heap_check_stack external MA_inquire_heap_no_partition external MA_inquire_stack external MA_inquire_stack_check_heap external MA_inquire_stack_no_partition external MA_pop_stack external MA_print_stats external MA_push_get external MA_push_stack external MA_set_auto_verify external MA_set_error_print external MA_set_hard_fail external MA_set_numalign external MA_sizeof external MA_sizeof_overhead external MA_summarize_allocated_blocks external MA_trace external MA_verify_allocator_stuff #endif ! ! variables ! ! common blocks #ifdef INTEL_64ALIGN !DIR$ ATTRIBUTES ALIGN : 64 :: mbc_byte #endif common /mbc_byte/ byte_mb(2) character*1 byte_mb #ifdef INTEL_64ALIGN !DIR$ ATTRIBUTES ALIGN : 64 :: mbc_int #endif common /mbc_int/ int_mb(2) integer int_mb common /mbc_log/ log_mb(2) logical log_mb #ifdef INTEL_64ALIGN !DIR$ ATTRIBUTES ALIGN : 64 :: mbc_real #endif common /mbc_real/ real_mb(2) real real_mb #ifdef INTEL_64ALIGN !DIR$ ATTRIBUTES ALIGN : 64 :: mbc_dbl #endif common /mbc_dbl/ dbl_mb(2) double precision dbl_mb #ifdef INTEL_64ALIGN !DIR$ ATTRIBUTES ALIGN : 64 :: mbc_scpl #endif common /mbc_scpl/ scpl_mb(2) complex scpl_mb #ifdef INTEL_64ALIGN !DIR$ ATTRIBUTES ALIGN : 64 :: mbc_dcpl #endif common /mbc_dcpl/ dcpl_mb(2) double complex dcpl_mb #define MA_ACCESS_INDEX_TYPE ${GA_ACCESS_INDEX_TYPE} #define MAPOINTER ${GA_ACCESS_INDEX_TYPE} ga-5.9.2/cmake/matypes.h.in000066400000000000000000000014761500715745200154540ustar00rootroot00000000000000/** @file * Private header file containing C type definitions. * * This file should only be included directly by internal C * header files (e.g., macdecls.h). It may be included indirectly * by external C files that include the appropriate header * file (e.g., macdecls.h). */ #ifndef _MATYPES_H #define _MATYPES_H /** ** types **/ #include "typesf2c.h" typedef Integer Boolean; /* MA_TRUE or MA_FALSE */ typedef char * Pointer; /* generic pointer */ /* not all C compilers support long double */ typedef ${MA_LONG_DOUBLE} MA_LongDouble; /* no C compilers support complex types */ typedef struct {float dummy[2];} MA_SingleComplex; typedef struct {double dummy[2];} MA_DoubleComplex; typedef struct {double dummy[4];} MA_LongDoubleComplex; typedef ${C_POINTER_AS_INTEGER} MA_AccessIndex; #endif /* _matypes_h */ ga-5.9.2/cmake/typesf2c.h.in000066400000000000000000000010311500715745200155140ustar00rootroot00000000000000#ifndef TYPESF2C_H_ #define TYPESF2C_H_ #if defined(WIN32) &&!defined(__MINGW32__) # define FATR __stdcall #else # define FATR #endif typedef ${F2C_INTEGER_C_TYPE} Integer; typedef ${F2C_REAL_C_TYPE} Real; typedef ${F2C_DOUBLE_PRECISION_C_TYPE} DoublePrecision; typedef Integer logical; typedef Integer Logical; typedef struct { DoublePrecision real; DoublePrecision imag; } DoubleComplex; typedef struct { Real real; Real imag; } SingleComplex; typedef ${C_POINTER_AS_INTEGER} intp; #endif /* TYPESF2C_H_ */ ga-5.9.2/cmx/000077500000000000000000000000001500715745200127135ustar00rootroot00000000000000ga-5.9.2/cmx/CMakeLists.txt000066400000000000000000000335701500715745200154630ustar00rootroot00000000000000# # module: CMakeLists.txt # author: Bruce Palmer # description: implements a primative CMake build that can be used to build # GXA on Windows-based systems. Only MPI-based runtimes are # supported. # # DISCLAIMER # # This material was prepared as an account of work sponsored by an # agency of the United States Government. Neither the United States # Government nor the United States Department of Energy, nor Battelle, # nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR # ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, # COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, # SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT # INFRINGE PRIVATELY OWNED RIGHTS. # # # ACKNOWLEDGMENT # # This software and its documentation were produced with United States # Government support under Contract Number DE-AC06-76RLO-1830 awarded by # the United States Department of Energy. The United States Government # retains a paid-up non-exclusive, irrevocable worldwide license to # reproduce, prepare derivative works, perform publicly and display # publicly by or for the US Government, including the right to # distribute to other US Government contractors. # # -*- mode: cmake -*- # ------------------------------------------------------------- # file: CMakeLists.txt # ------------------------------------------------------------- cmake_minimum_required (VERSION 2.8.8) project (CMX) option (CMX_RUNTIME, "Protocols using to build CMX" MPI_RMA) option (USE_MPI_RMA "use MPI RMA protocols for communication" OFF) option (USE_MPI_PR "use MPI progress ranks for communication" OFF) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} src-common src-armci) message (STATUS "CMX_RUNTIME: ${CMX_RUNTIME}") if (CMX_RUNTIME STREQUAL "MPI_RMA") message(STATUS "Found MPI_RMA") set (USE_MPI_RMA ON) elseif (CMX_RUNTIME STREQUAL "MPI_PR") message(STATUS "Found MPI_PR") set (USE_MPI_PR ON) else() message(STATUS "Found default MPI_RMA") set (USE_MPI_RMA ON) endif () message (STATUS "USE_MPI_RMA: ${USE_MPI_RMA}") message (STATUS "USE_MPI_PR: ${USE_MPI_PR}") if (USE_MPI_RMA) set(CMX_DEVICE src-mpi-rma/cmx.c src-mpi-rma/groups.c ) set (CMX_NETWORK_MPI_RMA ON) include_directories(AFTER src-mpi-rma) elseif (USE_MPI_PR) set(CMX_DEVICE src-mpi-pr/cmx.c src-mpi-pr/groups.c src-mpi-pr/reg_cache.c ) set (CMX_NETWORK_MPI_PR ON) include_directories(AFTER src-mpi-pr) endif() set(ARMCI_API src-armci/armci.c src-armci/capi.c src-armci/groups.c src-armci/iterator.c src-armci/message.c src-armci/reg_entry.c ) find_package(MPI) add_definitions (-DHAVE_CONFIG_H=1) # ------------------------------------------------------------- # Check for variable sizes, include files and functions # ------------------------------------------------------------- # check size of different variables include(CheckTypeSize) check_type_size("int" CM_SIZEOF_INT) check_type_size("double" CM_SIZEOF_DOUBLE) check_type_size("float" CM_SIZEOF_FLOAT) check_type_size("long" CM_SIZEOF_LONG) check_type_size("long double" CM_SIZEOF_LONG_DOUBLE) check_type_size("long long" CM_SIZEOF_LONG_LONG) check_type_size("short" CM_SIZEOF_SHORT) # check for standard C/C++ include files include(CheckIncludeFiles) check_include_files("assert.h" HAVE_ASSERT_H) check_include_files("limits.h" HAVE_LIMITS_H) check_include_files("linux/limits.h" HAVE_LINUX_LIMITS_H) check_include_files("malloc.h" HAVE_MALLOC_H) check_include_files("math.h" HAVE_MATH_H) check_include_files("stddef.h" HAVE_STDDEF_H) check_include_files("stdint.h" HAVE_STDINT_H) check_include_files("stdio.h" HAVE_STDIO_H) check_include_files("stdlib.h" HAVE_STDLIB_H) check_include_files("strings.h" HAVE_STRINGS_H) check_include_files("string.h" HAVE_STRING_H) check_include_files("sys/types.h" HAVE_SYS_TYPES_H) check_include_files("unistd.h" HAVE_UNISTD_H) check_include_files("windows.h" HAVE_WINDOWS_H) # check for certain functions include(CheckFunctionExists) check_function_exists("bzero" HAVE_BZERO) # check to see if rt library exists. If so, add -lrt to GA_EXTRA_LIBS find_library(LIBRT rt PATHS /lib /usr/lib /usr/local/lib) if (LIBRT) message(STATUS "VALUE OF LIBRT: ${LIBRT}") # if (CMX_EXTRA_LIBS) # set (CMX_EXTRA_LIBS "${CMX_EXTRA_LIBS} rt") # else() # set (CMX_EXTRA_LIBS rt) # endif() # set (CMAKE_REQUIRED_LIBRARIES "rt ${CMAKE_REQUIRED_LIBRARIES}") # message(STATUS "CMAKE_REQUIRED_LIBRARIES: ${CMAKE_REQUIRED_LIBRARIES}") list(APPEND CMX_EXTRA_LIBS rt) else() message(STATUS "NO LIBRT FOUND") endif() # ------------------------------------------------------------- # figure out what BLAS library looks like # ------------------------------------------------------------- if (NOT DEFINED ENABLE_BLAS) option (ENABLE_BLAS "Include external BLAS" ON) endif() INCLUDE( CheckCSourceCompiles ) if (ENABLE_BLAS) set (CMAKE_REQUIRED_LIBRARIES "blas ${CMAKE_REQUIRED_LIBRARIES}") CHECK_C_SOURCE_COMPILES( " int main(int argc, char *argv[]) { typedef struct {float dummy[2];} SingleComplex; typedef struct {double dummy[2];} DoubleComplex; int n = 100; SingleComplex c1,ca[100],cb[100]; double d1,da[100],db[100]; float s1,sa[100],sb[100]; DoubleComplex z1,za[100],zb[100]; char caxpy_result = caxpy (n,c1,ca,1,cb,1); char daxpy_result = daxpy (n,d1,da,1,db,1); char saxpy_result = saxpy (n,s1,sa,1,sb,1); char zaxpy_result = zaxpy (n,z1,za,1,zb,1); char ccopy_result = ccopy (n,c1,ca,1,cb,1); char dcopy_result = dcopy (n,d1,da,1,db,1); char scopy_result = scopy (n,s1,sa,1,sb,1); char zcopy_result = zcopy (n,z1,za,1,zb,1); return 0; } " BLAS_1_SIGNATURE ) CHECK_C_SOURCE_COMPILES( " int main(int argc, char *argv[]) { typedef struct {float dummy[2];} SingleComplex; typedef struct {double dummy[2];} DoubleComplex; int n = 100; SingleComplex c1,ca[100],cb[100]; double d1,da[100],db[100]; float s1,sa[100],sb[100]; DoubleComplex z1,za[100],zb[100]; char caxpy_result = caxpy_ (n,c1,ca,1,cb,1); char daxpy_result = daxpy_ (n,d1,da,1,db,1); char saxpy_result = saxpy_ (n,s1,sa,1,sb,1); char zaxpy_result = zaxpy_ (n,z1,za,1,zb,1); char ccopy_result = ccopy_ (n,c1,ca,1,cb,1); char dcopy_result = dcopy_ (n,d1,da,1,db,1); char scopy_result = scopy_ (n,s1,sa,1,sb,1); char zcopy_result = zcopy_ (n,z1,za,1,zb,1); return 0; } " BLAS_2_SIGNATURE ) CHECK_C_SOURCE_COMPILES( " int main(int argc, char *argv[]) { typedef struct {float dummy[2];} SingleComplex; typedef struct {double dummy[2];} DoubleComplex; int n = 100; SingleComplex c1,ca[100],cb[100]; double d1,da[100],db[100]; float s1,sa[100],sb[100]; DoubleComplex z1,za[100],zb[100]; char caxpy_result = caxpy__ (n,c1,ca,1,cb,1); char daxpy_result = daxpy__ (n,d1,da,1,db,1); char saxpy_result = saxpy__ (n,s1,sa,1,sb,1); char zaxpy_result = zaxpy__ (n,z1,za,1,zb,1); char ccopy_result = ccopy__ (n,c1,ca,1,cb,1); char dcopy_result = dcopy__ (n,d1,da,1,db,1); char scopy_result = scopy__ (n,s1,sa,1,sb,1); char zcopy_result = zcopy__ (n,z1,za,1,zb,1); return 0; } " BLAS_3_SIGNATURE ) CHECK_C_SOURCE_COMPILES( " int main(int argc, char *argv[]) { typedef struct {float dummy[2];} SingleComplex; typedef struct {double dummy[2];} DoubleComplex; int n = 100; SingleComplex c1,ca[100],cb[100]; double d1,da[100],db[100]; float s1,sa[100],sb[100]; DoubleComplex z1,za[100],zb[100]; char caxpy_result = CAXPY (n,c1,ca,1,cb,1); char daxpy_result = DAXPY (n,d1,da,1,db,1); char saxpy_result = SAXPY (n,s1,sa,1,sb,1); char zaxpy_result = ZAXPY (n,z1,za,1,zb,1); char ccopy_result = CCOPY (n,c1,ca,1,cb,1); char dcopy_result = DCOPY (n,d1,da,1,db,1); char scopy_result = SCOPY (n,s1,sa,1,sb,1); char zcopy_result = ZCOPY (n,z1,za,1,zb,1); return 0; } " BLAS_4_SIGNATURE ) CHECK_C_SOURCE_COMPILES( " int main(int argc, char *argv[]) { typedef struct {float dummy[2];} SingleComplex; typedef struct {double dummy[2];} DoubleComplex; int n = 100; SingleComplex c1,ca[100],cb[100]; double d1,da[100],db[100]; float s1,sa[100],sb[100]; DoubleComplex z1,za[100],zb[100]; char caxpy_result = CAXPY_ (n,c1,ca,1,cb,1); char daxpy_result = DAXPY_ (n,d1,da,1,db,1); char saxpy_result = SAXPY_ (n,s1,sa,1,sb,1); char zaxpy_result = ZAXPY_ (n,z1,za,1,zb,1); char ccopy_result = CCOPY_ (n,c1,ca,1,cb,1); char dcopy_result = DCOPY_ (n,d1,da,1,db,1); char scopy_result = SCOPY_ (n,s1,sa,1,sb,1); char zcopy_result = ZCOPY_ (n,z1,za,1,zb,1); return 0; } " BLAS_5_SIGNATURE ) CHECK_C_SOURCE_COMPILES( " int main(int argc, char *argv[]) { typedef struct {float dummy[2];} SingleComplex; typedef struct {double dummy[2];} DoubleComplex; int n = 100; SingleComplex c1,ca[100],cb[100]; double d1,da[100],db[100]; float s1,sa[100],sb[100]; DoubleComplex z1,za[100],zb[100]; char caxpy_result = CAXPY__ (n,c1,ca,1,cb,1); char daxpy_result = DAXPY__ (n,d1,da,1,db,1); char saxpy_result = SAXPY__ (n,s1,sa,1,sb,1); char zaxpy_result = ZAXPY__ (n,z1,za,1,zb,1); char ccopy_result = CCOPY__ (n,c1,ca,1,cb,1); char dcopy_result = DCOPY__ (n,d1,da,1,db,1); char scopy_result = SCOPY__ (n,s1,sa,1,sb,1); char zcopy_result = ZCOPY__ (n,z1,za,1,zb,1); return 0; } " BLAS_6_SIGNATURE ) endif() # set blas symbols if (BLAS_1_SIGNATURE) set(CM_BLAS_CAXPY caxpy) set(CM_BLAS_DAXPY daxpy) set(CM_BLAS_SAXPY saxpy) set(CM_BLAS_ZAXPY zaxpy) set(CM_BLAS_CCOPY ccopy) set(CM_BLAS_DCOPY dcopy) set(CM_BLAS_SCOPY scopy) set(CM_BLAS_ZCOPY zcopy) elseif (BLAS_2_SIGNATURE) set(CM_BLAS_CAXPY caxpy_) set(CM_BLAS_DAXPY daxpy_) set(CM_BLAS_SAXPY saxpy_) set(CM_BLAS_ZAXPY zaxpy_) set(CM_BLAS_CCOPY ccopy_) set(CM_BLAS_DCOPY dcopy_) set(CM_BLAS_SCOPY scopy_) set(CM_BLAS_ZCOPY zcopy_) elseif (BLAS_3_SIGNATURE) set(CM_BLAS_CAXPY caxpy__) set(CM_BLAS_DAXPY daxpy__) set(CM_BLAS_SAXPY saxpy__) set(CM_BLAS_ZAXPY zaxpy__) set(CM_BLAS_CCOPY ccopy__) set(CM_BLAS_DCOPY dcopy__) set(CM_BLAS_SCOPY scopy__) set(CM_BLAS_ZCOPY zcopy__) elseif (BLAS_4_SIGNATURE) set(CM_BLAS_CAXPY CAXPY) set(CM_BLAS_DAXPY DAXPY) set(CM_BLAS_SAXPY SAXPY) set(CM_BLAS_ZAXPY ZAXPY) set(CM_BLAS_CCOPY CCOPY) set(CM_BLAS_DCOPY DCOPY) set(CM_BLAS_SCOPY SCOPY) set(CM_BLAS_ZCOPY ZCOPY) elseif (BLAS_5_SIGNATURE) set(CM_BLAS_CAXPY CAXPY_) set(CM_BLAS_DAXPY DAXPY_) set(CM_BLAS_SAXPY SAXPY_) set(CM_BLAS_ZAXPY ZAXPY_) set(CM_BLAS_CCOPY CCOPY_) set(CM_BLAS_DCOPY DCOPY_) set(CM_BLAS_SCOPY SCOPY_) set(CM_BLAS_ZCOPY ZCOPY_) elseif (BLAS_6_SIGNATURE) set(CM_BLAS_CAXPY CAXPY__) set(CM_BLAS_DAXPY DAXPY__) set(CM_BLAS_SAXPY SAXPY__) set(CM_BLAS_ZAXPY ZAXPY__) set(CM_BLAS_CCOPY CCOPY__) set(CM_BLAS_DCOPY DCOPY__) set(CM_BLAS_SCOPY SCOPY__) set(CM_BLAS_ZCOPY ZCOPY__) else() set(CM_BLAS_CAXPY caxpy_) set(CM_BLAS_DAXPY daxpy_) set(CM_BLAS_SAXPY saxpy_) set(CM_BLAS_ZAXPY zaxpy_) set(CM_BLAS_CCOPY ccopy_) set(CM_BLAS_DCOPY dcopy_) set(CM_BLAS_SCOPY scopy_) set(CM_BLAS_ZCOPY zcopy_) endif() # check for weak alias pragma CHECK_C_SOURCE_COMPILES( " extern void weakf(int c); #pragma weak weakf = __weakf void __weakf(int c) {} int main(int argc, char **argv) { weakf(0); return(0); } " HAVE_SYS_WEAK_ALIAS_PRAGMA ) # Check for FUNCTION_NAME CHECK_C_SOURCE_COMPILES( " #include int sub(int i) { printf(\"%s\", __func__); } int main(int argc, char *argv[]) { sub(0); return 0; } " FUNCTION_1_SIGNATURE ) CHECK_C_SOURCE_COMPILES( " #include int sub(int i) { printf(\"%s\", __FUNCTION__); } int main(int argc, char *argv[]) { sub(0); return 0; } " FUNCTION_2_SIGNATURE ) if (FUNCTION_1_SIGNATURE) set(CM_FUNCTION_NAME __func__) elseif(FUNCTION_2_SIGNATURE) set(CM_FUNCTION_NAME __FUNCTION__) else() set(CM_FUNCTION_NAME __func__) endif() # check for availability of some functions include(CheckFunctionExists) CHECK_FUNCTION_EXISTS(sched_setaffinity HAVE_SCHED_SETAFFINITY) CHECK_FUNCTION_EXISTS(pthread_setaffinity_np HAVE_PTHREAD_SETAFFINITY_NP) # ------------------------------------------------------------- # CMX header installation # ------------------------------------------------------------- set(CMX_DEVICE_HEADERS src-common/cmx.h ) install (FILES ${CMX_DEVICE_HEADERS} DESTINATION include/ga ) list (APPEND GA_HEADER_PATHS ${CMAKE_CURRENT_LIST_DIR}/src-common) # ------------------------------------------------------------- # CMX library installation # ------------------------------------------------------------- add_library(cmx ${CMX_DEVICE} ) message(STATUS "CMX_EXTRA_LIBS: (${CMX_EXTRA_LIBS})") target_link_libraries(cmx PUBLIC ${CMX_EXTRA_LIBS}) install (TARGETS cmx DESTINATION lib ) # ------------------------------------------------------------- # ARMCI header installation # ------------------------------------------------------------- set(ARMCI_DEVICE_HEADERS src-armci/armci.h src-armci/message.h ) install (FILES ${ARMCI_DEVICE_HEADERS} DESTINATION include/ga ) list (APPEND GA_HEADER_PATHS ${CMAKE_CURRENT_LIST_DIR}/src-armci) set (GA_HEADER_PATHS ${GA_HEADER_PATHS} PARENT_SCOPE) # ------------------------------------------------------------- # ARMCI library installation # ------------------------------------------------------------- add_library(armci ${ARMCI_API} ${CMX_DEVICE} ) target_link_libraries(armci) install (TARGETS armci DESTINATION lib ) # ------------------------------------------------------------- # CMX test programs # ------------------------------------------------------------- add_executable(test.x testing/test.c) target_link_libraries(test.x cmx) add_executable(perf_amo.x testing/perf_amo.c) target_link_libraries(perf_amo.x cmx) add_executable(perf.x testing/perf.c) target_link_libraries(perf.x cmx) add_executable(perf_contig.x testing/perf_contig.c) target_link_libraries(perf_contig.x cmx) add_executable(perf_strided.x testing/perf_strided.c) target_link_libraries(perf_strided.x cmx) add_executable(shift.x testing/shift.c) target_link_libraries(shift.x cmx) # ------------------------------------------------------------- # ARMCI test programs # ------------------------------------------------------------- add_executable(testwrap.x src-armci/testing/testwrap.c) target_link_libraries(testwrap.x armci rt) ga-5.9.2/cmx/src-armci/000077500000000000000000000000001500715745200145735ustar00rootroot00000000000000ga-5.9.2/cmx/src-armci/armci.c000066400000000000000000000752661500715745200160520ustar00rootroot00000000000000/* #if HAVE_CONFIG_H # include "config.h" #endif */ #include #include #include #include "armci.h" #include "parmci.h" #include "cmx.h" #include "reg_entry.h" #include "groups.h" typedef struct _nb_t { struct _nb_t *next; cmx_request_t request; int id; } nb_t; extern int ARMCI_Default_Proc_Group; /** * Set up some data structures for non-blocking communication */ static nb_t *_nb_list = NULL; /** * Useful variable for debugging */ static int _armci_me; /** * This function checks to see if the data copy is contiguous for both the src * and destination buffers. If it is, then a contiguous operation can be used * instead of a strided operation. This function is intended for arrays of * dimension greater than 1 (contiguous operations can always be used for 1 * dimensional arrays). This operation does not identify all contiguous cases, * since no information is available about the last dimension. * src_stride: physical dimensions of source buffer * dst_stride: physical dimensions of destination buffer * count: number of elements being moved in each dimension * n_stride: number of strides (array dimension minus one) */ int armci_check_contiguous(int *src_stride, int *dst_stride, int *count, int n_stride) { int i; int ret = 1; int stridelen = 1; /* NOTE: The count array contains the length of the final dimension and could * be used to evaluate some corner cases that are not picked up by this * algorithm */ for (i=0; iid > maxID) maxID = curr_req->id; prev_req = curr_req; curr_req = curr_req->next; } maxID++; *req = (nb_t*)malloc(sizeof(nb_t)); (*req)->id = maxID; (*req)->next = NULL; *handle = maxID; if (prev_req) { prev_req->next = *req; } else { _nb_list = *req; } } /** * Delete a non-blocking request and remove it from the link list */ void delete_nb_request(armci_hdl_t *handle) { nb_t *curr_req = _nb_list; nb_t *prev_req = NULL; while (curr_req != NULL) { if (curr_req->id == *handle) break; prev_req = curr_req; curr_req = curr_req->next; } if (curr_req == NULL) { printf("Could not find request handle for delete\n"); } else { if (prev_req != NULL) { prev_req->next = curr_req->next; } if (_nb_list == curr_req) _nb_list = curr_req->next; free(curr_req); } } /** * Recover a non-blocking request from the handle */ nb_t* find_nb_request(armci_hdl_t *handle) { nb_t *curr_req = _nb_list; while (curr_req != NULL) { if (curr_req->id == *handle) return curr_req; curr_req = curr_req->next; } return NULL; } int PARMCI_Acc(int optype, void *scale, void *src, void *dst, int bytes, int proc) { reg_entry_t *entry; void *buf; MPI_Aint offset; int lproc; entry = reg_entry_find(proc,dst,bytes); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); optype = convert_optype(optype); offset = (MPI_Aint)dst-(MPI_Aint)buf; return cmx_acc(optype, scale, src, offset, bytes, lproc, *(entry->hdl)); } int PARMCI_AccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { int iret; reg_entry_t *entry; void *buf; MPI_Aint offset; int lproc; entry = reg_entry_find(proc,dst_ptr,0); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); optype = convert_optype(optype); offset = (MPI_Aint)dst_ptr-(MPI_Aint)buf; /* check if data is contiguous */ if (armci_check_contiguous(src_stride_arr, dst_stride_arr, count, stride_levels)) { int i; int lcount = 1; for (i=0; i<=stride_levels; i++) lcount *= count[i]; iret = cmx_acc(optype, scale, src_ptr, offset, lcount, lproc, *(entry->hdl)); } else { iret = cmx_accs(optype, scale, src_ptr, src_stride_arr, offset, dst_stride_arr, count, stride_levels, lproc, *(entry->hdl)); } return iret; } int PARMCI_AccV(int op, void *scale, armci_giov_t *darr, int len, int proc) { int rc; reg_entry_t *entry; void *buf; cmx_giov_t *adarr = malloc(sizeof(cmx_giov_t) * len); int lproc; /* find location of buffer on remote processor. Start by finding a buffer * location on the remote array */ buf = (darr[0].dst_ptr_array)[0]; entry = reg_entry_find(proc,buf,0); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); convert_giov(darr, adarr, len, buf, 1); op = convert_optype(op); rc = cmx_accv(op, scale, adarr, len, lproc, *(entry->hdl)); free_giov(adarr, len); return rc; } /* fence is always on the world group */ void PARMCI_AllFence() { int ierr = cmx_fence_all(CMX_GROUP_WORLD); assert(CMX_SUCCESS == ierr); } void PARMCI_Barrier() { int ierr; cmx_group_t grp = armci_get_cmx_group(ARMCI_Default_Proc_Group); ierr = cmx_barrier(grp); assert(CMX_SUCCESS == ierr); } int PARMCI_Create_mutexes(int num) { return cmx_create_mutexes(num); } int PARMCI_Destroy_mutexes() { return cmx_destroy_mutexes(); } /* fence is always on the world group */ void PARMCI_Fence(int proc) { cmx_fence_proc(proc, CMX_GROUP_WORLD); } void PARMCI_Finalize() { int i; reg_entries_destroy(); armci_group_finalize(); cmx_finalize(); } int PARMCI_Free(void *ptr) { reg_entry_t *entry; int rank, size, ierr, i; void **allgather_ptrs = NULL; void *buf; MPI_Comm comm; cmx_group_rank(CMX_GROUP_WORLD, &rank); cmx_group_size(CMX_GROUP_WORLD, &size); cmx_group_comm(CMX_GROUP_WORLD, &comm); entry = reg_entry_find(rank,ptr,0); buf = entry->buf; ierr = cmx_free(*(entry->hdl)); /* Need to clean up all entries corresponding to this allocation * in reg_entry lists. Allocate receive buffer for exchange of pointers */ allgather_ptrs = (void **)malloc(sizeof(void *) * size); assert(allgather_ptrs); /* exchange pointers */ MPI_Allgather(&buf, sizeof(void*), MPI_BYTE, allgather_ptrs, sizeof(void*), MPI_BYTE, comm); for (i=0; ibuf; ierr = cmx_free(*(entry->hdl)); /* Need to clean up all entries corresponding to this allocation * in reg_entry lists. Allocate receive buffer for exchange of pointers */ allgather_ptrs = (void **)malloc(sizeof(void *) * size); assert(allgather_ptrs); /* exchange pointers */ MPI_Allgather(&buf, sizeof(void*), MPI_BYTE, allgather_ptrs, sizeof(void*), MPI_BYTE, comm); for (i=0; ibuf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); offset = (MPI_Aint)src-(MPI_Aint)buf; return cmx_get(dst, offset, bytes, lproc, *(entry->hdl)); } int PARMCI_GetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { int iret; reg_entry_t *entry; void *buf; MPI_Aint offset; int lproc; entry = reg_entry_find(proc,src_ptr,0); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); offset = (MPI_Aint)src_ptr-(MPI_Aint)buf; /* check if data is contiguous */ if (armci_check_contiguous(src_stride_arr, dst_stride_arr, count, stride_levels)) { int i; int lcount = 1; for (i=0; i<=stride_levels; i++) lcount *= count[i]; iret = cmx_get(dst_ptr, offset, lcount, lproc, *(entry->hdl)); } else { iret = cmx_gets(dst_ptr, dst_stride_arr, offset, src_stride_arr, count, stride_levels, lproc, *(entry->hdl)); } return iret; } int PARMCI_GetV(armci_giov_t *darr, int len, int proc) { int rc; cmx_giov_t *adarr = malloc(sizeof(cmx_giov_t) * len); reg_entry_t *entry; void *buf; int lproc; /* find location of buffer on remote processor. Start by finding a buffer * location on the remote array */ buf = (darr[0].src_ptr_array)[0]; entry = reg_entry_find(proc,buf,0); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); convert_giov(darr, adarr, len, buf, 0); rc = cmx_getv(adarr, len, lproc, *(entry->hdl)); free_giov(adarr, len); return rc; } double PARMCI_GetValueDouble(void *src, int proc) { int ierr; double val; ierr = PARMCI_Get(src, &val, sizeof(double), proc); assert(CMX_SUCCESS == ierr); return val; } float PARMCI_GetValueFloat(void *src, int proc) { int ierr; float val; ierr = PARMCI_Get(src, &val, sizeof(float), proc); assert(CMX_SUCCESS == ierr); return val; } int PARMCI_GetValueInt(void *src, int proc) { int ierr; int val; ierr = PARMCI_Get(src, &val, sizeof(int), proc); assert(CMX_SUCCESS == ierr); return val; } long PARMCI_GetValueLong(void *src, int proc) { int ierr; long val; ierr = PARMCI_Get(src, &val, sizeof(long), proc); assert(CMX_SUCCESS == ierr); return val; } int PARMCI_Init() { int i, size; int rc = cmx_init(); armci_group_init(); cmx_group_size(CMX_GROUP_WORLD,&size); cmx_group_rank(CMX_GROUP_WORLD,&_armci_me); reg_entry_init(size); return rc; } int PARMCI_Init_args(int *argc, char ***argv) { int i, size; int rc = cmx_init_args(argc, argv); armci_group_init(); cmx_group_size(CMX_GROUP_WORLD,&size); cmx_group_rank(CMX_GROUP_WORLD,&_armci_me); reg_entry_init(size); return rc; } int PARMCI_Initialized() { return cmx_initialized(); } void PARMCI_Lock(int mutex, int proc) { cmx_lock(mutex, proc); } int ARMCI_Malloc_group(void **ptr_arr, armci_size_t bytes, ARMCI_Group *group) { cmx_handle_t *handle = NULL; reg_entry_t *reg_entries = NULL; reg_entry_t entry; MPI_Comm comm = MPI_COMM_NULL; void *buf; armci_igroup_t *igroup; int i, ret; int comm_rank = -1; int comm_size = -1; int tsize; cmx_group_t cmx_grp; igroup = armci_get_igroup_from_group(*group); cmx_grp = igroup->group; handle = (cmx_handle_t*)malloc(sizeof(cmx_handle_t)); /* ptr_array should already have been allocated externally */ CMX_ASSERT(ptr_arr); cmx_group_comm(cmx_grp, &comm); assert(comm != MPI_COMM_NULL); MPI_Comm_rank(comm, &comm_rank); MPI_Comm_size(comm, &comm_size); /* allocate ret_entry_t object for this process */ reg_entries = malloc(sizeof(reg_entry_t)*comm_size); ret = cmx_malloc(handle, bytes, cmx_grp); cmx_access(*handle,&buf); reg_entries[comm_rank].buf = buf; reg_entries[comm_rank].rank = comm_rank; reg_entries[comm_rank].len = bytes; reg_entries[comm_rank].hdl = handle; /* exchange buffer address */ memcpy(&entry, ®_entries[comm_rank], sizeof(reg_entry_t)); MPI_Allgather(&entry, sizeof(reg_entry_t), MPI_BYTE, reg_entries, sizeof(reg_entry_t), MPI_BYTE, comm); for (i=0; icpid==rank) { ptr = meminfo->addr; return ptr; } ptr = meminfo->addr; return ptr; } void PARMCI_Memget(size_t bytes, armci_meminfo_t *meminfo, int memflg) { void *myptr=NULL; void *armci_ptr=NULL; /* legal ARCMI ptr used in ARMCI data xfer ops*/ size_t size = bytes; int rank; cmx_group_rank(CMX_GROUP_WORLD, &rank); if(size<=0) cmx_error("PARMCI_Memget: size must be > 0", (int)size); if(meminfo==NULL) cmx_error("PARMCI_Memget: Invalid arg #2 (NULL ptr)",0); if(memflg!=0) cmx_error("PARMCI_Memget: Invalid memflg", memflg); armci_ptr = myptr = cmx_malloc_local(size); if(size) if(!myptr) cmx_error("PARMCI_Memget failed", (int)size); /* fill the meminfo structure */ meminfo->armci_addr = armci_ptr; meminfo->addr = myptr; meminfo->size = size; meminfo->cpid = rank; /* meminfo->attr = NULL; */ } void PARMCI_Memdt(armci_meminfo_t *meminfo, long offset) { } void PARMCI_Memctl(armci_meminfo_t *meminfo) { int rank; cmx_group_rank(CMX_GROUP_WORLD, &rank); if(meminfo==NULL) cmx_error("PARMCI_Memget: Invalid arg #2 (NULL ptr)",0); /* only the creator can delete the segment */ if(meminfo->cpid == rank) { void *ptr = meminfo->addr; cmx_free_local(ptr); } meminfo->addr = NULL; meminfo->armci_addr = NULL; /* if(meminfo->attr!=NULL) free(meminfo->attr); */ } int PARMCI_NbAcc(int optype, void *scale, void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { int iret; reg_entry_t *entry; void *buf; MPI_Aint offset; nb_t *req; int lproc; if (nb_handle == NULL) { return PARMCI_Acc(optype, scale, src, dst, bytes, proc); } entry = reg_entry_find(proc,dst,bytes); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); optype = convert_optype(optype); offset = (MPI_Aint)dst-(MPI_Aint)buf; get_nb_request(nb_handle, &req); iret = cmx_nbacc(optype, scale, src, offset, bytes, lproc, *(entry->hdl), &(req->request)); return iret; } int PARMCI_NbAccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { int iret; reg_entry_t *entry; void *buf; MPI_Aint offset; nb_t *req; int lproc; if (nb_handle == NULL) { return PARMCI_AccS(optype, scale, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); } entry = reg_entry_find(proc,dst_ptr,0); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); optype = convert_optype(optype); offset = (MPI_Aint)dst_ptr-(MPI_Aint)buf; get_nb_request(nb_handle, &req); /* check if data is contiguous */ if (armci_check_contiguous(src_stride_arr, dst_stride_arr, count, stride_levels)) { int i; int lcount = 1; for (i=0; i<=stride_levels; i++) lcount *= count[i]; iret = cmx_nbacc(optype, scale, src_ptr, offset, lcount, lproc, *(entry->hdl), &(req->request)); } else { iret = cmx_nbaccs(optype, scale, src_ptr, src_stride_arr, offset, dst_stride_arr, count, stride_levels, lproc, *(entry->hdl), &(req->request)); } return iret; } int PARMCI_NbAccV(int op, void *scale, armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { int rc; reg_entry_t *entry; void *buf; nb_t *req; cmx_giov_t *adarr; int lproc; if (nb_handle == NULL) { return PARMCI_AccV(op, scale, darr, len, proc); } adarr = malloc(sizeof(cmx_giov_t) * len); /* find location of buffer on remote processor. Start by finding a buffer * location on the remote array */ buf = (darr[0].dst_ptr_array)[0]; entry = reg_entry_find(proc,buf,0); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); convert_giov(darr, adarr, len, buf, 1); op = convert_optype(op); get_nb_request(nb_handle, &req); rc = cmx_nbaccv(op, scale, adarr, len, lproc, *(entry->hdl), &(req->request)); free_giov(adarr, len); return rc; } int PARMCI_NbGet(void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { int iret; reg_entry_t *entry; void *buf; MPI_Aint offset; nb_t *req; int lproc; if (nb_handle == NULL) { return PARMCI_Get(src, dst, bytes, proc); } entry = reg_entry_find(proc,src,bytes); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); offset = (MPI_Aint)src-(MPI_Aint)buf; get_nb_request(nb_handle, &req); iret = cmx_nbget(dst, offset, bytes, lproc, *(entry->hdl), &(req->request)); return iret; } int PARMCI_NbGetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { int iret; reg_entry_t *entry; void *buf; MPI_Aint offset; nb_t *req; int lproc; if (nb_handle == NULL) { return PARMCI_GetS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); } entry = reg_entry_find(proc,src_ptr,0); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); offset = (MPI_Aint)src_ptr-(MPI_Aint)buf; get_nb_request(nb_handle, &req); /* check if data is contiguous */ if (armci_check_contiguous(src_stride_arr, dst_stride_arr, count, stride_levels)) { int i; int lcount = 1; for (i=0; i<=stride_levels; i++) lcount *= count[i]; iret = cmx_nbget(dst_ptr, offset, lcount, lproc, *(entry->hdl), &(req->request)); } else { iret = cmx_nbgets(dst_ptr, dst_stride_arr, offset, src_stride_arr, count, stride_levels, lproc, *(entry->hdl), &(req->request)); } return iret; } int PARMCI_NbGetV(armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { int rc; reg_entry_t *entry; void *buf; nb_t *req; cmx_giov_t *adarr; int lproc; if (nb_handle == NULL) { return PARMCI_GetV(darr, len, proc); } adarr = malloc(sizeof(cmx_giov_t) * len); /* find location of buffer on remote processor. Start by finding a buffer * location on the remote array */ buf = (darr[0].src_ptr_array)[0]; entry = reg_entry_find(proc,buf,0); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); convert_giov(darr, adarr, len, buf, 0); get_nb_request(nb_handle, &req); rc = cmx_nbgetv(adarr, len, lproc, *(entry->hdl), &(req->request)); free_giov(adarr, len); return rc; } int PARMCI_NbPut(void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { int iret; reg_entry_t *entry; void *buf; MPI_Aint offset; nb_t *req; int lproc; if (nb_handle == NULL) { return PARMCI_Put(src, dst, bytes, proc); } entry = reg_entry_find(proc,dst,bytes); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); offset = (MPI_Aint)dst-(MPI_Aint)buf; get_nb_request(nb_handle, &req); iret = cmx_nbput(src, offset, bytes, lproc, *(entry->hdl), &(req->request)); return iret; } int PARMCI_NbPutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { int iret; reg_entry_t *entry; void *buf; MPI_Aint offset; nb_t *req; int lproc; if (nb_handle == NULL) { return PARMCI_PutS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); } entry = reg_entry_find(proc,dst_ptr,0); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); offset = (MPI_Aint)dst_ptr-(MPI_Aint)buf; get_nb_request(nb_handle, &req); /* check if data is contiguous */ if (armci_check_contiguous(src_stride_arr, dst_stride_arr, count, stride_levels)) { int i; int lcount = 1; for (i=0; i<=stride_levels; i++) lcount *= count[i]; iret = cmx_nbput(src_ptr, offset, lcount, lproc, *(entry->hdl), &(req->request)); } else { iret = cmx_nbputs(src_ptr, src_stride_arr, offset, dst_stride_arr, count, stride_levels, lproc, *(entry->hdl), &(req->request)); } return iret; } int PARMCI_NbPutV(armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { int rc; reg_entry_t *entry; void *buf; nb_t *req; cmx_giov_t *adarr; int lproc; if (nb_handle == NULL) { return PARMCI_PutV(darr, len, proc); } adarr = malloc(sizeof(cmx_giov_t) * len); /* find location of buffer on remote processor. Start by finding a buffer * location on the remote array */ buf = (darr[0].dst_ptr_array)[0]; entry = reg_entry_find(proc,buf,0); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); convert_giov(darr, adarr, len, buf, 1); get_nb_request(nb_handle, &req); rc = cmx_nbputv(adarr, len, lproc, *(entry->hdl), &(req->request)); free_giov(adarr, len); return rc; } int PARMCI_NbPutValueDouble(double src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPut(&src, dst, sizeof(double), proc, nb_handle); } int PARMCI_NbPutValueFloat(float src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPut(&src, dst, sizeof(float), proc, nb_handle); } int PARMCI_NbPutValueInt(int src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPut(&src, dst, sizeof(int), proc, nb_handle); } int PARMCI_NbPutValueLong(long src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPut(&src, dst, sizeof(long), proc, nb_handle); } int PARMCI_Put(void *src, void *dst, int bytes, int proc) { reg_entry_t *entry; void *buf; MPI_Aint offset; int lproc; entry = reg_entry_find(proc,dst,bytes); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); offset = (MPI_Aint)dst-(MPI_Aint)buf; return cmx_put(src, offset, bytes, lproc, *(entry->hdl)); } int PARMCI_PutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { int iret; reg_entry_t *entry; void *buf; MPI_Aint offset; nb_t *req; int lproc; entry = reg_entry_find(proc,dst_ptr,0); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); offset = (MPI_Aint)dst_ptr-(MPI_Aint)buf; /* check if data is contiguous */ if (armci_check_contiguous(src_stride_arr, dst_stride_arr, count, stride_levels)) { int i; int lcount = 1; for (i=0; i<=stride_levels; i++) lcount *= count[i]; iret = cmx_put(src_ptr, offset, lcount, lproc, *(entry->hdl)); } else { iret = cmx_puts(src_ptr, src_stride_arr, offset, dst_stride_arr, count, stride_levels, lproc, *(entry->hdl)); } return iret; } int PARMCI_PutS_flag(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { assert(0); return 0; } int PARMCI_PutS_flag_dir(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { assert(0); return 0; } int PARMCI_PutV(armci_giov_t *darr, int len, int proc) { int rc; reg_entry_t *entry; void *buf; cmx_giov_t *adarr = malloc(sizeof(cmx_giov_t) * len); int lproc; /* find location of buffer on remote processor. Start by finding a buffer * location on the remote array */ buf = (darr[0].dst_ptr_array)[0]; entry = reg_entry_find(proc,buf,0); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); convert_giov(darr, adarr, len, buf, 1); rc = cmx_putv(adarr, len, lproc, *(entry->hdl)); free_giov(adarr, len); return rc; } int PARMCI_PutValueDouble(double src, void *dst, int proc) { return PARMCI_Put(&src, dst, sizeof(double), proc); } int PARMCI_PutValueFloat(float src, void *dst, int proc) { return PARMCI_Put(&src, dst, sizeof(float), proc); } int PARMCI_PutValueInt(int src, void *dst, int proc) { return PARMCI_Put(&src, dst, sizeof(int), proc); } int PARMCI_PutValueLong(long src, void *dst, int proc) { return PARMCI_Put(&src, dst, sizeof(long), proc); } int PARMCI_Put_flag(void *src, void *dst, int bytes, int *f, int v, int proc) { assert(0); return 0; } int PARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc) { reg_entry_t *entry; void *buf; MPI_Aint offset; int lproc; entry = reg_entry_find(proc,prem,0); buf = entry->buf; cmx_group_translate_ranks(1, CMX_GROUP_WORLD, &proc, entry->hdl->group, &lproc); offset = (MPI_Aint)prem-(MPI_Aint)buf; op = convert_optype(op); return cmx_rmw(op, ploc, offset, extra, lproc, *(entry->hdl)); } int PARMCI_Test(armci_hdl_t *nb_handle) { int status, ierr; nb_t *req = find_nb_request(nb_handle); if (req == NULL) return 0; ierr = cmx_test(&(req->request), &status); if (status == 0) { /* operation was completed so free handle */ delete_nb_request(nb_handle); } assert(CMX_SUCCESS == ierr); return status; } void PARMCI_Unlock(int mutex, int proc) { cmx_unlock(mutex, proc); } int PARMCI_Wait(armci_hdl_t *nb_handle) { int ret; nb_t *req = find_nb_request(nb_handle); ret = cmx_wait(&(req->request)); delete_nb_request(nb_handle); return CMX_SUCCESS; } int PARMCI_WaitAll() { return cmx_wait_all(CMX_GROUP_WORLD); } int PARMCI_WaitProc(int proc) { return cmx_wait_proc(proc, CMX_GROUP_WORLD); } int parmci_notify(int proc) { assert(0); return 0; } int parmci_notify_wait(int proc, int *pval) { assert(0); return 0; } int armci_domain_nprocs(armci_domain_t domain, int id) { return 1; } int armci_domain_id(armci_domain_t domain, int glob_proc_id) { return glob_proc_id; } int armci_domain_glob_proc_id(armci_domain_t domain, int id, int loc_proc_id) { return id; } int armci_domain_my_id(armci_domain_t domain) { int rank; assert(cmx_initialized()); cmx_group_rank(CMX_GROUP_WORLD, &rank); return rank; } int armci_domain_count(armci_domain_t domain) { int size; assert(cmx_initialized()); cmx_group_size(CMX_GROUP_WORLD, &size); return size; } int armci_domain_same_id(armci_domain_t domain, int proc) { int rank; cmx_group_rank(CMX_GROUP_WORLD, &rank); return (proc == rank); } void ARMCI_Error(const char *msg, int code) { cmx_error(msg, code); } void ARMCI_Set_shm_limit(unsigned long shmemlimit) { /* ignore */ } /* Shared memory not implemented */ int ARMCI_Uses_shm() { return 0; } int ARMCI_Uses_shm_group() { return 0; } /* Is it memory copy? */ void PARMCI_Copy(void *src, void *dst, int n) { assert(0); memcpy(dst, src, sizeof(int) * n); } /* Group Functions */ int ARMCI_Uses_shm_grp(ARMCI_Group *group) { assert(0); return 0; } void ARMCI_Cleanup() { PARMCI_Finalize(); } /* JAD technically not an error to have empty impl of aggregate methods */ void ARMCI_SET_AGGREGATE_HANDLE(armci_hdl_t* handle) { } void ARMCI_UNSET_AGGREGATE_HANDLE(armci_hdl_t* handle) { } /* Always return 0, since shared memory not implemented yet */ int ARMCI_Same_node(int proc) { return 0; } ga-5.9.2/cmx/src-armci/armci.h000066400000000000000000000412601500715745200160420ustar00rootroot00000000000000/* ARMCI header file */ #ifndef _ARMCI_H #define _ARMCI_H /* for size_t */ #include #include #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif extern int armci_sameclusnode(int proc); typedef struct { void **src_ptr_array; void **dst_ptr_array; int ptr_array_len; int bytes; } armci_giov_t; typedef long armci_size_t; extern int armci_notify(int proc); extern int armci_notify_wait(int proc,int *pval); extern int ARMCI_Init(); /* initialize ARMCI */ extern int ARMCI_Init_args(int *argc, char ***argv); /* initialize ARMCI */ extern int ARMCI_Initialized(); extern void ARMCI_Barrier(); /* ARMCI Barrier*/ extern int ARMCI_Put(void *src, void* dst, int bytes, int proc); extern int ARMCI_Put_flag(void *src, void* dst,int bytes,int *f,int v,int proc); /* On path for deprecation */ #define ARMCI_Put1(_s,_d,_b,_p) memcpy(_d,_s,_b), 0 extern int ARMCI_PutS( /* strided put */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc /* remote process(or) ID */ ); extern int ARMCI_PutS_flag_dir( /* put with flag that uses direct put */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of segments at each stride levels: count[0]=bytes*/ int stride_levels, /* number of stride levels */ int *flag, /* pointer to remote flag */ int val, /* value to set flag upon completion of data transfer */ int proc /* remote process(or) ID */ ); extern int ARMCI_PutS_flag( void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of segments at each stride levels: count[0]=bytes*/ int stride_levels, /* number of stride levels */ int *flag, /* pointer to remote flag */ int val, /* value to set flag upon completion of data transfer */ int proc /* remote process(or) ID */ ); extern int ARMCI_Acc(int optype, void *scale, void *src, void* dst, int bytes, int proc); extern int ARMCI_AccS( /* strided accumulate */ int optype, /* operation */ void *scale, /* scale factor x += scale*y */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc /* remote process(or) ID */ ); extern int ARMCI_Get(void *src, void* dst, int bytes, int proc); extern int ARMCI_GetS( /* strided get */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc /* remote process(or) ID */ ); extern int ARMCI_GetV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ); extern int ARMCI_PutV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ); extern int ARMCI_AccV( int op, /* operation code */ void *scale, /* scaling factor for accumulate */ armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ); extern int ARMCI_PutValueInt(int src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc /* remote process (or) ID */ ); extern int ARMCI_PutValueLong(long src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc /* remote process (or) ID */ ); extern int ARMCI_PutValueFloat(float src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc /* remote process (or) ID */ ); extern int ARMCI_PutValueDouble(double src,/* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc /* remote process (or) ID */ ); extern int ARMCI_GetValueInt(void *src, int proc); extern long ARMCI_GetValueLong(void *src, int proc); extern float ARMCI_GetValueFloat(void *src, int proc); extern double ARMCI_GetValueDouble(void *src, int proc); extern int ARMCI_Malloc(void* ptr_arr[], armci_size_t bytes); extern int ARMCI_Free(void *ptr); extern void* ARMCI_Malloc_local(armci_size_t bytes); extern int ARMCI_Free_local(void *ptr); extern int ARMCI_Same_node(int proc); extern int ARMCI_Malloc_memdev(void* ptr_arr[], armci_size_t bytes, const char *device); extern int ARMCI_Free_memdev(void *ptr); extern void ARMCI_Finalize(); /* terminate ARMCI */ extern void ARMCI_Error(const char *msg, int code); extern void ARMCI_Fence(int proc); extern void ARMCI_AllFence(); extern int ARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc); extern void ARMCI_Cleanup(); extern int ARMCI_Create_mutexes(int num); extern int ARMCI_Destroy_mutexes(); extern void ARMCI_Lock(int mutex, int proc); extern void ARMCI_Unlock(int mutex, int proc); extern void ARMCI_Set_shm_limit(unsigned long shmemlimit); extern int ARMCI_Uses_shm(); extern void ARMCI_Copy(void *src, void *dst, int n); #define FAIL -1 #define FAIL2 -2 #define FAIL3 -3 #define FAIL4 -4 #define FAIL5 -5 #define FAIL6 -6 #define FAIL7 -7 #define FAIL8 -8 #define ARMCI_SWAP 10 #define ARMCI_SWAP_LONG 11 #define ARMCI_FETCH_AND_ADD 12 #define ARMCI_FETCH_AND_ADD_LONG 13 #define ARMCI_ACC_OFF 36 #define ARMCI_ACC_INT (ARMCI_ACC_OFF + 1) #define ARMCI_ACC_DBL (ARMCI_ACC_OFF + 2) #define ARMCI_ACC_FLT (ARMCI_ACC_OFF + 3) #define ARMCI_ACC_CPL (ARMCI_ACC_OFF + 4) #define ARMCI_ACC_DCP (ARMCI_ACC_OFF + 5) #define ARMCI_ACC_LNG (ARMCI_ACC_OFF + 6) #define ARMCI_MAX_STRIDE_LEVEL 8 /****************Error/termination macros************************/ /**Debug assert macro. To be used instead of assert for more user * informative and cleaner death. Also allows individualized * enabling/disabling of assert statements. * @param _enable Ignore this assertion if _enable==0 * @param _cond Condition to be evaluated (assert that it is true) * @param _plist Information to be printed using printf, should be * within parenthesis (eg., dassert(1,0,("%d:test n=%d\n",me,0)); * ). This is fed directly to printf. */ int dassertp_fail(const char *cond_string, const char *file, const char *func, unsigned int line, int code); void derr_printf(const char *format, ...); #undef dassertp #define dassertp(_enable,_cond,_plist) do { \ if((_enable) && !(_cond)) { \ derr_printf _plist; \ dassertp_fail(#_cond,__FILE__,FUNCTION_NAME,__LINE__,1); \ }} while(0) #undef dassertc #define dassertc(_enable,_cond,_plist,_code) do { \ if((_enable) && !(_cond)) { \ derr_printf _plist; \ dassertp_fail(#_cond,__FILE__,FUNCTION_NAME,__LINE__,_code); \ }} while(0) #undef dassert #define dassert(_enable,_cond) dassertp((_enable),(_cond),("")) #undef dassert1 #define dassert1(_enable,_cond,_ival) \ dassertp((_enable),(_cond),("%d: error ival=%d\n", \ armci_msg_me(),(int)(_ival))) #define armci_die(_msg,_code) dassertc(1,0, \ ("%d:%s: %d\n", armci_msg_me(),(_msg),(_code)),_code) #define armci_die2(_msg,_code1,_code2) dassertc(1,0, \ ("%d:%s: (%d,%d)\n",armci_msg_me(),(_msg),(_code1),(_code2)),_code1) /************ locality information **********************************************/ typedef int armci_domain_t; #define ARMCI_DOMAIN_SMP 0 /* SMP node domain for armci_domain_XXX calls */ extern int armci_domain_nprocs(armci_domain_t domain, int id); extern int armci_domain_id(armci_domain_t domain, int glob_proc_id); extern int armci_domain_glob_proc_id(armci_domain_t domain, int id, int loc_proc_id); extern int armci_domain_my_id(armci_domain_t domain); extern int armci_domain_count(armci_domain_t domain); extern int armci_domain_same_id(armci_domain_t domain, int proc); extern char *mp_group_name; /*********************stuff for non-blocking API******************************/ /*\ the request structure for non-blocking api. \*/ /*max of sizes for all platforms. Increase if any platform needs more*/ typedef int armci_hdl_t; typedef int ARMCI_Group; extern void ARMCI_Group_create(int n, int *pid_list, ARMCI_Group *group_out); extern void ARMCI_Group_create_child(int n, int *pid_list, ARMCI_Group *group_out, ARMCI_Group *group_parent); extern void ARMCI_Group_free(ARMCI_Group *group); extern int ARMCI_Group_rank(ARMCI_Group *group, int *rank); extern void ARMCI_Group_size(ARMCI_Group *group, int *size); extern void ARMCI_Group_set_default(ARMCI_Group *group); extern void ARMCI_Group_get_default(ARMCI_Group *group_out); extern void ARMCI_Group_get_world(ARMCI_Group *group_out); extern int ARMCI_Absolute_id(ARMCI_Group *group, int group_rank); extern int ARMCI_Uses_shm_grp(ARMCI_Group *group); extern int ARMCI_Malloc_group(void *ptr_arr[], armci_size_t bytes,ARMCI_Group *group); extern int ARMCI_Malloc_group_memdev(void *ptr_arr[], armci_size_t bytes, ARMCI_Group *group, const char *device); extern int ARMCI_Free_group(void *ptr, ARMCI_Group *group); extern int ARMCI_NbPut(void *src, void* dst, int bytes, int proc,armci_hdl_t* nb_handle); extern int ARMCI_NbPutS( /* strided put */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbAccS( /* strided accumulate */ int optype, /* operation */ void *scale, /* scale factor x += scale*y */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbGet(void *src, void* dst, int bytes, int proc,armci_hdl_t* nb_handle); extern int ARMCI_NbGetS( /* strided get */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handler/*armci_non-blocking request handle*/ ); extern int ARMCI_NbGetV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbPutV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbAccV( int op, /* operation code */ void *scale, /* scaling factor for accumulate */ armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbPutValueInt(int src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc, /* remote process (or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle */ ); extern int ARMCI_NbPutValueLong(long src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc, /* remote process (or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle */ ); extern int ARMCI_NbPutValueFloat(float src,/* value in a register to put */ void *dst,/* dest starting addr to put data */ int proc, /* remote process (or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle */ ); extern int ARMCI_NbPutValueDouble(double src,/* value in a register to put */ void *dst,/* dest starting addr to put data*/ int proc, /* remote process (or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle */ ); extern int ARMCI_Wait(armci_hdl_t* nb_handle); /*non-blocking request handle*/ extern int ARMCI_Test(armci_hdl_t* nb_handle); /*non-blocking request handle*/ extern int ARMCI_WaitAll (); extern int ARMCI_WaitProc (int proc); extern void ARMCI_SET_AGGREGATE_HANDLE(armci_hdl_t* nb_handle); extern void ARMCI_UNSET_AGGREGATE_HANDLE(armci_hdl_t* nb_handle); /* #define ARMCI_INIT_HANDLE(hdl) do {((double *)((hdl)->data))[0]=0; \ ((double *)((hdl)->data))[1]=0; }while(0) */ #define ARMCI_INIT_HANDLE(hdl) /* -------------- ARMCI Non-collective memory allocator ------------- */ typedef struct armci_meminfo_ds { char * armci_addr; /* remote address of the creator which can be used in ARMCI communication */ char *addr; /* local address of creator which can be used in to set SMP memoffset, armci_set_mem_offset() */ size_t size; /* size of remote pid's segment (bytes) */ int cpid; /* armci pid of creator */ long idlist[128]; } armci_meminfo_t; extern void ARMCI_Memget(size_t bytes, armci_meminfo_t *meminfo, int memflg); extern void* ARMCI_Memat(armci_meminfo_t *meminfo, long offset); extern void ARMCI_Memdt(armci_meminfo_t *meminfo, long offset); extern void ARMCI_Memctl(armci_meminfo_t *meminfo); /* ------------------------------------------------------------------ */ #if defined(__cplusplus) || defined(c_plusplus) } #endif #endif /* _ARMCI_H */ ga-5.9.2/cmx/src-armci/capi.c000066400000000000000000000270251500715745200156610ustar00rootroot00000000000000/* #if HAVE_CONFIG_H # include "config.h" #endif */ #include #include "armci.h" #include "parmci.h" #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Acc #endif int ARMCI_Acc(int optype, void *scale, void *src, void *dst, int bytes, int proc) { return PARMCI_Acc(optype, scale, src, dst, bytes, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_AccS #endif int ARMCI_AccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { return PARMCI_AccS(optype, scale, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_AccV #endif int ARMCI_AccV(int op, void *scale, armci_giov_t *darr, int len, int proc) { return PARMCI_AccV(op, scale, darr, len, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_AllFence #endif void ARMCI_AllFence() { PARMCI_AllFence(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Barrier #endif void ARMCI_Barrier() { PARMCI_Barrier(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Create_mutexes #endif int ARMCI_Create_mutexes(int num) { return PARMCI_Create_mutexes(num); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Destroy_mutexes #endif int ARMCI_Destroy_mutexes() { return PARMCI_Destroy_mutexes(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Fence #endif void ARMCI_Fence(int proc) { PARMCI_Fence(proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Finalize #endif void ARMCI_Finalize() { PARMCI_Finalize(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Free #endif int ARMCI_Free(void *ptr) { return PARMCI_Free(ptr); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Free_memdev #endif int ARMCI_Free_memdev(void *ptr) { return PARMCI_Free_memdev(ptr); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Free_local #endif int ARMCI_Free_local(void *ptr) { return PARMCI_Free_local(ptr); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Get #endif int ARMCI_Get(void *src, void *dst, int bytes, int proc) { return PARMCI_Get(src, dst, bytes, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetS #endif int ARMCI_GetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { return PARMCI_GetS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetV #endif int ARMCI_GetV(armci_giov_t *darr, int len, int proc) { return PARMCI_GetV(darr, len, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetValueDouble #endif double ARMCI_GetValueDouble(void *src, int proc) { return PARMCI_GetValueDouble(src, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetValueFloat #endif float ARMCI_GetValueFloat(void *src, int proc) { return PARMCI_GetValueFloat(src, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetValueInt #endif int ARMCI_GetValueInt(void *src, int proc) { return PARMCI_GetValueInt(src, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetValueLong #endif long ARMCI_GetValueLong(void *src, int proc) { return PARMCI_GetValueLong(src, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Init #endif int ARMCI_Init() { return PARMCI_Init(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Init_args #endif int ARMCI_Init_args(int *argc, char ***argv) { return PARMCI_Init_args(argc, argv); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Initialized #endif int ARMCI_Initialized() { return PARMCI_Initialized(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Lock #endif void ARMCI_Lock(int mutex, int proc) { PARMCI_Lock(mutex, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Malloc #endif int ARMCI_Malloc(void **ptr_arr, armci_size_t bytes) { return PARMCI_Malloc(ptr_arr, bytes); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Malloc_memdev #endif int ARMCI_Malloc_memdev(void **ptr_arr, armci_size_t bytes, const char *device) { return PARMCI_Malloc_memdev(ptr_arr, bytes, device); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Malloc_local #endif void* ARMCI_Malloc_local(armci_size_t bytes) { return PARMCI_Malloc_local(bytes); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Memat #endif void* ARMCI_Memat(armci_meminfo_t *meminfo, long offset) { return PARMCI_Memat(meminfo, offset); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Memget #endif void ARMCI_Memget(size_t bytes, armci_meminfo_t *meminfo, int memflg) { PARMCI_Memget(bytes, meminfo, memflg); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Memctl #endif void ARMCI_Memctl(armci_meminfo_t *meminfo) { PARMCI_Memctl(meminfo); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbAccS #endif int ARMCI_NbAccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbAccS(optype, scale, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbAccV #endif int ARMCI_NbAccV(int op, void *scale, armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbAccV(op, scale, darr, len, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbGet #endif int ARMCI_NbGet(void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbGet(src, dst, bytes, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbGetS #endif int ARMCI_NbGetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbGetS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbGetV #endif int ARMCI_NbGetV(armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbGetV(darr, len, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPut #endif int ARMCI_NbPut(void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPut(src, dst, bytes, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutS #endif int ARMCI_NbPutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutV #endif int ARMCI_NbPutV(armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutV(darr, len, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutValueDouble #endif int ARMCI_NbPutValueDouble(double src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutValueDouble(src, dst, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutValueFloat #endif int ARMCI_NbPutValueFloat(float src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutValueFloat(src, dst, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutValueInt #endif int ARMCI_NbPutValueInt(int src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutValueInt(src, dst, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutValueLong #endif int ARMCI_NbPutValueLong(long src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutValueLong(src, dst, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Put #endif int ARMCI_Put(void *src, void *dst, int bytes, int proc) { return PARMCI_Put(src, dst, bytes, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutS #endif int ARMCI_PutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { return PARMCI_PutS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutS_flag #endif int ARMCI_PutS_flag(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { return PARMCI_PutS_flag(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, flag, val, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutS_flag_dir #endif int ARMCI_PutS_flag_dir(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { return PARMCI_PutS_flag_dir(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, flag, val, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutV #endif int ARMCI_PutV(armci_giov_t *darr, int len, int proc) { return PARMCI_PutV(darr, len, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutValueDouble #endif int ARMCI_PutValueDouble(double src, void *dst, int proc) { return PARMCI_PutValueDouble(src, dst, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutValueFloat #endif int ARMCI_PutValueFloat(float src, void *dst, int proc) { return PARMCI_PutValueFloat(src, dst, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutValueInt #endif int ARMCI_PutValueInt(int src, void *dst, int proc) { return PARMCI_PutValueInt(src, dst, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutValueLong #endif int ARMCI_PutValueLong(long src, void *dst, int proc) { return PARMCI_PutValueLong(src, dst, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Put_flag #endif int ARMCI_Put_flag(void *src, void *dst, int bytes, int *f, int v, int proc) { return PARMCI_Put_flag(src, dst, bytes, f, v, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Rmw #endif int ARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc) { return PARMCI_Rmw(op, ploc, prem, extra, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Test #endif int ARMCI_Test(armci_hdl_t *nb_handle) { return PARMCI_Test(nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Unlock #endif void ARMCI_Unlock(int mutex, int proc) { PARMCI_Unlock(mutex, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Wait #endif int ARMCI_Wait(armci_hdl_t *nb_handle) { return PARMCI_Wait(nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_WaitAll #endif int ARMCI_WaitAll() { return PARMCI_WaitAll(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_WaitProc #endif int ARMCI_WaitProc(int proc) { return PARMCI_WaitProc(proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak armci_msg_barrier #endif void armci_msg_barrier() { parmci_msg_barrier(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak armci_msg_group_barrier #endif void armci_msg_group_barrier(ARMCI_Group *group) { parmci_msg_group_barrier(group); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak armci_notify #endif int armci_notify(int proc) { return parmci_notify(proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak armci_notify_wait #endif int armci_notify_wait(int proc, int *pval) { return parmci_notify_wait(proc, pval); } ga-5.9.2/cmx/src-armci/groups.c000066400000000000000000000171521500715745200162640ustar00rootroot00000000000000/* #if HAVE_CONFIG_H # include "config.h" #endif */ #include #include #include "armci.h" #include "message.h" #include "cmx.h" #include "groups.h" /* the HEAD of the group linked list */ armci_igroup_t *_iarm_group_list = NULL; /* ARMCI has the notion of a default group and a world group. */ ARMCI_Group ARMCI_Default_Proc_Group = 0; /** * Return the igroup instance given the group id. * * The group linked list is searched sequentially until the given group * is found. It is an error if this function is called before * armci_group_init(). An error occurs if the given group is not found. * * @param id group identifier */ armci_igroup_t* armci_get_igroup_from_group(ARMCI_Group id) { armci_igroup_t *current_group_list_item = _iarm_group_list; assert(group_list != NULL); while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { return current_group_list_item; } current_group_list_item = current_group_list_item->next; } cmx_error("cmx group lookup failed", -1); return NULL; } /** * Return the cmx_group_corresponding to an ARMCI_Group * @param id ARMCI Group identifier * @return CMX group */ cmx_group_t armci_get_cmx_group(ARMCI_Group id) { return (armci_get_igroup_from_group(id)->group); } /** * Creates and associates an ARMCI_Group with an armci_igroup_t. * This only places the new group in the internal group list and * assigns it an external ARMCI_Group id. */ void iarm_create_group_and_igroup( ARMCI_Group *id, armci_igroup_t **igroup) { int maxID = 0; armci_igroup_t *new_group_list_item = NULL; armci_igroup_t *last_group_list_item = NULL; /* find the last group in the group linked list */ last_group_list_item = _iarm_group_list; while (last_group_list_item->next != NULL) { if (last_group_list_item->id > maxID) maxID = last_group_list_item->id; last_group_list_item = last_group_list_item->next; } if (last_group_list_item->id > maxID) maxID = last_group_list_item->id; /* create, init, and insert the new node for the linked list */ new_group_list_item = malloc(sizeof(armci_igroup_t)); new_group_list_item->id = maxID + 1; new_group_list_item->next = NULL; new_group_list_item->handle_list = NULL; last_group_list_item->next = new_group_list_item; /* return the group id and cmx igroup */ *igroup = new_group_list_item; *id = new_group_list_item->id; } /** * Destroy the given armci_igroup_t */ void iarm_igroup_delete(armci_igroup_t *igroup) { int status; armci_handle_link_t *curr_hdl; armci_handle_link_t *next_hdl; armci_igroup_t *previous_group_list_item = NULL; armci_igroup_t *current_group_list_item = NULL; assert(*igroup); /* find the group in the group linked list */ current_group_list_item = _iarm_group_list; while (current_group_list_item != igroup) { previous_group_list_item = current_group_list_item; current_group_list_item = previous_group_list_item->next; if (current_group_list_item == NULL) { printf("Error deleting group: group not found\n"); } } /* Remove all handles associated with this group */ curr_hdl = igroup->handle_list; while (curr_hdl != NULL) { next_hdl = curr_hdl->next; cmx_free(*(curr_hdl->handle)); curr_hdl = next_hdl; } /* Fix up link list of groups before deleting group */ previous_group_list_item->next = current_group_list_item->next; free(igroup); } /** * Initialize group linked list. Prepopulate with world group. */ void armci_group_init() { /* create the head of the group linked list */ assert(group_list == NULL); _iarm_group_list = malloc(sizeof(armci_igroup_t)); /* First item in list contains the world group */ _iarm_group_list->id = -1; _iarm_group_list->next = NULL; _iarm_group_list->handle_list = NULL; _iarm_group_list->group = CMX_GROUP_WORLD; /* Second item in list contains the mirror group */ armci_igroup_t *mirror = malloc(sizeof(armci_igroup_t)); mirror->id = 0; mirror->next = NULL; mirror->handle_list = NULL; mirror->group = CMX_GROUP_WORLD; /* Create link from world group */ _iarm_group_list->next = mirror; } /** * Clean up group link list */ void armci_group_finalize() { armci_igroup_t *current_group_list_item = _iarm_group_list; armci_igroup_t *previous_group_list_item = NULL; current_group_list_item = current_group_list_item->next; while (current_group_list_item != NULL) { previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; if (current_group_list_item) iarm_igroup_delete(previous_group_list_item); } _iarm_group_list = NULL; } /** * Add a CMX handle to the list in the group */ void iarm_igroup_add_handle(ARMCI_Group id, cmx_handle_t *cmx_hdl) { armci_igroup_t *current_group_list_item = _iarm_group_list; armci_handle_link_t *curr_hdl = NULL; armci_handle_link_t *new_hdl = NULL; /* find the group*/ while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { break; } current_group_list_item = current_group_list_item->next; } /* add handle to group. Start by finding last member of * handle list */ curr_hdl = current_group_list_item->handle_list; while (curr_hdl != NULL && curr_hdl->next != NULL) { curr_hdl = curr_hdl->next; } new_hdl = malloc(sizeof(armci_handle_link_t)); if (curr_hdl == NULL) { new_hdl->next = NULL; new_hdl->prev = NULL; new_hdl->handle = cmx_hdl; current_group_list_item->handle_list = new_hdl; } else { curr_hdl->next = new_hdl; new_hdl->next = NULL; new_hdl->prev = curr_hdl; new_hdl->handle = cmx_hdl; } } int ARMCI_Group_rank(ARMCI_Group *id, int *rank) { armci_igroup_t *grp; grp = armci_get_igroup_from_group(*id); return cmx_group_rank(grp->group, rank); } void ARMCI_Group_size(ARMCI_Group *id, int *size) { armci_igroup_t *grp; grp = armci_get_igroup_from_group(*id); cmx_group_size(grp->group, size); } int ARMCI_Absolute_id(ARMCI_Group *id, int group_rank) { armci_igroup_t *grp; int ierr; int world_rank; grp = armci_get_igroup_from_group(*id); ierr = cmx_group_translate_world(grp->group, group_rank, &world_rank); assert(CMX_SUCCESS == ierr); return world_rank; } void ARMCI_Group_set_default(ARMCI_Group *id) { ARMCI_Default_Proc_Group = *id; } void ARMCI_Group_get_default(ARMCI_Group *group_out) { *group_out = ARMCI_Default_Proc_Group; } void ARMCI_Group_get_world(ARMCI_Group *group_out) { *group_out = 0; } void ARMCI_Group_free(ARMCI_Group *id) { armci_igroup_t *grp; cmx_group_t cmx_grp; grp = armci_get_igroup_from_group(*id); cmx_grp = grp->group; cmx_group_free(cmx_grp); iarm_igroup_delete(grp); } void ARMCI_Group_create_child( int n, int *pid_list, ARMCI_Group *id_child, ARMCI_Group *id_parent) { armci_igroup_t *old_grp; armci_igroup_t *new_grp; cmx_group_t child; cmx_group_t parent; old_grp = armci_get_igroup_from_group(*id_parent); parent = old_grp->group; cmx_group_create(n, pid_list, parent, &child); iarm_create_group_and_igroup(id_child, &new_grp); new_grp->group = child; new_grp->id = *id_child; new_grp->handle_list = NULL; new_grp->next = NULL; } void ARMCI_Group_create(int n, int *pid_list, ARMCI_Group *group_out) { armci_igroup_t *old_grp; armci_igroup_t *new_grp; cmx_group_t child; cmx_group_t parent; old_grp = armci_get_igroup_from_group(ARMCI_Default_Proc_Group); parent = old_grp->group; cmx_group_create(n, pid_list, parent, &child); iarm_create_group_and_igroup(group_out, &new_grp); new_grp->group = child; new_grp->id = *group_out; new_grp->handle_list = NULL; new_grp->next = NULL; } ga-5.9.2/cmx/src-armci/groups.h000066400000000000000000000014051500715745200162630ustar00rootroot00000000000000#ifndef _GROUPS_H #define _GROUPS_H /* #if HAVE_CONFIG_H # include "config.h" #endif */ #include #include #include "armci.h" #include "message.h" #include "cmx.h" typedef struct _armci_handle_link { struct _armci_handle_link *next; struct _armci_handle_link *prev; cmx_handle_t *handle; } armci_handle_link_t; typedef struct _armci_group_link { struct _armci_group_link *next; ARMCI_Group id; armci_handle_link_t *handle_list; cmx_group_t group; } armci_igroup_t; extern armci_igroup_t* iarm_get_igroup_from_group(ARMCI_Group id); extern void armci_group_init(); extern void armci_group_finalize(); extern cmx_group_t armci_get_cmx_group(ARMCI_Group id); extern armci_igroup_t* armci_get_igroup_from_group(ARMCI_Group id); #endif ga-5.9.2/cmx/src-armci/iterator.c000066400000000000000000000123111500715745200165660ustar00rootroot00000000000000/* #if HAVE_CONFIG_H # include "config.h" #endif */ /** @file iterator.h * @author Sriram Krishnamoorthy * @brief Stride iterator. * An iterator for the stride descriptor to reuse common traversal * functionality. More functionality related to the strided * descriptor reusable across files will be extracted here as well. */ #include #include #include #include #include "iterator.h" /**Create a stride iterator. * @param base_ptr IN Starting pointer for stride descriptor * @param stride_levels IN #stride levels * @param stride_arr IN the strides (arr of size[stride_levels]) * @param seg_count IN #segments in each stride * level([stride_levels+1]) * @return Handle to stride iterator created */ void armci_stride_info_init(stride_info_t *sinfo, void *base_ptr, int stride_levels, const int *stride_arr, const int *seg_count) { int i; assert(sinfo!=NULL); assert(stride_levels>=0); assert(stride_levels<=ARMCI_MAX_STRIDE_LEVEL); for(i=0; i= seg_count[0]); else assert(stride_arr[i] >= stride_arr[i-1]*seg_count[i]); } sinfo->base_ptr= base_ptr; sinfo->stride_levels = stride_levels; for(i=0; istride_arr[i] = stride_arr[i]; } for(i=0; iseg_count[i] = seg_count[i]; } sinfo->size=1; for(i=1; isize *= sinfo->seg_count[i]; } assert(sinfo->size>0); sinfo->pos=0; for(i=0; iitr[i] = 0; } } /**Destroy a stride iterator. * @param psitr IN/OUT Pointer to stride iterator * @return void */ void armci_stride_info_destroy(stride_info_t *sinfo) { } /**Size of the stride iterator. Defined as total #contiguous * segments in the stride iterator. * @param sitr IN Handle to stride iterator * @return Size of the stride iterator */ int armci_stride_info_size(stride_info_t *sinfo) { assert(sinfo!=NULL); return sinfo->size; } /**Position of the stride iterator. Between 0 and (size-1), * inclusive. Position is the index of the contiguous segment * currently traversed by the iterator. * @param sitr IN Handle to stride descriptor * @return Position of the iterator */ int armci_stride_info_pos(stride_info_t *sinfo) { assert(sinfo!=NULL); return sinfo->pos; } /**Move the iterator to the next position. Assumes position<=size. * @param sitr IN Handle to stride descriptor * @return void */ void armci_stride_info_next(stride_info_t *sinfo) { int i; assert(sinfo!=NULL); assert(sinfo->pos size); sinfo->pos += 1; if(sinfo->stride_levels>0) { sinfo->itr[0] += 1; for(i=0; istride_levels-1 && sinfo->itr[i]==sinfo->seg_count[i+1]; i++) { sinfo->itr[i] = 0; sinfo->itr[i+1] += 1; } assert(sinfo->itr[i] <= sinfo->seg_count[i+1]); } } /**Get pointer to the contiguous segment currently being * traversed. This is the pointer to the user buffer. * @param sitr IN Handle to stride descriptor * @return pointer to current contiguous segment */ void *armci_stride_info_seg_ptr(stride_info_t *sinfo) { assert(sinfo!=NULL); return sinfo->base_ptr + armci_stride_info_seg_off(sinfo); } /**Get the size of the current segment. * @param sitr IN Handle to stride descriptor * @return Size of the current segment */ int armci_stride_info_seg_size(stride_info_t *sinfo) { assert(sinfo!=NULL); return sinfo->seg_count[0]; } /**Get the offset of the current segment with respect to the start of * the first segment (a.k.a src_ptr) * @param sitr IN Handle to stride descriptor * @return Offset of the current segment */ int armci_stride_info_seg_off(stride_info_t *sinfo) { int i; int off; assert(sinfo!=NULL); off=0; for(i=0; istride_levels; i++) { off += sinfo->itr[i] * sinfo->stride_arr[i]; } return off; } /**Check if there are more segments to iterate over. (a.k.a * positionpossize; } void armci_write_strided( void *ptr, int stride_levels, int stride_arr[], int count[], char *buf) { const int seg_size = count[0]; int off; stride_info_t sitr; off=0; assert(count[0]>0); armci_stride_info_init(&sitr,ptr,stride_levels,stride_arr,count); while(armci_stride_info_has_more(&sitr)) { char *sptr = armci_stride_info_seg_ptr(&sitr); memcpy(&buf[off],sptr,seg_size); off += seg_size; armci_stride_info_next(&sitr); } armci_stride_info_destroy(&sitr); } void armci_read_strided( void *ptr, int stride_levels, int stride_arr[], int count[], char *buf) { const int seg_size = count[0]; int off; stride_info_t sitr; off=0; assert(count[0]>0); armci_stride_info_init(&sitr,ptr,stride_levels,stride_arr,count); while(armci_stride_info_has_more(&sitr)) { char *dptr = armci_stride_info_seg_ptr(&sitr); memcpy(dptr,&buf[off],seg_size); off += seg_size; armci_stride_info_next(&sitr); } armci_stride_info_destroy(&sitr); } ga-5.9.2/cmx/src-armci/iterator.h000066400000000000000000000026041500715745200165770ustar00rootroot00000000000000/** @file iterator.h * @author Sriram Krishnamoorthy * @brief Stride iterator. * An iterator for the stride descriptor to reuse common traversal * functionality. More functionality related to the strided * descriptor reusable across files will be extracted here as well. */ #ifndef _STRIDE_INFO_H_ #define _STRIDE_INFO_H_ #include "armci.h" /*for ARMCI_MAX_STRIDE_LEVEL and dassert*/ #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif typedef struct { char *base_ptr; int stride_levels; int stride_arr[ARMCI_MAX_STRIDE_LEVEL]; int seg_count[ARMCI_MAX_STRIDE_LEVEL+1]; int size, pos, itr[ARMCI_MAX_STRIDE_LEVEL]; } stride_info_t; void armci_stride_info_init(stride_info_t *sinfo, void *base_ptr, int stride_levels, const int *stride_arr, const int *seg_count); void armci_stride_info_destroy(stride_info_t *sinfo); int armci_stride_info_count(stride_info_t *sinfo); int armci_stride_info_pos(stride_info_t *sinfo); void armci_stride_info_next(stride_info_t *sinfo); void *armci_stride_info_seg_ptr(stride_info_t *sinfo); int armci_stride_info_seg_size(stride_info_t *sinfo); int armci_stride_info_seg_off(stride_info_t *sinfo); int armci_stride_info_size(stride_info_t *sinfo); int armci_stride_info_has_more(stride_info_t *sinfo); #if defined(__cplusplus) || defined(c_plusplus) } #endif #endif /*_STRIDE_INFO_H_*/ ga-5.9.2/cmx/src-armci/message.c000066400000000000000000000420741500715745200163720ustar00rootroot00000000000000/* #if HAVE_CONFIG_H # include "config.h" #endif */ #include #include #include #include #include #include "armci.h" #include "message.h" #include "cmx.h" #include "groups.h" #if CMX_NETWORK_MPI_TS extern void cmx_make_progress(void); #define CMX_TAG 27624 #endif /* hacky alias for MPI_COMM_SELF */ static ARMCI_Group ARMCI_GROUP_SELF = -2; extern int ARMCI_Default_Proc_Group; /* for armci_msg_sel_scope */ static MPI_Datatype MPI_LONGLONG_INT; static MPI_Comm wc() { MPI_Comm comm; /* assert(CMX_SUCCESS == cmx_group_comm(CMX_GROUP_WORLD, &comm)); */ cmx_group_comm(CMX_GROUP_WORLD, &comm); return comm; } /* undocumented, but used in GA to expose MPI_Comm */ MPI_Comm armci_group_comm(ARMCI_Group *group) { MPI_Comm comm; cmx_group_t grp = armci_get_cmx_group(*group); /* assert(CMX_SUCCESS == cmx_group_comm(*group, &comm)); */ cmx_group_comm(grp, &comm); return comm; } static MPI_Datatype armci_type_to_mpi_type(int type) { MPI_Datatype mpi_dt; if (type == ARMCI_INT) { mpi_dt = MPI_INT; } else if (type == ARMCI_LONG) { mpi_dt = MPI_LONG; } else if (type == ARMCI_LONG_LONG) { mpi_dt = MPI_LONG_LONG; } else if (type == ARMCI_FLOAT) { mpi_dt = MPI_FLOAT; } else if (type == ARMCI_DOUBLE) { mpi_dt = MPI_DOUBLE; } else { assert(0); } return mpi_dt; } static MPI_Op armci_op_to_mpi_op(char *op) { MPI_Op result; if (strncmp(op, "+", 1) == 0) { result = MPI_SUM; } else if (strncmp(op, "max", 3) == 0) { result = MPI_MAX; } else if (strncmp(op, "min", 3) == 0) { result = MPI_MIN; } else if (strncmp(op, "*", 1) == 0) { result = MPI_PROD; } else if (strncmp(op, "absmin", 6) == 0) { result = MPI_MIN; } else if (strncmp(op, "absmax", 6) == 0) { result = MPI_MAX; } else if (strncmp(op, "or", 2) == 0) { result = MPI_BOR; } /* these are new */ else if ((strncmp(op, "&&", 2) == 0) || (strncmp(op, "land", 4) == 0)) { result = MPI_LAND; } else if ((strncmp(op, "||", 2) == 0) || (strncmp(op, "lor", 3) == 0)) { result = MPI_LOR; } else if ((strncmp(op, "&", 1) == 0) || (strncmp(op, "band", 4) == 0)) { result = MPI_BAND; } else if ((strncmp(op, "|", 1) == 0) || (strncmp(op, "bor", 3) == 0)) { result = MPI_BOR; } else { printf("Unsupported gop operation:%s\n",op); assert(0); } return result; } static void do_abs(void *x, int n, int type) { #define ARMCI_ABS_INT(a) (((a) >= 0) ? (a) : (-(a))) #define ARMCI_ABS_FLT(a) (((a) >= 0.0) ? (a) : (-(a))) #define DO_ABS(ARMCI_TYPE, C_TYPE, WHICH) \ if (type == ARMCI_TYPE) { \ int i; \ C_TYPE *y = (C_TYPE *)x; \ for (i = 0; i < n; i++) { \ y[i] = ARMCI_ABS_##WHICH(y[i]); \ } \ } \ else DO_ABS(ARMCI_INT, int, INT) DO_ABS(ARMCI_LONG, long, INT) DO_ABS(ARMCI_LONG_LONG, long long, INT) DO_ABS(ARMCI_FLOAT, float, FLT) DO_ABS(ARMCI_DOUBLE, double, FLT) { assert(0); } #undef ARMCI_ABS_INT #undef ARMCI_ABS_FLT #undef DO_ABS } static MPI_Comm get_comm(ARMCI_Group *group) { MPI_Comm comm; cmx_group_t grp = armci_get_cmx_group(*group); /* assert(CMX_SUCCESS == cmx_group_comm(*group, &comm)); */ cmx_group_comm(grp, &comm); return comm; } static ARMCI_Group get_default_group() { ARMCI_Group group; ARMCI_Group_get_default(&group); return group; } static MPI_Comm get_default_comm() { ARMCI_Group group; ARMCI_Group_get_default(&group); return get_comm(&group); } static int get_default_rank() { int rank; MPI_Comm_rank(get_default_comm(), &rank); return rank; } static void do_gop(void *x, int n, char* op, int type, ARMCI_Group group) { MPI_Comm comm = MPI_COMM_NULL; int mpi_type_size = 0; MPI_Datatype mpi_type = MPI_DATATYPE_NULL; int rc = 0; void *result = NULL; MPI_Op mpi_op = MPI_OP_NULL; if (ARMCI_GROUP_SELF == group) { comm = MPI_COMM_SELF; } else { comm = get_comm(&group); } mpi_type = armci_type_to_mpi_type(type); MPI_Type_size(mpi_type, &mpi_type_size); mpi_op = armci_op_to_mpi_op(op); if (strncmp(op, "absmin", 6) == 0) { do_abs(x, n, type); } else if (strncmp(op, "absmax", 6) == 0) { do_abs(x, n, type); } result = malloc(n*mpi_type_size); assert(result); if (ARMCI_GROUP_SELF != group) { cmx_group_t grp = armci_get_cmx_group(group); cmx_barrier(grp); } rc = MPI_Allreduce(x, result, n, mpi_type, mpi_op, comm); assert(rc == MPI_SUCCESS); memcpy(x, result, mpi_type_size * n); free(result); } void armci_msg_bcast(void *buf, int len, int root) { assert(buf != NULL); cmx_group_t grp = armci_get_cmx_group(ARMCI_Default_Proc_Group); cmx_barrier(grp); MPI_Bcast(buf, len, MPI_BYTE, root, get_default_comm()); } /* the payload is a struct with a union e.g. * * typedef struct { * union val_t {double dval; int ival; long lval; long long llval; float fval;}v; * Integer subscr[MAXDIM]; * DoubleComplex extra; * SingleComplex extra2; * } elem_info_t; * * The key piece is the first sizeof(double) bytes. The rest of the struct * simply tags along for the communication and can be represented as a byte * stream. * * The 'n' parameter is the size of the entire payload i.e. * sizeof(struct elem_info_t). * * We really care which process has the min/max value and then bcast the * entire payload using the min/max answer as the root. */ void armci_msg_sel_scope(int scope, void *x, int n, char* op, int type, int contribute) { static int initialized = 0; MPI_Op mpi_op = MPI_OP_NULL; MPI_Comm comm = get_default_comm(); if (SCOPE_NODE == scope) { comm = MPI_COMM_SELF; } /* first time this function is called we establish the * long long w/ int type that MPI doesn't provide by default */ if (!initialized) { int block[2]; MPI_Aint disp[2]; MPI_Datatype type[2]; initialized = 1; type[0] = MPI_LONG_LONG; type[1] = MPI_INT; disp[0] = 0; disp[1] = sizeof(long long); block[0] = 1; block[1] = 1; MPI_Type_create_struct(2, block, disp, type, &MPI_LONGLONG_INT); } if (strncmp(op, "min", 3) == 0) { mpi_op = MPI_MINLOC; } else if (strncmp(op, "max", 3) == 0) { mpi_op = MPI_MAXLOC; } else { assert(0); } #define SELECT(ARMCI_TYPE, C_TYPE, MPI_TYPE) \ if (type == ARMCI_TYPE) { \ struct { \ C_TYPE val; \ int rank; \ } in, out; \ in.val = *((C_TYPE*)x); \ in.rank = get_default_rank(); \ if (SCOPE_NODE != scope) { \ cmx_group_t grp \ = armci_get_cmx_group(ARMCI_Default_Proc_Group); \ cmx_barrier(grp); \ } \ MPI_Allreduce(&in, &out, 1, MPI_TYPE, mpi_op, comm); \ armci_msg_bcast(x, n, out.rank); \ } \ else SELECT(ARMCI_INT, int, MPI_2INT) SELECT(ARMCI_LONG, long, MPI_LONG_INT) SELECT(ARMCI_LONG_LONG, long long, MPI_LONGLONG_INT) SELECT(ARMCI_FLOAT, float, MPI_FLOAT_INT) SELECT(ARMCI_DOUBLE, double, MPI_DOUBLE_INT) { assert(0); } #undef SELECT } void armci_msg_bcast_scope(int scope, void* buffer, int len, int root) { if (SCOPE_ALL == scope || SCOPE_MASTERS == scope) { armci_msg_bcast(buffer, len, root); } else if (SCOPE_NODE == scope) { assert(buffer != NULL); MPI_Bcast(buffer, len, MPI_BYTE, root, MPI_COMM_SELF); } else { assert(0); } } void armci_msg_brdcst(void* buffer, int len, int root) { armci_msg_bcast(buffer, len, root); } /* there was a case in ghost update where a proc sent a message to itself */ static MPI_Request self_request = MPI_REQUEST_NULL; static int self_request_flag = 0; void armci_msg_snd(int tag, void* buffer, int len, int to) { MPI_Comm comm = wc(); MPI_Request request; MPI_Status status; int self; int flag = 0; int rc; rc = MPI_Comm_rank(comm, &self); assert(MPI_SUCCESS == rc); #if CMX_NETWORK_MPI_TS assert(CMX_TAG != tag); #endif rc = MPI_Isend(buffer, len, MPI_CHAR, to, tag, wc(), &request); assert(MPI_SUCCESS == rc); if (to == self) { /* make sure this proc hasn't already sent to itself -- we only allow * one outstanding self send */ assert(!self_request_flag); self_request_flag = 1; self_request = request; } else { do { MPI_Test(&request, &flag, &status); assert(MPI_SUCCESS == rc); #if CMX_NETWORK_MPI_TS cmx_make_progress(); #endif } while (!flag); } } void armci_msg_rcv(int tag, void* buffer, int len, int *msglen, int from) { MPI_Comm comm = wc(); MPI_Request request; MPI_Status status; int self; int flag = 0; int rc; rc = MPI_Comm_rank(comm, &self); assert(MPI_SUCCESS == rc); #if CMX_NETWORK_MPI_TS assert(CMX_TAG != tag); #endif rc = MPI_Irecv(buffer, len, MPI_CHAR, from, tag, wc(), &request); assert(MPI_SUCCESS == rc); if (from == self && self_request_flag) { do { MPI_Test(&self_request, &flag, &status); assert(MPI_SUCCESS == rc); #if CMX_NETWORK_MPI_TS cmx_make_progress(); #endif } while (!flag); self_request = MPI_REQUEST_NULL; self_request_flag = 0; } do { MPI_Test(&request, &flag, &status); assert(MPI_SUCCESS == rc); #if CMX_NETWORK_MPI_TS cmx_make_progress(); #endif } while (!flag); if(msglen) { rc = MPI_Get_count(&status, MPI_CHAR, msglen); assert(MPI_SUCCESS == rc); } } int armci_msg_rcvany(int tag, void* buffer, int len, int *msglen) { MPI_Comm comm = wc(); MPI_Request request; MPI_Status status; int rank; int flag = 0; int rc; rc = MPI_Comm_rank(comm, &rank); assert(MPI_SUCCESS == rc); #if CMX_NETWORK_MPI_TS assert(CMX_TAG != tag); #endif rc = MPI_Irecv(buffer, len, MPI_CHAR, MPI_ANY_SOURCE, tag, wc(), &request); assert(MPI_SUCCESS == rc); do { MPI_Test(&request, &flag, &status); assert(MPI_SUCCESS == rc); #if CMX_NETWORK_MPI_TS cmx_make_progress(); #endif } while (!flag); if(msglen) { rc = MPI_Get_count(&status, MPI_CHAR, msglen); assert(MPI_SUCCESS == rc); } return (int)status.MPI_SOURCE; } void armci_msg_reduce(void *x, int n, char *op, int type) { do_gop(x, n, op, type, get_default_group()); } void armci_msg_reduce_scope(int scope, void *x, int n, char *op, int type) { if (SCOPE_NODE == scope) { do_gop(x, n, op, type, ARMCI_GROUP_SELF); } else { do_gop(x, n, op, type, get_default_group()); } } void armci_msg_gop_scope(int scope, void *x, int n, char* op, int type) { if (SCOPE_NODE == scope) { do_gop(x, n, op, type, ARMCI_GROUP_SELF); } else { do_gop(x, n, op, type, get_default_group()); } } void armci_msg_igop(int *x, int n, char* op) { do_gop(x, n, op, ARMCI_INT, get_default_group()); } void armci_msg_lgop(long *x, int n, char* op) { do_gop(x, n, op, ARMCI_LONG, get_default_group()); } void armci_msg_llgop(long long *x, int n, char* op) { do_gop(x, n, op, ARMCI_LONG_LONG, get_default_group()); } void armci_msg_fgop(float *x, int n, char* op) { do_gop(x, n, op, ARMCI_FLOAT, get_default_group()); } void armci_msg_dgop(double *x, int n, char* op) { do_gop(x, n, op, ARMCI_DOUBLE, get_default_group()); } void armci_exchange_address(void *ptr_ar[], int n) { ARMCI_Group group; ARMCI_Group_get_default(&group); armci_exchange_address_grp(ptr_ar, n, &group); } void parmci_msg_barrier() { cmx_group_t grp = armci_get_cmx_group(ARMCI_Default_Proc_Group); cmx_barrier(grp); MPI_Barrier(get_default_comm()); } void armci_msg_bintree(int scope, int* Root, int *Up, int *Left, int *Right) { int root, up, left, right, index, nproc; assert(SCOPE_NODE != scope); assert(SCOPE_MASTERS != scope); root = 0; nproc = armci_msg_nproc(); index = armci_msg_me() - root; up = (index-1)/2 + root; if( up < root) up = -1; left = 2*index + 1 + root; if(left >= root+nproc) left = -1; right = 2*index + 2 + root; if(right >= root+nproc)right = -1; *Up = up; *Left = left; *Right = right; *Root = root; } int armci_msg_me() { int rank; assert(cmx_initialized()); assert(wc() != MPI_COMM_NULL); MPI_Comm_rank(wc(), &rank); return rank; } int armci_msg_nproc() { int size; assert(cmx_initialized()); assert(wc() != MPI_COMM_NULL); MPI_Comm_size(wc(), &size); return size; } void armci_msg_abort(int code) { fprintf(stderr, "Exiting, Error in Communication\n"); MPI_Abort(wc(), code); } void armci_msg_init(int *argc, char ***argv) { int flag; MPI_Initialized(&flag); if(!flag) { MPI_Init(argc, argv); } } void armci_msg_finalize() { int flag; MPI_Initialized(&flag); assert(flag); MPI_Finalize(); } double armci_timer() { return MPI_Wtime(); } void armci_msg_clus_brdcst(void *buf, int len) { assert(0); } void armci_msg_clus_igop(int *x, int n, char* op) { assert(0); } void armci_msg_clus_fgop(float *x, int n, char* op) { assert(0); } void armci_msg_clus_lgop(long *x, int n, char* op) { assert(0); } void armci_msg_clus_llgop(long long *x, int n, char* op) { assert(0); } void armci_msg_clus_dgop(double *x, int n, char* op) { assert(0); } void armci_msg_group_gop_scope(int scope, void *x, int n, char* op, int type, ARMCI_Group *group) { if (SCOPE_NODE == scope) { do_gop(x, n, op, type, ARMCI_GROUP_SELF); } else { do_gop(x, n, op, type, *group); } } void armci_msg_group_igop(int *x, int n, char* op, ARMCI_Group *group) { do_gop(x, n, op, ARMCI_INT, *group); } void armci_msg_group_lgop(long *x, int n, char* op, ARMCI_Group *group) { do_gop(x, n, op, ARMCI_LONG, *group); } void armci_msg_group_llgop(long long *x, int n, char* op, ARMCI_Group *group) { do_gop(x, n, op, ARMCI_LONG_LONG, *group); } void armci_msg_group_fgop(float *x, int n, char* op, ARMCI_Group *group) { do_gop(x, n, op, ARMCI_FLOAT, *group); } void armci_msg_group_dgop(double *x, int n,char* op, ARMCI_Group *group) { do_gop(x, n, op, ARMCI_DOUBLE, *group); } void armci_exchange_address_grp(void *ptr_arr[], int n, ARMCI_Group *group) { assert(0); #if 0 MPI_Datatype mpi_datatype; if (sizeof(void*) == sizeof(int)) { mpi_datatype = MPI_INT; } else if (sizeof(void*) == sizeof(long)) { mpi_datatype = MPI_LONG; } else if (sizeof(void*) == sizeof(long long)) { mpi_datatype = MPI_LONG_LONG; } else { assert(0); } MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, ptr_ar, n, mpi_datatype, get_comm(group)); #endif } void parmci_msg_group_barrier(ARMCI_Group *group) { cmx_group_t grp = armci_get_cmx_group(*group); cmx_barrier(grp); MPI_Barrier(get_comm(group)); } void armci_msg_group_bcast_scope(int scope, void *buf, int len, int root, ARMCI_Group *group) { if (SCOPE_NODE == scope) { MPI_Bcast(buf, len, MPI_BYTE, root, MPI_COMM_SELF); } else { cmx_group_t grp = armci_get_cmx_group(*group); int root_sub; int err; /* NOTE: this function is passed a root which has been translated back * to the world group while the group passed in is the sub * communicator group... what a mess */ MPI_Group group_world; MPI_Group group_sub; err = MPI_Comm_group(wc(), &group_world); assert(MPI_SUCCESS == err); err = MPI_Comm_group(get_comm(group), &group_sub); assert(MPI_SUCCESS == err); err = MPI_Group_translate_ranks(group_world, 1, &root, group_sub, &root_sub); cmx_barrier(grp); err = MPI_Bcast(buf, len, MPI_BYTE, root_sub, get_comm(group)); assert(MPI_SUCCESS == err); } } void armci_grp_clus_brdcst(void *buf, int len, int grp_master, int grp_clus_nproc,ARMCI_Group *mastergroup) { assert(0); } ga-5.9.2/cmx/src-armci/message.h000066400000000000000000000064501500715745200163750ustar00rootroot00000000000000#ifndef _MESSAGE_H_ #define _MESSAGE_H_ #include "armci.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif #define ARMCI_INT -99 #define ARMCI_LONG -101 #define ARMCI_LONG_LONG -102 #define ARMCI_FLOAT -306 #define ARMCI_DOUBLE -307 #define SCOPE_ALL 333 #define SCOPE_NODE 337 #define SCOPE_MASTERS 339 #define armci_msg_sel(x,n,op,type,contribute)\ armci_msg_sel_scope(SCOPE_ALL,(x),(n),(op),(type),(contribute)) #if 0 #define armci_msg_bcast(buffer, len, root)\ armci_msg_bcast_scope(SCOPE_ALL, (buffer), (len), (root)) #else extern void armci_msg_bcast(void *buffer, int len, int root); #endif extern void armci_msg_sel_scope(int scope, void *x, int n, char* op, int type, int contribute); extern void armci_msg_bcast_scope(int scope, void* buffer, int len, int root); extern void armci_msg_brdcst(void* buffer, int len, int root); extern void armci_msg_snd(int tag, void* buffer, int len, int to); extern void armci_msg_rcv(int tag, void* buffer, int buflen, int *msglen, int from); extern int armci_msg_rcvany(int tag, void* buffer, int buflen, int *msglen); extern void armci_msg_reduce(void *x, int n, char *op, int type); extern void armci_msg_reduce_scope(int scope, void *x, int n, char *op, int type); extern void armci_msg_gop_scope(int scope, void *x, int n, char* op, int type); extern void armci_msg_igop(int *x, int n, char* op); extern void armci_msg_lgop(long *x, int n, char* op); extern void armci_msg_llgop(long long *x, int n, char* op); extern void armci_msg_fgop(float *x, int n, char* op); extern void armci_msg_dgop(double *x, int n, char* op); extern void armci_exchange_address(void *ptr_ar[], int n); extern void armci_msg_barrier(); extern void armci_msg_bintree(int scope, int* Root, int *Up, int *Left, int *Right); extern int armci_msg_me(); extern int armci_msg_nproc(); extern void armci_msg_abort(int code); extern void armci_msg_init(int *argc, char ***argv); extern void armci_msg_finalize(); extern double armci_timer(); extern void armci_msg_clus_brdcst(void *buf, int len); extern void armci_msg_clus_igop(int *x, int n, char* op); extern void armci_msg_clus_fgop(float *x, int n, char* op); extern void armci_msg_clus_lgop(long *x, int n, char* op); extern void armci_msg_clus_llgop(long long *x, int n, char* op); extern void armci_msg_clus_dgop(double *x, int n, char* op); extern void armci_msg_group_gop_scope(int scope, void *x, int n, char* op, int type, ARMCI_Group *group); extern void armci_msg_group_igop(int *x, int n, char* op,ARMCI_Group *group); extern void armci_msg_group_lgop(long *x, int n, char* op,ARMCI_Group *group); extern void armci_msg_group_llgop(long long *x, int n, char* op,ARMCI_Group *group); extern void armci_msg_group_fgop(float *x, int n, char* op,ARMCI_Group *group); extern void armci_msg_group_dgop(double *x, int n,char* op,ARMCI_Group *group); extern void armci_exchange_address_grp(void *ptr_arr[], int n, ARMCI_Group *group); extern void armci_msg_group_barrier(ARMCI_Group *group); extern void armci_msg_group_bcast_scope(int scope, void *buf, int len, int root, ARMCI_Group *group); extern void armci_grp_clus_brdcst(void *buf, int len, int grp_master, int grp_clus_nproc,ARMCI_Group *mastergroup); #if defined(__cplusplus) || defined(c_plusplus) } #endif #endif ga-5.9.2/cmx/src-armci/parmci.h000066400000000000000000000106651500715745200162270ustar00rootroot00000000000000#ifndef _PARMCI_H_ #define _PARMCI_H_ #include "armci.h" extern int PARMCI_Acc(int optype, void *scale, void *src, void* dst, int bytes, int proc); extern int PARMCI_AccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc); extern int PARMCI_AccV(int op, void *scale, armci_giov_t * darr, int len, int proc); extern void PARMCI_AllFence(); extern void PARMCI_Barrier(); extern int PARMCI_Create_mutexes(int num); extern int PARMCI_Destroy_mutexes(); extern void PARMCI_Fence(int proc); extern void PARMCI_Finalize(); extern int PARMCI_Free_local(void *ptr); extern int PARMCI_Free(void *ptr); extern int PARMCI_GetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc); extern double PARMCI_GetValueDouble(void *src, int proc); extern float PARMCI_GetValueFloat(void *src, int proc); extern int PARMCI_GetValueInt(void *src, int proc); extern long PARMCI_GetValueLong(void *src, int proc); extern int PARMCI_GetV(armci_giov_t * darr, int len, int proc); extern int PARMCI_Get(void *src, void *dst, int bytes, int proc); extern int PARMCI_Init(); extern int PARMCI_Init_args(int *argc, char ***argv); extern int PARMCI_Initialized(); extern void PARMCI_Lock(int mutex, int proc); extern void* PARMCI_Malloc_local(armci_size_t bytes); extern int PARMCI_Malloc(void **ptr_arr, armci_size_t bytes); extern void* PARMCI_Memat(armci_meminfo_t * meminfo, long offset); extern void PARMCI_Memget(size_t bytes, armci_meminfo_t * meminfo, int memflg); extern int PARMCI_NbAccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbAccV(int op, void *scale, armci_giov_t * darr, int len, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbGetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbGetV(armci_giov_t * darr, int len, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbGet(void *src, void *dst, int bytes, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbPutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbPutValueDouble(double src, void *dst, int proc, armci_hdl_t* nb_handle); extern int PARMCI_NbPutValueFloat(float src, void *dst, int proc, armci_hdl_t* nb_handle); extern int PARMCI_NbPutValueInt(int src, void *dst, int proc, armci_hdl_t* nb_handle); extern int PARMCI_NbPutValueLong(long src, void *dst, int proc, armci_hdl_t* nb_handle); extern int PARMCI_NbPutV(armci_giov_t * darr, int len, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbPut(void *src, void *dst, int bytes, int proc, armci_hdl_t * nb_handle); extern int PARMCI_Put_flag(void *src, void *dst, int bytes, int *f, int v, int proc); extern int PARMCI_PutS_flag_dir(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc); extern int PARMCI_PutS_flag(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc); extern int PARMCI_PutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc); extern int PARMCI_PutValueDouble(double src, void *dst, int proc); extern int PARMCI_PutValueFloat(float src, void *dst, int proc); extern int PARMCI_PutValueInt(int src, void *dst, int proc); extern int PARMCI_PutValueLong(long src, void *dst, int proc); extern int PARMCI_PutV(armci_giov_t * darr, int len, int proc); extern int PARMCI_Put(void *src, void *dst, int bytes, int proc); extern int PARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc); extern int PARMCI_Test(armci_hdl_t * nb_handle); extern void PARMCI_Unlock(int mutex, int proc); extern int PARMCI_WaitAll(); extern int PARMCI_Wait(armci_hdl_t * nb_handle); extern int PARMCI_WaitProc(int proc); extern void parmci_msg_barrier(); extern void parmci_msg_group_barrier(ARMCI_Group *group); extern int parmci_notify(int proc); extern int parmci_notify_wait(int proc, int *pval); #endif /* _PARMCI_H_ */ ga-5.9.2/cmx/src-armci/reg_entry.c000066400000000000000000000404021500715745200167350ustar00rootroot00000000000000/** * Registration window. * * Defensive programming via copious CMX_ASSERT statements is encouraged. */ /* #if HAVE_CONFIG_H # include "config.h" #endif */ /* C headers */ #include #include #include #include /* 3rd party headers */ #include /* our headers */ #include "cmx.h" #include "reg_entry.h" #define STATIC static inline /* the static members in this module */ static reg_entry_t **reg_entries = NULL; /* list of allocations on each procss. This array contains the starting node in a linked list */ static int reg_nprocs = 0; /* number of allocations on this process */ /* the static functions in this module */ static reg_return_t seg_cmp(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len, int op); static reg_return_t seg_intersects(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len); static reg_return_t seg_contains(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len); static reg_return_t reg_entry_intersects(reg_entry_t *reg_entry, void *buf, int len); static reg_return_t reg_entry_contains(reg_entry_t *reg_entry, void *buf, int len); #define TEST_FOR_INTERSECTION 0 #define TEST_FOR_CONTAINMENT 1 /*#define TEST_DEBUG*/ #ifdef TEST_DEBUG int reg_entry_rank; int reg_entry_nprocs; #endif /** * Detects whether two memory segments intersect or one contains the other. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * @param[in] op op to perform, either TEST_FOR_INTERSECTION or * TEST_FOR_CONTAINMENT * @return RR_SUCCESS on success */ STATIC reg_return_t seg_cmp(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len, int op) { ptrdiff_t reg_beg = 0; ptrdiff_t reg_end = 0; ptrdiff_t oth_beg = 0; ptrdiff_t oth_end = 0; int result = 0; /* preconditions */ /* CMX_ASSERT(NULL != reg_addr); CMX_ASSERT(NULL != oth_addr); */ /* if reg_len = 0 and oth_len = 0 do a direct comparison. This assumes that * registered region is zero length and we are just using a small buffer to * keep from running into problems associated with not allocating memory on * some processors */ if (reg_len == 0 && oth_len == 0) { if (reg_addr == oth_addr) { return RR_SUCCESS; } else { return RR_FAILURE; } } /* casts to ptrdiff_t since arithmetic on void* is undefined */ reg_beg = (ptrdiff_t)(reg_addr); reg_end = reg_beg + (ptrdiff_t)(reg_len); oth_beg = (ptrdiff_t)(oth_addr); oth_end = oth_beg + (ptrdiff_t)(oth_len); /* hack? we had problems with adjacent registered memory regions and * when the length of the query region was 0 */ if (oth_beg == oth_end) { oth_end += 1; } switch (op) { case TEST_FOR_INTERSECTION: result = (reg_beg >= oth_beg && reg_beg < oth_end) || (reg_end > oth_beg && reg_end <= oth_end); #if DEBUG printf("[%d] TEST_FOR_INTERSECTION " "(%td >= %td [%d] && %td < %td [%d]) ||" "(%td > %td [%d] && %td <= %td [%d])\n", g_state.rank, reg_beg, oth_beg, (reg_beg >= oth_beg), reg_beg, oth_end, (reg_beg < oth_end), reg_end, oth_beg, (reg_end > oth_beg), reg_end, oth_end, (reg_end <= oth_end)); #endif break; case TEST_FOR_CONTAINMENT: result = reg_beg <= oth_beg && reg_end >= oth_end; #if DEBUG printf("[%d] TEST_FOR_CONTAINMENT " "%td <= %td [%d] && %td >= %td [%d]\n", g_state.rank, reg_beg, oth_beg, (reg_beg <= oth_beg), reg_end, oth_end, (reg_end >= oth_end)); #endif break; default: CMX_ASSERT(0); } if (result) { return RR_SUCCESS; } else { return RR_FAILURE; } } /** * Detects whether two memory segments intersect. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * * @return RR_SUCCESS on success */ STATIC reg_return_t seg_intersects(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len) { /* preconditions */ /* CMX_ASSERT(NULL != reg_addr); CMX_ASSERT(NULL != oth_addr); */ return seg_cmp( reg_addr, reg_len, oth_addr, oth_len, TEST_FOR_INTERSECTION); } /** * Detects whether the first memory segment contains the other. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * * @pre NULL != reg_beg * @pre NULL != oth_beg * * @return RR_SUCCESS on success */ STATIC reg_return_t seg_contains(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len) { /* preconditions */ /* CMX_ASSERT(NULL != reg_addr); CMX_ASSERT(NULL != oth_addr); */ return seg_cmp( reg_addr, reg_len, oth_addr, oth_len, TEST_FOR_CONTAINMENT); } /** * Detects whether two memory segments intersect. * (Probably not used) * * @param[in] reg_entry the registration entry * @param[in] buf starting address for the contiguous memory region * @param[in] len length of the contiguous memory region * * @pre NULL != reg_entry * @pre NULL != buf * @pre len >= 0 * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_intersects(reg_entry_t *reg_entry, void *buf, int len) { #if DEBUG printf("[%d] reg_entry_intersects(reg_entry=%p, buf=%p, len=%d)\n", g_state.rank, reg_entry, buf, len); #endif /* preconditions */ /* CMX_ASSERT(NULL != reg_entry); CMX_ASSERT(NULL != buf); */ CMX_ASSERT(len >= 0); return seg_intersects( reg_entry->buf, reg_entry->len, buf, len); } /** * Detects whether the first memory segment contains the other. * * @param[in] reg_entry the registration entry * @param[in] buf starting address for the contiguous memory region * @param[in] len length of the contiguous memory region * * @pre NULL != reg_entry * @pre NULL != buf * @pre len >= 0 * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_contains(reg_entry_t *reg_entry, void *buf, int len) { #if DEBUG printf("[%d] reg_entry_contains(reg_entry=%p, buf=%p, len=%d)\n", g_state.rank, reg_entry, buf, len); #endif /* preconditions */ CMX_ASSERT(NULL != reg_entry); /*CMX_ASSERT(NULL != buf);*/ CMX_ASSERT(len >= 0); return seg_contains( reg_entry->buf, reg_entry->len, buf, len); } /** * Remove registration window entry without deregistration. * * @param[in] rank the local rank (on group) where the entry came from * @param[in] reg_entry the entry * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_destroy(int rank, reg_entry_t *reg_entry) { #if DEBUG printf("[%d] reg_entry_destroy(rank=%d, reg_entry=%p)\n" "buf=%p len=%zu name=%s mapped=%p\n", g_state.rank, rank, reg_entry, reg_entry->buf, reg_entry->len, reg_entry->name, reg_entry->mapped); #endif /* preconditions */ CMX_ASSERT(NULL != reg_entry); CMX_ASSERT(0 <= rank && rank < reg_nprocs); /* free window entry */ free(reg_entry); return RR_SUCCESS; } /** * Create internal data structures for the registration window. * This function is called once to initialize the internal data * structures and cannot be called again until reg_entry_destroy() has been * called * * @param[in] nprocs number of registration windows to create i.e. one per * process * * @return RR_SUCCESS on success */ reg_return_t reg_entry_init(int nprocs) { int i = 0; #ifdef TEST_DEBUG reg_entry_nprocs = nprocs; MPI_Comm_rank(MPI_COMM_WORLD,®_entry_rank); #endif #if DEBUG printf("[%d] reg_entry_init(nprocs=%d)\n", g_state.rank, nprocs); #endif /* preconditions */ CMX_ASSERT(0 == reg_nprocs); /* keep the number of processors around for later use */ reg_nprocs = nprocs; /* allocate the registration window list: */ reg_entries = (reg_entry_t **)malloc(sizeof(reg_entry_t*) * reg_nprocs); CMX_ASSERT(reg_entries); /* initialize the registration window list: */ for (i = 0; i < reg_nprocs; ++i) { reg_entries[i] = NULL; } return RR_SUCCESS; } /** * Deregister and destroy all window entries and associated buffers. * * @pre this function is called once to destroy the internal data structures * and cannot be called again until reg_entry_init() has been called * * @see reg_entry_init() * * @return RR_SUCCESS on success */ reg_return_t reg_entries_destroy() { int i = 0; #if DEBUG printf("[%d] reg_entries_destroy()\n", g_state.rank); #endif /* preconditions */ CMX_ASSERT(NULL != reg_entries); CMX_ASSERT(0 != reg_nprocs); for (i = 0; i < reg_nprocs; ++i) { reg_entry_t *runner = reg_entries[i]; while (runner) { reg_entry_t *previous = runner; /* pointer to previous runner */ /* get next runner */ runner = runner->next; /* destroy the entry */ reg_entry_destroy(i, previous); } } /* free registration window list */ free(reg_entries); reg_entries = NULL; /* reset the number of windows */ reg_nprocs = 0; return RR_SUCCESS; } /** * Locate a registration window entry which contains the given segment * completely. * * @param[in] rank the local rank (on group) of the process * @param[in] buf starting address of the buffer * @parma[in] len length of the buffer * * @return the reg window entry, or NULL on failure */ reg_entry_t* reg_entry_find(int rank, void *buf, int len) { reg_entry_t *entry = NULL; reg_entry_t *runner = NULL; #ifdef TEST_DEBUG printf("p[%d] reg_entry_find(rank=%d, buf=%p, len=%d reg_entries[%d]=%p)\n", reg_entry_rank, rank, buf, len, rank, reg_entries[rank]); #endif /* preconditions */ CMX_ASSERT(NULL != reg_entries); CMX_ASSERT(0 <= rank && rank < reg_nprocs); runner = reg_entries[rank]; while (runner && NULL == entry) { #ifdef TEST_DEBUG printf("p[%d] rank: %d runner: %p buf: %p len: %d\n", reg_entry_rank,rank,runner,runner->buf,runner->len); #endif if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { entry = runner; #ifdef TEST_DEBUG printf("p[%d] reg_entry_find entry found " "runner=%p buf=%p len=%d " "runner: rank=%d buf=%p len=%d\n", reg_entry_rank, runner, buf, len, runner->rank, runner->buf, runner->len); #endif } runner = runner->next; } #ifndef NDEBUG /* we CMX_ASSERT that the found entry was unique. This code checks all * entries after the one found in the previous loop to see if there * is another overlap. */ while (runner) { if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { #ifdef TEST_DEBUG printf("[%d] reg_entry_find duplicate found " "runner=%p buf=%p len=%d " "rank=%d buf=%p len=%d\n", reg_entry_rank, runner, buf, len, runner->rank, runner->buf, runner->len); #endif CMX_ASSERT(0); } runner = runner->next; } #endif return entry; } /** * Locate a registration window entry which intersects the given segment. * (probably not used) * * @param[in] rank local rank (on group) of the process * @param[in] buf starting address of the buffer * @parma[in] len length of the buffer * * @return the reg window entry, or NULL on failure */ reg_entry_t* reg_entry_find_intersection(int rank, void *buf, int len) { reg_entry_t *entry = NULL; reg_entry_t *runner = NULL; #if DEBUG printf("[%d] reg_entry_find_intersection(rank=%d, buf=%p, len=%d)\n", g_state.rank, rank, buf, len); #endif /* preconditions */ CMX_ASSERT(NULL != reg_entries); CMX_ASSERT(0 <= rank && rank < reg_nprocs); runner = reg_entries[rank]; while (runner && NULL == entry) { if (RR_SUCCESS == reg_entry_intersects(runner, buf, len)) { entry = runner; } runner = runner->next; } /* we CMX_ASSERT that the found entry was unique */ while (runner) { if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { CMX_ASSERT(0); } runner = runner->next; } return entry; } /** * Create a new registration entry based on the given members. * * @param[in] rank local rank (on group) of processor inserting entry * @param[in] buf pointer to local memory allocation * @param[in] len length of local memory allocation in bytes * @param[in] cmx_hdl handle of CMX allocation * @return pointer to window registration entry for this cmx_handle_t */ reg_entry_t* reg_entry_insert(int world_rank, void *buf, int len, cmx_handle_t *cmx_hdl) { reg_entry_t *node = NULL; #ifdef TEST_DEBUG printf("p[%d] reg_entry_insert(rank=%d, buf=%p, len=%d)\n", reg_entry_rank, world_rank, buf, len); #endif /* preconditions */ CMX_ASSERT(NULL != reg_entries); CMX_ASSERT(0 <= world_rank && world_rank < reg_nprocs); /* CMX_ASSERT(NULL != buf); */ CMX_ASSERT(len >= 0); CMX_ASSERT(NULL == reg_entry_find(world_rank, buf, len)); CMX_ASSERT(NULL == reg_entry_find_intersection(world_rank, buf, len)); /* allocate the new entry */ node = (reg_entry_t *)malloc(sizeof(reg_entry_t)); CMX_ASSERT(node); /* initialize the new entry */ node->rank = world_rank; node->buf = buf; node->len = len; node->hdl = cmx_hdl; node->next = NULL; /* push new entry to tail of linked list */ if (NULL == reg_entries[world_rank]) { reg_entries[world_rank] = node; } else { reg_entry_t *runner = reg_entries[world_rank]; while (runner->next) { runner = runner->next; } runner->next = node; } return node; } /** * Removes the reg window entry associated with the given rank and buffer. * This does not destroy the armci_handle_t object, this must be done * separately. * * @param[in] rank world rank of processor calling this function * @param[in] buf pointer to memory allocation * * @return RR_SUCCESS on success * RR_FAILURE otherwise */ reg_return_t reg_entry_delete(int rank, void *buf) { reg_return_t status = RR_FAILURE; reg_entry_t *runner = NULL; reg_entry_t *previous_runner = NULL; #ifdef TEST_DEBUG printf("p[%d] reg_entry_delete(rank=%d, buf=%p)\n", reg_entry_rank, rank, buf); #endif /* preconditions */ CMX_ASSERT(NULL != reg_entries); CMX_ASSERT(0 <= rank && rank < reg_nprocs); /* CMX_ASSERT(NULL != buf); */ CMX_ASSERT(NULL != reg_entry_find(rank, buf, 0)); /* this is more restrictive than reg_entry_find() in that we locate * exactly the same region starting address */ runner = reg_entries[rank]; while (runner) { if (runner->buf == buf) { break; } /* no match so match may be next runner */ previous_runner = runner; runner = runner->next; } /* we should have found an entry */ if (NULL == runner) { CMX_ASSERT(0); return RR_FAILURE; } /* pop the entry out of the linked list */ if (previous_runner) { /* runner is a match and it is not the first entry in list */ previous_runner->next = runner->next; } else { /* buf corresponds to first entry in list */ reg_entries[rank] = reg_entries[rank]->next; } status = reg_entry_destroy(rank, runner); return status; } ga-5.9.2/cmx/src-armci/reg_entry.h000066400000000000000000000020621500715745200167420ustar00rootroot00000000000000#ifndef _REG_WINDOW_H_ #define _REG_WINDOW_H_ #include #include /** * Enumerate the return codes for registration window functions. */ typedef enum _reg_return_t { RR_SUCCESS=0, /**< success */ RR_FAILURE /**< non-specific failure */ } reg_return_t; /** * A registered contiguous memory region. */ typedef struct _reg_entry_t { cmx_handle_t* hdl; int rank; /**< global rank where this region lives */ void* buf; /**< starting address of region */ size_t len; /**< length of region */ struct _reg_entry_t *next; /**< next memory region in list */ } reg_entry_t; /* functions * * documentation is in the *.c file */ extern reg_return_t reg_entry_init(int nprocs); extern reg_return_t reg_entries_destroy(); extern reg_entry_t *reg_entry_find(int rank, void *buf, int len); extern reg_entry_t *reg_entry_insert(int world_rank, void *buf, int len, cmx_handle_t *hdl); extern reg_return_t reg_entry_delete(int rank, void *buf); #endif /* _REG_WINDOW_H_ */ ga-5.9.2/cmx/src-armci/testing/000077500000000000000000000000001500715745200162505ustar00rootroot00000000000000ga-5.9.2/cmx/src-armci/testing/testwrap.c000066400000000000000000001472541500715745200203020ustar00rootroot00000000000000/* #if HAVE_CONFIG_H # include "config.h" #endif */ /* $Id: test.c,v 1.43.6.6 2007-08-30 22:59:27 manoj Exp $ */ # include # include # include # include #if HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif #include "armci.h" #include "message.h" #define MEMLOCK_TEST 0 #if MEMLOCK_TEST extern void armci_lockmem(void *, void *, int); extern void armci_unlockmem(void); #endif #define DIM1 5 #define DIM2 3 #ifdef __sun /* Solaris has shared memory shortages in the default system configuration */ # define DIM3 6 # define DIM4 5 # define DIM5 4 #elif defined(__alpha__) # define DIM3 8 # define DIM4 5 # define DIM5 6 #else # define DIM3 8 # define DIM4 9 # define DIM5 7 #endif #define DIM6 3 #define DIM7 2 #define OFF 1 #define EDIM1 (DIM1+OFF) #define EDIM2 (DIM2+OFF) #define EDIM3 (DIM3+OFF) #define EDIM4 (DIM4+OFF) #define EDIM5 (DIM5+OFF) #define EDIM6 (DIM6+OFF) #define EDIM7 (DIM7+OFF) #define DIMS 4 #define MAXDIMS 7 #define MAX_DIM_VAL 50 #define LOOP 200 #define BASE 100. #define MAXPROC 128 #define TIMES 100 # define ELEMS 200 /***************************** macros ************************/ #define COPY(src, dst, bytes) memcpy((dst),(src),(bytes)) #define ARMCI_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define ARMCI_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define ARMCI_ABS(a) (((a) <0) ? -(a) : (a)) /***************************** global data *******************/ int me, nproc; int work[MAXPROC]; /* work array for propagating addresses */ /*\ generate random range for a section of multidimensional array \*/ void get_range(int ndim, int dims[], int lo[], int hi[]) { int dim; for (dim = 0; dim < ndim; dim++) { int toss1, toss2; toss1 = rand() % dims[dim]; toss2 = rand() % dims[dim]; if (toss1 < toss2) { lo[dim] = toss1; hi[dim] = toss2; } else { hi[dim] = toss1; lo[dim] = toss2; } } } /*\ generates a new random range similar to the input range for an array with specified dimensions \*/ void new_range(int ndim, int dims[], int lo[], int hi[], int new_lo[], int new_hi[]) { int dim; for (dim = 0; dim < ndim; dim++) { int toss, range; int diff = hi[dim] - lo[dim] + 1; assert(diff <= dims[dim]); range = dims[dim] - diff; toss = (range > 0) ? rand() % range : lo[dim]; new_lo[dim] = toss; new_hi[dim] = toss + diff - 1; assert(new_hi[dim] < dims[dim]); assert(diff == (new_hi[dim] - new_lo[dim] + 1)); } } /*\ print range of ndim dimensional array with two strings before and after \*/ void print_range(char *pre, int ndim, int lo[], int hi[], char *post) { int i; printf("%s[", pre); for (i = 0; i < ndim; i++) { printf("%d:%d", lo[i], hi[i]); if (i == ndim - 1) { printf("] %s", post); } else { printf(","); } } } /*\ print subscript of ndim dimensional array with two strings before and after \*/ void print_subscript(char *pre, int ndim, int subscript[], char *post) { int i; printf("%s [", pre); for (i = 0; i < ndim; i++) { printf("%d", subscript[i]); if (i == ndim - 1) { printf("] %s", post); } else { printf(","); } } } /*\ print a section of a 2-D array of doubles \*/ void print_2D_double(double *a, int ld, int *lo, int *hi) { int i, j; for (i = lo[0]; i <= hi[0]; i++) { for (j = lo[1]; j <= hi[1]; j++) { printf("%13f ", a[ld*j+i]); } printf("\n"); } } /*\ initialize array: a[i,j,k,..]=i+100*j+10000*k+ ... \*/ void init(double *a, int ndim, int elems, int dims[]) { int idx[MAXDIMS]; int i, dim; for (i = 0; i < elems; i++) { int Index = i; double field, val; for (dim = 0; dim < ndim; dim++) { idx[dim] = Index % dims[dim]; Index /= dims[dim]; } field = 1.; val = 0.; for (dim = 0; dim < ndim; dim++) { val += field * idx[dim]; field *= BASE; } a[i] = val; /* printf("(%d,%d,%d)=%6.0f",idx[0],idx[1],idx[2],val); */ } } /*\ compute Index from subscript * assume that first subscript component changes first \*/ int Index(int ndim, int subscript[], int dims[]) { int idx = 0, i, factor = 1; for (i = 0; i < ndim; i++) { idx += subscript[i] * factor; factor *= dims[i]; } return idx; } void update_subscript(int ndim, int subscript[], int lo[], int hi[], int dims[]) { int i; for (i = 0; i < ndim; i++) { if (subscript[i] < hi[i]) { subscript[i]++; return; } subscript[i] = lo[i]; } } void compare_patches(double eps, int ndim, double *patch1, int lo1[], int hi1[], int dims1[], double *patch2, int lo2[], int hi2[], int dims2[]) { int i, j, elems = 1; int subscr1[MAXDIMS], subscr2[MAXDIMS]; double diff, max; int offset1, offset2; for (i = 0; i < ndim; i++) { /* count # of elements & verify consistency of both patches */ int diff = hi1[i] - lo1[i]; assert(diff == (hi2[i] - lo2[i])); assert(diff < dims1[i]); assert(diff < dims2[i]); elems *= diff + 1; subscr1[i] = lo1[i]; subscr2[i] = lo2[i]; } /* compare element values in both patches */ offset1 = Index(ndim, subscr1, dims1); offset2 = Index(ndim, subscr2, dims2); for (j = 0; j < elems; j++) { int idx1, idx2; idx1 = Index(ndim, subscr1, dims1); /* calculate element Index from a subscript */ idx2 = Index(ndim, subscr2, dims2); idx1 -= offset1; idx2 -= offset2; diff = patch1[idx1] - patch2[idx2]; max = ARMCI_MAX(ARMCI_ABS(patch1[idx1]), ARMCI_ABS(patch2[idx2])); if (max == 0. || max < eps) { max = 1.; } if (eps < ARMCI_ABS(diff) / max) { char msg[48]; sprintf(msg, "(proc=%d):%f", me, patch1[idx1]); print_subscript("ERROR: a", ndim, subscr1, msg); sprintf(msg, "%f\n", patch2[idx2]); print_subscript(" b", ndim, subscr2, msg); fflush(stdout); sleep(1); ARMCI_Error("Bailing out", 0); } { /* update subscript for the patches */ update_subscript(ndim, subscr1, lo1, hi1, dims1); update_subscript(ndim, subscr2, lo2, hi2, dims2); } } /* make sure we reached upper limit */ /*for(i=0;i= 100) { ARMCI_Error("increase MMAX", g_idx); } ARMCI_Memget(bytes, &meminfo[g_idx][me], 0); for (i = 0; i < nproc; i++) { armci_msg_brdcst(&meminfo[g_idx][i], sizeof(armci_meminfo_t), i); } for (i = 0; i < nproc; i++) { a[i] = ARMCI_Memat(&meminfo[g_idx][i], 0); } g_idx++; } #else rc = ARMCI_Malloc(a, bytes); assert(rc == 0); #endif assert(a[me]); } void destroy_array(void *ptr[]) { ARMCI_Barrier(); #if 0 assert(!ARMCI_Free(ptr[me])); #endif } int loA[MAXDIMS], hiA[MAXDIMS]; int dimsA[MAXDIMS] = {DIM1, DIM2, DIM3, DIM4, DIM5, DIM6, DIM7}; int loB[MAXDIMS], hiB[MAXDIMS]; int dimsB[MAXDIMS] = {EDIM1, EDIM2, EDIM3, EDIM4, EDIM5, EDIM6, EDIM7}; int count[MAXDIMS]; int strideA[MAXDIMS], strideB[MAXDIMS]; int loC[MAXDIMS], hiC[MAXDIMS]; int idx[MAXDIMS] = {0, 0, 0, 0, 0, 0, 0}; void test_dim(int ndim) { int dim, elems; int i, j, proc; /* double a[DIM4][DIM3][DIM2][DIM1], b[EDIM4][EDIM3][EDIM2][EDIM1];*/ void *b[MAXPROC]; void *a, *c; elems = 1; strideA[0] = sizeof(double); strideB[0] = sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } elems *= dimsA[i]; } /* create shared and local arrays */ create_array(b, sizeof(double), ndim, dimsB); a = malloc(sizeof(double) * elems); assert(a); c = malloc(sizeof(double) * elems); assert(c); init(a, ndim, elems, dimsA); if (me == 0) { printf("--------array[%d", dimsA[0]); for (dim = 1; dim < ndim; dim++) { printf(",%d", dimsA[dim]); } printf("]--------\n"); } sleep(1); ARMCI_AllFence(); ARMCI_Barrier(); for (i = 0; i < LOOP; i++) { int idx1, idx2, idx3; get_range(ndim, dimsA, loA, hiA); new_range(ndim, dimsB, loA, hiA, loB, hiB); new_range(ndim, dimsA, loA, hiA, loC, hiC); proc = nproc - 1 - me; if (me == 0) { print_range("local", ndim, loA, hiA, "-> "); print_range("remote", ndim, loB, hiB, "-> "); print_range("local", ndim, loC, hiC, "\n"); } idx1 = Index(ndim, loA, dimsA); idx2 = Index(ndim, loB, dimsB); idx3 = Index(ndim, loC, dimsA); for (j = 0; j < ndim; j++) { count[j] = hiA[j] - loA[j] + 1; } count[0] *= sizeof(double); /* convert range to bytes at stride level zero */ (void)ARMCI_PutS((double *)a + idx1, strideA, (double *)b[proc] + idx2, strideB, count, ndim - 1, proc); /* sleep(1);*/ /* printf("%d: a=(%x,%f) b=(%x,%f)\n",me,idx1 + (double*)a,*(idx1 + (double*)a),idx2 + (double*)b,*(idx2 + (double*)b));*/ /* fflush(stdout);*/ /* sleep(1);*/ /* note that we do not need ARMCI_Fence here since * consectutive operations targeting the same process are ordered */ (void)ARMCI_GetS((double *)b[proc] + idx2, strideB, (double *)c + idx3, strideA, count, ndim - 1, proc); compare_patches(0., ndim, (double *)a + idx1, loA, hiA, dimsA, (double *)c + idx3, loC, hiC, dimsA); } free(c); destroy_array(b); free(a); } int nloA[MAXDIMS+1][MAXDIMS], nhiA[MAXDIMS+1][MAXDIMS]; int nloB[MAXDIMS+1][MAXDIMS], nhiB[MAXDIMS+1][MAXDIMS]; int nloC[MAXDIMS+1][MAXDIMS], nhiC[MAXDIMS+1][MAXDIMS]; int get_next_RRproc(int initialize, int ndim) { static int distance; int proc; /* Initialize distance and return process 0*/ if (initialize) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } if (nproc == 1) { distance = 0; } return(0); } /*send it to a different process everytime based on the current value of * distance */ proc = (me <= ((nproc % 2 == 0) ? ((nproc / 2) - 1) : (nproc / 2))) ? (me + distance) : (me - distance); if ((nproc % 2) != 0 && me == (nproc / 2)) { proc = me; } /* find a new value for distance */ if (distance != 0) { if (me < (nproc / 2)) { distance++; if ((me + distance) >= nproc) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } distance -= me; } } else { distance--; if ((me - distance) >= (nproc / 2)) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } distance = distance + (me - distance); } } if (ndim != 1 && MAXDIMS > nproc && (ndim % (nproc / 2) == 0)) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } } } return(proc); } void test_nbdim() { int elems = 1, elems1 = 1; int i, j, proc, ndim, rc; void *b[MAXDIMS+1][MAXPROC]; void *a[MAXDIMS+1], *c[MAXDIMS+1]; armci_hdl_t hdl_put[MAXDIMS+1], hdl_get[MAXDIMS+1]; int idx1 = 0, idx2 = 0, idx3 = 0; /* create shared and local arrays */ for (ndim = 1; ndim <= MAXDIMS; ndim++) { elems1 *= dimsB[ndim-1]; elems *= dimsA[ndim-1]; rc = ARMCI_Malloc(b[ndim], sizeof(double) * elems1); assert(rc == 0); assert(b[ndim][me]); a[ndim] = malloc(sizeof(double) * elems); assert(a[ndim]); c[ndim] = malloc(sizeof(double) * elems); assert(c[ndim]); init(a[ndim], ndim, elems, dimsA); ARMCI_INIT_HANDLE(hdl_put + ndim); ARMCI_INIT_HANDLE(hdl_get + ndim); } ARMCI_AllFence(); ARMCI_Barrier(); (void)get_next_RRproc(1, 0); for (ndim = 1; ndim <= MAXDIMS; ndim++) { strideA[0] = sizeof(double); strideB[0] = sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } } proc = get_next_RRproc(0, ndim); get_range(ndim, dimsA, nloA[ndim], nhiA[ndim]); new_range(ndim, dimsB, nloA[ndim], nhiA[ndim], nloB[ndim], nhiB[ndim]); new_range(ndim, dimsA, nloA[ndim], nhiA[ndim], nloC[ndim], nhiC[ndim]); if (me == 0) { print_range("local", ndim, nloA[ndim], nhiA[ndim], "-> "); print_range("remote", ndim, nloB[ndim], nhiB[ndim], "-> "); print_range("local", ndim, nloC[ndim], nhiC[ndim], "\n"); fflush(stdout); sleep(1); } idx1 = Index(ndim, nloA[ndim], dimsA); idx2 = Index(ndim, nloB[ndim], dimsB); idx3 = Index(ndim, nloC[ndim], dimsA); for (j = 0; j < ndim; j++) { count[j] = nhiA[ndim][j] - nloA[ndim][j] + 1; } count[0] *= sizeof(double); if (ndim == 1) { (void)ARMCI_NbPut((double *)a[ndim] + idx1, (double *)b[ndim][proc] + idx2, count[0], proc, (hdl_put + ndim)); } else { (void)ARMCI_NbPutS((double *)a[ndim] + idx1, strideA, (double *)b[ndim][proc] + idx2, strideB, count, ndim - 1, proc, (hdl_put + ndim)); } } sleep(5); ARMCI_Barrier(); /*before we do gets, we have to make sure puts are complete on the remote processor*/ for (ndim = 1; ndim <= MAXDIMS; ndim++) { ARMCI_Wait(hdl_put + ndim); } ARMCI_Barrier(); ARMCI_AllFence(); (void)get_next_RRproc(1, 0); for (ndim = 1; ndim <= MAXDIMS; ndim++) { strideA[0] = sizeof(double); strideB[0] = sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } } /*send it to a different process everytime*/ proc = get_next_RRproc(0, ndim); idx1 = Index(ndim, nloA[ndim], dimsA); idx2 = Index(ndim, nloB[ndim], dimsB); idx3 = Index(ndim, nloC[ndim], dimsA); for (j = 0; j < ndim; j++) { count[j] = nhiA[ndim][j] - nloA[ndim][j] + 1; } count[0] *= sizeof(double); if (ndim == 1) { (void)ARMCI_NbGet((double *)b[ndim][proc] + idx2, (double *)c[ndim] + idx3, count[0], proc, (hdl_get + ndim)); } else { (void)ARMCI_NbGetS((double *)b[ndim][proc] + idx2, strideB, (double *)c[ndim] + idx3, strideA, count, ndim - 1, proc, (hdl_get + ndim)); } } ARMCI_Barrier(); if (me == 0) { printf("Now waiting for all non-blocking calls and verifying data...\n"); fflush(stdout); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { double *ptr = (double*)b[ndim][me]+idx2; ARMCI_Wait(hdl_get + ndim); idx1 = Index(ndim, nloA[ndim], dimsA); idx2 = Index(ndim, nloB[ndim], dimsB); idx3 = Index(ndim, nloC[ndim], dimsA); compare_patches(0., ndim, (double *)a[ndim] + idx1, nloA[ndim], nhiA[ndim], dimsA, (double *)c[ndim] + idx3, nloC[ndim], nhiC[ndim], dimsA); } if (me == 0) { printf("OK\n"); fflush(stdout); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { destroy_array(b[ndim]); free(c[ndim]); free(a[ndim]); } } #define PTR_ARR_LEN 5 /* 10*/ #define VLOOP 50 #define VEC_ELE_LEN 10 /* 20 */ /*number of doubles in each dimension*/ #define GIOV_ARR_LEN 9 /*9 */ void verify_vector_data(double *data, int procs, int isput, int datalen) { double facto = 2.89; int i, j = 0, k = 0, kc = 0, dst = 0; if (isput) { facto = 1.89; } for (i = 0; i < datalen; i++) { if (dst != me) if (ARMCI_ABS((data[i] - (me + facto + dst)*((kc + 1) * (j % PTR_ARR_LEN + 1)))) > 0.001) { printf("\n%d:while verifying data of a op from proc=%d ", me, dst); printf("giov index=%d ptr_arr_index=%d \n :element index=%d", kc, (j % PTR_ARR_LEN), k); printf(" elem was supposed to be %f but is %f", (me + facto + dst)*((kc + 1)*(j % PTR_ARR_LEN + 1)) , data[i]); fflush(stdout); sleep(1); ARMCI_Error("vector non-blocking failed", 0); } k++; if (k == VEC_ELE_LEN) { j++; k = 0; if (j % PTR_ARR_LEN == 0) { kc++; if ((kc % GIOV_ARR_LEN) == 0) { kc = 0; dst++; } } } } } void test_vec_small() { double *getdst; double **putsrc; armci_giov_t dsc[MAXPROC*GIOV_ARR_LEN]; void **psrc; /*arrays of pointers to be used by giov_t*/ void **pdst; void *getsrc[MAXPROC]; /*to allocate mem via armci_malloc*/ void *putdst[MAXPROC]; /*to allocate mem via armci_malloc*/ armci_hdl_t hdl_put[MAXPROC], hdl_get[MAXPROC]; int i = 0, j = 0, k = 0, kc = 0, kcold = 0, rc, dstproc, dst = 0; int lenpergiov; lenpergiov = PTR_ARR_LEN * VEC_ELE_LEN; rc = ARMCI_Malloc(getsrc, sizeof(double) * nproc * GIOV_ARR_LEN * lenpergiov); assert(rc == 0); assert(getsrc[me]); rc = ARMCI_Malloc(putdst, sizeof(double) * nproc * GIOV_ARR_LEN * lenpergiov); assert(rc == 0); assert(putdst[me]); /*first malloc for getdst and putsrc, both are 2d arrays*/ getdst = (double *)malloc(sizeof(double) * nproc * GIOV_ARR_LEN * lenpergiov); putsrc = (double **)malloc(sizeof(double *) * nproc * GIOV_ARR_LEN * PTR_ARR_LEN); assert(getdst); assert(putsrc); for (i = 0; i < nproc * GIOV_ARR_LEN * PTR_ARR_LEN; i++) { putsrc[i] = (double *)malloc(sizeof(double) * VEC_ELE_LEN); assert(putsrc[i]); } /*allocating memory for psrc and pdst*/ psrc = (void **)malloc(sizeof(void *) * PTR_ARR_LEN * nproc * GIOV_ARR_LEN); pdst = (void **)malloc(sizeof(void *) * PTR_ARR_LEN * nproc * GIOV_ARR_LEN); assert(pdst); assert(psrc); for (i = 0; i < nproc * lenpergiov * GIOV_ARR_LEN; i++) { putsrc[j][k] = (me + 1.89 + dst) * ((kc + 1) * ((j % PTR_ARR_LEN) + 1)); ((double *)getsrc[me])[i] = (me + 2.89 + dst) * ((kc + 1) * (j % PTR_ARR_LEN + 1)); k++; if (k == VEC_ELE_LEN) { j++; k = 0; if ((j % PTR_ARR_LEN) == 0) { kc++; if ((kc % GIOV_ARR_LEN) == 0) { kc = 0; dst++; } } } } /* for (i=0; i< nproc * GIOV_ARR_LEN * lenpergiov; i++) { printf("p[%d] putsrc[%d]: %f\n",me,i,((double*)putsrc)[i]); } */ /*********************Testing NbPutV*********************************/ i = 0; j = 0; k = 0; kc = 0; dstproc = me; for (i = 0; i < nproc - 1; i++) { dstproc++; if (dstproc == nproc) { dstproc = 0; } for (j = 0; j < GIOV_ARR_LEN; j++) { kcold = kc; for (k = 0; k < PTR_ARR_LEN; k++, kc++) { double *ptr; psrc[kc] = (void *)putsrc[PTR_ARR_LEN*(dstproc*GIOV_ARR_LEN+j)+k]; ptr = (double *)putdst[dstproc]; pdst[kc] = (void *)(ptr + lenpergiov * (GIOV_ARR_LEN * me + j) + k * VEC_ELE_LEN); } dsc[j].bytes = VEC_ELE_LEN * sizeof(double); dsc[j].src_ptr_array = &psrc[kcold]; dsc[j].dst_ptr_array = &pdst[kcold]; dsc[j].ptr_array_len = PTR_ARR_LEN; } ARMCI_INIT_HANDLE(hdl_put + dstproc); if ((rc = ARMCI_NbPutV(dsc, GIOV_ARR_LEN, dstproc, hdl_put + dstproc))) { ARMCI_Error("putv failed", rc); } } if (me == 0) { printf("\n\tNow verifying the vector put data for correctness"); } for (i = 0; i < nproc; i++)if (i != me) { ARMCI_Wait(hdl_put + i); } sleep(1); ARMCI_Barrier(); ARMCI_AllFence();/*every one syncs after get */ verify_vector_data((double *)putdst[me], nproc, 1, nproc * GIOV_ARR_LEN * lenpergiov); if (me == 0) { printf("\n\tPuts OK\n"); } /****************Done Testing NbPutV*********************************/ /*********************Testing NbGetV*********************************/ i = 0; j = 0; k = 0; kc = 0; dstproc = me; for (i = 0; i < nproc - 1; i++) { dstproc++; if (dstproc == nproc) { dstproc = 0; } for (j = 0; j < GIOV_ARR_LEN; j++) { kcold = kc; for (k = 0; k < PTR_ARR_LEN; k++, kc++) { double *ptr; ptr = getdst; pdst[kc] = (void *)(ptr + lenpergiov * (dstproc * GIOV_ARR_LEN + j) + k * VEC_ELE_LEN); ptr = (double *)(getsrc[dstproc]); psrc[kc] = (void *)(ptr + lenpergiov * (me * GIOV_ARR_LEN + j) + k * VEC_ELE_LEN); } dsc[j].bytes = VEC_ELE_LEN * sizeof(double); dsc[j].src_ptr_array = &psrc[kcold]; dsc[j].dst_ptr_array = &pdst[kcold]; dsc[j].ptr_array_len = PTR_ARR_LEN; } ARMCI_INIT_HANDLE(hdl_get + dstproc); if ((rc = ARMCI_NbGetV(dsc, GIOV_ARR_LEN, dstproc, hdl_get + dstproc))) { ARMCI_Error("putv failed", rc); } } if (me == 0) { printf("\n\tNow verifying the vector get data for correctness"); } for (i = 0; i < nproc; i++)if (i != me) { ARMCI_Wait(hdl_get + i); } sleep(1); ARMCI_Barrier(); verify_vector_data((double *)getdst, nproc, 0, nproc * GIOV_ARR_LEN * lenpergiov); if (me == 0) { printf("\n\tGets OK\n"); } /****************Done Testing NbGetV*********************************/ free(pdst); free(psrc); free(getdst); for (i = 0; i < nproc * GIOV_ARR_LEN * PTR_ARR_LEN; i++) { free(putsrc[i]); } free(putsrc); } void GetPermutedProcList(int *ProcList) { int i, iswap, temp; if (nproc > MAXPROC) { ARMCI_Error("permute_proc: nproc to big ", nproc); } /* initialize list */ for (i = 0; i < nproc; i++) { ProcList[i] = i; } if (nproc == 1) { return; } /* every process generates different random sequence */ (void)srand((unsigned)me); /* list permutation generated by random swapping */ for (i = 0; i < nproc; i++) { iswap = (int)(rand() % nproc); temp = ProcList[iswap]; ProcList[iswap] = ProcList[i]; ProcList[i] = temp; } } /*\ Atomic Accumulate test: remote += alpha*local * Every process/or has its patch of array b updated TIMES*NPROC times. * The sequence of updates is random: everybody uses a randomly permuted list * and accumulate is non-collective (of-course) \*/ void test_acc(int ndim) { int dim, elems; int i, proc; void *b[MAXPROC]; void *a, *c; double alpha = 0.1, scale; int idx1, idx2; int *proclist = work; elems = 1; strideA[0] = sizeof(double); strideB[0] = sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } elems *= dimsA[i]; /* set up patch coordinates: same on every processor */ loA[i] = 0; hiA[i] = loA[i] + 1; loB[i] = dimsB[i] - 2; hiB[i] = loB[i] + 1; count[i] = hiA[i] - loA[i] + 1; } /* create shared and local arrays */ create_array(b, sizeof(double), ndim, dimsB); a = malloc(sizeof(double) * elems); assert(a); c = malloc(sizeof(double) * elems); assert(c); init(a, ndim, elems, dimsA); if (me == 0) { printf("--------array[%d", dimsA[0]); for (dim = 1; dim < ndim; dim++) { printf(",%d", dimsA[dim]); } printf("]--------\n"); } GetPermutedProcList(proclist); idx1 = Index(ndim, loA, dimsA); idx2 = Index(ndim, loB, dimsB); count[0] *= sizeof(double); /* convert range to bytes at stride level zero */ /* initialize all elements of array b to zero */ elems = 1; for (i = 0; i < ndim; i++) { elems *= dimsB[i]; } for (i = 0; i < elems; i++) { ((double *)b[me])[i] = 0.; } sleep(1); if (me == 0) { print_range("patch", ndim, loA, hiA, " -> "); print_range("patch", ndim, loB, hiB, "\n"); fflush(stdout); } ARMCI_AllFence(); ARMCI_Barrier(); for (i = 0; i < TIMES * nproc; i++) { proc = proclist[i%nproc]; (void)ARMCI_AccS(ARMCI_ACC_DBL, &alpha, (double *)a + idx1, strideA, (double *)b[proc] + idx2, strideB, count, ndim - 1, proc); } /* sleep(9);*/ ARMCI_AllFence(); ARMCI_Barrier(); /* copy my patch into local array c */ (void)ARMCI_GetS((double *)b[me] + idx2, strideB, (double *)c + idx1, strideA, count, ndim - 1, me); scale = alpha * TIMES * nproc; scale_patch(scale, ndim, (double *)a + idx1, loA, hiA, dimsA); compare_patches(.0001, ndim, (double *)a + idx1, loA, hiA, dimsA, (double *)c + idx1, loA, hiA, dimsA); ARMCI_Barrier(); if (0 == me) { printf(" OK\n\n"); fflush(stdout); } free(c); destroy_array(b); free(a); } /*************************** vector interface *********************************\ * tests vector interface for transfers of triangular sections of a 2-D array * ******************************************************************************/ void test_vector() { int dim, elems, ndim, cols, rows, mrc; int i, proc, loop; int rc; int idx1, idx3; void *b[MAXPROC]; void *a, *c; armci_giov_t dsc[MAX_DIM_VAL]; void *psrc[MAX_DIM_VAL]; void *pdst[MAX_DIM_VAL]; elems = 1; ndim = 2; for (i = 0; i < ndim; i++) { dimsA[i] = MAX_DIM_VAL; dimsB[i] = MAX_DIM_VAL + 1; elems *= dimsA[i]; } /* create shared and local arrays */ create_array(b, sizeof(double), ndim, dimsB); a = malloc(sizeof(double) * elems); assert(a); c = malloc(sizeof(double) * elems); assert(c); init(a, ndim, elems, dimsA); if (me == 0) { printf("--------array[%d", dimsA[0]); for (dim = 1; dim < ndim; dim++) { printf(",%d", dimsA[dim]); } printf("]--------\n"); } sleep(1); for (loop = 0; loop < LOOP; loop++) { get_range(ndim, dimsA, loA, hiA); new_range(ndim, dimsB, loA, hiA, loB, hiB); new_range(ndim, dimsA, loA, hiA, loC, hiC); proc = nproc - 1 - me; if (me == 0) { print_range("local", ndim, loA, hiA, "-> "); print_range("remote", ndim, loB, hiB, "-> "); print_range("local", ndim, loC, hiC, "\n"); } /* printf("array at source\n");*/ /* print_2D_double((double *)a, dimsA[0], loA, hiA);*/ cols = hiA[1] - loA[1] + 1; rows = hiA[0] - loA[0] + 1; mrc = ARMCI_MIN(cols, rows); /* generate a data descriptor for a lower-triangular patch */ for (i = 0; i < mrc; i++) { int ij[2]; int idx; ij[0] = loA[0] + i; ij[1] = loA[1] + i; idx = Index(ndim, ij, dimsA); psrc[i] = (double *)a + idx; ij[0] = loB[0] + i; ij[1] = loB[1] + i; idx = Index(ndim, ij, dimsB); pdst[i] = (double *)b[proc] + idx; dsc[i].bytes = (rows - i) * sizeof(double); dsc[i].src_ptr_array = &psrc[i]; dsc[i].dst_ptr_array = &pdst[i]; /* assume each element different in size (not true in rectangular patches) */ dsc[i].ptr_array_len = 1; } if ((rc = ARMCI_PutV(dsc, mrc, proc))) { ARMCI_Error("putv failed ", rc); } /* printf("array at destination\n");*/ /* print_2D_double((double *)b[proc], dimsB[0], loB, hiB);*/ /* generate a data descriptor for the upper-triangular patch */ /* there is one less element since diagonal is excluded */ for (i = 1; i < cols; i++) { int ij[2]; ij[0] = loA[0]; ij[1] = loA[1] + i; psrc[i-1] = (double *)a + Index(ndim, ij, dimsA); ij[0] = loB[0]; ij[1] = loB[1] + i; pdst[i-1] = (double *)b[proc] + Index(ndim, ij, dimsB); mrc = ARMCI_MIN(i, rows); dsc[i-1].bytes = mrc * sizeof(double); dsc[i-1].src_ptr_array = &psrc[i-1]; dsc[i-1].dst_ptr_array = &pdst[i-1]; /* assume each element different in size (not true in rectangular patches) */ dsc[i-1].ptr_array_len = 1; } if ((cols - 1))if ((rc = ARMCI_PutV(dsc, cols - 1, proc))) { ARMCI_Error("putv(2) failed ", rc); } /* we get back entire rectangular patch */ for (i = 0; i < cols; i++) { int ij[2]; ij[0] = loB[0]; ij[1] = loB[1] + i; psrc[i] = (double *)b[proc] + Index(ndim, ij, dimsB); ij[0] = loC[0]; ij[1] = loC[1] + i; pdst[i] = (double *)c + Index(ndim, ij, dimsA); } dsc[0].bytes = rows * sizeof(double); dsc[0].src_ptr_array = psrc; dsc[0].dst_ptr_array = pdst; dsc[0].ptr_array_len = cols; /* note that we do not need ARMCI_Fence here since * consecutive operations targeting the same process are ordered */ if ((rc = ARMCI_GetV(dsc, 1, proc))) { ARMCI_Error("getv failed ", rc); } idx1 = Index(ndim, loA, dimsA); idx3 = Index(ndim, loC, dimsA); compare_patches(0., ndim, (double *)a + idx1, loA, hiA, dimsA, (double *)c + idx3, loC, hiC, dimsA); } free(c); destroy_array(b); free(a); } /*\ Atomic Accumulate test for vector API: remote += alpha*local * Every process/or has its patch of array b updated TIMES*NPROC times. * The sequence of updates is random: everybody uses a randomly permuted list * and accumulate is non-collective (of-course) \*/ void test_vector_acc() { int dim, elems, bytes; int i, j, proc, rc, one = 1; void *b[MAXPROC]; void *psrc[ELEMS/2], *pdst[ELEMS/2]; void *a, *c; double alpha = 0.1, scale; int *proclist = work; armci_giov_t dsc; elems = ELEMS; dim = 1; bytes = sizeof(double) * elems; /* create shared and local arrays */ create_array(b, sizeof(double), dim, &elems); a = malloc(bytes); assert(a); c = malloc(bytes); assert(c); init(a, dim, elems, &elems); if (me == 0) { printf("--------array[%d", elems); printf("]--------\n"); fflush(stdout); } GetPermutedProcList(proclist); /* initialize all elements of array b to zero */ for (i = 0; i < elems; i++) { ((double *)b[me])[i] = 0.; } sleep(1); dsc.bytes = sizeof(double); dsc.src_ptr_array = psrc; dsc.dst_ptr_array = pdst; dsc.ptr_array_len = elems / 2; ARMCI_Barrier(); for (i = 0; i < TIMES * nproc; i++) { /* proc=proclist[i%nproc];*/ proc = 0; /* accumulate even numbered elements */ for (j = 0; j < elems / 2; j++) { psrc[j] = 2 * j + (double *)a; pdst[j] = 2 * j + (double *)b[proc]; } if ((rc = ARMCI_AccV(ARMCI_ACC_DBL, &alpha, &dsc, 1, proc))) { ARMCI_Error("accumlate failed", rc); } /* for(j=0; j 0.1) { ARMCI_Error("Float register-originated put failed", 0); } if (ARMCI_ABS(fdst_get[i] - 100.01 *(i + 1)) > 0.1) { ARMCI_Error("Float register-originated get failed", 0); } } if (me == 0) { printf("OK\ndouble data type: "); } for (i = 0; i < elems; i++) { if (ARMCI_ABS(ddst[me][i] - 10.001 *(i + 1)) > 0.1) { ARMCI_Error("Double register-originated put failed", 0); } if (ARMCI_ABS(ddst_get[i] - 100.001 *(i + 1)) > 0.1) { ARMCI_Error("Double register-originated get failed", 0); } } if (me == 0) { printf("OK\n"); fflush(stdout); } ARMCI_AllFence(); ARMCI_Barrier(); destroy_array((void **)idst); destroy_array((void **)ldst); destroy_array((void **)fdst); destroy_array((void **)ddst); destroy_array((void **)isrc_get); destroy_array((void **)lsrc_get); destroy_array((void **)fsrc_get); destroy_array((void **)dsrc_get); } #define MAXELEMS 6400 #define NUMAGG 20 /* NUMAGG < MAXELEMS/10 */ #define MAX_REQUESTS 325 /* MAXELEMS/NUMAGG */ #define COUNT 50 void test_aggregate() { int i, j, k, rc, bytes, elems[2] = {MAXPROC, MAXELEMS}; double *ddst_put[MAXPROC]; double *ddst_get[MAXPROC]; double *dsrc[MAXPROC]; armci_hdl_t usr_hdl_put[MAXPROC]; armci_hdl_t usr_hdl_get[MAXPROC]; armci_giov_t darr; void *src_ptr[MAX_REQUESTS], *dst_ptr[MAX_REQUESTS]; int start = 0, end = 0; create_array((void **)ddst_put, sizeof(double), 2, elems); create_array((void **)ddst_get, sizeof(double), 2, elems); create_array((void **)dsrc, sizeof(double), 1, &elems[1]); for (i = 0; i < elems[1]; i++) { dsrc[me][i] = i * 1.001 * (me + 1); } for (i = 0; i < elems[0]*elems[1]; i++) { ddst_put[me][i] = 0.0; ddst_get[me][i] = 0.0; } ARMCI_Barrier(); for (i = 0; i < nproc; i++) { ARMCI_INIT_HANDLE(&usr_hdl_put[i]); } for (i = 0; i < nproc; i++) { ARMCI_INIT_HANDLE(&usr_hdl_get[i]); } for (i = 0; i < nproc; i++) { ARMCI_SET_AGGREGATE_HANDLE(&usr_hdl_put[i]); } for (i = 0; i < nproc; i++) { ARMCI_SET_AGGREGATE_HANDLE(&usr_hdl_get[i]); } /* Testing aggregate put */ for (i = 0; i < nproc; i++) { start = 0; end = COUNT * NUMAGG; for (j = start; j < end; j++) { bytes = sizeof(double); ARMCI_NbPutValueDouble(dsrc[me][j], &ddst_put[i][me*elems[1] + j], i, &usr_hdl_put[i]); } start = end; end = start + COUNT * NUMAGG; for (j = start, k = 0; j < end; j += NUMAGG, k++) { src_ptr[k] = (void *)&dsrc[me][j]; dst_ptr[k] = (void *)&ddst_put[i][me*elems[1] + j]; } darr.src_ptr_array = src_ptr; darr.dst_ptr_array = dst_ptr; darr.bytes = NUMAGG * sizeof(double); darr.ptr_array_len = k; if ((rc = ARMCI_NbPutV(&darr, 1, i, &usr_hdl_put[i]))) { ARMCI_Error("armci_nbputv failed\n", rc); } start = end; end = start + COUNT * NUMAGG; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbPutS(&dsrc[me][j], NULL, &ddst_put[i][me*elems[1] + j], NULL, &bytes, 0, i, &usr_hdl_put[i]))) { ARMCI_Error("armci_nbputs failed\n", rc); } } start = end; end = elems[1]; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbPut(&dsrc[me][j], &ddst_put[i][me*elems[1] + j], bytes, i, &usr_hdl_put[i]))) { ARMCI_Error("armci_nbput failed\n", rc); } } } for (i = 0; i < nproc; i++) { ARMCI_Wait(&usr_hdl_put[i]); } /* Testing aggregate get */ for (i = 0; i < nproc; i++) { start = 0; end = COUNT * NUMAGG; for (j = start, k = 0; j < end; j += NUMAGG, k++) { src_ptr[k] = (void *)&dsrc[i][j]; dst_ptr[k] = (void *)&ddst_get[me][i*elems[1] + j]; } darr.src_ptr_array = src_ptr; darr.dst_ptr_array = dst_ptr; darr.bytes = NUMAGG * sizeof(double); darr.ptr_array_len = k; if ((rc = ARMCI_NbGetV(&darr, 1, i, &usr_hdl_get[i]))) { ARMCI_Error("armci_nbgetv failed\n", rc); } start = end; end = start + COUNT * NUMAGG; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbGetS(&dsrc[i][j], NULL, &ddst_get[me][i*elems[1] + j], NULL, &bytes, 0, i, &usr_hdl_get[i]))) { ARMCI_Error("armci_nbputs failed\n", rc); } } start = end; end = elems[1]; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbGet(&dsrc[i][j], &ddst_get[me][i*elems[1] + j], bytes, i, &usr_hdl_get[i]))) { ARMCI_Error("armci_nbget failed\n", rc); } } } for (i = 0; i < nproc; i++) { ARMCI_Wait(&usr_hdl_get[i]); } ARMCI_Barrier(); ARMCI_AllFence(); ARMCI_Barrier(); for (i = 0; i < nproc; i++) { for (j = 0; j < elems[1]; j++) { if (ARMCI_ABS(ddst_put[me][i*elems[1] + j] - j * 1.001 *(i + 1)) > 0.1) { ARMCI_Error("aggregate put failed...1", 0); } } } ARMCI_Barrier(); if (me == 0) { printf(" aggregate put ..O.K.\n"); } fflush(stdout); printf("p[%d] (test_aggregate) Got to 1\n",me); for (i = 0; i < nproc; i++) { for (j = 0; j < elems[1]; j++) { if (ARMCI_ABS(ddst_get[me][i*elems[1] + j] - j * 1.001 *(i + 1)) > 0.1) { ARMCI_Error("aggregate get failed...1", 0); } } } ARMCI_Barrier(); if (me == 0) { printf(" aggregate get ..O.K.\n"); } fflush(stdout); printf("p[%d] (test_aggregate) Got to 2\n",me); ARMCI_AllFence(); ARMCI_Barrier(); printf("p[%d] (test_aggregate) Got to 3\n",me); if (me == 0) { printf("O.K.\n"); fflush(stdout); } destroy_array((void **)ddst_put); destroy_array((void **)ddst_get); destroy_array((void **)dsrc); printf("p[%d] (test_aggregate) Got to 4\n",me); } void test_implicit() { int i, j, k, rc, bytes, elems[2] = {MAXPROC, MAXELEMS}; double *ddst_put[MAXPROC]; double *ddst_get[MAXPROC]; double *dsrc[MAXPROC]; armci_giov_t darr; void *src_ptr[MAX_REQUESTS], *dst_ptr[MAX_REQUESTS]; int start = 0, end = 0; armci_hdl_t usr_hdl[MAXPROC]; create_array((void **)ddst_put, sizeof(double), 2, elems); create_array((void **)ddst_get, sizeof(double), 2, elems); create_array((void **)dsrc, sizeof(double), 1, &elems[1]); for (i = 0; i < elems[1]; i++) { dsrc[me][i] = i * 1.001 * (me + 1); } for (i = 0; i < elems[0]*elems[1]; i++) { ddst_put[me][i] = 0.0; ddst_get[me][i] = 0.0; } ARMCI_Barrier(); for (i = 0; i < nproc; i++) { ARMCI_INIT_HANDLE(&usr_hdl[i]); } for (i = 0; i < nproc; i++) { start = 0; end = COUNT * NUMAGG; for (j = start; j < end; j++) { bytes = sizeof(double); ARMCI_NbPutValueDouble(dsrc[me][j], &ddst_put[i][me*elems[1] + j], i, NULL); } start = end; end = start + COUNT * NUMAGG; for (j = start, k = 0; j < end; j += NUMAGG, k++) { src_ptr[k] = (void *)&dsrc[me][j]; dst_ptr[k] = (void *)&ddst_put[i][me*elems[1] + j]; } darr.src_ptr_array = src_ptr; darr.dst_ptr_array = dst_ptr; darr.bytes = NUMAGG * sizeof(double); darr.ptr_array_len = k; if ((rc = ARMCI_NbPutV(&darr, 1, i, NULL))) { ARMCI_Error("armci_nbputv failed\n", rc); } start = end; end = start + COUNT * NUMAGG; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbPutS(&dsrc[me][j], NULL, &ddst_put[i][me*elems[1] + j], NULL, &bytes, 0, i, NULL))) { ARMCI_Error("armci_nbputs failed\n", rc); } } start = end; end = elems[1]; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbPut(&dsrc[me][j], &ddst_put[i][me*elems[1] + j], bytes, i, NULL))) { ARMCI_Error("armci_nbput failed\n", rc); } } } for (i = 0; i < nproc; i++) { start = 0; end = COUNT * NUMAGG; for (j = start, k = 0; j < end; j += NUMAGG, k++) { src_ptr[k] = (void *)&dsrc[i][j]; dst_ptr[k] = (void *)&ddst_get[me][i*elems[1] + j]; } darr.src_ptr_array = src_ptr; darr.dst_ptr_array = dst_ptr; darr.bytes = NUMAGG * sizeof(double); darr.ptr_array_len = k; if ((rc = ARMCI_NbGetV(&darr, 1, i, NULL))) { ARMCI_Error("armci_nbgetv failed\n", rc); } start = end; end = start + COUNT * NUMAGG; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbGetS(&dsrc[i][j], NULL, &ddst_get[me][i*elems[1] + j], NULL, &bytes, 0, i, NULL))) { ARMCI_Error("armci_nbputs failed\n", rc); } } start = end; end = elems[1]; for (j = start; j < end; j += NUMAGG) { bytes = sizeof(double) * NUMAGG; if ((rc = ARMCI_NbGet(&dsrc[i][j], &ddst_get[me][i*elems[1] + j], bytes, i, NULL))) { ARMCI_Error("armci_nbget failed\n", rc); } } } ARMCI_WaitAll(); ARMCI_Barrier(); ARMCI_AllFence(); ARMCI_Barrier(); for (i = 0; i < nproc; i++) { for (j = 0; j < elems[1]; j++) { if (ARMCI_ABS(ddst_put[me][i*elems[1] + j] - j * 1.001 *(i + 1)) > 0.1) { ARMCI_Error("implicit handle(s) failed...(a)", 0); } } } ARMCI_Barrier(); for (i = 0; i < nproc; i++) { for (j = 0; j < elems[1]; j++) { if (ARMCI_ABS(ddst_get[me][i*elems[1] + j] - j * 1.001 *(i + 1)) > 0.1) { ARMCI_Error("implicit handles(s) failed...(b)", 0); } } } ARMCI_Barrier(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("O.K.\n\n"); fflush(stdout); } destroy_array((void **)ddst_put); destroy_array((void **)ddst_get); destroy_array((void **)dsrc); } int main(int argc, char *argv[]) { int ndim; armci_msg_init(&argc, &argv); ARMCI_Init_args(&argc, &argv); nproc = armci_msg_nproc(); me = armci_msg_me(); /* printf("nproc = %d, me = %d\n", nproc, me);*/ if (nproc > MAXPROC && me == 0) { ARMCI_Error("Test works for up to %d processors\n", MAXPROC); } if (me == 0) { printf("ARMCI test program (%d processes)\n", nproc); fflush(stdout); sleep(1); } /* if(me==1)armci_die("process 1 committing suicide",1); */ if (me == 0) { printf("\nTesting strided gets and puts\n"); printf("(Only std output for process 0 is printed)\n\n"); fflush(stdout); sleep(1); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { test_dim(ndim); } ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting non-blocking gets and puts\n"); fflush(stdout); sleep(1); } test_nbdim(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting non-blocking vector gets and puts\n"); fflush(stdout); sleep(1); } test_vec_small(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting atomic accumulate\n"); fflush(stdout); sleep(1); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { test_acc(ndim); } ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting Vector Interface using triangular patches of a 2-D array\n\n"); fflush(stdout); sleep(1); } test_vector(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting Accumulate with Vector Interface\n\n"); fflush(stdout); sleep(1); } test_vector_acc(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting atomic fetch&add\n"); printf("(Std Output for all processes is printed)\n\n"); fflush(stdout); sleep(1); } ARMCI_Barrier(); test_fetch_add(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting atomic swap\n"); fflush(stdout); } test_swap(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting register-originated put and get\n"); fflush(stdout); sleep(1); } test_rput(); ARMCI_AllFence(); ARMCI_Barrier(); if (me == 0) { printf("\nTesting aggregate put/get requests\n"); fflush(stdout); } /** * Aggregate put/get requests cannot be tested for\ number of procs * greater than 32. (Current implementation of aggregate put/get * can use at the maximum of 32 handles (defined by macro * _MAX_AGG_BUFFERS in aggregate.c). This test case is written in * such a way that each process puts/gets data to all the other * processes, thus the number of aggregate handle used is equal to * the number of processes created. */ #if 0 /* This test is probably incorrectly written */ if (nproc > 32) { if (me == 0) { printf("\n WARNING: Aggregate put/get requests cannot be tested for number of procs greater than 32.\n\n"); fflush(stdout); } } else { test_aggregate(); } ARMCI_AllFence(); ARMCI_Barrier(); #endif if (me == 0) { printf("\nTesting implicit handles\n"); fflush(stdout); } test_implicit(); ARMCI_AllFence(); ARMCI_Barrier(); ARMCI_Barrier(); #if MEMLOCK_TEST test_memlock(); #endif ARMCI_Barrier(); if (me == 0) { printf("All tests passed\n"); fflush(stdout); } sleep(2); #ifdef NEWMALLOC { int i, j; for (i = 0; i < g_idx; i++) for (j = 0; j < nproc; j++) { ARMCI_Memdt(&meminfo[i][j], 0); } for (i = 0; i < g_idx; i++) { ARMCI_Memctl(&meminfo[i][me]); } } #endif ARMCI_Barrier(); ARMCI_Finalize(); armci_msg_finalize(); //MPI_Finalize(); return(0); } ga-5.9.2/cmx/src-common/000077500000000000000000000000001500715745200147705ustar00rootroot00000000000000ga-5.9.2/cmx/src-common/acc.h000066400000000000000000000116321500715745200156720ustar00rootroot00000000000000#ifndef _CMX_COMMON_ACC_H_ #define _CMX_COMMON_ACC_H_ #include "cmx.h" /* needed for complex accumulate */ typedef struct { double real; double imag; } DoubleComplex; typedef struct { float real; float imag; } SingleComplex; #if SIZEOF_INT == BLAS_SIZE #define BLAS_INT int #elif SIZEOF_LONG == BLAS_SIZE #define BLAS_INT long #elif SIZEOF_LONG_LONG == BLAS_SIZE #define BLAS_INT long long #endif #define IADD_SCALE_REG(A,B,C) (A) += (B) * (C) #define IADD_SCALE_CPL(A,B,C) \ (A).real += ((B).real*(C).real) - ((B).imag*(C).imag);\ (A).imag += ((B).real*(C).imag) + ((B).imag*(C).real); #define MUL_REG(A,B,C) (A) = (B) * (C) #define MUL_CPL(A,B,C) \ (A).real = ((B).real*(C).real) - ((B).imag*(C).imag);\ (A).imag = ((B).real*(C).imag) + ((B).imag*(C).real); static inline void _scale( const int op, const int bytes, void * const restrict dst, const void * const restrict src, const void * const restrict scale) { #define SCALE_BLAS(CMX_TYPE, C_TYPE, LETTER) \ if (op == CMX_TYPE) { \ const BLAS_INT ONE = 1; \ const BLAS_INT N = bytes/sizeof(C_TYPE); \ BLAS_##LETTER##COPY(&N, src, &ONE, dst, &ONE); \ BLAS_##LETTER##AXPY(&N, scale, src, &ONE, dst, &ONE); \ } else #define SCALE(WHICH, CMX_TYPE, C_TYPE) \ if (op == CMX_TYPE) { \ int m; \ const int m_lim = bytes/sizeof(C_TYPE); \ C_TYPE * const restrict iterator = (C_TYPE * const restrict )dst; \ const C_TYPE * const restrict value = (const C_TYPE * const restrict)src;\ const C_TYPE calc_scale = *(const C_TYPE * const restrict )scale; \ for (m = 0 ; m < m_lim; ++m) { \ MUL_##WHICH(iterator[m], value[m], calc_scale); \ } \ } else #if 0 //HAVE_BLAS SCALE_BLAS(CMX_ACC_DBL, double, D) SCALE_BLAS(CMX_ACC_FLT, float, S) SCALE(REG, CMX_ACC_INT, int) SCALE(REG, CMX_ACC_LNG, long) SCALE_BLAS(CMX_ACC_DCP, DoubleComplex, Z) SCALE_BLAS(CMX_ACC_CPL, SingleComplex, C) #else SCALE(REG, CMX_ACC_DBL, double) SCALE(REG, CMX_ACC_FLT, float) SCALE(REG, CMX_ACC_INT, int) SCALE(REG, CMX_ACC_LNG, long) SCALE(CPL, CMX_ACC_DCP, DoubleComplex) SCALE(CPL, CMX_ACC_CPL, SingleComplex) #endif { #ifdef CMX_ASSERT CMX_ASSERT(0); #else assert(0); #endif } #undef SCALE_BLAS #undef SCALE } static inline void _acc( const int op, const int bytes, void * const restrict dst, const void * const restrict src, const void * const restrict scale) { #define ACC_BLAS(CMX_TYPE, C_TYPE, LETTER) \ if (op == CMX_TYPE) { \ const BLAS_INT ONE = 1; \ const BLAS_INT N = bytes/sizeof(C_TYPE); \ BLAS_##LETTER##AXPY(&N, scale, src, &ONE, dst, &ONE); \ } else #define ACC(WHICH, CMX_TYPE, C_TYPE) \ if (op == CMX_TYPE) { \ int m; \ const int m_lim = bytes/sizeof(C_TYPE); \ C_TYPE * const restrict iterator = (C_TYPE * const restrict)dst; \ const C_TYPE * const restrict value = (const C_TYPE * const restrict)src;\ const C_TYPE calc_scale = *(const C_TYPE * const restrict)scale; \ for (m = 0 ; m < m_lim; ++m) { \ IADD_SCALE_##WHICH(iterator[m], value[m], calc_scale); \ } \ } else #if HAVE_BLAS ACC_BLAS(CMX_ACC_DBL, double, D) ACC_BLAS(CMX_ACC_FLT, float, S) ACC(REG, CMX_ACC_INT, int) ACC(REG, CMX_ACC_LNG, long) ACC_BLAS(CMX_ACC_DCP, DoubleComplex, Z) ACC_BLAS(CMX_ACC_CPL, SingleComplex, C) #else ACC(REG, CMX_ACC_DBL, double) ACC(REG, CMX_ACC_FLT, float) ACC(REG, CMX_ACC_INT, int) ACC(REG, CMX_ACC_LNG, long) ACC(CPL, CMX_ACC_DCP, DoubleComplex) ACC(CPL, CMX_ACC_CPL, SingleComplex) #endif { #ifdef CMX_ASSERT CMX_ASSERT(0); #else assert(0); #endif } #undef ACC_BLAS #undef ACC } #undef IADD_SCALE_REG #undef IADD_SCALE_CPL #undef MUL_REG #undef MUL_CPL #undef BLAS_INT #endif /* _CMX_COMMON_ACC_H_ */ ga-5.9.2/cmx/src-common/cmx.h000066400000000000000000000555071500715745200157440ustar00rootroot00000000000000/* cmx header file */ #ifndef _CMX_H #define _CMX_H #include #include #include "cmx_impl.h" #if defined(__cplusplus) || defined(c_plusplus) extern "c" { #endif typedef _cmx_handle cmx_handle_t; typedef struct { void **loc; /**< array of local starting addresses */ cmxInt *rem; /**< array of remote offsets */ cmxInt count; /**< size of address arrays (src[count],dst[count]) */ cmxInt bytes; /**< length in bytes for each src[i]/dst[i] pair */ } cmx_giov_t; typedef _cmx_request cmx_request_t; #define CMX_SUCCESS 0 #define CMX_FAILURE 1 #define CMX_SWAP 10 #define CMX_SWAP_LONG 11 #define CMX_FETCH_AND_ADD 12 #define CMX_FETCH_AND_ADD_LONG 13 #define CMX_ACC_OFF 36 #define CMX_ACC_INT (CMX_ACC_OFF + 1) #define CMX_ACC_DBL (CMX_ACC_OFF + 2) #define CMX_ACC_FLT (CMX_ACC_OFF + 3) #define CMX_ACC_CPL (CMX_ACC_OFF + 4) #define CMX_ACC_DCP (CMX_ACC_OFF + 5) #define CMX_ACC_LNG (CMX_ACC_OFF + 6) #define CMX_MAX_STRIDE_LEVEL 8 #define CMX_NOT_SET 0 #define CMX_INT 1 #define CMX_LONG 2 #define CMX_FLOAT 3 #define CMX_DOUBLE 4 #define CMX_COMPLEX 5 #define CMX_DCMPLX 6 #define CMX_USER 7 /** * Initialize cmx. * * @return CMX_SUCCESS on success */ extern int cmx_init(); /** * Initialize cmx with command line arguments. * * @return CMX_SUCCESS on success */ extern int cmx_init_args(int *argc, char ***argv); /** * Test whether cmx has been initialized. * * @return CMX_SUCCESS if cmx has been initialized * CMX_FAILURE if cmx has not */ extern int cmx_initialized(); /** * Terminate cmx and clean up resources. * * @return CMX_SUCCESS on success */ extern int cmx_finalize(); /** * Abort cmx, printing the msg, and exiting with code. * * @param[in] msg the message to print * @param[in] code the code to exit with */ extern void cmx_error(const char *msg, int code); /** * Create a new group from the given group and process ID list. * * The rank list selects the ranks from the given group to become members of * the new group. The ranks should be nonnegative and range from zero to the * size of the given group. * * This functions is collective only over the ranks within the rank list and * not over the entire original group. * * @param[in] n the number of ranks to select for the new group * @param[in] rank_list the list of ranks to select for the new group * @param[in] group the group to subset for the new group * @param[out] new_group the newly created group * @return CMX_SUCCESS on success * CMX_FAILURE if a rank in the rank list is out of bounds */ extern int cmx_group_create( int n, int *pid_list, cmx_group_t group, cmx_group_t *new_group); /** * Marks the group for deallocation. * * @param[in] group group to be destroyed * @return CMX_SUCCESS on success */ extern int cmx_group_free(cmx_group_t group); /** * Determines the rank of the calling process in the given group. * * @param[in] group group handle * @param[out] rank rank of the calling process in the group * @return CMX_SUCCESS on success */ extern int cmx_group_rank(cmx_group_t group, int *rank); /** * Determines the size of the given group. * * @param[in] group group handle * @param[out] size number of processes in the group * @return CMX_SUCCESS on success */ extern int cmx_group_size(cmx_group_t group, int *size); /** * Returns the MPI_Comm object backing the given group. * * The actual MPI_Comm object is returned, therefore do not call * MPI_Comm_free() on the returned communicator. This function is for * convenience to be able to MPI_Comm_dup() the returned MPI_Comm instance. * * @param[in] group group handle * @param[out] comm the communicator handle * @return CMX_SUCCESS on success */ extern int cmx_group_comm(cmx_group_t group, MPI_Comm *comm); /** * Translates the ranks of processes in one group to those in another group. * * @param[in] n the number of ranks in the ranks_from and ranks_to arrays * @param[in] group_from the group to translate ranks from * @param[in] ranks_from array of zer or more valid ranks in group_from * @param[in] group_to the group to translate ranks to * @param[out] ranks_to array of corresponding ranks in group_to * @return CMX_SUCCESS on success */ extern int cmx_group_translate_ranks(int n, cmx_group_t group_from, int *ranks_from, cmx_group_t group_to, int *ranks_to); /** * Translate the given rank from its group to its corresponding rank in the * world group. * * Shorthand notation for common case. * * @param[in] group the group to translate from * @param[in] group_rank the rank to translate from * @param[out] world_rank the corresponding world rank * @return CMX_SUCCESS on success */ extern int cmx_group_translate_world( cmx_group_t group, int group_rank, int *world_rank); /** * Extact group object from CMX allocation handle * * @param[in] handle CMX handle for data allocation * @param[out] group CMX group associated with CMX data allocation * @return CMX_SUCCESS on success */ extern int cmx_get_group_from_handle(cmx_handle_t handle, cmx_group_t *group); /** * A collective communication and operations barrier. * * Ensures all cmx communication has completed prior to performing the * operations barrier. * * @param[in] group the group to perform the collective barrier over * @return CMX_SUCCESS on success */ extern int cmx_barrier(cmx_group_t group); /** * Contiguous Put. * * @param[in] src pointer to 1st segment at source * @param[in] dst_offset offset from start of data allocation on remote * process * @param[in] bytes number of bytes to transfer * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @return CMX_SUCCESS on success */ extern int cmx_put( void *src, cmxInt dst_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl); /** * Strided Put. * * @param[in] src pointer to 1st segment at source * @param[in] src_stride array of strides at source * @param[in] dst_offset offset from start of data allocation on remote * process * @param[in] dst_stride array of strides at destination * @param[in] count number of units at each stride level count[0]=bytes * @param[in] stride_levels number of stride levels * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @return CMX_SUCCESS on success */ extern int cmx_puts( void *src, cmxInt *src_stride, cmxInt dst_offset, cmxInt *dst_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl); /** * Vector Put. * * @param[in] darr descriptor array * @param[in] len length of descriptor array * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @return CMX_SUCCESS on success */ extern int cmx_putv( cmx_giov_t *darr, cmxInt len, int proc, cmx_handle_t cmx_hdl); /** * Nonblocking Contiguous Put. * * @param[in] src pointer to 1st segment at source * @param[in] dst_offset offset from start of data allocation on remote * process * @param[in] bytes number of bytes to transfer * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @param[out] req nonblocking request object * @return CMX_SUCCESS on success */ extern int cmx_nbput( void *src, cmxInt dst_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl, cmx_request_t* req); /** * Nonblocking Strided Put. * * @param[in] src pointer to 1st segment at source * @param[in] src_stride array of strides at source * @param[in] dst_offset offset from start of data allocation on remote * process * @param[in] dst_stride array of strides at destination * @param[in] count number of units at each stride level count[0]=bytes * @param[in] stride_levels number of stride levels * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @param[out] req nonblocking request object * @return CMX_SUCCESS on success */ extern int cmx_nbputs( void *src, cmxInt *src_stride, cmxInt dst_offset, cmxInt *dst_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl, cmx_request_t* req); /** * Nonblocking Vector Put. * * @param[in] darr descriptor array * @param[in] len length of descriptor array * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @param[out] req nonblocking request object * @return CMX_SUCCESS on success */ extern int cmx_nbputv( cmx_giov_t *darr, cmxInt len, int proc, cmx_handle_t cmx_hdl, cmx_request_t* req); /** * Contiguous Atomic Accumulate. * * @param[in] op operation * @param[in] scale factor x += scale*y * @param[in] src pointer to 1st segment at source * @param[in] dst_offset offset from start of data allocation on remote * process * @param[in] bytes number of bytes to transfer * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @return CMX_SUCCESS on success */ extern int cmx_acc( int op, void *scale, void *src, cmxInt dst_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl); /** * Strided Atomic Accumulate. * * @param[in] op operation * @param[in] scale factor x += scale*y * @param[in] src pointer to 1st segment at source * @param[in] src_stride [stride_levels] array of strides at source * @param[in] dst_offset offset from start of data allocation on remote * process * @param[in] dst_stride [stride_levels] array of strides at destination * @param[in] count [stride_levels+1] number of units at each stride level * count[0]=bytes * @param[in] stride_levels number of stride levels * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @return CMX_SUCCESS on success */ extern int cmx_accs( int op, void *scale, void *src, cmxInt *src_stride, cmxInt dst_offset, cmxInt *dst_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl); /** * Vector Atomic Accumulate. * * @param[in] op operation * @param[in] scale factor x += scale*y * @param[in] darr descriptor array * @param[in] len length of descriptor array * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @return CMX_SUCCESS on success */ extern int cmx_accv( int op, void *scale, cmx_giov_t *darr, int len, int proc, cmx_handle_t cmx_hdl); /** * Nonblocking Contiguous Atomic Accumulate. * * @param[in] op operation * @param[in] scale factor x += scale*y * @param[in] src pointer to 1st segment at source * @param[in] dst_offset offset from start of data allocation on remote * process * @param[in] bytes number of bytes to transfer * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @param[out] req nonblocking request object * @return CMX_SUCCESS on success */ extern int cmx_nbacc( int op, void *scale, void *src, cmxInt dst_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl, cmx_request_t *req); /** * Strided Atomic Accumulate. * * @param[in] op operation * @param[in] scale factor x += scale*y * @param[in] src pointer to 1st segment at source * @param[in] src_stride array of strides at source * @param[in] dst_offset offset from start of data allocation on remote * process * @param[in] dst_stride array of strides at destination * @param[in] count number of units at each stride level count[0]=bytes * @param[in] stride_levels number of stride levels * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @param[out] req nonblocking request object * @return CMX_SUCCESS on success */ extern int cmx_nbaccs( int op, void *scale, void *src, cmxInt *src_stride, cmxInt dst_offset, cmxInt *dst_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl, cmx_request_t *req); /** * Vector Atomic Accumulate. * * @param[in] op operation * @param[in] scale factor x += scale*y * @param[in] darr descriptor array * @param[in] len length of descriptor array * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @param[out] req nonblocking request object * @return CMX_SUCCESS on success */ extern int cmx_nbaccv( int op, void *scale, cmx_giov_t *darr, cmxInt len, int proc, cmx_handle_t cmx_hdl, cmx_request_t *req); /** * Contiguous Get. * * @param[in] dst pointer to 1st segment at destination * @param[in] src_offset offset from start of data allocation on remote * process * @param[in] bytes number of bytes to transfer * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @return CMX_SUCCESS on success */ extern int cmx_get( void *dst, cmxInt src_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl); /** * Strided Get. * * @param[in] dst pointer to 1st segment at destination * @param[in] dst_stride array of strides at destination * @param[in] src_offset offset from start of data allocation on remote * process * @param[in] src_stride array of strides at source * @param[in] count number of units at each stride level count[0]=bytes * @param[in] stride_levels number of stride levels * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @return CMX_SUCCESS on success */ extern int cmx_gets( void *dst, cmxInt *dst_stride, cmxInt src_offset, cmxInt *src_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl); /** * Vector Get. * * @param[in] darr descriptor array * @param[in] len length of descriptor array * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @return CMX_SUCCESS on success */ extern int cmx_getv( cmx_giov_t *darr, int len, int proc, cmx_handle_t cmx_hdl); /** * Nonblocking Contiguous Get. * * @param[in] dst pointer to 1st segment at destination * @param[in] src_offset offset from start of data allocation on remote * process * @param[in] bytes number of bytes to transfer * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @param[out] req nonblocking request object * @return CMX_SUCCESS on success */ extern int cmx_nbget( void *dst, cmxInt src_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl, cmx_request_t *req); /** * Nonblocking Strided Get. * * @param[in] dst pointer to 1st segment at destination * @param[in] dst_stride array of strides at destination * @param[in] src_offset offset from start of data allocation on remote * process * @param[in] src_stride array of strides at source * @param[in] count number of units at each stride level count[0]=bytes * @param[in] stride_levels number of stride levels * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @param[out] req nonblocking request object * @return CMX_SUCCESS on success */ extern int cmx_nbgets( void *dst, cmxInt *dst_stride, cmxInt src_offset, cmxInt *src_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl, cmx_request_t *req); /** * Nonblocking Vector Get. * * @param[in] darr descriptor array * @param[in] len length of descriptor array * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation. The calling process * and remote process must belong to the same group as the * allocation * @param[out] req nonblocking request object * @return CMX_SUCCESS on success */ extern int cmx_nbgetv( cmx_giov_t *darr, cmxInt len, int proc, cmx_handle_t cmx_hdl, cmx_request_t *req); /** * Collective allocation of registered memory and exchange of addresses. * * @param[out] cmx_hdl handle describing data allocation that should be * used in all operations using this allocation * @param[in] bytes how many bytes to allocate locally * @param[in] group the group to which the calling process belongs * @return CMX_SUCCESS on success */ extern int cmx_malloc( cmx_handle_t *cmx_hdl, cmxInt bytes, cmx_group_t group); /** * Access local buffer from CMX handle * @param[in] handle CMX handle for data allocation * @param buf[out] pointer to local buffer * @return CMX_SUCCESS on success */ extern int cmx_access(cmx_handle_t cmx_hdl, void **buf); /** * Collective free of memory given the original local pointer. * * @param[in] cmx_hdl handle for data allocation * @return CMX_SUCCESS on success */ extern int cmx_free(cmx_handle_t cmx_hdl); /** * Local (noncollective) allocation of registered memory. * * Using memory allocated here may have performance benefits when used as a * communication buffer. * * @param[in] bytes how many bytes to allocate locally * @return CMX_SUCCESS on success */ extern void* cmx_malloc_local(size_t bytes); /** * Local (noncollective) free of memory allocated by cmx_malloc_local. * * @param[in] the original local memory allocated using cmx_malloc_local * @return CMX_SUCCESS on success */ extern int cmx_free_local(void *ptr); /** * Flush all outgoing messages from me to the given proc. * * @param[in] proc the proc with which to flush outgoing messages * @return CMX_SUCCESS on success */ extern int cmx_fence_proc(int proc, cmx_group_t group); /** * Flush all outgoing messages to all procs in group. * * @param[in] group flush operation if performed on all processors in group * @return CMX_SUCCESS on success */ extern int cmx_fence_all(cmx_group_t group); /** * Collectively create num locks locally. * * Remote procs may create a different number of locks, including zero. * * This function is always collective on the world group. * * @param[in] num number of locks to create locally * @return CMX_SUCCESS on success */ extern int cmx_create_mutexes(int num); /** * Collectively destroy all previously created locks. * * This function is always collective on the world group. * * @param[in] num number of locks to create locally * @return CMX_SUCCESS on success */ extern int cmx_destroy_mutexes(); /** * Lock the given mutex on the given proc. * * This function is always on the world group. * * @param[in] mutex the ID of the mutex to lock on proc * @param[in] the ID of the proc which owns the mutex * * @return CMX_SUCCESS on success * CMX_FAILURE if given mutex or proc is out of range */ extern int cmx_lock(int mutex, int proc); /** * Unlock the given mutex on the given proc. * * This function is always on the world group. * * @param[in] mutex the ID of the mutex to unlock on proc * @param[in] the ID of the proc which owns the mutex * * @return CMX_SUCCESS on success * CMX_FAILURE if given mutex or proc is out of range */ extern int cmx_unlock(int mutex, int proc); /** * Read-modify-write atomic operation. * * The operations may be one of * - CMX_SWAP * - CMX_SWAP_LONG * - CMX_FETCH_AND_ADD * - CMX_FETCH_AND_ADD_LONG * * For the swap operations, the extra parameter is not used. The values of the * ploc and prem locations are swapped. * * For the fetch and add operations, the extra parameter is also used to * indicate how much to increment the remote value. The original remove value * is returned in the ploc parameter. * * @param[in] op the operation to perform (see list above) * @param[in] ploc the value to update locally * @param[in] rem_offset offset to remote value * @param[in] extra for CMX_FETCH_AND_ADD and CMX_FETCH_AND_ADD_LONG, the * amount to increment the remote value by * @param[in] proc remote process(or) id * @param[in] cmx_hdl handle for data allocation * @return CMX_SUCCESS on success */ extern int cmx_rmw( int op, void *ploc, cmxInt rem_offset, int extra, int proc, cmx_handle_t cmx_hdl); /** * Waits for completion of non-blocking cmx operations with explicit handles. * * @param[in] req the request handle * @return CMX_SUCCESS on success */ extern int cmx_wait(cmx_request_t *req); /** * Checks completion status of non-blocking cmx operations with explicit * handles. * * @param[in] req the request handle * @param[out] status 0-completed, 1-in progress * @return CMX_SUCCESS on success */ extern int cmx_test(cmx_request_t *req, int *status); /** * Wait for all outstanding implicit non-blocking operations to finish. * * @param[in] group group handle * @return CMX_SUCCESS on success */ extern int cmx_wait_all(cmx_group_t group); /** * Wait for all outstanding implicit non-blocking operations to a particular * process to finish. * * @param[in] proc proc for which all the outstanding non-blocking operations * have to be completed * @param[in] group group handle * @return CMX_SUCCESS on success */ extern int cmx_wait_proc(int proc, cmx_group_t group); #if defined(__cplusplus) || defined(c_plusplus) } #endif #endif /* _CMX_H */ ga-5.9.2/cmx/src-mpi-pr/000077500000000000000000000000001500715745200147045ustar00rootroot00000000000000ga-5.9.2/cmx/src-mpi-pr/Makefile.inc000066400000000000000000000005701500715745200171160ustar00rootroot00000000000000libcmx_la_SOURCES += src-mpi-pr/cmx.c libcmx_la_SOURCES += src-mpi-pr/cmx_impl.h libcmx_la_SOURCES += src-mpi-pr/groups.c libcmx_la_SOURCES += src-mpi-pr/groups.h libcmx_la_SOURCES += src-mpi-pr/reg_cache.c libcmx_la_SOURCES += src-mpi-pr/reg_cache.h AM_CPPFLAGS += -I$(top_srcdir)/src-mpi-pr check_PROGRAMS += src-mpi-pr/hello src_mpi_pr_hello_SOURCES = src-mpi-pr/hello.c ga-5.9.2/cmx/src-mpi-pr/NOTES.md000066400000000000000000000126651500715745200161300ustar00rootroot00000000000000# MPI Progress Ranks (MPI-PR) NOTES on generalization of number of Progress-Ranks per computing node ------ Number of Progress-Ranks per node can be suitably User has the flexibility to choose the number of Progress-Ranks per node during the execution of a program. An environment variable GA_NUM_PROGRESS_RANKS_PER_NODE will capture the number to be set for number of Progress-Ranks per node. If not set in the environment, then this number will set to 1 by default. e.g. of defining this variable in the environment during execution: ``` $ export GA_NUM_PROGRESS_RANKS_PER_NODE=1 $ export GA_NUM_PROGRESS_RANKS_PER_NODE=2 $ export GA_NUM_PROGRESS_RANKS_PER_NODE=4 $ setenv GA_NUM_PROGRESS_RANKS_PER_NODE 2 ``` CAUTION: For optimum performance, number of MPI ranks per node used to execute a program should be in multiples of value set for GA_NUM_PROGRESS_RANKS_PER_NODE. With the use of two optional environmental variables, it is possible to set the GA-Ranks in one group managed by Progress-Rank in either PACKED or CYCLIC distribution pattern. Following example shows an application run with 8 MPI processes/node with 2-PRs. Total 8 MPI ranks will be divided into two groups and by default, the highest rank will be the Progress-Rank of each group. PACKED distribution ``` $ export GA_NUM_PROGRESS_RANKS_PER_NODE=2 $ export GA_PROGRESS_RANKS_DISTRIBUTION_PACKED=1 ``` or ``` $ export GA_NUM_PROGRESS_RANKS_PER_NODE=2 $ export GA_PROGRESS_RANKS_DISTRIBUTION_PACKED=Y ``` Above environmental settings sets 2 Progress-Ranks/node and PACKED distribution The two groups are (0,1,2,3) and (4,5,6,7) Here, MPI ranks 3 and 7 are Progress-Ranks on the node Next, CYCLIC distribution ``` $ export GA_NUM_PROGRESS_RANKS_PER_NODE=2 $ export GA_PROGRESS_RANKS_DISTRIBUTION_PACKED=0 $ export GA_PROGRESS_RANKS_DISTRIBUTION_CYCLIC=1 ``` Please make sure to set GA_PROGRESS_RANKS_DISTRIBUTION_PACKED=0 if previously used. Above environmental settings sets 2 Progress-Ranks/node and CYCLIC distribution The two groups are (0,2,4,6) and (1,3,5,7) Here, MPI ranks 6 and 7 are Progress-Ranks on the node Other notes on MPI Progress-Rank ------ These are notes describing the MPI Progress ranks runtime. These notes are intended to help developers navigate the contents of these files and to locate specific functionality. The MPI-PR is intended to be the highest-performing MPI-1 compatible ARMCI/CMX runtime. It uses only features from the MPI-1 standard and provides asynchronous progress. Posix shared memory is used extensively. Asynchronous progress is made by reserving one MPI rank per compute node, taken from MPI_COMM_WORLD, and using posix shared memory between the reserved "progress rank" and the remaining ranks on the associated compute node. The world communicator is split using `MPI_Comm_split()` and using the result of `gethostid()` to separate the user/worker ranks from the progress ranks. The progress rank is the largest rank on each compute node, unless you set `MASTER_IS_SMALLEST_SMP_RANK` in the [cmx_impl.h](cmx_impl.h) file to 1. When a user's get/put/acc request is made, it is sent as a small MPI message to the progress rank. The progress rank interprets the header and processes the request, copying from/to the shared memory of one of the MPI ranks the progress rank is managing on its compute node. The code that implements the progress engine can be found in the `_progress_server` function located in the [cmx.c](cmx.c) file. Incoming requests to the progress rank are all based on the active message concept. A 'header' message is sent first to the progress engine indicating the type of request, e.g., OP_PUT, OP_ACC_INT. A complete listing of the request types is defined in an enumerated list at the top of [cmx.c](cmx.c). The header contains enough information to complete the request such as source and destination pointers, source and destination ranks, etc. After a header message is sent, any data payload is sent as a separate message. The intent was to let MPI directly use the buffer pointers in case the buffers were allocated using any special, network-specific allocator. Otherwise, a data payload could have been mem-copied to the end of the header message (this is done in the MPI-PR implementation as an optimization). Posix shared memory is used between all ranks on a compute node, including the reserved progress rank. When `cmx_malloc` is called (collectively), it calls `cmx_malloc_local` that creates the shared memory buffer on each user-level MPI rank. The posix shmem names associated with each buffer is collectively exchanged with all ranks on the node so that all ranks on the same node can access each other's memory directly. The progress rank does not allocate memory, but rather attaches to all segments allocated on it's node-local ranks. The shmem name is guaranteed to be unique to the UID and PID and uses an internal counter. There are a finite number of user-level non-blocking handles. This is set using the environment variable CMX_MAX_NB_OUTSTANDING. This controls the size of an allocated array of our non-blocking handle data structure nb_t. The nb_t structure contains linked lists of MPI_Request objects associated with the given user-level handle. It is slightly more complicated than that since get requests might be using the packing optimization where the request is first compressed into a contiguous buffer. The stride information is kept with the nb_t message so that the received buffer can be unpacked. All memory is freed when operations complete. ga-5.9.2/cmx/src-mpi-pr/cmx.c000066400000000000000000006612571500715745200156600ustar00rootroot00000000000000/* C and/or system headers */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 3rd party headers */ #include #if USE_SICM #include //#include sicm_device_list nill; #endif /* our headers */ #include "cmx.h" #include "cmx_impl.h" #include "groups.h" #include "reg_cache.h" #include "acc.h" #define XSTR(x) #x #define STR(x) XSTR(x) #define PAUSE_ON_ERROR 0 #define STATIC static inline #if USE_MEMSET_AFTER_MALLOC #define MAYBE_MEMSET(a,b,c) (void)memset(a,b,c) #else #define MAYBE_MEMSET(a,b,c) ((void)0) #endif #define XSTR(x) #x #define STR(x) XSTR(x) /* data structures */ typedef enum { OP_PUT = 0, OP_PUT_PACKED, OP_PUT_DATATYPE, OP_PUT_IOV, OP_GET, OP_GET_PACKED, OP_GET_DATATYPE, OP_GET_IOV, OP_ACC_INT, OP_ACC_DBL, OP_ACC_FLT, OP_ACC_CPL, OP_ACC_DCP, OP_ACC_LNG, OP_ACC_INT_PACKED, OP_ACC_DBL_PACKED, OP_ACC_FLT_PACKED, OP_ACC_CPL_PACKED, OP_ACC_DCP_PACKED, OP_ACC_LNG_PACKED, OP_ACC_INT_IOV, OP_ACC_DBL_IOV, OP_ACC_FLT_IOV, OP_ACC_CPL_IOV, OP_ACC_DCP_IOV, OP_ACC_LNG_IOV, OP_FENCE, OP_FETCH_AND_ADD, OP_SWAP, OP_CREATE_MUTEXES, OP_DESTROY_MUTEXES, OP_LOCK, OP_UNLOCK, OP_QUIT, OP_MALLOC, OP_FREE, OP_NULL } op_t; typedef struct { op_t operation; void *remote_address; void *local_address; int rank; /**< rank of target (rank of sender is iprobe_status.MPI_SOURCE */ int length; /**< length of message/payload not including header */ } header_t; /* keep track of all mutex requests */ typedef struct lock_link { struct lock_link *next; int rank; } lock_t; #if 0 typedef struct message_link { struct message_link *next; void *message; MPI_Request request; MPI_Datatype datatype; int need_free; stride_t *stride; cmx_giov_t *iov; } message_t; typedef struct { int in_use; int send_size; message_t *send_head; message_t *send_tail; int recv_size; message_t *recv_head; message_t *recv_tail; } nb_t; typedef struct { int rank; void *ptr; } rank_ptr_t; #endif int _cmx_me; /* static state */ static int *num_mutexes = NULL; /**< (all) how many mutexes on each process */ static int **mutexes = NULL; /**< (masters) value is rank of lock holder */ static lock_t ***lq_heads = NULL; /**< array of lock queues */ static char *sem_name = NULL; /* local semaphore name */ static sem_t **semaphores = NULL; /* semaphores for locking within SMP node */ static int initialized = 0; /* for cmx_initialized(), 0=false */ static char *fence_array = NULL; static int nb_max_outstanding = CMX_MAX_NB_OUTSTANDING; static int nb_last_request = 0; static int nb_index = 0; static int nb_count_event = 0; static int nb_count_event_processed = 0; static int nb_count_send = 0; static int nb_count_send_processed = 0; static int nb_count_recv = 0; static int nb_count_recv_processed = 0; static char *static_server_buffer = NULL; static int static_server_buffer_size = 0; static int eager_threshold = -1; static int max_message_size = -1; static int CMX_ENABLE_PUT_SELF = ENABLE_PUT_SELF; static int CMX_ENABLE_GET_SELF = ENABLE_GET_SELF; static int CMX_ENABLE_ACC_SELF = ENABLE_ACC_SELF; static int CMX_ENABLE_PUT_SMP = ENABLE_PUT_SMP; static int CMX_ENABLE_GET_SMP = ENABLE_GET_SMP; static int CMX_ENABLE_ACC_SMP = ENABLE_ACC_SMP; static int CMX_ENABLE_PUT_PACKED = ENABLE_PUT_PACKED; static int CMX_ENABLE_GET_PACKED = ENABLE_GET_PACKED; static int CMX_ENABLE_ACC_PACKED = ENABLE_ACC_PACKED; static int CMX_ENABLE_PUT_DATATYPE = ENABLE_PUT_DATATYPE; static int CMX_ENABLE_GET_DATATYPE = ENABLE_GET_DATATYPE; static int CMX_PUT_DATATYPE_THRESHOLD = 8192; static int CMX_GET_DATATYPE_THRESHOLD = 8192; static int CMX_ENABLE_PUT_IOV = ENABLE_PUT_IOV; static int CMX_ENABLE_GET_IOV = ENABLE_GET_IOV; static int CMX_ENABLE_ACC_IOV = ENABLE_ACC_IOV; #if USE_SICM static sicm_device_list devices = {0}; #if SICM_OLD static sicm_device *device_dram = NULL; static sicm_device *device_knl_hbm = NULL; static sicm_device *device_ppc_hbm = NULL; #else static sicm_device_list device_dram = {0}; static sicm_device_list device_knl_hbm = {0}; static sicm_device_list device_ppc_hbm = {0}; #endif #endif /* Non-blocking handle list */ static _cmx_request **nb_list; #if PAUSE_ON_ERROR static int AR_caught_sig=0; static int AR_caught_sigsegv=0; void (*SigSegvOrig)(int); void SigSegvHandler(int sig) { char name[256]; AR_caught_sig= sig; AR_caught_sigsegv=1; if (-1 == gethostname(name, 256)) { perror("gethostname"); cmx_error("gethostname failed", errno); } fprintf(stderr,"%d(%s:%d): Segmentation Violation ... pausing\n", g_state.rank, name, getpid()); pause(); cmx_error("Segmentation Violation error, status=",(int) sig); } #endif /* static function declarations */ /* error checking */ #define CHECK_MPI_RETVAL(retval) check_mpi_retval((retval), __FILE__, __LINE__) STATIC void check_mpi_retval(int retval, const char *file, int line); STATIC const char *str_mpi_retval(int retval); /* server fuctions */ STATIC void server_send(void *buf, int count, int dest); STATIC void server_send_datatype(void *buf, MPI_Datatype dt, int dest); STATIC void server_recv(void *buf, int count, int source); STATIC void server_recv_datatype(void *buf, MPI_Datatype dt, int source); STATIC void _progress_server(); STATIC void _put_handler(header_t *header, char *payload, int proc); STATIC void _put_packed_handler(header_t *header, char *payload, int proc); STATIC void _put_datatype_handler(header_t *header, char *payload, int proc); STATIC void _put_iov_handler(header_t *header, int proc); STATIC void _get_handler(header_t *header, int proc); STATIC void _get_packed_handler(header_t *header, char *payload, int proc); STATIC void _get_datatype_handler(header_t *header, char *payload, int proc); STATIC void _get_iov_handler(header_t *header, int proc); STATIC void _acc_handler(header_t *header, char *scale, int proc); STATIC void _acc_packed_handler(header_t *header, char *payload, int proc); STATIC void _acc_iov_handler(header_t *header, char *scale, int proc); STATIC void _fence_handler(header_t *header, int proc); STATIC void _fetch_and_add_handler(header_t *header, char *payload, int proc); STATIC void _swap_handler(header_t *header, char *payload, int proc); STATIC void _mutex_create_handler(header_t *header, int proc); STATIC void _mutex_destroy_handler(header_t *header, int proc); STATIC void _lock_handler(header_t *header, int proc); STATIC void _unlock_handler(header_t *header, int proc); STATIC void _malloc_handler(header_t *header, char *payload, int proc); STATIC void _free_handler(header_t *header, char *payload, int proc); /* worker functions */ STATIC void nb_send_common(void *buf, int count, int dest, _cmx_request *nb, int need_free); STATIC void nb_send_datatype(void *buf, MPI_Datatype dt, int dest, _cmx_request *nb); STATIC void nb_send_header(void *buf, int count, int dest, _cmx_request *nb); STATIC void nb_send_buffer(void *buf, int count, int dest, _cmx_request *nb); STATIC void nb_recv_packed(void *buf, int count, int source, _cmx_request *nb, stride_t *stride); STATIC void nb_recv_datatype(void *buf, MPI_Datatype dt, int source, _cmx_request *nb); STATIC void nb_recv_iov(void *buf, int count, int source, _cmx_request *nb, _cmx_giov_t *iov); STATIC void nb_recv(void *buf, int count, int source, _cmx_request *nb); STATIC void nb_wait_for_send1(_cmx_request *nb); STATIC void nb_wait_for_recv1(_cmx_request *nb); STATIC void nb_wait_for_all(_cmx_request *nb); STATIC int nb_test_for_all(_cmx_request *nb); STATIC void nb_register_request(_cmx_request *nb); STATIC void nb_unregister_request(_cmx_request *nb); STATIC void nb_handle_init(_cmx_request *nb); STATIC void nb_put(void *src, void *dst, int bytes, int proc, _cmx_request *nb); STATIC void nb_get(void *src, void *dst, int bytes, int proc, _cmx_request *nb); STATIC void nb_acc(int datatype, void *scale, void *src, void *dst, int bytes, int proc, _cmx_request *nb); STATIC void nb_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, _cmx_request *nb); STATIC void nb_puts_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, _cmx_request *nb); STATIC void nb_puts_datatype( void *src_ptr, int *src_stride_ar, void *dst_ptr, int *dst_stride_ar, int *count, int stride_levels, int proc, _cmx_request *nb); STATIC void nb_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, _cmx_request *nb); STATIC void nb_gets_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, _cmx_request *nb); STATIC void nb_gets_datatype( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, _cmx_request *nb); STATIC void nb_accs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, _cmx_request *nb); STATIC void nb_accs_packed( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, _cmx_request *nb); STATIC void nb_putv(_cmx_giov_t *iov, int iov_len, int proc, _cmx_request *nb); STATIC void nb_putv_packed(_cmx_giov_t *iov, int proc, _cmx_request *nb); STATIC void nb_getv(_cmx_giov_t *iov, int iov_len, int proc, _cmx_request *nb); STATIC void nb_getv_packed(_cmx_giov_t *iov, int proc, _cmx_request *nb); STATIC void nb_accv(int datatype, void *scale, _cmx_giov_t *iov, int iov_len, int proc, _cmx_request *nb); STATIC void nb_accv_packed(int datatype, void *scale, _cmx_giov_t *iov, int proc, _cmx_request *nb); STATIC void _fence_master(int master_rank); STATIC int _eager_check(int extra_bytes); /* other functions */ STATIC int _packed_size(int *src_stride, int *count, int stride_levels); STATIC char* pack(char *src, int *src_stride, int *count, int stride_levels, int *size); STATIC void unpack(char *packed_buffer, char *dst, int *dst_stride, int *count, int stride_levels); STATIC char* _generate_shm_name(int rank); STATIC reg_entry_t* _cmx_malloc_local(size_t size); #if USE_SICM #if SICM_OLD STATIC reg_entry_t* _cmx_malloc_local_memdev(size_t size, sicm_device *device); #else STATIC reg_entry_t* _cmx_malloc_local_memdev(size_t size, sicm_device_list device); #endif int _cmx_free_local_memdev(void *ptr); #endif STATIC void* _get_offset_memory(reg_entry_t *reg_entry, void *memory); STATIC int _is_master(void); STATIC int _get_world_rank(cmx_igroup_t *igroup, int rank); STATIC int* _get_world_ranks(cmx_igroup_t *igroup); STATIC int _smallest_world_rank_with_same_hostid(cmx_igroup_t *group); STATIC int _largest_world_rank_with_same_hostid(cmx_igroup_t *igroup); STATIC void _malloc_semaphore(void); STATIC void _free_semaphore(void); STATIC void* _shm_create(const char *name, size_t size); STATIC void* _shm_attach(const char *name, size_t size); STATIC void* _shm_map(int fd, size_t size); #if USE_SICM #if SICM_OLD STATIC void* _shm_create_memdev(const char *name, size_t size, sicm_device *device); STATIC void* _shm_attach_memdev(const char *name, size_t size, sicm_device *device); #else STATIC void* _shm_create_memdev(const char *name, size_t size, sicm_device_list device); STATIC void* _shm_attach_memdev(const char *name, size_t size, sicm_device_list device); #endif STATIC void* _shm_map_arena(int fd, size_t size, sicm_arena arena); #endif STATIC int _set_affinity(int cpu); STATIC void _translate_mpi_error(int ierr, const char* location); STATIC void strided_to_subarray_dtype(int *stride_array, int *count, int levels, MPI_Datatype base_type, MPI_Datatype *type); int cmx_init() { int status = 0; int init_flag = 0; int i = 0; if (initialized) { return 0; } initialized = 1; /* Assert MPI has been initialized */ status = MPI_Initialized(&init_flag); _translate_mpi_error(status,"cmx_init"); CHECK_MPI_RETVAL(status); assert(init_flag); /*MPI_Errhandler_set(MPI_COMM_WORLD, MPI_ERRORS_RETURN);*/ /* groups */ cmx_group_init(); /* env vars */ { char *value = NULL; nb_max_outstanding = CMX_MAX_NB_OUTSTANDING/2; /* default */ value = getenv("CMX_MAX_NB_OUTSTANDING"); if (NULL != value) { nb_max_outstanding = atoi(value); } CMX_ASSERT(nb_max_outstanding > 0); static_server_buffer_size = CMX_STATIC_BUFFER_SIZE; /* default */ value = getenv("CMX_STATIC_BUFFER_SIZE"); if (NULL != value) { static_server_buffer_size = atoi(value); } CMX_ASSERT(static_server_buffer_size > 0); eager_threshold = -1; /* default */ value = getenv("CMX_EAGER_THRESHOLD"); if (NULL != value) { eager_threshold = atoi(value); } CMX_ENABLE_PUT_SELF = ENABLE_PUT_SELF; /* default */ value = getenv("CMX_ENABLE_PUT_SELF"); if (NULL != value) { CMX_ENABLE_PUT_SELF = atoi(value); } CMX_ENABLE_GET_SELF = ENABLE_GET_SELF; /* default */ value = getenv("CMX_ENABLE_GET_SELF"); if (NULL != value) { CMX_ENABLE_GET_SELF = atoi(value); } CMX_ENABLE_ACC_SELF = ENABLE_ACC_SELF; /* default */ value = getenv("CMX_ENABLE_ACC_SELF"); if (NULL != value) { CMX_ENABLE_ACC_SELF = atoi(value); } CMX_ENABLE_PUT_SMP = ENABLE_PUT_SMP; /* default */ value = getenv("CMX_ENABLE_PUT_SMP"); if (NULL != value) { CMX_ENABLE_PUT_SMP = atoi(value); } CMX_ENABLE_GET_SMP = ENABLE_GET_SMP; /* default */ value = getenv("CMX_ENABLE_GET_SMP"); if (NULL != value) { CMX_ENABLE_GET_SMP = atoi(value); } CMX_ENABLE_ACC_SMP = ENABLE_ACC_SMP; /* default */ value = getenv("CMX_ENABLE_ACC_SMP"); if (NULL != value) { CMX_ENABLE_ACC_SMP = atoi(value); } CMX_ENABLE_PUT_PACKED = ENABLE_PUT_PACKED; /* default */ value = getenv("CMX_ENABLE_PUT_PACKED"); if (NULL != value) { CMX_ENABLE_PUT_PACKED = atoi(value); } CMX_ENABLE_GET_PACKED = ENABLE_GET_PACKED; /* default */ value = getenv("CMX_ENABLE_GET_PACKED"); if (NULL != value) { CMX_ENABLE_GET_PACKED = atoi(value); } CMX_ENABLE_ACC_PACKED = ENABLE_ACC_PACKED; /* default */ value = getenv("CMX_ENABLE_ACC_PACKED"); if (NULL != value) { CMX_ENABLE_ACC_PACKED = atoi(value); } CMX_ENABLE_PUT_DATATYPE = ENABLE_PUT_DATATYPE; /* default */ value = getenv("CMX_ENABLE_PUT_DATATYPE"); if (NULL != value) { CMX_ENABLE_PUT_DATATYPE = atoi(value); } CMX_ENABLE_GET_DATATYPE = ENABLE_GET_DATATYPE; /* default */ value = getenv("CMX_ENABLE_GET_DATATYPE"); if (NULL != value) { CMX_ENABLE_GET_DATATYPE = atoi(value); } CMX_PUT_DATATYPE_THRESHOLD = 8192; /* default */ value = getenv("CMX_PUT_DATATYPE_THRESHOLD"); if (NULL != value) { CMX_PUT_DATATYPE_THRESHOLD = atoi(value); } CMX_GET_DATATYPE_THRESHOLD = 8192; /* default */ value = getenv("CMX_GET_DATATYPE_THRESHOLD"); if (NULL != value) { CMX_GET_DATATYPE_THRESHOLD = atoi(value); } CMX_ENABLE_PUT_IOV = ENABLE_PUT_IOV; /* default */ value = getenv("CMX_ENABLE_PUT_IOV"); if (NULL != value) { CMX_ENABLE_PUT_IOV = atoi(value); } CMX_ENABLE_GET_IOV = ENABLE_GET_IOV; /* default */ value = getenv("CMX_ENABLE_GET_IOV"); if (NULL != value) { CMX_ENABLE_GET_IOV = atoi(value); } CMX_ENABLE_ACC_IOV = ENABLE_ACC_IOV; /* default */ value = getenv("CMX_ENABLE_ACC_IOV"); if (NULL != value) { CMX_ENABLE_ACC_IOV = atoi(value); } max_message_size = INT_MAX; /* default */ value = getenv("CMX_MAX_MESSAGE_SIZE"); if (NULL != value) { max_message_size = atoi(value); } #if DEBUG if (0 == g_state.rank) { printf("CMX_MAX_NB_OUTSTANDING=%d\n", nb_max_outstanding); printf("CMX_STATIC_BUFFER_SIZE=%d\n", static_server_buffer_size); printf("CMX_MAX_MESSAGE_SIZE=%d\n", max_message_size); printf("CMX_EAGER_THRESHOLD=%d\n", eager_threshold); printf("CMX_PUT_DATATYPE_THRESHOLD=%d\n", CMX_PUT_DATATYPE_THRESHOLD); printf("CMX_GET_DATATYPE_THRESHOLD=%d\n", CMX_GET_DATATYPE_THRESHOLD); printf("CMX_ENABLE_PUT_SELF=%d\n", CMX_ENABLE_PUT_SELF); printf("CMX_ENABLE_GET_SELF=%d\n", CMX_ENABLE_GET_SELF); printf("CMX_ENABLE_ACC_SELF=%d\n", CMX_ENABLE_ACC_SELF); printf("CMX_ENABLE_PUT_SMP=%d\n", CMX_ENABLE_PUT_SMP); printf("CMX_ENABLE_GET_SMP=%d\n", CMX_ENABLE_GET_SMP); printf("CMX_ENABLE_ACC_SMP=%d\n", CMX_ENABLE_ACC_SMP); printf("CMX_ENABLE_PUT_PACKED=%d\n", CMX_ENABLE_PUT_PACKED); printf("CMX_ENABLE_GET_PACKED=%d\n", CMX_ENABLE_GET_PACKED); printf("CMX_ENABLE_ACC_PACKED=%d\n", CMX_ENABLE_ACC_PACKED); printf("CMX_ENABLE_PUT_DATATYPE=%d\n", CMX_ENABLE_PUT_DATATYPE); printf("CMX_ENABLE_GET_DATATYPE=%d\n", CMX_ENABLE_GET_DATATYPE); printf("CMX_ENABLE_PUT_IOV=%d\n", CMX_ENABLE_PUT_IOV); printf("CMX_ENABLE_GET_IOV=%d\n", CMX_ENABLE_GET_IOV); printf("CMX_ENABLE_ACC_IOV=%d\n", CMX_ENABLE_ACC_IOV); fflush(stdout); } #endif } /* mutexes */ mutexes = NULL; num_mutexes = NULL; lq_heads = NULL; #if USE_SICM devices = sicm_init(); #if SICM_OLD for(i = 0; i < devices.count; i++) { if (devices.devices[i].tag == SICM_DRAM) { device_dram = &devices.devices[i]; } if (devices.devices[i].tag == SICM_KNL_HBM) { device_knl_hbm = &devices.devices[i]; } if (devices.devices[i].tag == SICM_POWERPC_HBM) { device_ppc_hbm = &devices.devices[i]; } } if (!device_dram) { printf("Device DRAM not found\n"); exit(18); } #else for(i =0; i < devices.count; ++i){ sicm_device *curr = devices.devices[i]; if(curr->tag == SICM_DRAM){ device_dram.count = 1; device_dram.devices = &devices.devices[i]; } if (curr->tag == SICM_KNL_HBM) { device_knl_hbm.count = 1; device_knl_hbm.devices = &devices.devices[i]; } if (curr->tag == SICM_POWERPC_HBM) { device_ppc_hbm.count = 1; device_ppc_hbm.devices = &devices.devices[i]; } } if(device_dram.devices == NULL){ printf("Device DRAM not found\n"); exit(18); } #endif #endif /* reg_cache */ /* note: every process needs a reg cache and it's always based on the * world rank and size */ reg_cache_init(g_state.size); _malloc_semaphore(); #if DEBUG fprintf(stderr, "[%d] cmx_init() before progress server\n", g_state.rank); #endif #if PAUSE_ON_ERROR if ((SigSegvOrig=signal(SIGSEGV, SigSegvHandler)) == SIG_ERR) { cmx_error("signal(SIGSEGV, ...) error", -1); } #endif status = _set_affinity(g_state.node_rank); CMX_ASSERT(0 == status); if (_is_master()) { /* TODO: wasteful O(p) storage... */ mutexes = (int**)malloc(sizeof(int*) * g_state.size); CMX_ASSERT(mutexes); /* create one lock queue for each proc for each mutex */ lq_heads = (lock_t***)malloc(sizeof(lock_t**) * g_state.size); CMX_ASSERT(lq_heads); /* start the server */ _progress_server(); } nb_list = (_cmx_request**)malloc(sizeof(_cmx_request*)*nb_max_outstanding); for (i=0; icomm); _translate_mpi_error(status, "cmx_init:MPI_Barrier"); /* static state */ fence_array = malloc(sizeof(char) * g_state.size); CMX_ASSERT(fence_array); for (i = 0; i < g_state.size; ++i) { fence_array[i] = 0; } #if DEBUG fprintf(stderr, "[%d] cmx_init() before barrier\n", g_state.rank); #endif /* Synch - Sanity Check */ /* This barrier is on the world worker group */ status = MPI_Barrier(CMX_GROUP_WORLD->comm); _translate_mpi_error(status, "cmx_init:MPI_Barrier"); #if DEBUG fprintf(stderr, "[%d] cmx_init() success\n", g_state.rank); #endif return CMX_SUCCESS; } int cmx_init_args(int *argc, char ***argv) { int init_flag; int status; status = MPI_Initialized(&init_flag); CHECK_MPI_RETVAL(status); if(!init_flag) { status = MPI_Init(argc, argv); _translate_mpi_error(status, "cmx_init_args:MPI_Init"); CHECK_MPI_RETVAL(status); } return cmx_init(); } int cmx_initialized() { #if DEBUG if (initialized) { fprintf(stderr, "[%d] cmx_initialized()\n", g_state.rank); } #endif return initialized; } int cmx_finalize() { int i, ierr; #if DEBUG fprintf(stderr, "[%d] cmx_finalize()\n", g_state.rank); #endif /* it's okay to call multiple times -- extra calls are no-ops */ if (!initialized) { return CMX_SUCCESS; } cmx_barrier(CMX_GROUP_WORLD); initialized = 0; _free_semaphore(); /* Make sure that all outstanding operations are done */ cmx_wait_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); /* clean up non-blocking calls */ for (i=0; ioperation = OP_QUIT; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = 0; nb_send_header(header, sizeof(header_t), my_master, &nb); nb_wait_for_all(&nb); } free(fence_array); ierr = MPI_Barrier(g_state.comm); _translate_mpi_error(ierr, "cmx_finalize:MPI_Barrier"); /* reg_cache */ reg_cache_destroy(); /* destroy the groups */ #if DEBUG fprintf(stderr, "[%d] before cmx_group_finalize()\n", g_state.rank); #endif cmx_group_finalize(); #if DEBUG fprintf(stderr, "[%d] after cmx_group_finalize()\n", g_state.rank); #endif #if USE_SICM sicm_fini(); #endif #if DEBUG_TO_FILE fclose(cmx_trace_file); #endif return CMX_SUCCESS; } void cmx_error(const char *msg, int code) { #if DEBUG fprintf(stderr, "[%d] Received an Error in Communication: (%d) %s\n", g_state.rank, code, msg); #if DEBUG_TO_FILE fclose(cmx_trace_file); #endif #endif fprintf(stderr,"[%d] Received an Error in Communication: (%d) %s\n", g_state.rank, code, msg); MPI_Abort(g_state.comm, code); } /* return the allocated pointer corresponding to proc from the cmx_handle * object*/ void* find_alloc_ptr(cmx_handle_t cmx_hdl, int proc) { void *ret = NULL; cmx_alloc_t *next = cmx_hdl.list; while (next != NULL) { if (next->rank == proc) { ret = next->buf; break; } next = next->next; } return ret; } int cmx_put( void *src, cmxInt dst_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl) { _cmx_request nb; int world_proc = -1; cmx_igroup_t *igroup = NULL; void *dst; nb_handle_init(&nb); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); dst = find_alloc_ptr(cmx_hdl, world_proc); dst = (void*)((char*)dst+dst_offset); nb_put(src, dst, bytes, world_proc, &nb); nb_wait_for_all(&nb); return CMX_SUCCESS; } int cmx_get( void *dst, cmxInt src_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl) { _cmx_request nb; int world_proc = -1; cmx_igroup_t *igroup = NULL; void *src; nb_handle_init(&nb); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); src = find_alloc_ptr(cmx_hdl, world_proc); src = (void*)((char*)src+src_offset); nb_get(src, dst, bytes, world_proc, &nb); nb_wait_for_all(&nb); return CMX_SUCCESS; } int cmx_acc( int datatype, void *scale, void *src, cmxInt dst_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl) { _cmx_request nb; int world_proc = -1; cmx_igroup_t *igroup = NULL; void *dst; nb_handle_init(&nb); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); dst = find_alloc_ptr(cmx_hdl, world_proc); dst = (void*)((char*)dst+dst_offset); nb_acc(datatype, scale, src, dst, bytes, world_proc, &nb); nb_wait_for_all(&nb); return CMX_SUCCESS; } int cmx_puts( void *src, cmxInt *src_stride, cmxInt dst_offset, cmxInt *dst_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl) { _cmx_request nb; int world_proc = -1; cmx_igroup_t *igroup = NULL; void *dst; nb_handle_init(&nb); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); dst = find_alloc_ptr(cmx_hdl, world_proc); dst = (void*)((char*)dst+dst_offset); nb_puts(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, &nb); nb_wait_for_all(&nb); return CMX_SUCCESS; } int cmx_gets( void *dst, cmxInt *dst_stride, cmxInt src_offset, cmxInt *src_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl) { _cmx_request nb; int world_proc = -1; cmx_igroup_t *igroup = NULL; void *src; nb_handle_init(&nb); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); src = find_alloc_ptr(cmx_hdl, world_proc); src = (void*)((char*)src+src_offset); nb_gets(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, &nb); nb_wait_for_all(&nb); return CMX_SUCCESS; } int cmx_accs( int datatype, void *scale, void *src, cmxInt *src_stride, cmxInt dst_offset, cmxInt *dst_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl) { _cmx_request nb; int world_proc = -1; cmx_igroup_t *igroup = NULL; void *dst; nb_handle_init(&nb); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); dst = find_alloc_ptr(cmx_hdl, world_proc); dst = (void*)((char*)dst+dst_offset); nb_accs(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, world_proc, &nb); nb_wait_for_all(&nb); return CMX_SUCCESS; } int cmx_putv( cmx_giov_t *iov, cmxInt iov_len, int proc, cmx_handle_t cmx_hdl) { _cmx_request nb; int world_proc = -1; cmx_igroup_t *igroup = NULL; void *dst; _cmx_giov_t *tiov; nb_handle_init(&nb); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); dst = find_alloc_ptr(cmx_hdl, world_proc); /* copy iov to create a data structure compatible with underlying PR * implementation */ tiov = (_cmx_giov_t*)malloc(iov_len*sizeof(_cmx_giov_t)); int i,j; for (i=0; ioperation = OP_FENCE; header->remote_address = NULL; header->local_address = NULL; header->length = 0; header->rank = 0; nb_send_header(header, sizeof(header_t), p_master, &nb); } } nb_wait_for_all(&nb); for (p=0; poperation = OP_FENCE; header->remote_address = NULL; header->local_address = NULL; header->length = 0; header->rank = 0; nb_send_header(header, sizeof(header_t), master_rank, &nb); nb_wait_for_all(&nb); fence_array[master_rank] = 0; } } int cmx_fence_proc(int proc, cmx_igroup_t *igroup) { int world_rank = -1; int master_rank = -1; #if DEBUG printf("[%d] cmx_fence_proc(proc=%d, group=%d)\n", g_state.rank, proc, group); #endif world_rank = _get_world_rank(igroup, proc); master_rank = g_state.master[world_rank]; _fence_master(master_rank); return CMX_SUCCESS; } /* cmx_barrier is cmx_fence_all + MPI_Barrier */ int cmx_barrier(cmx_igroup_t *igroup) { int status = 0; MPI_Comm comm = MPI_COMM_NULL; #if DEBUG static int count=-1; ++count; fprintf(stderr, "[%d] cmx_barrier(%d) count=%d\n", g_state.rank, group, count); #endif cmx_fence_all(igroup); status = cmx_group_comm(igroup, &comm); CMX_ASSERT(CMX_SUCCESS == status); status = MPI_Barrier(comm); _translate_mpi_error(status, "cmx_barrier:MPI_Barrier"); return CMX_SUCCESS; } STATIC int _packed_size(int *src_stride, int *count, int stride_levels) { int size; int i; int n1dim; /* number of 1 dim block */ CMX_ASSERT(stride_levels >= 0); CMX_ASSERT(stride_levels < CMX_MAX_STRIDE_LEVEL); CMX_ASSERT(NULL != src_stride); CMX_ASSERT(NULL != count); CMX_ASSERT(count[0] > 0); #if DEBUG fprintf(stderr, "[%d] _packed_size(src_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, src_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* allocate packed buffer now that we know the size */ size = n1dim * count[0]; return size; } STATIC char* pack( char *src, int *src_stride, int *count, int stride_levels, int *size) { int i, j; long src_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int packed_index = 0; char *packed_buffer = NULL; CMX_ASSERT(stride_levels >= 0); CMX_ASSERT(stride_levels < CMX_MAX_STRIDE_LEVEL); CMX_ASSERT(NULL != src); CMX_ASSERT(NULL != src_stride); CMX_ASSERT(NULL != count); CMX_ASSERT(count[0] > 0); CMX_ASSERT(NULL != size); #if DEBUG fprintf(stderr, "[%d] pack(src=%p, src_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, src, src_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* allocate packed buffer now that we know the size */ packed_buffer = malloc(n1dim * count[0]); CMX_ASSERT(packed_buffer); /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } (void)memcpy(&packed_buffer[packed_index], &src[src_idx], count[0]); packed_index += count[0]; } CMX_ASSERT(packed_index == n1dim*count[0]); *size = packed_index; return packed_buffer; } STATIC void unpack(char *packed_buffer, char *dst, int *dst_stride, int *count, int stride_levels) { int i, j; long dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int dst_bvalue[7], dst_bunit[7]; int packed_index = 0; CMX_ASSERT(stride_levels >= 0); CMX_ASSERT(stride_levels < CMX_MAX_STRIDE_LEVEL); CMX_ASSERT(NULL != packed_buffer); CMX_ASSERT(NULL != dst); CMX_ASSERT(NULL != dst_stride); CMX_ASSERT(NULL != count); CMX_ASSERT(count[0] > 0); #if DEBUG fprintf(stderr, "[%d] unpack(dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, dst, dst_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { dst_bvalue[i] = 0; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { dst_bvalue[j] = 0; } } (void)memcpy(&dst[dst_idx], &packed_buffer[packed_index], count[0]); packed_index += count[0]; } CMX_ASSERT(packed_index == n1dim*count[0]); } STATIC char* _generate_shm_name(int rank) { int snprintf_retval = 0; /* /cmxUUUUUUUUUUPPPPPPPPPPCCCCCCN */ /* 0000000001111111111222222222233 */ /* 1234567890123456789012345678901 */ char *name = NULL; static const unsigned int limit = 62; static const char letters[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; static unsigned int counter[6] = {0}; CMX_ASSERT(rank >= 0); name = malloc(SHM_NAME_SIZE*sizeof(char)); CMX_ASSERT(name); snprintf_retval = snprintf(name, SHM_NAME_SIZE, "/cmx%010u%010u%c%c%c%c%c%c", getuid(), getpid(), letters[counter[5]], letters[counter[4]], letters[counter[3]], letters[counter[2]], letters[counter[1]], letters[counter[0]]); CMX_ASSERT(snprintf_retval < (int)SHM_NAME_SIZE); name[SHM_NAME_SIZE-1] = '\0'; ++counter[0]; if (counter[0] >= limit) { ++counter[1]; counter[0] = 0; } if (counter[1] >= limit) { ++counter[2]; counter[1] = 0; } if (counter[2] >= limit) { ++counter[3]; counter[2] = 0; } if (counter[3] >= limit) { ++counter[4]; counter[3] = 0; } if (counter[4] >= limit) { ++counter[5]; counter[4] = 0; } if (counter[5] >= limit) { cmx_error("_generate_shm_name: too many names generated", -1); } #if DEBUG fprintf(stderr, "[%d] _generate_shm_name(%d)=%s\n", g_state.rank, rank, name); #endif return name; } void* cmx_malloc_local(size_t size) { reg_entry_t *reg_entry; void *memory = NULL; if (size > 0) { reg_entry = _cmx_malloc_local(size); memory = reg_entry->mapped; } else { memory = NULL; } return memory; } STATIC reg_entry_t* _cmx_malloc_local(size_t size) { char *name = NULL; void *memory = NULL; reg_entry_t *reg_entry = NULL; #if DEBUG fprintf(stderr, "[%d] _cmx_malloc_local(size=%lu)\n", g_state.rank, (long unsigned)size); #endif if (0 == size) { return NULL; } /* create my shared memory object */ name = _generate_shm_name(g_state.rank); memory = _shm_create(name, size); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] _cmx_malloc_local registering " "rank=%d mem=%p size=%lu name=%s mapped=%p\n", g_state.rank, g_state.rank, memory, (long unsigned)size, name, memory); #endif /* register the memory locally */ #if USE_SICM #if SICM_OLD reg_entry = reg_cache_insert( g_state.rank, memory, size, name, memory, 0, NULL); #else reg_entry = reg_cache_insert( g_state.rank, memory, size, name, memory, 0, nill); #endif #else reg_entry = reg_cache_insert( g_state.rank, memory, size, name, memory, 0); #endif if (NULL == reg_entry) { cmx_error("_cmx_malloc_local: reg_cache_insert", -1); } free(name); return reg_entry; } #if USE_SICM #if SICM_OLD STATIC reg_entry_t* _cmx_malloc_local_memdev(size_t size, sicm_device *device) #else STATIC reg_entry_t* _cmx_malloc_local_memdev(size_t size, sicm_device_list device) #endif { char *name = NULL; void *memory = NULL; reg_entry_t *reg_entry = NULL; #if DEBUG fprintf(stderr, "[%d] _cmx_malloc_local(size=%lu)\n", g_state.rank, (long unsigned)size); #endif if (0 == size) { return NULL; } /* create my shared memory object */ name = _generate_shm_name(g_state.rank); memory = _shm_create_memdev(name, size, device); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] _cmx_malloc_local registering " "rank=%d mem=%p size=%lu name=%s mapped=%p\n", g_state.rank, g_state.rank, memory, (long unsigned)size, name, memory); #endif /* register the memory locally */ reg_entry = reg_cache_insert( g_state.rank, memory, size, name, memory, 1, device); if (NULL == reg_entry) { cmx_error("_cmx_malloc_local: reg_cache_insert", -1); } free(name); return reg_entry; } #endif int cmx_free_local(void *ptr) { int retval = 0; reg_entry_t *reg_entry = NULL; #if DEBUG fprintf(stderr, "[%d] cmx_free_local(ptr=%p)\n", g_state.rank, ptr); #endif if (NULL == ptr) { return CMX_SUCCESS; } /* find the registered memory */ reg_entry = reg_cache_find(g_state.rank, ptr, 0); /* unmap the memory */ retval = munmap(ptr, reg_entry->len); if (-1 == retval) { perror("cmx_free_local: munmap"); cmx_error("cmx_free_local: munmap", retval); } /* remove the shared memory object */ retval = shm_unlink(reg_entry->name); if (-1 == retval) { perror("cmx_free_local: shm_unlink"); cmx_error("cmx_free_local: shm_unlink", retval); } /* delete the reg_cache entry */ retval = reg_cache_delete(g_state.rank, ptr); CMX_ASSERT(RR_SUCCESS == retval); return CMX_SUCCESS; } #if USE_SICM int _cmx_free_local_memdev(void *ptr) { int retval = 0; reg_entry_t *reg_entry = NULL; #if DEBUG fprintf(stderr, "[%d] _cmx_free_local_memdev(ptr=%p)\n", g_state.rank, ptr); #endif if (NULL == ptr) { return CMX_SUCCESS; } /* find the registered memory */ reg_entry = reg_cache_find(g_state.rank, ptr, 0); /* unmap the memory */ sicm_free(ptr); retval = 0; if (-1 == retval) { perror("_cmx_free_local_memdev: munmap"); cmx_error("_cmx_free_local_memdev: munmap", retval); } /* remove the shared memory object */ retval = shm_unlink(reg_entry->name); if (-1 == retval) { perror("_cmx_free_local_memdev: shm_unlink"); cmx_error("_cmx_free_local_memdev: shm_unlink", retval); } /* delete the reg_cache entry */ retval = reg_cache_delete(g_state.rank, ptr); CMX_ASSERT(RR_SUCCESS == retval); return CMX_SUCCESS; } #endif int cmx_wait_proc(int proc, cmx_igroup_t *igroup) { return cmx_wait_all(igroup); } int cmx_wait(_cmx_request* hdl) { int index = 0; CMX_ASSERT(NULL != hdl); #if 0 /* this condition will likely be tripped if a blocking operation follows a * non-blocking operation*/ if (0 == nb->in_use) { fprintf(stderr, "p[%d] cmx_wait Error: invalid handle\n", g_state.rank); } #endif nb_wait_for_all(hdl); nb_unregister_request(hdl); return CMX_SUCCESS; } /* return 0 if operation is completed, 1 otherwise */ int cmx_test(_cmx_request* hdl, int *status) { int index = 0; CMX_ASSERT(NULL != hdl); #if 0 /* this condition will likely be tripped if a blocking operation follows a * non-blocking operation*/ if (0 == nb->in_use) { fprintf(stderr, "{%d} cmx_test Error: invalid handle\n", g_state.rank); } #endif if (!nb_test_for_all(hdl)) { /* Completed */ CMX_ASSERT(0 == hdl->send_size); CMX_ASSERT(0 == hdl->recv_size); *status = 0; } else { /* Not completed */ *status = 1; } return CMX_SUCCESS; } int cmx_wait_all(cmx_igroup_t *igroup) { int i; for (i=0; igroup == igroup) { nb_wait_for_all(nb_list[i]); nb_list[i]->in_use = 0; nb_list[i] = NULL; } } return CMX_SUCCESS; } /* The register and unregister functions are used limit to the number of * outstanding non-blocking handles*/ STATIC void nb_register_request(_cmx_request *nb) { int ival = -1; int i; return; for (i=nb_last_request; isend_size = 0; nb->send_head = NULL; nb->send_tail = NULL; nb->recv_size = 0; nb->recv_head = NULL; nb->recv_tail = NULL; nb->group = NULL; nb->in_use = 1; } int cmx_nbput( void *src, cmxInt dst_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl, cmx_request_t *hdl) { int world_proc = -1; cmx_igroup_t *igroup = NULL; void *dst; hdl->in_use = 1; nb_handle_init(hdl); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); dst = find_alloc_ptr(cmx_hdl, world_proc); dst = (void*)((char*)dst+dst_offset); nb_put(src, dst, bytes, world_proc, hdl); nb_register_request(hdl); return CMX_SUCCESS; } int cmx_nbget( void *dst, cmxInt src_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl, cmx_request_t *hdl) { int world_proc = -1; cmx_igroup_t *igroup = NULL; void *src; hdl->in_use = 1; nb_handle_init(hdl); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); src = find_alloc_ptr(cmx_hdl, world_proc); src = (void*)((char*)src+src_offset); nb_get(src, dst, bytes, world_proc, hdl); nb_register_request(hdl); return CMX_SUCCESS; } int cmx_nbacc( int datatype, void *scale, void *src, cmxInt dst_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl, cmx_request_t *hdl) { int world_proc = -1; cmx_igroup_t *igroup = NULL; void *dst; hdl->in_use = 1; nb_handle_init(hdl); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); dst = find_alloc_ptr(cmx_hdl, world_proc); dst = (void*)((char*)dst+dst_offset); nb_acc(datatype, scale, src, dst, bytes, world_proc, hdl); nb_register_request(hdl); return CMX_SUCCESS; } int cmx_nbputs( void *src, cmxInt *src_stride, cmxInt dst_offset, cmxInt *dst_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl, cmx_request_t *hdl) { int world_proc = -1; cmx_igroup_t *igroup = NULL; void *dst; hdl->in_use = 1; nb_handle_init(hdl); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); dst = find_alloc_ptr(cmx_hdl, world_proc); dst = (void*)((char*)dst+dst_offset); nb_puts(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, hdl); nb_register_request(hdl); return CMX_SUCCESS; } int cmx_nbgets( void *dst, cmxInt *dst_stride, cmxInt src_offset, cmxInt *src_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl, cmx_request_t *hdl) { _cmx_request *nb = NULL; int world_proc = -1; cmx_igroup_t *igroup = NULL; void *src; hdl->in_use = 1; nb_handle_init(hdl); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); src = find_alloc_ptr(cmx_hdl, world_proc); src = (void*)((char*)src+src_offset); nb_gets(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, hdl); nb_register_request(hdl); return CMX_SUCCESS; } int cmx_nbaccs( int datatype, void *scale, void *src, cmxInt *src_stride, cmxInt dst_offset, cmxInt *dst_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl, cmx_request_t *hdl) { _cmx_request *nb = NULL; int world_proc = -1; cmx_igroup_t *igroup = NULL; void *dst; hdl->in_use = 1; nb_handle_init(hdl); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); dst = find_alloc_ptr(cmx_hdl, world_proc); dst = (void*)((char*)dst+dst_offset); nb_accs(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, world_proc, hdl); nb_register_request(hdl); return CMX_SUCCESS; } /** * non-blocking vector put operation * @param iov: descriptor array * @param iov_len: length of descriptor array * @param proc: remote processor id * @param cmx_hdl: handle for data allocation * @param req: non-blocking request handle */ int cmx_nbputv( cmx_giov_t *iov, cmxInt iov_len, int proc, cmx_handle_t cmx_hdl, cmx_request_t *hdl) { _cmx_request *nb = NULL; int world_proc = -1; cmx_igroup_t *igroup = NULL; void *dst; _cmx_giov_t *tiov; hdl->in_use = 1; nb_handle_init(hdl); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); dst = find_alloc_ptr(cmx_hdl, world_proc); /* copy iov to create a data structure compatible with underlying PR * implementation */ tiov = (_cmx_giov_t*)malloc(iov_len*sizeof(_cmx_giov_t)); int i,j; for (i=0; iin_use = 1; nb_handle_init(hdl); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); src = find_alloc_ptr(cmx_hdl, world_proc); /* copy iov to create a data structure compatible with underlying PR * implementation */ tiov = (_cmx_giov_t*)malloc(iov_len*sizeof(_cmx_giov_t)); int i,j; for (i=0; iin_use = 1; nb_handle_init(hdl); igroup = cmx_hdl.group; CHECK_GROUP(igroup,proc); world_proc = _get_world_rank(igroup, proc); dst = find_alloc_ptr(cmx_hdl, world_proc); /* copy iov to create a data structure compatible with underlying PR * implementation */ tiov = (_cmx_giov_t*)malloc(iov_len*sizeof(_cmx_giov_t)); int i,j; for (i=0; ioperation = op; header->remote_address = prem; header->local_address = ploc; header->rank = world_rank; header->length = length; switch (cmx_op) { case CMX_FETCH_AND_ADD: case CMX_SWAP: (void)memcpy(message+sizeof(header_t), &payload_int, length); break; case CMX_FETCH_AND_ADD_LONG: case CMX_SWAP_LONG: (void)memcpy(message+sizeof(header_t), &payload_long, length); break; default: CMX_ASSERT(0); } nb_recv(ploc, length, master_rank, &nb); /* prepost recv */ nb_send_header(message, sizeof(header_t)+length, master_rank, &nb); nb_wait_for_all(&nb); return CMX_SUCCESS; } /* Mutex Operations */ int cmx_create_mutexes(int num) { /* always on the world group */ int my_master = g_state.master[g_state.rank]; int status = 0; #if DEBUG fprintf(stderr, "[%d] cmx_create_mutexes(num=%d)\n", g_state.rank, num); #endif /* preconditions */ CMX_ASSERT(0 <= num); CMX_ASSERT(NULL == num_mutexes); num_mutexes = (int*)malloc(group_list->size * sizeof(int)); /* exchange of mutex counts */ num_mutexes[group_list->rank] = num; status = MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, num_mutexes, 1, MPI_INT, group_list->comm); _translate_mpi_error(status, "cmx_create_mutexes:MPI_Allgather"); CMX_ASSERT(MPI_SUCCESS == status); /* every process sends their own create message to their master */ { _cmx_request nb; header_t *header = NULL; nb_handle_init(&nb); header = malloc(sizeof(header_t)); CMX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_CREATE_MUTEXES; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = num; nb_recv(NULL, 0, my_master, &nb); /* prepost ack */ nb_send_header(header, sizeof(header_t), my_master, &nb); nb_wait_for_all(&nb); } cmx_barrier(CMX_GROUP_WORLD); return CMX_SUCCESS; } int cmx_destroy_mutexes() { /* always on the world group */ int my_master = g_state.master[g_state.rank]; #if DEBUG fprintf(stderr, "[%d] cmx_destroy_mutexes()\n", g_state.rank); #endif /* preconditions */ CMX_ASSERT(num_mutexes); /* this call is collective on the world group and this barrier ensures * there are no outstanding lock requests */ cmx_barrier(CMX_GROUP_WORLD); /* let masters know they need to participate */ /* first non-master rank in an SMP node sends the message to master */ if (_smallest_world_rank_with_same_hostid(group_list) == g_state.rank) { _cmx_request nb; header_t *header = NULL; nb_handle_init(&nb); header = malloc(sizeof(header_t)); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_DESTROY_MUTEXES; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = num_mutexes[g_state.rank]; nb_recv(NULL, 0, my_master, &nb); /* prepost ack */ nb_send_header(header, sizeof(header_t), my_master, &nb); nb_wait_for_all(&nb); } free(num_mutexes); num_mutexes = NULL; return CMX_SUCCESS; } int cmx_lock(int mutex, int proc) { header_t *header = NULL; int world_rank = 0; int master_rank = 0; int ack = 0; cmx_igroup_t *igroup = NULL; _cmx_request nb; #if DEBUG fprintf(stderr, "[%d] cmx_lock mutex=%d proc=%d\n", g_state.rank, mutex, proc); #endif nb_handle_init(&nb); CHECK_GROUP(CMX_GROUP_WORLD,proc); igroup = CMX_GROUP_WORLD; world_rank = _get_world_rank(igroup, proc); master_rank = g_state.master[world_rank]; header = malloc(sizeof(header_t)); CMX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_LOCK; header->remote_address = NULL; header->local_address = NULL; header->rank = world_rank; header->length = mutex; nb_recv(&ack, sizeof(int), master_rank, &nb); /* prepost ack */ nb_send_header(header, sizeof(header_t), master_rank, &nb); nb_wait_for_all(&nb); CMX_ASSERT(mutex == ack); return CMX_SUCCESS; } int cmx_unlock(int mutex, int proc) { header_t *header = NULL; int world_rank = 0; int master_rank = 0; cmx_igroup_t *igroup = NULL; _cmx_request nb; #if DEBUG fprintf(stderr, "[%d] cmx_unlock mutex=%d proc=%d\n", g_state.rank, mutex, proc); #endif nb_handle_init(&nb); CHECK_GROUP(CMX_GROUP_WORLD,proc); igroup = CMX_GROUP_WORLD; world_rank = _get_world_rank(igroup, proc); master_rank = g_state.master[world_rank]; fence_array[master_rank] = 1; header = malloc(sizeof(header_t)); CMX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_UNLOCK; header->remote_address = NULL; header->local_address = NULL; header->rank = world_rank; header->length = mutex; nb_send_header(header, sizeof(header_t), master_rank, &nb); nb_wait_for_all(&nb); return CMX_SUCCESS; } int cmx_malloc(cmx_handle_t *cmx_hdl, cmxInt size, cmx_igroup_t *igroup) { #if USE_SICM && TEST_SICM char cdevice[32]; # ifdef TEST_SICM_DEV strcpy(cdevice,STR(TEST_SICM_DEV)); # else strcpy(cdevice,"dram"); # endif return cmx_malloc_mem_dev(cmx_hdl, size, group, cdevice); #else reg_entry_t *reg_entries = NULL; reg_entry_t my_reg; size_t size_entries = 0; int my_master = -1; int my_world_rank = -1; int i = 0; int is_notifier = 0; int reg_entries_local_count = 0; reg_entry_t *reg_entries_local = NULL; int status = 0; /* preconditions */ CMX_ASSERT(cmx_hdl); #if DEBUG fprintf(stderr, "[%d] cmx_malloc(ptrs=%p, size=%lu, group=%d)\n", g_state.rank, ptrs, (long unsigned)size, group); #endif /* is this needed? */ cmx_barrier(igroup); my_world_rank = _get_world_rank(igroup, igroup->rank); my_master = g_state.master[my_world_rank]; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_malloc my_master=%d\n", g_state.rank, my_master); #endif int smallest_rank_with_same_hostid, largest_rank_with_same_hostid; int num_progress_ranks_per_node, is_node_ranks_packed; num_progress_ranks_per_node = get_num_progress_ranks_per_node(); is_node_ranks_packed = get_progress_rank_distribution_on_node(); smallest_rank_with_same_hostid = _smallest_world_rank_with_same_hostid(igroup); largest_rank_with_same_hostid = _largest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == get_my_master_rank_with_same_hostid(g_state.rank, g_state.node_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed); #if 0 #if MASTER_IS_SMALLEST_SMP_RANK // is_notifier = _smallest_world_rank_with_same_hostid(igroup) == g_state.rank; int smallest_rank_with_same_hostid = _smallest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == smallest_rank_with_same_hostid + g_state.node_size* ((g_state.rank - smallest_rank_with_same_hostid)/g_state.node_size); #else // is_notifier = _largest_world_rank_with_same_hostid(igroup) == g_state.rank; int largest_rank_with_same_hostid = _largest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == (largest_rank_with_same_hostid - g_state.node_size * ((largest_rank_with_same_hostid - g_state.rank)/g_state.node_size)); #endif #endif if (is_notifier) { reg_entries_local = malloc(sizeof(reg_entry_t)*g_state.node_size); } /* allocate space for registration cache entries */ size_entries = sizeof(reg_entry_t) * igroup->size; reg_entries = malloc(size_entries); MAYBE_MEMSET(reg_entries, 0, sizeof(reg_entry_t)*igroup->size); #if DEBUG fprintf(stderr, "[%d] cmx_malloc lr_same_hostid=%d\n", g_state.rank, largest_rank_with_same_hostid); fprintf(stderr, "[%d] cmx_malloc igroup size=%d\n", g_state.rank, igroup->size); fprintf(stderr, "[%d] cmx_malloc node_size=%d\n", g_state.rank, g_state.node_size); fprintf(stderr, "[%d] cmx_malloc is_notifier=%d\n", g_state.rank, is_notifier); fprintf(stderr, "[%d] rank, igroup size[5%d]\n", g_state.rank, igroup->size); #endif #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_malloc allocated reg entries\n", g_state.rank); #endif /* allocate and register segment */ MAYBE_MEMSET(&my_reg, 0, sizeof(reg_entry_t)); if (0 == size) { reg_cache_nullify(&my_reg); } else { my_reg = *_cmx_malloc_local(sizeof(char)*size); } #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_malloc allocated and registered local shmem\n", g_state.rank); #endif /* exchange buffer address via reg entries */ reg_entries[igroup->rank] = my_reg; status = MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, reg_entries, sizeof(reg_entry_t), MPI_BYTE, igroup->comm); _translate_mpi_error(status, "cmx_malloc:MPI_Allgather"); #if DEBUG fprintf(stderr, "[%d] cmx_malloc allgather status [%d]\n", g_state.rank, status); #endif CMX_ASSERT(MPI_SUCCESS == status); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_malloc allgather reg entries\n", g_state.rank); #endif /* insert reg entries into local registration cache */ for (i=0; isize; ++i) { if (NULL == reg_entries[i].buf) { /* a proc did not allocate (size==0) */ #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_malloc found NULL buf at %d\n", g_state.rank, i); #endif } else if (g_state.rank == reg_entries[i].rank) { /* we already registered our own memory, but PR hasn't */ #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_malloc found self at %d\n", g_state.rank, i); #endif if (is_notifier) { /* does this need to be a memcpy?? */ reg_entries_local[reg_entries_local_count++] = reg_entries[i]; } } // else if (g_state.hostid[reg_entries[i].rank] // == g_state.hostid[my_world_rank]) else if (g_state.master[reg_entries[i].rank] == g_state.master[get_my_master_rank_with_same_hostid(g_state.rank, g_state.node_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed)] ) #if 0 #if MASTER_IS_SMALLEST_SMP_RANK else if (g_state.master[reg_entries[i].rank] == g_state.master[(smallest_rank_with_same_hostid + g_state.node_size * ((g_state.rank - smallest_rank_with_same_hostid)/g_state.node_size))]) #else else if (g_state.master[reg_entries[i].rank] == g_state.master[(largest_rank_with_same_hostid - g_state.node_size * ((largest_rank_with_same_hostid - g_state.rank)/g_state.node_size))]) #endif #endif { /* same SMP node, need to mmap */ /* open remote shared memory object */ void *memory = _shm_attach(reg_entries[i].name, reg_entries[i].len); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_malloc registering " "rank=%d buf=%p len=%lu name=%s map=%p\n", g_state.rank, reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, memory); #endif (void)reg_cache_insert( reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, memory,0 #if USE_SICM #if SICM_OLD ,NULL #else ,nill #endif #endif ); if (is_notifier) { /* does this need to be a memcpy?? */ reg_entries_local[reg_entries_local_count++] = reg_entries[i]; } } else { #if 0 /* remote SMP node */ /* i.e. we know about the mem but don't have local shared access */ (void)reg_cache_insert( reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, NULL); #endif } } /* assign the cmx handle to return to caller */ cmx_alloc_t *prev = NULL; for (i=0; isize; ++i) { cmx_alloc_t *link = (cmx_alloc_t*)malloc(sizeof(cmx_alloc_t)); cmx_hdl->list = link; link->buf = reg_entries[i].buf; link->size = (cmxInt)reg_entries[i].len; link->rank = reg_entries[i].rank; link->next = prev; prev = link; } cmx_hdl->group = igroup; cmx_hdl->rank = igroup->rank; cmx_hdl->buf = my_reg.mapped; cmx_hdl->bytes = my_reg.len; /* send reg entries to my master */ /* first non-master rank in an SMP node sends the message to master */ if (is_notifier) { _cmx_request nb; int reg_entries_local_size = 0; int message_size = 0; char *message = NULL; header_t *header = NULL; nb_handle_init(&nb); reg_entries_local_size = sizeof(reg_entry_t)*reg_entries_local_count; message_size = sizeof(header_t) + reg_entries_local_size; message = malloc(message_size); CMX_ASSERT(message); header = (header_t*)message; header->operation = OP_MALLOC; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = reg_entries_local_count; (void)memcpy(message+sizeof(header_t), reg_entries_local, reg_entries_local_size); nb_recv(NULL, 0, my_master, &nb); /* prepost ack */ nb_send_header(message, message_size, my_master, &nb); nb_wait_for_all(&nb); free(reg_entries_local); } free(reg_entries); cmx_barrier(igroup); return CMX_SUCCESS; #endif } int cmx_malloc_mem_dev(cmx_handle_t *cmx_hdl, size_t size, cmx_igroup_t *igroup, const char* device) { #if (!defined(USE_SICM) || !USE_SICM) return cmx_malloc(cmx_hdl,size,igroup); #else cmx_igroup_t *igroup = NULL; reg_entry_t *reg_entries = NULL; reg_entry_t my_reg; size_t size_entries = 0; int my_master = -1; int my_world_rank = -1; int i = 0; int is_notifier = 0; int reg_entries_local_count = 0; reg_entry_t *reg_entries_local = NULL; int status = 0; #if SICM_OLD sicm_device *idevice = NULL; #else sicm_device_list idevice; idevice.count = 0; idevice.devices = NULL; #endif /* preconditions */ CMX_ASSERT(ptrs); #if DEBUG fprintf(stderr, "[%d] cmx_malloc(ptrs=%p, size=%lu, group=%d)\n", g_state.rank, ptrs, (long unsigned)size, group); #endif /* is this needed? */ cmx_barrier(igroup); my_world_rank = _get_world_rank(igroup, igroup->rank); my_master = g_state.master[my_world_rank]; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_malloc my_master=%d\n", g_state.rank, my_master); #endif #if MASTER_IS_SMALLEST_SMP_RANK is_notifier = _smallest_world_rank_with_same_hostid(igroup) == g_state.rank; #else is_notifier = _largest_world_rank_with_same_hostid(igroup) == g_state.rank; #endif if (is_notifier) { reg_entries_local = malloc(sizeof(reg_entry_t)*g_state.node_size); } /* allocate space for registration cache entries */ size_entries = sizeof(reg_entry_t) * igroup->size; reg_entries = malloc(size_entries); MAYBE_MEMSET(reg_entries, 0, sizeof(reg_entry_t)*igroup->size); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_malloc allocated reg entries\n", g_state.rank); #endif /* allocate and register segment */ MAYBE_MEMSET(&my_reg, 0, sizeof(reg_entry_t)); if (0 == size) { reg_cache_nullify(&my_reg); } else { idevice = device_dram; if (!strncmp(device,"dram",4)) { idevice = device_dram; } else if (!strncmp(device,"knl_hbm",7)) { idevice = device_knl_hbm; } else if (!strncmp(device,"ppc_hbm",7)) { idevice = device_ppc_hbm; } my_reg = *_cmx_malloc_local_memdev(sizeof(char)*size, idevice); } #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_malloc allocated and registered local shmem\n", g_state.rank); #endif /* exchange buffer address via reg entries */ reg_entries[igroup->rank] = my_reg; status = MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, reg_entries, sizeof(reg_entry_t), MPI_BYTE, igroup->comm); _translate_mpi_error(status, "cmx_malloc_mem_dev:MPI_Allgather"); CMX_ASSERT(MPI_SUCCESS == status); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_malloc allgather reg entries\n", g_state.rank); #endif /* insert reg entries into local registration cache */ for (i=0; isize; ++i) { if (NULL == reg_entries[i].buf) { /* a proc did not allocate (size==0) */ #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_malloc found NULL buf at %d\n", g_state.rank, i); #endif } else if (g_state.rank == reg_entries[i].rank) { /* we already registered our own memory, but PR hasn't */ #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_malloc found self at %d\n", g_state.rank, i); #endif if (is_notifier) { /* does this need to be a memcpy?? */ reg_entries_local[reg_entries_local_count++] = reg_entries[i]; } } else if (g_state.hostid[reg_entries[i].rank] == g_state.hostid[my_world_rank]) { /* same SMP node, need to mmap */ /* open remote shared memory object */ void *memory = _shm_attach_memdev(reg_entries[i].name, reg_entries[i].len, idevice); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_malloc registering " "rank=%d buf=%p len=%lu name=%s map=%p\n", g_state.rank, reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, memory); #endif (void)reg_cache_insert( reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, memory, 1, idevice); if (is_notifier) { /* does this need to be a memcpy?? */ reg_entries_local[reg_entries_local_count++] = reg_entries[i]; } } else { #if 0 /* remote SMP node */ /* i.e. we know about the mem but don't have local shared access */ (void)reg_cache_insert( reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, NULL); #endif } } /* assign the cmx handle to return to caller */ cmx_alloc_t *prev = NULL; for (i=0; isize; ++i) { cmx_alloc_t *link = (cmx_alloc_t*)malloc(sizeof(cmx_alloc_t)); cmx_hdl->list = link; link->buf = reg_entries[i].buf; link->size = (cmxInt)reg_entries[i].len; link->rank = reg_entries[i].rank; link->next = prev; prev = link; } cmx_hdl->group = igroup; cmx_hdl->rank = igroup->rank; cmx_hdl->buf = my_reg.mapped; cmx_hdl->bytes = my_reg.len; /* send reg entries to my master */ /* first non-master rank in an SMP node sends the message to master */ if (is_notifier) { _cmx_request nb; int reg_entries_local_size = 0; int message_size = 0; char *message = NULL; header_t *header = NULL; nb_handle_init(&nb); reg_entries_local_size = sizeof(reg_entry_t)*reg_entries_local_count; #if DEBUG fprintf(stderr, "[%d] cmx_malloc, reg_entries_local_count[%d]\n", g_state.rank, reg_entries_local_count); #endif message_size = sizeof(header_t) + reg_entries_local_size; message = malloc(message_size); CMX_ASSERT(message); header = (header_t*)message; header->operation = OP_MALLOC; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = reg_entries_local_count; (void)memcpy(message+sizeof(header_t), reg_entries_local, reg_entries_local_size); nb_recv(NULL, 0, my_master, &nb); /* prepost ack */ nb_send_header(message, message_size, my_master, &nb); nb_wait_for_all(&nb); free(reg_entries_local); } free(reg_entries); cmx_barrier(igroup); return CMX_SUCCESS; #endif } /* one unnamed semaphore per world process */ void _malloc_semaphore() { char *name = NULL; char *names = NULL; sem_t *my_sem = NULL; int status = 0; MPI_Datatype shm_name_type; int i = 0; #if DEBUG fprintf(stderr, "[%d] _malloc_semaphore()\n", g_state.rank); #endif status = MPI_Type_contiguous(SHM_NAME_SIZE, MPI_CHAR, &shm_name_type); _translate_mpi_error(status, "_malloc_semaphore:MPI_Type_contiguous"); CMX_ASSERT(MPI_SUCCESS == status); status = MPI_Type_commit(&shm_name_type); _translate_mpi_error(status, "_malloc_semaphore:MPI_Type_commmit"); CMX_ASSERT(MPI_SUCCESS == status); semaphores = (sem_t**)malloc(sizeof(sem_t*) * g_state.size); CMX_ASSERT(semaphores); name = _generate_shm_name(g_state.rank); CMX_ASSERT(name); #if ENABLE_UNNAMED_SEM { my_sem = _shm_create(name, sizeof(sem_t)); /* initialize the memory as an inter-process semaphore */ if (0 != sem_init(my_sem, 1, 1)) { perror("_malloc_semaphore: sem_init"); cmx_error("_malloc_semaphore: sem_init", -1); } } #else { my_sem = sem_open(name, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR, 1); if (SEM_FAILED == my_sem) { if (EEXIST == errno) { status = sem_unlink(name); if (-1 == status) { perror("_malloc_semaphore: sem_unlink"); cmx_error("_malloc_semaphore: sem_unlink", status); } } /* second try */ my_sem = sem_open(name, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR, 1); } if (SEM_FAILED == my_sem) { perror("_malloc_semaphore: sem_open"); cmx_error("_malloc_semaphore: sem_open", -1); } } #endif /* store my sem in global cache */ semaphores[g_state.rank] = my_sem; names = (char*)malloc(sizeof(char) * SHM_NAME_SIZE * g_state.size); CMX_ASSERT(names); /* exchange names */ (void)memcpy(&names[SHM_NAME_SIZE*g_state.rank], name, SHM_NAME_SIZE); status = MPI_Allgather(MPI_IN_PLACE, 1, shm_name_type, names, 1, shm_name_type, g_state.comm); _translate_mpi_error(status, "_malloc_semaphore:MPI_Allgather"); CMX_ASSERT(MPI_SUCCESS == status); /* create/open remote semaphores and store in cache */ for (i=0; irank]; my_master = g_state.master[my_world_rank]; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free my_master=%d\n", g_state.rank, my_master); #endif int num_progress_ranks_per_node = get_num_progress_ranks_per_node(); int is_node_ranks_packed = get_progress_rank_distribution_on_node(); int smallest_rank_with_same_hostid = _smallest_world_rank_with_same_hostid(igroup); int largest_rank_with_same_hostid = _largest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == get_my_master_rank_with_same_hostid(g_state.rank, g_state.node_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed); #if 0 #if MASTER_IS_SMALLEST_SMP_RANK // is_notifier = _smallest_world_rank_with_same_hostid(igroup) == g_state.rank; int smallest_rank_with_same_hostid = _smallest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == smallest_rank_with_same_hostid + g_state.node_size* ((g_state.rank - smallest_rank_with_same_hostid)/g_state.node_size); #else // is_notifier = _largest_world_rank_with_same_hostid(igroup) == g_state.rank; int largest_rank_with_same_hostid = _largest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == (largest_rank_with_same_hostid - g_state.node_size * ((largest_rank_with_same_hostid - g_state.rank)/g_state.node_size)); #endif #endif if (is_notifier) { rank_ptrs = malloc(sizeof(rank_ptr_t)*g_state.node_size); } /* allocate receive buffer for exchange of pointers */ ptrs = (void **)malloc(sizeof(void *) * igroup->size); CMX_ASSERT(ptrs); ptrs[igroup->rank] = find_alloc_ptr(cmx_hdl, my_world_rank); ptr = ptrs[igroup->rank]; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free ptrs allocated and assigned\n", g_state.rank); #endif /* exchange of pointers */ status = MPI_Allgather(MPI_IN_PLACE, sizeof(void *), MPI_BYTE, ptrs, sizeof(void *), MPI_BYTE, igroup->comm); _translate_mpi_error(status, "cmx_free:MPI_Allgather"); CMX_ASSERT(MPI_SUCCESS == status); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free ptrs exchanged\n", g_state.rank); #endif /* remove all pointers from registration cache */ for (i=0; isize; ++i) { if (i == igroup->rank) { #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free found self at %d\n", g_state.rank, i); #endif if (is_notifier) { /* does this need to be a memcpy? */ rank_ptrs[reg_entries_local_count].rank = world_ranks[i]; rank_ptrs[reg_entries_local_count].ptr = ptrs[i]; reg_entries_local_count++; } } else if (NULL == ptrs[i]) { #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free found NULL at %d\n", g_state.rank, i); #endif } // else if (g_state.hostid[world_ranks[i]] // == g_state.hostid[g_state.rank]) else if (g_state.master[world_ranks[i]] == g_state.master[get_my_master_rank_with_same_hostid(g_state.rank, g_state.node_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed)] ) #if 0 #if MASTER_IS_SMALLEST_SMP_RANK else if (g_state.master[world_ranks[i]] == g_state.master[(smallest_rank_with_same_hostid + g_state.node_size * ((g_state.rank - smallest_rank_with_same_hostid)/g_state.node_size))]) #else else if (g_state.master[world_ranks[i]] == g_state.master[(largest_rank_with_same_hostid - g_state.node_size * ((largest_rank_with_same_hostid - g_state.rank)/g_state.node_size))]) #endif #endif { /* same SMP node */ reg_entry_t *reg_entry = NULL; int retval = 0; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free same hostid at %d\n", g_state.rank, i); #endif /* find the registered memory */ reg_entry = reg_cache_find(world_ranks[i], ptrs[i], 0); CMX_ASSERT(reg_entry); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free found reg entry\n", g_state.rank); #endif /* unmap the memory */ retval = munmap(reg_entry->mapped, reg_entry->len); if (-1 == retval) { perror("cmx_free: munmap"); cmx_error("cmx_free: munmap", retval); } #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free unmapped mapped memory in reg entry\n", g_state.rank); #endif reg_cache_delete(world_ranks[i], ptrs[i]); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free deleted reg cache entry\n", g_state.rank); #endif if (is_notifier) { /* does this need to be a memcpy? */ rank_ptrs[reg_entries_local_count].rank = world_ranks[i]; rank_ptrs[reg_entries_local_count].ptr = ptrs[i]; reg_entries_local_count++; } } else { #if 0 reg_cache_delete(world_ranks[i], ptrs[i]); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free deleted reg cache entry\n", g_state.rank); #endif #endif } } /* send ptrs to my master */ /* first non-master rank in an SMP node sends the message to master */ if (is_notifier) { _cmx_request nb; int rank_ptrs_local_size = 0; int message_size = 0; char *message = NULL; header_t *header = NULL; nb_handle_init(&nb); rank_ptrs_local_size = sizeof(rank_ptr_t) * reg_entries_local_count; message_size = sizeof(header_t) + rank_ptrs_local_size; message = malloc(message_size); CMX_ASSERT(message); header = (header_t*)message; header->operation = OP_FREE; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = reg_entries_local_count; (void)memcpy(message+sizeof(header_t), rank_ptrs, rank_ptrs_local_size); nb_recv(NULL, 0, my_master, &nb); /* prepost ack */ nb_send_header(message, message_size, my_master, &nb); nb_wait_for_all(&nb); free(rank_ptrs); } /* free ptrs array */ free(ptrs); free(world_ranks); /* remove my ptr from reg cache and free ptr */ cmx_free_local(ptr); /* Is this needed? */ cmx_barrier(igroup); /* clean up the cmx_handle_t struct */ list = cmx_hdl.list; while (list) { next = list; list = next->next; free(next); } return CMX_SUCCESS; #endif } int cmx_free_dev(cmx_handle_t cmx_hdl) { #if USE_SICM cmx_igroup_t *igroup = NULL; int my_world_rank = -1; int *world_ranks = NULL; int my_master = -1; void **ptrs = NULL; int i = 0; int is_notifier = 0; int reg_entries_local_count = 0; rank_ptr_t *rank_ptrs = NULL; int status = 0; #if DEBUG fprintf(stderr, "[%d] cmx_free_dev(ptr=%p, group=%d)\n", g_state.rank, ptr, group); #endif igroup = cmx_hdl.group; cmx_barrier(group); world_ranks = _get_world_ranks(igroup); my_world_rank = world_ranks[igroup->rank]; my_master = g_state.master[my_world_rank]; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free_dev my_master=%d\n", g_state.rank, my_master); #endif int num_progress_ranks_per_node = get_num_progress_ranks_per_node(); int is_node_ranks_packed = get_progress_rank_distribution_on_node(); int smallest_rank_with_same_hostid = _smallest_world_rank_with_same_hostid(igroup); int largest_rank_with_same_hostid = _largest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == get_my_master_rank_with_same_hostid(g_state.rank, g_state.node_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed); #if 0 #if MASTER_IS_SMALLEST_SMP_RANK // is_notifier = _smallest_world_rank_with_same_hostid(igroup) == g_state.rank; int smallest_rank_with_same_hostid = _smallest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == smallest_rank_with_same_hostid + g_state.node_size* ((g_state.rank - smallest_rank_with_same_hostid)/g_state.node_size); #else // is_notifier = _largest_world_rank_with_same_hostid(igroup) == g_state.rank; int largest_rank_with_same_hostid = _largest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == (largest_rank_with_same_hostid - g_state.node_size * ((largest_rank_with_same_hostid - g_state.rank)/g_state.node_size)); #endif #endif if (is_notifier) { rank_ptrs = malloc(sizeof(rank_ptr_t)*g_state.node_size); } /* allocate receive buffer for exchange of pointers */ ptrs = (void **)malloc(sizeof(void *) * igroup->size); CMX_ASSERT(ptrs); ptrs[igroup->rank] = find_alloc_ptr(handle, my_world_rank); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free_dev ptrs allocated and assigned\n", g_state.rank); #endif /* exchange of pointers */ status = MPI_Allgather(MPI_IN_PLACE, sizeof(void *), MPI_BYTE, ptrs, sizeof(void *), MPI_BYTE, igroup->comm); _translate_mpi_error(status, "cmx_free_dev:MPI_Allgather"); CMX_ASSERT(MPI_SUCCESS == status); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free_dev ptrs exchanged\n", g_state.rank); #endif /* remove all pointers from registration cache */ for (i=0; isize; ++i) { if (i == igroup->rank) { #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free_dev found self at %d\n", g_state.rank, i); #endif if (is_notifier) { /* does this need to be a memcpy? */ rank_ptrs[reg_entries_local_count].rank = world_ranks[i]; rank_ptrs[reg_entries_local_count].ptr = ptrs[i]; reg_entries_local_count++; } } else if (NULL == ptrs[i]) { #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free_dev found NULL at %d\n", g_state.rank, i); #endif } // else if (g_state.hostid[world_ranks[i]] // == g_state.hostid[g_state.rank]) else if (g_state.master[world_ranks[i]] == g_state.master[get_my_master_rank_with_same_hostid(g_state.rank, g_state.node_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed)] ) #if 0 #if MASTER_IS_SMALLEST_SMP_RANK else if (g_state.master[world_ranks[i]] == g_state.master[(smallest_rank_with_same_hostid + g_state.node_size * ((g_state.rank - smallest_rank_with_same_hostid)/g_state.node_size))]) #else else if (g_state.master[world_ranks[i]] == g_state.master[(largest_rank_with_same_hostid - g_state.node_size * ((largest_rank_with_same_hostid - g_state.rank)/g_state.node_size))]) #endif #endif { /* same SMP node */ reg_entry_t *reg_entry = NULL; int retval = 0; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free_dev same hostid at %d\n", g_state.rank, i); #endif /* find the registered memory */ reg_entry = reg_cache_find(world_ranks[i], ptrs[i], 0); CMX_ASSERT(reg_entry); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free_dev found reg entry\n", g_state.rank); #endif /* free the memory */ sicm_free(reg_entry->mapped); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free_dev unmapped mapped memory in reg entry\n", g_state.rank); #endif reg_cache_delete(world_ranks[i], ptrs[i]); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] cmx_free_dev deleted reg cache entry\n", g_state.rank); #endif if (is_notifier) { /* does this need to be a memcpy? */ rank_ptrs[reg_entries_local_count].rank = world_ranks[i]; rank_ptrs[reg_entries_local_count].ptr = ptrs[i]; reg_entries_local_count++; } } } /* send ptrs to my master */ /* first non-master rank in an SMP node sends the message to master */ if (is_notifier) { _cmx_request nb; int rank_ptrs_local_size = 0; int message_size = 0; char *message = NULL; header_t *header = NULL; nb_handle_init(&nb); rank_ptrs_local_size = sizeof(rank_ptr_t) * reg_entries_local_count; message_size = sizeof(header_t) + rank_ptrs_local_size; message = malloc(message_size); CMX_ASSERT(message); header = (header_t*)message; header->operation = OP_FREE; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = reg_entries_local_count; (void)memcpy(message+sizeof(header_t), rank_ptrs, rank_ptrs_local_size); nb_recv(NULL, 0, my_master, &nb); /* prepost ack */ nb_send_header(message, message_size, my_master, &nb); nb_wait_for_all(&nb); free(rank_ptrs); } /* free ptrs array */ free(ptrs); free(world_ranks); /* remove my ptr from reg cache and free ptr */ _cmx_free_local_memdev(ptr); /* Is this needed? */ cmx_barrier(igroup); /* clean up the cmx_handle_t struct */ list = handle.list; while (list) { next = list; list = next->next; free(next); } return CMX_SUCCESS; #else return cmx_free(cmx_hdl); #endif } STATIC void _progress_server() { int running = 0; char *static_header_buffer = NULL; int static_header_buffer_size = 0; int extra_size = 0; int ierr; #if DEBUG fprintf(stderr, "[%d] _progress_server()\n", g_state.rank); fprintf(stderr, "[%d] _progress_server(); node_size[%d]\n", g_state.rank, g_state.node_size); #endif { int status = _set_affinity(g_state.node_size); if (0 != status) { status = _set_affinity(g_state.node_size-1); CMX_ASSERT(0 == status); } } /* static header buffer size must be large enough to hold the biggest * message that might possibly be sent using a header type message. */ static_header_buffer_size += sizeof(header_t); /* extra header info could be reg entries, one per local rank */ extra_size = sizeof(reg_entry_t)*g_state.node_size; /* or, extra header info could be an acc scale plus stride */ if ((sizeof(stride_t)+sizeof(DoubleComplex)) > extra_size) { extra_size = sizeof(stride_t)+sizeof(DoubleComplex); } static_header_buffer_size += extra_size; /* after all of the above, possibly grow the size based on user request */ if (static_header_buffer_size < eager_threshold) { static_header_buffer_size = eager_threshold; } /* initialize shared buffers */ static_header_buffer = (char*)malloc(sizeof(char)*static_header_buffer_size); CMX_ASSERT(static_header_buffer); static_server_buffer = (char*)malloc(sizeof(char)*static_server_buffer_size); CMX_ASSERT(static_server_buffer); running = 1; while (running) { int source = 0; int length = 0; char *payload = NULL; header_t *header = NULL; MPI_Status recv_status; ierr = MPI_Recv(static_header_buffer, static_header_buffer_size, MPI_CHAR, MPI_ANY_SOURCE, CMX_TAG, g_state.comm, &recv_status); _translate_mpi_error(ierr, "_progress_server:MPI_Recv"); MPI_Get_count(&recv_status, MPI_CHAR, &length); _translate_mpi_error(ierr, "_progress_server:MPI_Get_count"); source = recv_status.MPI_SOURCE; # if DEBUG fprintf(stderr, "[%d] progress MPI_Recv source=%d length=%d\n", g_state.rank, source, length); # endif header = (header_t*)static_header_buffer; payload = static_header_buffer + sizeof(header_t); /* dispatch message handler */ switch (header->operation) { case OP_PUT: _put_handler(header, payload, source); break; case OP_PUT_PACKED: _put_packed_handler(header, payload, source); break; case OP_PUT_DATATYPE: _put_datatype_handler(header, payload, source); break; case OP_PUT_IOV: _put_iov_handler(header, source); break; case OP_GET: _get_handler(header, source); break; case OP_GET_PACKED: _get_packed_handler(header, payload, source); break; case OP_GET_DATATYPE: _get_datatype_handler(header, payload, source); break; case OP_GET_IOV: _get_iov_handler(header, source); break; case OP_ACC_INT: case OP_ACC_DBL: case OP_ACC_FLT: case OP_ACC_CPL: case OP_ACC_DCP: case OP_ACC_LNG: _acc_handler(header, payload, source); break; case OP_ACC_INT_PACKED: case OP_ACC_DBL_PACKED: case OP_ACC_FLT_PACKED: case OP_ACC_CPL_PACKED: case OP_ACC_DCP_PACKED: case OP_ACC_LNG_PACKED: _acc_packed_handler(header, payload, source); break; case OP_ACC_INT_IOV: case OP_ACC_DBL_IOV: case OP_ACC_FLT_IOV: case OP_ACC_CPL_IOV: case OP_ACC_DCP_IOV: case OP_ACC_LNG_IOV: _acc_iov_handler(header, payload, source); break; case OP_FENCE: _fence_handler(header, source); break; case OP_FETCH_AND_ADD: _fetch_and_add_handler(header, payload, source); break; case OP_SWAP: _swap_handler(header, payload, source); break; case OP_CREATE_MUTEXES: _mutex_create_handler(header, source); break; case OP_DESTROY_MUTEXES: _mutex_destroy_handler(header, source); break; case OP_LOCK: _lock_handler(header, source); break; case OP_UNLOCK: _unlock_handler(header, source); break; case OP_QUIT: running = 0; break; case OP_MALLOC: _malloc_handler(header, payload, source); break; case OP_FREE: _free_handler(header, payload, source); break; default: fprintf(stderr, "[%d] header operation not recognized: %d\n", g_state.rank, header->operation); CMX_ASSERT(0); } } initialized = 0; free(static_header_buffer); free(static_server_buffer); _free_semaphore(); free(mutexes); free(lq_heads); ierr = MPI_Barrier(g_state.comm); _translate_mpi_error(ierr, "_progress_server:MPI_Barrier"); /* reg_cache */ reg_cache_destroy(); // destroy the communicators #if DEBUG fprintf(stderr, "[%d] before cmx_group_finalize()\n", g_state.rank); #endif cmx_group_finalize(); #if DEBUG fprintf(stderr, "[%d] after cmx_group_finalize()\n", g_state.rank); #endif #if DEBUG_TO_FILE fclose(cmx_trace_file); #endif // assume this is the end of a user's application MPI_Finalize(); exit(EXIT_SUCCESS); } STATIC void _put_handler(header_t *header, char *payload, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int use_eager = _eager_check(header->length); #if DEBUG fprintf(stderr, "[%d] _put_handler rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory( reg_entry, header->remote_address); if (use_eager) { (void)memcpy(mapped_offset, payload, header->length); } else { char *buf = (char*)mapped_offset; int bytes_remaining = header->length; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; server_recv(buf, size, proc); buf += size; bytes_remaining -= size; } while (bytes_remaining > 0); } } STATIC void _put_packed_handler(header_t *header, char *payload, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; char *packed_buffer = NULL; stride_t *stride = NULL; int use_eager = _eager_check(sizeof(stride_t)+header->length); #if DEBUG int i=0; #endif #if DEBUG fprintf(stderr, "[%d] _put_packed_handler rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif stride = (stride_t*)payload; CMX_ASSERT(stride->stride_levels >= 0); CMX_ASSERT(stride->stride_levels < CMX_MAX_STRIDE_LEVEL); #if DEBUG fprintf(stderr, "[%d] _put_packed_handler stride_levels=%d, count[0]=%d\n", g_state.rank, stride->stride_levels, stride->count[0]); for (i=0; istride_levels; ++i) { fprintf(stderr, "[%d] stride[%d]=%d count[%d+1]=%d\n", g_state.rank, i, stride->stride[i], i, stride->count[i+1]); } #endif reg_entry = reg_cache_find( header->rank, header->remote_address, stride->count[0]); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory( reg_entry, header->remote_address); if (use_eager) { packed_buffer = payload+sizeof(stride_t); unpack(packed_buffer, mapped_offset, stride->stride, stride->count, stride->stride_levels); } else { if ((unsigned)header->length > static_server_buffer_size) { packed_buffer = malloc(header->length); } else { packed_buffer = static_server_buffer; } { /* we receive the buffer backwards */ char *buf = packed_buffer + header->length; int bytes_remaining = header->length; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; buf -= size; server_recv(buf, size, proc); bytes_remaining -= size; } while (bytes_remaining > 0); } unpack(packed_buffer, mapped_offset, stride->stride, stride->count, stride->stride_levels); if ((unsigned)header->length > static_server_buffer_size) { free(packed_buffer); } } } STATIC void _put_datatype_handler(header_t *header, char *payload, int proc) { MPI_Datatype dst_type; reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; stride_t *stride = NULL; int ierr; #if DEBUG int i=0; #endif #if DEBUG fprintf(stderr, "[%d] _put_datatype_handler rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif stride = (stride_t*)payload; CMX_ASSERT(stride); CMX_ASSERT(stride->stride_levels >= 0); CMX_ASSERT(stride->stride_levels < CMX_MAX_STRIDE_LEVEL); #if DEBUG fprintf(stderr, "[%d] _put_datatype_handler stride_levels=%d, count[0]=%d\n", g_state.rank, stride->stride_levels, stride->count[0]); for (i=0; istride_levels; ++i) { fprintf(stderr, "[%d] stride[%d]=%d count[%d+1]=%d\n", g_state.rank, i, stride->stride[i], i, stride->count[i+1]); } #endif reg_entry = reg_cache_find( header->rank, header->remote_address, stride->count[0]); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory( reg_entry, header->remote_address); strided_to_subarray_dtype(stride->stride, stride->count, stride->stride_levels, MPI_BYTE, &dst_type); ierr = MPI_Type_commit(&dst_type); _translate_mpi_error(ierr,"_put_datatype_handler:MPI_Type_commit"); server_recv_datatype(mapped_offset, dst_type, proc); ierr = MPI_Type_free(&dst_type); _translate_mpi_error(ierr,"_put_datatype_handler:MPI_Type_free"); } STATIC void _put_iov_handler(header_t *header, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int i = 0; char *packed_buffer = NULL; int packed_index = 0; char *iov_buf = NULL; int iov_off = 0; int limit = 0; int bytes = 0; void **src = NULL; void **dst = NULL; #if DEBUG fprintf(stderr, "[%d] _put_iov_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG fprintf(stderr, "[%d] _put_iov_handler header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_PUT_IOV == header->operation); iov_buf = malloc(header->length); CMX_ASSERT(iov_buf); server_recv(iov_buf, header->length, proc); limit = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); CMX_ASSERT(limit > 0); bytes = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); CMX_ASSERT(bytes > 0); src = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; dst = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; CMX_ASSERT(iov_off == header->length); #if DEBUG fprintf(stderr, "[%d] _put_iov_handler limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif if ((unsigned)(bytes*limit) > static_server_buffer_size) { packed_buffer = malloc(bytes*limit); CMX_ASSERT(packed_buffer); } else { packed_buffer = static_server_buffer; } server_recv(packed_buffer, bytes * limit, proc); packed_index = 0; for (i=0; irank, dst[i], bytes); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory( reg_entry, dst[i]); (void)memcpy(mapped_offset, &packed_buffer[packed_index], bytes); packed_index += bytes; } CMX_ASSERT(packed_index == bytes*limit); if ((unsigned)(bytes*limit) > static_server_buffer_size) { free(packed_buffer); } free(iov_buf); } STATIC void _get_handler(header_t *header, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; #if DEBUG fprintf(stderr, "[%d] _get_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG fprintf(stderr, "[%d] header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif CMX_ASSERT(OP_GET == header->operation); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); { char *buf = (char*)mapped_offset; int bytes_remaining = header->length; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; server_send(buf, size, proc); buf += size; bytes_remaining -= size; } while (bytes_remaining > 0); } } STATIC void _get_packed_handler(header_t *header, char *payload, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; char *packed_buffer = NULL; int packed_index = 0; stride_t *stride_src = (stride_t*)payload; #if DEBUG fprintf(stderr, "[%d] _get_packed_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG fprintf(stderr, "[%d] header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_GET_PACKED == header->operation); CMX_ASSERT(stride_src->stride_levels >= 0); CMX_ASSERT(stride_src->stride_levels < CMX_MAX_STRIDE_LEVEL); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); packed_buffer = pack(mapped_offset, stride_src->stride, stride_src->count, stride_src->stride_levels, &packed_index); { /* we send the buffer backwards */ char *buf = packed_buffer + packed_index; int bytes_remaining = packed_index; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; buf -= size; server_send(buf, size, proc); bytes_remaining -= size; } while (bytes_remaining > 0); } free(packed_buffer); } STATIC void _get_datatype_handler(header_t *header, char *payload, int proc) { MPI_Datatype src_type; reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; stride_t *stride_src = NULL; int ierr; #if DEBUG int i; fprintf(stderr, "[%d] _get_datatype_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG fprintf(stderr, "[%d] header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_GET_DATATYPE == header->operation); stride_src = (stride_t*)payload; CMX_ASSERT(stride_src); CMX_ASSERT(stride_src->stride_levels >= 0); CMX_ASSERT(stride_src->stride_levels < CMX_MAX_STRIDE_LEVEL); #if DEBUG for (i=0; istride_levels; ++i) { fprintf(stderr, "\tstride[%d]=%d\n", i, stride_src->stride[i]); } for (i=0; istride_levels+1; ++i) { fprintf(stderr, "\tcount[%d]=%d\n", i, stride_src->count[i]); } #endif reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); strided_to_subarray_dtype(stride_src->stride, stride_src->count, stride_src->stride_levels, MPI_BYTE, &src_type); ierr = MPI_Type_commit(&src_type); _translate_mpi_error(ierr,"_get_datatype_handler:MPI_Type_commit"); server_send_datatype(mapped_offset, src_type, proc); ierr = MPI_Type_free(&src_type); _translate_mpi_error(ierr,"_get_datatype_handler:MPI_Type_free"); } STATIC void _get_iov_handler(header_t *header, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int i = 0; char *packed_buffer = NULL; int packed_index = 0; char *iov_buf = NULL; int iov_off = 0; int limit = 0; int bytes = 0; void **src = NULL; void **dst = NULL; #if DEBUG fprintf(stderr, "[%d] _get_iov_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG fprintf(stderr, "[%d] _get_iov_handler header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_GET_IOV == header->operation); iov_buf = malloc(header->length); CMX_ASSERT(iov_buf); server_recv(iov_buf, header->length, proc); limit = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); CMX_ASSERT(limit > 0); bytes = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); CMX_ASSERT(bytes > 0); src = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; dst = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; CMX_ASSERT(iov_off == header->length); #if DEBUG fprintf(stderr, "[%d] _get_iov_handler limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif if ((unsigned)(bytes*limit) > static_server_buffer_size) { packed_buffer = malloc(bytes*limit); CMX_ASSERT(packed_buffer); } else { packed_buffer = static_server_buffer; } packed_index = 0; for (i=0; irank, src[i], bytes); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, src[i]); (void)memcpy(&packed_buffer[packed_index], mapped_offset, bytes); packed_index += bytes; } CMX_ASSERT(packed_index == bytes*limit); server_send(packed_buffer, packed_index, proc); if ((unsigned)(bytes*limit) > static_server_buffer_size) { free(packed_buffer); } free(iov_buf); } STATIC void _acc_handler(header_t *header, char *scale, int proc) { int sizeof_scale = 0; int acc_type = 0; reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; char *acc_buffer = NULL; int use_eager = 0; #if DEBUG fprintf(stderr, "[%d] _acc_handler\n", g_state.rank); #endif switch (header->operation) { case OP_ACC_INT: acc_type = CMX_ACC_INT; sizeof_scale = sizeof(int); break; case OP_ACC_DBL: acc_type = CMX_ACC_DBL; sizeof_scale = sizeof(double); break; case OP_ACC_FLT: acc_type = CMX_ACC_FLT; sizeof_scale = sizeof(float); break; case OP_ACC_LNG: acc_type = CMX_ACC_LNG; sizeof_scale = sizeof(long); break; case OP_ACC_CPL: acc_type = CMX_ACC_CPL; sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP: acc_type = CMX_ACC_DCP; sizeof_scale = sizeof(DoubleComplex); break; default: CMX_ASSERT(0); } use_eager = _eager_check(sizeof_scale+header->length); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); if (use_eager) { acc_buffer = scale + sizeof_scale; } else { if ((unsigned)header->length > static_server_buffer_size) { acc_buffer = malloc(header->length); } else { acc_buffer = static_server_buffer; } { char *buf = (char*)acc_buffer; int bytes_remaining = header->length; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; server_recv(buf, size, proc); buf += size; bytes_remaining -= size; } while (bytes_remaining > 0); } } if (CMX_ENABLE_ACC_SELF || CMX_ENABLE_ACC_SMP) { sem_wait(semaphores[header->rank]); _acc(acc_type, header->length, mapped_offset, acc_buffer, scale); sem_post(semaphores[header->rank]); } else { _acc(acc_type, header->length, mapped_offset, acc_buffer, scale); } if (use_eager) { } else { if ((unsigned)header->length > static_server_buffer_size) { free(acc_buffer); } } } STATIC void _acc_packed_handler(header_t *header, char *payload, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; void *scale = NULL; int sizeof_scale = 0; int acc_type = 0; char *acc_buffer = NULL; stride_t *stride = NULL; int use_eager = 0; #if DEBUG fprintf(stderr, "[%d] _acc_packed_handler\n", g_state.rank); #endif switch (header->operation) { case OP_ACC_INT_PACKED: acc_type = CMX_ACC_INT; sizeof_scale = sizeof(int); break; case OP_ACC_DBL_PACKED: acc_type = CMX_ACC_DBL; sizeof_scale = sizeof(double); break; case OP_ACC_FLT_PACKED: acc_type = CMX_ACC_FLT; sizeof_scale = sizeof(float); break; case OP_ACC_LNG_PACKED: acc_type = CMX_ACC_LNG; sizeof_scale = sizeof(long); break; case OP_ACC_CPL_PACKED: acc_type = CMX_ACC_CPL; sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP_PACKED: acc_type = CMX_ACC_DCP; sizeof_scale = sizeof(DoubleComplex); break; default: CMX_ASSERT(0); } use_eager = _eager_check(sizeof_scale+sizeof(stride_t)+header->length); scale = payload; stride = (stride_t*)(payload + sizeof_scale); if (use_eager) { acc_buffer = payload+sizeof_scale+sizeof(stride_t); } else { if ((unsigned)header->length > static_server_buffer_size) { acc_buffer = malloc(header->length); } else { acc_buffer = static_server_buffer; } { /* we receive the buffer backwards */ char *buf = acc_buffer + header->length; int bytes_remaining = header->length; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; buf -= size; server_recv(buf, size, proc); bytes_remaining -= size; } while (bytes_remaining > 0); } } reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); if (CMX_ENABLE_ACC_SELF || CMX_ENABLE_ACC_SMP) { sem_wait(semaphores[header->rank]); } { char *packed_buffer = acc_buffer; char *dst = mapped_offset; int *dst_stride = stride->stride; int *count = stride->count; int stride_levels = stride->stride_levels; int i, j; long dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int dst_bvalue[7], dst_bunit[7]; int packed_index = 0; CMX_ASSERT(stride_levels >= 0); CMX_ASSERT(stride_levels < CMX_MAX_STRIDE_LEVEL); CMX_ASSERT(NULL != packed_buffer); CMX_ASSERT(NULL != dst); CMX_ASSERT(NULL != dst_stride); CMX_ASSERT(NULL != count); CMX_ASSERT(count[0] > 0); #if DEBUG fprintf(stderr, "[%d] unpack(dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, dst, dst_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { dst_bvalue[i] = 0; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { dst_bvalue[j] = 0; } } _acc(acc_type, count[0], &dst[dst_idx], &packed_buffer[packed_index], scale); packed_index += count[0]; } CMX_ASSERT(packed_index == n1dim*count[0]); } if (CMX_ENABLE_ACC_SELF || CMX_ENABLE_ACC_SMP) { sem_post(semaphores[header->rank]); } if (use_eager) { } else { if ((unsigned)header->length > static_server_buffer_size) { free(acc_buffer); } } } STATIC void _acc_iov_handler(header_t *header, char *scale, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int i = 0; char *packed_buffer = NULL; int packed_index = 0; char *iov_buf = NULL; int iov_off = 0; int limit = 0; int bytes = 0; void **src = NULL; void **dst = NULL; int sizeof_scale = 0; int acc_type = 0; #if DEBUG fprintf(stderr, "[%d] _acc_iov_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG fprintf(stderr, "[%d] _acc_iov_handler header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif #if DEBUG fprintf(stderr, "[%d] _acc_iov_handler limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif switch (header->operation) { case OP_ACC_INT_IOV: acc_type = CMX_ACC_INT; sizeof_scale = sizeof(int); break; case OP_ACC_DBL_IOV: acc_type = CMX_ACC_DBL; sizeof_scale = sizeof(double); break; case OP_ACC_FLT_IOV: acc_type = CMX_ACC_FLT; sizeof_scale = sizeof(float); break; case OP_ACC_LNG_IOV: acc_type = CMX_ACC_LNG; sizeof_scale = sizeof(long); break; case OP_ACC_CPL_IOV: acc_type = CMX_ACC_CPL; sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP_IOV: acc_type = CMX_ACC_DCP; sizeof_scale = sizeof(DoubleComplex); break; default: CMX_ASSERT(0); } iov_buf = malloc(header->length); CMX_ASSERT(iov_buf); server_recv(iov_buf, header->length, proc); limit = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); CMX_ASSERT(limit > 0); bytes = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); CMX_ASSERT(bytes > 0); src = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; dst = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; CMX_ASSERT(iov_off == header->length); if ((unsigned)(bytes*limit) > static_server_buffer_size) { packed_buffer = malloc(bytes*limit); } else { packed_buffer = static_server_buffer; } server_recv(packed_buffer, bytes*limit, proc); if (CMX_ENABLE_ACC_SELF || CMX_ENABLE_ACC_SMP) { sem_wait(semaphores[header->rank]); } packed_index = 0; for (i=0; irank, dst[i], bytes); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, dst[i]); _acc(acc_type, bytes, mapped_offset, &packed_buffer[packed_index], scale); packed_index += bytes; } CMX_ASSERT(packed_index == bytes*limit); if (CMX_ENABLE_ACC_SELF || CMX_ENABLE_ACC_SMP) { sem_post(semaphores[header->rank]); } if ((unsigned)(bytes*limit) > static_server_buffer_size) { free(packed_buffer); } free(iov_buf); } STATIC void _fence_handler(header_t *header, int proc) { #if DEBUG fprintf(stderr, "[%d] _fence_handler proc=%d\n", g_state.rank, proc); #endif /* preconditions */ CMX_ASSERT(header); #if NEED_ASM_VOLATILE_MEMORY #if DEBUG fprintf(stderr, "[%d] _fence_handler asm volatile (\"\" : : : \"memory\"); \n", g_state.rank); #endif asm volatile ("" : : : "memory"); #endif /* we send the ack back to the originating proc */ server_send(NULL, 0, proc); } STATIC void _fetch_and_add_handler(header_t *header, char *payload, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int *value_int = NULL; long *value_long = NULL; #if DEBUG fprintf(stderr, "[%d] _fetch_and_add_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG fprintf(stderr, "[%d] header rem=%p loc=%p rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif CMX_ASSERT(OP_FETCH_AND_ADD == header->operation); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); if (sizeof(int) == header->length) { value_int = malloc(sizeof(int)); *value_int = *((int*)mapped_offset); /* "fetch" */ *((int*)mapped_offset) += *((int*)payload); /* "add" */ server_send(value_int, sizeof(int), proc); free(value_int); } else if (sizeof(long) == header->length) { value_long = malloc(sizeof(long)); *value_long = *((long*)mapped_offset); /* "fetch" */ *((long*)mapped_offset) += *((long*)payload); /* "add" */ server_send(value_long, sizeof(long), proc); free(value_long); } else { CMX_ASSERT(0); } } STATIC void _swap_handler(header_t *header, char *payload, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int *value_int = NULL; long *value_long = NULL; #if DEBUG fprintf(stderr, "[%d] _swap_handler rem=%p loc=%p rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif CMX_ASSERT(OP_SWAP == header->operation); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); if (sizeof(int) == header->length) { value_int = malloc(sizeof(int)); *value_int = *((int*)mapped_offset); /* "fetch" */ *((int*)mapped_offset) = *((int*)payload); /* "swap" */ server_send(value_int, sizeof(int), proc); free(value_int); } else if (sizeof(long) == header->length) { value_long = malloc(sizeof(long)); *value_long = *((long*)mapped_offset); /* "fetch" */ *((long*)mapped_offset) = *((long*)payload); /* "swap" */ server_send(value_long, sizeof(long), proc); free(value_long); } else { CMX_ASSERT(0); } } STATIC void _mutex_create_handler(header_t *header, int proc) { int i; int num = header->length; #if DEBUG fprintf(stderr, "[%d] _mutex_create_handler proc=%d num=%d\n", g_state.rank, proc, num); #endif mutexes[proc] = (int*)malloc(sizeof(int) * num); lq_heads[proc] = (lock_t**)malloc(sizeof(lock_t*) * num); for (i=0; ilength; #if DEBUG fprintf(stderr, "[%d] _mutex_destroy_handler proc=%d\n", g_state.rank, proc); #endif for (i=0; ilength; int rank = header->rank; #if DEBUG fprintf(stderr, "[%d] _lock_handler id=%d in rank=%d req by proc=%d\n", g_state.rank, id, rank, proc); #endif CMX_ASSERT(0 <= id); if (UNLOCKED == mutexes[rank][id]) { mutexes[rank][id] = proc; server_send(&id, sizeof(int), proc); } else { lock_t *lock = NULL; #if DEBUG fprintf(stderr, "[%d] _lq_push rank=%d req_by=%d id=%d\n", g_state.rank, rank, proc, id); #endif lock = malloc(sizeof(lock_t)); lock->next = NULL; lock->rank = proc; if (lq_heads[rank][id]) { /* insert at tail */ lock_t *lq = lq_heads[rank][id]; while (lq->next) { lq = lq->next; } lq->next = lock; } else { /* new head */ lq_heads[rank][id] = lock; } } } STATIC void _unlock_handler(header_t *header, int proc) { int id = header->length; int rank = header->rank; #if DEBUG fprintf(stderr, "[%d] _unlock_handler id=%d in rank=%d req by proc=%d\n", g_state.rank, id, rank, proc); #endif CMX_ASSERT(0 <= id); if (lq_heads[rank][id]) { /* a lock requester was queued */ /* find the next lock request and update queue */ lock_t *lock = lq_heads[rank][id]; lq_heads[rank][id] = lq_heads[rank][id]->next; /* update lock */ mutexes[rank][id] = lock->rank; /* notify next in line */ server_send(&id, sizeof(int), lock->rank); free(lock); } else { /* no enqued request */ mutexes[rank][id] = UNLOCKED; } } STATIC void _malloc_handler( header_t *header, char *payload, int proc) { int i; int n; reg_entry_t *reg_entries = (reg_entry_t*)payload; #if DEBUG fprintf(stderr, "[%d] _malloc_handler proc=%d\n", g_state.rank, proc); #endif CMX_ASSERT(header); CMX_ASSERT(header->operation == OP_MALLOC); n = header->length; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] _malloc_handler preconditions complete\n", g_state.rank); #endif /* insert reg entries into local registration cache */ for (i=0; ilength; rank_ptr_t *rank_ptrs = (rank_ptr_t*)payload; #if DEBUG fprintf(stderr, "[%d] _free_handler proc=%d\n", g_state.rank, proc); #endif /* remove all pointers from registration cache */ for (i=0; iuse_dev) { sicm_free(reg_entry->mapped); retval = 0; } else { retval = munmap(reg_entry->mapped, reg_entry->len); } #else retval = munmap(reg_entry->mapped, reg_entry->len); #endif if (-1 == retval) { perror("_free_handler: munmap"); cmx_error("_free_handler: munmap", retval); } #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] _free_handler unmapped mapped memory in reg entry\n", g_state.rank); #endif reg_cache_delete(rank_ptrs[i].rank, rank_ptrs[i].ptr); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] _free_handler deleted reg cache entry\n", g_state.rank); #endif } else { #if 0 reg_cache_delete(rank_ptrs[i].rank, rank_ptrs[i].ptr); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] _free_handler deleted reg cache entry\n", g_state.rank); #endif #endif } } server_send(NULL, 0, proc); /* ack */ } STATIC void* _get_offset_memory(reg_entry_t *reg_entry, void *memory) { ptrdiff_t offset = 0; CMX_ASSERT(reg_entry); #if DEBUG_VERBOSE fprintf(stderr, "[%d] _get_offset_memory reg_entry->buf=%p memory=%p\n", g_state.rank, reg_entry->buf, memory); #endif offset = ((char*)memory) - ((char*)reg_entry->buf); #if DEBUG_VERBOSE fprintf(stderr, "[%d] _get_offset_memory ptrdiff=%lu\n", g_state.rank, (unsigned long)offset); #endif return (void*)((char*)(reg_entry->mapped)+offset); } STATIC int _is_master(void) { return (g_state.master[g_state.rank] == g_state.rank); } STATIC int _get_world_rank(cmx_igroup_t *igroup, int rank) { #if 0 int world_rank; int status; status = MPI_Group_translate_ranks(igroup->group, 1, &rank, g_state.group, &world_rank); CHECK_MPI_RETVAL(status); CMX_ASSERT(MPI_PROC_NULL != world_rank); return world_rank; #else return igroup->world_ranks[rank]; #endif } /* gets (in group order) corresponding world ranks for entire group */ STATIC int* _get_world_ranks(cmx_igroup_t *igroup) { #if 0 int i = 0; int *group_ranks = (int*)malloc(sizeof(int)*igroup->size); int *world_ranks = (int*)malloc(sizeof(int)*igroup->size); int status; for (i=0; isize; ++i) { group_ranks[i] = i; world_ranks[i] = MPI_PROC_NULL; } status = MPI_Group_translate_ranks( igroup->group, igroup->size, group_ranks, g_state.group, world_ranks); CMX_ASSERT(MPI_SUCCESS == status); for (i=0; isize; ++i) { CMX_ASSERT(MPI_PROC_NULL != world_ranks[i]); } free(group_ranks); return world_ranks; #else #if 0 MPI_Comm comm = igroup->comm; int i = 0; int my_world_rank = g_state.rank; int *world_ranks = (int*)malloc(sizeof(int)*igroup->size); int status; for (i=0; isize; ++i) { world_ranks[i] = MPI_PROC_NULL; } status = MPI_Allgather(&my_world_rank,1,MPI_INT,world_ranks, 1,MPI_INT,comm); CMX_ASSERT(MPI_SUCCESS == status); for (i=0; isize; ++i) { CMX_ASSERT(MPI_PROC_NULL != world_ranks[i]); } return world_ranks; #else int size = igroup->size; int i = 0; int *world_ranks = (int*)malloc(sizeof(int)*size); for (i=0; iworld_ranks[i]; } return world_ranks; #endif #endif } /* we sometimes need to notify a node master of some event and the rank in * charge of doing that is returned by this function */ STATIC int _smallest_world_rank_with_same_hostid(cmx_igroup_t *igroup) { int i = 0; int smallest = g_state.rank; int *world_ranks = _get_world_ranks(igroup); for (i=0; isize; ++i) { if (g_state.hostid[world_ranks[i]] == g_state.hostid[g_state.rank]) { /* found same host as me */ if (world_ranks[i] < smallest) { smallest = world_ranks[i]; } } } free(world_ranks); return smallest; } /* we sometimes need to notify a node master of some event and the rank in * charge of doing that is returned by this function */ STATIC int _largest_world_rank_with_same_hostid(cmx_igroup_t *igroup) { int i = 0; int largest = g_state.rank; int *world_ranks = _get_world_ranks(igroup); for (i=0; isize; ++i) { if (g_state.hostid[world_ranks[i]] == g_state.hostid[g_state.rank]) { /* found same host as me */ if (world_ranks[i] > largest) { largest = world_ranks[i]; } } } free(world_ranks); return largest; } STATIC void* _shm_create(const char *name, size_t size) { void *mapped = NULL; int fd = 0; int retval = 0; #if DEBUG fprintf(stderr, "[%d] _shm_create(%s, %lu)\n", g_state.rank, name, (unsigned long)size); #endif /* create shared memory segment */ fd = shm_open(name, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); if (-1 == fd && EEXIST == errno) { retval = shm_unlink(name); if (-1 == retval) { perror("_shm_create: shm_unlink"); cmx_error("_shm_create: shm_unlink", retval); } } /* try a second time */ if (-1 == fd) { fd = shm_open(name, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); } /* finally report error if needed */ if (-1 == fd) { perror("_shm_create: shm_open"); cmx_error("_shm_create: shm_open", fd); } /* set the size of my shared memory object */ retval = ftruncate(fd, size); if (-1 == retval) { perror("_shm_create: ftruncate"); cmx_error("_shm_create: ftruncate", retval); } /* map into local address space */ mapped = _shm_map(fd, size); /* close file descriptor */ retval = close(fd); if (-1 == retval) { perror("_shm_create: close"); cmx_error("_shm_create: close", -1); } return mapped; } #if USE_SICM #if SICM_OLD STATIC void* _shm_create_memdev(const char *name, size_t size, sicm_device* device) #else STATIC void* _shm_create_memdev(const char *name, size_t size, sicm_device_list device) #endif { void *mapped = NULL; int fd = 0; int retval = 0; #if DEBUG fprintf(stderr, "[%d] _shm_create_memdev(%s, %lu)\n", g_state.rank, name, (unsigned long)size); #endif /* create shared memory segment */ fd = shm_open(name, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); if (-1 == fd && EEXIST == errno) { retval = shm_unlink(name); if (-1 == retval) { perror("_shm_create_memdev: shm_unlink"); cmx_error("_shm_create_memdev: shm_unlink", retval); } } /* try a second time */ if (-1 == fd) { fd = shm_open(name, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); } /* finally report error if needed */ if (-1 == fd) { perror("_shm_create_memdev: shm_open"); cmx_error("_shm_create_memdev: shm_open", fd); } /* the file will be used for arena allocation, * so it should not be truncated here */ #if SICM_OLD sicm_arena arena = sicm_arena_create_mmapped(0, device, fd, 0, -1, 0); #else sicm_arena arena = sicm_arena_create_mmapped(0, 0, &device, fd, 0, -1, 0); #endif /* map into local address space */ mapped = _shm_map_arena(fd, size, arena); /* close file descriptor */ retval = close(fd); if (-1 == retval) { perror("_shm_create_memdev: close"); cmx_error("_shm_create_memdev: close", -1); } return mapped; } #endif STATIC void* _shm_attach(const char *name, size_t size) { void *mapped = NULL; int fd = 0; int retval = 0; #if DEBUG fprintf(stderr, "[%d] _shm_attach(%s, %lu)\n", g_state.rank, name, (unsigned long)size); #endif /* attach to shared memory segment */ fd = shm_open(name, O_RDWR, S_IRUSR|S_IWUSR); if (-1 == fd) { perror("_shm_attach: shm_open"); cmx_error("_shm_attach: shm_open", -1); } /* map into local address space */ mapped = _shm_map(fd, size); /* close file descriptor */ retval = close(fd); if (-1 == retval) { perror("_shm_attach: close"); cmx_error("_shm_attach: close", -1); } return mapped; } #if USE_SICM #if SICM_OLD STATIC void* _shm_attach_memdev(const char *name, size_t size, sicm_device *device) #else STATIC void* _shm_attach_memdev(const char *name, size_t size, sicm_device_list device) #endif { void *mapped = NULL; int fd = 0; int retval = 0; #if DEBUG fprintf(stderr, "[%d] _shm_attach_memdev(%s, %lu)\n", g_state.rank, name, (unsigned long)size); #endif /* attach to shared memory segment */ fd = shm_open(name, O_RDWR, S_IRUSR|S_IWUSR); if (-1 == fd) { perror("_shm_attach_memdev: shm_open"); cmx_error("_shm_attach_memdev: shm_open", -1); } #if SICM_OLD sicm_arena arena = sicm_arena_create_mmapped(0, device, fd, 0, -1, 0); #else sicm_arena arena = sicm_arena_create_mmapped(0, 0, &device, fd, 0, -1, 0); #endif /* map into local address space */ mapped = _shm_map_arena(fd, size, arena); /* close file descriptor */ retval = close(fd); if (-1 == retval) { perror("_shm_attach_memdev: close"); cmx_error("_shm_attach_memdev: close", -1); } return mapped; } #endif #if USE_SICM STATIC void* _shm_map_arena(int fd, size_t size, sicm_arena arena) { void *memory = sicm_arena_alloc(arena, size); if (NULL == memory) { perror("_shm_map_arena: mmap"); cmx_error("_shm_map_arena: mmap", -1); } return memory; } #endif STATIC void* _shm_map(int fd, size_t size) { void *memory = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (MAP_FAILED == memory) { perror("_shm_map: mmap"); cmx_error("_shm_map: mmap", -1); } return memory; } STATIC int _set_affinity(int cpu) { int status = 0; #if CMX_SET_AFFINITY #if HAVE_PTHREAD_SETAFFINITY_NP || HAVE_SCHED_SETAFFINITY cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); #if HAVE_PTHREAD_SETAFFINITY_NP status = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); if (0 != status) { perror("pthread_setaffinity_np"); } #elif HAVE_SCHED_SETAFFINITY status = sched_setaffinity(getpid(), sizeof(cpu_set_t), &cpuset); if (0 != status) { perror("sched_setaffinity"); } #endif #endif #endif return status; } STATIC void check_mpi_retval(int retval, const char *file, int line) { if (MPI_SUCCESS != retval) { const char *msg = str_mpi_retval(retval); fprintf(stderr, "{%d} MPI Error: %s: line %d: %s\n", g_state.rank, file, line, msg); MPI_Abort(g_state.comm, retval); } } STATIC const char *str_mpi_retval(int retval) { const char *msg = NULL; if (retval == MPI_SUCCESS ) { msg = "MPI_SUCCESS"; } else if (retval == MPI_ERR_BUFFER ) { msg = "MPI_ERR_BUFFER"; } else if (retval == MPI_ERR_COUNT ) { msg = "MPI_ERR_COUNT"; } else if (retval == MPI_ERR_TYPE ) { msg = "MPI_ERR_TYPE"; } else if (retval == MPI_ERR_TAG ) { msg = "MPI_ERR_TAG"; } else if (retval == MPI_ERR_COMM ) { msg = "MPI_ERR_COMM"; } else if (retval == MPI_ERR_RANK ) { msg = "MPI_ERR_RANK"; } else if (retval == MPI_ERR_ROOT ) { msg = "MPI_ERR_ROOT"; } else if (retval == MPI_ERR_GROUP ) { msg = "MPI_ERR_GROUP"; } else if (retval == MPI_ERR_OP ) { msg = "MPI_ERR_OP"; } else if (retval == MPI_ERR_TOPOLOGY ) { msg = "MPI_ERR_TOPOLOGY"; } else if (retval == MPI_ERR_DIMS ) { msg = "MPI_ERR_DIMS"; } else if (retval == MPI_ERR_ARG ) { msg = "MPI_ERR_ARG"; } else if (retval == MPI_ERR_UNKNOWN ) { msg = "MPI_ERR_UNKNOWN"; } else if (retval == MPI_ERR_TRUNCATE ) { msg = "MPI_ERR_TRUNCATE"; } else if (retval == MPI_ERR_OTHER ) { msg = "MPI_ERR_OTHER"; } else if (retval == MPI_ERR_INTERN ) { msg = "MPI_ERR_INTERN"; } else if (retval == MPI_ERR_IN_STATUS) { msg = "MPI_ERR_IN_STATUS"; } else if (retval == MPI_ERR_PENDING ) { msg = "MPI_ERR_PENDING"; } else if (retval == MPI_ERR_REQUEST ) { msg = "MPI_ERR_REQUEST"; } else if (retval == MPI_ERR_LASTCODE ) { msg = "MPI_ERR_LASTCODE"; } else { msg = "DEFAULT"; } return msg; } STATIC void server_send(void *buf, int count, int dest) { int retval = 0; #if DEBUG fprintf(stderr, "[%d] server_send(buf=%p, count=%d, dest=%d)\n", g_state.rank, buf, count, dest); #endif retval = MPI_Send(buf, count, MPI_CHAR, dest, CMX_TAG, g_state.comm); _translate_mpi_error(retval,"server_send:MPI_Send"); CHECK_MPI_RETVAL(retval); } STATIC void server_send_datatype(void *buf, MPI_Datatype dt, int dest) { int retval = 0; #if DEBUG fprintf(stderr, "[%d] server_send_datatype(buf=%p, ..., dest=%d)\n", g_state.rank, buf, dest); #endif retval = MPI_Send(buf, 1, dt, dest, CMX_TAG, g_state.comm); _translate_mpi_error(retval,"server_send_datatype:MPI_Send"); CHECK_MPI_RETVAL(retval); } STATIC void server_recv(void *buf, int count, int source) { int retval = 0; MPI_Status status; int recv_count = 0; retval = MPI_Recv(buf, count, MPI_CHAR, source, CMX_TAG, g_state.comm, &status); _translate_mpi_error(retval,"server_recv:MPI_Recv"); CHECK_MPI_RETVAL(retval); CMX_ASSERT(status.MPI_SOURCE == source); CMX_ASSERT(status.MPI_TAG == CMX_TAG); retval = MPI_Get_count(&status, MPI_CHAR, &recv_count); _translate_mpi_error(retval,"server_recv:MPI_Get_count"); CHECK_MPI_RETVAL(retval); CMX_ASSERT(recv_count == count); } STATIC void server_recv_datatype(void *buf, MPI_Datatype dt, int source) { int retval = 0; MPI_Status status; retval = MPI_Recv(buf, 1, dt, source, CMX_TAG, g_state.comm, &status); _translate_mpi_error(retval,"server_recv_datatype:MPI_Recv"); CHECK_MPI_RETVAL(retval); CMX_ASSERT(status.MPI_SOURCE == source); CMX_ASSERT(status.MPI_TAG == CMX_TAG); } STATIC void nb_send_common(void *buf, int count, int dest, _cmx_request *nb, int need_free) { int retval = 0; message_t *message = NULL; CMX_ASSERT(NULL != nb); nb->send_size += 1; nb_count_event += 1; nb_count_send += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = need_free; message->stride = NULL; message->iov = NULL; message->datatype = MPI_DATATYPE_NULL; if (NULL == nb->send_head) { nb->send_head = message; } if (NULL != nb->send_tail) { nb->send_tail->next = message; } nb->send_tail = message; retval = MPI_Isend(buf, count, MPI_CHAR, dest, CMX_TAG, g_state.comm, &(message->request)); _translate_mpi_error(retval,"nb_send_common:MPI_Isend"); CHECK_MPI_RETVAL(retval); } STATIC void nb_send_datatype(void *buf, MPI_Datatype dt, int dest, _cmx_request *nb) { int retval = 0; message_t *message = NULL; CMX_ASSERT(NULL != nb); nb->send_size += 1; nb_count_event += 1; nb_count_send += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = 0; message->stride = NULL; message->iov = NULL; message->datatype = dt; if (NULL == nb->send_head) { nb->send_head = message; } if (NULL != nb->send_tail) { nb->send_tail->next = message; } nb->send_tail = message; retval = MPI_Isend(buf, 1, dt, dest, CMX_TAG, g_state.comm, &(message->request)); _translate_mpi_error(retval,"nb_send_datatype:MPI_Isend"); CHECK_MPI_RETVAL(retval); } STATIC void nb_send_header(void *buf, int count, int dest, _cmx_request *nb) { nb_send_common(buf, count, dest, nb, 1); } STATIC void nb_send_buffer(void *buf, int count, int dest, _cmx_request *nb) { nb_send_common(buf, count, dest, nb, 0); } STATIC void nb_recv_packed(void *buf, int count, int source, _cmx_request *nb, stride_t *stride) { int retval = 0; message_t *message = NULL; CMX_ASSERT(NULL != buf); CMX_ASSERT(count > 0); CMX_ASSERT(NULL != nb); #if DEBUG fprintf(stderr, "[%d] nb_recv_packed(buf=%p, count=%d, source=%d, nb=%p)\n", g_state.rank, buf, count, source, nb); #endif nb->recv_size += 1; nb_count_event += 1; nb_count_recv += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = 1; message->stride = stride; message->iov = NULL; message->datatype = MPI_DATATYPE_NULL; if (NULL == nb->recv_head) { nb->recv_head = message; } if (NULL != nb->recv_tail) { nb->recv_tail->next = message; } nb->recv_tail = message; retval = MPI_Irecv(buf, count, MPI_CHAR, source, CMX_TAG, g_state.comm, &(message->request)); _translate_mpi_error(retval,"nb_recv_packed:MPI_Irecv"); CHECK_MPI_RETVAL(retval); } STATIC void nb_recv_datatype(void *buf, MPI_Datatype dt, int source, _cmx_request *nb) { int retval = 0; message_t *message = NULL; CMX_ASSERT(NULL != buf); CMX_ASSERT(NULL != nb); #if DEBUG fprintf(stderr, "[%d] nb_recv_datatype(buf=%p, count=%d, source=%d, nb=%p)\n", g_state.rank, buf, count, source, nb); #endif nb->recv_size += 1; nb_count_event += 1; nb_count_recv += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = 0; message->stride = NULL; message->iov = NULL; message->datatype = dt; if (NULL == nb->recv_head) { nb->recv_head = message; } if (NULL != nb->recv_tail) { nb->recv_tail->next = message; } nb->recv_tail = message; retval = MPI_Irecv(buf, 1, dt, source, CMX_TAG, g_state.comm, &(message->request)); _translate_mpi_error(retval,"nb_recv_datatype:MPI_Irecv"); CHECK_MPI_RETVAL(retval); } STATIC void nb_recv_iov(void *buf, int count, int source, _cmx_request *nb, _cmx_giov_t *iov) { int retval = 0; message_t *message = NULL; CMX_ASSERT(NULL != nb); #if DEBUG fprintf(stderr, "[%d] nb_recv_iov(buf=%p, count=%d, source=%d, nb=%p)\n", g_state.rank, buf, count, source, nb); #endif nb->recv_size += 1; nb_count_event += 1; nb_count_recv += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = 1; message->stride = NULL; message->iov = iov; message->datatype = MPI_DATATYPE_NULL; if (NULL == nb->recv_head) { nb->recv_head = message; CMX_ASSERT(NULL == nb->recv_tail); } if (NULL != nb->recv_tail) { nb->recv_tail->next = message; } nb->recv_tail = message; retval = MPI_Irecv(buf, count, MPI_CHAR, source, CMX_TAG, g_state.comm, &(message->request)); _translate_mpi_error(retval,"nb_recv_iov:MPI_Irecv"); CHECK_MPI_RETVAL(retval); } STATIC void nb_recv(void *buf, int count, int source, _cmx_request *nb) { int retval = 0; message_t *message = NULL; CMX_ASSERT(NULL != nb); #if DEBUG fprintf(stderr, "[%d] nb_recv(buf=%p, count=%d, source=%d, nb=%p)\n", g_state.rank, buf, count, source, nb); #endif nb->recv_size += 1; nb_count_event += 1; nb_count_recv += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = NULL; message->need_free = 0; message->stride = NULL; message->iov = NULL; message->datatype = MPI_DATATYPE_NULL; if (NULL == nb->recv_head) { nb->recv_head = message; } if (NULL != nb->recv_tail) { nb->recv_tail->next = message; } nb->recv_tail = message; retval = MPI_Irecv(buf, count, MPI_CHAR, source, CMX_TAG, g_state.comm, &(message->request)); _translate_mpi_error(retval,"nb_recv:MPI_Irecv"); CHECK_MPI_RETVAL(retval); } STATIC int nb_get_handle_index() { int value = 0; if (0 == nb_index) { value = nb_max_outstanding-1; } else { value = nb_index-1; } return value; } #if 0 STATIC _cmx_request* nb_wait_for_handle() { _cmx_request *nb = NULL; int in_use_count = 0; int loop_index = nb_index; int found = 0; /* find first handle that isn't associated with a user-level handle */ /* make sure the handle we find has processed all events */ /* the user can accidentally exhaust the available handles */ #if 0 /* NOTE: it looks like this loop just forces completion of the handle * corresponding to nb_index. It should probably test all handles and if it * doesn't find a free one, then use the one at nb_index */ do { ++in_use_count; if (in_use_count > nb_max_outstanding) { fprintf(stderr, "{%d} nb_wait_for_handle Error: all user-level " "nonblocking handles have been exhausted\n", g_state.rank); MPI_Abort(g_state.comm, -1); } nb = &nb_state[nb_index]; nb_index++; nb_index %= nb_max_outstanding; /* wrap around if needed */ nb_wait_for_all(nb); } while (nb->in_use); #else /* look through list for unused handle */ do { ++in_use_count; if (in_use_count > nb_max_outstanding) { break; } nb = &nb_state[loop_index]; if (!nb->in_use) { nb_index = loop_index; found = 1; break; } loop_index++; loop_index %= nb_max_outstanding; /* wrap around if needed */ } while (nb->in_use); if (!found) { nb = &nb_state[nb_index]; nb_wait_for_all(nb); } //nb->hdl = nb_index; nb_index++; nb_index %= nb_max_outstanding; /* wrap around if needed */ /* make sure in_use flag is set to 1 */ nb->in_use = 1; #endif return nb; } #endif STATIC void nb_wait_for_send1(_cmx_request *nb) { #if DEBUG fprintf(stderr, "[%d] nb_wait_for_send1(nb=%p)\n", g_state.rank, nb); #endif CMX_ASSERT(NULL != nb); CMX_ASSERT(NULL != nb->send_head); { MPI_Status status; int retval = 0; message_t *message_to_free = NULL; retval = MPI_Wait(&(nb->send_head->request), &status); _translate_mpi_error(retval,"nb_wait_for_send1:MPI_Irecv"); CHECK_MPI_RETVAL(retval); if (nb->send_head->need_free) { free(nb->send_head->message); } if (MPI_DATATYPE_NULL != nb->send_head->datatype) { retval = MPI_Type_free(&nb->send_head->datatype); _translate_mpi_error(retval,"nb_wait_for_send1:MPI_Type_free"); CHECK_MPI_RETVAL(retval); } message_to_free = nb->send_head; nb->send_head = nb->send_head->next; free(message_to_free); CMX_ASSERT(nb->send_size > 0); nb->send_size -= 1; nb_count_send_processed += 1; nb_count_event_processed += 1; if (NULL == nb->send_head) { nb->send_tail = NULL; } } } /* returns true if operation has completed */ STATIC int nb_test_for_send1(_cmx_request *nb, message_t **save_send_head, message_t **prev) { #if DEBUG fprintf(stderr, "[%d] nb_test_for_send1(nb=%p)\n", g_state.rank, nb); #endif CMX_ASSERT(NULL != nb); CMX_ASSERT(NULL != nb->send_head); { MPI_Status status; int retval = 0; int flag; message_t *message_to_free = NULL; retval = MPI_Test(&(nb->send_head->request), &flag, &status); _translate_mpi_error(retval,"nb_test_for_send1:MPI_Test"); CHECK_MPI_RETVAL(retval); if (flag) { if (nb->send_head->need_free) { free(nb->send_head->message); } if (MPI_DATATYPE_NULL != nb->send_head->datatype) { retval = MPI_Type_free(&nb->send_head->datatype); _translate_mpi_error(retval,"nb_test_for_send1:MPI_Type_free"); CHECK_MPI_RETVAL(retval); } message_to_free = nb->send_head; if (*prev) (*prev)->next=nb->send_head->next; nb->send_head = nb->send_head->next; *save_send_head = NULL; free(message_to_free); CMX_ASSERT(nb->send_size > 0); nb->send_size -= 1; nb_count_send_processed += 1; nb_count_event_processed += 1; if (NULL == nb->send_head) { nb->send_tail = NULL; } } else { *prev = nb->send_head; *save_send_head = nb->send_head; nb->send_head = nb->send_head->next; } return flag; } } STATIC void nb_wait_for_recv1(_cmx_request *nb) { #if DEBUG fprintf(stderr, "[%d] nb_wait_for_recv1(nb=%p)\n", g_state.rank, nb); #endif CMX_ASSERT(NULL != nb); CMX_ASSERT(NULL != nb->recv_head); { MPI_Status status; int retval = 0; message_t *message_to_free = NULL; retval = MPI_Wait(&(nb->recv_head->request), &status); _translate_mpi_error(retval,"nb_wait_for_recv1:MPI_Wait"); CHECK_MPI_RETVAL(retval); if (NULL != nb->recv_head->stride) { stride_t *stride = nb->recv_head->stride; CMX_ASSERT(nb->recv_head->message); CMX_ASSERT(stride); CMX_ASSERT(stride->ptr); CMX_ASSERT(stride->stride); CMX_ASSERT(stride->count); CMX_ASSERT(stride->stride_levels); unpack(nb->recv_head->message, stride->ptr, stride->stride, stride->count, stride->stride_levels); free(stride); } if (NULL != nb->recv_head->iov) { int i = 0; char *message = nb->recv_head->message; int off = 0; _cmx_giov_t *iov = nb->recv_head->iov; for (i=0; icount; ++i) { (void)memcpy(iov->dst[i], &message[off], iov->bytes); off += iov->bytes; } free(iov->src); free(iov->dst); free(iov); } if (nb->recv_head->need_free) { free(nb->recv_head->message); } if (MPI_DATATYPE_NULL != nb->recv_head->datatype) { retval = MPI_Type_free(&nb->recv_head->datatype); _translate_mpi_error(retval,"nb_wait_for_recv1:MPI_Type_free"); CHECK_MPI_RETVAL(retval); } message_to_free = nb->recv_head; nb->recv_head = nb->recv_head->next; free(message_to_free); CMX_ASSERT(nb->recv_size > 0); nb->recv_size -= 1; nb_count_recv_processed += 1; nb_count_event_processed += 1; if (NULL == nb->recv_head) { nb->recv_tail = NULL; } } } /* returns true if operation has completed */ STATIC int nb_test_for_recv1(_cmx_request *nb, message_t **save_recv_head, message_t **prev) { #if DEBUG fprintf(stderr, "[%d] nb_wait_for_recv1(nb=%p)\n", g_state.rank, nb); #endif CMX_ASSERT(NULL != nb); CMX_ASSERT(NULL != nb->recv_head); { MPI_Status status; int retval = 0; int flag; message_t *message_to_free = NULL; retval = MPI_Test(&(nb->recv_head->request), &flag, &status); _translate_mpi_error(retval,"nb_test_for_recv1:MPI_Test"); CHECK_MPI_RETVAL(retval); if (flag) { if (NULL != nb->recv_head->stride) { stride_t *stride = nb->recv_head->stride; CMX_ASSERT(nb->recv_head->message); CMX_ASSERT(stride); CMX_ASSERT(stride->ptr); CMX_ASSERT(stride->stride); CMX_ASSERT(stride->count); CMX_ASSERT(stride->stride_levels); unpack(nb->recv_head->message, stride->ptr, stride->stride, stride->count, stride->stride_levels); free(stride); } if (NULL != nb->recv_head->iov) { int i = 0; char *message = nb->recv_head->message; int off = 0; _cmx_giov_t *iov = nb->recv_head->iov; for (i=0; icount; ++i) { (void)memcpy(iov->dst[i], &message[off], iov->bytes); off += iov->bytes; } free(iov->src); free(iov->dst); free(iov); } if (nb->recv_head->need_free) { free(nb->recv_head->message); } if (MPI_DATATYPE_NULL != nb->recv_head->datatype) { retval = MPI_Type_free(&nb->recv_head->datatype); _translate_mpi_error(retval,"nb_test_for_recv1:MPI_Type_free"); CHECK_MPI_RETVAL(retval); } message_to_free = nb->recv_head; if (*prev) (*prev)->next=nb->recv_head->next; nb->recv_head = nb->recv_head->next; *save_recv_head = NULL; free(message_to_free); CMX_ASSERT(nb->recv_size > 0); nb->recv_size -= 1; nb_count_recv_processed += 1; nb_count_event_processed += 1; if (NULL == nb->recv_head) { nb->recv_tail = NULL; } } else { *prev = nb->recv_head; *save_recv_head = nb->recv_head; nb->recv_head = nb->recv_head->next; } return flag; } } STATIC void nb_wait_for_all(_cmx_request *nb) { #if DEBUG fprintf(stderr, "[%d] nb_wait_for_all(nb=%p)\n", g_state.rank, nb); #endif int world_proc = g_state.rank; if (nb->in_use == 0) return; CMX_ASSERT(NULL != nb); /* fair processing of requests */ while (NULL != nb->send_head || NULL != nb->recv_head) { if (NULL != nb->send_head) { nb_wait_for_send1(nb); } if (NULL != nb->recv_head) { nb_wait_for_recv1(nb); } } nb->in_use = 0; } /* Returns 0 if no outstanding requests */ STATIC int nb_test_for_all(_cmx_request *nb) { #if DEBUG fprintf(stderr, "[%d] nb_test_for_all(nb=%p)\n", g_state.rank, nb); #endif int ret = 0; message_t *save_send_head = NULL; message_t *save_recv_head = NULL; message_t *tmp_send_head; message_t *tmp_recv_head; message_t *send_prev = NULL; message_t *recv_prev = NULL; /** * TODO: Determine if this condition may be true for a valid series of * operations. In particular, if a set of non-blocking operations follow a * set of blocking operations. CMX_ASSERT(NULL != nb); */ if (nb == NULL) return 0; /* check for outstanding requests */ while (NULL != nb->send_head || NULL != nb->recv_head) { if (NULL != nb->send_head) { if (!nb_test_for_send1(nb, &tmp_send_head, &send_prev)) { ret = 1; } if ((NULL == save_send_head) && (ret == 1)) { save_send_head = tmp_send_head; } } if (NULL != nb->recv_head) { if (!nb_test_for_recv1(nb, &tmp_recv_head, &recv_prev)) { ret = 1; } if ((NULL == save_recv_head) && (ret == 1)) { save_recv_head = tmp_recv_head; } } } nb->send_head = save_send_head; nb->recv_head = save_recv_head; if (ret == 0) nb->in_use = 0; return ret; } STATIC void nb_put(void *src, void *dst, int bytes, int proc, _cmx_request *nb) { CMX_ASSERT(NULL != src); CMX_ASSERT(NULL != dst); CMX_ASSERT(bytes > 0); CMX_ASSERT(proc >= 0); CMX_ASSERT(proc < g_state.size); CMX_ASSERT(NULL != nb); #if DEBUG printf("[%d] nb_put(src=%p, dst=%p, bytes=%d, proc=%d, nb=%p)\n", g_state.rank, src, dst, bytes, proc, nb); #endif if (CMX_ENABLE_PUT_SELF) { /* put to self */ if (g_state.rank == proc) { if (fence_array[g_state.master[proc]]) { _fence_master(g_state.master[proc]); } (void)memcpy(dst, src, bytes); return; } } if (CMX_ENABLE_PUT_SMP) { /* put to SMP node */ // if (g_state.hostid[proc] == g_state.hostid[g_state.rank]) if (g_state.master[proc] == g_state.master[g_state.rank]) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; if (fence_array[g_state.master[proc]]) { _fence_master(g_state.master[proc]); } reg_entry = reg_cache_find(proc, dst, bytes); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, dst); (void)memcpy(mapped_offset, src, bytes); return; } } { char *message = NULL; int message_size = 0; header_t *header = NULL; int master_rank = -1; int use_eager = _eager_check(bytes); master_rank = g_state.master[proc]; /* only fence on the master */ fence_array[master_rank] = 1; if (use_eager) { message_size = sizeof(header_t) + bytes; } else { message_size = sizeof(header_t); } message = malloc(message_size); header = (header_t*)message; header->operation = OP_PUT; MAYBE_MEMSET(header, 0, sizeof(header_t)); header->remote_address = dst; header->local_address = src; header->rank = proc; header->length = bytes; if (use_eager) { (void)memcpy(message+sizeof(header_t), src, bytes); nb_send_header(message, message_size, master_rank, nb); } else { char *buf = (char*)src; int bytes_remaining = bytes; nb_send_header(header, sizeof(header_t), master_rank, nb); do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; nb_send_buffer(buf, size, master_rank, nb); buf += size; bytes_remaining -= size; } while (bytes_remaining > 0); } } nb->in_use = 1; } STATIC void nb_get(void *src, void *dst, int bytes, int proc, _cmx_request *nb) { CMX_ASSERT(NULL != src); CMX_ASSERT(NULL != dst); CMX_ASSERT(bytes > 0); CMX_ASSERT(proc >= 0); CMX_ASSERT(proc < g_state.size); CMX_ASSERT(NULL != nb); if (CMX_ENABLE_GET_SELF) { /* get from self */ if (g_state.rank == proc) { if (fence_array[g_state.master[proc]]) { _fence_master(g_state.master[proc]); } (void)memcpy(dst, src, bytes); return; } } if (CMX_ENABLE_GET_SMP) { /* get from SMP node */ // if (g_state.hostid[proc] == g_state.hostid[g_state.rank]) if (g_state.master[proc] == g_state.master[g_state.rank]) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; if (fence_array[g_state.master[proc]]) { _fence_master(g_state.master[proc]); } reg_entry = reg_cache_find(proc, src, bytes); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, src); (void)memcpy(dst, mapped_offset, bytes); return; } } { header_t *header = NULL; int master_rank = -1; master_rank = g_state.master[proc]; header = malloc(sizeof(header_t)); CMX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_GET; header->remote_address = src; header->local_address = dst; header->rank = proc; header->length = bytes; { /* prepost all receives */ char *buf = (char*)dst; int bytes_remaining = bytes; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; nb_recv(buf, size, master_rank, nb); buf += size; bytes_remaining -= size; } while (bytes_remaining > 0); } nb_send_header(header, sizeof(header_t), master_rank, nb); } nb->in_use = 1; } STATIC void nb_acc(int datatype, void *scale, void *src, void *dst, int bytes, int proc, _cmx_request *nb) { CMX_ASSERT(NULL != src); CMX_ASSERT(NULL != dst); CMX_ASSERT(bytes > 0); CMX_ASSERT(proc >= 0); CMX_ASSERT(proc < g_state.size); CMX_ASSERT(NULL != nb); if (CMX_ENABLE_ACC_SELF) { /* acc to self */ if (g_state.rank == proc) { if (fence_array[g_state.master[proc]]) { _fence_master(g_state.master[proc]); } sem_wait(semaphores[proc]); _acc(datatype, bytes, dst, src, scale); sem_post(semaphores[proc]); return; } } if (CMX_ENABLE_ACC_SMP) { /* acc to same SMP node */ // if (g_state.hostid[proc] == g_state.hostid[g_state.rank]) if (g_state.master[proc] == g_state.master[g_state.rank]) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; if (fence_array[g_state.master[proc]]) { _fence_master(g_state.master[proc]); } reg_entry = reg_cache_find(proc, dst, bytes); CMX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, dst); sem_wait(semaphores[proc]); _acc(datatype, bytes, mapped_offset, src, scale); sem_post(semaphores[proc]); return; } } { header_t *header = NULL; char *message = NULL; int master_rank = -1; int message_size = 0; int scale_size = 0; op_t operation = OP_NULL; int use_eager = 0; switch (datatype) { case CMX_ACC_INT: operation = OP_ACC_INT; scale_size = sizeof(int); break; case CMX_ACC_DBL: operation = OP_ACC_DBL; scale_size = sizeof(double); break; case CMX_ACC_FLT: operation = OP_ACC_FLT; scale_size = sizeof(float); break; case CMX_ACC_CPL: operation = OP_ACC_CPL; scale_size = sizeof(SingleComplex); break; case CMX_ACC_DCP: operation = OP_ACC_DCP; scale_size = sizeof(DoubleComplex); break; case CMX_ACC_LNG: operation = OP_ACC_LNG; scale_size = sizeof(long); break; default: CMX_ASSERT(0); } use_eager = _eager_check(scale_size+bytes); master_rank = g_state.master[proc]; /* only fence on the master */ fence_array[master_rank] = 1; if (use_eager) { message_size = sizeof(header_t) + scale_size + bytes; } else { message_size = sizeof(header_t) + scale_size; } message = malloc(message_size); CMX_ASSERT(message); header = (header_t*)message; header->operation = operation; header->remote_address = dst; header->local_address = src; header->rank = proc; header->length = bytes; (void)memcpy(message+sizeof(header_t), scale, scale_size); if (use_eager) { (void)memcpy(message+sizeof(header_t)+scale_size, src, bytes); nb_send_header(message, message_size, master_rank, nb); } else { char *buf = (char*)src; int bytes_remaining = bytes; nb_send_header(message, message_size, master_rank, nb); do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; nb_send_buffer(buf, size, master_rank, nb); buf += size; bytes_remaining -= size; } while (bytes_remaining > 0); } } nb->in_use = 1; } STATIC void nb_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, _cmx_request *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; #if DEBUG fprintf(stderr, "[%d] nb_puts(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif /* if not actually a strided put */ if (0 == stride_levels) { nb_put(src, dst, count[0], proc, nb); return; } /* if not a strided put to self or SMP, use datatype algorithm */ if (CMX_ENABLE_PUT_DATATYPE && (!CMX_ENABLE_PUT_SELF || g_state.rank != proc) && (!CMX_ENABLE_PUT_SMP || g_state.hostid[proc] != g_state.hostid[g_state.rank]) && (_packed_size(src_stride, count, stride_levels) > CMX_PUT_DATATYPE_THRESHOLD)) { nb_puts_datatype(src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } /* if not a strided put to self or SMP, use packed algorithm */ if (CMX_ENABLE_PUT_PACKED && (!CMX_ENABLE_PUT_SELF || g_state.rank != proc) && (!CMX_ENABLE_PUT_SMP || g_state.hostid[proc] != g_state.hostid[g_state.rank])) { nb_puts_packed(src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } /* index mangling */ for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_put((char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } } STATIC void nb_puts_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, _cmx_request *nb) { int i; int packed_index = 0; char *packed_buffer = NULL; stride_t stride; #if DEBUG fprintf(stderr, "[%d] nb_puts_packed(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif CMX_ASSERT(proc >= 0); CMX_ASSERT(proc < g_state.size); CMX_ASSERT(NULL != src); CMX_ASSERT(NULL != dst); CMX_ASSERT(NULL != count); CMX_ASSERT(NULL != nb); CMX_ASSERT(stride_levels >= 0); CMX_ASSERT(stride_levels < CMX_MAX_STRIDE_LEVEL); /* copy dst info into structure */ stride.stride_levels = stride_levels; stride.count[0] = count[0]; for (i=0; i= 0); CMX_ASSERT(stride.stride_levels < CMX_MAX_STRIDE_LEVEL); #if DEBUG fprintf(stderr, "[%d] nb_puts_packed stride_levels=%d, count[0]=%d\n", g_state.rank, stride_levels, count[0]); for (i=0; i 0); { char *message = NULL; int message_size = 0; header_t *header = NULL; int master_rank = -1; int use_eager = _eager_check(sizeof(stride_t)+packed_index); master_rank = g_state.master[proc]; /* only fence on the master */ fence_array[master_rank] = 1; if (use_eager) { message_size = sizeof(header_t)+sizeof(stride_t)+packed_index; } else { message_size = sizeof(header_t)+sizeof(stride_t); } message = malloc(message_size); header = (header_t*)message; header->operation = OP_PUT_PACKED; header->remote_address = dst; header->local_address = NULL; header->rank = proc; header->length = packed_index; (void)memcpy(message+sizeof(header_t), &stride, sizeof(stride_t)); if (use_eager) { (void)memcpy(message+sizeof(header_t)+sizeof(stride_t), packed_buffer, packed_index); nb_send_header(message, message_size, master_rank, nb); free(packed_buffer); } else { /* we send the buffer backwards */ char *buf = packed_buffer + packed_index;; int bytes_remaining = packed_index; nb_send_header(message, message_size, master_rank, nb); do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; buf -= size; if (size == bytes_remaining) { /* on the last send, mark buffer for deletion */ nb_send_header(buf, size, master_rank, nb); } else { nb_send_buffer(buf, size, master_rank, nb); } bytes_remaining -= size; } while (bytes_remaining > 0); } } nb->in_use = 1; } STATIC void nb_puts_datatype( void *src_ptr, int *src_stride_ar, void *dst_ptr, int *dst_stride_ar, int *count, int stride_levels, int proc, _cmx_request *nb) { MPI_Datatype src_type; int ierr; int i; stride_t stride; #if DEBUG fprintf(stderr, "[%d] nb_puts_datatype(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src_ptr, src_stride_ar, dst_ptr, dst_stride_ar, count[0], stride_levels, proc, nb); #endif CMX_ASSERT(proc >= 0); CMX_ASSERT(proc < g_state.size); CMX_ASSERT(NULL != src_ptr); CMX_ASSERT(NULL != dst_ptr); CMX_ASSERT(NULL != count); CMX_ASSERT(NULL != nb); CMX_ASSERT(stride_levels >= 0); CMX_ASSERT(stride_levels < CMX_MAX_STRIDE_LEVEL); /* copy dst info into structure */ MAYBE_MEMSET(&stride, 0, sizeof(stride_t)); stride.stride_levels = stride_levels; stride.count[0] = count[0]; for (i=0; i= 0); CMX_ASSERT(stride.stride_levels < CMX_MAX_STRIDE_LEVEL); #if DEBUG fprintf(stderr, "[%d] nb_puts_datatype stride_levels=%d, count[0]=%d\n", g_state.rank, stride_levels, count[0]); for (i=0; ioperation = OP_PUT_DATATYPE; header->remote_address = dst_ptr; header->local_address = NULL; header->rank = proc; header->length = 0; (void)memcpy(message+sizeof(header_t), &stride, sizeof(stride_t)); nb_send_header(message, message_size, master_rank, nb); nb_send_datatype(src_ptr, src_type, master_rank, nb); } nb->in_use = 1; } STATIC void nb_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, _cmx_request *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; /* if not actually a strided get */ if (0 == stride_levels) { nb_get(src, dst, count[0], proc, nb); return; } /* if not a strided get from self or SMP, use datatype algorithm */ if (CMX_ENABLE_GET_DATATYPE && (!CMX_ENABLE_GET_SELF || g_state.rank != proc) && (!CMX_ENABLE_GET_SMP || g_state.hostid[proc] != g_state.hostid[g_state.rank]) && (_packed_size(src_stride, count, stride_levels) > CMX_GET_DATATYPE_THRESHOLD)) { nb_gets_datatype(src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } /* if not a strided get from self or SMP, use packed algorithm */ if (CMX_ENABLE_GET_PACKED && (!CMX_ENABLE_GET_SELF || g_state.rank != proc) && (!CMX_ENABLE_GET_SMP || g_state.hostid[proc] != g_state.hostid[g_state.rank])) { nb_gets_packed(src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } dst_idx = 0; for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_get((char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } } STATIC void nb_gets_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, _cmx_request *nb) { int i; stride_t stride_src; stride_t *stride_dst = NULL; #if DEBUG fprintf(stderr, "[%d] nb_gets_packed(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif CMX_ASSERT(proc >= 0); CMX_ASSERT(proc < g_state.size); CMX_ASSERT(NULL != src); CMX_ASSERT(NULL != dst); CMX_ASSERT(NULL != count); CMX_ASSERT(NULL != nb); CMX_ASSERT(count[0] > 0); CMX_ASSERT(stride_levels >= 0); CMX_ASSERT(stride_levels < CMX_MAX_STRIDE_LEVEL); /* copy src info into structure */ stride_src.ptr = src; stride_src.stride_levels = stride_levels; stride_src.count[0] = count[0]; for (i=0; i= 0); CMX_ASSERT(stride_src.stride_levels < CMX_MAX_STRIDE_LEVEL); /* copy dst info into structure */ stride_dst = malloc(sizeof(stride_t)); CMX_ASSERT(stride_dst); stride_dst->ptr = dst; stride_dst->stride_levels = stride_levels; stride_dst->count[0] = count[0]; for (i=0; istride[i] = dst_stride[i]; stride_dst->count[i+1] = count[i+1]; } for (/*no init*/; istride[i] = -1; stride_dst->count[i+1] = -1; } CMX_ASSERT(stride_dst->stride_levels >= 0); CMX_ASSERT(stride_dst->stride_levels < CMX_MAX_STRIDE_LEVEL); { char *message = NULL; int message_size = 0; int recv_size = 0; char *packed_buffer = NULL; header_t *header = NULL; int master_rank = -1; master_rank = g_state.master[proc]; message_size = sizeof(header_t) + sizeof(stride_t); message = malloc(message_size); header = (header_t*)message; CMX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_GET_PACKED; header->remote_address = src; header->local_address = dst; header->rank = proc; header->length = 0; recv_size = _packed_size(stride_dst->stride, stride_dst->count, stride_dst->stride_levels); CMX_ASSERT(recv_size > 0); packed_buffer = malloc(recv_size); CMX_ASSERT(packed_buffer); { /* prepost all receives backward */ char *buf = (char*)packed_buffer + recv_size; int bytes_remaining = recv_size; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; buf -= size; if (size == bytes_remaining) { /* on the last recv, indicate a packed recv */ nb_recv_packed(buf, size, master_rank, nb, stride_dst); } else { nb_recv(buf, size, master_rank, nb); } bytes_remaining -= size; } while (bytes_remaining > 0); } (void)memcpy(message+sizeof(header_t), &stride_src, sizeof(stride_t)); nb_send_header(message, message_size, master_rank, nb); } nb->in_use = 1; } STATIC void nb_gets_datatype( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, _cmx_request *nb) { MPI_Datatype dst_type; int i; stride_t stride_src; #if DEBUG fprintf(stderr, "[%d] nb_gets_datatype(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif #if DEBUG for (i=0; i= 0); CMX_ASSERT(proc < g_state.size); CMX_ASSERT(NULL != src); CMX_ASSERT(NULL != dst); CMX_ASSERT(NULL != count); CMX_ASSERT(NULL != nb); CMX_ASSERT(count[0] > 0); CMX_ASSERT(stride_levels >= 0); CMX_ASSERT(stride_levels < CMX_MAX_STRIDE_LEVEL); /* copy src info into structure */ MAYBE_MEMSET(&stride_src, 0, sizeof(header_t)); stride_src.ptr = src; stride_src.stride_levels = stride_levels; stride_src.count[0] = count[0]; for (i=0; i= 0); CMX_ASSERT(stride_src.stride_levels < CMX_MAX_STRIDE_LEVEL); { char *message = NULL; int message_size = 0; header_t *header = NULL; int master_rank = -1; int ierr; master_rank = g_state.master[proc]; message_size = sizeof(header_t) + sizeof(stride_t); message = malloc(message_size); header = (header_t*)message; CMX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_GET_DATATYPE; header->remote_address = src; header->local_address = dst; header->rank = proc; header->length = 0; strided_to_subarray_dtype(dst_stride, count, stride_levels, MPI_BYTE, &dst_type); ierr = MPI_Type_commit(&dst_type); _translate_mpi_error(ierr,"nb_gets_datatype:MPI_Type_commit"); nb_recv_datatype(dst, dst_type, master_rank, nb); (void)memcpy(message+sizeof(header_t), &stride_src, sizeof(stride_t)); nb_send_header(message, message_size, master_rank, nb); } nb->in_use = 1; } STATIC void nb_accs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, _cmx_request *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; /* if not actually a strided acc */ if (0 == stride_levels) { nb_acc(datatype, scale, src, dst, count[0], proc, nb); return; } /* if not a strided acc to self or SMP, use packed algorithm */ if (CMX_ENABLE_ACC_PACKED && (!CMX_ENABLE_ACC_SELF || g_state.rank != proc) && (!CMX_ENABLE_ACC_SMP || g_state.hostid[proc] != g_state.hostid[g_state.rank])) { nb_accs_packed(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } /* index mangling */ for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_acc(datatype, scale, (char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } } STATIC void nb_accs_packed( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, _cmx_request *nb) { int i; int packed_index = 0; char *packed_buffer = NULL; stride_t stride; #if DEBUG fprintf(stderr, "[%d] nb_accs_packed(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif CMX_ASSERT(proc >= 0); CMX_ASSERT(proc < g_state.size); CMX_ASSERT(NULL != scale); CMX_ASSERT(NULL != src); CMX_ASSERT(NULL != dst); CMX_ASSERT(NULL != count); CMX_ASSERT(NULL != nb); CMX_ASSERT(count[0] > 0); CMX_ASSERT(stride_levels >= 0); CMX_ASSERT(stride_levels < CMX_MAX_STRIDE_LEVEL); /* copy dst info into structure */ stride.ptr = dst; stride.stride_levels = stride_levels; stride.count[0] = count[0]; for (i=0; i= 0); CMX_ASSERT(stride.stride_levels < CMX_MAX_STRIDE_LEVEL); #if DEBUG fprintf(stderr, "[%d] nb_accs_packed stride_levels=%d, count[0]=%d\n", g_state.rank, stride_levels, count[0]); for (i=0; i 0); { header_t *header = NULL; char *message = NULL; int message_size = 0; int scale_size = 0; op_t operation = OP_NULL; int master_rank = -1; int use_eager = 0; switch (datatype) { case CMX_ACC_INT: operation = OP_ACC_INT_PACKED; scale_size = sizeof(int); break; case CMX_ACC_DBL: operation = OP_ACC_DBL_PACKED; scale_size = sizeof(double); break; case CMX_ACC_FLT: operation = OP_ACC_FLT_PACKED; scale_size = sizeof(float); break; case CMX_ACC_CPL: operation = OP_ACC_CPL_PACKED; scale_size = sizeof(SingleComplex); break; case CMX_ACC_DCP: operation = OP_ACC_DCP_PACKED; scale_size = sizeof(DoubleComplex); break; case CMX_ACC_LNG: operation = OP_ACC_LNG_PACKED; scale_size = sizeof(long); break; default: CMX_ASSERT(0); } use_eager = _eager_check(scale_size+sizeof(stride_t)+packed_index); master_rank = g_state.master[proc]; /* only fence on the master */ fence_array[master_rank] = 1; if (use_eager) { message_size = sizeof(header_t) + scale_size + sizeof(stride_t) + packed_index; } else { message_size = sizeof(header_t) + scale_size + sizeof(stride_t); } message = malloc(message_size); CMX_ASSERT(message); header = (header_t*)message; header->operation = operation; header->remote_address = dst; header->local_address = NULL; header->rank = proc; header->length = packed_index; (void)memcpy(message+sizeof(header_t), scale, scale_size); (void)memcpy(message+sizeof(header_t)+scale_size, &stride, sizeof(stride_t)); if (use_eager) { (void)memcpy(message+sizeof(header_t)+scale_size+sizeof(stride_t), packed_buffer, packed_index); nb_send_header(message, message_size, master_rank, nb); free(packed_buffer); } else { /* we send the buffer backwards */ char *buf = packed_buffer + packed_index; int bytes_remaining = packed_index; nb_send_header(message, message_size, master_rank, nb); do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; buf -= size; if (size == bytes_remaining) { nb_send_header(buf, size, master_rank, nb); } else { nb_send_buffer(buf, size, master_rank, nb); } bytes_remaining -= size; } while (bytes_remaining > 0); } } nb->in_use = 1; } STATIC void nb_putv( _cmx_giov_t *iov, int iov_len, int proc, _cmx_request *nb) { int i = 0; for (i=0; isrc; dst = iov->dst; bytes = iov->bytes; limit = iov->count; #if DEBUG fprintf(stderr, "[%d] nb_putv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif /* allocate compressed iov */ iov_size = 2*limit*sizeof(void*) + 2*sizeof(int); iov_buf = malloc(iov_size); CMX_ASSERT(iov_buf); iov_off = 0; /* copy limit */ (void)memcpy(&iov_buf[iov_off], &limit, sizeof(int)); iov_off += sizeof(int); /* copy bytes */ (void)memcpy(&iov_buf[iov_off], &bytes, sizeof(int)); iov_off += sizeof(int); /* copy src pointers */ (void)memcpy(&iov_buf[iov_off], src, limit*sizeof(void*)); iov_off += limit*sizeof(void*); /* copy dst pointers */ (void)memcpy(&iov_buf[iov_off], dst, limit*sizeof(void*)); iov_off += limit*sizeof(void*); CMX_ASSERT(iov_off == iov_size); /* allocate send buffer */ packed_size = bytes * limit; packed_buffer = malloc(packed_size); CMX_ASSERT(packed_buffer); packed_index = 0; for (i=0; ioperation = OP_PUT_IOV; header->remote_address = NULL; header->local_address = NULL; header->rank = proc; header->length = iov_size; nb_send_header(header, sizeof(header_t), master_rank, nb); nb_send_header(iov_buf, iov_size, master_rank, nb); nb_send_header(packed_buffer, packed_size, master_rank, nb); } } STATIC void nb_getv( _cmx_giov_t *iov, int iov_len, int proc, _cmx_request *nb) { int i = 0; for (i=0; isrc; dst = iov->dst; bytes = iov->bytes; limit = iov->count; #if DEBUG fprintf(stderr, "[%d] nb_getv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif /* allocate compressed iov */ iov_size = 2*limit*sizeof(void*) + 2*sizeof(int); iov_buf = malloc(iov_size); iov_off = 0; CMX_ASSERT(iov_buf); /* copy limit */ (void)memcpy(&iov_buf[iov_off], &limit, sizeof(int)); iov_off += sizeof(int); /* copy bytes */ (void)memcpy(&iov_buf[iov_off], &bytes, sizeof(int)); iov_off += sizeof(int); /* copy src pointers */ (void)memcpy(&iov_buf[iov_off], src, limit*sizeof(void*)); iov_off += limit*sizeof(void*); /* copy dst pointers */ (void)memcpy(&iov_buf[iov_off], dst, limit*sizeof(void*)); iov_off += limit*sizeof(void*); CMX_ASSERT(iov_off == iov_size); /* copy given iov for later */ iov_copy = malloc(sizeof(_cmx_giov_t)); iov_copy->bytes = bytes; iov_copy->count = limit; iov_copy->src = malloc(sizeof(void*)*iov->count); CMX_ASSERT(iov_copy->src); (void)memcpy(iov_copy->src, iov->src, sizeof(void*)*iov->count); iov_copy->dst = malloc(sizeof(void*)*iov->count); CMX_ASSERT(iov_copy->dst); (void)memcpy(iov_copy->dst, iov->dst, sizeof(void*)*iov->count); #if DEBUG fprintf(stderr, "[%d] nb_getv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p copy\n", g_state.rank, iov_copy->count, iov_copy->bytes, iov_copy->src[0], iov_copy->dst[0]); #endif /* allocate recv buffer */ packed_size = bytes * limit; packed_buffer = malloc(packed_size); CMX_ASSERT(packed_buffer); { header_t *header = NULL; int master_rank = g_state.master[proc]; header = malloc(sizeof(header_t)); CMX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_GET_IOV; header->remote_address = NULL; header->local_address = NULL; header->rank = proc; header->length = iov_size; nb_recv_iov(packed_buffer, packed_size, master_rank, nb, iov_copy); nb_send_header(header, sizeof(header_t), master_rank, nb); nb_send_header(iov_buf, iov_size, master_rank, nb); } } STATIC void nb_accv( int datatype, void *scale, _cmx_giov_t *iov, int iov_len, int proc, _cmx_request *nb) { int i = 0; for (i=0; isrc; dst = iov->dst; bytes = iov->bytes; limit = iov->count; #if DEBUG fprintf(stderr, "[%d] nb_accv_packed limit=%d bytes=%d loc[0]=%p rem_offset[0]=%d\n", g_state.rank, limit, bytes, loc[0], rem[0]); #endif /* allocate compressed iov */ iov_size = 2*limit*sizeof(void*) + 2*sizeof(int); iov_buf = malloc(iov_size); iov_off = 0; CMX_ASSERT(iov_buf); /* copy limit */ (void)memcpy(&iov_buf[iov_off], &limit, sizeof(int)); iov_off += sizeof(int); /* copy bytes */ (void)memcpy(&iov_buf[iov_off], &bytes, sizeof(int)); iov_off += sizeof(int); /* copy src pointers */ (void)memcpy(&iov_buf[iov_off], src, limit*sizeof(void*)); iov_off += limit*sizeof(void*); /* copy dst pointers */ (void)memcpy(&iov_buf[iov_off], dst, limit*sizeof(void*)); iov_off += limit*sizeof(void*); CMX_ASSERT(iov_off == iov_size); /* allocate send buffer */ packed_size = bytes * limit; packed_buffer = malloc(packed_size); CMX_ASSERT(packed_buffer); packed_index = 0; for (i=0; ioperation = operation; header->remote_address = NULL; header->local_address = NULL; header->rank = proc; header->length = iov_size; (void)memcpy(message+sizeof(header_t), scale, scale_size); nb_send_header(message, message_size, master_rank, nb); nb_send_header(iov_buf, iov_size, master_rank, nb); nb_send_header(packed_buffer, packed_size, master_rank, nb); } } /** * Utility function to catch and translate MPI errors. Returns silently if * no error detected. * @param ierr: Error code from MPI call * @param location: User specified string to indicate location of error */ STATIC void _translate_mpi_error(int ierr, const char* location) { if (ierr == MPI_SUCCESS) return; char err_string[MPI_MAX_ERROR_STRING]; int len; fprintf(stderr,"p[%d] Error in %s\n",g_state.rank,location); MPI_Error_string(ierr,err_string,&len); fprintf(stderr,"p[%d] MPI_Error: %s\n",g_state.rank,err_string); } /** * No checking for data consistency. Assume correctness has already been * established elsewhere. Individual elements are assumed to be one byte in size * stride_array: physical dimensions of array * count: number of elements along each array dimension * levels: number of stride levels (should be one less than array dimension) * type: MPI_Datatype returned to calling program */ STATIC void strided_to_subarray_dtype(int *stride_array, int *count, int levels, MPI_Datatype base_type, MPI_Datatype *type) { int ndims = levels+1; int i = 0; int ierr = 0; int array_of_sizes[7]; int array_of_starts[7]; int array_of_subsizes[7]; int stride = 0; ierr = MPI_Type_size(base_type,&stride); _translate_mpi_error(ierr,"strided_to_subarray_dtype:MPI_Type_size"); /* the pointer to the local buffer points to the first data element * in data exchange, not the origin of the local array, so all starts * should be zero */ for (i=0; i #include #include #define CMX_MAX_NB_OUTSTANDING 256 #define CMX_MAX_STRIDE_LEVEL 8 #define CMX_TAG 27624 #define CMX_STATIC_BUFFER_SIZE (2u*1048576u) #define SHM_NAME_SIZE 31 #define UNLOCKED -1 /* performance or correctness related settings */ #if 0 #define ENABLE_UNNAMED_SEM 1 #else #define ENABLE_UNNAMED_SEM 0 #endif #define NEED_ASM_VOLATILE_MEMORY 0 #define MASTER_IS_SMALLEST_SMP_RANK 0 #define CMX_SET_AFFINITY 0 #define ENABLE_PUT_SELF 1 #define ENABLE_GET_SELF 1 #define ENABLE_ACC_SELF 1 #define ENABLE_PUT_SMP 1 #define ENABLE_GET_SMP 1 #define ENABLE_ACC_SMP 1 #define ENABLE_PUT_PACKED 1 #define ENABLE_GET_PACKED 1 #define ENABLE_ACC_PACKED 1 #define ENABLE_PUT_DATATYPE 1 #define ENABLE_GET_DATATYPE 1 #define ENABLE_ACC_DATATYPE 0 #define ENABLE_PUT_IOV 1 #define ENABLE_GET_IOV 1 #define ENABLE_ACC_IOV 1 #define DEBUG 0 #define DEBUG_VERBOSE 0 #define DEBUG_TO_FILE 0 #if DEBUG_TO_FILE FILE *cmx_trace_file; # define printf(...) fprintf(cmx_trace_file, __VA_ARGS__); fflush(cmx_trace_file) #else # define printf(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) #endif #define CMX_STRINGIFY(x) #x //#ifdef NDEBUG //#define CMX_ASSERT(WHAT) ((void) (0)) //#else #define CMX_ASSERT(WHAT) \ ((WHAT) \ ? (void) (0) \ : cmx_assert_fail (CMX_STRINGIFY(WHAT), __FILE__, __LINE__, __func__)) //#endif typedef int cmxInt; typedef struct group_link { struct group_link *next;/**< next group in linked list */ MPI_Comm comm; /**< whole comm; all ranks */ MPI_Group group; /**< whole group; all ranks */ int size; /**< comm size */ int rank; /**< comm rank */ int *world_ranks; /**< list of world ranks corresponding to local ranks */ } cmx_igroup_t; typedef cmx_igroup_t* cmx_group_t; typedef struct alloc_link { struct alloc_link *next; int rank; void *buf; cmxInt size; } cmx_alloc_t; typedef struct { cmx_igroup_t *group; cmxInt bytes; cmx_alloc_t *list; int rank; void *buf; } _cmx_handle; /* structure to describe strided data transfers */ typedef struct { void *ptr; int stride_levels; cmxInt stride[CMX_MAX_STRIDE_LEVEL]; cmxInt count[CMX_MAX_STRIDE_LEVEL+1]; } stride_t; /* Internal struct for vector communication */ typedef struct { void **src; /**< array of source starting addresses */ void **dst; /**< array of destination starting addresses */ int count; /**< size of address arrays (src[count],dst[count]) */ int bytes; /**< length in bytes for each src[i]/dst[i] pair */ } _cmx_giov_t; typedef struct message_link { struct message_link *next; void *message; MPI_Request request; MPI_Datatype datatype; int need_free; stride_t *stride; _cmx_giov_t *iov; } message_t; typedef struct { int in_use; cmxInt send_size; message_t *send_head; message_t *send_tail; cmxInt recv_size; message_t *recv_head; message_t *recv_tail; cmx_igroup_t *group; } _cmx_request; typedef struct { int rank; void *ptr; } rank_ptr_t; extern cmx_igroup_t *group_list; extern cmx_igroup_t *CMX_GROUP_WORLD; extern int _cmx_me; /* TODO: Problem with this function since cmx_error is defined in cmx.h * * On the other hand, this function is currently not used */ #if 1 static inline void cmx_assert_fail( const char *assertion, const char *file, unsigned int line, const char *function) { #if 0 fprintf(stderr, "[%d] %s:%u: %s: Assertion `%s' failed", g_state.rank, file, line, function, assertion); #endif fprintf(stderr, "p[%d] ASSERT %s:%u: %s: Assertion `%s' failed\n", _cmx_me, file, line, function, assertion); fflush(stderr); #if DEBUG printf("[%d] %s:%u: %s: Assertion `%s' failed", _cmx_me, file, line, function, assertion); #endif assert(-1); /* cmx_error("cmx_assert_fail", -1); */ } #endif #endif /* CMX_IMPL_H_ */ ga-5.9.2/cmx/src-mpi-pr/groups.c000066400000000000000000000435171500715745200164010ustar00rootroot00000000000000#include #include #include #include #include #include #if defined(__CRAYXE) # include #endif #include "cmx.h" #include "cmx_impl.h" #include "groups.h" /* world group state */ cmx_group_world_t g_state = { MPI_COMM_NULL, MPI_GROUP_NULL, -1, -1, NULL, NULL, MPI_COMM_NULL, -1, -1 }; /* the HEAD of the group linked list */ cmx_igroup_t *group_list = NULL; #define RANK_OR_PID (g_state.rank >= 0 ? g_state.rank : getpid()) /* static functions implemented in this file */ static void _create_igroup(cmx_igroup_t **igroup); static void _igroup_free(cmx_igroup_t *igroup); static long xgethostid(); /** * Creates and associates a cmx group with a cmx igroup. * * This does *not* initialize the members of the cmx igroup. */ static void _create_igroup(cmx_igroup_t **igroup) { cmx_igroup_t *new_group_list_item = NULL; cmx_igroup_t *last_group_list_item = NULL; #if DEBUG printf("[%d] _create_group(...)\n", RANK_OR_PID); #endif /* create, init, and insert the new node for the linked list */ new_group_list_item = malloc(sizeof(cmx_igroup_t)); new_group_list_item->next = NULL; new_group_list_item->comm = MPI_COMM_NULL; new_group_list_item->group = MPI_GROUP_NULL; new_group_list_item->size = -1; new_group_list_item->rank = -1; new_group_list_item->world_ranks = NULL; /* find the last group in the group linked list and insert */ if (group_list) { last_group_list_item = group_list; while (last_group_list_item->next != NULL) { last_group_list_item = last_group_list_item->next; } last_group_list_item->next = new_group_list_item; } else { group_list = new_group_list_item; } /* return the group id and cmx igroup */ *igroup = new_group_list_item; } int cmx_group_rank(cmx_igroup_t *igroup, int *rank) { *rank = igroup->rank; #if DEBUG printf("[%d] cmx_group_rank(group=%d, *rank=%d)\n", RANK_OR_PID, group, *rank); #endif return CMX_SUCCESS; } int cmx_group_size(cmx_igroup_t *igroup, int *size) { *size = igroup->size; #if DEBUG printf("[%d] cmx_group_size(group=%d, *size=%d)\n", RANK_OR_PID, group, *size); #endif return CMX_SUCCESS; } int cmx_group_comm(cmx_igroup_t *igroup, MPI_Comm *comm) { *comm = igroup->comm; #if DEBUG printf("[%d] cmx_group_comm(group=%d, comm)\n", RANK_OR_PID, group); #endif return CMX_SUCCESS; } int cmx_group_translate_ranks(int n, cmx_group_t group_from, int *ranks_from, cmx_group_t group_to, int *ranks_to) { int i; if (group_from == group_to) { for (i=0; igroup, n, ranks_from, group_to->group, ranks_to); if (status != MPI_SUCCESS) { cmx_error("MPI_Group_translate_ranks: Failed ", status); } } return CMX_SUCCESS; } int cmx_group_translate_world( cmx_igroup_t *igroup, int group_rank, int *world_rank) { #if DEBUG printf("[%d] cmx_group_translate_world(" "group=%d, group_rank=%d, world_rank)\n", RANK_OR_PID, group, group_rank); #endif if (CMX_GROUP_WORLD == igroup) { *world_rank = group_rank; } else { int status; CMX_ASSERT(group_list); /* first group is world worker group */ status = MPI_Group_translate_ranks(igroup->group, 1, &group_rank, group_list->group, world_rank); } return CMX_SUCCESS; } /** * Destroys the given cmx igroup. */ static void _igroup_free(cmx_igroup_t *igroup) { int status; #if DEBUG printf("[%d] _igroup_free\n", RANK_OR_PID); #endif CMX_ASSERT(igroup); if (igroup->group != MPI_GROUP_NULL) { status = MPI_Group_free(&igroup->group); if (status != MPI_SUCCESS) { cmx_error("MPI_Group_free: Failed ", status); } } #if DEBUG printf("[%d] free'd group\n", RANK_OR_PID); #endif if (igroup->comm != MPI_COMM_NULL) { status = MPI_Comm_free(&igroup->comm); if (status != MPI_SUCCESS) { cmx_error("MPI_Comm_free: Failed ", status); } } #if DEBUG printf("[%d] free'd comm\n", RANK_OR_PID); #endif if (igroup->world_ranks != NULL) { free(igroup->world_ranks); } free(igroup); } int cmx_group_free(cmx_igroup_t *igroup) { cmx_igroup_t *previous_group_list_item = NULL; cmx_igroup_t *current_group_list_item = group_list; #if DEBUG printf("[%d] cmx_group_free(id=%d)\n", RANK_OR_PID, id); #endif /* find the group to free */ while (current_group_list_item != NULL) { if (current_group_list_item == igroup) { break; } previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; } /* make sure we found a group */ CMX_ASSERT(current_group_list_item != NULL); /* remove the group from the linked list */ if (previous_group_list_item != NULL) { previous_group_list_item->next = current_group_list_item->next; } /* free the igroup */ _igroup_free(current_group_list_item); return CMX_SUCCESS; } void _igroup_set_world_ranks(cmx_igroup_t *igroup) { int i = 0; int my_world_rank = g_state.rank; igroup->world_ranks = (int*)malloc(sizeof(int)*igroup->size); int status; for (i=0; isize; ++i) { igroup->world_ranks[i] = MPI_PROC_NULL; } status = MPI_Allgather(&my_world_rank,1,MPI_INT,igroup->world_ranks, 1,MPI_INT,igroup->comm); CMX_ASSERT(MPI_SUCCESS == status); for (i=0; isize; ++i) { CMX_ASSERT(MPI_PROC_NULL != igroup->world_ranks[i]); } } int cmx_group_create( int n, int *pid_list, cmx_igroup_t *igroup_parent, cmx_igroup_t **igroup_child) { int status = 0; int grp_me = 0; MPI_Group *group_child = NULL; MPI_Comm *comm_child = NULL; MPI_Group *group_parent = NULL; MPI_Comm *comm_parent = NULL; #if DEBUG printf("[%d] cmx_group_create(" "n=%d, pid_list=%p, id_parent=%d, id_child)\n", RANK_OR_PID, n, pid_list, id_parent); { int p; printf("[%d] pid_list={%d", RANK_OR_PID, pid_list[0]); for (p=1; pgroup); comm_child = &((*igroup_child)->comm); /* get the parent's MPI_Group and MPI_Comm */ group_parent = &(igroup_parent->group); comm_parent = &(igroup_parent->comm); status = MPI_Group_incl(*group_parent, n, pid_list, group_child); CMX_ASSERT(MPI_SUCCESS == status); #if DEBUG printf("[%d] cmx_group_create before crazy logic\n", RANK_OR_PID); #endif { MPI_Comm comm, comm1, comm2; int lvl=1, local_ldr_pos; status = MPI_Group_rank(*group_child, &grp_me); CMX_ASSERT(MPI_SUCCESS == status); if (grp_me == MPI_UNDEFINED) { /* FIXME: keeping the group around for now */ #if DEBUG printf("[%d] cmx_group_create aborting -- not in group\n", RANK_OR_PID); #endif return CMX_SUCCESS; } /* SK: sanity check for the following bitwise operations */ CMX_ASSERT(grp_me>=0); /* FIXME: can be optimized away */ status = MPI_Comm_dup(MPI_COMM_SELF, &comm); CMX_ASSERT(MPI_SUCCESS == status); local_ldr_pos = grp_me; while(n>lvl) { int tag=0; int remote_ldr_pos = local_ldr_pos^lvl; if (remote_ldr_pos < n) { int remote_leader = pid_list[remote_ldr_pos]; MPI_Comm peer_comm = *comm_parent; int high = (local_ldr_poscomm, &((*igroup_child)->size)); CMX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_rank((*igroup_child)->comm, &((*igroup_child)->rank)); CMX_ASSERT(MPI_SUCCESS == status); } #if DEBUG printf("[%d] cmx_group_create after crazy logic\n", RANK_OR_PID); #endif _igroup_set_world_ranks(*igroup_child); return CMX_SUCCESS; } static int cmplong(const void *p1, const void *p2) { return *((long*)p1) - *((long*)p2); } /** * Initialize group linked list. Prepopulate with world group. */ void cmx_group_init() { int status = 0; int i = 0; int smallest_rank_with_same_hostid = 0; int largest_rank_with_same_hostid = 0; int size_node = 0; cmx_igroup_t *igroup = NULL; long *sorted = NULL; int count = 0; /* populate g_state */ /* dup MPI_COMM_WORLD and get group, rank, and size */ status = MPI_Comm_dup(MPI_COMM_WORLD, &(g_state.comm)); CMX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_group(g_state.comm, &(g_state.group)); CMX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_rank(g_state.comm, &(g_state.rank)); CMX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_size(g_state.comm, &(g_state.size)); CMX_ASSERT(MPI_SUCCESS == status); #if DEBUG_TO_FILE { char pathname[80]; sprintf(pathname, "trace.%d.log", g_state.rank); cmx_trace_file = fopen(pathname, "w"); CMX_ASSERT(NULL != cmx_trace_file); printf("[%d] cmx_group_init()\n", RANK_OR_PID); } #endif /* need to figure out which proc is master on each node */ g_state.hostid = (long*)malloc(sizeof(long)*g_state.size); g_state.hostid[g_state.rank] = xgethostid(); status = MPI_Allgather(MPI_IN_PLACE, 1, MPI_LONG, g_state.hostid, 1, MPI_LONG, g_state.comm); CMX_ASSERT(MPI_SUCCESS == status); /* First create a temporary node communicator and then * split further into number of gruoups within the node */ MPI_Comm temp_node_comm; int temp_node_size; /* create node comm */ /* MPI_Comm_split requires a non-negative color, * so sort and sanitize */ sorted = (long*)malloc(sizeof(long) * g_state.size); (void)memcpy(sorted, g_state.hostid, sizeof(long)*g_state.size); qsort(sorted, g_state.size, sizeof(long), cmplong); for (i=0; i largest_rank_with_same_hostid) { largest_rank_with_same_hostid = i; } } } /* Get number of Progress-Ranks per node from environment variable * equal to 1 by default */ int num_progress_ranks_per_node = get_num_progress_ranks_per_node(); /* Perform check on the number of Progress-Ranks */ if (size_node < 2 * num_progress_ranks_per_node) { cmx_error("ranks per node, must be at least", 2 * num_progress_ranks_per_node); } if (size_node % num_progress_ranks_per_node > 0) { cmx_error("number of ranks per node must be multiple of number of process groups per node", -1); } int is_node_ranks_packed = get_progress_rank_distribution_on_node(); int split_group_size; split_group_size = node_group_size / num_progress_ranks_per_node; MPI_Comm_free(&temp_node_comm); g_state.master = (int*)malloc(sizeof(int)*g_state.size); g_state.master[g_state.rank] = get_my_master_rank_with_same_hostid(g_state.rank, split_group_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed); #if DEBUG printf("[%d] rank; split_group_size: %d\n", g_state.rank, split_group_size); printf("[%d] rank; largest_rank_with_same_hostid[%d]; my master is:[%d]\n", g_state.rank, largest_rank_with_same_hostid, g_state.master[g_state.rank]); #endif status = MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, g_state.master, 1, MPI_INT, g_state.comm); CMX_ASSERT(MPI_SUCCESS == status); CMX_ASSERT(group_list == NULL); // put split group stamps int proc_split_group_stamp; int num_split_groups; num_split_groups = num_nodes * num_progress_ranks_per_node; int* split_group_list = (int*)malloc(sizeof(int)*num_split_groups); int split_group_index = 0; int j; for (i=0; icomm)); CMX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_group(igroup->comm, &(igroup->group)); CMX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_rank(igroup->comm, &(igroup->rank)); CMX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_size(igroup->comm, &(igroup->size)); igroup->next = NULL; CMX_ASSERT(MPI_SUCCESS == status); _igroup_set_world_ranks(igroup); CMX_ASSERT(igroup->world_ranks != NULL); #if DEBUG printf("Creating comm: I AM WORKER[%ld]\n", g_state.rank); #endif CMX_GROUP_WORLD = igroup; group_list = igroup; } status = MPI_Comm_split(MPI_COMM_WORLD, proc_split_group_stamp, g_state.rank, &(g_state.node_comm)); CMX_ASSERT(MPI_SUCCESS == status); /* node rank */ status = MPI_Comm_rank(g_state.node_comm, &(g_state.node_rank)); CMX_ASSERT(MPI_SUCCESS == status); /* node size */ status = MPI_Comm_size(g_state.node_comm, &(g_state.node_size)); CMX_ASSERT(MPI_SUCCESS == status); #if DEBUG printf("node_rank[%d]/ size[%d]\n", g_state.node_rank, g_state.node_size); if (g_state.master[g_state.rank] == g_state.rank) { printf("[%d] world %d/%d\tI'm a master\n", RANK_OR_PID, g_state.rank, g_state.size); } else { printf("[%d] world %d/%d\tI'm a worker\n", RANK_OR_PID, g_state.rank, g_state.size); } #endif } void cmx_group_finalize() { int status; cmx_igroup_t *current_group_list_item = group_list; cmx_igroup_t *previous_group_list_item = NULL; #if DEBUG printf("[%d] cmx_group_finalize()\n", RANK_OR_PID); #endif /* This loop will also clean up CMX_GROUP_WORLD */ while (current_group_list_item != NULL) { previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; _igroup_free(previous_group_list_item); } free(g_state.master); free(g_state.hostid); status = MPI_Comm_free(&(g_state.node_comm)); CMX_ASSERT(MPI_SUCCESS == status); status = MPI_Group_free(&(g_state.group)); CMX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_free(&(g_state.comm)); CMX_ASSERT(MPI_SUCCESS == status); } static long xgethostid() { #if defined(__CRAYXE) #warning CRAY int nodeid; PMI_Get_nid(g_state.rank, &nodeid); #else long nodeid = gethostid(); #endif return nodeid; } ga-5.9.2/cmx/src-mpi-pr/groups.h000066400000000000000000000166141500715745200164040ustar00rootroot00000000000000/** * Private header file for cmx groups backed by MPI_comm. * * The rest of the cmx group functions are defined in the public cmx.h. * * @author Jeff Daily */ #ifndef _CMX_GROUPS_H_ #define _CMX_GROUPS_H_ #include #include "cmx_impl.h" typedef struct { MPI_Comm comm; /**< whole comm; all ranks */ MPI_Group group;/**< whole group; all ranks */ int size; /**< comm size */ int rank; /**< comm rank */ int *master; /**< master[size] rank of a given rank's master */ long *hostid; /**< hostid[size] hostid of SMP node for a given rank */ MPI_Comm node_comm; /**< node comm; SMP ranks */ int node_size; /**< node comm size */ int node_rank; /**< node comm rank */ } cmx_group_world_t; extern cmx_group_world_t g_state; extern void cmx_group_init(); extern void cmx_group_finalize(); cmx_igroup_t *CMX_GROUP_WORLD; /* verify that proc is part of group */ #define CHECK_GROUP(GROUP,PROC) do { \ int size; \ CMX_ASSERT(CMX_SUCCESS == cmx_group_size(GROUP,&size)); \ CMX_ASSERT(PROC >= 0); \ CMX_ASSERT(PROC < size); \ } while(0) static int get_num_progress_ranks_per_node() { int num_progress_ranks_per_node; const char* num_progress_ranks_env_var = getenv("GA_NUM_PROGRESS_RANKS_PER_NODE"); if (num_progress_ranks_env_var != NULL && num_progress_ranks_env_var[0] != '\0') { int env_number = atoi(getenv("GA_NUM_PROGRESS_RANKS_PER_NODE")); if ( env_number > 0 && env_number < 16) num_progress_ranks_per_node = env_number; #if DEBUG printf("num_progress_ranks_per_node: %d\n", num_progress_ranks_per_node); #endif } else { num_progress_ranks_per_node = 1; } return num_progress_ranks_per_node; } static int get_progress_rank_distribution_on_node() { const char* progress_ranks_packed_env_var = getenv("GA_PROGRESS_RANKS_DISTRIBUTION_PACKED"); const char* progress_ranks_cyclic_env_var = getenv("GA_PROGRESS_RANKS_DISTRIBUTION_CYCLIC"); int is_node_ranks_packed; int rank_packed =0; int rank_cyclic =0; if (progress_ranks_packed_env_var != NULL && progress_ranks_packed_env_var[0] != '\0') { if (strchr(progress_ranks_packed_env_var, 'y') != NULL || strchr(progress_ranks_packed_env_var, 'Y') != NULL || strchr(progress_ranks_packed_env_var, '1') != NULL ) { rank_packed = 1; } } if (progress_ranks_cyclic_env_var != NULL && progress_ranks_cyclic_env_var[0] != '\0') { if (strchr(progress_ranks_cyclic_env_var, 'y') != NULL || strchr(progress_ranks_cyclic_env_var, 'Y') != NULL || strchr(progress_ranks_cyclic_env_var, '1') != NULL ) { rank_cyclic = 1; } } if (rank_packed == 1 || rank_cyclic == 0) is_node_ranks_packed = 1; if (rank_packed == 0 && rank_cyclic == 1) is_node_ranks_packed = 0; return is_node_ranks_packed; } static int get_my_master_rank_with_same_hostid(int rank, int split_group_size, int smallest_rank_with_same_hostid, int largest_rank_with_same_hostid, int num_progress_ranks_per_node, int is_node_ranks_packed) { int my_master; #if MASTER_IS_SMALLEST_SMP_RANK if(is_node_ranks_packed) { /* Contiguous packing of ranks on a node */ my_master = smallest_rank_with_same_hostid + split_group_size * ((rank - smallest_rank_with_same_hostid)/split_group_size); } else { if(num_progress_ranks_per_node == 1) { my_master = 2 * (split_group_size * ( ((rank - smallest_rank_with_same_hostid)/2) / split_group_size)); } else { /* Cyclic packing of ranks on a node between two sockets * with even and odd numbering */ if(rank % 2 == 0) { my_master = 2 * (split_group_size * ( ((rank - smallest_rank_with_same_hostid)/2) / split_group_size)); } else { my_master = 1 + 2 * (split_group_size * ( ((rank - smallest_rank_with_same_hostid)/2) / split_group_size)); } } } #else /* By default creates largest SMP rank as Master */ if(is_node_ranks_packed) { /* Contiguous packing of ranks on a node */ my_master = largest_rank_with_same_hostid - split_group_size * ((largest_rank_with_same_hostid - rank)/split_group_size); } else { if(num_progress_ranks_per_node == 1) { my_master = largest_rank_with_same_hostid - 2 * (split_group_size * ( ((largest_rank_with_same_hostid - rank)/2) / split_group_size)); } else { /* Cyclic packing of ranks on a node between two sockets * with even and odd numbering */ if(rank % 2 == 0) { my_master = largest_rank_with_same_hostid - 1 - 2 * (split_group_size * ( ((largest_rank_with_same_hostid - rank)/2) / split_group_size)); } else { my_master = largest_rank_with_same_hostid - 2 * (split_group_size * ( ((largest_rank_with_same_hostid - rank)/2) / split_group_size)); } } } #endif return my_master; } static int get_my_rank_to_free(int rank, int split_group_size, int smallest_rank_with_same_hostid, int largest_rank_with_same_hostid, int num_progress_ranks_per_node, int is_node_ranks_packed) { int my_rank_to_free; #if MASTER_IS_SMALLEST_SMP_RANK /* By default creates largest SMP rank as Master */ if(is_node_ranks_packed) { /* Contiguous packing of ranks on a node */ my_rank_to_free = largest_rank_with_same_hostid - split_group_size * ((largest_rank_with_same_hostid - rank)/split_group_size); } else { if(num_progress_ranks_per_node == 1) { my_rank_to_free = largest_rank_with_same_hostid - 2 * (split_group_size * ( ((largest_rank_with_same_hostid - rank)/2) / split_group_size)); } else { /* Cyclic packing of ranks on a node between two sockets * with even and odd numbering */ if(rank % 2 == 0) { my_rank_to_free = largest_rank_with_same_hostid - 1 - 2 * (split_group_size * ( ((largest_rank_with_same_hostid - rank)/2) / split_group_size)); } else { my_rank_to_free = largest_rank_with_same_hostid - 2 * (split_group_size * ( ((largest_rank_with_same_hostid - rank)/2) / split_group_size)); } } } #else if(is_node_ranks_packed) { /* Contiguous packing of ranks on a node */ my_rank_to_free = smallest_rank_with_same_hostid + split_group_size * ((rank - smallest_rank_with_same_hostid)/split_group_size); } else { if(num_progress_ranks_per_node == 1) { my_rank_to_free = 2 * (split_group_size * ( ((rank - smallest_rank_with_same_hostid)/2) / split_group_size)); } else { /* Cyclic packing of ranks on a node between two sockets * with even and odd numbering */ if(rank % 2 == 0) { my_rank_to_free = 2 * (split_group_size * ( ((rank - smallest_rank_with_same_hostid)/2) / split_group_size)); } else { my_rank_to_free = 1 + 2 * (split_group_size * ( ((rank - smallest_rank_with_same_hostid)/2) / split_group_size)); } } } #endif return my_rank_to_free; } #endif /* _CMX_GROUPS_H_ */ ga-5.9.2/cmx/src-mpi-pr/reg_cache.c000066400000000000000000000377631500715745200167700ustar00rootroot00000000000000/** * Registration cache. * * Defensive programming via copious CMX_ASSERT statements is encouraged. */ /* C headers */ #include #include #include #include /* 3rd party headers */ #include /* our headers */ #include "cmx.h" #include "cmx_impl.h" #include "reg_cache.h" #define STATIC static inline /* the static members in this module */ static reg_entry_t **reg_cache = NULL; /**< list of caches (one per process) */ static int reg_nprocs = 0; /**< number of caches (one per process) */ /* the static functions in this module */ static reg_return_t seg_cmp(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len, int op); static reg_return_t seg_intersects(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len); static reg_return_t seg_contains(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len); static reg_return_t reg_entry_intersects(reg_entry_t *reg_entry, void *buf, size_t len); static reg_return_t reg_entry_contains(reg_entry_t *reg_entry, void *buf, size_t len); #define TEST_FOR_INTERSECTION 0 #define TEST_FOR_CONTAINMENT 1 /** * Detects whether two memory segments intersect or one contains the other. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * @param[in] op op to perform, either TEST_FOR_INTERSECTION or * TEST_FOR_CONTAINMENT * * @pre NULL != reg_beg * @pre NULL != oth_beg * * @return RR_SUCCESS on success */ STATIC reg_return_t seg_cmp(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len, int op) { ptrdiff_t reg_beg = 0; ptrdiff_t reg_end = 0; ptrdiff_t oth_beg = 0; ptrdiff_t oth_end = 0; int result = 0; /* preconditions */ CMX_ASSERT(NULL != reg_addr); CMX_ASSERT(NULL != oth_addr); /* casts to ptrdiff_t since arithmetic on void* is undefined */ reg_beg = (ptrdiff_t)(reg_addr); reg_end = reg_beg + (ptrdiff_t)(reg_len); oth_beg = (ptrdiff_t)(oth_addr); oth_end = oth_beg + (ptrdiff_t)(oth_len); /* hack? we had problems with adjacent registered memory regions and * when the length of the query region was 0 */ if (oth_beg == oth_end) { oth_end += 1; } switch (op) { case TEST_FOR_INTERSECTION: result = (reg_beg >= oth_beg && reg_beg < oth_end) || (reg_end > oth_beg && reg_end <= oth_end); #if DEBUG printf("[%d] TEST_FOR_INTERSECTION " "(%td >= %td [%d] && %td < %td [%d]) ||" "(%td > %td [%d] && %td <= %td [%d])\n", g_state.rank, reg_beg, oth_beg, (reg_beg >= oth_beg), reg_beg, oth_end, (reg_beg < oth_end), reg_end, oth_beg, (reg_end > oth_beg), reg_end, oth_end, (reg_end <= oth_end)); #endif break; case TEST_FOR_CONTAINMENT: result = reg_beg <= oth_beg && reg_end >= oth_end; #if DEBUG printf("[%d] TEST_FOR_CONTAINMENT " "%td <= %td [%d] && %td >= %td [%d]\n", g_state.rank, reg_beg, oth_beg, (reg_beg <= oth_beg), reg_end, oth_end, (reg_end >= oth_end)); #endif break; default: CMX_ASSERT(0); } if (result) { return RR_SUCCESS; } else { return RR_FAILURE; } } /** * Detects whether two memory segments intersect. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * * @pre NULL != reg_beg * @pre NULL != oth_beg * * @return RR_SUCCESS on success */ STATIC reg_return_t seg_intersects(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len) { /* preconditions */ CMX_ASSERT(NULL != reg_addr); CMX_ASSERT(NULL != oth_addr); return seg_cmp( reg_addr, reg_len, oth_addr, oth_len, TEST_FOR_INTERSECTION); } /** * Detects whether the first memory segment contains the other. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * * @pre NULL != reg_beg * @pre NULL != oth_beg * * @return RR_SUCCESS on success */ STATIC reg_return_t seg_contains(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len) { /* preconditions */ CMX_ASSERT(NULL != reg_addr); CMX_ASSERT(NULL != oth_addr); return seg_cmp( reg_addr, reg_len, oth_addr, oth_len, TEST_FOR_CONTAINMENT); } /** * Detects whether two memory segments intersect. * * @param[in] reg_entry the registration entry * @param[in] buf starting address for the contiguous memory region * @param[in] len length of the contiguous memory region * * @pre NULL != reg_entry * @pre NULL != buf * @pre len >= 0 * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_intersects(reg_entry_t *reg_entry, void *buf, size_t len) { #if DEBUG printf("[%d] reg_entry_intersects(reg_entry=%p, buf=%p, len=%d)\n", g_state.rank, reg_entry, buf, len); #endif /* preconditions */ CMX_ASSERT(NULL != reg_entry); CMX_ASSERT(NULL != buf); CMX_ASSERT(len >= 0); return seg_intersects( reg_entry->buf, reg_entry->len, buf, len); } /** * Detects whether the first memory segment contains the other. * * @param[in] reg_entry the registration entry * @param[in] buf starting address for the contiguous memory region * @param[in] len length of the contiguous memory region * * @pre NULL != reg_entry * @pre NULL != buf * @pre len >= 0 * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_contains(reg_entry_t *reg_entry, void *buf, size_t len) { #if DEBUG printf("[%d] reg_entry_contains(reg_entry=%p, buf=%p, len=%d)\n", g_state.rank, reg_entry, buf, len); #endif /* preconditions */ CMX_ASSERT(NULL != reg_entry); CMX_ASSERT(NULL != buf); CMX_ASSERT(len >= 0); return seg_contains( reg_entry->buf, reg_entry->len, buf, len); } /** * Remove registration cache entry without deregistration. * * @param[in] rank the rank where the entry came from * @param[in] reg_entry the entry * * @pre NULL != reg_entry * @pre 0 <= rank && rank < reg_nprocs * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_destroy(int rank, reg_entry_t *reg_entry) { #if DEBUG printf("[%d] reg_entry_destroy(rank=%d, reg_entry=%p)\n" "buf=%p len=%zu name=%s mapped=%p\n", g_state.rank, rank, reg_entry, reg_entry->buf, reg_entry->len, reg_entry->name, reg_entry->mapped); #endif /* preconditions */ CMX_ASSERT(NULL != reg_entry); CMX_ASSERT(0 <= rank && rank < reg_nprocs); /* free cache entry */ free(reg_entry); return RR_SUCCESS; } /** * Create internal data structures for the registration cache. * * @param[in] nprocs number of registration caches to create i.e. one per * process * * @pre this function is called once to initialize the internal data * structures and cannot be called again until reg_cache_destroy() has been * called * * @see reg_cache_destroy() * * @return RR_SUCCESS on success */ reg_return_t reg_cache_init(int nprocs) { int i = 0; #if DEBUG printf("[%d] reg_cache_init(nprocs=%d)\n", g_state.rank, nprocs); #endif /* preconditions */ CMX_ASSERT(NULL == reg_cache); CMX_ASSERT(0 == reg_nprocs); /* keep the number of caches around for later use */ reg_nprocs = nprocs; /* allocate the registration cache list: */ reg_cache = (reg_entry_t **)malloc(sizeof(reg_entry_t*) * reg_nprocs); CMX_ASSERT(reg_cache); /* initialize the registration cache list: */ for (i = 0; i < reg_nprocs; ++i) { reg_cache[i] = NULL; } return RR_SUCCESS; } /** * Deregister and destroy all cache entries and associated buffers. * * @pre this function is called once to destroy the internal data structures * and cannot be called again until reg_cache_init() has been called * * @see reg_cache_init() * * @return RR_SUCCESS on success */ reg_return_t reg_cache_destroy() { int i = 0; #if DEBUG printf("[%d] reg_cache_destroy()\n", g_state.rank); #endif /* preconditions */ CMX_ASSERT(NULL != reg_cache); CMX_ASSERT(0 != reg_nprocs); for (i = 0; i < reg_nprocs; ++i) { reg_entry_t *runner = reg_cache[i]; while (runner) { reg_entry_t *previous = runner; /* pointer to previous runner */ /* get next runner */ runner = runner->next; /* destroy the entry */ reg_entry_destroy(i, previous); } } /* free registration cache list */ free(reg_cache); reg_cache = NULL; /* reset the number of caches */ reg_nprocs = 0; return RR_SUCCESS; } /** * Locate a registration cache entry which contains the given segment * completely. * * @param[in] rank rank of the process * @param[in] buf starting address of the buffer * @parma[in] len length of the buffer * * @pre 0 <= rank && rank < reg_nprocs * @pre reg_cache_init() was previously called * * @return the reg cache entry, or NULL on failure */ reg_entry_t* reg_cache_find(int rank, void *buf, size_t len) { reg_entry_t *entry = NULL; reg_entry_t *runner = NULL; #if DEBUG printf("[%d] reg_cache_find(rank=%d, buf=%p, len=%d)\n", g_state.rank, rank, buf, len); #endif /* preconditions */ CMX_ASSERT(NULL != reg_cache); CMX_ASSERT(0 <= rank && rank < reg_nprocs); runner = reg_cache[rank]; while (runner && NULL == entry) { if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { entry = runner; #if DEBUG printf("[%d] reg_cache_find entry found\n" "reg_entry=%p buf=%p len=%d\n" "rank=%d buf=%p len=%zu name=%s mapped=%p\n", g_state.rank, runner, buf, len, runner->rank, runner->buf, runner->len, runner->name, runner->mapped); #endif } runner = runner->next; } #ifndef NDEBUG /* we CMX_ASSERT that the found entry was unique */ while (runner) { if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { #if DEBUG printf("[%d] reg_cache_find duplicate found\n" "reg_entry=%p buf=%p len=%d\n" "rank=%d buf=%p len=%zu name=%s mapped=%p\n", g_state.rank, runner, buf, len, runner->rank, runner->buf, runner->len, runner->name, runner->mapped); #endif CMX_ASSERT(0); } runner = runner->next; } #endif return entry; } /** * Locate a registration cache entry which intersects the given segment. * * @param[in] rank rank of the process * @param[in] buf starting address of the buffer * @parma[in] len length of the buffer * * @pre 0 <= rank && rank < reg_nprocs * @pre reg_cache_init() was previously called * * @return the reg cache entry, or NULL on failure */ reg_entry_t* reg_cache_find_intersection(int rank, void *buf, size_t len) { reg_entry_t *entry = NULL; reg_entry_t *runner = NULL; #if DEBUG printf("[%d] reg_cache_find_intersection(rank=%d, buf=%p, len=%d)\n", g_state.rank, rank, buf, len); #endif /* preconditions */ CMX_ASSERT(NULL != reg_cache); CMX_ASSERT(0 <= rank && rank < reg_nprocs); runner = reg_cache[rank]; while (runner && NULL == entry) { if (RR_SUCCESS == reg_entry_intersects(runner, buf, len)) { entry = runner; } runner = runner->next; } /* we CMX_ASSERT that the found entry was unique */ while (runner) { if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { CMX_ASSERT(0); } runner = runner->next; } return entry; } /** * Create a new registration entry based on the given members. * * @pre 0 <= rank && rank < reg_nprocs * @pre NULL != buf * @pre 0 <= len * @pre reg_cache_init() was previously called * @pre NULL == reg_cache_find(rank, buf, 0) * @pre NULL == reg_cache_find_intersection(rank, buf, 0) * * @return RR_SUCCESS on success */ reg_entry_t* reg_cache_insert(int rank, void *buf, size_t len, const char *name, void *mapped, int use_dev #if USE_SICM #if SICM_OLD ,sicm_device *device #else ,sicm_device_list device #endif #endif ) { reg_entry_t *node = NULL; #if DEBUG printf("[%d] reg_cache_insert(rank=%d, buf=%p, len=%ld, name=%s, mapped=%p)\n", g_state.rank, rank, buf, len, name, mapped); #endif /* preconditions */ CMX_ASSERT(NULL != reg_cache); CMX_ASSERT(0 <= rank && rank < reg_nprocs); CMX_ASSERT(NULL != buf); CMX_ASSERT(len >= 0); CMX_ASSERT(NULL == reg_cache_find(rank, buf, len)); CMX_ASSERT(NULL == reg_cache_find_intersection(rank, buf, len)); /* allocate the new entry */ node = (reg_entry_t *)malloc(sizeof(reg_entry_t)); CMX_ASSERT(node); /* initialize the new entry */ node->rank = rank; node->buf = buf; node->len = len; node->use_dev = use_dev; (void)memcpy(node->name, name, SHM_NAME_SIZE); node->mapped = mapped; node->next = NULL; #if USE_SICM node->device = device; #endif /* push new entry to tail of linked list */ if (NULL == reg_cache[rank]) { reg_cache[rank] = node; } else { reg_entry_t *runner = reg_cache[rank]; while (runner->next) { runner = runner->next; } runner->next = node; } return node; } /** * Removes the reg cache entry associated with the given rank and buffer. * * If this process owns the buffer, it will unregister the buffer, as well. * * @param[in] rank * @param[in] buf * * @pre 0 <= rank && rank < reg_nprocs * @pre NULL != buf * @pre reg_cache_init() was previously called * @pre NULL != reg_cache_find(rank, buf, 0) * * @return RR_SUCCESS on success * RR_FAILURE otherwise */ reg_return_t reg_cache_delete(int rank, void *buf) { reg_return_t status = RR_FAILURE; reg_entry_t *runner = NULL; reg_entry_t *previous_runner = NULL; #if DEBUG printf("[%d] reg_cache_delete(rank=%d, buf=%p)\n", g_state.rank, rank, buf); #endif /* preconditions */ CMX_ASSERT(NULL != reg_cache); CMX_ASSERT(0 <= rank && rank < reg_nprocs); CMX_ASSERT(NULL != buf); CMX_ASSERT(NULL != reg_cache_find(rank, buf, 0)); /* this is more restrictive than reg_cache_find() in that we locate * exactlty the same region starting address */ runner = reg_cache[rank]; while (runner) { if (runner->buf == buf) { break; } previous_runner = runner; runner = runner->next; } /* we should have found an entry */ if (NULL == runner) { CMX_ASSERT(0); return RR_FAILURE; } /* pop the entry out of the linked list */ if (previous_runner) { previous_runner->next = runner->next; } else { reg_cache[rank] = reg_cache[rank]->next; } status = reg_entry_destroy(rank, runner); return status; } reg_return_t reg_cache_nullify(reg_entry_t *node) { #if DEBUG printf("[%d] reg_cache_nullify(node=%p)\n", g_state.rank, node); #endif node->next = NULL; node->buf = NULL; node->len = 0; node->mapped = NULL; node->rank = -1; node->use_dev = 0; (void)memset(node->name, 0, SHM_NAME_SIZE); return RR_SUCCESS; } ga-5.9.2/cmx/src-mpi-pr/reg_cache.h000066400000000000000000000031051500715745200167540ustar00rootroot00000000000000#ifndef _REG_CACHE_H_ #define _REG_CACHE_H_ #if USE_SICM #include //#include #endif #include /** * Enumerate the return codes for registration cache functions. */ typedef enum _reg_return_t { RR_SUCCESS=0, /**< success */ RR_FAILURE /**< non-specific failure */ } reg_return_t; /** * A registered contiguous memory region. */ typedef struct _reg_entry_t { struct _reg_entry_t *next; /**< next memory region in list */ void *buf; /**< starting address of region */ size_t len; /**< length of region */ void *mapped; /**< starting address of mmap'd region */ int rank; /**< rank where this region lives */ char name[SHM_NAME_SIZE]; /**< name of region */ int use_dev; /**< memory is on a device */ #if USE_SICM #if SICM_OLD sicm_device *device; /**< pointer to SICM device */ #else sicm_device_list device; /**< pointer to SICM device */ #endif #endif } reg_entry_t; /* functions * * documentation is in the *.c file */ reg_return_t reg_cache_init(int nprocs); reg_return_t reg_cache_destroy(); reg_entry_t *reg_cache_find(int rank, void *buf, size_t len); reg_entry_t *reg_cache_insert(int rank, void *buf, size_t len, const char *name, void *mapped, int use_dev #if USE_SICM #if SICM_OLD ,sicm_device *device #else ,sicm_device_list device #endif #endif ); reg_return_t reg_cache_delete(int rank, void *buf); reg_return_t reg_cache_nullify(reg_entry_t *entry); #endif /* _REG_CACHE_H_ */ ga-5.9.2/cmx/src-mpi-rma/000077500000000000000000000000001500715745200150425ustar00rootroot00000000000000ga-5.9.2/cmx/src-mpi-rma/cmx.c000066400000000000000000002453771500715745200160170ustar00rootroot00000000000000/* C and/or system headers */ #include #include #include #include #include #include /* 3rd party headers */ #include /* Configuration options */ #define DEBUG 0 #define USE_MPI_REQUESTS /* #define USE_MPI_FLUSH_LOCAL #define USE_MPI_WIN_ALLOC */ #ifdef USE_MPI_FLUSH_LOCAL #define USE_MPI_REQUESTS #endif /* our headers */ #include "cmx.h" #include "cmx_impl.h" #include "groups.h" /* exported state */ local_state l_state; /* static state */ static int initialized=0; /* for cmx_initialized(), 0=false */ static char skip_lock=0; /* don't acquire or release lock */ /* static function declarations */ static void acquire_remote_lock(int proc); static void release_remote_lock(int proc); static inline void acc( int datatype, int count, void *get_buf, void *src_ptr, long src_idx, void *scale); /* definitions needed to implement mutexes */ MPI_Win *_mutex_list; int **_mutex_buf; int *_mutex_num; int _mutex_total; /* Maximum number of outstanding non-blocking requests */ /* static int nb_max_outstanding = CMX_MAX_NB_OUTSTANDING; */ typedef struct { MPI_Request request; MPI_Win win; int active; #ifdef USE_MPI_FLUSH_LOCAL int remote_proc; int tag; #endif } nb_t; static nb_t **nb_list = NULL; /* needed for complex accumulate */ typedef struct { double real; double imag; } DoubleComplex; typedef struct { float real; float imag; } SingleComplex; /* Find first available non-blocking handle */ #ifdef ZUSE_MPI_REQUESTS void get_nb_request(cmx_request_t *handle, nb_t **req) { int i; for (i=0; iactive == 0) break; } if (igroup, 1, &world_rank, group, local_rank); if (status != MPI_SUCCESS) { translate_mpi_error(status,"get_local_rank_from_win:MPI_Group_translate_ranks"); cmx_error("MPI_Group_translate_ranks: Failed", status); } return CMX_SUCCESS; } int cmx_init() { int i, status; if (initialized) { return 0; } initialized = 1; /* Assert MPI has been initialized */ int init_flag; status = MPI_Initialized(&init_flag); assert(MPI_SUCCESS == status); assert(init_flag); /* Duplicate the World Communicator */ status = MPI_Comm_dup(MPI_COMM_WORLD, &(l_state.world_comm)); translate_mpi_error(status,"cmx_init:MPI_Comm_dup"); assert(MPI_SUCCESS == status); assert(l_state.world_comm); /* My Rank */ status = MPI_Comm_rank(l_state.world_comm, &(l_state.rank)); assert(MPI_SUCCESS == status); /* World Size */ status = MPI_Comm_size(l_state.world_comm, &(l_state.size)); assert(MPI_SUCCESS == status); /* groups */ cmx_group_init(); /* set mutex list equal to null */ _mutex_list = NULL; _mutex_buf = NULL; _mutex_num = NULL; _mutex_total = 0; /* initialize non-blocking handles */ #ifdef ZUSE_MPI_REQUESTS nb_list = (nb_t**)malloc(sizeof(nb_t*) * nb_max_outstanding); CMX_ASSERT(nb_list); for (i=0; iactive = 0; } #endif /* sync - initialize first communication epoch */ cmx_fence_all(CMX_GROUP_WORLD); /* Synch - Sanity Check */ MPI_Barrier(l_state.world_comm); return CMX_SUCCESS; } int cmx_init_args(int *argc, char ***argv) { int init_flag; MPI_Initialized(&init_flag); if(!init_flag) { MPI_Init(argc, argv); } return cmx_init(); } int cmx_initialized() { return initialized; } void cmx_error(const char *msg, int code) { fprintf(stderr,"[%d] Received an Error in Communication: (%d) %s\n", l_state.rank, code, msg); MPI_Abort(l_state.world_comm, code); } /** * blocking contiguous put * @param src pointer to source buffer * @param dst_offset offset from start of data allocation on remote process * @param bytes length of data to be transferred * @param proc rank of destination processor * @param cmx_hdl handle for data allocation */ int cmx_put( void *src, cmxInt dst_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl) { MPI_Aint displ; int ierr; MPI_Win win = cmx_hdl.win; #ifdef USE_MPI_REQUESTS MPI_Request request; MPI_Status status; #endif displ = (MPI_Aint)(dst_offset); /** * All processes called MPI_WIN_LOCK_ALL on the window (win) used for * these calls. This call: * * Starts an RMA access epoch to all processors in win with a lock of * type MPI_LOCK_SHARED. During the epoch, the calling process can access * the window memory on all processes in win using RMA operations. * A window locked with MPI_WIN_LOCK_ALL must be unlocked with * MPI_WIN_UNLOCK_ALL. This routine is not collective. The ALL refers to a * lock on all members of the group of the window. * * Locks are used to protect accesses to the locked target window effected * by RMA calls issued between the lock and unlock calls, and to protect * load/store accesses to a locked local or shared memory window executed * between the lock and unlock calls. Accesses that are protected by an * exclusive lock will not be concurrent at the window site with other * accesses to the same window that are lock protected. Accesses that are * protected by a shared lock will not be concurrent at the window site with * accesses protected by an exclusive lock to the same window. * * A passive synchronization epoch is created by calling MPI_WIN_LOCK_ALL on * win */ /** * MPI_WIN_FLUSH completes all outstanding RMA operations initiated by the * calling process to the target rank on the specified window. The * operations are completed both at the origin and at the target */ #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(proc, reg_win->win); translate_mpi_error(ierr,"cmx_put:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL /** * The local communication buffer of an RMA call should not be updated, and * the local communication buffer of a get call should not be accessed after * the RMA call until the operation completes at the origin. */ ierr = MPI_Put(src, bytes, MPI_CHAR, proc, displ, bytes, MPI_CHAR, win); translate_mpi_error(ierr,"cmx_put:MPI_Put"); /** * MPI_WIN_FLUSH_LOCAL locally completes at the origin all outstanding RMA * operations initiated by the calling process to the target process * specified by the rank on the specified window. For example, after the * routine completes, the user may reused any buffers provided to put, get, * or accumulate operations */ ierr = MPI_Win_flush_local(proc, win); translate_mpi_error(ierr,"cmx_put:MPI_Win_flush_local"); #else /** * MPI_RPUT is similar to MPI_PUT, except that it allocates a communication * request object and associates it with the request handle. The completion * of an MPI_RPUT operation (i.e. after the corresponding test or wait) * indicates that the sender is now free to update the locations in the * origin buffer. It does not indicate that the data is available at the * target window. If remote completion is required, MPI_WIN_FLUSH, * MPI_WIN_FLUSH_ALL, MPI_WIN_UNLOCK or MPI_WIN_UNLOCK_ALL can be used. */ ierr = MPI_Rput(src, bytes, MPI_CHAR, proc, displ, bytes, MPI_CHAR, win, &request); translate_mpi_error(ierr,"cmx_put:MPI_Rput"); /** * A call to MPI_WAIT returns when the operation identified by the request * is complete. If the request is an active persistent request, it is marked * inactive. Any other type of request is and the request handle is set to * MPI_REQUEST_NULL. MPI_WAIT is a non-local operation */ ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"cmx_put:MPI_Wait"); #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,proc,0,win); translate_mpi_error(ierr,"cmx_put:MPI_Win_lock"); ierr = MPI_Put(src, bytes, MPI_CHAR, proc, displ, bytes, MPI_CHAR, win); translate_mpi_error(ierr,"cmx_put:MPI_Put"); ierr = MPI_Win_unlock(proc,win); translate_mpi_error(ierr,"cmx_put:MPI_Win_unlock"); #endif return CMX_SUCCESS; } /** * blocking contiguous get * @param dst pointer to source local buffer * @param src_offset offset from start of data allocation on remote process * @param bytes length of data to be transferred * @param proc rank of destination processor * @param cmx_hdl handle for data allocation */ int cmx_get( void *dst, cmxInt src_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl) { MPI_Aint displ; int ierr; MPI_Win win = cmx_hdl.win; #ifdef USE_MPI_REQUESTS MPI_Request request; MPI_Status status; #endif displ = (MPI_Aint)(src_offset); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(proc, reg_win->win); translate_mpi_error(ierr,"cmx_get:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Get(dst, bytes, MPI_CHAR, proc, displ, bytes, MPI_CHAR, win); translate_mpi_error(ierr,"cmx_get:MPI_Get"); ierr = MPI_Win_flush_local(proc, win); translate_mpi_error(ierr,"cmx_get:MPI_Win_flush_local"); #else ierr = MPI_Rget(dst, bytes, MPI_CHAR, proc, displ, bytes, MPI_CHAR, win, &request); translate_mpi_error(ierr,"cmx_get:MPI_Rget"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"cmx_get:MPI_Wait"); #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,proc,0,win); translate_mpi_error(ierr,"cmx_get:MPI_Win_lock"); ierr = MPI_Get(dst, bytes, MPI_CHAR, proc, displ, bytes, MPI_CHAR, win); translate_mpi_error(ierr,"cmx_get:MPI_Get"); ierr = MPI_Win_unlock(proc,win); translate_mpi_error(ierr,"cmx_get:MPI_Win_unlock"); #endif return CMX_SUCCESS; } /** * blocking contiguous accumulate * @param op CMX op for accumulate operation * @param scale value to scale data by before accumulating it * @param src pointer to source buffer * @param dst_offset offset from start of data allocation on remote processor * @param bytes length of data to be transferred * @param proc rank of destination processors * @param cmx_hdl handle for data allocation */ int cmx_acc( int op, void *scale, void *src, cmxInt dst_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl) { MPI_Aint displ, count; void *buf_ptr; int i, ierr; MPI_Win win = cmx_hdl.win; #ifdef USE_MPI_REQUESTS MPI_Request request; MPI_Status status; #endif MPI_Datatype mpi_type; displ = (MPI_Aint)(dst_offset); if (op == CMX_ACC_INT) { int *buf; int *isrc = (int*)src; int iscale = *((int*)scale); count = (MPI_Aint)(bytes/sizeof(int)); buf = (int*)malloc(bytes); buf_ptr = buf; for (i=0; iwin); translate_mpi_error(ierr,"cmx_acc:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Accumulate(buf_ptr,count,mpi_type,proc,displ,count, mpi_type,MPI_SUM,win); translate_mpi_error(ierr,"cmx_acc:MPI_Accumulate"); ierr = MPI_Win_flush_local(proc, win); translate_mpi_error(ierr,"cmx_acc:MPI_Win_flush_local"); #else ierr = MPI_Raccumulate(buf_ptr,count,mpi_type,proc,displ,count, mpi_type,MPI_SUM,win,&request); translate_mpi_error(ierr,"cmx_acc:MPI_Raccumulate"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"cmx_acc:MPI_Wait"); #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,proc,0,win); translate_mpi_error(ierr,"cmx_acc:MPI_Win_lock"); ierr = MPI_Accumulate(buf_ptr,count,mpi_type,proc,displ,count, mpi_type,MPI_SUM,win); translate_mpi_error(ierr,"cmx_acc:MPI_Accumulate"); ierr = MPI_Win_unlock(proc,win); translate_mpi_error(ierr,"cmx_acc:MPI_Win_unlock"); #endif free(buf_ptr); return CMX_SUCCESS; } /** * No checking for data consistency. Assume correctness has already been * established elsewhere. Individual elements are assumed to be one byte in size * @param stride_array: physical dimensions of array * @param count: number of elements along each array dimension * @param levels: number of stride levels (should be one less than array dimension) * @param type: MPI_Datatype returned to calling program */ void strided_to_subarray_dtype(int *stride_array, int *count, int levels, MPI_Datatype base_type, MPI_Datatype *type) { int ndims, i, ierr; int array_of_sizes[7]; int array_of_starts[7]; int array_of_subsizes[7]; int stride; ierr = MPI_Type_size(base_type,&stride); translate_mpi_error(ierr,"strided_to_subarray_dtype:MPI_Type_size"); ndims = levels+1; /* the pointer to the local buffer points to the first data element in data exchange, not the origin of the local array, so all starts should be zero */ for (i=0; iwin); translate_mpi_error(ierr,"cmx_puts:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Put(src_ptr, 1, src_type, proc, displ, 1, dst_type, win); translate_mpi_error(ierr,"cmx_puts:MPI_Put"); ierr = MPI_Win_flush_local(proc, win); translate_mpi_error(ierr,"cmx_puts:MPI_Win_flush_local"); #else ierr = MPI_Rput(src_ptr, 1, src_type, proc, displ, 1, dst_type, win, &request); translate_mpi_error(ierr,"cmx_puts:MPI_Rput"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"cmx_puts:MPI_Wait"); #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,proc,0,win); translate_mpi_error(ierr,"cmx_puts:MPI_Win_lock"); ierr = MPI_Put(src_ptr, 1, src_type, proc, displ, 1, dst_type, win); translate_mpi_error(ierr,"cmx_puts:MPI_Put"); ierr = MPI_Win_unlock(proc,win); translate_mpi_error(ierr,"cmx_puts:MPI_Win_unlock"); #endif ierr = MPI_Type_free(&src_type); translate_mpi_error(ierr,"cmx_puts:MPI_Type_free"); ierr = MPI_Type_free(&dst_type); translate_mpi_error(ierr,"cmx_puts:MPI_Type_free"); return CMX_SUCCESS; } /** * blocking strided get * @param dst_ptr: pointer to origin of data on destination processor * @param dst_stride_ar: physical dimensions of destination array * @param src_offset: offset from start of data allocation on remote process * @param src_stride_ar: physical dimensions of array containing source data * @param count: array containing number of data points along each dimension. The 0 * dimension contains the actual length of contiguous segments in bytes (number * of elements times the size of each element). * @param stride_levels: this should be one less than the dimension of the array * @param proc: global rank of source array * @param cmx_hdl: handle for data allocation */ int cmx_gets( void *dst_ptr, cmxInt *dst_stride_ar, cmxInt src_offset, cmxInt *src_stride_ar, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl) { MPI_Datatype src_type, dst_type; MPI_Aint displ; int ierr; MPI_Win win = cmx_hdl.win; #ifdef USE_MPI_REQUESTS MPI_Request request; MPI_Status status; #endif /* If data is contiguous, use cmx_get */ if (stride_levels == 0) { return cmx_get(dst_ptr, src_offset, count[0], proc, cmx_hdl); } displ = (MPI_Aint)(src_offset); strided_to_subarray_dtype(src_stride_ar, count, stride_levels, MPI_BYTE, &src_type); strided_to_subarray_dtype(dst_stride_ar, count, stride_levels, MPI_BYTE, &dst_type); ierr = MPI_Type_commit(&src_type); translate_mpi_error(ierr,"cmx_gets:MPI_Type_commit"); ierr = MPI_Type_commit(&dst_type); translate_mpi_error(ierr,"cmx_gets:MPI_Type_commit"); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(proc, reg_win->win); translate_mpi_error(ierr,"cmx_gets:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Get(dst_ptr, 1, dst_type, proc, displ, 1, src_type, win); translate_mpi_error(ierr,"cmx_gets:MPI_Get"); ierr = MPI_Win_flush_local(proc, win); translate_mpi_error(ierr,"cmx_gets:MPI_Win_flush_local"); #else ierr = MPI_Rget(dst_ptr, 1, dst_type, proc, displ, 1, src_type, win, &request); translate_mpi_error(ierr,"cmx_gets:MPI_Rget"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"cmx_gets:MPI_Wait"); #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,proc,0,win); translate_mpi_error(ierr,"cmx_gets:MPI_Win_lock"); ierr = MPI_Get(dst_ptr, 1, dst_type, proc, displ, 1, src_type, win); translate_mpi_error(ierr,"cmx_gets:MPI_Get"); ierr = MPI_Win_unlock(proc,win); translate_mpi_error(ierr,"cmx_gets:MPI_Win_lock"); #endif ierr = MPI_Type_free(&src_type); translate_mpi_error(ierr,"cmx_gets:MPI_Type_free"); ierr = MPI_Type_free(&dst_type); translate_mpi_error(ierr,"cmx_gets:MPI_Type_free"); return CMX_SUCCESS; } /** * Utility function for allocating a buffer that can be used to scale strided * data before sending it to remote processor for accumulate operation. Data is * copied into buffer after allocation. * @param ptr: pointer to first member of data array * @param strides: physical dimensions of array containing data * @param count: array containing the number of data points along each dimension. * The 0 element contains the actual length of the contiguous segments in bytes * @param levels: this should be one less than the dimension of the array * @param size: size (in bytes) of allocated buffer * @param new_strides: stride array for newly allocated buffer */ void* malloc_strided_acc_buffer(void* ptr, cmxInt *strides, cmxInt *count, int levels, cmxInt *size, cmxInt *new_strides) { cmxInt index[7]; cmxInt lcnt[7]; cmxInt i, j, idx, stride, src_idx; void *new_ptr; char *cursor, *src_cursor; cmxInt n1dim; *size = 1; /* evaluate size of new array */ for (i=0; i<=levels; i++) { *size *= count[i]; } new_ptr = malloc(*size); cursor = (char*)new_ptr; stride = 1; /* calculate strides in new array */ n1dim = 1; for (i=0; i 0) index[levels-1] = idx; /* evaluate offset in source ptr */ src_idx = 0; if (levels > 0) stride = strides[0]; for (j=0; jwin); translate_mpi_error(ierr,"cmx_accs:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Accumulate(packbuf,1,src_type,proc,displ,1,dst_type, MPI_SUM,win); translate_mpi_error(ierr,"cmx_accs:MPI_Accumulate"); ierr = MPI_Win_flush_local(proc, win); translate_mpi_error(ierr,"cmx_accs:MPI_Win_flush_local"); #else ierr = MPI_Raccumulate(packbuf,1,src_type,proc,displ,1,dst_type, MPI_SUM,win,&request); translate_mpi_error(ierr,"cmx_accs:MPI_Raccumulate"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"cmx_accs:MPI_Wait"); #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,proc,0,win); translate_mpi_error(ierr,"cmx_accs:MPI_Win_lock"); ierr = MPI_Accumulate(packbuf,1,src_type,proc,displ,1,dst_type, MPI_SUM,win); translate_mpi_error(ierr,"cmx_accs:MPI_Accumulate"); ierr = MPI_Win_unlock(proc,win); translate_mpi_error(ierr,"cmx_accs:MPI_Win_unlock"); #endif ierr = MPI_Type_free(&src_type); translate_mpi_error(ierr,"cmx_accs:MPI_Type_free"); ierr = MPI_Type_free(&dst_type); translate_mpi_error(ierr,"cmx_accs:MPI_Type_free"); free(packbuf); return CMX_SUCCESS; } /** * Utility function to create MPI data type for vector data object * @param src_ptr: location of first data point in vector data object for source * @param iov: cmx vector struct containing data information * @param iov_len: number of elements in vector data object * @param base_type: MPI_Datatype of elements * @param type: returned MPI_Datatype corresponding to iov for source data * @param type: returned MPI_Datatype corresponding to iov for destination data */ void vector_to_struct_dtype(void* loc_ptr, cmx_giov_t *iov, int iov_len, MPI_Datatype base_type, MPI_Datatype *loc_type, MPI_Datatype *rem_type) { cmxInt i, j, size, ierr; cmxInt nelems, icnt; cmxInt *blocklengths; MPI_Aint *displacements; MPI_Datatype *types; MPI_Aint displ; MPI_Type_size(base_type,&size); /* find total number of elements */ nelems = 0; for (i=0; iwin); translate_mpi_error(ierr,"cmx_putv:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Put(src_ptr, 1, src_type, proc, displ, 1, dst_type, win); translate_mpi_error(ierr,"cmx_putv:MPI_Put"); ierr = MPI_Win_flush_local(proc, win); translate_mpi_error(ierr,"cmx_putv:MPI_Win_flush_local"); #else ierr = MPI_Rput(src_ptr, 1, src_type, proc, displ, 1, dst_type, win, &request); translate_mpi_error(ierr,"cmx_putv:MPI_Rput"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"cmx_putv:MPI_Wait"); #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,proc,0,win); translate_mpi_error(ierr,"cmx_putv:MPI_Win_lock"); ierr = MPI_Put(src_ptr, 1, src_type, proc, displ, 1, dst_type, win); translate_mpi_error(ierr,"cmx_putv:MPI_Put"); ierr = MPI_Win_unlock(proc,win); translate_mpi_error(ierr,"cmx_putv:MPI_Win_unlock"); #endif ierr = MPI_Type_free(&src_type); translate_mpi_error(ierr,"cmx_putv:MPI_Type_free"); ierr = MPI_Type_free(&dst_type); translate_mpi_error(ierr,"cmx_putv:MPI_Type_free"); return CMX_SUCCESS; } /** * Vector get operation * @param iov: descriptor array * @param iov_len: length of descriptor array * @param proc: remote processor id * @param cmx_hdl: handle for data allocation */ int cmx_getv( cmx_giov_t *iov, int iov_len, int proc, cmx_handle_t cmx_hdl) { MPI_Datatype src_type, dst_type; MPI_Aint displ; void *dst_ptr; int ierr; MPI_Win win = cmx_hdl.win; #ifdef USE_MPI_REQUESTS MPI_Request request; MPI_Status status; #endif dst_ptr = iov[0].loc[0]; displ = 0; vector_to_struct_dtype(dst_ptr, iov, iov_len, MPI_BYTE, &dst_type, &src_type); ierr = MPI_Type_commit(&src_type); translate_mpi_error(ierr,"cmx_getv:MPI_Type_commit"); ierr = MPI_Type_commit(&dst_type); translate_mpi_error(ierr,"cmx_getv:MPI_Type_commit"); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(proc, reg_win->win); translate_mpi_error(ierr,"cmx_getv:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Get(dst_ptr, 1, dst_type, proc, displ, 1, src_type, win); translate_mpi_error(ierr,"cmx_getv:MPI_Get"); ierr = MPI_Win_flush_local(proc, win); translate_mpi_error(ierr,"cmx_getv:MPI_Win_flush_local"); #else ierr = MPI_Rget(dst_ptr, 1, dst_type, proc, displ, 1, src_type, win, &request); translate_mpi_error(ierr,"cmx_getv:MPI_Rget"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"cmx_getv:MPI_Wait"); #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,proc,0,win); translate_mpi_error(ierr,"cmx_getv:MPI_Win_lock"); ierr = MPI_Get(dst_ptr, 1, dst_type, proc, displ, 1, src_type, win); translate_mpi_error(ierr,"cmx_getv:MPI_Get"); ierr = MPI_Win_unlock(proc,win); translate_mpi_error(ierr,"cmx_getv:MPI_Win_unlock"); #endif ierr = MPI_Type_free(&src_type); translate_mpi_error(ierr,"cmx_getv:MPI_Type_free"); ierr = MPI_Type_free(&dst_type); translate_mpi_error(ierr,"cmx_getv:MPI_Type_free"); return CMX_SUCCESS; } /** * Utility function to create MPI data type for vector data object used in * vector accumulate operation. This also handles creation of a temporary buffer * to handle scaling of the accumulated values. The returned buffer must be * deallocated using free * @param loc_ptr: location of first data point in vector data object for * local data * @param iov: cmx vector struct containing data information * @param iov_len: number of elements in vector data object * @param base_type: MPI_Datatype of elements * @param src_type: returned MPI_Datatype corresponding to iov for source data * @param dst_type: returned MPI_Datatype corresponding to iov for destination data */ void* create_vector_buf_and_dtypes(void *loc_ptr, cmx_giov_t *iov, int iov_len, int cmx_size, void *scale, MPI_Datatype base_type, MPI_Datatype *src_type, MPI_Datatype *dst_type) { int i, j, size, ierr; int nelems, icnt, ratio; void* src_buf; int *blocklengths; MPI_Aint *displacements; MPI_Datatype *types; MPI_Aint displ; MPI_Type_size(base_type,&size); /* find total number of elements */ nelems = 0; for (i=0; iwin); translate_mpi_error(ierr,"cmx_accv:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Accumulate(src_ptr,1,src_type,proc,displ,1,dst_type, MPI_SUM,win); translate_mpi_error(ierr,"cmx_accv:MPI_Accumulate"); ierr = MPI_Win_flush_local(proc, win); translate_mpi_error(ierr,"cmx_accv:MPI_Win_flush_local"); #else ierr = MPI_Raccumulate(src_ptr,1,src_type,proc,displ,1,dst_type, MPI_SUM,win,&request); translate_mpi_error(ierr,"cmx_accv:MPI_Raccumulate"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"cmx_accv:MPI_Wait"); #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,proc,0,win); translate_mpi_error(ierr,"cmx_accv:MPI_Win_lock"); ierr = MPI_Accumulate(src_ptr,1,src_type,proc,displ,1,dst_type, MPI_SUM,win); translate_mpi_error(ierr,"cmx_accv:MPI_Accumulate"); ierr = MPI_Win_unlock(proc,win); translate_mpi_error(ierr,"cmx_accv:MPI_Win_unlock"); #endif ierr = MPI_Type_free(&src_type); translate_mpi_error(ierr,"cmx_accv:MPI_Type_free"); ierr = MPI_Type_free(&dst_type); translate_mpi_error(ierr,"cmx_accv:MPI_Type_free"); free(src_ptr); return CMX_SUCCESS; } /** * Flush all outgoing messages to all procs in group * @param group: group containing processors */ int cmx_fence_all(cmx_group_t group) { return cmx_wait_all(group); } /** * Flush all messages from me going to processor proc * @param proc: target processor * @param group: processor group */ int cmx_fence_proc(int proc, cmx_group_t group) { cmx_igroup_t *igroup = group; win_link_t *curr_win; int ierr; if (igroup != NULL) { curr_win = igroup->win_list; while (curr_win != NULL) { ierr = MPI_Win_flush(proc, curr_win->win); translate_mpi_error(ierr,"cmx_fence_proc:MPI_Win_flush"); curr_win = curr_win->next; } } } /** * cmx_barrier is cmx_fence_all + MPI_Barrier * @param group: processor group */ int cmx_barrier(cmx_group_t group) { MPI_Comm comm; cmx_fence_all(group); MPI_Barrier(group->comm); return CMX_SUCCESS; } /** * local allocation of registered memory * @param size: size in bytes of requested segment */ void *cmx_malloc_local(size_t size) { void *ptr; if (size == 0) { ptr = NULL; } else { MPI_Aint tsize; int ierr; tsize = size; ierr = MPI_Alloc_mem(tsize,MPI_INFO_NULL,&ptr); translate_mpi_error(ierr,"cmx_malloc_local:MPI_Alloc_mem"); } return ptr; } /** * free segment of registered memory * @param ptr: pointer to start of registered memory segment */ int cmx_free_local(void *ptr) { if (ptr != NULL) { int ierr; ierr = MPI_Free_mem(ptr); translate_mpi_error(ierr,"cmx_free_local:MPI_Free_mem"); } return CMX_SUCCESS; } /** * Terminate cmx and clean up resources */ int cmx_finalize() { int i, ierr; /* it's okay to call multiple times -- extra calls are no-ops */ if (!initialized) { return CMX_SUCCESS; } initialized = 0; /* Make sure that all outstanding operations are done */ cmx_wait_all(CMX_GROUP_WORLD); /* groups */ cmx_group_finalize(); MPI_Barrier(l_state.world_comm); /* destroy the communicators */ #if 1 ierr = MPI_Comm_free(&l_state.world_comm); translate_mpi_error(ierr,"cmx_finalize:MPI_Comm_free"); #endif /* Clean up request list */ #ifdef ZUSE_MPI_REQUESTS for (i=0; iremote_proc,req->win); translate_mpi_error(ierr,"cmx_wait:MPI_Win_flush_local"); #else MPI_Status status; ierr = MPI_Wait(&(req->request),&status); translate_mpi_error(ierr,"cmx_wait:MPI_Wait"); #endif req->active = 0; #else /* Non-blocking functions not implemented */ #endif return CMX_SUCCESS; } /** * Check compoletion status of non-blocking request. Returns true if request * has completed * @param req: request handle * @param status: returns 0 if complete, 1 otherwise */ int cmx_test(cmx_request_t *req, int *status) { #ifdef USE_MPI_REQUESTS int flag; MPI_Status stat; MPI_Test(&req->request,&flag,&stat); if (flag) { *status = 0; } else { *status = 1; } return CMX_SUCCESS; #else *status = 0; return CMX_SUCCESS; #endif } /** * Wait for all non-blocking operations on the group to finish locally * @param proc: processor group */ int cmx_wait_all(cmx_group_t group) { cmx_igroup_t *igroup = group; win_link_t *curr_win; if (igroup != NULL) { curr_win = igroup->win_list; while (curr_win != NULL) { #ifdef USE_MPI_REQUESTS MPI_Win_flush_all(curr_win->win); #else MPI_Win_fence(0,curr_win->win); #endif curr_win = curr_win->next; } return CMX_SUCCESS; } return CMX_FAILURE; } /** * Non-blocking contiguous put * * @param src: pointer to source buffer * @param dst_offset: offset from start of data allocation on remote processor * @param bytes: number of bytes in data transfer * @param proc: remote processor id * @param cmx_hdl: handle for data allocation * @param req: non-blocking request handle */ int cmx_nbput( void *src, cmxInt dst_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl, cmx_request_t *req) { #ifdef USE_MPI_REQUESTS if (req == NULL) { return cmx_put(src, dst_offset, bytes, proc, cmx_hdl); } MPI_Aint displ; void *ptr; int ierr; MPI_Win win = cmx_hdl.win; MPI_Request request; ptr = cmx_hdl.buf; displ = (MPI_Aint)(dst_offset); #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Put(src, bytes, MPI_CHAR, proc, displ, bytes, MPI_CHAR, win); translate_mpi_error(ierr,"cmx_nbput:MPI_Put"); req->remote_proc = proc; req->win = win; #else ierr = MPI_Rput(src, bytes, MPI_CHAR, proc, displ, bytes, MPI_CHAR, win, &request); translate_mpi_error(ierr,"cmx_nbput:MPI_Rput"); #endif req->request = request; req->active = 1; return CMX_SUCCESS; #else return cmx_put(src, dst_offset, bytes, proc, cmx_hdl); #endif } /** * Non-blocking contiguous put * * @param dst: pointer to local buffer * @param src_offset: offset from start of data allocation on remote processor * @param bytes: number of bytes in data transfer * @param proc: remote processor id * @param cmx_hdl: handle for data allocation * @param req: non-blocking request handle */ int cmx_nbget( void *dst, cmxInt src_offset, int bytes, int proc, cmx_handle_t cmx_hdl, cmx_request_t *req) { #ifdef USE_MPI_REQUESTS if (req == NULL) { return cmx_get(dst, src_offset, bytes, proc, cmx_hdl); } MPI_Aint displ; void *ptr; int ierr; MPI_Win win = cmx_hdl.win; MPI_Request request; ptr = cmx_hdl.buf; displ = (MPI_Aint)(src_offset); #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Get(dst, bytes, MPI_CHAR, proc, displ, bytes, MPI_CHAR, win); translate_mpi_error(ierr,"cmx_nbget:MPI_Get"); req->remote_proc = proc; req->win = win; #else ierr = MPI_Rget(dst, bytes, MPI_CHAR, proc, displ, bytes, MPI_CHAR, win, &request); translate_mpi_error(ierr,"cmx_nbget:MPI_Rget"); #endif req->request = request; req->active = 1; return CMX_SUCCESS; #else return cmx_get(dst, src_offset, bytes, proc, cmx_hdl); #endif } /** * Non-blocking contiguous accumulate * * @param op: operation * @param scale: scale factor x += scale*y * @param src_ptr: pointer to source buffer * @param dst_offset: offset from start of data allocation on remote processor * @param bytes: number of bytes in data transfer * @param proc: remote processor id * @param cmx_hdl: handle for data allocation * @param req: non-blocking request handle */ int cmx_nbacc( int op, void *scale, void *src_ptr, cmxInt dst_offset, cmxInt bytes, int proc, cmx_handle_t cmx_hdl, cmx_request_t *req) { #ifdef USE_MPI_REQUESTS if (req == NULL) { return cmx_acc( op, scale, src_ptr, dst_offset, bytes, proc, cmx_hdl); } MPI_Aint displ; void *ptr; int count, i, ierr; MPI_Win win = cmx_hdl.win; MPI_Request request; MPI_Status status; ptr = cmx_hdl.buf; displ = (MPI_Aint)(dst_offset); if (op == CMX_ACC_INT) { int *buf; int *isrc = (int*)src_ptr; int iscale = *((int*)scale); count = bytes/sizeof(int); buf = (int*)malloc(bytes); for (i=0; iremote_proc = proc; req->win = win; #else ierr = MPI_Raccumulate(buf,count,MPI_INT,proc,displ,count, MPI_INT,MPI_SUM,win,&request); translate_mpi_error(ierr,"cmx_nbacc:MPI_Raccumulate"); #endif req->request = request; req->active = 1; free(buf); } else if (op == CMX_ACC_LNG) { long *buf; long *lsrc = (long*)src_ptr; long lscale = *((long*)scale); count = bytes/sizeof(long); buf = (long*)malloc(bytes); for (i=0; iremote_proc = proc; req->win = win; #else ierr = MPI_Raccumulate(buf,count,MPI_LONG,proc,displ,count, MPI_LONG,MPI_SUM,win,&request); translate_mpi_error(ierr,"cmx_nbacc:MPI_Raccumulate"); #endif req->request = request; req->active = 1; free(buf); } else if (op == CMX_ACC_FLT) { float *buf; float *fsrc = (float*)src_ptr; float fscale = *((float*)scale); count = bytes/sizeof(float); buf = (float*)malloc(bytes); for (i=0; iremote_proc = proc; req->win = win; #else ierr = MPI_Raccumulate(buf,count,MPI_FLOAT,proc,displ,count, MPI_FLOAT,MPI_SUM,win,&request); translate_mpi_error(ierr,"cmx_nbacc:MPI_Raccumulate"); req->request = request; #endif req->active = 1; free(buf); } else if (op == CMX_ACC_DBL) { double *buf; double *dsrc = (double*)src_ptr; double dscale = *((double*)scale); count = bytes/sizeof(double); buf = (double*)malloc(bytes); for (i=0; iremote_proc = proc; req->win = win; #else ierr = MPI_Raccumulate(buf,count,MPI_DOUBLE,proc,displ,count, MPI_DOUBLE,MPI_SUM,win,&request); translate_mpi_error(ierr,"cmx_nbacc:MPI_Raccumulate"); req->request = request; #endif req->active = 1; free(buf); } else if (op == CMX_ACC_CPL) { int cnum; float *buf; float *csrc = (float*)src_ptr; float crscale = *((float*)scale); float ciscale = *((float*)scale+1); count = bytes/sizeof(float); cnum = count/2; buf = (float*)malloc(bytes); for (i=0; iremote_proc = proc; req->win = win; #else ierr = MPI_Raccumulate(buf,count,MPI_FLOAT,proc,displ,count, MPI_FLOAT,MPI_SUM,win,&request); translate_mpi_error(ierr,"cmx_nbacc:MPI_Raccumulate"); #endif req->request = request; req->active = 1; free(buf); } else if (op == CMX_ACC_DCP) { int cnum; double *buf; double *csrc = (double*)src_ptr; double crscale = *((double*)scale); double ciscale = *((double*)scale+1); count = bytes/sizeof(double); cnum = count/2; buf = (double*)malloc(bytes); for (i=0; iremote_proc = proc; req->win = win; #else ierr = MPI_Raccumulate(buf,count,MPI_DOUBLE,proc,displ,count, MPI_DOUBLE,MPI_SUM,win,&request); translate_mpi_error(ierr,"cmx_nbacc:MPI_Raccumulate"); #endif req->request = request; req->active = 1; free(buf); } else { assert(0); } return CMX_SUCCESS; #else return cmx_acc( op, scale, src_ptr, dst_offset, bytes, proc, cmx_hdl); #endif } /** * non-blocking strided put * @param src: pointer to origin of data on source processor * @param src_stride: physical dimensions of array containing source data * @param dst_offset: offset from start of data allocation on remote process * @param dst_stride: physical dimensions of destination array * @param count: array containing number of data points along each dimension. The 0 * dimension contains the actual length of contiguous segments in bytes (number * of elements times the size of each element). * @param stride_levels: this should be one less than the dimension of the array * @param proc: global rank of destination array * @param cmx_hdl: handle for data allocation * @param req: non-blocking request handle */ int cmx_nbputs( void *src, cmxInt *src_stride, cmxInt dst_offset, cmxInt *dst_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl, cmx_request_t *req) { #ifdef USE_MPI_REQUESTS if (req == NULL) { return cmx_puts(src, src_stride, dst_offset, dst_stride, count, stride_levels, proc, cmx_hdl); } MPI_Datatype src_type, dst_type; MPI_Aint displ; void *ptr; int ierr; MPI_Win win = cmx_hdl.win; MPI_Request request; MPI_Status status; ptr = cmx_hdl.buf; displ = (MPI_Aint)(dst_offset); strided_to_subarray_dtype(src_stride, count, stride_levels, MPI_BYTE, &src_type); strided_to_subarray_dtype(dst_stride, count, stride_levels, MPI_BYTE, &dst_type); MPI_Type_commit(&src_type); MPI_Type_commit(&dst_type); #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Put(src, 1, src_type, proc, displ, 1, dst_type, win); translate_mpi_error(ierr,"cmx_nbputs:MPI_Put"); req->remote_proc = proc; req->win = win; #else ierr = MPI_Rput(src, 1, src_type, proc, displ, 1, dst_type, win, &request); translate_mpi_error(ierr,"cmx_nbputs:MPI_Rput"); #endif req->request = request; req->active = 1; MPI_Type_free(&src_type); MPI_Type_free(&dst_type); return CMX_SUCCESS; #else return cmx_puts(src, src_stride, dst_offset, dst_stride, count, stride_levels, proc, cmx_hdl); #endif } /** * non-blocking strided get * @param dst_ptr: pointer to origin of data on destination processor * @param dst_stride: physical dimensions of destination array * @param src_offset: offset from start of data allocation on remote process * @param src_stride: physical dimensions of array containing source data * @param count: array containing number of data points along each dimension. The 0 * dimension contains the actual length of contiguous segments in bytes (number * of elements times the size of each element). * @param stride_levels: this should be one less than the dimension of the array * @param proc: global rank of source array * @param cmx_hdl: handle for data allocation * @param req: non-blocking request handle */ int cmx_nbgets( void *dst, cmxInt *dst_stride, cmxInt src_offset, cmxInt *src_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl, cmx_request_t *req) { #ifdef USE_MPI_REQUESTS if (req == NULL) { return cmx_gets(dst, dst_stride, src_offset, src_stride, count, stride_levels, proc, cmx_hdl); } MPI_Datatype src_type, dst_type; MPI_Aint displ; void *ptr; int ierr; MPI_Win win = cmx_hdl.win; MPI_Request request; MPI_Status status; ptr = cmx_hdl.buf; displ = (MPI_Aint)(src_offset); strided_to_subarray_dtype(src_stride, count, stride_levels, MPI_BYTE, &src_type); strided_to_subarray_dtype(dst_stride, count, stride_levels, MPI_BYTE, &dst_type); MPI_Type_commit(&src_type); MPI_Type_commit(&dst_type); #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Get(dst, 1, dst_type, proc, displ, 1, src_type, win); translate_mpi_error(ierr,"cmx_nbgets:MPI_Get"); req->remote_proc = proc; req->win = win; #else ierr = MPI_Rget(dst, 1, dst_type, proc, displ, 1, src_type, win, &request); translate_mpi_error(ierr,"cmx_nbgets:MPI_Rget"); #endif req->request = request; req->active = 1; MPI_Type_free(&src_type); MPI_Type_free(&dst_type); return CMX_SUCCESS; #else return cmx_gets(dst, dst_stride, src_offset, src_stride, count, stride_levels, proc, cmx_hdl); #endif } /** * non-blocking strided accumulate * @param op: operation * @param scale: scale factor x += scale*y * @param src: pointer to origin of data on source processor * @param src_stride: physical dimensions of array containing source data * @param dst_offset: offset from start of data allocation on remote process * @param dst_stride: physical dimensions of destination array * @param count: array containing number of data points along each dimension. The 0 * dimension contains the actual length of contiguous segments in bytes (number * of elements times the size of each element). * @param stride_levels: this should be one less than the dimension of the array * @param proc: global rank of destination array * @param cmx_hdl: handle for data allocation * @param req: non-blocking request handle */ int cmx_nbaccs( int op, void *scale, void *src, cmxInt *src_stride, cmxInt dst_offset, cmxInt *dst_stride, cmxInt *count, int stride_levels, int proc, cmx_handle_t cmx_hdl, cmx_request_t *req) { #ifdef USE_MPI_REQUESTS if (req == NULL) { return cmx_accs(op, scale, src, src_stride, dst_offset, dst_stride, count, stride_levels, proc, cmx_hdl); } MPI_Datatype src_type, dst_type; MPI_Aint displ; void *ptr; int i, ierr; void *packbuf; int bufsize; int new_strides[7]; MPI_Win win = cmx_hdl.win; MPI_Request request; MPI_Status status; ptr = cmx_hdl.buf; displ = (MPI_Aint)(dst_offset); packbuf = malloc_strided_acc_buffer(src, src_stride, count, stride_levels, &bufsize, new_strides); if (op == CMX_ACC_INT) { int *buf; int iscale = *((int*)scale); int nvar = bufsize/sizeof(int); buf = (int*)packbuf; for (i=0; iremote_proc = proc; req->win = win; #else ierr = MPI_Raccumulate(packbuf,1,src_type,proc,displ,1,dst_type, MPI_SUM,win,&request); translate_mpi_error(ierr,"cmx_nbaccs:MPI_Raccumulate"); #endif req->request = request; req->active = 1; MPI_Type_free(&src_type); MPI_Type_free(&dst_type); free(packbuf); return CMX_SUCCESS; #else return cmx_accs(op, scale, src, src_stride, dst_offset, dst_stride, count, stride_levels, proc, cmx_hdl); #endif } /** * non-blocking vector put operation * @param iov: descriptor array * @param iov_len: length of descriptor array * @param proc: remote processor id * @param cmx_hdl: handle for data allocation * @param req: non-blocking request handle */ int cmx_nbputv( cmx_giov_t *iov, cmxInt iov_len, int proc, cmx_handle_t cmx_hdl, cmx_request_t* req) { #ifdef USE_MPI_REQUESTS MPI_Datatype src_type, dst_type; MPI_Aint displ; void *ptr, *src_ptr; int ierr; MPI_Win win = cmx_hdl.win; MPI_Request request; MPI_Status status; if (req == NULL) { return cmx_putv(iov, iov_len, proc, cmx_hdl); } src_ptr = iov[0].loc[0]; ptr = cmx_hdl.buf; displ = 0; vector_to_struct_dtype(src_ptr, iov, iov_len, MPI_BYTE, &src_type, &dst_type); MPI_Type_commit(&src_type); MPI_Type_commit(&dst_type); #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Put(src_ptr, 1, src_type, proc, displ, 1, dst_type, win); translate_mpi_error(ierr,"cmx_nbputv:MPI_Put"); req->remote_proc = proc; req->win = win; #else ierr = MPI_Rput(src_ptr, 1, src_type, proc, displ, 1, dst_type, win, &request); translate_mpi_error(ierr,"cmx_nbputv:MPI_Rput"); #endif req->request = request; req->active = 1; MPI_Type_free(&src_type); MPI_Type_free(&dst_type); return CMX_SUCCESS; #else return cmx_putv(iov, iov_len, proc, cmx_hdl); #endif } /** * non-blocking vector get operation * @param iov: descriptor array * @param iov_len: length of descriptor array * @param proc: remote processor id * @param cmx_hdl: handle for data allocation * @param req: non-blocking request handle */ int cmx_nbgetv( cmx_giov_t *iov, cmxInt iov_len, int proc, cmx_handle_t cmx_hdl, cmx_request_t* req) { #ifdef USE_MPI_REQUESTS MPI_Datatype src_type, dst_type; MPI_Aint displ; void *ptr, *dst_ptr; int ierr; MPI_Win win = cmx_hdl.win; MPI_Request request; MPI_Status status; if (req == NULL) { return cmx_getv(iov, iov_len, proc, cmx_hdl); } dst_ptr = iov[0].loc[0]; ptr = cmx_hdl.buf; displ = 0; vector_to_struct_dtype(ptr, iov, iov_len, MPI_BYTE, &src_type, &dst_type); MPI_Type_commit(&src_type); MPI_Type_commit(&dst_type); #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Get(dst_ptr, 1, dst_type, proc, displ, 1, src_type, win); translate_mpi_error(ierr,"cmx_nbgetv:MPI_Get"); req->remote_proc = proc; req->win = win; #else ierr = MPI_Rget(dst_ptr, 1, dst_type, proc, displ, 1, src_type, win, &request); translate_mpi_error(ierr,"cmx_nbgetv:MPI_Rget"); #endif req->request = request; req->active = 1; MPI_Type_free(&src_type); MPI_Type_free(&dst_type); return CMX_SUCCESS; #else return cmx_getv(iov, iov_len, proc, cmx_hdl); #endif } /** * non-blocking vector accumulate operation * @param op: operation * @param scale: scale factor x += scale*y * @param iov: descriptor array * @param iov_len: length of descriptor array * @param proc: remote processor id * @param cmx_hdl: handle for data allocation * @param req: non-blocking request handle */ int cmx_nbaccv( int op, void *scale, cmx_giov_t *iov, cmxInt iov_len, int proc, cmx_handle_t cmx_hdl, cmx_request_t* req) { #ifdef USE_MPI_REQUESTS MPI_Datatype src_type, dst_type; MPI_Aint displ; MPI_Datatype base_type; void *ptr, *src_ptr; MPI_Request request; MPI_Status status; MPI_Win win = cmx_hdl.win; int size, ierr; if (req == NULL) { return cmx_accv(op, scale, iov, iov_len, proc, cmx_hdl); } if (op == CMX_ACC_INT) { size = sizeof(int); base_type = MPI_INT; } else if (op == CMX_ACC_LNG) { size = sizeof(long); base_type = MPI_LONG; } else if (op == CMX_ACC_FLT) { size = sizeof(float); base_type = MPI_FLOAT; } else if (op == CMX_ACC_DBL) { size = sizeof(double); base_type = MPI_DOUBLE; } else if (op == CMX_ACC_CPL) { size = 2*sizeof(float); base_type = MPI_FLOAT; } else if (op == CMX_ACC_DCP) { size = 2*sizeof(double); base_type = MPI_DOUBLE; } ptr = cmx_hdl.buf; displ = 0; src_ptr = create_vector_buf_and_dtypes(ptr, iov, iov_len, size, scale, base_type, &src_type, &dst_type); MPI_Type_commit(&src_type); MPI_Type_commit(&dst_type); #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Accumulate(src_ptr,1,src_type,proc,displ,1,dst_type, MPI_SUM,win); translate_mpi_error(ierr,"cmx_nbaccv:MPI_Accumulate"); req->remote_proc = proc; req->win = win; #else ierr = MPI_Raccumulate(src_ptr,1,src_type,proc,displ,1,dst_type, MPI_SUM,win,&request); translate_mpi_error(ierr,"cmx_nbaccv:MPI_Raccumulate"); #endif req->request = request; req->active = 1; MPI_Type_free(&src_type); MPI_Type_free(&dst_type); free(src_ptr); return CMX_SUCCESS; #else return cmx_accv(op, scale, iov, iov_len, proc, cmx_hdl); #endif } /** * Read-modify_write atomic operation * @param op: operation * @param ploc: the value to update locally * @param rem_offset: offset to remote value * @param extra: amount to increment remote value * @param proc: rank of remote processor * @param cmx_hdl: handle for data allocation */ int cmx_rmw( int op, void *ploc, cmxInt rem_offset, int extra, int proc, cmx_handle_t cmx_hdl) { MPI_Aint displ; void *ptr; int ierr; MPI_Win win = cmx_hdl.win; ptr = cmx_hdl.buf; if (ptr == NULL) return CMX_FAILURE; displ = (MPI_Aint)(rem_offset); if (op == CMX_FETCH_AND_ADD) { int incr = extra; #ifdef USE_MPI_REQUESTS ierr = MPI_Fetch_and_op(&incr,ploc,MPI_INT,proc,displ, MPI_SUM,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_flush(proc,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Win_flush"); #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,proc,0,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Win_lock"); ierr = MPI_Fetch_and_op(&incr,ploc,MPI_INT,proc,displ, MPI_SUM,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_unlock(proc,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Win_unlock"); #endif } else if (op == CMX_FETCH_AND_ADD_LONG) { long incr = extra; #ifdef USE_MPI_REQUESTS ierr = MPI_Fetch_and_op(&incr,ploc,MPI_LONG,proc,displ, MPI_SUM,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_flush(proc,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Win_flush"); #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,proc,0,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Win_lock"); ierr = MPI_Fetch_and_op(&incr,ploc,MPI_LONG,proc,displ, MPI_SUM,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_unlock(proc,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Win_unlock"); #endif } else if (op == CMX_SWAP) { int incr = *((int*)ploc); #ifdef USE_MPI_REQUESTS ierr = MPI_Fetch_and_op(&incr,ploc,MPI_INT,proc,displ, MPI_REPLACE,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_flush(proc,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Win_flush"); #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,proc,0,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Win_lock"); ierr = MPI_Fetch_and_op(&incr,ploc,MPI_INT,proc,displ, MPI_REPLACE,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_unlock(proc,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Win_unlock"); #endif } else if (op == CMX_SWAP_LONG) { long incr = *((long*)ploc); #ifdef USE_MPI_REQUESTS ierr = MPI_Fetch_and_op(&incr,ploc,MPI_LONG,proc,displ, MPI_REPLACE,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_flush(proc,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Win_flush"); #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,proc,0,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Win_lock"); ierr = MPI_Fetch_and_op(&incr,ploc,MPI_LONG,proc,displ, MPI_REPLACE,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_unlock(proc,win); translate_mpi_error(ierr,"cmx_rmw:MPI_Win_unlock"); #endif } else { assert(0); } return CMX_SUCCESS; } /* Mutex Operations. These are implemented using an MPI-based algorithm described in R. Latham, R. Ross, R. Thakur, The International Journal of High Performance Computing Applications, vol. 21, pp. 132-143, (2007). The actual algorithm appears to have some errors, the code below is a combination of this paper and some examples from the web. */ /** * create some mutexes on this processor. This is a collective operation * on the world group but different processors may create different numbers * of mutexes * num: number of mutexes to create to create on this processor */ int cmx_create_mutexes(int num) { int i, j, k, idx, isize, nsize; cmx_igroup_t *igroup = NULL; MPI_Comm comm; int *sbuf; int me = l_state.rank; int nproc = l_state.size; igroup = CMX_GROUP_WORLD; comm = igroup->comm; sbuf = (int*)malloc(nproc*sizeof(int)); _mutex_num = (int*)malloc(nproc*sizeof(int)); for (i=0; i= _mutex_total) assert(0); waitlistcopy = (int*)malloc(nproc*sizeof(int)); /* Set value in the wait list */ MPI_Win_lock(MPI_LOCK_EXCLUSIVE,proc,0,_mutex_list[idx]); MPI_Get(waitlistcopy,nproc,MPI_INT,proc,0,nproc,MPI_INT,_mutex_list[idx]); MPI_Put(&lock,1,MPI_INT,proc,me*sizeof(int),1,MPI_INT,_mutex_list[idx]); MPI_Win_unlock(proc,_mutex_list[idx]); /* Check to see if lock is already held */ ok = 1; for (i=0; i= _mutex_total) assert(0); waitlistcopy = (int*)malloc(nproc*sizeof(int)); /* Set value in the wait list */ MPI_Win_lock(MPI_LOCK_EXCLUSIVE,proc,0,_mutex_list[idx]); MPI_Get(waitlistcopy,nproc,MPI_INT,proc,0,nproc,MPI_INT,_mutex_list[idx]); MPI_Put(&lock,1,MPI_INT,proc,me*sizeof(int),1,MPI_INT,_mutex_list[idx]); MPI_Win_unlock(proc,_mutex_list[idx]); /* Check to see if someone is waiting for lock */ ok = 1; for (i=0; icomm; assert(comm != MPI_COMM_NULL); MPI_Comm_rank(comm, &comm_rank); MPI_Comm_size(comm, &comm_size); #if DEBUG printf("[%d] cmx_malloc(ptrs=%p, size=%lu, group=%d)\n", comm_rank, ptrs, (long unsigned)bytes, group); #endif /* is this needed? */ /* cmx_barrier(group); */ /* allocate ret_entry_t object for this process */ handle->rank = comm_rank; handle->bytes = bytes; handle->comm = comm; handle->group = group; /* allocate and register segment. We need to allocate something even if size is zero so allocate a nominal amount so that routines don't break. Count on the fact that the length is zero to keep things from breaking down */ if (bytes > 0) { tsize = bytes; } else { tsize = 8; } #ifdef USE_MPI_WIN_ALLOC MPI_Win_allocate(sizeof(char)*tsize,1,MPI_INFO_NULL,comm,&(handle->buf), &(handle->win)); #else MPI_Alloc_mem(tsize,MPI_INFO_NULL,&(handle->buf)); MPI_Win_create(handle->buf,tsize,1,MPI_INFO_NULL,comm, &(handle->win)); #endif #ifdef USE_MPI_REQUESTS MPI_Win_lock_all(0,(handle->win)); #endif cmx_igroup_add_win(igroup,handle->win); cmx_wait_all(igroup); /* MPI_Win_fence(0,reg_entries[comm_rank].win); */ MPI_Barrier(comm); return CMX_SUCCESS; } /** * Access local buffer from CMX handle * @param handle CMX handle for data allocation * @param buf pointer to local buffer * @return CMX_SUCCESS on success */ int cmx_access(cmx_handle_t cmx_hdl, void **buf) { *buf = cmx_hdl.buf; return CMX_SUCCESS; } /** * Extact group object from CMX allocation handle * * @param handle CMX handle for data allocation * @param group CMX group associated with CMX data allocation * @return CMX_SUCCESS on success */ int cmx_get_group_from_handle(cmx_handle_t handle, cmx_group_t *group) { *group = handle.group; return CMX_SUCCESS; } int cmx_free(cmx_handle_t handle) { cmx_igroup_t *group = handle.group; MPI_Comm comm = MPI_COMM_NULL; MPI_Win window; int comm_rank; int comm_size; int i; comm = handle.comm; assert(comm != MPI_COMM_NULL); MPI_Comm_rank(comm, &comm_rank); MPI_Comm_size(comm, &comm_size); window = handle.win; /* Remove window from group list */ cmx_igroup_delete_win(group, window); /* free up window */ #ifdef USE_MPI_REQUESTS MPI_Win_unlock_all(window); #endif MPI_Win_free(&window); #ifndef USE_MPI_WIN_ALLOC /* Clear memory for this window */ MPI_Free_mem(handle.buf); #endif /* Is this needed? */ MPI_Barrier(comm); return CMX_SUCCESS; } static void acquire_remote_lock(int proc) { assert(0); } static void release_remote_lock(int proc) { assert(0); } static inline void acc( int datatype, int count, void *get_buf, void *src_ptr, long src_idx, void *scale) { #define EQ_ONE_REG(A) ((A) == 1.0) #define EQ_ONE_CPL(A) ((A).real == 1.0 && (A).imag == 0.0) #define IADD_REG(A,B) (A) += (B) #define IADD_CPL(A,B) (A).real += (B).real; (A).imag += (B).imag #define IADD_SCALE_REG(A,B,C) (A) += (B) * (C) #define IADD_SCALE_CPL(A,B,C) (A).real += ((B).real*(C).real) - ((B).imag*(C).imag);\ (A).imag += ((B).real*(C).imag) + ((B).imag*(C).real); #define ACC(WHICH, CMX_TYPE, C_TYPE) \ if (datatype == CMX_TYPE) { \ int m; \ int m_lim = count/sizeof(C_TYPE); \ C_TYPE *iterator = (C_TYPE *)get_buf; \ C_TYPE *value = (C_TYPE *)((char *)src_ptr + src_idx); \ C_TYPE calc_scale = *(C_TYPE *)scale; \ if (EQ_ONE_##WHICH(calc_scale)) { \ for (m = 0 ; m < m_lim; ++m) { \ IADD_##WHICH(iterator[m], value[m]); \ } \ } \ else { \ for (m = 0 ; m < m_lim; ++m) { \ IADD_SCALE_##WHICH(iterator[m], value[m], calc_scale); \ } \ } \ } else ACC(REG, CMX_ACC_DBL, double) ACC(REG, CMX_ACC_FLT, float) ACC(REG, CMX_ACC_INT, int) ACC(REG, CMX_ACC_LNG, long) ACC(CPL, CMX_ACC_DCP, DoubleComplex) ACC(CPL, CMX_ACC_CPL, SingleComplex) { assert(0); } #undef ACC #undef EQ_ONE_REG #undef EQ_ONE_CPL #undef IADD_REG #undef IADD_CPL #undef IADD_SCALE_REG #undef IADD_SCALE_CPL } ga-5.9.2/cmx/src-mpi-rma/cmx_impl.h000066400000000000000000000025451500715745200170310ustar00rootroot00000000000000#ifndef CMX_IMPL_H_ #define CMX_IMPL_H_ #include #include #include #define SHM_NAME_SIZE 20 typedef int cmxInt; typedef struct { MPI_Comm world_comm; int rank; int size; } local_state; typedef struct win_link { struct win_link *next; struct win_link *prev; MPI_Win win; } win_link_t; typedef struct group_link { struct group_link *next; MPI_Comm comm; MPI_Group group; win_link_t *win_list; } _cmx_group; typedef _cmx_group cmx_igroup_t; typedef struct { MPI_Win win; MPI_Comm comm; cmx_igroup_t *group; cmxInt bytes; int rank; void *buf; } _cmx_handle; typedef struct { MPI_Request request; MPI_Win win; int active; /* Last element is only needed by the win_flush_local implementation */ int remote_proc; } _cmx_request; typedef cmx_igroup_t* cmx_group_t; extern local_state l_state; extern cmx_group_t CMX_GROUP_WORLD; #define DEBUG 0 #define CMX_STRINGIFY(x) #x #ifdef NDEBUG #define CMX_ASSERT(WHAT) ((void) (0)) #else #define CMX_ASSERT(WHAT) \ ((WHAT) \ ? (void) (0) \ : cmx_assert_fail (CMX_STRINGIFY(WHAT), __FILE__, __LINE__, __func__)) #endif /* TODO: Problem with this function since cmx_error is defined in cmx.h * On the other hand, this function is currently not used */ #endif /* CMX_IMPL_H_ */ /* #define printf(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) */ ga-5.9.2/cmx/src-mpi-rma/groups.c000066400000000000000000000241211500715745200165250ustar00rootroot00000000000000#include #include #include "groups.h" #include "cmx.h" /* #define USE_MPI_ERRORS_RETURN */ /* the HEAD of the group linked list */ cmx_igroup_t *group_list = NULL; /* static functions implemented in this file */ static void cmx_create_group(cmx_igroup_t **group); static void cmx_igroup_finalize(cmx_igroup_t *group); cmx_igroup_t *CMX_GROUP_WORLD; /* Add a variable to keep track of world rank. Useful for debugging */ int _world_me; /** * Creates a cmx group object and puts it in the linked list of groups. Does not * assign properties to the group */ void cmx_create_group(cmx_igroup_t **group) { cmx_igroup_t *new_group_list_item = NULL; cmx_igroup_t *last_group_list_item = NULL; /* find the last group in the group linked list */ last_group_list_item = group_list; while (last_group_list_item->next != NULL) { last_group_list_item = last_group_list_item->next; } /* create, init, and insert the new node for the linked list */ new_group_list_item = (cmx_igroup_t*)malloc(sizeof(cmx_igroup_t)); new_group_list_item->comm = MPI_COMM_NULL; new_group_list_item->group = MPI_GROUP_NULL; new_group_list_item->next = NULL; new_group_list_item->win_list = NULL; last_group_list_item->next = new_group_list_item; /* return the group id */ *group = new_group_list_item; } int cmx_group_rank(cmx_igroup_t *group, int *rank) { int status; status = MPI_Group_rank(group->group, rank); if (status != MPI_SUCCESS) { cmx_error("MPI_Group_rank: Failed ", status); } return CMX_SUCCESS; } int cmx_group_size(cmx_igroup_t *group, int *size) { int status; status = MPI_Group_size(group->group, size); if (status != MPI_SUCCESS) { cmx_error("MPI_Group_size: Failed ", status); } return CMX_SUCCESS; } /** * Returns the MPI_Comm object backing the given group. * * @param group: group handle * @param comm the communicator handle */ int cmx_group_comm(cmx_igroup_t *group, MPI_Comm *comm) { *comm = group->comm; return CMX_SUCCESS; } int cmx_group_translate_ranks(int n, cmx_group_t group_from, int *ranks_from, cmx_group_t group_to, int *ranks_to) { int i; if (group_from == group_to) { for (i=0; igroup, n, ranks_from, group_to->group, ranks_to); if (status != MPI_SUCCESS) { cmx_error("MPI_Group_translate_ranks: Failed ", status); } } return CMX_SUCCESS; } int cmx_group_translate_world(cmx_igroup_t *group, int group_rank, int *world_rank) { if (CMX_GROUP_WORLD == group) { *world_rank = group_rank; } else { int status = MPI_Group_translate_ranks( group->group, 1, &group_rank, CMX_GROUP_WORLD->group, world_rank); if (status != MPI_SUCCESS) { cmx_error("MPI_Group_translate_ranks: Failed ", status); } } return CMX_SUCCESS; } /** * Destroys the given cmx group. */ void cmx_igroup_finalize(cmx_igroup_t *group) { int status; win_link_t *curr_win; win_link_t *next_win; assert(group); if (group->group != MPI_GROUP_NULL) { status = MPI_Group_free(&group->group); if (status != MPI_SUCCESS) { cmx_error("MPI_Group_free: Failed ", status); } } if (group->comm != MPI_COMM_NULL) { status = MPI_Comm_free(&group->comm); if (status != MPI_SUCCESS) { cmx_error("MPI_Comm_free: Failed ", status); } } /* Remove all windows associated with this group */ curr_win = group->win_list; while (curr_win != NULL) { next_win = curr_win->next; MPI_Win_free(&curr_win->win); free(curr_win); curr_win = next_win; } } int cmx_group_free(cmx_igroup_t *group) { cmx_igroup_t *current_group_list_item = group_list; cmx_igroup_t *previous_group_list_item = NULL; /* find the group to free */ while (current_group_list_item != NULL) { if (current_group_list_item == group) { break; } previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; } /* make sure we found a group */ assert(current_group_list_item != NULL); /* remove the group from the linked list */ if (previous_group_list_item != NULL) { previous_group_list_item->next = current_group_list_item->next; } /* free the group */ cmx_igroup_finalize(current_group_list_item); free(current_group_list_item); return CMX_SUCCESS; } int cmx_group_create( int n, int *pid_list, cmx_igroup_t *group_parent, cmx_igroup_t **group_child) { int status; int grp_me; cmx_igroup_t *igroup_child = NULL; MPI_Group *mpi_group_child = NULL; MPI_Comm *mpi_comm_child = NULL; cmx_igroup_t *igroup_parent = NULL; MPI_Group *mpi_group_parent = NULL; MPI_Comm *mpi_comm_parent = NULL; /* create the node in the linked list of groups and */ /* get the child's MPI_Group and MPI_Comm, to be populated shortly */ cmx_create_group(&igroup_child); mpi_group_child = &(igroup_child->group); mpi_comm_child = &(igroup_child->comm); /* get the parent's MPI_Group and MPI_Comm */ igroup_parent = group_parent; mpi_group_parent = &(igroup_parent->group); mpi_comm_parent = &(igroup_parent->comm); status = MPI_Group_incl(*mpi_group_parent, n, pid_list, mpi_group_child); if (status != MPI_SUCCESS) { cmx_error("MPI_Group_incl: Failed ", status); } { MPI_Comm comm, comm1, comm2; int lvl=1, local_ldr_pos; MPI_Group_rank(*mpi_group_child, &grp_me); if (grp_me == MPI_UNDEFINED) { /* FIXME: keeping the group around for now */ return CMX_SUCCESS; } /* SK: sanity check for the following bitwise operations */ assert(grp_me>=0); MPI_Comm_dup(MPI_COMM_SELF, &comm); /* FIXME: can be optimized away */ local_ldr_pos = grp_me; while(n>lvl) { int tag=0; int remote_ldr_pos = local_ldr_pos^lvl; if (remote_ldr_pos < n) { int remote_leader = pid_list[remote_ldr_pos]; MPI_Comm peer_comm = *mpi_comm_parent; int high = (local_ldr_posnext = NULL; group_list->win_list = NULL; #ifdef USE_MPI_ERRORS_RETURN MPI_Comm_set_errhandler(MPI_COMM_WORLD,MPI_ERRORS_RETURN); #endif /* save MPI world group and communicatior in CMX_GROUP_WORLD */ group_list->comm = l_state.world_comm; MPI_Comm_group(group_list->comm, &(group_list->group)); CMX_GROUP_WORLD = group_list; } void cmx_group_finalize() { cmx_igroup_t *current_group_list_item = group_list; cmx_igroup_t *previous_group_list_item = NULL; /* don't free the world group (the list head) */ current_group_list_item = current_group_list_item->next; while (current_group_list_item != NULL) { previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; cmx_igroup_finalize(previous_group_list_item); free(previous_group_list_item); } /* ok, now free the world group, but not the world comm */ MPI_Group_free(&(group_list->group)); free(group_list); group_list = NULL; } /** * Add an MPI window to the window list in the group */ void cmx_igroup_add_win(cmx_igroup_t *group, MPI_Win win) { cmx_igroup_t *igroup = group; win_link_t *curr_win = NULL; win_link_t *new_win = NULL; /* add window to group. Start by finding last member of window list */ curr_win = igroup->win_list; while (curr_win != NULL && curr_win->next != NULL) { curr_win = curr_win->next; } new_win = (win_link_t*)malloc(sizeof(win_link_t)); int rank; MPI_Comm_rank(group->comm,&rank); if (curr_win == NULL) { new_win->next = NULL; new_win->prev = NULL; new_win->win = win; igroup->win_list = new_win; } else { curr_win->next = new_win; new_win->next = NULL; new_win->prev = curr_win; new_win->win = win; } } /** * Remove an MPI window from the window list in the group. This only removes * the window from the group list, it does not get rid of the window itself. */ void cmx_igroup_delete_win(cmx_igroup_t *group, MPI_Win win) { cmx_igroup_t *current_group_list_item = group; win_link_t *curr_win; win_link_t *prev_win; /* find the window in list */ /* NOTE: Might be able to simplify this if win_link_t only has next and not * prev data members. Need to see if prev is used anywhere */ curr_win = current_group_list_item->win_list; if (curr_win != NULL) { while (curr_win->win != win) { curr_win = curr_win->next; } if (curr_win->prev != NULL && curr_win->next != NULL) { /* window is not at start or end of list */ prev_win = curr_win->prev; prev_win->next = curr_win->next; (curr_win->next)->prev = prev_win; } else if (curr_win->prev == NULL && curr_win->next != NULL) { /* window is at the start of the list */ (curr_win->next)->prev = NULL; current_group_list_item->win_list = curr_win->next; } else if (curr_win->next == NULL && curr_win->prev != NULL) { /* window is at the end of the list */ (curr_win->prev)->next = NULL; } else { /* only one window in list */ current_group_list_item->win_list = NULL; } free(curr_win); } } ga-5.9.2/cmx/src-mpi-rma/groups.h000066400000000000000000000007651500715745200165420ustar00rootroot00000000000000/** * Private header file for cmx groups backed by MPI_comm. * * The rest of the cmx group functions are defined in the public cmx.h. * * @author Bruce Palmer */ #ifndef _CMX_GROUPS_H_ #define _CMX_GROUPS_H_ #include #include "cmx_impl.h" #include "cmx.h" extern void cmx_group_init(); extern void cmx_group_finalize(); extern void cmx_igroup_add_win(cmx_igroup_t *group, MPI_Win win); extern void cmx_igroup_delete_win(cmx_igroup_t *group, MPI_Win win); #endif /* _CMX_GROUPS_H_ */ ga-5.9.2/cmx/testing/000077500000000000000000000000001500715745200143705ustar00rootroot00000000000000ga-5.9.2/cmx/testing/perf.c000066400000000000000000000061061500715745200154730ustar00rootroot00000000000000#include #include #include #include #include #include #include "cmx.h" static int me; static int nproc; #define PUT 0 #define GET 1 #define ACC 2 #define MAX_MESSAGE_SIZE 1024*1024 #define MEDIUM_MESSAGE_SIZE 8192 #define ITER_SMALL 100 #define ITER_LARGE 100 #define WARMUP 10 static void fill_array(double *arr, int count, int which); static void contig_test(cmxInt buffer_size, int op); double dclock() { struct timeval tv; gettimeofday(&tv, NULL); return(tv.tv_sec * 1.0e6 + (double)tv.tv_usec); } int main(int argc, char **argv) { cmx_init_args(&argc, &argv); cmx_group_rank(CMX_GROUP_WORLD, &me); cmx_group_size(CMX_GROUP_WORLD, &nproc); if (0 == me) { printf("msg size (bytes) avg time (us) avg b/w (MB/sec)\n"); } if (0 == me) { printf("CMX Put Test\n"); } contig_test(MAX_MESSAGE_SIZE, PUT); if (0 == me) { printf("CMX Get Test\n"); } contig_test(MAX_MESSAGE_SIZE, GET); if (0 == me) { printf("CMX Accumulate Test\n"); } contig_test(MAX_MESSAGE_SIZE, ACC); cmx_finalize(); MPI_Finalize(); return 0; } static void fill_array(double *arr, int count, int which) { int i; for (i = 0; i < count; i++) { arr[i] = i * 8.23 + which * 2.89; } } static void contig_test(cmxInt buffer_size, int op) { cmx_handle_t rem_hdl; void *put_buf; void *get_buf; double *times; int me, nproc; int participate = 1; cmx_group_rank(CMX_GROUP_WORLD, &me); cmx_group_size(CMX_GROUP_WORLD, &nproc); times = (double*)malloc(nproc * sizeof(double)); cmx_malloc(&rem_hdl, (cmxInt)buffer_size, CMX_GROUP_WORLD); put_buf = malloc(buffer_size); get_buf = malloc(buffer_size); /* initialize what we're putting */ fill_array((double*)put_buf, buffer_size/sizeof(double), me); cmxInt msg_size; /* set up senders and receivers */ if (nproc%2 != 0 && me == nproc-1) participate = 0; int dst = me + 1; double scale = 1.0; for (msg_size = 16; msg_size <= buffer_size; msg_size *= 2) { int j; int iter = msg_size > MEDIUM_MESSAGE_SIZE ? ITER_LARGE : ITER_SMALL; double t_start, t_end; if (me%2 == 0 && participate) { for (j= 0; j < iter + WARMUP; ++j) { if (WARMUP == j) { t_start = dclock(); } switch (op) { case PUT: cmx_put(put_buf, 0, msg_size, dst, rem_hdl); break; case GET: cmx_get(get_buf, 0, msg_size, dst, rem_hdl); break; case ACC: cmx_acc(CMX_ACC_DBL, &scale, put_buf, 0, msg_size, dst, rem_hdl); break; default: cmx_error("oops", 1); } } } /* calculate total time and average time */ t_end = dclock(); cmx_barrier(CMX_GROUP_WORLD); if (0 == me) { printf("%8zu\t\t%8.2f\t\t%10.2f\n", msg_size, ((t_end - t_start))/iter, msg_size*iter/((t_end - t_start))); } } cmx_free(rem_hdl); free(put_buf); free(get_buf); free(times); } ga-5.9.2/cmx/testing/perf_amo.c000066400000000000000000000065501500715745200163320ustar00rootroot00000000000000/* Test Rmw Performance * The number of processes are increases from 2 to the number of * processes present in the job */ #include #include #include #include #include #include #include "cmx.h" static int me; static int nproc; #define FETCH_AND_ADD 0 #define FETCH_AND_ADD_LONG 1 #define SWAP 2 #define SWAP_LONG 3 #define MAX_MESSAGE_SIZE 1024 #define MEDIUM_MESSAGE_SIZE 8192 #define ITER_SMALL 10000 #define ITER_LARGE 10000 #define WARMUP 20 static void fill_array(double *arr, int count, int which); static void rmw_test(size_t buffer_size, int op); double dclock() { struct timeval tv; gettimeofday(&tv, NULL); return(tv.tv_sec * 1.0e6 + (double)tv.tv_usec); } int main(int argc, char **argv) { cmx_init_args(&argc, &argv); cmx_group_rank(CMX_GROUP_WORLD, &me); cmx_group_size(CMX_GROUP_WORLD, &nproc); /* This test only works for two processes */ if (0 == me) { printf("#Processes avg time (us)\n"); printf("\n\n"); } if (0 == me) { printf("CMX Rmw-Fetch and Add Long Test\n"); printf("\n\n"); } rmw_test(MAX_MESSAGE_SIZE, FETCH_AND_ADD_LONG); if (0 == me) printf("\n\n"); if (0 == me) { printf("CMX Rmw-Fetch and Add Test\n"); } rmw_test(MAX_MESSAGE_SIZE, FETCH_AND_ADD); if (0 == me) printf("\n\n"); if (0 == me) { printf("CMX Rmw-Swap Long Test\n"); } rmw_test(MAX_MESSAGE_SIZE, SWAP_LONG); if (0 == me) printf("\n\n"); if (0 == me) { printf("CMX Rmw-Swap Test\n"); } rmw_test(MAX_MESSAGE_SIZE, SWAP); if (0 == me) printf("\n\n"); cmx_finalize(); MPI_Finalize(); return 0; } static void fill_array(double *arr, int count, int which) { int i; for (i = 0; i < count; i++) { arr[i] = i * 8.23 + which * 2.89; } } static void rmw_test(size_t buffer_size, int op) { void *put_buf; cmx_handle_t dst_hdl; double *times; int dst = 0; double t_start, t_end; int j; int iter = ITER_LARGE; int part_proc; times = (double*)malloc(nproc * sizeof(double)); put_buf = malloc(buffer_size); cmx_malloc(&dst_hdl, (cmxInt)buffer_size, CMX_GROUP_WORLD); /* initialize what we're putting */ fill_array((double*)put_buf, buffer_size/sizeof(double), me); /* All processes perform Rmw on process 0*/ for (part_proc = 2; part_proc <= nproc; part_proc *= 2) { if (me < part_proc) { for (j= 0; j < iter + WARMUP; ++j) { if (WARMUP == j) { t_start = dclock(); } switch (op) { case FETCH_AND_ADD: cmx_rmw(CMX_FETCH_AND_ADD, put_buf, 0, 1, dst, dst_hdl); break; case FETCH_AND_ADD_LONG: cmx_rmw(CMX_FETCH_AND_ADD_LONG, put_buf, 0, 1, dst, dst_hdl); break; case SWAP: cmx_rmw(CMX_SWAP, put_buf, 0, 1, dst, dst_hdl); break; case SWAP_LONG: cmx_rmw(CMX_SWAP_LONG, put_buf, 0, 1, dst, dst_hdl); break; default: cmx_error("oops", 1); } } } cmx_barrier(CMX_GROUP_WORLD); /* calculate total time and average time */ t_end = dclock(); if (0 == me) { printf("%5d\t\t%6.2f\n", part_proc, ((t_end - t_start))/iter); } } cmx_free(dst_hdl); free(times); free(put_buf); } ga-5.9.2/cmx/testing/perf_contig.c000066400000000000000000000057321500715745200170420ustar00rootroot00000000000000#include #include #include #include #include #include #include "cmx.h" static int me; static int nproc; #define PUT 0 #define GET 1 #define ACC 2 #define MAX_MESSAGE_SIZE 1024*1024 #define MEDIUM_MESSAGE_SIZE 8192 #define ITER_SMALL 1000 #define ITER_LARGE 100 #define WARMUP 20 static void fill_array(double *arr, int count, int which); static void contig_test(cmxInt buffer_size, int op); double dclock() { struct timeval tv; gettimeofday(&tv, NULL); return(tv.tv_sec * 1.0e6 + (double)tv.tv_usec); } int main(int argc, char **argv) { cmx_init_args(&argc, &argv); cmx_group_rank(CMX_GROUP_WORLD, &me); cmx_group_size(CMX_GROUP_WORLD, &nproc); if (0 == me) { printf("msg size (bytes) avg time (us) avg b/w (MB/sec)\n"); printf("\n\n"); } if (0 == me) { printf("CMX Put Test\n"); } contig_test(MAX_MESSAGE_SIZE, PUT); if (0 == me) { printf("\n\n"); printf("CMX Get Test\n"); } contig_test(MAX_MESSAGE_SIZE, GET); if (0 == me) { printf("\n\n"); printf("CMX Accumulate Test\n"); } contig_test(MAX_MESSAGE_SIZE, ACC); cmx_finalize(); MPI_Finalize(); return 0; } static void fill_array(double *arr, int count, int which) { int i; for (i = 0; i < count; i++) { arr[i] = i * 8.23 + which * 2.89; } } static void contig_test(cmxInt buffer_size, int op) { cmx_handle_t rem_hdl; void *put_buf; void *get_buf; double *times; int participate = 1; put_buf = (void*)malloc(buffer_size); get_buf = (void*)malloc(buffer_size); times = (double*)malloc(nproc * sizeof(double)); cmx_malloc(&rem_hdl, buffer_size, CMX_GROUP_WORLD); /* initialize what we're putting */ fill_array((double*)put_buf, buffer_size/sizeof(double), me); cmxInt msg_size; int dst = me + 1; if (nproc%2 != 0 && nproc-1 == me) participate = 0; double scale = 1.0; for (msg_size = 16; msg_size <= buffer_size; msg_size *= 2) { int j; int iter = msg_size > MEDIUM_MESSAGE_SIZE ? ITER_LARGE : ITER_SMALL; double t_start, t_end; if (me%2 == 0 && participate) { for (j= 0; j < iter + WARMUP; ++j) { if (WARMUP == j) { t_start = dclock(); } switch (op) { case PUT: cmx_put(put_buf, 0, msg_size, dst, rem_hdl); break; case GET: cmx_get(get_buf, 0, msg_size, dst, rem_hdl); break; case ACC: cmx_acc(CMX_ACC_DBL, &scale, put_buf, 0, msg_size, dst, rem_hdl); break; default: cmx_error("oops", 1); } } } cmx_barrier(CMX_GROUP_WORLD); /* calculate total time and average time */ t_end = dclock(); if (0 == me) { printf("%8zu\t\t%8.2f\t\t%10.2f\n", msg_size, ((t_end - t_start))/iter, msg_size*(nproc-1)*iter/((t_end - t_start))); } } cmx_free(rem_hdl); free(put_buf); free(get_buf); free(times); } ga-5.9.2/cmx/testing/perf_strided.c000066400000000000000000000067511500715745200172170ustar00rootroot00000000000000#include #include #include #include #include #include #include "cmx.h" static int me; static int nproc; #define PUTS 0 #define GETS 1 #define ACCS 2 #define MAX_MESSAGE_SIZE 1024*1024 #define MEDIUM_MESSAGE_SIZE 8192 #define ITER_SMALL 100 #define ITER_LARGE 10 #define WARMUP 2 static void fill_array(double *arr, int count, int which); static void strided_test(cmxInt buffer_size, int op); double dclock() { struct timeval tv; gettimeofday(&tv, NULL); return(tv.tv_sec * 1.0e6 + (double)tv.tv_usec); } int main(int argc, char **argv) { cmx_init_args(&argc, &argv); cmx_group_rank(CMX_GROUP_WORLD, &me); cmx_group_size(CMX_GROUP_WORLD, &nproc); if (0 == me) { printf("msg size (bytes) avg time (us) avg b/w (MB/sec) xdim ydim\n"); } if (0 == me) { printf("\n\n"); printf("CMX Put Strided Test\n"); } strided_test(MAX_MESSAGE_SIZE, PUTS); if (0 == me) { printf("\n\n"); printf("CMX Get Strided Test\n"); } strided_test(MAX_MESSAGE_SIZE, GETS); if (0 == me) { printf("\n\n"); printf("CMX Accumulate Strided Test\n"); } strided_test(MAX_MESSAGE_SIZE, ACCS); cmx_finalize(); MPI_Finalize(); return 0; } static void fill_array(double *arr, int count, int which) { int i; for (i = 0; i < count; i++) { arr[i] = i; } } static void strided_test(cmxInt buffer_size, int op) { cmx_handle_t rem_hdl; void *put_buf; void *get_buf; double *times; int participate = 1; put_buf = (void*)malloc(buffer_size); get_buf = (void*)malloc(buffer_size); times = (double*)malloc(nproc * sizeof(double)); cmx_malloc(&rem_hdl, buffer_size, CMX_GROUP_WORLD); /* initialize what we're putting */ fill_array((double*)put_buf, buffer_size/sizeof(double), me); cmxInt msg_size; int dst = me + 1; if (nproc%2 != 0 && me == nproc-1) participate = 0; double scale = 1; /* Information for strided data transfer */ int levels = 1; cmxInt count[2]; cmxInt stride[1]; cmxInt xdim, ydim; for (msg_size = 16; msg_size <= buffer_size; msg_size *= 2) { int j; int iter = msg_size > MEDIUM_MESSAGE_SIZE ? ITER_LARGE : ITER_SMALL; for (xdim = 8; xdim <= msg_size; xdim *=2 ) { ydim = msg_size / xdim; count[0] = xdim; count[1] = ydim; stride[0] = xdim; double t_start, t_end; if (me%2 == 0 && participate) { for (j= 0; j < iter + WARMUP; ++j) { if (WARMUP == j) { t_start = dclock(); } switch (op) { case PUTS: cmx_puts(put_buf, stride, 0, stride, count, levels, dst, rem_hdl); break; case GETS: cmx_gets(get_buf, stride, 0, stride, count, levels, dst, rem_hdl); break; case ACCS: cmx_accs(CMX_ACC_DBL, (void *)&scale, put_buf, stride, 0, stride, count, levels, dst, rem_hdl); break; default: cmx_error("oops", 1); } } } cmx_barrier(CMX_GROUP_WORLD); /* calculate total time and average time */ t_end = dclock(); if (0 == me) { printf("%5zu\t\t%6.2f\t\t%6.2f\t\t%zu\t\t%zu\n", msg_size, ((t_end - t_start))/iter, msg_size*(nproc-1)*iter/((t_end - t_start)), xdim, ydim); } } } cmx_free(rem_hdl); free(put_buf); free(get_buf); free(times); } ga-5.9.2/cmx/testing/shift.c000066400000000000000000000067111500715745200156560ustar00rootroot00000000000000#include #include #include #include #include "cmx.h" static int me; static int nproc; static int size[] = {2,4,8,16,32,64,128,256,512,1024,0}; /* 0 is sentinal */ #define PUT_FORWARD 0 #define PUT_BACKWARD 1 #define GET_FORWARD 2 #define GET_BACKWARD 3 static void fill_array(double *arr, int count, int which); static void shift(cmxInt buffer_size, int op); int main(int argc, char **argv) { int i; cmx_init_args(&argc, &argv); cmx_group_rank(CMX_GROUP_WORLD, &me); cmx_group_size(CMX_GROUP_WORLD, &nproc); if (0 == me) { printf("msg size (bytes) avg time (milliseconds) avg b/w (bytes/sec)\n"); } if (0 == me) { printf("shifting put forward\n"); } for (i=0; size[i]!=0; ++i) { shift(size[i], PUT_FORWARD); } if (0 == me) { printf("shifting put backward\n"); } for (i=0; size[i]!=0; ++i) { shift(size[i], PUT_BACKWARD); } if (0 == me) { printf("shifting get forward\n"); } for (i=0; size[i]!=0; ++i) { shift(size[i], GET_FORWARD); } if (0 == me) { printf("shifting get backward\n"); } for (i=0; size[i]!=0; ++i) { shift(size[i], GET_BACKWARD); } cmx_finalize(); MPI_Finalize(); return 0; } static void fill_array(double *arr, int count, int which) { int i; for (i = 0; i < count; i++) { arr[i] = i * 8.23 + which * 2.89; } } static void shift(cmxInt buffer_size, int op) { cmx_handle_t rem_hdl; void *put_buf; void *get_buf; int i=0; double *times; double *result; double total_time=0; MPI_Comm comm = MPI_COMM_NULL; cmx_group_comm(CMX_GROUP_WORLD, &comm); put_buf = (void*)malloc(buffer_size); get_buf = (void*)malloc(buffer_size); times = (double*)malloc(nproc * sizeof(double)); result = (double*)malloc(nproc * sizeof(double)); cmx_malloc(&rem_hdl, buffer_size, CMX_GROUP_WORLD); /* initialize what we're putting */ fill_array((double*)put_buf, buffer_size/sizeof(double), me); /* initialize time keepers */ (void)memset(times, 0, nproc*sizeof(double)); (void)memset(result, 0, nproc*sizeof(double)); times[me] = MPI_Wtime()*1.0e6; /* the shift */ switch (op) { case PUT_FORWARD: for (i=1; i #include #include #include #include #include #include #include "cmx.h" #define DIM1 5 #define DIM2 3 #ifdef __sun /* Solaris has shared memory shortages in the default system configuration */ # define DIM3 6 # define DIM4 5 # define DIM5 4 #elif defined(__alpha__) # define DIM3 8 # define DIM4 5 # define DIM5 6 #else # define DIM3 8 # define DIM4 9 # define DIM5 7 #endif #define DIM6 3 #define DIM7 2 #define OFF 1 #define EDIM1 (DIM1+OFF) #define EDIM2 (DIM2+OFF) #define EDIM3 (DIM3+OFF) #define EDIM4 (DIM4+OFF) #define EDIM5 (DIM5+OFF) #define EDIM6 (DIM6+OFF) #define EDIM7 (DIM7+OFF) #define DIMS 4 #define MAXDIMS 7 /* #define MAX_DIM_VAL 50 */ #define MAX_DIM_VAL 10 #define LOOP 200 #define BASE 100. #define MAXPROC 128 #define TIMES 100 # define ELEMS 200 /***************************** macros ************************/ #define COPY(src, dst, bytes) memcpy((dst),(src),(bytes)) #define CMX_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define CMX_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define CMX_ABS(a) (((a) <0) ? -(a) : (a)) /***************************** global data *******************/ int me, nproc; int work[MAXPROC]; /* work array for propagating addresses */ static void all_sum_int(int *x, int n) { MPI_Comm comm = MPI_COMM_NULL; MPI_Datatype mpi_type = MPI_INT; int rc = 0; void *result = NULL; MPI_Op mpi_op = MPI_SUM; cmx_group_comm(CMX_GROUP_WORLD,&comm); result = malloc(n*sizeof(int)); assert(result); cmx_barrier(CMX_GROUP_WORLD); rc = MPI_Allreduce(x, result, n, mpi_type, mpi_op, comm); assert(rc == MPI_SUCCESS); memcpy(x, result, sizeof(int) * n); free(result); } static void all_sum_long(long *x, int n) { MPI_Comm comm = MPI_COMM_NULL; MPI_Datatype mpi_type = MPI_LONG; int rc = 0; void *result = NULL; MPI_Op mpi_op = MPI_SUM; cmx_group_comm(CMX_GROUP_WORLD, &comm); result = malloc(n*sizeof(long)); assert(result); cmx_barrier(CMX_GROUP_WORLD); rc = MPI_Allreduce(x, result, n, mpi_type, mpi_op, comm); assert(rc == MPI_SUCCESS); memcpy(x, result, sizeof(long) * n); free(result); } static double timer() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000.0 + tv.tv_usec; } /*\ generate random range for a section of multidimensional array \*/ void get_range(int ndim, int dims[], int lo[], int hi[]) { int dim; for (dim = 0; dim < ndim; dim++) { int toss1, toss2; toss1 = rand() % dims[dim]; toss2 = rand() % dims[dim]; if (toss1 < toss2) { lo[dim] = toss1; hi[dim] = toss2; } else { hi[dim] = toss1; lo[dim] = toss2; } } } /*\ generates a new random range similar to the input range for an array with specified dimensions \*/ void new_range(int ndim, int dims[], int lo[], int hi[], int new_lo[], int new_hi[]) { int dim; for (dim = 0; dim < ndim; dim++) { int toss, range; int diff = hi[dim] - lo[dim] + 1; assert(diff <= dims[dim]); range = dims[dim] - diff; toss = (range > 0) ? rand() % range : lo[dim]; new_lo[dim] = toss; new_hi[dim] = toss + diff - 1; assert(new_hi[dim] < dims[dim]); assert(diff == (new_hi[dim] - new_lo[dim] + 1)); } } /*\ print range of ndim dimensional array with two strings before and after \*/ void print_range(char *pre, int ndim, int lo[], int hi[], char *post) { int i; printf("%s[", pre); for (i = 0; i < ndim; i++) { printf("%d:%d", lo[i], hi[i]); if (i == ndim - 1) { printf("] %s", post); } else { printf(","); } } } /*\ print subscript of ndim dimensional array with two strings before and after \*/ void print_subscript(char *pre, int ndim, int subscript[], char *post) { int i; printf("%s [", pre); for (i = 0; i < ndim; i++) { printf("%d", subscript[i]); if (i == ndim - 1) { printf("] %s", post); } else { printf(","); } } } /*\ print a section of a 2-D array of doubles \*/ void print_2D_double(double *a, int ld, int *lo, int *hi) { int i, j; for (i = lo[0]; i <= hi[0]; i++) { for (j = lo[1]; j <= hi[1]; j++) { printf("%13f ", a[ld*j+i]); } printf("\n"); } } /*\ initialize array: a[i,j,k,..]=i+100*j+10000*k+ ... \*/ void init(double *a, int ndim, int elems, int dims[]) { int idx[MAXDIMS]; int i, dim; for (i = 0; i < elems; i++) { int Index = i; double field, val; for (dim = 0; dim < ndim; dim++) { idx[dim] = Index % dims[dim]; Index /= dims[dim]; } field = 1.; val = 0.; for (dim = 0; dim < ndim; dim++) { val += field * idx[dim]; field *= BASE; } a[i] = val; /* printf("(%d,%d,%d)=%6.0f",idx[0],idx[1],idx[2],val); */ } } /*\ initialize complex array: real parts are a[i,j,k,..]=i+100*j+10000*k+ ... * all imaginary parts are 1 \*/ void cplx_init(double *a, int ndim, int elems, int dims[]) { int idx[MAXDIMS]; int i, dim; for (i = 0; i < elems; i++) { int Index = i; double field, val; for (dim = 0; dim < ndim; dim++) { idx[dim] = Index % dims[dim]; Index /= dims[dim]; } field = 1.; val = 0.; for (dim = 0; dim < ndim; dim++) { val += field * idx[dim]; field *= BASE; } a[2*i] = val; a[2*i+1] = 1.0; /* printf("(%d,%d,%d)=%6.0f",idx[0],idx[1],idx[2],val); */ } } /*\ compute Index from subscript * assume that first subscript component changes fastest \*/ int Index(int ndim, int subscript[], int dims[]) { int idx = 0, i, factor = 1; for (i = 0; i < ndim; i++) { idx += subscript[i] * factor; factor *= dims[i]; } return idx; } void update_subscript(int ndim, int subscript[], int lo[], int hi[]) { int i; for (i = 0; i < ndim; i++) { if (subscript[i] < hi[i]) { subscript[i]++; return; } subscript[i] = lo[i]; } } void compare_patches(double eps, int ndim, double *patch1, int lo1[], int hi1[], int dims1[], double *patch2, int lo2[], int hi2[], int dims2[]) { int i, j, elems = 1; int subscr1[MAXDIMS], subscr2[MAXDIMS]; double diff, max; int offset1, offset2; for (i = 0; i < ndim; i++) { /* count # of elements & verify consistency of both patches */ int diff = hi1[i] - lo1[i]; assert(diff == (hi2[i] - lo2[i])); assert(diff < dims1[i]); assert(diff < dims2[i]); elems *= diff + 1; subscr1[i] = lo1[i]; subscr2[i] = lo2[i]; } /* compare element values in both patches */ offset1 = Index(ndim, subscr1, dims1); offset2 = Index(ndim, subscr2, dims2); for (j = 0; j < elems; j++) { int idx1, idx2; idx1 = Index(ndim, subscr1, dims1); /* calculate element Index from a subscript */ idx2 = Index(ndim, subscr2, dims2); idx1 -= offset1; idx2 -= offset2; diff = patch1[idx1] - patch2[idx2]; max = CMX_MAX(CMX_ABS(patch1[idx1]), CMX_ABS(patch2[idx2])); if (max == 0. || max < eps) { max = 1.; } if (eps < CMX_ABS(diff) / max) { char msg[48]; sprintf(msg, "(proc=%d):%f", me, patch1[idx1]); print_subscript("ERROR: a", ndim, subscr1, msg); sprintf(msg, "%f\n", patch2[idx2]); print_subscript(" b", ndim, subscr2, msg); fflush(stdout); sleep(1); cmx_error("Bailing out", 0); } { /* update subscript for the patches */ update_subscript(ndim, subscr1, lo1, hi1); update_subscript(ndim, subscr2, lo2, hi2); } } /* make sure we reached upper limit */ /*for(i=0;i "); print_range("remote", ndim, loB, hiB, "-> "); print_range("local", ndim, loC, hiC, "\n"); } idx1 = Index(ndim, loA, dimsA); idx2 = Index(ndim, loB, dimsB); idx3 = Index(ndim, loC, dimsA); for (j = 0; j < ndim; j++) { count[j] = hiA[j] - loA[j] + 1; } count[0] *= sizeof(double); /* convert range to bytes at stride level zero */ (void)cmx_puts((double *)a + idx1, strideA, idx2, strideB, count, ndim - 1, proc, b); /* sleep(1);*/ /* printf("%d: a=(%x,%f) b=(%x,%f)\n",me,idx1 + (double*)a,*(idx1 + (double*)a),idx2 + (double*)b,*(idx2 + (double*)b));*/ /* fflush(stdout);*/ /* sleep(1);*/ /* note that we do not need cmx_fence here since * consecutive operations targeting the same process are ordered */ (void)cmx_gets((double *)c + idx3, strideA, idx2, strideB, count, ndim - 1, proc, b); compare_patches(0., ndim, (double *)a + idx1, loA, hiA, dimsA, (double *)c + idx3, loC, hiC, dimsA); } free(c); destroy_array(b); free(a); } int nloA[MAXDIMS+1][MAXDIMS], nhiA[MAXDIMS+1][MAXDIMS]; int nloB[MAXDIMS+1][MAXDIMS], nhiB[MAXDIMS+1][MAXDIMS]; int nloC[MAXDIMS+1][MAXDIMS], nhiC[MAXDIMS+1][MAXDIMS]; int get_next_RRproc(int initialize, int ndim) { static int distance; int proc; if (initialize) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } if (nproc == 1) { distance = 0; } return(0); } /*send it to a different process everytime*/ proc = (me <= ((nproc % 2 == 0) ? ((nproc / 2) - 1) : (nproc / 2))) ? (me + distance) : (me - distance); if ((nproc % 2) != 0 && me == (nproc / 2)) { proc = me; } if (distance != 0) { if (me < (nproc / 2)) { distance++; if ((me + distance) >= nproc) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } distance -= me; } } else { distance--; if ((me - distance) >= (nproc / 2)) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } distance = distance + (me - distance); } } if (ndim != 1 && MAXDIMS > nproc && (ndim % (nproc / 2) == 0)) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } } } return(proc); } void test_nbdim() { int elems = 1, elems1 = 1; int i, j, proc, ndim, rc; void *a[MAXDIMS+1], *c[MAXDIMS+1]; cmx_request_t hdl_put[MAXDIMS+1], hdl_get[MAXDIMS+1]; cmxInt idx1 = 0, idx2 = 0, idx3 = 0; cmx_handle_t b[MAXDIMS+1]; /* create shared and local arrays */ for (ndim = 1; ndim <= MAXDIMS; ndim++) { elems1 *= dimsB[ndim-1]; elems *= dimsA[ndim-1]; rc = cmx_malloc(&b[ndim], sizeof(double) * elems1, CMX_GROUP_WORLD); assert(rc == 0); a[ndim] = malloc(sizeof(double) * elems); assert(a[ndim]); c[ndim] = malloc(sizeof(double) * elems); assert(c[ndim]); init(a[ndim], ndim, elems, dimsA); } cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); (void)get_next_RRproc(1, 0); for (ndim = 1; ndim <= MAXDIMS; ndim++) { strideA[0] = sizeof(double); strideB[0] = sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } } proc = get_next_RRproc(0, ndim); get_range(ndim, dimsA, nloA[ndim], nhiA[ndim]); new_range(ndim, dimsB, nloA[ndim], nhiA[ndim], nloB[ndim], nhiB[ndim]); new_range(ndim, dimsA, nloA[ndim], nhiA[ndim], nloC[ndim], nhiC[ndim]); if (me == 0) { print_range("local", ndim, nloA[ndim], nhiA[ndim], "-> "); print_range("remote", ndim, nloB[ndim], nhiB[ndim], "-> "); print_range("local", ndim, nloC[ndim], nhiC[ndim], "\n"); fflush(stdout); sleep(1); } idx1 = Index(ndim, nloA[ndim], dimsA); idx2 = Index(ndim, nloB[ndim], dimsB); idx3 = Index(ndim, nloC[ndim], dimsA); for (j = 0; j < ndim; j++) { count[j] = nhiA[ndim][j] - nloA[ndim][j] + 1; } count[0] *= sizeof(double); if (ndim == 1) { (void)cmx_nbput((double *)a[ndim] + idx1, idx2, count[0], proc, b[ndim], (hdl_put + ndim)); } else { (void)cmx_nbputs((double *)a[ndim] + idx1, strideA, idx2, strideB, count, ndim - 1, proc, b[ndim], (hdl_put + ndim)); } } sleep(5); cmx_barrier(CMX_GROUP_WORLD); /*before we do gets, we have to make sure puts are complete on the remote processor*/ for (ndim = 1; ndim <= MAXDIMS; ndim++) { cmx_wait(hdl_put + ndim); } cmx_barrier(CMX_GROUP_WORLD); cmx_fence_all(CMX_GROUP_WORLD); (void)get_next_RRproc(1, 0); for (ndim = 1; ndim <= MAXDIMS; ndim++) { strideA[0] = sizeof(double); strideB[0] = sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } } /*send it to a different process everytime*/ proc = get_next_RRproc(0, ndim); idx1 = Index(ndim, nloA[ndim], dimsA); idx2 = Index(ndim, nloB[ndim], dimsB); idx3 = Index(ndim, nloC[ndim], dimsA); for (j = 0; j < ndim; j++) { count[j] = nhiA[ndim][j] - nloA[ndim][j] + 1; } count[0] *= sizeof(double); if (ndim == 1) { (void)cmx_nbget((double *)c[ndim] + idx3, idx2, count[0], proc, b[ndim], (hdl_get + ndim)); } else { (void)cmx_nbgets((double *)c[ndim] + idx3, strideA, idx2, strideB, count, ndim - 1, proc, b[ndim], (hdl_get + ndim)); } } cmx_barrier(CMX_GROUP_WORLD); if (me == 0) { printf("Now waiting for all non-blocking calls and verifying data...\n"); fflush(stdout); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { cmx_wait(hdl_get + ndim); idx1 = Index(ndim, nloA[ndim], dimsA); idx2 = Index(ndim, nloB[ndim], dimsB); idx3 = Index(ndim, nloC[ndim], dimsA); compare_patches(0., ndim, (double *)a[ndim] + idx1, nloA[ndim], nhiA[ndim], dimsA, (double *)c[ndim] + idx3, nloC[ndim], nhiC[ndim], dimsA); } if (me == 0) { printf("OK\n"); fflush(stdout); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { destroy_array(b[ndim]); free(c[ndim]); free(a[ndim]); } } #define PTR_ARR_LEN 10 #define VLOOP 50 #define VEC_ELE_LEN 20 /*number of doubles in each dimention*/ #define GIOV_ARR_LEN 9 void verify_vector_data(double *data, int procs, int isput, int datalen) { double facto = 2.89; int i, j = 0, k = 0, kc = 0, dst = 0; if (isput) { facto = 1.89; } for (i = 0; i < datalen; i++) { if (dst != me) if (CMX_ABS((data[i] - (me + facto + dst)*((kc + 1)*(j % PTR_ARR_LEN + 1)))) > 0.001) { printf("\n%d:while verifying data of a op from proc=%d ", me, dst); printf("giov index=%d ptr_arr_index=%d \n :element index=%d", kc, (j % PTR_ARR_LEN), k); printf(" elem was supposed to be %f but is %f", (me + facto + dst)*((kc + 1)*(j % PTR_ARR_LEN + 1)) , data[i]); fflush(stdout); sleep(1); cmx_error("vector non-blocking failed", 0); } k++; if (k == VEC_ELE_LEN) { j++; k = 0; if (j % PTR_ARR_LEN == 0) { kc++; if ((kc % GIOV_ARR_LEN) == 0) { kc = 0; dst++; } } } } } #if 1 void test_vec_small() { double *getdst; double **putsrc; cmx_giov_t dsc[MAXPROC*GIOV_ARR_LEN]; void **ploc; /*arrays of pointers to be used by giov_t*/ cmxInt *prem; cmx_handle_t getsrc; /*to allocate mem via cmx_malloc*/ cmx_handle_t putdst; /*to allocate mem via cmx_malloc*/ cmx_request_t hdl_put[MAXPROC], hdl_get[MAXPROC]; int i = 0, j = 0, k = 0, kc = 0, kcold = 0, rc, dstproc, dst = 0; int lenpergiov; lenpergiov = PTR_ARR_LEN * VEC_ELE_LEN; rc = cmx_malloc(&getsrc, sizeof(double) * nproc * GIOV_ARR_LEN * lenpergiov, CMX_GROUP_WORLD); assert(rc == 0); assert(getsrc[me]); rc = cmx_malloc(&putdst, sizeof(double) * nproc * GIOV_ARR_LEN * lenpergiov, CMX_GROUP_WORLD); assert(rc == 0); assert(putdst[me]); /*first malloc for getdst and putsrc, both are 2d arrays*/ getdst = (double *)malloc(sizeof(double) * nproc * GIOV_ARR_LEN * lenpergiov); putsrc = (double **)malloc(sizeof(double *) * nproc * GIOV_ARR_LEN * PTR_ARR_LEN); assert(getdst); assert(putsrc); for (i = 0; i < nproc * GIOV_ARR_LEN * PTR_ARR_LEN; i++) { putsrc[i] = (double *)malloc(sizeof(double) * VEC_ELE_LEN); assert(putsrc[i]); } /*allocating memory for psrc and pdst*/ ploc = (void **)malloc(sizeof(void *) * PTR_ARR_LEN * nproc * GIOV_ARR_LEN); prem = (cmxInt *)malloc(sizeof(cmxInt) * PTR_ARR_LEN * nproc * GIOV_ARR_LEN); assert(pdst); assert(psrc); for (i = 0; i < nproc * lenpergiov * GIOV_ARR_LEN; i++) { putsrc[j][k] = (me + 1.89 + dst) * ((kc + 1) * ((j % PTR_ARR_LEN) + 1)); ((double *)getsrc.buf)[i] = (me + 2.89 + dst) * ((kc + 1) * (j % PTR_ARR_LEN + 1)); k++; if (k == VEC_ELE_LEN) { j++; k = 0; if ((j % PTR_ARR_LEN) == 0) { kc++; if ((kc % GIOV_ARR_LEN) == 0) { kc = 0; dst++; } } } } /*********************Testing NbPutV*********************************/ i = 0; j = 0; k = 0; kc = 0; dstproc = me; for (i = 0; i < nproc - 1; i++) { dstproc++; if (dstproc == nproc) { dstproc = 0; } for (j = 0; j < GIOV_ARR_LEN; j++) { kcold = kc; for (k = 0; k < PTR_ARR_LEN; k++, kc++) { double *ptr; ploc[kc] = (void *)putsrc[PTR_ARR_LEN*(dstproc*GIOV_ARR_LEN+j)+k]; prem[kc] = sizeof(double)*(lenpergiov * (GIOV_ARR_LEN * me + j) + k * VEC_ELE_LEN); } dsc[j].bytes = VEC_ELE_LEN * sizeof(double); dsc[j].loc = &ploc[kcold]; dsc[j].rem = &prem[kcold]; dsc[j].count = PTR_ARR_LEN; } if ((rc = cmx_nbputv(dsc, GIOV_ARR_LEN, dstproc, putdst, hdl_put + dstproc))) { cmx_error("putv failed", rc); } } if (me == 0) { printf("\n\tNow verifying the vector put data for correctness\n"); } for (i = 0; i < nproc; i++)if (i != me) { cmx_wait(hdl_put + i); } sleep(1); cmx_barrier(CMX_GROUP_WORLD); cmx_fence_all(CMX_GROUP_WORLD); verify_vector_data((double *)putdst.buf, nproc, 1, nproc * GIOV_ARR_LEN * lenpergiov); if (me == 0) { printf("\n\tPuts OK\n"); } /****************Done Testing NbPutV*********************************/ /*********************Testing NbGetV*********************************/ i = 0; j = 0; k = 0; kc = 0; dstproc = me; for (i = 0; i < nproc - 1; i++) { dstproc++; if (dstproc == nproc) { dstproc = 0; } for (j = 0; j < GIOV_ARR_LEN; j++) { kcold = kc; for (k = 0; k < PTR_ARR_LEN; k++, kc++) { double *ptr; ptr = getdst; ploc[kc] = (void *)(ptr + lenpergiov * (dstproc * GIOV_ARR_LEN + j) + k * VEC_ELE_LEN); prem[kc] = sizeof(double) * (lenpergiov * (me * GIOV_ARR_LEN + j) + k * VEC_ELE_LEN); } dsc[j].bytes = VEC_ELE_LEN * sizeof(double); dsc[j].loc = &ploc[kcold]; dsc[j].rem = &prem[kcold]; dsc[j].count = PTR_ARR_LEN; } if ((rc = cmx_nbgetv(dsc, GIOV_ARR_LEN, dstproc, getsrc, hdl_get + dstproc))) { cmx_error("putv failed", rc); } } if (me == 0) { printf("\n\tNow verifying the vector get data for correctness\n"); } for (i = 0; i < nproc; i++)if (i != me) { cmx_wait(hdl_get + i); } sleep(1); cmx_barrier(CMX_GROUP_WORLD); verify_vector_data((double *)getdst, nproc, 0, nproc * GIOV_ARR_LEN * lenpergiov); if (me == 0) { printf("\n\tGets OK\n"); } /****************Done Testing NbGetV*********************************/ free(ploc); free(prem); free(getdst); for (i = 0; i < nproc * GIOV_ARR_LEN * PTR_ARR_LEN; i++) { free(putsrc[i]); } free(putsrc); cmx_free(getsrc); cmx_free(putdst); } #endif void GetPermutedProcList(int *ProcList) { int i, iswap, temp; if (nproc > MAXPROC) { cmx_error("permute_proc: nproc to big ", nproc); } /* initialize list */ for (i = 0; i < nproc; i++) { ProcList[i] = i; } if (nproc == 1) { return; } /* every process generates different random sequence */ (void)srand((unsigned)me); /* list permutation generated by random swapping */ for (i = 0; i < nproc; i++) { iswap = (int)(rand() % nproc); temp = ProcList[iswap]; ProcList[iswap] = ProcList[i]; ProcList[i] = temp; } } /*\ Atomic Accumulate test: remote += alpha*local * Every process/or has its patch of array b updated TIMES*NPROC times. * The sequence of updates is random: everybody uses a randomly permuted list * and accumulate is non-collective (of-course) \*/ void test_acc(int ndim) { int dim, elems; int i, proc; cmx_handle_t b; void *a, *c; double alpha = 0.1, scale; int idx1, idx2; int *proclist = work; elems = 1; strideA[0] = sizeof(double); strideB[0] = sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } elems *= dimsA[i]; /* set up patch coordinates: same on every processor */ loA[i] = 0; hiA[i] = loA[i] + 1; loB[i] = dimsB[i] - 2; hiB[i] = loB[i] + 1; count[i] = hiA[i] - loA[i] + 1; } /* create shared and local arrays */ create_array(&b, sizeof(double), ndim, dimsB); a = malloc(sizeof(double) * elems); assert(a); c = malloc(sizeof(double) * elems); assert(c); init(a, ndim, elems, dimsA); if (me == 0) { printf("--------array[%d", dimsA[0]); for (dim = 1; dim < ndim; dim++) { printf(",%d", dimsA[dim]); } printf("]--------\n"); } GetPermutedProcList(proclist); idx1 = Index(ndim, loA, dimsA); idx2 = Index(ndim, loB, dimsB); count[0] *= sizeof(double); /* convert range to bytes at stride level zero */ /* initialize all elements of array b to zero */ elems = 1; for (i = 0; i < ndim; i++) { elems *= dimsB[i]; } for (i = 0; i < elems; i++) { ((double *)b.buf)[i] = 0.; } sleep(1); if (me == 0) { print_range("patch", ndim, loA, hiA, " -> "); print_range("patch", ndim, loB, hiB, "\n"); fflush(stdout); } cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); for (i = 0; i < TIMES * nproc; i++) { proc = proclist[i%nproc]; (void)cmx_accs(CMX_ACC_DBL, &alpha, (double *)a + idx1, strideA, idx2, strideB, count, ndim - 1, proc, b); } /* sleep(9);*/ cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); /* copy my patch into local array c */ (void)cmx_gets((double *)c + idx1, strideA, idx2, strideB, count, ndim - 1, me, b); scale = alpha * TIMES * nproc; scale_patch(scale, ndim, (double *)a + idx1, loA, hiA, dimsA); compare_patches(.0001, ndim, (double *)a + idx1, loA, hiA, dimsA, (double *)c + idx1, loA, hiA, dimsA); cmx_barrier(CMX_GROUP_WORLD); if (0 == me) { printf(" OK\n\n"); fflush(stdout); } free(c); destroy_array(b); free(a); } void test_cplx_acc(int ndim) { int dim, elems; int i, proc; cmx_handle_t b; void *a, *c; double alpha[2], scale[2]; int idx1, idx2; int *proclist = work; alpha[0] = 0.1; alpha[1] = 0.1; elems = 1; strideA[0] = 2*sizeof(double); strideB[0] = 2*sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } elems *= dimsA[i]; /* set up patch coordinates: same on every processor */ loA[i] = 0; hiA[i] = loA[i] + 1; loB[i] = dimsB[i] - 2; hiB[i] = loB[i] + 1; count[i] = hiA[i] - loA[i] + 1; } /* create shared and local arrays */ create_array(&b, 2*sizeof(double), ndim, dimsB); a = malloc(2*sizeof(double) * elems); assert(a); c = malloc(2*sizeof(double) * elems); assert(c); cplx_init(a, ndim, elems, dimsA); if (me == 0) { printf("--------array[%d", dimsA[0]); for (dim = 1; dim < ndim; dim++) { printf(",%d", dimsA[dim]); } printf("]--------\n"); } GetPermutedProcList(proclist); idx1 = Index(ndim, loA, dimsA); idx2 = Index(ndim, loB, dimsB); count[0] *= 2*sizeof(double); /* convert range to bytes at stride level zero */ /* initialize all elements of array b to zero */ elems = 1; for (i = 0; i < ndim; i++) { elems *= dimsB[i]; } for (i = 0; i < elems; i++) { ((double *)b.buf)[2*i] = 0.; ((double *)b.buf)[2*i+1] = 0.; } sleep(1); if (me == 0) { print_range("patch", ndim, loA, hiA, " -> "); print_range("patch", ndim, loB, hiB, "\n"); fflush(stdout); } cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); for (i = 0; i < TIMES * nproc; i++) { proc = proclist[i%nproc]; (void)cmx_accs(CMX_ACC_DCP, &alpha, (double *)a + 2*idx1, strideA, 2*idx2, strideB, count, ndim - 1, proc, b); } /* sleep(9);*/ cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); /* copy my patch into local array c */ (void)cmx_gets((double *)c + 2*idx1, strideA, 2*idx2, strideB, count, ndim - 1, me, b); scale[0] = alpha[0] * TIMES * nproc; scale[1] = alpha[1] * TIMES * nproc; scale_cplx_patch(scale, ndim, (double *)a + 2*idx1, loA, hiA, dimsA); compare_cplx_patches(.0001, ndim, (double *)a + 2*idx1, loA, hiA, dimsA, (double *)c + 2*idx1, loA, hiA, dimsA); cmx_barrier(CMX_GROUP_WORLD); if (0 == me) { printf(" OK\n\n"); fflush(stdout); } free(c); destroy_array(b); free(a); } /*************************** vector interface *********************************\ * tests vector interface for transfers of triangular sections of a 2-D array * ******************************************************************************/ void test_vector() { int dim, elems, ndim, cols, rows, mrc; int i, proc, loop; int rc; int idx1, idx3; cmx_handle_t b; void *a, *c; cmx_giov_t dsc[MAX_DIM_VAL]; void *ploc[MAX_DIM_VAL]; cmxInt prem[MAX_DIM_VAL]; elems = 1; ndim = 2; for (i = 0; i < ndim; i++) { dimsA[i] = MAX_DIM_VAL; dimsB[i] = MAX_DIM_VAL + 1; elems *= dimsA[i]; } /* create shared and local arrays */ create_array(&b, sizeof(double), ndim, dimsB); a = malloc(sizeof(double) * elems); assert(a); c = malloc(sizeof(double) * elems); assert(c); init(a, ndim, elems, dimsA); if (me == 0) { printf("--------array[%d", dimsA[0]); for (dim = 1; dim < ndim; dim++) { printf(",%d", dimsA[dim]); } printf("]--------\n"); } sleep(1); for (loop = 0; loop < LOOP; loop++) { get_range(ndim, dimsA, loA, hiA); new_range(ndim, dimsB, loA, hiA, loB, hiB); new_range(ndim, dimsA, loA, hiA, loC, hiC); proc = nproc - 1 - me; if (me == 0) { print_range("local", ndim, loA, hiA, "-> "); print_range("remote", ndim, loB, hiB, "-> "); print_range("local", ndim, loC, hiC, "\n"); } /* printf("array at source\n");*/ /* print_2D_double((double *)a, dimsA[0], loA, hiA);*/ cols = hiA[1] - loA[1] + 1; rows = hiA[0] - loA[0] + 1; mrc = CMX_MIN(cols, rows); /* generate a data descriptor for a lower-triangular patch */ for (i = 0; i < mrc; i++) { int ij[2]; int idx; ij[0] = loA[0] + i; ij[1] = loA[1] + i; idx = Index(ndim, ij, dimsA); ploc[i] = (double *)a + idx; ij[0] = loB[0] + i; ij[1] = loB[1] + i; idx = Index(ndim, ij, dimsB); prem[i] = sizeof(double)*idx; dsc[i].bytes = (rows - i) * sizeof(double); dsc[i].loc = &ploc[i]; dsc[i].rem = &prem[i]; /* assume each element different in size (not true in rectangular patches) */ dsc[i].count = 1; } if ((rc = cmx_putv(dsc, mrc, proc, b))) { cmx_error("putv failed ", rc); } /* printf("array at destination\n");*/ /* print_2D_double((double *)b[proc], dimsB[0], loB, hiB);*/ /* generate a data descriptor for the upper-triangular patch */ /* there is one less element since diagonal is excluded */ for (i = 1; i < cols; i++) { int ij[2]; ij[0] = loA[0]; ij[1] = loA[1] + i; ploc[i-1] = (double *)a + Index(ndim, ij, dimsA); ij[0] = loB[0]; ij[1] = loB[1] + i; prem[i-1] = sizeof(double)*Index(ndim, ij, dimsB); mrc = CMX_MIN(i, rows); dsc[i-1].bytes = mrc * sizeof(double); dsc[i-1].loc = &ploc[i-1]; dsc[i-1].rem = &prem[i-1]; /* assume each element different in size (not true in rectangular patches) */ dsc[i-1].count = 1; } if ((cols - 1))if ((rc = cmx_putv(dsc, cols - 1, proc, b))) { cmx_error("putv(2) failed ", rc); } /* we get back entire rectangular patch */ for (i = 0; i < cols; i++) { int ij[2]; ij[0] = loB[0]; ij[1] = loB[1] + i; prem[i] = sizeof(double)*Index(ndim, ij, dimsB); ij[0] = loC[0]; ij[1] = loC[1] + i; ploc[i] = (double *)c + Index(ndim, ij, dimsA); } dsc[0].bytes = rows * sizeof(double); dsc[0].loc = ploc; dsc[0].rem = prem; dsc[0].count = cols; /* note that we do not need cmx_fence here since * consecutive operations targeting the same process are ordered */ if ((rc = cmx_getv(dsc, 1, proc, b))) { cmx_error("getv failed ", rc); } idx1 = Index(ndim, loA, dimsA); idx3 = Index(ndim, loC, dimsA); compare_patches(0., ndim, (double *)a + idx1, loA, hiA, dimsA, (double *)c + idx3, loC, hiC, dimsA); } free(c); destroy_array(b); free(a); } /*\ Atomic Accumulate test for vector API: remote += alpha*local * Every process/or has its patch of array b updated TIMES*NPROC times. * The sequence of updates is random: everybody uses a randomly permuted list * and accumulate is non-collective (of-course) \*/ void test_vector_acc() { int dim, elems, bytes; int i, j, proc, rc, one = 1; cmx_handle_t b; void *ploc[ELEMS/2]; cmxInt prem[ELEMS/2]; void *a, *c; double alpha = 0.1, scale; int *proclist = work; cmx_giov_t dsc; elems = ELEMS; dim = 1; bytes = sizeof(double) * elems; /* create shared and local arrays */ create_array(&b, sizeof(double), dim, &elems); a = malloc(bytes); assert(a); c = malloc(bytes); assert(c); init(a, dim, elems, &elems); if (me == 0) { printf("--------array[%d", elems); printf("]--------\n"); fflush(stdout); } GetPermutedProcList(proclist); /* initialize all elements of array b to zero */ for (i = 0; i < elems; i++) { ((double *)b.buf)[i] = 0.; } sleep(1); dsc.bytes = sizeof(double); dsc.loc = ploc; dsc.rem = prem; dsc.count = elems / 2; cmx_barrier(CMX_GROUP_WORLD); for (i = 0; i < TIMES * nproc; i++) { /* proc=proclist[i%nproc];*/ proc = 0; /* accumulate even numbered elements */ for (j = 0; j < elems / 2; j++) { ploc[j] = 2 * j + (double *)a; prem[j] = sizeof(double) * (2 * j); } if ((rc = cmx_accv(CMX_ACC_DBL, &alpha, &dsc, 1, proc, b))) { cmx_error("accumlate failed", rc); } /* for(j=0; j MAXPROC && me == 0) { cmx_error("Test works for up to %d processors\n", MAXPROC); } if (me == 0) { printf("CMX test program (%d processes)\n", nproc); fflush(stdout); sleep(1); } /* if(me==1)cmx_die("process 1 committing suicide",1); */ if (me == 0) { printf("\nTesting strided gets and puts\n"); printf("(Only std output for process 0 is printed)\n\n"); fflush(stdout); sleep(1); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { test_dim(ndim); } cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); if (me == 0) { printf("\nTesting non-blocking gets and puts\n"); fflush(stdout); sleep(1); } double timer_test_nbdim = timer(); test_nbdim(); cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); timer_test_nbdim = timer() - timer_test_nbdim; if (me == 0) { printf("timer_test_nbdim=%f\n", timer_test_nbdim); } if (me == 0) { printf("\nTesting non-blocking vector gets and puts\n"); fflush(stdout); sleep(1); } double timer_test_vec_small = timer(); test_vec_small(); cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); timer_test_vec_small = timer() - timer_test_vec_small; if (me == 0){ printf("timer_test_vec_small=%f\n", timer_test_vec_small); } if (me == 0) { printf("\nTesting atomic accumulate\n"); fflush(stdout); sleep(1); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { test_acc(ndim); } cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); if (me == 0) { printf("\nTesting complex atomic accumulate\n"); fflush(stdout); sleep(1); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { test_cplx_acc(ndim); } cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); if (me == 0) { printf("\nTesting Vector Interface using triangular patches of a 2-D array\n\n"); fflush(stdout); sleep(1); } test_vector(); cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); if (me == 0) { printf("\nTesting Accumulate with Vector Interface\n\n"); fflush(stdout); sleep(1); } double test_vector_acc_timer = timer(); test_vector_acc(); test_vector_acc_timer = timer() - test_vector_acc_timer; if (me == 0) { printf("test_vector_acc_timer=%f\n", test_vector_acc_timer); } cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); if (me == 0) { printf("\nTesting atomic fetch&add\n"); printf("(Std Output for all processes is printed)\n\n"); fflush(stdout); sleep(1); } cmx_barrier(CMX_GROUP_WORLD); test_fetch_add(); if (me == 0) { printf("\nTesting atomic fetch&add long\n"); printf("(Std Output for all processes is printed)\n\n"); fflush(stdout); sleep(1); } cmx_barrier(CMX_GROUP_WORLD); test_fetch_add_long(); cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); if (me == 0) { printf("\nTesting atomic swap\n"); fflush(stdout); } test_swap(); cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); if (me == 0) { printf("\nTesting atomic swap long\n"); fflush(stdout); } test_swap_long(); cmx_fence_all(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); if (me == 0) { printf("\nTesting aggregate put/get requests\n"); fflush(stdout); } cmx_barrier(CMX_GROUP_WORLD); cmx_barrier(CMX_GROUP_WORLD); if (me == 0) { printf("All tests passed\n"); fflush(stdout); } sleep(2); cmx_barrier(CMX_GROUP_WORLD); cmx_finalize(); MPI_Finalize(); return(0); } ga-5.9.2/comex/000077500000000000000000000000001500715745200132375ustar00rootroot00000000000000ga-5.9.2/comex/CMakeLists.txt000066400000000000000000000107511500715745200160030ustar00rootroot00000000000000# # module: CMakeLists.txt # author: Bruce Palmer # description: CMake build for GA. Only MPI-based runtimes are supported. # # DISCLAIMER # # This material was prepared as an account of work sponsored by an # agency of the United States Government. Neither the United States # Government nor the United States Department of Energy, nor Battelle, # nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR # ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, # COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, # SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT # INFRINGE PRIVATELY OWNED RIGHTS. # # # ACKNOWLEDGMENT # # This software and its documentation were produced with United States # Government support under Contract Number DE-AC06-76RLO-1830 awarded by # the United States Department of Energy. The United States Government # retains a paid-up non-exclusive, irrevocable worldwide license to # reproduce, prepare derivative works, perform publicly and display # publicly by or for the US Government, including the right to # distribute to other US Government contractors. # # -*- mode: cmake -*- # ------------------------------------------------------------- # file: CMakeLists.txt # ------------------------------------------------------------- option (COMEX_NETWORK_MPI_TS "use MPI 2-sided protocol for communication" OFF) option (COMEX_NETWORK_MPI_PR "use MPI progress ranks protocol for communication" OFF) option (COMEX_NETWORK_MPI3 "use MPI RMA protocols for communication" OFF) option (COMEX_NETWORK_MPI_MT "use MPI multi-threading protocol for communication" OFF) option (COMEX_NETWORK_MPI_PT "use MPI progress threads protocol for communication" OFF) include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR} src-common) if (MPI_TS) set(COMEX_DEVICE src-mpi/comex.c src-mpi/groups.c ) set (COMEX_NETWORK_MPI_TS ON) include_directories(AFTER src-mpi) elseif (MPI_PR) set(COMEX_DEVICE src-mpi-pr/comex.c src-mpi-pr/groups.c src-mpi-pr/reg_cache.c ) set (COMEX_NETWORK_MPI_PR ON) include_directories(AFTER src-mpi-pr) elseif (MPI3) set(COMEX_DEVICE src-mpi3/comex.c src-mpi3/groups.c src-mpi3/reg_win.c ) set (COMEX_NETWORK_MPI3 ON) include_directories(AFTER src-mpi3) elseif (MPI_MT) set(COMEX_DEVICE src-mpi-mt/comex.c src-mpi-mt/groups.c ) set (COMEX_NETWORK_MPI_MT ON) include_directories(AFTER src-mpi-mt) elseif (MPI_PT) set(COMEX_DEVICE src-mpi-pt/comex.c src-mpi-pt/groups.c src-mpi-pt/reg_cache.c ) set (COMEX_NETWORK_MPI_PT ON) include_directories(AFTER src-mpi-pt) endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(comex-utils) CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h ) # ------------------------------------------------------------- # ARMCI and COMEX header installation # ------------------------------------------------------------- set(ARMCI_HEADERS src-armci/armci.h src-armci/message.h src-armci/parmci.h ) set(COMEX_DEVICE_HEADERS src-common/comex.h ) install (FILES ${ARMCI_HEADERS} ${COMEX_DEVICE_HEADERS} DESTINATION include/ga ) list (APPEND GA_HEADER_PATHS ${CMAKE_CURRENT_LIST_DIR}/src-armci ${CMAKE_CURRENT_LIST_DIR}/src-common) set (GA_HEADER_PATHS ${GA_HEADER_PATHS} PARENT_SCOPE) # ------------------------------------------------------------- # ARMCI and COMEX library installation # ------------------------------------------------------------- set(ARMCI_FILES src-armci/armci.c src-armci/capi.c src-armci/groups.c src-armci/iterator.c src-armci/message.c ) add_library(armci_comex OBJECT ${ARMCI_FILES} ${COMEX_DEVICE} ) target_compile_definitions(armci_comex PRIVATE HAVE_CONFIG_H) target_include_directories(armci_comex PRIVATE ${MPI_C_INCLUDE_DIRS}) add_library(armci ${ARMCI_FILES} ${COMEX_DEVICE} ) target_compile_definitions(armci PRIVATE HAVE_CONFIG_H) add_library(GlobalArrays::armci ALIAS armci) target_include_directories(armci PRIVATE ${MPI_C_INCLUDE_DIRS}) install(TARGETS armci EXPORT globalarrays-targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) add_library(comex ${COMEX_DEVICE} ) add_library(GlobalArrays::comex ALIAS comex) target_include_directories(comex PRIVATE ${MPI_C_INCLUDE_DIRS}) install(TARGETS comex EXPORT globalarrays-targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) ga-5.9.2/comex/Makefile.am000066400000000000000000000206701500715745200153000ustar00rootroot00000000000000############################################################################## # Makefile.am for COMEX. # # Rationale: # This Makefile.am follows many of the suggestions outlined in the paper # "Recursive Make Considered Harmful". # # Additional targets: # Besides the traditional make targets supplied by Automake, we have added the # "checkprogs" target to build test programs. # # The usual aclocal nonsense to get include paths right. ACLOCAL_AMFLAGS = -I m4 # All public headers, installed programs, test programs, and example programs # are listed in these variables. Appended to throughout. These are the # automake variables used. include_HEADERS = bin_PROGRAMS = bin_SCRIPTS = check_PROGRAMS = check_LTLIBRARIES = lib_LTLIBRARIES = noinst_LTLIBRARIES = EXTRA_DIST = README BUILT_SOURCES = MOSTLYCLEANFILES = CLEANFILES = DISTCLEANFILES = MAINTAINERCLEANFILES = AM_CFLAGS = AM_CPPFLAGS = AM_LDFLAGS = LDADD = lib_LTLIBRARIES += libcomex.la # later Makefile fragments append to this libcomex_la_SOURCES = libcomex_la_LIBADD = libcomex_la_LIBADD += $(MPI_LIBS) libcomex_la_LIBADD += $(COMEX_NETWORK_LIBS) libcomex_la_LIBADD += $(BLAS_LIBS) libcomex_la_LIBADD += $(LIBS) if COMEX_NETWORK_OFI libcomex_la_LIBADD += $(LIBADD_DLOPEN) endif include_HEADERS += $(top_srcdir)/src-common/comex.h AM_CPPFLAGS += $(MPI_CPPFLAGS) AM_CPPFLAGS += $(COMEX_NETWORK_CPPFLAGS) AM_CPPFLAGS += $(BLAS_CPPFLAGS) AM_CPPFLAGS += -I$(top_srcdir)/src-common AM_LDFLAGS += $(MPI_LDFLAGS) AM_LDFLAGS += $(COMEX_NETWORK_LDFLAGS) AM_LDFLAGS += $(BLAS_LDFLAGS) LDADD += libcomex.la # internal libcomex for possibly rolling up into other libraries noinst_LTLIBRARIES += libcomexi.la libcomexi_la_SOURCES = $(libcomex_la_SOURCES) libcomexi_la_LIBADD = libcomexi_la_LIBADD += $(MPI_LIBS) libcomexi_la_LIBADD += $(COMEX_NETWORK_LIBS) libcomexi_la_LIBADD += $(BLAS_LIBS) libcomexi_la_LIBADD += $(LIBS) if COMEX_NETWORK_OFI libcomexi_la_LIBADD += $(LIBADD_DLOPEN) endif ############################################################################## # compiler and linker flags # # Important for external tools wanting to know how to link to COMEX. SED_NORMALIZE_WHITESPACE = $(SED) 's/ [ ]*/ /g;s/" /"/g;s/ "/"/g' .PHONY: flags flags: @echo ' =========================================================================== ' @echo ' Suggested compiler/linker options are as follows.' @echo ' COMEX libraries are installed in $(libdir)' @echo ' COMEX headers are installed in $(includedir)' @echo ' ' @echo ' CPPFLAGS="$(MPI_CPPFLAGS) $(COMEX_NETWORK_CPPFLAGS) $(BLAS_CPPFLAGS) -I$(includedir)"' | $(SED_NORMALIZE_WHITESPACE) @echo ' ' @echo ' LDFLAGS="$(MPI_LDFLAGS) $(COMEX_NETWORK_LDFLAGS) $(BLAS_LDFLAGS) -L$(libdir)"' | $(SED_NORMALIZE_WHITESPACE) @echo ' ' @echo ' For C/C++ Programs: ' @echo ' LIBS="-lcomex $(MPI_LIBS) $(COMEX_NETWORK_LIBS) $(BLAS_LIBS) $(LIBS)"' | $(SED_NORMALIZE_WHITESPACE) @echo ' =========================================================================== ' bin_SCRIPTS += tools/armci-config bin_SCRIPTS += tools/comex-config CLEANFILES += $(bin_SCRIPTS) ############################################################################## # src-portals4 # if COMEX_NETWORK_PORTALS4 include $(top_srcdir)/src-portals4/Makefile.inc endif ############################################################################## # src-ofa # if COMEX_NETWORK_OFA include $(top_srcdir)/src-ofa/Makefile.inc endif ############################################################################## # src-mpi # if COMEX_NETWORK_MPI_TS include $(top_srcdir)/src-mpi/Makefile.inc endif ############################################################################## # src-mpi-mt # if COMEX_NETWORK_MPI_MT include $(top_srcdir)/src-mpi-mt/Makefile.inc endif ############################################################################## # src-mpi-pt # if COMEX_NETWORK_MPI_PT include $(top_srcdir)/src-mpi-pt/Makefile.inc endif ############################################################################## # src-mpi-pr # if COMEX_NETWORK_MPI_PR include $(top_srcdir)/src-mpi-pr/Makefile.inc endif ############################################################################## # src-mpi3 # if COMEX_NETWORK_MPI3 include $(top_srcdir)/src-mpi3/Makefile.inc endif ############################################################################## # src-ofi # if COMEX_NETWORK_OFI include $(top_srcdir)/src-ofi/Makefile.inc endif ############################################################################## # src-armci # include $(top_srcdir)/src-armci/Makefile.inc ############################################################################## # profiling # if ENABLE_PROFILING_ARMCI AM_CPPFLAGS += -I$(top_srcdir)/src-armci if HAVE_SYS_WEAK_ALIAS_PRAGMA lib_LTLIBRARIES += libarmci_prof.la libarmci_prof_la_SOURCES = libarmci_prof_la_SOURCES += tools/armci_prof.c libarmci_la_SOURCES += src-armci/capi.c else # HAVE_SYS_WEAK_ALIAS_PRAGMA libarmci_la_SOURCES += tools/armci_prof.c endif # HAVE_SYS_WEAK_ALIAS_PRAGMA else # ENABLE_PROFILING_ARMCI libarmci_la_SOURCES += src-armci/capi.c include_HEADERS += src-armci/parmci.h endif ############################################################################## # testing # check_PROGRAMS += testing/perf check_PROGRAMS += testing/perf_amo check_PROGRAMS += testing/perf_contig check_PROGRAMS += testing/perf_strided check_PROGRAMS += testing/shift check_PROGRAMS += testing/test COMEX_SERIAL_TESTS = COMEX_SERIAL_TESTS_XFAIL = COMEX_DUAL_TESTS = COMEX_DUAL_TESTS_XFAIL = COMEX_PARALLEL_TESTS = COMEX_PARALLEL_TESTS_XFAIL = COMEX_TESTS = $(COMEX_SERIAL_TESTS) $(COMEX_DUAL_TESTS) $(COMEX_PARALLEL_TESTS) COMEX_TESTS_XFAIL = $(COMEX_SERIAL_TESTS_XFAIL) $(COMEX_DUAL_TESTS_XFAIL) $(COMEX_PARALLEL_TESTS_XFAIL) COMEX_DUAL_TESTS += testing/perf$(EXEEXT) COMEX_DUAL_TESTS += testing/perf_contig$(EXEEXT) COMEX_DUAL_TESTS += testing/perf_strided$(EXEEXT) COMEX_PARALLEL_TESTS += testing/perf_amo$(EXEEXT) COMEX_PARALLEL_TESTS += testing/shift$(EXEEXT) COMEX_PARALLEL_TESTS += testing/test$(EXEEXT) testing_perf_SOURCES = testing/perf.c testing_perf_amo_SOURCES = testing/perf_amo.c testing_perf_contig_SOURCES = testing/perf_contig.c testing_perf_strided_SOURCES = testing/perf_strided.c testing_shift_SOURCES = testing/shift.c testing_test_SOURCES = testing/test.c ############################################################################## # the end # .PHONY: checkprogs checkprogs: $(check_PROGRAMS) # support verbose/silent make rules for additional programs # sed SED_V = $(SED__v_$(V)) SED__v_ = $(SED__v_$(AM_DEFAULT_VERBOSITY)) SED__v_0 = @echo " SED " $@; ############################################################################## # test suite # # Some tests were commented out either because they required an input file or # they were failing for unknown reasons and we didn't want to further debug. # SERIAL_TESTS = SERIAL_TESTS += $(COMEX_SERIAL_TESTS) SERIAL_TESTS_XFAIL = SERIAL_TESTS_XFAIL += $(COMEX_SERIAL_TESTS_XFAIL) DUAL_TESTS = DUAL_TESTS += $(COMEX_DUAL_TESTS) DUAL_TESTS_XFAIL = DUAL_TESTS_XFAIL += $(COMEX_DUAL_TESTS_XFAIL) PARALLEL_TESTS = PARALLEL_TESTS += $(COMEX_PARALLEL_TESTS) PARALLEL_TESTS_XFAIL = PARALLEL_TESTS_XFAIL += $(COMEX_PARALLEL_TESTS_XFAIL) TESTS = TESTS += $(SERIAL_TESTS) TESTS += $(DUAL_TESTS) TESTS += $(PARALLEL_TESTS) XFAIL_TESTS = XFAIL_TESTS += $(SERIAL_TESTS_XFAIL) XFAIL_TESTS += $(DUAL_TESTS_XFAIL) XFAIL_TESTS += $(PARALLEL_TESTS_XFAIL) if CROSS_COMPILING maybe_mpiexec=`if echo "$(SERIAL_TESTS)" | $(GREP) "$$p" > /dev/null; then echo "$(MPIEXEC)" | $(SED) 's/%NP%/1/'; elif echo "$(DUAL_TESTS)" | $(GREP) "$$p" > /dev/null; then echo "$(MPIEXEC)" | $(SED) 's/%NP%/2/'; else echo "$(MPIEXEC)" | $(SED) 's/%NP%/$(NPROCS)/'; fi`; eval $$maybe_mpiexec else if COMEX_NETWORK_MPI_PR NPROCS1=$(shell expr $(NPROCS) + 1) LOG_COMPILER = \ maybe_mpiexec=`if echo "$(SERIAL_TESTS)" | $(GREP) "$$p" > /dev/null; then echo ""; elif echo "$(DUAL_TESTS)" | $(GREP) "$$p" > /dev/null; then echo "$(MPIEXEC)" | $(SED) 's/%NP%/3/'; elif echo "$(MPIEXEC)" | $(GREP) "%NP%" > /dev/null; then echo "$(MPIEXEC)" | $(SED) 's/%NP%/$(NPROCS1)/'; else echo "$(MPIEXEC)"; fi`; eval $$maybe_mpiexec else LOG_COMPILER = \ maybe_mpiexec=`if echo "$(SERIAL_TESTS)" | $(GREP) "$$p" > /dev/null; then echo ""; elif echo "$(DUAL_TESTS)" | $(GREP) "$$p" > /dev/null; then echo "$(MPIEXEC)" | $(SED) 's/%NP%/2/'; elif echo "$(MPIEXEC)" | $(GREP) "%NP%" > /dev/null; then echo "$(MPIEXEC)" | $(SED) 's/%NP%/$(NPROCS)/'; else echo "$(MPIEXEC)"; fi`; eval $$maybe_mpiexec endif # COMEX_NETWORK_MPI_PR endif # CROSS_COMPILING ga-5.9.2/comex/cmake/000077500000000000000000000000001500715745200143175ustar00rootroot00000000000000ga-5.9.2/comex/cmake/comex-utils.cmake000066400000000000000000000203541500715745200175760ustar00rootroot00000000000000 # ------------------------------------------------------------- # Check for variable sizes, include files and functions # ------------------------------------------------------------- # check size of different variables include(CheckTypeSize) check_type_size("int" CM_SIZEOF_INT) check_type_size("double" CM_SIZEOF_DOUBLE) check_type_size("float" CM_SIZEOF_FLOAT) check_type_size("long" CM_SIZEOF_LONG) check_type_size("long double" CM_SIZEOF_LONG_DOUBLE) check_type_size("long long" CM_SIZEOF_LONG_LONG) check_type_size("short" CM_SIZEOF_SHORT) # check for standard C/C++ include files include(CheckIncludeFiles) check_include_files("assert.h" HAVE_ASSERT_H) check_include_files("limits.h" HAVE_LIMITS_H) check_include_files("linux/limits.h" HAVE_LINUX_LIMITS_H) check_include_files("malloc.h" HAVE_MALLOC_H) check_include_files("math.h" HAVE_MATH_H) check_include_files("stddef.h" HAVE_STDDEF_H) check_include_files("stdint.h" HAVE_STDINT_H) check_include_files("stdio.h" HAVE_STDIO_H) check_include_files("stdlib.h" HAVE_STDLIB_H) check_include_files("strings.h" HAVE_STRINGS_H) check_include_files("string.h" HAVE_STRING_H) check_include_files("sys/types.h" HAVE_SYS_TYPES_H) check_include_files("unistd.h" HAVE_UNISTD_H) check_include_files("windows.h" HAVE_WINDOWS_H) # check for certain functions include(CheckFunctionExists) check_function_exists("bzero" HAVE_BZERO) # ------------------------------------------------------------- # figure out what BLAS library looks like # ------------------------------------------------------------- if (NOT ENABLE_BLAS) option (ENABLE_BLAS "Include external BLAS" ON) endif() INCLUDE( CheckCSourceCompiles ) if (ENABLE_BLAS) set (CMAKE_REQUIRED_LIBRARIES ${BLAS_LIBRARIES}) CHECK_C_SOURCE_COMPILES( " int main(int argc, char *argv[]) { typedef struct {float dummy[2];} SingleComplex; typedef struct {double dummy[2];} DoubleComplex; int n = 100; SingleComplex c1,ca[100],cb[100]; double d1,da[100],db[100]; float s1,sa[100],sb[100]; DoubleComplex z1,za[100],zb[100]; char caxpy_result = caxpy (n,c1,ca,1,cb,1); char daxpy_result = daxpy (n,d1,da,1,db,1); char saxpy_result = saxpy (n,s1,sa,1,sb,1); char zaxpy_result = zaxpy (n,z1,za,1,zb,1); char ccopy_result = ccopy (n,c1,ca,1,cb,1); char dcopy_result = dcopy (n,d1,da,1,db,1); char scopy_result = scopy (n,s1,sa,1,sb,1); char zcopy_result = zcopy (n,z1,za,1,zb,1); return 0; } " BLAS_1_SIGNATURE ) CHECK_C_SOURCE_COMPILES( " int main(int argc, char *argv[]) { typedef struct {float dummy[2];} SingleComplex; typedef struct {double dummy[2];} DoubleComplex; int n = 100; SingleComplex c1,ca[100],cb[100]; double d1,da[100],db[100]; float s1,sa[100],sb[100]; DoubleComplex z1,za[100],zb[100]; char caxpy_result = caxpy_ (n,c1,ca,1,cb,1); char daxpy_result = daxpy_ (n,d1,da,1,db,1); char saxpy_result = saxpy_ (n,s1,sa,1,sb,1); char zaxpy_result = zaxpy_ (n,z1,za,1,zb,1); char ccopy_result = ccopy_ (n,c1,ca,1,cb,1); char dcopy_result = dcopy_ (n,d1,da,1,db,1); char scopy_result = scopy_ (n,s1,sa,1,sb,1); char zcopy_result = zcopy_ (n,z1,za,1,zb,1); return 0; } " BLAS_2_SIGNATURE ) CHECK_C_SOURCE_COMPILES( " int main(int argc, char *argv[]) { typedef struct {float dummy[2];} SingleComplex; typedef struct {double dummy[2];} DoubleComplex; int n = 100; SingleComplex c1,ca[100],cb[100]; double d1,da[100],db[100]; float s1,sa[100],sb[100]; DoubleComplex z1,za[100],zb[100]; char caxpy_result = caxpy__ (n,c1,ca,1,cb,1); char daxpy_result = daxpy__ (n,d1,da,1,db,1); char saxpy_result = saxpy__ (n,s1,sa,1,sb,1); char zaxpy_result = zaxpy__ (n,z1,za,1,zb,1); char ccopy_result = ccopy__ (n,c1,ca,1,cb,1); char dcopy_result = dcopy__ (n,d1,da,1,db,1); char scopy_result = scopy__ (n,s1,sa,1,sb,1); char zcopy_result = zcopy__ (n,z1,za,1,zb,1); return 0; } " BLAS_3_SIGNATURE ) CHECK_C_SOURCE_COMPILES( " int main(int argc, char *argv[]) { typedef struct {float dummy[2];} SingleComplex; typedef struct {double dummy[2];} DoubleComplex; int n = 100; SingleComplex c1,ca[100],cb[100]; double d1,da[100],db[100]; float s1,sa[100],sb[100]; DoubleComplex z1,za[100],zb[100]; char caxpy_result = CAXPY (n,c1,ca,1,cb,1); char daxpy_result = DAXPY (n,d1,da,1,db,1); char saxpy_result = SAXPY (n,s1,sa,1,sb,1); char zaxpy_result = ZAXPY (n,z1,za,1,zb,1); char ccopy_result = CCOPY (n,c1,ca,1,cb,1); char dcopy_result = DCOPY (n,d1,da,1,db,1); char scopy_result = SCOPY (n,s1,sa,1,sb,1); char zcopy_result = ZCOPY (n,z1,za,1,zb,1); return 0; } " BLAS_4_SIGNATURE ) CHECK_C_SOURCE_COMPILES( " int main(int argc, char *argv[]) { typedef struct {float dummy[2];} SingleComplex; typedef struct {double dummy[2];} DoubleComplex; int n = 100; SingleComplex c1,ca[100],cb[100]; double d1,da[100],db[100]; float s1,sa[100],sb[100]; DoubleComplex z1,za[100],zb[100]; char caxpy_result = CAXPY_ (n,c1,ca,1,cb,1); char daxpy_result = DAXPY_ (n,d1,da,1,db,1); char saxpy_result = SAXPY_ (n,s1,sa,1,sb,1); char zaxpy_result = ZAXPY_ (n,z1,za,1,zb,1); char ccopy_result = CCOPY_ (n,c1,ca,1,cb,1); char dcopy_result = DCOPY_ (n,d1,da,1,db,1); char scopy_result = SCOPY_ (n,s1,sa,1,sb,1); char zcopy_result = ZCOPY_ (n,z1,za,1,zb,1); return 0; } " BLAS_5_SIGNATURE ) CHECK_C_SOURCE_COMPILES( " int main(int argc, char *argv[]) { typedef struct {float dummy[2];} SingleComplex; typedef struct {double dummy[2];} DoubleComplex; int n = 100; SingleComplex c1,ca[100],cb[100]; double d1,da[100],db[100]; float s1,sa[100],sb[100]; DoubleComplex z1,za[100],zb[100]; char caxpy_result = CAXPY__ (n,c1,ca,1,cb,1); char daxpy_result = DAXPY__ (n,d1,da,1,db,1); char saxpy_result = SAXPY__ (n,s1,sa,1,sb,1); char zaxpy_result = ZAXPY__ (n,z1,za,1,zb,1); char ccopy_result = CCOPY__ (n,c1,ca,1,cb,1); char dcopy_result = DCOPY__ (n,d1,da,1,db,1); char scopy_result = SCOPY__ (n,s1,sa,1,sb,1); char zcopy_result = ZCOPY__ (n,z1,za,1,zb,1); return 0; } " BLAS_6_SIGNATURE ) endif() # set blas symbols if (BLAS_1_SIGNATURE) set(CM_BLAS_CAXPY caxpy) set(CM_BLAS_DAXPY daxpy) set(CM_BLAS_SAXPY saxpy) set(CM_BLAS_ZAXPY zaxpy) set(CM_BLAS_CCOPY ccopy) set(CM_BLAS_DCOPY dcopy) set(CM_BLAS_SCOPY scopy) set(CM_BLAS_ZCOPY zcopy) elseif (BLAS_2_SIGNATURE) set(CM_BLAS_CAXPY caxpy_) set(CM_BLAS_DAXPY daxpy_) set(CM_BLAS_SAXPY saxpy_) set(CM_BLAS_ZAXPY zaxpy_) set(CM_BLAS_CCOPY ccopy_) set(CM_BLAS_DCOPY dcopy_) set(CM_BLAS_SCOPY scopy_) set(CM_BLAS_ZCOPY zcopy_) elseif (BLAS_3_SIGNATURE) set(CM_BLAS_CAXPY caxpy__) set(CM_BLAS_DAXPY daxpy__) set(CM_BLAS_SAXPY saxpy__) set(CM_BLAS_ZAXPY zaxpy__) set(CM_BLAS_CCOPY ccopy__) set(CM_BLAS_DCOPY dcopy__) set(CM_BLAS_SCOPY scopy__) set(CM_BLAS_ZCOPY zcopy__) elseif (BLAS_4_SIGNATURE) set(CM_BLAS_CAXPY CAXPY) set(CM_BLAS_DAXPY DAXPY) set(CM_BLAS_SAXPY SAXPY) set(CM_BLAS_ZAXPY ZAXPY) set(CM_BLAS_CCOPY CCOPY) set(CM_BLAS_DCOPY DCOPY) set(CM_BLAS_SCOPY SCOPY) set(CM_BLAS_ZCOPY ZCOPY) elseif (BLAS_5_SIGNATURE) set(CM_BLAS_CAXPY CAXPY_) set(CM_BLAS_DAXPY DAXPY_) set(CM_BLAS_SAXPY SAXPY_) set(CM_BLAS_ZAXPY ZAXPY_) set(CM_BLAS_CCOPY CCOPY_) set(CM_BLAS_DCOPY DCOPY_) set(CM_BLAS_SCOPY SCOPY_) set(CM_BLAS_ZCOPY ZCOPY_) elseif (BLAS_6_SIGNATURE) set(CM_BLAS_CAXPY CAXPY__) set(CM_BLAS_DAXPY DAXPY__) set(CM_BLAS_SAXPY SAXPY__) set(CM_BLAS_ZAXPY ZAXPY__) set(CM_BLAS_CCOPY CCOPY__) set(CM_BLAS_DCOPY DCOPY__) set(CM_BLAS_SCOPY SCOPY__) set(CM_BLAS_ZCOPY ZCOPY__) else() set(CM_BLAS_CAXPY caxpy_) set(CM_BLAS_DAXPY daxpy_) set(CM_BLAS_SAXPY saxpy_) set(CM_BLAS_ZAXPY zaxpy_) set(CM_BLAS_CCOPY ccopy_) set(CM_BLAS_DCOPY dcopy_) set(CM_BLAS_SCOPY scopy_) set(CM_BLAS_ZCOPY zcopy_) endif() # Check for FUNCTION_NAME CHECK_C_SOURCE_COMPILES( " #include int sub(int i) { printf(\"%s\", __func__); } int main(int argc, char *argv[]) { sub(0); return 0; } " FUNCTION_1_SIGNATURE ) CHECK_C_SOURCE_COMPILES( " #include int sub(int i) { printf(\"%s\", __FUNCTION__); } int main(int argc, char *argv[]) { sub(0); return 0; } " FUNCTION_2_SIGNATURE ) if (FUNCTION_1_SIGNATURE) set(CM_FUNCTION_NAME __func__) elseif(FUNCTION_2_SIGNATURE) set(CM_FUNCTION_NAME __FUNCTION__) else() set(CM_FUNCTION_NAME __func__) endif() # check for availability of some functions include(CheckFunctionExists) CHECK_FUNCTION_EXISTS(sched_setaffinity HAVE_SCHED_SETAFFINITY) CHECK_FUNCTION_EXISTS(pthread_setaffinity_np HAVE_PTHREAD_SETAFFINITY_NP) ga-5.9.2/comex/cmake/config.h.in000066400000000000000000000024101500715745200163370ustar00rootroot00000000000000#cmakedefine01 COMEX_NETWORK_MPI3 #cmakedefine01 COMEX_NETWORK_MPI_MT #cmakedefine01 COMEX_NETWORK_MPI_PR #cmakedefine01 COMEX_NETWORK_MPI_PT #cmakedefine01 COMEX_NETWORK_MPI_TS #cmakedefine01 HAVE_BLAS #define BLAS_CAXPY ${CM_BLAS_CAXPY} #define BLAS_DAXPY ${CM_BLAS_DAXPY} #define BLAS_SAXPY ${CM_BLAS_SAXPY} #define BLAS_ZAXPY ${CM_BLAS_ZAXPY} #define BLAS_CCOPY ${CM_BLAS_CCOPY} #define BLAS_DCOPY ${CM_BLAS_DCOPY} #define BLAS_SCOPY ${CM_BLAS_SCOPY} #define BLAS_ZCOPY ${CM_BLAS_ZCOPY} #cmakedefine01 HAVE_ASSERT_H #cmakedefine01 HAVE_BZERO #cmakedefine01 HAVE_MATH_H #cmakedefine01 HAVE_STDIO_H #cmakedefine01 HAVE_STDLIB_H #cmakedefine01 HAVE_STRCHR #cmakedefine01 HAVE_STRING_H #cmakedefine01 HAVE_UNISTD_H #define FUNCTION_NAME ${CM_FUNCTION_NAME} #cmakedefine01 HAVE_PTHREAD_SETAFFINITY_NP #cmakedefine01 HAVE_SCHED_SETAFFINITY #cmakedefine01 HAVE_SYS_WEAK_ALIAS_PRAGMA #cmakedefine NDEBUG #cmakedefine __CRAYXE #cmakedefine01 ENABLE_SYSV #define SIZEOF_INT ${CM_SIZEOF_INT} #define SIZEOF_LONG ${CM_SIZEOF_LONG} #define SIZEOF_LONG_LONG ${CM_SIZEOF_LONG_LONG} #define SIZEOF_VOIDP ${CMAKE_SIZEOF_VOID_P} #define BLAS_SIZE ${BLAS_SIZE} /* #define BLAS_SIZE ${CM_BLAS_SIZE} */ #ifndef __cplusplus #cmakedefine inline ${inline} #endif #cmakedefine restrict ${restrict} ga-5.9.2/comex/configure.ac000066400000000000000000000177501500715745200155370ustar00rootroot00000000000000# Process this file with autoconf to produce a configure script. ############################################################################### # Init autoconf ############################################################################### AC_PREREQ([2.67]) AC_INIT([Communication Runtime for Extreme Scale (comex)], [1.1], [https://github.com/GlobalArrays/ga/issues], [comex], [https://hpc.pnl.gov/globalarrays/]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([src-common/comex.h]) ############################################################################### # Init automake ############################################################################### AM_INIT_AUTOMAKE([color-tests foreign parallel-tests silent-rules subdir-objects]) # Don't emit "rebuild rules" for configure, Makefile.ins, etc. AM_MAINTAINER_MODE ############################################################################### # Misc. information and package setup. ############################################################################### COMEX_WITH_HELP COMEX_TOP_BUILDDIR="`pwd`" cd "$srcdir" COMEX_TOP_SRCDIR="`pwd`" cd "$COMEX_TOP_BUILDDIR" AS_IF([test "$COMEX_TOP_BUILDDIR" != "$COMEX_TOP_SRCDIR"], [AC_MSG_NOTICE([Detected VPATH build])]) # We use the MPI compiler wrappers instead of the standard compilers. COMEX_WITH_MPI # In case users don't care whether MPI actually works during configure. COMEX_DISABLE_MPI_TESTS # ARMCI profiling layer COMEX_ENABLE_PROFILING_ARMCI ######################################### # C compiler ######################################## AC_MSG_NOTICE AC_MSG_NOTICE([C compiler]) AC_MSG_NOTICE COMEX_PROG_MPICC AC_USE_SYSTEM_EXTENSIONS AS_IF([test x$with_mpi_wrappers = xyes], [COMEX_MPI_UNWRAP], [COMEX_ARG_PARSE([with_mpi], [MPI_LIBS], [MPI_LDFLAGS], [MPI_CPPFLAGS])]) AS_CASE([$enable_mpi_tests], [yes],[COMEX_MPICC_TEST_LINK], [no], [COMEX_MPICC_TEST_COMPILE]) AM_CONDITIONAL([CROSS_COMPILING], [test "x$cross_compiling" = xyes]) # Establish the underlying network infrastructure (MPI, OFA, etc) COMEX_NETWORK_SETUP # Checks for C header files. AC_HEADER_ASSERT AC_HEADER_DIRENT AC_HEADER_STDBOOL AC_HEADER_STDC AC_HEADER_SYS_WAIT COMEX_CHECK_HEADERS([assert.h]) COMEX_CHECK_HEADERS([errno.h]) COMEX_CHECK_HEADERS([getopt.h]) COMEX_CHECK_HEADERS([math.h]) COMEX_CHECK_HEADERS([pthread.h]) COMEX_CHECK_HEADERS([sched.h]) COMEX_CHECK_HEADERS([semaphore.h]) COMEX_CHECK_HEADERS([stdint.h]) COMEX_CHECK_HEADERS([stdio.h]) COMEX_CHECK_HEADERS([stdlib.h]) COMEX_CHECK_HEADERS([string.h]) COMEX_CHECK_HEADERS([strings.h]) COMEX_CHECK_HEADERS([sys/time.h]) COMEX_CHECK_HEADERS([sys/types.h]) COMEX_CHECK_HEADERS([unistd.h]) # Checks for C typedefs, structures, and compiler characteristics. AC_C_CONST AC_C_INLINE AC_C_RESTRICT AC_C_VOLATILE AC_TYPE_INT8_T AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_INT64_T AC_TYPE_OFF_T AC_TYPE_PID_T AC_TYPE_SIZE_T AC_TYPE_UINT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_CHECK_TYPES([ptrdiff_t]) COMEX_FUNCTION COMEX_DISABLE_SYS_WEAK_ALIAS COMEX_SYS_WEAK_ALIAS # Checks for C type sizes. comex_save_LIBS="$LIBS"; LIBS="$LIBS $MPI_LIBS" comex_save_LDFLAGS="$LDFLAGS"; LDFLAGS="$LDFLAGS $MPI_LDFLAGS" comex_save_CPPFLAGS="$CPPFLAGS"; CPPFLAGS="$CPPFLAGS $MPI_CPPFLAGS" AC_CHECK_SIZEOF([MPI_Aint], [], [[#include ]]) LIBS="$comex_save_LIBS" LDFLAGS="$comex_save_LDFLAGS" CPPFLAGS="$comex_save_CPPFLAGS" AC_CHECK_SIZEOF([void*]) AC_CHECK_SIZEOF([char]) AC_CHECK_SIZEOF([short]) AC_CHECK_SIZEOF([int]) AC_CHECK_SIZEOF([long]) AC_CHECK_SIZEOF([long long]) AC_CHECK_SIZEOF([float]) AC_CHECK_SIZEOF([double]) # Checks for C library functions. AC_FUNC_MMAP COMEX_SEARCH_LIBS([sqrt], [m]) COMEX_SEARCH_LIBS([sem_open], [rt pthread]) COMEX_SEARCH_LIBS([shm_open], [rt]) COMEX_SEARCH_LIBS([shm_unlink], [rt]) COMEX_CHECK_FUNCS([bzero]) COMEX_CHECK_FUNCS([gettimeofday]) COMEX_CHECK_FUNCS([memset]) COMEX_CHECK_FUNCS([munmap]) COMEX_CHECK_FUNCS([pthread_setaffinity_np]) COMEX_CHECK_FUNCS([sched_setaffinity]) COMEX_CHECK_FUNCS([strchr]) COMEX_CHECK_FUNCS([strdup]) COMEX_CHECK_FUNCS([strncasecmp]) COMEX_CHECK_FUNCS([strstr]) # Checks for advanced MPI functions. comex_save_LIBS="$LIBS"; LIBS="$LIBS $MPI_LIBS" comex_save_LDFLAGS="$LDFLAGS"; LDFLAGS="$LDFLAGS $MPI_LDFLAGS" comex_save_CPPFLAGS="$CPPFLAGS"; CPPFLAGS="$CPPFLAGS $MPI_CPPFLAGS" COMEX_CHECK_FUNCS([MPI_Ibarrier]) COMEX_CHECK_FUNCS([MPIX_Ibarrier]) LIBS="$comex_save_LIBS" LDFLAGS="$comex_save_LDFLAGS" CPPFLAGS="$comex_save_CPPFLAGS" # SysV shared memory COMEX_ENABLE_SYSV # Checks for C libraries. COMEX_BLAS ############################################################################### # Checks for programs. ############################################################################### AC_MSG_NOTICE AC_MSG_NOTICE([Checks for additional programs]) AC_MSG_NOTICE AC_PROG_GREP AC_PROG_SED ############################################################################### # Libtool setup -- no compiler/linker tests after this ############################################################################### AC_MSG_NOTICE AC_MSG_NOTICE([Libtool setup]) AC_MSG_NOTICE # temporarily restore unwrapped compilers # this works around a bug where libtool sadly relies on matching compiler # names in order to determine features (Fortran only, I think) # libtool doesn't recognize MPI compiler names, nor should it AS_IF([test x$with_mpi_wrappers = xyes], [COMEX_MPI_UNWRAP_PUSH]) COMEX_AR LT_INIT([disable-shared]) # and now that that's over, put the MPI compilers back # also, the above hack incorrectly sets the base compiler as the linker AS_IF([test x$with_mpi_wrappers = xyes], [COMEX_MPI_UNWRAP_POP compiler="$CC" LTCC="$CC" lt_save_CC="$CC" compiler_DEFAULT="$CC"]) ############################################################################### # Test suite setup ############################################################################### AC_ARG_VAR([NPROCS], [number of procs to use for parallel tests (default 4)]) AS_IF([test "x$NPROCS" = x], [NPROCS=4]) AC_SUBST([NPROCS]) AC_ARG_VAR([MPIEXEC], [how to run parallel tests if built with MPI e.g. "mpiexec -np %NP%"]) AS_IF([test "x$MPIEXEC" = x], [AC_PATH_PROGS([MPIEXEC], [mpirun mpiexec]) MPIEXEC="$MPIEXEC -n %NP%"]) AC_SUBST([MPIEXEC]) ############################################################################### # The End ############################################################################### AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([tools/comex-config], [chmod +x tools/comex-config]) AC_CONFIG_FILES([tools/armci-config], [chmod +x tools/armci-config]) AC_OUTPUT # Report on what we found. AC_MSG_NOTICE([]) AC_MSG_NOTICE([**************************************************************]) AC_MSG_NOTICE([ $PACKAGE_NAME configured as follows:]) AC_MSG_NOTICE([**************************************************************]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ MPI_LIBS=$MPI_LIBS]) AC_MSG_NOTICE([ MPI_LDFLAGS=$MPI_LDFLAGS]) AC_MSG_NOTICE([ MPI_CPPFLAGS=$MPI_CPPFLAGS]) AC_MSG_NOTICE([ COMEX_NETWORK=$comex_network]) AC_MSG_NOTICE([ COMEX_NETWORK_LDFLAGS=$COMEX_NETWORK_LDFLAGS]) AC_MSG_NOTICE([ COMEX_NETWORK_LIBS=$COMEX_NETWORK_LIBS]) AC_MSG_NOTICE([COMEX_NETWORK_CPPFLAGS=$COMEX_NETWORK_CPPFLAGS]) AC_MSG_NOTICE([ CC=$CC]) AS_IF([test "x$with_mpi_wrappers" = xyes], [ AC_MSG_NOTICE([ unwrapped CC=$comex_cv_mpic_naked]) ]) AC_MSG_NOTICE([ CFLAGS=$CFLAGS]) AC_MSG_NOTICE([ CPP=$CPP]) AC_MSG_NOTICE([ CPPFLAGS=$CPPFLAGS]) AC_MSG_NOTICE([ LDFLAGS=$LDFLAGS]) AC_MSG_NOTICE([ LIBS=$LIBS]) AC_MSG_NOTICE([ FLIBS=$FLIBS]) AC_MSG_NOTICE([ AR=$AR]) AC_MSG_NOTICE([ AR_FLAGS=$AR_FLAGS]) AC_MSG_NOTICE([ DEFS=$DEFS]) AC_MSG_NOTICE([ SHELL=$SHELL]) AC_MSG_NOTICE([ MPIEXEC=$MPIEXEC]) AC_MSG_NOTICE([ NPROCS=$NPROCS]) AC_MSG_NOTICE([]) ga-5.9.2/comex/m4/000077500000000000000000000000001500715745200135575ustar00rootroot00000000000000ga-5.9.2/comex/m4/ax_sys_weak_alias.m4000066400000000000000000000272471500715745200175230ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_sys_weak_alias.html # =========================================================================== # # SYNOPSIS # # AX_SYS_WEAK_ALIAS # # DESCRIPTION # # Determines whether weak aliases are supported on the system, and if so, # what scheme is used to declare them. Also checks to see if aliases can # cross object file boundaries, as some systems don't permit them to. # # Most systems permit something called a "weak alias" or "weak symbol." # These aliases permit a library to provide a stub form of a routine # defined in another library, thus allowing the first library to operate # even if the other library is not linked. This macro will check for # support of weak aliases, figure out what schemes are available, and # determine some characteristics of the weak alias support -- primarily, # whether a weak alias declared in one object file may be referenced from # another object file. # # There are four known schemes of declaring weak symbols; each scheme is # checked in turn, and the first one found is prefered. Note that only one # of the mentioned preprocessor macros will be defined! # # 1. Function attributes # # This scheme was first introduced by the GNU C compiler, and attaches # attributes to particular functions. It is among the easiest to use, and # so is the first one checked. If this scheme is detected, the # preprocessor macro HAVE_SYS_WEAK_ALIAS_ATTRIBUTE will be defined to 1. # This scheme is used as in the following code fragment: # # void __weakf(int c) # { # /* Function definition... */ # } # # void weakf(int c) __attribute__((weak, alias("__weakf"))); # # 2. #pragma weak # # This scheme is in use by many compilers other than the GNU C compiler. # It is also particularly easy to use, and fairly portable -- well, as # portable as these things get. If this scheme is detected first, the # preprocessor macro HAVE_SYS_WEAK_ALIAS_PRAGMA will be defined to 1. This # scheme is used as in the following code fragment: # # extern void weakf(int c); # #pragma weak weakf = __weakf # void __weakf(int c) # { # /* Function definition... */ # } # # 3. #pragma _HP_SECONDARY_DEF # # This scheme appears to be in use by the HP compiler. As it is rather # specialized, this is one of the last schemes checked. If it is the first # one detected, the preprocessor macro HAVE_SYS_WEAK_ALIAS_HPSECONDARY # will be defined to 1. This scheme is used as in the following code # fragment: # # extern void weakf(int c); # #pragma _HP_SECONDARY_DEF __weakf weakf # void __weakf(int c) # { # /* Function definition... */ # } # # 4. #pragma _CRI duplicate # # This scheme appears to be in use by the Cray compiler. As it is rather # specialized, it too is one of the last schemes checked. If it is the # first one detected, the preprocessor macro # HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE will be defined to 1. This scheme is # used as in the following code fragment: # # extern void weakf(int c); # #pragma _CRI duplicate weakf as __weakf # void __weakf(int c) # { # /* Function definition... */ # } # # In addition to the preprocessor macros listed above, if any scheme is # found, the preprocessor macro HAVE_SYS_WEAK_ALIAS will also be defined # to 1. # # Once a weak aliasing scheme has been found, a check will be performed to # see if weak aliases are honored across object file boundaries. If they # are, the HAVE_SYS_WEAK_ALIAS_CROSSFILE preprocessor macro is defined to # 1. # # This Autoconf macro also makes two substitutions. The first, WEAK_ALIAS, # contains the name of the scheme found (one of "attribute", "pragma", # "hpsecondary", or "criduplicate"), or "no" if no weak aliasing scheme # was found. The second, WEAK_ALIAS_CROSSFILE, is set to "yes" or "no" # depending on whether or not weak aliases may cross object file # boundaries. # # LICENSE # # Copyright (c) 2008 Kevin L. Mitchell # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 6 AU_ALIAS([KLM_SYS_WEAK_ALIAS], [AX_SYS_WEAK_ALIAS]) AC_DEFUN([AX_SYS_WEAK_ALIAS], [ # starting point: no aliasing scheme yet... ax_sys_weak_alias=no # Figure out what kind of aliasing may be supported... _AX_SYS_WEAK_ALIAS_ATTRIBUTE _AX_SYS_WEAK_ALIAS_PRAGMA _AX_SYS_WEAK_ALIAS_HPSECONDARY _AX_SYS_WEAK_ALIAS_CRIDUPLICATE # Do we actually support aliasing? AC_CACHE_CHECK([how to create weak aliases with $CC], [ax_cv_sys_weak_alias], [ax_cv_sys_weak_alias=$ax_sys_weak_alias]) # OK, set a #define AS_IF([test $ax_cv_sys_weak_alias != no], [ AC_DEFINE([HAVE_SYS_WEAK_ALIAS], 1, [Define this if your system can create weak aliases]) ]) # Can aliases cross object file boundaries? _AX_SYS_WEAK_ALIAS_CROSSFILE # OK, remember the results AC_SUBST([WEAK_ALIAS], [$ax_cv_sys_weak_alias]) AC_SUBST([WEAK_ALIAS_CROSSFILE], [$ax_cv_sys_weak_alias_crossfile]) ]) AC_DEFUN([_AX_SYS_WEAK_ALIAS_ATTRIBUTE], [ # Test whether compiler accepts __attribute__ form of weak aliasing AC_CACHE_CHECK([whether $CC accepts function __attribute__((weak,alias()))], [ax_cv_sys_weak_alias_attribute], [ # We add -Werror if it's gcc to force an error exit if the weak attribute # isn't understood AS_IF([test $GCC = yes], [ save_CFLAGS=$CFLAGS CFLAGS=-Werror]) # Try linking with a weak alias... AC_LINK_IFELSE([ AC_LANG_PROGRAM([ void __weakf(int c) {} void weakf(int c) __attribute__((weak, alias("__weakf")));], [weakf(0)])], [ax_cv_sys_weak_alias_attribute=yes], [ax_cv_sys_weak_alias_attribute=no]) # Restore original CFLAGS AS_IF([test $GCC = yes], [ CFLAGS=$save_CFLAGS]) ]) # What was the result of the test? AS_IF([test $ax_sys_weak_alias = no && test $ax_cv_sys_weak_alias_attribute = yes], [ ax_sys_weak_alias=attribute AC_DEFINE([HAVE_SYS_WEAK_ALIAS_ATTRIBUTE], 1, [Define this if weak aliases may be created with __attribute__]) ]) ]) AC_DEFUN([_AX_SYS_WEAK_ALIAS_PRAGMA], [ # Test whether compiler accepts #pragma form of weak aliasing AC_CACHE_CHECK([whether $CC supports @%:@pragma weak], [ax_cv_sys_weak_alias_pragma], [ # Try linking with a weak alias... AC_LINK_IFELSE([ AC_LANG_PROGRAM([ extern void weakf(int c); @%:@pragma weak weakf = __weakf void __weakf(int c) {}], [weakf(0)])], [ax_cv_sys_weak_alias_pragma=yes], [ax_cv_sys_weak_alias_pragma=no]) ]) # What was the result of the test? AS_IF([test $ax_sys_weak_alias = no && test $ax_cv_sys_weak_alias_pragma = yes], [ ax_sys_weak_alias=pragma AC_DEFINE([HAVE_SYS_WEAK_ALIAS_PRAGMA], 1, [Define this if weak aliases may be created with @%:@pragma weak]) ]) ]) AC_DEFUN([_AX_SYS_WEAK_ALIAS_HPSECONDARY], [ # Test whether compiler accepts _HP_SECONDARY_DEF pragma from HP... AC_CACHE_CHECK([whether $CC supports @%:@pragma _HP_SECONDARY_DEF], [ax_cv_sys_weak_alias_hpsecondary], [ # Try linking with a weak alias... AC_LINK_IFELSE([ AC_LANG_PROGRAM([ extern void weakf(int c); @%:@pragma _HP_SECONDARY_DEF __weakf weakf void __weakf(int c) {}], [weakf(0)])], [ax_cv_sys_weak_alias_hpsecondary=yes], [ax_cv_sys_weak_alias_hpsecondary=no]) ]) # What was the result of the test? AS_IF([test $ax_sys_weak_alias = no && test $ax_cv_sys_weak_alias_hpsecondary = yes], [ ax_sys_weak_alias=hpsecondary AC_DEFINE([HAVE_SYS_WEAK_ALIAS_HPSECONDARY], 1, [Define this if weak aliases may be created with @%:@pragma _HP_SECONDARY_DEF]) ]) ]) AC_DEFUN([_AX_SYS_WEAK_ALIAS_CRIDUPLICATE], [ # Test whether compiler accepts "_CRI duplicate" pragma from Cray AC_CACHE_CHECK([whether $CC supports @%:@pragma _CRI duplicate], [ax_cv_sys_weak_alias_criduplicate], [ # Try linking with a weak alias... AC_LINK_IFELSE([ AC_LANG_PROGRAM([ extern void weakf(int c); @%:@pragma _CRI duplicate weakf as __weakf void __weakf(int c) {}], [weakf(0)])], [ax_cv_sys_weak_alias_criduplicate=yes], [ax_cv_sys_weak_alias_criduplicate=no]) ]) # What was the result of the test? AS_IF([test $ax_sys_weak_alias = no && test $ax_cv_sys_weak_alias_criduplicate = yes], [ ax_sys_weak_alias=criduplicate AC_DEFINE([HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE], 1, [Define this if weak aliases may be created with @%:@pragma _CRI duplicate]) ]) ]) dnl Note: This macro is modeled closely on AC_LINK_IFELSE, and in fact dnl depends on some implementation details of that macro, particularly dnl its use of _AC_MSG_LOG_CONFTEST to log the failed test program and dnl its use of ac_link for running the linker. AC_DEFUN([_AX_SYS_WEAK_ALIAS_CROSSFILE], [ # Check to see if weak aliases can cross object file boundaries AC_CACHE_CHECK([whether $CC supports weak aliases across object file boundaries], [ax_cv_sys_weak_alias_crossfile], [ AS_IF([test $ax_cv_sys_weak_alias = no], [ax_cv_sys_weak_alias_crossfile=no], [ dnl Must build our own test files... # conftest1 contains our weak alias definition... cat >conftest1.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest1.$ac_ext cat >>conftest1.$ac_ext <<_ACEOF /* end confdefs.h. */ @%:@ifndef HAVE_SYS_WEAK_ALIAS_ATTRIBUTE extern void weakf(int c); @%:@endif @%:@if defined(HAVE_SYS_WEAK_ALIAS_PRAGMA) @%:@pragma weak weakf = __weakf @%:@elif defined(HAVE_SYS_WEAK_ALIAS_HPSECONDARY) @%:@pragma _HP_SECONDARY_DEF __weakf weakf @%:@elif defined(HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE) @%:@pragma _CRI duplicate weakf as __weakf @%:@endif void __weakf(int c) {} @%:@ifdef HAVE_SYS_WEAK_ALIAS_ATTRIBUTE void weakf(int c) __attribute((weak, alias("__weakf"))); @%:@endif _ACEOF # And conftest2 contains our main routine that calls it cat >conftest2.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >> conftest2.$ac_ext cat >>conftest2.$ac_ext <<_ACEOF /* end confdefs.h. */ extern void weakf(int c); int main () { weakf(0); return 0; } _ACEOF # We must remove the object files (if any) ourselves... rm -f conftest2.$ac_objext conftest$ac_exeext # Change ac_link to compile *2* files together save_aclink=$ac_link ac_link=`echo "$ac_link" | \ sed -e 's/conftest\(\.\$ac_ext\)/conftest1\1 conftest2\1/'` dnl Substitute our own routine for logging the conftest m4_pushdef([_AC_MSG_LOG_CONFTEST], [echo "$as_me: failed program was:" >&AS_MESSAGE_LOG_FD echo ">>> conftest1.$ac_ext" >&AS_MESSAGE_LOG_FD sed "s/^/| /" conftest1.$ac_ext >&AS_MESSAGE_LOG_FD echo ">>> conftest2.$ac_ext" >&AS_MESSAGE_LOG_FD sed "s/^/| /" conftest2.$ac_ext >&AS_MESSAGE_LOG_FD ])dnl # Since we created the files ourselves, don't use SOURCE argument AC_LINK_IFELSE(, [ax_cv_sys_weak_alias_crossfile=yes], [ax_cv_sys_weak_alias_crossfile=no]) dnl Restore _AC_MSG_LOG_CONFTEST m4_popdef([_AC_MSG_LOG_CONFTEST])dnl # Restore ac_link ac_link=$save_aclink # We must remove the object files (if any) and C files ourselves... rm -f conftest1.$ac_ext conftest2.$ac_ext \ conftest1.$ac_objext conftest2.$ac_objext ]) ]) # What were the results of the test? AS_IF([test $ax_cv_sys_weak_alias_crossfile = yes], [ AC_DEFINE([HAVE_SYS_WEAK_ALIAS_CROSSFILE], 1, [Define this if weak aliases in other files are honored]) ]) ]) ga-5.9.2/comex/m4/comex_ar.m4000066400000000000000000000007661500715745200156270ustar00rootroot00000000000000# COMEX_AR # ----- # Libtool doesn't advertise AR nor AR_FLAGS in case the user wishes to # override them. Further, certain systems require a different archiver. # RANLIB may also be affected. # Use this prior to LT_INIT. # # Known archivers: # ar - all known systems # AC_DEFUN([COMEX_AR], [ AC_ARG_VAR([AR], [archiver used by libtool (default: ar)]) AC_ARG_VAR([AR_FLAGS], [archiver flags used by libtool (default: cru)]) AC_ARG_VAR([RANLIB], [generates index to archive (default: ranlib)]) ])dnl ga-5.9.2/comex/m4/comex_arg_parse.m4000066400000000000000000000070321500715745200171610ustar00rootroot00000000000000# COMEX_ARG_PARSE(ARG, VAR_LIBS, VAR_LDFLAGS, VAR_CPPFLAGS) # ------------------------------------------------------ # Parse whitespace-separated ARG into appropriate LIBS, LDFLAGS, and # CPPFLAGS variables. # some examples (not exhaustive): # $arg="/apps/mpi/hp/2.03.01.00/include/64 /apps/mpi/hp/2.03.01.00/lib/linux_amd64 -lmtmpi" # $arg="/usr/local" # $arg="-I/usr/local/include -L/usr/local/lib -lsomelib" # $arg="/usr/lib/mpich-shmem -lfmpich-shmem -lmpich-shmem -lpmpich-shmem" # $arg="/usr/lib/mpich-shmem/include /usr/lib/mpich-shmem/lib -lfmpich-shmem -lmpich-shmem -lpmpich-shmem" # $arg="/usr/local/lib64 /usr/local/include64" # # Special Cases: # -mkl[=arg] Intel compiler with special -mkl flag for headers and linking AC_DEFUN([COMEX_ARG_PARSE], [AC_COMPUTE_INT([comex_arg_parse_sizeof_voidp], [(long int) (sizeof (void*))]) for arg in $$1 ; do AS_CASE([$arg], [yes], [], [no], [], [-l*], [$2="$$2 $arg"], [-L*], [$3="$$3 $arg"], [-WL*], [$3="$$3 $arg"], [-Wl*], [$3="$$3 $arg"], [-I*], [$4="$$4 $arg"], [*.a], [$2="$$2 $arg"], [*.so], [$2="$$2 $arg"], [*lib], [AS_IF([test -d $arg], [$3="$$3 -L$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [*lib/], [AS_IF([test -d $arg], [$3="$$3 -L$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [*lib64], [AS_IF([test -d $arg], [$3="$$3 -L$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [*lib64/], [AS_IF([test -d $arg], [$3="$$3 -L$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [*include], [AS_IF([test -d $arg], [$4="$$4 -I$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [*include/], [AS_IF([test -d $arg], [$4="$$4 -I$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [*include64], [AS_IF([test -d $arg], [$4="$$4 -I$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [*include64/], [AS_IF([test -d $arg], [$4="$$4 -I$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [-mkl*], [$2="$$2 $arg"], [comex_arg_parse_ok=no]) # $arg didn't fit the most common cases # check for subdirectories e.g. lib,include AS_IF([test "x$comex_arg_parse_ok" = xno], [AS_IF([test "x$comex_arg_parse_sizeof_voidp" = x8], [AS_IF([test -d $arg/lib64], [$3="$$3 -L$arg/lib64"; comex_arg_parse_ok=yes], [test -d $arg/lib], [$3="$$3 -L$arg/lib"; comex_arg_parse_ok=yes]) AS_IF([test -d $arg/include64],[$4="$$4 -I$arg/include64"; comex_arg_parse_ok=yes], [test -d $arg/include], [$4="$$4 -I$arg/include"; comex_arg_parse_ok=yes])], [AS_IF([test -d $arg/lib], [$3="$$3 -L$arg/lib"; comex_arg_parse_ok=yes]) AS_IF([test -d $arg/include], [$4="$$4 -I$arg/include"; comex_arg_parse_ok=yes])])]) # $arg still unknown, look for "lib" and "include" anywhere... AS_IF([test "x$comex_arg_parse_ok" = xno], [AS_CASE([$arg], [*lib*], [AS_IF([test -d $arg], [$3="$$3 -L$arg"; comex_arg_parse_ok=yes])], [*include*],[AS_IF([test -d $arg], [$4="$$4 -I$arg"; comex_arg_parse_ok=yes])])]) # warn user that $arg fell through AS_IF([test "x$comex_arg_parse_ok" = xno], [AC_MSG_WARN([$arg of $1 not parsed])]) done])dnl ga-5.9.2/comex/m4/comex_blas.m4000066400000000000000000000275771500715745200161570ustar00rootroot00000000000000# COMEX_C_BLAS_TEST # ----------------- # Generate C conftest for BLAS. AC_DEFUN([COMEX_C_BLAS_TEST], [ AC_LANG_CONFTEST([AC_LANG_PROGRAM( [#ifdef __cplusplus extern "C" { #endif char $caxpy (); char $daxpy (); char $saxpy (); char $zaxpy (); char $ccopy (); char $dcopy (); char $scopy (); char $zcopy (); #ifdef __cplusplus } #endif ], [[char caxpy_result = $caxpy (); char daxpy_result = $daxpy (); char saxpy_result = $saxpy (); char zaxpy_result = $zaxpy (); char ccopy_result = $ccopy (); char dcopy_result = $dcopy (); char scopy_result = $scopy (); char zcopy_result = $zcopy (); ]])]) ]) # COMEX_RUN_BLAS_TEST # ------------------- # Test the linker. # Clears BLAS_LIBS on failure. Sets comex_blas_ok=yes on success. AC_DEFUN([COMEX_RUN_BLAS_TEST], [ AC_LANG_PUSH([C]) comex_blas_ok=no AS_IF([test "x$comex_blas_ok" = xno], [caxpy=caxpy daxpy=daxpy saxpy=saxpy zaxpy=zaxpy ccopy=ccopy dcopy=dcopy scopy=scopy zcopy=zcopy COMEX_C_BLAS_TEST() AC_LINK_IFELSE([], [comex_blas_ok=yes])]) AS_IF([test "x$comex_blas_ok" = xno], [caxpy=caxpy_ daxpy=daxpy_ saxpy=saxpy_ zaxpy=zaxpy_ ccopy=ccopy_ dcopy=dcopy_ scopy=scopy_ zcopy=zcopy_ COMEX_C_BLAS_TEST() AC_LINK_IFELSE([], [comex_blas_ok=yes])]) AS_IF([test "x$comex_blas_ok" = xno], [caxpy=caxpy__ daxpy=daxpy__ saxpy=saxpy__ zaxpy=zaxpy__ ccopy=ccopy__ dcopy=dcopy__ scopy=scopy__ zcopy=zcopy__ COMEX_C_BLAS_TEST() AC_LINK_IFELSE([], [comex_blas_ok=yes])]) AS_IF([test "x$comex_blas_ok" = xno], [caxpy=CAXPY daxpy=DAXPY saxpy=SAXPY zaxpy=ZAXPY ccopy=CCOPY dcopy=DCOPY scopy=SCOPY zcopy=ZCOPY COMEX_C_BLAS_TEST() AC_LINK_IFELSE([], [comex_blas_ok=yes])]) AS_IF([test "x$comex_blas_ok" = xno], [caxpy=CAXPY_ daxpy=DAXPY_ saxpy=SAXPY_ zaxpy=ZAXPY_ ccopy=CCOPY_ dcopy=DCOPY_ scopy=SCOPY_ zcopy=ZCOPY_ COMEX_C_BLAS_TEST() AC_LINK_IFELSE([], [comex_blas_ok=yes])]) AS_IF([test "x$comex_blas_ok" = xno], [caxpy=CAXPY__ daxpy=DAXPY__ saxpy=SAXPY__ zaxpy=ZAXPY__ ccopy=CCOPY__ dcopy=DCOPY__ scopy=SCOPY__ zcopy=ZCOPY__ COMEX_C_BLAS_TEST() AC_LINK_IFELSE([], [comex_blas_ok=yes])]) AS_IF([test "x$comex_blas_ok" = xno], [caxpy=NOTFOUND daxpy=NOTFOUND saxpy=NOTFOUND zaxpy=NOTFOUND ccopy=NOTFOUND dcopy=NOTFOUND scopy=NOTFOUND zcopy=NOTFOUND BLAS_LIBS=]) AC_LANG_POP([C]) ])dnl # COMEX_BLAS([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # ------------------------------------------------- # Parse the --with-blas argument and test for all *axpy routines. We use # different tests depending on whether Fortran sources are enabled. There are # many flavors of BLAS that we test for explicitly, although the list could # probably be reduced based on currently available systems. AC_DEFUN([COMEX_BLAS], [ blas_size=4 blas_size_hack=no AC_ARG_WITH([blas], [AS_HELP_STRING([--with-blas[[=ARG]]], [use external BLAS library; attempt to detect sizeof(INTEGER)])], [blas_size_hack=yes]) AC_ARG_WITH([blas4], [AS_HELP_STRING([--with-blas4[[=ARG]]], [use external BLAS library compiled with sizeof(INTEGER)==4])], [blas_size=4; with_blas="$with_blas4"]) AC_ARG_WITH([blas8], [AS_HELP_STRING([--with-blas8[[=ARG]]], [use external BLAS library compiled with sizeof(INTEGER)==8])], [blas_size=8; with_blas="$with_blas8"]) comex_blas_ok=no AS_IF([test "x$with_blas" = xno], [comex_blas_ok=skip]) # Parse --with-blas argument. Clear previous values first. BLAS_LIBS= BLAS_LDFLAGS= BLAS_CPPFLAGS= COMEX_ARG_PARSE([with_blas], [BLAS_LIBS], [BLAS_LDFLAGS], [BLAS_CPPFLAGS]) comex_save_LIBS="$LIBS" comex_save_LDFLAGS="$LDFLAGS"; LDFLAGS="$BLAS_LDFLAGS $LDFLAGS" comex_save_CPPFLAGS="$CPPFLAGS"; CPPFLAGS="$BLAS_CPPFLAGS $CPPFLAGS" AC_MSG_NOTICE([Attempting to locate BLAS library]) # First, check environment/command-line variables. # If failed, erase BLAS_LIBS but maintain BLAS_LDFLAGS and BLAS_CPPFLAGS. AS_IF([test $comex_blas_ok = no], [AC_MSG_CHECKING([for BLAS with user-supplied flags]) LIBS="$BLAS_LIBS $LIBS" COMEX_RUN_BLAS_TEST() LIBS="$comex_save_LIBS" AS_IF([test $comex_blas_ok = yes], [AS_IF([test $blas_size_hack = yes], [AS_CASE(["$BLAS_LIBS $LIBS $LDFLAGS $CPPFLAGS"], [*ilp64*], [blas_size=8], # Intel MKL [*_int64*], [blas_size=8])])]) # AMD ACML AC_MSG_RESULT([$comex_blas_ok])]) # AMD Core Math Library (ACML) AS_IF([test $comex_blas_ok = no], [AC_MSG_CHECKING([for BLAS in AMD Core Math Library]) # add -lacml to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*acml*], [], [BLAS_LIBS="-lacml"]) LIBS="$BLAS_LIBS $LIBS" COMEX_RUN_BLAS_TEST() LIBS="$comex_save_LIBS" AS_IF([test "x$comex_blas_ok" = xyes], [AS_IF([test $blas_size_hack = yes], [AS_CASE(["$BLAS_LIBS $LIBS $LDFLAGS $CPPFLAGS"], [*_int64*], [blas_size=8])])]) AC_MSG_RESULT([$comex_blas_ok])]) # Intel MKL library AS_IF([test $comex_blas_ok = no], [AC_MSG_CHECKING([for BLAS in Intel Math Kernel Library]) # add -lmkl to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*mkl*], [], [BLAS_LIBS="-lmkl"]) LIBS="$BLAS_LIBS $LIBS" COMEX_RUN_BLAS_TEST() LIBS="$comex_save_LIBS" AS_IF([test "x$comex_blas_ok" = xyes], [AS_IF([test $blas_size_hack = yes], [AS_CASE(["$BLAS_LIBS $LIBS $LDFLAGS $CPPFLAGS"], [*ilp64*], [blas_size=8])])]) AC_MSG_RESULT([$comex_blas_ok])]) # ATLAS library (http://math-atlas.sourceforge.net/) AS_IF([test $comex_blas_ok = no], [AC_MSG_CHECKING([for BLAS in ATLAS]) AS_IF([test "x$enable_f77" = xno], [# add -lcblas if needed but missing from LIBS AS_CASE([$LIBS], [*cblas*], [], [BLAS_LIBS="-lcblas"])], [# add -lf77blas if needed but missing from LIBS AS_CASE([$LIBS], [*f77blas*], [], [BLAS_LIBS="-lf77blas"])]) # add -latlas if needed but missing from LIBS AS_CASE([$LIBS], [*atlas*], [], [BLAS_LIBS="$BLAS_LIBS -latlas"]) LIBS="$BLAS_LIBS $LIBS" COMEX_RUN_BLAS_TEST() LIBS="$comex_save_LIBS" AC_MSG_RESULT([$comex_blas_ok])]) # PhiPACK libraries (requires generic BLAS lib, too) AS_IF([test $comex_blas_ok = no], [AC_MSG_CHECKING([for BLAS in PhiPACK libraries]) # add -lblas to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*blas*], [], [BLAS_LIBS="-lblas"]) # add -ldgemm to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*dgemm*], [], [BLAS_LIBS="-ldgemm $BLAS_LIBS"]) # add -lsgemm to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*sgemm*], [], [BLAS_LIBS="-lsgemm $BLAS_LIBS"]) LIBS="$BLAS_LIBS $LIBS" COMEX_RUN_BLAS_TEST() LIBS="$comex_save_LIBS" AC_MSG_RESULT([$comex_blas_ok])]) # Apple Accelerate.framework AS_IF([test $comex_blas_ok = no], [AC_MSG_CHECKING([for BLAS in Apple Accelerate.framework]) # add -framework Accelerate to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*Accelerate*], [], [BLAS_LIBS="-framework Accelerate"]) LIBS="$BLAS_LIBS $LIBS" COMEX_RUN_BLAS_TEST() LIBS="$comex_save_LIBS" AC_MSG_RESULT([$comex_blas_ok])]) # Apple vecLib.framework AS_IF([test $comex_blas_ok = no], [AC_MSG_CHECKING([for BLAS in Apple vecLib.framework]) # add -framework vecLib to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*vecLib*], [], [BLAS_LIBS="-framework vecLib"]) LIBS="$BLAS_LIBS $LIBS" COMEX_RUN_BLAS_TEST() LIBS="$comex_save_LIBS" AC_MSG_RESULT([$comex_blas_ok])]) # Alpha CXML library (CXML stands for Compaq Extended Math Library) AS_IF([test $comex_blas_ok = no], [AC_MSG_CHECKING([for BLAS in Alpha CXML library]) # add -lcxml to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*cxml*], [], [BLAS_LIBS="-lcxml"]) LIBS="$BLAS_LIBS $LIBS" COMEX_RUN_BLAS_TEST() LIBS="$comex_save_LIBS" AS_IF([test $comex_blas_ok = no], [# add -lcxml to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*cxml*], [], [BLAS_LIBS="-lcxml"]) # add -lcpml to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*cpml*], [], [BLAS_LIBS="$BLAS_LIBS -lcpml"]) LIBS="$BLAS_LIBS $LIBS" COMEX_RUN_BLAS_TEST() LIBS="$comex_save_LIBS"]) AC_MSG_RESULT([$comex_blas_ok])]) # Alpha DXML library (now called CXML, see above) # Sun Performance library AS_IF([test $comex_blas_ok = no], [AC_MSG_CHECKING([for BLAS in Sun Performance Library]) # add -xlic_lib=sunperf to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*sunperf*], [], [BLAS_LIBS="-xlic_lib=sunperf"]) LIBS="$BLAS_LIBS $LIBS" COMEX_RUN_BLAS_TEST() LIBS="$comex_save_LIBS" AS_IF([test $comex_blas_ok = no], [# add -xlic_lib=sunperf to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*sunperf*], [], [BLAS_LIBS="-xlic_lib=sunperf"]) # add -lsunmath to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*sunmath*], [], [BLAS_LIBS="$BLAS_LIBS -lsunmath"]) LIBS="$BLAS_LIBS $LIBS" COMEX_RUN_BLAS_TEST() LIBS="$comex_save_LIBS"]) AC_MSG_RESULT([$comex_blas_ok])]) # IBM ESSL library (might require generic BLAS lib, too) AS_IF([test $comex_blas_ok = no], [AC_MSG_CHECKING([for BLAS in IBM ESSL library]) # add -lessl to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*essl*], [], [BLAS_LIBS="-lessl"]) LIBS="$BLAS_LIBS $LIBS" COMEX_RUN_BLAS_TEST() LIBS="$comex_save_LIBS" AS_IF([test $comex_blas_ok = no], [# add both -lessl and -lblas to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*essl*], [], [BLAS_LIBS="-lessl"]) AS_CASE([$LIBS], [*blas*], [], [BLAS_LIBS="$BLAS_LIBS -lblas"]) LIBS="$BLAS_LIBS $LIBS" COMEX_RUN_BLAS_TEST() LIBS="$comex_save_LIBS"]) AC_MSG_RESULT([$comex_blas_ok])]) # Generic BLAS library AS_IF([test $comex_blas_ok = no], [AC_MSG_CHECKING([for BLAS in generic library]) BLAS_LIBS="-lblas" LIBS="$BLAS_LIBS $LIBS" COMEX_RUN_BLAS_TEST() LIBS="$comex_save_LIBS" AC_MSG_RESULT([$comex_blas_ok])]) CPPFLAGS="$comex_save_CPPFLAGS" LDFLAGS="$comex_save_LDFLAGS" AC_SUBST([BLAS_LIBS]) AC_SUBST([BLAS_LDFLAGS]) AC_SUBST([BLAS_CPPFLAGS]) # Tests are complete. Execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: AS_IF([test $comex_blas_ok = yes], [have_blas=1 $1], [AC_MSG_WARN([BLAS library not found, using internal BLAS]) blas_size=4 have_blas=0 $2]) AC_SUBST([have_blas]) AC_SUBST([blas_size]) AC_DEFINE_UNQUOTED([HAVE_BLAS], [$have_blas], [Define to 1 if using external BLAS library]) AC_DEFINE_UNQUOTED([BLAS_SIZE], [$blas_size], [Define to sizeof(INTEGER) used to compile BLAS]) AC_DEFINE_UNQUOTED([BLAS_CAXPY], [$caxpy], [Define to name of caxpy routine to call from C]) AC_DEFINE_UNQUOTED([BLAS_DAXPY], [$daxpy], [Define to name of daxpy routine to call from C]) AC_DEFINE_UNQUOTED([BLAS_SAXPY], [$saxpy], [Define to name of saxpy routine to call from C]) AC_DEFINE_UNQUOTED([BLAS_ZAXPY], [$zaxpy], [Define to name of zaxpy routine to call from C]) AC_DEFINE_UNQUOTED([BLAS_CCOPY], [$ccopy], [Define to name of ccopy routine to call from C]) AC_DEFINE_UNQUOTED([BLAS_DCOPY], [$dcopy], [Define to name of dcopy routine to call from C]) AC_DEFINE_UNQUOTED([BLAS_SCOPY], [$scopy], [Define to name of scopy routine to call from C]) AC_DEFINE_UNQUOTED([BLAS_ZCOPY], [$zcopy], [Define to name of zcopy routine to call from C]) AM_CONDITIONAL([HAVE_BLAS], [test $comex_blas_ok = yes]) ])dnl COMEX_BLAS ga-5.9.2/comex/m4/comex_check_func.m4000066400000000000000000000025261500715745200173110ustar00rootroot00000000000000# COMEX_CHECK_FUNCS(FUNCTION..., [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ------------------------------------------------------------------------ # Inspired by # http://pdh11.blogspot.com/2009/04/standard-macros-available-in-gnu.html # but really a modified version of AC_CHECK_FUNCS. AC_DEFUN([COMEX_CHECK_FUNCS], [m4_map_args_w([$1], [_AH_CHECK_FUNC(], [)])]dnl [AS_FOR([AC_func], [ac_func], [$1], [AC_CHECK_FUNC(AC_func, [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_]AC_func), 1, [Define to 1 if you have AC_func, 0 if you don't]) $2], [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_]AC_func), 0, [Define to 1 if you have AC_func, 0 if you don't]) $3], [$4])dnl]) ])# COMEX_CHECK_FUNCS # COMEX_SEARCH_LIBS(FUNCTION, LIBRARIES..., # [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ------------------------------------------------------------------------ # Inspired by # http://pdh11.blogspot.com/2009/04/standard-macros-available-in-gnu.html # but really a wrapped version of AC_SEARCH_LIBS. AC_DEFUN([COMEX_SEARCH_LIBS], [ AS_VAR_PUSHDEF([HAVE_FUNC], m4_toupper(m4_translit([$1], [-.], [__]))) AC_SEARCH_LIBS([$1], [$2], [AC_DEFINE([HAVE_FUNC], [1], [Define to 1 if you have the `$1' function.]) $3], [AC_DEFINE([HAVE_FUNC], [0], [Define to 1 if you have the `$1' function.]) $4]) AS_VAR_POPDEF([HAVE_FUNC]) ]) # COMEX_SEARCH_LIBS ga-5.9.2/comex/m4/comex_check_header.m4000066400000000000000000000013641500715745200176050ustar00rootroot00000000000000# COMEX_CHECK_HEADERS(HEADER-FILE..., # [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], [INCLUDES]) # ---------------------------------------------------------------------- # Inspired by # http://pdh11.blogspot.com/2009/04/standard-macros-available-in-gnu.html # but really a modified version of AC_CHECK_HEADERS. AC_DEFUN([COMEX_CHECK_HEADERS], [m4_map_args_w([$1], [_AH_CHECK_HEADER(], [)])]dnl [AS_FOR([AC_header], [ac_header], [$1], [AC_CHECK_HEADER(AC_header, [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_]AC_header), 1, [Define to 1 if you have AC_header, 0 if you don't]) $2], [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_]AC_header), 0, [Define to 1 if you have AC_header, 0 if you don't]) $3], [$4])dnl]) ])# COMEX_CHECK_HEADERS ga-5.9.2/comex/m4/comex_enable_profile.m4000066400000000000000000000023051500715745200201620ustar00rootroot00000000000000# COMEX_ENABLE_PROFILING_ARMCI # ---------------------------- # Whether to enable profiling for ARMCI. # AC_DEFINEs COMEX_PROFILING_ARMCI. AC_DEFUN([COMEX_ENABLE_PROFILING_ARMCI], [ AC_ARG_ENABLE([profiling-armci], [AS_HELP_STRING([--enable-profiling-armci], [enable profiling for ARMCI])], [], [enable_profiling_armci=no]) AS_IF([test "x$enable_profiling_armci" = xyes], [AC_DEFINE([COMEX_PROFILING_ARMCI], [1], [Define if ARMCI profiling is enabled])]) AM_CONDITIONAL([ENABLE_PROFILING_ARMCI], [test "x$enable_profiling_armci" = xyes]) ])dnl # COMEX_ENABLE_PROFILING_COMEX # ---------------------------- # Whether to enable profiling for COMEX. # AC_DEFINEs COMEX_PROFILING_COMEX. AC_DEFUN([COMEX_ENABLE_PROFILING_COMEX], [ AC_ARG_ENABLE([profiling-comex], [AS_HELP_STRING([--enable-profiling-comex], [enable profiling for ComEx])], [], [enable_profiling_comex=no]) AS_IF([test "x$enable_profiling_comex" = xyes], [AC_DEFINE([COMEX_PROFILING_COMEX], [1], [Define if ComEx profiling is enabled])]) AM_CONDITIONAL([ENABLE_PROFILING_COMEX], [test "x$enable_profiling_comex" = xyes]) ])dnl ga-5.9.2/comex/m4/comex_enable_sysv.m4000066400000000000000000000007611500715745200175320ustar00rootroot00000000000000# COMEX_ENABLE_SYSV # ----------------- # Whether to enable System V shared memory. AC_DEFUN([COMEX_ENABLE_SYSV], [AC_ARG_ENABLE([sysv], [AS_HELP_STRING([--enable-sysv], [enable System V shared memory])], [], [enable_sysv=0]) AS_IF([test "x$enable_sysv" = xyes], [enable_sysv=1], [enable_sysv=0]) AC_SUBST([enable_sysv]) AC_DEFINE_UNQUOTED([ENABLE_SYSV], [$enable_sysv], [Define to 1 if SYSV is enabled]) AM_CONDITIONAL([ENABLE_SYSV], [test "x$enable_sysv" = x1]) ])dnl ga-5.9.2/comex/m4/comex_function.m4000066400000000000000000000017221500715745200170430ustar00rootroot00000000000000# COMEX_FUNCTION # ----------- # Define FUNCTION_NAME to either __func__ or __FUNCTION__ appropriately. # If all else fails, #define FUNCTION_NAME . AC_DEFUN([COMEX_FUNCTION], [AC_CACHE_CHECK([for preprocessor symbol for function name], [comex_cv_cpp_function], [AS_IF([test x$comex_cv_cpp_function = x], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[extern int printf(const char *format, ...);]], [[printf("__func__ = %s\n", __func__);]])], [comex_cv_cpp_function=__func__])]) AS_IF([test x$comex_cv_cpp_function = x], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[extern int printf(const char *format, ...);]], [[printf("__FUNCTION__ = %s\n", __FUNCTION__);]])], [comex_cv_cpp_function=__FUNCTION__])]) AS_IF([test x$comex_cv_cpp_function = x], [comex_cv_cpp_function='"Unknown"'])]) AC_DEFINE_UNQUOTED([FUNCTION_NAME], [$comex_cv_cpp_function], [CPP symbol for function name, if available]) ])# COMEX_FUNCTION ga-5.9.2/comex/m4/comex_mpi_test_disable.m4000066400000000000000000000004501500715745200205220ustar00rootroot00000000000000# COMEX_DISABLE_MPI_TESTS() # --------------------- # Whether to disable all MPI linker tests. AC_DEFUN([COMEX_DISABLE_MPI_TESTS], [AC_ARG_ENABLE([mpi-tests], [AS_HELP_STRING([--disable-mpi-tests], [disable MPI linker tests])], [], [enable_mpi_tests=yes]) ])# COMEX_DISABLE_MPI_TESTS ga-5.9.2/comex/m4/comex_mpi_unwrap.m4000066400000000000000000000221151500715745200173760ustar00rootroot00000000000000# COMEX_MPI_UNWRAP() # --------------- # Attempt to unwrap the MPI compiler for the current language and determine # the underlying compiler. # # The strategy is to first compare the stdout of the version flags using # a custom perl script. Next we combine stdout and sterr for the comparison. # Occasionally, the MPI compiler will always report a non-zero exit status. # That is the last case checked for. AC_DEFUN([COMEX_MPI_UNWRAP], [ # Find perl. AC_PATH_PROG([PERL], [perl]) # Create inside.pl. rm -f inside.pl [cat >inside.pl <<"EOF" #!/usr/bin/perl use strict; use warnings; my $numargs = @S|@#ARGV + 1; if ($numargs != 2) { print "Usage: wrapped.txt naked.txt\n"; exit 1; } # Read each input file as a string (rather than a list). local $/=undef; open WRAPPED, "$ARGV[0]" or die "Could not open wrapped text file: $!"; my $wrapped_lines = ; close WRAPPED; open NAKED, "$ARGV[1]" or die "Could not open naked text file: $!"; my $naked_lines = ; close NAKED; # Replace newlines, + from wrapped and naked lines. $wrapped_lines =~ tr/\n+/ /; $naked_lines =~ tr/\n+/ /; # Remove whitespace from beginning of wrapped and naked lines. $wrapped_lines =~ s/^\s+//; $naked_lines =~ s/^\s+//; # Remove whitespace from end of wrapped and naked lines. $wrapped_lines =~ s/\s+$//; $naked_lines =~ s/\s+$//; # If either wrapped_lines or naked_lines are empty, this is an error. # It is assumed that the particular version string which created the input # files should generate SOMETHING. unless ($wrapped_lines) { exit 1; } unless ($naked_lines) { exit 1; } # Cray compilers append a timestamp into their version string. Remove it. if ($wrapped_lines =~ /\QCray\E/) { $wrapped_lines = substr $wrapped_lines, 0, -28; $naked_lines = substr $naked_lines, 0, -28; } # Can the naked lines be found within the wrapped lines? if ($wrapped_lines =~ /\Q$naked_lines\E/) { #print "Found as substring\n"; exit 0; } # Are the naked lines exactly the same as the wrapped lines? elsif ($wrapped_lines eq $naked_lines) { #print "Found equal\n"; exit 0; } else { #print "Not found\n"; exit 1; } EOF] inside="$PERL inside.pl" wrapped="$_AC_CC" AC_LANG_CASE( [C], [AS_CASE([$wrapped], [*_r], [compilers="bgxlc_r xlc_r"], [*], [compilers="bgxlc xlc pgcc pathcc icc sxcc fcc opencc suncc craycc gcc ecc cl ccc cc"]) ], [C++], [AS_CASE([$wrapped], [*_r], [compilers="bgxlC_r xlC_r"], [*], [compilers="icpc pgCC pathCC sxc++ xlC bgxlC openCC sunCC craycxx g++ c++ gpp aCC cxx cc++ cl.exe FCC KCC RCC CC"]) ], [Fortran 77], [AS_CASE([$wrapped], [*_r], [compilers="bgxlf95_r xlf95_r bgxlf90_r xlf90_r bgxlf_r xlf_r"], [*], [compilers="gfortran g95 bgxlf95 xlf95 f95 fort ifort ifc efc pgf95 pathf95 lf95 openf95 sunf95 crayftn bgxlf90 xlf90 f90 pgf90 pathf90 pghpf epcf90 sxf90 openf90 sunf90 g77 bgxlf xlf f77 frt pgf77 pathf77 cf77 fort77 fl32 af77"]) ], [Fortran], [ ]) AS_VAR_PUSHDEF([comex_save_comp], [comex_save_[]_AC_CC[]]) AS_VAR_PUSHDEF([comex_orig_comp], [comex_orig_[]_AC_CC[]]) AS_VAR_PUSHDEF([comex_cv_mpi_naked], [comex_cv_mpi[]_AC_LANG_ABBREV[]_naked]) AC_CACHE_CHECK([for base $wrapped compiler], [comex_cv_mpi_naked], [ base="`$wrapped -show 2>/dev/null | sed 's/@<:@ @:>@.*@S|@//' | head -1`" comex_save_comp="$_AC_CC" _AC_CC="$base" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [comex_cv_mpi_naked="$base"]) _AC_CC="$comex_save_comp" versions="--version -v -V -qversion" found_wrapped_version=0 # Try separating stdout and stderr. Only compare stdout. AS_IF([test "x$comex_cv_mpi_naked" = x], [ # prepend any CC/CXX/F77 the user may have specified compilers="$comex_orig_comp $compilers" echo "only comparing stdout" >&AS_MESSAGE_LOG_FD for version in $versions do echo "trying version=$version" >&AS_MESSAGE_LOG_FD rm -f mpi.txt mpi.err naked.txt naked.err AS_IF([$wrapped $version 1>mpi.txt 2>mpi.err], [found_wrapped_version=1 for naked_compiler in $compilers do AS_IF([test "x$naked_compiler" != "x$wrapped"], [AS_IF([$naked_compiler $version 1>naked.txt 2>naked.err], [AS_IF([$inside mpi.txt naked.txt >/dev/null], [comex_cv_mpi_naked=$naked_compiler; break], [echo "inside.pl $wrapped $naked_compiler failed, skipping" >&AS_MESSAGE_LOG_FD])], [echo "$naked_compiler $version failed, skipping" >&AS_MESSAGE_LOG_FD])]) done], [echo "$wrapped $version failed, skipping" >&AS_MESSAGE_LOG_FD]) AS_IF([test "x$comex_cv_mpi_naked" != x], [break]) done ]) # Perhaps none of the MPI compilers had a zero exit status (this is bad). # In this case we have to do a brute force match regardless of exit status. AS_IF([test "x$found_wrapped_version" = x0], [ AS_IF([test "x$comex_cv_mpi_naked" = x], [ echo "no zero exit status found for MPI compilers" >&AS_MESSAGE_LOG_FD for version in $versions do echo "trying version=$version" >&AS_MESSAGE_LOG_FD rm -f mpi.txt mpi.err $wrapped $version 1>mpi.txt 2>mpi.err for naked_compiler in $compilers do AS_IF([test "x$naked_compiler" != "x$wrapped"], [rm -f naked.txt naked.err AS_IF([$naked_compiler $version 1>naked.txt 2>naked.err], [AS_IF([$inside mpi.txt naked.txt >/dev/null], [comex_cv_mpi_naked=$naked_compiler; break], [echo "inside.pl $wrapped $naked_compiler failed, skipping" >&AS_MESSAGE_LOG_FD])], [echo "$naked_compiler $version failed, skipping" >&AS_MESSAGE_LOG_FD])]) done AS_IF([test "x$comex_cv_mpi_naked" != x], [break]) done ]) ]) # Try by combining stdout/err into one file. AS_IF([test "x$comex_cv_mpi_naked" = x], [ echo "try combining stdout and stderr into one file" >&AS_MESSAGE_LOG_FD for version in $versions do echo "trying version=$version" >&AS_MESSAGE_LOG_FD rm -f mpi.txt naked.txt AS_IF([$wrapped $version 1>mpi.txt 2>&1], [for naked_compiler in $compilers do AS_IF([test "x$naked_compiler" != "x$wrapped"], [AS_IF([$naked_compiler $version 1>naked.txt 2>&1], [AS_IF([$inside mpi.txt naked.txt >/dev/null], [comex_cv_mpi_naked=$naked_compiler; break], [echo "inside.pl $wrapped $naked_compiler failed, skipping" >&AS_MESSAGE_LOG_FD])], [echo "$naked_compiler $version failed, skipping" >&AS_MESSAGE_LOG_FD])]) done], [echo "$wrapped $version failed, skipping" >&AS_MESSAGE_LOG_FD]) AS_IF([test "x$comex_cv_mpi_naked" != x], [break]) done ]) # If we got this far, then it's likely that the MPI compiler had a zero exit # status when it shouldn't have for one version flag, but later had a non-zero # exit status for a flag it shouldn't have. One false positive hid a false # negative. In this case, brute force compare all MPI compiler output against # all compiler output. AS_IF([test "x$comex_cv_mpi_naked" = x], [ echo "we have a very badly behaving MPI compiler" >&AS_MESSAGE_LOG_FD for version in $versions do echo "trying version=$version" >&AS_MESSAGE_LOG_FD rm -f mpi.txt mpi.err $wrapped $version 1>mpi.txt 2>mpi.err for naked_compiler in $compilers do AS_IF([test "x$naked_compiler" != "x$wrapped"], [rm -f naked.txt naked.err AS_IF([$naked_compiler $version 1>naked.txt 2>naked.err], [AS_IF([$inside mpi.txt naked.txt >/dev/null], [comex_cv_mpi_naked=$naked_compiler; break], [echo "inside.pl $wrapped $naked_compiler failed, skipping" >&AS_MESSAGE_LOG_FD])], [echo "$naked_compiler $version failed, skipping" >&AS_MESSAGE_LOG_FD])]) done AS_IF([test "x$comex_cv_mpi_naked" != x], [break]) done ]) rm -f mpi.txt mpi.err naked.txt naked.err ]) AS_IF([test "x$comex_cv_mpi_naked" = x], [AC_MSG_ERROR([Could not determine the ]_AC_LANG[ compiler wrapped by MPI])], [AS_IF([test "x$comex_orig_comp" != x && test "x$comex_orig_comp" != "x$comex_cv_mpi_naked"], [AC_MSG_WARN([unwrapped $wrapped ($comex_cv_mpi_naked) does not match user-specified $comex_orig_comp])])]) AS_VAR_POPDEF([comex_save_comp]) AS_VAR_POPDEF([comex_cv_mpi_naked]) rm -f inside.pl ])dnl # COMEX_MPI_UNWRAP_PUSH() # -------------------- # Set CC/CXX/F77/FC to their unwrapped MPI counterparts. # Save their old values for restoring later. AC_DEFUN([COMEX_MPI_UNWRAP_PUSH], [ comex_mpi_unwrap_push_save_CC="$CC" comex_mpi_unwrap_push_save_CXX="$CXX" comex_mpi_unwrap_push_save_F77="$F77" comex_mpi_unwrap_push_save_FC="$FC" AS_IF([test "x$comex_cv_mpic_naked" != x], [ CC="$comex_cv_mpic_naked"]) AS_IF([test "x$comex_cv_mpicxx_naked" != x], [CXX="$comex_cv_mpicxx_naked"]) AS_IF([test "x$comex_cv_mpif77_naked" != x], [F77="$comex_cv_mpif77_naked"]) AS_IF([test "x$comex_cv_mpifc_naked" != x], [ FC="$comex_cv_mpifc_naked"]) ])dnl # COMEX_MPI_UNWRAP_POP() # ------------------- # Restore CC/CXX/F77/FC to their MPI counterparts. AC_DEFUN([COMEX_MPI_UNWRAP_POP], [ CC="$comex_mpi_unwrap_push_save_CC" CXX="$comex_mpi_unwrap_push_save_CXX" F77="$comex_mpi_unwrap_push_save_F77" FC="$comex_mpi_unwrap_push_save_FC" ])dnl ga-5.9.2/comex/m4/comex_mpicc.m4000066400000000000000000000036141500715745200163130ustar00rootroot00000000000000# COMEX_PROG_MPICC # ---------------- # If desired, replace CC with MPICC while searching for a C compiler. # # Known C compilers # cc generic compiler name # cl # gcc GNU # icc Intel # xlc Intel # xlc_r Intel, thread safe # pgcc Portland Group # pathcc PathScale # fcc Fujitsu # opencc AMD's x86 open64 # suncc Sun's Studio # craycc Cray # # Known MPI C compilers: # mpicc # mpixlc_r # mpixlc # hcc # mpxlc_r # mpxlc # mpifcc Fujitsu # mpgcc # mpcc # cmpicc # cc # AC_DEFUN([COMEX_PROG_MPICC], [AC_ARG_VAR([MPICC], [MPI C compiler]) # In the case of using MPI wrappers, set CC=MPICC since CC will override # absolutely everything in our list of compilers. # Save CC, just in case. AS_IF([test x$with_mpi_wrappers = xyes], [AS_IF([test "x$CC" != "x$MPICC"], [comex_orig_CC="$CC"]) AS_CASE([x$CC:x$MPICC], [x:x], [], [x:x*], [CC="$MPICC"], [x*:x], [AC_MSG_WARN([MPI compilers desired but CC is set while MPICC is unset.]) AC_MSG_WARN([CC will be ignored during compiler selection, but will be]) AC_MSG_WARN([tested first during MPI compiler unwrapping. Perhaps you]) AC_MSG_WARN([meant to set MPICC instead of or in addition to CC?]) CC=], [x*:x*], [AS_IF([test "x$CC" != "x$MPICC"], [AC_MSG_WARN([MPI compilers desired, MPICC and CC are set, and MPICC!=CC.]) AC_MSG_WARN([Choosing MPICC as main compiler.]) AC_MSG_WARN([CC will be assumed as the unwrapped MPI compiler.])]) comex_cv_mpic_naked="$CC" CC="$MPICC"], [AC_MSG_ERROR([CC/MPICC case failure])])]) comex_cc="bgxlc_r bgxlc xlc_r xlc pgcc pathcc icc sxcc fcc opencc suncc craycc gcc cc ecc cl ccc" comex_mpicc="mpicc mpixlc_r mpixlc hcc mpxlc_r mpxlc sxmpicc mpifcc mpgcc mpcc cmpicc cc" AS_IF([test x$with_mpi_wrappers = xyes], [CC_TO_TEST="$comex_mpicc_pref $comex_mpicc"], [CC_TO_TEST="$comex_cc_pref $comex_cc"]) AC_PROG_CC([$CC_TO_TEST]) ])dnl ga-5.9.2/comex/m4/comex_mpicc_test.m4000066400000000000000000000110001500715745200173360ustar00rootroot00000000000000# COMEX_MPICC_TEST_PROGRAM # --------------------- # Create an MPI test program in C. AC_DEFUN([COMEX_MPICC_TEST_PROGRAM], [ AC_LANG_PUSH([C]) AC_LANG_CONFTEST([AC_LANG_PROGRAM([[#include ]], [[int myargc; char **myargv; MPI_Init(&myargc, &myargv); MPI_Finalize();]])]) AC_LANG_POP([C]) ])dnl # COMEX_MPICC_TEST_COMPILE # --------------------- # Attempt to compile a simple MPI program in C. AC_DEFUN([COMEX_MPICC_TEST_COMPILE], [ AC_LANG_PUSH([C]) COMEX_MPICC_TEST_PROGRAM() AC_CACHE_CHECK([whether a simple C MPI program compiles], [comex_cv_c_mpi_test_compile], [comex_save_CPPFLAGS="$CPPFLAGS"; CPPFLAGS="$CPPFLAGS $MPI_CPPFLAGS" AC_COMPILE_IFELSE([], [comex_cv_c_mpi_test_compile=yes], [comex_cv_c_mpi_test_compile=no]) CPPFLAGS="$comex_save_CPPFLAGS"]) rm -f conftest.$ac_ext AC_LANG_POP([C]) AS_IF([test "x$comex_cv_c_mpi_test_compile" = xno], [AC_MSG_FAILURE([could not compile simple C MPI program])]) ])dnl # COMEX_MPICC_TEST_LINK # ------------------ # Attempt to link a simple MPI program in C. AC_DEFUN([COMEX_MPICC_TEST_LINK], [ AC_LANG_PUSH([C]) COMEX_MPICC_TEST_PROGRAM() comex_cv_c_mpi_test_link=no AS_IF([test "x$comex_cv_c_mpi_test_link" = xno], [AC_MSG_CHECKING([whether a C MPI program links natively]) AC_LINK_IFELSE([], [comex_cv_c_mpi_test_link=yes MPI_LIBS= MPI_LDFLAGS= MPI_CPPFLAGS=], [comex_cv_c_mpi_test_link=no]) AC_MSG_RESULT([$comex_cv_c_mpi_test_link])]) # That didn't work, so now let's try adding our MPI_* flags. # The CPPFLAGS and LDFLAGS are added up top here, but LIBS will change. comex_save_LIBS="$LIBS" comex_save_CPPFLAGS="$CPPFLAGS"; CPPFLAGS="$CPPFLAGS $MPI_CPPFLAGS" comex_save_LDFLAGS="$LDFLAGS"; LDFLAGS="$LDFLAGS $MPI_LDFLAGS" AS_IF([test "x$comex_cv_c_mpi_test_link" = xno], [AC_MSG_CHECKING([whether a C MPI program links with additional env]) LIBS="$LIBS $MPI_LIBS" AC_LINK_IFELSE([], [comex_cv_c_mpi_test_link=yes], [comex_cv_c_mpi_test_link=no]) LIBS="$comex_save_LIBS" AC_MSG_RESULT([$comex_cv_c_mpi_test_link])]) # That didn't work, so now let's try with specific libs. AS_IF([test "x$comex_cv_c_mpi_test_link" = xno], [AC_MSG_CHECKING([for mvapich libraries]) for lib in "-lmpich -lpthread" "-lmpich" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [comex_cv_c_mpi_test_link="$lib"; break], [comex_cv_c_mpi_test_link=no]) LIBS="$comex_save_LIBS" done LIBS="$comex_save_LIBS" AC_MSG_RESULT([$comex_cv_c_mpi_test_link])]) AS_IF([test "x$comex_cv_c_mpi_test_link" = xno], [AC_MSG_CHECKING([for mpich libraries]) for lib in "-lmpich -lpthread" "-lmpich" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [comex_cv_c_mpi_test_link="$lib"; break], [comex_cv_c_mpi_test_link=no]) LIBS="$comex_save_LIBS" done LIBS="$comex_save_LIBS" AC_MSG_RESULT([$comex_cv_c_mpi_test_link])]) AS_IF([test "x$comex_cv_c_mpi_test_link" = xno], [AC_MSG_CHECKING([for hpmpi libraries]) for lib in "-lhpmpio -lhpmpi" "-lhpmpi" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [comex_cv_c_mpi_test_link="$lib"; break], [comex_cv_c_mpi_test_link=no]) LIBS="$comex_save_LIBS" done LIBS="$comex_save_LIBS" AC_MSG_RESULT([$comex_cv_c_mpi_test_link])]) AS_IF([test "x$comex_cv_c_mpi_test_link" = xno], [AC_MSG_CHECKING([for intelmpi libraries]) for lib in "-lmpi -lmpiif -lmpigi -lrt -lpthread" "-lmpi -lmpiif -lmpigi" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [comex_cv_c_mpi_test_link="$lib"; break], [comex_cv_c_mpi_test_link=no]) LIBS="$comex_save_LIBS" done LIBS="$comex_save_LIBS" AC_MSG_RESULT([$comex_cv_c_mpi_test_link])]) AS_IF([test "x$comex_cv_c_mpi_test_link" = xno], [AC_MSG_CHECKING([for openmpi libraries]) for lib in "-lmpi -lpthread" "-lmpi" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [comex_cv_c_mpi_test_link="$lib"; break], [comex_cv_c_mpi_test_link=no]) LIBS="$comex_save_LIBS" done LIBS="$comex_save_LIBS" AC_MSG_RESULT([$comex_cv_c_mpi_test_link])]) rm -f conftest.$ac_ext LIBS="$comex_save_LIBS" LDFLAGS="$comex_save_LDFLAGS" CPPFLAGS="$comex_save_CPPFLAGS" AC_LANG_POP([C]) AS_CASE([$comex_cv_c_mpi_test_link], [yes], [], [no], [AC_MSG_FAILURE([could not link a C MPI program])], [*], [MPI_LIBS="$comex_cv_c_mpi_test_link"], []) ])dnl ga-5.9.2/comex/m4/comex_network_setup.m4000066400000000000000000000314441500715745200201330ustar00rootroot00000000000000# _COMEX_NETWORK_WITH(KEY, DESCRIPTION) # -------------------------------------------------- # A helper macro for generating all of the AC_ARG_WITHs. # Also may establish value of comex_network. # Counts how many comex networks were specified by user. AC_DEFUN([_COMEX_NETWORK_WITH], [ AC_ARG_WITH([$1], [AS_HELP_STRING([--with-$1[[=ARG]]], [select comex network as $2])]) AS_VAR_PUSHDEF([KEY], m4_toupper(m4_translit([$1], [-.], [__]))) AS_VAR_PUSHDEF([with_key], m4_translit([with_$1], [-.], [__])) dnl Can't have AM_CONDITIONAL here in case configure must find comex network dnl without user intervention. dnl AM_CONDITIONAL([COMEX_NETWORK_]KEY, [test "x$with_key" != x]) AS_IF([test "x$with_key" != x], [COMEX_ARG_PARSE([with_key], [COMEX_NETWORK_LIBS], [COMEX_NETWORK_LDFLAGS], [COMEX_NETWORK_CPPFLAGS])]) AS_IF([test "x$with_key" != xno && test "x$with_key" != x], [comex_network=KEY AS_VAR_ARITH([comex_network_count], [$comex_network_count + 1])]) AS_VAR_POPDEF([KEY]) AS_VAR_POPDEF([with_key]) ])dnl # _COMEX_NETWORK_WARN(KEY) # --------------------------- # Helper macro for idicating value of comex network arguments. AC_DEFUN([_COMEX_NETWORK_WARN], [ AS_VAR_PUSHDEF([with_key], m4_translit([with_$1], [-.], [__])) AS_IF([test "x$with_key" != x && test "x$with_key" != xno], [AC_MSG_WARN([--with-$1=$with_key])]) AS_VAR_POPDEF([with_key]) ])dnl # _COMEX_NETWORK_AC_DEFINE(KEY) #-------------------------------------- # Helper macro for generating all AC_DEFINEs. AC_DEFUN([_COMEX_NETWORK_AC_DEFINE], [ AS_VAR_PUSHDEF([KEY], m4_toupper(m4_translit([$1], [-.], [__]))) AS_VAR_PUSHDEF([with_key], m4_translit([with_$1], [-.], [__])) AS_IF([test "x$with_key" != x && test "x$with_key" != xno], [AC_DEFINE([COMEX_NETWORK_]KEY, [1], [Define to 1 if the network is ]KEY)], [AC_DEFINE([COMEX_NETWORK_]KEY, [0], [Define to 1 if the network is ]KEY)]) AS_VAR_POPDEF([KEY]) AS_VAR_POPDEF([with_key]) ])dnl # _COMEX_NETWORK_AM_CONDITIONAL(KEY) #-------------------------------------- # Helper macro for generating all AM_CONDITIONALs. AC_DEFUN([_COMEX_NETWORK_AM_CONDITIONAL], [ AS_VAR_PUSHDEF([KEY], m4_toupper(m4_translit([$1], [-.], [__]))) AS_VAR_PUSHDEF([with_key], m4_translit([with_$1], [-.], [__])) AM_CONDITIONAL([COMEX_NETWORK_]KEY, [test "x$with_key" != x && test "x$with_key" != xno]) AS_VAR_POPDEF([KEY]) AS_VAR_POPDEF([with_key]) ])dnl # _COMEX_NETWORK_MPI_TS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------- AC_DEFUN([_COMEX_NETWORK_MPI_TS], [ AC_MSG_NOTICE([searching for MPI_TS...]) happy=yes CPPFLAGS="$CPPFLAGS $MPI_CPPFLAGS" LDFLAGS="$LDFLAGS $MPI_LDFLAGS" LIBS="$LIBS $MPI_LIBS" AS_IF([test "x$happy" = xyes], [AC_CHECK_HEADER([mpi.h], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([MPI_Init], [], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [comex_network=MPI_TS; with_mpi_ts=yes; $1], [$2]) ])dnl # _COMEX_NETWORK_MPI_MT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------- AC_DEFUN([_COMEX_NETWORK_MPI_MT], [ AC_MSG_NOTICE([searching for MPI_MT...]) happy=yes CPPFLAGS="$CPPFLAGS $MPI_CPPFLAGS" LDFLAGS="$LDFLAGS $MPI_LDFLAGS" LIBS="$LIBS $MPI_LIBS" AS_IF([test "x$happy" = xyes], [AC_CHECK_HEADER([mpi.h], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([MPI_Init_thread], [], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [comex_network=MPI_MT; with_mpi_mt=yes; $1], [$2]) ])dnl # _COMEX_NETWORK_MPI_PT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------- AC_DEFUN([_COMEX_NETWORK_MPI_PT], [ AC_MSG_NOTICE([searching for MPI_PT...]) happy=yes CPPFLAGS="$CPPFLAGS $MPI_CPPFLAGS" LDFLAGS="$LDFLAGS $MPI_LDFLAGS" LIBS="$LIBS $MPI_LIBS" AS_IF([test "x$happy" = xyes], [AC_CHECK_HEADER([mpi.h], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([MPI_Init_thread], [], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [comex_network=MPI_PT; with_mpi_pt=yes; $1], [$2]) ])dnl # _COMEX_NETWORK_MPI_PR([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------- AC_DEFUN([_COMEX_NETWORK_MPI_PR], [ AC_MSG_NOTICE([searching for MPI_PR...]) happy=yes CPPFLAGS="$CPPFLAGS $MPI_CPPFLAGS" LDFLAGS="$LDFLAGS $MPI_LDFLAGS" LIBS="$LIBS $MPI_LIBS" AS_IF([test "x$happy" = xyes], [AC_CHECK_HEADER([mpi.h], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([MPI_Init], [], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [comex_network=MPI_PR; with_mpi_pr=yes; $1], [$2]) ])dnl # _COMEX_NETWORK_MPI3([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------- AC_DEFUN([_COMEX_NETWORK_MPI3], [ AC_MSG_NOTICE([searching for MPI3...]) happy=yes CPPFLAGS="$CPPFLAGS $MPI_CPPFLAGS" LDFLAGS="$LDFLAGS $MPI_LDFLAGS" LIBS="$LIBS $MPI_LIBS" AS_IF([test "x$happy" = xyes], [AC_CHECK_HEADER([mpi.h], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([MPI_Init], [], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [comex_network=MPI3; with_mpi3=yes; $1], [$2]) ])dnl # _COMEX_NETWORK_OFA([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ------------------------------------------------------------------ AC_DEFUN([_COMEX_NETWORK_OFA], [ AC_MSG_NOTICE([searching for OFA...]) happy=yes AS_IF([test "x$happy" = xyes], [AC_CHECK_HEADER([infiniband/verbs.h], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([ibv_open_device], [ibverbs], [], [happy=no]) AS_CASE([$ac_cv_search_ibv_open_device], ["none required"], [], [no], [], [COMEX_NETWORK_LIBS="$COMEX_NETWORK_LIBS $ac_cv_search_ibv_open_device"])]) AS_IF([test "x$happy" = xyes], [comex_network=OFA; with_ofa=yes; $1], [$2]) ])dnl # _COMEX_NETWORK_PORTALS4([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ------------------------------------------------------------------- AC_DEFUN([_COMEX_NETWORK_PORTALS4], [ AC_MSG_NOTICE([searching for PORTALS4...]) happy=yes AS_IF([test "x$happy" = xyes], [AC_CHECK_HEADER([portals4.h], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([PtlInit], [portals4], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([PtlFini], [portals4], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [comex_network=PORTALS4; with_portals4=yes; $1], [$2]) ])dnl # _COMEX_NETWORK_OFI([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ------------------------------------------------------------------- AC_DEFUN([_COMEX_NETWORK_OFI], [ AC_MSG_NOTICE([searching for OFI...]) happy=yes AS_IF([test "x$happy" = xyes], [AC_CHECK_HEADERS([rdma/fabric.h rdma/fi_domain.h], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([fi_getinfo], [fabric], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([fi_freeinfo], [fabric], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([fi_dupinfo], [fabric], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([fi_fabric], [fabric], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([fi_strerror], [fabric], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([fi_tostr], [fabric], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AS_CASE([$ac_cv_search_fi_getinfo], ["none required"], [], [no], [], [# add missing lib to COMEX_NETWORK_LIBS if not there AS_CASE([$COMEX_NETWORK_LIBS], [*$ac_cv_search_fi_getinfo*], [], [COMEX_NETWORK_LIBS="$COMEX_NETWORK_LIBS $ac_cv_search_fi_getinfo"])])]) LT_LIB_DLLOAD AS_IF([test "x$happy" = xyes], [comex_network=OFI; with_ofi=yes; $1], [$2]) ])dnl # COMEX_NETWORK_SETUP # ------------------- # This macro allows user to choose the comex network but also allows the # network to be tested for automatically. AC_DEFUN([COMEX_NETWORK_SETUP], [ # Clear the variables we will be using, just in case. comex_network= COMEX_NETWORK_LIBS= COMEX_NETWORK_LDFLAGS= COMEX_NETWORK_CPPFLAGS= AC_ARG_ENABLE([autodetect], [AS_HELP_STRING([--enable-autodetect], [attempt to locate COMEX_NETWORK besides MPI two-sided])]) # First, all of the "--with" stuff is taken care of. comex_network_count=0 _COMEX_NETWORK_WITH([mpi-ts], [MPI-1 two-sided]) _COMEX_NETWORK_WITH([mpi-mt], [MPI-2 multi-threading]) _COMEX_NETWORK_WITH([mpi-pt], [MPI-2 multi-threading with progress thread]) _COMEX_NETWORK_WITH([mpi-pr], [MPI-1 two-sided with progress rank]) _COMEX_NETWORK_WITH([mpi3], [MPI-3 one-sided]) _COMEX_NETWORK_WITH([ofa], [Infiniband OpenIB]) _COMEX_NETWORK_WITH([portals4], [Portals4]) _COMEX_NETWORK_WITH([ofi], [OFI]) # Temporarily add COMEX_NETWORK_CPPFLAGS to CPPFLAGS. comex_save_CPPFLAGS="$CPPFLAGS"; CPPFLAGS="$CPPFLAGS $COMEX_NETWORK_CPPFLAGS" # Temporarily add COMEX_NETWORK_LDFLAGS to LDFLAGS. comex_save_LDFLAGS="$LDFLAGS"; LDFLAGS="$LDFLAGS $COMEX_NETWORK_LDFLAGS" # Temporarily add COMEX_NETWORK_LIBS to LIBS. comex_save_LIBS="$LIBS"; LIBS="$COMEX_NETWORK_LIBS $LIBS" AS_IF([test "x$enable_autodetect" = xyes], [AC_MSG_NOTICE([searching for COMEX_NETWORK...]) AS_IF([test "x$comex_network" = x && test "x$with_ofa" != xno], [_COMEX_NETWORK_OFA()]) AS_IF([test "x$comex_network" = x && test "x$with_portals4" != xno], [_COMEX_NETWORK_PORTALS4()]) AS_IF([test "x$comex_network" = x && test "x$with_ofi" != xno], [_COMEX_NETWORK_OFI()]) AS_IF([test "x$comex_network" = x], [AC_MSG_WARN([!!!]) AC_MSG_WARN([No COMEX_NETWORK detected, defaulting to MPI_TS]) AC_MSG_WARN([!!!]) comex_network=MPI_TS; with_mpi_ts=yes])], [# Not autodetecting # Check whether multiple comex networks were selected by user. AS_CASE([$comex_network_count], [0], [AC_MSG_WARN([No COMEX_NETWORK specified, defaulting to MPI_TS]) comex_network=MPI_TS; with_mpi_ts=yes], [1], [AS_IF([test "x$comex_network" = xMPI_TS], [_COMEX_NETWORK_MPI_TS([], [AC_MSG_ERROR([test for COMEX_NETWORK=MPI_TS failed])])]) AS_IF([test "x$comex_network" = xMPI_MT], [_COMEX_NETWORK_MPI_MT([], [AC_MSG_ERROR([test for COMEX_NETWORK=MPI_MT failed])])]) AS_IF([test "x$comex_network" = xMPI_PT], [_COMEX_NETWORK_MPI_PT([], [AC_MSG_ERROR([test for COMEX_NETWORK=MPI_PT failed])])]) AS_IF([test "x$comex_network" = xMPI_PR], [_COMEX_NETWORK_MPI_PR([], [AC_MSG_ERROR([test for COMEX_NETWORK=MPI_PR failed])])]) AS_IF([test "x$comex_network" = xMPI3], [_COMEX_NETWORK_MPI3([], [AC_MSG_ERROR([test for COMEX_NETWORK=MPI3 failed])])]) AS_IF([test "x$comex_network" = xOFA], [_COMEX_NETWORK_OFA([], [AC_MSG_ERROR([test for COMEX_NETWORK=OFA failed])])]) AS_IF([test "x$comex_network" = xPORTALS4], [_COMEX_NETWORK_PORTALS4([], [AC_MSG_ERROR([test for COMEX_NETWORK=PORTALS4 failed])])]) AS_IF([test "x$comex_network" = xOFI], [_COMEX_NETWORK_OFI([], [AC_MSG_ERROR([test for COMEX_NETWORK=OFI failed])])]) ], [AC_MSG_WARN([too many comex networks specified: $comex_network_count]) AC_MSG_WARN([the following were specified:]) _COMEX_NETWORK_WARN([mpi-ts]) _COMEX_NETWORK_WARN([mpi-mt]) _COMEX_NETWORK_WARN([mpi-pt]) _COMEX_NETWORK_WARN([mpi-pr]) _COMEX_NETWORK_WARN([mpi3]) _COMEX_NETWORK_WARN([ofa]) _COMEX_NETWORK_WARN([portals4]) _COMEX_NETWORK_WARN([ofi]) AC_MSG_ERROR([please select only one comex network])])]) # Remove COMEX_NETWORK_CPPFLAGS from CPPFLAGS. CPPFLAGS="$comex_save_CPPFLAGS" # Remove COMEX_NETWORK_LDFLAGS from LDFLAGS. LDFLAGS="$comex_save_LDFLAGS" # Remove COMEX_NETWORK_LIBS from LIBS. LIBS="$comex_save_LIBS" _COMEX_NETWORK_AM_CONDITIONAL([mpi-ts]) _COMEX_NETWORK_AM_CONDITIONAL([mpi-mt]) _COMEX_NETWORK_AM_CONDITIONAL([mpi-pt]) _COMEX_NETWORK_AM_CONDITIONAL([mpi-pr]) _COMEX_NETWORK_AM_CONDITIONAL([mpi3]) _COMEX_NETWORK_AM_CONDITIONAL([ofa]) _COMEX_NETWORK_AM_CONDITIONAL([portals4]) _COMEX_NETWORK_AM_CONDITIONAL([ofi]) _COMEX_NETWORK_AC_DEFINE([mpi-ts]) _COMEX_NETWORK_AC_DEFINE([mpi-mt]) _COMEX_NETWORK_AC_DEFINE([mpi-pt]) _COMEX_NETWORK_AC_DEFINE([mpi-pr]) _COMEX_NETWORK_AC_DEFINE([mpi3]) _COMEX_NETWORK_AC_DEFINE([ofa]) _COMEX_NETWORK_AC_DEFINE([portals4]) _COMEX_NETWORK_AC_DEFINE([ofi]) AC_SUBST([COMEX_NETWORK_LDFLAGS]) AC_SUBST([COMEX_NETWORK_LIBS]) AC_SUBST([COMEX_NETWORK_CPPFLAGS]) ])dnl ga-5.9.2/comex/m4/comex_sys_weak_alias.m4000066400000000000000000000020051500715745200202070ustar00rootroot00000000000000# COMEX_DISABLE_SYS_WEAK_ALIAS # ------------------------- # Whether to disable the test for weak aliases AC_DEFUN([COMEX_DISABLE_SYS_WEAK_ALIAS], [ AC_ARG_ENABLE([weak], [AS_HELP_STRING([--disable-weak], [don't use weak symbols for profiling])], [], [enable_weak=yes]) AS_IF([test "x$comex_cv_target_base" = xCYGWIN], [enable_weak=no]) ])dnl # COMEX_SYS_WEAK_ALIAS # ----------------- # Whether pragma weak is supported. AC_DEFUN([COMEX_SYS_WEAK_ALIAS], [ AS_IF([test "x$enable_weak" = xyes], [ax_sys_weak_alias=no _AX_SYS_WEAK_ALIAS_PRAGMA], [ax_cv_sys_weak_alias_pragma=no AC_DEFINE([HAVE_SYS_WEAK_ALIAS_PRAGMA], [0], [Define this if weak aliases may be created with @%:@pragma weak])]) AM_CONDITIONAL([HAVE_SYS_WEAK_ALIAS_PRAGMA], [test "x$ax_cv_sys_weak_alias_pragma" = xyes]) # enable shared libs automatically if profiling using weak symbols AS_IF([test "x$ax_cv_sys_weak_alias_pragma" = xyes], [AS_IF([test "x$enable_profiling" = xyes], [enable_shared=yes])]) ])dnl ga-5.9.2/comex/m4/comex_with_help.m4000066400000000000000000000006611500715745200172020ustar00rootroot00000000000000# COMEX_WITH_HELP # ------------ # Using undocumented features, add some text to our --with-PACKAGE help to # avoid repetition. AC_DEFUN([COMEX_WITH_HELP], [AC_ARG_WITH([PACKAGE], [AS_HELP_STRING([--with-PACKAGE[[=ARG]]], [for most of the external software packages, ARG can be one or more whitespace-separated directories, linker or preprocessor directives; for example, --with-PACKAGE="/path/to/PACKAGE -lmylib -I/mydir"])])]) ga-5.9.2/comex/m4/comex_with_mpi.m4000066400000000000000000000020611500715745200170330ustar00rootroot00000000000000# COMEX_WITH_MPI # -------------- # Establishes all things related to the Message Passing Interface. # This includes the compilers to use (either standard or MPI wrappers) # or the proper linker flags (-L), libs (-l) or preprocessor directives (-I). # Yes, it's a beefy AC macro, but because when MPI is desired it replaces the # usual compiler the order here is necessary and it is all interdependent. AC_DEFUN([COMEX_WITH_MPI], [ # MPI_* vars might exist in environment, but they are really internal. # Reset them. MPI_LIBS= MPI_LDFLAGS= MPI_CPPFLAGS= AC_ARG_WITH([mpi], [AS_HELP_STRING([--with-mpi[[=ARG]]], [path to MPI; leave ARG blank to use MPI compiler wrappers in PATH])], [], [with_mpi=yes]) AS_IF([test "x$with_mpi" = xyes], [with_mpi_wrappers=yes], [with_mpi_wrappers=no]) dnl postpone parsing with_mpi until we know sizeof(void*) dnl AS_IF([test x$with_mpi_wrappers = xno], dnl [COMEX_ARG_PARSE([with_mpi], [MPI_LIBS], [MPI_LDFLAGS], [MPI_CPPFLAGS])]) AC_SUBST([MPI_LIBS]) AC_SUBST([MPI_LDFLAGS]) AC_SUBST([MPI_CPPFLAGS]) ])dnl ga-5.9.2/comex/src-armci/000077500000000000000000000000001500715745200151175ustar00rootroot00000000000000ga-5.9.2/comex/src-armci/Makefile.inc000066400000000000000000000005741500715745200173350ustar00rootroot00000000000000lib_LTLIBRARIES += libarmci.la libarmci_la_SOURCES = libarmci_la_SOURCES += src-armci/armci.c libarmci_la_SOURCES += src-armci/groups.c libarmci_la_SOURCES += src-armci/message.c libarmci_la_SOURCES += src-armci/iterator.c libarmci_la_SOURCES += src-armci/iterator.h libarmci_la_LIBADD = libcomexi.la include_HEADERS += src-armci/armci.h include_HEADERS += src-armci/message.h ga-5.9.2/comex/src-armci/armci.c000066400000000000000000000537111500715745200163650ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include "armci.h" #include "parmci.h" #include "comex.h" extern int ARMCI_Default_Proc_Group; MPI_Comm ARMCI_COMM_WORLD; int _number_of_procs_per_node = 1; int _my_node_id; ARMCI_Group ARMCI_Node_group; /** * Initialize parameters that can be used by the armci_domain_xxx function */ void armci_init_domains(MPI_Comm comm) { int i, status; char name[MPI_MAX_PROCESSOR_NAME]; char *namebuf, *buf_ptr, *prev_ptr; int namelen, rank, size, nprocs; int *nodeid, *nodesize; int ncnt; status = MPI_Comm_rank(comm, &rank); assert(MPI_SUCCESS == status); status = MPI_Comm_size(comm, &size); assert(MPI_SUCCESS == status); /* determine number of processors per node. First find node name */ namebuf = (char*)malloc(MPI_MAX_PROCESSOR_NAME*size*sizeof(char)); nodeid = (int*)malloc(size*sizeof(int)); nodesize = (int*)malloc(size*sizeof(int)); MPI_Get_processor_name(name, &namelen); status = MPI_Allgather(name,MPI_MAX_PROCESSOR_NAME,MPI_CHAR,namebuf, MPI_MAX_PROCESSOR_NAME,MPI_CHAR,comm); assert(MPI_SUCCESS == status); /* Bin all processors with the same node name */ ncnt = 0; nodeid[0] = ncnt; nodesize[0] = 1; prev_ptr = namebuf; buf_ptr = namebuf+MPI_MAX_PROCESSOR_NAME; for (i=1; i 1) { int *nodelist = (int*)malloc(_number_of_procs_per_node*sizeof(int)); for (i=0; i<_number_of_procs_per_node; i++) nodelist[i] = _my_node_id*_number_of_procs_per_node+i; comex_group_create(_number_of_procs_per_node, nodelist, COMEX_GROUP_WORLD, &ARMCI_Node_group); free(nodelist); } } /** * This function checks to see if the data copy is contiguous for both the src * and destination buffers. If it is, then a contiguous operation can be used * instead of a strided operation. This function is intended for arrays of * dimension greater than 1 (contiguous operations can always be used for 1 * dimensional arrays). * * The current implementation tries to identify all contiguous cases by using * all information from the stride and count arrays. The old implementation did * not identify all cases of contiguous data transfers. * * src_stride: physical dimensions of source buffer * dst_stride: physical dimensions of destination buffer * count: number of elements being moved in each dimension * n_stride: number of strides (array dimension minus one) */ int armci_check_contiguous(int *src_stride, int *dst_stride, int *count, int n_stride) { #if 1 /* This is code from the merge between CMX and the current develop branch * (2018/7/5) */ int i; int ret = 1; int stridelen = 1; int gap = 0; int src_ld[7], dst_ld[7]; /** * Calculate physical dimensions of buffers from stride arrays */ src_ld[0] = src_stride[0]; dst_ld[0] = dst_stride[0]; for (i=1; i 0) { if (count[n_stride] != 1) ret = 0; } return ret; #else int i; int ret = 1; int stridelen = 1; /* NOTE: The count array contains the length of the final dimension and could * be used to evaluate some corner cases that are not picked up by this * algorithm */ for (i=0; i 0) { rc = comex_fence_all(*group); } else { rc = comex_fence_all(COMEX_GROUP_WORLD); } assert(COMEX_SUCCESS == rc); } void PARMCI_Barrier() { int rc; rc = comex_barrier(ARMCI_Default_Proc_Group); assert(COMEX_SUCCESS == rc); } int PARMCI_Create_mutexes(int num) { return comex_create_mutexes(num); } int PARMCI_Destroy_mutexes() { return comex_destroy_mutexes(); } /* fence is always on the world group */ void PARMCI_Fence(int proc) { comex_fence_proc(proc, COMEX_GROUP_WORLD); } void PARMCI_Finalize() { comex_finalize(); } int PARMCI_Free(void *ptr) { return comex_free(ptr, ARMCI_Default_Proc_Group); } int PARMCI_Free_memdev(void *ptr) { return comex_free_dev(ptr, ARMCI_Default_Proc_Group); } int ARMCI_Free_group(void *ptr, ARMCI_Group *group) { return comex_free(ptr, *group); } int PARMCI_Free_local(void *ptr) { return comex_free_local(ptr); } int PARMCI_Get(void *src, void *dst, int bytes, int proc) { return comex_get(src, dst, bytes, proc, COMEX_GROUP_WORLD); } int PARMCI_GetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { int iret; /* check if data is contiguous */ if (armci_check_contiguous(src_stride_arr, dst_stride_arr, count, stride_levels)) { int i; int lcount = 1; for (i=0; i<=stride_levels; i++) lcount *= count[i]; iret = comex_get(src_ptr, dst_ptr, lcount, proc, COMEX_GROUP_WORLD); } else { iret = comex_gets(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, COMEX_GROUP_WORLD); } return iret; } int PARMCI_GetV(armci_giov_t *darr, int len, int proc) { int rc; comex_giov_t *adarr = malloc(sizeof(comex_giov_t) * len); convert_giov(darr, adarr, len); rc = comex_getv(adarr, len, proc, COMEX_GROUP_WORLD); free(adarr); return rc; } double PARMCI_GetValueDouble(void *src, int proc) { int rc; double val; rc = comex_get(src, &val, sizeof(double), proc, COMEX_GROUP_WORLD); assert(COMEX_SUCCESS == rc); return val; } float PARMCI_GetValueFloat(void *src, int proc) { int rc; float val; rc = comex_get(src, &val, sizeof(float), proc, COMEX_GROUP_WORLD); assert(COMEX_SUCCESS == rc); return val; } int PARMCI_GetValueInt(void *src, int proc) { int rc; int val; rc = comex_get(src, &val, sizeof(int), proc, COMEX_GROUP_WORLD); assert(COMEX_SUCCESS == rc); return val; } long PARMCI_GetValueLong(void *src, int proc) { int rc; long val; rc = comex_get(src, &val, sizeof(long), proc, COMEX_GROUP_WORLD); assert(COMEX_SUCCESS == rc); return val; } int PARMCI_Init() { int rc = comex_init(); assert(COMEX_SUCCESS == rc); rc = comex_group_comm(COMEX_GROUP_WORLD, &ARMCI_COMM_WORLD); assert(COMEX_SUCCESS == rc); ARMCI_Default_Proc_Group = 0; armci_init_domains(ARMCI_COMM_WORLD); return rc; } int PARMCI_Init_args(int *argc, char ***argv) { int rc = comex_init_args(argc, argv); assert(COMEX_SUCCESS == rc); rc = comex_group_comm(COMEX_GROUP_WORLD, &ARMCI_COMM_WORLD); assert(COMEX_SUCCESS == rc); armci_init_domains(ARMCI_COMM_WORLD); ARMCI_Default_Proc_Group = 0; return rc; } int PARMCI_Init_mpi_comm(MPI_Comm comm) { int ret = comex_init_comm(comm); if (ret == COMEX_SUCCESS) { int rc = comex_group_comm(COMEX_GROUP_WORLD, &ARMCI_COMM_WORLD); assert(COMEX_SUCCESS == rc); ARMCI_Default_Proc_Group = 0; armci_init_domains(ARMCI_COMM_WORLD); ret = 1; } else { ret = 0; } return ret; } int PARMCI_Initialized() { return comex_initialized(); } void PARMCI_Lock(int mutex, int proc) { comex_lock(mutex, proc); } int PARMCI_Malloc(void **ptr_arr, armci_size_t bytes) { return comex_malloc(ptr_arr, bytes, ARMCI_Default_Proc_Group); } int PARMCI_Malloc_memdev(void **ptr_arr, armci_size_t bytes, const char *device) { return comex_malloc_mem_dev(ptr_arr, bytes, ARMCI_Default_Proc_Group,device); } int ARMCI_Malloc_group(void **ptr_arr, armci_size_t bytes, ARMCI_Group *group) { return comex_malloc(ptr_arr, bytes, *group); } int ARMCI_Malloc_group_memdev(void **ptr_arr, armci_size_t bytes, ARMCI_Group *group, const char *device) { return comex_malloc_mem_dev(ptr_arr, bytes, *group,device); } void* PARMCI_Malloc_local(armci_size_t bytes) { return comex_malloc_local(bytes); } void* PARMCI_Memat(armci_meminfo_t *meminfo, long offset) { void *ptr=NULL; int rank; comex_group_rank(COMEX_GROUP_WORLD, &rank); if(meminfo==NULL) comex_error("PARMCI_Memat: Invalid arg #1 (NULL ptr)",0); if(meminfo->cpid==rank) { ptr = meminfo->addr; return ptr; } ptr = meminfo->addr; return ptr; } void PARMCI_Memget(size_t bytes, armci_meminfo_t *meminfo, int memflg) { void *myptr=NULL; void *armci_ptr=NULL; /* legal ARCMI ptr used in ARMCI data xfer ops*/ size_t size = bytes; int rank; comex_group_rank(COMEX_GROUP_WORLD, &rank); if(size<=0) comex_error("PARMCI_Memget: size must be > 0", (int)size); if(meminfo==NULL) comex_error("PARMCI_Memget: Invalid arg #2 (NULL ptr)",0); if(memflg!=0) comex_error("PARMCI_Memget: Invalid memflg", memflg); armci_ptr = myptr = comex_malloc_local(size); if(size) if(!myptr) comex_error("PARMCI_Memget failed", (int)size); /* fill the meminfo structure */ meminfo->armci_addr = armci_ptr; meminfo->addr = myptr; meminfo->size = size; meminfo->cpid = rank; /* meminfo->attr = NULL; */ } void PARMCI_Memdt(armci_meminfo_t *meminfo, long offset) { } void PARMCI_Memctl(armci_meminfo_t *meminfo) { int rank; comex_group_rank(COMEX_GROUP_WORLD, &rank); if(meminfo==NULL) comex_error("PARMCI_Memget: Invalid arg #2 (NULL ptr)",0); /* only the creator can delete the segment */ if(meminfo->cpid == rank) { void *ptr = meminfo->addr; comex_free_local(ptr); } meminfo->addr = NULL; meminfo->armci_addr = NULL; /* if(meminfo->attr!=NULL) free(meminfo->attr); */ } int PARMCI_NbAccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { int iret; /* check if data is contiguous */ if (armci_check_contiguous(src_stride_arr, dst_stride_arr, count, stride_levels)) { int i; int lcount = 1; for (i=0; i<=stride_levels; i++) lcount *= count[i]; iret = comex_nbacc(optype, scale, src_ptr, dst_ptr, lcount, proc, COMEX_GROUP_WORLD, nb_handle); } else { iret = comex_nbaccs(optype, scale, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, COMEX_GROUP_WORLD, nb_handle); } return iret; } int PARMCI_NbAccV(int op, void *scale, armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { int rc; comex_giov_t *adarr = malloc(sizeof(comex_giov_t) * len); convert_giov(darr, adarr, len); rc = comex_nbaccv(op, scale, adarr, len, proc, COMEX_GROUP_WORLD, nb_handle); free(adarr); return rc; } int PARMCI_NbGet(void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { return comex_nbget(src, dst, bytes, proc, COMEX_GROUP_WORLD, nb_handle); } int PARMCI_NbGetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { int iret; /* check if data is contiguous */ if (armci_check_contiguous(src_stride_arr, dst_stride_arr, count, stride_levels)) { int i; int lcount = 1; for (i=0; i<=stride_levels; i++) lcount *= count[i]; iret = comex_nbget(src_ptr, dst_ptr, lcount, proc, COMEX_GROUP_WORLD, nb_handle); } else { iret = comex_nbgets(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, COMEX_GROUP_WORLD, nb_handle); } return iret; } int PARMCI_NbGetV(armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { int rc; comex_giov_t *adarr = malloc(sizeof(comex_giov_t) * len); convert_giov(darr, adarr, len); rc = comex_nbgetv(adarr, len, proc, COMEX_GROUP_WORLD, nb_handle); free(adarr); return rc; } int PARMCI_NbPut(void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { return comex_nbput(src, dst, bytes, proc, COMEX_GROUP_WORLD, nb_handle); } int PARMCI_NbPutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { int iret; /* check if data is contiguous */ if (armci_check_contiguous(src_stride_arr, dst_stride_arr, count, stride_levels)) { int i; int lcount = 1; for (i=0; i<=stride_levels; i++) lcount *= count[i]; iret = comex_nbput(src_ptr, dst_ptr, lcount, proc, COMEX_GROUP_WORLD, nb_handle); } else { iret = comex_nbputs(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, COMEX_GROUP_WORLD, nb_handle); } return iret; } int PARMCI_NbPutV(armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { int rc; comex_giov_t *adarr = malloc(sizeof(comex_giov_t) * len); convert_giov(darr, adarr, len); rc = comex_nbputv(adarr, len, proc, COMEX_GROUP_WORLD, nb_handle); free(adarr); return rc; } int PARMCI_NbPutValueDouble(double src, void *dst, int proc, armci_hdl_t *nb_handle) { return comex_nbput(&src, dst, sizeof(double), proc, COMEX_GROUP_WORLD, nb_handle); } int PARMCI_NbPutValueFloat(float src, void *dst, int proc, armci_hdl_t *nb_handle) { return comex_nbput(&src, dst, sizeof(float), proc, COMEX_GROUP_WORLD, nb_handle); } int PARMCI_NbPutValueInt(int src, void *dst, int proc, armci_hdl_t *nb_handle) { return comex_nbput(&src, dst, sizeof(int), proc, COMEX_GROUP_WORLD, nb_handle); } int PARMCI_NbPutValueLong(long src, void *dst, int proc, armci_hdl_t *nb_handle) { return comex_nbput(&src, dst, sizeof(long), proc, COMEX_GROUP_WORLD, nb_handle); } int PARMCI_Put(void *src, void *dst, int bytes, int proc) { return comex_put(src, dst, bytes, proc, COMEX_GROUP_WORLD); } int PARMCI_PutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { int iret; /* check if data is contiguous */ if (armci_check_contiguous(src_stride_arr, dst_stride_arr, count, stride_levels)) { int i; int lcount = 1; for (i=0; i<=stride_levels; i++) lcount *= count[i]; iret = comex_put(src_ptr, dst_ptr, lcount, proc, COMEX_GROUP_WORLD); } else { iret = comex_puts(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, COMEX_GROUP_WORLD); } return iret; } int PARMCI_PutS_flag(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { assert(0); return 0; } int PARMCI_PutS_flag_dir(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { assert(0); return 0; } int PARMCI_PutV(armci_giov_t *darr, int len, int proc) { int rc; comex_giov_t *adarr = malloc(sizeof(comex_giov_t) * len); convert_giov(darr, adarr, len); rc = comex_putv(adarr, len, proc, COMEX_GROUP_WORLD); free(adarr); return rc; } int PARMCI_PutValueDouble(double src, void *dst, int proc) { return comex_put(&src, dst, sizeof(double), proc, COMEX_GROUP_WORLD); } int PARMCI_PutValueFloat(float src, void *dst, int proc) { return comex_put(&src, dst, sizeof(float), proc, COMEX_GROUP_WORLD); } int PARMCI_PutValueInt(int src, void *dst, int proc) { return comex_put(&src, dst, sizeof(int), proc, COMEX_GROUP_WORLD); } int PARMCI_PutValueLong(long src, void *dst, int proc) { return comex_put(&src, dst, sizeof(long), proc, COMEX_GROUP_WORLD); } int PARMCI_Put_flag(void *src, void *dst, int bytes, int *f, int v, int proc) { assert(0); return 0; } int PARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc) { return comex_rmw(op, ploc, prem, extra, proc, COMEX_GROUP_WORLD); } int PARMCI_Test(armci_hdl_t *nb_handle) { int rc; int status; rc = comex_test(nb_handle, &status); assert(COMEX_SUCCESS == rc); return status; } void PARMCI_Unlock(int mutex, int proc) { comex_unlock(mutex, proc); } int PARMCI_Wait(armci_hdl_t *nb_handle) { return comex_wait(nb_handle); } int PARMCI_WaitAll() { return comex_wait_all(COMEX_GROUP_WORLD); } int PARMCI_WaitProc(int proc) { return comex_wait_proc(proc, COMEX_GROUP_WORLD); } int parmci_notify(int proc) { assert(0); return 0; } int parmci_notify_wait(int proc, int *pval) { assert(0); return 0; } /** * Return number of processes on node id */ int armci_domain_nprocs(armci_domain_t domain, int id) { return _number_of_procs_per_node; } /** * Return ID of node corresponding to glob_proc_id */ int armci_domain_id(armci_domain_t domain, int glob_proc_id) { return glob_proc_id/_number_of_procs_per_node; } /** * Return global rank of local proc (loc_proc_id) on node id */ int armci_domain_glob_proc_id(armci_domain_t domain, int id, int loc_proc_id) { return id*_number_of_procs_per_node+loc_proc_id; } /** * Return ID of node containing calling process */ int armci_domain_my_id(armci_domain_t domain) { int rank; assert(comex_initialized()); return _my_node_id; } /** * Return number of nodes in the entire system */ int armci_domain_count(armci_domain_t domain) { int size; assert(comex_initialized()); comex_group_size(COMEX_GROUP_WORLD, &size); return size/_number_of_procs_per_node; } int armci_domain_same_id(armci_domain_t domain, int proc) { int rank; comex_group_rank(COMEX_GROUP_WORLD, &rank); return (proc/_number_of_procs_per_node == rank/_number_of_procs_per_node); } void ARMCI_Error(const char *msg, int code) { comex_error(msg, code); } void ARMCI_Set_shm_limit(unsigned long shmemlimit) { /* ignore */ } /* Shared memory not implemented */ int ARMCI_Uses_shm() { return 0; } int ARMCI_Uses_shm_group() { return 0; } /* Is it memory copy? */ void PARMCI_Copy(void *src, void *dst, int n) { assert(0); memcpy(dst, src, sizeof(int) * n); } /* Group Functions */ int ARMCI_Uses_shm_grp(ARMCI_Group *group) { assert(0); return 0; } void ARMCI_Cleanup() { comex_finalize(); } /* JAD technically not an error to have empty impl of aggregate methods */ void ARMCI_SET_AGGREGATE_HANDLE(armci_hdl_t* handle) { } void ARMCI_UNSET_AGGREGATE_HANDLE(armci_hdl_t* handle) { } /* Always return 0, since shared memory not implemented yet */ int ARMCI_Same_node(int proc) { return 0; } ga-5.9.2/comex/src-armci/armci.h000066400000000000000000000414711500715745200163720ustar00rootroot00000000000000/* ARMCI header file */ #ifndef _ARMCI_H #define _ARMCI_H /* for size_t */ #include #include #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif extern int armci_sameclusnode(int proc); typedef struct { void **src_ptr_array; void **dst_ptr_array; int ptr_array_len; int bytes; } armci_giov_t; typedef long armci_size_t; extern int armci_notify(int proc); extern int armci_notify_wait(int proc,int *pval); extern int ARMCI_Init(); /* initialize ARMCI */ extern int ARMCI_Init_args(int *argc, char ***argv); /* initialize ARMCI */ extern int ARMCI_Init_mpi_comm(MPI_Comm comm); /* initialize ARMCI with external communicator */ extern int ARMCI_Initialized(); extern void ARMCI_Barrier(); /* ARMCI Barrier*/ extern int ARMCI_Put(void *src, void* dst, int bytes, int proc); extern int ARMCI_Put_flag(void *src, void* dst,int bytes,int *f,int v,int proc); /* On path for deprecation */ #define ARMCI_Put1(_s,_d,_b,_p) memcpy(_d,_s,_b), 0 extern int ARMCI_PutS( /* strided put */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc /* remote process(or) ID */ ); extern int ARMCI_PutS_flag_dir( /* put with flag that uses direct put */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of segments at each stride levels: count[0]=bytes*/ int stride_levels, /* number of stride levels */ int *flag, /* pointer to remote flag */ int val, /* value to set flag upon completion of data transfer */ int proc /* remote process(or) ID */ ); extern int ARMCI_PutS_flag( void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of segments at each stride levels: count[0]=bytes*/ int stride_levels, /* number of stride levels */ int *flag, /* pointer to remote flag */ int val, /* value to set flag upon completion of data transfer */ int proc /* remote process(or) ID */ ); extern int ARMCI_Acc(int optype, void *scale, void *src, void* dst, int bytes, int proc); extern int ARMCI_AccS( /* strided accumulate */ int optype, /* operation */ void *scale, /* scale factor x += scale*y */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc /* remote process(or) ID */ ); extern int ARMCI_Get(void *src, void* dst, int bytes, int proc); extern int ARMCI_GetS( /* strided get */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc /* remote process(or) ID */ ); extern int ARMCI_GetV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ); extern int ARMCI_PutV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ); extern int ARMCI_AccV( int op, /* operation code */ void *scale, /* scaling factor for accumulate */ armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc /* remote process(or) ID */ ); extern int ARMCI_PutValueInt(int src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc /* remote process (or) ID */ ); extern int ARMCI_PutValueLong(long src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc /* remote process (or) ID */ ); extern int ARMCI_PutValueFloat(float src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc /* remote process (or) ID */ ); extern int ARMCI_PutValueDouble(double src,/* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc /* remote process (or) ID */ ); extern int ARMCI_GetValueInt(void *src, int proc); extern long ARMCI_GetValueLong(void *src, int proc); extern float ARMCI_GetValueFloat(void *src, int proc); extern double ARMCI_GetValueDouble(void *src, int proc); extern int ARMCI_Malloc(void* ptr_arr[], armci_size_t bytes); extern int ARMCI_Malloc_memdev(void* ptr_arr[], armci_size_t bytes, const char *device); extern int ARMCI_Free(void *ptr); extern int ARMCI_Free_memdev(void *ptr); extern void* ARMCI_Malloc_local(armci_size_t bytes); extern int ARMCI_Free_local(void *ptr); extern int ARMCI_Same_node(int proc); extern void ARMCI_Finalize(); /* terminate ARMCI */ extern void ARMCI_Error(const char *msg, int code); extern void ARMCI_Fence(int proc); extern void ARMCI_AllFence(); extern int ARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc); extern void ARMCI_Cleanup(); extern int ARMCI_Create_mutexes(int num); extern int ARMCI_Destroy_mutexes(); extern void ARMCI_Lock(int mutex, int proc); extern void ARMCI_Unlock(int mutex, int proc); extern void ARMCI_Set_shm_limit(unsigned long shmemlimit); extern int ARMCI_Uses_shm(); extern void ARMCI_Copy(void *src, void *dst, int n); #define FAIL -1 #define FAIL2 -2 #define FAIL3 -3 #define FAIL4 -4 #define FAIL5 -5 #define FAIL6 -6 #define FAIL7 -7 #define FAIL8 -8 #define ARMCI_SWAP 10 #define ARMCI_SWAP_LONG 11 #define ARMCI_FETCH_AND_ADD 12 #define ARMCI_FETCH_AND_ADD_LONG 13 #define ARMCI_ACC_OFF 36 #define ARMCI_ACC_INT (ARMCI_ACC_OFF + 1) #define ARMCI_ACC_DBL (ARMCI_ACC_OFF + 2) #define ARMCI_ACC_FLT (ARMCI_ACC_OFF + 3) #define ARMCI_ACC_CPL (ARMCI_ACC_OFF + 4) #define ARMCI_ACC_DCP (ARMCI_ACC_OFF + 5) #define ARMCI_ACC_LNG (ARMCI_ACC_OFF + 6) #define ARMCI_MAX_STRIDE_LEVEL 8 /****************Error/termination macros************************/ /**Debug assert macro. To be used instead of assert for more user * informative and cleaner death. Also allows individualized * enabling/disabling of assert statements. * @param _enable Ignore this assertion if _enable==0 * @param _cond Condition to be evaluated (assert that it is true) * @param _plist Information to be printed using printf, should be * within parenthesis (eg., dassert(1,0,("%d:test n=%d\n",me,0)); * ). This is fed directly to printf. */ int dassertp_fail(const char *cond_string, const char *file, const char *func, unsigned int line, int code); void derr_printf(const char *format, ...); #undef dassertp #define dassertp(_enable,_cond,_plist) do { \ if((_enable) && !(_cond)) { \ derr_printf _plist; \ dassertp_fail(#_cond,__FILE__,FUNCTION_NAME,__LINE__,1); \ }} while(0) #undef dassertc #define dassertc(_enable,_cond,_plist,_code) do { \ if((_enable) && !(_cond)) { \ derr_printf _plist; \ dassertp_fail(#_cond,__FILE__,FUNCTION_NAME,__LINE__,_code); \ }} while(0) #undef dassert #define dassert(_enable,_cond) dassertp((_enable),(_cond),("")) #undef dassert1 #define dassert1(_enable,_cond,_ival) \ dassertp((_enable),(_cond),("%d: error ival=%d\n", \ armci_msg_me(),(int)(_ival))) #define armci_die(_msg,_code) dassertc(1,0, \ ("%d:%s: %d\n", armci_msg_me(),(_msg),(_code)),_code) #define armci_die2(_msg,_code1,_code2) dassertc(1,0, \ ("%d:%s: (%d,%d)\n",armci_msg_me(),(_msg),(_code1),(_code2)),_code1) /************ locality information **********************************************/ typedef int armci_domain_t; #define ARMCI_DOMAIN_SMP 0 /* SMP node domain for armci_domain_XXX calls */ extern int armci_domain_nprocs(armci_domain_t domain, int id); extern int armci_domain_id(armci_domain_t domain, int glob_proc_id); extern int armci_domain_glob_proc_id(armci_domain_t domain, int id, int loc_proc_id); extern int armci_domain_my_id(armci_domain_t domain); extern int armci_domain_count(armci_domain_t domain); extern int armci_domain_same_id(armci_domain_t domain, int proc); extern char *mp_group_name; /*********************stuff for non-blocking API******************************/ /*\ the request structure for non-blocking api. \*/ /*max of sizes for all platforms. Increase if any platform needs more*/ typedef int armci_hdl_t; typedef int ARMCI_Group; extern void ARMCI_GroupFence(ARMCI_Group *group); extern void ARMCI_Group_create(int n, int *pid_list, ARMCI_Group *group_out); extern void ARMCI_Group_create_child(int n, int *pid_list, ARMCI_Group *group_out, ARMCI_Group *group_parent); extern void ARMCI_Group_free(ARMCI_Group *group); extern int ARMCI_Group_rank(ARMCI_Group *group, int *rank); extern void ARMCI_Group_size(ARMCI_Group *group, int *size); extern void ARMCI_Group_set_default(ARMCI_Group *group); extern void ARMCI_Group_get_default(ARMCI_Group *group_out); extern void ARMCI_Group_get_world(ARMCI_Group *group_out); extern int ARMCI_Absolute_id(ARMCI_Group *group, int group_rank); extern int ARMCI_Uses_shm_grp(ARMCI_Group *group); extern int ARMCI_Malloc_group(void *ptr_arr[], armci_size_t bytes,ARMCI_Group *group); extern int ARMCI_Malloc_group_memdev(void *ptr_arr[], armci_size_t bytes, ARMCI_Group *group, const char *device); extern int ARMCI_Free_group(void *ptr, ARMCI_Group *group); extern int ARMCI_NbPut(void *src, void* dst, int bytes, int proc,armci_hdl_t* nb_handle); extern int ARMCI_NbPutS( /* strided put */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbAccS( /* strided accumulate */ int optype, /* operation */ void *scale, /* scale factor x += scale*y */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbGet(void *src, void* dst, int bytes, int proc,armci_hdl_t* nb_handle); extern int ARMCI_NbGetS( /* strided get */ void *src_ptr, /* pointer to 1st segment at source*/ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination*/ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level count[0]=bytes */ int stride_levels, /* number of stride levels */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handler/*armci_non-blocking request handle*/ ); extern int ARMCI_NbGetV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbPutV( armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbAccV( int op, /* operation code */ void *scale, /* scaling factor for accumulate */ armci_giov_t darr[], /* descriptor array */ int len, /* length of descriptor array */ int proc, /* remote process(or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle*/ ); extern int ARMCI_NbPutValueInt(int src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc, /* remote process (or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle */ ); extern int ARMCI_NbPutValueLong(long src, /* value in a register to put */ void *dst, /* dest starting addr to put data */ int proc, /* remote process (or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle */ ); extern int ARMCI_NbPutValueFloat(float src,/* value in a register to put */ void *dst,/* dest starting addr to put data */ int proc, /* remote process (or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle */ ); extern int ARMCI_NbPutValueDouble(double src,/* value in a register to put */ void *dst,/* dest starting addr to put data*/ int proc, /* remote process (or) ID */ armci_hdl_t* nb_handle /*armci_non-blocking request handle */ ); extern int ARMCI_Wait(armci_hdl_t* nb_handle); /*non-blocking request handle*/ extern int ARMCI_Test(armci_hdl_t* nb_handle); /*non-blocking request handle*/ extern int ARMCI_WaitAll (); extern int ARMCI_WaitProc (int proc); extern void ARMCI_SET_AGGREGATE_HANDLE(armci_hdl_t* nb_handle); extern void ARMCI_UNSET_AGGREGATE_HANDLE(armci_hdl_t* nb_handle); /* #define ARMCI_INIT_HANDLE(hdl) do {((double *)((hdl)->data))[0]=0; \ ((double *)((hdl)->data))[1]=0; }while(0) */ #define ARMCI_INIT_HANDLE(hdl) /* -------------- ARMCI Non-collective memory allocator ------------- */ typedef struct armci_meminfo_ds { char * armci_addr; /* remote address of the creator which can be used in ARMCI communication */ char *addr; /* local address of creator which can be used in to set SMP memoffset, armci_set_mem_offset() */ size_t size; /* size of remote pid's segment (bytes) */ int cpid; /* armci pid of creator */ long idlist[128]; } armci_meminfo_t; extern void ARMCI_Memget(size_t bytes, armci_meminfo_t *meminfo, int memflg); extern void* ARMCI_Memat(armci_meminfo_t *meminfo, long offset); extern void ARMCI_Memdt(armci_meminfo_t *meminfo, long offset); extern void ARMCI_Memctl(armci_meminfo_t *meminfo); /* ------------------------------------------------------------------ */ #if defined(__cplusplus) || defined(c_plusplus) } #endif #endif /* _ARMCI_H */ ga-5.9.2/comex/src-armci/capi.c000066400000000000000000000274761500715745200162170ustar00rootroot00000000000000 #if HAVE_CONFIG_H # include "config.h" #endif #include #include "armci.h" #include "parmci.h" #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Acc #endif int ARMCI_Acc(int optype, void *scale, void *src, void *dst, int bytes, int proc) { return PARMCI_Acc(optype, scale, src, dst, bytes, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_AccS #endif int ARMCI_AccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { return PARMCI_AccS(optype, scale, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_AccV #endif int ARMCI_AccV(int op, void *scale, armci_giov_t *darr, int len, int proc) { return PARMCI_AccV(op, scale, darr, len, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_AllFence #endif void ARMCI_AllFence() { PARMCI_AllFence(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GroupFence #endif void ARMCI_GroupFence(ARMCI_Group *group) { PARMCI_GroupFence(group); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Barrier #endif void ARMCI_Barrier() { PARMCI_Barrier(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Create_mutexes #endif int ARMCI_Create_mutexes(int num) { return PARMCI_Create_mutexes(num); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Destroy_mutexes #endif int ARMCI_Destroy_mutexes() { return PARMCI_Destroy_mutexes(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Fence #endif void ARMCI_Fence(int proc) { PARMCI_Fence(proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Finalize #endif void ARMCI_Finalize() { PARMCI_Finalize(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Free #endif int ARMCI_Free(void *ptr) { return PARMCI_Free(ptr); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Free_memdev #endif int ARMCI_Free_memdev(void *ptr) { return PARMCI_Free_memdev(ptr); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Free_local #endif int ARMCI_Free_local(void *ptr) { return PARMCI_Free_local(ptr); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Get #endif int ARMCI_Get(void *src, void *dst, int bytes, int proc) { return PARMCI_Get(src, dst, bytes, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetS #endif int ARMCI_GetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { return PARMCI_GetS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetV #endif int ARMCI_GetV(armci_giov_t *darr, int len, int proc) { return PARMCI_GetV(darr, len, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetValueDouble #endif double ARMCI_GetValueDouble(void *src, int proc) { return PARMCI_GetValueDouble(src, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetValueFloat #endif float ARMCI_GetValueFloat(void *src, int proc) { return PARMCI_GetValueFloat(src, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetValueInt #endif int ARMCI_GetValueInt(void *src, int proc) { return PARMCI_GetValueInt(src, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_GetValueLong #endif long ARMCI_GetValueLong(void *src, int proc) { return PARMCI_GetValueLong(src, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Init #endif int ARMCI_Init() { return PARMCI_Init(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Init_args #endif int ARMCI_Init_args(int *argc, char ***argv) { return PARMCI_Init_args(argc, argv); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Init_mpi_comm #endif int ARMCI_Init_mpi_comm(MPI_Comm comm) { return PARMCI_Init_mpi_comm(comm); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Initialized #endif int ARMCI_Initialized() { return PARMCI_Initialized(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Lock #endif void ARMCI_Lock(int mutex, int proc) { PARMCI_Lock(mutex, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Malloc #endif int ARMCI_Malloc(void **ptr_arr, armci_size_t bytes) { return PARMCI_Malloc(ptr_arr, bytes); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Malloc_memdev #endif int ARMCI_Malloc_memdev(void **ptr_arr, armci_size_t bytes, const char *device) { return PARMCI_Malloc_memdev(ptr_arr, bytes, device); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Malloc_local #endif void* ARMCI_Malloc_local(armci_size_t bytes) { return PARMCI_Malloc_local(bytes); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Memat #endif void* ARMCI_Memat(armci_meminfo_t *meminfo, long offset) { return PARMCI_Memat(meminfo, offset); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Memget #endif void ARMCI_Memget(size_t bytes, armci_meminfo_t *meminfo, int memflg) { PARMCI_Memget(bytes, meminfo, memflg); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Memctl #endif void ARMCI_Memctl(armci_meminfo_t *meminfo) { PARMCI_Memctl(meminfo); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbAccS #endif int ARMCI_NbAccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbAccS(optype, scale, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbAccV #endif int ARMCI_NbAccV(int op, void *scale, armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbAccV(op, scale, darr, len, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbGet #endif int ARMCI_NbGet(void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbGet(src, dst, bytes, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbGetS #endif int ARMCI_NbGetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbGetS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbGetV #endif int ARMCI_NbGetV(armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbGetV(darr, len, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPut #endif int ARMCI_NbPut(void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPut(src, dst, bytes, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutS #endif int ARMCI_NbPutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutV #endif int ARMCI_NbPutV(armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutV(darr, len, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutValueDouble #endif int ARMCI_NbPutValueDouble(double src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutValueDouble(src, dst, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutValueFloat #endif int ARMCI_NbPutValueFloat(float src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutValueFloat(src, dst, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutValueInt #endif int ARMCI_NbPutValueInt(int src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutValueInt(src, dst, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_NbPutValueLong #endif int ARMCI_NbPutValueLong(long src, void *dst, int proc, armci_hdl_t *nb_handle) { return PARMCI_NbPutValueLong(src, dst, proc, nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Put #endif int ARMCI_Put(void *src, void *dst, int bytes, int proc) { return PARMCI_Put(src, dst, bytes, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutS #endif int ARMCI_PutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { return PARMCI_PutS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutS_flag #endif int ARMCI_PutS_flag(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { return PARMCI_PutS_flag(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, flag, val, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutS_flag_dir #endif int ARMCI_PutS_flag_dir(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { return PARMCI_PutS_flag_dir(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, flag, val, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutV #endif int ARMCI_PutV(armci_giov_t *darr, int len, int proc) { return PARMCI_PutV(darr, len, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutValueDouble #endif int ARMCI_PutValueDouble(double src, void *dst, int proc) { return PARMCI_PutValueDouble(src, dst, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutValueFloat #endif int ARMCI_PutValueFloat(float src, void *dst, int proc) { return PARMCI_PutValueFloat(src, dst, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutValueInt #endif int ARMCI_PutValueInt(int src, void *dst, int proc) { return PARMCI_PutValueInt(src, dst, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_PutValueLong #endif int ARMCI_PutValueLong(long src, void *dst, int proc) { return PARMCI_PutValueLong(src, dst, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Put_flag #endif int ARMCI_Put_flag(void *src, void *dst, int bytes, int *f, int v, int proc) { return PARMCI_Put_flag(src, dst, bytes, f, v, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Rmw #endif int ARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc) { return PARMCI_Rmw(op, ploc, prem, extra, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Test #endif int ARMCI_Test(armci_hdl_t *nb_handle) { return PARMCI_Test(nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Unlock #endif void ARMCI_Unlock(int mutex, int proc) { PARMCI_Unlock(mutex, proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_Wait #endif int ARMCI_Wait(armci_hdl_t *nb_handle) { return PARMCI_Wait(nb_handle); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_WaitAll #endif int ARMCI_WaitAll() { return PARMCI_WaitAll(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak ARMCI_WaitProc #endif int ARMCI_WaitProc(int proc) { return PARMCI_WaitProc(proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak armci_msg_barrier #endif void armci_msg_barrier() { parmci_msg_barrier(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak armci_msg_group_barrier #endif void armci_msg_group_barrier(ARMCI_Group *group) { parmci_msg_group_barrier(group); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak armci_notify #endif int armci_notify(int proc) { return parmci_notify(proc); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak armci_notify_wait #endif int armci_notify_wait(int proc, int *pval) { return parmci_notify_wait(proc, pval); } ga-5.9.2/comex/src-armci/groups.c000066400000000000000000000024551500715745200166100ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include "armci.h" #include "message.h" #include "comex.h" /* ARMCI has the notion of a default group and a world group. */ ARMCI_Group ARMCI_Default_Proc_Group = 0; int ARMCI_Group_rank(ARMCI_Group *id, int *rank) { return comex_group_rank(*id, rank); } void ARMCI_Group_size(ARMCI_Group *id, int *size) { comex_group_size(*id, size); } int ARMCI_Absolute_id(ARMCI_Group *id, int group_rank) { int world_rank; assert(COMEX_SUCCESS == comex_group_translate_world(*id, group_rank, &world_rank)); return world_rank; } void ARMCI_Group_set_default(ARMCI_Group *id) { ARMCI_Default_Proc_Group = *id; } void ARMCI_Group_get_default(ARMCI_Group *group_out) { *group_out = ARMCI_Default_Proc_Group; } void ARMCI_Group_get_world(ARMCI_Group *group_out) { *group_out = COMEX_GROUP_WORLD; } void ARMCI_Group_free(ARMCI_Group *id) { comex_group_free(*id); } void ARMCI_Group_create_child( int n, int *pid_list, ARMCI_Group *id_child, ARMCI_Group *id_parent) { comex_group_create(n, pid_list, *id_parent, id_child); } void ARMCI_Group_create(int n, int *pid_list, ARMCI_Group *group_out) { comex_group_create(n, pid_list, ARMCI_Default_Proc_Group, group_out); } ga-5.9.2/comex/src-armci/iterator.c000066400000000000000000000123031500715745200171130ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** @file iterator.h * @author Sriram Krishnamoorthy * @brief Stride iterator. * An iterator for the stride descriptor to reuse common traversal * functionality. More functionality related to the strided * descriptor reusable across files will be extracted here as well. */ #include #include #include #include #include "iterator.h" /**Create a stride iterator. * @param base_ptr IN Starting pointer for stride descriptor * @param stride_levels IN #stride levels * @param stride_arr IN the strides (arr of size[stride_levels]) * @param seg_count IN #segments in each stride * level([stride_levels+1]) * @return Handle to stride iterator created */ void armci_stride_info_init(stride_info_t *sinfo, void *base_ptr, int stride_levels, const int *stride_arr, const int *seg_count) { int i; assert(sinfo!=NULL); assert(stride_levels>=0); assert(stride_levels<=ARMCI_MAX_STRIDE_LEVEL); for(i=0; i= seg_count[0]); else assert(stride_arr[i] >= stride_arr[i-1]*seg_count[i]); } sinfo->base_ptr= base_ptr; sinfo->stride_levels = stride_levels; for(i=0; istride_arr[i] = stride_arr[i]; } for(i=0; iseg_count[i] = seg_count[i]; } sinfo->size=1; for(i=1; isize *= sinfo->seg_count[i]; } assert(sinfo->size>0); sinfo->pos=0; for(i=0; iitr[i] = 0; } } /**Destroy a stride iterator. * @param psitr IN/OUT Pointer to stride iterator * @return void */ void armci_stride_info_destroy(stride_info_t *sinfo) { } /**Size of the stride iterator. Defined as total #contiguous * segments in the stride iterator. * @param sitr IN Handle to stride iterator * @return Size of the stride iterator */ int armci_stride_info_size(stride_info_t *sinfo) { assert(sinfo!=NULL); return sinfo->size; } /**Position of the stride iterator. Between 0 and (size-1), * inclusive. Position is the index of the contiguous segment * currently traversed by the iterator. * @param sitr IN Handle to stride descriptor * @return Position of the iterator */ int armci_stride_info_pos(stride_info_t *sinfo) { assert(sinfo!=NULL); return sinfo->pos; } /**Move the iterator to the next position. Assumes position<=size. * @param sitr IN Handle to stride descriptor * @return void */ void armci_stride_info_next(stride_info_t *sinfo) { int i; assert(sinfo!=NULL); assert(sinfo->pos size); sinfo->pos += 1; if(sinfo->stride_levels>0) { sinfo->itr[0] += 1; for(i=0; istride_levels-1 && sinfo->itr[i]==sinfo->seg_count[i+1]; i++) { sinfo->itr[i] = 0; sinfo->itr[i+1] += 1; } assert(sinfo->itr[i] <= sinfo->seg_count[i+1]); } } /**Get pointer to the contiguous segment currently being * traversed. This is the pointer to the user buffer. * @param sitr IN Handle to stride descriptor * @return pointer to current contiguous segment */ void *armci_stride_info_seg_ptr(stride_info_t *sinfo) { assert(sinfo!=NULL); return sinfo->base_ptr + armci_stride_info_seg_off(sinfo); } /**Get the size of the current segment. * @param sitr IN Handle to stride descriptor * @return Size of the current segment */ int armci_stride_info_seg_size(stride_info_t *sinfo) { assert(sinfo!=NULL); return sinfo->seg_count[0]; } /**Get the offset of the current segment with respect to the start of * the first segment (a.k.a src_ptr) * @param sitr IN Handle to stride descriptor * @return Offset of the current segment */ int armci_stride_info_seg_off(stride_info_t *sinfo) { int i; int off; assert(sinfo!=NULL); off=0; for(i=0; istride_levels; i++) { off += sinfo->itr[i] * sinfo->stride_arr[i]; } return off; } /**Check if there are more segments to iterate over. (a.k.a * positionpossize; } void armci_write_strided( void *ptr, int stride_levels, int stride_arr[], int count[], char *buf) { const int seg_size = count[0]; int off; stride_info_t sitr; off=0; assert(count[0]>0); armci_stride_info_init(&sitr,ptr,stride_levels,stride_arr,count); while(armci_stride_info_has_more(&sitr)) { char *sptr = armci_stride_info_seg_ptr(&sitr); memcpy(&buf[off],sptr,seg_size); off += seg_size; armci_stride_info_next(&sitr); } armci_stride_info_destroy(&sitr); } void armci_read_strided( void *ptr, int stride_levels, int stride_arr[], int count[], char *buf) { const int seg_size = count[0]; int off; stride_info_t sitr; off=0; assert(count[0]>0); armci_stride_info_init(&sitr,ptr,stride_levels,stride_arr,count); while(armci_stride_info_has_more(&sitr)) { char *dptr = armci_stride_info_seg_ptr(&sitr); memcpy(dptr,&buf[off],seg_size); off += seg_size; armci_stride_info_next(&sitr); } armci_stride_info_destroy(&sitr); } ga-5.9.2/comex/src-armci/iterator.h000066400000000000000000000026041500715745200171230ustar00rootroot00000000000000/** @file iterator.h * @author Sriram Krishnamoorthy * @brief Stride iterator. * An iterator for the stride descriptor to reuse common traversal * functionality. More functionality related to the strided * descriptor reusable across files will be extracted here as well. */ #ifndef _STRIDE_INFO_H_ #define _STRIDE_INFO_H_ #include "armci.h" /*for ARMCI_MAX_STRIDE_LEVEL and dassert*/ #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif typedef struct { char *base_ptr; int stride_levels; int stride_arr[ARMCI_MAX_STRIDE_LEVEL]; int seg_count[ARMCI_MAX_STRIDE_LEVEL+1]; int size, pos, itr[ARMCI_MAX_STRIDE_LEVEL]; } stride_info_t; void armci_stride_info_init(stride_info_t *sinfo, void *base_ptr, int stride_levels, const int *stride_arr, const int *seg_count); void armci_stride_info_destroy(stride_info_t *sinfo); int armci_stride_info_count(stride_info_t *sinfo); int armci_stride_info_pos(stride_info_t *sinfo); void armci_stride_info_next(stride_info_t *sinfo); void *armci_stride_info_seg_ptr(stride_info_t *sinfo); int armci_stride_info_seg_size(stride_info_t *sinfo); int armci_stride_info_seg_off(stride_info_t *sinfo); int armci_stride_info_size(stride_info_t *sinfo); int armci_stride_info_has_more(stride_info_t *sinfo); #if defined(__cplusplus) || defined(c_plusplus) } #endif #endif /*_STRIDE_INFO_H_*/ ga-5.9.2/comex/src-armci/message.c000066400000000000000000000440201500715745200167070ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include "armci.h" #include "message.h" #include "comex.h" #if COMEX_NETWORK_MPI_TS extern void comex_make_progress(void); #define COMEX_TAG 27624 #endif /* hacky alias for MPI_COMM_SELF */ static ARMCI_Group ARMCI_GROUP_SELF = -2; extern int ARMCI_Default_Proc_Group; /* for armci_msg_sel_scope */ static MPI_Datatype MPI_LONGLONG_INT; /* Information on nodes */ extern int _number_of_procs_per_node; extern int _my_node_id; extern ARMCI_Group ARMCI_Node_group; static MPI_Comm wc() { int check; MPI_Comm comm; check = comex_group_comm(COMEX_GROUP_WORLD, &comm); assert(COMEX_SUCCESS == check); return comm; } /* undocumented, but used in GA to expose MPI_Comm */ MPI_Comm armci_group_comm(ARMCI_Group *group) { int check; MPI_Comm comm; check = comex_group_comm(*group, &comm); assert(COMEX_SUCCESS == check); return comm; } static MPI_Datatype armci_type_to_mpi_type(int type) { MPI_Datatype mpi_dt; if (type == ARMCI_INT) { mpi_dt = MPI_INT; } else if (type == ARMCI_LONG) { mpi_dt = MPI_LONG; } else if (type == ARMCI_LONG_LONG) { mpi_dt = MPI_LONG_LONG; } else if (type == ARMCI_FLOAT) { mpi_dt = MPI_FLOAT; } else if (type == ARMCI_DOUBLE) { mpi_dt = MPI_DOUBLE; } else { comex_error("armci_type_to_mpi_type: unrecognized type", type); } return mpi_dt; } static MPI_Op armci_op_to_mpi_op(char *op) { MPI_Op result; if (strncmp(op, "+", 1) == 0) { result = MPI_SUM; } else if (strncmp(op, "max", 3) == 0) { result = MPI_MAX; } else if (strncmp(op, "min", 3) == 0) { result = MPI_MIN; } else if (strncmp(op, "*", 1) == 0) { result = MPI_PROD; } else if (strncmp(op, "absmin", 6) == 0) { result = MPI_MIN; } else if (strncmp(op, "absmax", 6) == 0) { result = MPI_MAX; } else if (strncmp(op, "or", 2) == 0) { result = MPI_BOR; } /* these are new */ else if ((strncmp(op, "&&", 2) == 0) || (strncmp(op, "land", 4) == 0)) { result = MPI_LAND; } else if ((strncmp(op, "||", 2) == 0) || (strncmp(op, "lor", 3) == 0)) { result = MPI_LOR; } else if ((strncmp(op, "&", 1) == 0) || (strncmp(op, "band", 4) == 0)) { result = MPI_BAND; } else if ((strncmp(op, "|", 1) == 0) || (strncmp(op, "bor", 3) == 0)) { result = MPI_BOR; } else { comex_error("Unsupported gop operation",1); } return result; } static void do_abs(void *x, int n, int type) { #define ARMCI_ABS_INT(a) (((a) >= 0) ? (a) : (-(a))) #define ARMCI_ABS_FLT(a) (((a) >= 0.0) ? (a) : (-(a))) #define DO_ABS(ARMCI_TYPE, C_TYPE, WHICH) \ if (type == ARMCI_TYPE) { \ int i; \ C_TYPE *y = (C_TYPE *)x; \ for (i = 0; i < n; i++) { \ y[i] = ARMCI_ABS_##WHICH(y[i]); \ } \ } \ else DO_ABS(ARMCI_INT, int, INT) DO_ABS(ARMCI_LONG, long, INT) DO_ABS(ARMCI_LONG_LONG, long long, INT) DO_ABS(ARMCI_FLOAT, float, FLT) DO_ABS(ARMCI_DOUBLE, double, FLT) { comex_error("unsupported ABS operation", 1); } #undef ARMCI_ABS_INT #undef ARMCI_ABS_FLT #undef DO_ABS } static MPI_Comm get_comm(ARMCI_Group *group) { int check; MPI_Comm comm; check = comex_group_comm(*group, &comm); assert(COMEX_SUCCESS == check); return comm; } static ARMCI_Group get_default_group() { ARMCI_Group group; ARMCI_Group_get_default(&group); return group; } static MPI_Comm get_default_comm() { ARMCI_Group group; ARMCI_Group_get_default(&group); return get_comm(&group); } static int get_default_rank() { int rank; MPI_Comm_rank(get_default_comm(), &rank); return rank; } static void do_gop(void *x, int n, char* op, int type, ARMCI_Group group) { MPI_Comm comm = MPI_COMM_NULL; int mpi_type_size = 0; MPI_Datatype mpi_type = MPI_DATATYPE_NULL; int rc = 0; void *result = NULL; MPI_Op mpi_op = MPI_OP_NULL; if (ARMCI_GROUP_SELF == group) { comm = MPI_COMM_SELF; } else { comm = get_comm(&group); } mpi_type = armci_type_to_mpi_type(type); MPI_Type_size(mpi_type, &mpi_type_size); mpi_op = armci_op_to_mpi_op(op); if (strncmp(op, "absmin", 6) == 0) { do_abs(x, n, type); } else if (strncmp(op, "absmax", 6) == 0) { do_abs(x, n, type); } result = malloc(n*mpi_type_size); assert(result); if (ARMCI_GROUP_SELF != group) { comex_barrier(group); } rc = MPI_Allreduce(x, result, n, mpi_type, mpi_op, comm); assert(rc == MPI_SUCCESS); memcpy(x, result, mpi_type_size * n); free(result); } void armci_msg_bcast(void *buf, int len, int root) { assert(buf != NULL); comex_barrier(ARMCI_Default_Proc_Group); MPI_Bcast(buf, len, MPI_BYTE, root, get_default_comm()); } /* the payload is a struct with a union e.g. * * typedef struct { * union val_t {double dval; int ival; long lval; long long llval; float fval;}v; * Integer subscr[MAXDIM]; * DoubleComplex extra; * SingleComplex extra2; * } elem_info_t; * * The key piece is the first sizeof(double) bytes. The rest of the struct * simply tags along for the communication and can be represented as a byte * stream. * * The 'n' parameter is the size of the entire payload i.e. * sizeof(struct elem_info_t). * * We really care which process has the min/max value and then bcast the * entire payload using the min/max answer as the root. */ void armci_msg_sel_scope(int scope, void *x, int n, char* op, int type, int contribute) { static int initialized = 0; MPI_Op mpi_op = MPI_OP_NULL; MPI_Comm comm = get_default_comm(); if (SCOPE_NODE == scope) { if (_number_of_procs_per_node == 1) { comm = MPI_COMM_SELF; } else { comex_group_comm(ARMCI_Node_group,&comm); } } /* first time this function is called we establish the * long long w/ int type that MPI doesn't provide by default */ if (!initialized) { int block[2]; MPI_Aint disp[2]; MPI_Datatype type[2]; initialized = 1; type[0] = MPI_LONG_LONG; type[1] = MPI_INT; disp[0] = 0; disp[1] = sizeof(long long); block[0] = 1; block[1] = 1; #if defined(MPI_VERSION) && (MPI_VERSION >= 2) MPI_Type_create_struct(2, block, disp, type, &MPI_LONGLONG_INT); #else MPI_Type_struct(2, block, disp, type, &MPI_LONGLONG_INT); #endif } if (strncmp(op, "min", 3) == 0) { mpi_op = MPI_MINLOC; } else if (strncmp(op, "max", 3) == 0) { mpi_op = MPI_MAXLOC; } else { comex_error("unsupported armci_msg_sel_scope operation", 1); } #define SELECT(ARMCI_TYPE, C_TYPE, MPI_TYPE) \ if (type == ARMCI_TYPE) { \ struct { \ C_TYPE val; \ int rank; \ } in, out; \ in.val = *((C_TYPE*)x); \ in.rank = get_default_rank(); \ if (SCOPE_NODE != scope) { \ comex_barrier(ARMCI_Default_Proc_Group); \ } \ MPI_Allreduce(&in, &out, 1, MPI_TYPE, mpi_op, comm); \ armci_msg_bcast(x, n, out.rank); \ } \ else SELECT(ARMCI_INT, int, MPI_2INT) SELECT(ARMCI_LONG, long, MPI_LONG_INT) SELECT(ARMCI_LONG_LONG, long long, MPI_LONGLONG_INT) SELECT(ARMCI_FLOAT, float, MPI_FLOAT_INT) SELECT(ARMCI_DOUBLE, double, MPI_DOUBLE_INT) { comex_error("unsupported SELECT operation", 1); } #undef SELECT } void armci_msg_bcast_scope(int scope, void* buffer, int len, int root) { if (SCOPE_ALL == scope || SCOPE_MASTERS == scope) { armci_msg_bcast(buffer, len, root); } else if (SCOPE_NODE == scope) { MPI_Comm comm; assert(buffer != NULL); comex_group_comm(ARMCI_Node_group,&comm); MPI_Bcast(buffer, len, MPI_BYTE, root, comm); } else { comex_error("unsupported armci_msg_bcast_scope scope", 1); } } void armci_msg_brdcst(void* buffer, int len, int root) { armci_msg_bcast(buffer, len, root); } /* there was a case in ghost update where a proc sent a message to itself */ static MPI_Request self_request = MPI_REQUEST_NULL; static int self_request_flag = 0; void armci_msg_snd(int tag, void* buffer, int len, int to) { MPI_Comm comm = wc(); MPI_Request request; MPI_Status status; int self; int flag = 0; int rc; rc = MPI_Comm_rank(comm, &self); assert(MPI_SUCCESS == rc); #if COMEX_NETWORK_MPI_TS assert(COMEX_TAG != tag); #endif rc = MPI_Isend(buffer, len, MPI_CHAR, to, tag, wc(), &request); assert(MPI_SUCCESS == rc); if (to == self) { /* make sure this proc hasn't already sent to itself -- we only allow * one outstanding self send */ assert(!self_request_flag); self_request_flag = 1; self_request = request; } else { do { MPI_Test(&request, &flag, &status); assert(MPI_SUCCESS == rc); #if COMEX_NETWORK_MPI_TS comex_make_progress(); #endif } while (!flag); } } void armci_msg_rcv(int tag, void* buffer, int len, int *msglen, int from) { MPI_Comm comm = wc(); MPI_Request request; MPI_Status status; int self; int flag = 0; int rc; rc = MPI_Comm_rank(comm, &self); assert(MPI_SUCCESS == rc); #if COMEX_NETWORK_MPI_TS assert(COMEX_TAG != tag); #endif rc = MPI_Irecv(buffer, len, MPI_CHAR, from, tag, wc(), &request); assert(MPI_SUCCESS == rc); if (from == self && self_request_flag) { do { MPI_Test(&self_request, &flag, &status); assert(MPI_SUCCESS == rc); #if COMEX_NETWORK_MPI_TS comex_make_progress(); #endif } while (!flag); self_request = MPI_REQUEST_NULL; self_request_flag = 0; } do { MPI_Test(&request, &flag, &status); assert(MPI_SUCCESS == rc); #if COMEX_NETWORK_MPI_TS comex_make_progress(); #endif } while (!flag); if(msglen) { rc = MPI_Get_count(&status, MPI_CHAR, msglen); assert(MPI_SUCCESS == rc); } } int armci_msg_rcvany(int tag, void* buffer, int len, int *msglen) { MPI_Comm comm = wc(); MPI_Request request; MPI_Status status; int rank; int flag = 0; int rc; rc = MPI_Comm_rank(comm, &rank); assert(MPI_SUCCESS == rc); #if COMEX_NETWORK_MPI_TS assert(COMEX_TAG != tag); #endif rc = MPI_Irecv(buffer, len, MPI_CHAR, MPI_ANY_SOURCE, tag, wc(), &request); assert(MPI_SUCCESS == rc); do { MPI_Test(&request, &flag, &status); assert(MPI_SUCCESS == rc); #if COMEX_NETWORK_MPI_TS comex_make_progress(); #endif } while (!flag); if(msglen) { rc = MPI_Get_count(&status, MPI_CHAR, msglen); assert(MPI_SUCCESS == rc); } return (int)status.MPI_SOURCE; } void armci_msg_reduce(void *x, int n, char *op, int type) { do_gop(x, n, op, type, get_default_group()); } void armci_msg_reduce_scope(int scope, void *x, int n, char *op, int type) { if (SCOPE_NODE == scope) { if (_number_of_procs_per_node > 1) { do_gop(x, n, op, type, ARMCI_Node_group); } else { do_gop(x, n, op, type, ARMCI_GROUP_SELF); } } else { do_gop(x, n, op, type, get_default_group()); } } void armci_msg_gop_scope(int scope, void *x, int n, char* op, int type) { if (SCOPE_NODE == scope) { if (_number_of_procs_per_node > 1) { do_gop(x, n, op, type, ARMCI_Node_group); } else { do_gop(x, n, op, type, ARMCI_GROUP_SELF); } } else { do_gop(x, n, op, type, get_default_group()); } } void armci_msg_igop(int *x, int n, char* op) { do_gop(x, n, op, ARMCI_INT, get_default_group()); } void armci_msg_lgop(long *x, int n, char* op) { do_gop(x, n, op, ARMCI_LONG, get_default_group()); } void armci_msg_llgop(long long *x, int n, char* op) { do_gop(x, n, op, ARMCI_LONG_LONG, get_default_group()); } void armci_msg_fgop(float *x, int n, char* op) { do_gop(x, n, op, ARMCI_FLOAT, get_default_group()); } void armci_msg_dgop(double *x, int n, char* op) { do_gop(x, n, op, ARMCI_DOUBLE, get_default_group()); } void armci_exchange_address(void *ptr_ar[], int n) { ARMCI_Group group; ARMCI_Group_get_default(&group); armci_exchange_address_grp(ptr_ar, n, &group); } void parmci_msg_barrier() { comex_barrier(ARMCI_Default_Proc_Group); MPI_Barrier(get_default_comm()); } void armci_msg_bintree(int scope, int* Root, int *Up, int *Left, int *Right) { int root, up, left, right, index, nproc; assert(SCOPE_NODE != scope); assert(SCOPE_MASTERS != scope); root = 0; nproc = armci_msg_nproc(); index = armci_msg_me() - root; up = (index-1)/2 + root; if( up < root) up = -1; left = 2*index + 1 + root; if(left >= root+nproc) left = -1; right = 2*index + 2 + root; if(right >= root+nproc)right = -1; *Up = up; *Left = left; *Right = right; *Root = root; } int armci_msg_me() { int rank; assert(comex_initialized()); assert(wc() != MPI_COMM_NULL); MPI_Comm_rank(wc(), &rank); return rank; } int armci_msg_nproc() { int size; assert(comex_initialized()); assert(wc() != MPI_COMM_NULL); MPI_Comm_size(wc(), &size); return size; } void armci_msg_abort(int code) { fprintf(stderr, "Exiting, Error in Communication\n"); MPI_Abort(wc(), code); } void armci_msg_init(int *argc, char ***argv) { int flag; MPI_Initialized(&flag); if(!flag) { MPI_Init(argc, argv); } } void armci_msg_finalize() { int flag; MPI_Initialized(&flag); assert(flag); MPI_Finalize(); } double armci_timer() { return MPI_Wtime(); } void armci_msg_clus_brdcst(void *buf, int len) { comex_error("armci_msg_clus_brdcst not implemented", 1); } void armci_msg_clus_igop(int *x, int n, char* op) { comex_error("armci_msg_clus_igop not implemented", 1); } void armci_msg_clus_fgop(float *x, int n, char* op) { comex_error("armci_msg_clus_fgop not implemented", 1); } void armci_msg_clus_lgop(long *x, int n, char* op) { comex_error("armci_msg_clus_lgop not implemented", 1); } void armci_msg_clus_llgop(long long *x, int n, char* op) { comex_error("armci_msg_clus_llgop not implemented", 1); } void armci_msg_clus_dgop(double *x, int n, char* op) { comex_error("armci_msg_clus_dgop not implemented", 1); } void armci_msg_group_gop_scope(int scope, void *x, int n, char* op, int type, ARMCI_Group *group) { if (SCOPE_NODE == scope) { do_gop(x, n, op, type, ARMCI_GROUP_SELF); } else { do_gop(x, n, op, type, *group); } } void armci_msg_group_igop(int *x, int n, char* op, ARMCI_Group *group) { do_gop(x, n, op, ARMCI_INT, *group); } void armci_msg_group_lgop(long *x, int n, char* op, ARMCI_Group *group) { do_gop(x, n, op, ARMCI_LONG, *group); } void armci_msg_group_llgop(long long *x, int n, char* op, ARMCI_Group *group) { do_gop(x, n, op, ARMCI_LONG_LONG, *group); } void armci_msg_group_fgop(float *x, int n, char* op, ARMCI_Group *group) { do_gop(x, n, op, ARMCI_FLOAT, *group); } void armci_msg_group_dgop(double *x, int n,char* op, ARMCI_Group *group) { do_gop(x, n, op, ARMCI_DOUBLE, *group); } void armci_exchange_address_grp(void *ptr_arr[], int n, ARMCI_Group *group) { comex_error("armci_exchange_address_grp not implemented", 1); #if 0 MPI_Datatype mpi_datatype; if (sizeof(void*) == sizeof(int)) { mpi_datatype = MPI_INT; } else if (sizeof(void*) == sizeof(long)) { mpi_datatype = MPI_LONG; } else if (sizeof(void*) == sizeof(long long)) { mpi_datatype = MPI_LONG_LONG; } else { comex_error("armci_exchange_address_grp bad size", 1); } MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, ptr_ar, n, mpi_datatype, get_comm(group)); #endif } void parmci_msg_group_barrier(ARMCI_Group *group) { comex_barrier(*group); MPI_Barrier(get_comm(group)); } void armci_msg_group_bcast_scope(int scope, void *buf, int len, int root, ARMCI_Group *group) { if (SCOPE_NODE == scope) { if (_number_of_procs_per_node > 1) { MPI_Comm comm; comex_group_comm(ARMCI_Node_group, &comm); MPI_Bcast(buf, len, MPI_BYTE, root, comm); } else { MPI_Bcast(buf, len, MPI_BYTE, root, MPI_COMM_SELF); } } else { int root_sub; int err; /* NOTE: this function is passed a root which has been translated back * to the world group while the group passed in is the sub * communicator group... what a mess */ MPI_Group group_world; MPI_Group group_sub; err = MPI_Comm_group(wc(), &group_world); assert(MPI_SUCCESS == err); err = MPI_Comm_group(get_comm(group), &group_sub); assert(MPI_SUCCESS == err); err = MPI_Group_translate_ranks(group_world, 1, &root, group_sub, &root_sub); comex_barrier(*group); err = MPI_Bcast(buf, len, MPI_BYTE, root_sub, get_comm(group)); assert(MPI_SUCCESS == err); } } void armci_grp_clus_brdcst(void *buf, int len, int grp_master, int grp_clus_nproc,ARMCI_Group *mastergroup) { comex_error("armci_grp_clus_brdcst not implemented", 1); } ga-5.9.2/comex/src-armci/message.h000066400000000000000000000064501500715745200167210ustar00rootroot00000000000000#ifndef _MESSAGE_H_ #define _MESSAGE_H_ #include "armci.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif #define ARMCI_INT -99 #define ARMCI_LONG -101 #define ARMCI_LONG_LONG -102 #define ARMCI_FLOAT -306 #define ARMCI_DOUBLE -307 #define SCOPE_ALL 333 #define SCOPE_NODE 337 #define SCOPE_MASTERS 339 #define armci_msg_sel(x,n,op,type,contribute)\ armci_msg_sel_scope(SCOPE_ALL,(x),(n),(op),(type),(contribute)) #if 0 #define armci_msg_bcast(buffer, len, root)\ armci_msg_bcast_scope(SCOPE_ALL, (buffer), (len), (root)) #else extern void armci_msg_bcast(void *buffer, int len, int root); #endif extern void armci_msg_sel_scope(int scope, void *x, int n, char* op, int type, int contribute); extern void armci_msg_bcast_scope(int scope, void* buffer, int len, int root); extern void armci_msg_brdcst(void* buffer, int len, int root); extern void armci_msg_snd(int tag, void* buffer, int len, int to); extern void armci_msg_rcv(int tag, void* buffer, int buflen, int *msglen, int from); extern int armci_msg_rcvany(int tag, void* buffer, int buflen, int *msglen); extern void armci_msg_reduce(void *x, int n, char *op, int type); extern void armci_msg_reduce_scope(int scope, void *x, int n, char *op, int type); extern void armci_msg_gop_scope(int scope, void *x, int n, char* op, int type); extern void armci_msg_igop(int *x, int n, char* op); extern void armci_msg_lgop(long *x, int n, char* op); extern void armci_msg_llgop(long long *x, int n, char* op); extern void armci_msg_fgop(float *x, int n, char* op); extern void armci_msg_dgop(double *x, int n, char* op); extern void armci_exchange_address(void *ptr_ar[], int n); extern void armci_msg_barrier(); extern void armci_msg_bintree(int scope, int* Root, int *Up, int *Left, int *Right); extern int armci_msg_me(); extern int armci_msg_nproc(); extern void armci_msg_abort(int code); extern void armci_msg_init(int *argc, char ***argv); extern void armci_msg_finalize(); extern double armci_timer(); extern void armci_msg_clus_brdcst(void *buf, int len); extern void armci_msg_clus_igop(int *x, int n, char* op); extern void armci_msg_clus_fgop(float *x, int n, char* op); extern void armci_msg_clus_lgop(long *x, int n, char* op); extern void armci_msg_clus_llgop(long long *x, int n, char* op); extern void armci_msg_clus_dgop(double *x, int n, char* op); extern void armci_msg_group_gop_scope(int scope, void *x, int n, char* op, int type, ARMCI_Group *group); extern void armci_msg_group_igop(int *x, int n, char* op,ARMCI_Group *group); extern void armci_msg_group_lgop(long *x, int n, char* op,ARMCI_Group *group); extern void armci_msg_group_llgop(long long *x, int n, char* op,ARMCI_Group *group); extern void armci_msg_group_fgop(float *x, int n, char* op,ARMCI_Group *group); extern void armci_msg_group_dgop(double *x, int n,char* op,ARMCI_Group *group); extern void armci_exchange_address_grp(void *ptr_arr[], int n, ARMCI_Group *group); extern void armci_msg_group_barrier(ARMCI_Group *group); extern void armci_msg_group_bcast_scope(int scope, void *buf, int len, int root, ARMCI_Group *group); extern void armci_grp_clus_brdcst(void *buf, int len, int grp_master, int grp_clus_nproc,ARMCI_Group *mastergroup); #if defined(__cplusplus) || defined(c_plusplus) } #endif #endif ga-5.9.2/comex/src-armci/parmci.h000066400000000000000000000113401500715745200165420ustar00rootroot00000000000000#ifndef _PARMCI_H_ #define _PARMCI_H_ #include "armci.h" extern int PARMCI_Acc(int optype, void *scale, void *src, void* dst, int bytes, int proc); extern int PARMCI_AccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc); extern int PARMCI_AccV(int op, void *scale, armci_giov_t * darr, int len, int proc); extern void PARMCI_AllFence(); extern void PARMCI_GroupFence(ARMCI_Group *group); extern void PARMCI_Barrier(); extern int PARMCI_Create_mutexes(int num); extern int PARMCI_Destroy_mutexes(); extern void PARMCI_Fence(int proc); extern void PARMCI_Finalize(); extern int PARMCI_Free_local(void *ptr); extern int PARMCI_Free(void *ptr); extern int PARMCI_Free_memdev(void *ptr); extern int PARMCI_GetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc); extern double PARMCI_GetValueDouble(void *src, int proc); extern float PARMCI_GetValueFloat(void *src, int proc); extern int PARMCI_GetValueInt(void *src, int proc); extern long PARMCI_GetValueLong(void *src, int proc); extern int PARMCI_GetV(armci_giov_t * darr, int len, int proc); extern int PARMCI_Get(void *src, void *dst, int bytes, int proc); extern int PARMCI_Init(); extern int PARMCI_Init_args(int *argc, char ***argv); extern int PARMCI_Init_mpi_comm(MPI_Comm comm); extern int PARMCI_Initialized(); extern void PARMCI_Lock(int mutex, int proc); extern void* PARMCI_Malloc_local(armci_size_t bytes); extern int PARMCI_Malloc(void **ptr_arr, armci_size_t bytes); extern int PARMCI_Malloc_memdev(void **ptr_arr, armci_size_t bytes, const char *device); extern void* PARMCI_Memat(armci_meminfo_t * meminfo, long offset); extern void PARMCI_Memctl(armci_meminfo_t *meminfo); extern void PARMCI_Memget(size_t bytes, armci_meminfo_t * meminfo, int memflg); extern int PARMCI_NbAccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbAccV(int op, void *scale, armci_giov_t * darr, int len, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbGetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbGetV(armci_giov_t * darr, int len, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbGet(void *src, void *dst, int bytes, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbPutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbPutValueDouble(double src, void *dst, int proc, armci_hdl_t* nb_handle); extern int PARMCI_NbPutValueFloat(float src, void *dst, int proc, armci_hdl_t* nb_handle); extern int PARMCI_NbPutValueInt(int src, void *dst, int proc, armci_hdl_t* nb_handle); extern int PARMCI_NbPutValueLong(long src, void *dst, int proc, armci_hdl_t* nb_handle); extern int PARMCI_NbPutV(armci_giov_t * darr, int len, int proc, armci_hdl_t * nb_handle); extern int PARMCI_NbPut(void *src, void *dst, int bytes, int proc, armci_hdl_t * nb_handle); extern int PARMCI_Put_flag(void *src, void *dst, int bytes, int *f, int v, int proc); extern int PARMCI_PutS_flag_dir(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc); extern int PARMCI_PutS_flag(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc); extern int PARMCI_PutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc); extern int PARMCI_PutValueDouble(double src, void *dst, int proc); extern int PARMCI_PutValueFloat(float src, void *dst, int proc); extern int PARMCI_PutValueInt(int src, void *dst, int proc); extern int PARMCI_PutValueLong(long src, void *dst, int proc); extern int PARMCI_PutV(armci_giov_t * darr, int len, int proc); extern int PARMCI_Put(void *src, void *dst, int bytes, int proc); extern int PARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc); extern int PARMCI_Test(armci_hdl_t * nb_handle); extern void PARMCI_Unlock(int mutex, int proc); extern int PARMCI_WaitAll(); extern int PARMCI_Wait(armci_hdl_t * nb_handle); extern int PARMCI_WaitProc(int proc); extern void parmci_msg_barrier(); extern void parmci_msg_group_barrier(ARMCI_Group *group); extern int parmci_notify(int proc); extern int parmci_notify_wait(int proc, int *pval); #endif /* _PARMCI_H_ */ ga-5.9.2/comex/src-common/000077500000000000000000000000001500715745200153145ustar00rootroot00000000000000ga-5.9.2/comex/src-common/acc.h000066400000000000000000000143161500715745200162200ustar00rootroot00000000000000#ifndef _COMEX_COMMON_ACC_H_ #define _COMEX_COMMON_ACC_H_ #include "comex.h" /* needed for complex accumulate */ typedef struct { double real; double imag; } DoubleComplex; typedef struct { float real; float imag; } SingleComplex; #if SIZEOF_INT == BLAS_SIZE #define BLAS_INT int #elif SIZEOF_LONG == BLAS_SIZE #define BLAS_INT long #elif SIZEOF_LONG_LONG == BLAS_SIZE #define BLAS_INT long long #endif #if HAVE_BLAS void sscal(const BLAS_INT *n, const float *a, const float *x, const BLAS_INT *incx); void dscal(const BLAS_INT *n, const double *a, const double *x, const BLAS_INT *incx); void BLAS_SAXPY(const BLAS_INT *n, const float *a, const float *x, const BLAS_INT *incx, float *y, const BLAS_INT *incy); void BLAS_DAXPY(const BLAS_INT *n, const double *a, const double *x, const BLAS_INT *incX, double *Y, const BLAS_INT *incy); void BLAS_CAXPY(const BLAS_INT *n, const void *a, const void *x, const BLAS_INT *incX, void *Y, const BLAS_INT *incy); void BLAS_ZAXPY(const BLAS_INT *n, const void *a, const void *x, const BLAS_INT *incX, void *Y, const BLAS_INT *incy); void BLAS_SCOPY(const BLAS_INT *n, const float *x, const BLAS_INT *incx, float *y, const BLAS_INT *incy); void BLAS_DCOPY(const BLAS_INT *n, const double *x, const BLAS_INT *incx, double *y, const BLAS_INT *incy); void BLAS_CCOPY(const BLAS_INT *n, const void *x, const BLAS_INT *incx, void *y, const BLAS_INT *incy); void BLAS_ZCOPY(const BLAS_INT *n, const void *x, const BLAS_INT *incx, void *y, const BLAS_INT *incy); #endif #define IADD_SCALE_REG(A,B,C) (A) += (B) * (C) #define IADD_SCALE_CPL(A,B,C) \ (A).real += ((B).real*(C).real) - ((B).imag*(C).imag);\ (A).imag += ((B).real*(C).imag) + ((B).imag*(C).real); #define MUL_REG(A,B,C) (A) = (B) * (C) #define MUL_CPL(A,B,C) \ (A).real = ((B).real*(C).real) - ((B).imag*(C).imag);\ (A).imag = ((B).real*(C).imag) + ((B).imag*(C).real); static inline void _scale( const int op, const int bytes, void * const restrict dst, const void * const restrict src, const void * const restrict scale) { #define SCALE_BLAS(COMEX_TYPE, C_TYPE, LETTER) \ if (op == COMEX_TYPE) { \ const BLAS_INT ONE = 1; \ const BLAS_INT N = bytes/sizeof(C_TYPE); \ BLAS_##LETTER##COPY(&N, src, &ONE, dst, &ONE); \ BLAS_##LETTER##AXPY(&N, scale, src, &ONE, dst, &ONE); \ } else #define SCALE(WHICH, COMEX_TYPE, C_TYPE) \ if (op == COMEX_TYPE) { \ int m; \ const int m_lim = bytes/sizeof(C_TYPE); \ C_TYPE * const restrict iterator = (C_TYPE * const restrict )dst; \ const C_TYPE * const restrict value = (const C_TYPE * const restrict)src;\ const C_TYPE calc_scale = *(const C_TYPE * const restrict )scale; \ for (m = 0 ; m < m_lim; ++m) { \ MUL_##WHICH(iterator[m], value[m], calc_scale); \ } \ } else #if 0 // HAVE_BLAS SCALE_BLAS(COMEX_ACC_DBL, double, D) SCALE_BLAS(COMEX_ACC_FLT, float, S) SCALE(REG, COMEX_ACC_INT, int) SCALE(REG, COMEX_ACC_LNG, long) SCALE_BLAS(COMEX_ACC_DCP, DoubleComplex, Z) SCALE_BLAS(COMEX_ACC_CPL, SingleComplex, C) #else SCALE(REG, COMEX_ACC_DBL, double) SCALE(REG, COMEX_ACC_FLT, float) SCALE(REG, COMEX_ACC_INT, int) SCALE(REG, COMEX_ACC_LNG, long) SCALE(CPL, COMEX_ACC_DCP, DoubleComplex) SCALE(CPL, COMEX_ACC_CPL, SingleComplex) #endif { #ifdef COMEX_ASSERT COMEX_ASSERT(0); #else assert(0); #endif } #undef SCALE_BLAS #undef SCALE } static inline void _acc( const int op, const int bytes, void * const restrict dst, const void * const restrict src, const void * const restrict scale) { #define ACC_BLAS(COMEX_TYPE, C_TYPE, LETTER) \ if (op == COMEX_TYPE) { \ const BLAS_INT ONE = 1; \ const BLAS_INT N = bytes/sizeof(C_TYPE); \ BLAS_##LETTER##AXPY(&N, scale, src, &ONE, dst, &ONE); \ } else #define ACC(WHICH, COMEX_TYPE, C_TYPE) \ if (op == COMEX_TYPE) { \ int m; \ const int m_lim = bytes/sizeof(C_TYPE); \ C_TYPE * const restrict iterator = (C_TYPE * const restrict)dst; \ const C_TYPE * const restrict value = (const C_TYPE * const restrict)src;\ const C_TYPE calc_scale = *(const C_TYPE * const restrict)scale; \ for (m = 0 ; m < m_lim; ++m) { \ IADD_SCALE_##WHICH(iterator[m], value[m], calc_scale); \ } \ } else #if HAVE_BLAS ACC_BLAS(COMEX_ACC_DBL, double, D) ACC_BLAS(COMEX_ACC_FLT, float, S) ACC(REG, COMEX_ACC_INT, int) ACC(REG, COMEX_ACC_LNG, long) ACC_BLAS(COMEX_ACC_DCP, DoubleComplex, Z) ACC_BLAS(COMEX_ACC_CPL, SingleComplex, C) #else ACC(REG, COMEX_ACC_DBL, double) ACC(REG, COMEX_ACC_FLT, float) ACC(REG, COMEX_ACC_INT, int) ACC(REG, COMEX_ACC_LNG, long) ACC(CPL, COMEX_ACC_DCP, DoubleComplex) ACC(CPL, COMEX_ACC_CPL, SingleComplex) #endif { #ifdef COMEX_ASSERT COMEX_ASSERT(0); #else assert(0); #endif } #undef ACC_BLAS #undef ACC } #undef IADD_SCALE_REG #undef IADD_SCALE_CPL #undef MUL_REG #undef MUL_CPL #undef BLAS_INT #endif /* _COMEX_COMMON_ACC_H_ */ ga-5.9.2/comex/src-common/comex.h000066400000000000000000000535061500715745200166110ustar00rootroot00000000000000/* comex header file */ #ifndef _COMEX_H #define _COMEX_H #include #include #if defined(__cplusplus) || defined(c_plusplus) extern "c" { #endif typedef struct { void **src; /**< array of source starting addresses */ void **dst; /**< array of destination starting addresses */ int count; /**< size of address arrays (src[count],dst[count]) */ int bytes; /**< length in bytes for each src[i]/dst[i] pair */ } comex_giov_t; typedef int comex_request_t; typedef int comex_group_t; #define COMEX_GROUP_WORLD 0 #define COMEX_GROUP_NULL -1 #define COMEX_SUCCESS 0 #define COMEX_FAILURE 1 #define COMEX_SWAP 10 #define COMEX_SWAP_LONG 11 #define COMEX_FETCH_AND_ADD 12 #define COMEX_FETCH_AND_ADD_LONG 13 #define COMEX_ACC_OFF 36 #define COMEX_ACC_INT (COMEX_ACC_OFF + 1) #define COMEX_ACC_DBL (COMEX_ACC_OFF + 2) #define COMEX_ACC_FLT (COMEX_ACC_OFF + 3) #define COMEX_ACC_CPL (COMEX_ACC_OFF + 4) #define COMEX_ACC_DCP (COMEX_ACC_OFF + 5) #define COMEX_ACC_LNG (COMEX_ACC_OFF + 6) #define COMEX_MAX_STRIDE_LEVEL 8 /** * Initialize comex. * * @return COMEX_SUCCESS on sucess */ extern int comex_init(); /** * Initialize comex with command line arguments. * * @return COMEX_SUCCESS on sucess */ extern int comex_init_args(int *argc, char ***argv); /** * Initialize comex with an externally supplied communicator. * * @param[in] comm external communicator. * @return COMEX_SUCCESS on sucess */ extern int comex_init_comm(MPI_Comm); /** * Test whether comex has been initialized. * * @return COMEX_SUCCESS if comex has been initialized * COMEX_FAILURE if comex has not */ extern int comex_initialized(); /** * Terminate comex and clean up resources. * * @return COMEX_SUCCESS on sucess */ extern int comex_finalize(); /** * Abort comex, printing the msg, and exiting with code. * * @param[in] msg the message to print * @param[in] code the code to exit with */ extern void comex_error(const char *msg, int code); /** * Create a new group from the given group and process ID list. * * The rank list selects the ranks from the given group to become members of * the new group. The ranks should be nonnegative and range from zero to the * size of the given group. * * This functions is collective only over the ranks within the rank list and * not over the entire original group. * * @param[in] n the number of ranks to select for the new group * @param[in] rank_list the list of ranks to select for the new group * @param[in] group the group to subset for the new group * @param[out] new_group the newly created group * @return COMEX_SUCCESS on success * COMEX_FAILURE if a rank in the rank list is out of bounds */ extern int comex_group_create( int n, int *pid_list, comex_group_t group, comex_group_t *new_group); /** * Marks the group for deallocation. * * @param[in] group group to be destroyed * @return COMEX_SUCCESS on sucess */ extern int comex_group_free(comex_group_t group); /** * Determines the rank of the calling process in the given group. * * @param[in] group group handle * @param[out] rank rank of the calling process in the group * @return COMEX_SUCCESS on sucess */ extern int comex_group_rank(comex_group_t group, int *rank); /** * Determines the size of the given group. * * @param[in] group group handle * @param[out] size number of processes in the group * @return COMEX_SUCCESS on sucess */ extern int comex_group_size(comex_group_t group, int *size); /** * Returns the MPI_Comm object backing the given group. * * The actual MPI_Comm object is returned, therefore do not call * MPI_Comm_free() on the returned communicator. This function is for * convenience to be able to MPI_Comm_dup() the returned MPI_Comm instance. * * @param[in] group group handle * @param[out] comm the communicator handle * @return COMEX_SUCCESS on sucess */ extern int comex_group_comm(comex_group_t group, MPI_Comm *comm); /** * Translates the ranks of processes in one group to those in another group. * * @param[in] n the number of ranks in the ranks_from and ranks_to arrays * @param[in] group_from the group to translate ranks from * @param[in] ranks_from array of zer or more valid ranks in group_from * @param[in] group_to the group to translate ranks to * @param[out] ranks_to array of corresponding ranks in group_to * @return COMEX_SUCCESS on sucess */ extern int comex_group_translate_ranks(int n, comex_group_t group_from, int *ranks_from, comex_group_t group_to, int *ranks_to); /** * Translate the given rank from its group to its corresponding rank in the * world group. * * Shorthand notation for common case. * * @param[in] group the group to translate from * @param[in] group_rank the rank to translate from * @param[out] world_rank the corresponding world rank * @return COMEX_SUCCESS on sucess */ extern int comex_group_translate_world( comex_group_t group, int group_rank, int *world_rank); /** * A collective communication and operations barrier. * * Ensures all comex communication has completed prior to performing the * operations barrier. * * @param[in] group the group to perform the collective barrier over * @return COMEX_SUCCESS on sucess */ extern int comex_barrier(comex_group_t group); /** * Contiguous Put. * * @param[in] src pointer to 1st segment at source * @param[in] dst pointer to 1st segment at destination * @param[in] bytes number of bytes to transfer * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @return COMEX_SUCCESS on sucess */ extern int comex_put( void *src, void *dst, int bytes, int proc, comex_group_t group); /** * Strided Put. * * @param[in] src pointer to 1st segment at source * @param[in] src_stride array of strides at source * @param[in] dst pointer to 1st segment at destination * @param[in] dst_stride array of strides at destination * @param[in] count number of units at each stride level count[0]=bytes * @param[in] stride_levels number of stride levels * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @return COMEX_SUCCESS on success */ extern int comex_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group); /** * Vector Put. * * @param[in] darr descriptor array * @param[in] len length of descriptor array * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @return COMEX_SUCCESS on success */ extern int comex_putv( comex_giov_t *darr, int len, int proc, comex_group_t group); /** * Nonblocking Contiguous Put. * * @param[in] src pointer to 1st segment at source * @param[in] dst pointer to 1st segment at destination * @param[in] bytes number of bytes to transfer * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @param[out] nb_handle nonblocking request object * @return COMEX_SUCCESS on sucess */ extern int comex_nbput( void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t* nb_handle); /** * Nonblocking Strided Put. * * @param[in] src pointer to 1st segment at source * @param[in] src_stride array of strides at source * @param[in] dst pointer to 1st segment at destination * @param[in] dst_stride array of strides at destination * @param[in] count number of units at each stride level count[0]=bytes * @param[in] stride_levels number of stride levels * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @param[out] nb_handle nonblocking request object * @return COMEX_SUCCESS on success */ extern int comex_nbputs( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t* nb_handle); /** * Nonblocking Vector Put. * * @param[in] darr descriptor array * @param[in] len length of descriptor array * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @param[out] nb_handle nonblocking request object * @return COMEX_SUCCESS on success */ extern int comex_nbputv( comex_giov_t *darr, int len, int proc, comex_group_t group, comex_request_t* nb_handle); /** * Contiguous Atomic Accumulate. * * @param[in] op operation * @param[in] scale factor x += scale*y * @param[in] src pointer to 1st segment at source * @param[in] dst pointer to 1st segment at destination * @param[in] bytes number of bytes to transfer * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @return COMEX_SUCCESS on success */ extern int comex_acc( int op, void *scale, void *src, void *dst, int bytes, int proc, comex_group_t group); /** * Strided Atomic Accumulate. * * @param[in] op operation * @param[in] scale factor x += scale*y * @param[in] src pointer to 1st segment at source * @param[in] src_stride [stride_levels] array of strides at source * @param[in] dst pointer to 1st segment at destination * @param[in] dst_stride [stride_levels] array of strides at destination * @param[in] count [stride_levels+1] number of units at each stride level * count[0]=bytes * @param[in] stride_levels number of stride levels * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @return COMEX_SUCCESS on success */ extern int comex_accs( int op, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group); /** * Vector Atomic Accumulate. * * @param[in] op operation * @param[in] scale factor x += scale*y * @param[in] darr descriptor array * @param[in] len length of descriptor array * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @return COMEX_SUCCESS on success */ extern int comex_accv( int op, void *scale, comex_giov_t *darr, int len, int proc, comex_group_t group); /** * Nonblocking Contiguous Atomic Accumulate. * * @param[in] op operation * @param[in] scale factor x += scale*y * @param[in] src pointer to 1st segment at source * @param[in] dst pointer to 1st segment at destination * @param[in] bytes number of bytes to transfer * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @param[out] nb_handle nonblocking request object * @return COMEX_SUCCESS on success */ extern int comex_nbacc( int op, void *scale, void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *nb_handle); /** * Strided Atomic Accumulate. * * @param[in] op operation * @param[in] scale factor x += scale*y * @param[in] src pointer to 1st segment at source * @param[in] src_stride array of strides at source * @param[in] dst pointer to 1st segment at destination * @param[in] dst_stride array of strides at destination * @param[in] count number of units at each stride level count[0]=bytes * @param[in] stride_levels number of stride levels * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @param[out] nb_handle nonblocking request object * @return COMEX_SUCCESS on success */ extern int comex_nbaccs( int op, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t* nb_handle); /** * Vector Atomic Accumulate. * * @param[in] op operation * @param[in] scale factor x += scale*y * @param[in] darr descriptor array * @param[in] len length of descriptor array * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @param[out] nb_handle nonblocking request object * @return COMEX_SUCCESS on success */ extern int comex_nbaccv( int op, void *scale, comex_giov_t *darr, int len, int proc, comex_group_t group, comex_request_t* nb_handle); /** * Contiguous Get. * * @param[in] src pointer to 1st segment at source * @param[in] dst pointer to 1st segment at destination * @param[in] bytes number of bytes to transfer * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @return COMEX_SUCCESS on sucess */ extern int comex_get( void *src, void *dst, int bytes, int proc, comex_group_t group); /** * Strided Get. * * @param[in] src pointer to 1st segment at source * @param[in] src_stride array of strides at source * @param[in] dst pointer to 1st segment at destination * @param[in] dst_stride array of strides at destination * @param[in] count number of units at each stride level count[0]=bytes * @param[in] stride_levels number of stride levels * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @return COMEX_SUCCESS on success */ extern int comex_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group); /** * Vector Get. * * @param[in] darr descriptor array * @param[in] len length of descriptor array * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @return COMEX_SUCCESS on success */ extern int comex_getv( comex_giov_t *darr, int len, int proc, comex_group_t group); /** * Nonblocking Contiguous Get. * * @param[in] src pointer to 1st segment at source * @param[in] dst pointer to 1st segment at destination * @param[in] bytes number of bytes to transfer * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @param[out] nb_handle nonblocking request object * @return COMEX_SUCCESS on sucess */ extern int comex_nbget( void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t* nb_handle); /** * Nonblocking Strided Get. * * @param[in] src pointer to 1st segment at source * @param[in] src_stride array of strides at source * @param[in] dst pointer to 1st segment at destination * @param[in] dst_stride array of strides at destination * @param[in] count number of units at each stride level count[0]=bytes * @param[in] stride_levels number of stride levels * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @param[out] nb_handle nonblocking request object * @return COMEX_SUCCESS on success */ extern int comex_nbgets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *nb_handler); /** * Nonblocking Vector Get. * * @param[in] darr descriptor array * @param[in] len length of descriptor array * @param[in] proc remote process(or) id * @param[in] group the calling process and remote process must belong to the * same group * @param[out] nb_handle nonblocking request object * @return COMEX_SUCCESS on success */ extern int comex_nbgetv( comex_giov_t *darr, int len, int proc, comex_group_t group, comex_request_t* nb_handle); /** * Collective allocation of registered memory and exchange of addresses. * * @param[out] ptr_arr array of memory addresses * w.r.t. each process's address space * @param[in] bytes how many bytes to allocate locally * @param[in] group the group to which the calling process belongs * @return COMEX_SUCCESS on success */ extern int comex_malloc( void **ptr_arr, size_t bytes, comex_group_t group); /** * Collective allocation of registered memory and exchange of addresses on * a specified memory device. * * @param[out] ptr_arr array of memory addresses * w.r.t. each process's address space * @param[in] bytes how many bytes to allocate locally * @param[in] group the group to which the calling process belongs * @param[in] device character string describing memory device * @return COMEX_SUCCESS on success */ extern int comex_malloc_mem_dev( void **ptr_arr, size_t bytes, comex_group_t group, const char *device); /** * Collective free of memory given the original local pointer. * * @param[in] ptr the original local memory allocated using comex_malloc * @param[in] group the group to which the calling process belongs * @return COMEX_SUCCESS on success */ extern int comex_free(void *ptr, comex_group_t group); /** * Collective free of memory on a specified device given the original local pointer. * * @param[in] ptr the original local memory allocated using comex_malloc * @param[in] group the group to which the calling process belongs * @return COMEX_SUCCESS on success */ extern int comex_free_dev(void *ptr, comex_group_t group); /** * Local (noncollective) allocation of registered memory. * * Using memory allocated here may have performance benefits when used as a * communication buffer. * * @param[in] bytes how many bytes to allocate locally * @return COMEX_SUCCESS on success */ extern void* comex_malloc_local(size_t bytes); /** * Local (noncollective) free of memory allocated by comex_malloc_local. * * @param[in] the original local memory allocated using comex_malloc_local * @return COMEX_SUCCESS on success */ extern int comex_free_local(void *ptr); /** * Flush all outgoing messages from me to the given proc. * * @param[in] proc the proc with which to flush outgoing messages * @return COMEX_SUCCESS on success */ extern int comex_fence_proc(int proc, comex_group_t group); /** * Flush all outgoing messages to all procs. * * @return COMEX_SUCCESS on success */ extern int comex_fence_all(comex_group_t group); /** * Collectively create num locks locally. * * Remote procs may create a different number of locks, including zero. * * This function is always collective on the world group. * * @param[in] num number of locks to create locally * @return COMEX_SUCCESS on success */ extern int comex_create_mutexes(int num); /** * Collectively destroy all previously created locks. * * This function is always collective on the world group. * * @param[in] num number of locks to create locally * @return COMEX_SUCCESS on success */ extern int comex_destroy_mutexes(); /** * Lock the given mutex on the given proc. * * This function is always on the world group. * * @param[in] mutex the ID of the mutex to lock on proc * @param[in] the ID of the proc which owns the mutex * * @return COMEX_SUCCESS on success * COMEX_FAILURE if given mutex or proc is out of range */ extern int comex_lock(int mutex, int proc); /** * Unlock the given mutex on the given proc. * * This function is always on the world group. * * @param[in] mutex the ID of the mutex to unlock on proc * @param[in] the ID of the proc which owns the mutex * * @return COMEX_SUCCESS on success * COMEX_FAILURE if given mutex or proc is out of range */ extern int comex_unlock(int mutex, int proc); /** * Read-modify-write atomic operation. * * The operations may be one of * - COMEX_SWAP * - COMEX_SWAP_LONG * - COMEX_FETCH_AND_ADD * - COMEX_FETCH_AND_ADD_LONG * * For the swap operations, the extra parameter is not used. The values of the * ploc and prem locations are swapped. * * For the fetch and add operations, the extra parameter is also used to * indicate how much to increment the remote value. The original remove value * is returned in the ploc parameter. * * @param[in] op the operation to perform (see list above) * @param[in] ploc the value to update locally * @param[in] prem the value to update remotely * @param[in] extra for COMEX_FETCH_AND_ADD and COMEX_FETCH_AND_ADD_LONG, the * amount to increment the remote value by * @param[in] proc remote process(or) id * @param[in] group group handle * @return COMEX_SUCCESS on sucess */ extern int comex_rmw( int op, void *ploc, void *prem, int extra, int proc, comex_group_t group); /** * Waits for completion of non-blocking comex operations with explicit handles. * * @param[in] nb_handle the handle * @return COMEX_SUCCESS on sucess */ extern int comex_wait(comex_request_t *nb_handle); /** * Checks completion status of non-blocking comex operations with explicit * handles. * * @param[in] nb_handle the handle * @param[out] status 0-completed, 1-in progress * @return COMEX_SUCCESS on sucess */ extern int comex_test(comex_request_t *nb_handle, int *status); /** * Wait for all outstanding implicit non-blocking operations to finish. * * @param[in] group group handle * @return COMEX_SUCCESS on sucess */ extern int comex_wait_all(comex_group_t group); /** * Wait for all outstanding implicit non-blocking operations to a particular * process to finish. * * @param[in] proc proc for which all the outstanding non-blocking operations * have to be completed * @param[in] group group handle * @return COMEX_SUCCESS on sucess */ extern int comex_wait_proc(int proc, comex_group_t group); #if defined(__cplusplus) || defined(c_plusplus) } #endif #endif /* _COMEX_H */ ga-5.9.2/comex/src-mpi-mt/000077500000000000000000000000001500715745200152275ustar00rootroot00000000000000ga-5.9.2/comex/src-mpi-mt/Makefile.inc000066400000000000000000000003321500715745200174350ustar00rootroot00000000000000libcomex_la_SOURCES += src-mpi-mt/comex.c libcomex_la_SOURCES += src-mpi-mt/comex_impl.h libcomex_la_SOURCES += src-mpi-mt/groups.c libcomex_la_SOURCES += src-mpi-mt/groups.h AM_CPPFLAGS += -I$(top_srcdir)/src-mpi-mt ga-5.9.2/comex/src-mpi-mt/NOTES.md000066400000000000000000000046011500715745200164420ustar00rootroot00000000000000# MPI Multi-Threaded (MPI-MT) These are notes describing the MPI multi-threaded runtime. These notes are intended to help developers navigate the contents of these files and to locate specific functionality. The MPI-MT is intended to be high-performing but experience tells us that it is not. It relies on the MPI-2 standard to allow `MPI_Init_thread` with a threading level of `MPI_THREAD_MULTIPLE`. Every MPI rank creates a thread that simply calls the data server progress engine indefinitely. The progress thread blocks in an `MPI_Recv` with `MPI_ANY_SOURCE` and handles incoming requests to the data server. User-level requests, e.g., `comex_put`, make progress at the initiator by internally waiting for an available non-blocking handle. By default, all functions call an internal non-blocking interface. The blocking calls, e.g., `comex_get`, internally call a non-blocking get followed by a blocking wait. The user-level non-blocking handles are also flushed any time a `comex_barrier` is called, since it enforces a communication fence followed by a barrier. There are a finite number of user-level non-blocking handles. This is set using the environment variable `COMEX_MAX_NB_OUTSTANDING`. This controls the size of an allocated array of our non-blocking handle data structure `nb_t`. The `nb_t` structure contains linked lists of `MPI_Request` objects associated with the given user-level handle. It is slightly more complicated than that since get requests might be using the packing optimization where the request is first compressed into a contiguous buffer. The stride information is kept with the `nb_t` message so that the received buffer can be unpacked. All memory is freed when operations complete. Incoming requests are all based on the active message concept. A 'header' message is sent first to the progress engine indicating the type of request, e.g., `OP_PUT`, `OP_ACC_INT`. The header contains enough information to complete the request such as source and destination pointers, source and destination ranks, etc. After a header message is sent, any data payload is sent as a separate message. The intent was to let MPI directly use the buffer pointers in case the buffers were allocated using any special, network-specific allocator. Otherwise, a data payload could have been mem-copied to the end of the header message (this is done in the MPI-PR implementation as an optimization). ga-5.9.2/comex/src-mpi-mt/comex.c000066400000000000000000003461751500715745200165260ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* C and/or system headers */ #include #include #include #include #include #include #include #include #include #include /* 3rd party headers */ #include /* our headers */ #include "comex.h" #include "comex_impl.h" #include "groups.h" #include "acc.h" #define PAUSE_ON_ERROR 0 #define STATIC static inline /* data structures */ typedef enum { OP_PUT = 0, OP_PUT_PACKED, OP_PUT_IOV, OP_GET, OP_GET_PACKED, OP_GET_IOV, OP_ACC_INT, OP_ACC_DBL, OP_ACC_FLT, OP_ACC_CPL, OP_ACC_DCP, OP_ACC_LNG, OP_ACC_INT_PACKED, OP_ACC_DBL_PACKED, OP_ACC_FLT_PACKED, OP_ACC_CPL_PACKED, OP_ACC_DCP_PACKED, OP_ACC_LNG_PACKED, OP_ACC_INT_IOV, OP_ACC_DBL_IOV, OP_ACC_FLT_IOV, OP_ACC_CPL_IOV, OP_ACC_DCP_IOV, OP_ACC_LNG_IOV, OP_FENCE, OP_FETCH_AND_ADD, OP_SWAP, OP_CREATE_MUTEXES, OP_DESTROY_MUTEXES, OP_LOCK, OP_UNLOCK, OP_QUIT, } op_t; typedef struct { op_t operation; void *remote_address; void *local_address; int rank; /**< rank of target (rank of sender is iprobe_status.MPI_SOURCE */ int length; /**< length of message/payload not including header */ } header_t; /* keep track of all mutex requests */ typedef struct lock_link { struct lock_link *next; int rank; } comex_lock_t; typedef struct { void *ptr; int stride_levels; int stride[COMEX_MAX_STRIDE_LEVEL]; int count[COMEX_MAX_STRIDE_LEVEL+1]; } stride_t; typedef struct message_link { struct message_link *next; void *message; MPI_Request request; int need_free; stride_t *stride; comex_giov_t *iov; } message_t; typedef struct { int in_use; int send_size; message_t *send_head; message_t *send_tail; int recv_size; message_t *recv_head; message_t *recv_tail; } nb_t; /* static state */ static int *num_mutexes = NULL; /**< (all) how many mutexes on each process */ static int **mutexes = NULL; /**< (masters) value is rank of lock holder */ static comex_lock_t ***lq_heads = NULL; /**< array of lock queues */ static int initialized = 0; /* for comex_initialized(), 0=false */ static char *fence_array = NULL; static pthread_t progress_thread; static pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; static nb_t *nb_state = NULL; /* keep track of all nonblocking operations */ static int nb_max_outstanding = COMEX_MAX_NB_OUTSTANDING; static int nb_index = 0; static int nb_count_event = 0; static int nb_count_event_processed = 0; static int nb_count_send = 0; static int nb_count_send_processed = 0; static int nb_count_recv = 0; static int nb_count_recv_processed = 0; static char *static_acc_buffer = NULL; #if PAUSE_ON_ERROR static int AR_caught_sig=0; static int AR_caught_sigsegv=0; void (*SigSegvOrig)(int); void SigSegvHandler(int sig) { char name[256]; AR_caught_sig= sig; AR_caught_sigsegv=1; if (-1 == gethostname(name, 256)) { perror("gethostname"); comex_error("gethostname failed", errno); } fprintf(stderr,"%d(%s:%d): Segmentation Violation ... pausing\n", g_state.rank, name, getpid()); pause(); comex_error("Segmentation Violation error, status=",(int) sig); } #endif /* static function declarations */ /* error checking */ #define CHECK_MPI_RETVAL(retval) check_mpi_retval((retval), __FILE__, __LINE__) STATIC void check_mpi_retval(int retval, const char *file, int line); STATIC const char *str_mpi_retval(int retval); /* server fuctions */ STATIC void server_send(void *buf, int count, int dest); STATIC void server_recv(void *buf, int count, int source); STATIC void* _progress_server(void *arg); STATIC void _put_handler(header_t *header, int proc); STATIC void _put_packed_handler(header_t *header, int proc); STATIC void _put_iov_handler(header_t *header, int proc); STATIC void _get_handler(header_t *header, int proc); STATIC void _get_packed_handler(header_t *header, int proc); STATIC void _get_iov_handler(header_t *header, int proc); STATIC void _acc_handler(header_t *header, int proc); STATIC void _acc_packed_handler(header_t *header, int proc); STATIC void _acc_iov_handler(header_t *header, int proc); STATIC void _fence_handler(header_t *header, int proc); STATIC void _fetch_and_add_handler(header_t *header, int proc); STATIC void _swap_handler(header_t *header, int proc); STATIC void _mutex_create_handler(header_t *header, int proc); STATIC void _mutex_destroy_handler(header_t *header, int proc); STATIC void _lock_handler(header_t *header, int proc); STATIC void _unlock_handler(header_t *header, int proc); /* worker functions */ STATIC void nb_send_common(void *buf, int count, int dest, nb_t *nb, int need_free); STATIC void nb_send_header(void *buf, int count, int dest, nb_t *nb); STATIC void nb_send_buffer(void *buf, int count, int dest, nb_t *nb); STATIC void nb_recv_packed(void *buf, int count, int source, nb_t *nb, stride_t *stride); STATIC void nb_recv_iov(void *buf, int count, int source, nb_t *nb, comex_giov_t *iov); STATIC void nb_recv(void *buf, int count, int source, nb_t *nb); STATIC int nb_get_handle_index(); STATIC nb_t* nb_wait_for_handle(); STATIC void nb_wait_for_send(nb_t *nb); STATIC void nb_wait_for_send1(nb_t *nb); STATIC void nb_wait_for_recv(nb_t *nb); STATIC void nb_wait_for_recv1(nb_t *nb); STATIC void nb_wait_for_all(nb_t *nb); STATIC void nb_wait_all(); STATIC void nb_put(void *src, void *dst, int bytes, int proc, nb_t *nb); STATIC void nb_get(void *src, void *dst, int bytes, int proc, nb_t *nb); STATIC void nb_acc(int datatype, void *scale, void *src, void *dst, int bytes, int proc, nb_t *nb); STATIC void nb_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_puts_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_gets_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_accs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_accs_packed( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_putv(comex_giov_t *iov, int iov_len, int proc, nb_t *nb); STATIC void nb_putv_packed(comex_giov_t *iov, int proc, nb_t *nb); STATIC void nb_getv(comex_giov_t *iov, int iov_len, int proc, nb_t *nb); STATIC void nb_getv_packed(comex_giov_t *iov, int proc, nb_t *nb); STATIC void nb_accv(int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, nb_t *nb); STATIC void nb_accv_packed(int datatype, void *scale, comex_giov_t *iov, int proc, nb_t *nb); /* other functions */ STATIC int packed_size(int *src_stride, int *count, int stride_levels); STATIC char* pack(char *src, int *src_stride, int *count, int stride_levels, int *size); STATIC void unpack(char *packed_buffer, char *dst, int *dst_stride, int *count, int stride_levels); STATIC int _get_world_rank(comex_igroup_t *igroup, int rank); STATIC int* _get_world_ranks(comex_igroup_t *igroup); STATIC int _set_affinity(int cpu); STATIC size_t get_scale_size(op_t operation); STATIC int get_acc_type(op_t operation); STATIC op_t get_op_type(int acc_type); STATIC void print_scale(op_t operation, void *scale); int _comex_init(MPI_Comm comm) { int status = 0; int init_flag = 0; int i = 0; int mpi_thread_level_provided; if (initialized) { return 0; } initialized = 1; /* Assert MPI has been initialized */ status = MPI_Initialized(&init_flag); CHECK_MPI_RETVAL(status); assert(init_flag); /* Assert MPI has been initialized with proper threading level */ status = MPI_Query_thread(&mpi_thread_level_provided); CHECK_MPI_RETVAL(status); COMEX_ASSERT(MPI_THREAD_MULTIPLE == mpi_thread_level_provided); /* env vars */ { char *value = NULL; nb_max_outstanding = COMEX_MAX_NB_OUTSTANDING; /* default */ if ((value = getenv("COMEX_MAX_NB_OUTSTANDING")) != NULL) { nb_max_outstanding = atoi(value); } COMEX_ASSERT(nb_max_outstanding > 0); } /* groups */ comex_group_init(comm); /* mutexes */ mutexes = NULL; num_mutexes = NULL; lq_heads = NULL; /* nonblocking message queues */ nb_state = (nb_t*)malloc(sizeof(nb_t) * nb_max_outstanding); COMEX_ASSERT(nb_state); for (i = 0; i < nb_max_outstanding; ++i) { nb_state[i].in_use = 0; nb_state[i].send_size = 0; nb_state[i].send_head = NULL; nb_state[i].send_tail = NULL; nb_state[i].recv_size = 0; nb_state[i].recv_head = NULL; nb_state[i].recv_tail = NULL; } nb_index = 0; nb_count_event = 0; nb_count_event_processed = 0; nb_count_send = 0; nb_count_send_processed = 0; nb_count_recv = 0; nb_count_recv_processed = 0; #if DEBUG printf("[%d] comex_init() before progress server\n", g_state.rank); #endif /* Synch - Sanity Check */ /* This barrier is on the world worker group */ MPI_Barrier(group_list->comm); status = _set_affinity(g_state.node_rank); COMEX_ASSERT(0 == status); /* TODO: wasteful O(p) storage... */ mutexes = (int**)malloc(sizeof(int*) * g_state.size); COMEX_ASSERT(mutexes); /* create one lock queue for each proc for each mutex */ lq_heads = (comex_lock_t***)malloc(sizeof(comex_lock_t**) * g_state.size); COMEX_ASSERT(lq_heads); /* start the server (each rank does this) */ pthread_create(&progress_thread, NULL, _progress_server, NULL); /* static state */ fence_array = malloc(sizeof(char) * g_state.size); COMEX_ASSERT(fence_array); for (i = 0; i < g_state.size; ++i) { fence_array[i] = 0; } #if DEBUG printf("[%d] comex_init() before barrier\n", g_state.rank); #endif #if PAUSE_ON_ERROR if ((SigSegvOrig=signal(SIGSEGV, SigSegvHandler)) == SIG_ERR) { comex_error("signal(SIGSEGV, ...) error", -1); } #endif /* Synch - Sanity Check */ /* This barrier is on the world worker group */ MPI_Barrier(group_list->comm); #if DEBUG printf("[%d] comex_init() success\n", g_state.rank); #endif return COMEX_SUCCESS; } int comex_init() { return _comex_init(MPI_COMM_WORLD); } int comex_init_comm(MPI_Comm comm) { return _comex_init(comm); } int comex_init_args(int *argc, char ***argv) { int init_flag; int status; status = MPI_Initialized(&init_flag); CHECK_MPI_RETVAL(status); if(!init_flag) { int level; status = MPI_Init_thread(argc, argv, MPI_THREAD_MULTIPLE, &level); CHECK_MPI_RETVAL(status); COMEX_ASSERT(MPI_THREAD_MULTIPLE == level); } return comex_init(); } int comex_initialized() { #if DEBUG if (initialized) { printf("[%d] comex_initialized()\n", g_state.rank); } #endif return initialized; } int comex_finalize() { #if DEBUG printf("[%d] comex_finalize()\n", g_state.rank); #endif /* it's okay to call multiple times -- extra calls are no-ops */ if (!initialized) { return COMEX_SUCCESS; } comex_barrier(COMEX_GROUP_WORLD); initialized = 0; /* Make sure that all outstanding operations are done */ comex_wait_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); /* send quit message to thread */ { int my_master = g_state.rank; header_t *header = NULL; nb_t *nb = NULL; nb = nb_wait_for_handle(); header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = OP_QUIT; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = 0; nb_send_header(header, sizeof(header_t), my_master, nb); nb_wait_for_all(nb); pthread_join(progress_thread, NULL); } free(fence_array); MPI_Barrier(g_state.comm); /* destroy the groups */ #if DEBUG printf("[%d] before comex_group_finalize()\n", g_state.rank); #endif comex_group_finalize(); #if DEBUG printf("[%d] after comex_group_finalize()\n", g_state.rank); #endif #if DEBUG_TO_FILE fclose(comex_trace_file); #endif return COMEX_SUCCESS; } void comex_error(const char *msg, int code) { #if DEBUG printf("[%d] Received an Error in Communication: (%d) %s\n", g_state.rank, code, msg); #if DEBUG_TO_FILE fclose(comex_trace_file); #endif #endif fprintf(stderr,"[%d] Received an Error in Communication: (%d) %s\n", g_state.rank, code, msg); MPI_Abort(g_state.comm, code); } int comex_put( void *src, void *dst, int bytes, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; #if DEBUG printf("[%d] comex_put(src=%p, dst=%p, bytes=%d, proc=%d, group=%d)\n", g_state.rank, src, dst, bytes, proc, group); #endif nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_put(src, dst, bytes, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_get( void *src, void *dst, int bytes, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; #if DEBUG printf("[%d] comex_get(src=%p, dst=%p, bytes=%d, proc=%d, group=%d)\n", g_state.rank, src, dst, bytes, proc, group); #endif nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_get(src, dst, bytes, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_acc( int datatype, void *scale, void *src, void *dst, int bytes, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; #if DEBUG printf("[%d] comex_acc(datatype=%d, scale=%p, src=%p, dst=%p, bytes=%d, proc=%d, group=%d)\n", g_state.rank, datatype, scale, src, dst, bytes, proc, group); #endif nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_acc(datatype, scale, src, dst, bytes, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; #if DEBUG printf("[%d] comex_puts(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, group=%d)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, group); #endif nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_puts(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; #if DEBUG printf("[%d] comex_gets(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, group=%d)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, group); #endif nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_gets(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_accs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; #if DEBUG printf("[%d] comex_accs(datatype=%d, scale=%p, src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, group=%d)\n", g_state.rank, datatype, scale, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, group); #endif nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_accs(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_putv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; #if DEBUG printf("[%d] comex_putv(iov=%p, iov_len=%d, proc=%d, group=%d)\n", g_state.rank, iov, iov_len, proc, group); #endif nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_putv(iov, iov_len, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_getv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; #if DEBUG printf("[%d] comex_getv(iov=%p, iov_len=%d, proc=%d, group=%d)\n", g_state.rank, iov, iov_len, proc, group); #endif nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_getv(iov, iov_len, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_accv( int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; #if DEBUG printf("[%d] comex_accv(datatype=%d, scale=%p, iov=%p, iov_len=%d, proc=%d, group=%d)\n", g_state.rank, datatype, scale, iov, iov_len, proc, group); #endif nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_accv(datatype, scale, iov, iov_len, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_fence_all(comex_group_t group) { int p = 0; int count_before = 0; int count_after = 0; nb_t *nb = NULL; #if DEBUG printf("[%d] comex_fence_all(group=%d)\n", g_state.rank, group); #endif /* NOTE: We always fence on the world group */ /* count how many fence messagse to send */ for (p=0; poperation = OP_FENCE; header->remote_address = NULL; header->local_address = NULL; header->length = 0; header->rank = 0; nb_send_header(header, sizeof(header_t), p_master, nb); } } nb_wait_for_all(nb); for (p=0; poperation = OP_FENCE; header->remote_address = NULL; header->local_address = NULL; header->length = 0; header->rank = 0; nb_send_header(header, sizeof(header_t), p_master, nb); nb_wait_for_all(nb); fence_array[world_proc] = 0; } return COMEX_SUCCESS; } /* comex_barrier is comex_fence_all + MPI_Barrier */ int comex_barrier(comex_group_t group) { int status = 0; MPI_Comm comm = MPI_COMM_NULL; #if DEBUG static int count=-1; ++count; printf("[%d] comex_barrier(%d) count=%d\n", g_state.rank, group, count); #endif comex_fence_all(group); status = comex_group_comm(group, &comm); COMEX_ASSERT(COMEX_SUCCESS == status); MPI_Barrier(comm); return COMEX_SUCCESS; } void *comex_malloc_local(size_t size) { void *memory = NULL; #if DEBUG printf("[%d] comex_malloc_local(size=%lu)\n", g_state.rank, (long unsigned)size); #endif #if COMEX_USE_MPI_ALLOC_MEM if (MPI_SUCCESS != MPI_Alloc_mem(size, MPI_INFO_NULL, &memory)) { assert(0); } #else memory = malloc(size); #endif return memory; } int comex_free_local(void *ptr) { #if DEBUG printf("[%d] comex_free_local(ptr=%p)\n", g_state.rank, ptr); #endif assert(NULL != ptr); #if COMEX_USE_MPI_ALLOC_MEM if (MPI_SUCCESS != MPI_Free_mem(ptr)) { assert(0); } #else free(ptr); #endif return COMEX_SUCCESS; } int comex_wait_proc(int proc, comex_group_t group) { return comex_wait_all(group); } int comex_wait(comex_request_t* hdl) { int index = 0; nb_t *nb = NULL; COMEX_ASSERT(NULL != hdl); index = *(int*)hdl; COMEX_ASSERT(index >= 0); COMEX_ASSERT(index < nb_max_outstanding); nb = &nb_state[index]; if (0 == nb->in_use) { fprintf(stderr, "{%d} comex_wait Error: invalid handle\n", g_state.rank); } nb_wait_for_all(nb); nb->in_use = 0; return COMEX_SUCCESS; } int comex_test(comex_request_t* hdl, int *status) { int index = 0; nb_t *nb = NULL; COMEX_ASSERT(NULL != hdl); index = *(int*)hdl; COMEX_ASSERT(index >= 0); COMEX_ASSERT(index < nb_max_outstanding); nb = &nb_state[index]; if (0 == nb->in_use) { fprintf(stderr, "{%d} comex_test Error: invalid handle\n", g_state.rank); } if (NULL == nb->send_head && NULL == nb->recv_head) { COMEX_ASSERT(0 == nb->send_size); COMEX_ASSERT(0 == nb->recv_size); *status = 0; nb->in_use = 0; } else { *status = 1; } return COMEX_SUCCESS; } int comex_wait_all(comex_group_t group) { nb_wait_all(); return COMEX_SUCCESS; } int comex_nbput( void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_put(src, dst, bytes, world_proc, nb); return COMEX_SUCCESS; } int comex_nbget( void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_get(src, dst, bytes, world_proc, nb); return COMEX_SUCCESS; } int comex_nbacc( int datatype, void *scale, void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_acc(datatype, scale, src, dst, bytes, world_proc, nb); return COMEX_SUCCESS; } int comex_nbputs( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_puts(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); return COMEX_SUCCESS; } int comex_nbgets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_gets(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); return COMEX_SUCCESS; } int comex_nbaccs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_accs(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); return COMEX_SUCCESS; } int comex_nbputv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_putv(iov, iov_len, world_proc, nb); return COMEX_SUCCESS; } int comex_nbgetv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_getv(iov, iov_len, world_proc, nb); return COMEX_SUCCESS; } int comex_nbaccv( int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_accv(datatype, scale, iov, iov_len, world_proc, nb); return COMEX_SUCCESS; } int comex_rmw( int comex_op, void *ploc, void *prem, int extra, int proc, comex_group_t group) { header_t *header = NULL; void *payload = NULL; int length = 0; int op = 0; long extra_long = (long)extra; int world_rank = 0; comex_igroup_t *igroup = NULL; nb_t *nb = NULL; #if DEBUG printf("[%d] comex_rmw(%d, %p, %p, %d, %d)\n", g_state.rank, comex_op, ploc, prem, extra, proc); #endif CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_rank = _get_world_rank(igroup, proc); switch (comex_op) { case COMEX_FETCH_AND_ADD: op = OP_FETCH_AND_ADD; length = sizeof(int); payload = malloc(length); *((int*)payload) = extra; break; case COMEX_FETCH_AND_ADD_LONG: op = OP_FETCH_AND_ADD; length = sizeof(long); payload = malloc(length); *((long*)payload) = extra; break; case COMEX_SWAP: op = OP_SWAP; length = sizeof(int); payload = malloc(length); *((int*)payload) = *((int*)ploc); break; case COMEX_SWAP_LONG: op = OP_SWAP; length = sizeof(long); payload = malloc(length); *((long*)payload) = *((long*)ploc); break; default: COMEX_ASSERT(0); } /* create and prepare the header */ header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = op; header->remote_address = prem; header->local_address = ploc; header->rank = world_rank; header->length = length; nb = nb_wait_for_handle(); nb_recv(ploc, length, world_rank, nb); /* prepost recv */ nb_send_header(header, sizeof(header_t), world_rank, nb); nb_send_header(payload, length, world_rank, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } /* Mutex Operations */ int comex_create_mutexes(int num) { /* always on the world group */ int my_master = g_state.rank; #if DEBUG printf("[%d] comex_create_mutexes(num=%d)\n", g_state.rank, num); #endif /* preconditions */ COMEX_ASSERT(0 <= num); COMEX_ASSERT(NULL == num_mutexes); num_mutexes = (int*)malloc(group_list->size * sizeof(int)); /* exchange of mutex counts */ num_mutexes[group_list->rank] = num; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, num_mutexes, 1, MPI_INT, group_list->comm); /* every process sends their own create message to their master */ { nb_t *nb = NULL; header_t *header = NULL; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = OP_CREATE_MUTEXES; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = num; nb = nb_wait_for_handle(); nb_recv(NULL, 0, my_master, nb); /* prepost ack */ nb_send_header(header, sizeof(header_t), my_master, nb); nb_wait_for_all(nb); } MPI_Barrier(group_list->comm); return COMEX_SUCCESS; } int comex_destroy_mutexes() { /* always on the world group */ int my_master = g_state.rank; #if DEBUG printf("[%d] comex_destroy_mutexes()\n", g_state.rank); #endif /* preconditions */ COMEX_ASSERT(num_mutexes); /* this call is collective on the world group and this barrier ensures * there are no outstanding lock requests */ comex_barrier(COMEX_GROUP_WORLD); /* let masters know they need to participate */ { nb_t *nb = NULL; header_t *header = NULL; header = malloc(sizeof(header_t)); header->operation = OP_DESTROY_MUTEXES; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = num_mutexes[g_state.rank]; nb = nb_wait_for_handle(); nb_recv(NULL, 0, my_master, nb); /* prepost ack */ nb_send_header(header, sizeof(header_t), my_master, nb); nb_wait_for_all(nb); } free(num_mutexes); num_mutexes = NULL; return COMEX_SUCCESS; } int comex_lock(int mutex, int proc) { header_t *header = NULL; int world_rank = 0; int ack = 0; comex_igroup_t *igroup = NULL; nb_t *nb = NULL; #if DEBUG printf("[%d] comex_lock mutex=%d proc=%d\n", g_state.rank, mutex, proc); #endif CHECK_GROUP(COMEX_GROUP_WORLD,proc); igroup = comex_get_igroup_from_group(COMEX_GROUP_WORLD); world_rank = _get_world_rank(igroup, proc); header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = OP_LOCK; header->remote_address = NULL; header->local_address = NULL; header->rank = world_rank; header->length = mutex; nb = nb_wait_for_handle(); nb_recv(&ack, sizeof(int), world_rank, nb); /* prepost ack */ nb_send_header(header, sizeof(header_t), world_rank, nb); nb_wait_for_all(nb); COMEX_ASSERT(mutex == ack); return COMEX_SUCCESS; } int comex_unlock(int mutex, int proc) { header_t *header = NULL; int world_rank = 0; comex_igroup_t *igroup = NULL; nb_t *nb = NULL; #if DEBUG printf("[%d] comex_unlock mutex=%d proc=%d\n", g_state.rank, mutex, proc); #endif CHECK_GROUP(COMEX_GROUP_WORLD,proc); igroup = comex_get_igroup_from_group(COMEX_GROUP_WORLD); world_rank = _get_world_rank(igroup, proc); fence_array[world_rank] = 1; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = OP_UNLOCK; header->remote_address = NULL; header->local_address = NULL; header->rank = world_rank; header->length = mutex; nb = nb_wait_for_handle(); nb_send_header(header, sizeof(header_t), world_rank, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_malloc(void *ptrs[], size_t size, comex_group_t group) { comex_igroup_t *igroup = NULL; MPI_Comm comm = MPI_COMM_NULL; int comm_rank = -1; int comm_size = -1; /* preconditions */ assert(ptrs); #if DEBUG printf("[%d] comex_malloc(ptrs=%p, size=%lu, group=%d)\n", g_state.rank, ptrs, (long unsigned)size, group); #endif igroup = comex_get_igroup_from_group(group); comm = igroup->comm; assert(comm != MPI_COMM_NULL); MPI_Comm_rank(comm, &comm_rank); MPI_Comm_size(comm, &comm_size); /* allocate and register segment */ ptrs[comm_rank] = comex_malloc_local(sizeof(char)*size); /* exchange buffer address */ MPI_Allgather(MPI_IN_PLACE, sizeof(void *), MPI_BYTE, ptrs, sizeof(void *), MPI_BYTE, comm); MPI_Barrier(comm); return COMEX_SUCCESS; } int comex_malloc_mem_dev(void *ptrs[], size_t size, comex_group_t group, const char* device) { return comex_malloc(ptrs,size,group); } int comex_free(void *ptr, comex_group_t group) { comex_igroup_t *igroup = NULL; MPI_Comm comm = MPI_COMM_NULL; int comm_rank; int comm_size; long **allgather_ptrs = NULL; /* preconditions */ assert(NULL != ptr); #if DEBUG printf("[%d] comex_free(ptr=%p, group=%d)\n", g_state.rank, ptr, group); #endif igroup = comex_get_igroup_from_group(group); comm = igroup->comm; assert(comm != MPI_COMM_NULL); MPI_Comm_rank(comm, &comm_rank); MPI_Comm_size(comm, &comm_size); /* allocate receive buffer for exchange of pointers */ allgather_ptrs = (long **)malloc(sizeof(void *) * comm_size); assert(allgather_ptrs); /* exchange of pointers */ MPI_Allgather(&ptr, sizeof(void *), MPI_BYTE, allgather_ptrs, sizeof(void *), MPI_BYTE, comm); /* TODO do something useful with pointers */ /* remove my ptr from reg cache and free ptr */ comex_free_local(ptr); free(allgather_ptrs); /* Is this needed? */ MPI_Barrier(comm); return COMEX_SUCCESS; } int comex_free_dev(void *ptr, comex_group_t group) { return comex_free(ptr, group); } static void* _progress_server(void *arg) { int running = 0; header_t *header = NULL; #if DEBUG printf("[%d] _progress_server()\n", g_state.rank); #endif { int status = _set_affinity(g_state.node_size+g_state.node_rank); if (0 != status) { status = _set_affinity(g_state.node_rank); COMEX_ASSERT(0 == status); } } /* initialize shared buffers */ header = (header_t*)malloc(sizeof(header_t)); COMEX_ASSERT(header); static_acc_buffer = (char*)malloc(sizeof(char)*COMEX_STATIC_BUFFER_SIZE); COMEX_ASSERT(static_acc_buffer); running = 1; while (running) { int source = 0; int length = 0; MPI_Status recv_status; MPI_Recv(header, sizeof(header_t), MPI_CHAR, MPI_ANY_SOURCE, COMEX_TAG, g_state.comm, &recv_status); MPI_Get_count(&recv_status, MPI_CHAR, &length); source = recv_status.MPI_SOURCE; # if DEBUG printf("[%d] progress MPI_Recv source=%d length=%d\n", g_state.rank, source, length); # endif /* dispatch message handler */ switch (header->operation) { case OP_PUT: _put_handler(header, source); break; case OP_PUT_PACKED: _put_packed_handler(header, source); break; case OP_PUT_IOV: _put_iov_handler(header, source); break; case OP_GET: _get_handler(header, source); break; case OP_GET_PACKED: _get_packed_handler(header, source); break; case OP_GET_IOV: _get_iov_handler(header, source); break; case OP_ACC_INT: case OP_ACC_DBL: case OP_ACC_FLT: case OP_ACC_CPL: case OP_ACC_DCP: case OP_ACC_LNG: _acc_handler(header, source); break; case OP_ACC_INT_PACKED: case OP_ACC_DBL_PACKED: case OP_ACC_FLT_PACKED: case OP_ACC_CPL_PACKED: case OP_ACC_DCP_PACKED: case OP_ACC_LNG_PACKED: _acc_packed_handler(header, source); break; case OP_ACC_INT_IOV: case OP_ACC_DBL_IOV: case OP_ACC_FLT_IOV: case OP_ACC_CPL_IOV: case OP_ACC_DCP_IOV: case OP_ACC_LNG_IOV: _acc_iov_handler(header, source); break; case OP_FENCE: _fence_handler(header, source); break; case OP_FETCH_AND_ADD: _fetch_and_add_handler(header, source); break; case OP_SWAP: _swap_handler(header, source); break; case OP_CREATE_MUTEXES: _mutex_create_handler(header, source); break; case OP_DESTROY_MUTEXES: _mutex_destroy_handler(header, source); break; case OP_LOCK: _lock_handler(header, source); break; case OP_UNLOCK: _unlock_handler(header, source); break; case OP_QUIT: running = 0; break; default: printf("[%d] header operation not recognized: %d\n", g_state.rank, header->operation); COMEX_ASSERT(0); } } free(header); free(static_acc_buffer); return NULL; } STATIC void _put_handler(header_t *header, int proc) { int retval = 0; MPI_Status recv_status; #if DEBUG printf("[%d] _put_handler rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif server_recv(header->remote_address, header->length, proc); } STATIC void _put_packed_handler(header_t *header, int proc) { int retval = 0; MPI_Status recv_status; char *packed_buffer = NULL; int packed_index = 0; stride_t *stride = NULL; int i, j; long dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int dst_bvalue[7], dst_bunit[7]; #if DEBUG printf("[%d] _put_packed_handler rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif stride = malloc(sizeof(stride_t)); COMEX_ASSERT(stride); server_recv(stride, sizeof(stride_t), proc); COMEX_ASSERT(stride->stride_levels >= 0); COMEX_ASSERT(stride->stride_levels < COMEX_MAX_STRIDE_LEVEL); #if DEBUG printf("[%d] _put_packed_handler stride_levels=%d, count[0]=%d\n", g_state.rank, stride->stride_levels, stride->count[0]); for (i=0; istride_levels; ++i) { printf("[%d] stride[%d]=%d count[%d+1]=%d\n", g_state.rank, i, stride->stride[i], i, stride->count[i+1]); } #endif if ((unsigned)header->length > COMEX_STATIC_BUFFER_SIZE) { packed_buffer = malloc(header->length); } else { packed_buffer = static_acc_buffer; } server_recv(packed_buffer, header->length, proc); unpack(packed_buffer, header->remote_address, stride->stride, stride->count, stride->stride_levels); if ((unsigned)header->length > COMEX_STATIC_BUFFER_SIZE) { free(packed_buffer); } } STATIC void _put_iov_handler(header_t *header, int proc) { int i = 0; char *packed_buffer = NULL; int packed_index = 0; char *iov_buf = NULL; int iov_off = 0; int limit = 0; int bytes = 0; void **src = NULL; void **dst = NULL; #if DEBUG printf("[%d] _put_iov_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG printf("[%d] _put_iov_handler header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_PUT_IOV == header->operation); iov_buf = malloc(header->length); COMEX_ASSERT(iov_buf); server_recv(iov_buf, header->length, proc); limit = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(limit > 0); bytes = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(bytes > 0); src = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; dst = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; COMEX_ASSERT(iov_off == header->length); #if DEBUG printf("[%d] _put_iov_handler limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif if ((bytes*limit) > COMEX_STATIC_BUFFER_SIZE) { packed_buffer = malloc(bytes*limit); COMEX_ASSERT(packed_buffer); } else { packed_buffer = static_acc_buffer; } server_recv(packed_buffer, bytes * limit, proc); packed_index = 0; for (i=0; i COMEX_STATIC_BUFFER_SIZE) { free(packed_buffer); } free(iov_buf); } STATIC void _get_handler(header_t *header, int proc) { #if DEBUG printf("[%d] _get_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG printf("[%d] header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_GET == header->operation); assert(header->rank == g_state.rank); server_send(header->remote_address, header->length, proc); } STATIC void _get_packed_handler(header_t *header, int proc) { char *packed_buffer = NULL; int packed_index = 0; stride_t *stride_src = NULL; #if DEBUG printf("[%d] _get_packed_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG printf("[%d] header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_GET_PACKED == header->operation); stride_src = malloc(sizeof(stride_t)); COMEX_ASSERT(stride_src); server_recv(stride_src, sizeof(stride_t), proc); COMEX_ASSERT(stride_src->stride_levels >= 0); COMEX_ASSERT(stride_src->stride_levels < COMEX_MAX_STRIDE_LEVEL); packed_buffer = pack(header->remote_address, stride_src->stride, stride_src->count, stride_src->stride_levels, &packed_index); server_send(packed_buffer, packed_index, proc); free(stride_src); free(packed_buffer); } STATIC void _get_iov_handler(header_t *header, int proc) { int i = 0; char *packed_buffer = NULL; int packed_index = 0; char *iov_buf = NULL; int iov_off = 0; int limit = 0; int bytes = 0; void **src = NULL; void **dst = NULL; #if DEBUG printf("[%d] _get_iov_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG printf("[%d] _get_iov_handler header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_GET_IOV == header->operation); iov_buf = malloc(header->length); COMEX_ASSERT(iov_buf); server_recv(iov_buf, header->length, proc); limit = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(limit > 0); bytes = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(bytes > 0); src = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; dst = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; COMEX_ASSERT(iov_off == header->length); #if DEBUG printf("[%d] _get_iov_handler limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif if ((bytes*limit) > COMEX_STATIC_BUFFER_SIZE) { packed_buffer = malloc(bytes*limit); COMEX_ASSERT(packed_buffer); } else { packed_buffer = static_acc_buffer; } packed_index = 0; for (i=0; i COMEX_STATIC_BUFFER_SIZE) { free(packed_buffer); } free(iov_buf); } STATIC void _acc_handler(header_t *header, int proc) { void *scale = NULL; int sizeof_scale = 0; int acc_type = 0; char *acc_buffer = NULL; MPI_Status recv_status; #if DEBUG printf("[%d] _acc_handler\n", g_state.rank); #endif switch (header->operation) { case OP_ACC_INT: acc_type = COMEX_ACC_INT; sizeof_scale = sizeof(int); break; case OP_ACC_DBL: acc_type = COMEX_ACC_DBL; sizeof_scale = sizeof(double); break; case OP_ACC_FLT: acc_type = COMEX_ACC_FLT; sizeof_scale = sizeof(float); break; case OP_ACC_LNG: acc_type = COMEX_ACC_LNG; sizeof_scale = sizeof(long); break; case OP_ACC_CPL: acc_type = COMEX_ACC_CPL; sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP: acc_type = COMEX_ACC_DCP; sizeof_scale = sizeof(DoubleComplex); break; default: COMEX_ASSERT(0); } scale = malloc(sizeof_scale); COMEX_ASSERT(scale); server_recv(scale, sizeof_scale, proc); if ((unsigned)header->length > COMEX_STATIC_BUFFER_SIZE) { acc_buffer = malloc(header->length); } else { acc_buffer = static_acc_buffer; } server_recv(acc_buffer, header->length, proc); pthread_mutex_lock(&mutex); _acc(acc_type, header->length, header->remote_address, acc_buffer, scale); pthread_mutex_unlock(&mutex); if ((unsigned)header->length > COMEX_STATIC_BUFFER_SIZE) { free(acc_buffer); } free(scale); } STATIC void _acc_packed_handler(header_t *header, int proc) { void *scale = NULL; int sizeof_scale = 0; int acc_type = 0; char *acc_buffer = NULL; MPI_Status recv_status; stride_t *stride = NULL; #if DEBUG printf("[%d] _acc_packed_handler\n", g_state.rank); #endif switch (header->operation) { case OP_ACC_INT_PACKED: acc_type = COMEX_ACC_INT; sizeof_scale = sizeof(int); break; case OP_ACC_DBL_PACKED: acc_type = COMEX_ACC_DBL; sizeof_scale = sizeof(double); break; case OP_ACC_FLT_PACKED: acc_type = COMEX_ACC_FLT; sizeof_scale = sizeof(float); break; case OP_ACC_LNG_PACKED: acc_type = COMEX_ACC_LNG; sizeof_scale = sizeof(long); break; case OP_ACC_CPL_PACKED: acc_type = COMEX_ACC_CPL; sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP_PACKED: acc_type = COMEX_ACC_DCP; sizeof_scale = sizeof(DoubleComplex); break; default: COMEX_ASSERT(0); } scale = malloc(sizeof_scale); COMEX_ASSERT(scale); server_recv(scale, sizeof_scale, proc); stride = malloc(sizeof(stride_t)); COMEX_ASSERT(stride); server_recv(stride, sizeof(stride_t), proc); if ((unsigned)header->length > COMEX_STATIC_BUFFER_SIZE) { acc_buffer = malloc(header->length); } else { acc_buffer = static_acc_buffer; } server_recv(acc_buffer, header->length, proc); pthread_mutex_lock(&mutex); { char *packed_buffer = acc_buffer; char *dst = header->remote_address; int *dst_stride = stride->stride; int *count = stride->count; int stride_levels = stride->stride_levels; int i, j; long dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int dst_bvalue[7], dst_bunit[7]; int packed_index = 0; COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); COMEX_ASSERT(NULL != packed_buffer); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != dst_stride); COMEX_ASSERT(NULL != count); COMEX_ASSERT(count[0] > 0); #if DEBUG printf("[%d] unpack(dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, dst, dst_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { dst_bvalue[i] = 0; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { dst_bvalue[j] = 0; } } _acc(acc_type, count[0], &dst[dst_idx], &packed_buffer[packed_index], scale); packed_index += count[0]; } COMEX_ASSERT(packed_index == n1dim*count[0]); } pthread_mutex_unlock(&mutex); if ((unsigned)header->length > COMEX_STATIC_BUFFER_SIZE) { free(acc_buffer); } free(stride); free(scale); } STATIC void _acc_iov_handler(header_t *header, int proc) { int i = 0; char *packed_buffer = NULL; int packed_index = 0; char *iov_buf = NULL; int iov_off = 0; int limit = 0; int bytes = 0; void **src = NULL; void **dst = NULL; void *scale = NULL; int sizeof_scale = 0; int acc_type = 0; MPI_Status recv_status; #if DEBUG printf("[%d] _acc_iov_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG printf("[%d] _acc_iov_handler header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif #if DEBUG printf("[%d] _acc_iov_handler limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif switch (header->operation) { case OP_ACC_INT_IOV: acc_type = COMEX_ACC_INT; sizeof_scale = sizeof(int); break; case OP_ACC_DBL_IOV: acc_type = COMEX_ACC_DBL; sizeof_scale = sizeof(double); break; case OP_ACC_FLT_IOV: acc_type = COMEX_ACC_FLT; sizeof_scale = sizeof(float); break; case OP_ACC_LNG_IOV: acc_type = COMEX_ACC_LNG; sizeof_scale = sizeof(long); break; case OP_ACC_CPL_IOV: acc_type = COMEX_ACC_CPL; sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP_IOV: acc_type = COMEX_ACC_DCP; sizeof_scale = sizeof(DoubleComplex); break; default: COMEX_ASSERT(0); } scale = malloc(sizeof_scale); COMEX_ASSERT(scale); server_recv(scale, sizeof_scale, proc); iov_buf = malloc(header->length); COMEX_ASSERT(iov_buf); server_recv(iov_buf, header->length, proc); limit = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(limit > 0); bytes = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(bytes > 0); src = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; dst = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; COMEX_ASSERT(iov_off == header->length); if ((bytes*limit) > COMEX_STATIC_BUFFER_SIZE) { packed_buffer = malloc(bytes*limit); } else { packed_buffer = static_acc_buffer; } server_recv(packed_buffer, bytes*limit, proc); pthread_mutex_lock(&mutex); packed_index = 0; for (i=0; i COMEX_STATIC_BUFFER_SIZE) { free(packed_buffer); } free(scale); free(iov_buf); } STATIC void _fence_handler(header_t *header, int proc) { #if DEBUG printf("[%d] _fence_handler proc=%d\n", g_state.rank, proc); #endif /* preconditions */ COMEX_ASSERT(header); #if NEED_ASM_VOLATILE_MEMORY #if DEBUG printf("[%d] _fence_handler asm volatile (\"\" : : : \"memory\"); \n", g_state.rank); #endif asm volatile ("" : : : "memory"); #endif /* we send the ack back to the originating proc */ server_send(NULL, 0, proc); } STATIC void _fetch_and_add_handler(header_t *header, int proc) { int *value_int = NULL; long *value_long = NULL; void *payload = NULL; #if DEBUG printf("[%d] _fetch_and_add_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG printf("[%d] header rem=%p loc=%p rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif COMEX_ASSERT(OP_FETCH_AND_ADD == header->operation); if (sizeof(int) == header->length) { payload = malloc(sizeof(int)); server_recv(payload, sizeof(int), proc); value_int = malloc(sizeof(int)); *value_int = *((int*)header->remote_address); /* "fetch" */ *((int*)header->remote_address) += *((int*)payload); /* "add" */ server_send(value_int, sizeof(int), proc); free(value_int); } else if (sizeof(long) == header->length) { payload = malloc(sizeof(long)); server_recv(payload, sizeof(long), proc); value_long = malloc(sizeof(long)); *value_long = *((long*)header->remote_address); /* "fetch" */ *((long*)header->remote_address) += *((long*)payload); /* "add" */ server_send(value_long, sizeof(long), proc); free(value_long); } else { COMEX_ASSERT(0); } free(payload); } STATIC void _swap_handler(header_t *header, int proc) { int *value_int = NULL; long *value_long = NULL; void *payload = NULL; #if DEBUG printf("[%d] _swap_handler rem=%p loc=%p rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif COMEX_ASSERT(OP_SWAP == header->operation); if (sizeof(int) == header->length) { payload = malloc(sizeof(int)); server_recv(payload, sizeof(int), proc); value_int = malloc(sizeof(int)); *value_int = *((int*)header->remote_address); /* "fetch" */ *((int*)header->remote_address) = *((int*)payload); /* "swap" */ server_send(value_int, sizeof(int), proc); free(value_int); } else if (sizeof(long) == header->length) { payload = malloc(sizeof(long)); server_recv(payload, sizeof(long), proc); value_long = malloc(sizeof(long)); *value_long = *((long*)header->remote_address); /* "fetch" */ *((long*)header->remote_address) = *((long*)payload); /* "swap" */ server_send(value_long, sizeof(long), proc); free(value_long); } else { COMEX_ASSERT(0); } free(payload); } STATIC void _mutex_create_handler(header_t *header, int proc) { int i; int num = header->length; #if DEBUG printf("[%d] _mutex_create_handler proc=%d num=%d\n", g_state.rank, proc, num); #endif mutexes[proc] = (int*)malloc(sizeof(int) * num); lq_heads[proc] = (comex_lock_t**)malloc(sizeof(comex_lock_t*) * num); for (i=0; ilength; #if DEBUG printf("[%d] _mutex_destroy_handler proc=%d\n", g_state.rank, proc); #endif for (i=0; ilength; int rank = header->rank; #if DEBUG printf("[%d] _lock_handler id=%d in rank=%d req by proc=%d\n", g_state.rank, id, rank, proc); #endif COMEX_ASSERT(0 <= id); if (UNLOCKED == mutexes[rank][id]) { mutexes[rank][id] = proc; server_send(&id, sizeof(int), proc); } else { comex_lock_t *lock = NULL; #if DEBUG printf("[%d] _lq_push rank=%d req_by=%d id=%d\n", g_state.rank, rank, proc, id); #endif lock = malloc(sizeof(comex_lock_t)); lock->next = NULL; lock->rank = proc; if (lq_heads[rank][id]) { /* insert at tail */ comex_lock_t *lq = lq_heads[rank][id]; while (lq->next) { lq = lq->next; } lq->next = lock; } else { /* new head */ lq_heads[rank][id] = lock; } } } STATIC void _unlock_handler(header_t *header, int proc) { int id = header->length; int rank = header->rank; #if DEBUG printf("[%d] _unlock_handler id=%d in rank=%d req by proc=%d\n", g_state.rank, id, rank, proc); #endif COMEX_ASSERT(0 <= id); if (lq_heads[rank][id]) { /* a lock requester was queued */ /* find the next lock request and update queue */ comex_lock_t *lock = lq_heads[rank][id]; lq_heads[rank][id] = lq_heads[rank][id]->next; /* update lock */ mutexes[rank][id] = lock->rank; /* notify next in line */ server_send(&id, sizeof(int), lock->rank); free(lock); } else { /* no enqued request */ mutexes[rank][id] = UNLOCKED; } } static int _get_world_rank(comex_igroup_t *igroup, int rank) { int world_rank; int status; status = MPI_Group_translate_ranks(igroup->group, 1, &rank, g_state.group, &world_rank); CHECK_MPI_RETVAL(status); COMEX_ASSERT(MPI_PROC_NULL != world_rank); return world_rank; } /* gets (in group order) corresponding world ranks for entire group */ static int* _get_world_ranks(comex_igroup_t *igroup) { int i = 0; int *group_ranks = (int*)malloc(sizeof(int)*igroup->size); int *world_ranks = (int*)malloc(sizeof(int)*igroup->size); int status; for (i=0; isize; ++i) { group_ranks[i] = i; world_ranks[i] = MPI_PROC_NULL; } status = MPI_Group_translate_ranks( igroup->group, igroup->size, group_ranks, g_state.group, world_ranks); COMEX_ASSERT(MPI_SUCCESS == status); for (i=0; isize; ++i) { COMEX_ASSERT(MPI_PROC_NULL != world_ranks[i]); } free(group_ranks); return world_ranks; } static int _set_affinity(int cpu) { int status = 0; #if COMEX_SET_AFFINITY #if HAVE_PTHREAD_SETAFFINITY_NP || HAVE_SCHED_SETAFFINITY cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); #if HAVE_PTHREAD_SETAFFINITY_NP status = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); if (0 != status) { perror("pthread_setaffinity_np"); } #elif HAVE_SCHED_SETAFFINITY status = sched_setaffinity(getpid(), sizeof(cpu_set_t), &cpuset); if (0 != status) { perror("sched_setaffinity"); } #endif #endif #endif return status; } STATIC void check_mpi_retval(int retval, const char *file, int line) { if (MPI_SUCCESS != retval) { const char *msg = str_mpi_retval(retval); fprintf(stderr, "{%d} MPI Error: %s: line %d: %s\n", g_state.rank, file, line, msg); MPI_Abort(g_state.comm, retval); } } STATIC const char *str_mpi_retval(int retval) { const char *msg = NULL; if (retval == MPI_SUCCESS ) { msg = "MPI_SUCCESS"; } else if (retval == MPI_ERR_BUFFER ) { msg = "MPI_ERR_BUFFER"; } else if (retval == MPI_ERR_COUNT ) { msg = "MPI_ERR_COUNT"; } else if (retval == MPI_ERR_TYPE ) { msg = "MPI_ERR_TYPE"; } else if (retval == MPI_ERR_TAG ) { msg = "MPI_ERR_TAG"; } else if (retval == MPI_ERR_COMM ) { msg = "MPI_ERR_COMM"; } else if (retval == MPI_ERR_RANK ) { msg = "MPI_ERR_RANK"; } else if (retval == MPI_ERR_ROOT ) { msg = "MPI_ERR_ROOT"; } else if (retval == MPI_ERR_GROUP ) { msg = "MPI_ERR_GROUP"; } else if (retval == MPI_ERR_OP ) { msg = "MPI_ERR_OP"; } else if (retval == MPI_ERR_TOPOLOGY ) { msg = "MPI_ERR_TOPOLOGY"; } else if (retval == MPI_ERR_DIMS ) { msg = "MPI_ERR_DIMS"; } else if (retval == MPI_ERR_ARG ) { msg = "MPI_ERR_ARG"; } else if (retval == MPI_ERR_UNKNOWN ) { msg = "MPI_ERR_UNKNOWN"; } else if (retval == MPI_ERR_TRUNCATE ) { msg = "MPI_ERR_TRUNCATE"; } else if (retval == MPI_ERR_OTHER ) { msg = "MPI_ERR_OTHER"; } else if (retval == MPI_ERR_INTERN ) { msg = "MPI_ERR_INTERN"; } else if (retval == MPI_ERR_IN_STATUS) { msg = "MPI_ERR_IN_STATUS"; } else if (retval == MPI_ERR_PENDING ) { msg = "MPI_ERR_PENDING"; } else if (retval == MPI_ERR_REQUEST ) { msg = "MPI_ERR_REQUEST"; } else if (retval == MPI_ERR_LASTCODE ) { msg = "MPI_ERR_LASTCODE"; } else { msg = "DEFAULT"; } return msg; } STATIC void server_send(void *buf, int count, int dest) { int retval = 0; #if DEBUG printf("[%d] server_send(buf=%p, count=%d, dest=%d)\n", g_state.rank, buf, count, dest); #endif retval = MPI_Send(buf, count, MPI_CHAR, dest, COMEX_TAG, group_list->comm); CHECK_MPI_RETVAL(retval); } STATIC void server_recv(void *buf, int count, int source) { int retval = 0; MPI_Status status; int recv_count = 0; #if DEBUG printf("[%d] server_recv(buf=%p, count=%d, source=%d)\n", g_state.rank, buf, count, source); #endif retval = MPI_Recv(buf, count, MPI_CHAR, source, COMEX_TAG, g_state.comm, &status); CHECK_MPI_RETVAL(retval); COMEX_ASSERT(status.MPI_SOURCE == source); COMEX_ASSERT(status.MPI_TAG == COMEX_TAG); retval = MPI_Get_count(&status, MPI_CHAR, &recv_count); CHECK_MPI_RETVAL(retval); COMEX_ASSERT(recv_count == count); } STATIC void nb_send_common(void *buf, int count, int dest, nb_t *nb, int need_free) { int retval = 0; message_t *message = NULL; COMEX_ASSERT(NULL != nb); nb->send_size += 1; nb_count_event += 1; nb_count_send += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = need_free; message->stride = NULL; message->iov = NULL; if (NULL == nb->send_head) { nb->send_head = message; } if (NULL != nb->send_tail) { nb->send_tail->next = message; } nb->send_tail = message; retval = MPI_Isend(buf, count, MPI_CHAR, dest, COMEX_TAG, g_state.comm, &(message->request)); CHECK_MPI_RETVAL(retval); } STATIC void nb_send_header(void *buf, int count, int dest, nb_t *nb) { #if DEBUG printf("[%d] nb_send_header(buf=%p, count=%d, dest=%d, nb=%p)\n", g_state.rank, buf, count, dest, nb); #endif nb_send_common(buf, count, dest, nb, 1); } STATIC void nb_send_buffer(void *buf, int count, int dest, nb_t *nb) { #if DEBUG printf("[%d] nb_send_buffer(buf=%p, count=%d, dest=%d, nb=%p)\n", g_state.rank, buf, count, dest, nb); #endif nb_send_common(buf, count, dest, nb, 0); } STATIC void nb_recv_packed(void *buf, int count, int source, nb_t *nb, stride_t *stride) { int retval = 0; message_t *message = NULL; COMEX_ASSERT(NULL != nb); #if DEBUG printf("[%d] nb_recv(buf=%p, count=%d, source=%d, nb=%p)\n", g_state.rank, buf, count, source, nb); #endif nb->recv_size += 1; nb_count_event += 1; nb_count_recv += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = 1; message->stride = stride; message->iov = NULL; if (NULL == nb->recv_head) { nb->recv_head = message; } if (NULL != nb->recv_tail) { nb->recv_tail->next = message; } nb->recv_tail = message; retval = MPI_Irecv(buf, count, MPI_CHAR, source, COMEX_TAG, group_list->comm, &(message->request)); CHECK_MPI_RETVAL(retval); } STATIC void nb_recv_iov(void *buf, int count, int source, nb_t *nb, comex_giov_t *iov) { int retval = 0; message_t *message = NULL; COMEX_ASSERT(NULL != nb); #if DEBUG printf("[%d] nb_recv(buf=%p, count=%d, source=%d, nb=%p)\n", g_state.rank, buf, count, source, nb); #endif nb->recv_size += 1; nb_count_event += 1; nb_count_recv += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = 1; message->stride = NULL; message->iov = iov; if (NULL == nb->recv_head) { nb->recv_head = message; COMEX_ASSERT(NULL == nb->recv_tail); } if (NULL != nb->recv_tail) { nb->recv_tail->next = message; } nb->recv_tail = message; retval = MPI_Irecv(buf, count, MPI_CHAR, source, COMEX_TAG, group_list->comm, &(message->request)); CHECK_MPI_RETVAL(retval); } STATIC void nb_recv(void *buf, int count, int source, nb_t *nb) { int retval = 0; message_t *message = NULL; COMEX_ASSERT(NULL != nb); #if DEBUG printf("[%d] nb_recv(buf=%p, count=%d, source=%d, nb=%p)\n", g_state.rank, buf, count, source, nb); #endif nb->recv_size += 1; nb_count_event += 1; nb_count_recv += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = 0; message->stride = NULL; message->iov = NULL; if (NULL == nb->recv_head) { nb->recv_head = message; } if (NULL != nb->recv_tail) { nb->recv_tail->next = message; } nb->recv_tail = message; retval = MPI_Irecv(buf, count, MPI_CHAR, source, COMEX_TAG, group_list->comm, &(message->request)); CHECK_MPI_RETVAL(retval); } STATIC int nb_get_handle_index() { int value = 0; if (0 == nb_index) { value = nb_max_outstanding-1; } else { value = nb_index-1; } return value; } STATIC nb_t* nb_wait_for_handle() { nb_t *nb = NULL; int in_use_count = 0; #if DEBUG printf("[%d] nb_wait_for_handle()\n", g_state.rank); #endif /* find first handle that isn't associated with a user-level handle */ /* make sure the handle we find has processed all events */ /* the user can accidentally exhaust the available handles */ do { ++in_use_count; if (in_use_count > nb_max_outstanding) { fprintf(stderr, "{%d} nb_wait_for_handle Error: all user-level " "nonblocking handles have been exhausted\n", g_state.rank); MPI_Abort(g_state.comm, -1); } nb = &nb_state[nb_index++]; nb_index %= nb_max_outstanding; /* wrap around if needed */ nb_wait_for_all(nb); } while (nb->in_use); return nb; } STATIC void nb_wait_for_send(nb_t *nb) { #if DEBUG printf("[%d] nb_wait_for_send(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); while (NULL != nb->send_head) { nb_wait_for_send1(nb); } nb->send_tail = NULL; } STATIC void nb_wait_for_send1(nb_t *nb) { #if DEBUG printf("[%d] nb_wait_for_send1(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); COMEX_ASSERT(NULL != nb->send_head); { MPI_Status status; int retval = 0; message_t *message_to_free = NULL; retval = MPI_Wait(&(nb->send_head->request), &status); CHECK_MPI_RETVAL(retval); if (nb->send_head->need_free) { free(nb->send_head->message); } message_to_free = nb->send_head; nb->send_head = nb->send_head->next; free(message_to_free); COMEX_ASSERT(nb->send_size > 0); nb->send_size -= 1; nb_count_send_processed += 1; nb_count_event_processed += 1; if (NULL == nb->send_head) { nb->send_tail = NULL; } } } STATIC void nb_wait_for_recv(nb_t *nb) { #if DEBUG printf("[%d] nb_wait_for_recv(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); while (NULL != nb->recv_head) { nb_wait_for_recv1(nb); } } STATIC void nb_wait_for_recv1(nb_t *nb) { #if DEBUG printf("[%d] nb_wait_for_recv1(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); COMEX_ASSERT(NULL != nb->recv_head); { MPI_Status status; int retval = 0; message_t *message_to_free = NULL; retval = MPI_Wait(&(nb->recv_head->request), &status); CHECK_MPI_RETVAL(retval); if (NULL != nb->recv_head->stride) { stride_t *stride = nb->recv_head->stride; unpack(nb->recv_head->message, stride->ptr, stride->stride, stride->count, stride->stride_levels); free(stride); } if (NULL != nb->recv_head->iov) { int i = 0; char *message = nb->recv_head->message; int off = 0; comex_giov_t *iov = nb->recv_head->iov; for (i=0; icount; ++i) { (void)memcpy(iov->dst[i], &message[off], iov->bytes); off += iov->bytes; } free(iov->src); free(iov->dst); free(iov); } if (nb->recv_head->need_free) { free(nb->recv_head->message); } message_to_free = nb->recv_head; nb->recv_head = nb->recv_head->next; free(message_to_free); COMEX_ASSERT(nb->recv_size > 0); nb->recv_size -= 1; nb_count_recv_processed += 1; nb_count_event_processed += 1; if (NULL == nb->recv_head) { nb->recv_tail = NULL; } } } STATIC void nb_wait_for_all(nb_t *nb) { #if DEBUG printf("[%d] nb_wait_for_all(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); /* fair processing of requests */ while (NULL != nb->send_head || NULL != nb->recv_head) { if (NULL != nb->send_head) { nb_wait_for_send1(nb); } if (NULL != nb->recv_head) { nb_wait_for_recv1(nb); } } } STATIC void nb_wait_all() { int i = 0; #if DEBUG printf("[%d] nb_wait_all()\n", g_state.rank); #endif COMEX_ASSERT(nb_count_event-nb_count_event_processed >= 0); for (i=0; i 0); COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != nb); #if ENABLE_PUT_SELF /* put to self */ if (g_state.rank == proc) { memcpy(dst, src, bytes); return; } #endif { header_t *header = NULL; /* only fence on the master */ fence_array[proc] = 1; header = malloc(sizeof(header_t)); header->operation = OP_PUT; header->remote_address = dst; header->local_address = src; header->rank = proc; header->length = bytes; nb_send_header(header, sizeof(header_t), proc, nb); nb_send_buffer(src, bytes, proc, nb); } } STATIC void nb_get(void *src, void *dst, int bytes, int proc, nb_t *nb) { #if DEBUG printf("[%d] nb_get(src=%p, dst=%p, bytes=%d, proc=%d, nb=%p)\n", g_state.rank, src, dst, bytes, proc, nb); #endif COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(bytes > 0); COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != nb); #if ENABLE_GET_SELF /* get from self */ if (g_state.rank == proc) { memcpy(dst, src, bytes); return; } #endif { header_t *header = NULL; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = OP_GET; header->remote_address = src; header->local_address = dst; header->rank = proc; header->length = bytes; nb_recv(dst, bytes, proc, nb); /* prepost receive */ nb_send_header(header, sizeof(header_t), proc, nb); } } STATIC void nb_acc(int datatype, void *scale, void *src, void *dst, int bytes, int proc, nb_t *nb) { #if DEBUG printf("[%d] nb_acc(datatype=%d, scale=%p, src=%p, dst=%p, bytes=%d, proc=%d, nb=%p)\n", g_state.rank, datatype, scale, src, dst, bytes, proc, nb); #endif COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(bytes > 0); COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != nb); #if ENABLE_ACC_SELF /* acc to self */ if (g_state.rank == proc) { pthread_mutex_lock(&mutex); _acc(datatype, bytes, dst, src, scale); pthread_mutex_unlock(&mutex); return; } #endif { header_t *header = NULL; int scale_size = 0; op_t operation = 0; switch (datatype) { case COMEX_ACC_INT: operation = OP_ACC_INT; scale_size = sizeof(int); break; case COMEX_ACC_DBL: operation = OP_ACC_DBL; scale_size = sizeof(double); break; case COMEX_ACC_FLT: operation = OP_ACC_FLT; scale_size = sizeof(float); break; case COMEX_ACC_CPL: operation = OP_ACC_CPL; scale_size = sizeof(SingleComplex); break; case COMEX_ACC_DCP: operation = OP_ACC_DCP; scale_size = sizeof(DoubleComplex); break; case COMEX_ACC_LNG: operation = OP_ACC_LNG; scale_size = sizeof(long); break; default: COMEX_ASSERT(0); } /* only fence on the master */ fence_array[proc] = 1; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = operation; header->remote_address = dst; header->local_address = src; header->rank = proc; header->length = bytes; nb_send_header(header, sizeof(header_t), proc, nb); nb_send_buffer(scale, scale_size, proc, nb); nb_send_buffer(src, bytes, proc, nb); } } STATIC void nb_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; #if DEBUG printf("[%d] nb_puts(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif /* if not actually a strided put */ if (0 == stride_levels) { nb_put(src, dst, count[0], proc, nb); return; } #if ENABLE_PUT_PACKED #if ENABLE_PUT_SELF /* if not a strided put to self, use packed algorithm */ if (g_state.rank != proc) #endif { nb_puts_packed(src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } /* index mangling */ for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_put((char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } } STATIC void nb_puts_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i, j; long src_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int packed_index = 0; char *packed_buffer = NULL; stride_t *stride = NULL; #if DEBUG printf("[%d] nb_puts_packed(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != count); COMEX_ASSERT(NULL != nb); COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); /* copy dst info into structure */ stride = malloc(sizeof(stride_t)); COMEX_ASSERT(stride); stride->stride_levels = stride_levels; stride->count[0] = count[0]; for (i=0; istride[i] = dst_stride[i]; stride->count[i+1] = count[i+1]; } for (/*no init*/; istride[i] = -1; stride->count[i+1] = -1; } COMEX_ASSERT(stride->stride_levels >= 0); COMEX_ASSERT(stride->stride_levels < COMEX_MAX_STRIDE_LEVEL); #if DEBUG printf("[%d] nb_puts_packed stride_levels=%d, count[0]=%d\n", g_state.rank, stride_levels, count[0]); for (i=0; istride[i], i, stride->count[i+1]); } #endif packed_buffer = pack(src, src_stride, count, stride_levels, &packed_index); COMEX_ASSERT(NULL != packed_buffer); COMEX_ASSERT(packed_index > 0); { header_t *header = NULL; /* only fence on the master */ fence_array[proc] = 1; header = malloc(sizeof(header_t)); header->operation = OP_PUT_PACKED; header->remote_address = dst; header->local_address = NULL; header->rank = proc; header->length = packed_index; nb_send_header(header, sizeof(header_t), proc, nb); nb_send_header(stride, sizeof(stride_t), proc, nb); nb_send_header(packed_buffer, packed_index, proc, nb); } } STATIC void nb_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; #if DEBUG printf("[%d] nb_gets(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif /* if not actually a strided get */ if (0 == stride_levels) { nb_get(src, dst, count[0], proc, nb); return; } #if ENABLE_GET_PACKED #if ENABLE_GET_SELF /* if not a strided get from self, use packed algorithm */ if (g_state.rank != proc) #endif { nb_gets_packed(src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } dst_idx = 0; for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_get((char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } } STATIC void nb_gets_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i; stride_t *stride_src = NULL; stride_t *stride_dst = NULL; #if DEBUG printf("[%d] nb_gets_packed(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != count); COMEX_ASSERT(NULL != nb); COMEX_ASSERT(count[0] > 0); COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); /* copy src info into structure */ stride_src = malloc(sizeof(stride_t)); COMEX_ASSERT(stride_src); stride_src->ptr = src; stride_src->stride_levels = stride_levels; stride_src->count[0] = count[0]; for (i=0; istride[i] = src_stride[i]; stride_src->count[i+1] = count[i+1]; } for (/*no init*/; istride[i] = -1; stride_src->count[i+1] = -1; } COMEX_ASSERT(stride_src->stride_levels >= 0); COMEX_ASSERT(stride_src->stride_levels < COMEX_MAX_STRIDE_LEVEL); /* copy dst info into structure */ stride_dst = malloc(sizeof(stride_t)); COMEX_ASSERT(stride_dst); stride_dst->ptr = dst; stride_dst->stride_levels = stride_levels; stride_dst->count[0] = count[0]; for (i=0; istride[i] = dst_stride[i]; stride_dst->count[i+1] = count[i+1]; } for (/*no init*/; istride[i] = -1; stride_dst->count[i+1] = -1; } COMEX_ASSERT(stride_dst->stride_levels >= 0); COMEX_ASSERT(stride_dst->stride_levels < COMEX_MAX_STRIDE_LEVEL); { int recv_size = 0; char *packed_buffer = NULL; header_t *header = NULL; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = OP_GET_PACKED; header->remote_address = src; header->local_address = dst; header->rank = proc; header->length = 0; recv_size = packed_size(stride_dst->stride, stride_dst->count, stride_dst->stride_levels); COMEX_ASSERT(recv_size > 0); packed_buffer = malloc(recv_size); COMEX_ASSERT(packed_buffer); nb_recv_packed(packed_buffer, recv_size, proc, nb, stride_dst); nb_send_header(header, sizeof(header_t), proc, nb); nb_send_header(stride_src, sizeof(stride_t), proc, nb); } } STATIC void nb_accs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != nb); COMEX_ASSERT(NULL != scale); COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != count); COMEX_ASSERT(count[0] > 0); COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); #if DEBUG printf("[%d] nb_accs(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif /* if not actually a strided acc */ if (0 == stride_levels) { nb_acc(datatype, scale, src, dst, count[0], proc, nb); return; } #if ENABLE_ACC_PACKED #if ENABLE_ACC_SELF /* if not a strided acc to self, use packed algorithm */ if (g_state.rank != proc) #endif { nb_accs_packed(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } /* index mangling */ for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_acc(datatype, scale, (char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } } STATIC void nb_accs_packed( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i; int packed_index = 0; char *packed_buffer = NULL; stride_t *stride = NULL; #if DEBUG printf("[%d] nb_accs_packed(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != scale); COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != count); COMEX_ASSERT(NULL != nb); COMEX_ASSERT(count[0] > 0); COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); /* copy dst info into structure */ stride = malloc(sizeof(stride_t)); COMEX_ASSERT(stride); stride->ptr = dst; stride->stride_levels = stride_levels; stride->count[0] = count[0]; for (i=0; istride[i] = dst_stride[i]; stride->count[i+1] = count[i+1]; } /* assign remaining values to invalid */ for (/*no init*/; istride[i] = -1; stride->count[i+1] = -1; } COMEX_ASSERT(stride->stride_levels >= 0); COMEX_ASSERT(stride->stride_levels < COMEX_MAX_STRIDE_LEVEL); #if DEBUG printf("[%d] nb_accs_packed stride_levels=%d, count[0]=%d\n", g_state.rank, stride_levels, count[0]); for (i=0; istride[i], i, stride->count[i+1]); } #endif packed_buffer = pack(src, src_stride, count, stride_levels, &packed_index); COMEX_ASSERT(NULL != packed_buffer); COMEX_ASSERT(packed_index > 0); { header_t *header = NULL; int scale_size = 0; op_t operation = 0; switch (datatype) { case COMEX_ACC_INT: operation = OP_ACC_INT_PACKED; scale_size = sizeof(int); break; case COMEX_ACC_DBL: operation = OP_ACC_DBL_PACKED; scale_size = sizeof(double); break; case COMEX_ACC_FLT: operation = OP_ACC_FLT_PACKED; scale_size = sizeof(float); break; case COMEX_ACC_CPL: operation = OP_ACC_CPL_PACKED; scale_size = sizeof(SingleComplex); break; case COMEX_ACC_DCP: operation = OP_ACC_DCP_PACKED; scale_size = sizeof(DoubleComplex); break; case COMEX_ACC_LNG: operation = OP_ACC_LNG_PACKED; scale_size = sizeof(long); break; default: COMEX_ASSERT(0); } /* only fence on the master */ fence_array[proc] = 1; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = operation; header->remote_address = dst; header->local_address = NULL; header->rank = proc; header->length = packed_index; nb_send_header(header, sizeof(header_t), proc, nb); nb_send_buffer(scale, scale_size, proc, nb); nb_send_header(stride, sizeof(stride_t), proc, nb); nb_send_header(packed_buffer, packed_index, proc, nb); } } STATIC void nb_putv( comex_giov_t *iov, int iov_len, int proc, nb_t *nb) { int i = 0; for (i=0; isrc; dst = iov->dst; bytes = iov->bytes; limit = iov->count; #if DEBUG printf("[%d] nb_putv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif /* allocate compressed iov */ iov_size = 2*limit*sizeof(void*) + 2*sizeof(int); iov_buf = malloc(iov_size); COMEX_ASSERT(iov_buf); iov_off = 0; /* copy limit */ (void)memcpy(&iov_buf[iov_off], &limit, sizeof(int)); iov_off += sizeof(int); /* copy bytes */ (void)memcpy(&iov_buf[iov_off], &bytes, sizeof(int)); iov_off += sizeof(int); /* copy src pointers */ (void)memcpy(&iov_buf[iov_off], src, limit*sizeof(void*)); iov_off += limit*sizeof(void*); /* copy dst pointers */ (void)memcpy(&iov_buf[iov_off], dst, limit*sizeof(void*)); iov_off += limit*sizeof(void*); COMEX_ASSERT(iov_off == iov_size); /* allocate send buffer */ packed_size = bytes * limit; packed_buffer = malloc(packed_size); COMEX_ASSERT(packed_buffer); packed_index = 0; for (i=0; ioperation = OP_PUT_IOV; header->remote_address = NULL; header->local_address = NULL; header->rank = proc; header->length = iov_size; nb_send_header(header, sizeof(header_t), proc, nb); nb_send_header(iov_buf, iov_size, proc, nb); nb_send_header(packed_buffer, packed_size, proc, nb); } } STATIC void nb_getv( comex_giov_t *iov, int iov_len, int proc, nb_t *nb) { int i = 0; for (i=0; isrc; dst = iov->dst; bytes = iov->bytes; limit = iov->count; #if DEBUG printf("[%d] nb_getv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif /* allocate compressed iov */ iov_size = 2*limit*sizeof(void*) + 2*sizeof(int); iov_buf = malloc(iov_size); iov_off = 0; COMEX_ASSERT(iov_buf); /* copy limit */ (void)memcpy(&iov_buf[iov_off], &limit, sizeof(int)); iov_off += sizeof(int); /* copy bytes */ (void)memcpy(&iov_buf[iov_off], &bytes, sizeof(int)); iov_off += sizeof(int); /* copy src pointers */ (void)memcpy(&iov_buf[iov_off], src, limit*sizeof(void*)); iov_off += limit*sizeof(void*); /* copy dst pointers */ (void)memcpy(&iov_buf[iov_off], dst, limit*sizeof(void*)); iov_off += limit*sizeof(void*); COMEX_ASSERT(iov_off == iov_size); /* copy given iov for later */ iov_copy = malloc(sizeof(comex_giov_t)); iov_copy->bytes = bytes; iov_copy->count = limit; iov_copy->src = malloc(sizeof(void*)*iov->count); COMEX_ASSERT(iov_copy->src); (void)memcpy(iov_copy->src, iov->src, sizeof(void*)*iov->count); iov_copy->dst = malloc(sizeof(void*)*iov->count); COMEX_ASSERT(iov_copy->dst); (void)memcpy(iov_copy->dst, iov->dst, sizeof(void*)*iov->count); #if DEBUG printf("[%d] nb_getv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p copy\n", g_state.rank, iov_copy->count, iov_copy->bytes, iov_copy->src[0], iov_copy->dst[0]); #endif /* allocate recv buffer */ packed_size = bytes * limit; packed_buffer = malloc(packed_size); COMEX_ASSERT(packed_buffer); { header_t *header = NULL; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = OP_GET_IOV; header->remote_address = NULL; header->local_address = NULL; header->rank = proc; header->length = iov_size; nb_recv_iov(packed_buffer, packed_size, proc, nb, iov_copy); nb_send_header(header, sizeof(header_t), proc, nb); nb_send_header(iov_buf, iov_size, proc, nb); } } STATIC void nb_accv( int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, nb_t *nb) { int i = 0; for (i=0; isrc; dst = iov->dst; bytes = iov->bytes; limit = iov->count; #if DEBUG printf("[%d] nb_accv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif /* allocate compressed iov */ iov_size = 2*limit*sizeof(void*) + 2*sizeof(int); iov_buf = malloc(iov_size); iov_off = 0; COMEX_ASSERT(iov_buf); /* copy limit */ (void)memcpy(&iov_buf[iov_off], &limit, sizeof(int)); iov_off += sizeof(int); /* copy bytes */ (void)memcpy(&iov_buf[iov_off], &bytes, sizeof(int)); iov_off += sizeof(int); /* copy src pointers */ (void)memcpy(&iov_buf[iov_off], src, limit*sizeof(void*)); iov_off += limit*sizeof(void*); /* copy dst pointers */ (void)memcpy(&iov_buf[iov_off], dst, limit*sizeof(void*)); iov_off += limit*sizeof(void*); COMEX_ASSERT(iov_off == iov_size); /* allocate send buffer */ packed_size = bytes * limit; packed_buffer = malloc(packed_size); COMEX_ASSERT(packed_buffer); packed_index = 0; for (i=0; ioperation = operation; header->remote_address = NULL; header->local_address = NULL; header->rank = proc; header->length = iov_size; nb_send_header(header, sizeof(header_t), proc, nb); nb_send_buffer(scale, scale_size, proc, nb); nb_send_header(iov_buf, iov_size, proc, nb); nb_send_header(packed_buffer, packed_size, proc, nb); } } STATIC int packed_size(int *src_stride, int *count, int stride_levels) { int size; int i; int n1dim; /* number of 1 dim block */ COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); COMEX_ASSERT(NULL != src_stride); COMEX_ASSERT(NULL != count); COMEX_ASSERT(count[0] > 0); #if DEBUG printf("[%d] packed_size(src_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, src_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* allocate packed buffer now that we know the size */ size = n1dim * count[0]; return size; } STATIC char* pack( char *src, int *src_stride, int *count, int stride_levels, int *size) { int i, j; long src_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int packed_index = 0; char *packed_buffer = NULL; stride_t stride; COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != src_stride); COMEX_ASSERT(NULL != count); COMEX_ASSERT(count[0] > 0); COMEX_ASSERT(NULL != size); #if DEBUG printf("[%d] pack(src=%p, src_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, src, src_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* allocate packed buffer now that we know the size */ packed_buffer = malloc(n1dim * count[0]); COMEX_ASSERT(packed_buffer); /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } (void)memcpy(&packed_buffer[packed_index], &src[src_idx], count[0]); packed_index += count[0]; } COMEX_ASSERT(packed_index == n1dim*count[0]); *size = packed_index; return packed_buffer; } STATIC void unpack(char *packed_buffer, char *dst, int *dst_stride, int *count, int stride_levels) { int i, j; long dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int dst_bvalue[7], dst_bunit[7]; int packed_index = 0; stride_t stride; COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); COMEX_ASSERT(NULL != packed_buffer); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != dst_stride); COMEX_ASSERT(NULL != count); COMEX_ASSERT(count[0] > 0); #if DEBUG printf("[%d] unpack(dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, dst, dst_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { dst_bvalue[i] = 0; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { dst_bvalue[j] = 0; } } (void)memcpy(&dst[dst_idx], &packed_buffer[packed_index], count[0]); packed_index += count[0]; } COMEX_ASSERT(packed_index == n1dim*count[0]); } STATIC size_t get_scale_size(op_t operation) { size_t sizeof_scale = 0; switch (operation) { case OP_ACC_INT: sizeof_scale = sizeof(int); break; case OP_ACC_DBL: sizeof_scale = sizeof(double); break; case OP_ACC_FLT: sizeof_scale = sizeof(float); break; case OP_ACC_CPL: sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP: sizeof_scale = sizeof(DoubleComplex); break; case OP_ACC_LNG: sizeof_scale = sizeof(long); break; case OP_ACC_INT_PACKED: sizeof_scale = sizeof(int); break; case OP_ACC_DBL_PACKED: sizeof_scale = sizeof(double); break; case OP_ACC_FLT_PACKED: sizeof_scale = sizeof(float); break; case OP_ACC_CPL_PACKED: sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP_PACKED: sizeof_scale = sizeof(DoubleComplex); break; case OP_ACC_LNG_PACKED: sizeof_scale = sizeof(long); break; case OP_ACC_INT_IOV: sizeof_scale = sizeof(int); break; case OP_ACC_DBL_IOV: sizeof_scale = sizeof(double); break; case OP_ACC_FLT_IOV: sizeof_scale = sizeof(float); break; case OP_ACC_CPL_IOV: sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP_IOV: sizeof_scale = sizeof(DoubleComplex); break; case OP_ACC_LNG_IOV: sizeof_scale = sizeof(long); break; default: COMEX_ASSERT(0); } return sizeof_scale; } STATIC int get_acc_type(op_t operation) { int type = 0; switch (operation) { case OP_ACC_INT: type = COMEX_ACC_INT; break; case OP_ACC_DBL: type = COMEX_ACC_DBL; break; case OP_ACC_FLT: type = COMEX_ACC_FLT; break; case OP_ACC_CPL: type = COMEX_ACC_CPL; break; case OP_ACC_DCP: type = COMEX_ACC_DCP; break; case OP_ACC_LNG: type = COMEX_ACC_LNG; break; case OP_ACC_INT_PACKED: type = COMEX_ACC_INT; break; case OP_ACC_DBL_PACKED: type = COMEX_ACC_DBL; break; case OP_ACC_FLT_PACKED: type = COMEX_ACC_FLT; break; case OP_ACC_CPL_PACKED: type = COMEX_ACC_CPL; break; case OP_ACC_DCP_PACKED: type = COMEX_ACC_DCP; break; case OP_ACC_LNG_PACKED: type = COMEX_ACC_LNG; break; case OP_ACC_INT_IOV: type = COMEX_ACC_INT; break; case OP_ACC_DBL_IOV: type = COMEX_ACC_DBL; break; case OP_ACC_FLT_IOV: type = COMEX_ACC_FLT; break; case OP_ACC_CPL_IOV: type = COMEX_ACC_CPL; break; case OP_ACC_DCP_IOV: type = COMEX_ACC_DCP; break; case OP_ACC_LNG_IOV: type = COMEX_ACC_LNG; break; default: COMEX_ASSERT(0); } return type; } STATIC void print_scale(op_t operation, void *scale) { switch (operation) { case OP_ACC_INT: printf("[%d] scale=%d\n", g_state.rank, *((int*)scale)); break; case OP_ACC_DBL: printf("[%d] scale=%f\n", g_state.rank, *((double*)scale)); break; case OP_ACC_FLT: printf("[%d] scale=%f\n", g_state.rank, *((float*)scale)); break; case OP_ACC_CPL: printf("[%d] scale=(%f,%f)\n", g_state.rank, ((SingleComplex*)scale)->real, ((SingleComplex*)scale)->imag); break; case OP_ACC_DCP: printf("[%d] scale=(%f,%f)\n", g_state.rank, ((DoubleComplex*)scale)->real, ((DoubleComplex*)scale)->imag); break; case OP_ACC_LNG: printf("[%d] scale=%ld\n", g_state.rank, *((long*)scale)); break; case OP_ACC_INT_PACKED: printf("[%d] scale=%d\n", g_state.rank, *((int*)scale)); break; case OP_ACC_DBL_PACKED: printf("[%d] scale=%f\n", g_state.rank, *((double*)scale)); break; case OP_ACC_FLT_PACKED: printf("[%d] scale=%f\n", g_state.rank, *((float*)scale)); break; case OP_ACC_CPL_PACKED: printf("[%d] scale=(%f,%f)\n", g_state.rank, ((SingleComplex*)scale)->real, ((SingleComplex*)scale)->imag); break; case OP_ACC_DCP_PACKED: printf("[%d] scale=(%f,%f)\n", g_state.rank, ((DoubleComplex*)scale)->real, ((DoubleComplex*)scale)->imag); break; case OP_ACC_LNG_PACKED: printf("[%d] scale=%ld\n", g_state.rank, *((long*)scale)); break; case OP_ACC_INT_IOV: printf("[%d] scale=%d\n", g_state.rank, *((int*)scale)); break; case OP_ACC_DBL_IOV: printf("[%d] scale=%f\n", g_state.rank, *((double*)scale)); break; case OP_ACC_FLT_IOV: printf("[%d] scale=%f\n", g_state.rank, *((float*)scale)); break; case OP_ACC_CPL_IOV: printf("[%d] scale=(%f,%f)\n", g_state.rank, ((SingleComplex*)scale)->real, ((SingleComplex*)scale)->imag); break; case OP_ACC_DCP_IOV: printf("[%d] scale=(%f,%f)\n", g_state.rank, ((DoubleComplex*)scale)->real, ((DoubleComplex*)scale)->imag); break; case OP_ACC_LNG_IOV: printf("[%d] scale=%ld\n", g_state.rank, *((long*)scale)); break; default: COMEX_ASSERT(0); } } ga-5.9.2/comex/src-mpi-mt/comex_impl.h000066400000000000000000000030471500715745200175400ustar00rootroot00000000000000#ifndef COMEX_IMPL_H_ #define COMEX_IMPL_H_ #include "groups.h" #define COMEX_MAX_NB_OUTSTANDING 8 #define COMEX_MAX_STRIDE_LEVEL 8 #define COMEX_TAG 27624 #define COMEX_STATIC_BUFFER_SIZE (2u*1048576u) #define UNLOCKED -1 /* performance or correctness related settings */ #define NEED_ASM_VOLATILE_MEMORY 0 #define COMEX_SET_AFFINITY 0 #define ENABLE_PUT_SELF 1 #define ENABLE_GET_SELF 1 #define ENABLE_ACC_SELF 1 #define ENABLE_PUT_PACKED 1 #define ENABLE_GET_PACKED 1 #define ENABLE_ACC_PACKED 1 #define ENABLE_PUT_IOV 1 #define ENABLE_GET_IOV 1 #define ENABLE_ACC_IOV 1 #define DEBUG 0 #define DEBUG_VERBOSE 0 #define DEBUG_TO_FILE 0 #if DEBUG_TO_FILE FILE *comex_trace_file; # define printf(...) fprintf(comex_trace_file, __VA_ARGS__); fflush(comex_trace_file) #else # define printf(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) #endif #define COMEX_STRINGIFY(x) #x #ifdef NDEBUG #define COMEX_ASSERT(WHAT) ((void) (0)) #else #define COMEX_ASSERT(WHAT) \ ((WHAT) \ ? (void) (0) \ : comex_assert_fail (COMEX_STRINGIFY(WHAT), __FILE__, __LINE__, __func__)) #endif static inline void comex_assert_fail( const char *assertion, const char *file, unsigned int line, const char *function) { fprintf(stderr, "[%d] %s:%u: %s: Assertion `%s' failed", g_state.rank, file, line, function, assertion); #if DEBUG printf("[%d] %s:%u: %s: Assertion `%s' failed", g_state.rank, file, line, function, assertion); #endif comex_error("comex_assert_fail", -1); } #endif /* COMEX_IMPL_H_ */ ga-5.9.2/comex/src-mpi-mt/groups.c000066400000000000000000000327411500715745200167210ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #if defined(__CRAYXE) # include #endif #include "comex.h" #include "comex_impl.h" #include "groups.h" /* world group state */ comex_group_world_t g_state = { MPI_COMM_NULL, MPI_GROUP_NULL, -1, -1, NULL, MPI_COMM_NULL, -1, -1 }; /* the HEAD of the group linked list */ comex_igroup_t *group_list = NULL; #define RANK_OR_PID (g_state.rank >= 0 ? g_state.rank : getpid()) /* static functions implemented in this file */ static void _create_group_and_igroup(comex_group_t *id, comex_igroup_t **igroup); static void _igroup_free(comex_igroup_t *igroup); static long xgethostid(); /** * Return the comex igroup instance given the group id. * * The group linked list is searched sequentially until the given group * is found. It is an error if this function is called before * comex_group_init(). An error occurs if the given group is not found. */ comex_igroup_t* comex_get_igroup_from_group(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; #if DEBUG printf("[%d] comex_get_igroup_from_group(%d)\n", RANK_OR_PID, id); #endif COMEX_ASSERT(group_list != NULL); while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { return current_group_list_item; } current_group_list_item = current_group_list_item->next; } comex_error("comex group lookup failed", -1); return NULL; } /** * Creates and associates a comex group with a comex igroup. * * This does *not* initialize the members of the comex igroup. */ static void _create_group_and_igroup( comex_group_t *id, comex_igroup_t **igroup) { comex_igroup_t *new_group_list_item = NULL; comex_igroup_t *last_group_list_item = NULL; #if DEBUG printf("[%d] _create_group_and_igroup(...)\n", RANK_OR_PID); #endif /* create, init, and insert the new node for the linked list */ new_group_list_item = malloc(sizeof(comex_igroup_t)); new_group_list_item->next = NULL; new_group_list_item->id = -1; new_group_list_item->comm = MPI_COMM_NULL; new_group_list_item->group = MPI_GROUP_NULL; new_group_list_item->size = -1; new_group_list_item->rank = -1; /* find the last group in the group linked list and insert */ if (group_list) { last_group_list_item = group_list; while (last_group_list_item->next != NULL) { last_group_list_item = last_group_list_item->next; } last_group_list_item->next = new_group_list_item; new_group_list_item->id = last_group_list_item->id + 1; } else { group_list = new_group_list_item; new_group_list_item->id = COMEX_GROUP_WORLD; } /* return the group id and comex igroup */ *igroup = new_group_list_item; *id = new_group_list_item->id; } int comex_group_rank(comex_group_t group, int *rank) { comex_igroup_t *igroup = comex_get_igroup_from_group(group); *rank = igroup->rank; #if DEBUG printf("[%d] comex_group_rank(group=%d, *rank=%d)\n", RANK_OR_PID, group, *rank); #endif return COMEX_SUCCESS; } int comex_group_size(comex_group_t group, int *size) { comex_igroup_t *igroup = comex_get_igroup_from_group(group); *size = igroup->size; #if DEBUG printf("[%d] comex_group_size(group=%d, *size=%d)\n", RANK_OR_PID, group, *size); #endif return COMEX_SUCCESS; } int comex_group_comm(comex_group_t group, MPI_Comm *comm) { comex_igroup_t *igroup = comex_get_igroup_from_group(group); *comm = igroup->comm; #if DEBUG printf("[%d] comex_group_comm(group=%d, comm)\n", RANK_OR_PID, group); #endif return COMEX_SUCCESS; } int comex_group_translate_world( comex_group_t group, int group_rank, int *world_rank) { #if DEBUG printf("[%d] comex_group_translate_world(" "group=%d, group_rank=%d, world_rank)\n", RANK_OR_PID, group, group_rank); #endif if (COMEX_GROUP_WORLD == group) { *world_rank = group_rank; } else { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); COMEX_ASSERT(group_list); /* first group is world worker group */ status = MPI_Group_translate_ranks(igroup->group, 1, &group_rank, group_list->group, world_rank); } return COMEX_SUCCESS; } /** * Destroys the given comex igroup. */ static void _igroup_free(comex_igroup_t *igroup) { int status; #if DEBUG printf("[%d] _igroup_free\n", RANK_OR_PID); #endif COMEX_ASSERT(igroup); if (igroup->group != MPI_GROUP_NULL) { status = MPI_Group_free(&igroup->group); if (status != MPI_SUCCESS) { comex_error("MPI_Group_free: Failed ", status); } } #if DEBUG printf("[%d] free'd group\n", RANK_OR_PID); #endif if (igroup->comm != MPI_COMM_NULL) { status = MPI_Comm_free(&igroup->comm); if (status != MPI_SUCCESS) { comex_error("MPI_Comm_free: Failed ", status); } } #if DEBUG printf("[%d] free'd comm\n", RANK_OR_PID); #endif free(igroup); } int comex_group_free(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; #if DEBUG printf("[%d] comex_group_free(id=%d)\n", RANK_OR_PID, id); #endif /* find the group to free */ while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { break; } previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; } /* make sure we found a group */ COMEX_ASSERT(current_group_list_item != NULL); /* remove the group from the linked list */ if (previous_group_list_item != NULL) { previous_group_list_item->next = current_group_list_item->next; } /* free the igroup */ _igroup_free(current_group_list_item); return COMEX_SUCCESS; } int comex_group_create( int n, int *pid_list, comex_group_t id_parent, comex_group_t *id_child) { int status = 0; int grp_me = 0; comex_igroup_t *igroup_child = NULL; MPI_Group *group_child = NULL; MPI_Comm *comm_child = NULL; comex_igroup_t *igroup_parent = NULL; MPI_Group *group_parent = NULL; MPI_Comm *comm_parent = NULL; #if DEBUG printf("[%d] comex_group_create(" "n=%d, pid_list=%p, id_parent=%d, id_child)\n", RANK_OR_PID, n, pid_list, id_parent); { int p; printf("[%d] pid_list={%d", RANK_OR_PID, pid_list[0]); for (p=1; pgroup); comm_child = &(igroup_child->comm); /* get the parent's MPI_Group and MPI_Comm */ igroup_parent = comex_get_igroup_from_group(id_parent); group_parent = &(igroup_parent->group); comm_parent = &(igroup_parent->comm); status = MPI_Group_incl(*group_parent, n, pid_list, group_child); COMEX_ASSERT(MPI_SUCCESS == status); #if DEBUG printf("[%d] comex_group_create before crazy logic\n", RANK_OR_PID); #endif { MPI_Comm comm, comm1, comm2; int lvl=1, local_ldr_pos; status = MPI_Group_rank(*group_child, &grp_me); COMEX_ASSERT(MPI_SUCCESS == status); if (grp_me == MPI_UNDEFINED) { /* FIXME: keeping the group around for now */ #if DEBUG printf("[%d] comex_group_create aborting -- not in group\n", RANK_OR_PID); #endif return COMEX_SUCCESS; } /* SK: sanity check for the following bitwise operations */ COMEX_ASSERT(grp_me>=0); /* FIXME: can be optimized away */ status = MPI_Comm_dup(MPI_COMM_SELF, &comm); COMEX_ASSERT(MPI_SUCCESS == status); local_ldr_pos = grp_me; while(n>lvl) { int tag=0; int remote_ldr_pos = local_ldr_pos^lvl; if (remote_ldr_pos < n) { int remote_leader = pid_list[remote_ldr_pos]; MPI_Comm peer_comm = *comm_parent; int high = (local_ldr_poscomm, &(igroup_child->size)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_rank(igroup_child->comm, &(igroup_child->rank)); COMEX_ASSERT(MPI_SUCCESS == status); } #if DEBUG printf("[%d] comex_group_create after crazy logic\n", RANK_OR_PID); #endif return COMEX_SUCCESS; } static int cmplong(const void *p1, const void *p2) { return *((long*)p1) - *((long*)p2); } /** * Initialize group linked list. Prepopulate with world group. */ void comex_group_init(MPI_Comm comm) { int status = 0; int i = 0; comex_group_t group = 0; comex_igroup_t *igroup = NULL; long *sorted = NULL; int count = 0; /* populate g_state */ /* dup MPI_COMM_WORLD and get group, rank, and size */ status = MPI_Comm_dup(comm, &(g_state.comm)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_group(g_state.comm, &(g_state.group)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_rank(g_state.comm, &(g_state.rank)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_size(g_state.comm, &(g_state.size)); COMEX_ASSERT(MPI_SUCCESS == status); #if DEBUG_TO_FILE { char pathname[80]; sprintf(pathname, "trace.%d.log", g_state.rank); comex_trace_file = fopen(pathname, "w"); COMEX_ASSERT(NULL != comex_trace_file); printf("[%d] comex_group_init()\n", RANK_OR_PID); } #endif g_state.hostid = (long*)malloc(sizeof(long)*g_state.size); g_state.hostid[g_state.rank] = xgethostid(); status = MPI_Allgather(MPI_IN_PLACE, 1, MPI_LONG, g_state.hostid, 1, MPI_LONG, g_state.comm); COMEX_ASSERT(MPI_SUCCESS == status); COMEX_ASSERT(group_list == NULL); /* create the head of the group linked list */ _create_group_and_igroup(&group, &igroup); /* create a comm of only the workers (every rank is a worker) */ status = MPI_Comm_dup(comm, &(igroup->comm)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_group(igroup->comm, &(igroup->group)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_rank(igroup->comm, &(igroup->rank)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_size(igroup->comm, &(igroup->size)); COMEX_ASSERT(MPI_SUCCESS == status); /* create node comm */ /* MPI_Comm_split requires a non-negative color, * so sort and sanitize */ sorted = (long*)malloc(sizeof(long) * g_state.size); (void)memcpy(sorted, g_state.hostid, sizeof(long)*g_state.size); qsort(sorted, g_state.size, sizeof(long), cmplong); for (i=0; inext; _igroup_free(previous_group_list_item); } free(g_state.hostid); status = MPI_Comm_free(&(g_state.node_comm)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Group_free(&(g_state.group)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_free(&(g_state.comm)); COMEX_ASSERT(MPI_SUCCESS == status); } static long xgethostid() { #if defined(__CRAYXE) #warning CRAY int nodeid; PMI_Get_nid(g_state.rank, &nodeid); #else long nodeid = gethostid(); #endif return nodeid; } ga-5.9.2/comex/src-mpi-mt/groups.h000066400000000000000000000035761500715745200167320ustar00rootroot00000000000000/** * Private header file for comex groups backed by MPI_comm. * * The rest of the comex group functions are defined in the public comex.h. * * @author Jeff Daily */ #ifndef _COMEX_GROUPS_H_ #define _COMEX_GROUPS_H_ #include #include "comex.h" typedef struct { MPI_Comm comm; /**< whole comm; all ranks */ MPI_Group group;/**< whole group; all ranks */ int size; /**< comm size */ int rank; /**< comm rank */ long *hostid; /**< hostid[size] hostid of SMP node for a given rank */ MPI_Comm node_comm; /**< node comm; SMP ranks */ int node_size; /**< node comm size */ int node_rank; /**< node comm rank */ } comex_group_world_t; extern comex_group_world_t g_state; typedef struct group_link { struct group_link *next;/**< next group in linked list */ comex_group_t id; /**< user-space id for this group */ MPI_Comm comm; /**< whole comm; all ranks */ MPI_Group group; /**< whole group; all ranks */ int size; /**< comm size */ int rank; /**< comm rank */ } comex_igroup_t; /** list of worker groups */ extern comex_igroup_t *group_list; extern void comex_group_init(MPI_Comm comm); extern void comex_group_finalize(); extern comex_igroup_t* comex_get_igroup_from_group(comex_group_t group); /* verify that proc is part of group */ #define CHECK_GROUP(GROUP,PROC) do { \ int size; \ int ierr = comex_group_size(GROUP,&size); \ COMEX_ASSERT(GROUP >= 0); \ COMEX_ASSERT(COMEX_SUCCESS == ierr); \ COMEX_ASSERT(PROC >= 0); \ COMEX_ASSERT(PROC < size); \ } while(0) #endif /* _COMEX_GROUPS_H_ */ ga-5.9.2/comex/src-mpi-pr/000077500000000000000000000000001500715745200152305ustar00rootroot00000000000000ga-5.9.2/comex/src-mpi-pr/Makefile.inc000066400000000000000000000006101500715745200174350ustar00rootroot00000000000000libcomex_la_SOURCES += src-mpi-pr/comex.c libcomex_la_SOURCES += src-mpi-pr/comex_impl.h libcomex_la_SOURCES += src-mpi-pr/groups.c libcomex_la_SOURCES += src-mpi-pr/groups.h libcomex_la_SOURCES += src-mpi-pr/reg_cache.c libcomex_la_SOURCES += src-mpi-pr/reg_cache.h AM_CPPFLAGS += -I$(top_srcdir)/src-mpi-pr check_PROGRAMS += src-mpi-pr/hello src_mpi_pr_hello_SOURCES = src-mpi-pr/hello.c ga-5.9.2/comex/src-mpi-pr/NOTES.md000066400000000000000000000127111500715745200164440ustar00rootroot00000000000000# MPI Progress Ranks (MPI-PR) NOTES on generalization of number of Progress-Ranks per computing node ------ Number of Progress-Ranks per node can be suitably User has the flexibility to choose the number of Progress-Ranks per node during the execution of a program. An environment variable GA_NUM_PROGRESS_RANKS_PER_NODE will capture the number to be set for number of Progress-Ranks per node. If not set in the environment, then this number will set to 1 by default. e.g. of defining this variable in the environment during execution: ``` $ export GA_NUM_PROGRESS_RANKS_PER_NODE=1 $ export GA_NUM_PROGRESS_RANKS_PER_NODE=2 $ export GA_NUM_PROGRESS_RANKS_PER_NODE=4 $ setenv GA_NUM_PROGRESS_RANKS_PER_NODE 2 ``` CAUTION: For optimum performance, number of MPI ranks per node used to execute a program should be in multiples of value set for GA_NUM_PROGRESS_RANKS_PER_NODE. With the use of two optional environmental variables, it is possible to set the GA-Ranks in one group managed by Progress-Rank in either PACKED or CYCLIC distribution pattern. Following example shows an application run with 8 MPI processes/node with 2-PRs. Total 8 MPI ranks will be divided into two groups and by default, the highest rank will be the Progress-Rank of each group. PACKED distribution ``` $ export GA_NUM_PROGRESS_RANKS_PER_NODE=2 $ export GA_PROGRESS_RANKS_DISTRIBUTION_PACKED=1 ``` or ``` $ export GA_NUM_PROGRESS_RANKS_PER_NODE=2 $ export GA_PROGRESS_RANKS_DISTRIBUTION_PACKED=Y ``` Above environmental settings sets 2 Progress-Ranks/node and PACKED distribution The two groups are (0,1,2,3) and (4,5,6,7) Here, MPI ranks 3 and 7 are Progress-Ranks on the node Next, CYCLIC distribution ``` $ export GA_NUM_PROGRESS_RANKS_PER_NODE=2 $ export GA_PROGRESS_RANKS_DISTRIBUTION_PACKED=0 $ export GA_PROGRESS_RANKS_DISTRIBUTION_CYCLIC=1 ``` Please make sure to set GA_PROGRESS_RANKS_DISTRIBUTION_PACKED=0 if previously used. Above environmental settings sets 2 Progress-Ranks/node and CYCLIC distribution The two groups are (0,2,4,6) and (1,3,5,7) Here, MPI ranks 6 and 7 are Progress-Ranks on the node Other notes on MPI Progress-Rank ------ These are notes describing the MPI Progress ranks runtime. These notes are intended to help developers navigate the contents of these files and to locate specific functionality. The MPI-PR is intended to be the highest-performing MPI-1 compatible ARMCI/ComEx runtime. It uses only features from the MPI-1 standard and provides asynchronous progress. Posix shared memory is used extensively. Asynchronous progress is made by reserving one MPI rank per compute node, taken from MPI_COMM_WORLD, and using posix shared memory between the reserved "progress rank" and the remaining ranks on the associated compute node. The world communicator is split using `MPI_Comm_split()` and using the result of `gethostid()` to separate the user/worker ranks from the progress ranks. The progress rank is the largest rank on each compute node, unless you set `MASTER_IS_SMALLEST_SMP_RANK` in the [comex_impl.h](comex_impl.h) file to 1. When a user's get/put/acc request is made, it is sent as a small MPI message to the progress rank. The progress rank interprets the header and processes the request, copying from/to the shared memory of one of the MPI ranks the progress rank is managing on its compute node. The code that implements the progress engine can be found in the `_progress_server` function located in the [comex.c](comex.c) file. Incoming requests to the progress rank are all based on the active message concept. A 'header' message is sent first to the progress engine indicating the type of request, e.g., OP_PUT, OP_ACC_INT. A complete listing of the request types is defined in an enumerated list at the top of [comex.c](comex.c). The header contains enough information to complete the request such as source and destination pointers, source and destination ranks, etc. After a header message is sent, any data payload is sent as a separate message. The intent was to let MPI directly use the buffer pointers in case the buffers were allocated using any special, network-specific allocator. Otherwise, a data payload could have been mem-copied to the end of the header message (this is done in the MPI-PR implementation as an optimization). Posix shared memory is used between all ranks on a compute node, including the reserved progress rank. When `comex_malloc` is called (collectively), it calls `comex_malloc_local` that creates the shared memory buffer on each user-level MPI rank. The posix shmem names associated with each buffer is collectively exchanged with all ranks on the node so that all ranks on the same node can access each other's memory directly. The progress rank does not allocate memory, but rather attaches to all segments allocated on it's node-local ranks. The shmem name is guaranteed to be unique to the UID and PID and uses an internal counter. There are a finite number of user-level non-blocking handles. This is set using the environment variable COMEX_MAX_NB_OUTSTANDING. This controls the size of an allocated array of our non-blocking handle data structure nb_t. The nb_t structure contains linked lists of MPI_Request objects associated with the given user-level handle. It is slightly more complicated than that since get requests might be using the packing optimization where the request is first compressed into a contiguous buffer. The stride information is kept with the nb_t message so that the received buffer can be unpacked. All memory is freed when operations complete. ga-5.9.2/comex/src-mpi-pr/comex.c000066400000000000000000006771631500715745200165330ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* C and/or system headers */ #include #include #include #include #include #include #include #include #if HAVE_ERRNO_H # include #endif #include #include #include #include #include #include #include #include #include /* System V headers */ // #define ENABLE_SYSV #if ENABLE_SYSV #include #include #endif /* 3rd party headers */ #include #if USE_SICM #include //#include sicm_device_list nill; #endif /* our headers */ #include "comex.h" #include "comex_impl.h" #include "groups.h" #include "reg_cache.h" #include "acc.h" #define ENABLE_FTOK 1 #define XSTR(x) #x #define STR(x) XSTR(x) #define PAUSE_ON_ERROR 0 #define STATIC static inline #if USE_MEMSET_AFTER_MALLOC #define MAYBE_MEMSET(a,b,c) (void)memset(a,b,c) #else #define MAYBE_MEMSET(a,b,c) ((void)0) #endif #define XSTR(x) #x #define STR(x) XSTR(x) #define MIN(a, b) (((b) < (a)) ? (b) : (a)) #ifndef HOST_NAME_MAX #define HOST_NAME_MAX 256 #endif /* data structures */ typedef enum { OP_PUT = 0, OP_PUT_PACKED, OP_PUT_DATATYPE, OP_PUT_IOV, OP_GET, OP_GET_PACKED, OP_GET_DATATYPE, OP_GET_IOV, OP_ACC_INT, OP_ACC_DBL, OP_ACC_FLT, OP_ACC_CPL, OP_ACC_DCP, OP_ACC_LNG, OP_ACC_INT_PACKED, OP_ACC_DBL_PACKED, OP_ACC_FLT_PACKED, OP_ACC_CPL_PACKED, OP_ACC_DCP_PACKED, OP_ACC_LNG_PACKED, OP_ACC_INT_IOV, OP_ACC_DBL_IOV, OP_ACC_FLT_IOV, OP_ACC_CPL_IOV, OP_ACC_DCP_IOV, OP_ACC_LNG_IOV, OP_FENCE, OP_FETCH_AND_ADD, OP_SWAP, OP_CREATE_MUTEXES, OP_DESTROY_MUTEXES, OP_LOCK, OP_UNLOCK, OP_QUIT, OP_MALLOC, OP_FREE, OP_NULL } op_t; typedef struct { op_t operation; void *remote_address; void *local_address; int rank; /**< rank of target (rank of sender is iprobe_status.MPI_SOURCE */ int length; /**< length of message/payload not including header */ } header_t; /* keep track of all mutex requests */ typedef struct lock_link { struct lock_link *next; int rank; } comex_lock_t; typedef struct { void *ptr; int stride_levels; int stride[COMEX_MAX_STRIDE_LEVEL]; int count[COMEX_MAX_STRIDE_LEVEL+1]; } stride_t; typedef struct message_link { struct message_link *next; void *message; MPI_Request request; MPI_Datatype datatype; int need_free; stride_t *stride; comex_giov_t *iov; } message_t; typedef struct { int in_use; int send_size; message_t *send_head; message_t *send_tail; int recv_size; message_t *recv_head; message_t *recv_tail; } nb_t; typedef struct { int rank; void *ptr; } rank_ptr_t; /* static state */ static int *num_mutexes = NULL; /**< (all) how many mutexes on each process */ static int **mutexes = NULL; /**< (masters) value is rank of lock holder */ static comex_lock_t ***lq_heads = NULL; /**< array of lock queues */ static char *sem_name = NULL; /* local semaphore name */ static sem_t **semaphores = NULL; /* semaphores for locking within SMP node */ static int initialized = 0; /* for comex_initialized(), 0=false */ static char *fence_array = NULL; static nb_t *nb_state = NULL; /* keep track of all nonblocking operations */ static int nb_max_outstanding = COMEX_MAX_NB_OUTSTANDING; static int nb_index = 0; static int nb_count_event = 0; static int nb_count_event_processed = 0; static int nb_count_send = 0; static int nb_count_send_processed = 0; static int nb_count_recv = 0; static int nb_count_recv_processed = 0; static char *static_server_buffer = NULL; static int static_server_buffer_size = 0; static int eager_threshold = -1; static int max_message_size = -1; #if ENABLE_SYSV static int use_dev_shm = 1; #endif static int token_counter = 0; static int init_from_comm = 0; static int COMEX_ENABLE_PUT_SELF = ENABLE_PUT_SELF; static int COMEX_ENABLE_GET_SELF = ENABLE_GET_SELF; static int COMEX_ENABLE_ACC_SELF = ENABLE_ACC_SELF; static int COMEX_ENABLE_PUT_SMP = ENABLE_PUT_SMP; static int COMEX_ENABLE_GET_SMP = ENABLE_GET_SMP; static int COMEX_ENABLE_ACC_SMP = ENABLE_ACC_SMP; static int COMEX_ENABLE_PUT_PACKED = ENABLE_PUT_PACKED; static int COMEX_ENABLE_GET_PACKED = ENABLE_GET_PACKED; static int COMEX_ENABLE_ACC_PACKED = ENABLE_ACC_PACKED; static int COMEX_ENABLE_PUT_DATATYPE = ENABLE_PUT_DATATYPE; static int COMEX_ENABLE_GET_DATATYPE = ENABLE_GET_DATATYPE; static int COMEX_PUT_DATATYPE_THRESHOLD = 8192; static int COMEX_GET_DATATYPE_THRESHOLD = 8192; static int COMEX_ENABLE_PUT_IOV = ENABLE_PUT_IOV; static int COMEX_ENABLE_GET_IOV = ENABLE_GET_IOV; static int COMEX_ENABLE_ACC_IOV = ENABLE_ACC_IOV; #if USE_SICM static sicm_device_list devices = {0}; #if SICM_OLD static sicm_device *device_dram = NULL; static sicm_device *device_knl_hbm = NULL; static sicm_device *device_ppc_hbm = NULL; #else static sicm_device_list device_dram = {0}; static sicm_device_list device_knl_hbm = {0}; static sicm_device_list device_ppc_hbm = {0}; #endif #endif #if PAUSE_ON_ERROR static int AR_caught_sig=0; static int AR_caught_sigsegv=0; void (*SigSegvOrig)(int); void SigSegvHandler(int sig) { char name[256]; AR_caught_sig= sig; AR_caught_sigsegv=1; if (-1 == gethostname(name, 256)) { perror("gethostname"); comex_error("gethostname failed", errno); } fprintf(stderr,"%d(%s:%d): Segmentation Violation ... pausing\n", g_state.rank, name, getpid()); pause(); comex_error("Segmentation Violation error, status=",(int) sig); } #endif /* static function declarations */ /* error checking */ #define CHECK_MPI_RETVAL(retval) check_mpi_retval((retval), __FILE__, __LINE__) STATIC void check_mpi_retval(int retval, const char *file, int line); STATIC const char *str_mpi_retval(int retval); /* server fuctions */ STATIC void server_send(void *buf, int count, int dest); STATIC void server_send_datatype(void *buf, MPI_Datatype dt, int dest); STATIC void server_recv(void *buf, int count, int source); STATIC void server_recv_datatype(void *buf, MPI_Datatype dt, int source); STATIC void _progress_server(); STATIC void _put_handler(header_t *header, char *payload, int proc); STATIC void _put_packed_handler(header_t *header, char *payload, int proc); STATIC void _put_datatype_handler(header_t *header, char *payload, int proc); STATIC void _put_iov_handler(header_t *header, int proc); STATIC void _get_handler(header_t *header, int proc); STATIC void _get_packed_handler(header_t *header, char *payload, int proc); STATIC void _get_datatype_handler(header_t *header, char *payload, int proc); STATIC void _get_iov_handler(header_t *header, int proc); STATIC void _acc_handler(header_t *header, char *scale, int proc); STATIC void _acc_packed_handler(header_t *header, char *payload, int proc); STATIC void _acc_iov_handler(header_t *header, char *scale, int proc); STATIC void _fence_handler(header_t *header, int proc); STATIC void _fetch_and_add_handler(header_t *header, char *payload, int proc); STATIC void _swap_handler(header_t *header, char *payload, int proc); STATIC void _mutex_create_handler(header_t *header, int proc); STATIC void _mutex_destroy_handler(header_t *header, int proc); STATIC void _lock_handler(header_t *header, int proc); STATIC void _unlock_handler(header_t *header, int proc); STATIC void _malloc_handler(header_t *header, char *payload, int proc); STATIC void _free_handler(header_t *header, char *payload, int proc); /* worker functions */ STATIC void nb_send_common(void *buf, int count, int dest, nb_t *nb, int need_free); STATIC void nb_send_datatype(void *buf, MPI_Datatype dt, int dest, nb_t *nb); STATIC void nb_send_header(void *buf, int count, int dest, nb_t *nb); STATIC void nb_send_buffer(void *buf, int count, int dest, nb_t *nb); STATIC void nb_recv_packed(void *buf, int count, int source, nb_t *nb, stride_t *stride); STATIC void nb_recv_datatype(void *buf, MPI_Datatype dt, int source, nb_t *nb); STATIC void nb_recv_iov(void *buf, int count, int source, nb_t *nb, comex_giov_t *iov); STATIC void nb_recv(void *buf, int count, int source, nb_t *nb); STATIC int nb_get_handle_index(); STATIC nb_t* nb_wait_for_handle(); STATIC void nb_wait_for_send1(nb_t *nb); STATIC void nb_wait_for_recv1(nb_t *nb); STATIC void nb_wait_for_all(nb_t *nb); STATIC int nb_test_for_all(nb_t *nb); STATIC void nb_wait_all(); STATIC void nb_put(void *src, void *dst, int bytes, int proc, nb_t *nb); STATIC void nb_get(void *src, void *dst, int bytes, int proc, nb_t *nb); STATIC void nb_acc(int datatype, void *scale, void *src, void *dst, int bytes, int proc, nb_t *nb); STATIC void nb_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_puts_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_puts_datatype( void *src_ptr, int *src_stride_ar, void *dst_ptr, int *dst_stride_ar, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_gets_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_gets_datatype( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_accs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_accs_packed( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_putv(comex_giov_t *iov, int iov_len, int proc, nb_t *nb); STATIC void nb_putv_packed(comex_giov_t *iov, int proc, nb_t *nb); STATIC void nb_getv(comex_giov_t *iov, int iov_len, int proc, nb_t *nb); STATIC void nb_getv_packed(comex_giov_t *iov, int proc, nb_t *nb); STATIC void nb_accv(int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, nb_t *nb); STATIC void nb_accv_packed(int datatype, void *scale, comex_giov_t *iov, int proc, nb_t *nb); STATIC void _fence_master(int master_rank); STATIC int _eager_check(int extra_bytes); /* other functions */ STATIC int _packed_size(int *src_stride, int *count, int stride_levels); STATIC char* pack(char *src, int *src_stride, int *count, int stride_levels, int *size); STATIC void unpack(char *packed_buffer, char *dst, int *dst_stride, int *count, int stride_levels); STATIC char* _generate_shm_name(int rank); STATIC reg_entry_t* _comex_malloc_local(size_t size); #if USE_SICM #if SICM_OLD STATIC reg_entry_t* _comex_malloc_local_memdev(size_t size, sicm_device *device); #else STATIC reg_entry_t* _comex_malloc_local_memdev(size_t size, sicm_device_list device); #endif int _comex_free_local_memdev(void *ptr); #endif STATIC void* _get_offset_memory(reg_entry_t *reg_entry, void *memory); STATIC int _is_master(void); STATIC int _get_world_rank(comex_igroup_t *igroup, int rank); STATIC int* _get_world_ranks(comex_igroup_t *igroup); STATIC int _smallest_world_rank_with_same_hostid(comex_igroup_t *group); STATIC int _largest_world_rank_with_same_hostid(comex_igroup_t *igroup); STATIC void _malloc_semaphore(void); STATIC void _free_semaphore(void); #if ENABLE_SYSV STATIC void* _shm_create(char *name, key_t *key, size_t size); STATIC void* _shm_attach(const char *name, size_t size, key_t key); #else STATIC void* _shm_create(const char *name, size_t size); STATIC void* _shm_attach(const char *name, size_t size); #endif STATIC void* _shm_map(int fd, size_t size); #if USE_SICM #if SICM_OLD STATIC void* _shm_create_memdev(const char *name, size_t size, sicm_device *device); STATIC void* _shm_attach_memdev(const char *name, size_t size, sicm_device *device); #else STATIC void* _shm_create_memdev(const char *name, size_t size, sicm_device_list device); STATIC void* _shm_attach_memdev(const char *name, size_t size, sicm_device_list device); #endif STATIC void* _shm_map_arena(int fd, size_t size, sicm_arena arena); #endif STATIC int _set_affinity(int cpu); STATIC void translate_mpi_error(int ierr, const char* location); STATIC void strided_to_subarray_dtype(int *stride_array, int *count, int levels, MPI_Datatype base_type, MPI_Datatype *type); STATIC void check_devshm(int fd, size_t size); static int devshm_initialized = 0; static long devshm_fs_left = 0; static long devshm_fs_initial = 0; static long counter_open_fds = 0; STATIC void count_open_fds(void); int _comex_init(MPI_Comm comm) { int status = 0; int init_flag = 0; int i = 0; if (initialized) { return 0; } initialized = 1; /* Assert MPI has been initialized */ status = MPI_Initialized(&init_flag); CHECK_MPI_RETVAL(status); assert(init_flag); /*MPI_Errhandler_set(MPI_COMM_WORLD, MPI_ERRORS_RETURN);*/ /* groups */ comex_group_init(comm); /* env vars */ { int armci_verbose; char *value = NULL; nb_max_outstanding = COMEX_MAX_NB_OUTSTANDING; /* default */ value = getenv("COMEX_MAX_NB_OUTSTANDING"); if (NULL != value) { nb_max_outstanding = atoi(value); } COMEX_ASSERT(nb_max_outstanding > 0); static_server_buffer_size = COMEX_STATIC_BUFFER_SIZE; /* default */ value = getenv("COMEX_STATIC_BUFFER_SIZE"); if (NULL != value) { static_server_buffer_size = atoi(value); } COMEX_ASSERT(static_server_buffer_size > 0); eager_threshold = -1; /* default */ value = getenv("COMEX_EAGER_THRESHOLD"); if (NULL != value) { eager_threshold = atoi(value); } COMEX_ENABLE_PUT_SELF = ENABLE_PUT_SELF; /* default */ value = getenv("COMEX_ENABLE_PUT_SELF"); if (NULL != value) { COMEX_ENABLE_PUT_SELF = atoi(value); } COMEX_ENABLE_GET_SELF = ENABLE_GET_SELF; /* default */ value = getenv("COMEX_ENABLE_GET_SELF"); if (NULL != value) { COMEX_ENABLE_GET_SELF = atoi(value); } COMEX_ENABLE_ACC_SELF = ENABLE_ACC_SELF; /* default */ value = getenv("COMEX_ENABLE_ACC_SELF"); if (NULL != value) { COMEX_ENABLE_ACC_SELF = atoi(value); } COMEX_ENABLE_PUT_SMP = ENABLE_PUT_SMP; /* default */ value = getenv("COMEX_ENABLE_PUT_SMP"); if (NULL != value) { COMEX_ENABLE_PUT_SMP = atoi(value); } COMEX_ENABLE_GET_SMP = ENABLE_GET_SMP; /* default */ value = getenv("COMEX_ENABLE_GET_SMP"); if (NULL != value) { COMEX_ENABLE_GET_SMP = atoi(value); } COMEX_ENABLE_ACC_SMP = ENABLE_ACC_SMP; /* default */ value = getenv("COMEX_ENABLE_ACC_SMP"); if (NULL != value) { COMEX_ENABLE_ACC_SMP = atoi(value); } COMEX_ENABLE_PUT_PACKED = ENABLE_PUT_PACKED; /* default */ value = getenv("COMEX_ENABLE_PUT_PACKED"); if (NULL != value) { COMEX_ENABLE_PUT_PACKED = atoi(value); } COMEX_ENABLE_GET_PACKED = ENABLE_GET_PACKED; /* default */ value = getenv("COMEX_ENABLE_GET_PACKED"); if (NULL != value) { COMEX_ENABLE_GET_PACKED = atoi(value); } COMEX_ENABLE_ACC_PACKED = ENABLE_ACC_PACKED; /* default */ value = getenv("COMEX_ENABLE_ACC_PACKED"); if (NULL != value) { COMEX_ENABLE_ACC_PACKED = atoi(value); } COMEX_ENABLE_PUT_DATATYPE = ENABLE_PUT_DATATYPE; /* default */ value = getenv("COMEX_ENABLE_PUT_DATATYPE"); if (NULL != value) { COMEX_ENABLE_PUT_DATATYPE = atoi(value); } COMEX_ENABLE_GET_DATATYPE = ENABLE_GET_DATATYPE; /* default */ value = getenv("COMEX_ENABLE_GET_DATATYPE"); if (NULL != value) { COMEX_ENABLE_GET_DATATYPE = atoi(value); } COMEX_PUT_DATATYPE_THRESHOLD = 8192; /* default */ value = getenv("COMEX_PUT_DATATYPE_THRESHOLD"); if (NULL != value) { COMEX_PUT_DATATYPE_THRESHOLD = atoi(value); } COMEX_GET_DATATYPE_THRESHOLD = 8192; /* default */ value = getenv("COMEX_GET_DATATYPE_THRESHOLD"); if (NULL != value) { COMEX_GET_DATATYPE_THRESHOLD = atoi(value); } COMEX_ENABLE_PUT_IOV = ENABLE_PUT_IOV; /* default */ value = getenv("COMEX_ENABLE_PUT_IOV"); if (NULL != value) { COMEX_ENABLE_PUT_IOV = atoi(value); } COMEX_ENABLE_GET_IOV = ENABLE_GET_IOV; /* default */ value = getenv("COMEX_ENABLE_GET_IOV"); if (NULL != value) { COMEX_ENABLE_GET_IOV = atoi(value); } COMEX_ENABLE_ACC_IOV = ENABLE_ACC_IOV; /* default */ value = getenv("COMEX_ENABLE_ACC_IOV"); if (NULL != value) { COMEX_ENABLE_ACC_IOV = atoi(value); } max_message_size = INT_MAX; /* default */ value = getenv("COMEX_MAX_MESSAGE_SIZE"); if (NULL != value) { max_message_size = atoi(value); } #if DEBUG armci_verbose = 1; #else armci_verbose = 0; #endif value = getenv("ARMCI_VERBOSE"); if (NULL != value) { armci_verbose = atoi(value); } if (armci_verbose && 0 == g_state.rank) { printf("COMEX_MAX_NB_OUTSTANDING=%d\n", nb_max_outstanding); printf("COMEX_STATIC_BUFFER_SIZE=%d\n", static_server_buffer_size); printf("COMEX_MAX_MESSAGE_SIZE=%d\n", max_message_size); printf("COMEX_EAGER_THRESHOLD=%d\n", eager_threshold); printf("COMEX_PUT_DATATYPE_THRESHOLD=%d\n", COMEX_PUT_DATATYPE_THRESHOLD); printf("COMEX_GET_DATATYPE_THRESHOLD=%d\n", COMEX_GET_DATATYPE_THRESHOLD); printf("COMEX_ENABLE_PUT_SELF=%d\n", COMEX_ENABLE_PUT_SELF); printf("COMEX_ENABLE_GET_SELF=%d\n", COMEX_ENABLE_GET_SELF); printf("COMEX_ENABLE_ACC_SELF=%d\n", COMEX_ENABLE_ACC_SELF); printf("COMEX_ENABLE_PUT_SMP=%d\n", COMEX_ENABLE_PUT_SMP); printf("COMEX_ENABLE_GET_SMP=%d\n", COMEX_ENABLE_GET_SMP); printf("COMEX_ENABLE_ACC_SMP=%d\n", COMEX_ENABLE_ACC_SMP); printf("COMEX_ENABLE_PUT_PACKED=%d\n", COMEX_ENABLE_PUT_PACKED); printf("COMEX_ENABLE_GET_PACKED=%d\n", COMEX_ENABLE_GET_PACKED); printf("COMEX_ENABLE_ACC_PACKED=%d\n", COMEX_ENABLE_ACC_PACKED); printf("COMEX_ENABLE_PUT_DATATYPE=%d\n", COMEX_ENABLE_PUT_DATATYPE); printf("COMEX_ENABLE_GET_DATATYPE=%d\n", COMEX_ENABLE_GET_DATATYPE); printf("COMEX_ENABLE_PUT_IOV=%d\n", COMEX_ENABLE_PUT_IOV); printf("COMEX_ENABLE_GET_IOV=%d\n", COMEX_ENABLE_GET_IOV); printf("COMEX_ENABLE_ACC_IOV=%d\n", COMEX_ENABLE_ACC_IOV); fflush(stdout); } } /* mutexes */ mutexes = NULL; num_mutexes = NULL; lq_heads = NULL; /* nonblocking message queues */ nb_state = (nb_t*)malloc(sizeof(nb_t) * nb_max_outstanding); COMEX_ASSERT(nb_state); for (i = 0; i < nb_max_outstanding; ++i) { nb_state[i].in_use = 0; nb_state[i].send_size = 0; nb_state[i].send_head = NULL; nb_state[i].send_tail = NULL; nb_state[i].recv_size = 0; nb_state[i].recv_head = NULL; nb_state[i].recv_tail = NULL; } nb_index = 0; nb_count_event = 0; nb_count_event_processed = 0; nb_count_send = 0; nb_count_send_processed = 0; nb_count_recv = 0; nb_count_recv_processed = 0; #if USE_SICM devices = sicm_init(); #if SICM_OLD for(i = 0; i < devices.count; i++) { if (devices.devices[i].tag == SICM_DRAM) { device_dram = &devices.devices[i]; } if (devices.devices[i].tag == SICM_KNL_HBM) { device_knl_hbm = &devices.devices[i]; } if (devices.devices[i].tag == SICM_POWERPC_HBM) { device_ppc_hbm = &devices.devices[i]; } } if (!device_dram) { printf("Device DRAM not found\n"); exit(18); } #else for(i =0; i < devices.count; ++i){ sicm_device *curr = devices.devices[i]; if(curr->tag == SICM_DRAM){ device_dram.count = 1; device_dram.devices = &devices.devices[i]; } if (curr->tag == SICM_KNL_HBM) { device_knl_hbm.count = 1; device_knl_hbm.devices = &devices.devices[i]; } if (curr->tag == SICM_POWERPC_HBM) { device_ppc_hbm.count = 1; device_ppc_hbm.devices = &devices.devices[i]; } } if(device_dram.devices == NULL){ printf("Device DRAM not found\n"); exit(18); } #endif #endif #if ENABLE_SYSV /* if using SYSTEM V instead of POSIX SHM, check if /dev/shm exist */ { struct stat sb; if (stat("/dev/shm", &sb) == 0 && S_ISDIR(sb.st_mode)) { use_dev_shm = 1; } else if (stat("/tmp", &sb) == 0 && S_ISDIR(sb.st_mode)) { use_dev_shm = 0; } else { comex_error("No directory available for System V memory\n",-1); } } token_counter = g_state.rank; #endif /* reg_cache */ /* note: every process needs a reg cache and it's always based on the * world rank and size */ reg_cache_init(g_state.size); _malloc_semaphore(); #if DEBUG fprintf(stderr, "[%d] comex_init() before progress server\n", g_state.rank); #endif #if PAUSE_ON_ERROR if ((SigSegvOrig=signal(SIGSEGV, SigSegvHandler)) == SIG_ERR) { comex_error("signal(SIGSEGV, ...) error", -1); } #endif status = _set_affinity(g_state.node_rank); COMEX_ASSERT(0 == status); if (_is_master()) { /* TODO: wasteful O(p) storage... */ mutexes = (int**)malloc(sizeof(int*) * g_state.size); COMEX_ASSERT(mutexes); /* create one lock queue for each proc for each mutex */ lq_heads = (comex_lock_t***)malloc(sizeof(comex_lock_t**) * g_state.size); COMEX_ASSERT(lq_heads); /* start the server */ _progress_server(); if (init_from_comm) { status = COMEX_FAILURE; } return status; } /* Synch - Sanity Check */ /* This barrier is on the world worker group */ MPI_Barrier(group_list->comm); /* static state */ fence_array = malloc(sizeof(char) * g_state.size); COMEX_ASSERT(fence_array); for (i = 0; i < g_state.size; ++i) { fence_array[i] = 0; } #if DEBUG fprintf(stderr, "[%d] comex_init() before barrier\n", g_state.rank); #endif /* Synch - Sanity Check */ /* This barrier is on the world worker group */ MPI_Barrier(group_list->comm); #if DEBUG fprintf(stderr, "[%d] comex_init() success\n", g_state.rank); #endif return COMEX_SUCCESS; } int comex_init() { init_from_comm = 0; return _comex_init(MPI_COMM_WORLD); } int comex_init_comm(MPI_Comm comm) { init_from_comm = 1; return _comex_init(comm); } int comex_init_args(int *argc, char ***argv) { int init_flag; int status; status = MPI_Initialized(&init_flag); CHECK_MPI_RETVAL(status); if(!init_flag) { status = MPI_Init(argc, argv); CHECK_MPI_RETVAL(status); } return comex_init(); } int comex_initialized() { #if DEBUG if (initialized) { fprintf(stderr, "[%d] comex_initialized()\n", g_state.rank); } #endif return initialized; } int comex_finalize() { #if DEBUG fprintf(stderr, "[%d] comex_finalize()\n", g_state.rank); #endif /* it's okay to call multiple times -- extra calls are no-ops */ if (!initialized) { return COMEX_SUCCESS; } comex_barrier(COMEX_GROUP_WORLD); initialized = 0; _free_semaphore(); /* Make sure that all outstanding operations are done */ comex_wait_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); /* send quit message to thread */ int smallest_rank_with_same_hostid, largest_rank_with_same_hostid; int num_progress_ranks_per_node, is_node_ranks_packed; int my_rank_to_free; int is_notifier = 0; num_progress_ranks_per_node = get_num_progress_ranks_per_node(); is_node_ranks_packed = get_progress_rank_distribution_on_node(); smallest_rank_with_same_hostid = _smallest_world_rank_with_same_hostid(group_list); largest_rank_with_same_hostid = _largest_world_rank_with_same_hostid(group_list); my_rank_to_free = get_my_rank_to_free(g_state.rank, g_state.node_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed); #if DEBUG fprintf(stderr, "[%d] comex_finalize() sr_in_group %d\n", g_state.rank, my_rank_to_free); // smallest_rank_with_same_hostid + g_state.node_size* // ((g_state.rank - smallest_rank_with_same_hostid)/g_state.node_size)); #endif // is_notifier = g_state.rank == smallest_rank_with_same_hostid + g_state.node_size* // ((g_state.rank - smallest_rank_with_same_hostid)/g_state.node_size); // if (_smallest_world_rank_with_same_hostid(group_list) == g_state.rank) if((is_notifier = my_rank_to_free) == g_state.rank) { int my_master = -1; header_t *header = NULL; nb_t *nb = NULL; nb = nb_wait_for_handle(); my_master = g_state.master[g_state.rank]; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_QUIT; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = 0; nb_send_header(header, sizeof(header_t), my_master, nb); nb_wait_for_all(nb); } free(fence_array); free(nb_state); #if DEBUG printf(" %d freed nb_state ptr %p \n", g_state.rank, nb_state); #endif MPI_Barrier(g_state.comm); /* reg_cache */ reg_cache_destroy(); /* destroy the groups */ #if DEBUG fprintf(stderr, "[%d] before comex_group_finalize()\n", g_state.rank); #endif comex_group_finalize(); #if DEBUG fprintf(stderr, "[%d] after comex_group_finalize()\n", g_state.rank); #endif #if USE_SICM sicm_fini(); #endif #if DEBUG_TO_FILE fclose(comex_trace_file); #endif return COMEX_SUCCESS; } void comex_error(const char *msg, int code) { #if DEBUG fprintf(stderr, "[%d] Received an Error in Communication: (%d) %s\n", g_state.rank, code, msg); #if DEBUG_TO_FILE fclose(comex_trace_file); #endif #endif fprintf(stderr,"[%d] Received an Error in Communication: (%d) %s\n", g_state.rank, code, msg); MPI_Abort(g_state.comm, code); } int comex_put( void *src, void *dst, int bytes, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_put(src, dst, bytes, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_get( void *src, void *dst, int bytes, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_get(src, dst, bytes, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_acc( int datatype, void *scale, void *src, void *dst, int bytes, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_acc(datatype, scale, src, dst, bytes, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_puts(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_gets(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_accs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_accs(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_putv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_putv(iov, iov_len, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_getv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_getv(iov, iov_len, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_accv( int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_accv(datatype, scale, iov, iov_len, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_fence_all(comex_group_t group) { int p = 0; int count_before = 0; int count_after = 0; nb_t *nb = NULL; #if DEBUG fprintf(stderr, "[%d] comex_fence_all(group=%d)\n", g_state.rank, group); #endif /* NOTE: We always fence on the world group */ /* count how many fence messagse to send */ for (p=0; poperation = OP_FENCE; header->remote_address = NULL; header->local_address = NULL; header->length = 0; header->rank = 0; nb_send_header(header, sizeof(header_t), p_master, nb); } } nb_wait_for_all(nb); for (p=0; poperation = OP_FENCE; header->remote_address = NULL; header->local_address = NULL; header->length = 0; header->rank = 0; nb_send_header(header, sizeof(header_t), master_rank, nb); nb_wait_for_all(nb); fence_array[master_rank] = 0; } } int comex_fence_proc(int proc, comex_group_t group) { int world_rank = -1; int master_rank = -1; comex_igroup_t *igroup = NULL; #if DEBUG printf("[%d] comex_fence_proc(proc=%d, group=%d)\n", g_state.rank, proc, group); #endif CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_rank = _get_world_rank(igroup, proc); master_rank = g_state.master[world_rank]; _fence_master(master_rank); return COMEX_SUCCESS; } /* comex_barrier is comex_fence_all + MPI_Barrier */ int comex_barrier(comex_group_t group) { int status = 0; MPI_Comm comm = MPI_COMM_NULL; #if DEBUG static int count=-1; ++count; fprintf(stderr, "[%d] comex_barrier(%d) count=%d\n", g_state.rank, group, count); #endif comex_fence_all(group); status = comex_group_comm(group, &comm); COMEX_ASSERT(COMEX_SUCCESS == status); MPI_Barrier(comm); return COMEX_SUCCESS; } STATIC int _packed_size(int *src_stride, int *count, int stride_levels) { int size; int i; int n1dim; /* number of 1 dim block */ COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); COMEX_ASSERT(NULL != src_stride); COMEX_ASSERT(NULL != count); COMEX_ASSERT(count[0] > 0); #if DEBUG fprintf(stderr, "[%d] _packed_size(src_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, src_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* allocate packed buffer now that we know the size */ size = n1dim * count[0]; return size; } STATIC char* pack( char *src, int *src_stride, int *count, int stride_levels, int *size) { int i, j; long src_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int packed_index = 0; char *packed_buffer = NULL; COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != src_stride); COMEX_ASSERT(NULL != count); COMEX_ASSERT(count[0] > 0); COMEX_ASSERT(NULL != size); #if DEBUG fprintf(stderr, "[%d] pack(src=%p, src_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, src, src_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* allocate packed buffer now that we know the size */ packed_buffer = malloc(n1dim * count[0]); COMEX_ASSERT(packed_buffer); /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } (void)memcpy(&packed_buffer[packed_index], &src[src_idx], count[0]); packed_index += count[0]; } COMEX_ASSERT(packed_index == n1dim*count[0]); *size = packed_index; return packed_buffer; } STATIC void unpack(char *packed_buffer, char *dst, int *dst_stride, int *count, int stride_levels) { int i, j; long dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int dst_bvalue[7], dst_bunit[7]; int packed_index = 0; COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); COMEX_ASSERT(NULL != packed_buffer); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != dst_stride); COMEX_ASSERT(NULL != count); COMEX_ASSERT(count[0] > 0); #if DEBUG fprintf(stderr, "[%d] unpack(dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, dst, dst_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { dst_bvalue[i] = 0; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { dst_bvalue[j] = 0; } } (void)memcpy(&dst[dst_idx], &packed_buffer[packed_index], count[0]); packed_index += count[0]; } COMEX_ASSERT(packed_index == n1dim*count[0]); } STATIC char* _generate_shm_name(int rank) { int snprintf_retval = 0; /* /cmxUUUUUUUUUUPPPPPPPPPPCCCCCCN */ /* 0000000001111111111222222222233 */ /* 1234567890123456789012345678901 */ char *name = NULL; static const unsigned int limit = 62; static const char letters[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; static unsigned int counter[6] = {0}; COMEX_ASSERT(rank >= 0); name = malloc(SHM_NAME_SIZE*sizeof(char)); COMEX_ASSERT(name); if (counter[0] == 0 && counter[1] == 0 && counter[2] == 0 && counter[3] == 0 && counter[4] == 0 && counter[5] == 0) { int n = rank; counter[0] = n%limit; n = (n-counter[0])/limit; counter[1] = n%limit; n = (n-counter[1])/limit; counter[2] = n%limit; n = (n-counter[2])/limit; counter[3] = n%limit; n = (n-counter[3])/limit; counter[4] = n%limit; n = (n-counter[4])/limit; counter[5] = n%limit; } snprintf_retval = snprintf(name, SHM_NAME_SIZE, #if ENABLE_SYSV "/cmx%010u%010u%c%c%c%c%c%c", getuid()+token_counter, getpid(), #else "/cmx%010u%010u%c%c%c%c%c%c", getuid(), getpid(), #endif letters[counter[5]], letters[counter[4]], letters[counter[3]], letters[counter[2]], letters[counter[1]], letters[counter[0]]); COMEX_ASSERT(snprintf_retval < (int)SHM_NAME_SIZE); name[SHM_NAME_SIZE-1] = '\0'; ++counter[0]; if (counter[0] >= limit) { ++counter[1]; counter[0] = 0; } if (counter[1] >= limit) { ++counter[2]; counter[1] = 0; } if (counter[2] >= limit) { ++counter[3]; counter[2] = 0; } if (counter[3] >= limit) { ++counter[4]; counter[3] = 0; } if (counter[4] >= limit) { ++counter[5]; counter[4] = 0; } if (counter[5] >= limit) { comex_error("_generate_shm_name: too many names generated", -1); } #if DEBUG fprintf(stderr, "[%d] _generate_shm_name(%d)=%s\n", g_state.rank, rank, name); #endif return name; } void* comex_malloc_local(size_t size) { reg_entry_t *reg_entry; void *memory = NULL; if (size > 0) { reg_entry = _comex_malloc_local(size); memory = reg_entry->mapped; } else { memory = NULL; } return memory; } STATIC reg_entry_t* _comex_malloc_local(size_t size) { char *name = NULL; void *memory = NULL; reg_entry_t *reg_entry = NULL; #if ENABLE_SYSV key_t key; char file[SHM_NAME_SIZE+10]; #endif #if DEBUG fprintf(stderr, "[%d] _comex_malloc_local(size=%lu)\n", g_state.rank, (long unsigned)size); #endif if (0 == size) { return NULL; } /* create my shared memory object */ name = _generate_shm_name(g_state.rank); #if ENABLE_SYSV memory = _shm_create(name, &key, size); #else memory = _shm_create(name, size); #endif #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] _comex_malloc_local registering " "rank=%d mem=%p size=%lu name=%s mapped=%p\n", g_state.rank, g_state.rank, memory, (long unsigned)size, name, memory); #endif /* register the memory locally */ #if USE_SICM #if SICM_OLD reg_entry = reg_cache_insert( g_state.rank, memory, size, name, memory, 0, NULL); #else reg_entry = reg_cache_insert( g_state.rank, memory, size, name, memory, 0, nill); #endif #else #if ENABLE_SYSV reg_entry = reg_cache_insert( g_state.rank, memory, size, name, key, memory, 0); #else reg_entry = reg_cache_insert( g_state.rank, memory, size, name, memory, 0); #endif #endif if (NULL == reg_entry) { comex_error("_comex_malloc_local: reg_cache_insert", -1); } free(name); return reg_entry; } #if USE_SICM #if SICM_OLD STATIC reg_entry_t* _comex_malloc_local_memdev(size_t size, sicm_device *device) #else STATIC reg_entry_t* _comex_malloc_local_memdev(size_t size, sicm_device_list device) #endif { char *name = NULL; void *memory = NULL; reg_entry_t *reg_entry = NULL; #if DEBUG fprintf(stderr, "[%d] _comex_malloc_local(size=%lu)\n", g_state.rank, (long unsigned)size); #endif if (0 == size) { return NULL; } /* create my shared memory object */ name = _generate_shm_name(g_state.rank); memory = _shm_create_memdev(name, size, device); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] _comex_malloc_local registering " "rank=%d mem=%p size=%lu name=%s mapped=%p\n", g_state.rank, g_state.rank, memory, (long unsigned)size, name, memory); #endif /* register the memory locally */ reg_entry = reg_cache_insert( g_state.rank, memory, size, name, memory, 1, device); if (NULL == reg_entry) { comex_error("_comex_malloc_local: reg_cache_insert", -1); } free(name); return reg_entry; } #endif /* Utility function to translate errors from shmget */ void _shmget_err(int shm_id, const char* buf) { int lerr = errno; if (shm_id == -1) { perror("shmget"); fprintf(stderr,"%s",buf); if (EACCES == lerr) { fprintf(stderr,"p[%d] shmget error EACCES\n",g_state.rank); } else if (EEXIST == lerr) { fprintf(stderr,"p[%d] shmget error EEXIST\n",g_state.rank); } else if (EINVAL == lerr) { fprintf(stderr,"p[%d] shmget error EINVAL\n",g_state.rank); } else if (ENOENT == lerr) { fprintf(stderr,"p[%d] shmget error ENOENT\n",g_state.rank); } else if (ENOMEM == lerr) { fprintf(stderr,"p[%d] shmget error ENOMEM\n",g_state.rank); } else if (ENOSPC == lerr) { fprintf(stderr,"p[%d] shmget error ENOSPC\n",g_state.rank); } else { fprintf(stderr,"p[%d] shmget error is unknown\n",g_state.rank); } /* { char buf[128]; sprintf(buf,"ipcs -a > shmdev%d.dbg\n",g_state.rank); system(buf); } */ } } /* Utility function to translate errors from shmat */ void _shmat_err(void *ptr) { int lerr = errno; if (ptr == (void*)-1) { perror("shmat"); if (EACCES == lerr) { fprintf(stderr,"p[%d] shmat error EACCES\n",g_state.rank); } else if (EIDRM == lerr) { fprintf(stderr,"p[%d] shmat error EIDRM\n",g_state.rank); } else if (EINVAL == lerr) { fprintf(stderr,"p[%d] shmat error EINVAL\n",g_state.rank); } else if (ENOMEM == lerr) { fprintf(stderr,"p[%d] shmat error ENOMEM\n",g_state.rank); } else if (ENOMEM == lerr) { fprintf(stderr,"p[%d] shmat error ENOMEM\n",g_state.rank); } else { fprintf(stderr,"p[%d] shmat error is unknown\n",g_state.rank); } } } /* Utility function to translate errors from shmdt */ void _shmdt_err(int flag) { int lerr = errno; if (flag == -1) { perror("shmdt"); if (EINVAL == lerr) { fprintf(stderr,"p[%d] shmdt error EINVAL\n",g_state.rank); } else { fprintf(stderr,"p[%d] shmdt error is unknown\n",g_state.rank); } } } /* Utility function to translate errors from shmctl */ void _shmctl_err(int flag) { int lerr = errno; if (flag == -1) { perror("shmctl"); if (EACCES == lerr) { fprintf(stderr,"p[%d] shmctl error EACCES\n",g_state.rank); } else if (EFAULT == lerr) { fprintf(stderr,"p[%d] shmctl error EFAULT\n",g_state.rank); } else if (EIDRM == lerr) { fprintf(stderr,"p[%d] shmctl error EIDRM\n",g_state.rank); } else if (EINVAL == lerr) { fprintf(stderr,"p[%d] shmctl error EINVAL\n",g_state.rank); } else if (ENOMEM == lerr) { fprintf(stderr,"p[%d] shmctl error ENOMEM\n",g_state.rank); } else if (EOVERFLOW == lerr) { fprintf(stderr,"p[%d] shmctl error EOVERFLOW\n",g_state.rank); } else if (EPERM == lerr) { fprintf(stderr,"p[%d] shmctl error EPERM\n",g_state.rank); } else { fprintf(stderr,"p[%d] shmctl error is unknown\n",g_state.rank); } } } int comex_free_local(void *ptr) { #if ENABLE_SYSV key_t key; int shm_id; char file[SHM_NAME_SIZE+10]; char ebuf[128]; #endif int retval = 0; reg_entry_t *reg_entry = NULL; #if DEBUG fprintf(stderr, "[%d] comex_free_local(ptr=%p)\n", g_state.rank, ptr); #endif if (NULL == ptr) { return COMEX_SUCCESS; } /* find the registered memory */ reg_entry = reg_cache_find(g_state.rank, ptr, 0); #if ENABLE_SYSV shm_id = shmget(reg_entry->key,reg_entry->len,0600); sprintf(ebuf,"p[%d] (shmget in comex_free_local) flags: 0600, key: %d, name: %s\n", g_state.rank,reg_entry->key,reg_entry->name); _shmget_err(shm_id,ebuf); /* printf("p[%d] DETACH SHM name: %s key: %d\n",g_state.rank,reg_entry->name, reg_entry->key); */ _shmdt_err(shmdt(reg_entry->mapped)); /* printf("p[%d] DESTROY SHM name: %s key: %d\n",g_state.rank,reg_entry->name, reg_entry->key); */ _shmctl_err(shmctl(shm_id, IPC_RMID, NULL)); if (use_dev_shm) { sprintf(file,"/dev/shm/%s",reg_entry->name); } else { sprintf(file,"/tmp/%s",reg_entry->name); } #if ENABLE_FTOK remove(file); #endif #else /* unmap the memory */ retval = munmap(ptr, reg_entry->len); check_devshm(0, -(reg_entry->len)); if (-1 == retval) { perror("comex_free_local: munmap"); comex_error("comex_free_local: munmap", retval); } /* remove the shared memory object */ retval = shm_unlink(reg_entry->name); if (-1 == retval) { perror("comex_free_local: shm_unlink"); comex_error("comex_free_local: shm_unlink", retval); } #endif /* delete the reg_cache entry */ retval = reg_cache_delete(g_state.rank, ptr); COMEX_ASSERT(RR_SUCCESS == retval); return COMEX_SUCCESS; } #if USE_SICM int _comex_free_local_memdev(void *ptr) { int retval = 0; reg_entry_t *reg_entry = NULL; #if DEBUG fprintf(stderr, "[%d] _comex_free_local_memdev(ptr=%p)\n", g_state.rank, ptr); #endif if (NULL == ptr) { return COMEX_SUCCESS; } /* find the registered memory */ reg_entry = reg_cache_find(g_state.rank, ptr, 0); /* unmap the memory */ sicm_free(ptr); retval = 0; if (-1 == retval) { perror("_comex_free_local_memdev: munmap"); comex_error("_comex_free_local_memdev: munmap", retval); } /* remove the shared memory object */ retval = shm_unlink(reg_entry->name); if (-1 == retval) { perror("_comex_free_local_memdev: shm_unlink"); comex_error("_comex_free_local_memdev: shm_unlink", retval); } /* delete the reg_cache entry */ retval = reg_cache_delete(g_state.rank, ptr); COMEX_ASSERT(RR_SUCCESS == retval); return COMEX_SUCCESS; } #endif int comex_wait_proc(int proc, comex_group_t group) { return comex_wait_all(group); } int comex_wait(comex_request_t* hdl) { int index = 0; nb_t *nb = NULL; COMEX_ASSERT(NULL != hdl); index = *(int*)hdl; COMEX_ASSERT(index >= 0); COMEX_ASSERT(index < nb_max_outstanding); nb = &nb_state[index]; #if 0 /* this condition will likely be tripped if a blocking operation follows a * non-blocking operation*/ if (0 == nb->in_use) { fprintf(stderr, "p[%d] comex_wait Error: invalid handle\n", g_state.rank); } #endif nb_wait_for_all(nb); nb->in_use = 0; return COMEX_SUCCESS; } /* return 0 if operation is completed, 1 otherwise */ int comex_test(comex_request_t* hdl, int *status) { int index = 0; nb_t *nb = NULL; COMEX_ASSERT(NULL != hdl); index = *(int*)hdl; COMEX_ASSERT(index >= 0); COMEX_ASSERT(index < nb_max_outstanding); nb = &nb_state[index]; #if 0 /* this condition will likely be tripped if a blocking operation follows a * non-blocking operation*/ if (0 == nb->in_use) { fprintf(stderr, "{%d} comex_test Error: invalid handle\n", g_state.rank); } #endif if (!nb_test_for_all(nb)) { /* Completed */ COMEX_ASSERT(0 == nb->send_size); COMEX_ASSERT(0 == nb->recv_size); *status = 0; } else { /* Not completed */ *status = 1; } if (*status == 0) { nb->in_use = 0; } return COMEX_SUCCESS; } int comex_wait_all(comex_group_t group) { nb_wait_all(); return COMEX_SUCCESS; } int comex_nbput( void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_put(src, dst, bytes, world_proc, nb); return COMEX_SUCCESS; } int comex_nbget( void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_get(src, dst, bytes, world_proc, nb); return COMEX_SUCCESS; } int comex_nbacc( int datatype, void *scale, void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_acc(datatype, scale, src, dst, bytes, world_proc, nb); return COMEX_SUCCESS; } int comex_nbputs( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_puts(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); return COMEX_SUCCESS; } int comex_nbgets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_gets(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); return COMEX_SUCCESS; } int comex_nbaccs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_accs(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); return COMEX_SUCCESS; } int comex_nbputv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_putv(iov, iov_len, world_proc, nb); return COMEX_SUCCESS; } int comex_nbgetv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_getv(iov, iov_len, world_proc, nb); return COMEX_SUCCESS; } int comex_nbaccv( int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_accv(datatype, scale, iov, iov_len, world_proc, nb); return COMEX_SUCCESS; } int comex_rmw( int comex_op, void *ploc, void *prem, int extra, int proc, comex_group_t group) { header_t *header = NULL; char *message = NULL; int payload_int = 0; long payload_long = 0; int length = 0; op_t op = OP_NULL; long extra_long = (long)extra; int world_rank = 0; int master_rank = 0; comex_igroup_t *igroup = NULL; nb_t *nb = NULL; #if DEBUG fprintf(stderr, "[%d] comex_rmw(%d, %p, %p, %d, %d)\n", g_state.rank, comex_op, ploc, prem, extra, proc); #endif CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_rank = _get_world_rank(igroup, proc); master_rank = g_state.master[world_rank]; switch (comex_op) { case COMEX_FETCH_AND_ADD: op = OP_FETCH_AND_ADD; length = sizeof(int); payload_int = extra; break; case COMEX_FETCH_AND_ADD_LONG: op = OP_FETCH_AND_ADD; length = sizeof(long); payload_long = extra_long; break; case COMEX_SWAP: op = OP_SWAP; length = sizeof(int); payload_int = *((int*)ploc); break; case COMEX_SWAP_LONG: op = OP_SWAP; length = sizeof(long); payload_long = *((long*)ploc); break; default: COMEX_ASSERT(0); } /* create and prepare the header */ message = malloc(sizeof(header_t) + length); COMEX_ASSERT(message); MAYBE_MEMSET(message, 0, sizeof(header_t) + length); header = (header_t*)message; header->operation = op; header->remote_address = prem; header->local_address = ploc; header->rank = world_rank; header->length = length; switch (comex_op) { case COMEX_FETCH_AND_ADD: case COMEX_SWAP: (void)memcpy(message+sizeof(header_t), &payload_int, length); break; case COMEX_FETCH_AND_ADD_LONG: case COMEX_SWAP_LONG: (void)memcpy(message+sizeof(header_t), &payload_long, length); break; default: COMEX_ASSERT(0); } nb = nb_wait_for_handle(); nb_recv(ploc, length, master_rank, nb); /* prepost recv */ nb_send_header(message, sizeof(header_t)+length, master_rank, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } /* Mutex Operations */ int comex_create_mutexes(int num) { /* always on the world group */ int my_master = g_state.master[g_state.rank]; int status = 0; #if DEBUG fprintf(stderr, "[%d] comex_create_mutexes(num=%d)\n", g_state.rank, num); #endif /* preconditions */ COMEX_ASSERT(0 <= num); COMEX_ASSERT(NULL == num_mutexes); num_mutexes = (int*)malloc(group_list->size * sizeof(int)); /* exchange of mutex counts */ num_mutexes[group_list->rank] = num; status = MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, num_mutexes, 1, MPI_INT, group_list->comm); COMEX_ASSERT(MPI_SUCCESS == status); /* every process sends their own create message to their master */ { nb_t *nb = NULL; header_t *header = NULL; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_CREATE_MUTEXES; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = num; nb = nb_wait_for_handle(); nb_recv(NULL, 0, my_master, nb); /* prepost ack */ nb_send_header(header, sizeof(header_t), my_master, nb); nb_wait_for_all(nb); } comex_barrier(COMEX_GROUP_WORLD); return COMEX_SUCCESS; } int comex_destroy_mutexes() { /* always on the world group */ int my_master = g_state.master[g_state.rank]; #if DEBUG fprintf(stderr, "[%d] comex_destroy_mutexes()\n", g_state.rank); #endif /* preconditions */ COMEX_ASSERT(num_mutexes); /* this call is collective on the world group and this barrier ensures * there are no outstanding lock requests */ comex_barrier(COMEX_GROUP_WORLD); /* let masters know they need to participate */ /* first non-master rank in an SMP node sends the message to master */ if (_smallest_world_rank_with_same_hostid(group_list) == g_state.rank) { nb_t *nb = NULL; header_t *header = NULL; header = malloc(sizeof(header_t)); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_DESTROY_MUTEXES; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = num_mutexes[g_state.rank]; nb = nb_wait_for_handle(); nb_recv(NULL, 0, my_master, nb); /* prepost ack */ nb_send_header(header, sizeof(header_t), my_master, nb); nb_wait_for_all(nb); } free(num_mutexes); num_mutexes = NULL; return COMEX_SUCCESS; } int comex_lock(int mutex, int proc) { header_t *header = NULL; int world_rank = 0; int master_rank = 0; int ack = 0; comex_igroup_t *igroup = NULL; nb_t *nb = NULL; #if DEBUG fprintf(stderr, "[%d] comex_lock mutex=%d proc=%d\n", g_state.rank, mutex, proc); #endif CHECK_GROUP(COMEX_GROUP_WORLD,proc); igroup = comex_get_igroup_from_group(COMEX_GROUP_WORLD); world_rank = _get_world_rank(igroup, proc); master_rank = g_state.master[world_rank]; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_LOCK; header->remote_address = NULL; header->local_address = NULL; header->rank = world_rank; header->length = mutex; nb = nb_wait_for_handle(); nb_recv(&ack, sizeof(int), master_rank, nb); /* prepost ack */ nb_send_header(header, sizeof(header_t), master_rank, nb); nb_wait_for_all(nb); COMEX_ASSERT(mutex == ack); return COMEX_SUCCESS; } int comex_unlock(int mutex, int proc) { header_t *header = NULL; int world_rank = 0; int master_rank = 0; comex_igroup_t *igroup = NULL; nb_t *nb = NULL; #if DEBUG fprintf(stderr, "[%d] comex_unlock mutex=%d proc=%d\n", g_state.rank, mutex, proc); #endif CHECK_GROUP(COMEX_GROUP_WORLD,proc); igroup = comex_get_igroup_from_group(COMEX_GROUP_WORLD); world_rank = _get_world_rank(igroup, proc); master_rank = g_state.master[world_rank]; fence_array[master_rank] = 1; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_UNLOCK; header->remote_address = NULL; header->local_address = NULL; header->rank = world_rank; header->length = mutex; nb = nb_wait_for_handle(); nb_send_header(header, sizeof(header_t), master_rank, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_malloc(void *ptrs[], size_t size, comex_group_t group) { #if USE_SICM && TEST_SICM char cdevice[32]; # ifdef TEST_SICM_DEV strcpy(cdevice,STR(TEST_SICM_DEV)); # else strcpy(cdevice,"dram"); # endif return comex_malloc_mem_dev(ptrs, size, group, cdevice); #else comex_igroup_t *igroup = NULL; reg_entry_t *reg_entries = NULL; reg_entry_t my_reg; size_t size_entries = 0; int my_master = -1; int my_world_rank = -1; int i = 0; int is_notifier = 0; int reg_entries_local_count = 0; reg_entry_t *reg_entries_local = NULL; int status = 0; /* preconditions */ COMEX_ASSERT(ptrs); #if DEBUG fprintf(stderr, "[%d] comex_malloc(ptrs=%p, size=%lu, group=%d)\n", g_state.rank, ptrs, (long unsigned)size, group); #endif /* is this needed? */ comex_barrier(group); igroup = comex_get_igroup_from_group(group); my_world_rank = _get_world_rank(igroup, igroup->rank); my_master = g_state.master[my_world_rank]; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_malloc my_master=%d\n", g_state.rank, my_master); #endif int smallest_rank_with_same_hostid, largest_rank_with_same_hostid; int num_progress_ranks_per_node, is_node_ranks_packed; num_progress_ranks_per_node = get_num_progress_ranks_per_node(); is_node_ranks_packed = get_progress_rank_distribution_on_node(); smallest_rank_with_same_hostid = _smallest_world_rank_with_same_hostid(igroup); largest_rank_with_same_hostid = _largest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == get_my_master_rank_with_same_hostid(g_state.rank, g_state.node_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed); #if 0 #if MASTER_IS_SMALLEST_SMP_RANK // is_notifier = _smallest_world_rank_with_same_hostid(igroup) == g_state.rank; int smallest_rank_with_same_hostid = _smallest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == smallest_rank_with_same_hostid + g_state.node_size* ((g_state.rank - smallest_rank_with_same_hostid)/g_state.node_size); #else // is_notifier = _largest_world_rank_with_same_hostid(igroup) == g_state.rank; int largest_rank_with_same_hostid = _largest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == (largest_rank_with_same_hostid - g_state.node_size * ((largest_rank_with_same_hostid - g_state.rank)/g_state.node_size)); #endif #endif if (is_notifier) { reg_entries_local = malloc(sizeof(reg_entry_t)*g_state.node_size); } /* allocate space for registration cache entries */ size_entries = sizeof(reg_entry_t) * igroup->size; reg_entries = malloc(size_entries); MAYBE_MEMSET(reg_entries, 0, sizeof(reg_entry_t)*igroup->size); #if DEBUG fprintf(stderr, "[%d] comex_malloc lr_same_hostid=%d\n", g_state.rank, largest_rank_with_same_hostid); fprintf(stderr, "[%d] comex_malloc igroup size=%d\n", g_state.rank, igroup->size); fprintf(stderr, "[%d] comex_malloc node_size=%d\n", g_state.rank, g_state.node_size); fprintf(stderr, "[%d] comex_malloc is_notifier=%d\n", g_state.rank, is_notifier); fprintf(stderr, "[%d] rank, igroup size[5%d]\n", g_state.rank, igroup->size); #endif #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_malloc allocated reg entries\n", g_state.rank); #endif /* allocate and register segment */ MAYBE_MEMSET(&my_reg, 0, sizeof(reg_entry_t)); if (0 == size) { reg_cache_nullify(&my_reg); } else { my_reg = *_comex_malloc_local(sizeof(char)*size); } #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_malloc allocated and registered local shmem\n", g_state.rank); #endif /* exchange buffer address via reg entries */ reg_entries[igroup->rank] = my_reg; status = MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, reg_entries, sizeof(reg_entry_t), MPI_BYTE, igroup->comm); #if DEBUG fprintf(stderr, "[%d] comex_malloc allgather status [%d]\n", g_state.rank, status); #endif COMEX_ASSERT(MPI_SUCCESS == status); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_malloc allgather reg entries\n", g_state.rank); #endif /* insert reg entries into local registration cache */ for (i=0; isize; ++i) { if (NULL == reg_entries[i].buf) { /* a proc did not allocate (size==0) */ #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_malloc found NULL buf at %d\n", g_state.rank, i); #endif } else if (g_state.rank == reg_entries[i].rank) { /* we already registered our own memory, but PR hasn't */ #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_malloc found self at %d\n", g_state.rank, i); #endif if (is_notifier) { /* does this need to be a memcpy?? */ reg_entries_local[reg_entries_local_count++] = reg_entries[i]; } } // else if (g_state.hostid[reg_entries[i].rank] // == g_state.hostid[my_world_rank]) else if (g_state.master[reg_entries[i].rank] == g_state.master[get_my_master_rank_with_same_hostid(g_state.rank, g_state.node_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed)] ) #if 0 #if MASTER_IS_SMALLEST_SMP_RANK else if (g_state.master[reg_entries[i].rank] == g_state.master[(smallest_rank_with_same_hostid + g_state.node_size * ((g_state.rank - smallest_rank_with_same_hostid)/g_state.node_size))]) #else else if (g_state.master[reg_entries[i].rank] == g_state.master[(largest_rank_with_same_hostid - g_state.node_size * ((largest_rank_with_same_hostid - g_state.rank)/g_state.node_size))]) #endif #endif { /* same SMP node, need to mmap */ /* open remote shared memory object */ #if ENABLE_SYSV void *memory = _shm_attach(reg_entries[i].name, reg_entries[i].len, reg_entries[i].key); #else void *memory = _shm_attach(reg_entries[i].name, reg_entries[i].len); #endif #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_malloc registering " "rank=%d buf=%p len=%lu name=%s map=%p\n", g_state.rank, reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, memory); #endif (void)reg_cache_insert( reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, #if ENABLE_SYSV reg_entries[i].key, #endif memory,0 #if USE_SICM #if SICM_OLD ,NULL #else ,nill #endif #endif ); if (is_notifier) { /* does this need to be a memcpy?? */ reg_entries_local[reg_entries_local_count++] = reg_entries[i]; } } else { #if 0 /* remote SMP node */ /* i.e. we know about the mem but don't have local shared access */ (void)reg_cache_insert( reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, NULL); #endif } } /* assign the ptr array to return to caller */ for (i=0; isize; ++i) { ptrs[i] = reg_entries[i].buf; } /* send reg entries to my master */ /* first non-master rank in an SMP node sends the message to master */ if (is_notifier) { nb_t *nb = NULL; int reg_entries_local_size = 0; int message_size = 0; char *message = NULL; header_t *header = NULL; reg_entries_local_size = sizeof(reg_entry_t)*reg_entries_local_count; message_size = sizeof(header_t) + reg_entries_local_size; message = malloc(message_size); COMEX_ASSERT(message); header = (header_t*)message; header->operation = OP_MALLOC; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = reg_entries_local_count; (void)memcpy(message+sizeof(header_t), reg_entries_local, reg_entries_local_size); nb = nb_wait_for_handle(); nb_recv(NULL, 0, my_master, nb); /* prepost ack */ nb_send_header(message, message_size, my_master, nb); nb_wait_for_all(nb); free(reg_entries_local); } free(reg_entries); comex_barrier(group); return COMEX_SUCCESS; #endif } int comex_malloc_mem_dev(void *ptrs[], size_t size, comex_group_t group, const char* device) { #if (!defined(USE_SICM) || !USE_SICM) return comex_malloc(ptrs,size,group); #else comex_igroup_t *igroup = NULL; reg_entry_t *reg_entries = NULL; reg_entry_t my_reg; size_t size_entries = 0; int my_master = -1; int my_world_rank = -1; int i = 0; int is_notifier = 0; int reg_entries_local_count = 0; reg_entry_t *reg_entries_local = NULL; int status = 0; #if SICM_OLD sicm_device *idevice = NULL; #else sicm_device_list idevice; idevice.count = 0; idevice.devices = NULL; #endif printf("p[%d] calling comex_malloc_mem_dev\n",g_state.rank); /* preconditions */ COMEX_ASSERT(ptrs); #if DEBUG fprintf(stderr, "[%d] comex_malloc(ptrs=%p, size=%lu, group=%d)\n", g_state.rank, ptrs, (long unsigned)size, group); #endif /* is this needed? */ comex_barrier(group); igroup = comex_get_igroup_from_group(group); my_world_rank = _get_world_rank(igroup, igroup->rank); my_master = g_state.master[my_world_rank]; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_malloc my_master=%d\n", g_state.rank, my_master); #endif #if MASTER_IS_SMALLEST_SMP_RANK is_notifier = _smallest_world_rank_with_same_hostid(igroup) == g_state.rank; #else is_notifier = _largest_world_rank_with_same_hostid(igroup) == g_state.rank; #endif if (is_notifier) { reg_entries_local = malloc(sizeof(reg_entry_t)*g_state.node_size); } /* allocate space for registration cache entries */ size_entries = sizeof(reg_entry_t) * igroup->size; reg_entries = malloc(size_entries); MAYBE_MEMSET(reg_entries, 0, sizeof(reg_entry_t)*igroup->size); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_malloc allocated reg entries\n", g_state.rank); #endif /* allocate and register segment */ MAYBE_MEMSET(&my_reg, 0, sizeof(reg_entry_t)); if (0 == size) { reg_cache_nullify(&my_reg); } else { idevice = device_dram; if (!strncmp(device,"dram",4)) { idevice = device_dram; } else if (!strncmp(device,"knl_hbm",7)) { idevice = device_knl_hbm; } else if (!strncmp(device,"ppc_hbm",7)) { idevice = device_ppc_hbm; } my_reg = *_comex_malloc_local_memdev(sizeof(char)*size, idevice); } #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_malloc allocated and registered local shmem\n", g_state.rank); #endif /* exchange buffer address via reg entries */ reg_entries[igroup->rank] = my_reg; status = MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, reg_entries, sizeof(reg_entry_t), MPI_BYTE, igroup->comm); COMEX_ASSERT(MPI_SUCCESS == status); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_malloc allgather reg entries\n", g_state.rank); #endif /* insert reg entries into local registration cache */ for (i=0; isize; ++i) { if (NULL == reg_entries[i].buf) { /* a proc did not allocate (size==0) */ #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_malloc found NULL buf at %d\n", g_state.rank, i); #endif } else if (g_state.rank == reg_entries[i].rank) { /* we already registered our own memory, but PR hasn't */ #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_malloc found self at %d\n", g_state.rank, i); #endif if (is_notifier) { /* does this need to be a memcpy?? */ reg_entries_local[reg_entries_local_count++] = reg_entries[i]; } } else if (!strcmp(g_state.host[reg_entries[i].rank].name, g_state.host[my_world_rank].name)) { /* same SMP node, need to mmap */ /* open remote shared memory object */ void *memory = _shm_attach_memdev(reg_entries[i].name, reg_entries[i].len, idevice); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_malloc registering " "rank=%d buf=%p len=%lu name=%s map=%p\n", g_state.rank, reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, memory); #endif (void)reg_cache_insert( reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, memory, 1, idevice); if (is_notifier) { /* does this need to be a memcpy?? */ reg_entries_local[reg_entries_local_count++] = reg_entries[i]; } } else { #if 0 /* remote SMP node */ /* i.e. we know about the mem but don't have local shared access */ (void)reg_cache_insert( reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, NULL); #endif } } /* assign the ptr array to return to caller */ for (i=0; isize; ++i) { ptrs[i] = reg_entries[i].buf; } /* send reg entries to my master */ /* first non-master rank in an SMP node sends the message to master */ if (is_notifier) { nb_t *nb = NULL; int reg_entries_local_size = 0; int message_size = 0; char *message = NULL; header_t *header = NULL; reg_entries_local_size = sizeof(reg_entry_t)*reg_entries_local_count; #if DEBUG fprintf(stderr, "[%d] comex_malloc, reg_entries_local_count[%d]\n", g_state.rank, reg_entries_local_count); #endif message_size = sizeof(header_t) + reg_entries_local_size; message = malloc(message_size); COMEX_ASSERT(message); header = (header_t*)message; header->operation = OP_MALLOC; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = reg_entries_local_count; (void)memcpy(message+sizeof(header_t), reg_entries_local, reg_entries_local_size); nb = nb_wait_for_handle(); nb_recv(NULL, 0, my_master, nb); /* prepost ack */ nb_send_header(message, message_size, my_master, nb); nb_wait_for_all(nb); free(reg_entries_local); } free(reg_entries); comex_barrier(group); return COMEX_SUCCESS; #endif } /* one unnamed semaphore per world process */ void _malloc_semaphore() { char *name = NULL; char *names = NULL; sem_t *my_sem = NULL; int status = 0; MPI_Datatype shm_name_type; int i = 0; #if DEBUG fprintf(stderr, "[%d] _malloc_semaphore()\n", g_state.rank); #endif status = MPI_Type_contiguous(SHM_NAME_SIZE, MPI_CHAR, &shm_name_type); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Type_commit(&shm_name_type); COMEX_ASSERT(MPI_SUCCESS == status); semaphores = (sem_t**)malloc(sizeof(sem_t*) * g_state.size); COMEX_ASSERT(semaphores); name = _generate_shm_name(g_state.rank); COMEX_ASSERT(name); #if ENABLE_UNNAMED_SEM { my_sem = _shm_create(name, sizeof(sem_t)); /* initialize the memory as an inter-process semaphore */ if (0 != sem_init(my_sem, 1, 1)) { perror("_malloc_semaphore: sem_init"); comex_error("_malloc_semaphore: sem_init", -1); } } #else { my_sem = sem_open(name, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR, 1); if (SEM_FAILED == my_sem) { if (EEXIST == errno) { status = sem_unlink(name); if (-1 == status) { perror("_malloc_semaphore: sem_unlink"); comex_error("_malloc_semaphore: sem_unlink", status); } } /* second try */ my_sem = sem_open(name, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR, 1); } if (SEM_FAILED == my_sem) { perror("_malloc_semaphore: sem_open"); comex_error("_malloc_semaphore: sem_open", -1); } } #endif /* store my sem in global cache */ semaphores[g_state.rank] = my_sem; names = (char*)malloc(sizeof(char) * SHM_NAME_SIZE * g_state.size); COMEX_ASSERT(names); /* exchange names */ (void)memcpy(&names[SHM_NAME_SIZE*g_state.rank], name, SHM_NAME_SIZE); status = MPI_Allgather(MPI_IN_PLACE, 1, shm_name_type, names, 1, shm_name_type, g_state.comm); COMEX_ASSERT(MPI_SUCCESS == status); /* create/open remote semaphores and store in cache */ for (i=0; irank]; my_master = g_state.master[my_world_rank]; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free my_master=%d\n", g_state.rank, my_master); #endif int num_progress_ranks_per_node = get_num_progress_ranks_per_node(); int is_node_ranks_packed = get_progress_rank_distribution_on_node(); int smallest_rank_with_same_hostid = _smallest_world_rank_with_same_hostid(igroup); int largest_rank_with_same_hostid = _largest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == get_my_master_rank_with_same_hostid(g_state.rank, g_state.node_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed); #if 0 #if MASTER_IS_SMALLEST_SMP_RANK // is_notifier = _smallest_world_rank_with_same_hostid(igroup) == g_state.rank; int smallest_rank_with_same_hostid = _smallest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == smallest_rank_with_same_hostid + g_state.node_size* ((g_state.rank - smallest_rank_with_same_hostid)/g_state.node_size); #else // is_notifier = _largest_world_rank_with_same_hostid(igroup) == g_state.rank; int largest_rank_with_same_hostid = _largest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == (largest_rank_with_same_hostid - g_state.node_size * ((largest_rank_with_same_hostid - g_state.rank)/g_state.node_size)); #endif #endif if (is_notifier) { rank_ptrs = malloc(sizeof(rank_ptr_t)*g_state.node_size); } /* allocate receive buffer for exchange of pointers */ ptrs = (void **)malloc(sizeof(void *) * igroup->size); COMEX_ASSERT(ptrs); ptrs[igroup->rank] = ptr; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free ptrs allocated and assigned\n", g_state.rank); #endif /* exchange of pointers */ status = MPI_Allgather(MPI_IN_PLACE, sizeof(void *), MPI_BYTE, ptrs, sizeof(void *), MPI_BYTE, igroup->comm); COMEX_ASSERT(MPI_SUCCESS == status); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free ptrs exchanged\n", g_state.rank); #endif /* remove all pointers from registration cache */ for (i=0; isize; ++i) { if (i == igroup->rank) { #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free found self at %d\n", g_state.rank, i); #endif if (is_notifier) { /* does this need to be a memcpy? */ rank_ptrs[reg_entries_local_count].rank = world_ranks[i]; rank_ptrs[reg_entries_local_count].ptr = ptrs[i]; reg_entries_local_count++; } } else if (NULL == ptrs[i]) { #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free found NULL at %d\n", g_state.rank, i); #endif } // else if (g_state.hostid[world_ranks[i]] // == g_state.hostid[g_state.rank]) else if (g_state.master[world_ranks[i]] == g_state.master[get_my_master_rank_with_same_hostid(g_state.rank, g_state.node_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed)] ) #if 0 #if MASTER_IS_SMALLEST_SMP_RANK else if (g_state.master[world_ranks[i]] == g_state.master[(smallest_rank_with_same_hostid + g_state.node_size * ((g_state.rank - smallest_rank_with_same_hostid)/g_state.node_size))]) #else else if (g_state.master[world_ranks[i]] == g_state.master[(largest_rank_with_same_hostid - g_state.node_size * ((largest_rank_with_same_hostid - g_state.rank)/g_state.node_size))]) #endif #endif { /* same SMP node */ reg_entry_t *reg_entry = NULL; int retval = 0; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free same hostid at %d\n", g_state.rank, i); #endif /* find the registered memory */ reg_entry = reg_cache_find(world_ranks[i], ptrs[i], 0); COMEX_ASSERT(reg_entry); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free found reg entry\n", g_state.rank); #endif /* unmap the memory */ #if ENABLE_SYSV /* printf("p[%d] DETACH SHM name: %s key: %d\n",g_state.rank,reg_entry->name, reg_entry->key); */ _shmdt_err(shmdt(reg_entry->mapped)); #else retval = munmap(reg_entry->mapped, reg_entry->len); check_devshm(0, -(reg_entry->len)); if (-1 == retval) { perror("comex_free: munmap"); comex_error("comex_free: munmap", retval); } #endif #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free unmapped mapped memory in reg entry\n", g_state.rank); #endif reg_cache_delete(world_ranks[i], ptrs[i]); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free deleted reg cache entry\n", g_state.rank); #endif if (is_notifier) { /* does this need to be a memcpy? */ rank_ptrs[reg_entries_local_count].rank = world_ranks[i]; rank_ptrs[reg_entries_local_count].ptr = ptrs[i]; reg_entries_local_count++; } } else { #if 0 reg_cache_delete(world_ranks[i], ptrs[i]); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free deleted reg cache entry\n", g_state.rank); #endif #endif } } /* send ptrs to my master */ /* first non-master rank in an SMP node sends the message to master */ if (is_notifier) { nb_t *nb = NULL; int rank_ptrs_local_size = 0; int message_size = 0; char *message = NULL; header_t *header = NULL; rank_ptrs_local_size = sizeof(rank_ptr_t) * reg_entries_local_count; message_size = sizeof(header_t) + rank_ptrs_local_size; message = malloc(message_size); COMEX_ASSERT(message); header = (header_t*)message; header->operation = OP_FREE; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = reg_entries_local_count; (void)memcpy(message+sizeof(header_t), rank_ptrs, rank_ptrs_local_size); nb = nb_wait_for_handle(); nb_recv(NULL, 0, my_master, nb); /* prepost ack */ nb_send_header(message, message_size, my_master, nb); nb_wait_for_all(nb); free(rank_ptrs); } /* free ptrs array */ free(ptrs); free(world_ranks); /* remove my ptr from reg cache and free ptr */ comex_free_local(ptr); /* Is this needed? */ comex_barrier(group); return COMEX_SUCCESS; #endif } int comex_free_dev(void *ptr, comex_group_t group) { #if USE_SICM comex_igroup_t *igroup = NULL; int my_world_rank = -1; int *world_ranks = NULL; int my_master = -1; void **ptrs = NULL; int i = 0; int is_notifier = 0; int reg_entries_local_count = 0; rank_ptr_t *rank_ptrs = NULL; int status = 0; comex_barrier(group); #if DEBUG fprintf(stderr, "[%d] comex_free_dev(ptr=%p, group=%d)\n", g_state.rank, ptr, group); #endif igroup = comex_get_igroup_from_group(group); world_ranks = _get_world_ranks(igroup); my_world_rank = world_ranks[igroup->rank]; my_master = g_state.master[my_world_rank]; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free_dev my_master=%d\n", g_state.rank, my_master); #endif int num_progress_ranks_per_node = get_num_progress_ranks_per_node(); int is_node_ranks_packed = get_progress_rank_distribution_on_node(); int smallest_rank_with_same_hostid = _smallest_world_rank_with_same_hostid(igroup); int largest_rank_with_same_hostid = _largest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == get_my_master_rank_with_same_hostid(g_state.rank, g_state.node_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed); #if 0 #if MASTER_IS_SMALLEST_SMP_RANK // is_notifier = _smallest_world_rank_with_same_hostid(igroup) == g_state.rank; int smallest_rank_with_same_hostid = _smallest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == smallest_rank_with_same_hostid + g_state.node_size* ((g_state.rank - smallest_rank_with_same_hostid)/g_state.node_size); #else // is_notifier = _largest_world_rank_with_same_hostid(igroup) == g_state.rank; int largest_rank_with_same_hostid = _largest_world_rank_with_same_hostid(igroup); is_notifier = g_state.rank == (largest_rank_with_same_hostid - g_state.node_size * ((largest_rank_with_same_hostid - g_state.rank)/g_state.node_size)); #endif #endif if (is_notifier) { rank_ptrs = malloc(sizeof(rank_ptr_t)*g_state.node_size); } /* allocate receive buffer for exchange of pointers */ ptrs = (void **)malloc(sizeof(void *) * igroup->size); COMEX_ASSERT(ptrs); ptrs[igroup->rank] = ptr; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free_dev ptrs allocated and assigned\n", g_state.rank); #endif /* exchange of pointers */ status = MPI_Allgather(MPI_IN_PLACE, sizeof(void *), MPI_BYTE, ptrs, sizeof(void *), MPI_BYTE, igroup->comm); COMEX_ASSERT(MPI_SUCCESS == status); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free_dev ptrs exchanged\n", g_state.rank); #endif /* remove all pointers from registration cache */ for (i=0; isize; ++i) { if (i == igroup->rank) { #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free_dev found self at %d\n", g_state.rank, i); #endif if (is_notifier) { /* does this need to be a memcpy? */ rank_ptrs[reg_entries_local_count].rank = world_ranks[i]; rank_ptrs[reg_entries_local_count].ptr = ptrs[i]; reg_entries_local_count++; } } else if (NULL == ptrs[i]) { #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free_dev found NULL at %d\n", g_state.rank, i); #endif } // else if (g_state.hostid[world_ranks[i]] // == g_state.hostid[g_state.rank]) else if (g_state.master[world_ranks[i]] == g_state.master[get_my_master_rank_with_same_hostid(g_state.rank, g_state.node_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed)] ) #if 0 #if MASTER_IS_SMALLEST_SMP_RANK else if (g_state.master[world_ranks[i]] == g_state.master[(smallest_rank_with_same_hostid + g_state.node_size * ((g_state.rank - smallest_rank_with_same_hostid)/g_state.node_size))]) #else else if (g_state.master[world_ranks[i]] == g_state.master[(largest_rank_with_same_hostid - g_state.node_size * ((largest_rank_with_same_hostid - g_state.rank)/g_state.node_size))]) #endif #endif { /* same SMP node */ reg_entry_t *reg_entry = NULL; int retval = 0; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free_dev same hostid at %d\n", g_state.rank, i); #endif /* find the registered memory */ reg_entry = reg_cache_find(world_ranks[i], ptrs[i], 0); COMEX_ASSERT(reg_entry); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free_dev found reg entry\n", g_state.rank); #endif /* free the memory */ sicm_free(reg_entry->mapped); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free_dev unmapped mapped memory in reg entry\n", g_state.rank); #endif reg_cache_delete(world_ranks[i], ptrs[i]); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] comex_free_dev deleted reg cache entry\n", g_state.rank); #endif if (is_notifier) { /* does this need to be a memcpy? */ rank_ptrs[reg_entries_local_count].rank = world_ranks[i]; rank_ptrs[reg_entries_local_count].ptr = ptrs[i]; reg_entries_local_count++; } } } /* send ptrs to my master */ /* first non-master rank in an SMP node sends the message to master */ if (is_notifier) { nb_t *nb = NULL; int rank_ptrs_local_size = 0; int message_size = 0; char *message = NULL; header_t *header = NULL; rank_ptrs_local_size = sizeof(rank_ptr_t) * reg_entries_local_count; message_size = sizeof(header_t) + rank_ptrs_local_size; message = malloc(message_size); COMEX_ASSERT(message); header = (header_t*)message; header->operation = OP_FREE; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = reg_entries_local_count; (void)memcpy(message+sizeof(header_t), rank_ptrs, rank_ptrs_local_size); nb = nb_wait_for_handle(); nb_recv(NULL, 0, my_master, nb); /* prepost ack */ nb_send_header(message, message_size, my_master, nb); nb_wait_for_all(nb); free(rank_ptrs); } /* free ptrs array */ free(ptrs); free(world_ranks); /* remove my ptr from reg cache and free ptr */ _comex_free_local_memdev(ptr); /* Is this needed? */ comex_barrier(group); return COMEX_SUCCESS; #else return comex_free(ptr, group); #endif } STATIC void _progress_server() { int running = 0; char *static_header_buffer = NULL; int static_header_buffer_size = 0; int extra_size = 0; #if DEBUG fprintf(stderr, "[%d] _progress_server()\n", g_state.rank); fprintf(stderr, "[%d] _progress_server(); node_size[%d]\n", g_state.rank, g_state.node_size); #endif { int status = _set_affinity(g_state.node_size); if (0 != status) { status = _set_affinity(g_state.node_size-1); COMEX_ASSERT(0 == status); } } /* static header buffer size must be large enough to hold the biggest * message that might possibly be sent using a header type message. */ static_header_buffer_size += sizeof(header_t); /* extra header info could be reg entries, one per local rank */ extra_size = sizeof(reg_entry_t)*g_state.node_size; /* or, extra header info could be an acc scale plus stride */ if ((sizeof(stride_t)+sizeof(DoubleComplex)) > extra_size) { extra_size = sizeof(stride_t)+sizeof(DoubleComplex); } static_header_buffer_size += extra_size; /* after all of the above, possibly grow the size based on user request */ if (static_header_buffer_size < eager_threshold) { static_header_buffer_size = eager_threshold; } /* initialize shared buffers */ static_header_buffer = (char*)malloc(sizeof(char)*static_header_buffer_size); COMEX_ASSERT(static_header_buffer); static_server_buffer = (char*)malloc(sizeof(char)*static_server_buffer_size); COMEX_ASSERT(static_server_buffer); running = 1; while (running) { int source = 0; int length = 0; char *payload = NULL; header_t *header = NULL; MPI_Status recv_status; MPI_Recv(static_header_buffer, static_header_buffer_size, MPI_CHAR, MPI_ANY_SOURCE, COMEX_TAG, g_state.comm, &recv_status); MPI_Get_count(&recv_status, MPI_CHAR, &length); source = recv_status.MPI_SOURCE; # if DEBUG fprintf(stderr, "[%d] progress MPI_Recv source=%d length=%d\n", g_state.rank, source, length); # endif header = (header_t*)static_header_buffer; payload = static_header_buffer + sizeof(header_t); /* dispatch message handler */ switch (header->operation) { case OP_PUT: _put_handler(header, payload, source); break; case OP_PUT_PACKED: _put_packed_handler(header, payload, source); break; case OP_PUT_DATATYPE: _put_datatype_handler(header, payload, source); break; case OP_PUT_IOV: _put_iov_handler(header, source); break; case OP_GET: _get_handler(header, source); break; case OP_GET_PACKED: _get_packed_handler(header, payload, source); break; case OP_GET_DATATYPE: _get_datatype_handler(header, payload, source); break; case OP_GET_IOV: _get_iov_handler(header, source); break; case OP_ACC_INT: case OP_ACC_DBL: case OP_ACC_FLT: case OP_ACC_CPL: case OP_ACC_DCP: case OP_ACC_LNG: _acc_handler(header, payload, source); break; case OP_ACC_INT_PACKED: case OP_ACC_DBL_PACKED: case OP_ACC_FLT_PACKED: case OP_ACC_CPL_PACKED: case OP_ACC_DCP_PACKED: case OP_ACC_LNG_PACKED: _acc_packed_handler(header, payload, source); break; case OP_ACC_INT_IOV: case OP_ACC_DBL_IOV: case OP_ACC_FLT_IOV: case OP_ACC_CPL_IOV: case OP_ACC_DCP_IOV: case OP_ACC_LNG_IOV: _acc_iov_handler(header, payload, source); break; case OP_FENCE: _fence_handler(header, source); break; case OP_FETCH_AND_ADD: _fetch_and_add_handler(header, payload, source); break; case OP_SWAP: _swap_handler(header, payload, source); break; case OP_CREATE_MUTEXES: _mutex_create_handler(header, source); break; case OP_DESTROY_MUTEXES: _mutex_destroy_handler(header, source); break; case OP_LOCK: _lock_handler(header, source); break; case OP_UNLOCK: _unlock_handler(header, source); break; case OP_QUIT: running = 0; break; case OP_MALLOC: _malloc_handler(header, payload, source); break; case OP_FREE: _free_handler(header, payload, source); break; default: fprintf(stderr, "[%d] header operation not recognized: %d\n", g_state.rank, header->operation); COMEX_ASSERT(0); } } initialized = 0; free(static_header_buffer); free(static_server_buffer); _free_semaphore(); free(mutexes); free(lq_heads); MPI_Barrier(g_state.comm); /* reg_cache */ reg_cache_destroy(); // destroy the communicators #if DEBUG fprintf(stderr, "[%d] before comex_group_finalize()\n", g_state.rank); #endif comex_group_finalize(); #if DEBUG fprintf(stderr, "[%d] after comex_group_finalize()\n", g_state.rank); #endif #if DEBUG_TO_FILE fclose(comex_trace_file); #endif free(nb_state); #if DEBUG printf(" %d freed nb_state ptr %p \n", g_state.rank, nb_state); #endif if (!init_from_comm) { // assume this is the end of a user's application if initialized from // world communicator MPI_Finalize(); exit(EXIT_SUCCESS); } } STATIC void _put_handler(header_t *header, char *payload, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int use_eager = _eager_check(header->length); #if DEBUG fprintf(stderr, "[%d] _put_handler rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory( reg_entry, header->remote_address); if (use_eager) { (void)memcpy(mapped_offset, payload, header->length); } else { char *buf = (char*)mapped_offset; int bytes_remaining = header->length; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; server_recv(buf, size, proc); buf += size; bytes_remaining -= size; } while (bytes_remaining > 0); } } STATIC void _put_packed_handler(header_t *header, char *payload, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; char *packed_buffer = NULL; stride_t *stride = NULL; int use_eager = _eager_check(sizeof(stride_t)+header->length); #if DEBUG int i=0; #endif #if DEBUG fprintf(stderr, "[%d] _put_packed_handler rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif stride = (stride_t*)payload; COMEX_ASSERT(stride->stride_levels >= 0); COMEX_ASSERT(stride->stride_levels < COMEX_MAX_STRIDE_LEVEL); #if DEBUG fprintf(stderr, "[%d] _put_packed_handler stride_levels=%d, count[0]=%d\n", g_state.rank, stride->stride_levels, stride->count[0]); for (i=0; istride_levels; ++i) { fprintf(stderr, "[%d] stride[%d]=%d count[%d+1]=%d\n", g_state.rank, i, stride->stride[i], i, stride->count[i+1]); } #endif reg_entry = reg_cache_find( header->rank, header->remote_address, stride->count[0]); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory( reg_entry, header->remote_address); if (use_eager) { packed_buffer = payload+sizeof(stride_t); unpack(packed_buffer, mapped_offset, stride->stride, stride->count, stride->stride_levels); } else { if ((unsigned)header->length > static_server_buffer_size) { packed_buffer = malloc(header->length); } else { packed_buffer = static_server_buffer; } { /* we receive the buffer backwards */ char *buf = packed_buffer + header->length; int bytes_remaining = header->length; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; buf -= size; server_recv(buf, size, proc); bytes_remaining -= size; } while (bytes_remaining > 0); } unpack(packed_buffer, mapped_offset, stride->stride, stride->count, stride->stride_levels); if ((unsigned)header->length > static_server_buffer_size) { free(packed_buffer); } } } STATIC void _put_datatype_handler(header_t *header, char *payload, int proc) { MPI_Datatype dst_type; reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; stride_t *stride = NULL; int ierr; #if DEBUG int i=0; #endif #if DEBUG fprintf(stderr, "[%d] _put_datatype_handler rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif stride = (stride_t*)payload; COMEX_ASSERT(stride); COMEX_ASSERT(stride->stride_levels >= 0); COMEX_ASSERT(stride->stride_levels < COMEX_MAX_STRIDE_LEVEL); #if DEBUG fprintf(stderr, "[%d] _put_datatype_handler stride_levels=%d, count[0]=%d\n", g_state.rank, stride->stride_levels, stride->count[0]); for (i=0; istride_levels; ++i) { fprintf(stderr, "[%d] stride[%d]=%d count[%d+1]=%d\n", g_state.rank, i, stride->stride[i], i, stride->count[i+1]); } #endif reg_entry = reg_cache_find( header->rank, header->remote_address, stride->count[0]); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory( reg_entry, header->remote_address); strided_to_subarray_dtype(stride->stride, stride->count, stride->stride_levels, MPI_BYTE, &dst_type); ierr = MPI_Type_commit(&dst_type); translate_mpi_error(ierr,"_put_datatype_handler:MPI_Type_commit"); server_recv_datatype(mapped_offset, dst_type, proc); ierr = MPI_Type_free(&dst_type); translate_mpi_error(ierr,"_put_datatype_handler:MPI_Type_free"); } STATIC void _put_iov_handler(header_t *header, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int i = 0; char *packed_buffer = NULL; int packed_index = 0; char *iov_buf = NULL; int iov_off = 0; int limit = 0; int bytes = 0; void **src = NULL; void **dst = NULL; #if DEBUG fprintf(stderr, "[%d] _put_iov_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG fprintf(stderr, "[%d] _put_iov_handler header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_PUT_IOV == header->operation); iov_buf = malloc(header->length); COMEX_ASSERT(iov_buf); server_recv(iov_buf, header->length, proc); limit = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(limit > 0); bytes = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(bytes > 0); src = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; dst = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; COMEX_ASSERT(iov_off == header->length); #if DEBUG fprintf(stderr, "[%d] _put_iov_handler limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif if ((unsigned)(bytes*limit) > static_server_buffer_size) { packed_buffer = malloc(bytes*limit); COMEX_ASSERT(packed_buffer); } else { packed_buffer = static_server_buffer; } server_recv(packed_buffer, bytes * limit, proc); packed_index = 0; for (i=0; irank, dst[i], bytes); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory( reg_entry, dst[i]); (void)memcpy(mapped_offset, &packed_buffer[packed_index], bytes); packed_index += bytes; } COMEX_ASSERT(packed_index == bytes*limit); if ((unsigned)(bytes*limit) > static_server_buffer_size) { free(packed_buffer); } free(iov_buf); } STATIC void _get_handler(header_t *header, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; #if DEBUG fprintf(stderr, "[%d] _get_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG fprintf(stderr, "[%d] header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif COMEX_ASSERT(OP_GET == header->operation); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); { char *buf = (char*)mapped_offset; int bytes_remaining = header->length; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; server_send(buf, size, proc); buf += size; bytes_remaining -= size; } while (bytes_remaining > 0); } } STATIC void _get_packed_handler(header_t *header, char *payload, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; char *packed_buffer = NULL; int packed_index = 0; stride_t *stride_src = (stride_t*)payload; #if DEBUG fprintf(stderr, "[%d] _get_packed_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG fprintf(stderr, "[%d] header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_GET_PACKED == header->operation); COMEX_ASSERT(stride_src->stride_levels >= 0); COMEX_ASSERT(stride_src->stride_levels < COMEX_MAX_STRIDE_LEVEL); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); packed_buffer = pack(mapped_offset, stride_src->stride, stride_src->count, stride_src->stride_levels, &packed_index); { /* we send the buffer backwards */ char *buf = packed_buffer + packed_index; int bytes_remaining = packed_index; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; buf -= size; server_send(buf, size, proc); bytes_remaining -= size; } while (bytes_remaining > 0); } free(packed_buffer); } STATIC void _get_datatype_handler(header_t *header, char *payload, int proc) { MPI_Datatype src_type; reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; stride_t *stride_src = NULL; int ierr; #if DEBUG int i; fprintf(stderr, "[%d] _get_datatype_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG fprintf(stderr, "[%d] header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_GET_DATATYPE == header->operation); stride_src = (stride_t*)payload; COMEX_ASSERT(stride_src); COMEX_ASSERT(stride_src->stride_levels >= 0); COMEX_ASSERT(stride_src->stride_levels < COMEX_MAX_STRIDE_LEVEL); #if DEBUG for (i=0; istride_levels; ++i) { fprintf(stderr, "\tstride[%d]=%d\n", i, stride_src->stride[i]); } for (i=0; istride_levels+1; ++i) { fprintf(stderr, "\tcount[%d]=%d\n", i, stride_src->count[i]); } #endif reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); strided_to_subarray_dtype(stride_src->stride, stride_src->count, stride_src->stride_levels, MPI_BYTE, &src_type); ierr = MPI_Type_commit(&src_type); translate_mpi_error(ierr,"_get_datatype_handler:MPI_Type_commit"); server_send_datatype(mapped_offset, src_type, proc); ierr = MPI_Type_free(&src_type); translate_mpi_error(ierr,"_get_datatype_handler:MPI_Type_free"); } STATIC void _get_iov_handler(header_t *header, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int i = 0; char *packed_buffer = NULL; int packed_index = 0; char *iov_buf = NULL; int iov_off = 0; int limit = 0; int bytes = 0; void **src = NULL; void **dst = NULL; #if DEBUG fprintf(stderr, "[%d] _get_iov_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG fprintf(stderr, "[%d] _get_iov_handler header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_GET_IOV == header->operation); iov_buf = malloc(header->length); COMEX_ASSERT(iov_buf); server_recv(iov_buf, header->length, proc); limit = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(limit > 0); bytes = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(bytes > 0); src = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; dst = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; COMEX_ASSERT(iov_off == header->length); #if DEBUG fprintf(stderr, "[%d] _get_iov_handler limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif if ((unsigned)(bytes*limit) > static_server_buffer_size) { packed_buffer = malloc(bytes*limit); COMEX_ASSERT(packed_buffer); } else { packed_buffer = static_server_buffer; } packed_index = 0; for (i=0; irank, src[i], bytes); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, src[i]); (void)memcpy(&packed_buffer[packed_index], mapped_offset, bytes); packed_index += bytes; } COMEX_ASSERT(packed_index == bytes*limit); server_send(packed_buffer, packed_index, proc); if ((unsigned)(bytes*limit) > static_server_buffer_size) { free(packed_buffer); } free(iov_buf); } STATIC void _acc_handler(header_t *header, char *scale, int proc) { int sizeof_scale = 0; int acc_type = 0; reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; char *acc_buffer = NULL; int use_eager = 0; #if DEBUG fprintf(stderr, "[%d] _acc_handler\n", g_state.rank); #endif switch (header->operation) { case OP_ACC_INT: acc_type = COMEX_ACC_INT; sizeof_scale = sizeof(int); break; case OP_ACC_DBL: acc_type = COMEX_ACC_DBL; sizeof_scale = sizeof(double); break; case OP_ACC_FLT: acc_type = COMEX_ACC_FLT; sizeof_scale = sizeof(float); break; case OP_ACC_LNG: acc_type = COMEX_ACC_LNG; sizeof_scale = sizeof(long); break; case OP_ACC_CPL: acc_type = COMEX_ACC_CPL; sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP: acc_type = COMEX_ACC_DCP; sizeof_scale = sizeof(DoubleComplex); break; default: COMEX_ASSERT(0); } use_eager = _eager_check(sizeof_scale+header->length); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); if (use_eager) { acc_buffer = scale + sizeof_scale; } else { if ((unsigned)header->length > static_server_buffer_size) { acc_buffer = malloc(header->length); } else { acc_buffer = static_server_buffer; } { char *buf = (char*)acc_buffer; int bytes_remaining = header->length; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; server_recv(buf, size, proc); buf += size; bytes_remaining -= size; } while (bytes_remaining > 0); } } if (COMEX_ENABLE_ACC_SELF || COMEX_ENABLE_ACC_SMP) { sem_wait(semaphores[header->rank]); _acc(acc_type, header->length, mapped_offset, acc_buffer, scale); sem_post(semaphores[header->rank]); } else { _acc(acc_type, header->length, mapped_offset, acc_buffer, scale); } if (use_eager) { } else { if ((unsigned)header->length > static_server_buffer_size) { free(acc_buffer); } } } STATIC void _acc_packed_handler(header_t *header, char *payload, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; void *scale = NULL; int sizeof_scale = 0; int acc_type = 0; char *acc_buffer = NULL; stride_t *stride = NULL; int use_eager = 0; #if DEBUG fprintf(stderr, "[%d] _acc_packed_handler\n", g_state.rank); #endif switch (header->operation) { case OP_ACC_INT_PACKED: acc_type = COMEX_ACC_INT; sizeof_scale = sizeof(int); break; case OP_ACC_DBL_PACKED: acc_type = COMEX_ACC_DBL; sizeof_scale = sizeof(double); break; case OP_ACC_FLT_PACKED: acc_type = COMEX_ACC_FLT; sizeof_scale = sizeof(float); break; case OP_ACC_LNG_PACKED: acc_type = COMEX_ACC_LNG; sizeof_scale = sizeof(long); break; case OP_ACC_CPL_PACKED: acc_type = COMEX_ACC_CPL; sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP_PACKED: acc_type = COMEX_ACC_DCP; sizeof_scale = sizeof(DoubleComplex); break; default: COMEX_ASSERT(0); } use_eager = _eager_check(sizeof_scale+sizeof(stride_t)+header->length); scale = payload; stride = (stride_t*)(payload + sizeof_scale); if (use_eager) { acc_buffer = payload+sizeof_scale+sizeof(stride_t); } else { if ((unsigned)header->length > static_server_buffer_size) { acc_buffer = malloc(header->length); } else { acc_buffer = static_server_buffer; } { /* we receive the buffer backwards */ char *buf = acc_buffer + header->length; int bytes_remaining = header->length; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; buf -= size; server_recv(buf, size, proc); bytes_remaining -= size; } while (bytes_remaining > 0); } } reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); if (COMEX_ENABLE_ACC_SELF || COMEX_ENABLE_ACC_SMP) { sem_wait(semaphores[header->rank]); } { char *packed_buffer = acc_buffer; char *dst = mapped_offset; int *dst_stride = stride->stride; int *count = stride->count; int stride_levels = stride->stride_levels; int i, j; long dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int dst_bvalue[7], dst_bunit[7]; int packed_index = 0; COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); COMEX_ASSERT(NULL != packed_buffer); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != dst_stride); COMEX_ASSERT(NULL != count); COMEX_ASSERT(count[0] > 0); #if DEBUG fprintf(stderr, "[%d] unpack(dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, dst, dst_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { dst_bvalue[i] = 0; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { dst_bvalue[j] = 0; } } _acc(acc_type, count[0], &dst[dst_idx], &packed_buffer[packed_index], scale); packed_index += count[0]; } COMEX_ASSERT(packed_index == n1dim*count[0]); } if (COMEX_ENABLE_ACC_SELF || COMEX_ENABLE_ACC_SMP) { sem_post(semaphores[header->rank]); } if (use_eager) { } else { if ((unsigned)header->length > static_server_buffer_size) { free(acc_buffer); } } } STATIC void _acc_iov_handler(header_t *header, char *scale, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int i = 0; char *packed_buffer = NULL; int packed_index = 0; char *iov_buf = NULL; int iov_off = 0; int limit = 0; int bytes = 0; void **src = NULL; void **dst = NULL; int sizeof_scale = 0; int acc_type = 0; #if DEBUG fprintf(stderr, "[%d] _acc_iov_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG fprintf(stderr, "[%d] _acc_iov_handler header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif #if DEBUG fprintf(stderr, "[%d] _acc_iov_handler limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif switch (header->operation) { case OP_ACC_INT_IOV: acc_type = COMEX_ACC_INT; sizeof_scale = sizeof(int); break; case OP_ACC_DBL_IOV: acc_type = COMEX_ACC_DBL; sizeof_scale = sizeof(double); break; case OP_ACC_FLT_IOV: acc_type = COMEX_ACC_FLT; sizeof_scale = sizeof(float); break; case OP_ACC_LNG_IOV: acc_type = COMEX_ACC_LNG; sizeof_scale = sizeof(long); break; case OP_ACC_CPL_IOV: acc_type = COMEX_ACC_CPL; sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP_IOV: acc_type = COMEX_ACC_DCP; sizeof_scale = sizeof(DoubleComplex); break; default: COMEX_ASSERT(0); } iov_buf = malloc(header->length); COMEX_ASSERT(iov_buf); server_recv(iov_buf, header->length, proc); limit = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(limit > 0); bytes = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(bytes > 0); src = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; dst = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; COMEX_ASSERT(iov_off == header->length); if ((unsigned)(bytes*limit) > static_server_buffer_size) { packed_buffer = malloc(bytes*limit); } else { packed_buffer = static_server_buffer; } server_recv(packed_buffer, bytes*limit, proc); if (COMEX_ENABLE_ACC_SELF || COMEX_ENABLE_ACC_SMP) { sem_wait(semaphores[header->rank]); } packed_index = 0; for (i=0; irank, dst[i], bytes); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, dst[i]); _acc(acc_type, bytes, mapped_offset, &packed_buffer[packed_index], scale); packed_index += bytes; } COMEX_ASSERT(packed_index == bytes*limit); if (COMEX_ENABLE_ACC_SELF || COMEX_ENABLE_ACC_SMP) { sem_post(semaphores[header->rank]); } if ((unsigned)(bytes*limit) > static_server_buffer_size) { free(packed_buffer); } free(iov_buf); } STATIC void _fence_handler(header_t *header, int proc) { #if DEBUG fprintf(stderr, "[%d] _fence_handler proc=%d\n", g_state.rank, proc); #endif /* preconditions */ COMEX_ASSERT(header); #if NEED_ASM_VOLATILE_MEMORY #if DEBUG fprintf(stderr, "[%d] _fence_handler asm volatile (\"\" : : : \"memory\"); \n", g_state.rank); #endif asm volatile ("" : : : "memory"); #endif /* we send the ack back to the originating proc */ server_send(NULL, 0, proc); } STATIC void _fetch_and_add_handler(header_t *header, char *payload, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int *value_int = NULL; long *value_long = NULL; #if DEBUG fprintf(stderr, "[%d] _fetch_and_add_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG fprintf(stderr, "[%d] header rem=%p loc=%p rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif COMEX_ASSERT(OP_FETCH_AND_ADD == header->operation); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); if (sizeof(int) == header->length) { value_int = malloc(sizeof(int)); *value_int = *((int*)mapped_offset); /* "fetch" */ *((int*)mapped_offset) += *((int*)payload); /* "add" */ server_send(value_int, sizeof(int), proc); free(value_int); } else if (sizeof(long) == header->length) { value_long = malloc(sizeof(long)); *value_long = *((long*)mapped_offset); /* "fetch" */ *((long*)mapped_offset) += *((long*)payload); /* "add" */ server_send(value_long, sizeof(long), proc); free(value_long); } else { COMEX_ASSERT(0); } } STATIC void _swap_handler(header_t *header, char *payload, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int *value_int = NULL; long *value_long = NULL; #if DEBUG fprintf(stderr, "[%d] _swap_handler rem=%p loc=%p rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif COMEX_ASSERT(OP_SWAP == header->operation); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); if (sizeof(int) == header->length) { value_int = malloc(sizeof(int)); *value_int = *((int*)mapped_offset); /* "fetch" */ *((int*)mapped_offset) = *((int*)payload); /* "swap" */ server_send(value_int, sizeof(int), proc); free(value_int); } else if (sizeof(long) == header->length) { value_long = malloc(sizeof(long)); *value_long = *((long*)mapped_offset); /* "fetch" */ *((long*)mapped_offset) = *((long*)payload); /* "swap" */ server_send(value_long, sizeof(long), proc); free(value_long); } else { COMEX_ASSERT(0); } } STATIC void _mutex_create_handler(header_t *header, int proc) { int i; int num = header->length; #if DEBUG fprintf(stderr, "[%d] _mutex_create_handler proc=%d num=%d\n", g_state.rank, proc, num); #endif mutexes[proc] = (int*)malloc(sizeof(int) * num); lq_heads[proc] = (comex_lock_t**)malloc(sizeof(comex_lock_t*) * num); for (i=0; ilength; #if DEBUG fprintf(stderr, "[%d] _mutex_destroy_handler proc=%d\n", g_state.rank, proc); #endif for (i=0; ilength; int rank = header->rank; #if DEBUG fprintf(stderr, "[%d] _lock_handler id=%d in rank=%d req by proc=%d\n", g_state.rank, id, rank, proc); #endif COMEX_ASSERT(0 <= id); if (UNLOCKED == mutexes[rank][id]) { mutexes[rank][id] = proc; server_send(&id, sizeof(int), proc); } else { comex_lock_t *lock = NULL; #if DEBUG fprintf(stderr, "[%d] _lq_push rank=%d req_by=%d id=%d\n", g_state.rank, rank, proc, id); #endif lock = malloc(sizeof(comex_lock_t)); lock->next = NULL; lock->rank = proc; if (lq_heads[rank][id]) { /* insert at tail */ comex_lock_t *lq = lq_heads[rank][id]; while (lq->next) { lq = lq->next; } lq->next = lock; } else { /* new head */ lq_heads[rank][id] = lock; } } } STATIC void _unlock_handler(header_t *header, int proc) { int id = header->length; int rank = header->rank; #if DEBUG fprintf(stderr, "[%d] _unlock_handler id=%d in rank=%d req by proc=%d\n", g_state.rank, id, rank, proc); #endif COMEX_ASSERT(0 <= id); if (lq_heads[rank][id]) { /* a lock requester was queued */ /* find the next lock request and update queue */ comex_lock_t *lock = lq_heads[rank][id]; lq_heads[rank][id] = lq_heads[rank][id]->next; /* update lock */ mutexes[rank][id] = lock->rank; /* notify next in line */ server_send(&id, sizeof(int), lock->rank); free(lock); } else { /* no enqued request */ mutexes[rank][id] = UNLOCKED; } } STATIC void _malloc_handler( header_t *header, char *payload, int proc) { int i; int n; reg_entry_t *reg_entries = (reg_entry_t*)payload; #if DEBUG fprintf(stderr, "[%d] _malloc_handler proc=%d\n", g_state.rank, proc); #endif COMEX_ASSERT(header); COMEX_ASSERT(header->operation == OP_MALLOC); n = header->length; #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] _malloc_handler preconditions complete\n", g_state.rank); #endif /* insert reg entries into local registration cache */ for (i=0; ilength; rank_ptr_t *rank_ptrs = (rank_ptr_t*)payload; #if ENABLE_SYSV int shm_id; #endif #if DEBUG fprintf(stderr, "[%d] _free_handler proc=%d\n", g_state.rank, proc); #endif /* remove all pointers from registration cache */ for (i=0; iuse_dev) { sicm_free(reg_entry->mapped); retval = 0; } else { retval = munmap(reg_entry->mapped, reg_entry->len); } #else #if ENABLE_SYSV /* shm_id = shmget(reg_entry->key,reg_entry->len,0600); _shmget_err(shm_id); */ /* printf("p[%d] DETACH SHM name: %s key: %d\n",g_state.rank,reg_entry->name, reg_entry->key); */ _shmdt_err(shmdt(reg_entry->mapped)); retval = 0; #else retval = munmap(reg_entry->mapped, reg_entry->len); check_devshm(0, -(reg_entry->len)); #endif #endif if (-1 == retval) { perror("_free_handler: munmap"); comex_error("_free_handler: munmap", retval); } #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] _free_handler unmapped mapped memory in reg entry\n", g_state.rank); #endif reg_cache_delete(rank_ptrs[i].rank, rank_ptrs[i].ptr); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] _free_handler deleted reg cache entry\n", g_state.rank); #endif } else { #if 0 reg_cache_delete(rank_ptrs[i].rank, rank_ptrs[i].ptr); #if DEBUG && DEBUG_VERBOSE fprintf(stderr, "[%d] _free_handler deleted reg cache entry\n", g_state.rank); #endif #endif } } server_send(NULL, 0, proc); /* ack */ } STATIC void* _get_offset_memory(reg_entry_t *reg_entry, void *memory) { ptrdiff_t offset = 0; COMEX_ASSERT(reg_entry); #if DEBUG_VERBOSE fprintf(stderr, "[%d] _get_offset_memory reg_entry->buf=%p memory=%p\n", g_state.rank, reg_entry->buf, memory); #endif offset = ((char*)memory) - ((char*)reg_entry->buf); #if DEBUG_VERBOSE fprintf(stderr, "[%d] _get_offset_memory ptrdiff=%lu\n", g_state.rank, (unsigned long)offset); #endif return (void*)((char*)(reg_entry->mapped)+offset); } STATIC int _is_master(void) { return (g_state.master[g_state.rank] == g_state.rank); } STATIC int _get_world_rank(comex_igroup_t *igroup, int rank) { #if 0 int world_rank; int status; status = MPI_Group_translate_ranks(igroup->group, 1, &rank, g_state.group, &world_rank); CHECK_MPI_RETVAL(status); COMEX_ASSERT(MPI_PROC_NULL != world_rank); return world_rank; #else return igroup->world_ranks[rank]; #endif } /* gets (in group order) corresponding world ranks for entire group */ STATIC int* _get_world_ranks(comex_igroup_t *igroup) { #if 0 int i = 0; int *group_ranks = (int*)malloc(sizeof(int)*igroup->size); int *world_ranks = (int*)malloc(sizeof(int)*igroup->size); int status; for (i=0; isize; ++i) { group_ranks[i] = i; world_ranks[i] = MPI_PROC_NULL; } status = MPI_Group_translate_ranks( igroup->group, igroup->size, group_ranks, g_state.group, world_ranks); COMEX_ASSERT(MPI_SUCCESS == status); for (i=0; isize; ++i) { COMEX_ASSERT(MPI_PROC_NULL != world_ranks[i]); } free(group_ranks); return world_ranks; #else #if 0 MPI_Comm comm = igroup->comm; int i = 0; int my_world_rank = g_state.rank; int *world_ranks = (int*)malloc(sizeof(int)*igroup->size); int status; for (i=0; isize; ++i) { world_ranks[i] = MPI_PROC_NULL; } status = MPI_Allgather(&my_world_rank,1,MPI_INT,world_ranks, 1,MPI_INT,comm); COMEX_ASSERT(MPI_SUCCESS == status); for (i=0; isize; ++i) { COMEX_ASSERT(MPI_PROC_NULL != world_ranks[i]); } return world_ranks; #else int size = igroup->size; int i = 0; int *world_ranks = (int*)malloc(sizeof(int)*size); for (i=0; iworld_ranks[i]; } return world_ranks; #endif #endif } /* we sometimes need to notify a node master of some event and the rank in * charge of doing that is returned by this function */ STATIC int _smallest_world_rank_with_same_hostid(comex_igroup_t *igroup) { int i = 0; int smallest = g_state.rank; int *world_ranks = _get_world_ranks(igroup); for (i=0; isize; ++i) { if (!strcmp(g_state.host[world_ranks[i]].name,g_state.host[g_state.rank].name)) { /* found same host as me */ if (world_ranks[i] < smallest) { smallest = world_ranks[i]; } } } free(world_ranks); return smallest; } /* we sometimes need to notify a node master of some event and the rank in * charge of doing that is returned by this function */ STATIC int _largest_world_rank_with_same_hostid(comex_igroup_t *igroup) { int i = 0; int largest = g_state.rank; int *world_ranks = _get_world_ranks(igroup); for (i=0; isize; ++i) { if (!strcmp(g_state.host[world_ranks[i]].name,g_state.host[g_state.rank].name)) { /* found same host as me */ if (world_ranks[i] > largest) { largest = world_ranks[i]; } } } free(world_ranks); return largest; } #if ENABLE_SYSV STATIC void* _shm_create(char *name, key_t *key, size_t size) #else STATIC void* _shm_create(const char *name, size_t size) #endif { #if ENABLE_SYSV FILE *fp; int shm_id; char file[SHM_NAME_SIZE+10]; char ebuf[128]; void *mapped = NULL; char token = (char)(token_counter%256); int try_next = 1; int try_cnt = 0; token_counter ++; if (use_dev_shm) { sprintf(file,"/dev/shm/%s",name); } else { sprintf(file,"/tmp/%s",name); } #if ENABLE_FTOK while (try_next && try_cnt < 100) { fp = fopen(file,"w"); fprintf(fp,"0\n"); fclose(fp); *key = ftok(file,token); sprintf(ebuf,"p[%d] (shmget in _shm_create) flags: IPC_CREAT|0600, key: %d, name: %s id: %d\n", g_state.rank,*key,name,(int)token); shm_id = shmget(*key,size,IPC_CREAT| IPC_EXCL |0600); if (shm_id != -1) { try_next = 0; } else { free(name); name = _generate_shm_name(g_state.rank); if (use_dev_shm) { sprintf(file,"/dev/shm/%s",name); } else { sprintf(file,"/tmp/%s",name); } /* printf("p[%d] shm_create failed on try %d\n",g_state.rank,try_cnt); */ try_cnt++; } } _shmget_err(shm_id, ebuf); if (shm_id == -1) { comex_error("_shm_create: shmget failed", shm_id); } #else *key = (key_t)token_counter; token_counter += g_state.size; sprintf(ebuf,"p[%d] (shmget in _shm_create) flags: IPC_CREAT|0600, key: %d, name: %s id: %d\n", g_state.rank,*key,name,(int)token); shm_id = shmget(*key,size,IPC_CREAT| IPC_EXCL |0600); _shmget_err(shm_id, ebuf); if (shm_id == -1) { comex_error("_shm_create: shmget failed", shm_id); } #endif mapped = shmat(shm_id, NULL, 0); /* printf("p[%d] ATTACH SHM name: %s key: %d\n",g_state.rank,name,*key); */ _shmat_err(mapped); return mapped; #else #include #include void *mapped = NULL; int fd = 0; int retval = 0; #if DEBUG fprintf(stderr, "[%d] _shm_create(%s, %lu)\n", g_state.rank, name, (unsigned long)size); #endif /* create shared memory segment */ fd = shm_open(name, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); if (-1 == fd && EEXIST == errno) { retval = shm_unlink(name); if (-1 == retval) { perror("_shm_create: shm_unlink"); comex_error("_shm_create: shm_unlink", retval); } } /* try a second time */ if (-1 == fd) { fd = shm_open(name, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); } /* finally report error if needed */ if (-1 == fd) { if (errno == EMFILE) { printf("The per process limit on the number of open file" " descriptors has been reached (relevant to PR runtime)\n"); } else if ( errno == ENFILE) { printf("The system-wide limit on the total number of open files" " has been reached (relevant to PR runtime)\n"); } perror("_shm_create: shm_open"); comex_error("_shm_create: shm_open", fd); } /* set the size of my shared memory object */ check_devshm(fd, size); count_open_fds(); retval = ftruncate(fd, size); if (-1 == retval) { if (errno == EFAULT) { printf("File descriptor points outside the processes allocated" " address space\n"); } perror("_shm_create: ftruncate"); comex_error("_shm_create: ftruncate", retval); } /* map into local address space */ mapped = _shm_map(fd, size); // check_devshm(fd); /* close file descriptor */ retval = close(fd); if (-1 == retval) { perror("_shm_create: close"); comex_error("_shm_create: close", -1); } return mapped; #endif } #if USE_SICM #if SICM_OLD STATIC void* _shm_create_memdev(const char *name, size_t size, sicm_device* device) #else STATIC void* _shm_create_memdev(const char *name, size_t size, sicm_device_list device) #endif { void *mapped = NULL; int fd = 0; int retval = 0; #if DEBUG fprintf(stderr, "[%d] _shm_create_memdev(%s, %lu)\n", g_state.rank, name, (unsigned long)size); #endif /* create shared memory segment */ fd = shm_open(name, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); if (-1 == fd && EEXIST == errno) { retval = shm_unlink(name); if (-1 == retval) { perror("_shm_create_memdev: shm_unlink"); comex_error("_shm_create_memdev: shm_unlink", retval); } } /* try a second time */ if (-1 == fd) { fd = shm_open(name, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); } /* finally report error if needed */ if (-1 == fd) { perror("_shm_create_memdev: shm_open"); comex_error("_shm_create_memdev: shm_open", fd); } /* the file will be used for arena allocation, * so it should not be truncated here */ #if SICM_OLD sicm_arena arena = sicm_arena_create_mmapped(0, device, fd, 0, -1, 0); #else sicm_arena arena = sicm_arena_create_mmapped(0, 0, &device, fd, 0, -1, 0); #endif /* map into local address space */ mapped = _shm_map_arena(fd, size, arena); /* close file descriptor */ retval = close(fd); if (-1 == retval) { perror("_shm_create_memdev: close"); comex_error("_shm_create_memdev: close", -1); } return mapped; } #endif #if ENABLE_SYSV STATIC void* _shm_attach(const char *name, size_t size, key_t key) { int shm_id; void *mapped = NULL; char ebuf[128]; sprintf(ebuf,"p[%d] (shmget in shm_attach) flags: 0600, key: %d, name: %s\n", g_state.rank,key,name); shm_id = shmget(key,size,0600); _shmget_err(shm_id, ebuf); if (shm_id == -1) { comex_error("_shm_attach: shmget failed", shm_id); } mapped = shmat(shm_id, NULL, 0); /* printf("p[%d] ATTACH SHM name: %s key: %d\n",g_state.rank,name,key); */ _shmat_err(mapped); return mapped; } #else STATIC void* _shm_attach(const char *name, size_t size) { void *mapped = NULL; int fd = 0; int retval = 0; #if DEBUG fprintf(stderr, "[%d] _shm_attach(%s, %lu)\n", g_state.rank, name, (unsigned long)size); #endif /* attach to shared memory segment */ fd = shm_open(name, O_RDWR, S_IRUSR|S_IWUSR); if (-1 == fd) { if (errno == EMFILE) { printf("The per process limit on the number of open file" " descriptors has been reached (relevant to PR runtime)\n"); } else if (errno == ENFILE) { printf("The system-wide limit on the total number of open files" " has been reached (relevant to PR runtime)\n"); } perror("_shm_attach: shm_open"); comex_error("_shm_attach: shm_open", -1); } /* map into local address space */ mapped = _shm_map(fd, size); // check_devshm(fd, size); /* close file descriptor */ retval = close(fd); if (-1 == retval) { perror("_shm_attach: close"); comex_error("_shm_attach: close", -1); } return mapped; } #endif #if USE_SICM #if SICM_OLD STATIC void* _shm_attach_memdev(const char *name, size_t size, sicm_device *device) #else STATIC void* _shm_attach_memdev(const char *name, size_t size, sicm_device_list device) #endif { void *mapped = NULL; int fd = 0; int retval = 0; #if DEBUG fprintf(stderr, "[%d] _shm_attach_memdev(%s, %lu)\n", g_state.rank, name, (unsigned long)size); #endif /* attach to shared memory segment */ fd = shm_open(name, O_RDWR, S_IRUSR|S_IWUSR); if (-1 == fd) { perror("_shm_attach_memdev: shm_open"); comex_error("_shm_attach_memdev: shm_open", -1); } #if SICM_OLD sicm_arena arena = sicm_arena_create_mmapped(0, device, fd, 0, -1, 0); #else sicm_arena arena = sicm_arena_create_mmapped(0, 0, &device, fd, 0, -1, 0); #endif /* map into local address space */ mapped = _shm_map_arena(fd, size, arena); /* close file descriptor */ retval = close(fd); if (-1 == retval) { perror("_shm_attach_memdev: close"); comex_error("_shm_attach_memdev: close", -1); } return mapped; } #endif #if USE_SICM STATIC void* _shm_map_arena(int fd, size_t size, sicm_arena arena) { void *memory = sicm_arena_alloc(arena, size); if (NULL == memory) { perror("_shm_map_arena: mmap"); comex_error("_shm_map_arena: mmap", -1); } return memory; } #endif STATIC void* _shm_map(int fd, size_t size) { void *memory = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); // check_devshm(fd, size); if (MAP_FAILED == memory) { if (errno == EBADF) { printf("File descriptor used in mmap is bad\n"); } else if (errno == ENFILE) { printf("The system-wid limit on the total number of open files" " has been reached\n"); } else if (errno == ENODEV) { printf("The system does not support memory mapping\n"); } else if (errno == ENOMEM) { printf("The processes maximum number of mappings has been exceeded\n"); } perror("_shm_map: mmap"); comex_error("_shm_map: mmap", -1); } return memory; } STATIC int _set_affinity(int cpu) { int status = 0; #if COMEX_SET_AFFINITY #if HAVE_PTHREAD_SETAFFINITY_NP || HAVE_SCHED_SETAFFINITY cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); #if HAVE_PTHREAD_SETAFFINITY_NP status = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); if (0 != status) { perror("pthread_setaffinity_np"); } #elif HAVE_SCHED_SETAFFINITY status = sched_setaffinity(getpid(), sizeof(cpu_set_t), &cpuset); if (0 != status) { perror("sched_setaffinity"); } #endif #endif #endif return status; } STATIC void check_mpi_retval(int retval, const char *file, int line) { if (MPI_SUCCESS != retval) { const char *msg = str_mpi_retval(retval); fprintf(stderr, "{%d} MPI Error: %s: line %d: %s\n", g_state.rank, file, line, msg); MPI_Abort(g_state.comm, retval); } } STATIC const char *str_mpi_retval(int retval) { const char *msg = NULL; if (retval == MPI_SUCCESS ) { msg = "MPI_SUCCESS"; } else if (retval == MPI_ERR_BUFFER ) { msg = "MPI_ERR_BUFFER"; } else if (retval == MPI_ERR_COUNT ) { msg = "MPI_ERR_COUNT"; } else if (retval == MPI_ERR_TYPE ) { msg = "MPI_ERR_TYPE"; } else if (retval == MPI_ERR_TAG ) { msg = "MPI_ERR_TAG"; } else if (retval == MPI_ERR_COMM ) { msg = "MPI_ERR_COMM"; } else if (retval == MPI_ERR_RANK ) { msg = "MPI_ERR_RANK"; } else if (retval == MPI_ERR_ROOT ) { msg = "MPI_ERR_ROOT"; } else if (retval == MPI_ERR_GROUP ) { msg = "MPI_ERR_GROUP"; } else if (retval == MPI_ERR_OP ) { msg = "MPI_ERR_OP"; } else if (retval == MPI_ERR_TOPOLOGY ) { msg = "MPI_ERR_TOPOLOGY"; } else if (retval == MPI_ERR_DIMS ) { msg = "MPI_ERR_DIMS"; } else if (retval == MPI_ERR_ARG ) { msg = "MPI_ERR_ARG"; } else if (retval == MPI_ERR_UNKNOWN ) { msg = "MPI_ERR_UNKNOWN"; } else if (retval == MPI_ERR_TRUNCATE ) { msg = "MPI_ERR_TRUNCATE"; } else if (retval == MPI_ERR_OTHER ) { msg = "MPI_ERR_OTHER"; } else if (retval == MPI_ERR_INTERN ) { msg = "MPI_ERR_INTERN"; } else if (retval == MPI_ERR_IN_STATUS) { msg = "MPI_ERR_IN_STATUS"; } else if (retval == MPI_ERR_PENDING ) { msg = "MPI_ERR_PENDING"; } else if (retval == MPI_ERR_REQUEST ) { msg = "MPI_ERR_REQUEST"; } else if (retval == MPI_ERR_LASTCODE ) { msg = "MPI_ERR_LASTCODE"; } else { msg = "DEFAULT"; } return msg; } STATIC void server_send(void *buf, int count, int dest) { int retval = 0; #if DEBUG fprintf(stderr, "[%d] server_send(buf=%p, count=%d, dest=%d)\n", g_state.rank, buf, count, dest); #endif retval = MPI_Send(buf, count, MPI_CHAR, dest, COMEX_TAG, g_state.comm); CHECK_MPI_RETVAL(retval); } STATIC void server_send_datatype(void *buf, MPI_Datatype dt, int dest) { int retval = 0; #if DEBUG fprintf(stderr, "[%d] server_send_datatype(buf=%p, ..., dest=%d)\n", g_state.rank, buf, dest); #endif retval = MPI_Send(buf, 1, dt, dest, COMEX_TAG, g_state.comm); CHECK_MPI_RETVAL(retval); } STATIC void server_recv(void *buf, int count, int source) { int retval = 0; MPI_Status status; int recv_count = 0; retval = MPI_Recv(buf, count, MPI_CHAR, source, COMEX_TAG, g_state.comm, &status); CHECK_MPI_RETVAL(retval); COMEX_ASSERT(status.MPI_SOURCE == source); COMEX_ASSERT(status.MPI_TAG == COMEX_TAG); retval = MPI_Get_count(&status, MPI_CHAR, &recv_count); CHECK_MPI_RETVAL(retval); COMEX_ASSERT(recv_count == count); } STATIC void server_recv_datatype(void *buf, MPI_Datatype dt, int source) { int retval = 0; MPI_Status status; retval = MPI_Recv(buf, 1, dt, source, COMEX_TAG, g_state.comm, &status); CHECK_MPI_RETVAL(retval); COMEX_ASSERT(status.MPI_SOURCE == source); COMEX_ASSERT(status.MPI_TAG == COMEX_TAG); } STATIC void nb_send_common(void *buf, int count, int dest, nb_t *nb, int need_free) { int retval = 0; message_t *message = NULL; COMEX_ASSERT(NULL != nb); nb->send_size += 1; nb_count_event += 1; nb_count_send += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = need_free; message->stride = NULL; message->iov = NULL; message->datatype = MPI_DATATYPE_NULL; if (NULL == nb->send_head) { nb->send_head = message; } if (NULL != nb->send_tail) { nb->send_tail->next = message; } nb->send_tail = message; retval = MPI_Isend(buf, count, MPI_CHAR, dest, COMEX_TAG, g_state.comm, &(message->request)); CHECK_MPI_RETVAL(retval); } STATIC void nb_send_datatype(void *buf, MPI_Datatype dt, int dest, nb_t *nb) { int retval = 0; message_t *message = NULL; COMEX_ASSERT(NULL != nb); nb->send_size += 1; nb_count_event += 1; nb_count_send += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = 0; message->stride = NULL; message->iov = NULL; message->datatype = dt; if (NULL == nb->send_head) { nb->send_head = message; } if (NULL != nb->send_tail) { nb->send_tail->next = message; } nb->send_tail = message; retval = MPI_Isend(buf, 1, dt, dest, COMEX_TAG, g_state.comm, &(message->request)); CHECK_MPI_RETVAL(retval); } STATIC void nb_send_header(void *buf, int count, int dest, nb_t *nb) { nb_send_common(buf, count, dest, nb, 1); } STATIC void nb_send_buffer(void *buf, int count, int dest, nb_t *nb) { nb_send_common(buf, count, dest, nb, 0); } STATIC void nb_recv_packed(void *buf, int count, int source, nb_t *nb, stride_t *stride) { int retval = 0; message_t *message = NULL; COMEX_ASSERT(NULL != buf); COMEX_ASSERT(count > 0); COMEX_ASSERT(NULL != nb); #if DEBUG fprintf(stderr, "[%d] nb_recv_packed(buf=%p, count=%d, source=%d, nb=%p)\n", g_state.rank, buf, count, source, nb); #endif nb->recv_size += 1; nb_count_event += 1; nb_count_recv += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = 1; message->stride = stride; message->iov = NULL; message->datatype = MPI_DATATYPE_NULL; if (NULL == nb->recv_head) { nb->recv_head = message; } if (NULL != nb->recv_tail) { nb->recv_tail->next = message; } nb->recv_tail = message; retval = MPI_Irecv(buf, count, MPI_CHAR, source, COMEX_TAG, g_state.comm, &(message->request)); CHECK_MPI_RETVAL(retval); } STATIC void nb_recv_datatype(void *buf, MPI_Datatype dt, int source, nb_t *nb) { int retval = 0; message_t *message = NULL; COMEX_ASSERT(NULL != buf); COMEX_ASSERT(NULL != nb); #if DEBUG fprintf(stderr, "[%d] nb_recv_datatype(buf=%p, source=%d, nb=%p)\n", g_state.rank, buf, source, nb); #endif nb->recv_size += 1; nb_count_event += 1; nb_count_recv += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = 0; message->stride = NULL; message->iov = NULL; message->datatype = dt; if (NULL == nb->recv_head) { nb->recv_head = message; } if (NULL != nb->recv_tail) { nb->recv_tail->next = message; } nb->recv_tail = message; retval = MPI_Irecv(buf, 1, dt, source, COMEX_TAG, g_state.comm, &(message->request)); CHECK_MPI_RETVAL(retval); } STATIC void nb_recv_iov(void *buf, int count, int source, nb_t *nb, comex_giov_t *iov) { int retval = 0; message_t *message = NULL; COMEX_ASSERT(NULL != nb); #if DEBUG fprintf(stderr, "[%d] nb_recv_iov(buf=%p, count=%d, source=%d, nb=%p)\n", g_state.rank, buf, count, source, nb); #endif nb->recv_size += 1; nb_count_event += 1; nb_count_recv += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = 1; message->stride = NULL; message->iov = iov; message->datatype = MPI_DATATYPE_NULL; if (NULL == nb->recv_head) { nb->recv_head = message; COMEX_ASSERT(NULL == nb->recv_tail); } if (NULL != nb->recv_tail) { nb->recv_tail->next = message; } nb->recv_tail = message; retval = MPI_Irecv(buf, count, MPI_CHAR, source, COMEX_TAG, g_state.comm, &(message->request)); CHECK_MPI_RETVAL(retval); } STATIC void nb_recv(void *buf, int count, int source, nb_t *nb) { int retval = 0; message_t *message = NULL; COMEX_ASSERT(NULL != nb); #if DEBUG fprintf(stderr, "[%d] nb_recv(buf=%p, count=%d, source=%d, nb=%p)\n", g_state.rank, buf, count, source, nb); #endif nb->recv_size += 1; nb_count_event += 1; nb_count_recv += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = NULL; message->need_free = 0; message->stride = NULL; message->iov = NULL; message->datatype = MPI_DATATYPE_NULL; if (NULL == nb->recv_head) { nb->recv_head = message; } if (NULL != nb->recv_tail) { nb->recv_tail->next = message; } nb->recv_tail = message; retval = MPI_Irecv(buf, count, MPI_CHAR, source, COMEX_TAG, g_state.comm, &(message->request)); CHECK_MPI_RETVAL(retval); } STATIC int nb_get_handle_index() { int value = 0; if (0 == nb_index) { value = nb_max_outstanding-1; } else { value = nb_index-1; } return value; } STATIC nb_t* nb_wait_for_handle() { nb_t *nb = NULL; int in_use_count = 0; int loop_index = nb_index; int found = 0; /* find first handle that isn't associated with a user-level handle */ /* make sure the handle we find has processed all events */ /* the user can accidentally exhaust the available handles */ #if 0 /* NOTE: it looks like this loop just forces completion of the handle * corresponding to nb_index. It should probably test all handles and if it * doesn't find a free one, then use the one at nb_index */ do { ++in_use_count; if (in_use_count > nb_max_outstanding) { fprintf(stderr, "{%d} nb_wait_for_handle Error: all user-level " "nonblocking handles have been exhausted\n", g_state.rank); MPI_Abort(g_state.comm, -1); } nb = &nb_state[nb_index]; nb_index++; nb_index %= nb_max_outstanding; /* wrap around if needed */ nb_wait_for_all(nb); } while (nb->in_use); #else /* look through list for unused handle */ do { ++in_use_count; if (in_use_count > nb_max_outstanding) { break; } nb = &nb_state[loop_index]; if (!nb->in_use) { nb_index = loop_index; found = 1; break; } loop_index++; loop_index %= nb_max_outstanding; /* wrap around if needed */ } while (nb->in_use); if (!found) { nb = &nb_state[nb_index]; nb_wait_for_all(nb); } //nb->hdl = nb_index; nb_index++; nb_index %= nb_max_outstanding; /* wrap around if needed */ /* make sure in_use flag is set to 1 */ nb->in_use = 1; #endif return nb; } STATIC void nb_wait_for_send1(nb_t *nb) { #if DEBUG fprintf(stderr, "[%d] nb_wait_for_send1(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); COMEX_ASSERT(NULL != nb->send_head); { MPI_Status status; int retval = 0; message_t *message_to_free = NULL; retval = MPI_Wait(&(nb->send_head->request), &status); CHECK_MPI_RETVAL(retval); if (nb->send_head->need_free) { free(nb->send_head->message); } if (MPI_DATATYPE_NULL != nb->send_head->datatype) { retval = MPI_Type_free(&nb->send_head->datatype); CHECK_MPI_RETVAL(retval); } message_to_free = nb->send_head; nb->send_head = nb->send_head->next; free(message_to_free); COMEX_ASSERT(nb->send_size > 0); nb->send_size -= 1; nb_count_send_processed += 1; nb_count_event_processed += 1; if (NULL == nb->send_head) { nb->send_tail = NULL; } } } /* returns true if operation has completed */ STATIC int nb_test_for_send1(nb_t *nb, message_t **save_send_head, message_t **prev) { #if DEBUG fprintf(stderr, "[%d] nb_test_for_send1(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); COMEX_ASSERT(NULL != nb->send_head); { MPI_Status status; int retval = 0; int flag; message_t *message_to_free = NULL; retval = MPI_Test(&(nb->send_head->request), &flag, &status); CHECK_MPI_RETVAL(retval); if (flag) { if (nb->send_head->need_free) { free(nb->send_head->message); } if (MPI_DATATYPE_NULL != nb->send_head->datatype) { retval = MPI_Type_free(&nb->send_head->datatype); CHECK_MPI_RETVAL(retval); } message_to_free = nb->send_head; if (*prev) (*prev)->next=nb->send_head->next; nb->send_head = nb->send_head->next; *save_send_head = NULL; free(message_to_free); COMEX_ASSERT(nb->send_size > 0); nb->send_size -= 1; nb_count_send_processed += 1; nb_count_event_processed += 1; if (NULL == nb->send_head) { nb->send_tail = NULL; } } else { *prev = nb->send_head; *save_send_head = nb->send_head; nb->send_head = nb->send_head->next; } return flag; } } STATIC void nb_wait_for_recv1(nb_t *nb) { #if DEBUG fprintf(stderr, "[%d] nb_wait_for_recv1(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); COMEX_ASSERT(NULL != nb->recv_head); { MPI_Status status; int retval = 0; message_t *message_to_free = NULL; retval = MPI_Wait(&(nb->recv_head->request), &status); CHECK_MPI_RETVAL(retval); if (NULL != nb->recv_head->stride) { stride_t *stride = nb->recv_head->stride; COMEX_ASSERT(nb->recv_head->message); COMEX_ASSERT(stride); COMEX_ASSERT(stride->ptr); COMEX_ASSERT(stride->stride); COMEX_ASSERT(stride->count); COMEX_ASSERT(stride->stride_levels); unpack(nb->recv_head->message, stride->ptr, stride->stride, stride->count, stride->stride_levels); free(stride); } if (NULL != nb->recv_head->iov) { int i = 0; char *message = nb->recv_head->message; int off = 0; comex_giov_t *iov = nb->recv_head->iov; for (i=0; icount; ++i) { (void)memcpy(iov->dst[i], &message[off], iov->bytes); off += iov->bytes; } free(iov->src); free(iov->dst); free(iov); } if (nb->recv_head->need_free) { free(nb->recv_head->message); } if (MPI_DATATYPE_NULL != nb->recv_head->datatype) { retval = MPI_Type_free(&nb->recv_head->datatype); CHECK_MPI_RETVAL(retval); } message_to_free = nb->recv_head; nb->recv_head = nb->recv_head->next; free(message_to_free); COMEX_ASSERT(nb->recv_size > 0); nb->recv_size -= 1; nb_count_recv_processed += 1; nb_count_event_processed += 1; if (NULL == nb->recv_head) { nb->recv_tail = NULL; } } } /* returns true if operation has completed */ STATIC int nb_test_for_recv1(nb_t *nb, message_t **save_recv_head, message_t **prev) { #if DEBUG fprintf(stderr, "[%d] nb_wait_for_recv1(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); COMEX_ASSERT(NULL != nb->recv_head); { MPI_Status status; int retval = 0; int flag; message_t *message_to_free = NULL; retval = MPI_Test(&(nb->recv_head->request), &flag, &status); CHECK_MPI_RETVAL(retval); if (flag) { if (NULL != nb->recv_head->stride) { stride_t *stride = nb->recv_head->stride; COMEX_ASSERT(nb->recv_head->message); COMEX_ASSERT(stride); COMEX_ASSERT(stride->ptr); COMEX_ASSERT(stride->stride); COMEX_ASSERT(stride->count); COMEX_ASSERT(stride->stride_levels); unpack(nb->recv_head->message, stride->ptr, stride->stride, stride->count, stride->stride_levels); free(stride); } if (NULL != nb->recv_head->iov) { int i = 0; char *message = nb->recv_head->message; int off = 0; comex_giov_t *iov = nb->recv_head->iov; for (i=0; icount; ++i) { (void)memcpy(iov->dst[i], &message[off], iov->bytes); off += iov->bytes; } free(iov->src); free(iov->dst); free(iov); } if (nb->recv_head->need_free) { free(nb->recv_head->message); } if (MPI_DATATYPE_NULL != nb->recv_head->datatype) { retval = MPI_Type_free(&nb->recv_head->datatype); CHECK_MPI_RETVAL(retval); } message_to_free = nb->recv_head; if (*prev) (*prev)->next=nb->recv_head->next; nb->recv_head = nb->recv_head->next; *save_recv_head = NULL; free(message_to_free); COMEX_ASSERT(nb->recv_size > 0); nb->recv_size -= 1; nb_count_recv_processed += 1; nb_count_event_processed += 1; if (NULL == nb->recv_head) { nb->recv_tail = NULL; } } else { *prev = nb->recv_head; *save_recv_head = nb->recv_head; nb->recv_head = nb->recv_head->next; } return flag; } } STATIC void nb_wait_for_all(nb_t *nb) { #if DEBUG fprintf(stderr, "[%d] nb_wait_for_all(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); /* fair processing of requests */ while (NULL != nb->send_head || NULL != nb->recv_head) { if (NULL != nb->send_head) { nb_wait_for_send1(nb); } if (NULL != nb->recv_head) { nb_wait_for_recv1(nb); } } nb->in_use = 0; } /* Returns 0 if no outstanding requests */ STATIC int nb_test_for_all(nb_t *nb) { #if DEBUG fprintf(stderr, "[%d] nb_test_for_all(nb=%p)\n", g_state.rank, nb); #endif int ret = 0; message_t *save_send_head = NULL; message_t *save_recv_head = NULL; message_t *tmp_send_head; message_t *tmp_recv_head; message_t *send_prev = NULL; message_t *recv_prev = NULL; COMEX_ASSERT(NULL != nb); /* check for outstanding requests */ while (NULL != nb->send_head || NULL != nb->recv_head) { if (NULL != nb->send_head) { if (!nb_test_for_send1(nb, &tmp_send_head, &send_prev)) { ret = 1; } if ((NULL == save_send_head) && (ret == 1)) { save_send_head = tmp_send_head; } } if (NULL != nb->recv_head) { if (!nb_test_for_recv1(nb, &tmp_recv_head, &recv_prev)) { ret = 1; } if ((NULL == save_recv_head) && (ret == 1)) { save_recv_head = tmp_recv_head; } } } nb->send_head = save_send_head; nb->recv_head = save_recv_head; if (ret == 0) nb->in_use = 0; return ret; } STATIC void nb_wait_all() { int i = 0; COMEX_ASSERT(nb_count_event-nb_count_event_processed >= 0); for (i=0; i 0); COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != nb); #if DEBUG printf("[%d] nb_put(src=%p, dst=%p, bytes=%d, proc=%d, nb=%p)\n", g_state.rank, src, dst, bytes, proc, nb); #endif if (COMEX_ENABLE_PUT_SELF) { /* put to self */ if (g_state.rank == proc) { if (fence_array[g_state.master[proc]]) { _fence_master(g_state.master[proc]); } (void)memcpy(dst, src, bytes); return; } } if (COMEX_ENABLE_PUT_SMP) { /* put to SMP node */ // if (g_state.hostid[proc] == g_state.hostid[g_state.rank]) if (g_state.master[proc] == g_state.master[g_state.rank]) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; if (fence_array[g_state.master[proc]]) { _fence_master(g_state.master[proc]); } reg_entry = reg_cache_find(proc, dst, bytes); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, dst); (void)memcpy(mapped_offset, src, bytes); return; } } { char *message = NULL; int message_size = 0; header_t *header = NULL; int master_rank = -1; int use_eager = _eager_check(bytes); master_rank = g_state.master[proc]; /* only fence on the master */ fence_array[master_rank] = 1; if (use_eager) { message_size = sizeof(header_t) + bytes; } else { message_size = sizeof(header_t); } message = malloc(message_size); header = (header_t*)message; header->operation = OP_PUT; MAYBE_MEMSET(header, 0, sizeof(header_t)); header->remote_address = dst; header->local_address = src; header->rank = proc; header->length = bytes; if (use_eager) { (void)memcpy(message+sizeof(header_t), src, bytes); nb_send_header(message, message_size, master_rank, nb); } else { char *buf = (char*)src; int bytes_remaining = bytes; nb_send_header(header, sizeof(header_t), master_rank, nb); do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; nb_send_buffer(buf, size, master_rank, nb); buf += size; bytes_remaining -= size; } while (bytes_remaining > 0); } } } STATIC void nb_get(void *src, void *dst, int bytes, int proc, nb_t *nb) { COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(bytes > 0); COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != nb); if (COMEX_ENABLE_GET_SELF) { /* get from self */ if (g_state.rank == proc) { if (fence_array[g_state.master[proc]]) { _fence_master(g_state.master[proc]); } (void)memcpy(dst, src, bytes); return; } } if (COMEX_ENABLE_GET_SMP) { /* get from SMP node */ // if (g_state.hostid[proc] == g_state.hostid[g_state.rank]) if (g_state.master[proc] == g_state.master[g_state.rank]) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; if (fence_array[g_state.master[proc]]) { _fence_master(g_state.master[proc]); } reg_entry = reg_cache_find(proc, src, bytes); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, src); (void)memcpy(dst, mapped_offset, bytes); return; } } { header_t *header = NULL; int master_rank = -1; master_rank = g_state.master[proc]; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_GET; header->remote_address = src; header->local_address = dst; header->rank = proc; header->length = bytes; { /* prepost all receives */ char *buf = (char*)dst; int bytes_remaining = bytes; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; nb_recv(buf, size, master_rank, nb); buf += size; bytes_remaining -= size; } while (bytes_remaining > 0); } nb_send_header(header, sizeof(header_t), master_rank, nb); } } STATIC void nb_acc(int datatype, void *scale, void *src, void *dst, int bytes, int proc, nb_t *nb) { COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(bytes > 0); COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != nb); if (COMEX_ENABLE_ACC_SELF) { /* acc to self */ if (g_state.rank == proc) { if (fence_array[g_state.master[proc]]) { _fence_master(g_state.master[proc]); } sem_wait(semaphores[proc]); _acc(datatype, bytes, dst, src, scale); sem_post(semaphores[proc]); return; } } if (COMEX_ENABLE_ACC_SMP) { /* acc to same SMP node */ // if (g_state.hostid[proc] == g_state.hostid[g_state.rank]) if (g_state.master[proc] == g_state.master[g_state.rank]) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; if (fence_array[g_state.master[proc]]) { _fence_master(g_state.master[proc]); } reg_entry = reg_cache_find(proc, dst, bytes); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, dst); sem_wait(semaphores[proc]); _acc(datatype, bytes, mapped_offset, src, scale); sem_post(semaphores[proc]); return; } } { header_t *header = NULL; char *message = NULL; int master_rank = -1; int message_size = 0; int scale_size = 0; op_t operation = OP_NULL; int use_eager = 0; switch (datatype) { case COMEX_ACC_INT: operation = OP_ACC_INT; scale_size = sizeof(int); break; case COMEX_ACC_DBL: operation = OP_ACC_DBL; scale_size = sizeof(double); break; case COMEX_ACC_FLT: operation = OP_ACC_FLT; scale_size = sizeof(float); break; case COMEX_ACC_CPL: operation = OP_ACC_CPL; scale_size = sizeof(SingleComplex); break; case COMEX_ACC_DCP: operation = OP_ACC_DCP; scale_size = sizeof(DoubleComplex); break; case COMEX_ACC_LNG: operation = OP_ACC_LNG; scale_size = sizeof(long); break; default: COMEX_ASSERT(0); } use_eager = _eager_check(scale_size+bytes); master_rank = g_state.master[proc]; /* only fence on the master */ fence_array[master_rank] = 1; if (use_eager) { message_size = sizeof(header_t) + scale_size + bytes; } else { message_size = sizeof(header_t) + scale_size; } message = malloc(message_size); COMEX_ASSERT(message); header = (header_t*)message; header->operation = operation; header->remote_address = dst; header->local_address = src; header->rank = proc; header->length = bytes; (void)memcpy(message+sizeof(header_t), scale, scale_size); if (use_eager) { (void)memcpy(message+sizeof(header_t)+scale_size, src, bytes); nb_send_header(message, message_size, master_rank, nb); } else { char *buf = (char*)src; int bytes_remaining = bytes; nb_send_header(message, message_size, master_rank, nb); do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; nb_send_buffer(buf, size, master_rank, nb); buf += size; bytes_remaining -= size; } while (bytes_remaining > 0); } } } STATIC void nb_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; #if DEBUG fprintf(stderr, "[%d] nb_puts(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif /* if not actually a strided put */ if (0 == stride_levels) { nb_put(src, dst, count[0], proc, nb); return; } /* if not a strided put to self or SMP, use datatype algorithm */ if (COMEX_ENABLE_PUT_DATATYPE && (!COMEX_ENABLE_PUT_SELF || g_state.rank != proc) && (!COMEX_ENABLE_PUT_SMP || strcmp(g_state.host[proc].name,g_state.host[g_state.rank].name)) && (_packed_size(src_stride, count, stride_levels) > COMEX_PUT_DATATYPE_THRESHOLD)) { nb_puts_datatype(src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } /* if not a strided put to self or SMP, use packed algorithm */ if (COMEX_ENABLE_PUT_PACKED && (!COMEX_ENABLE_PUT_SELF || g_state.rank != proc) && (!COMEX_ENABLE_PUT_SMP || strcmp(g_state.host[proc].name,g_state.host[g_state.rank].name))) { nb_puts_packed(src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } /* index mangling */ for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_put((char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } } STATIC void nb_puts_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i; int packed_index = 0; char *packed_buffer = NULL; stride_t stride; #if DEBUG fprintf(stderr, "[%d] nb_puts_packed(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != count); COMEX_ASSERT(NULL != nb); COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); /* copy dst info into structure */ stride.stride_levels = stride_levels; stride.count[0] = count[0]; for (i=0; i= 0); COMEX_ASSERT(stride.stride_levels < COMEX_MAX_STRIDE_LEVEL); #if DEBUG fprintf(stderr, "[%d] nb_puts_packed stride_levels=%d, count[0]=%d\n", g_state.rank, stride_levels, count[0]); for (i=0; i 0); { char *message = NULL; int message_size = 0; header_t *header = NULL; int master_rank = -1; int use_eager = _eager_check(sizeof(stride_t)+packed_index); master_rank = g_state.master[proc]; /* only fence on the master */ fence_array[master_rank] = 1; if (use_eager) { message_size = sizeof(header_t)+sizeof(stride_t)+packed_index; } else { message_size = sizeof(header_t)+sizeof(stride_t); } message = malloc(message_size); header = (header_t*)message; header->operation = OP_PUT_PACKED; header->remote_address = dst; header->local_address = NULL; header->rank = proc; header->length = packed_index; (void)memcpy(message+sizeof(header_t), &stride, sizeof(stride_t)); if (use_eager) { (void)memcpy(message+sizeof(header_t)+sizeof(stride_t), packed_buffer, packed_index); nb_send_header(message, message_size, master_rank, nb); free(packed_buffer); } else { /* we send the buffer backwards */ char *buf = packed_buffer + packed_index;; int bytes_remaining = packed_index; nb_send_header(message, message_size, master_rank, nb); do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; buf -= size; if (size == bytes_remaining) { /* on the last send, mark buffer for deletion */ nb_send_header(buf, size, master_rank, nb); } else { nb_send_buffer(buf, size, master_rank, nb); } bytes_remaining -= size; } while (bytes_remaining > 0); } } } STATIC void nb_puts_datatype( void *src_ptr, int *src_stride_ar, void *dst_ptr, int *dst_stride_ar, int *count, int stride_levels, int proc, nb_t *nb) { MPI_Datatype src_type; int ierr; int i; stride_t stride; #if DEBUG fprintf(stderr, "[%d] nb_puts_datatype(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src_ptr, src_stride_ar, dst_ptr, dst_stride_ar, count[0], stride_levels, proc, nb); #endif COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != src_ptr); COMEX_ASSERT(NULL != dst_ptr); COMEX_ASSERT(NULL != count); COMEX_ASSERT(NULL != nb); COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); /* copy dst info into structure */ MAYBE_MEMSET(&stride, 0, sizeof(stride_t)); stride.stride_levels = stride_levels; stride.count[0] = count[0]; for (i=0; i= 0); COMEX_ASSERT(stride.stride_levels < COMEX_MAX_STRIDE_LEVEL); #if DEBUG fprintf(stderr, "[%d] nb_puts_datatype stride_levels=%d, count[0]=%d\n", g_state.rank, stride_levels, count[0]); for (i=0; ioperation = OP_PUT_DATATYPE; header->remote_address = dst_ptr; header->local_address = NULL; header->rank = proc; header->length = 0; (void)memcpy(message+sizeof(header_t), &stride, sizeof(stride_t)); nb_send_header(message, message_size, master_rank, nb); nb_send_datatype(src_ptr, src_type, master_rank, nb); } } STATIC void nb_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; /* if not actually a strided get */ if (0 == stride_levels) { nb_get(src, dst, count[0], proc, nb); return; } /* if not a strided get from self or SMP, use datatype algorithm */ if (COMEX_ENABLE_GET_DATATYPE && (!COMEX_ENABLE_GET_SELF || g_state.rank != proc) && (!COMEX_ENABLE_GET_SMP || strcmp(g_state.host[proc].name,g_state.host[g_state.rank].name)) && (_packed_size(src_stride, count, stride_levels) > COMEX_GET_DATATYPE_THRESHOLD)) { nb_gets_datatype(src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } /* if not a strided get from self or SMP, use packed algorithm */ if (COMEX_ENABLE_GET_PACKED && (!COMEX_ENABLE_GET_SELF || g_state.rank != proc) && (!COMEX_ENABLE_GET_SMP || strcmp(g_state.host[proc].name,g_state.host[g_state.rank].name))) { nb_gets_packed(src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } dst_idx = 0; for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_get((char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } } STATIC void nb_gets_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i; stride_t stride_src; stride_t *stride_dst = NULL; #if DEBUG fprintf(stderr, "[%d] nb_gets_packed(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != count); COMEX_ASSERT(NULL != nb); COMEX_ASSERT(count[0] > 0); COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); /* copy src info into structure */ stride_src.ptr = src; stride_src.stride_levels = stride_levels; stride_src.count[0] = count[0]; for (i=0; i= 0); COMEX_ASSERT(stride_src.stride_levels < COMEX_MAX_STRIDE_LEVEL); /* copy dst info into structure */ stride_dst = malloc(sizeof(stride_t)); COMEX_ASSERT(stride_dst); stride_dst->ptr = dst; stride_dst->stride_levels = stride_levels; stride_dst->count[0] = count[0]; for (i=0; istride[i] = dst_stride[i]; stride_dst->count[i+1] = count[i+1]; } for (/*no init*/; istride[i] = -1; stride_dst->count[i+1] = -1; } COMEX_ASSERT(stride_dst->stride_levels >= 0); COMEX_ASSERT(stride_dst->stride_levels < COMEX_MAX_STRIDE_LEVEL); { char *message = NULL; int message_size = 0; int recv_size = 0; char *packed_buffer = NULL; header_t *header = NULL; int master_rank = -1; master_rank = g_state.master[proc]; message_size = sizeof(header_t) + sizeof(stride_t); message = malloc(message_size); header = (header_t*)message; COMEX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_GET_PACKED; header->remote_address = src; header->local_address = dst; header->rank = proc; header->length = 0; recv_size = _packed_size(stride_dst->stride, stride_dst->count, stride_dst->stride_levels); COMEX_ASSERT(recv_size > 0); packed_buffer = malloc(recv_size); COMEX_ASSERT(packed_buffer); { /* prepost all receives backward */ char *buf = (char*)packed_buffer + recv_size; int bytes_remaining = recv_size; do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; buf -= size; if (size == bytes_remaining) { /* on the last recv, indicate a packed recv */ nb_recv_packed(buf, size, master_rank, nb, stride_dst); } else { nb_recv(buf, size, master_rank, nb); } bytes_remaining -= size; } while (bytes_remaining > 0); } (void)memcpy(message+sizeof(header_t), &stride_src, sizeof(stride_t)); nb_send_header(message, message_size, master_rank, nb); } } STATIC void nb_gets_datatype( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { MPI_Datatype dst_type; int i; stride_t stride_src; #if DEBUG fprintf(stderr, "[%d] nb_gets_datatype(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif #if DEBUG for (i=0; i= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != count); COMEX_ASSERT(NULL != nb); COMEX_ASSERT(count[0] > 0); COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); /* copy src info into structure */ MAYBE_MEMSET(&stride_src, 0, sizeof(header_t)); stride_src.ptr = src; stride_src.stride_levels = stride_levels; stride_src.count[0] = count[0]; for (i=0; i= 0); COMEX_ASSERT(stride_src.stride_levels < COMEX_MAX_STRIDE_LEVEL); { char *message = NULL; int message_size = 0; header_t *header = NULL; int master_rank = -1; int ierr; master_rank = g_state.master[proc]; message_size = sizeof(header_t) + sizeof(stride_t); message = malloc(message_size); header = (header_t*)message; COMEX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_GET_DATATYPE; header->remote_address = src; header->local_address = dst; header->rank = proc; header->length = 0; strided_to_subarray_dtype(dst_stride, count, stride_levels, MPI_BYTE, &dst_type); ierr = MPI_Type_commit(&dst_type); translate_mpi_error(ierr,"nb_gets_datatype:MPI_Type_commit"); nb_recv_datatype(dst, dst_type, master_rank, nb); (void)memcpy(message+sizeof(header_t), &stride_src, sizeof(stride_t)); nb_send_header(message, message_size, master_rank, nb); } } STATIC void nb_accs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; /* if not actually a strided acc */ if (0 == stride_levels) { nb_acc(datatype, scale, src, dst, count[0], proc, nb); return; } /* if not a strided acc to self or SMP, use packed algorithm */ if (COMEX_ENABLE_ACC_PACKED && (!COMEX_ENABLE_ACC_SELF || g_state.rank != proc) && (!COMEX_ENABLE_ACC_SMP || strcmp(g_state.host[proc].name,g_state.host[g_state.rank].name))) { nb_accs_packed(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } /* index mangling */ for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_acc(datatype, scale, (char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } } STATIC void nb_accs_packed( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i; int packed_index = 0; char *packed_buffer = NULL; stride_t stride; #if DEBUG fprintf(stderr, "[%d] nb_accs_packed(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != scale); COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != count); COMEX_ASSERT(NULL != nb); COMEX_ASSERT(count[0] > 0); COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); /* copy dst info into structure */ stride.ptr = dst; stride.stride_levels = stride_levels; stride.count[0] = count[0]; for (i=0; i= 0); COMEX_ASSERT(stride.stride_levels < COMEX_MAX_STRIDE_LEVEL); #if DEBUG fprintf(stderr, "[%d] nb_accs_packed stride_levels=%d, count[0]=%d\n", g_state.rank, stride_levels, count[0]); for (i=0; i 0); { header_t *header = NULL; char *message = NULL; int message_size = 0; int scale_size = 0; op_t operation = OP_NULL; int master_rank = -1; int use_eager = 0; switch (datatype) { case COMEX_ACC_INT: operation = OP_ACC_INT_PACKED; scale_size = sizeof(int); break; case COMEX_ACC_DBL: operation = OP_ACC_DBL_PACKED; scale_size = sizeof(double); break; case COMEX_ACC_FLT: operation = OP_ACC_FLT_PACKED; scale_size = sizeof(float); break; case COMEX_ACC_CPL: operation = OP_ACC_CPL_PACKED; scale_size = sizeof(SingleComplex); break; case COMEX_ACC_DCP: operation = OP_ACC_DCP_PACKED; scale_size = sizeof(DoubleComplex); break; case COMEX_ACC_LNG: operation = OP_ACC_LNG_PACKED; scale_size = sizeof(long); break; default: COMEX_ASSERT(0); } use_eager = _eager_check(scale_size+sizeof(stride_t)+packed_index); master_rank = g_state.master[proc]; /* only fence on the master */ fence_array[master_rank] = 1; if (use_eager) { message_size = sizeof(header_t) + scale_size + sizeof(stride_t) + packed_index; } else { message_size = sizeof(header_t) + scale_size + sizeof(stride_t); } message = malloc(message_size); COMEX_ASSERT(message); header = (header_t*)message; header->operation = operation; header->remote_address = dst; header->local_address = NULL; header->rank = proc; header->length = packed_index; (void)memcpy(message+sizeof(header_t), scale, scale_size); (void)memcpy(message+sizeof(header_t)+scale_size, &stride, sizeof(stride_t)); if (use_eager) { (void)memcpy(message+sizeof(header_t)+scale_size+sizeof(stride_t), packed_buffer, packed_index); nb_send_header(message, message_size, master_rank, nb); free(packed_buffer); } else { /* we send the buffer backwards */ char *buf = packed_buffer + packed_index; int bytes_remaining = packed_index; nb_send_header(message, message_size, master_rank, nb); do { int size = bytes_remaining>max_message_size ? max_message_size : bytes_remaining; buf -= size; if (size == bytes_remaining) { nb_send_header(buf, size, master_rank, nb); } else { nb_send_buffer(buf, size, master_rank, nb); } bytes_remaining -= size; } while (bytes_remaining > 0); } } } STATIC void nb_putv( comex_giov_t *iov, int iov_len, int proc, nb_t *nb) { int i = 0; for (i=0; isrc; dst = iov->dst; bytes = iov->bytes; limit = iov->count; #if DEBUG fprintf(stderr, "[%d] nb_putv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif /* allocate compressed iov */ iov_size = 2*limit*sizeof(void*) + 2*sizeof(int); iov_buf = malloc(iov_size); COMEX_ASSERT(iov_buf); iov_off = 0; /* copy limit */ (void)memcpy(&iov_buf[iov_off], &limit, sizeof(int)); iov_off += sizeof(int); /* copy bytes */ (void)memcpy(&iov_buf[iov_off], &bytes, sizeof(int)); iov_off += sizeof(int); /* copy src pointers */ (void)memcpy(&iov_buf[iov_off], src, limit*sizeof(void*)); iov_off += limit*sizeof(void*); /* copy dst pointers */ (void)memcpy(&iov_buf[iov_off], dst, limit*sizeof(void*)); iov_off += limit*sizeof(void*); COMEX_ASSERT(iov_off == iov_size); /* allocate send buffer */ packed_size = bytes * limit; packed_buffer = malloc(packed_size); COMEX_ASSERT(packed_buffer); packed_index = 0; for (i=0; ioperation = OP_PUT_IOV; header->remote_address = NULL; header->local_address = NULL; header->rank = proc; header->length = iov_size; nb_send_header(header, sizeof(header_t), master_rank, nb); nb_send_header(iov_buf, iov_size, master_rank, nb); nb_send_header(packed_buffer, packed_size, master_rank, nb); } } STATIC void nb_getv( comex_giov_t *iov, int iov_len, int proc, nb_t *nb) { int i = 0; for (i=0; isrc; dst = iov->dst; bytes = iov->bytes; limit = iov->count; #if DEBUG fprintf(stderr, "[%d] nb_getv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif /* allocate compressed iov */ iov_size = 2*limit*sizeof(void*) + 2*sizeof(int); iov_buf = malloc(iov_size); iov_off = 0; COMEX_ASSERT(iov_buf); /* copy limit */ (void)memcpy(&iov_buf[iov_off], &limit, sizeof(int)); iov_off += sizeof(int); /* copy bytes */ (void)memcpy(&iov_buf[iov_off], &bytes, sizeof(int)); iov_off += sizeof(int); /* copy src pointers */ (void)memcpy(&iov_buf[iov_off], src, limit*sizeof(void*)); iov_off += limit*sizeof(void*); /* copy dst pointers */ (void)memcpy(&iov_buf[iov_off], dst, limit*sizeof(void*)); iov_off += limit*sizeof(void*); COMEX_ASSERT(iov_off == iov_size); /* copy given iov for later */ iov_copy = malloc(sizeof(comex_giov_t)); iov_copy->bytes = bytes; iov_copy->count = limit; iov_copy->src = malloc(sizeof(void*)*iov->count); COMEX_ASSERT(iov_copy->src); (void)memcpy(iov_copy->src, iov->src, sizeof(void*)*iov->count); iov_copy->dst = malloc(sizeof(void*)*iov->count); COMEX_ASSERT(iov_copy->dst); (void)memcpy(iov_copy->dst, iov->dst, sizeof(void*)*iov->count); #if DEBUG fprintf(stderr, "[%d] nb_getv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p copy\n", g_state.rank, iov_copy->count, iov_copy->bytes, iov_copy->src[0], iov_copy->dst[0]); #endif /* allocate recv buffer */ packed_size = bytes * limit; packed_buffer = malloc(packed_size); COMEX_ASSERT(packed_buffer); { header_t *header = NULL; int master_rank = g_state.master[proc]; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); MAYBE_MEMSET(header, 0, sizeof(header_t)); header->operation = OP_GET_IOV; header->remote_address = NULL; header->local_address = NULL; header->rank = proc; header->length = iov_size; nb_recv_iov(packed_buffer, packed_size, master_rank, nb, iov_copy); nb_send_header(header, sizeof(header_t), master_rank, nb); nb_send_header(iov_buf, iov_size, master_rank, nb); } } STATIC void nb_accv( int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, nb_t *nb) { int i = 0; for (i=0; isrc; dst = iov->dst; bytes = iov->bytes; limit = iov->count; #if DEBUG fprintf(stderr, "[%d] nb_accv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif /* allocate compressed iov */ iov_size = 2*limit*sizeof(void*) + 2*sizeof(int); iov_buf = malloc(iov_size); iov_off = 0; COMEX_ASSERT(iov_buf); /* copy limit */ (void)memcpy(&iov_buf[iov_off], &limit, sizeof(int)); iov_off += sizeof(int); /* copy bytes */ (void)memcpy(&iov_buf[iov_off], &bytes, sizeof(int)); iov_off += sizeof(int); /* copy src pointers */ (void)memcpy(&iov_buf[iov_off], src, limit*sizeof(void*)); iov_off += limit*sizeof(void*); /* copy dst pointers */ (void)memcpy(&iov_buf[iov_off], dst, limit*sizeof(void*)); iov_off += limit*sizeof(void*); COMEX_ASSERT(iov_off == iov_size); /* allocate send buffer */ packed_size = bytes * limit; packed_buffer = malloc(packed_size); COMEX_ASSERT(packed_buffer); packed_index = 0; for (i=0; ioperation = operation; header->remote_address = NULL; header->local_address = NULL; header->rank = proc; header->length = iov_size; (void)memcpy(message+sizeof(header_t), scale, scale_size); nb_send_header(message, message_size, master_rank, nb); nb_send_header(iov_buf, iov_size, master_rank, nb); nb_send_header(packed_buffer, packed_size, master_rank, nb); } } /** * Utility function to catch and translate MPI errors. Returns silently if * no error detected. * @param ierr: Error code from MPI call * @param location: User specified string to indicate location of error */ STATIC void translate_mpi_error(int ierr, const char* location) { if (ierr == MPI_SUCCESS) return; char err_string[MPI_MAX_ERROR_STRING]; int len; fprintf(stderr,"p[%d] Error in %s\n",g_state.rank,location); MPI_Error_string(ierr,err_string,&len); fprintf(stderr,"p[%d] MPI_Error: %s\n",g_state.rank,err_string); } /** * No checking for data consistency. Assume correctness has already been * established elsewhere. Individual elements are assumed to be one byte in size * stride_array: physical dimensions of array * count: number of elements along each array dimension * levels: number of stride levels (should be one less than array dimension) * type: MPI_Datatype returned to calling program */ STATIC void strided_to_subarray_dtype(int *stride_array, int *count, int levels, MPI_Datatype base_type, MPI_Datatype *type) { int ndims = levels+1; int i = 0; int ierr = 0; int array_of_sizes[7]; int array_of_starts[7]; int array_of_subsizes[7]; int stride = 0; ierr = MPI_Type_size(base_type,&stride); translate_mpi_error(ierr,"strided_to_subarray_dtype:MPI_Type_size"); /* the pointer to the local buffer points to the first data element * in data exchange, not the origin of the local array, so all starts * should be zero */ for (i=0; i struct stat finfo; struct statfs ufs_statfs; long newspace; if (g_state.rank == (g_state.node_size -1)) return; if (!devshm_initialized) { fstatfs(fd, &ufs_statfs); devshm_initialized = 1; devshm_fs_initial = (long)(ufs_statfs.f_bavail * ufs_statfs.f_bsize); devshm_fs_left = devshm_fs_initial; // #define DEBUGSHM 1 #define CONVERT_TO_M 1048576 #ifdef DEBUGSHM fprintf(stderr, "[%d] nodesize %d init /dev/shm size %ld bsize %ld nodesize %ld \n", g_state.rank, g_state.node_size, devshm_fs_initial/CONVERT_TO_M, (long) ufs_statfs.f_bsize, (long) g_state.node_size); #endif } newspace = (long) ( size*(g_state.node_size -1)); if(newspace>0){ fstatfs(fd, &ufs_statfs); #ifdef DEBUGSHM fprintf(stderr, "[%d] /dev/shm filesize %ld filesize*np %ld initial devshm space %ld current /dev/shm space %ld \n", g_state.rank, (long) size/CONVERT_TO_M, newspace/CONVERT_TO_M, devshm_fs_initial/CONVERT_TO_M, (long)((ufs_statfs.f_bavail * ufs_statfs.f_bsize)/CONVERT_TO_M)); #endif } if ( newspace > devshm_fs_left ) { char hostname[HOST_NAME_MAX+1]; gethostname(hostname, HOST_NAME_MAX+1); fprintf(stderr, "hostname: %s, [%d] /dev/shm fs has size %ld bytes left, new shm area has size %ld need to increase /dev/shm by %ld Mbytes\n", hostname, g_state.rank, devshm_fs_left/CONVERT_TO_M, newspace/CONVERT_TO_M, (newspace - devshm_fs_left)/CONVERT_TO_M); perror("check_devshm: /dev/shm out of space"); // _free_semaphore(); comex_error("check_devshm: /dev/shm out of space", -1); }else{ devshm_fs_left -= newspace ; } if (devshm_fs_left > devshm_fs_initial) { // reset devshm_fs_left=devshm_fs_initial; } #ifdef DEBUGSHM fprintf(stderr, "[%d] /dev/shm filesize %ld space left %ld \n", g_state.rank, newspace/CONVERT_TO_M, devshm_fs_left/CONVERT_TO_M); #endif #endif } STATIC void count_open_fds(void) { #ifdef __linux__ /* check only every 100 ops && rank == 1 */ counter_open_fds += 1; if (counter_open_fds % 100 == 0 && g_state.rank == MIN(1,g_state.node_size)) { FILE *f = fopen("/proc/sys/fs/file-nr", "r"); long nfiles, unused, maxfiles; fscanf(f, "%ld %ld %ld", &nfiles, &unused, &maxfiles); #ifdef DEBUGSHM if(nfiles % 1000 == 0) fprintf(stderr," %d: no. open files = %ld maxfiles = %ld\n", g_state.rank, nfiles, maxfiles); #endif if(nfiles > (maxfiles/100)*80) { printf(" %d: running out of files; files = %ld maxfiles = %ld \n", g_state.rank, nfiles, maxfiles); #if PAUSE_ON_ERROR fprintf(stderr,"%d(%d): too many open files\n", g_state.rank, getpid()); pause(); #endif comex_error("count_open_fds: too many open files", -1); } fclose(f); } #endif } ga-5.9.2/comex/src-mpi-pr/comex_impl.h000066400000000000000000000036301500715745200175370ustar00rootroot00000000000000#ifndef COMEX_IMPL_H_ #define COMEX_IMPL_H_ #include #include #include "groups.h" #define COMEX_MAX_NB_OUTSTANDING 256 #define COMEX_MAX_STRIDE_LEVEL 8 #define COMEX_TAG 27624 #define COMEX_STATIC_BUFFER_SIZE (2u*1048576u) #define SHM_NAME_SIZE 31 #define UNLOCKED -1 /* performance or correctness related settings */ #if 0 #define ENABLE_UNNAMED_SEM 1 #else #define ENABLE_UNNAMED_SEM 0 #endif #define NEED_ASM_VOLATILE_MEMORY 0 #define MASTER_IS_SMALLEST_SMP_RANK 0 #define COMEX_SET_AFFINITY 0 #define ENABLE_PUT_SELF 1 #define ENABLE_GET_SELF 1 #define ENABLE_ACC_SELF 1 #define ENABLE_PUT_SMP 1 #define ENABLE_GET_SMP 1 #define ENABLE_ACC_SMP 1 #define ENABLE_PUT_PACKED 1 #define ENABLE_GET_PACKED 1 #define ENABLE_ACC_PACKED 1 #define ENABLE_PUT_DATATYPE 1 #define ENABLE_GET_DATATYPE 1 #define ENABLE_ACC_DATATYPE 0 #define ENABLE_PUT_IOV 1 #define ENABLE_GET_IOV 1 #define ENABLE_ACC_IOV 1 #define DEBUG 0 #define DEBUG_VERBOSE 0 #define DEBUG_TO_FILE 0 #if DEBUG_TO_FILE FILE *comex_trace_file; # define printf(...) fprintf(comex_trace_file, __VA_ARGS__); fflush(comex_trace_file) #else # define printf(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) #endif #define COMEX_STRINGIFY(x) #x #ifdef NDEBUG #define COMEX_ASSERT(WHAT) ((void) (0)) #else #define COMEX_ASSERT(WHAT) \ ((WHAT) \ ? (void) (0) \ : comex_assert_fail (COMEX_STRINGIFY(WHAT), __FILE__, __LINE__, __func__)) #endif static inline void comex_assert_fail( const char *assertion, const char *file, unsigned int line, const char *function) { fprintf(stderr, "[%d] %s:%u: %s: Assertion `%s' failed", g_state.rank, file, line, function, assertion); fflush(stderr); #if DEBUG printf("[%d] %s:%u: %s: Assertion `%s' failed", g_state.rank, file, line, function, assertion); #endif comex_error("comex_assert_fail", -1); } #endif /* COMEX_IMPL_H_ */ ga-5.9.2/comex/src-mpi-pr/groups.c000066400000000000000000000470651500715745200167270ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #if defined(__CRAYXE) # include #endif #include "comex.h" #include "comex_impl.h" #include "groups.h" /* world group state */ comex_group_world_t g_state = { MPI_COMM_NULL, MPI_GROUP_NULL, -1, -1, NULL, NULL, MPI_COMM_NULL, -1, -1 }; /* the HEAD of the group linked list */ comex_igroup_t *group_list = NULL; #define RANK_OR_PID (g_state.rank >= 0 ? g_state.rank : getpid()) /* static functions implemented in this file */ static void _create_group_and_igroup(comex_group_t *id, comex_igroup_t **igroup); static void _igroup_free(comex_igroup_t *igroup); static long xgethostid(); /** * Return the comex igroup instance given the group id. * * The group linked list is searched sequentially until the given group * is found. It is an error if this function is called before * comex_group_init(). An error occurs if the given group is not found. */ comex_igroup_t* comex_get_igroup_from_group(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; #if DEBUG printf("[%d] comex_get_igroup_from_group(%d)\n", RANK_OR_PID, id); #endif COMEX_ASSERT(group_list != NULL); while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { return current_group_list_item; } current_group_list_item = current_group_list_item->next; } comex_error("comex group lookup failed", -1); return NULL; } /** * Creates and associates a comex group with a comex igroup. * * This does *not* initialize the members of the comex igroup. */ static void _create_group_and_igroup( comex_group_t *id, comex_igroup_t **igroup) { comex_igroup_t *new_group_list_item = NULL; comex_igroup_t *last_group_list_item = NULL; #if DEBUG printf("[%d] _create_group_and_igroup(...)\n", RANK_OR_PID); #endif /* create, init, and insert the new node for the linked list */ new_group_list_item = malloc(sizeof(comex_igroup_t)); new_group_list_item->next = NULL; new_group_list_item->id = -1; new_group_list_item->comm = MPI_COMM_NULL; new_group_list_item->group = MPI_GROUP_NULL; new_group_list_item->size = -1; new_group_list_item->rank = -1; new_group_list_item->world_ranks = NULL; /* find the last group in the group linked list and insert */ if (group_list) { last_group_list_item = group_list; while (last_group_list_item->next != NULL) { last_group_list_item = last_group_list_item->next; } last_group_list_item->next = new_group_list_item; new_group_list_item->id = last_group_list_item->id + 1; } else { group_list = new_group_list_item; new_group_list_item->id = COMEX_GROUP_WORLD; } /* return the group id and comex igroup */ *igroup = new_group_list_item; *id = new_group_list_item->id; } int comex_group_rank(comex_group_t group, int *rank) { comex_igroup_t *igroup = comex_get_igroup_from_group(group); *rank = igroup->rank; #if DEBUG printf("[%d] comex_group_rank(group=%d, *rank=%d)\n", RANK_OR_PID, group, *rank); #endif return COMEX_SUCCESS; } int comex_group_size(comex_group_t group, int *size) { comex_igroup_t *igroup = comex_get_igroup_from_group(group); *size = igroup->size; #if DEBUG printf("[%d] comex_group_size(group=%d, *size=%d)\n", RANK_OR_PID, group, *size); #endif return COMEX_SUCCESS; } int comex_group_comm(comex_group_t group, MPI_Comm *comm) { comex_igroup_t *igroup = comex_get_igroup_from_group(group); *comm = igroup->comm; #if DEBUG printf("[%d] comex_group_comm(group=%d, comm)\n", RANK_OR_PID, group); #endif return COMEX_SUCCESS; } int comex_group_translate_world( comex_group_t group, int group_rank, int *world_rank) { #if DEBUG printf("[%d] comex_group_translate_world(" "group=%d, group_rank=%d, world_rank)\n", RANK_OR_PID, group, group_rank); #endif if (COMEX_GROUP_WORLD == group) { *world_rank = group_rank; } else { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); COMEX_ASSERT(group_list); /* first group is world worker group */ status = MPI_Group_translate_ranks(igroup->group, 1, &group_rank, group_list->group, world_rank); } return COMEX_SUCCESS; } /** * Destroys the given comex igroup. */ static void _igroup_free(comex_igroup_t *igroup) { int status; #if DEBUG printf("[%d] _igroup_free\n", RANK_OR_PID); #endif COMEX_ASSERT(igroup); if (igroup->group != MPI_GROUP_NULL) { status = MPI_Group_free(&igroup->group); if (status != MPI_SUCCESS) { comex_error("MPI_Group_free: Failed ", status); } } #if DEBUG printf("[%d] free'd group\n", RANK_OR_PID); #endif if (igroup->comm != MPI_COMM_NULL) { status = MPI_Comm_free(&igroup->comm); if (status != MPI_SUCCESS) { comex_error("MPI_Comm_free: Failed ", status); } } #if DEBUG printf("[%d] free'd comm\n", RANK_OR_PID); #endif if (igroup->world_ranks != NULL) { free(igroup->world_ranks); } free(igroup); } int comex_group_free(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; #if DEBUG printf("[%d] comex_group_free(id=%d)\n", RANK_OR_PID, id); #endif /* find the group to free */ while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { break; } previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; } /* make sure we found a group */ COMEX_ASSERT(current_group_list_item != NULL); /* remove the group from the linked list */ if (previous_group_list_item != NULL) { previous_group_list_item->next = current_group_list_item->next; } /* free the igroup */ _igroup_free(current_group_list_item); return COMEX_SUCCESS; } void _igroup_set_world_ranks(comex_igroup_t *igroup) { int i = 0; int my_world_rank = g_state.rank; igroup->world_ranks = (int*)malloc(sizeof(int)*igroup->size); int status; for (i=0; isize; ++i) { igroup->world_ranks[i] = MPI_PROC_NULL; } status = MPI_Allgather(&my_world_rank,1,MPI_INT,igroup->world_ranks, 1,MPI_INT,igroup->comm); COMEX_ASSERT(MPI_SUCCESS == status); for (i=0; isize; ++i) { COMEX_ASSERT(MPI_PROC_NULL != igroup->world_ranks[i]); } } int comex_group_create( int n, int *pid_list, comex_group_t id_parent, comex_group_t *id_child) { int status = 0; int grp_me = 0; comex_igroup_t *igroup_child = NULL; MPI_Group *group_child = NULL; MPI_Comm *comm_child = NULL; comex_igroup_t *igroup_parent = NULL; MPI_Group *group_parent = NULL; MPI_Comm *comm_parent = NULL; #if DEBUG printf("[%d] comex_group_create(" "n=%d, pid_list=%p, id_parent=%d, id_child)\n", RANK_OR_PID, n, pid_list, id_parent); { int p; printf("[%d] pid_list={%d", RANK_OR_PID, pid_list[0]); for (p=1; pgroup); comm_child = &(igroup_child->comm); /* get the parent's MPI_Group and MPI_Comm */ igroup_parent = comex_get_igroup_from_group(id_parent); group_parent = &(igroup_parent->group); comm_parent = &(igroup_parent->comm); status = MPI_Group_incl(*group_parent, n, pid_list, group_child); COMEX_ASSERT(MPI_SUCCESS == status); #if DEBUG printf("[%d] comex_group_create before crazy logic\n", RANK_OR_PID); #endif { MPI_Comm comm, comm1, comm2; int lvl=1, local_ldr_pos; status = MPI_Group_rank(*group_child, &grp_me); COMEX_ASSERT(MPI_SUCCESS == status); if (grp_me == MPI_UNDEFINED) { /* FIXME: keeping the group around for now */ #if DEBUG printf("[%d] comex_group_create aborting -- not in group\n", RANK_OR_PID); #endif return COMEX_SUCCESS; } /* SK: sanity check for the following bitwise operations */ COMEX_ASSERT(grp_me>=0); /* FIXME: can be optimized away */ status = MPI_Comm_dup(MPI_COMM_SELF, &comm); COMEX_ASSERT(MPI_SUCCESS == status); local_ldr_pos = grp_me; while(n>lvl) { int tag=0; int remote_ldr_pos = local_ldr_pos^lvl; if (remote_ldr_pos < n) { int remote_leader = pid_list[remote_ldr_pos]; MPI_Comm peer_comm = *comm_parent; int high = (local_ldr_poscomm, &(igroup_child->size)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_rank(igroup_child->comm, &(igroup_child->rank)); COMEX_ASSERT(MPI_SUCCESS == status); } #if DEBUG printf("[%d] comex_group_create after crazy logic\n", RANK_OR_PID); #endif _igroup_set_world_ranks(igroup_child); return COMEX_SUCCESS; } static int cmplong(const void *p1, const void *p2) { return *((long*)p1) - *((long*)p2); } static int cmpname(const void *name1, const void *name2) { const char* n1 = (const char*)name1; const char* n2 = (const char*)name2; int comp = 0; int i; for (i=0; i (int)n2[i]) { comp = 1; break; } else if (n1[i] == '\0' || n2[i] == '\0') { break; } } return comp; } /** * Initialize group linked list. Prepopulate with world group. */ void comex_group_init(MPI_Comm comm) { int status = 0; int i = 0; int smallest_rank_with_same_hostid = 0; int largest_rank_with_same_hostid = 0; int size_node = 0; comex_group_t group = 0; comex_igroup_t *igroup = NULL; host_name_t *sorted = NULL; int count = 0; /* populate g_state */ /* dup MPI_COMM_WORLD and get group, rank, and size */ status = MPI_Comm_dup(comm, &(g_state.comm)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_group(g_state.comm, &(g_state.group)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_rank(g_state.comm, &(g_state.rank)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_size(g_state.comm, &(g_state.size)); COMEX_ASSERT(MPI_SUCCESS == status); #if DEBUG_TO_FILE { char pathname[80]; sprintf(pathname, "trace.%d.log", g_state.rank); comex_trace_file = fopen(pathname, "w"); COMEX_ASSERT(NULL != comex_trace_file); printf("[%d] comex_group_init()\n", RANK_OR_PID); } #endif /* need to figure out which proc is master on each node */ g_state.host = (host_name_t*)malloc(sizeof(host_name_t)*g_state.size); gethostname(g_state.host[g_state.rank].name,COMEX_MAX_HOST_NAME_LEN); status = MPI_Allgather(MPI_IN_PLACE, sizeof(host_name_t), MPI_BYTE, g_state.host, sizeof(host_name_t), MPI_BYTE, g_state.comm); COMEX_ASSERT(MPI_SUCCESS == status); /* First create a temporary node communicator and then * split further into number of groups within the node */ MPI_Comm temp_node_comm; int temp_node_size; /* create node comm */ /* MPI_Comm_split requires a non-negative color, * so sort and sanitize */ sorted = (host_name_t*)malloc(sizeof(host_name_t) * g_state.size); (void)memcpy(sorted, g_state.host, sizeof(host_name_t)*g_state.size); qsort(sorted, g_state.size, sizeof(host_name_t), cmpname); /* count is number of distinct host IDs that are lower than * the host ID of this rank */ for (i=0; i largest_rank_with_same_hostid) { largest_rank_with_same_hostid = i; } } } /* Get number of Progress-Ranks per node from environment variable * equal to 1 by default */ int num_progress_ranks_per_node = get_num_progress_ranks_per_node(); /* Perform check on the number of Progress-Ranks */ if (size_node < 2 * num_progress_ranks_per_node) { comex_error("ranks per node, must be at least", 2 * num_progress_ranks_per_node); } if (size_node % num_progress_ranks_per_node > 0) { comex_error("number of ranks per node must be multiple of number of process groups per node", -1); } int is_node_ranks_packed = get_progress_rank_distribution_on_node(); int split_group_size; split_group_size = node_group_size / num_progress_ranks_per_node; MPI_Comm_free(&temp_node_comm); g_state.master = (int*)malloc(sizeof(int)*g_state.size); g_state.master[g_state.rank] = get_my_master_rank_with_same_hostid(g_state.rank, split_group_size, smallest_rank_with_same_hostid, largest_rank_with_same_hostid, num_progress_ranks_per_node, is_node_ranks_packed); #if DEBUG printf("[%d] rank; split_group_size: %d\n", g_state.rank, split_group_size); printf("[%d] rank; largest_rank_with_same_hostid[%d]; my master is:[%d]\n", g_state.rank, largest_rank_with_same_hostid, g_state.master[g_state.rank]); #endif status = MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, g_state.master, 1, MPI_INT, g_state.comm); COMEX_ASSERT(MPI_SUCCESS == status); COMEX_ASSERT(group_list == NULL); // put split group stamps int proc_split_group_stamp; int num_split_groups; num_split_groups = num_nodes * num_progress_ranks_per_node; int* split_group_list = (int*)malloc(sizeof(int)*num_split_groups); int split_group_index = 0; int j; for (i=0; icomm)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_group(igroup->comm, &(igroup->group)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_rank(igroup->comm, &(igroup->rank)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_size(igroup->comm, &(igroup->size)); COMEX_ASSERT(MPI_SUCCESS == status); _igroup_set_world_ranks(igroup); COMEX_ASSERT(igroup->world_ranks != NULL); #if DEBUG printf("Creating comm: I AM WORKER[%ld]\n", g_state.rank); #endif } status = MPI_Comm_split(comm, proc_split_group_stamp, g_state.rank, &(g_state.node_comm)); COMEX_ASSERT(MPI_SUCCESS == status); /* node rank */ status = MPI_Comm_rank(g_state.node_comm, &(g_state.node_rank)); COMEX_ASSERT(MPI_SUCCESS == status); /* node size */ status = MPI_Comm_size(g_state.node_comm, &(g_state.node_size)); COMEX_ASSERT(MPI_SUCCESS == status); #if DEBUG printf("node_rank[%d]/ size[%d]\n", g_state.node_rank, g_state.node_size); if (g_state.master[g_state.rank] == g_state.rank) { printf("[%d] world %d/%d\tI'm a master\n", RANK_OR_PID, g_state.rank, g_state.size); } else { printf("[%d] world %d/%d\tI'm a worker\n", RANK_OR_PID, g_state.rank, g_state.size); } #endif } void comex_group_finalize() { int status; comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; #if DEBUG printf("[%d] comex_group_finalize()\n", RANK_OR_PID); #endif while (current_group_list_item != NULL) { previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; _igroup_free(previous_group_list_item); } free(g_state.master); free(g_state.host); status = MPI_Comm_free(&(g_state.node_comm)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Group_free(&(g_state.group)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_free(&(g_state.comm)); COMEX_ASSERT(MPI_SUCCESS == status); } static long xgethostid() { #if defined(__CRAYXE) #warning CRAY int nodeid; PMI_Get_nid(g_state.rank, &nodeid); #else long nodeid = gethostid(); #endif return nodeid; } ga-5.9.2/comex/src-mpi-pr/groups.h000066400000000000000000000176211500715745200167270ustar00rootroot00000000000000/** * Private header file for comex groups backed by MPI_comm. * * The rest of the comex group functions are defined in the public comex.h. * * @author Jeff Daily */ #ifndef _COMEX_GROUPS_H_ #define _COMEX_GROUPS_H_ #define COMEX_MAX_HOST_NAME_LEN 256 #include #include "comex.h" typedef struct { char name[COMEX_MAX_HOST_NAME_LEN]; } host_name_t; typedef struct { MPI_Comm comm; /**< whole comm; all ranks */ MPI_Group group;/**< whole group; all ranks */ int size; /**< comm size */ int rank; /**< comm rank */ int *master; /**< master[size] rank of a given rank's master */ host_name_t *host; /**< host[size] host name of SMP node for a given rank */ MPI_Comm node_comm; /**< node comm; SMP ranks */ int node_size; /**< node comm size */ int node_rank; /**< node comm rank */ } comex_group_world_t; extern comex_group_world_t g_state; typedef struct group_link { struct group_link *next;/**< next group in linked list */ comex_group_t id; /**< user-space id for this group */ MPI_Comm comm; /**< whole comm; all ranks */ MPI_Group group; /**< whole group; all ranks */ int size; /**< comm size */ int rank; /**< comm rank */ int *world_ranks; /**< list of ranks in MPI_COMM_WORLD */ } comex_igroup_t; /** list of worker groups */ extern comex_igroup_t *group_list; extern void comex_group_init(MPI_Comm comm); extern void comex_group_finalize(); extern comex_igroup_t* comex_get_igroup_from_group(comex_group_t group); /* verify that proc is part of group */ #define CHECK_GROUP(GROUP,PROC) do { \ int size; \ int ierr = comex_group_size(GROUP,&size); \ COMEX_ASSERT(GROUP >= 0); \ COMEX_ASSERT(COMEX_SUCCESS == ierr); \ COMEX_ASSERT(PROC >= 0); \ COMEX_ASSERT(PROC < size); \ } while(0) static int get_num_progress_ranks_per_node() { int num_progress_ranks_per_node; const char* num_progress_ranks_env_var = getenv("GA_NUM_PROGRESS_RANKS_PER_NODE"); if (num_progress_ranks_env_var != NULL && num_progress_ranks_env_var[0] != '\0') { int env_number = atoi(getenv("GA_NUM_PROGRESS_RANKS_PER_NODE")); if ( env_number > 0 && env_number < 16) num_progress_ranks_per_node = env_number; #if DEBUG printf("num_progress_ranks_per_node: %d\n", num_progress_ranks_per_node); #endif } else { num_progress_ranks_per_node = 1; } return num_progress_ranks_per_node; } static int get_progress_rank_distribution_on_node() { const char* progress_ranks_packed_env_var = getenv("GA_PROGRESS_RANKS_DISTRIBUTION_PACKED"); const char* progress_ranks_cyclic_env_var = getenv("GA_PROGRESS_RANKS_DISTRIBUTION_CYCLIC"); int is_node_ranks_packed; int rank_packed =0; int rank_cyclic =0; if (progress_ranks_packed_env_var != NULL && progress_ranks_packed_env_var[0] != '\0') { if (strchr(progress_ranks_packed_env_var, 'y') != NULL || strchr(progress_ranks_packed_env_var, 'Y') != NULL || strchr(progress_ranks_packed_env_var, '1') != NULL ) { rank_packed = 1; } } if (progress_ranks_cyclic_env_var != NULL && progress_ranks_cyclic_env_var[0] != '\0') { if (strchr(progress_ranks_cyclic_env_var, 'y') != NULL || strchr(progress_ranks_cyclic_env_var, 'Y') != NULL || strchr(progress_ranks_cyclic_env_var, '1') != NULL ) { rank_cyclic = 1; } } if (rank_packed == 1 || rank_cyclic == 0) is_node_ranks_packed = 1; if (rank_packed == 0 && rank_cyclic == 1) is_node_ranks_packed = 0; return is_node_ranks_packed; } static int get_my_master_rank_with_same_hostid(int rank, int split_group_size, int smallest_rank_with_same_hostid, int largest_rank_with_same_hostid, int num_progress_ranks_per_node, int is_node_ranks_packed) { int my_master; #if MASTER_IS_SMALLEST_SMP_RANK if(is_node_ranks_packed) { /* Contiguous packing of ranks on a node */ my_master = smallest_rank_with_same_hostid + split_group_size * ((rank - smallest_rank_with_same_hostid)/split_group_size); } else { if(num_progress_ranks_per_node == 1) { my_master = smallest_rank_with_same_hostid + split_group_size * ((rank - smallest_rank_with_same_hostid)/split_group_size); } else { /* Cyclic packing of ranks on a node between different split groups, * progress ranks are smallest ranks obtained via modulo operator */ my_master = smallest_rank_with_same_hostid + (rank - smallest_rank_with_same_hostid) % num_progress_ranks_per_node; } } #else /* By default creates largest SMP rank as Master */ if(is_node_ranks_packed) { /* Contiguous packing of ranks on a node */ my_master = largest_rank_with_same_hostid - split_group_size * ((largest_rank_with_same_hostid - rank)/split_group_size); } else { if(num_progress_ranks_per_node == 1) { my_master = largest_rank_with_same_hostid - split_group_size * ((largest_rank_with_same_hostid - rank)/split_group_size); // my_master = largest_rank_with_same_hostid - 2 * (split_group_size * // ( ((largest_rank_with_same_hostid - rank)/2) / split_group_size)); } else { /* Cyclic packing of ranks on a node between different split groups, * progress ranks are highest ranks obtained via modulo operator */ my_master = largest_rank_with_same_hostid - num_progress_ranks_per_node + 1 + (rank - smallest_rank_with_same_hostid) % num_progress_ranks_per_node; } } #endif return my_master; } static int get_my_rank_to_free(int rank, int split_group_size, int smallest_rank_with_same_hostid, int largest_rank_with_same_hostid, int num_progress_ranks_per_node, int is_node_ranks_packed) { int my_rank_to_free; #if MASTER_IS_SMALLEST_SMP_RANK /* By default creates largest SMP rank as Master */ if(is_node_ranks_packed) { /* Contiguous packing of ranks on a node */ my_rank_to_free = largest_rank_with_same_hostid - split_group_size * ((largest_rank_with_same_hostid - rank)/split_group_size); } else { if(num_progress_ranks_per_node == 1) { my_rank_to_free = largest_rank_with_same_hostid - 2 * (split_group_size * ( ((largest_rank_with_same_hostid - rank)/2) / split_group_size)); } else { /* Cyclic packing of ranks on a node between different split groups, * progress ranks are smallest ranks obtained via modulo operator */ my_rank_to_free = smallest_rank_with_same_hostid + (rank - smallest_rank_with_same_hostid) % num_progress_ranks_per_node; } } #else if(is_node_ranks_packed) { /* Contiguous packing of ranks on a node */ my_rank_to_free = smallest_rank_with_same_hostid + split_group_size * ((rank - smallest_rank_with_same_hostid)/split_group_size); } else { if(num_progress_ranks_per_node == 1) { my_rank_to_free = 2 * (split_group_size * ( ((rank - smallest_rank_with_same_hostid)/2) / split_group_size)); } else { /* Cyclic packing of ranks on a node between different split groups, * progress ranks are highest ranks obtained via modulo operator */ my_rank_to_free = largest_rank_with_same_hostid - num_progress_ranks_per_node + 1 + (rank - smallest_rank_with_same_hostid) % num_progress_ranks_per_node; } } #endif return my_rank_to_free; } #endif /* _COMEX_GROUPS_H_ */ ga-5.9.2/comex/src-mpi-pr/hello.c000066400000000000000000000010501500715745200164730ustar00rootroot00000000000000#include #include #include #include "comex.h" int main(int argc, char **argv) { int rank = 0; int size = 0; void **memory = NULL; MPI_Init(&argc, &argv); comex_init(); comex_group_size(COMEX_GROUP_WORLD, &size); comex_group_rank(COMEX_GROUP_WORLD, &rank); memory = malloc(sizeof(void*) * size); assert(memory); comex_malloc(memory, 1024*(rank+1), COMEX_GROUP_WORLD); comex_free(memory[rank], COMEX_GROUP_WORLD); comex_finalize(); MPI_Finalize(); return 0; } ga-5.9.2/comex/src-mpi-pr/reg_cache.c000066400000000000000000000403161500715745200173000ustar00rootroot00000000000000/** * Registration cache. * * Defensive programming via copious COMEX_ASSERT statements is encouraged. */ #if HAVE_CONFIG_H # include "config.h" #endif /* C headers */ #include #include #include #include /* 3rd party headers */ #include /* our headers */ #include "comex.h" #include "comex_impl.h" #include "reg_cache.h" #define STATIC static inline /* the static members in this module */ static reg_entry_t **reg_cache = NULL; /**< list of caches (one per process) */ static int reg_nprocs = 0; /**< number of caches (one per process) */ /* the static functions in this module */ static reg_return_t seg_cmp(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len, int op); static reg_return_t seg_intersects(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len); static reg_return_t seg_contains(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len); static reg_return_t reg_entry_intersects(reg_entry_t *reg_entry, void *buf, size_t len); static reg_return_t reg_entry_contains(reg_entry_t *reg_entry, void *buf, size_t len); #define TEST_FOR_INTERSECTION 0 #define TEST_FOR_CONTAINMENT 1 /** * Detects whether two memory segments intersect or one contains the other. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * @param[in] op op to perform, either TEST_FOR_INTERSECTION or * TEST_FOR_CONTAINMENT * * @pre NULL != reg_beg * @pre NULL != oth_beg * * @return RR_SUCCESS on success */ STATIC reg_return_t seg_cmp(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len, int op) { ptrdiff_t reg_beg = 0; ptrdiff_t reg_end = 0; ptrdiff_t oth_beg = 0; ptrdiff_t oth_end = 0; int result = 0; /* preconditions */ COMEX_ASSERT(NULL != reg_addr); COMEX_ASSERT(NULL != oth_addr); /* casts to ptrdiff_t since arithmetic on void* is undefined */ reg_beg = (ptrdiff_t)(reg_addr); reg_end = reg_beg + (ptrdiff_t)(reg_len); oth_beg = (ptrdiff_t)(oth_addr); oth_end = oth_beg + (ptrdiff_t)(oth_len); /* hack? we had problems with adjacent registered memory regions and * when the length of the query region was 0 */ if (oth_beg == oth_end) { oth_end += 1; } switch (op) { case TEST_FOR_INTERSECTION: result = (reg_beg >= oth_beg && reg_beg < oth_end) || (reg_end > oth_beg && reg_end <= oth_end); #if DEBUG printf("[%d] TEST_FOR_INTERSECTION " "(%td >= %td [%d] && %td < %td [%d]) ||" "(%td > %td [%d] && %td <= %td [%d])\n", g_state.rank, reg_beg, oth_beg, (reg_beg >= oth_beg), reg_beg, oth_end, (reg_beg < oth_end), reg_end, oth_beg, (reg_end > oth_beg), reg_end, oth_end, (reg_end <= oth_end)); #endif break; case TEST_FOR_CONTAINMENT: result = reg_beg <= oth_beg && reg_end >= oth_end; #if DEBUG printf("[%d] TEST_FOR_CONTAINMENT " "%td <= %td [%d] && %td >= %td [%d]\n", g_state.rank, reg_beg, oth_beg, (reg_beg <= oth_beg), reg_end, oth_end, (reg_end >= oth_end)); #endif break; default: COMEX_ASSERT(0); } if (result) { return RR_SUCCESS; } else { return RR_FAILURE; } } /** * Detects whether two memory segments intersect. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * * @pre NULL != reg_beg * @pre NULL != oth_beg * * @return RR_SUCCESS on success */ STATIC reg_return_t seg_intersects(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len) { /* preconditions */ COMEX_ASSERT(NULL != reg_addr); COMEX_ASSERT(NULL != oth_addr); return seg_cmp( reg_addr, reg_len, oth_addr, oth_len, TEST_FOR_INTERSECTION); } /** * Detects whether the first memory segment contains the other. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * * @pre NULL != reg_beg * @pre NULL != oth_beg * * @return RR_SUCCESS on success */ STATIC reg_return_t seg_contains(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len) { /* preconditions */ COMEX_ASSERT(NULL != reg_addr); COMEX_ASSERT(NULL != oth_addr); return seg_cmp( reg_addr, reg_len, oth_addr, oth_len, TEST_FOR_CONTAINMENT); } /** * Detects whether two memory segments intersect. * * @param[in] reg_entry the registration entry * @param[in] buf starting address for the contiguous memory region * @param[in] len length of the contiguous memory region * * @pre NULL != reg_entry * @pre NULL != buf * @pre len >= 0 * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_intersects(reg_entry_t *reg_entry, void *buf, size_t len) { #if DEBUG printf("[%d] reg_entry_intersects(reg_entry=%p, buf=%p, len=%d)\n", g_state.rank, reg_entry, buf, len); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_entry); COMEX_ASSERT(NULL != buf); COMEX_ASSERT(len >= 0); return seg_intersects( reg_entry->buf, reg_entry->len, buf, len); } /** * Detects whether the first memory segment contains the other. * * @param[in] reg_entry the registration entry * @param[in] buf starting address for the contiguous memory region * @param[in] len length of the contiguous memory region * * @pre NULL != reg_entry * @pre NULL != buf * @pre len >= 0 * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_contains(reg_entry_t *reg_entry, void *buf, size_t len) { #if DEBUG printf("[%d] reg_entry_contains(reg_entry=%p, buf=%p, len=%d)\n", g_state.rank, reg_entry, buf, len); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_entry); COMEX_ASSERT(NULL != buf); COMEX_ASSERT(len >= 0); return seg_contains( reg_entry->buf, reg_entry->len, buf, len); } /** * Remove registration cache entry without deregistration. * * @param[in] rank the rank where the entry came from * @param[in] reg_entry the entry * * @pre NULL != reg_entry * @pre 0 <= rank && rank < reg_nprocs * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_destroy(int rank, reg_entry_t *reg_entry) { #if DEBUG printf("[%d] reg_entry_destroy(rank=%d, reg_entry=%p)\n" "buf=%p len=%zu name=%s mapped=%p\n", g_state.rank, rank, reg_entry, reg_entry->buf, reg_entry->len, reg_entry->name, reg_entry->mapped); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_entry); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); /* free cache entry */ free(reg_entry); return RR_SUCCESS; } /** * Create internal data structures for the registration cache. * * @param[in] nprocs number of registration caches to create i.e. one per * process * * @pre this function is called once to initialize the internal data * structures and cannot be called again until reg_cache_destroy() has been * called * * @see reg_cache_destroy() * * @return RR_SUCCESS on success */ reg_return_t reg_cache_init(int nprocs) { int i = 0; #if DEBUG printf("[%d] reg_cache_init(nprocs=%d)\n", g_state.rank, nprocs); #endif /* preconditions */ COMEX_ASSERT(NULL == reg_cache); COMEX_ASSERT(0 == reg_nprocs); /* keep the number of caches around for later use */ reg_nprocs = nprocs; /* allocate the registration cache list: */ reg_cache = (reg_entry_t **)malloc(sizeof(reg_entry_t*) * reg_nprocs); COMEX_ASSERT(reg_cache); /* initialize the registration cache list: */ for (i = 0; i < reg_nprocs; ++i) { reg_cache[i] = NULL; } return RR_SUCCESS; } /** * Deregister and destroy all cache entries and associated buffers. * * @pre this function is called once to destroy the internal data structures * and cannot be called again until reg_cache_init() has been called * * @see reg_cache_init() * * @return RR_SUCCESS on success */ reg_return_t reg_cache_destroy() { int i = 0; #if DEBUG printf("[%d] reg_cache_destroy()\n", g_state.rank); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_cache); COMEX_ASSERT(0 != reg_nprocs); for (i = 0; i < reg_nprocs; ++i) { reg_entry_t *runner = reg_cache[i]; while (runner) { reg_entry_t *previous = runner; /* pointer to previous runner */ /* get next runner */ runner = runner->next; /* destroy the entry */ reg_entry_destroy(i, previous); } } /* free registration cache list */ free(reg_cache); reg_cache = NULL; /* reset the number of caches */ reg_nprocs = 0; return RR_SUCCESS; } /** * Locate a registration cache entry which contains the given segment * completely. * * @param[in] rank rank of the process * @param[in] buf starting address of the buffer * @parma[in] len length of the buffer * * @pre 0 <= rank && rank < reg_nprocs * @pre reg_cache_init() was previously called * * @return the reg cache entry, or NULL on failure */ reg_entry_t* reg_cache_find(int rank, void *buf, size_t len) { reg_entry_t *entry = NULL; reg_entry_t *runner = NULL; #if DEBUG printf("[%d] reg_cache_find(rank=%d, buf=%p, len=%d)\n", g_state.rank, rank, buf, len); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_cache); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); runner = reg_cache[rank]; while (runner && NULL == entry) { if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { entry = runner; #if DEBUG printf("[%d] reg_cache_find entry found\n" "reg_entry=%p buf=%p len=%d\n" "rank=%d buf=%p len=%zu name=%s mapped=%p\n", g_state.rank, runner, buf, len, runner->rank, runner->buf, runner->len, runner->name, runner->mapped); #endif } runner = runner->next; } #ifndef NDEBUG /* we COMEX_ASSERT that the found entry was unique */ while (runner) { if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { #if DEBUG printf("[%d] reg_cache_find duplicate found\n" "reg_entry=%p buf=%p len=%d\n" "rank=%d buf=%p len=%zu name=%s mapped=%p\n", g_state.rank, runner, buf, len, runner->rank, runner->buf, runner->len, runner->name, runner->mapped); #endif COMEX_ASSERT(0); } runner = runner->next; } #endif return entry; } /** * Locate a registration cache entry which intersects the given segment. * * @param[in] rank rank of the process * @param[in] buf starting address of the buffer * @parma[in] len length of the buffer * * @pre 0 <= rank && rank < reg_nprocs * @pre reg_cache_init() was previously called * * @return the reg cache entry, or NULL on failure */ reg_entry_t* reg_cache_find_intersection(int rank, void *buf, size_t len) { reg_entry_t *entry = NULL; reg_entry_t *runner = NULL; #if DEBUG printf("[%d] reg_cache_find_intersection(rank=%d, buf=%p, len=%d)\n", g_state.rank, rank, buf, len); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_cache); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); runner = reg_cache[rank]; while (runner && NULL == entry) { if (RR_SUCCESS == reg_entry_intersects(runner, buf, len)) { entry = runner; } runner = runner->next; } /* we COMEX_ASSERT that the found entry was unique */ while (runner) { if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { COMEX_ASSERT(0); } runner = runner->next; } return entry; } /** * Create a new registration entry based on the given members. * * @pre 0 <= rank && rank < reg_nprocs * @pre NULL != buf * @pre 0 <= len * @pre reg_cache_init() was previously called * @pre NULL == reg_cache_find(rank, buf, 0) * @pre NULL == reg_cache_find_intersection(rank, buf, 0) * * @return RR_SUCCESS on success */ reg_entry_t* reg_cache_insert(int rank, void *buf, size_t len, const char *name, #if ENABLE_SYSV key_t key, #endif void *mapped, int use_dev #if USE_SICM #if SICM_OLD ,sicm_device *device #else ,sicm_device_list device #endif #endif ) { reg_entry_t *node = NULL; #if DEBUG printf("[%d] reg_cache_insert(rank=%d, buf=%p, len=%ld, name=%s, mapped=%p)\n", g_state.rank, rank, buf, len, name, mapped); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_cache); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); COMEX_ASSERT(NULL != buf); COMEX_ASSERT(len >= 0); COMEX_ASSERT(NULL == reg_cache_find(rank, buf, len)); COMEX_ASSERT(NULL == reg_cache_find_intersection(rank, buf, len)); /* allocate the new entry */ node = (reg_entry_t *)malloc(sizeof(reg_entry_t)); COMEX_ASSERT(node); /* initialize the new entry */ node->rank = rank; node->buf = buf; node->len = len; node->use_dev = use_dev; (void)memcpy(node->name, name, SHM_NAME_SIZE); #if ENABLE_SYSV node->key = key; #endif node->mapped = mapped; node->next = NULL; #if USE_SICM node->device = device; #endif /* push new entry to tail of linked list */ if (NULL == reg_cache[rank]) { reg_cache[rank] = node; } else { reg_entry_t *runner = reg_cache[rank]; while (runner->next) { runner = runner->next; } runner->next = node; } return node; } /** * Removes the reg cache entry associated with the given rank and buffer. * * If this process owns the buffer, it will unregister the buffer, as well. * * @param[in] rank * @param[in] buf * * @pre 0 <= rank && rank < reg_nprocs * @pre NULL != buf * @pre reg_cache_init() was previously called * @pre NULL != reg_cache_find(rank, buf, 0) * * @return RR_SUCCESS on success * RR_FAILURE otherwise */ reg_return_t reg_cache_delete(int rank, void *buf) { reg_return_t status = RR_FAILURE; reg_entry_t *runner = NULL; reg_entry_t *previous_runner = NULL; #if DEBUG printf("[%d] reg_cache_delete(rank=%d, buf=%p)\n", g_state.rank, rank, buf); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_cache); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); COMEX_ASSERT(NULL != buf); COMEX_ASSERT(NULL != reg_cache_find(rank, buf, 0)); /* this is more restrictive than reg_cache_find() in that we locate * exactlty the same region starting address */ runner = reg_cache[rank]; while (runner) { if (runner->buf == buf) { break; } previous_runner = runner; runner = runner->next; } /* we should have found an entry */ if (NULL == runner) { COMEX_ASSERT(0); return RR_FAILURE; } /* pop the entry out of the linked list */ if (previous_runner) { previous_runner->next = runner->next; } else { reg_cache[rank] = reg_cache[rank]->next; } status = reg_entry_destroy(rank, runner); return status; } reg_return_t reg_cache_nullify(reg_entry_t *node) { #if DEBUG printf("[%d] reg_cache_nullify(node=%p)\n", g_state.rank, node); #endif node->next = NULL; node->buf = NULL; node->len = 0; node->mapped = NULL; node->rank = -1; node->use_dev = 0; (void)memset(node->name, 0, SHM_NAME_SIZE); return RR_SUCCESS; } ga-5.9.2/comex/src-mpi-pr/reg_cache.h000066400000000000000000000034231500715745200173030ustar00rootroot00000000000000#ifndef _REG_CACHE_H_ #define _REG_CACHE_H_ #if USE_SICM #include //#include #endif #include // #define ENABLE_SYSV #if ENABLE_SYSV #include #endif /** * Enumerate the return codes for registration cache functions. */ typedef enum _reg_return_t { RR_SUCCESS=0, /**< success */ RR_FAILURE /**< non-specific failure */ } reg_return_t; /** * A registered contiguous memory region. */ typedef struct _reg_entry_t { struct _reg_entry_t *next; /**< next memory region in list */ void *buf; /**< starting address of region */ size_t len; /**< length of region */ void *mapped; /**< starting address of mmap'd region */ int rank; /**< rank where this region lives */ int use_dev; /**< memory is on a device */ #if ENABLE_SYSV char name[2*SHM_NAME_SIZE]; /**< name of region */ key_t key; #else char name[SHM_NAME_SIZE]; /**< name of region */ #endif #if USE_SICM #if SICM_OLD sicm_device *device; /**< pointer to SICM device */ #else sicm_device_list device; /**< pointer to SICM device */ #endif #endif } reg_entry_t; /* functions * * documentation is in the *.c file */ reg_return_t reg_cache_init(int nprocs); reg_return_t reg_cache_destroy(); reg_entry_t *reg_cache_find(int rank, void *buf, size_t len); reg_entry_t *reg_cache_insert(int rank, void *buf, size_t len, const char *name, #if ENABLE_SYSV key_t key, #endif void *mapped, int use_dev #if USE_SICM #if SICM_OLD ,sicm_device *device #else ,sicm_device_list device #endif #endif ); reg_return_t reg_cache_delete(int rank, void *buf); reg_return_t reg_cache_nullify(reg_entry_t *entry); #endif /* _REG_CACHE_H_ */ ga-5.9.2/comex/src-mpi-pt/000077500000000000000000000000001500715745200152325ustar00rootroot00000000000000ga-5.9.2/comex/src-mpi-pt/Makefile.inc000066400000000000000000000006101500715745200174370ustar00rootroot00000000000000libcomex_la_SOURCES += src-mpi-pt/comex.c libcomex_la_SOURCES += src-mpi-pt/comex_impl.h libcomex_la_SOURCES += src-mpi-pt/groups.c libcomex_la_SOURCES += src-mpi-pt/groups.h libcomex_la_SOURCES += src-mpi-pt/reg_cache.c libcomex_la_SOURCES += src-mpi-pt/reg_cache.h AM_CPPFLAGS += -I$(top_srcdir)/src-mpi-pt check_PROGRAMS += src-mpi-pt/hello src_mpi_pt_hello_SOURCES = src-mpi-pt/hello.c ga-5.9.2/comex/src-mpi-pt/NOTES.md000066400000000000000000000010731500715745200164450ustar00rootroot00000000000000# MPI Progress Threads (MPI-PT) These are notes describing the MPI progress threads runtime. These notes are intended to help developers navigate the contents of these files and to locate specific functionality. This implementation is nearly identical to MPI-PR. We recommend reading the MPI-PR NOTES.md file for details. The only difference is that instead of using `MPI_Comm_split()` to reserve a user-level MPI rank for progress, a thread is created for asynch progress. Read the MPI-PR NOTES.md for details about shared memory and how the progress server works. ga-5.9.2/comex/src-mpi-pt/comex.c000066400000000000000000004450121500715745200165170ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* C and/or system headers */ #include #include #include #if HAVE_ERRNO_H # include #endif #include #include #include #include #include #include #include #include #include #include /* System V headers */ // #define ENABLE_SYSV #if ENABLE_SYSV #include #include #include #endif /* 3rd party headers */ #include /* our headers */ #include "comex.h" #include "comex_impl.h" #include "groups.h" #include "reg_cache.h" #include "acc.h" #define PAUSE_ON_ERROR 1 #define STATIC static inline /* data structures */ typedef enum { OP_PUT = 0, OP_PUT_PACKED, OP_PUT_IOV, OP_GET, OP_GET_PACKED, OP_GET_IOV, OP_ACC_INT, OP_ACC_DBL, OP_ACC_FLT, OP_ACC_CPL, OP_ACC_DCP, OP_ACC_LNG, OP_ACC_INT_PACKED, OP_ACC_DBL_PACKED, OP_ACC_FLT_PACKED, OP_ACC_CPL_PACKED, OP_ACC_DCP_PACKED, OP_ACC_LNG_PACKED, OP_ACC_INT_IOV, OP_ACC_DBL_IOV, OP_ACC_FLT_IOV, OP_ACC_CPL_IOV, OP_ACC_DCP_IOV, OP_ACC_LNG_IOV, OP_FENCE, OP_FETCH_AND_ADD, OP_SWAP, OP_CREATE_MUTEXES, OP_DESTROY_MUTEXES, OP_LOCK, OP_UNLOCK, OP_QUIT, OP_MALLOC, OP_FREE } op_t; typedef struct { op_t operation; void *remote_address; void *local_address; int rank; /**< rank of target (rank of sender is iprobe_status.MPI_SOURCE */ int length; /**< length of message/payload not including header */ } header_t; /* keep track of all mutex requests */ typedef struct lock_link { struct lock_link *next; int rank; } comex_lock_t; typedef struct { void *ptr; int stride_levels; int stride[COMEX_MAX_STRIDE_LEVEL]; int count[COMEX_MAX_STRIDE_LEVEL+1]; } stride_t; typedef struct message_link { struct message_link *next; void *message; MPI_Request request; int need_free; stride_t *stride; comex_giov_t *iov; } message_t; typedef struct { int in_use; int send_size; message_t *send_head; message_t *send_tail; int recv_size; message_t *recv_head; message_t *recv_tail; } nb_t; typedef struct { int rank; void *ptr; } rank_ptr_t; /* static state */ static int *num_mutexes = NULL; /**< (all) how many mutexes on each process */ static int **mutexes = NULL; /**< (masters) value is rank of lock holder */ static comex_lock_t ***lq_heads = NULL; /**< array of lock queues */ static char *sem_name = NULL; /* local semaphore name */ static sem_t **semaphores = NULL; /* semaphores for locking within SMP node */ static int initialized = 0; /* for comex_initialized(), 0=false */ static char *fence_array = NULL; static pthread_t progress_thread; static int token_counter = 0; static nb_t *nb_state = NULL; /* keep track of all nonblocking operations */ static int nb_max_outstanding = COMEX_MAX_NB_OUTSTANDING; static int nb_index = 0; static int nb_count_event = 0; static int nb_count_event_processed = 0; static int nb_count_send = 0; static int nb_count_send_processed = 0; static int nb_count_recv = 0; static int nb_count_recv_processed = 0; static char *static_acc_buffer = NULL; static int use_dev_shm = 1; #if PAUSE_ON_ERROR static int AR_caught_sig=0; static int AR_caught_sigsegv=0; void (*SigSegvOrig)(int); void SigSegvHandler(int sig) { char name[256]; AR_caught_sig= sig; AR_caught_sigsegv=1; if (-1 == gethostname(name, 256)) { perror("gethostname"); comex_error("gethostname failed", errno); } fprintf(stderr,"%d(%s:%d): Segmentation Violation ... pausing\n", g_state.rank, name, getpid()); pause(); comex_error("Segmentation Violation error, status=",(int) sig); } #endif /* static function declarations */ /* error checking */ #define CHECK_MPI_RETVAL(retval) check_mpi_retval((retval), __FILE__, __LINE__) STATIC void check_mpi_retval(int retval, const char *file, int line); STATIC const char *str_mpi_retval(int retval); /* server fuctions */ STATIC void server_send(void *buf, int count, int dest); STATIC void server_recv(void *buf, int count, int source); STATIC void* _progress_server(void *arg); STATIC void _put_handler(header_t *header, int proc); STATIC void _put_packed_handler(header_t *header, int proc); STATIC void _put_iov_handler(header_t *header, int proc); STATIC void _get_handler(header_t *header, int proc); STATIC void _get_packed_handler(header_t *header, int proc); STATIC void _get_iov_handler(header_t *header, int proc); STATIC void _acc_handler(header_t *header, char *scale, int proc); STATIC void _acc_packed_handler(header_t *header, int proc); STATIC void _acc_iov_handler(header_t *header, int proc); STATIC void _fence_handler(header_t *header, int proc); STATIC void _fetch_and_add_handler(header_t *header, char *payload, int proc); STATIC void _swap_handler(header_t *header, char *payload, int proc); STATIC void _mutex_create_handler(header_t *header, int proc); STATIC void _mutex_destroy_handler(header_t *header, int proc); STATIC void _lock_handler(header_t *header, int proc); STATIC void _unlock_handler(header_t *header, int proc); STATIC void _malloc_handler(header_t *header, char *payload, int proc); STATIC void _free_handler(header_t *header, char *payload, int proc); /* worker functions */ STATIC void nb_send_common(void *buf, int count, int dest, nb_t *nb, int need_free); STATIC void nb_send_header(void *buf, int count, int dest, nb_t *nb); STATIC void nb_send_buffer(void *buf, int count, int dest, nb_t *nb); STATIC void nb_recv_packed(void *buf, int count, int source, nb_t *nb, stride_t *stride); STATIC void nb_recv_iov(void *buf, int count, int source, nb_t *nb, comex_giov_t *iov); STATIC void nb_recv(void *buf, int count, int source, nb_t *nb); STATIC int nb_get_handle_index(); STATIC nb_t* nb_wait_for_handle(); STATIC void nb_wait_for_send(nb_t *nb); STATIC void nb_wait_for_send1(nb_t *nb); STATIC void nb_wait_for_recv(nb_t *nb); STATIC void nb_wait_for_recv1(nb_t *nb); STATIC void nb_wait_for_all(nb_t *nb); STATIC void nb_wait_all(); STATIC void nb_put(void *src, void *dst, int bytes, int proc, nb_t *nb); STATIC void nb_get(void *src, void *dst, int bytes, int proc, nb_t *nb); STATIC void nb_acc(int datatype, void *scale, void *src, void *dst, int bytes, int proc, nb_t *nb); STATIC void nb_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_puts_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_gets_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_accs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_accs_packed( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); STATIC void nb_putv(comex_giov_t *iov, int iov_len, int proc, nb_t *nb); STATIC void nb_putv_packed(comex_giov_t *iov, int proc, nb_t *nb); STATIC void nb_getv(comex_giov_t *iov, int iov_len, int proc, nb_t *nb); STATIC void nb_getv_packed(comex_giov_t *iov, int proc, nb_t *nb); STATIC void nb_accv(int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, nb_t *nb); STATIC void nb_accv_packed(int datatype, void *scale, comex_giov_t *iov, int proc, nb_t *nb); /* other functions */ STATIC int packed_size(int *src_stride, int *count, int stride_levels); STATIC char* pack(char *src, int *src_stride, int *count, int stride_levels, int *size); STATIC void unpack(char *packed_buffer, char *dst, int *dst_stride, int *count, int stride_levels); STATIC char* _generate_shm_name(int rank); STATIC reg_entry_t* _comex_malloc_local(size_t size); STATIC void* _get_offset_memory(reg_entry_t *reg_entry, void *memory); STATIC int _is_master(void); STATIC int _get_world_rank(comex_igroup_t *igroup, int rank); STATIC int* _get_world_ranks(comex_igroup_t *igroup); STATIC int _smallest_world_rank_with_same_hostid(comex_igroup_t *group); STATIC int _largest_world_rank_with_same_hostid(comex_igroup_t *igroup); STATIC void _malloc_semaphore(void); STATIC void _free_semaphore(void); #if ENABLE_SYSV STATIC void* _shm_create(const char *name, key_t *key, size_t size); STATIC void* _shm_attach(const char *name, size_t size, key_t key); #else STATIC void* _shm_create(const char *name, size_t size); STATIC void* _shm_attach(const char *name, size_t size); #endif STATIC void* _shm_map(int fd, size_t size); STATIC int _set_affinity(int cpu); int _comex_init(MPI_Comm comm) { int status = 0; int init_flag = 0; int i = 0; int mpi_thread_level_provided; if (initialized) { return 0; } initialized = 1; /* Assert MPI has been initialized */ status = MPI_Initialized(&init_flag); CHECK_MPI_RETVAL(status); assert(init_flag); /* Assert MPI has been initialized with proper threading level */ status = MPI_Query_thread(&mpi_thread_level_provided); CHECK_MPI_RETVAL(status); COMEX_ASSERT(MPI_THREAD_MULTIPLE == mpi_thread_level_provided); /* env vars */ { char *value = NULL; nb_max_outstanding = COMEX_MAX_NB_OUTSTANDING; /* default */ if ((value = getenv("COMEX_MAX_NB_OUTSTANDING")) != NULL) { nb_max_outstanding = atoi(value); } COMEX_ASSERT(nb_max_outstanding > 0); } /* groups */ comex_group_init(comm); /* mutexes */ mutexes = NULL; num_mutexes = NULL; lq_heads = NULL; /* nonblocking message queues */ nb_state = (nb_t*)malloc(sizeof(nb_t) * nb_max_outstanding); COMEX_ASSERT(nb_state); for (i = 0; i < nb_max_outstanding; ++i) { nb_state[i].in_use = 0; nb_state[i].send_size = 0; nb_state[i].send_head = NULL; nb_state[i].send_tail = NULL; nb_state[i].recv_size = 0; nb_state[i].recv_head = NULL; nb_state[i].recv_tail = NULL; } nb_index = 0; nb_count_event = 0; nb_count_event_processed = 0; nb_count_send = 0; nb_count_send_processed = 0; nb_count_recv = 0; nb_count_recv_processed = 0; #if ENABLE_SYSV /* if using SYSTEM V instead of POSIX SHM, check if /dev/shm exist */ { struct stat sb; if (stat("/dev/shm", &sb) == 0 && S_ISDIR(sb.st_mode)) { use_dev_shm = 1; } else if (stat("/tmp", &sb) == 0 && S_ISDIR(sb.st_mode)) { use_dev_shm = 0; } else { comex_error("No directory available for System V memory\n",-1); } } token_counter = g_state.rank; #endif /* reg_cache */ /* note: every process needs a reg cache and it's always based on the * world rank and size */ reg_cache_init(g_state.size); _malloc_semaphore(); #if DEBUG printf("[%d] comex_init() before progress server\n", g_state.rank); #endif #if PAUSE_ON_ERROR if ((SigSegvOrig=signal(SIGSEGV, SigSegvHandler)) == SIG_ERR) { comex_error("signal(SIGSEGV, ...) error", -1); } #endif /* Synch - Sanity Check */ /* This barrier is on the world worker group */ MPI_Barrier(group_list->comm); status = _set_affinity(g_state.node_rank); COMEX_ASSERT(0 == status); if (_is_master()) { /* TODO: wasteful O(p) storage... */ mutexes = (int**)malloc(sizeof(int*) * g_state.size); COMEX_ASSERT(mutexes); /* create one lock queue for each proc for each mutex */ lq_heads = (comex_lock_t***)malloc(sizeof(comex_lock_t**) * g_state.size); COMEX_ASSERT(lq_heads); /* start the server */ pthread_create(&progress_thread, NULL, _progress_server, NULL); } /* static state */ fence_array = malloc(sizeof(char) * g_state.size); COMEX_ASSERT(fence_array); for (i = 0; i < g_state.size; ++i) { fence_array[i] = 0; } #if DEBUG printf("[%d] comex_init() before barrier\n", g_state.rank); #endif /* Synch - Sanity Check */ /* This barrier is on the world worker group */ MPI_Barrier(group_list->comm); #if DEBUG printf("[%d] comex_init() success\n", g_state.rank); #endif return COMEX_SUCCESS; } int comex_init() { return _comex_init(MPI_COMM_WORLD); } int comex_init_comm(MPI_Comm comm) { return _comex_init(comm); } int comex_init_args(int *argc, char ***argv) { int init_flag; int status; status = MPI_Initialized(&init_flag); CHECK_MPI_RETVAL(status); if(!init_flag) { int level; status = MPI_Init_thread(argc, argv, MPI_THREAD_MULTIPLE, &level); CHECK_MPI_RETVAL(status); COMEX_ASSERT(MPI_THREAD_MULTIPLE == level); } return comex_init(); } int comex_initialized() { #if DEBUG if (initialized) { printf("[%d] comex_initialized()\n", g_state.rank); } #endif return initialized; } int comex_finalize() { #if DEBUG printf("[%d] comex_finalize()\n", g_state.rank); #endif /* it's okay to call multiple times -- extra calls are no-ops */ if (!initialized) { return COMEX_SUCCESS; } comex_barrier(COMEX_GROUP_WORLD); initialized = 0; _free_semaphore(); /* Make sure that all outstanding operations are done */ comex_wait_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); /* send quit message to thread */ if (_is_master()) { int my_master = -1; header_t *header = NULL; nb_t *nb = NULL; nb = nb_wait_for_handle(); my_master = g_state.master[g_state.rank]; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = OP_QUIT; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = 0; nb_send_header(header, sizeof(header_t), my_master, nb); nb_wait_for_all(nb); pthread_join(progress_thread, NULL); } free(fence_array); free(nb_state); #if DEBUG printf(" %d freed nb_state ptr %p \n", g_state.rank, nb_state); #endif MPI_Barrier(g_state.comm); /* reg_cache */ reg_cache_destroy(g_state.size); /* destroy the groups */ #if DEBUG printf("[%d] before comex_group_finalize()\n", g_state.rank); #endif comex_group_finalize(); #if DEBUG printf("[%d] after comex_group_finalize()\n", g_state.rank); #endif #if DEBUG_TO_FILE fclose(comex_trace_file); #endif return COMEX_SUCCESS; } void comex_error(const char *msg, int code) { #if DEBUG printf("[%d] Received an Error in Communication: (%d) %s\n", g_state.rank, code, msg); #if DEBUG_TO_FILE fclose(comex_trace_file); #endif #endif fprintf(stderr,"[%d] Received an Error in Communication: (%d) %s\n", g_state.rank, code, msg); MPI_Abort(g_state.comm, code); } int comex_put( void *src, void *dst, int bytes, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_put(src, dst, bytes, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_get( void *src, void *dst, int bytes, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_get(src, dst, bytes, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_acc( int datatype, void *scale, void *src, void *dst, int bytes, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_acc(datatype, scale, src, dst, bytes, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_puts(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_gets(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_accs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_accs(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_putv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_putv(iov, iov_len, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_getv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_getv(iov, iov_len, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_accv( int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; nb = nb_wait_for_handle(); CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_accv(datatype, scale, iov, iov_len, world_proc, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_fence_all(comex_group_t group) { int p = 0; int count_before = 0; int count_after = 0; nb_t *nb = NULL; #if DEBUG printf("[%d] comex_fence_all(group=%d)\n", g_state.rank, group); #endif /* NOTE: We always fence on the world group */ /* count how many fence messagse to send */ for (p=0; poperation = OP_FENCE; header->remote_address = NULL; header->local_address = NULL; header->length = 0; header->rank = 0; nb_send_header(header, sizeof(header_t), p_master, nb); } } nb_wait_for_all(nb); for (p=0; poperation = OP_FENCE; header->remote_address = NULL; header->local_address = NULL; header->length = 0; header->rank = 0; nb_send_header(header, sizeof(header_t), p_master, nb); nb_wait_for_all(nb); fence_array[world_proc] = 0; } return COMEX_SUCCESS; } /* comex_barrier is comex_fence_all + MPI_Barrier */ int comex_barrier(comex_group_t group) { int status = 0; MPI_Comm comm = MPI_COMM_NULL; #if DEBUG static int count=-1; ++count; printf("[%d] comex_barrier(%d) count=%d\n", g_state.rank, group, count); #endif comex_fence_all(group); status = comex_group_comm(group, &comm); COMEX_ASSERT(COMEX_SUCCESS == status); MPI_Barrier(comm); return COMEX_SUCCESS; } STATIC int packed_size(int *src_stride, int *count, int stride_levels) { int size; int i; int n1dim; /* number of 1 dim block */ COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); COMEX_ASSERT(NULL != src_stride); COMEX_ASSERT(NULL != count); COMEX_ASSERT(count[0] > 0); #if DEBUG printf("[%d] packed_size(src_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, src_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* allocate packed buffer now that we know the size */ size = n1dim * count[0]; return size; } STATIC char* pack( char *src, int *src_stride, int *count, int stride_levels, int *size) { int i, j; long src_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int packed_index = 0; char *packed_buffer = NULL; stride_t stride; COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != src_stride); COMEX_ASSERT(NULL != count); COMEX_ASSERT(count[0] > 0); COMEX_ASSERT(NULL != size); #if DEBUG printf("[%d] pack(src=%p, src_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, src, src_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* allocate packed buffer now that we know the size */ packed_buffer = malloc(n1dim * count[0]); COMEX_ASSERT(packed_buffer); /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } (void)memcpy(&packed_buffer[packed_index], &src[src_idx], count[0]); packed_index += count[0]; } COMEX_ASSERT(packed_index == n1dim*count[0]); *size = packed_index; return packed_buffer; } STATIC void unpack(char *packed_buffer, char *dst, int *dst_stride, int *count, int stride_levels) { int i, j; long dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int dst_bvalue[7], dst_bunit[7]; int packed_index = 0; stride_t stride; COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); COMEX_ASSERT(NULL != packed_buffer); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != dst_stride); COMEX_ASSERT(NULL != count); COMEX_ASSERT(count[0] > 0); #if DEBUG printf("[%d] unpack(dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, dst, dst_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { dst_bvalue[i] = 0; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { dst_bvalue[j] = 0; } } (void)memcpy(&dst[dst_idx], &packed_buffer[packed_index], count[0]); packed_index += count[0]; } COMEX_ASSERT(packed_index == n1dim*count[0]); } STATIC char* _generate_shm_name(int rank) { int snprintf_retval = 0; /* /cmxPPPPPPPPCCCCCCC */ /* 00000000011111111112 */ /* 12345678901234567890 */ char *name = NULL; static const unsigned int limit = 62; static const char letters[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; static unsigned int counter[7] = {0}; unsigned int urank = rank; COMEX_ASSERT(rank >= 0); name = malloc(SHM_NAME_SIZE*sizeof(char)); COMEX_ASSERT(name); snprintf_retval = snprintf(name, SHM_NAME_SIZE, "/cmx%08u%c%c%c%c%c%c%c", urank, letters[counter[6]], letters[counter[5]], letters[counter[4]], letters[counter[3]], letters[counter[2]], letters[counter[1]], letters[counter[0]]); COMEX_ASSERT(snprintf_retval < (int)SHM_NAME_SIZE); name[SHM_NAME_SIZE-1] = '\0'; ++counter[0]; if (counter[0] >= limit) { ++counter[1]; counter[0] = 0; } if (counter[1] >= limit) { ++counter[2]; counter[1] = 0; } if (counter[2] >= limit) { ++counter[3]; counter[2] = 0; } if (counter[3] >= limit) { ++counter[4]; counter[3] = 0; } if (counter[4] >= limit) { ++counter[5]; counter[4] = 0; } if (counter[5] >= limit) { ++counter[6]; counter[5] = 0; } if (counter[6] >= limit) { comex_error("_generate_shm_name: too many names generated", -1); } #if DEBUG printf("[%d] _generate_shm_name(%d)=%s\n", g_state.rank, rank, name); #endif return name; } void* comex_malloc_local(size_t size) { reg_entry_t *reg_entry; void *memory = NULL; if (size > 0) { reg_entry = _comex_malloc_local(size); memory = reg_entry->mapped; } else { memory = NULL; } return memory; } STATIC reg_entry_t* _comex_malloc_local(size_t size) { char *name = NULL; void *memory = NULL; reg_entry_t *reg_entry = NULL; #if ENABLE_SYSV key_t key; char file[SHM_NAME_SIZE+10]; #endif #if DEBUG printf("[%d] _comex_malloc_local(size=%lu)\n", g_state.rank, (long unsigned)size); #endif if (0 == size) { return NULL; } /* create my shared memory object */ name = _generate_shm_name(g_state.rank); #if ENABLE_SYSV memory = _shm_create(name, &key, size); #else memory = _shm_create(name, size); #endif #if DEBUG && DEBUG_VERBOSE printf("[%d] _comex_malloc_local registering " "rank=%d mem=%p size=%lu name=%s mapped=%p\n", g_state.rank, g_state.rank, memory, (long unsigned)size, name, memory); #endif /* register the memory locally */ #if ENABLE_SYSV reg_entry = reg_cache_insert( g_state.rank, memory, size, name, key, memory); #else reg_entry = reg_cache_insert( g_state.rank, memory, size, name, memory); #endif if (NULL == reg_entry) { comex_error("_comex_malloc_local: reg_cache_insert", -1); } free(name); return reg_entry; } /* Utility function to translate errors from shmget */ void _shmget_err(int shm_id, const char* buf) { int lerr = errno; if (shm_id == -1) { perror("shmget"); fprintf(stderr,"%s",buf); if (EACCES == lerr) { fprintf(stderr,"p[%d] shmget error EACCES\n",g_state.rank); } else if (EEXIST == lerr) { fprintf(stderr,"p[%d] shmget error EEXIST\n",g_state.rank); } else if (EINVAL == lerr) { fprintf(stderr,"p[%d] shmget error EINVAL\n",g_state.rank); } else if (ENOENT == lerr) { fprintf(stderr,"p[%d] shmget error ENOENT\n",g_state.rank); } else if (ENOMEM == lerr) { fprintf(stderr,"p[%d] shmget error ENOMEM\n",g_state.rank); } else if (ENOSPC == lerr) { fprintf(stderr,"p[%d] shmget error ENOSPC\n",g_state.rank); } else { fprintf(stderr,"p[%d] shmget error is unknown\n",g_state.rank); } /* { char buf[128]; sprintf(buf,"ipcs -a > shmdev%d.dbg\n",g_state.rank); system(buf); } */ } } /* Utility function to translate errors from shmat */ void _shmat_err(void *ptr) { int lerr = errno; if (ptr == (void*)-1) { perror("shmat"); if (EACCES == lerr) { fprintf(stderr,"p[%d] shmat error EACCES\n",g_state.rank); } else if (EIDRM == lerr) { fprintf(stderr,"p[%d] shmat error EIDRM\n",g_state.rank); } else if (EINVAL == lerr) { fprintf(stderr,"p[%d] shmat error EINVAL\n",g_state.rank); } else if (ENOMEM == lerr) { fprintf(stderr,"p[%d] shmat error ENOMEM\n",g_state.rank); } else if (ENOMEM == lerr) { fprintf(stderr,"p[%d] shmat error ENOMEM\n",g_state.rank); } else { fprintf(stderr,"p[%d] shmat error is unknown\n",g_state.rank); } } } /* Utility function to translate errors from shmdt */ void _shmdt_err(int flag) { int lerr = errno; if (flag == -1) { perror("shmdt"); if (EINVAL == lerr) { fprintf(stderr,"p[%d] shmdt error EINVAL\n",g_state.rank); } else { fprintf(stderr,"p[%d] shmdt error is unknown\n",g_state.rank); } } } /* Utility function to translate errors from shmctl */ void _shmctl_err(int flag) { int lerr = errno; if (flag == -1) { perror("shmctl"); if (EACCES == lerr) { fprintf(stderr,"p[%d] shmctl error EACCES\n",g_state.rank); } else if (EFAULT == lerr) { fprintf(stderr,"p[%d] shmctl error EFAULT\n",g_state.rank); } else if (EIDRM == lerr) { fprintf(stderr,"p[%d] shmctl error EIDRM\n",g_state.rank); } else if (EINVAL == lerr) { fprintf(stderr,"p[%d] shmctl error EINVAL\n",g_state.rank); } else if (ENOMEM == lerr) { fprintf(stderr,"p[%d] shmctl error ENOMEM\n",g_state.rank); } else if (EOVERFLOW == lerr) { fprintf(stderr,"p[%d] shmctl error EOVERFLOW\n",g_state.rank); } else if (EPERM == lerr) { fprintf(stderr,"p[%d] shmctl error EPERM\n",g_state.rank); } else { fprintf(stderr,"p[%d] shmctl error is unknown\n",g_state.rank); } } } int comex_free_local(void *ptr) { #if ENABLE_SYSV key_t key; int shm_id; char file[SHM_NAME_SIZE+10]; char ebuf[128]; #endif int retval = 0; reg_entry_t *reg_entry = NULL; #if DEBUG printf("[%d] comex_free_local(ptr=%p)\n", g_state.rank, ptr); #endif if (NULL == ptr) { return COMEX_SUCCESS; } /* find the registered memory */ reg_entry = reg_cache_find(g_state.rank, ptr, 0); #if ENABLE_SYSV shm_id = shmget(reg_entry->key,reg_entry->len,0600); sprintf(ebuf,"p[%d] (shmget in comex_free_local) flags: 0600, key: %d, name: %s\n", g_state.rank,reg_entry->key,reg_entry->name); _shmget_err(shm_id,ebuf); /* printf("p[%d] DETACH SHM name: %s key: %d\n",g_state.rank,reg_entry->name, reg_entry->key); */ _shmdt_err(shmdt(reg_entry->mapped)); /* printf("p[%d] DESTROY SHM name: %s key: %d\n",g_state.rank,reg_entry->name, reg_entry->key); */ _shmctl_err(shmctl(shm_id, IPC_RMID, NULL)); if (use_dev_shm) { sprintf(file,"/dev/shm/%s\0",reg_entry->name); } else { sprintf(file,"/tmp/%s\0",reg_entry->name); } remove(file); #else /* unmap the memory */ retval = munmap(ptr, reg_entry->len); if (-1 == retval) { perror("comex_free_local: munmap"); comex_error("comex_free_local: munmap", retval); } /* remove the shared memory object */ retval = shm_unlink(reg_entry->name); if (-1 == retval) { perror("comex_free_local: shm_unlink"); comex_error("comex_free_local: shm_unlink", retval); } #endif /* delete the reg_cache entry */ retval = reg_cache_delete(g_state.rank, ptr); COMEX_ASSERT(RR_SUCCESS == retval); return COMEX_SUCCESS; } int comex_wait_proc(int proc, comex_group_t group) { return comex_wait_all(group); } int comex_wait(comex_request_t* hdl) { int index = 0; nb_t *nb = NULL; COMEX_ASSERT(NULL != hdl); index = *(int*)hdl; COMEX_ASSERT(index >= 0); COMEX_ASSERT(index < nb_max_outstanding); nb = &nb_state[index]; if (0 == nb->in_use) { fprintf(stderr, "{%d} comex_wait Error: invalid handle\n", g_state.rank); } nb_wait_for_all(nb); nb->in_use = 0; return COMEX_SUCCESS; } int comex_test(comex_request_t* hdl, int *status) { int index = 0; nb_t *nb = NULL; COMEX_ASSERT(NULL != hdl); index = *(int*)hdl; COMEX_ASSERT(index >= 0); COMEX_ASSERT(index < nb_max_outstanding); nb = &nb_state[index]; if (0 == nb->in_use) { fprintf(stderr, "{%d} comex_test Error: invalid handle\n", g_state.rank); } if (NULL == nb->send_head && NULL == nb->recv_head) { COMEX_ASSERT(0 == nb->send_size); COMEX_ASSERT(0 == nb->recv_size); *status = 0; nb->in_use = 0; } else { *status = 1; } return COMEX_SUCCESS; } int comex_wait_all(comex_group_t group) { nb_wait_all(); return COMEX_SUCCESS; } int comex_nbput( void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_put(src, dst, bytes, world_proc, nb); return COMEX_SUCCESS; } int comex_nbget( void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_get(src, dst, bytes, world_proc, nb); return COMEX_SUCCESS; } int comex_nbacc( int datatype, void *scale, void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_acc(datatype, scale, src, dst, bytes, world_proc, nb); return COMEX_SUCCESS; } int comex_nbputs( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_puts(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); return COMEX_SUCCESS; } int comex_nbgets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_gets(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); return COMEX_SUCCESS; } int comex_nbaccs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_accs(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); return COMEX_SUCCESS; } int comex_nbputv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_putv(iov, iov_len, world_proc, nb); return COMEX_SUCCESS; } int comex_nbgetv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_getv(iov, iov_len, world_proc, nb); return COMEX_SUCCESS; } int comex_nbaccv( int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* hdl) { nb_t *nb = NULL; int world_proc = -1; comex_igroup_t *igroup = NULL; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); COMEX_ASSERT(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_proc = _get_world_rank(igroup, proc); nb_accv(datatype, scale, iov, iov_len, world_proc, nb); return COMEX_SUCCESS; } int comex_rmw( int comex_op, void *ploc, void *prem, int extra, int proc, comex_group_t group) { header_t *header = NULL; char *message = NULL; int payload_int = 0; long payload_long = 0; int length = 0; int op = 0; long extra_long = (long)extra; int world_rank = 0; int master_rank = 0; comex_igroup_t *igroup = NULL; nb_t *nb = NULL; #if DEBUG printf("[%d] comex_rmw(%d, %p, %p, %d, %d)\n", g_state.rank, comex_op, ploc, prem, extra, proc); #endif CHECK_GROUP(group,proc); igroup = comex_get_igroup_from_group(group); world_rank = _get_world_rank(igroup, proc); master_rank = g_state.master[world_rank]; switch (comex_op) { case COMEX_FETCH_AND_ADD: op = OP_FETCH_AND_ADD; length = sizeof(int); payload_int = extra; break; case COMEX_FETCH_AND_ADD_LONG: op = OP_FETCH_AND_ADD; length = sizeof(long); payload_long = extra_long; break; case COMEX_SWAP: op = OP_SWAP; length = sizeof(int); payload_int = *((int*)ploc); break; case COMEX_SWAP_LONG: op = OP_SWAP; length = sizeof(long); payload_long = *((long*)ploc); break; default: COMEX_ASSERT(0); } /* create and prepare the header */ message = malloc(sizeof(header_t) + length); COMEX_ASSERT(message); header = (header_t*)message; header->operation = op; header->remote_address = prem; header->local_address = ploc; header->rank = world_rank; header->length = length; switch (comex_op) { case COMEX_FETCH_AND_ADD: case COMEX_SWAP: (void)memcpy(message+sizeof(header_t), &payload_int, length); break; case COMEX_FETCH_AND_ADD_LONG: case COMEX_SWAP_LONG: (void)memcpy(message+sizeof(header_t), &payload_long, length); break; default: COMEX_ASSERT(0); } nb = nb_wait_for_handle(); nb_recv(ploc, length, master_rank, nb); /* prepost recv */ nb_send_header(message, sizeof(header_t)+length, master_rank, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } /* Mutex Operations */ int comex_create_mutexes(int num) { /* always on the world group */ int my_master = g_state.master[g_state.rank]; #if DEBUG printf("[%d] comex_create_mutexes(num=%d)\n", g_state.rank, num); #endif /* preconditions */ COMEX_ASSERT(0 <= num); COMEX_ASSERT(NULL == num_mutexes); num_mutexes = (int*)malloc(group_list->size * sizeof(int)); /* exchange of mutex counts */ num_mutexes[group_list->rank] = num; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, num_mutexes, 1, MPI_INT, group_list->comm); /* every process sends their own create message to their master */ { nb_t *nb = NULL; header_t *header = NULL; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = OP_CREATE_MUTEXES; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = num; nb = nb_wait_for_handle(); nb_recv(NULL, 0, my_master, nb); /* prepost ack */ nb_send_header(header, sizeof(header_t), my_master, nb); nb_wait_for_all(nb); } MPI_Barrier(group_list->comm); return COMEX_SUCCESS; } int comex_destroy_mutexes() { /* always on the world group */ int my_master = g_state.master[g_state.rank]; #if DEBUG printf("[%d] comex_destroy_mutexes()\n", g_state.rank); #endif /* preconditions */ COMEX_ASSERT(num_mutexes); /* this call is collective on the world group and this barrier ensures * there are no outstanding lock requests */ comex_barrier(COMEX_GROUP_WORLD); /* let masters know they need to participate */ /* first non-master rank in an SMP node sends the message to master */ if (_smallest_world_rank_with_same_hostid(group_list) == g_state.rank) { nb_t *nb = NULL; header_t *header = NULL; header = malloc(sizeof(header_t)); header->operation = OP_DESTROY_MUTEXES; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = num_mutexes[g_state.rank]; nb = nb_wait_for_handle(); nb_recv(NULL, 0, my_master, nb); /* prepost ack */ nb_send_header(header, sizeof(header_t), my_master, nb); nb_wait_for_all(nb); } free(num_mutexes); num_mutexes = NULL; return COMEX_SUCCESS; } int comex_lock(int mutex, int proc) { header_t *header = NULL; int world_rank = 0; int master_rank = 0; int ack = 0; comex_igroup_t *igroup = NULL; nb_t *nb = NULL; #if DEBUG printf("[%d] comex_lock mutex=%d proc=%d\n", g_state.rank, mutex, proc); #endif CHECK_GROUP(COMEX_GROUP_WORLD,proc); igroup = comex_get_igroup_from_group(COMEX_GROUP_WORLD); world_rank = _get_world_rank(igroup, proc); master_rank = g_state.master[world_rank]; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = OP_LOCK; header->remote_address = NULL; header->local_address = NULL; header->rank = world_rank; header->length = mutex; nb = nb_wait_for_handle(); nb_recv(&ack, sizeof(int), master_rank, nb); /* prepost ack */ nb_send_header(header, sizeof(header_t), master_rank, nb); nb_wait_for_all(nb); COMEX_ASSERT(mutex == ack); return COMEX_SUCCESS; } int comex_unlock(int mutex, int proc) { header_t *header = NULL; int world_rank = 0; int master_rank = 0; comex_igroup_t *igroup = NULL; nb_t *nb = NULL; #if DEBUG printf("[%d] comex_unlock mutex=%d proc=%d\n", g_state.rank, mutex, proc); #endif CHECK_GROUP(COMEX_GROUP_WORLD,proc); igroup = comex_get_igroup_from_group(COMEX_GROUP_WORLD); world_rank = _get_world_rank(igroup, proc); master_rank = g_state.master[world_rank]; fence_array[master_rank] = 1; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = OP_UNLOCK; header->remote_address = NULL; header->local_address = NULL; header->rank = world_rank; header->length = mutex; nb = nb_wait_for_handle(); nb_send_header(header, sizeof(header_t), master_rank, nb); nb_wait_for_all(nb); return COMEX_SUCCESS; } int comex_malloc(void *ptrs[], size_t size, comex_group_t group) { comex_igroup_t *igroup = NULL; reg_entry_t *reg_entries = NULL; size_t size_entries = 0; int my_master = -1; int my_world_rank = -1; int i; int is_notifier = 0; int reg_entries_local_count = 0; reg_entry_t *reg_entries_local = NULL; /* preconditions */ COMEX_ASSERT(ptrs); #if DEBUG printf("[%d] comex_malloc(ptrs=%p, size=%lu, group=%d)\n", g_state.rank, ptrs, (long unsigned)size, group); #endif /* is this needed? */ comex_barrier(group); igroup = comex_get_igroup_from_group(group); my_world_rank = _get_world_rank(igroup, igroup->rank); my_master = g_state.master[my_world_rank]; #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_malloc my_master=%d\n", g_state.rank, my_master); #endif #if MASTER_IS_SMALLEST_SMP_RANK is_notifier = _smallest_world_rank_with_same_hostid(igroup) == g_state.rank; #else is_notifier = _largest_world_rank_with_same_hostid(igroup) == g_state.rank; #endif if (is_notifier) { reg_entries_local = malloc(sizeof(reg_entry_t)*g_state.node_size); } /* allocate space for registration cache entries */ size_entries = sizeof(reg_entry_t) * igroup->size; reg_entries = malloc(size_entries); #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_malloc allocated reg entries\n", g_state.rank); #endif /* allocate and register segment */ if (0 == size) { reg_cache_nullify(®_entries[igroup->rank]); } else { reg_entries[igroup->rank] = *_comex_malloc_local(sizeof(char)*size); } #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_malloc allocated and registered local shmem\n", g_state.rank); #endif /* exchange buffer address via reg entries */ MPI_Allgather(MPI_IN_PLACE, sizeof(reg_entry_t), MPI_BYTE, reg_entries, sizeof(reg_entry_t), MPI_BYTE, igroup->comm); #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_malloc allgather reg entries\n", g_state.rank); #endif /* insert reg entries into local registration cache */ for (i=0; isize; ++i) { if (NULL == reg_entries[i].buf) { #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_malloc found NULL buf at %d\n", g_state.rank, i); #endif continue; /* a proc did not allocate (size==0) */ } if (g_state.rank == reg_entries[i].rank) { #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_malloc found self at %d\n", g_state.rank, i); #endif if (is_notifier) { /* does this need to be a memcpy?? */ reg_entries_local[reg_entries_local_count++] = reg_entries[i]; } continue; /* we already registered our own memory */ } if (g_state.hostid[reg_entries[i].rank] == g_state.hostid[my_world_rank]) { /* same SMP node, need to mmap */ /* open remote shared memory object */ #if ENABLE_SYSV void *memory = _shm_attach(reg_entries[i].name, reg_entries[i].len, reg_entries[i].key); #else void *memory = _shm_attach(reg_entries[i].name, reg_entries[i].len); #endif #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_malloc registering " "rank=%d buf=%p len=%lu name=%s map=%p\n", g_state.rank, reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, memory); #endif (void)reg_cache_insert( reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, #if ENABLE_SYSV reg_entries[i].key, #endif memory); if (is_notifier) { /* does this need to be a memcpy?? */ reg_entries_local[reg_entries_local_count++] = reg_entries[i]; } } else { #if 0 /* remote SMP node */ /* i.e. we know about the mem but don't have local shared access */ (void)reg_cache_insert( reg_entries[i].rank, reg_entries[i].buf, reg_entries[i].len, reg_entries[i].name, NULL); #endif } } /* assign the ptr array to return to caller */ for (i=0; isize; ++i) { ptrs[i] = reg_entries[i].buf; } /* send reg entries to my master */ /* first non-master rank in an SMP node sends the message to master */ if (is_notifier && !_is_master()) { nb_t *nb = NULL; int reg_entries_local_size = 0; int message_size = 0; char *message = NULL; header_t *header = NULL; reg_entries_local_size = sizeof(reg_entry_t)*reg_entries_local_count; message_size = sizeof(header_t) + reg_entries_local_size; message = malloc(message_size); COMEX_ASSERT(message); header = (header_t*)message; header->operation = OP_MALLOC; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = reg_entries_local_count; memcpy(message+sizeof(header_t), reg_entries_local, reg_entries_local_size); nb = nb_wait_for_handle(); nb_recv(NULL, 0, my_master, nb); /* prepost ack */ nb_send_header(message, message_size, my_master, nb); nb_wait_for_all(nb); free(reg_entries_local); } else if (is_notifier) { free(reg_entries_local); } free(reg_entries); comex_barrier(group); return COMEX_SUCCESS; } int comex_malloc_mem_dev(void *ptrs[], size_t size, comex_group_t group, const char* device) { return comex_malloc(ptrs,size,group); } /* one unnamed semaphore per world process */ void _malloc_semaphore() { char *name = NULL; char *names = NULL; sem_t *my_sem = NULL; int status = 0; MPI_Datatype shm_name_type; int i = 0; #if DEBUG printf("[%d] _malloc_semaphore()\n", g_state.rank); #endif status = MPI_Type_contiguous(SHM_NAME_SIZE, MPI_CHAR, &shm_name_type); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Type_commit(&shm_name_type); COMEX_ASSERT(MPI_SUCCESS == status); semaphores = (sem_t**)malloc(sizeof(sem_t*) * g_state.size); COMEX_ASSERT(semaphores); name = _generate_shm_name(g_state.rank); COMEX_ASSERT(name); #if ENABLE_UNNAMED_SEM { my_sem = _shm_create(name, sizeof(sem_t)); /* initialize the memory as an inter-process semaphore */ if (0 != sem_init(my_sem, 1, 1)) { perror("_malloc_semaphore: sem_init"); comex_error("_malloc_semaphore: sem_init", -1); } } #else { my_sem = sem_open(name, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR, 1); if (SEM_FAILED == my_sem) { if (EEXIST == errno) { status = sem_unlink(name); if (-1 == status) { perror("_malloc_semaphore: sem_unlink"); comex_error("_malloc_semaphore: sem_unlink", status); } } /* second try */ my_sem = sem_open(name, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR, 1); } if (SEM_FAILED == my_sem) { perror("_malloc_semaphore: sem_open"); comex_error("_malloc_semaphore: sem_open", -1); } } #endif /* store my sem in global cache */ semaphores[g_state.rank] = my_sem; names = (char*)malloc(sizeof(char) * SHM_NAME_SIZE * g_state.size); COMEX_ASSERT(names); /* exchange names */ (void)memcpy(&names[SHM_NAME_SIZE*g_state.rank], name, SHM_NAME_SIZE); status = MPI_Allgather(MPI_IN_PLACE, 1, shm_name_type, names, 1, shm_name_type, g_state.comm); /* create/open remote semaphores and store in cache */ for (i=0; irank]; my_master = g_state.master[my_world_rank]; #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_free my_master=%d\n", g_state.rank, my_master); #endif #if MASTER_IS_SMALLEST_SMP_RANK is_notifier = _smallest_world_rank_with_same_hostid(igroup) == g_state.rank; #else is_notifier = _largest_world_rank_with_same_hostid(igroup) == g_state.rank; #endif if (is_notifier) { rank_ptrs = malloc(sizeof(rank_ptr_t)*g_state.node_size); } /* allocate receive buffer for exchange of pointers */ ptrs = (void **)malloc(sizeof(void *) * igroup->size); COMEX_ASSERT(ptrs); ptrs[igroup->rank] = ptr; #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_free ptrs allocated and assigned\n", g_state.rank); #endif /* exchange of pointers */ MPI_Allgather(MPI_IN_PLACE, sizeof(void *), MPI_BYTE, ptrs, sizeof(void *), MPI_BYTE, igroup->comm); #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_free ptrs exchanged\n", g_state.rank); #endif /* remove all pointers from registration cache */ for (i=0; isize; ++i) { if (i == igroup->rank) { #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_free found self at %d\n", g_state.rank, i); #endif if (is_notifier) { /* does this need to be a memcpy? */ rank_ptrs[reg_entries_local_count].rank = world_ranks[i]; rank_ptrs[reg_entries_local_count].ptr = ptrs[i]; reg_entries_local_count++; } } else if (NULL == ptrs[i]) { #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_free found NULL at %d\n", g_state.rank, i); #endif } else if (g_state.hostid[world_ranks[i]] == g_state.hostid[g_state.rank]) { /* same SMP node */ reg_entry_t *reg_entry = NULL; int retval = 0; #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_free same hostid at %d\n", g_state.rank, i); #endif /* find the registered memory */ reg_entry = reg_cache_find(world_ranks[i], ptrs[i], 0); COMEX_ASSERT(reg_entry); #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_free found reg entry\n", g_state.rank); #endif /* unmap the memory */ #if ENABLE_SYSV /* printf("p[%d] DETACH SHM name: %s key: %d\n",g_state.rank,reg_entry->name, reg_entry->key); */ _shmdt_err(shmdt(reg_entry->mapped)); #else retval = munmap(reg_entry->mapped, reg_entry->len); if (-1 == retval) { perror("comex_free: munmap"); comex_error("comex_free: munmap", retval); } #endif #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_free unmapped mapped memory in reg entry\n", g_state.rank); #endif reg_cache_delete(world_ranks[i], ptrs[i]); #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_free deleted reg cache entry\n", g_state.rank); #endif if (is_notifier) { /* does this need to be a memcpy? */ rank_ptrs[reg_entries_local_count].rank = world_ranks[i]; rank_ptrs[reg_entries_local_count].ptr = ptrs[i]; reg_entries_local_count++; } } else { #if 0 reg_cache_delete(world_ranks[i], ptrs[i]); #if DEBUG && DEBUG_VERBOSE printf("[%d] comex_free deleted reg cache entry\n", g_state.rank); #endif #endif } } /* send ptrs to my master */ /* first non-master rank in an SMP node sends the message to master */ if (is_notifier && !_is_master()) { nb_t *nb = NULL; int rank_ptrs_local_size = 0; int message_size = 0; char *message = NULL; header_t *header = NULL; rank_ptrs_local_size = sizeof(rank_ptr_t) * reg_entries_local_count; message_size = sizeof(header_t) + rank_ptrs_local_size; message = malloc(message_size); COMEX_ASSERT(message); header = (header_t*)message; header->operation = OP_FREE; header->remote_address = NULL; header->local_address = NULL; header->rank = 0; header->length = reg_entries_local_count; memcpy(message+sizeof(header_t), rank_ptrs, rank_ptrs_local_size); nb = nb_wait_for_handle(); nb_recv(NULL, 0, my_master, nb); /* prepost ack */ nb_send_header(message, message_size, my_master, nb); nb_wait_for_all(nb); free(rank_ptrs); } else if (is_notifier) { free(rank_ptrs); } /* free ptrs array */ free(ptrs); free(world_ranks); /* remove my ptr from reg cache and free ptr */ comex_free_local(ptr); /* Is this needed? */ comex_barrier(group); return COMEX_SUCCESS; } int comex_free_dev(void *ptr, comex_group_t group) { return comex_free(ptr, group); } STATIC void* _progress_server(void *arg) { int running = 0; char *static_buffer = NULL; #if DEBUG printf("[%d] _progress_server()\n", g_state.rank); #endif { int status = _set_affinity(g_state.node_size); if (0 != status) { status = _set_affinity(g_state.node_size-1); COMEX_ASSERT(0 == status); } } /* initialize shared buffers */ static_buffer = (char*)malloc(sizeof(char)*COMEX_STATIC_BUFFER_SIZE); COMEX_ASSERT(static_buffer); static_acc_buffer = (char*)malloc(sizeof(char)*COMEX_STATIC_BUFFER_SIZE); COMEX_ASSERT(static_acc_buffer); running = 1; while (running) { int source = 0; int length = 0; char *payload = NULL; header_t *header = NULL; MPI_Status recv_status; MPI_Recv(static_buffer, COMEX_STATIC_BUFFER_SIZE, MPI_CHAR, MPI_ANY_SOURCE, COMEX_TAG, g_state.comm, &recv_status); MPI_Get_count(&recv_status, MPI_CHAR, &length); source = recv_status.MPI_SOURCE; # if DEBUG printf("[%d] progress MPI_Recv source=%d length=%d\n", g_state.rank, source, length); # endif header = (header_t*)static_buffer; payload = static_buffer + sizeof(header_t); /* dispatch message handler */ switch (header->operation) { case OP_PUT: _put_handler(header, source); break; case OP_PUT_PACKED: _put_packed_handler(header, source); break; case OP_PUT_IOV: _put_iov_handler(header, source); break; case OP_GET: _get_handler(header, source); break; case OP_GET_PACKED: _get_packed_handler(header, source); break; case OP_GET_IOV: _get_iov_handler(header, source); break; case OP_ACC_INT: case OP_ACC_DBL: case OP_ACC_FLT: case OP_ACC_CPL: case OP_ACC_DCP: case OP_ACC_LNG: _acc_handler(header, payload, source); break; case OP_ACC_INT_PACKED: case OP_ACC_DBL_PACKED: case OP_ACC_FLT_PACKED: case OP_ACC_CPL_PACKED: case OP_ACC_DCP_PACKED: case OP_ACC_LNG_PACKED: _acc_packed_handler(header, source); break; case OP_ACC_INT_IOV: case OP_ACC_DBL_IOV: case OP_ACC_FLT_IOV: case OP_ACC_CPL_IOV: case OP_ACC_DCP_IOV: case OP_ACC_LNG_IOV: _acc_iov_handler(header, source); break; case OP_FENCE: _fence_handler(header, source); break; case OP_FETCH_AND_ADD: _fetch_and_add_handler(header, payload, source); break; case OP_SWAP: _swap_handler(header, payload, source); break; case OP_CREATE_MUTEXES: _mutex_create_handler(header, source); break; case OP_DESTROY_MUTEXES: _mutex_destroy_handler(header, source); break; case OP_LOCK: _lock_handler(header, source); break; case OP_UNLOCK: _unlock_handler(header, source); break; case OP_QUIT: running = 0; break; case OP_MALLOC: _malloc_handler(header, payload, source); break; case OP_FREE: _free_handler(header, payload, source); break; default: printf("[%d] header operation not recognized: %d\n", g_state.rank, header->operation); COMEX_ASSERT(0); } } free(static_buffer); free(static_acc_buffer); return NULL; } STATIC void _put_handler(header_t *header, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int retval = 0; MPI_Status recv_status; #if DEBUG printf("[%d] _put_handler rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory( reg_entry, header->remote_address); retval = MPI_Recv(mapped_offset, header->length, MPI_CHAR, proc, COMEX_TAG, g_state.comm, &recv_status); CHECK_MPI_RETVAL(retval); } STATIC void _put_packed_handler(header_t *header, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int retval = 0; MPI_Status recv_status; char *packed_buffer = NULL; int packed_index = 0; stride_t *stride = NULL; int i, j; long dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int dst_bvalue[7], dst_bunit[7]; #if DEBUG printf("[%d] _put_packed_handler rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif stride = malloc(sizeof(stride_t)); COMEX_ASSERT(stride); server_recv(stride, sizeof(stride_t), proc); COMEX_ASSERT(stride->stride_levels >= 0); COMEX_ASSERT(stride->stride_levels < COMEX_MAX_STRIDE_LEVEL); #if DEBUG printf("[%d] _put_packed_handler stride_levels=%d, count[0]=%d\n", g_state.rank, stride->stride_levels, stride->count[0]); for (i=0; istride_levels; ++i) { printf("[%d] stride[%d]=%d count[%d+1]=%d\n", g_state.rank, i, stride->stride[i], i, stride->count[i+1]); } #endif if ((unsigned)header->length > COMEX_STATIC_BUFFER_SIZE) { packed_buffer = malloc(header->length); } else { packed_buffer = static_acc_buffer; } server_recv(packed_buffer, header->length, proc); reg_entry = reg_cache_find( header->rank, header->remote_address, stride->count[0]); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory( reg_entry, header->remote_address); unpack(packed_buffer, mapped_offset, stride->stride, stride->count, stride->stride_levels); free(stride); if ((unsigned)header->length > COMEX_STATIC_BUFFER_SIZE) { free(packed_buffer); } } STATIC void _put_iov_handler(header_t *header, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int i = 0; char *packed_buffer = NULL; int packed_index = 0; char *iov_buf = NULL; int iov_off = 0; int limit = 0; int bytes = 0; void **src = NULL; void **dst = NULL; #if DEBUG printf("[%d] _put_iov_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG printf("[%d] _put_iov_handler header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_PUT_IOV == header->operation); iov_buf = malloc(header->length); COMEX_ASSERT(iov_buf); server_recv(iov_buf, header->length, proc); limit = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(limit > 0); bytes = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(bytes > 0); src = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; dst = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; COMEX_ASSERT(iov_off == header->length); #if DEBUG printf("[%d] _put_iov_handler limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif if ((bytes*limit) > COMEX_STATIC_BUFFER_SIZE) { packed_buffer = malloc(bytes*limit); COMEX_ASSERT(packed_buffer); } else { packed_buffer = static_acc_buffer; } server_recv(packed_buffer, bytes * limit, proc); packed_index = 0; for (i=0; irank, dst[i], bytes); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory( reg_entry, dst[i]); (void)memcpy(mapped_offset, &packed_buffer[packed_index], bytes); packed_index += bytes; } COMEX_ASSERT(packed_index == bytes*limit); if ((bytes*limit) > COMEX_STATIC_BUFFER_SIZE) { free(packed_buffer); } free(iov_buf); } STATIC void _get_handler(header_t *header, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; #if DEBUG printf("[%d] _get_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG printf("[%d] header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif COMEX_ASSERT(OP_GET == header->operation); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); server_send(mapped_offset, header->length, proc); } STATIC void _get_packed_handler(header_t *header, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; char *packed_buffer = NULL; int packed_index = 0; stride_t *stride_src = NULL; #if DEBUG printf("[%d] _get_packed_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG printf("[%d] header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_GET_PACKED == header->operation); stride_src = malloc(sizeof(stride_t)); COMEX_ASSERT(stride_src); server_recv(stride_src, sizeof(stride_t), proc); COMEX_ASSERT(stride_src->stride_levels >= 0); COMEX_ASSERT(stride_src->stride_levels < COMEX_MAX_STRIDE_LEVEL); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); packed_buffer = pack(mapped_offset, stride_src->stride, stride_src->count, stride_src->stride_levels, &packed_index); server_send(packed_buffer, packed_index, proc); free(stride_src); free(packed_buffer); } STATIC void _get_iov_handler(header_t *header, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int i = 0; char *packed_buffer = NULL; int packed_index = 0; char *iov_buf = NULL; int iov_off = 0; int limit = 0; int bytes = 0; void **src = NULL; void **dst = NULL; #if DEBUG printf("[%d] _get_iov_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG printf("[%d] _get_iov_handler header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif assert(OP_GET_IOV == header->operation); iov_buf = malloc(header->length); COMEX_ASSERT(iov_buf); server_recv(iov_buf, header->length, proc); limit = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(limit > 0); bytes = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(bytes > 0); src = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; dst = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; COMEX_ASSERT(iov_off == header->length); #if DEBUG printf("[%d] _get_iov_handler limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif if ((bytes*limit) > COMEX_STATIC_BUFFER_SIZE) { packed_buffer = malloc(bytes*limit); COMEX_ASSERT(packed_buffer); } else { packed_buffer = static_acc_buffer; } packed_index = 0; for (i=0; irank, src[i], bytes); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, src[i]); (void)memcpy(&packed_buffer[packed_index], mapped_offset, bytes); packed_index += bytes; } COMEX_ASSERT(packed_index == bytes*limit); server_send(packed_buffer, packed_index, proc); if ((bytes*limit) > COMEX_STATIC_BUFFER_SIZE) { free(packed_buffer); } free(iov_buf); } STATIC void _acc_handler(header_t *header, char *scale, int proc) { int sizeof_scale = 0; int acc_type = 0; reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; char *acc_buffer = NULL; MPI_Status recv_status; #if DEBUG printf("[%d] _acc_handler\n", g_state.rank); #endif switch (header->operation) { case OP_ACC_INT: acc_type = COMEX_ACC_INT; sizeof_scale = sizeof(int); break; case OP_ACC_DBL: acc_type = COMEX_ACC_DBL; sizeof_scale = sizeof(double); break; case OP_ACC_FLT: acc_type = COMEX_ACC_FLT; sizeof_scale = sizeof(float); break; case OP_ACC_LNG: acc_type = COMEX_ACC_LNG; sizeof_scale = sizeof(long); break; case OP_ACC_CPL: acc_type = COMEX_ACC_CPL; sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP: acc_type = COMEX_ACC_DCP; sizeof_scale = sizeof(DoubleComplex); break; default: COMEX_ASSERT(0); } if ((unsigned)header->length > COMEX_STATIC_BUFFER_SIZE) { acc_buffer = malloc(header->length); } else { acc_buffer = static_acc_buffer; } MPI_Recv(acc_buffer, header->length, MPI_CHAR, proc, COMEX_TAG, g_state.comm, &recv_status); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); sem_wait(semaphores[header->rank]); _acc(acc_type, header->length, mapped_offset, acc_buffer, scale); sem_post(semaphores[header->rank]); if ((unsigned)header->length > COMEX_STATIC_BUFFER_SIZE) { free(acc_buffer); } } STATIC void _acc_packed_handler(header_t *header, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; void *scale = NULL; int sizeof_scale = 0; int acc_type = 0; char *acc_buffer = NULL; MPI_Status recv_status; stride_t *stride = NULL; #if DEBUG printf("[%d] _acc_packed_handler\n", g_state.rank); #endif switch (header->operation) { case OP_ACC_INT_PACKED: acc_type = COMEX_ACC_INT; sizeof_scale = sizeof(int); break; case OP_ACC_DBL_PACKED: acc_type = COMEX_ACC_DBL; sizeof_scale = sizeof(double); break; case OP_ACC_FLT_PACKED: acc_type = COMEX_ACC_FLT; sizeof_scale = sizeof(float); break; case OP_ACC_LNG_PACKED: acc_type = COMEX_ACC_LNG; sizeof_scale = sizeof(long); break; case OP_ACC_CPL_PACKED: acc_type = COMEX_ACC_CPL; sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP_PACKED: acc_type = COMEX_ACC_DCP; sizeof_scale = sizeof(DoubleComplex); break; default: COMEX_ASSERT(0); } scale = malloc(sizeof_scale); COMEX_ASSERT(scale); server_recv(scale, sizeof_scale, proc); stride = malloc(sizeof(stride_t)); COMEX_ASSERT(stride); server_recv(stride, sizeof(stride_t), proc); if ((unsigned)header->length > COMEX_STATIC_BUFFER_SIZE) { acc_buffer = malloc(header->length); } else { acc_buffer = static_acc_buffer; } server_recv(acc_buffer, header->length, proc); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); sem_wait(semaphores[header->rank]); { char *packed_buffer = acc_buffer; char *dst = mapped_offset; int *dst_stride = stride->stride; int *count = stride->count; int stride_levels = stride->stride_levels; int i, j; long dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int dst_bvalue[7], dst_bunit[7]; int packed_index = 0; COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); COMEX_ASSERT(NULL != packed_buffer); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != dst_stride); COMEX_ASSERT(NULL != count); COMEX_ASSERT(count[0] > 0); #if DEBUG printf("[%d] unpack(dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d)\n", g_state.rank, dst, dst_stride, count[0], stride_levels); #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { dst_bvalue[i] = 0; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { dst_bvalue[j] = 0; } } _acc(acc_type, count[0], &dst[dst_idx], &packed_buffer[packed_index], scale); packed_index += count[0]; } COMEX_ASSERT(packed_index == n1dim*count[0]); } sem_post(semaphores[header->rank]); if ((unsigned)header->length > COMEX_STATIC_BUFFER_SIZE) { free(acc_buffer); } free(stride); free(scale); } STATIC void _acc_iov_handler(header_t *header, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int i = 0; char *packed_buffer = NULL; int packed_index = 0; char *iov_buf = NULL; int iov_off = 0; int limit = 0; int bytes = 0; void **src = NULL; void **dst = NULL; void *scale = NULL; int sizeof_scale = 0; int acc_type = 0; MPI_Status recv_status; #if DEBUG printf("[%d] _acc_iov_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG printf("[%d] _acc_iov_handler header rem=%p loc=%p rem_rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif #if DEBUG printf("[%d] _acc_iov_handler limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif switch (header->operation) { case OP_ACC_INT_IOV: acc_type = COMEX_ACC_INT; sizeof_scale = sizeof(int); break; case OP_ACC_DBL_IOV: acc_type = COMEX_ACC_DBL; sizeof_scale = sizeof(double); break; case OP_ACC_FLT_IOV: acc_type = COMEX_ACC_FLT; sizeof_scale = sizeof(float); break; case OP_ACC_LNG_IOV: acc_type = COMEX_ACC_LNG; sizeof_scale = sizeof(long); break; case OP_ACC_CPL_IOV: acc_type = COMEX_ACC_CPL; sizeof_scale = sizeof(SingleComplex); break; case OP_ACC_DCP_IOV: acc_type = COMEX_ACC_DCP; sizeof_scale = sizeof(DoubleComplex); break; default: COMEX_ASSERT(0); } scale = malloc(sizeof_scale); COMEX_ASSERT(scale); server_recv(scale, sizeof_scale, proc); iov_buf = malloc(header->length); COMEX_ASSERT(iov_buf); server_recv(iov_buf, header->length, proc); limit = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(limit > 0); bytes = *((int*)(&iov_buf[iov_off])); iov_off += sizeof(int); COMEX_ASSERT(bytes > 0); src = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; dst = (void**)&iov_buf[iov_off]; iov_off += sizeof(void*)*limit; COMEX_ASSERT(iov_off == header->length); if ((bytes*limit) > COMEX_STATIC_BUFFER_SIZE) { packed_buffer = malloc(bytes*limit); } else { packed_buffer = static_acc_buffer; } server_recv(packed_buffer, bytes*limit, proc); sem_wait(semaphores[header->rank]); packed_index = 0; for (i=0; irank, dst[i], bytes); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, dst[i]); _acc(acc_type, bytes, mapped_offset, &packed_buffer[packed_index], scale); packed_index += bytes; } COMEX_ASSERT(packed_index == bytes*limit); sem_post(semaphores[header->rank]); if ((bytes*limit) > COMEX_STATIC_BUFFER_SIZE) { free(packed_buffer); } free(scale); free(iov_buf); } STATIC void _fence_handler(header_t *header, int proc) { #if DEBUG printf("[%d] _fence_handler proc=%d\n", g_state.rank, proc); #endif /* preconditions */ COMEX_ASSERT(header); #if NEED_ASM_VOLATILE_MEMORY #if DEBUG printf("[%d] _fence_handler asm volatile (\"\" : : : \"memory\"); \n", g_state.rank); #endif asm volatile ("" : : : "memory"); #endif /* we send the ack back to the originating proc */ server_send(NULL, 0, proc); } STATIC void _fetch_and_add_handler(header_t *header, char *payload, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int *value_int = NULL; long *value_long = NULL; #if DEBUG printf("[%d] _fetch_and_add_handler proc=%d\n", g_state.rank, proc); #endif #if DEBUG printf("[%d] header rem=%p loc=%p rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif COMEX_ASSERT(OP_FETCH_AND_ADD == header->operation); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); if (sizeof(int) == header->length) { value_int = malloc(sizeof(int)); *value_int = *((int*)mapped_offset); /* "fetch" */ *((int*)mapped_offset) += *((int*)payload); /* "add" */ server_send(value_int, sizeof(int), proc); free(value_int); } else if (sizeof(long) == header->length) { value_long = malloc(sizeof(long)); *value_long = *((long*)mapped_offset); /* "fetch" */ *((long*)mapped_offset) += *((long*)payload); /* "add" */ server_send(value_long, sizeof(long), proc); free(value_long); } else { COMEX_ASSERT(0); } } STATIC void _swap_handler(header_t *header, char *payload, int proc) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; int *value_int = NULL; long *value_long = NULL; #if DEBUG printf("[%d] _swap_handler rem=%p loc=%p rank=%d len=%d\n", g_state.rank, header->remote_address, header->local_address, header->rank, header->length); #endif COMEX_ASSERT(OP_SWAP == header->operation); reg_entry = reg_cache_find( header->rank, header->remote_address, header->length); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, header->remote_address); if (sizeof(int) == header->length) { value_int = malloc(sizeof(int)); *value_int = *((int*)mapped_offset); /* "fetch" */ *((int*)mapped_offset) = *((int*)payload); /* "swap" */ server_send(value_int, sizeof(int), proc); free(value_int); } else if (sizeof(long) == header->length) { value_long = malloc(sizeof(long)); *value_long = *((long*)mapped_offset); /* "fetch" */ *((long*)mapped_offset) = *((long*)payload); /* "swap" */ server_send(value_long, sizeof(long), proc); free(value_long); } else { COMEX_ASSERT(0); } } STATIC void _mutex_create_handler(header_t *header, int proc) { int i; int num = header->length; #if DEBUG printf("[%d] _mutex_create_handler proc=%d num=%d\n", g_state.rank, proc, num); #endif mutexes[proc] = (int*)malloc(sizeof(int) * num); lq_heads[proc] = (comex_lock_t**)malloc(sizeof(comex_lock_t*) * num); for (i=0; ilength; #if DEBUG printf("[%d] _mutex_destroy_handler proc=%d\n", g_state.rank, proc); #endif for (i=0; ilength; int rank = header->rank; #if DEBUG printf("[%d] _lock_handler id=%d in rank=%d req by proc=%d\n", g_state.rank, id, rank, proc); #endif COMEX_ASSERT(0 <= id); if (UNLOCKED == mutexes[rank][id]) { mutexes[rank][id] = proc; server_send(&id, sizeof(int), proc); } else { comex_lock_t *lock = NULL; #if DEBUG printf("[%d] _lq_push rank=%d req_by=%d id=%d\n", g_state.rank, rank, proc, id); #endif lock = malloc(sizeof(comex_lock_t)); lock->next = NULL; lock->rank = proc; if (lq_heads[rank][id]) { /* insert at tail */ comex_lock_t *lq = lq_heads[rank][id]; while (lq->next) { lq = lq->next; } lq->next = lock; } else { /* new head */ lq_heads[rank][id] = lock; } } } STATIC void _unlock_handler(header_t *header, int proc) { int id = header->length; int rank = header->rank; #if DEBUG printf("[%d] _unlock_handler id=%d in rank=%d req by proc=%d\n", g_state.rank, id, rank, proc); #endif COMEX_ASSERT(0 <= id); if (lq_heads[rank][id]) { /* a lock requester was queued */ /* find the next lock request and update queue */ comex_lock_t *lock = lq_heads[rank][id]; lq_heads[rank][id] = lq_heads[rank][id]->next; /* update lock */ mutexes[rank][id] = lock->rank; /* notify next in line */ server_send(&id, sizeof(int), lock->rank); free(lock); } else { /* no enqued request */ mutexes[rank][id] = UNLOCKED; } } STATIC void _malloc_handler( header_t *header, char *payload, int proc) { int i; int n; reg_entry_t *reg_entries = (reg_entry_t*)payload; #if DEBUG printf("[%d] _malloc_handler proc=%d\n", g_state.rank, proc); #endif COMEX_ASSERT(header); COMEX_ASSERT(header->operation == OP_MALLOC); n = header->length; #if DEBUG && DEBUG_VERBOSE printf("[%d] _malloc_handler preconditions complete\n", g_state.rank); #endif /* insert reg entries into local registration cache */ for (i=0; ilength; rank_ptr_t *rank_ptrs = (rank_ptr_t*)payload; #if ENABLE_SYSV int shm_id; #endif #if DEBUG printf("[%d] _free_handler proc=%d\n", g_state.rank, proc); #endif /* remove all pointers from registration cache */ for (i=0; ikey,reg_entry->len,0600); */ /* printf("p[%d] DETACH SHM name: %s key: %d\n",g_state.rank,reg_entry->name, reg_entry->key); */ _shmdt_err(shmdt(reg_entry->mapped)); retval = 0; #else retval = munmap(reg_entry->mapped, reg_entry->len); if (-1 == retval) { perror("_free_handler: munmap"); comex_error("_free_handler: munmap", retval); } #endif #if DEBUG && DEBUG_VERBOSE printf("[%d] _free_handler unmapped mapped memory in reg entry\n", g_state.rank); #endif reg_cache_delete(rank_ptrs[i].rank, rank_ptrs[i].ptr); #if DEBUG && DEBUG_VERBOSE printf("[%d] _free_handler deleted reg cache entry\n", g_state.rank); #endif } else { #if 0 reg_cache_delete(rank_ptrs[i].rank, rank_ptrs[i].ptr); #if DEBUG && DEBUG_VERBOSE printf("[%d] _free_handler deleted reg cache entry\n", g_state.rank); #endif #endif } } server_send(NULL, 0, proc); /* ack */ } STATIC void* _get_offset_memory(reg_entry_t *reg_entry, void *memory) { ptrdiff_t offset = 0; COMEX_ASSERT(reg_entry); #if DEBUG_VERBOSE printf("[%d] _get_offset_memory reg_entry->buf=%p memory=%p\n", g_state.rank, reg_entry->buf, memory); #endif offset = ((char*)memory) - ((char*)reg_entry->buf); #if DEBUG_VERBOSE printf("[%d] _get_offset_memory ptrdiff=%lu\n", g_state.rank, (unsigned long)offset); #endif return (void*)((char*)(reg_entry->mapped)+offset); } STATIC int _is_master(void) { return (g_state.master[g_state.rank] == g_state.rank); } STATIC int _get_world_rank(comex_igroup_t *igroup, int rank) { int world_rank; int status; status = MPI_Group_translate_ranks(igroup->group, 1, &rank, g_state.group, &world_rank); CHECK_MPI_RETVAL(status); COMEX_ASSERT(MPI_PROC_NULL != world_rank); return world_rank; } /* gets (in group order) corresponding world ranks for entire group */ STATIC int* _get_world_ranks(comex_igroup_t *igroup) { int i = 0; int *group_ranks = (int*)malloc(sizeof(int)*igroup->size); int *world_ranks = (int*)malloc(sizeof(int)*igroup->size); int status; for (i=0; isize; ++i) { group_ranks[i] = i; world_ranks[i] = MPI_PROC_NULL; } status = MPI_Group_translate_ranks( igroup->group, igroup->size, group_ranks, g_state.group, world_ranks); COMEX_ASSERT(MPI_SUCCESS == status); for (i=0; isize; ++i) { COMEX_ASSERT(MPI_PROC_NULL != world_ranks[i]); } free(group_ranks); return world_ranks; } /* we sometimes need to notify a node master of some event and the rank in * charge of doing that is returned by this function */ STATIC int _smallest_world_rank_with_same_hostid(comex_igroup_t *igroup) { int i = 0; int smallest = g_state.rank; int *world_ranks = _get_world_ranks(igroup); for (i=0; isize; ++i) { if (g_state.hostid[world_ranks[i]] == g_state.hostid[g_state.rank]) { /* found same host as me */ if (world_ranks[i] < smallest) { smallest = world_ranks[i]; } } } free(world_ranks); return smallest; } /* we sometimes need to notify a node master of some event and the rank in * charge of doing that is returned by this function */ STATIC int _largest_world_rank_with_same_hostid(comex_igroup_t *igroup) { int i = 0; int largest = g_state.rank; int *world_ranks = _get_world_ranks(igroup); for (i=0; isize; ++i) { if (g_state.hostid[world_ranks[i]] == g_state.hostid[g_state.rank]) { /* found same host as me */ if (world_ranks[i] > largest) { largest = world_ranks[i]; } } } free(world_ranks); return largest; } STATIC void* _shm_create(const char *name, #if ENABLE_SYSV key_t *key, #endif size_t size) { #if ENABLE_SYSV FILE *fp; int shm_id; char file[SHM_NAME_SIZE+10]; char ebuf[128]; void *mapped = NULL; char token = (char)(token_counter%256); token_counter++; if (use_dev_shm) { sprintf(file,"/dev/shm/%s\0",name); } else { sprintf(file,"/tmp/%s\0",name); } fp = fopen(file,"w"); fprintf(fp,"0\n"); fclose(fp); *key = ftok(file,token); /* printf("p[%d] CREATE SHM name: %s key: %d id: %d\n",g_state.rank,name,*key,(int)token); */ sprintf(ebuf,"p[%d] (shmget in _shm_create) flags: IPC_CREAT|0600, key: %d, name: %s id: %d\n", g_state.rank,*key,name,(int)token); shm_id = shmget(*key,size,IPC_CREAT|0600); _shmget_err(shm_id, ebuf); if (shm_id == -1) { comex_error("_shm_create: shmget failed", shm_id); } mapped = shmat(shm_id, NULL, 0); /* printf("p[%d] ATTACH SHM name: %s key: %d\n",g_state.rank,name,*key); */ _shmat_err(mapped); return mapped; #else void *mapped = NULL; int fd = 0; int retval = 0; #if DEBUG printf("[%d] _shm_create(%s, %lu)\n", g_state.rank, name, (unsigned long)size); #endif /* create shared memory segment */ fd = shm_open(name, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); if (-1 == fd && EEXIST == errno) { retval = shm_unlink(name); if (-1 == retval) { perror("_shm_create: shm_unlink"); comex_error("_shm_create: shm_unlink", retval); } } /* try a second time */ if (-1 == fd) { fd = shm_open(name, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); } /* finally report error if needed */ if (-1 == fd) { perror("_shm_create: shm_open"); comex_error("_shm_create: shm_open", fd); } /* set the size of my shared memory object */ retval = ftruncate(fd, size); if (-1 == retval) { perror("_shm_create: ftruncate"); comex_error("_shm_create: ftruncate", retval); } /* map into local address space */ mapped = _shm_map(fd, size); /* close file descriptor */ retval = close(fd); if (-1 == retval) { perror("_shm_create: close"); comex_error("_shm_create: close", -1); } return mapped; #endif } #if ENABLE_SYSV STATIC void* _shm_attach(const char *name, size_t size, key_t key) { int shm_id; void *mapped = NULL; char ebuf[128]; sprintf(ebuf,"p[%d] (shmget in shm_attach) flags: 0600, key: %d, name: %s\n", g_state.rank,key,name); shm_id = shmget(key,size,0600); _shmget_err(shm_id, ebuf); if (shm_id == -1) { comex_error("_shm_attach: shmget failed", shm_id); } mapped = shmat(shm_id, NULL, 0); /* printf("p[%d] ATTACH SHM name: %s key: %d\n",g_state.rank,name,key); */ _shmat_err(mapped); return mapped; } #else STATIC void* _shm_attach(const char *name, size_t size) { void *mapped = NULL; int fd = 0; int retval = 0; #if DEBUG printf("[%d] _shm_attach(%s, %lu)\n", g_state.rank, name, (unsigned long)size); #endif /* attach to shared memory segment */ fd = shm_open(name, O_RDWR, S_IRUSR|S_IWUSR); if (-1 == fd) { perror("_shm_attach: shm_open"); comex_error("_shm_attach: shm_open", -1); } /* map into local address space */ mapped = _shm_map(fd, size); /* close file descriptor */ retval = close(fd); if (-1 == retval) { perror("_shm_attach: close"); comex_error("_shm_attach: close", -1); } return mapped; } #endif STATIC void* _shm_map(int fd, size_t size) { void *memory = NULL; memory = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (MAP_FAILED == memory) { perror("_shm_map: mmap"); comex_error("_shm_map: mmap", -1); } return memory; } STATIC int _set_affinity(int cpu) { int status = 0; #if COMEX_SET_AFFINITY #if HAVE_PTHREAD_SETAFFINITY_NP || HAVE_SCHED_SETAFFINITY cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); #if HAVE_PTHREAD_SETAFFINITY_NP status = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); if (0 != status) { perror("pthread_setaffinity_np"); } #elif HAVE_SCHED_SETAFFINITY status = sched_setaffinity(getpid(), sizeof(cpu_set_t), &cpuset); if (0 != status) { perror("sched_setaffinity"); } #endif #endif #endif return status; } STATIC void check_mpi_retval(int retval, const char *file, int line) { if (MPI_SUCCESS != retval) { const char *msg = str_mpi_retval(retval); fprintf(stderr, "{%d} MPI Error: %s: line %d: %s\n", g_state.rank, file, line, msg); MPI_Abort(g_state.comm, retval); } } STATIC const char *str_mpi_retval(int retval) { const char *msg = NULL; if (retval == MPI_SUCCESS ) { msg = "MPI_SUCCESS"; } else if (retval == MPI_ERR_BUFFER ) { msg = "MPI_ERR_BUFFER"; } else if (retval == MPI_ERR_COUNT ) { msg = "MPI_ERR_COUNT"; } else if (retval == MPI_ERR_TYPE ) { msg = "MPI_ERR_TYPE"; } else if (retval == MPI_ERR_TAG ) { msg = "MPI_ERR_TAG"; } else if (retval == MPI_ERR_COMM ) { msg = "MPI_ERR_COMM"; } else if (retval == MPI_ERR_RANK ) { msg = "MPI_ERR_RANK"; } else if (retval == MPI_ERR_ROOT ) { msg = "MPI_ERR_ROOT"; } else if (retval == MPI_ERR_GROUP ) { msg = "MPI_ERR_GROUP"; } else if (retval == MPI_ERR_OP ) { msg = "MPI_ERR_OP"; } else if (retval == MPI_ERR_TOPOLOGY ) { msg = "MPI_ERR_TOPOLOGY"; } else if (retval == MPI_ERR_DIMS ) { msg = "MPI_ERR_DIMS"; } else if (retval == MPI_ERR_ARG ) { msg = "MPI_ERR_ARG"; } else if (retval == MPI_ERR_UNKNOWN ) { msg = "MPI_ERR_UNKNOWN"; } else if (retval == MPI_ERR_TRUNCATE ) { msg = "MPI_ERR_TRUNCATE"; } else if (retval == MPI_ERR_OTHER ) { msg = "MPI_ERR_OTHER"; } else if (retval == MPI_ERR_INTERN ) { msg = "MPI_ERR_INTERN"; } else if (retval == MPI_ERR_IN_STATUS) { msg = "MPI_ERR_IN_STATUS"; } else if (retval == MPI_ERR_PENDING ) { msg = "MPI_ERR_PENDING"; } else if (retval == MPI_ERR_REQUEST ) { msg = "MPI_ERR_REQUEST"; } else if (retval == MPI_ERR_LASTCODE ) { msg = "MPI_ERR_LASTCODE"; } else { msg = "DEFAULT"; } return msg; } STATIC void server_send(void *buf, int count, int dest) { int retval = 0; #if DEBUG printf("[%d] server_send(buf=%p, count=%d, dest=%d)\n", g_state.rank, buf, count, dest); #endif retval = MPI_Send(buf, count, MPI_CHAR, dest, COMEX_TAG, group_list->comm); CHECK_MPI_RETVAL(retval); } STATIC void server_recv(void *buf, int count, int source) { int retval = 0; MPI_Status status; int recv_count = 0; retval = MPI_Recv(buf, count, MPI_CHAR, source, COMEX_TAG, g_state.comm, &status); CHECK_MPI_RETVAL(retval); COMEX_ASSERT(status.MPI_SOURCE == source); COMEX_ASSERT(status.MPI_TAG == COMEX_TAG); retval = MPI_Get_count(&status, MPI_CHAR, &recv_count); CHECK_MPI_RETVAL(retval); COMEX_ASSERT(recv_count == count); } STATIC void nb_send_common(void *buf, int count, int dest, nb_t *nb, int need_free) { int retval = 0; message_t *message = NULL; COMEX_ASSERT(NULL != nb); nb->send_size += 1; nb_count_event += 1; nb_count_send += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = need_free; message->stride = NULL; message->iov = NULL; if (NULL == nb->send_head) { nb->send_head = message; } if (NULL != nb->send_tail) { nb->send_tail->next = message; } nb->send_tail = message; retval = MPI_Isend(buf, count, MPI_CHAR, dest, COMEX_TAG, g_state.comm, &(message->request)); CHECK_MPI_RETVAL(retval); } STATIC void nb_send_header(void *buf, int count, int dest, nb_t *nb) { nb_send_common(buf, count, dest, nb, 1); } STATIC void nb_send_buffer(void *buf, int count, int dest, nb_t *nb) { nb_send_common(buf, count, dest, nb, 0); } STATIC void nb_recv_packed(void *buf, int count, int source, nb_t *nb, stride_t *stride) { int retval = 0; message_t *message = NULL; COMEX_ASSERT(NULL != buf); COMEX_ASSERT(count > 0); COMEX_ASSERT(NULL != nb); #if DEBUG printf("[%d] nb_recv_packed(buf=%p, count=%d, source=%d, nb=%p)\n", g_state.rank, buf, count, source, nb); #endif nb->recv_size += 1; nb_count_event += 1; nb_count_recv += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = 1; message->stride = stride; message->iov = NULL; if (NULL == nb->recv_head) { nb->recv_head = message; } if (NULL != nb->recv_tail) { nb->recv_tail->next = message; } nb->recv_tail = message; retval = MPI_Irecv(buf, count, MPI_CHAR, source, COMEX_TAG, group_list->comm, &(message->request)); CHECK_MPI_RETVAL(retval); } STATIC void nb_recv_iov(void *buf, int count, int source, nb_t *nb, comex_giov_t *iov) { int retval = 0; message_t *message = NULL; COMEX_ASSERT(NULL != nb); #if DEBUG printf("[%d] nb_recv_iov(buf=%p, count=%d, source=%d, nb=%p)\n", g_state.rank, buf, count, source, nb); #endif nb->recv_size += 1; nb_count_event += 1; nb_count_recv += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = buf; message->need_free = 1; message->stride = NULL; message->iov = iov; if (NULL == nb->recv_head) { nb->recv_head = message; COMEX_ASSERT(NULL == nb->recv_tail); } if (NULL != nb->recv_tail) { nb->recv_tail->next = message; } nb->recv_tail = message; retval = MPI_Irecv(buf, count, MPI_CHAR, source, COMEX_TAG, group_list->comm, &(message->request)); CHECK_MPI_RETVAL(retval); } STATIC void nb_recv(void *buf, int count, int source, nb_t *nb) { int retval = 0; message_t *message = NULL; COMEX_ASSERT(NULL != nb); #if DEBUG printf("[%d] nb_recv(buf=%p, count=%d, source=%d, nb=%p)\n", g_state.rank, buf, count, source, nb); #endif nb->recv_size += 1; nb_count_event += 1; nb_count_recv += 1; message = (message_t*)malloc(sizeof(message_t)); message->next = NULL; message->message = NULL; message->need_free = 0; message->stride = NULL; message->iov = NULL; if (NULL == nb->recv_head) { nb->recv_head = message; } if (NULL != nb->recv_tail) { nb->recv_tail->next = message; } nb->recv_tail = message; retval = MPI_Irecv(buf, count, MPI_CHAR, source, COMEX_TAG, group_list->comm, &(message->request)); CHECK_MPI_RETVAL(retval); } STATIC int nb_get_handle_index() { int value = 0; if (0 == nb_index) { value = nb_max_outstanding-1; } else { value = nb_index-1; } return value; } STATIC nb_t* nb_wait_for_handle() { nb_t *nb = NULL; int in_use_count = 0; /* find first handle that isn't associated with a user-level handle */ /* make sure the handle we find has processed all events */ /* the user can accidentally exhaust the available handles */ do { ++in_use_count; if (in_use_count > nb_max_outstanding) { fprintf(stderr, "{%d} nb_wait_for_handle Error: all user-level " "nonblocking handles have been exhausted\n", g_state.rank); MPI_Abort(g_state.comm, -1); } nb = &nb_state[nb_index++]; nb_index %= nb_max_outstanding; /* wrap around if needed */ nb_wait_for_all(nb); } while (nb->in_use); return nb; } STATIC void nb_wait_for_send(nb_t *nb) { #if DEBUG printf("[%d] nb_wait_for_send(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); while (NULL != nb->send_head) { nb_wait_for_send1(nb); } nb->send_tail = NULL; } STATIC void nb_wait_for_send1(nb_t *nb) { #if DEBUG printf("[%d] nb_wait_for_send1(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); COMEX_ASSERT(NULL != nb->send_head); { MPI_Status status; int retval = 0; message_t *message_to_free = NULL; retval = MPI_Wait(&(nb->send_head->request), &status); CHECK_MPI_RETVAL(retval); if (nb->send_head->need_free) { free(nb->send_head->message); } message_to_free = nb->send_head; nb->send_head = nb->send_head->next; free(message_to_free); COMEX_ASSERT(nb->send_size > 0); nb->send_size -= 1; nb_count_send_processed += 1; nb_count_event_processed += 1; if (NULL == nb->send_head) { nb->send_tail = NULL; } } } STATIC void nb_wait_for_recv(nb_t *nb) { #if DEBUG printf("[%d] nb_wait_for_recv(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); while (NULL != nb->recv_head) { nb_wait_for_recv1(nb); } } STATIC void nb_wait_for_recv1(nb_t *nb) { #if DEBUG printf("[%d] nb_wait_for_recv1(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); COMEX_ASSERT(NULL != nb->recv_head); { MPI_Status status; int retval = 0; message_t *message_to_free = NULL; retval = MPI_Wait(&(nb->recv_head->request), &status); CHECK_MPI_RETVAL(retval); if (NULL != nb->recv_head->stride) { stride_t *stride = nb->recv_head->stride; COMEX_ASSERT(nb->recv_head->message); COMEX_ASSERT(stride); COMEX_ASSERT(stride->ptr); COMEX_ASSERT(stride->stride); COMEX_ASSERT(stride->count); COMEX_ASSERT(stride->stride_levels); unpack(nb->recv_head->message, stride->ptr, stride->stride, stride->count, stride->stride_levels); free(stride); } if (NULL != nb->recv_head->iov) { int i = 0; char *message = nb->recv_head->message; int off = 0; comex_giov_t *iov = nb->recv_head->iov; for (i=0; icount; ++i) { (void)memcpy(iov->dst[i], &message[off], iov->bytes); off += iov->bytes; } free(iov->src); free(iov->dst); free(iov); } if (nb->recv_head->need_free) { free(nb->recv_head->message); } message_to_free = nb->recv_head; nb->recv_head = nb->recv_head->next; free(message_to_free); COMEX_ASSERT(nb->recv_size > 0); nb->recv_size -= 1; nb_count_recv_processed += 1; nb_count_event_processed += 1; if (NULL == nb->recv_head) { nb->recv_tail = NULL; } } } STATIC void nb_wait_for_all(nb_t *nb) { #if DEBUG printf("[%d] nb_wait_for_all(nb=%p)\n", g_state.rank, nb); #endif COMEX_ASSERT(NULL != nb); /* fair processing of requests */ while (NULL != nb->send_head || NULL != nb->recv_head) { if (NULL != nb->send_head) { nb_wait_for_send1(nb); } if (NULL != nb->recv_head) { nb_wait_for_recv1(nb); } } } STATIC void nb_wait_all() { int i = 0; COMEX_ASSERT(nb_count_event-nb_count_event_processed >= 0); for (i=0; i 0); COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != nb); #if ENABLE_PUT_SELF /* put to self */ if (g_state.rank == proc) { memcpy(dst, src, bytes); return; } #endif #if ENABLE_PUT_SMP /* put to SMP node */ if (g_state.hostid[proc] == g_state.hostid[g_state.rank]) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; reg_entry = reg_cache_find(proc, dst, bytes); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, dst); memcpy(mapped_offset, src, bytes); return; } #endif { header_t *header = NULL; int master_rank = -1; master_rank = g_state.master[proc]; /* only fence on the master */ fence_array[master_rank] = 1; header = malloc(sizeof(header_t)); header->operation = OP_PUT; header->remote_address = dst; header->local_address = src; header->rank = proc; header->length = bytes; nb_send_header(header, sizeof(header_t), master_rank, nb); nb_send_buffer(src, bytes, master_rank, nb); } } STATIC void nb_get(void *src, void *dst, int bytes, int proc, nb_t *nb) { COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(bytes > 0); COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != nb); #if ENABLE_GET_SELF /* get from self */ if (g_state.rank == proc) { memcpy(dst, src, bytes); return; } #endif #if ENABLE_GET_SMP /* get from SMP node */ if (g_state.hostid[proc] == g_state.hostid[g_state.rank]) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; reg_entry = reg_cache_find(proc, src, bytes); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, src); memcpy(dst, mapped_offset, bytes); return; } #endif { header_t *header = NULL; int master_rank = -1; master_rank = g_state.master[proc]; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = OP_GET; header->remote_address = src; header->local_address = dst; header->rank = proc; header->length = bytes; nb_recv(dst, bytes, master_rank, nb); /* prepost receive */ nb_send_header(header, sizeof(header_t), master_rank, nb); } } STATIC void nb_acc(int datatype, void *scale, void *src, void *dst, int bytes, int proc, nb_t *nb) { COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(bytes > 0); COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != nb); #if ENABLE_ACC_SELF /* acc to self */ if (g_state.rank == proc) { sem_wait(semaphores[proc]); _acc(datatype, bytes, dst, src, scale); sem_post(semaphores[proc]); return; } #endif #if ENABLE_ACC_SMP /* acc to same SMP node */ if (g_state.hostid[proc] == g_state.hostid[g_state.rank]) { reg_entry_t *reg_entry = NULL; void *mapped_offset = NULL; reg_entry = reg_cache_find(proc, dst, bytes); COMEX_ASSERT(reg_entry); mapped_offset = _get_offset_memory(reg_entry, dst); sem_wait(semaphores[proc]); _acc(datatype, bytes, mapped_offset, src, scale); sem_post(semaphores[proc]); return; } #endif { header_t *header = NULL; char *message = NULL; int master_rank = -1; int message_size = 0; int scale_size = 0; op_t operation = 0; switch (datatype) { case COMEX_ACC_INT: operation = OP_ACC_INT; scale_size = sizeof(int); break; case COMEX_ACC_DBL: operation = OP_ACC_DBL; scale_size = sizeof(double); break; case COMEX_ACC_FLT: operation = OP_ACC_FLT; scale_size = sizeof(float); break; case COMEX_ACC_CPL: operation = OP_ACC_CPL; scale_size = sizeof(SingleComplex); break; case COMEX_ACC_DCP: operation = OP_ACC_DCP; scale_size = sizeof(DoubleComplex); break; case COMEX_ACC_LNG: operation = OP_ACC_LNG; scale_size = sizeof(long); break; default: COMEX_ASSERT(0); } master_rank = g_state.master[proc]; /* only fence on the master */ fence_array[master_rank] = 1; message_size = sizeof(header_t) + scale_size; message = malloc(message_size); COMEX_ASSERT(message); header = (header_t*)message; header->operation = operation; header->remote_address = dst; header->local_address = src; header->rank = proc; header->length = bytes; memcpy(message+sizeof(header_t), scale, scale_size); nb_send_header(message, message_size, master_rank, nb); nb_send_buffer(src, bytes, master_rank, nb); } } STATIC void nb_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; #if DEBUG printf("[%d] nb_puts(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif /* if not actually a strided put */ if (0 == stride_levels) { nb_put(src, dst, count[0], proc, nb); return; } #if ENABLE_PUT_PACKED #if ENABLE_PUT_SELF /* if not a strided put to self, use packed algorithm */ if (g_state.rank != proc) #endif { nb_puts_packed(src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } /* index mangling */ for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_put((char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } } STATIC void nb_puts_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i, j; long src_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int packed_index = 0; char *packed_buffer = NULL; stride_t *stride = NULL; #if DEBUG printf("[%d] nb_puts_packed(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != count); COMEX_ASSERT(NULL != nb); COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); /* copy dst info into structure */ stride = malloc(sizeof(stride_t)); COMEX_ASSERT(stride); stride->stride_levels = stride_levels; stride->count[0] = count[0]; for (i=0; istride[i] = dst_stride[i]; stride->count[i+1] = count[i+1]; } for (/*no init*/; istride[i] = -1; stride->count[i+1] = -1; } COMEX_ASSERT(stride->stride_levels >= 0); COMEX_ASSERT(stride->stride_levels < COMEX_MAX_STRIDE_LEVEL); #if DEBUG printf("[%d] nb_puts_packed stride_levels=%d, count[0]=%d\n", g_state.rank, stride_levels, count[0]); for (i=0; istride[i], i, stride->count[i+1]); } #endif packed_buffer = pack(src, src_stride, count, stride_levels, &packed_index); COMEX_ASSERT(NULL != packed_buffer); COMEX_ASSERT(packed_index > 0); { header_t *header = NULL; int master_rank = -1; master_rank = g_state.master[proc]; /* only fence on the master */ fence_array[master_rank] = 1; header = malloc(sizeof(header_t)); header->operation = OP_PUT_PACKED; header->remote_address = dst; header->local_address = NULL; header->rank = proc; header->length = packed_index; nb_send_header(header, sizeof(header_t), master_rank, nb); nb_send_header(stride, sizeof(stride_t), master_rank, nb); nb_send_header(packed_buffer, packed_index, master_rank, nb); } } STATIC void nb_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; /* if not actually a strided get */ if (0 == stride_levels) { nb_get(src, dst, count[0], proc, nb); return; } #if ENABLE_GET_PACKED #if ENABLE_GET_SELF /* if not a strided get from self, use packed algorithm */ if (g_state.rank != proc) #endif { nb_gets_packed(src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } dst_idx = 0; for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_get((char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } } STATIC void nb_gets_packed( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i; stride_t *stride_src = NULL; stride_t *stride_dst = NULL; #if DEBUG printf("[%d] nb_gets_packed(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != count); COMEX_ASSERT(NULL != nb); COMEX_ASSERT(count[0] > 0); COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); /* copy src info into structure */ stride_src = malloc(sizeof(stride_t)); COMEX_ASSERT(stride_src); stride_src->ptr = src; stride_src->stride_levels = stride_levels; stride_src->count[0] = count[0]; for (i=0; istride[i] = src_stride[i]; stride_src->count[i+1] = count[i+1]; } for (/*no init*/; istride[i] = -1; stride_src->count[i+1] = -1; } COMEX_ASSERT(stride_src->stride_levels >= 0); COMEX_ASSERT(stride_src->stride_levels < COMEX_MAX_STRIDE_LEVEL); /* copy dst info into structure */ stride_dst = malloc(sizeof(stride_t)); COMEX_ASSERT(stride_dst); stride_dst->ptr = dst; stride_dst->stride_levels = stride_levels; stride_dst->count[0] = count[0]; for (i=0; istride[i] = dst_stride[i]; stride_dst->count[i+1] = count[i+1]; } for (/*no init*/; istride[i] = -1; stride_dst->count[i+1] = -1; } COMEX_ASSERT(stride_dst->stride_levels >= 0); COMEX_ASSERT(stride_dst->stride_levels < COMEX_MAX_STRIDE_LEVEL); { int recv_size = 0; char *packed_buffer = NULL; header_t *header = NULL; int master_rank = -1; master_rank = g_state.master[proc]; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = OP_GET_PACKED; header->remote_address = src; header->local_address = dst; header->rank = proc; header->length = 0; recv_size = packed_size(stride_dst->stride, stride_dst->count, stride_dst->stride_levels); COMEX_ASSERT(recv_size > 0); packed_buffer = malloc(recv_size); COMEX_ASSERT(packed_buffer); nb_recv_packed(packed_buffer, recv_size, master_rank, nb, stride_dst); nb_send_header(header, sizeof(header_t), master_rank, nb); nb_send_header(stride_src, sizeof(stride_t), master_rank, nb); } } STATIC void nb_accs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; /* if not actually a strided acc */ if (0 == stride_levels) { nb_acc(datatype, scale, src, dst, count[0], proc, nb); return; } #if ENABLE_ACC_PACKED #if ENABLE_ACC_SELF /* if not a strided acc to self, use packed algorithm */ if (g_state.rank != proc) #endif { nb_accs_packed(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, proc, nb); return; } #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } /* index mangling */ for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_acc(datatype, scale, (char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } } STATIC void nb_accs_packed( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i; int packed_index = 0; char *packed_buffer = NULL; stride_t *stride = NULL; #if DEBUG printf("[%d] nb_accs_packed(src=%p, src_stride=%p, dst=%p, dst_stride=%p, count[0]=%d, stride_levels=%d, proc=%d, nb=%p)\n", g_state.rank, src, src_stride, dst, dst_stride, count[0], stride_levels, proc, nb); #endif COMEX_ASSERT(proc >= 0); COMEX_ASSERT(proc < g_state.size); COMEX_ASSERT(NULL != scale); COMEX_ASSERT(NULL != src); COMEX_ASSERT(NULL != dst); COMEX_ASSERT(NULL != count); COMEX_ASSERT(NULL != nb); COMEX_ASSERT(count[0] > 0); COMEX_ASSERT(stride_levels >= 0); COMEX_ASSERT(stride_levels < COMEX_MAX_STRIDE_LEVEL); /* copy dst info into structure */ stride = malloc(sizeof(stride_t)); COMEX_ASSERT(stride); stride->ptr = dst; stride->stride_levels = stride_levels; stride->count[0] = count[0]; for (i=0; istride[i] = dst_stride[i]; stride->count[i+1] = count[i+1]; } /* assign remaining values to invalid */ for (/*no init*/; istride[i] = -1; stride->count[i+1] = -1; } COMEX_ASSERT(stride->stride_levels >= 0); COMEX_ASSERT(stride->stride_levels < COMEX_MAX_STRIDE_LEVEL); #if DEBUG printf("[%d] nb_accs_packed stride_levels=%d, count[0]=%d\n", g_state.rank, stride_levels, count[0]); for (i=0; istride[i], i, stride->count[i+1]); } #endif packed_buffer = pack(src, src_stride, count, stride_levels, &packed_index); COMEX_ASSERT(NULL != packed_buffer); COMEX_ASSERT(packed_index > 0); { header_t *header = NULL; int scale_size = 0; op_t operation = 0; int master_rank = -1; switch (datatype) { case COMEX_ACC_INT: operation = OP_ACC_INT_PACKED; scale_size = sizeof(int); break; case COMEX_ACC_DBL: operation = OP_ACC_DBL_PACKED; scale_size = sizeof(double); break; case COMEX_ACC_FLT: operation = OP_ACC_FLT_PACKED; scale_size = sizeof(float); break; case COMEX_ACC_CPL: operation = OP_ACC_CPL_PACKED; scale_size = sizeof(SingleComplex); break; case COMEX_ACC_DCP: operation = OP_ACC_DCP_PACKED; scale_size = sizeof(DoubleComplex); break; case COMEX_ACC_LNG: operation = OP_ACC_LNG_PACKED; scale_size = sizeof(long); break; default: COMEX_ASSERT(0); } master_rank = g_state.master[proc]; /* only fence on the master */ fence_array[master_rank] = 1; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = operation; header->remote_address = dst; header->local_address = NULL; header->rank = proc; header->length = packed_index; nb_send_header(header, sizeof(header_t), master_rank, nb); nb_send_buffer(scale, scale_size, master_rank, nb); nb_send_header(stride, sizeof(stride_t), master_rank, nb); nb_send_header(packed_buffer, packed_index, master_rank, nb); } } STATIC void nb_putv( comex_giov_t *iov, int iov_len, int proc, nb_t *nb) { int i = 0; for (i=0; isrc; dst = iov->dst; bytes = iov->bytes; limit = iov->count; #if DEBUG printf("[%d] nb_putv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif /* allocate compressed iov */ iov_size = 2*limit*sizeof(void*) + 2*sizeof(int); iov_buf = malloc(iov_size); COMEX_ASSERT(iov_buf); iov_off = 0; /* copy limit */ (void)memcpy(&iov_buf[iov_off], &limit, sizeof(int)); iov_off += sizeof(int); /* copy bytes */ (void)memcpy(&iov_buf[iov_off], &bytes, sizeof(int)); iov_off += sizeof(int); /* copy src pointers */ (void)memcpy(&iov_buf[iov_off], src, limit*sizeof(void*)); iov_off += limit*sizeof(void*); /* copy dst pointers */ (void)memcpy(&iov_buf[iov_off], dst, limit*sizeof(void*)); iov_off += limit*sizeof(void*); COMEX_ASSERT(iov_off == iov_size); /* allocate send buffer */ packed_size = bytes * limit; packed_buffer = malloc(packed_size); COMEX_ASSERT(packed_buffer); packed_index = 0; for (i=0; ioperation = OP_PUT_IOV; header->remote_address = NULL; header->local_address = NULL; header->rank = proc; header->length = iov_size; nb_send_header(header, sizeof(header_t), master_rank, nb); nb_send_header(iov_buf, iov_size, master_rank, nb); nb_send_header(packed_buffer, packed_size, master_rank, nb); } } STATIC void nb_getv( comex_giov_t *iov, int iov_len, int proc, nb_t *nb) { int i = 0; for (i=0; isrc; dst = iov->dst; bytes = iov->bytes; limit = iov->count; #if DEBUG printf("[%d] nb_getv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif /* allocate compressed iov */ iov_size = 2*limit*sizeof(void*) + 2*sizeof(int); iov_buf = malloc(iov_size); iov_off = 0; COMEX_ASSERT(iov_buf); /* copy limit */ (void)memcpy(&iov_buf[iov_off], &limit, sizeof(int)); iov_off += sizeof(int); /* copy bytes */ (void)memcpy(&iov_buf[iov_off], &bytes, sizeof(int)); iov_off += sizeof(int); /* copy src pointers */ (void)memcpy(&iov_buf[iov_off], src, limit*sizeof(void*)); iov_off += limit*sizeof(void*); /* copy dst pointers */ (void)memcpy(&iov_buf[iov_off], dst, limit*sizeof(void*)); iov_off += limit*sizeof(void*); COMEX_ASSERT(iov_off == iov_size); /* copy given iov for later */ iov_copy = malloc(sizeof(comex_giov_t)); iov_copy->bytes = bytes; iov_copy->count = limit; iov_copy->src = malloc(sizeof(void*)*iov->count); COMEX_ASSERT(iov_copy->src); (void)memcpy(iov_copy->src, iov->src, sizeof(void*)*iov->count); iov_copy->dst = malloc(sizeof(void*)*iov->count); COMEX_ASSERT(iov_copy->dst); (void)memcpy(iov_copy->dst, iov->dst, sizeof(void*)*iov->count); #if DEBUG printf("[%d] nb_getv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p copy\n", g_state.rank, iov_copy->count, iov_copy->bytes, iov_copy->src[0], iov_copy->dst[0]); #endif /* allocate recv buffer */ packed_size = bytes * limit; packed_buffer = malloc(packed_size); COMEX_ASSERT(packed_buffer); { header_t *header = NULL; int master_rank = g_state.master[proc]; header = malloc(sizeof(header_t)); COMEX_ASSERT(header); header->operation = OP_GET_IOV; header->remote_address = NULL; header->local_address = NULL; header->rank = proc; header->length = iov_size; nb_recv_iov(packed_buffer, packed_size, master_rank, nb, iov_copy); nb_send_header(header, sizeof(header_t), master_rank, nb); nb_send_header(iov_buf, iov_size, master_rank, nb); } } STATIC void nb_accv( int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, nb_t *nb) { int i = 0; for (i=0; isrc; dst = iov->dst; bytes = iov->bytes; limit = iov->count; #if DEBUG printf("[%d] nb_accv_packed limit=%d bytes=%d src[0]=%p dst[0]=%p\n", g_state.rank, limit, bytes, src[0], dst[0]); #endif /* allocate compressed iov */ iov_size = 2*limit*sizeof(void*) + 2*sizeof(int); iov_buf = malloc(iov_size); iov_off = 0; COMEX_ASSERT(iov_buf); /* copy limit */ (void)memcpy(&iov_buf[iov_off], &limit, sizeof(int)); iov_off += sizeof(int); /* copy bytes */ (void)memcpy(&iov_buf[iov_off], &bytes, sizeof(int)); iov_off += sizeof(int); /* copy src pointers */ (void)memcpy(&iov_buf[iov_off], src, limit*sizeof(void*)); iov_off += limit*sizeof(void*); /* copy dst pointers */ (void)memcpy(&iov_buf[iov_off], dst, limit*sizeof(void*)); iov_off += limit*sizeof(void*); COMEX_ASSERT(iov_off == iov_size); /* allocate send buffer */ packed_size = bytes * limit; packed_buffer = malloc(packed_size); COMEX_ASSERT(packed_buffer); packed_index = 0; for (i=0; ioperation = operation; header->remote_address = NULL; header->local_address = NULL; header->rank = proc; header->length = iov_size; nb_send_header(header, sizeof(header_t), master_rank, nb); nb_send_buffer(scale, scale_size, master_rank, nb); nb_send_header(iov_buf, iov_size, master_rank, nb); nb_send_header(packed_buffer, packed_size, master_rank, nb); } } ga-5.9.2/comex/src-mpi-pt/comex_impl.h000066400000000000000000000034761500715745200175510ustar00rootroot00000000000000#ifndef COMEX_IMPL_H_ #define COMEX_IMPL_H_ #include #include #include "groups.h" #define COMEX_MAX_NB_OUTSTANDING 256 #define COMEX_MAX_STRIDE_LEVEL 8 #define COMEX_TAG 27624 #define COMEX_STATIC_BUFFER_SIZE (2u*1048576u) #define SHM_NAME_SIZE 20 #define UNLOCKED -1 /* performance or correctness related settings */ #if 0 #define ENABLE_UNNAMED_SEM 1 #else #define ENABLE_UNNAMED_SEM 0 #endif #define NEED_ASM_VOLATILE_MEMORY 0 #define MASTER_IS_SMALLEST_SMP_RANK 0 #define COMEX_SET_AFFINITY 0 #define ENABLE_PUT_SELF 1 #define ENABLE_GET_SELF 1 #define ENABLE_ACC_SELF 1 #define ENABLE_PUT_SMP 1 #define ENABLE_GET_SMP 1 #define ENABLE_ACC_SMP 1 #define ENABLE_PUT_PACKED 1 #define ENABLE_GET_PACKED 1 #define ENABLE_ACC_PACKED 1 #define ENABLE_PUT_IOV 1 #define ENABLE_GET_IOV 1 #define ENABLE_ACC_IOV 1 #define DEBUG 0 #define DEBUG_VERBOSE 0 #define DEBUG_TO_FILE 0 #if DEBUG_TO_FILE FILE *comex_trace_file; # define printf(...) fprintf(comex_trace_file, __VA_ARGS__); fflush(comex_trace_file) #else # define printf(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) #endif #define COMEX_STRINGIFY(x) #x #ifdef NDEBUG #define COMEX_ASSERT(WHAT) ((void) (0)) #else #define COMEX_ASSERT(WHAT) \ ((WHAT) \ ? (void) (0) \ : comex_assert_fail (COMEX_STRINGIFY(WHAT), __FILE__, __LINE__, __func__)) #endif static inline void comex_assert_fail( const char *assertion, const char *file, unsigned int line, const char *function) { fprintf(stderr, "[%d] %s:%u: %s: Assertion `%s' failed", g_state.rank, file, line, function, assertion); fflush(stderr); #if DEBUG printf("[%d] %s:%u: %s: Assertion `%s' failed", g_state.rank, file, line, function, assertion); #endif comex_error("comex_assert_fail", -1); } #endif /* COMEX_IMPL_H_ */ ga-5.9.2/comex/src-mpi-pt/groups.c000066400000000000000000000354171500715745200167270ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #if defined(__CRAYXE) # include #endif #include "comex.h" #include "comex_impl.h" #include "groups.h" /* world group state */ comex_group_world_t g_state = { MPI_COMM_NULL, MPI_GROUP_NULL, -1, -1, NULL, NULL }; /* the HEAD of the group linked list */ comex_igroup_t *group_list = NULL; #define RANK_OR_PID (g_state.rank >= 0 ? g_state.rank : getpid()) /* static functions implemented in this file */ static void _create_group_and_igroup(comex_group_t *id, comex_igroup_t **igroup); static void _igroup_free(comex_igroup_t *igroup); static long xgethostid(); /** * Return the comex igroup instance given the group id. * * The group linked list is searched sequentially until the given group * is found. It is an error if this function is called before * comex_group_init(). An error occurs if the given group is not found. */ comex_igroup_t* comex_get_igroup_from_group(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; #if DEBUG printf("[%d] comex_get_igroup_from_group(%d)\n", RANK_OR_PID, id); #endif COMEX_ASSERT(group_list != NULL); while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { return current_group_list_item; } current_group_list_item = current_group_list_item->next; } comex_error("comex group lookup failed", -1); return NULL; } /** * Creates and associates a comex group with a comex igroup. * * This does *not* initialize the members of the comex igroup. */ static void _create_group_and_igroup( comex_group_t *id, comex_igroup_t **igroup) { comex_igroup_t *new_group_list_item = NULL; comex_igroup_t *last_group_list_item = NULL; #if DEBUG printf("[%d] _create_group_and_igroup(...)\n", RANK_OR_PID); #endif /* create, init, and insert the new node for the linked list */ new_group_list_item = malloc(sizeof(comex_igroup_t)); new_group_list_item->next = NULL; new_group_list_item->id = -1; new_group_list_item->comm = MPI_COMM_NULL; new_group_list_item->group = MPI_GROUP_NULL; new_group_list_item->size = -1; new_group_list_item->rank = -1; /* find the last group in the group linked list and insert */ if (group_list) { last_group_list_item = group_list; while (last_group_list_item->next != NULL) { last_group_list_item = last_group_list_item->next; } last_group_list_item->next = new_group_list_item; new_group_list_item->id = last_group_list_item->id + 1; } else { group_list = new_group_list_item; new_group_list_item->id = COMEX_GROUP_WORLD; } /* return the group id and comex igroup */ *igroup = new_group_list_item; *id = new_group_list_item->id; } int comex_group_rank(comex_group_t group, int *rank) { comex_igroup_t *igroup = comex_get_igroup_from_group(group); *rank = igroup->rank; #if DEBUG printf("[%d] comex_group_rank(group=%d, *rank=%d)\n", RANK_OR_PID, group, *rank); #endif return COMEX_SUCCESS; } int comex_group_size(comex_group_t group, int *size) { comex_igroup_t *igroup = comex_get_igroup_from_group(group); *size = igroup->size; #if DEBUG printf("[%d] comex_group_size(group=%d, *size=%d)\n", RANK_OR_PID, group, *size); #endif return COMEX_SUCCESS; } int comex_group_comm(comex_group_t group, MPI_Comm *comm) { comex_igroup_t *igroup = comex_get_igroup_from_group(group); *comm = igroup->comm; #if DEBUG printf("[%d] comex_group_comm(group=%d, comm)\n", RANK_OR_PID, group); #endif return COMEX_SUCCESS; } int comex_group_translate_world( comex_group_t group, int group_rank, int *world_rank) { #if DEBUG printf("[%d] comex_group_translate_world(" "group=%d, group_rank=%d, world_rank)\n", RANK_OR_PID, group, group_rank); #endif if (COMEX_GROUP_WORLD == group) { *world_rank = group_rank; } else { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); COMEX_ASSERT(group_list); /* first group is world worker group */ status = MPI_Group_translate_ranks(igroup->group, 1, &group_rank, group_list->group, world_rank); } return COMEX_SUCCESS; } /** * Destroys the given comex igroup. */ static void _igroup_free(comex_igroup_t *igroup) { int status; #if DEBUG printf("[%d] _igroup_free\n", RANK_OR_PID); #endif COMEX_ASSERT(igroup); if (igroup->group != MPI_GROUP_NULL) { status = MPI_Group_free(&igroup->group); if (status != MPI_SUCCESS) { comex_error("MPI_Group_free: Failed ", status); } } #if DEBUG printf("[%d] free'd group\n", RANK_OR_PID); #endif if (igroup->comm != MPI_COMM_NULL) { status = MPI_Comm_free(&igroup->comm); if (status != MPI_SUCCESS) { comex_error("MPI_Comm_free: Failed ", status); } } #if DEBUG printf("[%d] free'd comm\n", RANK_OR_PID); #endif free(igroup); } int comex_group_free(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; #if DEBUG printf("[%d] comex_group_free(id=%d)\n", RANK_OR_PID, id); #endif /* find the group to free */ while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { break; } previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; } /* make sure we found a group */ COMEX_ASSERT(current_group_list_item != NULL); /* remove the group from the linked list */ if (previous_group_list_item != NULL) { previous_group_list_item->next = current_group_list_item->next; } /* free the igroup */ _igroup_free(current_group_list_item); return COMEX_SUCCESS; } int comex_group_create( int n, int *pid_list, comex_group_t id_parent, comex_group_t *id_child) { int status = 0; int grp_me = 0; comex_igroup_t *igroup_child = NULL; MPI_Group *group_child = NULL; MPI_Comm *comm_child = NULL; comex_igroup_t *igroup_parent = NULL; MPI_Group *group_parent = NULL; MPI_Comm *comm_parent = NULL; #if DEBUG printf("[%d] comex_group_create(" "n=%d, pid_list=%p, id_parent=%d, id_child)\n", RANK_OR_PID, n, pid_list, id_parent); { int p; printf("[%d] pid_list={%d", RANK_OR_PID, pid_list[0]); for (p=1; pgroup); comm_child = &(igroup_child->comm); /* get the parent's MPI_Group and MPI_Comm */ igroup_parent = comex_get_igroup_from_group(id_parent); group_parent = &(igroup_parent->group); comm_parent = &(igroup_parent->comm); status = MPI_Group_incl(*group_parent, n, pid_list, group_child); COMEX_ASSERT(MPI_SUCCESS == status); #if DEBUG printf("[%d] comex_group_create before crazy logic\n", RANK_OR_PID); #endif { MPI_Comm comm, comm1, comm2; int lvl=1, local_ldr_pos; status = MPI_Group_rank(*group_child, &grp_me); COMEX_ASSERT(MPI_SUCCESS == status); if (grp_me == MPI_UNDEFINED) { /* FIXME: keeping the group around for now */ #if DEBUG printf("[%d] comex_group_create aborting -- not in group\n", RANK_OR_PID); #endif return COMEX_SUCCESS; } /* SK: sanity check for the following bitwise operations */ COMEX_ASSERT(grp_me>=0); /* FIXME: can be optimized away */ status = MPI_Comm_dup(MPI_COMM_SELF, &comm); COMEX_ASSERT(MPI_SUCCESS == status); local_ldr_pos = grp_me; while(n>lvl) { int tag=0; int remote_ldr_pos = local_ldr_pos^lvl; if (remote_ldr_pos < n) { int remote_leader = pid_list[remote_ldr_pos]; MPI_Comm peer_comm = *comm_parent; int high = (local_ldr_poscomm, &(igroup_child->size)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_rank(igroup_child->comm, &(igroup_child->rank)); COMEX_ASSERT(MPI_SUCCESS == status); } #if DEBUG printf("[%d] comex_group_create after crazy logic\n", RANK_OR_PID); #endif return COMEX_SUCCESS; } static int cmplong(const void *p1, const void *p2) { return *((long*)p1) - *((long*)p2); } /** * Initialize group linked list. Prepopulate with world group. */ void comex_group_init(MPI_Comm comm) { int status = 0; int i = 0; int smallest_rank_with_same_hostid = 0; int largest_rank_with_same_hostid = 0; comex_group_t group = 0; comex_igroup_t *igroup = NULL; long *sorted = NULL; int count = 0; /* populate g_state */ /* dup MPI_COMM_WORLD and get group, rank, and size */ status = MPI_Comm_dup(comm, &(g_state.comm)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_group(g_state.comm, &(g_state.group)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_rank(g_state.comm, &(g_state.rank)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_size(g_state.comm, &(g_state.size)); COMEX_ASSERT(MPI_SUCCESS == status); #if DEBUG_TO_FILE { char pathname[80]; sprintf(pathname, "trace.%d.log", g_state.rank); comex_trace_file = fopen(pathname, "w"); COMEX_ASSERT(NULL != comex_trace_file); printf("[%d] comex_group_init()\n", RANK_OR_PID); } #endif /* need to figure out which proc is master on each node */ g_state.hostid = (long*)malloc(sizeof(long)*g_state.size); g_state.hostid[g_state.rank] = xgethostid(); status = MPI_Allgather(MPI_IN_PLACE, 1, MPI_LONG, g_state.hostid, 1, MPI_LONG, g_state.comm); COMEX_ASSERT(MPI_SUCCESS == status); smallest_rank_with_same_hostid = g_state.rank; largest_rank_with_same_hostid = g_state.rank; for (i=0; i largest_rank_with_same_hostid) { largest_rank_with_same_hostid = i; } } } g_state.master = (int*)malloc(sizeof(int)*g_state.size); #if MASTER_IS_SMALLEST_SMP_RANK g_state.master[g_state.rank] = smallest_rank_with_same_hostid; #else g_state.master[g_state.rank] = largest_rank_with_same_hostid; #endif status = MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, g_state.master, 1, MPI_INT, g_state.comm); COMEX_ASSERT(MPI_SUCCESS == status); COMEX_ASSERT(group_list == NULL); /* create the head of the group linked list */ _create_group_and_igroup(&group, &igroup); /* create a comm of only the workers (every rank is a worker) */ status = MPI_Comm_dup(comm, &(igroup->comm)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_group(igroup->comm, &(igroup->group)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_rank(igroup->comm, &(igroup->rank)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_size(igroup->comm, &(igroup->size)); COMEX_ASSERT(MPI_SUCCESS == status); /* create node comm */ /* MPI_Comm_split requires a non-negative color, * so sort and sanitize */ sorted = (long*)malloc(sizeof(long) * g_state.size); (void)memcpy(sorted, g_state.hostid, sizeof(long)*g_state.size); qsort(sorted, g_state.size, sizeof(long), cmplong); for (i=0; inext; _igroup_free(previous_group_list_item); } free(g_state.master); free(g_state.hostid); status = MPI_Comm_free(&(g_state.node_comm)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Group_free(&(g_state.group)); COMEX_ASSERT(MPI_SUCCESS == status); status = MPI_Comm_free(&(g_state.comm)); COMEX_ASSERT(MPI_SUCCESS == status); } static long xgethostid() { #if defined(__CRAYXE) #warning CRAY int nodeid; PMI_Get_nid(g_state.rank, &nodeid); #else long nodeid = gethostid(); #endif return nodeid; } ga-5.9.2/comex/src-mpi-pt/groups.h000066400000000000000000000037051500715745200167270ustar00rootroot00000000000000/** * Private header file for comex groups backed by MPI_comm. * * The rest of the comex group functions are defined in the public comex.h. * * @author Jeff Daily */ #ifndef _COMEX_GROUPS_H_ #define _COMEX_GROUPS_H_ #include #include "comex.h" typedef struct { MPI_Comm comm; /**< whole comm; all ranks */ MPI_Group group;/**< whole group; all ranks */ int size; /**< comm size */ int rank; /**< comm rank */ int *master; /**< master[size] rank of a given rank's master */ long *hostid; /**< hostid[size] hostid of SMP node for a given rank */ MPI_Comm node_comm; /**< node comm; SMP ranks */ int node_size; /**< node comm size */ int node_rank; /**< node comm rank */ } comex_group_world_t; extern comex_group_world_t g_state; typedef struct group_link { struct group_link *next;/**< next group in linked list */ comex_group_t id; /**< user-space id for this group */ MPI_Comm comm; /**< whole comm; all ranks */ MPI_Group group; /**< whole group; all ranks */ int size; /**< comm size */ int rank; /**< comm rank */ } comex_igroup_t; /** list of worker groups */ extern comex_igroup_t *group_list; extern void comex_group_init(MPI_Comm comm); extern void comex_group_finalize(); extern comex_igroup_t* comex_get_igroup_from_group(comex_group_t group); /* verify that proc is part of group */ #define CHECK_GROUP(GROUP,PROC) do { \ int size; \ int ierr = comex_group_size(GROUP,&size); \ COMEX_ASSERT(GROUP >= 0); \ COMEX_ASSERT(COMEX_SUCCESS == ierr); \ COMEX_ASSERT(PROC >= 0); \ COMEX_ASSERT(PROC < size); \ } while(0) #endif /* _COMEX_GROUPS_H_ */ ga-5.9.2/comex/src-mpi-pt/hello.c000066400000000000000000000010501500715745200164750ustar00rootroot00000000000000#include #include #include #include "comex.h" int main(int argc, char **argv) { int rank = 0; int size = 0; void **memory = NULL; MPI_Init(&argc, &argv); comex_init(); comex_group_size(COMEX_GROUP_WORLD, &size); comex_group_rank(COMEX_GROUP_WORLD, &rank); memory = malloc(sizeof(void*) * size); assert(memory); comex_malloc(memory, 1024*(rank+1), COMEX_GROUP_WORLD); comex_free(memory[rank], COMEX_GROUP_WORLD); comex_finalize(); MPI_Finalize(); return 0; } ga-5.9.2/comex/src-mpi-pt/reg_cache.c000066400000000000000000000377611500715745200173140ustar00rootroot00000000000000/** * Registration cache. * * Defensive programming via copious COMEX_ASSERT statements is encouraged. */ #if HAVE_CONFIG_H # include "config.h" #endif /* C headers */ #include #include #include #include /* 3rd party headers */ #include /* our headers */ #include "comex.h" #include "comex_impl.h" #include "reg_cache.h" #define STATIC static inline /* the static members in this module */ static reg_entry_t **reg_cache = NULL; /**< list of caches (one per process) */ static int reg_nprocs = 0; /**< number of caches (one per process) */ /* the static functions in this module */ static reg_return_t seg_cmp(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len, int op); static reg_return_t seg_intersects(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len); static reg_return_t seg_contains(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len); static reg_return_t reg_entry_intersects(reg_entry_t *reg_entry, void *buf, size_t len); static reg_return_t reg_entry_contains(reg_entry_t *reg_entry, void *buf, size_t len); #define TEST_FOR_INTERSECTION 0 #define TEST_FOR_CONTAINMENT 1 /** * Detects whether two memory segments intersect or one contains the other. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * @param[in] op op to perform, either TEST_FOR_INTERSECTION or * TEST_FOR_CONTAINMENT * * @pre NULL != reg_beg * @pre NULL != oth_beg * * @return RR_SUCCESS on success */ STATIC reg_return_t seg_cmp(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len, int op) { ptrdiff_t reg_beg = 0; ptrdiff_t reg_end = 0; ptrdiff_t oth_beg = 0; ptrdiff_t oth_end = 0; int result = 0; /* preconditions */ COMEX_ASSERT(NULL != reg_addr); COMEX_ASSERT(NULL != oth_addr); /* casts to ptrdiff_t since arithmetic on void* is undefined */ reg_beg = (ptrdiff_t)(reg_addr); reg_end = reg_beg + (ptrdiff_t)(reg_len); oth_beg = (ptrdiff_t)(oth_addr); oth_end = oth_beg + (ptrdiff_t)(oth_len); /* hack? we had problems with adjacent registered memory regions and * when the length of the query region was 0 */ if (oth_beg == oth_end) { oth_end += 1; } switch (op) { case TEST_FOR_INTERSECTION: result = (reg_beg >= oth_beg && reg_beg < oth_end) || (reg_end > oth_beg && reg_end <= oth_end); #if DEBUG printf("[%d] TEST_FOR_INTERSECTION " "(%td >= %td [%d] && %td < %td [%d]) ||" "(%td > %td [%d] && %td <= %td [%d])\n", g_state.rank, reg_beg, oth_beg, (reg_beg >= oth_beg), reg_beg, oth_end, (reg_beg < oth_end), reg_end, oth_beg, (reg_end > oth_beg), reg_end, oth_end, (reg_end <= oth_end)); #endif break; case TEST_FOR_CONTAINMENT: result = reg_beg <= oth_beg && reg_end >= oth_end; #if DEBUG printf("[%d] TEST_FOR_CONTAINMENT " "%td <= %td [%d] && %td >= %td [%d]\n", g_state.rank, reg_beg, oth_beg, (reg_beg <= oth_beg), reg_end, oth_end, (reg_end >= oth_end)); #endif break; default: COMEX_ASSERT(0); } if (result) { return RR_SUCCESS; } else { return RR_FAILURE; } } /** * Detects whether two memory segments intersect. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * * @pre NULL != reg_beg * @pre NULL != oth_beg * * @return RR_SUCCESS on success */ STATIC reg_return_t seg_intersects(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len) { /* preconditions */ COMEX_ASSERT(NULL != reg_addr); COMEX_ASSERT(NULL != oth_addr); return seg_cmp( reg_addr, reg_len, oth_addr, oth_len, TEST_FOR_INTERSECTION); } /** * Detects whether the first memory segment contains the other. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * * @pre NULL != reg_beg * @pre NULL != oth_beg * * @return RR_SUCCESS on success */ STATIC reg_return_t seg_contains(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len) { /* preconditions */ COMEX_ASSERT(NULL != reg_addr); COMEX_ASSERT(NULL != oth_addr); return seg_cmp( reg_addr, reg_len, oth_addr, oth_len, TEST_FOR_CONTAINMENT); } /** * Detects whether two memory segments intersect. * * @param[in] reg_entry the registration entry * @param[in] buf starting address for the contiguous memory region * @param[in] len length of the contiguous memory region * * @pre NULL != reg_entry * @pre NULL != buf * @pre len >= 0 * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_intersects(reg_entry_t *reg_entry, void *buf, size_t len) { #if DEBUG printf("[%d] reg_entry_intersects(reg_entry=%p, buf=%p, len=%d)\n", g_state.rank, reg_entry, buf, len); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_entry); COMEX_ASSERT(NULL != buf); COMEX_ASSERT(len >= 0); return seg_intersects( reg_entry->buf, reg_entry->len, buf, len); } /** * Detects whether the first memory segment contains the other. * * @param[in] reg_entry the registration entry * @param[in] buf starting address for the contiguous memory region * @param[in] len length of the contiguous memory region * * @pre NULL != reg_entry * @pre NULL != buf * @pre len >= 0 * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_contains(reg_entry_t *reg_entry, void *buf, size_t len) { #if DEBUG printf("[%d] reg_entry_contains(reg_entry=%p, buf=%p, len=%d)\n", g_state.rank, reg_entry, buf, len); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_entry); COMEX_ASSERT(NULL != buf); COMEX_ASSERT(len >= 0); return seg_contains( reg_entry->buf, reg_entry->len, buf, len); } /** * Remove registration cache entry without deregistration. * * @param[in] rank the rank where the entry came from * @param[in] reg_entry the entry * * @pre NULL != reg_entry * @pre 0 <= rank && rank < reg_nprocs * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_destroy(int rank, reg_entry_t *reg_entry) { #if DEBUG printf("[%d] reg_entry_destroy(rank=%d, reg_entry=%p)\n" "buf=%p len=%zu name=%s mapped=%p\n", g_state.rank, rank, reg_entry, reg_entry->buf, reg_entry->len, reg_entry->name, reg_entry->mapped); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_entry); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); /* free cache entry */ free(reg_entry); return RR_SUCCESS; } /** * Create internal data structures for the registration cache. * * @param[in] nprocs number of registration caches to create i.e. one per * process * * @pre this function is called once to initialize the internal data * structures and cannot be called again until reg_cache_destroy() has been * called * * @see reg_cache_destroy() * * @return RR_SUCCESS on success */ reg_return_t reg_cache_init(int nprocs) { int i = 0; #if DEBUG printf("[%d] reg_cache_init(nprocs=%d)\n", g_state.rank, nprocs); #endif /* preconditions */ COMEX_ASSERT(NULL == reg_cache); COMEX_ASSERT(0 == reg_nprocs); /* keep the number of caches around for later use */ reg_nprocs = nprocs; /* allocate the registration cache list: */ reg_cache = (reg_entry_t **)malloc(sizeof(reg_entry_t*) * reg_nprocs); COMEX_ASSERT(reg_cache); /* initialize the registration cache list: */ for (i = 0; i < reg_nprocs; ++i) { reg_cache[i] = NULL; } return RR_SUCCESS; } /** * Deregister and destroy all cache entries and associated buffers. * * @pre this function is called once to destroy the internal data structures * and cannot be called again until reg_cache_init() has been called * * @see reg_cache_init() * * @return RR_SUCCESS on success */ reg_return_t reg_cache_destroy() { int i = 0; #if DEBUG printf("[%d] reg_cache_destroy()\n", g_state.rank); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_cache); COMEX_ASSERT(0 != reg_nprocs); for (i = 0; i < reg_nprocs; ++i) { reg_entry_t *runner = reg_cache[i]; while (runner) { reg_entry_t *previous = runner; /* pointer to previous runner */ /* get next runner */ runner = runner->next; /* destroy the entry */ reg_entry_destroy(i, previous); } } /* free registration cache list */ free(reg_cache); reg_cache = NULL; /* reset the number of caches */ reg_nprocs = 0; return RR_SUCCESS; } /** * Locate a registration cache entry which contains the given segment * completely. * * @param[in] rank rank of the process * @param[in] buf starting address of the buffer * @parma[in] len length of the buffer * * @pre 0 <= rank && rank < reg_nprocs * @pre reg_cache_init() was previously called * * @return the reg cache entry, or NULL on failure */ reg_entry_t* reg_cache_find(int rank, void *buf, size_t len) { reg_entry_t *entry = NULL; reg_entry_t *runner = NULL; #if DEBUG printf("[%d] reg_cache_find(rank=%d, buf=%p, len=%d)\n", g_state.rank, rank, buf, len); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_cache); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); runner = reg_cache[rank]; while (runner && NULL == entry) { if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { entry = runner; #if DEBUG printf("[%d] reg_cache_find entry found\n" "reg_entry=%p buf=%p len=%d\n" "rank=%d buf=%p len=%zu name=%s mapped=%p\n", g_state.rank, runner, buf, len, runner->rank, runner->buf, runner->len, runner->name, runner->mapped); #endif } runner = runner->next; } #ifndef NDEBUG /* we COMEX_ASSERT that the found entry was unique */ while (runner) { if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { #if DEBUG printf("[%d] reg_cache_find duplicate found\n" "reg_entry=%p buf=%p len=%d\n" "rank=%d buf=%p len=%zu name=%s mapped=%p\n", g_state.rank, runner, buf, len, runner->rank, runner->buf, runner->len, runner->name, runner->mapped); #endif COMEX_ASSERT(0); } runner = runner->next; } #endif return entry; } /** * Locate a registration cache entry which intersects the given segment. * * @param[in] rank rank of the process * @param[in] buf starting address of the buffer * @parma[in] len length of the buffer * * @pre 0 <= rank && rank < reg_nprocs * @pre reg_cache_init() was previously called * * @return the reg cache entry, or NULL on failure */ reg_entry_t* reg_cache_find_intersection(int rank, void *buf, size_t len) { reg_entry_t *entry = NULL; reg_entry_t *runner = NULL; #if DEBUG printf("[%d] reg_cache_find_intersection(rank=%d, buf=%p, len=%d)\n", g_state.rank, rank, buf, len); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_cache); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); runner = reg_cache[rank]; while (runner && NULL == entry) { if (RR_SUCCESS == reg_entry_intersects(runner, buf, len)) { entry = runner; } runner = runner->next; } /* we COMEX_ASSERT that the found entry was unique */ while (runner) { if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { COMEX_ASSERT(0); } runner = runner->next; } return entry; } /** * Create a new registration entry based on the given members. * * @pre 0 <= rank && rank < reg_nprocs * @pre NULL != buf * @pre 0 <= len * @pre reg_cache_init() was previously called * @pre NULL == reg_cache_find(rank, buf, 0) * @pre NULL == reg_cache_find_intersection(rank, buf, 0) * * @return RR_SUCCESS on success */ reg_entry_t* reg_cache_insert(int rank, void *buf, size_t len, const char *name, #if ENABLE_SYSV key_t key, #endif void *mapped) { reg_entry_t *node = NULL; #if DEBUG printf("[%d] reg_cache_insert(rank=%d, buf=%p, len=%d, name=%s, mapped=%p)\n", g_state.rank, rank, buf, len, name, mapped); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_cache); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); COMEX_ASSERT(NULL != buf); COMEX_ASSERT(len >= 0); COMEX_ASSERT(NULL == reg_cache_find(rank, buf, len)); COMEX_ASSERT(NULL == reg_cache_find_intersection(rank, buf, len)); /* allocate the new entry */ node = (reg_entry_t *)malloc(sizeof(reg_entry_t)); COMEX_ASSERT(node); /* initialize the new entry */ node->rank = rank; node->buf = buf; node->len = len; #if ENABLE_SYSV node->key = key; #endif (void)memcpy(node->name, name, SHM_NAME_SIZE); node->mapped = mapped; node->next = NULL; /* push new entry to tail of linked list */ if (NULL == reg_cache[rank]) { reg_cache[rank] = node; } else { reg_entry_t *runner = reg_cache[rank]; while (runner->next) { runner = runner->next; } runner->next = node; } return node; } /** * Removes the reg cache entry associated with the given rank and buffer. * * If this process owns the buffer, it will unregister the buffer, as well. * * @param[in] rank * @param[in] buf * * @pre 0 <= rank && rank < reg_nprocs * @pre NULL != buf * @pre reg_cache_init() was previously called * @pre NULL != reg_cache_find(rank, buf, 0) * * @return RR_SUCCESS on success * RR_FAILURE otherwise */ reg_return_t reg_cache_delete(int rank, void *buf) { reg_return_t status = RR_FAILURE; reg_entry_t *runner = NULL; reg_entry_t *previous_runner = NULL; #if DEBUG printf("[%d] reg_cache_delete(rank=%d, buf=%p)\n", g_state.rank, rank, buf); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_cache); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); COMEX_ASSERT(NULL != buf); COMEX_ASSERT(NULL != reg_cache_find(rank, buf, 0)); /* this is more restrictive than reg_cache_find() in that we locate * exactlty the same region starting address */ runner = reg_cache[rank]; while (runner) { if (runner->buf == buf) { break; } previous_runner = runner; runner = runner->next; } /* we should have found an entry */ if (NULL == runner) { COMEX_ASSERT(0); return RR_FAILURE; } /* pop the entry out of the linked list */ if (previous_runner) { previous_runner->next = runner->next; } else { reg_cache[rank] = reg_cache[rank]->next; } status = reg_entry_destroy(rank, runner); return status; } reg_return_t reg_cache_nullify(reg_entry_t *node) { #if DEBUG printf("[%d] reg_cache_nullify(node=%p)\n", g_state.rank, node); #endif node->rank = -1; node->buf = NULL; node->len = 0; (void)memset(node->name, 0, SHM_NAME_SIZE); node->mapped = NULL; node->next = NULL; return RR_SUCCESS; } ga-5.9.2/comex/src-mpi-pt/reg_cache.h000066400000000000000000000025501500715745200173050ustar00rootroot00000000000000#ifndef _REG_CACHE_H_ #define _REG_CACHE_H_ #include // #define ENABLE_SYSV #if ENABLE_SYSV #include #endif /** * Enumerate the return codes for registration cache functions. */ typedef enum _reg_return_t { RR_SUCCESS=0, /**< success */ RR_FAILURE /**< non-specific failure */ } reg_return_t; /** * A registered contiguous memory region. */ typedef struct _reg_entry_t { int rank; /**< rank where this region lives */ void *buf; /**< starting address of region */ size_t len; /**< length of region */ #if ENABLE_SYSV key_t key; char name[SHM_NAME_SIZE]; /**< name of region */ #else char name[SHM_NAME_SIZE]; /**< name of region */ #endif void *mapped; /**< starting address of mmap'd region */ struct _reg_entry_t *next; /**< next memory region in list */ } reg_entry_t; /* functions * * documentation is in the *.c file */ reg_return_t reg_cache_init(int nprocs); reg_return_t reg_cache_destroy(); reg_entry_t *reg_cache_find(int rank, void *buf, size_t len); reg_entry_t *reg_cache_insert(int rank, void *buf, size_t len, const char *name, #if ENABLE_SYSV key_t key, #endif void *mapped); reg_return_t reg_cache_delete(int rank, void *buf); reg_return_t reg_cache_nullify(reg_entry_t *entry); #endif /* _REG_CACHE_H_ */ ga-5.9.2/comex/src-mpi/000077500000000000000000000000001500715745200146115ustar00rootroot00000000000000ga-5.9.2/comex/src-mpi/Makefile.inc000066400000000000000000000003131500715745200170160ustar00rootroot00000000000000libcomex_la_SOURCES += src-mpi/comex.c libcomex_la_SOURCES += src-mpi/comex_impl.h libcomex_la_SOURCES += src-mpi/groups.c libcomex_la_SOURCES += src-mpi/groups.h AM_CPPFLAGS += -I$(top_srcdir)/src-mpi ga-5.9.2/comex/src-mpi/NOTES.md000066400000000000000000000075241500715745200160330ustar00rootroot00000000000000# MPI Two-Sided (MPI-TS) These are notes describing the MPI two-sided runtime. These notes are intended to help developers navigate the contents of these files and to locate specific functionality. MPI-TS aims to be the basic reference implementation for other one-sided runtimes based solely on MPI_Send/MPI_Recv. It is strictly compatible with the MPI-1 standard. It does not use shared memory within a node. Practically *all* use of MPI takes advantage of the non-blocking interfaces. There is no asynchronous progress. ## Message Queues There are three linked lists representing message queues for outgoing messages (`_mq_push`, `_mq_test`, `_mq_pop`), incoming get notifications (`_gq_push`, `_gq_test`), and mutex lock requests (`_lq_progress`). ### Active Messages MPI-TS uses an active message concept. The `header_t` type contains attributes for the operation, remote and local addresses, length of the data payload, and an optional notification address. The `typedef enum op_t` represents the types of operations, for example `OP_PUT`, `OP_ACC_INT`, `OP_BARRIER_REQUEST`/`OP_BARRIER_RESPONSE`. Messages are sent as a single contiguous buffer containing the header as well as the data payload. The progress function (see below) queries for and responds to incoming requests from all other MPI ranks. The `op_t` used in the active message header triggers a callback function to handle the operation. Some operations require sending a notification message back to the originator of the request. ### MQ Message Queue The `_mq_push` function calls `MPI_Isend` and adds the MPI_Request to the end of the linked list. The `_mq_test` function calls `MPI_Test` but only on the head of the linked list of requests. This function is used in conjunction with `_mq_pop` to remove the first request from the linked list head when the test of completion succeeded. ### GQ Get Queue The GQ is for get requests only. It functions similar to the outgoing message queue above. A get request can be thought of as a reverse put operation. But since all MPI-TS requests must be non-blocking, we must repeatedly call the progress engine while waiting for the `comex_get` operation to complete. Completion is indicated by toggling a variable associated with the request. The get queue contains a linked list of these status indicators. The progress engine will loop until all outstanding get requests are satisfied. ## Progress Progress is made by aggressively calling the progress engine function `comex_make_progress` any time any other comex function is called. This function is not externally part of the comex API. This function checks for any incoming messages from any MPI rank. The operation associated with the message is interpreted to a callback funtion. The data payload is always after `sizeof(header_t)` bytes. The progress function will loop until all outstanding incoming get requests as well as outgoing sends are satisfied. This significantly affects performance since progress is effectively synchronous. A special note about `comex_barrier()`. The barrier is effectively a `comex_fence_all()` followed by an `MPI_Barrier()`. However, if we were to use the `MPI_Barrier` directly, it would not be able to call the comex progress function -- a late fence request message is not the same as no message at all. The comex_barrier is an expensive all-to-all communication. This is only the case for MPI-TS; other MPI-based comex runtimes are able to use `MPI_Barrier` directly. ## Special Note About MPI-TS and MPI Interoperability The information about the `comex_barrier` above indicates a complicated interaction between the comex and MPI runtimes. Users *must not* make any MPI calls within a comex phase of communication, otherwise comex progress might stall and deadlock the application. Users must take care to call `comex_barrier` before calling any other MPI function. ga-5.9.2/comex/src-mpi/comex.c000066400000000000000000001655671500715745200161140ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* C and/or system headers */ #if HAVE_STDIO_H #include #endif #if HAVE_STDLIB_H #include #endif #if HAVE_ASSERT_H #include #endif #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if HAVE_UNISTD_H #include #endif /* 3rd party headers */ #include /* our headers */ #include "comex.h" #include "comex_impl.h" #include "groups.h" #include "acc.h" #define MEMSET_AFTER_MALLOC 0 #define DEBUG 0 #define DEBUG_TO_FILE 0 #if DEBUG_TO_FILE # define printf(...) fprintf(my_file, __VA_ARGS__); fflush(my_file); #endif /* exported state */ local_state l_state; int comex_me=-1; int comex_nproc=-1; /* static state */ static int initialized=0; /* for comex_initialized(), 0=false */ static char *fence_array=NULL; #if DEBUG_TO_FILE static FILE *my_file=NULL; #endif /* static function declarations */ static void _mq_push(int dest, char *message, int count); static int _mq_test(void); static void _mq_pop(void); static void _gq_push(char *notify); static void _gq_push_item(get_t *item); static void _make_progress_if_needed(void); static int _my_isend(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request); static void _my_free(void *ptr); static void* _my_malloc(size_t size); static void* _my_memcpy(void *dest, const void *src, size_t n); static int _get_nbi(void *src, void *dst, int bytes, int proc); static int _put_nbi(void *src, void *dst, int bytes, int proc); static void _put_handler(header_t *header, char *payload); static void _get_request_handler(header_t *header, int proc); static void _get_response_handler(header_t *header, char *payload); static void _acc_handler(header_t *header, char *payload); static void _fence_request_handler(header_t *header, int proc); static void _fence_response_handler(header_t *header, int proc); static void _barrier_request_handler(header_t *header, int proc); static void _barrier_response_handler(header_t *header, int proc); static void _send_fence_message(int proc, int *notify); static void _fetch_and_add_request_handler(header_t *header, char *payload, int proc); static void _fetch_and_add_response_handler(header_t *header, char *payload); static void _swap_request_handler(header_t *header, char *payload, int proc); static void _swap_response_handler(header_t *header, char *payload); static void _lock_request_handler(header_t *header, int proc); static void _lock_response_handler(header_t *header); static void _unlock_request_handler(header_t *header, int proc); static void _lq_push(int rank, int id, char *notify); static int _lq_progress(void); int _my_isend(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request *request) { #if DEBUG { char *op; switch (((header_t*)buf)->operation) { case OP_PUT: op="OP_PUT"; break; case OP_GET_REQUEST: op="OP_GET_REQUEST"; break; case OP_GET_RESPONSE: op="OP_GET_RESPONSE"; break; case OP_ACC_INT: op="OP_ACC_INT"; break; case OP_ACC_DBL: op="OP_ACC_DBL"; break; case OP_ACC_FLT: op="OP_ACC_FLT"; break; case OP_ACC_CPL: op="OP_ACC_CPL"; break; case OP_ACC_DCP: op="OP_ACC_DCP"; break; case OP_ACC_LNG: op="OP_ACC_LNG"; break; case OP_FENCE_REQUEST: op="OP_FENCE_REQUEST"; break; case OP_FENCE_RESPONSE: op="OP_FENCE_RESPONSE"; break; case OP_BARRIER_REQUEST: op="OP_BARRIER_REQUEST"; break; case OP_BARRIER_RESPONSE: op="OP_BARRIER_RESPONSE"; break; case OP_FETCH_AND_ADD_REQUEST: op="OP_FETCH_AND_ADD_REQUEST"; break; case OP_FETCH_AND_ADD_RESPONSE: op="OP_FETCH_AND_ADD_RESPONSE"; break; case OP_SWAP_REQUEST: op="OP_SWAP_REQUEST"; break; case OP_SWAP_RESPONSE: op="OP_SWAP_RESPONSE"; break; case OP_LOCK_REQUEST: op="OP_LOCK_REQUEST"; break; case OP_LOCK_RESPONSE: op="OP_LOCK_RESPONSE"; break; case OP_UNLOCK: op="OP_UNLOCK"; break; default: assert(0); } printf("[%d] sending %s to %d\n", l_state.rank, op, dest); } #endif assert(dest != MPI_ANY_SOURCE); return MPI_Isend(buf, count, datatype, dest, tag, comm, request); } static void* _my_memcpy(void *dest, const void *src, size_t n) { #if DEBUG printf("[%d] _my_memcpy(dest=%p, src=%p, n=%zu)\n", l_state.rank, dest, src, n); #endif return memcpy(dest, src, n); } static void* _my_malloc(size_t size) { void *memptr=NULL; memptr = malloc(size); assert(memptr); #if MEMSET_AFTER_MALLOC /* valgrind was reporting uninitialized values coming from _my_malloc * so use this memset to quiet valgrind for now. We may turn this * off after additional development/testing if we find the true * source of the uninitialized bytes. */ memptr = memset(memptr, 0, size); assert(memptr); #endif #if DEBUG printf("[%d] _my_malloc(%zu) -> %p\n", l_state.rank, size, memptr); #endif return memptr; } static void _my_free(void *ptr) { #if DEBUG printf("[%d] _my_free(%p)\n", l_state.rank, ptr); #endif free(ptr); } #if DEBUG # define _my_free(ARG) _my_free(ARG); printf(#ARG) #endif static void _mq_push(int dest, char *message, int count) { message_t *mq = NULL; mq = _my_malloc(sizeof(message_t)); assert(mq); mq->next = NULL; mq->dest = dest; mq->message = message; _my_isend(message, count, MPI_CHAR, dest, COMEX_TAG, l_state.world_comm, &(mq->request)); if (l_state.mq_tail) { l_state.mq_tail->next = mq; l_state.mq_tail = mq; } else { l_state.mq_head = mq; l_state.mq_tail = mq; } ++l_state.mq_size; assert(l_state.mq_size >= 0); } static int _mq_test() { int flag; int rc; MPI_Status status; assert(l_state.mq_head); assert(l_state.mq_tail); assert(l_state.mq_head->request != MPI_REQUEST_NULL); rc = MPI_Test(&(l_state.mq_head->request), &flag, &status); assert(MPI_SUCCESS == rc); return flag; } static void _mq_pop() { message_t *mq_to_delete = NULL; assert(l_state.mq_head); assert(l_state.mq_tail); assert(l_state.mq_head->request == MPI_REQUEST_NULL); mq_to_delete = l_state.mq_head; l_state.mq_head = l_state.mq_head->next; /* don't need message or request any longer */ _my_free(mq_to_delete->message); _my_free(mq_to_delete); if (NULL == l_state.mq_head) { l_state.mq_tail = NULL; } --l_state.mq_size; assert(l_state.mq_size >= 0); } static void _gq_push(char *notify) { get_t *item = _my_malloc(sizeof(get_t)); item->next = NULL; item->notify_address = notify; _gq_push_item(item); } static void _gq_push_item(get_t *item) { assert(item); if (l_state.gq_tail) { l_state.gq_tail->next = item; l_state.gq_tail = item; } else { l_state.gq_head = item; l_state.gq_tail = item; } } /* iterate over get queue and pop completed get requests * return true if there are get requests remaining */ static int _gq_test() { get_t *old_gq = l_state.gq_head; l_state.gq_head = NULL; l_state.gq_tail = NULL; while (old_gq) { if (*((char*)old_gq->notify_address)) { _gq_push_item(old_gq); old_gq = old_gq->next; } else { get_t *need_to_free = old_gq; old_gq = old_gq->next; _my_free((char*)need_to_free->notify_address); _my_free(need_to_free); } if (l_state.gq_tail) { l_state.gq_tail->next = NULL; } } return NULL != l_state.gq_tail; } static void _make_progress_if_needed(void) { #if DEBUG printf("[%d] _make_progress_if_needed()\n", l_state.rank); #endif if (l_state.mq_size >= MAX_NB_OUTSTANDING) { comex_make_progress(); } } void comex_make_progress(void) { int iprobe_flag=0; MPI_Status iprobe_status; int get_flag=0; int lock_flag=0; #if DEBUG printf("[%d] comex_make_progress()\n", l_state.rank); #endif do { iprobe_flag = 0; get_flag = 0; /* test for outgoing message completion only if we have outgoing * messages; we test from the head only */ if (l_state.mq_head) { while (l_state.mq_head && _mq_test()) { _mq_pop(); } } /* test for incoming get responses */ if (_gq_test()) { get_flag = 1; } /* test for incoming messages */ MPI_Iprobe(MPI_ANY_SOURCE, COMEX_TAG, l_state.world_comm, &iprobe_flag, &iprobe_status); if (iprobe_flag) { int length; char *message; char *payload; header_t *header; MPI_Status recv_status; /* allocate message buffer and get message */ MPI_Get_count(&iprobe_status, MPI_CHAR, &length); #if DEBUG printf("[%d] iprobe source=%d length=%d\n", l_state.rank, iprobe_status.MPI_SOURCE, length); #endif message = _my_malloc(length); MPI_Recv(message, length, MPI_CHAR, iprobe_status.MPI_SOURCE, COMEX_TAG, l_state.world_comm, &recv_status); header = (header_t*)message; payload = message + sizeof(header_t); /* dispatch message handler */ switch (header->operation) { case OP_PUT: _put_handler(header, payload); break; case OP_GET_REQUEST: _get_request_handler(header, iprobe_status.MPI_SOURCE); break; case OP_GET_RESPONSE: _get_response_handler(header, payload); break; case OP_ACC_INT: case OP_ACC_DBL: case OP_ACC_FLT: case OP_ACC_CPL: case OP_ACC_DCP: case OP_ACC_LNG: _acc_handler(header, payload); break; case OP_FENCE_REQUEST: _fence_request_handler(header, iprobe_status.MPI_SOURCE); break; case OP_FENCE_RESPONSE: _fence_response_handler(header, iprobe_status.MPI_SOURCE); break; case OP_BARRIER_REQUEST: _barrier_request_handler(header, iprobe_status.MPI_SOURCE); break; case OP_BARRIER_RESPONSE: _barrier_response_handler(header, iprobe_status.MPI_SOURCE); break; case OP_FETCH_AND_ADD_REQUEST: _fetch_and_add_request_handler(header, payload, iprobe_status.MPI_SOURCE); break; case OP_FETCH_AND_ADD_RESPONSE: _fetch_and_add_response_handler(header, payload); break; case OP_SWAP_REQUEST: _swap_request_handler(header, payload, iprobe_status.MPI_SOURCE); break; case OP_SWAP_RESPONSE: _swap_response_handler(header, payload); break; case OP_LOCK_REQUEST: _lock_request_handler(header, iprobe_status.MPI_SOURCE); break; case OP_LOCK_RESPONSE: _lock_response_handler(header); break; case OP_UNLOCK: _unlock_request_handler(header, iprobe_status.MPI_SOURCE); break; default: printf("[%d] header operation not recognized: %d\n", l_state.rank, header->operation); assert(0); } /* free the message buffer */ _my_free(message); } lock_flag = _lq_progress(); /* loop until we run out of incoming and outgoing messages */ #if DEBUG printf("[%d] iprobe_flag=%d || get_flag=%d || l_state.mq_size=%d lock_flag=%d\n", l_state.rank, iprobe_flag, get_flag, l_state.mq_size, lock_flag); #endif } while (iprobe_flag || get_flag || l_state.mq_size); } static void _put_handler(header_t *header, char *payload) { #if DEBUG printf("[%d] _put_handler rem=%p loc=%p len=%d not=%p\n", l_state.rank, header->remote_address, header->local_address, header->length, header->notify_address); #endif assert(OP_PUT == header->operation); _my_memcpy(header->remote_address, payload, header->length); } static void _get_request_handler(header_t *request_header, int proc) { char *message; #if DEBUG printf("[%d] _get_request_handler proc=%d\n", l_state.rank, proc); #endif #if DEBUG printf("[%d] request_header rem=%p loc=%p len=%d not=%p\n", l_state.rank, request_header->remote_address, request_header->local_address, request_header->length, request_header->notify_address); #endif assert(OP_GET_REQUEST == request_header->operation); /* reuse the header, just change the OP */ /* NOTE: it gets deleted anyway when we return from this handler */ request_header->operation = OP_GET_RESPONSE; message = _my_malloc(sizeof(header_t) + request_header->length); _my_memcpy(message, request_header, sizeof(header_t)); _my_memcpy(message+sizeof(header_t), request_header->remote_address, request_header->length); _mq_push(proc, message, sizeof(header_t) + request_header->length); } static void _get_response_handler(header_t *header, char *payload) { #if DEBUG printf("[%d] _get_response_handler rem=%p loc=%p len=%d not=%p\n", l_state.rank, header->remote_address, header->local_address, header->length, header->notify_address); #endif assert(OP_GET_RESPONSE == header->operation); _my_memcpy(header->local_address, payload, header->length); *((char*)(header->notify_address)) = 0; } static void _acc_handler(header_t *header, char *payload) { int op; int sizeof_scale; #if DEBUG printf("[%d] _acc_handler\n", l_state.rank); #endif switch (header->operation) { case OP_ACC_INT: sizeof_scale = sizeof(int); op = COMEX_ACC_INT; break; case OP_ACC_DBL: sizeof_scale = sizeof(double); op = COMEX_ACC_DBL; break; case OP_ACC_FLT: sizeof_scale = sizeof(float); op = COMEX_ACC_FLT; break; case OP_ACC_LNG: sizeof_scale = sizeof(long); op = COMEX_ACC_LNG; break; case OP_ACC_CPL: sizeof_scale = sizeof(SingleComplex); op = COMEX_ACC_CPL; break; case OP_ACC_DCP: sizeof_scale = sizeof(DoubleComplex); op = COMEX_ACC_DCP; break; default: assert(0); } _acc(op, header->length, header->remote_address, payload+sizeof_scale, payload); } static void _fence_request_handler(header_t *header, int proc) { header_t *response_header = NULL; #if DEBUG printf("[%d] _fence_request_handler proc=%d\n", l_state.rank, proc); #endif /* preconditions */ assert(header); /* allocate new header */ response_header = _my_malloc(sizeof(header_t)); assert(response_header); /* we make a copy since the original header will be free'd upon return */ memcpy(response_header, header, sizeof(header_t)); /* the only thing that changes is the operation */ response_header->operation = OP_FENCE_RESPONSE; /* we send the header back to the originating proc */ _mq_push(proc, (char*)response_header, sizeof(header_t)); } static void _fence_response_handler(header_t *header, int proc) { #if DEBUG printf("[%d] _fence_response_handler proc=%d decrementing %d\n", l_state.rank, proc, (int)(*((int*)header->notify_address))); #endif assert(header); --(*((int*)(header->notify_address))); } /* barrier requests are always queued for later */ static void _barrier_request_handler(header_t *header, int proc) { barrier_t *new_barrier_request = NULL; #if DEBUG printf("[%d] _barrier_request_handler proc=%d\n", l_state.rank, proc); #endif /* create new queued barrier request */ new_barrier_request = _my_malloc(sizeof(barrier_t)); new_barrier_request->next = NULL; new_barrier_request->world_rank = proc; new_barrier_request->notify_address = header->notify_address; if (NULL == l_state.bq_head) { l_state.bq_head = new_barrier_request; l_state.bq_tail = new_barrier_request; } else { l_state.bq_tail->next = new_barrier_request; l_state.bq_tail = new_barrier_request; } } static void _barrier_response_handler(header_t *header, int proc) { #if DEBUG printf("[%d] _barrier_response_handler proc=%d\n", l_state.rank, proc); #endif ++(*((char*)header->notify_address)); } int comex_put(void *src, void *dst, int bytes, int proc, comex_group_t group) { CHECK_GROUP(group,proc); #if DEBUG printf("[%d] comex_put(src=%p, dst=%p, bytes=%d, proc=%d, group=%d)\n", l_state.rank, src, dst, bytes, proc, group); #endif _put_nbi(src, dst, bytes, proc); comex_wait_proc(proc, group); return COMEX_SUCCESS; } int comex_get(void *src, void *dst, int bytes, int proc, comex_group_t group) { CHECK_GROUP(group,proc); #if DEBUG printf("[%d] comex_get(src=%p, dst=%p, bytes=%d, proc=%d)\n", l_state.rank, src, dst, bytes, proc); #endif _get_nbi(src, dst, bytes, proc); comex_wait_proc(proc, group); return COMEX_SUCCESS; } static int _put_nbi(void *src, void *dst, int bytes, int proc) { header_t header; char *message; #if DEBUG printf("[%d] _put_nbi(src=%p, dst=%p, bytes=%d, proc=%d)\n", l_state.rank, src, dst, bytes, proc); #endif /* Corner case */ if (proc == l_state.rank) { _my_memcpy(dst, src, bytes); return COMEX_SUCCESS; } header.operation = OP_PUT; header.remote_address = dst; header.local_address = src; header.length = bytes; header.notify_address = NULL; message = _my_malloc(sizeof(header_t) + bytes); _my_memcpy(message, &header, sizeof(header_t)); _my_memcpy(message+sizeof(header_t), src, bytes); fence_array[proc] = 1; _mq_push(proc, message, sizeof(header_t) + bytes); _make_progress_if_needed(); return COMEX_SUCCESS; } /* TODO: need data type as a parameter before using this function! */ static int _acc_nbi(void *src, void *dst, int bytes, int proc, int datatype, void *scale) { header_t header; char *message; int message_size = 0; int scale_size = 0; #if DEBUG printf("[%d] _acc_nbi(scale=%p src=%p, dst=%p, bytes=%d, proc=%d)\n", l_state.rank, scale, src, dst, bytes, proc); #endif switch (datatype) { case COMEX_ACC_INT: header.operation = OP_ACC_INT; scale_size = sizeof(int); break; case COMEX_ACC_DBL: header.operation = OP_ACC_DBL; scale_size = sizeof(double); break; case COMEX_ACC_FLT: header.operation = OP_ACC_FLT; scale_size = sizeof(float); break; case COMEX_ACC_CPL: header.operation = OP_ACC_CPL; scale_size = sizeof(SingleComplex); break; case COMEX_ACC_DCP: header.operation = OP_ACC_DCP; scale_size = sizeof(DoubleComplex); break; case COMEX_ACC_LNG: header.operation = OP_ACC_LNG; scale_size = sizeof(long); break; default: assert(0); } /* Corner case */ if (proc == l_state.rank) { _acc(datatype, bytes, dst, src, scale); return COMEX_SUCCESS; } header.remote_address = dst; header.local_address = src; header.length = bytes; header.notify_address = NULL; message_size = sizeof(header_t) + scale_size + bytes; message = _my_malloc(message_size); _my_memcpy(message, &header, sizeof(header_t)); _my_memcpy(message+sizeof(header_t), scale, scale_size); _my_memcpy(message+sizeof(header_t)+scale_size, src, bytes); fence_array[proc] = 1; _mq_push(proc, message, message_size); _make_progress_if_needed(); return COMEX_SUCCESS; } static int _get_nbi(void *src, void *dst, int bytes, int proc) { header_t *header = NULL; #if DEBUG printf("[%d] _get_nbi(src=%p, dst=%p, bytes=%d, proc=%d)\n", l_state.rank, src, dst, bytes, proc); #endif /* Corner case */ if (proc == l_state.rank) { _my_memcpy(dst, src, bytes); return COMEX_SUCCESS; } header = _my_malloc(sizeof(header_t)); assert(header); header->operation = OP_GET_REQUEST; header->remote_address = src; header->local_address = dst; header->length = bytes; header->notify_address = _my_malloc(sizeof(char)); /* set the value to wait on for a get response */ *((char*)(header->notify_address)) = 1; _gq_push(header->notify_address); /* get request is a header-only message */ _mq_push(proc, (char*)header, sizeof(header_t)); _make_progress_if_needed(); return COMEX_SUCCESS; } int comex_puts( void *src_ptr, int *src_stride_ar, void *dst_ptr, int *dst_stride_ar, int *count, int stride_levels, int proc, comex_group_t group) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; CHECK_GROUP(group,proc); #if DEBUG if (stride_levels) { printf("[%d] comex_puts(src_ptr=%p, src_stride_ar[0]=%d, dst_ptr=%p, dst_stride_ar[0]=%d, count[0]=%d, stride_levels=%d, proc=%d)\n", l_state.rank, src_ptr, src_stride_ar[0], dst_ptr, dst_stride_ar[0], count[0], stride_levels, proc); } else { printf("[%d] comex_puts(src_ptr=%p, src_stride_ar=NULL, dst_ptr=%p, dst_stride_ar=NULL, count=NULL, stride_levels=%d, proc=%d)\n", l_state.rank, src_ptr, dst_ptr, stride_levels, proc); } #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } /* index mangling */ for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride_ar[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } _put_nbi((char *)src_ptr + src_idx, (char *)dst_ptr + dst_idx, count[0], proc); } comex_wait_proc(proc, group); return COMEX_SUCCESS; } int comex_gets( void *src_ptr, int *src_stride_ar, void *dst_ptr, int *dst_stride_ar, int *count, int stride_levels, int proc, comex_group_t group) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; CHECK_GROUP(group,proc); #if DEBUG if (stride_levels) { printf("[%d] comex_gets(src_ptr=%p, src_stride_ar[0]=%d, dst_ptr=%p, dst_stride_ar[0]=%d, count[0]=%d, stride_levels=%d, proc=%d)\n", l_state.rank, src_ptr, src_stride_ar[0], dst_ptr, dst_stride_ar[0], count[0], stride_levels, proc); } else { printf("[%d] comex_gets(src_ptr=%p, src_stride_ar=NULL, dst_ptr=%p, dst_stride_ar=NULL, count=NULL, stride_levels=%d, proc=%d)\n", l_state.rank, src_ptr, dst_ptr, stride_levels, proc); } #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } dst_idx = 0; for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride_ar[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } _get_nbi((char *)src_ptr + src_idx, (char *)dst_ptr + dst_idx, count[0], proc); } comex_wait_proc(proc, group); return COMEX_SUCCESS; } int comex_acc( int datatype, void *scale, void *src_ptr, void *dst_ptr, int bytes, int proc, comex_group_t group) { comex_accs(datatype, scale, src_ptr, NULL, dst_ptr, NULL, &bytes, 0, proc, group); return COMEX_SUCCESS; } int comex_accs( int datatype, void *scale, void *src_ptr, int *src_stride_ar, void *dst_ptr, int *dst_stride_ar, int *count, int stride_levels, int proc, comex_group_t group) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; CHECK_GROUP(group,proc); #if DEBUG if (stride_levels) { printf("[%d] comex_accs(src_ptr=%p, src_stride_ar[0]=%d, dst_ptr=%p, dst_stride_ar[0]=%d, count[0]=%d, stride_levels=%d, proc=%d)\n", l_state.rank, src_ptr, src_stride_ar[0], dst_ptr, dst_stride_ar[0], count[0], stride_levels, proc); } else { printf("[%d] comex_accs(src_ptr=%p, src_stride_ar=NULL, dst_ptr=%p, dst_stride_ar=NULL, count=NULL, stride_levels=%d, proc=%d)\n", l_state.rank, src_ptr, dst_ptr, stride_levels, proc); } #endif /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } /* index mangling */ for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride_ar[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } _acc_nbi((char *)src_ptr + src_idx, (char *)dst_ptr + dst_idx, count[0], proc, datatype, scale); } comex_wait_proc(proc, group); return COMEX_SUCCESS; } static void _send_fence_message(int proc, int *notify) { header_t *header = NULL; #if DEBUG printf("[%d] _send_fence_message(proc=%d notify=%p)\n", l_state.rank, proc, notify); #endif /* create and prepare the header */ header = _my_malloc(sizeof(header_t)); assert(header); header->operation = OP_FENCE_REQUEST; header->remote_address = NULL; header->local_address = NULL; header->length = 0; header->notify_address = notify; _mq_push(proc, (char*)header, sizeof(header_t)); _make_progress_if_needed(); } int comex_fence_all(comex_group_t group) { int p = 0; int count_before = 0; int count_after = 0; int *notify = NULL; #if DEBUG printf("[%d] comex_fence_all()\n", l_state.rank); #endif /* count how many fence messagse to send */ for (p=0; p 0) { comex_make_progress(); #if DEBUG printf("[%d] are we stuck? notify=%d\n", l_state.rank, *notify); #endif } for (p=0; p 0) { comex_make_progress(); } fence_array[proc] = 0; _my_free(notify); return COMEX_SUCCESS; } int comex_barrier(comex_group_t comex_group) { MPI_Comm mpi_comm_default; MPI_Group mpi_group_world; MPI_Group mpi_group_default; comex_igroup_t *comex_igroup = NULL; int group_size = -1; int group_rank = -1; int group_proc = -1; /* used in for loop */ int status = 0; char *group_mask; /* which procs are part of default group 0/1 */ char *request_received_mask; /* which procs did we queue requests from */ char *response_received_mask; /* which procs did we get ack from */ int request_received_count = 0; int response_received_count = 0; int ring_proc; #if DEBUG printf("[%d] comex_barrier(%d)\n", l_state.rank, comex_group); #endif /* get comm size and rank from group parameter */ comex_igroup = comex_get_igroup_from_group(comex_group); mpi_group_default = comex_igroup->group; mpi_comm_default = comex_igroup->comm; status = MPI_Comm_size(mpi_comm_default, &group_size); assert(MPI_SUCCESS == status); status = MPI_Comm_rank(mpi_comm_default, &group_rank); assert(MPI_SUCCESS == status); /* get world group (for later rank translation) */ status = MPI_Comm_group(l_state.world_comm, &mpi_group_world); assert(MPI_SUCCESS == status); /* create array to note which procs we should respond to */ group_mask = _my_malloc(sizeof(char) * l_state.size); memset(group_mask, 0, sizeof(char) * l_state.size); /* create array to note which procs we've received requests from */ request_received_mask = _my_malloc(sizeof(char) * l_state.size); memset(request_received_mask, 0, sizeof(char) * l_state.size); /* create array to note which procs we've received responses from */ response_received_mask = _my_malloc(sizeof(char) * group_size); memset(response_received_mask, 0, sizeof(char) * group_size); /* send out barrier requests to all procs in default group */ #define USE_RING 1 #if USE_RING for (ring_proc=0; ring_procoperation = OP_BARRIER_REQUEST; header->remote_address = NULL; header->local_address = NULL; header->length = 0; header->notify_address = &response_received_mask[group_proc]; /* barrier request is a header-only message */ #if DEBUG printf("[%d] sending barrier to %d\n", l_state.rank, world_rank); #endif _mq_push(world_rank, (char*)header, sizeof(header_t)); _make_progress_if_needed(); } /* done with MPI_Group so free it */ MPI_Group_free(&mpi_group_world); /* wait until we've queued all incoming requests */ do { int p; barrier_t *qcur = NULL; comex_make_progress(); qcur = l_state.bq_head; while (NULL != qcur) { if (group_mask[qcur->world_rank]) { request_received_mask[qcur->world_rank] = 1; } qcur = qcur->next; } request_received_count = 0; for (p=0; pworld_rank] && 1 == request_received_mask[qcur->world_rank]) { barrier_t *last = NULL; header_t *response = NULL; request_received_mask[qcur->world_rank] = 0; response = _my_malloc(sizeof(header_t)); response->operation = OP_BARRIER_RESPONSE; response->remote_address = NULL; response->local_address = NULL; response->length = 0; response->notify_address = qcur->notify_address; /* barrier response is a header-only message */ _mq_push(qcur->world_rank, (char*)response, sizeof(header_t)); _make_progress_if_needed(); last = qcur; qcur = qcur->next; _my_free(last); } else { if (new_bq_tail) { assert(NULL == new_bq_tail->next); assert(new_bq_head); new_bq_tail->next = qcur; new_bq_tail = qcur; } else { assert(NULL == new_bq_head); new_bq_head = qcur; new_bq_tail = qcur; } qcur = qcur->next; new_bq_tail->next = NULL; } } l_state.bq_head = new_bq_head; l_state.bq_tail = new_bq_tail; request_received_count = 0; for (p=0; poperation = op; header->remote_address = prem; header->local_address = ploc; header->length = length; header->notify_address = notify; /* set the value to wait on for a rmw response */ *notify = 1; message = _my_malloc(sizeof(header_t) + length); _my_memcpy(message, header, sizeof(header_t)); _my_memcpy(message+sizeof(header_t), payload, length); _mq_push(proc, (char*)message, sizeof(header_t)+length); while (*notify > 0) { comex_make_progress(); #if DEBUG printf("[%d] are we stuck? rmw notify=%d\n", l_state.rank, (int)*notify); #endif } _my_free(notify); _my_free(payload); _my_free(header); return COMEX_SUCCESS; } static void _fetch_and_add_request_handler(header_t *request_header, char *payload, int proc) { char *message; #if DEBUG printf("[%d] _fetch_and_add_request_handler proc=%d\n", l_state.rank, proc); #endif #if DEBUG printf("[%d] request_header rem=%p loc=%p len=%d not=%p\n", l_state.rank, request_header->remote_address, request_header->local_address, request_header->length, request_header->notify_address); #endif assert(OP_FETCH_AND_ADD_REQUEST == request_header->operation); /* reuse the header, just change the OP */ /* NOTE: it gets deleted anyway when we return from this handler */ request_header->operation = OP_FETCH_AND_ADD_RESPONSE; /* fetch */ message = _my_malloc(sizeof(header_t) + request_header->length); _my_memcpy(message, request_header, sizeof(header_t)); _my_memcpy(message+sizeof(header_t), request_header->remote_address, request_header->length); /* "add" */ if (sizeof(int) == request_header->length) { *((int*)request_header->remote_address) += *((int*)payload); } else if (sizeof(long) == request_header->length) { *((long*)request_header->remote_address) += *((long*)payload); } else { assert(0); } _mq_push(proc, message, sizeof(header_t) + request_header->length); } static void _fetch_and_add_response_handler(header_t *header, char *payload) { #if DEBUG printf("[%d] _fetch_and_add_response_handler rem=%p loc=%p len=%d not=%p\n", l_state.rank, header->remote_address, header->local_address, header->length, header->notify_address); #endif assert(OP_FETCH_AND_ADD_RESPONSE == header->operation); _my_memcpy(header->local_address, payload, header->length); *((char*)(header->notify_address)) = 0; } static void _swap_request_handler(header_t *header, char *payload, int proc) { char *message = NULL; void *tmp = NULL; #if DEBUG printf("[%d] _swap_request rem=%p loc=%p len=%d not=%p\n", l_state.rank, header->remote_address, header->local_address, header->length, header->notify_address); #endif assert(OP_SWAP_REQUEST == header->operation); assert(header->length > 0); /* need a temporary for the swap */ tmp = _my_malloc(header->length); _my_memcpy(tmp, payload, header->length); _my_memcpy(payload, header->remote_address, header->length); _my_memcpy(header->remote_address, tmp, header->length); _my_free(tmp); /* reuse the header, just change the OP */ /* NOTE: it gets deleted anyway when we return from this handler */ header->operation = OP_SWAP_RESPONSE; message = _my_malloc(sizeof(header_t) + header->length); _my_memcpy(message, header, sizeof(header_t)); _my_memcpy(message+sizeof(header_t), payload, header->length); _mq_push(proc, message, sizeof(header_t) + header->length); } static void _swap_response_handler(header_t *header, char *payload) { #if DEBUG printf("[%d] _swap_response_handler rem=%p loc=%p len=%d not=%p\n", l_state.rank, header->remote_address, header->local_address, header->length, header->notify_address); #endif assert(OP_SWAP_RESPONSE == header->operation); _my_memcpy(header->local_address, payload, header->length); *((char*)(header->notify_address)) = 0; } static void _lock_request_handler(header_t *header, int proc) { #if DEBUG printf("[%d] _lock_request_handler id=%d proc=%d\n", l_state.rank, header->length, proc); #endif _lq_push(proc, header->length, header->notify_address); } static void _lock_response_handler(header_t *header) { #if DEBUG printf("[%d] _lock_response_handler id=%d\n", l_state.rank, header->length); #endif *((char*)header->notify_address) = 0; } static void _unlock_request_handler(header_t *header, int proc) { #if DEBUG printf("[%d] _unlock_request_handler id=%d proc=%d\n", l_state.rank, header->length, proc); #endif assert(header->length >= 0); assert(header->length < l_state.num_mutexes); /* make sure the unlock request came from the lock holder */ assert(l_state.mutexes[header->length] == proc); l_state.mutexes[header->length] = -1; } static void _lq_push(int rank, int id, char *notify) { comex_lock_t *lock = NULL; #if DEBUG printf("[%d] _lq_push rank=%d id=%d\n", l_state.rank, rank, id); #endif lock = _my_malloc(sizeof(comex_lock_t)); lock->next = NULL; lock->rank = rank; lock->id = id; lock->notify_address = notify; if (l_state.lq_tail) { #if DEBUG printf("[%d] _lq_push rank=%d id=%d to tail\n", l_state.rank, rank, id); #endif assert(NULL == l_state.lq_tail->next); l_state.lq_tail->next = lock; l_state.lq_tail = lock; } else { #if DEBUG printf("[%d] _lq_push rank=%d id=%d to head\n", l_state.rank, rank, id); #endif assert(NULL == l_state.lq_head); l_state.lq_head = lock; l_state.lq_tail = lock; } } static int _lq_progress(void) { int needs_progress = 0; comex_lock_t *lock = NULL; comex_lock_t *new_lock_head = NULL; comex_lock_t *new_lock_tail = NULL; #if DEBUG if (l_state.num_mutexes > 0) { printf("[%d] _lq_progress mutex[0]=%d\n", l_state.rank, l_state.mutexes[0]); } else { //printf("[%d] _lq_progress no mutexes\n", l_state.rank); } #endif lock = l_state.lq_head; while (lock) { if (l_state.mutexes[lock->id] < 0) { comex_lock_t *last = NULL; header_t *header = NULL; l_state.mutexes[lock->id] = lock->rank; #if DEBUG printf("[%d] _lq_progress rank=%d now holds lock %d\n", l_state.rank, lock->rank, lock->id); #endif header = _my_malloc(sizeof(header_t)); header->operation = OP_LOCK_RESPONSE; header->remote_address = NULL; header->local_address = NULL; header->length = -1; header->notify_address = lock->notify_address; _mq_push(lock->rank, (char*)header, sizeof(header_t)); last = lock; lock = lock->next; _my_free(last); needs_progress = 1; } else { #if DEBUG printf("[%d] _lq_progress rank=%d could not acquire lock %d\n", l_state.rank, lock->rank, lock->id); #endif if (new_lock_tail) { assert(new_lock_head); assert(NULL == new_lock_tail->next); new_lock_tail->next = lock; new_lock_tail = lock; } else { assert(NULL == new_lock_head); new_lock_head = lock; new_lock_tail = lock; } lock = lock->next; new_lock_tail->next = NULL; } } l_state.lq_head = new_lock_head; l_state.lq_tail = new_lock_tail; return needs_progress; } /* Mutex Operations */ int comex_create_mutexes(int num) { int i=0; assert(0 <= num); assert(NULL == l_state.mutexes); assert(0 == l_state.num_mutexes); assert(NULL == l_state.lq_head); assert(NULL == l_state.lq_tail); l_state.num_mutexes = num; if (num > 0) { /* create all of the mutexes */ l_state.mutexes = (int*)_my_malloc(num * sizeof(int)); assert(l_state.mutexes); /* init all of my mutexes to unlocked */ for (i=0; i= 0) { ++number_of_outstanding_locks; } } } while (l_state.lq_head || number_of_outstanding_locks > 0); assert(NULL == l_state.lq_head); assert(NULL == l_state.lq_tail); #ifndef NDEBUG { int i; for (i=0; ioperation = OP_LOCK_REQUEST; header->remote_address = NULL; header->local_address = NULL; header->length = mutex; header->notify_address = notify; _mq_push(proc, (char*)header, sizeof(header_t)); while (*notify) { #if DEBUG printf("notify=%d\n", (int)*notify); #endif comex_make_progress(); } _my_free(notify); return COMEX_SUCCESS; } int comex_unlock(int mutex, int proc) { #if DEBUG printf("[%d] comex_unlock id=%d proc=%d\n", l_state.rank, mutex, proc); #endif header_t *header = NULL; header = _my_malloc(sizeof(header_t)); header->operation = OP_UNLOCK; header->remote_address = NULL; header->local_address = NULL; header->length = mutex; header->notify_address = NULL; _mq_push(proc, (char*)header, sizeof(header_t)); _make_progress_if_needed(); return COMEX_SUCCESS; } int comex_malloc(void **ptrs, size_t size, comex_group_t group) { comex_igroup_t *igroup = NULL; MPI_Comm comm = MPI_COMM_NULL; int comm_rank = -1; int rc = MPI_SUCCESS; #if DEBUG printf("[%d] comex_malloc_group(ptrs=%p, size=%ld, ...)\n", l_state.rank, ptrs, size); #endif /* preconditions */ assert(ptrs); assert(group >= 0); igroup = comex_get_igroup_from_group(group); comm = igroup->comm; assert(comm != MPI_COMM_NULL); MPI_Comm_rank(comm, &comm_rank); /* allocate and register segment */ ptrs[comm_rank] = comex_malloc_local(sizeof(char)*size); /* exchange buffer address */ comex_barrier(group); /* end ARMCI epoch, enter MPI epoch */ rc = MPI_Allgather(MPI_IN_PLACE, 0, MPI_CHAR, ptrs, SIZEOF_VOIDP, MPI_CHAR, comm); assert(MPI_SUCCESS == rc); #if DEBUG { int i; int size; MPI_Comm_size(comm, &size); for (i=0; i= 0); igroup = comex_get_igroup_from_group(group); comm = igroup->comm; /* remove my ptr from reg cache and free ptr */ comex_free_local(ptr); /* Synchronize: required by ARMCI semantics */ comex_barrier(group); return COMEX_SUCCESS; } int comex_free_dev(void *ptr, comex_group_t group) { return comex_free(ptr, group); } ga-5.9.2/comex/src-mpi/comex_impl.h000066400000000000000000000036111500715745200171170ustar00rootroot00000000000000#ifndef COMEX_IMPL_H_ #define COMEX_IMPL_H_ #include //#define MAX_NB_OUTSTANDING 1024 #define MAX_NB_OUTSTANDING 8 #define COMEX_TAG 27624 typedef enum { OP_PUT = 0, OP_GET_REQUEST, OP_GET_RESPONSE, OP_ACC_INT, OP_ACC_DBL, OP_ACC_FLT, OP_ACC_CPL, OP_ACC_DCP, OP_ACC_LNG, OP_FENCE_REQUEST, OP_FENCE_RESPONSE, OP_BARRIER_REQUEST, OP_BARRIER_RESPONSE, OP_FETCH_AND_ADD_REQUEST, OP_FETCH_AND_ADD_RESPONSE, OP_SWAP_REQUEST, OP_SWAP_RESPONSE, OP_LOCK_REQUEST, OP_LOCK_RESPONSE, OP_UNLOCK, } op_t; typedef struct { op_t operation; void *remote_address; void *local_address; int length; /**< length of message/payload not including header */ void *notify_address; } header_t; typedef struct message_link { struct message_link *next; int dest; char *message; MPI_Request request; } message_t; typedef struct get_link { struct get_link *next; char *notify_address; } get_t; typedef struct barrier_link { struct barrier_link *next; int world_rank; void *notify_address; } barrier_t; typedef struct lock_link { struct lock_link *next; int rank; int id; void *notify_address; } comex_lock_t; typedef struct { MPI_Comm world_comm; int rank; int size; /* buffers for locks */ int *mutexes; /**< local mutexes */ int num_mutexes; /**< how many mutexes on this process */ /* a queue for outgoing messages */ int mq_size; message_t *mq_head; message_t *mq_tail; /* a queue for get notifications */ get_t *gq_head; get_t *gq_tail; /* a queue for barrier requests */ barrier_t *bq_head; barrier_t *bq_tail; /* a queue for lock requests */ comex_lock_t *lq_head; comex_lock_t *lq_tail; } local_state; extern local_state l_state; extern void comex_make_progress(void); #endif /* COMEX_IMPL_H_ */ ga-5.9.2/comex/src-mpi/groups.c000066400000000000000000000206671500715745200163070ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include "comex.h" #include "comex_impl.h" #include "groups.h" /* the HEAD of the group linked list */ comex_igroup_t *group_list = NULL; /* static functions implemented in this file */ static void comex_create_group_and_igroup( comex_group_t *id, comex_igroup_t **igroup); static void comex_igroup_finalize(comex_igroup_t *igroup); /** * Return the comex igroup instance given the group id. * * The group linked list is searched sequentially until the given group * is found. It is an error if this function is called before * comex_group_init(). An error occurs if the given group is not found. */ comex_igroup_t* comex_get_igroup_from_group(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; assert(group_list != NULL); while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { return current_group_list_item; } current_group_list_item = current_group_list_item->next; } comex_error("comex group lookup failed", -1); return NULL; } /** * Creates and associates an comex group with an comex igroup. * * This does *not* initialize the members of the comex igroup. */ static void comex_create_group_and_igroup( comex_group_t *id, comex_igroup_t **igroup) { comex_igroup_t *new_group_list_item = NULL; comex_igroup_t *last_group_list_item = NULL; /* find the last group in the group linked list */ last_group_list_item = group_list; while (last_group_list_item->next != NULL) { last_group_list_item = last_group_list_item->next; } /* create, init, and insert the new node for the linked list */ new_group_list_item = malloc(sizeof(comex_igroup_t)); new_group_list_item->id = last_group_list_item->id + 1; new_group_list_item->comm = MPI_COMM_NULL; new_group_list_item->group = MPI_GROUP_NULL; new_group_list_item->next = NULL; last_group_list_item->next = new_group_list_item; /* return the group id and comex igroup */ *igroup = new_group_list_item; *id = new_group_list_item->id; } int comex_group_rank(comex_group_t group, int *rank) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); status = MPI_Group_rank(igroup->group, rank); if (status != MPI_SUCCESS) { comex_error("MPI_Group_rank: Failed ", status); } return COMEX_SUCCESS; } int comex_group_size(comex_group_t group, int *size) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); status = MPI_Group_size(igroup->group, size); if (status != MPI_SUCCESS) { comex_error("MPI_Group_size: Failed ", status); } return COMEX_SUCCESS; } int comex_group_comm(comex_group_t group, MPI_Comm *comm) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); *comm = igroup->comm; return COMEX_SUCCESS; } int comex_group_translate_world(comex_group_t group, int group_rank, int *world_rank) { if (COMEX_GROUP_WORLD == group) { *world_rank = group_rank; } else { comex_igroup_t *igroup = comex_get_igroup_from_group(group); comex_igroup_t *world_igroup = comex_get_igroup_from_group(COMEX_GROUP_WORLD); int status = MPI_Group_translate_ranks( igroup->group, 1, &group_rank, world_igroup->group, world_rank); if (status != MPI_SUCCESS) { comex_error("MPI_Group_translate_ranks: Failed ", status); } } return COMEX_SUCCESS; } /** * Destroys the given comex igroup. */ static void comex_igroup_finalize(comex_igroup_t *igroup) { int status; assert(igroup); if (igroup->group != MPI_GROUP_NULL) { status = MPI_Group_free(&igroup->group); if (status != MPI_SUCCESS) { comex_error("MPI_Group_free: Failed ", status); } } if (igroup->comm != MPI_COMM_NULL) { status = MPI_Comm_free(&igroup->comm); if (status != MPI_SUCCESS) { comex_error("MPI_Comm_free: Failed ", status); } } } int comex_group_free(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; /* find the group to free */ while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { break; } previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; } /* make sure we found a group */ assert(current_group_list_item != NULL); /* remove the group from the linked list */ if (previous_group_list_item != NULL) { previous_group_list_item->next = current_group_list_item->next; } /* free the group */ comex_igroup_finalize(current_group_list_item); free(current_group_list_item); return COMEX_SUCCESS; } int comex_group_create( int n, int *pid_list, comex_group_t id_parent, comex_group_t *id_child) { int status; int grp_me; comex_igroup_t *igroup_child = NULL; MPI_Group *group_child = NULL; MPI_Comm *comm_child = NULL; comex_igroup_t *igroup_parent = NULL; MPI_Group *group_parent = NULL; MPI_Comm *comm_parent = NULL; /* create the node in the linked list of groups and */ /* get the child's MPI_Group and MPI_Comm, to be populated shortly */ comex_create_group_and_igroup(id_child, &igroup_child); group_child = &(igroup_child->group); comm_child = &(igroup_child->comm); /* get the parent's MPI_Group and MPI_Comm */ igroup_parent = comex_get_igroup_from_group(id_parent); group_parent = &(igroup_parent->group); comm_parent = &(igroup_parent->comm); status = MPI_Group_incl(*group_parent, n, pid_list, group_child); if (status != MPI_SUCCESS) { comex_error("MPI_Group_incl: Failed ", status); } { MPI_Comm comm, comm1, comm2; int lvl=1, local_ldr_pos; MPI_Group_rank(*group_child, &grp_me); if (grp_me == MPI_UNDEFINED) { /* FIXME: keeping the group around for now */ return COMEX_SUCCESS; } /* SK: sanity check for the following bitwise operations */ assert(grp_me>=0); MPI_Comm_dup(MPI_COMM_SELF, &comm); /* FIXME: can be optimized away */ local_ldr_pos = grp_me; while(n>lvl) { int tag=0; int remote_ldr_pos = local_ldr_pos^lvl; if (remote_ldr_pos < n) { int remote_leader = pid_list[remote_ldr_pos]; MPI_Comm peer_comm = *comm_parent; int high = (local_ldr_posid = COMEX_GROUP_WORLD; group_list->next = NULL; /* save MPI world group and communicatior in COMEX_GROUP_WORLD */ group_list->comm = l_state.world_comm; MPI_Comm_group(group_list->comm, &(group_list->group)); } void comex_group_finalize() { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; /* don't free the world group (the list head) */ current_group_list_item = current_group_list_item->next; while (current_group_list_item != NULL) { previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; comex_igroup_finalize(previous_group_list_item); free(previous_group_list_item); } /* ok, now free the world group, but not the world comm */ MPI_Group_free(&(group_list->group)); free(group_list); group_list = NULL; } ga-5.9.2/comex/src-mpi/groups.h000066400000000000000000000027611500715745200163070ustar00rootroot00000000000000/** * Private header file for comex groups backed by MPI_comm. * * The rest of the comex group functions are defined in the public comex.h. * These are the private group functions. * * @author Jeff Daily */ #ifndef _COMEX_GROUPS_H_ #define _COMEX_GROUPS_H_ #include #include "comex.h" typedef struct group_link { struct group_link *next; comex_group_t id; MPI_Comm comm; MPI_Group group; } comex_igroup_t; extern void comex_group_init(); extern void comex_group_finalize(); extern comex_igroup_t* comex_get_igroup_from_group(comex_group_t group); /* verify that proc is part of group * translate proc to world group * change group to world group */ #define CHECK_GROUP(GROUP,PROC) do { \ int size; \ int ierr = comex_group_size(GROUP,&size); \ assert(COMEX_SUCCESS == ierr); \ assert(PROC >= 0); \ assert(PROC < size); \ if (COMEX_GROUP_WORLD != GROUP) { \ int world_proc; \ comex_group_translate_world(GROUP, PROC, &world_proc); \ PROC = world_proc; \ GROUP = COMEX_GROUP_WORLD; \ } \ } while(0) #endif /* _COMEX_GROUPS_H_ */ ga-5.9.2/comex/src-mpi3/000077500000000000000000000000001500715745200146745ustar00rootroot00000000000000ga-5.9.2/comex/src-mpi3/Makefile.inc000066400000000000000000000004441500715745200171060ustar00rootroot00000000000000libcomex_la_SOURCES += src-mpi3/comex.c libcomex_la_SOURCES += src-mpi3/comex_impl.h libcomex_la_SOURCES += src-mpi3/groups.c libcomex_la_SOURCES += src-mpi3/groups.h libcomex_la_SOURCES += src-mpi3/reg_win.c libcomex_la_SOURCES += src-mpi3/reg_win.h AM_CPPFLAGS += -I$(top_srcdir)/src-mpi3 ga-5.9.2/comex/src-mpi3/NOTES.md000066400000000000000000000055651500715745200161210ustar00rootroot00000000000000These are notes describing the MPI RMA runtime. These notes are intended to help developers navigate the contents of these files and to locate specific functionality. The files [groups.h](groups.h) and [groups.c](groups.c) contain functionality that describes the relation between groups in GA (which are essentially equivalent to MPI communicators) and MPI windows. Each global array in GA has its own MPI window associated with it and and any communication involving the global array must use its corresponding window. On the other hand, collective operations in GA, particularly sync, are defined on groups. To implement sync, each group must have a list of all windows that are associated with it and there must be functionality available to manage the association of windows with the group as global arrays are created and destroyed. Most of the code that supports this association is located in these two files. [reg_win.h](reg_win.h) and [reg_win.c](reg_win.c) contain code for finding the window corresponding to a point in registered memory. When a global array is created, a `reg_entry_t` struct is created for each processor in the group on which the global array is defined. These structs are grouped into a link list so that the global array can determine where on a remote processor the data allocated for the global array resides. The functions in these files allow you to identify which window contains a given pointer. It allows conversion between the pointers used in ARMCI and the integer offsets used in MPI RMA calls. The remainder of the code is located in [comex.c](comex.c), with a few type declarations in [comex_impl.h](comex_impl.h). The comex.c code is has a number of preprocessor declarations that can be used to investigate the performance of different implemenations of the individual ComEx operations. The USE_MPI_DATATYPES symbol uses MPI Datatypes to send strided and vector data instead of decomposing the request into multiple contiguous data transfers. This option should be used if at all possible, it represents a substantial performance boost over sending multiple individual messages. If the USE_MPI_REQUESTS variable is defined then request based calls are used for all ComEx one-sided operations. These calls are cleared using the `MPI_Wait` function. If USE_MPI_FLUSH_LOCAL is defined, then local completion of the call is accomplished by using the `MPI_Win_flush_local` function. If neither of these calls is used, then `MPI_Win_lock` and `MPI_Win_unlock` are used to guarantee progress for one-sided operations. The request and flush-based protocols use `MPI_Win_lock_all` on the window that is created for each GA to create a passive synchronization epoch for each window. Both the request-basted and flush-based protocols support true non-blocking operations, for the lock/unlock protocol non-blocking operations default to blocking operations and the GA wait function is a no-op. ga-5.9.2/comex/src-mpi3/comex.c000066400000000000000000003404031500715745200161570ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* C and/or system headers */ #include #include #include #include #include #include /* 3rd party headers */ #include #if USE_SICM #include //#include #endif /* our headers */ #include "comex.h" #include "comex_impl.h" #include "groups.h" #include "reg_win.h" #define DEBUG 0 #define XSTR(x) #x #define STR(x) XSTR(x) /* #define USE_PRIOR_MPI_WIN_FLUSH #define USE_POST_MPI_WIN_FLUSH */ /* */ #define USE_MPI_DATATYPES #define USE_MPI_REQUESTS /* #define USE_MPI_FLUSH_LOCAL #define USE_MPI_WIN_ALLOC */ #ifdef USE_MPI_FLUSH_LOCAL #define USE_MPI_REQUESTS #endif #define XSTR(x) #x #define STR(x) XSTR(x) #if USE_SICM static sicm_device_list devices = {0}; #if SICM_OLD static sicm_device *device_dram = NULL; static sicm_device *device_knl_hbm = NULL; static sicm_device *device_ppc_hbm = NULL; #else static sicm_device_list device_dram = {0}; static sicm_device_list device_knl_hbm = {0}; static sicm_device_list device_ppc_hbm = {0}; #endif #endif /* exported state */ local_state l_state; /* static state */ static int initialized=0; /* for comex_initialized(), 0=false */ static char skip_lock=0; /* don't acquire or release lock */ /* static function declarations */ static void acquire_remote_lock(int proc); static void release_remote_lock(int proc); static inline void acc( int datatype, int count, void *get_buf, void *src_ptr, long src_idx, void *scale); /* definitions needed to implement mutexes */ MPI_Win *_mutex_list; int **_mutex_buf; int *_mutex_num; int _mutex_total; /* Maximum number of outstanding non-blocking requests */ static int nb_max_outstanding = COMEX_MAX_NB_OUTSTANDING; typedef struct { MPI_Request request; MPI_Win win; int use_type; MPI_Datatype src_type; MPI_Datatype dst_type; int active; #ifdef USE_MPI_FLUSH_LOCAL int remote_proc; #else int remote_proc; #endif } nb_t; static nb_t **nb_list = NULL; /* needed for complex accumulate */ typedef struct { double real; double imag; } DoubleComplex; typedef struct { float real; float imag; } SingleComplex; static int nb_full_count = 0; /* Find first available non-blocking handle */ #ifdef USE_MPI_REQUESTS void get_nb_request(comex_request_t *handle, nb_t **req) { int i; for (i=0; iactive == 0) break; } if (igroup, 1, &world_rank, group, local_rank); if (status != MPI_SUCCESS) { translate_mpi_error(status,"get_local_rank_from_win:MPI_Group_translate_ranks"); comex_error("MPI_Group_translate_ranks: Failed", status); } return COMEX_SUCCESS; } int _comex_init(MPI_Comm comm) { int i, status; int init_flag; if (initialized) { return 0; } initialized = 1; /* Assert MPI has been initialized */ status = MPI_Initialized(&init_flag); assert(MPI_SUCCESS == status); assert(init_flag); /* Duplicate the World Communicator */ status = MPI_Comm_dup(comm, &(l_state.world_comm)); translate_mpi_error(status,"comex_init:MPI_Comm_dup"); assert(MPI_SUCCESS == status); assert(l_state.world_comm); /* My Rank */ status = MPI_Comm_rank(l_state.world_comm, &(l_state.rank)); assert(MPI_SUCCESS == status); /* World Size */ status = MPI_Comm_size(l_state.world_comm, &(l_state.size)); assert(MPI_SUCCESS == status); /* Pick up environment variables */ { int armci_verbose; int me; char *value = NULL; nb_max_outstanding = COMEX_MAX_NB_OUTSTANDING; /* default */ value = getenv("COMEX_MAX_NB_OUTSTANDING"); if (NULL != value) { nb_max_outstanding = atoi(value); } COMEX_ASSERT(nb_max_outstanding > 0); #if DEBUG armci_verbose = 1; #else armci_verbose = 0; #endif value = getenv("ARMCI_VERBOSE"); if (NULL != value) { armci_verbose = atoi(value); } if (armci_verbose && 0 == l_state.rank) { printf("COMEX_MAX_NB_OUTSTANDING=%d\n", nb_max_outstanding); fflush(stdout); } } /* groups */ comex_group_init(); /* register windows initialization */ reg_win_init(l_state.size); /* set mutex list equal to null */ _mutex_list = NULL; _mutex_buf = NULL; _mutex_num = NULL; _mutex_total = 0; /* initialize non-blocking handles */ #ifdef USE_MPI_REQUESTS nb_list = (nb_t**)malloc(sizeof(nb_t*) * nb_max_outstanding); COMEX_ASSERT(nb_list); for (i=0; iactive = 0; } #endif #if USE_SICM devices = sicm_init(); #if SICM_OLD for(i = 0; i < devices.count; i++){ if(devices.devices[i].tag == SICM_DRAM){ device_dram = &(devices.devices[i]); } if(devices.devices[i].tag == SICM_KNL_HBM){ device_knl_hbm = &(devices.devices[i]); } if(devices.devices[i].tag == SICM_POWERPC_HBM){ device_ppc_hbm = &(devices.devices[i]); } } if(!device_dram){ printf("Device DRAM not found\n"); exit(18); } #else for(i =0; i < devices.count; ++i){ sicm_device *curr = devices.devices[i]; if(curr->tag == SICM_DRAM){ device_dram.count = 1; device_dram.devices = &devices.devices[i]; } if (curr->tag == SICM_KNL_HBM) { device_knl_hbm.count = 1; device_knl_hbm.devices = &devices.devices[i]; } if (curr->tag == SICM_POWERPC_HBM) { device_ppc_hbm.count = 1; device_ppc_hbm.devices = &devices.devices[i]; } } if(device_dram.devices == NULL){ printf("Device DRAM not found\n"); exit(18); } #endif #endif /* sync - initialize first communication epoch */ comex_fence_all(COMEX_GROUP_WORLD); /* Synch - Sanity Check */ MPI_Barrier(l_state.world_comm); return COMEX_SUCCESS; } int comex_init() { return _comex_init(MPI_COMM_WORLD); } int comex_init_comm(MPI_Comm comm) { return _comex_init(comm); } int comex_init_args(int *argc, char ***argv) { int init_flag; MPI_Initialized(&init_flag); if(!init_flag) { MPI_Init(argc, argv); } return comex_init(); } int comex_initialized() { return initialized; } void comex_error(const char *msg, int code) { fprintf(stderr,"[%d] Received an Error in Communication: (%d) %s\n", l_state.rank, code, msg); MPI_Abort(l_state.world_comm, code); } /** * @param src pointer to source buffer * @param dst pointer to destination (remote) buffer * @param bytes length of data to be transferred * @param proc rank of destination processors * @param group handle for group that transfer is defined on */ int comex_put( void *src, void *dst, int bytes, int proc, comex_group_t group) { MPI_Aint displ; void *ptr; int lproc, ierr; reg_entry_t *reg_win; #ifdef USE_MPI_REQUESTS MPI_Request request; MPI_Status status; #endif reg_win = reg_win_find(proc, dst, 0); ptr = reg_win->buf; displ = (MPI_Aint)(dst) - (MPI_Aint)(ptr); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } /** * All processes called MPI_WIN_LOCK_ALL on the reg_win->win window used for * these calls. This call: * * Starts an RMA access epoch to all processors in reg_win->win with a lock * type of MPI_LOCK_SHARED. During the epoch, the calling process can access * the window memory on all processes in reg_win->win using RMA operations. * A window locked with MPI_WIN_LOCK_ALL must be unlocked with * MPI_WIN_UNLOCK_ALL. This routine is not collective. The ALL refers to a * lock on all members of the group of the window. * * Locks are used to protect accesses to the locked target window effected * by RMA calls issued between the lock and unlock calls, and to protect * load/store accesses to a locked local or shared memory window executed * between the lock and unlock calls. Accesses that are protected by an * exclusive lock will not be concurrent at the window site with other * accesses to the same window that are lock protected. Accesses that are * protected by a shared lock will not be concurrent at the window site with * accesses protected by an exclusive lock to the same window. * * A passive synchronization epoch is created by calling MPI_WIN_LOCK_ALL on * reg_win->win. */ /** * MPI_WIN_FLUSH completes all outstanding RMA operations initiated by the * calling process to the target rank on the specified window. The * operations are completed both at the origin and at the target */ #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_put:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL /** * The local communication buffer of an RMA call should not be updated, and * the local communication buffer of a get call should not be accessed after * the RMA call until the operation completes at the origin. */ ierr = MPI_Put(src, bytes, MPI_CHAR, lproc, displ, bytes, MPI_CHAR, reg_win->win); translate_mpi_error(ierr,"comex_put:MPI_Put"); /** * MPI_WIN_FLUSH_LOCAL locally completes at the origin all outstanding RMA * operations initiated by the calling process to the target process * specified by the rank on the specified window. For example, after the * routine completes, the user may reused any buffers provided to put, get, * or accumulate operations */ ierr = MPI_Win_flush_local(lproc, reg_win->win); translate_mpi_error(ierr,"comex_put:MPI_Win_flush_local"); #else /** * MPI_RPUT is similar to MPI_PUT, except that it allocates a communication * request object and associates it with the request handle. The completion * of an MPI_RPUT operation (i.e. after the corresponding test or wait) * indicates that the sender is now free to update the locations in the * origin buffer. It does not indicate that the data is available at the * target window. If remote completion is required, MPI_WIN_FLUSH, * MPI_WIN_FLUSH_ALL, MPI_WIN_UNLOCK or MPI_WIN_UNLOCK_ALL can be used. */ ierr = MPI_Rput(src, bytes, MPI_CHAR, lproc, displ, bytes, MPI_CHAR, reg_win->win, &request); translate_mpi_error(ierr,"comex_put:MPI_Rput"); /** * A call to MPI_WAIT returns when the operation identified by the request * is complete. If the request is an active persistent request, it is marked * inactive. Any other type of request is and the request handle is set to * MPI_REQUEST_NULL. MPI_WAIT is a non-local operation */ ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"comex_put:MPI_Wait"); #ifdef USE_POST_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc,reg_win->win); translate_mpi_error(ierr,"comex_put:MPI_Win_flush"); #endif #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,lproc,0,reg_win->win); translate_mpi_error(ierr,"comex_put:MPI_Win_lock"); ierr = MPI_Put(src, bytes, MPI_CHAR, lproc, displ, bytes, MPI_CHAR, reg_win->win); translate_mpi_error(ierr,"comex_put:MPI_Put"); ierr = MPI_Win_unlock(lproc,reg_win->win); translate_mpi_error(ierr,"comex_put:MPI_Win_unlock"); #endif return COMEX_SUCCESS; } /** * @param src pointer to source (remote) buffer * @param dst pointer to destination buffer * @param bytes length of data to be transferred * @param proc rank of destination processors * @param group handle for group that transfer is defined on */ int comex_get( void *src, void *dst, int bytes, int proc, comex_group_t group) { MPI_Aint displ; void *ptr; int lproc, ierr; reg_entry_t *reg_win; #ifdef USE_MPI_REQUESTS MPI_Request request; MPI_Status status; #endif reg_win = reg_win_find(proc, src, 0); ptr = reg_win->buf; displ = (MPI_Aint)(src) - (MPI_Aint)(ptr); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_get:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Get(dst, bytes, MPI_CHAR, lproc, displ, bytes, MPI_CHAR, reg_win->win); translate_mpi_error(ierr,"comex_get:MPI_Get"); ierr = MPI_Win_flush_local(lproc, reg_win->win); translate_mpi_error(ierr,"comex_get:MPI_Win_flush_local"); #else ierr = MPI_Rget(dst, bytes, MPI_CHAR, lproc, displ, bytes, MPI_CHAR, reg_win->win, &request); translate_mpi_error(ierr,"comex_get:MPI_Rget"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"comex_get:MPI_Wait"); #ifdef USE_POST_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc,reg_win->win); translate_mpi_error(ierr,"comex_get:MPI_Win_flush"); #endif #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,lproc,0,reg_win->win); translate_mpi_error(ierr,"comex_get:MPI_Win_lock"); ierr = MPI_Get(dst, bytes, MPI_CHAR, lproc, displ, bytes, MPI_CHAR, reg_win->win); translate_mpi_error(ierr,"comex_get:MPI_Get"); ierr = MPI_Win_unlock(lproc,reg_win->win); translate_mpi_error(ierr,"comex_get:MPI_Win_unlock"); #endif return COMEX_SUCCESS; } /** * @param datatype COMEX datatype for accumulate operation * @param scale value to scale data by before accumulating it * @param src pointer to source buffer * @param dst pointer to destination (remote) buffer * @param bytes length of data to be transferred * @param proc rank of destination processors * @param group handle for group that transfer is defined on */ int comex_acc( int datatype, void *scale, void *src, void *dst, int bytes, int proc, comex_group_t group) { MPI_Aint displ; void *ptr, *tbuf; int count, i, lproc, ierr; reg_entry_t *reg_win; #ifdef USE_MPI_REQUESTS MPI_Request request; MPI_Status status; #endif MPI_Datatype mpi_type; reg_win = reg_win_find(proc, dst, 0); ptr = reg_win->buf; displ = (MPI_Aint)(dst) - (MPI_Aint)(ptr); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } if (datatype == COMEX_ACC_INT) { int *buf; int *isrc = (int*)src; int iscale = *((int*)scale); count = bytes/sizeof(int); buf = (int*)malloc(bytes); tbuf = buf; for (i=0; iwin); translate_mpi_error(ierr,"comex_acc:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Accumulate(tbuf,count,mpi_type,lproc,displ,count, mpi_type,MPI_SUM,reg_win->win); translate_mpi_error(ierr,"comex_acc:MPI_Accumulate"); ierr = MPI_Win_flush_local(lproc, reg_win->win); translate_mpi_error(ierr,"comex_acc:MPI_Win_flush_local"); #else ierr = MPI_Raccumulate(tbuf,count,mpi_type,lproc,displ,count, mpi_type,MPI_SUM,reg_win->win,&request); translate_mpi_error(ierr,"comex_acc:MPI_Raccumulate"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"comex_acc:MPI_Wait"); #ifdef USE_POST_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc,reg_win->win); translate_mpi_error(ierr,"comex_acc:MPI_Win_flush"); #endif #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,lproc,0,reg_win->win); translate_mpi_error(ierr,"comex_acc:MPI_Win_lock"); ierr = MPI_Accumulate(tbuf,count,mpi_type,lproc,displ,count, mpi_type,MPI_SUM,reg_win->win); translate_mpi_error(ierr,"comex_acc:MPI_Accumulate"); ierr = MPI_Win_unlock(lproc,reg_win->win); translate_mpi_error(ierr,"comex_acc:MPI_Win_unlock"); #endif free(tbuf); return COMEX_SUCCESS; } /** * No checking for data consistency. Assume correctness has already been * established elsewhere. Individual elements are assumed to be one byte in size * stride_array: physical dimensions of array * count: number of elements along each array dimension * levels: number of stride levels (should be one less than array dimension) * type: MPI_Datatype returned to calling program */ void strided_to_subarray_dtype(int *stride_array, int *count, int levels, MPI_Datatype base_type, MPI_Datatype *type) { int ndims, i, ierr; int array_of_sizes[7]; int array_of_starts[7]; int array_of_subsizes[7]; int stride; ierr = MPI_Type_size(base_type,&stride); translate_mpi_error(ierr,"strided_to_subarray_dtype:MPI_Type_size"); ndims = levels+1; /* the pointer to the local buffer points to the first data element in data exchange, not the origin of the local array, so all starts should be zero */ for (i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } for(j=1; j<=stride_levels; j++) { dst_idx += dst_bvalue[j] * dst_stride_ar[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } status = comex_put((char *)src_ptr + src_idx, (char *)dst_ptr + dst_idx, count[0], proc, group); assert(status == COMEX_SUCCESS); } return COMEX_SUCCESS; #else MPI_Datatype src_type, dst_type; MPI_Aint displ; void *ptr; int lproc, ierr; reg_entry_t *reg_win; #ifdef USE_MPI_REQUESTS MPI_Request request; MPI_Status status; #endif /* If data is contiguous, use comex_put */ if (stride_levels == 0) { return comex_put(src_ptr, dst_ptr, count[0], proc, group); } reg_win = reg_win_find(proc, dst_ptr, 0); ptr = reg_win->buf; displ = (MPI_Aint)(dst_ptr) - (MPI_Aint)(ptr); strided_to_subarray_dtype(src_stride_ar, count, stride_levels, MPI_BYTE, &src_type); strided_to_subarray_dtype(dst_stride_ar, count, stride_levels, MPI_BYTE, &dst_type); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } ierr = MPI_Type_commit(&src_type); translate_mpi_error(ierr,"comex_puts:MPI_Type_commit"); ierr = MPI_Type_commit(&dst_type); translate_mpi_error(ierr,"comex_puts:MPI_Type_commit"); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_puts:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Put(src_ptr, 1, src_type, lproc, displ, 1, dst_type, reg_win->win); translate_mpi_error(ierr,"comex_puts:MPI_Put"); ierr = MPI_Win_flush_local(lproc, reg_win->win); translate_mpi_error(ierr,"comex_puts:MPI_Win_flush_local"); #else ierr = MPI_Rput(src_ptr, 1, src_type, lproc, displ, 1, dst_type, reg_win->win, &request); translate_mpi_error(ierr,"comex_puts:MPI_Rput"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"comex_puts:MPI_Wait"); #ifdef USE_POST_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc,reg_win->win); translate_mpi_error(ierr,"comex_puts:MPI_Win_flush"); #endif #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,lproc,0,reg_win->win); translate_mpi_error(ierr,"comex_puts:MPI_Win_lock"); ierr = MPI_Put(src_ptr, 1, src_type, lproc, displ, 1, dst_type, reg_win->win); translate_mpi_error(ierr,"comex_puts:MPI_Put"); ierr = MPI_Win_unlock(lproc,reg_win->win); translate_mpi_error(ierr,"comex_puts:MPI_Win_unlock"); #endif ierr = MPI_Type_free(&src_type); translate_mpi_error(ierr,"comex_puts:MPI_Type_free"); ierr = MPI_Type_free(&dst_type); translate_mpi_error(ierr,"comex_puts:MPI_Type_free"); return COMEX_SUCCESS; #endif } /** * src_ptr: pointer to origin of data on source processor * dst_ptr: pointer to origin of data on destination processor * src_stride_ar: physical dimensions of array containing source data * dst_strice_ar: physical dimensions of destination array * count: array containing number of data points along each dimension. The 0 * dimension contains the actual length of contiguous segments in bytes (number * of elements times the size of each element). * stride_levels: this should be one less than the dimension of the array * proc: global rank of source array * group: ID of group over which transfer takes place */ int comex_gets( void *src_ptr, int *src_stride_ar, void *dst_ptr, int *dst_stride_ar, int *count, int stride_levels, int proc, comex_group_t group) { #ifndef USE_MPI_DATATYPES int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; int status; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } dst_idx = 0; for(j=1; j<=stride_levels; j++) { dst_idx += dst_bvalue[j] * dst_stride_ar[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } status = comex_get((char *)src_ptr + src_idx, (char *)dst_ptr + dst_idx, count[0], proc, group); assert(status == COMEX_SUCCESS); } return COMEX_SUCCESS; #else MPI_Datatype src_type, dst_type; MPI_Aint displ; void *ptr; int lproc,ierr; reg_entry_t *reg_win; #ifdef USE_MPI_REQUESTS MPI_Request request; MPI_Status status; #endif /* If data is contiguous, use comex_get */ if (stride_levels == 0) { return comex_get(src_ptr, dst_ptr, count[0], proc, group); } reg_win = reg_win_find(proc, src_ptr, 0); ptr = reg_win->buf; displ = (MPI_Aint)(src_ptr) - (MPI_Aint)(ptr); strided_to_subarray_dtype(src_stride_ar, count, stride_levels, MPI_BYTE, &src_type); strided_to_subarray_dtype(dst_stride_ar, count, stride_levels, MPI_BYTE, &dst_type); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } ierr = MPI_Type_commit(&src_type); translate_mpi_error(ierr,"comex_gets:MPI_Type_commit"); ierr = MPI_Type_commit(&dst_type); translate_mpi_error(ierr,"comex_gets:MPI_Type_commit"); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_gets:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Get(dst_ptr, 1, dst_type, lproc, displ, 1, src_type, reg_win->win); translate_mpi_error(ierr,"comex_gets:MPI_Get"); ierr = MPI_Win_flush_local(lproc, reg_win->win); translate_mpi_error(ierr,"comex_gets:MPI_Win_flush_local"); #else ierr = MPI_Rget(dst_ptr, 1, dst_type, lproc, displ, 1, src_type, reg_win->win, &request); translate_mpi_error(ierr,"comex_gets:MPI_Rget"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"comex_gets:MPI_Wait"); #ifdef USE_POST_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc,reg_win->win); translate_mpi_error(ierr,"comex_gets:MPI_Win_flush"); #endif #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,lproc,0,reg_win->win); translate_mpi_error(ierr,"comex_gets:MPI_Win_lock"); ierr = MPI_Get(dst_ptr, 1, dst_type, lproc, displ, 1, src_type, reg_win->win); translate_mpi_error(ierr,"comex_gets:MPI_Get"); ierr = MPI_Win_unlock(lproc,reg_win->win); translate_mpi_error(ierr,"comex_gets:MPI_Win_lock"); #endif ierr = MPI_Type_free(&src_type); translate_mpi_error(ierr,"comex_gets:MPI_Type_free"); ierr = MPI_Type_free(&dst_type); translate_mpi_error(ierr,"comex_gets:MPI_Type_free"); return COMEX_SUCCESS; #endif } /** * Utility function for allocating a buffer that can be used to scale strided * data before sending it to remote processor for accumulate operation. Data is * copied into buffer after allocation. * ptr: pointer to first member of data array * strides: physical dimensions of array containing data * count: array containing the number of data points along each dimension. The * 0 element contains the actual length of the contiguous segments in bytes * levels: this should be one less than the dimension of the array * size: size (in bytes) of allocated buffer * new_strides: stride array for newly allocated buffer */ void* malloc_strided_acc_buffer(void* ptr, int *strides, int *count, int levels, int *size, int *new_strides) { #if 0 int i, j, stride, src_idx; void *new_ptr; char *cursor, *src_cursor; int n1dim, src_bvalue[7], src_bunit[7]; *size = 1; /* evaluate size of new array */ for (i=0; i<=levels; i++) { *size *= count[i]; } new_ptr = malloc(*size); cursor = (char*)new_ptr; stride = 1; /* calculate strides in new array */ n1dim = 1; for (i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } src_cursor = (char*)ptr+src_idx; memcpy(cursor, src_cursor, count[0]); cursor += count[0]; } return new_ptr; #else int index[7]; int lcnt[7]; int idx, i, j, stride, src_idx; void *new_ptr; char *cursor, *src_cursor; int n1dim; *size = 1; /* evaluate size of new array */ for (i=0; i<=levels; i++) { *size *= count[i]; } new_ptr = malloc(*size); cursor = (char*)new_ptr; stride = 1; /* calculate strides in new array */ n1dim = 1; for (i=0; i 0) index[levels-1] = idx; /* evaluate offset in source ptr */ src_idx = 0; if (levels > 0) stride = strides[0]; for (j=0; j (count[j]-1)) { src_bvalue[j] = 0; } } dst_idx = 0; for(j=1; j<=stride_levels; j++) { dst_idx += dst_bvalue[j] * dst_stride_ar[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } comex_acc(datatype, scale, (char*)src_ptr+src_idx, (char*)dst_ptr+dst_idx, count[0], proc, group); } return COMEX_SUCCESS; #else MPI_Datatype src_type, dst_type; MPI_Aint displ; void *ptr; int lproc, i, ierr; void *packbuf; int bufsize; int new_strides[7], new_count[7]; reg_entry_t *reg_win; #ifdef USE_MPI_REQUESTS MPI_Request request; MPI_Status status; #endif /* If data is contiguous, use comex_acc */ if (stride_levels == 0) { return comex_acc(datatype, scale, src_ptr, dst_ptr, count[0], proc, group); } reg_win = reg_win_find(proc, dst_ptr, 0); ptr = reg_win->buf; displ = (MPI_Aint)(dst_ptr) - (MPI_Aint)(ptr); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } packbuf = malloc_strided_acc_buffer(src_ptr, src_stride_ar, count, stride_levels, &bufsize, new_strides); for (i=0; iwin); translate_mpi_error(ierr,"comex_accs:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Accumulate(packbuf,1,src_type,lproc,displ,1,dst_type, MPI_SUM,reg_win->win); translate_mpi_error(ierr,"comex_accs:MPI_Accumulate"); ierr = MPI_Win_flush_local(lproc, reg_win->win); translate_mpi_error(ierr,"comex_accs:MPI_Win_flush_local"); #else ierr = MPI_Raccumulate(packbuf,1,src_type,lproc,displ,1,dst_type, MPI_SUM,reg_win->win,&request); translate_mpi_error(ierr,"comex_accs:MPI_Raccumulate"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"comex_accs:MPI_Wait"); #ifdef USE_POST_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc,reg_win->win); translate_mpi_error(ierr,"comex_accs:MPI_Win_flush"); #endif #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,lproc,0,reg_win->win); translate_mpi_error(ierr,"comex_accs:MPI_Win_lock"); ierr = MPI_Accumulate(packbuf,1,src_type,lproc,displ,1,dst_type, MPI_SUM,reg_win->win); translate_mpi_error(ierr,"comex_accs:MPI_Accumulate"); ierr = MPI_Win_unlock(lproc,reg_win->win); translate_mpi_error(ierr,"comex_accs:MPI_Win_unlock"); #endif ierr = MPI_Type_free(&src_type); translate_mpi_error(ierr,"comex_accs:MPI_Type_free"); ierr = MPI_Type_free(&dst_type); translate_mpi_error(ierr,"comex_accs:MPI_Type_free"); free(packbuf); return COMEX_SUCCESS; #endif } /** * Utility function to create MPI data type for vector data object * src_ptr: location of first data point in vector data object for source * dst_ptr: location of first data point in vector data object for destination * iov: comex vector struct containing data information * iov_len: number of elements in vector data object * base_type: MPI_Datatype of elements * type: returned MPI_Datatype corresponding to iov for source data * type: returned MPI_Datatype corresponding to iov for destination data */ void vector_to_struct_dtype(void* src_ptr, void *dst_ptr, comex_giov_t *iov, int iov_len, MPI_Datatype base_type, MPI_Datatype *src_type, MPI_Datatype *dst_type) { #if 0 int i, size; int *blocklengths; MPI_Aint *displacements; MPI_Datatype *types; MPI_Aint displ; /* allocate buffers to create data types */ blocklengths = (int*)malloc(iov_len*sizeof(int)); displacements = (MPI_Aint*)malloc(iov_len*sizeof(MPI_Aint)); types = (MPI_Datatype*)malloc(iov_len*sizeof(MPI_Datatype)); for (i=0; ibuf; displ = 0; vector_to_struct_dtype(src_ptr, ptr, iov, iov_len, MPI_BYTE, &src_type, &dst_type); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } ierr = MPI_Type_commit(&src_type); translate_mpi_error(ierr,"comex_putv:MPI_Type_commit"); ierr = MPI_Type_commit(&dst_type); translate_mpi_error(ierr,"comex_putv:MPI_Type_commit"); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_putv:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Put(src_ptr, 1, src_type, lproc, displ, 1, dst_type, reg_win->win); translate_mpi_error(ierr,"comex_putv:MPI_Put"); ierr = MPI_Win_flush_local(lproc, reg_win->win); translate_mpi_error(ierr,"comex_putv:MPI_Win_flush_local"); #else ierr = MPI_Rput(src_ptr, 1, src_type, lproc, displ, 1, dst_type, reg_win->win, &request); translate_mpi_error(ierr,"comex_putv:MPI_Rput"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"comex_putv:MPI_Wait"); #ifdef USE_POST_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc,reg_win->win); translate_mpi_error(ierr,"comex_putv:MPI_Win_flush"); #endif #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,lproc,0,reg_win->win); translate_mpi_error(ierr,"comex_putv:MPI_Win_lock"); ierr = MPI_Put(src_ptr, 1, src_type, lproc, displ, 1, dst_type, reg_win->win); translate_mpi_error(ierr,"comex_putv:MPI_Put"); ierr = MPI_Win_unlock(lproc,reg_win->win); translate_mpi_error(ierr,"comex_putv:MPI_Win_unlock"); #endif ierr = MPI_Type_free(&src_type); translate_mpi_error(ierr,"comex_putv:MPI_Type_free"); ierr = MPI_Type_free(&dst_type); translate_mpi_error(ierr,"comex_putv:MPI_Type_free"); return COMEX_SUCCESS; #endif } int comex_getv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { #ifndef USE_MPI_DATATYPES int status; int i; for (i=0; ibuf; displ = 0; vector_to_struct_dtype(ptr, dst_ptr, iov, iov_len, MPI_BYTE, &src_type, &dst_type); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } ierr = MPI_Type_commit(&src_type); translate_mpi_error(ierr,"comex_getv:MPI_Type_commit"); ierr = MPI_Type_commit(&dst_type); translate_mpi_error(ierr,"comex_getv:MPI_Type_commit"); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_getv:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Get(dst_ptr, 1, dst_type, lproc, displ, 1, src_type, reg_win->win); translate_mpi_error(ierr,"comex_getv:MPI_Get"); ierr = MPI_Win_flush_local(lproc, reg_win->win); translate_mpi_error(ierr,"comex_getv:MPI_Win_flush_local"); #else ierr = MPI_Rget(dst_ptr, 1, dst_type, lproc, displ, 1, src_type, reg_win->win, &request); translate_mpi_error(ierr,"comex_getv:MPI_Rget"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"comex_getv:MPI_Wait"); #ifdef USE_POST_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc,reg_win->win); translate_mpi_error(ierr,"comex_getv:MPI_Win_flush"); #endif #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,lproc,0,reg_win->win); translate_mpi_error(ierr,"comex_getv:MPI_Win_lock"); ierr = MPI_Get(dst_ptr, 1, dst_type, lproc, displ, 1, src_type, reg_win->win); translate_mpi_error(ierr,"comex_getv:MPI_Get"); ierr = MPI_Win_unlock(lproc,reg_win->win); translate_mpi_error(ierr,"comex_getv:MPI_Win_unlock"); #endif ierr = MPI_Type_free(&src_type); translate_mpi_error(ierr,"comex_getv:MPI_Type_free"); ierr = MPI_Type_free(&dst_type); translate_mpi_error(ierr,"comex_getv:MPI_Type_free"); return COMEX_SUCCESS; #endif } /** * Utility function to create MPI data type for vector data object used in * vector accumulate operation. This also handles creation of a temporary buffer * to handle scaling of the accumulated values. The returned buffer must be * deallocated using free * dst_ptr: location of first data point in vector data object for destination * iov: comex vector struct containing data information * iov_len: number of elements in vector data object * base_type: MPI_Datatype of elements * type: returned MPI_Datatype corresponding to iov for source data * type: returned MPI_Datatype corresponding to iov for destination data */ void* create_vector_buf_and_dtypes(void *dst_ptr, comex_giov_t *iov, int iov_len, int comex_size, void *scale, MPI_Datatype base_type, MPI_Datatype *src_type, MPI_Datatype *dst_type) { int i, j, size, ierr; int nelems, icnt, ratio; void* src_buf; int *blocklengths; MPI_Aint *displacements; MPI_Datatype *types; MPI_Aint displ; MPI_Type_size(base_type,&size); /* find total number of elements */ nelems = 0; for (i=0; ibuf; displ = 0; src_ptr = create_vector_buf_and_dtypes(ptr, iov, iov_len, size, scale, base_type, &src_type, &dst_type); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } ierr = MPI_Type_commit(&src_type); translate_mpi_error(ierr,"comex_accv:MPI_Type_commit"); ierr = MPI_Type_commit(&dst_type); translate_mpi_error(ierr,"comex_accv:MPI_Type_commit"); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_accv:MPI_Win_flush"); #endif #ifdef USE_MPI_REQUESTS #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Accumulate(src_ptr,1,src_type,lproc,displ,1,dst_type, MPI_SUM,reg_win->win); translate_mpi_error(ierr,"comex_accv:MPI_Accumulate"); ierr = MPI_Win_flush_local(lproc, reg_win->win); translate_mpi_error(ierr,"comex_accv:MPI_Win_flush_local"); #else ierr = MPI_Raccumulate(src_ptr,1,src_type,lproc,displ,1,dst_type, MPI_SUM,reg_win->win,&request); translate_mpi_error(ierr,"comex_accv:MPI_Raccumulate"); ierr = MPI_Wait(&request, &status); translate_mpi_error(ierr,"comex_accv:MPI_Wait"); #ifdef USE_POST_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc,reg_win->win); translate_mpi_error(ierr,"comex_accv:MPI_Win_flush"); #endif #endif #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,lproc,0,reg_win->win); translate_mpi_error(ierr,"comex_accv:MPI_Win_lock"); ierr = MPI_Accumulate(src_ptr,1,src_type,lproc,displ,1,dst_type, MPI_SUM,reg_win->win); translate_mpi_error(ierr,"comex_accv:MPI_Accumulate"); ierr = MPI_Win_unlock(lproc,reg_win->win); translate_mpi_error(ierr,"comex_accv:MPI_Win_unlock"); #endif ierr = MPI_Type_free(&src_type); translate_mpi_error(ierr,"comex_accv:MPI_Type_free"); ierr = MPI_Type_free(&dst_type); translate_mpi_error(ierr,"comex_accv:MPI_Type_free"); free(src_ptr); return COMEX_SUCCESS; #endif } int comex_fence_all(comex_group_t group) { return comex_wait_all(group); } int comex_fence_proc(int proc, comex_group_t group) { comex_igroup_t *igroup = NULL; win_link_t *curr_win; int ierr; igroup = comex_get_igroup_from_group(group); if (igroup != NULL) { curr_win = igroup->win_list; while (curr_win != NULL) { ierr = MPI_Win_flush(proc, curr_win->win); translate_mpi_error(ierr,"comex_fence_proc:MPI_Win_flush"); curr_win = curr_win->next; } } return COMEX_SUCCESS; } /* comex_barrier is comex_fence_all + MPI_Barrier */ int comex_barrier(comex_group_t group) { MPI_Comm comm; int ierr; comex_fence_all(group); ierr = comex_group_comm(group, &comm); assert(COMEX_SUCCESS == ierr); MPI_Barrier(comm); return COMEX_SUCCESS; } void *comex_malloc_local(size_t size) { void *ptr; if (size == 0) { ptr = NULL; } else { MPI_Aint tsize; int ierr; tsize = size; #if USEX_SICM sicm_device_list *idevice = &device_dram; #if TEST_SICM char cdevice[32]; #ifdef TEST_SICM_DEV strcpy(cdevice, STR(TEST_SICM_DEV)); if (!strncmp(cdevice, "knl_hbm", 7)){ idevice = &device_knl_hbm; }else if (!strncmp(cdevice, "ppc_hbm", 7)){ idevice = &device_ppc_hbm; } #endif #endif sicm_arena arena = sicm_arena_create(0, 0, idevice); ptr = sicm_arena_alloc(arena, size); if(!ptr){ fprintf(stderr, "Error in allocating the pool\n"); exit(11); } #else ierr = MPI_Alloc_mem(tsize,MPI_INFO_NULL,&ptr); translate_mpi_error(ierr,"comex_malloc_local:MPI_Alloc_mem"); #endif } return ptr; } int comex_free_local(void *ptr) { if (ptr != NULL) { int ierr; #if USEX_SICM sicm_free(ptr); #else ierr = MPI_Free_mem(ptr); translate_mpi_error(ierr,"comex_free_local:MPI_Free_mem"); #endif } return COMEX_SUCCESS; } int comex_finalize() { int i, ierr; /* it's okay to call multiple times -- extra calls are no-ops */ if (!initialized) { return COMEX_SUCCESS; } initialized = 0; /* Make sure that all outstanding operations are done */ comex_wait_all(COMEX_GROUP_WORLD); /* groups */ comex_group_finalize(); MPI_Barrier(l_state.world_comm); /* destroy the communicators */ #if 0 ierr = MPI_Comm_free(&l_state.world_comm); translate_mpi_error(ierr,"comex_finalize:MPI_Comm_free"); #endif /* Clean up request list */ #ifdef USE_MPI_REQUESTS for (i=0; iremote_proc,nb_list[*hdl]->win); translate_mpi_error(ierr,"comex_wait:MPI_Win_flush_local"); #else MPI_Status status; ierr = MPI_Wait(&(nb_list[*hdl]->request),&status); translate_mpi_error(ierr,"comex_wait:MPI_Wait"); #ifdef USE_POST_MPI_WIN_FLUSH ierr = MPI_Win_flush(nb_list[*hdl]->remote_proc,nb_list[*hdl]->win); translate_mpi_error(ierr,"comex_wait:MPI_Win_flush_local"); #endif #endif if (nb_list[*hdl]->active == 0) { printf("p[%d] comex_wait Error: handle not active\n",l_state.rank); } nb_list[*hdl]->active = 0; if (nb_list[*hdl]->use_type) { ierr = MPI_Type_free(&(nb_list[*hdl]->src_type)); translate_mpi_error(ierr,"comex_wait:MPI_Type_free"); ierr = MPI_Type_free(&(nb_list[*hdl]->dst_type)); translate_mpi_error(ierr,"comex_wait:MPI_Type_free"); nb_list[*hdl]->use_type = 0; } return COMEX_SUCCESS; #else /* Non-blocking functions not implemented */ return COMEX_SUCCESS; #endif } /* returns 0 if operation completed */ int comex_test(comex_request_t* hdl, int *status) { #ifndef USE_MPI_DATATYPES *status = 0; return COMEX_SUCCESS; #endif #ifdef USE_MPI_REQUESTS int flag; int ierr; MPI_Status stat; ierr = MPI_Test(&(nb_list[*hdl]->request),&flag,&stat); translate_mpi_error(ierr,"comex_test:MPI_Test"); if (nb_list[*hdl]->active == 0) { printf("comex_test Error: handle not active\n"); } if (flag) { /* operation is complete */ *status = 0; nb_list[*hdl]->active = 0; if (nb_list[*hdl]->use_type) { ierr = MPI_Type_free(&(nb_list[*hdl]->src_type)); translate_mpi_error(ierr,"comex_wait:MPI_Type_free"); ierr = MPI_Type_free(&(nb_list[*hdl]->dst_type)); translate_mpi_error(ierr,"comex_wait:MPI_Type_free"); nb_list[*hdl]->use_type = 0; } } else { /* operation is incomplete */ *status = 1; } return COMEX_SUCCESS; #else *status = 0; return COMEX_SUCCESS; #endif } int comex_wait_all(comex_group_t group) { comex_igroup_t *igroup = NULL; win_link_t *curr_win; int ierr; igroup = comex_get_igroup_from_group(group); if (igroup != NULL) { curr_win = igroup->win_list; while (curr_win != NULL) { #ifdef USE_MPI_REQUESTS ierr = MPI_Win_flush_all(curr_win->win); translate_mpi_error(ierr,"comex_wait_all:MPI_Win_flush_all"); #else ierr = MPI_Win_fence(0,curr_win->win); translate_mpi_error(ierr,"comex_wait_all:MPI_Win_fence"); #endif curr_win = curr_win->next; } } return COMEX_SUCCESS; } int comex_nbput( void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { #ifndef USE_MPI_DATATYPES return comex_put(src, dst, bytes, proc, group); #endif #ifdef USE_MPI_REQUESTS if (hdl == NULL) { return comex_put(src, dst, bytes, proc, group); } MPI_Aint displ; void *ptr; int lproc, ierr; nb_t *req; reg_entry_t *reg_win; MPI_Request request; reg_win = reg_win_find(proc, dst, 0); ptr = reg_win->buf; displ = (MPI_Aint)(dst) - (MPI_Aint)(ptr); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } get_nb_request(hdl, &req); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_nbput:MPI_Win_flush"); #endif #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Put(src, bytes, MPI_CHAR, lproc, displ, bytes, MPI_CHAR, reg_win->win); translate_mpi_error(ierr,"comex_nbput:MPI_Put"); req->remote_proc = lproc; req->win = reg_win->win; #else ierr = MPI_Rput(src, bytes, MPI_CHAR, lproc, displ, bytes, MPI_CHAR, reg_win->win, &request); translate_mpi_error(ierr,"comex_nbput:MPI_Rput"); #endif req->request = request; req->use_type = 0; req->active = 1; return COMEX_SUCCESS; #else return comex_put(src, dst, bytes, proc, group); #endif } int comex_nbget( void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { #ifndef USE_MPI_DATATYPES return comex_get(src, dst, bytes, proc, group); #endif #ifdef USE_MPI_REQUESTS if (hdl == NULL) { return comex_get(src, dst, bytes, proc, group); } MPI_Aint displ; void *ptr; int lproc, ierr; nb_t *req; reg_entry_t *reg_win; MPI_Request request; reg_win = reg_win_find(proc, src, 0); ptr = reg_win->buf; displ = (MPI_Aint)(src) - (MPI_Aint)(ptr); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } get_nb_request(hdl, &req); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_nbget:MPI_Win_flush"); #endif #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Get(dst, bytes, MPI_CHAR, lproc, displ, bytes, MPI_CHAR, reg_win->win); translate_mpi_error(ierr,"comex_nbget:MPI_Get"); req->remote_proc = lproc; req->win = reg_win->win; #else ierr = MPI_Rget(dst, bytes, MPI_CHAR, lproc, displ, bytes, MPI_CHAR, reg_win->win, &request); translate_mpi_error(ierr,"comex_nbget:MPI_Rget"); #endif req->request = request; req->use_type = 0; req->active = 1; return COMEX_SUCCESS; #else return comex_get(src, dst, bytes, proc, group); #endif } int comex_nbacc( int datatype, void *scale, void *src_ptr, void *dst_ptr, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { #ifndef USE_MPI_DATATYPES return comex_acc( datatype, scale, src_ptr, dst_ptr, bytes, proc, group); #endif #ifdef USE_MPI_REQUESTS if (hdl == NULL) { return comex_acc( datatype, scale, src_ptr, dst_ptr, bytes, proc, group); } MPI_Aint displ; void *ptr; int count, i, lproc, ierr; nb_t *req; reg_entry_t *reg_win; MPI_Request request; MPI_Status status; reg_win = reg_win_find(proc, dst_ptr, 0); ptr = reg_win->buf; displ = (MPI_Aint)(dst_ptr) - (MPI_Aint)(ptr); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } get_nb_request(hdl, &req); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_nbput:MPI_Win_flush"); #endif if (datatype == COMEX_ACC_INT) { int *buf; int *isrc = (int*)src_ptr; int iscale = *((int*)scale); count = bytes/sizeof(int); buf = (int*)malloc(bytes); for (i=0; iwin); translate_mpi_error(ierr,"comex_nbacc:MPI_Accumulate"); req->remote_proc = lproc; req->win = reg_win->win; #else ierr = MPI_Raccumulate(buf,count,MPI_INT,lproc,displ,count, MPI_INT,MPI_SUM,reg_win->win,&request); translate_mpi_error(ierr,"comex_nbacc:MPI_Raccumulate"); #endif req->request = request; req->use_type = 0; req->active = 1; free(buf); } else if (datatype == COMEX_ACC_LNG) { long *buf; long *lsrc = (long*)src_ptr; long lscale = *((long*)scale); count = bytes/sizeof(long); buf = (long*)malloc(bytes); for (i=0; iwin); translate_mpi_error(ierr,"comex_nbacc:MPI_Accumulate"); req->remote_proc = lproc; req->win = reg_win->win; #else ierr = MPI_Raccumulate(buf,count,MPI_LONG,lproc,displ,count, MPI_LONG,MPI_SUM,reg_win->win,&request); translate_mpi_error(ierr,"comex_nbacc:MPI_Raccumulate"); #endif req->request = request; req->use_type = 0; req->active = 1; free(buf); } else if (datatype == COMEX_ACC_FLT) { float *buf; float *fsrc = (float*)src_ptr; float fscale = *((float*)scale); count = bytes/sizeof(float); buf = (float*)malloc(bytes); for (i=0; iwin); translate_mpi_error(ierr,"comex_nbacc:MPI_Accumulate"); req->remote_proc = lproc; req->win = reg_win->win; #else ierr = MPI_Raccumulate(buf,count,MPI_FLOAT,lproc,displ,count, MPI_FLOAT,MPI_SUM,reg_win->win,&request); translate_mpi_error(ierr,"comex_nbacc:MPI_Raccumulate"); #endif req->request = request; req->use_type = 0; req->active = 1; free(buf); } else if (datatype == COMEX_ACC_DBL) { double *buf; double *dsrc = (double*)src_ptr; double dscale = *((double*)scale); count = bytes/sizeof(double); buf = (double*)malloc(bytes); for (i=0; iwin); translate_mpi_error(ierr,"comex_nbacc:MPI_Accumulate"); req->remote_proc = lproc; req->win = reg_win->win; #else ierr = MPI_Raccumulate(buf,count,MPI_DOUBLE,lproc,displ,count, MPI_DOUBLE,MPI_SUM,reg_win->win,&request); translate_mpi_error(ierr,"comex_nbacc:MPI_Raccumulate"); #endif req->request = request; req->use_type = 0; req->active = 1; free(buf); } else if (datatype == COMEX_ACC_CPL) { int cnum; float *buf; float *csrc = (float*)src_ptr; float crscale = *((float*)scale); float ciscale = *((float*)scale+1); count = bytes/sizeof(float); cnum = count/2; buf = (float*)malloc(bytes); for (i=0; iwin); translate_mpi_error(ierr,"comex_nbacc:MPI_Accumulate"); req->remote_proc = lproc; req->win = reg_win->win; #else ierr = MPI_Raccumulate(buf,count,MPI_FLOAT,lproc,displ,count, MPI_FLOAT,MPI_SUM,reg_win->win,&request); translate_mpi_error(ierr,"comex_nbacc:MPI_Raccumulate"); #endif req->request = request; req->use_type = 0; req->active = 1; free(buf); } else if (datatype == COMEX_ACC_DCP) { int cnum; double *buf; double *csrc = (double*)src_ptr; double crscale = *((double*)scale); double ciscale = *((double*)scale+1); count = bytes/sizeof(double); cnum = count/2; buf = (double*)malloc(bytes); for (i=0; iwin); translate_mpi_error(ierr,"comex_nbacc:MPI_Accumulate"); req->remote_proc = lproc; req->win = reg_win->win; #else ierr = MPI_Raccumulate(buf,count,MPI_DOUBLE,lproc,displ,count, MPI_DOUBLE,MPI_SUM,reg_win->win,&request); translate_mpi_error(ierr,"comex_nbacc:MPI_Raccumulate"); #endif req->request = request; req->use_type = 0; req->active = 1; free(buf); } else { assert(0); } return COMEX_SUCCESS; #else return comex_acc( datatype, scale, src_ptr, dst_ptr, bytes, proc, group); #endif } int comex_nbputs( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { #ifndef USE_MPI_DATATYPES return comex_puts(src, src_stride, dst, dst_stride, count, stride_levels, proc, group); hdl = NULL; #else #ifdef USE_MPI_REQUESTS MPI_Datatype src_type, dst_type; MPI_Aint displ; void *ptr; int lproc, ierr; reg_entry_t *reg_win; nb_t *req; MPI_Request request; MPI_Status status; if (hdl == NULL) { return comex_puts(src, src_stride, dst, dst_stride, count, stride_levels, proc, group); } reg_win = reg_win_find(proc, dst, 0); ptr = reg_win->buf; displ = (MPI_Aint)(dst) - (MPI_Aint)(ptr); strided_to_subarray_dtype(src_stride, count, stride_levels, MPI_BYTE, &src_type); strided_to_subarray_dtype(dst_stride, count, stride_levels, MPI_BYTE, &dst_type); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } get_nb_request(hdl, &req); ierr = MPI_Type_commit(&src_type); translate_mpi_error(ierr,"comex_nbputs:MPI_Type_commit"); ierr = MPI_Type_commit(&dst_type); translate_mpi_error(ierr,"comex_nbputs:MPI_Type_commit"); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_nbputs:MPI_Win_flush"); #endif #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Put(src, 1, src_type, lproc, displ, 1, dst_type, reg_win->win); translate_mpi_error(ierr,"comex_nbputs:MPI_Put"); req->remote_proc = lproc; req->win = reg_win->win; #else ierr = MPI_Rput(src, 1, src_type, lproc, displ, 1, dst_type, reg_win->win, &request); translate_mpi_error(ierr,"comex_nbputs:MPI_Rput"); #endif req->request = request; req->use_type = 1; req->src_type = src_type; req->dst_type = dst_type; req->remote_proc = lproc; req->win = reg_win->win; req->active = 1; return COMEX_SUCCESS; #else return comex_puts(src, src_stride, dst, dst_stride, count, stride_levels, proc, group); #endif #endif } int comex_nbgets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { #ifndef USE_MPI_DATATYPES hdl = NULL; return comex_gets(src, src_stride, dst, dst_stride, count, stride_levels, proc, group); #else #ifdef USE_MPI_REQUESTS if (hdl == NULL) { return comex_gets(src, src_stride, dst, dst_stride, count, stride_levels, proc, group); } MPI_Datatype src_type, dst_type; MPI_Aint displ; void *ptr; int lproc, ierr; reg_entry_t *reg_win; MPI_Request request; MPI_Status status; nb_t *req; reg_win = reg_win_find(proc, src, 0); ptr = reg_win->buf; displ = (MPI_Aint)(src) - (MPI_Aint)(ptr); strided_to_subarray_dtype(src_stride, count, stride_levels, MPI_BYTE, &src_type); strided_to_subarray_dtype(dst_stride, count, stride_levels, MPI_BYTE, &dst_type); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } get_nb_request(hdl, &req); ierr = MPI_Type_commit(&src_type); translate_mpi_error(ierr,"comex_nbgets:MPI_Type_commit"); ierr = MPI_Type_commit(&dst_type); translate_mpi_error(ierr,"comex_nbgets:MPI_Type_commit"); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_nbgets:MPI_Win_flush"); #endif #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Get(dst, 1, dst_type, lproc, displ, 1, src_type, reg_win->win); translate_mpi_error(ierr,"comex_nbgets:MPI_Get"); req->remote_proc = lproc; req->win = reg_win->win; #else ierr = MPI_Rget(dst, 1, dst_type, lproc, displ, 1, src_type, reg_win->win, &request); translate_mpi_error(ierr,"comex_nbgets:MPI_Rget"); #endif req->request = request; req->use_type = 1; req->src_type = src_type; req->dst_type = dst_type; req->remote_proc = lproc; req->win = reg_win->win; req->active = 1; return COMEX_SUCCESS; #else return comex_gets(src, src_stride, dst, dst_stride, count, stride_levels, proc, group); #endif #endif } int comex_nbaccs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { #ifndef USE_MPI_DATATYPES hdl = NULL; return comex_accs(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, proc, group); #else #ifdef USE_MPI_REQUESTS MPI_Datatype src_type, dst_type; MPI_Aint displ; void *ptr; int lproc, i, ierr; void *packbuf; int bufsize; int new_strides[7], new_count[7]; reg_entry_t *reg_win; MPI_Request request; MPI_Status status; if (hdl == NULL) { return comex_accs(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, proc, group); } /* If data is contiguous, use comex_acc */ if (stride_levels == 0) { return comex_nbacc(datatype, scale, src, dst, count[0], proc, group, hdl); } nb_t *req; reg_win = reg_win_find(proc, dst, 0); ptr = reg_win->buf; displ = (MPI_Aint)(dst) - (MPI_Aint)(ptr); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } packbuf = malloc_strided_acc_buffer(src, src_stride, count, stride_levels, &bufsize, new_strides); for (i=0; iwin); translate_mpi_error(ierr,"comex_nbaccs:MPI_Win_flush"); #endif #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Accumulate(packbuf,1,src_type,lproc,displ,1,dst_type, MPI_SUM,reg_win->win); translate_mpi_error(ierr,"comex_nbaccs:MPI_Accumulate"); req->remote_proc = lproc; req->win = reg_win->win; #else ierr = MPI_Raccumulate(packbuf,1,src_type,lproc,displ,1,dst_type, MPI_SUM,reg_win->win,&request); translate_mpi_error(ierr,"comex_nbaccs:MPI_Rget"); #endif req->request = request; req->use_type = 1; req->src_type = src_type; req->dst_type = dst_type; req->remote_proc = lproc; req->win = reg_win->win; req->active = 1; free(packbuf); return COMEX_SUCCESS; #else return comex_accs(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, proc, group); #endif #endif } int comex_nbputv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* handle) { #ifndef USE_MPI_DATATYPES return comex_putv(iov, iov_len, proc, group); #else #ifdef USE_MPI_REQUESTS if (handle == NULL) { return comex_putv(iov, iov_len, proc, group); } MPI_Datatype src_type, dst_type; MPI_Aint displ; void *ptr, *src_ptr, *dst_ptr; int lproc, ierr; reg_entry_t *reg_win; MPI_Request request; MPI_Status status; nb_t *req; src_ptr = iov[0].src[0]; dst_ptr = iov[0].dst[0]; reg_win = reg_win_find(proc, dst_ptr, 0); ptr = reg_win->buf; displ = 0; vector_to_struct_dtype(src_ptr, ptr, iov, iov_len, MPI_BYTE, &src_type, &dst_type); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } get_nb_request(handle, &req); ierr = MPI_Type_commit(&src_type); translate_mpi_error(ierr,"comex_nbputv:MPI_Type_commit"); ierr = MPI_Type_commit(&dst_type); translate_mpi_error(ierr,"comex_nbputv:MPI_Type_commit"); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_nbputv:MPI_Win_flush"); #endif #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Put(src_ptr, 1, src_type, lproc, displ, 1, dst_type, reg_win->win); translate_mpi_error(ierr,"comex_nbputv:MPI_Put"); req->remote_proc = lproc; req->win = reg_win->win; #else ierr = MPI_Rput(src_ptr, 1, src_type, lproc, displ, 1, dst_type, reg_win->win, &request); translate_mpi_error(ierr,"comex_nbputv:MPI_Rput"); #endif req->request = request; req->use_type = 1; req->src_type = src_type; req->dst_type = dst_type; req->remote_proc = lproc; req->win = reg_win->win; req->active = 1; return COMEX_SUCCESS; #else return comex_putv(iov, iov_len, proc, group); #endif #endif } int comex_nbgetv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* handle) { #ifndef USE_MPI_DATATYPES return comex_getv(iov, iov_len, proc, group); #else #ifdef USE_MPI_REQUESTS if (handle == NULL) { return comex_getv(iov, iov_len, proc, group); } MPI_Datatype src_type, dst_type; MPI_Aint displ; void *ptr, *src_ptr, *dst_ptr; int lproc, ierr; reg_entry_t *reg_win; MPI_Request request; MPI_Status status; nb_t *req; src_ptr = iov[0].src[0]; dst_ptr = iov[0].dst[0]; reg_win = reg_win_find(proc, src_ptr, 0); ptr = reg_win->buf; displ = 0; vector_to_struct_dtype(ptr, dst_ptr, iov, iov_len, MPI_BYTE, &src_type, &dst_type); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } get_nb_request(handle, &req); ierr = MPI_Type_commit(&src_type); translate_mpi_error(ierr,"comex_nbgetv:MPI_Type_commit"); ierr = MPI_Type_commit(&dst_type); translate_mpi_error(ierr,"comex_nbgetv:MPI_Type_commit"); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_nbgetv:MPI_Win_flush"); #endif #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Get(dst_ptr, 1, dst_type, lproc, displ, 1, src_type, reg_win->win); translate_mpi_error(ierr,"comex_nbgetv:MPI_Get"); req->remote_proc = lproc; req->win = reg_win->win; #else ierr = MPI_Rget(dst_ptr, 1, dst_type, lproc, displ, 1, src_type, reg_win->win, &request); translate_mpi_error(ierr,"comex_nbgetv:MPI_Rget"); #endif req->request = request; req->use_type = 1; req->src_type = src_type; req->dst_type = dst_type; req->remote_proc = lproc; req->win = reg_win->win; req->active = 1; return COMEX_SUCCESS; #else return comex_getv(iov, iov_len, proc, group); #endif #endif } int comex_nbaccv( int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* handle) { #ifndef USE_MPI_DATATYPES return comex_accv(datatype, scale, iov, iov_len, proc, group); #else #ifdef USE_MPI_REQUESTS if (handle == NULL) { return comex_accv(datatype, scale, iov, iov_len, proc, group); } MPI_Datatype src_type, dst_type; MPI_Aint displ; MPI_Datatype base_type; void *ptr, *src_ptr, *dst_ptr; MPI_Request request; MPI_Status status; nb_t *req; int lproc, size, ierr; if (datatype == COMEX_ACC_INT) { size = sizeof(int); base_type = MPI_INT; } else if (datatype == COMEX_ACC_LNG) { size = sizeof(long); base_type = MPI_LONG; } else if (datatype == COMEX_ACC_FLT) { size = sizeof(float); base_type = MPI_FLOAT; } else if (datatype == COMEX_ACC_DBL) { size = sizeof(double); base_type = MPI_DOUBLE; } else if (datatype == COMEX_ACC_CPL) { size = 2*sizeof(float); base_type = MPI_FLOAT; } else if (datatype == COMEX_ACC_DCP) { size = 2*sizeof(double); base_type = MPI_DOUBLE; } reg_entry_t *reg_win; dst_ptr = iov[0].dst[0]; reg_win = reg_win_find(proc, dst_ptr, 0); ptr = reg_win->buf; displ = 0; src_ptr = create_vector_buf_and_dtypes(ptr, iov, iov_len, size, scale, base_type, &src_type, &dst_type); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } get_nb_request(handle, &req); ierr = MPI_Type_commit(&src_type); translate_mpi_error(ierr,"comex_nbaccv:MPI_Type_commit"); ierr = MPI_Type_commit(&dst_type); translate_mpi_error(ierr,"comex_nbaccv:MPI_Type_commit"); #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc, reg_win->win); translate_mpi_error(ierr,"comex_nbaccv:MPI_Win_flush"); #endif #ifdef USE_MPI_FLUSH_LOCAL ierr = MPI_Accumulate(src_ptr,1,src_type,lproc,displ,1,dst_type, MPI_SUM,reg_win->win); translate_mpi_error(ierr,"comex_nbaccv:MPI_Accumulate"); req->remote_proc = lproc; req->win = reg_win->win; #else ierr = MPI_Raccumulate(src_ptr,1,src_type,lproc,displ,1,dst_type, MPI_SUM,reg_win->win,&request); translate_mpi_error(ierr,"comex_nbaccv:MPI_Raccumulate"); #endif req->request = request; req->use_type = 1; req->src_type = src_type; req->dst_type = dst_type; req->remote_proc = lproc; req->win = reg_win->win; req->active = 1; free(src_ptr); return COMEX_SUCCESS; #else return comex_accv(datatype, scale, iov, iov_len, proc, group); #endif #endif } int comex_rmw( int op, void *ploc, void *prem, int extra, int proc, comex_group_t group) { MPI_Aint displ; void *ptr; int lproc, ierr; reg_entry_t *reg_win; reg_win = reg_win_find(proc, prem, 0); ptr = reg_win->buf; if (ptr == NULL) return COMEX_FAILURE; displ = (MPI_Aint)(prem) - (MPI_Aint)(ptr); if (!(get_local_rank_from_win(reg_win->win, proc, &lproc) == COMEX_SUCCESS)) { assert(0); } #ifdef USE_PRIOR_MPI_WIN_FLUSH ierr = MPI_Win_flush(lproc,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Win_flush"); #endif if (op == COMEX_FETCH_AND_ADD) { int incr = extra; #ifdef USE_MPI_REQUESTS ierr = MPI_Fetch_and_op(&incr,ploc,MPI_INT,lproc,displ, MPI_SUM,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_flush(lproc,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Win_flush"); #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,lproc,0,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Win_lock"); ierr = MPI_Fetch_and_op(&incr,ploc,MPI_INT,lproc,displ, MPI_SUM,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_unlock(lproc,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Win_unlock"); #endif } else if (op == COMEX_FETCH_AND_ADD_LONG) { long incr = extra; #ifdef USE_MPI_REQUESTS ierr = MPI_Fetch_and_op(&incr,ploc,MPI_LONG,lproc,displ, MPI_SUM,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_flush(lproc,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Win_flush"); #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,lproc,0,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Win_lock"); ierr = MPI_Fetch_and_op(&incr,ploc,MPI_LONG,lproc,displ, MPI_SUM,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_unlock(lproc,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Win_unlock"); #endif } else if (op == COMEX_SWAP) { int incr = *((int*)ploc); #ifdef USE_MPI_REQUESTS ierr = MPI_Fetch_and_op(&incr,ploc,MPI_INT,lproc,displ, MPI_REPLACE,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_flush(lproc,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Win_flush"); #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,lproc,0,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Win_lock"); ierr = MPI_Fetch_and_op(&incr,ploc,MPI_INT,lproc,displ, MPI_REPLACE,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_unlock(lproc,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Win_unlock"); #endif } else if (op == COMEX_SWAP_LONG) { long incr = *((long*)ploc); #ifdef USE_MPI_REQUESTS ierr = MPI_Fetch_and_op(&incr,ploc,MPI_LONG,lproc,displ, MPI_REPLACE,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_flush(lproc,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Win_flush"); #else ierr = MPI_Win_lock(MPI_LOCK_SHARED,lproc,0,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Win_lock"); ierr = MPI_Fetch_and_op(&incr,ploc,MPI_LONG,lproc,displ, MPI_REPLACE,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Fetch_and_op"); ierr = MPI_Win_unlock(lproc,reg_win->win); translate_mpi_error(ierr,"comex_rmw:MPI_Win_unlock"); #endif } else { assert(0); } return COMEX_SUCCESS; } /* Mutex Operations. These are implemented using an MPI-based algorithm described in R. Latham, R. Ross, R. Thakur, The International Journal of High Performance Computing Applications, vol. 21, pp. 132-143, (2007). The actual algorithm appears to have some errors, the code below is a combination of this paper and some examples from the web. */ /** * create some mutexes on this processor. This is a collective operation * on the world group but different processors may create different numbers * of mutexes * num: number of mutexes to create to create on this processor */ int comex_create_mutexes(int num) { int i, j, k, idx, isize, nsize, ierr; comex_igroup_t *igroup = NULL; MPI_Comm comm; int *sbuf; int me = l_state.rank; int nproc = l_state.size; igroup = comex_get_igroup_from_group(COMEX_GROUP_WORLD); comm = igroup->comm; sbuf = (int*)malloc(nproc*sizeof(int)); _mutex_num = (int*)malloc(nproc*sizeof(int)); for (i=0; i= _mutex_total) assert(0); waitlistcopy = (int*)malloc(nproc*sizeof(int)); /* Set value in the wait list */ ierr = MPI_Win_lock(MPI_LOCK_EXCLUSIVE,proc,0,_mutex_list[idx]); translate_mpi_error(ierr,"comex_lock:MPI_Win_lock"); ierr = MPI_Get(waitlistcopy,nproc,MPI_INT,proc,0,nproc,MPI_INT,_mutex_list[idx]); translate_mpi_error(ierr,"comex_lock:MPI_Get"); ierr = MPI_Put(&lock,1,MPI_INT,proc,me*sizeof(int),1,MPI_INT,_mutex_list[idx]); translate_mpi_error(ierr,"comex_lock:MPI_Put"); ierr = MPI_Win_unlock(proc,_mutex_list[idx]); translate_mpi_error(ierr,"comex_lock:MPI_Win_unlock"); /* Check to see if lock is already held */ ok = 1; for (i=0; i= _mutex_total) assert(0); waitlistcopy = (int*)malloc(nproc*sizeof(int)); /* Set value in the wait list */ MPI_Win_lock(MPI_LOCK_EXCLUSIVE,proc,0,_mutex_list[idx]); MPI_Get(waitlistcopy,nproc,MPI_INT,proc,0,nproc,MPI_INT,_mutex_list[idx]); MPI_Put(&lock,1,MPI_INT,proc,me*sizeof(int),1,MPI_INT,_mutex_list[idx]); MPI_Win_unlock(proc,_mutex_list[idx]); /* Check to see if someone is waiting for lock */ ok = 1; for (i=0; icomm; assert(comm != MPI_COMM_NULL); MPI_Comm_rank(comm, &comm_rank); MPI_Comm_size(comm, &comm_size); #if DEBUG printf("[%d] comex_malloc(ptrs=%p, size=%lu, group=%d)\n", comm_rank, ptrs, (long unsigned)size, group); #endif /* is this needed? */ /* comex_barrier(group); */ /* allocate ret_entry_t object for this process */ reg_entries = malloc(sizeof(reg_entry_t)*comm_size); /*assert(reg_entries) */ reg_entries[comm_rank].rank = comm_rank; reg_entries[comm_rank].len = size; /* allocate and register segment. We need to allocate something even if size is zero so allocate a nominal amount so that routines don't break. Count on the fact that the length is zero to keep things from breaking down */ if (size > 0) { tsize = size; } else { tsize = 8; } #ifdef USE_MPI_WIN_ALLOC MPI_Win_allocate(sizeof(char)*tsize,1,MPI_INFO_NULL,comm,®_entries[comm_rank].buf, ®_entries[comm_rank].win); #else MPI_Alloc_mem(tsize,MPI_INFO_NULL,®_entries[comm_rank].buf); #endif MPI_Win_create(reg_entries[comm_rank].buf,tsize,1,MPI_INFO_NULL,comm, ®_entries[comm_rank].win); #ifdef USE_MPI_REQUESTS MPI_Win_lock_all(0,reg_entries[comm_rank].win); /* Use MPI_MODE_NOCHECK instead of 0 */ #endif /* exchange buffer address */ /* @TODO: Consider using MPI_IN_PLACE? */ memcpy(&src, ®_entries[comm_rank], sizeof(reg_entry_t)); MPI_Allgather(&src, sizeof(reg_entry_t), MPI_BYTE, reg_entries, sizeof(reg_entry_t), MPI_BYTE, comm); /* assign the ptr array to return to caller */ for (i=0; icomm; assert(comm != MPI_COMM_NULL); MPI_Comm_rank(comm, &comm_rank); MPI_Comm_size(comm, &comm_size); #if DEBUG printf("[%d] comex_malloc(ptrs=%p, size=%lu, group=%d)\n", comm_rank, ptrs, (long unsigned)size, group); #endif /* is this needed? */ /* comex_barrier(group); */ /* allocate ret_entry_t object for this process */ reg_entries = malloc(sizeof(reg_entry_t)*comm_size); /*assert(reg_entries) */ reg_entries[comm_rank].rank = comm_rank; reg_entries[comm_rank].len = size; /* allocate and register segment. We need to allocate something even if size is zero so allocate a nominal amount so that routines don't break. Count on the fact that the length is zero to keep things from breaking down */ if (size > 0) { tsize = size; } else { tsize = 8; } sicm_arena arena = sicm_arena_create(0, 0, idevice); reg_entries[comm_rank].buf = sicm_arena_alloc(arena, size); if(!(reg_entries[comm_rank].buf)){ fprintf(stderr, "Error in allocating the pool\n"); exit(11); } MPI_Win_create(reg_entries[comm_rank].buf,tsize,1,MPI_INFO_NULL,comm, ®_entries[comm_rank].win); #ifdef USE_MPI_REQUESTS MPI_Win_lock_all(0,reg_entries[comm_rank].win); /* Use MPI_MODE_NOCHECK instead of 0 */ #endif /* exchange buffer address */ /* @TODO: Consider using MPI_IN_PLACE? */ memcpy(&src, ®_entries[comm_rank], sizeof(reg_entry_t)); MPI_Allgather(&src, sizeof(reg_entry_t), MPI_BYTE, reg_entries, sizeof(reg_entry_t), MPI_BYTE, comm); /* assign the ptr array to return to caller */ for (i=0; icomm; assert(comm != MPI_COMM_NULL); MPI_Comm_rank(comm, &comm_rank); MPI_Comm_size(comm, &comm_size); /* Find the window that this buffer belongs to */ comex_group_translate_world(group,comm_rank,&world_rank); reg_win = reg_win_find(world_rank, ptr, 0); window = reg_win->win; #ifndef USE_MPI_WIN_ALLOC /* Save pointer to memory */ void* buf = reg_win->buf; #endif /* allocate receive buffer for exchange of pointers */ allgather_ptrs = (long **)malloc(sizeof(void *) * comm_size); assert(allgather_ptrs); /* exchange of pointers */ MPI_Allgather(&ptr, sizeof(void *), MPI_BYTE, allgather_ptrs, sizeof(void *), MPI_BYTE, comm); /* Get rid of pointers for this window */ for (i=0; i < comm_size; i++) { int world_rank; ierr = comex_group_translate_world(group, i, &world_rank); assert(COMEX_SUCCESS == ierr); /* probably should use rank for communicator, not world rank*/ reg_win_delete(world_rank,allgather_ptrs[i]); } /* Remove window from group list */ comex_igroup_delete_win(group, window); /* remove my ptr from reg cache and free ptr */ /* comex_free_local(ptr); */ free(allgather_ptrs); /* free up window */ #ifdef USE_MPI_REQUESTS MPI_Win_unlock_all(window); #endif MPI_Win_free(&window); #ifndef USE_MPI_WIN_ALLOC /* Clear memory for this window */ MPI_Free_mem(buf); #endif /* Is this needed? */ MPI_Barrier(comm); return COMEX_SUCCESS; #endif } int comex_free_dev(void *ptr, comex_group_t group) { #if (!defined(USE_SICM) || !USE_SICM) return comex_free(ptr,group); #else comex_igroup_t *igroup = NULL; MPI_Comm comm = MPI_COMM_NULL; MPI_Win window; int comm_rank, world_rank; int comm_size; void **allgather_ptrs = NULL; int i, ierr; reg_entry_t *reg_win; /* preconditions */ assert(ptr != NULL); igroup = comex_get_igroup_from_group(group); comm = igroup->comm; assert(comm != MPI_COMM_NULL); MPI_Comm_rank(comm, &comm_rank); MPI_Comm_size(comm, &comm_size); /* Find the window that this buffer belongs to */ comex_group_translate_world(group,comm_rank,&world_rank); reg_win = reg_win_find(world_rank, ptr, 0); window = reg_win->win; #ifndef USE_MPI_WIN_ALLOC /* Save pointer to memory */ void* buf = reg_win->buf; #endif /* allocate receive buffer for exchange of pointers */ allgather_ptrs = (void **)malloc(sizeof(void *) * comm_size); assert(allgather_ptrs); /* exchange of pointers */ MPI_Allgather(&ptr, sizeof(void *), MPI_BYTE, allgather_ptrs, sizeof(void *), MPI_BYTE, comm); /* Get rid of pointers for this window */ for (i=0; i < comm_size; i++) { int world_rank; ierr = comex_group_translate_world(group, i, &world_rank); assert(COMEX_SUCCESS == ierr); /* probably should use rank for communicator, not world rank*/ reg_win_delete(world_rank,allgather_ptrs[i]); } /* Remove window from group list */ comex_igroup_delete_win(group, window); /* remove my ptr from reg cache and free ptr */ /* comex_free_local(ptr); */ free(allgather_ptrs); /* free up window */ #ifdef USE_MPI_REQUESTS MPI_Win_unlock_all(window); #endif MPI_Win_free(&window); /* Clear memory for this window */ sicm_free(buf); /* Is this needed? */ MPI_Barrier(comm); return COMEX_SUCCESS; #endif } static void acquire_remote_lock(int proc) { assert(0); } static void release_remote_lock(int proc) { assert(0); } static inline void acc( int datatype, int count, void *get_buf, void *src_ptr, long src_idx, void *scale) { #define EQ_ONE_REG(A) ((A) == 1.0) #define EQ_ONE_CPL(A) ((A).real == 1.0 && (A).imag == 0.0) #define IADD_REG(A,B) (A) += (B) #define IADD_CPL(A,B) (A).real += (B).real; (A).imag += (B).imag #define IADD_SCALE_REG(A,B,C) (A) += (B) * (C) #define IADD_SCALE_CPL(A,B,C) (A).real += ((B).real*(C).real) - ((B).imag*(C).imag);\ (A).imag += ((B).real*(C).imag) + ((B).imag*(C).real); #define ACC(WHICH, COMEX_TYPE, C_TYPE) \ if (datatype == COMEX_TYPE) { \ int m; \ int m_lim = count/sizeof(C_TYPE); \ C_TYPE *iterator = (C_TYPE *)get_buf; \ C_TYPE *value = (C_TYPE *)((char *)src_ptr + src_idx); \ C_TYPE calc_scale = *(C_TYPE *)scale; \ if (EQ_ONE_##WHICH(calc_scale)) { \ for (m = 0 ; m < m_lim; ++m) { \ IADD_##WHICH(iterator[m], value[m]); \ } \ } \ else { \ for (m = 0 ; m < m_lim; ++m) { \ IADD_SCALE_##WHICH(iterator[m], value[m], calc_scale); \ } \ } \ } else ACC(REG, COMEX_ACC_DBL, double) ACC(REG, COMEX_ACC_FLT, float) ACC(REG, COMEX_ACC_INT, int) ACC(REG, COMEX_ACC_LNG, long) ACC(CPL, COMEX_ACC_DCP, DoubleComplex) ACC(CPL, COMEX_ACC_CPL, SingleComplex) { assert(0); } #undef ACC #undef EQ_ONE_REG #undef EQ_ONE_CPL #undef IADD_REG #undef IADD_CPL #undef IADD_SCALE_REG #undef IADD_SCALE_CPL } ga-5.9.2/comex/src-mpi3/comex_impl.h000066400000000000000000000020021500715745200171730ustar00rootroot00000000000000#ifndef COMEX_IMPL_H_ #define COMEX_IMPL_H_ #include #include #include #include "groups.h" #define COMEX_MAX_NB_OUTSTANDING 128 #define SHM_NAME_SIZE 20 typedef struct { MPI_Comm world_comm; int rank; int size; } local_state; extern local_state l_state; #define DEBUG 0 #define COMEX_STRINGIFY(x) #x #ifdef NDEBUG #define COMEX_ASSERT(WHAT) ((void) (0)) #else #define COMEX_ASSERT(WHAT) \ ((WHAT) \ ? (void) (0) \ : comex_assert_fail (COMEX_STRINGIFY(WHAT), __FILE__, __LINE__, __func__)) #endif static inline void comex_assert_fail( const char *assertion, const char *file, unsigned int line, const char *function) { fprintf(stderr, "[%d] %s:%u: %s: Assertion `%s' failed", l_state.rank, file, line, function, assertion); fflush(stderr); #if DEBUG printf("[%d] %s:%u: %s: Assertion `%s' failed", l_state.rank, file, line, function, assertion); #endif comex_error("comex_assert_fail", -1); } #endif /* COMEX_IMPL_H_ */ ga-5.9.2/comex/src-mpi3/groups.c000066400000000000000000000273161500715745200163700ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include "comex.h" #include "comex_impl.h" #include "groups.h" /* #define USE_MPI_ERRORS_RETURN */ /* the HEAD of the group linked list */ comex_igroup_t *group_list = NULL; /* static functions implemented in this file */ static void comex_create_group_and_igroup( comex_group_t *id, comex_igroup_t **igroup); static void comex_igroup_finalize(comex_igroup_t *igroup); /** * Return the comex igroup instance given the group id. * * The group linked list is searched sequentially until the given group * is found. It is an error if this function is called before * comex_group_init(). An error occurs if the given group is not found. */ comex_igroup_t* comex_get_igroup_from_group(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; assert(group_list != NULL); while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { return current_group_list_item; } current_group_list_item = current_group_list_item->next; } comex_error("comex group lookup failed", -1); return NULL; } /** * Creates and associates a comex group with an comex igroup. * * This does *not* initialize the members of the comex igroup. */ void comex_create_group_and_igroup( comex_group_t *id, comex_igroup_t **igroup) { comex_igroup_t *new_group_list_item = NULL; comex_igroup_t *last_group_list_item = NULL; /* find the last group in the group linked list */ last_group_list_item = group_list; while (last_group_list_item->next != NULL) { last_group_list_item = last_group_list_item->next; } /* create, init, and insert the new node for the linked list */ new_group_list_item = malloc(sizeof(comex_igroup_t)); new_group_list_item->id = last_group_list_item->id + 1; new_group_list_item->comm = MPI_COMM_NULL; new_group_list_item->group = MPI_GROUP_NULL; new_group_list_item->next = NULL; new_group_list_item->win_list = NULL; last_group_list_item->next = new_group_list_item; /* return the group id and comex igroup */ *igroup = new_group_list_item; *id = new_group_list_item->id; } int comex_group_rank(comex_group_t group, int *rank) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); status = MPI_Group_rank(igroup->group, rank); if (status != MPI_SUCCESS) { comex_error("MPI_Group_rank: Failed ", status); } return COMEX_SUCCESS; } int comex_group_size(comex_group_t group, int *size) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); status = MPI_Group_size(igroup->group, size); if (status != MPI_SUCCESS) { comex_error("MPI_Group_size: Failed ", status); } return COMEX_SUCCESS; } int comex_group_comm(comex_group_t group, MPI_Comm *comm) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); *comm = igroup->comm; return COMEX_SUCCESS; } int comex_group_translate_world(comex_group_t group, int group_rank, int *world_rank) { if (COMEX_GROUP_WORLD == group) { *world_rank = group_rank; } else { comex_igroup_t *igroup = comex_get_igroup_from_group(group); comex_igroup_t *world_igroup = comex_get_igroup_from_group(COMEX_GROUP_WORLD); int status = MPI_Group_translate_ranks( igroup->group, 1, &group_rank, world_igroup->group, world_rank); if (status != MPI_SUCCESS) { comex_error("MPI_Group_translate_ranks: Failed ", status); } } return COMEX_SUCCESS; } /** * Destroys the given comex igroup. */ void comex_igroup_finalize(comex_igroup_t *igroup) { int status; win_link_t *curr_win; win_link_t *next_win; assert(igroup); if (igroup->group != MPI_GROUP_NULL) { status = MPI_Group_free(&igroup->group); if (status != MPI_SUCCESS) { comex_error("MPI_Group_free: Failed ", status); } } if (igroup->comm != MPI_COMM_NULL) { status = MPI_Comm_free(&igroup->comm); if (status != MPI_SUCCESS) { comex_error("MPI_Comm_free: Failed ", status); } } /* Remove all windows associated with this group */ curr_win = igroup->win_list; while (curr_win != NULL) { next_win = curr_win->next; MPI_Win_free(&curr_win->win); free(curr_win); curr_win = next_win; } } int comex_group_free(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; /* find the group to free */ while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { break; } previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; } /* make sure we found a group */ assert(current_group_list_item != NULL); /* remove the group from the linked list */ if (previous_group_list_item != NULL) { previous_group_list_item->next = current_group_list_item->next; } else { group_list = current_group_list_item->next; } /* free the group */ comex_igroup_finalize(current_group_list_item); free(current_group_list_item); return COMEX_SUCCESS; } int comex_group_create( int n, int *pid_list, comex_group_t id_parent, comex_group_t *id_child) { int status; int grp_me; comex_igroup_t *igroup_child = NULL; MPI_Group *group_child = NULL; MPI_Comm *comm_child = NULL; comex_igroup_t *igroup_parent = NULL; MPI_Group *group_parent = NULL; MPI_Comm *comm_parent = NULL; /* create the node in the linked list of groups and */ /* get the child's MPI_Group and MPI_Comm, to be populated shortly */ comex_create_group_and_igroup(id_child, &igroup_child); group_child = &(igroup_child->group); comm_child = &(igroup_child->comm); /* get the parent's MPI_Group and MPI_Comm */ igroup_parent = comex_get_igroup_from_group(id_parent); group_parent = &(igroup_parent->group); comm_parent = &(igroup_parent->comm); status = MPI_Group_incl(*group_parent, n, pid_list, group_child); if (status != MPI_SUCCESS) { comex_error("MPI_Group_incl: Failed ", status); } { MPI_Comm comm, comm1, comm2; int lvl=1, local_ldr_pos; MPI_Group_rank(*group_child, &grp_me); if (grp_me == MPI_UNDEFINED) { /* FIXME: keeping the group around for now */ return COMEX_SUCCESS; } /* SK: sanity check for the following bitwise operations */ assert(grp_me>=0); MPI_Comm_dup(MPI_COMM_SELF, &comm); /* FIXME: can be optimized away */ local_ldr_pos = grp_me; while(n>lvl) { int tag=0; int remote_ldr_pos = local_ldr_pos^lvl; if (remote_ldr_pos < n) { int remote_leader = pid_list[remote_ldr_pos]; MPI_Comm peer_comm = *comm_parent; int high = (local_ldr_posid = COMEX_GROUP_WORLD; group_list->next = NULL; group_list->win_list = NULL; #ifdef USE_MPI_ERRORS_RETURN MPI_Comm_set_errhandler(l_state.world_comm,MPI_ERRORS_RETURN); #endif /* save MPI world group and communicatior in COMEX_GROUP_WORLD */ group_list->comm = l_state.world_comm; MPI_Comm_group(group_list->comm, &(group_list->group)); } void comex_group_finalize() { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; /* don't free the world group (the list head) */ current_group_list_item = current_group_list_item->next; while (current_group_list_item != NULL) { previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; comex_igroup_finalize(previous_group_list_item); free(previous_group_list_item); } /* ok, now free the world group, but not the world comm */ MPI_Group_free(&(group_list->group)); free(group_list); group_list = NULL; } /** * Add an MPI window to the window list in the group */ void comex_igroup_add_win(comex_group_t id, MPI_Win win) { comex_igroup_t *current_group_list_item = group_list; win_link_t *curr_win = NULL; win_link_t *new_win = NULL; /* find the group*/ while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { break; } current_group_list_item = current_group_list_item->next; } /* add window to group. Start by finding last member of window list */ curr_win = current_group_list_item->win_list; while (curr_win != NULL && curr_win->next != NULL) { curr_win = curr_win->next; } new_win = malloc(sizeof(win_link_t)); if (curr_win == NULL) { new_win->next = NULL; new_win->prev = NULL; new_win->win = win; current_group_list_item->win_list = new_win; } else { curr_win->next = new_win; new_win->next = NULL; new_win->prev = curr_win; new_win->win = win; } } /** * Remove an MPI window from the window list in the group. This only removes * the window from the group list, it does not get rid of the window itself. */ void comex_igroup_delete_win(comex_group_t id, MPI_Win win) { comex_igroup_t *current_group_list_item = group_list; win_link_t *curr_win; win_link_t *prev_win; /* find the group*/ while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { break; } current_group_list_item = current_group_list_item->next; } /* find the window in list */ /* NOTE: Might be able to simplify this if win_link_t only has next and not * prev data members. Need to see if prev is used anywhere */ curr_win = current_group_list_item->win_list; if (curr_win != NULL) { while (curr_win->win != win) { curr_win = curr_win->next; } if (curr_win->prev != NULL && curr_win->next != NULL) { /* window is not at start or end of list */ prev_win = curr_win->prev; prev_win->next = curr_win->next; (curr_win->next)->prev = curr_win->prev; } else if (curr_win->prev == NULL && curr_win->next != NULL) { /* window is at the start of the list */ (curr_win->next)->prev = NULL; current_group_list_item->win_list = curr_win->next; } else if (curr_win->next == NULL && curr_win->prev != NULL) { /* window is at the end of the list */ (curr_win->prev)->next = NULL; } else { /* only one window in list */ current_group_list_item->win_list = NULL; } free(curr_win); } } ga-5.9.2/comex/src-mpi3/groups.h000066400000000000000000000015121500715745200163630ustar00rootroot00000000000000/** * Private header file for comex groups backed by MPI_comm. * * The rest of the comex group functions are defined in the public comex.h. * * @author Jeff Daily */ #ifndef _COMEX_GROUPS_H_ #define _COMEX_GROUPS_H_ #include #include "comex.h" typedef struct win_link { struct win_link *next; struct win_link *prev; MPI_Win win; } win_link_t; typedef struct group_link { struct group_link *next; comex_group_t id; MPI_Comm comm; MPI_Group group; win_link_t *win_list; } comex_igroup_t; extern void comex_group_init(); extern void comex_group_finalize(); extern comex_igroup_t* comex_get_igroup_from_group(comex_group_t group); extern void comex_igroup_add_win(comex_group_t group, MPI_Win win); extern void comex_igroup_delete_win(comex_group_t group, MPI_Win win); #endif /* _COMEX_GROUPS_H_ */ ga-5.9.2/comex/src-mpi3/reg_win.c000066400000000000000000000412641500715745200165010ustar00rootroot00000000000000/** * Registration window. * * Defensive programming via copious COMEX_ASSERT statements is encouraged. */ #if HAVE_CONFIG_H # include "config.h" #endif /* C headers */ #include #include #include #include /* 3rd party headers */ #include /* our headers */ #include "comex.h" #include "comex_impl.h" #include "reg_win.h" #define STATIC static inline /* the static members in this module */ static reg_entry_t **reg_win = NULL; /* list of windows on each process. This array contains the starting node in a linked list */ static int reg_nprocs = 0; /* number of windows (one per process) */ /* the static functions in this module */ static reg_return_t seg_cmp(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len, int op); static reg_return_t seg_intersects(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len); static reg_return_t seg_contains(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len); static reg_return_t reg_entry_intersects(reg_entry_t *reg_entry, void *buf, int len); static reg_return_t reg_entry_contains(reg_entry_t *reg_entry, void *buf, int len); #define TEST_FOR_INTERSECTION 0 #define TEST_FOR_CONTAINMENT 1 /*#define TEST_DEBUG*/ #ifdef TEST_DEBUG int reg_win_rank; int reg_win_nprocs; #endif /** * Detects whether two memory segments intersect or one contains the other. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * @param[in] op op to perform, either TEST_FOR_INTERSECTION or * TEST_FOR_CONTAINMENT * * @pre NULL != reg_beg * @pre NULL != oth_beg * * @return RR_SUCCESS on success */ STATIC reg_return_t seg_cmp(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len, int op) { ptrdiff_t reg_beg = 0; ptrdiff_t reg_end = 0; ptrdiff_t oth_beg = 0; ptrdiff_t oth_end = 0; int result = 0; /* preconditions */ COMEX_ASSERT(NULL != reg_addr); COMEX_ASSERT(NULL != oth_addr); /* if reg_len = 0 and oth_len = 0 do a direct comparison. This assumes that * registered region is zero length and we are just using a small buffer to * keep from running into problems associated with not allocating memory on * some processors */ if (reg_len == 0 && oth_len == 0) { if (reg_addr == oth_addr) { return RR_SUCCESS; } else { return RR_FAILURE; } } /* casts to ptrdiff_t since arithmetic on void* is undefined */ reg_beg = (ptrdiff_t)(reg_addr); reg_end = reg_beg + (ptrdiff_t)(reg_len); oth_beg = (ptrdiff_t)(oth_addr); oth_end = oth_beg + (ptrdiff_t)(oth_len); /* hack? we had problems with adjacent registered memory regions and * when the length of the query region was 0 */ if (oth_beg == oth_end) { oth_end += 1; } switch (op) { case TEST_FOR_INTERSECTION: result = (reg_beg >= oth_beg && reg_beg < oth_end) || (reg_end > oth_beg && reg_end <= oth_end); #if DEBUG printf("[%d] TEST_FOR_INTERSECTION " "(%td >= %td [%d] && %td < %td [%d]) ||" "(%td > %td [%d] && %td <= %td [%d])\n", g_state.rank, reg_beg, oth_beg, (reg_beg >= oth_beg), reg_beg, oth_end, (reg_beg < oth_end), reg_end, oth_beg, (reg_end > oth_beg), reg_end, oth_end, (reg_end <= oth_end)); #endif break; case TEST_FOR_CONTAINMENT: result = reg_beg <= oth_beg && reg_end >= oth_end; #if DEBUG printf("[%d] TEST_FOR_CONTAINMENT " "%td <= %td [%d] && %td >= %td [%d]\n", g_state.rank, reg_beg, oth_beg, (reg_beg <= oth_beg), reg_end, oth_end, (reg_end >= oth_end)); #endif break; default: COMEX_ASSERT(0); } if (result) { return RR_SUCCESS; } else { return RR_FAILURE; } } /** * Detects whether two memory segments intersect. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * * @pre NULL != reg_beg * @pre NULL != oth_beg * * @return RR_SUCCESS on success */ STATIC reg_return_t seg_intersects(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len) { /* preconditions */ COMEX_ASSERT(NULL != reg_addr); COMEX_ASSERT(NULL != oth_addr); return seg_cmp( reg_addr, reg_len, oth_addr, oth_len, TEST_FOR_INTERSECTION); } /** * Detects whether the first memory segment contains the other. * * @param[in] reg_addr starting address of original segment * @param[in] reg_len length of original segment * @param[in] oth_addr starting address of other segment * @param[in] oth_len length of other segment * * @pre NULL != reg_beg * @pre NULL != oth_beg * * @return RR_SUCCESS on success */ STATIC reg_return_t seg_contains(void *reg_addr, size_t reg_len, void *oth_addr, size_t oth_len) { /* preconditions */ COMEX_ASSERT(NULL != reg_addr); COMEX_ASSERT(NULL != oth_addr); return seg_cmp( reg_addr, reg_len, oth_addr, oth_len, TEST_FOR_CONTAINMENT); } /** * Detects whether two memory segments intersect. * * @param[in] reg_entry the registration entry * @param[in] buf starting address for the contiguous memory region * @param[in] len length of the contiguous memory region * * @pre NULL != reg_entry * @pre NULL != buf * @pre len >= 0 * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_intersects(reg_entry_t *reg_entry, void *buf, int len) { #if DEBUG printf("[%d] reg_entry_intersects(reg_entry=%p, buf=%p, len=%d)\n", g_state.rank, reg_entry, buf, len); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_entry); COMEX_ASSERT(NULL != buf); COMEX_ASSERT(len >= 0); return seg_intersects( reg_entry->buf, reg_entry->len, buf, len); } /** * Detects whether the first memory segment contains the other. * * @param[in] reg_entry the registration entry * @param[in] buf starting address for the contiguous memory region * @param[in] len length of the contiguous memory region * * @pre NULL != reg_entry * @pre NULL != buf * @pre len >= 0 * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_contains(reg_entry_t *reg_entry, void *buf, int len) { #if DEBUG printf("[%d] reg_entry_contains(reg_entry=%p, buf=%p, len=%d)\n", g_state.rank, reg_entry, buf, len); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_entry); COMEX_ASSERT(NULL != buf); COMEX_ASSERT(len >= 0); return seg_contains( reg_entry->buf, reg_entry->len, buf, len); } /** * Remove registration window entry without deregistration. * * @param[in] rank the rank where the entry came from * @param[in] reg_entry the entry * * @pre NULL != reg_entry * @pre 0 <= rank && rank < reg_nprocs * * @return RR_SUCCESS on success */ STATIC reg_return_t reg_entry_destroy(int rank, reg_entry_t *reg_entry) { #if DEBUG printf("[%d] reg_entry_destroy(rank=%d, reg_entry=%p)\n" "buf=%p len=%zu name=%s mapped=%p\n", g_state.rank, rank, reg_entry, reg_entry->buf, reg_entry->len, reg_entry->name, reg_entry->mapped); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_entry); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); /* free window entry */ free(reg_entry); return RR_SUCCESS; } /** * Create internal data structures for the registration window. * * @param[in] nprocs number of registration windows to create i.e. one per * process * * @pre this function is called once to initialize the internal data * structures and cannot be called again until reg_win_destroy() has been * called * * @see reg_win_destroy() * * @return RR_SUCCESS on success */ reg_return_t reg_win_init(int nprocs) { int i = 0; #ifdef TEST_DEBUG reg_win_nprocs = nprocs; MPI_Comm_rank(l_state.world_comm,®_win_rank); #endif #if DEBUG printf("[%d] reg_win_init(nprocs=%d)\n", g_state.rank, nprocs); #endif /* preconditions */ COMEX_ASSERT(NULL == reg_win); COMEX_ASSERT(0 == reg_nprocs); /* keep the number of processors around for later use */ reg_nprocs = nprocs; /* allocate the registration window list: */ reg_win = (reg_entry_t **)malloc(sizeof(reg_entry_t*) * reg_nprocs); COMEX_ASSERT(reg_win); /* initialize the registration window list: */ for (i = 0; i < reg_nprocs; ++i) { reg_win[i] = NULL; } return RR_SUCCESS; } /** * Deregister and destroy all window entries and associated buffers. * * @pre this function is called once to destroy the internal data structures * and cannot be called again until reg_win_init() has been called * * @see reg_win_init() * * @return RR_SUCCESS on success */ reg_return_t reg_win_destroy() { int i = 0; #if DEBUG printf("[%d] reg_win_destroy()\n", g_state.rank); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_win); COMEX_ASSERT(0 != reg_nprocs); for (i = 0; i < reg_nprocs; ++i) { reg_entry_t *runner = reg_win[i]; while (runner) { reg_entry_t *previous = runner; /* pointer to previous runner */ /* get next runner */ runner = runner->next; /* destroy the entry */ reg_entry_destroy(i, previous); } } /* free registration window list */ free(reg_win); reg_win = NULL; /* reset the number of windows */ reg_nprocs = 0; return RR_SUCCESS; } /** * Locate a registration window entry which contains the given segment * completely. * * @param[in] rank rank of the process * @param[in] buf starting address of the buffer * @parma[in] len length of the buffer * * @pre 0 <= rank && rank < reg_nprocs * @pre reg_win_init() was previously called * * @return the reg window entry, or NULL on failure */ reg_entry_t* reg_win_find(int rank, void *buf, int len) { reg_entry_t *entry = NULL; reg_entry_t *runner = NULL; #ifdef TEST_DEBUG printf("[%d] reg_win_find(rank=%d, buf=%p, len=%d reg_win=%p)\n", reg_win_rank, rank, buf, len, reg_win); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_win); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); runner = reg_win[rank]; while (runner && NULL == entry) { #ifdef TEST_DEBUG printf("p[%d] searching on p[%d] runner rank: %d buf: %p len: %d\n", reg_win_rank,rank, runner->rank,runner->buf,runner->len); #endif if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { entry = runner; #ifdef TEST_DEBUG printf("[%d] reg_win_find entry found " "reg_entry=%p buf=%p len=%d " "runner: rank=%d buf=%p len=%d\n", reg_win_rank, runner, buf, len, runner->rank, runner->buf, runner->len); #endif } runner = runner->next; } #ifndef NDEBUG /* we COMEX_ASSERT that the found entry was unique */ while (runner) { if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { #ifdef TEST_DEBUG printf("[%d] reg_win_find duplicate found " "reg_entry=%p buf=%p len=%d " "rank=%d buf=%p len=%d\n", reg_win_rank, runner, buf, len, runner->rank, runner->buf, runner->len); #endif COMEX_ASSERT(0); } runner = runner->next; } #endif return entry; } /** * Locate a registration window entry which intersects the given segment. * * @param[in] rank rank of the process * @param[in] buf starting address of the buffer * @parma[in] len length of the buffer * * @pre 0 <= rank && rank < reg_nprocs * @pre reg_win_init() was previously called * * @return the reg window entry, or NULL on failure */ reg_entry_t* reg_win_find_intersection(int rank, void *buf, int len) { reg_entry_t *entry = NULL; reg_entry_t *runner = NULL; #if DEBUG printf("[%d] reg_win_find_intersection(rank=%d, buf=%p, len=%d)\n", g_state.rank, rank, buf, len); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_win); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); runner = reg_win[rank]; while (runner && NULL == entry) { if (RR_SUCCESS == reg_entry_intersects(runner, buf, len)) { entry = runner; } runner = runner->next; } /* we COMEX_ASSERT that the found entry was unique */ while (runner) { if (RR_SUCCESS == reg_entry_contains(runner, buf, len)) { COMEX_ASSERT(0); } runner = runner->next; } return entry; } /** * Create a new registration entry based on the given members. This new * entry contains the buffer location on the remote processor, size of the * buffer, the MPI window that the buffer belongs to and the group associated * with the window * @param rank processor rank for which buffer location applies * @param buf pointer to memory allocation * @param len size of memory allocation * @param win MPI window for memory allocation * @param group group associated with memory allocation * * @return return new entry in linked list */ reg_entry_t* reg_win_insert(int rank, void *buf, int len, MPI_Win win, comex_igroup_t *group) { reg_entry_t *node = NULL; #if DEBUG printf("[%d] reg_win_insert(rank=%d, buf=%p, len=%d, name=%s, mapped=%p)\n", g_state.rank, rank, buf, len, name, mapped); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_win); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); COMEX_ASSERT(NULL != buf); COMEX_ASSERT(len >= 0); COMEX_ASSERT(NULL != group); COMEX_ASSERT(NULL == reg_win_find(rank, buf, len)); COMEX_ASSERT(NULL == reg_win_find_intersection(rank, buf, len)); /* allocate the new entry */ node = (reg_entry_t *)malloc(sizeof(reg_entry_t)); COMEX_ASSERT(node); /* initialize the new entry */ node->rank = rank; node->buf = buf; node->len = len; node->win = win; node->igroup = group; node->next = NULL; /* push new entry to tail of linked list */ if (NULL == reg_win[rank]) { reg_win[rank] = node; } else { reg_entry_t *runner = reg_win[rank]; while (runner->next) { runner = runner->next; } runner->next = node; } return node; } /** * Removes the reg window entry associated with the given rank and buffer. * * If this process owns the buffer, it will unregister the buffer, as well. * * @param[in] rank * @param[in] buf * * @pre 0 <= rank && rank < reg_nprocs * @pre NULL != buf * @pre reg_win_init() was previously called * @pre NULL != reg_win_find(rank, buf, 0) * * @return RR_SUCCESS on success * RR_FAILURE otherwise */ reg_return_t reg_win_delete(int rank, void *buf) { reg_return_t status = RR_FAILURE; reg_entry_t *runner = NULL; reg_entry_t *previous_runner = NULL; #if DEBUG printf("[%d] reg_win_delete(rank=%d, buf=%p)\n", g_state.rank, rank, buf); #endif /* preconditions */ COMEX_ASSERT(NULL != reg_win); COMEX_ASSERT(0 <= rank && rank < reg_nprocs); COMEX_ASSERT(NULL != buf); COMEX_ASSERT(NULL != reg_win_find(rank, buf, 0)); /* this is more restrictive than reg_win_find() in that we locate * exactlty the same region starting address */ runner = reg_win[rank]; while (runner) { if (runner->buf == buf) { break; } /* no match so match may be next runner */ previous_runner = runner; runner = runner->next; } /* we should have found an entry */ if (NULL == runner) { COMEX_ASSERT(0); return RR_FAILURE; } /* pop the entry out of the linked list */ if (previous_runner) { /* runner is a match and it is not the first entry in list */ previous_runner->next = runner->next; } else { /* buf corresponds to first entry in list */ reg_win[rank] = reg_win[rank]->next; } status = reg_entry_destroy(rank, runner); return status; } ga-5.9.2/comex/src-mpi3/reg_win.h000066400000000000000000000022201500715745200164730ustar00rootroot00000000000000#ifndef _REG_WINDOW_H_ #define _REG_WINDOW_H_ #include #include /** * Enumerate the return codes for registration window functions. */ typedef enum _reg_return_t { RR_SUCCESS=0, /**< success */ RR_FAILURE /**< non-specific failure */ } reg_return_t; /** * A registered contiguous memory region. */ typedef struct _reg_entry_t { comex_igroup_t *igroup; /**< link to group that generated window */ MPI_Win win; /**< handle to MPI window containing this region */ int rank; /**< rank where this region lives */ void *buf; /**< starting address of region */ size_t len; /**< length of region */ struct _reg_entry_t *next; /**< next memory region in list */ } reg_entry_t; /* functions * * documentation is in the *.c file */ reg_return_t reg_win_init(int nprocs); reg_return_t reg_win_destroy(); reg_entry_t *reg_win_find(int rank, void *buf, int len); reg_entry_t *reg_win_insert(int rank, void *buf, int len, MPI_Win win, comex_igroup_t *group); reg_return_t reg_win_delete(int rank, void *buf); #endif /* _REG_WINDOW_H_ */ ga-5.9.2/comex/src-ofa/000077500000000000000000000000001500715745200145715ustar00rootroot00000000000000ga-5.9.2/comex/src-ofa/Makefile.inc000066400000000000000000000013231500715745200170000ustar00rootroot00000000000000libcomex_la_SOURCES += src-ofa/comex.c libcomex_la_SOURCES += src-ofa/comex_impl.h libcomex_la_SOURCES += src-ofa/contig.c libcomex_la_SOURCES += src-ofa/device.c libcomex_la_SOURCES += src-ofa/device.h libcomex_la_SOURCES += src-ofa/fence.c libcomex_la_SOURCES += src-ofa/gpa.c libcomex_la_SOURCES += src-ofa/groups.c libcomex_la_SOURCES += src-ofa/groups.h libcomex_la_SOURCES += src-ofa/mutex.c libcomex_la_SOURCES += src-ofa/openib.c libcomex_la_SOURCES += src-ofa/openib.h libcomex_la_SOURCES += src-ofa/reg_cache.c libcomex_la_SOURCES += src-ofa/reg_cache.h libcomex_la_SOURCES += src-ofa/strided.c libcomex_la_SOURCES += src-ofa/vector.c libcomex_la_SOURCES += src-ofa/wait.c AM_CPPFLAGS += -I$(top_srcdir)/src-ofa ga-5.9.2/comex/src-ofa/comex.c000066400000000000000000000214571500715745200160610ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* C and/or system headers */ #include #include #include #include #include /* 3rd party headers */ #include #include /* our headers */ #include "comex.h" #include "device.h" #include "comex_impl.h" #include "groups.h" #include "reg_cache.h" /* exported state */ local_state l_state; /* static state */ static int initialized=0; /* for comex_initialized(), 0=false */ static long sc_page_size=-1; /* from sysconf, in bytes */ /* static function declarations */ static void *_comex_malloc_local(size_t size, void **rinfo); void comex_error(const char *msg, int code) { if (0 == l_state.rank) { fprintf(stderr,"Received an Error in Communication\n"); } MPI_Abort(l_state.world_comm, code); } int comex_malloc(void **ptrs, size_t size, comex_group_t group) { comex_igroup_t *igroup = NULL; MPI_Comm comm = MPI_COMM_NULL; int comm_size = -1; int comm_rank = -1; int rc = MPI_SUCCESS; void *src_buf = NULL; size_t max_size = size; void *local_rkey_buf = NULL; struct ibv_mr *mr = NULL; int *allgather_rkey_buf = NULL; int i = 0; /* preconditions */ assert(ptrs); igroup = comex_get_igroup_from_group(group); comm = igroup->comm; assert(comm != MPI_COMM_NULL); rc = MPI_Comm_rank(comm, &comm_rank); assert(rc == MPI_SUCCESS); rc = MPI_Comm_size(comm, &comm_size); assert(rc == MPI_SUCCESS); /* achieve consensus on the allocation size */ rc = MPI_Allreduce(&size, &max_size, 1, MPI_LONG, MPI_MAX, comm); assert(rc == MPI_SUCCESS); size = max_size; /* 0 Byte registrations are potential problems */ if (size < MIN_REGISTRATION_SIZE) { size = MIN_REGISTRATION_SIZE; } /* allocate and register the user level buffer */ ptrs[comm_rank] = _comex_malloc_local(sizeof(char)*max_size, &local_rkey_buf); assert(local_rkey_buf); /* exchange buffer address */ /* @TODO: Consider usijng MPI_IN_PLACE? */ memcpy(&src_buf, &ptrs[comm_rank], sizeof(void *)); MPI_Allgather(&src_buf, sizeof(void *), MPI_BYTE, ptrs, sizeof(void *), MPI_BYTE, comm); /* allocate buffer for collecting remote keys */ allgather_rkey_buf = (int *)malloc(sizeof(int) * comm_size); assert(allgather_rkey_buf); /* set the rkey of the local buffer */ mr = (struct ibv_mr *)local_rkey_buf; allgather_rkey_buf[comm_rank] = mr->rkey; /* exchange rkeys */ MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, allgather_rkey_buf, 1, MPI_INT, comm); /* insert in the remote registration cache */ for (i = 0; i < comm_size; i++) { int world_rank; comex_group_translate_world(group, i, &world_rank); if (i == comm_rank) { continue; } reg_cache_insert(world_rank, ptrs[i], size, -1, allgather_rkey_buf[i], NULL); } /* free the temporary buffer */ free(allgather_rkey_buf); MPI_Barrier(comm); return 0; } int comex_malloc_mem_dev(void *ptrs[], size_t size, comex_group_t group, const char* device) { return comex_malloc(ptrs,size,group); } int comex_free(void *ptr, comex_group_t group) { comex_igroup_t *igroup = NULL; MPI_Comm comm = MPI_COMM_NULL; int comm_size = -1; int comm_rank = -1; int i; long **allgather_ptrs = NULL; int rc = -1; /* preconditions */ assert(NULL != ptr); igroup = comex_get_igroup_from_group(group); comm = igroup->comm; assert(comm != MPI_COMM_NULL); rc = MPI_Comm_rank(comm, &comm_rank); assert(rc == MPI_SUCCESS); rc = MPI_Comm_size(comm, &comm_size); assert(rc == MPI_SUCCESS); /* allocate receive buffer for exhange of pointers */ allgather_ptrs = (long **)malloc(sizeof(void *) * comm_size); assert(allgather_ptrs); /* exchange of pointers */ rc = MPI_Allgather(&ptr, sizeof(void *), MPI_BYTE, allgather_ptrs, sizeof(void *), MPI_BYTE, comm); assert(rc == MPI_SUCCESS); /* remove all ptrs from registration cache */ for (i = 0; i < comm_size; i++) { int world_rank; comex_group_translate_world(group, i, &world_rank); if (i == comm_rank) { continue; } reg_cache_delete(world_rank, allgather_ptrs[i]); } /* remove my ptr from reg cache and free ptr */ comex_free_local(ptr); /* Synchronize: required by COMEX semantics */ free(allgather_ptrs); MPI_Barrier(comm); return 0; } int comex_free_dev(void *ptr, comex_group_t group) { return comex_free(ptr, group); } static void *_comex_malloc_local(size_t size, void **rinfo) { void *ptr = NULL; int rc = 0; /* allocate the user level buffer */ rc = posix_memalign(&ptr, sc_page_size, sizeof(char)*size); assert(0 == rc); assert(ptr); /* register the buffer and check the return info */ *rinfo = COMEXD_register_memory(ptr, size); return ptr; } // recursively find individual regions which can be registered void recursive_reg(void *ptr, size_t size) { assert(sc_page_size >= 0); if (size <= (unsigned long)sc_page_size) return; if (COMEXD_register_memory(ptr, size)) return; else { recursive_reg(ptr, size / 2); recursive_reg(ptr + size /2, size / 2); } } void *comex_malloc_local(size_t size) { void *ptr = NULL; void *rinfo = NULL; ptr = _comex_malloc_local(size, &rinfo); if (!rinfo) { // registration failed recursive_reg(ptr, size); } return ptr; } int comex_free_local(void *ptr) { assert(ptr != NULL); COMEXD_deregister_memory(ptr); free(ptr); return COMEX_SUCCESS; } int _comex_init(MPI_Comm comm) { int init_flag; /* Test if initialized has been called more than once */ if (initialized) { return 0; } initialized = 1; /* Assert MPI has been initialized */ MPI_Initialized(&init_flag); assert(init_flag); /* Duplicate the World Communicator */ MPI_Comm_dup(comm, &(l_state.world_comm)); /* My Rank */ MPI_Comm_rank(l_state.world_comm, &(l_state.rank)); /* World Size */ MPI_Comm_size(l_state.world_comm, &(l_state.size)); // init groups comex_group_init(); // Initialize the number of outstanding messages l_state.num_outstanding = 0; // Get the page size for malloc sc_page_size = sysconf(_SC_PAGESIZE); // Initialize the COMEX device COMEXD_initialize(); /* Synch - Sanity Check */ MPI_Barrier(l_state.world_comm); return 0; } int comex_init() { return _comex_init(MPI_COMM_WORLD); } int comex_init_comm(MPI_Comm comm) { return _comex_init(comm); } int comex_init_args(int *argc, char ***argv) { int rc; int init_flag; MPI_Initialized(&init_flag); if(!init_flag) { MPI_Init(argc, argv); } rc = comex_init(); return rc; } int comex_finalize() { int rc; // Make sure that all outstanding operations are done comex_wait_all(COMEX_GROUP_WORLD); // Call COMEX device specific finalize COMEXD_finalize(); MPI_Barrier(l_state.world_comm); // destroys all groups and their communicators comex_group_finalize(); // destroy the primary communicator rc = MPI_Comm_free(&l_state.world_comm); assert(MPI_SUCCESS == rc); return COMEX_SUCCESS; } void comex_cleanup() { comex_finalize(); } int comex_rmw(int op, void *ploc, void *prem, int extra, int proc, comex_group_t group) { if (op == COMEX_FETCH_AND_ADD) { COMEXD_network_lock(proc); comex_get(prem, ploc, sizeof(int), proc, group); (*(int *)ploc) += extra; comex_put(ploc, prem, sizeof(int), proc, group); (*(int *)ploc) -= extra; COMEXD_network_unlock(proc); } else if (op == COMEX_FETCH_AND_ADD_LONG) { COMEXD_network_lock(proc); comex_get(prem, ploc, sizeof(long), proc, group); (*(long *)ploc) += extra; comex_put(ploc, prem, sizeof(long), proc, group); (*(long *)ploc) -= extra; COMEXD_network_unlock(proc); } else if (op == COMEX_SWAP) { int tmp; COMEXD_network_lock(proc); comex_get(prem, &tmp, sizeof(int), proc, group); comex_put(ploc, prem, sizeof(int), proc, group); COMEXD_network_unlock(proc); *(int*)ploc = tmp; } else if (op == COMEX_SWAP_LONG) { long tmp; COMEXD_network_lock(proc); comex_get(prem, &tmp, sizeof(long), proc, group); comex_put(ploc, prem, sizeof(long), proc, group); COMEXD_network_unlock(proc); *(long*)ploc = tmp; } else { assert(0); } return 0; } int comex_initialized() { return initialized; } ga-5.9.2/comex/src-ofa/comex_impl.h000066400000000000000000000011211500715745200170710ustar00rootroot00000000000000#ifndef COMEX_IMPL_H_ #define COMEX_IMPL_H_ #include #include "device.h" #define MIN_REGISTRATION_SIZE 64 #define ACC_PIPELINE_THRESHOLD 8192 typedef struct { MPI_Comm world_comm; int rank; int size; // buffer for lock void **atomic_lock_buf; void *local_lock_buf; void *acc_buf; int acc_buf_len; void *put_buf; int put_buf_len; void *get_buf; int get_buf_len; // Number of outstanding data transfers unsigned long num_outstanding; int comex_openib_use_dreg; } local_state; extern local_state l_state; #endif ga-5.9.2/comex/src-ofa/contig.c000066400000000000000000000037251500715745200162270ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include "comex.h" #include "comex_impl.h" int comex_put(void *src, void *dst, int bytes, int proc, comex_group_t group) { assert(src && dst && bytes); COMEXD_put_nbi(src, dst, bytes, proc); COMEXD_waitproc(proc); return 0; } int comex_get(void *src, void *dst, int bytes, int proc, comex_group_t group) { COMEXD_get_nbi(src, dst, bytes, proc); COMEXD_waitproc(proc); return 0; } int comex_acc(int datatype, void *scale, void *src_ptr, void *dst_ptr, int bytes, int proc, comex_group_t group) { double *get_buf = (double *)l_state.acc_buf; double *_src_buf = (double *)src_ptr; double calc_scale = *(double *)scale; int m, limit; assert(bytes <= l_state.acc_buf_len); assert(datatype == COMEX_ACC_DBL); assert(get_buf); COMEXD_network_lock(proc); comex_get(dst_ptr, get_buf, bytes, proc, group); for (m=0, limit=bytes/sizeof(double); m #include #include #include #include #include "comex.h" #include "comex_impl.h" #include "openib.h" // Device specific implementation // void* COMEXD_register_memory(void *buf, int len) { return openib_register_memory(buf, len); } int COMEXD_deregister_memory(void *buf) { return openib_deregister_memory(buf); } int COMEXD_put_nbi(void *src, void *dst, int bytes, int proc) { if (proc == l_state.rank) { (void) memcpy(dst, src, bytes); return 0; } return openib_put_nbi(src, dst, bytes, proc); } int COMEXD_get_nbi(void *src, void *dst, int bytes, int proc) { if (proc == l_state.rank) { (void) memcpy(dst, src, bytes); return 0; } return openib_get_nbi(src, dst, bytes, proc); } void COMEXD_network_lock(int proc) { openib_network_lock(proc); } void COMEXD_network_unlock(int proc) { openib_network_unlock(proc); } int COMEXD_waitproc(int proc) { return openib_waitproc(proc); } int COMEXD_waitall() { openib_waitall(); return 0; } int COMEXD_initialize() { return openib_initialize(); } int COMEXD_finalize() { return openib_finalize(); } ga-5.9.2/comex/src-ofa/device.h000066400000000000000000000010201500715745200161720ustar00rootroot00000000000000#include #include #include #include #include // Device specific functions void* COMEXD_register_memory(void *buf, int len); int COMEXD_deregister_memory(void *buf); int COMEXD_put_nbi(void *src, void *dst, int bytes, int proc); int COMEXD_get_nbi(void *src, void *dst, int bytes, int proc); void COMEXD_network_lock(int proc); void COMEXD_network_unlock(int proc); int COMEXD_waitproc(int proc); int COMEXD_waitall(); int COMEXD_initialize(); int COMEXD_finalize(); ga-5.9.2/comex/src-ofa/fence.c000066400000000000000000000011111500715745200160070ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include "comex.h" #include "comex_impl.h" #include "device.h" int comex_fence_all(comex_group_t group) { COMEXD_waitall(); return COMEX_SUCCESS; } int comex_fence_proc(int proc, comex_group_t group) { COMEXD_waitall(); return COMEX_SUCCESS; } int comex_barrier(comex_group_t group) { assert(l_state.world_comm); comex_fence_all(group); MPI_Barrier(l_state.world_comm); return COMEX_SUCCESS; } ga-5.9.2/comex/src-ofa/gpa.c000066400000000000000000000000001500715745200154720ustar00rootroot00000000000000ga-5.9.2/comex/src-ofa/groups.c000066400000000000000000000225401500715745200162570ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDLIB_H # include #endif #if HAVE_ASSERT_H # include #endif #include "comex.h" #include "comex_impl.h" #include "groups.h" /* COMEX has the notion of a default group and a world group. */ comex_group_t COMEX_Default_Proc_Group = 0; comex_group_t COMEX_World_Proc_Group = 0; /* the HEAD of the group linked list */ comex_igroup_t *group_list = NULL; /** * Return the comex_igroup_t instance given the comex_group_t. * * The group linked list is searched sequentially until the given group * is found. It is an error if this function is called before * comex_group_init(). An error occurs if the given group is not found. */ comex_igroup_t* comex_get_igroup_from_group(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; assert(group_list != NULL); while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { return current_group_list_item; } current_group_list_item = current_group_list_item->next; } comex_error("comex_group_t lookup failed", -1); return NULL; } /** * Creates and associates an comex_group_t with an comex_igroup_t. * * This does *not* initialize the members of the comex_igroup_t. */ static void comex_create_group_and_igroup( comex_group_t *id, comex_igroup_t **igroup) { comex_igroup_t *new_group_list_item = NULL; comex_igroup_t *last_group_list_item = NULL; /* find the last group in the group linked list */ last_group_list_item = group_list; while (last_group_list_item->next != NULL) { last_group_list_item = last_group_list_item->next; } /* create, init, and insert the new node for the linked list */ new_group_list_item = malloc(sizeof(comex_igroup_t)); new_group_list_item->id = last_group_list_item->id + 1; new_group_list_item->next = NULL; last_group_list_item->next = new_group_list_item; /* return the group id and comex_igroup_t */ *igroup = new_group_list_item; *id = new_group_list_item->id; } /** * Returns the rank of this process within the given group. */ int comex_group_rank(comex_group_t id, int *rank) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(id); status = MPI_Group_rank(igroup->group, rank); if (status != MPI_SUCCESS) { comex_error("MPI_Group_rank: Failed ", status); } return COMEX_SUCCESS; } /** * Returns the size of a group. */ int comex_group_size(comex_group_t id, int *size) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(id); status = MPI_Group_size(igroup->group, size); if (status != MPI_SUCCESS) { comex_error("MPI_Group_size: Failed ", status); } return COMEX_SUCCESS; } int comex_group_comm(comex_group_t group, MPI_Comm *comm) { comex_igroup_t *igroup = comex_get_igroup_from_group(group); *comm = igroup->comm; return COMEX_SUCCESS; } /** * Translates the given rank from the given group into that of the world group. */ int comex_group_translate_world(comex_group_t id, int group_rank, int *world_rank) { int status; MPI_Group world_group; comex_igroup_t *igroup = comex_get_igroup_from_group(id); status = MPI_Comm_group(l_state.world_comm, &world_group); if (status != MPI_SUCCESS) { comex_error("MPI_Comm_group: Failed ", status); } status = MPI_Group_translate_ranks( igroup->group, 1, &group_rank, world_group, world_rank); if (status != MPI_SUCCESS) { comex_error("MPI_Group_translate_ranks: Failed ", status); } return COMEX_SUCCESS; } /** * Sets the default comex_group_t. */ void comex_group_set_default(comex_group_t id) { /* sanity check that the group is valid */ comex_igroup_t *igroup = comex_get_igroup_from_group(id); assert(NULL != igroup); COMEX_Default_Proc_Group = id; } /** * Gets the default comex_group_t. */ void comex_group_get_default(comex_group_t *group_out) { *group_out = COMEX_Default_Proc_Group; } /** * Gets the world comex_group_t. */ void comex_group_get_world(comex_group_t *group_out) { *group_out = COMEX_World_Proc_Group; } /** * Destroys the given comex_igroup_t. */ static void comex_igroup_finalize(comex_igroup_t *igroup) { int status; assert(igroup); status = MPI_Group_free(&igroup->group); if (status != MPI_SUCCESS) { comex_error("MPI_Group_free: Failed ", status); } if (igroup->comm != MPI_COMM_NULL) { status = MPI_Comm_free(&igroup->comm); if (status != MPI_SUCCESS) { comex_error("MPI_Comm_free: Failed ", status); } } } /** * Removes and destroys the given comex_group_t from the group linked list. */ int comex_group_free(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; /* find the group to free */ while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { break; } previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; } /* make sure we found a group */ assert(current_group_list_item != NULL); /* remove the group from the linked list */ if (previous_group_list_item != NULL) { previous_group_list_item->next = current_group_list_item->next; } /* free the group */ comex_igroup_finalize(current_group_list_item); free(current_group_list_item); return COMEX_SUCCESS; } /** * Create a child group for to the given group. * * @param[in] n #procs in this group (<= that in group_parent) * @param[in] pid_list The list of proc ids (w.r.t. group_parent) * @param[out] id_child Handle to store the created group * @param[in] id_parent Parent group */ int comex_group_create( int n, int *pid_list, comex_group_t id_parent, comex_group_t *id_child) { int status; int grp_me; comex_igroup_t *igroup_child = NULL; MPI_Group *group_child = NULL; MPI_Comm *comm_child = NULL; comex_igroup_t *igroup_parent = NULL; MPI_Group *group_parent = NULL; MPI_Comm *comm_parent = NULL; /* create the node in the linked list of groups and */ /* get the child's MPI_Group and MPI_Comm, to be populated shortly */ comex_create_group_and_igroup(id_child, &igroup_child); group_child = &(igroup_child->group); comm_child = &(igroup_child->comm); /* get the parent's MPI_Group and MPI_Comm */ igroup_parent = comex_get_igroup_from_group(id_parent); group_parent = &(igroup_parent->group); comm_parent = &(igroup_parent->comm); status = MPI_Group_incl(*group_parent, n, pid_list, group_child); if (status != MPI_SUCCESS) { comex_error("MPI_Group_incl: Failed ", status); } { MPI_Comm comm, comm1, comm2; int lvl=1, local_ldr_pos; MPI_Group_rank(*group_child, &grp_me); if (grp_me == MPI_UNDEFINED) { *comm_child = MPI_COMM_NULL; /* FIXME: keeping the group around for now */ return COMEX_SUCCESS; } /* SK: sanity check for the following bitwise operations */ assert(grp_me>=0); MPI_Comm_dup(MPI_COMM_SELF, &comm); /* FIXME: can be optimized away */ local_ldr_pos = grp_me; while(n>lvl) { int tag=0; int remote_ldr_pos = local_ldr_pos^lvl; if (remote_ldr_pos < n) { int remote_leader = pid_list[remote_ldr_pos]; MPI_Comm peer_comm = *comm_parent; int high = (local_ldr_posid = COMEX_World_Proc_Group; group_list->next = NULL; /* save MPI world group and communicatior in COMEX_World_Proc_Group */ group_list->comm = l_state.world_comm; MPI_Comm_group(l_state.world_comm, &(group_list->group)); } void comex_group_finalize() { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; /* don't free the world group (the list head) */ current_group_list_item = current_group_list_item->next; while (current_group_list_item != NULL) { previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; comex_igroup_finalize(previous_group_list_item); free(previous_group_list_item); } } ga-5.9.2/comex/src-ofa/groups.h000066400000000000000000000012051500715745200162570ustar00rootroot00000000000000/** * Private header file for COMEX groups backed by MPI_comm. * * The rest of the COMEX_Group functions are defined in the public comex.h. * * @author Jeff Daily */ #ifndef _COMEX_GROUPS_H_ #define _COMEX_GROUPS_H_ #include /* dup of MPI_COMM_WORLD for internal MPI communication */ extern MPI_Comm COMEX_COMM_WORLD; typedef struct group_link { struct group_link *next; comex_group_t id; MPI_Comm comm; MPI_Group group; } comex_igroup_t; extern void comex_group_init(); extern void comex_group_finalize(); extern comex_igroup_t* comex_get_igroup_from_group(comex_group_t group); #endif /* _COMEX_GROUPS_H_ */ ga-5.9.2/comex/src-ofa/mutex.c000066400000000000000000000010371500715745200161000ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include "comex.h" #include "comex_impl.h" int comex_create_mutexes(int num) { assert(0); return COMEX_SUCCESS; } int comex_destroy_mutexes() { assert(0); return COMEX_SUCCESS; } int comex_lock(int mutex, int proc) { assert(0); return COMEX_SUCCESS; } int comex_unlock(int mutex, int proc) { assert(0); return COMEX_SUCCESS; } ga-5.9.2/comex/src-ofa/openib.c000066400000000000000000000457251500715745200162260ustar00rootroot00000000000000/* Author: Abhinav Vishnu */ #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include #include "comex.h" #include "comex_impl.h" #include "openib.h" #include "reg_cache.h" /* Global vars */ int me; int nprocs; double timecalc[20000]; struct RC_Conn conn; struct User_Opts opts; struct HCA hca; struct Local_Buf lbuf; struct Remote_Buf rbuf; struct ibv_recv_wr rr_desc; struct ibv_sge rr_sg_entry; pthread_t test_async_thread; void openib_init_envs() { char *value; if ((value = getenv("COMEX_OPENIB_USE_DREG")) != NULL){ l_state.comex_openib_use_dreg = (atoi(value)); } else { l_state.comex_openib_use_dreg = 0; } } void init_params(void) { opts.msg_size = DEFAULT_MSG_SIZE; opts.iterations = DEFAULT_ITERATIONS; opts.rdma_flag = 0; opts.sendrecv_flag = 0; opts.all_msgs = 1; opts.memperf = 0; opts.align = ALIGNMENT; opts.ib_devname = NULL; opts.num_cqe = DEFAULT_NUM_CQE; opts.send_wr = DEFAULT_NUM_OUST_SEND; opts.recv_wr = DEFAULT_NUM_OUST_RECV; opts.num_sge = DEFAULT_NUM_SGE; opts.latency = 0; opts.bandwidth = 0; opts.bibandwidth = 0; opts.window = DEFAULT_WINDOW_SIZE; opts.num_ports = 1; opts.striping_threshold = STRIPING_THRESHOLD; opts.num_qp_per_port = 1; opts.default_port = DEFAULT_PORT; hca.ib_dev = NULL; hca.context = NULL; hca.pd = NULL; lbuf.buf_original = NULL; lbuf.buf = NULL; lbuf.mr = NULL; conn.qp = (struct ibv_qp **) malloc(nprocs * sizeof(struct ibv_qp *)); conn.lid = (uint16_t *) malloc(nprocs * sizeof(uint16_t)); conn.qp_num = (uint32_t *) malloc(nprocs * sizeof(uint32_t)); rbuf.qp_num = (uint32_t *) malloc(nprocs * sizeof(uint32_t)); rbuf.lid = (uint16_t *) malloc(nprocs * sizeof(uint16_t)); rbuf.rkey = (uint32_t *) malloc(nprocs * sizeof(uint32_t)); rbuf.buf = (char **) malloc(nprocs * sizeof(char *)); assert(conn.qp && conn.lid && rbuf.qp_num && rbuf.lid && rbuf.rkey && rbuf.buf); } double my_wtime() { double t; struct timeval tv; static unsigned long initialized = 0; static unsigned long sec_base; gettimeofday(&tv, NULL); if (!initialized) { sec_base = tv.tv_sec; initialized = 1; } t = (double) (tv.tv_sec - sec_base) + (double) tv.tv_usec * 1.0e-6; return t; } void release_resources(void) { int i; // Destroy the registration cache reg_cache_destroy(nprocs); for(i = 0; i < nprocs ; i++) { if (me != i) { if(ibv_destroy_qp(conn.qp[i])) { printf("Exiting\n"); exit(1); } } } #if 0 if(ibv_destroy_cq(hca.cq)) { fprintf(stderr,"Couldn't destroy cq %s\n", ibv_get_device_name(hca.ib_dev)); } if(ibv_dealloc_pd(hca.pd)) { fprintf(stderr,"Couldn't free pd %s\n", ibv_get_device_name(hca.ib_dev)); } #endif free(conn.qp); free(conn.lid); free(conn.qp_num); free(rbuf.qp_num); free(rbuf.lid); #if 0 free(rbuf.rkey); free(rbuf.buf); #endif } int open_hca(void) { struct ibv_device **dev_list=NULL; int num_hcas; dev_list = ibv_get_device_list(&num_hcas); // Assume that the first device has an ACTIVE port // if it does not we do not handle this situation for now hca.ib_dev = dev_list[0]; hca.context = ibv_open_device(hca.ib_dev); if(!hca.context) { fprintf(stderr,"Couldn't get context %s\n", ibv_get_device_name(hca.ib_dev)); return 1; } hca.pd = ibv_alloc_pd(hca.context); assert(hca.pd != NULL); if(!hca.pd) { fprintf(stderr,"Couldn't get pd %s\n", ibv_get_device_name(hca.ib_dev)); return 1; } return 0; } int create_cq(void) { hca.cq = ibv_create_cq(hca.context, opts.num_cqe, NULL, NULL, 0); assert(hca.cq); return 0; } int exch_addr(void) { int rc; rc = MPI_Alltoall((void *)conn.qp_num, sizeof(uint32_t), MPI_BYTE, (void *)rbuf.qp_num, sizeof(uint32_t), MPI_BYTE, l_state.world_comm); assert(!rc); rc = MPI_Alltoall((void *)conn.lid, sizeof(uint16_t), MPI_BYTE, (void *)rbuf.lid, sizeof(uint16_t), MPI_BYTE, l_state.world_comm); assert(!rc); #ifdef DEBUG for (i = 0; i < nprocs; i++) { if (me == i) continue; fprintf(stdout,"[%d] Remote QP %d, Remote LID %u, Rkey %u, Lkey %u\n" " LBuf %p, RBuf %p\n", me, rbuf.qp_num[i], rbuf.lid[i], rbuf.rkey[i], lbuf.mr->lkey, lbuf.buf, rbuf.buf[i]); fflush(stdout); } #endif return 0; } int create_qp(void) { struct ibv_qp_attr qp_attr; int i; memset(&qp_attr, 0, sizeof qp_attr); struct ibv_qp_init_attr attr; memset(&attr, 0, sizeof attr); attr.send_cq = hca.cq; attr.recv_cq = hca.cq; attr.cap.max_send_wr = opts.send_wr; attr.cap.max_recv_wr = opts.recv_wr; attr.cap.max_send_sge = opts.num_sge; attr.cap.max_recv_sge = opts.num_sge; attr.cap.max_inline_data = 1; attr.qp_type = IBV_QPT_RC; // Create a connection to yourself for(i = 0; i < nprocs; i++) { conn.qp[i] = ibv_create_qp(hca.pd, &attr); if(!conn.qp[i]) { fprintf(stderr,"Couldn't create QP\n"); return 1; } conn.qp_num[i] = conn.qp[i]->qp_num; qp_attr.qp_state = IBV_QPS_INIT; qp_attr.pkey_index = 0; qp_attr.port_num = 1; qp_attr.qp_access_flags = IBV_ACCESS_LOCAL_WRITE| IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ | IBV_ACCESS_REMOTE_ATOMIC; if(ibv_modify_qp(conn.qp[i], &qp_attr, IBV_QP_STATE | IBV_QP_PKEY_INDEX | IBV_QP_PORT | IBV_QP_ACCESS_FLAGS)) { fprintf(stderr,"Could not modify QP to INIT\n"); return 1; } #ifdef DEBUG fprintf(stdout,"[%d] Created QP %d, LID %d\n", me, conn.qp_num[i], conn.lid[i]); fflush(stdout); #endif } return 0; } int get_lid(void) { struct ibv_port_attr port_attr[MAX_PORTS]; int i, j; int active_port_found = 0; for (j = 1; j <= opts.num_ports; j++) { if (!ibv_query_port(hca.context, j, &port_attr[j - 1]) && (port_attr[j - 1].state == IBV_PORT_ACTIVE)) { active_port_found = 1; for (i = 0; i < nprocs; i++) conn.lid[i] = port_attr[j - 1].lid; return 0; } } assert(active_port_found); return 1; } int connect_qp(void) { struct ibv_qp_attr attr; int i; memset(&attr, 0 , sizeof attr); for(i = 0; i < nprocs; i++){ attr.qp_state = IBV_QPS_RTR; attr.path_mtu = IBV_MTU_2048; attr.dest_qp_num = rbuf.qp_num[i]; attr.rq_psn = 0; attr.max_dest_rd_atomic = 10; attr.min_rnr_timer = 20; attr.ah_attr.is_global = 0; attr.ah_attr.dlid = rbuf.lid[i]; attr.ah_attr.sl = 0; attr.ah_attr.src_path_bits = 0; attr.ah_attr.port_num = 1; attr.ah_attr.static_rate = 0; if (ibv_modify_qp(conn.qp[i], &attr, IBV_QP_STATE | IBV_QP_AV | IBV_QP_PATH_MTU | IBV_QP_DEST_QPN | IBV_QP_RQ_PSN | IBV_QP_MAX_DEST_RD_ATOMIC | IBV_QP_MIN_RNR_TIMER)) { fprintf(stderr, "Failed to modify QP to RTR\n"); return 1; } attr.qp_state = IBV_QPS_RTS; attr.timeout = 14; attr.retry_cnt = 7; attr.rnr_retry = 7; attr.sq_psn = 0; attr.max_rd_atomic = 0; if (ibv_modify_qp(conn.qp[i], &attr, IBV_QP_STATE | IBV_QP_TIMEOUT | IBV_QP_RETRY_CNT | IBV_QP_RNR_RETRY | IBV_QP_SQ_PSN | IBV_QP_MAX_QP_RD_ATOMIC)) { fprintf(stderr, "Failed to modify QP to RTS\n"); return 1; } } return 0; } struct ibv_send_wr sr_desc; struct ibv_sge sr_sg_entry; // All data transfers must go through this function void prepare_and_post_send_desc(void *src, void *dst, int dest, int len, int lkey, int rkey, int type, int lock_or_unlock) { sr_desc.send_flags = IBV_SEND_SIGNALED; sr_desc.next = NULL; sr_desc.opcode = type; sr_desc.wr_id = 0; sr_desc.num_sge = 1; if(IBV_WR_RDMA_WRITE == type) { sr_desc.wr.rdma.remote_addr = (uintptr_t) (dst); sr_sg_entry.addr = (uintptr_t) (src); sr_sg_entry.length = len; sr_desc.wr.rdma.rkey = rkey; } if (IBV_WR_RDMA_READ == type) { sr_desc.wr.rdma.remote_addr = (uintptr_t) (src); sr_sg_entry.addr = (uintptr_t) (dst); sr_sg_entry.length = len; sr_desc.wr.rdma.rkey = rkey; } if (IBV_WR_ATOMIC_CMP_AND_SWP == type) { sr_desc.wr.atomic.remote_addr = (uintptr_t) (dst); sr_desc.wr.atomic.rkey = rkey; sr_sg_entry.addr = (uintptr_t) (src); sr_sg_entry.length = sizeof(long); if (lock_or_unlock == OPENIB_LOCK) { sr_desc.wr.atomic.compare_add = 0; sr_desc.wr.atomic.swap = l_state.rank + 1; } else if (lock_or_unlock == OPENIB_UNLOCK){ sr_desc.wr.atomic.compare_add = l_state.rank + 1; sr_desc.wr.atomic.swap = 0; } else { assert(0); } } sr_sg_entry.lkey = lkey; sr_desc.sg_list = &(sr_sg_entry); struct ibv_send_wr *bad_wr; if(ibv_post_send(conn.qp[dest], &sr_desc, &bad_wr)) { fprintf(stderr,"[%d] Error posting send\n", me); fflush(stderr); } // Increment outstanding and check whether we need to make progress increment_outstanding(); } /* Poll CQ till n_sends are complete and n_recvs * are complete */ int poll_cq(int n_sends, int n_recvs) { int ne; struct ibv_wc wc; int send_comp = 0; int recv_comp = 0; // return if poll cq is called with no outstanding messages if (!n_sends) return 0; // Make sure that no recv requests are called assert(!n_recvs); while (1) { do { ne = ibv_poll_cq(hca.cq, 1, &wc); } while (ne < 1); /* Okay, got an entry, check for errors */ if(ne < 0) { fprintf(stderr,"Error Polling CQ\n"); return 1; } if(wc.status != IBV_WC_SUCCESS) { fprintf(stderr, "[%d] Failed status %d\n", me, wc.status); return 1; } /* What type is it? */ /* In this test suite, we are using either send descriptors * or RDMA, hence the following is OK */ if(IBV_WC_RDMA_WRITE == wc.opcode || IBV_WC_RDMA_READ == wc.opcode || IBV_WC_COMP_SWAP == wc.opcode) { /* Send Completion */ if(n_sends) { send_comp++; } } else { fprintf(stderr, "Unknown opcode recv'd\n"); assert(0); } if((n_sends == send_comp) && (n_recvs == recv_comp)) { break; } } return 0; } void openib_waitall() { // Poll the CQ for all outstanding data transfer int rc = poll_cq(l_state.num_outstanding, 0); assert(!rc); l_state.num_outstanding = 0; } void increment_outstanding() { l_state.num_outstanding++; if ((l_state.num_outstanding == DEFAULT_NUM_OUST_SEND)) { openib_waitall(); } } void * openib_register_memory(void *buf, int len) { struct ibv_mr *mr; mr = ibv_reg_mr(hca.pd, buf, len, IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ | IBV_ACCESS_REMOTE_ATOMIC); if (mr) { // Insert in the registration cache reg_cache_insert(l_state.rank, buf, len, mr->lkey, mr->rkey, mr); } return (void *)mr; } int openib_deregister_memory(void *buf) { struct _reg_entry_t *reg = reg_cache_find(l_state.rank, buf, 0); assert(reg); if (ibv_dereg_mr(reg->mr)) { assert(0); } reg_cache_delete(l_state.rank, buf); return 0; } int openib_put_nbi(void *src, void *dst, int bytes, int proc) { void *src_ptr; int local_reg_failure = 0; struct _reg_entry_t *local_reg, *remote_reg; // Search for local key local_reg = reg_cache_find(l_state.rank, src, bytes); // Search for remote key remote_reg = reg_cache_find(proc, dst, bytes); if (!local_reg && l_state.comex_openib_use_dreg) { openib_register_memory(src, bytes); local_reg = reg_cache_find(l_state.rank, src, bytes); } if (local_reg) { src_ptr = src; } else { local_reg_failure = 1; openib_waitall(); src_ptr = l_state.put_buf; assert(bytes <= l_state.put_buf_len); memcpy(src_ptr, src, bytes); local_reg = reg_cache_find(l_state.rank, src_ptr, bytes); assert(local_reg); } // Ensure that the registration entries are valid assert(remote_reg); // Prepare a send decriptor and post prepare_and_post_send_desc(src_ptr, dst, proc, bytes, local_reg->lkey, remote_reg->rkey, IBV_WR_RDMA_WRITE, -1); if (local_reg_failure) { openib_waitall(); } return 0; } int openib_get_nbi(void *src, void *dst, int bytes, int proc) { void *dst_ptr; int local_reg_failure = 0; // Due to location consistency semantics, we need to call waitall here //openib_waitall(); // The source is the buffer from which the data is read struct _reg_entry_t *local_reg, *remote_reg; // Search for local key local_reg = reg_cache_find(l_state.rank, dst, bytes); // Search for remote key remote_reg = reg_cache_find(proc, src, bytes); assert(remote_reg); if (!local_reg && l_state.comex_openib_use_dreg) { openib_register_memory(dst, bytes); local_reg = reg_cache_find(l_state.rank, dst, bytes); } // Ensure that the registration entries are valid if (local_reg) { dst_ptr = dst; } else { local_reg_failure = 1; assert(bytes <= l_state.get_buf_len); dst_ptr = l_state.get_buf; local_reg = reg_cache_find(l_state.rank, dst_ptr, bytes); assert(local_reg); openib_waitall(); } assert(remote_reg); // Prepare a send decriptor and post prepare_and_post_send_desc(src, dst_ptr, proc, bytes, local_reg->lkey, remote_reg->rkey, IBV_WR_RDMA_READ, -1); if (local_reg_failure) { openib_waitall(); memcpy(dst, dst_ptr, bytes); } return 0; } void openib_network_lock(int proc) { void *src = l_state.local_lock_buf; void *dst = l_state.atomic_lock_buf[proc]; assert(src && dst); int bytes = sizeof(long); // The source is the buffer from which the data is read struct _reg_entry_t *local_reg, *remote_reg; // Search for local key local_reg = reg_cache_find(l_state.rank, src, sizeof(long)); // Search for remote key remote_reg = reg_cache_find(proc, dst, sizeof(long)); // Ensure that the registration entries are valid assert(local_reg && remote_reg); // Prepare a send decriptor and post // do { prepare_and_post_send_desc(src, dst, proc, bytes, local_reg->lkey, remote_reg->rkey, IBV_WR_ATOMIC_CMP_AND_SWP, OPENIB_LOCK); openib_waitall(); } while (*(long *)(src) != 0); } void openib_network_unlock(int proc) { void *src = l_state.local_lock_buf; void *dst = l_state.atomic_lock_buf[proc]; assert(src && dst); int bytes = sizeof(long); // The source is the buffer from which the data is read struct _reg_entry_t *local_reg, *remote_reg; // Search for local key local_reg = reg_cache_find(l_state.rank, src, sizeof(long)); // Search for remote key remote_reg = reg_cache_find(proc, dst, sizeof(long)); // Ensure that the registration entries are valid assert(local_reg); assert(remote_reg); // Prepare a send decriptor and post do { prepare_and_post_send_desc(src, dst, proc, bytes, local_reg->lkey, remote_reg->rkey, IBV_WR_ATOMIC_CMP_AND_SWP, OPENIB_UNLOCK); openib_waitall(); } while (*(long *)(src) != l_state.rank + 1); } void openib_create_locks() { // Create the locks and initialize them l_state.local_lock_buf = comex_malloc_local(sizeof(long)); assert(l_state.local_lock_buf); l_state.atomic_lock_buf = (void **)malloc(l_state.size * sizeof(void *)); assert(l_state.atomic_lock_buf); comex_malloc((l_state.atomic_lock_buf), sizeof(long), COMEX_GROUP_WORLD); *(long *)(l_state.atomic_lock_buf[l_state.rank]) = 0; *(long *)(l_state.local_lock_buf) = 0; MPI_Barrier(l_state.world_comm); } void openib_alloc_buf() { l_state.acc_buf_len = 1048576; l_state.acc_buf = (void *)malloc(sizeof(char) * l_state.acc_buf_len); l_state.put_buf_len = 1048576; l_state.put_buf = (void *)malloc(sizeof(char) * l_state.put_buf_len); l_state.get_buf_len = 1048576; l_state.get_buf = (void *)malloc(sizeof(char) * l_state.get_buf_len); assert(l_state.acc_buf && l_state.put_buf && l_state.get_buf); void *info; info = openib_register_memory(l_state.acc_buf, l_state.acc_buf_len); assert(info); info = openib_register_memory(l_state.put_buf, l_state.put_buf_len); assert(info); info = openib_register_memory(l_state.get_buf, l_state.get_buf_len); assert(info); } int openib_waitproc(int proc) { openib_waitall(); return 0; } int openib_finalize() { release_resources(); return 0; } int openib_initialize() { // Use the previously cached info me = l_state.rank; nprocs = l_state.size; assert(l_state.world_comm); // initialize the envs openib_init_envs(); //Initialize the registration cache reg_cache_init(nprocs, 0); init_params(); if(open_hca()) { release_resources(); exit(1); } if(create_cq()) { release_resources(); exit(1); } if(get_lid()) { release_resources(); exit(1); } if(create_qp()) { release_resources(); exit(1); } if(exch_addr()) { release_resources(); exit(1); } if(connect_qp()) { release_resources(); exit(1); } // Create network locks openib_create_locks(); // Allocate buffers for one sided operations openib_alloc_buf(); MPI_Barrier(l_state.world_comm); return 0; } ga-5.9.2/comex/src-ofa/openib.h000066400000000000000000000073421500715745200162240ustar00rootroot00000000000000#ifndef OPENIB_H_ #define OPENIB_H_ #include /* for uint16_t, uint32_t */ #define OPENIB_LOCK 0 #define OPENIB_UNLOCK 1 /* Default values */ #define MEMCPY_ITERS (10000) #define MAX_PORT_NUM (2) #define DEFAULT_PORT (1) #define MEMCPY_SIZE (16 * 1024 * 1024) #define MAX_PORTS (1) #define MAX_QP_PER_PORT (1) #define MAX_SUBCHANNELS (MAX_PORTS * MAX_QP_PER_PORT) #define DEFAULT_MSG_SIZE (8 * 1048576) #define DEFAULT_BW_ITERS (200) #define DEFAULT_ITERATIONS (10000) #define DEFAULT_NUM_CQE (40000) #define DEFAULT_NUM_OUST_RECV (0) #define DEFAULT_NUM_OUST_SEND (128) #define DEFAULT_NUM_SGE (1) #define DEFAULT_WINDOW_SIZE (32) #define ALIGNMENT (64) #define STRIPING_THRESHOLD (16 * 1024) #define MHZ 238 #define SKIP 10 #define rdtsc(x) asm volatile("rdtsc" : "=A" (x)) /* Read and Write Barriers for PPC, required */ #ifdef _PPC64_ # define STBAR() asm volatile ("sync": : :"memory") /* ": : :" for C++ */ # define READBAR() asm volatile ("sync": : :"memory") # define WRITEBAR() asm volatile ("eieio": : :"memory") #else # define STBAR() # define READBAR() # define WRITEBAR() #endif struct User_Opts { int msg_size; int iterations; int rdma_flag; int sendrecv_flag; char *ib_devname; int all_msgs; int align; int num_cqe; int send_wr; int recv_wr; int num_sge; int latency; int bandwidth; int bibandwidth; int window; int num_ports; int num_qp_per_port; int striping_threshold; int subchannels; int regcost; int memperf; int default_port; int use_apm; int apm_test; int test_nft; int use_srq; }; struct HCA { struct ibv_device *ib_dev; struct ibv_context *context; struct ibv_pd *pd; struct ibv_cq *cq; struct ibv_srq *srq_hndl; struct ibv_comp_channel *scomp_ch; struct ibv_comp_channel *rcomp_ch; void *cntx; }; struct Local_Buf { char *buf_original; char *buf; char *tmp; struct ibv_mr *mr; }; struct Remote_Buf { char **buf; uint32_t *qp_num; uint16_t *lid; uint32_t *rkey; }; struct RC_Conn { struct ibv_qp **qp; uint16_t *lid; uint32_t *qp_num; }; //static struct ibv_srq * create_srq(); static inline int min(int a, int b) { return (a < b ? a : b); } static inline int max(int a, int b) { return (a > b ? a : b); } extern void post_recv_desc(int dest); extern void init_params(void); extern double my_wtime(); extern void release_resources(void); extern int open_hca(void); extern int reg_buf(void); extern int create_cq(void); extern int exch_addr(void); extern int create_qp(void); extern int get_lid(void); extern int connect_qp(void); extern void prepare_and_post_send_desc(void *src, void *dst, int dest, int len, int lkey, int rkey, int type, int lock_or_unlock); extern int poll_cq(int n_sends, int n_recvs); extern void openib_waitall(); extern void increment_outstanding(); extern void * openib_register_memory(void *buf, int len); extern int openib_deregister_memory(void *buf); extern int openib_put_nbi(void *src, void *dst, int bytes, int proc); extern int openib_get_nbi(void *src, void *dst, int bytes, int proc); extern void openib_network_lock(int proc); extern void openib_network_unlock(int proc); extern void openib_create_locks(); extern void openib_alloc_buf(); extern int openib_waitproc(int proc); extern int openib_finalize(); extern int openib_initialize(); #endif /* OPENIB_H_ */ ga-5.9.2/comex/src-ofa/reg_cache.c000066400000000000000000000057161500715745200166460ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include "comex.h" #include "comex_impl.h" #include "reg_cache.h" // Registration cache : Defensive Programming // nprocs: number of processes // size: number of entries struct _reg_entry_t **reg_cache; // cache size for each process // int cache_size = -1; int reg_cache_init(int nprocs, int size) { // Allocate the registration cache: reg_cache = (struct _reg_entry_t **)malloc(sizeof(struct _reg_entry_t *) * nprocs); assert(reg_cache); int i; for (i = 0; i < nprocs; ++i) { reg_cache[i] = (struct _reg_entry_t *)malloc(sizeof(struct _reg_entry_t)); assert(reg_cache[i]); reg_cache[i]->next = NULL; } return 0; } int reg_cache_destroy(int nprocs) { int i; // TODO: Deregister all entries for (i = 0; i < nprocs; ++i) { if (reg_cache[i]) free(reg_cache[i]); } assert(reg_cache); free(reg_cache); return 0; } struct _reg_entry_t* reg_cache_find(int rank, void *buf, size_t len) { struct _reg_entry_t *runner = reg_cache[rank]->next; while (runner) { if (runner->buf <= buf && runner->len >= len && ((runner->buf + runner->len) >= (buf + len))) { return(runner); } runner = runner->next; } return NULL; } int reg_cache_insert(int rank, void *buf, size_t len, int lkey, int rkey, struct ibv_mr *mr) { struct _reg_entry_t *node, *runner = (struct _reg_entry_t *)(reg_cache[rank]); node = (struct _reg_entry_t *)malloc(sizeof(struct _reg_entry_t)); assert(node); node->buf = buf; node->len = len; node->lkey = lkey; node->rkey = rkey; node->next = NULL; if (mr) { node->mr = mr; assert(node->mr); assert(node->buf == buf); } assert(NULL == reg_cache_find(rank, buf, 0)); while (runner->next) { runner = runner->next; } runner->next = node; return 0; } void reg_cache_delete(int rank, void *buf) { struct _reg_entry_t *found, *runner = (struct _reg_entry_t *)reg_cache[rank]; while (runner->next) { if (runner->next->buf == buf) { found = runner->next; runner->next = runner->next->next; free(found); return; } runner = runner->next; } assert(0); } #if 0 // Test Driver int main() { reg_cache_init(16, 4); _reg_entry_t *reg_entry; long *a = (long *)malloc(sizeof(long) *8192); reg_cache_insert(0, a, 16, 1, 1); reg_entry = reg_cache_find(1, a , 16); assert(!reg_entry); #if 1 reg_entry = reg_cache_find(0, a , 32); assert(!reg_entry); reg_entry = reg_cache_find(0, a , 16); assert(reg_entry); reg_entry = reg_cache_find(0, a , 32); assert(!reg_entry); #endif reg_cache_destroy(16); } #endif ga-5.9.2/comex/src-ofa/reg_cache.h000066400000000000000000000007451500715745200166500ustar00rootroot00000000000000#ifndef _REG_CACHE_H_ #define _REG_CACHE_H_ #include struct _reg_entry_t { void *buf; size_t len; int lkey; int rkey; struct ibv_mr *mr; struct _reg_entry_t *next; }; struct _reg_entry_t *reg_cache_find(int, void *, size_t); int reg_cache_init(int, int); int reg_cache_destroy(int); int reg_cache_insert(int rank, void *buf, size_t len, int, int, struct ibv_mr *); void reg_cache_delete(int rank, void *buf); #endif /* _REG_CACHE_H_ */ ga-5.9.2/comex/src-ofa/strided.c000066400000000000000000000241071500715745200163770ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* C and/or system headers */ #include #include #include #include /* 3rd party headers */ #include /* our headers */ #include "comex.h" #include "comex_impl.h" /* needed for complex accumulate */ typedef struct { double real; double imag; } DoubleComplex; typedef struct { float real; float imag; } SingleComplex; int comex_nbputs(void *src_ptr, int src_stride_ar[/*stride_levels*/], void *dst_ptr, int dst_stride_ar[/*stride_levels*/], int count[/*stride_levels+1*/], int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } /* index mangling */ for(i=0; i (count[j]-1)) src_bvalue[j] = 0; } for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride_ar[j-1]; if((i+1) % dst_bunit[j] == 0) dst_bvalue[j]++; if(dst_bvalue[j] > (count[j]-1)) dst_bvalue[j] = 0; } COMEXD_put_nbi((char *)src_ptr + src_idx, (char *)dst_ptr + dst_idx, count[0], proc); } comex_wait_proc(proc, group); return COMEX_SUCCESS; } int comex_nbgets(void *src_ptr, int src_stride_ar[/*stride_levels*/], void *dst_ptr, int dst_stride_ar[/*stride_levels*/], int count[/*stride_levels+1*/], int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) n1dim *= count[i]; /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) src_bvalue[j] = 0; } dst_idx = 0; for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride_ar[j-1]; if((i+1) % dst_bunit[j] == 0) dst_bvalue[j]++; if(dst_bvalue[j] > (count[j]-1)) dst_bvalue[j] = 0; } COMEXD_get_nbi((char *)src_ptr + src_idx, (char *)dst_ptr + dst_idx, count[0], proc); } COMEXD_waitproc(proc); return COMEX_SUCCESS; } int comex_nbaccs(int datatype, void *scale, void *src_ptr, int src_stride_ar[/*stride_levels*/], void *dst_ptr, int dst_stride_ar[/*stride_levels*/], int count[/*stride_levels+1*/], int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; int sizetogetput; void *get_buf; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) n1dim *= count[i]; /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } sizetogetput = count[0]; /* TODO: Can we allocate a temporary buffer like we did for the * gemini port? */ if (sizetogetput <= l_state.acc_buf_len) { get_buf = l_state.acc_buf; } else { get_buf = comex_malloc_local(sizetogetput); } assert(get_buf); /* grab the atomics lock */ COMEXD_network_lock(proc); for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } dst_idx = 0; for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride_ar[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } comex_get((char *)dst_ptr + dst_idx, l_state.acc_buf, count[0], proc, group); #define EQ_ONE_REG(A) ((A) == 1.0) #define EQ_ONE_CPL(A) ((A).real == 1.0 && (A).imag == 0.0) #define IADD_REG(A,B) (A) += (B) #define IADD_CPL(A,B) (A).real += (B).real; (A).imag += (B).imag #define IADD_SCALE_REG(A,B,C) (A) += (B) * (C) #define IADD_SCALE_CPL(A,B,C) (A).real += ((B).real*(C).real) - ((B).imag*(C).imag);\ (A).imag += ((B).real*(C).imag) + ((B).imag*(C).real); #define ACC(WHICH, COMEX_TYPE, C_TYPE) \ if (datatype == COMEX_TYPE) { \ int m; \ int m_lim = count[0]/sizeof(C_TYPE); \ C_TYPE *iterator = (C_TYPE *)get_buf; \ C_TYPE *value = (C_TYPE *)((char *)src_ptr + src_idx); \ C_TYPE calc_scale = *(C_TYPE *)scale; \ if (EQ_ONE_##WHICH(calc_scale)) { \ for (m = 0 ; m < m_lim; ++m) { \ IADD_##WHICH(iterator[m], value[m]); \ } \ } \ else { \ for (m = 0 ; m < m_lim; ++m) { \ IADD_SCALE_##WHICH(iterator[m], value[m], calc_scale); \ } \ } \ } else ACC(REG, COMEX_ACC_DBL, double) ACC(REG, COMEX_ACC_FLT, float) ACC(REG, COMEX_ACC_INT, int) ACC(REG, COMEX_ACC_LNG, long) ACC(CPL, COMEX_ACC_DCP, DoubleComplex) ACC(CPL, COMEX_ACC_CPL, SingleComplex) { assert(0); } #undef ACC #undef EQ_ONE_REG #undef EQ_ONE_CPL #undef IADD_REG #undef IADD_CPL #undef IADD_SCALE_REG #undef IADD_SCALE_CPL comex_put((char *)l_state.acc_buf, (char *)dst_ptr + dst_idx, count[0], proc, group); } COMEXD_waitproc(proc); COMEXD_network_unlock(proc); if (sizetogetput > l_state.acc_buf_len) { comex_free_local(get_buf); } return COMEX_SUCCESS; } int comex_puts(void *src_ptr, int src_stride_ar[/*stride_levels*/], void *dst_ptr, int dst_stride_ar[/*stride_levels*/], int count[/*stride_levels+1*/], int stride_levels, int proc, comex_group_t group) { comex_nbputs(src_ptr, src_stride_ar, dst_ptr, dst_stride_ar, count,stride_levels,proc, group, NULL); COMEXD_waitproc(proc); return COMEX_SUCCESS; } int comex_gets(void *src_ptr, int src_stride_ar[/*stride_levels*/], void *dst_ptr, int dst_stride_ar[/*stride_levels*/], int count[/*stride_levels+1*/], int stride_levels, int proc, comex_group_t group) { comex_nbgets(src_ptr, src_stride_ar, dst_ptr, dst_stride_ar, count,stride_levels, proc, group, NULL); COMEXD_waitproc(proc); return COMEX_SUCCESS; } int comex_accs(int datatype, void *scale, void *src_ptr, int src_stride_ar[/*stride_levels*/], void *dst_ptr, int dst_stride_ar[/*stride_levels*/], int count[/*stride_levels+1*/], int stride_levels, int proc, comex_group_t group) { comex_nbaccs(datatype, scale, src_ptr, src_stride_ar, dst_ptr, dst_stride_ar, count, stride_levels, proc, group, NULL); COMEXD_waitproc(proc); return COMEX_SUCCESS; } ga-5.9.2/comex/src-ofa/vector.c000066400000000000000000000037551500715745200162510ustar00rootroot00000000000000#include #include #include #include #include #include "comex.h" #include "comex_impl.h" int comex_putv(comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { int i; for (i=0; i #include #include #include #include #include "comex.h" #include "comex_impl.h" int comex_wait_proc(int proc, comex_group_t group) { return COMEXD_waitproc(proc); } int comex_wait(comex_request_t * hdl) { return COMEXD_waitall(); } int comex_test(comex_request_t * hdl, int *status) { *status = 0; return COMEXD_waitall(); } int comex_wait_all(comex_group_t group) { return COMEXD_waitall(); } ga-5.9.2/comex/src-ofi/000077500000000000000000000000001500715745200146015ustar00rootroot00000000000000ga-5.9.2/comex/src-ofi/Makefile.inc000066400000000000000000000010021500715745200170020ustar00rootroot00000000000000libcomex_la_SOURCES += src-ofi/comex.c libcomex_la_SOURCES += src-ofi/comex_impl.h libcomex_la_SOURCES += src-ofi/datatype.h libcomex_la_SOURCES += src-ofi/env.h libcomex_la_SOURCES += src-ofi/env.c libcomex_la_SOURCES += src-ofi/groups.c libcomex_la_SOURCES += src-ofi/groups.h libcomex_la_SOURCES += src-ofi/log.h libcomex_la_SOURCES += src-ofi/mutex.h libcomex_la_SOURCES += src-ofi/ofi.h libcomex_la_SOURCES += src-ofi/request.h libcomex_la_SOURCES += src-ofi/fi_lock.h AM_CPPFLAGS += -I$(top_srcdir)/src-ofi ga-5.9.2/comex/src-ofi/README000066400000000000000000000070731500715745200154700ustar00rootroot00000000000000COMEX/OFI provider ------------------ COMEX/OFI is provider implemented to enable Omni-Path, InfiniBand and other interconnects. It works on top of OFI library (called also libfabric): https://github.com/ofiwg/libfabric COMEX/OFI provider supports set of environment variables to configure internal engines: COMEX_OFI_LOG_LEVEL ----------------- COMEX_OFI_LOG_LEVEL= Possible values: 0 - error 1 - warning 2 - info 3 - debug 4 - trace Default value: 0 Example: export COMEX_OFI_LOG_LEVEL=2 Description: set this environment variable to print logging information about the application. Higher levels include information logged in the lower levels. COMEX_OFI_NATIVE_ATOMICS ----------------- COMEX_OFI_NATIVE_ATOMICS= Possible values: 0 - disable native atomics 1 - enable native atomics Default value: 1 Example: export COMEX_OFI_NATIVE_ATOMICS=1 Description: set this environment variable to control atomics path: native or emulated. The native path is implemented over OFI Atomics API. The emulated path is implemented by 2 ways, controlled over COMEX_OFI_ATOMICS_EMULATION_TYPE. COMEX_OFI_ATOMICS_EMULATION_TYPE ----------------- COMEX_OFI_ATOMICS_EMULATION_TYPE= Possible values: 0 - operations are performed on origin side 1 - operations are performed on target side Default value: 1 Example: export COMEX_OFI_ATOMICS_EMULATION_TYPE=1 Description: some OFI providers have no built-in support of atomic operations (like remote add/sub/exchange). Such operations may be implemented using other OFI APIs. Set this environment variable to control emulated atomics approach: on origin side or on target side. The emulated atomics on origin side is implemented over OFI RMA API for accumulation and OFI Atomics API for rmw. The emulated atomics on target side is implemented over OFI Tagged API for accumulation and rmw, requires enabling of progress thread. COMEX_OFI_PROGRESS_THREAD ----------------- COMEX_OFI_PROGRESS_THREAD= Possible values: 0 - disable progress thread 1 - enable progress thread Default value: 1 Example: export COMEX_OFI_PROGRESS_THREAD=1 Description: set this environment variable to control creation of progress thread. COMEX_OFI_CQ_ENTRIES_COUNT ----------------- COMEX_OFI_CQ_ENTRIES_COUNT= Possible values: 1 <= value <= 1024 Default value: 8 Example: export COMEX_OFI_CQ_ENTRIES_COUNT=16 Description: set this environment variable specify the maximum number of entries what can be retrieved from OFI completion queue by one fi_cq_read call. COMEX_OFI_PROVIDER ------------------ COMEX_OFI_PROVIDER= Example: export COMEX_OFI_PROVIDER=psm2 Description: set this environment variable to specify OFI provider name. By default OFI selects provider using information about available hardware. But in some cases user may force selection of specific provider setting variable COMEX_OFI_PROVIDER. List of available providers may be obtained using application "fi_info" which is part of libfabric package. COMEX_OFI_LIBRARY ----------------- COMEX_OFI_LIBRARY= Example: export COMEX_OFI_LIBRARY=/usr/local/lib/libfabric.so Description: set this environment variable to specify path to libfabric.so. Useful when libfabric.so is installed to non-default location. ga-5.9.2/comex/src-ofi/comex.c000066400000000000000000004154171500715745200160740ustar00rootroot00000000000000/* * * Copyright (c) 2016 Intel Corporation. All rights reserved. */ #if HAVE_CONFIG_H # include "config.h" #endif /* C and/or system headers */ #include #include #include #include #include #include #include #include #include #include #include /* 3rd party headers */ #include #include "rdma/fi_domain.h" #include "rdma/fi_endpoint.h" #include "rdma/fi_cm.h" #include "rdma/fi_rma.h" #include "rdma/fi_atomic.h" #include "rdma/fi_errno.h" #include "rdma/fi_tagged.h" /* our headers */ #include "comex.h" #include "comex_impl.h" #include "datatype.h" #include "env.h" #include "groups.h" #include "log.h" #include "mutex.h" #include "ofi.h" /*#define ATOMIC_NATIVE_ONLY*/ #ifndef ATOMIC_NATIVE_ONLY # define USE_ATOMIC_EMULATION #endif /* ATOMIC_NO_EMULATION */ #define WAIT_COMPLETION_AND_RESET(_request) \ do { \ while ((_request)->state != rs_complete) \ { \ myofi_poll(0); \ PAUSE(); \ } \ reset_request((_request)); \ } while (0) #define IS_TARGET_ATOMICS_EMULATION() ((env_data.native_atomics == 0 && env_data.emulation_type == et_target) ? 1 : 0) #ifndef GA_OFI_STATIC_LINK fi_loadable_methods_t ld_table = {0}; #endif /* GA_OFI_STATIC_LINK */ /* exported state */ local_state l_state; ofi_data_t ofi_data; struct request_cache_t* request_cache = 0; /* static state */ static int initialized = 0; /* for comex_initialized(), 0=false */ static char skip_lock = 0; /* don't acquire or release lock */ /* static function declarations */ static inline int wait_request(request_t* request); static void acquire_remote_lock(int proc); static void release_remote_lock(int proc); static int finalize_ofi(); static int create_mutexes(mutex_t** mtx, int num); static int destroy_mutexes(mutex_t* mutex); static inline int destroy_all_windows(); struct fi_cq_attr cq_attr = { 0 }; struct fi_av_attr av_attr = { 0 }; local_window_t* local_wnd = 0; ofi_window_t* ofi_wnd = 0; ofi_window_t* ofi_wnd_cache = 0; /* last accessed window */ mutex_t* global_mutex = 0; /* public mutex */ mutex_t* local_mutex = 0; /* local mutex */ static int* am_mutex_locked = 0; static int* am_mutex_waiter = 0; static uint32_t reply_tag = 0; #define GETTAG() (++reply_tag) static fastlock_t mutex_lock; static fastlock_t acc_lock; static fastlock_t poll_lock; static int myofi_poll(int* items_processed); static pthread_t tid = 0; static int comex_acc_native( int datatype, void* scale, void* src_ptr, void* dst_ptr, int bytes, int proc, comex_group_t group); static int comex_accs_native( int datatype, void* scale, void* src_ptr, int* src_stride_ar, void* dst_ptr, int* dst_stride_ar, int* count, int stride_levels, int proc, comex_group_t group); static int comex_accv_native( int datatype, void* scale, comex_giov_t* iov, int iov_len, int proc, comex_group_t group); static int comex_nbacc_native( int datatype, void* scale, void* src_ptr, void* dst_ptr, int bytes, int proc, comex_group_t group, comex_request_t* handle); static int comex_nbaccs_native( int datatype, void* scale, void* src, int* src_stride, void* dst, int* dst_stride, int* count, int stride_levels, int proc, comex_group_t group, comex_request_t* handle); static int comex_nbaccv_native( int datatype, void* scale, comex_giov_t* iov, int iov_len, int proc, comex_group_t group, comex_request_t* handle); #ifdef USE_ATOMIC_EMULATION static int comex_acc_emu( int datatype, void* scale, void* src_ptr, void* dst_ptr, int bytes, int proc, comex_group_t group); static int comex_accs_emu( int datatype, void* scale, void* src_ptr, int* src_stride_ar, void* dst_ptr, int* dst_stride_ar, int* count, int stride_levels, int proc, comex_group_t group); static int comex_accv_emu( int datatype, void* scale, comex_giov_t* iov, int iov_len, int proc, comex_group_t group); static int comex_nbacc_emu( int datatype, void* scale, void* src_ptr, void* dst_ptr, int bytes, int proc, comex_group_t group, comex_request_t* handle); static int comex_nbaccs_emu( int datatype, void* scale, void* src, int* src_stride, void* dst, int* dst_stride, int* count, int stride_levels, int proc, comex_group_t group, comex_request_t *handle); static int comex_nbaccv_emu( int datatype, void* scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* handle); typedef int (comex_acc_t)( int datatype, void* scale, void* src_ptr, void* dst_ptr, int bytes, int proc, comex_group_t group); typedef int (comex_accs_t)( int datatype, void* scale, void* src_ptr, int* src_stride_ar, void* dst_ptr, int* dst_stride_ar, int* count, int stride_levels, int proc, comex_group_t group); typedef int (comex_accv_t)( int datatype, void* scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group); typedef int (comex_nbacc_t)( int datatype, void* scale, void* src_ptr, void* dst_ptr, int bytes, int proc, comex_group_t group, comex_request_t *handle); typedef int (comex_nbaccs_t)( int datatype, void* scale, void* src, int* src_stride, void* dst, int* dst_stride, int* count, int stride_levels, int proc, comex_group_t group, comex_request_t *handle); typedef int (comex_nbaccv_t)( int datatype, void* scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* handle); static comex_acc_t* comex_acc_f = comex_acc_emu; static comex_accs_t* comex_accs_f = comex_accs_emu; static comex_accv_t* comex_accv_f = comex_accv_emu; static comex_nbacc_t* comex_nbacc_f = comex_nbacc_emu; static comex_nbaccs_t* comex_nbaccs_f = comex_nbaccs_emu; static comex_nbaccv_t* comex_nbaccv_f = comex_nbaccv_emu; #else /* USE_ATOMIC_EMULATION */ #define comex_acc_f comex_acc_native #define comex_accs_f comex_accs_native #define comex_accv_f comex_accv_native #define comex_nbacc_f comex_nbacc_native #define comex_nbaccs_f comex_nbaccs_native #define comex_nbaccv_f comex_nbaccv_native #endif /* USE_ATOMIC_EMULATION */ #define mr_regv(iov, count, mrs, op_type) \ do \ { \ struct fi_context ctx; \ int i; \ for (i = 0; i < count; i++) \ { \ COMEX_CHKANDJUMP(mr_reg(iov[i].iov_base, \ iov[i].iov_len, \ MR_ACCESS_PERMISSIONS, \ &(mrs)[i], \ &ctx, \ op_type), \ "fi_mr_reg error:"); \ } \ } while(0) static inline ofi_ep_t* get_ep(op_type_t op_type) { assert(op_type == ot_rma || op_type == ot_atomic); return (op_type == ot_rma) ? &(ofi_data.ep_rma) : &(ofi_data.ep_atomics); } static inline int mr_reg(const void* buf, size_t len, uint64_t access, struct fid_mr** mr, void* context, op_type_t op_type) { ofi_ep_t* ep = get_ep(op_type); uint64_t key = 0; if (FI_MR_SCALABLE == ep->mr_mode) { /** * the key should be specified explicitly in case of scalable mr * use simple counter to get unique value */ key = __sync_fetch_and_add(&ofi_data.mr_counter, 1); } OFI_RETRY(fi_mr_reg(ep->domain, /* In: domain object */ buf, /* In: lower memory address */ len, /* In: length */ access, /* In: access rights */ 0ULL, /* In: offset (not used) */ key, /* In: requested key */ 0ULL, /* In: flags */ mr, /* Out: memregion object */ context), /* In: context */ "fi_mr_reg error:"); return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static inline uint64_t get_remote_addr(uint64_t base_addr, void* ptr, op_type_t op_type) { assert(base_addr <= (uint64_t)ptr); ofi_ep_t* ep = get_ep(op_type); if (FI_MR_SCALABLE == ep->mr_mode) { /* returns offset inside of memory region */ return ((uint64_t)ptr - base_addr); } else return (uint64_t)ptr; } static inline int mr_unreg(struct fid* mr) { OFI_CHKANDJUMP(fi_close(mr), "failed to unregister memory"); return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static inline void req_dtor(request_t* request) { assert(request); assert(request->state == rs_complete); if (request->mr_count && request->mrs) { int i; for (i = 0; i < request->mr_count; i++) if (request->mrs[i]) COMEX_CHKANDJUMP(mr_unreg(&request->mrs[i]->fid), "failed to unregister memory"); free(request->mrs); } if (request->mr_single) COMEX_CHKANDJUMP(mr_unreg(&request->mr_single->fid), "failed to unregister memory"); if (request->data && !(request->flags & rf_no_free_data)) free(request->data); fn_fail: return; } static inline int dual_provider() { return ofi_data.ep_rma.provider != ofi_data.ep_atomics.provider; } void complete_acc(request_t* request) { assert(request); if (request->data) { acc_data_t* data = (acc_data_t*)request->data; if (data->middle) free(data->middle); struct fi_msg_atomic* msg = (struct fi_msg_atomic*)data->msg; if (msg) { if (msg->msg_iov) free((void*)msg->msg_iov); if (msg->rma_iov) free((void*)msg->rma_iov); free(msg); } /* unlock remote host * TODO: is it required? per-element atomic is provided by OFI */ /*if (data->proc != PROC_NONE) release_remote_lock(data->proc);*/ free(data); request->data = 0; } } void complete_getput(request_t* request) { assert(request); if (request->data) { struct fi_msg_rma* msg = (struct fi_msg_rma*)request->data; assert(msg->context); if (msg && msg->context) { if (msg->msg_iov) free((void*)msg->msg_iov); if (msg->rma_iov) free((void*)msg->rma_iov); } free(request->data); request->data = 0; } } static int exchange_with_all(void* chunk, int len, comex_group_t group, void** result) { void* buf = 0; EXPR_CHKANDJUMP((chunk && len && result), "incorrect arguments"); comex_igroup_t* igroup = comex_get_igroup_from_group(group); EXPR_CHKANDJUMP(igroup, "failed to lookup group"); int group_size = 0; COMEX_CHKANDJUMP(comex_group_size(group, &group_size), "failed to get group size"); buf = malloc(len * group_size); EXPR_CHKANDJUMP(buf, "failed to allocate buffer"); MPI_CHKANDJUMP(MPI_Allgather(chunk, len, MPI_BYTE, buf, len, MPI_BYTE, igroup->comm), "failed to perform MPI_Allgather"); *result = buf; fn_success: return COMEX_SUCCESS; fn_fail: if (buf) free(buf); return COMEX_FAILURE; } static int connect_all(ofi_ep_t* ep) { int ret = COMEX_SUCCESS; size_t name_len = OFI_EP_NAME_MAX_LENGTH; char name[OFI_EP_NAME_MAX_LENGTH]; OFI_CALL(ret, fi_getname(&ep->endpoint->fid, &name, &name_len)); OFI_CHKANDJUMP(ret, "fi_getname:"); /* exchange OFI end-points adresses */ /* check if all addresses have same length */ typedef struct name_length_t { int proc; int length; } name_length_t; typedef struct proc_name_t { int proc; char name[]; } proc_name_t; int group_size = l_state.size; int i; name_length_t* lengths = 0; char* names = 0; proc_name_t* my_name = 0; name_length_t my_name_len = {l_state.proc, name_len}; COMEX_CHKANDJUMP(exchange_with_all(&my_name_len, sizeof(my_name_len), COMEX_GROUP_WORLD, (void**)&lengths), "failed to exchange name_len"); for (i = 0; i < group_size; i++) { EXPR_CHKANDJUMP(lengths[i].length == my_name_len.length, "proc %d has incorrect name length: %d (expected %d)", lengths[i].proc, lengths[i].length, my_name_len.length); } /* ok, all name lengths are equal. let's publish it */ int sizeof_procname = sizeof(proc_name_t) + my_name_len.length; my_name = malloc(sizeof_procname); EXPR_CHKANDJUMP(my_name, "failed to allocate data"); my_name->proc = my_name_len.proc; memcpy(my_name->name, name, my_name_len.length); COMEX_CHKANDJUMP(exchange_with_all(my_name, sizeof_procname, COMEX_GROUP_WORLD, (void**)&names), "failed to exchange proc name"); ep->peers = malloc(sizeof(*(ep->peers)) * group_size); EXPR_CHKANDJUMP(ep->peers, "failed to allocate peer's data"); memset(ep->peers, 0, sizeof(*(ep->peers)) * group_size); /* fill ofi_data.peers array: use proc as index in array */ for (i = 0; i < group_size; i++) { proc_name_t* proc_name = (proc_name_t*)(names + (i * sizeof_procname)); assert(proc_name->proc < group_size); peer_t* peer = ep->peers + proc_name->proc; peer->proc = proc_name->proc; struct fi_context av_context; int ret; OFI_CALL(ret, fi_av_insert(ep->av, proc_name->name, 1, &peer->fi_addr, 0, &av_context)); OFI_CHKANDJUMP(ret, "failed to fi_av_insert:"); } fn_success: if (lengths) free(lengths); if (names) free(names); return COMEX_SUCCESS; fn_fail: if (my_name) free(my_name); if (lengths) free(lengths); if (names) free(names); return COMEX_FAILURE; } void tune_ofi_provider() { #define VAR_NAME_LEN 32 #define PSM_COUNT 2 #define PREFIX_COUNT 2 char var_name[VAR_NAME_LEN] = {0}; const char * psms[PSM_COUNT] = {"PSM", "PSM2"}; const char * prefixes[PREFIX_COUNT] = {"OFI", "FI"}; int prefix_idx; for (prefix_idx = 0; prefix_idx < PREFIX_COUNT; prefix_idx++) { int psm_idx = 0; for (; psm_idx < PSM_COUNT; psm_idx++) { snprintf(var_name, VAR_NAME_LEN, "%s_%s_NAME_SERVER", prefixes[prefix_idx], psms[psm_idx]); setenv(var_name, "0", 1); snprintf(var_name, VAR_NAME_LEN, "%s_%s_TAGGED_RMA", prefixes[prefix_idx], psms[psm_idx]); setenv(var_name, "0", 0); } } char var[256]; sprintf(var, "%d", l_state.local_size); setenv("MPI_LOCALNRANKS", var, 0); sprintf(var, "%d", l_state.local_proc); setenv("MPI_LOCALRANKID", var, 0); setenv("IPATH_NO_CPUAFFINITY", "1", 0); setenv("HFI_NO_CPUAFFINITY", "1", 0); } static int init_ep(struct fi_info* hints, ofi_ep_t* ep, int suppress_fail) { memset(ep, 0, sizeof(*ep)); /* clean ep data */ struct fi_info* provider = 0; int ret; OFI_CALL(ret, CALL_TABLE_FUNCTION(&ld_table, fi_getinfo(OFI_VERSION, NULL, NULL, 0ULL, hints, &provider))); if (ret < 0 && suppress_fail) goto fn_fail; OFI_CHKANDJUMP(ret, "fi_getinfo:"); EXPR_CHKANDJUMP(provider, "no provider found with desired capabilities"); COMEX_OFI_LOG(INFO, "using provider '%s.%d'", provider->fabric_attr->prov_name, provider->fabric_attr->prov_version); ep->mr_mode = provider->domain_attr->mr_mode; COMEX_OFI_LOG(INFO, "prov_name: %s, mr_mode %d", provider->fabric_attr->prov_name, ep->mr_mode); EXPR_CHKANDJUMP(((ep->mr_mode == FI_MR_BASIC) || (ep->mr_mode == FI_MR_SCALABLE)), "unsupported mr mode"); /* ---------------------------- */ /* Open fabric */ /* ---------------------------- */ OFI_CALL(ret, CALL_TABLE_FUNCTION(&ld_table, fi_fabric(provider->fabric_attr, &ep->fabric, NULL))); if (ret < 0) { if (!suppress_fail) OFI_CHKANDJUMP(ret, "fi_fabric('%s')", provider->fabric_attr->prov_name); else goto fn_fail; } /* ---------------------------- */ /* Open domain */ /* ---------------------------- */ OFI_CALL(ret, fi_domain(ep->fabric, provider, &ep->domain, NULL)); if (ret < 0) { if(!suppress_fail) OFI_CHKANDJUMP(ret, "fi_domain"); else goto fn_fail_dom; } /* ----------------------------- */ /* Open endpoint */ /* ----------------------------- */ OFI_CALL(ret, fi_endpoint(ep->domain, provider, &ep->endpoint, NULL)); if (ret < 0) { if (!suppress_fail) OFI_CHKANDJUMP(ret, "fi_endpoint"); else goto fn_fail_ep; } /* -------------------------------- */ /* Open Completion Queue */ /* -------------------------------- */ memset(&cq_attr, 0, sizeof(cq_attr)); cq_attr.format = FI_CQ_FORMAT_TAGGED; OFI_CHKANDJUMP(fi_cq_open(ep->domain, &cq_attr, &ep->cq, NULL), "fi_cq_open:"); /* -------------------------------- */ /* Open Address Vector */ /* -------------------------------- */ memset(&av_attr, 0, sizeof(av_attr)); av_attr.type = FI_AV_MAP; OFI_CHKANDJUMP(fi_av_open(ep->domain, &av_attr, &ep->av, NULL), "fi_av_open:"); /* -------------------------------- */ /* Bind Endpoint to both the CQ */ /* and to the AV */ /* -------------------------------- */ OFI_CHKANDJUMP(fi_ep_bind(ep->endpoint, (fid_t)ep->cq, EP_COMPLETIONS_TO_REPORT), "fi_bind EP-CQ:"); OFI_CHKANDJUMP(fi_ep_bind(ep->endpoint, (fid_t)ep->av, 0), "fi_bind EP-AV:"); /* -------------------------------- */ /* Enable EP */ /* -------------------------------- */ OFI_CHKANDJUMP(fi_enable(ep->endpoint), "fi_enable:"); COMEX_CHKANDJUMP(connect_all(ep), "connect_all error"); ep->provider = provider; fn_success: return COMEX_SUCCESS; fn_fail_ep: fi_close(&ep->domain->fid); fn_fail_dom: fi_close(&ep->fabric->fid); fn_fail: if (provider) OFI_VCALL(CALL_TABLE_FUNCTION(&ld_table, fi_freeinfo(provider))); return COMEX_FAILURE; } #define PREPOST_ATOMICS_COUNT 64 static ofi_atomics_t atomics_headers[PREPOST_ATOMICS_COUNT]; static request_t atomics_requests[PREPOST_ATOMICS_COUNT]; static int completed_atomics_count = 0; static volatile int progress_thread_complete = 0; static pthread_t progress_thread = 0; static void* progress_thread_func(void* __data) { while (!progress_thread_complete) { myofi_poll(0); PAUSE(); } return 0; } #define PREPOST_ATOMICS() \ do \ { \ completed_atomics_count = 0; \ int req_idx; \ for (req_idx = 0; req_idx < PREPOST_ATOMICS_COUNT; req_idx++) \ { \ request_t* req = &atomics_requests[req_idx]; \ ofi_atomics_t* header = &atomics_headers[req_idx]; \ init_request(req); \ req->data = header; \ req->cmpl = target_emulated_atomics_completion; \ OFI_RETRY(fi_trecv(ofi_data.ep_tagged.endpoint, \ header, \ sizeof(*header), \ 0, \ FI_ADDR_UNSPEC, \ ATOMICS_PROTO_TAGMASK, \ ATOMICS_PROTO_IGNOREMASK, \ req), \ "fi_trecv: failed to prepost request"); \ } \ } while(0) #define ADD(_dst, _src, _len, type) \ do \ { \ int i; \ type* dst = (type*)_dst; \ type* src = (type*)_src; \ int cnt = (_len) / sizeof(type); \ fastlock_acquire(&acc_lock); \ for (i = 0; i < cnt; i++, dst++, src++) \ *dst += *src; \ fastlock_release(&acc_lock); \ } while(0) static void chunk_acc_completion(request_t* request) { ofi_atomics_t* header = request->data; assert(header); EXPR_CHKANDJUMP(header, "incorrect header"); switch (header->proto.op) { case COMEX_ACC_DBL: ADD(header->acc.addr, header->acc.data, header->acc.posted, double); break; case COMEX_ACC_FLT: ADD(header->acc.addr, header->acc.data, header->acc.posted, float); break; case COMEX_ACC_INT: ADD(header->acc.addr, header->acc.data, header->acc.posted, int); break; case COMEX_ACC_LNG: ADD(header->acc.addr, header->acc.data, header->acc.posted, long); break; case COMEX_ACC_DCP: ADD(header->acc.addr, header->acc.data, header->acc.posted, double complex); break; case COMEX_ACC_CPL: ADD(header->acc.addr, header->acc.data, header->acc.posted, float complex); break; default: assert(0); break; } fn_fail: return; } static void full_acc_completion(request_t* request) { ofi_atomics_t* header = request->data; assert(header); EXPR_CHKANDJUMP(header, "incorrect header"); int v; OFI_RETRY(fi_tinject(ofi_data.ep_tagged.endpoint, &v, sizeof(v), ofi_data.ep_tagged.peers[header->proto.proc].fi_addr, ATOMICS_ACC_CMPL_TAGMASK | header->proto.tag), "fi_tinject: failed"); fn_fail: return; } static void target_emulated_atomics_completion(request_t* request) { int proc = PROC_NONE; assert(request); ofi_atomics_t header = *(ofi_atomics_t*)request->data; request->state = rs_complete; if (__sync_add_and_fetch(&completed_atomics_count, 1) == PREPOST_ATOMICS_COUNT) { /* re-post atomics requests */ PREPOST_ATOMICS(); } switch (header.proto.op) { case COMEX_FETCH_AND_ADD: { int v = __sync_fetch_and_add((int*)header.rmw.addr, header.rmw.extra); OFI_RETRY(fi_tinject(ofi_data.ep_tagged.endpoint, &v, sizeof(v), ofi_data.ep_tagged.peers[header.proto.proc].fi_addr, ATOMICS_DATA_TAGMASK | header.proto.tag), "fi_tinject: failed"); } break; case COMEX_FETCH_AND_ADD_LONG: { uint64_t v = __sync_fetch_and_add((uint64_t*)header.rmw.addr, (uint64_t)header.rmw.extra); OFI_RETRY(fi_tinject(ofi_data.ep_tagged.endpoint, &v, sizeof(v), ofi_data.ep_tagged.peers[header.proto.proc].fi_addr, ATOMICS_DATA_TAGMASK | header.proto.tag), "fi_tinject: failed"); } break; case COMEX_SWAP: { int v = __sync_lock_test_and_set((int*)header.rmw.addr, (int)header.rmw.src); OFI_RETRY(fi_tinject(ofi_data.ep_tagged.endpoint, &v, sizeof(v), ofi_data.ep_tagged.peers[header.proto.proc].fi_addr, ATOMICS_DATA_TAGMASK | header.proto.tag), "fi_tinject: failed"); } break; case COMEX_SWAP_LONG: { uint64_t v = __sync_lock_test_and_set((uint64_t*)header.rmw.addr, header.rmw.src); OFI_RETRY(fi_tinject(ofi_data.ep_tagged.endpoint, &v, sizeof(v), ofi_data.ep_tagged.peers[header.proto.proc].fi_addr, ATOMICS_DATA_TAGMASK | header.proto.tag), "fi_tinject: failed"); } break; case COMEX_ACC_DBL: case COMEX_ACC_FLT: case COMEX_ACC_INT: case COMEX_ACC_LNG: case COMEX_ACC_DCP: case COMEX_ACC_CPL: { int i; request_t* parent = 0; size_t chunk = (size_t)header.acc.len + sizeof(header); size_t total = (size_t)header.acc.count * chunk + sizeof(header); char* buffer = 0; parent = alloc_request(); parent->dtor = req_dtor; parent->cmpl = full_acc_completion; parent->data = (char*)malloc(total); EXPR_CHKANDJUMP(parent->data, "failed to allocate data"); parent->flags |= rf_auto_free; increment_request_cnt(parent); /* after all accs are completed - reply packet will be sent. * save data for reply packet into buffer. */ ofi_atomics_t* reply_header = parent->data; *reply_header = header; buffer = reply_header->acc.data; if (header.acc.len > ofi_data.max_buffered_send && buffer && 0) { /* allocate region to receive data */ struct fi_context context; COMEX_CHKANDJUMP(mr_reg(buffer, total, MR_ACCESS_PERMISSIONS, &parent->mr_single, &context, ot_rma), "fi_mr_reg failed:"); } for (i = 0; i < header.acc.count; i++) { request_t* request = alloc_request(); request->dtor = req_dtor; request->flags |= rf_no_free_data; /* data will be removed by parent */ set_parent_request(parent, request); request->data = buffer; buffer += chunk; request->cmpl = chunk_acc_completion; OFI_RETRY(fi_trecv(ofi_data.ep_tagged.endpoint, request->data, chunk, parent->mr_single ? fi_mr_desc(parent->mr_single) : 0, ofi_data.ep_tagged.peers[header.proto.proc].fi_addr, ATOMICS_ACC_DATA_TAGMASK | header.proto.tag, 0, request), "fi_trecv: failed to prepost request"); } assert(parent->state == rs_progress); decrement_request_cnt(parent); } break; case OFI_MUTEX_AM_LOCK: { assert(am_mutex_locked); assert(am_mutex_waiter); fastlock_acquire(&mutex_lock); if (am_mutex_locked[header.mutex.num] == PROC_NONE) { /* mutex is not locked */ proc = header.proto.proc; am_mutex_locked[header.mutex.num] = header.proto.proc; } else { /* mutex is locked. add rank to waiters list */ int idx = am_mutex_locked[header.mutex.num]; do { if (am_mutex_waiter[idx] == PROC_NONE) { am_mutex_waiter[idx] = header.proto.proc; idx = PROC_NONE; } else idx = am_mutex_waiter[idx]; } while (idx != PROC_NONE); } fastlock_release(&mutex_lock); if (proc != PROC_NONE) { int v = 0; OFI_RETRY(fi_tinject(ofi_data.ep_tagged.endpoint, &v, sizeof(v), ofi_data.ep_tagged.peers[header.proto.proc].fi_addr, ATOMICS_MUTEX_TAGMASK | header.proto.proc), "fi_tinject: failed"); } } break; case OFI_MUTEX_AM_UNLOCK: { assert(am_mutex_locked); assert(am_mutex_waiter); fastlock_acquire(&mutex_lock); assert(am_mutex_locked[header.mutex.num] == header.proto.proc); am_mutex_locked[header.mutex.num] = am_mutex_waiter[header.proto.proc]; am_mutex_waiter[header.proto.proc] = PROC_NONE; proc = am_mutex_locked[header.mutex.num]; fastlock_release(&mutex_lock); if (proc != PROC_NONE) /* notify new owner of mutex */ { int v = 0; OFI_RETRY(fi_tinject(ofi_data.ep_tagged.endpoint, &v, sizeof(v), ofi_data.ep_tagged.peers[proc].fi_addr, ATOMICS_MUTEX_TAGMASK | proc), "fi_tinject: failed"); } } break; default: COMEX_OFI_LOG(ERROR, "incorrect atomic operation type: %d", header.proto.op); break; } fn_success: return; fn_fail: return; } static int init_ofi() { OFI_LOCK_INIT(); parse_env_vars(); if (load_ofi(&ld_table) != COMEX_SUCCESS) goto fn_fail; struct fi_info* hints_saw = fi_allocinfo_p(); hints_saw->mode = FI_CONTEXT; hints_saw->ep_attr->type = FI_EP_RDM; /* Reliable datagram */ hints_saw->caps = DESIRED_PROVIDER_CAPS; hints_saw->domain_attr->threading = FI_THREAD_ENDPOINT; hints_saw->domain_attr->control_progress = FI_PROGRESS_AUTO; hints_saw->domain_attr->data_progress = FI_PROGRESS_AUTO; hints_saw->fabric_attr->prov_name = env_data.provider ? strdup(env_data.provider) : 0; struct fi_info* hints_remcon = CALL_TABLE_FUNCTION(&ld_table, fi_dupinfo(hints_saw)); hints_saw->tx_attr->op_flags |= FI_COMPLETION | FI_TRANSMIT_COMPLETE; hints_saw->tx_attr->msg_order |= FI_ORDER_SAW; hints_saw->rx_attr->msg_order |= FI_ORDER_SAW; hints_remcon->tx_attr->op_flags |= FI_COMPLETION | FI_DELIVERY_COMPLETE; /* ------------------------------------------------------------------------ */ /* Set default settings before any ofi-provider is inited */ /* (before any call of fi_getinfo and fi_fabric) */ /* ------------------------------------------------------------------------ */ tune_ofi_provider(); /* first try to initialize requested endpoint with all desired capabilities */ int init_done; /* first try to get provider where delivery complete supported */ init_done = (init_ep(hints_remcon, &ofi_data.ep_rma, 1) == COMEX_SUCCESS); if (init_done) ofi_data.ep_atomics = ofi_data.ep_rma; else { init_done = (init_ep(hints_saw, &ofi_data.ep_rma, 1) == COMEX_SUCCESS); if (init_done) { /* great!!! we got provider with all required caps */ ofi_data.ep_atomics = ofi_data.ep_rma; } } if (!init_done) { /* ok, try to use different providers for RMA & atomics */ hints_saw->caps = RMA_PROVIDER_CAPS; hints_remcon->caps = RMA_PROVIDER_CAPS; init_done = (init_ep(hints_remcon, &ofi_data.ep_rma, 1) == COMEX_SUCCESS); if (!init_done) init_done = (init_ep(hints_saw, &ofi_data.ep_rma, 1) == COMEX_SUCCESS); EXPR_CHKANDJUMP(init_done, "failed to create endpoint"); if (IS_TARGET_ATOMICS_EMULATION()) { /* in case when emulated atomics are performed on target side use only p2p api */ ofi_data.ep_atomics = ofi_data.ep_rma; } else /* native or emulated on origin side (acc over rma, rmw over atomic) */ { hints_remcon->caps = ATOMICS_PROVIDER_CAPS; hints_saw->caps = ATOMICS_PROVIDER_CAPS; hints_remcon->fabric_attr->prov_name = 0; hints_saw->fabric_attr->prov_name = 0; init_done = (init_ep(hints_remcon, &ofi_data.ep_atomics, 0) == COMEX_SUCCESS); if (!init_done) init_done = (init_ep(hints_saw, &ofi_data.ep_atomics, 0) == COMEX_SUCCESS); EXPR_CHKANDJUMP(init_done, "failed to create endpoint"); } } CALL_TABLE_FUNCTION(&ld_table, fi_freeinfo(hints_saw)); CALL_TABLE_FUNCTION(&ld_table, fi_freeinfo(hints_remcon)); /* ----------------------------- */ /* Get provider limitations */ /* ----------------------------- */ if (ofi_data.ep_tagged.provider->mode & FI_MSG_PREFIX && ofi_data.ep_tagged.provider->ep_attr) ofi_data.msg_prefix_size = ofi_data.ep_tagged.provider->ep_attr->msg_prefix_size; if (ofi_data.ep_rma.provider->tx_attr) { ofi_data.rma_iov_limit = ofi_data.ep_rma.provider->tx_attr->rma_iov_limit; ofi_data.max_buffered_send = ofi_data.ep_rma.provider->tx_attr->inject_size; } int comex_dtype = 0; enum fi_datatype ofi_dtype = FI_DATATYPE_LAST; size_t max_elems_in_atomic = 0; #ifdef USE_ATOMIC_EMULATION if (env_data.native_atomics) { comex_acc_f = comex_acc_native; comex_accs_f = comex_accs_native; comex_accv_f = comex_accv_native; comex_nbacc_f = comex_nbacc_native; comex_nbaccs_f = comex_nbaccs_native; comex_nbaccv_f = comex_nbaccv_native; } else { comex_acc_f = comex_acc_emu; comex_accs_f = comex_accs_emu; comex_accv_f = comex_accv_emu; comex_nbacc_f = comex_nbacc_emu; comex_nbaccs_f = comex_nbaccs_emu; comex_nbaccv_f = comex_nbaccv_emu; } #endif /* USE_ATOMIC_EMULATION */ if (env_data.native_atomics) { for (comex_dtype = COMEX_ACC_INT; comex_dtype <= COMEX_ACC_LNG; comex_dtype++) { ofi_dtype = GET_FI_DTYPE(comex_dtype); EXPR_CHKANDJUMP(ofi_dtype != -1, "datatype is not supported: %d", comex_dtype); int ret; OFI_CALL(ret, fi_atomicvalid(ofi_data.ep_atomics.endpoint, ofi_dtype, FI_SUM, &max_elems_in_atomic)); if (ret < 0) ofi_data.max_bytes_in_atomic[COMEX_DTYPE_IDX(comex_dtype)] = -1; else { size_t comex_dtype_size = 0; COMEX_DTYPE_SIZEOF(comex_dtype, comex_dtype_size); ofi_data.max_bytes_in_atomic[COMEX_DTYPE_IDX(comex_dtype)] = max_elems_in_atomic * comex_dtype_size; } } } if (l_state.proc == 0) { COMEX_OFI_LOG(INFO, "rma_ep: %s", ofi_data.ep_rma.provider->fabric_attr->prov_name); COMEX_OFI_LOG(INFO, "atomics_ep: %s", ofi_data.ep_atomics.provider->fabric_attr->prov_name); COMEX_OFI_LOG(INFO, "msg_prefix_size: %d", ofi_data.msg_prefix_size); COMEX_OFI_LOG(INFO, "rma_iov_limit: %d", ofi_data.rma_iov_limit); COMEX_OFI_LOG(INFO, "max_buffered_send: %d", ofi_data.max_buffered_send); if (env_data.native_atomics) { for (comex_dtype = COMEX_ACC_INT; comex_dtype <= COMEX_ACC_LNG; comex_dtype++) COMEX_OFI_LOG(INFO, "max_bytes_in_atomic: datatype %d, bytes %zd", comex_dtype, ofi_data.max_bytes_in_atomic[COMEX_DTYPE_IDX(comex_dtype)]); } } if (env_data.progress_thread) { tid = pthread_self(); pthread_create(&progress_thread, 0, progress_thread_func, 0); } /* prepost atomics requests */ if (IS_TARGET_ATOMICS_EMULATION()) PREPOST_ATOMICS(); ofi_data.mr_counter = 0; fn_success: return COMEX_SUCCESS; fn_fail: finalize_ofi(); return COMEX_FAILURE; } #define CQ_CHKANDJUMP(cq, ret) \ do \ { \ if (ret < 0 && ret != -FI_EAGAIN) \ { \ struct fi_cq_err_entry error; \ COMEX_OFI_LOG(INFO, "cq_read: error available"); \ int err = fi_cq_readerr(cq, (void*)&error, 0); \ if (err < 0) \ { \ COMEX_OFI_LOG(INFO, "cq_read_err: can't retrieve error... (%ld)",\ ret); \ goto fn_fail; \ } \ COMEX_OFI_LOG(INFO, "cq_read_err: error is %d (ret=%ld)", \ error.err, ret); \ goto fn_fail; \ } \ } while(0) #define CQ_READ(cq) \ do \ { \ if (OFI_TRYLOCK()) \ { \ locked = 1; \ ret = fi_cq_read(cq, entries, env_data.cq_entries_count); \ CQ_CHKANDJUMP(cq, ret); \ OFI_UNLOCK(); \ locked = 0; \ } \ } while (0) static int myofi_poll(int* items_processed) { ssize_t ret = 0; int locked = 0; struct fi_cq_tagged_entry entries[env_data.cq_entries_count]; memset(entries, 0, sizeof(entries)); CQ_READ(ofi_data.ep_rma.cq); if (ret <= 0 && dual_provider()) CQ_READ(ofi_data.ep_atomics.cq); if (items_processed) *items_processed = 0; if (ret > 0) { int idx; for (idx = 0; idx < ret; idx++) { request_t* request = (request_t*)entries[idx].op_context; if (request && request->magic == REQUEST_MAGIC) complete_request(request); } if (items_processed) *items_processed = (int)ret; } else if (ret == -FI_EAGAIN) {} else if (ret < 0) { assert(0); /* should not be here */ } fn_success: return COMEX_SUCCESS; fn_fail: if (locked) OFI_UNLOCK(); return COMEX_FAILURE; } int _comex_init(MPI_Comm comm) { int status; if (initialized) return 0; initialized = 1; /* Assert MPI has been initialized */ int init_flag; status = MPI_Initialized(&init_flag); assert(MPI_SUCCESS == status); assert(init_flag); /* Duplicate the World Communicator */ status = MPI_Comm_dup(comm, &(l_state.world_comm)); assert(MPI_SUCCESS == status); assert(l_state.world_comm); /* My Proc Number */ status = MPI_Comm_rank(l_state.world_comm, &(l_state.proc)); assert(MPI_SUCCESS == status); /* World Size */ status = MPI_Comm_size(l_state.world_comm, &(l_state.size)); assert(MPI_SUCCESS == status); /* Evaluate local-proc information: local comm & rank */ MPI_Comm local_comm; status = MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, 0, MPI_INFO_NULL, &local_comm); assert(MPI_SUCCESS == status); assert(local_comm); /* Local Proc Number */ status = MPI_Comm_rank(local_comm, &(l_state.local_proc)); assert(MPI_SUCCESS == status); /* Local Size */ status = MPI_Comm_size(local_comm, &(l_state.local_size)); assert(MPI_SUCCESS == status); MPI_Comm_free(&local_comm); /* groups */ comex_group_init(); /* OFI initialization */ COMEX_CHKANDJUMP(init_ofi(), "failed to init ofi"); request_cache = create_request_cache(); int ret = create_mutexes(&local_mutex, 1); if (ret == COMEX_SUCCESS) { assert(local_mutex); local_mutex->tagmask = LOCAL_MUTEX_TAGMASK; } /* Synch - Sanity Check */ MPI_Barrier(l_state.world_comm); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_init() { return _comex_init(MPI_COMM_WORLD); } int comex_init_comm(MPI_Comm comm) { return _comex_init(comm); } int comex_init_args(int* argc, char*** argv) { int init_flag; MPI_Initialized(&init_flag); if (!init_flag) MPI_CHKANDJUMP(MPI_Init(argc, argv), "failed to init mpi"); COMEX_CHKANDJUMP(comex_init(), "failed to init comex"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_initialized() { return initialized; } void comex_error(const char *msg, int code) { COMEX_OFI_LOG(ERROR, "[%d] Received an Error in Communication: (%d) %s", l_state.proc, code, msg); MPI_Abort(l_state.world_comm, code); } static int lookup_window(void* ptr, int size, int proc, comex_group_t group, ofi_window_t** res) { VALIDATE_GROUP_AND_PROC(group, proc); int world_proc = PROC_NONE; comex_group_translate_world(group, proc, &world_proc); EXPR_CHKANDJUMP((world_proc != PROC_NONE), "invalid world proc"); uint64_t uptr = (uint64_t)ptr; ofi_window_t* wnd = ofi_wnd; #define CHECK_WND(_wnd, _proc, _ptr, _size) \ (_wnd->world_proc == _proc && _wnd->ptr <= _ptr && _wnd->ptr + _wnd->size >= _ptr + _size) /* first look in cache */ if (ofi_wnd_cache && CHECK_WND(ofi_wnd_cache, world_proc, uptr, size)) { *res = ofi_wnd_cache; return COMEX_SUCCESS; } /* else traverse list of windows */ while (wnd) { assert(wnd->local); if (CHECK_WND(wnd, world_proc, uptr, size)) { *res = ofi_wnd_cache = wnd; return COMEX_SUCCESS; } else if (wnd->world_proc == proc && wnd->ptr <= (uint64_t)ptr && wnd->ptr + (uint64_t)wnd->size > (uint64_t)ptr) { COMEX_OFI_LOG(INFO, "WARNING: found candidate window: missing %d bytes tail (length: %lu, expected: %d:%d)", (int)(((long)ptr + size) - ((long)wnd->ptr + wnd->size)), wnd->size, (int)((long)ptr - (long)wnd->ptr), size); } wnd = wnd->next; } fn_fail: return COMEX_FAILURE; } int comex_put( void* src, void* dst, int bytes, int proc, comex_group_t group) { comex_request_t handle; COMEX_CHKANDJUMP(comex_nbput(src, dst, bytes, proc, group, &handle), "failed to perform comex_nbput"); COMEX_CHKANDJUMP(comex_wait(&handle), "failed to perform comex_wait"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_get( void* src, void* dst, int bytes, int proc, comex_group_t group) { comex_request_t handle; COMEX_CHKANDJUMP(comex_nbget(src, dst, bytes, proc, group, &handle), "failed to perform comex_get"); COMEX_CHKANDJUMP(comex_wait(&handle), "failed to perform comex_wait"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static int comex_acc_native( int datatype, void* scale, void* src_ptr, void* dst_ptr, int bytes, int proc, comex_group_t group) { comex_request_t handle; COMEX_CHKANDJUMP(comex_nbacc_native(datatype, scale, src_ptr, dst_ptr, bytes, proc, group, &handle), "failed to perform comex_nbacc"); COMEX_CHKANDJUMP(comex_wait(&handle), "failed to perform comex_wait"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static int list_strides( int* src_stride_ar, int* dst_stride_ar, int* count, int stride_levels, int (*callback)(size_t src_idx, size_t dst_idx, void* data), void* data) { int i, j; size_t src_idx, dst_idx; /* index offset of current block position to ptr */ size_t n1dim; /* number of 1 dim block */ size_t src_bvalue[7], src_bunit[7]; size_t dst_bvalue[7], dst_bunit[7]; assert(callback); /* number of n-element of the first dimension */ n1dim = 1; for (i = 1; i <= stride_levels; i++) { n1dim *= count[i]; } /*assert(n1dim < 0x100000000);*/ /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for (i = 2; i <= stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i - 1] * count[i - 1]; dst_bunit[i] = dst_bunit[i - 1] * count[i - 1]; } for (i = 0; i < n1dim; i++) { src_idx = 0; for (j = 1; j <= stride_levels; j++) { src_idx += src_bvalue[j] * src_stride_ar[j - 1]; if ((i + 1) % src_bunit[j] == 0) { src_bvalue[j]++; } if (src_bvalue[j] > (count[j] - 1)) { src_bvalue[j] = 0; } } dst_idx = 0; for (j = 1; j <= stride_levels; j++) { dst_idx += dst_bvalue[j] * dst_stride_ar[j - 1]; if ((i + 1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if (dst_bvalue[j] > (count[j] - 1)) { dst_bvalue[j] = 0; } } COMEX_CHKANDJUMP(callback(src_idx, dst_idx, data), "failed to perform callback"); } fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_puts( void* src_ptr, int* src_stride_ar, void* dst_ptr, int* dst_stride_ar, int* count, int stride_levels, int proc, comex_group_t group) { comex_request_t handle; COMEX_CHKANDJUMP(comex_nbputs( src_ptr, src_stride_ar, dst_ptr, dst_stride_ar, count, stride_levels, proc, group, &handle), "failed to perform comex_nbputs"); COMEX_CHKANDJUMP(comex_wait(&handle), "failed to perform comex_wait"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_gets( void* src_ptr, int* src_stride_ar, void* dst_ptr, int* dst_stride_ar, int* count, int stride_levels, int proc, comex_group_t group) { comex_request_t handle; COMEX_CHKANDJUMP(comex_nbgets( src_ptr, src_stride_ar, dst_ptr, dst_stride_ar, count, stride_levels, proc, group, &handle), "failed to perform comex_nbgets"); COMEX_CHKANDJUMP(comex_wait(&handle), "failed to perform comex_wait"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static int comex_accs_native( int datatype, void* scale, void* src_ptr, int* src_stride_ar, void* dst_ptr, int* dst_stride_ar, int* count, int stride_levels, int proc, comex_group_t group) { comex_request_t handle; COMEX_CHKANDJUMP(comex_nbaccs_native(datatype, scale, src_ptr, src_stride_ar, dst_ptr, dst_stride_ar, count, stride_levels, proc, group, &handle), "failed to perform comex_nbaccs"); COMEX_CHKANDJUMP(comex_wait(&handle), "failed to perform comex_wait"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_putv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { comex_request_t handle; COMEX_CHKANDJUMP(comex_nbputv(iov, iov_len, proc, group, &handle), "failed to perform comex_nbputv"); COMEX_CHKANDJUMP(comex_wait(&handle), "failed to perform comex_wait"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_getv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { comex_request_t handle; COMEX_CHKANDJUMP(comex_nbgetv(iov, iov_len, proc, group, &handle), "failed to perform comex_nbgetv"); COMEX_CHKANDJUMP(comex_wait(&handle), "failed to perform comex_wait"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static int comex_accv_native( int datatype, void* scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { comex_request_t handle; COMEX_CHKANDJUMP(comex_nbaccv_native(datatype, scale, iov, iov_len, proc, group, &handle), "failed to perform comex_nbaccv"); COMEX_CHKANDJUMP(comex_wait(&handle), "failed to perform comex_wait"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_fence_all(comex_group_t group) { COMEX_CHKANDJUMP(comex_wait_all(group), "failed to perform comex_wait_all"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_fence_proc(int proc, comex_group_t group) { COMEX_CHKANDJUMP(comex_wait_all(group), "failed to perform comex_wait_all"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } /* comex_barrier is comex_fence_all + MPI_Barrier */ int comex_barrier(comex_group_t group) { MPI_Comm comm; COMEX_CHKANDJUMP(comex_fence_all(group), "failed to fence all"); COMEX_CHKANDJUMP(comex_group_comm(group, &comm), "failed to get group comm"); MPI_CHKANDJUMP(MPI_Barrier(comm), "failed to perform MPI_Barrier"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } void* comex_malloc_local(size_t size) { return malloc(size); } int comex_free_local(void* ptr) { assert(ptr); if (ptr) free(ptr); return COMEX_SUCCESS; } static int finalize_ep(ofi_ep_t* ep) { int ret; /* close OFI devices */ if (ep->endpoint) { OFI_CHKANDJUMP(fi_close((struct fid*)ep->endpoint), "fi_close endpoint:"); ep->endpoint = 0; } if (ep->av) { OFI_CHKANDJUMP(fi_close((struct fid*)ep->av), "fi_close address vector:"); ep->av = 0; } if (ep->cq) { OFI_CHKANDJUMP(fi_close((struct fid*)ep->cq), "fi_close completion queue:"); ep->cq = 0; } if (ep->domain) { OFI_CALL(ret, fi_close((struct fid*)ep->domain)); if (ret == -FI_EBUSY) { COMEX_OFI_LOG(WARN, "domain is busy"); COMEX_CHKANDJUMP(destroy_all_windows(), "failed to destroy all windows"); OFI_CALL(ret, fi_close((struct fid*)ep->domain)); } OFI_CHKANDJUMP(ret, "fi_close domain:"); ep->domain = 0; } if (ep->fabric) { OFI_CHKANDJUMP(fi_close((struct fid*)ep->fabric), "fi_close fabric:"); ep->fabric = 0; } if (ep->peers) { free(ep->peers); ep->peers = 0; } fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static int finalize_ofi() { int ret = 0; if (progress_thread) { progress_thread_complete = 1; pthread_join(progress_thread, 0); } if (IS_TARGET_ATOMICS_EMULATION()) { int req_idx; for (req_idx = 0; req_idx < PREPOST_ATOMICS_COUNT; req_idx++) { request_t* req = &atomics_requests[req_idx]; if (req->state == rs_complete) continue; assert(req->state == rs_progress); OFI_CHKANDJUMP(fi_cancel((fid_t)ofi_data.ep_tagged.endpoint, &atomics_requests[req_idx]), "fi_cancel failed"); struct fi_cq_err_entry err; fi_cq_readerr(ofi_data.ep_tagged.cq, &err, 0); } } if (dual_provider()) COMEX_CHKANDJUMP(finalize_ep(&ofi_data.ep_atomics), "failed to finalize ep_atomics"); COMEX_CHKANDJUMP(finalize_ep(&ofi_data.ep_rma), "failed to finalize ep_rma"); OFI_LOCK_DESTROY(); unload_ofi(&ld_table); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_finalize() { /* it's okay to call multiple times -- extra calls are no-ops */ if (!initialized) return COMEX_SUCCESS; initialized = 0; /* Make sure that all outstanding operations are done */ COMEX_CHKANDJUMP(comex_wait_all(COMEX_GROUP_WORLD), "failed to wait all"); COMEX_CHKANDJUMP(destroy_mutexes(local_mutex), "failed to destroy local mutex"); COMEX_CHKANDJUMP(finalize_ofi(), "failed to finalize ofi"); MPI_CHKANDJUMP(MPI_Barrier(l_state.world_comm), "failed to perform MPI_Barrier"); /* groups */ comex_group_finalize(); // destroy the communicators MPI_CHKANDJUMP(MPI_Comm_free(&l_state.world_comm), "failed to perform MPI_Comm_free"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_wait_proc(int proc, comex_group_t group) { request_cache_t* cache = request_cache; while (cache) { int i; if (group != COMEX_GROUP_WORLD) { /* wait for requests for specific group */ for (i = 0; i < sizeofa(cache->request); i++) { request_t* request = cache->request + i; /* check for proc/group on every iteration because * it may be changed during poll */ while (!(request->flags & rf_no_group_wait) && request->proc == proc && request->group == group && request->state == rs_progress) { COMEX_CHKANDJUMP(myofi_poll(0), "failed to poll"); PAUSE(); } } } else { /* wait all */ for (i = 0; i < sizeofa(cache->request); i++) { request_t* request = cache->request + i; while (request->proc == proc && request->state == rs_progress) { COMEX_CHKANDJUMP(myofi_poll(0), "failed to poll"); PAUSE(); } } } cache = cache->next; } fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static inline int wait_request(request_t* request) { assert(request); assert(!request->cmpl); assert(request->state == rs_progress || request->state == rs_complete); assert(!(request->flags & rf_auto_free)); if (request->state == rs_complete) { free_request(request); return COMEX_SUCCESS; } while (request->state == rs_progress) { COMEX_CHKANDJUMP(myofi_poll(0), "failed to poll"); PAUSE(); } free_request(request); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_wait(comex_request_t* handle) { assert(handle); if (*handle == HANDLE_UNDEFINED) return COMEX_SUCCESS; request_t* request = lookup_request(*handle); if (!request || request->state == rs_none) return COMEX_FAILURE; return wait_request(request); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } /* *status == 1 - request is in progress */ int comex_test(comex_request_t* handle, int* status) { assert(handle); assert(status); *status = 0; request_t* request = lookup_request(*handle); if (!request || request->state == rs_none) return COMEX_FAILURE; int items_processed = 1; /* process all CQ items in queue till request in 'progress' state * or queue is not empty (items_processed is not 0) */ while (request->state == rs_progress && items_processed) COMEX_CHKANDJUMP(myofi_poll(&items_processed), "failed to poll"); *status = (request->state == rs_progress); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_wait_all(comex_group_t group) { request_cache_t* cache = request_cache; while (cache) { int i; if (group != COMEX_GROUP_WORLD) { /* wait for requests for specific group */ for (i = 0; i < sizeofa(cache->request); i++) { request_t* request = cache->request + i; while (!(request->flags & rf_no_group_wait) && request->group == group && request->state == rs_progress) { COMEX_CHKANDJUMP(myofi_poll(0), "failed to poll"); PAUSE(); } } } else { /* wait all */ for (i = 0; i < sizeofa(cache->request); i++) { request_t* request = cache->request + i; while (!(request->flags & rf_no_group_wait) && request->state == rs_progress) { COMEX_CHKANDJUMP(myofi_poll(0), "failed to poll"); PAUSE(); } } } cache = cache->next; } fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } #define SCALE(type) \ do \ { \ type* ptr = 0; \ for (ptr = middle, i = 0; i < iov_len; i++) \ for (j = 0; j < iov[i].count; j++) \ for (k = 0; k < iov[i].bytes / sizeof(*ptr); k++, ptr++) \ *ptr = ((type*)iov[i].src[j])[k] * *(type*)scale; \ } while (0) static int iov_acc(int datatype, void* scale, int proc, comex_group_t group, comex_giov_t *iov, int iov_len, comex_request_t * handle) { VALIDATE_GROUP_AND_PROC(group, proc); if (handle) *handle = HANDLE_UNDEFINED; request_t* parent_req = alloc_request(); EXPR_CHKANDJUMP(parent_req, "failed to allocate parent request"); if (!handle) parent_req->flags |= rf_auto_free; else { /* create user's request. we can't use parent request because it can't have completion callback */ request_t* user = alloc_request(); user->group = group; user->proc = proc; *handle = user->index; set_parent_request(user, parent_req); } int ofi_iov_count = 0; int ofi_msg_count = 0; int bytes_in_msg = 0; // current count of bytes in atomic msg int iov_in_msg = 0; // current count of iov in atomic msg int middle_len = 0; /* common number of bytes to process */ void* middle = 0; /* buffer to store scaled values (in case if scale is not 1) */ struct fi_msg_atomic* msg = 0; int iov_idx; int iov_column; acc_data_t* acc_data = 0; ssize_t max_bytes_in_atomic = ofi_data.max_bytes_in_atomic[COMEX_DTYPE_IDX(datatype)]; EXPR_CHKANDJUMP(max_bytes_in_atomic > 0, "datatype is not supported: %d", datatype); /* calculate common count of iov elements & common length of data to send */ for (iov_idx = 0; iov_idx < iov_len; iov_idx++) { /* count of vector elements to process */ ofi_iov_count += iov[iov_idx].count * ((iov[iov_idx].bytes / max_bytes_in_atomic) + 1); middle_len += iov[iov_idx].count * iov[iov_idx].bytes; } /* * Calculate count of atomic msg which enough to pack all comex iov */ /* First rough option - in worst case we will have one iov in atomic msg & number of msg's will be equal number of iov*/ //ofi_msg_count = ofi_iov_count; /* Second more accurate option - in worst case we will have only one not fully filled (in terms of iov elements) atomic msg per comex iov */ /*iov_in_msg = 0; for (iov_idx = 0; iov_idx < iov_len; iov_idx++) { // initiate counts of msg and iov in msg for worst case int msg_per_comex_iov = iov[iov_idx].count * ((iov[iov_idx].bytes / max_bytes_in_atomic) + 1); iov_in_msg = 1; // bytes in iov-column less than atomic msg can hold and there is enough space for more than 1 iov-column if (max_bytes_in_atomic / iov[iov_idx].bytes > 1) { iov_in_msg = max_bytes_in_atomic / iov[iov_idx].bytes; iov_in_msg = min(iov_in_msg, ofi_data.rma_iov_limit); } if (iov_in_msg > 1) msg_per_comex_iov = iov[iov_idx].count / iov_in_msg + 1; ofi_msg_count += msg_per_comex_iov; }*/ /* Third the most accurate option - to calculate msg count we repeat iov repacking algorithm */ bytes_in_msg = 0; iov_in_msg = 0; for (iov_idx = 0; iov_idx < iov_len; iov_idx++) { for (iov_column = 0; iov_column < iov[iov_idx].count; iov_column++) { ssize_t bytes_to_send = iov[iov_idx].bytes; for (; bytes_to_send > 0; bytes_to_send -= max_bytes_in_atomic) { int bytes_in_chunk = min(max_bytes_in_atomic, bytes_to_send); if (iov_in_msg >= ofi_data.rma_iov_limit || bytes_in_msg + bytes_in_chunk > max_bytes_in_atomic) { iov_in_msg = 0; bytes_in_msg = 0; ofi_msg_count++; } bytes_in_msg += bytes_in_chunk; iov_in_msg++; } } } ofi_msg_count++; /* no data to process? just exit */ if (!middle_len) goto fn_success; acc_data = malloc(sizeof(*acc_data)); EXPR_CHKANDJUMP(acc_data, "failed to allocate acc_data"); memset(acc_data, 0, sizeof(*acc_data)); acc_data->proc = PROC_NONE; parent_req->data = acc_data; parent_req->cmpl = complete_acc; if (!scale_is_1(datatype, scale)) { /* create local scaled buffer */ int i; int j; int k; acc_data->middle = middle = malloc(middle_len); switch (datatype) { case COMEX_ACC_INT: SCALE(int); break; case COMEX_ACC_DBL: SCALE(double); break; case COMEX_ACC_FLT: SCALE(float); break; case COMEX_ACC_LNG: SCALE(long); break; case COMEX_ACC_DCP: assert(sizeof(DoubleComplex) == sizeof(double complex)); SCALE(double complex); break; case COMEX_ACC_CPL: assert(sizeof(SingleComplex) == sizeof(float complex)); SCALE(float complex); break; default: COMEX_OFI_LOG(WARN, "iov_acc: incorrect data type: %d", datatype); return 1; } } enum fi_datatype fi_dtype = GET_FI_DTYPE(datatype); EXPR_CHKANDJUMP(fi_dtype >= 0, "incorrect fi_datatype: %d", datatype); int datasize; COMEX_DTYPE_SIZEOF(datatype, datasize); int msg_len = sizeof(*msg) * ofi_msg_count; assert(msg_len); acc_data->msg = msg = malloc(msg_len); EXPR_CHKANDJUMP(msg, "failed to allocate atomic messages"); memset(msg, 0, msg_len); msg->msg_iov = malloc(sizeof(*msg->msg_iov) * ofi_iov_count); EXPR_CHKANDJUMP(msg->msg_iov, "failed to allocate msg iov"); msg->rma_iov = malloc(sizeof(*msg->rma_iov) * ofi_iov_count); EXPR_CHKANDJUMP(msg->rma_iov, "failed to allocate rma iov"); struct fi_msg_atomic* m = 0; struct fi_ioc* ioc = (struct fi_ioc*)msg->msg_iov; struct fi_rma_ioc* rma_ioc = (struct fi_rma_ioc*)msg->rma_iov; char* sbuf = middle; /* lock remote host * * TODO: is it required? per-element atomic is provided by OFI */ //acquire_remote_lock(proc); acc_data->proc = proc; bytes_in_msg = 0; iov_in_msg = 0; int msg_num = 0; /* lock request - deny to complete */ increment_request_cnt(parent_req); for (iov_idx = 0; iov_idx < iov_len; iov_idx++) { for (iov_column = 0; iov_column < iov[iov_idx].count; iov_column++) { ofi_window_t* wnd = 0; COMEX_CHKANDJUMP(lookup_window(iov[iov_idx].dst[iov_column], iov[iov_idx].bytes, proc, group, &wnd), "failed to lookup window"); assert(wnd); ssize_t bytes_to_send = iov[iov_idx].bytes; char* src = sbuf ? sbuf : iov[iov_idx].src[iov_column]; char* dst = iov[iov_idx].dst[iov_column]; for (; bytes_to_send > 0; ioc++, rma_ioc++, bytes_to_send -= max_bytes_in_atomic, src += max_bytes_in_atomic, dst += max_bytes_in_atomic) { int bytes_in_chunk = min(max_bytes_in_atomic, bytes_to_send); /* * OFI atomic msg has 2 limits: count of iov and count of bytes in all iovs. * If msg is close to one of limits - pass it to OFI & clean pointer */ if (m && (iov_in_msg >= ofi_data.rma_iov_limit || bytes_in_msg + bytes_in_chunk > max_bytes_in_atomic)) { OFI_RETRY(fi_atomicmsg(ofi_data.ep_atomics.endpoint, m, 0), "failed to fi_atomicmsg:"); m = 0; bytes_in_msg = 0; iov_in_msg = 0; } if (!m) /* if msg is NULL (not created or cleaned above) - just create it */ { request_t* child_req = alloc_request(); EXPR_CHKANDJUMP(child_req, "failed to allocate child request"); set_parent_request(parent_req, child_req); child_req->proc = proc; assert(msg_num < ofi_msg_count); m = msg + msg_num; msg_num++; m->datatype = fi_dtype; m->op = FI_SUM; m->context = child_req; m->msg_iov = ioc; m->rma_iov = rma_ioc; m->addr = wnd->peer_atomics->fi_addr; } /* add new chunk into IOV */ bytes_in_msg += bytes_in_chunk; iov_in_msg++; assert(m->iov_count == m->rma_iov_count); ioc->count = rma_ioc->count = (bytes_in_chunk % datasize) ? (bytes_in_chunk / datasize + 1) : (bytes_in_chunk / datasize); rma_ioc->addr = get_remote_addr(wnd->ptr, dst, ot_atomic); ioc->addr = src; rma_ioc->key = wnd->key_atomics; m->iov_count = m->rma_iov_count = iov_in_msg; } if (sbuf) sbuf += iov[iov_idx].bytes; } } /* send tail of data */ if (m) OFI_RETRY(fi_atomicmsg(ofi_data.ep_atomics.endpoint, m, 0), "failed to fi_atomicmsg:"); decrement_request_cnt(parent_req); fn_success: return COMEX_SUCCESS; fn_fail: if (parent_req) { complete_request(parent_req); free_request(parent_req); } return COMEX_FAILURE; } int nb_getput( void* src, void* dst, int bytes, int proc, comex_group_t group, comex_request_t *handle, int is_get_op) { VALIDATE_GROUP_AND_PROC(group, proc); if (handle) *handle = HANDLE_UNDEFINED; request_t* request = alloc_request(); EXPR_CHKANDJUMP(request, "failed to allocate request"); ofi_window_t* wnd = 0; COMEX_CHKANDJUMP(lookup_window(is_get_op ? src : dst, bytes, proc, group, &wnd), "failed to lookup window"); assert(wnd); request->group = group; request->proc = proc; if (bytes > ofi_data.max_buffered_send) { /* we have to register buffer */ struct fi_context context; request->dtor = req_dtor; COMEX_CHKANDJUMP(mr_reg(is_get_op ? dst : src, bytes, MR_ACCESS_PERMISSIONS, &request->mr_single, &context, ot_rma), "fi_mr_reg failed:"); if (!is_get_op) OFI_RETRY(fi_write(ofi_data.ep_rma.endpoint, src, bytes, fi_mr_desc(request->mr_single), wnd->peer_rma->fi_addr, get_remote_addr(wnd->ptr, dst, ot_rma), wnd->key_rma, request), "fi_write error:"); } if (is_get_op) OFI_RETRY(fi_read(ofi_data.ep_rma.endpoint, dst, bytes, request->mr_single ? fi_mr_desc(request->mr_single) : 0, wnd->peer_rma->fi_addr, get_remote_addr(wnd->ptr, src, ot_rma), wnd->key_rma, request), "fi_read error:"); else if (bytes <= ofi_data.max_buffered_send) { OFI_RETRY(fi_inject_write(ofi_data.ep_rma.endpoint, src, bytes, wnd->peer_rma->fi_addr, get_remote_addr(wnd->ptr, dst, ot_rma), wnd->key_rma), "fi_inject_write error:"); complete_request(request); /*OFI_RETRY(fi_write(ofi_data.ep_rma.endpoint, src, bytes, 0, wnd->peer_rma->fi_addr, get_remote_addr(wnd->ptr, dst, ot_rma), wnd->key_rma, request), "fi_write error:"); */ } if (handle) *handle = (comex_request_t)request->index; fn_success: if (env_data.force_sync) { if (request) wait_request(request); if (handle) *handle = HANDLE_UNDEFINED; } return COMEX_SUCCESS; fn_fail: if (request) free_request(request); return COMEX_FAILURE; } int comex_nbput( void* src, void* dst, int bytes, int proc, comex_group_t group, comex_request_t *handle) { COMEX_CHKANDJUMP(nb_getput(src, dst, bytes, proc, group, handle, 0), "failed to perform nb_getput"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_nbget( void* src, void* dst, int bytes, int proc, comex_group_t group, comex_request_t *handle) { COMEX_CHKANDJUMP(nb_getput(src, dst, bytes, proc, group, handle, 1), "failed to perform nb_getput"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static int comex_nbacc_native( int datatype, void* scale, void* src_ptr, void* dst_ptr, int bytes, int proc, comex_group_t group, comex_request_t *handle) { comex_giov_t iov; iov.bytes = bytes; iov.count = 1; iov.src = &src_ptr; iov.dst = &dst_ptr; COMEX_CHKANDJUMP(iov_acc(datatype, scale, proc, group, &iov, 1, handle), "failed to perform iov_acc"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } #define PUT_MSG(endpoint, msg) \ do \ { \ OFI_RETRY(fi_writemsg(endpoint, msg, 0), "fi_writemsg error:"); \ } while (0) #define GET_MSG(endpoint, msg) \ do \ { \ OFI_RETRY(fi_readmsg(endpoint, msg, 0), "fi_readmsg error:"); \ } while (0) #define REG_LOCAL_MR(msg, request, op_type) \ do \ { \ ofi_ep_t* ep = get_ep(op_type); \ if (FI_MR_SCALABLE == ep->mr_mode) \ { \ /* skip registration of local buffers in case of scalable mr */ \ break; \ } \ (request)->mrs = (struct fid_mr**)malloc((msg)->iov_count * sizeof(*(request)->mrs)); \ EXPR_CHKANDJUMP((request)->mrs, "failed to allocate memory"); \ mr_regv((msg)->msg_iov, (msg)->iov_count, (request)->mrs, ot_rma); \ (request)->mr_count = (msg)->iov_count; \ (request)->dtor = req_dtor; \ \ (msg)->desc = (void**)malloc(sizeof(*(msg)->desc) * (msg)->iov_count); \ EXPR_CHKANDJUMP((msg)->desc, "failed to register memory:"); \ int i; \ OFI_LOCK(); \ for(i = 0; i < (msg)->iov_count; i++) \ (msg)->desc[i] = fi_mr_desc((request)->mrs[i]); \ OFI_UNLOCK(); \ } while(0) #define FREE_DESC(msg) free((msg)->desc) static inline int getputs_stride(size_t src_idx, size_t dst_idx, void* data) { assert(data); assert(src_idx >= 0); assert(dst_idx >= 0); strided_context_t* context = (strided_context_t*)data; struct fi_msg_rma* msg = context->msg; if (!msg) { context->msg = msg = malloc(sizeof(*msg)); EXPR_CHKANDJUMP(msg, "failed to allocate rma message"); memset(msg, 0, sizeof(*msg)); msg->msg_iov = malloc(ofi_data.rma_iov_limit * sizeof(*msg->msg_iov)); EXPR_CHKANDJUMP(msg->msg_iov, "failed to allocate msg iov"); msg->rma_iov = malloc(ofi_data.rma_iov_limit * sizeof(*msg->rma_iov)); EXPR_CHKANDJUMP(msg->rma_iov, "failed to allocate rma iov"); request_t* child_req = alloc_request(); EXPR_CHKANDJUMP(child_req, "failed to allocate child request"); set_parent_request(context->request, child_req); msg->context = child_req; child_req->data = msg; child_req->cmpl = complete_getput; } assert(msg); struct iovec* iovelem = (struct iovec*)msg->msg_iov + msg->iov_count; struct fi_rma_iov* rmaelem = (struct fi_rma_iov*)msg->rma_iov + msg->rma_iov_count; iovelem->iov_base = context->is_get_op ? (char*)context->dst + dst_idx : (char*)context->src + src_idx; void* raw_remote_addr = (context->is_get_op ? (char*)context->src + src_idx : (char*)context->dst + dst_idx); rmaelem->addr = get_remote_addr(context->wnd->ptr, raw_remote_addr, ot_rma); iovelem->iov_len = context->count[0]; rmaelem->len = context->count[0]; assert((uint64_t)raw_remote_addr >= context->wnd->ptr); assert(rmaelem->len > 0); assert((uint64_t)raw_remote_addr + rmaelem->len <= context->wnd->ptr + context->wnd->size); rmaelem->key = context->wnd->key_rma; msg->addr = context->wnd->peer_rma->fi_addr; msg->iov_count++; msg->rma_iov_count++; assert(msg->iov_count == msg->rma_iov_count); if (msg->iov_count >= ofi_data.rma_iov_limit) { REG_LOCAL_MR(msg, (request_t*)msg->context, ot_rma); if (context->is_get_op) GET_MSG(ofi_data.ep_rma.endpoint, msg); else PUT_MSG(ofi_data.ep_rma.endpoint, msg); FREE_DESC(context->msg); context->msg = 0; } fn_success: return COMEX_SUCCESS; fn_fail: if (msg) { /* in case if context defined - then msg will be destroyed */ /* by request completion callback */ if (msg->context) { complete_request((request_t*)msg->context); free_request((request_t*)msg->context); } else { if (msg->msg_iov) free((void*)msg->msg_iov); if (msg->rma_iov) free((void*)msg->rma_iov); free(msg); } } return COMEX_FAILURE; } /* * src - pointer to source continuous buffer * src_stride - array with count of bytes for each dimension (src_stride[0] - for row, src_stride[1] - for matrix, ...) * dst, dst_stride - same as for src * count - count of elements to send for each dimension (count[0] - bytes! to send in first dimension, count[1] - elems! to send in second dimension, ...) * stride_levels - count of dimensions -1 (0 for row, 1 for matrix, ...) */ static int nb_getputs( void* src, int* src_stride, void* dst, int* dst_stride, int* count, int stride_levels, int proc, comex_group_t group, comex_request_t *handle, int is_get_op) { VALIDATE_GROUP_AND_PROC(group, proc); if (handle) *handle = HANDLE_UNDEFINED; ofi_window_t* wnd = 0; int total = count[0]; /* total count of bytes to process */ if (stride_levels > 0) { int i; for (i = 1; i <= stride_levels; i++) total *= count[i]; int * stride = (is_get_op) ? src_stride : dst_stride; } if (total == 0) goto fn_success; request_t* parent_req = alloc_request(); EXPR_CHKANDJUMP(parent_req, "failed to allocate parent request"); parent_req->group = group; parent_req->proc = proc; if (lookup_window(is_get_op ? src : dst, count[0], proc, group, &wnd) != COMEX_SUCCESS) { COMEX_OFI_LOG(INFO, "Failed to lookup window: length: %d", count[0]); int i; for (i = 0; i <= stride_levels; i++) COMEX_OFI_LOG(DEBUG, " %d: count = %d, stride: %d\n", i, count[i], ((is_get_op) ? src_stride : dst_stride)[i]); } assert(wnd); strided_context_t context = {.src = src, .dst = dst, .count = count, .proc = proc, .group = group, .request = parent_req, .ops = 0, .is_get_op = is_get_op, .msg = 0, .wnd = wnd}; /* lock parent_req - deny to complete */ increment_request_cnt(parent_req); COMEX_CHKANDJUMP(list_strides(src_stride, dst_stride, count, stride_levels, getputs_stride, &context), "failed to list strides"); if (context.msg) /* not all operation are scheduled */ { struct fi_msg_rma* msg = context.msg; REG_LOCAL_MR(msg, (request_t*)msg->context, ot_rma); if (is_get_op) GET_MSG(ofi_data.ep_rma.endpoint, msg); else PUT_MSG(ofi_data.ep_rma.endpoint, msg); FREE_DESC(msg); } if (!handle) parent_req->flags |= rf_auto_free; else *handle = parent_req->index; decrement_request_cnt(parent_req); /*if(!parent_req->child_cnt)*/ /*{*/ /*printf("no requests\n");*/ /*goto fn_fail;*/ /*}*/ fn_success: if (env_data.force_sync) { if (parent_req) wait_request(parent_req); if (handle) *handle = HANDLE_UNDEFINED; } return COMEX_SUCCESS; fn_fail: if (parent_req) free_request(parent_req); return COMEX_FAILURE; } int comex_nbputs( void* src, int* src_stride, void* dst, int* dst_stride, int* count, int stride_levels, int proc, comex_group_t group, comex_request_t *handle) { COMEX_CHKANDJUMP(nb_getputs(src, src_stride, dst, dst_stride, count, stride_levels, proc, group, handle, 0), "failed to perform nb_getputs"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_nbgets( void* src, int* src_stride, void* dst, int* dst_stride, int* count, int stride_levels, int proc, comex_group_t group, comex_request_t *handle) { COMEX_CHKANDJUMP(nb_getputs(src, src_stride, dst, dst_stride, count, stride_levels, proc, group, handle, 1), "failed to perform nb_getputs"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int add_to_iov_callback(size_t src_idx, size_t dst_idx, void* data) { assert(data); strided_context_t* ctx = (strided_context_t*)data; int iov_idx = ctx->cur_iov_idx; EXPR_CHKANDJUMP(iov_idx < ctx->iov_len, "incorrect iov_idx"); ctx->src_array[iov_idx] = ((char*)ctx->src + src_idx); ctx->dst_array[iov_idx] = ((char*)ctx->dst + dst_idx); ctx->iov[iov_idx].bytes = ctx->count[0]; ctx->iov[iov_idx].count = 1; ctx->iov[iov_idx].src = &(ctx->src_array[iov_idx]); ctx->iov[iov_idx].dst = &(ctx->dst_array[iov_idx]); ctx->cur_iov_idx++; fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static int comex_nbaccs_native( int datatype, void* scale, void* src, int* src_stride, void* dst, int* dst_stride, int* count, int stride_levels, int proc, comex_group_t group, comex_request_t *handle) { int ret = COMEX_SUCCESS; int iov_len = 1; int i; for (i = 1; i <= stride_levels; i++) iov_len *= count[i]; EXPR_CHKANDJUMP(iov_len, "incorrect iov_len"); comex_giov_t* iov = malloc(sizeof(comex_giov_t) * iov_len); EXPR_CHKANDJUMP(iov, "failed to allocate iov array"); memset(iov, 0, sizeof(*iov)); void** src_array = malloc(sizeof(void*) * iov_len); EXPR_CHKANDJUMP(src_array, "failed to allocate src array"); void** dst_array = malloc(sizeof(void*) * iov_len); EXPR_CHKANDJUMP(dst_array, "failed to allocate dst array"); strided_context_t context = { .datatype = datatype, .scale = scale, .src = src, .dst = dst, .count = count, .proc = proc, .group = group, .ops = 0, .cur_iov_idx = 0, .src_array = src_array, .dst_array = dst_array, .iov = iov, .iov_len = iov_len }; COMEX_CHKANDJUMP(list_strides(src_stride, dst_stride, count, stride_levels, add_to_iov_callback, &context), "failed to list strides"); COMEX_CHKANDJUMP(iov_acc(datatype, scale, proc, group, iov, iov_len, handle), "failed to perform iov_acc"); fn_success: ret = COMEX_SUCCESS; goto fn_clean; fn_fail: ret = COMEX_FAILURE; goto fn_clean; fn_clean: if (iov) free(iov); if (src_array) free(src_array); if (dst_array) free(dst_array); return ret; } static int nb_getputv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* handle, int is_get_op) { int i; int j; int count = 0; struct fi_msg_rma* msg = 0; VALIDATE_GROUP_AND_PROC(group, proc); if (handle) *handle = HANDLE_UNDEFINED; /* calculate common count of iov elements */ for (i = 0; i < iov_len; i++) count += iov[i].count; /* no data to process? just exit */ if (!count) goto fn_success; request_t* parent_req = alloc_request(); EXPR_CHKANDJUMP(parent_req, "failed to allocate parent request"); parent_req->group = group; parent_req->proc = proc; /* lock request - deny to complete */ increment_request_cnt(parent_req); /* create non-blocking requests */ for (i = 0; i < iov_len; i++) { for (j = 0; j < iov[i].count; j++) { if (!msg) { msg = malloc(sizeof(*msg)); EXPR_CHKANDJUMP(msg, "failed to allocate iov message"); memset(msg, 0, sizeof(*msg)); msg->msg_iov = malloc(ofi_data.rma_iov_limit * sizeof(*msg->msg_iov)); EXPR_CHKANDJUMP(msg->msg_iov, "failed to allocate msg iov"); msg->rma_iov = malloc(ofi_data.rma_iov_limit * sizeof(*msg->rma_iov)); EXPR_CHKANDJUMP(msg->rma_iov, "failed to allocate rma iov"); request_t* child_req = alloc_request(); EXPR_CHKANDJUMP(child_req, "failed to allocate child request"); set_parent_request(parent_req, child_req); msg->context = child_req; child_req->data = msg; child_req->cmpl = complete_getput; } ofi_window_t* wnd = 0; void* raw_remote_addr = (is_get_op ? iov[i].src[j] : iov[i].dst[j]); int iov_len = iov[i].bytes; COMEX_CHKANDJUMP(lookup_window(raw_remote_addr, iov_len, proc, group, &wnd), "failed to lookup window"); assert(wnd); assert(msg); struct iovec* iovelem = (struct iovec*)msg->msg_iov + msg->iov_count; struct fi_rma_iov* rmaelem = (struct fi_rma_iov*)msg->rma_iov + msg->rma_iov_count; iovelem->iov_base = is_get_op ? iov[i].dst[j] : iov[i].src[j]; rmaelem->addr = get_remote_addr(wnd->ptr, raw_remote_addr, ot_rma); iovelem->iov_len = iov_len; rmaelem->len = iov_len; rmaelem->key = wnd->key_rma; msg->addr = wnd->peer_rma->fi_addr; assert((uint64_t)raw_remote_addr >= wnd->ptr); assert(rmaelem->len > 0); assert((uint64_t)raw_remote_addr + rmaelem->len <= wnd->ptr + wnd->size); msg->iov_count++; msg->rma_iov_count++; assert(msg->iov_count == msg->rma_iov_count); if (msg->iov_count >= ofi_data.rma_iov_limit) { REG_LOCAL_MR(msg, (request_t*)msg->context, ot_rma); if (is_get_op) GET_MSG(ofi_data.ep_rma.endpoint, msg); else PUT_MSG(ofi_data.ep_rma.endpoint, msg); FREE_DESC(msg); msg = 0; } } } if (msg) { REG_LOCAL_MR(msg, (request_t*)msg->context, ot_rma); if (is_get_op) GET_MSG(ofi_data.ep_rma.endpoint, msg); else PUT_MSG(ofi_data.ep_rma.endpoint, msg); FREE_DESC(msg); msg = 0; } if(!handle) parent_req->flags |= rf_auto_free; else *handle = parent_req->index; decrement_request_cnt(parent_req); fn_success: if (env_data.force_sync) { if (parent_req) wait_request(parent_req); if (handle) *handle = HANDLE_UNDEFINED; } return COMEX_SUCCESS; fn_fail: if (msg) { /* in case if context defined - them msg will be destroyed */ /* by request completion callback */ if (msg->context) { complete_request((request_t*)msg->context); free_request((request_t*)msg->context); } else { if (msg->msg_iov) free((void*)msg->msg_iov); if (msg->rma_iov) free((void*)msg->rma_iov); free(msg); } } if (parent_req) free_request(parent_req); return COMEX_FAILURE; } int comex_nbputv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* handle) { COMEX_CHKANDJUMP(nb_getputv(iov, iov_len, proc, group, handle, 0), "failed to perform nb_getputv"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_nbgetv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* handle) { COMEX_CHKANDJUMP(nb_getputv(iov, iov_len, proc, group, handle, 1), "failed to perform nb_getputv"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static int comex_nbaccv_native( int datatype, void* scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* handle) { COMEX_CHKANDJUMP(iov_acc(datatype, scale, proc, group, iov, iov_len, handle), "failed to perform iov_acc"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_acc( int datatype, void* scale, void* src_ptr, void* dst_ptr, int bytes, int proc, comex_group_t group) { return comex_acc_f(datatype, scale, src_ptr, dst_ptr, bytes, proc, group); } int comex_accs( int datatype, void* scale, void* src_ptr, int* src_stride_ar, void* dst_ptr, int* dst_stride_ar, int* count, int stride_levels, int proc, comex_group_t group) { return comex_accs_f(datatype, scale, src_ptr, src_stride_ar, dst_ptr, dst_stride_ar, count, stride_levels, proc, group); } int comex_accv( int datatype, void* scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { return comex_accv_f(datatype, scale, iov, iov_len, proc, group); } int comex_nbacc( int datatype, void* scale, void* src_ptr, void* dst_ptr, int bytes, int proc, comex_group_t group, comex_request_t *handle) { return comex_nbacc_f(datatype, scale, src_ptr, dst_ptr, bytes, proc, group, handle); } int comex_nbaccs( int datatype, void* scale, void* src, int* src_stride, void* dst, int* dst_stride, int* count, int stride_levels, int proc, comex_group_t group, comex_request_t *handle) { return comex_nbaccs_f(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, proc, group, handle); } int comex_nbaccv( int datatype, void* scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* handle) { return comex_nbaccv_f(datatype, scale, iov, iov_len, proc, group, handle); } /* * ploc - pointer to fetch data (and in case of SWAP also pointer to src) prem - pointer to dst */ int comex_rmw( int op, void* ploc, void* prem, int extra, int proc, comex_group_t group) { request_t request; init_request(&request); ofi_window_t* window; COMEX_CHKANDJUMP(lookup_window(prem, sizeof(extra), proc, group, &window), "failed to lookup window"); assert(window); if (IS_TARGET_ATOMICS_EMULATION()) { ofi_atomics_t header = { .rmw = { .proto.proc = l_state.proc, .proto.op = op, .proto.tag = GETTAG(), .src = (op == COMEX_SWAP) ? *(int*)ploc : (op == COMEX_SWAP_LONG) ? *(uint64_t*)ploc : 0, .extra = extra, .addr = (uint64_t)prem } }; OFI_RETRY(fi_tinject(ofi_data.ep_tagged.endpoint, &header, sizeof(header), window->peer_tagged->fi_addr, ATOMICS_PROTO_TAGMASK), "failed to send tagged:"); OFI_RETRY(fi_trecv(ofi_data.ep_tagged.endpoint, ploc, (op == COMEX_FETCH_AND_ADD || op == COMEX_SWAP) ? sizeof(int) : sizeof(uint64_t), 0, window->peer_tagged->fi_addr, ATOMICS_DATA_TAGMASK | header.proto.tag, 0, &request), "failed to send tagged:"); COMEX_CHKANDJUMP(wait_request(&request), "failed to wait request"); } else { union atomics_param_t { int int_value; long long_value; } param; switch (op) { case COMEX_FETCH_AND_ADD: { OFI_RETRY(fi_fetch_atomic(ofi_data.ep_atomics.endpoint, &extra, 1, 0, ploc, 0, window->peer_atomics->fi_addr, get_remote_addr(window->ptr, prem, ot_atomic), window->key_atomics, FI_INT32, FI_SUM, &request), "fi_fetch_atomic error:"); } break; case COMEX_FETCH_AND_ADD_LONG: { param.long_value = (long)extra; OFI_RETRY(fi_fetch_atomic(ofi_data.ep_atomics.endpoint, &(param.long_value), 1, 0, ploc, 0, window->peer_atomics->fi_addr, get_remote_addr(window->ptr, prem, ot_atomic), window->key_atomics, FI_INT64, FI_SUM, &request), "fi_fetch_atomic error:"); } break; case COMEX_SWAP: { param.int_value = *(int*)ploc; OFI_RETRY(fi_fetch_atomic(ofi_data.ep_atomics.endpoint, &(param.int_value), 1, 0, ploc, 0, window->peer_atomics->fi_addr, get_remote_addr(window->ptr, prem, ot_atomic), window->key_atomics, FI_INT32, FI_ATOMIC_WRITE, &request), "fi_fetch_atomic error:"); } break; case COMEX_SWAP_LONG: { param.long_value = *(long*)ploc; OFI_RETRY(fi_fetch_atomic(ofi_data.ep_atomics.endpoint, &(param.long_value), 1, 0, ploc, 0, window->peer_atomics->fi_addr, get_remote_addr(window->ptr, prem, ot_atomic), window->key_atomics, FI_INT64, FI_ATOMIC_WRITE, &request), "fi_fetch_atomic error"); } break; default: EXPR_CHKANDJUMP(0, "incorrect rmw operation type"); break; } COMEX_CHKANDJUMP(wait_request(&request), "failed to wait request"); } fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } /* Mutex Operations */ static int create_mutexes(mutex_t** mtx, int num) { typedef struct mutex_cnt_t { int proc; int count; } mutex_cnt_t; mutex_cnt_t* global = 0; mcs_mutex_t* unsorted = 0; mutex_t* mutex = 0; assert(!*mtx); /* calculate total number of mutexes */ mutex_cnt_t info = {.proc = l_state.proc, .count = num}; COMEX_CHKANDJUMP(exchange_with_all(&info, sizeof(info), COMEX_GROUP_WORLD, (void**)&global), "failed to publish mutex info"); int group_size = l_state.size; int i; int count = 0; for (i = 0; i < group_size; i++) count += global[i].count; mutex = malloc(sizeof(*mutex)); EXPR_CHKANDJUMP(mutex, "failed to allocate mutex object"); memset(mutex, 0, sizeof(*mutex)); mutex->elem_offset = malloc(group_size * sizeof(*mutex->elem_offset)); mutex->mcs_mutex = malloc(group_size * sizeof(*mutex->mcs_mutex)); EXPR_CHKANDJUMP(mutex->elem_offset && mutex->mcs_mutex, "failed to allocate mutex object data"); memset(mutex->mcs_mutex, 0, group_size * sizeof(*mutex->mcs_mutex)); /* allocate data for local mutex & register it in OFI mr */ /* total number of elements to allocate is count + num: * count - total number of mutexes, * num - number of mutexes on current proc. * allocated 'count' elements for elem + 'num' elements for * tail. * add data is allocated as solid array */ mcs_mutex_t local_mutex = { .proc = l_state.proc, .count = num, .key = 0, .tail = 0, .elem = 0 }; /*mcs_mutex_t local_mutex = {.proc = l_state.proc, .count = num, .key = 0, .tail = 0, .elem_idx = 0};*/ size_t buflen = (count + num) * sizeof(*local_mutex.tail); mutex->data = malloc(buflen); local_mutex.tail = mutex->data; local_mutex.elem = local_mutex.tail + num; /*local_mutex.elem_idx = num;*/ EXPR_CHKANDJUMP(mutex->data, "failed to allocate mutex object shared data"); /* set default values to PROC_NONE (0 could not be used because 0 is valid proc value) */ for (i = 0; i < count + num; i++) local_mutex.tail[i] = PROC_NONE; struct fi_context context; COMEX_CHKANDJUMP(mr_reg(mutex->data, buflen, MR_ACCESS_PERMISSIONS, &mutex->mr, &context, ot_atomic), "failed to register memory:"); OFI_CALL(local_mutex.key, fi_mr_key(mutex->mr)); /*MUTEX_MR_KEY(mutex->mr, local_mutex.key);*/ /* data allocated, let's publish it to other procs */ COMEX_CHKANDJUMP(exchange_with_all(&local_mutex, sizeof(local_mutex), COMEX_GROUP_WORLD, (void**)&unsorted), "failed to publish mutex data"); /* sort data by proc for better performance */ for (i = 0; i < group_size; i++) { assert(unsorted[i].proc < group_size); mutex->mcs_mutex[unsorted[i].proc] = unsorted[i]; } /* ok, now calculate offset of elem item for every proc */ int offset = 0; for (i = 0; i < group_size; i++) { mutex->elem_offset[i] = offset; offset += mutex->mcs_mutex[i].count; } *mtx = mutex; fn_success: if (global) free(global); if (unsorted) free(unsorted); return COMEX_SUCCESS; fn_fail: if (global) free(global); if (mutex) { if (mutex->mr) OFI_VCALL(fi_close((struct fid*)(mutex->mr))); if (mutex->elem_offset) free(mutex->elem_offset); if (mutex->mcs_mutex) free(mutex->mcs_mutex); free(mutex); mutex = 0; } if (unsorted) free(unsorted); return COMEX_FAILURE; } static int destroy_mutexes(mutex_t* mutex) { assert(mutex); COMEX_CHKANDJUMP(comex_barrier(COMEX_GROUP_WORLD), "failed to barrier"); COMEX_CHKANDJUMP(mr_unreg(&(mutex->mr->fid)), "fi_close mutex memory region:"); free(mutex->data); free(mutex->elem_offset); free(mutex->mcs_mutex); free(mutex); mutex = 0; fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static int lock_mutex(mutex_t* mutex, int mtx, int proc) { assert(mutex); assert(mtx < mutex->mcs_mutex[proc].count); int my_proc = l_state.proc; int elem_index = ELEM_INDEX(mutex, proc, mtx); void* buf = 0; /* reset 'elem' element of current proc * using direct access because writing to this value is not * concurrent with remote write */ mutex->mcs_mutex[my_proc].elem[elem_index] = PROC_NONE; /*MUTEX_ELEM(mutex->mcs_mutex[my_proc], elem_index) = PROC_NONE;*/ request_t request; init_request(&request); /* trying to lock mutex */ int prev = PROC_NONE; OFI_RETRY(fi_fetch_atomic(ofi_data.ep_atomics.endpoint, &my_proc, 1, 0, &prev, 0, ofi_data.ep_atomics.peers[proc].fi_addr, get_remote_addr((uint64_t)mutex->mcs_mutex[proc].tail, mutex->mcs_mutex[proc].tail + mtx, ot_atomic), mutex->mcs_mutex[proc].key, FI_INT32, FI_ATOMIC_WRITE, &request), "failed to process atomic ops:"); WAIT_COMPLETION_AND_RESET(&request); /*MUTEX_FOP_ATOMIC(mutex, proc, &my_proc, &prev, mtx, MUTEX_OP_WRITE);*/ if (prev != PROC_NONE) { /* mutex was locked by another proc. write to prev's 'elem' object current proc & wait for notification from it */ OFI_RETRY(fi_atomic(ofi_data.ep_atomics.endpoint, &my_proc, 1, 0, ofi_data.ep_atomics.peers[prev].fi_addr, get_remote_addr((uint64_t)mutex->mcs_mutex[prev].tail, mutex->mcs_mutex[prev].elem + elem_index, ot_atomic), mutex->mcs_mutex[prev].key, FI_INT32, FI_ATOMIC_WRITE, &request), "failed to process atomic ops:"); WAIT_COMPLETION_AND_RESET(&request); int _buf; buf = ofi_data.msg_prefix_size ? malloc(sizeof(int) + ofi_data.msg_prefix_size) : &_buf; OFI_RETRY(fi_trecv(ofi_data.ep_tagged.endpoint, buf, sizeof(int) + ofi_data.msg_prefix_size, 0, ofi_data.ep_tagged.peers[prev].fi_addr, mutex->tagmask | mtx, 0, &request), "failed to receive tagged:"); WAIT_COMPLETION_AND_RESET(&request); if (ofi_data.msg_prefix_size) free(buf); } fn_success: return COMEX_SUCCESS; fn_fail: if (buf) free(buf); return COMEX_FAILURE; } static int unlock_mutex(mutex_t* mutex, int mtx, int proc) { assert(mutex); assert(mtx < mutex->mcs_mutex[proc].count); int elem_index = ELEM_INDEX(mutex, proc, mtx); int my_proc = l_state.proc; request_t request; init_request(&request); /* trying to lock mutex */ int next = PROC_NONE; /* read local 'next' value using atomic to prevent condition races: * same value may be processed by remote host at same time */ OFI_RETRY(fi_fetch_atomic(ofi_data.ep_atomics.endpoint, &my_proc, 1, 0, &next, 0, ofi_data.ep_atomics.peers[my_proc].fi_addr, get_remote_addr((uint64_t)mutex->mcs_mutex[my_proc].tail, mutex->mcs_mutex[my_proc].elem + elem_index, ot_atomic), mutex->mcs_mutex[my_proc].key, FI_INT32, FI_ATOMIC_READ, &request), "failed to process atomic ops:"); WAIT_COMPLETION_AND_RESET(&request); /*MUTEX_READ(mutex, my_proc, &next, MUTEX_ELEM_IDX(mutex->mcs_mutex[my_proc], elem_index));*/ if (next == PROC_NONE) { /* check if somebody is waiting for mutex unlock */ int no_proc = PROC_NONE; int tail = PROC_NONE; OFI_RETRY(fi_compare_atomic(ofi_data.ep_atomics.endpoint, &no_proc, 1, 0, &my_proc, 0, &tail, 0, ofi_data.ep_atomics.peers[proc].fi_addr, get_remote_addr((uint64_t)mutex->mcs_mutex[proc].tail, mutex->mcs_mutex[proc].tail + mtx, ot_atomic), mutex->mcs_mutex[proc].key, FI_INT32, FI_CSWAP, &request), "failed to process atomic ops:"); WAIT_COMPLETION_AND_RESET(&request); /*MUTEX_CSWAP(mutex, proc, &no_proc, &my_proc, &tail, mtx);*/ if (tail != my_proc) { /*printf("[%d] mutex tailed by: %d\n", l_state.proc, tail);*/ /*printf("[%d] reading from %d -> %d\n", l_state.proc, my_proc, MUTEX_ELEM_IDX(mutex->mcs_mutex[my_proc], elem_index));*/ while (next == PROC_NONE) { OFI_RETRY(fi_fetch_atomic(ofi_data.ep_atomics.endpoint, &my_proc, 1, 0, &next, 0, ofi_data.ep_atomics.peers[my_proc].fi_addr, get_remote_addr((uint64_t)mutex->mcs_mutex[my_proc].tail, mutex->mcs_mutex[my_proc].elem + elem_index, ot_atomic), mutex->mcs_mutex[my_proc].key, FI_INT32, FI_ATOMIC_READ, &request), "failed to process atomic ops:"); WAIT_COMPLETION_AND_RESET(&request); /*MUTEX_READ(mutex, my_proc, &next, MUTEX_ELEM_IDX(mutex->mcs_mutex[my_proc], elem_index));*/ } /*printf("[%d] mutex next to: %d\n", l_state.proc, next);*/ } } if (next != PROC_NONE) { int _data; void* data = ofi_data.msg_prefix_size ? malloc(sizeof(int) + ofi_data.msg_prefix_size) : &_data; OFI_RETRY(fi_tinject(ofi_data.ep_tagged.endpoint, data, sizeof(int) + ofi_data.msg_prefix_size, ofi_data.ep_tagged.peers[next].fi_addr, mutex->tagmask | mtx), "failed to send tagged:"); if (ofi_data.msg_prefix_size) free(data); /* do not wait for operation completed, use dtor callback to clean buffer instead */ } fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } /* AM mutex is mutex implemented on holder side (active messages) * used same queue for wait mutexes, assuming that rank could not * wait for 2 different mutexes at same time */ static int create_am_mutexes(int num) { int i; am_mutex_locked = (int*)malloc(num * sizeof(*am_mutex_locked)); EXPR_CHKANDJUMP(am_mutex_locked, "failed to allocate mutex"); am_mutex_waiter = (int*)malloc(l_state.size * sizeof(*am_mutex_waiter)); EXPR_CHKANDJUMP(am_mutex_waiter, "failed to allocate mutex"); for(i = 0; i < num; i++) am_mutex_locked[i] = PROC_NONE; for(i = 0; i < l_state.size; i++) am_mutex_waiter[i] = PROC_NONE; comex_barrier(COMEX_GROUP_WORLD); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static int destroy_am_mutexes() { EXPR_CHKANDJUMP(am_mutex_locked, "mutex is not allocated"); EXPR_CHKANDJUMP(am_mutex_waiter, "mutex is not allocated"); comex_barrier(COMEX_GROUP_WORLD); free(am_mutex_locked); free(am_mutex_waiter); am_mutex_locked = am_mutex_waiter = 0; fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static int lock_am_mutex(int num, int proc) { request_t request; init_request(&request); ofi_atomics_t header = { .mutex = { .proto.proc = l_state.proc, .proto.op = OFI_MUTEX_AM_LOCK, .num = num } }; OFI_RETRY(fi_tinject(ofi_data.ep_tagged.endpoint, &header, sizeof(header), ofi_data.ep_tagged.peers[proc].fi_addr, ATOMICS_PROTO_TAGMASK), "failed to send tagged:"); int v; OFI_RETRY(fi_trecv(ofi_data.ep_tagged.endpoint, &v, sizeof(v), 0, ofi_data.ep_tagged.peers[proc].fi_addr, ATOMICS_MUTEX_TAGMASK | l_state.proc, 0, &request), "failed to send tagged:"); COMEX_CHKANDJUMP(wait_request(&request), "failed to wait request"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static int unlock_am_mutex(int num, int proc) { ofi_atomics_t header = { .mutex = { .proto.proc = l_state.proc, .proto.op = OFI_MUTEX_AM_UNLOCK, .num = num } }; OFI_RETRY(fi_tinject(ofi_data.ep_tagged.endpoint, &header, sizeof(header), ofi_data.ep_tagged.peers[proc].fi_addr, ATOMICS_PROTO_TAGMASK), "failed to send tagged:"); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_lock(int mtx, int proc) { if (IS_TARGET_ATOMICS_EMULATION()) return lock_am_mutex(mtx, proc); else return lock_mutex(global_mutex, mtx, proc); } int comex_unlock(int mtx, int proc) { if (IS_TARGET_ATOMICS_EMULATION()) return unlock_am_mutex(mtx, proc); else return unlock_mutex(global_mutex, mtx, proc); } int comex_create_mutexes(int num) { if (IS_TARGET_ATOMICS_EMULATION()) COMEX_CHKANDJUMP(create_am_mutexes(num), "failed to create mutexes"); else { COMEX_CHKANDJUMP(create_mutexes(&global_mutex, num), "failed to create mutexes"); assert(global_mutex); global_mutex->tagmask = MUTEX_TAGMASK; } fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_destroy_mutexes() { if (IS_TARGET_ATOMICS_EMULATION()) COMEX_CHKANDJUMP(destroy_am_mutexes(), "failed to destroy mutexes"); else { COMEX_CHKANDJUMP(destroy_mutexes(global_mutex), "failed to destroy mutexes"); global_mutex = 0; } fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_malloc(void* ptrs[], size_t size, comex_group_t group) { typedef struct wnd_data_t { uint64_t key_rma; uint64_t key_atomics; uint64_t ptr; size_t size; int local_proc; /* proc in current group 'group' */ } wnd_data_t; wnd_data_t* all_wnd_data = 0; local_window_t* l_wnd = malloc(sizeof(*l_wnd)); EXPR_CHKANDJUMP(l_wnd, "failed to allocate local window"); memset(l_wnd, 0, sizeof(*l_wnd)); if (size) { l_wnd->ptr = comex_malloc_local(size); EXPR_CHKANDJUMP(l_wnd->ptr, "failed to allocate local window buffer"); struct fi_context context; COMEX_CHKANDJUMP(mr_reg(l_wnd->ptr, size, MR_ACCESS_PERMISSIONS, &l_wnd->mr_rma, &context, ot_rma), "failed to register memory:"); if (dual_provider()) { COMEX_CHKANDJUMP(mr_reg(l_wnd->ptr, size, MR_ACCESS_PERMISSIONS, &l_wnd->mr_atomics, &context, ot_atomic), "failed to register memory:"); } else l_wnd->mr_atomics = l_wnd->mr_rma; } wnd_data_t my_wnd_data = { .ptr = (uint64_t)l_wnd->ptr, .size = size, .local_proc = 0 }; if (size) { OFI_CALL(my_wnd_data.key_rma, fi_mr_key(l_wnd->mr_rma)); OFI_CALL(my_wnd_data.key_atomics, fi_mr_key(l_wnd->mr_atomics)); } comex_group_rank(group, &my_wnd_data.local_proc); COMEX_CHKANDJUMP(exchange_with_all(&my_wnd_data, sizeof(wnd_data_t), group, (void**)&all_wnd_data), "failed to exchange memory regions"); int group_size = 0; comex_group_size(group, &group_size); /* ok, we have locally created window, also we have info about all other windows in group*/ /* let's create appropriate structures */ /* create local window structure */ INSERT_TO_LIST(local_wnd, l_wnd); /* create remote windows structures */ int wnd_idx; for (wnd_idx = 0; wnd_idx < group_size; wnd_idx++) { wnd_data_t* wnd_data = all_wnd_data + wnd_idx; int world_proc = PROC_NONE; comex_group_translate_world(group, wnd_data->local_proc, &world_proc); EXPR_CHKANDJUMP((world_proc != PROC_NONE), "invalid world proc"); ofi_window_t* remote_wnd = malloc(sizeof(*remote_wnd)); EXPR_CHKANDJUMP(remote_wnd, "failed to allocate data for remote window"); remote_wnd->local = l_wnd; remote_wnd->peer_rma = ofi_data.ep_rma.peers + world_proc; remote_wnd->peer_atomics = ofi_data.ep_atomics.peers + world_proc; remote_wnd->world_proc = world_proc; remote_wnd->key_rma = wnd_data->key_rma; remote_wnd->key_atomics = wnd_data->key_atomics; remote_wnd->ptr = wnd_data->ptr; remote_wnd->size = wnd_data->size; INSERT_TO_LIST(ofi_wnd, remote_wnd); ptrs[wnd_data->local_proc] = (void*)wnd_data->ptr; } comex_barrier(group); fn_success: if (all_wnd_data) free(all_wnd_data); return COMEX_SUCCESS; fn_fail: if (l_wnd) { REMOVE_FROM_LIST(local_wnd, l_wnd, local_window_t); if (l_wnd->ptr) free(l_wnd->ptr); if (l_wnd->mr_rma) OFI_VCALL(fi_close((struct fid*)l_wnd->mr_rma)); if (l_wnd->mr_atomics) OFI_VCALL(fi_close((struct fid*)l_wnd->mr_atomics)); free(l_wnd); } if (all_wnd_data) free(all_wnd_data); return COMEX_FAILURE; } int comex_malloc_mem_dev(void *ptrs[], size_t size, comex_group_t group, const char* device) { return comex_malloc(ptrs,size,group); } int comex_free(void* ptr, comex_group_t group) { COMEX_CHKANDJUMP(comex_barrier(group), "failed to barrier"); local_window_t* local = local_wnd; while (local) { if (ptr == local->ptr) break; local = local->next; } EXPR_CHKANDJUMP(local, "failed to locate local window"); ofi_window_t* ofi = ofi_wnd; while (ofi) { if (ofi->local == local) { ofi_window_t* to_remove = ofi; ofi = ofi->next; REMOVE_FROM_LIST(ofi_wnd, to_remove, ofi_window_t); if (to_remove == ofi_wnd_cache) ofi_wnd_cache = 0; free(to_remove); } else ofi = ofi->next; } if (ptr) { int ret; COMEX_CHKANDJUMP(mr_unreg(&local->mr_rma->fid), "fi_close memory region:"); if (dual_provider()) COMEX_CHKANDJUMP(mr_unreg(&local->mr_atomics->fid), "fi_close memory region:"); comex_free_local(ptr); REMOVE_FROM_LIST(local_wnd, local, local_window_t); free(local); } fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } int comex_free_dev(void *ptr, comex_group_t group) { return comex_free(ptr, group); } static inline int destroy_all_windows() { local_window_t* local = local_wnd; while (local) { COMEX_CHKANDJUMP(comex_free(local->ptr, 0), "failed to free window"); local = local->next; } fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static void acquire_remote_lock(int proc) { assert(!IS_TARGET_ATOMICS_EMULATION()); lock_mutex(local_mutex, 0, proc); } static void release_remote_lock(int proc) { assert(!IS_TARGET_ATOMICS_EMULATION()); unlock_mutex(local_mutex, 0, proc); } #ifdef USE_ATOMIC_EMULATION #define CALC(_dst, _src, _scale, len, type) \ do \ { \ int i; \ int cnt = len / sizeof(type); \ type* src = (type*)(_src); \ type* dst = (type*)(_dst); \ type scl = *(type*)(_scale); \ for (i = 0; i < cnt; i++, src++, dst++) \ *dst = (*src) * scl; \ } while(0) static inline void acc_emu( int datatype, int count, void* get_buf, void* src_ptr, size_t src_idx, void* scale); static int comex_nbaccs_emu( int datatype, void* scale, void* src_ptr, int* src_stride_ar, void* dst_ptr, int* dst_stride_ar, int* count, int stride_levels, int proc, comex_group_t group, comex_request_t* comex_request) { assert(env_data.native_atomics == 0); assert(env_data.emulation_type == et_origin || env_data.emulation_type == et_target); int i, j; size_t src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ size_t src_bvalue[7], src_bunit[7]; size_t dst_bvalue[7], dst_bunit[7]; void* get_buf = 0; int result = COMEX_SUCCESS; int world_proc = PROC_NONE; void* desc = 0; int chunk = 0; request_t* request = 0; if (comex_request) *comex_request = HANDLE_UNDEFINED; /* number of n-element of the first dimension */ n1dim = 1; for (i = 1; i <= stride_levels; i++) n1dim *= count[i]; ofi_atomics_t header = { .acc = { .proto.proc = l_state.proc, .proto.op = datatype, .proto.tag = GETTAG(), .count = n1dim, .len = count[0], .posted = 0 } }; /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for (i = 2; i <= stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i - 1] * count[i - 1]; dst_bunit[i] = dst_bunit[i - 1] * count[i - 1]; } if (env_data.emulation_type == et_origin && 0 == skip_lock) { // grab the atomics lock acquire_remote_lock(proc); } if (env_data.emulation_type == et_origin) { /* use common buffer to acc */ get_buf = (char *)malloc(sizeof(char) * count[0]); EXPR_CHKANDJUMP(get_buf, "failed to allocate memory\n"); } else if (env_data.emulation_type == et_target) { /* use p2p way for atomics: send header to allow pre-post request */ VALIDATE_GROUP_AND_PROC(group, proc); comex_group_translate_world(group, proc, &world_proc); EXPR_CHKANDJUMP((world_proc != PROC_NONE), "invalid world proc"); OFI_RETRY(fi_tinject(ofi_data.ep_tagged.endpoint, &header, sizeof(header), ofi_data.ep_tagged.peers[world_proc].fi_addr, ATOMICS_PROTO_TAGMASK), "failed to send tagged:"); /* allocate buffer for all acc data & register it */ chunk = sizeof(ofi_atomics_t) + (sizeof(char) * count[0]); int total = chunk * n1dim; request = alloc_request(); EXPR_CHKANDJUMP(request, "failed to allocate request"); request->group = group; request->proc = proc; request->dtor = req_dtor; increment_request_cnt(request); if (comex_request) *comex_request = request->index; else request->flags |= rf_auto_free; request_t* complete = alloc_request(); EXPR_CHKANDJUMP(complete, "failed to allocate request"); set_parent_request(request, complete); OFI_RETRY(fi_trecv(ofi_data.ep_tagged.endpoint, &complete->inplace, sizeof(complete->inplace), 0, ofi_data.ep_tagged.peers[world_proc].fi_addr, ATOMICS_ACC_CMPL_TAGMASK | header.proto.tag, 0, complete), "failed to send tagged:"); if (chunk > ofi_data.max_buffered_send) { /* chunk used in comaring because in case if chunk is small data is sent by inject */ get_buf = (char *)malloc(total); request->data = get_buf; } else { /* if chunk is small - use same buffer for all packets (use inject call) */ get_buf = (char *)malloc(chunk); } EXPR_CHKANDJUMP(get_buf, "failed to allocate memory\n"); } for (i = 0; i < n1dim; i++) { src_idx = 0; for (j = 1; j <= stride_levels; j++) { src_idx += src_bvalue[j] * src_stride_ar[j - 1]; if ((i + 1) % src_bunit[j] == 0) { src_bvalue[j]++; } if (src_bvalue[j] > (count[j] - 1)) { src_bvalue[j] = 0; } } dst_idx = 0; for(j = 1; j <= stride_levels; j++) { dst_idx += dst_bvalue[j] * dst_stride_ar[j - 1]; if ((i + 1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j] - 1)) { dst_bvalue[j] = 0; } } if (env_data.emulation_type == et_origin) { // Get the remote data in a temp buffer COMEX_CHKANDJUMP(comex_get((char *)dst_ptr + dst_idx, get_buf, count[0], proc, group), "comex_accs_emu: failed to get data"); // Local accumulate acc_emu(datatype, count[0], get_buf, src_ptr, src_idx, scale); // Write back to remote data COMEX_CHKANDJUMP(comex_put(get_buf, (char *)dst_ptr + dst_idx, count[0], proc, group), "comex_accs_emu: failed to put data"); } else if (env_data.emulation_type == et_target) { /* use p2p way for atomics: send header to allow pre-post request */ ofi_atomics_t _hdr = { .acc = { .proto.proc = l_state.proc, .proto.op = datatype, .proto.tag = header.proto.tag, .len = count[0], .posted = count[0], .addr = (uint64_t)((char *)dst_ptr + dst_idx), .count = 0 } }; ofi_atomics_t* hdr = get_buf; *hdr = _hdr; switch (datatype) { case COMEX_ACC_DBL: CALC(hdr->acc.data, (char*)src_ptr + src_idx, scale, count[0], double); break; case COMEX_ACC_FLT: CALC(hdr->acc.data, (char*)src_ptr + src_idx, scale, count[0], float); break; case COMEX_ACC_INT: CALC(hdr->acc.data, (char*)src_ptr + src_idx, scale, count[0], int); break; case COMEX_ACC_LNG: CALC(hdr->acc.data, (char*)src_ptr + src_idx, scale, count[0], long); break; case COMEX_ACC_DCP: CALC(hdr->acc.data, (char*)src_ptr + src_idx, scale, count[0], double complex); break; case COMEX_ACC_CPL: CALC(hdr->acc.data, (char*)src_ptr + src_idx, scale, count[0], float complex); break; default: EXPR_CHKANDJUMP(0, "incorrect datatype: %d\n", datatype); break; } if (chunk > ofi_data.max_buffered_send) { request_t* child = alloc_request(); EXPR_CHKANDJUMP(child, "failed to allocate request"); set_parent_request(request, child); OFI_RETRY(fi_tsend(ofi_data.ep_tagged.endpoint, hdr, chunk, desc, ofi_data.ep_tagged.peers[world_proc].fi_addr, ATOMICS_ACC_DATA_TAGMASK | header.proto.tag, child), "failed to send tagged:"); get_buf += chunk; } else { OFI_RETRY(fi_tinject(ofi_data.ep_tagged.endpoint, hdr, chunk, ofi_data.ep_tagged.peers[world_proc].fi_addr, ATOMICS_ACC_DATA_TAGMASK | header.proto.tag), "failed to send tagged:"); } } } if (request) { decrement_request_cnt(request); if(request->data) get_buf = 0; /* buffer will be removed by request */ } if (env_data.emulation_type == et_origin && 0 == skip_lock) { // ungrab the lock release_remote_lock(proc); } fn_exit: if (get_buf) free(get_buf); if (env_data.force_sync) { if (request) wait_request(request); if (comex_request) *comex_request = HANDLE_UNDEFINED; } return result; fn_fail: result = COMEX_FAILURE; goto fn_exit; } static int comex_acc_emu( int datatype, void* scale, void* src_ptr, void* dst_ptr, int bytes, int proc, comex_group_t group) { return comex_accs_emu( datatype, scale, src_ptr, NULL, dst_ptr, NULL, &bytes, 0, proc, group); } static int comex_nbacc_emu( int datatype, void* scale, void* src_ptr, void* dst_ptr, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { return comex_nbaccs_emu( datatype, scale, src_ptr, NULL, dst_ptr, NULL, &bytes, 0, proc, group, hdl); } int comex_accs_emu( int datatype, void* scale, void* src, int* src_stride, void* dst, int* dst_stride, int* count, int stride_levels, int proc, comex_group_t group) { comex_request_t hdl; COMEX_CHKANDJUMP(comex_nbaccs_emu(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, proc, group, &hdl), "failed"); return comex_wait(&hdl); fn_fail: return COMEX_FAILURE; } int comex_accv_emu( int datatype, void* scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { comex_request_t hdl; COMEX_CHKANDJUMP(comex_nbaccv_emu(datatype, scale, iov, iov_len, proc, group, &hdl), "failed"); return comex_wait(&hdl); fn_fail: return COMEX_FAILURE; } static int comex_nbaccv_emu( int datatype, void* scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* hdl) { int i; int n; int result = COMEX_SUCCESS; comex_request_t* reqs = 0; if (hdl) *hdl = HANDLE_UNDEFINED; if (env_data.emulation_type == et_origin) { skip_lock = 1; acquire_remote_lock(proc); } int count = 0; for (i = 0; i < iov_len; ++i) count += iov[i].count; reqs = (comex_request_t*)malloc(count * sizeof(*reqs)); EXPR_CHKANDJUMP(reqs, "failed to llocate data"); for (i = 0, n = 0; i < iov_len; ++i) { int j; void** src = iov[i].src; void** dst = iov[i].dst; int bytes = iov[i].bytes; int limit = iov[i].count; for (j = 0; j < limit; ++j, n++) { comex_request_t h = HANDLE_UNDEFINED; COMEX_CHKANDJUMP(comex_nbacc_emu(datatype, scale, src[j], dst[j], bytes, proc, group, &reqs[n]), "comex_accv_emu: failed to acc"); } } for (i = 0; i < count; i++) comex_wait(&reqs[i]); fn_exit: if (reqs) free(reqs); if (env_data.emulation_type == et_origin) { skip_lock = 0; release_remote_lock(proc); } return result; fn_fail: result = COMEX_FAILURE; goto fn_exit; } static inline void acc_emu( int datatype, int count, void* get_buf, void* src_ptr, size_t src_idx, void* scale) { #define EQ_ONE_REG(A) ((A) == 1.0) #define EQ_ONE_CPL(A) ((A).real == 1.0 && (A).imag == 0.0) #define IADD_REG(A,B) (A) += (B) #define IADD_CPL(A,B) (A).real += (B).real; (A).imag += (B).imag #define IADD_SCALE_REG(A,B,C) (A) += (B) * (C) #define IADD_SCALE_CPL(A,B,C) (A).real += ((B).real*(C).real) - ((B).imag*(C).imag);\ (A).imag += ((B).real*(C).imag) + ((B).imag*(C).real); #define ACC(WHICH, COMEX_TYPE, C_TYPE) \ if (datatype == COMEX_TYPE) \ { \ int m; \ int m_lim = count / sizeof(C_TYPE); \ C_TYPE* iterator = (C_TYPE*)get_buf; \ C_TYPE* value = (C_TYPE*)((char*)src_ptr + src_idx); \ C_TYPE calc_scale = *(C_TYPE*)scale; \ if (EQ_ONE_##WHICH(calc_scale)) \ { \ for (m = 0 ; m < m_lim; ++m) \ { \ IADD_##WHICH(iterator[m], value[m]); \ } \ } \ else \ { \ for (m = 0 ; m < m_lim; ++m) \ { \ IADD_SCALE_##WHICH(iterator[m], value[m], calc_scale); \ } \ } \ } \ else ACC(REG, COMEX_ACC_DBL, double) ACC(REG, COMEX_ACC_FLT, float) ACC(REG, COMEX_ACC_INT, int) ACC(REG, COMEX_ACC_LNG, long) ACC(CPL, COMEX_ACC_DCP, DoubleComplex) ACC(CPL, COMEX_ACC_CPL, SingleComplex) { assert(0); } #undef ACC #undef EQ_ONE_REG #undef EQ_ONE_CPL #undef IADD_REG #undef IADD_CPL #undef IADD_SCALE_REG #undef IADD_SCALE_CPL } #endif /* USE_ATOMIC_EMULATION */ ga-5.9.2/comex/src-ofi/comex_impl.h000066400000000000000000000217641500715745200171200ustar00rootroot00000000000000/* * * Copyright (c) 2016 Intel Corporation. All rights reserved. */ #ifndef COMEX_IMPL_H_ #define COMEX_IMPL_H_ /* C and/or system headers */ #include #include #include /* 3rd party headers */ #include #include "rdma/fabric.h" /* our headers */ #include "request.h" #define HANDLE_UNDEFINED -1 #ifndef min # define min(a,b) \ ({ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a < _b ? _a : _b; }) #endif /* min */ #ifndef max # define max(a,b) \ ({ __typeof__ (a) _a = (a); \ __typeof__ (b) _b = (b); \ _a > _b ? _a : _b; }) #endif /* max */ #define unlikely(x_) __builtin_expect(!!(x_),0) #define likely(x_) __builtin_expect(!!(x_),1) #define INSERT_TO_LIST(root, item) \ do \ { \ item->next = root; \ root = item; \ } while (0) #define REMOVE_FROM_LIST(root, item, type) \ do \ { \ if (root == item) \ root = item->next; \ else \ { \ type* ptr = root; \ while (ptr) \ { \ if (ptr->next == item) \ { \ ptr->next = item->next; \ break; \ } \ else \ ptr = ptr->next; \ } \ } \ } while (0) #include "fi_lock.h" #define OFI_LOCK_INIT() \ do \ { \ fastlock_init(&poll_lock); \ fastlock_init(&mutex_lock); \ fastlock_init(&acc_lock); \ } while(0) #define OFI_LOCK_DESTROY() \ do \ { \ fastlock_destroy(&poll_lock); \ fastlock_destroy(&mutex_lock); \ fastlock_destroy(&acc_lock); \ } while(0) #define OFI_LOCK() fastlock_acquire(&poll_lock) #define OFI_TRYLOCK() (!fastlock_tryacquire(&poll_lock)) #define OFI_UNLOCK() fastlock_release(&poll_lock); #define OFI_CALL(ret, func) \ do \ { \ OFI_LOCK(); \ ret = func; \ OFI_UNLOCK(); \ } while(0) #define OFI_VCALL(func) \ do \ { \ OFI_LOCK(); \ func; \ OFI_UNLOCK(); \ } while(0) /* Logging macroses */ #define EXPR_CHKANDJUMP(ret, fmt, ...) \ do \ { \ if (unlikely(!(ret))) \ { \ err_printf("%s: " fmt, \ __FUNCTION__, ##__VA_ARGS__); \ goto fn_fail; \ } \ } while (0) #define OFI_FAILED() assert(0) #define COMEX_CHKANDJUMP(ret, fmt, ...) \ do \ { \ if (unlikely(ret != COMEX_SUCCESS)) \ { \ err_printf("(%u) %s:%d: " fmt, \ (unsigned)getpid(), \ __FUNCTION__, __LINE__, \ ##__VA_ARGS__); \ goto fn_fail; \ } \ } while (0) #define OFI_CHKANDJUMP(func, fmt, ...) \ do \ { \ ssize_t __ret; \ OFI_CALL(__ret, func); \ if (unlikely(__ret < 0)) \ { \ err_printf("(%u) %s: " fmt ": ret %d, error %s, ", \ (unsigned)getpid(), __FUNCTION__, \ ##__VA_ARGS__, __ret, \ STR_ERROR(&ld_table, __ret)); \ OFI_FAILED(); \ goto fn_fail; \ } \ } while (0) #define OFI_RETRY(func, ...) \ do \ { \ ssize_t _ret; \ do { \ OFI_CALL(_ret, func); \ if (likely(_ret == 0)) break; \ if (_ret != -FI_EAGAIN) \ OFI_CHKANDJUMP(_ret, __VA_ARGS__); \ myofi_poll(0); \ } while (_ret == -FI_EAGAIN); \ } while (0) #define MPI_CHKANDJUMP(ret, fmt, ...) \ do \ { \ if (unlikely(ret != MPI_SUCCESS)) \ { \ err_printf("(%u) %s: " fmt, \ (unsigned)getpid(), __FUNCTION__, \ ##__VA_ARGS__); \ goto fn_fail; \ } \ } while (0) #define PAUSE() \ do \ { \ if (env_data.progress_thread && \ !pthread_equal(pthread_self(), tid)) \ sched_yield(); \ } while(0) /*#define PAUSE() sched_yield()*/ static void err_printf(const char *fmt, ...) { va_list list; va_start(list, fmt); vfprintf(stderr, fmt, list); va_end(list); fprintf(stderr, "\n"); fflush(stderr); } /* Struct declaration */ typedef struct { MPI_Comm world_comm; int proc; int size; int local_proc; int local_size; } local_state; extern local_state l_state; typedef struct local_window_t { struct fid_mr* mr_rma; struct fid_mr* mr_atomics; void* ptr; struct local_window_t* next; } local_window_t; typedef struct ofi_window_t { int world_proc; uint64_t key_rma; uint64_t key_atomics; uint64_t ptr; /* remote address */ size_t size; /* size of remote buffer */ struct peer_t* peer_rma; struct peer_t* peer_atomics; struct local_window_t* local; struct ofi_window_t* next; } ofi_window_t; typedef struct strided_context_t { int datatype; void* scale; void* src; void* dst; int* count; int proc; comex_group_t group; struct request_t* request; int ops; int is_get_op; struct fi_msg_rma* msg; ofi_window_t* wnd; int cur_iov_idx; void** src_array; void** dst_array; comex_giov_t* iov; int iov_len; } strided_context_t; #define ATOMICS_PROTO_TAGMASK (779L << 32) #define ATOMICS_DATA_TAGMASK (780L << 32) #define ATOMICS_ACC_DATA_TAGMASK (781L << 32) #define ATOMICS_ACC_CMPL_TAGMASK (782L << 32) #define ATOMICS_MUTEX_TAGMASK (783L << 32) #define ATOMICS_PROTO_IGNOREMASK (0) /* struct used to store data required for fi_atomicmsg, used to free resources */ typedef struct acc_data_t { void* middle; struct fi_msg_atomic* msg; int proc; } acc_data_t; typedef enum op_type_t { ot_rma, ot_atomic } op_type_t; typedef enum emulation_type_t { et_origin = 0, et_target } emulation_type_t; typedef struct ofi_proto_t { int proc; int op; uint64_t tag; } ofi_proto_t; typedef struct ofi_rmw_t { ofi_proto_t proto; uint64_t src; int extra; uint64_t addr; } ofi_rmw_t; typedef struct ofi_acc_t { ofi_proto_t proto; int count; /* number of IOV elements expected */ uint64_t addr; /* target address to acc */ int len; /* length of every IOV element */ int posted; /* length of data in this packet */ char data[]; } ofi_acc_t; typedef struct ofi_mutex_t { ofi_proto_t proto; int num; } ofi_mutex_t; typedef union ofi_atomics_t { ofi_proto_t proto; ofi_rmw_t rmw; ofi_acc_t acc; ofi_mutex_t mutex; } ofi_atomics_t; #define OFI_MUTEX_AM_LOCK 1024 #define OFI_MUTEX_AM_UNLOCK 1025 #endif /* COMEX_IMPL_H_ */ ga-5.9.2/comex/src-ofi/datatype.h000066400000000000000000000070231500715745200165670ustar00rootroot00000000000000#ifndef DATATYPE_H_ #define DATATYPE_H_ #include "log.h" #define COMEX_DTYPES_COUNT (COMEX_ACC_LNG - COMEX_ACC_OFF) // comex datatype index starting from 0 #define COMEX_DTYPE_IDX(comex_dtype) (comex_dtype - COMEX_ACC_OFF - 1) enum fi_datatype comex_to_fi_dtype[COMEX_DTYPES_COUNT] = {FI_INT32, FI_DOUBLE, FI_FLOAT, FI_FLOAT_COMPLEX, FI_DOUBLE_COMPLEX, FI_INT64}; #define GET_FI_DTYPE(comex_dtype) \ (COMEX_DTYPE_IDX(comex_dtype) < COMEX_DTYPES_COUNT ? comex_to_fi_dtype[COMEX_DTYPE_IDX(comex_dtype)] : -1) #define COMEX_DTYPE_SIZEOF(comex_dtype, datasize) \ do \ { \ switch (comex_dtype) \ { \ case COMEX_ACC_INT: \ datasize = sizeof(int); \ break; \ case COMEX_ACC_DBL: \ datasize = sizeof(double); \ break; \ case COMEX_ACC_FLT: \ datasize = sizeof(float); \ break; \ case COMEX_ACC_LNG: \ datasize = sizeof(long); \ break; \ case COMEX_ACC_DCP: \ datasize = sizeof(DoubleComplex); \ break; \ case COMEX_ACC_CPL: \ datasize = sizeof(SingleComplex); \ break; \ default: \ COMEX_OFI_LOG(WARN, "incorrect comex_datatype: %d", \ comex_dtype); \ assert(0); \ goto fn_fail; \ break; \ } \ } while (0) /* needed for complex accumulate */ typedef struct { double real; double imag; } DoubleComplex; typedef struct { float real; float imag; } SingleComplex; static inline int scale_is_1(int datatype, void* scale) { if (!scale) return 1; switch (datatype) { case COMEX_ACC_INT: return *(int*)scale == 1; case COMEX_ACC_DBL: return *(double*)scale == 1.0; case COMEX_ACC_FLT: return *(float*)scale == 1.0f; case COMEX_ACC_LNG: return *(long*)scale == 1; case COMEX_ACC_DCP: return (((DoubleComplex*)scale)->real == 1.0 && ((DoubleComplex*)scale)->imag == 0.0); case COMEX_ACC_CPL: return (((SingleComplex*)scale)->real == 1.0f && ((SingleComplex*)scale)->imag == 0.0f); default: COMEX_OFI_LOG(WARN, "scale_is_1: incorrect data type: %d", datatype); assert(0); return 1; } } #endif /* DATATYPE_H_ */ ga-5.9.2/comex/src-ofi/env.c000066400000000000000000000070431500715745200155410ustar00rootroot00000000000000/* * * Copyright (c) 2016 Intel Corporation. All rights reserved. */ #include #include "comex.h" #include "comex_impl.h" #include "env.h" #include "log.h" #if defined(__APPLE__) && defined(__MACH__) #define DEFAULT_OFI_LIB "libfabric.dylib" #else #define DEFAULT_OFI_LIB "libfabric.so" #endif #define IS_SPACE(c) ((c==0x20 || c==0x09 || c==0x0a || c==0x0b || c==0x0c || c==0x0d) ? 8 : 0) env_data_t env_data = { ERROR, /* log_level */ 1, /* native_atomics */ et_target, /* emulation_type */ 1, /* progress_thread */ 8, /* cq_entries_count */ 0, /* force_sync */ NULL, /* provider */ NULL /* library_path */ }; void parse_env_vars() { env_to_int("COMEX_OFI_LOG_LEVEL", &(env_data.log_level)); env_to_int("COMEX_OFI_NATIVE_ATOMICS", &(env_data.native_atomics)); env_to_int("COMEX_OFI_ATOMICS_EMULATION_TYPE", &(env_data.emulation_type)); env_to_int("COMEX_OFI_PROGRESS_THREAD", &(env_data.progress_thread)); env_to_int("COMEX_OFI_CQ_ENTRIES_COUNT", &(env_data.cq_entries_count)); env_to_int("COMEX_OFI_FORCE_SYNC", &(env_data.force_sync)); env_data.provider = getenv("COMEX_OFI_PROVIDER"); env_data.library_path = getenv("COMEX_OFI_LIBRARY"); if (l_state.proc == 0) { COMEX_OFI_LOG(INFO, "COMEX_OFI_LOG_LEVEL: %d", env_data.log_level); COMEX_OFI_LOG(INFO, "COMEX_OFI_NATIVE_ATOMICS: %d", env_data.native_atomics); COMEX_OFI_LOG(INFO, "COMEX_OFI_ATOMICS_EMULATION_TYPE: %d", env_data.emulation_type); COMEX_OFI_LOG(INFO, "COMEX_OFI_PROGRESS_THREAD: %d", env_data.progress_thread); COMEX_OFI_LOG(INFO, "COMEX_OFI_CQ_ENTRIES_COUNT: %d", env_data.cq_entries_count); COMEX_OFI_LOG(INFO, "COMEX_OFI_FORCE_SYNC: %d", env_data.force_sync); COMEX_OFI_LOG(INFO, "COMEX_OFI_PROVIDER: %s", env_data.provider); COMEX_OFI_LOG(INFO, "COMEX_OFI_LIBRARY: %s", env_data.library_path); } assert(env_data.log_level >= ERROR && env_data.log_level <= TRACE); assert(env_data.native_atomics == 0 || env_data.native_atomics == 1); assert(env_data.emulation_type == et_origin || env_data.emulation_type == et_target); assert(env_data.progress_thread == 0 || env_data.progress_thread == 1); assert(env_data.cq_entries_count >= 1 && env_data.cq_entries_count <= 1024); assert(env_data.force_sync == 0 || env_data.force_sync == 1); if (env_data.native_atomics == 0 && env_data.emulation_type == et_target) { env_data.progress_thread = 1; if (l_state.proc == 0) COMEX_OFI_LOG(INFO, "enable progress thread to handle preposted requests in target atomics emulation"); } if (!env_data.library_path || *env_data.library_path == '\0') env_data.library_path = DEFAULT_OFI_LIB; } int env_to_int(const char* env_name, int* val) { const char* val_ptr; val_ptr = getenv(env_name); if (val_ptr) { const char* p; int sign = 1, value = 0; p = val_ptr; while (*p && IS_SPACE(*p)) p++; if (*p == '-') { p++; sign = -1; } if (*p == '+') p++; while (*p && isdigit(*p)) value = 10 * value + (*p++ - '0'); if (*p) { COMEX_OFI_LOG(ERROR, "invalid character %c in %s", *p, env_name); return -1; } *val = sign * value; return 1; } return 0; } ga-5.9.2/comex/src-ofi/env.h000066400000000000000000000010271500715745200155420ustar00rootroot00000000000000/* * * Copyright (c) 2016 Intel Corporation. All rights reserved. */ #ifndef ENV_H_ #define ENV_H_ #define CACHELINE_SIZE 64 typedef struct env_data_t { int log_level; int native_atomics; int emulation_type; int progress_thread; int cq_entries_count; int force_sync; char* provider; char* library_path; } env_data_t __attribute__ ((aligned (CACHELINE_SIZE))); extern env_data_t env_data; int env_to_int(const char* env_name, int* value); void parse_env_vars(); #endif /* ENV_H_ */ ga-5.9.2/comex/src-ofi/fi_lock.h000066400000000000000000000070011500715745200163560ustar00rootroot00000000000000/* * Copyright (c) 2013-2014 Intel Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * 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. */ #ifndef _FI_LOCK_H_ #define _FI_LOCK_H_ #include #include #include #ifdef __cplusplus extern "C" { #endif #if PT_LOCK_SPIN == 1 #define fastlock_t_ pthread_spinlock_t #define fastlock_init_(lock) pthread_spin_init(lock, PTHREAD_PROCESS_PRIVATE) #define fastlock_destroy_(lock) pthread_spin_destroy(lock) #define fastlock_acquire_(lock) pthread_spin_lock(lock) #define fastlock_tryacquire_(lock) pthread_spin_trylock(lock) #define fastlock_release_(lock) pthread_spin_unlock(lock) #else #define fastlock_t_ pthread_mutex_t #define fastlock_init_(lock) pthread_mutex_init(lock, NULL) #define fastlock_destroy_(lock) pthread_mutex_destroy(lock) #define fastlock_acquire_(lock) pthread_mutex_lock(lock) #define fastlock_tryacquire_(lock) pthread_mutex_trylock(lock) #define fastlock_release_(lock) pthread_mutex_unlock(lock) #endif /* PT_LOCK_SPIN */ #if ENABLE_DEBUG typedef struct { fastlock_t_ impl; int is_initialized; } fastlock_t; static inline int fastlock_init(fastlock_t *lock) { int ret; ret = fastlock_init_(&lock->impl); lock->is_initialized = !ret; return ret; } static inline void fastlock_destroy(fastlock_t *lock) { int ret; assert(lock->is_initialized); lock->is_initialized = 0; ret = fastlock_destroy_(&lock->impl); assert(!ret); } static inline void fastlock_acquire(fastlock_t *lock) { int ret; assert(lock->is_initialized); ret = fastlock_acquire_(&lock->impl); assert(!ret); } static inline int fastlock_tryacquire(fastlock_t *lock) { assert(lock->is_initialized); return fastlock_tryacquire_(&lock->impl); } static inline void fastlock_release(fastlock_t *lock) { int ret; assert(lock->is_initialized); ret = fastlock_release_(&lock->impl); assert(!ret); } #else /* !ENABLE_DEBUG */ # define fastlock_t fastlock_t_ # define fastlock_init(lock) fastlock_init_(lock) # define fastlock_destroy(lock) fastlock_destroy_(lock) # define fastlock_acquire(lock) fastlock_acquire_(lock) # define fastlock_tryacquire(lock) fastlock_tryacquire_(lock) # define fastlock_release(lock) fastlock_release_(lock) #endif #ifdef __cplusplus } #endif #endif /* _FI_LOCK_H_ */ ga-5.9.2/comex/src-ofi/groups.c000066400000000000000000000206471500715745200162750ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include "comex.h" #include "comex_impl.h" #include "groups.h" /* the HEAD of the group linked list */ comex_igroup_t *group_list = NULL; /* static functions implemented in this file */ static void comex_create_group_and_igroup( comex_group_t *id, comex_igroup_t **igroup); static void comex_igroup_finalize(comex_igroup_t *igroup); /** * Return the comex igroup instance given the group id. * * The group linked list is searched sequentially until the given group * is found. It is an error if this function is called before * comex_group_init(). An error occurs if the given group is not found. */ comex_igroup_t* comex_get_igroup_from_group(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; assert(group_list != NULL); while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { return current_group_list_item; } current_group_list_item = current_group_list_item->next; } comex_error("comex group lookup failed", -1); return NULL; } /** * Creates and associates an comex group with an comex igroup. * * This does *not* initialize the members of the comex igroup. */ static void comex_create_group_and_igroup( comex_group_t *id, comex_igroup_t **igroup) { comex_igroup_t *new_group_list_item = NULL; comex_igroup_t *last_group_list_item = NULL; /* find the last group in the group linked list */ last_group_list_item = group_list; while (last_group_list_item->next != NULL) { last_group_list_item = last_group_list_item->next; } /* create, init, and insert the new node for the linked list */ new_group_list_item = malloc(sizeof(comex_igroup_t)); new_group_list_item->id = last_group_list_item->id + 1; new_group_list_item->comm = MPI_COMM_NULL; new_group_list_item->group = MPI_GROUP_NULL; new_group_list_item->next = NULL; last_group_list_item->next = new_group_list_item; /* return the group id and comex igroup */ *igroup = new_group_list_item; *id = new_group_list_item->id; } int comex_group_rank(comex_group_t group, int *rank) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); status = MPI_Group_rank(igroup->group, rank); if (status != MPI_SUCCESS) { comex_error("MPI_Group_rank: Failed ", status); } return COMEX_SUCCESS; } int comex_group_size(comex_group_t group, int *size) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); status = MPI_Group_size(igroup->group, size); if (status != MPI_SUCCESS) { comex_error("MPI_Group_size: Failed ", status); } return COMEX_SUCCESS; } int comex_group_comm(comex_group_t group, MPI_Comm *comm) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); *comm = igroup->comm; return COMEX_SUCCESS; } int comex_group_translate_world(comex_group_t group, int group_rank, int *world_rank) { if (COMEX_GROUP_WORLD == group) { *world_rank = group_rank; } else { comex_igroup_t *igroup = comex_get_igroup_from_group(group); comex_igroup_t *world_igroup = comex_get_igroup_from_group(COMEX_GROUP_WORLD); int status = MPI_Group_translate_ranks( igroup->group, 1, &group_rank, world_igroup->group, world_rank); if (status != MPI_SUCCESS) { comex_error("MPI_Group_translate_ranks: Failed ", status); } } return COMEX_SUCCESS; } /** * Destroys the given comex igroup. */ static void comex_igroup_finalize(comex_igroup_t *igroup) { int status; assert(igroup); if (igroup->group != MPI_GROUP_NULL) { status = MPI_Group_free(&igroup->group); if (status != MPI_SUCCESS) { comex_error("MPI_Group_free: Failed ", status); } } if (igroup->comm != MPI_COMM_NULL) { status = MPI_Comm_free(&igroup->comm); if (status != MPI_SUCCESS) { comex_error("MPI_Comm_free: Failed ", status); } } } int comex_group_free(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; /* find the group to free */ while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { break; } previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; } /* make sure we found a group */ assert(current_group_list_item != NULL); /* remove the group from the linked list */ if (previous_group_list_item != NULL) { previous_group_list_item->next = current_group_list_item->next; } /* free the group */ comex_igroup_finalize(current_group_list_item); free(current_group_list_item); return COMEX_SUCCESS; } int comex_group_create( int n, int *pid_list, comex_group_t id_parent, comex_group_t *id_child) { int status; int grp_me; comex_igroup_t *igroup_child = NULL; MPI_Group *group_child = NULL; MPI_Comm *comm_child = NULL; comex_igroup_t *igroup_parent = NULL; MPI_Group *group_parent = NULL; MPI_Comm *comm_parent = NULL; /* create the node in the linked list of groups and */ /* get the child's MPI_Group and MPI_Comm, to be populated shortly */ comex_create_group_and_igroup(id_child, &igroup_child); group_child = &(igroup_child->group); comm_child = &(igroup_child->comm); /* get the parent's MPI_Group and MPI_Comm */ igroup_parent = comex_get_igroup_from_group(id_parent); group_parent = &(igroup_parent->group); comm_parent = &(igroup_parent->comm); status = MPI_Group_incl(*group_parent, n, pid_list, group_child); if (status != MPI_SUCCESS) { comex_error("MPI_Group_incl: Failed ", status); } { MPI_Comm comm, comm1, comm2; int lvl=1, local_ldr_pos; MPI_Group_rank(*group_child, &grp_me); if (grp_me == MPI_UNDEFINED) { /* FIXME: keeping the group around for now */ return COMEX_SUCCESS; } /* SK: sanity check for the following bitwise operations */ assert(grp_me>=0); MPI_Comm_dup(MPI_COMM_SELF, &comm); /* FIXME: can be optimized away */ local_ldr_pos = grp_me; while (n>lvl) { int tag=0; int remote_ldr_pos = local_ldr_pos^lvl; if (remote_ldr_pos < n) { int remote_leader = pid_list[remote_ldr_pos]; MPI_Comm peer_comm = *comm_parent; int high = (local_ldr_posid = COMEX_GROUP_WORLD; group_list->next = NULL; /* save MPI world group and communicatior in COMEX_GROUP_WORLD */ group_list->comm = l_state.world_comm; MPI_Comm_group(group_list->comm, &(group_list->group)); } void comex_group_finalize() { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; /* don't free the world group (the list head) */ current_group_list_item = current_group_list_item->next; while (current_group_list_item != NULL) { previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; comex_igroup_finalize(previous_group_list_item); free(previous_group_list_item); } /* ok, now free the world group, but not the world comm */ MPI_Group_free(&(group_list->group)); free(group_list); group_list = NULL; } ga-5.9.2/comex/src-ofi/groups.h000066400000000000000000000023461500715745200162760ustar00rootroot00000000000000/** * Private header file for comex groups backed by MPI_comm. * * The rest of the comex group functions are defined in the public comex.h. * * @author Jeff Daily */ #ifndef _COMEX_GROUPS_H_ #define _COMEX_GROUPS_H_ #include #include "comex.h" #include "comex_impl.h" typedef struct group_link { struct group_link *next; comex_group_t id; MPI_Comm comm; MPI_Group group; } comex_igroup_t; extern void comex_group_init(); extern void comex_group_finalize(); extern comex_igroup_t* comex_get_igroup_from_group(comex_group_t group); #define VALIDATE_GROUP_AND_PROC(group, proc) \ do { \ int group_size; \ EXPR_CHKANDJUMP((group >= 0), "invalid group"); \ COMEX_CHKANDJUMP(comex_group_size(group, &group_size), "failed to get group size"); \ EXPR_CHKANDJUMP((proc >= 0), "invalid proc"); \ EXPR_CHKANDJUMP((proc < group_size), "invalid proc"); \ } while(0) #endif /* _COMEX_GROUPS_H_ */ ga-5.9.2/comex/src-ofi/log.h000066400000000000000000000115201500715745200155320ustar00rootroot00000000000000/* * * Copyright (c) 2016 Intel Corporation. All rights reserved. */ #ifndef LOG_H_ #define LOG_H_ #include #include #include #include #include #include #include #include #ifndef __USE_MISC #define __USE_MISC #endif #ifndef __USE_XOPEN2K #define __USE_XOPEN2K #endif #include #include "env.h" typedef enum log_level_t { ERROR = 0, WARN, INFO, DEBUG, TRACE } log_level_t; #define COMEX_OFI_LOG(log_lvl, fmt, ...) \ do { \ if (log_lvl <= env_data.log_level) \ { \ char time_buf[20]; /*2016:07:21 14:47:39*/ \ get_time(time_buf, 20); \ switch (log_lvl) \ { \ case ERROR: \ { \ printf("%s: ERROR: (%d): %s:%u " fmt "\n", time_buf, get_tid(), \ __FUNCTION__, __LINE__, ##__VA_ARGS__); \ print_backtrace(); \ break; \ } \ case WARN: \ { \ printf("WARNING: (%d): " fmt "\n", get_tid(), ##__VA_ARGS__); \ break; \ } \ case INFO: \ { \ printf("(%d):" fmt "\n", get_tid(), ##__VA_ARGS__); \ break; \ } \ case DEBUG: \ case TRACE: \ { \ printf("%s: (%d): %s:%u " fmt "\n", time_buf, get_tid(), \ __FUNCTION__, __LINE__, ##__VA_ARGS__); \ break; \ } \ default: \ { \ assert(0); \ } \ } \ fflush(stdout); \ } \ } while (0) static int get_tid() { #if defined(__APPLE__) && defined(__MACH__) uint64_t tid; pthread_threadid_np(pthread_self(), &tid); return (int)tid; #else return (int)syscall(SYS_gettid); #endif } static void get_time(char* buf, size_t buf_size) { time_t timer; struct tm* time_info = 0; time(&timer); time_info = localtime(&timer); assert(time_info); strftime(buf, buf_size, "%Y:%m:%d %H:%M:%S", time_info); } static void print_backtrace(void) { int j, nptrs; void* buffer[100]; char** strings; nptrs = backtrace(buffer, 100); printf("backtrace() returned %d addresses\n", nptrs); fflush(stdout); strings = backtrace_symbols(buffer, nptrs); if (strings == NULL) { perror("backtrace_symbols"); exit(EXIT_FAILURE); } for (j = 0; j < nptrs; j++) { printf("%s\n", strings[j]); fflush(stdout); } free(strings); } #endif /* LOG_H_ */ ga-5.9.2/comex/src-ofi/mutex.h000066400000000000000000000046151500715745200161220ustar00rootroot00000000000000/* * * Copyright (c) 2016 Intel Corporation. All rights reserved. */ #ifndef MUTEX_H_ #define MUTEX_H_ #define MUTEX_NATIVE_ONLY #ifndef MUTEX_NATIVE_ONLY #include #endif /* MUTEX_NATIVE_ONLY */ /* mutex infrastructure/implementation, based on MCS mutexes */ #define MUTEX_TAGMASK (777L << 32) #define LOCAL_MUTEX_TAGMASK (778L << 32) typedef struct fid_mr* mutex_mr_t; typedef uint64_t mutex_key_t; typedef struct mcs_mutex_t { int proc; int count; mutex_key_t key; int* tail; /* pointer to tail elements on remote host */ int* elem; /* index of element where 'elem' begins in 'tail' array */ } mcs_mutex_t; typedef struct mutex_t { mutex_mr_t mr; struct mcs_mutex_t* mcs_mutex; int* elem_offset; /* list of start indexes for 'elem's */ void* data; /* used to store locally allocated data */ uint64_t tagmask; } mutex_t; /* every node must have 'elem' for every mutex used in cluster. * to optimize resource usage all such 'elem' are located in solid * array which registered as memory region in OFI. to get index of * required element based on proc and mutex is used macto below */ #define ELEM_INDEX(mtx, proc, id) ((mtx)->elem_offset[proc] + (id)) #define MUTEX_ELEM_IDX(mtx, index) ((mtx).elem_idx + (index)) #define MUTEX_ELEM(mtx, index) ((mtx).tail[MUTEX_ELEM_IDX(mtx, index)]) /* original implementation of mcs mutexes use 'tail' element on 'root' * node and 'elem' element on every node which may lock mutex. * usually it is implemented using atomic access to remote memory, * for example MPI window (MPI based implementation), or registered * memory/atomic API of OFI. * * core idea of MCS mutex is in manipulation of pair elements: 'tail' * on node which locked mutex, and 'elem' element on node which waiting * for mutex unlocked. when one of node is going to lock mutex on remote * (or local) node, it atomically swap values of 'tail' element on target * node, and in case if on target node previously was non-default value * then current node writes own proc to 'elem' of node which locked mutex. * using this mechanism connected list/queue is created. * * here implemented a bit modified algorithm: every node may have * any number of mutexes, to optimize usage of 'tail's and 'elem's * common arrays are used to store required objects */ #endif /* MUTEX_H_ */ ga-5.9.2/comex/src-ofi/ofi.h000066400000000000000000000111121500715745200155230ustar00rootroot00000000000000/* * * Copyright (c) 2016 Intel Corporation. All rights reserved. */ #ifndef _COMEX_OFI_H_ #define _COMEX_OFI_H_ #include "env.h" #include "log.h" #define STR_ERROR(tbl, ret) CALL_TABLE_FUNCTION(tbl, fi_strerror(ret)) #define OFI_EP_NAME_MAX_LENGTH (512) /* We use constant ep name length */ #define RMA_PROVIDER_CAPS (FI_RMA | TAGGED_PROVIDER_CAPS) #define TAGGED_PROVIDER_CAPS (FI_TAGGED) #define ATOMICS_PROVIDER_CAPS (FI_ATOMICS | TAGGED_PROVIDER_CAPS) #define DESIRED_PROVIDER_CAPS (RMA_PROVIDER_CAPS | TAGGED_PROVIDER_CAPS | ATOMICS_PROVIDER_CAPS) #define MR_ACCESS_PERMISSIONS (FI_READ | FI_WRITE | FI_REMOTE_READ | FI_REMOTE_WRITE) #define EP_COMPLETIONS_TO_REPORT (FI_TRANSMIT | FI_RECV) #define OFI_VERSION FI_VERSION(1, 1) /*#define OFI_VERSION FI_VERSION(FI_MAJOR_VERSION, FI_MINOR_VERSION)*/ #define ep_tagged ep_rma #define peer_tagged peer_rma typedef struct peer_t { int proc; /* world proc */ fi_addr_t fi_addr; } peer_t; typedef struct ofi_ep_t { struct fid_fabric *fabric; /* fabric object */ struct fid_domain *domain; /* domain object */ struct fi_info *provider; /* provider into */ struct fid_ep *endpoint; /* endpoint object */ struct fid_cq *cq; /* completion queue */ struct fid_av *av; /* address vector */ peer_t* peers; enum fi_mr_mode mr_mode; } ofi_ep_t; typedef struct ofi_data_t { ofi_ep_t ep_rma; ofi_ep_t ep_atomics; int msg_prefix_size; int rma_iov_limit; ssize_t max_bytes_in_atomic[COMEX_DTYPES_COUNT]; /* bytes in one atomic operation per comex datatype */ int max_buffered_send; uint64_t mr_counter; } ofi_data_t; extern ofi_data_t ofi_data; #ifndef GA_OFI_STATIC_LINK #define LOAD_TABLE_FUNCTION(table, fname) \ do { \ (table)->fname = dlsym((table)->handle, #fname); \ if ((table)->fname == NULL) \ { \ COMEX_OFI_LOG(WARN, "Can't load function %s, error=%s", \ #fname, dlerror()); \ dlclose((table)->handle); \ goto fn_fail; \ } \ } while (0) #define CALL_TABLE_FUNCTION(table, call) (table)->call #define fi_allocinfo_p() CALL_TABLE_FUNCTION(&ld_table, fi_dupinfo(0)) typedef int (*fi_getinfo_t)(uint32_t version, const char *node, const char *service, uint64_t flags, struct fi_info *hints, struct fi_info **info); typedef void (*fi_freeinfo_t)(struct fi_info *info); typedef struct fi_info* (*fi_dupinfo_t)(const struct fi_info *info); typedef int (*fi_fabric_t)(struct fi_fabric_attr *attr, struct fid_fabric **fabric, void *context); typedef const char* (*fi_strerror_t)(int errnum); typedef char* (*fi_tostr_t)(const void *data, enum fi_type datatype); typedef struct fi_loadable_methods { void * handle; fi_getinfo_t fi_getinfo; fi_freeinfo_t fi_freeinfo; fi_dupinfo_t fi_dupinfo; fi_fabric_t fi_fabric; fi_strerror_t fi_strerror; fi_tostr_t fi_tostr; } fi_loadable_methods_t; extern fi_loadable_methods_t ld_table; static inline int load_ofi(fi_loadable_methods_t* table) { ld_table.handle = dlopen(env_data.library_path, RTLD_NOW); if (!ld_table.handle) { COMEX_OFI_LOG(ERROR, "cannot load default ofi library %s, error=%s", env_data.library_path, dlerror()); goto fn_fail; } LOAD_TABLE_FUNCTION(&ld_table, fi_getinfo); LOAD_TABLE_FUNCTION(&ld_table, fi_freeinfo); LOAD_TABLE_FUNCTION(&ld_table, fi_dupinfo); LOAD_TABLE_FUNCTION(&ld_table, fi_fabric); LOAD_TABLE_FUNCTION(&ld_table, fi_strerror); LOAD_TABLE_FUNCTION(&ld_table, fi_tostr); fn_success: return COMEX_SUCCESS; fn_fail: return COMEX_FAILURE; } static inline int unload_ofi(fi_loadable_methods_t* table) { if(ld_table.handle) dlclose(ld_table.handle); return COMEX_SUCCESS; } #else /* GA_OFI_STATIC_LINK */ #define LOAD_TABLE_FUNCTION(table, fname) #define CALL_TABLE_FUNCTION(table, call) call #define fi_allocinfo_p(table) fi_allocinfo() #define load_ofi(table) COMEX_SUCCESS #define unload_ofi(table) COMEX_SUCCESS #endif /* GA_OFI_STATIC_LINK */ #endif /* _COMEX_OFI_H_ */ ga-5.9.2/comex/src-ofi/request.h000066400000000000000000000165771500715745200164620ustar00rootroot00000000000000/* * * Copyright (c) 2016 Intel Corporation. All rights reserved. */ #ifndef REQUEST_H_ #define REQUEST_H_ #include #include "comex.h" #include "comex_impl.h" #define sizeofa(x) (sizeof(x) / sizeof(*(x))) #define REQUEST_MAGIC 0xB4BAE6A2 #define REQUEST_CACHE_SIZE 1024 #define PROC_NONE (-1) #define ZERO_REQUEST(r) \ do \ { \ (r)->group = 0; \ (r)->proc = PROC_NONE; \ (r)->flags = rf_none; \ (r)->dtor = 0; \ (r)->cmpl = 0; \ (r)->parent = 0; \ (r)->child_cnt = 0; \ (r)->data = 0; \ (r)->mrs = 0; \ (r)->mr_count = 0; \ (r)->mr_single = 0; \ } while (0) extern struct request_cache_t* request_cache; typedef enum request_flag_t { rf_none = 0, rf_auto_free = 1 << 0, rf_no_group_wait = 1 << 1, rf_no_free_data = 1 << 2 } request_flag_t; typedef enum request_state { rs_none, /* message object/slot is empty */ rs_progress, /* slot is in progress */ rs_complete /* slot is ready to be read */ } request_state; typedef struct request_t { /* linked list of received messages */ struct fi_context context; /* OFI context */ int magic; /* magic */ int index; /* request index */ request_state state; /* writing rs_progress value to this value must be atomic */ comex_group_t group; /* group to wait */ int proc; /* proc to wait */ int flags; /* some useful flags */ void (*dtor)(struct request_t* request); /* destructor callback */ void (*cmpl)(struct request_t* request); /* compete state callback */ struct request_t* parent; /* pointer to parent request */ int child_cnt; /* number of child requests connected here */ void* data; /* request specific data */ int inplace; /* used as buffer to store trecv */ int mr_count; struct fid_mr** mrs; struct fid_mr* mr_single; /* some optimization - used when there is single buffer */ } request_t; typedef struct request_cache_t { request_t request[REQUEST_CACHE_SIZE]; int index; /* index of first request in current cache (used to find request by index) */ struct request_cache_t* next; } request_cache_t; static inline struct request_cache_t* create_request_cache() { request_cache_t* cache = malloc(sizeof(*cache)); if (cache) { cache->next = 0; cache->index = 0; int i; for (i = 0; i < sizeofa(cache->request); i++) { cache->request[i].state = rs_none; cache->request[i].magic = REQUEST_MAGIC; } return cache; } else return 0; } static inline void free_request_cache() { struct request_cache_t* cache = request_cache; while (cache) { struct request_cache_t* c = cache->next; free(cache); cache = c; } request_cache = 0; } static inline void reset_request(struct request_t* req) { assert(req); assert(req->state == rs_complete); if (req) { if (req->dtor) req->dtor(req); req->state = rs_progress; ZERO_REQUEST(req); } } static inline void free_request(struct request_t* req) { assert(req); assert(req->state == rs_complete); if (req) { if (req->dtor) req->dtor(req); ZERO_REQUEST(req); req->state = rs_none; } } /* find/create empty request slot. function is thread safe due to used * atomic primitives to locate/update entries */ static inline struct request_t* alloc_request() { assert(request_cache); int i; request_t* request = 0; /* try to find free request (state == rs_none) */ struct request_cache_t* cache = request_cache; while (cache) { for (i = 0; i < sizeofa(cache->request); i++) if (__sync_bool_compare_and_swap(&cache->request[i].state, rs_none, rs_progress)) { /* update request index */ cache->request[i].index = cache->index + i; ZERO_REQUEST(&cache->request[i]); request = cache->request + i; /* ok, one of entries has rs_none state, catched it & return */ goto fn_exit; } /* no entries in current cache element, try next one... */ cache = cache->next; } /* ok, no free entries... no problemo - create one more element for request cache, * catch there one element and append new cache into tail of cache list */ cache = create_request_cache(); assert(cache); if (cache) { cache->request[0].state = rs_progress; /* add new cache entry into list */ struct request_cache_t* c = request_cache; /* here is trick: __sync_val_compare_and_swap updates c->next only in case if there * was zero value, and returns original value. in case if c->next element is not zero * then just jump to next element */ for (i = 0; c; i++) { /* set index value to 'current' count */ cache->index = (i + 1) * sizeofa(c->request); c = __sync_val_compare_and_swap(&c->next, 0, cache); } cache->request->index = cache->index; ZERO_REQUEST(cache->request); request = cache->request; goto fn_exit; } fn_exit: return request; } /* initialize request on stack */ static inline void init_request(request_t* request) { request->magic = REQUEST_MAGIC; request->state = rs_progress; ZERO_REQUEST(request); } /* lookup request by index: list all cache arrays till * index is inside of cache array */ static inline request_t* lookup_request(int index) { request_cache_t* cache = request_cache; while (cache) { if (index >= cache->index && index < cache->index + sizeofa(cache->request)) return cache->request + (index - cache->index); cache = cache->next; } return 0; } static inline void complete_request(request_t* request); static inline void increment_request_cnt(request_t* request) { assert(request); assert(request->state == rs_progress); __sync_fetch_and_add(&request->child_cnt, 1); } static inline int decrement_request_cnt(request_t* request) { assert(request); assert(request->state == rs_progress); int ret = __sync_sub_and_fetch(&request->child_cnt, 1); if (!ret) { complete_request(request); } return ret; } /* setup request to follow another request */ static inline void set_parent_request(request_t* parent, request_t* child) { assert(parent); assert(child); child->flags |= rf_auto_free; /* child must be removed automatically */ increment_request_cnt(parent); child->parent = parent; } static inline void complete_request(request_t* request) { assert(request); assert(request->state == rs_progress); request_t _r = *request; if (__sync_lock_test_and_set(&request->state, rs_complete) == rs_progress) { if(_r.cmpl) _r.cmpl(request); if (_r.parent) decrement_request_cnt(_r.parent); /* auto-free request should not be waited */ if (_r.flags & rf_auto_free) free_request(request); } } #endif /* REQUEST_H_ */ ga-5.9.2/comex/src-portals4/000077500000000000000000000000001500715745200155745ustar00rootroot00000000000000ga-5.9.2/comex/src-portals4/Makefile.inc000066400000000000000000000003441500715745200200050ustar00rootroot00000000000000libcomex_la_SOURCES += src-portals4/comex.c libcomex_la_SOURCES += src-portals4/comex_impl.h libcomex_la_SOURCES += src-portals4/groups.c libcomex_la_SOURCES += src-portals4/groups.h AM_CPPFLAGS += -I$(top_srcdir)/src-portals4 ga-5.9.2/comex/src-portals4/comex.c000066400000000000000000001632061500715745200170630ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* C and/or system headers */ #include #include #include #include #include #include #include #include /* 3rd party headers */ #include #include /* our headers */ #include "acc.h" #include "comex.h" #include "comex_impl.h" #include "groups.h" #define CHECK_PTL_RETVAL(retval) check_ptl_retval((retval), __FILE__, __LINE__) #define CHECK_MPI_RETVAL(retval) check_mpi_retval((retval), __FILE__, __LINE__) #if SIZEOF_VOIDP == SIZEOF_MPI_AINT # define MPI_VOIDP MPI_AINT #elif SIZEOF_VOIDP == SIZEOF_INT # define MPI_VOIDP MPI_INT #elif SIZEOF_VOIDP == SIZEOF_LONG # define MPI_VOIDP MPI_LONG #elif SIZEOF_VOIDP == SIZEOF_LONG_LONG # define MPI_VOIDP MPI_LONG_LONG_INT #else # error cannot determine MPI_Datatype for void* #endif /* keep track of all nonblocking operations */ typedef struct { int in_use; /**< 1 == user-visible handle */ int send; int reply; int ack; } nb_t; static nb_t *nb_state = NULL; static int nb_index = 0; static int nb_count_event = 0; static int nb_count_event_processed = 0; static int nb_count_send = 0; static int nb_count_send_processed = 0; static int nb_count_reply = 0; static int nb_count_reply_processed = 0; static int nb_count_ack = 0; static int nb_count_ack_processed = 0; /* exported state */ local_state l_state; /* static state */ static int initialized=0; /* for comex_initialized(), 0=false */ static char skip_lock=0; /* don't acquire or release lock */ static int nb_max_outstanding = 0; static void* acc_buf = NULL; static void* rmw_buf = NULL; static void* swp_buf = NULL; /* static function declarations */ static void acquire_remote_lock(int proc); static void release_remote_lock(int proc); static inline void check_ptl_retval(int retval, const char *file, int line); static inline const char * str_ptl_retval(int retval); static inline void check_mpi_retval(int retval, const char *file, int line); static inline const char * str_mpi_retval(int retval); static inline void nb_process_event(); static inline void nb_wait_for_event_space(); static inline int nb_get_handle_index(); static inline nb_t* nb_wait_for_handle(); static inline void nb_wait_for_send(nb_t *nb); static inline void nb_wait_for_reply(nb_t *nb); static inline void nb_wait_for_ack(nb_t *nb); static inline void nb_wait_for_all(nb_t *nb); static inline void nb_wait_all(); static inline void nb_put(void *src, void *dst, int bytes, int proc, nb_t *nb); static inline void nb_get(void *src, void *dst, int bytes, int proc, nb_t *nb); static inline void nb_acc(int datatype, void *scale, void *src, void *dst, int bytes, int proc, nb_t *nb); static inline void nb_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); static inline void nb_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); static inline void nb_accs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb); static inline void nb_putv(comex_giov_t *iov, int iov_len, int proc, nb_t *nb); static inline void nb_getv(comex_giov_t *iov, int iov_len, int proc, nb_t *nb); static inline void nb_accv(int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, nb_t *nb); int _comex_init(MPI_Comm comm) { int init_flag = 0; int status = 0; ptl_ni_limits_t ptl_ni_limits_requested; ptl_md_t ptl_md; ptl_le_t ptl_le; int nb_max_outstanding_initial = 0; if (initialized) { return 0; } initialized = 1; /* Assert MPI has been initialized */ status = MPI_Initialized(&init_flag); CHECK_MPI_RETVAL(status); assert(init_flag); /* Duplicate the World Communicator */ status = MPI_Comm_dup(comm, &(l_state.world_comm)); CHECK_MPI_RETVAL(status); assert(l_state.world_comm); /* My Rank */ status = MPI_Comm_rank(l_state.world_comm, &(l_state.rank)); CHECK_MPI_RETVAL(status); /* World Size */ status = MPI_Comm_size(l_state.world_comm, &(l_state.size)); CHECK_MPI_RETVAL(status); /* groups */ comex_group_init(); /* nonblocking state */ nb_state = NULL; /* initialized later */ nb_index = 0; nb_count_event = 0; nb_count_event_processed = 0; nb_count_send = 0; nb_count_send_processed = 0; nb_count_reply = 0; nb_count_reply_processed = 0; nb_count_ack = 0; nb_count_ack_processed = 0; nb_max_outstanding = COMEX_MAX_NB_OUTSTANDING; /* initial hint */ /* env vars */ { char *value = NULL; if ((value = getenv("COMEX_MAX_NB_OUTSTANDING")) != NULL) { nb_max_outstanding = atoi(value); } } /* portals4 */ /* init portals */ status = PtlInit(); CHECK_PTL_RETVAL(status); /* init portals network */ ptl_ni_limits_requested.max_entries = INT_MAX; ptl_ni_limits_requested.max_unexpected_headers = INT_MAX; ptl_ni_limits_requested.max_mds = INT_MAX; ptl_ni_limits_requested.max_eqs = INT_MAX; ptl_ni_limits_requested.max_cts = INT_MAX; ptl_ni_limits_requested.max_pt_index = INT_MAX; ptl_ni_limits_requested.max_iovecs = INT_MAX; ptl_ni_limits_requested.max_list_size = INT_MAX; ptl_ni_limits_requested.max_triggered_ops = INT_MAX; ptl_ni_limits_requested.max_msg_size = LONG_MAX; ptl_ni_limits_requested.max_atomic_size = LONG_MAX; ptl_ni_limits_requested.max_fetch_atomic_size = LONG_MAX; ptl_ni_limits_requested.max_waw_ordered_size = LONG_MAX; ptl_ni_limits_requested.max_war_ordered_size = LONG_MAX; ptl_ni_limits_requested.max_volatile_size = LONG_MAX; ptl_ni_limits_requested.features = PTL_TARGET_BIND_INACCESSIBLE; status = PtlNIInit(PTL_IFACE_DEFAULT, PTL_NI_NO_MATCHING | PTL_NI_LOGICAL, PTL_PID_ANY, &ptl_ni_limits_requested, &l_state.ptl_ni_limits, &l_state.ptl_ni_handle); CHECK_PTL_RETVAL(status); if (0 == l_state.rank) { printf("max_entries = %d (%d requested)\n", l_state.ptl_ni_limits.max_entries, ptl_ni_limits_requested.max_entries); printf("max_unexpected_headers = %d (%d requested)\n", l_state.ptl_ni_limits.max_unexpected_headers, ptl_ni_limits_requested.max_unexpected_headers); printf("max_mds = %d (%d requested)\n", l_state.ptl_ni_limits.max_mds, ptl_ni_limits_requested.max_mds); printf("max_eqs = %d (%d requested)\n", l_state.ptl_ni_limits.max_eqs, ptl_ni_limits_requested.max_eqs); printf("max_cts = %d (%d requested)\n", l_state.ptl_ni_limits.max_cts, ptl_ni_limits_requested.max_cts); printf("max_pt_index = %d (%d requested)\n", l_state.ptl_ni_limits.max_pt_index, ptl_ni_limits_requested.max_pt_index); printf("max_iovecs = %d (%d requested)\n", l_state.ptl_ni_limits.max_iovecs, ptl_ni_limits_requested.max_iovecs); printf("max_list_size = %d (%d requested)\n", l_state.ptl_ni_limits.max_list_size, ptl_ni_limits_requested.max_list_size); printf("max_triggered_ops = %d (%d requested)\n", l_state.ptl_ni_limits.max_triggered_ops, ptl_ni_limits_requested.max_triggered_ops); printf("max_msg_size = %ld (%ld requested)\n", l_state.ptl_ni_limits.max_msg_size, ptl_ni_limits_requested.max_msg_size); printf("max_atomic_size = %ld (%ld requested)\n", l_state.ptl_ni_limits.max_atomic_size, ptl_ni_limits_requested.max_atomic_size); printf("max_fetch_atomic_size = %ld (%ld requested)\n", l_state.ptl_ni_limits.max_fetch_atomic_size, ptl_ni_limits_requested.max_fetch_atomic_size); printf("max_waw_ordered_size = %ld (%ld requested)\n", l_state.ptl_ni_limits.max_waw_ordered_size, ptl_ni_limits_requested.max_waw_ordered_size); printf("max_war_ordered_size = %ld (%ld requested)\n", l_state.ptl_ni_limits.max_war_ordered_size, ptl_ni_limits_requested.max_war_ordered_size); printf("max_volatile_size = %ld (%ld requested)\n", l_state.ptl_ni_limits.max_volatile_size, ptl_ni_limits_requested.max_volatile_size); printf("features = %u (%u requested)\n", l_state.ptl_ni_limits.features, ptl_ni_limits_requested.features); } /* establish physical to logical rank mapping */ //status = PtlGetId(l_state.ptl_ni_handle, &l_state.ptl_process_id); status = PtlGetPhysId(l_state.ptl_ni_handle, &l_state.ptl_process_id); CHECK_PTL_RETVAL(status); /* keep track of all process ids */ l_state.ptl_process_ids = (ptl_process_t*)malloc( sizeof(ptl_process_t)*l_state.size); assert(NULL != l_state.ptl_process_ids); l_state.ptl_process_ids[l_state.rank] = l_state.ptl_process_id; status = MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, l_state.ptl_process_ids, sizeof(ptl_process_t), MPI_CHAR, l_state.world_comm); CHECK_MPI_RETVAL(status); /* set the mapping (same on all procs) */ status = PtlSetMap(l_state.ptl_ni_handle, l_state.size, l_state.ptl_process_ids); CHECK_PTL_RETVAL(status); /* get this proc's uid */ status = PtlGetUid(l_state.ptl_ni_handle, &l_state.ptl_uid); CHECK_PTL_RETVAL(status); /* create event queue for initiator events */ nb_max_outstanding_initial = nb_max_outstanding; status = PtlEQAlloc(l_state.ptl_ni_handle, nb_max_outstanding*2, &l_state.ptl_eq_handle); while (PTL_NO_SPACE == status && nb_max_outstanding > 0) { /* half the max and try again */ nb_max_outstanding /= 2; status = PtlEQAlloc(l_state.ptl_ni_handle, nb_max_outstanding*2, &l_state.ptl_eq_handle); } assert(nb_max_outstanding > 0); CHECK_PTL_RETVAL(status); if (0 == l_state.rank) { printf("event queue size = %d (%d requested)\n", nb_max_outstanding, nb_max_outstanding_initial); } /* create nonblocking state registers */ nb_state = (nb_t*)malloc(sizeof(nb_t) * nb_max_outstanding); assert(NULL != nb_state); (void)memset(nb_state, 0, sizeof(nb_t) * nb_max_outstanding); /* create memory descriptor */ ptl_md.start = 0; ptl_md.length = PTL_SIZE_MAX; ptl_md.options = PTL_MD_UNORDERED; ptl_md.eq_handle = l_state.ptl_eq_handle; ptl_md.ct_handle = PTL_CT_NONE; status = PtlMDBind(l_state.ptl_ni_handle, &ptl_md, &l_state.ptl_md_handle); CHECK_PTL_RETVAL(status); /* allocate a single portal table entry as the target for all ops */ status = PtlPTAlloc(l_state.ptl_ni_handle, 0, PTL_EQ_NONE, PTL_PT_ANY, &l_state.ptl_pt_index); assert(l_state.ptl_pt_index == 0); CHECK_PTL_RETVAL(status); l_state.ptl_pt_indexes = (ptl_pt_index_t*)malloc( sizeof(ptl_pt_index_t)*l_state.size); l_state.ptl_pt_indexes[l_state.rank] = l_state.ptl_pt_index; status = MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, l_state.ptl_pt_indexes, sizeof(ptl_pt_index_t), MPI_CHAR, l_state.world_comm); CHECK_MPI_RETVAL(status); /* allocate a single list entry for the single portal table index */ ptl_le.start = NULL; ptl_le.length = PTL_SIZE_MAX; ptl_le.ct_handle = PTL_CT_NONE; ptl_le.uid = PTL_UID_ANY; ptl_le.options = PTL_LE_OP_PUT | PTL_LE_OP_GET; status = PtlLEAppend(l_state.ptl_ni_handle, l_state.ptl_pt_index, &ptl_le, PTL_PRIORITY_LIST, NULL, &l_state.ptl_le_handle); CHECK_PTL_RETVAL(status); /* mutexes */ l_state.mutexes = NULL; l_state.local_mutex = NULL; l_state.num_mutexes = NULL; if (l_state.rank == l_state.size-1) { printf("using %d process(es)\n", l_state.size); } /* Synch - Sanity Check */ MPI_Barrier(l_state.world_comm); return COMEX_SUCCESS; } int comex_init() { return _comex_init(MPI_COMM_WORLD); } int comex_init_comm(MPI_Comm comm) { return _comex_init(comm); } int comex_init_args(int *argc, char ***argv) { int init_flag; MPI_Initialized(&init_flag); if(!init_flag) { MPI_Init(argc, argv); } return comex_init(); } int comex_initialized() { return initialized; } void comex_error(const char *msg, int code) { fprintf(stderr,"[%d] Received an Error in Communication: (%d) %s\n", l_state.rank, code, msg); MPI_Abort(l_state.world_comm, code); } int comex_put( void *src, void *dst, int bytes, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; int status = 0; nb = nb_wait_for_handle(); status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_put(src, dst, bytes, world_proc, nb); nb_wait_for_send(nb); return COMEX_SUCCESS; } int comex_get( void *src, void *dst, int bytes, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; int status = 0; nb = nb_wait_for_handle(); status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_get(src, dst, bytes, world_proc, nb); nb_wait_for_reply(nb); return COMEX_SUCCESS; } int comex_acc( int datatype, void *scale, void *src, void *dst, int bytes, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; int status = 0; nb = nb_wait_for_handle(); status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_acc(datatype, scale, src, dst, bytes, world_proc, nb); nb_wait_for_send(nb); return COMEX_SUCCESS; } int comex_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; int status = 0; nb = nb_wait_for_handle(); status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_puts(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); nb_wait_for_send(nb); return COMEX_SUCCESS; } int comex_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; int status = 0; nb = nb_wait_for_handle(); status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_gets(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); nb_wait_for_reply(nb); return COMEX_SUCCESS; } int comex_accs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group) { nb_t *nb = NULL; int world_proc = -1; int status = 0; nb = nb_wait_for_handle(); status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_accs(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); nb_wait_for_send(nb); return COMEX_SUCCESS; } int comex_putv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { int i = 0; nb_t *nb = NULL; int world_proc = -1; int status = 0; nb = nb_wait_for_handle(); status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_putv(iov, iov_len, world_proc, nb); nb_wait_for_send(nb); return COMEX_SUCCESS; } int comex_getv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { int i = 0; nb_t *nb = NULL; int world_proc = -1; int status = 0; nb = nb_wait_for_handle(); status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_getv(iov, iov_len, world_proc, nb); nb_wait_for_reply(nb); return COMEX_SUCCESS; } int comex_accv( int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { int i = 0; nb_t *nb = NULL; int world_proc = -1; int status = 0; nb = nb_wait_for_handle(); status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_accv(datatype, scale, iov, iov_len, world_proc, nb); nb_wait_for_send(nb); return COMEX_SUCCESS; } int comex_fence_all(comex_group_t group) { return comex_wait_all(group); } int comex_fence_proc(int proc, comex_group_t group) { return comex_wait_all(group); } /* comex_barrier is comex_fence_all + MPI_Barrier */ int comex_barrier(comex_group_t group) { int status; MPI_Comm comm; comex_fence_all(group); status = comex_group_comm(group, &comm); assert(COMEX_SUCCESS == status); MPI_Barrier(comm); return COMEX_SUCCESS; } void *comex_malloc_local(size_t size) { int retval = 0; void *memptr = NULL; retval = posix_memalign(&memptr, sizeof(void*), size); if (0 != retval) { errno = retval; perror("comex_malloc_local: posix_memalign"); MPI_Abort(l_state.world_comm, retval); } return memptr; } int comex_free_local(void *ptr) { free(ptr); return COMEX_SUCCESS; } int comex_finalize() { /* it's okay to call multiple times -- extra calls are no-ops */ if (!initialized) { return; } initialized = 0; /* Make sure that all outstanding operations are done */ comex_wait_all(COMEX_GROUP_WORLD); /* groups */ comex_group_finalize(); /* portals4 */ PtlFini(); MPI_Barrier(l_state.world_comm); // destroy the communicators MPI_Comm_free(&l_state.world_comm); return COMEX_SUCCESS; } int comex_wait_proc(int proc, comex_group_t group) { return comex_wait_all(group); } int comex_wait(comex_request_t* hdl) { int index = 0; nb_t *nb = NULL; assert(NULL != hdl); index = *(int*)hdl; assert(index >= 0); assert(index < nb_max_outstanding); nb = &nb_state[index]; if (0 == nb->in_use) { fprintf(stderr, "{%d} comex_test Error: invalid handle\n", l_state.rank); } nb_wait_for_all(nb); nb->in_use = 0; return COMEX_SUCCESS; } int comex_test(comex_request_t* hdl, int *status) { int index = 0; nb_t *nb = NULL; assert(NULL != hdl); index = *(int*)hdl; assert(index >= 0); assert(index < nb_max_outstanding); nb = &nb_state[index]; if (0 == nb->in_use) { fprintf(stderr, "{%d} comex_test Error: invalid handle\n", l_state.rank); } if (nb->send == 0 && nb->reply == 0 && nb->ack == 0) { *status = 0; nb->in_use = 0; } else { *status = 1; } return COMEX_SUCCESS; } int comex_wait_all(comex_group_t group) { nb_wait_all(); return COMEX_SUCCESS; } int comex_nbput( void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; int status = 0; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); assert(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_put(src, dst, bytes, world_proc, nb); return COMEX_SUCCESS; } int comex_nbget( void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; int status = 0; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); assert(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_get(src, dst, bytes, world_proc, nb); return COMEX_SUCCESS; } int comex_nbacc( int datatype, void *scale, void *src, void *dst, int bytes, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; int status = 0; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); assert(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_acc(datatype, scale, src, dst, bytes, world_proc, nb); return COMEX_SUCCESS; } int comex_nbputs( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; int status = 0; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); assert(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_puts(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); return COMEX_SUCCESS; } int comex_nbgets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; int status = 0; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); assert(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_gets(src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); return COMEX_SUCCESS; } int comex_nbaccs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, comex_group_t group, comex_request_t *hdl) { nb_t *nb = NULL; int world_proc = -1; int status = 0; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); assert(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_accs(datatype, scale, src, src_stride, dst, dst_stride, count, stride_levels, world_proc, nb); return COMEX_SUCCESS; } int comex_nbputv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* hdl) { int i = 0; nb_t *nb = NULL; int world_proc = -1; int status = 0; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); assert(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_putv(iov, iov_len, world_proc, nb); return COMEX_SUCCESS; } int comex_nbgetv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* hdl) { int i = 0; nb_t *nb = NULL; int world_proc = -1; int status = 0; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); assert(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_getv(iov, iov_len, world_proc, nb); return COMEX_SUCCESS; } int comex_nbaccv( int datatype, void *scale, comex_giov_t *iov, int iov_len, int proc, comex_group_t group, comex_request_t* hdl) { int i = 0; nb_t *nb = NULL; int world_proc = -1; int status = 0; comex_request_t _hdl = 0; nb = nb_wait_for_handle(); _hdl = nb_get_handle_index(); assert(&nb_state[_hdl] == nb); if (hdl) { *hdl = _hdl; nb->in_use = 1; } status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); nb_accv(datatype, scale, iov, iov_len, world_proc, nb); return COMEX_SUCCESS; } int comex_rmw( int op, void *ploc, void *prem, int extra, int proc, comex_group_t group) { ptl_process_t peer; nb_t *nb = NULL; int world_proc = -1; int status = 0; if (DEBUG) { printf("[%d] comex_rmw(op=%d, ploc(%p)=%d, prem(%p), extra=%d, proc=%d, group=%d)\n", l_state.rank, op, ploc, *((int*)ploc), prem, extra, proc, group); } nb = nb_wait_for_handle(); nb_wait_for_event_space(); nb_count_event += 2; nb_count_send += 1; nb_count_reply += 1; nb->send += 1; nb->reply += 1; status = comex_group_translate_world(group, proc, &world_proc); assert(COMEX_SUCCESS == status); /* allocate rmw buffer on first use */ if (NULL == rmw_buf) { rmw_buf = comex_malloc_local(sizeof(long)); assert(sizeof(long) <= l_state.ptl_ni_limits.max_fetch_atomic_size); } peer.rank = world_proc; if (op == COMEX_FETCH_AND_ADD) { *((int*)rmw_buf) = extra; if (DEBUG) { printf("[%d] comex_rmw ploc=%d rmw_buf=%d\n", l_state.rank, *((int*)ploc), *((int*)rmw_buf)); } status = PtlFetchAtomic( l_state.ptl_md_handle, (ptl_size_t)ploc, l_state.ptl_md_handle, (ptl_size_t)rmw_buf, sizeof(int), peer, l_state.ptl_pt_indexes[world_proc], (ptl_match_bits_t)0, (ptl_size_t)prem, nb, (ptl_hdr_data_t)0, PTL_SUM, PTL_INT32_T); } else if (op == COMEX_FETCH_AND_ADD_LONG) { *((long*)rmw_buf) = (long)extra; if (DEBUG) { printf("[%d] comex_rmw rmw_buf=%ld\n", l_state.rank, *((long*)rmw_buf)); } status = PtlFetchAtomic( l_state.ptl_md_handle, (ptl_size_t)ploc, l_state.ptl_md_handle, (ptl_size_t)rmw_buf, sizeof(long), peer, l_state.ptl_pt_indexes[world_proc], (ptl_match_bits_t)0, (ptl_size_t)prem, nb, (ptl_hdr_data_t)0, PTL_SUM, PTL_INT64_T); } else if (op == COMEX_SWAP) { *((int*)rmw_buf) = *((int*)ploc); status = PtlSwap( l_state.ptl_md_handle, (ptl_size_t)ploc, l_state.ptl_md_handle, (ptl_size_t)rmw_buf, sizeof(int), peer, l_state.ptl_pt_indexes[world_proc], (ptl_match_bits_t)0, (ptl_size_t)prem, nb, (ptl_hdr_data_t)0, NULL, PTL_SWAP, PTL_INT32_T); } else if (op == COMEX_SWAP_LONG) { *((long*)rmw_buf) = *((long*)ploc); status = PtlSwap( l_state.ptl_md_handle, (ptl_size_t)ploc, l_state.ptl_md_handle, (ptl_size_t)rmw_buf, sizeof(long), peer, l_state.ptl_pt_indexes[world_proc], (ptl_match_bits_t)0, (ptl_size_t)prem, nb, (ptl_hdr_data_t)0, NULL, PTL_SWAP, PTL_INT64_T); } else { assert(0); } nb_wait_for_all(nb); return COMEX_SUCCESS; } /* Mutex Operations */ int comex_create_mutexes(int num) { int i=0; if (DEBUG) { printf("[%d] comex_create_mutexes(num=%d)\n", l_state.rank, num); } assert(NULL == l_state.mutexes); assert(NULL == l_state.local_mutex); assert(NULL == l_state.num_mutexes); /* every process knows how many mutexes created on every process */ l_state.num_mutexes = (unsigned int*)malloc(l_state.size * sizeof(unsigned int)); assert(l_state.num_mutexes); /* gather the counts */ MPI_Allgather(&num, 1, MPI_INT, l_state.num_mutexes, 1, MPI_UNSIGNED, l_state.world_comm); /* create the 1 element buffer to hold a remote mutex */ l_state.local_mutex = comex_malloc_local(sizeof(long)); assert(l_state.local_mutex); /* init the local mutex holder to rank+1, indicating no mutex is held */ *l_state.local_mutex = l_state.rank+1; MPI_Barrier(l_state.world_comm); /* create all of the mutexes */ l_state.mutexes = (long**)malloc(l_state.size * sizeof(long*)); assert(l_state.mutexes); comex_malloc((void**)l_state.mutexes, num*sizeof(long), COMEX_GROUP_WORLD); /* init all of my mutexes to 0 */ for (i=0; isend += 1; nb->reply += 1; status = PtlSwap( l_state.ptl_md_handle, (ptl_size_t)l_state.local_mutex, l_state.ptl_md_handle, (ptl_size_t)rmw_buf, sizeof(long), peer, l_state.ptl_pt_indexes[proc], (ptl_match_bits_t)0, (ptl_size_t)&l_state.mutexes[proc][mutex], nb, (ptl_hdr_data_t)0, swp_buf, PTL_CSWAP, PTL_INT64_T); nb_wait_for_all(nb); } while(*(l_state.local_mutex) != 0); return COMEX_SUCCESS; } int comex_unlock(int mutex, int proc) { ptl_process_t peer; nb_t *nb = NULL; int status = 0; if (DEBUG) { printf("[%d] comex_unlock(mutex=%d, proc=%d)\n", l_state.rank, mutex, proc); } /* preconditions */ assert(0 <= proc && proc < l_state.size); assert(0 <= mutex && ((unsigned int)mutex) < l_state.num_mutexes[proc]); /* you cannot unlock a mutex you haven't locked */ assert(*l_state.local_mutex == 0); assert(NULL != rmw_buf); assert(NULL != swp_buf); peer.rank = proc; *((long*)rmw_buf) = 0; /* 0 == unlocked */ *((long*)swp_buf) = l_state.rank + 1; /* value should be the one we put */ do { nb = nb_wait_for_handle(); nb_wait_for_event_space(); nb_count_event += 2; nb_count_send += 1; nb_count_reply += 1; nb->send += 1; nb->reply += 1; status = PtlSwap( l_state.ptl_md_handle, (ptl_size_t)l_state.local_mutex, l_state.ptl_md_handle, (ptl_size_t)rmw_buf, sizeof(long), peer, l_state.ptl_pt_indexes[proc], (ptl_match_bits_t)0, (ptl_size_t)&l_state.mutexes[proc][mutex], nb, (ptl_hdr_data_t)0, swp_buf, PTL_CSWAP, PTL_INT64_T); nb_wait_for_all(nb); } while(*(l_state.local_mutex) == 0); assert(*(l_state.local_mutex) == (long)(l_state.rank + 1)); return COMEX_SUCCESS; } int comex_malloc(void *ptrs[], size_t size, comex_group_t group) { comex_igroup_t *igroup = NULL; MPI_Comm comm = MPI_COMM_NULL; int comm_rank = -1; int comm_size = -1; int status = 0; /* preconditions */ assert(NULL != ptrs); igroup = comex_get_igroup_from_group(group); comm = igroup->comm; assert(MPI_COMM_NULL != comm); status = MPI_Comm_rank(comm, &comm_rank); CHECK_MPI_RETVAL(status); status = MPI_Comm_size(comm, &comm_size); CHECK_MPI_RETVAL(status); /* allocate and register segment */ ptrs[comm_rank] = comex_malloc_local(sizeof(char)*size); /* exchange buffer address */ status = MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, ptrs, 1, MPI_VOIDP, comm); CHECK_MPI_RETVAL(status); status = MPI_Barrier(comm); CHECK_MPI_RETVAL(status); return COMEX_SUCCESS; } int comex_malloc_mem_dev(void *ptrs[], size_t size, comex_group_t group, const char* device) { return comex_malloc(ptrs,size,group); } int comex_free(void *ptr, comex_group_t group) { comex_igroup_t *igroup = NULL; MPI_Comm comm = MPI_COMM_NULL; int comm_rank = -1; int comm_size = -1; long **allgather_ptrs = NULL; int status = 0; /* preconditions */ assert(NULL != ptr); igroup = comex_get_igroup_from_group(group); comm = igroup->comm; assert(MPI_COMM_NULL != comm); status = MPI_Comm_rank(comm, &comm_rank); CHECK_MPI_RETVAL(status); status = MPI_Comm_size(comm, &comm_size); CHECK_MPI_RETVAL(status); /* allocate receive buffer for exchange of pointers */ allgather_ptrs = (long **)malloc(sizeof(void *) * comm_size); assert(allgather_ptrs); /* exchange of pointers */ status = MPI_Allgather(&ptr, sizeof(void *), MPI_BYTE, allgather_ptrs, sizeof(void *), MPI_BYTE, comm); CHECK_MPI_RETVAL(status); /* TODO do something useful with pointers */ /* remove my ptr from reg cache and free ptr */ comex_free_local(ptr); free(allgather_ptrs); /* Is this needed? */ status = MPI_Barrier(comm); CHECK_MPI_RETVAL(status); return COMEX_SUCCESS; } int comex_free_dev(void *ptr, comex_group_t group) { return comex_free(ptr, group); } static void acquire_remote_lock(int proc) { assert(0); } static void release_remote_lock(int proc) { assert(0); } static inline void check_ptl_retval(int retval, const char *file, int line) { if (PTL_OK != retval) { const char *msg = str_ptl_retval(retval); fprintf(stderr, "{%d} PTL Error: %s: line %d: %s\n", l_state.rank, file, line, msg); MPI_Abort(l_state.world_comm, retval); } } static inline const char * str_ptl_retval(int retval) { const char *msg = NULL; switch(retval) { case PTL_OK : msg = "PTL_OK"; break; case PTL_ARG_INVALID : msg = "PTL_ARG_INVALID"; break; case PTL_CT_NONE_REACHED : msg = "PTL_CT_NONE_REACHED"; break; case PTL_EQ_DROPPED : msg = "PTL_EQ_DROPPED"; break; case PTL_EQ_EMPTY : msg = "PTL_EQ_EMPTY"; break; case PTL_FAIL : msg = "PTL_FAIL"; break; case PTL_IGNORED : msg = "PTL_IGNORED"; break; case PTL_IN_USE : msg = "PTL_IN_USE"; break; case PTL_INTERRUPTED : msg = "PTL_INTERRUPTED"; break; case PTL_LIST_TOO_LONG : msg = "PTL_LIST_TOO_LONG"; break; case PTL_NO_INIT : msg = "PTL_NO_INIT"; break; case PTL_NO_SPACE : msg = "PTL_NO_SPACE"; break; case PTL_PID_IN_USE : msg = "PTL_PID_IN_USE"; break; case PTL_PT_FULL : msg = "PTL_PT_FULL"; break; case PTL_PT_EQ_NEEDED : msg = "PTL_PT_EQ_NEEDED"; break; case PTL_PT_IN_USE : msg = "PTL_PT_IN_USE"; break; default : msg = "DEFAULT"; break; } return msg; } static inline void check_mpi_retval(int retval, const char *file, int line) { if (MPI_SUCCESS != retval) { const char *msg = str_mpi_retval(retval); fprintf(stderr, "{%d} MPI Error: %s: line %d: %s\n", l_state.rank, file, line, msg); MPI_Abort(l_state.world_comm, retval); } } static inline const char *str_mpi_retval(int retval) { const char *msg = NULL; if (retval == MPI_SUCCESS ) { msg = "MPI_SUCCESS"; } else if (retval == MPI_ERR_BUFFER ) { msg = "MPI_ERR_BUFFER"; } else if (retval == MPI_ERR_COUNT ) { msg = "MPI_ERR_COUNT"; } else if (retval == MPI_ERR_TYPE ) { msg = "MPI_ERR_TYPE"; } else if (retval == MPI_ERR_TAG ) { msg = "MPI_ERR_TAG"; } else if (retval == MPI_ERR_COMM ) { msg = "MPI_ERR_COMM"; } else if (retval == MPI_ERR_RANK ) { msg = "MPI_ERR_RANK"; } else if (retval == MPI_ERR_ROOT ) { msg = "MPI_ERR_ROOT"; } else if (retval == MPI_ERR_GROUP ) { msg = "MPI_ERR_GROUP"; } else if (retval == MPI_ERR_OP ) { msg = "MPI_ERR_OP"; } else if (retval == MPI_ERR_TOPOLOGY ) { msg = "MPI_ERR_TOPOLOGY"; } else if (retval == MPI_ERR_DIMS ) { msg = "MPI_ERR_DIMS"; } else if (retval == MPI_ERR_ARG ) { msg = "MPI_ERR_ARG"; } else if (retval == MPI_ERR_UNKNOWN ) { msg = "MPI_ERR_UNKNOWN"; } else if (retval == MPI_ERR_TRUNCATE ) { msg = "MPI_ERR_TRUNCATE"; } else if (retval == MPI_ERR_OTHER ) { msg = "MPI_ERR_OTHER"; } else if (retval == MPI_ERR_INTERN ) { msg = "MPI_ERR_INTERN"; } else if (retval == MPI_ERR_IN_STATUS) { msg = "MPI_ERR_IN_STATUS"; } else if (retval == MPI_ERR_PENDING ) { msg = "MPI_ERR_PENDING"; } else if (retval == MPI_ERR_REQUEST ) { msg = "MPI_ERR_REQUEST"; } else if (retval == MPI_ERR_LASTCODE ) { msg = "MPI_ERR_LASTCODE"; } else { msg = "DEFAULT"; } return msg; } static inline void nb_process_event() { ptl_event_t ptl_event; nb_t *nb = NULL; int status = 0; if (DEBUG) { printf("[%d] nb_process_event\n", l_state.rank); } assert(nb_count_event-nb_count_event_processed > 0); #if 0 status = PtlEQWait(l_state.ptl_eq_handle, &ptl_event); CHECK_PTL_RETVAL(status); #else while (1) { unsigned int which = 1; status = PtlEQPoll(&l_state.ptl_eq_handle, 1, 500, &ptl_event, &which); if (PTL_OK == status) { assert(0 == which); break; } else if (PTL_EQ_EMPTY == status) { /* no event found, so try again */ printf("[%d] nb_process_event timeout waiting for event; retry\n" "send %d/%d reply %d/%d ack %d/%d all %d/%d\n", l_state.rank, nb_count_send_processed, nb_count_send, nb_count_reply_processed, nb_count_reply, nb_count_ack_processed, nb_count_ack, nb_count_event_processed, nb_count_event); } else { CHECK_PTL_RETVAL(status); } } #endif assert(NULL != ptl_event.user_ptr); nb = (nb_t*)(ptl_event.user_ptr); assert(NULL != nb); /* check for error condition */ if (PTL_NI_OK != ptl_event.ni_fail_type) { const char *msg = NULL; switch (ptl_event.ni_fail_type) { case PTL_NI_UNDELIVERABLE : msg = "PTL_NI_UNDELIVERABLE"; break; case PTL_NI_PT_DISABLED : msg = "PTL_NI_PT_DISABLED"; break; case PTL_NI_DROPPED : msg = "PTL_NI_DROPPED"; break; case PTL_NI_PERM_VIOLATION : msg = "PTL_NI_PERM_VIOLATION"; break; case PTL_NI_OP_VIOLATION : msg = "PTL_NI_OP_VIOLATION"; break; default : msg = "DEFAULT"; break; } fprintf(stderr, "{%d} PTL Error: %s: line %d: %s\n", l_state.rank, __FILE__, __LINE__, msg); MPI_Abort(l_state.world_comm, ptl_event.ni_fail_type); } /* decrement associated nonblocking handle */ if (PTL_EVENT_REPLY == ptl_event.type) { if (DEBUG) { printf("[%d] nb_process_event PTL_EVENT_REPLY\n", l_state.rank); } assert(nb->reply > 0); nb_count_reply_processed += 1; nb->reply -= 1; } else if (PTL_EVENT_SEND == ptl_event.type) { if (DEBUG) { printf("[%d] nb_process_event PTL_EVENT_SEND\n", l_state.rank); } assert(nb->send > 0); nb_count_send_processed += 1; nb->send -= 1; } else if (PTL_EVENT_ACK == ptl_event.type) { if (DEBUG) { printf("[%d] nb_process_event PTL_EVENT_ACK\n", l_state.rank); } assert(nb->ack > 0); nb_count_ack_processed += 1; nb->ack -= 1; } else { fprintf(stderr, "{%d} nb_process_event Error: unrecognized event\n", l_state.rank); MPI_Abort(l_state.world_comm, ptl_event.type); } nb_count_event_processed += 1; } static inline void nb_wait_for_event_space() { /* make sure we didn't overrun the event queue */ assert((nb_count_event-nb_count_event_processed) <= nb_max_outstanding); /* we need at most space for two events */ while ((nb_count_event-nb_count_event_processed) >= (nb_max_outstanding-2)) { nb_process_event(); } } static inline int nb_get_handle_index() { int value = 0; if (0 == nb_index) { value = nb_max_outstanding-1; } else { value = nb_index-1; } return value; } /** Poll event queue until resources become available. */ static inline nb_t* nb_wait_for_handle() { nb_t *nb = NULL; int in_use_count = 0; /* find first handle that isn't associated with a user-level handle */ /* make sure the handle we find has processed all events */ /* the user can accidentally exhaust the available handles */ do { ++in_use_count; if (in_use_count > nb_max_outstanding) { fprintf(stderr, "{%d} nb_wait_for_handle Error: all user-level " "nonblocking handles have been exhausted\n", l_state.rank); MPI_Abort(l_state.world_comm, -1); } nb = &nb_state[nb_index++]; nb_index %= nb_max_outstanding; /* wrap around if needed */ nb_wait_for_all(nb); } while (nb->in_use); nb_wait_for_event_space(); return nb; } static inline void nb_wait_for_send(nb_t *nb) { assert(NULL != nb); while (nb->send > 0) { nb_process_event(); } } static inline void nb_wait_for_reply(nb_t *nb) { assert(NULL != nb); while (nb->reply > 0) { nb_process_event(); } } static inline void nb_wait_for_ack(nb_t *nb) { assert(NULL != nb); while (nb->ack > 0) { nb_process_event(); } } static inline void nb_wait_for_all(nb_t *nb) { if (DEBUG) { printf("[%d] nb_wait_for_all\n", l_state.rank); } assert(NULL != nb); while (nb->send > 0 || nb->reply > 0 || nb->ack > 0) { nb_process_event(); } } static inline void nb_wait_all() { assert(nb_count_event-nb_count_event_processed >= 0); while (nb_count_event-nb_count_event_processed > 0) { nb_process_event(); } #ifndef NDEBUG { size_t i=0; for (i=0; i 0); assert(proc >= 0); assert(proc < l_state.size); assert(NULL != nb); if (l_state.rank == proc) { memcpy(dst, src, bytes); return; } peer.rank = proc; nb_wait_for_event_space(); nb_count_event += 2; nb_count_send += 1; nb_count_ack += 1; nb->send += 1; nb->ack += 1; status = PtlPut(l_state.ptl_md_handle, (ptl_size_t)src, (ptl_size_t)bytes, PTL_ACK_REQ, peer, l_state.ptl_pt_indexes[proc], (ptl_match_bits_t)0, (ptl_size_t)dst, (void*)nb, (ptl_hdr_data_t)0); CHECK_PTL_RETVAL(status); } static inline void nb_get(void *src, void *dst, int bytes, int proc, nb_t *nb) { int status = 0; ptl_process_t peer; assert(NULL != src); assert(NULL != dst); assert(bytes > 0); assert(proc >= 0); assert(proc < l_state.size); assert(NULL != nb); if (l_state.rank == proc) { memcpy(dst, src, bytes); return; } peer.rank = proc; nb_wait_for_event_space(); nb_count_event += 1; nb_count_reply += 1; nb->reply += 1; status = PtlGet(l_state.ptl_md_handle, (ptl_size_t)dst, (ptl_size_t)bytes, peer, l_state.ptl_pt_indexes[proc], (ptl_match_bits_t)0, (ptl_size_t)src, (void*)nb); CHECK_PTL_RETVAL(status); } static inline void nb_acc(int datatype, void *scale, void *src, void *dst, int bytes, int proc, nb_t *nb) { int status = 0; ptl_process_t peer; ptl_datatype_t ptl_datatype; char need_scale = 0; void *send_buf = NULL; assert(NULL != src); assert(NULL != dst); assert(bytes > 0); assert(proc >= 0); assert(proc < l_state.size); assert(NULL != nb); /* if max_atomic_size is surpassed, break up the transfer into strides */ if (bytes > l_state.ptl_ni_limits.max_atomic_size) { int i = 0; int levels = 1; int xdim = l_state.ptl_ni_limits.max_atomic_size; int ydim = bytes / xdim; int remainder = bytes % xdim; for (i=0; isend += 1; nb->ack += 1; switch (datatype) { case COMEX_ACC_INT: ptl_datatype = PTL_INT32_T; break; case COMEX_ACC_DBL: ptl_datatype = PTL_DOUBLE; break; case COMEX_ACC_FLT: ptl_datatype = PTL_FLOAT; break; case COMEX_ACC_CPL: ptl_datatype = PTL_FLOAT_COMPLEX; break; case COMEX_ACC_DCP: ptl_datatype = PTL_DOUBLE_COMPLEX; break; case COMEX_ACC_LNG: ptl_datatype = PTL_INT64_T; break; default: assert(0); } switch (datatype) { case COMEX_ACC_INT: need_scale = (*((int*)scale) != 1); break; case COMEX_ACC_DBL: need_scale = (*((double*)scale) != 1); break; case COMEX_ACC_FLT: need_scale = (*((float*)scale) != 1); break; case COMEX_ACC_CPL: need_scale = 1; break; case COMEX_ACC_DCP: need_scale = 1; break; case COMEX_ACC_LNG: need_scale = (*((long*)scale) != 1); break; default: assert(0); } if (need_scale) { /* local scaling of src */ _scale(datatype, bytes, acc_buf, src, scale); send_buf = acc_buf; } else { send_buf = src; } status = PtlAtomic(l_state.ptl_md_handle, (ptl_size_t)send_buf, (ptl_size_t)bytes, PTL_ACK_REQ, peer, l_state.ptl_pt_indexes[proc], (ptl_match_bits_t)0, (ptl_size_t)dst, (void*)nb, (ptl_hdr_data_t)NULL, PTL_SUM, ptl_datatype); CHECK_PTL_RETVAL(status); if (need_scale) { nb_wait_for_send(nb); /* so we can reuse acc_buf */ } } static inline void nb_puts( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; int status = 0; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } status = PtlStartBundle(l_state.ptl_ni_handle); CHECK_PTL_RETVAL(status); /* index mangling */ for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_put((char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } status = PtlEndBundle(l_state.ptl_ni_handle); CHECK_PTL_RETVAL(status); } static inline void nb_gets( void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; int status = 0; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } status = PtlStartBundle(l_state.ptl_ni_handle); CHECK_PTL_RETVAL(status); for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } dst_idx = 0; for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_get((char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } status = PtlEndBundle(l_state.ptl_ni_handle); CHECK_PTL_RETVAL(status); } static inline void nb_accs( int datatype, void *scale, void *src, int *src_stride, void *dst, int *dst_stride, int *count, int stride_levels, int proc, nb_t *nb) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; int status = 0; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } status = PtlStartBundle(l_state.ptl_ni_handle); CHECK_PTL_RETVAL(status); /* index mangling */ for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } for(j=1; j<=stride_levels; j++) { dst_idx += (long) dst_bvalue[j] * (long) dst_stride[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } nb_acc(datatype, scale, (char *)src + src_idx, (char *)dst + dst_idx, count[0], proc, nb); } status = PtlEndBundle(l_state.ptl_ni_handle); CHECK_PTL_RETVAL(status); } static inline void nb_putv( comex_giov_t *iov, int iov_len, int proc, nb_t *nb) { int i = 0; int status = 0; status = PtlStartBundle(l_state.ptl_ni_handle); CHECK_PTL_RETVAL(status); for (i=0; i #include #define COMEX_MAX_NB_OUTSTANDING 16 #define DEBUG 0 #define DEBUG_TO_FILE 0 #if DEBUG_TO_FILE # define printf(...) fprintf(l_state.my_file, __VA_ARGS__); fflush(l_state.my_file) #else # define printf(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) #endif typedef struct { MPI_Comm world_comm; int rank; int size; ptl_ni_limits_t ptl_ni_limits; ptl_handle_ni_t ptl_ni_handle; ptl_process_t ptl_process_id; ptl_process_t *ptl_process_ids; ptl_uid_t ptl_uid; ptl_pt_index_t ptl_pt_index; ptl_pt_index_t *ptl_pt_indexes; ptl_handle_eq_t ptl_eq_handle; ptl_handle_md_t ptl_md_handle; ptl_handle_le_t ptl_le_handle; long **mutexes; /**< all mutexes */ long *local_mutex; /**< store the remote mutex value */ unsigned int *num_mutexes; /**< how many mutexes on each process */ #if DEBUG_TO_FILE FILE *my_file; #endif } local_state; extern local_state l_state; #endif /* COMEX_IMPL_H_ */ ga-5.9.2/comex/src-portals4/groups.c000066400000000000000000000206671500715745200172720ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include "comex.h" #include "comex_impl.h" #include "groups.h" /* the HEAD of the group linked list */ comex_igroup_t *group_list = NULL; /* static functions implemented in this file */ static void comex_create_group_and_igroup( comex_group_t *id, comex_igroup_t **igroup); static void comex_igroup_finalize(comex_igroup_t *igroup); /** * Return the comex igroup instance given the group id. * * The group linked list is searched sequentially until the given group * is found. It is an error if this function is called before * comex_group_init(). An error occurs if the given group is not found. */ comex_igroup_t* comex_get_igroup_from_group(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; assert(group_list != NULL); while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { return current_group_list_item; } current_group_list_item = current_group_list_item->next; } comex_error("comex group lookup failed", -1); return NULL; } /** * Creates and associates an comex group with an comex igroup. * * This does *not* initialize the members of the comex igroup. */ static void comex_create_group_and_igroup( comex_group_t *id, comex_igroup_t **igroup) { comex_igroup_t *new_group_list_item = NULL; comex_igroup_t *last_group_list_item = NULL; /* find the last group in the group linked list */ last_group_list_item = group_list; while (last_group_list_item->next != NULL) { last_group_list_item = last_group_list_item->next; } /* create, init, and insert the new node for the linked list */ new_group_list_item = malloc(sizeof(comex_igroup_t)); new_group_list_item->id = last_group_list_item->id + 1; new_group_list_item->comm = MPI_COMM_NULL; new_group_list_item->group = MPI_GROUP_NULL; new_group_list_item->next = NULL; last_group_list_item->next = new_group_list_item; /* return the group id and comex igroup */ *igroup = new_group_list_item; *id = new_group_list_item->id; } int comex_group_rank(comex_group_t group, int *rank) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); status = MPI_Group_rank(igroup->group, rank); if (status != MPI_SUCCESS) { comex_error("MPI_Group_rank: Failed ", status); } return COMEX_SUCCESS; } int comex_group_size(comex_group_t group, int *size) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); status = MPI_Group_size(igroup->group, size); if (status != MPI_SUCCESS) { comex_error("MPI_Group_size: Failed ", status); } return COMEX_SUCCESS; } int comex_group_comm(comex_group_t group, MPI_Comm *comm) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); *comm = igroup->comm; return COMEX_SUCCESS; } int comex_group_translate_world(comex_group_t group, int group_rank, int *world_rank) { if (COMEX_GROUP_WORLD == group) { *world_rank = group_rank; } else { comex_igroup_t *igroup = comex_get_igroup_from_group(group); comex_igroup_t *world_igroup = comex_get_igroup_from_group(COMEX_GROUP_WORLD); int status = MPI_Group_translate_ranks( igroup->group, 1, &group_rank, world_igroup->group, world_rank); if (status != MPI_SUCCESS) { comex_error("MPI_Group_translate_ranks: Failed ", status); } } return COMEX_SUCCESS; } /** * Destroys the given comex igroup. */ static void comex_igroup_finalize(comex_igroup_t *igroup) { int status; assert(igroup); if (igroup->group != MPI_GROUP_NULL) { status = MPI_Group_free(&igroup->group); if (status != MPI_SUCCESS) { comex_error("MPI_Group_free: Failed ", status); } } if (igroup->comm != MPI_COMM_NULL) { status = MPI_Comm_free(&igroup->comm); if (status != MPI_SUCCESS) { comex_error("MPI_Comm_free: Failed ", status); } } } int comex_group_free(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; /* find the group to free */ while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { break; } previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; } /* make sure we found a group */ assert(current_group_list_item != NULL); /* remove the group from the linked list */ if (previous_group_list_item != NULL) { previous_group_list_item->next = current_group_list_item->next; } /* free the group */ comex_igroup_finalize(current_group_list_item); free(current_group_list_item); return COMEX_SUCCESS; } int comex_group_create( int n, int *pid_list, comex_group_t id_parent, comex_group_t *id_child) { int status; int grp_me; comex_igroup_t *igroup_child = NULL; MPI_Group *group_child = NULL; MPI_Comm *comm_child = NULL; comex_igroup_t *igroup_parent = NULL; MPI_Group *group_parent = NULL; MPI_Comm *comm_parent = NULL; /* create the node in the linked list of groups and */ /* get the child's MPI_Group and MPI_Comm, to be populated shortly */ comex_create_group_and_igroup(id_child, &igroup_child); group_child = &(igroup_child->group); comm_child = &(igroup_child->comm); /* get the parent's MPI_Group and MPI_Comm */ igroup_parent = comex_get_igroup_from_group(id_parent); group_parent = &(igroup_parent->group); comm_parent = &(igroup_parent->comm); status = MPI_Group_incl(*group_parent, n, pid_list, group_child); if (status != MPI_SUCCESS) { comex_error("MPI_Group_incl: Failed ", status); } { MPI_Comm comm, comm1, comm2; int lvl=1, local_ldr_pos; MPI_Group_rank(*group_child, &grp_me); if (grp_me == MPI_UNDEFINED) { /* FIXME: keeping the group around for now */ return COMEX_SUCCESS; } /* SK: sanity check for the following bitwise operations */ assert(grp_me>=0); MPI_Comm_dup(MPI_COMM_SELF, &comm); /* FIXME: can be optimized away */ local_ldr_pos = grp_me; while(n>lvl) { int tag=0; int remote_ldr_pos = local_ldr_pos^lvl; if (remote_ldr_pos < n) { int remote_leader = pid_list[remote_ldr_pos]; MPI_Comm peer_comm = *comm_parent; int high = (local_ldr_posid = COMEX_GROUP_WORLD; group_list->next = NULL; /* save MPI world group and communicatior in COMEX_GROUP_WORLD */ group_list->comm = l_state.world_comm; MPI_Comm_group(group_list->comm, &(group_list->group)); } void comex_group_finalize() { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; /* don't free the world group (the list head) */ current_group_list_item = current_group_list_item->next; while (current_group_list_item != NULL) { previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; comex_igroup_finalize(previous_group_list_item); free(previous_group_list_item); } /* ok, now free the world group, but not the world comm */ MPI_Group_free(&(group_list->group)); free(group_list); group_list = NULL; } ga-5.9.2/comex/src-portals4/groups.h000066400000000000000000000010731500715745200172650ustar00rootroot00000000000000/** * Private header file for comex groups backed by MPI_comm. * * The rest of the comex group functions are defined in the public comex.h. * * @author Jeff Daily */ #ifndef _COMEX_GROUPS_H_ #define _COMEX_GROUPS_H_ #include #include "comex.h" typedef struct group_link { struct group_link *next; comex_group_t id; MPI_Comm comm; MPI_Group group; } comex_igroup_t; extern void comex_group_init(); extern void comex_group_finalize(); extern comex_igroup_t* comex_get_igroup_from_group(comex_group_t group); #endif /* _COMEX_GROUPS_H_ */ ga-5.9.2/comex/src-template/000077500000000000000000000000001500715745200156375ustar00rootroot00000000000000ga-5.9.2/comex/src-template/Makefile.inc000066400000000000000000000004571500715745200200550ustar00rootroot00000000000000# NOTE: Replace 'template' with appropriate name of new src-xxx directory. libcomex_la_SOURCES += src-template/comex.c libcomex_la_SOURCES += src-template/comex_impl.h libcomex_la_SOURCES += src-template/groups.c libcomex_la_SOURCES += src-template/groups.h AM_CPPFLAGS += -I$(top_srcdir)/src-template ga-5.9.2/comex/src-template/comex.c000066400000000000000000000472221500715745200171250ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* C and/or system headers */ #include #include #include #include #include #include /* 3rd party headers */ #include /* our headers */ #include "comex.h" #include "comex_impl.h" #include "groups.h" #define DEBUG 0 /* exported state */ local_state l_state; /* static state */ static int initialized=0; /* for comex_initialized(), 0=false */ static char skip_lock=0; /* don't acquire or release lock */ /* static function declarations */ static void acquire_remote_lock(int proc); static void release_remote_lock(int proc); static inline void acc( int datatype, int count, void *get_buf, void *src_ptr, long src_idx, void *scale); /* needed for complex accumulate */ typedef struct { double real; double imag; } DoubleComplex; typedef struct { float real; float imag; } SingleComplex; int comex_init() { int status; if (initialized) { return 0; } initialized = 1; /* Assert MPI has been initialized */ int init_flag; status = MPI_Initialized(&init_flag); assert(MPI_SUCCESS == status); assert(init_flag); /* Duplicate the World Communicator */ status = MPI_Comm_dup(MPI_COMM_WORLD, &(l_state.world_comm)); assert(MPI_SUCCESS == status); assert(l_state.world_comm); /* My Rank */ status = MPI_Comm_rank(l_state.world_comm, &(l_state.rank)); assert(MPI_SUCCESS == status); /* World Size */ status = MPI_Comm_size(l_state.world_comm, &(l_state.size)); assert(MPI_SUCCESS == status); /* groups */ comex_group_init(); /* Synch - Sanity Check */ MPI_Barrier(l_state.world_comm); return COMEX_SUCCESS; } int comex_init_args(int *argc, char ***argv) { int init_flag; MPI_Initialized(&init_flag); if(!init_flag) { MPI_Init(argc, argv); } return comex_init(); } int comex_initialized() { return initialized; } void comex_error(const char *msg, int code) { fprintf(stderr,"[%d] Received an Error in Communication: (%d) %s\n", l_state.rank, code, msg); MPI_Abort(l_state.world_comm, code); } int comex_put( void *src, void *dst, int bytes, int proc, comex_group_t group) { assert(0); return COMEX_SUCCESS; } int comex_get( void *src, void *dst, int bytes, int proc, comex_group_t group) { assert(0); return COMEX_SUCCESS; } int comex_acc( int datatype, void *scale, void *src_ptr, void *dst_ptr, int bytes, int proc, comex_group_t group) { return comex_accs( datatype, scale, src_ptr, NULL, dst_ptr, NULL, &bytes, 0, proc, group); } int comex_puts( void *src_ptr, int *src_stride_ar, void *dst_ptr, int *dst_stride_ar, int *count, int stride_levels, int proc, comex_group_t group) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; int status; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } /* index mangling */ for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } for(j=1; j<=stride_levels; j++) { dst_idx += dst_bvalue[j] * dst_stride_ar[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } status = comex_put((char *)src_ptr + src_idx, (char *)dst_ptr + dst_idx, count[0], proc, group); assert(status == COMEX_SUCCESS); } return COMEX_SUCCESS; } int comex_gets( void *src_ptr, int *src_stride_ar, void *dst_ptr, int *dst_stride_ar, int *count, int stride_levels, int proc, comex_group_t group) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; int status; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) { n1dim *= count[i]; } /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } dst_idx = 0; for(j=1; j<=stride_levels; j++) { dst_idx += dst_bvalue[j] * dst_stride_ar[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } status = comex_get((char *)src_ptr + src_idx, (char *)dst_ptr + dst_idx, count[0], proc, group); assert(status == COMEX_SUCCESS); } return COMEX_SUCCESS; } int comex_accs( int datatype, void *scale, void *src_ptr, int *src_stride_ar, void *dst_ptr, int *dst_stride_ar, int *count, int stride_levels, int proc, comex_group_t group) { int i, j; long src_idx, dst_idx; /* index offset of current block position to ptr */ int n1dim; /* number of 1 dim block */ int src_bvalue[7], src_bunit[7]; int dst_bvalue[7], dst_bunit[7]; void *get_buf; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i<=stride_levels; i++) n1dim *= count[i]; /* calculate the destination indices */ src_bvalue[0] = 0; src_bvalue[1] = 0; src_bunit[0] = 1; src_bunit[1] = 1; dst_bvalue[0] = 0; dst_bvalue[1] = 0; dst_bunit[0] = 1; dst_bunit[1] = 1; for(i=2; i<=stride_levels; i++) { src_bvalue[i] = 0; dst_bvalue[i] = 0; src_bunit[i] = src_bunit[i-1] * count[i-1]; dst_bunit[i] = dst_bunit[i-1] * count[i-1]; } if (0 == skip_lock) { // grab the atomics lock acquire_remote_lock(proc); } get_buf = (char *)malloc(sizeof(char) * count[0]); assert(get_buf); for(i=0; i (count[j]-1)) { src_bvalue[j] = 0; } } dst_idx = 0; for(j=1; j<=stride_levels; j++) { dst_idx += dst_bvalue[j] * dst_stride_ar[j-1]; if((i+1) % dst_bunit[j] == 0) { dst_bvalue[j]++; } if(dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } // Get the remote data in a temp buffer comex_get((char *)dst_ptr + dst_idx, get_buf, count[0], proc, group); // Local accumulate acc(datatype, count[0], get_buf, src_ptr, src_idx, scale); // Write back to remote data comex_put(get_buf, (char *)dst_ptr + dst_idx, count[0], proc, group); } if (0 == skip_lock) { // ungrab the lock release_remote_lock(proc); } free(get_buf); return COMEX_SUCCESS; } int comex_putv( comex_giov_t *iov, int iov_len, int proc, comex_group_t group) { int status; int i; for (i=0; icomm; assert(comm != MPI_COMM_NULL); MPI_Comm_rank(comm, &comm_rank); MPI_Comm_size(comm, &comm_size); /* allocate and register segment */ ptrs[comm_rank] = comex_malloc_local(sizeof(char)*size); /* exchange buffer address */ /* @TODO: Consider using MPI_IN_PLACE? */ memcpy(&src_buf, &ptrs[comm_rank], sizeof(void *)); MPI_Allgather(&src_buf, sizeof(void *), MPI_BYTE, ptrs, sizeof(void *), MPI_BYTE, comm); MPI_Barrier(comm); return COMEX_SUCCESS; } int comex_malloc_mem_dev(void *ptrs[], size_t size, comex_group_t group, const char* device) { return comex_malloc(ptrs,size,group); } int comex_free(void *ptr, comex_group_t group) { comex_igroup_t *igroup = NULL; MPI_Comm comm = MPI_COMM_NULL; int comm_rank; int comm_size; long **allgather_ptrs = NULL; /* preconditions */ assert(NULL != ptr); igroup = comex_get_igroup_from_group(group); comm = igroup->comm; assert(comm != MPI_COMM_NULL); MPI_Comm_rank(comm, &comm_rank); MPI_Comm_size(comm, &comm_size); /* allocate receive buffer for exchange of pointers */ allgather_ptrs = (long **)malloc(sizeof(void *) * comm_size); assert(allgather_ptrs); /* exchange of pointers */ MPI_Allgather(&ptr, sizeof(void *), MPI_BYTE, allgather_ptrs, sizeof(void *), MPI_BYTE, comm); /* TODO do something useful with pointers */ /* remove my ptr from reg cache and free ptr */ comex_free_local(ptr); free(allgather_ptrs); /* Is this needed? */ MPI_Barrier(comm); return COMEX_SUCCESS; } int comex_free_dev(void *ptr, comex_group_t group) { return comex_free(ptr, group); } static void acquire_remote_lock(int proc) { assert(0); } static void release_remote_lock(int proc) { assert(0); } static inline void acc( int datatype, int count, void *get_buf, void *src_ptr, long src_idx, void *scale) { #define EQ_ONE_REG(A) ((A) == 1.0) #define EQ_ONE_CPL(A) ((A).real == 1.0 && (A).imag == 0.0) #define IADD_REG(A,B) (A) += (B) #define IADD_CPL(A,B) (A).real += (B).real; (A).imag += (B).imag #define IADD_SCALE_REG(A,B,C) (A) += (B) * (C) #define IADD_SCALE_CPL(A,B,C) (A).real += ((B).real*(C).real) - ((B).imag*(C).imag);\ (A).imag += ((B).real*(C).imag) + ((B).imag*(C).real); #define ACC(WHICH, COMEX_TYPE, C_TYPE) \ if (datatype == COMEX_TYPE) { \ int m; \ int m_lim = count/sizeof(C_TYPE); \ C_TYPE *iterator = (C_TYPE *)get_buf; \ C_TYPE *value = (C_TYPE *)((char *)src_ptr + src_idx); \ C_TYPE calc_scale = *(C_TYPE *)scale; \ if (EQ_ONE_##WHICH(calc_scale)) { \ for (m = 0 ; m < m_lim; ++m) { \ IADD_##WHICH(iterator[m], value[m]); \ } \ } \ else { \ for (m = 0 ; m < m_lim; ++m) { \ IADD_SCALE_##WHICH(iterator[m], value[m], calc_scale); \ } \ } \ } else ACC(REG, COMEX_ACC_DBL, double) ACC(REG, COMEX_ACC_FLT, float) ACC(REG, COMEX_ACC_INT, int) ACC(REG, COMEX_ACC_LNG, long) ACC(CPL, COMEX_ACC_DCP, DoubleComplex) ACC(CPL, COMEX_ACC_CPL, SingleComplex) { assert(0); } #undef ACC #undef EQ_ONE_REG #undef EQ_ONE_CPL #undef IADD_REG #undef IADD_CPL #undef IADD_SCALE_REG #undef IADD_SCALE_CPL } ga-5.9.2/comex/src-template/comex_impl.h000066400000000000000000000003151500715745200201430ustar00rootroot00000000000000#ifndef COMEX_IMPL_H_ #define COMEX_IMPL_H_ #include typedef struct { MPI_Comm world_comm; int rank; int size; } local_state; extern local_state l_state; #endif /* COMEX_IMPL_H_ */ ga-5.9.2/comex/src-template/groups.c000066400000000000000000000206671500715745200173350ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include "comex.h" #include "comex_impl.h" #include "groups.h" /* the HEAD of the group linked list */ comex_igroup_t *group_list = NULL; /* static functions implemented in this file */ static void comex_create_group_and_igroup( comex_group_t *id, comex_igroup_t **igroup); static void comex_igroup_finalize(comex_igroup_t *igroup); /** * Return the comex igroup instance given the group id. * * The group linked list is searched sequentially until the given group * is found. It is an error if this function is called before * comex_group_init(). An error occurs if the given group is not found. */ comex_igroup_t* comex_get_igroup_from_group(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; assert(group_list != NULL); while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { return current_group_list_item; } current_group_list_item = current_group_list_item->next; } comex_error("comex group lookup failed", -1); return NULL; } /** * Creates and associates an comex group with an comex igroup. * * This does *not* initialize the members of the comex igroup. */ static void comex_create_group_and_igroup( comex_group_t *id, comex_igroup_t **igroup) { comex_igroup_t *new_group_list_item = NULL; comex_igroup_t *last_group_list_item = NULL; /* find the last group in the group linked list */ last_group_list_item = group_list; while (last_group_list_item->next != NULL) { last_group_list_item = last_group_list_item->next; } /* create, init, and insert the new node for the linked list */ new_group_list_item = malloc(sizeof(comex_igroup_t)); new_group_list_item->id = last_group_list_item->id + 1; new_group_list_item->comm = MPI_COMM_NULL; new_group_list_item->group = MPI_GROUP_NULL; new_group_list_item->next = NULL; last_group_list_item->next = new_group_list_item; /* return the group id and comex igroup */ *igroup = new_group_list_item; *id = new_group_list_item->id; } int comex_group_rank(comex_group_t group, int *rank) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); status = MPI_Group_rank(igroup->group, rank); if (status != MPI_SUCCESS) { comex_error("MPI_Group_rank: Failed ", status); } return COMEX_SUCCESS; } int comex_group_size(comex_group_t group, int *size) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); status = MPI_Group_size(igroup->group, size); if (status != MPI_SUCCESS) { comex_error("MPI_Group_size: Failed ", status); } return COMEX_SUCCESS; } int comex_group_comm(comex_group_t group, MPI_Comm *comm) { int status; comex_igroup_t *igroup = comex_get_igroup_from_group(group); *comm = igroup->comm; return COMEX_SUCCESS; } int comex_group_translate_world(comex_group_t group, int group_rank, int *world_rank) { if (COMEX_GROUP_WORLD == group) { *world_rank = group_rank; } else { comex_igroup_t *igroup = comex_get_igroup_from_group(group); comex_igroup_t *world_igroup = comex_get_igroup_from_group(COMEX_GROUP_WORLD); int status = MPI_Group_translate_ranks( igroup->group, 1, &group_rank, world_igroup->group, world_rank); if (status != MPI_SUCCESS) { comex_error("MPI_Group_translate_ranks: Failed ", status); } } return COMEX_SUCCESS; } /** * Destroys the given comex igroup. */ static void comex_igroup_finalize(comex_igroup_t *igroup) { int status; assert(igroup); if (igroup->group != MPI_GROUP_NULL) { status = MPI_Group_free(&igroup->group); if (status != MPI_SUCCESS) { comex_error("MPI_Group_free: Failed ", status); } } if (igroup->comm != MPI_COMM_NULL) { status = MPI_Comm_free(&igroup->comm); if (status != MPI_SUCCESS) { comex_error("MPI_Comm_free: Failed ", status); } } } int comex_group_free(comex_group_t id) { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; /* find the group to free */ while (current_group_list_item != NULL) { if (current_group_list_item->id == id) { break; } previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; } /* make sure we found a group */ assert(current_group_list_item != NULL); /* remove the group from the linked list */ if (previous_group_list_item != NULL) { previous_group_list_item->next = current_group_list_item->next; } /* free the group */ comex_igroup_finalize(current_group_list_item); free(current_group_list_item); return COMEX_SUCCESS; } int comex_group_create( int n, int *pid_list, comex_group_t id_parent, comex_group_t *id_child) { int status; int grp_me; comex_igroup_t *igroup_child = NULL; MPI_Group *group_child = NULL; MPI_Comm *comm_child = NULL; comex_igroup_t *igroup_parent = NULL; MPI_Group *group_parent = NULL; MPI_Comm *comm_parent = NULL; /* create the node in the linked list of groups and */ /* get the child's MPI_Group and MPI_Comm, to be populated shortly */ comex_create_group_and_igroup(id_child, &igroup_child); group_child = &(igroup_child->group); comm_child = &(igroup_child->comm); /* get the parent's MPI_Group and MPI_Comm */ igroup_parent = comex_get_igroup_from_group(id_parent); group_parent = &(igroup_parent->group); comm_parent = &(igroup_parent->comm); status = MPI_Group_incl(*group_parent, n, pid_list, group_child); if (status != MPI_SUCCESS) { comex_error("MPI_Group_incl: Failed ", status); } { MPI_Comm comm, comm1, comm2; int lvl=1, local_ldr_pos; MPI_Group_rank(*group_child, &grp_me); if (grp_me == MPI_UNDEFINED) { /* FIXME: keeping the group around for now */ return COMEX_SUCCESS; } /* SK: sanity check for the following bitwise operations */ assert(grp_me>=0); MPI_Comm_dup(MPI_COMM_SELF, &comm); /* FIXME: can be optimized away */ local_ldr_pos = grp_me; while(n>lvl) { int tag=0; int remote_ldr_pos = local_ldr_pos^lvl; if (remote_ldr_pos < n) { int remote_leader = pid_list[remote_ldr_pos]; MPI_Comm peer_comm = *comm_parent; int high = (local_ldr_posid = COMEX_GROUP_WORLD; group_list->next = NULL; /* save MPI world group and communicatior in COMEX_GROUP_WORLD */ group_list->comm = l_state.world_comm; MPI_Comm_group(group_list->comm, &(group_list->group)); } void comex_group_finalize() { comex_igroup_t *current_group_list_item = group_list; comex_igroup_t *previous_group_list_item = NULL; /* don't free the world group (the list head) */ current_group_list_item = current_group_list_item->next; while (current_group_list_item != NULL) { previous_group_list_item = current_group_list_item; current_group_list_item = current_group_list_item->next; comex_igroup_finalize(previous_group_list_item); free(previous_group_list_item); } /* ok, now free the world group, but not the world comm */ MPI_Group_free(&(group_list->group)); free(group_list); group_list = NULL; } ga-5.9.2/comex/src-template/groups.h000066400000000000000000000010731500715745200173300ustar00rootroot00000000000000/** * Private header file for comex groups backed by MPI_comm. * * The rest of the comex group functions are defined in the public comex.h. * * @author Jeff Daily */ #ifndef _COMEX_GROUPS_H_ #define _COMEX_GROUPS_H_ #include #include "comex.h" typedef struct group_link { struct group_link *next; comex_group_t id; MPI_Comm comm; MPI_Group group; } comex_igroup_t; extern void comex_group_init(); extern void comex_group_finalize(); extern comex_igroup_t* comex_get_igroup_from_group(comex_group_t group); #endif /* _COMEX_GROUPS_H_ */ ga-5.9.2/comex/testing/000077500000000000000000000000001500715745200147145ustar00rootroot00000000000000ga-5.9.2/comex/testing/perf.c000066400000000000000000000074361500715745200160260ustar00rootroot00000000000000#include #include #include #include #include #include #include "comex.h" static int me; static int nproc; #define PUT 0 #define GET 1 #define ACC 2 #define MAX_MESSAGE_SIZE 1024*1024*8 #define MEDIUM_MESSAGE_SIZE 8192 #define ITER_SMALL 100 #define ITER_LARGE 100 #define WARMUP 10 static void fill_array(double *arr, int count, int which); static void contig_test(size_t buffer_size, int op); double dclock() { struct timeval tv; gettimeofday(&tv, NULL); return(tv.tv_sec * 1.0e6 + (double)tv.tv_usec); } int main(int argc, char **argv) { comex_init_args(&argc, &argv); comex_group_rank(COMEX_GROUP_WORLD, &me); comex_group_size(COMEX_GROUP_WORLD, &nproc); /* This test only works for two processes */ assert(nproc == 2); if (0 == me) { printf("msg size (bytes) avg time (us) avg b/w (MB/sec)\n"); } if (0 == me) { printf("#PNNL comex Put Test\n"); } contig_test(MAX_MESSAGE_SIZE, PUT); if (0 == me) { printf("#PNNL comex Get Test\n"); } contig_test(MAX_MESSAGE_SIZE, GET); if (0 == me) { printf("#PNNL comex Accumulate Test\n"); } contig_test(MAX_MESSAGE_SIZE, ACC); comex_finalize(); MPI_Finalize(); return 0; } static void fill_array(double *arr, int count, int which) { int i; for (i = 0; i < count; i++) { arr[i] = i * 8.23 + which * 2.89; } } static void contig_test(size_t buffer_size, int op) { void **dst_ptr; void **put_buf; void **get_buf; double *times; dst_ptr = (void*)malloc(nproc * sizeof(void*)); put_buf = (void*)malloc(nproc * sizeof(void*)); get_buf = (void*)malloc(nproc * sizeof(void*)); times = (double*)malloc(nproc * sizeof(double)); comex_malloc(dst_ptr, buffer_size, COMEX_GROUP_WORLD); comex_malloc(put_buf, buffer_size, COMEX_GROUP_WORLD); comex_malloc(get_buf, buffer_size, COMEX_GROUP_WORLD); /* initialize what we're putting */ fill_array((double*)put_buf[me], buffer_size/sizeof(double), me); size_t msg_size; int dst = 1; double scale = 1.0; for (msg_size = 16; msg_size <= buffer_size; msg_size *= 2) { int j; int iter = msg_size > MEDIUM_MESSAGE_SIZE ? ITER_LARGE : ITER_SMALL; double t_start, t_end; if (0 == me) { for (j= 0; j < iter + WARMUP; ++j) { if (WARMUP == j) { t_start = dclock(); } switch (op) { case PUT: comex_put(put_buf[me], dst_ptr[dst], msg_size, dst, COMEX_GROUP_WORLD); break; case GET: comex_get(dst_ptr[dst], get_buf[me], msg_size, dst, COMEX_GROUP_WORLD); break; case ACC: comex_acc(COMEX_ACC_DBL, &scale, put_buf[me], dst_ptr[dst], msg_size, dst, COMEX_GROUP_WORLD); break; default: comex_error("oops", 1); } } } /* calculate total time and average time */ t_end = dclock(); comex_barrier(COMEX_GROUP_WORLD); if (0 == me) { printf("%zu\t\t%f\t\t%f\n", msg_size, ((t_end - t_start))/iter, msg_size*iter/((t_end - t_start))); } } comex_free(dst_ptr[me], COMEX_GROUP_WORLD); comex_free(put_buf[me], COMEX_GROUP_WORLD); comex_free(get_buf[me], COMEX_GROUP_WORLD); free(dst_ptr); free(put_buf); free(get_buf); free(times); } ga-5.9.2/comex/testing/perf_amo.c000066400000000000000000000106441500715745200166550ustar00rootroot00000000000000/* Test Rmw Performance * The number of processes are increases from 2 to the number of * processes present in the job */ #include #include #include #include #include #include #include "comex.h" static int me; static int nproc; #define FETCH_AND_ADD 0 #define FETCH_AND_ADD_LONG 1 #define SWAP 2 #define SWAP_LONG 3 #define MAX_MESSAGE_SIZE 1024 #define MEDIUM_MESSAGE_SIZE 8192 #define ITER_SMALL 10000 #define ITER_LARGE 10000 #define WARMUP 20 static void fill_array(double *arr, int count, int which); static void rmw_test(size_t buffer_size, int op); double dclock() { struct timeval tv; gettimeofday(&tv, NULL); return(tv.tv_sec * 1.0e6 + (double)tv.tv_usec); } int main(int argc, char **argv) { comex_init_args(&argc, &argv); comex_group_rank(COMEX_GROUP_WORLD, &me); comex_group_size(COMEX_GROUP_WORLD, &nproc); /* This test only works for two processes */ if (0 == me) { printf("#Processes avg time (us)\n"); printf("\n\n"); } if (0 == me) { printf("#PNNL armci Rmw-Fetch and Add Long Test\n"); printf("\n\n"); } rmw_test(MAX_MESSAGE_SIZE, FETCH_AND_ADD_LONG); if (0 == me) printf("\n\n"); if (0 == me) { printf("#PNNL armci Rmw-Fetch and Add Test\n"); } rmw_test(MAX_MESSAGE_SIZE, FETCH_AND_ADD); if (0 == me) printf("\n\n"); if (0 == me) { printf("#PNNL armci Rmw-Swap Long Test\n"); } rmw_test(MAX_MESSAGE_SIZE, SWAP_LONG); if (0 == me) printf("\n\n"); if (0 == me) { printf("#PNNL armci Rmw-Swap Test\n"); } rmw_test(MAX_MESSAGE_SIZE, SWAP); if (0 == me) printf("\n\n"); comex_finalize(); MPI_Finalize(); return 0; } static void fill_array(double *arr, int count, int which) { int i; for (i = 0; i < count; i++) { arr[i] = i * 8.23 + which * 2.89; } } static void rmw_test(size_t buffer_size, int op) { void **dst_ptr; void **put_buf; void **get_buf; double *times; dst_ptr = (void*)malloc(nproc * sizeof(void*)); put_buf = (void*)malloc(nproc * sizeof(void*)); get_buf = (void*)malloc(nproc * sizeof(void*)); times = (double*)malloc(nproc * sizeof(double)); comex_malloc(dst_ptr, buffer_size, COMEX_GROUP_WORLD); comex_malloc(put_buf, buffer_size, COMEX_GROUP_WORLD); comex_malloc(get_buf, buffer_size, COMEX_GROUP_WORLD); /* initialize what we're putting */ fill_array((double*)put_buf[me], buffer_size/sizeof(double), me); /* All processes perform Rmw on process 0*/ int dst = 0; double t_start, t_end; int j; int iter = ITER_LARGE; int part_proc; for (part_proc = 2; part_proc <= nproc; part_proc *= 2) { if (me < part_proc) { for (j= 0; j < iter + WARMUP; ++j) { if (WARMUP == j) { t_start = dclock(); } switch (op) { case FETCH_AND_ADD: comex_rmw(COMEX_FETCH_AND_ADD, put_buf[me], dst_ptr[dst], 1, dst, COMEX_GROUP_WORLD); break; case FETCH_AND_ADD_LONG: comex_rmw(COMEX_FETCH_AND_ADD_LONG, put_buf[me], dst_ptr[dst], 1, dst, COMEX_GROUP_WORLD); break; case SWAP: comex_rmw(COMEX_SWAP, put_buf[me], dst_ptr[dst], 1, dst, COMEX_GROUP_WORLD); break; case SWAP_LONG: comex_rmw(COMEX_SWAP_LONG, put_buf[me], dst_ptr[dst], 1, dst, COMEX_GROUP_WORLD); break; default: comex_error("oops", 1); } } } comex_barrier(COMEX_GROUP_WORLD); /* calculate total time and average time */ t_end = dclock(); if (0 == me) { printf("%5d\t\t%6.2f\n", part_proc, ((t_end - t_start))/iter); } } comex_free(dst_ptr[me], COMEX_GROUP_WORLD); comex_free(put_buf[me], COMEX_GROUP_WORLD); comex_free(get_buf[me], COMEX_GROUP_WORLD); free(dst_ptr); free(put_buf); free(get_buf); free(times); } ga-5.9.2/comex/testing/perf_contig.c000066400000000000000000000074251500715745200173670ustar00rootroot00000000000000#include #include #include #include #include #include #include "comex.h" static int me; static int nproc; #define PUT 0 #define GET 1 #define ACC 2 #define MAX_MESSAGE_SIZE 1024*1024 #define MEDIUM_MESSAGE_SIZE 8192 #define ITER_SMALL 1000 #define ITER_LARGE 100 #define WARMUP 20 static void fill_array(double *arr, int count, int which); static void contig_test(size_t buffer_size, int op); double dclock() { struct timeval tv; gettimeofday(&tv, NULL); return(tv.tv_sec * 1.0e6 + (double)tv.tv_usec); } int main(int argc, char **argv) { comex_init_args(&argc, &argv); comex_group_rank(COMEX_GROUP_WORLD, &me); comex_group_size(COMEX_GROUP_WORLD, &nproc); /* This test only works for two processes */ assert(nproc == 2); if (0 == me) { printf("msg size (bytes) avg time (us) avg b/w (MB/sec)\n"); } printf("\n\n"); if (0 == me) { printf("#PNNL armci Put Test\n"); } contig_test(MAX_MESSAGE_SIZE, PUT); printf("\n\n"); if (0 == me) { printf("#PNNL armci Get Test\n"); } contig_test(MAX_MESSAGE_SIZE, GET); if (0 == me) { printf("#PNNL armci Accumulate Test\n"); } contig_test(MAX_MESSAGE_SIZE, ACC); printf("\n\n"); comex_finalize(); MPI_Finalize(); return 0; } static void fill_array(double *arr, int count, int which) { int i; for (i = 0; i < count; i++) { arr[i] = i * 8.23 + which * 2.89; } } static void contig_test(size_t buffer_size, int op) { void **dst_ptr; void **put_buf; void **get_buf; double *times; dst_ptr = (void*)malloc(nproc * sizeof(void*)); put_buf = (void*)malloc(nproc * sizeof(void*)); get_buf = (void*)malloc(nproc * sizeof(void*)); times = (double*)malloc(nproc * sizeof(double)); comex_malloc(dst_ptr, buffer_size, COMEX_GROUP_WORLD); comex_malloc(put_buf, buffer_size, COMEX_GROUP_WORLD); comex_malloc(get_buf, buffer_size, COMEX_GROUP_WORLD); /* initialize what we're putting */ fill_array((double*)put_buf[me], buffer_size/sizeof(double), me); size_t msg_size; int dst = 1; double scale = 1.0; for (msg_size = 16; msg_size <= buffer_size; msg_size *= 2) { int j; int iter = msg_size > MEDIUM_MESSAGE_SIZE ? ITER_LARGE : ITER_SMALL; double t_start, t_end; if (0 == me) { for (j= 0; j < iter + WARMUP; ++j) { if (WARMUP == j) { t_start = dclock(); } switch (op) { case PUT: comex_put(put_buf[me], dst_ptr[dst], msg_size, dst, COMEX_GROUP_WORLD); break; case GET: comex_get(dst_ptr[dst], get_buf[me], msg_size, dst, COMEX_GROUP_WORLD); break; case ACC: comex_acc(COMEX_ACC_DBL, &scale, put_buf[me], dst_ptr[dst], msg_size, dst, COMEX_GROUP_WORLD); break; default: comex_error("oops", 1); } } } comex_barrier(COMEX_GROUP_WORLD); /* calculate total time and average time */ t_end = dclock(); if (0 == me) { printf("%5zu\t\t%6.2f\t\t%10.2f\n", msg_size, ((t_end - t_start))/iter, msg_size*(nproc-1)*iter/((t_end - t_start))); } } comex_free(dst_ptr[me], COMEX_GROUP_WORLD); comex_free(put_buf[me], COMEX_GROUP_WORLD); comex_free(get_buf[me], COMEX_GROUP_WORLD); free(dst_ptr); free(put_buf); free(get_buf); free(times); } ga-5.9.2/comex/testing/perf_strided.c000066400000000000000000000123271500715745200175370ustar00rootroot00000000000000#include #include #include #include #include #include #include "comex.h" #include "stats.h" static int me; static int nproc; #define PUTS 0 #define GETS 1 #define ACCS 2 #define MAX_MESSAGE_SIZE 1024*1024 #define MEDIUM_MESSAGE_SIZE 8192 #define ITER_SMALL 100 #define ITER_LARGE 10 #define WARMUP 2 static void fill_array(double *arr, int count, int which); static void strided_test(size_t buffer_size, int op); double dclock() { struct timeval tv; gettimeofday(&tv, NULL); return(tv.tv_sec * 1.0e6 + (double)tv.tv_usec); } int main(int argc, char **argv) { comex_init_args(&argc, &argv); comex_group_rank(COMEX_GROUP_WORLD, &me); comex_group_size(COMEX_GROUP_WORLD, &nproc); /* This test only works for two processes */ assert(nproc == 2); if (0 == me) { printf("msg size (bytes) avg time (us) avg b/w (MB/sec)\n"); } if (0 == me) { printf("\n\n"); printf("#PNNL ComEx Put Strided Test\n"); } strided_test(MAX_MESSAGE_SIZE, PUTS); if (0 == me) { printf("\n\n"); printf("#PNNL ComEx Get Strided Test\n"); } strided_test(MAX_MESSAGE_SIZE, GETS); if (0 == me) { printf("\n\n"); printf("#PNNL ComEx Accumulate Strided Test\n"); } strided_test(MAX_MESSAGE_SIZE, ACCS); comex_finalize(); MPI_Finalize(); return 0; } static void fill_array(double *arr, int count, int which) { int i; for (i = 0; i < count; i++) { arr[i] = i; } } static void strided_test(size_t buffer_size, int op) { void **dst_ptr; void **put_buf; void **get_buf; double *times; stats_t stats_latency; stats_t stats_bandwidth; stats_clear(&stats_latency); stats_clear(&stats_bandwidth); dst_ptr = (void*)malloc(nproc * sizeof(void*)); put_buf = (void*)malloc(nproc * sizeof(void*)); get_buf = (void*)malloc(nproc * sizeof(void*)); times = (double*)malloc(nproc * sizeof(double)); comex_malloc(dst_ptr, buffer_size, COMEX_GROUP_WORLD); comex_malloc(put_buf, buffer_size, COMEX_GROUP_WORLD); comex_malloc(get_buf, buffer_size, COMEX_GROUP_WORLD); /* initialize what we're putting */ fill_array((double*)put_buf[me], buffer_size/sizeof(double), me); size_t msg_size; int dst = 1; double scale = 1; /* Information for strided data transfer */ int levels = 1; int count[2]; int stride[1]; size_t xdim, ydim; for (msg_size = 16; msg_size <= buffer_size; msg_size *= 2) { int j; int iter = msg_size > MEDIUM_MESSAGE_SIZE ? ITER_LARGE : ITER_SMALL; for (xdim = 8; xdim <= msg_size; xdim *=2 ) { ydim = msg_size / xdim; count[0] = xdim; count[1] = ydim; stride[0] = xdim; double t_start, t_end; if (0 == me) { for (j= 0; j < iter + WARMUP; ++j) { if (WARMUP == j) { t_start = dclock(); } switch (op) { case PUTS: comex_puts(put_buf[me], stride, dst_ptr[dst], stride, count, levels, dst, COMEX_GROUP_WORLD); break; case GETS: comex_gets(dst_ptr[dst], stride, get_buf[me], stride, count, levels, dst, COMEX_GROUP_WORLD); break; case ACCS: comex_accs(COMEX_ACC_DBL, (void *)&scale, put_buf[me], stride, dst_ptr[dst], stride, count, levels, dst, COMEX_GROUP_WORLD); break; default: comex_error("oops", 1); } } } comex_barrier(COMEX_GROUP_WORLD); /* calculate total time and average time */ t_end = dclock(); if (0 == me) { double latency = (t_end-t_start)/iter; double bandwidth = msg_size*(nproc-1)*iter/(t_end-t_start); printf("%5zu\t\t%6.2f\t\t%6.2f\t\t%zu\t\t%zu\n", msg_size, latency, bandwidth, xdim, ydim); stats_sample_value(&stats_latency, latency); stats_sample_value(&stats_bandwidth, bandwidth); } } } if (0 == me) { printf("Latency avg %6.2f +- %6.2f\n", stats_latency._mean, stats_stddev(&stats_latency)); printf("Latency min %6.2f\n", stats_latency._min); printf("Latency max %6.2f\n", stats_latency._max); printf("Bandwidth avg %6.2f +- %6.2f\n", stats_bandwidth._mean, stats_stddev(&stats_bandwidth)); printf("Bandwidth min %6.2f\n", stats_bandwidth._min); printf("Bandwidth max %6.2f\n", stats_bandwidth._max); } comex_free(dst_ptr[me], COMEX_GROUP_WORLD); comex_free(put_buf[me], COMEX_GROUP_WORLD); comex_free(get_buf[me], COMEX_GROUP_WORLD); free(dst_ptr); free(put_buf); free(get_buf); free(times); } ga-5.9.2/comex/testing/shift.c000066400000000000000000000123521500715745200162000ustar00rootroot00000000000000#include #include #include #include #include "comex.h" static int me; static int nproc; static int size[] = {2,4,8,16,32,64,128,256,512,1024,1024*1024,1024*1024*8,0}; /* 0 is sentinal */ #define PUT_FORWARD 0 #define PUT_BACKWARD 1 #define GET_FORWARD 2 #define GET_BACKWARD 3 #define ACC_FORWARD 4 #define ACC_BACKWARD 5 static void fill_array(double *arr, int count, int which); static void shift(size_t buffer_size, int op); int main(int argc, char **argv) { int i; comex_init_args(&argc, &argv); comex_group_rank(COMEX_GROUP_WORLD, &me); comex_group_size(COMEX_GROUP_WORLD, &nproc); if (0 == me) { printf("msg size (bytes) avg time (milliseconds) avg b/w (bytes/sec)\n"); } if (0 == me) { printf("shifting acc forward\n"); } for (i=0; size[i]!=0; ++i) { shift(size[i], ACC_FORWARD); } if (0 == me) { printf("shifting acc backward\n"); } for (i=0; size[i]!=0; ++i) { shift(size[i], ACC_BACKWARD); } if (0 == me) { printf("shifting put forward\n"); } for (i=0; size[i]!=0; ++i) { shift(size[i], PUT_FORWARD); } if (0 == me) { printf("shifting put backward\n"); } for (i=0; size[i]!=0; ++i) { shift(size[i], PUT_BACKWARD); } if (0 == me) { printf("shifting get forward\n"); } for (i=0; size[i]!=0; ++i) { shift(size[i], GET_FORWARD); } if (0 == me) { printf("shifting get backward\n"); } for (i=0; size[i]!=0; ++i) { shift(size[i], GET_BACKWARD); } comex_finalize(); MPI_Finalize(); return 0; } static void fill_array(double *arr, int count, int which) { int i; for (i = 0; i < count; i++) { arr[i] = i * 8.23 + which * 2.89; } } static void shift(size_t buffer_size, int op) { void **dst_ptr; void **put_buf; void **get_buf; double scale = 2.0; int i=0; double *times; double *result; double total_time=0; MPI_Comm comm = MPI_COMM_NULL; comex_group_comm(COMEX_GROUP_WORLD, &comm); dst_ptr = (void*)malloc(nproc * sizeof(void*)); put_buf = (void*)malloc(nproc * sizeof(void*)); get_buf = (void*)malloc(nproc * sizeof(void*)); times = (double*)malloc(nproc * sizeof(double)); result = (double*)malloc(nproc * sizeof(double)); comex_malloc(dst_ptr, buffer_size, COMEX_GROUP_WORLD); comex_malloc(put_buf, buffer_size, COMEX_GROUP_WORLD); comex_malloc(get_buf, buffer_size, COMEX_GROUP_WORLD); /* initialize what we're putting */ fill_array((double*)put_buf[me], buffer_size/sizeof(double), me); /* initialize time keepers */ (void)memset(times, 0, nproc*sizeof(double)); (void)memset(result, 0, nproc*sizeof(double)); times[me] = MPI_Wtime()*1.0e6; /* the shift */ switch (op) { case ACC_FORWARD: for (i=1; i /** http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance */ #ifdef __cplusplus extern "C" { #endif typedef struct stats { unsigned long _n; double _mean; double _M2; double _sum; double _min; double _max; } stats_t; static inline void stats_clear(stats_t *stats) { stats->_n = 0UL; stats->_mean = 0.0; stats->_M2 = 0.0; stats->_sum = 0.0; stats->_min = 0.0; stats->_max = 0.0; } static inline void stats_sample_value(stats_t *stats, const double x) { double delta = 0; /* extra stats */ stats->_sum = stats->_sum + x; if (0UL == stats->_n) { stats->_min = x; stats->_max = x; } else { stats->_min = stats->_min < x ? stats->_min : x; stats->_max = stats->_max > x ? stats->_max : x; } stats->_n = stats->_n + 1UL; delta = x - stats->_mean; stats->_mean = stats->_mean + delta/stats->_n; stats->_M2 = stats->_M2 + delta * (x - stats->_mean); } static inline double stats_variance(const stats_t * const stats) { return stats->_M2/(stats->_n-1); } static inline double stats_stddev(const stats_t * const stats) { return sqrt(stats_variance(stats)); } #ifdef __cplusplus } #endif #endif /* _STATS_H_ */ ga-5.9.2/comex/testing/test.c000066400000000000000000001411241500715745200160420ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: test.c,v 1.43.6.6 2007-08-30 22:59:27 manoj Exp $ */ #include #include #include #include #include #include #include #include "comex.h" #define DIM1 5 #define DIM2 3 #ifdef __sun /* Solaris has shared memory shortages in the default system configuration */ # define DIM3 6 # define DIM4 5 # define DIM5 4 #elif defined(__alpha__) # define DIM3 8 # define DIM4 5 # define DIM5 6 #else # define DIM3 8 # define DIM4 9 # define DIM5 7 #endif #define DIM6 3 #define DIM7 2 #define OFF 1 #define EDIM1 (DIM1+OFF) #define EDIM2 (DIM2+OFF) #define EDIM3 (DIM3+OFF) #define EDIM4 (DIM4+OFF) #define EDIM5 (DIM5+OFF) #define EDIM6 (DIM6+OFF) #define EDIM7 (DIM7+OFF) #define DIMS 4 #define MAXDIMS 7 #define MAX_DIM_VAL 50 #define LOOP 200 #define BASE 100. #define MAXPROC 128 #define TIMES 100 # define ELEMS 200 /***************************** macros ************************/ #define COPY(src, dst, bytes) memcpy((dst),(src),(bytes)) #define COMEX_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define COMEX_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define COMEX_ABS(a) (((a) <0) ? -(a) : (a)) /***************************** global data *******************/ int me, nproc; int work[MAXPROC]; /* work array for propagating addresses */ static void all_sum_int(int *x, int n) { MPI_Comm comm = MPI_COMM_NULL; MPI_Datatype mpi_type = MPI_INT; int rc = 0; void *result = NULL; MPI_Op mpi_op = MPI_SUM; comex_group_comm(COMEX_GROUP_WORLD, &comm); result = malloc(n*sizeof(int)); assert(result); comex_barrier(COMEX_GROUP_WORLD); rc = MPI_Allreduce(x, result, n, mpi_type, mpi_op, comm); assert(rc == MPI_SUCCESS); memcpy(x, result, sizeof(int) * n); free(result); } static void all_sum_long(long *x, int n) { MPI_Comm comm = MPI_COMM_NULL; MPI_Datatype mpi_type = MPI_LONG; int rc = 0; void *result = NULL; MPI_Op mpi_op = MPI_SUM; comex_group_comm(COMEX_GROUP_WORLD, &comm); result = malloc(n*sizeof(long)); assert(result); comex_barrier(COMEX_GROUP_WORLD); rc = MPI_Allreduce(x, result, n, mpi_type, mpi_op, comm); assert(rc == MPI_SUCCESS); memcpy(x, result, sizeof(long) * n); free(result); } static double timer() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000.0 + tv.tv_usec; } /*\ generate random range for a section of multidimensional array \*/ void get_range(int ndim, int dims[], int lo[], int hi[]) { int dim; for (dim = 0; dim < ndim; dim++) { int toss1, toss2; toss1 = rand() % dims[dim]; toss2 = rand() % dims[dim]; if (toss1 < toss2) { lo[dim] = toss1; hi[dim] = toss2; } else { hi[dim] = toss1; lo[dim] = toss2; } } } /*\ generates a new random range similar to the input range for an array with specified dimensions \*/ void new_range(int ndim, int dims[], int lo[], int hi[], int new_lo[], int new_hi[]) { int dim; for (dim = 0; dim < ndim; dim++) { int toss, range; int diff = hi[dim] - lo[dim] + 1; assert(diff <= dims[dim]); range = dims[dim] - diff; toss = (range > 0) ? rand() % range : lo[dim]; new_lo[dim] = toss; new_hi[dim] = toss + diff - 1; assert(new_hi[dim] < dims[dim]); assert(diff == (new_hi[dim] - new_lo[dim] + 1)); } } /*\ print range of ndim dimensional array with two strings before and after \*/ void print_range(char *pre, int ndim, int lo[], int hi[], char *post) { int i; printf("%s[", pre); for (i = 0; i < ndim; i++) { printf("%d:%d", lo[i], hi[i]); if (i == ndim - 1) { printf("] %s", post); } else { printf(","); } } } /*\ print subscript of ndim dimensional array with two strings before and after \*/ void print_subscript(char *pre, int ndim, int subscript[], char *post) { int i; printf("%s [", pre); for (i = 0; i < ndim; i++) { printf("%d", subscript[i]); if (i == ndim - 1) { printf("] %s", post); } else { printf(","); } } } /*\ print a section of a 2-D array of doubles \*/ void print_2D_double(double *a, int ld, int *lo, int *hi) { int i, j; for (i = lo[0]; i <= hi[0]; i++) { for (j = lo[1]; j <= hi[1]; j++) { printf("%13f ", a[ld*j+i]); } printf("\n"); } } /*\ initialize array: a[i,j,k,..]=i+100*j+10000*k+ ... \*/ void init(double *a, int ndim, int elems, int dims[]) { int idx[MAXDIMS]; int i, dim; for (i = 0; i < elems; i++) { int Index = i; double field, val; for (dim = 0; dim < ndim; dim++) { idx[dim] = Index % dims[dim]; Index /= dims[dim]; } field = 1.; val = 0.; for (dim = 0; dim < ndim; dim++) { val += field * idx[dim]; field *= BASE; } a[i] = val; /* printf("(%d,%d,%d)=%6.0f",idx[0],idx[1],idx[2],val); */ } } /*\ initialize complex array: real parts are a[i,j,k,..]=i+100*j+10000*k+ ... * all imaginary parts are 1 \*/ void cplx_init(double *a, int ndim, int elems, int dims[]) { int idx[MAXDIMS]; int i, dim; for (i = 0; i < elems; i++) { int Index = i; double field, val; for (dim = 0; dim < ndim; dim++) { idx[dim] = Index % dims[dim]; Index /= dims[dim]; } field = 1.; val = 0.; for (dim = 0; dim < ndim; dim++) { val += field * idx[dim]; field *= BASE; } a[2*i] = val; a[2*i+1] = 1.0; /* printf("(%d,%d,%d)=%6.0f",idx[0],idx[1],idx[2],val); */ } } /*\ compute Index from subscript * assume that first subscript component changes first \*/ int Index(int ndim, int subscript[], int dims[]) { int idx = 0, i, factor = 1; for (i = 0; i < ndim; i++) { idx += subscript[i] * factor; factor *= dims[i]; } return idx; } void update_subscript(int ndim, int subscript[], int lo[], int hi[]) { int i; for (i = 0; i < ndim; i++) { if (subscript[i] < hi[i]) { subscript[i]++; return; } subscript[i] = lo[i]; } } void compare_patches(double eps, int ndim, double *patch1, int lo1[], int hi1[], int dims1[], double *patch2, int lo2[], int hi2[], int dims2[]) { int i, j, elems = 1; int subscr1[MAXDIMS], subscr2[MAXDIMS]; double diff, max; int offset1, offset2; for (i = 0; i < ndim; i++) { /* count # of elements & verify consistency of both patches */ int diff = hi1[i] - lo1[i]; assert(diff == (hi2[i] - lo2[i])); assert(diff < dims1[i]); assert(diff < dims2[i]); elems *= diff + 1; subscr1[i] = lo1[i]; subscr2[i] = lo2[i]; } /* compare element values in both patches */ offset1 = Index(ndim, subscr1, dims1); offset2 = Index(ndim, subscr2, dims2); for (j = 0; j < elems; j++) { int idx1, idx2; idx1 = Index(ndim, subscr1, dims1); /* calculate element Index from a subscript */ idx2 = Index(ndim, subscr2, dims2); idx1 -= offset1; idx2 -= offset2; diff = patch1[idx1] - patch2[idx2]; max = COMEX_MAX(COMEX_ABS(patch1[idx1]), COMEX_ABS(patch2[idx2])); if (max == 0. || max < eps) { max = 1.; } if (eps < COMEX_ABS(diff) / max) { char msg[48]; sprintf(msg, "(proc=%d):%f", me, patch1[idx1]); print_subscript("ERROR: a", ndim, subscr1, msg); sprintf(msg, "%f\n", patch2[idx2]); print_subscript(" b", ndim, subscr2, msg); fflush(stdout); sleep(1); comex_error("Bailing out", 0); } { /* update subscript for the patches */ update_subscript(ndim, subscr1, lo1, hi1); update_subscript(ndim, subscr2, lo2, hi2); } } /* make sure we reached upper limit */ /*for(i=0;i "); print_range("remote", ndim, loB, hiB, "-> "); print_range("local", ndim, loC, hiC, "\n"); } idx1 = Index(ndim, loA, dimsA); idx2 = Index(ndim, loB, dimsB); idx3 = Index(ndim, loC, dimsA); for (j = 0; j < ndim; j++) { count[j] = hiA[j] - loA[j] + 1; } count[0] *= sizeof(double); /* convert range to bytes at stride level zero */ (void)comex_puts((double *)a + idx1, strideA, (double *)b[proc] + idx2, strideB, count, ndim - 1, proc, COMEX_GROUP_WORLD); /* sleep(1);*/ /* printf("%d: a=(%x,%f) b=(%x,%f)\n",me,idx1 + (double*)a,*(idx1 + (double*)a),idx2 + (double*)b,*(idx2 + (double*)b));*/ /* fflush(stdout);*/ /* sleep(1);*/ /* note that we do not need comex_fence here since * consectutive operations targeting the same process are ordered */ (void)comex_gets((double *)b[proc] + idx2, strideB, (double *)c + idx3, strideA, count, ndim - 1, proc, COMEX_GROUP_WORLD); compare_patches(0., ndim, (double *)a + idx1, loA, hiA, dimsA, (double *)c + idx3, loC, hiC, dimsA); } free(c); destroy_array(b); free(a); } int nloA[MAXDIMS+1][MAXDIMS], nhiA[MAXDIMS+1][MAXDIMS]; int nloB[MAXDIMS+1][MAXDIMS], nhiB[MAXDIMS+1][MAXDIMS]; int nloC[MAXDIMS+1][MAXDIMS], nhiC[MAXDIMS+1][MAXDIMS]; int get_next_RRproc(int initialize, int ndim) { static int distance; int proc; if (initialize) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } if (nproc == 1) { distance = 0; } return(0); } /*send it to a different process everytime*/ proc = (me <= ((nproc % 2 == 0) ? ((nproc / 2) - 1) : (nproc / 2))) ? (me + distance) : (me - distance); if ((nproc % 2) != 0 && me == (nproc / 2)) { proc = me; } if (distance != 0) { if (me < (nproc / 2)) { distance++; if ((me + distance) >= nproc) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } distance -= me; } } else { distance--; if ((me - distance) >= (nproc / 2)) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } distance = distance + (me - distance); } } if (ndim != 1 && MAXDIMS > nproc && (ndim % (nproc / 2) == 0)) { distance = nproc / 2; if ((nproc % 2) != 0) { distance++; } } } return(proc); } void test_nbdim() { int elems = 1, elems1 = 1; int i, j, proc, ndim, rc; void *b[MAXDIMS+1][MAXPROC]; void *a[MAXDIMS+1], *c[MAXDIMS+1]; comex_request_t hdl_put[MAXDIMS+1], hdl_get[MAXDIMS+1]; int idx1 = 0, idx2 = 0, idx3 = 0; /* create shared and local arrays */ for (ndim = 1; ndim <= MAXDIMS; ndim++) { elems1 *= dimsB[ndim-1]; elems *= dimsA[ndim-1]; rc = comex_malloc(b[ndim], sizeof(double) * elems1, COMEX_GROUP_WORLD); assert(rc == 0); assert(b[ndim][me]); a[ndim] = malloc(sizeof(double) * elems); assert(a[ndim]); c[ndim] = malloc(sizeof(double) * elems); assert(c[ndim]); init(a[ndim], ndim, elems, dimsA); } comex_fence_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); (void)get_next_RRproc(1, 0); for (ndim = 1; ndim <= MAXDIMS; ndim++) { strideA[0] = sizeof(double); strideB[0] = sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } } proc = get_next_RRproc(0, ndim); get_range(ndim, dimsA, nloA[ndim], nhiA[ndim]); new_range(ndim, dimsB, nloA[ndim], nhiA[ndim], nloB[ndim], nhiB[ndim]); new_range(ndim, dimsA, nloA[ndim], nhiA[ndim], nloC[ndim], nhiC[ndim]); if (me == 0) { print_range("local", ndim, nloA[ndim], nhiA[ndim], "-> "); print_range("remote", ndim, nloB[ndim], nhiB[ndim], "-> "); print_range("local", ndim, nloC[ndim], nhiC[ndim], "\n"); fflush(stdout); sleep(1); } idx1 = Index(ndim, nloA[ndim], dimsA); idx2 = Index(ndim, nloB[ndim], dimsB); idx3 = Index(ndim, nloC[ndim], dimsA); for (j = 0; j < ndim; j++) { count[j] = nhiA[ndim][j] - nloA[ndim][j] + 1; } count[0] *= sizeof(double); if (ndim == 1) { (void)comex_nbput((double *)a[ndim] + idx1, (double *)b[ndim][proc] + idx2, count[0], proc, COMEX_GROUP_WORLD, (hdl_put + ndim)); } else { (void)comex_nbputs((double *)a[ndim] + idx1, strideA, (double *)b[ndim][proc] + idx2, strideB, count, ndim - 1, proc, COMEX_GROUP_WORLD, (hdl_put + ndim)); } } sleep(5); comex_barrier(COMEX_GROUP_WORLD); /*before we do gets, we have to make sure puts are complete on the remote processor*/ for (ndim = 1; ndim <= MAXDIMS; ndim++) { comex_wait(hdl_put + ndim); } comex_barrier(COMEX_GROUP_WORLD); comex_fence_all(COMEX_GROUP_WORLD); (void)get_next_RRproc(1, 0); for (ndim = 1; ndim <= MAXDIMS; ndim++) { strideA[0] = sizeof(double); strideB[0] = sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } } /*send it to a different process everytime*/ proc = get_next_RRproc(0, ndim); idx1 = Index(ndim, nloA[ndim], dimsA); idx2 = Index(ndim, nloB[ndim], dimsB); idx3 = Index(ndim, nloC[ndim], dimsA); for (j = 0; j < ndim; j++) { count[j] = nhiA[ndim][j] - nloA[ndim][j] + 1; } count[0] *= sizeof(double); if (ndim == 1) { (void)comex_nbget((double *)b[ndim][proc] + idx2, (double *)c[ndim] + idx3, count[0], proc, COMEX_GROUP_WORLD, (hdl_get + ndim)); } else { (void)comex_nbgets((double *)b[ndim][proc] + idx2, strideB, (double *)c[ndim] + idx3, strideA, count, ndim - 1, proc, COMEX_GROUP_WORLD, (hdl_get + ndim)); } } comex_barrier(COMEX_GROUP_WORLD); if (me == 0) { printf("Now waiting for all non-blocking calls and verifying data...\n"); fflush(stdout); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { comex_wait(hdl_get + ndim); idx1 = Index(ndim, nloA[ndim], dimsA); idx2 = Index(ndim, nloB[ndim], dimsB); idx3 = Index(ndim, nloC[ndim], dimsA); compare_patches(0., ndim, (double *)a[ndim] + idx1, nloA[ndim], nhiA[ndim], dimsA, (double *)c[ndim] + idx3, nloC[ndim], nhiC[ndim], dimsA); } if (me == 0) { printf("OK\n"); fflush(stdout); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { destroy_array(b[ndim]); free(c[ndim]); free(a[ndim]); } } #define PTR_ARR_LEN 10 #define VLOOP 50 #define VEC_ELE_LEN 20 /*number of doubles in each dimention*/ #define GIOV_ARR_LEN 9 void verify_vector_data(double *data, int procs, int isput, int datalen) { double facto = 2.89; int i, j = 0, k = 0, kc = 0, dst = 0; if (isput) { facto = 1.89; } for (i = 0; i < datalen; i++) { if (dst != me) if (COMEX_ABS((data[i] - (me + facto + dst)*((kc + 1)*(j % PTR_ARR_LEN + 1)))) > 0.001) { printf("\n%d:while verifying data of a op from proc=%d ", me, dst); printf("giov index=%d ptr_arr_index=%d \n :element index=%d", kc, (j % PTR_ARR_LEN), k); printf(" elem was supposed to be %f but is %f", (me + facto + dst)*((kc + 1)*(j % PTR_ARR_LEN + 1)) , data[i]); fflush(stdout); sleep(1); comex_error("vector non-blocking failed", 0); } k++; if (k == VEC_ELE_LEN) { j++; k = 0; if (j % PTR_ARR_LEN == 0) { kc++; if ((kc % GIOV_ARR_LEN) == 0) { kc = 0; dst++; } } } } } void test_vec_small() { double *getdst; double **putsrc; comex_giov_t dsc[MAXPROC*GIOV_ARR_LEN]; void **psrc; /*arrays of pointers to be used by giov_t*/ void **pdst; void *getsrc[MAXPROC]; /*to allocate mem via comex_malloc*/ void *putdst[MAXPROC]; /*to allocate mem via comex_malloc*/ comex_request_t hdl_put[MAXPROC], hdl_get[MAXPROC]; int i = 0, j = 0, k = 0, kc = 0, kcold = 0, rc, dstproc, dst = 0; int lenpergiov; lenpergiov = PTR_ARR_LEN * VEC_ELE_LEN; rc = comex_malloc(getsrc, sizeof(double) * nproc * GIOV_ARR_LEN * lenpergiov, COMEX_GROUP_WORLD); assert(rc == 0); assert(getsrc[me]); rc = comex_malloc(putdst, sizeof(double) * nproc * GIOV_ARR_LEN * lenpergiov, COMEX_GROUP_WORLD); assert(rc == 0); assert(putdst[me]); /*first malloc for getdst and putsrc, both are 2d arrays*/ getdst = (double *)malloc(sizeof(double) * nproc * GIOV_ARR_LEN * lenpergiov); putsrc = (double **)malloc(sizeof(double *) * nproc * GIOV_ARR_LEN * PTR_ARR_LEN); assert(getdst); assert(putsrc); for (i = 0; i < nproc * GIOV_ARR_LEN * PTR_ARR_LEN; i++) { putsrc[i] = (double *)malloc(sizeof(double) * VEC_ELE_LEN); assert(putsrc[i]); } /*allocating memory for psrc and pdst*/ psrc = (void **)malloc(sizeof(void *) * PTR_ARR_LEN * nproc * GIOV_ARR_LEN); pdst = (void **)malloc(sizeof(void *) * PTR_ARR_LEN * nproc * GIOV_ARR_LEN); assert(pdst); assert(psrc); for (i = 0; i < nproc * lenpergiov * GIOV_ARR_LEN; i++) { putsrc[j][k] = (me + 1.89 + dst) * ((kc + 1) * ((j % PTR_ARR_LEN) + 1)); ((double *)getsrc[me])[i] = (me + 2.89 + dst) * ((kc + 1) * (j % PTR_ARR_LEN + 1)); k++; if (k == VEC_ELE_LEN) { j++; k = 0; if ((j % PTR_ARR_LEN) == 0) { kc++; if ((kc % GIOV_ARR_LEN) == 0) { kc = 0; dst++; } } } } /*********************Testing NbPutV*********************************/ i = 0; j = 0; k = 0; kc = 0; dstproc = me; for (i = 0; i < nproc - 1; i++) { dstproc++; if (dstproc == nproc) { dstproc = 0; } for (j = 0; j < GIOV_ARR_LEN; j++) { kcold = kc; for (k = 0; k < PTR_ARR_LEN; k++, kc++) { double *ptr; psrc[kc] = (void *)putsrc[PTR_ARR_LEN*(dstproc*GIOV_ARR_LEN+j)+k]; ptr = (double *)putdst[dstproc]; pdst[kc] = (void *)(ptr + lenpergiov * (GIOV_ARR_LEN * me + j) + k * VEC_ELE_LEN); } dsc[j].bytes = VEC_ELE_LEN * sizeof(double); dsc[j].src = &psrc[kcold]; dsc[j].dst = &pdst[kcold]; dsc[j].count = PTR_ARR_LEN; } if ((rc = comex_nbputv(dsc, GIOV_ARR_LEN, dstproc, COMEX_GROUP_WORLD, hdl_put + dstproc))) { comex_error("putv failed", rc); } } if (me == 0) { printf("\n\tNow veryfying the vector put data for correctness"); } for (i = 0; i < nproc; i++)if (i != me) { comex_wait(hdl_put + i); } sleep(1); comex_barrier(COMEX_GROUP_WORLD); comex_fence_all(COMEX_GROUP_WORLD); verify_vector_data((double *)putdst[me], nproc, 1, nproc * GIOV_ARR_LEN * lenpergiov); if (me == 0) { printf("\n\tPuts OK\n"); } /****************Done Testing NbPutV*********************************/ /*********************Testing NbGetV*********************************/ i = 0; j = 0; k = 0; kc = 0; dstproc = me; for (i = 0; i < nproc - 1; i++) { dstproc++; if (dstproc == nproc) { dstproc = 0; } for (j = 0; j < GIOV_ARR_LEN; j++) { kcold = kc; for (k = 0; k < PTR_ARR_LEN; k++, kc++) { double *ptr; ptr = getdst; pdst[kc] = (void *)(ptr + lenpergiov * (dstproc * GIOV_ARR_LEN + j) + k * VEC_ELE_LEN); ptr = (double *)(getsrc[dstproc]); psrc[kc] = (void *)(ptr + lenpergiov * (me * GIOV_ARR_LEN + j) + k * VEC_ELE_LEN); } dsc[j].bytes = VEC_ELE_LEN * sizeof(double); dsc[j].src = &psrc[kcold]; dsc[j].dst = &pdst[kcold]; dsc[j].count = PTR_ARR_LEN; } if ((rc = comex_nbgetv(dsc, GIOV_ARR_LEN, dstproc, COMEX_GROUP_WORLD, hdl_get + dstproc))) { comex_error("putv failed", rc); } } if (me == 0) { printf("\n\tNow veryfying the vector get data for correctness"); } for (i = 0; i < nproc; i++)if (i != me) { comex_wait(hdl_get + i); } sleep(1); comex_barrier(COMEX_GROUP_WORLD); verify_vector_data((double *)getdst, nproc, 0, nproc * GIOV_ARR_LEN * lenpergiov); if (me == 0) { printf("\n\tGets OK\n"); } /****************Done Testing NbGetV*********************************/ free(pdst); free(psrc); free(getdst); for (i = 0; i < nproc * GIOV_ARR_LEN * PTR_ARR_LEN; i++) { free(putsrc[i]); } free(putsrc); comex_free(getsrc[me], COMEX_GROUP_WORLD); comex_free(putdst[me], COMEX_GROUP_WORLD); } void GetPermutedProcList(int *ProcList) { int i, iswap, temp; if (nproc > MAXPROC) { comex_error("permute_proc: nproc to big ", nproc); } /* initialize list */ for (i = 0; i < nproc; i++) { ProcList[i] = i; } if (nproc == 1) { return; } /* every process generates different random sequence */ (void)srand((unsigned)me); /* list permutation generated by random swapping */ for (i = 0; i < nproc; i++) { iswap = (int)(rand() % nproc); temp = ProcList[iswap]; ProcList[iswap] = ProcList[i]; ProcList[i] = temp; } } /*\ Atomic Accumulate test: remote += alpha*local * Every process/or has its patch of array b updated TIMES*NPROC times. * The sequence of updates is random: everybody uses a randomly permuted list * and accumulate is non-collective (of-course) \*/ void test_acc(int ndim) { int dim, elems; int i, proc; void *b[MAXPROC]; void *a, *c; double alpha = 0.1, scale; int idx1, idx2; int *proclist = work; elems = 1; strideA[0] = sizeof(double); strideB[0] = sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } elems *= dimsA[i]; /* set up patch coordinates: same on every processor */ loA[i] = 0; hiA[i] = loA[i] + 1; loB[i] = dimsB[i] - 2; hiB[i] = loB[i] + 1; count[i] = hiA[i] - loA[i] + 1; } /* create shared and local arrays */ create_array(b, sizeof(double), ndim, dimsB); a = malloc(sizeof(double) * elems); assert(a); c = malloc(sizeof(double) * elems); assert(c); init(a, ndim, elems, dimsA); if (me == 0) { printf("--------array[%d", dimsA[0]); for (dim = 1; dim < ndim; dim++) { printf(",%d", dimsA[dim]); } printf("]--------\n"); } GetPermutedProcList(proclist); idx1 = Index(ndim, loA, dimsA); idx2 = Index(ndim, loB, dimsB); count[0] *= sizeof(double); /* convert range to bytes at stride level zero */ /* initialize all elements of array b to zero */ elems = 1; for (i = 0; i < ndim; i++) { elems *= dimsB[i]; } for (i = 0; i < elems; i++) { ((double *)b[me])[i] = 0.; } sleep(1); if (me == 0) { print_range("patch", ndim, loA, hiA, " -> "); print_range("patch", ndim, loB, hiB, "\n"); fflush(stdout); } comex_fence_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); for (i = 0; i < TIMES * nproc; i++) { proc = proclist[i%nproc]; (void)comex_accs(COMEX_ACC_DBL, &alpha, (double *)a + idx1, strideA, (double *)b[proc] + idx2, strideB, count, ndim - 1, proc, COMEX_GROUP_WORLD); } /* sleep(9);*/ comex_fence_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); /* copy my patch into local array c */ (void)comex_gets((double *)b[me] + idx2, strideB, (double *)c + idx1, strideA, count, ndim - 1, me, COMEX_GROUP_WORLD); scale = alpha * TIMES * nproc; scale_patch(scale, ndim, (double *)a + idx1, loA, hiA, dimsA); compare_patches(.0001, ndim, (double *)a + idx1, loA, hiA, dimsA, (double *)c + idx1, loA, hiA, dimsA); comex_barrier(COMEX_GROUP_WORLD); if (0 == me) { printf(" OK\n\n"); fflush(stdout); } free(c); destroy_array(b); free(a); } void test_cplx_acc(int ndim) { int dim, elems; int i, proc; void *b[MAXPROC]; void *a, *c; double alpha[2], scale[2]; int idx1, idx2; int *proclist = work; alpha[0] = 0.0; alpha[1] = 0.1; elems = 1; strideA[0] = 2*sizeof(double); strideB[0] = 2*sizeof(double); for (i = 0; i < ndim; i++) { strideA[i] *= dimsA[i]; strideB[i] *= dimsB[i]; if (i < ndim - 1) { strideA[i+1] = strideA[i]; strideB[i+1] = strideB[i]; } elems *= dimsA[i]; /* set up patch coordinates: same on every processor */ loA[i] = 0; hiA[i] = loA[i] + 1; loB[i] = dimsB[i] - 2; hiB[i] = loB[i] + 1; count[i] = hiA[i] - loA[i] + 1; } /* create shared and local arrays */ create_array(b, 2*sizeof(double), ndim, dimsB); a = malloc(2*sizeof(double) * elems); assert(a); c = malloc(2*sizeof(double) * elems); assert(c); cplx_init(a, ndim, elems, dimsA); if (me == 0) { printf("--------array[%d", dimsA[0]); for (dim = 1; dim < ndim; dim++) { printf(",%d", dimsA[dim]); } printf("]--------\n"); } GetPermutedProcList(proclist); idx1 = Index(ndim, loA, dimsA); idx2 = Index(ndim, loB, dimsB); count[0] *= 2*sizeof(double); /* convert range to bytes at stride level zero */ /* initialize all elements of array b to zero */ elems = 1; for (i = 0; i < ndim; i++) { elems *= dimsB[i]; } for (i = 0; i < elems; i++) { ((double *)b[me])[2*i] = 0.; ((double *)b[me])[2*i+1] = 0.; } sleep(1); if (me == 0) { print_range("patch", ndim, loA, hiA, " -> "); print_range("patch", ndim, loB, hiB, "\n"); fflush(stdout); } comex_fence_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); for (i = 0; i < TIMES * nproc; i++) { proc = proclist[i%nproc]; (void)comex_accs(COMEX_ACC_DCP, alpha, (double *)a + 2*idx1, strideA, (double *)b[proc] + 2*idx2, strideB, count, ndim - 1, proc, COMEX_GROUP_WORLD); } /* sleep(9);*/ comex_fence_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); /* copy my patch into local array c */ (void)comex_gets((double *)b[me] + 2*idx2, strideB, (double *)c + 2*idx1, strideA, count, ndim - 1, me, COMEX_GROUP_WORLD); scale[0] = alpha[0] * TIMES * nproc; scale[1] = alpha[1] * TIMES * nproc; scale_cplx_patch(scale, ndim, (double *)a + idx1, loA, hiA, dimsA); compare_cplx_patches(.0001, ndim, (double *)a + idx1, loA, hiA, dimsA, (double *)c + idx1, loA, hiA, dimsA); comex_barrier(COMEX_GROUP_WORLD); if (0 == me) { printf(" OK\n\n"); fflush(stdout); } free(c); destroy_array(b); free(a); } /*************************** vector interface *********************************\ * tests vector interface for transfers of triangular sections of a 2-D array * ******************************************************************************/ void test_vector() { int dim, elems, ndim, cols, rows, mrc; int i, proc, loop; int rc; int idx1, idx3; void *b[MAXPROC]; void *a, *c; comex_giov_t dsc[MAX_DIM_VAL]; void *psrc[MAX_DIM_VAL]; void *pdst[MAX_DIM_VAL]; elems = 1; ndim = 2; for (i = 0; i < ndim; i++) { dimsA[i] = MAX_DIM_VAL; dimsB[i] = MAX_DIM_VAL + 1; elems *= dimsA[i]; } /* create shared and local arrays */ create_array(b, sizeof(double), ndim, dimsB); a = malloc(sizeof(double) * elems); assert(a); c = malloc(sizeof(double) * elems); assert(c); init(a, ndim, elems, dimsA); if (me == 0) { printf("--------array[%d", dimsA[0]); for (dim = 1; dim < ndim; dim++) { printf(",%d", dimsA[dim]); } printf("]--------\n"); } sleep(1); for (loop = 0; loop < LOOP; loop++) { get_range(ndim, dimsA, loA, hiA); new_range(ndim, dimsB, loA, hiA, loB, hiB); new_range(ndim, dimsA, loA, hiA, loC, hiC); proc = nproc - 1 - me; if (me == 0) { print_range("local", ndim, loA, hiA, "-> "); print_range("remote", ndim, loB, hiB, "-> "); print_range("local", ndim, loC, hiC, "\n"); } /* printf("array at source\n");*/ /* print_2D_double((double *)a, dimsA[0], loA, hiA);*/ cols = hiA[1] - loA[1] + 1; rows = hiA[0] - loA[0] + 1; mrc = COMEX_MIN(cols, rows); /* generate a data descriptor for a lower-triangular patch */ for (i = 0; i < mrc; i++) { int ij[2]; int idx; ij[0] = loA[0] + i; ij[1] = loA[1] + i; idx = Index(ndim, ij, dimsA); psrc[i] = (double *)a + idx; ij[0] = loB[0] + i; ij[1] = loB[1] + i; idx = Index(ndim, ij, dimsB); pdst[i] = (double *)b[proc] + idx; dsc[i].bytes = (rows - i) * sizeof(double); dsc[i].src = &psrc[i]; dsc[i].dst = &pdst[i]; /* assume each element different in size (not true in rectangular patches) */ dsc[i].count = 1; } if ((rc = comex_putv(dsc, mrc, proc, COMEX_GROUP_WORLD))) { comex_error("putv failed ", rc); } /* printf("array at destination\n");*/ /* print_2D_double((double *)b[proc], dimsB[0], loB, hiB);*/ /* generate a data descriptor for the upper-triangular patch */ /* there is one less element since diagonal is excluded */ for (i = 1; i < cols; i++) { int ij[2]; ij[0] = loA[0]; ij[1] = loA[1] + i; psrc[i-1] = (double *)a + Index(ndim, ij, dimsA); ij[0] = loB[0]; ij[1] = loB[1] + i; pdst[i-1] = (double *)b[proc] + Index(ndim, ij, dimsB); mrc = COMEX_MIN(i, rows); dsc[i-1].bytes = mrc * sizeof(double); dsc[i-1].src = &psrc[i-1]; dsc[i-1].dst = &pdst[i-1]; /* assume each element different in size (not true in rectangular patches) */ dsc[i-1].count = 1; } if ((cols - 1))if ((rc = comex_putv(dsc, cols - 1, proc, COMEX_GROUP_WORLD))) { comex_error("putv(2) failed ", rc); } /* we get back entire rectangular patch */ for (i = 0; i < cols; i++) { int ij[2]; ij[0] = loB[0]; ij[1] = loB[1] + i; psrc[i] = (double *)b[proc] + Index(ndim, ij, dimsB); ij[0] = loC[0]; ij[1] = loC[1] + i; pdst[i] = (double *)c + Index(ndim, ij, dimsA); } dsc[0].bytes = rows * sizeof(double); dsc[0].src = psrc; dsc[0].dst = pdst; dsc[0].count = cols; /* note that we do not need comex_fence here since * consecutive operations targeting the same process are ordered */ if ((rc = comex_getv(dsc, 1, proc, COMEX_GROUP_WORLD))) { comex_error("getv failed ", rc); } idx1 = Index(ndim, loA, dimsA); idx3 = Index(ndim, loC, dimsA); compare_patches(0., ndim, (double *)a + idx1, loA, hiA, dimsA, (double *)c + idx3, loC, hiC, dimsA); } free(c); destroy_array(b); free(a); } /*\ Atomic Accumulate test for vector API: remote += alpha*local * Every process/or has its patch of array b updated TIMES*NPROC times. * The sequence of updates is random: everybody uses a randomly permuted list * and accumulate is non-collective (of-course) \*/ void test_vector_acc() { int dim, elems, bytes; int i, j, proc, rc, one = 1; void *b[MAXPROC]; void *psrc[ELEMS/2], *pdst[ELEMS/2]; void *a, *c; double alpha = 0.1, scale; int *proclist = work; comex_giov_t dsc; elems = ELEMS; dim = 1; bytes = sizeof(double) * elems; /* create shared and local arrays */ create_array(b, sizeof(double), dim, &elems); a = malloc(bytes); assert(a); c = malloc(bytes); assert(c); init(a, dim, elems, &elems); if (me == 0) { printf("--------array[%d", elems); printf("]--------\n"); fflush(stdout); } GetPermutedProcList(proclist); /* initialize all elements of array b to zero */ for (i = 0; i < elems; i++) { ((double *)b[me])[i] = 0.; } sleep(1); dsc.bytes = sizeof(double); dsc.src = psrc; dsc.dst = pdst; dsc.count = elems / 2; comex_barrier(COMEX_GROUP_WORLD); for (i = 0; i < TIMES * nproc; i++) { /* proc=proclist[i%nproc];*/ proc = 0; /* accumulate even numbered elements */ for (j = 0; j < elems / 2; j++) { psrc[j] = 2 * j + (double *)a; pdst[j] = 2 * j + (double *)b[proc]; } if ((rc = comex_accv(COMEX_ACC_DBL, &alpha, &dsc, 1, proc, COMEX_GROUP_WORLD))) { comex_error("accumlate failed", rc); } /* for(j=0; j MAXPROC && me == 0) { comex_error("Test works for up to %d processors\n", MAXPROC); } if (me == 0) { printf("COMEX test program (%d processes)\n", nproc); fflush(stdout); sleep(1); } /* if(me==1)comex_die("process 1 committing suicide",1); */ if (me == 0) { printf("\nTesting strided gets and puts\n"); printf("(Only std output for process 0 is printed)\n\n"); fflush(stdout); sleep(1); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { test_dim(ndim); } comex_fence_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); if (me == 0) { printf("\nTesting non-blocking gets and puts\n"); fflush(stdout); sleep(1); } double timer_test_nbdim = timer(); test_nbdim(); comex_fence_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); timer_test_nbdim = timer() - timer_test_nbdim; if (me == 0) { printf("timer_test_nbdim=%f\n", timer_test_nbdim); } if (me == 0) { printf("\nTesting non-blocking vector gets and puts\n"); fflush(stdout); sleep(1); } double timer_test_vec_small = timer(); test_vec_small(); comex_fence_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); timer_test_vec_small = timer() - timer_test_vec_small; if (me == 0){ printf("timer_test_vec_small=%f\n", timer_test_vec_small); } if (me == 0) { printf("\nTesting atomic accumulate\n"); fflush(stdout); sleep(1); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { test_acc(ndim); } if (me == 0) { printf("\nTesting complex atomic accumulate\n"); fflush(stdout); sleep(1); } for (ndim = 1; ndim <= MAXDIMS; ndim++) { test_cplx_acc(ndim); } comex_fence_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); if (me == 0) { printf("\nTesting Vector Interface using triangular patches of a 2-D array\n\n"); fflush(stdout); sleep(1); } test_vector(); comex_fence_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); if (me == 0) { printf("\nTesting Accumulate with Vector Interface\n\n"); fflush(stdout); sleep(1); } double test_vector_acc_timer = timer(); test_vector_acc(); test_vector_acc_timer = timer() - test_vector_acc_timer; if (me == 0) { printf("test_vector_acc_timer=%f\n", test_vector_acc_timer); } comex_fence_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); if (me == 0) { printf("\nTesting atomic fetch&add\n"); printf("(Std Output for all processes is printed)\n\n"); fflush(stdout); sleep(1); } comex_barrier(COMEX_GROUP_WORLD); test_fetch_add(); if (me == 0) { printf("\nTesting atomic fetch&add long\n"); printf("(Std Output for all processes is printed)\n\n"); fflush(stdout); sleep(1); } comex_barrier(COMEX_GROUP_WORLD); test_fetch_add_long(); comex_fence_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); if (me == 0) { printf("\nTesting atomic swap\n"); fflush(stdout); } test_swap(); comex_fence_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); if (me == 0) { printf("\nTesting atomic swap long\n"); fflush(stdout); } test_swap_long(); comex_fence_all(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); if (me == 0) { printf("\nTesting aggregate put/get requests\n"); fflush(stdout); } comex_barrier(COMEX_GROUP_WORLD); comex_barrier(COMEX_GROUP_WORLD); if (me == 0) { printf("All tests passed\n"); fflush(stdout); } sleep(2); comex_barrier(COMEX_GROUP_WORLD); comex_finalize(); MPI_Finalize(); return(0); } ga-5.9.2/comex/tools/000077500000000000000000000000001500715745200143775ustar00rootroot00000000000000ga-5.9.2/comex/tools/armci-config.in000066400000000000000000000230521500715745200172670ustar00rootroot00000000000000#! /bin/sh # Generated from armci-config.in.m4sh by GNU Autoconf 2.69. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested="" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes break 2 fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset ## -------------------- ## ## Main body of script. ## ## -------------------- ## prefix="@prefix@" exec_prefix="@exec_prefix@" cc="@CC@" cppflags="@MPI_CPPFLAGS@ @COMEX_NETWORK_CPPFLAGS@ @BLAS_CPPFLAGS@ -I@includedir@" network_cppflags="@COMEX_NETWORK_CPPFLAGS@" cflags="" ldflags="@MPI_LDFLAGS@ @COMEX_NETWORK_LDFLAGS@ @BLAS_LDFLAGS@ -L@libdir@" network_ldflags="@COMEX_NETWORK_LDFLAGS@" libs="-larmci @MPI_LIBS@ @COMEX_NETWORK_LIBS@ @BLAS_LIBS@ @LIBS@" network_libs="@COMEX_NETWORK_LIBS@" version="@PACKAGE_VERSION@" blas_size="@blas_size@" use_blas="@have_blas@" usage="Usage: armci-config [OPTIONS]... With the exception of --version and --help, all other options can be combined or run exclusively. Output is echoed to stdout. Options: --cc --cppflags --cflags --ldflags --libs --network_cppflags --network_ldflags --network_libs --version --help " result= while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\(^=*\)='` ac_optarg=`expr "X$1" : 'X^=*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\(^=*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -help | --help | --hel | --he | -h ) $as_echo "$usage"; exit ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$version"; exit ;; -cc | --cc ) result="$result $cc" ;; -cppflags | --cppflags ) result="$result $cppflags" ;; -network_cppflags | --network_cppflags ) result="$result $network_cppflags" ;; -cflags | --cflags ) result="$result $cflags" ;; -ldflags | --ldflags ) result="$result $ldflags" ;; -network_ldflags | --network_ldflags ) result="$result $network_ldflags" ;; -libs | --libs ) result="$result $libs" ;; -network_libs | --network_libs ) result="$result $network_libs" ;; -blas_size | --blas_size ) result="$result $blas_size" ;; -use_blas | --use_blas ) result="$result $use_blas" ;; # This is an error. *) $as_echo "unrecognized option: \`$1' Try \`$0 --help' for more information."; exit ;; esac shift done $as_echo "$result" | sed 's/ */ /g;s/" /"/g;s/ "/"/g;s/^ *//;s/ *$//' ga-5.9.2/comex/tools/armci-config.in.m4sh000066400000000000000000000043571500715745200201500ustar00rootroot00000000000000dnl run "autom4te -l m4sh armci-config.in.m4sh > armci-config.in" AS_INIT prefix="@prefix@" exec_prefix="@exec_prefix@" cc="@CC@" cppflags="@MPI_CPPFLAGS@ @COMEX_NETWORK_CPPFLAGS@ @BLAS_CPPFLAGS@ -I@includedir@" network_cppflags="@COMEX_NETWORK_CPPFLAGS@" cflags="" ldflags="@MPI_LDFLAGS@ @COMEX_NETWORK_LDFLAGS@ @BLAS_LDFLAGS@ -L@libdir@" network_ldflags="@COMEX_NETWORK_LDFLAGS@" libs="-larmci @MPI_LIBS@ @COMEX_NETWORK_LIBS@ @BLAS_LIBS@ @LIBS@" network_libs="@COMEX_NETWORK_LIBS@" version="@PACKAGE_VERSION@" blas_size="@blas_size@" use_blas="@have_blas@" [usage="Usage: armci-config [OPTIONS]... With the exception of --version and --help, all other options can be combined or run exclusively. Output is echoed to stdout. Options: --cc --cppflags --cflags --ldflags --libs --network_cppflags --network_ldflags --network_libs --version --help "] result= while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -help | --help | --hel | --he | -h ) AS_ECHO(["$usage"]); exit ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) AS_ECHO(["$version"]); exit ;; -cc | --cc ) result="$result $cc" ;; -cppflags | --cppflags ) result="$result $cppflags" ;; -network_cppflags | --network_cppflags ) result="$result $network_cppflags" ;; -cflags | --cflags ) result="$result $cflags" ;; -ldflags | --ldflags ) result="$result $ldflags" ;; -network_ldflags | --network_ldflags ) result="$result $network_ldflags" ;; -libs | --libs ) result="$result $libs" ;; -network_libs | --network_libs ) result="$result $network_libs" ;; -blas_size | --blas_size ) result="$result $blas_size" ;; -use_blas | --use_blas ) result="$result $use_blas" ;; # This is an error. *) AS_ECHO(["unrecognized option: \`$1' Try \`$0 --help' for more information."]); exit ;; esac shift done AS_ECHO(["$result"]) | sed 's/ [ ]*/ /g;s/" /"/g;s/ "/"/g;s/^ [ ]*//;s/ [ ]*$//' ga-5.9.2/comex/tools/armci_prof.c000066400000000000000000001104751500715745200166740ustar00rootroot00000000000000 #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include "armci.h" #include "parmci.h" static MPI_Comm comm = MPI_COMM_NULL; static int me = -1; static int nproc = -1; static double global_start = 0.0; static double global_stop = 0.0; static long count_PARMCI_Acc = 0; static long count_PARMCI_AccS = 0; static long count_PARMCI_AccV = 0; static long count_PARMCI_AllFence = 0; static long count_PARMCI_Barrier = 0; static long count_PARMCI_Create_mutexes = 0; static long count_PARMCI_Destroy_mutexes = 0; static long count_PARMCI_Fence = 0; static long count_PARMCI_GroupFence = 0; static long count_PARMCI_Finalize = 0; static long count_PARMCI_Free = 0; static long count_PARMCI_Free_local = 0; static long count_PARMCI_Get = 0; static long count_PARMCI_GetS = 0; static long count_PARMCI_GetV = 0; static long count_PARMCI_GetValueDouble = 0; static long count_PARMCI_GetValueFloat = 0; static long count_PARMCI_GetValueInt = 0; static long count_PARMCI_GetValueLong = 0; static long count_PARMCI_Init = 0; static long count_PARMCI_Init_args = 0; static long count_PARMCI_Initialized = 0; static long count_PARMCI_Lock = 0; static long count_PARMCI_Malloc = 0; static long count_PARMCI_Malloc_local = 0; static long count_PARMCI_Memat = 0; static long count_PARMCI_Memget = 0; static long count_PARMCI_NbAccS = 0; static long count_PARMCI_NbAccV = 0; static long count_PARMCI_NbGet = 0; static long count_PARMCI_NbGetS = 0; static long count_PARMCI_NbGetV = 0; static long count_PARMCI_NbPut = 0; static long count_PARMCI_NbPutS = 0; static long count_PARMCI_NbPutV = 0; static long count_PARMCI_NbPutValueDouble = 0; static long count_PARMCI_NbPutValueFloat = 0; static long count_PARMCI_NbPutValueInt = 0; static long count_PARMCI_NbPutValueLong = 0; static long count_PARMCI_Put = 0; static long count_PARMCI_PutS = 0; static long count_PARMCI_PutS_flag = 0; static long count_PARMCI_PutS_flag_dir = 0; static long count_PARMCI_PutV = 0; static long count_PARMCI_PutValueDouble = 0; static long count_PARMCI_PutValueFloat = 0; static long count_PARMCI_PutValueInt = 0; static long count_PARMCI_PutValueLong = 0; static long count_PARMCI_Put_flag = 0; static long count_PARMCI_Rmw = 0; static long count_PARMCI_Test = 0; static long count_PARMCI_Unlock = 0; static long count_PARMCI_Wait = 0; static long count_PARMCI_WaitAll = 0; static long count_PARMCI_WaitProc = 0; static long count_parmci_msg_barrier = 0; static long count_parmci_msg_group_barrier = 0; static long count_parmci_notify = 0; static long count_parmci_notify_wait = 0; static double time_PARMCI_Acc = 0; static double time_PARMCI_AccS = 0; static double time_PARMCI_AccV = 0; static double time_PARMCI_AllFence = 0; static double time_PARMCI_Barrier = 0; static double time_PARMCI_Create_mutexes = 0; static double time_PARMCI_Destroy_mutexes = 0; static double time_PARMCI_Fence = 0; static double time_PARMCI_GroupFence = 0; static double time_PARMCI_Finalize = 0; static double time_PARMCI_Free = 0; static double time_PARMCI_Free_local = 0; static double time_PARMCI_Get = 0; static double time_PARMCI_GetS = 0; static double time_PARMCI_GetV = 0; static double time_PARMCI_GetValueDouble = 0; static double time_PARMCI_GetValueFloat = 0; static double time_PARMCI_GetValueInt = 0; static double time_PARMCI_GetValueLong = 0; static double time_PARMCI_Init = 0; static double time_PARMCI_Init_args = 0; static double time_PARMCI_Initialized = 0; static double time_PARMCI_Lock = 0; static double time_PARMCI_Malloc = 0; static double time_PARMCI_Malloc_local = 0; static double time_PARMCI_Memat = 0; static double time_PARMCI_Memget = 0; static double time_PARMCI_NbAccS = 0; static double time_PARMCI_NbAccV = 0; static double time_PARMCI_NbGet = 0; static double time_PARMCI_NbGetS = 0; static double time_PARMCI_NbGetV = 0; static double time_PARMCI_NbPut = 0; static double time_PARMCI_NbPutS = 0; static double time_PARMCI_NbPutV = 0; static double time_PARMCI_NbPutValueDouble = 0; static double time_PARMCI_NbPutValueFloat = 0; static double time_PARMCI_NbPutValueInt = 0; static double time_PARMCI_NbPutValueLong = 0; static double time_PARMCI_Put = 0; static double time_PARMCI_PutS = 0; static double time_PARMCI_PutS_flag = 0; static double time_PARMCI_PutS_flag_dir = 0; static double time_PARMCI_PutV = 0; static double time_PARMCI_PutValueDouble = 0; static double time_PARMCI_PutValueFloat = 0; static double time_PARMCI_PutValueInt = 0; static double time_PARMCI_PutValueLong = 0; static double time_PARMCI_Put_flag = 0; static double time_PARMCI_Rmw = 0; static double time_PARMCI_Test = 0; static double time_PARMCI_Unlock = 0; static double time_PARMCI_Wait = 0; static double time_PARMCI_WaitAll = 0; static double time_PARMCI_WaitProc = 0; static double time_parmci_msg_barrier = 0; static double time_parmci_msg_group_barrier = 0; static double time_parmci_notify = 0; static double time_parmci_notify_wait = 0; int ARMCI_Acc(int optype, void *scale, void *src, void *dst, int bytes, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_Acc; local_start = MPI_Wtime(); ret = PARMCI_Acc(optype, scale, src, dst, bytes, proc); local_stop = MPI_Wtime(); time_PARMCI_Acc += local_stop - local_start; return ret; } int ARMCI_AccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_AccS; local_start = MPI_Wtime(); ret = PARMCI_AccS(optype, scale, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); local_stop = MPI_Wtime(); time_PARMCI_AccS += local_stop - local_start; return ret; } int ARMCI_AccV(int op, void *scale, armci_giov_t *darr, int len, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_AccV; local_start = MPI_Wtime(); ret = PARMCI_AccV(op, scale, darr, len, proc); local_stop = MPI_Wtime(); time_PARMCI_AccV += local_stop - local_start; return ret; } void ARMCI_AllFence() { double local_start; double local_stop; ++count_PARMCI_AllFence; local_start = MPI_Wtime(); PARMCI_AllFence(); local_stop = MPI_Wtime(); time_PARMCI_AllFence += local_stop - local_start; } void ARMCI_Barrier() { double local_start; double local_stop; ++count_PARMCI_Barrier; local_start = MPI_Wtime(); PARMCI_Barrier(); local_stop = MPI_Wtime(); time_PARMCI_Barrier += local_stop - local_start; } int ARMCI_Create_mutexes(int num) { double local_start; double local_stop; int ret; ++count_PARMCI_Create_mutexes; local_start = MPI_Wtime(); ret = PARMCI_Create_mutexes(num); local_stop = MPI_Wtime(); time_PARMCI_Create_mutexes += local_stop - local_start; return ret; } int ARMCI_Destroy_mutexes() { double local_start; double local_stop; int ret; ++count_PARMCI_Destroy_mutexes; local_start = MPI_Wtime(); ret = PARMCI_Destroy_mutexes(); local_stop = MPI_Wtime(); time_PARMCI_Destroy_mutexes += local_stop - local_start; return ret; } void ARMCI_Fence(int proc) { double local_start; double local_stop; ++count_PARMCI_Fence; local_start = MPI_Wtime(); PARMCI_Fence(proc); local_stop = MPI_Wtime(); time_PARMCI_Fence += local_stop - local_start; } void ARMCI_GroupFence(ARMCI_Group *group) { double local_start; double local_stop; ++count_PARMCI_GroupFence; local_start = MPI_Wtime(); PARMCI_GroupFence(group); local_stop = MPI_Wtime(); time_PARMCI_GroupFence += local_stop - local_start; } int ARMCI_Free(void *ptr) { double local_start; double local_stop; int ret; ++count_PARMCI_Free; local_start = MPI_Wtime(); ret = PARMCI_Free(ptr); local_stop = MPI_Wtime(); time_PARMCI_Free += local_stop - local_start; return ret; } int ARMCI_Free_local(void *ptr) { double local_start; double local_stop; int ret; ++count_PARMCI_Free_local; local_start = MPI_Wtime(); ret = PARMCI_Free_local(ptr); local_stop = MPI_Wtime(); time_PARMCI_Free_local += local_stop - local_start; return ret; } int ARMCI_Get(void *src, void *dst, int bytes, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_Get; local_start = MPI_Wtime(); ret = PARMCI_Get(src, dst, bytes, proc); local_stop = MPI_Wtime(); time_PARMCI_Get += local_stop - local_start; return ret; } int ARMCI_GetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_GetS; local_start = MPI_Wtime(); ret = PARMCI_GetS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); local_stop = MPI_Wtime(); time_PARMCI_GetS += local_stop - local_start; return ret; } int ARMCI_GetV(armci_giov_t *darr, int len, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_GetV; local_start = MPI_Wtime(); ret = PARMCI_GetV(darr, len, proc); local_stop = MPI_Wtime(); time_PARMCI_GetV += local_stop - local_start; return ret; } double ARMCI_GetValueDouble(void *src, int proc) { double local_start; double local_stop; double ret; ++count_PARMCI_GetValueDouble; local_start = MPI_Wtime(); ret = PARMCI_GetValueDouble(src, proc); local_stop = MPI_Wtime(); time_PARMCI_GetValueDouble += local_stop - local_start; return ret; } float ARMCI_GetValueFloat(void *src, int proc) { double local_start; double local_stop; float ret; ++count_PARMCI_GetValueFloat; local_start = MPI_Wtime(); ret = PARMCI_GetValueFloat(src, proc); local_stop = MPI_Wtime(); time_PARMCI_GetValueFloat += local_stop - local_start; return ret; } int ARMCI_GetValueInt(void *src, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_GetValueInt; local_start = MPI_Wtime(); ret = PARMCI_GetValueInt(src, proc); local_stop = MPI_Wtime(); time_PARMCI_GetValueInt += local_stop - local_start; return ret; } long ARMCI_GetValueLong(void *src, int proc) { double local_start; double local_stop; long ret; ++count_PARMCI_GetValueLong; local_start = MPI_Wtime(); ret = PARMCI_GetValueLong(src, proc); local_stop = MPI_Wtime(); time_PARMCI_GetValueLong += local_stop - local_start; return ret; } int ARMCI_Init() { double local_start; double local_stop; int ret; if (comm == MPI_COMM_NULL) { MPI_Comm_dup(MPI_COMM_WORLD, &comm); MPI_Comm_rank(comm, &me); MPI_Comm_size(comm, &nproc); } ++count_PARMCI_Init; global_start = MPI_Wtime(); local_start = MPI_Wtime(); ret = PARMCI_Init(); local_stop = MPI_Wtime(); time_PARMCI_Init += local_stop - local_start; return ret; } int ARMCI_Init_args(int *argc, char ***argv) { double local_start; double local_stop; int ret; if (comm == MPI_COMM_NULL) { MPI_Comm_dup(MPI_COMM_WORLD, &comm); MPI_Comm_rank(comm, &me); MPI_Comm_size(comm, &nproc); } ++count_PARMCI_Init_args; global_start = MPI_Wtime(); local_start = MPI_Wtime(); ret = PARMCI_Init_args(argc, argv); local_stop = MPI_Wtime(); time_PARMCI_Init_args += local_stop - local_start; return ret; } int ARMCI_Initialized() { double local_start; double local_stop; int ret; ++count_PARMCI_Initialized; local_start = MPI_Wtime(); ret = PARMCI_Initialized(); local_stop = MPI_Wtime(); time_PARMCI_Initialized += local_stop - local_start; return ret; } void ARMCI_Lock(int mutex, int proc) { double local_start; double local_stop; ++count_PARMCI_Lock; local_start = MPI_Wtime(); PARMCI_Lock(mutex, proc); local_stop = MPI_Wtime(); time_PARMCI_Lock += local_stop - local_start; } int ARMCI_Malloc(void **ptr_arr, armci_size_t bytes) { double local_start; double local_stop; int ret; ++count_PARMCI_Malloc; local_start = MPI_Wtime(); ret = PARMCI_Malloc(ptr_arr, bytes); local_stop = MPI_Wtime(); time_PARMCI_Malloc += local_stop - local_start; return ret; } void* ARMCI_Malloc_local(armci_size_t bytes) { double local_start; double local_stop; void* ret; ++count_PARMCI_Malloc_local; local_start = MPI_Wtime(); ret = PARMCI_Malloc_local(bytes); local_stop = MPI_Wtime(); time_PARMCI_Malloc_local += local_stop - local_start; return ret; } void* ARMCI_Memat(armci_meminfo_t *meminfo, long offset) { double local_start; double local_stop; void* ret; ++count_PARMCI_Memat; local_start = MPI_Wtime(); ret = PARMCI_Memat(meminfo, offset); local_stop = MPI_Wtime(); time_PARMCI_Memat += local_stop - local_start; return ret; } void ARMCI_Memget(size_t bytes, armci_meminfo_t *meminfo, int memflg) { double local_start; double local_stop; ++count_PARMCI_Memget; local_start = MPI_Wtime(); PARMCI_Memget(bytes, meminfo, memflg); local_stop = MPI_Wtime(); time_PARMCI_Memget += local_stop - local_start; } int ARMCI_NbAccS(int optype, void *scale, void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { double local_start; double local_stop; int ret; ++count_PARMCI_NbAccS; local_start = MPI_Wtime(); ret = PARMCI_NbAccS(optype, scale, src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); local_stop = MPI_Wtime(); time_PARMCI_NbAccS += local_stop - local_start; return ret; } int ARMCI_NbAccV(int op, void *scale, armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { double local_start; double local_stop; int ret; ++count_PARMCI_NbAccV; local_start = MPI_Wtime(); ret = PARMCI_NbAccV(op, scale, darr, len, proc, nb_handle); local_stop = MPI_Wtime(); time_PARMCI_NbAccV += local_stop - local_start; return ret; } int ARMCI_NbGet(void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { double local_start; double local_stop; int ret; ++count_PARMCI_NbGet; local_start = MPI_Wtime(); ret = PARMCI_NbGet(src, dst, bytes, proc, nb_handle); local_stop = MPI_Wtime(); time_PARMCI_NbGet += local_stop - local_start; return ret; } int ARMCI_NbGetS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { double local_start; double local_stop; int ret; ++count_PARMCI_NbGetS; local_start = MPI_Wtime(); ret = PARMCI_NbGetS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); local_stop = MPI_Wtime(); time_PARMCI_NbGetS += local_stop - local_start; return ret; } int ARMCI_NbGetV(armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { double local_start; double local_stop; int ret; ++count_PARMCI_NbGetV; local_start = MPI_Wtime(); ret = PARMCI_NbGetV(darr, len, proc, nb_handle); local_stop = MPI_Wtime(); time_PARMCI_NbGetV += local_stop - local_start; return ret; } int ARMCI_NbPut(void *src, void *dst, int bytes, int proc, armci_hdl_t *nb_handle) { double local_start; double local_stop; int ret; ++count_PARMCI_NbPut; local_start = MPI_Wtime(); ret = PARMCI_NbPut(src, dst, bytes, proc, nb_handle); local_stop = MPI_Wtime(); time_PARMCI_NbPut += local_stop - local_start; return ret; } int ARMCI_NbPutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc, armci_hdl_t *nb_handle) { double local_start; double local_stop; int ret; ++count_PARMCI_NbPutS; local_start = MPI_Wtime(); ret = PARMCI_NbPutS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc, nb_handle); local_stop = MPI_Wtime(); time_PARMCI_NbPutS += local_stop - local_start; return ret; } int ARMCI_NbPutV(armci_giov_t *darr, int len, int proc, armci_hdl_t *nb_handle) { double local_start; double local_stop; int ret; ++count_PARMCI_NbPutV; local_start = MPI_Wtime(); ret = PARMCI_NbPutV(darr, len, proc, nb_handle); local_stop = MPI_Wtime(); time_PARMCI_NbPutV += local_stop - local_start; return ret; } int ARMCI_NbPutValueDouble(double src, void *dst, int proc, armci_hdl_t *nb_handle) { double local_start; double local_stop; int ret; ++count_PARMCI_NbPutValueDouble; local_start = MPI_Wtime(); ret = PARMCI_NbPutValueDouble(src, dst, proc, nb_handle); local_stop = MPI_Wtime(); time_PARMCI_NbPutValueDouble += local_stop - local_start; return ret; } int ARMCI_NbPutValueFloat(float src, void *dst, int proc, armci_hdl_t *nb_handle) { double local_start; double local_stop; int ret; ++count_PARMCI_NbPutValueFloat; local_start = MPI_Wtime(); ret = PARMCI_NbPutValueFloat(src, dst, proc, nb_handle); local_stop = MPI_Wtime(); time_PARMCI_NbPutValueFloat += local_stop - local_start; return ret; } int ARMCI_NbPutValueInt(int src, void *dst, int proc, armci_hdl_t *nb_handle) { double local_start; double local_stop; int ret; ++count_PARMCI_NbPutValueInt; local_start = MPI_Wtime(); ret = PARMCI_NbPutValueInt(src, dst, proc, nb_handle); local_stop = MPI_Wtime(); time_PARMCI_NbPutValueInt += local_stop - local_start; return ret; } int ARMCI_NbPutValueLong(long src, void *dst, int proc, armci_hdl_t *nb_handle) { double local_start; double local_stop; int ret; ++count_PARMCI_NbPutValueLong; local_start = MPI_Wtime(); ret = PARMCI_NbPutValueLong(src, dst, proc, nb_handle); local_stop = MPI_Wtime(); time_PARMCI_NbPutValueLong += local_stop - local_start; return ret; } int ARMCI_Put(void *src, void *dst, int bytes, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_Put; local_start = MPI_Wtime(); ret = PARMCI_Put(src, dst, bytes, proc); local_stop = MPI_Wtime(); time_PARMCI_Put += local_stop - local_start; return ret; } int ARMCI_PutS(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_PutS; local_start = MPI_Wtime(); ret = PARMCI_PutS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); local_stop = MPI_Wtime(); time_PARMCI_PutS += local_stop - local_start; return ret; } int ARMCI_PutS_flag(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_PutS_flag; local_start = MPI_Wtime(); ret = PARMCI_PutS_flag(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, flag, val, proc); local_stop = MPI_Wtime(); time_PARMCI_PutS_flag += local_stop - local_start; return ret; } int ARMCI_PutS_flag_dir(void *src_ptr, int *src_stride_arr, void *dst_ptr, int *dst_stride_arr, int *count, int stride_levels, int *flag, int val, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_PutS_flag_dir; local_start = MPI_Wtime(); ret = PARMCI_PutS_flag_dir(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, flag, val, proc); local_stop = MPI_Wtime(); time_PARMCI_PutS_flag_dir += local_stop - local_start; return ret; } int ARMCI_PutV(armci_giov_t *darr, int len, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_PutV; local_start = MPI_Wtime(); ret = PARMCI_PutV(darr, len, proc); local_stop = MPI_Wtime(); time_PARMCI_PutV += local_stop - local_start; return ret; } int ARMCI_PutValueDouble(double src, void *dst, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_PutValueDouble; local_start = MPI_Wtime(); ret = PARMCI_PutValueDouble(src, dst, proc); local_stop = MPI_Wtime(); time_PARMCI_PutValueDouble += local_stop - local_start; return ret; } int ARMCI_PutValueFloat(float src, void *dst, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_PutValueFloat; local_start = MPI_Wtime(); ret = PARMCI_PutValueFloat(src, dst, proc); local_stop = MPI_Wtime(); time_PARMCI_PutValueFloat += local_stop - local_start; return ret; } int ARMCI_PutValueInt(int src, void *dst, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_PutValueInt; local_start = MPI_Wtime(); ret = PARMCI_PutValueInt(src, dst, proc); local_stop = MPI_Wtime(); time_PARMCI_PutValueInt += local_stop - local_start; return ret; } int ARMCI_PutValueLong(long src, void *dst, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_PutValueLong; local_start = MPI_Wtime(); ret = PARMCI_PutValueLong(src, dst, proc); local_stop = MPI_Wtime(); time_PARMCI_PutValueLong += local_stop - local_start; return ret; } int ARMCI_Put_flag(void *src, void *dst, int bytes, int *f, int v, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_Put_flag; local_start = MPI_Wtime(); ret = PARMCI_Put_flag(src, dst, bytes, f, v, proc); local_stop = MPI_Wtime(); time_PARMCI_Put_flag += local_stop - local_start; return ret; } int ARMCI_Rmw(int op, void *ploc, void *prem, int extra, int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_Rmw; local_start = MPI_Wtime(); ret = PARMCI_Rmw(op, ploc, prem, extra, proc); local_stop = MPI_Wtime(); time_PARMCI_Rmw += local_stop - local_start; return ret; } int ARMCI_Test(armci_hdl_t *nb_handle) { double local_start; double local_stop; int ret; ++count_PARMCI_Test; local_start = MPI_Wtime(); ret = PARMCI_Test(nb_handle); local_stop = MPI_Wtime(); time_PARMCI_Test += local_stop - local_start; return ret; } void ARMCI_Unlock(int mutex, int proc) { double local_start; double local_stop; ++count_PARMCI_Unlock; local_start = MPI_Wtime(); PARMCI_Unlock(mutex, proc); local_stop = MPI_Wtime(); time_PARMCI_Unlock += local_stop - local_start; } int ARMCI_Wait(armci_hdl_t *nb_handle) { double local_start; double local_stop; int ret; ++count_PARMCI_Wait; local_start = MPI_Wtime(); ret = PARMCI_Wait(nb_handle); local_stop = MPI_Wtime(); time_PARMCI_Wait += local_stop - local_start; return ret; } int ARMCI_WaitAll() { double local_start; double local_stop; int ret; ++count_PARMCI_WaitAll; local_start = MPI_Wtime(); ret = PARMCI_WaitAll(); local_stop = MPI_Wtime(); time_PARMCI_WaitAll += local_stop - local_start; return ret; } int ARMCI_WaitProc(int proc) { double local_start; double local_stop; int ret; ++count_PARMCI_WaitProc; local_start = MPI_Wtime(); ret = PARMCI_WaitProc(proc); local_stop = MPI_Wtime(); time_PARMCI_WaitProc += local_stop - local_start; return ret; } void armci_msg_barrier() { double local_start; double local_stop; ++count_parmci_msg_barrier; local_start = MPI_Wtime(); parmci_msg_barrier(); local_stop = MPI_Wtime(); time_parmci_msg_barrier += local_stop - local_start; } void armci_msg_group_barrier(ARMCI_Group *group) { double local_start; double local_stop; ++count_parmci_msg_group_barrier; local_start = MPI_Wtime(); parmci_msg_group_barrier(group); local_stop = MPI_Wtime(); time_parmci_msg_group_barrier += local_stop - local_start; } int armci_notify(int proc) { double local_start; double local_stop; int ret; ++count_parmci_notify; local_start = MPI_Wtime(); ret = parmci_notify(proc); local_stop = MPI_Wtime(); time_parmci_notify += local_stop - local_start; return ret; } int armci_notify_wait(int proc, int *pval) { double local_start; double local_stop; int ret; ++count_parmci_notify_wait; local_start = MPI_Wtime(); ret = parmci_notify_wait(proc, pval); local_stop = MPI_Wtime(); time_parmci_notify_wait += local_stop - local_start; return ret; } void ARMCI_Finalize() { int ret; ++count_PARMCI_Finalize; PARMCI_Finalize(); global_stop = MPI_Wtime(); /* don't dump info if terminate more than once */ if (1 == count_PARMCI_Finalize) { double recvbuf = 0.0; MPI_Reduce(&time_PARMCI_Acc, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Acc,%ld,%lf\n", count_PARMCI_Acc, recvbuf); } MPI_Reduce(&time_PARMCI_AccS, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_AccS,%ld,%lf\n", count_PARMCI_AccS, recvbuf); } MPI_Reduce(&time_PARMCI_AccV, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_AccV,%ld,%lf\n", count_PARMCI_AccV, recvbuf); } MPI_Reduce(&time_PARMCI_AllFence, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_AllFence,%ld,%lf\n", count_PARMCI_AllFence, recvbuf); } MPI_Reduce(&time_PARMCI_Barrier, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Barrier,%ld,%lf\n", count_PARMCI_Barrier, recvbuf); } MPI_Reduce(&time_PARMCI_Create_mutexes, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Create_mutexes,%ld,%lf\n", count_PARMCI_Create_mutexes, recvbuf); } MPI_Reduce(&time_PARMCI_Destroy_mutexes, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Destroy_mutexes,%ld,%lf\n", count_PARMCI_Destroy_mutexes, recvbuf); } MPI_Reduce(&time_PARMCI_Fence, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Fence,%ld,%lf\n", count_PARMCI_Fence, recvbuf); } MPI_Reduce(&time_PARMCI_Finalize, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Finalize,%ld,%lf\n", count_PARMCI_Finalize, recvbuf); } MPI_Reduce(&time_PARMCI_Free, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Free,%ld,%lf\n", count_PARMCI_Free, recvbuf); } MPI_Reduce(&time_PARMCI_Free_local, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Free_local,%ld,%lf\n", count_PARMCI_Free_local, recvbuf); } MPI_Reduce(&time_PARMCI_Get, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Get,%ld,%lf\n", count_PARMCI_Get, recvbuf); } MPI_Reduce(&time_PARMCI_GetS, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_GetS,%ld,%lf\n", count_PARMCI_GetS, recvbuf); } MPI_Reduce(&time_PARMCI_GetV, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_GetV,%ld,%lf\n", count_PARMCI_GetV, recvbuf); } MPI_Reduce(&time_PARMCI_GetValueDouble, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_GetValueDouble,%ld,%lf\n", count_PARMCI_GetValueDouble, recvbuf); } MPI_Reduce(&time_PARMCI_GetValueFloat, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_GetValueFloat,%ld,%lf\n", count_PARMCI_GetValueFloat, recvbuf); } MPI_Reduce(&time_PARMCI_GetValueInt, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_GetValueInt,%ld,%lf\n", count_PARMCI_GetValueInt, recvbuf); } MPI_Reduce(&time_PARMCI_GetValueLong, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_GetValueLong,%ld,%lf\n", count_PARMCI_GetValueLong, recvbuf); } MPI_Reduce(&time_PARMCI_Init, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Init,%ld,%lf\n", count_PARMCI_Init, recvbuf); } MPI_Reduce(&time_PARMCI_Init_args, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Init_args,%ld,%lf\n", count_PARMCI_Init_args, recvbuf); } MPI_Reduce(&time_PARMCI_Initialized, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Initialized,%ld,%lf\n", count_PARMCI_Initialized, recvbuf); } MPI_Reduce(&time_PARMCI_Lock, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Lock,%ld,%lf\n", count_PARMCI_Lock, recvbuf); } MPI_Reduce(&time_PARMCI_Malloc, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Malloc,%ld,%lf\n", count_PARMCI_Malloc, recvbuf); } MPI_Reduce(&time_PARMCI_Malloc_local, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Malloc_local,%ld,%lf\n", count_PARMCI_Malloc_local, recvbuf); } MPI_Reduce(&time_PARMCI_Memat, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Memat,%ld,%lf\n", count_PARMCI_Memat, recvbuf); } MPI_Reduce(&time_PARMCI_Memget, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Memget,%ld,%lf\n", count_PARMCI_Memget, recvbuf); } MPI_Reduce(&time_PARMCI_NbAccS, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_NbAccS,%ld,%lf\n", count_PARMCI_NbAccS, recvbuf); } MPI_Reduce(&time_PARMCI_NbAccV, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_NbAccV,%ld,%lf\n", count_PARMCI_NbAccV, recvbuf); } MPI_Reduce(&time_PARMCI_NbGet, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_NbGet,%ld,%lf\n", count_PARMCI_NbGet, recvbuf); } MPI_Reduce(&time_PARMCI_NbGetS, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_NbGetS,%ld,%lf\n", count_PARMCI_NbGetS, recvbuf); } MPI_Reduce(&time_PARMCI_NbGetV, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_NbGetV,%ld,%lf\n", count_PARMCI_NbGetV, recvbuf); } MPI_Reduce(&time_PARMCI_NbPut, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_NbPut,%ld,%lf\n", count_PARMCI_NbPut, recvbuf); } MPI_Reduce(&time_PARMCI_NbPutS, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_NbPutS,%ld,%lf\n", count_PARMCI_NbPutS, recvbuf); } MPI_Reduce(&time_PARMCI_NbPutV, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_NbPutV,%ld,%lf\n", count_PARMCI_NbPutV, recvbuf); } MPI_Reduce(&time_PARMCI_NbPutValueDouble, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_NbPutValueDouble,%ld,%lf\n", count_PARMCI_NbPutValueDouble, recvbuf); } MPI_Reduce(&time_PARMCI_NbPutValueFloat, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_NbPutValueFloat,%ld,%lf\n", count_PARMCI_NbPutValueFloat, recvbuf); } MPI_Reduce(&time_PARMCI_NbPutValueInt, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_NbPutValueInt,%ld,%lf\n", count_PARMCI_NbPutValueInt, recvbuf); } MPI_Reduce(&time_PARMCI_NbPutValueLong, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_NbPutValueLong,%ld,%lf\n", count_PARMCI_NbPutValueLong, recvbuf); } MPI_Reduce(&time_PARMCI_Put, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Put,%ld,%lf\n", count_PARMCI_Put, recvbuf); } MPI_Reduce(&time_PARMCI_PutS, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_PutS,%ld,%lf\n", count_PARMCI_PutS, recvbuf); } MPI_Reduce(&time_PARMCI_PutS_flag, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_PutS_flag,%ld,%lf\n", count_PARMCI_PutS_flag, recvbuf); } MPI_Reduce(&time_PARMCI_PutS_flag_dir, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_PutS_flag_dir,%ld,%lf\n", count_PARMCI_PutS_flag_dir, recvbuf); } MPI_Reduce(&time_PARMCI_PutV, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_PutV,%ld,%lf\n", count_PARMCI_PutV, recvbuf); } MPI_Reduce(&time_PARMCI_PutValueDouble, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_PutValueDouble,%ld,%lf\n", count_PARMCI_PutValueDouble, recvbuf); } MPI_Reduce(&time_PARMCI_PutValueFloat, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_PutValueFloat,%ld,%lf\n", count_PARMCI_PutValueFloat, recvbuf); } MPI_Reduce(&time_PARMCI_PutValueInt, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_PutValueInt,%ld,%lf\n", count_PARMCI_PutValueInt, recvbuf); } MPI_Reduce(&time_PARMCI_PutValueLong, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_PutValueLong,%ld,%lf\n", count_PARMCI_PutValueLong, recvbuf); } MPI_Reduce(&time_PARMCI_Put_flag, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Put_flag,%ld,%lf\n", count_PARMCI_Put_flag, recvbuf); } MPI_Reduce(&time_PARMCI_Rmw, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Rmw,%ld,%lf\n", count_PARMCI_Rmw, recvbuf); } MPI_Reduce(&time_PARMCI_Test, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Test,%ld,%lf\n", count_PARMCI_Test, recvbuf); } MPI_Reduce(&time_PARMCI_Unlock, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Unlock,%ld,%lf\n", count_PARMCI_Unlock, recvbuf); } MPI_Reduce(&time_PARMCI_Wait, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_Wait,%ld,%lf\n", count_PARMCI_Wait, recvbuf); } MPI_Reduce(&time_PARMCI_WaitAll, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_WaitAll,%ld,%lf\n", count_PARMCI_WaitAll, recvbuf); } MPI_Reduce(&time_PARMCI_WaitProc, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("PARMCI_WaitProc,%ld,%lf\n", count_PARMCI_WaitProc, recvbuf); } MPI_Reduce(&time_parmci_msg_barrier, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("parmci_msg_barrier,%ld,%lf\n", count_parmci_msg_barrier, recvbuf); } MPI_Reduce(&time_parmci_msg_group_barrier, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("parmci_msg_group_barrier,%ld,%lf\n", count_parmci_msg_group_barrier, recvbuf); } MPI_Reduce(&time_parmci_notify, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("parmci_notify,%ld,%lf\n", count_parmci_notify, recvbuf); } MPI_Reduce(&time_parmci_notify_wait, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("parmci_notify_wait,%ld,%lf\n", count_parmci_notify_wait, recvbuf); } if (me == 0) { printf("global_stop-global_start,0,%lf\n", global_stop-global_start); } } MPI_Comm_free(&comm); } ga-5.9.2/comex/tools/capi_gen.py000077500000000000000000000103131500715745200165170ustar00rootroot00000000000000#!/usr/bin/env python '''Generate armci's capi.c source from the parmci.h header.''' import sys def get_signatures(header): # first, gather all function signatures from parmci.h aka argv[1] accumulating = False signatures = [] current_signature = '' EXTERN = 'extern' SEMICOLON = ';' for line in open(header): line = line.strip() # remove whitespace before and after line if not line: continue # skip blank lines if EXTERN in line and SEMICOLON in line: signatures.append(line) elif EXTERN in line: current_signature = line accumulating = True elif SEMICOLON in line and accumulating: current_signature += line signatures.append(current_signature) accumulating = False elif accumulating: current_signature += line return signatures class FunctionArgument(object): def __init__(self, signature): self.pointer = signature.count('*') self.array = '[' in signature signature = signature.replace('*','').strip() signature = signature.replace('[','').strip() signature = signature.replace(']','').strip() self.type,self.name = signature.split() def __str__(self): ret = self.type[:] ret += ' ' for p in range(self.pointer): ret += '*' ret += self.name if self.array: ret += '[]' return ret class Function(object): def __init__(self, signature): signature = signature.replace('extern','').strip() self.return_type,signature = signature.split(None,1) self.return_type = self.return_type.strip() signature = signature.strip() if '*' not in self.return_type and signature[0] == '*': # return type is void* not void self.return_type += '*' signature = signature[1:].strip() self.name,signature = signature.split('(',1) self.name = self.name.strip() signature = signature.replace(')','').strip() signature = signature.replace(';','').strip() self.args = [] if signature: for arg in signature.split(','): self.args.append(FunctionArgument(arg.strip())) def get_call(self, name=None): sig = '' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += arg.name sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def get_signature(self, name=None): sig = self.return_type[:] sig += ' ' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += str(arg) sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def __str__(self): return self.get_signature() if __name__ == '__main__': if len(sys.argv) != 2: print 'incorrect number of arguments' print 'usage: capi_gen.py > ' sys.exit(len(sys.argv)) # print headers print ''' #if HAVE_CONFIG_H # include "config.h" #endif #include #include "armci.h" #include "parmci.h" ''' functions = {} # parse signatures into the Function class for sig in get_signatures(sys.argv[1]): function = Function(sig) functions[function.name] = function # now process the functions for name in sorted(functions): func = functions[name] maybe_return = '' if '*' in func.return_type or 'void' not in func.return_type: maybe_return = 'return ' func = functions[name] new_name = None if 'PARMCI_' in name: new_name = name.replace('PARMCI_','ARMCI_') elif 'parmci_' in name: new_name = name.replace('parmci_','armci_') print ''' #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak %s #endif %s { %s%s; } ''' % (new_name, func.get_signature(new_name), maybe_return, func.get_call()) ga-5.9.2/comex/tools/comex-config.in000066400000000000000000000230521500715745200173070ustar00rootroot00000000000000#! /bin/sh # Generated from comex-config.in.m4sh by GNU Autoconf 2.69. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested="" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes break 2 fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset ## -------------------- ## ## Main body of script. ## ## -------------------- ## prefix="@prefix@" exec_prefix="@exec_prefix@" cc="@CC@" cppflags="@MPI_CPPFLAGS@ @COMEX_NETWORK_CPPFLAGS@ @BLAS_CPPFLAGS@ -I@includedir@" network_cppflags="@COMEX_NETWORK_CPPFLAGS@" cflags="" ldflags="@MPI_LDFLAGS@ @COMEX_NETWORK_LDFLAGS@ @BLAS_LDFLAGS@ -L@libdir@" network_ldflags="@COMEX_NETWORK_LDFLAGS@" libs="-lcomex @MPI_LIBS@ @COMEX_NETWORK_LIBS@ @BLAS_LIBS@ @LIBS@" network_libs="@COMEX_NETWORK_LIBS@" version="@PACKAGE_VERSION@" blas_size="@blas_size@" use_blas="@have_blas@" usage="Usage: comex-config [OPTIONS]... With the exception of --version and --help, all other options can be combined or run exclusively. Output is echoed to stdout. Options: --cc --cppflags --cflags --ldflags --libs --network_cppflags --network_ldflags --network_libs --version --help " result= while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\(^=*\)='` ac_optarg=`expr "X$1" : 'X^=*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\(^=*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -help | --help | --hel | --he | -h ) $as_echo "$usage"; exit ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$version"; exit ;; -cc | --cc ) result="$result $cc" ;; -cppflags | --cppflags ) result="$result $cppflags" ;; -network_cppflags | --network_cppflags ) result="$result $network_cppflags" ;; -cflags | --cflags ) result="$result $cflags" ;; -ldflags | --ldflags ) result="$result $ldflags" ;; -network_ldflags | --network_ldflags ) result="$result $network_ldflags" ;; -libs | --libs ) result="$result $libs" ;; -network_libs | --network_libs ) result="$result $network_libs" ;; -blas_size | --blas_size ) result="$result $blas_size" ;; -use_blas | --use_blas ) result="$result $use_blas" ;; # This is an error. *) $as_echo "unrecognized option: \`$1' Try \`$0 --help' for more information."; exit ;; esac shift done $as_echo "$result" | sed 's/ */ /g;s/" /"/g;s/ "/"/g;s/^ *//;s/ *$//' ga-5.9.2/comex/tools/comex-config.in.m4sh000066400000000000000000000043571500715745200201700ustar00rootroot00000000000000dnl run "autom4te -l m4sh comex-config.in.m4sh > comex-config.in" AS_INIT prefix="@prefix@" exec_prefix="@exec_prefix@" cc="@CC@" cppflags="@MPI_CPPFLAGS@ @COMEX_NETWORK_CPPFLAGS@ @BLAS_CPPFLAGS@ -I@includedir@" network_cppflags="@COMEX_NETWORK_CPPFLAGS@" cflags="" ldflags="@MPI_LDFLAGS@ @COMEX_NETWORK_LDFLAGS@ @BLAS_LDFLAGS@ -L@libdir@" network_ldflags="@COMEX_NETWORK_LDFLAGS@" libs="-lcomex @MPI_LIBS@ @COMEX_NETWORK_LIBS@ @BLAS_LIBS@ @LIBS@" network_libs="@COMEX_NETWORK_LIBS@" version="@PACKAGE_VERSION@" blas_size="@blas_size@" use_blas="@have_blas@" [usage="Usage: comex-config [OPTIONS]... With the exception of --version and --help, all other options can be combined or run exclusively. Output is echoed to stdout. Options: --cc --cppflags --cflags --ldflags --libs --network_cppflags --network_ldflags --network_libs --version --help "] result= while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -help | --help | --hel | --he | -h ) AS_ECHO(["$usage"]); exit ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) AS_ECHO(["$version"]); exit ;; -cc | --cc ) result="$result $cc" ;; -cppflags | --cppflags ) result="$result $cppflags" ;; -network_cppflags | --network_cppflags ) result="$result $network_cppflags" ;; -cflags | --cflags ) result="$result $cflags" ;; -ldflags | --ldflags ) result="$result $ldflags" ;; -network_ldflags | --network_ldflags ) result="$result $network_ldflags" ;; -libs | --libs ) result="$result $libs" ;; -network_libs | --network_libs ) result="$result $network_libs" ;; -blas_size | --blas_size ) result="$result $blas_size" ;; -use_blas | --use_blas ) result="$result $use_blas" ;; # This is an error. *) AS_ECHO(["unrecognized option: \`$1' Try \`$0 --help' for more information."]); exit ;; esac shift done AS_ECHO(["$result"]) | sed 's/ [ ]*/ /g;s/" /"/g;s/ "/"/g;s/^ [ ]*//;s/ [ ]*$//' ga-5.9.2/comex/tools/prof_gen.py000077500000000000000000000152701500715745200165600ustar00rootroot00000000000000#!/usr/bin/env python '''Generate the armci_prof.c source from the parmci.h header.''' import sys def get_signatures(header): # first, gather all function signatures from parmci.h aka argv[1] accumulating = False signatures = [] current_signature = '' EXTERN = 'extern' SEMICOLON = ';' for line in open(header): line = line.strip() # remove whitespace before and after line if not line: continue # skip blank lines if EXTERN in line and SEMICOLON in line: signatures.append(line) elif EXTERN in line: current_signature = line accumulating = True elif SEMICOLON in line and accumulating: current_signature += line signatures.append(current_signature) accumulating = False elif accumulating: current_signature += line return signatures class FunctionArgument(object): def __init__(self, signature): self.pointer = signature.count('*') self.array = '[' in signature signature = signature.replace('*','').strip() signature = signature.replace('[','').strip() signature = signature.replace(']','').strip() self.type,self.name = signature.split() def __str__(self): ret = self.type[:] ret += ' ' for p in range(self.pointer): ret += '*' ret += self.name if self.array: ret += '[]' return ret class Function(object): def __init__(self, signature): signature = signature.replace('extern','').strip() self.return_type,signature = signature.split(None,1) self.return_type = self.return_type.strip() signature = signature.strip() if '*' not in self.return_type and signature[0] == '*': # return type is void* not void self.return_type += '*' signature = signature[1:].strip() self.name,signature = signature.split('(',1) self.name = self.name.strip() signature = signature.replace(')','').strip() signature = signature.replace(';','').strip() self.args = [] if signature: for arg in signature.split(','): self.args.append(FunctionArgument(arg.strip())) def get_call(self, name=None): sig = '' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += arg.name sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def get_signature(self, name=None): sig = self.return_type[:] sig += ' ' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += str(arg) sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def __str__(self): return self.get_signature() if __name__ == '__main__': if len(sys.argv) != 2: print 'incorrect number of arguments' print 'usage: prof_gen.py > ' sys.exit(len(sys.argv)) # print headers print ''' #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include "armci.h" #include "parmci.h" static MPI_Comm comm = MPI_COMM_NULL; static int me = -1; static int nproc = -1; static double global_start = 0.0; static double global_stop = 0.0; ''' functions = {} # parse signatures into the Function class for sig in get_signatures(sys.argv[1]): function = Function(sig) functions[function.name] = function # for each function, generate a static count for name in sorted(functions): print 'static long count_%s = 0;' % name print '' # for each function, generate a static time for name in sorted(functions): print 'static double time_%s = 0;' % name print '' # now process the functions for name in sorted(functions): func = functions[name] maybe_declare = '' maybe_assign = '' maybe_return = '' if '*' in func.return_type or 'void' not in func.return_type: maybe_declare = '%s ret;' % func.return_type maybe_assign = 'ret = ' maybe_return = 'return ret;' new_name = None if 'PARMCI_' in name: new_name = name.replace('PARMCI_','ARMCI_') elif 'parmci_' in name: new_name = name.replace('parmci_','armci_') if new_name in ['ARMCI_Finalize']: continue elif new_name in ['ARMCI_Init','ARMCI_Init_args']: print ''' %s { double local_start; double local_stop; %s if (comm == MPI_COMM_NULL) { MPI_Comm_dup(MPI_COMM_WORLD, &comm); MPI_Comm_rank(comm, &me); MPI_Comm_size(comm, &nproc); } ++count_%s; global_start = MPI_Wtime(); local_start = MPI_Wtime(); %s%s; local_stop = MPI_Wtime(); time_%s += local_stop - local_start; %s } ''' % (func.get_signature(new_name), maybe_declare, name, maybe_assign, func.get_call(), name, maybe_return) else: print ''' %s { double local_start; double local_stop; %s ++count_%s; local_start = MPI_Wtime(); %s%s; local_stop = MPI_Wtime(); time_%s += local_stop - local_start; %s } ''' % (func.get_signature(new_name), maybe_declare, name, maybe_assign, func.get_call(), name, maybe_return) # prepare to output the terminate function name = 'PARMCI_Finalize' new_name = 'ARMCI_Finalize' func = functions[name] the_code = '' # establish 'the_code' to use in the body of terminate # it's printing the count of each function if it was called at least once the_code += ''' double recvbuf = 0.0; ''' for fname in sorted(functions): the_code += ''' MPI_Reduce(&time_%s, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("%s,%%ld,%%lf\\n", count_%s, recvbuf); } ''' % (fname, fname, fname) # output the terminate function print '''%s { int ret; ++count_%s; %s; global_stop = MPI_Wtime(); /* don't dump info if terminate more than once */ if (1 == count_%s) { %s if (me == 0) { printf("global_stop-global_start,0,%%lf\\n", global_stop-global_start); } } MPI_Comm_free(&comm); } ''' % (func.get_signature(new_name), name, func.get_call(), name, the_code) ga-5.9.2/compat/000077500000000000000000000000001500715745200134075ustar00rootroot00000000000000ga-5.9.2/compat/random.c000066400000000000000000000332021500715745200150330ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)random.c 5.5 (Berkeley) 7/6/88"; #endif /* LIBC_SCCS and not lint */ #include /* * random.c: * An improved random number generation package. In addition to the standard * rand()/srand() like interface, this package also has a special state info * interface. The initstate() routine is called with a seed, an array of * bytes, and a count of how many bytes are being passed in; this array is then * initialized to contain information for random number generation with that * much state information. Good sizes for the amount of state information are * 32, 64, 128, and 256 bytes. The state can be switched by calling the * setstate() routine with the same array as was initiallized with initstate(). * By default, the package runs with 128 bytes of state information and * generates far better random numbers than a linear congruential generator. * If the amount of state information is less than 32 bytes, a simple linear * congruential R.N.G. is used. * Internally, the state information is treated as an array of longs; the * zeroeth element of the array is the type of R.N.G. being used (small * integer); the remainder of the array is the state information for the * R.N.G. Thus, 32 bytes of state information will give 7 longs worth of * state information, which will allow a degree seven polynomial. (Note: the * zeroeth word of state information also has some other information stored * in it -- see setstate() for details). * The random number generation technique is a linear feedback shift register * approach, employing trinomials (since there are fewer terms to sum up that * way). In this approach, the least significant bit of all the numbers in * the state table will act as a linear feedback shift register, and will have * period 2^deg - 1 (where deg is the degree of the polynomial being used, * assuming that the polynomial is irreducible and primitive). The higher * order bits will have longer periods, since their values are also influenced * by pseudo-random carries out of the lower bits. The total period of the * generator is approximately deg*(2**deg - 1); thus doubling the amount of * state information has a vast influence on the period of the generator. * Note: the deg*(2**deg - 1) is an approximation only good for large deg, * when the period of the shift register is the dominant factor. With deg * equal to seven, the period is actually much longer than the 7*(2**7 - 1) * predicted by this formula. */ /* * For each of the currently supported random number generators, we have a * break value on the amount of state information (you need at least this * many bytes of state info to support this random number generator), a degree * for the polynomial (actually a trinomial) that the R.N.G. is based on, and * the separation between the two lower order coefficients of the trinomial. */ #define TYPE_0 0 /* linear congruential */ #define BREAK_0 8 #define DEG_0 0 #define SEP_0 0 #define TYPE_1 1 /* x**7 + x**3 + 1 */ #define BREAK_1 32 #define DEG_1 7 #define SEP_1 3 #define TYPE_2 2 /* x**15 + x + 1 */ #define BREAK_2 64 #define DEG_2 15 #define SEP_2 1 #define TYPE_3 3 /* x**31 + x**3 + 1 */ #define BREAK_3 128 #define DEG_3 31 #define SEP_3 3 #define TYPE_4 4 /* x**63 + x + 1 */ #define BREAK_4 256 #define DEG_4 63 #define SEP_4 1 /* * Array versions of the above information to make code run faster -- relies * on fact that TYPE_i == i. */ #define MAX_TYPES 5 /* max number of types above */ static int degrees[ MAX_TYPES ] = { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 }; static int seps[ MAX_TYPES ] = { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 }; /* * Initially, everything is set up as if from : * initstate( 1, &randtbl, 128 ); * Note that this initialization takes advantage of the fact that srandom() * advances the front and rear pointers 10*rand_deg times, and hence the * rear pointer which starts at 0 will also end up at zero; thus the zeroeth * element of the state information, which contains info about the current * position of the rear pointer is just * MAX_TYPES*(rptr - state) + TYPE_3 == TYPE_3. */ static unsigned long randtbl[ DEG_3 + 1 ] = { TYPE_3, 0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342, 0xde3b81e0, 0xdf0a6fb5, 0xf103bc02, 0x48f340fb, 0x7449e56b, 0xbeb1dbb0, 0xab5c5918, 0x946554fd, 0x8c2e680f, 0xeb3d799f, 0xb11ee0b7, 0x2d436b86, 0xda672e2a, 0x1588ca88, 0xe369735d, 0x904f35f7, 0xd7158fd6, 0x6fa6f051, 0x616e6b96, 0xac94efdc, 0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b, 0xf5ad9d0e, 0x8999220b, 0x27fb47b9 }; /* * fptr and rptr are two pointers into the state info, a front and a rear * pointer. These two pointers are always rand_sep places aparts, as they cycle * cyclically through the state information. (Yes, this does mean we could get * away with just one pointer, but the code for random() is more efficient this * way). The pointers are left positioned as they would be from the call * initstate( 1, randtbl, 128 ) * (The position of the rear pointer, rptr, is really 0 (as explained above * in the initialization of randtbl) because the state table pointer is set * to point to randtbl[1] (as explained below). */ static long *fptr = (long*) &randtbl[ SEP_3 + 1 ]; static long *rptr = (long*) &randtbl[ 1 ]; /* * The following things are the pointer to the state information table, * the type of the current generator, the degree of the current polynomial * being used, and the separation between the two pointers. * Note that for efficiency of random(), we remember the first location of * the state information, not the zeroeth. Hence it is valid to access * state[-1], which is used to store the type of the R.N.G. * Also, we remember the last location, since this is more efficient than * indexing every time to find the address of the last element to see if * the front and rear pointers have wrapped. */ static long *state = (long*) &randtbl[ 1 ]; static int rand_type = TYPE_3; static int rand_deg = DEG_3; static int rand_sep = SEP_3; static long *end_ptr = (long*) &randtbl[ DEG_3 + 1 ]; /* * random: * If we are using the trivial TYPE_0 R.N.G., just do the old linear * congruential bit. Otherwise, we do our fancy trinomial stuff, which is the * same in all ther other cases due to all the global variables that have been * set up. The basic operation is to add the number at the rear pointer into * the one at the front pointer. Then both pointers are advanced to the next * location cyclically in the table. The value returned is the sum generated, * reduced to 31 bits by throwing away the "least random" low bit. * Note: the code takes advantage of the fact that both the front and * rear pointers can't wrap on the same call by not testing the rear * pointer if the front one has wrapped. * Returns a 31-bit random number. */ long random() { long i; if( rand_type == TYPE_0 ) { i = state[0] = ( state[0]*1103515245 + 12345 )&0x7fffffff; } else { *fptr += *rptr; i = (*fptr >> 1)&0x7fffffff; /* chucking least random bit */ if( ++fptr >= end_ptr ) { fptr = state; ++rptr; } else { if( ++rptr >= end_ptr ) rptr = state; } } return( i ); } /* * srandom: * Initialize the random number generator based on the given seed. If the * type is the trivial no-state-information type, just remember the seed. * Otherwise, initializes state[] based on the given "seed" via a linear * congruential generator. Then, the pointers are set to known locations * that are exactly rand_sep places apart. Lastly, it cycles the state * information a given number of times to get rid of any initial dependencies * introduced by the L.C.R.N.G. * Note that the initialization of randtbl[] for default usage relies on * values produced by this routine. */ void srandom( x ) unsigned x; { register int i; if( rand_type == TYPE_0 ) { state[ 0 ] = x; } else { state[ 0 ] = x; for( i = 1; i < rand_deg; i++ ) { state[i] = 1103515245*state[i - 1] + 12345; } fptr = &state[ rand_sep ]; rptr = &state[ 0 ]; for( i = 0; i < 10*rand_deg; i++ ) random(); } } /* * initstate: * Initialize the state information in the given array of n bytes for * future random number generation. Based on the number of bytes we * are given, and the break values for the different R.N.G.'s, we choose * the best (largest) one we can and set things up for it. srandom() is * then called to initialize the state information. * Note that on return from srandom(), we set state[-1] to be the type * multiplexed with the current value of the rear pointer; this is so * successive calls to initstate() won't lose this information and will * be able to restart with setstate(). * Note: the first thing we do is save the current state, if any, just like * setstate() so that it doesn't matter when initstate is called. * Returns a pointer to the old state. */ char * initstate( seed, arg_state, n ) unsigned seed; /* seed for R. N. G. */ char *arg_state; /* pointer to state array */ int n; /* # bytes of state info */ { register char *ostate = (char *)( &state[ -1 ] ); if( rand_type == TYPE_0 ) state[ -1 ] = rand_type; else state[ -1 ] = MAX_TYPES*(rptr - state) + rand_type; if( n < BREAK_1 ) { if( n < BREAK_0 ) { fprintf( stderr, "initstate: not enough state (%d bytes) with which to do jack; ignored.\n", n ); return 0; } rand_type = TYPE_0; rand_deg = DEG_0; rand_sep = SEP_0; } else { if( n < BREAK_2 ) { rand_type = TYPE_1; rand_deg = DEG_1; rand_sep = SEP_1; } else { if( n < BREAK_3 ) { rand_type = TYPE_2; rand_deg = DEG_2; rand_sep = SEP_2; } else { if( n < BREAK_4 ) { rand_type = TYPE_3; rand_deg = DEG_3; rand_sep = SEP_3; } else { rand_type = TYPE_4; rand_deg = DEG_4; rand_sep = SEP_4; } } } } state = &( ( (long *)arg_state )[1] ); /* first location */ end_ptr = &state[ rand_deg ]; /* must set end_ptr before srandom */ srandom( seed ); if( rand_type == TYPE_0 ) state[ -1 ] = rand_type; else state[ -1 ] = MAX_TYPES*(rptr - state) + rand_type; return( ostate ); } /* * setstate: * Restore the state from the given state array. * Note: it is important that we also remember the locations of the pointers * in the current state information, and restore the locations of the pointers * from the old state information. This is done by multiplexing the pointer * location into the zeroeth word of the state information. * Note that due to the order in which things are done, it is OK to call * setstate() with the same state as the current state. * Returns a pointer to the old state information. */ char * setstate( arg_state ) char *arg_state; { register long *new_state = (long *)arg_state; register int type = new_state[0]%MAX_TYPES; register int rear = new_state[0]/MAX_TYPES; char *ostate = (char *)( &state[ -1 ] ); if( rand_type == TYPE_0 ) state[ -1 ] = rand_type; else state[ -1 ] = MAX_TYPES*(rptr - state) + rand_type; switch( type ) { case TYPE_0: case TYPE_1: case TYPE_2: case TYPE_3: case TYPE_4: rand_type = type; rand_deg = degrees[ type ]; rand_sep = seps[ type ]; break; default: fprintf( stderr, "setstate: state info has been munged; not changed.\n" ); } state = &new_state[ 1 ]; if( rand_type != TYPE_0 ) { rptr = &state[ rear ]; fptr = &state[ (rear + rand_sep)%rand_deg ]; } end_ptr = &state[ rand_deg ]; /* set end_ptr too */ return( ostate ); } ga-5.9.2/configure.ac000066400000000000000000000514421500715745200144200ustar00rootroot00000000000000# Process this file with autoconf to produce a configure script. ############################################################################### # Init autoconf ############################################################################### AC_PREREQ([2.67]) AC_INIT([Global Arrays (GA)], [m4_esyscmd([./version.sh])], [https://github.com/GlobalArrays/ga/issues], [ga], [http://hpc.pnl.gov/globalarrays/]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([global/src/ga.h]) ############################################################################### # Must occur before automake init ############################################################################### GA_TARGET GA_CROSS_COMPILING # This lets us use PACKAGE_VERSION in Makefiles AC_SUBST([PACKAGE_VERSION]) # Libtool -version-info (ABI version) # # Don't change this unless you know exactly what you're doing and have read and # understand: # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html # # Changes: # # ga version 5.6.0: 1:0:0 (ABI version 1) # ga version 5.6.1: 1:1:0 # ga version 5.6.2: 1:2:0 # ga version 5.6.3: 1:3:0 # ga version 5.6.4: 1:4:0 # ga version 5.6.5: 1:5:0 # ga version 5.7.0: 2:0:1 # ga version 5.7.1: 2:1:1 # # libga -version-info current:revision:age LTVER="2:1:1" AC_SUBST([LTVER]) ############################################################################### # Init automake ############################################################################### AM_INIT_AUTOMAKE([color-tests foreign parallel-tests silent-rules subdir-objects]) # Don't emit "rebuild rules" for configure, Makefile.ins, etc. AM_MAINTAINER_MODE ############################################################################### # Misc. information and package setup. ############################################################################### GA_WITH_HELP GA_TOP_BUILDDIR="`pwd`" cd "$srcdir" GA_TOP_SRCDIR="`pwd`" cd "$GA_TOP_BUILDDIR" AS_IF([test "$GA_TOP_BUILDDIR" != "$GA_TOP_SRCDIR"], [AC_MSG_NOTICE([Detected VPATH build])]) # Determine messaging library up front because if MPI is desired we use the # MPI compiler wrappers instead of the standard compilers. GA_MSG_COMMS # Whether to disable/enable Fortran 77 or C++ bindings. # Note that compiling the Fortran 77 source is enabled by default. GA_F77_DISABLE GA_CXX_ENABLE GA_DISABLE_MPI_TESTS GA_ENABLE_WARNINGS GA_ENABLE_CHECKPOINT GA_ENABLE_OPT GA_ENABLE_PEIGS GA_ENABLE_EISPACK GA_ENABLE_PROFILING GA_ENABLE_TRACE GA_ENABLE_UNIT_TESTS GA_THREAD_SAFE ######################################### # C compiler ######################################### AC_MSG_NOTICE AC_MSG_NOTICE([C compiler]) AC_MSG_NOTICE ga_save_CFLAGS="$CFLAGS" GA_PROG_MPICC CFLAGS="$ga_save_CFLAGS" AC_USE_SYSTEM_EXTENSIONS GA_COMPILER_VENDOR GA_WARN_FLAGS ga_save_CFLAGS="$CFLAGS" CFLAGS="$ga_cv_c_warning_flags $CFLAGS" AS_IF([test x$with_mpi_wrappers = xyes], [GA_MPI_UNWRAP], [GA_ARG_PARSE([with_mpi], [GA_MP_LIBS], [GA_MP_LDFLAGS], [GA_MP_CPPFLAGS])]) # If F77 is enabled, only perform a simple compilation test. AS_IF([test "x$with_mpi" != xno], [ AS_CASE([$enable_f77:$enable_mpi_tests], [yes:*], [GA_MPICC_TEST_COMPILE], [no:yes],[GA_MPICC_TEST_LINK], [no:no], [GA_MPICC_TEST_COMPILE])]) # Hack to append .x to executables. AC_SUBST([EXEEXT], [.x$EXEEXT]) GA_TARGET64 # Establish the underlying network infrastructure (SOCKETS, OPENIB, etc) GA_ARMCI_NETWORK # Checks for C header files. AC_HEADER_ASSERT AC_HEADER_DIRENT AC_HEADER_STDBOOL AC_HEADER_STDC AC_HEADER_SYS_WAIT GA_CHECK_HEADERS([assert.h]) GA_CHECK_HEADERS([errno.h]) GA_CHECK_HEADERS([fcntl.h]) GA_CHECK_HEADERS([ffio.h]) GA_CHECK_HEADERS([limits.h]) GA_CHECK_HEADERS([linux/limits.h]) GA_CHECK_HEADERS([malloc.h]) GA_CHECK_HEADERS([math.h]) GA_CHECK_HEADERS([stddef.h]) GA_CHECK_HEADERS([stdint.h]) GA_CHECK_HEADERS([stdio.h]) GA_CHECK_HEADERS([stdlib.h]) GA_CHECK_HEADERS([string.h]) GA_CHECK_HEADERS([strings.h]) GA_CHECK_HEADERS([sys/file.h]) GA_CHECK_HEADERS([sys/fsid.h]) GA_CHECK_HEADERS([sys/fstyp.h]) GA_CHECK_HEADERS([sys/ioctl.h]) GA_CHECK_HEADERS([sys/iosw.h]) GA_CHECK_HEADERS([sys/ipc.h]) GA_CHECK_HEADERS([sys/mman.h]) GA_CHECK_HEADERS([sys/mount.h]) GA_CHECK_HEADERS([sys/param.h]) GA_CHECK_HEADERS([sys/sem.h]) GA_CHECK_HEADERS([sys/shm.h]) GA_CHECK_HEADERS([sys/statfs.h]) GA_CHECK_HEADERS([sys/stat.h]) GA_CHECK_HEADERS([sys/statvfs.h]) GA_CHECK_HEADERS([sys/time.h]) GA_CHECK_HEADERS([sys/types.h]) GA_CHECK_HEADERS([sys/vfs.h]) GA_CHECK_HEADERS([time.h]) GA_CHECK_HEADERS([unistd.h]) GA_CHECK_HEADERS([windows.h]) # Checks for C typedefs, structures, and compiler characteristics. AC_C_CONST AC_C_INLINE AC_C_RESTRICT AC_C_VOLATILE AC_OPENMP AC_TYPE_INT64_T AC_TYPE_LONG_DOUBLE AC_TYPE_OFF_T AC_TYPE_PID_T AC_TYPE_SIGNAL AC_TYPE_SIZE_T AC_SYS_LARGEFILE AC_FUNC_FSEEKO AX_PTHREAD GA_FUNCTION GA_PROGNAME GA_DISABLE_SYS_WEAK_ALIAS GA_SYS_WEAK_ALIAS GA_UNION_SEMUN GA_GNU_LOOP_OPT GA_GNU_LOOP_VECT GA_GNU_STD_GNU17 # Checks for C type sizes. AC_CHECK_SIZEOF([void*]) AC_CHECK_SIZEOF([char]) AC_CHECK_SIZEOF([short]) AC_CHECK_SIZEOF([int]) AC_CHECK_SIZEOF([long]) AC_CHECK_SIZEOF([long long]) AC_CHECK_SIZEOF([float]) AC_CHECK_SIZEOF([double]) AS_IF([test x$ac_cv_type_long_double = xyes], [AC_CHECK_SIZEOF([long double])]) GA_C_POINTER_AS_INTEGER # Checks for C library functions. AC_FUNC_FORK AC_FUNC_MMAP AC_FUNC_SELECT_ARGTYPES AC_SEARCH_LIBS([floor], [m], [have_floor=1], [have_floor=0]) AC_DEFINE_UNQUOTED([HAVE_FLOOR], [$have_floor], [Define to 1 if you have the 'floor' function.]) AC_SEARCH_LIBS([modf], [m], [have_modf=1], [have_modf=0]) AC_DEFINE_UNQUOTED([HAVE_MODF], [$have_modf], [Define to 1 if you have the 'modf' function.]) AC_SEARCH_LIBS([pow], [m], [have_pow=1], [have_pow=0]) AC_DEFINE_UNQUOTED([HAVE_POW], [$have_pow], [Define to 1 if you have the 'pow' function.]) AC_SEARCH_LIBS([sqrt], [m], [have_sqrt=1], [have_sqrt=0]) AC_DEFINE_UNQUOTED([HAVE_SQRT], [$have_sqrt], [Define to 1 if you have the 'sqrt' function.]) AC_SEARCH_LIBS([hypot], [m], [have_hypot=1], [have_hypot=0]) AC_DEFINE_UNQUOTED([HAVE_HYPOT], [$have_hypot], [Define to 1 if you have the 'hypot' function.]) GA_CHECK_FUNCS([bcopy]) GA_CHECK_FUNCS([bzero]) GA_CHECK_FUNCS([create_shared_region]) GA_CHECK_FUNCS([ftruncate]) GA_CHECK_FUNCS([gethostbyname]) GA_CHECK_FUNCS([gethostname]) GA_CHECK_FUNCS([getpagesize]) GA_CHECK_FUNCS([gettimeofday]) GA_CHECK_FUNCS([memalign]) GA_CHECK_FUNCS([memcpy]) GA_CHECK_FUNCS([memset]) GA_CHECK_FUNCS([munmap]) GA_CHECK_FUNCS([pause]) GA_CHECK_FUNCS([posix_memalign]) GA_CHECK_FUNCS([putenv]) GA_CHECK_FUNCS([select]) GA_CHECK_FUNCS([setenv]) GA_CHECK_FUNCS([shared_malloc]) GA_CHECK_FUNCS([shmalloc]) GA_CHECK_FUNCS([socket]) GA_CHECK_FUNCS([srand]) GA_CHECK_FUNCS([srandom]) GA_CHECK_FUNCS([strchr]) GA_CHECK_FUNCS([strdup]) GA_CHECK_FUNCS([strerror]) GA_CHECK_FUNCS([strstr]) GA_CHECK_FUNCS([strtol]) # Checks for C libraries. GA_CHECK_LIBPLOT GA_C_OPT CFLAGS="$ga_save_CFLAGS" ######################################### # C++ compiler ######################################### AC_MSG_NOTICE AS_IF([test "x$enable_cxx" = xyes], [AC_MSG_NOTICE([C++ compiler])], [AC_MSG_NOTICE([C++ compiler (disabled, but some tests still required)])]) AC_MSG_NOTICE AC_LANG_PUSH([C++]) # GA_PROG_MPICXX is required to silence complaints that C++ source exists # even if C++ support has been disabled. ga_save_CXXFLAGS="$CXXFLAGS" GA_PROG_MPICXX CXXFLAGS="$ga_save_CXXFLAGS" AS_IF([test x$with_mpi_wrappers = xyes], [GA_MPI_UNWRAP]) AS_IF([test "x$enable_cxx" = xyes], [ GA_COMPILER_VENDOR GA_WARN_FLAGS ga_save_CXXFLAGS="$CXXFLAGS" CXXFLAGS="$ga_cv_cxx_warning_flags $CXXFLAGS" AS_IF([test "x$with_mpi" != xno], [GA_MPICXX_TEST]) # Checks for C++ header files. # Checks for C++ typedefs, structures, and compiler characteristics. AC_OPENMP GA_CXX_NAMESPACES # Checks for C++ type sizes. # Checks for C++ library functions. GA_CXX_OPT CXXFLAGS="$ga_save_CXXFLAGS" ]) AM_CONDITIONAL([CXX_NAMESPACES], [test x$ga_cv_cxx_namespaces = xyes]) AC_LANG_POP([C++]) ######################################### # Assembler ######################################### AC_MSG_NOTICE AC_MSG_NOTICE([Assembler]) AC_MSG_NOTICE AM_PROG_AS ######################################### # Fortran compiler ######################################### AC_MSG_NOTICE AS_IF([test "x$enable_f77" = xyes], [AC_MSG_NOTICE([Fortran compiler])], [AC_MSG_NOTICE([Fortran compiler (disabled, but some tests still required)])]) AC_MSG_NOTICE AC_LANG_PUSH([Fortran 77]) ga_save_FFLAGS="$FFLAGS" GA_PROG_MPIF77 FFLAGS="$ga_save_FFLAGS" # If Fortran 77 compiler was not found, we disable support for it. AS_IF([test "x$F77" = x], [enable_f77=no]) # If Fortran 77 compiler does not work, we disable support for it. AC_LINK_IFELSE( [AC_LANG_PROGRAM([],[])], [], [AC_MSG_WARN([Fortran compiler does not work, disabling]) enable_f77=no F77=]) GA_F77_DISABLE_RESULTS # Perform Fortran 77 tests only if enabled. if test "x$enable_f77" = xyes; then : AS_IF([test x$with_mpi_wrappers = xyes], [GA_MPI_UNWRAP]) GA_COMPILER_VENDOR GA_WARN_FLAGS ga_save_FFLAGS="$FFLAGS" FFLAGS="$ga_cv_f77_warning_flags $FFLAGS" # If F77 is enabled, perform a simple link test for MPI. AS_IF([test "x$with_mpi" != xno], [AS_IF([test "x$enable_mpi_tests" = xyes], [GA_MPIF77_TEST_LINK], [GA_MPIF77_TEST_COMPILE])]) GA_SUPPRESS_MESSAGE GA_F77_LIBRARY_LDFLAGS GA_F77_MAYBE_UNDERSCORING AC_F77_DUMMY_MAIN AC_F77_WRAPPERS GA_F77_CPP_SYMBOL GA_F77_FIXED GA_F77_MISMATCH_TYPE GA_F77_INTEGER_SIZE GA_GNU_LOOP_OPT GA_GNU_LOOP_VECT # Checks for Fortran typedefs, structures, and compiler characteristics. AC_OPENMP # Checks for Fortran type sizes. ga_save_FFLAGS="$FFLAGS"; FFLAGS="$FFLAGS $FFLAG_INT" GA_F77_CHECK_SIZEOF([INTEGER], [$ga_cv_f77_integer_size]) FFLAGS="$ga_save_FFLAGS" GA_F77_CHECK_SIZEOF([REAL], [4]) GA_F77_CHECK_SIZEOF([DOUBLE PRECISION], [8]) # Checks for Fortran library functions. GA_CHECK_FUNCS([dtime etime]) GA_F77_FLUSH # The rest of the Fortran 77 to C compatibility checks. GA_F2C_NOMAIN GA_F2C_CMDARGS GA_F2C_HIDDEN_STRING_LENGTH_CONVENTION GA_F2C_MATCH_TYPES([INTEGER], [short, int, long, long long]) GA_F2C_MATCH_TYPES([REAL], [float, double, long double]) GA_F2C_MATCH_TYPES([DOUBLE PRECISION], [float, double, long double]) GA_F2C_SRAND48 GA_F77_OPT # The ifort compiler breaks two of our tests if inlining is allowed. AC_DEFUN([GA_CHECK_COMPILE_FLAG], [ AS_VAR_PUSHDEF([CACHEVAR],[ga_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ga_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ga_check_save_flags]) AS_VAR_IF(CACHEVAR,yes, [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ]) F77_NO_INLINE=no GA_CHECK_COMPILE_FLAG([-fno-inline], [F77_NO_INLINE=yes]) AM_CONDITIONAL([F77_INTEL_NO_INLINE], [test "x$F77_NO_INLINE$ga_cv_f77_compiler_vendor" = xyesintel]) FFLAGS="$ga_save_FFLAGS" else # enable_f77=no version of AC_F77_WRAPPERS AC_DEFINE([F77_FUNC(name,NAME)], [name @%:@@%:@ _]) AC_DEFINE([F77_FUNC_(name,NAME)],[name @%:@@%:@ _]) # enable_f77=no version of GA_F77_CHECK_SIZEOF AC_DEFINE_UNQUOTED(AS_TR_CPP(sizeof_f77_INTEGER), [$ac_cv_sizeof_voidp], [The size of 'INTEGER' as computed by C's sizeof.]) AC_DEFINE_UNQUOTED(AS_TR_CPP(sizeof_f77_REAL), [4], [The size of 'REAL' as computed by C's sizeof.]) AC_DEFINE_UNQUOTED(AS_TR_CPP(sizeof_f77_DOUBLE PRECISION), [8], [The size of 'DOUBLE PRECISION' as computed by C's sizeof.]) # enable_f77=no version of GA_F2C_NOMAIN AC_SUBST([FLD_NOMAIN], []) # enable_f77=no version of GA_F2C_CMDARGS F2C_GETARG="F2C_GETARG" F2C_IARGC="F2C_IARGC" AC_SUBST([F2C_GETARG]) AC_SUBST([F2C_IARGC]) # enable_f77=no version of GA_F2C_HIDDEN_STRING_LENGTH_CONVENTION AC_DEFINE([F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS], [1], [whether the hidden string length comes after all other args]) # enable_f77=no version of GA_F2C_MATCH_TYPES AC_SUBST([F2C_INTEGER_C_TYPE], [long]) AC_SUBST([F2C_REAL_C_TYPE], [float]) AC_SUBST([F2C_DOUBLE_PRECISION_C_TYPE], [double]) AM_CONDITIONAL([F77_INTEL_NO_INLINE], [test "xno" = "xyes"]) fi # This is required to override what automake does, if needed. GA_F77_LD_OVERRIDE AC_LANG_POP([Fortran 77]) ############################################################################### # Checks for BLAS/LAPACK/SCALAPACK require both C and Fortran features to # have been detected. Further, they assume the current AC_LANG is C. ############################################################################### AC_MSG_NOTICE AC_MSG_NOTICE([Checks for BLAS,LAPACK,ScaLAPACK]) AC_MSG_NOTICE GA_BLAS GA_LAPACK GA_SCALAPACK GA_ELPA ############################################################################### # Checks for enabling interfaces to external libraries ############################################################################### GA_SICM ############################################################################### # Checks for programs. ############################################################################### AC_MSG_NOTICE AC_MSG_NOTICE([Checks for additional programs]) AC_MSG_NOTICE AC_PROG_GREP AC_PROG_SED AC_PATH_PROG([M4], [m4], [notfound]) AM_CONDITIONAL([HAVE_M4], [test "x$M4" != xnotfound]) AC_PATH_PROG([PERL], [perl], [notfound]) AM_CONDITIONAL([HAVE_PERL], [test "x$PERL" != xnotfound]) # X needed for xregion program, but only if desired. AC_ARG_ENABLE([xregion], [AS_HELP_STRING([--enable-xregion], [build global/X/xregion])], [enable_xregion=yes], [enable_xregion=no]) AS_IF([test "x$enable_xregion" = xyes], [AC_PATH_X AS_IF([test "x$x_includes" != x], [X_CPPFLAGS="-I$x_includes"], [X_CPPFLAGS=]) AC_SUBST([X_CPPFLAGS]) AS_IF([test "x$x_libraries" != x], [X_LDFLAGS="-L$x_libraries"], [X_LDFLAGS=]) AC_SUBST([X_LDFLAGS])]) AM_CONDITIONAL([ENABLE_XREGION], [test "x$enable_xregion" = xyes]) AM_CONDITIONAL([NO_X], [test "$no_x" = xyes]) ############################################################################### # Libtool setup -- no compiler/linker tests after this ############################################################################### AC_MSG_NOTICE AC_MSG_NOTICE([Libtool setup]) AC_MSG_NOTICE # temporarily restore unwrapped compilers # this works around a bug where libtool sadly relies on matching compiler # names in order to determine features (Fortran only, I think) # libtool doesn't recognize MPI compiler names, nor should it AS_IF([test x$with_mpi_wrappers = xyes], [GA_MPI_UNWRAP_PUSH]) GA_AR LT_INIT([disable-shared]) # and now that that's over, put the MPI compilers back # also, the above hack incorrectly sets the base compiler as the linker AS_IF([test x$with_mpi_wrappers = xyes], [GA_MPI_UNWRAP_POP compiler="$CC" LTCC="$CC" lt_save_CC="$CC" compiler_DEFAULT="$CC" compiler_CXX="$CXX" compiler_F77="$F77"]) ############################################################################### # Remaining setup -- some tests, some individual components ############################################################################### AC_MSG_NOTICE AC_MSG_NOTICE([Miscellaneous remaining setup]) AC_MSG_NOTICE # Establish some preprocessor symbols. ARMCI_SETUP # Set up MA. MA_ENABLE_CUDA_MEM MA_LONG_DOUBLE_TYPEDEF MA_STATS MA_VERIFY AS_IF([test "x$ga_cv_target" = xLINUX], [AS_CASE([$host_cpu], [i686|x86_64*|powerpc*|mips*|arm*], [AC_DEFINE([NOUSE_MMAP], [1], [Set some mallopt options])])]) AS_IF([test "x$ga_cv_target" = xLINUX64], [AS_CASE([$host_cpu], [x86_64|ppc64], [AC_DEFINE([NOUSE_MMAP], [1], [Set some mallopt options])])]) AC_CHECK_FUNCS([mallopt], [have_mallopt=1], [have_mallopt=0]) AC_DEFINE_UNQUOTED([HAVE_MALLOPT], [$have_mallopt], [Define to 1 if you have the 'mallopt' function.]) # Create proper types for our access functions. AS_IF([test "x$ac_cv_sizeof_voidp" = "x$ac_cv_sizeof_int"], [ga_access_index_type_c="int"], [test "x$ac_cv_sizeof_voidp" = "x$ac_cv_sizeof_long"], [ga_access_index_type_c="long"], [test "x$ac_cv_sizeof_voidp" = "x$ac_cv_sizeof_long_long"], [ga_access_index_type_c="long long"]) AS_IF([test "x$ac_cv_sizeof_voidp" = "x8"], [ga_access_index_type="integer*8"], [ga_access_index_type="integer"]) AC_SUBST([GA_ACCESS_INDEX_TYPE], [$ga_access_index_type]) AC_SUBST([MA_ACCESS_INDEX_TYPE], [$ga_access_index_type]) AC_SUBST([MA_ACCESS_INDEX_TYPE_C], [$ga_access_index_type_c]) # Make GP arrays optional. AC_ARG_ENABLE([gparrays], [AS_HELP_STRING([--enable-gparrays], [enable Global Pointer Arrays])], [], [enable_gparrays=no]) AM_CONDITIONAL([ENABLE_GPARRAYS], [test "x$enable_gparrays" = xyes]) ############################################################################### # Test suite setup ############################################################################### AC_ARG_VAR([NPROCS], [number of procs to use for parallel tests (default 4)]) AS_IF([test "x$NPROCS" = x], [NPROCS=4]) AC_SUBST([NPROCS]) AC_ARG_VAR([MPIEXEC], [how to run parallel tests if built with MPI e.g. "mpiexec -np %NP%"]) AS_CASE([$ga_msg_comms], [MPI|TCGMSGMPI], [AS_IF([test "x$MPIEXEC" = x], [AC_PATH_PROGS([MPIEXEC], [mpirun mpiexec]) MPIEXEC="$MPIEXEC -n %NP%"])]) AC_SUBST([MPIEXEC]) AC_SUBST([TCGEXEC]) ############################################################################### # The End ############################################################################### AC_CONFIG_FILES([Makefile gaf2c/farg.h gaf2c/typesf2c.h global/src/global.fh ma/mafdecls.fh ma/matypes.h]) AC_CONFIG_FILES([tools/ga-config], [chmod +x tools/ga-config]) AC_CONFIG_SUBDIRS([armci]) AC_CONFIG_SUBDIRS([comex]) AC_OUTPUT # Report on what we found. AC_MSG_NOTICE([]) AC_MSG_NOTICE([**************************************************************]) AC_MSG_NOTICE([ $PACKAGE_NAME configured as follows:]) AC_MSG_NOTICE([**************************************************************]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ TARGET=$ga_cv_target]) AC_MSG_NOTICE([ MSG_COMMS=$ga_msg_comms]) AC_MSG_NOTICE([ GA_MP_LIBS=$GA_MP_LIBS]) AC_MSG_NOTICE([ GA_MP_LDFLAGS=$GA_MP_LDFLAGS]) AC_MSG_NOTICE([ GA_MP_CPPFLAGS=$GA_MP_CPPFLAGS]) AC_MSG_NOTICE([ ARMCI_NETWORK=$ga_armci_network]) AC_MSG_NOTICE([ ARMCI_NETWORK_LDFLAGS=$ARMCI_NETWORK_LDFLAGS]) AC_MSG_NOTICE([ ARMCI_NETWORK_LIBS=$ARMCI_NETWORK_LIBS]) AC_MSG_NOTICE([ARMCI_NETWORK_CPPFLAGS=$ARMCI_NETWORK_CPPFLAGS]) AS_IF([test "x$enable_f77" = xyes], [ AC_MSG_NOTICE([ F77=$F77]) AS_IF([test "x$with_mpi_wrappers" = xyes], [ AC_MSG_NOTICE([ unwrapped F77=$ga_cv_mpif77_naked]) ]) AC_MSG_NOTICE([ FFLAGS=$FFLAGS]) AC_MSG_NOTICE([ FFLAG_INT=$FFLAG_INT]) AC_MSG_NOTICE([ FFLAG_NO_LOOP_OPT=$FFLAG_NO_LOOP_OPT]) AC_MSG_NOTICE([ FFLAG_NO_LOOP_VECT=$FFLAG_NO_LOOP_VECT]) AC_MSG_NOTICE([ GA_FOPT=$GA_FOPT]) ]) AC_MSG_NOTICE([ CC=$CC]) AS_IF([test "x$with_mpi_wrappers" = xyes], [ AC_MSG_NOTICE([ unwrapped CC=$ga_cv_mpic_naked]) ]) AC_MSG_NOTICE([ CFLAGS=$CFLAGS]) AC_MSG_NOTICE([ CFLAG_NO_LOOP_OPT=$CFLAG_NO_LOOP_OPT]) AC_MSG_NOTICE([ CFLAG_NO_LOOP_VECT=$CFLAG_NO_LOOP_VECT]) AC_MSG_NOTICE([ GA_COPT=$GA_COPT]) AS_IF([test "x$enable_cxx" = xyes], [ AC_MSG_NOTICE([ CXX=$CXX]) AS_IF([test "x$with_mpi_wrappers" = xyes], [ AC_MSG_NOTICE([ unwrapped CXX=$ga_cv_mpicxx_naked]) ]) AC_MSG_NOTICE([ CXXFLAGS=$CXXFLAGS]) AC_MSG_NOTICE([ GA_CXXOPT=$GA_CXXOPT]) ]) AC_MSG_NOTICE([ CPP=$CPP]) AC_MSG_NOTICE([ CPPFLAGS=$CPPFLAGS]) AC_MSG_NOTICE([ LDFLAGS=$LDFLAGS]) AC_MSG_NOTICE([ LIBS=$LIBS]) AC_MSG_NOTICE([ FLIBS=$FLIBS]) AC_MSG_NOTICE([ BLAS_LDFLAGS=$BLAS_LDFLAGS]) AC_MSG_NOTICE([ BLAS_LIBS=$BLAS_LIBS]) AC_MSG_NOTICE([ BLAS_CPPFLAGS=$BLAS_CPPFLAGS]) AC_MSG_NOTICE([ AR=$AR]) AC_MSG_NOTICE([ AR_FLAGS=$AR_FLAGS]) AC_MSG_NOTICE([ CCAS=$CCAS]) AC_MSG_NOTICE([ CCAS_FLAGS=$CCAS_FLAGS]) AC_MSG_NOTICE([ DEFS=$DEFS]) AC_MSG_NOTICE([ SHELL=$SHELL]) AS_CASE([$ga_msg_comms], [MPI|TCGMSGMPI], [ AC_MSG_NOTICE([ MPIEXEC=$MPIEXEC]) ]) AC_MSG_NOTICE([ NPROCS=$NPROCS]) if test "x$with_sicm" != "xno"; then : AC_MSG_NOTICE([ SICM_CPPFLAGS=$SICM_CPPFLAGS]) AC_MSG_NOTICE([ SICM_LDFLAGS=$SICM_LDFLAGS]) AC_MSG_NOTICE([ SICM_LIBS=$SICM_LIBS]) fi AC_MSG_NOTICE([]) ga-5.9.2/doc/000077500000000000000000000000001500715745200126715ustar00rootroot00000000000000ga-5.9.2/doc/armci/000077500000000000000000000000001500715745200137645ustar00rootroot00000000000000ga-5.9.2/doc/armci/armci_doc.pdf000066400000000000000000002670041500715745200164100ustar00rootroot00000000000000%PDF-1.2 %âãÏÓ 2 0 obj << /D [1 0 R /XYZ null 766 null] >> endobj 3 0 obj << /D [1 0 R /XYZ null 53 null] >> endobj 4 0 obj << /D [1 0 R /XYZ null 727 null] >> endobj 5 0 obj << /D [1 0 R /XYZ null 700 null] >> endobj 6 0 obj << /D [1 0 R /XYZ null 684 null] >> endobj 7 0 obj << /D [1 0 R /XYZ null 668 null] >> endobj 8 0 obj << /D [1 0 R /XYZ null 652 null] >> endobj 9 0 obj << /D [1 0 R /XYZ null 638 null] >> endobj 10 0 obj << /D [1 0 R /XYZ null 609 null] >> endobj 11 0 obj << /D [1 0 R /XYZ null 588 null] >> endobj 12 0 obj << /D [1 0 R /XYZ null 308 null] >> endobj 13 0 obj << /CreationDate (D:191001102164319) /Producer (\376\377\000A\000c\000r\000o\000b\000a\000t\000 \000D\000i\000s\000t\000i\000l\000l\000e\000r\000 \0003\000.\0000\0002) /Title (Untitled Document) /Creator (FrameMaker 5.5P4f) >> endobj 14 0 obj << /D [1 0 R /XYZ null null null] >> endobj 15 0 obj << /D [1 0 R /XYZ null null null] >> endobj 16 0 obj << /I << /Title (A) >> /F 17 0 R >> endobj 18 0 obj << /Length 4648 /Filter /FlateDecode >> stream H‰œWKÛȾðè#X4ß½y7Hb#ö›I.ëzÈ–ÄX"Šò¬ö×§ª¾jJšq `Ä®®ª®÷ãLJׯÞþ)1‰yؾ~ÕDMibú“,΢¢2Y5•y8¾~›aÿùo‰ÙøôÐÊÿ§×¯‚$|øsJ•S’EM*¬ð•ÄqTU¦Jâ¨LÀ,Šã´þÎKáôkðî—?½ÿÁ¼3ᦎÊàs˜ÇQŒaåÁnÒ4Ê‚%$š*°aÕÁ£\œy· “8Ø…›Šæ0#j·³‹3¿¸c¸)¢D¹äã=dÆán’¨ ôåŸÆ‰ùyàûŒ—°¢£T ·³­ ÿùðá΢0§I‹8ªSÖY”Á˜¢t•ÀˆQœÄ¹êÿz4aÑÃMF¬g~5Ná&']ûpÓˆÔDž$™·LÑC’7úaLHWYð3k:·à¶ÜÑ?*ýýSJ™ée Ü•qû‹ÚØûZ¾ÒœÄ/LI*WÙêë2¹ú:ËT×vbê¾Ò£Ið‰ Ÿ’’#ë³cK‰Š`RðÖX‡Î|ÀWϾ œ8É 4æÃ¢®îÈʈ‚¯¤ØÎ›UĤôÞˆ“J%ü¦9ÆñiÙ@¿Ç–؆){]O­`˜OcXþ¼ìŸœ@N‡M°˜O`±ô@ä¿Úü¯ÀyTJ1ÎVkz©½SS¥Qܘ¢I£ä&®âòjìÊÖÇqé¿… ©,êä •Xн>¤%ò¯ƒm¦Œ÷•c¯ vs(¡4ž‡îEœßUŽ&¡* ‡kÄ™·nUøT7çOEñX²ÂüÇhBA`qEy}èwøÞãOî .Q8þ£lÂáȧ†#^HQ€$ðÍõàšV‰&Ö;[Áý‰Ã4 Ì€sPÓ"È¥å„_ Lånñðá òºƒ}¯€Ó¢˜½èùˆ×Ï^ o”™¢Þó¾Šñ/x>7*zºÞØÙ©Ð"­ s÷ÌÇkŸº«?8¥÷ÆVé•M¢ƒD©cªþ÷áZõ¯i¶½¥?:s K6r&原2§Ü‘¿#$™ù˦œÑ”\)ûíÉ”æ5—ü\*•Ô c‰á†Ã;?7ôbî…ôŠÏH½÷¬5ÙjË9W³Yf;ð‹[6%•ÆÂÊI¯È6ƒœ—~wÆÍ™)Í#•¬¯rþU''+/è48PàÚ'06ËV).$÷=/¾?ÔÚH…ÍÑG•\-ÊžäëIþkEyøƒ ¸ä×€ZE'G%EÍ·8.hv½ïœˆ›Áøbi1È£¸Î *ÞãEê=¾vùcÈeÕ…,¾[ùž._89`»nY©§%9íôðô¨:<^ »D™äîKø¼B‘¬y§7RéôÁRÕ¾Üsr\DØëȆ_ÄìkøP°Ú"¦àˆÙˇx® ”~Ø™ (ªpqç"#Q—r®ñµ"sTÔ9úö|°Ëb¹ (Ïx¿Ý‹âvŸw⤂%"x/°VïÉÙN^0Ë(àiTÙ}¢–ÔóúE¦fëxRç¾eOaƒâÊÅd‹ «¹ŒJ ZQÆÒ°ä¥T$9á°„¹¤5…Q!áJèOÜ}¨T-{ìûÿ-¤@q™wƒðoÑ2/f¸hyÂã¸tüŠ÷Ì*¦±äW–}k©!ƒƒ—‡ÜÉG»Ee˜Ý¿ÏïV†Î JÞꯒQoâÓy<ß1ïÀÂZ³Ü1“ÂCX[HïfÿK°õj­Y+Õ³^ išª{ÏQ¿É\l¥·å(Ú ‡’愌v-Ï#r=ÂG/¶¤ pÛDàšÈi©5G w–¾ÃW¯îŽw/rŒ€HÙÄ IRESa£mØ¿bTE»U‚|¸8…¿æ;èÏL–=èÜ-¡òRÈJ7ï.Zõ¾#è‰êаá–¨æM0Þ*º½iíÉ)ä yQª64s”ø«MU>µ6)L™[5;¥˜ä¶j¡¼O÷±á‡­$®ý´Õs8åƒRIèý…â ]QÞŽÇ#l0hb(„gl 8]𳸣¡„‘¦ êý°cžÎ¶ æ®CìÐ!é,Âć©G®ÝrüW·‚tv‘A?ƒ‚…zŽsÇ€‡“';󂇫£Ã•؃(ŒóÀâôX0žÙê:™&Ta¬Öže–ÑYO§Å“úŽòhµ˜ŸdŽÔÐê(…ñK ê§$ÌVÞ¸ò‚N½~‡é:…ÝÔ{×íTÆuülÏ·ß: R§ªÎK‰{éup¥ycéý¼|2÷C悱{a®b˜>WeK‰Ùˆ=PjÔË ¥2±ÖY_S¹Ö‰˜:ûÕ ª¬ðrTΟÊl§;§Ùµñ7~¥Ðëu)ë¸Öc=ãÆÊñ…QçÝç÷¤¦]d†Ë0P(ð”‡}`…’xf²Üµ«À_ËœÀú+7}Í ”òh‘åDÖDcu<ã±Å@RjƒÂu¦³l®­§Q©e }Ÿ“Œ¼Åû€×¹¿½£VÍþú`/PëŒ)ˆI¯AÎPÑ=ïLÙuÿlüäoøèQ×ê‘}Tz¡ÖЩÅb¢¡ò_­zžhõšh'ˆG€NºÎá°øÈ‹X9"ù;hêëºv ‹œ>¸†?—Ó疱뗲Sêáfƒ¤¬{â©.¿nk7RÑÖ¥aÿGÛC¯zÞç+§› U0<'¿ñé•&éšÿýïî¦øM·GßÙ|¿ÅIšhl¥ì-W}êbZ}õ½­áM­AñØe:­ê-ò½Ÿ(CÒš7?¶ó‹ŽÅ©1ƒ£2¶×§´ýhrÝ‘ŒvŠß¸-ZH0Ü&ùšc?ôG¨ïxÚDó<>úg¨™3Ã;7S”¬]†A»g•§ôFIsß–±ê¹Û}ð‰¥¡ÑC7¸¯TJh¡”Å×) MÀü†'X|y·DîL×ËÙíò¿vSn»dÁÑXy`ë”ÿ´JPÝK4-X-GHÃòE#ƒu;…ž²ÖaêÍK/¼Ø4Aöœ¡‰®?G0¤R†ÁáZÊzàQ<‹E]õ¸¥™_Aç3–ºEW¶[&Åzôåvž±³§eÓéöDFco§¾±u šuÍ !ÑUÆ H;wX‘™ãÂK}ðb”à"`=,²ÎòžÄ\Þ„©ê¨ÞWñ29ñXʲ`¡¨t•üª"…kzq5½Š Äa±;Hä±°n•«…I.w/[zeÔì#Í vÁÕÎî^Zös®Çùž!Þœàdª;ýó‚²n£Mé{Êï/÷·/º¢Înë–vßãv‡öúvo‘¿Ê¦K–L°ôA±Iõ„ý¬½V©‘½Ð«ÄmU:ÑHî”ýåî-Ýz™M¤Œö~0“î~”œ=Uœ7fvGÈq;Z ÖŠ˜G7¿´€þbÕTÞïÈŽetz–0•ßê$Yûw-mgƒà—‰È¹5›ÎnOŠ(©\Éø ™UÁÚËÆó‘é‡`]Ïã|; â$ÿ¥ºe{¥Öj‹”ø;Ô® Øm8py¡¿;ð˜E™X!|kÆìûP6“SÉ0ž]E´Cë¤G•J¤q,óén*ò¯‚ݶ z/ÐØ£XI(‰"utÓ´Pøâæ@S+‰$²$UCß™÷fIJu^lírfvvvwÞ{zFXgt«:B‰IHQâ?GdyŠíÔ–‰àžìE|dN˜žuì`è@SÒ_Aõoœ5‡Œa¤FmÍE2Ƈ~\Ê[Wêìí›ã0Ë‹CAª&íÙ7Úgðùu_o ¸Æ$mfþ Þbò“(˜Îˆ!n“‘Y^,‚JÚÂVjÆP¶cŽü†£w¬¢ÀÕ‡d攬%ÑÔ}ùõ»¤c­o’P{*'¥¦A8]È_³ØMCyêÍc)×ó(OÔfõ¤yðK}íFb€}Yä>ÈØMéNšRK–  6ÓFº@»rÌø0&ƒz‡ñTá£Èo0ÃBÏ ä ØÊš£Ï‹í…D¦ +Om±SeÍv kÿCaŒ~Í­´ä¨F®›ÐUŒH¢Ï "j µÙK¶ùÎóH]ýsÀ¥Ør-ŠÈ 3©Uw¶PGæ3hW°b/Ü}ÔöA¸oWô»Ï´[¼*^/‚V ™| •¸\ííóÃúÓº\? ï’Ù3¿D~åÈezÒÑ"<]dß°½S’Yv8ØîûÊÞ`A: OK;úãD±Rsœ¿"¤£ûüûÊUø `DÚm'àô%ÔÀÀ)¹£þõ3EÈš Rgı±¼ s(- {ÔÖ[.’~=XHîåî{\÷‡Ç§u È©r2÷<ÅϯwŽaÏôbîs--br*ñõᇋíÔ\Û÷;ß²é§}ÓGÊ[:Ö?–99ž%žÁ±od @üBµŸåýSÄ^i1&*òpæÁ˜¤üZdAµžd¿€ÅûÎ0M ´—J{ô܉Þi}ôx/H}áb2ÅtÁyÎA(:šÜÌ„Ø}-”¬Úpi'pϧ±¢õê^×ó îr*Ù›·ì¯cð*Û¤µžÚë«0)P§¥“ò·¼^v˜—A›‘­*ž¢¶HÍWõˆu%‹”eWúp¤çp9`¶GûvXÉA2ênL¸¬6Á;]UáBà à쮯a¾Ç÷N…ØZ™]¨-ý“ÀIN% È+P-"»Y*2ÈJ ê±,ZÛ†¥ewé†q³äVmaÅ ‹SÞ2½F†aŠ|•wŠjHlNz:|2•gŠ8‚™Í˜­ÄÚô¡3÷âÛW/:*ÆSøØ•¾p®ÄEkx¤'pôD\²q*&ý'(}êa·ÐÝœ•n ôiª´ ÀÚ¥7ŽS)â;¤ó›nòö/ûg<µb!‰Hh…ºfGP{I®AïÕÝD|=3À6îÐ)nxååsüÞXë•Ò[ìo±ÌZÆä@Ìœyʓӥ¥[b´%¾ˆÖñy5×gy)ŒÇ´»£›'ì™äÆÙúåÕ:Ùu¾Œ ù OÊÊ2&Xâ‚Ý&3Žõmã!ö±µ·?­îo$’hÛÀàÍk<}<ø§/ÿUS[ˆ:9lp¼µÿû ŠÜ’‘yHe6›á¼Õ2FŸ„'ÄD}¡|‰$1µ@¬R„b7;¹A*IÐ=/ú\c¶€˜4ÏJ­o‰Õ‰µÊ{ü›`êD3TòºÐ2ÓªdÃ#ùQ³ƒ®Ñ0ÓÑjBÿéwôŸ\éÀöôÜ"t;9’Ó&3]´1±cê.7=xÆÈ>zú:꺓!Ó„³"n?\0ùGvÚnÑø7ãž5noŸ]:è·çlÆq#í cÁn:ot<ö5KéôŸì écmèé¶BßæÁ²¤ûQŠ´è21‡{”ŠîlM©Ú2ä^Ñ1»6-˜ÕŽïB×jgÚ2o:µEnFÚM ÇŠª"4•˜ME?“ݸ¬¨ƒ³Yh™G9mVacBº }W§ƒ6Ao]E‹e2óÝpÎ[šMX‹‰…4Cõ®NÔ1ë´k75ú•¦^<ædj-†„è]]Át¦uŸyé§ò¬¶€ëérl¥‰GD$¯À>Æ4QæZ4J`yãÔbôi¦¥$6•Ö¬ÞÚ«eïËëYiÚVÒ°-µmÇ8¿¤KÂ\u‹¿Â …ÆE×¾ˆmÞ–DÖ¸ÏëOë;÷›6”Ñ_r‰Îömýx(^tòÎékƒ Öè=ªd´~S>þ¸D™Wú¬S%œ°ûôåçŸþ˜÷  endstream endobj 19 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F2 21 0 R /F3 22 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 26 0 obj << /D [25 0 R /XYZ null 730 null] >> endobj 27 0 obj << /D [25 0 R /XYZ null 580 null] >> endobj 28 0 obj << /D [25 0 R /XYZ null 339 null] >> endobj 29 0 obj << /D [25 0 R /XYZ null 279 null] >> endobj 30 0 obj << /D [25 0 R /XYZ null 211 null] >> endobj 31 0 obj << /D [25 0 R /XYZ null 193 null] >> endobj 32 0 obj << /D [25 0 R /XYZ null 146 null] >> endobj 33 0 obj << /D [25 0 R /XYZ null null null] >> endobj 35 0 obj << /Length 4997 /Filter /FlateDecode >> stream H‰„WKoãF¾0ÿ¡äÆbćHq÷äõ&Y0Ƙ1ö2“C‹jIÜáCK‘ãèßoU}Õ’èI°ØÝÕõ~|ýÏ—÷ï~ü96±yÙ½WFen–ô'é2V…IWQY˜—öý»¥Ùõ/Ÿb³?ñꥒÿ¯ïßIøòß÷ïIeiZ˜|-ãļü‹¢å2)„vAßq\ÈÏÁË!\GEàÌý~/_C¸ˆ£Á]µÜÙêí­™•Šk[šýòâÊŠÜZM““Î| ºÞ¸£®+QþOÝÌÇ5e¨rgþÑ;TÆ^ÕÊ Oyû}>:g>*/»¸tìŸÃ˜] œ3ðd[…¹~¼ó¨=J»r[sTŒÔï=RØæáÕÔ8ýtcº#ì‚1äÆ38Û6uçd0×-ÀU#G®¥idj­Åb{¡Uô Å(9–ÝzôðTŠOà £BIkî¿å³¸zésЪ´ÊEûÒ‘ó²¼Bºµw&}«3 ³ÐPçBàžÊ6SG‡Ökè“°>¬GPɀ犷8|$ãžÝrL:5Ž;AZ¸dCjWgyÄT!cÀ%Zpõgdûƒw½H>PâpÎ|rÂ$PH\¹–€‘Ÿl½—ãÃxQô5äš±ºÚØ(¥lùà \™%V =ýûÔÆƒW†×ä·jÁ%ê×Fݽ÷ó_ ñ›fû¸1HJ¸¿dÒm°àÔnzY9-ý«¤µXìéöKÉÏ B³ì—îòà±mÎB*h1ãWΊs¬w|!ÖÇ ·x ¾(72­ @Øéµ ç{*ðR2FŒW­ïôcìÁä ÉÖÒBi tÒëyLØrFf®HsP'xc¿õî}úZ·Õ—¶Ý:Yíÿ²/³µ¢¾ÒƒÃ(ÞÔûIa«€ˆ‘%<Õ+ЖÆqŠFù¨G=PðÐ.°V„bWÕ;ß· T×jåÁ¥øû¶] ~$¤/78/ipwŠÍYG×òdWfä]Îú8a¶ýÑcñÙkJÎÞ2ÀöµN½05ç7ÃüòÐLã›QÎ H™Ïö+·‚-5sËs‡Ê—^‡Ì°äÁÍÓ7“'a"=ûúmªÉŒ¢½ç[VöF‡%¬ô?Í€ÅA40¶Â:@·“’€ãW~±òê§/É]ôoÎhp2þç Ù«Ü â#nhb¤9ÚáVt%7&ØÊ¯ Gò>#âÁPLv_$C 7U,Ô‚qß½}‡–Þóqâû ‡ºDN‚G£J¥&J¼×H¹Ê²‰k1A^+×w“ì4€_ŽMއeÄ€%÷ïÏHªÎ2yœ®ù¹9Bf/»T³1÷RªÙX·j!ÜJW=wäLºþZ"Ww£TÜtuöÓè­H¥!Ê¥Û Ë/á?Ì¡SÆ/ôª Ó"J¥¯z#ÉÛ¹àêÃüP- ˜` œÔ{[Óac4g:÷F«|E½³ú–Þƒ³¥‰QêñÌÇ\2VrJsw‘<‘hœa{FiC¤¦@}– ¸vØëƒ¸y%uÌóºDûÆpðÔÊ|‹Ú‡g2Š¢Íù)Án¡ 7:z<ù©Â©D‰TJ“WÄÉ÷ÿƒƒgP¼RÛâ'Ùw–º-Á3Üy=8?>¨ÇÑp‡u·¿²6`då¤uf¦?wº?D éIë›Ê¸¹J£¸ug>P ^Á Gw—ž½ ü-þ?îQO» kÍ–ú~ÊÃJ*ˆ¦ÛÀóüF‡âV‡\Ûv9Éuzʼnº3 )ª›-[ÄŽŠŒ-¡²Ôä`¯Öôqæ§àüa íœö°Bñr- ˜¡÷Ž{"ÅÁݪDÍnî¶åñ•{ùž×Ÿ4¨4O5 -JŠÞOò8«QRh,ZK(ØÿMõà¶h"³6f˜ËàŒ%;šb˜¾S¶ZªèmÕ±©lÈô)©rŽÓeò–ZÑvÁ³”dm­—Æá朡‘6‘O}ë´hýûn&Dx‘®Ûi+í@ÍÝ7V z™Œ¯ÃmF{'æiz™¯9 ‰ù}vÙcò†pKäTbµ9ðÎLDBÓX$ÓcHj[þœßÓÕV€g4…T ,¼ìZr¶÷ìO4V¤æŒUsÏ—ÆÛZð>í×"·ò½@ ³cªÌË‘öLèå8ç0ñºã[·ÆÜðA£Êð}ðú*J…)“0£áGSQÅÏôÌô*Íq¤ÔÅkcÝÌ:xg’3T-¿°hÅ®«•’õ|[Ù…®/¡ä—Ä‘S‚mý?áUÓã8rCïòê(m·%ëÃÊm²› ú0É`·\d«ÆVF–Y§÷×/ùK¶;³4ÐVU±HVùø‚1Cˆžð…€Ü Ä t…Ð.@?! >­HÙ{EÍRªóÆêª„r { è^ÁeS³6 ==Є\”òP ů†V®”½hiÄ×ÀõÉqÌ\X¯tœ_0AŸ¥Ú.Ýë¡5ÏëÏ„’«‘i¢¾Þ½?3=侃ùbZŽƒÉl蛡Z`¿¼ÇcžIjHªÞ–ÀÆ;ÐPi-ü’*ýÿtó€v~÷!Väý¤Àò¢j›ݠdš­ -‹±G@ÙÜ%µ‡nTÄ”ÄÍJ΂Tj¡B™™oÝ ÅÕ¡ãÏW*ócWŸØµ°„o"0k½œ,¢hOKÊû©ADiïÒÃÐïå~š”!ã_Øä݇õõ«èãwròv°e½ª>8çè”݃7_{®N5¸olU:UIù­·«°ŽK›Œ¬Ǹ`MÁWÞ»ãýÔº{jw—®]k—ÖÁ9”nñv!çtNߥ{Ò܇=®‘€ë‰>m?/o Y±ÊfÚ®ºaûÚ›Wk€Ì¹*tFÖ×ÐEN±©ëÙØØ-YßC%ƒrFÎÃéTœ†pмֻo红%kÝ”®9ç+"ûi1»kÅ‹ ïwë Í+î?Z/º–‚®EuÛÕÛnf#+Ônoû“œ±X7$XT!4™ko„>íÓ»Ìo9(UÛï: å ¥ ÊR”)–òœR²tÔ@cEN´8©¼·á¦¾mÚX¥-I1¥µ}ÞûéIó€äYŠÞZÂ[\Æ3…ϵ¬EO<”øõ<˜°¼É(ÁY3n¹ùüCê$?K–¦n%òS¦ËUå’*[j}>†*0×êÕz®Õ?Jª.*ÍÕXñÙ}_kȇ·_9Æ*úi/;›GaºŒžÃsp"1'„KpÕ Ž”„%ñtæ I5ûë~-È£ï0zq–åðÊá3Æòœÿ[ïû 4ªr6)‚Ešé·ìv[€Aa Íû€S\‰hÈ;5[ÌO$ý,nðá„ÀÆpªÐ\ô÷d4âdÓ£|pWç 9àóB=ÎÔíàX˵~é^4*q7ÓgƒUô™³<Ûh¦+w)JÁð±žÂÒpOk~<åÉ6¤!J¥=š†èwc+Ѽ½ŸSŒÆ°ßiUMp­ ˆÔ=g„»Èµ_88¦Š´‚’7® — §¡®ÇL”ž¹¦ÖY :µof4‡4ITè-Î+Pœp¥õj“çΆ±¯Š’eüúo ÚuÈÞÁêÒÙ ¤CˆþÕ÷Ú‹”Km*µu-yö/qŽº xѸ—ç¿“ª}•TÏ’ìæÓ}žäÉ&¿3ycÒeX‚¼yÉÊdzZ×Ê DwãÀŸQ!tñȬ8wžÜÞVÉez$Œ$j5Gt±EÓ'”µí•ùLž*4’ •Ãò¼[ló½Gï¦öÄ-ûg/!¹à#C§ipBˆ îÈqÕt¤]Æ Jñôí/žo`Avªiöš#™RÕ"8b˜æ[d6Øs¼Ï†´´GIV›=ÊqnDñ£ï®ågë§«bR=n­ß€ \:¶ñMzÚ]Ýõ „Þ–©å8L Ûa¤$øTÂa_&ešýÿÅÂN»Ð)T´ Õ71KhK t´;PnÔ:$éešÜåÞ  ZÛÆ~þÛË?\ˆä<™P\.j5çYZ²Úþ3Ò2‘VŒ²4±J«M××÷ù.Ó\ª’åo=¿KÖE& s•o‹xµ6³×“¶—>Ö„ý†¹¬Ê’»ô[Ïé·.ì9EO¥G‹ôÎô¸™\›E{} l!8¬<~Ä;ƒp*ÒÙúð9Pš¡í!‰(5qÓ÷§8 Îôæ ¡]ÕQ—>dD'Pf„ƒîêDã~ÜíjÌÇ©ík+oêgÓ˜eo¦)åá6¿Ÿb0S[ðîíq—4Ñ*f®ô‹Ø%} »òÿI±b.8IRÜ7ʾ:ßï§ȽÒNÝ,QïŽ4JÓ7×*½à¸@¬ŽW`ñè Ù*é/ÇAqÑÏ…GŒï´Ÿmqª}Ì^š,N˜]ºŸnó°á„d˜µéo©¬‡G6D¯\T~¬OôëêÍÓÎÝü½å¬(…uöª9ïô/¯üïF0\5 endstream endobj 36 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F2 21 0 R /F3 22 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 38 0 obj << /D [37 0 R /XYZ null 524 null] >> endobj 39 0 obj << /D [37 0 R /XYZ null 511 null] >> endobj 40 0 obj << /D [37 0 R /XYZ null 498 null] >> endobj 41 0 obj << /D [37 0 R /XYZ null 486 null] >> endobj 42 0 obj << /D [37 0 R /XYZ null 473 null] >> endobj 43 0 obj << /D [37 0 R /XYZ null 461 null] >> endobj 44 0 obj << /D [37 0 R /XYZ null 442 null] >> endobj 45 0 obj << /D [37 0 R /XYZ null 405 null] >> endobj 46 0 obj << /D [37 0 R /XYZ null 387 null] >> endobj 47 0 obj << /D [37 0 R /XYZ null 344 null] >> endobj 48 0 obj << /D [37 0 R /XYZ null 587 null] >> endobj 49 0 obj << /D [37 0 R /XYZ null 91 null] >> endobj 50 0 obj << /D [37 0 R /XYZ null null null] >> endobj 34 0 obj << /P 25 0 R /R [63 63 549 729] /V 17 0 R /N 51 0 R >> endobj 52 0 obj << /Length 6752 /Filter /FlateDecode >> stream H‰„Wێܸ}7àࣴpË)Šbü”d× ²ƒ¼Ì. ¹[Ý£ /³½ã ÿžºQ*õŒcð´ŽÈbÕ©SUÔŸn^¿zû¾2•¹Ù¿~‹Ø˜þÑWºÂã|ƒ¹9½~Uš¬þËÏ•9<àÓÍ–þÿòúUæò›¿~µ±UQ;Œ/‹èbkn~„EYZO‹7ðÛº†¶ÜfÃy{|ÌÛÂg»|Ge½yÈ7MQeÛnšúÑtg~kô·ËË"dS¾iáÏAý˜û¶¨³ÂÜÜõf!p{Ò娇¯ýÎ|xûwó™ñít!¤½{~-ާ<‚éN”rJa8 ê)–u€ŠLa4"Œ{Jät7ðÔF«Ö'%`Ø©ðHuv^Ð!ŠìLPòsº£Gö¬ÍÌVv ý«Ñ캩£ú"î)Ê*»lá5ª4-ºØö±Òî5UŠ*9€7—íóBôNí©íÜLJ0&1ú² „eOlbS»Âµ ./7¨ÂÌIådÿ]ާwù!ÿcÁŠ»†Ã"ÿU š!·ÐCδàoj€PÁý¸Ï± fݶÿÃu±ŠàO¬|û­jÅ:øã?ÿöçÿÁIþ×/Ù"ø“Ù=l±JÆÛ_ßPÑÀO¬\ËÀýxÙþ’¿&œMD4e%§ßÜ(-Æœ[ $ y ŒŒÐ!\<‹áÃ#-;õp\Nš@+/¤;î„‘:!ƒ—=#†—Ò®¯PyŽäw-_Ô¾m$ùEÖ-æ 5 ±ã¨Ó³€òŠuW€LfÑé©UP'R  ÚWÕãAu“Ò~ê¶wÈy÷Eáæšéý‰:‚ãE!zPÕŒ–퀔BÄù%9*zÞÏ‹±™xàYÀ Ùƒá„óI¿ú£ìiÃÛ¨õˆÉ>·50{ÀÜðÛkk\0Ûzáuy¸•wyª¡¯pTV Í‘ t\`öãþœ¨ñç¶;¦’ÄîødØ/¸k‘!È Á‘ô!G̯Ö=vËH½3›RÓ ÜtDïI{×=4Uk+­¡fi:Ît7 ~¾Ó{h—ˆfã@Ä!Àäµ(ÑkÛ™MÚrëÚW^ÔAÜø¥ KîC[ ÅëÑ•[P€¥k¿ÓåñN4p?æ›QÍ7£„ug= ¶‡Óð•F਺Œ2ŠÉ€i×KŸÇ‹Xs‘fÊÙAïx€ðø³ešf/³‡ zP½ä‰Ì‰ÊR#s<‡¦þ¼KA4§aYƒBpéá+¶•†ê¦ÉŒXœÈ) Ãè½I²gòþ¿êGêH6ûíqYáàþíIý¢µ-=ˆVë§y‘‘Ubj+rPîgÖ,^"1ø]/Ô4VæÂv«ß“1¤‹ Ò}2ƒ*ƒNÄpa>±¡Â|8Ã"Ù5Ñ®¾ÛÑipè€/·r žHWphâpï…`»Æ:âµ¶ÎÖõ• ÷÷Tºx§&H ¯Ër½½ù ÅÖűô&/Uê­¼#(ÊÔ2‰\ ±v^8z"ÏÜ=v»”89c¦™)"/W&G^1-ɲOþ.ÝH¼<“t°èzb‚Á “ù(‰Ùöà ‰h'p¨Ê"Ô?Pf¹Bð“¦ÃmÔ·S€öœ(®Õ™t5áç ÷…F7ƖɰÏqû(#Y†ñáÄ•KSέ¥çןÔ.¸ÂEŸ'˜A|- ¨2x:ÌMDð‰ïÍxéþ"†U-v#ùäù ¾T+3ø?À7¢3Z¦æ„ôà €¾:Í‘ï4ÐÀ@wõ 4 Xü#XÓ4EPW1×>ØRü³êH€E6Ê:­€Sh1¡¬’¨\`m:¬€R°¥)]Š…OiÊÏàµcêX|\ø h}´ˆ/| ‚b ®ë…­§>–Éuf]”ßF±aáF¿šcY¶Ø¦.ʨN±ài + ð ;Û˜vÝ‚§d4g´ö¨Â·Áᯅ €»…Bj n!6ª4$ jÀëT*€“-§,r@Ç‚ŒxºHJbYD'Ñ.²‚% …(ÙÆÌ§Aêåx]@k`•9$£s.@ëW@)ÃcK®;G®§D⣲3…%»^ÎÙÂzâ#¸DatDaS]‰BTé4øuE% ÔlÑÉžVŸ‚úXØ1”Ô3@tʱ  }PÑ¢N™dáCÄVƒDËœb½4:• X*j•9 pUÎ[°K¯N©šƒÓ€g 9†íÂêX°¡”•ŠVnnJN3†€­V@‹Œ-$+ 5¶°®¹¬š#éciŸtÊ"4âi™J,E[±`Àqç/ç¾N|,:ÆPÚ)|âtÑúñ€õ+ ân–(T@Zá¸Ö³QÇ=3%*Q¥N¶XrÊ"ql‘”¸¾ˆN‚[d)á/ÂÆ­ § `Ö—ò¼HE¯KLÍ:•£Ìeh¸Íøw¿yê¢ëmEsÙúPq‰Bˆ–\/At;l¥­Ô5·•ÒKãQ@¢”²Nêæ°Je¯’-@­OA}”^9†í>º+ ·eú$)'CÅÍ ÆPü-sŠõbu*5À%Ãm•9 Ìej}ªlº¶¦S këÔ $Çœ«¸ø¹E$h‰¹`ǹµš1ìttkOœ*ÀϽpUb XuKùXú”%û °Ú±EAâº8¸Etþ"K!•,Ñ2…‹´ç‘”Äÿ¬€^dÏUpó‚¾Û´-ž.5ð_Ýš±_½†‘^o^z¯¶olHomUÑ[¸™½d<½õ¿hü…íÚxøm|Ù¸¼ööÆŸoWÆ·Þœ^»úeã/lׯ[ºË­ê¹qy­Ã~é½Ú®ÇêÚµÕfy]†o¾]Ýa£jè;Ž$å°ÞšÆÎC™’ÙÀ•N*;®à¯#WGNLúöqžZYãZFý|‚fG@˜o ‘¾'ý\þà@…ë©T{Mì!ð?Ϋ¦GŽÜ†Þ ø?ô±;Àlôý‘cì!×ÌÍXÛ½pàJŒñAþ}H>RRU×6X`ÝóJ¢(Šä{,èy¬x8¥Á†‰îår>þÆ] ÍŽ7Ë©âôú÷£oÛ1Šg»Øû’—ûiì™FŠèy=“žºÝˆ•ZžmÂ0!rÿ4/K JüZMT‚M0ûDi^Ýa€#y4Ž&RšHò Ç¢E“õ­È½ Ç«1@¿J³¶~?bÛÙ®Ž{·¢Ag@ܳчÍtËJG7âr·¹„ú3]{èØÒ”ÊîÇ]d7¸€‰È¤\pPnÄwÍŸÚ½wm'»‚“û®v% £I Îàhf¡¢mwëL ˆ³qI¨¢+x‘à H:H¢@Åîj¦¡Çt·˜ÙaÛé®$Õ¨€Ô°­`3Ó¸Ü4 !¹®5 1„qå£d†°= µV4œ÷#¶îªüÜb´øzµ&Fצ’íhgíÖÜ!£8)”QHùˆÝ0;ÊÛNwuÉDjº xÐ_í‹/гð Jô¢—)¯P3¯ª™=ÌÄ¡ˆÌD}í;®ìrÆMª’«³]Q¸­”2¸7€½êì({l;Ý%‰XJªÑ+efV¤&‚Nñt p! x¾$h¤=¸tÔv,2µðƒklª[T*ï¢êº°VÏ€&£>.VRØÛAê 9=ê[iÒ·ZÔ¬õÖâX.Ú’ñžB€œâLqÙå†z¼‹_*í$wɳ›ï}ÛŽQ<ÛÅÞ‹³q¶*^”Qlè1|¸øÛLîÕjbbAÃô«”à^Þ«Ja @R%eè¡É}Yž¬DmØÂQxhJ;è$Å’¾|Mê±Ôàz,mNI!Šo*É…tЀG™ÎCT´ ¾ PFËKE‚CCS‡™TP£Y³Œù„[)Y1S­yemÜ‹¶S3IâPÛ8(A¶©0C†D¦—Qy‘!ÕG®…R¬)âÅhyÉh¡™`ýåÇ@êKK™Ô}¡å­Ò+4 ÓŒ=¢ˆœ2«§¼H$*äPXifbÛ鮕Œ­ºŠ³°²UÁ&m” (ÁjÙ9/389:U•Öî¿bÛé.äñ¿ªzW‡<9.z ½h"-€¶« Ò«ZÙM`Ö|À(Ð¥é{8 ƒô]öÈwâ—ã÷¨Î7ãHªµž¨R³Úõ&0ƒB’hߪê˜ç–ÀWëòÎï]Q–‰°ô½ÆÛï»Ñ7IÇ@ƒ«ñÔîý¸k;Ý…®M€Õd^äFómÀK«7E9ƒôͲ AœwÊ‘9CŸM@S5.îíÌHãv‡¦êbî–ñîü ûq×v¶«è°aT1ÀΙEÌ +·1ì– 96ɧ¡‡íM ÑŒ†ËlËg‘dQo¨Cpf…lf0®2PÀ²¥±Pâk%Aß-0 ºot¬Û¦#À× Î¼×-Áë«ÑåØã) ús9Vï’†Øx¸ f¢`’ul Kìw¾mÇ(žíâ!©kì9½C°'¬à:¼ÇÃ÷AðC¡2…§fŒ­ž &…gˆ7¦o€P¸[è$CðäjJV1#SÀ@Ì#þ %@WÑA²JIíæ’ì—yt‡m§»¤Ù—ÔiŠÚf`!Í ï™šª88×ÐxW“å…z˜ÚíH-45c»,‡§d“bG“I6;0À¢yC€Ø‹7;l;ÙeCTü·9v0[“k’×i )Â3vKH¢?Î?ÀØÆ‡´bN;l;ÝU$¢Ñȉn”qÑ‚6WÅ1ëÀÕ‹iöÐ%ñI#jk h<1ëì ¤—%ì1jZÇ(š€#'<]ô¬„Ç%`rgB‰GgÝŸ«Ž«·/$"½¤wf¼JhS™Æ‚l Õ8® ‹1~ »ÊfZRµºÇ¶Ó]èþ³‘Qý8ôñ¥‘%)² %»Î`gg›ŒƒEÀhd~~ÿ®²hqôŸüð¹Ò™Ïd™rß¹zyþ„ŸôÏÿyÿîÃõòãõÓí‰bxýøý„ëëÇ ¯ÜíɧŸòõ—Û/Ï“Cri{º<ÿåý»'6‚Øýpýq{ªl€v‘úº~úø]~¥ëÛø|àoOtë‹ýý!¨}OUW|ºðçJ÷åÄñ¦|þñv£Le äWº~ÇŸ¯ò'&ʾŠ÷½W³tƒDùDÅ™Z£¿×’WKêß2·ÿË’]•â[ä½ä‡ïÒÇr•@Þk0þÄ.|úryù'cŸ »|æ__ÈràÇÛWùðòöU6É—›×ïü‹1ò]Öпo_pÐåE ë?/0ö_²þcdÆš~­b,‹œ¤|É7òäõG+#ZOÞq¼8ë¼Û…û¯”Øþò+ý|¿fæJÙMÍ´]ž(hmhtwXGåY‰¦ËªäC•èƒVùÒ;/Òïüa~?1°, ON °‘ÈŒÜðMz€wUÜ%¢;¸»¬ãÓ¨ÛNKîú2Ü¥sž³[¾ŸX˜»;43R‚‘+ÁKä<Íì2<¦Ö}ðxYÆ>œxì¡óŠ]Ìó‚ÏË÷“ýó»ù»ÛÏÒ7V“¬Ù¨ûÛ  =uR× ðQVh‡oBú¾˜ˆMº¶'dÒVįPUøÑ—>TyæJeÀô²P*ml!ÚñuhÇ&Ò—b6NiB Ä!cõr!ÿÆ]àã ÉV;ß¶cÏvñ–Š ûU/óÛ³îË{^Tã˜2”M!¹³M¢—½Ô÷|[€ª/NüÅËuŘ!ÞaÛÙ®âáo3½l@×´—YB$cf hZ,¡ŠP™» •½[ôØÚ1,§0NiÒÜg XÝaÛÙ.$ê.˜¡ëÅvyxc@Õ!K'~H°NïW5Ç îÇEÛ©Qß ¨åD‚Ì–à=°¢·dÀ#]³Ž•l&9rPšóÓÛNwI 3 ÅKOc ÏQ#“.¶¨A,b@y9»%s¿Äf‡m§»"ò×Yÿ$ eÙÆ¥08i÷Ùé5k—4CJÃÊ6—Àý³¥ƒûÛNw!¢žÇ$„®Û³æˆ0x@/ v©ìÑ ƒE4ƒ¾Úï²ä‚oÝìfi¸m§f$ãUÌ)кË@Å›µ 3À×&@9 T!b*M Ä®[n¹Ã¶Ó]]·¶1 $ê:ÞëóE«$—T©  ¡ êã[‰E©âñú*:Pýñçtñ—çûƒ¶…T×z³ÑŠL²´eûýíõöünÌ?ùB±!ÉýËãO cË[­JOÏ«4£À7òPUuƒmTvt’Xs…—¨­6:ôÖ œ¢|€vÁÙ:OÉUír°Ð/‹Or¡jYŒÛÖY'{Œ 6ñ„IC‘õõ!åy³ZôÅF@Êÿ讒ݸaz/ÐðÑ.àÔZlÉzÍ¡×öÁ ™f&À,-ú÷%ER¢lç” MqyÅG&+*ݹÐÖ"¶r,(eú"8C޳6LlTe¨4¨ZÅW fÍÐGlœYC‚i\DƒªUl¬#¥ŒA0V˜dÁË2¶‘Æ9K£O¬:ˈnTLòýBñ—+œfô wþLa¤ÏýÖwu¼7Vʼní¹hÊÑQä³4eûûývº> ¥Ó¬KO̘æåQ ¹GÖ¦RáVÆ}”¸‘BëÙÐ.sN¶á¥2âe Eà¥)@`ÈÇ{®0ÊíÒ‹h·lÃf/Ë\lbC(]%\^”,Å–îFô”Üà›[a˜Ù*€ !ÑtÓ ±´);,((…tå Ž0Ë}•²ó‹ZˆF†Ml ØK©8 (A ç8À¨© _y í–‹3»í\, !uXPÕBc´U‹V?…! ²UˆÌÖµ` £×1(PÒrø¤¤Fì+^m'éx †í.mŸ]õm£QažcÑ\@°T£šÒ¨ÀÍ×¼â:©Ÿ_¼Xð¸ÿ°ƒa¡–ýe„~ Rœ2p|nRï‹“ú¾>¯¾§B,ÏÛ9ù·ÆÉŸÓP`ÁH“—‹ÈG€"iOzÑ@2UeƒöÉJ`É ß9 n8ö‚ ¸ˆFNÚÆ*ÒôÀ£€/•dëÕ¯dƒ¥ Ä#\¬æ@œŒð’®á¤Å·ç˜QbAÁÑ@⬠sñJ†ZƒFx¶ÁÁÕ‚™¼§p46d4±­0c% áÙÆF¤4lÝ”÷LÎvR#¼ŠíC¤ñ;p±äA@‚ŒðbRðyRWx=Âé3‡±1Â×Çû0ª8}`ª!k¬L¤d×EAêúÈ7QŽÀ~B•ô‘IPÖˆLèŠ æ/Z`É Ýwxé ÊËÔÒ£¢Á„®ØXEʤ(r‹älcé™Z¶®¤Bˆz&[ ÜÀ=£Ò-=#Z6rÏJ$P8æ»*GÌ$Hs†JƒªUlpp•`&/ÆiMÌ6z#û›ˆU«ØXGJÛÀ-’³ ¥gêØ>FÚFî™l5rGd@"÷Œ2Yzã/WxÕ3ò™ÂX÷ÌÆñÞÇíå1ÚóTõ3ñ5¨”cÞ‹ æåz~{Þ?íÎg!À@‚Å—ÿnš"m¢¸ª®¿Þ^{gtƒ;ý0Á 7TD¶^?¥­·Çß~ÿýü顽·oÝx7¶¯·sÔݵûÆ~K’ægú|MÿœIëyÿÜÁB3µÍË;ë»;Ò9)œÛ“Â3=îO—.²·÷ R{"Ý]ËQ{èú -éÒÜýcO—ýo²­ŽŽrôÒõžŽÜ.ô?¼6÷\ßJ<œÇîÔÒ/rÊNØèíÚ=þúÐz–pml 64¼à ÕèÀPRy:xE]ûtØÿÙ.Í÷Æ9¤Jÿ˲sU endstream endobj 53 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F3 22 0 R /F4 54 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 56 0 obj << /D [55 0 R /XYZ null 497 null] >> endobj 57 0 obj << /D [55 0 R /XYZ null 484 null] >> endobj 58 0 obj << /D [55 0 R /XYZ null 471 null] >> endobj 59 0 obj << /D [55 0 R /XYZ null 459 null] >> endobj 60 0 obj << /D [55 0 R /XYZ null 446 null] >> endobj 61 0 obj << /D [55 0 R /XYZ null 434 null] >> endobj 62 0 obj << /D [55 0 R /XYZ null 415 null] >> endobj 63 0 obj << /D [55 0 R /XYZ null 379 null] >> endobj 64 0 obj << /D [55 0 R /XYZ null 350 null] >> endobj 65 0 obj << /D [55 0 R /XYZ null 307 null] >> endobj 66 0 obj << /D [55 0 R /XYZ null 270 null] >> endobj 67 0 obj << /D [55 0 R /XYZ null 251 null] >> endobj 68 0 obj << /D [55 0 R /XYZ null 155 null] >> endobj 69 0 obj << /D [55 0 R /XYZ null 137 null] >> endobj 70 0 obj << /D [55 0 R /XYZ null 121 null] >> endobj 71 0 obj << /D [55 0 R /XYZ null 104 null] >> endobj 72 0 obj << /D [55 0 R /XYZ null 563 null] >> endobj 73 0 obj << /D [55 0 R /XYZ null null null] >> endobj 51 0 obj << /P 37 0 R /R [63 63 549 729] /V 34 0 R /N 74 0 R >> endobj 75 0 obj << /Length 9368 /Filter /FlateDecode >> stream H‰ŒW[¯Û¸~ÿÀ§‚LmEuí>%iR¤Ø´EÖÅ>œ²,û¨•-G’“œýõ-Ù>Ín‹äÜ9óqæõêù³—גּjµ}þ¬ŠT…ðŸ>\è‚$S. ŠL­öÏŸ…jÔûŪ݀«UE¿=¦c³úJŠEÒ2²AìœSq$Iš¨Õ_6Ã(&¶;­Ô×®Ù¨C_ÝéãØÿd>¯þŽ"—6ˆÒÄÓD¹Æ')7Q6‡Q cßlj¤¼oë¯u;ü_¹D ú‡©‘ë¾ìû; ÌŸ¨ múãÜ׺«îtïû‚éÏÖ³MWCò4K§HZ¾ø º…;ýÎX¸-Ý™<°ºW„Àéïfé`·Üi¿5N× õ­1Ë–ãí*³âó²VƒY¦A¢G FÛ6LµízS¹Þ£L«ËÑó•Ê›ìÄd0%M] é„&/ÑÐT.ÿxB.}å%Ä(Ë"á '‹ÉI+NªîX÷ƆA êÁ‰\D }PãC‰a=ÒRÎêÁ¤ð£6åXª±šZE@WÉ)x›™¾ubPØèI'zãÊT·ÞdEâfÞ«¯ö"ƒ”Ú`†hYu`Tª{ r®ѶD«Ú,™ Î5ø‚g1\×–i™±m;csðçÛ’Zº,°.v”Ea榻)À™˜Ôv‡9Z÷[ˆ”.«ú/Þ=_ãC…*€¹P.É…àŠ„XYD/sT¯>ë@î‡7ïïÿuù¤±ø œy{!µ(•´PÞ‡š…Ôœò6ÓP -ÔÅ>WÑBî2 r*>ëØk¸·®úd°ž†>qÉE†j/¡J|¨²(·VX- …?ŠàV(å2MÙEw”bI©²çãÝiÕ×eB ‚4B¢ÊW_>ªŽÄnñª ­„¨1x«Ÿ¬4›\$égK´(Ñ}ÏméÃu¶º ´ÑEíEg¯½@½-+ô9“{°”?¥ê¶FÿàŽõ‘(ê ÑŠ,&bTØ r<)F%†hu[n×_NeÛ>.i%4ÍoõF1#¡[¬w¤ñÕŠL1¨£³­Ô@â]œÝÔ€óù ?7ðÒ`¶HJV“½®nšzsÞÙöxºçb }´¸Y•-ó«}½ïèº3¬n'¬NLðÔ‰¤°êU)‡OõL4ÊòH6 ¶-1qñ-¶9x£íÀ¶ßC¶y#P]-A´+ «–pCÙèsF¹t* óC½Â×(×MIúá &i¢ßCpéü„H„ñ*ô±ì1EÆåô06»“€·%’©+âYz§ü~Â[4^aÁå+t6¹ˆÎ×ãÑ@öhiŒ/îBYI=cÁ;ýaǦ-×xÜt¡d:5Òþƒ‘w ×"VµP7SomR¿YÊoSê“æo|ÀïPU,áõ©r¸ Eäqìæ×z´øÕ‡¤»ç¯#ÔÉ7W‰—”Bw7EÕeçx%Éô`ÃÝd«Ò?GÐÚ§J1š^ÈпÙƒAç”ÿ}ƒim1è(ì®Ü[C¡p§Uª.Òº„ÄÐMËaBú#\{lg‰r½aªÚŠºG`Kø”Ð9et-u©HÉ uü•Žêjd1Quþœ@O…Ì[ ›ÄÊD1>•)$Äñô½:¾Þ¥cˆd“y q=Ç(0KQ Ö|Ç=…áì.í•ìѱ½}ÙåRðñ±I~Ý»‡r/¯>Îrj+~¨ÿñïŸæçz¶üÓaý8Öð*‡Hº0¡Vòÿ°Ó]úÖ³¢(|R½kv\:'þé8RCX:ƒ°_5m+dÐ(0´”ü¶ŽõÀO6µR)u¾{ ºãJ,´¬:(ÌR½m)_·‰F*Y‡:oo.ŒÆÎ›Ì–ìèølºxÂÚ¿ iÄNb{6Ì.=Lp’ëáìþÙg>Žßº £à2á+®7Ûƒ÷l¶•øÛ±Ö,*ËчÎÇÖPÿµñÊÄM%bâP‰ßìÌZ,`˜Rï÷q¡/9穦¶\MuÿT‰–Êq‘û5+óŠËbä:l˜ó€]—,0/¹:¥T¹p˜ù@°ÂûÂY¶Ø1sID&IáÇ׿¯&ް èÛÍ*ÿµÜbÃÐóþå?gˆâ1dë¿à""š‚ª󱣄¿}ýe u]“@»~©bºøúÈ·°z!QÏ}ÔãÜGß׈qÓ2ç·Ê‚Œª†„ßÐŽÚÔL‡H—q|b} 3™Ùb xLЖ{‰4r0/øÉ⼫ś€aŽZ¾œ½©×ü„—5Úžö¬„ŸoìÁlÄ#^Žô"¢à`×XÕQüj o±r1DR+ábGpÈÈEÆælß¾d@αüЉö‘”þ(TW.Î-èy0¤ä.@͆~A%•©csð%\n˜„„'º"l=_:ËœT“}4³¤ÔƒeL2ªh]0ø+ôê ©£Ö3+ÓÒóc1‰.$Aªm÷±ï½Q#‘àË̶¯ù¼êöGÚ8ÀκʹǬ˜ýTÞqd'bqr”Y+¥$)øŽ±‰ÆÑ)ƒËȇ硎æ;ß“¦4Ô¾A÷†sHJ¨µ¤¶ý@«Zmâ¤ã+ÀŸŽ÷ˆs Ddð±8!N$ñ*FpžiW¼† [—ê`A\ ‡çêëE›è,İ’Ø5“ 3…ø o‰1 Œ ƒ|¯çöå… Ùvî¨@l1È8«î˜¶öG¨èp¡hOFÌ*Ro§f±¢C 5D"“k‘6J ‚›nV €‚$+¦ÃIGûö{ !C¬ÄÏ»iðÍc˜G¦6žª1´…obaBÁ6eAàhÙÃ;j¾A·¥;ñö <Ñ›†Ž óxw@7 F;ìTå¨4XG-¨70;Xjoûò‘õ),ŸœzE$ɇsc9ð‡@5ÆŒéÝ ä>‹>2]Oöu•å¿5Ù0×½àRÂöf ‹O6ƒúGƒT¡ U¡¬‹ aSÅÁmìý,u§7 5è<­[üîløù. ?ÿü´­^¾PmWAúa‡VÒî ^¼ôÓC¦) Ò%Þ±®Ԧ#ѯQìï:R€¢ûzßµ³†Iv)Û¶œ*ÏÎ6—!–Tbó¿·UDY1Ë(™¯à3‹}B­LZP)cŸW³”9‹PjlÍŸ­±Ô}Ò‚F»Xÿ—y¤uc بQ«…t%ÓåB9ÞùND)/Ü7¨a¤QŸV/¥§JZ¯’'Û„ OiæÔ–$. éé_ÜX+/I]çQ3Gœžƒ2OžW”6³ H1½æóiqŽ_ù‚äþ²¸U®;—ÏÂ/¹ª_«+MÚ"Ëæã«;+òzŽ3ê¬ Ì­–W­*êe¦õBãèk¡âOfAB%´,'Ú÷ïP6»=Üß^û0Q‚)˜%×5DÓç0Í`7Ix™É¶ˆ—Íå¡ ÝtØl“¿ÀŽGÿã½Zzä8nðÝ€ÿCŸÉ®»ºªúaÀ‡ÄI€8ˆ_tÙhg%3»öÌ*vþ}ø,’Ý=+]’Ëî4‹üø(’EâßGØ2¾&‰G‰™Ÿ¬”éEƯgjùÌ èôlp( É‹mNþ=½pîû <.ÖÄùÞ³‚[hù *ºÕÚCD'*ù²›ØXÛóïð9½¬P±àßOÌÌ~£»f)~]„çôºÒ ‚G÷ì2 } ŸL>óŽF¿¥Gþå'H îcËr;×n\à9›º/4dïm™¡Õ|ý¦ÙkSêÆiÆs:a&=y;ß‘·ó9íÉwïáO.È4¥å6å®BÛž»þ'l}`¬¥«e£kž1QƱà90“žçâÎwäõ¼¢;ò DÎèˆO ä_ŒÅÂç0Ñy8®Ô’å¸äÛqü 0ì2 „刄L *ðzt&Ì"‚ÕX¥*2Ìž‹‚f¸•‰PhBè«'FÊ„ቄ¹©­úº²4 Fóe£\·íÄ„¬Ï=Âa\”€;ÂÊÂH¬P”8óŠK zÚEޤ!0H&HþŒõ… ù…j–Šør㈺L¶ªj€@ ƒ&ÇD*côI ‚¡„¦…AÖ¸¤™‚\Ø23nÁBÎDLZ›B¸’*óïä£#0s"Â\W„Å畟v•¯¤¢f†™M±ýðcˆZ´7å Ò%½P½áÂ{î-;)”4ÂÊo)Ôµ¥ÐPC%N\VFÙI+^ꮼa“oFÂå‡ ÆÇÕ*¼ãK¨f#X²S±1á¯ç|9Û($½f¦ú×’kë?mô´Š’ÉuGh2Iè5‘õ[óm’Ò4¶ï–Å;tWZQú­%O&]ŒÖú$)®õ5qŒ[ À÷\ã÷Æ &Ì=³¨£ð]–ÝoîkÖe±-xÚéðÝšþŸ~Ú¬6š_ˈ-JÂ~hsv™uü+N¸8¯Á­À n)¸,ž°]à÷wtÖý«3®˜# bÄÑïþCOø=¼ú‹Í#î;ƒa<#ѧó#Àç_M n°­XgÚ4:þ<î÷ô 6Ž`…%V—%ü8ü—;êžÏw¬ùw¤úê +øqË!¬a8æßÙÏßÙ¦LD8¼ƒýˆ-êž Äò ÿü³¬¬ð(ÂŽ+ëL£¶îIÆù»óB¯*ófFKeÕ= ¯xQu˜õ¾hjOÉ&ø¹m–ðÊfæ¿a'ßø‹Ëc[!Z†Ó81yUìþø#ø0¼ú[÷‰é8ÌÃø}éϯi­û…þ‰,,{øõ|8_`ó¹õÏûÜ’ØŒ‹,>b.Œ(Ek&r1f÷ öu .ÖÜQ‘~‚ÄH‘0Å~ûºãÕèNl`Ð{f–3q¬{“Y z¾tßÃr‹r}`øí< ÚåVèöËóù#$<칪o¡MÉõd 4èqœ0[–r….x´:û¥Ê]äñ¢¶ý"ŸO¯qúˆ©„;ŸñF*-37#mAÎ úö-\¼ ã“ðŸ‰üN„xez¦•i¥à…üôøö5kªg–ûnx#€EÞ[ÿúI ¬Pírü½ Ù…[OÎw¿Q9=5gÈ’{:»¸îWèȾ&e)Øä Ì$4Rl¾?Ó=~ýUÍôÊÕ>ã[A+ 0¸ý‚€¦.,–£akÑçµÂ2]Q’½¿Sßã¨kUA%ÐÌõ™žxe€¦>V>a&=y;ß‘×ó‰w·üD/ÏÔ']ž&zûxf-”©;$Ìma`S ÈêšY $ȼ „I³ƒÙ³&èÚHEÍNIÌ) [p k§pï N!!8…„àÔÙ²v*Ztm¤¢æc»É Ä›B¼©4noŠÇXwS<»›ÊeuS@ØÜÔž5ñ¦ÖRQóQkÉæFÏ ,5ò3VÑ·?ü3uï.Ý·?¦îòî1Hd¬«÷•<ºª2X ¡ÂÞ|ny ×›{#<ŸµVQ¡ùØ´17ËštÓšÍ xؘK"™ì]› ¹F)K©%ë×˰*ñ"¬,T ,_`«²ÿ;¨œT®R8Å]ÝrŠ»ºå´³ZZéϢ߀h¿æS,÷`´U†+wÎq+Ü+Ú ÙöK|]b­,ü¢ïÆåˆ)ËÿÌxú¡{Ü•{Ñ<áÛ…vŒ3í‘Ë(û“xé9šÈ¸¬ãáÔPoða½#ü‰‚ ø¼ï!>l±#¬3Ãâ WùêB€B%Hc&º#°ǵZ‡á8¨‰hÄ‚ZʦƒaÑt³tœb$”¢IÞ`iÕ…®+ gYèrYÃYz¼æèy ÅÚ¹iVÀ©©ÀâÕ; +äE *=a"uVP ¥¨5Y9)ÆHAILZò‚m"ZšBzõLíCˆF ÅXaVRÃl[YÏw6.â@o©Õدx@ßІO—œUfK`²Ç åûv¾#oç[y­Öc/°- ý Xd®Ö¤ÅY2_\Rו£×| ãa…;¦."®r4Ü€!¸AyŠ; ä¬:N¡4HJ©t[sPj‰/Öìl"j§×bîl¹¹Ëš`îR+Ɇ8ç®a¯y´1ow EQ¨øg~ú‡e`+1€K¥c\Ð$ç€Ð¶™™gl>Ý Û±&\®T©CB§Oô 7€ß…¬„opHÎù»$ú憯üdùq…wô*ØS¯cïUð¹A²?Ç•|ƒ4«x»Z¸( ͧ¥§Ö|‚ïœð7Ó|R~õ ¾Ç%žþQUŽ‹óQ ›ð=$烞«AþxÍ‹µÕkÈ5„{\x ³Ç¿ðz /E’ÖZqºñzÔ–=.J0 -{¦q7s¸0ËjWb\{ÌC á¦ì‡ uÅaZT³··×ÙÊÆûT3ÅÇ\®U¤¹ „Õh"Íå€q ¸æ²H™Ë‚ëæ—Aç4Åe7GyPUäÍÛó°peÏC{#ä•´ Á;LAÚmGj·á1Ží‰[®Îp W§ˆÁÍSÀ Çf¯)WÆÍˈ SuÚÍ~›§ê¬§ló˜\S]H@ÈY ¦EæAÏ4‹õ†«ƒu] w¦È8r˜sƒ¢`ÍNþGUZ uP›UP³›€0„]Øç ú<¨µƒ¾ÍŸ§`Áˆ”Áˆ¢M‰Ù*d¥KÌ ÛƒŽ ±ÄÜäëAõO:üêΕÖód¢s[BÒtl}R0¦‡i±âžkèNÍ<ƒ™ò w;2“²fîÈ»*Tya˜Ü(lžç rSÔD$•ƒÕ\yL¢9–ìa¬ˆÄ6«)mú‚‹»ö:E{0Lã88{—Ò®z'R›X†8 h¿¯ÅŠB¤¬ªIQiU=ªGVÕbŠUµa¼˜èÑž¢€ëš_èö”š¾ÛÓVé»=qX·÷jžl‰} ø¤#²íj”‰¶XÈ.Ú·pŠHß>éš¡^‹½_ò\%içb^³×¤ W4».ôÿzÄš¹¬"SnŒs8{#„)ÇáµÄHY›yZúM*F -RmÑ÷»0¼os‰œÄ‡ç:¦<ú oç;òv®»L”ϼ^Û£R™èüÊ"•2/q×6)w¾·J¥<‘÷mbGBŸÜâ‘ò·£Æ¡«Rv»F=EmÛP˜¶n¨¢¶ 4Ž–™Cq½y›µ*•Aw?ñ¯P…9ÿJ¬pþêÎiÐcS”üƨ¸æ²Ó8lðóÇklìÝàîÀ@±NVS>üõz’ô°¸$[IÛñnŽÍcÜ ‘–[$„= a½m"íæ1.¸QË1h¶K»ÑlÁSŽv)ÃpÍÞmÒ-\sæðÂã•9¼ð\d/<‘™;*Ò^x¶ óºÊ„É\sxI±†Gs8`¯{´q`ƒ»†é±áfœÛÑ3 z\o ·èåÔsªŽHçëcLU>.0HŒ›sÿxp%÷•}ÿË|µãHÛÐÜ€ï0±ñ–~%)Þ Øpà,0Q÷ÆÞ°(þ%Õx’5úùøIQð +©R¦˜µ¦@ÄÈ{yº.¬×¾ öŽùéD À<ÏY!`ž8À1K¼Á@ó@³sÐò“ÂxšˆCbi(á²§à}{0MÞ‰W €ø¯,›÷_¨“i\0wa*ÑÌtÐúÔjhÔøûh|Ñò–‘ªj^£OÇîª޽ÜxIÍHU©8X ç¿TÌn¾P%`2¯Àq0 V¢zfy§e 0òªÀäåý$•yäP}­®|¡VF¡«3ï ¦{‰«A^ /ZÞ2cŽ\yÇüG˜«Ï] ê°ú=uµ¯­ùer˜ÏZ"¶mÀ5N)Õ­Øå ÜXôÚÔ7nûÏÍjƒRb d¬îa°2“Ù¤I¨âi©nt´yÁïÁ¹¾²¾Í”¼´n‰O€VŽÞÿàÜéûDÚnž×8 =åžkY®9fÞ¸– µ| ixbÜYpïÅDÅÍ]ÀL·¨Ìõû€ØprÅò‚+ùÂzã°èé \áèÊÖ(‡>²¤ï©#ð€\ò:Šc¹œù:ï&b}Ü9懽Ñ|>í1Û-ëÀÕÍsñ+½š%ITxò¤/gH¶&Ö’­‰ É2#¼5yæµîmkb,v‡z#@)Н\8Š%>–pJà§\–ÅìK…4¾¸·ro/‘øJØÞ^‡6¼Mß½MÒU¿{›€ö7oóùXc| HÜSÄ“¾Ä{Šàž"¸Z ŽÏs¼žØüÝx7š1õt0ìE7®‹.åÀóàTtæó©èR®Ü´Â.wcšTKÈ ¸3p¤/gHÏ€hô ÈæŠ%ä óZ÷ö+q®#ß‚mª4Æ@·M%_Á¾ÕøéK …jã#^ow·ñ±„Äç8^Olþn¼;Í|î¸h¿y˜ä†Eÿp¡Ãç[Ö†±ßmûžL?Bɵò‚k`®‡6/±Ôꀄ·*ï½¹ÒÄæÍ8×€3± iàewVÜ s ä5×ÌÒ“ŽWeãi"‰—BŸ <ïÛÌÀ5Ç< pwzuÀÎ -|ÝΜÚ7‰g}YI"H „Röx¨ŸPŸØ××€æß ex=ÓÆ+.Õî• ru³$ÄŠ,íž,' P–ë’òòú ­˜Õyså µr1[¼¼_0ÝãÇ€d÷Æß'ã«–·LÇ2ר¹ÁJû¶C6 ùþ°õG¾=l}‰¼¼-"¨šoJ©ôânלwÃ^”ö½é2‘¿ .2 ÖÀÌtCI·+Ùõ³Uh,¬ÞÓ¹é`ÝÏÍ‘¾O¤ƒ &3k =’/Ê=ײ\s´njÙRË;M¸ðĸ³àÞËŠ›»€”L«J´ý@²/Ý£+ŽwºQˆÃÎ!a%€+]ÙåÐG–ô}$µØ¶ ®/©qkçæV¸<ª4¤Çƒ–pU×ï§ 0‡ÆÛ .\ÅlH9,Oº]øD…W&Oúr†dCb-ÙØ,."Á’ç`^ëÞ¶æQ1úy#pu_\ž ôjãc‰o1,îiö¥B0ñjÀqy ˆ„ì8^OXx4w6o©CÝÔ9¤Ën¨§úMÙé÷cÙñÕ Að–c€«!²K:«È±ðõb%ÜãÅXÖc!=²¬éc‰²\l>ŸÖß½k6{ûÛÕá²;F–øjæR¿d/íkÙ‘–ÆG¼ß\5ˆ„Äç8^Ïlþn¼+M£PgÅö^…-,ÔǪ+cV¶`ªnÑÖϧ¢+¡ûÎÀÍ‚2 µ>^ϸÞ2xÂÁ§€Þì˹"§Ä4rJlhHtçŠdÓHÜÎ9Go…-Ûˆ¶:-ûVs4€`³À~`$.0«HJË"‘–Æd!M ñjJ†e×v"!;Ž×SD[Þ•&À]o\ËûE›¼7y0Ow+0¬^*QgN,Ðy“üÛØ˜Žk°Ïò]6póYOà¦0Þ `¼ÁõÆñ‡† `³lñ’.ªÝ¯•÷ísI¦ œ°¤.ÓbµyoÒTçÆ8³øyJ#æ¿Û)1:ÔÉH ú>[cW 7ŠÙ#›¹P£û› xàÒÂ#m£’óÇé{7×ü[m³ÎR±Ü‡]”ÝÄÏè¦û>&|³T˜壎S-ŸÇiŠØµˆ jEWÍ~µ™·{ÎùeÈgP/òyW—ïsFíê×=gWïP9C´û-Ÿ[œÚeîÊóËÐîQÕËÜVðû®®ŸïpRosI®×·ÏHÆpö¤†KþðZ«áÚ>½#¯ù®„Ò‹äšï[èå9€!=}0)ë¯'o[›–·ìh èšLP”Ôb±A U]ü)™ àèù%9Ý£Zxôf±µiyË/9É^mn§'¥•D'5€üIA5Þö¤LõãI¥!7zR ÑÖ“Ú½YlmZÞ2ÒüGڃ¸p¦Fþ÷ÇïýË/¿þ+|üöÇÇ/ÿüö»ÓˆÐP7Lž$%ÿOKk»îóg¼¢ò¯u÷3IÇýO‡§Î ÈÕ=NIÆ7Í‘…8nO•4]^=¦ZçñÍxz eoi±nˆ¶„Ÿð²Êä*gb,-Ó/Xè¦{©Ðµ{©ø¤£û‰ì+QMR‡¸nÛõÞkmíz*uíßóÊœžÍïÑ/±mÖ¼ÇßFß+‹Åy#Â’m=þ\q^ŠG[¯PacuŽË­vÏ.àBÁ ÏÍ|?èë÷ZúÐRx‰—:iÖ]çKix†s„Ù+Y¥^+à6Ï¿·K,ÀøZg ðTh$`.3 ŽŒnZhSe˜d b|2 À8^«YÃa$„TUˆÔs @=½ ï€GOÇÁõd³E€ËggÀG;<•íS,ãå…ê³ð]xŠ\]hÖ¢?ŸŒ×#š'ÂlÍ Ði£“€¨UEχ3w³YVqÜwe Pá(©·’ôµ÷ôrUqÛ³_.?”Í1ѬZ"¡¾-Þ㙵†+°$p¨F„Ùš6#O•gȵ¥«µ@ÍTÐ;Âïòý ¯ßÍTP}S ‡@„Ujf$bG“˜Çé %ARd98ÓaὯ…wž—%„×q¯3Ú2KöW„ñòv ¹;QHðñZ ¦È3’xcŸp\d †$’ ¦Ø§èÙ߃{ïF“ ·1ÍÝP×ò‘åY¦9Š©¿×Êáy±ˆ o œ,âÕ9Ê2¾óZo.êx™JõÇy(ÐØqfûK@Xf~àA9Ž—ãÕ€HK"^s1„ÞG–ƒy­7§€Ò\UnÜßfZÉæ•æBvžEöÄq¼dLtÇËãYx/KDs‰ô•סÜËtÁ{£Þls3 €ß>4n>•ÈŸrp8tO /ïÂËsQxU‚x‡†£Æ÷pþ¬þ«fs8nÞæ.ÁeÔl,¡Ÿåо¼ä~;ãœÑRšÜ—ËP;Äwƒ¹Ûò_h—ùn‰/3Ø%ì,Wè…o\]Œ®åvÚe-¸© Æ7-ö ãn‘y¸^èY¼I³ÈP‚€É›¹ÈT%væàV kE-‡jË›´´¼É–·J(o¨KyŸ":`xÏ4µx}§ Š\>’‡¶†íyE‚Ê•„´½&ðRý*/KdÓ^Ê¡¼*txñrm‡ã, ;çªj‡ã”ˆËËÁ G^NƒÔ7m„Ѭˆ3ÿaÝ˃ˆåÐaN³›¶ 6Íû´ô 2Sl[ÿIC”Œ·²„Ýî%l”0aÀ‡-&ìa‹ÇÏaÑòa@ÂþÿY­Á‰Q cŒ—Qáxü?RHX*ð#j&:ɛ™Ó‚K¼ËÈüÏŒÃöd`~çá%BÃ0˜DÎKøoße¯Û0 á=@Þ!£=¤Ð_y-Ú¥³· SÛ¡C—6ï%“"í4Èè$ÑÔ'Eû‰.ÔÎGGz\ÁK“¼7"pj4eɵÑñ¡´Œ„¬yË8ïÐ6Sg½bdK`K™æ„“!´™¥5 ÷–H·¯4T Z¼YÚ* 0së7o?ýè €”ïu—lÍ3i̶1GÉÖ(SŒ9Ð6æ@Û˜ƒêmÄ3æ(gkióüdÍ!ù-'ýÆ%oÌÑRúךHm'kM öOÊ!zz¯õ "0—k½ÔÁ…-YÁK“¼7"pj$LQÑ É·Œ„¤yË8ïÐ6ÓXíÚW,‚‚l l)Ów2„A¢"³°âôÞ*s”|ß*æh½ž.ëŽçc¿é0µzËÕÓ3Ó-箹ù}¿» ¿·Ÿ¯ÑãaÃÇxÄÕ;|ލG‡‹k­ëxßö;<'ç3>HŸýÏ/¦z3õ©ü¢n­H endstream endobj 76 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F3 22 0 R /F4 54 0 R >> /Pattern << /P1 77 0 R /P2 78 0 R /P3 79 0 R >> /ExtGState << /GS1 23 0 R >> /ColorSpace << /CS1 80 0 R >> >> endobj 80 0 obj [/Pattern /DeviceRGB ] endobj 82 0 obj << /D [81 0 R /XYZ null 730 null] >> endobj 83 0 obj << /D [81 0 R /XYZ null 714 null] >> endobj 84 0 obj << /D [81 0 R /XYZ null 698 null] >> endobj 85 0 obj << /D [81 0 R /XYZ null 683 null] >> endobj 86 0 obj << /D [81 0 R /XYZ null 667 null] >> endobj 87 0 obj << /D [81 0 R /XYZ null 651 null] >> endobj 88 0 obj << /D [81 0 R /XYZ null 636 null] >> endobj 89 0 obj << /D [81 0 R /XYZ null 620 null] >> endobj 90 0 obj << /D [81 0 R /XYZ null 594 null] >> endobj 91 0 obj << /D [81 0 R /XYZ null 575 null] >> endobj 92 0 obj << /D [81 0 R /XYZ null 550 null] >> endobj 93 0 obj << /D [81 0 R /XYZ null 470 null] >> endobj 94 0 obj << /D [81 0 R /XYZ null 445 null] >> endobj 95 0 obj << /D [81 0 R /XYZ null 312 null] >> endobj 96 0 obj << /D [81 0 R /XYZ null 179 null] >> endobj 97 0 obj << /D [81 0 R /XYZ null 136 null] >> endobj 98 0 obj << /D [81 0 R /XYZ null 111 null] >> endobj 99 0 obj << /D [81 0 R /XYZ null null null] >> endobj 74 0 obj << /P 55 0 R /R [63 63 549 729] /V 51 0 R /N 100 0 R >> endobj 101 0 obj << /Length 4597 /Filter /FlateDecode >> stream H‰”W[oÜ6~ÿÀ§•f‘Ôu‹}p¤ð¢Íí¼Ù…!k豚™ÑTÒÔ™þú=7jd ¸¹Øâáá¹óœ—oß|øl”QËû·oª¸ÊUéÃ%.Π岸*ÔrûöM¢ÖÀýïF­\-úùøö΢åï()I kâÔ9§ò2NL–©å'à“ĦtL+ùCç*VªŒîJÆÅ.¨Dîk=ôus»{õõ¯‹ëä7øÇTõºÑF½S6O{}¯dÿ|xý¶üïÜO›Å FvÍsäϵV}¹D<.¶Ñ¢ˆ ]G™þ½‹ð³WíŽ>Ô¥ ²ƒçyœ§A4»úî9¹pEl0$ e^Ë”¦AådóÙ‡üúævûvÅk[÷¯Áûwò9´ùîþF¯ºÃÝÆ3 Ãò!0ìÛ;ß«î^ÖwÇÑêž]¯ÆD¡BN£ ™ŸŠùI0]Í­V«a yúÈTÎåÉ‘.îïT fÍEc„Pô‹BÙ–Ûš©â¼C·Éa&On?“Œ>“Üt‡ÝȲr¡ŠÀ™(ÞøžêITÎ7'¤4ɪ©¤*+AÚ¢…±q®·wòáûh‘ÇVCäã.2ilô1JâRXx•öÀÁæ &wr:œiºPÆ(‹SÝò±µÿ0¨U-²ÆðqV¿ ßå/pjbg_“¨¡äHÜ¢dºòÓÕŸø_ÑüP÷4 Eh!u! ð!„.²^æ )—Ñ"ü‰%Üí…ÕÇN¤F¤AÊŸÉ .41´øc}˜N ðQ„[¼*I'çá.ã,h¾pHg›¯¹ÜŒÿÓoäÞBôÍë®Ö¾ïì"Ä‹ÍØÄ•qØ‹;q“ÀjÈ×úêÓ“³/¥òÙûm7z’,íg}|ð½Wuß×Gaþ¼Ø>œÔã,È Êj®:„åÃg‚g‰~GVq戙j&qçýâ—Ÿ õ%h¾âþ¿½° R ¾u=¶ÝNl1I\‰ ú-Y§'¼ê–”Â2-Ÿê1‚ÌBÒÔ¢f ©Iu : än1R±ï¡z+¸§|flic²åï¦t•MÎb´‚M ·}¡$b.D,NÊ4=¿W×zí±m`Óy¦ÎÄ6I«Ù´œ¢jŒ ½Mu^‘=_*¼†ØÆ0–LÙѶïN„`:"$¨$ «ŸÆf±k!ß3¯aÕ[\T â–^É.T_ge[ÌÁÊ$¶IKÝx(Ráô¬s EbþpÙì$l²‘"ù0ÛàÜ /1|Aa : °t0¹©7@IJf%çfdæ“0m‚ýõF(Ϙ;ßC¸œe 'Užcw(“ÂR\N5mM%9FÔç L‚„ R/íŽÖ5;ã§£5¹a©‹â¡X]PGœµ@7!¦Ì%é¬À¸$õú0W﬋ÂÎ8™ì4Ø%B-V¨EˆiO÷™P}xë@Þußóç"5#lÔXž‘9÷¾ï¶PCš ѦÃÓÚÓ¶ë, ”d…<9$u ™¬¸fñtEV-ÜÈGwè±^K*5vXÎ,f •%ÅAqBÕ’ËsxžËtj‚¥ !ê=â (Œ!ƒ_,Z±GÝTm VÚˆ-’‚+Þ÷Ÿjé'ÎD¼£sZÇŒ»›(– , Oâ×.l.îøcƒãµ g2å«È^3ŠÇAiý 12ú ož±ú‘h-ŸfíÂVd™?«8Àÿ¹«ÊY!×Ü—»³’3yYÌ8í„ìÊ4t{\ 6ô|Ûü}doµß …ìs;Ú¶æûI¥ÿŒ,µ($žê¤m1÷øáÕ݆I 1~…j¢%¼BFß‹¶º>”®6ÎOHgõpº3‰ QSjÁˆ»DJºVЩy}« W –]Y‡ê×Áu‘?¿á !·Ålê¸*Œ¨DÄ•©þã|î¸@È,òÙ4Í“)ò£ŒºO)&`ß+ž¬»±¥{VNŽíh8@¥`õ8šÉÃŽvîÙ]ºó;Ä Œ„ÕŒæ+‘Dﯹϩž¹< ñ?~® ž"cO‘ya¤™‹³bšæéé1aËcMÀE£Ç)ºŒ«& .¬Ã}”²¨™µ}%”Hm ÝtRn‚î, ÃþjA¤kÎ4¡uËs^Ú<ºÙâ·¡"qÿ¸Vt—ܦñŽÐrRðä=& ë¦9ð.ÎãJãBoj㢀åÇ$‹Íii,Y‘¨ /*¦â„7X¡äK¬–b¬Øó¢}ª‚ê¡Ý¶›ºW£ì+±ƒlß+Ù°ú[„ƒ[öd…«y6~Ót‹¨Ÿ‚θ ×Ãè)òXk8iSBC”t€¥à’Máz”—¯ZžÐ¥¦‰bÅ’J¾FóyF¨J 9"&!LÛ¿E3R&IªÚ”2G¬©U=Ê>ßÂt«6rþÀgö̶"bÍ:GO¸Ç ÊKåYŒ[ÁYÙ£Æ q ÒêX]áq‡ÚÏÀfÊ.m#ælhÚ×!5…’Ć|ZEGؼ~Ä‘Ð<*°¼V§¬óŸi”ÕÏröé=ÊD‰Øµ.¡®*p;ú‹ö]2ñö rð$ÔןAÆ™¿Ó"¤ËMö¾ë¾0r~½¤ô|È1üXšô ÉøAE±¦¬„’½ß«lÖ€z±`èÀ¾•pîø€·n»g¢ðVéo€rVꌟ$?9CJ€^À¾Äyäý= GLgîÜI6–_ŠÛú{½å_yµÇ ¡~[³Aƒ%A ‰æ@q'D ænad]B†¨\±ßÊNë%Í–›ò8Ÿ_2‘öÜq›VöpÚeú<>ðé¶áñ†¶4w‹^$ ê±ÝlÏÔÉ¥>Ž<0•ŒVé‹7DÊAÆÕæâ fÑsôºƒèÓÓ'A-ï/;ävn×,<‘VJ/‰…e«™¸'¾ÿs^-;nAð ÿ0G ˆ Ÿ¢oÆÚŽ xÀØœ -EEŒ%Q (¯í¯OWWIIk$ð‰ÃytÏô³J—€S1µ±-@X¼µ\,K… Ç y9X†Kmbºí"'‡’~Ф[ Â}äÂNïV±×À^/?‚‚“û»wvƒZͰ¼åÂ}’æ£äL£À§ Á¥±51ól?Eùnì·FIß°Ì´Åä©ÕZ£+õ—«‚ ¾ËÒd¤3öƒ’жöC‰Z]‚¯E¿¨\÷„Œ·ÀðhuH2d¡k6®9ê¸A£¬OÛbŠÕM£Kf±°O¿L.Òh R›¼yÔ™ßWÝO8N2ÂŒÖÉ#'ãè²Xêé°øB=zã,yPÞ '5³êJ„ÄV…Ì(dE3yýR?ä}å)?>GBH®_ä™™…Q‹þm$Íýr±´ãjÊ8±H:=t»Š,®TGפ€ ª£¾CD"ó"bÅ©¸\5©+·3Az@<뙄«P00Ø?ÖϺÖâlu±r\9š\EÙ¨¿µ„¾”ý5Q(P (‚ýïªå௠"±›\dQ˜AjxÇk©ôJWö’è'%«»t7}øç"&æÑ")žÕ˜ž3iotä˸ïÊ4·Z´:fI6ÏÒhy“#‘÷æ’/ÓxA·zÐÅ3‘æSÍ„ñ¬Ü„„âIøTšÔaµsîy3o¸çtf¬8<§ºKd'µÔZ7î29©ÃÑ º­‘ÚÍ–¬[1ŒÓbëA§\¹²Á£~* ô³OÖ±×\ï“Z vÂÌÙ$†ÙK1‘ NŠ¡¹&«£Óp)B p‹üéE9esè4z FOÌr ¡ÐßµÕ¾/Wz9‡º°´ŸýÌ'Ÿ¦é3Ü'¸Ojom¶à‚\%eá–o¹ê, kNlK‡aP <È+J™—KE¿ÀzÕšøs×ðD)%»6‰ìW9ú•E%}v)–õŒe=Ö²® Õ ºÝ©Ä,¨µsádýµë“;ÃVâ ÛfçºpÑ ÿûU<å& ç>J~: f=¬×”#Èìx+ÌÖÿÊdŠþ_ô/’!"²!úõÁjïDâ_â>“Á‘Ó§ÕÓQà‹³Xª*â Œq ÙÕG\ÖãSÍÔr6Z†ZºK \¯#ŒµúçpúèLKôRé’ä†ö Õ?e‚±ö§¬ý2§”NNd”°YŽP£õûŽÂ)€Ž7pʯ “öŒsģܩê,_?üúË¿&z  endstream endobj 102 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F2 21 0 R /F3 22 0 R /F4 54 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 104 0 obj << /D [103 0 R /XYZ null 729 null] >> endobj 105 0 obj << /D [103 0 R /XYZ null 711 null] >> endobj 106 0 obj << /D [103 0 R /XYZ null 609 null] >> endobj 107 0 obj << /D [103 0 R /XYZ null 591 null] >> endobj 108 0 obj << /D [103 0 R /XYZ null 399 null] >> endobj 109 0 obj << /D [103 0 R /XYZ null 377 null] >> endobj 110 0 obj << /D [103 0 R /XYZ null 364 null] >> endobj 111 0 obj << /D [103 0 R /XYZ null 352 null] >> endobj 112 0 obj << /D [103 0 R /XYZ null 340 null] >> endobj 113 0 obj << /D [103 0 R /XYZ null 328 null] >> endobj 114 0 obj << /D [103 0 R /XYZ null 316 null] >> endobj 115 0 obj << /D [103 0 R /XYZ null 298 null] >> endobj 116 0 obj << /D [103 0 R /XYZ null 280 null] >> endobj 117 0 obj << /D [103 0 R /XYZ null 268 null] >> endobj 118 0 obj << /D [103 0 R /XYZ null 244 null] >> endobj 119 0 obj << /D [103 0 R /XYZ null 232 null] >> endobj 120 0 obj << /D [103 0 R /XYZ null 221 null] >> endobj 121 0 obj << /D [103 0 R /XYZ null 210 null] >> endobj 122 0 obj << /D [103 0 R /XYZ null 199 null] >> endobj 123 0 obj << /D [103 0 R /XYZ null 188 null] >> endobj 124 0 obj << /D [103 0 R /XYZ null 176 null] >> endobj 125 0 obj << /D [103 0 R /XYZ null 165 null] >> endobj 126 0 obj << /D [103 0 R /XYZ null 153 null] >> endobj 127 0 obj << /D [103 0 R /XYZ null 140 null] >> endobj 128 0 obj << /D [103 0 R /XYZ null 128 null] >> endobj 129 0 obj << /D [103 0 R /XYZ null 116 null] >> endobj 130 0 obj << /D [103 0 R /XYZ null 104 null] >> endobj 131 0 obj << /D [103 0 R /XYZ null 92 null] >> endobj 132 0 obj << /D [103 0 R /XYZ null null null] >> endobj 100 0 obj << /P 81 0 R /R [63 63 549 729] /V 74 0 R /N 133 0 R >> endobj 134 0 obj << /Length 4470 /Filter /FlateDecode >> stream H‰ŒW]Ûº}ÿÀGªˆU‘’(éq›6í] È]´7ÅBkÓ¶YRe9¹þ÷/ʲ½7(X‹äp8Μ9ó§ç÷ïþøÉ(£ž·ïßUqåTô‘&iœ*ÍãªPχ÷ïµé¿þbÔç5ýÿñþvÑóP“M&‰+Kªø«°qR©ÂØØå¬+N’Ì‘Š~)úU#›è~7F•öÇ£ª»ŠVÆè>2&¶š–7~l¢"®t·‹þýü÷ß¿C•๮ÊbS\ÎÍÙô8±ÖɱÿŠÊ¸Ð{8)εïT‡ÚØè×6ÊãL÷ëo\%× » §rp[SàŸÿÌÇ8CÇèá4‰w‚pâJ4IƒEi¡úÈÅ©¢U—ÚÑ .¡ë©éy¦£eµ–áüaƒtë'ŒV%|ˆVn¦"ºÍ´GB{µášo<Õ"°g2}T¯"ïoŽhØ€¼¯ö^•Å&5‰6¯ÖuÛ6ëÚ±¬˜*÷âË®ƒÕGuÀyÐ̆ðíú«-gÖó™^äÄÊ'µ§épV'—DÍÄ/ˆ7HØP&E:?еáQœ1ò(„‰Õžìtpê¢ÁÁa |M{V5Ä-˜ áiÑâï,Çgz£¦^,ÇU`@ Ÿ¬Bm<LM‡oÌÅ$G©Õó¾±ð[ßÅŸ© žóePq˜ÿª[ºp®×5¸&×ímÊ@nk.;ó¤ 1ÈÏí4½¿z Ðg%¿M¦1"qºcÑ,”üy¸3dm~Ê}'vîZÔ€± ©‡¿ºÆãЗêÞîÒîb·«R±{­ 00(: €Pv$ZÎð,1‰îFNîd‡ê·tÉL«‰—ödŒlSýIeõÈ—êjë6"%êI»¾‡Éç–Ø!.N/!R¯¤¬–’!VóÊÍ2@,BîŽõJB=ƒ¼AÌ» u#Û!2Ù.AHwðîÔ«WõzßøÈfà›ï´î!úS½¡ï5¥)EW™æä‡€' Ô.Z¥$êAàã ÅÏÓG G°ºÔ/,óÉwk¯úQ=|áÕ7¥Úöf:<Ÿ°öñ€7Ú_H ˆ“íaÏÅz·lrÏ‹$ø ³/Ÿo øFcF¹FÙäÅwGöÿó$â3W ¨ì郞0DjnZŠY€®:|µwq“B&™ê­T(>ß œëÊs9 ¡9nÈ7#Qž ±gz't=€À&”„´žvð{ÅRƒH­›md \°!Ѓ]£?Dì_zÂɋ۬¾AW!œ> XÖT=‘ú[¼Œ™HœäòÞ9ÅIu'†ãR›ÞCzÄ®ªî’CŠ3ùwF˜K!•S‡Bµ,[jª Qɱ ÖJì#¾Ð×-Ž¥°A ©1ÕW.®¶Œv,ûª”eb~?<èÑ~ !6’tAJðM’L‰£Üv\o©LÉÜÙ p©&T !øÐn :v¨èÓrêÇ 8*™ðHÅ, v{$Á1ºUžW`—Ø·eEˆºç9âAŽëIOdàò^ô!}üsž›WPîÍ-ßdžJë“?ô`µIÉ! æyqÑJJ&!ÿ 7/ ¼ÓùD[Í'å§È8b?%޳àO8rÜÊÒ¡îèCQ¾Ž SæO¼,#%C–=Rhî1ÝÀ‡#>€:ÐuitÆEÈTÙ^*CJhq`5þÎi¤˜ÓøûÈŠBªàþ• f]Ù7úÿÒøÄ"Íè @xëÄÙÅNP›¸0ð4¨tm¯¶7]`Š wÕs.¾6 Õ/`Ø€ £ßP "Ÿ£âSið 0Y4$fžçu”\–‰$]ˆi#äF³–›^Ôî‚x=µx©œœ{YgÝ í4EHF^ÚxÑ Í–jfžOõÄzxOO“_Š:3ІZ_ Ÿ+SÉуçÈ*BdËòüÈ&ó¥.…”¨Xþ¿ÔNrv¨J^Tjº¹½ñj{а˜NðÎdSN6ǰ`pF•T\ ­-"ޏx`ŠpíHõ£ ÁuHзäÚ’\ÂÔ!cBƒ¡ÆÒ¾嬛ææÙTˆ Õl<ïŸÂ9 JÆÄüñ7rn¦ã'ÏŠ2TøAdýò^™.ü&fŸ/uÍßó4¾Mèe…´K\sõÒVÀg‘0{øò/—Á±#NP&x?.s 2¶ HfIà>õ9r¡?£Ê@ÅšP¬¦**ZŽe3àyz ‘žˆä û§f‚Î ?¿ÓrØþe‹äÀ=­>Ñ,(âu¢MvÍV82‚DÏÌ"‰ ‘å'ÞzŒ.Š "ÈÂNàécðÅ.CÃK¡+I¾üQè@¡ ŸqrøÍ¥¯ê×—;LÖ™²ôÀgx9™ ŠwWO0õ7‰VÌ5ĦᡡH(RòoÜÌ<#[Ï1^ÃÏ´î&©}„¸Ʀ»Ùô@@N!׿¡~H›ËEQ[6Ü,’{šô‹>@2òšQ~¬;†DÙéGæ¾Å…ûRå\ö˜<Õ žžhnBõ¢}âW×®MgÒ=gP⃩ËYñ°Ã®²Ä u\7qă)B^‰…ψKápð¦W}÷,?ÉöÓ0ðôfö(PI•®B•fC-4¾òOd+z^’-}léy'V[2‹&c)æDÐ äç/ü‹¨ª™hbžØV(Ò-)ÞÑß Pä86”]¨/T…ÎJ^å¼|".2þjùeû“„ðz._5CfˆýË™eçÍ4ûÏ{ºcIÊ.½•IJ¡ÛÇ=R¯Ò8äá–ÏÙ¸(Óô–r›ÜÎèåãkìjÁ`ÁµŒ5Üøû!H±’Îä{Itn×Âp[äÍ·œ(ï °ó‘(”Yks”ßЮzܨ4™‹x² 퉾L§K¯àñð;oZ¨§•Kj&Î|õÛ(cM aRC pçÓJD†a¸m ȈŸ™ËS§ÔMÄ 0KiÒ0|7,`Iž÷Íb%NYs÷¹ñqp† •€±+QüUØ8©TZÂ] ä«Ë#•Vô†¨òqRý–¾þÇ|Õô¶mÑ{þ‡=JA-k¹ü›SÐ$…/I‘=0h‰’UH¤@­ ¸Eÿ{çÍÌŠ¤>lõ–‹M-—Ëá›73ï;øJ"¿€žDgQ û pŽ‘ò”_Íú愇ã7óëÆN?ÁxŠÜŽS r'‚¹âe(qT Ýzõ·¨Î”îÞê½]é÷²¾ ¡ÄkÓü˜ãƒ÷óÜ^îî*è•¿'žœ çóg¹ÀÐ?P²Üˆbf~~‚/=Œ>5#ø»›€žþÕÅ‘ê:vñiQñ„šRk :\ðÂ8^‡Úgžà¾š°•LŸ´5œ}WÞÏÄËOPõ Ͼë¼XµÇžÂD î¦k ßë{µBb¯wÆ>Ä”¶ë‰U×`ŽsêDIj0gÒùK>¯ZÅÿ LÉZ¦8ÿ³ >'Œ4s!ˆ„}mÆŒ¡›èLøBù˜ÊýUxtÁkß‘Nð·Ç4Ê”) §ŠõtÆzÚ:d’s+LÑhti.' Æ‹Í$r}ùŽýÌJ”wµ¤ôèÇt£v•!ý¨c=AãTրݎúïqKv=ªDÃ\[Ú˜Žÿ5!ûYLt ­ÞÊà¡ZŠÌÙJ9Öù^þ¯ªeÈv‘=¢-I3c"âù†é8'©Í‡Þʾ¯æy‘žžùZßFCJ¯6È(üÞå®9±y|\ÝJ &#¦?ïÉÕmf«‡åª~~ðæÝ|×ÌЦùÎÐÅú¬ N¼°mêÙX6…? ö÷²’DRCÀ¶æDe›HD=éî–Qf¶gSó:kZj0·*ÙÊ6Ö7…ü܉ÙYÈéecæ…/ŒþlÀFê‰1ž,ͺ¦Â0²Pr,›ºy‘“k}LkB³€Å¸¿”`È3LXZËÙ°¬p¬ˆÊ±1§€ÕAHAÔ …q ÷ÌP2€H Å;+I[Š‚Æˆa“ee;ÄÁòj½à…iÊlô%HÁ—Jë9Û2çõ°ìW• ŠEø 13GdB§±“S6<—ð™¿äc¡'òÆé0‹Å áè­zùuB´\¤Ž±‰ƒÐ‰l26u¡'ø—-ƲŒ\˜oö3oþ‘Ó,‡I‘Ã$Wižë•ì›w¨¥­o¨žÞËö¾ÀŠ&~ñ˜\¦å“ñÑCdQo8®„£sbX)ã0S¾×Ò™*£Ô$ óZ®h<ÚV1ç=󦔛òfæ€\2tŽ1e1{⩈ð€…ZnØ·–•?ä‡T¤¬9mN9—Š>¼ó¿ÞÎaçáín¸ Þnx£V,¥á{+:Ðæ‹Þ„0Ý] 4×Ô|®í]ê%Õ9"CB—x'aY*¹§hÞŠÅ%½FGÄ5`“µÛØ€™z½Àl_|¹{ßöù øv6\‡o'¢ ô ˨³–ÕÒ+å´9ƒ—˜G&fn"fޤ¦rûM˜2zè<'[°„™¯"Öu±Î†ëkàVŸºdy€ì‹èoRNrpóÈÜ!üµ˜Gz5/‚'¸±¬S– ½±ñˆÕKŠñħlÔÒéi•.{iÁ—Á•ÞkÉd¢ÕŸ€û/I€{)ï'F;c®-Ò >4ÃØ –{žÅ¢Wƃµí$½©KH[õª…%“ÄHzélÞ£äèYÍ{WѤk­¢‰;“Û†Ém> ñš±æ$_¨ý²x œ±† f@ºÀRª€>—Ãý@–hŠ…îqé]ä0Ï|ãiÂçyé&Œ~÷¤Q[½˜ýqˆÏ3sÐÙMܤ2°'<)èÊcÉø:H‰’ŒÃÆdXŽ Ó°—H–ìÞ$ }¹K%¬¿z-ŸíCÂ×·³µ… §'Iý‚QŒŠ$!úûºÜpóJØRÀ8±Ôsá¼›ÚÅDÆ"ëx†¬ÁEv4NIÄœ.V13€Ö—îtöãQîÙÿúø~ó­¼"eä‡xá#¨CMÊo2”²@‘OÓŸúo÷.ó® endstream endobj 135 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F2 21 0 R /F3 22 0 R /F4 54 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 137 0 obj << /D [136 0 R /XYZ null 730 null] >> endobj 138 0 obj << /D [136 0 R /XYZ null 718 null] >> endobj 139 0 obj << /D [136 0 R /XYZ null 706 null] >> endobj 140 0 obj << /D [136 0 R /XYZ null 694 null] >> endobj 141 0 obj << /D [136 0 R /XYZ null 681 null] >> endobj 142 0 obj << /D [136 0 R /XYZ null 657 null] >> endobj 143 0 obj << /D [136 0 R /XYZ null 646 null] >> endobj 144 0 obj << /D [136 0 R /XYZ null 635 null] >> endobj 145 0 obj << /D [136 0 R /XYZ null 623 null] >> endobj 146 0 obj << /D [136 0 R /XYZ null 612 null] >> endobj 147 0 obj << /D [136 0 R /XYZ null 601 null] >> endobj 148 0 obj << /D [136 0 R /XYZ null 590 null] >> endobj 149 0 obj << /D [136 0 R /XYZ null 578 null] >> endobj 150 0 obj << /D [136 0 R /XYZ null 566 null] >> endobj 151 0 obj << /D [136 0 R /XYZ null 554 null] >> endobj 152 0 obj << /D [136 0 R /XYZ null 541 null] >> endobj 153 0 obj << /D [136 0 R /XYZ null 529 null] >> endobj 154 0 obj << /D [136 0 R /XYZ null 517 null] >> endobj 155 0 obj << /D [136 0 R /XYZ null 505 null] >> endobj 156 0 obj << /D [136 0 R /XYZ null 493 null] >> endobj 157 0 obj << /D [136 0 R /XYZ null 475 null] >> endobj 158 0 obj << /D [136 0 R /XYZ null 456 null] >> endobj 159 0 obj << /D [136 0 R /XYZ null 432 null] >> endobj 160 0 obj << /D [136 0 R /XYZ null 408 null] >> endobj 161 0 obj << /D [136 0 R /XYZ null 396 null] >> endobj 162 0 obj << /D [136 0 R /XYZ null 384 null] >> endobj 163 0 obj << /D [136 0 R /XYZ null 371 null] >> endobj 164 0 obj << /D [136 0 R /XYZ null 359 null] >> endobj 165 0 obj << /D [136 0 R /XYZ null 347 null] >> endobj 166 0 obj << /D [136 0 R /XYZ null 323 null] >> endobj 167 0 obj << /D [136 0 R /XYZ null 311 null] >> endobj 168 0 obj << /D [136 0 R /XYZ null 299 null] >> endobj 169 0 obj << /D [136 0 R /XYZ null 286 null] >> endobj 170 0 obj << /D [136 0 R /XYZ null 274 null] >> endobj 171 0 obj << /D [136 0 R /XYZ null 262 null] >> endobj 172 0 obj << /D [136 0 R /XYZ null 250 null] >> endobj 173 0 obj << /D [136 0 R /XYZ null 238 null] >> endobj 174 0 obj << /D [136 0 R /XYZ null 214 null] >> endobj 175 0 obj << /D [136 0 R /XYZ null 189 null] >> endobj 176 0 obj << /D [136 0 R /XYZ null 177 null] >> endobj 177 0 obj << /D [136 0 R /XYZ null 165 null] >> endobj 178 0 obj << /D [136 0 R /XYZ null 153 null] >> endobj 179 0 obj << /D [136 0 R /XYZ null 141 null] >> endobj 180 0 obj << /D [136 0 R /XYZ null 128 null] >> endobj 181 0 obj << /D [136 0 R /XYZ null 104 null] >> endobj 182 0 obj << /D [136 0 R /XYZ null 92 null] >> endobj 183 0 obj << /D [136 0 R /XYZ null null null] >> endobj 133 0 obj << /P 103 0 R /R [63 63 549 729] /V 100 0 R /N 184 0 R >> endobj 185 0 obj << /Length 3828 /Filter /FlateDecode >> stream H‰¤WÛnÜ8}ࣤQ¢n3O™x&È“Nc^ìÀ%º­ÝnÉ«V{à öß·.dKêûì€[¢Šdñ°êÔ©_æ¯_½ûM %æ¯_å2ODô‘ŒSÅ2OÅ|õúU `ýñ›‹5¾ÍKúÿçëW^êÏÿ‰+i»Ò,TRGQ$’D¦Y‹ùØÊ 5M»ñê¦ï¯ÿðéîó½¯Bïë¦ÿãÖ+ºUYß-êöù®oªuWÞ]÷V 5<Ü- Ú6<ðÔµ%?½éÌ¿o}ÿûüèÑ,”ZÇ9í9Þè#M¾l›æ-Ún·q‹¿û-²'T2 ÒHÌ” U’oÏeö|_7~.3¯óg~žè¥]û© =ó“[ÌÁÉ$Õ9OËÐ ž°ºË d¦#ílÊ”oƒˆ®áÆûhÓù*Ú+ü@æÞ²ög™Œ½¦ò3™zâÓ»/âÙx6eßv¢¥ñ'ÉÈ3]Ñ×mæýcÑ‹¾+øuíÏ ¼^Ýt¢*úBØW8¦›vçLp¦á—WÖ^oH6nK³ö©Ü’‚ 9¼j»ÞþÖc«vCï]in}Ñ·b{Í} èmAÀÝñöÊb‰‡O¼ì8‡Q#°f w†YÜ¿ôf} ßÑüÃøŽ .ÂwìÑ‘ðs”iÑÛ\ŒÁc‘÷HòÓXô˦gaÊ´ ãƒ19€ÅшUò$bÃRG\†Ø0a†8%ѲÜAöÙŸaÝ@¦†àëꞈ38?AÀ(º§ªàOÀÝ@(109†,@;S\¨vˆ7¥ÀÆ®ÖØáž ø8¸Ì¼qZj î@u¬Ü­.ùy·\L!Qƒœ ’ÔÉ«÷¯#o"¼† Ì:_bM]¼ñH±s ^xï̵>V/ V³A½è–§{T¡ ²÷>n¡ù®xÞ…K ‘xñYÀøÂGØ)n=‚âåhãØ£0Âø9pÆ}øÝG´q–J¹ñJx&e—¡BRc žAybaÀS%“beHJdE¦ –Züjü2Œ§8T+šüÿl˜E‰ÌâóAºÚÒÞœºÂÉÔ?/¹ÈpH u÷øË.fèDÇåK³"¢ŠY¥Æd‚§%y±lÔØ~±½k_¯pãÄ«@ìR Õ!ƒ[E¹ÕaÁÈàZñ…?Ûf·âéâ[¼#‹/âÙW‰¦ì[6°+1XÙ!*±ø=éAp ¥õ6Âô!³¢½µlxdýÀk·6;V°cn9ûJÛ Öž÷±ÇÞîZŸ?”ÅÒ.As­=¶}Û.£DcyºÝKÀ2ËÚz¼°Od±uƒ¾ÍøŠƒI$ÒUe9VwØv-s:̾µàM†›4.bp „²e2ÈÄ ôNÇã/l)xÊr \OXÕ².6ï—mù/¦æV<ñ–ý¹Â%°…ÚÃgíðaâ;…ÒdË´Á¡ÒŠ e,̰JL…+¶Ô€à·þ&¸dö>†®³r±7þZ4¥-°X„ïýèC°/Çt]J¨D{àLtÝ,„ü ­¸Õðk«V—KÂ+Ö(¥/S#[˜®ÌºG"£ABá/½@*¨f ‡ÓÈÂÇô­°+$kÏ~ÆØBR^ $eBC扟7üÓ³šÁi×ü¾8IÐhNáoÄÐ0õÿŠ¡+{×¢Óˆ;P#B¶:;©RÈ»|;ô¥4v”$¿{úy¡ÿlêLÎbVËX¤Äqå=pÅ#Š4¿øÈKCÀÑÄš›†šÌ6Eù¨äJü XwMÝ@*÷{I•¥ã»Ñ£³Ýßw<Ö(óâ±Ç£–'p-éÉÈ{$îÌÉó uö âÞ8 J6djšM±ÝEÂL9™iü…þÛ9†—bÁÁìcWLf*•pˆ|¯ñ ²%ú˜… °rm«û†ø£}YC¬PV‘)%¤G!j;™|e¹BQÑ\÷Ñ©´©ø…˜f’²$‰œ¼Um5–<A‚?“Z€ÙËyl¬¼=•Ç“ÎFël§QŒ°st8…‹%eôÄÒ y+4eì+dr§'Q= ƒ!vüT30~yÞúað¥ãb_‰†ûº ò¨b· F¹˜»\¼6ýÆç¶n†M^ÃÄ!ž ~Xnø:”Œâø ºI.“qYòéÚèNì/+CTÙVblJ$sΘÕ9ÒÉfy6âTê|OÜx-H²ŽóÐXn̉ì-ò?Ö·_"jít¢\¾Œoâ²%°ìê{Ãß×Iñƒ™èÌÒl„ÙöÖÃ¥Õ3¥¥úoçÕÒ›6„ï•úöU øvoiSU9T©ÒÜhT-ØK€#g)ùõùfl,jš x×»ëÝٙÙ<y¶¼—·ß¿\ÿþVšþÐîT#ͱÕ½1DÁdÅØÝ‘ƒ£¦àâ˜M”Ka–v³9Ð+^>J ‹Èø¥À+%œÙè‡hžÙB>€t•‚÷ûÀäYO¶™£árþ›‚Ôü´ÃÙs©cXÂfÇ^s>¸¬‡q4X‹á#­æÔ=~RרƒmÁ”î ëϱ0í©çÑ^ÔÐz-ó—Ñ×0›?NV×1ÄÖ²5îÒæ9k;ÜÑ@†‘ÎãNäbnyã‰è¼Œr6ðªi±ñÂ×Uf]¸>ÐâtÌ:9ßÖ´x»­áè4ʉ¢c=¯‘BÔÂ=moPÁÍ·vçË’ bÁÈ‘úRdZ8œ( ÇDàè Ç8á($åN§QLÿ Ü ñçäUD:29ƒ)%\… +(zˆóGQ8Ðâ $÷¢‰úÖC–•†T75Y.&î½ÂJ \%¯‘Fky& ”2:Ñ¢e{“+$äjWGç?’«µÀÛ“ëJ“€ÁqÚª˜¥ç(du:©"H©)ÙÞ, õ¥tP~ñß ~e¨Ò)¶rMí%¾ò´\lO8•’*ygg>ƧpžD O¢tKt¾\:Ü]ØåE%ƒ+‹‡Ÿ ™)¤ãS„ ÒgÍ»‰g›OîvLô2™%Í7túÄët#¦öhšaç)¹fQx6ÄŒ (C· Ö ‹wºo %ú_ð«s Yž‡;WlEO/£`6¦Cd]òNäí#»¬`m•]©ÞöüÉKmˆ‘5¹2¦x9gçPìu†'J·9æHk`L.ÏåpOØ3¡ïÈïC>%”¼ß\_v¦¤é“6GpñJª<ÿQÆ­ù½¹::ò6ÉïîDÔ<@ÁäšMÁ˜ ž‡Aâ;9R¢µΔv¹bàš2(ÖòTJXr‘+˜÷Ô‡±qÔ,RÖÙ§k´5ü¼M ¿·t·(¤†!ËΡÜiÈ2NÅ:-#ò“…ºüšë+’eò\©ªg½¨'üz÷þÝ_yö0Á endstream endobj 186 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F3 22 0 R /F4 54 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 188 0 obj << /D [187 0 R /XYZ null 730 null] >> endobj 189 0 obj << /D [187 0 R /XYZ null 718 null] >> endobj 190 0 obj << /D [187 0 R /XYZ null 706 null] >> endobj 191 0 obj << /D [187 0 R /XYZ null 694 null] >> endobj 192 0 obj << /D [187 0 R /XYZ null 675 null] >> endobj 193 0 obj << /D [187 0 R /XYZ null 657 null] >> endobj 194 0 obj << /D [187 0 R /XYZ null 645 null] >> endobj 195 0 obj << /D [187 0 R /XYZ null 621 null] >> endobj 196 0 obj << /D [187 0 R /XYZ null 609 null] >> endobj 197 0 obj << /D [187 0 R /XYZ null 596 null] >> endobj 198 0 obj << /D [187 0 R /XYZ null 584 null] >> endobj 199 0 obj << /D [187 0 R /XYZ null 572 null] >> endobj 200 0 obj << /D [187 0 R /XYZ null 560 null] >> endobj 201 0 obj << /D [187 0 R /XYZ null 548 null] >> endobj 202 0 obj << /D [187 0 R /XYZ null 536 null] >> endobj 203 0 obj << /D [187 0 R /XYZ null 524 null] >> endobj 204 0 obj << /D [187 0 R /XYZ null 511 null] >> endobj 205 0 obj << /D [187 0 R /XYZ null 499 null] >> endobj 206 0 obj << /D [187 0 R /XYZ null 475 null] >> endobj 207 0 obj << /D [187 0 R /XYZ null 463 null] >> endobj 208 0 obj << /D [187 0 R /XYZ null 451 null] >> endobj 209 0 obj << /D [187 0 R /XYZ null 438 null] >> endobj 210 0 obj << /D [187 0 R /XYZ null 426 null] >> endobj 211 0 obj << /D [187 0 R /XYZ null 414 null] >> endobj 212 0 obj << /D [187 0 R /XYZ null 402 null] >> endobj 213 0 obj << /D [187 0 R /XYZ null 390 null] >> endobj 214 0 obj << /D [187 0 R /XYZ null 378 null] >> endobj 215 0 obj << /D [187 0 R /XYZ null 359 null] >> endobj 216 0 obj << /D [187 0 R /XYZ null 341 null] >> endobj 217 0 obj << /D [187 0 R /XYZ null 317 null] >> endobj 218 0 obj << /D [187 0 R /XYZ null 293 null] >> endobj 219 0 obj << /D [187 0 R /XYZ null 281 null] >> endobj 220 0 obj << /D [187 0 R /XYZ null 270 null] >> endobj 221 0 obj << /D [187 0 R /XYZ null 259 null] >> endobj 222 0 obj << /D [187 0 R /XYZ null 248 null] >> endobj 223 0 obj << /D [187 0 R /XYZ null 237 null] >> endobj 224 0 obj << /D [187 0 R /XYZ null 225 null] >> endobj 225 0 obj << /D [187 0 R /XYZ null 214 null] >> endobj 226 0 obj << /D [187 0 R /XYZ null 201 null] >> endobj 227 0 obj << /D [187 0 R /XYZ null 189 null] >> endobj 228 0 obj << /D [187 0 R /XYZ null 177 null] >> endobj 229 0 obj << /D [187 0 R /XYZ null 165 null] >> endobj 230 0 obj << /D [187 0 R /XYZ null 153 null] >> endobj 231 0 obj << /D [187 0 R /XYZ null 141 null] >> endobj 232 0 obj << /D [187 0 R /XYZ null 116 null] >> endobj 233 0 obj << /D [187 0 R /XYZ null 104 null] >> endobj 234 0 obj << /D [187 0 R /XYZ null 92 null] >> endobj 235 0 obj << /D [187 0 R /XYZ null null null] >> endobj 184 0 obj << /P 136 0 R /R [63 63 549 729] /V 133 0 R /N 236 0 R >> endobj 237 0 obj << /Length 2996 /Filter /FlateDecode >> stream H‰ìWmÛ¸þ ÿÅt­%ê-‡ØÛí9$é!1ú%[,´¶ìuk[†,çn¯èï33”-y½¶{rAÐXSäp8|æíáÃçϾûÑ(£†“çÏr?OT€ÿ<ˆ‚ÈSÅ~žªáâù³@M!ýú£QÓ5} Gü÷—çϼLÿAš"§i`r? •d~`âX ¯!êAó®óœ÷~ò>”ÍF'~îÕzú©·ÔƒØ7žú\È`¾‘ßò¥ÎüÄSúïßè0Ûš-6+G>Hã›ÈYL§y¿•uÅö/†ñcyºÍÀݦÅ%ð“ ´ØÖ^# åòt£ÌÝB}ÜŒFåZÇ0QþNô ôCo3÷OXœd9`êX8Us_ÖÚD~DhÌ7¥(:xŽ’3.î.µø Ž |ëÕ•¨Q5ä©WêîQ7ÞLàŠQ=»+eMÍ–ª¹1U—ó²‘R-«Æ‰ßh=0p@ b*±‰àú6‹Ø,މxgW–:»¬ÍA’{WÆÏ¼Õƒ’aê­ÜoI¡{u!¿XΰJU-ØZMHLªp¥ˆ"Ž¿ÔHöµj—{ ¦,½q«­Ô†g×ø²ä!mBoìLìÛQìD‚4R%‹ŒÝ¹ÅöÜ?[6êòû«7·?oHysã­ëÑ…¯› u÷t/Ôª®F7z›Ñ£B“äÛ¢ŒøäAaÃj±oÅÕZ§ˆW¤Ùžµ‘Ÿ¤6oÃGrJ= ±ÌFÝ‹¢]ªocl¨£„AævLÙâð5åx„œ1‘“"øa6ÝÈpC¡„pM¡&5ÉU Š‹ÀuD¿qgƒÜ£ó \_þÿî(¸Qáô|×—ÿ#\”†çìï$p%_;˜†ÛD!‹¦´5«¥U1ôq/3ABÝúr4Ú,Pu™¤ó‘\­X¬¬)&—\°6\Þ30>^šr]§…i¹,ùZ1ÕuoN1mÑF©ÅÿÆWAF¿ùî¯ê³©–£¦ªÁyxG%‡Ö‹¢ÙwåŽ f†šü)z9"kF»á¹qÑÍê¼PŸ«ÙX½XƒŽà£¨£Ùí‰ÙÏ·z1F;½-êúBñ¿6@~Mwà(ÚÕ¾—Ë Eç&·È¶Èæé#NðåÈ-1Ú6S‡Õkç)ø‰°ŸÏÈU¹sRÔqRäœTñüÊ1O mî ÑT:¡7¸¢kþ 뛕蔓(ZQØvD‘Dï¥æ«E¹sêw §7ÊT¡ØÚ’u,*®°™°\Z¯¤(¤žüuIDÃfv„::ïK™Ñï …÷ÇûZÖw¨üvè^÷ár¨a^®qÜêÔP§VÚ5Å•,–òõÈí{¥,Lppz ”Q”¥”•ð’z3jÔ¿D›„°u¬(D’2q¿jjÄþ÷‹_–ñ±8Ýšnñkí s»/°•{s»Þ†§ &qÏL †2Jª]£‰D:öYH•×®ºâÈ•kêjÅ"Q£Êb$唫ƒj7 ŸæÞ”"-yy„|&!_Ø¢÷ÄчŒôxwÊž€·#p¼ó„­p¸_3J–!̽™¼y–:'Ö!>–ª+õˆæTÛÄA '視%ôÕ8 ¨Å÷; Y”&§8f!ýû°#C?;° fÔ]׺™÷Åw·ÿ |;çáÛ±è‰ð¶M]bê:8ÞC ÅàS‘G ÿNšäƒôŶ͟„Éä‡yN,‰Fj]GÛ©z±ŽÀyˆí6HÞµ¦¼…ì=ã8±[oqÇ.Žî_s5fÛž”¼$õ›¸…, ‹À¥7™hA¼á-·Ú–nº‘|ŠD†AÖy=uÀý7Ñ WHÅ÷ûý¢‰ d“'~¶ ¢ÈºØ¾¬µ¼)±XXË ã¥†ýæ$A7™í[úÉc$6¢Oa¹½¿óux©Sp*‘{ŠÙ¼¸x皡ǃßtáwŕ̻6¸¦ò l‰èH`ÂGýÖaÆ´6ÔôÒÔ1rE+òdö–êNâ½y?TAô•f\gš„–²ÜðÇ…f¢~ùw¾ãjõ¼¹e]ꈞM‡Žxûþµ¼RsVU±ôrzÑ%%ZHݰ½Š1Ý«“æ« ®:Žfo ®ÜûQSŸ{«³ØR9AbàØÉ\µF¢àM ÕP~\ ‘`<ÕSål=}^Ýʦk¡h?ðä[hË<è:%)ïÄ:ªä3z×뺊ÚQ[#ãíõ$²aRåË]jK\øJe—âvîg†”lbº_Éₘa,zU…Ä«•µ = I®$uïœ"I[pÿTâdïÄÄ»n|Ká`ôJÓ_ eZ¯XÁFN¹“Ÿ9ÊÊx¼¸X¹éR¼ó+Ïžª8&Jü ›Ç?_¤LÞÞ†_Îz¨šSò6…?Ò1jB¶£ènŸxÜÅA¯yZ¸Qt©iJ•'Ëw†Ô\öqƒiun¹Q–Ò+¡«iKùVí¥n´j*uWªb4ÚÉ%7/šrL‹ðÂ{¶R®ÜR@&}ò «‰6º×ÖÀ Þã‹Zûî•›‹Œìd€?$ù 0î±³<âûÞÎóòaÇJ‚ô?ƒÅ¼_)¸Ã™›”ZoÜæ¤æ‚3³)¨¾0õ6aÛÌ<°jH¶ÁÕÀæ¹6Ÿ¡&khÈè›ffæ 56»UäɸK`ü›_€«cDenfóyP*¤öÎ% PÁj¤–@Eñ‡‰&Ã;² ª¸XÁ$骑Ê:XƒÜÂ3Ũ$@…¬´„HA©%¥ÀÙÔ•Òy +€ÅHY"„‘S ¡m pl—k/““ÿ endstream endobj 238 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F3 22 0 R /F4 54 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 240 0 obj << /D [239 0 R /XYZ null 730 null] >> endobj 241 0 obj << /D [239 0 R /XYZ null 718 null] >> endobj 242 0 obj << /D [239 0 R /XYZ null 706 null] >> endobj 243 0 obj << /D [239 0 R /XYZ null 694 null] >> endobj 244 0 obj << /D [239 0 R /XYZ null 682 null] >> endobj 245 0 obj << /D [239 0 R /XYZ null 670 null] >> endobj 246 0 obj << /D [239 0 R /XYZ null 652 null] >> endobj 247 0 obj << /D [239 0 R /XYZ null 634 null] >> endobj 248 0 obj << /D [239 0 R /XYZ null 598 null] >> endobj 249 0 obj << /D [239 0 R /XYZ null 574 null] >> endobj 250 0 obj << /D [239 0 R /XYZ null 562 null] >> endobj 251 0 obj << /D [239 0 R /XYZ null 550 null] >> endobj 252 0 obj << /D [239 0 R /XYZ null 538 null] >> endobj 253 0 obj << /D [239 0 R /XYZ null 526 null] >> endobj 254 0 obj << /D [239 0 R /XYZ null 513 null] >> endobj 255 0 obj << /D [239 0 R /XYZ null 501 null] >> endobj 256 0 obj << /D [239 0 R /XYZ null 489 null] >> endobj 257 0 obj << /D [239 0 R /XYZ null 477 null] >> endobj 258 0 obj << /D [239 0 R /XYZ null 465 null] >> endobj 259 0 obj << /D [239 0 R /XYZ null 453 null] >> endobj 260 0 obj << /D [239 0 R /XYZ null 429 null] >> endobj 261 0 obj << /D [239 0 R /XYZ null 417 null] >> endobj 262 0 obj << /D [239 0 R /XYZ null 405 null] >> endobj 263 0 obj << /D [239 0 R /XYZ null 393 null] >> endobj 264 0 obj << /D [239 0 R /XYZ null 381 null] >> endobj 265 0 obj << /D [239 0 R /XYZ null 369 null] >> endobj 266 0 obj << /D [239 0 R /XYZ null 351 null] >> endobj 267 0 obj << /D [239 0 R /XYZ null 333 null] >> endobj 268 0 obj << /D [239 0 R /XYZ null 321 null] >> endobj 269 0 obj << /D [239 0 R /XYZ null 297 null] >> endobj 270 0 obj << /D [239 0 R /XYZ null 285 null] >> endobj 271 0 obj << /D [239 0 R /XYZ null 273 null] >> endobj 272 0 obj << /D [239 0 R /XYZ null 261 null] >> endobj 273 0 obj << /D [239 0 R /XYZ null 249 null] >> endobj 274 0 obj << /D [239 0 R /XYZ null 237 null] >> endobj 275 0 obj << /D [239 0 R /XYZ null 224 null] >> endobj 276 0 obj << /D [239 0 R /XYZ null 212 null] >> endobj 277 0 obj << /D [239 0 R /XYZ null 200 null] >> endobj 278 0 obj << /D [239 0 R /XYZ null 188 null] >> endobj 279 0 obj << /D [239 0 R /XYZ null 176 null] >> endobj 280 0 obj << /D [239 0 R /XYZ null 158 null] >> endobj 281 0 obj << /D [239 0 R /XYZ null 140 null] >> endobj 282 0 obj << /D [239 0 R /XYZ null 128 null] >> endobj 283 0 obj << /D [239 0 R /XYZ null 104 null] >> endobj 284 0 obj << /D [239 0 R /XYZ null 92 null] >> endobj 285 0 obj << /D [239 0 R /XYZ null null null] >> endobj 236 0 obj << /P 187 0 R /R [63 63 549 729] /V 184 0 R /N 286 0 R >> endobj 287 0 obj << /Length 3522 /Filter /FlateDecode >> stream H‰œW[oÛ:~/ÐÿÀG*눺«Àp“ölmO‘û’†"ÓŽveË+ÓîIýÎ…´|imïi˜"‡äpæ›ofÞ^¿úíƒJŒ¦¯_~‘ŠþÓ "?ÉD”øE&Fóׯ1éß¿*1[áר¢¿ß_¿’…7ú7ž»“ø¡’È ‘©ÈøùCw-Iï߆ɡäÀêÔ ü4cØ0ºÅ CÖ†ANе܃lͳî<ÔLŠMÙ¬5ôóô‡\ð€¬@äð® üXv­ˆªx¹ŸIí~!Å£œÀS_ɪ«Ÿ4¯‰z!Ì3‹‰N7ºd-­±âž7P pdˆŠ}•d‘KÅ¡Ê{ã=CÔ #†÷ŸnîÆŸŸ<ÊaUýë‘f'¥)ÍËR_‹M[OÄÕª*ø(»yUg([·›±W“UWË®»¬¼0 ‹X€bat32ãF/®~,»¶âÑU§ÿ ïØ7ì@…~¥™€#Ã8Üê°¨z°Ð#R°E6¨ ý±ö 8wîe`ûÑ”F‹Ç¡\ÒÀœŸÊÒÔv~Á¢ß·æ©•7È@¶^Ж™ÏÐ@“¨•1:ÃZÓÇ„/Ý`Hç‰)¯·´b…çÞ `”æÐ‘ÊÏt£ý=ëÆa…úT_O:?®ø eÇKÓ±'øn°`„{î{èã•éꉗ¸©{øæŽ™¬L~89AQ^¨ÚõÂà¦þ¢ÑÌÒÞè†LÙãPa­9X~à"åTZlÍ1*ä—µW°UcøYÒG»„R¿9´->‹ ½txþ/DøKSÏëz"8,‡>ÅwÙP¾ûí±ñÂc¹2m0Äù%:?",jžKÓÎAwÑ DƒÛ^xy½¤_p,Pà©`¬–st1×s¾¡{¡åv*Jaɇ9g"ø¬Ž9¤­,©ð_ë$æXí!°¦~”ŒY@¸D]rP×@” l<¥’ÆHQ(æù2ö•Gûg£/c{Á°óâHÎУ±œë…Yq½ñ”êü%ù'1@ Ø!ÿIáÀ´Aq‚ó÷÷~¿„ùƒ´'¦Ø1ÿÐËB¹ñ"t|Y7å¾ –tp–ĉ,ÑàÃR˜°}ЏÀyÍó+`NJ¾…pàG £€-7 uœH Ä‚À ^‚ŠÜ C"ɳcÌ¥©¼û<¶ë[¬[{(´àeC×%¯á=íüÚ©í9wcrÓЋ?»âãçßÅà­h訖¤³kû #è¶oQj÷-9l¤·(Ü0}òìíUȆÅG/OäÀQ÷N/k!< ’¶‚W ”{‡G©Ÿ7cÞt‹/ä;šü'Nx2æ ?±x•¦;®{ª#d$=y$ÛG1¬ávL`HzÒЋ‘¼nx‘×h)qs_È’¨ Q~Ë‹s êTp"þÉkר3!âqŸìAp%nWg ƒSyë®üˆ(N”à_D0ç2<`Í·<ñOã·óÓâ|i§5ûäOš=WÁÅÄÃn')…ýºÜÜßpQ䆪wJ±-:ñ1Eݕ쀕9Ñ#ƒ`¹FÓÈz)<ˆ¿ ;V‰¤›ö Nø›«R¦q-®HRØ©ŽR¤º³–Èã}KpR^2™î›ío½È&?¥ñµe2ÆŒ!+MÙ.·äPv@þ3zm9™tÌ%zåj(íÔR7šsR¢ê)‡a1©àƒö?5mõNk­Xò•æ¬}2,\Žì³röá’䔕ö¸ 9?«öÑH;iÌTºú¿|±‰ÊkGå‚àzÁfýÝÕráÒ3öOXKäò…ë,~aœ¨€X‹Žc_èÓ !ÄLˆÅ6ð9ü³5v—À+ ÜáË™éV¯v«…-ÄÊ”W vy•„('.ŠŠ »ìjŽ•0-/d–<^óá ·Q–´–g‘çî=l„ÿCý֋놸¯ÒCã$;ÍI*í„(NlþCO‹ ¢Ê2NŽ5…‡)š„jb?seK.@2»´2ØAa(.H¨Òö:°¶‹¼TQ{¥q¢gmÁnµËçÔœàó½ —qW²Ã].ËŠw² V±tôBì¤dM“?4FNë²zÓð>^‚âc¯€–wáÕΠÍvy!ÞyÛCðí@ã{¹dWãbGãm ¶%’ÏĹižc¢Gž´+ûI†$´X°èüIÿ¦È¸Ì¨4ÿBíÍÇ‹–&§¶2à· TæÃ#Š£ª,èë—ÐY¶ÂF6qà4õŒ‹×5ÿð¢ýX•u6 #,{ÔI׆€¥K9¡½nÑžËi˜Çˆ[ðòAÆ˜Žœ|GÎV¹³èWî¡Ó¶§ÿ¶I]Šÿ½΢•†[›&¡óýˆãêÙ£²ÃW@Õb¹ñTâ&ÑV˜¸ÉÌЕØC‘v<ªÙ0ŽPÚ·:k‰ 8ˆÓe×V'ÂtOþ²0uo½×s®( P[º%yš"Z‰;ž¸E †ÎuŸÄþÀU'úOðPÅaìÄbábñ^›5@‡Á“1ž álJ4kþ…-'Ÿ¶n”¥~¸Ûšùtí¯­»/YiÒ£*È·¥ÉºBRçøÀ¿S¦“usQû*;* d ¥\ÇñÖhÖúDtìòk«÷®’jíº .™´ga1±eRÕÕOš××Í3‹‰N7ºd ÌÖXq„ –d¾c@è!Ó8P` éÂ5P–2j3}d«cð^»"e€ˆƒŽ‡ŠÞÖ.ðYtÊk/´Ù ~ïø·6ú(·`ªˆ¤¹*âÃãœP/ µ¯wãû92Á÷Gšj—×bÓÖqµ„Šz;îôüZàú†ŠuðÙµÀè„Hû^ˆrk‚/kê^Hý*/üh1¯„ O*6‹ ça†½8‹‚¨GAf_K½hL=g¹®^¤ÁÍ¥iç X-¤Èš/$E KQ ôY+B,k"Ôv‚9% ï®1çèN·n{¡Éñ0K‚ÅÄ÷Ú<»Œži7v»ˆL4I¸í#¸W[¼É#g:F=\ÏTó1V íaQ×ãa‚nwnÇÀ€îŠ Ê²½ÜbÉì’hJÉßîQ[)ÄWoñâi›ðM!U–‡ WæE´‘ý››–o^xH³ãû³< ÏÄý£Ç}¥á¾¨·{"û°žskùcîÆÝ‹hxTARà ü½å‚Øilö° ‚¥ÝíáêY½(pCR§ŠTƒn˜2 ‘_#¹v'–Ž€?¿ºû8Fuœ‹¢ØqØyq$gnTšgÌ7Xæ©Þ/}… éO¼’rصKw.6!bÃ"Ú‹Í‹MÚû+v€zY,É\™,ë¦|jÀ¢KʺÃüŒ¦†€íMh€ðˆiTåÀ0¼Ç$ÿé¸snÿð~„Ÿÿ ÏH?ßÚ…áí­¼¥ú’:J{Ùÿ˜¯š§zGâ?ä˜"m•É×$$`9¬Ø•X-GÔM¢¥"mª~üül§“i›mà€8µñx<ÉûùÙZ#Œ¬,Q3‰ ×>šÆÍWmw÷_oqݵ• ë/ürӞɇD¯ÍÇ[=ކ߾Sšãµ>PBÌ?0U7”&|¯:H>-ï?íñÝŸGü!î@5÷º‚žCtÊäN™ÊGZWË#š!ŽOå  «ßRòE®=Ä}{:j1ª2£›ˆ).#£’.¨ÂÙ~²OƒûãMÔ9(Žô©cGnu†ÚwŽÑäšj2evŠñÔ8¾ÿ4ŒC ô(À&a³-]¢b+ÐB[)uZ‰ í.`þU‡Sw>WN ƒ€WA*’y6”–¡èÈq”¼ “P2%sD鉾à9Ô`>’¹ÓЬjΑ0x–G”$F-ÙP­5;ÂK@Ôöˆ&‚h$¥ Ñ¥äùÒªn¤,³S´æÀ×g5ßÚš8–Ïs7¸‰[¡»±Ò-ÁDj%MIöÞZ‰Q×dž+0Í¥®Ž<ѧ±KòÌ%y¦§3©@Èotå[©à,|æ*ãJ +¡« Ûå–­ä-‘òcóZ\Þ‚¹¨ÕÒM²r^YÝó¼I—¸X³¹óRƒ¼°±µÑ[ÉR€_À;w÷ŠîÇïUÝÿŠ1\W(ÿ«rÞSÿNÎ÷=üãìÆ-+)BéAoþ%íf§üŠzѶ,_ XnÓL?ù¿•.Ì!¾Úd‘nG©‹ôuPœ3k¨è¢Ò S=W46ÅÄÂ~Ù%‘EU”DCéÚ««­Âa(ÒÄ#mWMñQ3-¤4ªê@™‘#kéô4”§v!‰â&D'õŠÜÚað Åh)H 4Ì/%’Q“ô˜† kŠ}°ÔßiÊ7µ$õ^IÛ@e¨£Fæ©*šó<#u¦õH-;-I©âB­ÿØà6²3Õ;Õõì5™òœvO€EÈÓV, Ru+qi…Ê5“¨s^i‚Z7îIßC•ž+ó Jú¯”ùwÛlNÓÓdêöÜÿ#æþôøöÍoUÇê endstream endobj 288 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F3 22 0 R /F4 54 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 290 0 obj << /D [289 0 R /XYZ null 730 null] >> endobj 291 0 obj << /D [289 0 R /XYZ null 718 null] >> endobj 292 0 obj << /D [289 0 R /XYZ null 688 null] >> endobj 293 0 obj << /D [289 0 R /XYZ null 670 null] >> endobj 294 0 obj << /D [289 0 R /XYZ null 658 null] >> endobj 295 0 obj << /D [289 0 R /XYZ null 622 null] >> endobj 296 0 obj << /D [289 0 R /XYZ null 610 null] >> endobj 297 0 obj << /D [289 0 R /XYZ null 598 null] >> endobj 298 0 obj << /D [289 0 R /XYZ null 586 null] >> endobj 299 0 obj << /D [289 0 R /XYZ null 574 null] >> endobj 300 0 obj << /D [289 0 R /XYZ null 562 null] >> endobj 301 0 obj << /D [289 0 R /XYZ null 550 null] >> endobj 302 0 obj << /D [289 0 R /XYZ null 538 null] >> endobj 303 0 obj << /D [289 0 R /XYZ null 526 null] >> endobj 304 0 obj << /D [289 0 R /XYZ null 513 null] >> endobj 305 0 obj << /D [289 0 R /XYZ null 501 null] >> endobj 306 0 obj << /D [289 0 R /XYZ null 489 null] >> endobj 307 0 obj << /D [289 0 R /XYZ null 477 null] >> endobj 308 0 obj << /D [289 0 R /XYZ null 465 null] >> endobj 309 0 obj << /D [289 0 R /XYZ null 453 null] >> endobj 310 0 obj << /D [289 0 R /XYZ null 441 null] >> endobj 311 0 obj << /D [289 0 R /XYZ null 429 null] >> endobj 312 0 obj << /D [289 0 R /XYZ null 417 null] >> endobj 313 0 obj << /D [289 0 R /XYZ null 405 null] >> endobj 314 0 obj << /D [289 0 R /XYZ null 393 null] >> endobj 315 0 obj << /D [289 0 R /XYZ null 381 null] >> endobj 316 0 obj << /D [289 0 R /XYZ null 369 null] >> endobj 317 0 obj << /D [289 0 R /XYZ null 345 null] >> endobj 318 0 obj << /D [289 0 R /XYZ null 333 null] >> endobj 319 0 obj << /D [289 0 R /XYZ null 321 null] >> endobj 320 0 obj << /D [289 0 R /XYZ null 303 null] >> endobj 321 0 obj << /D [289 0 R /XYZ null 285 null] >> endobj 322 0 obj << /D [289 0 R /XYZ null 273 null] >> endobj 323 0 obj << /D [289 0 R /XYZ null 249 null] >> endobj 324 0 obj << /D [289 0 R /XYZ null 237 null] >> endobj 325 0 obj << /D [289 0 R /XYZ null 225 null] >> endobj 326 0 obj << /D [289 0 R /XYZ null 212 null] >> endobj 327 0 obj << /D [289 0 R /XYZ null 200 null] >> endobj 328 0 obj << /D [289 0 R /XYZ null 188 null] >> endobj 329 0 obj << /D [289 0 R /XYZ null 176 null] >> endobj 330 0 obj << /D [289 0 R /XYZ null 164 null] >> endobj 331 0 obj << /D [289 0 R /XYZ null 152 null] >> endobj 332 0 obj << /D [289 0 R /XYZ null 140 null] >> endobj 333 0 obj << /D [289 0 R /XYZ null 128 null] >> endobj 334 0 obj << /D [289 0 R /XYZ null 116 null] >> endobj 335 0 obj << /D [289 0 R /XYZ null 104 null] >> endobj 336 0 obj << /D [289 0 R /XYZ null 92 null] >> endobj 337 0 obj << /D [289 0 R /XYZ null null null] >> endobj 286 0 obj << /P 239 0 R /R [63 63 549 729] /V 236 0 R /N 338 0 R >> endobj 339 0 obj << /Length 2891 /Filter /FlateDecode >> stream H‰ÔWmoÛFþ ÿa?.ƒ#ËårùRà>¤y9äPE¢ûEѶ®©’”ç×߼쒢$[n.ER°HîììÎÌ3ÏÌü´xþ쇷J(±¸zþ,òD„ðG:TAh„6AžŠÅæù³P\ƒô¿>(qÝÃ[†‰X”ÏŸIz‹ÿ¢¢Ø*ò#¨,Ô"É‚P#¯yCÓŽP,îž?û(×Í ^¾¿xõî·—µ§"Y¿­š²º”—ž÷ëâߨQ[*ÈÂT _‘JòQŸÎHßGùËÎ˃LvžÃÏ–^ÚÞKƒHV?:eîz:HÒ8;I Û ¬ Îa`ÒL;8*T$èãs®¬?y~$./[/ ”,=¸b*÷|p›ì=)ø‘7$R‰²¨ëµýÖзk–[þíàâp~)«¾÷ü íH¸áïúEmÕÔbpÚí¶•‚«õC{¹ÂkoïÐì>ðt±ŽÓ=Ëe¼ÝA`ãàö¨1:Q–Gv y(œ‘¯¤h;ëâ)¦aÍ\¼· 1G.˲DëS¹Ùñ{Mßµ*qx¨0s|¤È¤rËâ@DÃk1¬[VÔÐr‘„”,½A ÞgUpŠÝXy*Ák+0ÜJÚ¥}óù:~ÈÐyN˜ ã|òMh“ñ[+k 2œßzuÅ8Æ ú Ül„ôXÙÏ×=à "ÚаÈ~`r¸;`PöJY tD7¢lo”ŒÊí}òt…I»@ð%€§P≔RžJ`å¼Fpå-þ"öЇ”„ࢃ¯úPîè§¥=M˜“S‚g*ÏÆ«=RCõÛf7TŸªþ’VËv× ò‡‹Å_Í ^~wdF'«²9¯Ð'ø³»®ÑѰ à1òÖƒdÄàƒ‡Q¢êµ aahEÙ¡ù¢÷0Ï+/„½CAW’DÚ+±Cb¤¸2d ¦o acµ´c¶¡€%Š&ZÉ\|?ìJ¦¾Dö®‹A6ßÕÁ¹ÇñüƮ޷@û@ªCy[±¢ÓfLJž`@: o¨¶Å²kí@ë_Jô {)WHˆØçwëeÅkP3™>ALtU]½íÄ›v°âÐÐ\pä_š v&QG*;ßø¼ÆÂÍ_×ÞOÏm»^}—MωªüŠ{ê–Jž«_®éÑÜôh×ôèyÓüU¬l E²ã¯÷¼¶_@é‹rµjªéŠ—V͈ÕÐÇF˜âž@ìØžRkÊеa:âˆ9ŒafAÇy$†5I!t±Àdy86øß&jÏã\z¶ÛÇÉ8<Ç/gJˆ]¾Æn—£üYV7 Xxà$Yóþ+$Ô€Ö=ì±KÀ8yMgt,qx@€Òba·9;4ÛÁžEFIu:‹!0T6ô b•Ø¢ÒnèKÁÊ /D’ÕÜ¥áRU5¢(ÿ gàbëÈ;„f.aˆ;J1è#l„Žãj Àí ´I*hc÷ë…M¯ÀJŽü‡QŒú«øO+¸‹ùü··áïÅ|a|`èæ›ÉÌç+÷‹ ÄJ#ÜÌÔH{¬‚ªkÀo#‹«1HPXeâ›––)·syOçÄLeÍk0½ðûšß!)zX¹fÎ Í%ÃH˜™Êãó“çEgã´Äç ±º¿2'/ï¡Qü.GÐm2Ž 4E¦v5ãšòšº4=@ÝÖqr› [»¡¡±ù¾·´_ ¢,¸ðm­™«Ö¬°a…ȼ%+ox~Íi~MÝÁLåW/•dÆ;·ƒo{o›7ÜVv (èlŸ MQ¬N0qncÖz>Îrg˜‘sæq»gTv¢-ÿJt)ƒš±ÀÐ=Lsñ§€'ºzñK»æ6xàÎ[hÛ‹¢ëŠ{/ɧ¼ü¦(olOÞ²ó!€ qm ÊnžÅÐòŒ]ùˆ7à!›¶»§e¨Ä¶dsAò£ïXu©g…ç&Ò(LüG9úˆgžæÁS3èÂM’Œt ñÏÏh]Á6õjƯe1#ÝPºaîK]>Íse‰iš1Þc9°oò0…£´9B©E;!?ÏíÕßWÃÎCä¥4ù˜â¶à‡zÇ¿@M5VG²#Hfüç bù`æòO @4 sø°++Øšñ˜û ñ‘»úf ‰ßØU¨©ãF ¼Qïl19mƤä ôÆÒV×Úà¸Õ>‰^ÊUÅè*»õÒfÝp†!ölá#ªM;Xq¨P€„ô8yž:¡Låðm‡ ¬*.†âE±ZuUÿ}ÖÀ]Ç+&cîjÛP¸*h¸ WÍqLíVqÕQf£Ó1c)bç%¬Þݬ˼Cü$T‹R¬*‰ YfI?÷ÁTç*0àW 3Bz¦c§XÛòˆæ˜UaF˜€El ùk˜©x¿“ý( &|¹¾,‹¦­ç³(œ*Y4V²- žj’¡šÄ¿P™02v*„Hþoàü•V Ê=`ßA*(ˆH䀥sA)Üä5Ô€²!¡F«FóAU[˜ë_þ¢(ˆâ×Üս䔾3{áëÂËÀÃø× endstream endobj 340 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F3 22 0 R /F4 54 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 342 0 obj << /D [341 0 R /XYZ null 730 null] >> endobj 343 0 obj << /D [341 0 R /XYZ null 712 null] >> endobj 344 0 obj << /D [341 0 R /XYZ null 700 null] >> endobj 345 0 obj << /D [341 0 R /XYZ null 676 null] >> endobj 346 0 obj << /D [341 0 R /XYZ null 664 null] >> endobj 347 0 obj << /D [341 0 R /XYZ null 652 null] >> endobj 348 0 obj << /D [341 0 R /XYZ null 628 null] >> endobj 349 0 obj << /D [341 0 R /XYZ null 616 null] >> endobj 350 0 obj << /D [341 0 R /XYZ null 604 null] >> endobj 351 0 obj << /D [341 0 R /XYZ null 579 null] >> endobj 352 0 obj << /D [341 0 R /XYZ null 557 null] >> endobj 353 0 obj << /D [341 0 R /XYZ null 539 null] >> endobj 354 0 obj << /D [341 0 R /XYZ null 497 null] >> endobj 355 0 obj << /D [341 0 R /XYZ null 479 null] >> endobj 356 0 obj << /D [341 0 R /XYZ null 461 null] >> endobj 357 0 obj << /D [341 0 R /XYZ null 431 null] >> endobj 358 0 obj << /D [341 0 R /XYZ null 413 null] >> endobj 359 0 obj << /D [341 0 R /XYZ null null null] >> endobj 360 0 obj << /D [341 0 R /XYZ null null null] >> endobj 338 0 obj << /P 289 0 R /R [63 63 549 729] /V 286 0 R /N 361 0 R >> endobj 370 0 obj << /Length 3140 /Filter /FlateDecode >> stream H‰ÌWKsÛ8¾»Êÿ—­"¦D>@úæx<‰=ѬËöäoMÑ%q#‘ŠJ6ÿ~ûEQ’ãdö¶¥*~~Ýxóx~öó¯Fõ8??˃?ó­ òÌå*Í‚ÐXõø SE†È|üáûËùÙGÏè<0ðïÃ/Ð~’Ä0¿Ò.ˆ½UU4» ¨¢™ÉÇsÓÒW·.Vª¯ºµ6¡Wk?ƒµ†vоný¯Ç[T*¥L…Î)Ç(ß ec*q>·õL]ÞO¯nþ¼ÒÆŠOÞ“øE‡ü"ägÃxäeÄï£w·Y2¯Ó~ Æ&íô²^uq*\¤˜„̆­©È˜£W Eiâ£9S‘ÿ,D^¥…ßêî­¶ÚOáb…*ÎÀ¸_é_ÉÏdÒëy¬‰¼·D²ëÊŠ™ª',æ^¡í3¯þDzо‡~›í"‘Õ¥öA{o ä^«zÆw€Ù}»_Ÿ¢ç›èø¥ö ßë4pÞôŠ¿a‘TY4ê™å!–jÉ⋬3šÔÍ" ^7½ª÷&"×„Í çb0‹ÅM&¯öp_5äÜO+s–µGYÁ€¸b1öI°­nÑ^1žr£c°A"Ù89ð=F0…Ùs5×Ès"‡h‹ é,ç‡Ø±A…ö༤ìF¤ &Ø%_€„)i–Ù‡¯Fjƒ³#<"R8³8Z+4Zø9XÃx9Q+çœd±LhE¯ °ÀDpx¡!ÉF ¦ŒG4ÊE¡‰öÅÏ_U‰&‘pX¡œº ^ó´ñÅø9MМñPÅ@üSg‚h@Cxzwóçå³6ãmןÊ{Œ©q VrGlÐnLèHtxÒªnH‡’¤Ì!Ã0–7Gë:OU]‡Qƒ Àê1?ÉÀÁ©~ä‡v¬Ã<ûxMеݓW.‹Ný´®¶ÛbQM@¨^•í¬ú¿ÆÄdæ0ÆBÙ¡Ð\µèyƒÁyþ¬ pàˆø‚hf ˜s”ïp(J¬Náu ‡h¥à•UÝSÀ ~B€Ë sB¤WCÈ:ðU¢R(¨'!´¯;CRü¾FQ ºGG¬@ûxÔÞ ÚCýÄ29{Uª(ÉûM©0°Y¤Ëçv1öB"Éj)"{‚2¥Œ˜ÖªîRpBà þä^¿Eè ¥^0p‹ë\`†Îã´x5i³œ’ö¥nÛ~&»ûb‰¥T=QL`˜m=šzF`üª<&‰H}CÀ`’¨û¾<&ƒ=晜Œ’ŒR@•É‚©Æ¯êÆl q«*©…BT±–1âz„›jM5?Ž >£æíjÕêÈÜ|¡zœP½užœ{Ö×¾rËÐÅrSC4}…ntÞ¢"ƒâ‡°œ¢ d–s&y +ÑAS9„Öe§cì';ê*ÖUÓo¹?¸ÐÐ]©Æ=vM’çƒYý1¥>z g¨^x÷È?Ö&'ŒÂõü5wáØjØ¡ÕPSB_Žþ ÑlÚÙðíÜQ--ïdÊBŸj›¹#m“Aݲ•z8û›ÚŽŒþ7mョÙcÖ5D„ÁRÑʆ7x•¶àÍ}”Ø¡öX’ gI Ôy4jfÑì¾âFeŽŒ1hcìZd±)åcûý”;„£ƒ;†ÇP J'‡uñ°³ù˜¬fß¿c °7„8}Ã5² Ô£ôHÑÁØâÕ»-8Vj¾0Ý÷ÄÞj%yöM1Ír¯®(6EÄ{ߥÇÞ;m½éõTýÁ,+ìÞ\õd­Å^"óxC½ÝÕ3¨ýW$$ ™ð•ø«û¡­EY…QÑ•Kn°›2˜ ©ðáwËgulbè|:Qü‘ç1/:>z¹¥9¿àb7"ÀX\ŒµbG ‰Ov4aŠê^ µN^ÒBÉӛтY{¸„<»ÓÆ‘õcïSÝL4•z…9áMõ¾Ø‘OræÆt§DoøÖ]=TSÈ}Lñ[ Þ Øô=Èj­·uÑ4"dŒMi•r¦n¹-A:ˆ¸Ì»cQE ‡Ô+>ÑÉ–þ?ë+Iw@'ŒO4b¸ Í%·0Jذ-º9sk»5ç+ ¡>衜Yt=+ÄÛ+Ó×:¿£îrZ”à®ïqd¦’ï(8îèMóazÁ^sШG¨¤¦„ÄðV‘ üR÷KÅ߀rv”¤ DhÙ®q¶ãĝӄt†Ç¢p@—@k´©4½8ºyKFuØ+¢3 Ð_ŠÍ‹§YÝ\žÙ–‰Òw˜»1ÖÁ¶dn×hL¨RËžFN•‡›Ë©&s\Ñc¬E æDS ‹ ]ÒjJoÿ`ôaN]0ø@"SfÈ…  qBP“bïIµ<‡)ª•ºƒ7&oËÄ%•A”®&ІN-xš®Hf~”ŒuÅt½p¯¡ÕÀš\—èeG`†ûë Û1?y}B‡ðâ /ÃĺÓ'#„{,dÉ{¬—;AßäKJ b÷ˆœˆ<œŽ4à€$‰‡àgvITÜ-þRËðàÝìh½‡¨uØnív|(˜¨›†ÉªZ3æ  ˆÏ÷Äšºª ÏÆù)Hcw)4í–J};ïÕ‘ã}tбÄÇ ‡‹•ºá³ 󇬚óBñu)ŒËeÝWe¿ë*õ°F‘ZÄd¸½€wQ3‘ýK@OHž’;±Ùüñ§×ËÞùläß„‡˜¶¿jƒï©–HºÝšŒC…Bß^¨k.ãÿ´h¶I Ù«ç`eÔÌâ®Djfñù°)ŽÖk.œ |êVR|ç<€q&ê@=jgÐÁTó˜¼a¬9¾¢ªØo·<ݱ˜+©œ&£‘”* ;±˜³× Ù›hG Z “µ(¯«@fËàI‹‰ºßSÝbÙéºzÛ2mC$ÌSGxH«3ZÅÊž…;õ[FÏ@¨Q±÷5è/ûUÑ!f 9f,Ã@ûvê™eZAŒuÐA8¶êR±8BÚð°£5™0{\ëÖàҵΨîÑvÇUQ–•°ÝBLÄìÑ{ÁkÅz] g^ù~A‹Þfxs¬áè ’9Á`. ZòGv„€âÙ’Mâox ‰7Я¡ÞhS[Šy¦¨ŒÑ¼R‘7Ö>xYÍ(ìX’ _¢pè4¹'Mp1‰oò‘ ¸.¥Ö‡b;SÊ!R‡o¸AgííI€DTu/IÃC#¼ö=4Á€ò.åN„µ£³›«ù.65T_õvhY ÙŸ ùÝl9]—²&ªÈ°N¢ÄÏû.ÿÂÜ÷as€Q%’Ù|ÅŠ’´ƒt²&‹ö³pÿǯ–¥„¡ºwÆè’. ThKÙùÀÑ g\—‡R-бuþÞääÄZŒ+¸÷ž$'¹·yȹö­™9½ÐóŒü&ƒ½‡ÉYZÀ$VeÔôûÆò376aPpçþ{Ý̈3÷Ãy³CYžº³qIëFÜgæìŸ,?Nº-á‹Ì¤Š$žå…ÙÝèF™I,Ô6¥¢­JØVi5Æðr:5È!×k€*‚J4îKì͹÷3BB@këµ ŒŽOºhnKJ.Ãx†!<6 œ§ ãu2ÒAU. Ö;¥Ä‰ÚT´mj;¤÷† \<<ÝÛ¬ã…ico’GA[Ób!ô`%½€nã™è{j¶^Â,³æ8u*À‘¸© -ò§(Zß+º±³²D×ÊjCe ÆŸàÕV=—Ï—ªqÜ=ª¼FT°°ô©&'¾ª*éöoA4W6‰¯Ã±Çž\¿!i4ûSyשeàTÚ Ô·KÞµÝzP(µoÌp­[$ñDDNÚ¶6 ó(:%ûäÚ‘ì4ÑÕ¨9é™TRô*šhÿ ¸(ŒÔBØÇg+Wm°ê‡«hݱç¿áÅóåÅ77ŒÂ? endstream endobj 371 0 obj << /ProcSet [/PDF /Text ] /Font << /F1 20 0 R /F2 21 0 R /F3 22 0 R /F4 54 0 R /F5 372 0 R >> /ExtGState << /GS1 23 0 R >> >> endobj 375 0 obj << /ProcSet [/PDF /ImageB ] /ExtGState << /GS1 23 0 R >> >> endobj 77 0 obj << /Type /Pattern /PatternType 1 /Resources 375 0 R /Matrix [0.96 0 0 -0.96 9.45 773.85] /PaintType 1 /TilingType 1 /BBox [0 0 1 1] /XStep 1 /YStep 1 /Length 102 /Filter /FlateDecode >> stream H‰2T0¢t^.}÷`C…ôb^.°.P4•—+ ÄA’B^.C0GD*$çòr9yµ†+XI0éà¬`¤=}JŠJFxº(00üÿ¼\®@å¼\aè› endstream endobj 376 0 obj << /ProcSet [/PDF /ImageB ] /ExtGState << /GS1 23 0 R >> >> endobj 78 0 obj << /Type /Pattern /PatternType 1 /Resources 376 0 R /Matrix [0.96 0 0 -0.96 9.45 773.85] /PaintType 1 /TilingType 1 /BBox [0 0 1 1] /XStep 1 /YStep 1 /Length 99 /Filter /FlateDecode >> stream H‰2T0¢t^.}÷`C…ôb^.°.P4•—+ ÄA’B^.C0GD*$çòr9yµ†+XI0éà¬`¤=}JŠJFxº(C/—+Py /@€Ež7 endstream endobj 377 0 obj << /ProcSet [/PDF /ImageB ] /ExtGState << /GS1 23 0 R >> >> endobj 79 0 obj << /Type /Pattern /PatternType 1 /Resources 377 0 R /Matrix [0.96 0 0 -0.96 9.45 773.85] /PaintType 1 /TilingType 1 /BBox [0 0 1 1] /XStep 1 /YStep 1 /Length 104 /Filter /FlateDecode >> stream H‰2T0¢t^.}÷`C…ôb^.°.P4•—+ ÄA’B^.C0GD*$çòr9yµ†+XI0éà¬`¤=}JŠJFxº(|¨°‘ão?ü—˨<— Àdô› endstream endobj 378 0 obj << /Type /Halftone /HalftoneType 1 /HalftoneName (Default) /Frequency 60 /Angle 45 /SpotFunction /Round >> endobj 23 0 obj << /Type /ExtGState /SA false /OP false /HT /Default >> endobj 20 0 obj << /Type /Font /Subtype /Type1 /Name /F1 /BaseFont /Times-Roman >> endobj 21 0 obj << /Type /Font /Subtype /Type1 /Name /F2 /BaseFont /Helvetica-Bold >> endobj 22 0 obj << /Type /Font /Subtype /Type1 /Name /F3 /BaseFont /Times-Italic >> endobj 54 0 obj << /Type /Font /Subtype /Type1 /Name /F4 /BaseFont /Courier >> endobj 372 0 obj << /Type /Font /Subtype /Type1 /Name /F5 /Encoding 379 0 R /BaseFont /Times-Roman >> endobj 379 0 obj << /Type /Encoding /Differences [ 0/grave/acute/circumflex/tilde/macron/breve/dotaccent/dieresis /ring/cedilla/hungarumlaut/ogonek/caron/dotlessi/fi/fl /Lslash/lslash/Zcaron/zcaron/minus 39/quotesingle 96/grave 130/quotesinglbase /florin/quotedblbase/ellipsis/dagger/daggerdbl/circumflex/perthousand/Scaron /guilsinglleft/OE 145/quoteleft/quoteright/quotedblleft/quotedblright/bullet/endash /emdash/tilde/trademark/scaron/guilsinglright/oe 159/Ydieresis 164/currency 166/brokenbar 168/dieresis/copyright/ordfeminine 172/logicalnot/hyphen/registered/macron /degree/plusminus/twosuperior/threesuperior/acute/mu 183/periodcentered/cedilla /onesuperior/ordmasculine 188/onequarter/onehalf/threequarters 192/Agrave/Aacute/Acircumflex /Atilde/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex /Edieresis/Igrave/Iacute/Icircumflex/Idieresis/Eth/Ntilde/Ograve /Oacute/Ocircumflex/Otilde/Odieresis/multiply/Oslash/Ugrave/Uacute /Ucircumflex/Udieresis/Yacute/Thorn/germandbls/agrave/aacute/acircumflex /atilde/adieresis/aring/ae/ccedilla/egrave/eacute/ecircumflex /edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve /oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute /ucircumflex/udieresis/yacute/thorn/ydieresis ] >> endobj 1 0 obj << /Type /Page /Parent 24 0 R /Resources 19 0 R /Contents 18 0 R /CropBox [0 0 612 791] /B [17 0 R] >> endobj 25 0 obj << /Type /Page /Parent 24 0 R /Resources 36 0 R /Contents 35 0 R /CropBox [0 0 612 791] /B [34 0 R] >> endobj 37 0 obj << /Type /Page /Parent 24 0 R /Resources 53 0 R /Contents 52 0 R /CropBox [0 0 612 791] /B [51 0 R] >> endobj 55 0 obj << /Type /Page /Parent 24 0 R /Resources 76 0 R /Contents 75 0 R /CropBox [0 0 612 791] /B [74 0 R] >> endobj 81 0 obj << /Type /Page /Parent 24 0 R /Resources 102 0 R /Contents 101 0 R /CropBox [0 0 612 791] /B [100 0 R] >> endobj 103 0 obj << /Type /Page /Parent 24 0 R /Resources 135 0 R /Contents 134 0 R /CropBox [0 0 612 791] /B [133 0 R] >> endobj 136 0 obj << /Type /Page /Parent 24 0 R /Resources 186 0 R /Contents 185 0 R /CropBox [0 0 612 791] /B [184 0 R] >> endobj 187 0 obj << /Type /Page /Parent 24 0 R /Resources 238 0 R /Contents 237 0 R /CropBox [0 0 612 791] /B [236 0 R] >> endobj 239 0 obj << /Type /Page /Parent 24 0 R /Resources 288 0 R /Contents 287 0 R /CropBox [0 0 612 791] /B [286 0 R] >> endobj 289 0 obj << /Type /Page /Parent 24 0 R /Resources 340 0 R /Contents 339 0 R /CropBox [0 0 612 791] /B [338 0 R] >> endobj 341 0 obj << /Type /Page /Parent 374 0 R /Resources 371 0 R /Contents 370 0 R /CropBox [0 0 612 791] /B [361 0 R] >> endobj 24 0 obj << /Type /Pages /Kids [1 0 R 25 0 R 37 0 R 55 0 R 81 0 R 103 0 R 136 0 R 187 0 R 239 0 R 289 0 R] /Count 10 /Parent 373 0 R >> endobj 374 0 obj << /Type /Pages /Kids [341 0 R] /Count 1 /Parent 373 0 R >> endobj 373 0 obj << /Type /Pages /Kids [24 0 R 374 0 R ] /Count 11 /MediaBox [0 0 612 792] >> endobj 380 0 obj << /Count 8 /First 362 0 R /Last 362 0 R >> endobj 362 0 obj << /Title (ARMCI: A Portable Aggregate Remote Memory Copy Interface) /Dest [1 0 R /XYZ null 737 null] /Parent 380 0 R /First 363 0 R /Last 369 0 R /Count 7 >> endobj 363 0 obj << /Title (Motivation and Background) /Dest [1 0 R /XYZ null 617 null] /Parent 362 0 R /Next 364 0 R >> endobj 364 0 obj << /Title (ARMCI Data Structures) /Dest [25 0 R /XYZ null 219 null] /Parent 362 0 R /Prev 363 0 R /Next 365 0 R >> endobj 365 0 obj << /Title (ARMCI Operations) /Dest [81 0 R /XYZ null 602 null] /Parent 362 0 R /Prev 364 0 R /Next 369 0 R /First 366 0 R /Last 368 0 R /Count 3 >> endobj 366 0 obj << /Title (Progress and ordering) /Dest [103 0 R /XYZ null 737 null] /Parent 365 0 R /Next 367 0 R >> endobj 367 0 obj << /Title (Memory allocation) /Dest [103 0 R /XYZ null 617 null] /Parent 365 0 R /Prev 366 0 R /Next 368 0 R >> endobj 368 0 obj << /Title (List of Operations) /Dest [103 0 R /XYZ null 406 null] /Parent 365 0 R /Prev 367 0 R >> endobj 369 0 obj << /Title (References) /Dest [341 0 R /XYZ null 587 null] /Parent 362 0 R /Prev 365 0 R >> endobj 361 0 obj << /P 341 0 R /R [63 63 549 729] /V 338 0 R /N 17 0 R >> endobj 17 0 obj << /T 16 0 R /P 1 0 R /R [63 63 549 729] /V 361 0 R /N 34 0 R >> endobj 381 0 obj [ 16 0 R ] endobj 382 0 obj << /Limits [(F) (G1000493)] /Names [(F) 15 0 R (G1000018) 174 0 R (G1000019) 175 0 R (G1000020) 176 0 R (G1000021) 177 0 R (G1000022) 178 0 R (G1000023) 179 0 R (G1000024) 180 0 R (G1000038) 217 0 R (G1000039) 218 0 R (G1000040) 219 0 R (G1000041) 220 0 R (G1000042) 221 0 R (G1000043) 222 0 R (G1000044) 223 0 R (G1000045) 224 0 R (G1000046) 225 0 R (G1000047) 226 0 R (G1000048) 232 0 R (G1000049) 233 0 R (G1000054) 227 0 R (G1000055) 228 0 R (G1000056) 229 0 R (G1000059) 231 0 R (G1000066) 230 0 R (G1000073) 234 0 R (G1000084) 182 0 R (G1000085) 188 0 R (G1000097) 167 0 R (G1000098) 168 0 R (G1000106) 152 0 R (G1000115) 128 0 R (G1000116) 129 0 R (G1000128) 248 0 R (G1000136) 249 0 R (G1000137) 250 0 R (G1000138) 251 0 R (G1000139) 252 0 R (G1000140) 253 0 R (G1000141) 254 0 R (G1000145) 262 0 R (G1000189) 255 0 R (G1000190) 256 0 R (G1000191) 257 0 R (G1000192) 258 0 R (G1000193) 259 0 R (G1000194) 261 0 R (G1000216) 260 0 R (G1000225) 181 0 R (G1000232) 166 0 R (G1000239) 344 0 R (G1000288) 323 0 R (G1000289) 324 0 R (G1000290) 325 0 R (G1000300) 322 0 R (G1000345) 346 0 R (G1000382) 347 0 R (G1000425) 348 0 R (G1000426) 349 0 R (G1000427) 350 0 R (G1000448) 331 0 R (G1000449) 332 0 R (G1000450) 333 0 R (G1000493) 291 0 R] >> endobj 383 0 obj << /Limits [(G1000504) (G1000923)] /Names [(G1000504) 281 0 R (G1000506) 290 0 R (G1000510) 282 0 R (G1000511) 283 0 R (G1000512) 284 0 R (G1000543) 279 0 R (G1000560) 268 0 R (G1000561) 269 0 R (G1000562) 270 0 R (G1000571) 271 0 R (G1000572) 272 0 R (G1000575) 275 0 R (G1000579) 276 0 R (G1000583) 277 0 R (G1000590) 278 0 R (G1000687) 70 0 R (G1000688) 112 0 R (G1000689) 139 0 R (G1000690) 137 0 R (G1000692) 156 0 R (G1000693) 171 0 R (G1000694) 172 0 R (G1000695) 191 0 R (G1000696) 242 0 R (G1000697) 265 0 R (G1000789) 215 0 R (G1000801) 204 0 R (G1000806) 203 0 R (G1000814) 193 0 R (G1000815) 194 0 R (G1000823) 195 0 R (G1000824) 196 0 R (G1000826) 197 0 R (G1000827) 198 0 R (G1000830) 199 0 R (G1000831) 200 0 R (G1000851) 214 0 R (G1000853) 205 0 R (G1000854) 206 0 R (G1000855) 207 0 R (G1000856) 208 0 R (G1000857) 209 0 R (G1000858) 210 0 R (G1000859) 211 0 R (G1000860) 212 0 R (G1000861) 213 0 R (G1000864) 345 0 R (G1000875) 201 0 R (G1000876) 202 0 R (G1000880) 240 0 R (G1000881) 241 0 R (G1000885) 130 0 R (G1000886) 131 0 R (G1000894) 153 0 R (G1000902) 154 0 R (G1000903) 155 0 R (G1000907) 169 0 R (G1000908) 170 0 R (G1000912) 189 0 R (G1000913) 190 0 R (G1000917) 263 0 R (G1000918) 264 0 R (G1000921) 329 0 R (G1000923) 327 0 R] >> endobj 384 0 obj << /Limits [(G1000924) (G997567)] /Names [(G1000924) 328 0 R (G1000936) 326 0 R (G1000943) 334 0 R (G1000944) 335 0 R (G1000945) 336 0 R (G1000961) 245 0 R (G1000981) 244 0 R (G1000990) 7 0 R (G1001002) 5 0 R (G1001005) 9 0 R (G1001022) 273 0 R (G1001023) 274 0 R (G1001046) 92 0 R (G1001056) 93 0 R (G1001064) 94 0 R (G1001067) 97 0 R (G1001075) 98 0 R (G1001098) 320 0 R (G1001104) 293 0 R (G1001107) 296 0 R (G1001108) 297 0 R (G1001109) 298 0 R (G1001110) 299 0 R (G1001111) 300 0 R (G1001126) 294 0 R (G1001127) 295 0 R (G1001134) 302 0 R (G1001135) 303 0 R (G1001136) 304 0 R (G1001139) 301 0 R (G1001145) 305 0 R (G1001146) 306 0 R (G1001147) 307 0 R (G1001153) 309 0 R (G1001154) 310 0 R (G1001155) 311 0 R (G1001162) 308 0 R (G1001180) 312 0 R (G1001181) 313 0 R (G1001193) 315 0 R (G1001194) 316 0 R (G1001195) 317 0 R (G1001196) 318 0 R (G1001197) 319 0 R (G1001205) 314 0 R (G997335) 3 0 R (G997338) 2 0 R (G997347) 4 0 R (G997358) 12 0 R (G997360) 28 0 R (G997371) 30 0 R (G997372) 31 0 R (G997378) 32 0 R (G997382) 38 0 R (G997383) 39 0 R (G997384) 40 0 R (G997385) 41 0 R (G997386) 42 0 R (G997387) 43 0 R (G997388) 47 0 R (G997390) 56 0 R (G997391) 57 0 R (G997394) 61 0 R (G997567) 59 0 R] >> endobj 385 0 obj << /Limits [(G997568) (G999785)] /Names [(G997568) 60 0 R (G997571) 62 0 R (G997581) 64 0 R (G997603) 157 0 R (G997604) 280 0 R (G997639) 115 0 R (G997658) 158 0 R (G997663) 192 0 R (G997665) 173 0 R (G997672) 140 0 R (G997679) 116 0 R (G997689) 105 0 R (G997698) 292 0 R (G997702) 321 0 R (G997703) 330 0 R (G997704) 107 0 R (G997712) 109 0 R (G997713) 110 0 R (G997714) 113 0 R (G997823) 48 0 R (G997952) 49 0 R (G997963) 8 0 R (G997984) 106 0 R (G997988) 108 0 R (G998097) 6 0 R (G998684) 67 0 R (G998823) 72 0 R (G998824) 90 0 R (G998865) 10 0 R (G998866) 11 0 R (G998964) 58 0 R (G999004) 343 0 R (G999010) 342 0 R (G999023) 65 0 R (G999024) 66 0 R (G999033) 44 0 R (G999039) 63 0 R (G999055) 45 0 R (G999056) 46 0 R (G999098) 91 0 R (G999101) 104 0 R (G999157) 247 0 R (G999215) 95 0 R (G999218) 351 0 R (G999268) 266 0 R (G999271) 246 0 R (G999281) 216 0 R (G999282) 243 0 R (G999296) 267 0 R (G999324) 26 0 R (G999328) 352 0 R (G999329) 29 0 R (G999352) 27 0 R (G999365) 355 0 R (G999412) 353 0 R (G999415) 354 0 R (G999422) 356 0 R (G999437) 357 0 R (G999450) 358 0 R (G999568) 96 0 R (G999780) 68 0 R (G999781) 69 0 R (G999782) 71 0 R (G999785) 82 0 R] >> endobj 386 0 obj << /Limits [(G999786) (P.9)] /Names [(G999786) 83 0 R (G999787) 84 0 R (G999788) 85 0 R (G999789) 86 0 R (G999793) 88 0 R (G999794) 89 0 R (G999798) 87 0 R (G999824) 111 0 R (G999827) 114 0 R (G999851) 149 0 R (G999869) 117 0 R (G999870) 125 0 R (G999871) 126 0 R (G999872) 138 0 R (G999878) 127 0 R (G999883) 118 0 R (G999891) 124 0 R (G999896) 119 0 R (G999897) 120 0 R (G999902) 121 0 R (G999908) 122 0 R (G999914) 123 0 R (G999922) 141 0 R (G999923) 142 0 R (G999924) 143 0 R (G999925) 144 0 R (G999926) 145 0 R (G999927) 146 0 R (G999928) 147 0 R (G999929) 148 0 R (G999935) 150 0 R (G999936) 151 0 R (G999957) 159 0 R (G999965) 160 0 R (G999966) 161 0 R (G999967) 162 0 R (G999977) 163 0 R (G999981) 164 0 R (G999985) 165 0 R (L) 360 0 R (P.1) 14 0 R (P.10) 337 0 R (P.11) 359 0 R (P.2) 33 0 R (P.3) 50 0 R (P.4) 73 0 R (P.5) 99 0 R (P.6) 132 0 R (P.7) 183 0 R (P.8) 235 0 R (P.9) 285 0 R] >> endobj 387 0 obj << /Kids [382 0 R 383 0 R 384 0 R 385 0 R 386 0 R] >> endobj 388 0 obj << /Dests 387 0 R >> endobj 389 0 obj << /Type /Catalog /Pages 373 0 R /Outlines 380 0 R /Threads 381 0 R /Names 388 0 R /OpenAction [1 0 R /XYZ null null null] /PageMode /UseOutlines >> endobj xref 0 390 0000000000 65535 f 0000076306 00000 n 0000000017 00000 n 0000000073 00000 n 0000000128 00000 n 0000000184 00000 n 0000000240 00000 n 0000000296 00000 n 0000000352 00000 n 0000000408 00000 n 0000000464 00000 n 0000000521 00000 n 0000000578 00000 n 0000000635 00000 n 0000000886 00000 n 0000000944 00000 n 0000001002 00000 n 0000079378 00000 n 0000001062 00000 n 0000005791 00000 n 0000074551 00000 n 0000074642 00000 n 0000074736 00000 n 0000074471 00000 n 0000077752 00000 n 0000076434 00000 n 0000005923 00000 n 0000005981 00000 n 0000006039 00000 n 0000006097 00000 n 0000006155 00000 n 0000006213 00000 n 0000006271 00000 n 0000006329 00000 n 0000012352 00000 n 0000006388 00000 n 0000011466 00000 n 0000076563 00000 n 0000011598 00000 n 0000011656 00000 n 0000011714 00000 n 0000011772 00000 n 0000011830 00000 n 0000011888 00000 n 0000011946 00000 n 0000012004 00000 n 0000012062 00000 n 0000012120 00000 n 0000012178 00000 n 0000012236 00000 n 0000012293 00000 n 0000020441 00000 n 0000012431 00000 n 0000019264 00000 n 0000074828 00000 n 0000076692 00000 n 0000019396 00000 n 0000019454 00000 n 0000019512 00000 n 0000019570 00000 n 0000019628 00000 n 0000019686 00000 n 0000019744 00000 n 0000019802 00000 n 0000019860 00000 n 0000019918 00000 n 0000019976 00000 n 0000020034 00000 n 0000020092 00000 n 0000020150 00000 n 0000020208 00000 n 0000020266 00000 n 0000020324 00000 n 0000020382 00000 n 0000031274 00000 n 0000020520 00000 n 0000029969 00000 n 0000073155 00000 n 0000073578 00000 n 0000073997 00000 n 0000030187 00000 n 0000076821 00000 n 0000030229 00000 n 0000030287 00000 n 0000030345 00000 n 0000030403 00000 n 0000030461 00000 n 0000030519 00000 n 0000030577 00000 n 0000030635 00000 n 0000030693 00000 n 0000030751 00000 n 0000030809 00000 n 0000030867 00000 n 0000030925 00000 n 0000030983 00000 n 0000031041 00000 n 0000031099 00000 n 0000031157 00000 n 0000031215 00000 n 0000037918 00000 n 0000031354 00000 n 0000036033 00000 n 0000076953 00000 n 0000036178 00000 n 0000036238 00000 n 0000036298 00000 n 0000036358 00000 n 0000036418 00000 n 0000036478 00000 n 0000036538 00000 n 0000036598 00000 n 0000036658 00000 n 0000036718 00000 n 0000036778 00000 n 0000036838 00000 n 0000036898 00000 n 0000036958 00000 n 0000037018 00000 n 0000037078 00000 n 0000037138 00000 n 0000037198 00000 n 0000037258 00000 n 0000037318 00000 n 0000037378 00000 n 0000037438 00000 n 0000037498 00000 n 0000037558 00000 n 0000037618 00000 n 0000037678 00000 n 0000037738 00000 n 0000037798 00000 n 0000037857 00000 n 0000045516 00000 n 0000037999 00000 n 0000042551 00000 n 0000077086 00000 n 0000042696 00000 n 0000042756 00000 n 0000042816 00000 n 0000042876 00000 n 0000042936 00000 n 0000042996 00000 n 0000043056 00000 n 0000043116 00000 n 0000043176 00000 n 0000043236 00000 n 0000043296 00000 n 0000043356 00000 n 0000043416 00000 n 0000043476 00000 n 0000043536 00000 n 0000043596 00000 n 0000043656 00000 n 0000043716 00000 n 0000043776 00000 n 0000043836 00000 n 0000043896 00000 n 0000043956 00000 n 0000044016 00000 n 0000044076 00000 n 0000044136 00000 n 0000044196 00000 n 0000044256 00000 n 0000044316 00000 n 0000044376 00000 n 0000044436 00000 n 0000044496 00000 n 0000044556 00000 n 0000044616 00000 n 0000044676 00000 n 0000044736 00000 n 0000044796 00000 n 0000044856 00000 n 0000044916 00000 n 0000044976 00000 n 0000045036 00000 n 0000045096 00000 n 0000045156 00000 n 0000045216 00000 n 0000045276 00000 n 0000045336 00000 n 0000045396 00000 n 0000045455 00000 n 0000052522 00000 n 0000045599 00000 n 0000049509 00000 n 0000077219 00000 n 0000049642 00000 n 0000049702 00000 n 0000049762 00000 n 0000049822 00000 n 0000049882 00000 n 0000049942 00000 n 0000050002 00000 n 0000050062 00000 n 0000050122 00000 n 0000050182 00000 n 0000050242 00000 n 0000050302 00000 n 0000050362 00000 n 0000050422 00000 n 0000050482 00000 n 0000050542 00000 n 0000050602 00000 n 0000050662 00000 n 0000050722 00000 n 0000050782 00000 n 0000050842 00000 n 0000050902 00000 n 0000050962 00000 n 0000051022 00000 n 0000051082 00000 n 0000051142 00000 n 0000051202 00000 n 0000051262 00000 n 0000051322 00000 n 0000051382 00000 n 0000051442 00000 n 0000051502 00000 n 0000051562 00000 n 0000051622 00000 n 0000051682 00000 n 0000051742 00000 n 0000051802 00000 n 0000051862 00000 n 0000051922 00000 n 0000051982 00000 n 0000052042 00000 n 0000052102 00000 n 0000052162 00000 n 0000052222 00000 n 0000052282 00000 n 0000052342 00000 n 0000052402 00000 n 0000052461 00000 n 0000058576 00000 n 0000052605 00000 n 0000055683 00000 n 0000077352 00000 n 0000055816 00000 n 0000055876 00000 n 0000055936 00000 n 0000055996 00000 n 0000056056 00000 n 0000056116 00000 n 0000056176 00000 n 0000056236 00000 n 0000056296 00000 n 0000056356 00000 n 0000056416 00000 n 0000056476 00000 n 0000056536 00000 n 0000056596 00000 n 0000056656 00000 n 0000056716 00000 n 0000056776 00000 n 0000056836 00000 n 0000056896 00000 n 0000056956 00000 n 0000057016 00000 n 0000057076 00000 n 0000057136 00000 n 0000057196 00000 n 0000057256 00000 n 0000057316 00000 n 0000057376 00000 n 0000057436 00000 n 0000057496 00000 n 0000057556 00000 n 0000057616 00000 n 0000057676 00000 n 0000057736 00000 n 0000057796 00000 n 0000057856 00000 n 0000057916 00000 n 0000057976 00000 n 0000058036 00000 n 0000058096 00000 n 0000058156 00000 n 0000058216 00000 n 0000058276 00000 n 0000058336 00000 n 0000058396 00000 n 0000058456 00000 n 0000058515 00000 n 0000065276 00000 n 0000058659 00000 n 0000062263 00000 n 0000077485 00000 n 0000062396 00000 n 0000062456 00000 n 0000062516 00000 n 0000062576 00000 n 0000062636 00000 n 0000062696 00000 n 0000062756 00000 n 0000062816 00000 n 0000062876 00000 n 0000062936 00000 n 0000062996 00000 n 0000063056 00000 n 0000063116 00000 n 0000063176 00000 n 0000063236 00000 n 0000063296 00000 n 0000063356 00000 n 0000063416 00000 n 0000063476 00000 n 0000063536 00000 n 0000063596 00000 n 0000063656 00000 n 0000063716 00000 n 0000063776 00000 n 0000063836 00000 n 0000063896 00000 n 0000063956 00000 n 0000064016 00000 n 0000064076 00000 n 0000064136 00000 n 0000064196 00000 n 0000064256 00000 n 0000064316 00000 n 0000064376 00000 n 0000064436 00000 n 0000064496 00000 n 0000064556 00000 n 0000064616 00000 n 0000064676 00000 n 0000064736 00000 n 0000064796 00000 n 0000064856 00000 n 0000064916 00000 n 0000064976 00000 n 0000065036 00000 n 0000065096 00000 n 0000065156 00000 n 0000065215 00000 n 0000069607 00000 n 0000065359 00000 n 0000068332 00000 n 0000077618 00000 n 0000068465 00000 n 0000068525 00000 n 0000068585 00000 n 0000068645 00000 n 0000068705 00000 n 0000068765 00000 n 0000068825 00000 n 0000068885 00000 n 0000068945 00000 n 0000069005 00000 n 0000069065 00000 n 0000069125 00000 n 0000069185 00000 n 0000069245 00000 n 0000069305 00000 n 0000069365 00000 n 0000069425 00000 n 0000069485 00000 n 0000069546 00000 n 0000079296 00000 n 0000078158 00000 n 0000078344 00000 n 0000078473 00000 n 0000078614 00000 n 0000078791 00000 n 0000078918 00000 n 0000079056 00000 n 0000079180 00000 n 0000069690 00000 n 0000072912 00000 n 0000074915 00000 n 0000077988 00000 n 0000077903 00000 n 0000073070 00000 n 0000073493 00000 n 0000073912 00000 n 0000074337 00000 n 0000075026 00000 n 0000078090 00000 n 0000079468 00000 n 0000079499 00000 n 0000080777 00000 n 0000082069 00000 n 0000083309 00000 n 0000084505 00000 n 0000085433 00000 n 0000085509 00000 n 0000085552 00000 n trailer << /Size 390 /Root 389 0 R /Info 13 0 R /ID [] >> startxref 85729 %%EOF ga-5.9.2/doc/armci/armci_overview.pdf000066400000000000000000003561511500715745200175130ustar00rootroot00000000000000%PDF-1.2 %Çì¢ 5 0 obj <> stream xœí=k°&EuÈCäs4bdH ä»w=/DXî²YdÙ•‡¬¼vy,î²îY‚<!j$!AY4ÑðˆF\¨1QáWRQ„J,˜Ä”ùgUˆ‰©JY©ôtŸîéÓsºgæ{Üû}`ÝûõöLO÷éó>§OïÂ…(«?øwëÎÁîA’…AÌò`§ø•—qì¤iWOì\3¸ ¸n †Î[†{®æ£Dò7ü³ug°´y°xZ›¯¨Ï «I^.AÂÒ`óÎÁq¿ùÚAVüK›· Ž{•hÆÁ0Í«f‘ó¦ì>Ht³@¶½I Ÿ=¤j•Y¢ùꪙ–ðè¡âÑHô1P½‡yZ[®ã¢Î4ƒÖkÅ£yÈæšªóé&æWb˜à–=¢qA>}zúaé²ïpÑ—BK,5Šq§šÓëªVž@KÀw˜†Qäò^€ò7ªö/UíD½­ZÍ#`‘¹À Œr oO+ØÈV¤öò—Å Ôf­29Οi0da½ï©ZÛàaÙzsÕbüY>ª}¤øjd‘1T¢‡:€.[G©Åû^®çñ+Þ"^Naƒ~Z ¥äêßNÑ|+¼ z éhx×\ŸrdB5V˜|àEèÂë4DåhºõZ1läÆFóé24{7õ°mG„–RM¾íc«#jXù0CKQÃRË,ôäMÑŽñ‘R±‰#â¡ÎÔX'§²´îäÂllnD°ûøäcáäŠTXÎØ<ØÈÿvXÉ]†)g«ÕÏ4‰9Ãã¼”1«!RœuwÀÒ8)c+‹ ÏÓŒ?ÍYõâyüñÓwUÃeQ¬ËšqB±œéÇrõXò©8“O%¬O-=X\·iqy°xA¥É`ñ,ŽÐƒÅ¥s×Å`ñÌÅ3w O?÷ä“ÏÝså¶í[oص‡?Æ_ÙµãÆ×]_½rÊ)ƒ³OnúÔ·ÿqçàùpÍ«ÿ%?é[G|åÆÇ_÷Òþl¿í­Ï'¯9çꇎ~qßæç¾ó½ÿY>þîg¿cíöC>wâÝ?¹ï™Ç~ôÍ'÷}çãvÒYù#¾û¾ÝÛÖ<û‰®ý…7üôû?â¿8aá¨~éúÞ´pâ1º~ùß_X{ñ“?|ê[ûüòûþõ…ÿ[|à²OìÂøÙ£Wþó®zþÖo.í?‡ýïùgý0:ü /¼ã䣯8ã†ï½ÿéSößrËQ7ŸúãÅ»|ã;¯ßq×Þ¿ÿÁ%ÿýŸDÜ»oë¦ÿ ïÿ»ƒïyáùóîé郷¸æ³?½þ Ï\vùaÿµ&}íGþöWÞyÄžƒö-]üªÁg8s MÎì"ešyL@S3b=€©a¹õgß½ýà7óõ‹>sçº;=ä€u/’²,CNE•¼v÷öâ+¸@IJþ»’â¿.HƒË…D’ÆÛ€GK:ú A»Š›«é$ ¶øH¬‚P…oqÈ‚,Øsåàª03“+Å>Ès‰ß¬ ±å8Š-Çã¦êFRjœ€8íÛÉÖ‰¨%ú8“Ìg¼¸ùÛANèǂ€‘o.Ôç™>ã‡~‡3 ü8®À^QA€,v(€’iXGJ$ΆH³ÁlíiŒZ R§jd㢆5W IXç,žYXÕ¬jX3’äc…Ç©è+I`KÀ’k¥ÖqPšpq¯ éßÖXnDÁ6؈4¥‘>á{0«‘°´ÇFdØ1°E'R ‹1šÇúé\ÃÏAð²IQ8³0,Ê…ÄÃ8ü\›b+ ÕkæšE¡%£ žE ›U±8BxfK6‚¾lÂMp3Uˆ„åÚ 00tJfÁh@0iô|êq¯eY6³rå&à E´xÌM7 U)x¥´`9ÓÂ).i-, ÙÊÒ3áfª ŽMÒR‰K%OÃjBi÷ùð7Ô„~übôúÂ5}iß©wî<þàñéKÿq×w?pès÷~õ™åk¿vÀ÷oÚûÆ¿}dáöìèc;øc® iYfa©§¬Ú=uë„-ð( ®WVÛ_ s;×fçI‘¢]dïMÅàOaVî3ùê;ÁbYýjžÃçhlË¿%ÉBd•ªéŸª y& ðµ]×Â|eë4ðÈÖRóIÝZ'Z•.''ºr™Äõ«yEü¢y†‘Ð^B_=SˆHEO˽F> Äkç‘­µ¦à'9[ ˹’Ü™ß?‰¿ºx­ùj®^ýmà­êÕ Š1{ÖcMŠ¿I„96šëм i Kjò³ç"*xoBc;TŸah¬\¡€\ŒêÛˆœ5çIŒ“™’Gj:›E'³_·ß»a]°L…óÁÔŠrðadìFêÝdð×YºÁ÷|¥œ³ª1ïBô¬ EWèaµü‹Ä,jÁ+è…ÄrµÑ„±zrú$IÄÅâÉ|p¨¨Ðæzx…ÄzõNnB}© DU‰‡Ž´ËVR\ˆ('vLg¹¹æo"Çrï“B4¶-¦1CL-tO”z&¥Ü-¿õ©Kª¦ô÷¥ß|–æ U\Z¬\ˆ9<Ò(ZˆE»Rð£4LÔï­ÂI»À©Ou²ÂxQ6ĘՓ¢•¥ Üp¨_Ë1sS:#ãEcž[+;¤²D÷Îéô7õÒžÓJoÎX#¯´g¯F°L`z3—úÙÄN"ÜZÄô;+— ÐÄl[ø›:(8SXšrM -ÖÃ:å6CÓ'Mð4Í'¥Ky"˜nQ¿OCÃÜo)‹goð$3 /I1á%iM$ò·AxÐ)¨K½(™IxIŽ /)Œ1 ‹ðTgd¼hÌSò¹œxoŽQ}‘ÏMp ,þ¦im÷A*K;O1I¤šlBH[¶h0ýæÜ¼¶“~à4ã—'cØDhýŽã­¦x#·£Õl±4µ(­‘µ­Ò‡¿ˆå§aSe"t³%$ý65û|Û]k©]檌‹Ë”šÆâc4l&>«ÒJ-lX8ðÐ) AåžiUdV.üTŠê+‹A“”yÅÖÁ Ñ›ÁÜÜîh2“9ÁzFu(ŸQ»©ÄqéTb›qº«²pqtŸ×A=l‘Ú‚„—æ°*—&H*´è½ÛñÌL¢VRþ ôM¥y`úÉ1)2Ö®¥5Í"Zò/{«¶>_±«Z· I#±FΑsçºYA#qÚ!Ûf ûÓ6!Ùbè´ãx¯è5èÓÆ”M:L2.G‹Ò%rØØÇt#³=º£#…§¬üQ‡ãÊw*XF‹hê›–ciâN§@_N«I ϵÄk%ÅdŒ;†Ö‚LÓø},ub”/3‹¥‘Y7¥“'y¦²Áäù¹Ââê1REÐÛÔ’¬nµ^¥Âš˜äCQtÞÎP}Û‚ÅgF÷‚;·MOü]h—|YGÌè•?ZÛÕÁï2 JF‚ö¤Ùމ WlÒÚT†Bh–Ž7ÚÐë Æó)c—šL§”ŒªÙdj-£˜m í²h|‰ÜшÓ|Ž•u?d&uJ|&PÓ”íùr• ‹¸ÙÙVÜgT¯…M!m„H ¥®.9–ÂUàÚX.«LV'˜\¥…´*…6®¬åÝ5xJÛ$®µVó>„Z;`mйmœ¨(šë(ë.€¢Â´ÖÈï¯Ú…>ù´[lA}–w;Úù²VDÉÑDíÀõ°‚Y`¡'qÌ84–k\¼QL±P¸¸ qŒß¯ÖzÎ^ñpâúÒM–üû| š7#Àí…Œt °*5ò­hÄ«ö¤¬²HŽ;( ÛÛg,`R%9ªI]’£ˆ±òh1TüdUü‡ájÐÉÆ9S˜Y®Ï{û”!5˨oøÓ©ûâÐíu¦­ßÓÇåÇ  ;­*ÊèHüÂkô† 6BKÛñÂÙ£«MSÕkçNy¹~À9enøÐü€½+­©oºØâ)ÓE³ø[¿C&½‚+9Â>-ò/†iª]‘ZÚqî³Ó~ÎùçŽóû¬‡Õ'/·Eº'˜ªh;C\±âY¥ÊW˜žäÃi—s)¥<†þ”ð%ÅÁ·ùÕ{š»?ïRgT7X{AÖðöXÞwÒ¥0éú"õ–a‰Ï¥.\ñúr2u˜¤Uá\æÜ|ï1×–@–tÕT°rRiekÀ´øóVª.¢½üÄ‘ËÜ?ç¶OI»z « ØÐ½d]BÚSLTšpI®þq%Ð[ðärœÙ\Î_ Ò3;”\à€'ªB­”ÇÝ™ÅAe¹;#Ö£FçѤ,.{E“æË·”‘­ß%0;³¯owpiXæ•gµ@ÂÅ®Òg¬žcd÷(ËyÛU©ˆ`0aùp‰SÖ¬"˜v “‰¥IÒ @Æ:êôNi5’ºB„™–uaE«z• É_²¤¹¸G¾^—X\Uç‡Î7¦w¨ñ¶üÔm¢Ó*nkEð­Ý´“ÿœyBV‡–Uݦ„…Þòn©ðñÛ€Išjs¿$ƱÖQk “Ö›êX©\¢Â ‹> ¢1)í˜kø”‰M‹±–ƒ¾¼‘ê^û´»ÒoÃÆ.ºì™¦8Ù¼ Ç‘aÊec»Y;j³Öá68EGª´—øÊ¨u4Þ!‹aª‹œÜæ¹ÃËCä«ÙGC쬵¿Fæ«Y…ºQÏäùeÔ•]Þ27Òùó¥™ êOùû†Ç™žU- »n Q£[ýò=¤Å5£QèàAiœ1<òŸ&™Augál-š X£Ž×tŽÐ~|²gNK4ûŽm¬ûïž·»¸#v÷L@©ô1÷I¢¶ršú8"‚Î>Œ³>xIÍä#Ù'½DƒÚ³–ý‡ÌH d‚›iÄT«• e “$ç@ )áEn¤}{­gÀ{…1úÄ•Êrjq%ëB±Ìë6«n§…“:• ·d. œbÏÐ+Àù¥Ö/s ”ÓFž^cþ·¥‡­Ð­”óÞ}¦GE&ú›úö–pÞĦƒóœ—•3Ÿsš1Êñœ~³ÂbIO’&O¦Ü&ÞT8â(c㌜Ï.òçd4mÀ)ZÓËâ(3´KÎï¸Rs4rˆß!èœÈÍðVsIK}õ„¯b€â…mt%6ÊwGߌ˜ƒ¬Äœ%kX 3æéaÐZ»ê[ÆÁï›âµC}sçÎUëÍÅ%¦–ý5¶÷É—¾ë]ÌÃTKxé.G×WÆW®ÊìB†¼z¿qÜ%azŠ6‘U1•¸iÖ*‘ƒ`iÿ©Ÿ¯Ì^A4Tee\^EmQÀªs5FHʲÔõ%ŨêQ·„òzYuKzdVnïѦš|en°½WaÒk­9ÔV‘P?L²LŸC¹Síð îWÑh1+Îëóëºu¸ï'{o¡ë*ð‰»7⎪U¯²d8£ó‘¥«oxÌLZ›L™áÔ¡­Òª }. “-Nç-™¯œ Y‘a«ê€£Ã„…©¾XÛ'Ê'sóµ5ÍtÌNq%yÒ$AøÜ§²´/…Ý: ŽîØã©U°±ÝfCþ<Û¢1«ƒ´²ËêçšRüyÓ=r9¡ðŸ æ.®J¸¸Ÿ–‚Pí‚ó‡à»ÀÀï†^ù©­b–RÓ(ê ™$iáà ¢•TüˆÕCñ/ÃP­G¿k¢®ÖD3ƒ-&i:E7²ƒò'ḠbhX«^3V&ALÞÎ ïh9b¶#T½êNm„b™ºêA-½ÂÐDJ}Çc#*fa¥rËÿú |ËÇjBYÙH÷Ì9Hý̬>o7)ïE¯‚•o)üùH?9q³yÛñõ•<d•ð\·`yk¬­6ßmúýPÒ£·]Z+m]“ÃnøN‰·´W؛Ԁ½â®®|42IÊÔª°«U7z¼4Ëî­Fž±É‰¦Q.9AXß:6s(n;=–u@± „ö;·®Ù<8À,.ë<)¯¾ëµg „ŸmÕ¨åý‰“a¯õôÊ£­îdq\6GX´=òWK-ÃG f¿v#íj¡œå¬KM;*¼z©£ëñö Û|/ÈZKï07ÒWlOßJ˯QkñQª˜½¡‘jüBÔŸÆQ0mZ×%šÅŸƒØt§Ã>1c¬|˜¤ÜÎ݉Œ>VcÛþ»}&–5L9)«üS%ÐLLèZxÄï;l¿Œ²G*­®ŽqÜë0ãì •Iúœ9§}‘²}ŽÄâ±4RA¸•=û¿ µãvL ] ×»òÜä*yøÂÞÎ0ˆB¤47îÔK¹Jî¶ÞU*¾1·ºÙµÿvGžÊôìטD!¯ÝGŸÞ÷gq8hg²˜ ¹õÜ™Ÿk)}®½ÛÝo/ì²lõ,I¬®‹«ºÔlýçdÓw'¬î /e|f×OÓ(Iï6k{Ÿ)rÊtGÆp§ºÙÞŒ¬–¢«ýÒ'[ÒSê˜ò¥u¤‹÷¶­^±!™Ü¯OM¬úëjk³¢ÀrFÅÙ9YWÄ›7¢#‹™ìþœ"ÑŽl®W Ñz}÷|déËÊœÆ5nœíäáB)®·ú3ØGž~ñÊ'4½Kfò¤˜?u²ùzøÏEKIÓÏ‹ÅúÕ/€Ü†æ_ˆ‡þ|QˆÐ0F#)iûtÕÒ…xÉ)é hq-:ñ«¾éâÖèÉ/¢1J†*lý%À`Ù»ðHA÷`ø+j)ÏÂÒÂÎË\;ó@¾»°PÍã)ô%<ôçáY‰„_ä“TfžS<ÃD=X^ÁÔWÿÆU?A~­žo¤|E|˜)6À+°? ëSGÃÚ_…± ù5LIã] ¥ÉBZ_ g¤ŸNóÆär¦Œ¶§›ƒßR7ÏŸ¢?q³Ô—™N"ò°øõ-Ž!'EºTØ…*ÛJY¸­c•­Ürï/Cq] ¼†Nˆr ãÀqàŒN[ó•ìdQô()uðœ 2Ås2Ã$áÜ­œ}ýoî¢÷–Uˆo2òlÖx"†±¾‡Uq %+k^Þ¨„—;j1õ£Q«„¸Z''>¤Ó¶&Tj ¨ÊüŽV?ó»å‚ïi}¬5Áå=˜Ò³\nW o¢ê_•oîÐJëf]ÅÒ1Rû¯,ØøªMzuÀ®ý†Ðµ3Õ¥r€‹‹¡¨^å :»¸B uE KŒ©Õi]äÅŒàêʈꖛ “ÃYñÝ[bz§w½â´õ Œù®¯¶ª_¬ˆÒ«Leg6¾ì÷ê¤o9 5#:•ß^ul<§U­#á3Õb%Í´hÿpè8œÚšiÑ?·bEì~[4¶IZ«Ö΂ä«:ì¼$³çÅ<#{8ÚÒõý—a8s:xÔ „NZ;uµUÔ)èTىנËBi$f·T)i+õâ©ÔÞø.MU•F ƒ¼Cf•jl¥L¶^Ù D~t[‹Çj¹ö­Cš!g#ð.u8ÞŽµg}<üe1ÁWcß©þL:¹¼% 3¦Õ_Ì_\¨jè“vÚ‰#½¼ÿöW9>9áʦªZh”¦>dŒ´PÇW&ÍJ.rÙ¢t-´ÀÒÒÇ)r7sªdgÇ#gVaèM ÷g<£ %-9 =rÛY?tÙFëòÁ^OÏäéç}'¿—Ooé5P 4:lؘž«Ño ëޱÝúje¾ytù¡cÛ3áEn‹K;,"âî¯ê(]ê¼íl~è±_-Õqj¢¶\f!> stream xœì½_óuÉUöjzÅ%8qœðK.R@ÕÌœýÿì*—g˜ !„ !d(Ë6b,Ë)#{Lì!ŵU| c_på+ðE*‘o¹rñ ðGðe.\(ûìî}Nïîgõî½þô ‰q%ÌáÝ¿µW¯^½Öó¬îÕûݧË+ÍÓåöÿüÿþõwž¿û¼/Om?=½³þ×4·—§¯=†ëþ¿oO|íùWžáéëÏWO¿ô)ÿßü{‹”Æý·ÿ_¿þÎÓo=õ§žš§·Þ~¾½îåÛtÓüÊõ©ë_žÞzçù?û‰·¾ú|¼>-ozëËÏüCëÏöéåvýùÂíçuZ~ºþ¾õŸû'÷ëÃë¿vOîÙÜ~ÍãSs]~ôös˜ý£ß¿>ÚÜýÀ*èâÿõc™_¿öõUîî‡ÑÿúøúèôÔ¹Ÿ?xûÙ.êvá[Z¯à¯}sýÙ<µWøôÇvOÿW~èîß~hý·ÁÿZ‡Ú´ûÜtúÄí×Ôù_«}_.Íü4¹ÿËí óòò·ßÿÍíw·ýõö«éן?ìØ„¼™Ñ á/­Oo¶q¿šm.ÿÛuÛd_sûÔ99ÿÝ*wxz¹¿<æ}ØÆð—ýÃî×ûÕ/Ï.zÜ~ÿÈúÖéilQÝ]Ô{£»_/mƒÏýñCݹÂ_Yÿxðô?ø_›K¹Ñ÷Ët®?ÿGÿ§þEe'éÇü߆#ZTnB«¶›'ÿ°÷‹ å×Ãe'hÚ©ûëÓ*vxš‚‰^ÔíwÚÓ«§¿î¦£Ù å¦ü¼þþŸ¼W7H¬{¸ß e‹†y½+ÿ?ok'xɼ…‰Ùo÷C0Îe• \õÚ¾NÄÐ>ùÙwÞ°(ßfδ-•Õ,?óÖó_\þß»Ïûy1ô|–°zûÏ¡k—€·ÄÒ¾~¬©DÖf±ót½¾2®õ§ýlnºe[5½³Ãk”èý4ýÍõ_Çeןcý¹M…{tûõ©MPã¬ð «U–·ÁŸ.y£mjøŸ?å†×â—¶xâ×ßç½£yaoøŸÛ»>ï5ñ??å_}yˆ¾ÿz}ç²oìþí³~èß~Ê»¯ûõ¦ÿ·M_öáÕuê/ƒí üûBΨ?lýókß^õ»«¬åáÑýþ7k¼¼õoû…‰vÏ~½ÝŒŽí´Óêaó7ï®e½é§€øë½^{?úÜÎìÐú»~ænÊÒ!-+Þÿí—·!øßoîÞf7‚—ÛvI™Íà¬û¯lÑï}ÐÚœôk[XpÿÝÕ"ÛÿÙö°ÿׯø‡/ÁŸÞËW£©Xÿy‰LΛ~ÓD¿‚¿±Iö£úú.–º_›3ý“ÕZ[þü-ðd¿ë>æ½ì_³¾µo¶¼iü÷w¯yg/êË» ~kU{+Œ?Īs'!ÙP¶÷²]´º¿æ“‘ó~Á{ën9?,üYÿÏ„‹îÃÈë~q<¼óºE±ðÕËÌo‘äoû¬êÂ8¹DƒfBáF­Çƾ¯Ø¾ FrÑâ&ƒÎúÏãœDzÿ{ûçK°Ús¹ß'œÀ “À~r÷*__è6“~Úû Ÿ€×VIÛ[~f· ~vý·m±ýÜn…¬1¾ÝÞñ«k¨éÆe¤ÍèÑïlq}÷¶?xXbÚÙŸ¤8pÒ³99pÅ·}0ô?¿ècíÞSõS¿[«ÍΗ·úéùå]¤ŒÎ%×|¸æ>‚PÆÛ½ëñÇû!}œa—- X²eK/÷W¶1øß`†“E°½õKë³cæüïÍ—àWG„–x·GO4”ádìóA{˜Š&…3üº7×ζÝ=ºW7Û«¿¸‹)oùH'2"Ε¼j7“×»Ï*Ž G©,šã8ÝìF¼„ý.~Ó>ûD¹l¼»¦nl\`Øþ{üÚIÇCÞÇ•Øß³~ TAðØ'Å< ë‰x˜Lc÷šHþ!Ã1 ÝÞ 7?FH`Á•®»Šèõr™®Í{o¿i þ*nvÝ+ÓÓÔ_—ÿ¾W77²¾Ìf$NÍq3aâàÆëËæR.XmÿøÛŸ†–°âø6²s6˜"b„p&½®s¸þ²Ø}¼¾2<Íó’êž¾ùÏßþIYA¥ï†Ýlâ˜r-®¡ÄØsa]~ÖÖà×ï#5¸{oÞ7Ñ{_î†iI9—@Ó×< °<šò#c’@ÎãZ 1u¥ŒvéšžF:!Ý@fwQÐþ” ­ý.IÜ·BÎ)ÏÓ9ÐO²Ñ\ÜáxÛ`¨(æßE Û4Çjsü=—Ì1ºÈ$N.νuO®Ý[PºC¿Ï*~/:mè°6.«Üj§îç¯î^û¯n¿®mÊgÕÏ¥®á:ÝÿÊÕ\‰¿S)dë€cf!1Þ,ÈÜG· E¼ œöFM§§™A°ÀVTlÌ£ @°þÿz>Ù%¡¯å ò ÆOûÑà¨Ü¬§¦{ý\xé£ì…‹éž s|¹ëûfƒCXë8¶áŠíçNÛ’Kº>å_L£ó“»ìA”£â|‰ËfËzÛtGH¤`˘öü=J;#{“uÙ²«tª$»þgÊþ„ÑWßîÀ2ÚÌ^r/%Ÿß[î¨cEWk¡¨éæëÓ04·8ÍpyTŠÚëãÆÛI€ÖÿCøé?L—æ‹z÷©éç›Ú ø›§W¦Ûøºy~å²dá^¯·“—¯þR»Œú§¿q;.”ùƒK;½2ÛL‡0 Ýå•…@ø?¸º?Xÿd^OW-³8>u—ˆæëªÉŸ~þê§?ûô¾ù›¿ñüÕ/¬Ï=õgŸÆç¯¾ñ×ß|jž¿úÉWß|ó“Ï_ýé¿þWÿ꫟YLþüÕ7¿ñµß|çëÿp}ö¯ýµçŸþé§ÿå;ýøÝÿô¡gŸxþ3Ÿ~º½nŒßÕ‚w»-bž­ÿãd4OoU{¸¢FJÔí¹rµ‡Ë]í—^{¨}Y_yW½Ÿò}‰êß·þ5üiT>e8ü2Õ†Ñ>e9ü"ÕóÃ_X¬Öð¡(Ãá—©žþ€â oøP”áðËT?¾^胢,‡¯úz½ÐE¿Lõƒáë…>(Êrø ¡¯Ó }P”áðËTÏ¿e†¾n£ÑCIËc¥ƒ_ÝÆþöó?ûžýö„cïÆîž@½®ØÔÝÝÐú½ßþg¿Ž­m¯÷—õòeËc¥o[õ¯ûýß{é_Æck¯;rRš+ž•ðvH½¼‘JMuÉi?«ëø˜ÝñÚ“nY<»ã}v¿Ìn´Šº©§_u¼ú[™rÖúb®ÒoLå÷ÿ(ѹ Þ5“Ù´9«ïìý0ÂõB¡xí>–î·rº_'Ú»‹u"Áõj ÝçFÁî×þ!¯%í~íKu¿ö›îï}å{ÿþÃÏ^Çéâr!×I¦»\’Rëå±â£»&¯W‹59ÓŠÇ\RK%椬‹«¿Mœ2(ÐŽÁè”Áè”Áè”ÁÔ(ƒ‘yKÀ8zròΡÙ6“Šõš3å ’7ûðôpá×þüÙÓ y† ·»LV[FÍxu9è½ð~"õ_+½×{’ùêKI¹;½ û\ sŸ+½P’è…¯;Q0|Â^Býrí7,ù‡I, Š“ýšhð»T¶)àÐ÷ä»Îæƒ`²oLra)>Š´ÝÐÒ¯:Ž´m"g¸íÏå°Pê@ªW.5Õu"½û¬®ßôb×)Š#ptIe_Ò™ÏùÂ8ªiÄ$GØ1)YeãLzö*›úU§,;Ñ‘?ïÓU¢]²E&Ìñ™‚^׆ôGN}½»ÒA“Sì®È³Óí¹ž ÚÛsW:– ¨¸JFM¨¸šg:&®°žÝf¤vÖœ{…\|ñøû!u"½¾/fý<üIŠ«‚êC+Z±±ûK§7Þ~¡ÃúY_ u |¢¿Ìj>HmèHÈÎG ~×É–}£ßld/áÉM&â±viÊÛem4³zßÒ!¯`v»@¥œ2Ñ™¦Ã'ëQt å͵&~ ‡¾¥1±wê[­î:rõ25?uÕ2…9ï2Y‚üŽºvÖéhÀä= ¼ løàj‹ Z\­ïéð~nÞݦ¿ÞZw{þz˜h 1± tT>ëA¬hD«ë»éaèªËcÅo›î‡+ÞþpRkëÌeþvPgf6¤GÊ¡$»#åøuÊùÆŸ(‡ïb±v_gæÊKõkIÓóôsùP¦_PÃïÒÉtjøƒí¯¤-Ä¡!GÀ²°/ž«YxèHÒ±p¨û$²EjÛ«îêf…ÕÖ¤éÀv–ÿ†5éNÁŠA¾'X‚—9äÒ/ÈÉŽ€\ŸQ’ŸB9ä$ž«tÞa­\¿7QeåN­ªÏMÄç’Ceze¢ó +¢ø½¡~zè4ÑŠ_©ö{(jVô›(2+qôÚ¨eæ ‘ëÚ“s­ÞÈu•eªd—ŠNTçÖÏLƒxÞ…Òºö›j9Ërfª4yúÑI§´xúM¢ùH÷ fr>Uò‹Œ¥çQeì(ÕO–­RýdÙ*ÕO–­Rý2ÙŠxú Íд϶ˤû†¦YgP­? ,FµÛ~—\Ÿñ}{HçmT­l;ojkÂIé×Ê28µó¦å÷~çM>¯­*ëõ§½5ô¢i/o>iÚ*èWUÔf<ýfÝÛ]tsl§Ê|ûN‰ùn;jzÑP‚µó¨7Ÿ£ò|^u1S'ãÎ鞌;'gpz îœjIÃŽ÷õLAôèÍn/«XSû³jvSªôƒF ”'C©~4Œàé×颺A Ê(`P©=§ûÕŠ™íW+ÜwaÞ×Í—@ÒHIZ+û|ßBþÊ—rmQ3ù6N‰ÎõØŸ7±-Ô+ÙŠfË#¶¢¥úmWé?gKïAºI-¥Eèdn+ZeI”Ê?Òã›°Tt 7»'Ò:Gz’Í_¡]ˆÍ_µhà6¹Ñ Þüå×xóWAŸQìãÍ_…hé6õ¢‘ÛüÕYÌ6]H-uŽFÅ[À –Tâ|¼¬—‡Ü°ÞÌ»-`•™O¶€õ´t[ÀÚZº&8E-;=-©¹+ÝT§s¯ðª`ËxKZa=Ï4NçoIsõêBAdœÑÁÉv°Þ\ÏtclsÛÁÊØÆ_ÿ£Éýv°šýv°¢~²ÌEl+ê'Ë\Äv°P¿d;ØdEÛÁZQÉo‹q®ßã\¿¬ úÛÁòlâ·ƒõÖ„ÛÖ[n;XO¿V–=‰í`5¿wÛÁ óÚªò`¿¬¡M„ÛÁŠó©ÂCãí`Eý4ªÉv°ž~n;XÍï:%ì·ƒõô¢¡g;Xq>éz©`;XŠI.y4Ö¹%xWOcåjß Uð‡žNì‚ Z=èUY±ß U°Û ÁŠ“ Z=» tblÐ*ê§œ—å¼p[5–wúâË`'·!­¨ß¶Ük¬ï@Þ ò(ÜV-Õ/¸tmm«Æò¤—®áýl½¨´îgó£Rôi †»É1sÝMVôÈu7YÑ#G\îNÒɆ·'Ûªzݺ—­1¯“r®™”sͤ’k¢½lu¤³îe+ZqÝËV̈k;³NF ¢öÚÎlµ£ Su¢ãnçW¹­;¶R?w€é\¥Ž9fN5ð*úðÚÀ«ãÃñŽ­ž¸[=+º[MýdñïØjê'ãxǶΠr;¶j9ÞíØ* K·e«Œ,Ý~©qû¥Š¹î—*zd“É<ìýREýÖýR=¯[÷K5æµUåYn¿TE¯ Ïbï—jΧ.Órû¥šúé2-·_ª¨_§Ê´Ü~©†ßuL+Ú/UÑkPžÏQy>¯ºˆ¥“qµ—ºöYu\Ú·ºVì3€Û>«á}½¬2‰wg5íFW&Ù»³vd(ïÎ*Úm awVS?e0(£€A$»³2Ú-ôóƒÝYö·´£ëž±$³ëž‰×™\÷ŒßUîwñgMu>+Hè~¶¿5:’Qú€OÔߪ3‚¸¿K•~dP*5Õu"—–ô#ƒ6^HíJ¿þ×+¬È¨?•C½FÑ8©A=»]éhPÉÃÍ™ /«ü»qÜ9»S¹òù{ÀÓ@âä^!Y%{qÊߣ÷{qJéÞ¶âäÖ»frk°Uˆ3¡¼N´þüî` ¯'ÝNé²ãdwPcÝRg6 ²ûÜ’v–~ Q=»ÏtŒçˆu¦ó¹}Kû²÷„ÝG¥ë*þ(£Ž ã2jÇÎm÷MÒÝ7‹Lí¾iÅ×mûM ǹ~Gçõâþ¶ÿ¦•Õ·ý7 Ï :OšÙ˜—&;`⌿í€)!‘mL7[:Wð÷ƒÛQenû_jë¸UáOl3¼Âä㑊k½Ëð þ”|λL6ãÄ ¿¥‹ÜÜÇ#•Ñûx$Û†](¨ßÝvä‘ÈïþèF¢žN@'÷¦®º‘¨§s Y4?‘~ÚRY™œ#ØoQ‹ŒƒFÖû-p9ší·ù|~¿åzú“°žù_®¤‘’´ò½ÿô¡gŸ *nϾ®|în—]ݶ¤þËc¥ú/núöÅoüçg¯8{¢F¨ÿØéo=Rƒg²|d|l‚üÜ_þÞ³O|(çzensàzÌoz׃ŸØ²s=Ù-ÇÀõ@C×ÞžºœI+ד_Ò|˜÷5óp‰|Šº=W:øÛ³Ûèû—^‹½/~™êùáÌh0|(Êpøeª Ÿ[•ÇÂpø )`¦|0|(Êpøeª Ÿû)©ÚÛF%•£½ñö~î£ÿ6 ÛA¯èÚ[_'Øïp;¦B©éžæš"Ut;nËJúR{ýfîw“Êr°z¥ç¶œÄ;“Pþç?;Úêʼn}Šwó ¼rÿ~¸÷{ßÍœŸr]tð]êgܾ™ŽÏǽtBë7m`’Ì‚o˃k{7Ê{é[ÿñ…gOÔ>Мy]1ª÷ hµÏóøn'±íòrÕ²ª6n¿ÊTm\—’Šº¶tZèêöEÔu¥Wòéêì5;‘Ó¿<ÿÕRRÕïA™®¤›ðr”ƒ7„@õå®Yë VaFS-{ -»@ÍV³+ö‘åÑ F~û‡ãKxÌ$3§'Ý ½t#¾ç|jßs>=»ÑŸÃùôìÖÊrÁù„ú…GÊ;:Îho´*9(á]÷0â]e>vÀ»˜‡>ï‚’Ô äaËõ•zëêê™g‡¤E}¦­p"l P¬'€r}TQhœnIC³®…õ\Ô®­.dÓ#iL ›žÉð¸Ë…œ*wiH—–|L¸SÖ²WÓ2dù#©¥àJ×¢}Vù3Uþ‰É»ˆ$ڞ♢š§Ì™Ì!Ð’ü¬¶­ ¸%MÍ­†–ñ•Ìj±Ë_É,Ô/äÂ#[Ïî®ÅÜ✺Ñ\ys–;¢$Í©ñ%Ãø”òU­6LÏWå6õ|U¯…~“â«zúÍ¢|œ¶Ót@'ï„Ç:ò]‚¢»ËŒ¥ž™ŸÂ¾iu|ªl%äÙaË<Ÿ²C,ÉŒÂ×ñÙáy;`vȶgÄåú8v¨ Ïʹú$ìP8oM|ˆ Ë;{­b å@j)Ý'!Ø!Û";Ô³°c‡*NØ¡PËÇõ¤–ü áw¥Q&ä…ô².Ιïf~1»ÿÖ‹ü*ñSwOÏOëSÔO–ßS˜û©Ð!ý¬‡†»v-iE‹0BÆ:ªÅÚkIHÈ‘®""8—pþÎ…gʆsi¡#ϹäÙßs.µ5ï9—Þœ7t|æÍùLÆLý½®V5ï{Î¥œ÷ýÉJ©gÎ ¥ÓÈòXqN/Îõêç*[ œK­K²ã\ÂûbÎuÚi+ÍLšAp‘°c<\íÆ£Õ¢·1Y‹^(¯'ÊÓo gƒÑBè÷ä <ñžœž=ëZ1a]Ê­ºžuéiÙ«i™ìÉ©Ìx(õªàGÄ M=‹ºšÚóî¹ Ú÷\Pmõx.¨•Yf:Q‹ÝP¯p¯–ÎTêxÐ3C™î} ¯#ªsv¸KH!´“ «Ýõç‘vì-x¦äÑ{ôÆÀô. ˜D㤘šÝfçOiêÙ­•å Šýéé×) ¸]‡žw˜Ã+˜iö0LjåÑ;÷yá_ò>…« µ« °$3Þ_ÇûøÍL‚óÑ ·WƵkÈ .¢qÌOÏnŽù õKºò <›®¼ósDð,©Mcž…åIy–ÞÌ;ž¥ç™®;OQ¿IaE'ÝyBýBî7“óqzï%@ó iE£“—Ú~êx—ÐÎ1ïâ®ò¤G¯Še}žŠe“=¡Ç¥l çK»Óï }#Ùÿ2™S‚}©Å7O¿„ñ-¹MÅ;’kÑ´=¹¥Á¦à“ßîZ4¡EƒŠ»{—JÅ í#ð·QXYTÍs‹ÞÍÇP”áÝ·eª _ïæc(ÊrøòSžÓ¬vó1e7üBÕ†ÏÜpEÇe ÃáËëÓUíÒw,Êpøeªç‡?©…>,Êpøeª _-ôaQ–ÃW}£^胢 ‡_¦úÁðõBe9|…Ð7pCß}jKZ+üø¸‰òÅä GÝonß&%^wöÆÕÝon‡°Túø°|Û‘(°|Pºi{r ù×gví¨fßfœm¯¤…oÏ•ûÉt/«~ùc´™ý")3s~‘ôÜ9FûXÒòXùàïöí—¾D~¬»-OÞñbÃjsÏ5ûµÆÕ.¨…539LíºI×¶ä»XÕu·x•-ÛJóî‚€¶vWɼ7csÚ+=㙤sÏ:ßùò·>«W¨ùA,á¢Í4–@°iK¸z'±D£«?‰%Lí‚dêc‰j·ü4$Úqð߈㠇Â\úè¥;—>z)h7xš^ìõ›F/¼€Í¢—Yì¸d1‰^P’YôbëG/¬¶0zqµëÆ(zAA:7AÅ1„«sB_  1}ÎI_·5£/ef>X´ÜGºha…ÃlÑrõN­Æ‘ñdÑ2µK­ÅQ^bÑÊ-ê­®E]â×Ðn4˜o¿òåóíÒ¾á|ÁµkIKœ®¯ —ÖÙ¸¿>‚Aדã¸=W –g·Pøù{ ÁðÚÑ^×›nyôþI¡.ù¤PzÊ«-»¼‡^(É,ô²õŽC/V[z¹Ú%¡ ² ½ u¡WÙ¢.ôjh7Ì· ½\í†> ½Pögb»ž§óå•iè|°Æ(Øb».Ï•Ûéü>rÅ{ˆ¢m+òØ$ncå­âv™Þù¸Ý°k”ñ¶”d·-†_Çÿh¢ ¸B©ÉªnD6bNC›˜¯]G’Ão¡ 1¿U™çàö%hñ Un_Šããyûfã#¶‡i|dûo±òVñ±LïƒøÈ¬‚vÝÇGxIæ vaßyûãß#ǾyÝù zÖë`×Öë¸õçÔëðåžV^§P}¾p뤉×AIf^w^ï¬×Aq¶^ǵ|êuXy+¯+ÓûÀ븅ŸÔë`åÇÌëÎwvg½—{L½Ž[L½®jå¨Lï¬×3·Æ{–dåu ½s^‡Å™zÛò‰×Êy]¡Þy¯»rÏd%^%™yÝy½³^ÅÙz×ò©×aå­¼®Lï¯ÓbX’×IØÄíΠÈë _ž+ž¹åÙMýôýßû÷~öz¨~´éuWÚ\åmzÝ£Mïí¿ºù6ÌG‰aY=i3i‰^VZt‰˜³e%§Kã¤E—°$³eu^ï첂â—Õ-*¶ÙÇ@ÒLby¬|êaöçFŸ›ÒN…fO–9f¶Ì¹6O—9ö«e^¦÷Á2×â§X’Ý2ðÓ©9V~ßÓ㺧÷¾›ì±4’~ß’_+ö‘澑øÞO„>rßux pèzêËc¥ï\ÝÞùÙŒŽ>†±»$1 Ÿv±ŠaŸJcfôª„šÅ0…jǨV퀒ÌbØy½31 Ó‰acÁ8z/2–ÿ¯yú¦ÃYm ’4üòX9Êzèÿh§ë†–´U¡áÓà ¥™W¶³'Á;»Up•Ø< ®X˜YpåÚ< ®ØÏ­‚k™Þùà:¨õ $³àz^ïLp…Âl‚+Gïƒà EšW‰áÓà ¥™W®³‡µ±™ô¾òÒØ£2öÞkþìé…Pçà±~"=æÌ1í©owîgÒö%aïÁyú"œ'Ò{lDz?ª?ýH/ÆæDñ§{œ¿ûáï=ûćrR²p’ ‰…™eH¶É“ ‰Mn•!Ëô>Èì¦Îè¸'–dvÜ“xð,Õä¶åãc–Xô˜¥Ž}ãc–Ä -ŽY2ì›ÃPH˜†bè}„¡ðöš†`($ÍC‰;ð7 ¥ÑÜñÛ7¤o(}{ø1‚~PA€Ê&Qº ,1Ñ.]rGì+sНL¿ÅÈS>ÆÎÍec¼ñ‡†»­2Я! V8 Öôë”ücè„þq»ü?ÒÎ…¶ d <Yü0IJFfÂHäˆb_`û‰Žç¿áÑW­õ4ËÖSÌÔ.¼µ¯Éñ¢w Ë7-i°3|¨ƒ œÑ’Aø³Ë.^2Ñk•]vQò™L7û‡²xöÂå®Ýãò‚>R´iPÃ*!tÜH±•’¶=Ý¥LâÊ.zz‰eRvÁˆÌ¦ì"Á’IÙ 3+»ð!.»ž`Tv)Ó;_viÙ_b‹Ë.P’YÙ…­w\vÁj Ë.\í’² dSvQ°¨+»([Ô•]4´ æÛ•]æÛ•] ç;¬6¬_/&L¡Pm8*s˜½8-M´¤U5J”'xa—9änïË*nŸ\¥]¢»p}5AÁ¾®š Õš˜ya²Q²åð¼’¾,„# \xÑð’DÂ^éÀxTÇI.%p-.l‹’`ã^ÔÒ3íE^6²qÙˆÂ<Ž¢èWG¶OÛÆ\…DaŒ3ÓVÈî3s]Ãêˆ<θꈂ>ÚÆÕ€‹A:füƒ[tb–;Ø¢ Ó û\……owXaáê•TX0°Tª°Ä%¬u17xÜEþ­¢ªòâª1{FU‘2½ª"ì K“ª¾°Ôª*"¾Yp«Š¨ÞË·UE¸ß¡Kª"ð»j6U¹E}UD×¢¾*¢ Ýh0ß¾*ÂÔ.9ŒÙF9¯sæ0 fs…£÷Q•¦ê…®Ü,Z¿Ò»!ÌLzñÎÅqb~¤!£ÿØq'àî8Ã%FÚ~y¬Øu†ûÈg>òÿüìõg«Lòû¹·2“Õýܨ¦1rí8ì(²Ã54Ñv¸–Cžë=G}{÷åO¸ù&ñ½dó 3Û|cÛ<…™U/±+Ó;3ö%vñ½øP’ݽøøu'ïm¥®ÁC(5!ÈÆDjhóµëHCh—ÜωïçW™ç¤Ú¤û¼}3p ³s½àžK+8Ç@ÎA™FpNä8 œÃžcçØ!o$e"òPžR‡Œù‘œK·»z+‹¼ÀŠa!×Àýî£hnˆIÄf#ˆ)Y Ä´I$ÄdÛ<˜ØæV³Löõj(Éìã„çõÎ%îz7§pô>JÜUoNá ›¸‘L«Ä-qœ4qCÏ1KÜjŸÇ‹Õê˵›§É¡nÏ5׿ 9Tí¹.Ó;Ÿ.jŸ‡’Ì’Ãy½3É ³I½’i–¸È%(Ó(9ˆ'IØs¬’{±&É/V«ä ±y’°0³äÀµyšð:µJezg“Ã0k}ÖK²J ½é䀅™$–Þùä€EZ%ö2ÉË´I2lj“á9FÉ¿Xãä@,V£ä ²yœaVÉmó$9ëÔ(9ê}¸G¹ÒäÏr™%Åã=X˜QrP?ÞƒEÚ%ýã=X¦UrÐ<ÞCxŽYr`Ÿ»L’>xi•µÂì’ƒÖ±bš%ù±–áÊýºI’ $³äp^ïLr€Âl’GïƒäEš%îrÉÊ4J"ÇI’ö«äÀ^¬—G„uÉ/Öˉ;Êîzÿñ󃳟#ýÆ݃äpk¢éžHòIMâ+IR³Ñ›Lj\{§I Ç«¤V¦÷ARÓÚHÇ’ì’šâF:f”ÔÔ7Ò±H»¤¦¿‘ŽeZ%5ÍtÂsÌ’šüærŸÓln.m½F‚côXΕ»êNWž#û¨?Gßí à\~J}¾ŸRÿ=ºrÜ/,$à Â÷¢6p xÂHoh¤ ÜÄ ÈR “ÖA ,É œ×; 0pÀÑû@‘fà€;€8€2ÀÈqp€=Ç pk|§á2Â;µ°T;µÂ¢äÎÜ™ n­šx6†X¿rƒñ 2öÜrfr­á«´%€™<`«>ŽâJzPô{"Gbóð¦–4:÷ÆöŽ<çmÏN¦Àöt.=gûç§©íGÒIY÷ƒO\[õ×d'5½ÞÈÅ[ÓéÍj³tMï>=GC“"¿OïT_oÖÒð‹õj-Ý59ÒHìôš\o×RZ“ë [ kr½]KÅöƒlM¢›¿FIÌuIc±oJ»2×TŒÛFžîj~°í‹^ô˜hx>zLzô˜˜ì%ÁŸMYØY÷E 7IŠ(X˜Y…â" 1íFE”2½óE”QíÀ1”dVD9¯w¦ˆ…ÙQ8zQ H³" w¹" ”iTD9NRDÁžcUDá.Ö¤ˆ‚]Föý5 ψ¿ÀF‹/°i×Õw¤Æ…ûT\Sµ"îhã:‘’NWnh¶šÉrÄuÓo‘1s$þ&Yœ¾áÙŒ¨óðõBv£ƒ#ÃntÂgÙmàùQ£ÑÓkâ®§9½í|’øF\£QZK3w-mŒ(©ÑH|6©k¹‚_¸êzi w~=¹"šßºú ›Gÿé ÚßÕ6äëcdÂ2|xBÃ÷fÒ‰¹¾7±ÁX·ê¸ëvbÒ¢:I«#Ò‘TG°0³êß+ãê1íFÕ‘2½ª#jwP’]uD³ã 3ªŽèwÜA‘vÕƒŽ;(Óª:¢Úq‡=Ǭ:"¾—«Ž¨ÞË¿UGäž‘VG`P±©ŽÈë«#BãŠÓÀ‰†1ä_•ck˜Ô#ØmŽq-w9²k zµ¤¡XzÑaè¤^tZÔ ôI‡Ô ôšH‡(×+Àæ¾x€ä•csŠ©kYo¤ÇIשz›gè z 2o{ïî'9¦QÖ‚àÈ—I'bûJÓöI¥A®pX•µ>Ñ(éì O9½L*Áé5/JÀÂì8½ÚE „Xqz…‹µ‹ $3N^ï §‡Âl8=GïNEšqzîrœÊ4âô"ÇI8=ö+NÏ]¬ §Ç.#äô ž‘pzTL8=Û¸A÷Ì@· ¦û÷#¹@N#BWPp"W:Y`ŒV8V#O*ðgV¼ÍUÞFTв¨ùU »¤sð«zM¤c°çs殃ôܵ;dÀgÀBÖ´cAgúþ¨sJÞëªj^⪠^âªzz\4½U/’¾ =Ý®’|W äYe¤±§Z ºÒ'64ô*a†áaÒ`ÊŸÊöU ¸OªX˜Y‚ïWq‚p,£*D™ÞUµ›­ $»*„æÍVP˜QBÿf+(Ò® ap³”iU…P½Ù {ŽY‚{ËNR…€.#½¼JÕ¹¼"¥úøš ’ÈïHza!|M¨¾|¦<ÕÎÙ˜À-¦ú «QX°b q…Âø^¼£Ê| ÐÑç¼íÙ •ºÀBÁöÏG©’Ûö¸ä  ×DŠí³âzœ™þ· Û·ÒK .JëÑ·PµýH#±Ó¶÷õ•õè.¯P˜Ç^i=ú" ßöà‚ˆQ'ÞûŠŽ¯N:9Öb¸·YÆ…ÆØ@ƒ !Ou$ë9(@\Øè¸‚CgûbŽØN¾–£â—“ÃZ  é CKKIuïAåGȤ”TõÔ2½ó¥¤ž{}Éx $”¤å±â±÷«‚¿ý⿌K2ŽÕ5k˜Æ¯;y%Ѓ wÍ ¡T)ŽkmGàTÁ¸k{r • Æ];‘s\÷%˜öJÚYù†f_t<¯s¦è…Ù9zñš±*:r+:B™FEG‘ã$EGì9VEGvXo¹¸£#âòXyJ»ëýÞ_ mŠ\®ı; à ÏÐä APЙýw0Z:¢œí I)Ó£€éJ/ØY‹Sùt½OùkþìéÚŽêêØÀÑ]¶ç_‚KÒËcåÑòa†ŸÌ€9 %Ñ&Ð6é•Ðühh"Úè2½óºãv¸m i¤$-•è;úüöKôÇ6ºf-™°õŽ8V›{8Öãp®v±]3“ÃÔ9û(umK¾«¤@–èî€}%Ý'ò]åº'O  À?ï+€…Ù|ŽÞÏ¢Àç ð¡L#€/rœàcϱøìð}‰>#‡9`Ï~`¦Ìt›gL¸A|¢ ¥Öx÷™”¡íi/=g{vÐE»ØÜ8l?&+©P‚FÚI‹Ñã³ÙßÚCè´ñ˜ŽÜçvf:½yq´Lg^-ÓˆWŽ–™Å+HË$9"¡e6 ˆ¤eü8Ó2"Ѳ2½h·I9¥e°IÙŒ–‰¯ÊÙh™ê=D-cj÷(§m´Lµ©~á_íB9¼9À;< ÖòDPÙZ£D»pOˆ·ÒðÞ[Ÿ„zš\‡€©§æUyP˜õÔ¿*Ç3êipU”iE=U¯ÊÞcF=¹÷Z»£ÓIW²»Ê~ûùŸÑŸöPNóΛ CC9µ;Wð:5ƒr w®´ÜN¯ÊAIfPŽ­w å°ÚB(ÇÕ® ‘ÃT?~Ò‘†8gQ¨”-Ú¢ùjñ#=ßç¾{`ÅÕ+8値s8*Ä]KAO³C s~rŽ ”©dšf~°‡e°Á–çŠ-¶<»½ò¿øoüAæCþZ‹ó!±ØŒòa™Þù{\5͇ð¸ªY>·×mùP£2ɇܞê$–j›|(·¨Ï‡ ÷¯ÚÂýk7‚!(‘\Éw Å!h¸ wIÿy«gH?fCú9zåʪ]ìÜdseÅ.v‰ã4Íõˆ£¿S7%BIvM‰øu'›C©kŠJMòA#²qÀÚÄ|í:Òœ ÙdX®ÎI†Å‚L2ìy3 ³É°½2,öc« Ë@.ÃB™FVâ8 ÃbãÛfXvL2,ÖÞ*Öé}aÕ¾M %™Ý y^ï\¼ªwà†£÷Q¼ªzà†;€l¼ªxàFâ8(^AãÇ+µ3 X{³x%?ÒÏZßÝÁ’¬âCo:^aa&ñŠ¥w>^a‘VñŠ=€L¼Â2mâ•ÈqÒxEß4^±×l¯íâU¡ÞñJë†n,É.^)îmbaFñJ}o‹´‹Wú{›X¦U¼RÝÛ$Œo¯´ö6 íÍâ•|o³¿rw´’x%™Å«ózgâf¯8zÄ+(Ò,^q‹WP¦Q¼’8ˆWØø¶ñŠ»fÓx…µ·ŠWezçãÕÄÝHâ”d¯Îë‰WP˜M¼âè}¯ H³xÅ@.^A™FñJâ8 ^aãÛÆ+îšMãÖÞ*^•é}¯´êíX’]¼R¬·caFñJ½ÞŽEÚÅ+ýz;–i¯Tëí„ñã•V½ÐÞ,^)ÔÛGµz;”d¯Îë‰WP˜M¼âè}¯ H³xÅ@.^A™FñJâ8 ^aãÛÆ+îšMãÖÞ*^•é}¯ÔêíP’]¼Ò¬·CaFñJ¿ÞEÚÅ+ƒz;”i¯tëíØøÆñJ­Þ޵7‹W õöA­Þ%™Å«ózgâf¯8zÄ+(Ò,^q‹WP¦Q¼’8ˆWØø¶ñŠ»fÓx…µ·ŠWezçãU¯Vo‡’ÌâÕy½3ñ ³‰W½âi¯¸ÈÅ+(Ó(^IÄ+l|ÛxÅ]³i¼ÂÚ[Å«2½âûú×k¯ðõ¯FdÄ넟µÃRM>kǶ||é)töÒÓ°[mT³oܱE Ú¢c‹aß\F«·#ÃÑû(£UÝ‘á ›Ñ*îÈHe´÷aG†ÕÓŒVuG¦Lï|Fë´nÁÄ’¬náëgF¬¶ìö¶vñw¦° ›ïL–(þVS|û—¶e×Ë¿4æÝelmí®’yïÂLîcèœÉüP˜Mæçè}ù±OXe~îr™Ê4ÊüÇ™ß6ó³£I’ù‰ài”ùËô>ÈüÌ}’ùò%~¸?Uœ£æÇ‡|#ãs—™|WÒc¡/-)¨xºˆï0a¨¢tA)$†*Líâo¯`AÂo¯ðç;Æ6ç÷5a‚«OxéuO®>õÒÉUbÔT» •3_PRïGï#PRõwYPRñƒÄq(y0°óE Jª`(Ó;JZî§æãj¤½æ/ÓU s çÚ³ä$ÅèËLQPŒ¾},‘RO¹í×¢ bZ#&®vñÕ²XÉÕ²:;ÜRGç‰\Â%ë*ÞF©â¹·0Ö0[ªÄ0‡[8zà¼ö¬p w9Üeá‰ãÜ‚o‹[ØQ;Á-X{+ÜR¦w·4ÌC n‚lp Wç·œ—“Öv$6ŒkDr}\‰ˆ­OŒœ° äÄÕ9ANP9qµKdƒœØ:(¤'Ý[g+/DN’•Ô›d¹”úâ$ÒßSÈŸšp9ûO³Ù½"Fx:D*P¦Ù§Ùƒ™~šÙtµ·Bez  æ•]Ô8˜åÑ…ø;à]ˆ¿îÑ…\Ÿ™4!]È¿KîÑEC‚]pJ%èBv\’@ìHÇè?Ú]hk…‚»o£D» îx@R#î¸ c}Ò˜*z» Gï#\Sõ +wY\Sñ «Äq@ß¶ÃŽÏ)FªzµLï@/Ì3?SÜW MÅÓ6Ý'í½×þüÙÓ DEe­Ìpun‡rÍäè—ÇJµ^Ý(÷ï}$i†Mb¬æÙBlo›«¶Š´‹±g ¡L««{¶ß8ƪ-ÄÚ›ÅXùÙÂnæž-Œb,dcÙ:GÕo†ØÁÖçFvìKZ+Žùýåóð(æsÆOÆ|bþ-b>Kï|ÌÇ"­b>{™˜eÚÄ|‘ã¤1Ÿ0¾iÌg¯Ù$æÚÅüB½ó1ÿªt. Rª ïºUñ»Ê±ãØÞ©^3iƒr½nu¥]a \+Î]Ï?š¹2Àå†è`èqàè}ðLZåîr9Ê4ÊÇ9ß6pã`šˆ `”Êô>ÈJ§g° “Ó3læ•@@A‚“+vçj2<¶`–ÏÔñŸÔÏ~a–ôÏ`™V@õla|ã  u6€ÐÞ,ÈÏt“ÒÙ,HgÍ;œ.Àwéìp¦q¾Ì,ŸŸL6œ“8sô>ˆÃP¤Yæ ‡¡L£8,q‡±ñmã07¥qko‡Ëô>ˆÃÜ]Î$Ûíº¥q~¹Å*ÃP³8¬¸j9'IVß Å"íâ°þN(–i‡UwB ãÇa­PB{³8¬°:*ݲ‚ÙTD¸:Ç;ªçåÀ~" }fÒ„§ôq5®>i‚’Ìrcütªâ.qô>ÈAP¤Yâ —ƒ L£$qœùryÚ§ ¬ùåÄ-–wœ÷Ío%;|—{ÆëúuËcåSý8Dò“»^ç{š½Þ‡Ø=99ËcÅï¼ÞO\|ûÃበ2ÇrcRšc±îV9¶Lï|Ž´vž¡ ›ËÕ9αçåà« ÏLš‘c¹ú¤9J2˱ŒñÓ9¶Š?ºËÑû ÇB‘f9–;€\Ž…2r¬Äq®»k#HÛ_Ëo¸>.øìG¿¼(ˆlÝ)çñY»’óôcÇu:ûcÝ-³?7Z¦Ùën•ýËô>ÈþZg  £J'|—U¥¾Ì.jîüÎI’õwþ¡H» h°óeZe@〠ˆ¤Ùe@EçÙ2`çÙ2 äØEšñQ(Ë ¨væën–Î\ôÜ3q· ¤Óm“œ–ƒï:{{@ÚëÅšõºœ·|&×aë›ä:ŽÞ¹ϧU®ã —ë L£\'qœ4×Aif¹NÓy|®«ä<>×IŒŸæ:"æ:n¼OsÖÝ*וé}ë´Îµ@A6µ^®Îq­÷¼üÕ ,èôÍ=A p<‹ÕZÅ»»xÔF~-ª%¡þµ¨IâËc(ˆôeí/¬yÄ£yŽ©ÊôˆGÿ“­»'ˆÇà”i…xŽ’f‡xgCîWÈûG’zoÓ<€ç4“n¿€×xYÁkîrðºNàrðZâ8)¼Æ¶×‚×·9p­ê<ÓLN§óÌLø’ÃýXšî×4¾Çý•Œïq¿ÈóÜOämCÜφ îǺ[áþ2½p¿Ö!n(Èæ“Bø]ÂO 1,A n¦ESÔ]å°GÝò»ø<êÖ½‹Ï£nùW=ê®òÕÃI²°Rì Ù`_ÍÀ`_ýìÇfØ× ƒ NøðØWrˆ=žÐöfØWÓy<ö­ä<33…g±/”f†}õÛ7*þªíDö´Ä¾jíXw3ì«Ð¾Ñhµo@A6Káên¨¹j5”¤±¡&[ï`bµ…“«]0¡ €ÉÕ¹,:‘ÞÛ{o÷Þ?IÏ]©±kIiDç#@göbèÎOÎ1 ƒ2•]3†÷ÕÑël,¿½g¼ÞSÑ‹»TtžŒ'ª‹ážªò5;ÞíwÞyá?„×òÛ6ת‘³¤ÌNÁ~Ô<¤\¨w~\¹‡”cø™\ÌÁÖùv‹Á]Òí¸3–´€¢Üñ»PÒHIêN܉ÿøÆÛ?l9¯w"Õkáè}‘ª¶‹p…HÛE$Ž RÕvUçñ©j»ˆÄø"UmÑ4þ‘ª¶‹ÈŒŸ@¤ší"ª¶2+× "©¶‹`ÝM!’Z»ÖÝ ")´‹ôÌó’)D‚’Ì Òy½3 ³H½ i‘¸ÈA$(Ó"I'…HØöVIÕyDªå<33Úä ”f‘4ï!R%ã{ˆ$3~ ‘ãÛ@$UÛ™•k‘DQ'HXwKˆÄ… )Dº[A¤2½ó©ã¶UDߪĂt¾„|§È]oßUò¢T÷ÙZ÷Ç»š‰Öýä—›|Ú¬ÉJÕùªeøí©$©Œ ”Ú“spö«¡¡ÔQÍÚÁæ}{%­­½yï¨Àùõ™¡ØÏM¨Gï*€çÒŠ p£P¦8NJ°í­¨€ªó8*PËyffVÍQ(ÍŒ hßSJÆ÷T@fü˜ Æ·¡ª¶2+׊ ˆ¢NBˆìmH¸p8¥F5¢ezP­Ï]@A:0#xî,!|×i€ófÓM€Ê/­À¢àt‡± eÀÙŒô« V@Btd3“h7K,|G ™Éa >)ÛJ´K)–®í<÷¤Ý ‰Æêh%Oó$OÙZWÉ\ÁÇ‹¯ä0‡âóÇÃý–çwÉ¢f#œaOÈ¢~#ö 3²hÐeZ‘EÕF8l{3²¨ßWËyf&îʒŪpšÆßÈbÕF8™ñ²X³NÕöCfåš‘EÕF8X’EµF8¬»YTh„kµ>NÙܯÏÕ9³n?—“è3KlÓU¹>Ž®²õ‰é*dBW¹:'t ’ÒU®v ]…‚¤tUÁvŽ®*ÛÎÑUùêptU¾:]Õ°ÖhàiŽ®rµKè*dCW3CÓÕ*yÄÑUŽÞtû„]å GW¡L#º*qœ”®bÛ[ÑUUç™èä`FW%ÆOé*NlVtUÓøž®V2¾§«2ãÇt•0¾ ]UµýY¹VtUuºJ CºÊ0 ]ź[ÑÕ2½èªÖçà  #ºÊÔ9¡«ç;˜0]•ë3“&äÐU®> ]…‚lè*·+¡«¸‘LHW¹ Lè*l1”ÒU¹í<]Õµ§«ì>»˜¯â6»œvÙ#°ÂÑG`«¬O5/ ¨¿=MÔ¿0Ï¥M4¸0Ê´¢‰ª`Û›ÑDý lç–J#¢ˆßWÌ.–GïïûIøëfx ê Ê—ÇÊ'éª_üÞúгOdé¤þÅ•VøF'U/ÀYߊNê_,PËöžNª^,@dyK:©v±x¬è¤ÂÅÖw(¡ £Áwé\»™P*l JÅ‹„RAARJÅÕ.¡TP”R)ØÎQ*eÛ9JÅÕ.Þ(ÈætcfèmÃXoÛpô>àØ'¬¸w9®emÛH'ݶÁ¶·Ú¶Quž‰NfüG×yfi;g?2牷“ç1ÚNÒtÏÉ*9gdã§ÛIm'©Ú~ÈD«í$‘ã'ÛIŠ1ÜNb°„rbÝ­(g™Þ”Sëæ(Èæt"Wç9 ®ëñD(h.ö´ùN'ßû PK‰y Æu1¡T—õ•/ h$N(kH§à(kÉ™ßÿ¨bÔ©\~ÃScrøí¼äXT½+=8z±¨ªWzpeQ¯ô8`QU¯ôPuÏ¢ª^é¡ë<3Û­X”Èy…ÇŠEé_IRÉy6¥z% ”fÆ¢ô¯$©e{Ï¢T¯$!Ї%‹R»’„@V,J~%I3+]I‚™°(¶ÎÁ:w4 KR_ç•2Ô¦Qis¯¨”¥w•b‘V¨”=€ *Å2mP©ÈqTJØÞ•ê:ÏŠJ«9ÏÌ ‡¨´Žó8T*tž•RÎcƒJUÇ¡ÒZÎãP©Èø *­”­*Õµý‰€ÒÌsÐ)”®ñ1,¥¹jSˆ[JÒòXñ:Ÿîjÿñ}Ä‹¡w–ÖkQãè}K«¶¨q…¥[Ô$Ž`iÕ5Uçñ°´j‹š®óÌt°´‚¥ª-j„óXÁRýµJγÁRÕ5œ®¬`©~‹Z-Û{XªÚ¢†u7…¥j-jXw3XªÐ¢Ö1Ï8'° R¥}p¦x$ßÕ{w¿9÷ïÿQAE¾Ì¬¢z~>2Ð ³®½ +i]¹ÈAW(ÓºJ'…®ØöVÐUÕyt­å<33ŠC×*Îã¡«ÌybèJ8tÕt]+9‡®ã§Ð§+#èªjû!y¬ «ÈñèŠu·„®\ø–BW¬»t-Óûº2Ï8§Ð 2‚®è]:Ðu|È#W8®âïú­d\Íã ²fê©w5W<áÿ„#è‚8`úŠGÐÝAÈæFж¤µÎŽ ”Ú“ó].5ÕuT³v°Ú+imíOs4Û¡0#š£ßΈçÒŒæ´3B™V4GµÛÞŒæè·3Örž™™q hNÅvF™ó$4§j;£¦ól4§j;£Äø€æÔlgTµý‰‰}ŠOrã“u‚¾ã“ÇIù$¶½ŸTuž‰Nf|R×yfåñI™óÄ|’p#>©é<žOVrÏ'%ÆOù$FEF|RÕöC&òXñI‘ã'|’@1†|’ À>‰u·â“ezçùd£ÕÓ‹™ðI®Î Ÿ„‚Œø$Wé„OBAR>ÉÕ.á“P”O*ØÎñIeÛ9>É^BŸa…˹Èár(Óh›Bâ8é6¶½Õ6…ªóLtr0ã ºÎ3Ó˜Áh›Bæ<ñ6á€xP¤Äã ñ L#ˆ'qœâaÛ[A€xÌ“³âÁ/üšA<Å!,Ìâ©7a‘vO¿AË´‚xš B„íÍ žzƒP5癙ѦâÕk:Oñj6©:Ïñj6‰Œ ^Å!]Û™Ècñ4„ÝM!žVƒ¡»ÄSh™çSˆ%™A¼ózg fñ8z@<(Ò âqƒxP¦Ä“8N ñ°í­ žªó8ˆWËyff´9†xUœÇC<™óÄp#ˆ§é<âUrñ$ÆO!”fñTm?d"Ä9~ñ°î– sRˆ‡u·‚xezç!Þ Ön%™A¼ózg fñ8z@<(Ò âqƒxP¦Ä“8N ñ°í­ žªó8ˆWËyff´9†xUœÇC<™óÄp#ˆ§é<âUrñ$ÆO!”fñTm?d"Ä9~ñ°î– sRˆ‡u·‚xez@<µv (Éâi¶[@aFO¿ÝŠ´ƒxíP¦ÄSm·À¶7ƒxúíµœgfF›ˆW±ÝBæ< Ä«Ún¡é<Ä«Ún!1>€x5Û-Tm?d"ÄSm·Àº›B<µv ¬»ÄSh·èÕÚ- $3ˆw^ï ăÂl GïˆEšA<îrÊ4‚xÇI!¶½ÄSuñj9ÏÌŒ6ǯŠóxˆ'sžâÎcñ4ÇC¼JÎã!žÄø)ăҬ žªí‡L䱂x"ÇO ÖÝâqaN ñ°îV¯LÖn%ÙA<Ív (Ìâé·[@‘vÏ ÝÊ´‚xªíØöfO¿Ý¢–óÌÌhSñ*¶[Èœ'xUÛ-4gƒxUÛ-$Ưf»…ªí‡Lä1ƒxªíXwSˆ§Önu7ƒx íZ»”dñÎëxP˜ Äãè}ñ H3ˆÇ@âA™FOâ8)Äö·‚xªÎã ^-ç™™ÑæâUqñdÎC<ÂyŒ ž¦óxˆWÉy<Ä“?…xPšÄSµý‰Á–|ðNg—‡Ôžœ†®xõt÷Èýiæ#ríD¾ªÀw’ßaA&¾;¯s†@a6Ô€£÷5ÀþgE ¸ÈQ(ÓˆH'¥ØöVÔ@Õy5¨å<33KSƒ*Îã©Ìybj@85ÐtO l§iúˆà÷5}ñûšþþ¾Ÿß'©»’.k0I;:pˆJ+ÜsÑ I8­ 9G'ënÅ!Êô>à܃®cH)IËc墽ÏÛK_¢Óâ½ôN¸>[\Šäí\Òfj×ö r˜mÏÕ®m%Ú¥,H×vž)h7Ã<ýùðàÓé¹`J>Nñ'8\þ¤Ù…ñ'ý8<‹füÉ Ê´âOª=pØöfüI¿®–óÌÌ ]ÀŸ*öÀÉœ'áOU{à4gãOU{àœ'‚Ûa©5„Žh ÿñÔ®’ÿ ìÀyDíªöñ‰ŒŸR»ê}|l0™R»ª}|ezç©]Ã=àžP;(ɌڱõŽ©V[Hí¸Ú%Ô ’R;Û9j§l;GíØÚ$l ‡YB½FÒQJ6Ã"nx^Þœcë“K,È„\2ÆN“K(̆\rô> —Ø;­È%w9r e‘K‰ã¤äÛÞŠ\ª:ÏD§3r©ë<3ŒÈ¥ÌybrI8¹ÔtO.+9'— Γ’ËZCèhˆÈ&—µüg`ÎrYËøŽ\ŠŒŸK±’K>œÉ%gÈe™Þä’ÛZ“’KØZcF.¹z'äw3 É%S»”\Â>7)¹”ÛΓK]ÛyrÉÔ.3ðF™è3ˆ<- »#íi²;IìÔz]ICõÅë¶¿/Û?JÏÛ°¸ki#(d¦C2WµÏžá|ÇdΰU:ƒ?€,Áªv}†ÌqÁªx}†²óx‚Uñú mç™éŒiE°¯Ï ÇŠ`i_ŸQÍy6‚¥}F½!t4Ü⬊×g(ûωžfKñú õY,¥ë3HlE°Ä×g óUé3Ø„$£èpôÎÀT(̦rô>€©P¤Lå S¡L#˜*qœ¦bÛ[ÁTUçq0µ–óÌÌhs S«8‡©2ç‰a*áåM3‚©ÚŸò&DÚÁTõcB¦LUü”7e{3˜ªý)ïzÎ33£ML­ö)o©ó$0µâ§¼ugƒ©[±UœÀÔJCð0UÉ€©P¤Lå S¡L#˜*qœ¦bÛ[ÁTUçq0µ–óÌÌhs S«8‡©2ç‰a*Ძ2‚PjOOÃɇRG5k'×ÅÎgòqÝó~ž¡5P˜ ­áè}@kð\ZÑîr´Ê4¢5ÇIi ¶½­QuGkj9ÏÌÌNÇ´¦ŠóxZ#sž˜ÖÎcDk4ÇÓšJÎãi‚ó¤´¦Ö:ä°iM-ÿØó€ÖÔ2¾£5"ã'´†@P†´† íZƒu·¢5ezÐî9î± $”¤å±rZÓÞçí¥/ÑûF¨z'ô?GvÄÔnqLJ r˜mÏÕ®m%Ú¥ÄL×vž—1µ ä ¼Q&ú Jžæ©¡²µ®’¹ú@9Ì¡/]½C¿®w)¦f»,fD1õÛe±O˜QLƒvY(ÓŠbª¶ËbÛ›QLývÙZÎ33‘BŬØ.+sž„bVm—ÕtžbVm—Up@1«¶Ëjù§˜UÛeUýgÈDO-ã7—;zÚH&~_ù®ÉåzßO†ï{@‹Ç+'‰NÅoœ/|íÏŸ=½pHCÕZj±­Ìh¨BKmÃíÓHh(”dFCÙzÇ4«-¤¡\í Ii¨‚í U¶£¡\íº`ïn ‡Ùúî¾w÷§båÈ Wç„ BA6dð¼Î2…ÙAŽÞdû±ä G¡L#2(qœ” bÛ[‘AUç™èmFug¦3·”9OL ç1"ƒšÎãÉ`%çñdPÁyR2Xk רd°–ÿ ìÀy@kßQAÕàãw럩—f®„¤bÈcFRÙ =!©Øg¬Hj™Þy’závi%$J2#©l½c’ŠÕ’T®v I…‚¤$UÁvޤ*ÛΑTÛ9’ªm»I¢]rü 29þz^ç …Âlè(Gï:Š=ÖŠŽr££P¦•8NJG±í­è¨ªóLt06££ºÎ3Ó9ÚˆŽÊœ'¦£„óÑQMçñt´’óx:ªà<)­5„Žfl:ZËvà< £µŒïè¨jðñt´NðqtTyb:Š!eÃó„ŽbŸ±¢£ezÐQn7fJGa7¦åêÐQÜ+¤£LíR:Љé¨ÜvžŽêÚÎÓQír˜í&Ézéí&r˜}qŒëï1îOÒÞ×Hw-íä ¹ìþU½ÔåüäÐ?Ã{9¶nOq•·uR­ª¨èNÔL絉z CO¶¬²Ý!­©zY‰ÂDZc:„p®ºLŠ7+O!ª^ ¢º¨6 a¸¨®w°³qô²kñ¡ëýÀÐ{_ùÞ¿ÿð³×q¿ÚM$x¢Íp¿ü&’ë̾‰$ÂýX’îçëá~Bmîgkã~,Hˆû5l·â~mÛ­¸Ÿ¯ÝÃ!WÜOh׿´#ôIG)Ð+ØÈY©¤½‘³n@1t¦7 êè½n@±ôÎ3ÂŒ{‚eÚl@‰'Ù€"lo´¥ë<†­X‘²óÌtv¶Ù€:O´E9Í”ªó8¦VËySÓpž„©UBGC2îT5ÿØ3Ï«eºÁDZÇJÁg%2Èm@ÇjJ}#"Ja_"Z¨÷eß]‰‰%™]‰I¼Ž¡¤gÊ÷{4"¦¡M\L­R"©bÃ@Ë^AË]ó©¥´ù7Íáw™4Í1¼"G«Ý ÂÒûˆ$Ö¼A…=€,I¬wƒŠÈqI¬yƒŠ®óx’Xóeç™éÌiE5oP¡œÇŠ$ªß RËy6’hpƒJµ!t4ìâ“Äš7¨èúωžf$Q3øl$±Jðñ$Qy’XõfˆOHbÍ›] õΓÄ+÷øó^X’Õç½zg 6f±9z@l(Ò bsƒØP¦Ä–8N ±±í­ ¶ªó8ˆ]Ëyff´9†ØUœÇCl™óÄ›p#ˆ­é<bWr±œ'…ص†à ¶–ÿ8ˆ]Ëvà<€ØµŒï ¶jðñ»Nðq[ybˆ!ÄæÂÌbcŸ±‚Øezç!öÄl‚J!6”d±ÏëØP˜ Äæè}±¡H3ˆÍ@bC™F[â8)ÄÆ¶·‚تÎã v-ç™™ÑæbWq±eÎClÂyŒ ¶¦óxˆ]Éy<ÄVpžbׂƒØZþã v-ÿØób×2¾ƒØªÁÇCì:ÁÇAlä‰!6†Ä®|<Ä–@žbCÈc±µÚÚ Ÿ1ƒØ mí#³“"…ØP’Ä>¯wbCa6›£÷Ć"Í 6w9ˆ eAl‰ã¤ÛÞ b«:ƒØµœgfF›cˆ]Åy<Ä–9O ± ç1‚ØšÎã!v%çñ[ÁyRˆ]kbkùƒØµüg`Έ]Ëøb«±ë±E'†Øò˜Al.ÌL!6ö+ˆ]¦÷ÄfžCöUšAlÍvG(Ìbë·;B‘vÛ ÝÊ´‚ØªíŽØöf[¿Ý±–óÌÌhS±+¶;Êœ'ØUÛ5gƒØUÛœ@ìªíŽZþã!vÕvGUÿ2ÑÓ b´;Ö >bk¶;bÈc±ÕڱϘAl…vÇA­ÝJ2ƒØçõÎ@l(Ìbsô>€ØP¤Äæ ±¡L#ˆ-qœbcÛ[AlUçq»–óÌÌhs ±«8‡Ø2ç‰!6ác±ËôÎCì^­ÝJ2ƒØçõÎ@l(Ìbsô>€ØP¤Äæ ±¡L#ˆ-qœbcÛ[AlUçq»–óÌÌhs ±«8‡Ø2ç‰!6ác±Ëô>€ØjíŽP’ÄÖlw„ÂŒ ¶~»#i± Ú¡L+ˆ­Úîˆmo±õÛk9ÏÌŒ6»b»£Ìyˆ]µÝQÓy6ˆ]µÝQÁyÄ®Úî¨å?bWmwTõŸ!=Í ¶A»càã!¶f»#†b× >b‹ O ±1ä1ƒØ\˜™Blì3V»LÖî%ÙAlÍvG(Ìbë·;B‘vÛ ÝÊ´‚ØªíŽØöf[¿Ý±–óÌÌhS±+¶;Êœ'ØUÛ5gƒØUÛœ@ìªíŽZþã!vÕvGUÿ2ÑÓ b´;Ö >bk¶;bÈc±ÕڱϘAl…vÇ–yýöuö=Ć’–ÇJǾ<ºýß%;üzýÝáÛÎ~½~¼<¤^[ÒÅ1k¼mþ‹‡_€?oø G€Âl8GçÒŠ#pãP¦G8Nʰí­8‚ªó8ŽPËyff¸<æUœÇs™óÄp#Ž é<ž#TrÏœ'嵆à8‚–ÿ8ŽPËvà<ൌï8‚jðñ¡NðqAybŽ€!Gàâä”#`Ÿ±âezç9BÃcEZÊô> -ÌÖ@Z$;ÒÂm©HH 쨒¦v)iA‚ŒH‹fO1fDZô{бO˜‘ƒžb(ÓŠ´¨öcÛ›‘ýžâZÎ33ãwi©ØS,sž„´Tí)Ötž´Tí)Vp@Zªökù'-U{ŠUýgÈDO3ÒbÐS\'øxÒ¢ÙSŒ!iQë)Æ>cFZzŠ/Ìf”´@If¤…«wBZ  )iáj—(Ȇ´œ×9CZ 0ÒÂÑû€´`Ÿ°"-ÜäH ”iDZ$Ž“’l{+Ò¢ê<Ž´Ôrž™¿IKçñ¤Eæ<1i!œÇˆ´h:'-•œÇ“çIIK­!8Ò¢å?Ž´ÔòŸ8HK-ã;Ò¢|H ’dGZ¸m? i]?RÒÂí}‘Ã<Ñù~p˜–%À¼¥¥m± ªP’YPåëUBo£ ª ·ªµôvAUCoTkéí‚*Wï4¨â¥nTËô>ªÜÍÁùQ]Ú¢*ÜœO”—æ{}é;ýK¯Ñõ%?|ùþà4qË éð¡(Ãá—©~0|.cǬÑpø ¼q䢩tøP”áðËTÏàÆ½tøP”áðËT?¾^胢,‡¯úzî&~Ãi(iy¬˜à·w8ýµÐÛ nï—­÷ Þ%õ¤–ÇŠQF×ûít/6à×µ _w–Üùn;“EqÉjyô©3›:mOâTɪorÏ–¬B©·Ý9%‡bgÚSšrOiîžòÇ?˜àÑàuãEÍSB©-é(‹¯Z:º¶sÿ;’¹–î1<^·ÐÁ¯kÚÜ RÉÇU¸A¢‘–°Ú ìžÆgÝ]§™žË’lÕ=0ÆÜã\+¶j÷¨üð÷ž}âCØ)–8O¾­Ü)By.ÜsåÁ£ “¾% ÕIßÒwÒ/¡$rœ]ùæ_÷Øûûãç_¢ÃàxÑYJ[ÓµªÏ_LíÂc¼QÂã!*ÖêéµÁ·Ö(™ËÛ–ÝC9Îå±âT|}€Ÿ3>èS±¯ï¸;—M(i¤$-§âæ¾þ¾ö-º^}Ã|µ£· 9˜iqõI”d†¸z'ˆ ’"¶UÛǾºCتm1F^½[5Gü:‰Ú!äM&¢ìIŽ‘ žäbdò˜M ¼hRŽ)hc <ÙêÜð¹–í¤—§}²Åj—ƒÖ;fýSz›Òg<®ÎiÆÃSh•ñÊôÎg¼–{÷Y—2¡¤å±òŒwŸ²¯¦¥Ž Å4äÛN­bŸ;¡ ¿ð©«WZd„’슌Øâ"#1 £"£Ü+|l×ò ÚÙzŬS8K˜u²}vWî¤g[­Üù˜¥±ÍRšsZZ}íœ3H<"-xBIfÏ2½r÷êš4çÀ»k´rNPIóI½Nêé‡Ò†ž~™þ‡ÒõáÎIšGt/;šéy.*†’rœhØç#v}œp=;‰g{ËGª³½å#öl_ž¢|„g[§ôS£:ê£lÔqõdÉ!O„ÁË£´LŸÅ܎‰…FíÄ”dvb«wZGƒ’Ìêh\½“dI“ ÛªI2ÁVµJ&l«ÆÉ[•Lä³í“‰îlûdžídK ϶Ֆš‚UÝ–š²U]®SðE—ëÔ|Ñíˆi̶˗µfÛe¹2½²ûI’åð9«,ÇÝôNJfP’]É ¾N^2#FaT2ãZ?9—'´q.OÇÆÉ¹(Êrø ¡o`†¾yŽG%-•~ytû?üè¿x\È“lWÍ3ù:<7àñÒÒc+ÛüxÝ7Èð¼–ÍÉÁ¼2c:˜W$Im^ƒ#+~^Վد·Còå%Ûò—–4l¹~sp2îr+ö-?¯8?Î+~óÿáo)$Ìž™0Sß‚’ì| ¾Nà[ly„o)ê·º–P¿ÐUoH,ïì!Îà6Ž‘4bÖ›Rí&5íBÎsü( 4 =Çü.Ʀ¥µ,Êrø ¡oÔ }P”áðËT?¾^胢,‡¯ú½ÐE¿Lõýð›yl# ô8dåŸÕ\ž½«=~÷¡ö»ÏMŸ¦ù2<½³þçе¯ O_{>ô}ôc}èkÏ¿òü O_î ûKŸòÿqû”æsk„ÿ_¿þÎÓo-ª=5Oo½ýüòJãÆtû¦¿¼r>-ÿÿ·ÞyþãÏ~â­¯>õ§Ü“Ãôôòò¶·¾üüÇß¼ý߇ÛÞ~ýÔíW»˜Öýã[·Ÿcsø ÿðË͸þþåõñÞÿ±ûuyZþàöó‹·ŸÝö°ý©õ©Ûïµé)ìÿñ—¼ÜíáÏ®þá×o¿¦n{øwÖ¿½ÜþÌîa4¾Éÿúë“m¨Ã4lbv{·7ÔB_^hóí÷O¯ÃžüÏUrwÙ ™—¿î×ßo¯½(ížþ»^ÇØ°¡–ýÝÌ¿àµö??•>¼–ên¿>¹j=ÃÁï­ø¹Üàá,YÆÿüùut›_[]Ë;á°½ûC›½–ÿËí÷ ¡N£ê÷Zø‡ðÄOOΣþæúkó¨Õ¤ÓÍJÁx†È7»§EÍÇߎÛÓnê/Ñl¶Ûl‚ùîs»:ö°¹îÏÞ׈›éO¯ÿÚm/B~AzöÈÇ–u󲞧‡é݉ÜÝï{¯úìny"-¯;)‘%æ­ç¿èÂÖ+Ã<—ùƶß'£Y×ߢY3¼2¯ÑìûvÑl¼[ã·ÖAlòõõ×tw‰¯­Ão·uôÞÝcœæ¿¹šîº™î·ü_;YÿÄÿò/úßü:Û^¼þs7F¯j¶WaMúíŸßÙ‡¿o¬?ÛeéL´bðð¿Þ~_Û-îþ£UµmZ)Yþç7üºwÿãõO/Ѩº½^ÜñŽ—}‰ÌéÔú§›Ö#e·—”d¹Ÿ"ý¢Ÿçå¿o~ñ3Q–Ù»êë(Ë<>™rKv›¬}Þÿéþß@4oð>½qûG÷y.·Ð©Ôå} ¶ŠÛkV×mØÝ[a ~{ËÅÍ.ÿ”Åä7|([Bëíçÿá£ýËþ÷g`.x¹écjï~ý»æŠ5wÅ^ß9Œákoøúó7Eüo#ö—›‰%1 e°&;]¼%妙ÚP'z!WŸžöIe{íç}`òA æÍ=ιG1,Ø(Ü}* ¿°KE¿¼Ï{ô»OÄp^îÆEVsƒ¥p…ã͉ÞÛbÝÊî³s—3q.¼%ËaoÑ»X»qÍP9Y\^‚DÁÅw½³~Þ«.ÅH»ØÅ¢`LD…H~‡0k$º‡/¯ÿ8ßÇðóü²2ѦŒ Û[ã5ZgùçÜÊŽ¸i­_Ù]˜ï§6ò=¨%X˘åÞ§ðM«ÚýâN#A.Þ!’EºnŸ'eç,u½ÿ1X\Ãe—[ï1>Š™¿âãÑ"»ëÊ'åa/x?Üe”ƒ™‚D¶΂´ÖÃõ›Ë{£ƒüMÄ„7€Ìˆz 7N€Bþ~É|úЇn²ºÇ’æ’Gd2A©Îkû/Í÷Ùý‚ÿ×]‘(MÀÐJ§²óªÿÔì£Ýf—_½ýÒ'©Ý+×ÛåM¯t+ùðí%m¹U0ûû«ÚÛJüîªö#¶DDò«>Ô4ôŸíL(WLæ"bxCµ=zï^I@üÔ¸\¿U,a¥c¿¼ã|_(Çܪqè.O›ã”F§3T¬w%ˆ—½I¼¬&{ø °Ð%ˆøG¿ìî§·Ù$ÇUl¢ÍWwJ­¿î%îÆ«ó‘0À÷øÝðá%”S;+ÅÂkURû›ògKï¸s‹ÿanè÷Ù ¿›r­)wúýMï«›u\–Ú&ÿïo$ïÉâk>øº_ÿ44Î-r¹a~Ô›ÛÏì÷ûQ»¿ùÐtÓ˜/º.oêIË-èÌÿí¯nÐï<üÆúÏã¶‘'{ü¸‡\ŸóÁ(†?Á²nlD Æ,uÆåûš1€ŽRû pÀf`Äë)Ð "ÌjÈ‚ÚEAñQ®[SËËÝm}G·ˆ´nB=*(KDs’Ò…"â_}eü4âå¬9Þ.î÷ì‰æP˜j!À¸\¥IŸ&* Ò;ÍÅÏÎC䩉^òu¨`QkW‡ÚBJ]·¨ÇÃÖÚÅåÐ÷Åß–ð³°§©gŒÉÙ¶Ý%½~Ó ¬{BeªÄ ýŽJ‰tÖK*RäYlûؘ‹Ï’ìCÜ];0ÛzBŠ»ž+esÙVVñQ¾é1¦SŠQÍǹ~²ûBU6+í˜á ë-¦Üò}DÝ/x•éjlt†u–5á>ùçî8=*¼/)üž}à–xU ·¦÷aè3»1ì¡Züwð¨écp§ÊI¹ ®Û¾d\ùä†i.H&>ïÍí±3û·\0é—YŸƒ>nvÇ7*»(w¿~ný·[tð¾´1릻omÐ üÓ>üµŒ(üÓ  µ±<÷ð·Ö?m£ø0lñáË;³~€39ûGÇÅ¿µ{í¯¯*mÊ`‡âÿk¯ß6ëÐîíæe«âƒúØúß¶¼7äô^Ôn¨}óÜm'ÜÿÞ¿çïn9¢ö`^îÆ?L“É\Ýß\0ØÛñö‘N³)Qéæßï ïÁy4„¾‰½È-?ív‡»°Q}¤ãHí?¢CUñA˜+Vã 4„‘`†ÊuÈï`wq{òЋ qâ‘ÆØMlŽð÷‚ûLjýƒ7S ã­îƒÝ²Ëý¸ûß¹/®‡Ê/wݲTw{î¹ Âúíg±¿«Ñ›Çf–gtl?¦«HsXZz”tu¹J;ìéÆÝ!¶½6'êWv‚qýýþ¢_ÙœW_°8u=Ä;ƒ¡~ñÞînÇç1M–ž:7g뢼¨”ßl>àÓ‚Rû°ôr×^Ƙ¨à* .Ÿæ­s°¦,°?Àý_NT¸ßâì¹£ûbS6”ifh‹‚3 ˈšÅqÿܧÐÇÇ;ó‘óVtïƒWmq€uf!ª´’ê[î˜\Q9Æ=i?§_îúËpo[Ò]UEmeNÇîD†Hõ ZÑÞîûCîë†:Šht…úám݃­˜ì9ËK´:§˜Âv¹_w0–8äÄõÑìáh‹ù…»`=¹£†jÁ¢sŠcþ\÷_8c—À­8›mxörw»Ljº–ÔüúþmRdG¬¶št%]ÊßJÅÆ­R”Û/«õÜC¨µ}Çá;Cf\]åvÌ!óÏ,z±g19Ä•óGlÒ¼:8˜yФ¨å~·ª¦ÖÄftÜ ï빿~ñ¾ýù_ȳ¦u¡8žùŸÈÅÓŠ¬G¯ÄÙ/įNð̯I‚$Á;²ÞÉɼ»'ËýžTÑ63Km–Š—."üÇT#mÕÃYGÈ‹*¹å>½/TŸ¯ ž<8~²à¢v|šaƒ8¸G-Á'—ݙ֤5ƒXçKü‚Îí¤„»ƒÁ|K®1É"Ùì‚c]ì•kÆ;XÇòÂßýjåîâpqÉ]ûñ\ 7BT&Hÿ¢~ÞàâŽx¼eôûðWqqk‰=×ë„Þošu B$u= ˆÇ‡ÙC¥®‡) ù Íÿå¸Z§Íb˯6÷°æIpJÈ=ünÎí.ûŸ«ä{—òö†ýͯmö€Ì}ß EÝò|섨û7ácQS\»WûÁ˜øs9'îx<ü%/êÄñŠ÷´Flºµ¨›ªQØ7“7hw?õ»Þ }»»‚¿½>šãû¾|¦­³á¬xújeµN“KR¢’°<í4Q&áMk$E_%.Pg'ß&Zž$eëÒÅåQŒúÙO›b\'ª½û†-'€d3ÖäšÀê{‘%| [o(¹y×̱!ºA!éϾEÙ¡Ö7)ìЧ•F+ËUäÚm¥:ÿKc§m êŽÌ3ó 4{ ˜—%²qÄŒlÌ/q„’‘³Òµô¸/zäbÓ`jÊÀUŒ*À6Q øEK,$-kWàæÖ„'µú…\p>Â8 _Ú–©´ÌbœÜ&º2„Œ&f*²úµŒyYcŒñP±„C½=¶ËZ_êêÖùùDAêøÄ@„à‹åSlNµƒU¨H¿Èö;Œöª|6¼w°|Áô¥±•ß1˜×™Hõ$5"¿¨@÷Äý[_ÉŽ]ø—ºî‚l³4©bL!o?½vd‡Ÿó®áô†ùÿÛ+o—ÂQë·Áfüš®W¡ÓF›HHhº“_Á_hßPãÇ7~Е/{8žN¾kUà"´|£§1%«™Ö×hlQJ`ÈÕÂölÊ<¦~¼ÁǹþNÿW‹6r+{!itè"ŒƒùiûêcWËÇKiö7ÂíÆ—Zc¢*NΑrg›ÒXÕÜ)G3ƒT"‰mä-2ÈE†zÈÏ=2À‹~Y•B­’.W¾N Rû€—aQj¨ß_QÝ7_©mûÔü?fÆ´Á{ë ¥†‹$6¡»ÔÍ=¾m™?¦jRûÑ3§Åô´rÈhó|ß­CbŠ®”x~¯0 ࣈ~Äwâ8}­¬æK¢€pžAÆÒ+3mžœf<À–…c®{h°óOÙ‘ßAòZ§K£¬œü6SÛ¨ƒkYÿ‚´îel”÷:4?ÂÊU˜UÑ—˜ÝHèB õYzmˬWàHcУÐfù–Peäa´Ì‚(6êIñ@ûC.E­S ‡ñï?£‡endstream endobj 213 0 obj 36388 endobj 4 0 obj <> /Contents 5 0 R >> endobj 211 0 obj <> /Contents 212 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 211 0 R ] /Count 2 >> endobj 1 0 obj <> endobj 10 0 obj <>stream 0 0 0 0 64 70 d1 64 0 0 70 0 0 cm BI /IM true /W 64 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A0ÿÈeŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçÃD†ÿÿÿÿÿÿÄ EI endstream endobj 11 0 obj <>stream 0 0 0 0 57 52 d1 57 0 0 52 0 0 cm BI /IM true /W 57 /H 52 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ Ì3d×2Iè>ŸOßY8¹ ôŸ ýz¿¿_¿ÿÿïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷Aøãÿÿ€€ EI endstream endobj 12 0 obj <>stream 0 0 0 0 49 54 d1 49 0 0 54 0 0 cm BI /IM true /W 49 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡°Ð3È.ŸxOAè†è ú ½ Þ—íé?Þ“ÿ÷¤þ?ÿ|©ëù@edÕ?ÿÿúÛ_þ¶×ý´·ûKm-†Øav!ma¬0°Ád6|@ EI endstream endobj 13 0 obj <>stream 0 0 0 0 47 74 d1 47 0 0 74 0 0 cm BI /IM true /W 47 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ¹@h2@x ðƒ‹š‚¸ Ýá7O·]?w_äÔ57õÿúëôµë k„ d09c8…Áp¸>@¾˜0ðÞû{{÷ûÿœrÇøÿû¯_û^ëµí.Âða.+¬X0²F@Š@@ EI endstream endobj 14 0 obj <>stream 0 0 0 0 37 72 d1 37 0 0 72 0 0 cm BI /IM true /W 37 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¿ÿäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð}ðûá÷ÃäՇ߾|>øx€ EI endstream endobj 15 0 obj <>stream 0 0 0 0 90 70 d1 90 0 0 70 0 0 cm BI /IM true /W 90 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¨¨ OÿÿÈN pÿÿÿ ¢ßëýþ¿ßëýþ¿ßëýþ‰ þßéíþ—þßéíþ—þßéíþ—þßéíþ—þßéíþ—ÿ¤ÿ÷úOÿ¤ÿ÷úOÿ«ÿWú¿õõûõûõûõÆÈ@Ýw]Ö  EI endstream endobj 16 0 obj <>stream 0 0 0 0 51 54 d1 51 0 0 54 0 0 cm BI /IM true /W 51 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ƒ0Ì•á.¸O§ÓèÔCÁ:‹Iú}'éýâ¿ýÿþMWïÿ¿oïØ~Áû#v=ðø>A‚y *ÿåŽXëÇýv¿]¯ipÂðÁ.+¬0°a`ÂÈ8€ EI endstream endobj 17 0 obj <>stream 0 0 0 0 43 52 d1 43 0 0 52 0 0 cm BI /IM true /W 43 /H 52 /BPC 1 /D[1 0] /F/CCF /DP<> ID &äÔRp1ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿þÈ0:ø÷ïÚípÕÉÐÇÞ÷‡&¡•hø€ EI endstream endobj 18 0 obj <>stream 0 0 0 0 46 72 d1 46 0 0 72 0 0 cm BI /IM true /W 46 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID üš­ìœ2öû}¾ßo·ÛíöÈ5··············û{{··û}þýþßÿÿþPåñÿÿÚýv¿]¯iv¼0— %Åa…†°a`Ád¤@ EI endstream endobj 19 0 obj <>stream 0 0 0 0 49 74 d1 49 0 0 74 0 0 cm BI /IM true /W 49 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡°øl^¼ ðƒÓÓÑ oAô›Òo§Òoý&ÿÕ¾½oþŸýoÿúã¿ÿÿÿÿÿÿÿäÕkþ×ÿ÷¯û_­þ×zÿm/öÒívÒÛKa„¶+kk ,0Y Ÿ EI endstream endobj 20 0 obj <>stream 0 0 0 0 49 74 d1 49 0 0 74 0 0 cm BI /IM true /W 49 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ƒ@1"—Á‡ß!„| ßA¾›é¿~›û÷êñ~ÿ~ÿ÷ïò ‡ä8?AÜ'ÐèèAÓÒzO®¯¯¸õûÿׯûÿKÿäÕ:õúÛ_­µÛK´¶ì0Kb°ÂÚà CDà EI endstream endobj 21 0 obj <>stream 0 0 0 0 55 72 d1 55 0 0 72 0 0 cm BI /IM true /W 55 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ /ÿüƒIÿÿÿÿÿÿâTÿ&¨Ì“ƒïßÛÿÛû÷öþýý¿¿oïßÛÿÛöÿöþýý¿¿oïßÛÿÛö?ß÷ß÷ß÷ß÷€€ EI endstream endobj 22 0 obj <>stream 0 0 0 0 14 44 d1 14 0 0 44 0 0 cm BI /IM true /W 14 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¦qÂO^øäÕ~¶¶°ÂÇÿÿÿÿ/ðžŸ|rj¿[õ†  EI endstream endobj 23 0 obj <>stream 0 0 0 0 47 70 d1 47 0 0 70 0 0 cm BI /IM true /W 47 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡M.ýïýïÞÿÞýïÞÿÞýïßïÞýïýïÞýïýïÞÿÞýïÞÿÞýïÜðe÷ï~÷þ÷‹ïà EI endstream endobj 24 0 obj <>stream 0 0 0 0 46 72 d1 46 0 0 72 0 0 cm BI /IM true /W 46 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡‚pÙ! ä’`ÅÃB8A¸AºznŸn½¿¯o“PÓÿÿÿÿéé~aÌ:ñiw]…á¥Ã q\.¸\öƒ:ÿÿÿÿÿÿÿÈ4Çÿÿÿ EI endstream endobj 25 0 obj <>stream 0 0 0 0 57 70 d1 57 0 0 70 0 0 cm BI /IM true /W 57 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID a—þMD' oÿÿÿÿÿÿÿÿÿÿÿþAû‰8>‡ß“‚ïÁ¿íû~ÿûÿï÷ÿÿÿõëÿþ¿^¾—¯¥á+ÈqP¡A@@ EI endstream endobj 26 0 obj <>stream 0 0 0 0 67 70 d1 67 0 0 70 0 0 cm BI /IM true /W 67 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID %!–KA@8päÔBqò 'ðoÃÃ~÷öý¿oßïßÛÿïßÿoÿÿ÷ûÿÿÿÿÿÿÿÿúõÿÿý/ÿô¿ô½~½}/KÒõð—„¼ ¼¼ŠN"(PRR` EI endstream endobj 27 0 obj <>stream 0 0 0 -73 57 0 d1 57 0 0 73 0 -73 cm BI /IM true /W 57 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID *Çÿòj)8l'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö¿]þ½ü4» ÉÐþ½¯axkà ÁŸ œÿÿÿÿÿÿïþ  EI endstream endobj 28 0 obj <>stream 0 0 0 0 36 68 d1 36 0 0 68 0 0 cm BI /IM true /W 36 /H 68 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A˜I«==?ÑA>ŸI¿ÿ¿ÿÈkÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïGÿ&¢“ÿÿÿÿÿÿþ  EI endstream endobj 29 0 obj <>stream 0 0 0 0 55 72 d1 55 0 0 72 0 0 cm BI /IM true /W 55 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID $Ã7ÿ&¢¯ÿÿÿÿÿò~E×Áôý?Aúñ>ð›è7Óõ~ý7ÿWïÿÿzÿÿÿÿÿÿÿþ××ÿö—þ½×kév¼4®J&ˆVÖÖÖRjAd>  EI endstream endobj 30 0 obj <>stream 0 0 0 0 33 79 d1 33 0 0 79 0 0 cm BI /IM true /W 33 /H 79 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3äÕ?ýûýûýûÿÛÿÛÿ߿߿߿߿ý¿ý¿ýûýûýûýûÿÛÿÛÿ߿߿߿߿ý¿ý¾  EI endstream endobj 31 0 obj <>stream 0 0 0 0 80 50 d1 80 0 0 50 0 0 cm BI /IM true /W 80 /H 50 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ Ð†Žž¿§¯ßéë÷úzýþžŠN½^ÿ­êõõ{þ·«×ÕïúÞ¯_W¿ëz½}^ÿ­êõõ{þ·ï^•ïñßµéoýû^–ÿßµéoýħDgÿkÿÀ@ EI endstream endobj 32 0 obj <>stream 0 0 0 0 13 14 d1 13 0 0 14 0 0 cm BI /IM true /W 13 /H 14 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¬ã„œwõ&©ma… EI endstream endobj 33 0 obj <>stream 0 0 0 0 86 52 d1 86 0 0 52 0 0 cm BI /IM true /W 86 /H 52 /BPC 1 /D[1 0] /F/CCF /DP<> ID *Ȩ/ÿÿ“QIÃA8l4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÿÿÿÝÿÿýßê½ÝzýÚöƒ^D=„®N‡tÖÓ[O´Ö )50²¼@¿@@ EI endstream endobj 34 0 obj <>stream 0 0 0 0 40 54 d1 40 0 0 54 0 0 cm BI /IM true /W 40 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ä༊_,ƈw!…pƒpƒÓOÚÿ©5 Ýk `¸KÁ,Á, ´‚Ð-Ð. ¡Ud6XõëòjšÿÚí…Ø0»û ,†' EI endstream endobj 35 0 obj <>stream 0 0 0 0 26 73 d1 26 0 0 73 0 0 cm BI /IM true /W 26 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID ù5Ð_ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ EI endstream endobj 36 0 obj <>stream 0 0 0 0 57 52 d1 57 0 0 52 0 0 cm BI /IM true /W 57 /H 52 /BPC 1 /D[1 0] /F/CCF /DP<> ID *Çÿòj)8l'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö¿]þ½ý¥Ã rt8ëk}…†¤Ô2‚ÁŸ € EI endstream endobj 37 0 obj <>stream 0 0 0 0 55 74 d1 55 0 0 74 0 0 cm BI /IM true /W 55 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A8eÈrø x' ûä0„áúo§öúý¿ñ÷ïÿ DüƒÕááéú~¾Ž‚AéôŸIúôŸÿôŸÿÿþ?ëÿÿûþMWÿÿývÿývÿ]¿µÛ^×a…[èv»]®á‚!”k GÀ@ EI endstream endobj 38 0 obj <>stream 0 0 0 0 49 54 d1 49 0 0 54 0 0 cm BI /IM true /W 49 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ Ð4H.ŸƒÂOD1^‚ è&ôƒz_·¤ÿz_·þ—íÿÿ×ÿýüš¯ÿÿ¥·ÿémþ–ß¶—¥¶» -† lVÂÃXaa‚Èlœ@ EI endstream endobj 39 0 obj <>stream 0 0 0 0 55 50 d1 55 0 0 50 0 0 cm BI /IM true /W 55 /H 50 /BPC 1 /D[1 0] /F/CCF /DP<> ID & \ÌW¿Óý{ý?×½/·×¯íé~ß^·ï¥û}z·×¯íõêß^¿·¥û}zþÞ—íõëq)Ñ¿ÿð EI endstream endobj 40 0 obj <>stream 0 0 0 0 55 75 d1 55 0 0 75 0 0 cm BI /IM true /W 55 /H 75 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A˜fÈ=XA“O>ŸO¯GAœZ?O¤úOÓÿ¤ÿÿ¤ÿÿÿñÿ_ÿÿßòj¿ÿÿë·ÿë·úíý®Úö» .Äúí{^׆¼0^@ñÿÿÿÿÿÿÿò£üÿð EI endstream endobj 41 0 obj <>stream 0 0 0 0 46 54 d1 46 0 0 54 0 0 cm BI /IM true /W 46 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ Ð]>=„ôôCè Þ‚oIô›÷Ò¿}/ï½"F¿ÿãëÿ¹5_ÿòC‚ ãÛÿõÛývýµõÛ]†» cß ,0Y “€€ EI endstream endobj 42 0 obj <>stream 0 0 0 0 39 73 d1 39 0 0 73 0 0 cm BI /IM true /W 39 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID A?ɨ¤á³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýäxþME' Ÿÿÿÿÿ÷!×qþë¿ka‚Øïxk Ô` EI endstream endobj 43 0 obj <>stream 0 0 0 0 26 71 d1 26 0 0 71 0 0 cm BI /IM true /W 26 /H 71 /BPC 1 /D[1 0] /F/CCF /DP<> ID ù5œ_ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷Çÿ&¡•ÿÿÈ¿§§ÿÿûõ†  EI endstream endobj 44 0 obj <>stream 0 0 0 0 55 75 d1 55 0 0 75 0 0 cm BI /IM true /W 55 /H 75 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Ðf Hek)ÌžžƒÖMDá ¾ƒ}?WïÓõ~ÿÿ÷§ÿÿÿÿÿÿÿÿí}ÿiëÝv¾—kÃK†æˆWµí{^^^CÿÿÿÿÿÿÿýñÿÀ@ EI endstream endobj 49 0 obj <>stream 0 0 0 -47 43 0 d1 43 0 0 47 0 -47 cm BI /IM true /W 43 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID !ƒÿÿÿÿÿÿÇÿ!ƒÿÿÿÿÿþ  EI endstream endobj 50 0 obj <>stream 0 0 0 0 40 37 d1 40 0 0 37 0 0 cm BI /IM true /W 40 /H 37 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ‡@H xAéá§úz|ê]ß¿OüS÷ÿÿêMS_û^ÒÛ^=­¯Úà k ,(€ EI endstream endobj 51 0 obj <>stream 0 0 0 0 57 36 d1 57 0 0 36 0 0 cm BI /IM true /W 57 /H 36 /BPC 1 /D[1 0] /F/CCF /DP<> ID $!„ÿÿÿÿÿÿÿÿÿÿÿÿûýÿv­pÐaDWÔù!&¶šÚk €€ EI endstream endobj 52 0 obj <>stream 0 0 0 0 39 37 d1 39 0 0 37 0 0 cm BI /IM true /W 39 /H 37 /BPC 1 /D[1 0] /F/CCF /DP<> ID & †± Šƒ„\.¿®BøDôúãÔzòj‰‹þ×oí.]ŠÚßkkaa…ƒ  EI endstream endobj 53 0 obj <>stream 0 0 0 -48 40 1 d1 40 0 0 49 0 -48 cm BI /IM true /W 40 /H 49 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Á¨pD%<' ZÖ¹zCý§¤O_õÿýÿ&©÷ûav¶\0[ï{ÞÂÁ…ÎD@ EI endstream endobj 54 0 obj <>stream 0 0 0 0 37 37 d1 37 0 0 37 0 0 cm BI /IM true /W 37 /H 37 /BPC 1 /D[1 0] /F/CCF /DP<> ID &£$ƒ0tC¯¯¡Ó§½ûɪ~F>;Þ÷‡êä ‘ùŸàÂóA|Wõ×]p²*x€ EI endstream endobj 55 0 obj <>stream 0 0 0 0 38 50 d1 38 0 0 50 0 0 cm BI /IM true /W 38 /H 50 /BPC 1 /D[1 0] /F/CCF /DP<> ID $Wÿÿü‘ð@ôôâîûš‹OM§ý?ÿþ×ö­+X꺒ÚÃX`  EI endstream endobj 56 0 obj <>stream 0 0 0 0 38 50 d1 38 0 0 50 0 0 cm BI /IM true /W 38 /H 50 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A8BDON.﹨´ôÚÓÿÿíjÒµŽ«©!-¬5† ÿÿø€ EI endstream endobj 57 0 obj <>stream 0 0 0 0 12 49 d1 12 0 0 49 0 0 cm BI /IM true /W 12 /H 49 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿÿÿòjÉ¿ü@ EI endstream endobj 58 0 obj <>stream 0 0 0 0 12 49 d1 12 0 0 49 0 0 cm BI /IM true /W 12 /H 49 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿÿÿÿÿ€€ EI endstream endobj 59 0 obj <>stream 0 0 0 0 30 45 d1 30 0 0 45 0 0 cm BI /IM true /W 30 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡V¸_×õþkC ¯ÿÿÿÿÿÿÿûÔGüš‘ ?ÿÿÿø€ EI endstream endobj 60 0 obj <>stream 0 0 0 0 33 37 d1 33 0 0 37 0 0 cm BI /IM true /W 33 /H 37 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¤S‰(œC»ï“‹ž`œXK!Ädjð¸K].BaM@Á 6,ƒ‚Ã"Ò>MW÷߃5Š  EI endstream endobj 61 0 obj <>stream 0 0 0 -47 40 0 d1 40 0 0 47 0 -47 cm BI /IM true /W 40 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID !†_ÿÿÿò:Dû¾ä1x>ûÿë¬.HDu]T(PP EI endstream endobj 62 0 obj <>stream 0 0 0 0 27 36 d1 27 0 0 36 0 0 cm BI /IM true /W 27 /H 36 /BPC 1 /D[1 0] /F/CCF /DP<> ID $?ÿÿÿÿ}÷ (þH[ÃÀ€ EI endstream endobj 63 0 obj <>stream 0 0 0 0 28 49 d1 28 0 0 49 0 0 cm BI /IM true /W 28 /H 49 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¦H ÿÿÿÿÿÿÿÿÿÿÿÿþð”Gÿüš™ /ïø{ qûÞ÷ƒ€€ EI endstream endobj 64 0 obj <>stream 0 0 0 0 36 36 d1 36 0 0 36 0 0 cm BI /IM true /W 36 /H 36 /BPC 1 /D[1 0] /F/CCF /DP<> ID $ÿÿÿÿÿÿÿûû^Ô5õÉ ma¬0  EI endstream endobj 65 0 obj <>stream 0 0 0 0 32 37 d1 32 0 0 37 0 0 cm BI /IM true /W 32 /H 37 /BPC 1 /D[1 0] /F/CCF /DP<> ID & €°@ákZÖµÍBh úÞ£¯ÿ¹5^ýl0» Ž÷½ïxx3« EI endstream endobj 66 0 obj <>stream 0 0 0 -47 45 0 d1 45 0 0 47 0 -47 cm BI /IM true /W 45 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID &€pwîîä0œßo}û¿÷ÿÿëýu®ºX\ËQUB¨PRL3€€ EI endstream endobj 67 0 obj <>stream 0 0 0 0 37 37 d1 37 0 0 37 0 0 cm BI /IM true /W 37 /H 37 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¢”ƒ0pŸCõõùÕ🾜}ÿÿÿÿÿÿÿÿð EI endstream endobj 68 0 obj <>stream 0 0 0 0 55 36 d1 55 0 0 36 0 0 cm BI /IM true /W 55 /H 36 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¢ÂĦŸÿÿ¦Ÿÿýéþ¿ýéþ¼£Í^¯ïÿ_ýôž¯ãÿÿ}&¯ÿÿöôšÿÿÿlS_ÿÜ@ EI endstream endobj 69 0 obj <>stream 0 0 0 0 39 50 d1 39 0 0 50 0 0 cm BI /IM true /W 39 /H 50 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ ˆA–EÓèu¯®u-'Óëï¬}ÿþ¤Õ}ýwím®;ûÞÈ…µØ\0¸ÿÿÿÿþ  EI endstream endobj 70 0 obj <>stream 0 0 0 -48 38 1 d1 38 0 0 49 0 -48 cm BI /IM true /W 38 /H 49 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¢ádÕàƒ‹»¾çSÉÁ0M„á`¿ZÁd0B“Áp–ºZZZ ‚ä 8”áœê ¯Èh‹ &¨2œ¿ï¾ûáƒú@@ EI endstream endobj 71 0 obj <>stream 0 0 0 -90 91 2 d1 91 0 0 92 0 -90 cm BI /IM true /W 91 /H 92 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡¨S†¬ƒ@Іd< ðƒÂO=======?Ñè7Áé7Â} ß^­ôúMÿÓë}?ÿôñßý?ÿÿÿ÷¯ÿÿÿµÿù5T»ÿíw¯^ý´½{öÒívÒá…Øa-ƒ¸ö¶¶¶¶¶¶¶¶°ÂÚà ,,,ƒZŒ†×@@ EI endstream endobj 72 0 obj <>stream 0 0 0 0 76 66 d1 76 0 0 66 0 0 cm BI /IM true /W 76 /H 66 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡”UŠÿzþŸ÷¯éÿzþŸ÷¯éÿ%ÓÒÿÕ¾¿½'ÿïIÿÕ¾¿½_¯[÷êõú}oß«×÷Ò¿ý½/ý&ÿûÒý½/ïWëû€€ EI endstream endobj 73 0 obj <>stream 0 0 0 0 71 70 d1 71 0 0 70 0 0 cm BI /IM true /W 71 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡šSyày øD´H@ðë…×]u×_Ñ xD6U ø@úAúÔ?Aüz®wá¿ÿa•u&«ÿßémþ½ÖÚöØk²kkk­ö°ÖÂÚÃXaa…†@À<ƒIà EI endstream endobj 74 0 obj <>stream 0 0 0 0 49 68 d1 49 0 0 68 0 0 cm BI /IM true /W 49 /H 68 /BPC 1 /D[1 0] /F/CCF /DP<> ID Ã/ÿÿÿÿÿÿÿÿÿ}Ýݨ`¤P£þA­ýáïxrjY yÀ@ EI endstream endobj 75 0 obj <>stream 0 0 0 0 22 92 d1 22 0 0 92 0 0 cm BI /IM true /W 22 /H 92 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿÿÿÿÿÿÿɨ7ÿüƒ ÿÿà EI endstream endobj 76 0 obj <>stream 0 0 0 0 103 66 d1 103 0 0 66 0 0 cm BI /IM true /W 103 /H 66 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÁP €b«Ýÿþ¿iÿþ·Úÿþ·Úÿþ·Úÿ¯÷Úò!}yÕïÝ/ïô¾ÿßô¿oô¾ÿßô½[þ¾ÿßõõn¾¾ÿßãõkë¿ßÿV¯¯ýÿõjúÿß_ö¯¯ýõÿjúÿÞ?ÚµýÀ@ EI endstream endobj 78 0 obj <>stream 0 0 0 -59 55 0 d1 55 0 0 59 0 -59 cm BI /IM true /W 55 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID ' 95Zí~·û_­þ×zõî·ýx­úßúßöh×þ–ßÿioúí×þ–ßÿ¥·ú[þ–ßþéwým¯üVÿµýoýoÔ@ EI endstream endobj 79 0 obj <>stream 0 0 0 0 40 62 d1 40 0 0 62 0 0 cm BI /IM true /W 40 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡Î€ò0xAðûï¾fðAøA¼_oß¿!Ãáðƒéôúh†' é>ŸOIÿÜWýúÿýõÿ“T×ûî»]­µ°Â㽕kk°¸k†‚æ  EI endstream endobj 80 0 obj <>stream 0 0 0 0 26 45 d1 26 0 0 45 0 0 cm BI /IM true /W 26 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3?ÿÿÿÿÿÿ}ÝÜ(æk{ÓP^  EI endstream endobj 81 0 obj <>stream 0 0 0 0 41 47 d1 41 0 0 47 0 0 cm BI /IM true /W 41 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡°Ø/!„ø"­Â 뮊¢?Aô¤¡Òëþ+þ”~hüš¯ý¯í×k¶½¥¶¼0–Åmmma¬0°`  EI endstream endobj 82 0 obj <>stream 0 0 0 0 38 47 d1 38 0 0 47 0 0 cm BI /IM true /W 38 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID & æd‰¦2s ú}>‡-§÷¿¿äÕ_~ýöd@øïxxyW Ñ?ëõ®h^ %Á…âºë­¬Y ¬@ EI endstream endobj 83 0 obj <>stream 0 0 0 0 28 57 d1 28 0 0 57 0 0 cm BI /IM true /W 28 /H 57 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ð°µõ΂怔¿¯ÿÿÿÿÿÿÿÿÿÿÿÿÿ½D~MLÌ1ÿÿÿÿÿþ  EI endstream endobj 84 0 obj <>stream 0 0 0 -59 47 0 d1 47 0 0 59 0 -59 cm BI /IM true /W 47 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4 Í/Òý/Òý/Òý/Òý/Ò×KK ‘kŠ…Ã»¹ aáö÷ïýÿõÿ×­uÂYdUUB…*@€ EI endstream endobj 85 0 obj <>stream 0 0 0 0 65 45 d1 65 0 0 45 0 0 cm BI /IM true /W 65 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3 a£ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïûÿZ}ëwéöšPÓXaacb©æk[A¬4ÖÂjMC EI endstream endobj 86 0 obj <>stream 0 0 0 0 41 47 d1 41 0 0 47 0 0 cm BI /IM true /W 41 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œÀgMpAè< ôôôj^Hè ßO¤ß^­ôÿÿßÿÿÿÿòjš_ö¿í¥Úí¥Úì0–Åuµ†¶°ÂÁ‚€€ EI endstream endobj 87 0 obj <>stream 0 0 0 -59 55 0 d1 55 0 0 59 0 -59 cm BI /IM true /W 55 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4 _ÿÿÿòNŸÿÓÿ¾¿éùšÿé7ÿ­úý&ÿÿé7ÿ­úý&ÿÿé7ÿ­úý&ÿÿÅ1þŸ§ý?éÀ@ EI endstream endobj 88 0 obj <>stream 0 0 0 0 43 61 d1 43 0 0 61 0 0 cm BI /IM true /W 43 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¬ 30ûï¾ÿ!Q‡Ûý¿ÛÿÛþÿ^õïõï^ùšêý^¿·¥û}zþÞ—íõëûz_·×¯íé~Þ—ÿ·¥ûz_þØ®à EI endstream endobj 89 0 obj <>stream 0 0 0 -60 48 2 d1 48 0 0 62 0 -60 cm BI /IM true /W 48 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡–lП==:Ѩ.ÐAè ôƒéé>ƒ¯_¥ÿ×Çõÿýÿ&«ßþß÷¿am®ÖØ[k†Á‚Øïaa…† AA EI endstream endobj 90 0 obj <>stream 0 0 0 0 40 62 d1 40 0 0 62 0 0 cm BI /IM true /W 40 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3¯ÿÿÿñ‘‡‚AééÅÊyÁ ÓíÓ×·_ïWÿÿÿûõ¯Úõk}¥aa¥ÌÒØ[XjMC  EI endstream endobj 91 0 obj <>stream 0 0 0 -59 9 0 d1 9 0 0 59 0 -59 cm BI /IM true /W 9 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿÿÿÿÿÿà EI endstream endobj 92 0 obj <>stream 0 0 0 0 37 45 d1 37 0 0 45 0 0 cm BI /IM true /W 37 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3 Ÿÿÿÿÿÿÿÿÿÿÿ«ÞÕ¥k %L×k l)5€€ EI endstream endobj 93 0 obj <>stream 0 0 0 0 29 61 d1 29 0 0 61 0 0 cm BI /IM true /W 29 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID &£3ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷‚ #ÿ“Q™ƒÿÿï÷ï¾t-Þððà EI endstream endobj 94 0 obj <>stream 0 0 0 0 36 47 d1 36 0 0 47 0 0 cm BI /IM true /W 36 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ Øy¯AÂÖ´kôC ôzÒþ¾+ÿÿÿ“Tÿ½ûïlà † c½ï  @ EI endstream endobj 95 0 obj <>stream 0 0 0 -61 19 11 d1 19 0 0 72 0 -61 cm BI /IM true /W 19 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡õÖº_¥ú_¥úõúõ¯õúÿ_¯ÿ_ÿÿòj¿ÿßþÿßþýþýþýïû·û·ûw EI endstream endobj 96 0 obj <>stream 0 0 0 -61 19 11 d1 19 0 0 72 0 -61 cm BI /IM true /W 19 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID >ɪ·û·ûÞýïýïýþÿßþÿýÿÿÿÿõÿ¯ÿ_¯õúõúõú_¥úõ¥ú_À@ EI endstream endobj 97 0 obj <>stream 0 0 0 -61 8 0 d1 8 0 0 61 0 -61 cm BI /IM true /W 8 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿÿÿÿòj/ÿ™†ÿð EI endstream endobj 98 0 obj <>stream 0 0 0 0 34 47 d1 34 0 0 47 0 0 cm BI /IM true /W 34 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID & æÁy$ùÕÄ;»™ƒàƒa8Zëë ²O¥ðK a- Z#󨂈*ëï!±äÕ† ±þûï† Ô(€ EI endstream endobj 99 0 obj <>stream 0 0 0 -44 60 0 d1 60 0 0 44 0 -44 cm BI /IM true /W 60 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡IÁ‚pþûõý~ôÿý~ôÿ^f·ÿ­Ïÿ×éûû¯_×éûû¯_~ûÖëÿ~ûÖéÿ_§ÿûÖ)ÿú{úõßÿ§¿®=÷ë° EI endstream endobj 100 0 obj <>stream 0 0 0 0 40 63 d1 40 0 0 63 0 0 cm BI /IM true /W 40 /H 63 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Á€Ñ!àœáÓéô::ü &“Ò}>ž¯¯úqÿÝÿß_ɪû_íoím®Öà ŽöU­®]® ÿÿÿÿÿà EI endstream endobj 101 0 obj <>stream 0 0 0 0 8 61 d1 8 0 0 61 0 0 cm BI /IM true /W 8 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿÿÿÿÿÿø€ EI endstream endobj 102 0 obj <>stream 0 0 0 0 40 63 d1 40 0 0 63 0 0 cm BI /IM true /W 40 /H 63 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ À1!€æl=§§)ç7O·O^ݽ_ÿÿÿïÖ¿kÕ­ö•…†”W3Kama¬0Xÿÿÿÿ EI endstream endobj 103 0 obj <>stream 0 0 0 -61 37 0 d1 37 0 0 61 0 -61 cm BI /IM true /W 37 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3 Ÿÿÿÿÿÿÿÿÿÿÿ«ÞÕ¥k %L×k l,Xÿÿÿÿ EI endstream endobj 104 0 obj <>stream 0 0 0 -26 22 -18 d1 22 0 0 8 0 -26 cm BI /IM true /W 22 /H 8 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ EI endstream endobj 105 0 obj <>stream 0 0 0 0 38 46 d1 38 0 0 46 0 0 cm BI /IM true /W 38 /H 46 /BPC 1 /D[1 0] /F/CCF /DP<> ID & æÃD‰¦dçO ú}|tH„}?ôÿÿuÿÿÿÿÿÿÿÿÿþ  EI endstream endobj 106 0 obj <>stream 0 0 0 -10 9 0 d1 9 0 0 10 0 -10 cm BI /IM true /W 9 /H 10 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÀ@ EI endstream endobj 107 0 obj <>stream 0 0 0 -59 8 -36 d1 8 0 0 23 0 -59 cm BI /IM true /W 8 /H 23 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿþ  EI endstream endobj 108 0 obj <>stream 0 0 0 0 17 78 d1 17 0 0 78 0 0 cm BI /IM true /W 17 /H 78 /BPC 1 /D[1 0] /F/CCF /DP<> ID 48w}É©ðû~ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþAƒ_ÿ‘'ÿÿÿÀ@ EI endstream endobj 109 0 obj <>stream 0 0 0 -42 9 0 d1 9 0 0 42 0 -42 cm BI /IM true /W 9 /H 42 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿɨŸÿÿÿÿæ¿þ  EI endstream endobj 110 0 obj <>stream 0 0 0 0 40 61 d1 40 0 0 61 0 0 cm BI /IM true /W 40 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3 Z KKKKKKKKKKKKKK iEUU]Ù§†öööööÞÞÛÛÛÛØ{xoÿÿÿø€ EI endstream endobj 111 0 obj <>stream 0 0 0 0 43 44 d1 43 0 0 44 0 0 cm BI /IM true /W 43 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ¹@/½{×û×ùšzþÞ—ÿ·¥ûõzþÞ—ÿ·¥ÿÓzþÞ—ÿ·¥ÿíé~Þ—ÿ·¥ÿíŠî  EI endstream endobj 112 0 obj <>stream 0 0 0 0 36 44 d1 36 0 0 44 0 0 cm BI /IM true /W 36 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID üœ2œš§··¾ß{{{{{í÷····¾Þß{{{{íí÷·(Sÿÿ EI endstream endobj 113 0 obj <>stream 0 0 0 -59 39 0 d1 39 0 0 59 0 -59 cm BI /IM true /W 39 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4ÿÿÿÿÿ‘'páÝÜÐ+È`<7¾ýßûÿëý/× „²"xê¡T¨ € EI endstream endobj 114 0 obj <>stream 0 0 0 -60 52 2 d1 52 0 0 62 0 -60 cm BI /IM true /W 52 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡–p4Hh‚‚ ðƒÓÓ£P&Ð@ôK×K×__éþ¿Ð?ÿä6Íwüš¯÷ûÞý‚ l/k¶l/ .Á‚ì{áöXa`Ád ‘ EI endstream endobj 115 0 obj <>stream 0 0 0 -10 12 10 d1 12 0 0 20 0 -10 cm BI /IM true /W 12 /H 20 /BPC 1 /D[1 0] /F/CCF /DP<> ID <[îMW¿Ûþÿoÿÿà EI endstream endobj 116 0 obj <>stream 0 0 0 0 51 59 d1 51 0 0 59 0 0 cm BI /IM true /W 51 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡°Ð6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‰ ÿ€€ EI endstream endobj 117 0 obj <>stream 0 0 0 0 43 44 d1 43 0 0 44 0 0 cm BI /IM true /W 43 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID 'jš]û¥¶–Úým¥¶—k½m¥¶—k½m¥±_µµµû]ë§Þžº|ŸzW¤þÞ“zW«ôÞ“zW«ôÞ“z\@ EI endstream endobj 118 0 obj <>stream 0 0 0 -59 49 0 d1 49 0 0 59 0 -59 cm BI /IM true /W 49 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID " €áû»ïš;w¿¿þº×\%‘BEUUB‡wsPNßÿÿ×\ ×U †p EI endstream endobj 119 0 obj <>stream 0 0 0 0 15 61 d1 15 0 0 61 0 0 cm BI /IM true /W 15 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿÿÿÿÿÿø€ EI endstream endobj 120 0 obj <>stream 0 0 0 0 49 47 d1 49 0 0 47 0 0 cm BI /IM true /W 49 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Üà7ÀÌ ð@ôðƒÓÓ×½>D]ú}&ú⽿ÿÿÿù5M/û_öÒá…Ø¯ÚÚÚÚÚÚà ,ƒÀ@ EI endstream endobj 121 0 obj <>stream 0 0 0 0 40 47 d1 40 0 0 47 0 0 cm BI /IM true /W 40 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID &  àyÆ`‰5 ÂÖµ­k–ˆ€ü ôƒè=G_ÿÿrj½úì-† Çï{Þð÷ƒ k  EI endstream endobj 122 0 obj <>stream 0 0 0 0 49 61 d1 49 0 0 61 0 0 cm BI /IM true /W 49 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID :†4´ºÒÒÒÒÒÒÒ×KKKJ*ªªª®îîuOoooooooooaííííãÿÿÿÿ EI endstream endobj 123 0 obj <>stream 0 0 0 0 15 61 d1 15 0 0 61 0 0 cm BI /IM true /W 15 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿÿÿÿòjÿ:†ÿü@ EI endstream endobj 124 0 obj <>stream 0 0 0 0 45 45 d1 45 0 0 45 0 0 cm BI /IM true /W 45 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID :†?ÿÿÿÿÿÿÿÿïýý«Ú† +ëRÚÚÚÃRj @@ EI endstream endobj 125 0 obj <>stream 0 0 0 0 48 63 d1 48 0 0 63 0 0 cm BI /IM true /W 48 /H 63 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Á 7¥ò3@ûá÷ß}ÿ4ø ü ß ñöü‚ÁôO„üu­}r0|'¤ú~úÇßÿÿþ¤Õ?®þ¶ì.;ßÞöU­…Úá…Á„AA¨€ EI endstream endobj 126 0 obj <>stream 0 0 0 -45 45 2 d1 45 0 0 47 0 -45 cm BI /IM true /W 45 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID & æ2DÓ2z§Óè~¼e8á=ÓßÝï&¨?! Ž÷½ï!Ĥ5þXë‡\^NÅ_×]p°ÂÈQ EI endstream endobj 127 0 obj <>stream 0 0 0 0 48 63 d1 48 0 0 63 0 0 cm BI /IM true /W 48 /H 63 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Á°ËúaOZ„ú~:Ö¾¹N/OIôýþ+¿ÿÿýy5Oë¿­µÃ c÷÷½•kk°¸apÂãÿÿÿÿÿü@ EI endstream endobj 128 0 obj <>stream 0 0 0 -59 53 0 d1 53 0 0 59 0 -59 cm BI /IM true /W 53 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID :†}}}}këëëëëÖ¾ºúëÒÿKý/ô¿Òõ_______Zúúø€ EI endstream endobj 129 0 obj <>stream 0 0 0 -29 25 -17 d1 25 0 0 12 0 -29 cm BI /IM true /W 25 /H 12 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿð EI endstream endobj 130 0 obj <>stream 0 0 0 -61 48 2 d1 48 0 0 63 0 -61 cm BI /IM true /W 48 /H 63 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡˜`o¤êaá=§}÷"ÓtôÿMþ»ÿÿkÿi}­¬0”uÕN©mmma…†?ÿÿÿÀ@ EI endstream endobj 131 0 obj <>stream 0 0 0 -59 56 0 d1 56 0 0 59 0 -59 cm BI /IM true /W 56 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID "@ ‹ppîÝÝÜÔàøo}½÷ÛýÿöÿÿÿþµýKõÒÂé`¹cQ]UB¨P¡H˜7$Á @ EI endstream endobj 132 0 obj <>stream 0 0 0 0 36 57 d1 36 0 0 57 0 0 cm BI /IM true /W 36 /H 57 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ë …×õýù&j×ë×ÿÿÿÿÿÿÿÿÞ¢?ù5#¨cÿÿÿÿÿü@ EI endstream endobj 133 0 obj <>stream 0 0 0 -59 55 0 d1 55 0 0 59 0 -59 cm BI /IM true /W 55 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œÔ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüH¨oÿà EI endstream endobj 134 0 obj <>stream 0 0 0 0 34 45 d1 34 0 0 45 0 0 cm BI /IM true /W 34 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID :†ÿÿÿÿÿßwaA‚çU½ïMAD@ EI endstream endobj 135 0 obj <>stream 0 0 0 0 40 47 d1 40 0 0 47 0 0 cm BI /IM true /W 40 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÁÀ"F8Ô\;»îN ……Ö@€rÄŠ7 at´´ºˆ()P šÁ@*È(°Xd"#äÕ}÷ß ,…O EI endstream endobj 136 0 obj <>stream 0 0 0 0 35 61 d1 35 0 0 61 0 0 cm BI /IM true /W 35 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID &£:‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼%ÿÿòj3¨'ÿþûàÖÇýï{ÃÀ€ EI endstream endobj 137 0 obj <>stream 0 0 0 0 48 47 d1 48 0 0 47 0 0 cm BI /IM true /W 48 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID &  àh\F˜ xAë…×õבú„¤Ãô1ô£ÿ5êMWÿ¿ÒÛ^×`Â\{[[[[[Xaa…cX€ EI endstream endobj 138 0 obj <>stream 0 0 0 -60 61 2 d1 61 0 0 62 0 -60 cm BI /IM true /W 61 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡šl3$6ÁÁÓÂOO?ÓÓÑPß"ô›áÒ¿}&úõ½?ÿ¯¿ÿñüš¯ÿÿû_ÿ­ºÿµÛKþ×m.]ƒ¶+­ö¶¶°ÂÚà ,,‚  EI endstream endobj 139 0 obj <>stream 0 0 0 0 48 62 d1 48 0 0 62 0 0 cm BI /IM true /W 48 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID :ƒ_ÿÿÿÈ$dza„ôœ]÷܈MÓÓý7úïÿý¯ý¥ö¶°ÂQ×U:¥µµµ†š‚€  EI endstream endobj 140 0 obj <>stream 0 0 0 0 40 62 d1 40 0 0 62 0 0 cm BI /IM true /W 40 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÕÿÿÿÿÿäŽC‡Â„O§Ðèëò€šOIôúz¾¿éÇÿuÿÿ}&«íµ¿µ¶»[ .;ÙV¶¸av¸0D5 EI endstream endobj 141 0 obj <>stream 0 0 0 -59 23 -36 d1 23 0 0 23 0 -59 cm BI /IM true /W 23 /H 23 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3ÿÿÿÿÿÿÿÿ EI endstream endobj 142 0 obj <>stream 0 0 0 -59 45 0 d1 45 0 0 59 0 -59 cm BI /IM true /W 45 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4ÿ_____F‹_]}uõ××_]}uõ××K¯®¾ºúë믮¾ºúëëõõõõõõ€€ EI endstream endobj 143 0 obj <>stream 0 0 0 0 46 61 d1 46 0 0 61 0 0 cm BI /IM true /W 46 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡°Øltø@ðƒÂzOOG@ž7 Ÿ¦ôŸÛëÕúþûÿã×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€€ EI endstream endobj 144 0 obj <>stream 0 0 0 0 38 59 d1 38 0 0 59 0 0 cm BI /IM true /W 38 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID þh¿ÿÿÿÿÿÿÿÿÿÿÿÿ€€ EI endstream endobj 145 0 obj <>stream 0 0 0 -60 42 2 d1 42 0 0 62 0 -60 cm BI /IM true /W 42 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Á°Ù"ɤÁîîCæ€Fn á<'ïû×ÿK]p¸K!±>Ap™O°–Z Ah¨ò06Q¨ `¢.¿þäÕ2Cö Ø]†AǾûáðÂÃÁ EI endstream endobj 146 0 obj <>stream 0 0 0 0 42 6 d1 42 0 0 6 0 0 cm BI /IM true /W 42 /H 6 /BPC 1 /D[1 0] /F/CCF /DP<> ID ü@ EI endstream endobj 147 0 obj <>stream 0 0 0 0 55 59 d1 55 0 0 59 0 0 cm BI /IM true /W 55 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Ü ý{×¿×¿Óæ‹¯Õûëõ~úßO¯Õûëõz¿ßO¯Õûë}z¿ßO¯Õûë}z¿ßO¯Õêÿ}z¿ßO­õêÿ}q½X€ EI endstream endobj 148 0 obj <>stream 0 0 0 -59 27 8 d1 27 0 0 67 0 -59 cm BI /IM true /W 27 /H 67 /BPC 1 /D[1 0] /F/CCF /DP<> ID >ɪ÷ÿ·ÿ·ÿ·ÿ·ÿûûûûû÷ÿ·ÿ·ÿ·ÿ¿ûûûû÷ÿ·ýÀ@ EI endstream endobj 150 0 obj <>stream 0 0 0 -70 56 0 d1 56 0 0 70 0 -70 cm BI /IM true /W 56 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID :†V”š§ÿÿ¥·ÿÿ¥·ÿþéwÿõ±ÿõ¿þ×ÿ[ó ¯u¿ÿ¯u¿ÿÖëßÿÖëßÿÖÿ¯~õÿ¯¿ÿ[ÿíõ¿ÿ[À@ EI endstream endobj 151 0 obj <>stream 0 0 0 0 52 70 d1 52 0 0 70 0 0 cm BI /IM true /W 52 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID :†Íu¯Zõ¯Zõ¯Zõ¯Zõú_¥®–ºX,UuWwws¨^ßöÿ÷ÿýõ×KÉ«Šëªª… Fƒ`€ EI endstream endobj 152 0 obj <>stream 0 0 0 0 50 70 d1 50 0 0 70 0 0 cm BI /IM true /W 50 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID $ÿÿÿÿÿÿÿÿüŒôÿÿôÿÿôÿÿõ÷ÿôN¿oÿþ—ö?ëþ»þ»õßõßõ€€ EI endstream endobj 153 0 obj <>stream 0 0 0 0 44 74 d1 44 0 0 74 0 0 cm BI /IM true /W 44 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID & \œ$†q xAÖµ­kë\—  ƒè=ü>ŸN½Å×ÿÿÿòjŸÿ{ö» mv» a‚ã½ýï{ÞðÂÁ‚È<@ EI endstream endobj 154 0 obj <>stream 0 0 0 0 42 70 d1 42 0 0 70 0 0 cm BI /IM true /W 42 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿù5:ƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø ßÿÀ@ EI endstream endobj 155 0 obj <>stream 0 0 0 13 58 23 d1 58 0 0 10 0 13 cm BI /IM true /W 58 /H 10 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÀ@ EI endstream endobj 156 0 obj <>stream 0 0 0 0 48 70 d1 48 0 0 70 0 0 cm BI /IM true /W 48 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID !ëý}¯¯õþ¾¿×(Zúÿ__鯯õõ××úúÿ_Xõþ¾¿×úúÿ__ë EI endstream endobj 157 0 obj <>stream 0 0 0 0 50 70 d1 50 0 0 70 0 0 cm BI /IM true /W 50 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÁÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ$ÿø€ EI endstream endobj 158 0 obj <>stream 0 0 0 -70 46 0 d1 46 0 0 70 0 -70 cm BI /IM true /W 46 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID :‚ÿÿÿÿÿÿÿÿÿÿÿüþuÿÿÿÿÿÿÿÿÿü@ EI endstream endobj 159 0 obj <>stream 0 0 0 0 48 70 d1 48 0 0 70 0 0 cm BI /IM true /W 48 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID )`88pîîîûGÁ¼>û¿{ÿïýÿÿ¿ÿúÿÿ×ëÿ¯Zþ–¸\ÌN+ªªª… Iƒp EI endstream endobj 160 0 obj <>stream 0 0 0 0 44 70 d1 44 0 0 70 0 0 cm BI /IM true /W 44 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿùÔ3ÿÿÿÿÿÿÿÿÿÿÿÿÿÀ@ EI endstream endobj 161 0 obj <>stream 0 0 0 0 45 70 d1 45 0 0 70 0 0 cm BI /IM true /W 45 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿùÔ?ÿÿÿÉ]ÿÿþuÿÿü‚ÿþ  EI endstream endobj 163 0 obj <>stream 0 0 0 -73 23 13 d1 23 0 0 86 0 -73 cm BI /IM true /W 23 /H 86 /BPC 1 /D[1 0] /F/CCF /DP<> ID & úë^´¿K^µþµë_ÒþµÿÒÿý/ÿׯÿÿÿøÿäÕÿÿÿ½ÿÿöÿöÿ÷ï÷ïöÿ~÷ï~÷ï}¿Û EI endstream endobj 164 0 obj <>stream 0 0 0 -54 44 2 d1 44 0 0 56 0 -54 cm BI /IM true /W 44 /H 56 /BPC 1 /D[1 0] /F/CCF /DP<> ID & æ`ÞBU„ ™á>ƒéô:æ«D0½:ïuß¾ýüš§áöƒìƒc½ááàòÕÈiÿý,±þðkÁ„¹ O×]u† ,ƒÕ EI endstream endobj 165 0 obj <>stream 0 0 0 0 39 54 d1 39 0 0 54 0 0 cm BI /IM true /W 39 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID ' ßÿÿÿÿÿÿÿß}Ý…jPÁFN·½ïk )5 €  EI endstream endobj 166 0 obj <>stream 0 0 0 0 49 54 d1 49 0 0 54 0 0 cm BI /IM true /W 49 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3hÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿ¯ë×ÿÿ»õ[O´ÒˆþI¥3V¶Ÿi…&   EI endstream endobj 167 0 obj <>stream 0 0 0 0 41 56 d1 41 0 0 56 0 0 cm BI /IM true /W 41 /H 56 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡°Ð,‚ã0á„ë Z">uÐAè úzAôþ—õ_ÿÿýòj½ÿ{ö¶×am®[EEŽ÷¼<0°Á`Á@@ EI endstream endobj 168 0 obj <>stream 0 0 0 0 44 73 d1 44 0 0 73 0 0 cm BI /IM true /W 44 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID þMA‚pcÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿå„ÿÿÿüÿò ÿÿÿÿþ  EI endstream endobj 169 0 obj <>stream 0 0 0 18 48 23 d1 48 0 0 5 0 18 cm BI /IM true /W 48 /H 5 /BPC 1 /D[1 0] /F/CCF /DP<> ID ø€ EI endstream endobj 170 0 obj <>stream 0 0 0 -73 41 0 d1 41 0 0 73 0 -73 cm BI /IM true /W 41 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4ÿÿÿÿÿÿÿÿÿÿÿÿ÷ÿûKïµûJX0±SEëkk a¬0Xÿÿÿÿü@ EI endstream endobj 171 0 obj <>stream 0 0 0 0 44 75 d1 44 0 0 75 0 0 cm BI /IM true /W 44 /H 75 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Á˜g‚‚ (>Ÿ øëFµ£ œ ôŸOúOý=}?ÿÿºÿÿ¾¼š¯ÿÿkûû_Û]­µÚØ`¸ïe"Úíp×apaqÿÿÿÿÿÿÿ EI endstream endobj 172 0 obj <>stream 0 0 0 0 42 74 d1 42 0 0 74 0 0 cm BI /IM true /W 42 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID & _ Z××$Òë­zÿÿëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÿà EI endstream endobj 173 0 obj <>stream 0 0 0 -68 43 0 d1 43 0 0 68 0 -68 cm BI /IM true /W 43 /H 68 /BPC 1 /D[1 0] /F/CCF /DP<> ID & £¬/­sP| Òë¯þ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿïQäÔÐ4ÿÿÿÿÿÿ€€ EI endstream endobj 174 0 obj <>stream 0 0 0 -72 44 -27 d1 44 0 0 45 0 -72 cm BI /IM true /W 44 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œðgÿÿÿùŸ ï§§ÃÂè<'¬<&Mpð‚Ãz aá†"°Âà ,0¶°ƒÂ á ð‰äÃ,0âÙ5K p°û k¬5°°øãÿÿÿð EI endstream endobj 175 0 obj <>stream 0 0 0 -54 41 0 d1 41 0 0 54 0 -54 cm BI /IM true /W 41 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4ÿÿÿÿÿÿÿÿÿÿÿÿ÷ÿûKïµûJX0±SEëkk a©5@@ EI endstream endobj 176 0 obj <>stream 0 0 0 0 44 75 d1 44 0 0 75 0 0 cm BI /IM true /W 44 /H 75 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œø!fŒ=§§r]9¬L Ý7O^Ý{÷^ÿÿ_û×ÿ«ÿõû­oûJÖÒµ† EsDºÚà k ÿÿÿÿ€€ EI endstream endobj 177 0 obj <>stream 0 0 0 0 48 56 d1 48 0 0 56 0 0 cm BI /IM true /W 48 /H 56 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÜÐ`F¤ z ®º"?:ô>?>.¿Kÿþ+õɪ' ¿¯ÿíßí.ý׺Û^ÒØav %±ímml,5† @ EI endstream endobj 178 0 obj <>stream 0 0 0 -73 22 13 d1 22 0 0 86 0 -73 cm BI /IM true /W 22 /H 86 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4ɪ·ÞýïÞýïßíþýþßÿíÿ÷¿ÿ÷ïÿÿÿÿÿÿýuÿúõÿú_ú_ú_Ö½kÖ½kÖ½kÔW EI endstream endobj 179 0 obj <>stream 0 0 0 -59 75 0 d1 75 0 0 59 0 -59 cm BI /IM true /W 75 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡‚€PH¯ÿk÷¯ÿk÷¯ÿk÷£E™¯¿ý×úÞ—¿¿ý×úÞ—¿¿ý×úÞ—¿¿ý×®·×¿¿ý×®·×¿¿ý×®·×¿¿ÿ^¶·×¿¿ÿ^¶·×¿¿ÿ^±ß[ûÿ^½õ¿¿õÇÚÞÖ  EI endstream endobj 180 0 obj <>stream 0 0 0 -59 43 0 d1 43 0 0 59 0 -59 cm BI /IM true /W 43 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID #AÁû»šÛßÛÿÿõúé~Âä:x¨U ÜÐxo}½ÿÿÿÿ]t°¹ 2*ª¡B’`À€ EI endstream endobj 181 0 obj <>stream 0 0 0 -59 35 0 d1 35 0 0 59 0 -59 cm BI /IM true /W 35 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4 ¯ÿÿÿÿÿÿ#wÿæ•ÿÿÿù ÿ€€ EI endstream endobj 182 0 obj <>stream 0 0 0 0 55 62 d1 55 0 0 62 0 0 cm BI /IM true /W 55 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID & `àÏ^xAéá=£P‚ÐA½&ôŸ¦ô›éõ¾Ÿý&ÿÿ§þ;ýwÿÿWÿ©5_µÿý´¿íw®×m.×m-´¸avKb¶¶°ÂÚà  Ý EI endstream endobj 183 0 obj <>stream 0 0 0 -72 61 2 d1 61 0 0 74 0 -72 cm BI /IM true /W 61 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÖP He„È(ÁÂ:ÂÖµ­k1è¤ôAè úIôzþµëÿúÿÿ“Uïÿûß¿ïoÂÛ\0¶Âá‚Ø`¶@ËŽ÷½ïx{à  ,†XLƒZ  EI endstream endobj 184 0 obj <>stream 0 0 0 0 58 56 d1 58 0 0 56 0 0 cm BI /IM true /W 58 /H 56 /BPC 1 /D[1 0] /F/CCF /DP<> ID & \œ È< ôðƒÓÓÓý=>BAøO¤ß¿OüSôÿÿÿ«ÿjMV½{öÒívKkö¶¶¶¶¶à    EI endstream endobj 185 0 obj <>stream 0 0 0 0 54 56 d1 54 0 0 56 0 0 cm BI /IM true /W 54 /H 56 /BPC 1 /D[1 0] /F/CCF /DP<> ID & æ€Ó!+ =§Óëã¯àÓ§½ïýÞòjùH/ïïxx<ƒÒ‰õúåÇÃ^ %Éž>¿®ºë®¸Yo àà EI endstream endobj 186 0 obj <>stream 0 0 0 0 17 73 d1 17 0 0 73 0 0 cm BI /IM true /W 17 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿÿÿÿÿÿÿÿ€€ EI endstream endobj 187 0 obj <>stream 0 0 0 0 57 56 d1 57 0 0 56 0 0 cm BI /IM true /W 57 /H 56 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ` 6HgQ‚#bAÐ<.¸]]tI—`¿Aô¤ ýñŠýGÿ)ÃgV²j¿ý¥¶½ûa.]Šëk}­­¬0¶°ÂÁ…ÙT@ EI endstream endobj 188 0 obj <>stream 0 0 0 0 49 56 d1 49 0 0 56 0 0 cm BI /IM true /W 49 /H 56 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A 3ÈBy1<¢ž}÷ß÷ü§ÊŽ> ð¸þµÊ @Ô‚ `¸\%®––— @¸.@ÀŒ„N Ь†Uɪ`½‚ðÈ}8ÿß÷ßµƒ BH`d@ EI endstream endobj 189 0 obj <>stream 0 0 0 0 47 56 d1 47 0 0 56 0 0 cm BI /IM true /W 47 /H 56 /BPC 1 /D[1 0] /F/CCF /DP<> ID & `œ<†p˜ xAÂÂÖµ­k\“ÂH>ƒéê:úÿþþù5Oû a…Ø[ Žþ÷½ï “rÀ@ EI endstream endobj 190 0 obj <>stream 0 0 0 0 17 73 d1 17 0 0 73 0 0 cm BI /IM true /W 17 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿÿÿÿÿù5?óXoÿð EI endstream endobj 191 0 obj <>stream 0 0 0 0 53 54 d1 53 0 0 54 0 0 cm BI /IM true /W 53 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID 5ÿÿÿÿÿÿÿÿÿûÿýýõ{PÔ(¯®¦µÚÚÃ[ I¨f€€ EI endstream endobj 192 0 obj <>stream 0 0 0 0 57 75 d1 57 0 0 75 0 0 cm BI /IM true /W 57 /H 75 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ¹N $)Œ< ø|>ûï¾ÿÂ>h >? øMâû÷ä1«Â„AôøOÇZ××Ñ/ é×§î+÷ëÿÿûý^MUwõ¶¸k†Çïï{#KkµÚá…à ƒ†a¨€ EI endstream endobj 193 0 obj <>stream 0 0 0 0 86 54 d1 86 0 0 54 0 0 cm BI /IM true /W 86 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID )ÁrœÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûýÿÿÝÿi«öšVƒX0…ˆúêT¼§I­¦¶šÚk 5°ƒ MC0 `  EI endstream endobj 194 0 obj <>stream 0 0 0 -73 29 13 d1 29 0 0 86 0 -73 cm BI /IM true /W 29 /H 86 /BPC 1 /D[1 0] /F/CCF /DP<> ID & kÖ½k¥ú_¯Zõúõ¯õ¯õ¯ÿZÿÿÅÿÿÿÿ&«ßÿïßþýþýþýïýïû·û·û{ö  EI endstream endobj 195 0 obj <>stream 0 0 0 -68 43 0 d1 43 0 0 68 0 -68 cm BI /IM true /W 43 /H 68 /BPC 1 /D[1 0] /F/CCF /DP<> ID & BÁuÂëúþ¿ÿ¢¬&D5×ÿÿÿÿÿÿÿÿÿÿÿýê#ÿäÔRœÿÿÿÿÿÿÿ EI endstream endobj 196 0 obj <>stream 0 0 0 0 40 54 d1 40 0 0 54 0 0 cm BI /IM true /W 40 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID 5‚ÿÿÿÿÿÿßÝÝÃOÇóZßÞðäÔ3  EI endstream endobj 197 0 obj <>stream 0 0 0 0 57 75 d1 57 0 0 75 0 0 cm BI /IM true /W 57 /H 75 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œø4Èc\2Iè>éôú­}r¢Ð }¤ú}}Å~ýÿÿ¯&©ýwõ¶»\0¶?{ûÙ[]®× pÂá‚ãÿÿÿÿÿÿþ  EI endstream endobj 198 0 obj <>stream 0 0 0 0 60 74 d1 60 0 0 74 0 0 cm BI /IM true /W 60 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¢šƒSÃá÷ß÷ß÷ùaöÿo÷ï^õþõý?ÓÿÓý?׿׽ÿÞ¿O­õûzýz·×¯íõë}>¿Þ“ýõêÿz_¾•ÿíéÅ7ø€ EI endstream endobj 199 0 obj <>stream 0 0 0 -73 42 0 d1 42 0 0 73 0 -73 cm BI /IM true /W 42 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¢ÁÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxJ#ÿÿþMD5‚ÿÿ¿þùP/ÿ½ï{ÃÁÀ@ EI endstream endobj 200 0 obj <>stream 0 0 0 -73 57 2 d1 57 0 0 75 0 -73 cm BI /IM true /W 57 /H 75 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Ðfä2ªšÆzzzzqw}É7‚éééº÷ú~ÿ뿯þ•ÿkik a„£®«šÒÚÚÚà ,,ÿÿÿþ  EI endstream endobj 201 0 obj <>stream 0 0 0 0 83 53 d1 83 0 0 53 0 0 cm BI /IM true /W 83 /H 53 /BPC 1 /D[1 0] /F/CCF /DP<> ID & „@.DîÿÿUûïÿUûïÿ_Óï×ÿÓïÑ­ä„ÿ_î½'ÿûý_î½'ÿûý_î½'ÿûøßZúûýõ¯§¿ÿßZú{ÿýõ¯ûéÿÞ:þÓà EI endstream endobj 202 0 obj <>stream 0 0 0 0 53 73 d1 53 0 0 73 0 0 cm BI /IM true /W 53 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID 5ÿÿÿÿÿÿÿÿÿûÿýýõ{PÔ(¯®¦µÚÚÃ[ `Ácÿÿÿÿð EI endstream endobj 203 0 obj <>stream 0 0 0 -73 29 13 d1 29 0 0 86 0 -73 cm BI /IM true /W 29 /H 86 /BPC 1 /D[1 0] /F/CCF /DP<> ID :ƒ¹5O~÷ïöÿoöÿ½ûßûß¿ýûÿÛÿÿý¿ÿÿÿÿÿÿKÿÿô¿ÿKÿ­­zý/ׯÒý.µë^£€€ EI endstream endobj 204 0 obj <>stream 0 0 0 -59 39 0 d1 39 0 0 59 0 -59 cm BI /IM true /W 39 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID þh ÿÿÿÿ‘oÿó@hÿÿÿü‹¯ÿÀ@ EI endstream endobj 205 0 obj <>stream 0 0 0 -59 36 0 d1 36 0 0 59 0 -59 cm BI /IM true /W 36 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID &«ÿÿÈ=?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçÉÞ?þMnE× ô€€ EI endstream endobj 206 0 obj <>stream 0 0 0 0 50 44 d1 50 0 0 44 0 0 cm BI /IM true /W 50 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID 5Rjš[®Ýv–ém®Ýv–ë·[Öû[]oµÖÖúé÷§®ŸzzèÔŸ·¥zO«}7¥z¿Mé^­Å<@ EI endstream endobj 207 0 obj <>stream 0 0 0 -60 38 0 d1 38 0 0 60 0 -60 cm BI /IM true /W 38 /H 60 /BPC 1 /D[1 0] /F/CCF /DP<> ID þMQ@e[ÛÛÛÛÛÛÛÛïoooooooooo}½½½öÿo÷îÿÿëå:á× xa.HÅu×[X0°adð EI endstream endobj 208 0 obj <>stream 0 0 0 0 72 45 d1 72 0 0 45 0 0 cm BI /IM true /W 72 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID :‚A?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿnÿ¯÷¾´ûÖÐkƒX޾J’¦§TŸi¬4ÖÐaI¨(P EI endstream endobj 209 0 obj <> endobj 47 0 obj <>stream ÿØÿîAdobedÿÛC  $, !$4.763.22:ASF:=N>22HbINVX]^]8EfmeZlS[]YÿÛC**Y;2;YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYÿÀî"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?ôšçõï Zj¤Îˆ‰uýìpÿ_ñ®‚а5sË'ÓÛLÇ5¿’Ý#ƒô5.y—©Grû¯ÕOZô© Šâ2“F²!ìÃ5‡uá:rLbHIþãqùV¼é«4gÉm_²YÜ•¸òb¸ } YeVR¬R0A¬±êz›³Nu¼D9Ê>eìãùVž.ÔØ•ÄP°àœÎ¡FûÝŽâ;kw/ )c’£SQÕtè#hî$IsÖ5ù‰®}Vö¥aýÜà~B«o½h©÷!ϱ½q®0 š| gu*>cøö¬yÏ|:™]òI«z~‘{~ÃËŒÇwqÒ» +C¶Ó†ìy“®ÔÜã”[ÜÉÐ|4mÅâÝbÖºi­¡ž*h’HÿºÃ"¦¢±næ‰XÊúLRy‰c ·²Öš"Æ¡QB¨è§SY•³ªI=© ‚{KYË<1’,«I!œŽ6ħå__sýã×£g˜Ü?9ò©ïî þ½š“4f£Í  3Fj<Ñš“4f£Í  3Fj<Ñš“4f£Í  3Fj<Ñš“4f£Í  3Fj<Ñš“4f£Í  3Fj<Ñš“4f£Í  3Fj<Ñš“4f£Í  3Fj<Ñš“4f£Í  3Fj<Ñš“4f£Í  3Fj<Ñš“4f£Í  3Fj<Ñš“4f£Í  3Fj<Ñš“4f£Í  3Fj<Ñš“4f£Í  3Fi™¤ÍIš3QæŒû“4gØTyö4gØÐ™ögØTyö4gØÐ™ögØTyö4gØÐ™ögØTyö4gØÐ™ögØTyö4gØÐ™ögØTyö4gØÐ™ögØTyö4gØÐ™ögØTyö4gØÐ™ögØTyö4gØÐ™ögØTyö4gØÐ™£5is@ͨóKšÕ¢Š(¢Š(ªÚ=…ùÍͺ3x Ί(;þ4âLzo5z×CÓí1[®ïSÉ¢Šw  è)h¢Q@fK9¹lŽ"¨þ÷¹þƒúô( ÑEP£sMPœ4Må@ÌÌ3€zq‘ž‡¿ÿ]syëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yëýúÿì袀 ÞzÇÿ~¿û:3yêŸ÷ëÿ³¢Š3yêŸ÷ëÿ³£7¾©ÿ~¿û:( 7¾©ÿ~¿û:3{êŸ÷ëÿ³¢Š3{êŸ÷ëÿ³£7¾©ÿ~¿û:( 7¾©ÿ~¿û:3{êŸ÷ëÿ³¢Š3{êŸ÷ëÿ³£7¾©ÿ~¿û:( 7¾©ÿ~¿û:3{êŸ÷ëÿ³¢Š3{êŸ÷ëÿ³£7¾©ÿ~¿û:( 7¾©ÿ~¿û:3{êŸ÷ëÿ³¢Š3{êŸ÷ëÿ³£7¾©ÿ~¿û:( 7¾©ÿ~¿û:3{êŸ÷ëÿ³¢Š3{êŸ÷ëÿ³£7¾©ÿ~¿û:( 7ž±ÿ߯þÎŒÞz§ýúÿì袀 Þz§ýúÿìèÍçª߯þΊ(ÿÙ endstream endobj 46 0 obj <>stream ÿØÿîAdobedÿÛC  $, !$4.763.22:ASF:=N>22HbINVX]^]8EfmeZlS[]YÿÛC**Y;2;YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYÿÀî"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?î5­M4«¹sÂ/©¯?ûV±¯]°„É)œ6Ô_jÝñó¶û4çnþ§«ë÷b}Ä€ Dmª¾äÔNúÆ…t¾k<2¸|Û•‡õ­ß‡è¿éï›rŒþ?T}’ѱÈv ¡=l9Sý×´¾¦î—©Ç¤Ç|p€©.?ºGZâµßjwf°Þ¨I‘õorjޘ(OúÆjÅÐ￳î.%DS3 íÑ94Ev4œÜœbÞêáwo«iA%¹snFÙIaîEmøgYþÐÕ’û2]˜‡$uG×Ê­hšLº„ÿnÔ72•Õ½Ít1é6J²Gi:œ†UÁ¤ú <²æ‹·‘ÄøŸP¼‡]š(®eHÃ&[µw¯WvÊ—¤ªÊ2f¼çÅŸò1OþòJô¨ÔÇþè¥.‚ îåêQÓ´{m2YZÓz$¸Ìe² ûÕ£E'JV áõ½]5sqµ{{"25ÆÖ}íÊ1É;Hö&»Š*¢í©2W9íJþÞKèâö[k-ıear»œþ›5¬rßß¾§vö°†#5ÃÉ\“Ï_¼0MKáä·½†+ˆµ[¹¦Œ‰?´–$#׎9玵·cg¿“;å™ÙÜ噉É'êjÍ@¢q×·—†9 êÅ~Ómòãåïºa ŒcŒŸ®jÞ±¨F“j?k¿¹µšþ.WpÛø{'9Î@ºÊ¡u¦ý®Pg»¹hC«ù`Lƒ‘È]ØÈ­>eqr»WR­”piÓÞ\ "ŒO+ÉzÈÒ’Ú¤ÍÈè1Û®ißh’¬­u,…ð ÈçtJ[«ÉÚ:ý9ÕQK˜|¦ »Ám£ÞÍ£Í=ÙŽ< yEÈÏûzcµPÒ&škû7†ý&,32‹×›rãºl ‡8î;Žk­¢—0ùB¹;íRi5=BëN¸¶ìø6ÝK3ºF0Æ0>¸Çjë(¤†ÕÎ>æqim§Ãmwþ‡r^Y.Z HäçÕNÞI8éÖ†º‘4¿ ê æ\Ý*Y²\;‘È ó•Ràdžàä ì(ªæ'”áíoZ9’[kû‰ä?hhá–à¸! „wrHnzqÚ¯YO÷V c©]]ܳr “]§våè§Ðpsø×UE@¢rÈ,ä×ïÕåÜW&eŽVi+Îð’O°œÑ­^Ê5™-æm­£‰Y7Þ5°÷!‚’Þ˜Íu4QÌ¥ ÏþÉ·ûT¾l¸?9ÎHÉÛœ€sŒg sU|IsvÚÍ(‰o%XY‹c Õ¿Aƶh©¾·*ÚXã­âdÖã†iRâîÊ‘aãhÄj> î>å}*=2òæê{9úý¤Èe7Žß(ûàÄjñߌsšíhªç'â Ôwwí$ZâÛËq1QÃp‘¦_RFaÈÅMi}ÛÚ&£ªÍ oæ#$̦F.Û—xä•F3Îk±¢Žpå9[›õ·ðêCy{4NåmÝä)(~˜ðxR ýkNÒÊìå’ÏS»™.#($3—äËèséZõZúÕ¯ 1 ™íÁà´%Cé’? \Ãå1´H§½’Úåæ»Ž+X¼§G™›/ñΑžäz èª;x„ñB¤•@ŽÀÈT”›»VG5¦ê6V2^›Æa©¼Ï½ æG=FÜ~´ëé-m/a³žæM:ÀAæ&Ç1ïrÇ#pçŽ3ßÚº:)ó ”änf•t‹T–êú1-ÙK¹–E„d’ýÏÊ^ÜÖ†”¿h’ú;K»©l0©,’3!ÎâŒyÀ{õÍh\iÞ~¡ ßÚî#xTª¢ÛƒŒõRyÀïôÅMyn×P–æk|õxvî#2AÇáÍ7!(˜z<R[3Ü]ªXƒ¥¦sçÊ Ýžp@9õôè9é*XÖÖ(‹,jÆN>€ 𥻲’²05RÀßZÙÏy h’ù“«7÷FT© þ­uo6½¯ö”¶¦ç 1PWÆ:gœõ WGE4ÒG$·_hð¥Ü‘ÞO$_h*²‰¼1î³N[¿{ß·x-´{Ù´y§»1Ç„#ȹ€¹ã¿oLv­ê(rS‘Ñî$žúÖHõ'Ê^|]¼À®TØqÜwëRÁ=†¯«]¤z½Êï!"Ц]Ä.K(ôç§O”×SE>arœ¾».&£giu©¼M ß/ïÊ}Ð ä ÄAö4ýfî+am§-Ô± ¢_9îYYÇ .à 1ö=>•ÒÑK˜|§)i¨Íÿ¬Wlrp¯8;ÚËw$r@Ç$qŸj’Þ[aqq>—©Ý_4®âÜÈò#?c¸ñžÛéè§Ì.SŽÒn..nì^=A$™Îé”^<™\|ÀǰöÎ?ìh¢”ÊJÁETŒ*­ÙĶþ»Ïþ‚úÕf©óçóÜAµ=Ïs@gØÑŸcLϰ£>€ŸcF}3>ÂŒû ~}ö4Ìû 3ì(ùö4gØÓ3ì(ϰ çØÑŸcLϰ£>€ŸcF}3>ÂŒû ~}ö4Ìû 3ì(ùö4gØÓ3ì(ϰ çØÑŸcLϰ£>€ŸcF}3>ÂŒû ~}ö4Ìû 3@ͦfŒÐwŠ´—Õ4à`¸€ïAýáÜŸJãt?M£™!Ú"ß4mÁVþ”QWt8ñ •ûHîO­x¾MF²ÃïrH÷ô§xÕµù°?¸=½S{NòœdÞ¥-^D>,‘C®ïµ¯çï ôt…Я ˆäš(¤÷F´Õ£/™Çx Õõ›‚¬y#¡÷®ÆŒ©áùKõäýh¢—Ú¯ÜØÊøzÁ¢¿*Aבô©<}i=Œ¿HŸð‘Ü Ã!—#¿A^•¨¶GbvIíÅR‘µg/R¹»˜¶cJv.åOåŽ)>Õqÿ<#ÿ¿§ÿ‰¢Š“q~Õqÿ<#ÿ¿‡ÿ‰£íWóÂ?ûøøš( íWóÂ?ûøøš>Õqÿ<#ÿ¿‡ÿ‰¢Š>Õqÿ<#ÿ¿‡ÿ‰£íWóÂ?ûøøš( íWóÂ?ûøøš>Õqÿ<#ÿ¿‡ÿ‰¢Š>Õqÿ<#ÿ¿‡ÿ‰£íWóÂ?ûøøš( íWóÂ?ûøøš>Õqÿ<#ÿ¿‡ÿ‰¢Š>Õqÿ<#ÿ¿‡ÿ‰£íWóÂ?ûøøš( íWóÂ?ûøøš>Õqÿ<#ÿ¿‡ÿ‰¢Š>Õqÿ<#ÿ¿‡ÿ‰£íWóÂ?ûøøš( íWóÂ?ûøøš>Õqÿ<#ÿ¿‡ÿ‰¢Š>Õqÿ<#ÿ¿‡ÿ‰£íWóÂ?ûøøš( íWóÂ?ûøøš>Õqÿ<#ÿ¿‡ÿ‰¢Š>Õqÿ<#ÿ¿‡ÿ‰£íWóÂ?ûøøš( íWóÂ?ûøøš>Õqÿ<#ÿ¿‡ÿ‰¢Š>Õqÿ<#ÿ¿‡ÿ‰£íWóÂ?ûøøš( íWóÂ?ûøøš>Õqÿ<#ÿ¿‡ÿ‰¢Š>Õqÿ<#ÿ¿‡ÿ‰£íWóÂ?ûøøš( íWóÂ?ûøøš>Õqÿ<#ÿ¿‡ÿ‰¢Š>Õqÿ<#ÿ¿‡ÿ‰£íWóÂ?ûøøš( íWóÂ?ûøøš>Õqÿ<#ÿ¿‡ÿ‰¢Š>Õqÿ<#ÿ¿‡ÿ‰£íWóÂ?ûøøš( íWóÂ?ûøøš>Õqÿ<#ÿ¿‡ÿ‰¢Š>Õqÿ<#ÿ¿‡ÿ‰£íWóÂ?ûøøš( íWóÂ?ûøøš>Õqÿ<#ÿ¿‡ÿ‰¢Š>Õqÿ<#ÿ¿‡ÿ‰£íWàþþþ&Š(72ÌzØOî/õ?þªpã€8¢Š3ìhϱ¢Š3ìhϱ¢Š3ìhϱ¢Š3ìhϱ¢Š3ìhϱ¢Š3ìhϱ¢Š3ìhϱ¢Š3ìhϱ¢Š3ìhϱ¢Š3ìhϱ¢Š3ìhÍPš3EÿÙ endstream endobj 45 0 obj <>stream ÿØÿîAdobedÿÛC  $, !$4.763.22:ASF:=N>22HbINVX]^]8EfmeZlS[]YÿÛC**Y;2;YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYÿÀî"ÿÄ ÿĵ}!1AQa"q2‘¡#B±ÁRÑð$3br‚ %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚáâãäåæçèéêñòóôõö÷øùúÿÄ ÿĵw!1AQaq"2B‘¡±Á #3RðbrÑ $4á%ñ&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz‚ƒ„…†‡ˆ‰Š’“”•–—˜™š¢£¤¥¦§¨©ª²³´µ¶·¸¹ºÂÃÄÅÆÇÈÉÊÒÓÔÕÖרÙÚâãäåæçèéêòóôõö÷øùúÿÚ ?ôš(¢€ (¢€ (¢€ (¢€ (¢€ (¢€ (¬9õ›Ë·ŽóN‘­Ëá'€îí‘@›±·QÉŠfÎ(ÏùÅ>ŠfÎ(ÏùÅ>ŠfÎ(ÏùÅ>ŠfÎ(ÏùÅ>ŠfÎ(ÏùÅ>ŠfÎ(ÏùÅ>ŠfÎ(ÏùÅ>ŠfÎ(ÏùÅ>ŠfÎ(ÏùÅ>ŠfÎ(ÏùÅ>ŠfÎ(ÏùÅ>ŠfÎ(Í>:œQEäqKERddŒŒŠ( ¤„( œu zÑE-Q@„l’$j 2p=ÍP#‹×ã×›X“ìirm‰R¥mÇïõ­sû.V±M]™w#…Ïô¢ŠkS).H¶¨<¯! @|»F*J(¤jµAU.uKKˆ`ža³±©æ$à (ªŠ»‘nŠ(©((¢Š(¢Š)(¢€ÑÜ@“BÛ£q¹[ÔT”QMî%°U½^ÆÊq Ìá$#vݤñø (§waIÙ\µÑ\D²Á"Itd9¤¢ŠOq ¢Š) ŒÍ¸niRá}?¨©(¢˜TW7ZÛ¼ó¸He˜öQBW`ôDêvvâÜËp‰ö’ õ~â?:¹EÚ²waET”QEQES¼Õ,¬dXîn†BòN=p;T¶·P^Cç[J²Ç’»—¦AÁ¢Š·ËrŸ5‰è¢Š‚Š(  wÚžžWís·ôÊ“ŸÈTö×]@³@ÛãlààŒàã½U¸®[¥ïX’™ÑÜ@“BÛ£q¹[ÔQEOBº’QE†Cus ¤k‡€ŸRxæ›gt·q9£¶âhš3ùÜÑEU½Û“}lX¢Š*J (¢€ (¢€ª¨û<þXÿVãr{c¨¢Š›?çgüâŠ(ÏùÅÿ8¢Š3þqFÎ(¢€ ÿœQŸóŠ( ?çgüâŠ(ÏùÅÿ8¢Š3þqFÎ(¢€ ÿœQŸóŠ( ?çgüâŠ(ÏùÅÿ8¢Š3þqFÎ(¢€ ÑŸz( ÿÙ endstream endobj 210 0 obj <> endobj 215 0 obj <>stream 0 0 0 -59 46 0 d1 46 0 0 59 0 -59 cm BI /IM true /W 46 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4 ÏÿÿÿÿÿÿÿÿÿãþhŸÿÿÿÿÿÿÿÿ€€ EI endstream endobj 219 0 obj <>stream 0 0 0 -60 41 2 d1 41 0 0 62 0 -60 cm BI /IM true /W 41 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ À úᄃÓÓ×D ½ ý7¯Óëz×þ¿ÿâÿêúWÚÚV %SD¶¤ÕZì0¼/Ûþ÷ïöûÛ!¡ö]† Ǿø}ðø`² EI endstream endobj 220 0 obj <>stream 0 0 0 0 44 59 d1 44 0 0 59 0 0 cm BI /IM true /W 44 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡–h/ÿÿÿÿÿøšÃæ€A ¤Õ?ý¿¿oßïÛ÷ûöýþýý¿¿oïßÛû÷öþýý÷ß÷ý÷Þ  EI endstream endobj 221 0 obj <>stream 0 0 0 -59 53 0 d1 53 0 0 59 0 -59 cm BI /IM true /W 53 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 5®µë^µë^µë^µÒ׬%‚á(ªª¨wù¨#Ã{þßÿÿëúà¹5q]uUP ¤ € EI endstream endobj 222 0 obj <>stream 0 0 0 -59 62 0 d1 62 0 0 59 0 -59 cm BI /IM true /W 62 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 5E©5ZÿµÞ½{­ÿ]ŠÿÚþ·ý¯ý®jým¯û×kþõÚÿº]ÿ[¯~õÚÿ½qõ¿íí[þ×þ×õ EI endstream endobj 223 0 obj <>stream 0 0 0 0 45 46 d1 45 0 0 46 0 0 cm BI /IM true /W 45 /H 46 /BPC 1 /D[1 0] /F/CCF /DP<> ID & æ2D=a>ƒéøë_ÆS•:Óûÿÿÿÿÿÿÿÿÿð EI endstream endobj 224 0 obj <>stream 0 0 0 0 51 62 d1 51 0 0 62 0 0 cm BI /IM true /W 51 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID &£$Vj Ì>}ÿÞCˆÈ1®û·ú÷¯¯zþŸ÷¯éþŸ:/ú·×¯Ózÿ}>¿W«ýé~ÿz_¾•ÿ«}é7þ·§  EI endstream endobj 225 0 obj <>stream 0 0 0 11 40 19 d1 40 0 0 8 0 11 cm BI /IM true /W 40 /H 8 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ EI endstream endobj 226 0 obj <>stream 0 0 0 -44 70 0 d1 70 0 0 44 0 -44 cm BI /IM true /W 70 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡Í@æ ôÓÿÿôÓÿý~Óÿýoµÿýo~å ï^¿}kýï«×ÿ}küo«_÷×þúµÿýo­_¿ÿÖúÕûÿñ[õ` EI endstream endobj 227 0 obj <>stream 0 0 0 -59 50 0 d1 50 0 0 59 0 -59 cm BI /IM true /W 50 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID A°‰áûšï!£\7†÷÷¿{þýÿÿÿÿýô¿ôµý-p–– Â|ˆ®*B‚0ÐTP€ EI endstream endobj 228 0 obj <>stream 0 0 0 -59 66 0 d1 66 0 0 59 0 -59 cm BI /IM true /W 66 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID :†Ÿÿÿÿù}}õ÷þ¾úûëïý}õ÷ä…ë~—öú_Ûõõ¸®ëºý×uß®ëºïÓ€€ EI endstream endobj 229 0 obj <>stream 0 0 0 -59 42 0 d1 42 0 0 59 0 -59 cm BI /IM true /W 42 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿóPeÿÿÿÿÿÿÿÿÿÿø€ EI endstream endobj 230 0 obj <>stream 0 0 0 0 51 62 d1 51 0 0 62 0 0 cm BI /IM true /W 51 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Ðpn#R< ë ZֵʲÑÐAô‚ õzëõëÿ¯ÿ“Uÿþÿÿ½ûïuÚÛ †ÃÁ‘C±Þ÷½ï ,0°d²D@@ EI endstream endobj 231 0 obj <>stream 0 0 0 0 16 59 d1 16 0 0 59 0 0 cm BI /IM true /W 16 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿÿÿÿÿÿà EI endstream endobj 232 0 obj <> endobj 218 0 obj <>/Length 2471>>stream xœí]=lÇþ¦ ‹sq.ÈBj¨‚,ÄâXˆ…XX”±I…*N…XXMXE€@ °8.$ HAt!nÈ‚" 0DbL±WH… l¤BWä ³ð“7ov÷fn¹w·wÜYÍW,¿›Ù;¾ýæ½ù[!¥Ä âߟïòÕaQÄS¦€nÀ»|šD2]y)ÕÃO=é__“NCdd[â{Yꎠž(Þï:5à(-=YÈEø&-`XÆÍ–|ÜØ®F³…)I2xÇÿ¯Å å ÿ 9ò=ƒ¸ ùX šÈ–ç%¨" õ£õÈWBü…ɺ·™|#ÄŸ%’ERÊ©®ÉÄ,ÛÜëZòïM¾ë4Ð.ì+Pµ‡€UFy4”R’ÆÔ¢)ºH]ª †ŸÖ¤}M: ‘­Ë7í°Rþø(Q<ü ±‚á×LÞ×¼Â2ð3a็€ë©¿i&’W3IxG“$y (%Ø.R¦á¾ÀªTG‚&« q¯%ÉD‰Üo©-…E4(Z _ˆðᤋ¬q:¡æýû†¸Ý’³lùw?Ç×ý§C?w›¸»™þ¼§ÂŠÕ¡þ±%0Jr+ÏÑŸÕp>å@>e9a‡Ržø'0{ÒÕ(ó)CÑP®äÈÝüõ±˜š'ÉT¡V…¸/?ÛÄúKñÅ‚ü¶‰ßmbØdò# ¿@;}JpóýpÎ)%ú^gh¥Ô.àzWÐßo˜ïd’°C)áÜÊt\x"Õq·(åªøtA’h¹² w›¾Ú!M)/oBgÀ ö)WÕà î±R^”—áÉœ¸r I7âNK>l*Û=÷§€ÝÏ@v›5×Ôéxb«O‰Ü1x—³…ÚÖí£”f"J9hÂÝ úîŽ?úîŠÈ]ŽX)§dïK™ˆ_'¨ó ÑwFøœ9£oúI—Ò­Fô v+ÿœ.AMÊ„Â|Ê=ˆ;F÷ä7Ÿ°sÉ)ŸoâA£ïÕF„µ>%Ø(q]è#£Ÿïû,è%\€:%E«cªÃ§†5JI ºˆG³JHQÊB|ÉJIäKZ’Ä”2-'Å7aÏe8ØóEKþ5ìÔä”ë-¹=TÐÝ»»ÅJ v+÷]ÇÐH8„˜…¤#qMb§Š,ã¾ÙÅ Ø,wüÓC¯GBPŠ> ÉÀ¯ƒWï>žÊÑ!]&£ÌÙ1ôÞ`E`Cª#A “oòˆ‘¿I!AÔœˆˆÛR~%p”¸ÁéýQK‘•U~ºÝ£&nmÆÈ<ذ8ú†›™1hwó)ó?Ãöi\›‡¼èòÌ úèü<—‚œI2 r„³¥1J¸ÞÊ8î ¬Iu$h²Ö_¶$µƒk<ŽTÑÈÈù{)ÎÎûa8ù©÷üñЧÔ÷Òcß0H6LA]³[) Ï®9±'tnFâ!È¡_HL§lоáÜÊ8R¢ïuŽ ŒI“I‹¾3r®+T]˜X ïDæ\j2âW°<úåà|iêS9¼åœâ_Ÿ,Ÿ=ãQ™š1ýôßÈ?5„™R€”Z¦”pnå aQt=LÍGÒ)¥7‹Î­òL,ÚëÍ!_KùÞ(eMüÀ°Q);ïPÏ3áס@è¹#âù>ž7”ò¢Üxèó[ ñ]K®4³†Ý© %2gÀä0Á$Jg”êè”rL˜çŸ÷µô‰á y¨±‹ù‡[¼ G‘ eå¶“ %êÉÂÃÔÁ>¥„s+OæLÂ÷À õ—”m_uc­G}R“ô”@µŠ·jJ 6JrkÜäŒq ª”í×êlšå°íù ‰?ƒ3rø˜ä°U)yîªÃÄ`*å>÷ => >­e-Gõ}J°Qþ Ì•¬VÕ@,ú®sb>É6àÚ. ã±R)áÜʉ#m)Ü çIó^7)G•RÏ œq1ø0ÐJÈ­`S÷}DGÃ-Ù™0R 3yåáì [¾TR)Uóý 8[¾ŠÙŽ7lmô´›äÒq¥öp0¹0$Q5mUJÂ+áŒrrH*åS6Ù°qß¡SŽÊç« ÚÜÔàiT)‡jîÊj”mp±”u³ƒîC4ÜjñlX¬”gÄÛÿæZdäPúø”»ðMöò"v=\‡Åg†äÕô)Á±Î–›ð[4ú)eliNH‹*e½bJ録p²‘ÍÊérßøÓ(™Rºhn›1{,×þe'@Ï»•Rý8bÊà@†O©öi Hrn[Ò.=^ªû2<¯˜RÂm0T4ŠÛ/ÝŸ©>ĸŽíJ©:†¼!ý‡}{ÜÊ_M¼ôi«Ï˜x•£o¸•àE£ ¥ ô¨…µ_Of½R ‚OÇ*åBð‚)s£în°W·&µhºF•® ½PþžðyÚ4óDâUPJòfþãffŒ9GtúÌñ'¤O«¦Ržá¯ºÅ… Ùu<ÔÙG‚¨ý^´¹¬ä¨C”R­×ÁpÝ]1´yÌf2?ÉÀô2Ú¼SõôÚû8 ˆt¾Ñ ’Yz£ؾ.}%íŲñ2¡n”è1îpÔÞËty4‚] üÌ0ÞZ©‚R‚'üžu33Š@F|=tÄcb¿žK1Êñö›ð[&óÜìažÊ¥×q‹Ç„"ì²îhqo‘n¯w8Ew°oåÚf£„Ûž  ¤*åŽ1]È|é%à-†ÓˆÀd'‘§Æ $̆{‹ÛôÔœTD)Õƒ'j®ùh¾ƒÐç­ç·áõÅh†Ê%^p„èºÐGÄ’ÞÂJÆIR#‡#ÔqŸMó]ZÜS£¤æ{†_ï0;öƒlC\3DqÛõ6OV=Õ1J8·rœèŠÞ›ÇG$&–Ù¹ümÔ.+e”䉻پc‚Þó¼ßëí"&ôz´C£#3ÊÓB­>Þ·¤¶ l£?ñ‰SÁÐN¥ŒòªPCþÓ–ÔÖÁÄu†®WÌ(´26b1a¯šQ¾ni„­ Ö\w°×·K«Œ’ÜÊ7öÔÖ!‰ež[sÜ«é­2J‡÷Î(Jg”¥ƒ3J‡ÒÁ¥CéàŒÒ¡tpFéP:8£t(œQ:”Î(J‡ÿÄ]«É endstream endobj 217 0 obj <>/Length 4375>>stream xœí]1lGýSÐSH…TD œTØY8…S$ØQadaRaVq9À:À6`«¸+äB.äB.ÈÂ.” à p6¥° » ‹¨° û § ©0 ³˜›ùfvvf—Ü¥v%’â+VÿÏþ]ŠŸoÿÌü™aœsÈ“L÷2ª- +Ì-9ðIËm€BÔU-€¢–ï–ØJ“‹ãMþ¯’¬n¹É×°ðW^h›Õ ü\Ô»| F'l•kVÂ'Ææ9o2§\T{£tôq—«Ïh„¸rß`˜ÀÈ)¡ìye‘ç•‘å¦0îªh7 0c=ÆÀ+.²1\>räÈÔ)_œÎñû`U¯Èv)ÛAm‘-|-Mòõ=Æ~äü5ƒgÀV8ÉX‰ßªÃ­ Ú4•ë‹Bº.°GQ"U‚QWÿUÞEªx- Ž*…ó<ú 5xM}F#Ä•ûÄìœR@|Íçûš)[ (–ñxZÈ’@(g;¨ª’6´°Îb ZEUI± 0 °…w-‘ޤŽ™:åK&_agóú2`JM`‚´$SŠ:/H¦ä³ëu¸W ˜R„›¶ çI<¸~¼Sf„Lò “<‘+YvsĦ žZ5°Õ"2߬Õ\ji/7ªÀ# (¿õɾʼn‘©SаR¼ì¦„)ÿÎ`™òº„cÍžã? j¼­ÊX嶪:Š»|£oGOkÓ*•Œ˜22uJ@¿ÌµÆsüÒ÷Æ.–Øó&¿ˆ/$Ä©.5‚&ð'A>÷€?AÝh„ôÈÚ)w˜¤¥|Þàs‡îzTbsM.޳M¾…j@n»R—6‘*¼‚¹S®…¤F’@¯r€@PïVTÍUD±¦©^äðAócËÜ”óçl·3UMÌè”.aŸå|‡‰—»¼ê¥ª\Ù:ˆœò€Áû|z+ûl˜± ¨É?bÊôÈÁ)s{ƒÏÇįÁÕûð°W­,R# I0h²'õz]Å„k%¶Œ€J Òd)OaÄéta~hÀE¢ÈÒ øFõcó?Häà”ïX™Kø-g22qù⃠,ÔÕ©‚ç ¤ðÕºf)Ї7xÿÇ”OÃĦ3ª…n(S8Ö†×ß™®|S¦Æ¿eŸÆ¶SÞ¿×îÃý \³ÂGR# IðYŠì ç-!'­#9m”•‚Bù<(øjÈl–Æ#ìí«{®>£~¹o0LÈÇ)sÊ­8¦$x þ°xêòS¦F> ð 2î°å3|í-c·1uí æX˜ÎÂ;Œý¿~JKg…zÇJ]³ÕËè+w¢|H›µNCñ,Ù= Ó3À¾âüwÕC)Z÷¦]¯"U5Ë ÓçFý”¹T|À¤‹Lá/XÜqŒË£ ©TƒÎê× ÞÈ*™‘úµ>kdÝèqº- D„›ÁÿêJ‰Õ±ÏHT~…ÃÉÉ‚ËÍ)s Ë ˜r-óµ}9 #™rä¼ÔëŒ-!Sn+oê~jpÖ¼UQ•ý8ÚL9¢S®'©ÑQê8ÅB€f±™I¢H™Fõc0L$§ì ¶|#ôvs^vUƒ.ªßCDMuú]BL_Ò±¡Cßù*ü·wEžN ·Ác:VLY©C½[LI  [P©kºc¨ŠœTÛgóãÜÉÞØÄñùq9eG•ï³ê8¯…‡¡•‹BQa­¤«-YÀ4O¨jÅ”¶ u½z‡ÝØ>˨¡“-§³{ƒg5ÌH—ü#uæl¶sÆ}³/ð^B8ÇäŸLɆN0¯âéÐ'ûÁÅ1Ç”€oð7â[ÏæqI¾ºÁÐöp· ËÚQå†1ÖgJql[Yº„M)űˆå “|]ÆÕíSN„JåïklJǬN²pd…¶ÐNú3„3 “WêòÇ⟊¬<ø%ð…Az}S`—ý´0·íRü1eÆÈny‚$1er8a™SÞy¨F©ˆè„23¨ËSz>#i°,`)§õ³ß1‰øµg¹8‘g^gBŽèé§’$üÍß)gðÑŽ7ö/<Š}Ý€ó4#KÕ6ÊÆ ƒòQêÚ¡ ™2éZòBÀÈߪÓ_a¦@˜Aæ–Ž—˜êli[]˜&¦´{UçüÚª¹W3Ð ä¦ÄÉ~¹8ÃÍ…Æþq“¿Ä kB¸b•ûF>R†0ÓKÚQ嬧(I—ê‘8å.fvå ÙÑmåIiTƒäNÙÆV…cVÅ”3»=Tˆñ˜·LfmnËÅdæ¥(ZøU·ÁŸÀân?Æh˜1Sd4,×Îsƒ1e‡¤!ÏL¡L(Õ¨òÛ˜OÙ€¹y±k»5ˆ¨t·ŸbÊÁîØÅ _C–CÉ”qà-½]n[“¥…Sf„#_ž`°QÆ„„v}šÝÛ•ª©Üö’-öÄ?>JØC9²¤³p M{øc™òl ßǬ$»@¡Sv4f?²Å”fe"ÐLÙoÓÇzC_$ùÚØÅ/&·E‡ å`B…нas3*Û’Ñ–Îzog¶¬6¡b¥`oŽ9HðKºc2ˆ7øÏ½öÀ cJ»ž6ŽîįÛ6Hè;¦ÌyGÑ¡B™…&Žm0¶Ø7­ï¡bJ8\oåIfÊ-ëó^F¾¤ tÁÚIeÊgG·Íí£ÌœµÌP’ o°öY8U•ÂÊ9¾*Ò¹:˨”Í!ų [L ¸–KqÄ”é˜òà©[›íÀÁYµ™ÙÈŒ„ÐÞgQYEÇŒ~dJ‘e2´ØíÏøÍò(´›Q!n79•“ ØîVl¡«-¤x¶!dÊ3¬ý6÷Ù‡wý˜2yË"ž}£×¨ž¾¦2TúÇã”2lïa\çľ¾»åã˜9eŽà¨‚¿«CáÚùãB&êé œÂüQ>YgÉ â£uÃq8¥Àk–ëZ胇®LImêi+;8ÂŽle·ÞàÄÜ¢^ViÌòczÁáK–Nxq<ßÑ19e?yÚ%O2SnãìˆWxÖ(·ÍŒ@¾Ró(,Kµ¤V¼—ÿõoíýË”Mœž7êB7ø¿øm-´ÑçÂÛFÂ6ŒÁØ úŸÞìl§çªj¥àž+ÞâÔcrJV~Lï”´]Ú®D˜ó™¶·ÏN`zØG}vŒÃ¦Ê…@gۖꜵUƒfvmâ¾ãá=öìgvê)êýìОe[XÕ%«Ä,8éE¾À–Ñ)ÂdJS›ù=<Ãã% XøLÿoM!X;ô/SB^ÕV˜L‰]Aç¼[b+M~Û Ž Ž«8[wºl½Ä[°²Ëïâ²—ÆLÖs–­ì$$)½>ŽÏ)'üðUš»·zŠ)ýt/ºd §˜³E®ê'!•jÐÙ¬­©± WZ“ôæ\n>‚saƒBocµ·ýf{A-`A®2Aó£œe'ÌaŸ¨#È$κ6[P6lÁ—ô5SNanå¨ gÓ“Ç*u=÷÷-“#ã¿éµ7°9EË'[Z%Š)¡§t¡“ÜúnE•G¢»%½Ê5âÜ:¦fž˜Ö7á9ƒ‹#¦ŒEˆ)­6‡#¸žD›ð9ÀJt+{øó¼ò‚)Ú:båé“Ì””ä;ÅÕ*_F |«÷¹ï¹¼ÑYy÷8aÛ7`­uú)³ÞQtȰÊà—G™ºK (Üd·gø­ŠVg0¥÷&c·ÃüÞ{/ä oè‚«M~£$³†ùn XëÒ7ÀO2SÆÅ”‚Ë-ÍY²¿qþ«»"«„Õ‹ä y ß™²Üegø±ÔL3ؘ§ûÀŒ.'Cc?²Õ²Î:‹ã¸²«œý€{@o1åÿý•ýn endstream endobj 216 0 obj <>/Length 2695>>stream xœí]!pÉý(` $`‰H@'° îÀ œS•\U’ªÜð‚D b`9U©vUdp>  ˆ‰JUÎU]u±vÁ HDÄ68-ð‚N÷ï™ÞÞîžÙÙ™Ù™ÞM¿RúÿéÍÖ<½îþý»cà02À7Rx‡@ÊïHà)¼C e€w¤ ð”Þ!•”7ÞùDÙF…ŽOwP6¦Š”7.®S¹GGÎNÉÔ@ÓÂT‘’óïàK€9ú”0LPåTf ïô •” ¿xö9±Žˆ—Xv¸Ø”¹_$¢:ÚÙšn= +F‘’ ’?bÉùà€M×Ù ”³‚TR® œò†Ò›§(Yõ’w.ÉäÝÊFBM›ˆÊ ìô#BB=À†WíÝ9Â@ô,Û©¤äx¢&$4ÀWŒŠS¾@¸åM ÎÉÄ©¶°Aæ3âåfÌ?uTX J9•EJ1ç¿|z~ëDÍk¿%Sê¥~4”rÚ0Š”¿Gx ÞÁ%ÖpÐF+.Ú£œ€iÃ(R.!œÌûñh5¶õ:t¸Þ¢_|@Æ Y ­„‹„æÛ{d˜ûæ-ø*À+oá:QJÏ{p~ Ëkðâ~Ãÿyú4:ˆ@…Ð|O2‘òü,ûЭÔÛeŽ74âéS™“Œ·æY"DA)½G6¥ü7ÀÇÞ”Ò?Œ©”] ¦Ô%–餴5O6èÊ/ƒ‘φªÔúgAPÊR1&)ëÍ­L%¥1î€h'Îr_1VûY›Ôù$Î`7”²>Œ¿?åk„O}$ebï°['l¿©í®qIƒžÐ§ô㓲Æ1x ) =ÓaÑNÄØ×~¢d¢¹R¥¬¹HYÓ|#n[.pDPöQra~Æ”šÊ³ì1ŠV›‚Dò¬ãSdþ$°=×½ðl¿]a–0>)kLøu)%~Ì"ªÅGîŽ r~¼Ouº±)÷lÅ Êl¥Lj: 3HæÄkÏ󺢕g©}ÊMAÛaóÍx|-óϯÞgì ç\-zj]jv ÞnØ÷VÄoT°ýv…B.RrÚ®A*°pbĶÓN44EìSû~BRÚÚɪBdÆ2ÔŽ'åæûÞÜ~~‡qãÛs×7>hûí ³„¼JyB9`ÕâLu±º”4âü5yv¿Îm¾|N¢x94.–·Ä‘ƒW[Ípcòš#ýÆ=¤Ô7*Ø~»B†Ûœä"eMÑÊD¥œ'b¥C)åòÿ6PJÞˆËÑ~Mƒò+S€×ÙÕL^çñ°”²Tä}N-ËÎSû”7€v-´ åôÆp„üUlJ!Mʹù¯%Ô7>hûí ³„b¤¬xuÄd”Ræɹlö¼X‚Å5¸8'£W‘’¹˜ôº JY2Š‘òåƒUÕ­”]{IBïh¾Fi¡ô÷];§êöAÛ]ö˜÷H© ®ù;†/2Õ{°uÆöW±}Æxe>¸ág¹_Œè¨.»Ï`ËÅ1éŸÓn¨¿ú˜A£oÚ}ã˜þðuœUa–P˜”o* ¡WJµÁ•ñÒж¿Ê#Òv1(ÅH ”ãXU2_¹ÓŒWûØh‰iÆ«=¼ÖfªæÕAd²Ävœ;¼ƒø ]!GêZ˜f̆¤ìRûTM½¸R>‹ENî\Zá’BKê•£K´É¥ÚÑ@-w"(e©(LÊ —씜±+FiB œQüëøçm`ߢ\5q§Ë„)g©±„6©¬þBPÊrQ˜”+ÿ©j+¶RF߆³Ä%¶A)KBaRmXIú½\œÔÇ$»M¼×eüÈUð[š0üšÀÉùè6üé‰HGßçùø:2ã-²î1uV^çÁw™ãÞl¿ºÃ‘õ ¶ß®0K(ƒ”µïÆ&Ñr½4»© ™½ðÁ8¾•¶µµKPÇ  $RB!t;8®Rª(c»+BÜóU—RA‰å~,¢¹‡bZ\*åK÷&—vØRÁˆ&ª;LkÚBœr|Ì®RFD”Gý=¤|´þ†Á†³]3Èg1”AJ¨j%ød¦/=îÀ”a)ß=™ûx> endobj 234 0 obj <>stream xœ]1nÃ0 EwB70eËQ \’%C‹íd‰ >/FontMatrix[1 0 0 1 0 0]/FontBBox[0 -11 1000 1000]/FirstChar 0/LastChar 13/Widths[ 752 636 636 770 774 712 652 500 924 770 995 637 734 372] /Subtype/Type3>> endobj 236 0 obj <>stream xœ]–Kn9D÷}оøÿBmì6 Ì\€¬Ñ ·my1·ŸÈ •exR]EòeFfñåÓ—Ï_î·÷ëË÷çÛúÏþ~=n÷í¹ÿzûý\÷kßÜîë®Ûm}Ÿÿ®?Ûãòòék{üûßc¿âý¸ØñÄú¶í¿mÝŸíþc¿¼³¼ÇrÙïÛ_?YcÆ+ýøãÙ¡„v29Jè–!“úeÈx/aX†Œ·ÆeÈ„M´ ™Ä_ó2d²“°,CÆ3¬ËñF¶ _%ìËñ<äº ß$Ü–!㳄û2d<Ï|,C&Êʬ–¼A–²`µäMEB°Zòfþ VKÞÌwÁjÉë ÁjÉ›e_ VK^·KVKÞ$Ù°`µäÍ’: VKÞ´JVKÞÄwÁjÉ›¸2X-y÷«%oN‚Õ’7ñÌ`µäM²‘«#o’‡XÝà•Ä:°ºQ_©‚«#ol8°º û:°º¨D¬.©7Xy÷«+Z}VW5“¬®û‚ÕõóT`u«&ÇÕmZd—š™D)¨Y_؇š6ƒ1©‰¯Q3Ïp15ssQÓ°5{ÁƒÕ'-7¼FÍ´{°zòF.VOÞ ©ƒO©Y2Vß5u¬~=Ï V¿i6>/FontMatrix[1 0 0 1 0 0]/FontBBox[0 -18 1000 1000]/FirstChar 0/LastChar 191/Widths[ 667 644 644 636 636 636 636 636 636 636 636 636 337 636 636 636 802 644 644 644 337 856 318 318 513 320 320 320 320 565 640 640 640 320 640 837 687 1042 678 734 675 716 716 343 343 478 595 733 493 435 712 593 830 712 924 716 720 850 652 678 493 343 924 684 635 411 615 613 392 695 974 612 863 592 698 635 295 634 352 550 390 390 278 521 818 635 278 635 634 361 634 318 275 278 337 579 592 525 603 775 318 611 592 762 343 687 593 665 343 712 716 675 716 837 415 716 830 478 682 493 595 435 678 850 716 635 460 748 732 557 635 500 684 337 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 989 686 575 787 734 687 675 343 678 595 593 343 712 716 1042 457 478 493 716 652 435 716 924 712 457 632 636 645 636 1042] /Subtype/Type3>> endobj 7 0 obj <>/FontMatrix[0.001 0 0 0.001 0 0]/FontBBox[-182 -235 1287 928]/FirstChar 0/LastChar 0/Widths[ 0] /Subtype/Type3>> endobj 238 0 obj <> endobj 149 0 obj <>/FontMatrix[0.001 0 0 0.001 0 0]/FontBBox[-18 -235 606 928]/FirstChar 0/LastChar 0/Widths[ 0] /Subtype/Type3>> endobj 239 0 obj <> endobj 48 0 obj <>/FontMatrix[0.001 0 0 0.001 0 0]/FontBBox[-198 -235 1417 928]/FirstChar 0/LastChar 0/Widths[ 0] /Subtype/Type3>> endobj 240 0 obj <> endobj 162 0 obj <>/FontMatrix[0.001 0 0 0.001 0 0]/FontBBox[-4 -235 606 928]/FirstChar 0/LastChar 0/Widths[ 0] /Subtype/Type3>> endobj 241 0 obj <> endobj 77 0 obj <>/FontMatrix[0.001 0 0 0.001 0 0]/FontBBox[-182 -235 1287 928]/FirstChar 0/LastChar 0/Widths[ 0] /Subtype/Type3>> endobj 242 0 obj <> endobj 8 0 obj <> endobj 2 0 obj <>endobj xref 0 243 0000000000 65535 f 0000044617 00000 n 0000116865 00000 n 0000044550 00000 n 0000044194 00000 n 0000000015 00000 n 0000007689 00000 n 0000114192 00000 n 0000115952 00000 n 0000110750 00000 n 0000044665 00000 n 0000044877 00000 n 0000045103 00000 n 0000045346 00000 n 0000045609 00000 n 0000045824 00000 n 0000046097 00000 n 0000046346 00000 n 0000046558 00000 n 0000046802 00000 n 0000047052 00000 n 0000047315 00000 n 0000047551 00000 n 0000047749 00000 n 0000047968 00000 n 0000048211 00000 n 0000048445 00000 n 0000048696 00000 n 0000048932 00000 n 0000049153 00000 n 0000049402 00000 n 0000049622 00000 n 0000049875 00000 n 0000050054 00000 n 0000050305 00000 n 0000050548 00000 n 0000050751 00000 n 0000050975 00000 n 0000051244 00000 n 0000051483 00000 n 0000051707 00000 n 0000051961 00000 n 0000052202 00000 n 0000052427 00000 n 0000052634 00000 n 0000093345 00000 n 0000089104 00000 n 0000086650 00000 n 0000114834 00000 n 0000052883 00000 n 0000053072 00000 n 0000053284 00000 n 0000053489 00000 n 0000053703 00000 n 0000053925 00000 n 0000054138 00000 n 0000054341 00000 n 0000054545 00000 n 0000054722 00000 n 0000054895 00000 n 0000055093 00000 n 0000055311 00000 n 0000055511 00000 n 0000055694 00000 n 0000055896 00000 n 0000056085 00000 n 0000056287 00000 n 0000056495 00000 n 0000056689 00000 n 0000056907 00000 n 0000057120 00000 n 0000057356 00000 n 0000057647 00000 n 0000057884 00000 n 0000058144 00000 n 0000058345 00000 n 0000058529 00000 n 0000115497 00000 n 0000058797 00000 n 0000059030 00000 n 0000059272 00000 n 0000059460 00000 n 0000059687 00000 n 0000059916 00000 n 0000060121 00000 n 0000060343 00000 n 0000060564 00000 n 0000060791 00000 n 0000061018 00000 n 0000061248 00000 n 0000061489 00000 n 0000061710 00000 n 0000061883 00000 n 0000062081 00000 n 0000062291 00000 n 0000062507 00000 n 0000062725 00000 n 0000062943 00000 n 0000063121 00000 n 0000063352 00000 n 0000063592 00000 n 0000063823 00000 n 0000063994 00000 n 0000064216 00000 n 0000064420 00000 n 0000064591 00000 n 0000064796 00000 n 0000064964 00000 n 0000065135 00000 n 0000065341 00000 n 0000065519 00000 n 0000065737 00000 n 0000065954 00000 n 0000066157 00000 n 0000066369 00000 n 0000066620 00000 n 0000066804 00000 n 0000067006 00000 n 0000067233 00000 n 0000067451 00000 n 0000067626 00000 n 0000067852 00000 n 0000068070 00000 n 0000068282 00000 n 0000068462 00000 n 0000068660 00000 n 0000068904 00000 n 0000069134 00000 n 0000069360 00000 n 0000069570 00000 n 0000069744 00000 n 0000069966 00000 n 0000070188 00000 n 0000070394 00000 n 0000070597 00000 n 0000070787 00000 n 0000071017 00000 n 0000071226 00000 n 0000071454 00000 n 0000071708 00000 n 0000071926 00000 n 0000072157 00000 n 0000072339 00000 n 0000072556 00000 n 0000072773 00000 n 0000072957 00000 n 0000073214 00000 n 0000073379 00000 n 0000073613 00000 n 0000114576 00000 n 0000073829 00000 n 0000074065 00000 n 0000074288 00000 n 0000074502 00000 n 0000074735 00000 n 0000074935 00000 n 0000075106 00000 n 0000075317 00000 n 0000075522 00000 n 0000075720 00000 n 0000075938 00000 n 0000076124 00000 n 0000115202 00000 n 0000076314 00000 n 0000076543 00000 n 0000076787 00000 n 0000076985 00000 n 0000077204 00000 n 0000077432 00000 n 0000077639 00000 n 0000077807 00000 n 0000078018 00000 n 0000078258 00000 n 0000078469 00000 n 0000078686 00000 n 0000078941 00000 n 0000079149 00000 n 0000079380 00000 n 0000079620 00000 n 0000079846 00000 n 0000080113 00000 n 0000080336 00000 n 0000080529 00000 n 0000080780 00000 n 0000081037 00000 n 0000081272 00000 n 0000081510 00000 n 0000081687 00000 n 0000081926 00000 n 0000082178 00000 n 0000082405 00000 n 0000082587 00000 n 0000082791 00000 n 0000083049 00000 n 0000083278 00000 n 0000083503 00000 n 0000083718 00000 n 0000083913 00000 n 0000084150 00000 n 0000084391 00000 n 0000084610 00000 n 0000084843 00000 n 0000085089 00000 n 0000085296 00000 n 0000085524 00000 n 0000085716 00000 n 0000085925 00000 n 0000086148 00000 n 0000086376 00000 n 0000086595 00000 n 0000095387 00000 n 0000044370 00000 n 0000007709 00000 n 0000044171 00000 n 0000109053 00000 n 0000095474 00000 n 0000105793 00000 n 0000101216 00000 n 0000098543 00000 n 0000095670 00000 n 0000095920 00000 n 0000096143 00000 n 0000096363 00000 n 0000096591 00000 n 0000096793 00000 n 0000097027 00000 n 0000097195 00000 n 0000097431 00000 n 0000097658 00000 n 0000097878 00000 n 0000098064 00000 n 0000098307 00000 n 0000098482 00000 n 0000108690 00000 n 0000108730 00000 n 0000109032 00000 n 0000109460 00000 n 0000110728 00000 n 0000114365 00000 n 0000114749 00000 n 0000115008 00000 n 0000115374 00000 n 0000115671 00000 n trailer << /Size 243 /Root 1 0 R /Info 2 0 R /ID [(ŸæŒªpz`Yl9}ô’ `o)(ŸæŒªpz`Yl9}ô’ `o)] >> startxref 116976 %%EOF ga-5.9.2/doc/armci/www/000077500000000000000000000000001500715745200146105ustar00rootroot00000000000000ga-5.9.2/doc/armci/www/armci_doc.htm000066400000000000000000002506211500715745200172500ustar00rootroot00000000000000 ARMCI - Aggregate Remote Memory Copy Interface
Initialization & Termination
Put/Get (vector)
PutGet (strided)
Put/Get (contiguous)
Accumulate (vector)
Accumulate (strided)
Accumulate (contig.)
Register Ops
Fence / Wait / Barrier
Aggregation
Atomic / Sync
Memory Mgmt.
Collective Ops
Configuration Info

ARMCI - Programming Interfaces

ARMCI programming interfaces are explained below. This is an up-to-date document that containing all the supporting ARMCI APIs. There is also a document describing the ARMCI design (API corresponding to release 1.0 only) is available in the  PDF  format.

Header file
The interfaces are prototyped in the "armci.h" header file.
 

1 Initialization and Termination

int ARMCI_Init()
PURPOSE:     Initializes the ARMCI. This function must be called before any ARMCI functions.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_Finalize()
PURPOSE:     Finalizes the ARMCI. This function must be called after using ARMCI functions.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

void ARMCI_Cleanup()
PURPOSE: Releases any system resources (like System V shmem ids) that
ARMCI can be holding. It is intended to be used before terminating a program
(e.g., by calling MPI_Abort) in case of an error.

void ARMCI_Error(char *message, int code)
Purpose: Combines the functionality of ARMCI_Cleanup and MPI_Abort, and it prints (to the stdout and stderr) a user specified message followed by an integer code.
ARGUMENTS:   
message           - Message to print out
code              - Error code

2 Copy operations using the generalized I/O vector format

int ARMCI_PutV(armci_giov_t *dsrc_arr, int arr_len, int proc)
PURPOSE: Generalized  I/O vector operation that transfers data from the local memory of calling process (source) to the memory of a remote process (destination).
DATA TYPE:
       typedef struct {
         void **src_ptr_ar;  - Source starting addresses of each data segment.
         void **dst_ptr_ar;  - Destination starting addresses of each data segment.
         int bytes;         - The length of each segment in bytes.
         int ptr_ar_len;    - Number of data segment.
       }armci_giov_t;
ARGUMENTS:
       dsrc_arr - Array of data (type of armci_giov_t) to be put to remote process.
       arr_len  - Number of elements in the dsrc_arr.
       proc     - Remote process ID (destination).
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_NbPutV(armci_giov_t *dsrc_arr,int arr_len,int proc,armci_hdl_t* handle)
PURPOSE: Generalized Non-Blocking I/O vector operation that transfers data from the local memory of the calling process (source) to the memory of a remote process (destination).
DATA TYPE:
       typedef struct {
         void **src_ptr_ar;  - Source starting addresses of each data segment.
         void **dst_ptr_ar;  - Destination starting addresses of each data segment.
         int bytes;         - The length of each segment in bytes.
         int ptr_ar_len;    - Number of data segment.
       }armci_giov_t;
ARGUMENTS:
       dsrc_arr - Array of data (type of armci_giov_t) to be put to remote process.
       arr_len  - Number of elements in the dsrc_arr.
       proc     - Remote process ID (destination).
       handle   - Pointer to a desciptor associated with a particular non-blocking transfer. 
                  Passing of a NULL value for this arg makes this function do an implicit 
                  handle non-blocking transfer.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_GetV(armci_giov_t *dsrc_arr,int arr_len,int proc)
 PURPOSE: Generalized  I/O vector blocking operation that transfers data from the remote process memory (source) to the calling process local memory (destination).
DATA TYPE:
       typedef struct {
         void **src_ptr_ar;  - Source starting addresses of each data segment.
         void **dst_ptr_ar;  - Destination starting addresses of each data segment.
         int bytes;         - The length of each segment in bytes.
         int ptr_ar_len;    - Number of data segment.
       }armci_giov_t;
ARGUMENTS:
       dsrc_arr - Array of data (type of armci_giov_t) to get from remote process.
       arr_len  - Number of elements in the dsrc_arr.
       proc     - Remote process ID (source).
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_NbGetV(armci_giov_t *dsrc_arr,int arr_len,int proc,armci_hdl_t* handle)
PURPOSE: Generalized Non-blocking I/O vector operation that transfers data from the remote process memory (source) to the callingprocess local memory (destination).
DATA TYPE:
       typedef struct {
         void **src_ptr_ar;  - Source starting addresses of each data segment.
         void **dst_ptr_ar;  - Destination starting addresses of each data segment.
         int bytes;         - The length of each segment in bytes.
         int ptr_ar_len;    - Number of data segment.
       }armci_giov_t;
ARGUMENTS:

       dsrc_arr - Array of data (type of armci_giov_t) to get from remote process.
       arr_len  - Number of elements in the dsrc_arr.
       proc     - Remote process ID (source).
       handle   - Pointer to a desciptor associated with a particular non-blocking transfer. 
                  Passing of a NULL value for this arg makes this function do an implicit 
                  handle non-blocking transfer.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).
_____________________________________________________________________________

3 Copy operations using the strided format


int ARMCI_PutS(void* src_ptr, int src_stride_ar[], void* dst_ptr, int dst_stride_ar[],
               int count[], int stride_levels, int proc)
PURPOSE: Blocking strided  operation that transfers data from the local memory of calling process (source) to the memory of a remote process (destination).
ARGUMENTS:
       src_ptr        - Source starting address of the data block to put.
       src_stride_arr - Source array of stride distances in bytes.
       dst_ptr        - Destination starting address to put data.
       dst_stride_ar  - Destination array of stride distances in bytes.
       count          - Block size in each dimension. count[0] should be the
                        number of bytes of contiguous data in leading dimension.
       stride_levels  - The level of strides.
       proc           - Remote process ID (destination).
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_NbPutS(void* src_ptr, int src_stride_ar[], void* dst_ptr, int dst_stride_ar[],
                 int count[], int stride_levels, int proc,armci_hdl_t* handle)
PURPOSE: Non-blocking strided  operation that transfer data from the local memory of calling process (source) to the memory of a remote process (destination).
ARGUMENTS:
       src_ptr        - Source starting address of the data block to put.
       src_stride_arr - Source array of stride distances in bytes.
       dst_ptr        - Destination starting address to put data.
       dst_stride_ar  - Destination array of stride distances in bytes.
       count          - Block size in each dimension. count[0] should be the
                        number of bytes of contiguous data in leading dimension.
       stride_levels  - The level of strides.
       proc           - Remote process ID (destination).
       handle         - Pointer to a desciptor associated with a particular non-blocking transfer. 
                        Passing of a NULL value for this arg makes this function do an implicit 
                        handle non-blocking transfer.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_GetS(void *src_ptr c_ptr, int src_stride_ar[], void* dst_ptr,
               int dst_stride_ar[], int count[], int stride_levels, int proc)
PURPOSE: Blocking strided operation that transfers data from the remote process memory (source) to the calling process  local memory (destination).
ARGUMENTS:
src_ptr        - Source starting address of the data block to get.
       src_stride_arr - Source array of stride distances in bytes.
       dst_ptr        - Destination starting address to get data.
       dst_stride_arr - Destination array of stride distances in bytes.
       count          - Block size in each dimension. count[0] should be the
                        number of bytes of contiguous data in leading dimension.
       stride_levels  - The level of strides.
       proc           - Remote process ID (source).
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_NbGetS(void *src_ptr c_ptr, int src_stride_ar[], void* dst_ptr, int dst_stride_ar[],
                 int count[], int stride_levels, int proc, armci_hdl_t* handle)
PURPOSE: Non-blocking strided operation that transfers data from the remote process memory (source) to the calling process  local memory (destination).
     ARGUMENTS:
src_ptr        - Source starting address of the data block to get.
       src_stride_arr - Source array of stride distances in bytes.
       dst_ptr        - Destination starting address to get data.
       dst_stride_arr - Destination array of stride distances in bytes.
       count          - Block size in each dimension. count[0] should be the
                        number of bytes of contiguous data in leading dimension.
       stride_levels  - The level of strides.
       proc           - Remote process ID (source).
       handle         - Pointer to a desciptor associated with a particular non-blocking transfer. 
                        Passing of a NULL value for this arg makes this function do an implicit 
                        handle non-blocking transfer.
RETURN VALUE:
zero        - Successful.
       other value - Error code (described in the release notes).
_____________________________________________________________________________

4 Copy operations for contiguous data

int ARMCI_Put(void* src, void* dst, int bytes, int proc)
PURPOSE: Blocking transfer of contiguous data from the local process  memory (source) to remote process memory (destination).
ARGUMENTS:
       src     - Source starting address of the data block to put.
       dst     - Destination starting address to put data.
       bytes   - amount of data to transfer in bytes.
       proc    - Remote process ID (destination).
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_NbPut(void* src, void* dst, int bytes, int proc, armci_hdl_t* handle)
PURPOSE: Non-blocking transfer of contiguous data from the local process  memory (source) to remote process memory (destination).
ARGUMENTS:
       src     - Source starting address of the data block to put.
       dst     - Destination starting address to put data.
       bytes   - amount of data to transfer in bytes.
       proc    - Remote process ID (destination).
       handle  - Pointer to a desciptor associated with a particular non-blocking transfer. 
                 Passing of a NULL value for this arg makes this function do an implicit 
                 handle non-blocking transfer.

RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_Get(src, dst, bytes, proc)
PURPOSE: Blocking transfer of contiguous data from the remote process  memory (source) to the calling process memory (destination).
ARGUMENTS:
       src     - Source starting address of the data block to put.
       dst     - Destination starting address to put data.
       bytes   - amount of data to transfer in bytes.
       proc    - Remote process ID (destination).

RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_NbGet(src, dst, bytes, proc, armci_hdl_t* handle)
PURPOSE: Non-blocking transfer of contiguous data from the remote process  memory (source) to the calling process memory (destination).
ARGUMENTS:
       src     - Source starting address of the data block to put.
       dst     - Destination starting address to put data.
       bytes   - amount of data to transfer in bytes.
       proc    - Remote process ID (destination).
       handle  - Pointer to a desciptor associated with a particular non-blocking transfer. 
                 Passing of a NULL value for this arg makes this function do an implicit 
                 handle non-blocking transfer.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).
__________________________________________________________________________

5 Accumulate operation using the generalized I/O vector format

Accumulate operation performs atomic scaled reduction, i.e.,  remote += scale*local

int ARMCI_AccV(int datatype, void *scale, armci_giov_t *dsrc_arr, int arr_len, int proc, 
               armci_hdl_t* handle)

PURPOSE: Blocking generalized I/O vector operation that atomically updates the memory of
         a remote process (destination).
DATA TYPE:
       typedef struct {
         void **src_ptr_ar;  - Source starting addresses of each data segment.
         void **dst_ptr_ar;  - Destination starting addresses of each data segment.
         int bytes;         - The length of each segment in bytes.
         int ptr_ar_len;    - Number of data segment.
       }armci_giov_t;

ARGUMENTS:
       datatype  - Supported data types are:
                        ARMCI_ACC_INT -> int, ARMCI_ACC_LNG -> long,
                        ARMCI_ACC_FLT -> float, ARMCI_ACC_DBL-> double,
                        ARMCI_ACC_CPL -> complex, ARMCI_ACC_DCPL -> double complex.
       scale     - Scale for the data (dest = dest + scale * src).
       dsrc_arr  - Array of data (type of armci_giov_t) to be accumulated to the remote process.
       arr_len   - Number of elements in the dsrc_arr.
       proc      - Remote process ID.
       handle    - Pointer to a desciptor associated with a particular non-blocking transfer. 
                   Passing of a NULL value for this arg makes this function do an implicit 
                   handle non-blocking transfer.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_NbAccV(int datatype, void *scale, armci_giov_t *dsrc_arr, int arr_len, int proc,
armci_hdl_t* handle)
PURPOSE: Non-blocking generalized  I/O vector operation that atomically  updates the memory of a remote process (destination).
DATA TYPE:
       typedef struct {
         void **src_ptr_ar;  - Source starting addresses of each data segment.
         void **dst_ptr_ar;  - Destination starting addresses of each data segment.
         int bytes;         - The length of each segment in bytes.
         int ptr_ar_len;    - Number of data segment.
       }armci_giov_t;
ARGUMENTS:
       datatype  - Supported data types are:
                        ARMCI_ACC_INT -> int, ARMCI_ACC_LNG -> long,
                        ARMCI_ACC_FLT -> float, ARMCI_ACC_DBL-> double,
                        ARMCI_ACC_CPL -> complex, ARMCI_ACC_DCPL -> double complex.
       scale     - Scale for the data (dest = dest + scale * src).
       dsrc_arr  - Array of data (type of armci_giov_t) to be accumulated to the remote process.
       arr_len   - Number of elements in the dsrc_arr.
       proc      - Remote process ID.
       handle    - Pointer to a desciptor associated with a particular non-blocking transfer. 
                   Passing of a NULL value for this arg makes this function do an implicit 
                   handle non-blocking transfer.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).
_____________________________________________________________________________

6 Accumulate operation using the strided format

int ARMCI_AccS(int datatype, void *scale, void* src_ptr,int src_stride_ar[], void* dst_ptr, 
                 int dst_stride_ar[], int count[],  int stride_levels, int proc)
PURPOSE: Blocking strided operation that atomicaly updates the memory of a remote process (destination).
ARGUMENTS:
       datatype       - Supported data types are:
                        ARMCI_ACC_INT -> int, ARMCI_ACC_LNG -> long,
                        ARMCI_ACC_FLT -> float, ARMCI_ACC_DBL-> double,
                        ARMCI_ACC_CPL -> complex, ARMCI_ACC_DCPL -> double complex.
       scale          - Scale for the data (dest = dest + scale * src).
       src_ptr        - Source starting address of the data block to put.
       src_stride_arr - Source array of stride distances in bytes.
       dst_ptr        - Destination starting address to put data.
       dst_stride_arr - Destination array stride distances in bytes.
       count          - Block size in each dimension. count[0] should be the
                        number of bytes of contiguous data in leading dimension.
       stride_levels  - The level of strides.
       proc           - Remote process ID (destination).
RETURN VALUE:
       zero           - Successful.
       other value    - Error code (described in the release notes).

int ARMCI_NbAccS(int datatype, void *scale, void* src_ptr,int src_stride_ar[],
                  void* dst_ptr, int dst_stride_ar[], int count[],  int stride_levels,
                  int proc, armci_hdl_t* handle)
PURPOSE: Non-blocking strided operation that atomicaly updates the memory of a remote process (destination).
ARGUMENTS:
       datatype       - Supported data types are:
                        ARMCI_ACC_INT -> int, ARMCI_ACC_LNG -> long,
                        ARMCI_ACC_FLT -> float, ARMCI_ACC_DBL-> double,
                        ARMCI_ACC_CPL -> complex, ARMCI_ACC_DCPL -> double complex.
       scale          - Scale for the data (dest = dest + scale * src).
       src_ptr        - Source starting address of the data block to put.
       src_stride_arr - Source array of stride distances in bytes.
       dst_ptr        - Destination starting address to put data.
       dst_stride_arr - Destination array stride distances in bytes.
       count          - Block size in each dimension. count[0] should be the
                        number of bytes of contiguous data in leading dimension.
       stride_levels  - The level of strides.
       proc           - Remote process ID (destination).
       handle         - Pointer to a desciptor associated with a particular non-blocking transfer. 
                        Passing of a NULL value for this arg makes this function do an implicit 
                        handle non-blocking transfer.
RETURN VALUE:
       zero           - Successful.
       other value    - Error code (described in the release notes).

7 Accumulate operation using contiguous format

int ARMCI_Acc(int datatype, void *scale, void* src, void* dst, int bytes, int proc)
PURPOSE: Blocking operation that atomicaly updates the memory of a remote process (destination).

ARGUMENTS:
       datatype - Supported data types are:
                        ARMCI_ACC_INT -> int, ARMCI_ACC_LNG -> long,
                        ARMCI_ACC_FLT -> float, ARMCI_ACC_DBL-> double,
                        ARMCI_ACC_CPL -> complex, ARMCI_ACC_DCPL -> double complex.
       scale   - Scale for the data (dest = dest + scale * src).
       src     - Source starting address of the data to transfer.
       dst     - Destination starting address to add incoming data.
       bytes   - amount of data to transfer in bytes.
       proc    - Remote process ID (destination).
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_NbAcc(int datatype, void *scale, void* src, void* dst, int bytes, int proc,
                armci_hdl_t* handle)

PURPOSE: Bon-blocking operation that atomicaly updates the memory of a remote process (destination).
ARGUMENTS:
       datatype - Supported data types are:
                        ARMCI_ACC_INT -> int, ARMCI_ACC_LNG -> long,
                        ARMCI_ACC_FLT -> float, ARMCI_ACC_DBL-> double,
                        ARMCI_ACC_CPL -> complex, ARMCI_ACC_DCPL -> double complex.
       scale   - Scale for the data (dest = dest + scale * src).
       src     - Source starting address of the data to transfer.
       dst     - Destination starting address to add incoming data.
       bytes   - amount of data to transfer in bytes.
       proc    - Remote process ID (destination).
       handle   - Pointer to a desciptor associated with a particular non-blocking transfer. 
                  Passing of a NULL value for this arg makes this function do an implicit 
                  handle non-blocking transfer.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).
 _____________________________________________________________________________

8 Register Originated Data Transfer Operations

int ARMCI_PutValueXXX(DATATYPE src, void* dst, int proc)
PURPOSE: Transfer of a value stored in a register  of a local process to remote process memory (destination).   XXX can be "Long"/"Int"/"Double"/"Float"
ARGUMENTS:
DATA TYPE    - long, int, float, double according to XXX in the function name
       src   - Value in a register to put.
       dst   - Destination starting address to put data.
       proc  - Remote process ID (destination).
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_NbPutValueXXX(DATATYPE src, void* dst, int proc, armci_hdl_t* handle)
PURPOSE: Non-blocking transfer of a value stored in a register of a local process to remote process memory (destination).  XXX can be "Long"/"Int"/"Double"/"Float"
ARGUMENTS:
DATA TYPE:  - long, int, float, double according to XXX in the function name
      src   - Value in a register to put.
      dst   - Destination starting address to put data.
      proc  - Remote process ID (destination).
      handle - Pointer to a desciptor associated with a particular non-blocking transfer. 
                Passing of a NULL value for this arg makes this function do an implicit 
                handle non-blocking transfer.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

DATATYPE ARMCI_GetValueXXX(void *src, int proc)
PURPOSE: Transfer of a value stored in a register  of a process (source).   XXX can be "Long"/"Int"/"Double"/"Float"
ARGUMENTS:
DATATYPE    - long, int, float, double according to XXX in the function name
       src   - Source starting address.
       proc  - Remote process ID (source).
RETURN VALUE:
       the value (of type DATATYPE) is returned.
_____________________________________________________________________________

9 Completion of Outstanding Operations

int ARMCI_Fence(int proc)
PURPOSE: Blocks the calling process until all blocking put or accumulate operations
issued to the specified remote process complete at the destination.
ARGUMENTS:
    proc    - Remote process ID.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_AllFence()
PURPOSE: Blocks the calling process until all the outstanding blocking put or accumulate
operations complete remotely regardless of the destination processor.

ARGUMENTS: none


int ARMCI_Wait(armci_hdl_t* handle)
PURPOSE: A common function to be used  to wait for non-blocking ARMCI operations with explicit handle.
ARGUMENTS:
handle - Pointer to a desciptor associated with a particular non-blocking transfer. 
            A value of NULL for the pointer is erroneous. A value other then NULL would 
            make this routine act as a wait for an explicit non-blocking request with 
            the handle pointed to by reqid.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_Test(armci_hdl_t* handle)
PURPOSE: A function to be used as check completion status of  non-blocking ARMCI operations with explicit handle.
ARGUMENTS:
handle - Pointer to a desciptor associated with a particular non-blocking transfer.
            A value of NULL for the pointer is erroneous. A value other then NULL would 
            make this routine act as a wait for an explicit non-blocking request with 
            the handle pointed to by reqid.
RETURN VALUE:
       zero        - Completed
       1           - In progress
       other value - Error code (described in the release notes).

int ARMCI_WaitProc(int proc)
PURPOSE: Wait for all outstanding non-blocking operations with implicit handles to a particular process to finish.
ARGUMENTS:
       proc - proc for which all the outstanding non-blocking operations have to be completed.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_WaitAll()
PURPOSE: Wait for all outstanding non-blocking operations with implicit handles to finish.
ARGUMENTS: none
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_Barrier()

PURPOSE: Synchronize processors and memory. This operation combines functionality of
        MPI_Barrier and ARMCI_AllFence.
ARGUMENTS: none
RETURN VALUE: none
_____________________________________________________________________________

10 Aggregated Data Transfer Operations

ARMCI_SET_AGGREGATE_HANDLE (armci_hdl_t* handle)
handle - Pointer to a desciptor associated with a particular non-blocking transfer.
PURPOSE: Mark a handle as aggregate. This will allow ARMCI to combine nonblocking operations that use that particular handle and process them as a single operation. In the initial implementation only contiguous puts or gets could use aggregate handle. Specifying the same handle for a mix of put anmd get calls is not allowed i.e., only multiple put or only multiple get calls can use the same handle.

ARMCI_UNSET_AGGREGATE_HANDLE (armci_hdl_t* handle)

handle - Pointer to a desciptor associated with a particular non-blocking transfer.
PURPOSE: Clears a handle that has been marked as aggregate.

11 Atomic and Synchronization Operations

int ARMCI_Rmw(int op, void *ploc, void *prem, int value, proc)
PURPOSE: Combines atomically the specified integer  value with the corresponding integer value (int or long) at the remote memory location and returns the original value found at that location.
ARGUMENTS: 
    op    - Available operations are:
            ARMCI_FETCH_AND_ADD -> int
            ARMCI_FETCH_AND_ADD_LONG -> long
            ARMCI_SWAP -> int
            ARMCI_SWAP_LONG ->long
   ploc   - Pointer to the local memory.
   prem   - Pointer to the remote memory.
   value  - Value to be added to the remote memory.
   proc   - Remote process ID.

int ARMCI_Create_mutexes(int count)
PURPOSE: Collective operation to create sets of mutexes on individual processes.
Each process specifies the number of mutexes associated with that
process. The total number of mutexes allocate will be a sum of the
values specified on each process.
ARGUMENTS:
  count    - number of mutexes allocated on calling process
             count=0 means that no mutexes will be associated with that process.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_Destroy_mutexes(void)
PURPOSE: Collective operation to destroy mutex sets allocated by ARMCI_Create_mutexes.
ARGUMENTS:  none
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

void ARMCI_Lock(int mutex, int proc)
PURPOSE: Acquire the specified mutex on the specified process on behalf of the calling process.
ARGUMENTS:
 mutex    - Mutex number (0..count-1)
  proc    - Remote process ID

void ARMCI_Unlock(int mutex, int proc)
PURPOSE: Releas the specified mutex on the specified process on behalf of the calling process. The mutex must have been acquired with ARMCI_Lock.
ARGUMENTS:
    mutex    - Mutex number (0..count-1)
    proc     - Remote process ID

12 Memory Management

int ARMCI_Malloc(void* ptr[], armci_size_t bytes)
PURPOSE: Collective operation to allocate memory that can be used in the context
of ARMCI copy operations.

ARGUMENTS:
  ptr    - Pointer array. Each pointer points to the allocated memory of one process.
bytes    - The size of allocated memory in bytes.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

void* ARMCI_Malloc_local(armci_size_t bytes)
PURPOSE: Operation (noncollective) to allocate local memory. This memory can only be accessed locally. However, using this memory in ARMCI operations can improve performance on some systems. For example, on Myrinet or Infiniband, the memory is registered and therefore suitable for the native RDMA communication.
ARGUMENTS:
bytes    - The size of allocated memory in bytes.
RETURN VALUE:
       NULL pointer - Failure.
       other value - address of newly allocated memory.

int ARMCI_Free(void *address)
PURPOSE: Collective operation to free memory which was allocated by ARMCI_Malloc.
ARGUMENTS:
  address    - pointer to the allocated memory.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

int ARMCI_Free_local(void *address)
PURPOSE: Non-collective operation to free memory which was allocated by ARMCI_Malloc_local.
ARGUMENTS:
  address    - pointer to the allocated memory.
RETURN VALUE:
       zero        - Successful.
       other value - Error code (described in the release notes).

13 Collective Operations

These operations can be used as an alternative to the collective operations in a message-passing library ARMCI is running with. The programmer can use either.

void armci_msg_brdcst(void* buffer, int len, int root)
PURPOSE: broadcast data from process "root" to everybody else.
ARGUMENTS:
buffer - data to broadcast/receive
len - size of the data
root - rank of the sending process
RETURN VALUE: none

void armci_msg_gop2(void *x, int n, int type, char* op)
PURPOSE: allreduce operation for int, long, float, double corresponding to XXX is "i"/"l","f","d" 
ARGUMENTS:
x - data
n - number of elements
type - data type, one of: ARMCI_INT/ARMCI_LONG/ARMCI_FLOAT/ARMCI_DOUBLE
op - operator, one of: "+","-","*","min","max","abs"
RETURN VALUE: none

void armci_msg_barrier(void)
PURPOSE: synchronize all processors
ARGUMENTS: none
RETURN VALUE: none

void armci_msg_reduce(void *x, int n, char* op, int type, int root)
PURPOSE: reduce operation
ARGUMENTS:
x - data
len - size of the data
type - data type, one of: ARMCI_INT/ARMCI_LONG/ARMCI_FLOAT/ARMCI_DOUBLE
op - operator, one of: "+","-","*","min","max","abs"
RETURN VALUE: none

14 System Configuration

These operations can be used to determine configuration of the system the application  is running on.
The system configuration is described in terms of locality domains. For example on clusters with SMP nodes, SMP node is one of two locality domains for a process. The ARMCI header file predefines ARMCI_DOMAIN_SMP for querying configuration information on clusters composed of computer nodes with shared memory.

int armci_domain_nprocs(armci_domain_t domain, int id)
PURPOSE: return number of processes/tasks in locality domain represented by id. 
ARGUMENTS:
domain - domain name
id - identifier of a node within the locality domain, value < 0 means my node
RETURN VALUE:
< 0 - error
other value - number of processes/tasks (0, ..., armci_domain_count(domain)-1)

int armci_domain_count(armci_domain_t domain)
PURPOSE: return number of nodes in specified locality domain. 
ARGUMENTS:
domain - domain name
RETURN VALUE:
< 0 - error
other value - number of nodes

int armci_domain_id(armci_domain_t domain, int glob_proc_id)

PURPOSE: return ID of locality domain of specified process
ARGUMENTS:
domain - domain name
id - process/task id
RETURN VALUE
< 0 - error
other value - process/task id

int armci_domain_glob_proc_id(armci_domain_t domain, int id, int loc_proc_id)
PURPOSE: Returns global process/task id based on its id in a given locality domain node
ARGUMENTS:
  domain - domain name
  id - identifier of a node within the locality domain, value < 0 means my node
RETURN VALUE:
  < 0 - error
  other value - process/task id
int armci_domain_my_id(armci_domain_t domain)
PURPOSE: Returns id node in specified domain the calling process/task belongs to
ARGUMENTS:
domain - domain name
id - identifier of a node within the locality domain, value < 0 means my node
RETURN VALUE: id of domain


ga-5.9.2/doc/ga/000077500000000000000000000000001500715745200132605ustar00rootroot00000000000000ga-5.9.2/doc/ga/Doxyfile000066400000000000000000001452541500715745200150010ustar00rootroot00000000000000# Doxyfile 1.4.7 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a 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 #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = Global Arrays # 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 = 5.0 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # 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 = # 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 cause performance problems for the file system. 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. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, # Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, # Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, # Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, # Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # This tag can be used to specify the encoding used in the generated output. # The encoding is not always determined by the language that is chosen, # but also whether or not the output is meant for Windows or non-Windows users. # In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES # forces the Windows encoding (this is the default for the Windows binary), # whereas setting the tag to NO uses a Unix-style encoding (the default for # all platforms other than Windows). USE_WINDOWS_ENCODING = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) 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. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) 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. 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" "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. 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. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then 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. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then 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. 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 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. 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 # comments will behave just like the Qt-style comments (thus requiring an # explicit @brief command for a brief description. JAVADOC_AUTOBRIEF = YES # 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 behaviour. # 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 behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. 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. 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. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # 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 = # 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. OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_FOR_FORTRAN = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # 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); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # 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. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) 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. SUBGROUPING = YES #--------------------------------------------------------------------------- # 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 and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. 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. 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 (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) 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. 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 (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. 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 (the default) these declarations will be included in the # documentation. 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 (the default) these blocks will be appended to the # function's detailed documentation block. 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 (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. 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. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) 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. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = 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 default), 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. SORT_BY_SCOPE_NAME = 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. 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. 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. 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. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of 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 initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # 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. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # 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 , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED 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. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR 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. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. 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) 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 stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be 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. INPUT = ../../global/src # If the value of the INPUT tag contains directories, you can use the # FILE_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 the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py FILE_PATTERNS = *.c *.h *.f *.fh # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # 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. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # 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 = # 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. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are 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. INPUT_FILTER = # 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 # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # 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 also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # 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 documentstion. REFERENCES_LINK_SOURCE = 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. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) 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. 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. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # 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 one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. 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. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. 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 the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, 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. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, 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. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, 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). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, 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. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag 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 (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # 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. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will # generate a caller dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected # functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_WIDTH = 1024 # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_HEIGHT = 1024 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that a graph may be further truncated if the graph's # image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH # and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), # the graph is not depth-constrained. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, which results in a white background. # Warning: Depending on the platform used, enabling this option may lead to # badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO ga-5.9.2/doc/ga/ga_c_api.pdf000066400000000000000000010724601500715745200155070ustar00rootroot00000000000000%PDF-1.3 %Çì¢ 5 0 obj <> stream xœå\ëÜ4‡Ä»@Ë+´ôu°[ßæHˆÏT'õK¿Z@½J-ÿ¿„ãØIÆëÍ3›Þžª“îv.‰3óóøçñس϶†µ?ñսû¦yòßêùJhÖpiš“ðÉ8Κ§+¥,þÜÞñtõ÷êAólZjîÿ?¼xâ[îsüóè¤ùõxuï—šãÇ«ôÖuûA·±ÕŸ¬î¼v÷øß•¶ÓñŸ«;¯‘7kÄK­h»Ëo„˲é¤7ÃUÑt÷¾ÕJN7`ƒx¹•‹·¾n…¾¡wBC,^}wBzø,´‹.*¥÷­¦ø~+rÛ¬ÅNu•¼¹W8¼ÇºhÛá%/S›¤bw5)õA+¥ðZ1péþóa|ÑÚ?Ñʵ²HO' d?ŽÂXéÇåOÂÝ œN‚Ô™Ÿ’ùÉñFtí|ÚUÍZ²¡ãU²áJ¼¹“>o%éïõz´òá­¦Ñ0jJôMu@|øËdüÔÃW‘/\ «Ø)_E)ùTg½ôįã£ñE×PKßÄgÇy•Á¢–¢x5:Ö™£–G—¿c(Š—KOûî¥Wuz±ä*ßÑGÃЫFÈz Ýì}ûm+7Ñ7À ÈkèõÈð¹Ÿî°ý>Ú€ÜÃkÉ'<Ø$Ÿ½Ç²ø·ãÕþçùJ:å™)OxíG%¸§"ÏrRfB¸ qÞÆ2f<‹$êKr*áOƒ"0àõÐùºY«§A6M¢Ã—û‘q3Þ¼î,½I³áY•|îvºµ”|ðá8¬¢x={ÔCÍ?Þ#|§˪Ç@;ÿûba )Õ?úc¸È3 ÒHù)£NÅu¼ØI›Û€«6Öãjý´1We7n>®[j²AÔôïÕäÖÎWSÚYFMßg0ê3}#Ýz [$M”HO¬³-6†5çiQîû²ÍMäÏÑÛejê^TŠ|”a¨„iyÒø‰o6TÜRøïСò #šHáÇõØlz  ×ð¨p' Œ97-ß:MÀ,…o/æ>žÒ£÷f*ÊtóõØRÒBFc²§“T|tÄêCnG?#073K17¡‡v9ïO¼¸ƒnE¨`€*S¿ £|„=oP$ÐgwÓšÚ=Ú™˜±–êžA?<1j§Û‰ÑèùS½¶†61Ž±ó£¶šm mNÚ©eNM{µÑÔh\kC›Îlý@Ìk4Г‡Âh0[<0Ÿ¤·` ¦X@ke‰‘‹VšÆÑŒÓ6CïÛO• ,Ìa˜JMcᚈ²(Äi¡Êp<áú¾+õ–Dâå瀕T’ê«BSx7[[,NFfÉKž’¬?Âæ Õxø8×\®+<<ûñϰ幻0Úƒ¸bÄ¥]ÁÉ[„Æ`–ÇÐåáº+’F1v_Ì'¶>6)…tP ɤÌT)ç@y5a¬&NMr+væ>…i1Tª¨AyJÑá€#“íp·0?PNUpëàX\‚|j¥­œ”T­ªàÝs‚OPuL,Ù dé±’‡ AzĨš©á {ˆ)ftQ9É©ŒdTßÖŠ0'¼o÷ ñIÔ!ÓK‚Ñwóg¬l-`¢Î)9‚܆ï€q»1ë©ÎBo€S!"¥ä”gçùSNA"²*Õ–áÀ‚„þî<,®öâ«8%Â>©æóSiŠK÷z•|­ËδôP\P#ur‘:ÉžÂè=kþ ®±{¼´Pn©c™ .‚ÚÒéTÅ8•¥ ¼H°BÍ%}Z:.©»Ò Ó/EK®à¥åœêÓFœû„°}¸#逃Aœ *½r—{Έ™©ÅÁM4û©_ ¼œ±®ÁR—•RñWeË™xÿƒC&÷‡ä¯êÌxA¡„0á 7?ö–a5rhŽÏ“¤fcžCN¶ÔI6v+NzcJ)€óÒÏàuóï83í–dN­Šû#’#.ª¥7»n2œ©ôâKj Ì—Ô’Aå\TÌí Lävvá†3(’±–Ü¥ù•ܾ¼}ùK_î°ŽIâYaa¡’;ÑÒˆÕÕ0 ‡¶úa!±–µÜ%,ÁîÂFä[÷Y²mÐÛ0*} úª8Ž0(­œ¥*­Ø>hæ g(¶‘qk<£†÷ðẠé4‘s…dûà¤9`ŒÏ¶îà/¼i!„“Ä"äI÷dîÙ)8{0… øÔÆ$Åòˆaq_Ra˜¸£n~•¿‡Xª¯?AÛ9Ã!,%“¡:÷ŽG=J(€ÕðÚüsÀmÉÖÙ§ß<—®bO==‹£Á,5%Ϋ¡× Ýâ‘5w–œVàÖÕÐüþ:ešAûMïÛÙÍ…½ëB)_@ÇÚvRÐfþò›W3)>8‹×øqc9±–‡ëšúÑåh¢Çñj/bøv³ùÊH/@iKÍ—sUSdzÁ€Êß¹OUÆRÏksYSŽºàŒÆʨh(ãŽ9®*CA,ÉUEMAìjyW¡~—檼¦€W uä8ã]ªJÃÈw¬H97Ô\‡šÂÚ&ð’Ká€Ak0ÔLg5¥©­%[Ú™¦frÁՔ⠦­›sØ óŽ >².NRG­©›Ì6Ìòœ"Û3hiC‘"¥üLM‘b–§žì¯äy×`i{N · @MÝJ]SGIrQÐ]å$!+ª¦rr {ÐŒ_Ì‹éæ«PIÉö€¬)ˆ<õ(ÒÔ÷Ògó@*jFDMuâÎ[± BQ'¯©$(½x¶¸¢M¨ª"ÌÁÈNSœq7 @Qc(`UÅ‚ÓsE™I‹}¸÷(˜¤FP®ªp6YâO8Yõ,NÓ'É!ªÂ88I%#[UÍwX0äY¸Aùb º¼çeÉ¥|¦ª’oÂŽæÂh†Þñ-@…ÍÎÑWVqâ™ ]U÷R¬;í$þêœ×på >@·â-M/ˆ«ª‡; óMs±1†ßQÉO^¢Ý~›âÿ#9zéendstream endobj 6 0 obj 2313 endobj 74 0 obj <> stream xœí\[¯Ü4†Ľ@Ë-ôFo»Çã»E ñLu$^úVhj+µü DZ“Œ“³Îì¶Ûª µ;$v<ŸÇߌÇž4l kÿß{´:ºctóà¿Õ“•ЬáÒ4Â/ã8k®”²øwûÆÃÕ?«?›Ç«ÐUsç÷øãéß t¿ã_÷5¿¯Ž~i 9¾¿JŸ]·?„qÛ¹QÍñ£Õµ×®ÿ»Ò¶ñ_:þkuíõ òf̓x¦­ñb÷øðX6ôfx*šîÝ·ZÉél϶¢rñշëÐwôNèˆÅ§ïÎHw‡~ÑC¥£ô^xÕ4¢ßoEn›µ˜®²³/÷ß±.êv÷ièIŇAUà& ±{šõA+¥ðZ1péþˇñCkߢ•?je‘Z' d?ŽÂxÐ-ŽÊŸ„·8i2? :$õ ’ãèúù,ô«šµdÃÄ«¤Ã¹ør'}ÞJÒ¿ëÇÑÊ_„¯šFè+ÑwÕi±ñ—Iù¹ÆÃ8Î#[¸«8)_E)ÙT§½ôįcÓø¡ ¨§obÛ±F~È`QOQ< ™£žG¿k(ŠgK­ýô‰Ò§ºq±d*ßÑ/GÃЧFÈz ½ìmÛm+7Ñ6À ÈkèÇ‘ás&¶î°ý>ê€ÌÃ’ÏX°I6{.®¤ñoÇ«?ü¿OVÒ)ÏlLyÂk*Á=y–“2ÂKˆó6–1ãY$Q_’ëP ÿ‡±|#^ “¯›µêpºdÓDñr²²(óôz§ÛÕh:šÞ†þm'¡¶A²H2©ß£ º†‡*ï ÔèÑUÞª<ÌC¨ otóôïÕýõÐîÿÜšáñÝkŒ(_Œ:¬#8Gœ(ÞÄ7Ìú O}‹®aã7Ù4¡)–q*n•º}WjnEamÑ6²IÕLS'UÁưIÃŒb¢ó)4â_E¸ÅÅãžxjûC†ä bmÈ¢ Á<$Ä g2ŸÔN«52K­’ã 4’¬%;)“* †Äö¤‘7†‰ÅP¢-‰Ø¡õmˆXUyå( `-É·.†Ž‰äf¡³qÔ ™á<¹S.±0!Jaç)!liYÏí ûAÀ³Yåܶìî4ÁrÁížÝ—€þ³&Œ¥Ú.³4§Å›Q?¼-:iŸz¼á@‘º—`D ªRèÒ?Tsf~ú~f&`™™Ö#9XßAUx¤<-“èÿJ\›ÅÁé‡ÃyåLë(„]L©¾ìH5¿À‰JXéJøåRAÔ»Sâä2b£¦¬oº)óBi7wJB¯ìXÁ¾•1ÔH[i[Ãų¸K}qÆBù`›k+ejhqÞR&ÉQxs4™½{5¨ö.Íîx'ômô¬®N¬_©%uV„¡P"ÇÒÚ@Aå0f¡91÷¥¸¡pòt5 zí qͨ³†²Úw|È @‘;Ó”Å>Æõƒ–qÄ–?c˜¢.ué4e©o¯Ãà÷¦ŽÇ¡Y¢ƒž•Þ,§ n!;Ð5é”$¦ÓeH6T§îftr…c„Ìp†uýäl `XÕ2•æòqnÕÙò SêcG-bDz•ÞIV´À 9qÂ3»Ñ^ÇÉŸ²ç™ í"xï;-1Õ#•ªa¹çT%o·K>Ì %U •Px[Æ—ÒÿCEJ¨åô)Ñì{ ÙÜH<º`8¢N†r9:ÙÔÕ•ØU)#Òt•_a«5à&$§º®–3íÞp˳3vd‘:"ØêÞ€ä’zø-A-çú}ÂVÜs—ÂðÙ¤k65>¶`AMùI&—;‚gc–„Ýö:–d‚zåH8¹œþ÷Ïp@dâ\pá5 +ó+B…msf7Ô¡ž$|2Z>OGw͆°á®(0¶œ…‘è>S°Ÿ#”s#¨§MBËt=Ë!Å©·Sã鳨̲“v8#4·Äsáÿ<<Ï”g8ä2êUœì™¤8@Ï´£‹Ô>ª&»&!Ð5í;£+§îGèPöœ8ö°Q7$Äz…ìþ§4°äXˆñt'§`Ìy£`ñ‚S9³%P3[Üñt ;ëôú§ÉPrö¾? ܤš,Î|–Üç8Ñ…sSYßVN|–²­øP¥œå&”¡Q⸮*CÛ‘µA¶MJe ÓÒÉÕRš9»°U} ׌츪*¦Ûüy¶|âë¼=Œ8_ÊUWaëcÆíA¿®RÚåû6ÐU•lÏD¿ünv꾃k¼ÐÝ9§Ð1Ȫ’¹¸œl§.™ÏE-8EÝý8R@¢ªïåš‚ùT\v0ÉÌ•(Ím_¡;œP.åUÏe¾D(å™aÌ¡µýmàäª@€ªªÀ— Ègàºx€B*VUCøjŠž—b¡Î„\>×®ªÎñÕT¿þÎ…ÊL¥,Olme&ÊØ ËÚ¸Ô´ß(Ífù,¹0ÓÔÖeîDÜ¢v¾+4†º)Ôµµž{&#’<%–:¾^.¥õt(Фd‚Um}æ¾°˜.ÿÎSxªŒ… ¥c,Úÿ=ãÿ©sûendstream endobj 75 0 obj 2382 endobj 84 0 obj <> stream xœÝ[ëo5‡Ä»@ËË$´¤iïâÇŒm¡>REâK¾Z„šJ ÿ¿„íµwwöö¶·ëëh¤d§^?~3ãߌÇwgŒ/ãá'ý}tZ=´š=ù»:«”æL‚a§ñÉ8ÉÙÓ ÑÒçðÆÓêÏê7ö¬ŠC±‡ÒÃù?ЍŸÓŸG§ìÞqut— vü¸ÊÓ.ƒ2ni™‚%²ãÓêज़ÇUÚ2?ÓñïÕÁËQ”l!£x)ˆÖx±n~%6«¥Wc«bõ»¯Éi&l/]zõõøªhz#ÄSë›#Òɳ8.iD¤·â«†©Z|;ˆÒ²…Z»\´£/7 ŽóX—°œÇ‘05F¨Bš¼Äº5/ê •¤¨àrᘩÿçÝ4ÑÂ÷ò{AV¹w–Dñý„PtôXkùƒøvVN-‰lÌ#† @r’©zœâ¸ÈÀ[ÃcÆp%½\K ü»~Aþ$Îj˜¡T3T@¤ÎŸfðcÛu\%¾p-vÆd”Ï’”}ªFÞ QüRh¦3´È –VZÁg%›sÙ rA+H®{Iu¼wDNý`{×Mv¸ºè3…ÒR+ N`·]Ñë^z~^ã胧?\9—¯-µè}¦Tê)€ ŽrQÔ„îÉöm!ùá#j~¸…¹j ­‡õÓÛu m”Y JâÄ´€êÃè×6f)»½> žJ»ÿ–ꦇ?§B•~ˆ8LÌûÛ®GÝâwïŽdå*ä(Ûbäªô³hÊÁ¶ ’ü1"uޱ Ê÷ʩҒ¥²0…ÛîHº©†|”ä í¢­*Mä”)ä7Ñã×*[(m(£J¯Q”†)tÖW9"ô²ZªšvÑZÚR úßÈaþ¢ý>ù¼Ð6,ˆ²ô>[š’smO{mä“ ( åJ©öB‚iÖ¢RRP°ô»@I·”Ä~Ž^õ7îyïÝMøê/!ÜË6Ò(êo,ü@zþHÜä~î)L” ²3é ‡)º Œâƒì5µ­n\¬Ó7LkÚ¡G§ !^νþj)Ÿ Á[{„obü dendstream endobj 85 0 obj 1900 endobj 101 0 obj <> stream xœíi“7 Øð ¸sع†*>ªÞf4’F£o$qBL!aI8ìpb›$°“p„û>B¸0:º5jFïÍ;vßšW©rV+ÔjõÝ-íͪ>`Umÿƒÿ_=š=ô,«yuýÙÍo몪:r?)ÝÔÕk3);ú³ñÚìÆìùêõ™›«zö‹ðíëfæ†ÿ]=ª9œ=ôpŪÃk3\wnàJt²:<š=øžÏ¾2k»Ê¬tøÒìÁ÷ºfSÍ××-*ßz¿ëå•ûÛÒmÅ:×!þ¶ºõòìÚç ½v v´zÿë %£ï¢nÿÛ®Ig[ ƒd¬ö_µ1 ¡c †a7ü¶Mg[ ç²Ë­Ñ9†Ýþ·j˜Vy c ÃnÿÛN `@7AÑÉö€WJqó¯Ut_õ4ï<§=éÚª‚æ—‰€Ê‘¼ƒ¿â~Ýáè§€M±ÿi7™ Ý¢£k±èWA“ÉxýŒ› ™üÙÀN ‘ÀVPû§FIf~´ØøBh8Ï®rˆ&þºëo+ßûà „ôó¨ŒP°ío¸¶ª ùM@ž‡ÿ[ÉJ߆Át%Uùo/_vM#M4_ÁɘE/ Ø2­«Æ ®ï œþãAŽ ß…©ç°«ïD¯e_Iy *Å;ƒn‹è«Î¨”î"ú:§H…h Z¤®Â á3D¹SÛ •bü^ØI@Ät4ØìÚ®»Ñ@(úߨ06«é¬9B‚Ηð cK“Óu°¬*l?˜+ç€Jr:3 c‹œ™Ø ~–0j0‹Èà &Ïûí&vö}¨¬É·F0¶Ñh†£ÏêbS˜2ª-âÑXrÒÏô2R§ŠÉs-ËF*°l®$`ÙÐC¡[8KìºkH‚ͨqblv³ƒ]x†Ú×ö{Å"4ˆ‚<†’nCÖ …mô¦‘ó(°=MDh} *e´½—Å”ÑSœ%ÆòižŠ(·6A–Þƒ,1 Û©ÖÖþÔμŠTdæäûçlц3/ÐIÊcâèBF ¶”Œ7{å`מj ¡“)œ8ØÏ…„pøÖõ ­DSëÿÖ¸ & ÆÅ ð±ˆ1pŠfƒ0Ø[&xb7p,é p‘`Äe¿²–¸rv#ßÏE§‘0׆ ¨N/Žkéxáˆ^A¡ý0èzÄÉÞ¢#ûôÖý·A´< 3ùe^ ¤àšaÕ9LôH h>š@ñšëæÈ ùŽH+ø:Z®õ7©IE[Õ¸ÈE Îý‡HBô_[Êg¼ßAìàÈÄ/| hÄ·ž€©"ðƒèv v:,~Š_ߺ™™“¬äZÊè?ؘ µñÞjÏ¡!|ü¡WÃ)F80ä ¬ñ†7,dkcoudÂì÷½ñìr,Æ»Zž üм“ȶ39 aÏUálá´|°·HD­ÑÖ~sošjz. Q…gyÁx.ÂŒ}™Y„ˆ½ƒ7ê‘ˉ¯`ALàMB«› ù¦Ý:Kðk:FÖäÞîËzE,œ”CÎ\´]SéX¼­ED´$.ùBs?0ÍPÌ”Äm†©~iq>Å‘ÈêÛJe°Fá#R>Gþ‘6A~d…iIJ,ú&2>´7˃hð,ØyV·4áµ$Ú·ÝÇçUôiÃþîöß2qñ““…:;gFÒ‡sÈEÈÈbM´V7àYÀÎý¨/yDgur¨‰‘’j9Jw:² iÌ…2ÅZYP¦›tBF#ÛkÔS«‰‚{ô–ƒ˜Óü¡‘X`Œó´ùàfÎ6¼;Tv'–2K©,¡ ¶;›Y^À!›¡f\`$ø/ˆcMOc`Ë4Äè*ùç‡G1ØæD¡¯q¸<÷éiçj±:„‹³öFÏP¨d!EÝÛ» Sä4ñq‰„ tû1vìçGÈ2™EWÀÈ9f$?—™c î@^†œn£¥ŽQ9Ÿ$„ìÂ]S„ì½€ ÂV‰cÔMXIS)–ëg\KQ# U(±m•ê¼-Hq‡Ô.ñα¶ZR±Œl§É¦s2 )ˆÏ%môí…ä[Ð@pó'°ÍO­î¢(òç§^ÞXâô“¹Â2cÓ6ãÎj)BCŠ£ºÀot$Rqia#1êà*¶C]8æÃ7Ëc*Õ_/š"j«^ýBÕsáÛA °î¦ŒðÛµ•Œýå4‹˜0' H÷Ú/£‘J‚‹Ôê‚d¦:i&(ê)ý õABÐÑNGmû<#%*x~Ÿ™: Ô>ÚtÊW2ájCÚå²YóÇ9ÈÙ=`Œ’”Ò–D1Ý…lì´B(»ts ÜÒ»3ö[´ÅoB¸RººÛL¾Y+3›-T9{Õ"Ø)¯† íƒjdÃëM× ô;ÞeõšŸƒ¨¬ûOmÊ¿ýEø…Çî/}ÙéW¾d„›­Ã¹éJ¿NÀ Sûn_1€$àaFÌÒMUJóµ˜!…í©Êgíûô*$©CfnRIÀop0$ ëzQÖÿÎõâøÂùÁßôôýƒkršIÇUþ‡«Ðy}1zÙ^Ÿ|ÍÕ%r=Ì÷/‡¬?á.âʃaL~g#õV ûöŸ¥dŠŠÀ„ÏW¨]}Xy“B6 H26ïï—¼ Š2ý“°Ÿ[`*à±_ ûët9ú¿ÐýÁþàã7hñNŽ„YõäþXžäÀs/öÔ!µäú`ðeRêo™©3¤\÷pØHçùk¨ü–úõÙ¤¡É ÅÝȹPfZ=ž¶èFO¾œ½ÏE«ñÿqÊ,‡sÆŠøm™ÏDdKô:–XËñÒiÓꆺ4—Ä ºbÀ¾åb–ÚV˜ ç–O.#¦RmœÒ3Mvµ4jZΚåãй€v óH`Ÿ2ùØ¡&·‡Œâæ7ÇÌFÔÙ{ªp›þvbè¤üAÝíã¸=d«Í•ÅKIP)j° ÒÆI>¤îM‡c[“Òž÷;0Ïb”ãMÁEÉ8(P%uo’Ä^œFê³3ËTДmำ)Ú–Póµ 'ÃJ 96sëŸfxˆ$³¦HafÛ‡åck¤OÇ­Þi èʼnfœp·ï‡õÖ–¦#6ÉAŒõÊ%ÁÖ-f vìÂjÞèy”¬ËZäüÕ£)]A8kíR6-#Â6 ä±^]ƒ½ÀBéîšTTÓuÖ³u¤Ëê4}'"E(·§7æ™ ožêì7—r‡³ß\èg¿ûŸ\eO¾zkÅ0ÐÞXÝ£›7ÚlŠ+oÝì\áNv'óÝ®€@‡©ßé[´Ö †Æpíß·­_‰Ðèn—ŸÞJ„þmç€:Á,|ºaôH†_bx ‡-Mp­UÂ%òÕ*BC± âŸ ²®À.êhj¸eßûBÛ(#àU£äAë(Çgýš?KÆ¿Ðë^‘Å‹Én¾½pÜÃÇö™`æ>™L~»©,kÿFÁÆ‘$Es¬ ܦñ|±OàöÀw4ðËû«§¥nÎ Jìwr[â¶ÈànϱµÅdqPdÁU¼äÂè¶Â:¼ÔŠw-hÊØe¥usÐÃ\‹“bb`ÇH9s7aÒ Ëå+ „&™÷ÞaËz!]9J_Ó-fÞ“#_ÇÛKkΤ=1û§Ò©nXWvÑ©nê¶Ù´SÝïxïTïjïTóÈ3Þ§Ú–öíwAÉùo}ù> é§jFžÿËúãõ¿fñ žDk ¢ofÞÜÐËLrsßËÇsMàF²Ê‹@­ÉUÎÅ’®=úDÿé¡y%§ Æåž ì ý÷¾}ŸÚûö{ß~ýâì“Ê"çq§$Y*;>dWí ¤ÃÍE%ò¯ÑP ó'æ7±eÏaç8vª²4-\Öï¿:j¤ª!ë­ïIÊdËĬZ/¾v|m\ÜGõôëŠov–þ xiã¡ôiªrÜ%y^+¾Ä‘§Ëw)蕜Y²Ü͉‚ÆYPDZ›Š<¹q> á‘,z¡eÚ3`KMú–nʈÉûS²íkw÷Õº>8¸.©M¬+?#ÚÆLH©­‹ÌàIÑ”fI½ôê²Ê#q²}ÆSûléÐñëÞ¶[K#áŽoÑ·vgäi?Ò]Ã+wË΂'¶ÓªáÁCgÅG§8#s!l.ë$OuK6Ô‚bÕ-]3ÙŸÚÎ;¥Ë_¿X€ÈÅõÿÔ¯ÙÔeæ”Ìõ@Mï5À^Œå˜•ù–A¨l504±ÓçÓ´¬ øyuÊQ–_õ dz€cÖJÀmUÐêüó ù?‘ÜìÚ’Ý}rOpÑBÊ4#£<И){Øt~·öí²¶ƒG¯–yòÏ^äßî¿®­‚Ëy-œM†Êï ð›EëªU®ÝœûF²æ‚Ù¢GÅI-:¡ëM£N4ÓQ×ÿùæ·PïÅO¨'DšP±߬0gu3˜sH ïžmØì†?²üÌì*•§ endstream endobj 102 0 obj 3974 endobj 184 0 obj <> stream xœíû¯%5Y9ñ…À 8&˜àã\¦ít:ý…]eÑh$аuaYÖ½«P4Fc@Y‰|¿Ÿøß¿ÏNû}~NÏÌ9çž½ww² Üïôõõ{·ýÚÙÍò-–åÍ?øÿÉÙ­w³ªÌ~l¶;ežñBe;æ/Uó<;;“²¢75ÎÎNÏîËÎÍL_ÙÝ‚?}X÷Âìßð¿“;ÙíÛ³[d,Û>5ÃqçÍBÕ[U&Š-™mïÌnyÕ»·ÏÌÊ*Ó#m?8»åÕäٜ𲬔mñkLq‘YèrS*2[÷µ T—« x¨e U¯0U™ëèu¦£J¯L@'Ι~I¡,z½©ª2aÁ«WÙ\ô¢+«•«ævâQS(¡ÐL•q…(ÚRDê ¤@†Às™³:Sö—«2sÝ¢ßØÀ[#Ä ¾ fÈ|¤:Z*¿ÙÔFâXˆ!3ßsè…jž ÛÏ[M¿2›yËx‰s¸*[èm T躾֌ª²’y] וƒÆ×áäS[<®'²pØ4–À”·„2eg_h†ðh &=Ýmýi”YEzðzŠ3'={Å7x(ÖZ³Oòxå(*ï0 f·ò¨çhI‰s4ƒ4å®)bé3Eøó×uè– 'žW…j„£ 8zl{öqýowVÔRÛ±\jóÖü)׆GÛ´¢SÉY¸­Faùëü¡­6ˆz–Bÿ”•þ2{ô¡Ù©÷hy­8” öögg%½v^1üZ†½-Æ¡`p(}\AÝbøµ {[Œ+eÅ úpèÛ_ËÎŒœ·ádd©ÿ£ªrK'c¬¾à²ˆîi-q ¤Ë6ÕÚ£ígg`â¼¥œ´®°µªp²il‚&ŽŠU> …1³Eg³%Ebv±fÄCÊܯ©Ê`Ú9Nû:Ô¹€h ¡WîsÉ© ò«—nâ×w¡SÕM¹ä sòíFc­dÕº¾’ú=+\h3`Á‡Ì”[ƒuÜhUyöLÛh€K&FzÙI@ÇQ1ž]•·"« jtkOÞZ„^fü8p-dSˆ¦ð¼NîËnËB*ƒQ—㢄k©Š5EÂ99‹»6ËANH×áFibœ—87ª¥¡yn •­çUôT΋BãÈÀÕQZîÒBûW?ø¨I ìâ¥+`êTß´6«Xü‰”Ýl©‹·ô¹ìS" * ÑŽð¶e.cm#4*i X¢¸ŸÇŽ¥–H¨¥çu&=,h˜yA„Õ¨…õvK § tãÉEXç‘ÆÊë9°Ñ2´Ñƒd¼Ç^±²Ÿ!tXç·"ÞHu}í(·¸fE°tšgШY¿&¹©_G«ˆ‚-ïûRô¼àZæs3 ìãzŽ`y0Éï4ueFM¾nä«J9"GÉæ&ð•ÔkUHÛ¨ÏM‡tù¨U4±\Ñ¥éˆCœà^غš@´¾‘FmKŽHGUB†jjTÑ™¾D†Ø…QžW¤|ÞºÂôy!…lx ’S7Ψ1¢a”èQ CDÄ,ãㆠ⨪0òòÍõȞɤ8 ¯"âbÀk"L'JÚqî5²*ê´'PiwÆå$¾ Š•?a³c7k!d]¸ýb„Çm×õV‘)Î`Û8(§•×4¾€§Á /±mÌ ›™ÖµvoLšÍ Ù»Xf§œisÞÎ8¶Æ£²_¯,N/¢ÚTª+ºÝx´€kãÅa],vPQZ.¢ûV•yÊs†qt’b.äZ»Å$„ ÷˜Û‹^÷¸º)±JB5§ÊöQëó+Üt¹Ë°»t›.6°Ââ€]¶ý}#¨ü5ÛÎón"¶'Dôc*üšF­ê/[B+©ÿS–6!ïbÜoÀƤÂÏ @_Ú0ù¦¼Ï@ÚI«§Ÿ™ð“DhˆRYXÊTFsøiÒñ€E£Ù’àmí†g¶«Ï€ŸAO8tR¹ß¹Žý­F|×ÉšŸ†Ò<6ò³¨=£F®p÷#Aùî5½çhïæÐÛ#ë»9E<ñN ú‘©«48Ÿú1TðYbož!œ}g N»“˧À|Cá·€ë‰Ê®c*N ¼´Õs+ O.[ID.øM±ÛG´&îƒ2Å(£]"`ùM„ýÎ*'*?ŽÍ£,q®å³Îv¬äû­í(øVelÇ1ð$vŒsd6ç@%-tŒ°ãó*7ƒßCv% ‡M-t† ²c;rRúS<ìéj›>G z… zq´àÏ@=ü¶ÛškœÝ.t £~ÙîcAÉD ãÎ7v¹Z½¢„4à¸CÐk*]Ós t²5Nö(´ŒŸ³ =9S¥“­hcDëN€|º53ZÎê&oVaeBŠ…+D³ßEèÄ- KP÷çȹµáÑΉ“¾$è´×³^ì!9œVÏ¥íû½@,¬ ØÛ×bs’î~AC•¼pòt…gb¢®•ŸÚÌ*n­ÎP_þ…ÀGÌŠÛI»B$Ïšq ¢RËz£kjàܩ͗¸‚:J”á¸ãAÔ¤ßAF‰4­9G"m@43ÙÀÙ»õãqàÁRb0F¦4RÐÖàU)êK—fµBYŒ êß>‘ÆCoÆ$D‰6"Œ0)D×.Â-S7*§5P§•ãàÍ 3㡪À&©(âQ‹ÀœæÎ¶})«¼ž‘:‹e:HtÐ àq˜/y2ÚÑ.Ô ƒ¿¼¥²ë'ƒ®õqÇKg G5#50–ðX,Ï“ú\#I¿Mï8©òCá•,ž‡a᜗zÊ JxgJl€Äñ èøãS.6â1Xí6ãêJÒ‘ê[¼`G>›”Û†s²ÝNM (P)g¤M#µY1õLj 0(èÌèJ-Ó‚.Ü:ÁP„µ¶qÏD©aµüWÍO*wûž©S¿ø{°KL“ ÈaI°ážûPgsµ&Gyî0O‹Ý_Cé\‰ám{wÀ;)5=é©¶è‰m˜¦Lèá6=ÑÀ3ò5ŸGWÝ“’ûó1©xÄNùÂìˆTz 72••»Ã¬›/@kPÖOòÜŘJ×a7ýD¢ &%Q©Ü®`üxˆRÇ9„àð• `D $Ç.>3òdƒ&ØÑƒÐõ1ÙùàcF3JèüDÇáA²Ðã€6&VŸ Äp@`ŒäO¹‘Qé‡w4É×Söd1ï—€¸ßDX»Eð—Óžµ+€žž0 t¬è‹3¡õƒ ¥s9ö$Ó¦òUGݬˆtT‘ >=)²‘å@:3.L^ÅÇ]èå i¤ÐÉ—¦¿  ųXº¦BY8Ô¥ücÁ{r²Ï-T5§—£›Œ µ”ÔálÃ!WDÝ5œó€2Uôôm,zIdD|»N+¨5°ë·f\í&€WBkgŠ‘¿7‚ÁÇðÚ] 2P³4‡ê˜Hý|2Õ²³‡rãE™õhhÇ_ðêÅb@æ|IsåÇS ÀÈ­ír€ãÖ·°y1Gû2˜äbNý#ôÅö €…þ°IÛ2/вp‹ý= |íHp9¸Ê=20é&³ÈÀ8ß®'"!BçBQ_H;dÑêVûµZ {e K-P›îkÃÚÜí[œ¹'w®ì™rËön?¬ƒØÁ•.|§ï’-ô[%!Û"›%owFì_wGVÆep­Æio|Û‹QE k´á[ ñKl¡ÿHíç¸ ·`Ó,u¡)~C|Ø=ôÈ™½ÈÚ„Q³©èõ¸µú:ï…ÑGÂݧÀ‡÷ãJÑ?S¢7ǧ˥äAA°×p!µ.iäà=»Ê«z÷ÀR×ý0dN]èOóªsÃ{E¤çöÕa nl›….Â^NÔFI­7‚£¯^I—ïò}=Â׋`k0ôÕ,0Ì¢†’1k÷ï¾|$¸y2oiß”|…ž#§ž;©º×G>Іú‚íøtîvKšNºR']ááe‘kÇÃË“`Ô Ö‹wÔq{½ô»4qŸ„C"jcC,zÙ¥g¡§fë³2uê8GǶ¾Ó—‘1fN*:fÿjcþ‘x¬?Iõ¡‚gžTñ ,°ùéw¼µK‡Œ«Šl \ùö8]ۨአBs¨?É’J˜p&Ú(£ð\=~ Ì_h¡ýpË N* ¶ž)éi¯ > stream xœí=û³&GU²„Mr¡Œ(Y@Æ«âã»ÌtOÏ£àÂ&øÀ2µ€)6–%@ÜÙŒ¿Xeñ6±J(Å" ‚U >PË>@üÃèÇ9=}ΜéoæÞ¹ß½wóÕþ°·¿éé>}ú<»Ï9s«(«¢tÿàÿ'Þø˜RMñÜ:ÐMY¨º-Žü_m¯Êâæ1ýÛõ¸yðÁƒwÏø±ŠÇÞÜþ€¥ ÃOo½vðÆ‹ª¸öÔλqè¶?ì ]šâÚÑÁ?ðSמ>hºÂÎtíÆÁ/òMUl”o^rÍ®µÍðøÅþq]„Ö]þ©.Bß—¸VßUç›—]ÓôÐõnßµŠÝã*áé½™Öõgü¸ä¡i õRßµ-th¾Ì5UWlô$¸¦[й«am×oû‡ú¥VªEÃSê]«ÕÐòÞ˜²ê‹6ür`fcßpírmoc«ª}óå°Â*Úá1`ù‡}oDNhU¸™¯€5L¶zUè0Î+ý¸¦ØÔå°ñ×ð*èZ?âZµíkápíõ³¶ES%Cé8TXA/¿Ÿ{y€ã~B WüË6å5ÐB𠫝í†úækáU˜è éuðnº" rÕ‘‘ y?…Y‘‘“Ç?<ÍËÒÛvû´4U€«DRy½oÚínìE\Rä\Î ¯ªø*B™nŠN×oûÒÃ&X˜T†^[¤POÛч¯ü²ýwë î•c¥±âÍýi´²‚ÇÊ´ºf ß)J¸CÇ0ŽþFXigbaÕØ—ëÎSÜ~ÿÁS?méµSø qЇŸ£”LÞK‡_=(d´í0¨ÞÈ0Àƒ)ÆÃ¯Í”ô |WZ"èÔ¡öþßüîY®{ÐÓ:J›·øV_„­|+PŸi}óóD„߆mߨÍrí‡P TÍ0”AjÄiàáÓþa[À»ñ´FaôEOB]úaèžzPwiK#P¾§Uam¿áå”14¯DáMÿX£¬y˜«kF®’§ÆÅªS}‡,r ‡ÎGqހǷ‘¼ ôY^=éþ؇ò£AL+ÄòmP%tè÷ä!Ö#Ð ]±Qédægöû·Ñ•Š¿Ü‚×aêÏCsS×ÉxîÌClgþ6½SH-´vÜÌͰÍVwÁ`_ò+2qEG°Ë ÔU½¦¯Ž×®¢MFFìfó]–ú,:"}_%³¯ö ¨¥lÅExI¶¨F^$îai–BKÆ-I9Úö1’Ï<­ ±úÞ v‰ ölq–r÷ÕQY] ÑÚ1»Òµ©êÃìá~²6鯄öÝd/6LMZ·ì€Ó`yvhy^A˜±mK^Åa_ :—ZÑh±¾ú€ ?‰¥šÞ7ƒïSî_ÂÓM«ç¿+ùTêS9<öÉ@te¼šèˆþw?ˆ÷’âþ2 3,å)¯VÓ^-œÄçúÂ$ûbÕw ˜û`ٸŗQçÊþ“0¯w¤y€[±Ó|Ô/¼ÓÞO߇»í+‹Ñ©MI­‰¤ö:B——@µÐ!y…–Æååè’nºh¾E*&Z‹Ôvr’µ^¼¥ŽsG7Óî›H‹*îHpoébã^†£šB98á[ú2aSè/9Á|{MöÝJ寠ÉMO$„=`CÑm§‡iµ"èw¤ûª2çG"xüÇ_~Û;-.”b8R­Szša{Æ“9iÊhËŒMO[HÀŠ`Vô`bcfÊÐHjTP_‰Ë‰Xäþ)+³m¥=Q ;ÑÂbÊrAŸlQ†9Šs8§t ÝDv‘„u7%gà)W‰R&G׈‡«KìÜŠ+‘QÂmE–µÒʾQ¥Rñ¸¼©ÉïFYN­`®pöOÏ¥GÒ£ih©Y˜JT’“Dv*ê–34#– €õT^ÔTùNØ7âé¸j†KIËtŒŸ¨e1aݬ !£9w~mºåGIEßG’íåvÕ„À–n²ög»LÿžŠ]¦©~ pRÕ2a/å6o R1]2ƒºÊ!ª™Ò+"7nê¶$ªG–¡Dë¬ÈF¤ )¢èõH“¶aæÜSóŒìîœÞès6­#RYò&ý=?–)ÀgýMÀ.ú°Y‹^2ˆÙò;Œô¬oš¸SÆ-¼}Ø”Ü6kâÏé–:l#³3ãSŒ3<½šÆ®9;^pç;NÝÄ5£;³-‚b-U{ÖgäUv±h<ˆú £–Ü_Mzþ80ðF2%çˆjT Œõ<¡öÅØ+á`)²MÎ6µ)Ë¢^pêtaOŽÜ¥j™ T1™@ í±ËSN«­] ̆ ÌÚú…îwÁz¡ù,<CÍ“…ZrBX#˜s&²Ò–ŸpQ› Á M~\zmî"0MåèvS®›r2’Û‡žø-Ç4…Ð «RÑ:RÀÎÉ\#»ýädNµB¹„è£DxZ_Rµ5ñ:ý¢Jmâú16ît¤Ë–‰öÒe›t‰ŠM¼PéËy¦ïï–“ß¡âg yž–øÙ6-?}¼Z}%Œ…OÝ“p¾-ºŠš±“öØȦD²© @3uÇJ.¶Ä“:=š=\mñ -Hmê1Gî•Þ*ÊÄ—›+^U5\uäü¹x ÆŽírW€’#8†aÓ&;Â\z'–¿Þ\²šŽ”/óé×ô’)Yå¯1šÉÇè$Šw­þa¥¨Ÿ”½©`§½ n&]˜S¾—l'Ë*iùWÓwþ£S8~¤&IÄÈ·9W@Lµ.Y:“‘#$oW:\>™0¥Që&†·ý!’Ù×;à‘ëjųF°¹))ÙpÎ÷ïd ÿÃ=¬øZ‰iL.MS–g×ð„ô;®·êŠåô™ôpÓŸïW…ûåO§\.LN#TÏT´s´N£žÝßâÍ¿#³"Î>5°9·dU…g¡"ƒèÈÈx‘>'Ô]˜5N±Ï4üjê6cq¬ÙØKEí±ã‘úEžË)I‡-W|Û›ô<ýÊl™5o a߯M­s“ \¦15ÖŒGÚ-¼rç¼”½ýËž‚ZAfé /Z×ÿ[O­šÁ·V„mGùrÁIcæ&Ä3!Br6áš!–»pd²–MN!æ-Æ íZÙ²ËņP1±b”sv9‹"ô«ø¦Ilü&Úˆ…vµøi+– I=ËGS× pÃ7Gg í[áÕ™(¼ÖöYŒAž›·A}©xÑâÀåØŽ‰hŽUôþ¢ÄS2ÍÒ@±Q.:T©Õßø-å„w‚⮑¥ÝTˆ 1Ïhåw$F~H¿M• KyÓä MóoÔpl±ô,‰QÕñµöÄ%ÐjQÍyeÂfYä÷Øeže'Í0ð¨`vÌ¡QLCrØÁ"w(Òƒ˜H8¹ÂíöACƒÇ ;Þâ) ìo‹Õ\2Ï…ùKœÁs“¾>B¨ Ókmú:–sÁö²ª.µníÚ»Êþí²þ¥ÐËìFN]#È7Xô:5'ì—jáS…ik_/Á4ãÇ)eSwæP%‹–J\PGU ²6ZpeÌtÝZ3ª†óê¿u?¹¤ÑM†ÿpvÀÑ7=ߘ¢ Í¿#kø{躧ÿ@DØ?®ú&ïÐúˆ©´gÏ'¡a‚Ò€#+ÿ“Š þ¶JAßo@ÐõÛ Œ`\i1eœ5ŒÛ`gÀ“Ž‹ýg²¼ „™¾I«BªŽAB–‚œjÑ@Aïºr~OTÜaÒë×dHoý‘@ï±¶1P~Å·Ûš ü#à2L0þ„ïŒ;ûkdà'PÇ0\´JzÇ‚£,ƒöÇ¡ ÍÏ!˜Ð¾F× k “ éçá9²ÕãDX|zbè2Áæãü° `³wÂÄ€û›ÁPïõL8¡™»SeŠ1kéÿûˆ¢ðö§ài‚üaæÏ¢b[4s̶~âVý.@§¼ê­1·Û€ÙsÑÌúǾ¯Ž9á¡éÛ(e]½4…dô¯ÈÎde 8Wï‚Áj‘?·Ò(?M‹³‚g´X\̳d÷â3H®ÓãÀ”›Ÿ@vhÏ®ê á°H/é«8ì‡Hk%:¤J˜ª|2ò®ÈÊ ;8Ÿµ%K!âîS0:wuvK©bÌý«Ð‚‡¿C÷E•s"«Å«œº/k¯rNT€ÆîlÐü0†•ÌiØ«¡%TêPe”b…œž| e-FERb)?ÆMßeuNpub9&W§‡"†Õ¸áÕ/"õáA‹ ŠUžL|õàêlÙ¨†Ÿ T‹„¹ƒbLyqÉz •AÛ õvêAÌ}12gèý §Xmf¨-ò0¨ÝòçBR•qoò쩚ôÏÂÈðð ¨O ­ìÐù' …\˜ú~ ·fþDTŠt6À•É«Îñ9ö<|A‘ñ6°¢&¼ü3°\ì KÔ8Ú›ÕW»#ÆøËòâ]œÏ@ÄZ—©º44b Êù ¹~WM¨þØU²ÚÙkO¹á´ (í´F\O3ʶ‘r]¦£P®Y.Fï¸ö`yžè RŧIZÇÚYû¢”#ÂØ¥\_õì‹R ¤¶/J¹/J9­<öE)÷E)_E)=^Žºx²¦ÂkJ R$a¥úvʽy„ÌLE=¥@I¹±qó(éyu¬Z||5 ”;RmÈ@ÜÜȉ€“àiSõ š?í놦@œóêûº¡ûº¡ûº¡ûº¡Ãî놦Üß/©Ú—™ÎŒÆi©$ƒ¨˜SiU=ÉGÀÒldµ£¢jË“Hª3eá…í&uÉ ÙÔÚzF•6“Œ»/.{cQæW\v¼¦Q]Yí¼Ð\aÙ-Ñ“rnÀœº~9nbÉìsø»¥Ï py5×v¤†Tay0W§PŠË],¼v說»CU躂Vý5Yçû=„ÃgÌB]tø^•Eh¯î%–SßS[=;ÆãUÕ­û8cYš¢oVГզôŸ|là›‹YLÇü¨ç%Š˜*Ë(;?©ì,™®‹”7§òâÂ2¼s¤öù4¬â´zWŠ«;±ª#ˆq™d)ÁW‘µÖž×E®•ƒÊ‚~ÌwÖ4§iÊ¥>Fq•s¨¨Û‘©ÛLKŒ ml÷žæH ¹õnrú[ªLÆ!šåh,5¨ùÏ ÁnTW–û<¦ðxŸÇ´ÏcJZ/ì<&]Õç9I—í)ä1 ‹>OyLm ¥w¸Ÿ\x&˜Þ?çÛ ´Þ·[|ü ~@¤ñßa; †«zŒpo ×ÿK¾U“žFžåQ—ɳH“Z9š®g¥Œ"eÚÃþNË(bÉv*XT.÷λOê9öÜ»JêÙ§ÊL' “þ>¼±¼–P‡xÄ޻ȕ1H¨ O锃 H‰ºeÍä¥ÍùJ~©YöÃ>ùåÆÉ/UÙâà§Í£Úaº8çÄhÓM¥hül˜…*5¦[ Ÿ³ÉœÙÇ®k¢ãDy7*îãØ <~Þ çƒ}ÞÍÚùeµ»| Õêc'ÞhUˆwlzL¯Ü/Gø’µô,N²Ç2ÿ’n§­Jç[ïlç™è(k:kR%ëh1ìR1=»|ˆ‘ôiR™ÑDuÿßžBÕ(Àšg= ê“ €%=ÄL²ãä84sô´žÅk³h_W‡;³4`ß/-YAÂ΃$‡•wdäeaã!íDªÂ/Ogö;r÷¬Ž&E ±î~ìZ“”ľ¢½¹eÎ Ë‘w@;÷xz0'g½Ë£±8ßXL^x;h¼H7)YK‘è=åÚ|çæ¸|‡/® k+ÿ aÙ1?†ÙD'ÊÅqÑñuÌûÜZç™Y§(ê©Ðh3¦]ߦ/ŽÔ Í!¬I ÿ©åm¹$'%¬mKÎÛêúÐY +تڎ ‡§.ÂLã.ÂÏ0«BþºÊÒ Ì6Þ„å?Ö%”̼Jº‚Cû–\tvͽ ü&ˆ¤› ãžiømþÓÅãØÓ¸Íû_€¨ÚM]¹o£‰i߇ÖÞ˜­­œ• ­}A|qþ\|Ž‚FM|9ë2H“*Ù)6’?D›S«Óù~r4Üv–’?Ê×MÄD‰± †Òë2™ç”$Gò—åÌû.øÒŒ³óðe²-žèT¤à¦n¬ ìuNRç¤Üħ›%+•K–ìgW³lË“u˜úp†v Å{}o×,ö+þ©Y@_ØB^“<Œ…OEÚ|Ué²O5ffWÖ7EÓ_JfdæÁút¾*eG!K0'g¶‰¿|¸×ì"syÛAë:-ºßøiS—èÜgà[¹W«˜Âw"ê,ððA¾„Öw@øð®—ƒhr““äÚG¾þ.Aÿ:´$¶º> stream xœí]é¯dE—hŒŠ ÂÈr˜àr÷VÕ­ªýÀ0 ÌàÈC4 eç ~Òˆ bÄ51&*î¨_ÜPü¿¬åœºuªëÖëîׯß<èL}º¶Sgùs»N½{¦j6Úª±ÿàÿ÷nM®¿ U=øèǟ˦²Ä–û¤zÖT§&]§égÛãÔä¡ÉÕ鉛«ºýøðȃf–Ö†ÿÝ»Uݸ9¹þpÕV›LpÝÚ~àªßÐ]µ¹5¹î ïÝ<9‘º2+mÞ7¹îG²ªfŽ<`I­ é›ÏuÍ¢òÔy®•W¾ï-Õ˪Վ® ¶pÈ î@)ï mÊï^…:òJ "3]cã–[Mfò 0Ê3#3GÍWƒy07Ú¨ç–ò|5h*×8Ò¨[EÒ ²¤Â9žA†²0¹Œ•Âãý›¾ t¯Ã+Ø«B uơݜÜfþ™ˆ¾38ÖtÞìÇŽ3<Ó„H×) ܆ukS Ú@¬$“Ú Ú¸Š¬¹òÀûŒ½êÐ -÷þ뀒Ѹ¨Ù«D:Ûö4FóödHTr ã¬~Àq®«. rÿÁ,ó(³+ t%è{’8R¡Lü¾D–÷ËAòª.Ñ¿ K2ËïK¯Ê,3­ñˆ{ÃèßeE¬%¨Ô3¥cJ @á,Pê@&ÖI´ŒÇ¼³Ë8bJ æâsÍ™Io:9ä7’&7v¼ò*ЖI†-I#ç~¼YÅ’t‘ÄgÏ=çÈ0ñEz¾ü·ï7Deðoƒ;x¤¢JCyFw‰å%2OÄh¶6Ê$ŽMPâ@Øio\Mšb#A— ‹¤ü­qÑaǹD€SÉA°ëÂMî šÌÁ‘$ FÝr㨒·_Û¯ êWµöÓÿ<Ù è%ç']¥=ù[²‡ßA×ZO ëÄ‹^°öÔ–âž=]¤Ó¾LôMÉ,©C„ó[aì,Ç|öý'GKì rá‹?“í¼ ‹WrvÔ¢Á´ ŒîÌ:­Àbî;Û½ ¤†üðÄ `¹SŽþ.‰ wlÕ7ŒO:ZU@~ŠXÜ âZx£zÂuF±ÞC&¾caÃÌ ô=°0L… a{,›z‚Ì]Z÷ñܼhOán¡uÓµ" ßÕV|8º ,=öS}Æ9XO70ƒVšX:&£ñ¸þm`¬ö£Ÿ„ÖhÃÃÊOcškeЏ e»þ„›½ÙõÃËXÛ*`æµZ\ë!â˜ßs}­ÁøÎ߇Î@>M ã)b"Ï%VàL5üháŒK‡~`¼óˆ]Þ?hÔìMb—8-j!ŠÓ>L¨œöM:` ‡Ï£…xðwlÉ}VsؤJ@Ibï€R€œI+ó› ÐTwƒñ4ƒ‹ bÙFx= ³gA&o^dw {J„ïu­Bçφ²£ŒÃN˜'Q@ŽÂ*ž…Ó„¡Ókž:JLúóŽhÒ׺i†}ßD@ ‡zê$YdËRLåýœg:Šˆå¨Ï˜Ž–dtyôäÀJⱎu ÷¸»301¬ú<AÌL°Å3‰ùÜHCß2úêñÀ`4´ COÂÐÍö¸Ù›hð{Æ’m£pr÷$ÎWÏŽF¾Ž¡)F‚³[â_!©xÔ¡yð`¸vØ3lehÇÜy˜d•è_†¤DKz7( ’ªè&Žëˆe¢ÝQ»Sª™•Ͷ òA'În„ÍÀÌÐx-º2Ð ì‡4óÐÌaW~®÷ó:qxôý1ªui| {bd®<>šÙ<œì©8‚Ï× éç~? {ƒ€8Îö!/ÝêÜ ÁoÐ#Ñao š< ÓAãÉœ؇‚¨:e'i þB…2 £ËŽSÈä)·® .µh|p¾¦fÜ;!fÞDœáxÐAðU2CÛ¦ .¼S0YB;«êYÌgÇy1 .¬…&“×;>í홺̳¸çïÅÀ_´—>ØÓͨ,ó‡­i‰¶ý‘cešO@@S×´É'‹öa$¢29“èè' z`ÛoC%1Þ¸qŒ·Þóè#cÌ!ÜÿÃY( ó¦%‘í‹`° ã`3‘)ŠR^F¼~xFØ;ƒïhg›®Ò½•ÔÙ¡Ô^¦Ð?…Ý€6,#9i:s¯É̉7¤YSHªÜ–ÚÁV‚ªðÍÉ‚¾ÝÔÃ3¡gk@Q@Ybþ‚3bþ-ífæIb9õt@;÷ø»Ç i{¯ËâK\›S’¯¨|åg(›±h™ÏÄ 0)5o¾¼=DïZa™-dm{•DÔB™Ý¶ñ…Ó?5op¤e/nœl†xÑIü=å‹®3§·:dðœù.¢É…—¹Ÿœ¦“Q•Z‰ŽYðöŽ? Þ$ä£yT n29ÅÑLO+ó¦ô"¹ÇS3a ޘﱾ÷pr}ïa}ïáõzïÁ_,8[ï=pÍšeß{v|6Ý{08¨|¼ÿ˜ýÊ–|Ab}«sdc(@ØÑ ›?â&D›þZÒù+~2ÜÕíÄ[ýL¸ÿ;Jž]~•[qV\æ/î‹V ¿ê;àdß÷eÃB»³SÃb~¹ýÙÅ¢ZÒeòö×Ðe‡áhùq L ˆ“ì‡pϰGXLàž—{•b}Ãa‘µWvÃï¯9¥Ûx:paë«#‡cñçæ^Ö"û,ÇÞ¯õÛzØÌ²o è„ÅPB³û·2B]æ¥fò˜³êRAÓk(×— î[èRA¯0ØõB‰–áÍ]¬/tr§DÑb¿ýrm`e÷VUø¿ÔÊþN·«3F¹xejëÊþ%Wö÷Œ­¬µeýÂ…ýÉõƒeVo÷¤|õuse ¹áÁRï¦!p2Ό"YŒV`cž«M ¿LìvYÿê $ñ Qwš!­c•~@æjÓWZ kÂÉrôã*¸£rðÕŽÜ÷IÂëë$«Ã×ÄuÙ®4”ïÎm®éeªõm’õm’ÒmÖ ¯ö6É,OëË&»pÙ¤µ9êŠ.›3“}4°_íû› èêÙëIu7=*ò+€…‘BÙC@G;›ªH)Æ 5…ÃZŽá€?®ÕYßÍXßÍ$·¢»«ªO‹,v©¾²±IÌ.]kó óÁ€gÏDŠÉe´˜u{—ºåÔ‹ó%Ùº(I‹Y‡ôÀ‰Ùþ…kýñúýa’SðK*ìÊÕå…¤0)ç$‘²yVAR­re‘|œùq›Ù¾¬k¨¬þ¹kíJ¯·ït Ö;Rߟ¹b’XkzW1)Ô ’{>ù;þE°æeu®*5¤Ž2ŽGøª†83LrbsiÞ‘{F©æxêí%ç,×nóø³ÃV*¹öbз?Ư‰±—%â|½9 |¿M{AúîS}ã¿`<õo·¯78X‰{}ƒ/ôÉ)#øô+DD_rÛæeÜêøp•¥ä¹l4„†/c¨zOn …ß`r·&\iW’“Û.% K¸žÔ¿ ‘Ì× ¼£ï©°ï€Š¤ÊÑ~áôŠÔ`ç8ô\°Š žódæÐü_8Íâq¡œkÍ]¸Ð¢2,DÍ =-Š|á e§·3h *¥ÔC4uúP蓯bH›ýùoI·tx3ËìòÊÿ^ÇŸ’†æñgìÝ.©…à]x®ÞÓ¬£§ÿé/nSyV.U˜åY»Gáb›(£O©‰1&àÐ'‚Qpeì@ÿþØgaÙÒÌá­R¯€?7æ¨ð‹û¥€Auçã_¿Ïì2A‰’WfóÚˇ…GøâƒX€gsm[¾Û9rß -tŽ_eã'9\6ϾAʾh0÷ypmáýjÙ·M d‘ŸË)?íÆždÈ(Šbç =Zu¶n„ :~%8‘^¢çáyö0µÇ†fiJ‡‹Òù×–)Šb©I¥ïÛ®;}ëY‚‘óýÜ›Z$}]Y”:Œ„còöÀäÙ 6óÀþ0O)iŒ¨yÛvá=A«x¼¼ð¶Éÿ»ÉšÊendstream endobj 246 0 obj 4113 endobj 252 0 obj <> stream xœíû¯EUP ~ß(K‚ >¾ËîÌ>f£?¥PÄ€Ô‹h(-ÏÚV[à'CbJ@…¨ßo£‰‰ *Ššˆú“Qÿ,çqÎ윳³óí~ßÞÛ[Ó4{îÌΜ÷93sfîé,ß*²Üüƒÿ;¹¸ñˆ¨ÊìÑ'§²Î3Q6ÙIûSÓŠ<;±¨*E6=N,[Ü—Zر²#·ÃgÕ£îgøß±“Ù-Û‹oΊlû‘λ4?ȦÝR™,·ªlûäâ†×½oûø¢V™žiû¡Å YPdKaÁ‹ ¨ ºæ×Ûæ2sÐl«Ì\ß7¨­³BYpŸ«º^b»~ Kí@9´¾)=eÇ%U Лm×&“|‹…Ê–rÝJMè¬J íèÛXA£%µ ¢èZ©·¨‘Y/«¼h³ÆýæmÀ™¥þÂÀo7°Ä¯*J ¾(,B¤ —ßi{#sT 0ß4 B­È¤çÝvÜ*[–y'ø i¸ :;è=*u_‡/·³6Y]CI?”£ €¯@âSwx\Ita¿ý¸¡\ꔣ¾ÔµàÕð)L´ŸŒt |R¤Q. À+Aq(΂Œ4p_ìk->›Êá•£ª\kA-î&àžç%eÎ>° ò©ðŸ"–¡PdH¿î LwBÐ8‰„¾6¨¡V9“è¡íÅ=úßéEÙVÚå•voæÇJ íx´O+KØNÞÃmƒ1ú×ûA{;í³ZúWY©´©ÔÙ™‡¼_ë«ò µÁÞýÚ{Éà» ~[óÑVã 5âQ a‡~³ûmÕ£h5…R"Š4 áÐo†ßªm&UèQ𦨒6È8Ùø°â<±dÞUb³õêšI¡“_¹ö)²)Ã/üâR7`Kô÷_n@c Aÿe!ŒƒoTç§kꤵ¥-«0–ÄÖHp+ZÒ]Ö#v”šxäÚ[N,¶!VFø6~áè—Ä›p\‘éä­½A”.¢¼w®#› ¥ÇТ¤(J Á~¨¯OCïƒLñA¾d…r»×ËÁë%ØÂÅV­3¼íFFŠ|äÁd¯2täÝX¼ó~âõ}ø ™W{)P¾_ŒQ€„.”²2Å-ä{¨&žUWx0PŒ.ØÆì£$6ÐâH±®\ôãQPIsaüoZ’I(¦Ô`J­6Ûfá)8@¸È’t’QŠY‚æ>bR#Z–MQetGs \Ëx$Ñ1#€i–C7ì‹#‰”"PÅþ%L9L¢SMðÆ\­E*×£®›»ÌmçÏ|aÖ”“7¯½ê=íSf2ÜÓ¢ùeà” o$ÉM™@[ ÎíoêTXêüKÌgY#£jD%vÚÁ$F êXDöl$¿>¼Ä¹"ÖjWTÏjYnÓQèÔ£DQºdGúæÍc©N#t™R“}2²J‰¼t•ÒÎQ¡­6Ù‹SÕhÿC%ªò Ö¼ á ¼âù§£î—;o"BêíŸgeÐ XùÕË¢Êb!VÈÀ˜æd šÄõ¥lµWÂaõk+R®x‚‘¬¦Žd–õÑhuXRÔÛÅÅ E*ƒéV§þ8Üh3›KEè,ãB‹ Èû9*aåš‚à2ìHiäaZµ~Ô®â©Ô¾(òT¡TÊfè&a"³Iˆ²5ÜÌ“FävÔ'ãnˆ¶ÐÀÐÐPNWÌ^q( «óƒš¸B½hG͸\ÎŒm.¥Ð¿r¬‰&ÉyzÙÏš57!'í&ž;¸h¥¬ÚÒŸ9 <íè¡m·Ê¬.[8zˆí$eÇ4ñœ{-Ù*5~ˉ,¥­6µº”v®Šì­sÚRhí(ŽíÁ ¢*qG>”À¤{æ¹Ð9+aßü7æWE®ù§Üð¿Kv zÉÚI•)¾Lhøt]Bëï‰Ëú±¢—ÀY;èpKaÏ–OB1j£¿9„½éþѶ"Á¯Ù¡ô}‚®¯Aðq_F]€y_%zE‰‰¦ü¶™CPÐq)“rO÷ÆÎÀé±øaÛ« áLV_ TÌb ÅT•þOÕ ™÷‚¾ÜïØP= (W.aÿ‰>÷{\VN?iá&ðSD³¿ ,ÖË ?c;#[$?€±’ ¡GøA˜†r~è³ø"¢ ð6m~ ia²iF20s½–=C0OQu6†5êÜsHæ\Œ#p8| øŽà^ P: Ú C}ƺ‰–0BæyÈ—¹èô d¶ûúYh îfþ †ÚI3+óýÈ úvô|€êÇç˜Û`ô\T…¼ÙÏö5 ã::h[Š:×,ÍÝ䉩˜I:T4æe€¨Æ ÔóÅÉs{çûQOn&Öý‰»Ulå¹ûeTÀáÎ6ñšr§MšÒ²$6℟â°(ªy1´óÀ¼SáîÓ‘,¤(6øõßQ¯ û§abPaæ{PØû LG¡2UŽ}Fú£¸6PÄ,Ó³—ÑìDÓøÎŸõ‘l£ËE²Jm)ÉÁ,…S¡SàtˆhàçÁÛƒ^o‡é辕ø7üÔAÇÉ$' $r/Û2E9„ÎÍBŸó=˜¦ãÑ͈£ ~[á·vbŸ†aÖo¢„XP6QÆx³8ÍÔçƒO¿C‹uîðŸVþÓSà(±-{+“Ï;­EÅ´G¼²3°èLjÖaÔÄ€o†" å“›'¬c–…ðÌó$ËNUºÐw˜8§“nª–Š¿ñxÞDñ<äårׇ¾Cƒ{Ì‹I‘{*>d9Ö¥i%QéÂÈÐx=†"€% í:¿—hÈÑÀX ïQ4ã'ê°¤sV|ªWLëÏà ò†·Šj÷ñ€\ì $JíÃNë‹ÝSFÿ4 ´š›ˆ,n†á ñxLÍR„¸5w(Çiú ËWèPRˆä ;oI´~]'mÍ¡M{h >pÝJÔù/ƒ¨×=Hf‰|Ú LÚMÐÂ,FR_‰ÍJQ,¦Stªò™ŽÅ«ðÕÑp·¶¨ÔŸBá¢rèA!8‹wR¤ô ‡¿Ú%›¬¹ã·0šM ünÑüŠá÷.+pây"nž$­Ñt¥[†žîGW;¯ããm„€ÛÀÏÖóÈÇD"@ù8z &µ ´œšˆ¹œfrú ˆО]ñüÆE<äa«E²öÜü‡URÁ„kWXoÎλ€`I™ÊÈijßJšaž gØV´³Y¨PÚRÒ¬k?¤Ð²bÆâ•nÓ‘[EFfÆÀ³ioàNŠ] åð›ã qMÐRÔ!T’(¿TÄë¶íÍ}[oô–…´s‹{ccÖk*Í>fà\®5ðóvJòN'NP­>ØGÛT÷„F›î\¯kvøá ¾°D¯òO@ w¢£åáEž'í¶EL¾êùÞ}¼¹–0&ÎXË´-ËE-õMb9ÑNY!Ø­ÿáðZ"MñX\“fÿíjÈ„n-D„ô¡ØÀüÃlÁRûÏ9–O…ÉQËÝòpþ7îsï\qÙÏ˨á å W1i‹¶Zò&3SvE¤À>² ò¼¹ƒôŒ9ÕÓä€å̸")(1ýùTæhxWâ?<ãt£¬õЭ#ógæWMî‹ Žûg7® Gꦹ‹¶>J[?F8â¼ù¥ó”R³Î¥’½udÇ  é}Œ D|¦zèªÚR™h°§õ`Òÿöž`ïbÆ]"ÞÙÝ>â¿ýœ{úÅJLúÂJnN2ï:q2!y¡{Yÿg‹úþF<_¦{ 6+ Ÿˆí]]e{¬«^ôIû!½Ö×ùe[ vhâç.1 š!Õ¾OÑPÅŸ"$áߣtû~È:ÿH(0à)`W8ÚôO;“:Nا?¼#[W_%Å]cN/õxdÜëG^IèéÔ7¥R{½-ÆÄ[?©½¾ñ‘"öúK_oò€¸Ô3P«ÎÞ$S’·0+^EÑ]¶ÒEÕÇ™ùèL±1xƒï ïÖûvÊ?’´—(Lb^»™®#cE 5°Ÿ>fßžÖ7°ý·©OÏ¥NI©/F¤ËöÒdÔÄô3ùœR¿úb÷WÏéõ^ÜPý ä©|žY1|x¯Tª¸dle]*L?ïÍSi¦ßh—#§wÆ.ÍIK•µ;T¬ÀãÉ@ê«©C©Üú õ]Ÿñc¡‰N*-|úàü¬å@£Šñæ|.s#ùU^~/ fÅ]‰Mmú¤$wl³œ¥çâ¡6-+@0Nl¿ 08” ë½/qÆñÙ›[<.¼V~±­f(ÝX¹3a™ša95†ù6'R™÷dlO‹íd¤«ÔØÂ—qÌr.£Rj—>=Xÿ8*R˜º"w˜²P©&ßÚßo3ë‰ki‰o•`wv¨ŠÖIÆÖ+J7”*¢ ý ¶ÓÿmqaB¬$4JÍ´§Ú?ßÁ±*ÎZý®¯Yv–YªÅ@Þï¦:“Š{ýÝ­Å+ŠvËXK8ïcÇf7àcèSÂåTµúªgÅ ¶;`+4‰B ó÷F µó¡*±¥Ý¤l¡ qïø Å Þ窡ítªÚ™eWe¯ï‰ èÕ L¡Wœ³‰žß_7ÜòÔyxßrÒºu#ÑûãéTÖMÛb%dä¯[˜¿Ê`N}’cööû!%Ø—X¹)½†ÑlêË8ÓõÊá(³ 9ŸkpDŽe {±G乘»§£ø<®ÁÑ“µn„¯YóƳaWJƒiÝÝ`úá§öØ`ä®Væéè/~ßI ¯Û+vî¢#±yþcAIê„f(Êi·LþŠùÑÇ!”ÐF»“¡È›“­/°›T¢ó_ gzt…•á°V‹=û–äzï§V¸,: ó’*#N-{¸r®:«|mòç¯ÑüýÑBHxyøyP7§¥-¼à,d]íÖ£¢)}îfô›²•ôÑ¿¹Þ:j3ò:äh„Š2ß­ËÝK™+£é›üsSs?tÛƒW)"Êé ¹dÁÝsQÉ3h~Ï»ú„“mûÉôöFü”|Ì*%UÀþŒÍ´‹Ðék¬ñ³¦ÕWGž~]¨PܰBqYÖæþÂóˆë ôpŠ3sà‰¥8C€ìn°ó?q|öí¸&ß’™°’Ø[¥&;xƒÏåéu«2U˜Ûj®­=Q›…žgèîFG¶ƒ¿ê|Ïâ&Acendstream endobj 253 0 obj 4552 endobj 261 0 obj <> stream xœí]é¯G‡$øX#‘Ãï'ƒÄ‡€´/ÓÓ×Ì7bâÄ óâ€b£€Û1~Æ/ H„Dà€ Ÿ8$ Hü‡ÄŸEU=]½=½;o÷]²)y½}UWUÿªººkr³ª7XUÛà¿·&Om¯.ߞܜpUWÐÕ–ûKwM]]›HÙÒ¿m‹k“+“—ªë7Vuö9øãÖe3 óÃ.nU'7'O=]±jóÒçÚ?¸î6ÚŠ‹ YmnMžüÈ6¯NT[™™6_<ùQWlªiãŠ÷Ùb«MÑWßïªEåK¸Z^ù¶³¥NU¬uÅC¶(;hzØ5ea #n jJ篻qI¥TP:æšêŠûâÇm±i«)$W¶#·Övþ–«”Pé–Ê$úZ$궤9”ƒ§²f]¥ý/g¦¦‡-Ò–9öÆ®ø)X!‹‰¶|ô\þ´kÌñ%†Âü ¬a°Ô5÷ã<䯕ÕTÔ½à%®áahìKØ’0m ¶ü¨›UWŠECñ0”_ƒÎÇqñ¥Î=kDÖ]g By J¨S~õÂÔ? ]a¢u2Ò è¯ÈÌZ2×@q(Í 9ª~öåzññÜTž®Uå W4âÖ÷/)sÁÎ ]›Ð©Œ…Âãõ›¶Àt/CSSÐWꔣM$zjsò‚ùçæDtÒàX- ¼Ù?%o ðL")¸Fá6솱ú7ó‡A;ˆ•Vº5Ek¶Šªn½6¹ôE£¯m¨P–zÿs@ɨ_TímE:Ú|T­U–¨¢a¶ÚÿÚ6ãi`­œéåh€Š!f«á×®k3ÂÈHilŒn[ógod(½j¯1ì'‘Aµ2»±®Ö±Ö;mó¶ã\v"G,³‘]·! ùjƒ;òý¦UÝîN°á>ÒõîIØ:¸“¦²_h£°ïc*û¡ÜJ;c µîœ¤dFPÛq ˜h£çÀ៚gØEKÃò¦°@cŸ@’ëÄE8F4¡)ãf­€çyð0¦­˯¸²® øU0•ÓÎð+L X‹³®„VöëZ¤« #_†™êhž–tmã:'†ìgÛ`¿³=^Ù7W …gÜ&˜Ô¹]½ª|í9àØÐ—Ð>KÏæoº²® ø-"¿ÿÁH8Ó+ Ï)€ÿùóîQIõ6 &;”2/¢ ùîmÉ9Ìö§½iyºÃ2_FâÈhÆŒ@ù§°R-Ðåd²Ÿ;Áé‘“µ8ÙËÈVXê9”‰$"Ñ^_ÅÜÖZx.†s]!›÷·èÛ€À~¡¸=Âpg€xñÞ̯Q¾÷;P[÷ªeÔø] ÝÛE®Öø5Ks´ÕÏøyPuTˆSÞ±QUÎq7¶™ó4¬(|”ÛvUÝ•¾8ã—~Ü/Ø'·|5­ñ¯á—gýùÈ´ŽfV8óUG©ÙÓ@肘؂Ù[ “:¢1÷LX„ŠÒ8ЗÉ,®k‡¼Å:ìzÍÕrÔ«;X­¼â<‹#kÒ–w#A¡SFtßÛî{ÝWKt®“å^H©£4Èìûž‹m%Ä.‰_U[Tb²‰¥b6p¬¤ÛÌÂalß(íÚñ¯&Æ¥Û=5 K<‰”ðž”PO†Dœ=§€2Äò“HiÓWw*©AI§¼Ìw8ãÜpÞs`{ç#é;ÿÀÕZw/á1<6ÐÁ"'¢ƒÞёКm…’ˆˆ*¢cîéåHï# –ø°!ÂH.¨cOùõ-F"Ö ¹’…+t3´Š`X(ã0h~­‡‰&äŽC¯º¸¨Ì8MÄúYÿ0Y =svÆëLËÅCVºóT¦¡…ø9'‘º.á ‘âÐÊ©ÂæO”AäHÑB#ï(OÓ®1‡#f6ŠÛ«Ãê=W¤sðœÃ¹C0­æp—GaWfBS¡kQ±x8E÷^»7ײ,Ùöþ'ñÐPÚnZ½]D0'„:+àt%órq§v(dõ8hJ–‹8+•ÖÀö’@\ÛSEz-« åݰê@ ±ÒæßJ°Î/_s¦·ª_teŽa¤'RðáÏ®º4R] ±ÁE€.s13­Zu#9k”Y|mƒÕ­áÝJ¢–jSâÔî¼”¡$ÂþâjeiáSÁm.$Ñ^Køœ+iêÙO6ÑÖzȺrî=ôÆ<’âeq.x/»kò]}§J<Ý¢÷@7Ê0¡‡ŸÉ‚ ÊXíágG¹ÌÑÒ b&«´´9˸%XÓú@š­”s)ìÅ ß54½Úæ5úê@Qö~ïÏ<ëÛUÖ6úÊÀøÒ?ÝÆbW ZâËÛŸFØÓýÈ-›—qKZdz›"ç!ÓðšJ¿ÊÛÓãi’5l]ñL¦Q Ð%Ô6¡ë/Á2€úz†Ë Hü˜€Ž¸îØÿê䊥ÁʱëýÀ`A=çÉÈ¡úß`pêíÛ…²¯PôU%ùƒ( Qõ–óhOa—=ÄS³Tr°{Ûꤣq‡þ Ü⇸Äg^Šü¥¸—{}E(—iXp\Àz‹³ª¤u~„}Ù«z}Ù+$Ĥƒü²–s#26i†¹2½ëÜ@s¥Û¿”ÏÁH—˜’†Âˆ(£ "Á »:sÜ)¾Ûð!ìü:B'î8´šJ"¹{O÷*AÒÍzÀÇ……Ã~ñÈ Èi cåÛ™GÔ×…oNèFõq;ÿÉNÝáS>9S^_0¯ß:޹Ú‡¢)É¦áØ¡Šì-6>ê±K™öŒ ÚWP#½†s̕ﱮÚRÁ†õQÀ‚b©JõÍ,Ô<ØÝFŽ‹ç¦.u+Œ3ù>FpòG kâ85 õTŠÚˆ)gLV¢‰¸%Ãs^Wœ*{I¯DA/xG¿•EËÃÊœZLYí8¬†ÁHòäã[`xXï1W\ç)<’¥êwßJjbûý/=Êx®Â“Fk•w®f.6 ¹bH˜|é dgé}Ü¡ìÌ<€-â.„‚²Þkrµ7çLÆŠÞì†G¸Ÿ=£Éí%Z®Ñ±ÒÆëÄjd=³>~ž3)^×? Iâ§ ß þR‚Ï铺?ÄÌk†Á¦©è'aTj¥F¼È¿0H|a5ÏÙͧVöTfM…6ÈNãå‹Û +…=4 [y”4Ã%‚‡“õÊij:Dا٬÷so Ò.”¾–åq|6L_jí7§Ö‚Yêñ%‡Ù C¥ùgô šQÆ ž×±Œì“‘hòAÙtqµS›ø6}LÜ«§!sª—·¥C^øhËÊ9òÒ$F¸!É}ÛLhxxð;%‡™œà•h[ØÍó^šÎÂo Þ+|f¹¾ŽÖžOÂîš9oö{vSó ½o“°Y§›U'a÷+¾»“°ß$êífµ©d39Ù ˜û2ž8â‹áýä›AnËçN3QCnçȆöôƒ‘.Ë8\ßÓÔi̧õDþ ˆŠ¿;Ùç €dçðTÏN'CLø Ê‚Lý@ið7“|äû'kjøŠí9Ŭmå®}Œ³1¸¿øî!µÌÓ†{:›²•|íp6t/—\ÉC~âi2Í]ÌdÑìÑDVœ!-ó ¾q¦±ì‘ O@ÌÓ¶Š?ø×)’|¶RÜ-ERßK¾êé=ßÞCÍß›“x—³øe½Õ½·\ÚO¢Ïî}£­gñ~HQšÅƒÕƒ©Ð~ü/l|ä€>È"¸—µÍóÓõ¯t§Ÿ»+!Š©Îþo"Ä*ó¡ÙmÈž¡{šÕ'#üÐuå45t¶,pÑÎÜx5ké›…Ù‡PP,ÁêüwÎåt¨íb·ËóˆPRÁHúÒþÇE/Lþó…#endstream endobj 262 0 obj 3864 endobj 267 0 obj <> stream xœí]G‘s˜ "|l'Aâ! íez¾zú-qlCø 1 À‰cŸ‰mxB Q„"d¾œ' ‚?yá‘ü(ú£ª·«§¦gvwvï6¶,%W×=ÝÕÕUÕõÕ}7²|_d¹ùÿ¿|8{übѨì¥[³³²É³¢’Ù¡ýIª"Ï®Í꺥?›×fWgÏg×gv¬ìâ—à‡›/éQ„ûþwù0;{0{üÉLdWf8ïÜüPJµßfeµ_g‡³ÇÞ÷…ƒ—gM›é™^˜=vŸ‹l^Xðýl¥]óý¶¹Êt¶–™ëû©&­÷ X+èúAÛUøNÚrhýPºtÝŽKë lW™•ü°‹6›—½èÖíÛ Övé¦m¬¡Ñ.UQt­ˆÔG $K€,çu.T&ÝoÊÌõþ¨Kü!QYðc°B"mèè¨üqۉ㠛ù XC/¤Š¬tã|ÒŽ[gó*_l|kx:;èSªt_‡?mg•Y#‚¡J?”[€OáâS/ð8MxáŒý¸†My ä)·úJo¨Oa¢3d¤GáÛpEeÑ’‘< ŒCq.ÈÈAóg@†Üã¾ÖÛWrS9¼rd•ÏZPo· ¨çiI‰³’A>-ü§ˆe¸)e¸~݈î6AãT$øU"‡Zæh£=0{Vÿ»1«T­õX^kõf~¬ËB+­Óª*l'¯áöÀþëü µVˆ™,k£ÂªV‹J“Ý|qv勚_[ßÐìݯ½– ¾ šÝo›<m‡Z-‹4ôáÐmv¿-;+Æ¡*ržÐÐ蓃áÛì~[ð´Yâ1lØ6úsÄܼgжF ›²p* @†ÅõjæÀlÈ{ÀŠBçyžWTmå}3Ù3©B‰p"šF±mN7Vžáiˆ¼ÓOùIöˆ:»âÏZFm2ØEº¬"ØQÍÖHö¯Q*vLÚóÌ4ÿXs)* ¤2*Ï"°/.’£Gw³ç570ƒTÿ^ŸB%•⣰'€¿†Co¹É Äü@Uú9Ù!=XÅ®úÇ?Ú¾f$7óŸˆþ˜*ÄUõAkâ“Ì5×~» ›ìæø<®HsÞ*®\+eø… ÅoP|PníçÈ™üe€\×W<­qvHq˪…¢Ò 5¿zt, yä BÙ³”°o tµ¬âò* ý|œ“i ñeÛè)p ´€ëû¶å–¬Æ3ì:!Çu`ê|ÑS˦[ÚOAç| ¥p¸f›K”óÛÀÐùb­ ?EZŸök-„R-ŠÊ hvý¼ŽŒÈ,¤up3Íö]([$ Õ¶ØLjZ‹b#æø9«¤g1ܪ#ˆrE ohL~ ê„kEó½Ç ?CîcédÞOÜ#ÙoÿéyÁSráþÆwƽ:¢¹/ûXxœY–’tI=Î̈dH›I#éqóª.v›Õ9»ak»M]qêá#Ö†Õ¶!(·£>@¸×e$Iݢ9/¸ÀQn3+qóª)ôp¥ì—Ø Æ{§öÁD^êÿ6…—äEKÚE&å²…K´à]¢e‘<ú«mÎ=ŽëƘ˜lÌ45§:_!×~ ÞÎFûx­¦Ý4>žª÷Ë€¤Û=”"×ÎP°nûOÖÍ)vm[˜ŠyŒPóQˆUbžzJ@n){)@$Ú”ž N‚ëÛ2Ig\¬Xïô>DyF"´¥ÅFVôØŽ÷S²ø3šDÛ§z»œÛJ-†ÙƒÃ$5Ѐ­£Heé—ˆQ ¸÷Pç÷W8iFÏp[‘0 ÔrâYN±êç"ÝÌÑ›Jž2Õþ‘ˆ{}@†êNþá ‚ *ðb<{R$ך¢ïÚŽM¤”èL+ççá`ÃAÖQ‘Îuœ0™á^ W²1 ÷Á2ŽÅ*| 0UJŒb›:äz¸bÔ!Wy‹²¥ÑÕ¸Óé“Z ml×ôIÝVíÔé“ÅŠïîôÉëÑ<=Á%ÿÒ‚9‚ßp ë]¿Më¥Kª}cD¶P_{ÜÓ%Èáožºã±vìð+„ÃŒC룿¶C5ˆö$GZ¤ÂënqÝÛIÔÚ¦m!7&9"ŠzkQë:÷Y§ð9LýVDÙW(eÏ¡X*±°„›@Ù|1™ðËÞjŠÃg]&X_ë×7u¦Bêcí^¦b¹ZÁ­zWóœñkŒ‘UƒzüßZ$§Ž©ßËÜËÄ2nà®g êF›ÞU#Á¿»’öÎ@cïNL—+pþ‘§èNæ Ž•¦Ù€-sÝÀZ|J²qÉ.vàB£wƫҥ®Žàˆžf¼ÅÞy°¢ÈC®«S}†”VþEÞ«Föˆÿ’<5¯¡‘Ž¥³H†²:ñÞœs˜EµÖ½Q0Z`=î´qµ`Ùˆ¤Ù×ú8\ö9ÒÜ®ÆdS'õÔ§Ð\&b·ƒ²U•CÌâ8e«²l¦Ê.V|Ì‚²ÒÙ3Û ÊþÇžwAMû7½˜Yð5lW|÷]/ñÍ}çß `<N à× ?½îYb­°2·#JÙìH½<~ýOü¦þà ®dT-aSìm#¡Uxs±åÊÇÆþ‚ à›¸ÈpfC¡: B»ÍöÞ¥:|sã`±·ñº¼~ú %ðU¢3¶YÖŒÛde]/¼¯Ëñ}hãz߯¥B%öà[¨ÚÆÐ*|ûjĸç=ãEÞfÓd0õªñ]…lW¥Ÿ¤Ü§}øϽJ¶,]–ÕÖ‚®M®9EÌò`¦èn6OS…XÒ¨zÙQTõ*|;À8n¢ÑX6¨î}‹aºxüAö­W!Íì~4Á@¢qŸj­¸-®Pú ^'O]‡ºsËÔÍíœPŠkÔUAŸIW­„þ{|çtkÑ êµpæÙòѹ–tÞ…7ž³lai¡ÀjáB+* 5F‰Þ/†”F¯&8ÿׯ?]¼¶Z`2ã‚ü÷9 ÅwD’Äâk8锨§âþjͪîM+¼ÁÛKf—¨œPNØV&#'§mÐ`ãuç:tÒ¼1‹‡æZ¡zòè¼-R¤Èû.»r»ÕPˆ½¬”QÔy5‡ëóJhTPƒIšÈ;·Ü+‘CB»^Z¢é3M¹,Až¬(0‚Ó!÷(9½ŒL ½ŠÔM@Oþ¬¯.‹N>>å}v®Û­ZصÙçÑŸ-ì·¢GÕ4,=p§¦!©ƒOÅ¥Ãò_éj2Ó²{~ï0-8*O¤˜í¦:ø§ ­-U[Mè_Wü(BÜÞ© ’¸Üo¥*QvC&#}Z˜~ÊúuçyàQh¬ïÇ[»õÛ}W5”¶ä¥;¤(7ìR©jZš#—+ëLÛ £oØ®í¸FÏy.çáSÂ"ÄåÀãÒ¹x8ÎÅ•¬ÞUƒ½*À=°Zù=AoP˜×%V–VêÁѲ^Ï-ÄO×0 ZoüÏÓ r5=4éàtµ›Çmv@@£IÚ[ß~h›,îŒl̽˜šù+Cqè@ƒQ#Ÿ32®ð~`Õ°óruÁZD0/ùc Êÿõ¾„²ãºÆ²äIbwxg”v·ZÀ½þ`Y™ÜŒWuÞfj#BëÏÚ;“ÑYêõcœN‹Þq>Š’°1QtÎL?þ]@—ìRF\Uï¤Çóu‚qQ‚¤çœê}d•À_‚¶µ„‚ôŒ {ê›N™áÙ4ç¼*4ÕUÿã#Ç4ѹ¹«…zØ8þØîžs£åá"¯>ÅÃÔÒõ”îj—x70ºê‘xÇ)ºòSEʪ”5üÂøo%-a™±9²1©ÅkSJ/&£]?¢yÎÔÅS²LæÍ¯Ð÷ZÕÕ?ÆÇò:W¤–É:š?ÑÒ†»O«¿7ŠWÒ®~ú]¼ØŽ‰Ù¯ó^Y„WÌ}"‘¯º3[¨!Yûu¬Tí¯ýƇ|¹žC5„Gt‹ykÞ´'YEÚoš <ª'$šT0þÞ³³ÿÈðŽkendstream endobj 268 0 obj 3731 endobj 277 0 obj <> stream xœí]ë¯e5—ÇËÕˆŠ 30¸Iø€&ç²wwwÛýM£‚uEâhБðñÁG ĉÔ˜øHŒ|ô01$¨Qÿ&ûXíîj×îÙ=÷Ì{É$p»ûúuõ×ÕÕvµçrÓîuMkþÁÿÏ_Ü}ð “cóìË»—w{Ñ6ŒËæ¢ýKެm^Ø…ÿ6)^Ø}n÷‰æÒ®-«9óøã¥gu)ûþwþbóðÙÝjºæì…]_ïÊüÑËqO5=ßš³wxÏ'Î>¿+T£k:ûôî7Ù kVÌ™ ’:è¢o¶Ñ¼q¡[ll߸´ï5¡Q4²Áã&8Œô}6i Ú±µ{k!tî’-EBï·IeÓ»àL©fÕÏÂTEbÅ¡mç^²‘DÚ¦vLzˆ.Öƒú  ÉBVÀ«¡íÆFº/·dV:‡ È„{ŸÛ‡:nƒ†v1h#G'åØÔ^8.ÔùμÚ0YÓ»r>jËšo§Ž|î€Ä.t§ qVã0áÙZe#º¨¨>åZÐAæ»|ãK™''NÚÌtÊÝòœr­çºCmðÈ D%‚¼q‹4äN¡’ xˆƒ13Tr}/Œ!§rëî멪®ÖSåã6¨»[FÒ ²ÄÂ9#ee!«GwJ·_§¡»NИX¯Ò3Ô’C%=zúìî—õ¿Ë»|´k­ÞÌŸCÏ´âÑ:ó$` ·×ã¨nþiýƘîcÁÛV§ï†¶uÿyé™Ý Ÿœ”`–H@Š^j¨!PÖ=3. ͳ?’òP6!ÕÈÓŠÓÃI ›ƒ)6}4ø.lL}Ê'pß•Ik€kÁ’0TKÖ2Áˆ2"ö{5 Å£ïZ²– F”Á°ßkaôé-F?’µqÆ8û^ ƒO`JM0Ä:œ&ûîa¨¥bÕ·)xGQ÷½@Ñ<û>(¶E –¢%QÆœ¢Õ0,£rŽ¢%QÆœ¢µ0£–¢qÆœ¢Õ0øÄœ¢%œ&ûîaÔQ´íRðŽ¢î{¢y÷]Ø€¢ KÑŒ(cNÑj–Q9 GÑŒ(cNÑZŽQ KÑŒ8cNÑj|â@NÑ N“Ç}÷0ª(*d—‚·…ïó%¸ï£5)*)JÁ0-ˆ2f­‡aEÀ°-ˆ2f­†aEÁ0-Áˆ3f­‡Á'd-Âà4yÜw£Ž¢|†¢<Ô¢hŠæ €º™µ„¢ KÑŒ(#‚á¨[ Ã2*‡á(Z‚eD0u+a8F0,E 0âŒq n- >q §h §ÉÔÍŒÛ2ŒA/GEkU/W½¹†^…] OQDù¢hûµ³6*\K2&ÄÚµdœ­%!¢v-Ie³½6ïɵdÏi¨Ã÷ù)†Hà¾K¾¡NÁ0ã·#ʘM1õ0Ìp#`Øñ[„e̦˜jv¸Q0Ìø-Áˆ3fSL= >q ¿Eœ&ûîaTM1}'IC¾(š'êò u †¥h F”1§h5 ˨†£h F”1§h- Ç(†¥hFœ1§h5 >q §h §ÉÔåêLIÒ ‚ïó%u‡ u †¡hF”1§h5 Ã(†¥hF”1§h- Ë( †¡h Fœ1§h5 >q £h§ÉÔ60ÔÙ RðŽ¢î{¢y ®ØÀP§`XŠ–`D3ŠÖðLËa8Š–`D3ŠVÃpL#`XŠ`Ä3ŠÖÃàrŠ–`pš<@]Qg¨;F±вP ½–$u3;j E –¢%QÆl-YÃ2*‡á(Z‚eÌÖ’Õ0£–¢qÆl-YƒOÈ)Z‚Áiòu3ãvýZ²E¶ú³kIˆæD“XKæÑAðZ²Ï‹ ‡ ½êÛ3§g|OZ ëÁ°Ò‹ÑNÄÞÒû\ÜêÎÏunî>¼c3ô çQz|4¬§3Ž œÈÞ‚·]}b¾ÂWaŸ  ¡|7;r*€pï(!‘0s¨¬"ÿ‹•H²d­îƒ”Þ±bŒë0B}i…i#µ¾ÈÄ,cbÁ­Y?í §’I“µ”ø$¥5\ÚI¹ô.Å¥%Œë[ד%.½Kr‰êWšKkìx)uAJ¶U:ºÜµ†¿ ¥¿—¹4×HïÂãêi…K†¬ŒÌsig†K]AÒdÌ%ëKÕ¯éÙ¸J癕±o}3=J[ãJs£V3y:‚œ ëTv # ô¾—€šlŒ<ºvçÒü\IçÿÒ€wP7N¾5Rhã|´÷$;|gì´²7öý0òàêçÃuã¸ÇÁøÞhççbäýÀRç%çÛäG[âét e=å=Œ@²Þ/h5L 5†Ë{·÷"Š< §–š&Z¾¢³ö’97jùL²RîM}%;®"ÁQTÞ'ûËYoG>ÆjD\SßyBœ9Å_yf­º~4;2N›OÚÔZ)çAö9– ?ïG÷×Ö[‹ Ýòƒá‹àÉ…²ú±ö/2®¯èK65÷úsý"ÁõO —/Ü%nCâß@‚iY¾j~ÑÒ%ö:×óÓ¤žŸ…F•A—õx*PXËÇ?(ßyRvÒžÂ;ð~ÅrM³‹;:µµ¶a@ÕÆ‹ÆÅ~ zÜ-Ÿ°‘ƒ¦¢#ß×mX6|±úP’¯é)`ù üϳx£Õižûo>7Tý-À z÷,Ì Pöß*HýGf÷“¨Ç¯@½+˜- Š ‚W|#ãš„†H@cÞWÐüðm;8'ßGŸ„û ß($‚q𮓯@ÕЪ'¡‘^‚ß„±Á_YZËêÊV|êÛ´]Aÿ~ ø9¤Ì~oÓöAD€ÄÜ ˜ôRø%ê­Wmj?Ãýîµj*¯WPß¼ i L¡ d ƒŠþN2m8_è¡©«ç\ÿi†ækÀKWÇý0|cN;Cƒù¦ÿ „ìR¿æ;S8~FfçÆÖ# y_L†æé04å”ÛÐRNу¯êŒµ(Ô{ÁœöÕÚÐ÷m1£X¿£j|Ï>ꇌðOùuQƒ„‡tÑ!æ¾EÖ ë'uñ´ {aþ¬×%6ô½@Gçbý0°ÔÒËve\y‡µQMÂ×ô<(ù ›%÷*îeÃä:T%âÙTêÒ î‘ Gð˜AP/BÞ5Äq]NÊú3šïÖË%à¿#5îú1–™í$}A‘•µìY¦/›=,5pRäA‡Ýó0Ƴà¨?, ‹\„yŽàãˆ_Ƙ’k¨ïà C)ɬG¨~ƒ†ŽâúešâÆJpct“¥¯Û߯q%Ýä³2W÷½0 ‰&~A“díð5´Li=|ÅÝëË‹\Œ1¬°ï„NŠRt‚ÔÀ©€€fnnšé†» ²²‹cõ¾éD¨Âå²lç.Ö‹Ew|¥·r`kr4‡|q3ðÐPXA±L-Ç"°=qò8X8‡E¹xsÆß/.húa®C±ÿ‰çE Ú4k‘á¹¦ë‘ Œ8;½.XˆÄd%ǤËôLÈ\6²ö†•ÂA·aØLcmeÔ ÈкǬó˜0O»âêä0’DÏrÞ6¼6£;µ£ö3oU¥«ÂúZÎØš³ìPù¹lQ‚ÙZÌ~‚Øb ’ßȾ½12œú‘z1ÎoºljSÞ ÁÑ34µ‹À}-œUÚâÍ·ôx… áÔ‘ÈíKæc–yø±jkQ(]¨"9Œøé̹!%6Tî­ð«gî¼Í¸Å}ŸRçc=Ñ¢­Œ°DÈQ›ŽùÔ¸‰„¹âbÁ"6QEºù:DoEÛŠ×ùÊ )$ l¸B±G†—<É´N1îÏMÌíùQ.aÉ`ÙRIwËŽQÙâ-5¶[j~:F¼„é^òv‡Üxü íDG»m‰Ù:?¤Ð˜™´µÛ6‰á$—k÷z—éœ@J¿°Œ5CãæB’O±]ÛyYë4%+¶ÊEi¨W¹]œ±Ç [ßöÒ=XÙÞ¹NúŠ›»ñGßk2¬Yh³ò mÒ_·15bRîáÕò¬gÕÜxh7í›8˜#´R¾P+œ3«ÒQ­ú–ïrP)×9É—öäkôBýñßQT[ï;)°ea–mÔ#a(QCù}Å*YÈ®˜4>ëÒõß<0Ø©w?§^ç°vœyo!Ž6Np.øc[`§M‰ò'cxIcQ}*D'Ï`ÌÕ—>ï êê»Ñ¾bûÜã)lˆj4׸PØþjÌÞḅëê«–(~ÏÃ>)RSßöÛg#0c"Žîl—£;ëÆ Y_µDÑÕ;®¾è ™ ÜB -sCoßhßy麞—ÌÃþ6è~ƒÎ> stream xœí]ë¯%E×U¸qWÀQù&ç2Ó3=3 ……]P‰x‰«Ay »« ¨|#‰D†ð£_ýÔÿÇ~TõtõÔô<ι÷ž}d¸}z¦»ººúWÕÕÕ5²|·Èróþÿø¹Ûm“=ýÂÎ…²Î3Q5Ù9ûW£DžÝ‘²¥›'Îî<³óHv~Ƕ•=tüñüÓº•Âý ÿ{ü\vboçö»²"Û{jû]™?ÊFí¶YYíÊlïÜέï¹mïÙºÍtO{OìÜú^[ÙJØâSÔ„® ú}¶ºÊ\éý¶¶Ìܳ0%UgEk‹GMQ*xô*ûháºÚ6”Cí5‰Ò™ó¶]R)k(}Ð>Úd¥+~ÈE›­ÊAre;ãá¶‚±yÞVJ¨´C-Dƒ$ºZ$êæԔP² ^ɼPYã~ùpf¥ß0åšr‰oc©¨lñZamøè¸ü1û42Ç• œÌë` ƒ%%²ÒµóqÛ®ÌVUÞM¼Ä1\»Ò'L©ÒÏj:LùÛk“ÕEÐTé›r#(àåOâàS/wt#²pܾ,aR>%”)7úJO¨-Þ¯BGÇIK7Á»áˆ4ÉEKZ‚â1J³ -Õ7ÂâQîm=}%ו£+GQù´-êénîy^Ræ…•A^þU¤2œ”2¿~˜î&AÓ$òÚ „Záh£=¹·óUýïÂN¥¤Æ±\jx3ÊRhàјVUQÁ>än×,#½?4Úi@Ìš¶mõËU«—J=ÿäÎSŸ×òÚúŠÚPï~ö(¼T»_U·6NC]5‚¥*†hèWï½ÓPæ’§*†hèWï h¹âi€Š!úÕðkŸÔx3¬wˬ©ëÝÆ*ºûA=­Z·Ò°å&ƒâ—ý±úâóë Z럆æò 1\\Ú"íCv}HÚ"Ïý®áÛý‡ýA/-ÐÀ?w`c¯[ÝÒ>ˆc]¥]yò«X`!´Õ®ÖUZ_³à¨ñK/RÓË@®÷‡m}¹Ú¯[ÏA]¡ñÔ¿aËMÅGxÜàþe+•ïé×Ðòª¨»¶5%Pü>8uæ 4åÇ ohíQÄR(¿e(þ)%ÃôÕÏ -² :Ól€ò3Ä.ùŽ…È2Reßô )×ðå—Ò”Ûë~‘/ßõó¾–e(¥6 Ée`R«E+›UãÆ,T§B´ˆœC¡e5 7Zn@=]hŽ2}­7H N«û¦A9ÝÔ¢n:ÒWóŒÖ²Z#Ôl–¡ñ‹ƒ8ZP öáãD3{)ì©ödQöAM`O¾ $)|5&qs’9ÅãPšĦóÓÇ<© ¶Ë£ŒUUË@†s‰ Ñ‘pæe5ÖZRâ ·=45€¥³kÏfË¡·8Yhû6Ï$Q¡Ô34yê¹Ù”‘ …›ž CM¸Ft­¤ G)`LÈ6nˆîĉ[Fø·Ž#+˜›µpUÛj †ZÍ0!/Ú'j?!ŒpùέÁZˆ‚TÆ¥§Ú"ëæP\æá“‘o´†t–‹çC"ÌíD$Ü(ù™ÓªJND$3G@m£¥!Q Æ¸ž«…€MMåpD¯!ÓHx#y¤l«°%&$œÈ<1­]“*¢X ˶lùù`ÑÉlB}_âÓnjëhäIôš.¥mR5ÖyAýÃ79øÑÛµL%°n;bÁ lƉ~ ¥B°Ž)2¥ŠaXi'càȺ8\Si·QP·•/ ï<:ë,ë<[(gCË™ÞIËQK(Ÿ—Ï]-5ì.1°l8™=nÌî.Ÿ"¶¼’_ÃòQ,ڎȪ}h×Únµ\fk¯%«uEüßs%‰#xÒò¢óµ>nËÞwä]±Pþ»­ö'%›ØâæCnßõ±¸TØÕº{|© ’? HàtoÙ"WÒ–DÒu¯:::7Ê®*K©*ïMÁòÌã¶Víª¬V…þÛxU&‰JíO"þ`ËÆ~Î ÐÆp̈€îµÊêÖ9R5öü¨KüCU¥R5¸‡fOYâôOÑyáE€"²ØšÂ”ô.ROFQJòé9¦håÅ;ÁEÎ%Î"¢¦jÛ茖eý~úÓ“¨yJ6¹5:Nÿ‚ªdXVZ²]ØX3{È6õa#!–A‘ØGôWÐb¦¤QoD­K–+)¿Ö‚%_³ìeD:ŠDÚz¢6Úz—QËS³ÀùUœ}â"ת¹èàDïñkmÁ˜¶µ¦(`)p“Eä%8²ey̯].0€zÒèñ¼ß¬°ófݳ w©ñÆ…¹ô (äk ªÚ*}WîqIÓ°nocßp¸ òhÉ pÛÜ%±¨3Àö#0Á!¯„4§{®±%'Hñ”0Y¶oÚVSjW[MµS³RÓp™0zb}N™,j|—[7v¤Jï j)kcÓIæl|‰MWhÔëF¼- }U”f·X9|ÅüTä-®ÈKö$_#rÔ4¡Ñ4å/#˜Bùg^HÖ;ù—ú?u®àäÚ©uGà$Zô?ù×6”}ëò`i5ñîõ‡aª€‰¿¢D €ÂêßZ!kfw¶ªVyØú™ýÇ€’*÷Â0KyÐ“ß ?Kz:GD碤NG˜rb†+ÞMz±¯ŠÊ‹Ý›vò[ʉÖÈyBÄyXÓy÷¤éÆ–¾º«hwlZ©¡¤ž€Z˜€7°­p¨öè,xܬ¨ªëJ¿ ]YJTIŸ6s/:J”G‚DÐkǨ‘Ós FÒòN*s,N8竃·E^c¤ÚH´D‰Æy·d@zp¢Õ(*ˆ'süå’  ^ð*xºå¤9{dD|ó³hlA`ÇìEÿM&éK–,ã| û¶K_4¥Úo.@¼õp!˳¨]{÷âð&0zí‚ 1÷tÏoËp6‹y—ì; 'Yi€ÆÇ®ÿ&ªÖ^ðô7Án½Ÿó’`i§CK¨¸‡"Ká›"«$‹†@{%BÔµ@:<³ökh@ê¸B7&\ç˶õ±O|fIÞ”ó´h¢µ½aÔãù©ˆ»”ñq ŸÞ¶=GÌæÜô}¾m kì1¦^Ø‘+8|#†8ޏ¡?±o&5ðÒ{#TŸæÈA W ÚLEý®½HèÁA@èÏV~óCNÃꨟž»n@€9ÿ{ìúëEšr³Ë/9 Ne7ÉÃCs¨»JÆôyAãî£ÅÁdÉc³Þeµ¸ðg$iüæxð©°bsJf\zû„uQ8U?Øgjȷꘚ RÀ;o¡£´7:âRïnêðs^ÐJ£øÉåãhd¼Hn&«‚ó…·U `; ã‚wËa°Æ ÎÈ×ÐóŽmgI§LÙQQ·ôn…†.mJÕƒòš*5mF(ÜNŒ˜Ôò¾*µÞdGš)ÆÖžY6ÃÜ(’Å1…%ª²¡þ%çöôø­wsZR¬~‡px«°Aþ«)¹¹2â~pwî5»ÂõAóú—`k!èÆTp¸rÆösŒõ†ü·Š7Pù[ŠÔߨ+]ø4H£Ge2”N%ÐgU5ÆèJE­v¶Q (¶F»ô!’˜ùt_CÏü;á0"ή|.ýðÁU¾ Î ª=,Û ÅŒì§Rêà OÚà§BJ;Š_ãÑúd9Ù"à7Œ ü´~ºÿcŸm}, &’€âÛ¨0 ¶–˜dBhd;bl±±Í›¤õB7Çö®¡Gû^ ~åä}~o—Þv_,Æ¿º•Œ“äÄ"­µzk}d.ŰN<]Q¿½½äôž6aŽ4-«Y˜ÛÞQö—ô¶r” Ã7Ûõx`f^e×A±bƒ¯ì9j,†¢ë9¹¾¿ð¢„?þ®tJÙëC5Íc•&ÁX¹w½M·Òù{êÁ“ôNt*¯Á\¨Xö[‘ÄÝÞMû‘h×ÛNºÕÆ[IóÏDx3¢Ø[²Cãa[g¾6Ù·©Qk]î¹R31¶Õ=ܤ\¬IÑõÛµ¯Pp\î(ðf'•«JjÍXD+Ä=ÿ¦§‚²bPçŽ40›ÓøR\µt­)b,ç¹II9ØÉ¦°‹ºÜm²ª®àŽÙåt¯ÒEïÖZªºÌwe¦ò Åx‹ÚÞÞó\p&æ­'=†‡®|50 v•oƒÀ auÙy_j’˜s%sín{œöͱ¡plˆ‡ ¤s¬|Ôë*hLù`ÔÿÚb룫%p ŠŸEBùs¶ìƒ¯}x'”Ï܆}»‘n"Ò®BRo•ãøô (.WŠc ï&µ<K MÁ†§‡ÂÉœÆÕ!QéàWéCÎÞ©p¿ •yW磌Þ$ÍÞA ÷-İŽÄPùüÓJV¡ô®~E°.SÁ%Ñ\¥K_”’â2£"¬¯`ÔÁšŽCKEIÈ¢HœnÉÙõ|nÒãùôùÛé¬ã¯î9t†·«£i$†wªû.€;]ªq[‡*Ìõ)Ʀ½ vBùØá'%†ãÓhöEAÎJMo3쵘ëö‰æ¾jœqPöhéÎ~Ú)ûÚî”DÆÉu£Íq /H£:êöûÙm n^Uµ¹ê%6J9W÷ͯXÒS—(žrœ=n!Nuî‹k`mnÕ'ò¦ ¤™*t@ë?}jLŠC1Â8¸Ô±HëïÌòG‰ôd-Ц_–0pÈJ2*íc@r|µ‰Ìwûv\ÝÐ0e,$áhúÐÐ;:ØÈM”!í±Áyš–ÛdQJ™ÍÝ ™–soÁ}š2ft;‰dÀŠfÁaR ëÅŒ¤øˆêàÜÎ# ¥a’·,6ˆ¿s…1b0wéO™û£í~Áâ~ž§TY[ѶÕÔp½Û­ó(™üAÝQŒò¶ML€D'’—êA¼O¯:*¥¢AÜåÙezáƒAhŒ%¿¦‰Œ¶O9)ÜŠØÐÏZÀÃËM•ŠVîʬ”úõËó¨T3 ÏÊJÕ›<*-ÛÂ|QJà*·òeüñºèÌ>‘Tpî·gFbïÒ*{³bÒKe&0iÌP5pŸš_𤡌Þ`¦¶Ø‹á3Ú)íÓUË9YË[Oáåp]r%rMt!žå-ÎPXäb·ÊJQÂGí¶-E¡¦Oƒrá>‹¸±…²†¼=9 +3VX “rÊ(7 O¶ò€+úT¯ƒ$©œ¼w]mÛ'¥3j–%œ”*gõØÉqeëC¸òÍ¿‘‰³ßüU ‹ëbJ풳˜„mS‹+AÌò–ZðÓvÀ|A¼¨+ð¢­õñùYÅ â†ÕFɺâˤ­—±- sVë¾´÷˜" ñÏð0a(ôkIÔoPHH²ù&”ôÜHpÏ"Ìôw¤Õ~”,<ÞmhÏgl¨ªš‘÷»Q–÷»,šy¿ýïi%Beã‡)]IHoºe?bKWá9´ÙDà>7/ŸCÕÞ¡e §¯zzxjeÆ'¦íÒD¼'q-†)~•õó04.‚–½¼APô-¿j'î’ÄÃÑôD-{üµëMÔ¦Ù(mÿÓ:Q᪥, ¹‹óh8È{‘{AÀ~Ñ”Ñ"’!yr5û¹X–cXà/§ü áLû {6)ºêÖ;“÷<øZw×@záå’Aó«àd½(™%3[Ç· h²ê sEßgç"/¦ñäÕr(ôÛ¨SL›tí§ñ2)àf.R Àã™>> IŒ<8îú¿‚ÒŸXñzk«µ8ØÕ“H©É8mó"zäCñGbÜ“$7ûb­âïͬ Ÿ~¿Â2£-(¿w*mHPȪ|Ô€ ×ãÈW.¢­ ¥÷UˆË§8¶sc‰U×’’–碇‰À%nˆ™ Õ‚N¾•±ûŠ»€†¼ãl/ã#„Wqˆ¡ÄÚ)­:ñ°þ€õ±õãñ`Š]ReS-ÓöX|ÅkÆ'8f™gUVM‡¹6Zks[jÔ&Ô¶X­è‡YÉ*TäFÞ–„"ÞUÙ®ù÷äjcendstream endobj 286 0 obj 4917 endobj 298 0 obj <> stream xœå]Û¯7‡§tA…B/ihÒAêCAìvÆãÛ/ˆ$ ½@[§ QH›^OÛ¤¥}APõ®– ÊE\$OHüˆ—òŒàþ|ù>?¯Ç;»;{²§(R{¼{>wÛ?{.å¬*Jóþqorçéº,‹Ç_˜\žÔmY0.Š=û—P¬,ž™4¤›'ž™<19S<;±}§ï†?®<®{©Üßð¿‹{ʼnÝÉÇ‹ªØ½4Á÷N͵P3YÔ|Ö»{“;>ò¥Ý§&­,ô›vÜñQ[dÅ”Ùâ!S”B]õÇl5/\éã¶¶.ܳŸ0%Õ•´ÅSlYÌWï2îÍG¼%]ÓÎêBˆv&l 'í-ÍF$®2ž¥3Ùü*DÅȳš÷%B„Fyž,%zá’ô©=˜œ³@‰h‰×"§ÕÚ§¶ýj§1m βä¤/ë8q=x¾èá4ÕG!Ä4—Q•GÀë@ñEÀ±ʱªxè)Çõàq5Ét¼ä¨–í-» †à¦E¯û+óhDÆŸnêÇÛ qó*J©8Läâµ)ÍŒCó×Ƈo…Á²À0´5ª¾ð¤6½ÄŸJ?äóV3Bã„â"éàTcW·…IAö­V|xʹ`Ý$Œɤ£ œ´ÕIÛ¡ü±Â’˜Š6ؒ‡År΂ӵåœi¤éÜ«  3 hœÊ»ÉMŒ®‰Ðí2ä‰ÓÛ6iYhÁ,0ÝQEÅ´Y¬µ‡€FדKš[ ˜B3»z*û½a•¡wx#cݾC¯"޾½g“Ÿ\„oj£ AGG2*bëË)®TIÙ¥ø8ïŽBÙ-ç/©ƒüKäs@Ãê”×Z£[6L-{”i¡úp‘$ýè%ô]˜ûcæ’±–Š´®QˆC´V3ºþ³•)à1Û¶›]´åºÎ5Ý#šBù"¸M2O[ÑÎôL³g’¶¾©Ô GDª¦¹žçý'«û¢¦¶ Uâ„ìýàiŒ i&  à8 ¦Ì„¸¼ÖDÙž=|U 4Øb…Òß9÷/zuÑΊÝJ ªëFq¿ˆ‡ååÖòx©c'ogÊÎp–×oí&P¿¯ƒà:í½sªæ—PÌÄvQòºÞzxtÎås–¼ÔaYðÆÌ¤¥™ÂÇSÅUG›²œé^›VGĹd™ò¶ÔÔ³Ô4sì$^$Õ”zñ”EªUòù!37 a2'T6YŠTµ]¤‹IÏ0B²+é Û8sá„ßτ޹\ gª4H 7¤wÉ}©qÓ+ÒxD¯®rJRúiÉ¿­¾Uë±QyML"òbÊL€ðg>Tþt‹¨-eCCByäRˆ;Òþʬ?—åSLªLK÷.N þ|–d›rs¼ yJE2›¹ì¥"l¤ó¥Ugì/a-}Žss¸£Ü ²¢lîHFK/ ¬kc«¦v¬7/BÇ( ÔLŸØu¥ûYyNr@s¶ÖëóËöY³´Å:ï;åL«@ pÀ^!RzzÖœ *ÞˆŒ'TIÇ[ekå}ÅhUN?ªÌž‰ýeµu9šeDÕdCdìyR3V´ÊMƒÖeª%Né`«ìÓÑq”ÉEÅš™ž§(œ¯¥vdÑ"²Àí"Ì/Æ ·Å6}e ÂÛÖC•Î}Ö†Zït¾aË«¿‰fVW%ÃúW yIJøôijËï‘á=PUvoòŒûð=ØÕ_íǾ~ì ¡y¬;„‰í„,Ô™oªhÝ?ì[üÒ­|€’ð ×® Õ¨ZA$×ú£S6ý§Ñ¢ï çNü»V Ý®ËC¶¾…´üaÀ-~MÜÓŒSïÚ²( x´T›<Åß$}½‰}YìÃUÅ'Èî„L< bÓWiÓŸG=ï¦z6ÑØ¥ÙçÎÙxå¸÷ >:ß÷=(Cñ!ôIRÂ1¹Ò#øbˆkîÅÚNáÅßǸ)ÔCÀNSº¹jp¡ôU6ã<Ø!ß&yÅZ:òŠ¿ZÓú•‡£D,9lϵ'<éÎÙ½o‡âÄ3R­zô  ´ŒŽZ"%éqéÈ”üÆÖëî€/¿…qBñmçJ[íUêå<åDñÊN­ï=ùr?îРºj€ÒúÒ–¸ôõ˜{Ãí/O™2¥³EW˜$<‚ÝEwÚ˜-=íãR¯ö]-òýËØºÔÓžé»ñü5jÁ{нk{·3J¯@¿‡H¥{™¾+ÍHÈïZ7è“hGÐ6ù¦N=Nx³ËÑÜLÞ $“u¯6Õ•«þ:¨:O‚;pt^&TŸòî1C–@²hO–HÅÃñ7Ýâ[[VÚêÆiÜ# ô)í?œd¤îUçöJޤ?%Šxϳ'h«QЦç}¬rìzÎiˆo~/²/0‰ªù|äxñá@L~žaA²¼ìS®{R<Ñ4‚º\ïRv§*|‘³ì.Þ¢sÆ[¢æ­bé:OÆ_¶‰?&Æ7%5Æ«ýqT˜¶ÓUÝ´uÜEórîGR¿ÚBµ Kµ"†‰¬yÎ[xÆÖBàÏ#P„¨â¡« r g|„FŸÉÝ &Nzn|D}_>ö5fPY8Ül•ôÞ%õpCȶ ”¥ñur Î'ö&Ð6ë›umU˪¨›ŽÂ%E7l=}…i1ÖF|&òU"*KV]_sòQï^?¸Pͽ¥}’¼XUC_Ü1êäüð¤,VŽòŠ6Õ‘;m§Eé”À'§ÀÔB%Ëf]´¸L]õ~éæïΰ„&‹;ýŠmÍqú¾¶M+½íyIÆ$áUœ>)\ž|Ú‹¿ß·h‡úOoѱ ‹ïAp Eí0ýÄ€¡ºzÿü{hQ„+ajsšFËÑ!³†íÜû•§!¸¿¦÷ ƒ!ƒ«¨ónûå¼›!>¥Á…ö³D¡>@J¡6³DPDìàÓ¿ƒ2-šÂ_µ³ðáG9¢—Hu¢A̾!X«ß~W*ÑénöhÓ?_ˆ˜ïŸÝÑe`¯c[¼ †Žq”sPtkžá©„?“ñMGÑ_wCèfq_é£!áäB»MmÜj•ïܲ۶žbh—ZžÚæôZ§—ÓGô1“vϲiz7›ø% “¼Ëïå_Èúò@q(ãÈÜÓ¯û°a‹O¦(Ùठõ|f|é! T£ (Nå«Fkiˆ±°€¡Ñ¡‹ü¨7Vgª%—!`õ¦Tû6µ“ÒÿrW¢¹×˧€n ËŽ>ôyô*€ÙYŒç jÀ‡ˆZ#)F´VþvX Á3Ùé:7þN:¸t/¶TbV”Æäµy="óº‹$V®¯ç$N¿¥äƒ!ô(Î,ȰlC`éÌuº GP•-•ÿþ¡²˜²W °‘DS{€ýí8n(P\Ü?“°n~(‘Yü¯ Ô³g1hS¯Ñ7%Áï#™mö5VÙr[_1À/mörÂô´dì¸xv‘T.qÝä¢ÀÖ†_ÅSÒhh€–»Ã„¼›øZ£ƒ±àB%ü¡ºi{—6§\è¦UÒÙ{¯ü°­ó!YmOc^# ÇðÉ`Ý;B¿ïsÜk´ÇAÕŰQ‘Ãcd.SÏÝ|´±È¸ú¾Æ²àêqx-ÉÞ_Þ¢µlí¬ÕH¡-k nζøi•+ª¯Æ=ã=—3Ža‚ËaŸèàcì_þži“&%oÞꃾ•›P–î¦Úw‘ІbrûWíyäcÎA¶ï¯íJCè6nêcïèÔÌ€XX]Ïj»&6è21£Þ®ü«ˆÏi§É É7â,´bAOq‚1rlÙ^3³”Y3³¿d¯zc‰[ˆ¿$»KÄ~ý/× ?,w—úò’£\DÕíû-÷ÙM° 4²öÄŸf*=³—½Ã´\t8ü¹ëå-ëì§sXi67…b#!­6wÌÜž5¹ë÷I´Xâ²Ì-MGZ]HxU58I"ÙLôu°Œ|¨Í×™¹1p4ÜC-”7¾Ñà ÜuǾ¸¿(¿£P+í™køTâ@ŒV?nÊæ^$×ú'®èãÒlër‡Åû‰^‰v¯FÓCÔ²+öZ\ übæ&ÀÁè„þ }Ç·ÁvÙþ]8Þ£U˜uÆWðÇÎ{Æ@ ®Þæ?½<Kñ…ç!qµîV7¿…øP‘{X’ûyä†-ý †‚8‹^º}h ›æ. ܲÙ(eãœØZל˜íg7HÜ~¦[`mÔÔ•ÛTŒ+reK|‹·ÒÔÎf| MÛ"Íß\D·ý’ÚJw¶d¯_“~«hãðaö}a7uô¡‘œ fÁuw#^%U^Êñ>y¼Û Ž/uc’ê®Ò[|-Ž¿p+qy“ñJ¤£†^Æ#³MË„L³(A4xE[­´ccÝüUO6+s‚w_iêö¦r‡á׸rT¦?&ße‹¿Ú³òr© s˜&{T?BŒ–Q)wŸøÜÒÛ / ¸:#úêÐR_%È} n88vè%KqAôÌåRŸZîœrÏfQê¸h´Å·x·tÈD6%屿"úCK@M›ž3ùëDknFÚh¶<)È{ ^‰“‡õU4Íaf"çúý ¬oá Š|Þ$mÕbšÞq„áYüÙɇùå£ ™i¯ÉPg1Ž&÷ßú³®¨eî^†Ì½4ëKKßä2À¥›µ™e@CEBdAJ=_ÊX|µÌ‚ó…ñç=t-IÔõì¹R3^þÔ¡¹ŸrMÐ2uøoSˆÚ~¯¾iGZèÕêÇ»-çbì`Í¿ÿÜZ^Fendstream endobj 299 0 obj 4757 endobj 307 0 obj <> stream xœíÉ’GàA°#¬±eúÀÁñž»öê0AX–F–0KŒ1 $‡ŒlÉ@²‹Á8¾@œøÌQ\Ø~†Z2«+ëU¿mžF3Ö„zÙU]•[åR™=7›všÖÿƒÿ/o?'Ú®¹úÚèæHè¶áÒ4Ûá—éxÛ\)eéo?ãúèÕÑ ÍQX«9÷4ü¸uÕ­ÂâoøïòvóÔÖèñã k¶®Œpß±ÿ!L7±Õlm{Ïç·®´mÜN[/{ €¼ó>èAk‡ß†e¡÷…QÑĹï÷P§fxȃªƒ©‡ÃT–ú@X¨…Ñ΀.Üë’A¥úP˜jÁ{Ûf,ÑUv‰ÉVmn…AƒTÆ ¢G©xÈ€ƒÇªe]câ“gÆî ÌÃßFˆÉ~(d9Òž‘ËŸ³‘9b(ÌO ƒPÇ×ùTXW5cÙö‚WHçar„ŽxHº¹&ìjͲ¥DZ*RÀà凸Y/÷x%º°^V ”‡BŠÔK'Ð>¯ÂFd¥cðnN‘C™Y²€GAq(Μ¬œ ? gÀCµ·øDm«ˆW‹ªòÙ:q›Œ{‰—”9‡àdWyz±Ì…"rúÝ\`z‚ÉÏÐWƒ”ÃÝÜuÿnŽd§œk•3oþ§ÜgÓ¤,€0)Y¸‰?0^ÿ¦~8kç bc¬µîeiòëæÖ+£+_púj9h}|œ¬dö^6Ÿv²\m>Ú´ºŠ á0=Ÿ2¾<Êð:``‡éáøT¬ÀiTßÒ9i`‡éaxªËÕæã ¸m«|€!¦‡áé”dçãÀÛ®. Âaz8"›ÁÛµ-bøçàÏ,WëÝ gÀ w“°ñ{²ñv!°MPøêI”=°áITœ¸Ö)x™Ein‡mUBùÄŒcФã "Xì$Áù4@qêw“øb<òD 7¯…è•Yí0γðÖ`Ä#Edn5ØIñi-p*B²©… ʈA‡Å ãÁéøÍ¨b²;Xº`kÆ*ÏjèN$QI¼: ŒÌgbøy%Å=•e0PC><“Mäòë(0ÀˆîCYZ#ÕTqd7Œ_ 2€â¶ƒbñ±q4CM@5.$'9Åkt«a¤Zú{+˛Ǿ"Òsã2S¢ˆ 1MÖ3Ɔeäs$?ŠCÜ)†‰´zÛ6é„PÎ]¡‰Cx9K×uÙhÛ­£Ä—©ÉÌãHÕîæÅ™«žP®ñ݇1GÈNX ´s™»¶TTÙB/„ªbLư nŽçÃ[†xC¼sJ¼ê¢1:éËñÞ]kö·÷VÓ¹½·²Ò®Û{÷ïcïí6ëâ o‚eW ɦ€;¶øîO×èsU¸ÃÛÝ‹‡wp2äµo%MËü¨EÏøKâ‘t”µ¨ÔÛaO¯ mo}Ã1ª˜LLoTv×í F51«©Q[Xd__Æ"wuœ(:«ªö¬´¸æ’êY\e¦m‘ÌTE"óPá ª%ŒYŽÙ凕3ìÂ7ØöGaIј뫉ÁdrY¢Z [D¹,xwPµ;šzÍ9”À[€W‚ÙèK4—£©HÞ½³„ÉÖ¼¾‡U ×v'±iyi ©Ž…ûÜíËN€õÞ‹n_ZÓ®Ûí÷ïc·ß×&b&ÎÑ»=¯$Å _ý Ð?öf!E …™ÝKÛÿSìtFIí¦(áOæ¸Õy¢ñq²\=Yº²áñðFнç/x;Þ>ˆ9‹í¿^ …¸¼tBNUåèÔæß^[ÑÅE`’Ù‰ uÄ#ÇaKÀð5žAõòBÂ×dáX“1Ic®£°I´ë Œa)å/ñôÔŸ'ÀdÄÙÿÖDèÑ­/.Eè‹p&Q-¯Ã›°Í‰0™”‰’; %—‹EÒÛ„àqàÓŒ…l1¹(íhʸþžj›˜Ù¸1òé$¡|3q-rüè:€gà˜e¥-_ÄcÙJ¸ËMzäní(:Z2;F&GÉL‘øM"6CÙ´w¨ÔU¶’|¨µK4ÞYŽç"fµ·< Ò¾›NBÚäÄ|Ò;Cåy ùkÑ|¥vKó÷WÖ|õ8¦·4§óÑIÜ;f¬¬^X¬{û[!éålÊÔé…u­dM¢Í¬-®H©1íÌQä»Èä˪mL†}Ê6Èq«]¦Ò¦NFXD[+‹» zëÀó»í{~»²ã¼ c3d:;ˆæ•@Ýì"%}fœUUi¾2³’ýw&{(Ñ´%÷ø–£,+HåTÂ'wþÑÛ0ŸtTJíÒå(æ¥ZWx•Y³”†ŽÑ£¿Ðm ‘³É¥Ž žh=L\Oµˆd$Òú·pZ“ÜZчüzbkqæ‰úÒÓXÑŽÈØÛT¹~C¹³¶¤wµ•-.éý÷à2](j R×UTÝ¡ßïö1• ~-¢ît£‡#”µšÃúÅq­‰íÀžÜ¿ödõ¨mº¸E<š*¾v™é K[t`lv|/ïSlo»WÉë«EÔ‚Á©-ëbBOCQêËYÕ݉®¸1ÉGÉÂ5=˜*^R_˜ÊKŠå\4SPÊÀaÓ¡é‡rÔîÏveÚÇÕ™b/{²‹~2]w)¥(WYºrÙÐâÑ;ŠÒ¡Vâ¶¢ìNÛȘj@ϽQ×ÛÔE¶¢c)| N¯lGïeœµ|"!ù¤Û“Er‡_Û¡åZ«äÎñõ$ï=b2Ð-T%ïûÑϳ7ÐÚž£·¦Ê6·b×*Û(”ÿÂà" é[Àö¼‡|~fǵ€¾»>³óÝûí3»ÎîÚgvá;¤. t±+ǃ‹hyÝçE~P;©þѲÍ+÷ñz^Ò<T.ôÉB=M®tûMuÌÌ(lùö®|.ØŸÀºMœ\ë°õ1ŽTEOÊæÐÊêð± q¥éÖ—?Þ¢Ä}Ÿ?jIþ`㘠¹Ïó<ÛÁ%Ý^Lólø…kÍò½{%Ƀ¿ÔxvôƒÏ¿xendstream endobj 308 0 obj 3140 endobj 316 0 obj <> stream xœí\[G&60 H—ØÁ¦9H3éºuU‹<$vœ …˜MÂe‡€8^'ëDäP$  ‘Pžøá‰Äcò_¨Ë9Õuª«{gv{Ç»heËž3u;·:§Î×Õ³SÕ VÕîüÿôöìž‹‚™êÊ‹³™hêŠK]mûOºåuum¦”¡Ÿ]k³gfOV×g~®êâCðáÆ; Ÿá¿§·«s›³{î¯Xµyy†ëÎݡۅ©„\¨js{vö}wo^5¦²+mþxvöOòjÎ=y‘F[24¿ß7Ë*Pð­¢ }?訶©¬\ŽÜp¤j¡ë­¾+‹}ÈOTCë‡G¨­ë~^Ò¨ >â»êJò£Žä¦š‹Av•Y¡³‘ ÛÖ ß¨ Ñ‹Ê¸FC+2õ1Gi”Wð\Õ¬­tøæã ™¹áèÛ-p4RLzòv¥L;=-Â÷F务1ï©–W"ÌóI?¯ªæ²î ¯P†OAç@}ÚQÒöµ|8ú3~U]5,™JÄ©‚ ߉ îø8I|ᔬÀ(w…>¤—Ö žü, ……N‘™NÃØT"Ë23d& O‚ãPž9™9i>{ÈÒhk>QZ*ðU£«|ΓÖÜ:Ñ^Ô%UÎì 2”Ç¡Èej‘Êoû‚Òƒ,O|Ä_5z¨w“YôÂæì1ûgg&[eãX­lxs•à6ðؘ&eFøN1Â-܆qþ×û`£ ˆUS³Æ–Æ:SÝøÉìò—­¿Ž ã>|£d2.ißržÏ¶;R ð C<ô›Ã·{ákeŠ<@Ãýæðm#WçIÙåy€†!úÍá[U÷xÀŒ·B¢SÍBTÚf;áÝW!=ÍMØi_󴮀ü:xl¾âC|3á/|w¡çÑèã 9c{ÈÅFÙt#Ú³ø¸C-ŽïyÒr ûpk DPÚÓOb¶{ÓÑßö´®€üŽß¶ï¿‹!Ð>ái«í°§7aiº–µ¬õ”ßó] XŽU…!ä—°ºýÛ±fÃÐ߇äoñÔ1ábêÞÆ¤zøQ´ä¾NUÖ’¼Ò’/ZoÉÁ,hïœÂè×aé:áLD›^ò/ѦS-…{øù½þ¸Å„ݪ2¬t 48dÉ4Óâ…’¾Z‰l,e›5+Žm“ÚFÚ©Á6%•Ø}ŸIÙÍåìØðÄ r¸™–2•ˆÍ'?61•Í6R$2xêÛ·©|² BÍqj•h‹««+õpÜÌzxæ¸zR02Û"š@¸0ÈÅßÉ]BN!&®[Áº¬ídŠYÆÎ%<Á•õéÒM†Û ×·‚Å“RÜ):ÙU_"±C¬º°–ÓW·ºR '®d½6àÊ"i³ŠÏ¾ð^ÝQ¶#LýbB´hª1ؽ \Cï?¢Œë»®wpšl‰RbÑÿ6² S]B˶»š{íw‘уX ƒÝkxF˜úv¤M4+ {¾4_ Yf.éË]Æë#‡>Ò\´k+€ãv…/LJ8µS†K$†³üu*a£4(è½,Òâš(Ÿ@-™‹ˆ–S¨¶ Ž%P•WÎîKv¸î5l.œ;%©3à6Û ¢9öÆCâäAxäå:«²G»cçq>ió.ã#5ÓÁ”nôú”©±’þ©—·î]y;œ÷©–.™÷weŠŽkˆx;Ò ¤òK×°1q×&®jC5Ø6w‘=kŠñìe Èi¸Îž©ÞƒA†)]yîÑIå[³ƒcÒûµ9ÝÃÉ©t#wà›GSÓæl‘EÅ&{ã‹íg ˆ¦1Ut^@8ÞX…i,—·ºÃˆår£ÍÔXn'ñÑÅrãod¼‚ᆢÚA¹@ÿ 6i8†w{ç¬Ö|—wÎhøbY>‰QhìÌZßÜè¥óèU”c kpûÙÃmºgl›»ˆx”‹,ÖHøñ¡ÃXd1eøÔEV'ñ!+²L˜mïõù7» †ÑGao üÄÕ¿ÍŠìÇÆ—»ccúûWî|ÈŸ—¡ó2·πün­œÒÞ½Ë6Õm¹ÊÐæ¨#ÖýºÚ®7K⯠åܳ$©^"mÛ„¼¨_yGlÓšÓr•H\$Â@àÄ(=(ãÙ¢rtÕ&d?&Ñ“ž¾½‚3HÿØÎñ²úÒeB,å!â©Ønp®ÉªÕ…se@r¥UX|ÎyùËÞú üøº‹Ë¨ÖX“½ÔëQ˜B°,­»…c@+?%ƒ”?¦¡O_ów¥‘0úç6¡JL [RE›ø¡TÌv/vd7Úï·ìÝÞ1•½Î‚§¿Â‹&ÙËlmÚs©¿v s’Õ£‚<ÅU½¦š~³ó±Ùÿânx½endstream endobj 317 0 obj 3374 endobj 324 0 obj <> stream xœí][Gcœ0A€¹:„Fâ! ͤëÞý‚’ØNb.B1“„ƒvd{—¬à„«!òÂõ?ðÂðÀ+€¿„ºœS]§¦ºg{v×»kZ~Ø9S·SçòUõœoÆ;U½`UíþÁß+[³‡/ ®«k·g;3¡ëŠKSmùW¦åuus¦TC_»7g/Ξ­¶g~®êÒðâÖ5; ¯áÏ•­ê±åìáG+V-¯Îpݹ{!L»h*!ªZnÍzÓÇ–×gº©ìJËf½Ù‹¼šs/žpbc¬šßâ›e¤“¾UT¡ï[ÔêŠ5^<åDÕB×{|W'º×OTCëÛ¤ËÛ~^Ò¨4H÷ù®¦A|»ySÍE¯ºªѹ‘°·Ë·|£‚F¿UÆ ªZQ©w8ɼçªfmeÂ;ïËÌí'¿ËÉG£Ä¤OÃYª´³c°ò»}o4N:ó=°‡^©å•ó¼×Ï«ª¹¬;Ç+ÜÃû sÞï$iûZ=œü¿ª©4K¦qª°ƒÏàæ‡wzÜObᬬÀ)€„1v/­C½øA %3}Ʀ;²*³†Ìâý8TgNfNš„ñTi´uŸ(-ôª1T>ìEën“X/Ú’çdÊãPÔ2uŠH÷oû‚у¬N| ^ F¨Ž&óè…åì)ûog&[eq¬VÞÜK%¸‹iRf‚ïnáÆÅßÊ ‹v+#”ƒ0ÙØTÑÕ­¯Î®~ÜÆk´Ó>¼Q2—4‡wu϶^&]Ô´Eí’«Íþ]U¯ì("ý€Wzaƒ@· ãþˆè*tà¾àͽéCÁ¨¬Ùæ§îbÁ ™-‚ Åü€¬¢ñÇ“s†á9Q!]U7¨ÔYh†E¿p‹JÐ…JÐE• ‡’DŒ¿ŠgKÜêÐôfy0'Û¸½€/†((ãL¨ZaƒÐ‹ßt™›Ð\ÄÛ$š*I7¯‡2Ý.Tï.Np/Úõ›Y¨œ¹dWo;cªÆßÓ&6Ï€W’9©:M*ÙsÎØØlyw–jzÚ™©¥8Z*„² ‹Í& ]Zá¦Ö ¡Z/Y(»kµí‚WÆb²KÄÒ©Ñc–â‘á•kíEÈH。ÛSc'6¹2®ìÐ(±h½¢¥“b ¢££ÖtÌUb~†æ§©xR:=æŒY€<ŸôYb½Ù„“òS^6ˆŸ&4ß™Õ<ö¾ä³ïXŸödh8¶ái-¦[f_:»}Âw.Æ—p–£Ÿöíº ­Ï€þpñù%‰ýgñŠ¥‚±>âs€áig×}¼2‡óûòepŒ…'Ïó§¡?¨ùP“·g7È/ƒn°ÚsxEù‹àW‚¨:r±yˆ¡/YÍ–€30ù¯ðÞFü5èâfkG;ý íš·;qW‹c@nVQ˜êÇ*aª¿ {e0ÑpjÔþ9Ü÷d„Ôb:î±h‚ˆ_¡º)ðc_ñ:Ö-~ëÞ2]Õ¬ô ;ËêñƒòBÄZ<êªÜåÏàcùçx)-¤‰®V(ÜÐjã¤ö¼¿5ñ¼™–é"ÑšƒJKxîº4TvK .ytD±¢iûŒ+…cJ¨|Ð’cêDûdFÏú‰ój,1bíVÕ$ˆRÉdåfR0²ê§e‡V*TiŠõ½£o©nÚõ%ÆX$-;¥*WÇKõí¬|숃±8PµžK];à+‡'Ýð`¨yæ‘1¡N˽Ä:=Ü0N¤ég{öÛ*/gΊØ9Þ–d“ð‰â²YU~ˆ‚pTHwW¥Ë¢dèP8mÊÛ8º`®%a¨¹‹*`Å¿áH7 A¡I^¶Á²S²ÌÉHÃ'ÅJgšŽ=aBù[¢Öz>ÞB¹°¨>º¤q×è§Å•¸eÇh7ñ–]fi¬Ò]FÄõ\ƪVõÎU9q‘@IwIÏXH¥@n³ Ú5~ç`6HÇ*c$Õ1’\íLãÃà´Lû˜`ÏmÎÄÿãÌáð?ƒÁ=à¼É­Þ7'`hJyµOý6îTðËïýx•]§‰Y qÌn9’´s%rRÚ!Ðd Ùbi¡a@鉖rj¥(%¥¶õa= °qPa¯F þon‚|’@aठÂw=Cg4Á}°Ìôªï,âÌ¥¹_Z¤£Žþ#ƒ¨ö>Ô¬%É]¨3νàEqøW%–'¹“´ð° Ôÿ;Š)”9“óÞPhâ7&àîd/äÖ¥L¨^ÐßüÙdå \÷»_ÏZØwŸ&»oO^Ü›‘Qßð CŸŽ¢‚㟬òC׆UèwHô?ù¥ÑæŹû±­Ë†2\ilþºW‰oË„\;3ÛµRCʻξٞÚýJù™©©êä@ÒH·m©Ìsæ¾Ú£exë +®`Çÿíõ¶8ÞüÇÑáC7ðà‘áZýß5¦R²¿Ý®jSÙ1eÜ[ÇíÊBS¾qÇñ|SéAòÃpõ„€$*>öej@«Ö•PžV-UáëѪe³Ð+Û< ´jaw«!šFѪ¿åkʪŽ_Íû¶“\¥Z?ƒænCûw°=8çZO÷Ðù»ä–phìnn8¸ïN³»ÑßÃÎ)»;~òû¾•¯h╆'ÚgPi•ôŽŒæÁdu¢FGì~™ìa"™O$ó»‹d·ïDO$ó‰d~#NÝ žHæÉüØ’]'’yšœÇ F'’ù]‘wÉüÿ4¯&’ù É|"™O$ó]SÉ<;Þ&’ùòúD2ŸHæ½(¥ìN‘džÿÐÓzB%)ØfLBŒš=üâØ(6Í ‹ {F&*ýhFn$‘yé¯é­á€ät¸f )e¼†XyYûËF±¦V*——i%«t¹²+Þ_N­%Z¬lÓ¢vWŠ.×ç†èÇý\Ø^ùé+”‹"¯¨~NOø°5¿7xøÁk¨”h—ÀUÐH¼(׌³à¦T ?ö]JÑÕP–ÆrÆsÒ?KÍÌc¨>ƒ¡ï®ôão”Ç¡qÖr=6…òCŠ#YØô§êºoØ ‘=7q•q9=íOyéLKøI #B`ªáûOÈ·yTèø.»¢˜Hø©Œ[_¨o3zé G¾@èN¹×Jë_€Õr!…/€Þÿ4ÎᦅùXYïc ¤EáøðòüOÍþÓvPendstream endobj 325 0 obj 3177 endobj 335 0 obj <> stream xœí][GkÁ@‚c§‘xH3é®KW÷ "qìØ\„0›„¼¶#ÇØM¹„K J@Šˆ„ˆ@â/ðÂà•D]Ω®Ó]Ý3=޹즴’=gªºêÔ¹Uõ9ßtdù¬Èróÿ_ߟ ý9;«ÊÊ"Šû¡Ü ¸ø,.~èↇ‰-œ³KPÊçB›r«Z¡–|.…‰Î‘‘…kÃi–‹ŠŒäÃ`8”gFFšÏƒ¹»Z«Ç¦r|åh*YR«[Òó²¤ÂÙÏ —2)r*…‡ë×}AèN š'6`¯ -ÔGÕÒèÅÝÉ·õßÁDÔRDZ\êðf>JÎtàÑ1Mˆa;ù73cì¯óAG;3U&„‰J™Þ˜Üü²¶×ŠaCi¸w_û(\4»oYÞm>²¨ã<@CÝfû-Ë—à˼rW•!¾¡Ô;GŒ‡n³û–•íÑæóPT¢ŒÊúäÐmvßJÖ‘îx#6ºJÎx¦*¦ÿ5Ý=ëãz+ƒHó$8‡`ÔSmІ~ƒ\áŒþ«à˜ys±ÐŒ»ÆÛ¶Qepíkv{ðñò]ë •ú"ôv­v{UHqÜ5/ÏŽÅÙ(¬Ytäà9¼c›9ò´¡#_x95^Šß·ìÀ•\·÷BC@ç<àåtÅÎ#é‚!_芭* F¥‹Y—PrŠÈ zÿF&ÑLN¡û%àÈûRX‘ç¨1»@!ÈâQAÈݧ%ˆQQYä¡r‹¼ôxgˆAá .庾âùà zW‘ª1í©`¬Æo®xÙ¹ÁÿF4Üšù2l%„Ï#tººÜ„Ïyµ\ñüJÔ³é¦m®ÆåÍbBÿ½/ßìLåy‘Àœ ¥{BÐnæËß 1nêykSẹ¬‹õ37Û "ƒ±ŒPùÈàä®ÍÀŒµ3}s‚vv™¨ôBË7¢¬·}Þ›Ë>9±9¥ªVç ;_󧦗ãRï¡eŸûÓÈח46…Ò–ú?F‚¸ª;À1úö’CË]Ö…õáÖñóo; Ƨ¨\(œ˜x­Æ‰¸¤Á¬QþE/—¨>/‚Çû›Ýd`YŠ8‰+©Iz­æ{ÈGräc)‡R5ÞJ\˜ш@ÛÉ Î\ï’½õ+D +Ü:»»"HF£ãôl‡…–K¨f»å¨þ9«1*Õ·|Œ?–›ß*Z—rdð¶+÷j^ñÞÜš8pÕ}âÏÀ≵c>•M;¨Øy‰CAMÀÛ¦ }[ ­|Û—L¯‡9Iº,(ÍÐé{érf–}?[[Ò¦K.1–XAÓ i´ÓÄ^}^ò(˜V™Ê`Îçt³àµ6dŽÐePv††Ž[ž¶Afyi2L.q^—,ÂÕùŒâŒŒb³@.ó]s.ká“ÖHË]› Ë”M™{úÆoØU7¯ë–æYåRka@€TÛ Ð"‘‚I˜…BkV`X-jZ/l~B¶ÒÁ’úú ^¿lBŸ ©ÿm„òvL»:h8ê àžàwùFAÌ÷ zA(’æ©{m*jÝ‹^lùЇˆ0Ï[#ËÑ|ª>Â3çì± MôzÏ¥‹§všÄ깑1ýNOý?âµf™Àù#ÐÊÎч#©Þª,*ÌÁžÃx°ÈÈ'ö!ê,aÿ!HÃ03'& E(ºº i|l(±È¤ìu±•Þ:ʘÝnHuZjHÁ”¢Ç˜cnz¥f‘…ßKŽYò=îõP:˜[ŽÀæ¢K¼Wæq(T˜×êµ”ÊÂà>%B—8ÚªL»y‹Úå:«¼ä½5™XXw1gCév¨eN%mgí9´ì¬b{敜鵲Rîq Ñ[à{-í‹< ×Vm#ÞiW§Y2%¡ÚÖHdy4;®àÂCZëÜÀ PûIh:úÎ[¯–C=ûk6|hÂùçë–VxúØžëü´[hý¹íŒ“}Ë˳@¹KÈ­3ÀK¥?±}¼ o”ví›MìY°×ú°·".ù<ÆFédõ]K« ÈÈnñ ?¯û¯½­¹xvíèî~`õÐÔ?ŸÅ€î8ùž¿œ—›ê£ ¼Á*_@'úû V k¡FOu‚amp—´ >ƒÿÙ¶6ùÝ¿/@.7·—Ó; tr¹¬1š/49Úã¶¡…¡~¦â†ú4Ru7wº€}_Bè( o+ý£"ðáà‡4&/´ KQΔõ±·àDáfø"#Z÷9I¢ù;ùí­©éÀʼ›µ• ê&!cKŽ1¡W»¹Ä©w¦ñ¹P^s¢Ç *ñt„‰R¸ ~Hô8˜÷ÒW‚Îdy-)AÏOœ[‘ ¢¾9Ùü­¯7O™ÐkªD/[e±²òë†0Ãõ×VM…2a¶õ¡Š{ÂDgLD­6SENX£„5:É~µ^¬Q‚g$xÆvÂ3Z%|sø"5ckt’"A."ñ>A."ÂH‹nO‹µ@.LÐIòïæ+ÕdücIô¢¯ò¯1‘ôSˆ§Ù}µn´Ö¤Œ€¦){š„Ð °ýk&6ô›#—XUÊÈÂ)bªÿéÙë¸9ä©])” ;êc_Ëk×_ÑËNe> Ñiy,O9ªØ–#Û: l†_‡¤ !? ¼±\ÐF Ä F”ç«^k:åbUÉMà·È’~Õµ…•€¯nÃ>b²ê€-è‚[nÞŽw¤`¯íZ¿±Ü΄^:dNcapÛ’JA~µ;eÊÂÜšæÍÿÛ™EÙ'D!À5n³HÄët¦¹ÔiÚ³€Ù˜_&õsDíNÉGœ6ú,+øÇUA-ùsvÌA%6]åGF_šS·æÑÓPË2ë–K,ÛщKsF|¸‡ z䬦†A‡­“^Ç¥éoϧð%Ø*]ë¯l+ëpä.2 Aô9dZ½=òå. –l4Y’7¡³9‘éd‘„ Œ´—ÀH Œò“ÀH ŒÔf"‘N2h"‘)ùÕÑûU#í%0R#%0ÒíFJ`¤FJ`¤Å¢”Ô+E0Ò!0ÑBÒ’O=Qh5£ŸÖ›û_†ªÙmæ X‹ªAÎyØ…/®Ä˜\ä™TOg»zjIERΫ·á$®5ŸðÏw¸ MCTÛ˜§H,O8Z|XÖ-ñFÙoãì(KäYsžÕ½8’á‚{…rqR/E/ ·ŒÛ­€ð#{fƒDÔÙXlQÖ~dh÷<\ }cF4 [ÿ¸Q”Í4ŸÄ’k~ÈCÈG¢uèê V °Ka£L\Rˆ@¡ôn€›ãˆàe¹]^æ+@´—¹5áD´B@ù*ÿï­q ÝÚb|1£!´‡¸î^p+6ïú!> stream xœí]Û&E—ù4*"«,mâš|C×­«ëÅp[Y5â.AX„d/<€â4  Ä»ÑhĘøÞ5¾_|ñÉ»þVWS]§ººçëo¾ï›¶³ Ì™ºsêœSU§~S}®(·YQ6ÿàÿ§wg×ß!¤(Î<1;7UYp©‹]÷“6¼,ÎΔªéÏM³³‡gwÍ\_Å·ÂçÏØ^˜ÿþwz·¸igvý+všá¸óæ¡Ív]¹­ŠÝÙu¯{ßÎ#³ª.ìH;Ì®»È‘¼˜sGkÈZ[Ò¿ÞËÂS»RQøºoh(S¬väVC*U/qUYè讣J/ îyÌõK UÔ›\U]O¾¹!y]ÌE/»ªQ¹– Û=ç]¡‚B'*ãYô¥ÈÔ[J  œ‚çªd¦Ðþ7oÍÌm‹†~[C l“޼ $d1Ó½–ßîj£r<Åp2/z)à áûy‡ëWsY¶¯P†+ ²§Ž7”´u- ýN7ª.*u%BW^ß…Â5nù¸’ØÂ ×XÁ¤\Ú”—^Ú u仡) t‚ôt5´%²,³šôä•`8”gNzŽŠ¯r+×ÚNŸÈ åù*ÑT®u¤ni/è’*g <ƒ4å¡)rOŠˆå·uAé~,O|À^5Z¨3Ž:™Ñ“;³ÛçfÒ(ÇJeÃ[ó£ÜÓ¤LW)D¸íÆaûëü`£ ˆEU›Ú6–µ5þª8ÿàì¡÷[{­9T ÷þ×!JFí¢b÷[SÊ´·½yPŒ•¾Uó *µsd*Ͼ‡ªì@lÁ¹ J¥Ê¸…°t4¤ªÑØÑÍd”\S ¾ k)NómÁ¸¼fn‚iÒÏ{™ Œ6s…»Ä¼r[®pà]â·âÚÂDˆ lätS%<3¬ì6­¿œ¥Z¿¹kØvÁ®‰æÒ¯'" –r–LY°ÉX~â$µŽjiw²:;24°¬9ÖÄÛjèêÑ ¶HÇvVº sÉ…²ÈxÛ¾ÑÊÑ ðõ8:͇ij…Ö¬ìJÞЕ+ÉÚóCͱkÕvÂÑ#Ä®NÇ×ý=‹µãW¯¢®4¶Íyh':ù¿"q vÅ\ìçj¿„“·æ†§Ô$âVÄðÒ¨øÆô¹ÕI"!öL\´™Þ烬²V L¯ÝÈ¥=Â1ãyùEó+mgNyÚïMÑÓ ¥ßO3ØõŽÑ¦/GÒ±?¶m½Ö¡£äD6æà~_PÁ—ùÍÑ|Œ7ŽsÑf“@†)¢ Ž×&8^ÔïÇ›àx;oüHo‚ã ¹²2á}è÷aõ¥7»C ‚ØIÐ ¨Ì}ü­Ê©LABWŽÑyÆßJ…ëLÚð‰Í—Áä¶«¢„µ.C "ÏÒÛÊ“Bï&ScN™ ÒÙBµÒ®ÉMfÃ:˜%_šÃéHc*ZÔQþšz µ fµÈ% XʲŸ")K’¨eøÏÔ`)mšà(ëß HŒ`ÂÆÄÎyltSð£Ù¼YŸß]XÀ›É¯Â °²Ú ¢­ùs| ÕÞhýœÌp2²«=n¬ÜéÖÄØßZ·:ÀX߈ìŒâ2’FzïiÄÁ/Xef•˜0j™)y `Ôìb üüÎuƒñ)«Ê€ &nô8W4˜µ“2è¥_lè(#+jžm›CR)‘¥J› £G–Á £·¶¥bÂèM½ £§á&n‚äH^-7ÉãÍmÛòO” çvsݹ¹OùîѲ/P  ‰Vuàe¹Í ¡ÜAÿ†0þ “º…5žv´ÀKñkѧ€> ³Hž'°ÇèîÊòÉŒuO¡¤{-¹Ù˜Å ˆHž¾zƒí—Q¸›±V#/ä¦vã€Í¬iB{j“[­÷¤øá"ú=……àÔ½†@Ž™>ÃÖ•:ÿˆÌ> bÌ[ƒzL¹XÏë„8¸J>5—Zãæ.“ÍŠ¿˜–Êš<$¶èjü»„A‘ñ›{g0Y\äЃkí7$¾ÒRx&LÏeà­ä]´Ö?½•°ÞË(ŒÎ4 nYcó°ê›‘ r9A./$ÈåÑz“is¯‘¹è:"G›çdùk±|¾¹ÇÈ„àMºÖŸ‡¿èC¡|â­Jò($Í_®(†W=éºÜ×s2Ü*ýH‘Áäk&ϵPšùXý›ýyN?}É`_z v$"5„ûúißÌßš÷dºsWäû:iæ/Í/>dx{àÔô˜ôìPŽ*Ÿ ¤ïœä éd9{'´óÙ‘;’ëjÉ Ì°¡oWtÞp5ü Ýè¿»b…Åÿp$ûOüþåÊú·´êyc¦÷uovÿq]‰ðuÿBÌrO.X‰ýèé÷.´ÈÊê?ý”~L$s§;¹Êä*¡l©ó<$Ï|$yž×z_Ï9vÈÁÊß6Íœ‰íÚåköûi3Ÿ²vQcµûî2·kÚj¨dóßZÃ-<¼$Pêz¹ràßï÷$ ÿàHrÝOÁbÔyÀ·~š’îeZ­- ¿‚Ìxó¾(xb±~÷ñÙÚìIÇàü]ìD°<Òƒ;yl¯ï8׳ Åv¾›üO,ÅЗ>ßX'8©aMçN›Á;cM6ÆÉÖQìNÈêR-u>ðgä´‚;ÛjsðD˜f‰Æ|f”By}™Q®üÃŒ(ïaÁõ8a›ÿåEÌúendstream endobj 342 0 obj 4155 endobj 356 0 obj <> stream xœí]Ï&5UPwõÕ ˆ ¬câšéœžïžžÓÞ¨ê}RÕúüé`ïþ'›¶«^|eïÆ^ÃëŠ2Q˜¿„¤uum¯m;ü·~âÚÞ•½gªë{f¬êÉÇà—_T£û7üwé zèâÞýV¤ºxyÏ}w¥ÿh„Ü惘í·ÕѽûÞ÷ù‹W÷xW©/]üþÞ}7™&­VÔ4oÖÍN¨¦í~¿éf•m}Àô6•}öƒº%yE:Ó<£›­„GÏšG‰èCf z?\h=wÝŒ‹:[­˜GEÕØæGu“vժɂ«°>ýáŽÁÜž{Ùt¶Ði¦J¨p Ú^Ô-º%h¯ÚšÈJØ_>˜Y©7tûãºÝ¸·]‹0Ó¼fHB 5-–?ažvȱ-âˆyÌ!Û’´jì8Ÿ4ã¶ÕŠÕ=á[7‡ÛáaÛºC·˜zVÁ¡ÛŸ2_'ÁPÊ΀ÀËwºÉ—^îḠñÂ9ór Dù4´OÙÙ3EPÓ¼^…C#}Þ g¤@& šwã`˜)9è¾dšgRo+ò5©OY¸jÇ*÷š¦"·°çq‰‘s$½Jý«Ê(M8õ, ÝAÁD ü*‡æè"Š>rqïëêß=&[¥ÇêV©7ýgÛP¥x”Nc,j˜‡¼†Û×£ùoð‡ÒvJ!V¢ë:õ2ëóóêåö.AñkG]×ÐÛŸ½– Þ ºí¯’Å£ÃÐRAí[<„Áwp¥µS0 »í¯²ŽG‡¡!5OÂ9†ÝöWÊæÃ †/ìï9½æG6À¨·v3Œ\Ë÷›Jp¾/Œ‘{LÓª³Rö%Ó4¿ì<ì&5uý¿€×kÔrO? ÊLæÛH;텸i5ŒvÇö•ù`ÎhÃè+¥R”Üè¯\­áDð)ÓÏ+Ûû4ÌTìïáe à3N™«ùëö7M[TÐ|Ö4%îV”‡æh¬7ÜXÖó ½ y=|òïÒ똧à»0¡oÁ„V ’ž{ÎÌ©u*ê5ø2|éY§6¡ým 04í¼š™[Y…÷˜á=ò¹Ÿåœ-tÿ`æzßVÚA˜öo<èu53hNú¸³zqѯcÀ˯;Àà­§Ð/1/,D‚ºûY&‘Àœ'ú=/‘¹Ñ ‹Z#oš/8™ÿȈCÍ#¶·vsænŸ~ÓaÛ©> ˆ$–àç¡Þ}É!­È?êäBôÝ­ûÔu„µë ru0¨,Ê~d†QcÑýNê«­s<vbñ€› &ÄHF¹¶ý”Œó¬ô½#׃0%íaôãNöL뇞M¬3õè–WtsÕ4Ä‹OÔ:ø2w_¾jÔ º©…ˆÃÂà XZ7뫘ß_ŠTãòÆ®#Ž8ïñgá¸f¾ë|µq=²†LŽÞ<êx0pOP½êáICË*P¤vø'…eK¡yteaE™šSDz`õÀò³ˆØdÏ) á]ÃÁἬæè"9\W¼…ãÕ4 =/\ð =M5ßÓÚ£‡¥SÜIáný•±+ª‚2&ýÃ7àáP(âàO¸'Âùጼpn„rR{­x™ ,©7"ë—ÄaITÙîT»j_¹o?¦¨Ð<)’Ñ8˜R v"{žrþV8ŸIr~Åã)¸U4-²’ó$ÜëE5vŒç¿EÜ·œuÎhëóžÁ#94±˜!Y»õ‰#Y8ÃX¹ï\ƒæn=‡ZT"vŽMS¿,<ðß '‹]QÏã¾S„sżUóÓy|‰ DhÞ€n` £P ËÒ½ûäðXâZ?ôZ,Î… zÜõ Hyi[Ðìï"ýðE¤¾^A˜µJ_FîbDÞX)­¯{UrT|ð™z<æ¹¢S.óì;Ð¥e-z¶%Ž‘ d2ZaîPl¸ÖWmùÿYDÇÔŽF¿UPÀ‰ -F;7ûA _8’ŸGÃÝ–l%FñX·cj.³zÎFþ;ù7Àk§ u Œ2ûJbVm¸™ˆ¿äZ·‚> aÂðj‹&¬Ô•°hxÕ4IF<íÆr8½€†&þ°ß«IOØAÒ#²Ÿ %¦tÝ0¥@]·ÜodnaS)îF›¯ž¨˜Ï ¼ ¼î ^6Í¿vˆª·˜¶¼ºÉÑŸ& ¸á=Á¼ëBov¢òäPÌ,šÂ1Ï9ùA\£Æ¢ˆÅ»rrâ«· …|ßäQd4O(žÍÏ!œ¥°oغ޻Þ$ëÜéiLfÅ„ÖgMÛ¿ÐuIAvPÅ¢"Ît'Å £ã,ðȒʨAÌ1P"гÈìù ððS<›æ$;µµ^`€=F¡d‘ÙÅÈAM’m 5È{klµ†þ’Õ"6ŧ?hÑB¨¦n–YyRÜR¤~éWnÂôp„èŽÅóEsäNjf@ÜæØŽN¶˜Þ,i¤Å4}ƒñ™²³Cu¾=¨šwÓ+[½mm_~+IÕs†ÜVç$¤„L¸íŠZ#Ýî°j+y‘K _Éøg¶ü{Õò†˜AàH`JÅ#ï¤{:?6 68å…GžMƒ„>ÂydYô{µ ÜÅà•¼êiBT8 Ê1Ô=¯8Õ™E:‡aGʼ=¸Ø=îósj pxÀÜ3ÉÉ+ ¾Á›š¬ŽUv:JÐDnÙZ™Ôµ“êL3¿‹ŸT`ífõÕž¢Žrk—¶³ÎîÄ”¶û…g‘2ø›S`rª˜¼×ážþŒ Íg‘È]IÁèz µ~€Zn6¶•.¾n}±Ë1¨Õ÷•7¯# ™"tæø9…k Á‰+•KZûGл\q<㫘P/h‡‹ã—*äÛb{-vVÁN…¬Q¥™÷ð0Å[\tÅ¢³é#• ýä. 'SU(ýÂÔmoc‘ªœ1п;GêHùž r{å{Là’µø`€móEÅòŸžã»¼0Ň$êzzaòŽÓéšB\[ÞÔä°*y¨^,ÄpÀI:³©±riÚ p8¬63މ@Ó@UUl€ƒÂqóð-yR $ªw­=vx5]¤i|M×zíÅ„ÐL8c¹”ÝÑpmp„[r£ŠrÞ ÇÓÓ-Š›}åÊ‘­˜—­v[«lg¹ãó6ÓÑÄ!AHÙ“vЬ·ÄÓ¥Ò£TP6.ÊÉ‘“º(–Ž»'>´Ã°jb_n¥|o}`h=…›ry‰1™ØJ‹[(¢î +ÂÒž{4©^\~¤§¼¢†2&|ÒqïNÇDãD¡Yã}CLP)*Wh¸TåÆ¿ÁØ:ßlã²±S× /à”}©H~"eîk“ßqê bQ x/NÊËg†Î-’Ënð¦Õo2ãv~åæ— ·kŠ ^[M©Ñ‰KÒ”¢ë1(ul,ÖR2 ›÷—¸ÍKÀbÉʨ™LÄÕŒ˜ZŸwæDb¥ÈÑ–P0óÜsÅ÷×Ï€’j¸F”/3RÖ<'ÿ80€ûRœkì±zù)µ±SvK£²ä®˜ —šµ² Ÿ#ª6šWœØW¢Šä!ð³ŒÃN‹‹§#7í±G «ŒÒ5¹<– K:¿ØÚêª3öîf ÝF9A„”ƒ›d|Œùš#!¤²›DQŠêcìt‰Û Ü ¼«ðNsì”bòø(Ïj‰ÕN¹ä¹˜+R,pÕ ­ñÇžLZI‰tšN’çe8íÉM QÑ8åˆíáI:p)¦N×I‹F&cgzÔÌdùƒFòéKî?Ë”%ع™‰Nø”œ¸Ð~ä„…rÜ †£jqö\æ„¥tÐ!³ÖÙ”´s–EÛ4+Ã#Ø ™\¨Õµ>JdCOñb8`Jެíca §¹È‘«Ã,‘têa2›·™©M²m»ºˆxRóÒ!‹{„×eY =- ZÍ D.!VX‘îŒ:ð·éåáµG>³=ÃyÑFHj}p¤jÝtá ÷KÑÓ‚· I¿¹$ÁáóãFm›ZöØÈmggÑÄ1Eµf¾Œ°¸¶/ möÁòëÆÆh×íëa%äøí"”YÓ}zlYrkõ‚'šý›štžyE\†s–GX;4ÜQ\oýmµ£úˆÀÑz)BÅ%0ж,-^Rjé¢Rû‹UzxÔJeøZÓTÅGnæ±ÒÛk׋ÐV8ên<¼séâò$Â5ÃZýp’KVª¡ÜÀuZ9ru¬rD­• y,j8V2G¸>Çù ™ 5y‘OE÷uOc”?ï@Y’£uÃ!UNžà"Ù'lo½Ü€3¾þ-„¼ËÞ´Û[J$_›¤Ü× ‘«T”‘P¼ãŠº‡°gœÇ ¯ë»œ¢G`XŒÝ`h¾¡øÏf¨á¢|X¯‹”=R9”ä¸[T¬0ö¸yÓÆÈ=­´™^i#•ØœIÝâžvXyÃe{ô®žj¼ƒ¼Ó Û’;É7\©Uß¡•~ÉNžüôð‘‚ŸíÜr¥OÑR¢ÍN:J…/\Ü3 {IÏ$ÏJ¨Ýü0Âø®‘|ü0.™MQŠ‹ª6 žJRØëÇÙÛ¼'CßÔDñMMkß«åü™Ó›ùâ7rØQ=?Ç&ç¦a.[)Sº±Wé§7Êc^t~Ÿz™Ã­w¢®æWl¤\Ž™êfŘ²(î¬ÉñJÃÌYë·;é›l#¦›åt"¶¿/kcÕ†ït*ÞŽ'xcK¼PÎ}ùLCI¸™íÁŸÎ’i±SüˆœúZ«´k‰IF:u"àÈÑéf U,qà=§ËÃóø0¯ó/Ófiå®À”,ê,œÐ™½£!¾+)E\2PÜQ5ây"–ñzu™rHŠ}®U°Â$ç”ÑD÷ è“Yêç1e+BÓ0Ï:ëiÛ}°˜h³\Ãò‡è$dsâŽL•ÌPRa€Ô ;>`ähÓ©×eNÁ—GõG8”m:²+A]1NúòflÎù‰„RÝBGQä åè Ï`u'¢ä\Îáèî ΢OrÒ4Žü¢™Þü”`ÇÖ$¡Ï·õÜçTÝJG´Ð3oñ¢»Ètomà®…„ÁRh³…UÖ§Sí‡5Ù![ôe;-5mýœç­:|2›Ë¿íS1ôµ’méjÜÓs1zý»µs169ÐÿôHŒð;§GbœØ#1˜Ô÷žÚ­}$ÎmUã)+H…>{‚vù³+rÏå~Ÿs³K»¯³d#oVß›@ÌɾâáÜT3•Þ^;IMê©“ÒxOKØò:tk%l2º|þ¤W“Þœ6çN/¹¯¥^€n:^U; Ü饓'­ú“|¡:¬V¡—é,TưUûÙò}e¯j§"/‚Ã2,C?c:kÖé*¦†-Uc޶"µ€ (@iÚc^Å$kåàm§ˆ©£õ„¦áS…w7«`ê:TyøLgúßÿ»:_ˆendstream endobj 357 0 obj 5148 endobj 363 0 obj <> stream xœí][eE–Q{ôhDä>2²Ðä4»î»¢1\a@£Œ H :r Ð# úä5 hI4"Ñè“Ñÿ@|×D}ôxû Öe­ÚµêÔÞçÒ§Owk‡zMÝV­Zõ­U«jí³×´Û¬iýðÿK»“Û.­š'_˜ìM„n.M³þ2–·Í³¥:ú·¯ñìä©ÉÃÍåI諹püñü“®ÿ†ÿ]ÚmîÜ™ÜvGÚ'&8îÔÿ!ŒÝî!·U³³;¹õmÛyz¢»Æ´óµÉ­W’7SÈSžìŒ#cñÛC±l"õŽP*šX÷ž²ºa] ·<©,T=ª²ÔÑ»BG-”¾{„ºx9ôK •ê=¡ªiD$ßëIÞ5S1ȮꖨÜI˜ÛÅçC¡‚Â0UÆ ²K‘©÷yÊ ‚€§ªe¶1ñ_®ÉL] O¿ßÓ[#Åd ¯‚²œi/Ç(å„Ú(œH1\Ì«aƒ”åˆý\úUÍT¶ýÂ+œÃµP9R×yJººŽO_F5fYW"ugÀ ñ 8ù±Æ=7]8+X”…:g/Ý‚ò&h !=…¶ùŒˬ#=y#(噓ž³âÁr«ÖÚ-Ÿ¨ ùjQUn¤[n“I/É’ g viÊSSä2_‘ÏßÕ¡ÇEp<ñ}5¨¡A9ºbEïÞ™<àþÛ›H«޵ÊÁ›ÿS î€Çaš”*%„ÛöÆëßÌí 6ÊpßXvNùuóüã“'>îôµãX =÷ñŸJfí²bø×®ìm>‚+]å †x˜-Žÿªgf4ŸfŒŒ­tÎC*ÐÎrÔx˜-ŽÿjyÙ[²6K7?ÑgiD02÷i˜vQËï´i€ü *X^ÌZŽå߇æ-¡°ö'0Y?!0(cÛö#%ÿ3Žƒ]}ÚBWßÅr[ùO•i¤Îï+;ÿN1«Ï†YËTþdÚ7 %ÎV°ïÜv–F÷§ý´9ôq[̲ƒ»õÁP®›XúÌÐøçÐ8ÎèaÄ}·Tžþb Mä#´´Ø)*¯¾^Á¾€­Çè€|ŠTŽšŸGˆiz øŸË_†–LgÓMý"Xû—Ð3 ÜBõ‰³ñT›+tõ•’=h>Ò¶_B¶c/^ ’Wˆ¹/‚|€‘’±Ga0 „nÚRƒyØ#Ҟʈè?€…ò-è­­,Í+°ÈX]Frò2ôL9q& 8ùbâ0o ã Yԣ݀µÜy(ÐYðÐEw€L¡·s„é{CõLZËè±Ý  *û‚'§Ü:þeˆÕ6YãÈO‡µï®³7(…s^Îúiª”Ï ºK\¾Ë ²‘º+É/òñlW"[û[S/,³àÜS·»0™|½qói tÖ‘ÁŽî"£Tš:Hp;Ñß¾7CdÚ¡>WV޽í*äri ¶’VÝ[ À°ÏAO¢Nšb©—Ñ×´ ZÈÒŽZÄf&q(3¶A´®tï9Ä`ͳMmpSÓUÞ+úúuèKiÑýme®²«*·tÝ€à6 *Uɺ2 Û†Ù!ÁæÙæ÷+^_Åsµ±d«+´rG–Üšó×4qywâ2oŠã&Äï†I …mÑ—†Õ¸¯¬‡Ò™kbiú‘±öغ·(­•”¤xp¯ÌJ'çvpófœ4ÔýT(Dÿ<°%:Ekó š¾:”¨1@–Í¼Ø É œf Jm#Nº½~H (¸ñÐÛÇp2 u:÷~jb‡MàÍYâx& q'ãú‹¿œ«<Íð8Nƒ_gA馆¨ÔTe#1)F¿Pd7€yÌ{B}Ýš­™·ŠØ^ŒsHF~ò{±£§`ìé&Ø&ã¾Ã˜Ú€ïá8Ÿ‡E›Í´Å™âÜÈ8®:¯U>M4éDUÀ:±¸E˜:[i*I·4ÖÚå”QÅ*R9xð(N]W¢‚¨üº¢qN‘Øú`[°Öí hâhH»V“Ζê€Èu+,d•!…i{EtûÇùDÒÓ~*IÁ¯KÛxXchPy8ØO7ªA-2^èHßô5`²ª™t‹nÍî‡õÆã§Ò¸L¨ œ&KW{ V›g«€þËÕ@á@a†ÜB<¢¦Ê¶Wå8c‰Ðø3€–Øóo¥SlÿRØEÈáï‰Ôi\U•…&ë×ôw LX¼QÙïZ; ›ØÓoa±q¶gA¨™UR沯ëÚs½I@vÿ`ÛQð¤ÖK–Ø•îEPmÈ®ãiV(LØÀW ¥dô’easÖ•@2ˆ’Uèb¨ÂF«ÑݤÙÜkaéÜ€M®[áÜÌθ+N1÷40H/ã(ŒfØ0)»n›PÛ [Ç$N*¯Ó¶´;y0XÈ«°EQ?‰-ŠI‘‹WaVñëБýZ"©Ê‰’Ú—™cߘÒ¦©lZ B¹…"õ-ÐÜ#ÒãŒêÙË¿z¬Ó•ÿåáºê2–O–jQŠUÆŽl§©TnAäzvH ‹ûCn=TÁ5MžžFKOjÎ ›ªNéï‹kG›CÞ šÐt‡õWÜj8*I|ûn6F9GƒçÄiF:ˆ¯‡£ŠI>ãš•s‚êzÐŒN?EÇ‹MI=+*Öª•.PÞ»Ý@¯bh "¡Þ¬=g©°ò˜•Z1Ò:îö¸Ód×Ò X¡"f\ùêQÏÅ\?™Q=ÀƒÝü)Õ±zØžŒѼã~½–^Ÿ˜‡%“{ÿnhÛ ¡¬Lχ^î—|Û_0ZxHô» RTƒÇƒÎð"L)ðäÍ %øò¨y ½¢¢g¤ÜŒ¸t}jÖ…¯ÇÉÓÖlæCõFÛ¯œˆÑŠmÛhî²+ìÌ߃4JÄ^ÐW]ãqQF}°Ñ‹¨Áçë„-Aq´ëñ¶œT §’ýåOþl“}5Yò,P¸Èý1'‘Y zëS £CmG2âæ• 0ºö¥_g€±x¢1$h_÷QKXàU´q·´,Ã÷-3Cb¤Ö –¬ë’{aÊ}¶G'Ùê±oýÉ<é{ }AŸtá|š@5¯£Â„·a¤²E³±È“ún Æ%™*ïAå|+p¢ÁŠN¸_„»fg”6ç¾DÎÚ„Šç‰É ;“_Fe8¶U…UEWL§{=æl0ç¿@ÞÉaî <ÕvAõ<ÑüÑ|iÙÆ4_·®¾Œp”…DÄé‰|#‰éTxàÏïËâu=½¸ý®ÜЭíÚp¡$“{íÊUW'_Ç´ø#¶ò LÅh-ɇ¡¦¢õhøh=„k·ôR?=ž©¼¬-¹.®”‹÷®p¥2|*uù5²*‰ýE6f<½oÉw¼¦Æ ĺXAác8â#F@ßød:m-2yz>«oÄ—+’‡+IÝ$¶ÿgï?Õ¡§BÖÒV&¨ª¨[Ý6Í}¾eεÕÔ~•° C^9NÌÑ‘Ðë?aÁyë¬%.ø’°Ípe6ÍÊW ³Ø“L*þµnä¶ÀÏî5½/Eº¤ —^Î 2¾—ñq!Þ[Xå—rØÁ¦ï4ëw75'z¹œ¿"9q?9ëÅí2/#Rµ{)z@˜}b 7…ÕGÂ=GC¯ñàÙåa<@sž›P n‹ë}R˜r„TñsŒ•ï'®rá TøX/‡7«\ôÒôÁC¸¼ôÁó‘«æµ?³q˜—˜^ßÝ2Ö­‚íZ/—µô¯>„&÷ú¬5~¨ÈѺÙu0LFí¿[ù½Ø8=Eù6®yþ]ËôõÎ8ÔÂÏs’1µ¦+dÞ1Ø3ÿ³WÈ奯œù€#x5/'¯F÷sìG~¶;½xí½Ë“ûêã|_Í5>0>¹¯>¹¯~&uÝ7>¹¯>¹¯>¶·r'÷ÕµÍy,níNî«Oî«ÿ?5ÿ侺J«ºsw=Aü›¡² _ØÐ3?—’ßTŸŒ,f>ôÈu}²Ì£Í£€Ë~#dcâ WsÅ´x€Óh2]f·'§‘˜ÚØgÇJíKÇëÍÆÌ§ÒŸ<‰~ŸÎg9:zsfÌv¸¶: ¡sÇŒWön­¡sÎ}d•·$¿ÚºZÓ(ÝhúÜh:³jíÑtÖ™ˆ¦³ÎV¢é€nG8š¾Ö_¤ZS q8¿!uxô5þ¨”ÒÇè— \};\½?‡=T8ž þ‘-:«ë a3Æÿ¡¢™ö1ø¡"ƺM¹RéSe‹ßûÕÞEÎuOà³ÿ‘0 ;GÆø—\–b2äÊnæ[àW/˜ü«Á°endstream endobj 364 0 obj 4050 endobj 373 0 obj <> stream xœíÛŽ\G‘p`@@¸%11$ÒLNßû¼‘Ø& f“a#ƒƒmŒwñÚÝ=}ÎîÌάwv[~ðÔTwuݺº«êÌÙݦ‘¦µÿàÿ«Û“'.2%šëw'»&Û†rÕl»Oª£msk"„N?Û·&7&/5;G«¹ø4|¸sÝP!þ3üwu»yjkòÄ“ i¶®MpÝ©ýÀT7Ó ã3ÑlmOß—·nN¤nÌJ[¯LÀ´™Rž² Vôè÷;4o<ô‡eûA u²!Ú§-(:ú J¡9B-`?<]Úqt¤}Ä U óàG-Hu3eƒì ½À`ÍA¶KwRÒ‰J¨B=™ú˜…È)x*ZÒ5ÊóqÐÌÔ̰ð',Ìp6B„;ð!ÄL[=z-ÒFåxˆ 1?2 Bm˜§óiGW4SÞö†(Ãg`°‡>k!nÆ>,ü°[U5’D¤X å% 0ù~lrÏÇ£‰/œq“ås¡Oyé¹1¨ƒ©°Ð™„ÒY˜KdX&:¡à£à8)Ï4¡¡?{ÀÓ¥ÙÆ|¬´”ç«EWù‚¹U¤½ ËT9§ag$Si˜Š\ÆFa±üf,(ÝÁðDGüU¡‡:çЙE/lM¾cþíNx'Lk… oö£`ÔÓ8Ï7(D¸™Ý0Öÿæ>˜hgb£¸²!Œk³UdsçÇ“k_1þªBšˆ _‡(Í‹ÐþÛŽæÔöæA’NyÄóh÷-%síÍפÌ †x˜GûoÙ<0ÎÊ<bˆ‡y´ÿV,Á¥j΂Ž@ ñ0ößvíâ<BaVó@!íŽ*ð0†oeN-œü øBÎX£41íÿ¶;LHÑoãD9¸y4cLˆ?>lâ)xj>¤*•ÌKƒ//B*\'4MÀ‡Èã‘Æ@ä"œ±xŠ6G‹ìØÌ×4 ÛÈc)ÆgôSÿçcr t ,ôs8§ªx0…ó¡,•Cw©ÊQ=ÁIÓLuNð©¹xX¸›‚"=SÆ»§7pu€ÑH %| tÛöªª¡^ kxK$rÿŠŸç ÀpC(ë#•2=Ó[‹»s*=ºŽe+±7Ë)8z=Ï_,\¥d#Úa€ÖÙùni´°Âëya;®cËe[<óŠ.%cGÆÆà]ÑU‘Õ„ì”+sI"L$[jäIS47ùønk/QÂÃ>åI¯ãvõd'Pt#õ¢ ZœMwqJ!ÞÚm¥A3Û»gç•;w;s!&îaW2M肘øOØ›¸…΂»Ð^S"h*§µ·64?Èöë2×]²Tê Êp(.¿!N{|Š“™™,ž©™^Gy²3ö‘Ô=R3Zž7:Ùd›=£Fdùö‘)OÒÖ>†wôË“+­΋ÔöòÇă„{\ ]Q R(1c'«¹Â² ñ`óBž«ó…gvƒGtit³9Fu’ÁiÒª.€ï$aäFbº×ÁÈXYG9y (§œ˜€ œüÅáY°Ï_“¥WW½Ân›}V/¥Þ¼ê%7‘檗ŒÐðÍ«qÙPâÊ7rK”k³€Ïx(õÍtcßÎbÆvrHîÀþðй ?ÏÇ-·.”½YÊÚb3¡½Ÿ ª“!…„Î,œL ü”¹5Ö,öÏ›%5˜# À7Öx 8Ñiæ•®}£úÙ6d©ƒï…)UöÙ’Hy*±|¨ßF6€-®EÂE›Y!øþ3Y`tÚ4Û´éwŽÊrï6„`î]°DÅ‘n|OŽ/ïÂàØ'鮚bkn„ÖîÅ>T|HÆ ¥Ú8Þkã 0u?"“ÈãB…H{£0ÔUêq¥ZÕ|)ÀPʉÕ,S5§?– ~WJ?E\ˆÑkG!eô±ä)—&÷%Ii¶B׿›mÿ5õú\r-ý‡ÁµôüKÿOâ?ÁÊžÖ?$Ãï3¯:mà6ûWâ¤)~nœ#UDWõ¿û|â 4„œ4W;>{mŸ$æ]·ž‚½¶uŽŽìY°7:ù‚=QCU½‚=Ûˆ‚ýf>O|$;œ©“ÖØÔ§“k"xèIiCp"k¢¶!jb«¶!j¢¶!j¢¶!j¢—¨¶!Ö¹ðÃs)º|"ûuµ5Ôõ· }« Ž>L×Gmp¬ªÁ1åÌÝ“ýûØä°ùFùÝŸÇØ×ŽM‡ƒ û“&ÉZ:†:o˜áèX‡chÜèü¥;Êþ$Á,}ä:²þ$Áæ*0¼À‰ÁÀ๾Ŀݬ¬€o¡|>xêÁ{´ëNZbm´P™dú^ý$®Àª*WíE¨Úo@‡!{•JÚ ó=ô÷’êíÕt½º€¥ûîÍ7­ЩCkŒ¼GÅQKë±A…Š"×r¨°(¥‘ŠhõÙdä¹yKêþ…4á±Ì«˜a\«/S9ÅHŠ‘½k²„ǼРÇ+aó†ö£ñIÓZ°.–Êë.„4QGt™TÉerCK ±‰–¬bŠ`­ß£Š³šZ-kn¹ Í„og^鋟³ªfä0ò°õš^'Êgöˆ_Ë Eͽ…2©÷|¡hyÜèü¥_(Ú“SÎ ­92é1Ñt#Òããñà¦åáÆ; ©yxºPÍÃN$…÷?×<<æ¹æá5¯yxÍÃkA5¯y¸CoZNh·®,œÐ–ï' /Œ°,Üþ-Âî(fáðÇ{W…ç™6"¯—Iydþ£3È+5Ž~5Xy"ƒÁ£iUœà ÷€y¸í­!©Œ²Æ(Å|H'¹Ottš`àÚ€ŒlS~{eäÐË)öÊÔ‡]JgŒÛ²”iÚÿãñBendstream endobj 374 0 obj 2959 endobj 379 0 obj <> stream xœí][\GÆl$@ v 1뾜¾€Ä6$€„âl‘Øq"Çk²vOH!°!ñ'xžyŒP^ù ~}©êÓÕÓsÛÏ:G~ðÔé[ݺªN=³» Ûâ óÿàÿË;£3¤åÍs·F»#©Y#ZÓì„O¦¬¹>RÊÒϾÇõÑó£§š£0Wsáëðáæsn?×wšG¶Ggnx³}u„ëŽýiº-ÛÈvK5Û;£Óïøüöµ‘¶[iûÊèô‘@Šf,yÔ“Ö826¿34·M¤ÞZeû¾ÛSn¸ ä1Oªº]yšè=a"­ïA]ºæ%Jõ¾ÐÕ42’ï÷¤°ÍXNeWÙ%:Ûd»t34*h ¢raÅØŠL}ÀSFš»ž¾ÇÓG#ÅÛ@Þ òœi¯Ç¨å…Þ¨œHq4æ‡A†©T'çùH˜W5ã–õ†W(Ã}Ð9R'<Õº¾ŽO4¬jͳ©dš*JÀaðÇPøYƒ{>N_8+0Êý@¡OEé[gÐ@>Ca¡Sd¦ÃØ\"Ç2·d& O‚ãPž™9kþì! ÕF;óÉÚR‘/†®òÉ@:s›L{I—T9Ç`g¡" E.s£È\~×”àx3üÕ ‡ç°…EÏowÿvGm§\cÊ…7ÿQIá‹im[¡SŠp[~Ãxÿ›øà¢ ˆVÊ=jZëœ_77Ÿ]}Èù«Ø =÷ñqŠ’Ù¸¬žêr¶ù<´ŠOŒ <@Ã4&›ãS!–çÁåЃÎyH Ú}¨ñ0ÙŸZ[Ζ²ÍIÆ:{Z±%CŠy=ì. <Bd;›{ú|L@¬úOrbï×a?Œuôů…¹œï™@ž…fûbhÕtîÖ¹&t£š.òLksJbú:ËêGaš®iã¸][Uá¶<‡Ñ’ÇÝóU”ˆgidi'dÕ‹tÒÁäà ÌvŽ0ý(P±ë È¡æ*.u+$&éÍO¦±Ê²•5®|-FIìæÒ4já\……R_ƒ¸Fì¦ÒØ«n8›ôù¸ÖÅH6Ÿå¤¬à!7Úû±¤:Mdp¢³ “¡‰Ÿ:·mÓfa¨ÿÛ55¸Š ÈÍÝ ca\:äŒgÊ3Äò7ˋȰ…ãÜ»$-G.Xa…äû&#‰^›n›€6oBš¢¹Œw»‰`ì-°Ë\OÈpGî4æéý†3ôŒëTg'§õo9s\˜3MØ(w Ùn •(014ß6×i«òž®˜½ÓtF·EÓÌóHaœPåäîx€Ü*_±ÅÅ/ûGFA˜¹T[¬Ñîøí^P<݄μ2¤˜¼«µlzýˆ•­iDdójp‹ï’ÇI˜<:9¯«dqÞ€)RBãP,‚IgW€ŠŒ†üÓŠ9JƒÁ¹Æ}åáÕ¶ôÃ3ýê¤ß ·ÅDbÚ«Ä~ypûRMÕBš©Æ`éth¹PïLÔŽôä褶MWåá8ñ~j—c"EïTî-=ou釹ÔÅÿÆ ¯¢Ÿ%³=·eŸF{-`«ÅÎ bT-c²+9Éq ¾"h'%Ó´0ÍgH¸t:,)±ïç`ž1?e¢Úš©à:ƒÑ¦—ËQ tÐØ˜s–‚ÕYLZeTìC¨["ÙƒÆ!㽑Bj–lʹ:|ã-*ÃXó(L¼oÒµu‡Í_Ä„¹ ™¸È_…ºVeß½ÿM²)£%ºÅòW`jlµËå­Éve—6ºr¶ÑÒhù¶ÚvŒ¥ê©Rã„CK-¨^ `©hyRtÈÕÞÎNF'×q@õž¥é'M]d§¢Ùä©"=ÅãöNJ唀'åH/w`Þu[N7ºÛêÂi­žhyÉPEƒý%ðê4 ô‰äG´Ô‚©8°ÄÉH™„ë\¶Ö:á·ªrP³X€» Ö ¹@Áµj5sç%vZÕ±| ØåÚ7МÌÙö£9áÒféô˜U÷¨WŸu‹z.‰TsBMŽo.œ+IÀ6¾ÕÆ]úÍ@›Èo… Üb]ìþëDä˨_2eü6´æž ¤í§½!Pá| ·­ÓµælË?éÓã6Æ+ðÖ'a×ÅÖï€äì~OÏS¼ B2-Oûý“ØïU \$âØú „`˜ëÁøýü“07x±Hêqn´éŸk0ÛÓ)¾gœö¥É/ƒí11îK¹¯‘Bå â³q³œLm:ª|¸àó‰Ë¸y~ ù7b©ØYR#«Z^!vŠ:ð3h´@ý67¬òœ¤ÿ#èÈ= ì$ú"f¨·~Hcð^ƒ—3´êØâõ‘ê±°á’ß¹ƒzWTµ›rø¾–Ã4× äcQùª]Ÿò[«˜ýB=¬%:Uõ˜¹ç,Œ·éZAÜ4åûì~Îl¹ BP+Í:˜´äÔ8l½êã9Xvf³"E§Vøî °xŠ6ç|6ß2f͆ c-²µ?WÓÉh±…dN6–E„åPxªµÃY€æX,©š¾X$¤òqåðOtVd ºfÝ¿’ÅîìÍ ¢\ÎF³tî®-ÑE2À.Êù¿ ‰î˃‘ÜJkƒ‘<Gé¿ÿÆŒ)`ye¥÷Ø œ€¼û&ÝVqöŠ „{HÙ‚…´¸dý€jQøää†L DÀµ³²y5ø‹™„â¨z¸×Ÿ€Åû¤è@Ô*gó`§¥LdŠu€¥—‹jâvu ä'×iùôîÅPS (\~µ<Ź­k6²×¸ïmr2ÍëNBƒêlçTS“"°ëÝ}(Áœ³Ú“tsV¯žºÏcS yx[=w})§d¯Ú*½á<t¥Úžµ“D¨Ý™(½]M ¥z û:ú%X*§Tr¼jBl§]Á¨¨+%ªÚ6ƒC#äVˆ%f(N²%÷XF>æ—sý)SDYyª•þh'roAï8ö_#õtÓúÐÿ®ÃDœox*]Ü›3ÅYß$|D¶E_úoЇvî’ÃÊšÓWb»Xù ÞŠ:*½JÒ×ÞŠ± :ð-ÂAmÜá—çý2t}ûHŽ6ÿÊ\›cÿ;i«P–åÔaäÇRåÞnYÆRº\€‡ÿ D¸‰Ÿ6ŒÛ¯VkøNØF@Ü.zjÆV qoé 17ª•NÚ¥ ZÜ,õΙÅíò×¥øÁC³-3(­š¤³™Æö·–‹“Cÿ]D¦=4VZ 7I4v@c4¶®üÐØ]LöÐØ…L0 ±‹e¾õ¢±6¡±ÏúGÆÐÇä{èp {¥½»ØÐó6QµïVnŽsA¾wðWÛO»o¸ÙΰøÅ”™_Ðå]Žn‘£ËãÖ¸×T}·âËVs ÐòÚ·Þ-Ð2áq h¹såæ-_é¡ånæÁÊlŒ,•îÕom0õÝ#ùS¯¦Ngüw-´,´tŸ6Z* À+†–K17Zn£Š6è[ÀŪ¾ÐȧõîôËÀtÆÄŠpjÁÄ]þbifÃÌ)#¿Jf~•Ì4àÝÞ}<âà¬D¾ÿ¹¶ïÁd@þø†ÞåÅèM=>¶:\ž/ós¦.?àò.?àò.mÀå§É>àò.¿ \~±Ì·N\^iF¾ƒS ׆̞«¤¬®¬hËzÛ1ÒåÌ™„zaý¨r-¸±Ó=Â|¢ØEgš*ÏC×BÝt¡T‡¾‘Ò[®ë¦¹Zͦ¦áŠ_rÁÖ—ìü—ãE¥¾eÝW36ï®Â*ob‚‹'åÕ –®f\7-þøË(Np}#ê÷´Ÿ' 6ýUXß»ñÒ^snjLÝAwÌûwšÃù»"}þ(ä–8þSñ¨?ÏA¦gþà8ýCEVœÁgÝ—±KÚ´Ýø]h ÝÑÛF²¼m4+Í›ûúýòbÚÄL5½èPé÷¦±™äÅ55;lŽS(Ya`¾'ñŒ1Nn8dÖƒ@üd¸{vØïžib¸{6Ü=;˜­7Ü=îž¾{!ññÑÿ{´ç endstream endobj 380 0 obj 3231 endobj 387 0 obj <> stream xœíMG 8ð@„lbà 8¤·™þšîðW°Ã·Y'„Ø`â8Æ»xípñ¡€$ Á‰_ på!®üÄG€#=ÝU=S==3ï½}owM¬HÉÖëžîªêªêªêêÎVQ®±¢¬ÿÿ^ؘÜF[ºÍáWÝ¡h©­Éâ }8t›ý¯¦ìP4Ž—²Ã=4ôáÐm¿*>?¬´yy€†>ºÍðkWp×c³5jMÚ˜5é7Û+Þθ¹‚"÷Š…»È&¨¾Ó…üˆïëT,ã¯<ÌÑPþz‡oÿL”û°cö|ú7bg?+]o˜øØG{… ö Áã£`Vôw¿VÜ*غ_ƒ ’ÁŽøMTX:‘Š]÷f¦jcmMDú$FÃXMói‰.ÂPßœaâ«´IgÇy0p€±Ü$oÖeƒFœç›°ÌA`ÜrCgEÖ‰¿N0È}wëSŽã~ º¾v(Ç5ÿøèš£/÷Ò–ƒdÛ2L…pÎ(+Ãîþÿ¯w~ !uW—1Õç¼7Fôtå8gû›ÑÆí¾Aˆ‡¬ÊÊÊ <_Dc­3¯Zê5ëìapWrÓ>à› ”¾äqu«ðA°Ï”†ÑPâQâ`èøÜ‰³Î^jÉüVêV°³ƒ,·1·¤)‘Ô›ã„Hª;@ÒÛN¢‰r޾ÜT‚þ++â%N™p´ ¡ôVÙñÏ ÿ)ëÀOûTl>[Køø»a#â)Ìÿ¼iöÌfÈU¶Wt ­Ü­=[¿ºƒfavY\â³ ¡õ! ”ùaßèÈS{_Ãà#Dgÿ ‹„3ýhm€3ëÄø6·ºÇI×gÉ0/"FÐ÷|w ç¸Cãs€.v~Š õõi¼”›'¶>“(Îúc„ŽÁÅYW`ø£Q#!?¿s›z]°zÁÅz`Ãä:6Ú£EuÓ8Ró5ãeè2!wƒì_§IÛi@žî#@Éó‰(ú˜“€éÁ’LENRÜ1AÊŸ<_·'·µYÐÍ`5Ã`Çaâ§üLHÆLU šnç…™ÂXÅ 3û9™¹F«µÅ×C‹ÖÐŽ|=lt¥Ûåà—-ø lˆ’l|‚ŠŽç?·šò¿q›ç¬æo› «ãoå,üNñW0M= çF@rÃo'¦7Q’h s™M¢Ä¼W’ÉnÌÎÐñÖWâwèQï¬Ñy8kZf°Å‰§ý‡::4J§Ìm׆@6™¤ÕæPmoÇn5¦]AR2f–p`êÛP6ÔIž¶cPt€WF€Æž2cÒüœÉ®CX#”š£k”¡¡bTHlI³v”àr.æ‰È<”Ç^)ó´$ N‹PŸð ŽÙÔ=ïxz°ÏÞ‰ä«dp²þ1ÉG×qÙÔh‚7×SõkP$=ÏC‹Ð8b["7•ÚqÁÀTã1KL¤"uùmªs`kçqlxŠlêÉS±µ5”‹:¯ ”ÜŒNºèN_Œt·Õ°@“ÓÆË¹ƒÞ’ßlÑaè*eM²×uÎ á ÂÙ”1½ZXžÅèò§‡m$£½ÉD·;ván‰Qíu7ÑÄÌ´õnà]ó.Ð@¨IM(=Ù0Ý0uͳØ#z t(ƒ0”-•qº=1`âû=¤‰åUÔádaïmÛñ·ÔN£Uµ°¤§º¸ã …ήJ\v²ˆ9îU\@B>w’—œz !!œ«^1YŸ§p!—”ûwÅôšð˜B¶¥tvÁlËç½’ÈN>ņÑ~€‹AFCøœ‡gÚ‘Æ…’/ÊýKU˜|™-ôñÆÏÈ®?Sò£³ùF‘¤ybr#M*œÄ(&2ÆÖIJÃgd„3¥¡p´g`víÜÎHgYùÌãL!_ý#  Àß]§IŸgÁ*Ð Ore1¹·ƒ6&NÙ“Ÿãš@`ú 2õò5J2·aA™þ`BŽOÇèèÕœßÂg>°z×£=K<ÆÃ, ÉÔéÇ0Öð1 Û’ôÐk…¡=c1°Àá òn ™Ò§ù={;Éfhzv™a`Ù;¿^†%…o=‘L(šqóÜ×ýìG~æ²~Œ,kB„“ w•—܉V Iñ?±Ò毤ÌtjÚfE£|»báÒ[ƒU͉}öÁŸÚX¶Ÿ ƒgÒUל ëÉ¥²Áƒñíi‡Šl,3Ïw‰½_fà˜ÌùIbQã‹Nsx‰Tµí'?¹S8t]d̘ʪtÝ¡ÿî–¥;GOÕÅø=—aëY?Š—Û4âmÒE®â`MSVþÓ­r[FR˜^7KµàÝqëf¾®Ôw °§{úðšÉ²Ñ±Y}Æ%xj–zjS^‡qDrÞ¤Wš‹Sjægz¢Óóoh Y0Ÿ-ˆqböQ(ŸX>¦ÖqÝêEÓ‡óqr; ÷*æç‘[«ËMŽ® —dUâ['3>Œ/Š\Iækx^SÉ„$ääêÒLŠŒBSþMתlÖÑ¥p—›‚JŽœ9¨&m›Ï¶ÒJ;œ"ŽÙðL~+¹IãlA3 }læòÌDÜ­Ðð-OÖ…­Y°÷©^‹% ¼bkÕ’K@ݦZ.¿”+Ï/^:ÇJKhõ¥•¬4èÚý'À˜ü~–~ž«ò\Ò£S¼,ájç¶Z l—úËɼ[û_ß—AÖïNAÏÂH€b¦&ó–ºÓ×Ï!O†ÚT·„@èœ= wƒॠ=–y©’äŽá<ßú„òö-ÄyBPÆ—€2¦åòPÆÐy¼ÐþK‰ò½Ez)±¤*ÇÌÒO®o'ÒÕj­*‡ÃýÎK°at‚€ÁW=™ì5@žDfM¸´‡Bßþ`5ó¦r„h[æÍçºzÄC/€ú@¨ñ8š^p³›˜ªþçÕ¬]xendstream endobj 388 0 obj 4028 endobj 399 0 obj <> stream xœí]Û¯&E”E?Š¢À2&> Éw˜¾÷¼ÉMQ¼@eAt…]Ö=+»‹1¨Q£Fã-Ѩ$¾jü|'úⳉ×ûRÕÓÕÓ3ç»Ìw–ËÉ&»_MÏtWWWWýªºföbÓõàßÓ‹;’-kÎ\^\\Ý6\šæ ü2o›ó ¥,ýíï8¿8»x´¹°}5}~\:ãzañ7üsú ¹kqÇ köŸ^à¸KÿC˜nÏ6Bî©fÿ`qû›>¼n¡mãFÚÿêâö«É›%äÕž´Æ‘±ùÍ¡Y6‘zKhM¼÷Ouºa6'<©:¸õÚp+K½5tÔBëÛ&¨SB¿¤Qi Þn5ˆä;<Ém³£ì*»ÆÍVÂÜN] ÃT7ÈblE¦Þé)#€ ^ª–u‰WÞ’Yº'<ýnO |)&yÌåL{9F)¿'܉ÃÅ|/Ìa”êx#b?ׇ~U³”m¿ð çð>¸9Rï÷”t÷:><}CÕ4še]‰ÔUœƒ‡oÄÉO=ÜóqÑ…“áa‹r3P¨SqöÒ-h oGa “¤§[áÙ|FŽefIO@ÞŠCyæ¤ç¬ù°‡€^NV2{.k†«ºìíp´Ru aŒ‡a3\Ý€×UhãaØ W7àAòàN†<@ÃÃæxUft8Î?8<@ÃÃæxÕòõy`ŠÕy€†1†Íñ*ò€ž ‡ï´J4Æ2÷Ó;ü?ïäLŠÊ\¼·°“ÿÑ `‚—67æšøcˆ9Bÿ=k¨-âµ!’‡ÈrȓРì?®§Cª¹1Ê(ýýÓàðTl| þÒD×€×u>=qœYÀà”i¸è;sä’åö3MèêŠ+JÓ{ñ˜Êñ™@YÅ» ™­L|Ü|ñI¦Éº;G¬{£¯ 5Iè¡êÓ ©ùÇÂó:"\²)fÔ%Ÿ¾Ç\6Sý°„êÈ$T®;nºù¥]A•n«5äÄŠ° M  ÂRœÀªÓžZךR[BÕ%r-ô’#‘¥4­qf,\‰ñ ÅÒ^ØdgrœZMøtj¼j)jë[*©ÛÐ*“ᘕ%v»„Öƒ,űBu2ÅvH¯â1'=ãfíÍý&{qä˜èþRlGýx(l U] †wßOÉW)H_Q°(@¶­Úsp—©½¸Ò?$›ì—òTTÒÇÃô0Èü~he 4þäÅGUÔÑÏÃ~ò Di¾£â8Áñõ_á^ÖD…út´äq †ž@ß"S¨›OBGÐ/ŽZçq2÷gõ$ìGxðËaCôÑÌðՉدS9~N ²Q }¯Æ+ÄÜûú¯Ðý½é^qЧ;åpœ_èŸT‰Óû.äàÞ k>˜‹ôË`ÏI»B“xˆêȦíïtËóõ 'Æh…r"Ýââ݃*bø(´.c_ƒ‡™ äA`Ñíyè+¤S]BRàNX"èìÂó}@Å[¿––/b¯»@íUér°1œkÜvqAw‡ÕzÞ]®ÐñNiŠ ªðؤAhG7’޶<ÖR‘t9w E2'j"["›2B84&DSç¶ç‡.’âÕæŠxƒ®dŒ$üQxfºŒ7ÞŽ$sN€û!0‰wTäµ`’6|f˜¤µØLÒZm “À(>}±Êze6´â¦Á 8±ÕÜn2Ü+aTÒß Îÿñz•y|C?óúÀªkñÁ ¨®žB+SòEâ£bÄ ê+øÁd‡*j$¥4•{«L=,Cnî§|C!µòQšÌ8†4¯H#”>Îü¬ i¼Èæ…4Jí"ó£ôö™Ÿ¤úo‘æé†@aCÛ»8Üw‹áþÚ `+Ì#Ý U‹šÏýAŸ¸^ÄŽÀDõ¨¥K'ã ÔÄ¿ µ…®ž@—¨36R2óHx÷ïaßyø(±£)[k0‹ƒ=2‚ÞæƒE9ü64‹Ôü;"òùp´kä}ÜF}­á:g²ô‘»ÎŒÒ ý,Lz©yƪAVï&Ó¼˜¦ûÈؾ„^ú“J›M2ÈsÐ<{9ÊWýet*¤k*…â>GúYlú€8Å °ÕZ2_dù<H ïž3…¡ªå l»`—2›®›ßT# C¥~ÙaŸ»B8ìÅx÷'‹üŽî.m¯AÝ»~—¾tI?@ú£ÌmÕ+ÎÛyGºKúuÄ»§6B‹&qƒ]#oS8ô”¿äáw»x ¦oû×RK±y×P’†-Éa¬$"{‚µxb#Æ?-ä«€,òꨮ¬!8ζ¾îC“~•Šê£¦–à”P‰ ZÕtÜLSnϪÍöäHRߥ4n¦:7Q$*¢$ªݘþ­»…½ø¯tREÓp›µêøaÍp[ÊvOÎpKÁìü·"‰íîZECªÑOe´É—`¨xóÏÃf …ÞÛ>ðæê~ìwääâ^ªÛDñ¦õ/L˜ (Ï¢+cÒ8»/2aðjƒ¯ç‹k£s4ÿ‹ƒ@ðIbhÄ>¸¸H…R ‡Ñ#¿Ã!^5&ï!q™Åˆ!ûXã XÏ<{…à3ÅÉ¡ÆÕm`H(׸Þ/7¶¯ÞW8c¨AµÞ aøè'A'Tô\r|LfànvSÅ:[@ѯ<­ ¹6jl oô0fPÇŽ‹cwT»”‚;Í&/h]é Yç@98 ?‚ è8¤Äº#/˜9?!ãÃß7#ÏÏQk=ˆê|eƒà^HŒ †>ø:èT¦yzm3Ã:Y_¾å5ònµœ'ÒT;'lÁµC·z&@æ\•sX‚ÃÉÀQ‡$mbˆâeî&z+oMmÇ"óñ¯•Ö¸?¼)Þ,ÝÊá9mÐÅ(¦f¨ŠDÚ»‘Ó 0{°/¯ªŒó«[ Mk¨IñZÉ\xÊÙi/ençY^1ê1½8‘iâ|—9«þ՚܇ù“IѪÉ=ç~|]vÒ Æ¥hòÉr5=z]!-ç6¶¹ÒêQ2|¥T£@¶z$¯xr8ù¹=±zO;_' Ò½†HøtÒÝÇÁ¤ÏQ0'ÆY\¢ÐÚ9CÁ4 ãA“m#XŠR5Ð5SÇ ‡¼ 5‚œÂ8¶®ŽÕ—±>Úñ¢¥4ì·FT¹—Õ\M;ÈÞBÑ]ÌïºbB‘=wäi>-Hì˜6^rg£ðu4š: xTörÔè l%Ÿ?•«—U©#êwé%(I<¥3rÌWN€ÀÈ]Á™Ç·Vc¾¢~d6N–+¶Ì× ¤¤²Y!d-³ì#‰ð+êY|M$ ívF‰•a°³çAõ#õŸ]¸Ó9cÏ­€øgÍ€àO¡¹«¯Þ,AŸqÛ†[>OÀçÊÂK~ÒÑÃ!£/€qŠÔƒ²ËœbœírE˜±Û,É.c4Ça—G0›ÝCŽ­ãY±ií;3ºáFéN­ý àâŽä|¹‡ŸßŒ6щÄÄ ñSK¦1ű÷D‚xpüNqVòé¤O×ÂhÓ¾uà%è·—Vާ W|xRЧ£_'É ß +^íw*õºJ‡¯äé·§ ý¥4>3ÈrÙ]¹0©fK¶²36±WÓGŠ8[B•Qg©N ÓV³r:Ú/wva±(¢¶dâåá÷È–©š>­È'¸–á»6Ç¥k•pm÷øÌ¥\uzþRŸÎfƒR0{+•ô§ìð!…{ég`%#+8itÊu÷õ²Nøgzõ·x:¼ËWÿ€%RgñÑü€¾/ÈÍøhyìNÏï‹Âø]ž£3+æ[Q6¼Æ¡~ªÑdÍZ ;<·v,ñB}ôkï°u*gV ¨\ü»iÒ’±µtñ³ŒsÆÁ ç‘M‘Ø™­ `PÔ‘±Ôo€çwá“$ëö¼×c‡äs%³>ÂgÍçJ¡ýàÉ–T3u:Õ¥¿ˆÍyðႠ܈é->ß6> stream xœí]“%5UVÙ•+%*²+b[åZ5—t’î$o°ˆŠ®À¢`¡+°µîŒÎ¢òàG!H±…€–¥eùÀŸð_øüê»ù8'“N÷í¾sgv¦jj¶ÏMçäää|åœtf¿b˺bîüywñàS²®«W^]ì/DË*.UµëŸ”ᬺ¶hMŸÝ×WÏV{ «zê«ðpý‹¥ÏðßåÝêâÎâÁ‡«ºÚyyãn¹¡ÌRWB.›jgwñÀG¾¼suÑêÊŽ´óãÅ·xW[܃g¨•CóG}³¬ô1ß*ªðî­2mUkžu`càÕsþÕ:"ú¸GÄ õ¶èù=—46-@Ÿð¯ªJðvr]m‰Ar=ãe-anÏ_÷ 4ú©Ö\!‰¡‰ú¤ƒ”È3x«aµ©TøåàÌ–íáàO9X`o„jéÁOà ë”hÇÇÀåÏø·‘9ªq1ï„9 B†W"à¹Ëãmª-ɺ…opwÃ˺ÇAÒ¾képðgý¨ªj땈¨Â jè|/N~¬sGÇy" |çås¡L…ÙK» ¼ºÂ@¦ÏCßtF–äZLžÁ¡4s‚9i¾tÀ³¥ÞvùDi¨@CQù‚ír«„{‘—”9gA3HW»"•颈tþö]`zXK‘W…ê…Cg+úèÎâIûo!Mcík¬ysàÖðX›&eø—¢…[:…qò×{°ÖÎÄJi­mg©­ð·Õõ—/ÅÊ«æØÐ:êÃÏÑJ&ý’æð«‘9¶Õ4´¬)Ó C4ô›Ã¯í4H¥z½< Ð0DC¿9üªÙ| †hè7‡_ס3Ë4@à ýføµ·²ÑëÎp¶M»•jÛ¥òÎöëà"·tÐöoxXU>AœÌ¯ÁïáË¿ñN‡Eßü[„Mxÿuÿ>Ú†'ÀR`ïK¾UuŽƒÛÃëßß1Ð;Ÿ×£ ¨‘iõ¡Š–Kë4%†*O{+m ©µn”°•hx¾ãÛÛ*´>¼Çòè~]˜5kþ®‡Uàs3ºÊß/Äqï_F³»&ßòè*ŸC#ܨ)<iÅ)„·ß„ $ˆÝtë`v0€;dBo ^½ŽA ýCo°;eÐA}†Ý¢‘ÖXü0 føú$€_÷àÛ2Î,räMÂúլޡàzãê Væ-”ˆT¸"{wÈPH®Ì3(ˆÍðȨÕ/@#¥£“ˆé¯¯ýWŒS@¨ÿÈBë¢Öhƒ ­/æÒ>8{Ç€Ëñ¥Lõ‘n‹äy°Á¨xppd0y3ŠëOýR×p©ÒQ¢’<‚ Ì™p=ë°¬»!rm‘Ÿ>R·Ñ5rðaà {„Ðü8jœ‡~%(DnA&@€^uào:‘¢”%#·8òUÏJ.È,éÐ Îñ*•‘Ÿe¢»Kdfô"@Û‘[aØkà<Ø :Þ¨‰Shwa2é¼q¯E–· "…ˆ¶É(…®vg`Ý©sTq9cÌO§CQí‡Å`©Lïu}¬:BΓ©¡…y,G÷u”r»OHÙ&¨·û"ô¨„g4¶D~t´ƒ©,°Î…§©«(ðóÍÎúúé»Ààc:ùž¤P̦=lQo˜ £y} ¹¹—yÊ’”[D„¼3dHð˜3dÚ:Bo\žÅ„þÿò:oÚ&À~;·Ú÷À ¥Ù#Ïû@ˆHBÙî÷ìn ûeÜ>CœrŒƒã†”[†Û" ­•ïÌ-Õ YM7€ç¢œ™®77ƇD…%;ðèq±„5aè‡.L2n>° ¦ôsXQÖt-¤ £ ž#þ,ˆzÀù2ryxõtq-Ky!A9ÖM:ær¿5ò»(Ö÷ƒïeÁf\ŽfP.bÚ3—’ ]+‘œo–®BKrHÃY£³t%oà@ª˜c;ƒ½ÇØ#Šk{7ÚaÂx†Œ§‰¯3ÄÊùõTЬ ,®.•Y§1M `’”é+êFÓK®Yk]Xƒ¡ H{<3TcÂÍ*Uw¤¸C%âÙÉ~Y¸5®=MóRU-é˜^Ë1M¥«§ô¨ÀÌ´ºÊ̲¦ŠÚá>dQÜ1ÂÈj(Åüo§37‡o†¨nnsë3ÉÜ\AÓ&îåeñ-ø˜ž•›¤¯çAVôõB±ùÔEØ Oñ.Ö”¥u—YjB1Ô&iݬo •%má½d%n‰Ó¹˜¤P‘ÕiÊÞi’?:OX[^ñ(l%Öæ¶ÄT©qW-Y&g¢.˜‹ˆÙO·v…8¨…ºª¨Úõ¹YH3jãD&¶´ª<Ç=bã‡(î*´†"-ÇzçÝd¨ï+L¤ ©ú*: þEçßÒúí ÛÁ‰¹;zãO,×P´4HKåò^ÖblŒ`h6‘ (²Ô…Ѩ¥‹|­I¬ "y¥X(U7eIm„f%ºÝ„-Õ8›’"G¥e¤<„™yÌ´bwƒ3¹×TlÈlÕœ[7 l-Z'–ŪÔéf6…–¾y‘¥åè‡Óe$ßÏP»\ýÒÑSöÏËÜ×–}-o—Ò§îK§ÌIÑ|‡ú0ºjk媡\È~1t CݺòBËÙRxJa¹™æXŒÍTJþ& .Àß í8ûK ªn|ûO¾Y!ÃÒhÀõTÇ9äÐڕϦ­7QùÜ@­“g#­¨ªI‰y6DŽië?ÃØ¤R†tgÎAŸ€_Ä”hâþMÌØ÷€ú/ñTÿñÜÀùý9wp‚Ö+8Ϋªªéo¬¸gu®‘jɽ`]#l|Ål¸”ÆY,;ýµX9—A[ù8Y°mtø#ùÅÜ9©YµêÈʵ±[7¬7M¨r© ;µÎªžÇ¯˜Úê£+¦òÚË!G0ãI¹)´^,“í«»c é)!Rd‰"ˆéò†dnT¨²MeÄ´ñYpmùö1šºdC)Qìì ʤ¥ä AY;Ñ%Ùt€dŒ¤i¬suÖ¯”ÀêïaÙ0oæl&x‹}³½ó™8Sc×MÂID« ›‰µœ…‰3¾i‘°õ‘TŒ„ë1 öÜZf„è´«‰àëÕÿŒëxðÐO pŽ×¡·‚:zÔ­|(ÊÎ’ÎÂy¼¾"@'àp›«h{¸t„¯Uñt—¥À·×šh<Õñ>¬6éngà¡ >éTß‹ÅÅ«   B¤ð^4Ea^›;õ&kçt§œzë‚Ê“¹¹ÃÌGw .Æ á$à ÛT诃ܱ®wœã?ˆœ|8¡¹¥‡G("©±oÜ«óS>‚ÐÐ?¦'–GëøŠBzþo`sU¤©;D³Í€ÉcæñpÞÑHÍîl×g[zø';öD&öå"!3.ŠètPZ@ ˛ܖž@Ìe%@WaÜ1a TôÅÉ7ä•1½7Qý¨ohcÒÎnwDKªµ>º-œ.G¶+Nkై&ùÜÉÔY±b°R:%³M÷c‡´é›[¿Î ð“Ê•Å|W@*Í/«6åÇä4)Öæ¥íì;H6ö²t™-Þkºö Ýypš¤üµx.rÅSH|¨w‘•V%ÓÏç·ä‰ÏÑ yœs´Bl>GÛÍø4G{)rûà™T®7’I=úÜi3wG‘á<„BsÅÛù´ÓJèi%´uœnŠ7»)¶£ÚXàDÇœ+0+Ç16°šÊ7t3> Šõ[ï-y× !ªÏ%0,!‹Vsã©ãw7s¸óæ$Æýƒ{ù|{à“UõÛ+àÓXÏÈÎYn(âhš¥v– Te=ÛF zjXrc&ôC°2èFŽ‘M²®ˆú¥ï+bRúV4C–§é5(\gù4gpL’6dÌ%{ßÛŒ~öÃ+²Îç+ò â "ð@ÅíQ•‹ux«–îâ¶4fiP%ê%×µƒŸs7–Àãå…ªåRY’±%ÝXÄgßó€j–ÖPw}t‡O¾8VhI7‘à³>Ö”~ídQýô,¿âcnÀ¯lø;7e2S7PëšðÕ@fÜŽZÎÚ4Xð%¼+º]1ƒ¢©¾²*}&¶âÃnÖK)¦WN:e˾ÖLÓå,ãWMŠ QåX=þaýœ¯ôÇw:Ù÷©T>Ȭ‰iûÙ"Ñë)˸Â7Ð.7”Ycû ÚÙåé›Ò‡_SX9š¹Î¿ ÍÙ\œï…ŸTX1Ò2æqo³z• ÔL‡nƒ7ïìn œð­_#»º[GS“pSö:$“ß¹C&LçX˜³ò‰ca\¢#‰c6–tc8&q,¬éð5’:hI7‘à³îðDÑ;ÓÚ—4|Ur"ÃV²|/§ih¸VŠ¢¢¿-†”mFUß§–ó Kú‡‰lÊ•Îù•í¹W­òÆ zãYñzñ^˜Þ936EùÖ츘Û¨­¦VKî.  25å O®B Kú0Ä4Õa5®ŽšjdÄ%ÑShIÑá«rÜÉœUóÊ4185Ô~ªÑkhô„MÎÈ=ò1~Ä›yn"›°ãÙÇø£7 ˆQ±ÛwèÁà “>9ß°Õuí“f‚Ø'eZ°)ᩳOÐÂ’> qû¤kCì“æˆ+…Úâi}^.½vl;¡Ã¹Æä¨k§`‰€ØÂóg¡å}%єӰø ‡¯9ÏÈyiƒ,ɧì=‚J Ã@’ÃD >PÊNˆe»@ó\ÆI- $O ÓÌERÌ>®Ø¥†Ë!4³Óç fáBÔƒ‘îCØPR<>:¢¹ÄHß,›‰%HgŒR"„aï^hjU‚RL¦fƒ^±¨ÀÅ!òª'ÄqÞïÔÐHÅÜLèêZ:4iLbèav˜ñžî™¹V`t+aŸ'ÿÅ¡‚—âœëfønúô?ò¤)1†zkGÛ{•ö~‰¸Šs¢“šióïÝA'¸þ¿'ëÿk|ƽQš?G¶`¤?z€&Å=)­‹]0¦óÄÉ~cI±Ñì&rƒýÂ6#š"Ví<¶ Âû…ã_]˜%sD[y Äô`Gj Eá»ì([‘Ø%°ÃÌÚÁ/ONyô‚ÂÅ÷òi£y–• Àw¼Beð¢WAF× ãÛÂXć&-™,D¯·Nös0šÞ­² UéÞeçi qá ŽE# ž`“¡þôGçyÂñ¦¿’éçZ|h¾D¯:ß]_”Ò~Ž\f¿±Ýá@Q%f£cº«‘â{HÆ/u%nP>Ûµ<<¤Wˆ¤¹¥ýɆêŒÄ¯³ÇΆêRih/J(Fx“jâl¡ Þ”°0bµªõ=":çÔ¯OÀþg¢0ËnhTzªP66…ÕøSІf28Iô”r—G'wy÷5=Ô³_<¦ÒŒÞ ¤5Â,¯ 5ñ,ö®z<"¡Ð=µÝ1ë—žž„W³Ûý‰ Î>‡ît€Ê9oŸN§ßñ„ßd‡Ji™R$pÊ>É—kËÎLUz›5ülãéÊ&;ú²ï¾‘(šý["XùÍ…²w2°ôLJÈ)bôbœÃÒdìßx"äž:uÆã¶Îú ÔhçÄÑÍti) )À°cbèd&´¤ˆ²¢¯™Xņ*°ßËøçÖ@r+¹YM™¢òwèh3–޲P,½{§§~žËA688·÷•|p'·î—w}„sßöõ_ϵ[.†guyõ€0xn€ÅóÖyÞÇlÈ,­[ÆŸ7™ˆdq«Ú·^æ ò9§ÔžÏÉêM™r›áèàv9BËv&u5ûðÀAC®O´W‘˜Õ¦ïásþ+·Ÿ™? +Ë'tØ >¶NÛйMÌó&p/Ù>T£ö‹b ú¾ ëÁöVìÍÖŒ ,xORöó‚»ôà° Q¾û½m¼9Þ9ûë[=¥ Ÿ†#,9à=¼SddDnhÚðì3k)ïûovÒ»¸–áÛðSµ@lÐ[·ö2)#«¤.ì;‡4›¼bO^:™z;K?UÊzüfÖêG…dõùr&;Â(t½ç+U6óÅ…HŸwäLt)$|M²Æžá’ƒäÉéJÖoݳßþJÛÆ~mi¢CάYÔÑŒ÷ò³1õze§ß]…»N}÷9`=%ô(ô’Tÿ¤ìûìïŽn…*˜¿#•Rò­ÿz†Ê¸³ ÷ó¿–x³ÏĤ½H¿|wÅV¸Wgõ+Èdû]˜¿ú íäävÔ°/ÁÀ=w{½ m?‹žÛLbxÕÂÐmg{5Ú50gÏæz7”i¿…‹Ù”8ðËàaà·‘ùPŽÞAÖL3Ø¢2ž .d>CØ}…²{ÊéU쯑¢QÙBî·ÀƒãӤѿ~ƒˆÉAû¦ÂДÒš$dz׌¹ÖSrĹ~ËùЭX šÉ¦L4Au7ºŒ›°·±íð½\ðÿ xnö?ö×®¯endstream endobj 416 0 obj 3684 endobj 422 0 obj <> stream xœí]KG†\¢ð 814‚E@º“®WW—`;6 ob†d;2رã1¶’â¡„Dá) V<~›ì!Á!~K¶HÔãœê:ÕÕ=÷Îí;c;#[ž{nUW:ϯªN¯Võ«j÷~žÝšÝBrY]xzvu&šºâRW[þ“6¼®.Í”jég×ãÒìÉÙcÕ噫:ñ|¸vÁŽÂÂgøqv«:º9»ÿHŪÍó3œwî>m6ÚJÈ UmnÍî{݇7/Κ¶²3mž›Ý÷zOòjÎ=yÀ‘­¶dh~ƒo–U Þè[Eú¾ÉQ¦©XëɃŽTº¾Ùweq ·øjh½m„:uÙKUÔ[}W]‰@ÞîHÞVs1È®j—èÜJXÛ©k¾QA£_*ãY ­ÈÔŽÒ(/๪™©tøæm ™¹}ÂÑow´À§‘bғ”i'Ç åwúÞ(œ@1Tæ»` ƒ”á•ãÜéÇUÕ\Öâ®áÝÐ9Pïq”´}-Ž~¯ŸUW K†q¨°ß…‹{¸ããnb ‡üà ”rPhSaõÒ*Ô“÷£0Ñ!2Òax6]‘e™µd$ ïáð! –ž¶ê¥©_5šÊû=iÕ­éEYRáÏ òø(r™*E¤ë·}AèA –'>b¯-ÔG›iôøæìûçêLeãX­lxs•à6ðؘ&eFøN1Âm8‡qö×û`£ ˆUcXc–­5þ¦ºöÄìüG¬½¶Ç}ø:FÉ乤9|Ëy>Úö<¨–Ë"Ð0ÄC¿9|+êåyZ•å C<ô›Ã·Íä t]æ†xè7‡oÙxàmÓ“žç†xè7÷½-ÀCÍÊö ƒ<ôšÃ·¼o˜ù—HøVÏ¢Ò-³]ÂÿÏN¶Mñ.3KÌÓ!îKRlèWžß¢çèQ­ÓG #V“I8¶‰Ã&<"8"¦2Gc³ñtéÈ» `Ž6“èÚæ‹·™ªé¢¢íŒr<`ˆòÉTÅUå3K4÷zýh“I&}€2#§Óºå6ÃêcÐùžÒùQÝf B5˜ þèYÌD‘ë×Aš¦$`€m*¸†@ mbŠ¿ ð(#êÓ,†êå©Q…Q3£é‘{% YÊa¬PUÂ0C{+˜vd¥šŒ°œz¥)4µ › ÅL†o¢2D¶y‚/›JÕÃâVÄ?沑e€~ž‰²âÃ¾Ì Ê aå…*‚+VTg…ÍŠ¤Ûže<ÅË!5CŽZÝ>6CÝ75Âzvû¥–‹ç#æ,¶Ã  •À»f(!l´@¥sðè<Õà*ÞÀ2ï$ÓÆŒ0Ïß<%q ꇋÉ& FY.%ªÌ¨lJÐ%#9¥]OI¦Û¤Õqþ Fµr:o{^£Ð $Å}‹U‡Ë+’ˆr½Œêi L=÷Ëï`q{i7ê\t~ÑF-ÍËRÝgîµ €)%iël¬iv!MÇÐSRgÙ½JSŒÇ7·ý//¾èôYsUà1cÓPS¨cL? ¶[iŠ+¨±%Ѳo{±WÞ6ø±•Y´ß)xé…¥¬P`Ñc<ڹPWS= ²J‰"•¡òç¸!©Yã)5¼ÂÚ¬¤Æ£3XÆ G6P”»¹”Úº!¢®V‚}Xš î˜JÏvW€K‹}á̘ÚêºöW=È%#y;®E$ûKë0ñË* ;Ý Ê¯‡¨îíei–ȦÛ@ÂÀ¿EûžñT¼IزàA²¨üpE@¯§zþæw û³Âp?b„PÖÌðjéån8ŒÙvOXÃGé8!?ËE¥ l*Ç4¼Ágï)Ä"¿RcÜñ®ðçK6ŠôŽvvr©ÃlžëV¼Ó½ìÊxÝš¾$f$ìZ ÕOúb £ ùùSžÖŸOÃæ Žf5ò}Â÷Ž;àz3 Àç <…®ßÂ'¡ï·aâ@ýÈûŽšöÏþaaõ;þY\$猨ÿ…YÙ¿^ý õÌv çÖlX䯄ý×)ú‹ >0¹“žìÂÏ©S°r6,afUAƒ_ò´®€ü21â`2hÿ u ŸÄf;Ìeã:Ìý‚oG¿Ú„™Ÿ?†g¥Læeq¦§€¿%’‰ÜX¼ã+ýÈœ 2/¿‚#CÚû* F;õEÞÚÎx<@¥Ób:ÄÕþH£‡_“ínö¯ Õ¶qȇZd/ÔÛN§Bž:›q¥l:k„°Våìi©K¿‰Å4x/¹V¥{‰Z1¸ísÓÚ´i¬/M’6-ÄݰÚãd»®=¸N²ç²i­U`ròÓl uÒ¸Ö“ì¹6êæÆçM]ˆ¹ñ¹]”œŸw+¾%ðùç!̤ Ûn&øn6Ú_£|WÀCX¢põˆ±7!M¡õ€Ü@º~Vž´F°ø_ß(²™"²| ± 顦èJhîL¯Œˆ½Ñ >ʵŠà6Þqû¯SÄEÂá×2R· 9t~(XŽÆíÀ¯ ¢¤WÚÄÝÀƒ`0ð•Lgÿ{€t¾óý_ÍTtli˜>Í¡ó~IîP%Œ| €ÎtÔ3#Háa FÖ«â–îéàÇÚÕ]‚kÿ6¥º7ä¾U !¢¼Ž\ãµãM~›× 0Q#äGˆÛÜD¤ Ì©dU–s™ßÖÝUv Õ?2^¨²de54äVxº#F»úòÝ7 Öð8päkX[=Ù]s‡~“kkó·°¶BÜ-`ݶV>p$ô{ˆÓ¬Ëo²,N÷q÷¹}ܳîã»Sq-€»´nbÐ@™jÑúVi7.ZÈ~¬ ŒV[+xÝ%¨vZjD$æoÏíƒò!Pnø’ ¼;°Ž(<ÇÝ,Ö›Ô8ØgP´iuÊDõBð5Ösà‚~ðûBñ ðG2TNŒîì4) _S%…¨5ö¯û:véâ ŸðOw—îgƒ%ÅØõe›ü0#°VÿYߪbÍßÄe‹“—<ØÝO[ Æ—*yiÿÎ+GÐBJ¸ó#’ÑJäP}À¬VD­\õršKAÞ;\gߨ¥0ùsžB[(Wm*Ô ïì 2(Djt–¬ X¤j䛋wû‹•:•ë¤ ¯bš²—PÔWFG‹ƒŽ–“Gw!¥ÉÚ•Žvr“¯‰ö¢Ð#Œ28upp×\¢a°4ú6SHàÂXþ|*ßftnÈìpß^ÛÝ˪ð%;S\ͬ¥•ËÂôÚÑKá±^ËìA(÷¿UâÄ.{òšú’ùg;|¥éïLYS>ÐÈųˆ°`ã¹À‹£ô˜†áHÏašzm©&cgìeþ9×Lí§¡åÒg-œÒO˜ˆjÓ®!E,½óD”tö/W@3n”C3¼>ÁSr‚D#m¢a òúﮆ^‚xž8ÙB=Õ§Ü»ª*É2(¥—Hן GÐ÷4ÉO/`&i´»˜9M'ºî»·ÓMd0_¿ê{£Ÿ_‡è¸ÒÅ[Æõ²£µ8ÚIXÈŠ£Á•SvÅx²„ \à}ez…kÙ^ÍÀEv×y<¯îÔ‘ ˜ÇÁ¬|V‹,+“JtÎ¥U˜Ø‘†ž‡ña¼ü%¢ÙíØŠ“¡– oêh‘‹Ø;}J.é‰ÓŠD¦~í£pŠZç\Y¡±&úì^”üË~”Ü’ Q4ÈŸë’£mŸ.úU'…µÄÅÚÜ’qqJhbÓ7Y tÇï»îþ ÷ák=âºó ÄÊ𤫲úEiè̠Ђ^Œk(ÖÜ Û\[[øÕFÜ’á÷æW»équ³ý ÞiRöï†ög ß÷ àŒÍ£0Ž{:î~틹¶ô(¶]!1Ô?Ç*ÏhÆ£”Ÿf 8NT{ ý tù&µ ‘ÃÃP›ºEÒÔ'ü<‚–\ÊŒþ†^µ¨ÒÄŠ´ãÄe.“<%ðHï81ͯû°8Ï?“O¬» T(Ûê nX½¨¥«ÁR$²vCéíëuÇDP㣻§a«R‰á'¨´“ëÔ,÷UÜDsÊdWtöî7ŸàÜ@^ «¬SŸöo‚¤½cÔ,È$3“*8žè? kJõÉHO4Ð-ðfªùt•öTcÚjGÍ+S;±LÖXz7½¾ñ5î¹;¢Y+ ¯Ðá°@ž”µw&YW›Ë]ùRHYI¥ë{ËÈäŸ`U8úöÈÐFâÈ [YÀHõÓŠ¸›ù O»™¡±.ž¬eeS¾âŠ/k.ÚSæ²ÎRÄÐÞl]Šš5kü-fk61øß ™ýE]\endstream endobj 423 0 obj 3515 endobj 428 0 obj <> stream xœíK¯¤EPmˆ ú5A“¾óÕ뫪èBFð‘(x‘ŒŽ¹È F6F‰ˆ0Š •.ŒÿÀ¸aï†.ÔèÎ¥[]ªõ8§¾:Õõ}}ûyïÄÎ,¦O×뼫êœÓu/6íkZÿþ?09umóÈÓ“‹ѵ —º9Ÿ´åmóÄD)C?ûOLÜß<9 s5÷Ü .=âfañ3üwþ ¹}r궆5û&¸îÔÚî™FÈ=ÕìLn½âƒûO:Ó¸•ö¿4¹õÊòfÊx•v`l~]h–M„^ZEû¾ÁC¶k˜ à * ]¯]Yšèa¢Zß4}2ÌKUЛCW݈¾ÅƒÜ4S1ˆ®2 t6h;{)4*h ¤2®ÅØŠH½ÕCZ¢¯54(‡)$zfr·ûwq"­r~¬UνùJpçxœO“²B§äáö¼Áxý›ùà¼sˆ ë¬ûª‘Æ)×\úò䇜¾Ž Ç>~¼d6.k†o»r¶äep®Fí‰F›ÎyXï\ïˆ\nGŸgÍÏÀ•MíÒ4‘q_A»Šàã {èû¶|@äù$8ê-š²(±'Ð5Gðb˜ªË;K'?¦ü1€A#°j3ÐÉ<,#F‘ˆ6ÇÎwH#±*ælÆ©‡?æ àGµ&ñ⠙뎄q\é£aa].LñØü8!é Hh ¶p;à ``»ìçº-ÀŽÆÈÊ;Ã>ávnlŸÆÉEÓÅé/³Ÿ‚û» yßýÈxTh‹ÊD†Yר¸ôÓaÛª5Œ~Ý’AW»SÆ…”Q$e|¸Ýå•‘µ‰üÀ+a ùWþȧ> M›Sdk‘ï+Û.kQh›·G!|sfH¹4™èøNOäššbo‡ ",•±ÛcÎû¹˜ßqóQAK´ j"u*:¤â ‰*Ó ™ôjçÅÄÂ*š§A²™YKžÛ³ýæ¶qIšÖmÁ¼8ƒc çÕ«av;-`æ¼0Àñ(d‹ÎƒG]  ãèY,Þ”dÑ3GÀ![ž ¼fBüâ\ôðë¤Ó$œ¾ŠÃ3eÑÚ;Ÿg³÷‚@UœìW¡Õ¦…×¾T¸:f¡ÇOžó®žƒÄTõ‹ºàP“‘¥#4¿I9Ò~ˆ8†Ý™¹vØÿ&Çñ Ì)üüjÞÄõðï`ðØ<ßÃúËt¾!1·Óç.V¢—˜º£Î›µ‚nvèŠþ[Ûœ`…Àý)HÀào݃û«qœà:@Fe÷êҌԪf®T9ï׉Ný'’ŒJñLèëΚeúÈQ×é?œ_u¤‹C ±MB4g[bÔkQô¶ÉÎ+›3ådSxtI©ÿä‡ëFmpÛ3}Yx rKO÷ C¨ìR›)Kmé]º”Î" ië/®ûW›¡Ë 7ÇÖ©©( Q b˜@6c”²H’Ø à–€ /Õ¸2‡ "烮§øÃKÄù¾E¿ÐÌn#Òqê_Ž®ñm}»g¿»P®GA«Fú…T|ë 켊 ‘Ÿ,+Î+£îɤ8Ïy42{(9Æø¤Ž×Ç ÐÒP–3håp!Pá÷ô…Þë?©$ß^*æ|–¤Hîk¸…ðÃôÆhšÊÎíô P­rÁàɦÂÂÍ¡Öi2ËÈ}[w4*íÎWÜyq8øÿ¼ ’u#Ynù)Ö<† †ø/â¹Â =k‡Ô‘cgrÜ'ºI>£ß„QÏÔ6pìˆ7œ®8†$ÇUqUZͰeÎmdÙËãHnõVTδ’›ã…¼Ò¶{²°òÚ¦m8¨BL‚ Tw*A” Ý- E¹ão„ÆY½)WÒ!Iפ#½mrëxrÒd¡üÆåv.XçÚ46?{0ÂŽ tå«åMG¬Jwã ;{Nl3¨Èíè&¿p¢åR2ÇÛÖ”0Bì×Q…Q±þ@$ð7 ¡¬MÚ1£swhŒê"»®l^vcg„Yès÷ÒÛ™"ÎË _‚]*Í~x5½4Ç4u¯îxp,§‰ËtŸvÎ71X/¦óiªÙ[çØõ´+v•ÛkGÈëÅZ+„²2•! ¼X5‚µ{N Vï‰0£b!K­í—å-¬pb޽”#¯z”ZëÃü­ñ¹AwI .S€Á|`2Q|tq±Ž¤{§L8ZXâ'Àô¦&Úê'A1ü(yìü,v¯÷ͨã½à7ÀØp²XÕ'+;Ç•‘?ŸÄ¡Ïhþ6 Ž¡„Ø2rÏR„r½ˆ>‹[+„5ö3û-ïÞÐÞÁ]î>`DÁ ƒ#þ÷H¥ÇçàhàDà?yËucëó0U\æ‡ÐWJ2}šãЇÐ7‚9{f†xûw Î/‡Fû…ІHTzfñ¿‡ÈÈç`GƒÆ«°( ¡0:øY(N…;üƒ0¦z”¬ó½°NïÌïjAZ"W ñ6­ûm æ~¦4%*V\ì!d$IÀ}Øj`±ŸÂ¶„–W7×KŸâ}T“Ìæð0¸8ßä–ëZÆ«Ò+ \£¶¿Ší€úÈ ÀÅ{ZLiðév½ìne¼T¬ÝãÁÖÒÆ‚䘋Ì7Idz– ’ò4½ý¯6sºžÝE<Ýiâ6bO<"ëóž$.æ•êŠY™WÍ r‡Á–R¦¼ó“µ• õï/vˆjvPŠmOEGÎ%±’yi yŒ“èûjŽÅ ­žŒ-²•¬eÈÉcK»µ4¶“‰/HЈèiêz.½·ýl‰Q^³ü!ÙGM‰rg=+3wš‰êU(´Èšª-Ö+PZ´”‹tOÛž Z™!¶KtL˜eh‡Å9Š¢L>©¢8¾’g{ŠÓ%c~* ”¯Esh B›UâA2QïÔf­jc}1¹¨„¬€]°¡ôW-ŠL£¢|(Y+'_$fo1&TXÐx+ šúݺ~N±'Œ·çÙ♌ܜHöaýµD¦!½ð«<[‘ųk|˜á"†ÏIv!˜Ø4b¡-™j„)þV•E$ü¥ B…Õ¨V6çë’ó =u•Ÿ£+I6Ô‰Â×,§¦}r¶šâ-PóÇì·ªLjɦ6ƒô˜ ÐÔü¸©O¥v80øyK‘U¯mŒÏfr^$ÿÓb‚3e@Xîf~â0’*ÒiÝ ‰P¡Õ"f6l9š/ª—ˆPý«¸\SÊ!§Ž•™¢±ÄÙƒ0­nÅ[|M5ø–²’týƒïbZä3ºÃÆ,ª¾tÌÏÖ~@E•¯°\=^TDM¯%PyŽ ›ý“$a"x‘± ¾'S¢›@š?z:\ÌSHî„::Úy›ëºÄ’Šg@¯húEúŸ5aéjU}’ý(Sƒ²o®Y]ýäR¡æðU“ëè"çøÀY—¹d¡F½ã$QÙJAÀh)éòùzb+¤:PÃW21Ëõ#†aß?u{Œ:Íç/ föó¼{×Ëûʲÿˆ+«†Ê!‹ ª¹ü¦S¡œÕvTÁ6b´ÛKPW»ÝⲆ8Zb|T†#[uyW‹oÇjaL»îjƒžâcVmÀãl‡¬6ÀæG¢ŸMT„z‹vJÜÖu¼« „Œù´ûÀsçÉFÅ ¾÷po™“¨<\ƒ¸â ….ª!9S>à„¸ã†‹„ÝfjY¦Ò¡`¦™ö] ÁájŒÛª!X£°-Iéïjj¨ÐZ…)7¦ÏV×(+êЩ13+Vð|¸‘;ï<ßÎóí<ßÎómÍóùçiâE0¾HÙç g¾ ¤ZQš¤ýtn‰ï0®shØúg`A¬–ðÀx ùkâæÎï<ðÎï<ð6<ð:êdÞa˜e“¥–þÁ’^ž¥–á[+}ÒýÆpŒK-UÊïJ-Gjæ¸5[T~ìK-Q›ÿãRËíªM*í¯½uTúü®øñC:‡ÕÔíO%Ê7å4uúÉ"pwZ_™·í”ÙZ¬hCŽi¬–TI¥Ê Lžùº¼D¬Ï ®á‰ú°ÌÆ £ b_ÓãoÔù¹™d+MÒ×–+©aòª5bMsMøP3™Ù§MóR½”ü©¦Þº™tí*ñ•y=…ªyøÝü‰»²’©Ÿl~"Û¨H”ò”0ÄÚÅ,1ÉgŸèa”Âífág²ÈœQu|€+ë/¾“äé†â­¸2U]Ý[•9|Q€éº2µþ-#-àÀ½ÉTmRäÃŒ#i”.Åþ/¤®”ÛKê*Uùƒ>]¶–ÈKœ® sö/rÐÉÒA·š Sé|9”ÆÙþ†Ã—Fmf23Š— y¾z–OÎüõòNÁÒ‡0'ÙWênXWàÞ=ùûë^endstream endobj 429 0 obj 3859 endobj 440 0 obj <> stream xœíMG‘°á%@ÀC ˆÞ¦?¦§gbÇáBX¢ØÁÁÁ6Øáë€"„ àHˆ@€øÜrç’\ÈÏ 'tWWõtõë™·óöy÷müdÉÞÚî®®¯®®®*ÏÕJìÈJø?øïù˳{ªkU]|fvu¦Q©ÚV—á'Û)Q]šÓòŸýŒK³§fTWf€«zè£øÃµ‹‹ ?ã?ç/W§vg÷ÞWÉj÷ÂŒöû´ívÚJ×;¦Ú½<»ç5Ø}zÖ´•Ûi÷ÉÙ=7¨ª¹ðf¶Öaøµ0\WzŒê*Ì}½‡º¦’-€Ç³ü÷l^Ž[%ö”«—µt`>ŽÚ‰ˆ¸®r¢"6ê:® ?Ü$D‹ óU§ÑžÃdÞjÅHtàAê ÷:…t!™@˜îRùDïG2R¢ ›™)БûîsKxÁ¥Ö‡8"™EÐ7 :sÁ&,›)“ÎŒ‘E²ÜIL0iê3ëÜ,ÆÅЪÊBx,ÙdÏrˆÂ¥ €›pŸ¹ ”QpØÉd'w a›Ý©¥B,©+“„qÇpW0[UîaâÁÁ›¤¨[-îc[8!auˆÚ;öÂŒãÌÌNDµu Å’(>‰˜’Ð8*ƈozbÒðï´6î2¢“àiq¦ªÍŽª¬¶;ͽ…ã¨ÙìuÂ×–^_"…€U;·Zú Ú8WÑT×¾2»ðÁýϪ³;d œNîá(› Ì9Ð*«& Ôì’øg4˜j¢—~…ùú‚)w¢÷L-#mZ0åbÁçHAgê<Œúh¼åQÃþb7Á—ñ ÌM8FïcŽ ÆT—(×(YY¹õd“”«3!%å Ê­— ›¤Ü–<ÓËáÕÎÖÁËßÍD~7ž}&rrÄ+¼^Mvm¯×Z0\“^¯ãdM{½f¢îׇñ² ÐßÂã4O ¯³ctˆ­\«Œ÷=:¯µO‡ÕA ·£BM’:m¼ïŒbà c¶‹I½ó“‹Hºxïç?‘?&Ô¥|™¡xâÛô¹c›`³1Ãû‘õ3”`€^bd¼¶¥Ó¾ºfˆiÓ ½Üxrž²Š”ÁƒØ¤y~s˜†ˆé¿üV"½·³ÎÅLg¨CÔÙÌ4¸›+áCPͳ ÉcÀÄ·Õ‡™ˆ`ˆîÈs'îbÍîb<_xÂÈ–ä~½éhZšü?°}ÍN’ì|A)ý…ÍÊ3yN[Ⳅ"m ÒÁ ZSá#% á˨Í.•”xæÝ6L•Þ‹2ógE$k‡^Vüø—L<¯`”^7'™á@ÿU2ÃB…+îN%ƒô•ÅÉߋ܇Åúã±#)SŸ–ƒÜ „ÞòßÁú¢ø×Qº4œú2¯0FäÿíØØØ:Ë‹Î\¾ÔD' •2kȉ°ªãõ U ÓÐõü-tplcç—ð ‡%‘ ÉßJh±?G?…àjFY®þÄâPf³Hõmh s;\´Ę̂OÜ…°HŽ]ÜxÌVË_bIêVî)öKƒ‰wÅï±$%FÒC)†jn3¹«÷‚»«‡»ü¼^’² lÇØ°)'CnFÒÒ™£ÄÙ!2 ì¼øÛ,ÉÂä)±$ͺ³]·SWøþÙ~ó.ºúíÓÒ;ÑsåanzFUCkKY"à´sqIã¢Ãã®Ñf=9éãÕÈq)ÜáÚ.Ç›Cy¿”ÁÑä•s ÌŠ¤ö1˜ øLÚª §òpd¡ üI€-  ½! ÿ–+r½a6ñøY€HQaª¦ëéûacâùA°À.ß' ~}'*ßqð—¨cIÊ”+(³õÓbËçAG}–`äÞû•/À¸óõ=ŒRÄçɯqq ÿŒ(û"Þ">Š.)Lþg¶ï9´ºÍΞÅÕW‡D[á#ìq„‘®GÑ ùsȄ٪¯vþ˜qõ ¼VôŒÑ.ÌLÞÏçØÊgÑ}g»bðY†ùÖc¸"~ª¸Q|‰ÿ½õÐHžøE˜JP Å Ø?íŸƒÌøIžÛ#2ŸÃÕ8»,Lû ³?)Ï3Wø"N%T_Zbü%º\€ápë&Ù»£Ûá1ÂÏðuQ?D¾ÌQ1ô!ìÄÍŠ*ñi¤¦cKÕ¿Q"/ m®ƒ#¹þ3lŽq÷B‹»ÍÄZÕ GŽÌT¾-¹´hpHú!è £¥ci³TP§®3:ÇÕØnù­bSíÈ 7QQ_öPcï£'MUFXŒJJí-z¼I„ÜôÒª¨Ð½¬ciÜÆÒÕw†Î+Lœ¥¿Å¸åë çYC\î~á} &:ÎÐ…“æ¡yŠ9ÔN’Ñ ñ¬E°bÝÆö\é¼X§Jâ²µn¾P½aƒ”ÔžEžwGŠ£ ïCj†a/4Cîݼ[ †y^K¡l[±m)Ì0o[ ŸÜ¶Æ™¯Š–Â:¶Nþ¿7@ƒa^@L¿‡•‚WELÇŠ“‚ð.­¦ee¿)ý„Í‚ÌÓÄw__Ø6Hÿ«¡’;î¬A²þ0$Ýþ.ÎÖXgƒ¤¶µû»®%2u žt¸Û&6HX Dba?4ÌCjÆ ¾AQstvG¾=q=¢]l\“h³æ@«Ä¾z‡_EÓ iÏ/¦ ¬ntsÞ†ŸÀ¬5N›¦Ù¶Æm[ãv·­qK騶Æm[ã¶­qýÆÛÖ¸mk\IŽÛÖ¸i­qZ|ùmbkœ–Xwk\ÏñfµÆiŒî÷Ò çå•÷ØÅ¸ØG7¦¿M5¿nrÀýmþE–ÃN=êˆëaÄu½Ö¤ ÃóÚR€ÖÐÄd¯g³ÕÊ=Û–¦õµ4)¡l,-oXÛ›¦­ÓD¶íV“ÍcÛnõêh·RJmÛ­nŒv«ñd-K¢NlÅMÖZÙnØF-øÚ”F­)ÊšÐÄuT4µQß»[Ï‘šÖþutµýÞÜÓÛæ°iý>*Vâ7ƒó²ÂÞ»BÊy¼6—‡Ò¤lc*ZÇ“¥< }_÷V|àÞ¾i1­°‰Ø¶R,GæÉ¼˜ãj2I ëaÒŒdA6¼¸õŸŠ?Êå)kÌlobyE Û®»¼Òs|tË+É—~….ÁO“[FøAæáâkþÏÿ(y9/endstream endobj 441 0 obj 3100 endobj 446 0 obj <> stream xœíKœGLp`@ ¯$ö›‰C@šõ×ýu÷×-8į¼@Šl– ;rð+r¼Nv‡¡ BJDÈBˆ\¸¸@~B qAð¸rèGU]=ý};3ûð®­dOM¿ª«ªëÕÕ³VÕˬªÝüautä´ººrc´6jT]qÑV«þSkx]]I©ég×ãÚè™Ñ“Õõ‘Ÿ«:ý0|X¿bgaá3üwaµ:¶2:r´bÕÊå®;všÖ,ëª˲ZY=ðžÏ®\)]Ù•V.Žx¯y5æÜç@ÝZ04¿Ï7‹*@wøÖ¦ }ßï £*¦=¸ßÒ@×;}W'ú€Ÿ¨†Ö@g¯ûyI£T}Èwm«&€v ×Õ¸éEWê:k{;»î%4ú­2Þ"Š¡‘ºËAm'ðXÖÌTmøæn ÌØŽpðGÜàh„˜ðàGa‡,EÚÑ1Pùc¾7'@ ™ùqØC/dxÕ„y>áç•ÕXÔã%îáè {$l_‹‡ƒïó«¶•bÉTMœ*ì€Áà¸ù¡Á‰,,ùÁ˜r?@(Sa÷Â2ÔƒŸ„¡°Ð™éŒMwdQfšÌàAŠ3'3'͇á ¸¿4Ú²¯)-ðªQT>åAËî6¡^¤%%Î~8d(CË”)MºÛˆ˜`qâòÚ¢„záÐGO®ŒNÙ¿µ‘0Òê±ZZõæ>ʆ[Åcušà;E ·ìŒ“¿‰VÛY…XIaœ Ú ¿ªÖ/.ÎÊ«æØ öáë¨%“qI³ÿVÖu>ÛÆ8Ö–q€†>&›Ã·z¸Pe ¡‡Éæðm;‰ZF?Ìfv´û§Õ|¹ñfçËþÄÙCa9ï„fä…è+¾]U¡õ ¯¢’ø Òú$ª#+¢þª‡Û À3ä¤ÿæEY¦ëfQBÜ¢˜™ÿ/ô#`Ýó ýBëw}+Òx&&¡À"fAºGb–x¶·2VP\ /b…E¬p›Ä ­6UĽ &“¸§¹åWÑísåæv„‘(qÝrº¸”Zgd(¶Å\9±ì7PöÙj´ô~´ÎGï”3¯ó|qì½Í¡óE´A¼@êè,ƒ@cƒ•÷¤Ï+U 9œÝèóJÅÄVû¼ÝŽw“ÏË*Y‡_Ù ôø—WYéþÏKÿÉ‹‰màÛÐ{¬“ÑGŽJDWØß5"}ZÜÿïüT†¬„C#úwdCÉBmÀï=ÔâToÃŽ°ó#7ï ËÚÜ"eãÏD6^Å¡CÙØÎ<Ýó^ö˜oë,¿0lá¹_\xî·•ç>s–ßö‡/%çr­@Â<åOåϘ,‰¯p[T´×âFú/òã%v4ôÁúPO²éPßQÓÎÄYŠáI¡¦+n¡ä a¥Îe4&? ”063Z`2©4$Þt—ûá¹Â4ɪÔμ%9y©ÌÞÎÉ‹FîâøDp³åñI·ãÝŸ¸œ|8SåäÑú‹b—u‡æ˜†íW$¹t‹:¤åäÅLÃI~<sƒ°nÚÆ~Ütà0M¨£(ܵ¯àDÔd,`6U‘_«s.©ÿ êiPÌ DGöiàú–¹¢("ï ‘í•è7„… cSþx_,pHR'ƒ[´z;«4Á¥ËM ‰XMá¶¶ÊC—ÖCo¤Qôw‘XhôXÏG‰‰Ýïˆå“ƺÇ@­›žZ.€ä½¾M¶V _Cj€l¢k àt¾däÛT˜GN\T`®U¢T¯ƒ¾…?ï÷Œ‚þ<žmXç¹ÈÓÄ3rNÉØ¸þ˺écˆ¯ ?6`ó¦ž9ysORZÑæ²üÒ‚G¢“¦uiêí¿„»eᘩ‡”Ó töxIӪĆa›é3 Ÿ‰º3ômÅ8bųl‹&MÒ'…ŒHkЧ°´ª"Ûë¹Ö=€"AHé?$ù=ÝY×€ î8UãmQt– öa@XB¥¯ñ<[’ÐÒR•IÙPú&Ž,Ñd(½ò ÞÞ÷fx ûó2º|jhΦôØÌdãˆxÆsZT?CÖ®Í,W˜jú4áX4.ÿ–>UÛ/cÎð—ž7:bQ(¤È$.9\ÎA-€Î-6^Ygr†Bß’n¤œyÞvºÄlQͪxqwÉ3·+Ó¾à`V+Ìû÷X†±¶|¸Ž”ÕË !ç¯W*³…¼iöxA Wb'|¹Ô[žðív¼§¾Øœ§tCsº/á¿)¼ù¬.¯ùNeu#/þ‰ªVz™(V\7@¾ CÆæh1Â}†¦…îU…‡^$ µò. cK‰Rç{è? K0v›J(öíö„b¤G¡H%/—¡Õå:V•`EDÎÒ”´])ÀÖeX›ŠiµÌ½X:9^´†”J— ¤Dš†ˆo¼®™I²¬Û|!5©Qáö]¯“¬f=xñ‰~•@¥|^vëìD0€>EÓµS)¦‰õ{(ÃëxvRÊËÈ?žÅŠƒ›è²2’y«SZäè®b‹Ù¶ëÿ[˜úd*ÞhѼ¢ŸŒw¢˜s¸Ð;ËBñ»7Í\¨»Ð|çR–Œ;jTÂO½´ø ½‡ÔI­iÕÃp &1Œ¾rorhžŸQ V&üÍO®£#ݶ±³Ž·Hr ç»3\ù~eç.‡ÖâøXW0´þÊS ×7a˃®6ÍöDßÓx²Cõ$Se;K±(MA > P=Ðòº“RÌd ? c·!S›ýꌻžcsH)é%ëfuüå_}ûËÌúãÇÂïÙai0“ÏJväyÇ`.gz7?T%2N÷³r ?‘%¬†¯ bƆÜ(/=w7¥”=å[ƒ|›/e2ýÅX(«QyçRNF×)߸DC@Ò(“‘æŽu¢oMø:X%W£(–¢dòÓb®PàbNd²ºoZ-){¬- dò€kƒ\¬ÌÌiOÂl›/"·ìÂnËN_®¢ :boâTp•б ¿º(Œ‰raeYü¦ºý¸9b16x‰—]aäö£ðÅŸ±Ûš%5NÙõ¢¢Cù*Ò几Ï\,Pà¾åñRýƒUÞ†G–¹@V̨~¶#çvƒ<®¢ÅºC3›ÉD59ï ÒÍJÉ„orw›Âa&RpAÓ³j`>dr‰k:ç÷¡dȼ·Ó¼-9äC–"Û[7>tñ¹sv5†ËS] |P 3D /½†.†” ¿:µµU‹*€EÀ¢ à⢠`Q€ÿ¯*øùãS£ÿU¾uendstream endobj 447 0 obj 3475 endobj 455 0 obj <> stream xœíÛ²\EU¢D-¼ ÑýTͰûÞûHÄ» &Z`$IÅœh胠–ŠZeQŃ¥O>êÏPåðèØ—µz÷êé½çrfNæ„©ópfMßÖýÖ{Ïí¦±¦õðÿÊáäÉ‹R™æÚk“Û¡Û†KÓ†O¦ãmss¢”¥ŸýŒ›“ë“—š[“°WsñYøpçšÛ…ÅÏðïÊaóôÁäɧÖ\à¹SÿA˜nf!gª98œ<ö‘ÇnL´mÜI?_ûªf*Û^ð iø"LŽÐC’n®ÃÃÇSM£Y¶•H[E ,>ƒÄ-îñ8Ktá\X¬@(„:©—N ü,…ƒÎ‘…µ9EefÉNžÅ¡8s²s6üe°!O×V;ñ‰ÚQ¯Uå+tâ6÷/)sNƒe¥<-E,s¡ˆœ~7˜…àpâ#újPCƒrØB¢&Ï»¿ÛÙ)çÇZåÜ›ÿ¨wŽÇù4) LJnæ ÆëßÜçíœClŒ6Ö-–Ö)¿nî¼:¹ú„ÓWËq@{ìã×ÉKfë²áø­•ån‹qPVÁ*ã†p˜Žßê¶Üm1²s«00„Ãü0|«WÇÛvnUÀ†p˜Žß2¾:̰º,``‡ùáø-Ÿ—FÝ‚­R.ÖkÝÇ>ØÒ@àìwj¢å4àW\¨HVi´óJÝð°É­?X]Ì4:!T'S’€ðj¹B×ͤC_ÏD@?:'Œ ¥Û{˜„¢ÂGž"KEß.=ÊTõ„rkI!£ß*PÚ¹¬À˜.øUÔ:é“6£¸æ41 ÒÙåÒð¼!ÐÅiä9’*d"wÆS&­ð7 ÓšÚ4¾`Óø-’«|›p>,e­c}¿ø ~𛉻 ÙÈÖ`£s§N͹€,ó{;Ž22àÂêÎ a\7qôE r‚—0ßP‘]ß°iü!ð‡8Nú=œÜ’s#ôv€LS_A!Á¹?†¥Lgk1ö^‚sÚþÞâÂ@Odþ?0»€}ÿ ø̱Aº/€&k.k¦/_gYŒÎ— ìq7¤÷O„úw ÞüˆäÒ€8ƒ.—"ËYç¶æü„ºŸu­Ï*gx¦53Ôæ|pÎmïÒÛ„Óy”P€~†äEð°æþ¢ þ˜ö-B„žAõên†sjÂa6þ*ˆÀCÂô:$Jt¾íÓ0 à×cn˜dÊ ‘P‘žM´Î““¾Vá?Id'9ƒ‹(¿Œ.)‹NäÙ ÃuøÚFÎy•Ö3‰Fø(!ξ†–ñZÎ t~@ë"Ö?GÝ)±ôZ¹i±È$–ÿ"Ep2`L¦-¢¹†p¦-~ƒgehó^K Wf'çX±´BÞ–²Z&çö̼âXWí“=pæÎb]DåhŒ. ñ>Lû¬„„x‹UMxh¥VÉlÛÀ¾¯zÐ8?e"f(6]›½6·©(L} (?3§ñe-8›j^Ì«cJ‰p‹GmX%ñKà<¢» è$(”U§`£Z¤2³È‹KBiÆ/ YÊ‹¾‚ixõUˆhS>¢GT>´¯øð+䯊õ|œò09‚‡’™å)ɉ×=¸éúF¸ÚÁ6ZÛ™ ùÆr •.r°è/Õ:U¤pÈ»¥;‡ ·µ›©Y¤ä.aÒ¦uÄx2 Ì¢€h¯Ë—릥ââIÒ…¯AÝE Çví+*V×zys:ÂÿkpÇR;³Ñ|ØÏ ›S"ÃQ¾C5â?’ò§Ú´(kÁŒúyµËˆ–_:IÈíëžgkŠ‹Qê½4Ä{Ýk8‚w¶åÛÈ ü\ý!–JWƒ^ÇÅ—o—éŠü"ÛTp#Nxq^äßhiÁløÅ —Ìv;÷ÒÀI)-âà›ƒ`ô­x Ÿ+4ºI´žS¦0!à½õ{ªLéß·x›c˜,Qð ˜_N@!s½#º_¸HìX•BOÓ*+—ËÙWV’ÊŠfô‰<cÆœ¤m@5^OH³š«ÏäùŸˆÜr¢¸üÆŠݾ0`ð]) ý¯8ŒÛv?Rm¸ý¦Ã¾,Ü:g÷eášAhŲÐð“^Z¿š´¹ºÐH¾ù²Ð¶ìïJU?ñýüäÿ ÝÀendstream endobj 456 0 obj 2970 endobj 461 0 obj <> stream xœíÛŽG¬ñ€!²KZˆƒ4㮪®®ª7ì°$ÂÅYP6È`cGÁkX'€ å ñ€P”"ñG<ñ%滪Jðã亚Š^t¥ÑY7@Ûþ=ß(¡Ñ“ʸBC+"õ )gðTÖÌT*Ca¡2Óã06¥È¢Ì4™ ÀmPŠ3'3'ÍOÀp«4ÚŠO”– xÕ¨*Ÿ÷ ·J¸yI™³;ƒ åq(b™ E¤ôÛ¾Àô ‹ÐW…ê•CgÝÝ›|×þ;œ4FZ;VKkÞÜO)¸5<Ö¦5MøNÑÂÍ܆qú7÷ÃZ;k+¥µ¶ƒm·J[ÝûÉäÖ—­¾êØÐ:ìÃãh%“qIsxjš|¶Å8´ªn‹8@CóÍá)ããq «Ã¨:ÅcCóÍð´Íg[ŒƒÐ‚q€†>æ›á©ƒ‘eÂó> æZÃÃùõÑãŽp´²‰JµíLyGû ¸Ç©;ýV€ß$æ÷Yç ­¿óî§–è¥ßñGƒYü¶‹vþñž¯—Åe¾&ÖØþŒí¯‡öüa“¢mMÀïDSÀl…EKûGIfÿ:N~Ï[kƒöõEZ^¹ÚßæIåáÐ]Yæàï{XUþ€xº¿ãdà~˜…¹þè›uÕÓ÷2ö†¥ÂÒvn@å: VþvÓâÒ-lÝÀ/ÂØÐùOК mPŠ{€ |ª;œâš?òkv¶{9ÞÙ€Ïôw—)vÝ) s?0€x›•a­øÕ*¯”¨í_§ WCXä«@PÀðI`)¢ðŠáAØU52ò„^(üâÜÈç§q.ý422¸õ« PŒW}ȤqªÁ»¸°¥A¡õåS&¬ Æ`¦óÐÊοæ[N§a;Ã}IÄÇ1‡þ{*“•®´Þ> 8¢ù¸ IcÄ|ÞÙ•átpïQˆ‘’øCÉ9 AiÄ™dš•ˆt*‹0/­š'YlFÃ\¢Bò)Ë­˜†µ”3¥X³!\£‘§&ÊHV5áöÜ*ÄÅY€‰ØìåÍcsÌæÑê„”¬sDSÄ—jÕHä.’…ê(_‚±Ý¦ŽŸË"•†¶SÑXA2¹ÎF¤¸eÍD.É›!¤õÌhùg­¿µ¶¯5 ,`)éѽb*üsm“¨Vsýq›ŽÌ>«˜jÆõÌÆµÚ€ã.¥ h ;1n§ˆöªÌÚ"Æl"ÀºœåÀ,4®†¡|ã }»úÚAYkxsÿe"e£Ã0•…Z–b=Z $œz\fí¿x Îщ‰ÆîcùåôXr”ñaËŽ(:ìøôÏ(±bpø4qëw¡s²Pt݇AÅÌ&ê•ðOé,îˆt>K£¸ˆÝÂU2\óÈ+J©ìˆ5VÀ†bÖ´ð ¡ÝÆf De/÷Ï“%å:éRÓŠ,#XWjŒ> U•ÇÄqÊgQ75&h(uè;×,’o”³Vú\j¾é÷ƒjTä@Žÿ„Ÿ:‰ßãQI»¿Òà¾?ù@e*Oæ¿æÃjE¿Ð2x“3+Á ä@ŸBBñ9Ü<î8° "$t±¥ŠÓ²üZtéUá êsû·ßó ×ã]pEÿ3A¾µÎ^ϯ°ç¯n¸¦®—ÙñcFËŸdä/m0ªÿI€KïeGFoEéW¡¦ÂX¯Å룺 Ï[¢äÙUæ5^ìòß„H?´ÄQJ§}™óEÓ“€ë퉙Úì¶Fxt1ðƒÞ+=dÒ{Î —zA*÷OäíºìŃ«Ò‹ÙZwx8ÚH¹/Ï•0öŒÕóÇ(Kmµ^è =õ’—¯†Ò§€¹RTÛq «¢ª‰o.€²Ä—"„´ËÑÕÀÁö¨ã¸{5Þ?uNö—°9Q¸åÈ9%@ J%ÊÕ^Qo³ÀØ ¡‚½)Ñ2Þµ‰.äWÞˆpnAŒÕ~íéèדWQVZ°­Ï÷Iš8dëNŒÓù剓ӥ$©øWfrJ£ý[øºÛ+:óƒ‹ÞhJ#ÖfÌ+gmæQO%àšòF[#«U²‹›8yÆÎ|m4®™]â4(©»Õ4¦Ã¥—ÔÖ°Jsz„„ÌÊßQ'±^Ï•–pm…†åúCI:ü‹10Y\>d7 7¥zò…!µ)^6ZöMÍ A«˜]—e|\Õµg¼Ž§ècjò¢êÜ=Ÿ/”¿~jz¾‡ù62K%KùS!Å V¹ ²óe®õ|EÚ%±[ÀŽ¥ 2«Zç2£ç’Ò!'cñ°OŒè7*ô"Ù:aažªrÌÆ‚¿g¼vâMê›k)­K[«¨S•ðùlÜ dʜ盌‰3|3ÐåÜG}3°£xs3fSô{«=ßë>ûåþý^Õvendstream endobj 462 0 obj 3305 endobj 468 0 obj <> stream xœíÛŽG5 °†!!‡„6âÁ ͺ«ºº.oÄÄY(vØàXÁk²$$âOyÜ~Ù®ÂÂgøo{§9¿5;÷dÚ­ë3Üwî>tÊlꦛ}³µ3;{ß¶^™IÝØ¶^š½ßƒ¼™s>à@­,†ßç‡E ÷ûÑ® Ï~ÀAF6L{pýGOùGY\èƒ~¡F?T®Þòë’Á^ôaÿ¨jº~Ä\7ónÝ^¯ð°@ÛÕÛ~°‡AO*ã Q £ˆÔG¤:€<ƒç}ËL£Â7ÎÌí ÜÁÎFˆ >²iÇÇÀåOø§‘9b(̇€†QÈð¦ ë<ì×훹hÁ÷HÃ'áá=â aŸµx8øS~WÕH–,ÕÅ¥ &‰¯Mðx”èÂi?¹¡<êT ^Xzð306:MVzæ¦Y”™&+ø((Å™“•“á'À†Ü(ͶâëJ[¼ZT•ÏzЊ[%Ü‹¼¤ÌÙË SyœŠX¦BéRúí³Àô ‹¯è«B õÊ¡3‰^Úš=cÿí΄é­k{ëÞÜǾãÖñXŸ&Dø‡¢‡ÛtãôoáƒõvÖ!6Ò>o' m•_6·¿?»þE«¯šã€t؇¯£—Læ%Ãð­ÌW[Žƒ`-/âc8,÷zu˜ ˜{`` ‡Åáðm¿@Q€þšc0R÷$ìÍ‚²=åYgµ‰uÃl»̾òÄÙ_‚½Ûd-»•L0Qˆ÷› +H×2÷"H°M¦:4Z_F4’} âü;´Ó0º¬‰ã¾·á¦3—Ùl™lŒ–ù4ÙèQŸnس,ó5çâÜ^ج«Mˆ¤¢tÖdóv¥U=ADžþ>-yB´B2‘_@Ö«ˆ[ª&9ËLì+0œò÷ •„µh»~±¼ÑLw¶ÚÛ ï£,",ÕÓ‘•ˆêí¸n¢8q—Ùãgý-Q~A1k:Ð"±{Q˜ÎŽÂ7"œ[cã"u(ˆWâ'¸z,\µ°Ýåæ<µ†£Ó»Eläœ8éBHÁÁõ9ÕXÂU·´ƒvvèÚÑ[ö1æÿÙ}åâä>Ày .ùªáaÁÇà%aµ ú€7j8¼ý…[²¸øäÈᙬQ·hÜâ•E… ]ú°• DüÄo4ÐLƒ‘ÓÅ}áÑ¿ú©mœJ#lÒeéb˜FãmqÝðh®¹Ïìû£º%s5l›ñ°Eb[ÓV%MÈ‘ ä¬[V¨2i!çD4Cj§ˆÃ÷¬¡­j°ÖöËŽdêC“JVÅ1Òž'¬MSSKÜ”HÉŠâ|Ê;Q5ÂK1ErDg È}Ûà nÆÝC’|ˆ»ÿõ!Ir¦4Ÿ·À‡\ÿ(ίD`ÃýÎs\ÒT‹a¹Qd§€h| ®‘â3KÒ€O€§Q;j>™®l È›¢%”ÓŠz­¾f~ô$@K}(S!"Hó­Ñn Ê5ªvà!!Uýªá™äÊL¹^öItÛž¤Sçdöþü/*¯Õ8UUªËÙ0ñdC¾iº®·F⯳)iÔ+¨ë(^»ºê1W÷ßö‚Ü'*ÓD%]ô”«Ò0Ÿ‹´ÇBÒl/9fUw ¸”¸Å+À¤ÛQ…ÀáT,]ß$q€örliíÀ¬¿â-Ã*†>ñ«V €_=Äáo³!@(¦¯{ÈЩO†ÿà‡‘âŸ;Èõ|XI!sþ‹ƒ¼¥´¿ÔeoÿHÁ )4-µƒ‡IÉJtFÿöƒ]L‰¼{‰diîÃÒ¥ôdOSqyºÌiVX:Ï®½ à±Ï^¢Q¼ îYº¾<¡drSí5Oh•Çiüô# ˜ã@ˆ7Ajmá³Yþli®(^Õi"æbÆâó”ÃkOR smžly= ˆ§ò«AN8øx4æ5ü ‰Ã—Átüg¦—AGS!çiTƒUÀ"]~¯„C1[Èׯ„Ëúgc"¸ÃLÏY°ãaó5$`²HˆÜb \xzûr·%ž ަbŠqu~}ü„&$ ʹŒbØCï5…”Ž2Å',KC¾Yîùö@ڬƵ2f϶Ã9-ðúS"€ÿ…Qtˆwˆl|Ï/§ÝmY¶ æJ=®¥!£^$sI&À¿x¼Ñòò‡Ë—S¾$$€Ñ߃i‚þ^<ûvÐ/DZYsÃ*©‰Mý(Ë8¯ñVöDÕYå~qBÂ3†»Eó©óïxÚ¥„m}J¿ªXÃlÿá}¹¤Õ…ž·âYIs¥ä&•€Î,ƒèk½Zin¼!œjH:j³4x¬ù3Ôè¸Ó>£“¾j¶½”1Ó”j#ŸÔ:Ÿ½R•L´–äɳ´ë*§š] yQ=Õ2—³›ñðÕÑœe1É»î‚ Qa»RÚI:·!ÌážåNÍð…oµµ‘DÚ/ë4ŒÝ;&qî|íçÆÅtâ70è_Êwï\A?yý:H«'m³×*ˆ6䨚Xt+§´ëñJÖJˆ_’VVãÛ®T©Qù-eR#ú„Ô²dÖx}8Wš9ç-oLñ²zà—šràX®CêFMWõ çc­”Ëå »gëðYׯ ‡óÀ꬛œÊ¬vøb_vzKlëÞgTÅëßœuÌ-¨>–u%Á5¤pb]IØúëJÅ÷P] ý…XÝJ’k‚ïdoÿN<­+ÉØïú+sÌäx!I éü㮤ßFË>},J¼AVsÑ©>ûÀÂ7ÈÂÓ,/Õ¶Ü{‚}·)½š–ª´Ë4k§ý¼FÑy_ ØßÂ1¸{X  D“¨Ù;`B铤•6—×Ìë&G‡ÖHߪ®ZA;©uVKfއUë\"©“Zç~ŠÔ‡Xëm¿ÎZçþšÍOÊŸû,ª<™^ºc9ÑçùÝéCmö¾\Є*©{H¥E-úNòHsn1Ö3YñŒf?WÎÑâ‹Ö„ÅþhPHðñÍä!N§”{+nM:+ÎwÐm§;{Kù<™¿ Ž&ÿyP.š–ŠNåpñ€:¸÷¤je|˯½”Ʀ”LGÞ)0üIM¾¥ä[5qϹêˆk÷·ò§»ƒê“(Ùïû3e!Gýµ놅töâ$Ãb.ñ¼–¤¤žv±«zDAŠF.i[øòdþZ„ïJË“ÎÄKë<_µÜ8§ärj>4víf•𱿗É)RCâõ¥G7 li3¨öòXtñÓÚì{Åy&ÙG‡¨|î§[¥¢{©ðÄ*O¬2oñ8"v&¬âë,Å_Zk/Ž»cÏ'T1Cß(’}€o¸Ú™Ö:ΖL]{ “¤¿%å²’ðºÜˆ(ö½$4hø·£,¸µ©"opÈ ´yšùB_UÚܧMÕ˜Û¬`KªPI›RÔTC5u1Cr"0†Æ+´Ù/V­ú†f%eÅU2Gò*Þ\¸L ci„Z“> stream xœíIÏG Ø0 –`ÇqL[âæsWU×vóÇ !˜˜„;1þLì‰Ä’ >¶¡pA,¿€ w8 $þ¸qAÔò^M¿êêžéoæÛë“ìyS]U¯Þ^~[U½ÆªÚÿÁÿW6&'/6ZU×^œÜšUW¼ÑÕFø¤-¯«) ý쟸1ynòtusÖª.ž‡·¯¹UXü ÿ]٨άONž®Xµ~u‚ûNý¡íš©D³&«õÉCoúðúõ‰2•ÛiýÙÉCo ¯¦<€vÕ•b­¥DZ*ž€Áä#xø¡É3<î'²p4L–À”B™Š§oCx ¦ÂFGÉJÂÜö‰ÊÌ•¼‡âÌÉÊ­áã C,Ív쥭"^5ŠÊèØ­[ÔK´¤Ä9šA¦ò4±l3E´ÏG&8œø€¼j”Ð &ãè¹õÉ“îïÖ¤±ÒÙ±Z:óæ?JÁáq6­i2 <”,ÜšW/ÎÚ9ƒèäEzÖ'üªºý¥ÉÕ8y5”Ç>~¬dk^k8~«ê|µù8Õ¨"0ЇCw8~+ùxãe` ‡îpüVtq@o3Âɰº^Ó•6Æ}ö^æJ0˜uƒÂù`"Ñ~þ/Ž¢ü]Q8ÿ ¨q:ÂÁ¾PPt6À¯‚ê(ØHg“+’unÌ@šïCÏTËhª$Üa-nÑ?&æ÷0Uü¨Ë QÏ܈ЩNÁʶWvúÁßbâJùùuʌׂ!œ™ö‡Q]ñh¬®†M%ë¡uÚê`ÁÄrJYçn<Ú‡%0 yx<†Ù-ÉÒ‘Öd† §;!áÄ!X˜ÚHÞŠ…œD3Õ¿K+jšg{°‹çîŒêHäMÜ6_Ñ]À†DG–µqNñÁ¯ ÊTd­YSZ€$¤—S1‘IL6‘[²ˆŒÒ)‚ÑÔ¿¨kMx{^0'ݵ­Ùêï «X  *ᮽ8ñ_§O—òvÖ½¢Íð¢ûÈ!·Ð´%Œ¤&§“{†\Bã‚ øEÈ[A’ÀN¬š7ØUZ/!fÀ†&‡ޝMFŒ.©ó¯¬ã.©'ÏÄü”#‡c23z†,¦®æÊ‡“c>ÂIòa !QB@—æx¯± ™_cÛ.4Fû¦„EÛl¬M_FÈy¢‚ã¶Qtÿ¶¹o›åê ÷½)“ÎØXÙ¿“"aÞ¥ˆ”Wxûa*ÂNÚbüÜ…*‡“Z'ŠëP[tqa§¶•– æDnvâR!–~Ç—¢ä¥MWv›2áÎ*À™4h¤ ÓAXW~,,à6³q…Ÿw9‚ßåÇÙ×J³QÁŸ@¡†ÑG…Gü—‚aé½ $Þ…Ä6†üa[à‘îw¯ZÓAŸ²ƒ¨|6…Ð r H%£5~=ŸŒ”ÿ4h0€Ÿ!,ýZ &ríiñȆ§`1h‡ÀÉQó’ÀOf‹_³1.ú^eÙ9|™&žã A€ìð^2H¼Ì—â1g —g Àƒ~‘Ú–­ª–¯‚JUæÐ |iœ Àñ à_£ø:ѶsÉUÄãü MIÍì2}¶f‘–çãZn¯¨«¡AÈGtÀ×Ó°Ì~6‹[? P|ôˉîb†H“¤ïŸ¯:â,À“ý(_t'™vz1 θ3p*ßò“óŠ %b³Èƒžº›Ú¼¡‰Á»’T¤Ãh° Eûˆß6ùÉɵ* hlÝw9Ž**Ô´‰Ìª’s)ùÒœH4JÈ/FÑŠvò•/½]x·Kb»º$òœÁð÷áü,¥eNy Ãþ5ÕýzKÈ»³„ÎD1rÁß¡Ï&I±$ˆÚ̦øæ>B÷ò,Ë¢æâPz° ýwz¦Ãˆ"!ûTaˆÜ Ò'€Øh Ýl›6çԻʩ¯ žÊŽ,$'´ M¬ùpn¦úo1²­ &éB¹,°¨÷¢æ™^¡Iݾ¿bSfžäxÉÎ'Éîí¿f_Ž)…ùbO{V”C_¦í+Ë<)ÑÍ„uÖD§Ú»~'”ìv&ØtéåÞîãjÜ”Ñ@k?‹|ª?Yw(fZÆànùiÿ”Ÿteí·âÓ”óšW¶qo{P6¦/ÊŒi_»Þd¸ºKÖÝZRVæàJ5©`ÿÍ…\‡_¢¿!sgÞjsQ¹~äZKO×½ŒVd¹ä2„»‹¼sÅq¯¿àb„¶)§þ"JÖZý;SáŠÃën¯#Z šwh†/d8 è½Fq«I&η‰0p¤\$l¸Þ9 e¡øº#7šÂ°¨ÞïY~O+»‘f×&ë9À•ÚߊTë´YÆbÞbY:Ä$á/Þª‚r´fkzù%©[+‡9»ƒ–.Òþª|Úö> stream xœí\K\G&ÆÐD˜Gx؉ÃEbzR¯ûÚÅ6c'¼ fâÅŽ€1vd<&3AÀÂ$ C$GB!„²È_`—%+ı`ÅÏ`ÁŽºUß©[§ºnÏ«§Ýf1}º^çUç|§ªf¶ ±* Ñýà÷ÆæèÉK¦ÑÅWF[#]‰B™ºØtŸêV‰âÖ¨,þ¹ëqkôÒè¹âöÈÍU\º€Û7ì,ÒƯÍâìúèÉ3…,Ö¯hÝq÷A×íjSh³Z뛣'Þóùõ›£ª)ìJë×FO<äHUŒ•#udS[Ò7¿×5›ÂSïs­ºð}ßßQmUÈÆ‘+Y¶èzÜu•a¢¸‰Z?8…ºrÛÍËË Ô‡\׺О|¸#USŒõ »e³‡ÎlW¶]c‰F'ªT5±è[‰©wT­A9K!Û¢ößœ€fÆvDG¤£5&JG~ʘéN^Ës½I9ž’dÌC†AªU…öó<âæ-‹±½áK’áèì©Ov”±}-ý)·j]T2šJ‡©¼ƒ?MÂOÜóq’ùÂ)7¸„QE>å¥7Ö Ž| C±Ð)6ÓiŒ%²,ˆÍò$‡ó¬ØÌQóãØC Wr£­ùtn)Ï— WùŒ#­¹ëH{A—\9+Øl¨ C‰ËØ(:–ßö…Ò½,OjŠ¿Öä¡Î9šÄ¢kë£oØŸ­‘iKÇDiÃ[÷±ÔÊÓŒI×)D¸ÕnÃtþ7ñÁF;‹Z–•l£¨(ªbû{£ë_°þÚ(j¨:îý×!JFã¢fÿm¥ÒÙv桬+“å CÖA‘À ÿ$ìçþ ãëG„3ëÈÖ¶/ÈàñfEÍÿàóÔ[-gc¬•Õ˜„>= ç`Ób„qYʪ~I.ηGÒ\Ç>ï2»¯fZ­ËÖ„B„è½Õ#m»jŠÚÖ#Ò©Óp<Z8òSÜ! ==Æeä±}4ÀÒ~*'ik·Vµ‹ÆÖ¹&‚ñ~J0iC/q˜©ìžå{}(ÅZ†%Oe¶YÅŸÈVVMû%·i­c4~C|ÙÑuò+´OA_tͤûW1XôC¥ ™¾æH)H×oÀá[ß|Ñb ú§~4MM½ýØKÁN’ "÷aƬ¶EUK$ÊoRðF*\±Â;˳Èo¾õ24ƒ<ùœk´UIéÿ-aÏÑ{iþ˜E+ýÒu&ÿ¾Êú¾®Ðõ%ÌKC_S"bYòÎeèümxÈïPXñŸ…£AÞB>G5|ʼn\RôûfÇl$"-v•/ö¤’Y.F;ää?-ßåk¿Ö¨ªÔ»‡QÊú>è?9i{_:çúSD½ {ªÎ@x°—o#“¯”‘fÇ–y“µ€O¼VÏ‚-y¾Â\ü\VHz+¶]ªªWJ¢BÖMÑi¢Ž›b8Ñ¥®Cd•a÷Ò¶è°NY-/B1 ½·XD„bêFÌ¡ô!”ƒ#”¦ä¯TWÜÁžãR®éP?ODþkp‚ƒc£(‹>(Øgò2{•xfÐÇ*©óuï!¯zj)Îeqö"á…ª7{ ÿàZu€/ ÷·vC:¼7aíd ³‹?™%˜4Bîþ”Ø4K&¥²Áx^h²²Î…~ž¤ôÎqçÚ†}E?:ùghVD ©9Ï=€ÌL$ ;LnÈ&»°oË…ççôéùo8“ÝB3fz“‚6è-6×9¦äÌÝ‚n&Ì+†­5•Eލ7v}çg|ækæåZ¸ƒÛ£*ãÚƒ[eðe•j'2|F+£gUŸTÞ%¥†®ô†ê²žùah/ñ»«ÔðÞÈé; *Âço'èµDöå… ÜžºÈ2eÂ…^a\ F=xé -fº_ǦA—YVºŒ¹0õól7¼N 1.§¾Ò°A óȵO¥¹')Î-νõ¹Ölók=½Ü^ƒXè}/Qøaž«Fìþ4XÖzðÇ¢"øîê~^¾µÞcdÏY?wî•BC2¿LÞR©A1Lx§üOJNS2™á&ÛmOÓ:ìýG²N/OšhU¹ÓË ƒ~úѱ„¼¼™02ä»Ë$ Òƒ[A¦Á‰ƒI³(ßnZ0ùŸdÓfßu"×ÌAâº%uÜî–yÚ~15wåÀ*QhÜLâÇSç<+áÛ`ñøO8Üqsk¡æVÎhU…rƽڰ 1ù&ÑSVe~ôžNãsPpúËœ y>Uò·k*Y5}‰röÏ:v®VdĦäuAò¤¬*œXáµÎ+Ì~¹ú£M_÷V9Zq†ñ–G kÁe.½”¡7¢‹Xz)]5³.½z‰J¯õ9–^6¶-ö}t7ŠGEݵ¡ªÌâHŒ>º>êèe»>JÀêdY Úì¦N®–ï¦Ë˜¹=›ÒÝ’¸ÿ]šßtu‚…e€û+5Ç‚OªöèX$²Ëѱˆ0Ëq,bÚ¹jÓÝê‹‹ÐV^‚c‘û–^ú³)Õ¿€•¢šù Ø^âå=é6èW}¦"¾/a'g ¾ñ«ÌNw‚ª»Ÿÿ'ycendstream endobj 482 0 obj 2741 endobj 487 0 obj <> stream xœí\ÍeE—{ða@ù¬… 4y[uës34‚A†$>gDènc"FcdbĬ0ÄDMüرgáßâÊ•;ëãœ{ëÔ«÷¦»ßëG¿ž›^ô=·ªN:Ÿ¿[UÝ;¬™pÖ„øýòöèÁsÒ:véíÑÎ¨Õ Ò°íødœhØ#¥,}=Þýbô,»<Š¼Ø¹ÀÃî%Ï…§gøõò6;³5zðaÆÙÖÅÎ;­qËZ9Qlk{ôÀW¾·õË‘¶ÌÏ´õÊè")ØXDòD ­ñdj¾16K–¨›bkËR߯ÊiÆm$7©t=»òŽÑÍ‘Q­_›C¿ù’F¥º%v5¬Mä×),·3ÅUv­„µß ãR¹0(bjE¡n ”iŠ «†;fÒ›Û@3c?"Ðßt‹£‘â2’ß„ò\è Ç¤åoÅÞ¨œDq4æí°†™”¬M|îˆ|˦7¼Â5|:'êÎ@Iß×Ëè»â¬†iž±j;Viß‹Ÿ7¸—ãâ §â`F¹(ô©´zé Éû`(LtŠp: cóy‘¹%œ€¼‡Ê,ç¬ù~ˆ! 7j£½ùÚÚTI®]å;‘ôæ6™ö:]Rål@d¡¢ŠRæFióõû¾ ôd/“˜ã¯=4:‡-,º¹5zÒÿ쌤S>5ʧ·ð¨ZáÏiRDìÔe¸I˜àS>Ûù„È ×a°´Þù5Û}utñûÞ_­À¤O¯»,™Ëšá­-¹][å”®Ê ³d˜nNo§Wtm$oêz€†Y2L7ÃÛè[ÙTe€†Y2L7ÃÛ)­vÓ‡ý•>ë&†+&m,}OŨ÷é½/8îÄ:òÓ±]³ÔúLÌ]¢z“ Œ@ÿ,Ò†ùÉŸÿN8Ó Ö@^…±`Oƒ` Çû±·ÁÞ/ÆðsEo‡½ŽsAôž?WÍ¿ÁÛs˜a€~&óÄý¥.šB6€£ùÄEÕÿ) µè» ,”a3­ß¢ŒŸÆ ci»Âê{´•Q-ÂŽMÐlRÍ›QŽÉ”¸>.¬² öOCAƒ"BïH¼…ÁÜDr;UH"ïBƒ»< Ff™*u}½3`ªg ¯«4ÓÛO˜É bø4OW_pþLëÑ ‚N`™¨×¶ଆºÂe!U ËÀKÿkŠ÷+•£ÝÍË`^lò™,Îtt™KË9®|"[á-Å"”Q Ûj ×pL¥{ÀbY0¼ „+Q1H7wŽBÞ hèbä*  óÈÈO«tï%š¢P¿Ü±™m£©"Ëf“KÒ'‰‰k[åd—+Þ_Êpn"™ñ*&¦ js%·EãU˜ºˆ(5xfæ1ÇÞÛáÕžU\©óŸ$FØX|½†§*ÕA²$÷–îW\ClˆÁiìÕüÂÎ] ÿHKž"þ–¦&HÐ'"¿Ö¼÷‡1xǰÉÑ~uÈ£ïý›@{¬üÏÅÞ öþ0K?!vz/†Btä›·ÉBTÕ‘’å$Øù³Î†‹£ m°¤­šðß:¤þ÷µ÷}`Þôh@ºLøUA1ºJÒÓGP©š•á‡ÍŒQ.úÚ“Sˆ\À ¬{ÛéN£Ÿ€Ê°˜ÿdò`s[ÔáG5{ù•Y¼‹õeÂ3­8l íž£× žù̺2x¦šs裸ÈÔá,õ­]0oÓîÖø7Pl“éÃP™}²ÂH¶˜v?…´‹º|ŒDù&¦.ðÐ3 ;Ð c?Æt ôáu–¨îì´+´vÊfÍlÌ1(UôöU¸ÚǧfeÖ¶~¢Œ¿r|À8z]u`eq# —«²m3Ýçýc‚È5—þñ¨"rݽlDÞ¯øX r„à‰ßOI¢m_*sÌà”Ú[ŒàßF²i gäU`ò Ôå ZãPƒzþKgáÅñºògU»¸ºÿbg\€ÍHùy :Ï€½¯8ý9¶¸^—¼,Í€Þ]¶¢âC@wÕø–ÔÄëCÈF€ÆÖÍÏ› ÝòóHaž<Œ™ÐîWB»/««¶ÙûVªê°[B”ø]Šmo%Fظ.Ÿb@»Â Y˜5bŽb `6F®Û÷“5–j³_ÒY=OaÝ÷Ä´Û6;$Ñÿõ×C6Z/ñ82¯Ñ ì‘Þ.ºp'H$@¥¦Šöžvq×ÉS¬;Ž#Ûû ­ˆ6Ókwk¤Šçt§æåtª…|;&ôÌ7:@³ØÞzƒLïL¯¤×+™Ö{í<(ß!¼Ú—B€?@£ çNÛ’Éeá–á3#ÅKqù€zå24OïL˜ªf+=e±-vÒ¬¯†ªiHÖçÄ«ÄE½ @îWÜ2þr[p”µ¼úPS>ý$0nŽ)‚K9¦3m^qµ»Jc®þ'ºH5מ{ì#}P«˜ºÚ#Ù}NÓ Z!b®¹@Mo'•ŸÉs?®8ñŠ™ ËÌáÃ"‘DËs©½ET~ È`wÁò4™4 ÈÀ³±ùßÀÈ+ä ¦À%RtÝ€ô²ÉøüɺÂ_RÐÂ÷ƒ3¹„©<.Wå²­·r?¥Ì_ó6®hnS%j€j‡;¦÷y/$Íw!¹á¤§!ž€¼‚ÉÜï&bìÄõèb™Qj-¥‹ƒï&O—èéW å[Ú+d·¥°Gøp^ ±ôÃù~ÅÃVà—¶è!h!E6ô(\ hµZÙ¥€c°Éè›aËâ:¿Œôóè4Dg+½œ@eéï–ä®ÂÒd9ôm]ƒžô9¶×ƒi(dq¹W/f€±°þ“µ=¬ýn½ , æ™ùl¥7t*‰å(Ü×ñ¸ βõ À’K$k°OMÏx£WwàŸÅìýÀ ±n‡œó/í@8îàÍ&˜QÉ¢·¬¦U”_wÚ×µ«ÖÒS33'ÔŠ(uÔùŽõ-mÅ ãRvqùVgP±ϱy¶”¿­··!:3õà6Kwx³ƒö\R¾±…žíjòMQ€º«÷×£ã¬ðøºÍp|‹<_ïýÈÌÖwrµƒ:ËX¨ðï†íL½Ã‰ö´ò‡íáDó)p8Ñ^|ªáD›"ŸáD{8Ñ^݉¶ˆÿ娞hs§›eŸh÷+N´ §KXVòcêðÁ ôÿjíJNÏ&_Òù²WÜBX³óeû J©ˆ`ñ§°•Ö5óö'¯ëtÌ{ü• çn8¡‚Áà ÕupB5lôÞFŠükH¹ªù"ß3ÙÎ៭ø7Ã&ß°É7lò ›| nòÁy}rô8mý?endstream endobj 488 0 obj 2561 endobj 493 0 obj <> stream xœí]ÍGO l’! 81 ‡€4ëé™î¹;Aœ%aG€CÖd›‚B!rIN  "¡üÜr Ü9æ/àÆ ‰H©é®êéê×3ë÷üÞó®=ZÉžšþª_U÷TÍìîÕ¶(ªîÿ?·;;~Z·uqñåÙÞL5U!µ)vÝ•ieU¼8«k˯»/Î~2{®¸êZUáû~¬£Ú¦Ö‘[Y·Øõ.×U„‰îvUØzÏuæ²›—5Ö R÷º®¦PžüxGJ[”jÝÚ.ÐÙj”íÌ×Xc£UHC,úVbêeRNÁe]‰¶0þÎ'Q3%Œèèû:ZÑh¢„vä§PB3ÝéÑkù~×›”ã)AÆ|e¤ZY(?σnÞº(uÕ¾&>=õ™ŽÒÐøèèϺUMшh*¦òü ?6¸çãa†…£npFy)”—^ƒAù(Ņ޲™ŽáØX"`YX6’#p8Ï’Í5}É­Üh0ŸÊ-åùª*Ÿw$˜ÛDÚ ºäÊÙBÏ`CeJ\ÆFQ±üЕî<ɼB¨‡M,zjgö4üìͺ0gÚª†ðÖ]ÖJBà˜¦uB¸N!ÂmwÓáoî¢ÄÂÝ…0müMqå…Ù…/^­¤†¦ãÞßQ25û»u•ζ?ÚØ¹QŽlâa¾ï6‹ó [97Êñ€ C<Ì7û»Î$œÚmØdZ¹ –y”Ûd<ªMQ{@ýý§411$_wÞhBó¿ÝPó+.øVû¿yfÃÙ·Ú ,&ƃÞäE÷Ól*ru¯ZÕZ¥ „Ž6†ïàþ$êÜ€£=VÓŽ{$á(rÞ‰£”* hÀgïßÀº»J…EaœAòT75û½B° Dë^püZ:]ä–,@UC1w+Íuj]Älô~4±Á™z›I?Öóx¸¢§þž\GÏt¥j*M¨»ìy¢JÎ+s‘oiI³‰ã“;öûTG«TÝê¥ z±dEÛnw[ŽÜn£s$ßÒÇ2†ÍÔÙhè±þ’x"û¹_ääNÒ¶…3“l܃8ØÜ‰y™üŒ€àÙKœ{j#ôŽÅæðÐy]3·!7ìi° dUö¾á<€>÷”£ ¹à7i›Fú´k&Ã|‡ÅHÞöÏŽê+3]típØGú[¸aRûÿrí`õ¹ñÑâà\Éj¾ñ—È'’LñmGiúëD#±l» {é.;4~‡c=[gÑ#èŒzæ .Uûçœ×˜oà.é©9i®ZŠ?À…øÔmMçßß hé³èoHþÞ!Í,»ØYB*)±• ˜û£kUÁ:BÞ\/uXì Ä› Gr½¼d@áò]GÑæµÜJ†ô.µç½eHxicTߘJiZ¡e& óó¸åĶܙÖ&L`GË‚&öûqØGnèPa»šO£Z,úüÁ­AvøbEOáÙÉ?ã&é;ç†RÛK~ í®o¹6™—\î2SÒeŒ·UßTæ5òs§]ˆ–þ(ó&iŸMK'ˆ'hø¯Ðöâçz o©]×J ÝŸº£ŸÚÕuTï×£mpî'˜_#Ä9êg#>arÁŽyÙ{VÛ¥Ñ+9¢Y’rÏ)SJbí°(Wa¹:{™Nâð1UJ–R/ßðÎ)4]‰Ì=IZ0=+}|¸‚NYõ³%x˜W’ow0ÏXHffVá0üÆaWK¼´åàk˜‰mP‰*÷°9æ¸m8ÔÂ\'”NÎÃLÙ9&,” G–U>ÀY½ÃÍ”íËhvÊöeüxÊöÌc Mé½óIï‰Ñü‹ò`n cï×—$›¾N(5ëJ jÌ?¼j8šOiÂ)M8¥ 7Ÿ&TBàý1M¨*ݬ:MØK<¥ 7š&Ì,í[Ìç-R[ÌåLȹ äô­ _lúÖå&å|o—o]¬Ó·.‡ÿ[) –µ¦ÁT ¸ Ó·.·ô»çÓ·.× ›é[—ÁfúÖåVÎTÙž*ÛLç²²½ÁO[Z9»™z§b÷¼ò§b÷­òiKÖ{š >ר:¹Ïø~6 Gí^Ýë,ÕKY™©TSKõ.çoå~³ê¿•ÅÍþW¤ï³oná–»tlŸ+[^×é3uØ·ƒÐC¥èÎ>‚+Véiá[êð{ñsï4èÑÐf  7ìfÇOø¬k¨qº?æ@¹X¹N'¤¥ƒ‡ð¥Eì¢Ô·i³}«5:ð8›¡ ïÜN é ì`™þ*ÖÓß`Ü/þeÀXgßAÉà)¼BÃÿdã¹ §D&Llü{@Mt^Á¿,ðôìCÆ¢Gendstream endobj 494 0 obj 2779 endobj 500 0 obj <> stream xœí[&EUVäÓ(Šva±I|@“žíªêºô›ì²ÜT„u »]Ýuawe//F Q4P 1ЉÁ7žôŸø-þërNuúª{ö›éïc:“ìöéº{:§úÛ­šmV5îþ¿xmqêœlxuùÅÅîB¨¦â­®®ù'Ýñ¦ººÒÐg×ãêâ‹§ªë ?Wuî!x¸qÙÎÂÂ3üwñZuzgqêþŠU;—¸ní„î¶M%ÚmYí\[Ü÷/î<·P¦²+í|oqß-äUÍ=xÌF[04Ð7·U€>ä[Eú~ØAª˜ñà–e]oõ]Yœè#~¢ZoÎ_÷ó’F©ú¨ïª+À9›ªƒèJ³BgÓmçoøF žTÆ5¢Z©;H €<ƒkÙ°®ÒáÍ'€3µáàÛ,p4B¬õà'B–"íø¸ü)ß™ †Â¼h„:^‰0ϧý¼²ªÛ¦¼D>ôYµ¶¯ÅÃÁŸó«êJ±d*§ 0|'?6¸Çã8Ñ…~°¡ÜêT ¾µõàÝ0:Af: cSŠ,ÊÌ™<ŠCqædæ¤ù°!·J£­øDi©€WƒªòyZqë„{‘—”9[`d(CËT("¥ßö¦!Xœøˆ¾jÔP¯&“èÙÅöowÑvÒú±FZ÷æ¥àÖñXŸÖ¶à;E·í ÆéßÒƒõvÖ!VZ*çÂZc•_U7¾¿¸ô%«¯†cƒr؇×ÑK&ã’æðV7ùl{ã 5[åq€†!–›á­ZÁx™Ð0„Ãrsx+öÁÖ1SĆpXno—)ÚÃÊlï‡0Xjõ/ù2pÇ]a£•Òî³ÚûØo´t°–\Ët§EƒôÛe‹›ç±‚³B'x ø€š‹Ä7YÒ›ªfXîO|£ˆ‹ž—àOÁ)`spûŒø\÷’Ç×` PB˜_Ó ¹Ï­‚cnÓ‰âžu‚„Ô–{!?‚Ý;÷`É¢¶{¶ƒ+à)€w4ºL,YɆWà+ AQkŠ2/pÛ³°W¾¹Xq' ÛÇñt´è¥.¬ÿwï½Í•eˆT}“«®EF³„=Ùã‘Ó*ã^Ö¬S2ýfäNÙµ1¶Exµ·ë¶-uºÝî¼åQ•Êwk4˜¦¤›ÇÈГ¸¥‚4QjÙÊ޽+F:ýTžÒγZï¯,‡—|Ì~¢zfÕ¨§¸´×óÔPdÙÑÄPe„@^‚$Kæ¨H Q3ai`fzwbõüЇuàWÑH>ç›Q0û¦mo;È…ã3…v+U€¿RÅöÇ|{c†Úa<ºš_„î"]Ý –¡Ù>$hºm]iÆa»øø†º ¼ßÀÕö›¾Ýºn= ¬„(ú)Ø"j$õ-ë À§‰Vÿ;ÃàoÃÊàÁž%Nê t¬žÆøà+E,;4‰gœ¯Àlúâ•6v¸òwÐkrWRÑ©?ï9$1¦}iœŽ t±ßzåÓ“.†êó–‡Ð®c%ô謅 èïRß»_×c\>Au̪¯SÜW< ¸æ€VdìYð0þÅ“‹‘Mi(¶½¢{Ã2/rñ:ÑÙë *MßÓê`àÁ=ÿ¬ ƒÃ{ùK¦E?ó@ÔÈ œ/£¶‡¹„Á,Ä^×`·c NÃ9È0µÏØs>Jã~4Ìý!áa´I=µ ìà§a¿—aáÃÞ¹#ZÃG8‹Tîzfrލæ2Æ egOÓ>ƱÍfì‚»ÔBÀýo>¦éHº3âù¦ãEãÅBä¯åÒö<Æ’PÚ/å(¼”ð!±ul‹‚…A,Þt w1CšvRqÝR8Øêt%C±hFãp5ØŒlOÍ0óŒ#…Sœò¸P¢)ß>=QŽ»Ly"°A»Å€‘e­ÚŽEí1˜; 2–IÎDZ>= fÌdñ6ÓKË«:L–›þ¤FOƺÈÙ‚F/IL Ä.ÃúJ4µÉòšxBEÕ¿ppUtsVì$Wb>þu7"ŠšK«€*áöú\H‡^¯”Ž” õßQEŠÎöVÇj¶H$Y[r(c)š!ÏÀ£'šeÉŽ§y2ywÑÆÄÏ«^Ô¬µg|&ÓD´¦)žy{Žeý.°ø¿0?€>ÅÀôyÿ ËM{8ú¨˜ÿB5LÓJ¬‚*GU X_EEÕiêDbêd‚¥ZÔ™HG6(þœ2œS†sÊps)Ci²òЦ ¥ µ™)S†=ÅsÊpÓ)ÃÂê¡õg€7%Eóuµ8ô—‡þuæàÙHÉ$uæld)¹œÒÕ S4/Àº4kgƒŒÂË„¨×Àè(å9/ –€°2ÙíÀž ‹ýÉ·Š(é?nN‹Œ‹½†²Ng³„¸^\ÖžÿÕ¨Ioa{Ùz6! —.Õêƒ  æ¦evk˜L41®$CyÐmBfı¬(‚(¿)‹m§ Ê5 æbÁû XÀ;ÎÁ›³ º{¹ {TzÿpŒ²ég‹Lö°Ì"_æžèŒ=ƒX˜YÄ`ù ð;æ–YiG•O›è 2VîBsŠq§¨ªÅ¹ÎU:³¬fÂ,éC3l-£(:yð!ÃT†oÐ.Ûh—/DòI4ǤÞ2b¹^µ!:õ¬6“« ¼ÙEyNäoLÆg³“m@zû›¹Ê=W¹D•Û”ó‰åœaT‡R):ǃj‹K=›¹ðMØ;¾—™?¾‰yªXzœ Ö9}¼h=*²†Î5¶N©Ò3¾¥*–ÝKٽβ=çžËöàÇš¾ófËö\Æ8éÇ~:T©}9ö–ºÎþ+Æ=¶Î-Øu÷íÞ—JØEØušŒ[w”ã ¼£®¨f.üa$²˜Ò:ò"8oã§«¥û í¨w3¨ SXÚ©Ó!ýKžþ“kLÊòuÚ!.„.ÍR+žª+ömÖhÃãhÆú¾·p+ä¡þXxÉc ¿¤î†ÞXI÷r}”ÃzS'Þ¨¡å,A9íÄ:¼¸ H%Q‹KyÚØ†Ì™æ‹LóE¦Í]d¢9Äß> ÞNþícOñ¾Èdë ¿óJÎÁüà \6ú=˜Dë/ÙðkàfÓåà¾éÍ&—L1KÚEqû9¾È=†`C‡ðñ(ùƒ_GâÊlìãHÔ‘ÿ)VÐmkXç°jПP‹oc>^½4žDœ%A(À«4aÝ—´3è §!ãÈWãÓ¾ ·‰³Éì7Ð9LµÎy«oþCFÖ6ÍQ«ƒ‹fse(e%ز³~îRá ÍåB'%Ã6ïàÎ4RÅA1®ŠÕØôyâf¦9‚•ŠRÄS9pƒäÞ ÌIô­æÚ%Žði‡³f[ÚÓ·ÞcêÓNOñ{ð´ã?Ô0è‚Þ³§fMù®œfú`òÙ¸ï¦ñþ|f™ö¦?Ríå,õZŽ-Œëõ\©vœ ."Äl"FéGåÀC/SÝn.nä], þ\3­Uå;LO¬| jµp µö hÖyõ÷Žƒ¾ üVëô‹wË8fJšÕJ¦›º=·¾hY£ª—B±=¢:z¼ÉîÓt©•dóG_Ó/«ZX c=ëQ¬í?‡·ŠÐ‰É‹‘ÞÃUï(?±ø?‡ŸÀendstream endobj 501 0 obj 2970 endobj 506 0 obj <> stream xœí\Y\G&$ŒCAHÈâ,P‘òDêö­íVÝ„›@ ›é؉bPÀ&Nf";á‘( R@l/¿‚'Þá•W~ Ug©åÞÛíOÓm·FšésOÕ©³TóUÝê¹*š™Mü¡¿w''ÎÚÆ‹ËïO®NtÛeœØ…O®Sxwb­¯?ÇïNÞžœ{%Î>G®]R$~¦?wÅ©ùäÄI!Åü­ ;´ëf^h3³b¾;yê3OϯLZ/ÂHóK“§îR‰©ò³‘ô.ȾØF upµÀ¶Ÿ‹T× é܉¤í¨é1h*“ »APCÜÏ/¡.ìÜŠi[¢î¦Nh$¿IåÅT/T×ú4ö†l»p ˜–˜`ªTŽUD.+õÅH9M8xjÙ ‡O¾Dž™†‘¾7Òš{3% _& e©tô#zù>hÍÎAJr0ï'Rå|äZ15M¼e ÆH=)Ú="ýŒêD+ Q:‰B $u~˜_Ö9ëq¼š @gKAy”(žSh½ ò1êJ=RIzœú–•¥¯$yœ&N­³ª$ì¯Ò"rg¬wŸ õjxª| ÈnWx/ù²vέŒª«J]YË2(º´?´%§c‚NjÉ|u•êà:× zÄX¤ÃMOýÁuжôˆ±H‡!ž¶9¸Êtƒ^ 1é0dÃSÛ f×õuÊû‹t²ñéТTùPðm;ÓÂ…ª¯¡à?Oezê1ã|h'ˆü.UAf¿lÎ8g«Zü3(ƒMB ’¨®)DÙ$ê27'úȺ¾Ö¤à?ŸÄ“Àù÷"“MÝÉ—S’ì4ypÉw3'œuácôÞ÷Xç±â(sÊלü^~+{ŽBÅí<ÎL#ýÐNù:eaÔÿÄä‘Þ¬€ÏÏ˘“`jú:W ÷+i+Iåï“I(ê㊚“$¤Þ ašªßTbYø• ô‡P%rÕÀQ;õ RbJEæÂð•å¢óñ“ˆ†Á­Kƒÿ(EûPàØGlìtb£ýP‚!Ó“„‰X‡3€ó\Ë:þ ¡¯ù–Aâ^åØ=òN“[†X£kÞOtÂ`}ý={ªË¡?ÍsŠñ Ž5Êú&u–È]rIåk6y¦œ¤ ‘°Ó•Îß" ›¾“ˆ@æÍO‹#½°W7*Fûÿ‰pÑÔ›‹ˆPHÜK>!@‰³=vXLmF%AíJZ€·¼£éˆ\AÈXv+%ä42945âÅrê%‹Æâ¨fÉ5/¡äe®q=…ÉpÚíÁµÊoa‚8YxF±gPt;ªc²¾"¥„ÐeÉè’õO@€‹ãËáêµÆÆ¸wi“cQtGivÌÂ.¦;u‰&u¹§Mž>FÌ•)Úc°xª\£CM…'¸y®7v×wmåŸ>Û•¾Î mÖ逖LÊkL,½uÝÌe;ÚûãfÃTÓ¨¿ËáU1:•¹ëã£+ÙqÁÁh¥‚ó(Oî"`i×Åä<<=7’Ñe˜Ùâ±M]õ¼X4óKsvË m=‹¤¶jš°ûdÑ_}œ„/}r“EÉÙYXªˆçH‰¾mpWÇì_sN_é`h'D9B<2l•¯U-m‡öñâ¾É¿‚¹ˆ±®ÑS˜"þ†][†q›‚ÓäÛ ðÍÄŠ:´§,y‘Šaï,¯)(ɳìb™õ7²õ,p]u Úƒ7Ë+³g­êJuŒä¨¸Ðâ~´õyXŸw`c’ý°b®1z¿uqt°¢B;ÊÆ°6ü–k‹amØa¯Ãf‹7æ÷gTÎ=¯–—€êV aWB­fàpóNÏsæ-O#D­PUÀTN C3¾Û‚É}€I+í¬Û‚Éu“*<þ3 l<\åÌÉ#‹3¨ÛQyÛ#Ê*S5jXÍq ¯æoMuidª•² &÷t°´Ž`Òx¥V &³Å &]:’ü‹ ¿wþZ7‹ÎK_¦,ŒÔYªÂ °æè+î§Ð¯AC| —ÒØÛñƒZ’"yxäi£Œ£GžóÿT1ÿ%w-á[ÿ 2–i$ߤæëRï¥o°ÿY­¦·)+!µÅ¤ûÀ¤¦±ôÊc‹Iד.y^—žQ×/6ÕíŃ¢Ð%ï?mýÖÕs6»m 'ûñ2´®ì7}¸Rã½únæ­ïTk6û}·vžŽlÖÞi'ýªá]¶x 蘒C~ÄzŒ¿ÚFIíQ€9-¹oÅÛ™,ì·)ЇGÖÞÜ´CQv䟉Y´X÷Á®^ó!èy$»¸µëYèêL7<Ãêø;ÊI„A¶ þR âãÏÿ52Dendstream endobj 507 0 obj 2366 endobj 515 0 obj <> stream xœíM¯G‘„ð KDÂGBlb$iŸûczºûþ AØ/(¶eH°ãg¿àç ¸p@(„o)>2ü ”Kî qâ˜ÿ¿€DOwUÏTOϼÝ}»ûvÍÊ¿Ú𮮝®®ªî™ý‚mó‚ÕÿàÿW÷F§.*®‹›oŽöG²b…(u±çÿÒV°âÎH)Cÿ®Ÿ¸3z}ôrñÆÈÓ*.^€?îÝtTxøþ{u¯8³3:uºàÅÎÎ;®ÿÚn›B–ÛªØÙ=÷¡/ïìŽ*S¸™v^=÷ˆE1|´v`@Ø£Ë"@y¬,³©![Üxp«•…GùGy$ôQOˆöcЕ7<]‚T@÷êBðñ¦Ë^v•™âaS‚lWîy¤¤• ,,2õ‰Ò ¯à±bÜ:üòhfìFÔð“5,q4B¼ôà'ABÞfºÖcÐò§üÓ¨œq4æ§A†^ÈŠB:ŸñtU1.Ycx…2<èé*ݳŽþ¬ŸUo‘’‘T€ÃàgPø¡Á lj/œðƒås¡OéKgP> Ca¢„ÒIÛ–È±Ì ¡àqpʳ ”[èÏÃp+7Ú™Oæ¦ |1t•/xЙ[·´uI•³+ƒ q(rÙ6ŠlË#8žÄ€¿jôPï&±èùÑwÝ¿ýQi•‹cL¹ðVÿ©¤pÇÅ´²LÿPŒpÛõ‚©ý¯ó‡‹v. ZTu+sþª¸÷£Ñ¯85UÍ}ø9FÉÖ¸:üªYJí`”-ó<¢‡.:üªfà¡ä<Ï úxè¢Ã¯b„¶yÑÇCí5l¸0"Ë úxè¢áWÓáwÝ)6[SïµÚÍ ýfû;g0 bìØy Ÿ›­3]€ÿâaCñ 7¿-–¦ IÜmý“.a òÇ>zÛ¢ ëõ¾GV YŒ"ç0¸ó°Ø¿Øq õuì’—Üó!wÁ ˆÝƒÝk ñü´Þ@íaúy€Â£·‘/"ãˆg*Lõ¦ßG…­——&¬b»aâT{:й œ´5ät 1î èÀ \µp b_Z Ñÿ 1€t¶A<̼ÅlÍ\+!€g»F®]€ðÑQ'¢ï€ÝYÿÃ:¡¥‹²Å†S€l|Ʊye–"*óÁúx–†˜EPo:¥[Þeq3¼}±ílŒúšŠLâCîÄPÏ3øž”už ‰Øß|’çvf%ZÛ+ºî‰íÍ–AÚò@h‡'A_°>ÚÍLt­‚’ì½°?ùráÛ&Í~ BŒè¿ûÝÆÇi:AE8F–ªˆ‹ôQ óOGu2„!. M.‘rÌ–D®Æ.®á’)È=¾ˆ‹>g?V—${´¤ÒQšObòD¸ˆ6ÊÑEèøp¶ªj§Zc!]¹ ‚²BAF‹…Ú[4ov¹'Èü£¢úHѺ­[Ÿ™…jÔJéR‘XH"<]=iívYh—ÐX¿Å$zLSã9ã^“äÑ“˜À™Vµ _áØœ5½¤ÖUŽZŸŸ8 w6óYJhî°‘8—Xc©D˨cd êóô¶€®–KR— ·¢^Ä¥“Ušû†_UÎ1Lˆ¥ßô°.ü®X€ÿé×·D¾/ú§ÑN7=’ üm\„mÚ5><þbT>G-ó´ìr'A\/ÅM6èg·ð€— „ìeÒ—q'QA›ßƒÝÀï“úýd¦ëÉ.{gVdbàã*¡õ D õoÔ¦”z <ƾ’äW®àîà_‚@ ÙFF¯‚ø †sœ ]ù=²å/b&Œoƒ7S£À[ Zú‡4`Î/|PUÜùó„U€Š õ_½À˜ä†"î.QÔ$ãpÊ’J³n…‡ÂˆóBXílyUH‘0Ñ«[AËŽNY²úºuÒ‰£K´[‡DÛÐ\…&=CŠì“4§©Cl›öztèíVq3Ì¥±.DÞ±Û™4î1 CZÏÂÔ¢E9¦%[] ìaŠ5L“%šRígNâÊ¥\Ø?¾3Š– ’&IãÙ~Ÿ¸Gv´›É¶;ÄÜ%¦ÙÑ¥Öض=lìkÀèIp£F'™ŠãT±ñ?qU”ØïP‹-ŸEg—* "è«ÝLZ%ù6CÇ~–í ‹ÃeÙ¸ÔËÀ¡¼ÔÄÊ&kîæ€à)XËí >vSamg‹Ë¥(ÚiT÷SPNXŽú3OK&h> ƒºÉ1,Hàe}g8[€ÎÅsmŒÙ'×$¡•=õO5ÅÆWÅu:ÏxiŠÌ*“ÈR%gjOç|Tƒ§`q‰ºj‚”"”»R:G^ç¦IÅÅ 7M*VͽiÒH¼išäš&œÉt®€ý9ʪï;*qè/)þízø~ŒRlýû1¥H›HúW0–5CK›tÜLP­¼MBç;´´Nž«‰B¯ àìL0ÙŸòBúÈœ-õ:‹zèÉi_~‰d&ãm¡ü]¼ž¥±å¬gÖP‹JK¢«"ïÇHÑòWŒ˜¡\$—–úW[²À-u¾Š˜8½ÞU¹è6ǶJÖ1¹Ý])×'õÖ¿ZY,㠬̬d¹Ôkf¸.ïFƒŠ¹xŽiÚÈåb݆÷–©7n³¨Û‰ûhÏ9Å“èÙ,'Þ$»»­6ñf1Ž£ð—ݸ§ÎÇqM!š”v±Ž“f.öÿØq6wT7Gç+ut.²:Í7×£Õ“£ä¬]§Øœ¦oNÓ£*ÆeÅ\ŒelЇ‡ŽìÔ¼ÜÖà\›õ^†7êèìÉ;º ‹¢GyÆ^ ½Ög쥰+|Æ^ 9÷3öFâ5>cw“Ù@áÞQ ,D<é8€UÝâu:åžú4·9 ¼ iŸC9>ò-P¨ä€£Üë`eàâ×Ä0¯ãØÉͨšfü‡º[ L=õ>ýÐK¾p$\T¯³L\W¦™¬ÅDo‚RMS›u¯b'}#› ßc½¬gTIU—#%¾Jÿ>Pæe–––ñ+FŒ@IaBv›’îÕ”é¦õÓ4`Ä ¼<ïm˜;·j  ‘Ǭ»6«ñ£Ð†EÒùàƒú¦±ÛiÊø&¥k”ô6«‘¡5¨òV˜ )^ul4eÿù(\Ç´%êù–â@œáÏ`PŽrߨ.‡kÿºœ!µ±Uëýƒ°z…;,Âò¹wX‰7–é:,§Î•r_Vx<šëð!ÕCÞyAªØ ßžDD_\Dc†Å\}@øLzB‹¹p¦ºD&¾DÆ"æ¹u™ðÁ^¾x{±˜©¼Epª¿EÒ¶ÓÒš\ñ¦â M.tû2’LܾádžM0áöŽþ&Xc‚ DÍ2‚~;m Z[VTK»î¤­¼öÌ“oéá\‚n+¢[ÛÐ5mzÅývU}C°ÕñÁuç³FÓ’±ÒnUÂ%Þ°*íà¥Þf£~i÷7tM<§¸›h?×3¨í¼1íéÊwÉÄš¸jò6ÁÝΑ„•†Rb³ç3B(6žqžá=s;¿Œî«Þ4=™zÈ›û•©Ž¬·ÏuµéíOÓÛ·Èò73]Ë/žŽ¦¡=L`ø5`qœˆ7™6ò½ò„íªï6U†Úê‡Çj²uê’|½„Þëí4ÒéÅ]Úà¥è íñ¬¥O$:ÿ7ì^NÕ íëüg‘Ó(GEdo©FJAÝ|@É]=꺩~sÇ#TIÙëqMjÚ]%+×ûZ×+V±iÊK&æÝ4m$Þ4M§kš:¦Vû›0FB;üaíº–±0Ø|/¦‘s5>Ó°æß‹iáæýù—6é¹~ÍEÕW´Öóbdýïn Œendstream endobj 516 0 obj 3253 endobj 523 0 obj <> stream xœí˲7‘¸áƒa¨b¨:ÇÒHšÑ¬Àv‚$±o¨8”ƒ;ع!×aÁ‚ðŠÞ@ª (*°â€ {¨âWü T¡G·F­ÑÌyܹ¯ÜSYäö‘Ôê—ZÝš–¼]°/˜ýþukrê‚*Uqý¥ÉöDT¬(e]l¹¿ê¦dÅó¥4ýÛöx~òÜä‰â…‰ÃU\8ܺn°pÿ7üïêVqfsrêtÁ‹Íkœwjÿu3Ó…3UlnMîÇç6oL*]˜™6Ÿ™Ü—ËbZ:ð˜um@ßüN×, ½ËµŠÂ÷}·…šªàÚT t½ÛuåÑ{"­ï€.¿àð’FUô>×µ.„ßoÁRSÑK®ÒKtÖx»|Ë5*ht¬ò²F}+õ Õ 'à©b¼)jÿËA2S3ÂÂ÷XXàh„¸tà‡€Cmåè¥üa×…ã!ŽÊüðÐ 5e!<ž:¼ª˜JÖ*^!ƒÎº×BÒô5tXøãnÖº¨x„JTžƒ#óCƒ[:î#¶p V ”O„6å¹—F¡ü$ …‰NL'alÌ‘!™k‚ ÀûÀp(Í%Á5 Ö€¹ÑF}"7•§‹¡©|ÚFÝu$½ K*œ Xdh†"•±RDÌ¿é B÷J04•öZ£…:ãЉFÜœà¦À½“ø"´N=®/Á`^;p Žû ˆÿI˜{Cëñcž~„¶ ®sefÒ6žÓ@/ÿ`ŠtÌyxåTF$¡W>Ž$X4O…ód舶QàdVãhe‹Éá1íªƒ³Eq ¢ ®Áƒ Fq ßõf C´d”WåK.¾)ÜÁ/ËKKŠ ü`¼€ì °`Ö‚žnW±5+`ãî‡`‚§Ñ"«v¨¡*æY¡õÞðÒd+›©*ZPqCÒfˆ{ÕaÃ{ˆÿbªM@¡ÊþठmN‚Ð|ç» Þ˜–"×9à‚Àà°muC®Z%MXµ1GÕÒ鑲ÎL²Ì€FI*dBEÕ m†Ø7±a`êµZ8ŸlXÅ[¬~ÏzªÀ ðþ] ŠèLÓ(¸!›BL¨¿•œèŠ­&bKÃIÃ@¹MÊ|ö²–Q2Nš©.'Ñe\SÁm$RÆ€(jf%ç©{%¥SÛN6×¼Õ¾Q/hß{¹ko65á³Aã*X?e 1k®íjüUF)£c@ͧ š€™g*+Ã<'^$Ux,;ëÆñÔ$>Dõú\ÊË "¶I«À¼9g—¯ê¤OÁttÌQ;í‰mÀÎ_Ñ2¦C)E¹µ»†ˆÇ¬PYV’¢²börôYôežX«Ä “æ:&Üe´þ¯B52À!¼Ü9\ÓÌdQ çpÔ¦G ¸Õ°~ö‚e¤ì8m¬o/k—שL´ÊÑ#7KËqî@¢,rëî狘‘à¨Éœ-W‰ ë€åøe·îah ó°ƒëÀ¯à^Üx ¿†eäÅ÷ˆëŒš¸[$ ý>lÛ,B¤Â<ÿ¶0/1’Åi=ô? :ãEÊ™D”î4E]W jM+†PìH14þ(áþïÁ8*ž¯ x“gŒ©vš¿èôÙ&¨›[€Q>îÚÍÞä K È\žpFpÊ+øë® ¿A,ç?ЈÙÄåËð„ÓW`fßûU×(0P÷¨x óÛ@¶ïübrÐXC!äÌþdeRQ$1¿ ǘ›^x¢Ûè5BÇë°ë{èigýíYÝ〠$û$LDQ›4àŸ€p‰Œb€;dæ;àb¡ñg_,7µÙÙEn'Ïó7¡²µMØt€Î?€â0»ý#hÀñ)³g¥²¿Lp[p!ÊÚ\0+Ü[/:.Ƕ‹ M*ƒÚ®O r„?DYȲ·Ê8ÿ½Y,ƒf-d)ß-ƒž¹e6G^aæ1áòº²„dè6i» ž<ö§ÓR²f9ûœã·zgvÇ ÕÛöS­GöÄ_ʈ Þ†©`æ!Ky邌&œÿý Ä3ñŽ]wh”v^%ý*'¸y£®Ãa(É›¤í¦7ŽÄ­´˜Ï0¡ˆgX[Æ~X†ôêdƒƒû‚3†šÖÔA$ö‹k8;{(H«¢‚öDýœÎë0UºÚ¯µ¥6nVé…:uÑmŒ~ <6kqÃõ…%bŽg k ¦vZ1 ›3ó‘ [Í=qÆ©©ˆ™ij ­Ø·¡Dá8hÚU¼Mp%óží:¡‡B¢tÅ’˜,©‘r/w ‰F–‰(m¼9’‘ɡȵ=DÛ]# Ÿ­YÙî™ZÆ“‘Ð4vËË ÓÐ`t¼¼yò`‚;ôÃ$6A0GÀv…ÜˬkÐvâfk&{´C÷Ó Ww¬ãYîÖØŽŒÏíØí–þñºZ—þ-Sú× ÉÙæ”#RÔÔÛ2­- ugωӄ½Å¤‘/¥KÈ®H T4k.†•êÙÀz8eëî¨udé^X±ØÓ:;,ªÌÕ…²« B½HD5}"‘ùÁ]¸T¡T_a`V9‰rÄñæ0yqóAв*MM7•o$²žå««ÚƒZ_ #™-¦8Ä5UŠ›Ñ¶¦J1)Ç®©j9>Ä5U¤^)\÷tCí'Ž}9üà¥ûã*ú¡ÅpÑUnW°¥q§üTB%v¾Ž£Y;6ý;g먇‡‘FFiôà£nÏ:BÅ•šÙHS»žÁ†èa¡+\?ÆÎq K(Hzŵ"wùϨ Î)¿òßÙQ¦Wp%,,„Ñùï¹W°àß>•¡3ý<Þ2šÙ,˜ùiî¸ß`•š™ÈW(X髹æ¸$ÙÆ¡Pýݳm-îü}ƒÖIÍ9zàl‘9qȆèKýÕ±,Máh9áçnb9¹£é ˜2—Òý\Ñý¼[$?PŽ=ïFAËÎÕ†¤x(¸îD _d©’ èQWR~OËþiÈ¢ TÇXï4äÙ¾`¶%» Uë¿t¦¦ƒÅ/rý¡5ê9 º¿²„šéEƒy—_v„]vi¨ZóqC.öΧ²6¾osl™váž}ÚL-Ig™ÚËõr¼æ$…rÌ ¹›êõßf Ìæ/YÄáþàu°9÷IºŽÀ¿9—å³n±µw„®:8DOé¢qM!yæÀmæ~Þïíò!2RñÄ‚dÔV} {H%þÉ’€i!QU…7Áú:—ˆ@d¡e4œì4tß¡,‹œÜòç?4¸zr/DLEm/8ƒ‡0W´ìUÙx*çkzv¶¬àB‚'eV(nÇIðJø1¡üÁIñx¥ßÞ)ž¿“cŒà¯â&šʆèÆýùå`&;Ïì„Tp‹êgvñµœôöáÀWòÑ„!>Ðìf'Ê –ó:;Šy½eÌúâÇ¡(5¬tb±4Úð0§tMéÔ:¥[<¥›JY·¹×¸Á<}ƒaÒ­SºÍuJwøR:Ɍꔮlä¬7¥+uÅÆOéʦ

<@ß=Ù†iX‹&¹¸© ¤r­IÓçïU»òÄ׉è›íCú£ËˆîÙËôÒúöÕ÷Nš=íÃ~¼BŒîù3¸vâ¸'½±+<Žd¯g‘ PàÃð2ŽØ•:¬H.›°¦ }Ì<È]äÕxò)úË?JÝŸÖSá’Ô}ù[r±uîñYBpù˜“&kKfêûËÙH§$Æ}šh “p¼aØïOdbB©<ÁkÍI½¸Îf¼”‚c“mÿ}¤(}Œž‡ûŒ.yß[…7}2-é=ÆÌCÿYÞ¡õ¨‰xÆÎuJ*ùpœ™›vÞUÎw›1}ÑP•HrªÕsòŒh’dŸEPdà;¹li˜øßƒƒLì±Éÿó«’Zendstream endobj 524 0 obj 3621 endobj 529 0 obj <> stream xœíÛŽ$7 A!ì„$…ÄC@êI¹Ê.»ÙË ¡Cˆ² `“ÝhÙY²PÂ-!øø|âñ-¼ãË9.·«ºº§gº{Æ)Û§]¶Ïýûœ®Ü/ÊV”æþ½y*‹Ú5`¥ŠiÝ‹®PK<¬8Ðvã0hIe•DÝ("õ1É Ëà©(Y[H÷ÍCÀ™©žaà¸ÆÙ1nÁO…,DÚðÑqù“öidŽƒ óa ¡j«¢vë|Ê®+Š)/;Á ¤áxØAŸ6×Ïj< ü¨ÝU –ªýRŽ“?ƒÄMîð¸LtaßN ”ÇBrÔs-P ~¦ÂFûd¥ÇanH‘F™)²€—Aq(ÎY9~lÀ½Ôl-¾:µ•ëDUyÒ‚ZÜ2àžç%eÎX™Zù©ˆe(”:¤_? LwBÐ8Uú*QC­r¨H¢‡³É·õßý o…öc¥ÐîÍ|u¥öiœG€}È{¸c0Fÿæ>ho§b!©ôd®´©4ŃW'·¾¨õUùÆ`ï¾ö^2˜ »oW[Œƒh›*‰ ôá0? ßÎQ´^²4` ‡ùa÷mµªRÎͲ8À@óÃî[UÎá€o‰@'„ŽsR)ý± tÔ kÛ™J§µØ´vÓÞ"d£=BÛ?,C˳ï¢|[×¢å>@#¼\œnÛ®Ñoj‹¾s ¼(“.çQ"ÿt‰L}ý˜/ZóTt„V Î}Ì»ën)Ki«#²”­Õ=‘Ô*© ã* 8å°0Ñðd“ Þ†Ò0¼ PÇHä>IS¬má23~.Z¼âCàš§"Lª")©ÈÄ ¡4N)É$²v‰Ðåù1º»1» ÿ+QÞí–„ .$B•¬ŽÌGkŒÓªŸAŽCÏË×Bí¶‘\!Ù6¼ YT.€Þò)j‡•ñèQ…øK‰f©^ÉÆ^“fÕ’ 9­­æ5¨ÄW-÷4™Êmþ5 ËÀ¯“œòvµÀNeeåçþÒáËÁ äö+Ö¸¶]MáÚt§õ®íÆo#ÿ¶˜iƒjÝø_¬n¡Æ|Ó»†¶ÏV°}µo®j8–|4}ÊÕÎ /Bɼ`Ç›Â~ I䋘  §OßCð%¢ª¿Á‡a§·!r£¿µ£(•h“Ûæw°ìÔ¿‡$Öy‰¸¨wQ¥aײ¦£@¯ üúk@ìTk½oܽøGV1»í0pàÕË8ò97]³ à$âÈž¿Fìù> ZK J†ÇûÁàÑ`ÜJï]AàšB`× €×zpÊy '»þ†Éï YŠ{†#ýDRþH$ù,03N¾H(¯—Í Q!“õÞ@Ö½YŸ(¤+¡£¹,åAcÍúÈîÊŸ'ÑíoHŒ‚ ¸Ñ둪=ƒ â˜ñ˜ œ"Ý{qÐ5²Í}ãì2ãs„ƒÏ‘'¯AT vP Òù'°W¤,€#ä'^':Ì[¿ÒÚyÔÔ ×~€QÔï ¨,vP„j mTÑå« Là Þp±‘I}¦,¾½îíGd»)w­.3Í{Øþ¶xfÕ·éǵ3«`‡“¸~ÍŒ<¬ðákþ€K£÷lº ¼R·Dóš¤V¦éñ(Žá†DYÛN ž¡OzzûQae{ZJŽRý•vJ™Q¬‘%¢¹Šú Æ0ÙKY’wúHÓ2 ÃÊR™i´8–‘< ŠdhÝEËçx„š`÷öùýJ’öÁíKVÒjPÒ#{]på§^…CaGK±ðË—bèŠOß5¿Ixr¿ôµÿ+¢„g•üÃ=ÉÉš±–Ť\Æ|‡à7÷CТé] =vHð3‹^¼ÑÑ«u’û·ùJ1ñ9t ßˆ*»{izÞw£üM{xåÐhÈÔJé‹…÷´{meï'ÁŸ‹qš‹àF.3T1e o!žÎ‡x²ŸSCË?^hûÄd“¥zí×Ì—%–¸¯¢Wjþ‚s#Dàsw±%ÊéÂÞËiO¨Ã²·Ø,¶ärÎì$ÜNÉbÀ¼Žôü¨©$jr ¬®ívBír#æJmƒºL«´li Åà!Üa‘í…¥ÏHü¢;xDÎ-⢒ ¥`kµ½©6"b æu`ð¬e‡&¯|.¹’B‹Ñx—p¸já²m¸€’ŒIqõ{Іõ@G8ôcOÀ#ðÄYfV±fFb÷ax…¼«M‡‘wbkj¨þh!’NF®„ †ž7Éx½UµÆˆº„&ûn—˜ˆD² ‰_ìâ–Ê>ªP¹G0RK%RÙ8;wÑ'½Äw²|Þ0~ ·¶|Þ”ªZwù¼£8—Ï_ÙŽ4=—ÏGgè[^>ÇÅây·TÈßÂ/Ú¹5ÕÈ…`ú¿ç©FÎü–!–¾MìÉ+»•„¿ÜíxýiVÂ…ù»HÌMíÔU­FUžyC êÞrW«ûBÁË -„h\›v:¤YMÍW]Õ_Ìé¨ \ÖYÐu.gog9[h{fålç-_ÎlréíGE¨óXFæ:éƒo,Ú¬òˆ_O,'PÝï@4˜+ õ°¹žªqÙ—¨Séu/eXÇT9 é— `UçbL5z@Z wϺÌ1Þ9û†:j5GÎv[•´©£„‡UIÏœVù;®®€yA ˵¬ߣéFێŘ3ª)FB¤HX¨ÃønÞe§_®e¦6â²Ù1·¨Ãÿ°ˆuGw¨áxëöO VÃäf£í½ ì;;ÖéþÓÓ¨ãñ˜“%EŸÇ÷6£Nû‰ßûyù¥nðW\©è )ô4/ÉØÜåzT8«ª=tP~ÑÚrO?Œ2Wb1î§wK¡ra4¾q)C"v¥Þ8å9‚t^µüè~¾wÓÂþ‚âI´c€ýTÉOWe`Ì2s³‘ô`©Ÿg’+GMÐø?V]Ë Þ†%ˉœêiáJ¼Ä6²+â4¦´›ª¨ºSÊñ#š–¸Ÿ_*BÕ”=s.¬ß®Y¿íÒÀ¶€å^IÎ_÷_›Úõ‹a^jŸÖ¦9uRgª#QȺI§(Q)›tc¨åz Ojc¢#ÎÇü t$ëtרËpIÅu¬~Å9­Oü.€íTÝej²1žaŽ×«Ö ¶FÎ,®Õkç*ݱ|Ääz¼ ´!º‘$kQK7•ŒMÕEÏAƒ‡ìhm"]¤”)¯+íwÂcÒFúEÖ×´¹ÙX¹ø±étéÓ%Ãßô1X¾ßGß`¡‡iTK÷ùxeZ»R}ŠÒ©´S¹"¦¾iÑèþK–y|¨ƒþLTÔ¡ºüÜnØŒž×µ‰Ó”x0÷sàþŪ˖ìlW–½§ç/j*bœ|2ꦜW•od["Kñã¤Ýƒù¹fQqNw¯ÊdÿãK &é´,å¦)² æÒëͬz.žÆTæZª¨7]Ì %û€#/ÄÃ[:}Æ _5­”NÖvºAŒWå¿_…³zíïWé(Î bKYÇî5ˆÅ]µ»Ùf^~¸µaú‘Í™vƒÕBž»7¦Ð6#ï*s7XŠ•¹lãÝ`5gùõ&¹,÷ƒ¥†s?XîËý`IÏœûÁ.d?Xt¾ã[ô{?R‰¦¿ÚŽ~ Ø&qÈ}ZÛSd¹x;Ù§¥OhŽéÑ2/é`EåDyº]ZO•lÛÌ]Z¹K«å¹KkC·³çáç»SÞ˜—¢AŽ–µr£VnÔÊZCQèÜí6­)—:ïfµØ\ÀÌ­Z¹U+·j­º ×¥¥37suœ<]å>­sÖ§Å”Øñ>­ªðÒ›mìÓªXË×ݧÕQœû´–²ŽÜ§eÁܧî„‹} ã&^ÜÅšþW„ç¥U‹õµåV­+s«ÖÆ[µ—UnÕÊ­Z¹Uk~8·jåV­Üª•ô̹UëB¶j©øf!º#‹OÁ)Sì=+À5÷uíØEÉ…èë’ìŒúºJÿö­ˆèÜוûºúXžûº6t››ûºB}Ë}])XîëÊ}]³Ü×µñ¾.{¹lþþ“¦endstream endobj 530 0 obj 3286 endobj 535 0 obj <> stream xœíÙ®G‘\àIˆ˜dòÎõô63ý–x!›s“ì ƒ‰Œ¯‰< ³‰$@D„Äò ñQ,Äù ÄÂ;½TõtõôÌ™³ÜÕG–ìS§gº«ªkïêã[E¹ÅŠÒþ¯ìLN]P¢.®½>¹5UYpY;îS­yYܘ(ÕÐÏö‰“W'/7'n®âÂ3ðáö53 óŸáŸ+;ÅéíÉ©§ Vl_àºSûAÔz«)„ÜRÅöÎäÉ}qûú¤j ³Òö÷'OÞç@^L¹7,ØÔôÃvòðÐGܨ(ü³µ® Ö8pÓ‚Jã÷»GY˜ècn¢F?>]ºéæ%ƒªèîѺü¤ySLE/ºª™ãáFm—n»AƒŽTÆkDÑ"RŸ²P-r žª’é¢ößÎLÍþ´…¾“ü PÈb¤-=—?ëžFæxˆáf>4ôBšÂÏó ›WSY¶¯†‡àa}ÎBÒºÃþ[ÑáêlxÃò8À@Ýaÿ-_&t~/` ‡î0|ÛÝ ôºs8[«ŽMµ%œ«¥†ÐyD©cË–X'4Õ[Õ¨&ö‘øÜÚãz,\€¯ð8èmd¬&$†ìÒ1À}ªxÆ»£YBp’ØšÄ\nÀ«±±1oªüJWÖ ­øÃÄÁe-o þà¬DëHŠ”fønðb`Æ«v?°yˆy‰!fñžÔ¸'™8(ìúl0õ¸!?ø2Í$¼KŽ;àC<ôc!Í~¯ ›úp(F™ûpjjÑiËaVV„ ƒ3׉¦žWÐ-Æ—WJÝR8ÌÅ i\88âQ)AU|ìNãÊHG½tq ñ>À ´ƒŽ ¢yÞ°àTVfe9 ª¡è—ÿ`ç¨ÕÛìêT‚® F[’3ƒÄÐ&a'èäQ ÛLÌEƒæ"³¤*cÜë*ÙtŽ›ŽÃ¹oˆAãÝBÎafsÕ­'I`Œ¾ÙkÜÌJ™áš Q3H,AÌ…Š>=ÖB(-Cf‹ð| ®Ö[²¨•ØÒÎëR¦¥±ú {ÉqŸwÆTµ„ò ß}xk£Tõ¯eíB•‰.Éé™Ùø–âÕŠ8%Ð$—°“Tâãš³ B2/¤}f+—=îUЄЉçlŽÍk‰ûíÐÖ$¿’j`G¨‰ŽƒeK-]6ÆiÎÇÐ.‹VÕ[ü¿ßTت–øŠc—±_ü«® ¿æVCþýœhÙ¾ÊJÞýYøÂ#þs3ب_xPàÜn¶çÆ•p²?‚c°]L—ñä¼ÐnNÏ*4[@¡it¹ÒÌ|´ýMß©ôª¸ þ¹ÿ<~ô`”T^á›*/Rßéð%"¿Ä‡a¥7Ý(:§·Ü(r~$Æ/ó6L;õà¯( ˜ç%”.@ê;@«Úe‚~"ye;“‘b˜é»N¶ÚjÄó ”ÀŠ‹À Œ™.]nœÅ ©ÿ}BýË€uMm3†ªås@:¡è2ʼŸé2¤ÿútž•ÒÛˤ<ïÜJºÁ•>~Láí·QvXíà?Ál0Ùoˆâ¾ƒ&b.æ6ÈÜ‹ -„hœûׄ]yVSAc8‘CR«ŽüÇÒîË÷hT±¨Sm”ÉÛ«Zmq§ƒç)JOÿòd Œž#žMç)T_ÏÕ/Ã˰A;Þ•âÔçÉB7Aú=t†,s‹x7B/ó,aþ³äÉ3 `Ñ MØÃß‚| ey6£^èÝNî;DAýîh²ãUVÎ&ºz©æ-÷­™€e²«‡‡ݲÿ)úô¹@o?*ªÑ»µ™2læ¿Q¨`¥œø¦’P"š‹ˆ´5…ºE›ñ€øÙÌt Åý:øÀóµÄXæd¢N¤wΉ“‰D‰8•AörŠp¼N â ³òe]oMŒÉ݆H£Ã{Æ{a‰oÔ¹Áo¨Öœ÷º«›¬NÏXØ&k™ó"o½rDPe4×±.«.gÀçɆÖc1–ŒJŠ…)âÄ€`8b#Û-Ûc×·Sšl‰AÆäëo%&=>/cXÙ{höПbmŽã³‰u¥¿8—:H-CÓzUZ*÷Ÿ/ŽOˆÇä’ýŠ£òû\A©ÉbäŸTq±)É@I¡ù;)ÿ…"F8QŽË UOeŽÒAùµ ’C¹§¢CP[ÄP³qßÇÜ{¯ÆRÆDìßÖ|Uy[M·žßW?Ú£z*£šQH{¢ ìgYZlÊõb*£É²áÑÿY—áU,€Åh0D#Ó]b^‹g’º÷¸%SÀ­’⃆5ir¡¦aÐÆg`¾èÝUNrD´B%({:6¬_±Y¿ò&'˜Ã,m6ûbKfµ,g%åjìÔ.Õ‚«$: þ{Õ¥a_¬užSËZÓáSÊy¨¦{~1jwsÇDHžÚF¢d¯YÜ¢1g*ÇFMztÕ/8»u¬“6þLÑ%0×®Ó ¯W¬3lMŒYz8Ä ³?M™pue(zÀEW5 [š9› †ŽñUBOj i?OÝÒZ%²H3”©\ZDž3ô𠹗Ͱp$D­ŸšÝlìHEti~¡ËTÙ¬¯\7„­ÂÖ aËS7•’KÒqv…Ù“ò:ÖËuOØQí ãM}È{”Òpéé ö„)%ÊU÷„µ¯{ÂæÒŽuO˜×=añJ8Ù7üÓHXéÜèa¯!蟾à ˆü.Ø€åúÅ”½lR ¸i2®m'œ¢ 1æ½HÔ{¯¢y£Ǥ›ê2, 3½J08µ=fv[/ÀEñ²ðw'°ªh<øžOöÄÔÝô"Éög|§ðø]â¡ß1G´v´Ý5_l¡’m}T%»ì?ȲôÑ»ñªïcA*–¯rÉw±§-ÖÁºbÝ׫òu@úS1—a™ôgfþƒ“Zžá;€o‘¹rõÆ<—߸ö¶lQPaQðÍaš^†ðcåµ<ƒŸ‚Ív%-‰õ†qYÊýÀEÙ´:˜š»_C]éGŽn·þW–NK{{w²Î¹\-Œ}w)f߆è:Õä– ±ÁCǬ͘k’ƒ\šHâ©zdôÐó\H’¶Ó†ÿ’–˼ÅÞ(¡¾'ri)"˜ó¾k)QÐÔIk÷ö¶ÛŒú.Ʀ2©vKÂ(Z;Š/jéá‹Zã ³9tÍþÌaèöì@l>+#J’ü,ûüÍKn)ìáµ­ñïlx8_º"…-Aí õý1AùFô(À7(I]EIf‹*O3\Œ™„/ùK©eš«BøéÓÓÞÿâ%'v6Íc©bö—÷ÕòB9Ã9wîÆ_J®êuGë-Díÿð$!ï\Ö zŽr«þÂtRН÷£­¼Ûçœîó†ü„±ã/©æ}îOPïG«}òÿL…nêC[Úc®ªÇT}@/šü Š­ö¦™ŠI>(FÉÌcbåñUBË.R §Lÿrrc”¹}ú–ú4ÞÜÍŽ¿oóänæH”Žuñn™â]ÍaûŽFíÎþ|äÞTëà¿*{nò¸kJµendstream endobj 536 0 obj 3584 endobj 543 0 obj <> stream xœíËŽ\GLHƒHB ØÎƒ»`z|ëuëÖ%~ˆ0$ ÛQÀŽ9“±l€„`,b¡‚Ä …§„„^³`Ç’¿¡çÔ­S]·=ݾ3Óm_d÷™zw:uªg§ª7YU»øÿüöäØ3Jªê¥k“‰hêŠK]mûOÚðººâ -ò žªš™J‡ßÜœ™Ú¾ßÁG#Ĥ YŠ´ãcàòG}odN€ óA ¡2¼ažùyU5•u'x…4|:è!YíZ<ü ¿ª®–L%âTƒ#ñówx!ºpÔV ”‡B ÔK+P>Ca¡£d¦GalJ‘E™µd&€âPœ9™9i~ lÀÒh+>QZ*àU£ª|ÒƒVÜ:á^ä%eÎXÊãPÄ2ŠHé·}éA'>G_5j¨WŽ6“è©­ÉWìÏÎDeýX­¬{s•àÖñXŸ&eøNÑÃm:ƒqú7óÁz;ë+-”sa²µ¦ÒTW_œ\üŒÕ×664ûðëè%“qIsømSç³Ýe»q€†>f›Ão%ŸÁ=ý^)ëßuÛÚƒ§ÎÇêÌTiU ËÖ=EMÐmfé<ÙìP.=g¨çÞÅKSQuˆ2ªBS• Q#TË¿õ(8&•è¦í:UɆ¥hgUó õb‹•!hk¾>?×{‹KG< ÛŸl dHOdòE?Kç-Šòiˆ_®gpÞªG ø™”ã–‰ˆÃ#° qhË’f0¶(`j@t°àqîw­dç®Tƒîê~¦\%8í6Ø&QM†XQrƒ+OÌ¡•qà 1¯a>Ñž†7•êh2Ȥ¬S.¶f}eîœ[hüyÑ\y¶³R•:Eq*µ5I²,Ñ0˜¤µ‰’¾G“lSiÝB–ñ²¹ã™'~¿Ÿ†3„BÄ;°šoòJ[«q{D) ÉB%ê×ÚÂno§´Ê/µÛ^¹hfö°eN+L0·›)±i<¦¥(†ñ‹9Z™¹|b(j(‡Á’Óèhʸµe!‚f½ë…ÎÿÑÛ«ªÚþÉÇŽâ"êþ€$»£Äºÿ•l#¡>3,¤«¤«ÕÒâB&Îx_º”Á½ãïà5aæù¹8Ìü7Ø…R$zB.Ý“®b9®¸«†‰5Nõg`vþg§M¨5l ­i•ý§1 ”æk   ²g<è¬wg=M¥´‡ß"Žá9Ù(@Ýø:ø$¿áÙˆ;Õÿ`¨õ ~˜Z_÷­hW¯‘e^€e` N;…sѳ®Q_‘"E^FŠ€‚@‘uÂ@áë ídr±|†LÎÁÜ”=vm˜ü'ï7 …ßô uG…Ýq>âùZ>f àOA—–\ìÊ ¶‡çp v XìWs @~ ¸8.*.ö&è™MEqï-.E! W½é!Üš–[I£&ÝÄv@ü$ábR­^Pqð·è†½ìÖgnÓ´àÂ~æWÀ]áS™Ÿ‚Ý ÀwÀ_‡Î¥¡Øöj`º¤·ÁíÓyQÚWˆá_S¯»ž–aßö¼µ†6ÛÈ{2-.z,ˆý tua®ÏÁ`ä´í[qË?_#å>':zSŸ$|ÕÍC¯D §ÛãàØ@?®ù]Ÿ7¶?üæTô“:AT#¢'¨ö^•©»Ù"Þ-2ç€7¢2&DÙð ÀÂÌæD¢zŽ*dZd(w"ß6šÏq@ÀhN16A;d®D 'fuL´3ÊP÷‹s.ŠN¼OqXÝì£â4.èî¬Î ”¢9mjÍ˽UâAQj3¨Úi%üvΘp@õéáÂØ0:; Ó„KÌ›-{ZŠ×5![ARy='4šÌ>D”¬p žIÇãHuÙwzz-Yv tÍÃiN¹ëÓ9¼C±-SËGÑÓ饧é2&»ZxsE†c®è»YI;Òs¿±L†Þßó È¬ƒ¦˜Ý¹,„£D‰‡–fn³«h-Ø9e³Å™÷𻢍ɒDYK5ǼÂc`|Ù/§ë#QEù[§mOýL¨}׋§IoÞd¥E§¢;ɆÌrþyÓv²jì¿ár§ìÝæ8Æ.É×sS2ÇãóÇ>ŒÖ“dq» 2Ê_‹©Â=ÖR9@•’¼:@i5OÀ‰ði¯·V5Úpûë À/ú Tlþ’ƒYÝbûŒt< öÆ{ Y¾ÜñùºT–LÚÃ¥fSxÆ~]Ä6[h\ ôp nàãY Ÿ%rfHì[DL7¡k:‘¦ù´ÙÜT<“­Eþið²‘OC$˜00ZBo\æ24^5J…oʼnƒÇ]+ ýKpÿ·™èÊPwˆ5‘Æ]²‰¦ÉÜs%=’±óà>€¥Ô“¢™ß8óóÐ hI4ñÛå(®ƒØ[™¨;F#gb€Ñôâ•9ä½uì<\JL±RbRÆ$JáDëttÎ ¼KÀ¼ŠŠDæÎ2?΋$‰¡Ìšqi©Ý§ƒ«ä"^ô¡šÎã%í¥À Aäæ‚`€OpQHõ%ÐX"·N¡·a³HD5âDä_Àã²_C‰[£™µM¬æ$±°Ó‘uM2‘ƉN$L†F|ÊØÊJ&‰ü¿Qbƒ‰9ŒÕµ…)—–¦Vö¢•%É„ÎÔdSÑ+z f†æÂ†JŠkÔÕ²£.œŽ¤i©#•ÉAçxÜGû[ù»=bž+˜ÏÉØy:§¦@rgFQ‚;!zíè(«£Ï=M6$ê0•9WBóÑl_}7æªw“E¾ìN N%÷½Kåíj íŠÕ>ÃÂË<뼂0p/ˆQ´hiy D/ZNDQ4ý¨çlÊ.9òÀ¨gëñK™l®[Em5ïš4Š„Þø”õKWÍ"Dª.^ÄÎ˱àíyŽü?ü4h´E¾P‰!^ŒÅ u%ðË7\4”0Ômy R‰fé~×Óòì:LѱîºoŽÄËÀ¥ JLÊÅOÇ«'{°M½MÜÑg C®$ö`›qˆš8"5Ç=ê·™„î–ñ;£^h{ã”ÿɉSæô,¿v^yEÎb2Qÿë»v©‡b订³ r¯³…Qgž@+HÝŸ³pbªÛÄžâ‰å>Ÿò&wê ;/c5¬š›f¬F?½ÿ¹6çMºE ™ñ©Y_e½Â“©)Í‚$mJR7–³›’…$G„ß» oå*D€ÂÎÉo ûcLf•L?s*]BpÕa¢«'Ücú’T5›6-¼pþ½×G9SAÐÅÂhü7ëœtúÆ­ç µôN"lÆÈu}Ï·#‘ î! Ú=t¥†ÃÜŸrá®c¥†”noé¥iTœ‡¢øŠ2¥Êúh++TYì1Ýà·ç†oZöJ÷@Ò1b…kIJeNÙµ{¸Ù5.¬MསáJmJ¯ z¥îÒ]”¢ÂMw±"£ï‘å.˜ÙКžƒ Öвºò Qß3yE="/=‚¥e¤æ+V€=êyº^~17¿Ð'BÝî«1Z™Â5V]Ä"}g¬hóT6›!­2Ì[ÚV7´0EîÛNÒ¡îÛ:HÿÒ‡Öë/ŒÅqwNqÜÒ,èìæ:Ê"Õ2™ýŒî²â z—+Ú¨ŽN¹±6¢ËøˆÙí]ã˜ÊËøÆ2¾,K:–ñ%ËøÖ°œh,ãKs tt,ã»#ìn,ã»Kíj,ã»0–ñe|cߥ±Œo,ãËøÆ2¾Ýx)ŸÊr?ÿch¿endstream endobj 544 0 obj 3527 endobj 549 0 obj <> stream xœí[Ï%EQPý0rÓÄñò€Æs˜îéž™ŽšpÙ•Tâú‹Â~„]ˆ<ø`BHLIôoø¢ñ? jô‰„Wý-ö¥ªgª§»gæœùndó%»§OO÷TUWUWUWõ¹T”kV”æþjoçÎsR²âÙ—v.íTuYpÑ{öS£xY\Ü‘²¥ŸÍw~¼óHñÂŽ«8w|¸ü¬ž…¹ÏðßS{Å=»;wÞ]°b÷¾we>TZ·E%Ö²ØÝÛ¹ãC_Þ}n§n ý¦Ý§wî¸Ê6y±â¶yµi¶nºîÛnQ¸ÖGloU¸g?jZª.Xk›'LS*xôû(ó}ÌNTBïÇ3­ó/ØyI§¬¡u­}´)*×ü„iò¶XUIpe;ãáVnç/ÛN UÆÑõ"PŸ4­¦‚–%ðJ–Lûæ: ÌJ0íëM»ÂÑØbÂ6o YhCGGåíÓH×b¸˜7É–âEåæù”W+Qv /‡OÃîuÒ´„~VÃaÚŸ±omŠšõ¦ªüTƒoFäsƒ;8n!¼pÊ–°(·B yÊa/ô‚Úæm0^tŠÌôYÛÇHƒÌZ24oÆ¡0s2s¯ûv!hžˆÖËWÅ^åà*‘U>g›z¹›õ<-)qN€d¡ÜE(û‹Rõñ×ÏÑÝ"h˜x†_äPËm°¢gvw¾§ÿ.í%µ+¥Vo棬¸VÁ ×~ÉëÁûq§™±Á´z-Ûv-ìör—•,U0Ç:÷{;NyÞv¶…pŒô¢mêͦv|÷MÛÖš°±Í=èÖ¼bšïÁT+Í™¦]ÁdÀ•Û¿Is4s¯ZáÔ²›Z òDÔµÎâÌ0Utæ ºÞP½‡ÂN jp,bä^t”0Pãu«¡Qºÿ‹P@ïP*…3ŸE긧ß ÉhV ìÛÊ¿Þfðo‚\‚áeæÕ” Êt;¹ˆÝu7ÚãŒ3¯@1݃ëÂ{B0¬ÙÀ„ÞµÊ˰±®`«¹›²æi˜¬$$é³yqÕ½X«ZéxÉnñ‚ë}¾9M)ú"™;JßÁÂ"EîB>gIúñ’‘µÑÆÎ}–,ƽ~1ê4 Bú6Hß=²ƒXúÓ0³*˜Kz½…XœÈ%¤5¥¤Ì(Ð4‰ vB„.yÊ:ô0•iÚPyce·ˆ–5ˆký6¢‹$.èì½eO”$%›–kB¶Y‡U²= ±&sƒTUI%¼ƒíyŽŒ¬×¼h$_+»ÓüÙò£¶þ$ïÙ;¸h8Aç;`ƒ­šªg²!cö]`9roî¼ 3»‰ ;Tƒ¯Röš%ƒÙnÉÌ~[k"¶ÛM;Æ+=]# È­h¶õ\9oÑ_Cç¤_>ÕÂ!R*Ä û€°/[„åÀV„æu@\¤«÷ƒ1˜âÚÃmêRó‡!Ä.®wSOD¦ÁKܤW!I\­é~§Æ‹W1à½kFÏõ „ ÙQW“¤F†c¥ž`Žãʉ`«?BÇ\ Tlöóú1ކÖâr"7jîê».Ɔ’Οrá êz‡üç}⣳h¤C#©pJ†Ã'{®_àùZWBà ³Ñ÷"û¸–)§ûj”Ô õ{p´ ´HÇ :@„„$`e›Vú‚}KãWÞÒCÝ-áOçðEÛ©µ16&£è)>ਜfoü›„Á7˜S«Áìä®ùÓdeé!€¦†Ð|ÅÂÝ¡Iåõ@ aÝe‰t'˜%F¾U%2€7Gu¦~YÃz³sœÝÚMh;ÝMŸÿ–Þï•ÒOÃ庱»E÷H›J;H1W´È…ß”çØ:µ\‹òqtû+/òU'±)#d6§F¬³5j¿tX ‰%mdj°!–‡oh¬x©q5Ç6æ«û­âÕõñ€m7¨M¾ Š »¿ëÔE‹ýÿ6í;ïqÈ÷¦±'HV!ž¸á‡ÇB[úav ‚„g¾Z¸êº‚ã¢ï[z¶ÞÝÅ·<Läz €CùìxͤàýÀ¶›š‚€•½¡¸ïöIeâö0æú>©´ËüxȦpƒÎŸ®gõId @ñUõã5t>ŠbíÇ`jh¾KÀþb ôx˜ÌüCè…NÄ`90…ó–¶íÌ7C¯£*†Ç°¶“\-†ž›þUé› x+וö…›umùìJíJíJ-Œ0KkÈnc¥õÃ4•ѽntàÏR»Á›: û’ûíý‘ÓÝý5AY˵iRs‘cdƒjеåÉíÙ߆F(íš÷°?F(M‹X-g„ªzÝð<34Iûbªs®/ÞÁ™QNÑR"QçWë&mà9dŒ‡Âc`2·É:¦§xD6 Å€ªy2EUx’½…Å}”¤¾åfÁÖÒ>Åæ9ªÝÖê¶§}rl‘!}ZÄ_€pp°OÊ'N÷‚ø&EuÆ#«”1š2eÀSK™ÇN^i ,­~c¢ÕD…0¶}4Á´ §Å ô bFΕֆژAmxD}ç pç}<ªib«E4TÖÎz€DK‡Ó&L™ÞqÂë>çïÔ¾;¬é³*y }ìbë”õ#kZµöáB71ÆÏ[«½‚ög ¡c«ÝÒh~ÖqÛ,nTÅÔ Wó™T¯´ZŸ⣎ûJȚͰܨK‘±4Ol¡BÑ­™;`Ìr^9#²¹”œ-ÃV5†­¢9n‰¬ãÙf_»=Cm Fç¼5‚Ãm`ÒLÙ°l$ò:'~ǽ P§†[&ŸÕN©ÔÄÀàƒ&ÝNExšÆêƒd‰Ø!C뾌ôf22~<•TÁÆÑFÅ1f·V›_Æúú𖵿òö;Í×-YGVÑŠØ\pF†g9·…šuž¯®ÓBÙ>ÅÞO›nÃùjØœÿ żbO=¡Àª\ÖË sKÛºshÿel6îp/mD'ãÂÞ\(GOìë±p‡ Nq¨ fEm%„~”-å{ÌÚ4UÞ6©£’w@ºYúÐި蚤ŸÔ9oÛÖI¤–Q94Ë“ÖØì|€jÓúƒV¥œ“˜E˜s9ý yËâ¢êCˆq¢‹3uÚ` œ•í³µf_êñG¯£`dË ·jiR_`"Ïžñ2J÷ñC˜‰©G'º=`GSuБÍx¨2–ÎH·‘ø.Ÿç¹ÀXqa®á)U˜ºn¬üx–?‚g³ø• IþÂú§]s_pÎ4Êù­„;<0²%vA : ¼bwX¸´ó.6–<®|’ú”Ÿæß&ÒüQ^!A^“Ô=…¥°r?!ÂòðòdöBIíR®ÛBÔ ä §”Ýœ çõ×u$b8I3›¿4R¦(±ì.¬ÌYÞþ¸NŠy½äޤ‘äŽ0½Í0ž*GÎËîŒ7$çE“nÇbÖ_æ˜Ùü‡ªÓ øÈúšj†¾‰æ½²¿ÚÕ¢-øw4þ0}…ó±Áඵ連Aξ±nZíš û2¡ >aYcK·“âÇ¥›d>í¯ ‡GesSå•À;ЉÌ‡Î?;UeI8Ûèòä“A'¹!£)ÈÉ ÊÛ‘;Aê(KŽºîmW–¶´<ÎUºTpÈ1ë tˆÐ£ˆé²ÿÙ±­7>o»5õ›âŠÜ1¹ª„)¸RŽß–6Y‰4ˆÖkuUpgI÷°«Sƒ‡Ë^K÷:8î8à½Ï‘g÷e°ÿ~2Bªé|}d´,ÌÜÛ¶íkÿn)­}¶ ½“Êç½o›…û.(˜ß_„õ†æ—`&ìþ¨|¾šMН-\ç×í´¨r¥‰`–îM#s¶Ú®h—ÞX‡×ûÑÕöüëªþÊ’ðoC8¥Tž%`œ%øu—öç× Z”džŠaiéŒE°ÑÜúŒ1tyg’yŤRÈÝû*VL±ãÇ¿µ@ÚXnðV”mQ¦)Zhl U”AMÀ’Y@¼ݹÛvEÇÕW|ð‡}e‚ÞHÖ0;uŃኈe‡îºÆ÷û@+´:nèƒl€$uÊ v¤‹¼Çɨ :ß‘ý‘r`N¦íÝB€ßH@ä^(O÷7g߯ñÏ—È–»%5´¼ îö<¤ýÙCžŠ™ ž\(þhßY˪)×­ m›} ·É˜»H¸¹w…Wb©KM«ªÛ£vó cÚË’õ‹XqµIL·A.{P)‘H'I• @wªÊsJò擄ÆH\1ç L>0·ç¡®}­}Vï„=Þ©L\εý/$CK>tx˰/—¨7rž\Ä)‚k…ÌJ•pK2éZ!Í‚ÊÍðK €lOZÜÞFIÜHô¬isºìÆêYû÷Ç ÿ¹ûÂ_—v¿×DÛ>(­kL©½ÚþÆ¡Iw áRü fÂ7=û# Þb{väð:rQN^ȳ7ù5¿±Úæ«À^ý‰ªKH}FÒ!¨4%qo}Þ ½`ö*ì®Kßác~Y„·îð¹ è@ “âˆýºCiÿ6ð)Rç¸Ê¬33b˜;Î{žÒrÄ*uÎÊnÄ8xa µY¶¸ïGP.{Kº1`më"LK®¡ñwòœfÿbéõì~ßIÕÖ'ÇñNš\|=œ—VÞ$Ê@R‡ÒÓ*5ã)Ueõx6.è³'c“¾·Áïàñl¬í"Ÿ4z»&+%þ@K¶ỂYd¿NäbDó$³•0#‰üaHɦ… ÁÏ,e2›d­GüD¢"Ç[«L‡ìÕ¹áuZŸO¹î g.Æñfä‚gÓUU¾ò½¦•ï¿Q/v´’M©šx…ÇÌstÑÒ^»¹OÊ\w%Á6tp÷³RÏ ŠÈžF‹¢½áº[eR“Ь”Äu~qnVVs5VS.ôã\Y«ÝSæ`7óeS4·Ú;=Oaˆ.|¾ú1™O;n垘A¾á)肦ƒV·¥©LV,f=„©#Ã@c•#æDœ­ãëfà@çÓ€PîʰTaD(F6Ô¦˜¸)Ž¥¹ÏJ¯ÍºÁÝdÙÂòÜ¥qR *rºlÄMIQ‹¦ñze{MNµPÏËçÐÐÈÙ°À®Ï­Aj˜{ËÔþ— ”Цæa0rDÚ²Õ„¨žçkÍ•¨Œ/O<=dÖñ,Ž—2T7KÚOãõÞ¹›’FÓü£Ü1AºÌîýý‘„…ïù‹šÚ±2¿Í6óé—Hy·gÊ5¹<4Sتzò™O;jgåÃíWi~G§›G®#R˜äXÑŸÝPeU0؆å&/ñÆöÅŽ0™þgù#L^Jø ƒ#t„Y·óNyP!Ù‡ÍA >}Uš°  ñ8â÷©ÒdÓ›6ÊúñWŽàaÂDË?v™ qh÷ƒ«¶öÇsuÿ-¹Z+“ºÛxl,·ëÊød`L åÖ>”ûk\“C å6FäÖ—E­ú‘&Læc 6æØ-M;•¬}þÑ:&ÌÆrœ­g¥–5U)–·œ}&ÎQ1œ-ºæïÿ‹ †Õendstream endobj 550 0 obj 4729 endobj 558 0 obj <> stream xœí]¯$EUV\d4‚²°ìÊj«Ä f.]ÕUÕÝÑì²€ш°KPï»æ‚Ñâ1? ñE‚Æã?0>é«>ó;|öÙú8§ºNuuÍôLß¹³a²;çvUõ©óU§êœS}X”{¬(Í?øÿéƒÙÝKUϽ:;œUúuq`Õ-/‹—gR6ô·iñòìùÙcÅ•™«xøAøñÊszæ~ÃOç÷gwßW°bÿÙ¾wn~Tu»×•Ø“ÅþÁì®÷|fÿÅ™j ý¦ýgfw]gA^̹O°©5è¿×>…ƒ®·O«Âµ}ŸZU°Æ‚' ([hzƒmÊü@ï·•ðôÆ t銗<”  ئuQ9ðƒäM1¯ѕ͈ƀ¹]zÅ>”ðÐN•ñQtO©¨®²žË’µEíþrPf®{øfWØ!&,øa˜! ‘6ttTþˆmÄqCfÞs„Z^TnœSv\YÌEÙ1^ân…ƺÍ@B·Õxø´}k]( Uù¡Ü t¾'ŸëÜáq†ÈÂYÛYS> Ê”›½Ð µàÐ^t–Œtú†3Ò(³†ŒàŠ3'#?:àÉTo;*õ*‡W‰¢òq jv×õ<-)qN‚f®ÜwE,C¦Táüu[ ºc‚ƉgäµF µÂÑD½¸?û†þw8­Ôv¬”Ú¼™Ÿ²âÚðh›&DØFÞÂí…1ò×û¡­6ˆ…*e£;‹F«Š*^ùîìÙÏjymü¥-&üÙ[É _ðØýµVñh‹qàŠ÷zYàÁýÇî¯Bôp@K?ÂÀKµWµ¶ò•5ð_³W…{ú(P LÓchö´*ø[® ·c¡yý6†7]¶°VЩK—àÕ²¶ð뀙ëý†…ÐÞ>ŽFÆz `÷ô èéü%¾×/€êBÏo[Íí4ùÀ ¦ÿLŸ"©µàŸÃà0ZŒ×eú²_£ï0òesgVžÄy©€UkÞ ¹ßÞ‘îú€«®Û·¤9ô~›ÃË]sývè~™¸4¿°Y,)õ‰p#Í$ëë Äsùïx5\ËçlŒËYWJ+£Qëö¥¨*w‚«btÑNP›F€ß²pCŸK´Wˆâ\/»–†Îúž%„– '_o"¡È°hî÷zä¨|/Z 7ÖЙ9ž€-+gëj;Žt¼¨£ÝO~ˆhðKžÎA8ÒìÕ€ÚûÓ«õ8K×àˆ `ò6P¯ &]S¼TÝ¢{ L4å!@&¤gãeë<ÀC*Koâòð!ë!Á…>O«¦Gür˜–Y;U;GÇ5þ’“4µY®cÚ©+o!Þ âùª¥UãÇÅÎ0¯?¸Y¹Ä0Mü’pÕÛp×úÏTm.úU°D·Eç÷%ºx€å E¤ôBñ²}Z!"dQ¼?Á?î‹„ /Ã$–¦ˆ5¢€(pÑq ×ÐØ‡Nê9µ¯írˆÙÎ~ß„ŠPÒ·LQ™²GQºjÇæ7ÔlN©+Îõ`ÜMĦоޠÍk7`aqásk±ÝvÕønÜV»‘®Ã®Ü½Ûm*®Û7À,èÞ.êÊè~m.$JD‚n)Ý! €[÷ð- OŠn›“êH‘õûaØ?Þ Bà "aŽD2jìµ› œWÕ-9 hÅäF\(œ:úŠð¨ ½ÛÞú}·Šz,èÑ×í„2‰c1/}):¡<y6)º(£Ä:< d$"2ŠÚö|´B¸_EcnhÖWš.ÑÂ4‘@Œ$Cët rÝÐã²áã.§PküDþDð]köD} Fw­ÿ †Â/^€LO«‰§ îJÁ Ç"å)Šòšá‚·¨Ïà*˜ð¾àäñÚ¶¥±$ó8 䀘Gcieв ½bé½âÔì°#m­Òf•qŸÃ<Ø!”ÌúÁÜ¿ú^ªS‰Èk/")¢æv°ÞìáÇÍ)8͇­G[1‰(1‰R·ö&D½'-~£¹ŠhIýth*/uÂ3Â…Ž¿r‹­Š3¶B²:çd@:ri Ý>þ*JÙ¹®7Ó˜šRãV¥Ã»,[RÉQÒ4•ˆ Öm¸Ad6`T«É’y¤÷»ßvtGRwa~>ø¦Î)ý‡c7£­÷¦“˜ ¯ƒW ù]žŸ0g„ŠW‹{‹d‰ÙiœÿÞ„s×KU º6-¡¬WŽ4¥¼l%SªDÏ&‡¢G<Œ£Êk™snò|Û¥2µrY¤lªã²³Êd—ˆ(aŒ5Ú  KüËÎÂBÚæÊç!ò;”f©—YÐJ§¥jÚª¼¯  ´¡TÑŠÔÓYrøj'B°2à¤DãâR/v¢q—ɵÂêN²XZÆ‘Y–T‘ÍvÁJOÒ«bñH¡Twó‹üÑÞôqìÔ‹ç¼*%Ñã¨õ+~ßKð¶gµØ˜Â5¥\ëX÷ØÃ¼M³ Ž[ǺQH1ËaÍSÙA-£¹2ÿ§gËJ¤B‰8®FÉógÇQzÙ¦’‹âƒ|šR@´^À³|V~T‚Õ+cY{é^¦P4^ß4ÎÓ{õ£wls!4b¬ªB:õËضUd4Ç,GUƒËÑ_ÁPÍÁ=À{äü¡c/”jÚÒ• ©è4Þy!±ƒûXmr˜´ŒoTõ =lŠÈ§vN٪Ŵ¿Uæ¼IîµíFЉú¨D±õ¢x=XÓPƒ;6N#üN2)© Gv9onÝú® ÏÝò{¯¹¨ÊÖû=Ç¿K¹Km’þgP£–†ÑMÈÄUfáíŽZØjCçef•/Àù4Ø–ºË'•*ã3·“õQù@²*ñ´pq%ÙˆÔâ¨TÌûsSç±v óK½gÕ 5)H¦×Es¾îxè T!/ë… À+O¨Á Ù¢9Ö1&ÊKæß» ØdGÎ?5T¾±ÈôóºÉ“z„èwµ™–´¼*Óo&õw,®*d‚cáÝiwöã6œ‘ ¼UFë;ðŒ—*¤$¤%‡¹ÚHßñžDÇ• 'Q&OÓÖ|hð•ª,o[§H‹KÍ:–M¯F`ÙRÚÉMË@bæf–åtžF--åD+A±Œ`PÑ®Èéö€´¦k`Í/%e¦Ö1#ÜD€éÃÙÍÖ'hß ë¬Z¬bÓÈ«yu8Iz> ¨2wÝÚG;/dç… tÅÉí¼‹²óBÞ5^ˆ?eJ‰÷¦œ’4‰;I|'–é”+B_Öµ^FBó·o0x?•§14ÇØÚŒqãŒÜ¬w™æ±ßÖ¡g·\oãm¼æ|êÛ:ºïnëˆ>§à¿ƒý;náwžYëßÞÁY_€¸†nï]e÷ôWðÔaø{t-œðìnòxfw“Ç&oò 8S^§ÁÚn@Ú]§±»NÃs`wÆî:Ýuƒ’µ»NƒðywÆî:Ýu¡lí®Ó¸F®ÓX·º+¿©2ùN3Qs™V¹ÒôD¹Q.clAé|/Ö…FW ,]§I"£Ò É]I>µ)ºÉ>™}fRç\ßÉ«©òuãÛR>PÅ?öS3žfkß*°j]¢>mQýÈVÔá›RZæÓ’ ëÔÞ«èã@cJ:z_'9+• ÔyÞ/›"ªU@ŠÓÛrÛ²õµìí4uq täš«ZŸl‘Ôn­ùJ7ãCÍwß³™ µî¾û ÔèµêhîÜ÷¥¼í÷ì'úŠ'ù6ÑŠ7ÿåêz—‘ßüçU¯z“쯛ãqÿËaŒê¬“”#Lw̹Yþô'Ÿ3•!9q±7uñáÆJ÷<ÞDÝ!Δwƒµ[ø]¼kñus“*V˜]ó§ çéwaRÚ5}@DÔº4 ]Î~ðš söR"ù™¼V«ò./¢Æ¹¼|›gdþýZõendstream endobj 559 0 obj 4410 endobj 571 0 obj <> stream xœíIÏG•8Á $^‚rH@3êª(‹³!¾$ 8°ãÇŸás8"±dq GPàŠD ,Bâ€Ä?„#gjy¯º^MuÏòÍ|žˆ–¥d^×ööz]õÞ×U=cUíþÁÿ/îOÎ=#US½üêä`Ò´uÅ…ªöý/ex]]›H©éo×ãÚäÊäùêúÄÏU=óiøqãe; ¿á÷«ó{“sU¬Ú»<Áu§îG£ÌLW˜Éjoòà;Ú»:iueWÚ»4yð6òjÊ=xÌZY04ßî›E ;|kS…¾ïti+¦=xÜÒ@×wù®,Nôn?Q ­w@®ûyI£lz着&€ïu ×Õ´éEWê:k´]¸á%4zRWˆbhE¤Þç +ÝyOeÍL¥Â“»€3S;ÂÁïwpƒ£bƒ YŠ´ãcàò}odN€ ón ¡2¼jÂ<òóÊj*êNði¸:è^ Û×âáà~UUµ,™ª‰S  >‰Ä îð8Etá´,A(÷„:¨V ü0 ……N“™ÎÀØ”"‹2Ód&OâPœ9™9i> 6àñÒh+¾¦´TÀ«FU¹ßƒVÜ*á^ä%eÎq° 2”Ç¡ˆe*”&¥ßö¦!Xœø€¾*ÔP¯:“è“{“/Úa¤õcµ´îÍý” ·ŽÇú4!2ÀwŠnæ ÆéßÜëí¬C¬”àöQ%´5•¶ºñÒäòÇ­¾êØÐZ £—LÆ%Íái£óÙã o‹8@CóÍá©«ãÀx £êŽ ­“b‡ùæð”Õùlq·Ya“‘íŒWJóYã7™›Þ¾Ðõ<Š:µRwðg<Œ­ÞÝ TñPqèúßà[Påö~X øˆÏø0»Þ´Mü¼øD€- V­£¸ŸD8q–x*€h=Þ\ ¬f)rqÌ¿ý˜š`.t ™¶8ð‘Â@¤1'±C¢FRitá'hoÞ7yÊžtrxž÷OœTÚ»SÔwÊC™ó°Gó¼, «J«ÞCÚú¤^'PvðOɧ¢µîËJš;¯«Ëø‚CŸÀgôáEk*ú£¼Œ*Ìûål5Æ,íŒ2FXS˜ªÄ½Ò$–¡è› M,¡|XÑo•¯è×I o=¡FNST š­t¿ÁdòÙFšeØlGۺܛ•liV“›J†øo™c{J‘gnÑD&êÝæ¶kºA[š„„ìfv*”%’AXV´Ö&›žn?C*¦çïB7º¡KÇrLRÐ]‡°Äw@ÞdÉ$òôðT± |™36ƒœX12):ø–Îæ.yqtOÐ Š‡$Ö‰z fΖ²òˆâ·eÝùrjÏK;bù²¿CåÚf[í\žè¬–Cî!÷…=yîKÎ/)íòVWÖµžðŽnG˼Ýd ²õPçn×?ôó^.Ï…NÔ:¬KSîä¼^Ýñ È‹ú'Q–,&Û8Í+²b’õ¶¶š0¾ÇÇ•^…Å fVu¸×š·…sY1RiÉÖ^O,H“Ê_õJÇ[M×™‹03W{ß^[â½ |ÈMzÞÕ•séÔCÔm:MM²f&+©øÌø<‚—<Ç»dñ‹n0ßé~ŠƒÏ!f»@ö‰dÌhšH– ÕׯïùªIƲÕPÉò[Pk8c=I$¿ò1]F­Käik—®ÞX6õ[î64zÝDÓ΄´…\±]IºsGàà”‚µD™áFü©r ³ºÑ.:Τ®{A¼«ÇP>Ó’nцÝûP˜Úö´ÌâPŒôQ@ Ó÷>» € éعx}x€rIo\íXx7ý¤Ç ­™P$[z{*¦=LöJ—W·Ô:ëÞK[RÓûÝøjü0á^“+¨œh~ÿÉh/ÞA’ûœìÚ#fœoÕº‹O%øªÛVÚ‡ TήGLB!DV±¨gn]iªáÎb`ìâJ†±þe¬¹´Cõ/½E++‰yûÕ$SÆŒ©ÄV¼gơȒ1PL„ÖÖ*UÔV UŒ±ñ£ÐâÇ]«S1v¯šó–©¸“Hñ®DÌSÖ˜ËTºÂ”ïsCL¿@Ýoe‹à’Ô·_Ç‚rûÎ +½CëOˆwk\.5.c š×®Õ¸ˆZÁÙÔXã2Ö¸D Œ5.cËXãÒ«Yc ‘óXã2Ö¸Œ5.ó'$cËœk\v°Æeàä) `.m$X2bÁ_Ý™/€Ll+«Õ1G_Sºh—(¬‚xd>Ò5w3­},9âÝû§1Kùn„‚¡Òëûìбètd,zÙˆw‹^kÛXô2´ŽE/cÑËXô2½,­kcÑËXôrE/C¯äуì´ç+bþo+b¦BXziú6§Ä¬©¸áðgŒ—)‰q/ÜÇ/„¤Ÿ?Y¥ÄDYMæ¦öŸ!húëizº ^;_Î"Ë þ©Ñ]É—+LRc\­ÂDp¾Z…I›/<Ì1–.)IòÁX2ÖŸÌÕŸpÿ%£#(Šxûd³óz,ÉÙXçÕälóã"ãuZß“hvT¿¾ÞeÅéÅ•?2ŒôV?(B×r‚mÆŠT„pf ÞÛÅŠθÞtEHGñ®D¸®"„ÙÛ0Û›~çÇ8ö÷nèþ!UJÇ5x~{ª•QWÿŠ{lèôw¯SxhðgÒ™#3~lÚ °úKº°Á0ó®tÙXÏ÷'?³H‘ˆ(ãS2rj÷ckG¨®ÀD×±û>ãé來w¨X1I~Ë/[÷PÜVð–jéÊ2®qš°DáZÀІâ"#ûß2±9«YÅ·ZƒI÷@´Eë ¯YªE~ü1xÐ{g¯G髽Ëcƒ¹%ަ†=Ű!‘OTRwI¯­²Ãžì ±oh¥S¯7¢Uz(6÷]ÕXŸ>À¨bqëÑ ºÿßé¦Gendstream endobj 572 0 obj 3458 endobj 578 0 obj <> stream xœí=˲-IUt£8b«hsÚ4}Ù§+«*+«ð…DŽÁá¤gaÐD4¢„ø ×!s›ý :Ñopå'ÌÊ\++ת•«²öÞçœ{±ãFtï6iivSsê‹ÓµÓ—§ÖööwÃC ÃRMçpŠñ)Nê——–ë¡6ød[37.þåS°3'ÿÅÒþ•¥Ýãר2Ch¾+4ù¤—}Œ»ü«ámÜœØ2x˜¿k(¶æ®éc?¿úµÍih׃·¸†OÃ˱õKkðïúy,íß £ºf4YW}ê*®ÀÀǯââµ×y|†ÀÂãð±…Cy ZSqõƒ?ÐÐü,| =&=}¾ÍWä§l&Ò4?€CçÜ‘ž³Ç¯Aó‘ôµ?¾^*ΫEPù­ÐôÇí²ÝK{I7ç`ù´KŸâ,óCéóõûwaÓã!ø9u ¼:„Ð;Ñ?}zóuÿïý›a¶žŽµÖ“·å§í;OxŸ…w»uWüÚ<-'„0!N÷¯‡¯»'õ:Ò§>ã¼~Ê&Dz ;‹™Mln¯²QSš“xZ¾£øô?CkÀåã”r¼éØfÌ8ÿxSNpœÄKô´^!d퀈œŠÑu:6¦¾g=ÌWHëlò]špœO“g/ ¼ƒ.Œ@ZZ %×89góp…“í„e?"t_r¤`ÆÖEYÆD¹€áäñv°öÊøÁ9V÷ø6,TÕN0’4Š“°_š¶|‰ dÒ@aÃOÃèù¼W"íZyä;‘êY!"-39Àpü5ˆÚž| GĹÏ4 bðb«Ç1%¾½H 6 Q¶§r'ßa®Ÿ.çUÆñMGJùi‘Ún—~›ýœ€ï<ÃÕ9Qü“v‡rS˜$ÓÎDCª¥Ëˆ‹‚d2#}Ún˜ 4‘fr‚*òäs6ÚÓ Â¨zÂ[‘Ÿ™H¬3¥jŽ€c'cš#òåïÁÓVCk‡B¶$ wd\\å}s(W6@-`å…ó#*C\”eOƒ¼ˆ8üŰÛ ëÞØ ¶`:o¿º)mXñóóî{Ä·¿¦4â¹¾‰»Hì½ÇÞŸƒür>Tâ“?ÍW ñ ¨ET½ #Íé|¾˜wf¼x çÄ»‹o¿&‚pò„ìrÜ‚t`-âªÉ¶/ma·îW¹a¶tØa,PS¿$Ÿa ‚žm;vžÇvNJ ¶YgìèÿãÚ‹:•.MåI¾.dðçŸå_†Î_·}NÐ aLLmúÈ9€³íò¾û9‘cÞ7ü9­ÚÒ`ét…“÷á{¢ôÄß6Ivx‰æ¹Æ53u^õgç/ç÷n KBÊci«^ÈÞ4-s‘)vU¡ ¿‰Ý¢GT•>¥Ö#¤IÅP˜WÀƶ’uE±ÝŒ¯1õ…<Óê]%I”ig#µÙé’礉@l2ƒzYþ  M iþμ bÕœMâýqmÛ“„%{!æôý¥yœ‡ÃQ’\éÒŒóφ(zI ÿ;¶KâúíÆZZPHþ–CÄp«ÐŒ>Ÿ!K`ºFÂz&²&ŠüZZAü>(*¢9]®õ§Š&ãèýØx@ÂùÛKeÉÎd…Ñõ·.ðšž+Ïù)Lš? ë@ÊõÆ–‰D±²ËÏ*g—A*õfP²èI?¹*¯MëÿG PÿA î†ò‰õ¨¥ŽZ1Ë¢ª¼¯äòζ&8®óRª`ñâë) 7ýí°C™fÔ˜?ÌFåg,ûÈ<5Ñ[~É>_gýxðñ'~jväÀ3 úÒÞé’  µß‰¦3ްɡZ[ͧÇývˈ^Nä®Abò)raþá„ fbpé¸k¶Ó| *æ- Eb‘d9ÁŒÇé76[bæ¾"Xðöˆ¢NZˆê2–¨éØHø"Ò¡±`]¼.cÒÈ 5ѧE£ÿ–™-ŒÃ[Î Øw#!Ùáv¼g‚?sÒ'hó—ƒÏ¶`5Û_‹u·^µæçiFê¬Jˆ"÷yŽMn\Ý?;¾â¸ZäÁûm“'=y'™\qĦ­íÆÆº6{L:#³#;fKI,ÐÿböQž(Ü å5ç± 7Ú¹d£¿'cמH{L< Ã<7&÷Z2+œÂ—Ù­ŒÙ3\•»DÍE_\@ŒVXa^Ì ó¥ÃÃoÐh>²»¡õÝ–óää:3•kõô:Â%1 gÆìè:Ib§CšTˆ…ÏFÒ’Ê=‹9tæ®Åg¦E›Nû‡|&ÎåÇÝ„ëâb·0WÚT´cš%« _â‹a0¸¶b؇íD¤°Y&»/ˆ8qö5æ•Bè˜ìyŸˆvSi(x•ìšÄ¹%€ 4Nûjd?®íûÙåèœùê! G”»ƒlsâg”ãaËZ¹+ò“'Q2mŠ’è€Q3p_p¢«œü=Ƥ#¢QTŽ&i›ÒâËèÆÎ2µxÐC Ë¢l;²}¨8§þþXÐ9g#…îx²ç—4LGèÍ%âí:D²)ñš1Rävšq9ïsaô:ÄüÞ/§¢ö‰Šd”Pæ[ÌðÀYÄn8]5E>@Æ:‡f‡ÎéR`Ø ËOÈ·jÜ=g0M­ó•]O@¡N“dBRhâiÇÅýg–‚ ¢Ï4w†'0Yþr0¢^…-Æ-zµoŠt1eô€ ?^#ž^‡…æ,MÓ²à#}©~–°‰!®bìUaE ŽáÈ]iûR$¤é™~‘ÖGZ—àyÏ].Çù&jîê“P½Éí&<­Š“ØókR÷OrŠþˆXM¶I½è°qÛQ”ØèrŒƒpC]AðЬ®² K›Ô´“ÇÁ©¬liÜ7œÒ8.¢Nxö̪/²Ö1ŧœ95m×å]ÊCÙMë9#(ïâ›Ë¸ã¹ç&ËaóÏNÃ0-FÅq79h<]fD^ü_HÚmWìš /9x0ï‘÷IÌ[°‡R˜&ÖuÊc¤‹ gëDÊ/EÊ“¹ÆÛ#¯{ú…PÌg¸²#gc#ZŒü¢ïþ^IþæøXÀȮњ Ìs}™«“Ø™–ùRIE’T—¹•±M2pËë ˜èé–5)™ðŽ…yç$ãßHĪY9Á||\¦ƒIˆVqPª,†¾FàÉA÷a˜Ì÷ÏŽpªn®=šñˆ»S4‰÷¾rÒòè‚àWtž, óØß.EbÃÚÛ®ób–¹í¼b=ÌÃä: ðóÏH‡[çGÅgmöY›úóï…†3·^†_¿qkúKcÅg}öYŸõ÷î›7Æÿøþ‹5ë??⪠|lkrO÷‚©G“,±çLâ/EìÕ7™àè¨D¢ëN;q˜ š¬óP!eך¨†iLå+Nn1,yŽ$(<#[²ú5Z JÖA²)UI ¤oê*ó‘Ø“ûMȘYJOëÞ„¸‘·X–3û¡!·ÒÓɹ¸$®àÊ’°•F­óˆg¯E–…ÓórQ(rS^_:”‰å+ú$ÒÙ d…³ŠFû߉‹ØÉÂ푪cÍ$?­G~¢Æ1ëT\{,+¶Ib?y©ª’)¾»BH æHkŒ-g ’á º ¬çÔäû£GĈê,ìT¾p²™@4¯ìˆ \£²Èl™æq$Æ<—ë·]”Z¢—ÅP™ž ³Z !VjGÏ­– gD2f$¨6‹cUáŽýz6±IúËz¬H×å <­$ùÊþÛ-ޝÛ` Qq¼ôäof‰Ëm…Lk^µ­±3«Þ2šžè-c;¢žfz ë³þ¼¶õBÍ÷¸ž5ŒXË¡JÏ*ÄÔ‹H«+øŽ‚+óìo¼e;þø ƒe4ªP©®~ £Ó,è%‡ßÿ3«Ûá¶f++³§Ê¯;5C¶Þ4¥öË&eïòJÍ"y‰©´«ÁçèÆ,„hM­;ZàÔ1’u ˜õ¡Š>¯Þ`dMܳ~Fül MØ)£\X¼lLeV…#%¿.;ÓyLè[Šb#‘*$ uIø$Ò2#ÎkªH!mº£nxŠŒ2ä®Å!ó¥èâk–ü> ;æ»ÑVÙÎwLD*ÿ¦9ìÜܨiz,Ðø2ÁѹZL¯S~$AøXO2'*iQ3å‚}ˆwuxÇKíWV á¶Öptszùç„܆ÁKyG’›¸Ü`ªŒžýÃuaÕoæŠÚË[£vÊ ‡Š*%µåCòryÙÔÛ#¨JµŽ”ž)r 5—\bËv“îR6*jÙø£ê™s)Ï;zI‹™mŠ@ÔŒ%½àú䎛FÊeÝ©# „ ³ÝâJa­f°SbÚÓÀ”©½T"<ìöPA‡Ž›<ÇYЬsbyJEyÏr4<°ÁÊ5õ×(ÿ߯Âõ=cTN¶Ï½ ¨rWåü¬ òj8¾7¹[“«NCï©–!º‘•çõ8`}Øë‘¨pžïz° ×ÙNþƒÉ64;ˆšÀôb ”ª{EBubúпÀ|¡ùÃ0ÂÕ¥7yzêz‰Þ`R4›O¬œn_” ´ÕºFd#Ì[M+Þp'b\kwK-Å€âÖpžëú­Î­L{¡%ÆØ9åt=J«§‚?Ã7#ô½_îÈÝ3‹5…\òØÑ»žî@›xNU¶ÝBЪF§Whf¡¢Ž=S†ÕÝúîYÅŸ”TÞYOT:¸?õ”ÚuyOdnŠ~Æ?ËÜ¥>¾¯ -…H^rËK„O¨Ç~fä 8.¿èfñ·j™öë#àe=Å/°ÝÍ´_“ËOˆ»Q5Ì9zOj½iÈÖ‹›r…_M0•níÐBåTXK&5p /D‘ËÅ«D÷;¸ìön¬—¥œ²¿.=ØÈ`:ˆ^‡©¾j½ZXˆû²K÷oÔ¨¤{Á/DApáOKÜ +úz½;“<ƒM¨kü¿Âã<þKâýBq…í ‘ôXý¤+^GH/qÁ§‘ç˜t÷šnÔfo'_Ñe›¾füD=‰ÝAzým•,ó,æônn>õû“ƒÝXeF—6˜ ËIá×îK&§ÁâÁõ«ÊÅœ†E’DÊ!j€y–hô=aª5 ÐŽ½à¹!Ê(M¥£k\í¹_vVºxh¥¿Cë¢tÄÀ¿“ö²“ZR®..˶eºBC5 ×Ñ`¤\ôÖ]Bì$‚†EÏ0“#kÂe®Ä 7ÆËœ`ÎëXUóÌ· £x\ÅëTîØâÍ´çD’]±P©ZðåXazjaÞfóÔ{ºYy F3hYkàM½MæŠ? ÖkwK·«Â”]þú˜ÆÿŸüíìFÛ'd ´zÝzãâÏò¯‡µŠ½!ƒ•ªØÃÌl„D~´ºä~ÕnéÂÚÊ‹{;jÎ(Ü•¾žèíÄ. é`å™È.±Ò|V3%úa^0ΜOëdì’;ÔŠ„%i,Ù¿µÔ7ª¨i†t¹\q¡ND[vcz\ö߇çøö?G¸RBxÈx÷"ɘ©úž—ž òÅg/DQ6ÏmÒŒ9e,Ѳ+<™@@$[/ËðïÛç Jÿâ<¸…gF'î‘©Õ¤NËõ°ÊÚûµ%è±ýQØ#Ä.ñ'€íte¥ãâP™Ý²É;÷9•\ºPkt‹p’'óQ´›yγ¥´¶{1>I}«ÀÚ놙 n&á€÷êbïHc<§®Œ@õìD¯g° ìªr©Iª¾Ùl!óû“Ø ŠR¡o.—¬pueÖɨ®G,’mê[Óã*lûÑ}¥Iþºú,˜Ê'R_¯ÑëEfJ~æsÁ±&¶8è9š4"oéΕ[gh8t9žå‚Ñ{YtçňÇŸOaG«„_I\µåLÄÈ µ^ØžÖ^SÛx·ª#Û‡A‹€Øq§,5©q®ó•ûm)4Z%¯¾>©n%ð’Öl×ÓX ‰&ßcÑÈèSEÇ…rÓi¨‚–8“{Ôx²Ìr,ù® óB–Y'3pQQ‰h¾ÄqÂPES²fÚF#×”­Š;jwåZ, òšÍhgúP2Åè°Æ1†—G=î"i$?€aãÃÇV9·kªƒ±BøÓ6¡ …\ Okú§DæeVEþip~ªˆ/¸jÅY’µÂ殉Ï^PY§Põñ¬m“Kë‰Ñ…úa„ØjÚBU¼ ä¥îÐKnÿÿ‘êD6‚à r¦kÐü¦ ¬–õgД½¸õDDÊòÑ•õb´“hêcŠÊ8Ê•É#3tÓ@úÕð(óȈ›«ãYº0(ý»vÙÐÌkq8a[«ô»± pa\–5œ‹ÅÊy'1^V(ê*¸ペ~nÝ¢Tâ@ƒ­•ïZ$Ý1þ¼ÈÕgJ]’–ÄŠ$ïØ×—[ž:BÐ5[ÜD5šbJˆT%F—5VG*Ï5<îFc>ãP?rù÷ Šendstream endobj 579 0 obj 5504 endobj 4 0 obj <> /Contents 5 0 R >> endobj 73 0 obj <> /Contents 74 0 R >> endobj 83 0 obj <> /Contents 84 0 R >> endobj 100 0 obj <> /Contents 101 0 R >> endobj 183 0 obj <> /Contents 184 0 R >> endobj 223 0 obj <> /Contents 224 0 R >> endobj 244 0 obj <> /Contents 245 0 R >> endobj 251 0 obj <> /Contents 252 0 R >> endobj 260 0 obj <> /Contents 261 0 R >> endobj 266 0 obj <> /Contents 267 0 R >> endobj 276 0 obj <> /Contents 277 0 R >> endobj 284 0 obj <> /Contents 285 0 R >> endobj 297 0 obj <> /Contents 298 0 R >> endobj 306 0 obj <> /Contents 307 0 R >> endobj 315 0 obj <> /Contents 316 0 R >> endobj 323 0 obj <> /Contents 324 0 R >> endobj 334 0 obj <> /Contents 335 0 R >> endobj 340 0 obj <> /Contents 341 0 R >> endobj 355 0 obj <> /Contents 356 0 R >> endobj 362 0 obj <> /Contents 363 0 R >> endobj 372 0 obj <> /Contents 373 0 R >> endobj 378 0 obj <> /Contents 379 0 R >> endobj 386 0 obj <> /Contents 387 0 R >> endobj 398 0 obj <> /Contents 399 0 R >> endobj 408 0 obj <> /Contents 409 0 R >> endobj 414 0 obj <> /Contents 415 0 R >> endobj 421 0 obj <> /Contents 422 0 R >> endobj 427 0 obj <> /Contents 428 0 R >> endobj 439 0 obj <> /Contents 440 0 R >> endobj 445 0 obj <> /Contents 446 0 R >> endobj 454 0 obj <> /Contents 455 0 R >> endobj 460 0 obj <> /Contents 461 0 R >> endobj 467 0 obj <> /Contents 468 0 R >> endobj 473 0 obj <> /Contents 474 0 R >> endobj 480 0 obj <> /Contents 481 0 R >> endobj 486 0 obj <> /Contents 487 0 R >> endobj 492 0 obj <> /Contents 493 0 R >> endobj 499 0 obj <> /Contents 500 0 R >> endobj 505 0 obj <> /Contents 506 0 R >> endobj 514 0 obj <> /Contents 515 0 R >> endobj 522 0 obj <> /Contents 523 0 R >> endobj 528 0 obj <> /Contents 529 0 R >> endobj 534 0 obj <> /Contents 535 0 R >> endobj 542 0 obj <> /Contents 543 0 R >> endobj 548 0 obj <> /Contents 549 0 R >> endobj 557 0 obj <> /Contents 558 0 R >> endobj 570 0 obj <> /Contents 571 0 R >> endobj 577 0 obj <> /Contents 578 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 73 0 R 83 0 R 100 0 R 183 0 R 223 0 R 244 0 R 251 0 R 260 0 R 266 0 R 276 0 R 284 0 R 297 0 R 306 0 R 315 0 R 323 0 R 334 0 R 340 0 R 355 0 R 362 0 R 372 0 R 378 0 R 386 0 R 398 0 R 408 0 R 414 0 R 421 0 R 427 0 R 439 0 R 445 0 R 454 0 R 460 0 R 467 0 R 473 0 R 480 0 R 486 0 R 492 0 R 499 0 R 505 0 R 514 0 R 522 0 R 528 0 R 534 0 R 542 0 R 548 0 R 557 0 R 570 0 R 577 0 R ] /Count 48 >> endobj 1 0 obj <> endobj 7 0 obj <>endobj 11 0 obj <>stream 0 0 0 0 64 70 d1 64 0 0 70 0 0 cm BI /IM true /W 64 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A0ÿÈeŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿçÃD†ÿÿÿÿÿÿÄ EI endstream endobj 12 0 obj <>stream 0 0 0 0 57 52 d1 57 0 0 52 0 0 cm BI /IM true /W 57 /H 52 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ Ì3d×2Iè>ŸOßY8¹ ôŸ ýz¿¿_¿ÿÿïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷Aøãÿÿ€€ EI endstream endobj 13 0 obj <>stream 0 0 0 0 49 54 d1 49 0 0 54 0 0 cm BI /IM true /W 49 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡°Ð3È.ŸxOAè†è ú ½ Þ—íé?Þ“ÿ÷¤þ?ÿ|©ëù@edÕ?ÿÿúÛ_þ¶×ý´·ûKm-†Øav!ma¬0°Ád6|@ EI endstream endobj 14 0 obj <>stream 0 0 0 0 47 74 d1 47 0 0 74 0 0 cm BI /IM true /W 47 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ¹@h2@x ðƒ‹š‚¸ Ýá7O·]?w_äÔ57õÿúëôµë k„ d09c8…Áp¸>@¾˜0ðÞû{{÷ûÿœrÇøÿû¯_û^ëµí.Âða.+¬X0²F@Š@@ EI endstream endobj 15 0 obj <>stream 0 0 0 0 37 72 d1 37 0 0 72 0 0 cm BI /IM true /W 37 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¿ÿäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð}ðûá÷ÃäՇ߾|>øx€ EI endstream endobj 16 0 obj <>stream 0 0 0 0 90 70 d1 90 0 0 70 0 0 cm BI /IM true /W 90 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¨¨ OÿÿÈN pÿÿÿ ¢ßëýþ¿ßëýþ¿ßëýþ‰ þßéíþ—þßéíþ—þßéíþ—þßéíþ—þßéíþ—ÿ¤ÿ÷úOÿ¤ÿ÷úOÿ«ÿWú¿õõûõûõûõÆÈ@Ýw]Ö  EI endstream endobj 17 0 obj <>stream 0 0 0 0 51 54 d1 51 0 0 54 0 0 cm BI /IM true /W 51 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ƒ0Ì•á.¸O§ÓèÔCÁ:‹Iú}'éýâ¿ýÿþMWïÿ¿oïØ~Áû#v=ðø>A‚y *ÿåŽXëÇýv¿]¯ipÂðÁ.+¬0°a`ÂÈ8€ EI endstream endobj 18 0 obj <>stream 0 0 0 0 43 52 d1 43 0 0 52 0 0 cm BI /IM true /W 43 /H 52 /BPC 1 /D[1 0] /F/CCF /DP<> ID &äÔRp1ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿þÈ0:ø÷ïÚípÕÉÐÇÞ÷‡&¡•hø€ EI endstream endobj 19 0 obj <>stream 0 0 0 0 46 72 d1 46 0 0 72 0 0 cm BI /IM true /W 46 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID üš­ìœ2öû}¾ßo·ÛíöÈ5··············û{{··û}þýþßÿÿþPåñÿÿÚýv¿]¯iv¼0— %Åa…†°a`Ád¤@ EI endstream endobj 20 0 obj <>stream 0 0 0 0 49 74 d1 49 0 0 74 0 0 cm BI /IM true /W 49 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡°øl^¼ ðƒÓÓÑ oAô›Òo§Òoý&ÿÕ¾½oþŸýoÿúã¿ÿÿÿÿÿÿÿäÕkþ×ÿ÷¯û_­þ×zÿm/öÒívÒÛKa„¶+kk ,0Y Ÿ EI endstream endobj 21 0 obj <>stream 0 0 0 0 49 74 d1 49 0 0 74 0 0 cm BI /IM true /W 49 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ƒ@1"—Á‡ß!„| ßA¾›é¿~›û÷êñ~ÿ~ÿ÷ïò ‡ä8?AÜ'ÐèèAÓÒzO®¯¯¸õûÿׯûÿKÿäÕ:õúÛ_­µÛK´¶ì0Kb°ÂÚà CDà EI endstream endobj 22 0 obj <>stream 0 0 0 0 55 72 d1 55 0 0 72 0 0 cm BI /IM true /W 55 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ /ÿüƒIÿÿÿÿÿÿâTÿ&¨Ì“ƒïßÛÿÛû÷öþýý¿¿oïßÛÿÛöÿöþýý¿¿oïßÛÿÛö?ß÷ß÷ß÷ß÷€€ EI endstream endobj 23 0 obj <>stream 0 0 0 0 14 44 d1 14 0 0 44 0 0 cm BI /IM true /W 14 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¦qÂO^øäÕ~¶¶°ÂÇÿÿÿÿ/ðžŸ|rj¿[õ†  EI endstream endobj 24 0 obj <>stream 0 0 0 0 57 70 d1 57 0 0 70 0 0 cm BI /IM true /W 57 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID a—þMD' oÿÿÿÿÿÿÿÿÿÿÿþAû‰8>‡ß“‚ïÁ¿íû~ÿûÿï÷ÿÿÿõëÿþ¿^¾—¯¥á+ÈqP¡A@@ EI endstream endobj 25 0 obj <>stream 0 0 0 0 67 70 d1 67 0 0 70 0 0 cm BI /IM true /W 67 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID %!–KA@8päÔBqò 'ðoÃÃ~÷öý¿oßïßÛÿïßÿoÿÿ÷ûÿÿÿÿÿÿÿÿúõÿÿý/ÿô¿ô½~½}/KÒõð—„¼ ¼¼ŠN"(PRR` EI endstream endobj 26 0 obj <>stream 0 0 0 -73 57 0 d1 57 0 0 73 0 -73 cm BI /IM true /W 57 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID *Çÿòj)8l'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö¿]þ½ü4» ÉÐþ½¯axkà ÁŸ œÿÿÿÿÿÿïþ  EI endstream endobj 27 0 obj <>stream 0 0 0 0 36 68 d1 36 0 0 68 0 0 cm BI /IM true /W 36 /H 68 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A˜I«==?ÑA>ŸI¿ÿ¿ÿÈkÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïGÿ&¢“ÿÿÿÿÿÿþ  EI endstream endobj 28 0 obj <>stream 0 0 0 0 55 72 d1 55 0 0 72 0 0 cm BI /IM true /W 55 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID $Ã7ÿ&¢¯ÿÿÿÿÿò~E×Áôý?Aúñ>ð›è7Óõ~ý7ÿWïÿÿzÿÿÿÿÿÿÿþ××ÿö—þ½×kév¼4®J&ˆVÖÖÖRjAd>  EI endstream endobj 29 0 obj <>stream 0 0 0 0 33 79 d1 33 0 0 79 0 0 cm BI /IM true /W 33 /H 79 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3äÕ?ýûýûýûÿÛÿÛÿ߿߿߿߿ý¿ý¿ýûýûýûýûÿÛÿÛÿ߿߿߿߿ý¿ý¾  EI endstream endobj 30 0 obj <>stream 0 0 0 0 80 50 d1 80 0 0 50 0 0 cm BI /IM true /W 80 /H 50 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ Ð†Žž¿§¯ßéë÷úzýþžŠN½^ÿ­êõõ{þ·«×ÕïúÞ¯_W¿ëz½}^ÿ­êõõ{þ·ï^•ïñßµéoýû^–ÿßµéoýħDgÿkÿÀ@ EI endstream endobj 31 0 obj <>stream 0 0 0 0 13 14 d1 13 0 0 14 0 0 cm BI /IM true /W 13 /H 14 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¬ã„œwõ&©ma… EI endstream endobj 32 0 obj <>stream 0 0 0 0 86 52 d1 86 0 0 52 0 0 cm BI /IM true /W 86 /H 52 /BPC 1 /D[1 0] /F/CCF /DP<> ID *Ȩ/ÿÿ“QIÃA8l4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿëÿÿÿÝÿÿýßê½ÝzýÚöƒ^D=„®N‡tÖÓ[O´Ö )50²¼@¿@@ EI endstream endobj 33 0 obj <>stream 0 0 0 0 40 54 d1 40 0 0 54 0 0 cm BI /IM true /W 40 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ä༊_,ƈw!…pƒpƒÓOÚÿ©5 Ýk `¸KÁ,Á, ´‚Ð-Ð. ¡Ud6XõëòjšÿÚí…Ø0»û ,†' EI endstream endobj 34 0 obj <>stream 0 0 0 0 26 73 d1 26 0 0 73 0 0 cm BI /IM true /W 26 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID ù5Ð_ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ EI endstream endobj 35 0 obj <>stream 0 0 0 0 57 52 d1 57 0 0 52 0 0 cm BI /IM true /W 57 /H 52 /BPC 1 /D[1 0] /F/CCF /DP<> ID *Çÿòj)8l'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö¿]þ½ý¥Ã rt8ëk}…†¤Ô2‚ÁŸ € EI endstream endobj 36 0 obj <>stream 0 0 0 0 55 74 d1 55 0 0 74 0 0 cm BI /IM true /W 55 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A8eÈrø x' ûä0„áúo§öúý¿ñ÷ïÿ DüƒÕááéú~¾Ž‚AéôŸIúôŸÿôŸÿÿþ?ëÿÿûþMWÿÿývÿývÿ]¿µÛ^×a…[èv»]®á‚!”k GÀ@ EI endstream endobj 37 0 obj <>stream 0 0 0 0 49 54 d1 49 0 0 54 0 0 cm BI /IM true /W 49 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ Ð4H.ŸƒÂOD1^‚ è&ôƒz_·¤ÿz_·þ—íÿÿ×ÿýüš¯ÿÿ¥·ÿémþ–ß¶—¥¶» -† lVÂÃXaa‚Èlœ@ EI endstream endobj 38 0 obj <>stream 0 0 0 0 55 50 d1 55 0 0 50 0 0 cm BI /IM true /W 55 /H 50 /BPC 1 /D[1 0] /F/CCF /DP<> ID & \ÌW¿Óý{ý?×½/·×¯íé~ß^·ï¥û}z·×¯íõêß^¿·¥û}zþÞ—íõëq)Ñ¿ÿð EI endstream endobj 39 0 obj <>stream 0 0 0 0 55 75 d1 55 0 0 75 0 0 cm BI /IM true /W 55 /H 75 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A˜fÈ=XA“O>ŸO¯GAœZ?O¤úOÓÿ¤ÿÿ¤ÿÿÿñÿ_ÿÿßòj¿ÿÿë·ÿë·úíý®Úö» .Äúí{^׆¼0^@ñÿÿÿÿÿÿÿò£üÿð EI endstream endobj 40 0 obj <>stream 0 0 0 0 46 54 d1 46 0 0 54 0 0 cm BI /IM true /W 46 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ Ð]>=„ôôCè Þ‚oIô›÷Ò¿}/ï½"F¿ÿãëÿ¹5_ÿòC‚ ãÛÿõÛývýµõÛ]†» cß ,0Y “€€ EI endstream endobj 41 0 obj <>stream 0 0 0 0 55 75 d1 55 0 0 75 0 0 cm BI /IM true /W 55 /H 75 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Ðf Hek)ÌžžƒÖMDá ¾ƒ}?WïÓõ~ÿÿ÷§ÿÿÿÿÿÿÿÿí}ÿiëÝv¾—kÃK†æˆWµí{^^^CÿÿÿÿÿÿÿýñÿÀ@ EI endstream endobj 42 0 obj <>stream 0 0 0 0 48 5 d1 48 0 0 5 0 0 cm BI /IM true /W 48 /H 5 /BPC 1 /D[1 0] /F/CCF /DP<> ID ø€ EI endstream endobj 43 0 obj <>stream 0 0 0 0 26 71 d1 26 0 0 71 0 0 cm BI /IM true /W 26 /H 71 /BPC 1 /D[1 0] /F/CCF /DP<> ID ù5œ_ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷Çÿ&¡•ÿÿÈ¿§§ÿÿûõ†  EI endstream endobj 44 0 obj <>stream 0 0 0 0 52 50 d1 52 0 0 50 0 0 cm BI /IM true /W 52 /H 50 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¨ˆÿÿäs!œ´·®×m-ÒÛûKm-ÒÛûKm-þ+kk~ºÒý={Ó×Dýé7¤úWíé7¤ý^­é>“q5Œˆÿÿü@ EI endstream endobj 46 0 obj <>stream 0 0 0 -60 41 2 d1 41 0 0 62 0 -60 cm BI /IM true /W 41 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID &  ÀbAp@ðƒÓÂN¹ M‚}t½uõ×þ¿×þ¿ÿqÿÿñüš©“_ÿÿÿßïýþýíù¶µØaxav=÷ß -…ƒ !¢ EI endstream endobj 47 0 obj <>stream 0 0 0 0 46 59 d1 46 0 0 59 0 0 cm BI /IM true /W 46 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4 ËRj¿¯û]ëþýÒÿýµúÿcëÿkÿf€ëÿöÿKýþë×ýëµÿºõßúíÞ»ýw¯ûö>¿~¿ö¿ö  EI endstream endobj 48 0 obj <>stream 0 0 0 15 40 19 d1 40 0 0 4 0 15 cm BI /IM true /W 40 /H 4 /BPC 1 /D[1 0] /F/CCF /DP<> ID ð EI endstream endobj 49 0 obj <>stream 0 0 0 0 39 59 d1 39 0 0 59 0 0 cm BI /IM true /W 39 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID )ƒ‡ïš¼ß}¿ÿþºýtµÁ,Šž*¡T;¹ ;Ã{ïßïÿÿ¯ék…È©â¸UPR EI endstream endobj 50 0 obj <>stream 0 0 0 0 38 62 d1 38 0 0 62 0 0 cm BI /IM true /W 38 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ¹°"E–¯=ß}ò€¾7ÂÂo ‹ûÿý}|-, B|ƒƒÁ, KAh-áŒÖèè 0¢Wõrj¤?av^.Ç¿á÷à C€€ EI endstream endobj 51 0 obj <>stream 0 0 0 -59 44 0 d1 44 0 0 59 0 -59 cm BI /IM true /W 44 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ¹ ÿÞ¿úýèÏÿþ­õÿ÷«õýëûõëôÿzÿôúßþý^¿þúWÿý7¯ÿÞ“ÿþ­õÿ÷«õÿÝ|@ EI endstream endobj 52 0 obj <>stream 0 0 0 0 37 59 d1 37 0 0 59 0 0 cm BI /IM true /W 37 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID þhŸÿÿÿÿÿÿÿÿÿÿÿÿ€€ EI endstream endobj 53 0 obj <>stream 0 0 0 0 38 61 d1 38 0 0 61 0 0 cm BI /IM true /W 38 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Á°bBÕ„zz]=Å| ýô¾Ÿÿ~¯ãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà EI endstream endobj 54 0 obj <>stream 0 0 0 0 37 59 d1 37 0 0 59 0 0 cm BI /IM true /W 37 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID þhŸÿÿÿÿ‘Oÿó@ÌÿÿÿüНÿÀ@ EI endstream endobj 55 0 obj <>stream 0 0 0 -59 38 0 d1 38 0 0 59 0 -59 cm BI /IM true /W 38 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4 ßÿÿÿÿÿ‘gpáÝÜÐ#È8xo}û¿¿ÿ×úë\.ȩ㪨PR0  EI endstream endobj 56 0 obj <>stream 0 0 0 0 46 59 d1 46 0 0 59 0 0 cm BI /IM true /W 46 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ7þ  EI endstream endobj 57 0 obj <>stream 0 0 0 0 37 62 d1 37 0 0 62 0 0 cm BI /IM true /W 37 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÜØYêÁµ­hè&éé¤úKú×úøþ¿ÿÿ¿“Uÿïï~ûÛ mvñÞ÷¼5† !œð EI endstream endobj 58 0 obj <>stream 0 0 0 0 38 59 d1 38 0 0 59 0 0 cm BI /IM true /W 38 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4 ÿÿÿÿÿÿÿÿÿÇüÐ4ÿÿÿÿÿÿÿþ  EI endstream endobj 59 0 obj <>stream 0 0 0 0 40 62 d1 40 0 0 62 0 0 cm BI /IM true /W 40 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A°"A4Â=====ÆøO¤ßOÓë}?úß¿_ÿý¿_ÿÿÿýþ¤Õ_ÿÿk½Úïö—~Ú]…Ø­¯Úà k ,‚ô€€ EI endstream endobj 60 0 obj <>stream 0 0 0 0 43 59 d1 43 0 0 59 0 0 cm BI /IM true /W 43 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4KKô´¿KKô´¿KK­tº×K«[«JÖê+ªê¦‰ííííííííííííííí÷···¾ÞÞØ€ EI endstream endobj 61 0 obj <>stream 0 0 0 0 44 59 d1 44 0 0 59 0 0 cm BI /IM true /W 44 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4_¥ú_¥ú_Ö½kÖ½k¥úZX\Xª¨pîæ€·¾ßþÿÿúÿék®ÈÆEUT(R €€ EI endstream endobj 62 0 obj <>stream 0 0 0 0 34 59 d1 34 0 0 59 0 0 cm BI /IM true /W 34 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID þMA Á?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüIpßø€ EI endstream endobj 63 0 obj <>stream 0 0 0 0 39 59 d1 39 0 0 59 0 0 cm BI /IM true /W 39 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID Á€páÝÜÐWáá½ö÷ý¿ß¿ßÿÿ¿ÿÿÿõÿúÿ_¯Z륮ÂY‹ÅT*…)Ã`€ EI endstream endobj 64 0 obj <>stream 0 0 0 0 42 59 d1 42 0 0 59 0 0 cm BI /IM true /W 42 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3 ¯ÿÿÿÿÿÿþDŸÿÓÿúÿOÿèþÿÿé7ÿÿI¿ÿúMÿÿÒoÿüSéÿOî¿p EI endstream endobj 65 0 obj <>stream 0 0 0 0 38 59 d1 38 0 0 59 0 0 cm BI /IM true /W 38 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 45þ¾¿×××3_ÒÿKÿé¥ÿô¿Òÿú_é¥ÿô¿Òÿñ__ëëý}}|@ EI endstream endobj 66 0 obj <>stream 0 0 0 0 46 59 d1 46 0 0 59 0 0 cm BI /IM true /W 46 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œÌ3ÿÿÿÿÿÿÿÿÿÿÿÿý>õÓý?ÓÓæŸ[éõo«Ò}o§Öú}oI¿}+éõ¾›×Òoý&ÿŠnà EI endstream endobj 67 0 obj <>stream 0 0 0 0 35 59 d1 35 0 0 59 0 0 cm BI /IM true /W 35 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4 ¯ÿÿÿÿÿÿ#wÿæ•ÿÿÿù ÿ€€ EI endstream endobj 68 0 obj <>stream 0 0 0 0 48 59 d1 48 0 0 59 0 0 cm BI /IM true /W 48 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID ' É5M.ýÒÛý-´¶ÿKn·^ëm}-ºÛ_­µÞ»Km~¶×zâ·Úý­¯ý?Óý=>hº·Õé>­õz¾•ûé_MêúWíé}7«z_·¥ûp EI endstream endobj 69 0 obj <>stream 0 0 0 0 40 59 d1 40 0 0 59 0 0 cm BI /IM true /W 40 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID þN Õvûß½½ûÛß½½ûÛß½½ûÛß½¿ÛÛý½¿ÛÛý½¿ÛÛý½¿‹ÿþ  EI endstream endobj 70 0 obj <>stream 0 0 0 0 40 71 d1 40 0 0 71 0 0 cm BI /IM true /W 40 /H 71 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Ð\ZzOO]¥¥#L‚'ÂXOOOOOD1¾é7ÓôÞ¿Oþ·ï×ÿÿcï×ÿÿÿÿ©5_×ÿÿÚï_ö»ý¥ß¶—av+kö°ÂÚà ½  EI endstream endobj 71 0 obj <> endobj 72 0 obj <> endobj 76 0 obj <>endobj 77 0 obj <>stream 0 0 0 -61 19 11 d1 19 0 0 72 0 -61 cm BI /IM true /W 19 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡õ¥ú_¥¯Zõú_¯_¯_¯õúÿõñÿÿÿòj¿ßÿïßïýþýïýïÞÿ·û{·À@ EI endstream endobj 78 0 obj <>stream 0 0 0 -61 19 11 d1 19 0 0 72 0 -61 cm BI /IM true /W 19 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3 “Tÿo½ûß½ûßû߿߿ßûÿßûÿÿÿÿÿþ¿_þ¿_ëõëõë^¿Kô¿KôºŽ  EI endstream endobj 79 0 obj <>stream 0 0 0 0 49 59 d1 49 0 0 59 0 0 cm BI /IM true /W 49 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¢šš ÿû½Wÿÿíû×ïõÿŸYåûÿÖõÿÿ÷¯í_¯ÿïWÿºÿ_ÿÿýºŽô¿ÿÿÚÿÿÿ½]ú¯ÿÿÿÆõÿ÷ÿ¯ÿ¸ÿÿÞ¾ø€ EI endstream endobj 80 0 obj <>stream 0 0 0 -59 34 0 d1 34 0 0 59 0 -59 cm BI /IM true /W 34 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID &«ÿÿÈ%?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæÄÍ?þMnG®A) EI endstream endobj 81 0 obj <> endobj 82 0 obj <> endobj 86 0 obj <>endobj 87 0 obj <>stream 0 0 0 -60 37 0 d1 37 0 0 60 0 -60 cm BI /IM true /W 37 /H 60 /BPC 1 /D[1 0] /F/CCF /DP<> ID þMQ8e[ÛÛÛÛïoooooooo}½½¾öö÷ÛÛý½¿ß½ÿÿÿÿò{^ .ða.>ºëk ,Yx€ EI endstream endobj 88 0 obj <>stream 0 0 0 0 37 63 d1 37 0 0 63 0 0 cm BI /IM true /W 37 /H 63 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ¹€"B‡„§O§ÐëGEÁt ŸOWÓþ“ÿúûÿÿÿäÕ}¯ÿÖßý­µÚØaqÞöK­®\0¸ÿÿÿÿÿÿ EI endstream endobj 89 0 obj <>stream 0 0 0 0 37 61 d1 37 0 0 61 0 0 cm BI /IM true /W 37 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID üš† Áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþaÿÿÿä1?ÿÿÿÿ€€ EI endstream endobj 90 0 obj <>stream 0 0 0 0 36 57 d1 36 0 0 57 0 0 cm BI /IM true /W 36 /H 57 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Fµ¯¢\]kÿÿ×ÿÿÿÿÿÿÿÿÿÿýê#òjÌÿÿÿÿÿÿ EI endstream endobj 91 0 obj <>stream 0 0 0 0 38 47 d1 38 0 0 47 0 0 cm BI /IM true /W 38 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Á°bCë‚O==>tú ðƒzOÓë}?þدÿ~¿ßëÿäÕ5õÿk¶—~ØKµØ­­­­¬0°Â€€ EI endstream endobj 92 0 obj <>stream 0 0 0 0 35 45 d1 35 0 0 45 0 0 cm BI /IM true /W 35 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3?ÿÿÿÿÿÿÿÿÿ÷ëþýoµ´¡…ަkµµ†š†€€ EI endstream endobj 93 0 obj <>stream 0 0 0 0 37 47 d1 37 0 0 47 0 0 cm BI /IM true /W 37 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID & †ÃD‹¦dç>ŸOÇGK‚ƒ§º{ÿ~þÜš§áöS‰±Þ÷ƒÁä3gëõùxkÃKƒ Å]l,Y ¤@ EI endstream endobj 94 0 obj <>stream 0 0 0 0 35 62 d1 35 0 0 62 0 0 cm BI /IM true /W 35 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡Ÿ ZÖ¿'K®¿_þ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþøÿø€ EI endstream endobj 95 0 obj <>stream 0 0 0 0 44 44 d1 44 0 0 44 0 0 cm BI /IM true /W 44 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID (j©m¥¶—ûim¥·^»im¥·[¯im¥½l}mmoÖ×éé÷®žŸ4Þ“êÞ•ôÞ“ëzMôÞ“ëzMôÞ¯ EI endstream endobj 96 0 obj <>stream 0 0 0 0 37 62 d1 37 0 0 62 0 0 cm BI /IM true /W 37 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3 ßÿÿÿ‘“==8»”é΂ºM×O·_þý¯ÿÿÿºý~ý+õ´­a¥TÍ-¬0¤Ô(€ EI endstream endobj 97 0 obj <>stream 0 0 0 0 33 47 d1 33 0 0 47 0 0 cm BI /IM true /W 33 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID & †Áy4ø qpïÛ™‡pAá<&×ÿõ„°¹ W¢20˜]¥ Z#3  ¡W_y ï&©…á‚ñï¾ûá…ƒ EI endstream endobj 98 0 obj <> endobj 99 0 obj <> endobj 103 0 obj <>endobj 105 0 obj <>stream 0 0 0 -90 89 2 d1 89 0 0 92 0 -90 cm BI /IM true /W 89 /H 92 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡¬KƒDƒX–C4¯’…„< z)ÂCAŠ=z].—K¯®—_]Òÿúúÿ_ÿÖ>A ÿÿ Îkÿÿÿ“Uþÿ ¥Oö×ÿö×ý׿uÛývýµÛ^×mv× 0» .Á‚ìxaa…†  –ƒdX6Hi¤È*ô@ EI endstream endobj 106 0 obj <>stream 0 0 0 0 96 88 d1 96 0 0 88 0 0 cm BI /IM true /W 96 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID )/ÿÿòj2pU*ĺ÷_í¯Öëݶ¿[¯uþÚýl}~×ïÖýrp1Û_­×ºÿm~·^ëý´¿Ý{¯öÒÿuî·ûKýµúßí/ö×ëŠý¯ß¯Úýú߯߭úý¯ß­à EI endstream endobj 107 0 obj <>stream 0 0 0 17 60 29 d1 60 0 0 12 0 17 cm BI /IM true /W 60 /H 12 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿð EI endstream endobj 108 0 obj <>stream 0 0 0 -88 46 0 d1 46 0 0 88 0 -88 cm BI /IM true /W 46 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ&¡J€¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ$P7þ  EI endstream endobj 109 0 obj <>stream 0 0 0 0 100 88 d1 100 0 0 88 0 0 cm BI /IM true /W 100 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID #@Úk ]uýuÖMBš¯×ÿ______ý}}}}õõõõôF—Òþ¿Kô¿_Òý/Òþ¿Kô¿_Òý/ëô¿Kõý/Òý/ëô¿Kõý/Òþ¿Kô¿Kõø¯¯¯¯¯¯þ¾¾¾¾¾¿úúúúúã!¤4µ­k_ EI endstream endobj 110 0 obj <>stream 0 0 0 0 88 88 d1 88 0 0 88 0 0 cm BI /IM true /W 88 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ …ÏÿÿÿjQÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ4 )ÿÿÿÿÿÿÿÿÿ€€ EI endstream endobj 111 0 obj <>stream 0 0 0 0 77 88 d1 77 0 0 88 0 0 cm BI /IM true /W 77 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ&¡yP ÿÿÿÿÿÿÿä6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüH 4ÿþ  EI endstream endobj 112 0 obj <>stream 0 0 0 0 80 88 d1 80 0 0 88 0 0 cm BI /IM true /W 80 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ Ú“TûöýöûöýöûöýÚ5¾ß{{í÷¾Þß{íí÷¾ß{íí÷¾Þß{ííþÞß{íí÷¾Þßíííç§íû~þß·ÿ·íûû|ÿÿÿð EI endstream endobj 113 0 obj <>stream 0 0 0 0 81 88 d1 81 0 0 88 0 0 cm BI /IM true /W 81 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ&¡yP¿ÿÿÿÿÿÿü‚±¯ÿÿÿÿÿ ÞŸÿÿÿÿÿÿÿÇÿÿÿÿÊ€oÿÿÿÿÿÿÿÿÿ!©Oÿÿÿÿÿÿÿÿþ8ÿÿ€€ EI endstream endobj 115 0 obj <>stream 0 0 0 -44 43 0 d1 43 0 0 44 0 -44 cm BI /IM true /W 43 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID & † Þ¿Þ¿úú|½zßþúWÿý7¯ÿ·¥ÿ÷«õÿ­ôÿë~ýzßþñWÿM€€ EI endstream endobj 116 0 obj <>stream 0 0 0 0 42 47 d1 42 0 0 47 0 0 cm BI /IM true /W 42 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œØg}pAá§§§§§ú|ê] ßO¥}?þýcÿÿþMWõÚÿߺö—k°Â\{[õµµµ†Xa@@ EI endstream endobj 117 0 obj <>stream 0 0 0 0 41 66 d1 41 0 0 66 0 0 cm BI /IM true /W 41 /H 66 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ“Pr?ÿÿÿÿÿÿÿÿÿÿÿüÊ?ÿÿÿÿÿÈ8ÿÿÿÿÿÿð EI endstream endobj 118 0 obj <>stream 0 0 0 0 40 63 d1 40 0 0 63 0 0 cm BI /IM true /W 40 /H 63 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ƒÞB„ƒ§Óèu¯¯Î§¤O¯¸ôÿþ¿ßÿõ&©ÿÚõÃ[aqßßÞÈ…µÃ †ÿÿÿÿÿà EI endstream endobj 119 0 obj <>stream 0 0 0 -60 41 2 d1 41 0 0 62 0 -60 cm BI /IM true /W 41 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÜØbAz°@ôzzu­}sXº õÒõþ—ýøÈ£ø×ÿÿÿ!˜kï“UÿïöÈl^×m{^]’ë±þûï¾ûXaa‚Èh¤@ EI endstream endobj 120 0 obj <>stream 0 0 0 0 46 59 d1 46 0 0 59 0 0 cm BI /IM true /W 46 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID !†Íy5_uëþõÿkþõÿk±õÿ¿_û_É;¥ÿû¥ÿû×kþÿ×®ÿ×ü{_ÿµÿ¿_ûõûÀ@ EI endstream endobj 121 0 obj <>stream 0 0 0 11 49 19 d1 49 0 0 8 0 11 cm BI /IM true /W 49 /H 8 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ EI endstream endobj 122 0 obj <>stream 0 0 0 -59 36 0 d1 36 0 0 59 0 -59 cm BI /IM true /W 36 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿäÔ9 ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø•¿þ  EI endstream endobj 123 0 obj <>stream 0 0 0 0 36 45 d1 36 0 0 45 0 0 cm BI /IM true /W 36 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID !…ÿÿÿÿÿÿÿÿÿÿÿí_PÔkëÒÚÚ“P (€ EI endstream endobj 124 0 obj <>stream 0 0 0 0 38 57 d1 38 0 0 57 0 0 cm BI /IM true /W 38 /H 57 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡Ÿ­k_ý‚áuÿ×ÿÿÿÿÿÿÿÿÿÞ¢?òjÿÿÿÿÿÿ EI endstream endobj 125 0 obj <>stream 0 0 0 0 41 47 d1 41 0 0 47 0 0 cm BI /IM true /W 41 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ƒ¢DÓ3>‡Zú滄§ý?»ûɪ~Ê‹c÷½ááàòùc×à׃^CŠþ¿®¶°ad=p EI endstream endobj 126 0 obj <>stream 0 0 0 0 40 61 d1 40 0 0 61 0 0 cm BI /IM true /W 40 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡£ ZúÿF°¸]uýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßÿü@ EI endstream endobj 127 0 obj <>stream 0 0 0 0 37 44 d1 37 0 0 44 0 0 cm BI /IM true /W 37 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿA¹5Oo½öööööööÿooooooo½ööööå ÿÿÿ€€ EI endstream endobj 128 0 obj <>stream 0 0 0 0 42 47 d1 42 0 0 47 0 0 cm BI /IM true /W 42 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡°Ø1 â0@ðé뮺ëÈ‹£P'„Ð>“øõñúù “Uúïõ¶½®ÃKkö¶¶¶°Âà @ EI endstream endobj 129 0 obj <>stream 0 0 0 -61 23 11 d1 23 0 0 72 0 -61 cm BI /IM true /W 23 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ú×Kô¿Kôµþµë_ë_ý/ÿÒÿÿÿø¯þMSÿÿÿý¿ÿ߿߿߿߽ûß½öÿo~À@ EI endstream endobj 130 0 obj <>stream 0 0 0 0 22 72 d1 22 0 0 72 0 0 cm BI /IM true /W 22 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID '¹5Oöûß½û߿߿ÛÿÛÿþ÷ÿÿý¿ÿÿÿKÿÿÿKÿô¿ô¿­­z×­zÒø®  EI endstream endobj 131 0 obj <>stream 0 0 0 0 72 70 d1 72 0 0 70 0 0 cm BI /IM true /W 72 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID %À¿ÿÉ©Â@ö–ÿûiÚï_í¥ÿûiÚ[íísà_¶—ÿí¥ÿioÿ¶—ýû¥þÚ_÷î—ý¥¿þÚ_÷î—ûiß±_ÖÿµÿµýoûP EI endstream endobj 132 0 obj <>stream 0 0 0 0 45 50 d1 45 0 0 50 0 0 cm BI /IM true /W 45 /H 50 /BPC 1 /D[1 0] /F/CCF /DP<> ID ù5D€ÊÛíûïÛíûïÛí.k½öööÿooo½ööûßoo½öóó~ßþß·íýûñ}ÿÿÀ@ EI endstream endobj 133 0 obj <>stream 0 0 0 -72 65 2 d1 65 0 0 74 0 -72 cm BI /IM true /W 65 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÐHÕàˆØx@ðá„SˆŽ @t=“Òézë믮¾ºÿ×_ÿÖ2£øÿþ@Ü×ÿÿɪÿûù ±Ë¸÷_öÿ]¿µßÛ^×mv×a®Ø]ƒÃ)ÇØ¬0X`°Ád È4"  EI endstream endobj 134 0 obj <>stream 0 0 0 0 55 72 d1 55 0 0 72 0 0 cm BI /IM true /W 55 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡†´žº¾ûæ—„éþúñoöÿ½ÿoûý{ý?׿Óý{ÑBû}zß¾—íõë~ú_·×«}zþß^­õëûz_·×­ûé~ß^·y!ÈqÝDqÿ€€ EI endstream endobj 135 0 obj <>stream 0 0 0 0 95 88 d1 95 0 0 88 0 0 cm BI /IM true /W 95 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID )A¨vj àáÇMB•‡àÃð߆ý¿ ûû~ßþß߿߿ÿ~ÿýÿÿ¿ßÿÿÿÿÿÿÿýúÿÿÿÿÿÿÿÿ¯_ÿÿë×ÿ¯_¯ý/ý/¯_KÒô½/ xKÂ^Aï#ˆ… )ØàÐLƒH¬`€ EI endstream endobj 136 0 obj <>stream 0 0 0 -45 36 2 d1 36 0 0 47 0 -45 cm BI /IM true /W 36 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ¹°^F#2L'Ãïûþòp¼ð/Â|.?¬,AÁÈEx\%„µÐ]h.BÈÀ\AAUd3½…ÂɪF~?ß߆ Ô € EI endstream endobj 137 0 obj <>stream 0 0 0 0 43 45 d1 43 0 0 45 0 0 cm BI /IM true /W 43 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID '"rÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýßkúÚkõõ4D?´Ôš‡0  EI endstream endobj 138 0 obj <>stream 0 0 0 0 41 45 d1 41 0 0 45 0 0 cm BI /IM true /W 41 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID >!˜Ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßÿ×­¯i¬G\ƒJ}'ÚjMB&@ EI endstream endobj 139 0 obj <>stream 0 0 0 0 35 46 d1 35 0 0 46 0 0 cm BI /IM true /W 35 /H 46 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡Ì‰Œ Ë¥ÓèuôH>Ÿ¸®þëÿÿÿÿÿÿÿÿÿÿþ  EI endstream endobj 140 0 obj <>stream 0 0 0 0 35 61 d1 35 0 0 61 0 0 cm BI /IM true /W 35 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ÌÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþõù5fÿÿÿßì>tï{ÁÀ@ EI endstream endobj 141 0 obj <>stream 0 0 0 0 40 47 d1 40 0 0 47 0 0 cm BI /IM true /W 40 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ Ø/ õá‡]tkú!‡ðô?Aõ´¿úøÿQù5F`£ý¯ýmþ½ÖÚö»il0¼VÖÖÚà @ EI endstream endobj 142 0 obj <>stream 0 0 0 0 32 45 d1 32 0 0 45 0 0 cm BI /IM true /W 32 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3ÿÿÿÿÿÿþþûì(jPeÐfk{ÞRj ‚€€ EI endstream endobj 143 0 obj <>stream 0 0 0 0 42 61 d1 42 0 0 61 0 0 cm BI /IM true /W 42 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID &±8fàûï¾ÿ!‡ï}¿ß¿Ûÿï^õþõý?æiëôúß¿W¯ïÕëôúß¿W¯ï¥ôÞ¿¾•ÿÓzÿé7ÿ¼Uý6  EI endstream endobj 144 0 obj <>stream 0 0 0 -61 37 2 d1 37 0 0 63 0 -61 cm BI /IM true /W 37 /H 63 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A€^A™Áéè8¾S§: è=7O^ß׿}ÿÿïþ—ß­Ö·ØJÅULÒÚÃX`±ÿÿÿþ  EI endstream endobj 145 0 obj <>stream 0 0 0 0 35 47 d1 35 0 0 47 0 0 cm BI /IM true /W 35 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ ØY«:Ö´jôC  ƒè='Ðzׯê?ÿÿäÕ?þ÷ím®ÂÛ ƒ c½ï a…ƒ EI endstream endobj 146 0 obj <>stream 0 0 0 -61 17 11 d1 17 0 0 72 0 -61 cm BI /IM true /W 17 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID þf'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿšÃ€€ EI endstream endobj 147 0 obj <>stream 0 0 0 -61 18 11 d1 18 0 0 72 0 -61 cm BI /IM true /W 18 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID þMDÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ?€€ EI endstream endobj 148 0 obj <>stream 0 0 0 -73 39 0 d1 39 0 0 73 0 -73 cm BI /IM true /W 39 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID A?ɨ¤á³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýäxþME' Ÿÿÿÿÿ÷!×qþë¿ka‚Øïxk Ô` EI endstream endobj 149 0 obj <>stream 0 0 0 -70 28 0 d1 28 0 0 70 0 -70 cm BI /IM true /W 28 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID ù5œOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñ#€€ EI endstream endobj 150 0 obj <>stream 0 0 0 -73 24 15 d1 24 0 0 88 0 -73 cm BI /IM true /W 24 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡³ XZÖ‚ÒÒÒÒ×­/Òׯ׭­ÿ^¿ÿþ?ÿÿÿþMWÿÿûßÿöÿ÷ïï}¿Ûï}½½½½‡{ÃÞ@ EI endstream endobj 151 0 obj <>stream 0 0 0 -73 24 15 d1 24 0 0 88 0 -73 cm BI /IM true /W 24 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID 0‡w&©ì=½öööûßoöÿ½ûýûÿß¿ÿþýÿÿÿÿÿÿÿõúÿÿë_þµþµëô¿K^´´µÒÒÒˆUP¡@@ EI endstream endobj 152 0 obj <>stream 0 0 0 -72 46 -27 d1 46 0 0 45 0 -72 cm BI /IM true /W 46 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œÌ3ÿÿý¯Í=>Aá>õˆxMêM`ða‡„ˆXaa…†Xaa„x ðá3ØxKaÄ,0ÂÓT¶¸X}­­…†¸X|q׿ÿÿ€€ EI endstream endobj 153 0 obj <>stream 0 0 0 -56 61 -4 d1 61 0 0 52 0 -56 cm BI /IM true /W 61 /H 52 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ³‚ÂÁ`°XX,, X °@°@°`‚ÁÁ‚    ¡Apàààä×<<<x`ð`ð`ðaÃÁàðxx<  EI endstream endobj 154 0 obj <>stream 0 0 0 -70 26 -43 d1 26 0 0 27 0 -70 cm BI /IM true /W 26 /H 27 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3 ÿÿÿÿÿÿÿÿÿü@ EI endstream endobj 155 0 obj <>stream 0 0 0 0 55 72 d1 55 0 0 72 0 0 cm BI /IM true /W 55 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡›ÿÿ!ªÿÿÿÿÿÈ? õxAøAú~Ÿ¯£ ƒÐAú}'Ò~Ÿý'ÿý'ÿÿÿúÿÿþÿ“Uÿÿÿ]¿ÿ]¿×oív×µØaVÄú®×k†¸`ˆeÈð EI endstream endobj 156 0 obj <>stream 0 0 0 -88 83 0 d1 83 0 0 88 0 -88 cm BI /IM true /W 83 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID *A¥ÿù5 T'ÿÿÿÿÿÿÿÿÿÿÿÿÿò? gÁðø}ðûå@Wá¿ û~þß¿·ÿ·ÿÿûýÿÿÿÿÿ^¿ÿëõëõëéz^ÒðJòb‘ª(PR¶@ EI endstream endobj 157 0 obj <>stream 0 0 0 0 96 88 d1 96 0 0 88 0 0 cm BI /IM true /W 96 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID ¡£__Zòj¨ ¢¬'ý/ý/ý/ý/ý/ý/ý/_¯_¯_¯KÿKÿK×Òô¼%à—‚\W ‚å`0||¨Ã~öý¿oÛ÷ÿïßÿ¿ÿ÷ÿÿ_ÿÿë×ÿ_¯_K×Â^ðJò18ЍP¡NÍA¨¥¨€ EI endstream endobj 158 0 obj <>stream 0 0 0 0 95 92 d1 95 0 0 92 0 0 cm BI /IM true /W 95 /H 92 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡¬F´ƒPC0_< ðƒÐxD˜G @ÞaáÞoI¾¯I½_Júo_Vú½_«×÷ûÒÿûÕÿêÿÿý¿ÿÿÿÿþ—ÿɪÿ÷_úïýwïú[ýÖëõ·ú[uºö–Ým¥¶–Ú[a-†–`–Ä,5°°Âà   !¦¯ ©Ð EI endstream endobj 159 0 obj <>stream 0 0 0 0 99 90 d1 99 0 0 90 0 0 cm BI /IM true /W 99 /H 90 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡®DdƒXÞxAéá§§ÈÐ?@ˆlÓAøO¤ßMé?¾•ôÿêß_þÿé_ÿûÿÿÿÿ×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷£Ø>¢8ÿÿ EI endstream endobj 160 0 obj <>stream 0 0 0 -90 85 2 d1 85 0 0 92 0 -90 cm BI /IM true /W 85 /H 92 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡¬FdƒXC4WxAá„ôLxD˜ôoAô›Ò}&ô›Ò½'Ò¿}/VÿÒý¿Ô‚Á®¿_þ£ÿÿÿÿÿù5_ï÷ò ôÿmÿmßíwö××oÛ]µÛ]µÛ]†l.Á‚áaa…†  •ðdL4Hi«È*ô@ EI endstream endobj 161 0 obj <>stream 0 0 0 -59 39 0 d1 39 0 0 59 0 -59 cm BI /IM true /W 39 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID $_ÿÿÿÿü¼‰@ppáÝß|' Ãûÿÿþ¾°K,uÕUB‚€¸€ EI endstream endobj 162 0 obj <>stream 0 0 0 0 40 62 d1 40 0 0 62 0 0 cm BI /IM true /W 40 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID & „0yL ø}÷ßß'žðA¼./ï ”àƒáÓèu¯¯£Sè>ž“ëïýcïÿÿÔš¯¿ýwÛ]®ÂØýýïdBÚì.D5 EI endstream endobj 163 0 obj <>stream 0 0 0 0 34 45 d1 34 0 0 45 0 0 cm BI /IM true /W 34 /H 45 /BPC 1 /D[1 0] /F/CCF /DP<> ID $ÿÿÿÿÿ÷ýÞ¡¨aA—1ù!oxrjÁ@@ EI endstream endobj 164 0 obj <>stream 0 0 0 0 36 46 d1 36 0 0 46 0 0 cm BI /IM true /W 36 /H 46 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡LyAœ:}¾¿Œêtéúÿÿÿÿÿÿÿÿÿÿü@ EI endstream endobj 165 0 obj <>stream 0 0 0 0 40 62 d1 40 0 0 62 0 0 cm BI /IM true /W 40 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID !†_ÿÿÿÈ‘“#==8»¾ùNô§§¯»¯ûÿÿ¯ï®­oµ°”}WR[XjMC8(€ EI endstream endobj 166 0 obj <>stream 0 0 0 -45 36 2 d1 36 0 0 47 0 -45 cm BI /IM true /W 36 /H 47 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ Øy¯:Öµ­}âpƒá¤ü=|WÿÿÜš¯÷Ø[avÃÇ{ÞðÖX0P EI endstream endobj 167 0 obj <>stream 0 0 0 -60 40 -22 d1 40 0 0 38 0 -60 cm BI /IM true /W 40 /H 38 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ¹˜/ÿÿó>B¾ƒÂ|<.ƒÂ}ë𘌚áa…†Xaa…§„xAá„NˆÍ0°äÕ-­…†¸X}…†¸X|qÿÿø€ EI endstream endobj 168 0 obj <>stream 0 0 0 -15 17 12 d1 17 0 0 27 0 -15 cm BI /IM true /W 17 /H 27 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4÷ɪßíÿ{÷ýÿÿÿÿÀ@ EI endstream endobj 169 0 obj <>stream 0 0 0 0 34 44 d1 34 0 0 44 0 0 cm BI /IM true /W 34 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID üœ “Töÿooooöööûßooo½öööûßooo½ööï(Kÿø€ EI endstream endobj 170 0 obj <>stream 0 0 0 -45 37 18 d1 37 0 0 63 0 -45 cm BI /IM true /W 37 /H 63 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ƒÀ¼‰^MG|>ÿ““à¾? ¼}û÷òü>}>ñÖˆoAá='þž¾ŸÿÓüÿÿù5_®ÿþÚÿkmv¶\w½’ëk† "j  EI endstream endobj 171 0 obj <>stream 0 0 0 -11 19 13 d1 19 0 0 24 0 -11 cm BI /IM true /W 19 /H 24 /BPC 1 /D[1 0] /F/CCF /DP<> ID &±€Jz.äÕ<7Þû{÷¿¿ÿý߀€ EI endstream endobj 172 0 obj <>stream 0 0 0 0 57 73 d1 57 0 0 73 0 0 cm BI /IM true /W 57 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¨«/ÿÿÈ”! KÿKÒÿÒô½/ý/KÒÿ´»Kµî»KŠþ¹¢^׿µïíû~߆·íû~ß·íû~ßEÿÿÿøÿÿÿÿÿÿÿÿÇÿÀ@ EI endstream endobj 173 0 obj <>stream 0 0 0 0 73 92 d1 73 0 0 92 0 0 cm BI /IM true /W 73 /H 92 /BPC 1 /D[1 0] /F/CCF /DP<> ID & `— ¹òW‘²k ûáòLò 6>o„„ßMõôß_·×ÿ_Þ¿ÿ®¸­k ‚Èi‹dJ²`™ X%‚à¸K ¥¥„µëAiu ¸\r2X¹(¼Š®F´AAB¨]×òj±ô¿ÿëþëþëÚÿ¶m{]† °`»ø}ðÊHd° Šx2L H4ô@ EI endstream endobj 174 0 obj <>stream 0 0 0 0 88 88 d1 88 0 0 88 0 0 cm BI /IM true /W 88 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡°…ÿÿÿkQÿÿÿÿÿÿÿÿÿÿÿÿÿúþ¿§úz÷§úzòOzý7¥z¿W«ôÞ•êý^¯ÓzßO¥z¿W«}>•êý^¯ÓzW«õz¿Mëq" “ ÿÿþ  EI endstream endobj 175 0 obj <>stream 0 0 0 0 44 61 d1 44 0 0 61 0 0 cm BI /IM true /W 44 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¹Ð30}÷ß}ÿºá÷ï~÷þÿ^ÿOõïõï^ÿ^ôC_¾½_«ÿWêõý¾½oKÿÛÒþõ~¿½'ÿìSð EI endstream endobj 176 0 obj <>stream 0 0 0 -61 36 0 d1 36 0 0 61 0 -61 cm BI /IM true /W 36 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID !…ÿÿÿÿÿÿÿÿÿÿÿ}_PÔkëÒÚÚà ÿÿÿà EI endstream endobj 177 0 obj <>stream 0 0 0 -61 35 0 d1 35 0 0 61 0 -61 cm BI /IM true /W 35 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3?ÿÿÿÿÿÿÿÿÿ÷ëþýoµ´¡…ަkµµ†Xÿÿÿÿ EI endstream endobj 178 0 obj <>stream 0 0 0 0 79 88 d1 79 0 0 88 0 0 cm BI /IM true /W 79 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID *@Ñÿù5 T§ÿÿÿÿÿÿÿÿÿÿÿò)ÿÿÿÿÿÿÿüÿÿÿü¨ Ÿÿÿÿÿÿÿÿñÿòtÿÿÿÿÿÿÿücþ  EI endstream endobj 179 0 obj <>stream 0 0 0 0 37 61 d1 37 0 0 61 0 0 cm BI /IM true /W 37 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡Iÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþõÿ“P¤€ÿýÿ5‹Çï EI endstream endobj 180 0 obj <>stream 0 0 0 -72 51 2 d1 51 0 0 74 0 -72 cm BI /IM true /W 51 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œ$:¼ŒK8¹#š‡ ôÝ7^õ}?ÿÔšƒgÖµ¥…Âà– ‚Y æ0K°‚ÂXAh‚ÑH(r0(. *W^A¤rcñÿëÿ&©¯¯k¶» x`»5±áö @ÂC!Ï EI endstream endobj 181 0 obj <> endobj 182 0 obj <> endobj 186 0 obj <>endobj 187 0 obj <>stream 0 0 0 -59 40 0 d1 40 0 0 59 0 -59 cm BI /IM true /W 40 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID ( }¯õõþ¾¿×æ‹_]ë¯ú_õ×þºúëÿXþ¾¿××úÿ__à EI endstream endobj 188 0 obj <>stream 0 0 0 -60 38 2 d1 38 0 0 62 0 -60 cm BI /IM true /W 38 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID &  ØY+Á„-kZú×)ÅÐAðƒ÷¤úéõÿÿ_÷&«ÿÿßþßöëµØ[ .K­Þ÷½áí`Ád4  EI endstream endobj 189 0 obj <>stream 0 0 0 -61 21 11 d1 21 0 0 72 0 -61 cm BI /IM true /W 21 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ( ÿÿÿÿÿÿÿÿÿÿÿÿÿù.ÿ EI endstream endobj 190 0 obj <>stream 0 0 0 0 21 72 d1 21 0 0 72 0 0 cm BI /IM true /W 21 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ&¡?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð EI endstream endobj 191 0 obj <>stream 0 0 0 0 41 61 d1 41 0 0 61 0 0 cm BI /IM true /W 41 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID !ƒë­/Òë]/Òë].µêÒ¾+ªêº»¹ =½¾ööööÞÞÞÞøÿÿÿÿÀ@ EI endstream endobj 192 0 obj <>stream 0 0 0 -26 22 -18 d1 22 0 0 8 0 -26 cm BI /IM true /W 22 /H 8 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ EI endstream endobj 193 0 obj <>stream 0 0 0 0 37 62 d1 37 0 0 62 0 0 cm BI /IM true /W 37 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡™ÿÿÿÿÿä¹ §AðŸC­r @ôŸO§¯§þ±¿ÿõþÿÿüš¦¿ëoÚõ°×alw½‘ k†¸`ˆ`Ô@ EI endstream endobj 194 0 obj <>stream 0 0 0 -12 15 12 d1 15 0 0 24 0 -12 cm BI /IM true /W 15 /H 24 /BPC 1 /D[1 0] /F/CCF /DP<> ID >+“Uïûß¿ï~ûý¿ÿÿþ  EI endstream endobj 195 0 obj <>stream 0 0 0 0 38 61 d1 38 0 0 61 0 0 cm BI /IM true /W 38 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID 3 ëK].´µÒëK^´´µº´­n¢«ª©šííííì=½½½½½½½½½½¼ÿÿÿà EI endstream endobj 196 0 obj <>stream 0 0 0 0 42 44 d1 42 0 0 44 0 0 cm BI /IM true /W 42 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ¹@b¿Þ¿§ÿ§Í¯[÷êõú½~Ÿ[ÿ§Öÿéõ¿ú}oß«×÷êõýô¯ÿ}+ÿ§ŽÀ@ EI endstream endobj 197 0 obj <>stream 0 0 0 -72 63 2 d1 63 0 0 74 0 -72 cm BI /IM true /W 63 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡šNä —ÁÁ ðž¬W£ .‚ è Þ“é7¤ßO¤Þ—íé~Ý/õ Êk¯×ÿ¨ÿÿÿÿäÕ¿ýü†Ðà‚î=׿×oíwö×µÛ]µÛ]†a‚ì§ BÆ  ƒ$ EI endstream endobj 198 0 obj <>stream 0 0 0 -30 25 -22 d1 25 0 0 8 0 -30 cm BI /IM true /W 25 /H 8 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ EI endstream endobj 199 0 obj <>stream 0 0 0 -70 58 0 d1 58 0 0 70 0 -70 cm BI /IM true /W 58 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID a™þMD' ÿÿÿÿÿÿÿÿÿþAGÿÿÿÿÿÿñÿÿÿ'ÿÿÿÿÿÿÿò _ÿÿÿÿÿñÀ@ EI endstream endobj 200 0 obj <>stream 0 0 0 -73 23 13 d1 23 0 0 86 0 -73 cm BI /IM true /W 23 /H 86 /BPC 1 /D[1 0] /F/CCF /DP<> ID üœÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!€€ EI endstream endobj 201 0 obj <>stream 0 0 0 -73 23 13 d1 23 0 0 86 0 -73 cm BI /IM true /W 23 /H 86 /BPC 1 /D[1 0] /F/CCF /DP<> ID üš‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøþ  EI endstream endobj 202 0 obj <>stream 0 0 0 -44 61 -16 d1 61 0 0 28 0 -44 cm BI /IM true /W 61 /H 28 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ“PZÿÿÉoþ  EI endstream endobj 203 0 obj <>stream 0 0 0 -42 20 13 d1 20 0 0 55 0 -42 cm BI /IM true /W 20 /H 55 /BPC 1 /D[1 0] /F/CCF /DP<> ID &±€zz.äÕ<7Þû{÷¿¿ÿýïøÿÿÿÿù úzzÿý­­¨€ EI endstream endobj 204 0 obj <>stream 0 0 0 -70 76 2 d1 76 0 0 72 0 -70 cm BI /IM true /W 76 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID & X|JÉ0jþºë&¡¿×××ÿ___ý}}~C__Òý/Òÿý/Òý/ÿÒý/Òÿý/Òý/ëõý/Òþ¿_Òý/Òÿý/Òý/ÿÒý/Òÿý/Òý/ÿŠúúúÿëë댆Zµ­}` EI endstream endobj 205 0 obj <>stream 0 0 0 0 73 72 d1 73 0 0 72 0 0 cm BI /IM true /W 73 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÒH‰0D 6B è==z5áôŸ¦ôŸ¯Û×éÿÿ«Õÿÿÿÿÿ¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÈ£Ð>¢8ÿÀ@ EI endstream endobj 206 0 obj <>stream 0 0 0 0 57 70 d1 57 0 0 70 0 0 cm BI /IM true /W 57 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID ù5“ƒOÿÿÿÿÿÿÿ!¬kÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâFoþ  EI endstream endobj 207 0 obj <>stream 0 0 0 -70 70 0 d1 70 0 0 70 0 -70 cm BI /IM true /W 70 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID @£ëêMD'’@½/ý/ý/ý/ý/ý/ý/ý/ý/ý/ý/_¯KÂøKÈDüBëƒáðù8ƒ~߇û÷öÿÿÿoÿÿÿÿ¯_ÿõúõô½|%y/U @ EI endstream endobj 208 0 obj <>stream 0 0 0 -82 94 0 d1 94 0 0 82 0 -82 cm BI /IM true /W 94 /H 82 /BPC 1 /D[1 0] /F/CCF /DP<> ID a¬j ]u×õ“P¦µõõõõÿ×××××ÿ______ý ëú_¥ú_¥ý~—ëú_¥ú_×é~—ëú_¥ú_×é~¿¥ú_¥ý~—é~¿¥ñ_____ý}}}}}õõõõõõÆA¨ kZ×À@ EI endstream endobj 209 0 obj <>stream 0 0 0 0 83 86 d1 83 0 0 86 0 0 cm BI /IM true /W 83 /H 86 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ªS†dƒHÖC,§‚‡‚„=„Iƒ‡@è zOA=.½/]z_é¥þ¾¿×ÿéÿ!˜?ãøÿÿY5ÿüš¯ÿÿíÿùjo÷úï÷î¿í®ÿk·î»kÚí®Ã †×`Áv …I†=‚Æ  E@‰´™Ú@@ EI endstream endobj 210 0 obj <>stream 0 0 0 0 90 82 d1 90 0 0 82 0 0 cm BI /IM true /W 90 /H 82 /BPC 1 /D[1 0] /F/CCF /DP<> ID *ÀÇÿÿ&¤NE@·K¿ÒÛý.ýë×oô»÷¯]¿Šý¯ß¯Ù ]®õë·ýv»×®ßõÚï^»×k½zíÿ]®õÚï_í¯××ï×í~ý~×íí~×ï×í@@ EI endstream endobj 211 0 obj <>stream 0 0 0 15 56 27 d1 56 0 0 12 0 15 cm BI /IM true /W 56 /H 12 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿð EI endstream endobj 212 0 obj <>stream 0 0 0 -84 80 2 d1 80 0 0 86 0 -84 cm BI /IM true /W 80 /H 86 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ªj ¹”™ ±< x ðžƒÂz*Á oAô›ÒoIô›Ò~¯WÒ¿}+÷ÒþêúùƒZÿéÿüÿÿòj¿ÿÿ·ÿä'º÷ÿº÷ïúíû¯~Úî»k¶½®]†a…ÙH' {† ,)ƒ"@DƒZŒƒlà EI endstream endobj 213 0 obj <>stream 0 0 0 0 89 82 d1 89 0 0 82 0 0 cm BI /IM true /W 89 /H 82 /BPC 1 /D[1 0] /F/CCF /DP<> ID %€F¾¾¼š„ ÀÁP/¯_¯_¯_Kë×ë×ë×ë×ë×ë×ë×ÒÿÒô½}/ y€¸® ‡Áð}ò ? øoÛöý¿ý¿ÿöÿÿÿÿÿÿÿÿÒÿ×ÿK×Òð—„¼Š7xADUB…+ Ö  EI endstream endobj 214 0 obj <>stream 0 0 0 0 75 82 d1 75 0 0 82 0 0 cm BI /IM true /W 75 /H 82 /BPC 1 /D[1 0] /F/CCF /DP<> ID þMBr 4ÿÿÿÿÿÿÿù ³_ÿÿÿÿä6Oÿÿÿÿÿÿþ?ÿÿÿälÿÿÿÿÿÿÿÿÿÒŸÿÿÿÿÿÿÿÿÇÿÀ@ EI endstream endobj 215 0 obj <>stream 0 0 0 0 82 82 d1 82 0 0 82 0 0 cm BI /IM true /W 82 /H 82 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œ–†ÿÿù€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ3ò)ÿÿÿÿÿÿÿâ?à EI endstream endobj 216 0 obj <>stream 0 0 0 0 89 86 d1 89 0 0 86 0 0 cm BI /IM true /W 89 /H 86 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡¨k I€, ¨ ðá„ðƒÑPè ÃÐA½&ô›ÒoI½/Vú½_Jýô¯ÿo_«×÷ûÿKÿÿ¿÷ÿÿÿÿäÕõÿ¯ÿÛþ½wþ»÷_­¿Òßî·Km{­ÒÛKm-´¶[ %† Ø…†°Âà  A©^A² EI endstream endobj 217 0 obj <>stream 0 0 0 0 74 82 d1 74 0 0 82 0 0 cm BI /IM true /W 74 /H 82 /BPC 1 /D[1 0] /F/CCF /DP<> ID ›ÿäÔ!_ÿÿÿÿÿÿÿÿÿÿü†ŠÿÿÿÿÿÿÇÿÿÿüƒ ÿÿÿÿÿÿÿñÿò tÿÿÿÿÿÿÿŒ€€ EI endstream endobj 218 0 obj <>stream 0 0 0 0 43 82 d1 43 0 0 82 0 0 cm BI /IM true /W 43 /H 82 /BPC 1 /D[1 0] /F/CCF /DP<> ID þMBaÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüHoø€ EI endstream endobj 219 0 obj <>stream 0 0 0 0 103 88 d1 103 0 0 88 0 0 cm BI /IM true /W 103 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID ¡ÿÿÿ&¡J€Ò!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ•¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‰Aÿÿ€€ EI endstream endobj 220 0 obj <>stream 0 0 0 -44 49 0 d1 49 0 0 44 0 -44 cm BI /IM true /W 49 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID &£!ˆH¯ÿýé¯ÿÿÝê¿ÿßëÍfž¿û×ñÿÿÞ®ýWÿÿÿïz×ÖÿÿþÕëþ?¿_ÿqÿ½}ð EI endstream endobj 221 0 obj <> endobj 222 0 obj <> endobj 226 0 obj <>endobj 227 0 obj <>stream 0 0 0 -44 49 0 d1 49 0 0 44 0 -44 cm BI /IM true /W 49 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ Ú¯ÿk÷¯ßë÷§úÿϬòëk~õÿÿ{ôëzõÿÿí7¥ÿþÿÓéwÿíúíô?ÿýé?ÿýÆõÿö  EI endstream endobj 228 0 obj <>stream 0 0 0 -57 61 -3 d1 61 0 0 54 0 -57 cm BI /IM true /W 61 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ±‚ÂÁ`°XX, ‚  ,,(…ÁÓT  <<<0x0px<ƒÃÁàðp EI endstream endobj 229 0 obj <>stream 0 0 0 -72 47 0 d1 47 0 0 72 0 -72 cm BI /IM true /W 47 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¿ÿü†ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃï‡Ãá÷Ãáòj‡¾¾¾‡€€ EI endstream endobj 230 0 obj <>stream 0 0 0 0 96 82 d1 96 0 0 82 0 0 cm BI /IM true /W 96 /H 82 /BPC 1 /D[1 0] /F/CCF /DP<> ID %?ÿù5AƒQÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿò ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ$–ÿÿø€ EI endstream endobj 231 0 obj <>stream 0 0 0 0 68 86 d1 68 0 0 86 0 0 cm BI /IM true /W 68 /H 86 /BPC 1 /D[1 0] /F/CCF /DP<> ID & \§ ©Æò§# Á‚¾ù ¾á7 ôß_Mõôÿ}õÿ÷ý/X𵄰\ Ô)Ì(È,ÈhÈ.§…ÂX\%¥®––— K… \.A¸’ Ê"àÐDÃH†°((U ¢ŽL~:þMUõÿm}v××mv^]ƒ!Âa}ðÂÃ%aá‘@bCA<†]  EI endstream endobj 232 0 obj <>stream 0 0 0 -61 40 2 d1 40 0 0 63 0 -61 cm BI /IM true /W 40 /H 63 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÜÀb@‚$8==8»¾ùNxMÓï^ß_÷ÿÿ_ßWZßk %UÔ†–ÖXacÿÿÿü@ EI endstream endobj 233 0 obj <>stream 0 0 0 -12 10 0 d1 10 0 0 12 0 -12 cm BI /IM true /W 10 /H 12 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿð EI endstream endobj 234 0 obj <>stream 0 0 0 -42 15 12 d1 15 0 0 54 0 -42 cm BI /IM true /W 15 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID >+“Uïûß¿ï~ûý¿ÿÿÿ!ƒ_ÿÿÿùgÿÿÿÿÿÿ€€ EI endstream endobj 235 0 obj <>stream 0 0 0 -72 49 2 d1 49 0 0 74 0 -72 cm BI /IM true /W 49 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œ 3È9|< ôÑÔG ƒz7Óé7Óë}?ñÚÿÿþòj©Úï[iv»im¥°Â[°Ö X xAè†7 ƒz7¤Þ“é7ÿO­ÿÿÿÿÿû×kÿ~év»im¥°`–Åmma…ƒÑH€ EI endstream endobj 237 0 obj <>stream 0 0 0 -53 52 0 d1 52 0 0 53 0 -53 cm BI /IM true /W 52 /H 53 /BPC 1 /D[1 0] /F/CCF /DP<> ID $dš§[kº]¥·[kém¥·[kém¥·í¥ém¥·í¥ÅmoµÖÖû_§úz}ë§¢B}7¯¤Þ“}7¯¤Þ“~Þ—¤Þ“~Þ—¤Þ“~ð EI endstream endobj 238 0 obj <>stream 0 0 0 -72 50 2 d1 50 0 0 74 0 -72 cm BI /IM true /W 50 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡°Ð4H.ŸzxAè†#ÐA¾›Ò}&úo_¦õúõoÿÿÒýÿûøëõûÿõêýnµ´­(iC (Aa©5V†ˆñýûÿ߿߲C–>è}ûûkß¶»k°×`Áv=ðøa`È€’) EI endstream endobj 239 0 obj <>stream 0 0 0 -73 38 16 d1 38 0 0 89 0 -73 cm BI /IM true /W 38 /H 89 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡•Ù\-k\†%ëú×ÿÿÿÿÿÿÿÿÿÿÿ­ôµÂX%eWɨ ïÞÿ¿ÿÿÿÿÿÿÿÿÿÿû÷ýíóQXkŽðp EI endstream endobj 240 0 obj <>stream 0 0 0 -73 38 16 d1 38 0 0 89 0 -73 cm BI /IM true /W 38 /H 89 /BPC 1 /D[1 0] /F/CCF /DP<> ID ( ³P7îäÔNß÷ïÿÿÿÿÿÿÿÿÿÿÿ~ÿöûØ{³_†8xXZÑ %Ö½_¯ÿÿÿÿÿÿÿÿÿÿõ¯ë¥“ªÂQ¨(€ EI endstream endobj 241 0 obj <>stream 0 0 0 -70 46 2 d1 46 0 0 72 0 -70 cm BI /IM true /W 46 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡‚pÙ! ä’`ÅÃB8A¸AºznŸn½¿¯o“PÓÿÿÿÿéé~aÌ:ñiw]…á¥Ã q\.¸\öƒ:ÿÿÿÿÿÿÿÈ4Çÿÿÿ EI endstream endobj 242 0 obj <> endobj 243 0 obj <> endobj 247 0 obj <>endobj 248 0 obj <>stream 0 0 0 -70 47 0 d1 47 0 0 70 0 -70 cm BI /IM true /W 47 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡M.ýïýïÞÿÞýïÞÿÞýïßïÞýïýïÞýïýïÞÿÞýïÞÿÞýïÜðe÷ï~÷þ÷‹ïà EI endstream endobj 249 0 obj <> endobj 250 0 obj <> endobj 254 0 obj <>endobj 255 0 obj <>stream 0 0 0 -70 99 0 d1 99 0 0 70 0 -70 cm BI /IM true /W 99 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID & `ÐMÇ]?ïþ×[ÿ×OûÿµÖÿôPºÊ¿}þÒô¾ýÿôŸ[þ¾ý÷ûKÒû÷ÿéõµúû÷ßô½o¾¿÷ßí/KïßÒõ¿ëïß´½/¿ý'Öÿ¯¿}þÒô¾ýÿâŸ_ë¿wúô»÷þŸ_븑žCA}ý¯à EI endstream endobj 256 0 obj <>stream 0 0 0 0 123 88 d1 123 0 0 88 0 0 cm BI /IM true /W 123 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID #@­ÿÿù5 h² /ÿÿÈeUúÿ¯÷ÿúÿ¯÷úÿ¯÷ÿúÿ¯÷úÿ¯ò£ÿêÿÕþ¯ý_ëÿMþ¿ôßÿú¿õ«ÿWú¿õÿé7ÿþ¯ý_êÿÕþ¯ý_ÿúMÿÿ«ÿWú¿õ«ÿWÿüSþý~ý~ýôñÉPouÝw]À@ EI endstream endobj 257 0 obj <>stream 0 0 0 -60 39 2 d1 39 0 0 62 0 -60 cm BI /IM true /W 39 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ¹°Ï"ŒÁÁww܆.<xVœ,/úúÒÂápKÂX\%„´´— @¸(… ª¿ Ýð¶ÂØ,š¡þÿ¾ûía‚ÃsÀ@ EI endstream endobj 258 0 obj <> endobj 259 0 obj <> endobj 263 0 obj <>endobj 264 0 obj <> endobj 265 0 obj <> endobj 269 0 obj <>endobj 270 0 obj <>stream 0 0 0 0 103 88 d1 103 0 0 88 0 0 cm BI /IM true /W 103 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID ¡•­kZדP¥@4!:^—¥éz^—¯¥éz^—×¥éz^—¥éz^¾—¥é}z^—¥éz^—¥Å]u×]u×*ö½¯oÛðß·ì?oÛöü7íûÛöý¿ û~Ãöý¿oÃ~ß°ý¿oۉȿÿÿþ  EI endstream endobj 271 0 obj <>stream 0 0 0 -88 90 0 d1 90 0 0 88 0 -88 cm BI /IM true /W 90 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID ; ðh;8.8pîMB•»ðoÁ¿oßÛ÷öÿ÷ûÿÿÿÿÿÿÿ¯¯ý/ý/_KÒð—„¼^F/Ä.ÊÐ7Ãàø>T ùÌx7á¿oÛ÷û÷ÿÛÿÿÿÿÿÿÿÒÿ×ÿK×Â^ y¼Œ^"B… w ™†`€ EI endstream endobj 272 0 obj <>stream 0 0 0 0 31 93 d1 31 0 0 93 0 0 cm BI /IM true /W 31 /H 93 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¢ƒÉÕ‚!ÝÉÉÐnž›ú÷ï“PÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþAñÿüƒI¯ÿÿ!²z§ÿÖû[P EI endstream endobj 273 0 obj <>stream 0 0 0 -70 60 0 d1 60 0 0 70 0 -70 cm BI /IM true /W 60 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID % ¹ 8rj!8;ò*ð߇öýþýÿöÿÿÿÿÿÿÿô¿ÿÒõô½/ y+ò#ˆ\.ÃäàÃðoÃ~߿߿þÿÿÿÿú_ÿëézú^ò$ET(PP EI endstream endobj 274 0 obj <> endobj 275 0 obj <> endobj 279 0 obj <>endobj 280 0 obj <>stream 0 0 0 0 69 70 d1 69 0 0 70 0 0 cm BI /IM true /W 69 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¾µ­I¨„á”j¥éz^—¥éz^—¥éz^—¥éz^—¥éz^—¥éz^—¥Åu×]u×]òtý¿oÛöü0ý¿oÛöý¿oÃ~Ãöý¿oÛöü7ì?oÛöâFÿÿÀ@ EI endstream endobj 281 0 obj <>stream 0 0 0 -72 69 2 d1 69 0 0 74 0 -72 cm BI /IM true /W 69 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡šN Ëàá„xF±^Ž€»Ð@ÃÐMéôŸI¾›ÒoKöô¯ßJýõú¿}¿úãþÿÿÿÿúýdÕ~ÿþ·_û­þ×zõÛ®ÒÝvëm.×m-°–à lK Ö0¶+ ,0°ÂÁ‚Èfž  EI endstream endobj 282 0 obj <> endobj 283 0 obj <> endobj 287 0 obj <>endobj 288 0 obj <>stream 0 0 0 -59 40 2 d1 40 0 0 61 0 -59 cm BI /IM true /W 40 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A°"B§Â=====?ï^j/>stream 0 0 0 -46 42 -4 d1 42 0 0 42 0 -46 cm BI /IM true /W 42 /H 42 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Á˜1ÿÿÿÿÿÿÿþ%Xoüšƒ`Çÿÿÿÿÿÿÿø€ EI endstream endobj 290 0 obj <>stream 0 0 0 -59 40 8 d1 40 0 0 67 0 -59 cm BI /IM true /W 40 /H 67 /BPC 1 /D[1 0] /F/CCF /DP<> ID 4’jŸíþýþßíþßíþßíþýþßíþßíþßíþýïßíþßíþßíþýïßíþßíþßíþß EI endstream endobj 291 0 obj <>stream 0 0 0 -60 38 2 d1 38 0 0 62 0 -60 cm BI /IM true /W 38 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Á°bC鄃Âz}ë¢A>}&ú}o§ÿVúÿþ6×ÿþJ}?ûzÿý¥ý®×ÿù5M/ÿÿm~»]þÒívÒØ0Kkkk ,5ƒ3 € EI endstream endobj 292 0 obj <>stream 0 0 0 -61 61 0 d1 61 0 0 61 0 -61 cm BI /IM true /W 61 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡”fÿÿÿÿÿÿÿÿÿÿÿþ$P7þMC(Ì2ÿÿÿÿÿÿÿÿÿÿÿÿÿÀ@ EI endstream endobj 293 0 obj <>stream 0 0 0 -37 42 -13 d1 42 0 0 24 0 -37 cm BI /IM true /W 42 /H 24 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ&¡µÿù+ ÿ€€ EI endstream endobj 294 0 obj <>stream 0 0 0 -59 38 0 d1 38 0 0 59 0 -59 cm BI /IM true /W 38 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID $ÿÿÿÿÿÿÿÿÿÇÿÉÿÿÿÿÿÿÿÿà EI endstream endobj 295 0 obj <> endobj 296 0 obj <> endobj 300 0 obj <>endobj 301 0 obj <>stream 0 0 0 0 66 70 d1 66 0 0 70 0 0 cm BI /IM true /W 66 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œ˜ ÿÿò‡ÿÿÿÿÿÿÿÿÿÿÿÿ×Oôû×OôCOJýô¯§Õ½+÷Òo«Õô¯ßI½+÷Ò¾›Õô¯ßI½+÷Ò¿x‘€Aÿÿ EI endstream endobj 302 0 obj <>stream 0 0 0 -59 42 0 d1 42 0 0 59 0 -59 cm BI /IM true /W 42 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡‚@cÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿĤ ÿð EI endstream endobj 303 0 obj <>stream 0 0 0 -59 40 0 d1 40 0 0 59 0 -59 cm BI /IM true /W 40 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID !€pwîû¹ |7‡ß½ÿïßÿÿÿÿÿ®¿_ý-uÂYÂEuUU Œ € EI endstream endobj 304 0 obj <> endobj 305 0 obj <> endobj 309 0 obj <>endobj 310 0 obj <>stream 0 0 0 -59 42 0 d1 42 0 0 59 0 -59 cm BI /IM true /W 42 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID ( ÿÿÿÿÿÿÿ“?¾¿ÿ¾¿ÿ¾¿ÿ¾¿ó?øÇÝݧÿ§þëü@ EI endstream endobj 311 0 obj <>stream 0 0 0 -59 42 0 d1 42 0 0 59 0 -59 cm BI /IM true /W 42 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿäÕC*ýíïÞÞýííþÞßííïÞÞßííþÞßíí÷¾ß{í÷”Sÿÿÿÿÿ EI endstream endobj 312 0 obj <>stream 0 0 0 -59 37 0 d1 37 0 0 59 0 -59 cm BI /IM true /W 37 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID $ÿÿÿÿÿò)ÿÿù ÿþEƒü@ EI endstream endobj 313 0 obj <> endobj 314 0 obj <> endobj 318 0 obj <>endobj 319 0 obj <>stream 0 0 0 0 24 78 d1 24 0 0 78 0 0 cm BI /IM true /W 24 /H 78 /BPC 1 /D[1 0] /F/CCF /DP<> ID :ˆ;»äÔ;ß}¿ïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåãÿùæ¿ÿ ¼ÿÿÿø€ EI endstream endobj 320 0 obj <>stream 0 0 0 -60 36 -22 d1 36 0 0 38 0 -60 cm BI /IM true /W 36 /H 38 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ƒÁÿÿùß!_OO‡…ˆxMêMPxK < °ÄVÂà ,0¿Aá< ðƒÂ(GxAa‡¶š °×[ì,5Ž?ÿÿð EI endstream endobj 321 0 obj <> endobj 322 0 obj <> endobj 326 0 obj <>endobj 327 0 obj <>stream 0 0 0 -70 58 0 d1 58 0 0 70 0 -70 cm BI /IM true /W 58 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID ù5“ƒWÿÿÿÿÿü†¹¯ÿÿÿÿò ÿÿÿÿÿÿÿÿÿù87ÿÿÿÿÿÿøÿÿiÿÿÿÿÿÿÿâ?þ  EI endstream endobj 328 0 obj <>stream 0 0 0 -48 25 0 d1 25 0 0 48 0 -48 cm BI /IM true /W 25 /H 48 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¿ÿ!?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàûá÷Ãä×ø}ðûàð EI endstream endobj 329 0 obj <>stream 0 0 0 0 31 48 d1 31 0 0 48 0 0 cm BI /IM true /W 31 /H 48 /BPC 1 /D[1 0] /F/CCF /DP<> ID &£:˜¼\98®zn®Ÿ~úɨÿýô¿8ç.-xipÁ.+…Í¥àÍñÿÿÿÿÿ!°~?ÿø€ EI endstream endobj 330 0 obj <>stream 0 0 0 -59 39 2 d1 39 0 0 61 0 -59 cm BI /IM true /W 39 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ¹°Ï"W“Q‚.u0à¸Aéºn½ºïÜšƒGÿÿõúýt³wø´»K†ä6¸®¸\ä ÇÿÿÿÿÿÿÈeÇÿÿÿ EI endstream endobj 331 0 obj <>stream 0 0 0 0 33 49 d1 33 0 0 49 0 0 cm BI /IM true /W 33 /H 49 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡J è=“ÒÓzWï¥~úý_þñÿß_ÿÿÿúù5_׺ÿuî·ûKuØivج5°°a@@ EI endstream endobj 332 0 obj <> endobj 333 0 obj <> endobj 337 0 obj <>endobj 338 0 obj <> endobj 339 0 obj <> endobj 343 0 obj <>endobj 344 0 obj <>stream 0 0 0 -73 32 0 d1 32 0 0 73 0 -73 cm BI /IM true /W 32 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID üšŒÖ/ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÿø€ EI endstream endobj 345 0 obj <>stream 0 0 0 0 58 54 d1 58 0 0 54 0 0 cm BI /IM true /W 58 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID & \œ È< ôôFo@‡¤Þ“é7­ôúßúßý7¯ÿÿÿö?ÿù5_×ÿÿþõÚÿ½¶—ûÖÚ[im¥±[Xam`Á`Ád N  EI endstream endobj 346 0 obj <>stream 0 0 0 -73 27 13 d1 27 0 0 86 0 -73 cm BI /IM true /W 27 /H 86 /BPC 1 /D[1 0] /F/CCF /DP<> ID þk ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþRÿ EI endstream endobj 347 0 obj <>stream 0 0 0 -73 28 13 d1 28 0 0 86 0 -73 cm BI /IM true /W 28 /H 86 /BPC 1 /D[1 0] /F/CCF /DP<> ID þMBÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇð EI endstream endobj 348 0 obj <>stream 0 0 0 -47 61 -14 d1 61 0 0 33 0 -47 cm BI /IM true /W 61 /H 33 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿäÔ¿ÿòdÿð EI endstream endobj 349 0 obj <>stream 0 0 0 -72 59 2 d1 59 0 0 74 0 -72 cm BI /IM true /W 59 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID & `Ð Hh‚„zxOF±½Þ“~Þ•êÿz_·¯÷þ·ÿú_ßÿÿïýÿÿÿÿÿ×ɪþ¿ÿÿu¿ÿÖÿÿ[¯ÖßõºíÖÚ]¥¶–Åa­¬0°ÂÁ‚Ȥ@ EI endstream endobj 350 0 obj <>stream 0 0 0 -73 66 0 d1 66 0 0 73 0 -73 cm BI /IM true /W 66 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¨_ÿÿü‹YÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ÿõÚ÷ö¼0—\Ö¾×µíxaxayÿÿÿÿÿÿÿûãÿ€€ EI endstream endobj 351 0 obj <>stream 0 0 0 0 32 73 d1 32 0 0 73 0 0 cm BI /IM true /W 32 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID üšŒÖ/ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾?ù5¯ù}==={ýÿë}­­… EI endstream endobj 352 0 obj <>stream 0 0 0 -33 30 -19 d1 30 0 0 14 0 -33 cm BI /IM true /W 30 /H 14 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿü@ EI endstream endobj 353 0 obj <> endobj 354 0 obj <> endobj 358 0 obj <>endobj 359 0 obj <>stream 0 0 0 0 44 44 d1 44 0 0 44 0 0 cm BI /IM true /W 44 /H 44 /BPC 1 /D[1 0] /F/CCF /DP<> ID :†5&©¥½v»uº[kõ¶–õÚíÖÅoÖÖýmmo×é÷®žŸzé÷¢Öô›÷Ò½[êôŸ[Òoø€ EI endstream endobj 360 0 obj <> endobj 361 0 obj <> endobj 365 0 obj <>endobj 366 0 obj <>stream 0 0 0 -59 44 0 d1 44 0 0 59 0 -59 cm BI /IM true /W 44 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID $_¥ú_¥ú_¥ú_¥ú_¥®–J+ªƒ‡wɼo÷ÿÿÿýuÂäÄG×UP ¤ l EI endstream endobj 367 0 obj <>stream 0 0 0 -60 46 2 d1 46 0 0 62 0 -60 cm BI /IM true /W 46 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A€1!õÂHðƒKOÂ¥Î²Ñ /H5ÂkIúN4é{¥Ñ:zOþ—¥zOþ—&©¥ú_º_i{i}þÅÖÖûXk­zÒý/ëÿÿÿ²~Âì9ñï¾ûá…ƒ' à EI endstream endobj 368 0 obj <>stream 0 0 0 -45 42 -6 d1 42 0 0 39 0 -45 cm BI /IM true /W 42 /H 39 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡«…‚ÂÁa`°–, X XA`aÁPPP¸pprk‡† <<0x0ðÁàÜ<‡ƒ€€ EI endstream endobj 369 0 obj <>stream 0 0 0 -45 42 -6 d1 42 0 0 39 0 -45 cm BI /IM true /W 42 /H 39 /BPC 1 /D[1 0] /F/CCF /DP<> ID 8¡ÁÇ&¨<<0ð`ðaáƒÁƒÁ‡†x,,X@°A`a‚‚ ¡AB‚…  EI endstream endobj 370 0 obj <> endobj 371 0 obj <> endobj 375 0 obj <>endobj 376 0 obj <> endobj 377 0 obj <> endobj 381 0 obj <>endobj 382 0 obj <>stream 0 0 0 -61 32 13 d1 32 0 0 74 0 -61 cm BI /IM true /W 32 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡Ÿ ë\<.´¿ÿÿ¯ÿÿÿÿÿÿ×õÒÉéa(¨Ww&¤ð}½ÿÿÿÿÿÿÿïÿÿöûàø5±Þ÷ƒ€€ EI endstream endobj 383 0 obj <>stream 0 0 0 -61 32 13 d1 32 0 0 74 0 -61 cm BI /IM true /W 32 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID !†páßrj>}¿ýÿÿÿÿÿÿÿÿûþöùÐìØáëZÑ °Z]_ÿÿÿÿÿÿÿúÿék‚Ø,UUA@@ EI endstream endobj 384 0 obj <> endobj 385 0 obj <> endobj 389 0 obj <>endobj 390 0 obj <>stream 0 0 0 -59 37 0 d1 37 0 0 59 0 -59 cm BI /IM true /W 37 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿä0/ÿÿÿ‘OÿÿÈ`_ÿÿò,ÿà EI endstream endobj 391 0 obj <>stream 0 0 0 -59 37 0 d1 37 0 0 59 0 -59 cm BI /IM true /W 37 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿä0/ÿÿÿÿÿÿÿÿÿÿÿø€ EI endstream endobj 392 0 obj <>stream 0 0 0 -70 8 -43 d1 8 0 0 27 0 -70 cm BI /IM true /W 8 /H 27 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿÿà EI endstream endobj 393 0 obj <>stream 0 0 0 -42 10 0 d1 10 0 0 42 0 -42 cm BI /IM true /W 10 /H 42 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿòjÿÿÿüœ7ÿð EI endstream endobj 394 0 obj <>stream 0 0 0 0 95 112 d1 95 0 0 112 0 0 cm BI /IM true /W 95 /H 112 /BPC 1 /D[1 0] /F/CCF /DP<> ID & xÍú`¸\.¸]u×]]yV(Œ Ì‚ád5‹äÀ²¢Ø x ðƒÂAáaH7 ›Ò é7¥zMêúWÓzú·Õêý^¿¿Þ—÷þ¯ÿWüÿíÿÿÿÿÿô¿þMWÿºÿ×ë¿Òßî·_­¿ÒÛ­×´¶ëm-´¶ÒÛ l4°Ã¶!a­…†X0X0Y 5yN€€ EI endstream endobj 395 0 obj <>stream 0 0 0 0 40 62 d1 40 0 0 62 0 0 cm BI /IM true /W 40 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡—ÿÿÿÿÿä1„#„O§Ðë__šž‚§þœzÿëïÿú“Tÿízíl5Ç{"× .D3š€€ EI endstream endobj 396 0 obj <> endobj 397 0 obj <> endobj 401 0 obj <>endobj 402 0 obj <>stream 0 0 0 0 96 88 d1 96 0 0 88 0 0 cm BI /IM true /W 96 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Úuʧÿ§úú§úú§ÿ§ÊµÖúýõ¾Ÿ[ëöõú}o¯Vÿéõ¾Ÿ[ÿ§Öú}oþŸ[éõ¾½[ÿ§Öú}oþŸ[éõ¿ú}o§Öÿé½~Ÿ[ëÕ¿ú}n$L2Ê@ßÿÿà EI endstream endobj 403 0 obj <>stream 0 0 0 -70 13 2 d1 13 0 0 72 0 -70 cm BI /IM true /W 13 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¬ã„œwõ&©ma…ÿæÿÿÿÿÿÿÿÿýÿûÿÿÿ×ÿÿ¿ÿÿøÿïÀ@ EI endstream endobj 404 0 obj <>stream 0 0 0 -72 75 2 d1 75 0 0 74 0 -72 cm BI /IM true /W 75 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Üœ6ä‚)ðƒXA­5„ÑN£ Z Vk„I®šÒ®’ÒKÓé}?¾–4tÞ“é^“é\šªOé_¤ý¥ú·º_i{¥ö“î•ö‘ m¥í׺^Ú^Å{Cµ†¶ºÒÒý.º×¯ëÈ7±þ?_öÿ^ýµívÂì.Ǿø`°d`iHeR  EI endstream endobj 405 0 obj <>stream 0 0 0 0 72 70 d1 72 0 0 70 0 0 cm BI /IM true /W 72 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÒN ?Oõïôÿôÿ^HOý+÷ÿI¿õ¾Ÿý+÷×é¿ô¯ßý&ÿÖúô¯ß_¦ÿÖúô›ÿ[éÿÒ¿ô›ÿ[éÿÒoýo§ÿJýâF‚\7ÿþ  EI endstream endobj 406 0 obj <> endobj 407 0 obj <> endobj 411 0 obj <>endobj 412 0 obj <> endobj 413 0 obj <> endobj 417 0 obj <>endobj 418 0 obj <>stream 0 0 0 0 93 88 d1 93 0 0 88 0 0 cm BI /IM true /W 93 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¹ ÿÿÿü‚ ƒYºí-×m-ºô¶×n½-µÛ¯Kmvë´·]ºí-×m-ºõÛKbºßk­öºß­-tº×K­tû×O½tùHž—«zMõêÞ“}^¯¤ßMéz·Óz^­ôÞ—«zMõêÞ“q"ÁÈÛÿÿÿð EI endstream endobj 419 0 obj <> endobj 420 0 obj <> endobj 424 0 obj <>endobj 425 0 obj <> endobj 426 0 obj <> endobj 430 0 obj <>endobj 431 0 obj <>stream 0 0 0 -70 68 0 d1 68 0 0 70 0 -70 cm BI /IM true /W 68 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID *ÿ“QLÁ¤è;uºö–Ýn½¥·[¯imÖëÚ[uºö–Ým¯¥·[in½ÖÅoÖÖýmkÖ–½ééþžŽ‰ôúW«}>•êßO¥z·Óé^­ôÞ—«}7¤úßMĈT_ÿþ  EI endstream endobj 432 0 obj <>stream 0 0 0 -59 8 -36 d1 8 0 0 23 0 -59 cm BI /IM true /W 8 /H 23 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿþ  EI endstream endobj 434 0 obj <>stream 0 0 0 -52 65 0 d1 65 0 0 52 0 -52 cm BI /IM true /W 65 /H 52 /BPC 1 /D[1 0] /F/CCF /DP<> ID #Wÿù5¬)Çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿÿÿÿÿ¿ÿ®×¿µá„¸î¢k]­­¬0¤Ô‚Èjü@ EI endstream endobj 435 0 obj <>stream 0 0 0 0 42 68 d1 42 0 0 68 0 0 cm BI /IM true /W 42 /H 68 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œ <ƒ—ÂAéë¢!>?M×ïÿÿüƒ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿï!LjÿäÔe81ÿÿÿÿÿÿÿà EI endstream endobj 436 0 obj <>stream 0 0 0 0 29 83 d1 29 0 0 83 0 0 cm BI /IM true /W 29 /H 83 /BPC 1 /D[1 0] /F/CCF /DP<> ID )ƒ‡ww÷& ÷ý¿ÿÿÿÿÿÿÿÿÿÿÿÿÿ“Çÿÿÿ!škÿò ‡ÿÿÿÿÿ€€ EI endstream endobj 437 0 obj <> endobj 438 0 obj <> endobj 442 0 obj <>endobj 443 0 obj <> endobj 444 0 obj <> endobj 448 0 obj <>endobj 449 0 obj <>stream 0 0 0 0 72 82 d1 72 0 0 82 0 0 cm BI /IM true /W 72 /H 82 /BPC 1 /D[1 0] /F/CCF /DP<> ID þMBr ÿÿÿÿÿÿÿÈ6ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø‘ Íÿð EI endstream endobj 450 0 obj <>stream 0 0 0 -82 77 0 d1 77 0 0 82 0 -82 cm BI /IM true /W 77 /H 82 /BPC 1 /D[1 0] /F/CCF /DP<> ID €ÕÿäÔ! ¿ÿÿÿÿÿÿÿÿÿÿÿÿÈ>q$|ß¾ Â?ý¿íÿíûýûÿ÷ßÿÿÿÿÿ¯¯ýô¿ô½}/KÁ/#¼%P¡B‚•€n  EI endstream endobj 451 0 obj <>stream 0 0 0 -59 42 0 d1 42 0 0 59 0 -59 cm BI /IM true /W 42 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID aÁÁÝÝ÷Éœ>ß¿ÿýkúéar1UP¡AÝ܆÷ß¿ÿÿ×KÁbºêAI0\@ EI endstream endobj 452 0 obj <> endobj 453 0 obj <> endobj 457 0 obj <>endobj 458 0 obj <> endobj 459 0 obj <> endobj 463 0 obj <>endobj 464 0 obj <>stream 0 0 0 -56 61 -4 d1 61 0 0 52 0 -56 cm BI /IM true /W 61 /H 52 /BPC 1 /D[1 0] /F/CCF /DP<> ID .`ààáÁÁÁÓT<<<x`ð`ð`ðaáƒÁ¸<ðX,,,X X X@°A``aÁPPP   ¡A@@ EI endstream endobj 465 0 obj <> endobj 466 0 obj <> endobj 470 0 obj <>endobj 471 0 obj <> endobj 472 0 obj <> endobj 476 0 obj <>endobj 477 0 obj <>stream 0 0 0 0 22 23 d1 22 0 0 23 0 0 cm BI /IM true /W 22 /H 23 /BPC 1 /D[1 0] /F/CCF /DP<> ID >/ÿÿÿÿÿÿÿÿ EI endstream endobj 478 0 obj <> endobj 479 0 obj <> endobj 483 0 obj <>endobj 484 0 obj <> endobj 485 0 obj <> endobj 489 0 obj <>endobj 490 0 obj <> endobj 491 0 obj <> endobj 495 0 obj <>endobj 496 0 obj <>stream 0 0 0 -74 8 23 d1 8 0 0 97 0 -74 cm BI /IM true /W 8 /H 97 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿÿÿÿÿÿÿÿÿÿÿÿ€€ EI endstream endobj 497 0 obj <> endobj 498 0 obj <> endobj 502 0 obj <>endobj 503 0 obj <> endobj 504 0 obj <> endobj 508 0 obj <>endobj 509 0 obj <>stream 0 0 0 0 138 88 d1 138 0 0 88 0 0 cm BI /IM true /W 138 /H 88 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Öj cXiSþýtÿ¿]~ïýW¿ï×OûõÓþýtÿ¿]~ÿ¿]?åB~¾Š´ÿÞ›ý}+ÿzoõô¯ÿ¦ý×Òÿïýé¾µõ¿÷¦ÿ_JÿÞ›ý}+ÿé¿uô¿û¯ßo­}oýé¿×Ò¿÷¦þ?Ký¦ÿô¿é¿Kÿý¦úýÚoÿKý¦ÿô¿é¸‘0Q  Çõßÿí EI endstream endobj 510 0 obj <>stream 0 0 0 -90 58 0 d1 58 0 0 90 0 -90 cm BI /IM true /W 58 /H 90 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¿ÿÿò(ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒï‡Ãï‡Ãá÷Ç&¿¾¾¾¾  EI endstream endobj 511 0 obj <>stream 0 0 0 -59 38 0 d1 38 0 0 59 0 -59 cm BI /IM true /W 38 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿäÔ ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿò³æ—ÿÿÿÿ‘§È! EI endstream endobj 512 0 obj <> endobj 513 0 obj <> endobj 517 0 obj <>endobj 518 0 obj <>stream 0 0 0 -90 65 0 d1 65 0 0 90 0 -90 cm BI /IM true /W 65 /H 90 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿþMVÍA­°øo°øo°øo°øo°û ƒI¬7°öðÃÛØ{xaííì=½¾ööß{í÷Þýïß÷¿ÿÿû$>/õ×ß­Ö·VºPÒ°”0”ƒ5 ¤Õ“-Ȃ٠Èet@ EI endstream endobj 519 0 obj <>stream 0 0 0 -60 38 0 d1 38 0 0 60 0 -60 cm BI /IM true /W 38 /H 60 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿäÕE½½½½½½½½¾öööööööö÷ÛÛý½ûßöÿÿÊuÃð×—!Ÿ®¿®¸X5ƒè€€ EI endstream endobj 520 0 obj <> endobj 521 0 obj <> endobj 525 0 obj <>endobj 526 0 obj <> endobj 527 0 obj <> endobj 531 0 obj <>endobj 532 0 obj <> endobj 533 0 obj <> endobj 537 0 obj <>endobj 538 0 obj <>stream 0 0 0 -82 115 0 d1 115 0 0 82 0 -82 cm BI /IM true /W 115 /H 82 /BPC 1 /D[1 0] /F/CCF /DP<> ID aµÿÿɨS0<7 Âÿü}¿ÿÓÿý?ÿ×ûý¿ÿÓÿý?ÿ×ûý¿äþ·þÿ¥ïú_û¥ÿ·ý­ÿ¿é_ûþ—þßéíÿ_ëïúWþÿ¥ÿ·ú_û×ú·ÿþ•ÿ¿éíÿ_ÆÇ×ïÿ_¿_¿\l‹ë¿]Ö  EI endstream endobj 539 0 obj <>stream 0 0 0 0 88 82 d1 88 0 0 82 0 0 cm BI /IM true /W 88 /H 82 /BPC 1 /D[1 0] /F/CCF /DP<> ID +ÖRÃ08päÔ!ùÉø0ý¿ û~ß¿·÷ï÷ï÷ï÷ÿ·ÿÿûÿÿÿÿÿÿÿÿÿÿÿúÿÿÿõúÿÿ¯_þ½~½~½}/¯KÒð—¥á/¼ˆ7xADB… VÁ ’˜€ EI endstream endobj 540 0 obj <> endobj 541 0 obj <> endobj 545 0 obj <>endobj 546 0 obj <> endobj 547 0 obj <> endobj 551 0 obj <>endobj 552 0 obj <>stream 0 0 0 0 49 59 d1 49 0 0 59 0 0 cm BI /IM true /W 49 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID &£$$¿þ¿}ÿúÿ¯ßÿ¯ßýÿê¿ÿÉÓ™¯ëÿßÿýzßûÿÿÕß¿ÿ®ÿ_ÿÿ¿õõßÿþ¯ý~?þÿýõÇÿÿÿ€€ EI endstream endobj 553 0 obj <>stream 0 0 0 -56 46 0 d1 46 0 0 56 0 -56 cm BI /IM true /W 46 /H 56 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡˜P8Aéá>ôù¡ôoO®¯ÿÿö×®ÒÍ 5àâ´ðmpÚÁƒaa†Áƒ0ð`ðÃÁƒÈX <Ááz`Áé†ôÁôÃóBa®ƒÅ8í×ÿÿuk&©¯a-­­­¬0  EI endstream endobj 554 0 obj <>stream 0 0 0 -59 40 8 d1 40 0 0 67 0 -59 cm BI /IM true /W 40 /H 67 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÓÖ¿¥ú_¥ú_¥ýkÖ¿¥ú_¥ú_¥ú_Ö¿¥ú_¥ú_¥ú_Ö¿¥ú_¥ú_¥ú_Ö½kñP EI endstream endobj 555 0 obj <> endobj 556 0 obj <> endobj 560 0 obj <>endobj 561 0 obj <>stream 0 0 0 -59 38 0 d1 38 0 0 59 0 -59 cm BI /IM true /W 38 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¢šÍþýïýïßïÞÿÞýïýïÞÿÞÿ·ýïûûÞÿ·ÿ·ûû÷à EI endstream endobj 562 0 obj <>stream 0 0 0 -60 39 2 d1 39 0 0 62 0 -60 cm BI /IM true /W 39 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ƒ²E«‡ß}÷ÏŠø ü Þ-ï¾ßïßïùÁáðŸO§Ðèëp@ôéôõ}}ÿýcïõ¬š¯ßÿׯ~ëÚ]®ÃKa‚ñ[[[Xaa…ƒ EI endstream endobj 563 0 obj <>stream 0 0 0 0 38 62 d1 38 0 0 62 0 0 cm BI /IM true /W 38 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ƒažHfrqÝÊÏ‚î<&á?ïÝÿþ—þºZéa,„|W\.¸|>òÃá½ûßÿÿÿ_ÎépkÍø®ºë®HFCž  EI endstream endobj 564 0 obj <>stream 0 0 0 0 37 61 d1 37 0 0 61 0 0 cm BI /IM true /W 37 /H 61 /BPC 1 /D[1 0] /F/CCF /DP<> ID & æÃ<± qîîN#‚àƒÂnþß¿ÿÿý~µ×K4/ ã뮺áp¹šãÿÿÿÿÿþC`üÿÿð EI endstream endobj 565 0 obj <>stream 0 0 0 -59 42 0 d1 42 0 0 59 0 -59 cm BI /IM true /W 42 /H 59 /BPC 1 /D[1 0] /F/CCF /DP<> ID & `Ð_ÿÿÿÿÿñ5†ÿÍsAIªû~ÿ~þßß¿·ÿ·íÿíÿíûû~ÿ~ßþ߿Ǿÿ¾ÿ¿ï¼@ EI endstream endobj 566 0 obj <>stream 0 0 0 -60 38 2 d1 38 0 0 62 0 -60 cm BI /IM true /W 38 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A°^Cë„ è=={Ñ W ƒôÞ—ÓýÒÿúqÿ÷×÷ÕÖ¶°Ò† S4¶¶¬0°`±“UþÿoûÛý†@û °Áv=ÿ¾  œ(€ EI endstream endobj 567 0 obj <>stream 0 0 0 -60 39 2 d1 39 0 0 62 0 -60 cm BI /IM true /W 39 /H 62 /BPC 1 /D[1 0] /F/CCF /DP<> ID & Á°"BÕ‚„žžžŸ$éøOuÓÿ÷­ï“U^ÒÛ^ÒØa-ŠÚÃXau„z:o“„é7¤ßOíéÿÿÿÿßë·ö—av %±õµ†¶Y<@ EI endstream endobj 568 0 obj <> endobj 569 0 obj <> endobj 573 0 obj <>endobj 574 0 obj <>stream 0 0 0 0 93 84 d1 93 0 0 84 0 0 cm BI /IM true /W 93 /H 84 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ªS†´ƒPC0[<z===±_&Žôƒ|'Òo§Õ¾¿}+÷ÿ¯ý[ÿþ¿ÿÿßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûȃÐ>¢8ÿø€ EI endstream endobj 575 0 obj <> endobj 576 0 obj <> endobj 580 0 obj <>endobj 581 0 obj <>stream 0 0 0 0 116 105 d1 116 0 0 105 0 0 cm BI /IM true /W 116 /H 105 /BPC 1 /D[1 0] /F/CCF /DP<> ID "@×ÿÿÿ&¢à½Ú[ý®õßéoö»×~éÚï]®õÿ¿í[þ×ò@4-þ×zïÝ/ûK¿t¿í-þýÒÿ´·û÷K¿ÒßïÝ.ýÒÿºÝ{÷KþëuïØ¯ëÚþ·ëë~·ý¯ëÚþ·ý¯€€ EI endstream endobj 582 0 obj <>stream 0 0 0 0 93 112 d1 93 0 0 112 0 0 cm BI /IM true /W 93 /H 112 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡”k)êðƒ#„ ðŸO§ÐëZ"y ½ úOÓé}>¯þ—ïþ—ÿ¿úÿÿÿ_ÿÿÿÿÿÿ&«ÿÿýÿÿþÿ¯ÿíÿúíþ»ý®Úö»k°Âñï¾ÉjïíxkÚðÂð׃ä2êñÿÿÿÿÿÿÿÿÿÿÿò Ãüÿÿÿð EI endstream endobj 583 0 obj <>stream 0 0 0 0 48 110 d1 48 0 0 110 0 0 cm BI /IM true /W 48 /H 110 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ“P¤˜_ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷Çÿüšƒ'ÿÈL xOOOO½{×ûÿÿþ¿~·ëkkk ,P EI endstream endobj 584 0 obj <>stream 0 0 0 0 63 100 d1 63 0 0 100 0 0 cm BI /IM true /W 63 /H 100 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡”A†$àƒÂAéëÞˆø } ß¿^¯÷ÿ÷ÿÿÈeÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼†qâ?ÿäÔ)&_ÿÿÿÿÿÿÿÿÿÿà EI endstream endobj 585 0 obj <>stream 0 0 0 0 86 79 d1 86 0 0 79 0 0 cm BI /IM true /W 86 /H 79 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÔAƒT†XÞx@ðžzoD˜G ƒzMé7¤ßÞ“ë}7¯­ÿÓzÿëÿ÷¯ï×ÿÿÿÿòj¿ÿëÿû_÷¯ÿßúÿm~·þ¶ÒÿzÛK´¶ÒØa-†Ã¶¶°Âà ,,,ƒXˆ€ EI endstream endobj 586 0 obj <>stream 0 0 0 0 98 77 d1 98 0 0 77 0 0 cm BI /IM true /W 98 /H 77 /BPC 1 /D[1 0] /F/CCF /DP<> ID ¥ÿÿÿ&¡I0.I…ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿßÿÿÿßÿißÚú\5á¯%㺉&–û[[[Xaa©5PYz  EI endstream endobj 587 0 obj <>stream 0 0 0 0 85 79 d1 85 0 0 79 0 0 cm BI /IM true /W 85 /H 79 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ¹¬ Š€d„+>ƒéôO§ã®w0”FÏÁééú~¿xÿ§ÿÿ“Uÿïÿýþþýþý‡íü²(/cÃï‡Ãáò2Õ2 ¤ÿÿúÿÉF?ƽ×ÿö—ý¥Úö—kØKƒ q]pºá`ÂÈ!fA\‚…p EI endstream endobj 588 0 obj <>stream 0 0 0 0 48 110 d1 48 0 0 110 0 0 cm BI /IM true /W 48 /H 110 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ“P¤˜_ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñÿÿð EI endstream endobj 589 0 obj <>stream 0 0 0 -105 97 0 d1 97 0 0 105 0 -105 cm BI /IM true /W 97 /H 105 /BPC 1 /D[1 0] /F/CCF /DP<> ID ÿ“P~L§ÿÿÿÿÿÿÿÿþ@œ×ÿÿÿÿÿÿÊGÿÿÿÿÿÿÿÿÿüÿÿÿÿÉ€ÊÿÿÿÿÿÿÿÿÿÿãÿþCmÿÿÿÿÿÿÿÿÿÿÇÿü@ EI endstream endobj 590 0 obj <>stream 0 0 0 0 87 75 d1 87 0 0 75 0 0 cm BI /IM true /W 87 /H 75 /BPC 1 /D[1 0] /F/CCF /DP<> ID #BÿÿÿɨR€0@Ân¶×t»­´¶×ÒÛ­µÝ.ëm-µô¶ëmwKŠÚß­­öºÚßKK_ÓÓý=={ÓÑO§Öô›ÒoJô›éõ½&ôŸÛÒ½&ôŸî$\dÿÿÿÿ EI endstream endobj 591 0 obj <>stream 0 0 0 0 92 107 d1 92 0 0 107 0 0 cm BI /IM true /W 92 /H 107 /BPC 1 /D[1 0] /F/CCF /DP<> ID !`©ÿþMB`QÿÿÿÿÿÿÿÿÿÈ"Âáè? úïÓâûä£ò(ðƒ}?Môßý7ÿ_·ÿ_ß¿ÿýÿúÿÿÿÿÿÿÿÿÿþÿÿ×ÿúÿÝÿ¥ßõÚú]ý¥Ø^ %Å]EHªì-­­…µ†š‚°,‚•P EI endstream endobj 592 0 obj <>stream 0 0 0 0 70 79 d1 70 0 0 79 0 0 cm BI /IM true /W 70 /H 79 /BPC 1 /D[1 0] /F/CCF /DP<> ID & A0ä)NMK)f!û”†äaÁp›§¦éë«úþ¾êºÔš†¾ ²¥9¸.@…LÂáp–×KKK…¥ÂÐ\ ¤´2‰H4PÒ#a¬ iŒzŽ—ëýdÕ~××m}v׆`Áv=÷߆ –‡ S  $@ EI endstream endobj 593 0 obj <>stream 0 0 0 0 77 70 d1 77 0 0 70 0 0 cm BI /IM true /W 77 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID Ãgÿù5#0Ô"׺ÝÛ¯^ëuÿn½vëö¿kúÙ˜húíׯu¿ë·^½Öÿ®Ýzíׯûuë·^½Öÿ®Å[þ×õ¿í~×õ¿íP EI endstream endobj 594 0 obj <>stream 0 0 0 0 50 52 d1 50 0 0 52 0 0 cm BI /IM true /W 50 /H 52 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¨‰†?ÿþE¯ÿÿÿÿÿÿÿÿÿûÿÿÿþüƒ®ãë¿~×k†5¡¿Þ÷¹5 ,†·À@ EI endstream endobj 595 0 obj <>stream 0 0 0 0 62 75 d1 62 0 0 75 0 0 cm BI /IM true /W 62 /H 75 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡¤hä+• žƒÓ‹¹5&Ÿ Åôêýúý¾¿¿ýÿÿþ¯ÿÿÿÿÿû¯ÿÿ×ëþÿK¿Òíxiqõ×5¥í{ à à È_ñÿÿÿÿÿÿÿ¾?ø€ EI endstream endobj 596 0 obj <>stream 0 0 0 0 65 52 d1 65 0 0 52 0 0 cm BI /IM true /W 65 /H 52 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡ Ð5$¯2jè>ŸO¡×*C¢ ½éú}ÿ÷ÿ¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿï!ãÿþ  EI endstream endobj 597 0 obj <>stream 0 0 0 0 54 54 d1 54 0 0 54 0 0 cm BI /IM true /W 54 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID &  œ6H0<' ôBoD@G ƒé7¤ßO­ôúþßú_ÿÿÆ£òj¹úÿÿ÷¯ývëýëý´¿ÛKl%±[Xk ,0°`² ) EI endstream endobj 598 0 obj <>stream 0 0 0 0 47 54 d1 47 0 0 54 0 0 cm BI /IM true /W 47 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡‚g‘¤Î)áܧœèÂnÓuý}ôµ& c ÆAÆð\ k¥¥¥¥ÂˆPPR4€\",zŽ—ÿY5Uí{]†cß}ðdhpd y EI endstream endobj 599 0 obj <>stream 0 0 0 0 45 73 d1 45 0 0 73 0 0 cm BI /IM true /W 45 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¨‰„ÿÿù¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼‡#ÿ“QMa£ÿÿÿþA^‡[kmqÞððdA€€ EI endstream endobj 600 0 obj <>stream 0 0 0 -72 71 2 d1 71 0 0 74 0 -72 cm BI /IM true /W 71 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÖH © ¤™â˜D¨$ x@ðƒÑ8@è zOK¥Òõ×¥þ—úúÿ_úþ?ÈÇÿò F¿ÿÿ“Uïü†¼÷ÿíwÿÛ]þ×mwû]µÛ]†a…à ±á‚à  ‰ƒÁ’ažA¬ˆ€ EI endstream endobj 601 0 obj <>stream 0 0 0 0 62 72 d1 62 0 0 72 0 0 cm BI /IM true /W 62 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID #`Ñÿ“QšÃ[ÿÿÿÿÿù O%_„Óô§Å÷É4ù/ ßWï×íõýÿïÿÿõÿÿÿÿÿÝÿþ¿_÷ú]þ—kÃKŽê*T%µ°°Â“Pi !§ð EI endstream endobj 602 0 obj <>stream 0 0 0 0 58 54 d1 58 0 0 54 0 0 cm BI /IM true /W 58 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID & †a«"U„%pƒéô޹ ‰rðŸ§÷÷Šÿù5_þÿöÿöß² ûø|>Î5k¯ÿÿ,rc¯Ú÷^½¥Ã q]uÂÁ…ªòÏ EI endstream endobj 603 0 obj <>stream 0 0 0 0 62 75 d1 62 0 0 75 0 0 cm BI /IM true /W 62 /H 75 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡œÐ4¤1^e¨„øO§ÐëD"$Ç‚"ƒÒ~ŸK÷Òýÿ×ëÿßÿÿÿÿù5_ÿýÿ¿ÿ]¿×oívÂì¾Èá¯kà à Èhÿÿÿÿÿÿÿÿ Ò?Çÿÿ EI endstream endobj 604 0 obj <>stream 0 0 0 -70 36 9 d1 36 0 0 79 0 -70 cm BI /IM true /W 36 /H 79 /BPC 1 /D[1 0] /F/CCF /DP<> ID (äÕ{ýûýûýûÿ߿߿߿߿߿ýûýûýûýûýûÿ߿߿߿߿߿ýûýûýûýûýû EI endstream endobj 605 0 obj <>stream 0 0 0 -52 52 2 d1 52 0 0 54 0 -52 cm BI /IM true /W 52 /H 54 /BPC 1 /D[1 0] /F/CCF /DP<> ID & \œ ¾< ðžƒÂ!7¢ G ƒé7Óêß^­õëû©“]ÿÇÿɪÿÿöA@à‚Ýÿ÷û]þ×mxk¶cÃï† ƒ ^ EI endstream endobj 606 0 obj <>stream 0 0 0 0 61 50 d1 61 0 0 50 0 0 cm BI /IM true /W 61 /H 50 /BPC 1 /D[1 0] /F/CCF /DP<> ID &  ê +úÞ¿§ýëúÊtõú}o¯Û×éõ¾¿o_§Öúý½~Ÿ[ëöõú}o¯Û×éõ¸•È€ßÿø€ EI endstream endobj 607 0 obj <>stream 0 0 0 0 97 52 d1 97 0 0 52 0 0 cm BI /IM true /W 97 /H 52 /BPC 1 /D[1 0] /F/CCF /DP<> ID l_ÿÿù5¬5† qÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõûÿÿÿ¿ÿÿ¾¾×´þÿ´\0ƒ ĈC½¨šÒ­§Ú m5†ƒ MA¨ad5<†WÀ@ EI endstream endobj 608 0 obj <>stream 0 0 0 0 66 73 d1 66 0 0 73 0 0 cm BI /IM true /W 66 /H 73 /BPC 1 /D[1 0] /F/CCF /DP<> ID #GÿþMFkÂéz_ú^—¥ëõéz^—ý¥Ú]¥Ç×]uüׯkÚ÷ö½¿oÛöü7íû~Ãöý¿oÈ8Oÿÿÿüÿÿÿÿÿÿÿãÿà EI endstream endobj 609 0 obj <>stream 0 0 0 0 62 74 d1 62 0 0 74 0 0 cm BI /IM true /W 62 /H 74 /BPC 1 /D[1 0] /F/CCF /DP<> ID &¡° $^DÉ”wÃïþo ßMõ}?ß¿øßþù ôò¯ÂÂ~ôý]‹ÁAé?O¥ûé~ÿëõÿïÿÿÇÿÿüš¯ÿþ¿ÿß¿ÿ®ßë·ö»av?[%†»\0¸ai5Ñð EI endstream endobj 610 0 obj <>stream 0 0 0 -70 62 0 d1 62 0 0 70 0 -70 cm BI /IM true /W 62 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID üš‰ÈÀËÿÿÿÿÿÿü†‘¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‰0?ð EI endstream endobj 611 0 obj <>stream 0 0 0 -70 37 0 d1 37 0 0 70 0 -70 cm BI /IM true /W 37 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID üšˆF ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâLÿ EI endstream endobj 612 0 obj <>stream 0 0 0 0 61 72 d1 61 0 0 72 0 0 cm BI /IM true /W 61 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID & çÁ²D«}÷üÐo¦úoÿ~¯þõï_ï^õý?ï_Óý?ý?Óäbé}>·ï¥ýé?ßO¯÷¤ÿzOÿW«ýé?úWïÕêýw“…ÔGÿø€ EI endstream endobj 613 0 obj <>stream 0 0 0 -70 74 0 d1 74 0 0 70 0 -70 cm BI /IM true /W 74 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID &ÿ“Q ÃPœOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñÿÿÿ' OÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄŒ €ßÿø€ EI endstream endobj 614 0 obj <>stream 0 0 0 -70 98 0 d1 98 0 0 70 0 -70 cm BI /IM true /W 98 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID *ÃSÿüšŠf`Ÿÿÿ!£OÿÓÿý?ÿÿÓÿý?ÿÓÿþÿ_ÿÓÿýÿÿI¿ÿú¿Õÿ¯õoÿý&ÿÿÒoÿþ›ýëý[ÿÿI¿ÿô›ÿÿ¦ÿ_úÿVÿÿŠcÿ§ÿ§ÿ¯ßø¦FÃ~Ÿ§à EI endstream endobj 615 0 obj <>stream 0 0 0 -70 79 2 d1 79 0 0 72 0 -70 cm BI /IM true /W 79 /H 72 /BPC 1 /D[1 0] /F/CCF /DP<> ID & ÖC¹ µyXAá„ôž‰¾T:Ò ôú_Mûé_¿_ú¿ß_ÿÿÿ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞEƒê#ˆÿü@ EI endstream endobj 616 0 obj <>stream 0 0 0 -70 83 0 d1 83 0 0 70 0 -70 cm BI /IM true /W 83 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID "Âÿ“QÀË#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñÿÿÿò02ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`B,ÿÿ EI endstream endobj 617 0 obj <>stream 0 0 0 0 66 70 d1 66 0 0 70 0 0 cm BI /IM true /W 66 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID %—ÿ“QÀÖÿÿÿÿÿÿÿÿÿÿþC¿p_Ãáðûä`¯Ã~÷öý¿ý¿ÿÿïÿõÿÿþ—þ¾—¥ëá/ ^YˆŠ… @ EI endstream endobj 618 0 obj <>stream 0 0 0 -70 80 0 d1 80 0 0 70 0 -70 cm BI /IM true /W 80 /H 70 /BPC 1 /D[1 0] /F/CCF /DP<> ID *Á¨è%]u“Q†§×ÿ______ý}}}}õôU¯¥ú_¥ú_×ëú_¥ú_¥ý~—ëú_¥ú_¥ý~—ëú_¥ú_¥ý|Wÿ_____ý}}}}}ÅËVµ­k EI endstream endobj 619 0 obj <> endobj 620 0 obj <> endobj 621 0 obj <>stream xœ]”ÁnÛ0Dïú ý¹K‰Jƒ—ô’C‹íÈèYPœCÿ¾³³Ž\ô0†¤–û8$O/¯ß^×åÖž~î×ò«ÞÚyY§½~\?÷RÛK}[ÖF´–r»;þ–÷qkN/ßÇí÷Ÿ­¶˜PçF|F¹NõcKÝÇõ­6çòyžsS×é¿¡Ôù—ùŸ©®T3¬dW:³š]!fcv…®˜í²+ôÁlŸ]¡S³)»Bdå!»‚rô)»ÂÀRÏÙúÁì˜]!ŠÙ þ–‡²+D–ªÙºÉìœ]¡3œ„£²N¨½YÀ {kC'Œœ 8!`|6 8!`Œf'ÈR€F® 8!`Lf'ŒOf'ìù-MèÙ¬BÞÄR`ò&¶V©GFV!oBäg«>U°*y“UÆÞS°Æ«`UÔÚP°*yk;DÁZ¾ V%ï`ø VŽž¬êZ( V%o¢«’wàB`Uò&Z°jyT«:¯m‚U=_ EÁªóÑUk  uß: qÛæ 7 £¶‚¢PÊBŒFí !7 öb¬‘¼ÉÚ@ŒÔý.à€P°–Qkt^Z°FÏ×66‚5–ãÄF°Æé8 ¸.Ô×d°Æù¸eX;òv•7üë*Ûe·7žˆã{mËç¾×õƇ„‡=ËZ·f»nöU 5hõ  endstream endobj 622 0 obj 529 endobj 433 0 obj <>/FontMatrix[1 0 0 1 0 0]/FontBBox[0 0 1000 1000]/FirstChar 0/LastChar 64/Widths[ 727 462 602 703 752 602 838 602 337 1123 696 602 696 602 1107 867 602 602 602 602 602 602 602 602 602 602 872 776 699 380 462 667 727 648 380 762 596 699 563 776 527 699 727 636 563 430 854 699 648 699 365 609 581 1058 693 699 703 468 581 872 1107 872 945 752 914] /Subtype/Type3>> endobj 623 0 obj <>stream xœe×Kn7 Àá½Oád¤yI€1›t“E‹íô,¼ˆm8É¢·ï”Ì Ò ž‡>’ÃùϧÏ_~ûòòüýñÓŸï¯å¯öý±?¿Ô÷öíõÇ{i¹ýóüòàüc}.ßçJÿ–¯éíáÓçßÓÛßÿ¾µGhýÁ#ÊkmßÞRiïéåŸöð´,×Sï×C{©¿üË-Ë8%÷ŸŽ±oKwXÎ]–þ±º\¯˺Êr»F,«“å~X¶*Ëã±úßó±œ^–ᱬºŒ×ˆe]d™®Ëe™¯˪›,׈eM²¬×ˆe×sÛ5bÙôà~XŽÀÒauê=å¿«^9×auêõ]–XzÏS–Xz}“%V§ÞC¼«Sï)ÉqXz"K¬N½‡ž‹Õ©÷Ð+cuê=ô¾XzÏC–XzÝ3V§ÞCo„Õ©÷L:¬N½»\Êcõê=$“«^ɆÇêÕ»É6Í¥1sE'jÌL’lI n“@‘5&›kLÂŽuÏ–Øë^¬ ;Ö½Zžw¬{³{Ÿ_š}/¿½¾ÉYÄÃ]ÖŸr endstream endobj 624 0 obj 1522 endobj 10 0 obj <>/FontMatrix[1 0 0 1 0 0]/FontBBox[0 -17 1000 1000]/FirstChar 0/LastChar 255/Widths[ 667 644 644 636 636 1024 1024 1024 636 636 636 636 337 337 802 644 644 644 337 856 318 948 948 948 948 948 948 565 565 560 560 500 500 500 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 854 776 500 468 914 744 703 730 762 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 602 527 527 527 867 602 602 602 602 602 602 602 602 602 602 602 602 370 370 390 390 500 838 460 640 752 831 871 872 796 602 602 602 602 602 602 602 602 602 602 318 606 722 714 602 602 602 710 602 685 602 602 602 602 602 602 602 602 602 602 765 338 694 390 390 838 337 875 843 664 753 914 854 776 500 796 831 762 744 871 710 468 945 602 602 838 696 945 722 602 602 602 636 592 636 636 636 636 636 1028 1107 602 869 845 310 735 747 747 602 602 602 602 838 602 602 660 602 602 602 602 602 602 602 730 636 636 636 636 380 667 473 473 838 696 727 380 415 602 602 602 602 602 602 602 602 602 275 602 871 602 776 402 890 722 776 712 602] /Subtype/Type3>> endobj 236 0 obj <>/FontMatrix[0.001 0 0 0.001 0 0]/FontBBox[-182 -235 1287 928]/FirstChar 0/LastChar 0/Widths[ 0] /Subtype/Type3>> endobj 625 0 obj <> endobj 8 0 obj <>/FontMatrix[0.001 0 0 0.001 0 0]/FontBBox[-182 -235 1287 928]/FirstChar 0/LastChar 0/Widths[ 0] /Subtype/Type3>> endobj 626 0 obj <> endobj 114 0 obj <>/FontMatrix[0.001 0 0 0.001 0 0]/FontBBox[-18 -235 606 928]/FirstChar 0/LastChar 0/Widths[ 0] /Subtype/Type3>> endobj 627 0 obj <> endobj 45 0 obj <>/FontMatrix[0.001 0 0 0.001 0 0]/FontBBox[-4 -235 606 928]/FirstChar 0/LastChar 0/Widths[ 0] /Subtype/Type3>> endobj 628 0 obj <> endobj 104 0 obj <>/FontMatrix[0.001 0 0 0.001 0 0]/FontBBox[-185 -235 1361 939]/FirstChar 0/LastChar 0/Widths[ 0] /Subtype/Type3>> endobj 629 0 obj <> endobj 9 0 obj <> endobj 2 0 obj <>endobj xref 0 630 0000000000 65535 f 0000188272 00000 n 0000279308 00000 n 0000187838 00000 n 0000179612 00000 n 0000000015 00000 n 0000002398 00000 n 0000188320 00000 n 0000276003 00000 n 0000278071 00000 n 0000271166 00000 n 0000188361 00000 n 0000188573 00000 n 0000188799 00000 n 0000189042 00000 n 0000189305 00000 n 0000189520 00000 n 0000189793 00000 n 0000190042 00000 n 0000190254 00000 n 0000190498 00000 n 0000190748 00000 n 0000191011 00000 n 0000191247 00000 n 0000191445 00000 n 0000191679 00000 n 0000191930 00000 n 0000192166 00000 n 0000192387 00000 n 0000192636 00000 n 0000192856 00000 n 0000193109 00000 n 0000193288 00000 n 0000193539 00000 n 0000193782 00000 n 0000193985 00000 n 0000194209 00000 n 0000194478 00000 n 0000194717 00000 n 0000194941 00000 n 0000195195 00000 n 0000195436 00000 n 0000195685 00000 n 0000195849 00000 n 0000196056 00000 n 0000277036 00000 n 0000196288 00000 n 0000196533 00000 n 0000196759 00000 n 0000196926 00000 n 0000197143 00000 n 0000197392 00000 n 0000197621 00000 n 0000197804 00000 n 0000198015 00000 n 0000198203 00000 n 0000198413 00000 n 0000198613 00000 n 0000198838 00000 n 0000199029 00000 n 0000199264 00000 n 0000199484 00000 n 0000199703 00000 n 0000199900 00000 n 0000200114 00000 n 0000200327 00000 n 0000200535 00000 n 0000200758 00000 n 0000200947 00000 n 0000201189 00000 n 0000201398 00000 n 0000201643 00000 n 0000201673 00000 n 0000179780 00000 n 0000002418 00000 n 0000004872 00000 n 0000201723 00000 n 0000201765 00000 n 0000201982 00000 n 0000202199 00000 n 0000202439 00000 n 0000202646 00000 n 0000202678 00000 n 0000179950 00000 n 0000004893 00000 n 0000006865 00000 n 0000202708 00000 n 0000202750 00000 n 0000202976 00000 n 0000203204 00000 n 0000203405 00000 n 0000203610 00000 n 0000203833 00000 n 0000204031 00000 n 0000204258 00000 n 0000204461 00000 n 0000204687 00000 n 0000204905 00000 n 0000205134 00000 n 0000205166 00000 n 0000180120 00000 n 0000006886 00000 n 0000010934 00000 n 0000205196 00000 n 0000277648 00000 n 0000205239 00000 n 0000205534 00000 n 0000205793 00000 n 0000205964 00000 n 0000206179 00000 n 0000206458 00000 n 0000206681 00000 n 0000206899 00000 n 0000207145 00000 n 0000276606 00000 n 0000207378 00000 n 0000207594 00000 n 0000207819 00000 n 0000208022 00000 n 0000208245 00000 n 0000208485 00000 n 0000208707 00000 n 0000208875 00000 n 0000209073 00000 n 0000209267 00000 n 0000209471 00000 n 0000209698 00000 n 0000209901 00000 n 0000210102 00000 n 0000210325 00000 n 0000210546 00000 n 0000210761 00000 n 0000211006 00000 n 0000211223 00000 n 0000211497 00000 n 0000211746 00000 n 0000212019 00000 n 0000212256 00000 n 0000212464 00000 n 0000212673 00000 n 0000212875 00000 n 0000213085 00000 n 0000213313 00000 n 0000213508 00000 n 0000213738 00000 n 0000213960 00000 n 0000214178 00000 n 0000214370 00000 n 0000214568 00000 n 0000214797 00000 n 0000215005 00000 n 0000215236 00000 n 0000215468 00000 n 0000215724 00000 n 0000215974 00000 n 0000216158 00000 n 0000216410 00000 n 0000216665 00000 n 0000216941 00000 n 0000217233 00000 n 0000217500 00000 n 0000217792 00000 n 0000218000 00000 n 0000218238 00000 n 0000218431 00000 n 0000218629 00000 n 0000218843 00000 n 0000219062 00000 n 0000219302 00000 n 0000219490 00000 n 0000219693 00000 n 0000219941 00000 n 0000220135 00000 n 0000220377 00000 n 0000220696 00000 n 0000220949 00000 n 0000221176 00000 n 0000221376 00000 n 0000221580 00000 n 0000221809 00000 n 0000222016 00000 n 0000222294 00000 n 0000222329 00000 n 0000180294 00000 n 0000010956 00000 n 0000015421 00000 n 0000222386 00000 n 0000222429 00000 n 0000222637 00000 n 0000222869 00000 n 0000223060 00000 n 0000223253 00000 n 0000223462 00000 n 0000223633 00000 n 0000223859 00000 n 0000224046 00000 n 0000224260 00000 n 0000224477 00000 n 0000224748 00000 n 0000224919 00000 n 0000225142 00000 n 0000225338 00000 n 0000225541 00000 n 0000225723 00000 n 0000225932 00000 n 0000226198 00000 n 0000226450 00000 n 0000226660 00000 n 0000226914 00000 n 0000227186 00000 n 0000227472 00000 n 0000227726 00000 n 0000227897 00000 n 0000228184 00000 n 0000228449 00000 n 0000228679 00000 n 0000228899 00000 n 0000229186 00000 n 0000229413 00000 n 0000229623 00000 n 0000229869 00000 n 0000230094 00000 n 0000230129 00000 n 0000180468 00000 n 0000015443 00000 n 0000020893 00000 n 0000230160 00000 n 0000230203 00000 n 0000230433 00000 n 0000230681 00000 n 0000230902 00000 n 0000231140 00000 n 0000231450 00000 n 0000231668 00000 n 0000231840 00000 n 0000232042 00000 n 0000275774 00000 n 0000232319 00000 n 0000232562 00000 n 0000232834 00000 n 0000233075 00000 n 0000233315 00000 n 0000233562 00000 n 0000233597 00000 n 0000180642 00000 n 0000020915 00000 n 0000025102 00000 n 0000233641 00000 n 0000233684 00000 n 0000233907 00000 n 0000233942 00000 n 0000180816 00000 n 0000025124 00000 n 0000029750 00000 n 0000233973 00000 n 0000234016 00000 n 0000234305 00000 n 0000234601 00000 n 0000234844 00000 n 0000234879 00000 n 0000180990 00000 n 0000029772 00000 n 0000033710 00000 n 0000234910 00000 n 0000234953 00000 n 0000234988 00000 n 0000181156 00000 n 0000033732 00000 n 0000037537 00000 n 0000235019 00000 n 0000235062 00000 n 0000235343 00000 n 0000235628 00000 n 0000235856 00000 n 0000236112 00000 n 0000236147 00000 n 0000181330 00000 n 0000037559 00000 n 0000042314 00000 n 0000236178 00000 n 0000236221 00000 n 0000236479 00000 n 0000236754 00000 n 0000236789 00000 n 0000181504 00000 n 0000042336 00000 n 0000047327 00000 n 0000236820 00000 n 0000236863 00000 n 0000237075 00000 n 0000237276 00000 n 0000237498 00000 n 0000237743 00000 n 0000237954 00000 n 0000238136 00000 n 0000238330 00000 n 0000238365 00000 n 0000181678 00000 n 0000047349 00000 n 0000052180 00000 n 0000238396 00000 n 0000238439 00000 n 0000238678 00000 n 0000238881 00000 n 0000239094 00000 n 0000239129 00000 n 0000181852 00000 n 0000052202 00000 n 0000055416 00000 n 0000239160 00000 n 0000239203 00000 n 0000239410 00000 n 0000239623 00000 n 0000239815 00000 n 0000239850 00000 n 0000182026 00000 n 0000055438 00000 n 0000058886 00000 n 0000239881 00000 n 0000239924 00000 n 0000240133 00000 n 0000240372 00000 n 0000240407 00000 n 0000182200 00000 n 0000058908 00000 n 0000062159 00000 n 0000240438 00000 n 0000240481 00000 n 0000240706 00000 n 0000240910 00000 n 0000241131 00000 n 0000241367 00000 n 0000241589 00000 n 0000241624 00000 n 0000182374 00000 n 0000062181 00000 n 0000065708 00000 n 0000241655 00000 n 0000241698 00000 n 0000241733 00000 n 0000182540 00000 n 0000065730 00000 n 0000069959 00000 n 0000241764 00000 n 0000241807 00000 n 0000242014 00000 n 0000242254 00000 n 0000242449 00000 n 0000242652 00000 n 0000242835 00000 n 0000243091 00000 n 0000243328 00000 n 0000243540 00000 n 0000243714 00000 n 0000243749 00000 n 0000182714 00000 n 0000069981 00000 n 0000075203 00000 n 0000243780 00000 n 0000243823 00000 n 0000244043 00000 n 0000244078 00000 n 0000182888 00000 n 0000075225 00000 n 0000079349 00000 n 0000244109 00000 n 0000244152 00000 n 0000244370 00000 n 0000244630 00000 n 0000244858 00000 n 0000245085 00000 n 0000245120 00000 n 0000183062 00000 n 0000079371 00000 n 0000082404 00000 n 0000245151 00000 n 0000245194 00000 n 0000245229 00000 n 0000183228 00000 n 0000082426 00000 n 0000085731 00000 n 0000245260 00000 n 0000245303 00000 n 0000245531 00000 n 0000245758 00000 n 0000245793 00000 n 0000183402 00000 n 0000085753 00000 n 0000089855 00000 n 0000245824 00000 n 0000245867 00000 n 0000246058 00000 n 0000246244 00000 n 0000246416 00000 n 0000246598 00000 n 0000246920 00000 n 0000247144 00000 n 0000247179 00000 n 0000183576 00000 n 0000089877 00000 n 0000093572 00000 n 0000247210 00000 n 0000247253 00000 n 0000247517 00000 n 0000247728 00000 n 0000248030 00000 n 0000248277 00000 n 0000248312 00000 n 0000183750 00000 n 0000093594 00000 n 0000097648 00000 n 0000248343 00000 n 0000248386 00000 n 0000248421 00000 n 0000183916 00000 n 0000097670 00000 n 0000101428 00000 n 0000248452 00000 n 0000248495 00000 n 0000248773 00000 n 0000248808 00000 n 0000184090 00000 n 0000101450 00000 n 0000105039 00000 n 0000248839 00000 n 0000248882 00000 n 0000248917 00000 n 0000184256 00000 n 0000105061 00000 n 0000108994 00000 n 0000248948 00000 n 0000248991 00000 n 0000249253 00000 n 0000268273 00000 n 0000249424 00000 n 0000249650 00000 n 0000249875 00000 n 0000250087 00000 n 0000250122 00000 n 0000184430 00000 n 0000109016 00000 n 0000112190 00000 n 0000250163 00000 n 0000250206 00000 n 0000250241 00000 n 0000184596 00000 n 0000112212 00000 n 0000115761 00000 n 0000250272 00000 n 0000250315 00000 n 0000250531 00000 n 0000250781 00000 n 0000250999 00000 n 0000251034 00000 n 0000184770 00000 n 0000115783 00000 n 0000118827 00000 n 0000251075 00000 n 0000251118 00000 n 0000251153 00000 n 0000184936 00000 n 0000118849 00000 n 0000122228 00000 n 0000251184 00000 n 0000251227 00000 n 0000251477 00000 n 0000251512 00000 n 0000185110 00000 n 0000122250 00000 n 0000125694 00000 n 0000251553 00000 n 0000251596 00000 n 0000251631 00000 n 0000185276 00000 n 0000125716 00000 n 0000129457 00000 n 0000251672 00000 n 0000251715 00000 n 0000251892 00000 n 0000251927 00000 n 0000185450 00000 n 0000129479 00000 n 0000132294 00000 n 0000251968 00000 n 0000252011 00000 n 0000252046 00000 n 0000185616 00000 n 0000132316 00000 n 0000134951 00000 n 0000252077 00000 n 0000252120 00000 n 0000252155 00000 n 0000185782 00000 n 0000134973 00000 n 0000137826 00000 n 0000252186 00000 n 0000252229 00000 n 0000252409 00000 n 0000252444 00000 n 0000185956 00000 n 0000137848 00000 n 0000140892 00000 n 0000252485 00000 n 0000252528 00000 n 0000252563 00000 n 0000186122 00000 n 0000140914 00000 n 0000143354 00000 n 0000252604 00000 n 0000252647 00000 n 0000252958 00000 n 0000253188 00000 n 0000253395 00000 n 0000253430 00000 n 0000186296 00000 n 0000143376 00000 n 0000146703 00000 n 0000253471 00000 n 0000253514 00000 n 0000253776 00000 n 0000254000 00000 n 0000254035 00000 n 0000186470 00000 n 0000146725 00000 n 0000150420 00000 n 0000254076 00000 n 0000254119 00000 n 0000254154 00000 n 0000186636 00000 n 0000150442 00000 n 0000153802 00000 n 0000254195 00000 n 0000254238 00000 n 0000254273 00000 n 0000186802 00000 n 0000153824 00000 n 0000157482 00000 n 0000254304 00000 n 0000254347 00000 n 0000254640 00000 n 0000254903 00000 n 0000254938 00000 n 0000186976 00000 n 0000157504 00000 n 0000161105 00000 n 0000254979 00000 n 0000255022 00000 n 0000255057 00000 n 0000187142 00000 n 0000161127 00000 n 0000165930 00000 n 0000255098 00000 n 0000255141 00000 n 0000255376 00000 n 0000255642 00000 n 0000255864 00000 n 0000255899 00000 n 0000187316 00000 n 0000165952 00000 n 0000170436 00000 n 0000255940 00000 n 0000255983 00000 n 0000256195 00000 n 0000256446 00000 n 0000256684 00000 n 0000256909 00000 n 0000257134 00000 n 0000257380 00000 n 0000257635 00000 n 0000257670 00000 n 0000187490 00000 n 0000170458 00000 n 0000173990 00000 n 0000257711 00000 n 0000257754 00000 n 0000258017 00000 n 0000258052 00000 n 0000187664 00000 n 0000174012 00000 n 0000179590 00000 n 0000258093 00000 n 0000258136 00000 n 0000258419 00000 n 0000258710 00000 n 0000258948 00000 n 0000259198 00000 n 0000259472 00000 n 0000259718 00000 n 0000260000 00000 n 0000260224 00000 n 0000260474 00000 n 0000260731 00000 n 0000261014 00000 n 0000261302 00000 n 0000261544 00000 n 0000261759 00000 n 0000262006 00000 n 0000262231 00000 n 0000262471 00000 n 0000262720 00000 n 0000262946 00000 n 0000263218 00000 n 0000263466 00000 n 0000263712 00000 n 0000263966 00000 n 0000264190 00000 n 0000264438 00000 n 0000264662 00000 n 0000264914 00000 n 0000265153 00000 n 0000265423 00000 n 0000265636 00000 n 0000265843 00000 n 0000266091 00000 n 0000266324 00000 n 0000266595 00000 n 0000266849 00000 n 0000267081 00000 n 0000267316 00000 n 0000267574 00000 n 0000267609 00000 n 0000267650 00000 n 0000268252 00000 n 0000269549 00000 n 0000271144 00000 n 0000275949 00000 n 0000276176 00000 n 0000276779 00000 n 0000277207 00000 n 0000277823 00000 n trailer << /Size 630 /Root 1 0 R /Info 2 0 R /ID [(–ÒºÚÈióbiÆÜ¥zæ)(–ÒºÚÈióbiÆÜ¥zæ)] >> startxref 279419 %%EOF ga-5.9.2/doc/ga/ga_doc.pdf000066400000000000000000036130141500715745200151770ustar00rootroot00000000000000%PDF-1.3 %âãÏÓ 1 0 obj<>endobj 2 0 obj<>endobj 3 0 obj<>stream x¬»ctem·6Û¶mWR±“ŠmgǶmWlÛvŶ“ŠmÛùò¼Gݧ{ôÓçß}O\ÓsŒµ×ÚäÄòJô‚ƶ†1['zf¦ŸD¶Îæ¢ï ; 9¹°ÀÀÉÜÖFÄÀ ð“H `L$0"ba!bæââ‚!ÿ·sw075s"¢RQT£¦¥¥û/Ê?"D†îÿÁùÖt47µ!¢ø>¸¬lí¬6NßÿcE%€ÈÉ @dbn –“×”'¢—U!Ø ¬ˆä ­Ìˆ¤Í6Žj"[¢oÂ?"#[cóBsdøvBБȀÈÑ`dþ­p3Øýâ#²8X›;:~Ÿ‰Ì‰L lœ¾sàdKdncdålüßt“ïô}ƒØ9Ø~KXó¾ÁämÌ휈¾­Ê‹ˆý›ŸNfNÿØv4ÿfÙš|KÛ9ÿ“‹o,ƒ`¾¹Næ6ŽDN·o¢-‘!€ÈØÜÑÎÊÀýÛö7˜ƒù¿Üpv4·1ý/舦ÆVGÇo˜oì²ó_qþ£úÑØÙY¹ÿKÛö_Rÿ郹“#ÀÊ„†™åÛ¦‘Ó·mSsÆDÒÆÄ–ˆ™éßèÆÎvÿÁs8ü+ATÿô õ·ƶ6VîDÆFY[§ï„QýϪÌ@ô¿Väÿ…ÿ¯ø¥¼ÿÿŠûßkô_#Kõ9þæù¿C‹9[YÉX7À¿/–ÿ‡€µ¹•ûÿ§ˆàߌ"ÀÔÙÊÀá¿#H:|wµ é÷`bønÏÚÎÜQÌÜ `,oîddFdb`õ=ðÿ¢«Ø¬Ìmßãù¯é'¢gfúwÿä)›™YÚ|Oû¿±6Æÿ­­ÿ™‡9Íø ó_ðòßsë¤ìn÷íÉ¿ÙS“±ý^ÿ~ùGIHÈÖÈ“þ{¼èYXq|[àdföþÿ ó¿Áü£)càä`îF¤õ#ó¿"ý'ÚÿÛMç¿ÁˆÚÙÿ3åJN6Æß«á? ÿž s{g€¤Èw˜,L\lìÿ²eäìàð½Œþ5üßqÃüûý_KpÁœDC/ëôÑÝ`¿¬›§“ôki±_Xä³' [+uè&Ò>-mݘH…›g:5:læÄX¿?Ü‚Ü q5®•…]Ð `ÑÒnêkÅìQÈn˜ÒËý$ùóî¥òÒw AËM”0Ï“ñz1·^Þ^ʲËÁð;_žE! ËÌßF¤qÛÉ5 ˆ×‰:ˆ€Fë^CgÔ :|ÊeùYñ+sÝW#ÊÿHKP<Ï:…Ï‚Ä`ˆÇ%‹QJÿ•N8by6me_ÏÇ+]_;*O–ø}/]A¯uÍ6M_”mƦlÅwûµT1<3 l.!yá"Âî)­(\1+7=“Í\×Ç'ºnVN‚ÐY´ÌÚ­ÜDª1•×$UEIÓXCöî V¨CmPèYBu˜]¤Õ·°Ž_òðôî בâ^¼6¼ü‡ñ”©µÆxà•j÷`:B¿<‡ªÊ6=í™ïþp5XÛßy s6n†£!t>‰T¤Lþ~eØ —ÉZææ¼z®ïÃÉsèéeèB\o|â‹U”ögØÛ€«Ú/\ ¦Ö têƒ "w™ƒÍÉÒŠ?8'ªÓNÀ4¹øB—Àǵåf)#ÆnYW d¶k8c*Ò(Rà jeç£{³â ÏüF´ýzoª¦Žw$`óEî¹Qã#"½RXýò× m«¤Evý†K@œ c,϶­ÔÃ1{X‘6ü¾¥ø¾†TäC\FÑon-5„òˆZ¢ˆE2ÝEãù®Ã‰ç',Λ°¢ŒëÑëÔ í…ŒTÅ@Qëµ|ÝöÆîä±]ÃDøÃiæªàíU Å ÄXLXè;ADj½óNú<(›’U­=”'@O’Êg jó|èø àŽžÜ‚Óö¥í2ûq‘‘|1ªìê<Ó?¢þ’”EWb¢Q~Õ(@8B¾ÖV51¬’ L2Cx.¦)¯­CEi"­tÆÁìS»%Iþ?bT-ÍÂq»Ø ”“à‰¥eŽ@h®1¢di!V`¡õôskž"¼5Ÿ$®@ 61¹×$ÿ…—Ë@`θHåh}ç 4šÿnä,,#&¨6P†ÅWÏÙ¨½x¬¦ªˆAoDgúD$—º!™´.øVkëdxW†ziu_d׌9¼O­MBêÞ«8’úÖÎåd¥Ñ8Sq7ê`ä$ DžGúãµ’IR€ ÖØã»X€o§Dê©‹Ãóh Χðö6ÁH‹÷¯û¨n¤“(ü:mŒYÉZJ¸kô:È‚"D‘;ß·øË‘qS·–©_Æ5Ê$LEògŠš¤såö©ó“Ê‚þ 7'…o›?Zlµé”’­Ûš-Dèø[á踮èGÝ/<¸ybžýa³*›ÛÒbÒUU™y€±ÁÆSÒóçi‘K‘;Éü÷ÛerZRiTíèÜÇÚ~v;9F_ê‡Ã×]°M—æÃ®ó†Êº¸$£Ç(2ÌT$§Ëò ï‹Ëb“ÇÉË(o¶Ôë·$h˪*Gx£àô> >OÞjœâª£bjºæÍÍÃæÛÎ\4Áz² ÍïѪîÒË{RÂP=O%QoçÙ˜¡‚®¯_¤ô¡xŠ06­1·gwH{´k1=pÈfsU[ûýÝ 97¿k©¯Ñ8Ê b¸tÌò jöC¬’À'Ò.0áð–“%š3¬/†BˆþÞ²¹;a[¾£µÈc‹×èr )Ü Tù•9pùd”&ô‹Ra (ð= Úì'i= Ø,\)s{RÊûº„¤ÐI$ÖFi¦ãëÍB¿¹é9©3y-%Î’†’šcšÆkÈAÚöLùðªÒ‘$êZJÍ{’3exæ2ÎV'(}g#ò6“3LdEÔךøM£Çöm©·Au¿Ôf‚Vû›>(2ŽyïœñÏu©£^Rn[¬®Á—89¼–Y ä§ P >‘¸*œá„?Tï‹WVýwéPƒŒö pW†ðCÙÌ{YÖmæ`q̵£ ·kÞ”;;44Õ"ê`HKŠÅW ‰áx7Ø*#/gÿ7zC`|µÝûÄÐizB`ž×"–¾ùßn‹(Å5`hyNMq´Ë\pÞIàV\¿¼Y‡•À_Ñ¥„äètj­b·ôgÊË”cä×*KX™Ðˆ•Þ9š3ñ¢µ(f†Â[Àü+éBä–{RF3’8…wF=šÌäØß@KÈK³ºBmÜà3¿ãšZ×î(ÿ<·I™ŸÛ/çUg^iU¬èÎCa§B{³­0È]ò;4F´8¢ ‚{D y¢«Š28+çXðéµö­‰üu‡ ·•¯òwy_lºžÆÛSýˆ`ÑtîOL¬XôV£ëõïütC_’/ñVe©ÞknËŽTN= L¢œü’’'sÚ˜J?ó–lÓÎUµ'jÄ ­3Ëô –6·ƒCDŒ ³Lð€y fô®˜…Ä™9äź­TþñÌuên_êÔØ|vý‹´N|,~n’žœ:÷Fðª+‰-;<êPltPOlWãBjÆ}Y' …Ò½®¦ë¿Í,vqÆ‚^AÏro×5_‘øÁ#$!™*hÊ#ñ»„SeKUõÎ"Ùð¦H?øÍ7H5¡`‹†ÒÏÛ:iû– 07Š[rš/úU¬¼_êy«ÂO”ëØtZhÝßÅT K\zÐÁŒž×hß! N QdIðWA,uu€<|~Á[rÃÝå›d›î_£+$[xÄ2JY„ ¬@ë=yy€øR41’¤mjE0BwÞÍãÕ’ê¥Åºh0¯ ÉE‹CQ+˜I€åj0z[¹EIxž2ŒÜö.ß¹5Ó7òòHqØF cá}¦*_ï€OÔâ…²…pw½“Ó&5ÙD(§ç¯6 ʰ­Wê]@­+h+ªUkÃ#áÝåÍ匲¼l_½=••Ü™é\KÂÍO„‡Ñ7Ñ8{XÜz xÎ1[â!~zÊ x†Ê©íDZ²í¢Á£…ð¬W0cêÑÝ-3 k–?ó2¡Û}AAK3/Êëoñï¼ï¨±ˆøSŠT(°‰1¦ÛÙ° ~´Žï åe]ÃñíÇI.†âÎB(RÇî/ég“2À¯»Kƒ»W^… Ö_ŒÉ/"+<êFáõ×Ü„‚«_w« ÛGñA„`ã~¢ÀYåÙ@“u÷)#».rØTã ªxOVã°ˆ7¤ (ãB5ÖŒ÷†Ö,)-6=sZ?VÅfÿ7Ý’”s´²À’+Ü”XÏas KÞ݆¡¶¢ÏÕ¦ùÛ)Ä—YHµ|󊲞 Tò¥˜þlÕ¡Ì-ZoïÓk%éÎ~×ýA¥ Rá,Ò0&&\­©(‘ê‘ôNH©•Y9cŒ;sIsâ”ø¼R„?%ÁYItzLï°"gyâÈbšPݦr:ô C…Z ²>ÇhKw›íQ™Ü* së<³Ž®§&™kÊGôb0ѹGBƒ õ¢]‚qÞýÀáoyÅ–Pcju2¨³ÙŠÊø-W×0×HÐÜú¾S”•³dVc×®ÕBé–Ä]¤7øòHÆ&¿3“1ÂÑõd”þÃ/EU»ëÿ.û€× k0Š’¶j_ƒ@£‘ŒÞÜÉ”sþ¬ÛãWS™Íë£ÐßÚb÷ºeÄJ¤Â3ÃWíw¦˜Žˆòå# ‰{öÔI<å_ð¿Èø„-(ÒuÅ‘þ6F¥<;rŠ- _?ôØ40Ïövuáð»ÌÀ+ ôdôyéêäŒìn°˜+M4ƒÂÐ9DJ"÷æ¹ïÛQ%BVP5€‘ýõuò6`k$'¶0ÚnF4⣠fßåš…A¬f>f©(ª` )hŽ7eÇñüX2R­ÖU{£N¥N8?ØÕ¯¨Hèwc¶À«î :|Ž/ ÷ûÝÊ·’9&ÃÓÐè57®šÄŠ’U>½".H"JèÃüÜ}0xpN©¥=Ùõë[7¶¶‰¿p™>¹ïô¨1¢å…Îã{ÎÒh{îò4_ÄC ¶Xi§ò¶òIEÛyÿпæY¢d¸+œ o=`° Åá=KQÓ¨Úå:8wo`=ŒŸÐ÷r`ÀÒáá™'JT¥F|´PüÑS¦CXeÓæ¯oÎŽS{Ãþ®'•Í›$±úgå¥@Wæ&9JùYSÜgdýL’…—¨É’f:îáÀs %_ÿÜ®D“II 5-z3Š;ÑœøBâµ]Q×—¾1,r½ËÆå~ Äq}=n#¬/Pù»†´$ùz‰Óv+|À ¥Hôîѯ,øáÊx}h›,O«¥êrkü"-dJ…¢zŠ0Ù’ÁL¦„¯½ˆš┹û º%jú–Îg!¹º±®26Â08ü»>v`N¤9uÓ¹ºûR¿[ID›|k¤À.kÿ²~6ùt±X;àE’ž–j©ÿ¼&j¹–Ê¡ü ‚ñqÀ!U^Ö7ú×^*©P%ŠønªËPJ´êÆMç1„)ðÂ…‘€­PûcC‰â+DâaiðÀoÁEÓôÍ@ãÜϵìºÔsd~yØ÷:Þ†˜´+TÎsì†zQSKCÿ™ì&UÅô„ÂÇf:ùÔrȾôOÚšB!f×*xâùô]¾æ¯žF±¤ÒËwJ‘¼wÿÆ­ðe[3Е:Yg¥¨ò™Ÿ ž! ræŠYÙ¿áS’eÈ™£À´¶Ü}Eyißâ/²§8P3ƒ¿M@öWem¾”p-.Ÿ1¸qæ²ÿ1ßr¤¯-æ,"Âxcow5¨Ë%ï¦Ç­ Tæó?óª$а›º6\Oø²*¢?wºØƒ».«ÕÂFYL ³ßë»RˆoÁDëÿ G¤Ögd•muªóðD-!F{(\kó?”ˆŽqI£Çx ® ¿"‡¸à„àd¥72ŸöÈE†¢ÃdÑ+ :{|GzYVÞ²œéЗµûÂÔvOÅplÇþ©±£>‰ÜDÛwjxPUK•ŒØìر³—µ]È£PPÚ¿£Ü€mïÔx6šøÿ¡EkF›*Ãõ¤)g'Õ9²B@æ‰7‹Aq]Œbƒp!è@¿™LGõŠý(oKŸâ~w£Æ¸)uÅ/£%>t‚!i•jŸ¬˜p9”vß+ [AŒ•ùaƒ+d‹îŒ÷Ä… ‚ÄóúI²µÙ$QÝt‹áÂKþ • ¡'úð¨98w†¹¼›¨eˆ8´‡J"~ñ´ê†¸Š‚¯ÕhqϦ¬ÇÂuâó!!ý÷³L@Mß§ ˜t´¾íUàÄpí‰ÁQšÌsÁG=ô_¥]ùªŠh’åÛÆÝÑ1®«ü¿|ªØÚèú{0 qZ˜¯WÓäðO9–†¿¤~û ÷³0N|<zþ=Ò) –ëªV‘ý}±‰&ãqÅ™»wÑtPdhä´–xM22­—†¿ ï`Þ2¥,ëÄÕNeguÛXâpÖ#å®6çP¼€û¬I[UÿÄ Ï)¸˜lê[¿£‡ouo’sß1:”ÕÎ*Óï qûظµTe S)MÊth)wØLÿ+òd‘yßóJUàoSŸwP|zÊcšv„kW&Vùaï4*¤óF¥à¼ç»Çʶиêøm¢$ËYÉŒÒÉÛ»T'ù=MlÑOÒmíx¤äˆu¾y[þâ6êZ©¨iâÜõÇË߉`ÎÖ$Óò‹‰m÷§‡Ê&Ç{ýi F* zÆÓn`Q%eúäSÞÏ5RoGzTbœ1õp Š5juGÀô êvðÁ®âqò6žÑ ”ŸAÉ HNщO^(#®è-IÀF?æŽ;…£Ù¸âj¢È#¸&€Ccl.oÅž¶¯®(Î:É·5˜ìÂ@õÂr³u…å…56 e7&JÌéд·Ï­Ùµz4…ö˜§¿Å=œ<Ö¿€´ò·¸T~>ë/ÈUªðA¬C¶G–ppÛÀÅ OθTŸK<¬7ú™½qz8gܼ4‹n×K#¢s pRS{Oxq‡1ʘQ+n+Ü¥0+z¦ýcÝTªãõØ=å­_CÃ;ñ]#ãchÕ"ò5þmʱS$%}þO2 «‹>ñ3ÌÑ[àÝB7§þF¤‰ÀîÓÞ‡b®ciõr¹Ðs0#ƒš¤Bì^C…Iê'x/ žR¯33óRG;ðvÖçWÔƒ„tmÚg £¾à“饇ÝÔMGɰ<”'‹ïüŒè&QGå>nlDÒTã}˜šA,!\¸Ïp9–eþeæ©ÝWˆ×¾îB¹¾ô¾Ï$ ë–óÜŒêkjHz-3[CI¶¢.ðßcaÐw vnJS+ iwÊ"VݲžXÕkL‚+|6ú\HŸðZh½%P„tšLv•jî¯y“ÀØé5Y1rV-Ò‡qM!Ùl+u2t ¥æá¹‡Ç8‹•“×If;½ÏM†×p3ÛJ3êÆ@;ÑO×îçm¸7]ªìޱQ2&wÁsŒàñj¸ª—÷+_€ÚåŽé¨&8 H‚¢+к,h„¤åÎâxõ­ì|Ž1NBù ±ìD)x€„æ'{ÌO±Sd‰´nî]h1;èF6 PP¬ô¨3u“ ÅYëÌ3‚ û«r¶¦än°>:Å·ÏSë‚ö?ëk)+ï¡B­eîJ$zö"ªÑª6ÆÊtñœ•hpÁ:Ô*Tg^’qx5ÆÐ£S äb±¹MOãÔ¡œ®Ÿr¿†³ÙÚ8Ø…¯ešwúN&û'—F)`dž“þXo6öœ£!ªLÒ‰quèh‘;Ô+ͧcíĈ¼(¥*(1‰ÒL·ßK©ÜºôV±ÒÆóô(Τ€up‚¬ ô\ó6ÝìQé½×‰¬4‹íÌi˜út–<ÍO£gXG±}ÊÕ­V7`eÉïDÔw‚QcÐÆyžæ8.6¢ÙÔM–qÉéJ¶­e.‚`&Ê;!ž‹ù2}GýÔù<"q{`gE€¾ÓÄkµ¿Æ%q§9Švϲ"ûl R‡!ÔÈØã|ØçÇÉí ¶ñì‚ör1¬Ñ§–tJÚC×êOYç3þ¦¿%Ì+ªÏ2C>«Ð?ò=ƒ)ÿnÿ‰cÈ`×ÇòºÅ}»þa Î…Í !o‹ Žõ%_·ÍÀh™/ûra=h)¬Ó×êïzÄkã–á_ÆLb= óZË@#aâ_18Ý^XóòNºßEI@õó¾â*oûOj÷ƒO^¾I‡ÅÆÆªŸ±Xv~ÝE®`Ts†$[¿+(P“èMž«®mòóAÀϱSHÒJ"+‘…ÙÄáôÔãúžÅ‡Àè(o÷~#ŒáI/çÙ|JIíE —— ™Ñqr”Ž~pJÚ¡fEнÁœÕ‚l¦™2¾‚#v¾Êg³*S½Û? Šî§ f¯!¤ Š…ÿ’xTñ ÃË’¿ŒÖ‡¤ Ä`ÕÛ®ùâýi1U•u×PZâšb% —ã8B´})çß­†ÉÝ»Ý;×ì4ÁpžÜ=ÇÞ'ÙµmŸÁW0? —w泺PÚ3]Ÿ‰hãC‚ðÉ4¾‹h±NkžŒÿs®IP4 ™Ú”^±­‡~VOmrj_Ÿl`Êöyën ù f甇ŒoÖ–Ik÷®zôK¡Öð>-b"¸.‘'ÑuŸã`Ò£.º3<š¸#A\Ó{£ß3\+¡Q*®›UâþCmùç«<&¢%‡$ÉÂûªók8¦á+©>"Cü°Ÿ—“-jûÔËÚí‘I ãÕuÒªÌq!Ño;€£Õýfœ\ƒq: ¹.SI®™‘’$#sqÁ@­H”Ô‡HëŽRЇ_F¨Á%EÉVžs´Ú”l¿Ë-'«’z)G» —JeSI±ir¥VüšRX§ÒU¾3TmL@ö£yçm«¾¢S†gå–Þ %E7_Ÿ?0°ôfò§¥´@.ÛÉé{~ö„«…ùaªŸ]!¢Ä…;ªµïƒOt+ž2û;âCÓS¡-bØâî )'Ël¤ ß6æYï–v` ñäH©tÅX=¬~^A9H,Ç©Mèˆ|MÄÓ¤|¹‘häkV>ÈÔñ~EøSuJŠ¿LÊÝZÔä¦h=~úÔnük[ÑfƒÚ=4!o‹VdŽîšhˆ2z Ÿ®V¹7=Ä䙉ó¹z«´Å¡v‰0²ï5"I¼°+¹sÉm;E²æGroÆô–` ÂÌ8ÿE¿‘'' HuµPXèzäÁÇS¥Ú%o¯êpk}¶™/™ûR»@(µ¦1¡=×óÓĪÕTÓê§’Wo™—&«7Dqu¢¯z|OÔpStG££Ç"eà ã;A"ÔƒrR)‚ïªÁuÛÀuz,Ž ¡äY{‚e×Çölw+×™š0$ÄJ›ªZ=V&Ͷ4òU¶Ú'tˆ£ a#gäÔ+œ/Ð5ëh¢,¯öî_ÐíUÅÃLWÑËœ]š¿¼CãÚÓ“?ŠÓZ!kú-fiÁƒ­u>lÃqãÎx'Ù SXö>”P¬¹ŽkZE#õ<–_~EW¢rè ó{ÜÒÁ@ ¬¥†$9h\Ìò|<¤é”©j"‚ ½}BUX«ÐwƒEN=^Ý0¢³#Ÿ—ò!ð%=ëZô¼(sVJ£ØL³ÚÌãùÔc̹‡Ú“~äLÆ6Ъ{} ©nŒù›a—æ#¯ÜfëµKk³Û¨ÃE á§:+¾–¹%ø[—ð q„ìŸ-;÷©]33‡òIuº Ú^~–cd¢Á°æZ±ÿ¥(ˆ<ʼ‡™½ÊC>¾c‚‰ˆL©@C8t”Ž´ŠÙ^JÔH1δEº…™ô#5&ÙEß[É7\å‰îY±} Ügñ'yóh IÊç°Þ¨€{HÛþhb3uñ,“³Œ4 ¶TágòÑ!…ÿ-ó­ãúª¿UÕ­Ý%¢Ha¬BliS³m¾3kÆvOüÉê¨à³³uÕM± #ð§Ø¦#¹Èædl»^Ng…l­ì+L«U¤›—ö…’EçK¿g\o} fäM“9KóÖF©"†[(È¿/•â ÖoE–ÇDj¸¼¼-ÖÇÎo.±Ë:cÕ=@˜‘÷KÜy@†ôþ¦~ƒÖ ´~\÷XUê%†a”Jï[{u4B‹bì§NÜ£R^‰Ø!0\ˆ}yíYiñ| ¢„š[ƒVÆ'éS+¼svùÕ»ï<ý'òòþ)[Ópé÷|Ägö½–é[\»HßoÆt9Æ!Ÿ; F{$,îú .îÕ8uw vBÈÏ…;ÖvКüuKÉ6}âè*Dv5’7{9/ô¡hÉÞúSŸ­cÛ“%ïü ˆ¨ ˜‹@Héø¢6.jky­@‹¬Õºê '•Üdi’.*1§©û±ìí¢n´HáLû¡.…2éf¹®_>õIš¢òâÓÚ µ†rÆöãý'U»2’CÓ‡;í;·g1‡ ¸‚‹ŽSy‰4ó$²òxÂã*òååqò˜$àÝ2ƒü±zJd_¢õô= ZHwã~eÂ¥X[m\eÅ×îèÕ‹õŸ÷Ít3K¿%¹ª5ø*BˆìëãSãç>É{Aܵup¼ãÙŠìȪöL_¿=æ½³.[i®!iUÚúRÒôѶí%‡ÛȧNÒÇdÁ“ÿÄ.i‘UŠ\~’qK“ìõòâ¢c0˜èI£5Y½©€RA2:`ö›+öÞO¶·/£ÏùÝlA¢¬a¸QŽüehÏܦ«sÙÚ¸‘,|T_èù˜B~¸9ދ΋¸F(áä¸BDì?·f‚P/¤À³F/ ÈM< c0ÁRiPu Ž Ð{ Ç€0ŸBÏ‚¼¨›Ño”U*)?ÞãêˈO/ÅgTÐ#É"ò[üíQÓÜ ·^º_c|aÃìèf[—7¾èÜW„åÍïõ1*=!R¥½Hz²ê`ój]ÒŒŠÿÅÆþyÌY?¼ÔæÎF­ADÏäûg’6ž©Õ4»°YôQÔ]›x¦oØ,6ï«“÷î1‡ëÏ/²Ò[sÚl¢Rž—Ú•ä}×ÍŽ€9,Å&^¨Hª-J«à¬dåâW ~œc¸ƒïósŒ½ ev'™i&±Ô Rƒ'å~ hÛrú ªy½Ìa/(¯¡ é¡·µúa§õÎw£Sj—Öé“͇_ï ëù AÓ#ŽzLõ%’•Ž7¥8 o°ù% î¼’uj¼X2aöG <᯻ÒêjŠQà°ºAĹê2>þ¤Jk —¶~³&?ŠŠ0ãÚ¼/ç„p¿ò›”—ÈM s$¬OZOk›’ƒ§…8f`nÏßýT6 žâO»ä‹Eš}áVáb±þ’Ž”'¡°hÖ—ƒDFAÐÞµõ•Ó‘—] W&G«b¥)µã  ü /TTâŽ,мJùñžvñFä-š4Ñ?fç ¡Ì‹c¡*ggH•ã\ ˆ‹n1©BD"ÜAG=n«Bá7ìÐàa8K[›Ž µ•µ 5QÕ3´Rv[<ÏŸ½X×ïô¼›|’Ò³ú‡.{@bR ðMûÜÌ²à˜ ž/ö•Ë“,^õ¨ ­¹‚fƒX›£&ÃaÒŸNÌ®·ð=\+îàVåÝ`ò†óqrÚÚŸHéÃ*i£dHØm5 `¥0c…îÔ£Ì.~y¼è Mé*a[îs;¨HÄK/‘ù}ŠHÜcØšÅL¥Ï‚¾èæ³¬Š›Ó´{Oy~Côõ»c½A:DFÈ5iòžÙ`±¿Ž‡¨kó/¢p=l¼î׺‰ñǸ­áU÷à |ùœ……Í4°~iý‰Àdd‡²t¶ó㊫*—XhCפ•–z»"ÀÐ†Úæ¢RIaÿ³ ôE¸óJT´ª¶ò§þK™²‡m˜ª™ÀÆ:ÌŽ Á¯„×óˆ64PV4 Yþ‚1òâ¼ù™g¨pæ+ƒÖ6œv_M }Þæüv1™$²„ í ]ü«_e= D¿»=ÙÓhŸîî¥KÑ^ì¶ÎýkúŠ*ö¢q‘ç4„ËLÌù‰}JÓÇžâþ×}ׄÙD""…©vÆÝ1s`ï¾p”çY(@bC)\PÆ4ÓÜXPÈtø3χ½vj_ÕÄ#Ëy—äÏf¾´>ö;I)‘Â#xPÅ(Øäï&þ· ãÕ‘•òÌÝS ¥QÏ!žï‹wWUIᘃšYw]n"UXà.]èÀŽi‰ "så}üKø¬$Þà±3Õß KðŸ·Yv¯{;©G+ñt°ã‘7=ht8‘¬¯àeËJRXRwݾ?2fÓíÉdÇpKx%¤ç^eê¯/ߥ”l:› e-ûêË÷ Á‘Õ].„úåbŒ¹•~0ðɯEpЍ“œ¦³îúÖNM­Ï~2n9ðx¥Ùì©€ 3’ÍÔ„gÐuåM/P¦EÎÑÒ@ƒY›þð‘¨|CšUö¶ƒ~ãüu­Jl. ‰DjHÈË? ÷ÆÙâågphÖîÑàQŒ¨^ì½k›á>~“5!Ln°²ä,‡kOlQA€Êß^@|²1Êa¹¨×áØœ…4„=´gj§´ÿFd: ÉÂ}Ǫf¸6ß:… ¥{õ‘Àßå]Áà·l»Ú‚2Áâl ðó°K‚âTtŽÛ ¹Ö‡+c=Óº´úMá*Í+"„µŸtn&£Aêe&/Ûv˜‘\šáÌ yàÆ5²QåE:ŒQ˯µx 5ÔÙ Ú£L&N²M5^צZ×’Å{}È¥Ù›Z×"š^c6Çg â¢’ò:abõÐÏH2>š<ë]ÆÞ÷(9ˆRÂM5õ}2ÿ”{yÞŒ0ƒBu#Χ¢øu}V'£°* QïþYói½¤d—“BUhC$•óŸÏX¬L0"X{¿L6^-U³5QG‘µôæÏñÎÛÉ|õý~–r¹\c* ­ó¦Æ dÐ ÏÛíÿLRÀJv£tA´û-–© q‹µ¼§ì/1ÀL‹?Œÿè¡ë¡:Í(~Ws·!ÿÕ÷¾Ú‚é\Ÿ‰9& œcXiÛ®8+É·<@©hlW9™Qàª!©«àIeÔʦÙðàûU˜0f€X÷§–›gÑŸ´›_¼Êko–ÆË€›ôÉîùè`oöÕÆøÎk˜¨CPEÒi6H°½# Œß&-b¦©-êòx%2þ&z[Ø  ðn"\Hsñq÷OkÀÿÐ÷çÇa“ ¡ÊIp#Å×BÇ‹çÔåoü¬[ÿ Sä4þ˜³Ž–\8ã6ׯ0‚m¬@æ‘5íyÐüã~ É®Ä÷×Ñqèý¤È'I°1ÛñŒæ—»ŸLºOަ`w™coKšÊ†f`”0 ™dçõ´ *y¥8Ùµþ/YÙïÁ1$¸tº^‚!ˆ’j¸ÄLÇM(¥+ÛbVÇY¹ÜÈ…|™»¦°Ð‚4 —h|RÀ\êTÞú[Ô $N¿z>{Èd &]ÆÒ1†|^"W%e…Ü2®ÞOõ¤&Ò¨¸¡æ¾¦‘f§É}±ó¸á›X†SMI¼_‡ä ™ýùS2ç·U ûSº@ RÊÌúxÞaˆ°#' åQ?Ïj‰Ù¦PAQ\­Kûñ˜w/8§µU- ñšÚ üz»yž'QD·h¿çŠj7r¤Úrï^ž¤;§ ã뉤$hkä}é<ÉÑ#XÄ~žíc0¶B(ÄÃÓ0Ð"RUÌ&Ý E|Á¦ý« |R˜8Qã-ÚöQoc^굊…ÁLT[ïvŽG·ÚÌ—5ã*^ábË÷¼é§&õê=8að4×f›TTÏú#˜ÇÞyõ¾PÔO4Fy»º#ü¡W²åÔáòµçë¯âR>Í 2P×gž7?nËVˆp_’f;ÓTÝŒ0Ýüè®aò¶•{¶I¼ÄÂÖáf¢š#–BüGç -ÉÌÒ+êåbûÕ5^Ú¡Ûð¢,¶yŸþlTŠÌ‹Ytx¾YÓ(#ó´'®’|lÛ!KíÔ·ök ÇÑÛ?¾QI³ï÷ ÅJ]óÜKx‡«‚§£·]ÅÒ5Åjš‰Å F'Ò žþÌÒ€+AgÏ't,ˆ²ÖÚ;¿0£=Sþ&™®íÍ1ßN™þ?E÷Þì{¢¿­¸8¬"å„3w†nµY`gk¹uv¢ÈmÉ1 G¦z°{ÌöDÄ;AÛÔ (‹Vƒ¥;aèÍJï-‡c-<ÑHXe‡Ë"º>3·ñ§y¬âÄîÈòÉ« Nx"ä æºòo&Bš±Ç4Öñ”3ýB Yïí*Y,ý G}Š &‹TH&#`>ÐÐãî!üaC?¿{I?ƒÃ¡š¹‰Š«E<® Û²Š® 7¶ÅM— –‡Ÿí§2zn·…OÌ×¢š÷Öa½+Ëm’ת(DåÇe?æ °¾îG,¬a¢ƒ9xØ ¹3ÈÐS‰V:.—«?K*Ë4ßôJæ¢á@l™1oC°lÿÝ­,+p×G›Áhyµ#Ÿ›!ŒUÚÈg°p|pAOÓAãNš¡j›0ªp6¼²!lRž8šÃÓ“ÿlÏvÄ}ˈ nÔ˜ie¦ìT³_$4bŒßóCÇç3½#:H4〾 „ ÛüV¾RÌLIÊDü²˜± 5D&ÆÍ˜ç9ê00‘<Á™fRƒÆ‹nÞG–²ôyó,O­…ÆêÊ d<3dÜÓøi?•NGÁ>[`éYaÂÝ*‘fézúµÓn±*©]<(Úçàv_iv9xùçJËìœUo“²J~…õÁÊ‹#WãÕÎô©}1d<&×iÔ€â/¢ë÷<(g€Ù ®‡c5;OuÖóQ³Ð&¥w‚Sý´c>¾"?uAäÑöô”œO¥âݽô]Ìñ"Y¢Sÿ‘¨ËÙìqó1]^ýLqJÉhð#Q^Õèì!ÎIíÃ~–IÓ.1kmú»P@N¾u¾ jª»ÿG'ú«‰¼†V,6²©¾‘T²-€¥½Ç9E½÷>?P@ý½è(n,ß\€‰ øOr HÞˆIì¬S:­¢GŒüý`‰ÆÃl í;):c®"œµ.f¦þ™ÈʳI9‡ÛF(Û¿ßhçÞCóTó6}Ù®~Ð(¹ËHË‚ÃÞiyÎÝ´n&4Ž‘§ÏÍP;æÌà Ålq:°»à¨¡ñ]L„ùR1žÏ<æëÀø ËN>¬sîSøã[”‹»l^?iÖ” èàþ%˜toB{ž|*ºÉ ‡gÿ¸iu sŸøó»‘2€aI3;•áàé-Ôœ†/ÿ9oVØ„HH  (<7ûIÂ+ý’ÞDO3àE:Ô·©v] ïìæ?•/@ ¥@rÍKÄûâÞV|ÕŽ²ûØHòq£#Y 6ï0GЍÝ5‰¤·P°o/•9¦múʵV‹~ÖåŽ*Ï Ê … Ú=¢F?º%Yed.Œ|…Ê&"µ+]@¦hßûT¸üzcÇ÷3wç4ZÛ&—ÊÎg{´ñ–‘ÔÝöØQu0bwl›PZ}çM}´Q“¼2i­5?A\óO=šmV–ÅžÝç\8¶Ð韡®ÊW+‘29 =/—VÏ-öù'¼‹™E¯D§ ¸E˜ç@âÆ.Ë] >Ðé<†ÑË„_T¼¾BEâ[=ਯV¥ÅBqYm-ÅjvfâÇÐ\‡2)Bù²ÝgBÌ‘öÍCÞ—ñ’5· ¹ÚÙ°Pó=–X˜ Í›oƒL.‚&“£ø£4š~ã8Ìä#´Ðêÿ4سU¼Uýš|‚hT…•yÕùÛ©XØÐƒ»ÁÓ¨Æ@FF†ûräÌaþ rd;sUëÿ5@Ê¿[ñÌô0®˜$Ày( Pд`òpKèùæG¤ˆLEg êÏ|À8d>°ÿ£vÿo2¶þOrs2»ÞÆD:)œ­K&÷wŠ™öZ½àG>v@9í²Q“j¤=îPÝýø…ej¯ÄÅ®o Q¯y©¬Ôw]}¾p,ÂcÜ¥à¢[œ|O\A…‡fk/âH±àì’ùO»ƒ¯ÃñÕ,§œ8{Þød6n4É å/z ™57:äà&"Nvùì<¹çíå ïÚH$aóÎÈ€£¸„37]—å†Eób[ÛŽ‡-¦—”ïJøÛ ÏàwâiÜeÈ8ÄÞ+Z]ÀÛÓE”Jl2"`‰Öµš­B¨:´C-´be.)7@ôh ]KÔ{2®kßBϤÎ7s€›Eèˆ|›.B6˜ŸlHÖOY†õcÙ§SЇ/“jëÐFoKÔpCm‹vVðýø‘æÝ«ÞùsEi Æêœí ëndÀy z#x é—ÿà1vyîNéùWD“#å;JÚ€–Íb‹Xa¿yEC©/¢«Tÿ%¡=­CÚù9C)¹Z¥<£'IþË +À5ݽSÿŸµ­‹Î͉åV€‘ÁÌ1Lû˜!×ù¬|Uõ^ 㚇Ô1H ’P¿€™˜•y6Ó¿j¼$°£—%ž´Üei,°#Ù ±€Æ”˜Í »ë­ÈJ~èø¾£%Ù$ë ø.uÒÌ,Ëð9±46=C2ÅûÅìUkè^äçÌ'SÌÄ=.ÕXåB!É2<ܰ›{ƒEMãýÈÐóbÀ)Ÿ]Ù³Ž=Ú(OïÈùÈÞ0eCzèÃr­Ö.…'­7®9ª°²%÷j%öÆPRAísºILù#éöˆÝëñ“áˆøÄÁTò1c´Ð‘è­/Ödž Ù6g¥µúügT|£•¥êNµÖ‹Ÿme2Û›TXG5•XT**åŒ ó½)ï¾™éÈò¡¤Ò$Ç&ҬԨث®ÜeˆÏ*åfR‹”õ[‚:*{áóçW NpußÈÈhv•S;&ë>Ù5±)ÁzÊgœ>ˆ°i˜ÎZz%D·"ŸZjPKÓä['dq•œŠ?‘zSB‡9µïž=F;;§1„Hâ£ç• ÒÒE¢§/1ÿpp¶äbN:^!kÑd ó‡äs%)Æø¹q?ëÒöëù·Jgá™y&ßLžÞÔ5¹h/'œ»Ÿ`ÚþXE¢ÿ;—žTÇŠEÄy¾ûŸï<⺛Älwµ ä¦Ñ{vðo:Ñ7ÜiáxÅ+_´²aÉ1tíÝm[2& ýצÕ8H ç?lÞ¤&5è5Ò×L»j©³ŒªMœ&éQþ º´Ń,‘·¨{×4ÆÔôø4DÁé ©¿É¢ºÄ³Þùzü¥$-£¨{Óc¬%éçtK,nðI‡¦ô0éŒïÄ¡rÝgZí‹9!dADå·8ó! f±zË–1Ã=DHNy*|-Ð¥8/4ÅcvS&¶Ò[´Ù©«w ¯@¡b•÷´?'C×åƒC=ìûÂÔý üÂ*Aç|‡¿æþP‹"G¾ZŸ¾«M7A.*EàËÍ{³Ÿît~–0oyüO.‰Bß] åZ¬Í¥GÚÓ „ú˜´LœÎŒpŒi¨Ö¿Y\PÓü[æ–zº6m®•z70ÚJ[OZ’tg^ã»ðÊŒéØ4ŸPÊ»>¤”оoŸ5’ñê•@Ni © Ž2y©W®$$\5c1§^p¾é+꣺@·ÃôtÙ‹2GßPë×ÎT¤ŽÌ±°¾Q¤UÃÁ<'M†vŒ>¾CtRɰAÓç$Lt[/t- ž`i_>ÄýÁ À‚¹Ö¥~@z;G/s¥y‰e·±£âRþÈ÷œîÝën_”×ÿñÛßyGéé9«Òþwq²-ç(¹T8Rp9¬t^¯òAµm" •‹]œ‡ÀÀJÕ*ýG[T-9ÅOó=¯ö‡;îÓ-ÚK7µ#¶Î"´ AkdÉ7ß,TLËéá.!ÃlÃêRÚ#-϶_f²+^.ÀHRQ ^&Né9»g®ÄvK‡*ëh @¼î­EÇQž³¶œž3&aF6Gòû~ß0>þøH‘μðúÅ÷#©ÐGl?Œ &ÈÆœzôÖJäZGŽèº-õ¹?U£Ta#ð°âÁÄ ˜ê®Ÿ&¸øþnk”·â}(„Õ‹Š–b-ö¾`ŒUi׆K·Vß.ÝK+DíJ2Ðä îét6Q÷•FÄŽ@-¼ÈÔwêÀ4w•Ë'’éÁÔ .Ní‘E˜þÀ¶Ù¨ ¼}E`G¡ÀW”Sü|²ÑXÔf“š#×§¸«í~'wìt‡—>sò&mœ% ã)ãÅ":Ù;šèD;5qDj=Æ`>Ô\á”**ÐagàÎ,‹’¦È/ µÒBž™ñ¦&€rƵªªn¶(:‡,TÓiLØ%ë)&å}»ÇÁf0u懡DäÖ"WØÊáI{D#B´£ñÍkÛPã‘Ì9(Ã5þøE¶¾^-~?/_Z£úëEÊÐ×k˱텚œë¹÷Ep6ð¼?\±©Œœ†HöX<Û²8”¼qhÂÛØ;ÐŒŒª@¼ç„׿´îÉêSY¬nW¸°òëËLä ‡üxa3àËlâüOæum2z+Zàcã Ô ÐVð¹Aæ³`ˆ¥[¡ 5ý2ÕôŽ]–sW#GC(Ô€{—(1À×Lÿ¥cŒ[‰É‰æì”!H }m¸"WXYXMŸB!ÇÉ÷7ÛÑ0òw༈»О'#yíò^ꋳ<5†4wn8ÒéåÁ"|Åi«6Ë-òç·´GXùÜOú×¢JÇéôz¨PÂ!Œ<³Z7ÎŽ§V2®eþ2QFþÍc4®=<_I/y^󆺌·O ´ÜÚ8;ÄÌ? áw!CˆÖœ‹ZýÌÈ<"áj‡àm>yÜzú=°7f︶ø‰G*þñĨx$¹¹ÆY¥Táüä*^ÆZÑïKfÞ¯oÀVš ž•ô/po¾¤Ó.·ï¦H‘Yjeωœ/2/VyÆÑ#F‘’©¿,ÅT­¹{ÑšÛtjO¤ÕUž`«”ă۫öÎ/(Í´Õóüú;=ª¬Æ¨<þÙ̳¸ÒÁ•¯ô×SJlçôh–¡8àßêÅGæ<ˆf‘rŽJ5¬Õu´ rÌS…¢ë(×ÅR•(õË ›§ãÈ„hãwhû ƒ‰ZµM²ÝT&V*ùد0Köí¥Nœ’d=ù&õÃW‚iu 0*7Ë ºVºJÊÎêæ@¦Æ•ŽñVµˆ­°ê_;7»¡+{Ë"8{KpvúD£­n»]œ&ïëÛzÞ|`´D«ŸêI;e‹wg®+æI½»?è_FÑ7ÆÙ_|ÓØ°ÎE…8“ ¶* åKLá¥ì_ì +(³E¶p‰¤ä®Òáõ©È„¦ˆÊ)“†Iì:¯ï>‚ †˜ŒŽ>ZMK¹7y'™J£è ³î^Ãm_¹£’Оy¯QMdz <Ÿœ5\çžšýÊ4pM8ЉvÑîZ -þ±ADõèRD­ÅÀ§e¥"îóapK±Ÿî;_{DM¹gÚàºÂÄÆ¨6àïZiðs¾áiŸUéÇWëÖý‹QN+IÖ38·ÛÑàiF•ü£Ò  Ä¸RÙpm­Ø6š•ýÙ2‹é=Ó8•M½,!-,¨`í[„ëz_8ž$ä?O¨Éìôe‰úsAeUÏU±Â(àÁ˰ ¸¶ötrò{ÀÑHô‚KYµK·8o½¿¯4r{Ë\Ë|@ܶ¡(Nxƒž/ê½·ó'y¯Ž+P”¦é·§"_$Ä:Ž]+—~æ3Âgqâ0í#=»ÆOŒð9’ÆHE(ÐÈFKFòOžƒ€ó…ÕЈ“ÆÁI‰ƒÏ“”2ª8 ¥vÐ0ÍRý™½Ã¾Ç LÜ3e¿v)Jƒ¡ü¢6õþS!Nw›#*vøþRBUbg„65œ$a¾¥«' •AõW®*¦ ÉÊ ŸÇ‰Œ0lãZP{­ïÞË¿”î)"Ü€d ï‡Ûsê°iŠÒÛrÍͲ“çûÉMŸÈ~tP.i'¢ð×ÒòÑ”d÷¦jÀxrጱÝèñ5ܼ\Nð93?ÿÀüVÚ­çÚãì.àÓA[ «¶ŸÅoM¤î\©šÔÕæ)ÀÝ {[“Þ-ШÄ#LœEèÁ=àN”fÚ¸Ïˈ¤àYÁ¦F-«¹ §œ|yéM²ih±¦2øÅ}žV_ùioúÀfV¹ûéÈab!±üœþñu~@?_,ã%)ÝT’ü¡*=Í|PÓŽê­½­¿]Š‚D*©ÎaPëzF9ÝŸ¶ã…²ý 4“ÜͲ>±r⨖C¦ÊÑ 3CØ]&rÞ Ë¤Æµð¾L—‹úÓÊðOž¤Ÿn–‡ÈùšEgEê%/ ©–Œ•†ãÝ„©-Çp[Ýh-®AQŠ ß Ÿ2£Arôºv©„Ú:ݼXh…6/ð¢N¼C›=»ÚÏý„˜ëÒŸˆð±þ«Á…Cެ† UE%ó&`—¦u)š –{Ó· îî?ÅxѾ™ÓS;Ùq®Ù¿ÉÚQýÅ@nÖàÇûÇ3V<ÖÎlÃ’7Ê?Ca×1ckϘUZ¿„ŠÑ 7B¶6u¤ é|*.â»]®&@`¯UÙÕßò<ŠïáOñAI£n\”ÍÞÂwÂÃHpÐPùH°‡Z_õBðg÷éˆ õ†¼òµ"¡ò>ÎD4žSžpøN }ɾÇÍøV Fñ 2&ªAõAqšÑ¯ˆÿ!WÝ uÝ {GD  ‰¹Œné*}”p ƒÎ¾êíØ¦öXu‡º‹º<ãª^ޝ±ûHkã`›žûŠB’ôÀ7Šþ]$îÑÊ«QMÜfÆ–Øã)JeyßTˆéCf ¬×PÃÛ ” „þ+"­Âgobé‰%ø‘û¥7g›$T{»ó_‘] ±9ö Ñ‹[!i!Ò"ñPD6…iª0)€¹"[¸9ô”X„¹K rCR²ß` "°bV†ËQO½ªOŠ:eù’ú·ÉJE¸G^óßkmã\ó)w}Eðƒ%èP^ÀÙyõB€zç~WäSR_#¼Y¥@7ú˜gŠîlBSt®·E¹Û]ÛU¼ ±X2{¾ÌN¡¨ÞÆ“X¥t¢íFÌ1ÕdTžÿ#-’¿Ü»+¹…÷6>4õœºú;Ýq3‡6FLïÛ©˜3™^ÚB¿Ô©†}2ègûë}æ (ï“G˽‘1T„ïep‰"—Ý‹}–a3•5âŠË줩w_`©õü›Ø°mxþŽcâèGãùÊX|°⺠.½ã1Dôò0§XNÖ OqöÎÂ*“ lTàDš8`ü¨•J4{uˆ2š€—NËìÚpûÀUfcu"ŸÝ™^™&]øp¼‹ŒÆ4­¼jöE™'G[‘#I=F¾Ä‰¾ëgë—¨ÞשÉs 7ÆQdϽÂ.\¥‰Òç•Lq6ïNLC¦Çóa£éi‰#›Ý¹¥“Ì*€ºÞžé•Í99 nœ]<·$´Ê»2ƒ¾Rƒ±Œ´Wå{ ,0.åWýOÛœVælŸßNÉÿª¸_`Ð+§–”úºGjš1à x©.A{¢6â3dtÈ&3Áº¸|_•xF j+@'“¿š—º8oÅ0&HâY€!/jX,Þ'xÆw ¿³ÅѸ£t­¯jÛ÷Ü”_%ífÔ@³’,_%_²?Øöâ¦[“»"V„µ1$+uu—3E²)ºbÁâÃ[ÚìÑHõ²ó¼÷9›ßß?y—®¦—$[ÆG1n¡WHNË•ãg#OÐ.‹ ¬=)¿(-ÿ@¼ ºŽU#M”=³|ǧfr ¼)²Ð—©VÀñqwðÔ*Ü´sÍèѺ5´Wú‚˜ó½"Î]P!ï‡û4IßG؆PŸ÷µ›vÀÛ®D?3oî-a]~í(O3[È¢kôbÞ–û/½Rä·V]ŒeµN£mP‹Ï WŠI¢fR'ÍáùvŽþ„”ñ»·Ûƒp#!˜\rƒÔ{.@²z&„(ªïç_k/‡d¨INX'W7XËœ¤o©2ƒkÌȵVJiõ­ˆ R¯ÃúXiR†‚Çp€óh‹eçü$Ò£®¶t´£!Y'(DÝצî >“ˆøö1ÎrQrM÷&å‘-ÜÌŒe-ÖÉ`„b0=†Xàò÷³QBînù;dÖ2lM×܃'Žqîw ‚æº6ŽÙUSìþ"“¤ä/ƒ¼‘HǬé{}œ¸§$½³¯föާ3¡z¾°;ñHb‘2–•‰ó0ê¿©júÇ&Pµ¦ÿ95g˜¡ê3 ›ã0ë‹E$ù5D&46öi«Z¥;Ìî¬6±DUtï§èÞsR+ç%懂,·Ó‘'33g‰[¿¾–j\ÆSªµé³Ygkð…¹q2©ZÏÊMÄZuN£4éë„ÖüŽ?Äø—Ekíd»“µ“TŸðÕ5"uØä*fdÅàìíIJ~Ôš°¹e¾Á¨.Xsvk³}…`IÌâü¦Z¯aéa¶4⦞ðYuLcØ’Œ"'rÐpØ?ìo¥¬sJö^@¿ÞR}œ±eßduKÿ!ê?ð¯v©=R^ɲŒQ¯ì°NÈ2=ÖɰØSš4ü<ØÎ·•ŽŽ»þÔ1¸ùOR’÷ø ´»ýÕovÔ.zŒ½ÎF@/n}X-9™ æî'¢Äay‹7ܵyŽé÷Q!h·m&‹䤚ùl‰Ò5ò¶©n]¤tᛈסóž¨dt™Ä혔¡­ó=A¶ü.aM€‡ôÈNC{o<³-¸9 ãÇF=¢-pV¾v¸wc&F䋸2·îøÜ7ìŽé½þ­Œ'ëßד>ýO#n»¾…·ñ²Ö'A;2IÒG`OVì‰ ‡?ì8W‹ïv£ICd§-MHb6à Hâ·]…4êPºþ´á‘‰ŠâhÁWÀÊÆ(¢2yo¼rqÒG<ØŽS•ݽ±ü:¨ß=X†7ñš‹ƒ:ýë_eZˆ8îÍhN#·,uÎï¼ï”4JÎjÛãUŽÇ/çÎê°c¨•ôç?†v9¾ æs3„ˆ°[A’!¾fÃbÂ?è[9)0¿äÁ+•&ÿ/ÞÃ#¡Yía qŽqÂE—ØIúv6‘ST¤ –]Mrì”Úa 7f«9ª¶„á4ý¥±±›þºRª ¥Ùœ¾\‚Ûªf7æÞºJ9à QjCPÒ–¼¿àT…š‰Çérz1vþb°È7äìå1©PÀ=1ž2Up2 ·[ÒåÛ¦søš²ïÏ6î9L~ëvbKÈ@ú†å‹/XGTËkýýv0AëäTNZÁt­ë!Æ/fÊO\28–ºp—‚Þ4¦u·¢ÇöØË’OÞ]÷u´ð¶Ž.\/x‹½êðmŽÂ\˽X'À°?ÖÍdðÐsó-_”ƒ–DåìlQ…ºÜðA¦_I¨È1t©®•Žv¢õ¯Fž‹vÈ3x,_û‹×±»»> Ë|—†Ã¾*Þ^zúŒ ‰&Y¥›Ä!âˆE¡ãNáxd.õ‡¦Ö}ìß³õë@Ò"•…4z5gÌvƒzNMh0OáÅ9³\4µ›Ž2“EF˜)xÅäß•ZEN¢cÃÊ]qó1‚£ùüµ¨¯×¦R f;ÉÚ@ &ò…ïFÐáêø¬±ñË€^9†ÐKÅ…?ÆH•äÚáÌ·I»Cœ³.©vÕÜ6PÑ ûœ˜„Õû4$Îæ~ò]ÒGgQÉõOˆv<&—°m”‹íI$–îãÐj´W^Ž‹,¸&;[ÏO¡`‡ õ+gS ƒ0q‡õ,>‡ ó˜µÊÈø¤ÔGXMÙ Í®¤¡Ï K;ñª¸µÎ¶Ë4ˆg*¦½“‚ÛÁؼþ’ûõÚSXD<¶àrTé8Îg‡²» íÒÓY8sz—{^¤(’R Xøûµ<™KW8#yéI ip¹€á½øÏŸL<^ ön†î“ÿçþTmåA6KË7!¶7%qúyñÖrúÒ`ÁmËß>CvçÿVÒ§¬ZÃ_ËÛÎ,œå#»æ!†±<¦g›0‰y®|aÚnh6“$ÆËÇÝì6NZ†prfÀ´Yî{,þXNR›ÿÜ錢óÙ0Z§M>ØC ¿:Iþ.ÏÙļŸïÒ&þœŠ—:Ðr«oq>Mð-ͧ¬SY²ÖR>v³íluýSºXØb„bdbkžpØ€m›2$+tzg»+EgX@ÓO…*¨/¦°1í$Úà/l÷ˆ¡{“hQ•Ûr%œÎû,5ÁÒnwÌ£Õj‚|2BžvÖÝi¶Sv†¹¬ÿVêaPQEKÀ/l`÷U®ö¯®56»ú#øá¥Qø­Ñ{d(âtCq^ºNœÖò87”˜®ðçîOæ…f"ÌŽnÑAî_ž4<¶i\KáàöfÂyu˜;õWÀJÏÁ’'¡­~lDÀ)ü~݈m$·à)¹Ã•&ˆ»û–—!™§³'–ÒrWÞƒ§@"‘ÁOìßy˜ÅÉkÚÞ€øVŠP….í09i 3)‘ysÂÓ, —e¶º°g;Çìý»ûê˜ &8C9·ñ%‘ÙgÔþä¯Â=âÔ¹Út/BiÆÉ9õü2£€@s U­s?#M[À:XêçµõÌúò^Ü…RÏÈÙ\`ë®7ש|¡7éÔ¤½,ÓJïÖd³ôö#&¬B™!×û<#Q„þ™i w_VnÂX0Ó#re&÷çõ¯Ýq·~a=ÜOÆ>ßIëYæ3zÀ¤¶Ý‡.wlœÐ̸a0S"Øs!$õ—ІÁ†¿ ˆç3Î8ä×ÁºSxÅb›ü—€`Y™Ô,\уº×é@pä…iÚ.Qƒþ ®«CPº)®Å‚Çݒ寴BN¨b›ÉræOòHœ$U“ðuUÊj+%–G”Ï1ËÓ®\"š¹¸Â˜Àn½ðñYí-=}üsê´‘ ÿ[ X|ÙênoÄ]cÀ—f"MƒFˆ %‹)÷ ®yb›TH3’˜Œò0¯@w>S39MÙ›¡5mG0«"Ʀ`¿®VH“Ý­{}­ÑjÝ!ӖϦÃÜ€Râ÷IýYJÞ /ýÓfÉ›HlU¦é‘äÒ,Ë„? 3cgjòõ²Ñ·ïfÌòñ-Å cwkÿ°X× œvÜéluHr×,‚ Ã?Ôœ“TÍÒkmt;C¾Õص9BHIÁI]®sNˆZ}0ßbD¦òÙ{ýN “ö›¯9ê!æãG¤|î¿€?s¹x­¿Ïg‰»Žn%cއݚŒ‡qW¹4-ÎÚÅMHz²iO4 < .>€Ÿ&×áó¹ø1%äh2d ~-‰8ö—Ž%Ý P³Ø¡'—˨–2¾Ò`‘c8¨®u{e~j }ÜÓ¿Ýÿ>H¸GQ’W6Ñ1 IüC‡óíácù•ëõ_ËB\ïW)Ù›ØòÀrãl1#ç\ØåÞIÀ1Ä&4VéFÈÈèŸÙ   3ÐÞÅ%—µÆ“2‘§½ÅÎÚ•ÜÚVù®‚PTçîgEŒËfaÅŽJOÀË_É3Ð2*qk,Ô0£­HÚ³ÔËéÒ?- #hÏÄáõI\:’”.Æ!Ô\r6Fæ”´¨Íý*³ÿÀ™¢)th')ª:AOå†XÜ‘pÂG¶â?5ļ_ø0 ôbòîôÛ¬_ÑX¦XWù¶ÜQ’ß±iܶV!ʲñ°{Ò/AU¨éâ¶8CÞ^ÁÈ4Ò. 5Å.4H.­ÃkL®*®‰§(Nb³ÈK`kÐ_u¼11 -Ó²ÀŠàsO¤‰Õö¹¢Ã‹"ñÑ€>åפØÞDä«Ô–¡Þ ļ–Ð(F?©Î• þ釟=ÓùÐßCõU¾…C7öÐ_|A©ÙǨ°Òt–‚ÔŸXÝ_f±*e Ùò&ÑßeÍú&˜ ¢Z’d[†¶F)?‹ýãdÄ}*òÇ »çzp¥q/,Âo=fõ¾,0zHæø‡öÓ!ã+½‡']Äa](à ñZÒcû7BNVݦá{™‹E¾lÆÂŽ3”¨vLž²ß\be“µÆeÕPÔ`ryÒ®Ÿ7¼+ $+ PKuô×IQr¶—î!x4¨¤û|Ámož‹´¾ŠëP–ƒÜç%$ŸÌMé}˜†tZ¶l>/dÝŠ·š¡gñˆ×–]Á·`·râ‚Ûñ2Ió†¬h˜þ¿â>>‹,3à¦üI8Pì²æØ1ÑïÊ<.Èî.9E™ Õ‹íé}zC¶÷9ë„^¬ö±ëþ‡` Ì]€KíKVv<á¯YÀ{IÜyŠDS"—­sÕÔ6£g‚—Ì'öª¢ªÌɪ›oTY‘ŒVÏIž9 æóyP²¹É¾nÚ q[\ H4ϰYtxç`+ ,OxðnÞ.pœíp{p¤!³„ò §©©Ò$Ý8?(²ÓÇ©Å?]Lk|7&ßÏU#‹á²O§ Ì­=5gä‘…ëÙ—v£hýüùÒð¢79…:m.Ö]TÞæžÕØÓê^5o”ï%mÒgýžÜœÖ>t?)›ÌJE#;×e, g÷A`?“% }6žœÄÌmíVt‡À[ª™+V§c rŽ9F©Û$þV©VeÉÞ¦Â%‘‰DG—AʇH’‚Àá2¨¡2ØcÁþpÊW;TW}·1¯Ž ˆ ŠDû’¬£®Xypa€xŒ» ¶¹ZNž¦o›9¿á$D¦Df‘§½°>Ùðz§Y!fñRŸ4Íæÿí4y«ÏËÞ4ŽHÆ•° Yê‡B?fl=6(ˆD~ƒáOkR„,t+‰ñ:ÍkŸÅ ¾,§×íQ=C¶¬AîÚAkÿüÅÈD«-#ÔÜ ²ÕPLéç Ùæiݪ4Lc¡·V2ù¡ÀÚ8…ÚJ÷âC䤯I=e•»mV°5‘•rYåž3kªó[ÑÎÅÍç5'.¼ÊèÔJëÒ³&Æþ¢¶™AÖT.ËD×’£Ïw£Ð‡®™ÿ)g·Ý‡Xu›—®­x' /¾ãz¢Ü±×/l{ … Špа¾,h0ïŽ $±ÎAï‡:M}ÌP€‹ý”äÜ)ª0v¼Èd¤6…ê%ªüsëÞá–Z¥£˜˜])ðŽQ'µks4ØŽ¶8¤ÝæJý ItÓ•6±¶jT´ÛS‘W÷ð–Ì¢^6;I&L*ÑqX¤HŸ‘E?¾ÛÞ×·M˜èò2Qx]X)[¢—ò•ªbH©ÍÔñkbŽì»Q9pžvêJ·CÎÈ&bæ¼diÉ <<x“(ÓÕ±â–ÆC1füH|›`LCF?¤S;Ê6(noZnë/œ4½ÞE•IZ6_ˆ]’hTñeJÏG8JT–¨w.¡@'Q “X*f% ¤Hµý¾h5Õ˜dB–$›’̪?ÖD\š¿'õœ°als3ÇßNæJÈÅ»/úÁï+0å '^`Ïd»âÆqÜü:¤­Ö3G†/úfR‚[ÈHBsnG®ieÇ['P^í̧Ä>å`ðgñª*ýd/vÎŽ*ªóˆPð×WÍ€Œ’qn–ûÀ}§† úpή(ˆÀí<¿•†D52¶®Ù©&e\zAl8:SØòƒ*NgF šÜƒ~Øì•÷R’PÔAr9üBî6$Ê—ö‚t\ÅçlÇÆ{ÙŸ!€øÀ·ûISœŒÄtÂ\ ÞIqÿ2žQ¼úw“FÔhh5£­f3üµàö~ ©ÎØò¾ôÒ@E>²íÖ­²"xª[˜_¡@ɃM8u=òOŒæÚÑžÅLÁöË…‰†i™Y%bÊÙ_ ¥²«ä¨µKE}BèÃ:-QÀAšrû”ý§ŽÍ’½*Ežì©FŸ5äÄ}µ1rmî =pBí³/]óÕ‚T{±Ô_z[pÓßN»ãk yŽ 2ý*…°OÖqC䲇¥,Z|ÍxˆÅGc…¯há-—ÔK0bY<0µ±Æ×‹~ïÃÌ_TâK,<§´¬ý%åTˆ ¯@n¤ÃÆâ;U ±•%˜§èæHよ²®¶Äqƒ8YŠ+þM+ÆW2Ú0Lÿ>nð•¶_Ö Ú‡ì$¦’x¿æ\\Ð2ŸjHk…%·î¤÷®VÂc<Ø5C&žŠÍmqõØ/ß½ç÷÷±®æs(T’2âl¥P…¶èMž.t“© FTòó–å|_v÷æ»å#Ûû¦çf8»£M %§ž:)OçñÌ(£´Å}ÖýÄŽT…SK%á¼´÷€Er9êïÔ¾ÒñÙ…ó‰Û"@ 'œ”…Q¦´¡½ ÿÉB‹´=Çnûü!‚!o`´ãD :ÖÀùŸ‰òLœ>2‡z’}„ÂД9Ê‹qå•sê›ýI’2Þ8’žÊ©ÎC°lú}¡»íý¥laÿl$ôY±8pÕýë‚ÙO®]mQRÞi3“YFA©q;k¾GèhÉ3ÍtÇ]·ºäÞÈ^ú¡N­l %;Ç•e{æŒÏùîj£…ÏùâÅ=šä…Ç“äèÿP²·BO昼_7Ããæ³ÃT2F¢ç T5½øঘâ6¶IgÐa$‡·¸Ñ åßvÚ Ø–g§«7_Òg-Ë«$åP-ÛЋ M”ñνÌ÷íH@\¼Ã7t¦ŒRüo&AÿA<˜O‹C½ý{*45ñZð~ô— >J ¹GÁéÊ'Ì£[ѽ hu´XsŒ¢DøŒ(r‹tâ\¸Äü•¥l§^Ei­0‚ïJÝ5 ³·K–vE8Yo÷$;rG¡Dn BdDýG)˜F 髽´*ù8šÄR½»£¡:–‰%xŸ¹Ù"Qý;;ë,aíÛ7 En;þ_@9âU|ERO€‡[¯?ð_Q†Ÿ¹u–‹Û+IXœzñà…% Geteô€·>$Çg1¬«g=w—}:,LV¶‘4n¾XœaœQ]4øÆ¡u'ÕØ1ÅÔÿ'Ì9%âL™#Çûž¯ ºH-;u€zÈ]Í Þ]šà°{z?Ixe è§à€0 ¸Ã™~ݺ'=Òá² Üï­;3.mJÁÿ­„agß×FÚøJò’Ôô$%nÕ¬NO€ipPÕD§êÉç÷xOÐFó½ Y\¥æ½`}%Nx­Ü^ö¬I@ì†Z\#[kàž Ÿ,Ö×i`¨C¸ä3—ªÃËÚ…ÍÒôÛ|():w‚V5™*øó³‹D€Ù¡9±f#E¨«ácJwIl?WY})…éåG«.™Ê5x”A¼Ýíé©âk@–„­¾þ@ÖðuÁyéÌåžeC · DCßòªoðo"KnE[ÆO¸žì‘ä„S©t›M`(Çä¨0‘Ø^ªö5ÐÕ)ÃÍѸGšÀô Œ(K¶x¥Gò½w»ÈmE`COzÑ“w`¦ªU¶Ï©ÿŒœ–VæÈòN #t(¼Qß ™¶Äž>úæÔ9ææ˜tn»Äa%Ž ø œGÌ¢{-˜+rÉ |ò<Öânn(n4‹‰DŽ$€Ÿœ_=KLºS?V!¿ºÛwlcGFp<Ÿsº‰±– Id ÆT幦×3׬ǟ'_ÿ²Só‰ªH„F‹³M¡ç—ä;nŒ¯^Œ“š:'0å~Ûg{·>?ÂU™÷.S³z_™}.Ø÷¨,•û®¸ˆ;ßÖ{£¦LÜVËLõ°kFám˜‹ìÎúËžp#׿\Ï]eªf¡z³a¾yñ2¥xó«û—– `4ðRÙÕ¯Üyx.É$”:_š=–ž×9’TÿgØöh÷ÅoΡÿÜòGJÈI]:ô¶×î >6œ p…‚ i;9ŠÏŽCU2ä9ã s“|S@.EŸJ£Sœ÷©š?QÀ0}]ø·wWé.¢õð»OwÓMÓUfXpµŸ×øOÇÝ l]1‚Ûw‚|¦¢ +ŒƒÄêJC.ô:M •,ÆË¾R©ðÍfS‰sÞAÿéÅÏUòPkžþåºåæ;ÜõR„™¤|cÈ0"`g„Þ/F©É#Zî=OqÔ•› ýŷ猌 É”@Â&=ºË4<\d…wõa Pê±ÏÐ$ï9AÇÊ dë­K,°Z“ÛÃFÔGg½(^—yÆÊ ª¡ç9=¾ƒÈÓE\ßëÀæKT·7â˜Ý¦"| Kº‡ª>Çúnºì9æï%7Õ—C‘Ó9ÙüB˜=®äGÈ#ó_Ž#!¬t”8qs-¾—HþhY"cçq?'àÔ››\·¤å]åNk€ …dPÿÓ®_({®OC‹ ®ž'æÊ >®­¾C&„ýù“’UD¦€&£Á™}åÐc Õ S^fáÁ#=#–k5EÇC9¹r•¿¤ FÄ,… ^êË›GÅ“¼m¸cS:†ºà•ÞŽÊC-MÝIª` '~uÜ?\f1â S‚Ëx%úÛwCϧŽ%-y¶¡–ÕL/ë…×^Ê%ÿ€±Ùᇖ  üGÄ:!;ñA´‹Ãª©?¨bEFh9Fe¿Õ-þLãžýuC˜©DÃýú˜ì3{ª¯‰5èÏAj×þEÿñ?Æ5@PCùèBè9ºj(q,¨Z' ±5·/P&è!u†)éO/x élYéö'û_%=üsG銚3³¦¥5¼a†oQ=ë˜,J­¿ºÃÿp§3_@ÕHžHåííïsÙ2Gœb;%îŒOvÔñ®EÚêJ®Ýžø$ŽøÈ¥¾ÍÑê˜x>·£IQ±”,–Å=àm;üå9ìgÁI·‚h…>\rô…ø¢{v…iåBAöXuâ—2’ÏHÿ‘Ô^¾Ö'®ŸfÒ"² ýÔ[Ï3)™à½¸nFë<ü·Ü­ÏÕ l~?7樒Û?WâP r¤ŸrwÝj,€Bi‚3ÓZi›ì¥ÛÚm%Ô¡ỏ;ðÜ.3û½}íê<9`<‹~Zƒ:ÍÕW Јt›²2aSfhybÇånЉÍ{ %ÊúZ€õÄÏ3­ðÑ“~%g9Ô‘÷¿÷¥#Û`c(Áa켿œ@D'¡aŠåÔ퀟ŒÊ÷"QL”ì[î!ù1Û§œîåìö˜ë„«-bOX³b*x„L嘲ÎóޝâYÒ,{¦e† ¼Ý¥ŒÄ_f>²d(Ú¨^w…6:iÒy –ï]–!Õ0Bô/yIbþ¤nFó{ŠÑûvü\òhCøÌbQ9Ç_´-Úq§RºÄ)NL ‚‰üFÚ^ýœ‘½¦Ð'Q‹~è²uZ–b}X¬¦tÑRz…îÔãÆ(n}Y±Êæ¹·ééYše#äiÍ3¹½œLDãÁ«–l–x½ פàòÐz 8ÎÞp6ÐSMhJb¡zÖJòy q±Ë¦Gg*!Óùdl¢ö¸$©o¡wÆ'‚wPKº“c%õúJ°¡]Á,^“Íî¥+îÛx¥x(Ø>l8¹úóÜN·'>Î>¤H*bB£5†,,7ùÉí•÷{ Ni²/ÇÜ÷мšPBŽÄø½ ¯ˆkM—“ Óä)mˆ÷Çm– Á.pòËzŸN>6fe«?Kd@õ^ê&Üžà k·v71t SwfI ŒG#¿´ Sp­²CjÜâ"Á:Äéñz戌KNo:rNcfºb‚!£‡Ø«àJÖ“‡ÃÓR‹Òú¥²+ÿ?¤”õûúÄ÷qÉÇ@+•X ZÄ»:ö’§ ²ÈQú\ñ>ì>›Z ·(»»QÓ·­‹ ñzcè/¯Ù?‹q·¯ãR‡ONÆÓ4«gGQš¸èG·¼í3ëTX¨(àtçK¦!½Íþ¦{j½ ÇQK¡¯ðÔÐÀËNBÕ¨~9]šÍÐ,Ì" qSB rXFš:ÍŒŒÝ¶kƃk@&ýŸRj¢uÐl1yå’O&¢P”罨½&¯Äδ‰dÔüJñß°Y[ÅÖqL2ñ]ÇNš:sàü•ç­Òµô,Æ0$bKª ´ÝDx2´áÛ§{ß­Ò#˜ŸŽ•Œušª7“dÆïìp$Š*zóÖVÝêý›S3…Fæ~^XŠ…qg­ù"\öÆAõáœ>gŠ(™íÚÈ1µr#±¾êßܼo%Ó/Ê#6È.8VèGûl±9iW$%ª¬ƒ 3Þn†£¨]*f§ÖE«E#—¿Û‹&án%gFÍ x©Y6OÞmíÜÐôA¦º2Ǽ+°•ºÉç‘·ÕqÊÒÜ›ƒ²ù›7&`}œ¾X´™¡7S·üÜâ ÐRŽÐï¹²É'õ|xÆ&$Š¢¸5 yDʼnf†¦as쟀,Lc:B°†3M*HxSÐ ${æNRŸ–­/‹9"¦ }AMÞÇo•Rù¸Õ‹Ž4óY­y++å &Û^t—.|IÃ<Ù¹b ¦t’¾Zü£¨p€\ Q"?§xP*.Œ_î¢v™…„©XBÅòûhhmYþ$ ÍÞ›dc¼l{Ò ƒeð …y{¹õ>ÎÁg\~æ6ÐÙç}½‰g…^äÔ€óö•ÿ »ø Úö¸$(@Ï¥ºs¾¹Qô§‹ÌØ^ÌàÀòïo2ŠÑÝlÀIZ3´¤Ö¥ ¤mzÌïöîVEÒkY¯51ù_e‰Î­o A¯0gøŠ /ìÑ5Åf‚Û+E§§7ÀdîšO@K·*p³¯05““$~ÇQ$‚W˜e$ #Í„yò,D¡ëÄè0ت†æ =Ø­¥ þƒë ÞÏÚ@Zï´~l§Ý³vܲ7÷¬ÄÓéòk¨Þ‰@Ê:žu–:8ø,H`rö êõuZ¦~f8‚m±4VM¹“ÎíSw&@†c{bÛž_lÛ¶mÛ¶ídbÛ¶mÛ˜`ç?ìÍ^lßWŸÓÕ}ú­ª÷¹´õ4ÕîÓf1[~+”½¢Ó<|ƒ:YˆÁV3¶bÿèN¿jíxÏõÉ”Àvš`fàR|i.ÐCZ %ÞI¶È&5®©æÆc³üÞ¡¦]£W°4µÄªÀõ'B&–ºÞÅfF äç'&-ºÑå]\ ÓÞ£k7¿*˜RUp¸_)®øEQV©–œÇÖW Í…›ÃN2ª—#ðpôÓŠò%ö–fÏëi«nd\ym°&ÄÜØÓ%—ÒšÚx-x6J~+ÕÝ$:¸ÁÜQ!¾ÕH› dƒÈÞ^‹?¥rk# fdJŸ:÷}ß¶! П1-ÏÌ`$Þ¶Àdñ›HìíìZNm®‡ ªF‰±–‰ý«ø0¢hî$Ì)eôkøO|Øz5é±KfêeUµògOËÖNU¡ ž”«¾Ž^–g Raï³ðVl,Œ\CjÇN ’4Ô=¯ÌNP¤ø°px(U9ßVöÅóé&ûö”W¨´çt:úSÀ€*0“ÐïȵB˘A†{ήIØÍØGÈ&+¸áuÿZNp'®›LxëÆŒ¯8¿É ?=ü6¥‘QbdŠ¥õ« ×“‡;ÜWŒ 5°X¦ÀiFáæ·Š˜µMÇ'Óé 9Ÿ7íÇÄ·nM´piçfÆBõÎöû Ízäì}gì4y?‹¦ðwGõ…ò*-ŠûýñS'dl‹kÒAÔçµ#8UÍ@#aà$¬€‚$*¼Û:ì}þDÞ/{|YŒ‰ã¶¶¢LNoÊPz§Ò‚4fèSCµoÇs6›ña1öŒö²ÙÖ.Lè4phGi~"0'”£^– =pcqœ ­9Q„MÓ¶û¹Ò}†,µ¦ûgé+¯‘O6’§Æ €ü4} N>6äoÕ¹?Â=æÞ’WV$³ùÃâÑ×»—±&àCc`¸H½€ilr\³—…èÉ‹¥LÔÌÙØäÿ>ÃÞÑlã­}9? CâeyáYý08úÐÁQ¥2¸.ë.IÙAÍ)‚hšª…<-?k°¹µ0žP6_üeŸýòdlýX où£r¯•Fz"åò÷Jç¼0ìê~8o„1äp=aÞ ¾|*Äßû /è³'š¥<·X’ø®‹U'¿"<Ö"ºp¸Ðx9¬s€…ßh$Ç‘$¤ºª2ïÏÜVp>ÖÑM—”ÕãxN,ù>¹:eÞ>( u!œÒ1wV)鳫ø6JΗKe¢Ÿ#k­´ë:HÔã:֘ í¹Ú´¤§×.ze.w«W²¬͈87€û†™ Ó´ŸPx´ny¡Z”Æg-K\ÞàY›ücLVda~@ ÇÈ’&±G¨õ£BɾòÉ©DFÆÛ1öûUSyœoøwq¥RÍ+P€S|rñå3ÇE®a׆7á^¾SÏÁm5ËÂ-hG·‚˜I®|³˜}[N#¨sØâ³”Ð÷ ÊçH ÄpYí9ãz?ƒs­ï¦ˆÀ—Ž‚8ÁêØÇ^Ü 9_×µ·O#OæóÇîæ vŽÍXHÕD8!X]²0›Y»'éÙŠb!¿W~”ê(êͫκ÷ÇgbÄçžÑâú|)Uå–Ëaï?¯µ¨ÓC£ë•0F“ñÉ!§uo™ÅÝçËÌ®ma”A”8}&ýʵ7\[O™‰G=®Œ¦ ¤"!—&·Þ`¢²‹nJÈØ$~j¬8>Í)ÞÖÃøÎ[vè‘¢¯ô¡LÉÃ}XƒèqzÔ–`ª¢Zÿd$úÓe¯ÓÕLjR(ÉW>›W‚®Eš—.¾*²w7w?¯ºÁ¢³ŽÆOºº¦@ ycÞù=îÕm‚z‰"ÃòDòÖI™Z&ÊHeû#zû aÉXÛ E#‘*ñ“gtØBn­í¯R°Ü!ÙyQ\_øeÔz[††WBÎÀÛ%x?ëØ8éGÍQQ¯O5³’L†…¡®éµZ^Ÿú§?~tôPÈ»9À*¶;òÍwŠ÷r–mÁ?ÂkþÖ—“×Äk:/© Ê•iWk–Ó®{­Êøa~·jåäÝá·\’„¢´&)§ªOJT¸ùÂ3kH¡pÜÀ]§ÜWe¿À ŒkÀ PRj˜¼à© ƒ²/ÆSÐÕÎý듼lQ€*'6•‰€p\þ ä<ª•è’ÎöØ1z ±¢â°ðëF¥A*«…FY¼“/ti‹aõ› Ó&‰×‡;‘ŽÍÐ*TôUû•ñò¸‡]#íXµÛŒî>°Ýñ7%Øâ’&›ð$/!kszâ‡ç~\^ÿ3-úX^¯F»…;L":hÉAª#’4‡Ž[Œáˆ¼”ŽRØù¢âƒ”«ËHeÖ!x­- ~TReà“¤ÐÓ‰ºFÚïˆ6ü* {[IJQf{ ö°Ÿÿ–¯è¢Nô75ô„ÛV+ªIØ6kBS›qw'¿ L°êùõ¤:&±dU–Chè%lÅ qøÁ˜¹/-÷•œHۅؾ¨‰èÓóìvœ‰í¿ŒW^ Emg`kRƒáÙýØ÷޾[°Ê+*=g¿ÌMe O3+_½à9÷/¿HQŒ’PëÑ7›ÎZÛ£F$™Â0œ*U]dAù9Çø’½´&úª+OÕüÝôEÞEZË(]ŽqœÒ‘ņ HbÇø©£ ð³^œ,8³ATèåêÀ’©eÔö0¥˜ƒó"v¨>°Ÿ{ñZ?nêƒÄýy'÷â|»†¿ê»:y˜=óD`sU €WÔ'˜îdPž߸k Íy9B+¹ù9ªtzˆ¯€@=Õ9_<ÙXw{ïâØœSé®Ã¯^˜)W„JdtuR_;ç—SÞ&Ü\\±™;=<ûݾ.²zngF‡‹á¥ŠM KÅï² <’ýÏÈ|w¬èÁSCñ@Ç¢GëŽú{)%äGNš$‚ØÒY¾3Pµ….¬‰Ýè;ëÌ*‡¼73<º­^ÉfìÅÅí‘ýId±>ÿ¸Ë‘þáóÛCüSwç¥w£q›«-1KI9¤êý©Zo=KªRÌ¢ ?€±Ä>näÅ‹¾*é¿g‹ÀFT퇻׈P”2•°ž Vã¶Åò€Øgjãšk+²€?qo >JiÆÉØ è…kîÉš \=»e™¦ÖJâ'@%¤ k¿Œ/`pF=0xk‚3²=c‚eëÉrN;µÈ¦AXÙ‹KH/La‰U#$NH×¾ókØŸfðG5Tn>$Fz1A{ÂÎÇCõ_Úµ¢Ñ¾ÃY'Ðx)Nú~àJ¨Ê*0uÈîîeB?=|¦2nÒÀ¥²©é!¾zÚv^¦Í{IÊÕ×ßúw¾fjq$cŽ/>õ¥¯fá)µö`*P.ZldØkè¿YB¸{×¼÷²0³Ä3„n&Ný€ìÚ(æ6æƒM§ BåÕ=˜`¢0âéó,9b5ó؃8Îô6yNª“zÿζjŸCa$Ã`ÃæÝ3^âÁ| ôQrÏžå9¸<(­úÀ¶ FaƒÑ%®mß:yÎ7lnzïÄŽ×ÌÑD rÁÛ+W›Ö†HÖm]*,íEa¤F“Û‚XƶçþgXäÆ x±Î)¼rE¤)¢Q=`¯IÄ¥&Á”ùv¼ÑïQ«u¹&®?RI‰<Å–%ïá.¨Š,Ú G¨2ðm%Mzl‹éoÀ!ñ»8Òyâx|6‘êÎ3äo*JÐfϨ7þž½ ¤DyÝ*õ§ÁÑþ¸¬ÖJ m䜡3”YžW üT!&žÊ·Ùåðb0„Jš³Â±S²ë?*hjÁ|™E6_ü£!´WºS§1ǃråc}d7ÃÏîñ™ö¡±ê­<îlÜÝòÕ׊“¥ƒ?­ÿìþþ'–ÛÂóx…¬"í êiÉ+ÍJ‰¥7ºEDÑþ,ÎË㞎MYÔæ[‰Š,ÒI«„:Enþ{ mȨŒß=ŒècqÒ'å¹`GfÛªæ§>õÓ5YR¡Í;l<^s®5RÂiÔ°S×ì9Oc‡N¶vÓ®«æXyt8fjâSר‹3(²þ¶”fååúsè͆®BœA Zd‡Å;_0.öËŒÇW:@$¡…m”î\ú„LÈù=Ë ÚÏD|@î„…‰°¶Œ¦•s±V ™¶Çx°!y¼Ic§ ŽNP päniµ’æ9ˈ•+àoù½»Q’URœBW0ôÉs÷`f9[öjçNbW¸ƒôÅ*ÂêˆFjÕzLñà6ÅÊ3²š3ó€3ç¦Ï³­^8µÝÕò.0³¶ÇÊÄá”">„•XØ“”‰1…”L~ÈAöG£kÞ»>O~Ýñgòt½©®Z t}+‡ß4u@>Æú<yèEYìÜYâZOÜF&t±¬úªõ¼B$]ŒôvWÞ£þ ôÚŸ‡îâóÙ‡ÎÂAoE'‚È:¼îƒ É5àŒR¦ãµºK gÆ£[DÿO1šÃ`sÊ3™>Þhùß^.·üx]gzkyüÂ÷óìÁpr‡ð¯ûÁÃR‹ˆ|hàKz—_R¼ bƒ›‘†x9¬‡n¡!ìûjM~†6V#Fð~—Ôþ6¿ƒ¦±ë÷i"}HG×±æè[>Ʀ’5ž5hk0¿™CÈáô64ðXÇågŸçA‹ã}ö*–\†œéè<Ù[W-#ŠÕBøÏHd ¾5hÜ}mi AqH;_Ð]“Ò`ãtØÎV(g|­p[» ›>¿þh­Øª˜™A´sJ'âhaþR1Àa:»ú6nöÕæGñ8#\~¤)XÑh-°'·¯Ùc¬Bõ»üâ©öhM ‹?!C…úÙ ,)8°"ó <¹´w>-æ„È$Ý!ñâ¥лˆNËpýÓq6 EaÐÆ'ž§ÓPǾYêåLW€¬³!ïÄzD_Í/a¯©ô×ÍmÜëžµUÍ1+àæ¹ùy¿f‚ÎËÜûe}ŽPÀ9S;ü=þMý€ðÒ ­ìðDX>y…ãõ«òg:Mò£mYk¯ËÞÕ9\'µeA>ÊYDÔŠÇOc 7ëò¸Åhe Žd© æÕ‹ùb³í}90 i5±y|¦ór¸Çõ¬Q`y§»q2(èÅ•HQXGn}zªç  I¹Ç¹Þºü¬Ì#û†‡ñ tK¾Ûɾ¥a쫽Ü'|y T|1]–s$©åIÜ&CÌ]ßÓ¿…ÒqÛä,ÉÛapÖ>2 ãüæãïöç0W…±–ÇqvT(»vhÖõûì"Ôú𪲠ßó6gXôÕðjoãý+”CH_:gOpÃÇ"ÓÐERX[ž.`Ôã,^>ƒîk6¹K?]C¨ÎóŸ×(˜Â]°4üž-Æ¥|̘yî(9?¼¦ŸÝ%Ïhמ4q”wÃ0'›ñÚv¦Tj=ƒŽ}%]ÂÄ9×_=š»JÌ m¼‚2¨[ÖÞ8¿ïñÚþÁ0x(T n N¡[” À´ }`ÁãY™¢œ¤eˆÇ*¶Ó•YdRû¢Å4¡°ßsJÉÒ?(+?E 0ÍåÈÙÑ*Ó÷uKÈ9ą̀­AÇM®wéxñà¶¶:ð-VxT±^Ô=x¾˜=uª'º›‚,,t©@b°¨„ãD`Xk,GLm êúÑaÕlØ s÷ßRb0¼{ 8m¾H$K”¼Öíû…˜Â ¶l'Â]³YÝŽñâòj'””'ÿZw«Ç”ÆTœ’ Ñl&5£½{×Û€t=޶ÀÕ2”X2œˆ¤4ÊcÍĪBl+wáþ¯îµFIL,Iªà: ÄóÇ3ÀW/…ÝiZY€"ÙTʸgNõ³:K(£Žw®BÙ!û¼,w<èïKޝ¤Š”° z8ðl‡(ÄS%Dð¼bU˜÷¡bl©“¿ÿà¢o¯&û°’KˆäR³*©g·Ñ-RØ\Úà™õªBf¹“dÈáéÏ‚)ÆÙ­¼núks’S^S¶nžàŒÞ“}¡Nïcýîï+ŽÕ‘hÇl~pª Í—½Ùï$ Ñ?ØfÙ¤6ÞÉ÷ÓÛ›ó< á°^b“S—ïyÄgÌ«ãß4Ê“•øKê;|l$Q®u'®Â…‘6[@v–}¤ôR¦®œ§Š<]ñùªÈIýµ,â¸ôO 7Båôø½²¼ú8Vt)ÓI*ð3§²Ž/‹pÌcïØçÆõ/–³ƒé~)K qȶ™™ÁΕTýýÁ=œÜ[[·–vH˜›úË„¤9±ÍË:Ì2NYbì^¹hu–.!êy³Š%¢Á… 6_/hÅ-©WoiC±ú$€b——o)Uœˆ´NZµšV?Ìô,Ò袋öG!\µ²¢Å^LÝ ähØü.™gÓéœ ¸ϯ£d,ýÁm/Hçíj̳ªÚ8¥=žž—e/áé7"`iA÷/…FA—¤y×䦪™€§ìÎtiu÷é»ÁüÕ¯Œºö¢î˜¸¢”ðƶ¹éùõ†â*‘£8 5Q¯D°DþÃAùá ŠÂS F¥+ÖÅû :Gué´rµ»ˆŒîdCm|:·_±Ÿ•µ ^ߘžÄ(’áØ´–Æ>·F®×þ\†oü €°Ršd%îy×!5gw‚šáÂN±Ô¨×àî^sÙd€6n÷OÑÊtªY‚¸9(ºEÏg—G$B£(š®õ¢„¾l 9çà»Æ˜!òg¼î¤äîž)ª3#£S”X’VÞ^ˆLëqšÂ_Î0VãzEêsxáÀØpNP¾>ò€[š Â9 G÷“.‡k2#e»˜,áp©trŸŸv? ÌÞÕiaÛØë‡fGê=b“]猑A¹£Ê4‘¾Æ€ªðS™ß,fœ¼Îü!5n ˜¶|S.ÞÀUܰ¶mFôÍQÖiÛnC…ýô¬í mȵ͎©°0s%–b%vO*Îã\‡P ‰<÷ Ñ |•+%âd§‡A«G˜Œ:0sÀ}”©óþLèVY9ÀTIˆ ¹ W”°´¤G¹^­jæ…7xå5=K‡ê»8‰\·ÁýÝÌέLÀôðBÁ´mQͲŠÆn¯`³—”¶€â aÍ¥U‘ÐÏGúÔEÓx¤`6= .¥á¿J ½§]p5C`Céò‚+1ñ¥[¬L7Ì2*‘DrIÚëŸãù3=YzuÌ|R„Š)·â7Qš3!@rTÁwåvšãËFßÈ‚­Î¨qøuÅ[ƒ,ñŠJ\>'fT›¤m qNúÈxÕ/‰Iã ùJr€ÜDÒ2m‰ræ‡(z GC1È–íw2cšzø\í[’r™?ðÏ‚5€ïmM²¿0áYJãÁ3Y·›·-ÁÔó¨<ÓÏÐ+¢ñ=Pó$B” PUÆcz´ÀM*A„íÐMQz(ä²U‚€¤ ‘ä]ÅbŒJHñ©Ï5­,ˆ+µW©¿˜}íÂV~!µÐ7‡ê­Â ¸$úN¯Ø€ÏªÃ¿6M:gÖžk`µ¡ £™Äö)‘ÊÜš÷ð[—z+ÚªK'‹(¾qA’†õ=´^˜Žù¦7 †»¹`] 3ƒ:åß!ÑY’›Û¸v,P‰bRøJ÷ºÆ*`sdððÇx¤>[6šLTå-AvÀuù~‡)„ë¢aCÞjÔŒc‘ó œ”Ý ¿TÓ ÏÒ7<Ô›ßÃì: Aó ¹âÎ.Ó‘B' táÐ Ù¦‹kn-ÆóDlsÿqL B•‰òþ1ÂrŽ£Ó‰gDÌêc²J¼ºÜÓ׿’äGÀü‘6ߤwƒKêmìýFkó÷ø,`ù§4¬õOkˆhï€àÖ­ãZeM´b‹1u5³†Ñª‰U›|Eª‰SÄt·wY¦¢Ï¬(þïšñáM  šÅííèŒõ´¤®tJêò0†wkNÁ5ºd¬K÷–?ì%ÏXœn@¯:†’ÐÝdZj¶)šÌwÙ¸Ad¦;кCI9õï‡Ò>!CàOAåÀ}4]â:¡[/xbfâ[íLœ=ò‚ÁE—ëÚ÷”´ýc1Åâ³…êç9Ô. -j †R‹ âð€ƒ[š¨”Ò€És8½ô߸!Ä v0<Œ@¥z"Sk8ä¼È·Le^ö4{‹’ê{#Ò;E°™èù¹ÆõÉÞAÒ uÉËŒÅ2»g­-Zú¢|"6ÜiÉ Ñ’×"ÄÒð¼¥HÎqÙÁñ̘ß?Èì>e/÷­ØÔà W;)rän|œÕáh‚ws©Ùœ)ƒ ™±¯°‚„kVw87SƆ!º^æ¶Ø?¸m_Q’û{5É5KÂòvY¶Ol ¶?¶úÚfàú­b“3ËΦ,éà !ÔN¿Â®pâ·ÌÛQ9Ŧbç%¿¶zã¶ )ËÌÅÞÍAŽ ^¼o¾Ž4ÃÌ8ƒ>ÁÆÍ„7+ƒ¶‚bŒ8…Fu&s‘Äd¼XâÈÁïªâÃ"øÏD‡Ï‡¿£YÕI¬p‡À,$ÚäÍ&z00ĤZáÛqw¹ƒÅ‘]þ ³vÏ÷ŠÍ¼×,Ô6—ÿbíì³L^]ÎòÝQézyõ¼ArQ™ž n5—¦ ƒrv)ÊÌ)eñ:˜Qeýƒ1éHŸ!ïÕ®cá“øaoš›ÈãnIZlÀ"›Ì°™Â £øVësÖ¿åìóJ=ýHƒÏ#®ZÀÎö2®Iu¥u¦|Î S´±–ûKBd’/MMKï…t딄&¼ø}ÔŽŒœ­¢Ô9BFl_¾6l³B'§¹¶e1Õ¤K€qW‰/¬@µ t¤E²sÎÅ}¬) †%öh%+Âv9‡ ®ôN/Ú*ŽÛ¼Ìa->ºü&M3 Ñ> üí{BPÈŠîA0+Óà^kI¯TbšX“œ&î»{Ô­—ô¡,ÀÑoÁv¨#º—ÔJà Vꬿ€Þ´”J¶:a¡±„»ÎP-[h϶ídüuŠü«—øòž÷]ðØØßñÛùúŽïH| !Úø¯r»t‰ëQÀ‡jC}ä=œÃKFKï’³Æô~hâ"D¯x¦àqÃö¬J§U y/Öä/é²{>¼F»è §rfÕht2XíMô]ÒÞ€¥<-và¨E$rú2ÈÈÐÈ:"ë« ™Ï*üL‰²ÌJÍàfôWpàz«Uè ¤ºãõ÷ãçKž”X›'܇pÎçj0&ã0‘Õ /…<®"«‘¾Ø;þ©gÞ,›h8ìàUÈ+µ¢‘êLÃ*v2là’kþ¯+;­“CiË„9u¦dN·Húã96=ÁoQžßÌ>à?}“w1ZCj£$`âi íMs«>½H‹?·QÏ$¶ý62+sköëË—ÚÆãÅ–rê¼rA7ÎuÇ\ÐU_‰Yk/ÓŽíÙç*xOæœÆ%‰G9˜v”ŠêÃË/D©!Ì‚xN‡zz7#û)´ªtÊV”q7=? %~2!òDoZp^ƒ~S6& ³Œ­2–0S¡èT¢ÙÏ]ù©£ã¿ýÃÝÙž’âz%´«Qÿ–ÇDPÒÅH¬c¿Mþ‘–—ÈŒÎ/xlÖ´W¥å7á&®@ŸYkΉ(óÑpYœƒ k]Á²L§….µ+\é&õ]¿[ºW~TÒìÎãÿþëè®ÇÁRýK~¸2’¿ƒ«uš‹mp@àqÓãD†xiä§ RïbUQWGAóñA§(å ¸.?ÜOÍe´‘gmnZÆhŒÙùÐóu͆9Í}ü;@öOS{boÍ_è͹‰?mÓ9…ƘÞriŒ™Ö97ËŠÊ'à#š‡l“ÝOÇ«¯™séïn”º*­âß•ieJûUæ«4•±G?¦ ¦.D@:vê_a0°Ëý*æ2B-Œ%äµÜè¤V°9ßádAê.¶è t¤¨v>aç}÷ffS³´gò4Çñg~XîOkuNE_Ú©‡,U>Ÿ™Çîã-¼O-¤Nùú…uª.À™œ\¶DÂàÎ]c´¯b}Ùò(ϤøÅ},ÛN¦Ã°+ÇÈÑòúÎ|€óÙ`ü1¬¤V¤ìÅåa|jciH(M&•MS´kªÄ1žStçn«==Ú(õ§Õ ÷¾‚ÃÓñ,áí±Nv1À8"Â0î5×$‰$~²^'Öð×ïú(äÁîw»`¦;žŒ^<ŒÛÙ˜þá}Ü45Ð…VÈð cæÍ¼¥Ö!N^¥ÖIÑdéúÉ5À ‡e´NO°°ë¡•Pw¬a­qœ\B¿†cò¨þ©¸îªìÝpêLÀÐÃü U0,“uCî\ž}ã`¢DEªSRÖ:Ü §©Ùj¿vû×x—.¹5¦j0ÁýNש£—JâˆåcòVgYM^;ÎYÛ“oE_ù¾RhêCD ?«QyYqb_ “ØuäIôàÁL“ˆÝ®©õ?d‰›ó'åXx3}%Yès w_FÒŒàÓ0AÜJ4`£+¤v¬8„­=d iº&e´ nîë0W9Ù¹Û‘VÌÃi+ݲéÌõv,É!¼”WüÉÅ×’Ž®;Îîà-mŠþ„Â¹Ø SÌÿ® ŒÍñá–¯7.ž~…›äÛöÍ׿& ŠÚ?†ãO?ÍÅ›~õŸxÒtÔ0¾#€JkYõF¬‹2ÝqW²¶ÉVÌŒ˜ O§^ƒoEm@äÄÓ™³-ïvØãõ½G†ŒïQPubúZýÈš¸®3Q„š´ &ˆcVp+©‡k¦b‚‹'øÈ\¦ðOœ‚tc'¨Ó¬Â žZ™ÙaM9’ðLŒ–C%F8Rá˜þ‚¥UØ¢`¤¶ÿÚi¨l-ª#ž³cùúlCÊ—Õ/þî¡”f€Àœ_¿¹%âÎ$u£´Ší³ÚC’çØRß-­èäK—»þxŠ2|fÃN)ks/Úô¸207¬ô¨› ç‰%¤Œ“òo¡Ë§·Ê– Ö$M?,!ÕI£w¹Ÿ¦ØÉ*@Š@†Uj&aà²Qž´=åÈS]¦o)Þ‚èý ë¶Oz­ÈCj^XÝŽêää~9¶ƒzzýÀfá#†×Äÿã`>*#Ѓ×_ν¢åØøôÚ(Gúî—;^„ ÁÏô1ûP ATDHÊ¡' ;çƒFïŒóî º®P$(Wö|›$=1 Pʯò$PÕfð³bɹãî"ë\w¬¨yƒîÔ–/ŸºMLk¸þM:nö4ezÅÅ9Ù‚´{šÉ-TaëXàwVJ›·M̈dšL›}ì¢>G²¡çÌ”Û[Ä{~2jãM¸ Ý¢”S‘Ç tc¨TD¥ªð!dªìž4°åÙÕ+ÕªŸs<¼Wd“÷wÀ´‘0òLÙ—^õ ûi@I»üé‚ýðqƒÖ?*C w`€"ÚUeë¢õf¾$Ñ RX6$Ý÷ìP3J&6sÕƒ$ÕàÜV¾Õª£'ÃjlÚNT½sq|p‰÷rËÉ-Ðîì#÷§©‹r½Æ®uÏnË_Ún<ú¯Ð[Hp·Êz»÷¸r|Òõqh‘A²a}ý2˜9|U>õC“в¼‰(0o¨_90[È”Gô1p*PÒÎꯟ©·ìÂv$£x¬hª|(c¤è­þtɨ*¥„xÊ>ò«ÑD]ŽÉ“©ò´Ø\ˆÓÆ£¸Ž´XjL[U¨¸àJŒ(ý#ÇÜ3³zhn '3kÜÞ%„V§zZÒ4`ɺ³VlÙÐ"÷Jr 4äžÃY^ãqqquú_ÊûúZÆj|´ºÆ0‹þT¢ô¾±²Ñ`MŒ:K¢3!,ÏP‚§2h›pŽóã##jÜÐ’—N­e鮳Ÿ†è¼‹JÈÅÞð7‰rîs¸©«ÂUH?²³ŒÅßûegÖÁ¤)ËÁ#Gùá°T kÜáCf„þùõ=l½bƒÕ§”Ñßåý¥w’É~ÝV\e4ÖNåÃî Sãv$ŠLÖ¢^íúç-âZ…›ÝNñ¿ lÁS¤FÇï\ò4óð»>²ÇcÔ™JÃÞpР° ¥Iÿ=ã…L;ï'éKƒ…ZvÊÎÏ=–út¿=ÕóýÖñÛlðœÂ“ò¹,î¦ù pw¿-6imçŽ ðTß@;áûlýÎ498 ( ½pÎ?ÞV‚Qh+Ž'£v¨ØŸ~Èd˜s\8:—U8ÅórÐQÛS»¶îb3fÂE*ñ©Õ+ ‹cA:DŒ u+‰N±ôCˆVWË>ó»#ÀŠÓ±ŸsDûYeõúK_³g%'hîvœ6°¸ì2'¿a؆º.OÖíΈSuÎMHˆ‹ Ù0£¸ª6RÊ =àù{€¢,mŽ:?kòµÌBÛ;cá?àÁ¸¸!¯E)|!Ûýƒ‰Ç®Ñ]¢²9íd+¨V-ܵhSÌí˹ӳÈf1(–ðiìéýÂïÝ夃>ôËd¬è*È¡®2 ¦ÐðZüå ­õB×>"ælí®Óò7˜û«Ç[ˆovõ‘j½Ry²?ñ~UŒÊ]ÀàÆò†*M'V/MXÁ3šbÕ.ƒÊÛ¸9Ñ™ ¾€õˆ" ¨B3uï5Asvá¡X!¤½Rï7U‘4ËyRìÍ j‰ˆ*Ú­ L¸¥Ð Í` !*@ýãî¦ù’ 9F¥GõÖÑH¥UËØï~‰ó³¸fÈÇØñú1ƒjyÛ#Ab©Ó.‡k JqÕÈ´ ¸l€8,Á¦tFDï2ÃÃcØLðTE‡‰=yüÏ6p`~#m“RÝ?¹é0ªÊüˆÍáôÐZ;ÎȬBzƒ-ÔQHE ¿TÌ—¦J*ɉ͒âiƲ4ð…ôdÎ~þëk" pá|@{sŒ1 ñºxR Êš%Ysåá”`eö¨(þä!;DÚ8ACÙ= – \´d°Ï%oŒ< ¾67¸1Òà¹ö¬uš¡_4uãõbÌJºùÆ‘*ÕWÓ4í uæ?^øˆA~}@n³ ôÝß48§ƒ"B‚ƒ“*ÙD&o·XD¶ k7¾J™Y¯¡•5îJa#öàÀf"ÿô.×H˜FÞ &¡× YB ÀÞ¼6ŠC‰%If¼¬i™?m¿…dºÈ%SÖFTÀc*–Bþ@AœAA’_ËXàI‰õ‹ Qp]nwJZøÞ‹Og1Œ+&1ª«ïÌ.A´eåñmT«åúÏ2ãwÛ4¼N¬]ƒ5•2¡…Ûˆ¬  Zü¹/Äó¯á¡c]ˆÈÃ!¥IfÉ…©Üà-@ li¦ÙN³UßÒ¥#žGè=6uíÖ¦ï <ŽQㆼ‡8éq†É©¸x<›ÊBNîç”úßÖ}’\i‹ c@Wǘ‰B•ƒñ$œŒ¦ÿ³PAê°ÞÿôëÓ—›ŽsäH!ƒ¹b›bmü@ý¦†'…Ö™bl€æ#N¾«puÍï1ÜÊܘ=øDtÄ&<©í‡¶4ˆwY>/4M½²Ž¿Å¾)ß‚Êþé祘$±‡„Q\°Vz4æˆ^¯^ïAf¤Âì-ûJ@ŸEnw/Ô]¬ò•V¿5øoäùÚk›Tv+r‡£ýqÿœfûJû£r]îÿû4.(qZu:šwkÞ©¶ -oÝ€þÒúèðˆZ0ôŽ«8½AŒ\HÛrG’)<3‰ž¦<ÌÃJŒõ¼ü±×í[0]*†RŠêäÜ"(|€Ÿ;~7Ͻ@§eˆÒ`Í—Yò¦G¶§Û$ž[”…%ò{iØ…Lm‡h®t¶ÿ©"®Ü¬Õ¦ôˆ4Ùâ9á M†ó,ɸ˜·²Þ3÷ãd†FÅ#íïœJ¡Ÿ!Íj[=Âøó<"¹D©èWªä~=r£–h¡âÐΑ¬3ù¥çƒl$'D²†OcÁ·;и¯†JñÝQ&Ü õº>ym™ƒ#€O‘ýYwÍtü¯$ƒKªÂȯÂÙGCèÝŽ;io}ÒáßçŒ-RÐyõµ`uzíæf'·Ïšn´J AHé¾H“ïa‡î{¿¨^y•·À‚1|xx ^œÒRôî'dßjÐ!ZÁTÈtŒ°}lÛi¾:Pó(ªOϰWL/²¸5­Ššwå…?¤xc×U²õÏ£û™Î$Ìž*"ÍÙ–o¼k}æ_eòC±’|8¡ÌëD̼û†JY\|´Ë¤Ý©ª€¯‘¥Ñ¸È™QiØ^$ÁºlÇ+VÚœ@aåÓp‹o=‹ë%¿$Ú8ÑÓ’{ÁžM¤+Ú¡È¥Ýn†þ¢Õ½·Bâ„Â`& ´û7?GÆŠ²ÊêwØ¡dùχ¯Am}ÌZt¿ä/sµýD²áÉ¥x†˜âL×aTÕÒS]xbб =QH¹À!%ã¹`þËxÆÿoÀø¿#kGg;G+˜ÿzìI¹endstream endobj 4 0 obj<>endobj 5 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600]endobj 6 0 obj<>stream x¬¹c@¥íÛ/œíšŒ•m{2&Û^Ù­lM5a²&“]“mÛÆd»Ýý´÷óîOû}¾çßác­ëº(I•TEÌL€’ö FV&>€˜ƒ«³ЙQÔÁÖ ðIáD ¤sƒ¬ìÅA@>€&Ð 4°±Xyyy(?u=­,,AuMZzz†ÿ¢ü#0ñüΧ¦‹•…=€êóà´up´Úƒ>!þŸU@È0·²Ä•´e¤4R ê) =ÐÙØ äjbke ³2Ú»iæ΀OÂ?€©ƒ½™Õ?¡¹0}:!â0¸8M­>Õ€¦@ÇX G ³•‹Ëç`å°p6¶}æä°²7µu5ûÇOºùg?A>%ì>yŸ`J. Sg+GàÓª’¸ä¿ù ²4ýcÛÅê“ p0ÿ”4s0uý'ŸXÆÿÀ|rAÆVö.Ðã“è0̬\m=?m‚9:[ýË W+{‹ÿò€à ´0v6³º¸|Â|bÿ“ÿŠóÕÿˆÞØÑÑÖó_Úÿ’úO¬@.@[s&V¶O›¦ OÛVöÌÿt‰Œ½¹€•åßèf®ŽÿÁs:ÿ+A4ÿô í§Æfö¶ž3 9³‚è3ášÿ·*3þÇŠü?Pâÿ‘ÿ”÷ÿ_qÿ{þkdiþ·ãÿÓ<ÿwhIW[[c»Ïø÷íøg»ü¤Œí¬l=ÿ¹ÿ.¢ ü·-óS—öµˆ½Åç`aúlÐÏÊEÒÊh¦d2µ˜Û~Žü¿èêöf@g[+{àç€þkþŒ¬,ÿ®óŸ<5K+SûÏ9pþ hoößûŸ‰ø—ÇÌÿÇÞü— ¥Ïñ©y:~ºóoF5å>wÅ¿_þÑuðx3r°Ù¸y\<¬nVßÿ‹Á°þÌ?šòÆ g+€îg ,¬ÿ ÷Ÿÿ›þƒ‘°7u0ûgØUAÆöfŸâ? ÿž+'W Œøg¬l,¼\ÿ²eêêìü¹“þµ>ƒGø÷û¿v-è4E8ކ_4Êïa¸Â{Z³J'ëÕÕå<³ÎãL´Si3H¤XX¹2ÿa•ªwÞÈŽ±{¢¼ï»†:¾à­_- ?£ëÃû §ß0ÒÙ¥RX7aT$í'kõQ‡xê½Ùצç$Ì üz>›™ËŠ(k-aÛ†aŠËSbSÎĵ ´¯ß¹' z²ÐfÚÓé¾#këûaÁGL.->ª|d¬yhGYaƒéŠhAçÚýÀ²&U]©ÔéO—`Ó`­µ é]-Sä;G® …uõáÙ&ú‘yNrvºÒ´— ÐáÏ5•¸A˜4Æ3Šë;~a¢ïù¸ÈF™ªûN‰>ln~q’³" á³-÷É |o­ðrè*\Œ¼+d<â·É¦õ)«¸f F?iËT¬Up§?:¿C# #%AÑ! bd#E²‘£/÷éé´ d.ˆtò9˜}h›e)eõLP}[-í„kV{ò‘üeKARÌÜÝØhq%R°—ËŠö‘~Ñd€¶ÛÁã«!2¨0VyŸÚäײU¶‚«´§*K§G”(‡K_gL‡WÛ~°–{ÐvfxÍòñµw» Ñ‹ê”vò+‹æ\5­†2 êV§²ðO{ÒÙ'¹‹3O3$þzÁ8Ýg¾û›8º²ÝÌ9VWÓª]ãsäñ Ÿ–aµ…S¾h2âÈ÷0?|]Gknçœúþ…Ú)%ÙðG0QpN-Q|ù:Óœ¼-?ÿòïâÉ,+hÅi!!®;ÊsâX–Ùü>›rIy å%ž­í ežÊoÖá#­ì‚.>ÙÔ=BFÕëÌlÚÇ&fí73T§žÉ-~ã ••S½Zç­ÕT [Ž¢cQ‚ø“;uF…©¶¯¾â’‘z:ò‹ôEB.1#ß>¾+he»ü¡pñúÖªŽ/2¬0›q!¿™eXIìÍË@…ÞDWftµZ§DÜxö:æÿ¶@Š)š•n :ÝݽÇâ<ÂeTþâI4›>‡DîP/cϺÒM¸±­³’]²ì+8AÎT48˜¸%ã‘ ”\ļ ì¸hlBÜz“ 3•ëÚY…ACôʃËÛ ÚK$yê2_œ²qD—߯Øîã' ò…»”öúCY»<ðH+:åsGnμaG—¶MBŠáaôõº*FQ%åâ…—¹Ì|{W3‚à•{’:~vpCû–÷«S›œZUnlÝ ×a ü@£äÕÕÂj–´r)²Q"eièK7kÎs|JýºªöãሓdÑyÅ!Y5å4bö”.Óçž4äü\ Rÿl´ï–ËðÂÀŒý¿hæ\:FÁøÆ…àÐý&íæƒ"ó´ýݨBäªØrÀHÙLëÞ ×€X¿ÖÃc ¥<µØ­\°Š“~2ƒ[Ãt«Uù™‘N^ÚäRÍæoÅSÇ ao–ßkn£êœÍ…–Àã­^Árà@~*r’ðWLhK¶¢êR‡ð0¼ãÇ´ú¸5Öü‹åÁÆGÛ‚ˆàß ¦˜HÚÇ\²Úntwê¡qNK+Ê•qÓM(©è#® ¼”¨00LKtp¹Ç•_Ô+)脆à‰F÷¼hÏè%y2uÚ#/*¶f.Êo´K8P>­ªAZõ[½~|ðºy«ÏÂѱå|.åm¥ q”€ç3`a t Óžâï ÊÍ~Hý¹òì÷ž”lbG€ÈœÆnóC Ý¥MG%cHªS[”O·ÐæûA­Ÿ²“ÔpH&h([dgŒ>%̓—†Ô‡î*Äàpš1G3u‘7|fÐoÏi‘¹ù"e›Sà‚Š};T‡é:ƒGzÚQJÌ“Mº¹Yaך½öýðh¯‹&EÝÑd—±8 Méš)#ëÇÈ‹ÄbÕ‡’í-»fYhQÄ­hÓ8/Ú­êÏt=•cIÞsygždx–Gj}E¸›¤pŸÛñÆÝ€R~h¼'ö_„iü C#Ñ7+ïÀȪàÊT”*¾­fuÍÊeE!Ê£&šc…•Ah^¨Ÿ5(¶.P)1}…³ð<ܯ¬ÕÂmÉ‹g²»]‰‘Ô] þ Cð*ɽØLë'Ô¥i~®änqê:?4 Ô˜¿®îm®£øáÿIb´¸ ™"ƒ÷R- 2½ÿ=îQܨnžNT -p;èKF€(›Dº«&ùa>8ò~Ûˆ~®çYlƒ”˜·3äX‡Ê§=ÓÏœtÿ×Oç¼G¬‹@iZ;·g½ø~C= s5ùˆØ•ÍŒ Žã©Fp)Ç:.‘Ÿ«ju± Ï8pó,c[%•i²ÿO®²Ày ZâÍ,‰ëhf^ëÃai¸á|ܤh`%{%мâZÛ·àÚAáfÞ#dÐ+²»«Qj…k!ªßê¬Ëõd3ÓÑsëAÚmk_’zõ ïœ Ÿa[Çйթ( Yÿ—¼;÷¶V •‰ Ëã½¼²]!S$P›]_/‘Ö6£pÒ8{ðÜ/ï߇¹\‰Ö ÅEÁ¥ ©P=ˆïŽ T#Ëò`=—BCK"Ñø}Ÿ{ãÔ«›ª Iv[vxüPOkçD>L$óz²Li°Å{T±t6¶ª©×, à!ÿÇ*´RÖ$±H&”žÆo¾Ø;j07þø™#{„|®è 9X´s½kp.}ÁØ Ç¸r2•ïY ¯lHRoϵ¬¡óµ¦X`¥uA7Eýa…¾±¶ú-Ä^Z©ç(•ªµ‘­Ý[ƒv5 &ù þÝ•%$¯, ) õ6lÛ—†j(t׋ó²î­YÊì='¬‹‘PøÅzŒÖ^ÛyœôÆàÊØbFWD »Ç$'ª`hJz]d„>ʱ\¿# PŽ8ÆQ7 t‘%Ë ¬/¹R¡*ÝĦ7Þ\G=´B¾‡…$’))È*‚Ëí­ØÏÿd¦~_݇»H+†û¹kšÚæú€AŽçCgº³G"”$:Éô³¸ž‰rÁÙÀ‰·{2ŸP@mHõrÛ—Y¥Y»ÃÏmKkî,N/zÎé|†nþb, ÇÚíªµékA3¥ÿk@1±ñhtS«ðWc1B¦Ö›öŸn+‡VlK<‰`CGVמ1­Â«ØÜÁ¿1Òé¾È*-°µæùPDc†T8x»Ã ã–±¨$ܘùPJ»:×ånc¥éÀ{.Ó…K}̹Ðë°>Ôs¤@©Kz F”öìŒËÀ¼vôAþu.¬2yšÖÀ‹ô"oOüpÙ­1¿qÑi7‰Õòlo2h­’Dµïª“ð‡ Åa6îótÏSa­0HÌœÇr¤´ü)¤¿%«7›w®Â®}­­æ¤®ýLª˜¡ì=Ö]Ñ¥áßÓ&ì8vÑp(vÙø9ÆÄ$Rz°.ñ’†i&N6 0ˆ6WצèT…«q §à†ù—¶Õ­Cxæužìªü@çÑ6aùÅŒž„k$þnTÂÁ,ç©éAÖ~B ¦ÜÞödÊü:1¯XÄæ7Œ¹/U“òA0Þí _ØF7#¢^ýØAp`f^à•W/yþ€ÿˆ+ï¡¡Sî ª¯ÏPÿQ >#c×Úc®ºÚ$O Q¢ù—ê´jô4¡ð'”4/ŽQ´9fY=æì ÓÌ/KÁÌSOê·„’Qä&œDd`QÞ¨Õ¡nîrÂwDá:%V Xkºž,Ë´‘R („«íßàÉE^V¤§;—#{¸·«®¢Ÿ=9©¾ÆÚ\"ßF°B˜è° k™db! ¤ÿ|K€ÇØëè Y^²÷ûR$÷ŽË—øH ÿXÛT¥jÿ\y'/ÒúÕ  %ø]pqntðúdå¶y¡O…­éÆU`y’{KP)ôn4EOS‡[VÚÒ•Œ‹Á˜»¿Oöçmo¹Fk(‰Áö¶ßN¬þ–aê»86§|“ã-LR® ‘Ó   %H¿¯»)zO¿P™£ ¤R‚j3[YJ,^˜³ e‡5ÑÒ0;:Œ‘{y„ûU)¤0ˆh,®cYV€÷·©¡ìÞÒ¼xuËZæø@دZÄ«“­œ¬š~‰º %"”ݰÒÒ¾j$™¦uÆ÷Zm]íÈÍÅöïDï¶Ã¬"ã}Õ`éM¶N?¸YæµÝJ0#<ñÖƒó2^&«A§V,0jñõš6Âé'ëiwü,ý =m;~8€Î¿m|@>˜ Ûü½Ñµz2JiA;ý¼ûš¯»œJ>Ä“ŽázÕ2´œÀ?ïzŠ;˜Cmjà <Ë#¤ušVûi!A‹ƒMÙ C!'”„–^KÛ±+áy`ºoÑ0ø Ïçßp76×øVÔ å{p¯@ÇN|ÙÑèDÄN÷ÑZÚú·±te|0 ÕÀÌÐÞØ“±¤ä'ȱž#kùj ¯9 ›Àž[¥ógÐW|o©W„æÔM¿š‡âM˜lK³fŠÔÂB&}ú]ϱ½ ü³Í€ L³zëê«2oÏgB )ô‘ƒ—k8Ê®•:­U3 ßéÓ¯r®çœ·t&m„hÂ>©qŠ>Ú¤Åjqµ’·ýJ5/¹X.N¨Â¢ÐßÈZDÓévã2ñ¶LÕ©Ù] º“2‰rœ`^ר,yxèÈŸ¦ Ëf³(j³RDÍÐbZl°˜´³ á w9÷‚#ž¨ºÊ4M”Ñå@â£'—¢µ3At¨Ë\„_CV'R‚ù`ú\ßõÁµcð­à›Ð{Ý êÿ¨Zka«çÖö´$˜RpL–\xyÊ¢ÄØ¨ÎL¤²F ¯£üaUËvô­m«¿ŸhSFkñý@Oä £pt¡q4¾Uxs‘=DcH‡‡‡Ým$ÈœQñ)fý²ûx A·e)^Ì:mZEËð“å45Œó'žSì˜;HÉÑ3)°©Tå·^‚†gVÎ5x»Út]Îm!"òB–5•h“II¸#kº¥BVY:Œ,ÿÌ6þÜÚ&_гfcWðÜñí½ÇÅ¡¼¬x8µÂrÀ¹ý+,5\ç¢üD3W7ÇÞ\fúQJè¥?ÍsÖû%C‘Ù8jvÑ~'‘DnK‡«á þžnÃ6L[‡%ÓHë 2”¡¨ùÁÒ*&Bi×f%+¥BÓWÊt®ùç2S„cÿÚ ŒAƒ€U ,°Âjû[…Û0޼üÁìj?ë‡Å¡jìà0K¤±(+à;ô( šð>Ôˆäð‹FK4îöž³å¯§Mvtmš²2%·ƒ¢mfF9Ýlp—úH Qü¸“‹>N)ºËÙqâ»öý™ ‰Á>Ðip× ¹_g2a_:ŸZÞljˆ»|âí œœ<õOkØðËÜ—QWçEDXÊØÂ×^èç#×É_ʸ¡­½åDDN]‘—qÕVXààò³t·Ó-ÙÌòP¹ ÔD‰ÍlÄ 8æÅëUs¦¡~鑽¸ýãøúªØt›¢]4I½n¨v±9eT¾Æ>5ý8MˆaÏŠÃ’/E )x2N8MÇa¸Ôü[a,÷}«àáÛF2ÜJxó”þÔÀRm>y¿Ääo˜}fÝ÷¯;:nP/ô·¿èïU|ú®ž}ÿdÙ¯ ð é.+çÔß´SH±Y à=L;Z%>/·eßJ›G(@œµ'!±ƒéV]¹ý ì)/ÙÂ00õ²Un3«"ó·öñëPxæÕÎýÂl.3$yÞ‰•‚g°¥ ¯ ŠÀç]d&®U ò4]˜Ð‚J¨+/oAÇM·q9E-o|¹\OcÞÆßàárŸ¢Ž/4 åá× °?@ÇX` à ªÚƒš7I³T)„}®/êü°³ÝL!òàä­WF×1Š|ò­ÊrÉL°»ø¢ÅÜMf:ØL¦«ZƒÞJñâO‘Ö¹4”ôTÓУr9!¦ø1ymeæ¢î|&ê†Mûm•ÆÄµsb×g=ßjT×LåêœeœÅuó+ž¼ª]«è’f®Ú@“µ«ÄbÊ,´¶çxmQ_üšW;õ^‰=I8Yg+ÙÕe6±a\†šeCÚø·®;%4V@õÝŒ‹¸4/в­†G™>­U“{mʬ+ã'tCS]ü÷¿`&Ú¤µö&·©‰Ymªª5±zlŸãÚ“§Ló‰(Uù)!`NÉ)½¥Bo/1™…Å«{šùE×ÃUÎý¹Ý“ìS¥8ï¦]ûòü⃠ö>Ê=y–Igévdck¨$ë ßÃßåƒáEHìë ñÛ,^Õ†§ŸJ¡4¿àQp»ÔK/è¶Wtë|Ñ&àŠjOiÒ(I¢ýÁÅû¥ÞþÇ[%yhBï›VVŸ{óÓàö©ŽÒè ßü·îQ[ã>8-âîEBx¸×L¢R 87ÜãÎf#ò•§â$…d‰²¤É ßxîqĵmRj~üº7l~IÛß…tceoë¼Tü8hµó˜C[¿ÚŒu°8ž¨Þë„Ùd¤£%‹0_„Át€XUçÖ~c 0Pé8Ç‘^Ͼf½#êánA˜8°ž8êJMàë©:‘öê²èå ¨¢yø6¯æŒP-ú€¡3óÏN ’¼6#ã–ÖÞS¸Õk7=eRã’SPQ²¥½ñ|ÿ %ÝŠál;Rïu3>€Ú˜¹ÄBÆp/——ø|ÿ£jYñÏ;‰OT]Ä៮èö› T!S`5ç·÷ìä#ÃʺUHÊ ø-—£¸h¬ê)•®#Û“.Î3l?s/¤!M.*¢îé2,ª‘ßRO,nÁ_«zºQ ',šp–IA&ÐyA5¶o&§%M‹`õ“¸4­\ »°Ç׈Z2òÇÇ7õÖì¹sF–(Ô¨äÿÓz§ðÖ®õ¹Ž->M¿¸­“Ö¥°ÊC9ZÓÎä"Çô"›@‘qÈDú}Ík##{=8² Y]öçqúØßAÖnQcÜ3ÚD3?‰ÁØZ÷Ãjhdfžöv»|úQ)®á‡'ëlŸH„<'!h„°öÖøV–ˆéVŸðJŸü£ûI¢ûÓ‘ÿ°æv}%iéÞµwÈd—\IžµF–Jñ3שÔÃÖä­%³)L…-ØW<Ž%Ê|¾c½ §åfDëQœQÉ·Aíöa€¹wä•2Æ8â~;õ‡ÌDOC"ïó¹lùpz –ðÞ Ï·V[^ÛC ¦ :¿mLmÃÑÒ©l¨gL+”,íGÔ ósó°û3Pè_ [8Ô› üc‡&%š\ÊÃþ¾¿±"wnÂWwàìâ}y€<¤-õùÈÍ%[.G|Öò-ò.3T¦Žáƒ)Vy´Ó‰dFö #ÎKgàw6z1÷*µ²0¯ñ>¿Òµ8£5¶äÁQB4æ Ô"ŸG©5´äâ[[?’mÔ€ ¶:tóÔ/u"sUŠëáäLmØÚ/Gæú™/7 ™ÆQð¢Î{·¢§ÝúçÚÌÆÀ¡yþBMJ‡KY’õù^8yb«¥ëX6Bvˆ,šì¼½`½áíK±½iÓ` w»«AmhSDôaœÿ²qCy&šOˆèû¦©O˜|†ç× '¤3žéçjóµ ˺ç&Æ y9Ûœrb¶yY·‚€–avö™î¾‹«ÿlÿªgµ*ЉÛðû ÝÍßÍæ‡ r$,ô¸¤š¸ä‡©ð/qXc´+'Ÿ÷—Î êžn?¼öÄäð:TDEar@0ë»=ü7Å_·Î±w‰DW©áANgÕn-6 ¤o°#"àßoGå…‰gÆlqhðz%ï¤Óº'é7®6Á«fŸgxÐ8ç±§/€àÐGðÔ%ãJc´ZX(Ó'óíÛ¶Øp…AþWíæ…èÕâbˆ)dÞÆ8~±O4kO=«Ð¤Øü;ô†Ï7,Ã]¼ebv@…òÉûoÖ ºQƒÏè3£€_€{÷ò€h)ÙK(âfTD{SC6ÆùôD «4º-Ob:¸6äñ|sQ<):4 [«¯ ¢¨‚#£Åmí–Ò¯#ù67 :Pqœ³A‹Ì ¶ëúë«\±êrPVò¼[†tD§à`ãC×ϼҦªÜË0«¬².Ùv«MÒSÒû)Ú]ûm gÁ˜³•åwäjqnIF à·&´&ÆáĽ¼§= —óùEÖ³„ÒfX0ª½k·Kå„“ö9²fT⫇>“Yûjö§Å ÓxÚkl¹]ÙY2 ’µolãÔv€+ŒÞfžƒ£ tAhÔ‰žfªÜF4>’UÙ3Gèïeˆ\›?7ëÁlÎ{ª]Ç›­gÛ4*6˜MóÓìÚlÜ Ü[¡°˜@_âñˆË6 ¬0[­* ’&쟗¨Ó+áµ; ’ETÒZ©/‡í>:u˜:AÑÏºÓØW•·°ú„ß ²ZÀ¡;±--­V̈èëë7jÌݰ[½¯`˜Cyä°\qu9˜£Ñ&x~{@:¢(Å‚ùº’Zvs§'vnäašuérß#~…vE²Sq¦uÈà߇[RòŽ™è¼!|Å>7p¼n3•vza<Š d›¸g¸§(ËÒÀÝI8»Ú4—Ø1:GÁYŠ~#Ë‘>Är¥JUœÞª´Zè>5›¶nï%vѹµr+ûéôŠDú:j¼…ÄZÓÚz*º¿ÄPY×1“4Hû‰©‡7ÉôÎx¦i}¿âªUÖ™”Ÿ…4ó¼m‚QyØn nÖïllŸC:I—sÑñCð´á‰/@µ½øÖù3µ[}§áðÆù{]Â9òàb&½}„¢@LJ‹¯26/2Â|¶ö|XYÔ Ù„ký-OLÞëÎlJšbM¶çކQ—=Ó<.¢·,¤=p—ËÆ©Ï1fľÒtNAƒ‹‰r—Ø”˜¦„T¬µü67gæ¶Ç 4uÜæäFÿF®©_Zž¡9Žâ#ö]I¦÷åäݬ–oAC5)È—ï 8s[ôî?~:Wë²” M¦œ$š¯0¤;NÌy¬}§¯UkZN³ñ¯ôÂðg ÍNÐß®ÒñzIm°´uRZ6Ïb¶h3j{ã_D+º¾œ£Ôíí Íá1ÿ9†žvhÅ€ØÓóžnÕѽ½Õ(@&›bs.¼óßg7ôžY”0¹$`®ëu•¡¹ikbOž>Pì{½*ôžQfÁê«Ï¾ÜÃf t©À¢¬Ú„È+clDã1æÜ—T?C¯|{x%’ìa“‡ÿZ´9eîÛ×ÜTþ¸¶ºqW“K@Üw ,}+…õ*ì 3õ.;âÃÝšò‘dÞ.g“–“÷¤}di¼XŒ¨%GoÄÂσïINíê¶ä¡“ hñ³ýY5@þ¸ãîÀÁiÍÖuh,Ô)C0YVÑçvÊÔÕÏÄ\ ™”ç/»^ÊÛä·IÓª0²ûƒ äÇ„ºäÿ³—Æ–°ÑÝÛ’€Š²Öª.ÚCwU&ïªÚ{°ŽOݪ ͰÅ|,§¨ãÈ@è˜èÉŠPÇÍ4 ¶uyåšN<ý"Wæ_À?°`µdÛ¤Ÿg8HÑ16#•füŒŽ%_½@µË|Röžý@)Ç@3Æ·×Íó~­$&°À‚:ì7ƒ‚(ÍôÌC凄WQœ4‘s#ÏÚØì¹²›Ö $1^ú`@Ü®Ýãˆðc ¶ùÅÊ/)z@«X˜gŽ\Iåüõèm· !ú9©L·˜EÚ‚º¶^ 1ÑDCZï|<7#3«Í2H%åw¾Wþ~G__J~7Ê÷¶¶ùƒá$Ä[½šôl½rÅ?lޏEöeØØ¿h¸êõŒA3ì’{cÝz޳Ýé\>~QËî*p¬,LÚ6 Fñ=¿p®(ìåÁ¿Ê ¹š½Z«ê ¡ß~8‰xIt¤Ûe~8ý–á<Ìd9ú¹{àq Új…ÓVi|# öøöÔŠsF\=:×ᬩ?Òi7TJ®¨Ö ›©=ßL<±óö‡eù&U :èZÇ9ä;ÑÎ8hItÊ´oánÊÝ'í-í®Ýë¯ìψ”ÆÊBYvÛK-†{5³˜h#Œݹ:²w÷#ªœ9èË“òã¥WEs”sx줛_? ï_rçuÙyð5@R±6ÇôôêœÂËËmQ“ÄÈ,c¹׳êÌ¿]{ŽâihÑV…¹u,ײåŠe•©EúŠTjÉW)GhgœÏ(ðå˜ÙÝÒ¨²i‰è$ÑôT7öx㜻ñsÜ¥¿~Q9mψ,?¨_¡:|2aWßcq yþ¥jîÕè Cj }®†­??&Ι³ÿ.Xc…å(Fµñt¼‹Lßy@z3޳ ÌÁÉøåYž²ÝiHñVtÆžÈ^U Ç+Û1/Cpü‚9În<> ¹~ïûùïÁßKSreÒ‰ˆ[÷ï5Á•u;'îír¤žÅVzì0Ps˯‘eàF)Wr¢kvzêX¼(+›]¨H æ^!eùòwS•cèj—ê´Fí&4X4Ï_«t ‹H³ù±ÏÍf pFlCñL—ú'?®Y×_“Qæ7Ïž45»³ƒÆü…èì°Êjf3cíZ/â7æñÔG.XG‚éX:¾3gŒ%?ò½ëÌKù¼o]’H‘®{Üé>`|¶­è_xôÄ HFdšB”k@sígXÜš¨ü$̈…ãuÍoAIÍzç³Ü>#¬g@o™8à*ÃúÅ,¿Ê-šÓú >î3Õ5SK¥Þ01> êÀ•7¢ÏÜê q³¤HmþBÖÐå˜ê’eÎ7»Üep;£.nxùa ïÔ Æ‹e&Hž”³sÅÕÅd8Ì<·õ [ŽA(h(d†ï¹ÍôNwŠÏc>ººÃb䎛Ý&ŽJeî·Ü„èC@KoQЗôþ…¸üô²–D…¡a¨&.ßùB=øÀA§H_À» ÓuþW»Úzúfx“,}ñ·°UÚßWUy°ôZ#=›lÙ:þBë¶¢Á™5Ž£/¯šWÎu¸ÚaX$”¾Èv±øIïh³˜Í]!6]~ó²›D\^´]*µØð¤Äûn²ßszFlQ¯VZëãÄ"ٌ§ís?ØRÆ…uô.x¤›8Î;ÊH)ý®üT´-Y¤ƒñvC°mµ)ýª%dï“ ÂŠ5Òõ‚¶—¯þ,‰yÆÖ×£›ùMÞJÇvþ ð¥5ª¼£+I¤ò Þ7-ð«ò>§MÜ…?’)ÛcêÔ£NVC»·‡ýŒ?Åâ_5Üÿ¨ôàE^ö 6vÍ|áM.Óc'ú•ï@ >`h k›TúÙ»d`‰ôèœMÞ¹UC€jDâêyŸùëtjX%]™LÊÞÝìnG¤ÙvëÐÊ’kÈΡHµ*B€­fO˜¹lzUÇa» _ž"HRô…WŠpm‘Óuv‹â²ýêçÝÅtGí‘æ# ¤Il,n–["¡5Qú<¥aQ#R¢T‡â61o™+Ô x’Þ§YŠ`ªéïœðÚ5ß>¶iŒPBþ°*†oú¦¸¹Ÿ1¦Àèlð¬ëúnp:}Ø3¢´¼‘ÓúC© (ëÀ#­³¨† ?’uø‡MÔûºMÑþ‡ï¸Œ•hÓQ/; G¡ -u^}ó×½ìýh~1Éð[unbÊ ÎfKçm…MÅ»t͉Ôw ¼B'â'.Ô“œ[$#«i]]2E¶à²ÿ\ ô-ߢ<\umÇÅŸºYð•Gß<š#ˆjJ…‰Ô —®€•gøÍ/-ˆ!_d ŠÒ˜Ï¸@eéY` ò_9ù7ÃΚ„ŒYä@Vzóü<~:ljàAÃ;û‡î#†Ž¿Î½áb¼½¹„IC‘²Ãù¥– | .hÓ–?åùaÿ6ã@Œ9¦Y>îÿÃÓ4KÇÅ÷{D{ÛeÞŽŸÿÚÓmàO’„sr@ü±çUø¥¾™ ~öˆDû-;‰aäjoÈU×ß‹à’ ÿÜïs˜‹/^™4¸çûùÐ…^/¦º?ïM qF!‘iÿNgìd,³AÊ Á“ ȯós :x”¬APv£¿–Òà†7K¡a$iJ•g?R¸9 ÿ`’M_÷—¬¡} îa)ÎÛÖ¢.t"oº½_M–¸ê0Ù„¤J…d 6—½ØêÏZ‰o£Ü³DÚA‹ryär"ÙÐ+Às¼$f"°í·#ÞeKKs árŠ^ÑŸ²½þÕákÃÇãauå}Ÿ |nÑ-BãN%-Í¡Ž[ͯ½R›æo§ˆæO´ÚoË~‹¶êFápw TÿUì¯Ôí¼Ë¸èµa`*ùù‹lìãûÕ5)ˆP.óðÜ)ƒŒÇHÈ+c?éwåôHOǯ¶2·æé|ÞUètÄ;È^ÙÁ î}ÞPôë#ÏlUØÿéyÿ`Eý©a_vØ£è{ÄWsF$úH˜0é;­ðqžÒ!yN 6¶?  õkþG9w-×xñÊ{²Ÿsѯ°ûÌÆãh¶a·'ñPWI9ì[.ó q·Ĉ¡€ßÝÚ‡'ÉäÃ}r•q+W4{»\{zns›‰fEÖ]Î×ÞLyc¦¾ÖYt3‘#+ ú£¢†i9MMÞôž}w‰Ý[3²xɤµ¤8ÁcÝõk+ui“$K¦§{tÜô`‘Àr}³ÈÀð0/mÏiì8ƒž!®¾Ù¢,þاOFµX˜g'Tp íÇǪw’Mmn½P̘?@Òx›ÊÎk’Kqj˜@âeÎk½«¤LޫųhôocPI“l é¶ÒÔ`&¾ž‘²žK\Ôùa\š«œXF „L-ÃË…«“¢w†=Æ´V&{rʭ؆‡×8Ï™s’¡¶„$ÕšY1Tkê˜õ5Æ&;ÿ†y@éßD÷>5Êún é…|ñ%à[EóÓÝÀ‹ãß7a™ÂZÜŽØ•Q¯FJNþyKIœW´ÂypX­…œ-EAű¦¤Ò¡Ðjš™ìà*è*ìh%‚ÆÅBÎ¥5LÇ"½ž0»ÞÂ:tål·h$\A%„“IY‚ÉÍÙ›ÉÅ?ª¿‘Û+A·Žy‚˜Nò$~‹$E•¼UO¨òn»¤«bxš1ü- :—_y"£FW““›ŠÙ¸CY}æÍÜK¡) ÛÄ'Jõà= u²#¾•£Wð«KdÖ—»™îTè¿ztì1…{1¹5 çj2IÇiÿaF H¡'Þ+E\Ë~dSÑùI!Û;¡°íš6‡6˜—7oX?h”-v¾Qå“<×h‹ã·GÉ,zVPÅk„ÊôMæ Ð{ì­Kô¾J:¡ß—ª—@Èjþ‚e'ï–@Êbá=l¿Dú¬ÙÕžO¡¸—{ž¿qïykðtï‡Ö°1>»×1”~§ÚömÌNžµy1ÙÏH»¦¿“`KÞT8¦¹ï.ƒ~«Ò˯5 u=ð.DäÔ©W#äʽülBé†$"®ùb!½ÎƒB¼ s›LŸB®Œ÷,Û 0æ oÖÊV¿kêÏÑ×õd¢Ysêy^ÅLž/!®œ:1Òÿºä¯ŒÁöA,,Ïbp:(ÃFåé·ÞÅȃ.ÉýIX39¿©Üf½ÞhT¾yÑi4AÆí›:‘ŽÁ™ï d_VþÐ'¸kEy\8„Ú>[ãë/ê>ÈoEn®*=ˆ~õÃ~¼ëÊ,j“,ø7• ë¨IÑ¢tZ–ÇÄ5еI ñÂoÀ¼•ôðgáçŠÝEö§óY /ŒËÁ¸©ìæ)ª­ô ¶‘Ž¡ôŸ7±!ïè8IÊ1Cå£Y´2³ý!Øë¸ÞßÑnü´³¦3Í"Y°@Ò+9Hßhòï× ©Ð¢ªË‚o_NWõ׋ó*­Àõ›`¤Ð§-Í™W¼?õi;˜Í$†iœàÇ;I BbÍì7[ßÁm¡Ø¬r¥ÔŽt %"~•ÿs'wºpÍ”v|Ú9þÀLŸvNU°÷bÕhNÇw%Ô+G Õl)›38rˆ®*mÌöÃ@Òe®ÝvJ;׺si[“Þš¯ì„ $¥XWêëÿ‚íÁï{Ýfu;ß‹O0Œ [-s?d ÙÔªÕ•É¡ç&ëËÈU±8>õT»²ëÑR½‘¨îªL¦ëÇh™9ÌQ|-Ø*ÿi'_~ÌntA/{•`®ÊQ_ë‡#S.ü×B.¡Ò:§Ü–‚·é8ÿ3?JìaƒÎËÞ‰ø~È´íàïgB»éÒµ+ñJ˜£Ý÷J¸"G-_ò9rÊ›Jééw«Ð6¸G ‚„¨={«áª º„ÖNCüDè<üÓ‡ä´À¢QDÌ$묕†p4©$£³yÓqÅð#gÑ? -œùò Û€ˆ8\¦­¬wZ܆ HIR›9çT‡ïsÙ| ‰¡òíP¾ã.¬¦¨Á€§Þ®»î¨R¤R ïnèüvÙ[Þ×/‘饨SGJ5JÁêà*d: dUYúg—<×?Ðó U1¢•¾}“Å2Ç9†ÎÏu(“Ö·Ö”)†ñ:d¼@¨(s¨Ùýc>µ‹à]Çw«u}šÕˆKí—ùwÇ]¹èî¹ûK⽑^‡itzj¹-¡°-+e¡o¿²Öj^»<×{BhìÂkæ×~u>ó˜ë…»zÌhžªvMm@vVË8á9jÇ„Ýa]¼]‡s0O&HÙ«b$ì&ùÐÉ—í’Ç'vã+…/h‡±r$ÈpWOrõ»k´o\ejbç'ÇÉœ·„ nðo¶} `*Ø)º¼ÉKÄÞ6z”ŸjÔúÔ°Õ?´º¨9Æ5åFs±B#~ÖÇï¤ÝPåtvmáí4.Ä[ņ؉9¼¾-tK8D,ªË¶ ¡7`.­³=ðG2rma!S`—cª)JÂCÿÅ1û©ØK²dÀè>d€K¬öOp¤}ª ŒR%@ÌØåX°Î‡TÍa¢8O,– Ɖ›•Ÿ‚bÛÄš“­½eä\9+“üÌ™ÑàÆB‘L_yû,ŽÁpøî©…£‹<ê^WpÖ¬Þ¼¶²2–‚P{xtöïÓIºï3ÓÉ/¿#T?JÛ3´4nec¥S-„65t©`Ýl¬ÏGÈ4ldÆ,P5úJmÏŽ™°! úöTáã­Ï3×¢:ó G÷ÜÐ&Ú&ru¸-Ï9bÆÖ?¹ÛÂ×°Ê»ÔSØ[6|œÕQrPdë÷¼ß+-,ßóvìTªsQƒ?DÆiñ§¤Hàè<Û\·c,ìŠnj ­woføWLŠ9®™E×/FÒ¥£(o´Òza(mŒàß'ÊG4½äÝ`^»ú¾øÐ€»ÿäå'NÍÆÆ±?Ç·2Lö{÷ƒ:÷I°[÷a´pøhÑ9POŒš7NѨü4¾lò'€œAdÿK‹ ûd*|ßH}Tâ?ÃQ‹‚^âRv¡][oY|üum·ñ^ÇÈ@À?‡_V‹€zÔëÚbô”%“¾-×B§¿Ò çw;G"®TЧ–Í~ݨ´Ò†s‹ûbÒF.Gã~â£aÏ‚SUß8ò‚GP¼Ypì![¯&È f2º1š9w~*ÐAZ”߬c°»Ž îÁÄœõ"}ÿ©Î© Š0˱¿k›‹™ûÝ.ˆE­Ü€„A¦¶Û¶¶÷A:Ó¿·PèÅ]÷ùÖ÷ŽÈz)£‡rÓ&xçm;Å1a¾äðެäëœ p8ÌkÖ1ùEšèÂÏ,K–ðÉŇÓ÷9°µ·ŽsÿoR‡! u™rÇT›Ô¼†ˆÿl¦Y+–ÚGëpuì_3’}JñîΑ0 [ܪ8“v0òÐu£µöÃþw«^y°ot4f¥ã{»á~ÅÑ©­žøN¨ñƒ©‘\øì*ÀAØžK”oe<êžÂ;ž1û7ÝUNΞg™ßaÌÒl¨àÕaÚÓ†ŠéÿÖë,ÊÞÊÛèP€w'í)⟻8`s™Ú‰-óN .×ñïÙµOóúñy®&l¢ W6ìÒAz¤é¬DfÖü0âk3T·§°cÙ@|¾Š3ùÍ¡æÇ¿ìÜø*4äïôñ®;§¨R¤*t°PnÐG-„Œ—Yb»©:}³sÉ(WÜŠ½ÜÊ¥öŽÇ¦ØÎeïQãl•Q3¯|tÜ?¦Ç3„µ„"»T"ÃÂ$yL0B7ÕåI"±û¦q…œòܸ۱¨S‘ÀÀ1`ˆcöB§Ó!*]ñäy¤´ žÇ±vrïÆ £Q²Å V»ì…‰ÆD›ogHã^PA»Š.5¼Dä$‡ä¬ú‡°"ì5{*ð¬§9жUQSUEúýÞÚmÑ4¯_ë¯4sФú-XL¬W©ýÕ3†µ'îP3¢ î}ê!Øxí§-[øö¨%PYj×qfß^!"P+×¾“m¥µÃóÖŽÌ­ˆ Xõ‹jÓØFM »óÈ)½ŠÑ‹óŒê ˜‹¦@j6ÇÅÕï—è–xïOTQcŒÝ ÕðÄØÚg"‘‹p²‚¹1º¶ý‘XÉ~>ž€$Å‘{ß ßr¢p*òÙ’__xƨÈôvb+Nˆ¨¸ð –ª˜:è£gaoçßôg´NjϨ,Gäɸ2jMhI#UhTþEÐsmNÿaîØ´zCÑîË–´4z¹ÓGÊ`å÷lºà6 Å`Ù]“’RíÒÃ÷!}%RT|ó=svy-áÆ Ç†ç§XÁõ4Ì6çIwÒøÎÿ%nµÊÝ$SŸÓÄT®MΓ›·¥DD€°}Ö»òñ_Ľ„ ¡cB ã:hÿj¡¹ËMc”=ÿˆ‹¹šêíï™;ãê—[Ê.? ä²”å›ìx|„¹VO­D<‹ߣ@(;¹|I0à—ͰmSœé»Rû, $YÂ<¦ WO}AÚøn¤lPènì C!í«d'z ˜Ã%àIs½%ÜIØæC°QMþ’~ è¥Hú4ÞÅ(q)Oy“Ó;˼"ÿñ=,îßQ—‹ý7ò:4½þÀFÉ,fDzÌ%‘cl÷ECÄÒ`Ÿ$òÍ­jÉÏeó| Q™–ä“ù"çÄÛ¾–x•FD”í³å¶¾Ée§¢±£zˆó%ïßÊÈÎxoû±w)’Awé¡ìöˆ™/[MYŠªB?ð gT’‹Šô–ÑwwÔ PÆ=¤|Ã.,þÓ¹©ÈWóÖ‹ýy'Óõø¸úÉ#vÙq„ÛÁ× ºyZÙÚ°Ù™¦ðÅ·§ÕßEWÅðy{×€.t‚ýu¸ÃûŒ]5U¸qà—©¹M?k'(.Á„I^Ö§e”w^›ŠÄd}QæoÖSìÆøœL$ $Š?ðNüåVâFd¸s-R"ZËÔb¥:vè¹EvsäE… ᫆¤¾8$/KÐ7o‡…. c•ç‡2”¬M eоfÕ™ÓÚ?Ýð‹jÑÿÍð‡ žñ™NŸ]ŠÁ;jfPGØòZzŸVöû•bexhS,U‚†æn‚²Ÿ«6½;=’;J¯äj׊x7ü¾R&äaÕLÍ’urªÉW š|„Ë šœhYÚà2& 0ö>÷hEÊ4å1ð©ÿ/<@ÿ¿Œ™ÞÄ6g‚ÛS\³FðËšŒ5²–Rçc0v(~ѽŽä@Xo,'óÁ|®ž±;Õ!©$Å.Øùåá"±YúrG¯Aš#xê ¥rs–7*}m}S= 9ÎÉ¢|k¬ÉKøë¦ ¡ƒ°œ£ÊA¦Â`n )óìa*üÙå0&€ÑüŽ¢mƒU1ñWéÔS É|¬)ÐÞ;¸y—³×¢"°°úé~b3üG×±”ærÌQcÄ.눣³£ù¥Ÿ‚Ìrž']µ6¸Í•ÏÙ‚h„®kž—ttz ð9§|zÈ4¹Ò:÷–A%PRMô‹¬Š$ãÇœFJ%?ã2£Áƒ™û¬ôâ Æo,Ÿ‰•zb‹5pÇsë|{tP(‚Zú°[ ‰¥˜Îë4¡ô ÌaàRp¾'žPí‘ó ê¸ ðSµ6i3nïE ]ƒUòÅ,¤fÍ BOÀN=­n¦kóóª¼Æ<Õ¤yzv˜¤†êõ"E –Ö-' W²;«óz€‚¿›¸¼‚±ŸÙ„Ù¬#¬sŠðæ$€†S4ðçu­³aã0Û—W¶rá"ªg.¤‰éóu>•cÖuºçy |§F€B ¾ór3š^5Jîþ v܉bßû´C5¥¿ÄKè¨ë}  ø[1q¦›-Bÿ\ø¨‰¨1àÿRÚôO%÷àFưk±°ww†RÜö…RSšAˆO†Û‘.W×Äàm}È¡Îe¶(9œ²õÝ”¥¡TŒVS£>A+SØ2Nm³ñ]ø>…ËL_-߈åtŸ>ú(ù">1Œ ƒ‹/’÷ò%Nd›{xu~”mö]ðLGç;×õÌWrÁ%?gïT5‡­Ùò{a5Æ4Éwaäƒ@wæY¶àùÓzøU`ÏNª@ ‚ ‡·/3~²ú;Œ?o!ÂË” ©úX’CÓ'•Z®ÚÙ×κ9›¡Qø¯c»8 ¼õåœ+¬^¸¤SÈë¨jæÈFÒù(‡Š”¼œuÙQËI´¤ÈÔq»s&˜L~ ­ÏDªËõ˜:TÙy› )Œ$!fÄG¸.uRJa]! 3–[L÷g `Îæ¯äÑ(¤áU `|ª€ “Åf‹áÕýT¹Ì].¦ÂÙ(5ä¾>ÕÄ„¹Hª³Ê9oLºµ¶Ò”=zQ­ß}ɾÇîO•+Ë ›- ±­Vº™™K­&ëÚš¢bklØÆ·Ê}Ény…פœ~éÐ5»Ý?ø›Û-YÙŸÔEÉg¯11•£Ï›½âB#8w?\´»÷û7#;Ó˜£ŸÍûˈ¬°€ÍáêÛ€ñQã±úçhGɘzûMôe4Eô} ä~Ä(\[ø(ú;¢ã£>¾´„ôþ¦ªÙ'Ac¦¯ñy& i™ÛrR"±¼Ñî0öÒâU™µ6ܱ ç°ÂƒòT<·iW&tG~ ±Ñì_Xæ¨ê‘à^Ú0ðönÖó§¾qCÍަd›ÏÙÛ" ^Œ&LͰÞжæ)¬L@C(ið¾C jßp¸õnÕ(Wú Œ¢¿ž ˜œJTÇI‹ï[b{ð´-]Ì?Ž®Í92•gi6|r @CÖKhª™b‚îâ–ÇôA¢&íY±f᪙m$¬¦^΀¶té}o~9*Ö¿Y:´óŠ; l Ì1(ØG„Xs½´)0C¢)RÚ·˜5-rÎþøÿà,¨ö Ó¨«#Ša‚ùøìŒÊú× |ä~ósÀÅ]{®›!‡÷¨ÀçÌ‚Þ_&×6=•=ᇥp˜ ã{¾­n€$aRõ̆[)Q+–zD}˜ùqôÄÂŒ)yÐ|°æì@)ÜdªãÛW6!&ΠΣêDNA SéåñÙÕ"ªæú4ƒËÕECÍÒ?9Ôáæhe¬¦$$9Í›M>ÑýµÀu+^dâäƶM#ÈäZñ´X–ì‡|-‡øOJ…Ф±ÑSe Fid˜ˆl.°Ií×ó¶võ#Eúnù«JA€fàŠSóªû_»uÑ,úÇÙÝ«ºÆD”JDioiÑ0ã'šäWÜ™eâ}ΊG–m«]èáÒf·üÖîòØ À)ú`Ý2€„úø®;kÐõp…[üU~L}I1ºm.J«¥'àИ3L£ŒšxíÝÀ†Í»¸å¬t a@ö}t–T¯éMÚžÓ¥\MY4ìÂrwûÖ!««|–™ÁˆÉ{ÄmѪÖO ô,%`{U¸…DEZ¨r’27%QoqÏË\r´ïß(¾â˜"Í»êL¯†':‚ÓðPáBáึ(ãO(ô=W÷IFÛ³âÏþÑ;u„WTh¡M s¹ÚC:اÿãƒHÎm•ö%ÈÈr‡ŒFØ| éÍ=,B"Ô[µM>¿ë0å<úgµ*%’\ïÑy_xß;«Ú+êæ²Á™WQÃ4úÞË¥˜U¾½×`4‡Eqö‚æxPYË@F­ ¬˜Õ¡åfŠ/ÇüÊ;‘XuüÐÕ)¦5ÖL³C‡Í”}<Ÿ=2Äë@¦0Ãáø½Š¢ 5ÛÃݲiÂbp$L"L:“ºCP1lfK,ç; ê×Ó›ÄnðLƒp>‹tæ #,A *}àZþ”ÚÚ‹æ ¸~ïŒeÕ‹Foö—ÖFf ?4(NcB² %¦¥V«LŒB¡ÿg]•Zu/$yÎ>פøDˆÓŸb.å:Tà -N`ƒ'ÔS8¨ÖpvBÇn²\kcsôö{ÖûÁ¨m8ûÑD…ǘJw÷ ø\õÑÒ;òSREay¤½X‰AàçÓfPüài‹°¼ø1Ó`o>s í ‡¨Ó(,Ì_õøðÜqÄà÷Ü[ŠŽ·%à›i*Úæy? ÖÜÙ/^ÃqŸ a·át—wf‘ ] ¯@&NU5è÷·{‰»Ýì^MÇýT¸; ô_§å–F“SA½8r±ÆK‚G,ÉVÕ­k]ù@ˆOetÐ0N[Ì(‡ZŠé~óþ4Ž.6úFª\½=Ø’î‹V9”¡OPúÔ`NFÂq ¼ÿ%ãÑzœ v—ã±D§ 9±ûéÌ©¨Øc,òÂ…Õ«M -Î{‰¯IÑ¢(uËz¦ØÀ;TN‡+¨­äaÉëÿ‡FA»{žhÑ:ÜAâàŽy°Ü0Õ—eJG¦Kj3;™”Ô+v~D¡§~RAÜZ”d‡+“ß1‡Æ'E ´lM,ÐB„µç7G4DNí9‡GÚé(¸±ó š³hLçØ'¦£ßIçaôÅž>w¡¸¦ 5›ðvèŠmØ:YlTŸ¯Òâ]ÄNÄX¯^&¤Å;~¯œÇ_‡­‹Ž^˜H<®~†…8×EˆÈpxÅ‚7ì—ÛþŠ«„ˆ„ËÚ\‡kC€ï³W4!x!xyþöZ!—œ¯ø0gYØ´uÃß×PtÁ…ä"-ëxÊ.{eã×¼xŽ«Ò§EQżÒÐv•vsȳŒSËB^ä&áÙˆÄrno»˜Ž¢‹ÚÊQV92©Ë&ê®{•4âÒPƒ7ò¿¥tPJOzyF~—QàRXbˆ²à•Ä ÎwMRêßÛóð”×Ñ_=½¯?ÝñÔJQÔpìg#ø+Àèt½\_0ö^kg_ú³Íñã¿j"n>ÏÒµ…9_¿»åˆbß.RáH7”ÄLeH `yñ¹˜¿1w-µË¸Êö“"SsØ‘ášOæ,ø¤0•a'J2š;ë¨õ‰¿öËÅ¥Ö-,à8Ó¶Kï– A…yoPlšÛôw8Çl0ìÊa BMjÕíF¾Ñ2 f\H@µi/škÐù£e¯%¥Áë&±; ®¾# ÈÛuìÖˆ!›}³¬ôå]PBäsDe¦ø»f÷0ò‚Aó™Sê<¤¯“9[¾ŸZ2!~ÔÌáÅ õ"»'IuîØ1 ö¦v(m°sŸ]žú¨=8ÍìmöìQ3²¼n\ˆ`_«Ïë„-áÒ|È‘8wJE³Â±t¤$™Y«9Ö7*ÝÒ) ÿp¥ü<Ê¿¡f8G÷>>ø55aà7lÔóŽRZîœü¨bÈÌ ÅH¨F’‹Pä¡¥¯§Ê·ÝT Yê©0¿ÿiÍg)MG Û }ÿ(zìL•õNλïE®hà{ æÉfo²5Z—ê¯]wG‡ÕysN9Â" Vuo} yçÇ(Ø®ÓN(´úíµäK(ÓÞöóQÔ«ä +‡ŠGG±­K´:”Œbã½m<£,Ž%R«AhŽØ8ÉiÛèC_3I©n/ kœ}±٣ÕyjfN¿ aUå —j•ÆÚJ€Ê|®nFo´ÎNËÉÏÌŸFó‰FK¢QH†þðU1tÚy a/óÛA…W”Už§3Ìðùdbm£)²Ie$­7Y_Ò§†šÓRBÐó9V ¸+2äÛy(U­ð(¥|`Ìç2>p$?éËÄV9%Áæ“’’Ÿ¯l;QnÁõ_ÖTE•Ñê•)ZÌp’Aã[iXlU_Ýútj&vOÑþÏcÄòZu²d’ü\w=…%:û£Ò¥¹ D….æ¶‚~oqD½ïìzh²2½H5eÓ£èav²Ýð¤/Ép d<¦i8{„+¥ ™¾ØwÒ¢¿!¶žšÊþi¦„ôú)I¹²Ždý³xàænçVqB–œ\¹/é¡[ \5Jí÷¶. i$‹m>~ï{&Òeua¤Ú\³Ò|©¾Œ(># S¼™’êX:9¦)©MÏÚ{úGÈR£­¢Ö1¢÷MÑš–¢=a§>r©±.¾EóÀQ÷¡Q}_Ô< Å–ËÀ˜‘‘úŠËý—@•.hê̽×9kkk9•—oØ¥qå%>èÏÃõ#Ý"=Ô®bé2&Ø4yÖCR×¥s¼:X¢OÁ :Ì^½-4¤@±ÝwZ´ÝAY&ìÕ©mæ 7Ïg*©B ÿO“Žåq¹ šCa;'Xo@ ¿”Qa6:õ%¢C>í0„#Ò{Ö$`w®dÓ†%D?Ñ>x‘_ˆâ„ V‹ž* Ù÷P¹.µ´Š`ÑÙ®"{¤|¶þI±ÅÿZy}æÈE„eÍ3X»ÛµOÖ&<ºÆ—¬8tßâSfð§o¬tÂI.RO·CûaLÄŸ‚¿YþåB¦Þ4Ã>½ShÁKw½r°¥y/ PQ˜8„ãFÓÓ¨ÚG¥ãA5c¬u m-c:\«kðôÄáÚUUpymׯoO¼ÞïçuDÕ‰ÌLÂWüCwÌN4(¨tûÍŒ-Þ5Y¥ëY¦q’¼©íIÿÄ›`Z¬¡”ê\QÅì¼³§ ?¿M+—@úvwíuõŒ{“¥5¿Cú{ikl;øü‰ôåîØ #›Ñœ•4’,'†ÂùÿÒáà‰na‘ŠLJØø·ž)|ò¾®Èzû¬©Q]ɇâÝÜ@òZpÒ©= bnna„¢Ïè_†c Ò7ÑR•ÃÅo{Õ¢OY¨¼®€›–ëïßlâÅ‚;Rõ§j߸jf“Ì,¶T­ Ï«¦Ž#8õÎvü\u…~C¬ °$ÌQF)N ÇS^¥ÂñjüuöbÔGZ/ÃA£È.ãD9Ãå)¹k-å•JBÿ0ìwê£×Â>M¥Ð¥;í«ÖädWªb’X]:Š1ðUž…Ud¿.ž b ²Jå8*m%ÂJ]T²g,V‰Ãÿt5Šm!MDá…ÆKž•*ÂÖmœm^ÖbÖï³€“[,Ì£fýxù´ÓÃ}JlØ•77íüõéIñ¼Ã¥1 Æ¢zÒAºõМqÞYCœ{‹j—l3ÃC¸s—ÜZ“„˜&üLó¬ÜÊ:,°‚تE9Ø$=¿6<ûÞã8‡¢d).ß~ßwv¨Ñ+ô•?ãv kbuÛNѲq5 bëKzϲ½¨²Ý+©RM‘Ä>üùàŠñ¬¼ðîàXì[ì°J­éÂ=^Pß÷¨û,?O’â©d‚é\¹vÆãKêÂrê6)Ð 1ëHT7_h†m¥ì.Çþa+¹ø0Œ. óHÛO—øÕ&ëÜWÙ¨D)gQk}k¨”X>tÅÉ¢F#ΤMñw.*Ê»\ÞÅ‚±C sXÕñkxF‚ÀÐ*€. Dÿ®«5€óä %t™H2Er¾vQ¸ï×+H_•ÔÆQ^ÌíÄ[›¼v²v¥ö©XPhùl°¾eÞöbͱž¨îûÏ@5;pYGj¯£;¢jù;`•jêmxŸóØÓ&j-‡sî¬ïÓwm¦^ÐÏ›CVÂjS‹ða»;RO*jäÝ.›¢ˆ²Ó_îÑ¢ó%Â"WÛh@MÕ¹®¶=DA­8_ ÝJ¥Y)jÏBfÇã»Ê[“n`(«ºJDÇ›G²Ÿ¢íeØ'KTÁ}éôñQíÅwLÅ6Dwæu5]«^T±1ÖK»ª¬y%àö+½©xÆìé·Å"W7»½!†ò§ÉÈ««&+’M6­¿Ï6e–õãOYùêÓŽLÂNødW˜c™Ó Î,·þ,©!ñŠ‘vnäS€õGwpx=¬éÞÅ™"aK؉ˆ|jßÌîá8ïÂ4cüˆÑ­øa~NVÿ§ ’‘<&5„›N!BõjôGgŠEs >Ü;d8À9Ðk±zº˜õç¹ËÌ­ÿh³jÙA™bþ“\gÿžJ¤¶²…ZÒˆA²–„Ÿ;øož;Y×ç{KnÚ1¯„Áõ~P ÏŒ[5/ÔN4VÑê?ÿ¾$¨iq·RUODâ`U=ÖYx¥ë÷îfǬf羕ҹèi1º‰i‡ù|®¥·ëáÅ@iÞ§KaWñ›ü¾¢åšÕu2‘ë6’?14ÄÎÜ>Ð8?æÝók¥&RÞè"V‘  Iª?¢];pf3E&?µ›ð×g¢x9Mqacáø²3r·/¢–i­=B©8׿ùéòc D:m—|da5ƒ ‡Ò‚Ù'sŒÑd8Kôžß¾XYpRH:8_=l#Èì1îžÅv…Öp+ä#•þ5{örqEM‹¥A)õ ’‚=X×ÔPÉ’îÜ+Y§ ÇWDÚáG·`ºœƒön'tÃcRÊ$-KÆß°}‰nß]b¡¹IÄô¢ìâ ÄN¸ó¤â¾‡ÈWóúçÁ³)×U:/[Œîn3ž\2ÇZ‘NÙ3ÆgmÎuñŸv—7VPüq ë|ˆÏ“°‡‰ß–-”šÀÆrÊZcû›nŸs\º¹)~Ò1€™½ÃÒë&‚ù /8ŠibÁ!›óMãÎQ ™/0¹øèµ *Ð=Ðà…ÌFGÙ³(Fì#ûÈÉkºzI<³¥‹±=Õפ;¶b*ÖŒ„6ø•±Ô”–ˆzÇ­Ä”D€:J ®Â™Ú¹dûu1K¹×Ôªÿ³WVA÷{Ý~x·ÍÁzˆ!Œçàø)jÞ!ûß5ºè ¦¾ÈŸ*lm¯?º„Z[ˆÒÀ3[h{@0@–ˆ–?m¬TE MfÓ=x§c¶õýL›È€M@õ,ôºëÆÓ]©Íîz0·ÞÐñ…‡¡~@p°£à5‰ôKlÖfبhDù ˆË%zJùÆP6è3ÕõÐüxÉ‹Á·LB‚.tvSÐfgÚ§ªvTq’žÔãœÖ›Ý´p±Ñ7³¥LøÁÆ(â=éîþÌKÞB—µ)u*:„¹Fuiê?o@—ˆ–¸dÁ$Í£4GõÂßfÂ\UêBN:mÜÒ0æIÿÈ+;À‹¹™¼Q6¿æw®«lÇDÙ”{Ũ“bÇš&ŽiEÄ-ý‡·}œyAÌbST}©0†0ë{ÓáÀòŠÂŒÌè  ª`òkº·W«`/ûÙÉœ8ØRñðx–s›$n¶N Œ^Ñ—«ö&žá´­üöŒ{>R1xM«=C¡Š‰ŠãÓþ@SRHÄÎr¡Sp)‡âËûÏîøjí_Ðfÿ“ñôxFÌ¿!mBC’«¼•äVÀt‘§Áäo -€fIüÒ4ß/‰³¶ÀØd:Çha§@Ë'™äeR¤”¾QÆFÕÝ‘Ýèsj—ˆZC/%D¬ªì£tÅ vwªB kM¼G{Ff‰ÿ þ¸)«ìÖÀo¡­·o'þS»›Ð[«4j-e-$x°¨Çj¦É˜Érn‡ËÚfù¼büDºÇ4p²n„óV¸rKê¿vÓBcøäZêV„»`RÀ‚mW§0‹á'k“Ákÿ„oµów4Äg¿¦N“X—·‰Õ˜\ãÚšŽ¸ —6]ò^^‡^Ñe„Vjú9)Øi£úœ×ªô{x“˜ûá)!³“׈÷[ððÆ*[ÒQ$̘„NNÞÌ%Ù7ßVp exÄÑ‘£=¸ÂOû/õPJNaåû_fÛV»×Òô3þá¸ð×€ìOv@´ÿÀ5.³˜·Äoô?“¡àVÖØ ¿;A§Àž­ÄÿRBiÅkE™8~°u ji7°lÊCR£ô–ë9ïY-r÷§€•¡î>—‚¼•êh2ñ²q^ñ® ôr.…ý­ô\Òa!ç‰*qøl鉔ᴵhYÖzQ23Øm6¬M¤ÌäÊ×xãf' ÝÅ0Le·P¸½¹5Ο µ3}.PqZ!ÑÝ’ ýºÛjAi2½Æ—ªåÌó.™pÿ¡ŽÇf„Ñ%`~&Óqïni+ðf÷ëoRqx ›@w®þ¨¿ Ÿ-H6‰·\ýWSCR ì™^l,xŒ³h}cןwÏô!Móæ<ºYÁg‚3ubnÚl£ùÖL#ÞzŸ.#½2¿>eƒ Ø£düS<쨶{Far× ‰K?ãZÜö>wq£’K{¾€C¹_—w¤ðy-mo™BÐ$E°Ü´Q £5#Yª1b2 4pÐ{h¸©àߌ.ZvÒ½}XÈÂ×@>u_è–ªu";L¸F½ ¤éµk—©Ð‰ãçAnkÐ"›ý1šEþ0’æ¾$[QóçdÿûÒíž´‹ÑCBÊÁã c?½k‡Ò…)ú)¥Eg™¾ÈX<è'üÚ!A›šJö€”\1O€ô׫óosãçRyS6þ 4N˜Ãh$ žCfÙæjatëîo1̃àTJÓ!Ï/bøÅ0¾Ìú‘ÅóÅd]bË5Þo£Ú×idÛ:ƒè’Œš¼·-‘Ÿîpñ¹uˆÛ\ákÇE5—¡ò z‘1°¥mL6köž‹­N D\vûà¯*÷ÇM·š¸÷gýìïú° b ÞƒA­h‡T‰HhA)):?'åüÎu)»J'råAW,"S+õ ¢ü}KM¹¦³Ž&hÎØý_³K&÷í_)ßC¥±Ÿáù3ã.ÌØòU`ñ`t±Ü¢\à†6=>½a9Ó%”ÍY¢Ø¶¬žçÀ Ò+JÎvb³I‚L’m)ª_ÜœÄü°zÿqûÍa?û¤9(Xb—:•XjC(¶éJ±…ÓþéÈÊïßü•žçíQR{!Ü¥ºBº.:ŠwÛ¨ndÎ ²~›]°ÝÈÛaàY‚|pcÛenŠðZhÒod!ú†ä®be'—³…ÈX\r“.GÒ=„­÷æøÏB ¤çŠÑq¬RôCÇi‰ïQ wLqCëìTJ+t!r‹yP.‹ú*9LÐ̯q;\à»x牢»æÅò[fæMÿe 1?5($`äÉLÏ…V›å„Ö8ïQOÚè±±èwWœÔ{‡…¹‘dH ïFX ŽÛ”N'ôé)5î~*Uû¯ù’÷ž÷²Bh¿“²K¦Æ5-~©³7ÐŽÛÇ_ìP­HA®eQvASæàŒ„¢&O* "%ú9¬3ƒ»QìÐvíÆz€Ù®U—[`vâEðŽÃdEØð]Ñ7H™Zö_ÿçU—醃戢@SÇišáaÜÚƒZ*­ÍôÙ¨'P•Š9®hPXÎtø‘bn™I{È-bÒ5IMØ?‡äj7V‚>#CMG®O5¢Þè1F€_µâŠùâßxI~ö¤[­fß{)ä•€1Í–VC#ÇZˆ¹Žs3öøÞŽu&û_Œÿ®KÌþ2å2ñ"z Og³QŽ q‰NÛh‹ù—Q#ËáÞ•ê ˆ'zi¥×iLD±Zäµ’Ï:j¶éi=€Ù±¸›Ó\؈·d’†^~šÐè&oNÖRQ‡ÿîÃdB/Wbþ© Œy]D¦Î "b:¨¯ZË#ó5ç6Npml5}ýð‚Óëã°P?H·J­ÝSX±íXÖå}~nc‰È2k£.¢·.$š²Ü1ç:#ºå–6ñc}Áù÷wÇUç%ßN†À¬¸ûÚ?¨p„ýqSlí% 1… ›AI¤ÿPnàÖµpeW¸1àœh{«ÎxMʰĦ,óò¿‹%è)øáµ¡ôóëV¿ŽG1–wè0de}Â"ô¸Ÿ^Íoù,“ÑbGÎq®çÈ´dw"ÿ§¸ìÉ´ü Ã-ë`^h_nçÕJÐdBNyâ¡ì-B±Ò“ã–î „2¥ Ü2å)š§‘ ¥Ò zô+K.a}€¥ªK(º„Ðårìÿ4ý¶rc²[Š Jò'/³ˆÀÁÁÂ:Š‚o³5;öl\©Šþp}Á¸„a¯t@¨›æÙîH³b–ƒb¯ZÕùø{³y!A.N1{ O¡ø¡—1R2Ü]MÒ££±l‡gú3âò'Ì·ç_¾˜Qâ2$&ºãZéL¨sVÅÏ!}Þ­ðbS†èOÊ!x!xyþ¯u3´¥ÛX#zåNioÎPIŸ»OA_2ÆË¬¡àìàŽo56¤ËÓj·çÖ\ž~h(Ç ^óGì*C?Mî×àiV8øÃëƒCm]GÝeXÂ_¡¬®upqÌ–‰idrPË*ëþv?SHP­{ìÆœˆÃjåÞMÀ1ÇK#¿À¾ bÜ ô 6¹†ÐoujjÓîQs¹J!e2¨# fÞ¯ÅÝðÇ»ÊnÚN #–ѷ×X9Uw¥Ø4΀Œu×AØ4±„ÈeÃØ¦\xs©û`—wQRjs„ÁSÙKj’²eŽàë}©Î±_y3„yÈzJ+çeQŽ’ÅÍ%}4ïÕñVE0¯yç dzïÐáûÏ !6™Hžgªë9Y‹q‹ªØf0;VêVÁx,%µËX^Á*3k÷K³æU•žo…žcìrêÅéIfû,há`´oçÚ`~¶ãqâ¢=.¤7T;Ým£àÚµòÌÝè]åög§F'ÛÈÉ2J2\ÌRÂÎÓ f½p¿Y†œÉ¢Õu{j¼%–¹I¾uÃæ£8íñ±<ï?â -ç)ßV̵Ï%véqXJ—'¸N®nÙÓSõB L;üšQɆó ÓZZÝž1Ќݤ”$G¢]–Ç‹÷cÕÔM⫌•»8tŠ“ná¸Å¶º·É•þß¹¶7_êgÉ(=lj%/>‡ ™T¸ßfd#å&² ¯Â‰ŽÎ²r î’ªbwñ‚rgï=þàªJ»2ÄWDvaºxRatO|Ufý«]ßO¶ÀŠ ·€ŒY»BÞIÈ­Ô÷Y5 Î׸óµö„Šç¦– ´²¼´w—bdâ¨JCÏhT¸(DÕ nB*9¯ó¦ö'Ľ½µcgóEМÎKO éÂùÏ6§ÿ±/,Ž£iEÀ‡ù½é~Z§àxLç¿^úúqP×P[÷A+¹2 ’³qSœäDr>¬™ªPƒ™ÓíÞ¿OL©d»¤J¾s¥bì&‹*îfroüâ2ÝHhÍ…`d¶1n®Òôý»ÔËu_Ú‡9kªnXxLÚ m‹Ç™®Ò*…ô³êÝFÆœÂjhs‹² Ë&˜ù|«I9iüðqN7bœƒQ2h;O^MÏ+x¶Èòò Äï_ƒîwÕ2Öªp˜È“æñK wz¿|éˆLîUi7ƒV½ùœXáa”ü;+"¼ä„.ÏÞhGÛæ•ß%ºp ×\.ÂÌ—ßâ¤ç\UŽ^Rp×0^Sy½°õ¬0ð‘§|d·»ÙOÍH†]†oM$!ïR‘Ä®s‚FÙ–\¡…Ã[%ƒ„b¹7ÉÙæIÕ,ÛÍ ÏjŠ02\— ø3ðÄ#eðãtƒFï‰_é$S˜‡nD‘@¼Mv' ‹ äf*¤[—–¬¢^?øbOî|j†7y–¤ú޽Kå2/ìÒñÿ…dZšD]Ç2Šzi¿õ&:÷–i^J£¸Xóøú¢ÎFY‡±òV‰Ø´ÿoyÓv£êãªÇÈÌd6åq:a­ÆÕŽŠžž—‰“‹óÞ5ó­]/a¶»]5'}'Ð ¼4«Dîë™5ô×qƒG2ü2zc¸v—Ûùê5º —a#[ÐûÉìíÀ*Vò2€¾öÅ|¬Ïþ.dymA…íÄi§–ˆždFÄ p9›‰_ñå¼ôjÂ0y¿~‰_¿d‡ 7îfr_,Vq-¤Ô€¸¦8,1~ï &‚fH7À®Hæh†àI øe mêá6(ú;4~ÑzîHãø2@–»äîN0ùדµN÷.ðo½é×®hý®_nzÜD¸™d<ÂÅ>Çf\êA2«È‘jÒ÷còhˆa6«&Q]¢“H¶»M! >`øPI5Θ·xìàe¶¢F™n ü0ï(­¤Rø —í]¤‡OÕ@ŸpV .P•Àõav‚­•§@Ù_ÏÇ¿ ;_ïè­í'u Ò$¸6;ç´ïöìrÄä„Uj]}bÞÆO,·ÑZÝ-|õ¹eSq8Lì§6®‡„—TÆEü çÞÔãȺKŠ™qnvz/BB‹aú*ÂèýÊ!­¢Ž¥«¥2Ía]G’lDçÔýkô`c®rTw»éÿÐÊl].5Ë‘Ck ×énáˆ!|¼`<:ÄSø>•€c®ÆÕ&ÑŸ eGçÄËZ q¼]ß 6:æHÔL·qfRYåsÿ”[R)‚%"ÀÝ%ZÓ­BGeŠÁ½÷,‹¶/CÂó™D¸˜„‚¿–<¯¤XšAŽa1Ëù¸~ò6rL… Bù¯/6¨Pr»‘xÎ5µŠk:—Œnî³y¾DQö&#¥ksþTÛ¼O4ý_³t¢ô²íx8Â$wøëLÑÅ;L¥­txšDhϧÏÝÝÒ=²+¤1\p7cÖ³ÚWŽÜNÀ@ÔðHBxïö²ô±ã¥uÜS½­²KèŨHü—ÉcZWò¼‹‡’+?\«å·Ÿo¬;¸ZOö Q'ä]›8×f æíÒîU©šì‡™_T€Ÿ"a{(Ÿ0:Çh—ÎùÕEozHZ¾LÛ\þ,Ç”•¸±‡µ?t¸.ªÜ©kعÅŽÑ»^އ» £TËíƒu:ïÞõN2Fª…÷–þ;éÍ]¶™,&GäÓ+¡Ký^ðË_±÷]×T¾+àêll§P$ÝL€’Äe‘0̸ÙIâÝ\Øzf@½ö@$ÌóÊ·ð"¦ˆ¹Æpæ´ûû z¥O5-ö[0¤]_ïær)óO¸e` 1ÑÇQ«ØÜ¨ÌnÃÇç5£¬Ëk@~(º”v¼Òâ!ðj¢á€ ÈŠuà,‡QPX¡ØèݤÄįײ…øw‰ÐèO‰­Ê–_û7ÇOcàžZËZfÈ[„­Œ>…5MàT”¿¯’ÝN‘ï¦üSþ’·ÂËÙ<æ›5ÏDºÊ£:YÞ€ÕF¡$ âañû5^Iù±[VÊP2z,bÀ¹Dï}D‹bûu€Ve¥ Û0œßÇËsh*uBú·ë8`ˆt“ B7QØ\"ŠÆl¡Ÿ?{_+¹3²Ý2¿'æ†9v+œµ%ùÐGާínés^v)²Ó,®AYÌþw=šÌ—&rð”>+̸̿÷ÿÓ‰ÜðO‰ä!¢èØ r¾ž(l>‚N `O‰W‰†lÛ&êÚÔ´o¯8ô¬—ÿ*bkÀðYÒ:°Z­”E‰¬µáT,“’Ø#Àrª‡™” \˜8jִе6R^0V™ôs$!»X§Uh­NÛS&Œhù¾…,†êÓi1]Õ’69ÜF—)ðñ¿úô¥Òžç ¦2¹æ=ÓƒîÍN£!d,¶È—·•Ò“aø%<&}Ëe/`Z–ÖI-ܬ+¿NˆÉ×^Çsd»u® a6&J”Çà3»†ÔE—í9±­;{ÎŽeœ­¿ž³ûñ¨­Ä·L90j…Õ66í‡)œG̨G ©÷q‚`o†·$y)6rUg}VI_"ªu²ÿ¯ïÚ:‰„ÆÔ8Í|·iI{šö!”‰\ly“W&L} Lç¢YÝ–çsÍ"âŠ[êþ&ÇÐJØgˆjÅÙÑø=þ—öp Î…ž eF%ç|½ å‘”pųœ0˜$‹9æP7k/½—P»'E´Êáa]{jÞDÕÔ¹°„’çÒ*ñiº ƒ3v›ù†*ÔnÌX¸÷+XþÆÝiÁJï]z4?› ê¤û¿Õ0·<ðþïLE§ÈǶŽSà“fr‚G]¥ÛgeÿV`i![ÀШålÂÁ5wµn±:ê`5ÒÜ:Ýzá_𩵠dÇ·ÁßÕ[Ûÿ¬Ç[„%H¸¾I³/E®;wL¶:›£E¤C/òRv†ãͧð5G´½ßNNÙ0žp‹àCS)ËLnx&{V‚!ÏdXõ¢îÎ˨!Ù¸rµ¹…¤]°Ë³ ”2½¬-·<)™ÚÞJÁa¥ßDÁ¼`ßÌáu¹ü¥þÜ4Ïzœ‰{7…}Œ±ç¯ôXj#¿É† ˆ¢‹\Ý¢[t´»%Sèíyk#åÆô»nîMž_>‹KxÕ€š+ä¦ ãš ¬»«–aš4ß–ªô.Å»É.K•% ⪜*@š–oEKÅð', ôrª;ÿ-UsOž>“Éïƒ×ÃS³4Ì áè°ÊÔÇOY“Ê·'OÜßèkÛ»óêv.V“yÝô ZT$3rwøäñ¿ÛeD¼`Žý¤Qw¾´± ž;a!Û_ %Þø¡h‰°ÒÚÇΞç31µÝH²â"LÎfj[!úí«k¹Û£kkXŽÐí8…øg.¡zk& öSÙŽe‡dRʬëÔøÓ™èXꉜYåõ2ܺ ¦ ôpGó:¢ U1×Ür*&"°;~‚ÏLü‡E÷·ð@¿ ò Û ùóR#:ËPÑBº[è©Wñ²¯O>e³? ºÌ“HcÒ„^Pqúá㘞ò( V¨/yîàZ¤ëßkaÂY××÷m }¼ˆ?aO5Dà%Iœ–{ÆmÕŸÇþý›­>ÂIE¼oᵈI¨¼TL`Ôß +ä·eLD»Â¼1¸gÆè#zñ&(Sl}B‡çË;ü`ýÈDo1 j­EB ¦Út’mÉi×RзÏo6­ØŠÆg¿I¢7K 8ü¢å ùi—ûBu›!A Ðm]õf4Dì@µ‚‡"ðúu(T¤ú'òáz=[fv–¨‡òüî%…R3¶pçŽ@¨e¦Í·óI.мM#õñšå±[±Cª¼Þ 9‹ÍÈË3žãúH»/ű¦6³„³BÄÑeº )ê¨Ôøz¸³kP¥¢0^hhz[KÈUPR5J©¦ý¦¢³{²H#~t!l8Š*jòxˆ½-W²dc³½)$£˜h|ÝGäêqëÈ×\rE;rˆÈÅxb³+ «¹ÎkÖíåc'Ó7º<™2)-wê µÓëá;[·ÆfXc¬OëƒLUØÍƒ‡÷ѧ‚¤#¹öp¥œÛ0ìÉM*OO³”c¢[Ë–{]Z¡¨—£¨<ÖK±\É¿1Í}M§Œ•uz`zm<äÞN„þy ¹@R £ªuàÅV} 'ç°ÂÊÂÞ ”í®‘]ZvÊj¿_óRñ=ì&ÛJÆ·(k¤]MåíÝ8b­çö¼tÿV#ü'IìK_ØèÞÄ‚QIÿˆªg§izA·Ux¦`T«?øß`ïƒJà¨j›PPãÝ2$µy~ø+°Ìr£w)ŠqM€ì$¦dcèÑ Êf½Ä¿ôq‹‰‰ Lâ£J­™IÛf”®çÖ;2y$åLÜ5ªNøý·HÒÆ…ä"-üð°=<XC‰V)$Pç¼`à 6óºZ%XÁS³§³å¶x™AøÈ?e°æ¿zbh$°é0É›mlróHÇ%„¦~†-„4„²µµ†H⃪bæ âg;Þ$¬±váíœùÔd9ÝÏ[ è8M¢ºîÄ<ûcÿ7ƒ ¹¤çì ójñu_û,iðèô€z)‡×£Œåÿ=ißⱚԸ¤ÃX/ô¤£ƒ±^±_åóa4¹±k#¬ôz½R«W Éd—ØN‡T¬µå¼'spï. áDhÒÅæÂ2]%áy°Ý Øˆa§ÐC‘ eðnr’M–KÊTƒ•ŸoÔ7×ïàFügMÛó¹ô­Ò±L å"® i°±Þ‰³§”µþ´ÖFKÑ8Ýazµ:¨ñ»–$RY4V1lö©­jÈ“0½¥Œâ­[0Ë6ÜŽ+b„^|–[ðRîÒœÄ:ˆ°D`D_;-I5ÎMFù,™I‰Fa©,$&§D@ ïõ\¢Àìh ÅA.u:Vn|²Ò0=µ_3qÖº|ô¬Ë:N¬©#Õ¡#ò•Ã}ùJ†p0TdVtiÕG"û‚£áŠel­C¨r–=ç´PÛ æ—‹„娘‡ºÐØó‹º¥röf |Å&TÇð¸á˜)lÂÆîea{8:?IV'Fü½M·&¥ ODæNÖÕäõˆtÞÚÒÀ;œ©•ôž&Bç0}|Í6êËÕ`fªë#&`¢¹_n|YE?]lèE ŠÛ:7+@·e ~°˜ R èÚ±”L)e‘D¯ï,ß_À_1Ç9D×nžâý’k%¶(W´’²:fÌÿ U•_¡g·ª~¿_¨sf•QƒA§ ¤DïÄ£odr\†ž(%™`8GŦÛK/Ù¾Ó›“Ñœûä¹]dƒ»øÓ|·ˆãµÓë."ì{H6z‘m÷rdY»FL`–/x0P(‰¬,“D¸úEìûP™UXK'Àä‰þ~Åêm üãP€`sr¾Öjã=±®f¢ïW;RÝ)´îÕ6îK X{‘ Ì=Qr´¬H˜¦ì½ËïòÉhÒnÜ3šÍû|0ä±>›Sb@&KÌ*4¢ê½džšípo&lIjÆÀ"²?ß å€Ÿ\¯˜‡k©+W{Ø ÿĶª›Œ§G·ƒ.Q@L¾¶99µØ§=}§ò?²vÕP Y£”¢¨TÚ6Ž@Ñ ôÆ}Q4 h|+ëÍ¥»¤iLÃ’0(ãÂÂ>®ªôIpàÛ.DæŸ7*ÿg÷5±ß"‹äßÓëÀ’-õ¾Î,±¼ŽxC1ª-@_G+¹í´€Ç„õ§§ó»Ú…h·²êË¡ úW(õ¦`šÂ¥·Hƒ ±ñ-Ón TÎ8Œ\04ø…¢™yYŸæÕž“sŸù‹.¬yx_ùVB$Ö´TüÂC ÒŒ7”u¹ßÙ•^O&†£zCzï>@ßq5>“%Fÿ’¹û·ÄM±Sp`éJBdèÞ2ÑóÔÔ4V&¸¾ãˆ–ÿ3PFª)1…IÌÇ‹~Ë-Öü%[iÉ'ù¥aÝØ½àŸÄ7L%|˜âN.ät¸`"ÙÜÄI·0–Ï›e¯ž¯Žß{Óñͪ^¶3@‰Ü†.?Þf3úÙ@¿ˆ\—a…­Vg™u¢Y08üŒ^ÚÈ/°ڙ p"D›vza›¾éžeÙÊSΖm Ô ªŠ§Æ Î&^ý’Ý«¶À«¾Tñ‰ @EH]O ‡KR|ÒiU+f4`“^÷‚Lì·×¸_fd§¿ìDŠ~Y¯h‘øø°ÅøÈ$Õ,žöƒ¼…¾N$ä ±â=NoŠ1ôdñ¥Ñ9‰Edÿ:PÍáIVÛ;$àj÷½µRz*…>•5‘MF$Í9ê¤ïܬ®x7 0žó` yöV¿Ô,îøò§¬ofGH$Dõ‡‡pkéÇ«„D(ª»fy丘'¯ŒùU“°÷êx¥<¢KŒ‰g ®"0… ,[\>I3 +lZ-NøŒÒF—)L×Jz0W2wpTLî}ØT ÁnÅôÞÔÔ=Î_#+`}w(£zLч“%õƒÁ³pÑn¿º•€j¶+ðþ“¥ŽhüWÌá¾d‹©jöôòˆ¨>uÂ&=Ã*«“Ôí,Ùù-Ù}Ó/‰Z~v l¼¨ì(l1ÕˆP¬·ƒä"~æ¥^µk¹ª¢_äd!šˆÃ¦ ?ƒ‘¬‚,W¤Ø?ŽÓ9Õ§ù*GÉQî© sh“&AƒqÅÍL¶Îÿßz˜5…_SØ¿áåJ˜TØ÷XNÔéw0cT—`JñVB»{¾…L‡uUo û<Ï<Š~ñ®¯óq‡ZÒ0àåÏ¡ÑìÎZÄ+·/ôc3<ï²7{äÍqÌT}Ît‚z`»=,’ ‘e°ôíCl:9˜ye 4™r ¤£Þb³Š!h‘K'œ”U•œQòÜI1-&ó‘ä,üx|Ӟ؄†u)Œ™pÎk¤9®ÓFñ¡ÄÅAÓ¢…õ›¾#€r`Ž!Ýnµ(%5$lxÀj¾âމh#om©öÑU.ºÒ¢†3¬óòk†ÞeZ54’;õGXó”FýŒ– Áÿ Ȋп”òOŒDIa¿BÞß4ƒšN†–Q)LŒšƒƒ)[F¦ÃȲmttM;™¼}}ù¯æÂ—.ÖÙ¾Bá¨RrÙj=ƒFÒ³ýló¿ëušvŸ<ƒ£r±j¡}'SQU¾óG}ÑDØPšV\þ K]ƒ%:s‚¹ê[®Ø0;zµ!ZLÓ¬»ÒŠ?ÂOØæëY>³}¼V %qÑ""Úé·ÜOPRAH`Ùªz¯‘CÜ.6¤ÔÚz”ÃRÓE_U¡¾~ËáŽþ®¨›mÑØÚ7!¬I³þ·^A$²-¿>ÆÎ›:úSzä=|][¥d…]ûXu¦Ìv’ó;†áwj­’&¼†ÏÔíÌŒûe[†–Ón£ÿø}Ⱦr ÍpINf49͈1Qˆø¾Dô|Þ»?L„¢ÙÅÚîå¢5ó|ª.…@Qœ˜9ˆI͆ ˆ%ËëÒEÍ–2ßàrÛKõ Qyf™«Ž]šÊ“y¿úï«2¤Í³ô¶©mÀËà3ß¾äÞ›Ž¤õpÿ{^~è ’ªÒ$#{xÔ´ õïŠóЕÃgÆ«z¸9¥ëíT­^€e’ BʽIÀÂ(nÚó ’é?–ë¶rXÛ^ÝMP}†¤[P—{~ay$d÷‰¢sÞóŸ¢ê™6Wc© 6½ë(2ØÛ É.u †w•À'ý·¢ºì¥ÀUÚ§º-è]:9Hi†õùîM.Þ{Q ‚—Œ˜æ–Ãcyy€(°Ï“LÉA 6»Z ÖÆv4áw&· óµ‚ñ%@Ú6h•—mšMSÿë×Â5DÅÈÕUæwnAé;¬°s¸a2„}Ú=Z”¼Ø ]³3‡¤ìS5ÕF¬¸¤T….úÇ9¶ŒèWKç𮟷l1Á[æfòÚèH†­Ý3—›.ëìQÕh×>Ê9ñ*§.ÛÓ3Z]Å¡è½b äçÛ\<ÅNŸùÂlþK‰Q„w;†C…§D‚‡lKu$(óa6ί1 ¢xÁÂ':t¿°©å¶_¶JCÄ­ÍúIw3ÙâŠ1›Ì¼Ó[­ùÚ˹(É«¸ƒƒl9@Óh#ÌÄ/·Ü~ÌhšxÊøJ¾«‹a03æ¥>`éni}ÎɪH?øÓûŽOFGŽ6Š ?DšÝM̾tHî ´ÖÊ`vôPw]›ÍÍra…è,Ûήà ^¬¿S~0S0hlÝ•˜—xà®DMRTÛ¢&Ö(У˜C‹Yc¶¥[ÒÀ’o¶÷°JÐcdº±RËÅuÞÍ•лšmËé •lxBkË”D|îQJšHœå½±é8Wð¬Õ|C™ÂÑ„›ßê¯Ç{å·¶as—@:´o©¨©ÊJcãØ×-ýÄßa§«%èpš<Ìæ‡ÿµ&‚ÌWJM&V×\:S¤ >#—w7 `@ÞJ÷‡‚Yòná•!û1þNý \Éc3or¾‡y¯Vç¸ dŸ"´è§T¹çp ß$ذsïÅ¡ELŠvØ %´UyÍW[d ¤ùU¹H2‡e }6Nµ—/ci´h¦€ÊN<ªrü/°m–šŠ ¯u‰ð{¿à÷®y €¤‰ÏÐË÷÷^Hn'_ æ¾}ˆ6š•ë÷;±o¿Í}'EBC¨ƒ`Ñ$ Ò„•@o=7Ò« _•°>iVM‘ý^©.\{Þj^w—W§ºñ2Ť˜;ü0QÍ8ÈàcßT¯²‘E“Ç´Ûo_rSBa3FÃÕönpMó1â¬õb:ÿbo˜D^O‡™öå–YRi|h4;‰0Ól_þ.1€ëØÑ×ÿ.£±ÈY¾]¥¼7›a˜ê_p 2£]ø¤§é…•eJö§ˆMYÐ6…!´m›_í)´Ò ’ÿŸ¾yYdµDª FJ®D•\¢ý‚* .¹ÀjÃøg‚ ¿kée"ë~;P$¤ßXYæ§‹•ìûß!y”F‡½ÝÞwÜa>ï\‹gG½cÊà¹kO ¯ÁÛJ3Ä¿e¤ ¤ /Ú2޵Z¶A–K`gÆ—8ÃÖùÐ`À=ËÚk¶"ÛvÞÓ@³ê—G˜ö]7C¸U+_°ƒ'øï*'ŒBcdv’wÑ×¾5ÿ’·ÿêÜÔ9¡kº¾¢eš8„”Ý‘Äó!2G§3>ëîž[’eQ§ùé3Ö..o_*lÒ{–¶ø›ªæ[`Ûša±8CתöZ2XÖv.•'BÝA;-)_á~ÒÖjâ^ˆ36Ÿ•Gwz(š¶NjöÉ€ ?¾.¹Â›¤üÚ»ïu-À~R¾F@87«CÍ4ÎÇ iáÌÊ®$kV»u’ß/üÌ@`x»–€W¤½J.7Sw"¦ìVUž[” ìã´F‹EV\SfÙ}ý–Žâ’+†7¡<±¹Ì½uÏç{¨µSη[ßrKVÊ(F-ðŠ=ä ¤Ý‡‡Ü0w¿FO kLn`§ êˆMÎ Å—¥P>°?`~ΉRû +‰Ù’ÕT½^‚Ó®/ŠìøÕǯБSÓÑÌÁˆíe€eëâa÷%\T¬Ô‡.N„½1Ib8ÃGb°MWX{aôâ÷ ߆¬ñ@ãûñe\]8Jñ&)”۹ܰßMVw„é#æ&g£{Š’ﺩ=6E¾šòh…£€]œ(¸¿œ:|¯tÐ`ûܯ8Ì “zÝà[y¥±¡ûö°xVÄaSçãý‰‚8"φRñpæJZ‹Ieþ³Õf×é¤Ù¡º^Öÿ‰™Gjð„ \&I¯ðh„æ\Ó­'pþÎó³Çk¤†‘Íáp@Ä{XÁiÄjáñdDI€NÅôú¤è^\¸ä¤g²æÆÊ”Œ]ÒbÎ{ˆ&Þ¯d½@‚:½Mm)S¾+½h¹ôù|i㪠¶Ä©;Í{ôšÌƒxR%WÎHK .õ9“»jzátäÉ%(Ù³!wMG–Œ¢í©Ù½ òàu¼,=ÓÆÚ#»³q]|ƸžH?±›À&·0WÒ4ky‹ñÅWbŒfè´=ž/®š…l(ð Õ K@ˆ§Ää+Žú„ã¾-8ÇÁ+#¬°˜JÛ 7OXILe` Ü?“Ö+À¦þ²B'˜—°Õ掎@Ÿï2Þú]p.ä3DACºAŸânå$ÚsfžNL°óãá¡Á»”ç†A¢ïò<§jø•W›!Ç!Ë» Ô’tÒÀ¼ã}c´w)ÜYÉ7ñ‡ »ä87wOƇ½ aƒ?ã>ÃÕôõ¬­±kL TóÈ7ä?ø\þÂW°YLi!xõ»š ©5R…$¶~½€®ïZûXH˜)ÎÝ3:$]ù :}rª¤¾ô«k¯œ¦Jâ½ð´¯‡>æ| N ¾;•}p‰åÒ=òÊs_’-Üô.ý‰Àd.â÷ËÉY¼:ø®çÂ}˜Âm…dÕ¯ÝÕßP—¯tÀ)l„ÕËÓ7âÛþŽ,>;˜ËÕ:0\ßăÝj7ÈZ¥R86ôªìgçq·öÁ¨¤«>þÃ×_‰8›Ð Ç|» øó\âÛ‘€§˜‰Þø”,þ‡,Ë Žì ‰!{ü'Œ47ò'™áÒaùÞ‹Œÿ‹R‡# HëøiÒY™z«–Ù"~M´^ 7EC÷ÙB<î©ônÚ÷ÍXŽÇ/è3;BYÔKôæFGv ßMv5\ÔbpçrAãæ|éűô/cýmV¶ëàŽmv I_ƒpZÞ)ʱ½Þ–Æ"ÇÒÝÈ£ðw6ß#ÁE5ïÌà‡¯Ôàåbý” ä%›cNéðóÇüûD8Žd¨D+¸öì± )…¤’ÛüiÖtŽ>h)ÆI:÷[L±’ˆË‘œÜhUÂR6ÐH*FÇÔšhüɆ¯P»9{}J­´ÚK|5-^ÍOç[~G¯&##ÃË(”Û©+5Y#¦ïP ´aGÔ<R¥ä)ÌB8†un¿¹}f{eQŠÃZàpÞŽ—Aws÷§´äÏ ¹—³S÷у7µ¤òzï O*¤@ø@ìæzÈæç:F¹[óÀ 9êï¹Z¡ Üh&Y‚µÏ‡·¿6(õ¡çÞ¼Sa±l·ºrô%+>Á@çs›9ÇGªOöLÞ§iÐE( T,Š„yÞØÒ¸O¬ŠH5‘bõsêoí䢓Šáh(p`ª~mªï$Æ•Ô/&îVÑ>_ëVäu?Ži±Ô'¸3s¾x›ÀF$ÈÛ¥®#ÈñjC3ÍSœž8á•ÓèîÛa½ÙÄ-Ms5OñåR@A™Þ4ss—Ô™SB§®døº8刭ìðíQ±9Ÿ7t a&WËüKÙ6÷òÎ2 ©R”+åR‰–ݘ ¶^)~·5ëÑcÛáP+µã8§3òzä©SÒáú˜ï¦æ×F¡žÅmzÐ>Õ٩͵óÛu)Q@‰?»¬XÔ•:bôHAM?Ƽ½e‘þd&,W¦Ï¬íùhLYY0kXBj ZÂ6’2[yÿTJ’êýµôå/ç7"žaÆ–¿yϺª@) jˆÍº¿LÙõ“œ²ö³*J¡Ð Kó¹ni¦¡R¬I,Ÿ'5Ùßh¨S¤.½Â‡Xð'9Tòì­·Ž:Ȥ”(¯Ï})WõQá$m¢æ‹Wä|4¬²—©¨ bž`*¤œô\#Oζ÷ëy±Q¾à¨L Kos×SŽ!LF©¶*¹›ÿP ~0%Äžii«RX¸Ö€b ¨éfv”Ã,¼£8áH¾]zÓ 4 ¡¯ø?Z~0îÅ^oa¿Þ¦ô}ùSˆÅ­ïÈó6ÕrµÒRî‚4Ûh‹ #l„–# .Ñ›µ=ÁL?J<¦?ˆ®’.Ût0˜¡\”YÑV²I-¸aaÈfb–³Õ’° œúþTJnع밵ðÛ4ŒT@X÷Z)OÔÛR©h_6 Âk–T”ç3‡neÀláe6­<Á`M•Ǻ'†ŒêÖ„ïçÞ ƒ÷ÐV)JöÉi|Ê%J.§ÊyǺt@Ðv#¯$çCM°NóEi'E´‰P¨^D@®Ü9ö˜¤šá"*m^ƒÞHuPÏ«¸Ö‘²3]¨1¡‡x7GÓVñÐÕÚɺÖ™!¢U€Hz‘-_èÓoš‘Ì’Uköš´hÚi¾«î•ü?ÇÏ¥Ôò Ž~wšßLþÞ ¦“väÕb”Õ€ 7÷@(Á¶ù!d×°N€ ç. ØhQÖ«YðÂÅòá·47:JûºØ%F¬8DDÿ)¬·¯…fÑÞ‚ &ŽÁ@È!qÕ(Ö–a-Àö_(hãK\޲:›5S•›»ŽÍq‹“š, NŒ]C{µÀÏž$ ¥î#àä^nYôœl íŽÎ±ð|¨Úk¹W؇[{rÁÐÐÇ›ê'+Ù4ôŽ”ê˜—ÀÀ¶2èe¦£Z.ášé­s Ú*e…Oq/³‡îŒùýü—§T³¢×Éé¡ÎcG†P+EsÙ§zúËZ"7s’u]ꥻw\tiðÈêWßˈóeA:‡“›LjKÆÇÊ9âÚyIìÒùä3áÐ7¡܇úEZŽÝ Û˜ñäxzvï Ã'v 4°8 Ÿ^Sl¼bL¶‘ WB7ƒ¸N´œÕ•*!›žZþ%õÿ“ŽÛY§\çϸ0kÕ°€O¡˜ÂhúJnÑ· „GO<‰ê©Ýºêˆ‰uLÇ›.MòºJB:7“R7ˆm˜{´’ÙR,±2>‚û& ĵC #*aþKÆæh!!¾ªU(wQ8žÍ—y3tKðºÅd. ²+²²qVëaKô÷ù¦ž¦¨H5[Œ)ቷ#jÃQœ¸ý7|F‰ª†^º,‚0I]Š" ØWê2BÊ%,t$?ÚVÑ .9átÜŠ©J%TÀ}¾„ÇRǸi?5”YåßÏ ;v.¨…¿×ËJU …#Í1†Ø4T­_樛 éW Fý q1Ù ÿ/ €˜­fŽ”®´=ÖPýÊR–²7úõÕ–p…!Ú¥9ï´ªJ²ÞW|çÍýøë MItÕÿÂÔÛgᘀԧÍpï×ÝõõýtÕ|NXµ#¿ýztÿà EN…¼àL‡Þ,í›kº=VWqçPTnÍxö-_ƒŠ?ºÿŠÝQ«‰ó6ÿ-A-¼ñ/%x59µSáîèr`¨µúÀE/õÖ…IÒ¢Ìû}[ˆÌSuj¯@5þq"'WÒï_VfYņæž±y‘ Ø€FcQ’þ7³ FßõÌ ®Ÿ@àteºÇë>Øã—=ÁÁB‚Ž\Í#X ¸¢ë¢ï,…Éç+ÞÊ‚V3˾CZqà¥I6 ¤x¯›o]× <%н]ùO‹÷q‰`ß%iQm&öIè4ˆ¸½Ò;¡ÇÕüöÚÛe”gwOJé©ç-Gà¿Q&CJa§tºÛ§/Þ ._ô£w/@qÝö«0}¯ÜËkëÍ–x¦YŒJ¸€»^ÈqÏœOàSW¹®Zˆ3ÂÊÌKÑbþKñSK PgŸa#;s0°ÊǬi…d¾ú,í:T–R­C³9”ˆÛÀì›ÎÉ0ø YŠÂwY ò`‡Ûà%°üïôI^¡„ð×űiÛ@1Í’!—Åuúô¯B´h©GñÃ*‹úD)…ÔŒï=Eìf€È™ ã9¾ã)·¶Ü1)CP^eëƒïo8˜š¹y$h]kÖ¼m*ˆÕÙ±%„+È•%Ð [qFyí'©T7Þ\v…ÙÐåÃ-Wp5çîäMdVþS18³˜ØÄ© ?UïØ…q¹Ž0þ>‰bºÅ-åõ‘^²Åtnõp æNÊ`ÚbPpM¨ɚߨçšä bBR.­`6 xÔY‰ëTM_à#“[.WN Ÿ¾7 ÆŠöå0úTT·ùFùu Ê‹ÑU,º¤£è¸÷úPR^ ?üǵ—Õú #³ÿr)>y °<ÊD‹Ÿcu&¥’Qñlø´ =:ƒb²oø™,‹iÍÊóTTæ–)st2q{·x„3"%7w""†ãu‰Ä´¶Ú‘ÏnŸ=ÄÝËJ8öß×t]•;&ƒÚ'àuÛ•ã\ÍÅRobçVÒç)w#+4>#C©&[Hÿb~Á`1åSu–2àSƒxÕ™X«¼A6dÀDn¥(ÔçÒÒ ƒ*î'š,ôê‚u|Š!^fTœ$×dÐàÒ„hÿ¦£¸ß8¸‡—LÿW{9}S7Ô¶w>SßøMñÕU@PùÁò{¡—-éÊùºAû}ÿñ'ÜfæÈÝÑE;ë1±ÆDÜ“6ž@“i›‹h;PP§Ã(üú†ÔħRjoÀÔÝõý˵g÷(çR"ÜG¦‰·ý°Û$yû8Ìóå&€ôßfBîí¶ûÑÉŠ®™¿Uægñi%Ë·5'+—f“—d†¯À*"àÆ¯°žfësAkoÚkýþ•—ûb¤Ò¡®œULŠ‚(»æeµØí¼öH褉_„t$¡Û¥ã¿ûKᘫDÿ.-!š9-ÌæÚqær(^Ïp%9xs¢ `}îû¸ÇÑL9·¤ÆxkuÉ¢p¯”‹Œ{Ñb+vèõ] —Ì 3Õœ~Òž™ ÀÑ–•S™$›¥Gh5Û[Ó7ºH âßPâšå|Öᦻ”Ì(ãÕR>°ØX»¢Œæè*t®@œÃT¢™G»×Á”>U 5؈‘ÞÆrv$¹€ÓÏæ«ÚÁËLÓÓ%½öòÏ*#JT†yÇ=vÁª0A!… %1L†“Ãʶ¡Þô>ÌÉ}?#ôù)Ÿžâe¾@ª’ô1¦1ûF™Ðc]¡ö;y÷®|3äm­Tb¡%Æ’[§až°JHk)Æ ¬~Qz°«ÔÍkÄ1Çw^d›à Gþ5£Ý=a†l)¶Ìýô´%ÜYJ ®.zï ¯ÎéHRb ªn=BÒÔ‰M1‰‘™Gáo´Ã³÷@SÖ.šyÄJcX:]ÌÒ0e„“è•Ç@¶ëì®§ðÈr~{Ыëe8Ff%‘oú9 ¦ÄÖÎzÜ;Ì*÷¤'-ųº3~œ7n˜AÞ.Z þ÷ºj³5õQ“æ\õóׯ‡DŽš9Sç=æÀëçÊ5žQojÎÅ· »1‹ú^–hÃ}$ÐÕàV™_I/K.¡…Ûæ•ôä$~rñ0´ûŸ½Ú¬’A'¸=†Õ5ó¹*1šþů¬àÚ™5lS[º³‡§[ÎUyûí û'Ž#C>Hß3:ߨSå¹§“ÂòuNGù}”0<-ŸÅžÀ\Kês¾—Ù&@ø>XÔ’šV ]Ìöz!—†áˆ7¦•€~ %ø…Ôm†´FÊv£áoW?f-X®òY'€§òƒãxçéæ‚¤Æ¥ÐˆQeîkhzÉ.Á0<·‰wt4Ä\çêðÆìnQ«ç?b1¹ß¼QÀšV=[¹h0rIáP)þ̱»Ÿ Ï-â×V9?|ÐÙ¥Ph6‡³Šç˘38,¦«³ëáÖÆ¤i›¨#;–‰@ÅÓLñ6×g÷-C†ßn¼„—°ê"æ»ÍÌ5Tœ—u¨v{ü7¸žƒ^`ë×~Có%áÉýØqÞu7îÓE#jâ4£c#bð&wÆ.œ¢änr£ræÜçZ¢ ¬­ìúÖQïÛ|ÔºÊOÌý¾½½Üúj¡·þuŽ"ë0ZÀ ¾² úßíL·ø±÷´üÊgE?Æ¿Wß5.PÝ&.êš+9ž„æxÇä TÚSHA‹oò=s“§¶ñ (HVñU£¹¿ÿ6¦íÃýqÉÓõ(EÀ£Øþ”ѦX¦‚Ou®¶>£«eîðI%¡Ì{où*~ ÚƒMÈ{ÃãtTÉGO$½Øé(Ôv˜p îÜê• “İ«áÜvÞT1GMRú¥^(Nò}‘göy,£ c¡ãµÇd³š‰Ÿ‰ä_Š_ès5ä·-wóc`¢´€$bµ|æìŒHAkUCç¦ßæ)><Íï.$„‘z.IÃäž>¡Ùtà ¢Ëº…Ái„»Êªˆõ¡é1=ªŽçÚ2€5[•õJÅ>›D;£÷x1Ï0ÎÎ| W¬mx*gRZUKÖp’$wš?m;Äk¤‡Éé–¦=ÊÚ³Óòï¥wt­ žÎ§Ñ†ÜÖç²–ÐìI|‹Ø€QІèiòLö0•ûŽô: ªÊS ãP†Zì‚gÖT TÇômÏ7¬zÖ|s?TÓ2û±9]K)†°˜…¼!oq®­ÄÐ~I»dÂ5GLŽsK-ô?‰p¸˜ìixŽBwïÀÁA»XƒÓðMɌڬ›ÂvÑgt‘|T ÂdU½Vµ×7®h}Mp¡b÷§“fa¬[Žýw—~5$•Þ±¨þ¤±›¥ð»¬(¬-Øus¡ÏÌLÃþôW_ì"]8uQg•5¡ðôZc¦i·Œý.¶ ,êÒÅ)C²†‰œŠ$ÍÁÚq½‹w—iš.`³-rÔ,@iƒGsß’ ½&`4 €I2‡ó.-@€N¶\Vlwrºm‚@h›¯ëtÍÏ¢~2-M$úÚV8>5ÇáÈÑØü7Š#K!Y×°MI¤zžMBÓ Æjç)Ó‰_£|Cð—xïæpÚŽJÔÒ$4ô’mj™ùØ«¢/ÃE«üëX…AZN.‡çÎN‡ÎJ+c¢’ÏG2ñ`ÎŽ$´ÐíbÏoPö™w…ß­°ÐÛ Va]wù£¬Ap$Z³´Å%Kk–·Åþ£Щ¿‰ý•tÉ8—QÙë•Êý/rNFzÖ'̨h8dËõ…•[A§C­é>T,nþ²mÚîcìköäÛØ&È¿{…ÒJ7jK<Á‡w,}óI€¾q~®à+­G0M>í§Vƒɵÿ”3*=ܤ3ú+3 ‰Ž·2¾%N$ü¯³©:ÿíŠñðÓ®áŽ{¸SQö¿ic§HÇ]¯ ÌMkU7Ý E9T;ß§BáSzNäÊýì&ÇAËOÛVØ{‡¡Æ‡èªèpê~¡Eò#NXÔ"}ÀŽ{rœç-¼"bÉ×Ðãj.•Ò8L½²­Ói7¿Ÿ¹ñ±´Œu[¦Lnn·èfŸÆ8X­Ù…è@S™QQ’¤ñ§yùG²ÔqáæìH­ãy!FÁþ—EwÍ\ƒ™\0Îvð‘(TQEÌK`Èž¦8jQ¸) Ùª0¾úì ’ª¢)J ãó_USFéóÈÚ_¦y Ô×–Y{®´窚Ü$¬ñðwý‰4jÄìùGP2°ãë!6·×K¹Þ~F¢™oMÞ²¡ÛüFâ°û`b/h‘ržiF;y×Ïáp@Ÿx{^¸;Õ·f44P[_Ùw„L¸Dº›zñW5„D)¥Å&‰‘Rôð ãíØ.6À¦&ä^‡/O& ¤!¬çagEv½ý7`w_ ßXñÄ¥¥ŒÌ3}ǯb)Í®”cjmË +Îrã«P8ÒŒA”ŒÊšcyÀ† »Ð4259ƒÕžþP•Œ‹.ï_,Âà$ c ¼œ÷œ~ "UÜö ^ýà€píÅyH8Ù!0‘EXcâÀÚûï s‹g¨|ðä(j‚æPI9ù-È~ÅÅ¡ÙQ­ð" y¾%an–,=ÞàÊdÄðpšR½{ë•­§êßÛìdSj;gäÏs Eð[÷Ÿ–à«”¨¥}މ¬n3ÔZU“Ò¬-Ò}âÈjÖ‘þÉ¿ êôSqJÆÌ»z¿g`[ØŠ@Ê-Ï¢D7øê: ˯òPBÅ>1CAÝ®FZ/;ˆÅ ŠnÄJVÛÚŠE ùx£Îê_N僓E©PRß¼Y¦é+(ySü0 ²_µK•J‚ÿ¼^1 ¼V ˜œØàJéžjÈk3©òÖ‹Ý9™Ê¹V¶c›°$»iÁ.ÛO4el.Ò³h´ÅøcÙTqŽ›4 ¾`÷÷n=á…°õê†ÁT€(wG@‰hxÍn_›uxN‹R.޽Ú(¬Ñ":J!!.²yæh…d¯p6è£ÖÉø Öø ¥ÿñîeåaÒõKoÕ_vk9®€56›¸ŒÀ~ìœ=H¡hä±ö¤“Afã*»'GÍ£#ñR)“σW )ÆÃ‘o_Ó÷»»Ö½a#¨ýªÊy+'âŒì®¤Ð‡TðÝ»~-Á9 º,¾FíÁ¸X'CŽ?©,VâÄ2_þ^ÞÁð‰ü¹ë ب¨[9„a"ø¼Ü‚NžˆX’É´×)<_a:lEFG…+ VvqÀm—8¤ØUôo9ë3a© ?lRˆÀ›ˆY—ÆÕ,&ñÄæzí+.Ú'ÄMô!LÁºÒ êÀv†SkmËÛo‹©ü-žîu6I¨H•€*!}þ›?Ûì@w•— #8!6Þ‘~š>ι/?[¼ "/ØÁÿe&Z{îr MÒœp8¢~ÖlÒ6£0#ø| ½/9ΫÛóýgòón¾Añ«5SUþ…HVMþ8ååÃ|-•³eëït“Œ"è\EÔ–?¢eÆsQÇ¢ÜÓÈP8S/U¦ Ó5<(BµÔNa&ŠRÀC3Õù$þ©$xú*ídÿ'¹6öæ–i5gX3ª|1ï) Q?f”²R€M“É# –ˆ½1ߢì.$zô@hZHí?—§&–tÊ)¢‚ÿOA=r<1æ!þ‘úЋnòá‚}…„ÎÔòöåó’ ×fä„o‘”ÓLzÃÃ÷K- é›  ÒÕV%dk½j‰TjJóóhÍß[-Em5Ôïâ`в¢‰ z•h¿+8:Œ(G¡AÞ|Aó/ Ðv ß9| êšCÝ&Žq3Eƒ£‘0µVÕÍœl¬T=4°„£RL±ÃBR7@ö)÷¡M`*ÞbF¬½ƒãƒî¥ ·:Ÿ6,}`/(›„á|žBíαÉÈ îs~Þò—ÉŽý$nq?ÑÝcð®ÐÜX%øOgÙ]É8©¦ûÿ¢‰÷gž#E„?~ÌÙ•?Lf(rÉf­ ã C íVýö^bCÅ}8Eª_CtFªÄÏ‚®ÓNê›ÊBQzS’fZ«Ð H3[ÒAjzž÷u$H¼f\x΄²¾é?ǵçºHY–î.i±"0FĤá6Å&„ìâ¼@ºûÆÑ{“Š y•k¡˜î|0m.Ê]ÿEáÁi‘ðäÆÿ¡e†h™,æ6/ÁŸp!çF€foyY\óƒú9·mKiÊü[6µ:>ϲ¹±DÖª#|Hk•²¼UÕô”ìFUòî¦f©i=#ö½«Þv)ªEí2Ò´—ýnìè2AsU&ÑÎl¨\ ¾‡Q  ·o×?ÊÇ:“ú'¢Ûäs¤ûr{QKiþÒ)5Þý]¦Rær³•O­Âv3¶Âű™äº݉:ß´{W Ï òž¾87}¬W_”³Ê§­lCÿ í£‡Šå´9¹u´Þ¦ƒ£³ÕüwRm›dZ>†ýò!ÌkÃNi¶¾ ˜ÞÌý. Ä<Èå¬ ¨l$£ô4 ‘gv_]C¦äÒ~á½¢/â'/Ö¾‰˜õ?•ýÿö•æ<à¦ÝEåÈÅ8¾lùIÉq¦–’µw0¤LÇÎ|]±ÌQ‚¸fba5žöb,¨. Ô·›ÍJŸ8«ë¥×D. Τ¿BÖE]˜¾MGœ… é1R{› ~ÜÒ£eêŒ?í:×-«º4»Õæ<¤ÿ;£¼öÿQõWX¿Ï"åUëUáï„öͦR÷XÖ©¦KNÆ%J餄ìÀmcä.YóGä=c»S[«¨ Æ:EA|¢[ª¢éðL‹ì"o' ¼S0G‰¢›Hx¥ˆŽþ±Šdß”¥ÒéÔ!TãлÎì‡Q6~×ßB†ùx˜ÖKàsÏú5Ș9 ^ÙjP1UE_`Í1@¸{õ¨Ý`«<ô›9U¹¿å¼iW Þ•¯ÌÂþ³”²R=ê¡%dÚkÕaD4§•¥æeÎë‚QQq p´R¹, Ç3¬Áé6™¿ª\.чÈè`‚‚Ÿˆ.Gln×~¡¾±ƒ 3æëßt €²è«–†’t؛ﯢUe‚%“PŒ\>' UH Z¹ì‰ ”wæ—AëWR^cø^¦™^Aa/|¬€²Ì¥”a(y9áÃ8á]n…‡ƒ7лD¶£›¯Ó}›¼mâÂØ8œo¯á»?ˆaø›©R/ŒÕøBZxŒuç]‰e‘MÝèéŒk«ß3˜òò ùsÐWå>ƒ”vÍcûlßv&ð‹¸íG<’}Çc‹žX)@Œì/ YT`{;Li—ƒ—kå:žŽ|è°Ü”Âà•y»‹.‚º›¨Ÿ!i£(%:”ܯT‹ÓU?’í¡¶y¯p‡½¤ätK¶ÌäY¿óx‚”R€RÙºk¢8\ŒéæK×XÎå[”^Œ&ƒ©÷Ë*æ¶YÑ£.ˆà† çkuòêÿ¨n¹¯æþ”f±}™¯(Eïë8üÉë6?J\I ÂüUô@u9?3¦Ìo`Þ‰:Û/QºÁAs½ *bô#æóbmk¾%ÐÁe’P#TÈorHŠ;,‘‰¼– `ÖaZ©«*—’…pÃ>‡'¯þøs+ÁV‚‡)’++//˜âÄ…m¿ÏCèùÆd%dtNÞ* Þýû%X~j£¿pÏ÷p‰ˆi|ô`ãv´±õÛ¼s¨,N¹AÛâ<™¾dˆŸ#ì_µí€¸©ãSnÕP½›ùÇ‚«õlˤ#ò‹¢À™ E°beà:û'A=ÏÝ5!ô_§F ™Fæj ñßó9ÙTÆU샡²y•#€Éjçô$÷óÌ}b+ E=ó£Uºn0b LþŒ”ˆ‚±Écv„¤5vM„àî ai¿¾ë˜ÖIPÇYÕþ¡›¢Ø'ÉŒùE#Ác_‘æÖ*ÆKþ®”)4@0ý³º¶IhÐN®Í¸4q iÁÀçíó§{V®p°öœµþèY°“þ± ÊIæ³ûœr…L‘ŽìÑÑ;^߈¿X@F%ë¦só-b?¢û± +Ý-½Z©Âö ×t‡øŽGCŸ–ÅÞ †©áD‰8úOCiKÄ•qI¸æùnÜ÷ÅÔÄü\åóŠ= ä]®b“Ú!½HÔRo£ðoø DÌ&Q®<ùd¥ãH¤fÊ®/æfUIÝÕ¶ž÷`w¿àòã^CîK§ý<œsï|“£ÍôŒï  M?†ÖdµQ¢ïðÜø2¬VøŒà#,ö­cjÇrnÀvF’t‘®’¯PÒnTRGÓh“»>38€‚ô&æ3ÅGÛ8 cuRÍäE Åj©\ QÜÖHÎñk¼é°ë/+~ºEq×Öõ«Dá‘`n|Á¢Á¯J÷ jÜàçœèÆë)Jî20øF½ýd´®t[‰J› F)XZp„-=)°Å·•Ý0™ñKZU­ê¡DÖÀK}Ø}èöÒH_–ëü ü™cVу$ UüÛyOäù}p±ȉíWWÁ¹ +ý;ú¨W%÷EÜ)ßð¼´7F™‰S㨋Ÿ‹øZ*x»L|DNMgmÇ÷’öM¹Iç“v¯Ô…¿ñþ¯ZûÄÊdVEZÝé—¡ÔxÛJµßŒÉ6ÆÁ>-h‚Ç-ÐÏ·N$.D2½ ,kšÒMîÖõ WÚ´•éõ~3ìT:WÁT«’$Úùëy£ UJ»ÿ(²B™-®ÒI_Eå¨G+”_íÏuXlÔ\–ÝÕ_º Ù=¯ÐÎ,r¢±"V„ƒ àäÇ5÷ò‰ê¶Õ­ky<ÜG:³p$Àº9zXʶÎWÀ9éS½ä¡âµE@Ø|¡ GîÝÁå7¨tVÿpù5q¿ƒac…?ËëÙ°KÔ Ì’²óeµ·T­mÁŒ|ø?âH¢€ìUÓjzΆÏÕO~kà ]é: BUÇÿêƒhG5FïŒ R.¬Kë°¨QvcŠóY½¶á$(Ñyj)ì»±` Iaig‚ ŸÿºYqvÊÜN†Ò¥€gs}«P·Åw†-ÆîX…ÞR˜«™Š^ÛÕ‰§D!l|[3Ð;éëý<¤ü_šŠè„×cèL'Žq'VáXla2ªLAº.Uà{%qSU—Öµ7ÚW™ X¥\w Ö]Qb²¾m *±‘¬ÑwЊø ¾’É%©Šgà ˆÖÈíŸæënª' ûŸé=¡ܽÆÕ‡^«¢7ÏlB#‹ºªLË.AõcuÀb¤× c :™¦ÿ‘„†™;ûõGoשz‘¤“Øø½>qMÑUØ·¶zÞBÓ÷·YSKíte¥[k Øl(¿²7DWGâ\þÊÚ,rZ‰{w41àT´¥ÚÝ4Qr³ÉMfÅêÛ±†23Ñv¸8ùúŠˆ'Šì?\» =dxq³-Ê$¿fwF&³‰Zy¬£–Ñ8 :`H\T¤óŒD>ä.Äì½)°*ÝÔö©µ:#‹/|âJå(»Ôè©¶‚U~Ôâ01j¢ˆï,£â‚=W"‹ Ë0ñïÉ~|ëŒÛ' %EÎe5êzJÁßÁ.*h¡î5âó‹ßPý Æž?'Ü÷ÖMÉF_}ì4Éñ I3ë@'QªÍд,ÇxÊH0ó&ÜsMùºt½&øÒoA{—ÑèÛ,ü.èÑmº>âU¸!Òd’ø9f60ZË*eº¹žyáfþèæ­ÑzI$ ±ñ´@S›ù¥¼­k}Æ•ž_®…¥¥ðL°×s‚à„¤ì}aª½ ³ï¨³-ûÔµM!ÄŇ¹MÏÏ«ÑÏ3ܶÒ*ƒÎ¤Üiú,P*±ôÊÛœ*OÑ3÷¥•íqHrã-:ÙP—Xs‰[ôõ"çÕÖ¡–(€—NÒsÍÿϧY«ññr˜G–búp_D26ñåГÛlFî(z¸Õ‹‰Ò’ZTY|ÜãŸÂßjpwƒqMW ÊÒ\T_äE]]Pî4ðL¦¤Ôã¦jñŸs8HåV“Ulî+8/€æ_ßé1uìå«c=\Hø÷¾×ë¿,×;Fº>q7]S€s2\µíˆØ­kœ3út°jÍÌ–fBæ:ý?ª!l۴ŨE¦X bÔ”ÅÆAlR.ò²e†2÷?ØeÑ®öâ/¬6¨…Q-GQÚ%‘?r#Ø«fø`Îý¤ økßš€P£ø»˜ååJ±öͼ·—N..ëQL £ Àç‘èÐ á~½³”ë÷@Y½&΃èÇtws° Ô¬‰Ðä)‘¢!…4ËÐÝêÆªû·rÑÛ< þË 9ÜWìeΪHGVIS¯I‰tš.ÉxhØxAdh!×ò™!¥Ûÿâ&ü¸9þ|ú߈®Ø÷Q‡†%j±&âC«d 4š÷§ø”²ÿ¥¸ý`SÛúlÆÉ±oÉеLLÀΚJºÓâ#cý2LøJc‰¸x_ï }½øñ=fÏ숌È6bñÆ ²Ú‘ ¯î O“,È´-¼XìÈ «¯ " TˆB»ÂŒ'õÊè’Ëu¢×Z:! È‘–g IüwyQ»®@øÍrµWˆ*#¥ür¨ä­P`zHU-¯H‘bœç&qŒ£8MÙ÷Â^·Ïyv9—Æ©0uJ<{ìºvàþ¤,›ç¹(í:3‘W¦¶:o_„ªX•×fzŒ’ìRzc^–Û’KIOqYßù;[”¥]ž²ÇgcÓS¶ôÕDZm›˜ôõ¢ˆ+o›Hû¤[¸3–’6ÝzÊéÖWZîcx§Âhº0ï.+OÖpÕáui(9¢€o\Y­(ù'Aðûìs,Ž–V¯ùÝ9ϵll÷VÒ–¶ï^—ÊJî”±9ª°áøVdZ¥Èó]Ϋ’ùÙ²m¹/˜´ ©Ù²UäšñæBâÕRÁŪ&óIÓ÷%›ÕÝ€§K©x\üœí{âd-Ð8^ÑÉCß¼¯+ÓKÅ®EŽXý‰4ñB×È"ÝZÈÌ-Iª9J?u,‰­•‹äÄ”wdl*M ¥èÖ€[ŸÛBh„Ll|ÿTÅ7.Z;­™AÈvÚs!× ümQ¥Ãóæß·ú®ä×=7cXm?¢ÿ©i¡BÉ|ŸzCš“:iÌ"4Æü©bÇ–k–É5 m–ØIy޾Ìp*h`MµËØ£Zšò@žUÖ–²½‘égNsÎåÁ†FZ•È`€4Æ'óÊu·å‡µH^&Á‡ñFYÙ8bV±Åí9}e<àîóªE¿xMßGz«îCPȦL3ü/Ôÿr>Ãÿ_€áÿ…=0²61pt¶³1p´‚úæY’endstream endobj 7 0 obj<>endobj 8 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600]endobj 9 0 obj<>stream x¬»cxd}Ÿ?»c³bÛÛ¶mÛ¶í¤c;ÛìØ¶±¹Ÿyffwv_ýw^T]ç÷5?çT]U¤„òJ´Æv†&¢v¶Î´Œt ?Bv.Ž&Ž´r†Ö.&€o"+ )©£‰³…­°³ÉO€š‰1@ØÄÀÄ`äää„!ýV³÷p´03wP¨(ªQRSÓü7倡Çr¾5,Ìldß®&Övö6&¶Îß&þ•LLÎæ&S k€œ¼†„¬€BLV fbkâh` wùÎÆ madbëdB 0µs|þ9Œìl-þI͉î;'€ÀÉÞÄÈâ[ÍÄÝÈÄþ ÀÞÄÑÆÂÉéû`á0s4°uþ®³ÀÂÖÈÚÅøŸ¾é¦ßeü6bïh÷-aóÍû6&oçäìdähaï øö*/,úq:›8ÿãÛÉâ› °3ý–4¶3rù§ß¶ þ1óÍu6°°u8›¸í†&c '{koß߯ì-þ†‹“…­ÙG@p413p4¶6qrú6ómûŸêüwžÿ¨þgöööÖÿÒ¶û—ÔÅ`áìdbmJÃÈôíÓÈùÛ·™…- ý?ƒ"akj`døº±‹ýò\MÿU Šf†ò;c;[k€±‰) ½¬ówÁÿg]¦ü¯5ù¡Åÿ+ þ_iïÿ¿æþÏý÷ÊRüß.ÿöùšu±¶–5°ù€ àßóÿ4°±°öøˆþO5“ÿE3kÇÿÉ–p6øžn[³o4 ed¢ûÔÐÂIÔÂÝÄXÞÂÙÈ`j`ý½úÿ¢«Ø›8Z[Øš|/ê¿pà[‰áß:ÿÅS6·0²²ýÞ'ë°LlÿÇ€ÿ³ÿ ›þBè¿ÜÈo²³²‡½ àß¶Õdì¾aã߇”íÜ¿hÙ´LÌìvv#£÷ÿ‡›™aüðÿÑ”1pv´ph1Ð100¾ßÿóõß'ÿaFÄÖÈÎøŸ½Wr6°5þ‹ÿ"ü»(ÿ€¿„ðwºL œ,ìÿòeäâèø Oÿ‚ƒïüaþ}þ욘¸›ÁœDC/ëöÓÜ`½lXd hi±^X°&Û(uè&Q?-ý º1• ·ÈrntÜ̱y!}üs vr3ÄÙ¸^vAõ닚zS_+fLö¯©­áä Q×»— ÈËÀÝ5 qž;óõbn!'¼¢½Œi—‚.¾@žI!ÓÜßV¸qÛÙ-ˆÇƒ2ÛŸJëó‡†Î¨*tø”ÿÊò³âWÖ†'ŽF” Þ%–€:x¾M2¯% ih¦_¥­¸ƒ~s]†3Ëú&髊Ø<ÐÕ Áüþ*èÒÂÊÌûìœ Ú)èöÐ)ÿ=¡¥^ß‚£)×;îkðÌAo±EnÄ» þ@šæÜRƪA^E÷÷í¹\›öÈzC<ßÜ౿Fn¤ýc/*¾[AT?JòÆp„éž/­{¨AƒƒÌ‹L«ÔÇ»8ÐêY¸8ê ½4ñÞÚл¨ú¹…4Ë€ÞI)ä…Ò/Cÿ/&™ÂŸªïižÃóþgdG§¯“µéOj\²lž¶2$µˆáE¦þ«¾@×{§·úºGÁº¬Í¾¥Í@cv⽡­ßÔR _úþáR"£*-@ø †Hõ–Ë…3ÎÓ |YÊE0lp­ š˜Ä…õ°H&í]ÑZd@åƒö¡èV£äxPľ=½ß”4ÈjôŠfš›¹ðúðƒi®Û´m±æWZ"ïéU›ƒ~{Àa¥£Žlk}JHø1hÔ­–åa’B‘¾GQY¬j1¯â2¦Ó1/P~¨,Ø|=ã-„Ãoÿ¡4¥ð&IR'§š™Ý5|™âݱ°$ÿÓí=Ú4€ñÈ￞ú?T—ÙÀÛD¹ ùõ­˜ Úo©ždŸ3FÀŠ=¸‘×hQî;pœ®Æð ñçJtL5«aæ(ZöVt5CàcP›Û&°jz9k¬0¦ƒÁ°Eª¦þhÅ[›°»ç×EL©#ßÀÌ;IÓ.ÖüaááØtrÓÉàªcæÒa>” Ö €¿’Ej‰øÛL”[”a…FZ]}ã@Ôûõ€ UáyJR%¤xÖ†?Ó§¶+èßÕ÷aœ|Ÿ|loX6ó̦ÂgÇÖ¿S/ô Q+•1hßB8 “Jq{Þ‡$³’1-2Î.{l¶#øÚìçù!ÌÔfjëºëxÄfôG¸¹ d™&Ráš„pZm W°=FÉ"ä2 !±rÈ÷“·JîÂF]õl3‘NÖŒFª᯵{ÿŸ"œáõ ÷ë­<[âì#Vó/±¼O•ª¡;‡tü/Tž¢6{Š”üÆJ.à—w/P£gIò6µ4¯»á7@à%û´´SBNØž{æªR” ÑÕ¡ö†Xq˜œZ½ï¿)<©Áàž˜ôòOÊýÂ=ê?oâRÚ"#`cÚb© ç¹$öúaOhG+øˆýkü&…PÔ£Tõ"Næ—£}Ž)ãÐþ99Æ´N&/%WBà ð‹?º.^$›ÒÏL-uúÜkóº*ÁÈth6ö¬"bÍëÆÅs/r”U”î:¦øEðÅZN•>x~¶[oWéÔ±t½rݬàâ­¨æé@“ló¹’ž‘,¿r­¨¡|SpTýxºÁlý/ô6Øc³ŠiP?‚…Ǽ”€»·CÏ6¾¸(ŽÉd‡?tJ·ýLjËQ„§(Ö˜Õ]NžëY<…÷x^ó͈©´y-Ž›"½Ñ•ÜÉGÙœqBßÍ]xkŠLSð€ä×5¶“»˜=Œ}£>çuÊ—§i/D΄i{NðˆÎˆ"ÿÞYE{·“CÐêϑ㗠РÆ5ÅjíÚ]Ûk„ÒgïoV´ëVJ€µÈçÚ(xÙ†;!ÿ]tX¶è“›wÿ-¤\wZárJضݮžˆµ£ã\ ºÌ0peå%/*·"[BiŸÚhCæ÷Q ëIªb4­!O¯â¬*‰â ÊæìÁ+ÎòiVÚ#þ裡¤òY1[qì(KÞ™R-.q}Fàü7h€ÌK/䉈†¦¢ùU¨sÙÏË®Ÿ,•¬#Л_Ú¸í 80m&XæêxERÜð62;ÜøôSd¥T29œ ¦xa ›bêÛ(§ßsåK’¬€ªry¯”¾Ö±Ç˜“zÐ9îÒ1j‰8¥]òÌ–¤Â‹dUÀ7—W‡ZÛ­òÒnøG{á¡ðªKÝ9}‹ŠRн6YvãºPà0Ì™ºz ­¬E±'f"€8üjD_³ª¨ù}P`,÷eŸ9Ò;ƒN„òHÀåï>'·u|G”mZä† R¤Æ¿ÛÁêË:Z‹–ŒH“¤>'¶,¢W¸®×@#ñÖÒ»‚º«³ZêS³k%} `¦GuŠoºvõ ™i·V”/þÌ¿Ã,aJBòâºò7 ëMÆPÁCÐe]Õ—Ÿ:ÛkZ.e9 º!G‡VõÉþ¿ŸÇMVfy¨Äx„>åóÔæ‰‡ ©@ÈšçüÀñÞ•©&êøÜiè‡Zø°Tßt_Ê+A¶ý¸äÂNEÙ<á·(¿.hH(¸‹ÿ3¬DîÇ¥t«8‘sÍÄ”Yä`-Ï¡êÿ¥%»(A€–ôbå…œš„ƒÅa/½ª¯ÄÓi}_ñ6^ š3N×5‘„xTOž uá@œ$êR¼>Æé€ñ ºÕ‚òìŠÀ……­½~Š›§1R±ÉÏ¡â¯ëá7evïSÁÞíÍ|ÃîzêÁ­ˆ±ÌÆZ1~ÐëpÊ?Zcó|_ð+ må®Äñè§@ŒvjЙµÝvz‡-²Œ«!˜hœo:׎ÝÁï©!ÿ‡`}|LLº*†€ß©Ç ¼™ˆ²aH4¬Ÿ¶ù0‰rK1TÓw¯—z³ä°”giA{9õàu›‡Bíý¼ãW¹‹0<­4£©ŠPÆÍà0ž ‡]\Î «ÚÙO‘¼ëͧg$g®=ÄeêH7P‹aæk)åÔf½c3ôõ߯9T! Œí£ÀÐ’¶9-a÷[›íNQq§¤æMËCªÛ¢3,Xÿd©=O è&~Elo¹ìmSU™oAHã¸gšëíè*€m›æäĪ˜—®º:Ô ƒèë ï'rº`¦±«ÛÕËC¾Õž ”P’{±åÇuÿ–¸âCàX¨žkÚÜNGoI‰;ã6:¡yÄ0ð4Ý£’0ÆË‡PÞ«æð›Ñ¯¥’´vi—MÇ(h^¹ZC^Ì>(Û%Ú𠸦'.Ÿ_dG“sÈDE’é@¤>8smX>I‡„£óK¢‰¸ÐăÅêÜAkJÈhÛ¹‘0Súk…‘Æ$§Û73|k7¦ó«Ñ .½²H·OdÜE¶Æ;ìŒãWƒÈ«}maDã÷Ϧ¹Œ\È­:9EÁ„rco6YïäY쥥`˜¶!䲄Òô„:ï írïm6kWV½BkÁBÝFê.™Ì¨.kö[õfÐû©¥F?¶ñªÑ!«k³¡œkÀ)‹‘•†ö$„äï»0ïxs315Æžñ§]]‚nËÊ{åþ°SÙ˜†€x4},Åä·ÄÅ'§Œås×2î…Yð˜.oÛî©6IüiŠæ¼øm'§ãø§(ið¹ãU˜ˆ]9ß~£RÙ{–`±ŸÊdÏÊnÀ …ÑUÝY{w±šÕpwê Ó4UívÈRjØò\Ð7µ9»»ø ˜J»sî2ì²XôëÑîdðn̾qïø%{[*¾ï;-„JoG µ ¬Îc·!M2Í«`ÍúSq¡ügèd6Yð8¢_犱ÚO‘2ñêTSj w{ƶN!‚V•ó žø]1HêMKEȪSütãxeùR—¬ÅÈ”FÇR©ÝθÂêUËT>Ju×QÉzÌy¼NPäp¾Ï¨uH"éÁN܆Ç!l¤Å ŠwãwT¹,º¨MÁõ¼Â.ŒÊkO÷øª_"Ï©À”¬r©;øú%]Ôºü¥žð… !Lϵ->[Ðrø|P«ù Ճˠõ ™ê4’Íô .‘ß)oª½ˆ …C(Ùê'KÙñÐ똪8*§K"$©A°'pÎYhZF”vÀŠ¥ÕŠë3­ZéÉ;¦þw±ë[EB¡™ÚÒ¤”Û½ÞÇè¸l˶·!ça‹^Î=ÂëãrWOó]#÷»Ì¬^âèW{ë9úWè14"M¢ [šãzqO ™ãèSº¹–Bñs¡ë#;´¹9©ëØš óÙ¤¹Ü»Neöq–ÆÜ}Ñ!»|H:T¬›dàßJµá¢rŸågË•­ÎšéŒ¦?÷ßé¹^ú/ sh³ùÔtþœ‰|ö¶‚ònF‰äV̪Éò ÉÅÀnÊ¿ûÇýüpzþŠ#jÐÕTbìíÃæŸ-pÇØ £&Þëg:_гɩ ¸]úä«·Æûnåyöf™+¢Þ8mbü°<õõ¬úá_ñ7ù§¥o £º«%yFÝöï®öyïß¡@{ \À¦¯›1GA‘Òj“vaªÞ’H`…"ÍÚ)cçï÷fãö#ßwáìf³™Í%ŸØnlÇ%½þá ÝMòÝL4z÷ö²‰£l{†‘Ó Ðä^ãÁY7Lâön.3Ÿ,nŒ=õ­âØ_-§£ù³&^—D¥›+îÌHÎ~†ÙŸ-h: —Ò"ôþ[}a 0yÄô3Sšh£Š|>êÞnzý‹àCLÁnlUwÔtÛ·½w c C¨òZ'bLôT ïšM qÞ ñ˜Nì>žê¨p4ë8ðä—£cò è?‘‰¦|‹fü„À*Ù$+t}ô¨Â@>þ,: å÷ÑW—5OŽ¿i"s 䂱‚(”¡ÃS˜.i È'òòo:#ãJqMÁ™q£ù›JRygQêËŒ’ov÷„ ÂM”¥¦¶°`7™)ù9 Í3Ö­ØâÏ4•ÑL¤Á"-õ¼jG#ÿ‡œ¦­â‡¾0™àóˆßvLAMÔ´7ͺq"ýNråâÇ ×qÍßÈ‚gªÃènÞ%äìÞ?Ýò‹j’ûŸ&tö÷ö\Ÿã¬§›X†IŸ¼ÅfÙí6h±÷Ò?°ã¸$œk‰½µ‡ÈH@æÏ Xá÷¬û2`ÆâÌÝ f°+€U-)Æœp­‹gæ—?Ü-6­iQGôó}4ÞOüÓÁ®Öðóig®É³ ›}¶$-bš:›ao·KmrÈÆ#HÇlÔYŠ+á¸(}ÐìDцxbÈãÙÛíÍð›T©'”ÔÛ²‚‡ó‚*I[ª‰Ø;/Ž–‡áxFõü‡G9Zwpà€ø%ÿv$þ4çé•‹Kê=F3[Æ* gÈøYàÌ–ð19 Èû. ª;uTñų„­÷ÙÁî+}Ef† 0U XÃÍ&]‹*ú¶ÜJü‰¸9qåC$Œò %:o[”>]” ]ôb)²‹YRäÍõ‚]Oéç¬Ò[÷6æo>ÏP^2ÊCbÊÛ{›ljhîmJ&ð0{+£Ýº§ŸZÓ0´ÛéˆÜ²PÖ³øêF>“wŒÓ½“4캪4!Di™Ú›,нÆäBÊ‹ë±Þšî  ù mû.@31^ }wÓ¥÷Ül!…ý’>MÀ‘éªr¡»ÈS0UæKߺÛ>̃fê«H²Zà‰•&¤`,ITéUå:†¡áø¶ƒ*á‡Ð¶Þv˜îÐìGJ´ýåãaŠ:üÑÇíçvf±,ˆcì@h c î~ ý¹‚;zRÚò+«NJ ÓÄÉX;ˆ]³«-AO©þ)OÆ@JµÜä<"€äÍÒè2ñ'ôNýÅú0# ÒÆ¼s,§ `Šö[\ƈL‡BáW©Ôi˜ëG!S™ä)òád@›äŒ[Ô|zeoPòc.DµÝsõû*Ò³h1êGOEþÈvE®ß î!šB†˜îM¾·/¡3•Þk$ç-pê—õoRXËõõ,\Eyû‘ùÀ›<.Ÿß}š!6ÈÙÄÒÕ ê•Ž~o~‘ÒnX1ìÎŘ·J%°/ݾ§¿mÜÒÀûëe!äV­ õÄ>‹z‰“rÑEóÁ #ó¦\·Àä¥î°§Îë?JtùN‹0ÅØ U.eÓÀÔæA™þ{LF†Jq—»꧃w¢èg7ñXTŒè–½¯–p<{üdÀ²{mòšß¾’0­6$ïf Dñzé×Ñpeù€q¡ƒÅCðH`;J n×*³¥©OÑÞºžÌc§¥ õñü4ç¹ÙÁ—C¸ÆK‰ÛCE¸ìÜì$#Ý Ý>ƪ¶Ä¨©C=_íŽSL8ÕÖ§á9.twó$TVÚf4‰÷ÓÆVAr6›á ø1‡K5CŸ’…Æóát,{m.?û“·ºf·è &K¹Êg Ä,ós¡¨ÙÍÏÈ2ƉOÂ>ž6z^·&íÝøG,غ٤B¾eF…Gì.Ú¸Ì͸£”®tZ ¤uꓵ#…®ËÏ¡Kà¶X¸¡(pÌ$ŽP*}0ϨŠ8ÎpV†ÂƒÇ.—Q+Rt5öQÎßw§=÷¥ëÐýÅ VpÖºêöã¤á¡>÷rARJM-BhSŽs§ïF8&è´6’#5äÇõ ´Š€œ8ÒñbbqY¨Ñ^á0üd×]‚iÇÉ÷xÍ»%,ŠsÿD«Aá` ~ù’Ñãj˜y\é1<鲪äÎðæ)®„=𜬟¡ÆN¦uªyY€x$ÇJ >Miýmõ°ñÎ+¢§™4G¸_…Upß+!8r%œGž’íöמu„z]%7(æ´2;f%"ƒ~&vŒÄMsW›>­!¼­Ö-_ ¤L”÷‹/*è7ïÁ‹Î’¬LL×õB)|–iR"û€î]Ãg&¨íÆO7éQ87õC§®1jVàvž l#ÖH âLÀ’_FJ$)û5eÖ¨ —u³•WFî¿{KR¥VÀmÝ@ø¢6¸#+ðLoº³ &™ó o ;Ç?m.’F†ž:ŠSÅÞ ‚ºˆ8—ª†Âß&=]%$­HÞK¬<¿¢³+â‹•áûQ25*tk˜ ³WØ Ç¦ȳÜ"£“UÒ.M´Ï62'c[™  W1Ûš\§±; nØJexŠíû&a¶±[‡«°¨‰¡˜óP}w™‘2stœk~áÏÁ¦„#%z‰T!À'À:FsÌÔã~þêU —k|–0bó>‹FݲPɈ×ÇÙ¤’âk$ ­Í¬PZÄs6—Èk•ëžý›ýJÉn:E›Óó¦å%âÙ¸øîž¿¥ê ¬)™_„Íýà L!É?œ™—^¬Þä\Nûƒ–SDhEzÉdü ‰%“Q3x³~Ö"²v¤q˜Ìð”RãO ýub…îá-AxÃD-jÖÕ¦J[Q4E z/Zþý)–ËÀŠŸ¯µ:Æ:)†¨$VL÷á¨=垀 KafrdT°à‰8«¼oÿ|\dí9»šlNwè1¹?*æUâݸh8Î;Bƒ„Ň÷©ô‘¢LÎÕm£ÕE\Bý6Ý;:‰1]MþXß™¸À6@‰.ì c$îpà^Œ¥-•èÄßdvæãL[Ùÿ\(ж§1&·““P”&.Mi´#?Ñçzíš~KÖkÔ/p*¿QѾç} o¹ß×Çeqs*`˜n4¤–UMÛXLÙg·¢¯ë‹™&!ML}WÔ\TurîŽè¡ç¹]`nò²,•m òUE qéúÂ>­ìœ£¦‡˜Ð£KêÝzÔä¦H{e¿Y«Â6* ~·Åþ*1k๿fþæøšds"àA× ƒ aºµŠ–ÚÉÕ¥‚Ò×7JjÀ~Jš¨ç\ï 䤒$à©N麨®F5_‘[H´Tüïv"ÛØì³ïÀò4„4¾†8L<á6ñžjíNûÐö"é0ć’Ì~§Ÿõ»”¾!M‰^"‘Ö‰ùúäa–Q½ø7Y,£%PmšAÍ{¨Ê }ÅÚ-ugÞ}–}'œþE6çËG×Uª¡Â«Úþœ¡äûpêäþ¹#ß/?W¬ÍôßjÅÞ¶ }£‡ŽGrâý(…ÓU·Y5Ž,š øx,Ñz¼½§›ø®e… ¦ 4w!9Üxd… ²Ê?zcŠÖh7yÏ0úÌâÖdo Kmæ¥Ü#B#o‹ ×ý]_zS瘀äKsJþ”K­!UNN’­çåÎó1Ô\¤úDj®RÜ¥øê»R'Qn4žŸ{ àìi‚#b4±¦©2e³GmÌr}*yJ|ä4oáÿü­-p¶§FøjÎÜÿ UÁý5 æã;Õ“ór¿vHÿ˘ä?psD™>R”xn»›Ìh¥^ÏÖi6—%Å=(™½LE¬V¡US?îµqD;7eXg9+mé*Àžéjý÷%n½þÝ7k½ÝEË–çT(vÉ"‹¡<#’Ñýp ¥"Ѩ«àÌ8çÚ™_é¬ôi«XÝJÍ£TÈ5!á —ìÌx~ÏÁÈš¥8Ÿ`ÓÈ_ǹ‡¬Ý¿mQ7m“reÎïË´®à(8P;äÈ« ‹ òîZ¼/‹¡0ÒòòhÑ/¹8þ,WË•OØj‘D,K“•ähñ;å=/Âý‰m[rªHZ«âcOÿ}¥ÝRÃQºÌ@¿XÑwÑXÔŸxd]ƒ‡Ð xCˆ­Ž¯«c[9‘]ŠYíªŽq¬çÊ3nXŒÔ Šià4T&J¡=ªTvƒ‚ûÚ ˆq)×ô ‚{Û|íA'ªÏ Ž!=«ècpé×þÑaæ#³ÔjPNx©×äKIÞ…=­†ùͶœÜÉçèìIR˜v3 ¨V§2Ùx̘j@lÌ¥K·ŽÈk_BøYv…œ´iï±Ì/WŠ5E×Þýnj&Rk~lÓ½¦ÀÃlâ“ò¨Å[Œ`áeô¶ÆGì°"Lj۩ ÈÞ€/ã’ðñ^;¾,}v C¢Õx–å™òžw£Õ’Ý"!³ ¸ñæAIs»óÊÁßlD{Û’àæ•R~5Ø7÷½4©:•4m‹Î‰FÜ&,—Ëü¾FJé&ÑéZžœgÛκTŸØ¤R‡±Ú쪀›¥áøWw ƒ¼gܬØÉÒ¢Ê#-åšSgÏ¢[U& t:fGÆ"/GŒ¹ŸKÓŸFõ›Èó˜Ró; /*îVÑõqÈ1ï °04¶\ÙqéûÜ”%­µ+Q+* #²^–qÕ~_/¦ ¥e]/‹±k’mäÌešVŠ­›3X‹÷‹KÛ%ÜqáÄ&ܺ>œ>œ2”å#¯›¢3÷?ásYÄÇÊÇéW%åN*§T¶x£dßœ1﵊–öul\é\ŽÉ^W“Âf‘+ìð>™ãÇÖ€ÅO—ô ¯W dsÙÖÑòžŒ¡e”z“']ÿafÑBÑ*az†¢îÃÏ-¬™/]0©tP?®J}Œ$èŒf‹!–é‹yÛ< u_IZ5~¥àÖÜS)3êø1è¯þ´eJ VÜG,vÔ¥%ÍðrU¬kO§üì;ñƄ٩– Ý‡±‹]´W´Õâ…”óÉf“xÛàó<>:Pšëö™®u¸Zóú¼}‡ˆáÇ5?̾¶xSÂxv.9gïnrÔép‹KÃ'løˆ°é2Rè6-ÑÎ(j4tÞÄQׂCOèÊkMÁU P/Ǥª W31[é[ IÂý%Ýèã86pš=~Ű­3*⌮0y>í§+±KI/vgK V^:½åø:·2Aí[Š·Køœ¢_‰=‡ÜÀM&ÛN xiØq ¤—›8šúù»RSS¸(ÄK"÷—D³LÆï? éæ¿³Ø¢Ö(ÆI@eZ8íª»«9•OÅHY "{‘›1&»ºÄúã¶ß~5â02XKÜyDZDˆõì(üY…ƒ©*ˆ2ÀV´€ðÀËAóQž’Å™Ö2·ˆkh ÷6”½†¯L¯"Õ<Áð.¿^1›×¡™»cGý¥ÃÛqÛ¦Èêá/Û`ïSÔPØ)9Žõ:L•¿°ßJtý I»ú~þè’§Ê„ë܆• Ž™”nÆÞÑ93eÁà2òx1§ 7bØèà écC–9Å%% KÙu¸x'¿'é"—˜m‘ŸkžÐùB;‘7*x‰œ®S±äi·¢Îy;EÏiû*ü¢ý ¯ó¹î¡y–ÆÐÑ…Øò³ÿ<7Y ÕÅÝyévÛ ²;’’Ýļá~œ@Ù K{úÔÞà9eýÇ%®[ŸÜVdÂ}„j_óž}-û8E);_€è7{,Y3%Xî|ƒ¼s™É¨7³bUŸj: Ð&Y5áhÀ0üÆg3Zbÿ«ÍÙ-·Ü¤œÔºÓ97Y‚àì’£aË7‡[ƒÜ(I¿×Ø„Òé°$¸BbÉj$7 Ê:š¯ajª¼{&IÀƒÙ€T—†á|¤ECZ\Yj*ú4ú‘r=÷4›q–dN>9 À!=ËO s}Lb•Í1Ly×QL¹è*÷h}î×û8ïÍM‰â­{Òϰތ¢¢YÃÀ—:ɶ+œÖàreP1ÄÆò5é°²¼DjÌYÎ’PÞÃó\;ä»ã[ïŠ,—u„rÑxì4ê¦0Øw‰¢ê¨%s ¾q5î œA1~‰’Äñ³…åŒay± Ï ûQ(ƒ»ó7µ¶‡Ÿm½KµÚQj0±Ü`‹Á*E*$£±×ñ­ÏED·Û'¥-_7Sï(ûô¸®‹A 6dV%ѺYå'Áròë45KØU¿]"¼~dqUô05ÇWŸaàCU äv«[*îoÒ¬X1¢‹vDJÑØŠs({â˜*¤È ¥Eù±%"†ÅIu¨g•uBKÿ(ÇÆB[Ý7§¨Nù@ãüB.é÷dMdqòËñ¯?Ù[àb½U05;„ü:ËAÕüå-s¡Bp‰Àj0ü£~)£=ÅŒ—sÓ—/öà>²2biû"øÁú‹ä®i£J"âÏ "ó~j ¾y!™LIäÀ «;AÁ ¦˜6V®Ðxé|Þ¢Å/&œjN)€ÛÎŽ•€‡„ËqïmýÎ_è´ãi~oý|8Ìä#¬"^!iŠ+×ÖäX ç†Ù e=w!}Ö|&ó¡ˆkê»^­è1©OzŒÃA³È ¥¤¤›õòO0ÑËÏ ‚[‘^ôáÕC³?Ô"I¬ù³èSC& Hû`νU#O¬~Ä›Lx§ÿ!× uÖ¦pQ:ÃbùA?ÔI¨eÀgñš”Õtž¦ãYæ•Ñ÷Ò9’.mÝyòËލì§Î!^;øÂoó Q=Ñv_R^Xbö=äx[tîßÙõ窃‚qŸN—JûÔ\ùÓÆô µƒ•‹”Mæ'Õ_!ZÙ}q$0ËsdaRÜZÒµ{ 9~Úf7ŒÑ˜&`«€`W)ûM˦J‹¸ÿ®b„dC–è4øaS¨i( kRm“*Ú˜âéZáÝï_ ±?[°€ÐYD+­•2†sx%"î½QÛ[,$³øSõ¥”sVŸAµt¡¨84«j”¼ÅÑËD"7QÙeÞâ¶±f¼*­¡¸ç¬Ðϱ—jÐ.Î}Ž{.ÿùÈmAú¤ãÚ”JŽ‘Èþ¨Ò6ýÉdäFG±ycb¼-¦¸ÈÛÇ»¼…> ¿r”ÍíxÕ\uøÍ;±_ˆ“ˆÜ‡Ùû üøèiËMJ»/²m •{8ê}Ú*"p„eŽ&½®y |«ŒXÈÕsѳÑ÷SK¥7UC(O$tÑA—Å](¢˜ÖQŠpåüê<ÞoQTÂíó@ND(pîjÍÿÚ ¬+(:ûªÒäµèlŽ|æîDƒ×>äøQ„•ÝdR&K2"FÓƒ¡˜mvYæ­¨vÍ úä|†'ÛíFËÃmXµ¸ÆQAÍtš«3 Ýüõ8g’úl‰ƒvV˜ðù.­´¡Ç¥kŸòžÐO²y!éãZEl\L™ÕYkXë «ôòu¤‡¦²ŸJq#tÓ ”P–ªIû:*æ¢%û›ã —b9ÁÊ]38Á¦öeFEð…±+Åü•ùŸÿ¤·¢ê7Íïš§ ˆ ù¬Õ9t V ä•®lÿJ÷ ƒpYœ2aÙÀ°Y×ÿ{‚`Éæüèlx厥ýåü¦Pë±¹GpB~c½SúÒ…®@ï·»›ƒ˜.uìÌ- qÏs?“â¤e:3…hg†£WÐ9ïmÌí^ç3~wáXc³ïŠO‰ßò"‹Ê(y4ÖÌ,Âj ¨ùØŽ»ÃgÌŸz#ôfW’ÔE*l¼YÜè8*ÛZ+€å²Vé…}ø‚€ÑVž\§¾,å¹6üˆZv«;GîBç[k×p<-Áç=©ò§ÆæûD]^àö„Ù¯û+Ýø2lX&i ,!E%+‹ ⹄ Þ£T¿ú9M;-ëÅõ;h L¹”ŸGNψ_À©´Ý¿îôüC‚'ÔÙdCu—djðW„z|À»I‘½¨AùžÂûø0£fè!½ÉœìYÖej–-âú´6:Õz‚íÈDæ×žÿcka Ö¬Î)%‡ö{úñiaæ°ÎúOÎì•ëNLŽû%ç¶î“ǧn™¬ÈUŸh ü/f8´SÔü¯9-®IÎD0B‚í"::cTŠæÜõ]s‰—–ãÅÝTá¶™¤|æì1Jñ¡Å2ujh’9wY«.‹Åé8ºÓ©MÓ¶+þIÎt'X›±6å~Ñ”ªòXtõ°ÈÖ  iW«¸.çDÚøÀ]ìLiêÁV?t’b@”)[­ž ‹æt‰ž¬w-L^Ÿ›¥ QÔZ>³©ÓÛ”<ÉZfœ£À>­¥©û¢¿¡ó…Ò×a/DXà ËÀnˆÓ ß¡d±1É© bkÌñ˜ø·ÁNØéÚ&·mŸhŸUÕšÓ ùcR|딘Ða2æ6ÍÝâ-3H#‹laxe BÔdrÆqŠ1Žª ªž,’ï<̽Y®Œ²ó%y°mvi#<’­ÐþóbÖ•Ï7šæ-æ¾m0妒ªZ Ü ž²bkóT~Kn»pô‹;¿*T¾µÊAøÆÊ•Ø!Ó½§§S¡*&ÐV &õ6í”ø¿Ÿbu¶gPõ±a˜m¢o:v;ð–ï²ùO†'°Ì~æ€}ªû0-ÌŸEÑMû[£Å²°roOý¶užr(‰ÈÄÂ,_×*Gê¿òØŽó2 D¾Þ­$ƉZ%‘ó`°2X¶!WÔñ2®± ô9ñRùÈ·’›Íû2Uxz‰Gm”V^¹Íæ6 GtÌ‹žtRDtŠð÷æOÏôˆv:+×ë’ŸÄYòZ÷˜E~ŽÍÖeÊ rFÂ-š³¾!éhÿ¸¾Õ¼ÍÌRÙ¥õÂycPf›\­l²[Ê`b‰ôJ‚¸å+|%Æñ G´„Y íÙ"+Ú®¨T =RdaÆ‘´^õ ¹S³Köø5ŸñóØ å)¬«ßâÂ&¹mꈨk? M%c0Ýþ`JÉåÄ¢ïA›¬[ðSZ0›ùUmS´Fð &¯rcG <i’ÔÅÀTdû ‡Î’Å’rWYlq Øz\v „ãÜH¤Òzq:²™|a#Èrñ$u[SRýP4M÷ú„Yàè/UwüÅ`5œš,v™tì³0Z’X°¸²’•å™;=Óó«§?pšS•P6¶ty2Øæû‰¡@X\¹·;ˆ$Þðwô‘dÔ=¼âé-¨N½í¢Ø Rtˆ5›¼¶¿ËUŽ:¬DˆÑߦLz/m­^'zBRQ’NgAõ¦ò´¼ éK2µM­n &ð3­ò:è#â3)ûÇ8C\T¼Ó½é“¼È£Ø&neÑv*óâ^žá0!§±»`j`MŠÙu£u9;”ŸÆi©P¾V›)@^ÙÀSY¿]Œm5µ˜zNWÙ¨‰xœ-„ f²”¬_Ÿbôqúç®[Œªé~zAd›¼Üv]8º+àN!ñJr£¨hp‘4vŒð½]½îâ€ë—|` ð nÏÂÃ'Þ–•î˜u˜\/ßüÝõË-œüHݨÃaâèë…ïZ9ªÄõ­¥ÛL…äŽP×gÇögêæÄ¯Hm&H µ;Š¢ˆ¹Ý‹Þ˜ (žî(O*—ÄyÆd¤Y!D{Ÿô"A«. ÞÁÈÑñ6ß W’ª@% =µÔc59x…ÙJƒÉÕI?ªÑJ®LuAL‘UØgÌØ¯û;Zë ÆN“\Y’¦)Óm3ÄŒ&WÁPu>o鋌s›u1ašPÖP[ìLg}NoQ¯ÒyQj¸yD¬ÛžŽû%VÑ'–í/(á1 ™#þ#éZFºz×sðу 2Lb„¿Ø£rèj¯Bv÷É«°ßϼ¿îƃ¿Û)M"`¶B™¹¼Ë$¦{k-©ÕÙ¥CU»?©ùä“ÏMñ"3…[Ñ£éËÌpf‡Î¦Èî%ol“ÃuyËB'Üd\J¬Aá•Ï% ‰,ѵ~EÔˆo“|´–ä±ÝÿnÇ"ëQRÐS0²d€Ìê}p­øUz„¾yƒø‹Ö™P_!j¾Dsã€Ñ Ò:ßPÙt{HÔ>_URy uC[mN™‚¯³ça1©ŠJõR½IñIÝœtÿ ¿Ÿ/€Ð=adÐNœ:ôì‹Ã‹)‰«Å& î]éB„j? ln‹w‚êWëtI(L;ÇÜÄSHN¢Ñ̪\UÍb5“Ì@ógàQƒÜnF'~µacîÛ¸?özj´f¿3ïOðÀP»êJ»k‡N\.?â¨æ/ñu˜ºÑ!çÐE¤èäpšHÓjë,CÐÇç&”:$ŒºeŽšZÂìMe)¿ÝWT—€±lý C"׌÷¡>6»P—40ýOMLÓôa Ö`#ÏCѨ3Ø×S¿W5gEck€ó|9ò÷¿J–Êñ ÈÇ4§f_DË–›] —”îhK®jT)9ô ¸>6âbðÀTÔÀáíL8œ“±i¿-èV&ôp²z©ai 8S°×Q'sT9þ®®˜›y¥sŒŠ éâ;Òkín³¬ãRö{*\tÆoÂçåï'¸½ðN \ õòÆŽ:¿KÚᛎ’ýhˆ LJÿÒ^µ­”[½ÓáoÃPëEK¤@†Oœ& çC¡ôµ$­K,³ÇõÍÏÜè~?î\‚ +~Hzÿ4>÷µŒÜ:äÚÕåÅ—Jâ!KÂ5ån”$ ª|²ì+yµK©ùs²Hþî5ËnÛ]óMc~‡UÅôyTBfI³×Jugv/ø9§«šŽ8ÔŒƒ—Õ ñ)ÛÊû«[ÉÍCEÐBu4i}¦[ÉîK‹—:åëÕ ëÄ]³äã0ÄQEÒŽr¥ô1fÆWÝ¡Š3#b¾À<’:Âùo3¥:¶B“öêŇÄqÜi¸Æz¢weà°ÛmY›Ùoõ°Ÿl¬-ÑÆžKíÂøD4É1ÅÑIzîx>÷Ðn8áõ ‚ÕÞýW4=Üz#9£g—RCìÐ5Ìͺ{иçݰA>ȃª#u¨ûú´Ì ÕŽ]ÒPe˜âý@TPLÖwµ™(à?Xkâ= vôe§\:Ž‘JÑj"žN³Øq˜hÁ(.îG?Nõ}ìwš $Ę €O¯0w3·U¼©¦ úäu#…¤̧ '*:!ŸKxÞ:ÏøøE©Ê"ð•nÂFO÷8_Û/-!ˆQЬ~9w)¸ûr£4• ?… 9#=l‹álð^Eõ$‰‡ñ¦Óó¤/op๶´œ J •æ«È6ä‹“­ã“‹ˆŒÂ¶“yþ‚þFŽH»æÚÊÓ§é¨TæLl9ª¤P1Ó¡/p@î§}…iéeŠÿ( ŒqOây$C?ÜÊ9cÛš.?{ç(s¿;J8îŠÒ±yP+^`®×à–IÿŽ!ìÆ1õ{ש'!"‚u³ÍÌëÁŸk!¤¼A:Šr«'îÕm:¸™®œ›a¿oGHÝ ¼ÿþ£3˜«ÑuCXàÚ"Å3ƒÈ—¡ ‘Sý'LÆâ¶Ê¯þü´|ÿ§ea5ýñüùkÝ¢6õ¾-(â®:yüE¼Rm1Ny§•´ø¬ÿA@¾¿ãö}³%žNç:‡ÏÙo„â!yes•6JQæ5üUGŒœ¿šê·ØÂ_!Ïä·õ˜TâIhƒ; ÖM3*fmú*?×¢k«}£‚{Þ €‡ñ„Ób+b=~ ¼2¤öê.m¤P©êÓmSýìƒáþó/O®Å°hm† Ó†gñŠLFGõ—„„Ùã 7¾ ®„À…F¿’œfž“fY &'¬˜7º§WWõ°¨.ŠéÏ!“8¿ÉGDâýãøqB¬aR†ä¬çòit: Tc¯S}”#̨YØÉ›|np¾á¶˜X›éáÈéX*Nõꔵö( É ¼*bÅ6àl¸¾u&§‰™mt­˜€£8¦þ’(lÍÉË@~³¹]œŸJÿ8³p‡¬X,{d§ÃÒ +Ýb鮳Ê…ÄÏ2?Ú1‹‘÷Ü &f0÷DNÐhµ\F—U#f¨,.t<ì5=QÜõ@;;tÓy¸.¶œC€í@#ž¨k.\†‘âg̵p^;|zñ”`Tµú_ÅOкCfn{«ÒÉ…Ÿ9>ÔŸq#íûdŠ=aRòÁ~CŒ c‰h¬´û?wöO5Šàc‚ Ó?È\@äÙ~Ñì-¡Âáê%‹ñ¯gZ™_`¿£r"¹Â³%À+¸S}+'ÝC¡)ÇèÿBu{:É´Gp=8!ÚR ‹;³èÇ)[wkDí(øc¸áø ÑÚîŠ%Ð& ÀMÓ!ˆ .Ìy(-×±w*œ»Êpi ®‹ÇyŽnÇ7Q‰¶ÏΊ‡XInY¸Zûz3ÊK[@X`<ñúy¥hV´=SŽžÎ}™¯§;Wã6OU6DÞí´b4ï¬kn~Ìc”/ z׬ð¤\©RcÞ”9¨€ÖµÅ!M) T×þ›.b~ôž›YüLJEèxä[FÀ¦H•]>¨É5=”ù.ÉcölóÏ:Rk§ß<ÔÊiï«·8ž3qh’¤‡+ÒÁàˆšB×ÿ´šØN¹Åtæ°'¼uào_”½7x¾˜^† 'ä@ˆÃË*ëÄ Üã¨ôz•HH^al@šÄÝAä¶¢)Б²SëhðnCQŪnÊMnHÖª hÚïÊ7v¿¨µ!Q‚C-Vƒ)Mg§ÁývÅ"3|¤ICìr€^“_^zåÁƒdá³gAéd|MÁöŠXìD Y ýÏS/`7€÷„ ~õÂMcâ`@ÍßøßÖ]‡¹Cõ Þ„ rï3ýйˆœ‚ùOa æ„" óžÇ²2ËøħDó1YC.‚pÇÊw䀤Лj‡aXŠ1ãùŠw»Ö*:#Ñ@Ó”s¿|»¢3ó{ž¶@UŠ#è‚NŽb1VŠ›vô‰q˜§ ù1?JÄ'ØÃ§*¿ð+ýWÓ /µÃ›…¯T Ú Ü'¨[1iN{aùÙ´vW"Ù-v‚FU ‡r“óÿnÙ¤˜Ö¦">a‰ I¬û`&XgΔdùÃ,YéOvAÇ?´ún²7øí”W™W' oÖ@½•Cæƒò7,Í{`ªÒià:rÅÍ·2±(A¦ÝÒ¼Ÿ}9ôŽ&=Ô®ŽLᦆó  ,¿H—†1Í$5fâ.hø×9q4£S£t^O¬´Ö¥ý„Ôóus÷ù# S’gÔx k %{÷­”È?A ,}Ò£æä±NÚ/Û³k:>D•ö&°î¡FÒ*Ã4WôOAgYgm*fì+rm/@«ò%3ŸôÔç¦.R3Ýò çÈl ÍÕaóÞÐ5/ÆlowA¥B_Jœ×x¾V¹I,ÙQÁ a‰¢ÏÒærYe¡=ésvZMšZKàaZø¿j^¯øDh¸I•M¾®ÝWÄ5x‹3À!µ ±„Pj ïtc0¼˜éÈšª/€C¾¶¦‚š‹Œö{/=zôØ»à†%LÞSv^2&º/•®€cdŸŸ”½•A¯Š‡0s5fcˆ┄¤ÿ¼F±€±bÂ;À´ÀÝLůã@$1²ÈÚ†ÃKÉܤâÕ††ˆ§x|Ñ »šÊ·iÞ‰‘<ØúµÈEê*¡NT·¨ÎGO!>Ï-âèˆ =îÉ7Rh·–íuúž.Üžn„Vœ»Ž9ªí¿Tf0tUSÖOùROìjrdøŒë~Ã5Ž’;9$t㤈eVD1f)ˆþ§hÎUZ°ÚH½Rj„°Ë´X@fÁd #Ùxiï Mpá!¦ãÈ2'IX®×¢³ `(ò!Z¤N†ØRýÆ}¥Ìºyî¨cºÉíÂS[f«TìMD9Þ¸ÑûÁt?öÉ*†Y稒Õå8rêQïQ>‡Âš'éǹÏ7D…Ò÷:å…PM›Ì¾¿.9Í;“Ü£úÃíÝ‚0Wfà Œò–utlZw¿ãÍuÒ\ñ“Þšð*ø÷¦èøKT€XÍÓÆ™ŽÑ4cúÞs‘&Ø=<âÇ •S‚ƒ.2Á ËÌ£X5˜[š“øã° ‹æé$(x|GÓ€Šw¸ñ×nkö¡øÛQ€à8 Ð680{׿\í¿dN‘ȬSy’‹d€öã½îG,º˜S á±¥ò>¯2½V…æCn«9]¤wǦ׈#w³üAi6ƒWØ€Î|‡^•fÛ}›5Ñîf¾GnØLÀ"0'h)ÂÀ ˜ðQP.‚РŝØÃß)=¡PŠÄÒXf¾îk½Z#œ-ÍÔðhƒ“côvÝ͆ÌþÂÎ<2CáHËæ¸Q:|禑 f© p‰åÌÔ6‹ïÿ+ÏŽ‘‹þ š²©Ê{¹ óÀ°`úö^‰åI1X—ÈFD–Ìmê~:ô?úL2`gX,¢U±Ò虚:À@+½‚L;µ$[‚¤Ru€{Âó e^¢ùh²—ùŒ6‘’Ȭ¢kë§ÜEûÑ0^.úM¾Su(ˆî-oÈÀ§Uç)4´µmÓ½¯qq¤£ÇvWg5$’¿©§u…u ·ó‡Th;péâ“Ëײ;¦+×9z»„×í².öÃõ‹>êöVãat~Ð@ 2SÑÏ?[^…ø?ÿ0«'xÊä7f|eÈý@Mk’©ž§jù®6cP¶k8G mʃ.¨ÜJný¶tµ¢eR§ÇÕL'™ÇÔà<$öa©mã© wJiˆ4vV4Lû `e«"Gk°Ÿ¶™(À_'d¯d<þðQm‡ýæÛù;®()±vËP{¹˜5à­^UÂøyŒ“ú5ë?ïÏ£=!°0T†Ò{”HÖŽ+ZeÇB½)™Ú Ã®ÖDG¹Ìg÷¯3¶:úò_<÷ï†eèùR(Œ¤¶‘Óièñ“\ÚD¡€¦vu`Âí?/Ì8¶½y‘Ô |Vmi Š%¾!*€‡ÑŒÎÿ$f#æSv1ò’'ÖGT² øNTw™æ‘ÊG7€ÝÕjæ Í•=Q3Ñ5ãÕ#pxþë·7Tîc<þ#Šêcù™ž2…lv‡ ìNYÒ¡ŒSIb~3jµã[!‚(`:KRŸ^èIÕq¸8}ÎŽŸéO­ï‹¨;Ý$_Œ'Á.Ѩ«-m`&„,îtNçªÆ¶ú̪9Ûõ³±Ôs9óúV&q©Ï}Þiþùñ›>ŠFMÓP£­m$sµ*J‡hùvr>nGÀ`EÄe‘¾òl@x›s31¨@Õš¾Ó˜hø ìª!Ä ½ Ã&цÿùë7hÜ2¬xù’€Ð{_òãTþÕEŸ¥ú=Cÿ!à'`/Û%"ðI»T_ôÚ`$0øN4ƒs/1HÞÐ8¯º&Õ´ŸÀ-é€ ‰N’9ÈŽàíä1ø6—Ë ã´—G4såqGÉ¡©&s $”þ¢a^÷+Û “3‡¨’7wî¿bÒlÕƒîd;ã#‚yÀÜ–dÓjãÍöF¹]íÇ×ý(éPxñ-ü d³'·CÅH £QzZ~Ùcíukzç~$Fºp,Ôân-ÍÁ¨¶—‹[^éð1ò Ç-JëÅveˆ|ŒydŸ¾”$ý±MvîøÆ÷£ á®Ë“ÒáKÞGô¡Ð^Õ±2ª9‘’ÒL“—jηïYzu#§;,ÇaxT“Ðîܵ¯é_<kACŠ…Éb°pÞ+ЖËc¸GÈu9ˆ £Óü\4^ ’×¾wÆÏô”†ýÚxLÅVR+×uœN¤$ZA¼ó‡[ãÚâÚþúé Í+ll v/ˆÀÅÀPS\IÛC`üá]:Ž0íZtúùÇ{Š«Ýo½Z¯%]² ×ò¹")kVû^,É\¼_z`D‹€D­Îs¬ý‰awñ÷­/e4Ü:×U«+¨q&Ö<¢éÄAß e½Ýkä”åkPW¥aÑãÓŽ’ËsÁFZö¹À÷"Y¼¤HGÑcžN/sdµeH¿ÐëF1k$IŸjGm6Ö·ÀÁlòé¡$j;[!çÈúÆâ;‚ã:wƒäó@éniÉDN}z’†6ÏІG]ñࢄd8{²U¹øD™´Z®ÜÈKÅÕ«6t`»¶QíçµßȤcÚ´ypm(\Oc™zÈÎó\­Qûåõ»?¦Ú,:¿+>’U4—(ÖÚ YÁïdDS—Bîš#¥r Ï“wôøMêBZ0ûc-J-Ñ»ü:„üI›1V¶u¹Ê<ʽ‡ý²I|gúpë¤:ݶϖ0èö«Ÿ„áÝm³1K4·÷ª;¾Õ½Ç^Êߨêáž³‡¡üXo0ÛiY&vKT ‰ñØT°ÿ†RŠÙR<¯V7)IŒ¯²ôÕ &pÈ4é0?4ˆ$¤›ù;„ý<ñ žï}I”È0$6ÀyD—Fõ¸º+¬¼ÀŠø¡_JMóïûr'Ê—ü!ÒÐ5l“9 tœ¾—P¸!ñ§¼úâȼmš'øDØ­ƒ y«ðêØìÔêr„mþíWó<ÎL¾É˜¶)LI3¾³u¹¥= ù&þn‰ç"Ç,²Ý¿±kïzOPßx3XLj½})I3;")¿t¢(†Š³¥Ãè|xÃð–!™H ¼­¾õ;ÝäXIÚ…zOØ[–h/Þ¸S„’îJúøLQ Ÿ]W'g=ˆPp0?ÛG܉‡N:ž|‰AßÝ­TœsuÔ®ÜÎ.RÁ>Q0´ o|ZúñùîƒÚˆ× G2iÊ( jÞ…0 “ؤö´û]õ'G áÆ>Îòïª9jf€¦tj 8-•)µŽ|愳šÀp†¼´|"0ßC¾Ù¹*aÉ‚jïž¡E-‘0\ðTÔ¼ ­ÉÆü¿©?­Rè¡•hIuWÀ‡>½ÏaÌÝ"5JOV!«3±2\™ ›h…ƒ1îxuÊÈUù…c±Nùá êñ•¯ÿ”rŠé5E48Ú³Q#Ðâi”uˆMÚüs¥Ì’54(ØYeõº†òaÏü±â›BŸ—ipÔ-æ¯lKy+C„*ï$Hâ*XÓªwCÇ$êÐhþÑJÁò*O äx{ÈsŽï$wWe€NÓçH×.‰ÉKo1 çHúê1î$hYÊ÷¡ê̵²FÈ~«w*ÅÓxe‘ÝÂÆhxãvl² ÄŸc'C§úì¾¾žZ<µ}­ò¿õï/Î#¥b˜£ ./êï½iùÛ™&pf“Ëúöö ‹µçÐ¥ 3’¹ƒó£¨FÍJô1éðüÓ¥±c‡U-UÚêh=6%tª ç¿Ší{«M|žlqÁ”k×<BáéTaQ¸S¿®1næ§Þä˜÷qè ÙCYiÁdr^ô àƒóìnÂ{ŽÓþ2ê^jÃlK¹¬^Ñ¡x3—ñ¹)“E®±òQüę}Èb´F=U#¹²\[i÷ ¶²œüñì«‚'ë>Ñø‚é àqM@>Ä;{TIF¶£Ýn¶Aï¿ï Eèsç9€%±ËpeDSoŒI©hÀÕ˜à˜êÜ–¢ø¶ýniGd/“â$% kY*Õ 1{iÀù@G8jEåëÉPOåTQ ²ßšAÔºØ_ÈL‗ƒOÎôœŒ í,êȉñX˨Àpx€Lÿ7̈ ª(}ƒ‚ƒúAÿ<òò,²DÁqä] l"ÑšRNx ìׯcBw‚è^F=Ë37‰ô–4ŒÿˆZšBP‹CœqƾQ©3‹â’QªyNÜgÞìc7úcÊ›ÁÉ÷^s:J‘†Fç¼—’Hlµ¤¼ÅøOº½þ3Œ7’%J>ê=ˆ<,G¹bYÜÌ‹ÓÜ©ÌÈ2ÀœEbBkþ0)¥bÃîÌÜÔ\òeíœ=傈N¹‹:÷4±³'jÄC8N¾s=Î9ÿÌ•ƒ9GCDù…Pá8ùlEd´”åHGÈS½Ò7âsõ^B£¾Ñ­¡RW+{FZX¿çÛ&6\ñmq¿ b¹üðKøÐ…švôn{Wòu~Üã2Ó!;Š0¬,ç×y~ïo0I°€]ú|£m2¢ ˜˜XIzfÎrI“Ýy3 Õ\ ZZµßvKåÀAú„&=`NS {kIbEÛ¾éjcñ—™æW1(h“fþ‹ ­¤‹P[†­ã`"ºÔ'O%B¶5öÇ”KïütÞñÖYŒ´ï.­çëí·ÙºýÏ&5S1PZ8m×9Ÿ¹™S]`aê¼aÝvï>´WDo)лnÂü «¬ ²z<<'¼'§c6Р¦ÔVÚgM•šJþBŽ"¥¿È9ìÕnuö—†Ú €„ØfdMÙ‹`hp­à›Ù¿=ÖÕР:âmÏRTžEŸá³¨wk,„hÁL¨±¹§¯)VPz;pZ×Z î§þ’s5<ìÐ{¸V4eìì ëBô?ãfNµó$U9²P‹Í^ÕvØNÓýc}Woy'#D¯°%Z‘ÿÆ”ŽN5ˆgéüv©¯úÊëÿဌ2¢»°Ûï?EÿÇ?"(ñ.˜G‹'9}EgížqòéîËÃþa÷I?ßYFh©å/“ܤ»Ò-î£9æí£Öëîß•áÊIÈ9,YÚgdˆ.ãf?b¥Zå ~‘´þ­þ P¬Ì]uñ¥ðÅ6¥ñIßÐâG~@*°ÏŸgÕ¤Bbœ…“í]%§.Û—F°/+YÌœÜ´ÉØ´QœŒa~V›C/ ö‰rª‡œ§ÞFáÙ^# ORÏeT6]ô£ä ž/Óy´}£#= ïûέ‹ª8ˆOjiþ\7>8®Ð$€ò¾|ͯ…‘m' IwŸç9Ä}4„TÏò.³þÌF•x˜µd4d„TEÂ\ }h\°B¯]X‚Ct¬éƆx›Ê–¬`.´1{ÖR)>æ(•udaì†¢Ä Bèð:†A õŽUCe é‘PF-µîð æÕ[ ×ì[®¢NÓé }j‰v"¹j‘:íÝ;h%M‰ì÷gÎŽ'ùfBj‹O±´S„ý Ö>(„·ŠdoDÈ~î"ÈYk€…GŸuö==—¢œƒý }¿Óíðÿë}Ø *2’߿֭ñ³>…°0ž>ÀXüÙ"±2_îqïòÛ¼.hÛRÕà$ÀGÏe{¶—4dšJ¶< ôrÃïÍ‚Ý<¦è ¶;Ïè ÆÅÆ;vþhÀ²††{áBOÃI7‹o2=–XoÂ1]”ž}€*C øÒðΙò¢7l)SïÄCUäÑ/B,ž%Äß8Þ£4ÛÈ›TÆwi×qΉhûv²}c΢IgƒL$ÔÌm¨„ÙSÚ?›:ø“‹Æ¿& q•l ~jU ê/]€åË똬v vîu´pÜ ª;£ö<(vë(õ;´ÖVQz¡±ú1@<Ê>,¤ì̓‡MœüмLÀùšúV„‹ò"4 ÅVÁK4‚H-Î] nñ«R*¡+ÝÓ9~2~Á'HüHB›— .$áÞ | ',k§;7Ó 0ÅÞZGÙiUË]í:Ù)¨´-(9Xõ‘ “ayíÛ'‰Cßá|qmeùe‰v“Ió¶j·¸ÀÌÅžöˆt^ǘe§oœ-F`ÌûM_A+£7*'Ë^e÷YW\ñJX™¨Óð9¬ÛØ93—BŠÀÕq|Ry…5HÈ…›é‘Ol¹Ætñé©d†WµN^RulO˜-÷NsöäÔÇ$ ûtbþÉäWÀAJ–äuƾ,Cp–ü ÉBé•©­§‰«c{d‡Ó-ûhæ.‘ÂÎ~h1ìøÈ$ˆ_§a‰Íû3Z¡´ Åžã7ßN_Ì’ _ÀÖ Üò ­côÿ^å¢ó¯2+§x!)å¥ãí—¼<ÝÝEnsœ§‚í¿KR6ŽœsK x°¸ãø²Ýx(b·C‡±ïx(Ì={˜üõ˜¸ïÞM›ÅDHHÉ€)ÈTó®FFÝ­ŒæzR<£–Ë€óÈØìˆ0S/·ùbO|®Œmøu33Þ²Šªªô†«Œ“%SÎi{ˆ^qìŽ@ǃ\ÕÕš,i]¹åÿ›w¡[ ¦7ÿ%°[“žEϾIQé4B³.\-áMvßÕ„PˆÞd[u*aØ/±uZ kh;ÅŸó.ºÿ÷쨵@ð“~ïóƒ®WF,{ó±þ$Òºþ„QRpàcÎ<€ßI5ä ö.´õJñjmC)—+›ßûFhŽÔ™(B’&ÊÙö|cmuË·°ŠV1•kâœÉï¨×\žIÈÂÅO@. {û¤ PÊâÒÆóêX3ŸcüL•xvóì,€läLøâŸv`lÕƒonZ.B<ב¾Í?/%e}þÙ6oÄ­ù h\ÎJV˜åþ×T+‘?¼²ˆÝô=²±íŒË€½É 2]øßT|HQÊ`oMdwA&„[Ey7xx'¨ž7Í k±‘fšÈK†êá2Ñwóü8I€ó¦ä9°H£ kœï óò×2ªq½iÖÐøÉÑFÙÚgtuY¨³„?a´B~xGjØðøŠk €`ÿ}Ö†P?—-lBýlœ:Ã×.7Qò.D¥rîÈîDɪzú¼Óìë˜ý@höäÞÐæÎÅ#É M¨ÖJ&ù¼% 0$’J².r"—ˆ‹…¹Á/ý*tL½?›%ÁËéˆÙËNwÙ .ý\*RƒU£_|Jð9ÇÑ×V0Yg¸GÔ¬»&=I’Àâ¢b¹þâÑõ+å±^g¼R"sDî‘,Œ·>_GÎÕOÒbww»wªÏ>æ²'û/©+î‡â,Ò€.w2&HÚ Æ$êˆT0_tnSošŽa Jv$„&ÛcÉ1šˆ£úDœ?¸¬”ÆÝÖûëSJñ_tönøÑN÷}Š](L‚ÝZ`SΪ`kù%™#‚^ôïú¨Øxe*L¯.ä>ÒkóYhl‡o†Á®•nRvÍÓÄ)½LÃùeM,vÝAG÷FúŸ£y±mùkþ86Ô?Ì Ž• '7I¬¤¬1>P×-òVk‹)úœœ ÐH‡Cr*vd6wgƒ¹9ÿ €Z‹X!MOO{œ<Ît†®¢¼È•ìÞ€•½åy1Š“ò0ðV¤!·3ÄY4«»4¤¤¶¾ú¡ÝÒ¤šlDCƒ3ÍÔŒ…ÍiŠÚóý†vsžD@ ˜µu¾5vùlúTÔK¤uU¸Ñ#t¸m_¥ô¾/õ˜™›H²úó!.ÕK.7ZtÏhѰåÍ'›¼‹ºïiJ‰¦Ä?Q»N„ªß4én›?Ì«L¾¢t?Ù­÷¯28wq„Zù™e¦M£5ï¤6¾6>3§CÁÀ&s òfÝDú™Œš—丫•.ìþ*Àœ‚Ùô—SqÌÂ}£xì„1“1MŠzp|!ü•T~å¶Ñ·Èí…¾½?K› jxå÷߈É1ãó–—9tENY4 ¥¿êÞKdc&ÆÖ£Y;‚ˆâ}°kò¨~ï&Š¡±5¬ µ,þS¼ÈŒìVW½GöÈá¦$‘ahOÙJÂyõzO›½ Tj‡áG¶*È`‘i¢›Š¢:ªÐdæ¿,èðÅL ~@3X%¶,júØ ×­)òÎÊØ+Þ8—SvŒ}:¹xí³ÄZŒkw%ê…–¨íP¸5_¸Rû‰fGáb#±æR:eû¨Ñþ%Os q‡h«Ÿ÷qJŒ6; ݧ<ÓøùÀÍ; €ló·»ú·>FúÊÒHë©zæŽÉM=yªì g…Žÿ©×·ù—«ÒÎZª‡åIž…€ÄhÆ>êS»Gå^Ì[¢§£ÅG-ð4°"ôUÆÿ—X:)â¨qc{ ƒÖ–µ†éCغ¤Q¥íÍ嫚XUÚÈßÛå‚«ÙEæ™­ÄE)ˆ©…ž9É÷”ÅÄæ)—[z ÙІºZÖcw4VbÚ}¤ØþøGî]Wã¬T¹( •|ÁÉDçÿeä¦AÍ&ÆÀñ6§YPvKiÊPž<5Ek"È|UèÜĬÓáPÙ63?ód™­k¶@=àñ)0Ë.º2K§+X j¸ó{ ë] ‡œŽ¨¢ëpè\•CEdº=ÿH‰rX~ÿt±O³+Š„¸üBëì¦]%èÃ,ÊYbƒ+ô]ý¤èqPŠì1„• m¾‰œê)ä„Ãn3×kubUSݲL²¦JG½º?ܱõ¹oŽì¶ X4«pt˜r ûnÀ4EÌ5µy‡–ÆÇŒ¾0A¾Üi¶ûBÎÅÈh;UŠþ0$ï÷¡-ïY§.mñ3« ŠŽë%ÙRœÐ­Nô{oñeB6G=û“õ—3^&ÇyoReÉOà{;OMÔ4ú Á‰ßÍÿpÂñÆÓß0®>*ÅÏòd«“LåÁ–“) qÉ;Õ8> 1àMݯ¶(õ×G.E{ø`díúëÿ‘pP8Ë0†]a"j3g2ÛͰ¦%T‡sÐ6H§o»Üœ(MŽÇ¡®7¦"yAœ:/6 ztÖýÐãmÊ*‹Õ™E¤e˜ö‡„È”…,¯êKã¹ö¥!žf(ÆfiÛØúš ü-Þw£VüO²qØ¢ÍÜF„ÞDsU¼z(}ÅhR¦8Åwx&ën·/¬Ì†ø íÝ dšˆ<þïMtur© ]Ø%%’üþÿªØhé•bÚêçõÞ)n÷µ~o77«¶ª1yVXD#~fJg8‘—­Ÿw¥•Q˜Éî@ v9æ8Ή ôhd&p#_sÓ¬kCµwÀ©–à•蛹ýbaMäVÎzýk…Q6usýŸË½JOœgng-bÝÓJ›þƒ¶Z<ÑCÀßäÀ´ŒsTnH®íko¢Ùô „!]ÿ(wN-ßÚwJ@ßĖU>•¡Æ]ñÖ{ÀÆ ºM \ £Ú€"ŸÝq…Ÿe­Pk0Œ+x89óþD%&2$ðŒ\~˜¢ÉÓÜŽ¥¬ ôã•A2bàƒkÀ ’ ÞÊN²ßkc9*èâcy åü˜°¶˜`þŽ1ÚµÅßxÑ›ê4¬à«ÆöQœQ${MçÙÅ¿nñHümøEéõþ»H÷/ñ·"½Ã».ƒ[.|Æ2K9–¸Ü-MÂy:OiÁŽcÚðJ{µÀ©n-Z4>LÃÈœGVÿÂᘅ&Ø Ù(亊µÐ%æ:„k{ì³F!,½þz˜¾:$‘óÄiqŠXçƒí‘ù'O´—‡é*™¡èñBê~,F¯ð¢ûPôÑ èê0›H Èø Par¶ êK´â»®˜Øˆ(û'>i ©¶ œÚ ¢A‚F½®gØF úZÂXXZÝ¡¦wЇåš*¾0 }¹»«1Ë[– ~NùFˆm‡•Ü6½ÝÞëÕKAô&÷úƒ™' /Tɘ¾’ÑF"rp¨èšù HºüNÌÊæ2*üWCG^u/9½®Ä’—)ÿ¢d4* þ)…ø]îÆl6ëJFh>çÅ‘¨›Vš…f¯¼¢hݰ—w§nÈ fb&°ƒÛ.lg2YÇ67×sÑ¡€ªÖjÚÚ*e•µôåõžQ˨vúÒgLú´jÒgâ42M‘ç„|Æ›üUòê÷SrkΰÞìô*Ú–Kùäu•3Âü¨· w”‚Ü“´àœ–F áÁš-É¡8ÏwX÷çu™tMd{ ±n¿7:‡XÄB“6wD5UHQéQ›o ÃòklÙ—ÚúÚ‘ú—Yñ{pyL[G¹œËz‡oº±ÐÕÑá¢h<Ö‘Nk;uY9ÎñÉ0²sŒCIe ølÒT/ɹ6¤”À5¡Èm×J¡nkžÇª±eT¾Ä6QÓ «ÐÍIí«–÷NÒdütŒ½žáî®âM£ ¶øsKR‹:^Q ¥ÁNB˜>~6Ë©¦@BÏ”§NKRãzÀ'ÀÜiºÄ”LÎç­®ÜæBì4Ê#ãÿ¸Í5ßñŸ3ÈÔÛµjRSOŠ‚{äzԮܭƒ²s8@ŸÑMÿ?Óßy­Îe²ôEÐYÈ‹ðfA>#Þ'Ób¬-ÌŸ6 ÚÐô±¦èÉæè­µRáë,K*;s˜jÕ>Ùí‰ø/u ð\Òë£á°ß Їû^ÔL2–¸ˆ—vÑ3PÍ)Ã÷𵨸UxK§i¾e‰ªWž­±öWq-òWC×Åÿ@ ÔÒ2ÓT}ȹ.ÕÄÛw·bUæaÿ‹,a‘K×–ïŠs÷Ÿ–OÞ(·’º™¢ ?^ÑýŸ±†|„ÜÖ¯CÔ” ȱóÜçÛ®qÞ±äVDÛ$OD±|vŽ»rTôÚÊd麇ª|ÐðIJ"ûž½Ï=ÞMÎÓ“™ÿë4ˆT}S ¨‹H C£9âÍ/£`Zªèy9±×e¡‡›ËL[á¡yçãqñº.DøŠû®›xÜÊG®Šíõ½=EŒ}Jz¬Ëø€ÑñÜiÆ6bñëÜ\Bÿˆÿ‹f5?r×å/<éNu—S¥™ýØcxŽ™…„˜¶k“å;ÃP9©º’‹ v<(‚oÒ7éIï ¨\Êš¢ @]¶Õa-·÷´­1ÒS®0nM|¶ÅšæhÓG¤æÿ{?‹ÐçËÛ*v;芴ïkÂEr‰0­”þR¬¿WÄ|Ç”'«ؽšœïkÊ}¹*§±°«µªè·x“ #ùîîÔ¢¼0ù"(*zâñ¬ÿdÖ¸¬.ªåq¡€¼ûµÞ"ëæ;ø0@b#×LË«?#Ϥ¦s^úŠ&¿9~&óºœ&)ÏڄߣÑ~»ݧâ×n0Ü.¸‚R‘Úýs”U`¨¦Ü‘¾uã힌¬…¬ v†ŒÒÜEÞ­Ì‹Yª¾nå²ø‘à×Ë®‚ƒG“2¿—Hf˜þlrïrµ/T1C)üIqx+ñ`á ¸ Å‘ï §öW´:|ÆI¨H‹uŸ{iKOÏyÚëÁÎRÌ€ó$ÉÔc ôNQí¦Øa9ßÑÛLóŠ"¨Ÿ«´÷_¸–°v˜ ÷cHn†f‚SÌFl)¥©$Ì3|HDˆQ£Üïc £¶RÖWÑûÀýjïH‘Ip3œëZHþD"I”Å@–ñÐòCmž`I] Ïùg§A³gÒJư"fk•ýE2I‡± ïJªñßh‡7zpõUß¹ÿÆt8¡g  ÒÃpeeQs§íšãBß¡I±òþÖ ‰$š]1¶m»jÂ^ñlk oóGÍöd4Ü?Ú­è ”-R,ÕšôÃ!W³Ð¹Ë†âª{J[óŠ ZŽª,g—ª fäÃENßÂIŠ×n8šö],šm†u)ŒF¬ë}¾iÃsÀn_q9›.Ú @«–è¾™Ü9ù/$yžÉòúâVi´7TâIŽŽ¥ÄKl?³è÷§¡Hu¤aòTšüLÇ>(A{ØÅ!,^«–}ªgšè"¹·[7*™Çè-kØ:¢º1M¤¬Q¹Ê£ €Pu¾$ºÐ¨|]]s6íö ÊLì‚ÒCÓ/tÑqâ@xš­ §»"ûmë’®3ÿê‰|HMt9óŒ1~£– û¹ñZ'ØËàU‡™º·2¶åúò  trN¬‡ØfÛÁFLõÔ™BˆQÿ™$iDQô,?’HQ>žQY?%=‰Æ8Š×-jžØˆà ¬‹¦òáë¹ywåØÄSe=9qÌŸœT "¡æ[¥x®rbü„_Ì÷{3ó>ï&d‰¯‹¦Ö¡4d¼¥‹ìÂ?1ÖxñJ“‹ì1'-¬<Ë-®Wz&¾ØRüY„1v¥üüú ·ûÒ0å\‚ËIôO¦12§cÄÏRšDV—$–ôjœ×• ‹\@a…0@<1žh³ˆØdÆ•}ø³Оj±&òï\rÇz«ºsá+ZæAj±ŸecÎkq[ÔË+M1_BÑtÍíè+ÍÕ$¡¥F !„ÏÚ¿ç,È7Tx´¼©5–Öü/6åøu8_NËP÷1'N½ë¥ÛÙ¦$`Bãy›³¬d|ÚrD·™Šäó¿¾\vÒ.HQŠBYžÂÏ^†ÈÂpÿ‹ùIîjC›ËKÇ×÷È|:à§å–‡û‹öó{ê¡dTޝÙ..?ÐôÉœÌ4üتEZ UÞÈF¥‡K”ü•Ïñ»8k*"Í9&Km[Ñtir…|’5-w¦ÀÝ0ø£ûîc£ï°9²$‚vÿ¨@!ñ¬[t\Ti ?õ‡´±q ÃS:g¼ïÅ­ñô¶+)³eãdîÉÌC|ħ2 Õ+éÅF¸ñÜ‚C!l/Ø#.+¸ÔâãTÀ—r£‡ùR3VÂiõŽj½Ÿ¶¦‹ý_Ur°»Ò"{‰YËѽJî« MÑŽ‰aû“< vMœ dEÓLÜžØ^°†Á;â}÷4ë¹ÏŠÿeC…IËé* %ïä¥'Øo‹NˆpǬ]l–C£)€yÌ 9ƒ$ʇ';†í€W×—ÙÃÏ/–ÆPîfÍ<øelÅwÓ‹ƒ¾qɤ”&w¥•Fîí¿–¾Y61eKcŒu¦º¦HÍz©¬¦LÖ‰ÜÏø³ä0¥~ÖÞŃ~x•nÙ‘6F!›êéNM“£=HÊ›ñ+T§?øiÐîí~ €"•ælÝ€‘sÅ}_Ί7 ÔÄGSEé´6;cC|7LYT]=(BSÏ6©Í÷%!ñÀ†Îî±…~©ƒö·\âEØu¾­ˈåFEàG·‹Öã5ð­w¸2¯V‚**Ç|ÕíÕÜ7.äVÉm8¿‹óHÚ´ë»$@òΗ´³7òâ2GãèB;ú’7Êlëo¹?– ­‰J–ð7h£2"-é³¾°"«•ÀÂCHn5w!üU1Å_‡¼rЛl” £nf@®¶l9JYI¦çñ]Î:‹´ï(@£WóHD;MÎKLåìRäHÉ…ú¡ÃÖRbp±Ìi •šv ɦ &¡›äÿ{Õ5¨²d ªEýË+³Qbd—裗ÔùàGˆ2*sÈ|¶CÏcÉX é ï÷*°J¦à.Ì÷éŸùóï"X§VVŠªÉó¢lA†-RlÁN’¨Ö5ùYn Íeœ¹µ¤V=×p9¸ÅÕ±5e𠕤7Ï—Ÿ98ã–Y¦MÃÖmžöeDÊø¢c]ô™&£îóý¼Ìê(†ì…_!ĹΡš»¾ô:}MSgTŠb€/d¯0»ËêŒ~U­VÈØ¥iÈ6§ŒÓ“ÊBÁñU”„WÃ5ô¬üx +’©ƒí$»…ñU`èò|)u+ÜÛ'@rw’SÖ×ÜÜbjWJ‚¨£Ow²üŒ÷Á§2/"ßËEYëE R· óXJ|TÈÛAãÝ%:°+ÂR4jð5W—¶ø! E=×áçVÿÇæ¥ôøváßóZ yŠW·­ÙJŸãðUÏ—Nº ¢±=îÊ.êŠX¸‡CFFÇta¼a¸jß-]ŒE¦¥F³D—üÍ çô{út,sø{%{Só ¶$ÜŠÆ|$•¨#n¢×˜Z^êÆ™×·æÓŽî.“±‘º¥¨¢ÉÒÆ?æ4'ª¯í+W„'oÂïò×GÈT ÌÃà4d+p7•Í7câ©:!‹a帛¼ÿøOV~7£›4—¨Í¹É7rºƒ÷F›Ä†¶Nò¶Ù%À¿&ãe)ô@‰õtøoãÞŽ!âßSeØñšÍºHHM0êÓ¸NЇîGŠK$3FÓÝS@ÏnG±…æJ½ôOŒ¼Û‚”I+‘’›þ¹Ú+<:ÎåTðñ§ø¥m÷Àj5ƒÁéÅÊE@åPýóâò羃Õþé…C8ŽÜŠÊ)ñ¸+z´­«º?,3ÿ5!²¹/™Ä7z¸'©‰´#u 6­Ý.(n;iN)¸¼@6“Æ÷ʵþ4ñäŒA´x1èÃõ¾^Õ.KDÙ›¥Í3T4 5ÝÔf«êNråü›ªå#3¨Î}— äÁû[ j=œd÷}ɘ†ƒÝÄònŽ8»Ý¥ô0Œ•œ{¿6¤--¦†)7ë Ôú.Y'tXÚ\¿ý•S¾ÿ½’æÖHqز]Iôçp¨ÿUWÑÞƒÝ$(ml>Gpîù¡ °<™™JìuèMoœH„4ÑóÜý ‹îžØ%|©~uè±(WGMm"(ï•Ô ¸Ú%#«Ó\ƒ˜üeh²”Ù ³§g_Ÿ@𠌈­ ¡Ê,ø^Mú;‡c_˜ë}¼fj%ŸÛgAÑ>²0]ÖÝdÄ8@šñ XMûŒœÖY5ÄÌ<ÆÞp‰n#ãfÁ¸"‹&¥Yé—øíØ†? tîOò;å…º÷ ŸŒýëBûŽ·L¶×u~—>MŒÝ'Â?†Ž5ìÙP»C5•‹Á/ÚuîµkáDÁ÷jˆÀ!&÷QË¿ë=É™ú6r¡ïuL\·‰bº«vìHFI/¨˜— èW̃tȺäÔÇŠVp$sJE_“¢|«aJ÷G»ÂŽh€ÔÐ ló¡Þ[µ­úK_¿àÅu˜ÇŸ%®W»yzHQøZ›œø¾¦B“øüÄ; T„ß™Ûá‘Ri,çVöûèÎX1ÏKŠZôu$ârÄ\b¬¿½þ~`°[±¡¦¯’ i³Û´žÇ²:6?¦„·ΧJÊ.6øŒmÞûp ]ó«|tÏeÏÐø_¯™ñrh—73Llÿz)—rô‡ûö%ñ{ÚØ›JÀv”‚w´íhx@0·­LmøˆÇ[z×$â4ãŠ8&“QX"Eãa¤,u¸%jýV$µX­¢– ‰kºç¨Å~vÚÜ)替:¨bÀ¢{9ÁÑ|T¥`†¥ÏS籉kðªé8zº›.±é-ÓuJ7>IòÆîÏGë¬,üáÀÅË6rÓ'3V?>‰`ss›Å:ªCüEéA=ûÔTÓ ¶íp§D:#=yãð8ÒiuXn\Ý!ªaG'±öÝ@$¸\ÌüyÑ {júx³{ÐèÄ#Á¤¨h_ó©úa¤nFıƒm¤‰&JK%žKìß7[ÒjA/à½À3hü¯Hª‹Ö¦ ÐÚÞfÑL›r…V’0ú·h5¾Cä·Hú™ú1ò9‘`ýÒ }¢’U6òUŸ~ï?·Çý´æQÃÐ:L£Wµ‡$R6ôÿ2RV?kàŽø£ì ¾+ò{‘ ÷Óxö™žûŠÑ †˜ÌYή,üÛö½”ÞÉO~¿3¯õ,…v c• EUÆ«“˜ÐšÂˆï žcY°?»Ç[½¯Öuÿ¬Ìø­÷ZJIñK÷BÛ sGµ(N#L$!wÛ½WdŽ9¦µEq£{¨É‰P5”ÐxÑäÇ4öNâñ,ÉV,½ócï¯R¶s­X?ñ¯ö¶€U¥ò™E-nªû’ÈOLŒ" ^­ed£7 êždcÞ—Ôácð‡ü$œ¼ ->ˆJEbÌ'7 —8Žý#~dJsp¸³ŽÑÃwõ"Ç_˜‡Š^‡bùØ\r»å¨|Ë`“¢å/fÈ:zlæÐ´¶*øÚGÏ—Ô» úŽÿ©Àö2Z—­¦”Eò:±þ'¦l'9ø9 0@ÔKìÆèûèˆC³LOÕ-]¡°¯Â7t±œžõ’õØ„™ a” ‚“¨mµ*á~ˆÚ‚ š¡kÒ›‹/ý`ª%üú—Eõ|‚(<n¡ÁË›WÀRÿ½V9Z>ÂÓ ŽÓ8žü$ýUê3›8’V‚)ØÓâ¨!è!ŸËwüóE‹ï—l ¾ÊG7jŒ§=ø[4 gïäSME–™e&µåÓÑžÌTIåï+‚²ÄÂø¡ŸÏ框©„ ‡-…Äe§ Ngî&¸¼’Ã[DÈø¡¯Ã†}–ÜmH½E;â[ˆ.›ÀДŒ܇i%Žá}3bñtëDõÀí7£¾¯¯MFøWg(¾Äa®dhØzserôÿ•µ‹VJ<"uE‡ßIZ1‚+ƒ„avKsH<|¹0îÌot$„á@‡éQ /èŠ9uÉkìD€2ŠTt÷A[£Ÿ€8#P{ˆ]FŒa²¨«®t€ã›¥#¡‰,~ÅqkN€vü:Ú"&•ß7#…Ú{z)àLÒ¸ 2maÉcûÚþ’$å7|Iè.@Q–ª¶!µþ@áx*t~ù5틱Ú9¡AÌ ¤-®3ƒ¼•¡Ð:…©Dùa0dÿì§a—á ºPóX7í›Í}åæÙhªËoÉx ¤gïoÞ·(ár“܉Ýå¥&ª•¢°ÇPç9ÔØ‚GÚFF­Â³:a2ÙjÀáÅd8_ûö$êv¾¢©‘`c-Òü+™>Ÿ/Gr´]o´P ö°lÙK£€]¶¹Åæ©Ç‰¬çjéÇœÓD4é^PÉhc:ù?ôˆB§% 4YË6î4 ”šõüô‡Óßæ?­ ¿ û`Þí¢¬Ê5écVê½%?ùÑcý¼ÏÚ=¢å Î<`\™21%Šqt#öKþëÃíCc%L”†cÛ¶mÛ¶íŽ;¶Ñ±qãŽÑámÛ¶ms¾ÿ0›YLíO-juªê=Ï#vSêÁ“­!@"Öý¦ò÷€Â6"C²©}B|uˇäó{ǃo~hî?^ónÙæq‰EÖ4‹f³ÊÚŠn^âÑà· ÈRùzñ¾µ¹Ê컌çlÛ³š‚&#¼ÃO šÑ`_‚»t—jŸXäaeËkA>„Ž®=/Üõñ;KtßKäûb`Mí¡â1'# 07.HÖÂÛà=Ô °ŸÌ|üëÒ×ÐW^u¤!üÕ§ÿÁd•']Y±r2 ¢¹5ˆŒóôt­$ÓŸ—LZ¤–ao¾Ç7>q¿ŠÇÀ:Ÿ¿2†ÊÓ‹–žÌEråç“—£°Ûï?m‹sΉáYHòt¦ÇUÉÛ†í7$ûgðÌÏmégfOFR†ó<]XâÓÿ‘?õt€Ç‡5Œ›Œn+Y?‡G)»ª_…\-9½TqtQÀˆ U2þÖá·ìrÔÞ†Î^TçÙ2Ö W¦©Ùyoµ~¯?õjos‰¢ça#ò 5ÛÜ…[þ9 ¸™¡+Hù0È{“t¯½þNÈhs©,î2ÆèÏ_è(5YÒø‚ÆyBš0|:±"‡ÒQ[/SÈZ^‡.ÀÝAÐÜ÷¨Ñ$”ME«œ*^æÔ…A’¬$”½•õCÑL‘\zȶ%=ÇnÓl˜„ß+Ϥ`üUœ ûX ¤ds`*¾\j[{,ò2¯Æé!WÊic~!å)/Îs=¢>+•‰V(‡©ïL]N™b@àô§1¶ù]ø†hú1îxë *D¯ÝÄ:ÿEpX~øë½5ü A',Œ/?ž¾ç®ÄÝ‚›º§j0ó‡ã@üd7]QqÒ‘TÄ‹Zǘrͨ +Þ)–¬>(ÿ´#Ý‘œÏÊо§}1N<_hÛ*ž¤ÆÆeöy–ÂË^{ÔñâNÃõpѶðÔU6£©¨ÖUMìWµñíÍ5뺭ƚ™$Á¿5w‡¤îpVcKŒöQÒë`ø¦Õ½Ö[c‹‡yÎEŠ—ÃM¾¦à¹íæeZ¿mŸrê(Ø{›ï•lÿ/^`/{<è’ÖàFNQ¹vô´ÐÀŠø•|¨ä¡šQ,«±~†Ž<ÀÀévÓ)6výU¸`;J‘íjž¶0ïð¡Ö½Èž×ULi±R憵õ89ÓÄ]¨¶š¢>b_J‡É&ȶ¿öâë:6ˆ™Q*k¤ëìÝ¥ ß²a…ùm+wq«Þl50a>÷‡øÏyÕ"[Ço>lZœ”ÿž·¥ÝN&Uc*[ø%"¡(óчaÎ’«±+ª~‰Ì q6ê6’½pã+ìüóO*°5ž]<J¡?Û°¼§‚Þa ³ÝÝ*í¤]!³¯¯YjlåÖk)©®ûZb+U–71­Ž¡uZH|¿tTc±Ï.¾e[˜€2.íò›tcÚc=h:·…‡nN•H?‰{‰Å¨0Ir­}Wv¨»ÖØ^j8N§XµtŸ.Õ¸ÕÍŒ¼¼ ±}«Å»¹®Ê—E‚) ¼K£Ú¼BêœÙ‹HÚö¨güò hôcÀŽG1ñZeø©®÷më!ª¦)·ýœƒD³öÐÁ(pQÔïxÅšÂ[âÜ”èuÆë*$¶¿b’ÂO}†—t籸¡äc§ß%ú†ƒî„LLX͘Jt[RrÊû0ÆrƒNÎ +UÏ(·±®–½®ÍBjâfu5rPè$™šEOÁ{•‡áG`EˆDÙѹ_ƒu-oó׳ÍYËw!ÀYšêù{ óPyô§>–À¤ó(ŠST˜#¿/5ÔW …3;”@~âÓY‹\ˆb‡éÝm«Zð²Ÿ¿Šµ—[kÜa“QÙjža»ÎA*Ÿ{á]¹SË&õ2qä•?u`ñ¾žc§¾Î=MËHÈŽÓÒÀE‚()bgÙá™a£N¼©9FËb²v.º„åñ*ÕÿÒ§•ƽrR‡,Y㣂–¹[…Z“[s=o•\d’ÊQ£d·bà¨À²Ò¤«ÐœÚDŠ,ç»x)Þ1‡0çX+>qÑí^nl».š˜=/ÚØ2*-Ÿl·:5WÞa«ÂÔ‚@kÒ‰{ñ9¡b ‰-­:KŒ–•º˜+h>ÛÑÒ~›0Gû® Þ5MSÁÚ‚gSÔ;&ªHÓr‡6µ5ñAƒŸ„9Í!}‡p—M±–ÞdTÀ<ä“dNTc[¾oÜ·XÃfAîÿ¤Î¢S©ÈÊÞ÷ÒŠ9×ùìÊùr€M€+”LÚ êŒë¾TØ­£Ì£Ô_[¼ †Ž!ÚÔJvÜ̓üÑþ› 3·ô×ɰ…þMåÏf,/dZeI6¯š6*&+k­_i ¼]Y.¥…Yádµ°¢»wÁx™†o²“¬vò\ö)ä÷„Å6Óä.­„ÿý þÅ/Ö»S‹ú‚ˈ~É–’:™ ŒÖ“‚b9q+À)žQ ëÊ©ßv¦â£šÖ'Õ†@Ïl°[ŽÒœ™\¥ö‚È ìë§z:8Mªô·Ä@•Œ®hÙ¥QVb´Té)טचÞÚ8žŠÊ`N“h/Z fGâ²­‚¿F 4Zˆbž“Æ:à×§¡qø5‰’**Mþk/µó2ö.ô­ú§.4ñîõN¢|uìy齄 <hE‡WÙ/H6rIÉ£¦=´¹uÑ…¬(M‚ MÆó+CÃÂânÌy›Rr·Åp :“ã°Æ–üæ°‚u¨ñk¾l:Dë”øB-=óBô÷öPÜ«J.Ú³{l–CªÚ;¶N•²BWÀ–zÖ¼ÿ¥MDà ì=@BA™5*Wr£r{âpм#âR¹ÍI3«÷ãØ`JD¼òKɲ'†f#Díìáp¯ÝêB:ÑnÂx?‘éÃÃH¥KfaÞéoKX(ǧÛ]¬ƒí/Œ3GÊÉ»‰ÄBìø ŠúùœŒnÝ=ÙáFƒ€™Ã ·A' ¸®ôü ó‰¸ö˜1Èplôk†4qm(\—n*ROÝOqÝXøQHF/ϺòœTžÌ{¾t×"diØ„öA5˜ùϯ ÝF³õȲÓE ­;×ö´)¤f%‘Á_ÃÇ‚ÅþkÒ\; Š×sÛnŽËf_”(uHÌ…ñìüpÔ¡ÈûèÞ6ç{ËhÔˆ²Íóš9 O>ùqG˜|RB¿Y1ÏN:ºëc[¤5«ÉˆÒ«“0çLÒL¼ÅãK‘øb÷-lµ¹ªQózÿ»F"0&@«ãš–XYI¹ƒÁÏóÇ„²pC“fåÏ xlBšWœÿ â~ÇRH ¨Agd°»*R?±p}¥ò&rwóµDúœÃÒœ'æ-À€DAÂ3bì’ ¾Ùj@!~p¥Ø† ùd£žoç¼Å‡¿‚Hì⬶¿7©ººËcÅ­ê_Uçë%&%`(ñöG\e&϶bÚF3@š\b Üà¯; 7àbêU-Û¢ÂÉI ±XñþN¾Œ;üœ dé”|ìá7iÝôªèzjÚÁêNj¾ÛèKùêv”êrU ?Qt¦Å¦V!ø=xÃ@÷6þ0¿½ØCžvš™K<¦vbk& ªºé?êí0Çú¤I­Ü(‰rSÕÓÙú‹Ò¡f%‚®%SaV Ëm‚|@# ðë hmEè¡aC ©."€­ÛX}RÛÞO̺â ê¬×­jA9ñ„&UŠØ¿Ï~Çá>OÖÓu´úIÖÐÑfš:’øY•Äç/m›£|ËîÇK7ä—óšv¦wÿ%ùÛ¨ØZ÷Ó™l§x/ºK”ÄY ¿ÑÇÃøubËÛ”½lÞ<`K¿Á«þ\ûÏ_ÆüñS%s?¿¿-Ëzž¬ž^¢%Ww,kA¥vÜv)>!Èi"íû¶<½s6ZT8 ^–½JË£Ü=is/xª­ ï ö¢èpúµpPüÎ( ð:S¦_Zz Ø/³„ÓØT©ðŠæÊ9\LOà¤^<ùÂçòwæéÒÅRd·qÀQö¼5È=êdHÏ2‚àÃ*åK}æëϤÕSöŒð†rÛõOj¶U/3boå=2lŒÊ®K?kaM.äÌ]BºèB óï¸/á¶«hªn/«ªÚ"¸ClýÆñXBºÆc£BB!=6ˆ¥”^Aÿ¹º/Ú€gHlZ]R*Á¶ÝFåöÇL8³ÕëÆBùJØÿb%¹&¥ñúÁ¸ŽǹpïLwnŠÃyìY.Zm}ÖX¾àªï×m6k©zŸ=IIX€ú\f½Õà}æŽ=ÕÄÌ ÜOïZu¸±ú„AÍ9F?k »ÎªœÃ²ývÙTvpúÔ³ÑN¦V½ $Iõþû„†2PÏ' -ù¶J´Í´"¸¡gãñ§6•³Šë¸ðßzާõŒªøaç:ra¤ÄÔµQ8›z¯?Œ\ðvãñVí óÓ¶”'§©PÖŠå““=³aÊÝSþÇžC9`Œ@¬8ã}×o0œ¹=Ìqâa¨Ú ˆûü›$¯»Ùô°í"bp¹¤Ê\ºÕ›¯ ì÷ÇÝ Üš¨¥H0ðô¶þ\VŦ`ëŽpÈZ{>}3Ü[§1ÚÛ•÷‹ìF~íað~ªxŽ¡@‚5ŧ™ü­W¤–0ˆ˜Ë§kÜl/:Qeìw»ž^`µÄf ¶Àµß ÷ÜôÓß3k1µNÄ-r$0þH2ŠÔ¾ÑÞH1–JÔ96ÊÚ;}E¹¥÷†ôù§C9wçz+DU8ÕcÿH«>Üküè^•¢Áâu®®}õ ´zy€ýRʦÝY:™iEÖQjŠÈТAjÅ?Áe®éYߎ`rI¿e >õ†k]ž áƒ]ÿ®áª%ƒš˜=¼/í’':dUqî»L†¦ÙÅÁ¸ÔûêMþQŸ¢p#0b/‰fÓ-5ÁøD½!Úæ³çºgÉL¥G*ç[X9v¾T]tŽQêae÷D±é l=6Èc¹UH'J- 6©Z°Åbô²…ÙŠûÌ7)_‘y„7AÎqFB–ȇEìˆy™;zŒ1Žùì~õz¬ÝBoèa÷A;V­ šÂ;Š q˜4Y/µê??èzæ2[^rtöˆ¶E,u6&Œ7ÓÞËÏBÔ y®Eÿ9ž¥–.ÚþôTïX׷΄ÔÚ³Ÿ„?èSç–1¶Ã‹UávÝ›Þ5`J²ºÕÃóhþK£\äØú3ÿ#œSˆÒ/÷Íiò 'RMùì/¯ ÿ¦ÙùÍ«Hž¯ÓùJ¸Ú«P߯"‚¬  0 w˜P"Ýo’ÁßìÕMe®×o-m~b cŸ’Ž‘°ÔÒÒ¾¨ ÂÎ;Z‹5¸Ø­×ÿZSû¹  ÂX)é”WÆ#ï(”GK‡PÆVÁU`]¡Lƒ5ªIZ]èÒ,‘£;"ÿ[àñî·|gWç*ô<íèéÁGþoÄ¿<6þÝ·ƒ6éÐå‡Äýƒ²Lo¦ ¢?Ââ±F ŸH2>¡Xd@œŠnØeDÜ“f#èØ V8Ë ©"ô˜dg9Äk ðè‰j3ª ®å°a[Š6¸Æ 8¹Ÿ¥µî!Ž+ñ ÀåT{‚ü©Š‹îdùu{_U¹ t^è–”…rSÆ"Õ¤È)€<Çz™˜å†þ¨&¢1<ªkÕE„dÌ4ðÍ&}Õ& ù°¢Uv!‘ÜňžèäjÎÞÀ½ÆnºtCôð‚„óåâ¸ívs©¾TŒìÈø âã©­¥WY*r©†“ö«ª!ƨ®Ö€/B¯è·GDv½«'X&K‚(ˆÂØúÛ^µ{tï/{¼ï;è Ú)¨Hª¿1'RØK"½ð¯,ÃñÎMvË~sLê/³¥Œ–qbÎ^ œà¶@U@"ÑÔà]Z§•^˜5ùü!6·ð^-_²„“A/bè"Ó²g«ÈGõE DÚ†¸Ja¸¿êâPDŒ‹•®i¤7R\†&SˆT¤ÒØîÚŸ<ŸƒÚà~ʵå:~¢ž³¢ñ« [†•ž¦ê ¿»«*:Š øÁRä7F|æ~"ÜänaCøûÍT ¤)¶tŒ°×É‘}tLëv™á•dó•v¯«Û`¤soB]8†éKV5'x7ýÒTÞf_^B–Jþv<Èÿœ1Ž[ÅXŸ߃¨‹ þ’Å¢oš]¤Ù¼_Š“R¿3ýRÚôçBr0+Âu*èCüíñ:…Xº³„¬ ØìQüÿ_ÙP7‘C›®±n¬ìäù©7Ò¢ú}üGÿm±R땹'aÆ}„cމ¥—K9ÁÅ;ïJM­Q‚>Xð©0'ììy ŸÐ¤äùç½òÕk>϶^mÍòoÇPaoì(œ4 •~xÊÀÃõcžšÕ[Þp·ÐgÈ_@;ÙZ*‡ Ü¥[-1c@åfS>âŽDO;¿NA jR¹Ô."¥£[–a€š¸ñ ŽfÊ1мu T# ²¶D†V ®Ë,ÜÚ}¼ÏÆ]‹ÂU6Æ—¶ÕÎ÷}9åÛrz$øá3èl¤å¬I?U4©ÀIÀ<î£ÑæŠp•R„5çDn·ÎóÎI:Ÿ¾·*ÿ…Ñs] ·|lÝUèCò1§u¤S´ze5×*wêk6삯 œÇ pf–œ®Š%ÊÙ›ºÖAškt±Ð}Ë1Š>ÙS/À@Eͬ`ž“ûÃ@+bÂý†ËS²GæŠâFÓô[%v»—h<êMš7tÃÓŽ~P„tyoBtP_z»ÇÉëAö¯ï~GMàø¸Ó }BáF®m”šaÓ¼*:Þ^¢ •±6D~¤Œyjp¶8ÈÙ÷ÆðÚòZ\b¶‘¨·s'ŒÒ ¸öÄÉOl %ün¿uc®³TV*œIˆ u>n)¸ì>-{ë®á¦'7ÄNêa6/DhÃnˆÞIs§¢Gÿow§F:Æ®xÜh„Œ){Š iÎJ_£HzAììÅKl['> D©þö¿9'aH¸ÀŠ=þNÃÎ0n·q«Ó¨6IH¤Y`q›Òhy?ÌDÚÎtrX”Wzâ¤d{›ï¡ìëþÓ¿d^mtð3¯X©“—(MýÏMካ¸òêìâ¾ÝT+ñ¤c¾Œ¼»}ùF ¾üÌÃû•ôµ jg¨ÕUüZæp³©Î/"éSÐØ. é&aÞ­0Œ0ëmª=‰?‹`Ͱr_ã F…Ä v–\Þ0€í`ÃH׬¢Ðuq`?gUDûevåVºö4ýUM— õç[ݪÎ@Q‹¶Zô¼a±ŒuáyñVø}ë5˜Ä×Nv‘UÊJÄ*ÁCXCÛ9jž©s}”5ùõ`89,û$öpmøAð‚3.̦éò;ÁrŸo €$"K¦›Áo~ w7¤JÛº ú^ÊJ{µá‘A¨+‚Þ›~6Ž9GÞ+xQH8l kì„»76œVïßD2}˾¬ñNõêÔû ´Þz‚[¶0HØCƒj>IòH¥©ÚäF´8¼o0!)KpST¬=™‡=}²s÷1©a¦•þx}gÊlÆé#IæÌ2cIVi‘ÆžÖÞ½÷êìU³PË\hÌ0cåáõæ'å@iJËgÙpÕì~¸u៬À·JÿÉôç(§*×*®­9öµ£šë+ëüùh2¶Ÿòde`§ÓdýÏSÝ›G ²[fÇËEK'X¸õô/¶#Ûp€i}R†z9lÞíX?Æãƒ÷eÉÄü=Mˆ£ƒœØG!ab¯i…(Pnà¡TR'ØòYiðâëhr+‘=þ•ØYYŒNoß9s÷+ìC·ÁÎs.¯Äåpt„²t†IdjG¿j"1u1ŠR\Bgf“*½Ö¸w¼!Rú »­k·E-¹àu,êSÎܤ^"¨Kw"Ò'³î‘µ)ÄÙ·ìò{OTÜR^Ýy´­~*¼šgPMda@3öÑPÔqeê?‘=JÖ2”£‡Ó;„Å·Óò¼eêTÖ °†Ì´†Ì’6Ô†¥ýI«t{ ºÁž|õ¡~÷,­ rfM^“‚Aá‘êÞtM%µ½ïÖp8Ê cÖ/©؆,/L³ ÕÔ—ÑîdpX¾®É+E¶FÚ–N)$È«-É"d5óæ‡zÐ)‡0\©%æˆ*áTµLÍh»¦D³/ãÌ·£,ÔˆP\ųFž`ªÇ…\ãyÈ¥Èí ÄAšöT »`–:TÜ5Ö#óuÅ““wܾ•»Úî¿~ý‘F Tl¼#¤YB6¯çLøu„*6OºËÊ„†r9ýÀ ÒI»~—³Ñ_^CŒ»U1Z…«²ÍᩪIDI;7µ” y;øûì¾õ}ŒMl䇢¸=΃pä"Cr·Q¤ÛP[Ý’T‰WnÄÐÞTÌc³uó±Ûÿ¡¯?&ÙºØF¥vHì­àì³î6°ð¨¾Â„ñ×(P°¾žèhJLö#B™O£Qç˜@ºêâ¥ßVT7j&Ž÷}ŠîÛÑ'Ð3L:Ø€¹\ÞŽê[Pâú”k©­€Õ´Òª•wùùë 7e¾*â×¾&º$S娯 _ ŽÞ.D5º÷NIH(ÀB»FùâeÿeQɶ¤@&Ðà”òÏöœœO/'šF•RQûìQüv!ȤÚ/l@‘ü˜"7Šܲ×þÎŒï"ú[‚)¥Ö͇Þ9ó:‹-å%X¬Tš‡ì½™:9 jÊ´á,‡ÅE—‡~ä³}öë0Ê„Rt£ó OËÆM3_Ú$+ÒI+G8A7Mÿp;¤Rg2kO ¬Bì¯8íî1xc³­³Ò;FRY½ÜÍ^ÂEÿ˜à·Jóhþû:Oþ…¶ #HMáÚ™«RPůÈÅý>:±NS³ÏÈ2q£=6¾þáÉ¡y<ðTèÆAZü g ©éZjŒ`ˆ pí¥«üs=ƒ}¹ÐÄ\÷žÞÿ,perþþ{„ë|†¥S2l‚‰JÎvFF°¿'¸SÔ¯œfäW¡0xP£Bç½ß ·(dK׺?y›š!ÁÃ+/Èòí”ëµhLKø‡·Nª7M<{Žb¸ÿÆc8ù±OE8TžE^kbË~ü†EAÂêI Dæßæ[…á”5”ý‹õ–ì áõEJÝãwµÜÔ¥bÜ= ¥/ç”86ï·Ô`Ï· „Ïzá qà¯Ú»®Ò…C†Ó0EÖÈæ”èöq¬ÅÙ±³'0ã‘«tuµãÙ³– ìûeð Rð‡½S|åÛ#Ï$à¯ß¿uÇï ¿sÙœ¶oS‹tk:«]ù®ª½MTzR­¶ºz8ò ñ¤«¶ Hrù,í(º‘ec=ÖöÇñØ9#¨Ž ²Š€cì%ùã°TINP9=3a¿ç¯ÛÄÌ€$™˜wL(y'æ?ÊÓ–ËÓ¯þtphMúQµT¨­­*£"ļP½A T8k6ѿ؊áþU(éoÍÒ¬’/7ƒ7ÏùÏJmã¯E#5Ž›½ü—|Úe+">îL0Xÿ'çâÐJ)TÉÀî·“U´w²¦î0› á·šÎä)ìò,Íw£G“ÍÕ§V‚pÒ%/+üÓh[Âì}þëÀì+Y3«øMŽÙ+ÐÞÕð}óþ6º6£A}\ ºB™òV®TÏ©L—; ÉR_ÐòRÁ|ÿ0ÞZ&6³@èLvWHÊEuÒ=—Œ¡§š /b|Å»^çeÿÙd=òünF'SÑ}xÒü.â«M6§‰;¿H7ôì :nôÍä¡ nƒ"ŸË%‹ˆ&1Hh>jŒ Ã4¶¦cÆ¢J9J6?õ°þñèžå©E‘é†Ik7ˆUªV:œñ'鳕u&­¾Æoƒ¿0)†3Y~i ª:Ý„{¥—ÎùÆZt™BØX¯Tޤƒ’ÑBhð+bê£-Æì¶o#,l6–8ED„) >U-û|£>Ëý‡T¸LÕâƒ5ÂÄ}«SÔò)«¹ÛÌJÁÑÅ?½¢äLìQ>]–›w~û ä‡ÓQû×vU?˜) RÔø’$²'ÆUVäbÅ8@{Þäì¨åôa›"\³ÕDÓôÿå3)ñšU :NMÃø²)Ì Q3qC©³9MÓÞ¿ê§ï|žh ð´x 1skàY3HõRÊá¥øéÆÍEƒËœ_#}[ËçTRªò–ÎDŠv„õ¶­ýq¢3ÞY¹ÓQ~"Stôݮȵß#­J–eþfŠCNZ±×é±…ËM1¨KÈ;6Àö~þ8L{ßPÿ˜™~h[:ö‹9W[Šô¨Û„n4¡ˆ5èûåZ²>`¿ƒÉ˜¨ô^CØBîɤm“ÍÇý—ØÞ~¾&Þg ; žÏaªº¶è¿k$^9Âwâ}YÅ4 Õ'0æOû;-ó.¡…§‡ŠÄÈœœF ”r8Er(&Š’{jîw|zª"ªxRüŠ.kÖ˜3óï˜3SvfM/™¨ó£?ˈï”5a¼—m¨ô‡+ÜÒ}£§>™¸!N²¡Œu=å£Y”ÉU 4°Ð° —Õj%¿ñ¿$m¿¡,am”ª µ#%íàá·üpÙˆþ6ÿÛ¬¹EÖF&›’S'û˜N¨H¨iN…I3sêÇí#,õÔ¤suå¾4,¯xh >³^ÿWïNЋ†›» h”ɰÿi?\DëÉ›xü%欗f“BùÁ€ŸOƒuʉæyDuc¿c‡„þ–¡6;šÄ};™œìÜïÝÄ Ôã=-&¡[œ…êãÍ–ÛýYüü×Eàž÷C@\“Ê} ˜SrñÄ/²B£i'<ò"4BP‰u h2œMÍ\ÝÏï»i†²ûµ†¾ûøw>>­U%  LÅ;¨Ìqýކì)/‰ÉÌ.ew¾§ñ Iã °²¶YQ´rP2m1 zjœÑ]™g¡*[ÍVsÑÄBaµpÆ|‹% «™ŽÀ1[#dÃkCJæ'ÞÞELŒò©&wµäÚßuBF °×É~6¡]IªQ¡I%8³´lB”‰ôUÇ(_Âiî ÖʱÆFî‘æc)’¦€F/¡< | ŒM6C…z yGÞ(ûИ>[â^(4du8ƒKw- GÝ_‰ }[æÐïÓ0Â+l§Z‰Ùÿ¼˜4°¼Ÿ†i‚jFð¢-Ý8qÞRì+NóŠ9KüQz”,{ѬÝJ! a,ÿË÷¿¬gùÿ Xþ/œ¹½¥©‹ÛoS;¸ÿó²endstream endobj 10 0 obj<>endobj 11 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600]endobj 12 0 obj<>stream x¬ºspå_»/ul;;¶mÛ¶¹cì˜vl«;¶m§cuìNÇN:Öä÷¾çÜsçÌTMÕóßZ>ŸªïÞ‹’TEQÔÂÉÌRÊÉÄÈÊÄÂÐ:Xº1ª99˜:> œð””â®–¦  “£„)È’ mi°4°±Xyyyá)âNÎÞ®@k€FSM›–žžá¿(ÿˆÌ¼ÿ“ó©é´vP}<,íœ,AŸÿÇŠê––%À ho WVÑ•U’ÐH+i¤--]Mí*îfö@s€ÐÜÒÑÍ’`åä ø$üs˜;9Zÿ ÍéÓ Q7€)ÀÍÙÒø©féenéü‹àléêtsû<€nkWSGÐg@N £¹½»Å?|Ò­>Sø âìêô)áðÉûSqr¹™»A€O«*Rÿödc úǶð“ p²ú”´p2wÿ'ŸX¦ÿÀ|rA¦@G7ÈÒë“è0³XÝœíM½?m‚9»ÿ冻ÐÑú¿<`¸ZZ›ºZØ[º¹}Â|bÿ“ÿŠóÕÿŒÞÔÙÙÞû_ÚNÿ’ú_>An–öVLð¬lŸ6ÍAŸ¶­ŽðÌÿ4‰¬£•€•åßt wçÿäyXºþ+A4ÿô í§¦NŽöÞ K+xf%'ÐgÂ4ÿgUfüù Äÿ#þ)ïÿ¿âþ÷ý×ÈÒüoÇÿ£yþïÐRîööJ¦Ÿ ð¯åø×rù™:í½ÿ?Å´-ÿ½hÔ,­ÝíM]ÿ;Š,Èô³»E­?· Óg›þÓ~@7) —¥… dn°2µÿüÑ5-,]펖Ÿcú¯-`deùÿÅÓ°šÛ9~N€óß,KG‹ÿÖÞÿÌÅ¿gþß—ç¿L¨|Î0HÃÛùÓ›ÛÔVtú\ÿqùGQLÌÉ àËÈÊÅ`dãaýœ­OxÙ8üÿ_Œü ˆõß@ÿè*š‚\^ýÏHYXÿï?1ÿßn†ÿ FÒÑÜÉ⟙W™:Z|.ŠÿEø”]Ü-e%>ƒecáeÿ·-swW×ÏÕô¯Uð=üÜÿµr--½,Íácá–M ®ñž6€Ydƒúúœç¶œÉ£ãF)ôK›¡×Vò‘ÀP“ëV~œÃåýÐ ÔñõoÓzù·sº!¼zú-ý¸=*¥M+QFeÒ¹a²îW?Mˆ§ÁÛ}]z~@ò‚@öóùü¯¼ÈŠŽ2¶ßÂÐL‰*lª¹¸6_%šv@ž©`‚Þ,´¹ŽÄtúïHº†c˜p‘Ó_W–Õ>r6|tc€D`ú¢:_~8D¡ 94ndpäÌÏf±’QÞûdçõZ»?¥uL§·B5DÉv-êÕ+ÆÝ%î·eXe«“‘Tó$FØØï£1q1M§µúlú°¶±—Õîj&%¾$w.ªýB•Ư fíýý¾mšg¨0‚ÕMÁUU ó\zÖ^1 á'[ôч>ܲ·xJíËÛ2, ‰ÏààÐå×”ÊÅÏG ææM¼âœ©ºx2HõL‡K{û‘eŒÆyq/=ÕG—9@:Ó”á9gAþ¨þãôk }Åvfˆµ9‡æÞü|O>Aþªj;±åÁæùéÖºñ›Xmþ§ýpËÃf«ˆ!43µ$FÞq¶Í—³C6øb(ZL¢tϬ&3€f©8 lim¶ÃQùO@ß!’ø>\7`lzk—;t ± ýaG4*Á%ÝHX¬—®©åqD~,rW|pà+òE4³ø±&]t~QÍEýiª/®}Õ@qóNæ žúmŽãW"á·c/Gzº(–§}­å(&Dž¿ÅòŸ¥%Ĥ«4$%‹¿ærO¿òýQ”“uÎC6§¢0ýÂN­§z̯‾ʙÛ)„(/ÄþkW²ú›šèÏ” ˜'Š~pô¶]9cBéˆ%; Ñó3Ü> €\d–Ÿœ=$î>KåZM íB:Ï–|ÞH¬$6y±›Ï²V ªý­i—Ø:&êÀW:”@´ó¯Þwù”³U­‘¡ ¦èÒ©vm–͆¿þF׸x~SÝî} ÎæßO XÖØ³‹©m‘@Dô,¼lžô½‰)i®Ú~Z7jgç¬ã!Ë@,c ZÔõÑ·–×Óã>Üã›}+äÙ*æË%Nþ³šücÞý5|† 瘔‡¶ù¡œsn¼b-C¾Qýåè…,“Ÿ—|^«WÆ&íŸ7jßdi­Y­_¯ïÑômùjoè†áNG©¡lu“hå¸êÞ£o 6¯¶t´,úI»0,y r~Ï‚&cS¤õÍ__lh¢Í‹Ñ\G<%³3ݹضŸÊZWªòtÈu'Ù’[œuÙbUyÚÕ7S(6õ`í>EXRà‰ãlšfðœÝ\Pú¾9†®?‰JJ ý kJ¾áÂbVî…Q¢Ñ|/9[ôÞ¨ï)D#_#°ôEƒ…'ÄHc,ýB‰îJä&`=„wÞHdZþ}€ƒ,èÛ½Sƒß†#úìÜ·ámS&ƒ$éK ¿êP ˆVrºV oª¹Dǯ- nªZÀäp#vT}dêÉ„»›vP©½tü\‚™2sÍ–Ü¥B,@¨S诺†¥Z|ª£¢M_£zÞó ©¢‹4¡a¡•¾ª+жA*¡¤]cÏ/<-JX<þ¼.ÃÎÙ¯yl¯ˆ@¾´;åH I>:°­Uª‡²IŒMHcýfµ<]K;Ïíe R&K…Bpý€ ¦Tm]q,g@hú ³“þàúa¯M~ìÈíÅ€{I#…n…‹Y³Ì<á˽û26‘Á5É“!Ÿ$tD‰º#%œ£°Ù·RÁ‚—Ù©v<†Î" «ž›‘ÆeÎvÎãjáÁ “ ÿL‹aýh©Ù¨B“øÊik­q╳SS~A#$^®Îr8õz‘ÎF\Š'!‹ò„·¨çŒ¼¶Dq5l-öÃì±lwïÇ@€ª¡¿-9Q¯ˆäàÁ”ˆëL߇üybc•ú¼ÚýĉÊ!Oƒ~úZî©(]†¦bFÑ0H=,Hw ©(êòÍ\è£t‘'I2׌Yc ×OÉ®„×á CÚáüãæJ ª½–w;nßnÐßC®‘Å1~ŸÝ¾m`æhšÎx ÂÿÒ‰?å˜Óø韄.^XÓÈ:¹2\.ûÒL{­­Îuiõz#BK¼×=Ÿ&VgJi|¶*—§ìô=ë 3­¬§øoŒÉž³»”Uã¥'vlx{ì¾õ ÷ÑÂÅ_¡IÊND¥CÄJ?ïK¼ØDðO~öûcÖ »»Aãφpõ#j0·ose£m´‡­T; »´Küƒ7©I€”l¶¹vJ¾&¼têQÓq®ÑØÜN²nVgáêžæ#ë X*Í­£÷BC«Géß»t"sç5öV›%/q¦rSÖ£¥Å£›¯Û@~ÚYj#Ô'M^8ø†“^jÇÉï dHluwÌ'󺛀´Ì!TÅÏtŠü‘T)zLžÄSàÅå÷á˜|ˆsú­ê!xXM~,]ž)KæuÝ¿…µñ½ˆÈ+·‰±DI`›¦øHr?¦ -HÐ_é$UµëÇf97žÐÚE‹Áf:ò-lhÎBçÝûÞ­FöœÛ•ñЇ›yåGWž¨þ˜£˜l×™yØ(0Zʪ@ûtïú%º•O¨F\ .ïòi„2cú\Ƚ¨´)¿†8èéG Uà¶G?ƒT)ïô&N*BâÐj€Gšø-'HP¹ê›nßYˆ£ŸÙƒiÇn;µfÖ@vÅ5Å7fM®óƒ#.+=ô®ÈmÍ 4E?<7–ܧ@yãG´Où0@Í‘‚a[üÅøõÖ±¿º”—qͽjζó&êT¤×ýõY£_Ý ,@À@¢–}c¦ÎÂ;ŒS§¿Ä¢VAU•ͳ]°i÷ˆP¬¶)êÎ3ü8¼Ma?<0Ä ó-8ÆFsáQ‹N ­ÈæYm’áÐLiÔBß»¼­å•'1[Ì’œ8>LíódšE‚B¡<%ú•Ö®²ÄŠDâû .î%ÎGËPÒ¥£cKù`Ñ@ÎMx;¦âÂwB;Nø¼.Vì)G†:êm{JégÏÿya”¡¹²´•‡Ø7»62Gš{‰Öø@]Cï{ijІØ8¥ ­È2F›Ø%„‡js°0V4§;J³a,×%(J9³¸lŸ=&qK7žbÂ/ I׿"t”õŽ›‹§» ˜(>äþ¤ÏÀHE ðVš2£Ww4N£Ù*dO!4a*ð.˱k¦ŸUM´Éذ£øçV2±hè[»_59:EK>ÜÕc@F;¢×v¨3(«Ý:,ØÀ,á vcÀ Rœ 3G[ço€×À§ˆ±É‡Vq¶Í-5¿ñ†z3“¼ÒkïÛø#WÉiy*è/¿Ídbjصûs½BÙq1r‘E;„¼RÖE߉¹¿Ffƒ'Lå%&8HŠžŠËu(íQ®rdÀÖÖ¥nWu_Ï?ùwmÉc„biаßãJN)lL¹ƒ*b÷êØwír§Á&sÚï=T¦}\üëýœßÃ7°êÔtÖ.Ze‘A¾Â"Á4$ÚÕÊu|ICum÷8¬ÒßñÄ›‘4ÿf9Ø8œ«Ž ¬ëw•ý”Ic×ÓJb»)¶·¹:ãCM}{Œ´ž –ž&Á?ù ç,ÒÁo³û¿ o´š{>ÿ'´ú^Fâþ·¶?)aÝ=–ØUöÚ¨áû²¨×AEJÿ«/Æ…Û÷AËO1ÄŸÿ5ñ²Ö¥L~g§1Q<ˆ@qb ‡YIr A¦í4ZCàÉIßï4˜’P÷Ý#;k]Œ¬ŸñxüÞ=£#ô²ûÁ£ÅH_­î£¯%ÈuýZ®®Æa›6ÿû`•;e ëR80œU¤³d<5)÷f^ø4]щ|³k0ýèƵoãAÉz#GÝ|(Â×*ãŠbz*³ÊsžZX!̇ñm[ëq»ü¯G~޽¦…🎼œÊB•‚YÜ_AŠ^;HWî9rÇz,¿+ ²2æ¬ÉP=f6ÎeÅê÷£ ÂwÊÿ}ïºÀûíí`ŸÄ·'Üõºì˯(`6?±ÂåÁãLƒ<7¬TyŽ¸Ä xÎÄÍd —“Z\Ó®-°£  áCËôç #ó]ã¿jàCX›'BÏŠt§»©ÚÀ àd)ô:áuH¿Ç®÷÷áÀ×Îí´‰K!«ÆD÷öy‚_—ô`\ðæjñk'Ô*çwÒ×O†×òiýw³ÂÃ8¹ÞßMcx-t¹Úp2ÞƒÞ¿–7 ÙÀ˜ôƒHK…®¸¨& Ÿ¹²µD=Jr§x-“*o©¢Ñí²Z÷ÃÒÆAc­Ç„…HßQ¨òÃÂ뤎RŒ÷þ¦•"`¢³.jÙÚÄÞÑ©•£Ðú4¹„ÉŽÅîÚ*_× ¬ í*vG»Öá†y“Â5¯œ½ÙaÌ»Bû¿^¨ýî«D0Ü(Mà {£âÂ1oK7–¹°–¾'ªâZºQT~W ÿK”=!jRu’UÂ¥GHïƒËs3üëkÈßÕÓƒ·ƒÂ¾‚ªB€¦׎à)6ó Œÿ]näQæ”NÇ™#²ÆñÈʹÊì„.”¿È×á+ÍÉjÔµ5¹Z_8N‚"é°,—ñê;<šÏUø¸‘xs±­ 3-[Þø×õø4Á®ÈVRzOj’è9ÕS†øš»Ñ9P›ž@äF¼¡þD˜Kzm8·(ËÎay‡˜,AýŒCoËMQ% æÉ‚é¼ V{CùkÔ •ÒMô"Hœ4Îs=…ª)dUøzüg}iõo’¤ë¼7ý(×D5*¹»#j? î1H·›à˰ˆ0“:ȹcÔ S™TZf9ºßÈä,àÉŽ‘ÿþp6ròFqDCýå»%ìÈËŒ‘˜{€˜/ê´q×µ]dêV’û]Ÿ÷•¯Ô(ÁÔWŽÄÆœ>o‚ˆ/ºkÖañÉɵYÅÇÒ¯AmHÍÇ¦ÖÆ}Õp%¢«B{±€ž{Xëò~ŸÆ½ªÀž/by½¤À28&_ı6 04¡ÝÁXx3GGÓPˆUëü­©€ B¼ º3µ½¢GÜÑØ_®ðB?¸)íü'¿W0Qp^±3ünÅSyË"Á*°‡ðƒb?o9Á𦠧­« HÞèÖ°Zƒœ}w ,Ç·ûÕfeOüÀ_4úZ.Çk¬T´k2U?šÓ;ïŸ"H"gǶJ óÁ}àZ{;ƒ‚ÐëÞUHNÛóºÀDÊ“&1­ï;‰{¢i3ëv.5œ1ö¢¤ˆüûV¥y*K7ˆ”;[‹™Ãè£N=±&Ï×9Î[”̇ñ½0v<>³`?ÖÖŸ -®íév€ÝXÓ{@Šž9b3},yd'{“yKVUµXÌż‘ :Ñ8†cÜQ£cql ™³ ªÞÚ2ý£¨YÑöÎÉR\æ'áôVèh´i¡>¬rû\‰Ð¢)ñû·_8µÍîc‹ö-¹Èkð^ÁyÇW„hÕ)°ËÛ¯§Ò1¹±!¼á™F+6¹Wzº¨Ì|5àÎØïºO¢ HˆRKùžwçb”šÉ ²¼ZÈžO•Vˆ_±æ£<*¸µ¬ý¬óŒ. ‹W6ŒP¿ëž×¬ôK¾$ž·%MÙƒ¬¼zG^x}44"ŒÔ­í]8-9bÜgvöÕÖªyýdXË¢<Åå–Yú¦‚]¡Æ–Å×EÿSD9ƒ3Öû .IÉR¢I—Îfj+³ÓùL˜”™ÑyÎ~ßn–`ƳŸ·(³¦wÐÑø¦#x¸Ã0Ub´Õ€ÏfÀ _G¨6œ¢™ µ²U;†¶ïö#¤qŸtÓ•’µÄÙ§7_]4‹S&ÂÖW™8ßC{þ€ƒ;¤ð=œp…î·óh2§êÜ[õéRÉÄ>äëøê–°Ã7îC_CJneá5߬Œ£™+´Aq-T}ï¹gBÙ&*nÔÿF¶¾É ›t»äÃñPšCï¯Ë0Â?bÍvˆÊâëY9°ÎRß º¥Âbd›ƒ×>¯¼–ß&Óí£ÊFëæñ’›ëv’ö~óÑ™)ÅUO•¸—d53ÎCìhäõ0ÆH`,ûü}â=Œƒè©d>«…•gï{W(ôiÉq¨!ø/Q ù³¬Z¯ÚÅXØ…ÌqUzÕç– ±ûè³–¬ê_ÝpŸ´Þ$ÅìǦQãSÿé? ïÞ•´`œÌ'ú6ûy½'É^Ò¨ß.(ñê²eËâQ;©"ðá–Îj–nëy¨ÙžkfÅÜ›òvý¬ßΕƒ3ßHçÞ¥—gÁµiÜ,4»æØdeù×U”ø~U#Wýt<á”o:ÁB턲·°ÂÉš¾›/¾u±Ôwã+ûn©0$Q”~©ëí)»ßâÞ vF0|éc‘–‘’bóy-YXúFwäzû7…7ú‰ÊãS^‘ûÑ€êâkÆö²LlI\v±Í´±ÿ%W,EØ~útù'dêæîàD*þ“}‘t¡ìÌAÅB¦ˆö-où‡ìäœC¢=8¶%–°Ô©í/¾ó8i Ûob¾#µ¦°'AÜ‘h^Ù5ä„‘Ñ2¹y®–~ßT ÓÌßÂOœ-CÓ3Üý*ÅßìÓÙªýᶸÚG`‹<Á%5Ä 2¶K.b ¨fƒ(~§¦Ë Î2T“ÆYè¢åî¿ÁkÊüAcjV1ê€ ¨C8+ DZ,©– ‡ëëRÃxõ Ì£ ‡Š‘¼Ä"ª|zI%áÚ{ÕÑÆÐI°ßSéRC©;´wRï¨uBI¹óÙ¦³þ*J~nîÕ¹:~#4ŸFŸâÖM”ÛÉÑE}.l5°@ð2g¢M6÷z^ÊW–öíñêO™oiš‹>Ä`†¢N¦*¯Â–€y¾º%'s].ïg%·%Ë&(cî/Ð,§F@ã5È3ÔÒ-IúÖë³,Χ^ί“‰²¸‰¹RùbsZÆ]ã« gÁK ?iîL×ñq(?g*1—«çð°Ví Y1å cìîÚø¦ÀàÉûvßš×Ñ8÷ë¯<}n™…;93ò#³D˜îWm3Š/ó ‹k6OIM ¢PÉ!E=ÉG=¯€¸íPͨ‘L)1dãš5/EÀ•]‘­c¿OüÑ+:TŠyÍ×uÔov­˜Ä‚/)úôQïu'8­ø¹¤¼bdÊ¿È?Îâ¼Bó³ð "¶û€º3­ zU¿„7(c’PJ\ß}¶Åùšý6PœíéaI ŽQ¾œ³îC˜;Ô&jæ¢AÃG°à™3a©y}Pë¼ý:Ûsû÷é«;bBÙ%Œæ(ùz> Ϙ¿cí%Þ²Ëh Sö0£¸9jøÔ?Ål6¸\Ÿd§±’ý¨ä¥éu¶c [lcøpìúÅ%ï7à‹«>‹ghúõmÚ4¿¡·{C‰è¼]Xë)-@<5´·44O›ºýä©]Ôåg-:.y¥´m!ÿ910cKÜ ïì ¶µÿ ?B.'†Á¡GpCi(¯lÝqÈ{œäXG XŒ{W]ÛÚ##[Íë=ÿ·gd™w9“‰ƒºœ9gg"÷Üw±ÿýƒ‡¹TÖx“þ¸ì÷_¤LèYß•ƒŸ²½íÐ/Þyà•!¼÷a’ίõ¼˜cX+æµÁ±R!Ñ6u@0h8Ùñ{»kqa)‰¼?¹”yÔ‘ç ‰ks S~Y~q:;v²¼Vœ©V£¡ò%•jôÎ]Uø—sb†–emŠr‹üÙï}ùxÇì–2}$V’F¯¿“¥%ÇÙ¼Æ ֖壞bÃ6úÑBÞp= Ó^²¥ð_NBÏã2åRÈG´ÙÕ¼yV˜ŽÿþŠèE|6{ÝÉkÓ{¬c1îixû6’5¼Û;‘{È<¥ÇõH÷Ó¥]¾*N™µí1ÇU³~)YãA±%C‚NéÇ ²Ö] iò½1ýâ ð\hÎÌèÏTÂ×ùqq×ò eÂMóvöpMµ–ö§ ÷ÅRî¥àÑ=fµÒÔú¸7xešt˜$ÓKÎ'Í‚)iø”¦wá¡ å{íc5ézƒ¶í¡]t41-Ô$?§afE_Sã|Ohq™BògÒÍ &ÀâšáêW¸ô´›Yú]®œ¦¢RüJ¬càHþä×À¼JÖ좪:FÅ¥q >öŠC×¢w´²U“U9%9Ä#З*øOĺ†3FÔìó’|é¿SùBÛ>ü€i`:gRr8*/ í׿Ï:4§´‚ÂÞòׯ†(Œ= ŽJñ{eâ <ým4õÂÎOC^”¿TŽq-(–Kd#îÊmíÝp.õÕPQžû`ŸŒ˜ËÝy²5O‘¯³ží:š¶ ?1_§9±qîcõ ¿;‡ß$÷”kMwqÁ4”ÊúܨŸ‰gZØÌ@»‹ùç>)›v´í(•Óe0/ú䦢TEiMfi®Íìíj%ÚôØ—"+ ž]˜‚sçd¤!A0ú àD¿§Raõ¸ÿX ™ÜjŽZ|¶ò¸î[ >¸1&ÄoãôѰ‡ ¥Â ÀÓ1ãe‰éÖ‡« ºÎ÷Ñ9)’>ô\\ò‡å^a/7·»~Ýc¢äõä‘}R[N3W#Ô™0Ääþ¯ÅüCõ@prAÕnRyœø…úBæƒýY4¾œ[ÑŠÀÂ~VÆÜ±ÒÏ ;ø…§T55µ_øO³¿üƒÔtÇu‰<€ÄÆPºÂV­’ùgô±Ö6³ºrèdKãÒSºèÞ-àB.Ñ _u2Jþ,ºŽ¥=ÞSmÓåd·½Ч…ió¯±åæ*ÚµB‰*ƒC˜Î±6Í|ÿ’8ÿq%¢2ym…W vŒH+Òúvƒ«¢£EDÀþ˜ç)¬Ò1}ÂgIh]7†Ö=a¹×…ùרU2L¥4´à*tž¹gÝyKÊ’pÞWdpF÷Ò°ÙœãÅÜÝ2±")÷ZAôe•àWcêð:¬U)½º qFå’ŠÛ[0C2—â°ƒ_êì:nv@e¼×sDÜf„,ÛF¯ks¼€ÖKx’ò9D7¡ŠX˜¨3o«ß¼«‰•L”^oIxüW»_Wè<»ÖófºÏ8G° ÒéuI+µá‚$Z­CŽÀ4xZ¦y§Ú§Û-¾©£Â‹$ÛØZìɸŽb¼¨÷b¾yd.#‡ˆ6„RÆ]O¼‡¸¹8o>;Än^+òº´ÚH¶žÕ»«q«Ý!€'ÞšiÝr‘?º–ÊL>@1ͼ¯DlC•£.t“‡m¨ÔcêK–‚ÚŽH”–„2уújþ— ÜŸulþ ðwr|~Š?€ÒSÌ3ÐŒ¤:[æ,œáÄ¿K´ IÑPCe&÷ˆwÊ2ÛÊvî]~BÈ-z®gg„ÁŽE2k'qüKš;lܽkÛ{ kT:{©rdî;k¸?'”ý/Ý^Ϧô¯³‡4ŒÄ/Tp¼˜zäàiGô‹E…ÉÇžnEZ0ð z ò>\Øuáq€=µeš´ŠÑö=žŠ&%¢à bW-fÄldÄjo¥I*¨„*“¾*ξ €QÝV“¬ ýÅÚ H¾e+MêEüìACÃ-d&=i*@É7 ‚›‰û» ð•9§i¦T‡Çš"Š W¿ÉB‚Îø$2‘×™qùý‹¯ÌATÑ!†éŽJÑôõ#wØ×†aî™þvQ»PfdÄøŒ[¿Ÿó$'"È-9ÛO~!âl¸Å[f_pM›ÞEƒ,´›cÀÅ;ñ\|Ýã¼(Þ!R¿Ð(*.#/‹5ÆIñ#-juÁ1\ø£õˆÛíšíŸ“w͉-åNjïp›\BÞåtâ Ê*öà´ZX–|GT™h×@AY9 ¢Íç$wø‹|i_¾gÍG×;“ÞÇTYÿ4µ•ßÅú$‡SW‘²T Ä´ @Ìv$½~¿fWûqC‹ãਬ½X†MdÙˆœÓ¥!=fI/{9:'AucDiŸjƒ€:Ž›HÁÊC¾¬öLL÷¯ù.R¸*ÜgÚ–7¬…@(Ư`«Ô:W´±Þ æÆå5ç:RE9ï‹(x/>õ1èqò/ŸÑMâÜà·©Ånø¶z#‚¼Ø%1%-qÎëÖ¹ß:çÒònsÙ9h½VS,~9=q}÷Zª¡èWE«9âû{YÝ­“ä—éƒËJ|-ECP»ÂUœ$(äP$÷ðGX¼¶{pdÍ•iÆ¿^á—âÝ3w§ÆPm]8ýnwNO¹‰A{³Çßï~iÜ EYØF\mÊP­“¸/Þæ; î»dÒJ clç³ü_bÝ& ôÊÚ´êùr¦iu9­ïrs˜0ŽÞoÙ… °ºœ’¼ÒíZeÉ7VhŒ1Ú™êSg‰Ÿ|l¥è|Åžëà6dJCœé?©ç¦-CÀ†SÞ …+l¢tf¤<õÈxû.“m|Õäxmh¯ßÀõ ²ÜèΚ¡ž2q~$0«ÿùñ-[cc¬’ˆNŽ‚Ú–!‹¾øD íåÞ¨«p6ãÒV¤0Ý õ.ý«€Ôýú{a<‡q€c³ÿ¸>(†É áYÿxGnê iÀN—ÙjAݯۻ”=sÏîÑ$ivÜ.¤8ìÜ2 Á4¬Ò²ïë¾ ~5ëšôȯåŒbÒª»ªúÖ* «†‹#ñФ½â¶á"GçÈq3â¹ àˆþ‡©®žµ#Üú˜ýê5ågs(,^³tå±Äµq§æF0¸Iǃ R=NôÄžËX2p®i]¡Ç('&†#5ý|·³7¨íËõ­;PÄ÷>šçŠ$d\V/•Ç¢C7C8ßÌŸ™!¬½ êç·ØæÔSŽÄ†t$—¤ìþR3ßd?#'´ç …¨4;Ø€ï¥:¤»NQÁ¿’Šæ/`ÔŠóÜØ$ÒèÏíª$Šê0•¢`+ ËP}NÐ’ßñ¨b]‡¼§¯3:êh/n4 Ø*CˆÊÝÓDmÌtd/æ¿ÈyïœlÉ @"á*ÂÛ<pVŽ?¾9ˆåÕb[‹â-™®k'Nœ¦…“vX\0¿w©4âܪx ‚^µ>GÆ-º_Þ­@Xv\±¥’± "ŒIbbSŽó¶2¶ÜÊóæCÙ®|b×=’¢OéM¾pçn“Äg6Ë-ºõñ¼uubHu™eØ÷PŽ_™>QÂE †ÒS÷ãr‡šÆÁˆ?ÙÍ2ÕÜ«¡Qæž]8ååOYbd¿šEH ߦÈw*!QhdOoì×}G©ÙN‡æô$þpuPx£Ë¶Ø·«wCÈã‡Ì¨]EZí?¡Ñ¼xÂ5a\²ºžÓÁÞ9^?paÅþÙî¥ðÒ˜OÏß—Ç':}1˜~OÒ@0ÄØ$^Òžªk™žngùdÜÚeÞÒTðxëg)Á«§+2"c2_!ïéžX`¯žE5Œ«A7ÒOcë0ùÙÚ)´6ÑVk×æÎ¤ìñ;áSO Áì˜+óìKÿž€l²™0·EÍ<-b”¾m\ÅÓðTÁRˆqü©;ÑRñ̇ó9ï²…\ÜiW)@ÚWÄ}ÜmTÙ¨ÝcãC /eÖñ-ÿ¯¨ m9TÚÕ4NðrKMì5¬àMÏý Bgã~8Ž8¤_M)6{ž²~_¢ôäã)t æDD7#»ÁéÖÛQ/§Om  Áêq§×%2ÖàküÖLZ®Š<ú¼$u†3;²ÊTsòSà1^xŽ@Ÿàͺv“œ^ñ Ó~»jn?Þøà=òFžL*uëë›ñ^Á¤ÂMÃsÊ1lÀ¿; wù1p¾].˜M‰&ÏN¢Zt±qû ìЂaD.(ÕTî]þ+JÏ#ÎÆn‡è>êu}?¦êäÅE¬ùé¼X`ú]hjn?%IÙùë57{îžjüÚŸ\k{O;ª’Ì©´->mY…ÊïæäÜZ.‰´‹±8&£hE(‡ï*~YÛtz–9‰qD ¿Œå¿Qç8ë§U’ÒÓKŒ¸Pd¾$8ÇŽSº4ñ ·8d`Û˜ç¾ezßYÈÊL šªF³9š"Ylv<±çÌõ¼ ïöCFŽtJ¤tÛx½€ÙßHž“ï„ït‘œŽ ­MTMaId\_Ú\‡b^¹-Y@3²™•“EÂkh³¿×b µù¨åXÌ3Å>³‰X±×]\Ù"å9ú-GY·0:a, Q/»˜C­“I,ªzÒÖÀ‰'>ÚW†¥fûÙ˜K'*h âÃA¯]7Éj¯XÊ*Hàò&¥WBIËêTצVÌõ–&Ï93¦î¨»æ»æ’S>æ5&éŠF;á›9G,t¢}lÓÔ}Òàïä‹+K-AB lÕS­ƒÉ¨i¿°wZaÃZƒžçXH£©7_ácØPõ*ôÍ.¿Øm)½$ÛÌк £öÜÀð˜èúÂnRÙ¼Þ8tfMiéâ>“‘ãñ¸Åújj!ÕÏ›’ÛZL!ó›Ì4ay­ÿ¨  <”ÓØšái¾”9"û…¬ÌqbdÄý3—Ä$SÍQù]ÆûUæ”>(æ›22 Ueeï€>»ÂóßKñ‡Ñé¡Õ%)<½c¥™ñëAR}.nI-Îo *0®M>s[ÂÞï«ñXmaèRÓ`t§^õTÌ÷r¥]B§¿Ì‹Ló])·ziAõ™”wg?nW‰Dçi—·Ô¾³Rþ9^¼—Ì•(£(úñé˜4ìl>jËA# ';Ã$ÐŽ…2Çéebwd‚é2™5Sï"hrW»nà%²´‰"`²<»Ù’ Nn‚†¢v°iÐã9½Ä¢Ê½¹Ýî]Ey룤H”€¶}QA]8¤@s½xŸabnñöˆÇÕh½R€˜¤b(U’K¨ˆJ QdXÖ™¶–ÛÌü%X-%}ÀJuó“gô‹sºnMO¡pï…Äà ƒŸ$£ýûìÄFåžø¦~Ï~¾—‹?bkeà8çײͶSV6hYË/ôÀUx1,;'ÿ²qÎQ8Ì·ÑW‡œª2 Ω:>[à-™G݈®ùC%jR~Ga»ï'3ŽÎœ—íBÖîÞúy»Ï´ôñ‰ß,ñÇÏíF5Œ1ì èòþ*±VýW3w?(ùQÖº‡ Û[Cºr4GzÍàåFJEXú¥Õu‘žòníç…¡žƈ¥ž&Ün¢˜ŘOÇ4t•¬?NªØP qK.yár Ÿfò Kÿó@ôHü*ç©È-1Éì€Ï¯¾ q¤Z³Õmvõ{e8¡Óðs.ß¢Øø^c‚çGqŒ²¼¡"3|üë±<ßìæ ÞÜ¢[šod…‡xŒHNyÄùÈù¹¾YLŠæ¨¿x–åM}e°t=ûR“ØðbId)Š{êí{,[K¾o´œ)=ù{­B EùVU§Å7½9Š­0H¡h’޹dPâ–Ð,@}EeޏÈz%¨&œ+vú-šáÊ^K.Æáƒòã®r«&G@Á„ %8ºZÃJf\ôaÀ0Ù¾ÂÁpµàJbˆe­’BäÃ~ ü­vsAöû¿ýé@÷jŸ1¨Ã$‘Ö­ù³ºT]ÒûÍÃÌÏ_=Ü©YUªFúćüNM‘qÆ€Áád^ígÎqc”Ù#•z•Í®·WM„EÛLéè­ðU¨ù$Ò8ŠõXŠ‚ú KyĶæ“M³äʆ*¶bªqžŽ›ŸÇóé¾$n ¤ÂÒõzMÆ™2pŒgy£{*,×»ÇA9Ç=÷â ÍÁ¼sþ@MV$¾]öD»0I U£bZKÆÞ·Üçþ`éAWkªp©5wÞÁ|g-F¾÷Ài›n¯P„Ë‚/?| PÊ{'³äÐ> ÐÿÁ•˵äX÷ž% `&xÕ0‡—œïù|5䳉Æ-K×1‘Pææ±ß)ë’+ bÝËb€$¹Ë|P—¼ƒê°»ä¬‚Š3,°­œÈÌ‹>¸Ii¼qÊóW…¸HÛ5m™*j[ô€ #0cª1úºr|Ñ ¡ß¿[S€ѳŒÙȯꎨ0ŒÄ•Pç´IHôÞ½Êöm-Žeúû]3¬/ž™Îïnz:ÒÙ(™]AÞ-zà ̇­ÿZ¨µÚ;«!ßã&³ÝÜß5³äâ¯Wà Ë/yª;—sò‘à‹.|ºIÙLk÷·ýu.È2»—üB½k–<ú­}‹wLÎB-·7žäHÜNœ­îó8é)¬H¦ mó×Ëæäß[Õ£r¾Q¹FÁ>qò§Žß^" F[Ê‹“ÒSHZ?4nî–»Ì(DY'Slj”h ôŸOñ–vO£{Îx7” 5ÏÏ‹ø‰dØÕQ"%Up¤Í4¯²•¦ùVWœo~r‡½'Ê.Õõ+êÝUŒ¥tF ßÚè¸n äGÙHfHɇÌ¥¨OÿüçóðWâ« HSƒ‚ݵÉ'tž6N–cÄí&m0?œ¦_s´S²ÊãVß/ǺžÇGAÜø!ÈËì[þN,ã+g¾’ •”õ\ØÆ"-D­TŒ7Iµ;¨™\m™¹3’R¨Yßì.'›[ïÛ—Cßõ,ª±p€.ñÉ<ÅñQ!}²ÙàcpSnò£Ð èÝýþ8¹Û'.[¶9i#ˆÎ÷7ÌR¤ÆU5E,Ì`ñ^kÓz}ÎñMËT Ño+eŸ“auå°¡tí¼‰ª¨(Zs{Ô¯}] `­ÿÜU;¨`l¦NÛ­ã×ÐÙÏš¾Dô7èÞfÉ­Lçý‰/éjŸ™NÛ?­›Äׯ¶<àüö{÷hÂÿ:æoßU)àš„4&ý…,!¶¿[_€øˆÃpO¡<Œ¶oímZ7_®ßL†ruvÿ½…•Z%u3²Ikøl„%¢,§ÿAÑúÖÀSϼ÷T³ùWù¤Íž…md$ ,ÙÕ(ð{ÖrOª±Ê.ÛžlãtÓ“sé/eS;Š>íÉr¹â-‰ Šõ'݆ŽøˆùìIMoÂÑ 8{‰fÐà5D1Bh³„Üô‹(”€hÞG<ò¸ b›»¤ƒnO³2;¾Î*p‡*/öC;9(šåÛV¸¹?KdB̃téÌI­¦ÄÈj?x¸†Ü¢mÕ>ÕeÞž¶ò4OCaFQ˜Àܧá¨%B.Õß5;¦A‹¿~Tœæ¿ö˜‚"‡M 8ŸƒwSP{ïÓÉuõ+èv\CG!®„$—5L‰ù0¡@Ã'9@5 Ohnx¶š¼þ€Í$1ÖNÎ5ü²/‚®ˆÇü%¥®2¼¾}¦84Pâkr! <Ñu]²ç ͈Ú!›½Òtúœí%]TJDº:é¦dnÌúpñdz÷ÚéÝú B–œõCT¼ÎÀž–Öd~l—UvŠžáµ°[®{Óý65öœ¥GQL‰*â'·E,nªÓáOÀÒŒxO‰DZ„Tµ¤£¤>$Ã`Fä%Wå—/OËpÛ8âè4YS™ ½ê©˜"sÕ¦jü}Ä] ºJÆeˆpõðòC!gÖö2`J¤ÚNhCœ—r©ŽÂpÿ5ù[ƒØL¶~òä QZÁÕb¯­ä˜}ØÌ&<‘Â{ÒļÌÂår7 q×tøòd†@ªÝÉÌ!–ËkÙ!«‡?¡¡k•e\MCJ\HU“ßÉm†áèw"A†)0°Òöô±… ¨-Äí`‰kâ_óòc~^m¼Iäd¾I ;væøŸ†Cp ð`•Ø"/ïp3””éßwŽ!@_H¼6“í‰ï ~Ôñ†ž¥†:›¿dÒ§×ì¨âDÎ}3B>_V{Ù2ù)3zÚõÙ¶@v ¼2û«§}¨X»ÑÔ…ß=“‰Îf‰h,¡9¨¢1NrpféÏÂ‘ÉØvk•EލqáŒâžÇ=m5›*8¸Ä)|þkyuÕÝG{ö<¼;Î=³ô3@©™š§_qp‹h©R NNÂÇôü‚²3=pÛ bƒÉé^øš„ª<½c(<ÑÕo¸}L\ŠRÛˆža*îš½¶.6_Î+Jh ðqVRƒÆ+›`eY ïBq³/彊´ºhñBÀÿޛӮàJDï}õE'µÐ;,4Î˜Ãøçå–›3!Ò¢ˆØQ² ²˜M]  ¼ÇD,U?"ºë·]ñ¾>¾L£åèi_ ‚Gó=’óò;#5rééR¢ÅÌdT¼9ë»ÃѳÈIî¾ÝÞ ¬D&ÏÕ„k O·½ýdä+™G@?¸Wăn4@}üZ#3ÀNúE¨BŸg=«T~/<á/ÛVØ*˜%JŒ˜øŽhñ¯“G­úO_ òÀaí¨;GŸ‡ôS\ãmù„¬ ù°û¾x²:B±0ÍÉ݇ú_ ùXPÈýþ 欧VSGue¼¸H¡‹ot»mç¡GŽ¥»þZ­ÎôP3î§æ#ÌÚ”áÐn”U—Û,C`?¨¨)î²SšŽó²TÝ>±1‡Ü¢E$<}³'J™0e0Ëóm#n{3¢_•:ÎA¡f ­´AÍlhœÉàHKœÙ!¶©÷ÙLƒö9ĪGEBq@, Ø.çØ;/ ozñ ®{‡¾`™V¦½þ¶3ã2Í`f+ç¨{S©¨©[1£Ï©œï$SC¼™+ð˜AívN“ß“$±žÎY+h<ËYA”Ò_PŽßd±³jç46=Êÿ«.{Å’WV|UZ2á)óH>Ha®îmŽô½J†3°Z¹=…9ݶOOÍï 6kƒ r ^xúr\U`ÜroMAH"ÍÍIÅDðr¢»ª*ª?À›#º­ð”Þ>ÿŠªºŸô‹¼UÓóÐÇgz{#gbá¶œš+A¿«äÈü]ñe8Ìz¥ÑLYétØOjÃ϶³ù Ѹ癣–åÄvðËjpàu?ê²é©ª?»—1cÀõÁ°Ó)éÅâÐf¦Ô\¿?bãþqI???p‘d®hc1ŒM‹%Æ~„#Ï/ûGEûfD uþí;5r«ÂQÏVúþÐ](Ô{SÅ„Cú"NücmHqïœkÉt¼†}wï²Ôò/³>[­–¹.ðKm¿ã¢¿lŒbPM(ò·¤®Yu4žk÷`NÜBÕ×1ú؉»“RC‚ÝÅAܲ[ã¬ä[̪¡}bö»}dÑ•ãm§€šˆÝiÉ»`u;‰ŠÚ[¡lôÐõ‘~mËŽ{…)…íëËGäêJ-m¶.tª+Õ’?þü1ëQKžŠCà™ÑŸÆÀ!KôÈ Pú©þ_*@Õ¿€û9©žìL~AÒÀ0Þ­(ïÕ)hÁ8ÈYWt‰\Ä DèØ [Ù=øŽ`ìã§+jèÅÑulœd毬¯›éštz¼u°GW&¬ ¹.Ê¢ÎZø lSö‰©Yžë êgIíMU¯ ·â(j°÷Žf„…¯]Ô„²÷ª2)üo1:Τ?kk¢"Á–_Wô ThÕ3W\˸âBBÅŸ2(#k½Ë¤m>AýÊÊb‚á€OÛÛk}$fçü8ÙJÈLÙÈßgšåº¥i&-U7ý–YÄ=X&sÊšhozX×'»ìÊ2( µ'ÙÇé/ç’„òZ~\Ú!ç×……ÂVN¥ÕØÛ¾ê‚cœsjñ|é8ð ª5áâ"ú¤Ò(#ÇÜÙ© ¼éjœéÍ QB9]4O X¼j6놈×èNC‘û¹ £°‚9]åëïxÔ_B7¸‚)§VøÀ½Wô÷q3m˜\žÏ=é¶ô?¦¢·Þy(Þkzx4YGO…2Ók2š9M’Xþ^Ní‡ø¤?å? ã)ò ‘@Œš^n)a…Öq(¸3äÿµL1œ÷˜áˆ¹ýxA·Jʆª½áŠZøûíëJ~âªC²ÛÏÜ&"\r"Çs±G )IVk§ÈZÓ-þ<çƒÑpŒ{›Œ›Cs,|ÐÞùÏBò8䵑~KcØÃÆáÜ¡$Ÿ1uæYdš òÓ߆ý¤-€£½F—Ú[ÓF³úîÒÈÒǪGº>*íÞUœ”†HéÑ·»æ¨{NÊ垃‘yïA€ä×3ÐàTê¯ßµQO3„ŠëЗ ö÷GlCÚöÆ-ƒ›µ.$rs¢ó"3ÂÆúè—>ɇ·ã)©ëg6­æã÷Ó„U¿öŸ½òÍüAÉB¼)z½?7ºËSc‰GÒ¹ws¶ý Ó±F£Š!Û@n•‡t[š+éëÌñJP\«'ˆ Õ8/o™d;žbGµû±ùàÈÑ]Gå…L« 5Íò²¶¬%z!Lj‘ö@&í³×<“uîx_DKþÎ닊W³ ßÀé¼cÎ|û›ï&9¢ÓÂ×Öfhׯ‹¥#ãà²Q¼–ÊÝHx¢UÑA·exí7Z¥ZºæuÙoÄÞa̱˜ÎãzÄÑùÞpÕ; I1=Îïd©µ-n|tçZo(ø/¢Y®´Ÿ¸NM09Rë}½X7îÊ!‚4yçkãtŒæÎäK©”Ê9ݦÖÉ´š[=¯åEFIÙÔ9ÔU­!ýÕ£å?u;³_>Í +<êO¦ OŒtû^U±·Û~"ÌƆ;WÜÛÊ¢qjrxÚ“?yxP†˜.°'VÛG% ‰CoÒ鈈&(ì)ær<ç¾/k åàÝЄ¬­·w¸{óƒ•[§ÖÜgÖè Ñ8 vçÅ2J§s!»³ çÔ|xQƒÅálœ’Føßú P ›X¯¨‘ÙÝux9IuÉ(ø´ô²Â°±]øË²Ì ±}Œ?.õ7íàÈí^„ )RFŪ!Á»—ê°èúÞFˆS s-ÈÃ\°†qC ”¥«‚Æ_Œ§ÀäðAU-ÿ³ÏaÛû¦¼Ümÿ!»°¤2Áf{e\–÷|mjˆË²È™7!5(ªȪ°FóUÕ%íÄ8q·nwµ¾;ë§dgm¿ýÆÖú![å3Öþ‘ŠÐÆ¿/ÛÒŠ´åÅ>?ƒgc”af³ n¬¹|óëlŸL* ^#ª(à{DMx¢';ö³¤z[м7Úº‘:^W€äÂc&gU°|–ÄX‡ñ«ˆ4‚¨’¬\^~¶¯qݶ¢V#‰yÅøÑÕú—áKÃ…3J§|‘4]Iùgd»òg\!"/ƒˆ¦Û¢gRô!®ëûÖÄs›o!Y¢GíA®is_8Tø«ÞºU"70ãmœÔÊ@ž*}»x¿Ø\W$ 0 †!ºþ+P%â9IyýÌñd+’¢CcL“Bk-íýjÈ^›  w{ï«$ý•TðÈ D…¤}Mæ=U“©¨7X ŸÙ#±@õUO˽N.8¬Ç¡³×T}vþÞï±tÓ0ôý.Ÿì#Ñ®ÂÝÌÂMìÔ•IOÁø Ͻßtõ'™i‘¬²ok%hIÄ.3a<¿žJ_F{DÛ(©AøÚðÈÑ,CèÆùf]~iþÙr Õ¦vÞF!`îB£¾!Ag°±š£jšŒ´; éÞomƒpkazŠZ(Îþ®Ï(/ô#@®"¾l™û+‚€Zý7ƒxçyÈJu¿éSpWhÈ(:ÉÇbøC’î¶Õ{™X‚n£ã}qBû?g}Ä\ùÇ LN6hJÇ…˜IíYª@r èÖL™éÊN¸ÍÑÚ mð@r?çuI~àUœ>—Sú@WîX|ƺ¿øÏ; Ô»ÿ@£¸ F¡V¹öáë£éÞæûebA¸GÿP_é3¥jK³³h˜ö¤ÿCÓŠÊW®%Lï2¤që!0SY(„À–ÚK¡_ìñU¯Ÿ/¿Kks`„¸6“Ê ¼tש½Y6ó{”CÈbFo†2þ™†¾ëƒöF­t…ŠÒÈXN÷§è}í OÅ‚çБŠt𞬠1Iø,Ç¢Þà¼|ûbÇKP•]²gÓª^qŒÅ¡ð­¶Œ|i²à0ÐèÑ¡i¬ —Kt’ö,ÍVµËÙhy%ä+Ï«e¿ñ'øïWc1[ÛèÐÀ÷ØAg0¯ä,“åÖ™‰a %H%ú6¼74bÑ…†ìM‡/‚Y(í )(îõšp@Ë‘ª~ìVnœArÐáß}4.¤ºï"¡—Tt¡²—f"Ѹ̱¾¨ß.+@·[Âlè»Ëm)"’"mX/jvLÐÚÏ©©ü˜‰’¹ŽÛ¤ÚiñV\PS9ïb¨ ðÁ£}äÞ¶D¡_[ÞÃMiýÅ· OWóÉ®¨÷#ÂE¿Þ¼}á3T¸jý*ë>|BÀtè†5¶ÀÌ׼ȫ“=(÷»º÷zw×(öŒED]«€j›¹8³q*¶[>¦±qF—Ó¹mËgÂ,‰yV8¹Ó«æŸô¹²uSUfŠã)0BûÁ’·ÔŸ\å*r©b‰Jº+»¹µgêÔ[¬.$Æ– ÷î´…zŠîÁŸÜ3ˆõ¯…F[gØ4 /–áSï[íèZì}¯vi«5´¬ê|˳ùÚÛ¹¨å¦Îו]®)dš:ŒwÌÔʲé(—W@k²Eœó£57ƒ[{|78m89Ö‹;  ÄW•3}ŠEjõ„ç†aäd@Röf¶R·<ŽßÞÊiù‡ý(7Aç#po Tã¦p¹Â°Î¨Àžd3„uèál¸YÇ¢Ço='E—û6eM¸%uÕzø'_°E;iTï-KšE·0‹”_ëg¨Ž©žf¾ÙB Lsë㣚èIâß/÷á?ø’ŒJ¢Â&:zŽ‘W!ŸzÐÐZ‰m~‚“ Þ€ÿ-*~ä÷®Ø¢\Øböƒ{‹g_‹>x'fjåUœŸIF`í!JÄŒ¸“X‘äì mŠ}qÖk <È’þ*V€ã€M­E¹ÀÚ'%YŽêU²TG’q¹ñ=Шžùe¨ÿ¦ÌKwêÙüú¦[u§t«©…\ÖûŸ¯F`¢ÈçøÊQéÛâ–IS‰°Ùä\í¶^ãLžÀŸõÜ%Ž!DtÇìç3OsÑg»O¤øªÂ5¼†6ˆ´>ü*ÞŽ˜ð•üK3„$mcr9FŒQ‹Ku*Ó(¬á›–Áº¹÷Ü™aÅ*ÖšjÒäçF–@þ±³3s f ¢DäÄsŒý8dšqw¦ÅfÊíÛ5æ¹Aîz·6ñ«.*H`·`þê^úæ9ÃΧ؃÷!Å-jc(R‚ €| ê·æÖË™qíǹ…3Q³VvHHœ­ýÝì~Ìc[œ@ÀÒ#ÿþ"\ˆ¦Ùì{ú¿²·_›Q½m~l§~6k+ÖyFÊËéPˆ°È!‡“ Éÿ+CïeâD/ú0Zì•r°4è í×ñTm¯‘§Nõú©4¢= ¿dj¬N²ø©õü£›íÔ1D†ÌŒR¦>àä\êj(ÎìÕQÕiTí´{ì3Ù-×táºùî*d UáHCêfËdC4†€Ãw ÿöL¥\„n³êÖ Ôãü øß*^¦î» 6YAX™ªï~S¯ÕÎ0¾“òÜò5V¦êòŸ°Ò—O N5'%[òí±ÝáVï*oO~=ù"¹FkíÊš5y*!±!÷·õ œm*ÿZ(=œ`سša‹ôMÈ@ì¸I¿Ì¹§ƒvG¥·¶ìJØ0€X#·§ƒxbÂØ†P£Ó‹ùÁ.77o>@`òЕáò”’ï€râXÜÑibAÚ5_,‘¨RPy¹}v K²4ù^Qu Q‚Tl¥öke³ý•ß0uˆÐ¥a,a¤­^)Jo=˜ë½‹*/«ÿH§ž—ü~z« ñúŸœrJwßÅ>RQ<ñÙÜ•Ê{ÂXñP§ð”çÛ‘é‘Eñq^µ·…Ò«hcª‰—+øRO€Â=ƒšu¼Æ­›¬W&êšKÔNŸÍÐÿ‚ BD òò)i€MZˆÓŒ #ÑE8]‰cƒ´•úWµê¥€d®#ågük<ýv—>À'ßHv…së¾ÑtÍ_LY#3ÆÞ gà0»Ò67 –®£º¹$Ñ–eŠv:ŽÄ(Îà¹GÒåšÔü¢Àu99Íæ*–)0ÓÐê¼u¬bÀ]ÀY"MžU—{܈öv2?ÌÀþÍv! ,~P„ÅØªlÓÂQÄ9»×º8Õ: 2)›¾;Ÿøfg}_ísïúý&fç…án‡„ð™ÝQpíïbªC‘ëáQ<†Jb*RÆDôÜ3Õ^PP¢ÏV=f1§rÐáTÇ=ypµè'! 1´WǧdÛb= ô7¸@YvöDÖ<#ÝÚƒúMÖგYå¡EÜØý­5½oqÏ’h°CwéÚØé¾Ãß’ÂñçïѬê%¬¹!™Ù± ‘QƒchÑ¢!Zrë Ù‹lªÀ(pciXüŠ®|¬©Z;Ψ¢$=ËgÓ»Cß’ȲSÖE!è.ýx QÆ)¨…"T$¨º\´N(`åö@ËóÇ.@xSÑgÔ]ö?dB¡Ð¤ö‰Ýn§ÑÞ—ø\WÞv\Ö²ó@0/]¢ZåCÜ‚mÑ´Õ¡Ùÿ;±R9ØÒÛÈm$Vo* ¦ë¦<™ý>‚¦lùàèÄ3Æ/¿øi°„ â3ÀãC»D™aƒ;¢’=ªâXäk"Ï€yuiªô#Ãã©þRîjÂDËhö”U–o‹X¯~JÄsÖSXòà<Þ¸À6öR¦« ˜0ý.(âónèÈöì1p€à`ÄÉO[TÍâN4Mß÷/»ˆŽí\]I*„8ïÀ 7Ôz&Â3£] 0`x’í’\Uä°ßÎÊ`‘À/Üij…©0HÁ ¬×òT…Àw›>aýyM¢™E_,Ää‡BÁn]æÿÛØ× Y0 ´h+µ¤_˜¨ÿpŸA=ñ0ÍMS´‘5ÿ§£ü~ùÃWNµÉ¤ƒdôÏ´›ûÌùB¿q¥ZÙ,®e¿þW<Öfž&iÒ6ªÿõfjж÷0šüÀ2õ–-Ip±„›O5 Â7aþƒÌ…ÉR%“í|ž*´°ƒà¤ áþ5¾À—M"Ýy®ÛS{jÄÓ˜.À^ªḧËrY^5—á{ üP Jý•¸á~ƒiù1„-M^®¯¨"­D0@2vV°ÒÜjM$…˜—ÄuoQ‡œ±“z[#:·l dɯ³Ü]‘:ݱ¼4˜€9‘áyð›J®ÿÛ Af»Ã¨wQy¤§ š|Ëñ߯•$K }¾á*>¼8 öމDlý?¨ïs¥5ǹ‚Qä·¾ú_j…÷N G̉GìAKŸ(p²õÍ÷3ºÕ ÅeòTôˆ/0³W>a=]äÏH;Ïy•u €Ýϗ悸qËî Ÿ/—5lóüè9: ™€ææYÕó£CÊà_êÃQ€·§3"‹þ*úµéð=Ï a>Ü÷;›úNäø¦ô¶ž¯ŸFz‚òÅÒZ-1daPª›ø†c¹~ýJÝ—D¨ÂÑù×_e XÍÔµ75:˜oã¾Zwåzyd†2À+H.6PÙ oµLí[Ø"Ò'º_üY÷iÐg1Á¾g“°{~xŸçÃ[X…Šðœ¶Å„;‡IX7O.:~‡*èŠv<š¹Îfú¿QTEr™AmtíÖ*wÓ pꀞ³·—²\øóãzÇG™òÚèÙ%˜^·Í·§ŽÓîîªòš®u|9pÖ÷]G¾îP]M ‡ yj> õÒÓ‘pÎ~S<ú§øHnó’~  “þ™¸)|+˜Æ!]KP’S8ºYWŒ™e^’.´ïÄ´&Û(Å3<äö ø‰gv͆†¹Tð<áÿ™hï:m–›(3ÿoH¾A_³E¶?üìØl†¼³¤ßÓdOIÿUg ^ûÞbÀÓéŽYMºz´Öþ¯zÈî ß²· nò†Ô× tareCÿ Ó­ÊF"™ÙÊÎhJZÂ`4N­¨™ÏAL¨„çX´vƒšëäA-ßïŸð°¡¸ÂM))‹"Ë(Ç K¼8J…6¾>âõe{…M–¢¦†l Zöî"lœ Í]…»nᣠ2àêàµo£V(¶Ô¶„¶Çk4ú­¤‚rS=ä§3­¬ÔÅê­Ç2¢WBŒ\zánø[UŸ° épèñi‘ .^Bü®ÝqÊ6¿=IÀÜð¨ÀÊ[i®1O°´‘¢²)I;5ö:â]»fY'ù{(RMfnÃ>K¨¿¬ÚpPÒ=”TÚØZ×±j ÁL/5í®Ž”s?çë$"BO>k؃‹•hp\aÞÅÞ7HÙè‚ð3EʆªÄRÈWW%ª%ߊʦÀÉŒÚmÕõ#²oÛ¨*ä7jPM†÷ò™Ëcèíbɤ%ØÛŽÀÈ”@d!•îîÍÿsxæé‰o9j[¯¸ˆ †!ºþ+1¶ `‡` J‘•¥0é|졦Z~=¥5•.ø•%’gŠÎiýg*gËzˆ3_µWÒ)ÿÐ8Æ—¡®æöbfæ¿Óäš3¿ åè'[tûZœn³’=Ø'4ÔG¥ßsüà6—ŽŽÑCâ\büÎÃð@·ÁßšÛÈ?%]…'n^G÷„xí”9dsÂT6«²Œ}ÖORÑ»Lv3îþ}ãÛšƒ'èütÉml nåBÝl‰ë£”І¤*ŠÊì›è$Ñ"y‚Ãá'MËVq>ÞMí|™á òw0Êc¬uX‡·æ¢%9«ph€Ï8ój,R˜9õ¥ÚƵçü¨tŽ…ÍOç+’x+Ú÷·áÙQ™n¦£ICÍŸJþÇ å*I‘“LQ¦‰6M-ÿn[î7[~¨ Ū6ޤü¤4Ñî£Cž¦9Šr` 'ôÊÁϸG1¡ _¡\·\O®ç„?a«>h„wØÏ#D£°^bŸº=¼“Œnµáðkí®ì—èü”°%|jÅÍSàdÁ‘<Ýßëx°ãÀ$Ï5 n›_{qát(ïjY/ƒ> ó¯àNGJ6v¬­Þ0ÚäæG ë3Xþ #°«ødÊÇ^KÜ8Û÷dX¤Y¿8®ûÃÄÇÂȸ$I»v¯ÊÎ@‰¹¦ƒ¾Äbb1&DÃ6Dõ°]õ›°YÜ1¾oÕ!u„í›e£)À4 ù¶ [äê_%R¨ìœû˜cIN ‡§%õ ‡'¥]Å8KáhEGJ_Äà˜Õ§¨uÚ…ƒoäÖ:ö†ŽþÚ߈­,ûY™c0|q†ÈÍÉø¶&–pÂ2ÕvBe_íFÔ•*OÖŠ Ã/æ9Åf4È=çädë¤XtUßlÆôµ™“ÌcG[»ŒFÔvÝ]½ÓÙc85±áMµ-Ä£yý©÷¿¨`UÛj HQ¦ÕZ†Bõæ9Wyç2-ç‘—gÉ%œSS~—¡Cß„Wª „§ñï³rÙÎâÏÞY€ge/@EWÛ‰Pù™¶ÜƒËßÿ ú`†¡ õKëá ÍHÉAN@Bßµ “ïT:áN*´ýý@ÃÑ´ 4hìÈá”ò(NôšsQÔØ§ü°vMÇÓç€åD°ÒÁ6%¹8ö:¢¨~©"ú–ÊÂöGcäffìo‹ÛÌoÁ#Ë|‘;u@bý„*"8‰äW¡÷ ×’’¶ ‹’—ø¶ [ä¬BxtcµSòëYÛ¾åãs^;¿¥[sÆÅáe†oÜ„+ÒU5цô÷Wú5†æ¦øp³#ôߟ«[©ß~Œíë¼4Pë—-ß)=˸ßÞ3ðœ0S¦ÐútxSúmB. Âu4I.9¸ñE iŽa3ÌãÒc»Â¨{Xä/@‡Õˆºú5ÿõm´­Gï }”bpodšn›ezÊÁéšµ–íºmøG„^ÿMëfœÛz·$iŒQ©ïô½ÃÄ#HémV‰Ü]êö8À²@Mfj‚ ì.°Õ¨¥l0QNyVh2Ÿ+8?·ä.„›£ÚhŽG[æÎúC©¼ÒÿµóãªÝlGzÞþñÒÛÃ9I÷ûÉò‹¾î·d…Rô8ÇÌcä=T© 9ë[xe»rÀ¹éÏâáÂÌ–t¥Ã9øŒìîªx܃‚*‘pƒ,šÎ |€7›ù’ÞXˆ¶ åÚŠóéçš°¬Cxôý¤m}3›|e!³¼N¾Aï_“g?‡îC¼[]én=`šÓ4rz€¿QITÑñ“ñ»•#ã¦zôø†œ­‹“Xñ›þ6æçôL½cËF`q¿ya4›$§<ÒI)©u£2Œë%JÕc¨0™Cà€l\‘>^Í·jKަ(A„Ü{>RÍ{iW‡-*¦|UMcÊ€‰IŒƒO `U¡ƒ8|ü¬SMªßÐXn“°³“~Ñɦ–ÏWj¤,2‰e¸Î<£õÐvj ÓÙÞ¯öu¦žÇqeÖ ‡Uo`û‘)'7|Li­ ·ÂÒ*>1‡¿Hû}ºT&–mê„)(aÒä\p<=8Ò oˆæL»ávJ¦ŸÄ1YìÒÞŒìä˜ìøŸý2Çoçê?Ø45 VLËn£–û× azˆ/%ÁµôxI£t‰Uàè%®Ú`_;‹@6 l&”Ûºø‚øÞ°@”O4íÖïÞ˜•8§× b Ò ÷œë ¾QBLг‹žêÿ,IbÝjóûèÇôo—}zzÒ|Ð=våj#@rq!Ý_¬o­+q­4ðîDÀˆõá×ì?b/RQz‹">?—¦È o÷BDs»þ¥TŽ/&B^ßÛ^³'¡n`Hü¤?¦£µb²¡.þß¾N”ôÈØ±{ðÃzkûá2P& È«ú€°tš òîfà]zÏii}ŒŸS°ta”’IïɆÕVÅãHç£JR‡-üfÕ…®1€5©^j &.Ê”Ìa¥&O¥½©Q¨ªZ·DŽ6<7 þܘ?3’¸6P»õ Øéo~Æã›‘äã\Â:ú¥ù¸Ò+\w™M’ùJ„©+j¡Ö:¿@ûbÜkÂ%BS†u¹]Œò¶Éœ¸†8 II9;ðn€›kŠWì‹"mLî óLº£\㔃=½>{‰2ÜË(ÚHjYë›yæVBVà°ˆ{u—"§´€Ž/¸Y¾nÛ|»Ó\­]˜ÿåÐ_j%¹&aoÏlܘŽQH¤64Ó²±ƒ_F_¾.8²l(ÓØÅø(Iêmã¶ä22±c°FV0z~ݳU¤n?ÂÒ\˜1F”Jj~äÓXóã†Fþ‹:d ·7Ð$ŒVLÇ\M~ÁÇ#³0d>éË)rš©³6”•IAÉþ¯Kï—ø‚¼k}ØÅ8–湊éÏZ³,›ÚÚ¾p^ÁÔQ_ H| &DyšºQY“Ç>ÓJMër7÷ŒÍ>>W$D9NÙ®˜ô±ã‘ß6”V=]I$•øÇ$š$ÂBŸ}±Ä¤1´¢±èܾ(E|ç´$^:z ‡®­Ï–ôu}[Ë׈ÇzŸ³hkÛÉ'Fxø>ùdhŨʃ¤l{æ&c»­½Å[ ¾mÁÞW5 Ð&0”ºWÁUž;†¢`ü{? ­­¥¹ûVš(#WõÏÔÎà+1¬`W7dÝþ´4Çò¹^È϶:£[eQBçq;ïGÒFRî5™ç?Ó’8øæˆ5DÑȧ—;CÜ" R¢®×ó0x‰¢"¨0…uÅ¢¢)°i+i²ö±x\¼¢«ˆø–`ÁÄf«£ìvöâà•ÆDNé]ôEà–W™ÑÑ2[$Ž ,ô òYî6$Ÿ:2¸d–Dy¢ª'’µ[·B.„~Pcfõ±[Ážd'IgPе5;d(«ºÇþâÓ!ÉÆÞ?è£GIŽ_Wñ•ÍÑàåN} £Èøþ#_G>48(í3QOñmn_ØL®ˆáhÐÁm­d À˜­Kb‰Ë^9hõÓÚK([t›*W$¤37¹öÍü}˜þpÜÛ˱ÍíAª$Šœ7>,"¿WnÕX¿é…Õ3ô^Xí„óGôòMœIÏŠISªèùNÈ™òÆ9Rƒ„µ&ÿߢ02ý±ÌÝ¢*õ(ŽñÏ’•»wÿTðÌÊÂxºÍÔ©„ì¹o0Å3üHt½;ŠP¶)šþÏ$*tz’®Üì>³„xœ¡ ÿº™4¶ø~O[A¦®£¦E˜DzÇÝö0†SöIz±3㪅UÍ›µ­\\Ô¢ô„7[`0:Ÿ(JC7Âïªv`µò½a¥« È+µ‚Î*ü4W~¸‚»‘ÛúÖý¼åuªcV•&µ;Òÿ»þÓ8çHHöJjG‘T*iôpû˜ÃíƒBðêîWüêldl­ˆãârEʃª’­¥¹ÞMO;‡“™}[€A©™] Š*c`a‹Ÿõ~€•m?°@.òiY¬Ëý±~¥„ºWÅP÷‹œ07‡mF6VœÕ”þ2°äá"’ ¯yd¼1£âNT7sî3s´svzJì15Å;L±5¿äº2´¨T®¿„Èzë¨È!£o£ãùiù­ Cz¸OSZígJð\GtuªÍC¬·êÄŠ;${âøØ,¢—á?cA¨¹ú«ûÜhÆë]¢-r8|ëàaeÆ:¾ÙÌ’·jË·x£ìvA¸x½vëÒx,“ë«RQHØ>é;sK¹KQ7ëY^jN¥žQšIT1& B¬¼bQà[ÝúšÕß³`c®mË+W+ŽV~$oÿeмáÂ…œålB$ûS_±ulqɵïwé‚_Ì~ƒõÏ!ñ8¥  "@ùº,5I~VõS¨vr¾ò‘BÇÇÄÇ_•ï£xºÊûAÀ›AîÚËÜ@ë¹B³®+ÓÙ&Û«‘ncÑ↠S³Äc¤äYU$GE’à˜y7©@ ÝÝÓ©ˆòàŒÁA(5ÓÔb§ÞõJOÚ zYh—…6–(Lù §”¶VäÅþ•E nÕ»~p®lMí9<ñtN+ÁIÍ®½œK:Özf¯ ÐSdeðgupLs­Éû-;ˆÞ¿D䋆õ]S*ß3ÝWg´î¶‰ĪGi¢¡Ëü¹EnšP~W`˜(’äRÔ¦#®|‹ÜFèÆfí€sð¬‚yh¢]Z¶®Káâ¸ýˆ¾%@e!Ôæ <â@îñ’úêž8R)fiùwÐ’×Nn¿!“!É Ä´—¾E/šº•sóraßLžèüÙºG¢©ÎÚæst¾ù]”Ž+Ñ¿gXv÷Šk™2é$î#o bÖ"Ú ´çÛÙjm º¡åe?Æ•¬3VW?<ÇÔJþ+¶ÃȤƒKyäC‘A?Bÿ9¿ 󒙵݀ >O…“Kêoüö^{x(0=>Ù5Sd7wR j þõŽ¿åDÐa1ÓK±Ð¸^·Yeηû‹ñOÍGÙ#Šá݈›èB3ÌSÿH¸'Õ Ò­3ƒÁÍR ÙqÄ~€&tGí$Ë4nŠHFm`®¸b03SÓ Ö<éeu=]Íñj"Oe€U€ôëÿ‡¹ˆüìâ A#·ºzçJçFbp»TO†ñ7ÍEý0¢p1ÎBWZŽXÿ{é]oD× ·oIoÚ :YÏÄÅÎ Æ—^×VYm‹&ÞØ:>¤¡2§Z%XÆ/0"jzdÉ¿0­~rX}ä=\nf@ÅÇ–<«05ðóU€gºµaf|èÍ( „·ÖÿPjûþ˜Fë¦Á—,ÃÁ¹-Pö±ðÑè[®¶7b=ØÝ…æ\jEmîoHð…ï)'¨³Ä0N:è¾-2Â2ŸšPص‰éWYg_»¨„G‚ ÷; Ï…Ôàù L›³7«ì4Mî¬ÈíQÙ:…[c óÐðw‰¬ Ä9A‹Žó X6J™÷dB ~1~Œ'Ë0KM’%ÐpNÑ?ôªiIøER0ž²#÷&öê-(Áv` Öï$Ä)–ã mž³=wKg>ØÅ–™Ž—’úlSZœ²Ëø¥ü; ´ß'bþŒÎÀ¡Õ± ± =!µÉ“Y/((­ŠKžÆØ‘kûkµµ“®Â÷_Ôó×l ›†§•ò{ Ø(^ø†3üÏ:ðõý!7swÙããa>oK-jÏÝ@\AÙ½ÂkEô¤þyêÆ3’SkGLØI(‚6±þ¶ïM—s­iÉÈ-—h°ì9@ö–¬eÏø¨¥ŸÛ΂ôÔi{ç+{ÕfŸ¨¿ìŸu&Tiå{ФT¥N{7tâ‹«Y]Ã,ŽftJ[ÛômÛ ëvx\Ã_6”fÞÀÏÆŽX5з·û^äßåÖ-=J“‘馷SÃÙ çJ~þfʶÿýÊ’÷Ä"®8ÚâÉ1/âüt:©ä/×”J«©Ñ_ú£†$?;š"ÿ†ùŒœã´¨9”<’¦1ËÅô“ž2 hçòè¶+h*æðˆ)€ ;"¡ù\$%ßB¨œv¯×Y-~uG¼[mw²³ÊœÑguû3gu@Ýv{Í4;0.E-õV#dçÌÇÈ›py¢°Ú¯>fíÍ-[+he™°²ªúž[Š·£<+ ÔÒr½~Ï_ðÉøÈ‡§œÜÝÚ ±¢_SÕˆKÏô§“¦¢ø9²,WTŽª–}<)„£zßR¿ÛIk*˜½8h$ æ(H ÓEºˆ¬Æ—wwÐršV©§ù¨)—GrÃK$lbâÓa²()Ü«„ujW% Wy?³k¼#V8ànêÉ%Œ5Yôà ,0dCt¼.ñ ¶¨–)ǯm[´Õ÷;/WŠbœ[•…M{”-wœ%g(s›Š‚BK NÎ#S“ŽŽêEžèÌTž^2D£E0©ˆ %š 5mH5|5ŽÚÁ£lÐW¥–¦q,Ô[ YUý7®R¦ø¼†ª•Ù0ˆ¸£ÓM"=öè‚ô/ž+cY±MÞŠmõ¬æ€-ßM„j[Ë–c÷×ÖÁóxÑ=\P u·ä´iÊ–°4ºâ-úÄzý(­Šóþi$æ¤H„ÚÈ™à02M X.“‹·ê[†›W“ç¢gz°Aà°ðö°Ø‹÷©˜î<ãX¯?ºóOn&Œäá"§V#MKÇþ¬(~G¸R6„šXh²*†“¹Ï'`Sá(å·*{Ý75ýRʤ‚a `EØø$r‰&53ÆV [½&ÔÏ[¹d€…"/…iõªàSûG8ßÀ<&än<š‹§ŽÐã øKUÎP·^É}bª,ïmF¸ÈÅn±JËN«4¼‹¬_|–<üN·Æ€ÏQÓ¥ûð á°¿\r0'-~£¿Ÿ‘¢ïkæ"vR²÷Éé2¼ÖmYÔ1ƒcÙЇ “Ë}h[oG &¾éÎÎ/ä˜iF!7^•óHާ$«/=‰]!#Œ\æèÓ¹û\f¯ï{Ì¿8Ó=N£ï%®ŒJ â͙ڊˆºÜUû›J£ßrÁ“cÙ*ÒÀžÓìÅK½%îø„ÿµYn«ñ¹KP^*Ïã¾JJG<Ó¾Møœ0:ǃæÓê\³‚{ÀÜ೸N!¶Êc'±8Ø¿¬}-C<%\æÑ-mñ_nõï‹¡­ô=Ml§Ü˜’ wpÙ;g*ç/2‘¤ø:VÓ‹ºÆ®iä:?@£bÕ…ãÕãv’BÁqƆHMPã‰Ê»ØŒ¾MÿׇÑ'±gMÔ FÌrQºzv4K¾þè ¹ ÒŠè6Ÿ¿³§€$#öyQïMµ²ŒN¾Aæ—ÖÖ±ƒñu,’1¸EËšÕb3F¢Dï¾ÓŽK‰g ²Z)É^P{Ò{ûZæ-šDoÌ”ù&7‰¤©•¾eÃXâl"wnæÃ,|º ðŽµ¼„¯i nšZo:Q_ç$'gÑíƒUß´fðÿD‰/ÒÑ ØÚ¿ôoÕ—ž4ëÁlc&í|áÍŽ€( O“^U¹xâfÐ¥y~åó<†6!Ëf¢ÞßvHÁ¥’`è.çvÃøU”úµÃ–ˆr ?5ñ.'¹Nf½ âV’› !¬|/«+‚ô)a;ÐÌÄ€ñoþŠøÊïT¢Gç܆¦Ws®ú ãPç­®~!”ú5‹¾Ý5.dèÞÉ ÛAð„¥K@´^=å©Ä³ðfoµð.ð¥9ÂM !Weê'ó´?˔̫Ÿ™² ž Üì}úª×ÆZ¦j.N'6*`Ø<ïð_îú¼ ý÷·„*u+‡a¿óΑMTL¸&ÚÞûýª&5¼àÒG$š5þ0úÎ å_à•Mé-VT~mEÈ?)õ\‰@åÔ€]3Ëå‘YÃ~iÔ3hö‹‡¸ ë›Ï ³ÊÍ–¾SìmB`}©$ÿº0yü| îºÅm8®Œ^{Ÿ“êyUÕö“~ƒtVIŒ£³ï˜ŒZº×4w9€@Šnç©è(wdrÑs•Ôýu¦(6÷ûüG©’È ¢Ç„/Êí.7ãU?í» ¾†n¢zËc<šP!ŠØ¡&x–¤ƒi³þÌÇhXžøÒC†öýóU…4†Éœàk"Ï|èPwgâù¬]PˆeA:´œ;±#3sOkïë}PhpµÐTHîÂz<ßË;˜—Òúˆ¥5_0W!áÃó§rEot_æåW‡.‡¼³™Qæƒ\w5.õ1Ç_Yƒ¾{|ì¦Z†€»¢C© ·-‘K›D<¡Àe˜Í+úêó7'¿Øm=EáâõâxÁ¸ÍÁ©HVæ fß8Ž!ŸK]±cÙò»q^n^rÈÍæÕq´=ÜE«îšNàå¥,ÀpÒj„êY­E²§=¿?>íË«Àá qÿÿêfÆ^·¢ró+vßDEšà˜žœ6üosKÈZCƒó€Çp­¢nÝuTÐF—ÓM…‘å÷…Åà☤äLþÐÁðÖ5Ù%í.ÜNo®«‡™ü O…¸\Ðm'Ãl¹9¡DU‡“Ù}g£@gtyjò‰ÛlÙ šZ£Íhpí=ˆ’+Äc³ÉX µo3浉 uɃº®öè’<%†gýAÉØfA¸"#o“ KÕÌ;2ã`ezX„á_Ë›&Œ–tw€8#ùgf‰<,*[¸{ñ´vfXÔ\Û¢™Ô¦›¿~l°£HšÓGERå¢ý Ú-ÚýåY'ݰ‡«Ñ×'åxL˜6–*â- ²40Q–ò+~J^Ž8'wÕ¼BéwÂOÊŽ_ÍKÇGR_£‘ÞS²”7nÓÒmÇÔJ»ÝpàIù•öÂ@Èðæq´z…9_@)2‰ŽÏ‹ãüWgÅ!md›4Ûð]që—‰¸¹ ­Ðq'ª\QúÈ‘\Ù6?¸uœèÿlÈÒnÕ2fó( ³šÑ!6âdïúÃ3/Vv‘³8ñF/ ×CdâË!í3°ôZCªþFŒ ØÇ«êœèBr¦ç*!R“C¸È»š*z³{j]Ëãá•YL ó²È«ŒõÅÐÅÔÉ=Ív"©­å½!ßrv.­»¹¬ã6Gß*ôKᮋ®[KòVr }Í%Èdqiÿqéq”+¢FƒÀÓiìôb ±+|ä±_dHíIvŽ’tÚDÚ=U.¸mÒmÙ?,²ÅLˆZ6t­¢U¶¿3 çø¹b2M±=ßkq´niäÈÞ·¥Q{ÛÝ”´Ó•¸<ˆZ4êð„0‚lN•”gj] d±‡ÆúŒ"€3vò´¾Q_š, »4ÙdÖܸ¬di†Kü*'¹Y6RDv<&Ö7wš«¿À7¼B_+ÝÈÙ›ó†ÄO§^”+ +©e •¥ú!:TÅàIS h­¯Yãß”K‰3}f¢øVéÉ,³„îiŒZY°ç·ÝT•eì”±G‡&€ÛÕ`Çúöö™ìcUÊŠ-Ebíc 3®Wñ™–REI`(Üf¡a5.¾f¶…šíûÝ|ˆ†^ãºÒ”$ðhÏÞCÉÁóìmƒ7Âñ%Þñ®Z ”2v eK’l›Ê!”¡"s÷dV˜“ž_5(=%Q{¼g2°ýæ e˜6CgX‹‰æs‚¢»á¤Øq“ñï–*YÔÍ)ÄwÞ€!z=xŒ.ÜÌ«-×äpnÑYGÿÂ5ÿslôµ‰Ý5õí:ÁN‡ €¨ÔÓvgÈB¿¿ÆI½J&£ÎNò̓n¿BO77 wGŒLCž§a³„$£G–ì㿃¯$g¨áøˆÎßRÂù´÷zºd û6'¿\6çš¡kìÜœÝT9À?×"IbÞ¥>9¶ç.´¨Âÿö%ÎzñFWÏì¼S-¹é÷Ò6x_†\ÊÌ"®|Üù©·«=ýŽÌ`¬Å™}‡”äÈéÖG7µá•ã27HX‘(…eIð3æÜrŸ\9ç0òh^^ÕO=“Y¯?¢ÚBV™–ÑdtǨˆƒÐu¸€K¯ÈXvK3—“bKÏŸÚ Üê¾EŒùgÛÜœÐî¨&ÍÁ<³Ã]á´z¸p–G€ˆlí™Ê"F)gï@U}á3´Xëü’ª&dÏ<1¹%Ä–ôƒ.9Õq{0 ‰¶]ÉÀKÑ‚Aú°ï± V9ÀÏ ª¹•„ë³RÕÁ‚›Êîáª÷+G[ $zú)ÅEÀ˜‘¹ÜFÕ8¨£—ʘ(¤Wh.¢ê¾¶Rû;k]tpß ÛOmGÀ$/œ€® R+ùaee¸iX_!ç%Ee¼íµÝ“¶A§¨7ÅôãLé9B¥.*¦äÇ ,Éιå]jò·†=›z¢f¯1DVN€—ýy4: æù4ð·¨×Ni7äÖÁušOa9ÌdÐ0°þJ!>aŸMFzæ-èßœáÒFÀƒÒüëZyÚÇëÍðY¿¦‡Ôa›7~]7Pc 2‘Å¥(ÌÕA™Ëçûøo îjkªC4G |})Œà«&|art¿~`ƼŒªñ¹ïo¾ÒÎ)õ£ýˆévP7­é8»ãVj¤îˆ«Y5å Ÿ½Oncss,d¬ä£hîPàº+ˆñ‘¦ ß9!ŒÛL3;Üá™dÊ1¤Ú[¶”‘œc7äWÊø ÂÃÍûWÌ-ñ̳-¶Úü¤‰¿?[¨Ö8d [° ²N7; -ãþô³ ÉALÈ[·ULBM¼§r§¤N݉”ß÷×8lÓ#0‰—\f%«Ò®=DÔݳ•eƒ³‹ œOã}§pj½´¦'ú/2´ò vÜ §&©{'¼Òÿ=ÿÇ̪köÛº!²w(¡—ÜnáSÇ›s&tWU…áášé‡Ç"ë÷^aÆfçuº*:/=YFAóÔ˜÷¼#j•wΉÿDUꟇ¾z“R'RÀž™¾5dÊãHiÞ`6y‘-¸í“St‹ŠJ¸†Ë‹4B6üœJÖí ÝQ?úÉuÚ40‘Åucõ5'5ï‘*g'GÙµñú÷D#èËg=Û{d¯ãgJ7 Âùûy‘à¤W6ô(¢ÃÃv}š¤"pC¶ìsoºSoë2æo?BŠS»“a¦ÝxxÛb”ÄòÊïaçI¢KôÂÜ\œãGÕë{Kù P°]efìrt!$Žloí—„X•8eõ»5#^Yþ³HÐÿ ò¿¥ º&{ù;gš&7X³Rl›&Šùë’hj“@/)©¨‡Ÿ\D PîEÐ@axgô/‘ý£ãIXeûUÇi+Mˆ8{àð­D-¿Oì1|wäàtí¸ñoìÄk¥QÙœ-ƒ§Fm'„aÒq@àÙ? ½Ð»† µïsnhTêmEHmÛ•4TÅ"4< <ÏêFÔ¿Çg %oÚèªÚ;H¸¿:Ãáɯ%t(6‰$8Ï~¢½\Ìá<Ç;è¢çÙ—øÏÕ3Ák5+C]±A¯e·°0–5Æ_¤,¯@ <[ÎNXœÅÞX7ÌŒm߉Ÿöû'¾¤QÇÌâÞðº¼Ö¿˜œ°HËrÑÓ¨Cñfc.æCɦ³Áµ½mikn¦Ý§ØÕøÐ©e@j ‘ðô|â kháR×gÍóµ–]ìµâÝÇÃðÚÄõ– u($ñUˆðmù|ò5ÒíÁ¡!ÒîÆ|^wªÙ¬#%˜F8½ž‰ó;Ù¢~VCYT³}AÈpÔ.m¾ÖÇu®[EOr<‘ºâ ìPŸ]îq\cçPcŒ:eÞÉáQö«û‡[žh"<îüÁT‡£×âŠÙlg¾ï´dÖ~5ƒÛò<Ü@Ü.ui†L¥C Y ™ÃNËXù¾ó¦o³õåô½2 b$çžšr>¨³žoÑjº Ïæº¢¿†NϤH”T$ìh€Ì­ÂsÙð{©èVž6ÔÞ+n°M%O“ð#îƒV’#åôM¿Æ•á#M)¾à|l[,[ð.ÿìÁ‡Ž/ºcƒ“¨i‡aÑ È“*â,¤ ãÁ¸ t¦ÁösÒ ìÝ1ñ,‰Æ‹u¹ÐŽ< j¿x×~ö;$ØÀ¥|7yx€džËúÚÅ3uiDy.–Æ}°ˆR9žMd·˜É2+4n©égß©(ôvº¤iÊã8’mn»m³ŠÂÀ˜BÜOÉìz@S@3ƒU7ˆ¼~âàq`ÙI´W²C€6Çëã –¢]“C yñ àd)Ñq ‚g¡®Ý'õ?¤ƒÝ1!–Çî’D„«”¨YIœÌ/Xz‘27x;‹Ü—EâV_E]ȳ8³t‹ðÂÖ$O~…%Ö5¦”6Ÿœ?ù–ªtÌŒqN‚¤Ÿ¬ô ¦œË,|-Ã+NûU1"ôQúMSké˜=(z€ÐX÷]‰ï)æFYΩÉÛõ£P0‚Ñíå‚Q ^Ä‘¢;ª ¿UÛg£B)û%h /ÏêÆk˜‚S‘OŠ+n—æÔóÎtGá&Ðmu\÷›-iÎ^ ¦’Zþ-Î  ñeI¨(ˆ˜éŸ+å¹4àyX½%­åoñDS ·áÐw% '¢OªüPþ-錸 ‘‘‡ÅWÑ€Ùð¡ü^îAsêVÞ¡U“­Á¾ eS]pÙô£œÌ-—°`ØÒ¤6wñHÉùˆæÍôšÙ–çe\ÔµvÝÔˆï^;qúqò)ÝŸù¡Êµ3ëW à,ZÃFþ’BqƒA>ÿ×xÅo¡l”^DéfÁ½·¦Žcö{Ÿô¡Ýv»{š•Ÿý 6¢t2ôü™ÝîÇr– £˜0µË†pÆRÏͧV÷=¹kÉ á«PÝE˜ÿkêF&´£O+hEýiÒËòÓ¹˜"Gï~ öh>lüüƒBñ‘pJD ±/gZ›¯Ê—ö“À^Î)» X²í#HßNhª}± !*ÁJÖ¢C"ÊD2š‡‹;nþ·±j+2´›-q½ÈvÜ€gÖ $•M›T2Ší΢/Rþ#,sáGŒ²w|êØ7 ùÅžóRù®Çµ“²Y4ùȹ‚lñ;«W‹¶0Žù²É´”áœ\spcÜ Ê‹¿‡KS%âíà¡)XáE“†´R¶Pê„©+uí#®ÐûÊ-ª4ö¡)Çß<›¤Gxš§ô{9úßÿDP¶¬’à*DK_ôUÈݫْmIGÜ«Ï{‚# ÝØ—–‘{”Õ°šocîÌŸg¯Ûn©—Ë4eÇs¨ðõlø§ÂÜ<„.^†GÊj–ª‡-F†fg BF.(4M-©üãÏ4z›-½ˆ8­GØî›¾[ôì²” `pò&º¯IàmÇÔoô)pG’áÓJx”Ýv.|fTä'âÇOq–Œµr(@pW¯Óü ¶Ÿà*öçP)4˃4D¯_¸»°.Œ Ó&Ê@wtô*ó&QOù>…¢odvÀ#sa†G’»ƒZþÁÔ?¯5 ˜,5W3`Êì9\Ói¿)Eb†˜Ž¨ÚÂ(,ÑŸtLÁàq¡Bnd‹ÚD°`•¿µºÛ¹âoP¤­Œ+DÚ€nÚ$tCêªQç˜ÁÜçA„ÏbÓ.špQÊ‚B€þñº“%H+ñ»j6 €…{BaOùM¼Úhö^ÇÚ“ÅÌ·i]~Ë„Dá…  Œè RÙC‘m]sª—X¸Êˆ&Âß,¥³,&-g»sq€¯Ò‡œ>Šøĵ§›”ì’·8Ño_"§!Å”)BD~Äß[[ÀU6¶á¿+ÇéÐÒ=J÷]M'_Cb¤ô=’‘,JÔ<<_^}Ú ÑBM²&‡¤6wÿ˜j3cï¤åF1ƒv{àAðç#—APÔe"y*KVŒ´0t5íXÍ)2ˆ'Ú¡•c;5»~ô¿ø%?™ìï’^!ëü¢´3¤Ð“`Þ(Õ}¡(¬6LË`Tå7§¹e µS=U÷J/Hçþy…©]ƒ¾¾«o$š‰`qã·[~°dh±f GU¬dž+ÂÓ?–_JÌ …µw7ϾªÂ°cZÀ'Küåà¡¯Ž‹K)$GB{‘þþWT©ScpœÒ¶ª–ù!y"›²{õC9Ø<È»=‚Ò8ÿÉ€¦Ù‘wæ€BÍW} ì¶aLÉhµð°{º†¢ F‰ù #¾À¥]~ƒ“£ä8Ò¡›˜_¬¼¦ä$äÊVÙ\ó>…° /¸ÿã,ÆÜÄiïóZ}§ç¼*"¢9¶Æ”EûE€Á÷tˆ Ñêçâ¨Q²HïJ¯è„¥œ7Ž–&ù ;ÈdZsÙZèbq9gÆþ{Š-R):˜† íyä¢dÿia-~¸6ÿ:ØS# Ð3—ÔÍIãbÍ,En`U$ñ™ë±ÖP@Ž 0 |þ®×e÷á׫3ïŸ2¦*zbÃl·ºdÚÎkR¶Å+…Ò»ÒâTq?2.±è$Ή‚9 û³Wptg°.¦P‰·éq_ûð/ õ=ò„Ç$nÏ¢~o-ò -”ØŒ6Û߈£-;ï-fŸ:3{¾ .KŸuÞ2aމŽ«½…ªì’U 8:hxU p ˜€dýYý… F}(ͨã’Eõ`ã –VåBìÖ˜ yÅñ{ÿ&úU˜ãçGªjºª–å‡9ý=Z“xŠ™EÆ ”åM`üDæ'1:ÖÉ9´ ºt¦]ˆèö§‡›9aß˾¦WË(‹IÛäsÄ •Û:Íé!:ô£WUúð€øD‡P¾¸ÿZÚmÝAáUÄâÊfØ¥ýþ=èâŸ;ÙÞzí‹Ç¨a½4x2æ5’\âHú¡ûÞB§Oø² K^«pÑ€Ô—q}TeãT‰³\üÛ¾’Xvˆ$¥2)`íÈËé’Xæ1K=GxcÌC¾—t•Xã0•‘?Òèy Ä“Û_‘Ar|)]¾¼ÆÛÀCûæcŠ4$£¿M[Zß×(ën u±Ò*2Ùºz°dí´ÃînȲlÓÚMån“³¶¿ÅEM»‡Xð†ºq}ú5}C¤_z¦«ÐPÞ~cµ ^‹|¿ #Ø)Ö(í]Õ¿èCz¥äYeŦ°P •ßðCÆÙ¿Ÿ%m²Þ1b°dk Xâ4Zs^ú"®£²Avš·Ì·¾P& $WLÆ}ñËÞ:– ð®Õ>#z´ÀùÏhJWdÓ+ðcŸèÈñDö÷vhHùXôV±GÆ\³ùù¾„RסP칕 ðCÏcÁD¦ž3~$ø·Â«»FÔMöò¾â.\t8+y_+Z¢CéDèIaA ¥+ aÅñpM[Çw„#úÞ-ÒšdDI ’qLç_m¯N·§™]™L:ç³-¥|O%˜cÝ>9Tö.:03y(-(­ fªb‰’âˆË 1ò*98¡»cÉ•1®æ4ó)BÒíâR]ˆ¨—„þ³%–Þ®7 #°_“\Ñ ¶Ê'«?³MRÆÊUÉóÖrÌœt¦{°ÌïŸ;!æ$ŸŽ<Òhè1^BVzî—û¶úœj?%!vqÌ¢b:?†Ë©`&£rUsó>%ÏÁn;™d7»¯’…›¦Ôã–9I~ȲMA…-à$Ä8AWNÉ\±ÚQ¡ÑÀFÌìvíŠÞK%NËÞ–Œ!zP "uk÷çÎé3kb’h©ó&…Ã.ý0Ý`W¬d z€ly¡*ú¤d¿Àqùª4“çû1ô®”Ï×2‚8Læ$niaO­D|Tùj9EpQG¤æk!õ-fJ1?·ãÔ™/üÒ Ý´z[nááÉ@ðØw IZÏnºÔ‡ ˜¯`-¶-Y‚Ànš‡æ“vë÷7T_$Ÿ5ï•) ™ïÙ—Þ&£z»u‹>)2¾'Ê&ÛÓŸÊj0±vm­»®”Aûûlœu§ˆ­J5° 2¤Ÿo9¥il mZj#‹ùíj|gW&r“þ¦RuUì9Š•çÁ­º ãx‰•9SUW)С–…h@JÏ%r»Äô÷Ó³Þe «éÅý¨úì a®+ÞNŸß×ÌjcÐrÄ«ã®XÛ”À†>HЮ×È ó[¿@4±wð6öU÷š»z+”Ï Ó«gFpWOzŒ·Ž#o 'Í›mc‡Œ´í>Ú5æ:B~—‚ùM„àÐ15ÉeíªLÈ2Íÿ~ÎÒU¹Ñ'(Ô%ÈL8B’–¼"ï·å¾ÀœèOániH^Cíá`ÝX_éÜdaé~žªj÷vÖ½ä½ Såßú\3ñÁý)PÖf`\-à·i ópBêÃDP¥„—Ý̬=,ÒͺÅ|äZ¬´93‘þêb¥Ž—÷©¦ä»Æ†‹u[ÖÂvŽƒ(R' lÉXˆ—¨™ûªÐ`¤Þ*n¦H¹K|MÿbÜIg¯Ð‡=_¸'«ÙíÑ—¡^Å m™9‰ ®æ´.ç¦ÔVð@0zøØXR™º|Ûûà†©rÍIA‘b`Ír×Eç%‘þ°+ܸáxš¡ÃðÃ^Mȳ±šŒÌFk’5á‚64õëùN,xŒb÷ÞÄ·õòª¼ó9å̲ŠAhíõÞs4"Ö³Øp/hÀ–%DþõQGçéÏÜÛ+¯­ç"Ý_”A,÷Û ìb‡áÅì©#v‘6‡måß¾/†’=Ðag„¶6ød"«AÎ×.ƒ“0¡'½Q˜Ìwj“YËáêšÆX‚My5‹£éa(Q³LüˆzÑO0O¤³¢ÿ"èêÅØM¥¶XÔe"C4j¬?^Èk6¦¾^‚+·ôÃpǶèüÿý&íV V†vž7€DÑ’p ÞƒÃ߃ j2¤d:±¼¿[iûþfXœT_±ä.*T¼mð®»r(ðë]x¼!£ž‡àÖNv²øÛïømss ©Ë¯ÿêËÍ/j6$ñ#EÎ'ç,áöñÎn4íx5ÅâžÚ³šý¡”]ã!ˆ,Î%õæ=ÝHaLl‘K×}ý¹‡\]Ä±Í º×sQ)7 Ëˆ`õ¿ÃQ¿ÙeÞ FCs|üèþ¹¡þÝë°ÏL+5n<ýwF^_dåuþH¹' \Ãüê¬áˆœï1kÓÚd ´260U{²ÁGÙ,:,ƒã¶N1NÑíE5¢5ˆLœZu=›Ü‘¦è^ò0sŒ>SƒœY9  Ïú^žˆ§ÿÂ&Iwi|>RGL5î>R)j66,Žáwú2Êþè>ûïžÊ&¸‚~Üñ¯Á´ÀЄç{@·Ò¨è±£?êüX¾ÈGƒ *ÝMö{XóÐhÉk¸0 ö˜7r+ôæT|FE˜óƺ¢@HÃvA}âP$F%À 0 %8;2 •‡]-v$ŠðÜÊ3Ûr+ø˜Lý}ÀâB0šùÕŽ–˜óÁÚøÇùRtöaó<þ¶YŸ2`ˆ_VKxøÂ)^£Ê’F'nI£f $y>4Ü”„'iän…ØjSú‘Ô¾Må#6¦ð*øž˜ÿØïÚµ?ý)Øsvw{!ÒR°ñlÊ‹²¸ú7—Š8úûÐÌý’!Ò‡ûGË I7üI…lÓÔîØµ‘LƒX= ¥‚›¢0“¾ñÖ˘讙õ«oç¤ýÄÙoŒ1ë’˜û#ReªXÊ â‰ÿÊ Öa…’ðaì›8l\«Xàø•#ÛÁzYJ<‡ª3Í_QÖY±s”l†Rõ½š‘¤Ú>Åq±ðDYÓ×`‹MkÊSødÜïŸ*\…–zÿ \Šf|[šlVÊç¼eî ;÷^V2_éÍ@8Zˆ8°á¬Ðÿ×èêa+¡eÌß^†)Mgê+_þnI³&ݯíÃ0{Àç'þjÿYñâXt"ŠöÈ'—9§ˆYé„ûöˆ9ƒ/ðÔD¯v]í)…×lÅ,¢kLê¼Ð‰ÒˆÜY:Y|¨ØhC2?í¤xã)?ñ«d5ÓìËUvÚnUå+KªH !=¹y´Ö[çnö‹B–¸Ñ×PeUŒ¤aÊþé+'Xé&ßBè•yaÏ"ƒ;ÇFGdÎ.M´®“v Ü/ˆ!gv£Y–ö›(íõgÅ ã‹ä{ñ—i9ʽÜêÔ/r föö|?è0þö^ùu>x±dåfà•bó&Ãàj ­Ÿ¿(f…XnÎÃ6ÞýD|=Q²r÷üò¤”~£+Á©3A–®2Øš’JÛ6)~v«ÐbØš¿ŒòÛk÷ÆÖÁM÷u©óbËBZê\±l6ú䉀Töò¡u-o7Û Jve¶ù„.éñ4eâà`µ”ìÏ@|ý˜EpmB^¬’æœo+äXYõŸþSjG§Yª_K '.ÄÚxÛ Í 2+Ä‹ ð¸ø|QàÒúuG×Ó7òoÙŒKŽ?à\`®ˆú€Q‘_þ6=Öé‰(O¶> ªù’2 çzb¸»#˜F¸4öÑ ahêÀo /ŸˆiL嚃ò¼ÌSRÝ:ØìÍ¢çHy†ÞìŠ4U ÆJ|u3Aoˆèz¤±ôWB¿¢ÇVB)qŠ÷ÂÀªÍ£þ›Ö¤)¿þ&†ÆOí·–Ð× zÉ2E ÀŽ”Àáä‚ô:4çÄcAçp]ýbºåºñ0e÷6̨Þi¼‡åv|ðÁRFK?/פµàs«§5³Ý7øK›b›E¸lE›e´¬xµÛùîO¹Q)rd)FM?•hkm™ˆE}gí…ÙýÛIýd…úÁ†´šÀÇ~Ü©X«Î.æ2U´0ÿ5ê †úÅ<tÀ˜À¤&&bÏxöÑõG Ü¥Kš+ÏìÏ™#†£ZÓ¹»¯(í™Ì§„Ö5¹±ŠX+fŒèFŸSW܉Î@G<5ü%ëfK?¨ÉŸì[ÇØ/\}'IÛÝaõmîÿE1'u˜ËšD¯ÌàÔ\…³%,!<`iW®)‘8!?Õt9ñü¤_ï?K#Û0Øß÷¦ºp0d¦œêð%T5eÐÃ<ꢂðY;t”?2×#t-ÀÀþyˆQ>ìâšRcàN|Ò V„±¢±› 08b¶)Î =ßߘQÊÇ¥ÈqôH®ZU?r†êøz;_ˆO‰±p Ï×g±ôÜ:=Dè³I‹ÅÎõ·*Ž%­Þ¶ñ1ÄE™¶Ó'óÉ»¬-Uq€/kF>Ýh±ËÑö©,gE)kéÂÇǵ_Œq;í“eÔ½8ƒËóÆVê¬ô·RÉæëˆm”0⺕7l•V`JH¤]ôꀩAR`³ÚncƒtÎî W<НtÖŒJŠZæ ·7!KÂUì˜*’ëA¼;wmv™ëµo@¸‡C”fg§ñƒ‚Ð$½¼ÙPQî±~­*†Oõ_Dn™Ry’·Æ‘vjíµ?_ šRX¤/ë]N]½åÂOû3âù=cSožsñy¦TdÊ+Åh-Ö̵ŸÝ\vùŸ”…%=.e]‚+k”ºäÎU2±=#Ü8³ ÕþÍf'Û©d—]­›M¨šFD¼ý^rª\e3 ¤úø…!—Á bÿ”ÌðfûI–XÊŽ^ž”…z ›š(áªOO³š~¤*䊰´Ìêڢ⩜5Hv4–sªUoŸÇ>4Îâ¸T÷Î »äwO! ý0ºò/ò +5"3ަq $@.†v:2çI£ ïÇ€_õ,ü5r˜Uö¨'P7ñK°ª­Ò>J¿³g@·ÝèAŸ¯1Û„™e¤"›®e°]™!¨”Ÿ+¿ËÛ[<¯ZG©”ö"ò+ÄïFKƒ…®™ô» Wx=·W„Ú’æ9MZþÛ]6 IÒáêåÁT,DÒ`ž‘Ëp¹»ŸÎ!ªè”“—i«Œ†ájm ÜBé¡PB•ÈeÿNÞ¥î·æo\?}aH_6Ó­ŸhÍñ=§Âú̳ cJ‹~‰¶<_z7OˆŠ8¢®)Ø8­U¿F¨V,xìþkZMPè5µº %"6YH6 ›‘Î>ox ÑÑ@‚>EYÙtï$ÆX‹,c†T€ÿeFh5wET?àUÇè²vAöQ|²®Q… Þ2Z>¯nk#ûÐ9Yš¹°2/›¢yÔ²4H·k+ÉÑ £Fzºd€€Ù0ö™Ø-¯á,»¨;Ðiv¶~†j}áÓ·ý‚`úÕug½‹7À(ñ^r5"Ý]ô‡QZ[HT&C‡‹7ÀU䯴€8j· ºü}NˆC”Kiéí›4ݽ» è¬ÿz€"m{/Xíl"X†ŸŒÁÓ8$bN«0û ¸'1ùߦ“;7ÆW–@˜WÜ#ÿÙ#ü`’q"3R’à0ýîö©— :Ë 7âÎ#«ƒÉä>¢tKA} 7ìð’ºØ k¤=r¡,HS]íðœ²eÇ8çû‘/ÅvŽÏÏmî%¦¯snrÍ1­Ü'?yëÖêé§ ‹”ŠRöËÀEè,Ônú>Ãy› ½_ÚËgob5%û¡ið`þ§¼~ Ø~ÜC³]ÐYÎÌ¡{E—Á é¼€3{ÝEs¸'Bø¼µÓÙÐñ$ÒºV’Ë3xâÍáë[êÝèé]Fe@„‡¦5lyŽZ©×›Tñ{w€_›·T=ó8‹Äà„­’v#I½Ê½¯ö]÷&À§_uèušapÇM0Í¿ÊEJÄ&åM¬äé?Õ€|o íc-žëêXá´²ŒÜ p•[‹:RbïPAZ×Aö~©}CÕùÜw¤MÇôKt“p¤QÆ,tÚPg@¹z%;ã& ì²F1@VwÚ¼Š @?¬Äüël•j5[Õ¾™ jr1›ê†Òªù–à_ús^áÇ”5<jtET¤©uE#™È6UØxÕª8hÀá)ežKM<úñX­‹púVUV”Ém´©ibgiÖb±E¥{Ú}XÒ4R›–ؑߋ€åzñ(º´w±œE؈|¨àÂüIè–¾4=iù²'°Þ=r*¹¯lRˆVeP®tzRB5Z@*Û‘'ÞÚ3äÞ¿±t8Kú›5Çý=±»!¦¿è£‰)7DvÌ¿ö¸²Š^T­­ôeŸ‰@§Ä$uY͘ 4ˆH@ž«EÙ4˜¸Èv_€3ˆǺâT¥g8)¿¾†©Ñ™þ<å%ÌÙ‰£×0•pµÊÇ‹ey<¿Üjé΂Úŧ[£‘ÓÇ×€ÚùØ\{µ†¾ ÷a쨷ÅÕ—ÛE"XE^•‡é”î;}IQ"IÒÝHcb¼2Òÿ‡ ê?ÜOÿ¿èÿîÀÈÚÄÀÑÙÎÆÀÑ êÿ®âÊendstream endobj 13 0 obj<>endobj 14 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 250 333 408 500 500 833 778 180 333 333 500 564 250 333 250 278 500 500 500 500 500 500 500 500 500 500 278 278 564 564 564 444 921 722 667 667 722 611 556 722 722 333 389 722 611 889 722 722 556 722 667 556 611 722 722 944 722 722 611 333 278 333 469 500 333 444 500 444 500 444 333 500 500 278 278 500 278 778 500 500 500 500 333 389 278 500 500 722 500 500 444 480 200 480 541 600 444 444 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 333 500 500 500 500 200 500 333 760 276 500 564 564 760 333 400 564 300 300 333 500 453 250 333 300 310 500 750 750 750 444 722 722 722 722 722 722 889 667 611 611 611 611 333 333 333 333 722 722 722 722 722 722 722 564 722 722 722 722 722 722 556 500 444 444 444 444 444 444 667 444 444 444 444 444 278 278 278 278 500 500 500 500 500 500 500 564 500 500 500 500 500 500 500 500]endobj 15 0 obj<>stream x¬»ct¥]·&ÛvvlÛv*¶µcÛV…§‚Šm»R±ŠmÛè<ïQ§ûWçßZ×ô÷Øcm %U3 ¤ƒ½+ #3/@ÍÊè ê`køºsÀQPˆ9]­ìÅ]¼M @h `e°ðððÀQĽœ­,,]Ôê*š4ttôÿEùG`âõœ/M+ {å×Áhëàh´wý‚øVT®–@€¹•- ¦¨¤-£  –RPHíÎÆ¶%7[+S€¼•)ÐÞH0wp|þ¹Lìͬþ Í…ñË €1ÀÅhjõ¥ô4:þâ8í¬\\¾Î+€…³±½ëW\Vö¦¶nfÿ8ðE7ÿÊàˆ£³Ã—„Ýï LÉÁÅÕÅÔÙÊÑðeUI\òßütµ4výǶ‹Õà`þ%iæ`êöO.¾°Œÿùâº[Ù»\ž_D€ `fåâhkìõeû ÌÑÙê_n¸¹XÙ[ü—ôg …±³™-ÐÅå æ ûŸìüWœÿ¨þGôÆŽŽ¶^ÿÒvø—Ôú`åê´5g„caý²iêúeÛÂʎ韑±7w°0ÿÝÌÍñ?xî@ç%ˆúŸž¡ùrÂØÌÁÞÖ `4‡cRppýJ8€úÿ­ÊŒ€ÿ±"ÿ”ø¤Àÿ#åýÿWÜÿ^£ÿYêÿíøÿ4ÏÿZÒÍÖVÁØî«þµ[*vÆö€6Ìÿ!ilgeëõÈþw1Mà¿m›ÿ„Œ«ñW‹Ø[|ífƯFý§­\$­‘“uC¼ŠÒU úÚŽ¯L#l¦Š¦E¡„nV&\=ŒÖ^ BóŸXà§ífÝMÇ\ òaÈ$,œÈû9‰‰ùÒe/ß ó„08cy”'TÖ.ƒs~c®™L†a0fÖÂìÑÇþ=(ù¡åIàŽÐvN_å}tή8ºÀ{…232)‹Å묑†ô-ÇÞe×Å­)²¬¡«&´•5íòÓ8}~¯±•"ÏY1`Øv­Njð¤Wxâ¡aÍø}vÏ¡À¯ßÁl֯܈Öy2:ý{ƒÚ[ÑRÞ+©åeíö}ÌS?]HlÄÿÀ_!M€§?þ‰ÉÌtîú ÷ЂVf¤ E3Ä¢©/±ÿ§Ðr×Åš¥©þ›á,,qÃÆýű5ö»:A›–…ëXšŽfèäÄo¶O\ <ÃÞ}åq'1bN1M–„„"–tøçKv&K ÷F°'>ÈHÔUg„@úÝPüÊ™`9˜ÇbU |¹À’§YŒÕ|Ùq >'²¥:o ÛÅÎîÙu”*®¥2*ìNäîÉØí·Ó8žþ—*¨­t.ÕÍZƆ²½'B^³¿Æ{™X¥ª Û³.„à%©†4s~FÚÚº!ƒDgEaàÈ“5ug‚Öæ\ï³qi^ÈW XÌÜ4û5$Òj·ä Ã?šÆ•üöͦ^ ‰ Ù‹ºAúLbª‹¢yiŽÝ*qEcØ^ÿ¸Aºßšìš³a‘Ëq¨F2…•Ç‘AŽ}¶UÔÌ|oÞ&P±ï]¼³»5õ LáÒÂj Nb¨aØÏÅ êbWξËBW(6ØÎ`‚¹F13öÿmwµKŽÈ$Xÿ>¯˜Ù&)Þ·]¢±t[@3/ÅGŸ"etðË:¯,†·µÙ;»·8œýZf4ÑtnLDz¨š Æ+}ÞT‡9÷työh›Ø7,£mœ{ Ïœ: v¨Aû«ø#;vôzè‡|€bŸFœŒ”¼GW˜“;UT„ÅM.Ÿ*î´E>ÜùŒ•NÃdÄŸ\ÞÞx¸Æ¥1é… YiÈX¬z6ršs¼Œ|jQMiœ¥]f|,úÙJÍAÆÉ‡(Z^åA‘EH"©ÓÃÄøÇÌ*8ca½c´©÷Dn#¿×'4ë]ïç‚¥.»ˆ¦°uvy‡{êÿЬe÷=:Hxïs!¯=qètä“ÌhŠèŒµšßbDíÐ~û=¸}T‚™ØHbѰ»›OŠSâ S`-¥É!o·Ýö‘ðZz6m0K ï¯M`ì6š\¯Ù\ªm{ z/üŽ~Åœ‘w¦TÕ;dõýr©‚ÿ?Å© ÍAÖgi”0ÑŸ&gPe(C^ &M3`ÊÓ<>¦¡!¼_Íè ß–ZÉrúŸ‡„Ï?ØÆÔ Þ¾0âÍk(êúT!æ6MØQýÆÆXVYÛ„ â¶z8`>N]ŸÓ<‰f®lûa“,îf™xò^†œ)Ì=?%À%«N/‰f±•Qxã`˜TPï]„ K~• +€IŒ°’¡zQLåôó·;ÊJÈ@¯y$;a…9„²CÝ€RêÑ3Å~È#ƒ`‚b‚;ÔR`à1ç…Cm‡åà¶(:FmGÓó`ž˜ø¢Í}fö”E‘æËí÷  ¬hmNÃHÍž¦ÏBZVQõ…‡,™vR÷¹u 8ÈT6ÝÍ÷£ÀÓ)ÌQË¿ê}Q³©T.••ñ01Hj ƒT÷‡îS :?Š«6`îa•*óx«Ì/ílÖ”QF+d×=SéßøsµKbØG¬Q†µ¨x…’Ë(ð^ƒ”>*õ%k1hQý RC¸Ñnس.¨Ö^·y ÖÖ4`„+¶-™Q"ÚVEF<îpm §¢Ÿ‹'“ì³ÉCfmaÇ௞#³¼Áýåˆ\4Ý©b´i©OÆå²þUéé”ÑÇo_šR†µ8¤¿l“Åxb“jN‹öfñ&mmÎ#SzÆd‰ý¹Í7$m$à9VwEP\^E¯É–Šca¥ùÍ LßôèD{Ã?mï¿ÂG!ѽJTHf ‹ÑC5C©k ÖÍé^Té.kz¦{ÏØZÖ©—Ï;îºBèuŒm0¿Oº¬güb6ùÑ·Y1Tök÷ý8|ÆŽÛåh¹˜^{T•GQ³¹O M.’çtĶÆÄìJ7y XkéxX;TË®]ÂçüæHƒË%½ÃëuO3!1½ëšå@/!Ê\¹úgiô¸RO»ät/ø©"G½@íúÞ[¥öY±Kõ,]¤WÆûÙæRÜÊO¢e×ßÙð;û2©@zO¹!¾ ?¶ê}µ&5½M þ‹!G%… Ù Ýñí8bqÕB"ˆé¨l8½ø¡“Œ ¼„‹Ð“b”T­:=S2{¼!Ó²®ï·?ÍÕZëâº7ô‰ lU3lR‚×>-µì*î^ñx‘a?!ìeù=µûŠzÕ;ÁÎ{üvEN@}%).j+…Äùœ-)¾€G²Gaºß‰¨šŒÆ%4 icøÜ )Ý_ú*ZoÖJº29‘t½'¥Föc‘ÓK¦æùEyâ*Wôõ±Õƒþ¨éî$kj!L!ÊMâ+$§´™)j™ä ¸]«uƒ:XqVÕ ÄÙãEx,ç¼i9ood¡bîÓJ ñ >à«™§´²½]ê4ç뺩ób°»ëPð÷™ àˆØ$ׄŸ¿bZˆe<âÐ5Aõñéï~;G/ —©ñÅ∳ˆ®R¢¼Oçϳà WgÚÈ’³‹¾Ï¯.P»ë s¡§¾¸1/xÄâèÙ1)/ŒU±ŽÞ̨mh”?S¹Ÿt “ )OÈÀ£šbCiØhMÓM¯o6‡¸ÒV¬˜ä÷}¬4¨Uæÿ–‰`؇[ÒvÙ&$´·ã” ޵7K ²xjR¨4áM‚1º%¹¥ïÁ[ÿ.Âeh3¬6m™¾A Ù0[no¸{ƨ!€.šÃ<°+ŽÜ«sx¶88£z³j§½ç{¨«òH3GaC¡¸¢AžÔ'fe]÷‘ óqO’ìàMx5RòDå6éåÎa¬gyU4ø8Wô˹—Þb´äîoØàÞpòƒB±hÞlŽM¡3~·ƒ€D™-å‹ÛLXÖÞÌø³ÌÌÔØÔ©1Žy_½‹©ž¬’޹°üp…®.NU væx±œu·tåeüyAÛ&p2º&àúãueÖrkDþ©’5×õ!C¤á­Y’6u?‘ lfþ"é þQùêf¬g¤ÊòJ4W6'k)þËr<öñ“f…MV¯ŒÊ‹Â×W‘/eZäø(µ4æ\möl\ɸªn{/ ¼#Ñ¡Ž²8z¸ž{$, —PõØÖ±yãË%¦ nåŒ 9EzÛ `Òñ rºéGBiŠM.ÙJIØ•TEKËÏ7æß”‡¿«¹©¥©ÇfÁèV¦l1›«å§.e¢·–Š¼ì´•ê2™P6FRéV¤Æœ=¯ÅuÌbMIOî‘q×–q:MüýˆµŠÞ¿_ÃG ”_æÕ»—·Ð¶3΋¹3 ïT¡úB4JèÞm!š çê4MýÕ®F=K¬˜»·Xʟн|[—SÈåÕ„ßlà®Ôôª~à.ÿ1fÄ^€ÄÇ~@ vcÎWeçK» R30=4ÜŒáE˯0Ê5JþH“¾>Q)P"©ú¬¢;ÕqÆà*|¬­ãèü Ó'¬ IáGä‘c~+¯Î"íˆàÖ]H±¨Ò®m3ÎD€©ž¥}©çûOžÝ­ N&N¤¸,KK=”å7Baš@~*·¢6"NÒ t‘åEá:ʶ<ž‰œÕ\”êÊ-¢Ú–ó®Lz˜Z³®!Þ™RT›ÉD~‚¾¥Vä¤ÄB@ ¤Î €]ñþîš·¥=ù M?³–þãÐ××2¡ñ4'9¦Õ¨îümÏÅq—§'Úh-ÈfN©84ž&„¯vkñ.ûÀò.xÁE™Ÿ˜+q1 óÝ™ü¢‚Å‚-4SAÎ¥s—ÞiøbZÅ™Ð2¼HŸõö^Ú¦½„?ƒŽÛÖAåÛ¤7Ñq08mDVnŸVJO¼ì‡ß'Ù8C7ƒÙng¬&l13CîBN˜­%•g ˆþœS…¿Š ¬™)·Ž™~9éì`üI‡í1oY•ûèsp•ŠÎ¯Ùã‡Ô\]¾á5+Y™‰G´„Ó%ûƒE«¥¶áOÉÀ°‘b›¦´yÁº­9ïÌeŽÅ0iš,é}9h¥‡å}Û4„ÄßïµÉ€…ÊÌ`z90¢sTšI2y‡©I.Õýxª²k ΩdûåP@K¼\n€Ãæg›=dnÒ´8j“TDò»X>± P\Þô$!Ë>#½õû­nCcöØ}|úïÛßâï·G¬ ÷=b¡ñЬJ6"÷zzÃg£Aç„SÆ/†'küË}i'èýT“eZßß7Èc9b‚œ»x¢wûv§,: 4äžãçÁ —p¯¦§ØšÔ5^gxI`øš®=Çb¬MÄ:}»Ø›Ü…Gyl‚i×mg­Œb©h÷ÈÛ9¼<¤r=ó‘šaó3W~š³N›dìaCbîxÔ×W ¯\PHŽú–-¾ðì}¢3ËaÏÅu8ð7ÊÎŒ¶§ÅGw Ñýn¨´íG,[ÞâB3Ϋ ÇÔ0NsJ¿Ïšq½Ë>à÷<30ðHØÄºî#0Ñ·ûœMÿ¸ê¯Ìo1”CcÞüµÉ"¥ÆX‘«mÜúõÂvHéL¬ôFD*ÉÁÒSnöT“­ “2¬Šg vϸSWÇÚUg˜0[øQ{Am × þ¬3J0>ÑËKqá°£@UvóÉœÞ'‰t@M ó¡3Xý^‰¯‡=úc" R/Œ§›^üœþÒÚáhÝž&ÌŠY œ“)§›Ùìxß=ÞXy ¼ ÿWâTNà{éUë÷Ðf ŽÑ‹1m³²€Uˆ4TpÝå¡*ýTû[MÑ6ËŸ¯VEГ頡âîÞÅßQHÎ ƒE³'oW¯Y*-636«½p.Ž!ð¥xÿ^ƒÑ§h9ÍÎK Ò͘ûxòß„¤k/¼wÆ|¦òñöÉnŸŠØô¸HäùH>ýP’FI-Tê IY†xu ÷wSì^FrPßVÃ¥n0««’*öçwã¡UšöÀË@Ea´dì&« õìÚV_ê3žÔ >³Gzlw ˜×ÎÇ·TG£C2¢¼ß#È5Zï!Ô\ÀÞY+‘Öe„#V‰®… Ó‹átÒ]Ê]ܰ5?UƒŒ±\Aó‘>ü®–,_ÿ µç7¦B!±±ï‘%ÉYj 5{ ÌÊçDÇŒf€{VZ]CdPŠ)õPÔ¥|—Äœ._è 3è¼úžOõ©æ~[ KÉIm'Íðä«-¸'r´£­ ñEÜ}Úñ ‚y"ØØÁﺬÊiWÑŒ‹+à|[~‹÷ëN¬„Õ†@;åÂ’øÞn T„pT¯ê…:Jöù]k²™Ëóv µ|ºþgVc‚±c¹D2ýþ—Tt­Ùbõaþ¦ÐSòe÷^Šøí¨ó@£¨eĨnß3LFºs53d×ZŸ‰:ÊÓÍjû'{Ö÷÷×?3(‘Ó€øáø¿¡A:°óV†dù/Ò 3:Q‘mÞ*¦ÐhèTÇ&½8ªÿÑ;…Ã9-{åãh„ûŽç·lÄ9Y™™öMÐ ·C­¾¡`>ÿ6‘d’¥Ëí~¬°‘±ÏÂd´gST­‘¡}D‹Ø\5ª‚'qÿòÀ³DùdeÚp)ÊÛ&¶v¬hQ ]r8”%Vl´ ’ûû<"Ý£ ”<ÅÖ½]ÔI)óïo¢Æ 2hH¦»m¾Ñµ1k/~ßË!>¤x…þþô»6:»Ü·Fc\Ä>=¦¿â,—˜ïèp?0mÈÔ"À¶©”ÒŒŒÇB™]غãåo‘š³çÝ¥U @)ÕÖ˜ÑF7!¾lÍ_)™#+ÛêNÆpê6 £Ñ©V“ñ¤„WUžŠ®HCüœ‘¡³ˆûÖ2<4l;¹›,uû(>B¤äPÆ\¿­$öÈ& ·üêÙ{ÔÙyüñsó“\ä¹0¬s ÚHß]Ê)b3®Ô¾eĈ¶ùDÊh5Qþc!˦ôñ=û§|Üñ€R–‚(ˆ5¸3½· ».× ÜÍn›)¬H‹ŠÌx” ƒÃAÆ£Ä:ßÕGšµG‚NX…7²šuT…[^ÏþG±P9þ —<§DÙdØÖÊc‘ø;b¼¹3îš«ê»ÓRü· ë$&‡öêÌreÄsýÜÊwŒBê!mÔì t9«²Nyñ (èNù3€AèÏ¥Âa)œ³ÒäMç´»l®Â^«ðx ”_y£xYKÊñá œ‘isU¥MèM‰êEëG#¶8ÊKV9©¨+û@ݩ͛ec¸Û÷\HÐ èêÓɨ”2SìŽs­ z"ÅhºHË’‰ùÔ\Ð#¯þëU„ -ƒtCL€Pb_»\P­¤™Á*q¹×UºÚÚÈã -€ÿ$‰Ô7^KXVxÏÅ?xú´\SÞÓr·£™ ›©jÞ­!àåž™¦Aº•4Ú›v‰ùÕg¶» ¤Þ)\Õw¿êC¬Óô”SÓL”$(ZWì2+UÒó\[ÌZC¢Ž>¹-½\rüC³yV!ëd3‡çÙÙ%ÅžgJjÛj¦"ÿ2’ 1`^ü],ÙÀYuݹ"›¢TóH±º&Û·Û›‰Yã Û#Yj[ =´Ü—¯ýz™™8ü»Í»vŸ÷[…âu›ž——(‹WÔfI«qz»2ûR‘Ó0M"8jPúsa®¨†ä­ý[…Y6úìÛ'ÿ¾‹‘§Fוּuž?ck„èi‹a{ìI9jXky»Vz>ã°°L&ÚC¼nA‰eyñ‰4#Îg°èA݉Ž?pñtSŽØc‰–E'ùb5T¡$˜D‚Æ‚PDÎãE§ÀpFw¬U&/8`õØð¾•¬þkzvrÏø™½«¶ ´­'­ŸIÝ•—‰ƒBËØ("QüÒ‹Ð@ÍTåÔùÔ&Ö¨ôCâhnÿD*õeïXG9]µ@¢•K×…“œÜ﬈ø™ùÉ–òϱø%Ì'gøkèà̼gp/"\…i»ÏMþýNhÐ ­ç“¡·¬¯Jú ëI; ºÓ&ìÉ „Ѧ јöF‹³úUe¤ÁÌ; §fEëÞæ Ø(—ºÓã/¯Ð+F$Ð[™:¿Y$|–“i%w'ø™='µ7cZ-’߈-o5‰X7ÿqîk¨L›Í1 Ž^çÜâk`µÇÙ·o™¿FÜŽÃ~s§ÿÆmš:âpžU‹¶è ƒRüºÃ¤Ô[؇²Ì[>ÚÆßJS· }Jÿ2²iŸY” ¯þÄNÃ\¢œdùaZƒïV^z«ª¢ì;±ÊÙ©®3FTì( ØU»À(bt®âƒßq‰néb0Ž;MÑ-í;Òûν»$R¶ôüî|qq·N¬Ÿúª[³\díàGr h”;”ý€˜eMæÇ€zVŠéiÔy(¦ ©h œ‚dßû¨áEÈ|öNp~”Î?öê®6ãÿ´X>TÉ’ªÈ”ôýTW­{ÑéW'G„Š"1³”›ÍÀ°Â¬]ÅG–ÕqWØþS´psÎCô=—‰×á]3-bfºÔ¶ ÃËê2·±¢–kH©Bêm¬r‚:ñjªÎ4R€"8ñÉ8ãöÄs½Ó8–NuÛ]MJ\X®ìŽ&êâ<+T\JYSËf Šôh`9£yŒ÷¬³€5 W¬®FyBÚ¥¹ÆåZêãz±+•µû³+í5«Ø‡ßnwÏäÙ¬‘…mv/ÿSŠ #’¾N~«I»$xbºm‡Ø€WH\2ïqÔNê&0ÙÖõžÇé‘*;Sãu"“\aiLlž:„‡°@Œ*íÚÈ+@@—ª&‰z ë[ÜÛšž]ZŽ$xÚÈdưБ”etÉÛWÓ÷GÐÔ©@\k4yNņIí][p„눺I63 Ë>ûZÿœ´ìˆÏ7 O «‰ƒdhLÏ„*9×µ;þ¤ÿªØ=47»n9‰wÀ+9;)«aª¶Í´O6-ú;~R#©[$»ñÔw1f2/í />L FT#o¹Gw&uã˜äjÛŽ““gãÝÛ_Ú\ûðö¹^=JŸàÃwñ'©oAQ¯¹öBRU™I\eÓK"¡-ñ½”„Nsƒuÿç¶Ç‹{“ù×äòÓ1Ü ¶î!±Gž<‹£ç\%¾“Y‹Æ\|<¢Wµ±ðt»$ÐSˆ)õk»‘%P±íhoîN^¢Èk¤é¤€Ñæ”,Àç—SÀV+’æ·>ålÞäýO49gl¶xˆ7ÑÏdѹÖÉ1å>ÊÇùðÝ7Ú¡U„e©!'…$çf¨Ê!/ñ!/ôÙÛ¦ñ˜Ñiîéf¬‚ƒè W°q×`áô4æÞÐÛå,ScüSá§ÛPöm°÷m£¥ãétøã‘£1ó vžGAn<¤XŸ·Î¤IN†²L_׸ÒAöj[ƒpÄêä!ì ¦¦Ý$Nîǽ†ÏB’ $=DͶ[“'…fÙXŸ—!-_ð„HZ<Ì™àÔüe °Q¤DÿÇ%Õùã~©¡ Ö¥²W†YøqœžüÔk¦(_`ľäà[Ñ@Âå½Z|½ÿݪ‘…õ,ªÕ (”i¬ùëq~µàŒÕÒMû)ÌŸYmð£Ô;TÀ„•¸1E®×,6N••Ýz•ž§‚+2]í®$F5_Wú)T©Zwe«¼/ú;Y0-ä'b˜«³ÎŸ íîb1¿ür;…ì‹Ï¿uï¤lø?~ù@-*r5)hòév\¸\ÀUacD'þ)èlÒΚ£cuàhû9½£_hËv]RL\ïñ;G†ñº˜Ì]ò“‹ü~üÛú¦Jl~nïY3e¶L¿|ÒûñôÍXÒO¨øq¦“š£&knNI®V-Y±²ðWÍY‡ÜÈ«×hšù½SýhÇÙ€áþÎ$„ßMì‚‘à€L"Ø=Åhê·ÑåöI¬,^Î#7{I'éƒ ¶VÞP×8Ê„Ïðâ­®ÕlY°ô˜y4ý´GŒùî 4©³2ß#·¬Ñz²z¨8i¢Š±ÑáŸìèÆ?}8cxS˜ . gÏBßÄê·ëËÆÎUþòàv! Ö\Dš¼?ß‘^ ×GL–}•fîKEàÔÞhzBáVw©¥&uð‹ë³WÛ~çÀâuq~ü\F}ì*šª© 7и—a"…€eèEÔÍâ°R¨xZô s#šܼF >ŒË—²ÏÇÛð÷–ðI‘ºhê7O±yè}u E2^’rer¶C- —Ñ)¡ûcìê2*MXÜå’ÜM<4m'4±™ýäÇÆmcÇ”“ÛRsÈÃí¹[ŠÈ&/Ñy-^¶ªMÄt ÒÊ™Ygi´–ôk+çHF¡Ý§¬1cà'=õÏ÷è¿Üyû 4™ùM‡˜DÇÛd™ðIL£8Bx;ŠS 1#ioàfQ çß2{°äê’55…TÜv¯ÉDÒ®áùpr°nn·Sç [“Õ–§rÃ|RHq{דßÀȳU¿ è}‹d‚œöR.”V²K_ySö¼q½-ª¡4¿Ve‘ƒ.M-Ï¢l¸ïJÏZÉv­@EQD`h&·=éû|‡“‹ë‡‹Îæ8ª~‹và¿wKÇ9$mÈÒç°6Ç7®\¨aôµŒº¾ã‡Hñ*ˆÈ µº'‚ß lfÃ>]ê«å­ Ël­ìèï 6£¥ÄÒÞÅŽÄ! G+iXzç´Ã>ã{?a5ëóøÇ£“>_ÃBÚ²3Ã/]#1=#JD~×:›HMx7Úåÿd¥ F»2Ío£†ò‹[ÚO| ëeؼ"ì)ø€ ôá!9óÖ8ÀÜ›5d\ òÏ©nÏ$Ä…¯T¶)µ;ãÝ .MyÊÓZÇŽ=©Rãá†aê½è.KúëÓô܉O~Û±uo¶ÀBÀ%t·êL çJÈ‚ê$ÉU7>ÙAÜ00Hz†L“…ç‚ÞB¬9ƒ0T>À+1ã)Ç¡êfÛŽ®ÛMYÀr?¹þMò¹*ä@ïétÏX)ÑC R—cï@ϳ›ÆÖ¹¨×Àƒ ç͆‚²oœø+.øv±ˆ… j ˇ4…qŽž(Lǵˆ@@ŽTŸ*Èo†—Å5ÈZ€œÄ4%ޭÛ»™OÚ‹µGê :„L è¶& Ts¥ ,[ãMÏضõ@üÌxÜÑΫ§û*L¾}†P”¢Âñ‡®•+ÕV¬üÁÓ5¾¸q0†Á{ZÅ›Ñ2ëé öÔ¤º\Q™<¶uŠ,ê–­¤ \°”W -Ÿu^õfb˜JÎõ”‡¬)ž…y)\{ž–´¬¨½¹gѫ霛h „úÑ>{çÅmòߢiè w†}·N»ãeæ¼QUä«Gn½w¸t¸Lö Á6˜Ø*OÓt¸`WëúS´ºEæT“®Vî¾ ç’Á7¤D­ØŠÙIfÛ¼§Ì¡0‹eã»ù7ÿåà&Ýð#RùóSŸ Iy ýÈ. Åî°Â;åÇ¿?@ª(þAãMFˉbÔP.@å-–Öp \O).›)É, k€¼Þ3££MÃŵ?„†¢á²Öm±7Üve^6÷/Í£1dZ‚Kp£u¡ƒ^“W¹hEAFñóÀq.qËXÐ#ÏQ¡±Žº?Ó៽*{Z÷ýý;…|i" UÚ9íqý=äxðQë ®'[îTduÁYµ‘Tâœj¶ê…®NZFÒpOS£H†Æ_|‰Ôàòg<•t´)é‘òæ&:柴ëÓ§V °„«Á‡ò·Ã¼œŒ‰a ÌÒVUoçÄò%"]‘ð# &:œÉúZ]Þà2Ùxy ÛÆIì¡paëõ•’o+9*“–ó )FA§dè²Ø-%l(ì¾Â¸‘T¤¡äºes§F6AA¡XßU'ˆõˆSŸ°¤Và,%º 'æßÀ¿&9»TZCa*ûu-w=fKu®µP«&@c×}ªo´ôæ&#¡×¼ sdäÄpN ^p…Z9‘ënv)9=ÖÓ‹%Ê?í³ç[^K¯Øïu„¸nà×öèû½t¿·KÊ|Ëí÷ˆVàbF6먎l‡]Î'ÍekQ`õßzX/û Iõú·«Nxzóšm‹{müên‹áß‚ºÖž£ÉôŽ£b±ÅpÒëëØ7†[†Óû<‹“ûÆ.C?½˜G" ÄòG\çÝ?|ŽþÙ¡)ðC@UJ-O=z|'IóäWʵ„ýûúeFo³Å¶¬ÍQ³á]ã&;™4z±ZÕmj¶¥ãõ’‘žüÝßNÅoÄǨ–|üh˜Zbß5hz¬%I‘T¹úL<¼“Jeqûü”8úô·Fèº×XëQdUÓ |¿Ileÿ†=[5“KÏ06éÈ‹ÌèCŒçØŒǨž_ 4/þlíY¹‰ß›pÌèP-“=—„‚Û^"‘wÄÆ¾ :Ô¾}zÚš°¿¦¯%®zß÷¤Ê`¢sbÅ3©:Èè,ˆtÎà+YŽŽ…Œü¶{Òú™)šÔuz8ÊùLܴȸq½„„ÅßÐÒé -अ€k v&¨3a­~NýâƒÒnðK‡cA¬Ì%QߡѡVù7í´m¤£>„6~Àa2Èw™¦â忱2ÄpN<‹Ð‹%‰%(e¥…|.÷üé¶­0Ì™HRê^…#r‡|ÀÙ¨¨¨S4ç™ ­­@X,õ׸|å8½Ïl¢œlqöSpr˜Üf¿Ìù˜ûÎy N& F°ò}Æ@ÂæÚ·EŠËQÁï%/a¹=Åö&>¯~Ê?˜ß2¢•;b%0º(¯D¶.¾Å@õİèÍò ñÿèU˜Æà`¯xy27­¹] ²÷o„||†%ër¤]šý‰6„AhÕ'ÿ¤*örдyfÕ1¾Ópö4!U,’` Ó"K‚ùm þÿƒì½/ÑÎÿü­ÿÞïçfPP˜ƒ]2¡êsSì£CÙ¹|äIàâõÒ§á°Ne,åA¼ü½Â~›J‘+ ¦½ä†„ZïJMáp/›žÔ÷oå¯ÅlÌ'¬Ý«Ž¢4Èú8•Þ='©oËõ+@,W ®²‰üqª@ð‰| šâŒjñÝã«&EX‡G¦·-0ÅÀؼÄu#% ÔùV%'ù ßÏPcuWÑÌTÌ÷‰ø!H h†¨ô ü Õñ6Bû‰‚&q· Ùâr'Ed†~û&;¹ËÈñ­© ˜|‹—&œút÷ŠGNSbWJ|Tp4Cn–#úc¾²™ä,aÊD»€)<ÎEz«úŽ‚¥‘4p [¨‰f¾ü¦p››M)¯'gÿÒqaBL—¬H¢ÛEEkT°°­äsó•ä† ÐÞ4hõÊk&¬¢ßaAhJú—ß÷Ô9QÈá‹)-dÿÞÕ°«ªÔ?;¸7Ø¢á·}­´ëæ ÒÌÕAçÑDy@iæùg¼•=Ø»½ ›ún­6öU°OûÀHÈË\NEé^aCSoÚ«™2’U Ú&åî™5x–EÝ¡ÉêÀô:-ƒ+9ÊŸcrpœPŽ1¦ê™v!C¿Ë‡>­s௦@O¾{œ{¼‘ò zI`à›âQ®ôÓß8&š}{×$K-WÁ’Êÿ@ز÷j²”Žf´]ÿw8èû4·ážÎ%ë1t}û“{D©±ÜÍXÓC×=àlZ³âÊ1lëáÄ¿ƒ~a¨ÿ.úó<á—:…Ñœ|â,š“_¢ _³›ÔõQæì¢ø™R DeúÐmÊPì+=˜ÞÊ‘Û&£¬ÀJ‚¶ø¤`Z$âr‰±\ÏìfòÃ"qOKè —èH[a8Å÷ „ÃØí¸êm<,è£íg¬ËòjXÄMjtèà’ÞÒ+fÑÝ/_ÝßÄáæû™«O9Ah~á+sÕ¶°Þ{/‚´Z?nœ×J¯Pºï*¤YñºxÎæZð=Ïcå¡}ÊÙ2ÿºbb½õr÷Ÿ´p0Á³r9ÑkþG7ƒÚX>Åסñ¢aúmkaB:…sú„é–p³G/©«Lªš-ɺV1C¨GzW[R욂ԣEF¨t!-wj·Ý–¬F†ƒ’ôwg©ÚC`¶Gêþ){ÎÛrAFVÍŠmD-¨#Þý‘nþg|{KfÅ¡‰ÕðÐO]¡´9DACœþ>L¨½]¶ðM¸&Õ]SD9"?gå”júÍó>”F¦¨hIß Cï~£á0”eç~–Š~ÜGã³,6bIŒ¡ß€ gjn້pÆ¡`‹ËQ‹…H±† Ñ äJÕFó0c䙜rG€3ŒÉt° Yoò¬#qMaEÏyð)&O’KìJo#¨DOf‡ÙøgrQ5ÎÈKˆf™£P %-¨RT†xÊ,CÞ’UÝy˜‰ìétêÂÐ:Wpæ¢ÂlõÃ) ¯Žq®µ¾Pìz©«}Å[§@ט7™í=Ç’mW²ŸeÀizOºÄúI‰ÂÂén\‹eÉ ôL·)eå0²&‡”äç$W°Ð šç†‹mïÆ p¦.2’°˜Àùãx¡Ï‡AÝ–(,eBYëI\}!HŒ¯?_éfÝÞDû›™çW£õ^QÅI1Ä6 ‚çד2¶`Tþ¦Å"¦Žð¦õL/Nqnv'Á¦yÔ‰S\ìB Ê.UF|úlÞe51¾cª9V—"šÙ”¤‹ÖT¬Û>ÄÓüJl¡½æÍ™$á*q]«Gª€ÙD Ó»—²4sŠMÕî%ì]‚`¥ƒ~òF®›BskDoGüf#Iæâp/ ×~ênõë0Êß~sŠñàvXÖB¡(ÖCLZiäûõƒ„Ra–„zϸnòe•â9M½—OêÂdÙ\”S‹a»Çç/«P¦…J•PõѪ×(éM¸ßHö•D8™˜r'о$%ý¾lä)éB3èÖùµOx±«?`8Qx†òÜ77øv½¿ôNW€Ÿ—Ë ÿWÎÔ—P^úI…ÌØX;ª{5h”ž ÀÖMœGàV·6'š¿òx&þDб|ˆÁÚ$`À–Y3•p¥Z^\<;æ/Fº­ûærúäTó¡¼ 8žIˆ™)Ârû —¡©Û©Ôf(*‘vc'¥véêä_× ¤å‘ÊGr1Á„ÞSTñ=÷¿ŽDwæ;sISÍê#p:åbÚ¨Fòþ9GìBèúªX]a?0Wý¤vƒApˆÙæ­}¦`ñȱÑ:¿`ÔúÛĺt&üí/jÀ¸KXñIè®Ì<Í E˜Çt4±R/vA}ðîÔÊ«I»EÆ·oö~£½Š c †DA²<ïõ.Í[ñʇsa×' è–¥“޲Û}¡þæ}¹Á&á+!9DûÆzóøŒ%ÆÜ W) <Ú0‰l9é"ÞŸî­EÑ%è…Ïá¾¥À‰°ˆÙµ7ìé²–¡ .þaSrCvsÏ»ð±V—Š2•$01ñ»þfË»Lcµg¹»*ß…ÙIàRÚA+á§æØæŸþo!©‹Ä6¬³ûã·´íú÷Þ˜è#„ú{§ýñï M=ݱΙ 5W÷“!wzÒgj9Ç¢Î`EÊÀß5Ø4WŸ&Y˜zqŒépíL³+ìºéE „$6ÍCçÖüŽ¿þ“…û+¦y÷®MÛlJÆ&Å\$£æ7UWϹá€?ôt´Ý“óeéòKXÖ©dßÁ»+z ìîÌyqƇix_¯÷äé(éÏöl|ÕüòÕf_¡#ØlôXÔF‰]«rå’‹²í‘MË DÀ) â¢{ Gð¶ÿ;@Ä¿w1[vŒ´ˆ hødrNº„“!âTeç°«³‡¯–y]ÇP8Äà;œ=cœ`èÇýÅ¢?–¾™&ÿãN+Íé÷”äØÐ}ºìóy®å‘yÝLÍO9ÅÖàu“p3š®¸w³üò·3"ÏóàS¾†åY³}"å‡f»5Wˆ'µfúÍÈž±å‡ŒóyZûË(E³ŽiÖLá8V'[ñœ} ºÉÜÑQv+óœ~KývR’lMÛzŽ3¬]Èžo!ÓïLçÞ暴«9BÐW”ê®8¼Ž~¨\ Œå¡ªžTDׂx°›ÿŸït¦=ºßd‰ÁúȽ|4¶˜HOîíi9Š»"KàB°,ŸŸ?üꬼ–Vªûúó©$K¨¦‡l%+¥ý™‘9¢ÎNùÍ’2„7¸òbåª\‹ì‚VðPÛ:ƒ=•—ZÎ*”eÕ±R>CJg­[=ÂÍ å\U7°Ã[,7aëÆ:9¬,´ŒÑVl/Üò.·ljolHë•Ò08íbЦ/˜åÞ:™ÃÛ*€C@QFÈà S€O“‘ÿúŠ}öÉßF˜ø÷°[Á¢8È?.2Ð<àœ÷âˆdÁérf¨ËMÿ û-–Èù–ú¶ðÿE} T}.¡n© "öÛ\žŸ ß2 ¯¡•á4Îb>k\àL#‰Ñª2£Œy×àÊ?4‡ÄäôÏ_ QJUNB–‹™‰0×d{äÒìx°Û´Ô’ºü6å×TOúþBêE¦‘4ùlÔ#†Â4©æúTîv5Ðñ~’²ŽpÖÚQp–uX()7Kï….–dˆRýp{@íïÚ0Ú•µ¢NIt + „€àê{`žoÃ38å9æž•¶²¼“EÀ”QX$U¬üÁÛ5ÆáNñâúõ:”8ొôK”]5õMÁÑœ©è;™IÐUYÐjâÉ™ÁµÈ½ ×EÄÉðA/›ú‡Â9žaö©g!séHhÍ#ýÝÿ²,­)€9´Ç'nË"€¯è>Âû\uè‡ò‡§ÅÁ0«¦6(ΟµµQϾO2{a†º¹¬xµ ú\ºüÉýý÷üe¡1`¸K!ð§sð ƒè´Ì©`Ñ)•ƒŒû:á;†çvÑqÅ ?A„pUR¶öÀ‹±â®KÁsí ö‚l•ñFŠ8,Šik?Ô4ýá2›ÅRi9â?*b !ft~7¤ËL¿ç¿1WsœoWÎcI0??j[(¨Ãaa:…rÖú¸²²Š:/†Õ½­Œ-g5ŸöàX¾T¥†wªU–Žºè®’½ÁèÚ{-~ÿµ¢ÒYÆ' *äaAék3Ö1Å`ÑïŒoËÝó2a_*—Óö`î#0ÇžKúMw uá ³sAä}4á–_’¥¯7;°åù ¼÷°.Ö,¾W\iš"¯£ ]#q~Ì^E±u_·xÅ@ͬ3 «ä|-t©¦ iĬµ…äSxCÞ}»ѤmEð¸êb¨Jŵ±qœ|(ÓiE›eÍ"GŽ c÷4`B{-϶¬IáÄ;ß©í+…º€Ö,‡ÿ,uJw˜é|ó+Û®¯ütóS]«I «ëBÊvǿˎ{Ë#X/#Ô¤_œ¿èTGJß„W‹Ë¥šl1müqʳ÷ÍOÓÓ:5t> œ&¹zíˆ×·U™,4ÿ–ëŠðù9gµ§A²R÷Ë¢f§ôþJõ;à"°Šièd8«nZÃÊ’Àe Æê\Ƨ”÷ýQX ]@&^€.—ó¡ƒ"é œ¡ F,£á÷J¦ì.–Ð$©!9H#7密gÀÝ¥u^n_&;@ðüŠ’ä{³Œä{¯#¿^õ-œv Pg9²RËiMkDRn$`’í»ïdý°K<`±Ðž_w­] rú6ëµ1cì8L”³Çln¤öù6°Ĩl—Xß/¦üb·Gb$ãÕZâÔÊoífq‚³œl":…›b(‘]?Q’>îWötŽãwÙNT@ƒMQDŸÒ¼­ë›q¶ý` z•zŒ_—¶éŠœÚþ?©òc¬³€ /ßmá¨tÍ2OÑÈoý‘ÞC“©àV}Ò’Åó¯€HˆÕ% HMº Ú°‚¼‚Ô*uŸ·óÅvŽ¥ø4;Ì$³´-„•±“˜83o[_õq]›\‰|ëXâÐàFôQ·Rfß-–¸ôL­ö³*Ìi/~}Ñ#ÕŠäMõ¶ª\ÏèïƒKäÕì^öM'æìó¿0•[ãŸ?í¶Áx¹ÏÇõ—9ònsȯá¡ 4,©¥øA˜i@Rµµ•Ä<ˆÒì2´fÒ›/±¶ò€»h¹NØ8Vb x• ¿šü¦Ôa÷ÛzÂ"¿OuCP^E ðX<ÎPü¯E¡Ë™Þ›4 dñ*bý§T‚2¼vYñ¸ÖsÉëXÕ0(^hÇIaþvu]§¡1ß0_Åûõ\ $™‘±3~÷æü[f™€÷³á‚*ù£l'ËCÙTN¨tj S)l"o5©Hc­[$Ñ=€ªÚÃK{œ˜Öɾ½.zªc¦ÿ>“HÖê¦xþÏ :55 c}E»Èç!Á^ù^·O»Qe!¬×¼bÒ¾¾±÷±ÿ‡J§ÌÍšdq´e™F2ñ©Q#$»Ñã¹³¶x®§-Ñ øl©1SÞû¼tÕV­år"hý5[J˜™“<ÚØ¦gzåÜ£›Iališ0Vñ›¬¤ciάNÙJwfNS*ݬHƒõµÃþ‰ ©Á”¯rÙV²¢f-`•·ÕÞ)ö-\ µ0¬û>[ï{”$D8î:Î4 ~R!dL$û‡ éw»¬Á±ä,OrsO‚ŠÈzÀß9â2ù’\¡¢íC/ü ·Çn:>y€›í0/5WÐPbÄÙ{ÃÑñÉ:n-ÃðåêÛ¼³1åzṪQâÞ Vp  Ýž¸6è ^­6 ÐùÛ”sp±”µ] ¿¸í®ò!YÚDzw}å\ Èw¶õ}„«ù,áW=MÂü§®Lxµ —åõ–ÜÿñéÉ‘¹ÈÚ§+6hÊ 4Â9ÄþéÀ€þ/±ŠQ€D€I’¡ƒª‰ÄJýr²9êµ±Lj2é£Ff ¨Ž¨vN•íG‚´¯ˆAž)x=‰ÕnN•ÓÜØAì&Ÿé†ÈÔ‚4õŠ{¨ßé9Ó`³“܈’j±Æ©çIf]ÜÂ&Upsò„hÎUòXwáDwqâËrk2[ûr‚'=«Od‚øÕ]ùÃ\ßiï›åe*`Í¿àÈõœÉ>Û Ð§¤"°dí¶¿¦¸êÄýhn-Z[F}ú¹KCs<­hFևт³N ¤y4ëen‡à6·fi@$«ñluŒîI§Tƒ…è0ev$òÅ5DÇGhʼwÌð£gž÷K|³‚H„¡U°©îÔI$ñCQuÉ(ÎéG­/ãØ·&;ùÚ' `G˜É'Òĉ ®û<߯d÷‡(Ö%ðšÊÅm5³)aºMÏ:õÚæFñçD~ y6¿2þœ'¯›-nÀ¸ô·€lÊóØp¼7δ¹|È&ÕΜ¨ D oÜèžÒä[‹“<¸$ÿòTÖró ŠõϘ£$Cü»6„æÀá?wf0C¹PÆ¿`/O ‰{Ö¹ 6al“¿v:‹[Ð_:)á “>‡6LðNÓ,1JÆsk²‰±gU[ºS˜ËxzžK Eý~Xl¿-ú3çÏ$ôÝUwúD³QÏ›_nSŽSÇ=PÔƒ{'K›¬ÔÉ1‘Ç}ºÆªÏÄ‘¾ÿÀe™Þ\pÇlïãÓ.[|@5pºAÒm¿šФ…¨-ß|Ö 6s±ÁìÆ4’–+h_è²} ™]%f©+°¬'+€ææóÞ¼¬2aMÿÀñ0þúÎÕ¯µ„D—l5×"äýp·;q/Äur~±[k©$€úr¡ÑÖ£±Ö¶ZJXï3«*Îç–Ýfê5›Û† ±Ó¢]±çØŠRgïpˆ÷j!£pD&ô°8~2€&ÉËñxÒß±¼l,8GÇ$”óÏ_Þ‰ ÆŠé£tä¦Gk^»þWJ_ u,úÕÒ‰kªÝoÇ<|^=ä•N_~›Ö6øÿqÓ;rÈ>¢Å«{‰ýزÔ9•åïŒq½Yˆ뮼iäÇPMP›Á‡qúvµ9NZ¿“ÚöTo<ÃÛG*.M‚Y­Ž¯%Û;mg–S§ÑdÀ4w_`ÚÏ`àÎð²¶á*ªÅ±ÎBèÛ Ú3¤cÐ:³6SLâL/‰Œø) 2[¼½uC¿ô~•šXhÖm¾™‹÷ê„LðÜ$ ˆ“HCÿ¯a,¸¢ÊΤª2O¢}²üÄ7Ýî9§9JàHû-Ï›_‰djˆ™cר‹J¢¼è†[‹7¬0T“*'ÍT?ã/ýs’óÙ<õµž ZêqŒ‡}„F4 …²š2_žhØè@?¯€8À}úŶÀ™äØk5€å"Ïspˆ{<;e.Úß7k<¸VØ›EWÇ,n¯Œ >;èC É~òÑŠZè>H)a(iÜ~‹S9ÇG+iŸèÿÒÜ#Ëâà¯'„|B”‰±×j[ ‡¾ï^M¡ÚÍðÄ3*ºkÕ°óÕ"ä Šcm3P&‡YƾúýåŒi[²>ÏNø–Òº¾|¼¸åø“ìsY¬@Æ(\ú21tE¸±r÷ÛYð½ðج+“M¸ù$ŠÜÀ ˽oËh²””˭_,@8ãµüèü®åŸêdLM½–‰Kq÷„©‰Æè\î¥a]QøÀs¾N¸±ÃîÛK`¯f¡)ë)ÙE¢KSå:E {óiüd$†¤þA·×IâÛ2Mùv 3YרZ³=ǘá'k&­•„.µV?hV".¯L VAA&<þ7K¼´¯ø$5ƒ ³°2†% ¡VBKéËŠpˆyPŸ¨C†XÓ´( "¢å8üŒ,ÐL—Áem¤.ž†ñÀ0NïÊ?IÊP8¶8”Ó¹û1a¿zÖ¦H#•KJm¦a~ªwvÝðÁPç Ù{ZZ»‡úóWx¼•¨ì¬š›SS,¼Þ³D6M¥a„¾–wCæ¤)Á <ò&ârþšðæ}–A…¸Ty\˜q÷ºe1Úk:ð(üÚÀ¢VÞsüÇEjmå}9PúmÎäÌɧÛÚeu"¬‘$r⋲™¡¤(…)F‹vI´Ø`JN7¯ß³ò«YãmãÙíä0X˜K°±€ìÍ€¥ÝKÁè>‡šf[ .þ,c±p‹xö«ïO¿¢ØÁ-3KåeÁóB<,êà‚ºQzeØ]}ÑjGÃeÇSY)8åS5QyŸûgNã}~ ã8Þö‘Þ XîŠoY¾8WpϹª{ŸNlí±•U½øÕ>XÚràñgœ Ž™œX¢ °é8¿ÖX•Z>相0¢öš­ä±"o)» ‡Í?´‹ås&á­ÚgNÌ{Ó&ÐQ‚É>Ï“DÆäHiQfçdô6>ú¹R½—ºy/m`Ý; yf!~É Š}ÒË`]i88Ï’?8Iᕞ¿ܧ L:°·×5ï#¹åÈ”ÎUëÇ©òáò䲿¦O°¯ÄÀƪEÁûéï­þpžZ[XSª{›+QqòuÛÏVœ@&ò¾Ýdê]]dÊNù[lñŠ c•‘ȃDÑÈ/°¯ÃÙ3ÚÓƒþâËéV``âÖ©—e±-³‚\° iÛ=s–„Ü©›:Çi(ã)è+@üßék„ýxßÝxªÿ#ø¤˜N\l…ˆòàË8c¢L–ÙM#ÊÌÖs±X¤â «á𬲟Hé個$|µ‘…T'ñÑW®QËân,àí…6[7k /“ú²ÝI£§ÕVWw=H®¾Õó–t’ž'Ü&ÁGaN°JKâ…‚%Û&¾åïmÝé…rÙkeP–¹,©>'} ‘ê5‹€Ëáô.s›d…íaÄ;>^d"‰OµM‘ùü4Çõ·ö%·ü3ŸŠÞÖîÅnUÞ¯6¶uYŽÆÅ7Å\T݈!'=I¢ð2&"’© ) 5›ì8Ñ3•’©8ÙG3Yî6$Ÿy¿^ëI´ lh© nNëÜo‹ £ÕìÌSz…»?7§CÃ4ùV÷5HãySýˆùxäÉ<0}ÜLg®Ix¤Àg¡Áìk TÎ*vNŲ“ŒÈ7« Ìâ«£/˯jw?ö4€ñ$/ó!ˆ¶]:=š¦yÙÎ{L2š}õPªµMGâ©/FL䯷pä 0¶€…W>C:L¸Æó¬þGu›A<ÖŒ=ÉÈ w.ŠÏК*O,°Lt²] hà)u¾"Ôí—3`MjYÒ0øH‹ý ÕŒ<6fa Œ€¤ŠOÿÙLôM(([óƒ=gПÄP¡Ç^- ÙÀÁ{«>±”^7½WÇšÒ¥Â+˜Eò“\HØ¿ˆ±¸X÷x)Ü/RR 1;*ê!¿%)ˆ†”bnB±7Q0é䥟)Ie&G®ÐI‚~ß÷ͺ¬ä¢>ÄÚÓLÎ= _8@°é­îŠÖoa© ï³ ywzd‰b“¥h™.åŸIðÁÁ_ß›?}È"†…83Æ­Û ¹Ý/þøÆ³sátËúB¾sBœKõ½ú}ÌÛ°¶`}db¸û†„ƒì®î£A;6ÃÆ…SÉè›ÿe_9J=j%mÞ‹˜ý¯À˜Å ¿’ Ï•Ì>4¾›Ô|”ü '±,6A :Þàúav;Î!É}¯ý2  ša‡ÚIg7ðîÑW±WO_|çõäÙ+Š¿ëÐΙñ^þ\\_7æÖÈEµ›(2pn¹¹ÏÅþÖŽËã!•½òƒÈûð{ƒ3rªì#ù‚ÀÆÁd PNg™¹ºA¿`x…²i $ýæeÙç߸ôÂS'øp£æŸÉ»Ò½:}È<Èd“:ç¯,ȳVgñë֌9qT}k×uÇk†‚"ãR¯yâOsZ^]ö÷U/‚¦ÃÏ*áBÏç–oΟó›fI |È «¹@ûLrFæ­[‘æ‰ί“VÝ™ö3QR8ÁÆËعVUüDr•da?ŠkÀÒ^°ëœy¾$ËdÆòñ“0Ò²@ê®î2á÷ªk¾Æï´ÙÔ NŽÄ™ô6™œÅ¸ —lN 8ׄgÏ—­û—Æ&ìøåç%„Í‘×Sœ«KFOظöä¨ä÷\,O#:·Ý.Êögâôá •Î8c¤>з!|Ð5뼎ēæcØ€«êxãô%4§3X®åÂ\Z%cÂt%« o©ü˜Äl’«ûW‡ªœ°6»‰ÝâHˆæ}t6±'ÆÐ;uSWýŵ 7™†'r²AÖ²™º,Þd µ[Û™¿êØ7ç×»è²álÌ^ý‡‚mBj_‹»7æÞßas¾öÓ|Dæ©e¹ e¨S1.r±±Ø¤ vš5í#å¿qž &Xh1ó's=r»yÊŒ—7Ù Úæ­Ç"ðÒþËv‹¸SåYçê&㎃{÷¶=ÂêQ±jæ‘éÛEpc±l'´X¾]î¯}ÝÑšàÂ(×÷„ ö––¦hNžw ]Žm§-¨fˆ)n[T¦½Àk ÒY±×SoþV­íDì˜'q]Âí–é9©·NäV{_NØ´ä6±h…““¯š¥æ €ïjz­XœWƒáïÞU ?|‚ÐŒ V"ÍîÆáJ’lâuÉU\œ{`Šæ3;ÓÄlRí/ÓŠW« ت¯§ß^ \í峡ó#Ú¬C™»­©£Bî ÕKð³·ªe×ïŠÌú‹dsˆ1w.¢xaï=·]ã¼î†Cô2S¯¬# ìi­ŒæÐ0Š-LüŒ‹IÄôCʼ…t×ûY±Œ+³;Ÿœ2Y¸üï1^ÀWÌtÅô±{ ‹>{P‘ ‡K©ßIwóJŽÓ€ºÖ?p…ú?]&8Z"0´g6’£‰Gn»*¸fÍûÍúbÏ“‰-ü­-%¾å>Þÿe]Å뻄°`rÚì(pŒÍÏ\ŒÊQk…&÷6ÑjdÔÜwŽø¢°ÛDÓŽK d–×—S¨àbÚ7¨Ó¼ŽZ ARÕd¾Û'1—ùñ›ÛÇ>bM•O‹ðƒù¼ŒÕDPÛѩ؆ŸqÌÿ@îRÿÉf¶—6ø"Ñõk_Ä*\§bô¸^)ô˜o½æƒ ˆÙ곓 |FF¬‹@\:1#É.>RAJ›á ”Šå\Yk·¯G¿Î.u^š×-‘ýâRÑ@†*0 wÞŒAd¼cVT«%ÏWó5¤æ"ÑeI)ËN7# ¬9”;ï\œ~4z1Ô.̬ ­µyÓ'ÌÚ¢™ •&˱l•>WÆu½Ð„q%²#ÔÖ"Ö~Ñ-Q°4*—F:ÅMeÃÕö-f­úÂn<Ú,©¹ …un»–µ†’Ü÷«¡ž¢«±<âù´Þ´!¾åQ-ps$Ë 8ZA³7b˜X‹ö¬È;¢¾{@½Sa±)hFa^©” ¶´ÅˆxœÊ³TãäätB÷¥.0<)0âÆw H¼\gAq9Á! H‹)ŽÁ*âTßË+Á¿¬îCh¶i,ÖÖe]—¯ ´Xÿ½§Y"Rÿ  …Ï}½Aðïr5v”nöBTQïè¶Ñ÷GLÈ<ëì€MÀþõ»<û©ó Sëþµ•¹›HŽK~èì+­#&òƒÀté_ü`ý{LBø‹,°Ô{xpK[‡D —Œÿxö?—tczVî…áþëbímjœKÔ«4”hÑÈ:SuÙ-d]%j~±TdDÀQ¤qò¸Ç:éÒYl™Eq°•æ$Ë\ÆÆ*—’ó+oÌ]œ7Aü‘†<Ο5wÑw…Që™ÛËàï}oðU…l׎Ç Äýv¶Uê¬Ru€z"_£_;I:ÿ|@üàTCÃï)LÏ¿·CÅ·,Cu¢€œÅ탥xH¼¸÷hþÃpe)Qò£+ù{@Ö~}ê¬åðÅ:ëMÝ՛˿歸?®â6™b’‡°%˜™rIeftÒ /Žf-žêÕ7q÷€P¢À1°ÈÃH/]ÿ™¼…{´×*˜Ç-j…ß;g%ã+Ɖd× Âþ§E"gPÈQ‹ÿž°9€³†—ϱ¦€-ì…u÷ìðu®õéÂ>RfÕÔÙ³U¨±„&˜Yž¦MöVÅ$á\~]Ј¢^÷…Ô4MX¹xØV[\), üž®â~§Í£Ÿš:f¯?û‚0;Ù=-¼°nFU;ÑÐiç!Çž|´3!BíØ[N“ £çG¯/ÊPt±îìý^FÄ–óÀ Ý8íéŠ/M+qq&8p–{UûçoÊãolkÊDÃ÷§&D‡À-Òw·™Hœ¾'¤z°5cBäHt&™r5=&]¿o[RpZ±™ÍtáÀã6PâO¥ÝÀ2¨qûÈÐÓg+¶íM.V”·Hnðò-‹Ø¼ª„ƒçúr+B°Ã íŽ °‘£Ü;dtJǾ4ê8tćú „f¬%Ó†,|ÛÍqW&¾êÌÞ’Àêíuäx囿1›óØ^$%v!`6 ×Ó‰ã+Q·£NK†žrv¥ËÃÉæ\zNý'f(óâÆOͧ˜ay©58›F ‡ ìd„ëá]ïqJ(U’—(”œô£ã”ÏŠD€H̘NJÞ„Ú~cƒˆažº(x•³ƒ)‹×x8JÎ4½lt9­4g»ð6Èv¡ ?eßloU5;JI½ÌÐçR ò\l;%H£…‹Ú~üÌÝ·øo¡ˆÕ²„Ò‹)•Ðß5ª%›ê}¡’Ò!DÖ#tËD/%£ùô&ãù°'®Ý:ÓÌóMJT|,î~ºAÂ,÷¹‰Ä°’ú0L.˜ï`¶Ö7Q¸Í‰)Y¥´ >CÊ—T?E®® b1˜‡7Ȧ®<wåw ¶Z<à2a«²XCü‚ò]1̇”·wÇäøt2‡¡3¿&ûÙ·^Ç)…€ê½ä Ú"ø åÃt&£kç†Áï7Þ¢<Ë÷A-ll8×'£xžÚ-œ½øEÈP“‚><¢Ô‡;1ѶÉ@bqU*êºG¶@€Chá,ípZòFì3_‘OÊ-¿ÇMÝ]“es·rVëÙêµ›€v‹EbUèÍ<é%ßNNaýÏ+¾ ƒ)Cò¬•õ¾H‘ÍXKï=ö‘›¯¤×úYâε.&|>óé<Z¹þ#axO×3)¾ygÌGÒ4p³–Ÿ€ž‘Ù§µ|Ýç/ÉXÂ¥%á@]nô‡v‘Ò¬ó ÿi]Üá¥` Ó˜"g-°L0¶.W6S۪„èö>9þ>­†%¨)FÍý‘’Ì+"F}Ê„[®ÙIu1wþ˜ M_Yós¹ zç&ÜÍTòæ°<ñ&Aµ€X<„k*;%÷›EdؼñÁ`a7«ˆãQ´Ïl ™Êl'„ÍÉÀ÷(((é1xa8ÙìÕZåÿˆ )#»Ã„Á9ËPç›aŽK9 B®›.f¥äE?é.^gÏz=džJ}|¡;emì`Ê6Å<ð­  Õì͸µ_ìÖcP ›öÙ›Hw3‘»D“L^ÔCONšÇ±NRuéål9hN#׌«€»/ËlÝbœƒúTÖÅ2¼U?ÖI.3iy¬Š)yœÞúª(bo¼ìÉ-77€Ž©À§T¦%#Vú–è?7ç·¿Ϲx4à¡èŽ953ªKk™zÉØofSÃÇû% –ê@-œCÔhaj$¢tåð$›ˆx~I™“8_iïÎñ*Á¼6ø{°XÐì¾ÖðÎn­Âd.iB–ò"à0Xü-éu}¶„–óŒ!*„è ¨ùzœ¿ú„žË5\m~_{î‚ðytb,¡:e‚×<3#O‡ºPiŠZ§Û—Ž“~0Oz¾t Qnæƒ2,RWu‰|ÇiíÏ2t~¡Jw¥6**Š£Ê ×0vcüéR}°Žñw¾®;Ÿü ªÆÿe\ÂìQ-!ÓÁ‹C“;»Ø0«tãa½WB†¹ðoò6è9躮t¡ `Vûv-xϵ÷+) 4Ø€{o3цá$è(›d®_Atí¡–RâlGû½Ú¢€LT¥køËñE“Ú~øÞEmí<-Æ>ïìc+&ÊZ ü8yRQxA©ÿªû®ê<bìô¿”žã²Ö‰Îð»r(uýO_:b„f‡)ôŸ¨ÖaýÔl̪ÖQæöW…?' )¥^Ió Æ¥—ׯi `²<{aˆÍ®;âÐ1otϹq Õl+—{W¾ªyDHÙN>ËÿN4÷R†hÚqeÁpmÓ.•²Vû ñ¾iüØ=r!ºRÿeÒ}–Ÿµl‚A¯è>Ë@ÿý🹹åTÉqØÔ*±C¦ží¯ w¢QP­h\°|µÉ¡§ŽÓÌ•c´î%ãߌ–©x’1Cwè`>ÆkD{¦7nJ§Yù÷u9DU@RFÆxý_)KYœÇ«§o#"‚%Á}*ÚÕ_¥ñµð}óaSB£”Mà=(sRy°)Ð9”;“»^ FQÔGëñœ¤æMΉð¡SjjžéØ µëyRd,ÅÊw‚ûš¦œsKHJÝÕÃ<´wóNÕùö+ݽ&¾TÁÒ ÁÊïbh8®Iå,´÷N6ì¬Âžª)–&À‡)Cl¡E°©bÔ)U[ý¯4CÈ£úš»×±.¨ž],è¥=Þ=MñyÆ -rUT¡V­ Ǽr^½[1Áçt(‘W8¤ 0ZP`@=£—ø3yî!¤+ö£åoO23ÚŽÌØˆB#”ÝÖB×b pá.5 û ɯ’˜ÏïZL$•5e•s¼À‹Ü:ÿ ÎfK«NÒwß“©íëZœ<°U{êKGƒƒ’Ç™k¥#ˆ3 ™°oI×±‰z²ŽÉ*Áï@€n‘_"z­³ÇSœuéUiiÞM|Í „ÿÍBÓ«ª°V ’ŸYØÚEÝš¨öG¨šDb/@_ê§øyýíjÌïÝkáÆv¦ylé—¶÷¾ðÅ-)÷êÇ`Xe'Ì01¦K½S£7Aëï#_—3ò³#Eó½ßá°Ä ’Òc*ä5q•ÚÔKR¤!=èòÑ¢~¦°4çí¶i¤>b}â­¾J£­íEHïG}Ê=c´ {ó˜æ;9 æý«ÆÕŠ«’8yÙéþ³ÖÖø=ŠÆ…J¸äÜPž¬‡à'€yý=&~vÜúƒ*™Q¨–n< Œ`y§%{RŸp´šØ}ÑÆLƄƜöw+Ølú»©Q#bü0‡F´&…(†â?&©³=E ÎLM QÇ~X¨b u2pЋ­¢Žøõ’ï¶ý‚É$¨s¤¬»ä¾3’·–±˜*«îËá ÉãðÜú—P ZÜ ŒÊÈ¿¬üðy *O6¢tämöü®Ù„€(àYÆ{™Ó¶ðé·”XjœÑ„ò vv$)?36ÕvÔµDk„Ì:÷” ÄþPÞþ¯sôVHŒÞÏiÔ³ÖC̱!‡­ëDõ³¯Y¶¯g+k³k¶C‰©eu•î/« ÂxœÐ*{PI^´â•m*ºÌa$I3«X°Só^£ëÁåpŠÛæ©=ALlÆÕ0ŠŒ‘Wh¦d¾(ßq…#%ÄáË“ìóŒCrK˜Â ¤‚º"°‰Tµ’äŸÎRyNPå8œG.`E{B½§ÑCpÉÿ|Ú'õ6Égʘ¿„ÿA}éΦCøG×Lz ¬Ïè¹ãÊbâ³´Ò™–û;’4çJå¸Î~抚ŵΠr6l”ªú´ B5•íÑÌbýêCBŒoìÒâ::`= _Ýí"É}7U™P«C¯¼A²;3Vµ:ô5Á „oe"Ú MÈíLE¸rõæ,­9NŠ€öWÞ;›,(Ö—Í$€)px³Qá.žĺ4åh»MâìÅáí;0Íæ3™UéÔV²Nάö#uϨN•³¿[|.JÏ#Ô½œtÓ^¤³¼©‡$¦mœWjÎÒ2&xôì@•ÉOøÐà›7T÷u%ÞѺo["ëÛ³¼¶ÂCš?M‘(O©Èd…}B ì×iÛÔüÂê¨Ã>#(=»H^B1gŠÕ3¡ã^J’ri‡]¿.}ªiÛ¦Á#f5çR[=²ìß:ÎñnN8ÁÕn \óS‘³% ½µQa}q-<gô±ƒ×BŒO”4a2Pø=Ÿ£RXµè­ØE˜½8©¹P{z+§WS®ý`÷CIãìZârîS©_>€"6U`·¸6âÙ µ äÓÛb)89Ûµ*Š´ ïþù¨«“’ÞŽÌ(FÒBÛµQÜ~EK¹|JRN[‰¬‡9¨¶Ô(]§ )é•¿ î:" ZÃaú§¥hcîùõX®­%lÖøþÏ7Y. OžˆsÆŠdQ¹>VIœñýð߀˵Æä:Áα *!¹ük¯·‡Ô¶†h æ˜Ï&²§T ðíB/O)8H·÷^;Ù¾n|‹Ü¯#ÛFãéI§k°oXñü¶;°h!y$ÔqN^'XhÌnfã`¦×W!÷‹ui˜yZuƒÔCÙ[óN”º¦z›œ::9Ò‡ŠÎ‚ѼÓQE=¶  °·ƒ…ç²&'Y¤“@'ÁZeŒÖ×s0ÒÄXm¨I|`Ub0 Wø°š´°M)¾‹TYS£ &qŸÆ¯#ó:]ù^Zìþçå¦Zwk•9Ò*ÙàÍwq¥ïës¹Î«˜¥å"ÿb®„˜D'gZòE.fzˆFbi_=¦á;æ³A˜EQÁ&oHññ ç7V|JõÔîíàœÀŽ2~ЀÏMò#¦$Ž¥:ÙúÀËECå°ãj"thŠ8†wTA-År8ËÀ¬.ʾ/þŠ ƒóî¯Ó{§0G×qÉŠ;«Ä¤O‰^]CÂXåm,§¦˜Ók&Éöý€L+Ùu§Æ„«Ü³‘gñ¹-5©“PV>iêÎ\lAÓë?NÎ&,UÁ4w-vïÊ$Ùúªá3ê„&èhZê„2–-ž¼{£æLù…Åí·”l“½9k?ðw•@+Eâ!ÀMf. 1¦)2üxÒ.jÆöy¨öh—ñ½²gr´ EmËchpÎ~NÞû€TTЬ¦^%ÖÈûzH7x;fÆ`§CuClùt'qg‚(ØÌ/CP…q= 4#é1šS1<þ`Á…5Wœ•ÏÓnèú¿ò©îaí!‹µ¼(ØãzìãI¼g]ÇÍ?l´J¢B>ÄJ±`‘…Ö1T6^oº!{N†xË;i׳ŸGˆáÿ y·°ušuÎ'A$èZlsr¿†@$ ^“7ͲìX­D,þnµ¨6äΤ¥²š‚çúàÍ´Tw­¬RCtvèM‚»»·êm@R¬sÝÇuÈý8²®í <ñ:Ù¢, oVk{¨Æ”¶ U0ñ¼îè1U4’ÅÑŠ}»&â;í„Ç>»÷âÞ.´i%mk e¥Ñyj³ÞÚM go””q£Äpâv€Ò¾ŠM‚Òo$ =k<Ð>p »õ+ù º1Î%">JV.¿¶Ô÷š£,˜R,WAR/æ¿ö¤^6úq‘´/þ@™ –aVÞ¦â÷ªqÄqŒÆOâùÌsµ ÿ /™™z¹œøµ½-÷s:èz1>IŸ,‰Oõ:˜O”‹„W«1£úŒg?â¦xr€ê îHþæN(ОR++4]^m’ÿUj«ü3J»Ô–ÅÔ‚Ÿ +šÒ]M'™XÏX+oßRDè’6.¦³À@ÑÚíÝzÌuCó>J3o´Úÿ“Աάøf;¡5ö´XÊ­æ¤ñ)üŸý~Š)®æÇ©&9u¹˜KÇ ¼õÇ„‰º§®z§ ½˜km•¶#—/7ë9î!uA¤Áï Ê¢9±ÑÎË›Ê` ü­uÅÈj#Ïw¬î.v¬‹Æ·Èñ±L`‹ø|r *@ ƒÚŽ=y®”•§&V C]']ù˜?þA<žÉS(å4Ø;÷™! ½æ¥Ù®ˆ{ &ZbÛ&ø KZ†ª‡n_FÈW³ fÅ2¨"’ª¥1Öà’;Ÿþ«C;žéѧì‹eSBŒã;Tó|.ƺ@Ë´}…Sµ·çťЏïj&nN5añ)˜Ÿ|Ž ŒZð ¦e¦#Ô3Êâ« T—ÇÝþ'ãÁðÑó'ñKÆ^ó4–Óâ£äOF ìål;ÎM@)š[÷F%lQù©¬Ö£Œi½KÓ&Ü ‹Üµ€.žžÀï Z‡Ù&IíµÂ¢0™~æ =}c§LTNe/[žŽÐMȽóÛ}‰+[“lÌ8€›Î0b‹YNÉAÊÏÞdSèäµ<á0°j# ö*¥\^AÙÆëN‘ÕÝ8U¨™¾g>|vW~›–FØ~¡Û#Ÿ” u y‹ù„u»Ž2þ](qÅ"]OìÝ. [Ýφɻe·9q~®¤Zïÿ‚|ïÏi´¶÷Ø'vGÁ3Cœ¬;¨Ì wR¼´Ù&÷TeZ<†(°l8í!Ë ¶L·ªØ Í~%}±ƒ¢ 2„ôA$ìqøŽ ’QÝLh"¨˜50W¦Úë.ŽËLã*3KÊ•;6wˆ>µð±PV=Õ㎵Î4ÈšœC ˜g¤yœg}âAàdhlÊl7^ÌòO¡]`˜~|¶ÓãˆÇŽ-}šãø»6ᵫdóVþñ¾§ˆ¤5ZÕúE%žQYŒp®GéÚƒ¦½É€K¥í1q’åË“gú€ûå±ÐB=i7'dí¶øH¡Ë=žKŒY“´€‡Áô"MËjq¿hrÌ¿€½ï3õ.ÊIä?gU¯Ÿª8Þª$¹µÛ\ž§e3—ðIbrâH„ñ‚x‚ŒHÒ„—4åùÝÌTµÆ)ÔèJY '‘ŽÍLSþÜÁžjúSmvsƒ¥ùð~c½Ëþõ³”Ím£ÕY Ðíæwošûös´ƒmâ+˜’·´šÁ (íŽ7‘ý¿VÏ?-® ´‘SÂÈN[ãg°C>S²ò%Ð9¥·Éš£c—RP"¹Šì“ÅÜ?ˆBw5‘1Ÿÿ~'·­ÀÍÕk ¿—sOD86%”£iC¥:3û™Œs$¾îÝ`@kãÜ™ ?¦d3ËB [}¨ÍÜJ»r…ýSs ÒKc$ßõ~Å4ùšÙÛÔôjÝZõr1Ö@•'n–ý¸ÅŸ­u9Ô[—ÏÞs<»¥ÛO«+@ä]±BHQÌY„è– “¼ª†¸æjßšýý|Oô6µçA1dhv|®d Ôî½,:EÀŒÏ?XdáⳌ(„¢ÒL¼Š!d~¬üüc^ÉŠ¢ø™*HVuõsóZvèwÚ{Iev‘Ƥ¥ %ðkæs“oax˲L~Ðà3ÄâÙ©´ÕfÒ‚ya‰ÎD'íhÞ–XÎ3syŽY¦F Nˆç—ͨ‚nc¨ñ6P‹¯ÂB’@¥®qï>*ˆjØÃic»á.ø—úê…Ê¢'3‚½sÇ8Èç÷znÛs?‚¹Iíy° êõ 9…•µú9ÜÊ;Ôj~ýB»0|N¹Óå½°Ö› 4õÂbE°2Éç6 ¥ÖlvVÄ ÕADI–.Pƒ¤È®m¬Û ÑX! áÓl4ͬOÆ|}<Sw[ŒrüÕ–²4Å{,_ô—úÁýæú$…¬S…~¹‚:¤ô¾6*FÛÂßši¯žoÍ£×cýËÇùÖ& ÖþRK–`¥zG.Yö9®µÌˆ£Ï”¯:¶”dbR/`ו“ãâÍÍPˆvb! ]=¿Eq`o:±}Ê |QöMÂד &&fBóÄI$ ”çT¸ôÀn£HðåѱNáÇõ‚yJÿªøèC|º%Š¡BìÙ57“/ Fœ¼3\S9x0†ÌÆÑ\7I S¤K³/F[áõ=žî–1%ÛÐZXÇÜ?NÁœ{ãgÁ›<»¡§‹èN¥uH¼zK§ ¿~OdÑY eµ©<‡%”MÐáëíÔÛ™(‘ª×µ%— ÆH“ý@ CÚ·§–tN†<é-j@²¯S?îÉ®~-’«CÐ~…VüKÞžú}Ûâ\t\Õ@ÕôV¸ì8á·ôʼnWEêÿ–*Õ­ÜüB²hà0¼1þ&÷¾,–5#ÓëL¢Ü͹ ÂÌZˆ'¬OQ—“ÁGžj ´2œ  ©B9X«<Ù ŒSR±UÀž—%BŸÝõÏWÍ„]'“©Z¥ÄÚ!þ pª4+CÊø,D¥&Ƕx–ðÉvWŠ×fÚ¸?å>ü¦¥ "Íæ¢¹=Й»z"!ù䂚Æd`Y6;˜·Ò™7|6[ÎÖØRsz3Æ„Î3#T‚þž?þœ—Ô L+®w<ܱr(ͦà½urXÊÓzÊÖ—h¢ì"p9â}sÈŪt“Ÿ9aîÐÛ_ÐæT!£‡m_’7”Jáµ%•aßq¥¾ø×ÜbjÅ9 ¯ógK²t^¡Ç2rJfyfa.:bš‰U' ¤ª2lðîš–Lm"¸Sã–q¸‚EXãërð@胷出ËWo<ŸuK²¦‡™4zÈmu& ŠB& ŠcœŒW0b¼È±‡Éhï0Ÿ;@‹ âU9Ö„,V%æø¾ ~àêð{‚SYE-~_}±áýñ/µ4w´Q.ìã›p[ªh¼ „³î¤Ü ë@NqÌ­ð'Z½KÒ³+_I=O“–Hõ±{]Ì8[çg|B±§òçÖÜLØïá²ûêr¼2Õ&Ù9§.Zxo%7,ëΔ1§o‹Šý"ùؼ ð¤Ý,°8¸Z+C?ÕÍé“ÒðÃT!Œdd ¬5šáG×þ`$•Ó %áú2ãO+ Ä4seí¯ðs®6úÞíçqÍxmùHèÕëå^  gÐõáUa¿oa©köM¼åˆ§º­Ì,`½ô’NÏ|³4yånW‰b{àœÕmgNÑ9”‚Q‚ºO·"f\;`ºÍ¯©È Éד>™{P`¿Õ÷=6À;ÂMÓ-Îýy¶*ÖºT޼ó«Ã?B×g"öëh›£¥ZO”$ó¯d¦ðcÞœKí>möŠúŒYUî8å[#cüÜåË™ÚæO¥}@1\*ÃÍ#2UÍÂ(YvrÏUÆwÖw9dÒ¸&c1ÌGÉ‚0ïØô%±$Øå úKP(êï$î=.”=KÄ–\›§»Tý“>®¶Ptp/yJy2ѼÄ2æÛ@^' Äü¶„÷Ž-Ug¼Ÿsè¹Çl:<'ï²O¡âü—á§L0ûˆÔàrmÐäˆJepõèòçu€¬´A½õ͉Óøw/nP˜ ~ob”­ö‘ª1T™àÇ îè:Š9¦Ö†Ð4W'3±Y#.F­¨?Ç^ŸÛÀõÓw§óÕ WU*=Fxy#ô_ßrËHJ>²/ØÐf×x9œ/¼bvø€‡œŠ)ÜÀ–MeSál÷x«ŽÕ‡“еsýÄÉ<œéi}¿¶9!†E4&K+жÁçÙÀ!à4 Ä™À[%[¼ùóê\np Ò3ß0]’+uMÈpG$"CÉž5ø€Ì)@®™è1fÝ4«‚^-Ó 9;AÖhO9 Ctáe¾%¶øÈ¥PWƒýM‚x¯M…®¨õA`Ôc#50‡{¬e¬-§›;+²luÞÃåî<¶êv„!ž‘lˆÈø ¯¼Šz%ó2ì6’ÊilÀ¾Iö“žøïo5§oyvJÜ?)…}®Sªýø¥%t³Ý!¥†;›hÁ^'‰Üj¼­1‰þ4MeÇ{ˆ ͦ{«„fŸÛÎÒì)ç¯èŽ2r#üÙË,ÞªŸÀ¼šòPóYÏò‰Gè`ÃÜɾ°ô¬^\"3§Å¡¦¶Å¡NH8ºßϳÂÍ ¬›<~ôO{ W娄¥üËoÔfô}¶£e ¦o ýcjW‡®{Lî­<·’‹V}T«%?4ªäfðáÅŽßav vϺí÷kº‰Éì¡­…2”Û ôº¬Iλƒ%î¿Gs¹òu|‚Å2½¬Œ6|[øŠÒË£þB€ÄÌ_Ý›*% â²~1JL1-ƒkU꜡csÏÿ3Ȥ VÿÝ šºAqTÓ†Tsèpÿ|•ü¹ÕÝ…o9„û¶=We±§ú;¤‡¹=×2NæUïË‚jA¸mõQ‡ù)Æ£_y¨•»Ë—B–•0O“ ´» Õ˜íÓdFÓø+OºŒbÀ/\ý' ẖ4(êÀ„váv[@€•ž«ÀÛz‚qI~¾¾‹£lƒà)V–2%RÚØŠ™E¾ÚØùyg⌅ꙪԧŒË&lÛ¡P•¡d_¯Ùù¥£<«" a‡b¡<¤…·ä ñM/$å,9(ú&´¾¿»A¾yö: ɧ@ÓH;Äîf¤˜¼l3ÖH†œZ¯¤£õa•82é`ÄŸQ-$»Ñ„^"!uAF'1]¨î‡+À\1ˆ14éYWYô Ûþ Ló=®¿¾æSEdžŽ¬EGk2®ÿ†ü DÈ +GªáùY8ÃØæDy}VÒ Ì”8‰.÷.{4`à*.êN[e™v‹à˜݈}jCŽä; ·±¦ÂP}¦ÝP4î°)àå툤ªÑ\ÊfAÀ4ª~·p&Ú–ÁÚuÜ\VÓ’tu¾m-¢j%÷†IBšö"úî¯þðV'•9pmLŠG7°=v·Ç.C¹aiñŒ¦´žÃ|OÙÅ»4å†uBÅNÙl·^J‚^~·íC%Ój_‰|ÌUâ›Dø±æÂ¿?_1ÉÊEÞƒ5ÔHÅ«%gw€ÿÑD©»"ÁÍñóXòTœÂH*Ôvy耷br¶¬íL˸¥A -õ®ÌÅVø±NפëӜެɓÑà:>C(yþë鸢âù»ÏâEÀ'šYƒGêÜé[,_aú[J\KêÎ!­cVƒ}ï󃨸@¿ùtù9b3Óœè®1ii¶£Á¼£ø8U5…>Öôzþ¤,Ö¸¸¸­Ÿ ¢7ÉH\ISf±“ »76ãX­[ß"°$›,·_Z5›üJ;o¨‘‚†¬29¢‰|×…›Ý4O°Ú hEB7ܳӳš¦î§ëû 9C=îN¥ø4 ×{ÎçÆo¨z½ø¬œ ü¹ôzìê-i†ûºz˜á ü)Ò”1 èÐ`óºÁäÒ”âGž Ò§°Óox?«[¿+×YRªÖ=–9T³FëšUHÏ´ 5Ô‘oÁýwÂý?é-ËXê½jTã­ïõovêó­õ¹²µ9~D9¨šgý f®ÆÂ,Ǭº1=;}þÍUÀõƒ½¾'Næ` Ѳ̿¥Þ¹þ!ÂÞ´ÂÖ=¥ wû3äܶ$ Øæ&õí¶oý˜g¢û„äV$ ÈÐDŠÒIª§ÿX”cè9`õȇmýzu?œN9ÐÍìpMc¶­2å ±JAôÕ?y^¢ï¬’³¸dQx%·äÞaìVúDžé‚9$'ðû»ó¬gBœ6aãiìϺoáîååM dÕ–èWŸ ‚pìGË¿¿s)Jy ¥aŒŒ`KÏ»-J½ºXR ÊK}9ˆ»J˜ÇÇžÎÍê¢KílG]$9&wË"õÏ`¦Êëµ#ek9bˆQToc„ɤ fVÕÒÂeåø~©çb!£x =AÏ ÛtW†?i’µO\¶ì…°-ç6DKI±Gõ…“‚½- ¥ìäË|Jq`̹± ¼á GâèÖWþÄþ¶)‚3ëкÐüù_¿MÝÝ* ªÑzdEq³„FÛX…;åi /ÃóŠEg:B4ï/ÅÚHŸµÅŸ¥QÈW&g<<)€_)£eÁzb˜~]:ë 2ß,ÏÿÒ(š&…J*B+ÜT`~]²v½ú53éº:6â~X:MÔÃÐìÅ0—R¤K4;ÞßO꛽%6wÀWkuÙlºÔÇ=ŽøE‘õV U¶)YP¦9¯&]¨Â% ¾‘d#ÚÈPe©”þ&lX«¯ÆHžË,'êâ€1JÍ ­:Æe[õ%h…uJäxêÌÃ*ª »®a‘kŸCn4­§bõe–Ò6êñ…½0u4¥M`'lä0ïâE”9æyŸ$ ÂØ0lüŠÁ|jàÔI;ÝõæÞV÷T[L»Mpå¤cïÆ§–³´Ñïâ/‡¦€aWÍ¿ñ2-¸Ö$^g ûη½z–m„J ‡9»Æ¹ÅeQÈ2SHe·&—ù0S’D™Ôj,ÕZß³ /œÇ†52é‹ÈV¦‚“sÝѵV7Wè‘m½p¦—¥Æ–àaK»Ú kåîÈί?äÄV¿ áõØK Ίëp„:"+4 zQ…;«~ ¶íð1+‡r<Í}%eSˆÙÖôÎòU¯‡U&žC³ªwéÎÊ|›±^šN×^,/ä!ÿ‰•õl—o—HÇÍT¹Éd;°]y÷Ý…çR›ì]†H~’Ũtƒº™*™¼­<¸7k›Å&àcKëT-ö錜"=¨UÐMTfêØìš~ÝNõ¤YÅ~dVŸy©b‰»¤ù³ƒ»DW¡é|µÚ±>ÚªOÔdð•hÏl¹ ×x·óοA·ÿKÛ ßZ Ìæ?ñÙ Äᄚºñ=‡þíb¹ /¨›ÐSQ·|)çB½ cë _î`]¶ˆ§\ÿ”¬hB»¸IòâàÊ6}šá¯`nä•-ºT8^¶œR;x –WB¿d#$ÝïxJàüÕ†üí²AçPŒfÖÄwUvglH¦w7%ëC!ƒïÝ~w¬ÑŸâöQœj†¦›|>ìÃÅ›hJ ¯H’ihQy˜È÷ ›På,È1ëdmuàõÉÝkûSG7ö÷‚b·õh'õ{Á¨YòÙT½˜WL¾e3¸Ç’9ôCÿ¹wÒ‰¡A4þùÒm¾¥ÈjÔ‹Nw®‹æó®ËóPláxÌ÷jRº— ÿÊ;?`}ØÒûŸ‰j˜¶b«ƒ·‚ LPƒR{Ï;dÚì¡"e¢%Õ¶Ó„°† ë¬ÂÇñÁìM?²Íªg5ˆ Ô»òêu±€„—¢Ö^þU[œÄ§q(ÿl[%c¡Ó˜Q?.aýqµÓf¿%DÊ **Õá3cN¡Ö@F1FÌmjžEvœH©ˆ‹ÔP>£dŽ7%ã«™)Œù˜r˜¨ùµÍ ^9³*ÓTt°ü"õªýWHʾì»~ N&¤û;C0Q ð1›ýx<2òn²dŒ=<~¿ùúð¸5\·ˆ Åõ¹ƒÃÍCÜ@Q%j‰ >u›çp9jiǘËÁ ´v²c”ðcòfµ¸ñ_/øH-+__ ´+Os2ͨXE˜˜ªö¶ö@nV }A~ì7/î{ƒÕîï²c¢.““XIøh¡;ðÎïËɼ`yÜ¿×ËÅ,¼½/:áY(,[’8ª-„p†òÆ]Á>£[ÅSŠh^ûÒÝÒ/’ß„¦gÆÝDN¾GÞæÿI |…¬ëŸ!Š®G~”wñ‡/‰ò•,Þ7ï|”Y@Öþg=ƒ÷gÿ®×¾(åÁôŽ·ŒÃuT `ddt>¯‡¤ö'Òa-…Ìú4¼Èm¶˜§jYõéK9ë;:qêBfä{Ðï ªájóÌgèyaÂkªLÁnu0"\-LÿÔ!·"|t[°Ó¡,vXt¨ƒ<𥋠»HgÑÊ-Í”Õþè©DØ;Š<ö¯T{ëÕ¬åÁ·m„{?1íkÅ›¾3ãýÓŽ>ß³,[¸§7RwɬÜ9ž2í5gWËÝûu3 "æ ;Û=™Á:^;$ûX &D”1øÍ®Æ#ˆ%GSÀ· ,+˜DF¬Œ$­Ò­!ºÛý"ò¥x‚Ý$®‹Y£ûÓP,4¡k0þì( ô{D¿²+KóJÿeñ³ûð1ƒ…7RÅ¡®°~ËÄy!C£±É=™’cåå ’ö*MµVš2`-Nó-2¬µÿ)ÇUû÷s¸Þ*{X_û`À x¬‚n úr1†ËdàZ-Òúž0µ¸l%Õ)É?•M¶d"æÍ6âÝÇØ²x0e-â‹=ã"õz·Kö:)Àìu'9Æþƒ_Åûò’ú[·s’̯©x=<¶:,- *W³ÃBüXnk¼PäüÊbnòâœ&XºfÌð­Ó½hPü}Ú³Õå„%mKêÓm=ųC(?¢ ùÔ—¢Xû`öúpëë+ª)@«ÃMl§„—+¡eæÔ(™á…Il‘9e&6iýâÇiœ`€—(«¿9Ó#Uü¸”Ý}µ4ßÎЇøÝ¨ã?¥È½î|]öñì‚ÏÜž9å3Z hDz'Ê„þHÙ»- uA´¼ ‰ÈœÍ×°ºdŒJ[eŠ¿Øâ˜© 9!nûŒüXà#l,þÅVÿFX®Y°öFˆAæYëÀ¼ ºú4H쮫š‡‰bœ¸0^ÉÈî‚èM†8e‹'ù'âRØè‘‡Vxô‹k,²Õµô‘bkýñ›Žhz~`B£ –„kº¾|¿¹Ê‹âxeƒ”„×·ÎΩÛÂ&ª%„EUZiŽ!ÛDû0mæNš¯5;²þ›âzõ*(%.?UÔæ›Î®FÛþt3üuÎôs´/Ìeo¯ÙŸò' Ìø8LðÑ¿êÚzIŒ;!L¼»ä#¸WH ñC«¯-ba¡ˆ*é ꘥#mª¸í­pÖD“9F£I*„M”Y]Ÿ\Á±¼›Sž¨~„äÿ–æv ÇÎB²ÜxH0 ëTÊŽ •^ÒbÛÄû]ÁÎ9Tòà 1ò5LøÜ,F Ó ‰«“ôóL“qe¼ùë·hå³4R…Ñrþ´í|—áÞW>úÓPþ4ç¦E2¹`ÕËi6R”oBÌ¥0I¯J?¿âÏ£±¸÷=cõN0é•[÷Š‹_4†óC·?ß7É—Ãú‚¼P1Z LëF-j&3A„iìŒ ¼>¥ØKµó×È‚¤c´Œ¢c1ULífQ1 [Ü¿¡ÿœ:‡aV3(sÕ ’z—h²’M¼Xi= Í¿ÚÊ¥!œ°¬Óà˞ìlí¬Í½Fñ€H*Ô-Ô–}nÓжkÑøÊHÆ;O˜N®¸Dïœ!ýÍMò¼L “›ÍªNæ`Uè{4mž×<ägÃĽ„—Ž\P\-5½²ÑO³÷7‹òê+ª„øÂGi) Nr?†~±#bÚéöÚ³«íJ³*íFò°Zeë'Ú¨}Åçàx påt hþå6VLälö\ÑQ¨`²¦ùM‡Ç&BÒï´³ ·†d²Ä«qÜgåûB6¢ØäŸ©ƒ‘á? ¤Çñ è[ó²àìÃ(ù’'áýô›;íKÍJ´^˜È§Åê ¶oDˆˆ›xö»ì ¾”h{eÍšfw1‹;¨§ÊµºúÏåPšƒ`É6&#´xè†À#ý¿íwŒÀ `6‡ÙùöSã Ñ'W„T@É&똲]ú)ȆD4mŒð¿¼nÓáýø¦áö[åF`zºý¥ãFÑÍòšÍˆœMÙR9a<’&ög¨ˆM[£)$èhÜ>òGÏ2íZ´c¾†9œ4j=Õg·Á|.¤qsîƒo… îÞ\R U÷·ÑÿJïYÝø4â¿62þм•ÆJs—n*ÀœúÚíî#MÈžÁ­ºÎÄ$9‘·’A ‰¿ï‚[2é«F­]î¦nŸ°™òÅ^pÊ u™‡ì¹5x˜’Ya>7z{§1u•ä2Štg(^>äîc”dD!£Ån˜¦°ðÖ.b´Ã—ŠêëÌËýàM¢ÿì.,t¹É"²ßàhŽuks0’THcf‹ûÅcŽ‚YÜá4,z Y*˜MA?8±+÷ 9—fl$›Íã…c³90f2Ò‡Ô]·í—æ©~‡r©ýó%uBÙÄÔTìMZ]¶DQU\s?´Í'”5àäÞE ç‹6„ArO9¸°µÿ}úÝg‰ÙÁ1îVr;À‘9“X%€ú˜BÂ7z‰m+Š®N–úv¶ÕPif¶Û¡‚mîVÚÌC„JX˜„|»K Œ—‚e‡t:´µÿ¸«ýÈ›;ÊgÌÈvmÏ’•Þ˜ý)X¤k2JVIäÛ:®U‚¨j¼¦8fª§QÙkj­,ÉGRwÊä.kc†‡ æ]L’{8UξD›ÖS¹é(âý¡)ѵ¬Ó²B7k)ü¨Ë¦›_?Ü›p­ð 7mRŸv'hU2«™áBñ÷Š`ÿ:<ÀÃr*Dƒð”5Rr»Ø¤¿Ôq Ë)RTÕnœ6бç¡C ŠèpîˆgïBKpÏùü²éƒe¸ïúÚHBOAtÈ,ÏU« l[˜\_/nó$.˜—¦ ÷Cç•üˆâúwïÝ šA"â ) Å4³A„›)»0Xò’:UØYF;ºöì©¢7F'O±G³Mƒ¸hýLMbFãhh˜*¯^ÅÉ£Ÿ‘ g ƒÏ ¥Ú[/ˆŠ É{zÍÄë•(|•ü½1»Û[ªÒÛXù°=è9r•—2õ¯¡*åÂu¢Ä¬ƒ)“ºÂyÅê¡õßÏ&0„„Á`zX¤X=ë„Ñ;׿uG½ <·\’=P›\žwAÐövJi¶À`!tJ®r)ûà_þÓž9uî^Ü:Ù¬CfÕõH«ÏºzÂýŠQª±±ýañwû}JY¶²cBëâœâ?W„Êr{½µÎI‚VnîèðÛ±É+Ш&qzÑ’éR¶áwñð‡Q*tº•å•ë«ZkU12{n|'–ŸŠ£Ù¬  {ü´Ñüå½üÆ#gÝLŠOšªÃ%.C’["ù¼(âû GÕB z,‹É©bJS²…W»­%Á5`—vbj¢Š hÍLÀc^ƒ­öþ.úŽÄŽñûðÖhˆ-ý‹]¢?ýSZÕÆ³!–¨ÛÓf•VQ¢¯ðD† "1ÄêOÞˆ®ô€¯ƒ7˜QÞ¦ewŸaÔ=Y9ÑËÿä`"_Öí™J{:R»6ml°~ÕúÌI~õdÈ ó¡<Ú̉stùjî ;8ÝØNïÓ×M݇Uf­{±Æß7Ñ‘Z`7 º-ÐyÒ O: $]™¬sY7eÿ¬Øã3e h³à{Ñ¢xtÜ‹ûÚ-šIžÍ‰ÞŸs2Œý¤³hÛ•ßwU)ƒ?Õ‹NA“‰ÑPÒEõÏÁ1G‚pU#Y—©·¡ÑØ®–ܨÑÄIt”‰¶txÉzŒ¨|€Õ-|Ý•lp$´$ôžzšúø$}›Ðœ6ÿmÏ&µ%Á\Ï_u€ yÚÅ^ÊÌÀ˜=„P„Ö¶ËzmóŠdR[Ü*ˆK òùËî%Ì'÷rÁìåÎ#JW ‡ŠC2Ù ;‹†”õdR§æCµ§¹l šØ `m`Ùˆ•wñ×Wjã*ìÓŽ2Ùä:¸WT‚‡Š ‰Ð±¡ß¹œ…<çÒP¢ ª0¡s>ÒÍÚäûTŸ£—ð ×Jò£'w¯¼Ñe¼°N@H³Taÿöõ”Ü3Ùrq| !¼h^íÞ(£»CËFȲôÿ¨êృ®âô„Sß~¢ž±Òê3黼㷖hÀxóôñ{Ìz#3ë~Í»ë‡^WA\™£m–jeÇFÑÕfÍ!ôêŽVCŽwz|•Äeqõ亿sÓá8‡˜”¨ˆŽ¬KC±?Þ ú?Ã+ËÀŪÐ*€:¤;Ðþzè:#=g!oéJ}ÉŸ–¤ZÁîþ¤I£ŸPA ¿°ŒIÞ™_ØBS3Ä…Ž/¬–«Š)s ¿|bšÆ!kQM‚ÞË £cÑ Ý+½˜!/Ëå—Ø)ˆG€Ž¿6÷w¨¡³ò ZP%àš[Tûi_1ç5–³E/GdäIç”9ÂB©­žfOWH‡ýéNKÈjÃ5CÛ¾Ktrpé½±O+OÀÓOSÈ^üK̬ÌTQgW_Ÿû˜ÈóÜ€I=„$r56Ú%D¾qÅ™º1óºè¤PÂ,¼¶,w ð6Ë MGÄôÐ4‰3U½³%ìƒËb4êÈ¥>U]â +}$|71Tn™j‰p±Ï{εJ(¿7 ·Q€åc­(ùìaæoªª“ÌäâÝo”zé‰o¹™ã:ö9ãlAÔlk¤é #{Ôeù2½p²×¾JŒÀ\dÈ#¾øˆ™o©‰§/\¦SÛ…›Ï²¨ßÁO’¥òƒÿpØ4Ô«ì þLMañ‹"',Áó'9qØŽÌøB\ów/6’±×…¶1º‘÷wÊ×<Ö¹PV’³¢4â ç‚'±e¦á‘·ÊÚhÕG‰ñôḛ̂­Að¾’D”À”4‚ËQϾM»èèj@vA|ìv¦½ÃC'ò '|ÀR‰~§E²V_×v´ÃOÎ {ÀÙ—ŸÒ–Ï䓊½¾ÊÁw][ªÓ7§KxvAÐ÷¨åGõŽÿìGáá–†û·² Åuhp ýÚ0qoüEJŽóSúžŠ10Ûï KÕië•´Á&)Íʆ5£ª$ÆN Èÿ¾¶}oQªÎÇÄûtÇ¥£Ý5NÖêêYÞõÓàÄÃÏE£5ØDÏfˆC6L±íä >ƒS95Åã³f!Q2h& ø-SzW|åìÝÿÉöïgµ ú_®5³¡çáÌ’ÕÊ‘Žä¶ŠTØÇSlÜIQŽZˆK–)³‡(gÍ|ùžœ¦C Ê'Ð%ÅD 8ú¿¾ÃÓaÊ5p¥~«3èˆe‰¶T5€b>ëR’ÈÆAÓIÿQYΫì2H+81ÏÙÊ/<Òý¹Þmþá"÷N*WµTO°ºåH<Áˆaâ“ær’! bƒ’šEO¤º¨»”-ûhÒ‡jLŸ´Á¦þrÂ|ŽéŽŒ¿a¿ ã®h§K*@´*Ã'~Jïr 7ºÓû¸!~{9àGþ­byL¶ð«f¥g¬Ê¿)õ ’)ê’T|+Ô7;&ùN@ 5m{ͽ­ÊšT`Þ䮹žÐÒ»u𙞶è7°F „âÞáYÇÅfŽn‹*§j®­ÎHçRùŒÉ ¸¸æ¾‘pz(¹P ¾Òÿö9-Ï©¸,s ”ÜElµ77ë¡#œŸLëKö Z0±gΖ`Ì?•´0?¹áM¤Us"™­Âºåíòçk‚Et±WîE°.3ƈ¶¥B[D¥¯îi¾åŠé¯èzA;毡€Û:õ‚ãÕ¯ZÀó‚!ŒÞ„øÆQžS um6Vš¸^¯Äk`ÿ’IDB-‚ÃQ°fÝJ5–‚qËo¥ÊS™q£}º`š»|¶çUϾ“–™<«“®ó?ôFl>ïþÙOqúmþ§ê>|õ‡LÊd>þf«å{j·j9Òp*¬s·Ëãîu?mKs{§Ûš¬y;®ÊsÖS  A) >Jî p&ÓÊwœò~MŒ#êËü¶Â3Å©'üâÎzë™r >®<°~$b÷¡ÕøBÆ0¦^±%IŸ¸ªÁµêLª\Xô>æDá“o2= }>?0Ñ×mñ{“ùxÿ%hê>b-é=G_3(ãÀßNøÖ·ŽË²Š˜«-lÊŸ/c3aÅ“¸”¬ßœžO•@¿$ýåX¯ÕŒü`ãÜ<¨XJ…:ué%Ól9Љ‰\7ÀLÆ2ÿ•ÿ_ð0²h?ˆ|å¿2ª»Ò5q›(9D: w¨/½^BÖ¼Ú†½äŠ Ý‰QÂS ¬†ÆÕµX?`N™XË€Ïlþ_{Ùu×ï2y )û`Ÿ|½gbñÀVIÄ™RåïàrYé2ur#AÝ’öHýrž×ü…y_ÃuKÁöý&.‘˜ôEñòÔ^ÁlìûРӢgžñùVòË~ûÀüõ­Xì•'eQÁ£&0ŠidZ,RùzAä‰ÌhÊ J¶}ž¾?ª#ç9ëç5@c}°»%(gÊ:í[Áù4‡=9²”åŸëá³e£‘ìYÈNѨCAÇKr9Ò ßJiíb<Ù«~‘3‚ûS2‡¬QÊ-åZH _€G“7 m§É 6e†¿O‘Ü×"=·¨—²ìcàØjȰ¤æo‡ƒ”ß]!?”™3ƒÀÊÒáª_Ú)êçOþ>dmľ3œÝ9ºwTTÈW‰¬xæ­ÐyT~—PÖm?Ów†\ñˆ3‘“š?‹`êyÀ3—‰G´s ÎâXØíú  2#C§Z<¦bãj^'K„µ¸9OfàµìJU¨òmŠ(¬±ë†–Ò ÏñÛY‰órýþ±NÈépl÷2:ì`ÊI‘zûARÄ<¿´D›x¬ Æ4¹¨$¦þU¥æš»VÉ:ó*Uù˜ØcÄL{5Òë7ÏPÛªÀ5,©¥%çòðÔ•ýM…µ}½ªM>çÚS•‰„d5HçQDWÞßqæ<…äšË¤‹šzc˜Vkª»ýÈœgkm×ð˜‚&†Cpóû˜½bhÁ¥ x@ @ì‰ñ×å›ü8Ó5 ³½¥³úx·4(@·ÝÞHÕ®ª°úæ] ¨^¾»ÂÍ#?#¢e}!ç?÷. ùõ̦ûëyïµpïÔ&,)¢r¤X=K®¢,*?;(Ñ¢À¸Ô§$4Ÿð]7Í,†ƒ•Žt?›ÙÃwÛ^_ ¼I¸ñv`Ý“üÁ©ZÑ7z”:í?ñÓ ~²¤LoÏȜ˵çUÎSs|@…hny¹š¹E¿OR_ ª‘öEƒÄœÈ¶M^)`¦òW¾Ø˜{?6¿Óô&®&a8írø(òÁ.ë^q­° õMX‹³Ò)ƒ4ÚqWÖ)ÙâeãâÁb€–q}ÿ¨T®ìÍWË]ôq9•BÁÌòÛH®‡n×Gæ öýÛUŠWý4ÂÀæ?ÕïÏv+î³Ì‚¬Y \ÕCÿMûT¼j!Êñ†“yBÐKŸ­7>ÆV÷ºÞ0TלtoñÅ_lس¬L¿ q^ÿí!ƒ0¸ú1½d¯-©iÜíFjö ¹TeòM¿cŠ«ç#&ç乜x{E·NÚÜÁ„é‚ Y¸¼…n5)1ÚôÖÒ³|øŸo†RôK+=X ­ähËEZ# ±èM]+(ÄÓÌž2ð®.(wt;öeqY™f«É©Æ2ÃÃùµ" ”™½¹ÇíÞÙTŠJôšð«ªÿó uã³… ™‘æóPªÅBÑVÕ_Mê@§„ìcþüWrÅf:+wI,¤Ô’† QöÏ]: %U b€Ö4„è’¯-`øGé!mTÜÓ³â6áøŒÊ+rcù‰3Ý|§MûD UŽœÔŠÙÕ' \“wƉ+µÄƒvV˜bˆéÿx`þñLÿÿ¦ÿÞÀÔÎÜØÅÍÑÞØÅæ°ÃIµendstream endobj 16 0 obj<>endobj 17 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 250 333 555 500 500 1000 833 278 333 333 500 570 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 570 570 570 500 930 722 667 722 722 667 611 778 778 389 500 778 667 944 722 778 611 778 722 556 667 722 722 1000 722 722 667 333 278 333 581 500 333 500 556 444 556 444 333 500 556 278 333 556 278 833 556 500 556 556 444 389 333 556 500 722 500 500 444 394 220 394 520 600 500 500 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 333 500 500 500 500 220 500 333 747 300 500 570 570 747 333 400 570 300 300 333 556 540 250 333 300 330 500 750 750 750 500 722 722 722 722 722 722 1000 722 667 667 667 667 389 389 389 389 722 722 778 778 778 778 778 570 778 722 722 722 722 722 611 556 500 500 500 500 500 500 722 444 444 444 444 444 278 278 278 278 500 556 500 500 500 500 500 570 500 556 556 556 556 500 556 500]endobj 18 0 obj<>stream x¬ºcte]·.³b{Ŷm[;+¶m³‚Š“J*¶mÛªØÛ>y¿oï³ïÝçþ:wÿX«ÑñôÞGGksŽIN¬¤J/ljo ”°·s¡gf`â¨YÚé¥]Œl,M_v8rrQ' ‘‹¥½˜‘  4ˆM,,fnnn8r€¨½ƒ§“¥¹… €J]Eƒš––î¿(ÿˆŒ=ÿ“ó¥élin øZ¸mìlv._ÿ׊ª@ ÀÅ0³´D•´¤$T’ êI ÐÉÈ äjüO,r–&@;g 5ÀÌÞ ðEøg0±·3µü'4g†/'„Fg ‰å—ÐÃèð‹àt²µtvþZ,æNFv._gàb°´3±q5ýÇ/ºÙ×~88ÙIØ~ñ¾À”ì]œMœ,\_V•Ä$þí§‹…‘Ë?¶-¿Ø{³/IS{×Îâ Ëè˜/®‹‘¥3ÀèñE´¦–Î6Fž_¶¿Àœ,ÿ冫³¥ùy@pš9™Ú¿`¾°ÿ9ÿŠóÕÿŒÞÈÁÁÆó_Úöÿ’úß>Xº8mÌà˜Y¾lš¸|Ù6·´ƒcü§J¤íÌìÌLÿ¦›º:ü'Ï èô¯¢ú§f¨¿œ02µ·³ñ˜Íàì]¾@õ—eÀÿX’ÿRü?’àÿ‘ôþÿKîÏѵ,ÕÿcùÕÏÿZÂÕÆFÁÈö«þ5]*ö¶Fv€ϘÿCÖÈÖÒÆóÿþïbÀÏ ¹«‘Ógÿ[ØÎük(Ð3³3°ÿ»-%,=€¦J–.&3#›¯ ð¯úT·3:ÙXÚ¿úõ_ãàK‰é«Æÿ©ÝÿÍS³°4±¶ûj+û¿Y@;ÓÿVçÿ4È¿\güÑá(}u³‹š§ðÀòö_£ã?6ÿhŠˆØ{¼é™9¸ô,œL_]öÕgÜ,l¾ÿVþÄüoÿÑ•7rq²ôè01011¾þÿó÷_;½ÿ#ngboúO÷«ºÙ™~ŒÿMø¸-]Òb_Ѳ0q³rþË–‰«“Ó×ú×Pø î?öÿ¾@ Ðî8vÙ0¿îçyÝ2ƒ¤_G‡ýÜê7{Ò°­ÚX»~2íãÒFȵ™l¤e–KƒÓæ¯8Ûgò‡ˆãë!҈sšœOZÚMC¸= … 3azEâ!™A’Î7u°çþÛ-Z^@Ò<_æËùÜBNdY[ Ë® CÂo%åll‹@;±†m÷Ÿ üžLÔÙv„4:Zz£~è°‘S–ŸT>³Ö½ðð´b,Ñ.@t„5!ól£Pl[³b Ê› lÒ!'/LŒ­‘ªÉ\ÿ4Æ€¿O‹ö°¸.Ù´Y’ü,36‰/k‰÷Éê£al!âí3Ë[{œ£¯+ÝÊ—fÛ Ú±–dÿœ¿¬é€£5øíÆ_ùÇд³%ð…u;èÆãão´ÄrÝwµª€o Ôö½Âs»µÉÇ©M'‡)¶ŽŽ,¡·““Ñ¿ŒÒµeøf¼™&)WD5Ñ+§†Oªƒ*oTîÞxÂVÓ63bìõ*Ñš.‹N›D}Ú^âä™xÆìn3£|唼ԷúBoòæUïâmü ß´Œ{ÅØùä_^Öþ˜»CÊ?rjØíçNÕÊ('yªÞã–ÁÈâ5gÊTFU›S~R7ûÑ*¤Ûjzm£Fñ`“É ®áqÊ=V´¢£ç—Ô8EñÙÄ“ÐÇùû¼n¬ŒïÎãsEb§Ç½'ïºRu´Â¾¾Í·ñχ˵QÄ•¡D¹¯¼JáÔ…ô&A75OÛ<Ùdªê«`ó Vu#uš¼òîó¯ý2êÙìí3œÇƒº‡”}¦Éwo€èúÏ=Šƒ&ŸâS qš ‘HL™°¡½VuØ œ²éGè’pCíp…!‚‹Pɬ)Ÿ5UÁ²€%òk­>ÿ0áÎaà# {gG=F>¾Y8 u1¢ÿ´‘T$û{“Ó™ÕïÑT)qCÒLì<ﲢߟüÆ”[„11×uè9ÕyÆ×ÑÚGÒÃ¥–¶rŽÕ™t>…’xØíé7Unê˜/à­áß›·ÜàfZŸBi;ÌÃR>³~ú˜Ö%åLÚ®c‡êm›KÖÝß¼A(?ü*B>³èFLäŸæhôeI¼I¸“g,jìòWîãÛT×}èžÎž†Dòkw3üÄš´Í£ ¶O!q~3Wé§ØÆ… Ùn¶ÒA׃CÓG3Ë/Qd Ÿp–ËCcˆìqÐvìÛ&ð²9IMÒå+ñ»9ù&z4&úT6Í,ûÛ,’«`⮘ ˜©eoÓ¨2·=T/^—¦Op?ÕiiªŒ@U‡LVçjÌìz«0!¾·&ÄØJI4&ÃWn¶¦@æÝU*Ø›]±øÅjK„_¬Ciu8ÛÿSš~µcˆÇKyW@âÛÒªˆ€¡(I‹#"õ⾘¤§ýÞ‰ÄØ·ðŒb®w ë‚Ãç¿Dœ"™]ù¼¡UMe^eCå‹ùl]]¤}÷jz"½Êh¹¨™K&³§#Nê¡W\ý„®&ö{uu£öF[‡ EPJ“*…ÓeŽb´þµûx‹ÞWë]¾ G¨‚HµäRªE„H7µI“ }Ǫ_Ó«ïל‚ iéÏ¡÷í­€ªvÝÒ†ÔÊ‹ŒÍ‘ñ@™E Cž¢;3–›‡×¦€v¤šðm­^'&´U¨Oaì³åÃa¤Â¨o;t7Œ±`]ЕYßIWUÅvþ:Å@õ¿]vÇðÙÏ¥\XeB„" èB©F‚݆žù5¢%_ÚQî¯Tã?ÕM¨3`ˆ÷éR'~O,×RïS¾{µ}h»æmIµÏ:Mü¤5<#¦0¨V:Ë +/š²oâw€%%`bí2šµ/ý}OvM Øo¾oT~ìÜ,¯æÒ©ùlbÓñàøÉ„(m´/(àÂÞüS#gOy%uUeRÜjSµ$›Å9ühWô›ûTj…{}ÔA;ø^‘ße|tEÍÎФ¤±1)½YÁ{Å+bF÷**ö†±ŽéÛÁB9$\~®ßT¯y¥S“Çá» ië÷ñ€øÌÔaöüòˆ5æ’Z9š!ßÜA,Å_ûxFŒô0½nå¢k»PÜ»žÙµ‡Ö†¼0·2Ä¢ e^ ø/§Ø©1Å«»V&A ~5l¼@É·©,çõ}ìõ¶ÀÙ\Á©p–M5¡Ï4v(¶<ò!©ÀuHéÝŒ ††êô4ûweƒ.ùa¤¿9~7•µ¯ à\îv–}£Ý `½[V}˜@?ü¥­E 4ÞiR!WFrÂt»%oÝðéÀ3߇ ›T$f‡¨™`ù¡–î5Þpuï¾(j:_¸Ö ]ÇŸõNq²…Ó­2{êvÍ •ì&c•ƒjä­xE­œË…èHz©\uù’ l¥‚Ø<¼û)<µÉô£4º"ñ`ý}8…ŠË˜gZ(Ø®^ç).«“~„Ú$¶@Ž€(XÇ»'i1L<ý‘Ʀ_‡Êi‘• ·:§R-ì+*lÐcØkIEd?æ)¼<ÁU“ý¡Ï·r‚úƒ®%VŒùWÛÉcð~ ?åÐ IÅ‘*ÝÑ7oêÏÖp 1:ôäxÏ3mö Ö!U={(µ•×cÝÌR Ç‹z<쮆ïàùiÛšÐ&®`’eÒyñ¨Lšž»LeÚ½.¤Å½Þ]ÁÕßP‘Nr½ßó~ߘMѲEP ìKÁîPP=ˆXt¦ößÙ“¥ôk@‘˜èÌw ûl †NŠTÈ¡æ¤ðäâ ¥¤5?ŒD0¬þ1rý“mÌ Šˆ°C³hQZæHÈ<èæáYf»ÚñJÒåwû'€lÌ%o=¦ënR¬… ÄžõE¼ºK]Ê¢Úíé¬`+yÛqÊÔd¼§tGWcÝõ×Z+14<¾JqÔ;ÛµF|PÚ©jQ%(=òaDð·ß—š3ªÆÝl:ôr)[’xQå½@Nåç¥uüÜz0u=~9É0ýù«êŽ•ó3| /$%×0‰Ú~~ÀlסÃÞIÛâî›$f=dȨ )NêZ=µP&V Jm%Š1˜ÜIo8¨¸’Œ1 0~ú¹G>“!Þ9ªö`Ú)gúÔ:nûÆ)W‘ß„Æ`)nDª®´ ù´½Ú¥ˆò§Ê!ׂþæTT¢èÞ¬YêàgÇéÀ@gÙù;C·¯ÔZ׊rWÊΑq^kzVZvÏdSnø`œ›ÐI4‹XÙ§[i‹)›þìÀ©C6d™~ϼUѺ  ^[¶T§Ou‚ôÛº öч²N,"úo¨»Š%ÿl9ùQQ –eo š)tU‚‚S2 b¡¶«3!~bÃ`5›üINØ-^æ\µûµd(ð'Kît7–Þ‚;è|½!£npmY]å›l޶‹mÁL@øNBœÆa®çÚÙó%šNÔ¿õ`©ÆrÊh òû^QmÛwfDsÓ[ì•b$u17áy*|ì寸qSöß÷Ço}M‚a~nhp!0ÐqñC?^³¹H÷Yÿ%{0ÅÛÈÏéSÉùt1£±”¾Åoa”ïrˆ nK'’ôºOX`:oQÆ2çBÝÆoaöZk ž‘ˆ ¦nïqãoS¡߸>é;ßvÖÓé]WécsÊa¼¡Ö²EF+‹íñܘ-ýMs‹ß®53\vôè*¢8à øØq2a¯7¢ç–/Úôâ‹”6cÏ6EKÆáä&]´šÓ[(ŒKØW²ì¨Éç<_´9GAÑ~ôm Ð|7¼ÏžßŒþ¦¸ÂSóºöÖJÎ8±Ê7WÚ›4ŽH×FgW‡ÂñÆ·²û¾W@’ª¶z*1µ¬|¬6‚K°lÄ­\6m6Ë —`AÐ(ö0vh¶²CêB. ó 5ç æp#ˆ? ÐL}¡DFöfó-@[`ÈR‚ zæG[Ëá¢ëO|MûÄ‹¨VYèüÉ/”öVmP^vrœµîy‡ ÕÜ¿Ìgg ¦ê*:´—¤vþZ1¸w,íͼ¾ÿGBx¶@þ™µòs³úxôeÙA/öe§ÀF¾ 65«¡WpÃã|w¦ÎpNƒéÉsÚ¼ø®\b½*b[³œeÏ1~œkDº…2*ðJ­#FÐ2 8ÝÜž¤ƒ‹âý©¤)jr^‡è¢C1ÉXº< PÐ…Úö!7ÃÝ.À-¿VcÚ®où{ %îÛŸõ\Ñ5!œŽ@ "øs÷ó ^¾ÙMvr{‚§¿Ü+¸wåö+ÿÑ'Œ}V ø:îHÏŠÔëmjcÔ›•'ƒµ‹4ŠUÐ ¤ݸÊ_9¥ëDŒ”Éøšû÷ð?lãôýžq{+¸ØÇ}¸h¤0²7åß:œZhöÏ7æu6¸ÜÃ2:~£þÒï“Jþ<[­[ )[U2"ê¹Ñpä7À÷Tùç¡ÛÓû›U]Ônê2§ªkå4«ëKÏiE¿Ö‘Š/ÔË(sÕ-8Ë+µïè/ZŠ'a¬ïßÕMfmŒtÜÅ2õ c¬Mðy6”à‡öˆ u[íûº5äZU-òd=ÒÈé IÄêjÀ/ÿ€‰$Ø1ÚBõ]ßܨÖÍš[jš¨Y˜?pœ<´î•œ5¸¢²I£ˆ;0÷~ñ²4YW1Fm²l³ª¾B{KdåGÄq—¢ãðBdÆ®¼l {N¿„ˆyX¸PpÏ÷MÊJ¬U¥ºá=††Wø=c0J’ðßÛ4[{DËÇ)ÌO¡7rÅìT'1oš@Ü|œþHß—Co/äLŠ·u9ˆ2C`3†Vf9äl½a—’ËîŠß®Ìa¯K)ØÀ¡ý™lìlJ“&«#º¥{4Ÿm¯èfäœHÊÿþý‘ Š ¤ÎsE$¸}KV‹åä®3œ8™+ksT8y€G©ïÅîëðæ_ú„³°8¶j£0Ï\ãl…51Jì…D;›oYôo€ÅNÎõÆ`¶ò'm–¤ï1vÓ¿jÀ³ Hž™e>DB‡™ÙÑxðC8–Q©I£…`M ó˜vª[Z¨CS¸?îN??Û]LAxðöÿzT[|® [²wÏÕ7Ë=¼Ä"á$ üIeõœ‹ÍØ0ùœ8®¼ÆÌ"¢šˆdIÏ2›îñÖRÄM âÆàÚ¶(/ ©Ðeö—ºqà²PÀ“€eìºô@Wä$Â,ð 'â‰l@{K? ì°¥Ã}z$¿.¦'¿”¯®÷CK &šÓÎð™†ÆØ®`­·<}Á+ïå‘=ǸÑ99®fâÙ*T¿Îý;¸‡…'œdeVKDo½?°¦›fS93c(ã /ðÞþÓ–ÞÅž¦/‰å7–˘ÄEöbëSÃGÓrAÙ:'!GÍa‹ÇŸC‘8¯½f|®Ë‘˜milr˜.Z (B5”KÎ…9œ#S'Ò¯–Âv‡º çʾgÿðÁÄäo6h<Êžm) ´ {@âVñ¨óMá×'¶ŒªûW _‹cI$pÿ Œ¿+ï Nï‹üRwW¨°¤frÝÔ¨K¡„$†$Óà‹–Î1\ì I½V~ ìÓ}rTˆqºo[ÇáÈËÌÅ>×õðÇg¢Pº>Fp׸M®½Ó@~žm_´s¬S´H8ð ã~K¸ÐÒ­@`_oRIRzgÃh<2 ä/¥pÜ(”(n=üâ÷\‚íiî|RLo¹êÊËzOq3–Ãw\²¿ê–fG®œ©vÊěԠÛЗWTýb ,©¢"eꉾ–ª·W`Cíž#,,Á 0ÏÊ¡âí¯”*M0KVà'±ŸŒÀQR’?£Pw¡Hæu—ÐCĘÉ#%¿§¥X 2qfrÞ—ylE2úGåCÈãÕ=z>~,¿ÊçvÜ¿÷ŽëýîµÂ)’XyHî‰ÝŠãË”E&cú¯º1c,HèÒóÛ(Ô!€YåáZÂùÅ}Ö°^¢nÊ9(ÍÅß‚òŒ;ßx`?IR !QéxÛS@FS‚{ºêö¶öWIÓÛú°\CŸ›_^#Uð=ªî|w,e…ÂÀsÙÞùôR3f«MÊK¡›˜=áF´lp).Ä…0³¾p’&× ", ÅUŠŠÕ髨dÇÐ YPE«õyS¼Mœ®=]ŠàÕhÖ–˜«ÐDeå§Ä‚ŽÛf§äš*©pp>[wÌ&ääQJcÚM´°hxÍqàÞó]ªžIë¶¶Æ"2S¥9!HȉòTÆvŠ=Ül3F_‘Ÿý(³h-´%@‘59H€ƒ¢OWöbïÇÆœå¹ßŒÌ(HHû&a-ÐÆiâIª`ž2œÃ.I·›\?Më“6+ÙiOû‰Ý(ÍïÇÍ—ÚIÃ]‚ö&•Ðó–/Óå—À3=œz Zâ>BÆp±åÒÛRHµ˜³¬ 3¹XÅõ¢—ÊI¤¬øÒ[†·'ëñú K$Vþýž-þÒ;à Õ^‡”²P×K‰\¼ý[µü°Ê_äj¿<Ý|2¥@h(§Œ†Ñ¶“ö<ÂMø=µ"""k?Ã8(“96¿`ºø’Žò5 ™fƒÏ?_òÙ7¦¶Ê(«¬;ŒcmWÎél)Vyú\/¸ï(»ÛôÂðhÏë[k‰YH#viÍN¦ °„¿y|_jBw^$¦ „¬]¶ˆƒ½§ÞkÊŒ¤)üþaÚ¦¹Æ`¸V Ýq`K¶É€}ŒÝt€p:Í/ô53‚`•ªjþäxÀD\£x*vˆ-à-ÈxbAv¬9Câ;¿ÕªjC f1“¥U|<¼TØ V‘'(z‚“ëp»Ð¤ŸCH’õØÚœi|É¥q´0cÎ¥ÑÒ¼]†lóv9 ¹xÓ¤ã´z]óÔK±O(r ¨‚rwa­žå(¢‚B,Þ9X&Ði.fáqþòt!”¿^Z„qŒLBnå,Q&©V]`8£ü{¤Ó’„T^ø-É[)ìç5Ÿ{n ÂdØß!â¨)›Y§¦®,¤+”zÄ0ßLÕ™€„Õx-²1Iv´!AK~aÆî(Lëw°;àl¥,€±¬Ft‚’±z rI]ÍÓ€ëƒñOãð Væh$ÇZ)Oææ£d”C4ÃÝ»‘¦»T´¾X¸Àûï$.xmˆ‚¤ÎÏv ú;›(@|?ºê˜½ꂹ=/®áú_x¶mA ‘X½RñÝÔJ!wŸŒÎ$¶Ä®±¢Q‘û!¥ÂÝP[15 É%Q¦ä»~Í!RRي犪¹ËRÐÐ$ éx¡@âø–KXEÔ|Hƒ÷ÄQa73m›V»"0âµÀÂÅÿ»§U¼¨ˆ²ÿ²znì”9ÚƒŠTy5t"¨[‘Ž®YŽ'‡.Ÿ2Å;ô˜0hØÝ¸Â‹YNÇ &阊[$‘†Nçœk¨R•¹ðåyúôÀ¾Âb½[Dî®"z龂le}"yî06¨çA⦷ÓÅ“•O­u¡[¸_‡Æ¢P·°UسÛFz \V2 ™Jo|ì7ctõ¼"\ªì†ØOŠÛº!» „&o$þÅò{ÝÎH‘¡òÄNÁ9MËö|©IÔ?Ø[ž‚?‡Úë͆!8+1’ý&¦‹ ¼q Ð}pu>ÌPc‹ég/á=cޝ=¼šE—‰® lå#W[·Ù¿×…Ì™Më2K7gçI!;Ø¢KÉ.&ÊÖ uã&¹ü„4TœKÖ›¼â†ÌCÀ‰i+Eâ½c0Ó¸°<™§ÜÊ­j\x½¹Ð E´±y>;CëÖÐlÍ‘K¨<îbÂya±ZI|reþÞ/î³Lv‰£š†¶ea¿‘P\[¶zªƒ¶3A›áþn͆ï+ššƒ/qd¿MX›$b‚þÍÊ¡ Âë ë!iSÈ7Z¹ÿ®ÏY¨Þy Ô" ýGžuF=Ö'C|ÙŸ¹øÜ¢{†Ø‰…Ø?k· 9­Ú^¸hÍvA`­³Ú¤ÛðÈh e>ßû<*Ÿ¸X%ÉoCÿr<ÑYKûÙ1Íç8ÒÇT«,,  ^] ×’÷ØhX¹,,m\ÜÚZÓµËÙÇš'x§£ÍþÌÓ¯´Ù’^ƒà€ó¤$D³ zÜ%GÁÜh°êJ„®iY” þÍ4ðm-œ»ó޽`æ€-¾á ¸q¨htÇv°œØx*•$£Ó.skŠÌtIÔ‚*Å%G[GSµÌ;Ó¥¬ÓOëÌœ)oî V’|n¡,çÏRt‘QÈ7)MHr:1úì^—bp‡?óýff—Fækªj½óbï¬ W÷Ä¡þùoÍæ]oÀ:Ñ»Eªkwº¬–B¬‹ÊbyÀ-@1ˆPFÇ)ŸœWR'çÇ=-ŽWý·Õ™g›ó”î So8¯pGYfþt•;Ê€"¨peîµÎX@óå|Uj¦gÖ 6‡'y-ê;c Û£²ªó§µÐÚ_«+s­­²}…*B™lx­ªŒ„³"?Íó„Dýe/^ ËßëXKÎ"€¼MÑ®¼ÁXP%lQñkøD}Ö'†çùý97yänîb¬)b‹Ë´; ½ÜN½¦%ïá=™±Î' ºXÝ£íñ7¦f³è»ŒžßòAóByì$ÜM:¤q{:ÇUÕê™ ­À¨‡õÎÃDÞÙK—*ô±§Ç·EdvûQoim’hõ*bæîžB½]VD5ÆŽ“I¦{]ØeM¥yÓ4($“ÅÄ4ÉOsÛ:ß{{þÖIý—8I“[Óçò¬érB¹‘d3>HPDÚÙŒ×ÎîáÕ´¯» ãuò:ÿ<Ȉ¿’b(”6=éf%Q¬ðR·Ëø‚ÐK•˜4¿¸d†ÝKgQ4] WÎ!3c¾Ùí$lw ¿YVÂê}à+M<€—SÝ÷ã¥Z¹‹èbõp ÝØïu¯ëïtÈ~Ĺú…Å46Ð ¹&_™,EÒƒÚjR¸ Û÷¢gbÚ‰r§ÑFå[ÙµèØÐ¬},‘c;ÇŠ‚ʯ"è¤#’üd÷µµýB’%7¼±Äð±ˆ?8Þ(;5e³&ÇŒ°šëÐʥȎGRè r)´&ÁøŠãñŸ…fã³HŠÞ€¤?áÙæÏòo™ˆ>”]rK6ùøÃf7U§M™¿ÅÖüg)FÔ:¿ž-ãðó1ÇŽ›‚T™2`»ð¢ŒX"¤5oÀìÝÝ"~Qžh³xoj®°±À#YŽ'ÏZ²RµªèÜk¸‰Á§™ŠŒ3ˆÎŸÙU;Q‰ð€ W½ÓF¼|¼ˆFwõ³¯²íÀZæ´O¯nÜÞr°Ày Æl‹ÉB¨ÚÄ?ò(þJo_!Þv8„ê"¼Ï[Yîymýv¢-â¢åj wì˜xÆ{ÙÊÄÝ)Têv êóGòlæêö•šR9¾.,cØèÿ‘yªØœeÅš^$øó¼BEök'P‰Ya¬`7áí°Ý`†<Áãàš“è0À?8¨=áÉRs©w,üY±™Ôæ³×cÊùÑ¿ã8ž4oïûw4”YXÖüy0ÐVá'ãß¼TëÍ’]{¾Ö"fnUwá’TWïû¿-—X~÷ðˆß[SÎMšT×TD‘›4Mì²BýŽfòB]„&ªÒI¾ßd) Ͳ‰”äìþ›ÏÒÒGd·œ_=Ž4µËó“ÚDѪƒ3šòÂË-¶>zàr’cÿ6°ÏIŸ•õÅR…|g³úõ×´Ä,YR˜©+E} ‡=.Îf[*Å£¾G½›ž~‚žË!¯„쨵 Š1ës§EéŒ\C›DNßõðy½Þ¥Úaïatl0%w`Ä=\hCÏ¥¿Ê4a¹ÈÐØCM&ý­×ö:Š ê™ÒPí(]êñ%§æHM”™š4sb1¡Û1Œ5’ähU“ zdoCîÙïãVY!¡%Á님Cί×CŽËwQØT•» °õT‰ ScöNüÍÿ$Ý„=3­Â=1 ºñ@Εrm¼ªüðØÍoÉ÷šq;.À¹Š –âÊfRa“¶…Ã¥Ö˜ÿü­žg+øÃ*[_[8ÔáUq\õó—Ÿr»2«‚Œ«Ó5‘mŸYÚ‰öž‘;Ész™„A …íµ5ÕB*³>rË*qÏ¥Ü-¯ Ì¶/ T˜}~ôH=_/oDÎÇRÑ$CG¹Èls²|/¤ "Pjíìº pò¹f€p’‰CŠÿŒWŒS¦ï‘<ù¡P•n•G€³UºÁ¨åμ;ˆ…ˆý¹Nd‚VÃ@è5)S$…µ}½ a,°Ç·¤J'³ð²ö:róî)W³=êôêjm1¡ëÌÜ™/rÎó|Ìâóµs)xÕìè$ž±û÷PsxFgex$•9ˆ îü÷ƒÀ,‚¯‹Ìa:´ ý˜ÔÆ`zÏhÈ!ÁÐРb5w†r›)Óг&†ÊHç*÷zZ†Àú;ÚdU|­FF¨:C[’PºßœÖ3}·´’âÔÆEÈÎäz ­ìûkB`nÎpê¤Ëï™)ÅŠ)öóªºròM¨ÎXNkéVËÈ?–?ß­ÆZ»¦çü,økUñ§paJíhS0„ô3¹A °¬~Á¶qåö0݈µá72R”>]ž¢™Áû…‘Tªâþtžà>*ªïÞ¢¥¦n¦¼šKŸÎkèðéÚiÎÉ='ú¼MüE‘žþ9@ ³K(Ûžò:5²HÆÁÙlPµiˆ-êXËÆJÄ["¿MƒÑI´;¼w9þ®ä…³È{ŠÅØWå&ˆjÆÞ&ºVÔ¼!&ŠWÂòæ[ÕX`èøN¼þ€¨›o188Ý¡øÓ3Q€¨·ÐGØm‹ì½û2ß‹‰ûÓ÷ùþôݧ1Uû¹)ªÓéçÊ‹Âíëì|Ž –¡!y}îY<œ<ÅO>” äl`ôœÇ_· {¢U^¼¼ Aí~O/ç¹Çò“JQωŒ±9ÐDJä·f ®‘KL³±ha‚Àɪy¤Ü=QvF[H¬µêqõî4fs¦‡éÖ“*ÏÀõ‰–ØÄF`Xé-WÛK ͽ㛴;­ÅTI‰º¾Ò^ýÛ°ŠÂjh!ôš¨.ä:àvÂËÅTð<_‹° Mjö݃%ʼì…{qG57z  Lº—¢ †ÐåÜÅtjâ§.ùø7ý^WQüÚ&§´’ÅÍ´Á<J®_„á¶{36}SÓ.ðÿÄÛ»=rþI*·j*ó&ÒJ|íõ¥ƒ–N­¸‹Šã¨qPl#$ù…÷‰'¶\˜xTHÒ´íGX¹m§èµQ¥†aã3Ò#t/ž;«Ð[¯cä¯ÿäž‚#êÆøZ)„뤪 ±•nÎÚ!Æ4›×¾–À08éCeââ&€…Ͳ +:§öaÚ…©žýkÌ[Bèÿ[I_>¡¡ÈÚd'Ng“YN…®²fØ‹M‰&±ØòÄ‘¿ôpª¶ŒDÎâ…Iµ¹šÖÑ*›SZ>š?ZÓlOºÚUŽ»c+Š’÷ÛlR¬¸ýahÊMMUÂoT/º»šqº2çEèÆâƒ–ª–;¹ŸáV­½›ÄÒNrQ¯àÊóùh6)†…-g·Ť7isÁ½=÷l!Ç>µk¥#•í"¾÷w=^tªh˜ÄÃÝò’Íi À5 ~ì2Å?ŸX¶Ñ¨Ü“áú«HGƒÅ¿ZmÏ9Þ‚õHA“€÷`Z§ÝêÛÛíÊrêû¨uî€FU†câ S”v-ËfÌêú«ð ù¥U2’ƒ›Ý|ÃÁTzj@S`̦n°/¤]'y–ÍP‰„똘ùµ?ûÖÓ\ˆ´kÒ~p¡ŠÑÉ£Á¤lý7¯ q"@ {sAêªÓ õp®‘Ö†a:Ö¯j‰Ô¼V*Åfô¢ìž¢4ñ7LÌ3˜—AH• ¢¾Äª—$ïø~0\ãÜ®UP•œ¾j'(hàêhëwq_oÓÝ ’®¦=¬I-%غ)%"}ÖöÐi=´¬U«)=«1,AÑɇaz…I™/¢Ç#S[X£5 á(ýD>9—…(ÙíÂ<çG ªZÉÊr1áè±³·âv{eEîa¼à©‹”&§ /^…]'zo9’ߺ'^ sÑ7k~ zÔéúÆ"«<Œ.8Þš»ßÞ‚y\T`ïÎÊ›õz%ÿ0Ñ] ÷,ùÏ|½.H*l‘¤M7Êè­šx™â‡]ZÝ÷]j–L³9¾ó=ƒ5ÇÎ"ª·„M¦7£~˜NŸ B„,ñZ)ŠÄ£)?"Wp·½<ö¥ KfèY'<;Êo‹,j½f Ð9IÀÐDÃâÍÚ8ïEp•ÑjšóËvi;!LÚÒ+‚“®á—`,„ dý›w9g^NŒ3ýÔí«ùðÙ {R’æ’göðˆë¡Ôï3xGìz2ùSmglˆ˜nI·çÏG&g½¨«Âý qÐÌ¢Oß;O‡ŸbH+µd!›0Å|:ÂHþ½Ë²›ÅžLws߈DQC]¯õx¤Eú ¶ÅÌ}†AŒ,wU¡¶M-LÆ.“Ð$@_¬Ãm-F ð|:R‚·ÏÊP=Å.)C¨"MâP_ŸßZZÃ=ß(q% OÐÌKÆõyþý žörõµf•µ®ïÞëœøé>@öuÕøw2Û£Œ*}ò§öóH’¡ïÓÁB0QøÔCª1SsëÜLZFÍ0ü/ÖWJ$CÓ/¯ˆ£‹fÓcÁÊ™|öJÏWÿ ›|ú?]¨\éV¿„Š]\TÇ<öCutáó?Š(FÉÐÒ5›ñ£å_ÐîË{É€‰Ïð^Ôoœü<šVÊÈo‚!% ÔÙÝLMH×.e¦ÌøÓÅÁÚYµ·´öxÅ5×$æ™êË#omCaÅ Ve¯Šp~ÄpðçTË7°œâéy«û÷¿jˆsç ÛZ!ïi#§p‰û|wói…囡fN¢Ýõ‘Ï•Ñ\/~äÑ@ýHaŸXÑbÖ;œúŽ$fÙaãg-Çs!,$MA˜¹ûhݸÚ"g«^ÂJ  6÷ƒ%˜RÁŸ}Å!<Ì'G¾Ç)·ó u´2¾¥ÌJð`h.Æ"s½Îï3ÝB5 q'¿rõ3ƒL¼u1ÙÂ8õ»]v¦W»\ÅìøÍ õÖw~37þYìØöõWÞCY†O(¯×ÕjŽeð;\{0Wüьݟ}Ùᚸ*“ MZÞwÜø¤dˆ)AêO†vKùfœýµâ'0"â&‚?(Ô•²%¢×ƒuÈW¶ÃbY>›N¥´HJœ®FZ›ôN¨ÒŠÂAw…h(µ$)¤U|%ÕO»ö'¨¶Ù™ÁÑ,ȯܕÂñdMÖ!bãcpá7dšƒaEùuHÍBu˜’)Z iü²õÒ]ÈíÚ§RÒýŠÊèWû4¸PoH_Ð̆þAx“nb“¦{w9a>“‡L¾ÏF¯èßœ–vüŒín’ß%ÖxÎTJ#5ßÚwdò¹˜“â’7}oKT,í?Ú›Ú°æ«eÇŸaË%ª49ãç-3æ|m._­¡©NÞ#Ò{Y6YdVDé_`>ë$‡‹÷YÁóhbx–q«vÔp›/IP÷M›k‹ùÛœÎ{Ê,‹ÿt¿š>û›wœbY /`ÝîÆaÍl‰8÷ 7?#˜;^ƒ·beÛÏ$þJåV{)q°A"C:g‹iíÝ×+ªãA,í mö¿E Wç]–êhĺ”Î;´ãb%Î=ŸhûXþpѱ Rø<–£§·£c'z.4ÑQ{’=Òñø äˆ*½uнS\h¸*];ÌÄ\(ùÁ,à ¥ÐØ-ô+£^I£±f1ùÝH¤{€o!XL2ΞKJº¼ò ³ëBÄ4-­›ÂŸÞ•mPÊTêm lV¹k´ ¯_€Ôx±d?ª:ôê uYÅÑ ”˜ÀL«”QÍâ¢ëa;N9\c~ kEAÇÁâ«âui»õ*š±M鲨ÀM;TF¶Q¨©¢åˆ´îNRkŒ2À´Ë{ÍÓÛ%ŒÊõ“~Í4‚Z’ñ5´!ZçÖ^ÞÔó”Àé0Ñ…›4BÁS© W"$P¡ÊýÓ8Q2¬8ƯE%ë’·Ýû a·zð‘Rdͱˆš3R® ÁC³ž÷$Öç-âbó¤yw-=숢i%VëÛ.fÅ’Âz8ëI .zQãgra‚ÁõÛhmôÖ¸v¶lç6C!ÃØs,Èû;–²åoÎlt‚ ÷Åxû(dÊ¥ ×_ó͢™™lÒ·¥t¿,åúw×hÕ”šIÈóô°» y‚JãaèTm üÂÕµÌR¼qC‘[ÏRéOï÷nn¥ ‘”‘æY¥h–o‹ž Ã7Ш£ T¤ÌqÈâp|Ïp GFÐßã x¤Ï5gNÂ[fì‹û¦‰÷EPñ°¦¯"=Ëü¼Ja™—s¶=ŽÙ¬ú°Õ~„ü¥kÿ®æß`¹¶U®]eâÂÞ‰7ÞL>±Ä€%X7á¢çM¦qA6ú{¸3_Ãõèx¸s"ž4ÊFn4:pĆm‡¿&·J¥*K†˜:ðC‡šK—ÛC¡Øelî¬@sãZõjH÷eïUÁ’ ‚–ÇÛñyºßi ÿ]OŠè XÆt©d@fO¬w>®˜ÿ=æ÷\Ju¨ÊšòìÕëòàˆ~V© $°Ò¿eR¢w|íÁîR‰’µ_æˆ;í5BpŒ‘U±˜žÔrrÌiåá~ðŒË=[g $*nü üÉL‡1/ >ªÓày†ÂÂ>©/;4G{mA[V4¿‹·C¦(¼öLK*ˆË68|Ln°˜Hˆ‚fÙCS±¦Òƒ‰Wª;Õ ¹ûþ2ŽmŒåPFìM=Q‚‚–âräÍmåzŸWÛ‚‘Hý³G²ffyäÐú—®®³&à £JüMw™$vŸ…J-$Á~‘pî$ ³úJC¢Ý™(òëA»"µ©,=DÐO)®j²À:HA?Ð7ÖŠ)ôÉWÎeÍîON¯áݶóõž;ªNfƒ]tàEƒÜ˜8‰Ùãq¿ø ‘«UÉýUì4 ºJÎåXò5Ù+$] •70cmgMšˆ? õÇE^Ÿ¸._¼k€ò®n5Ô‡‘Ì£é§C½Ìݱy )îÒ@¡4£‘MŠæ€%Á_ë‰7½8Ê5Ì£æ¢\¯ã1÷4Í|‚VÒ ñö±>t‚OÿM 7x[ÆþË Ê}ùz¬áãÑÖ -„ŽËZE)CtÕÝY£«X(c ìPüBæ6äíìºtX©U:R² ¹jÞ?å½àøþA3ÖÄß•Â*ï5åœL;%ѧ7"“E;p¹œ/Ö´©â'ÔÆdR¤“ÇíÈ’Æ;¦ŽFqw‡íGÍ¡F,…¬‰z·»ÙRåJhg ÿ={V«Àn(Ó®ðçW²£›ûH˦‚¥UØ-óYU æ•0g5·vc?ªfPçùõ;\_—ùªo÷1ŠÖIºa0þ…P’Ï_oÄßVö3ž«XÞ ”óQ 8²›smR evÝ^\ûOä'f]b j9uu ú!ÀÓlwEbÊÚSþß/45J8^ÕyCïšl—7Îü¼Ñèé¿X”YÙ¥åçÏ’Ø ³‚˜!R‰6ç(¿7.½…Ÿ€ÔüúŸÛõ{nûÐ=WæI ´UÂç)ŠO Ù8¾gÓ,œžrÙ"°A« W仞oîü³V˜ódDrœL&#óu…ƒ<Ò¶À-»©ô‰XÇ‚fŒƒn4—J«GO°í- G¼>Šs,Du?Ñ—ɶÿxüf™©±Æx•Ž£,›^$™^SmîLf’o­ë ³øͤô Ù•b}ÕèB‰Î,.Eë¡ùCÿÛö»í’Ç]…¡k´€¤™!°q SÏçóER‹«.¯ -ž|Ý2;”$BÆuµR¸—£IÚâ׆a( (d¿}µ&?rKÿ¨ú’£5¶iáR y¢¹¿q,@œ 0àóa¬ª¨ÒO`»åü™Ð°'ôRN_=ì©‘Ô]3¶Ñ^õEÕ¯}¼ÃÄ]Õqç´ØW°¦3®O üÆÉio[Fè+Î¥wêkžb›ãºèš\KI8íAúÞ­?ò[hÅ.ƒ~È çÒ>ð¼Ù9Ô»@=C6/­öð¶s{š`87yç=bo¶g‡qÀü fôØû#ŒK~ãÌ^´ÿ„*.ÅêDƒ¨/bC-'ßC—.)j\aµÂe{Á³5|]B)øƒ†Bç/ ㈋éd¿34x7¥2í jÛ“)h>„‰²ÁeðÐé6šÒ1Ÿ“Ñœ±#s«¾ÞEËSŒÑmrv±CÃõÏäi¸5ä´ >G À›Êdý°¯~)Ãv°› ™"JŠ Æž¯ï·QÇæ%%0@ 9óV± J†PÄnzóˆi¥DYºˆ òŽ­–½³æ`öPN<¤r#–9qQ\HD ¡ƒDìtJaÒ{¤}E³®4l(º3LÄëÃ÷°ÀÌÚ[× #ìÝR˜å—Ù7Šââ}?ÇN§ýÉž\}Äg¨ ÖØ,„× kÍ¥p,M.­ñ<ìTå6¨°g²£ró+”%êzÀô0. ŸU­² OgÂ=Œ»í"è5¦‰êÒ` i§\_}ó »2Ú‹´¿¹‡Ú[ã7ÿ-Q¿²lz #Cò½G\ð8ÅX‡.Ðùj ö]Y¾tt׊ù6ZI³¡Oé}Ëë U`ÈŠâ þÉ:çÀIMçèng…3><ïØÎlEfŽÂ”Æ+Ç·…Αt–ä …Š>´;9§èЮû–§Œr:|¢Çåþ-¦s4aF%¦Ù8àã^”êdÕçO#£r¿¾VV1d´âªKz…¢oOìy6ÉÂÈã"I©8ãåB‡œ1$ã qBºæUÛF»°.BïÎ ÅÉ©oq_‹+¹D~tvp¡±®³çGNF÷´VH]w)p§fèO$ÅëG|°;² 8Ë\eCË^³VÅ×òxm+B`ØZ(ÊÒH…Ü‚.7Wšf‚ü•gÑ,-P„' ʼ7Á²ô’õúQGïñ¼5òÚ¸w ­‘ºsPÒ`þ«­¯Zót®eiÅüü{ò›+ÍkVê¯ds7a®Ü–Gê•âró™þ)/Ù#;óã;ÕëB 鬠÷æ*¶ÓàUôÙ½}•;ñ¹?Z8ÄB-V5÷³ŠÓðãõ‰‰e]j7M‚•¾ÏB£‰ü|c#ZWþVÑ„?ÍÑW4MÒgrGxÒ{{þNˆ»;1:A4ºÎÛbD¸fvÚ>ÙÏx-ÛDAsT*cLÁyúm‹?*§îo¸rº_Êóy–š %‘¯É(Ö…A…Ry,ýê‚3êØ×'—‰ÀäÊô«ÐËà3!õ¥‚ùÑ\«±O¢7¢'M^&e}‡æy65MNzíFg¹;ÄX&ûÔ¾ú:ΡDýo)CôÉèwà҉ݘLÞüˆ¿C½*L©ú­Ib+¬Ôír(á]Mæÿþꘈþ›%_Mg ÿ‰í5o"… ¶^,ûB€öK•Mñ†÷¾ ¯3ë^h¹]x~+Îg°âL‡®Ø¸øïÈ×­ýŸò.?æ®ËzììÑH–!‡OS“èrŒâ¯ˆÏÃö,”F†Âýø<àCJZfÅŒŽ¬bšžª ëo¼´$Äm_Ä›Ø;W¯'5=`9VŸî–¼L)ÝÈäWè!§¤Ý¦pO(ôË´6÷¦:¼a'8ÕÇðÚ`óX—eŸF‘P,føób²ZãA—€)¼9f>2[ –R&ÄÁˆÎº\EX#øPúñþ^Ö¬¬¦»Xe•Ò6ùq¶Æ#…©‰t/:_ê¤l,rÅ*£G@Cqœ†βž(BU{qùþ²G]\k.[Ên¼”³™'ÍHO$ɲt}ºE}'bXPp ¿i4È &”ëJC¿ji:Q rñäاk„,v³‡:ìlÏ%ÄC‚¾Öî>”!£R‰ZˆïêU³=—!]Ä6v±Ï_ÝüѧýF!±ÀmD7Öæ¥ÎZÎ~>•·™zlX‘¨Ê~«“$.okPfÆaN×b2 ¯-êt¯8̆¯g~/Çè ”5ÄÍìQÎÿ–ÙKúu'À£Zåµr§5(¤NŠh“ZÁyWûWè¤ô³ yö—!fd3ï²2fŠ}ôà{„ VXà=_ýe2§%)»—ñÀòé²­³¿t>L£õ>]Zíö«žíÄÞÆËZé„D•ÕÇ8Cî™í#€,‚O_º)ßkiŒ3¿ÓowÚcð¤¦â<˜ñà(au¿ê¤«<¹­Îä…" 0ï𣔠AI›Á¹þ³;M>zŒÞsÀžA$¤£®Ê•oRAeÉCšáʄ߹Çq … áRE>IÂ4ˆ4±‚•&@*zkB‡¶ÃÕhWQB’—â=ÖGΠ±âù"ø¡–¸f_µÓûdJ»©Cô¦yº²¶ÜäÓ)ªiŽæãÇz{^4mÌí©?¦D"ÎÈ/ÜoC[鶇½HnþÖÞ[TÙË_ôÅZ(4ºŒ¶òm™Ön±6LËù)BwQäÜû 4Å@Z5[\8§ðÅ߉÷Ù= ъ׾f£ïmO;i8\.ÌПZ¯±_¦@ëæåsqn•­zâøî$ÆU³æÅÖC=Fiìz(È4ööÄ¥`DË6qÕúöÍæ–SH–÷©vÃ-“%J«Ÿ¯$¶ ©$ܧZ/ÛçÙtHuüË þ£‚{gUܹ%L`mê%€@ÇÙIíîÌ:ÆbˆÏ¼ýSŒÝŠzyš!kô?„âžiÜï5ýP™¶€ÜéZ•å÷QŠ”¿‘Þ…ÐíÛIèw/ðæ~á”;ùT€(™ï<¢S®R* ùÐ0ÂÓ°ÉÕÊbp¶™*·aV “âB?#8‡÷"ƒu ëÙ¼ÌéÐÝY‰˜‹fY‹–‡vÀÝL¥ûÅÞ$)ì >z÷î“¢æp&všO K1;I:¡Ž®¼†ž.´4Û÷,ËôE­”o5¨ƒ¿ßQ“¤K³CV埛¸Ž„GC X™7ÇpÖGœ{B_ýÇîµýsÄ]‘ Íe><æ<_qÚ?BcMrÞõT•{i«}Æ鄪Þ6Qwľi~ÛZƒ\q±ØÇiŒÀòœ`.×GÊY0(ºúR!Åo ySïŸÞHÙR´²ó9Y.n¨å›jæì¥œwåÈ´|c+Ʊ¡´’&1*¿ò»žÉÇ!í]ÕÁ¤”vÆœ®Pݾ ^¹(øÔÙRè¥p©ÇãEü†ÙL¨³“2²/,CÃÖÌÖÁ¬öÙp`a–[6E®2B[ï,µ®gNæ²±½_&¡ç'ü¿¼ƒ„ ){Í)â•É¾ë© IU'KóI«pÊÀ…ÀvÁžÐtARP)¦%ôWe8C ]Î#éD9jIùÙp§¶¬÷š ˜M”›µRNðú)Þ©ÄoVë½éf éòãå€Íøé¹êÏ<0æ–½Óäbü\Ÿyž5ÂÿäZiBÏßpŒ°¤eÐEçYnûA9ËlçÔ\kƒ!$Ø•HŒ#Ö4ÃãáîP\¨¤´:ާlÌ®¸—Ñ`ñ&ײ ’IßÖb ƒï©" rö@K¦6Û¦ˆ‡Öi’ Xn%Y³kÏ+ÒýªöuÛ]zÆõ›€Ü'%{Àzi¿¸i–S±’¼þ«ÂÓH툈'Ü/çà1éMN`ç.‘Ø-¾súm–¦V×úÇl²B¤4ɾÌ)«.©ùüÆð ÝÛÀãÖê™/ø«s^eÚùÙ‰_\LÊ?X¯fÖ 8„gßÌzM0Èðü^)¼AKðkQ!’„íE\šSW*ÙÙѦâùS¼õYÁ)‚‡VmKÐv묶4Z *Þï–åS(d*“ž\ºbÍ"!Öî(ÑPƒä­ùI+ÇÅëî¶í*^(ðýŠ;T5õ`áÛï¯DóÚ÷sa!ÞÝ^œJëç7UbZÑiËBþа€à ÔJžeŒÖ7ÑafØqiKV¯Y(·ÒlÃ`#8ÇOñ;kà-ü»ÛoÙmÃÛ½‚ŸÚreè¬ð® Ç ŽRú£Qg¤ÇÀÇá‘òV 4ÿZr‘Úq6’šµ6ôÖ(PÅ×"á<õº(n$ì½P„7ÚÚ símoBñ-h–±ÈÙ9k$pR*йÀfáR|½“…_Æœòk¨â¦¯ÓdÌÌVäñz;[Íÿ ×`ûBMÞga××"Û‚þ4Û¢NJ®Ÿ¯À$–iÛjD™¤;´ž ÇøÑÇ=“‡kÆWX¦‰ho?&;ÀÈ6we¢‡³ðiIÐ%ZƒÂså‹ÇSº©MÚ„óÀ´â™0°Yk„mqâ-|2Ýj%¯îvèË=PM-%Q.òÏ¡};äꎧåºÅŸ§;H ¦}NÃ+ ‡êÌ ¤NŒ:¿LÁƒ$_s<mº yÈDEM”=Ê»¥nò†Ãbí-l4õÈimÇ`™ÅT}ÀÏ¥€oøÈfž²Þ„·ΕE /ÕÚ5–>Ÿ!”Á´†Bòó• E!Á Ý ü ‘UKiúùïz€úN«£»Tl5wèzÞ“Ôãû´‹ëç¨æhÀµLjçqw>†‡\¤üÆG]7x0¸#JŠUÔDÒ;_!(9Q¶ÆôwuËÕž~½r4ØäYmÚŸ67ç6./ƒä zßëŒß§lêÀj*ÛrçË’ªÇ&¼¨õ7Ú~w?ÇüºÀ5'3}ÈÖ$€Õaê«Wp¾×c³DÆ QòVh·ˆbQUòœ¦Ö«ãOtÞv¶­² ¬æá¾1[`ªÉ3"kÌå2$¼P@»wÒˆÕkÑTókM©G)Œârîhë Ø!ø¹•‚c[Ã!ÂÅÜ/:ò‰fKÍ.ŸßçB;¦'‰ŠNT⿦¼ô"ɤ¤kËBx†pH¥Ä‰¶²(“îBœßHª‹c›Oõh}Ô5àîéW70ÛçømDúeaòÓ+­¦]¿ ë©ÞóÆ?¡×‡i ÊZ^ü%蟔)d¯r‡…ª:¿Z ¨ß/pó[ÜîÚëQMDPÚ x7ÿ7huc‡ÅÕßå¢63‰ª´âÙ¿O'ájþJÃX5ëF›HWQ—. ”p'¼ªCAJÆÔBÉïr³¨êIV ZÃðÏŒjc¿8§cò aYÑUó&{V1äËp8KZ»e…( ®~ïÆ\}(½Ê-2O^žMÍàì;º›võx˶XQ&õ0+r­Uw<|¯ci —NqG_×Çi ¤Hd ¤À¢ä cá9*‰Ù£jü, `²†É~‰O©aTô¶ÜO"phfg˜—(&ô‡b¹[‡`F)³âÌšMLç?PïÐ$‘zðzÉî‰H™¼.=Eòµ[ Z²©oèl™Ñs59Áð:­Ó‡Úu@úðpùGìdçIC}ÿÌÈÖxM‹Ív”䵤ÖåòÖïÛ&D¹ž~T,»nP”I{Á•6^€¥”é J‹OÈ{VWðÿ"w|mΣ{8îSä)ðLïhYÎðÍXv7ª¾—8i–ˆ¡á²»¼žñʰ#òöú°îi?8T–C§aJ½3‚º«Î ¤ä·“X_ôÏg íŽö2õŒŸÉ©Žä¢t?z5ÇL…K<7`c"ûÿñJró¶ØÁ ä딡/R Ç$Ë€\ž(1ÓM&ÅüN¤Gfk‚èNÆEM?#3•G¾¼«2ï>5—ƒ³RT­Ð  áKwÊDJ}qSï¥e)p¥Éö$añãB-x2c×ix¹!ªñH˨±HÑçÓJõº¥Ý.cLˆ%ŒƒÐØ{„ŒbÝw8 IW ¦[­*ÍrÍé7U`¨¢^ÜR)!T¬ú[Âã‡¥ß %Ý“Ònâ µUâE Ú|ˆº´ {#SLâüÑë;ý—ä4›ç}˜u¦á-–$”¼«Xª¥mø*¨'­¶L\ÿèi^4×/ˆ¢¿n‹ƒ±6´ÿð%k¡ŽAf7[Ðá‹·'n‘žœÀr3´ŸüqÜfòú‘í0Ÿ­õÍ•néî5¸®’yùÇÅ.pã‡!Fb¢üõw.¨Ýƒ"l%Ã&CC«—Ù¨ˆÌ€´/ÌÃVÀªó'}Þð3æµaùñ €xASö÷/€ñ×™‚«h ûYjð \}cˆø[“<Ý댒Ùs„õŸ7¼ìŒ†žÖ>ÀO¢«àF£®—U~€T±É;L,ãKûãÂ.+›2£üäBQ›OCÊÂL#¯£!ÊîÒ¢<¶lÓÉËM˜È‘¹§òŠÜRGöy&Ã>°5J}[1]Ÿ¯ÛO Gl ¦ó#‘°zèZ£í¢¯¾9ˆ`%WªŠ¶IÈ“Q‘rÚjÌÀ¦ðð4qa§‚uu ¹—›æ€´³ÕÖ6àà]J°¿õ§ˆ±¾ŠBF©ŽØ* §#ÏÝøvÐÍ/º ÂÖ!jû-¨Ÿ+˜b¡$E6ç‘FUcUSíéÌÒ}èΨaMQ…Ð-êØÿ7—AGjXƒÄ†„:ÛÈCŒüu—ûq—ƒñ¼ÆqÅÝÆl 7ä×G¾Áp”!ðÉÚê‹!Ç¡Ýè¼íúeü®ñºpºû,•ÄdOtŽ÷ªSI‹ì19*‡¥—!›v…þ$`¥ß¯¸èLtdÝZï jƒÖiÉCÇ8÷÷…C~Ôù.®¯>¤ê¼œ,eœ8®œd5ÇÓàíQÀ ?É::öÇοO©çØAÃÚn)rÑ”sØ8ƒ-(Wþ/ò‹G¾HBùg»q¢¥©eèxr`\¶ Ñ)À«‘ÛIÞâ—ÑÙЭ÷l~œKÃPµª-® é¼I”¬¢lâѱ٧—«éÅŽsÁKœ=kó =CZ?÷JZéÒžÀ©ƒwºt;úô›hC7WñÉ<µ{JvWˆºýù-YWS¤ƒ´…L[¾6‹‡?øùp6›vÜÁJã©ÞŽWœöeþ@tâò| ¡´n¸Ÿñäíº“žBù=¹uí›ðNˆî¹ÉT¦¶0-,¯ãp›3|'ñ“R\ c]º})iVCª5W*»]z$Ú±oÙДTÅ6¬0)žÒ«—’N4»[”_ÒXó&¦½?p²€ç[8×IOö>ýO|=©‹%¸ÙÓU¾,e{“ìÁ\Ì‚„œ\ê–ô$q¬[CE¸Kàï¶óN‰¼’'Éf :%?ÀnëÑã­mì0=»]äi·“:ûð¿jT»qk•#\’…CnXhçVx'ºpwFã[¶"¹>:¿¸aétDVÆì^°Î ‹{žÅ’ð—i)­lapžö¨‰™þ±TlÒÉ¾Ì JÐE¡W,F‰C®]ØÔŒZß°’—¯XÒ%Y¿¦ìJÝÔ[àê,³U¥ ó í4™½€p²ƒÎpnüZ²)UTúìÿ85eܲ¥–¶`ñÅ"Ïm7çLRL÷ÈIü#Új]µ’ÙÈAÇ…”ï|Œhü»r-bQÃØ°Õ3Lõ‹%IÍu51}Z3Ñ–÷Äšl+ØW±ö‡†M¦W, ™˜ +´îRj.®$fÆ@ËI '°½´E¥Ùµéïö“Ò>ìG½%{`¨Cªj05Óº`I£¹¨(Z,DÜ6Â6'/ísKDí>Yçi ùí¯ÐÓàR0^[‰FEÞ¯r)¥NHkY†Ð©nöÖœ;wä‹.1ìò5Y5¬¶£nkkLšÛ`XA³ò¬p^ÏïÆ+Ñ"Œr\¦Á7Iö:ð–©ÓòºËÐf ŠðNŒà1YZQi×·ýÈ øÄ¯UœƆ,†%úŒ©ƒñ*û0 ’F2¡WEÙ&¡Í³¶f£‚ )ÕÃZ¨¥ƒƒ ™mÜt©{—½ŒÎ;Rc§Œy¸¡•Œ®áYŽ Jå …‡*® 0¹euPDÝXÍš…8Ìä¤ÏªLÊÕCÛçðig¬WÒ^M-„²p”žOU• ˆt©ÎëaÔnJ.ÈuÉõÝÆôYjH_«ÝµaÑDAÇ]w¤´ÏKäÄxÙbåo¥à³‡6®Ðʲ`ƒË®¹8ò†ê‘Ní8ÒèeöÕzœYcF6²h{@±r™&z c‰°¨Î0åî6ŒõA†ž:éÌ{` ;f¶Ëq¤¥$ý˜³Ù¥~¹-¡jË{qü„-h ¼4‡ÈŒfï1×-½6hÂ…±lÜm…5ÈŸÞÝÿxÿ»0‚TrËpœxk ï?¿xû(wcnEÒ$³…ª_éîÇYO9ÊÁ2âÚ‰æÓ°1ÈìûZÖT(m\,-Žóƒ $:µ·´’jU–Öà¤g^éËBšr¨Æ~ÀšÑŸ+“R®v èÚÁIŒº¦BѶÇP¨m7,ÀèKʆ'‘O½‹OŒg[²´û†×·È®F¸ Y~ Fû±í)•ï½À³T<رÙFýç,hh¤VÖøiˆª$ÑÓª¨†¨JÖ’ ‘.¯4¹/<4 WÁ’žš¹k!þB–‰ì0¡ÿç¦Û®&íGmH­Ê³ãâÛÈ{¶dcx¨ à–é‡õ{’Éô¨Í>;a€§çé¥z6Ô^êȹ¶ëâ†üŠ}Ó9¡ò«jüB·Ò4x Qn%Th’¸Ô§õ=£¼„¤° {è°”Ï⮚þxQð*-UIÝÜö#»åÒ!`Kà¥[5?a5h–cÌ@@½¥¶`0 m¶ð½rö'þ±^hñ°KÜ@µLL&ĉû“G„y³ŠY‘Šåbî2Ù1* µ®¯kô²°¯áµ£ZnŸ¢D‰]³Úx˜ñ>•1þœ»´°æeíÄÆµgüê(¥ï ¢H™S/PÉÍoç,Gú 2òSöt˜÷×nd]Á¯Ñ"®k‚xáF‹?†s%±¶ûdѹKì–~döì:c¯sO9ÖM~²ÄÉþ1dsnk³Õ²ÒF_KyL …Çbˆ-‡¤ÑHýÕ7!¸Q&þX#W»I‘ýó3&ß;$€àŽS!]ëQ6Î «Æ@ª*gi¬7‰Œ‡V®!{YÆèû ÜlÜù ¸å„þ¬‘ë§K>á¾ì ?¤O£@»Æiöbìð±k¼ôŸ8þ“>ê½Ù'pôÞýüØDYmÀ•ù'Y]p¸£éu”³:3hŸH|ûzls¾Äb5hÝÏ£˜Eaë(UQ¯Þ58ë‚V½;¥lÓ¤©È=AKÚ[y&(þZ ‰M3ëyv¯êjº›ËLòî©ê3vO•Î#]ìI(å`õ)µ "aéZÄô(ÅñÈczj‡âM*l»"ªȪ°F¯3‰¾?û]lÙ˜C[Z÷Ú™¿;{ O Oâcé×(t®! ŠJç²Þêî6Ï5‰›uÝbsÜPŒw–‚<¬‚×o3YøÏiÜ$ÿvE98¦¬‹¼*Ýò'ɺú*ú«C™z“3ö¤–b†ç˜A/¿¼Ä8éeí!:¥ÊdÜžaÔQÑC°Rß>Íîý&B½Ó~㟊#Õô¸÷ßm5¢úûÒˆåU$rICLtóJB&gêôR¶+Ú`éÀŽšr9]|Jiý*Õ¸ f°H› yb±1þ¾vA‡tÔª=¼G´¾š¡ØôÆ#´LþØf0qM¶_^ãƒûXß" B<ˆ>÷nÝ`Å OÊÆÀ°NMt fzòtÍÍVÏOꧨr×Éw¡D,}-­ñMxë5çs™|SÿtÛ0ñ”>!æäxr4KÕÙµ r¼ßbwøžå÷T™uêr9²V¢ó쎮¨´ÒÝYíEcCë§þîÐÖG „OÃôbZÏÛ­o½:7{ô ¯ðˆq«;\¤è8_û1v© ~ÎÒƒ‘:ÔÅpåp¢ÍŽ ^M°°‰™vê$Ì\ç?–¨ÍÓŽš¹tßîø„(š±é÷,ÑAz¸cÂa_ÿèÕõSv@öA[Yä ·= Ýu¹¸x¬­È«á"e:ôÜdÅѵͅE)Õ \´69•äLÔÜY"FD»V5á[)Ó‘“-ytŒ© ~ì*ãäq£&LóÐé&ž¥~Z8z®…­ \ºýƒ„C†•=y°¬ó‡˜ ¨‘ d÷dÈŸ¼E²q*ÅÞøcóÈkÐN?Õ†8¼ïƒ-Œ0ÉßÈ0sÂ8›m()¥.’y¾˜f¨Ö#xг|àœé,¿Ð¤`’¿Ë«þÒ×Hª©ú MD޶Îc«FkØÿPÎwv$1+ä3n~ÁTóôüÖ¾áAº°]·q! ¡`æ³*€¼•¸hkS%¸áªAø§â&n¬Ùµ‘$ªÝ{–;—ß)à—Ü+ÜG@e`ò¸ÚÍN9 ±O¹a&ë—‘äfK$Îäê ëÅÿª_8·o5NI—ªB €51EF ÉJ*\ ÇrTò:ÇÚÏ‹HÁJ…'­Ø6i-<ðˆ’ÇžcÌ ì¢›˜ÃW7LÿÊ vû3—m| ŽìK$,`³ÖÀfDÜ0(ÀIš<¿A›JÕ ¦h©ÖwÚ GÐBD]ÃIšxíÚÔ=Ôa,¤Ôqå¤N ¾ @®âµÆuÛu<¹"&86ûVyO 8;LRŠ€èœ±‰¼Igþk„5êÏx²$å¾çPnlP±#Á‚QWQƒ‹p…þó=1“á¥v2Yü¶:ä%Ií¡í s640 .ER¶UãŽæD—DDÚšB?é¶D 't||žX\;È»‘ˬ3P%KfBv+7ÇÄèÊ”93Zf ^»Ê\{+F:&°‡;}ÊyO¢š*}FJ‰¼ˆ‹–›qÚ«’¬I·iö®¾ç¸€èòG0zåYƕܯ_»\£j{©+Ÿ0þÖkÙ)‘2ëè$zS×ÀqÓå1c@ƒáäJwíG¯{•4âÒPƒ7¤â7>k„ê4™X ½1Ï™ÞGÌ4>ç¬]yV†ÿm9Fö§¢´á8Žj6a;Р錯VCO°øÖAÜXôu£‚²Oᄹ°bØÚXuÒ’„y™œÀÊû ëÕ¯ÄËôÑÒóxy¡wÑëQk ‡Nª¥¥?´ ã7Sâé“a‡äÞQHhrG ¿TÆt[C†Õh[§¦%û[¢Î›)u‹m¥#¸•Ö|Ó˜ßMîi)d8ç ¾8t&ÇÖVÒv.ZË©„m.úŽI@t¡Gs¼Ê¯ü² ‚×÷(©Íî¹ÄÇ1çtO”Wõµuë‹ ÒYÀ [6\ª5I Q[´¢áíµÚ²ÖA§_6B7VÊô£¶ÔµR^¡L|f›ÛbU?šµÑà7ÖÚÙš=pmä›#N ¬cÛöí׊}âÜhþÓÞ¹²u†àF·ë);ÀKfŒ\d¼¾þœýýHiÖ$»ˆEª¸ªÚÙ±‚^ƒÆ¿'–Õ›}  §Õ@|:\Þâ«tTáWöA›{iûÑUkþpRµJ+‚êðÈ¡¡a´šäËq«>ü÷¾5™YMÚkÇ 6E9úoú-stÜ$½†ºù¾®[¥¤®Mî2üfHRos&,ç)7ØÛ 4ª‘²0t_Jg‘N€°HdŽÍŠŽ@¹ êër¯ CB/f㥑–*t›•Gà•wB1…îTNsÑT”ѱŕóbët%§s#ØIÛΉã•õÎÿ?$‰d `å)«€[“aH¤îËgëó›“x¼ø¢-é`„×¢ÐD{~UÁ ŽmªeM–Îö½ÐEä Ø…4=mïSWñ¯……ÏÍm#Ùý X‚Tša ya€¬„.:6ÚŒµåù^CØ•@qß º¬£–#,YàØÈ†›ØrA±$TVÉö÷Dg(¡¬!Ô¨8ß…–à ˜­ÈˆzTÏÈa~¯/ëf¼1Ð*“­¤ÌX(ÌÖêU<1WøÇ$Ìoc—2·Œö¶?Ñî‚;œuð2àY<ÊwuÿçÁae6ô[š^)…9ãC6ü§²±Óö(]ìõU*y¸‚N+{ Ÿó›=AðS>©×ê× en bPaè(#_-cc*Ý€Œ&‘Û¡ÇQ³xùÓ(èAÓ”( äÙ!ò,ôUk› ]jô>,bæº4õ°@&‰a¾Ví‰ؼçx›ä <Ÿ!FûÙŒmj‡ÎaöP|´oôPGm¨®dSˆNîGÍ…f³êUË­r% ¡ž–k¿Â=Ÿ§=Ô5~=Sù'c#©9Ég²°Ì³ÄZè=Èà(¼ÂýC)*¿qZªPpÓJ”X·Åú17ïäœÅ¾Í*a]dÆý¾ÆÜaõÖã©fðþ™ìbUa#ƒô|üʺ:eÒû0’ÂÑÑoéŽtY4 †ÚVìƒöoY£~­Õ#%µAá¹]‘¥‘à8Ò ¸/j­öÌÊ#fžeP´<‘`sõ<þéÜ]Ȇ&$·=[Ã15ËrVj~døÞÃí¶ñ.#òZ¯l; x×¥3¼ñýžOí ¸î•<7¨#ŒæÿCÙ·àð¤¤d¥òþ½CûoÖüÊA•Öù³Vö·]˸êj ÿPåvߦÛÍ×dtɲ;ê¥Ðþqc­ma-T&¦]ó^¿¶ÛQ¯Ó§¯'Ê<ªÜF¾Fu~¡YØ@eSx¦(‘>‹ž Kv×/v>Œó„õÏeØ©ï­tïÊüÂ`R4$WEýËü¦ Õ‹É<9—2YúÖŽ˜V¢ëd’[^ÿ̺ÉÜ5 AÊgtt‡l Na`T¿ò=sþë†y³²ÒûѪѫ°àZ„c ­d¿ÿL¹³Š''uëÒÓHbÈO,Ž>¤a’‘u¹Í†ƒ²ƒÌüž€qF³› Bš€ÂÆOßpQn~w:áõOHÆ}i·ê`˜®Øûƒ—zÆ¢ ê!)}²ΑIКî) àŒcú»éÁîÆñ${÷Ä»àÅ.0êsc¤v–£1ŠoØã/#Ús, ÁyÈ-ùçbØ`¾LK¬4ýX e–œ!þcïmbss½•'“ Þâfß6Kš‹óôÆ…ùK?Âáæ{\.êÚ»÷ éݹ±¯¤¡=ŸÀA,+åš»gQM- 0«G}(=áÐg}Æ“<<õ*ˆ–zåìÒ.´Ÿ Y*3—°¶­gÒl.¶ŽôGäLÈ¿<ìú6S¤µÿÇ}kÍ€ê¥Ä€lgîÏib5‚Í~´>I@Tšt&¾ÝÖ mÛW@%)©ºdp‡¥o–Z“QkÏ.'«#~+ŸaÌ‰ÕÆËogÑB¾èšøšVÿMÂie{ð¿Wñý{êÂzóûè[óÿñUf¬)N#õ`BWK·­88îæÌ  ÁÝÏqz’a¤íV¥ºy¯(6ÓÂ*Gq³ð…ƒ,ݰ–T ¦>¹P3Ðö Fâ\˜yß÷bÒÿc—øuù¦)ƒ„T;u[JÜ=ݹ1݈¢È·K™”!cÈÙ<aBÄ1Vö¤èâ­;Ò‘4›«Sš¨ü<ð+¸÷+ÇË•:¿'ÎqÊchCNÕßZMû'²¨Pb¹ 1¿²Ü»%êjP’$Ï#¢Œ\ô¬•HàCBa žŸ7Ò……¶Õ‘cí^?j¿)â ‹•/-*Œu+x¬ˆ(Fb%z^!ƒ€Ó]5flé,7Ê7ÁÚgv€¾cöyîMñUξlÝɳç¶sH‹Ë’*×ñ$-ÙO—‘Bmf¢ºAÅcÉžWeGÍ%â Å«ì"›C6…Ût–j³¸-ÏI¨Ù…ÈŽ 1À'o¹ž_³I¾%œÕé´[GÞ¼‡Œ|[Lˆëàqíÿ.$éÝZ´Ià èE’OÔTög“k2Pç1[‡”OròVfà'Ë–Y™Šµ„ˆ¶%+Õï]Ãa—Ë ¿ˆþS>úШ=t#¹Š%5ªG·é¥C­]Ç¡Àà;qÝÊÅ[–v ÿ/ÔÜŽù»n\D„¦^òRŒÓL«¿ßh°ÝéOï:IOòâ̱È-9i§Êòî_e5*E[h))*ÀªdV‘°¥¦]ð|ˆB&ŠñÍá&XסٗVëõxUÿä"]­>wZü÷ê’¿Í7ƒÂ_e…A®”Ò_^x¿S¶Ó ÁÕ@åE©¥ä˜Iú^®£ ñùZLM˜D9•¶ÿ„×t†e@ÃdrøÞb®JX£š:ËBņ9'ëX(ç_V´¢‡ö:™ˆJamwà_KÐêÕö‘¦'wãºK(@†»ÎêÞƒ93ä ¡4#Ã`ž†Æ‚Êíünç*M—ñ,lý‰©8lñ¦}Àm_”ƒÜD wrv®ÞŽð‰^—¶¹ &`™¢§’70±³©AC/™ùšÄQ§es8‘&à/EdU&ç­8uüu]½éܹ‹çÆNguÂñ3êg=ŒOzb!ïðؑϓ”‹ù_ˆß1Q°àILô!d®^óæ5³'àâ§üŽõ~º1µt>ëè©àßRUNâ-Iä ¹kS[ú±ÒwD¾èÉw/¹˜‘ÐÆ’"þY·’Æ•à‚ßWøh‹ÔõdxX­CDïкbô8à#PFÔð"±Û«gGòûe'`¥à‹ô[,w.©¡^vtqîÂÕû2;ÐyY»¥Û¦»"ðz;Ñr.0ö†'€öêicíBKõPóʶ4ì¤UœG%‘ÃÉÕNÄF.n¹$`J]%æüÌ^hžùÏxê²aeº+Ûé¹Â!OwãQu7œ#s¯˜.AYÿ/æUoÃeÅ`Êáñ£K[¾ Hz škkFØ~äÍþqHïu ýZG|Äã k ÒiÝífjE[Ó(©m‹>¤8P>é|m°#¶õ¶Ö£»fˆ6Ùó_o]4 s!¤ÍZ/ùB½R‡Ó·eq½ÙÀOÿ¾ãêkï'á¶ëVaâúE"ÔI ÒiîåÛ}q…Ê2òQ„ݮƌ©éèÁñ6Ù8Û.èåÿráþ¶lµ®qCë¦A’¼&ÛžEeìv¡†¨¬ïsw­m’yü‘”â„fÎf|8gìWd÷íÜU즊ÁŒÅ1Zp¹ŽqÏ'Q˜ôÛbvë½.7Øk ÆjÅ¥~Æ ÷€]®¥„,-€Ç2ÁîŽ8ÙYòÇw¡‰$®‚:ð亊cÝG¯Õ{6ÝB†"ï$òyÍi´ôá̈ç=ŠýçdþÈAFÎ’SßO‰é¢}æH¯`5CF¨TZˆ3|‰Ú*,¿Ø2“˜ á ˆ5+æcÎ×Å*š/ÌÕçÄý:?ø¿ÜÝ–g¼‰s .öËéx¬›ÔË”²368àbYý¿yQéí¹3>PQɲe§œx0 ïBîBó¾_´­ˆ³¢¶rû+Óá߸ëÇ£Ò´Y£»T­LU´£Âý²˜tc`ë~ò­\™{y¸›ÙD¡éÉCç6_ˆwBMÀ(ߨ‘‚ø›/æ:AX[æà™DˆQ¹½Nn%#ç$»Mp;ÆÌÜ CÏÉL²USv©Qd_ŠØ–-QZÏgÐ6Í…Ôÿ*Ý–¼RüYJ«o[2Œ ãa†­q¢ ÂE`öv¤ÙW=ˆ‚¼[ÆËöÚF‡ µ)l/¹S)rÒ’-–GÎÔ}£# ÁFÏTL¤Š9"#÷_©?…ÆVcRw¸?û0Äœ ‹ódï\tçíÜCæ…Þžn–§§Cè©0_„u—¥°ÅCx•Ùs©ÀŒ+çR» 'îž”^ë"®4D¦ð0´êGdLSl§fN¾ðh ¦÷w^s À–Ú»+վʣ]k€Õò(í¬ñ6pÅDúŠJ{ƒ !žÓà©î¾/²>³ raþj@-Ÿ/·›í#qï~/dðQ4or†kÉÉåÊP„›A}b&‚¯©zÂç¹N2÷åþãS#ïÍ! )þöýjïSAeÓûæ«*Ü›P­†û€ ;ÆÑ2P¤eo¼|HôRÙdñ×nk 'xÖÞV¨ã‰ï»Ç«¢BZÎ+nÀB¿þVYÈQoš0Ü¥ß åÅÄâ1Áª¡g–ô‰º½E·ˆÓx0›Ž'‡"ZóM(uÔæè>Ú~Äh…^¯c;¬=ÊpèkGâ&%þÈó*;¥¹Üã¹á%Ô•fw“—Nhdžˆ7m”RšW92ìshUÇxë¾vÅUí{ ÞsáÓÔa¯BÛ·!äÿ)SO}&¸ñ”4Í™~;ãYœ „ ‘ñ·¾²ÞX}eL“¸¸S4³z¶fôF`n²N8g/á/ Òé8>£Þ@÷^»WlØ)Üþž¹6qØîi ÔE#ªM•râƒðu¡U•Õ¼–!úK&ŸX,jІó‡ÄS¹= ÚCÊ3¯Å»UæG¦~ͲðOÉgF£?ÃRûíA¯Ê ®´çÍí×–%C¢Aš7¤!¸Â‹l#ub¸ksZ§¢¨á^µ€¸È9QµP´O•!Åfwýg[›üP”ÛêBÌ8÷‡=)ÌÝãú}·a~F—ä¼6Þ¯×,ì‚dœêOÒýóR±(,ãZÏÂJ?æ!†¬‚΋þ«›P`ˆÛœØ×(¸Î*z)<°,Éæ¦kîîm€dì`Ef9Ôœ[7eAí§¬É ?yŸB3 S“H¯¤Ö)~Û´â,„\g‹&¢§Ú)œY ”Ÿ8O$z+[òiˆ·gÀÁÿŽâ/•¯f»7{Û0ù~kA½­áz?ÚyŸ@õAañ·G¹E€u%ÿ^Kßù£H|Ê:KQ²…´Ï݈t}¼Q¡Ô·¤ÙñvÁ‘Fjd Ç t½3é¹&äÈ@½üÍ ²«Ù¤Œ÷…ÁÛ:üIVZ­‘_¬"‹›sV(ÎaOP"·¼Ž›ýô¥VgÚžD‘˾1{N1ÐÞ[쇕pªõ”!-Ô {ëÁ•Yh,§Õºf'yU¯=Ò^X.#r˜{m¿Éøj5¢ÿ§¨Žé‡ûL]ÑÀòµÓ·5|mã½ð¡µžaMfL)Ý·ÏÅÕ\6doâÀ-ŽÕ3Ü…ùÚ]%óº­"tÉqs–hÕšh€²ÇWw±î †4u@©sĽ¤0jlth¾‚A€Õ8¶s묋@bÅo>wKìæ}ß­)~Ãþ¯债)¿ÔµÈí^};‡!ÄÓŽ† EWäqãÍ‚¿»Ñ=’eÕýE>Ø÷]MàC2oq |øTÂÖþsb!`-„•|KÖ¾î}Û—T…÷­“ —¯–;>À%6qA·@Õî3c=|6 ªmrh7ô>¬gìýo¬®3aõLÐ7¿‚ÿ Õôà’ÌLËpz`PwÌ(® b[4èê]* àñ_¡qÌÍê:T…‡Â©|;ïz=òï<¤u½ÀÉD¸MºÆš`ˆÇÑÚÅñ÷Nï…/{¨­U¸hà€Gê=ݠаìº8“.aÇ6ا5ÕÀó dÈriŽæŒbD¼o„/ý”@¤ `ZL1È ¡uÐï[¿ï#yöIÉ-œ¯”£§j߀”c.îcÁt²þU=ƒË"±è^Âð”«ÔˆÂ{'Èç´cãΟfŠÓ~Œ¢}œæò’ ‰îÁ¶1tb[ª×Úsìën€}be{³tíÂþTl7ý5›hÞF%©j· ”:B¡ŸºÝ©j ‚ž€Ãýs°õ!?KÁýN¡Ó¦#cCºyåØóéŸrŸàÝ–½Ò¡ ×(ÑQO!ŠpGTpy+ÞóýF÷9§hv† ™Ýé3wù…+CȯGDGE’qì¤*£râÉ)Kt¸¨°Q⃠?Ÿ›ð鳓`MA‘Ú žò§†Ófd—›{ƒ‰œ3[ÿag¥L0ñ"K%oÜiK-ÙŸt™`ðVÿ)D8m˜§Œìá÷(pOõÒgÔÆÝhîWòÉãÛÎ;jx¥vìbtf¤¥<ìté K扜aŠ;ºoËÊ䉋“:¤ÁA þM¾~DÕ „3vÎ!$ªHJæÒåFŒ1HÍZšë§ˆZ¯2æ\§ÁbÂ[¸æÃ£—,m´º¨'VPþ(Móë1¼øOÌGG¨Gmç° " Æ>Ž “¦±‡7ÜE°û4GŒ@xýå@º¦rć3pà ¶Ë¡ú”Ÿ!.qìÞqyd ðØÔ»t‘S»X ­ ¦7•ÄÆ®€¾ïPvÙ±-íîS$ryêmu[¶Gj+m{ÎÒ±$0KìBÍobaè˜ûáèªáÈüËdüÇõ|à"ËÜæŽ'?|®aa¥@[S²­‚7!íÐí3—( „ǶmÛ¶mÛ¶mÛ¶mÞ±mÛ¶=³ï?l²Áv^TÖ}ê|õ/¥u €rE:~¬Açíë΢T£€ÐMPµ[îø! Òãj%œ/õBÎì!XWø[Wü7l5´/©ÀšqË®£?yÏ#B½à¨lÊv·@qWÂFOû‰œý ¶¹ µ¡È’°$Ïiè+À‘ (/ÍŽ§’Ñ Ù¤%ñ~ðseui5æÆÎh±]2¶’Sl®B34Æb`±õBkéöM‘ô˜EÈ ¢·àoû½²02¤Bó^ùÈGNŠ~“q½¶lsÄî0ƒ2 € ]¹£ß»çØÜéVª%2£…Å`°+†0í¤™w+–ÜÕ,=eÀº/ÎCŠœqÿÀR;£‚a•ñ¢bÉ«–C‹\ˆÛ |RÒ„@Gds ÂE¶gŽgЍçw3ªt0òïº/ŽÕ#vÿ÷bG™¯¬Èû‘¡ü>Š寱«Úë³Î§htrÏRƒe9wz©Þ'e]pÏUn¢Wûm1a_ðcÌ+E#'ydؽºÀ62£Þ )úŸª{bã4ŒFC¼éÇ&™_°ˆxù¬hý„öh÷¡]‘‚çîËôCÿÃ,ÍD9àK„Ž!Ï|ÙÞl@’¶nÅUÌÏ6jœVÞ=Sl æ[KÂAïJ’Jn&>|‘¿2“£ëM òêüFT_`EÿtEÈ6’ñµ»=j–±°aÖ§/·œB#šù¿9=ð%ÇÙnI¦=?V],¯A>b/«Ðx/Ôñ£æV#pÆ#W©äœJ$ªïÑG´‰tz¹SFïJ‹9|R¤J‚—d<ž-ƒyAW >¢!Fz±J£i°®z°Ð_EοØlȇÈZÌ~ò†Æ””'² LõÀ$3ËÖ|ÏÒ4h‰ú&µi¼€W’•}¦4*Óta=È™1æöjÌËg)&_Ó‘©r×¶ZhI‡@ÈÌ[qžQ<Æ2z‰ÈUÌJÓc­õ@–¾xÈpÔ ®_áÑ«£2yKmÖinØðoì%ù+Ó͉׿E@¾'9–öc´š©‰Ç€œdYѨ/1NöøÓW*N—Ž1„׬»–ª«¯ô£ÝŽ÷õ¬7„IÅy ·y ܱÚâÁõ í>zè@ñ¤0ý~[¥›B¶,–‹Î6¨xÄ ¦GÔèux2uBòÄreypÁU]`#¸ƒ#Ÿƒ™lÏŒcþ¥Ht)ªÒ% ¯/#°ó~“¨=YÊŽôH­aXPAq„ßêžñg·n±×Íx`šrh¼Ú{w„[@¦¤{Q=ï&`œÂú6^ø¸0D™z‘¿Úb‘EœçŠá±Ú%MV¶P‘ENo÷²)ׯÐ‹È ¼¥¸Ê35çõ9R&h›à$o§o‰\SÝ#a°Ç¬%ˆÆ±;Þ÷‰\)]çf·-K'ú§ú‡ä3Dà t®ú¦\£ˆx´nƒcSïêá´ÐŒ±¼éhf7ñéU’gþü/â¼£ò-_Ïb·Å›#ßtzòFE.T°È¨¤ Û‡MQ§gi—è—rŽÆX„`‘ZcL@7·ºÆ‰ÿ.wOãYBG°Ne†eF"ÐGG]8uŒ[ÒÙṲϩ’[kÄ9|Ì‹ùÀ…D «kË^Æå™ÈS‘òœØ¥rò³Ò?¡ðG®Á`"˜’r: +m=ÚTý¶Õ6š"àJø½!ÓQ烗´G5–ë@1qŠ›ïðšòVÅmÐ_5n¤P¸9d{ߥâ¿–hµ=çI3pýó#ÔUšFœtŒPàåi´|/” “lí„À€©¿ÑÒéÕõ=ý#ÌR]¸ß™­Bµï‹ÞAPÅha‘ŽGÀƒI~57Ôy ?šÄJÁ}ïG€âe«È[¹-ÿiGjW—cêÓ‹¼bàè(ëM@>bVÙ;Kjt~íO;uVõñ`ïõÚ!_Ô3Êç³}Ò±®ñ]}3¸È#”èJ¨$ø£²üÇÁž­Y"º&ÚÆ\Ç à+ð˜¶ùæ´Ò„a_ƒ­l!ïšžB*5¦±þ¾ÎÍí­Æ]eýX¼÷Ô ëÇ#^ÚUh[ö=!9 ͯõ",HÊ»ìýͽ9еù,è­Dü_=¼A`Û/ÜÅtÛ>æø(ã•a݉‚„×¢¸§k1*yéŠN àtóŸëR dG»œç Äz|á·2-‘aK¹')÷·\lj¾(5±… G³og)ZêgÏJâæ‘ùÔN±ŠÒdP­ ™p._G20''=,Ìú¹óÑSÒ›ªRˆžp"q¿=þev·A5ì3/{1ÉѨKû±,WãëÓ\^¼£ºY”„VŽiåDþŽ]³M€îJÛ¬D7€{T/´X¼cìGWÑ÷l1òÜ·;+E>K(ǯ+/ýwgí[ƒq…ƒAé{öW„ÏÕ\hðY‘‚ ÆÔÂÞ|°s®LUÿƒ`]y9}™ÃØÖ¹ÄôB)µ¢(ÊV`iî¨ –„ûsª«•t  ŸûŒ‚ƒï 8 #¹óÎ0˜k:*ˆÜô«¸EŽÕ§¨è¶׿?wU\—C¼4Û"19ç’Rp]ú˜µÏÁZž£t$ÅHa –NGé65–¹‚+6r!Þø ¹È«ÎxÛ¥žzGÇ’•¹ª¾^½xÍ"æµ[Á¼ï“y_66‰b=…urÌiÞF"V=¹Ÿ]xï.þ:¶` +-Þ2D¿V£è†RË8Ìɳ‘+ïô°õ cÈêñ%VS+|dÃ÷ð¯8Eâ'wËZªE‡ˆ;1%8ãYˆQ~Æ“}…¡‹'Ìî·«OmËt²¼R%¶cúQ Øc¬y·…îB³ºÀº=bÖ¹€ŽÛüÀî@ä]¼bd|iG#¿´8ýTVV-lÃìúß›—<¹É8Àƒ:–ƳäÅ>E“’Våk›å{|£MGѯˆŠÿdÔÇÐ bŇçW*‰xÕ\ø‰Ùj{ Üø9`›IÕczQ>5ágOÖ®] ö½›aK‚›¡;7Æù¿ÕÆÅŽV#ÞLQ¥‰d2‘œP¶™ÔÅÁV¥ -(vÚ§b@GÅSp&ôøíeCÎÔ ~ðŠ›gãóí»h0òð6ܶ«¯&<±vèÚÍðI8ÒØÔÎçÇUúH`‡Ô+÷–\òùɰªÎ}Ó;’Ö¹æòb<³š€ÙÔÅ®ÝRtÍ4寅ïÂü®ïôV;æö¹a··#Œ&>AÂ‰âø¤yÛ÷š¼L®ÇúÃ$BÕèæçƽsѬ bê>ºÜàîkƒK,&YZMHû9Þ×…Ñt¯Áö2X󮯠K!QœBÙôˆ÷Æ«SGGpœmìƒõçGQ¡¾jWÆcÑãM¨"c3Ö½´yÿO[±Þ–!FÅù÷p ­±<™Ue E£:“rÇKd·þüwsRfUk|yTâCž]ýi'‰nÑ ut“¶àëÚ-µ¯V„CôòDöóÙJy0j«//ú$¶×BHôÐ’õè{=p1¯B{‘6ëТ¬µ©åz‹ ÊfÂ\dgî¡ÄIAýˆýã2Ÿc”l YS¿:¶Ô;1§"äQò3BÓR^ö˜­ðÓNõ¾ÃÚ£Ð`„±Wx8‡ípõÆÅ±+7¿ÕŸ9,ýµ’±|Êq6˜:|™«V/T¶X{¿… WÎMë‹lfÑäSì#ûû@S¤j¥75ÀÕp"c_þ3/r¿õ{œŸÈ›-Á3VµL-oIH¨!auºRµ±œÑ}‘Õó—úJlD>¹÷‰†&Œá¤zWþmN“îŒ+…g®f2i-òÖÞ{ó¡$Ë,GWŽ5·ï¤ß®;0“ŸfGó'9@³lhá¹ä*çÖ{͉Ùú›i[‰wZ/i±e2úå™j>ޘNJŸ´ãÆÜÔÁKŒåH¯.íò¬)}1?ÏÔ³~Ä54 Üúüú…N/l6›T޾ÿs/íf¤@áÝ;û¤Íƒß°'/¯ý0N.²ä=NfÒ àûª}’G~A<©–ƒ¼.É ä6“xô" ¹#µrÓð—rÜãOz ñ_9K’r«ÍeÃïŸï„ñ³ô4ÒÛnözºO³nxg¹>óç+Ü;•Kr’¸Â]–¶uÆGÏlŒÀüI‚Q'{-€ws“Štó™ŸIˆ²"Š#ãݾ«tÛ$tŠd¢Á6ú) ¹fŘÈHùÖ¨hÊÒ/l·”ɺ–q°´èF]‘‡bÞĺ|îÅߌ…ú!ùà (jWd†T™.òA"a$öñLJ×=t$ãªdß¼„ÅO zìýéaZ.c¬‡wMú4§*–}f2À:PÓü‹Ó¾¡'*ý(r«±O·ŸèX,þïNÉ“´Çï|:X_ÛÂY³ÂÃnV£ú½ð˜nk.ªa¬$ÝW¢¼táœ=ê%«¡°¶+s»³œA…ÔGDà¿€-dÑ#Ã.m ýHx£,­Š<…8ósì’3“¨ðsX¡o ëç°õ¼ŸÐCTC‘+/c«\5à$cÙ¼V olHí™%céúà%[¶1ˆSn.Orn0P‘耘’U÷cœ”³Ø´lÂÁîÞGí,¾ì¢jAØIì¼. ÔøH±á2¸_öse+Ý€Ón~,ˆ¾%„û&—ßµÀz*OICmcí`oZêd'h‡ÝàÖsÍ`î¡l‡>É•Ûóö»øÀ#ðos.·‘K §9NCG°»Ž MÄëH O¯õ&f=¼×_àߦ` ã„ý mÿâX°8¸®ÄJ‡âŸFwíAä[Ø:ßM6hPĈ/GÁPÖÁn[%J÷g–pf*™/:Š]°xØá>8Ðx¢ân4PÓÃNp¡òõšôU·¥Ÿ40r•´éd;ËT#Ë!¥Ì>Œôˆ!fMµ¥_øKbRI áSŽNÓƒQ2LP+úÈ£xàÃC5Áÿ²«8d ßr)€Y;­bä?:™ä]¼ ¨òÎ,”øàK¯!ÿåšÄÖQöÂëØSÍ›Û#.|ck1¾õð =ïSû|1Õ›31¢QɨŽÝù5$š0Cý«j¾†=‘ßਫ;‰‚ë+°¿‡òêRM1N\zO#ÜGiƒ>Íå6AÚŽŽ!Z2ae6v3}@gLµFµnëÆç1s-ÓM|;µpà®R~ŒÕá*ºf3¨Ce€E<(Û9H F‚çêä¾²aé½8Ý»®’0¦r€M;¨VÚMùµâö¼¯ )2 £yÊï²y '|– exTav”Ò5åÄÈ ~À€¦Q©Óæ"ΡîÖ—_\ùQE9éY2ï‘îQ‚Cõ°™5¿CMTÒ7²Y©Ñ—ÅH"Ñœ„ùíeÌ'l«ñ©¾}Õ~œõª.aW§Ç¥³‘ *·;l³jÈÙ˜ÊëÐäà)¬#d«p×÷fYhCC±Š1}—ܧj#¬‡w–šéh9ôlÆ3z<Ko‰ûq­3úq¡kœ¢ êúº’Xm;Ö~üŽaȲäÓî5Çÿé½(Ž^7˜ v ¨Ð¸ íhŧ qSrm4Àˆ~Ún= Ù Š™ÉRܼµhJâ@õ¿’¾YÃ¸Ž¢!m£ºó™v‹%nAŸ˜ÀÖ&e"“^æ°l9Âk'E-Í·W“MÅ1C ò5“ÊÅq‡µ/Ú鬢 ˜o‹âŒÑÞ¬NK¸iR°4 o+C+Qá“Møžæ   {ãÚf¡ëÕÓ‘ÀÔ··.E° ŽÛœ/™yîœîÚk›´èW³c挫’k§1îkÁv;§ã¬@þ–HlÉ‹þtT›“–§+•Of’ÓSWƒ\&µ Fñ|Ø’MÈ×Î¥˜0`8ú‘Gd5ÿ0ٮꀎȱ÷7L=á㙘•ˆ6^K¢ÌAŸ¸4Y{STäŒF)S*LÌ_Xò}qÜB~”a>Š_ÆJÓ½ôQ{±ñITäOà@[ÄD+òÄ ÌW -fJTY>¢Á»ÆëÁÉo \˜ÅƒØñv°ko¼ÉkƺÒÅ¥Rèîb„½rÒ‰ÔÓI›m4¶Š­?”·¯¥j\ýø î(¼€èé§ÅUÇ/Ü*T-•£þB¬‰E}¸P¿u¡ÿÅãµ*麜0ÁÒ¨Ÿw§p„Ÿz{z"ñ.AÌ>÷Ô_褑+}ÌpþJ} þÃ÷RtM…•ÊLF°SÝãÄVˆQ¢VFïת;˜Cñ.3 /E)Þ|©' ¦’x?ÃDAeJ’£p¸ÞÌ&T‘æCø"³ÖNU¾©YX{ â!ä¿úû1VùÊ.þlÏÆ–qÂÅ[Z$Rbc³ÅC1 ±ûµ‹Bur ²Ærµ~Ù½ÎíŸG#)n%½ÐA?\Á]L2 iÈT©HìËQLØÁÝÙË¿i—–‡¥ý÷À»)«AvVà³ö¶h–_x;®ž° ÄöÕ„.¬ïfŠó¶­õç½5Çjœ#dTVõ"z9öölLÈ'Ō̗ï£`CAüYÏI¾f­SôÖEOø…6ÖÔõÊ(6º¯D9´Ü¢s!E‡qR1áϸP\ÇqSžÂ÷—½¼lôÍ5Ôº¼¯€¦§|ý¾üP'K~¶]eªžoXœ{nT²d?*WIú4½E¬(´KÁY“ËU˜¸EoO…bhó§ýérWÞâ…€²Æ µJl]àÙG]—!ÕŦüMGQzÖÉí3Óë6_šXì¶“?&7V•gÇÌðð÷ª1¥b%×Ï«"¾ÿÐov<Ó¨q^\¨‰5Ð2SÿM©ØŽâýþ÷M®] ¢²²ˆÅeB¾Z~”€v¥Ù½†6YeÔ½Ö ]¯öû޽>{Tæ7S)ÊÁ5ž€vO䎎t¡€ÜÐÿœ’-K‰®µJ¥Ö"ͳǔ$e{©-ÊÊŒÁ»’qÍþmÌ¡‘a.ubC£Åg +ö¢rDÏÞ˜›T3Ö¬Ÿ;eOyÚž §j „ú£OiñàN,ƒŒša‘AV­JF^忇ê§r•HP‰|‡¡3½—²´¡Vñcs9¤˜j¯ ÌÚ¶­ïž 6yÚë͉!-mUÖ”±ÏRKÍ‹I›Š»æNh›Ã«©ûNú‚€AloÞÀÖ«N¼F¿ì'À¥9ƒúକ(ØG’2ãMWÁ§ñN‘•3h?¤Eަ@™2ûÓpéБþ±½9]òº·¦3}2Ê‚VÅîôNWóŽÊ 'F}\Î )A ØVµ=ãÑ^îµvä–ÿ`‚Y‘)Ÿ,å&$§/ólÚ‹*ëO ¬¶Ès0¹#¡í7­§‚º£{Ž*dÊ…w)Õ©‡ßÃêä'Vg~_L-fÛaNàM]œœcã—®0ss*Þ•‡âsÈ& Ôý4ë›Iû)ÁP-?ä…L!üz¹ÕÍï±”ÜøT¢^³T#sÃݽöª¹9•»Ý9tÚ»Q¬T—ÄõÏ@ËR2_‚Žðädå6¥¿ÄŠ-\}ËF9Ë$yÅiÁª"lDèYà\3Kêó*Ý[äòR $–Ç=tÆÓQÖ>äÖz̘E7d÷ •4û•×Îßi<•úö:®ÙߌJ`Oн[Yê쪑šùlˆ›;,»µËVPM$ã^9³Ã¬¡¶p…»wTTëg;ÛñY,„XÇ:óû £ŒÇÙN( á›G§§5(R¢ üÔ;&ë†qAÖv"f’Rui*Â!¯‹–gM¤`]ßµOŸ©¹ÌèJ¹ò.k$kJÜŽï *\uF©'ÄŹMaÔå¬{µº[™©ú²pݰ€ÍßYºÅ×¶Tå7äP~—Šz8cÑ¥v`ÃëÇ' (Ð#ƒiOëä ðgý¹} ¤ýýþd<½‰¬ió/ogEÛïÏš ¤¿&jæßŽ; ±‡Õ}ð±è ýä’ÿpF­¯K¶Ÿ`s;Ï@9!$ä™ö´jÄ'ÏjßL‰äˆxGéœÒÆšB¶ik=õ¶³Éà/ƒ;\# JÆ|dMÛ—ÕO´QåÒŒHXúE3— _?Ö×t+Ï«\mY—tv×]7¬"³é+KEÝý–aÜížÊÞI(æžË´u/ün­ë.÷´"?$gÒÄÐæ†l ùò6Þ—Š?Ioç4‡÷"qÞÖC}vƒ]{ _Ç}•C;É Ýi³ÑÇG¡•XvŸ´+‚Jg”ùX¬çŒ7×vµA-TV$lo ˜Ë•G—(Y«—”Õj¹zÚvPaºŒ3à óÃ!5$”S¢Ø8‡ØÒ22ò!ØS»@ʈ—~˜±ü ôí+?`éÊ.Ʊó¾ÏŒ^/Ô¯F€ÀUnôôD ¨zïhPÚvÜ|¶šoåÝ!â_I„hù_» ìÒ¦ëeà›ú–ðÑDc¡xònåJUò•+eLϸ’läP6Â-Ioð ŒåÈb×ËêkS®´¬˜`ËUó(5¸ýqÃð¨IšÀ?>Áx"ïîkB’L7 ½VwÎÖÄB‘oŽmë¨ÿP'‹p²>W¸ÄÕäõ\¤‹ò[%ãÃÞ³7“²:ý.} Nš{Õ¶,°ÞÆûä/ÙühÄÍ! ºöN‚ÉÁtoƒ“´¯É=혙„þÈ•°™qn€a¨ë'ú²ùõ@¡›gÚE¹OêT+jIRUBƒõ&VÔÝã:æ3 ×y‹¾pãµáó|¿~g¯I¾Ð›Î98"LKRWa •ož5q÷ü…‰9ÿÉŸäÈ÷WZ{³V>«¡˜*åHL<·RP}{Xªò—‡ŽÏ“Óz2IvìkDõ/8\Œ£M ¤Í좫)E\yvçŸï#¾Tã9þ¬bJeóeÙC"«³?æèÕQ¥€ûÑ#¡ôιòÚÓ0b;óÈ×FEå Á¹²§:ªÁÇG·;Bvñµ‘„ñ9Z[»z¼ J¬câäæYdP6À 5÷eQ]…©mü½¶¼[ïßäÊåÈ]þÿav¶ÅÏ~ë Vƒ‹¬{Œn¨ó–JÛ¢¤Š`À,›°°û,›"€›»½TÌ·Oá¨Kýu„\2àÆªUÁ €3ä`n:5X1öçÞ›k Ý'Êecÿ6ÂÆˆî¼Ìt¤õ¢Ë´$²û@‡úUU˜æzÌ´,jõ6ù$Û®èÈ;##MtLÒ«Ö"'ãö;ZÜÙ3øü˜l®î¨ìŠÉ—ªWfhý²ŒŠ)ëø¤ Ææ ’„èRÕÛü*ÅoÜiÍÖ£ƒ®OÉ)œ¶˜‚N9lãR~ô›BæW½KYH< ª]áÛ=Ó»^d[<ï”~×ðf+"‘<[@áâé¥ À |Ì}zfpæÙÏ8|o7ü AðÇ¢”æˆì®ã%nK%ÓÝà €d®¸ô*9Vù\«ÞÀ™¾]š)¼Yª§ÓHêÚƒdµ¡¬µõ²0ÙEŒÇ|sºê;a¤×qÍðškk¦¯>RK ÒÊðáz~—ÕÃñ¬@TRïí„ÐüÕñº¶•KÈVGJÓ ~P¤Pì=ä{Âo“‰:ÒŠ¯é|Õsh.|U%¸é‰¢èÍižx¸°7sfâ½ÞCtÞþCïtH *ÞÖ¢s;²£¢s?XÇØœáÞ1_wªˆ¨Ó9õ n4ÿÓ` ò9Ø‚êV¶úöù4Ôã¨0ïR½4ÏÇiX !ÿ/˜¸' RÖÌI˜N6yF•+XÍ´R[F|ì%¢á¦dZ¾"ü—¶\ºØî#ãôç|¾j !¡.%Ge‚w,?#÷¥qeµº7`ü_.»^ë¾WzÓ²ÛÇ›‹ðÁ¹ÙÐßa /!K¸®±{ËÜçÎzL”5½ðÖYe˜‘ãhòkhg4t@‚nŒN””¾°‚¸>\• ‘üÕZF—}%Fù•å:5X´ÒXødÌúhJ ™)h™1ï©-(Í÷ãÇß0½¬Ž<ˆs’ê]@fžl2tfv­`e þÁrs:ò`¡ò¾›á†»³Èô‰mÆ´ÂÞ†´Ð’(ðÃáƒî†îŽi µop±'¼Œ¾Ê^Ä\ggAw]xt/Ì¢@GE ›±¦Ó )].ã¢ÚëÃ'9 8Þ-ìr>×f”Gç?öžïjÕÒ”¯ ‘ž2IQù^ЀCnÚÌ ‰-ˆ}0‹·ð߬—d¬>H ŒQø¸¸Ù'ë‹á”‚‚Þi¢H›C`VÇ„V_êw°åXYŸ¤Iù'VnÛ#I~Wéý#aÿÀx@|aKþËc…ÂÑàž-.Šú [倆‡19tjÝ{êhH•­aâÞ0ÙZÝ1‚"í<œ™H«ZZÓËW—‡°•®UaZù63ÑŠ{ø¦ÈÛHJñK˜rm°!Ÿ#6ó9}m&õ$.?Y×ñðe’ª Wî%›·W™ë Û¿Á©}F%c5߉Îù4÷lîIGç~ç?]”BÖ*•/Ö™_Op×7¹à‚°3ÏBú~홀”ÒV’Ú/‡vnPËÖsBypÛ ºe€8[Pª@L¬sÀDZzá» ÄžgSxvPF×[#ªÐìn™ÚçÛbA«{ûß %œŠø ó2Åø6"qd:ÃLMm“ÐJ»r^#J»÷Çø,m\µ·zê-d &º§ªè9Î Žñ€Þ †PBȧÇÞ%pú¦ `ùAΕÛðÖC9p°_h’:)xÙ—Fpw5(­ÿøús פAvÝsm°ïæ½µ§\÷zœëGNH•|£DàJS}eJ„ZeÁqqD<ê‘°# ÑÏíeUúML#¬ÅeGÈï¡Aâ_e«Éñ¶sX+¢ ×^ ¦Tø¼ýøéV㽯ÀÊO´Ò ÿËú_êþÃÿŒmL \ìm ¬¡þq8ÁÙendstream endobj 19 0 obj<>endobj 20 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 250 333 420 500 500 833 778 214 333 333 500 675 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 675 675 675 500 920 611 611 667 722 611 611 722 722 333 444 667 556 833 667 722 611 722 611 500 556 722 611 833 611 556 556 389 278 389 422 500 333 500 500 444 500 444 278 500 500 278 278 444 278 722 500 500 500 500 389 389 278 500 444 667 444 444 389 400 275 400 541 600 556 556 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 389 500 500 500 500 275 500 333 760 276 500 675 675 760 333 400 675 300 300 333 500 523 250 333 300 310 500 750 750 750 500 611 611 611 611 611 611 889 667 611 611 611 611 333 333 333 333 722 667 722 722 722 722 722 675 722 722 722 722 722 556 611 500 500 500 500 500 500 500 667 444 444 444 444 444 278 278 278 278 500 500 500 500 500 500 500 675 500 500 500 500 500 444 500 444]endobj 21 0 obj<>stream x¬ºeTdͲ-Š»»S¸»»kãî^¸»ÓÐHãîîîи»»;4î oï#oœû~Ýw~T•3"Cæ«*)H”TDÌL€’ö® ,ŒÌ¼5+;  ƒ¨ƒ­™Œ«±­•)àKÊGA!æ 4vµr°7vò4fq )€•ÀÂÃÃGspôr¶²°tP««hÒÐÑÑÿ·äŸ-¯ÿÔ|YºXYØ(¿ܶŽv@{×/ˆÿkCU àj ˜[ÙbŠJÚ2 Rj)u€Ðèll Pr3ùç,rV¦@{ ÀÜÁð%øg0u°7³úçh.Œ_Aˆ¸Œ.Ž@S«/3 §)Ðñ=Àèlgåâòõ °rX8Û»~åÀÕ`eojëföO_ró¯<~8:;|í°ûÒ})9¸¸º˜:[9º¾¼*‰Kþ;NWKc×|»X}©æ_;ÍLÝþÉÅ–ñ?0_ZWc+{€+ÐóKè0̬\m½¾|9:[ý+ 7+{‹ÿŽ€à ´0v6³º¸|Á|aÿ“ÿ>ç?¦ÿyzcGG[¯Y;ük×Å`åê´5g„caýòiêúåÛÂÊŽéŸN‘±7w°0ÿ[nææøŸ:w ó¿DýOÏÐ|alæ`oë0šÃ1)8¸~%@ýWeFÀÿZ‘ÿJü¿Ràÿ•òþÿ+îÿ¬Ñ,õÿëñÿjžÿ'´¤›­­‚±ÝWü‹a*vÆö€xðo¢ù? Œí¬l½þ“ÿ¹MøoÒùéêþ ,boñE ,ŒlÿîD+I+O ™’•«©%ÀÜØö‹þÕ¡êöf@g[+{à×Äþ‹¾Œ˜¿ºüŸîý/𥕩ý×`8þ­Ú›ýNÿgDþ7ÓÿA¦ÿÂRúšiW5/G à?À5å¾ä?ÿX‹Š:x|X¿¼3°±²xx8<œì~ÿŽþ…Ãòï ÿ1•7vu¶òè2323³¾¾ÿóóß+ýÿ#aoê`ö¨ºÛ›}ñÆ þãèVNn@ñ¯³2ó°ñüË—©›³óSý‹¾2÷ë10è 4…;†]1*ì§¿Å}Ý´Ê ÐÕ帴.àH±Sï0H¢{^Þ ¾5ÿn•åÚä¼c÷Jñ4xqz;ÌÓ´Qþó’v÷“ŽnÛH7æ€RaË\„A‘dXvˆ´ëÝWìuàþP›Ž¸ÀŸùçr~1'¼¢½Œu_Š1¾@‰U9Ç2Ð^¼i×Õ#DÀ‹™&ÛžˆV÷Q[Ì6|:puåEå3kÓ_;Ê ð DWD 2ß.MЮCégûn…eeþXDŽR!é²ìãÓ%SmùÓŽ¤d›ñôï6DAƒè5‘óžn÷`t­€9%—T§?k¼7df."ŘýGËÇÊ|>ôD~ Š–Îç&um`×7q m‡¼‚óž—ßÅR–ú#M9O,ª@§X² 7Úå¢ÿÀüo Þ×W¢Õ`Øw±Ÿ(ö%ÛºÅÞ3ül"Œì¬ö(ø4G¨ãÒr²d–ñ\AºeÛáçu“VdûÁ%ĘoMC/ëÝ]æ)qð]OÅK¦†r‚„žP¾›6Ð}ì‚#£ {Swû—úF›0ç ˜—ߺ¯.…ËÝšMÒ¤™–ÍÎÆˆn“rÝѱ‰ˆé‚@ÛoIÓôëáfÈ•y÷kŸ¥ZŠâÑT;^†%™Îûû( Ò6SNŽ¤Ã ¬óFå©Ú¹s}å>ÈZ¶ž‰ QmàÌ[õ”49åë¹~p°¾Q„¶­NzCô0îÞILÈ&Ó̈́#ö[¬UMüdkµs±6~êj V¦—ivS̺vc›­ÍþNÑ»âx?BŠÇ¦nYâ¯ªÑÆZg2©-,%E7¨OÌh@½)«=;F·W ei· · ¶)f5îCßÄ•{NÙ<Ó Ô$¶bQïoÕã ãù픂£{+"J|ÉæÙvOi´œ˜‚—þ±ýù¿%à«ñø£G*«—¡{aÓI­Õ´ýïÐÞ¿8pâDl¸?Ú”|b(ý8„o§O›’ßÞÖ¤ÚkcA ·æÕ„RÔptsøî‡ü=M#ž¡5Wð‚r¦m‘“EOÀ,Þ`7Cèäp{9wÓ½¨ã —Cv¾IYPïek.ŒpÒ¬§ƒí.¡{™&3¡Å 'µšA±´øùAxvS"ïQ4KÙ¤Ôn0oG·(Ä Ì³{já÷µ1odXólOb9\Ûüé>Š)=éíéBÄsaþ¯n’©DÝA¾²dôýð”¤'êœéàPx# ŸïÊE þN)£KCe ÷P<³d3È|—‚²¿©m|àc¼<„ß%qŒˆêº/*-GšLÒˆkž|WË„ô/H½V–ÿsog§‚JieŒ”nÒ‰zÇh46ð·XÑÙ±>ÀJ<¨NÕ=R–ñyŒ)< Þ¦Ð}«¢—„9¡íívMXpší.x Ÿ2…ÜFùíFú]ÞÔ*jî²RÅß@\™UgÖÞöéÌâYöA X]ŸÕ@ü{c¬©ü™È×™Wóù€€ïu99Ö¸Ÿq.„ZÌ®ED'ÚHy‘ásdVVù]èÚªRož~ά‘àÎ+¯6[*Ô_òˆ,9ê¹¹LÞ°ðÞ‰yrê83PISåÿ‰Ã8cƒy5Š•¬ÉìOš3ó3\ˆ1D¾´Uù¿³i3ûg?M³e±k+Çgd*™m|ë3ºbb”’-Åã—§]+J'=õ\ÂäUÌGN ý0&‚BÇS‘9TÏ¥Q9ñ_U',ïHr¶ã‘)}ÅëL+T²F©¢Õb‚;- üD¶G;Ò¾ÍéqÔ’ÓeŸµÌê¼´”fTáÞ§¹;G“­ãž^¨Ü7ñÿ Ï„Q€£~Äz@.óc §èÁñN[GŽ•¿áD5ˆ³ºÛ¾J²Š3ÒùÜ6¾+•šè–׉ÂbóÀhºß«ÇŒ×ŠìÆh:eÉzé,UHèÊBšQ¥Ä‰aˆ·„5¸T“fÉÝ‚£;λÖoÄ þáðkøÄk_ÈK#eƒ.FI ‹Lnn£#¨9T]“££¦¡ÜÜаؔøøh(±Q˜ÄyOò…<ªš¨ƒkìî‘[ý<“¥£96²Á'«5–©1‹À äÛJ^?ƒlMcýgb}Ó@i.É«•ŽÚ“§¹}ÝŒ´‰kIÆ#¹0º„VµGª’±úv1!`CB}NþÉ_Líàv…Á>4Pr*⦘–+ÆôÉ'‹X`C¸OQ-ô|تhÊ€;•’ôÔ Xr¤$•“€›ÎC¼n˜‘­ëä/ kÍeé±únZV|®›'F†#·T8äÇ$=¬Ùê#ÉâE)5¶# 6zTÎôû`ª3 [RžÉãàR$ýù-6€öFH4i†ž{'añ)À‡ I™q8lþ´;¸_9Ú–è]õ¶è©ü#€ŠŠé(&YæÌΆ·J®È†³‹Ya©Àšf6³˜åú®,ñTû;e.n‘èj> '}KàÇ•ø‰*ý"Ú©ytl‡C‹ÜùÌ_pã,´FUûiÃB7x¥ši|Ó´§³Lrå ñNÓ¡C„ßßzË „ŽøM–KY½óÇ.ニèù>§“µÃ¯„aÏ n×nÐ ÈGø ØÃèCNÔSë í$MÛÁµrz œöÜÅpm‚‹È‡°7›¬”¤<Ä©à¤ÿù6™µ8ü³xˆ ú}€}W@p¶çq#£F w·IhVK‘O™TFÞSk­ä‘êöpT›Úžž ä î4·ì|ÎѦdùM!La/NS¤¬ç}µ9‘=S±©¬&ÙÔ„J]â§ Ny¼H1¶Cãúú!!'½YÉÑÚ–#íí±]áÒÇ׃ï$–/íH¸ &ÖI8›njn§6iøsvrÉHeAºZ½U}jºíŽcï®—T Ò@ ’‹'[ƒbhÓâáçÛôÁÈ&j“×ìë…ˆ]ŇÌW;ÁIÄ»[6üˆgÞ»ú­pÜàÁ–Þ{ ´£B>~FAþ9¦hÞš”¹»r~Ú—ÞÆ†ÜnÃÈOÞáTøÒwËÑp é>°Ö¹Þת¥ Ê [틊 Õá÷}gWö—r­G@¤£uëQ’ñvhư¶þ–:æ Qû2Ä«AiÓ{fJ._Ó£ç8kÝ<Ä>7Ö²H08&ÈøÈÏQaá–R °Šð§dã™Ý‰SmY›°&/èJÂdŽ ]sª;LkkXó‡½I”ø"ÈåÏÀqJÍ2d‚7êua·'üSí[‘Xµ1í¼~Ü_žë,Þ…04–Ý*w¬4…ùùñ}™[Ç/ô¾Œ®µ`4±Ë*Ótìëæ‰GÃÚG ~p‘"ï{€1ËÉ"¥'BŸ­¡Ï4Kea] Ž{œ‰Äw±O?;º—Æô•¡Ÿxã$Ïë.P)[LÏ|Oôú³€“®šš2϶-©ñ{²ÏB|þÄ¢ðõ:¹Be©;1 ðTV†Ö7 ævâê?™ÜãrÖ“-L#s=¡´€ƒäfk':| <1ÄNï¼~om|U’chÔŸß^{ÇŒ¶KU‚î±çÛeJpðhà ƒ¯4©(Kc¹ÅWĨ+9DÞ6ƒá-ho4\w¾R»òRmyúŒÒ·Ú°Íš YÓÜÚ³ÐBt,¥ò*Øš„¡’Ñí! ÍmðE§ÎÛñ‹áHxã1Cq•d:×S´ù?ê?tXä´©zôòûñ/‚§tVr­ûÈÐRž|¢mwšî ýÌR=¾)Éfäû•óÂåüµêÔ[¥w]]#À·"g)˜Ÿ'.ZYg×¹Ž7ºÛ:ÝÕO·<“<̵)ê”û#øÊM† Ëðô 9È]üúAšyáúFËY°?˜Ä.LæÁÎIâôöÇ»Ç÷â‡Íy‰ö¹”“bï÷|YŒ$AÛÚàs ÐÝvs`Ÿ]G¤he§K7=5³‹Ž·jx‘„ü7š³_&;FR×üÑ6ñ‹e[·‡ÉvÖöœA.S/¢¶ƒaâ®ãi%=ÿh›Ó‚ê µiý·«§ ûtLrA´^cŸ™ÀG¡¨§;‹FML¢éPˆÜïŽB¡k:½£óê?o-È·9ˆ¥aOPwÇUòÎéøùŽÂL síŸ"ÓÑP&duêñ±'î©ãPäãk„­‘UŽ+ƒ ,³®.vODtþËð}ºJʲ´µœPþÙ|.b„`°m”ÜI‹^µÐÅHz‡$ò3e®[?¡©…ňXȽ¢BŽF$!•·¬÷£ÏÙ„¸2y{TŸC ¨ÒÃ$+îÜfêßV>!Ó0úÈÆM¤žñ˜:“:kc”õÄ5ÑÚ¯g9<ÍŠöä:‰û?Äþq½SSø ©WåôßI9 ó >?0ž®×Â9É*e;I L…ëæñÔ^9Ý¥ Û:ÀÈÀyÍδ6¢ð—Œ:¢ž%ÐEŠ}|mo:,Ø?jÌ? ÌY…øÁNÒ¼pœ*#<¶wÓÎa†ØP“=×nä6P€Zw«}t–$J7´˜ÒJéÚ¾™«±5o ­.ë„WÖ¶äkåÉ7°õ3Ô ev«ú¹¶oØYpÙÔü†BÓçp²˜(f]ŸÇ¤,GJ >zó¡üêµu‡òGëÚö€Ô;™5OEA¦hy8ñœýÑ…)"6 `´¾:y˜÷B!ƒ¯\t(ð×ó7EÇò¦;>³–ëb͘D—¤Ÿ‡Š¥ú×T}‚¥cDÐÔ\Tœä$~ÛC…n]UšŒÆÉæ†t‰U•_^1“x´·|T‡ròªÌ˜šD\Ó>)×åöXš:Æ5>žùû«¸‚ÖcZ¨êÿX¦K#øy±RØ|ˆ¼­£6븿æÄ„]“Î=B]!› è °ønºb©9Ê sT}ׯxÿ½¦îÙ4TÞ¾{Èä[¯·ÉãÙsÁ `UÊàéx²¶@pŽíÍÌ ZÈE•Tíy[¶‰ñïñá×Ýô—gN-Ñó2 žú/.µ5…À𴇾¸¿ÂNuüåèZ­V‚Y›xó9»nœ$E¢ªÅ<}L†Ru$†¼‰F[Y— +‡tª†ü@b,Ùÿë|qÞë¯Ù¼œÆÁbÜ0Ç4#uO§~?)„Gª£án«ÔòÜ?í÷]´O“Õ¶ÝòÆ o¶óWˆžDNƈuL>9*0ògŸÞCÿu!ÿ«ùÊ­bÅ1K®AP†‘uÚv‚\á² FKÿùÜu½ín}à‘/âf-¸Ž¼œ^¸'N ôc :.ðû¥'†ÁËrç7ˆÈ01ìöÖS±ðvî'·¤ì+¦Rº]¾šÇÝiÂE#Pƒ3ÙtºÈÓZñµðΚÉÖB±î£6«E±ˆ´íÝò'Qs› wþ;g¸ñì,?Ÿ|Rd¬?3tž ÃW¬jì÷T,„–LD8úWÞŸVy}³äVë¶ç³Ø^¿˜;ì8ˆwŠ®H¢åõÐ TÂßX•#ºéO´šßcDìÓ²ü(§­¯&åqëÒ…4VF4y üE}í¿ŽÞ}f¶#NnEù8õ3,§z×jzÉŠ×ó‡¹7#¾4VUè”õ°¢Ñç9ßybËàó[Älþ÷-ÅD ‚ª¡¤KääÝúYE¬þ4ÎWÁtÑ_sý™|%e°iPµUÞdê-g¦àn>ÇlÓ“5ÜYPf&ä§aÁ%‘[Ù\m^iýM ßw§ç\Ƙc ó®!Çÿú•[l³${ë)ô| ~Â' tkÿm®¨çOç¤À;Ñ ^–GZríHmãu¸`zå3|º©¢9 -'WG‚@Å@Ùo'‡úÁP*ïëÈ£½Ú4á ¸{@-|šÚõŽÅ+†>KÓÜŒÑÝ2dMõ N!\ÆÚ?zá¼"=ÔÞ´¥¾¦¬‘Íœé€t“D=²mbŠ 鎗è{ªíÁÂ@m5Œï0ãû!Ÿ¥†óaó¸XÆÃP˦öJ±Ûg­šú Æ™(‡MaU³²œÄŠ¥ùs:½°C6Ì1 KzªÛ§…z™ç·@¼á!Û”6¥õ£ãll&5u³ÂXìúÞØÜoVé)<÷÷‰¿ÙÐ`€UÁ’‘ô5· (°©ŠïÆ’,Ñ¢¾z)ÇÕ¤ìg:™M(* œÉ5›ðfGR<ÇòD‡ú«ËuO¯îTsÝG04YÜ™*üaA’cÐ*¶_îÖ)à#Á~¸†¤N©¤f-ع“ãÄÓµ½Èo6F@{hGù-“£'å¡g—þžSVŽÀ˜D¿“>OR¥CdZV;ÈÆ©ÓV®«O«£– ËçOF¿ó—·©Žs™g þrÕîK¯›¾Yó¢×öû+=ý„“‹ˆK?¸ÁêЇ€¢¬ì NÙ®»lü*x[g~Ö¹\¸â•ò·˜Ì¼¡d½ 㺟‹†ªÝô_iãµ-é|¥^úì3CÔ%ùŠ‘:®ÂHØzþǽí¡@³ˆâ³ŠÒ“vI÷PDäfO‚…tðÞ²*lr|ŠlÜDɆI$ åð1K–ÔÄ\¶3¦x„)X•¥‘^Wz9âÓ ŽÌQ[Ê]ûuªv¢ûÅ ÆÓ8p ò'{º(Õ_Ÿ¾¿|\ß—3ewXlÒ™Jùßbµ–ã‹çw }U|Œõ=š0ÒÆG;Œã!„%ß§I'{eíM³÷Š úž#e½ýŽ˜ cøÊT{j4ÿQ4†®ª ÕÆê¹dj¥çç`íÒ«ÚCŠ­‰ ±AŒYwùü›½ jñù{m¤>ÿPmA¶æÔN`€áðÙ»›£aÙMšo8¢žhØ ±¼Dš‚^Ö’ÄšÕG^¨ ²v.úÑ¥‰+W RÄ=¢ “Ç–¥BêE¾Æ_vlS©ÌîhmIœnC—\ù¶öÍÁøÙ—™ð•ìrÍq˜ÄÅ3ÿÄåÄZO×WÊœ`ÅÏ;t¼°¡Ì"=OCP¢hê=NNx%‰=—†R¢$ZL{-LbBgw[P&;N¤ö$m1@Ê“ÔÎÑV­ÒX*­ó}ñªãƹ¾à/ %¥07'_/^¬eJrÎFFõªäïÛ#]4Ký4×#œ+žVÿ'T/ÆÝüZaŽv-°P­wƒœê‚㣩ñ{½»BÒÇŒ°F*ÅÕ bþΉïŒaÞ˜tßÙ¬ÜÜCü4ÊPHÍœÿÂ;#•ºx¤w`¶Ÿ(ÄqÂç‡û¶òå)mHJsC2ê̆4ÄväèhHbnïâ"P·ƒdüLüì'õ›ß8&Ã9q˘˜ÿ(sœúåô^WÐ;ç¶Ÿ×ÙæŽ_³¢|0$cnRøbH‡ ždj´k÷G:ê”xé§ùªcôƒ6ÛýŸC ÒvÆÝ z´É¿äíÚbŒ ùϦ^Xê•NíÏ+ƒzßn Ï[-×Îcèƒf~c8d©¥¯©’÷Ž‚§²\Ü?¥øÕÂ6ë”' ¦ Ð_‹ßFé “¹û^aü†‰vNç•Ô^ë§<ïd[>bØ!©l„`+ˆkúŒtŠJ“‚9åB“oX£–f¾¯ w e½gÛÖ!dÛÈ^¾Eq´—ÈZ}³×­Xßú땜§ôå=ÈQ@rV˜Þ¦Å²9àfè¼ãɯÊj©3ÈupfâPÄ/©~œ¸6€«#Ãú×\!?YQ‡|¸€UÕ¤8[UWod5¨tm£F¸w iÍ™j%ÑÏÝ•´L?ˇõ‡ûLJæQtM½ê„wæÒlöôSÐÚ¦ö‚h%öÆÆ"fö “bv@NÌG×ÈÝ·ö×DbÃôeVCÀ³÷Äá!j«(SifoŽl¥^(Ïœz ÍD_©qc6~dÑOîõ!¥Bš †`Æ‚ÂÒ‡¸ßÞ¹pxTI-KXÉתãBÀ£ÐAûÇ6@²NA2 §t²¿”ƒº7F®Ë)aí¶ÔiÁã;‚øBØDšihÍN§&MwçžÂyŒ›…*jcgÜõkhˆA'Yú:ÆìY$°#ÕŠÅÙïŠjÜÒ¾p  ·©1/‚˜´N¯¢›s… .FL¿jpk»ø¥ˆA6—Gð”A‹WÆÊ¶â‚K9î/,rLOȹŸŰ(„ ¦›è06i˜õA6”6%Ü/ÃQ²7•Ü*ñ¸ßÐN6á‡)-Ni©Zîc‹tÂ6meW‘–Il¥(œA5°úègæ´÷|¯Þ„V“@ôŠjML>> SêF?r·ÂG¢Ç¾<ûè^¥Š.@ˆÚA”kh–Uºøè2|ÎN~k²ÆÿÖìòÐäÁ£@k0ÿÜ­²fs¼¸ã[DyjÒ²ÎTÕt$9ÞÖÕzñ>–tÇcû• ë%bÊ-E 0ü —:0…€ÜTÁy}ÞHÐvµi‹ïbeo j:½Íò%V™L§a&”±õ\n‡™éê#«ÙÖ¹Z°Ý-gùqŸú<=ÓUpÜÁ©ÏeòßsúÃ9FJ¸ë±;g)¢ Xaª‰îC’O4FÜmß̓^1y ^or×Hõ»%¡=`¦ë'æGõaØc­yéÃ,'y_óú–»l(ýzpë(ÝÞ"æR*rï’Ûía¤ê(Ba# Ûö¦lït·sÄsòx%*d(£0~ö¸ôG;*°Ú8«µ#óqÂ`¢÷„ÛEcdöVþ\)/"òñÊæ‹]S|T HÔ»ÞÛ6k½ßdW¥l4ï£bê:»ó•|ñîγÇV™ÍFj»ÚÿLKÂÛ#4'>bþGšdHGû®¼Øc€þ; ð±LÿÁU°C`p Zpfþ®íÁ’4 ðE4P2•ýöLˆÔ¸F0:m,‡wŠÛÞ¥«ÅÑ[¶[Œ×ãg{ÙÚ¼ ¬Ô;–4ÿ'Va©Ý¨™¿f£x‚tÔ\·hÖÞÇ@˜•Pà#ðÃús ËÔ¾·ï Áê+BZ2 sÌþ÷wøÞÅaöåÖWhXq„]ïíô¨kͱ6~M=N¡ 9—2(–Ø=ôþÝntÇüÛ¼?ÙƒØ{l€hG?.£>2 ‹Ò¦ç:,ÅZðª)Œ€×Y’ëp³Ó€¬¡þ_¶\ˆ®SÕ¶ü‰½N. 1N²ªá+⓾›b#Üäç;x>fØPÈf<§“%hMÛŒÌašÓg1wT€›_NUv úêºð;œºÚ%N†Õ[ OõvÀ¿4|C§oì¿ :Œ· ÿ-\UÖ7¬`ÔUªröŽˆö± }â¿Êd[ÝvK¯W^óg HøÞ?Ë£x»IP&À!¢§-±Én©%0ƒ“à!ó¸;S~t8Øwɳ=1ýž=Rœ~[ å1ZÆJyQ‰ßþÊÇ£K×û„ç?•½† ¨RêWi ™KŒãk∣ù}^öÛn*K4.Œ„ÑñFÓŒ{B‡Ë^Ý Ûw ŽÁôñºD9ÉÒk$Á“{÷u¢ÙÆSŒl¦™z³µå:kÖìÒY¾Åjl€†å˜~¹B4¬K0ZjìG¥ãAýÖéùÓ“%ˆ KJ~±å2C ¶ÙŒ:ÁQb¬¸&íæ©•–®ü‚ç'É¡‹vµsqË}-nÃÃúµÚÂKnŒ þF]¹Bj„Ne]g?ô>ó¹;¾½þ²Xc¿&ã¾É4ò× ÷Õ¹M¬´œ¾.tÞ0*ÕÉ ~ƪSádÿ…Qð±,„úõ1›í﬇¿5ÔF©è“Å™Y<*ƒÑç(ÛmrgÆRÆóºg±×ì*¾I3¸$#î­@BÖBA~îÏä?›Êû/ßâ…Ü&Æ;Yö›þN­Ðº¸Y)ÒèçôÆÁ¬TKezÕ”4oý¸ `ÔÿFßþWì²Ã­Ú]üÕSf^åmñV¯Lñ…(y&ã„ɈRþ¬:‡D¨Ú¥Ã²ƒºßf‰~ ÅGµ,ã%„YaçÖ¢3Ë0Ð 5xŸA5ùCEg‡‡[‡TÈÅ”Í-K–Çjö=GDàóRpï˜Ä.m‡ÀÞ¢W.üÒú[Nê¾61?¬'ÈSú·‡%•½Ìè*†üY‹#±×P(qð* èÆê6ä¨!þYPßÄY*éË:gJOW¯Æd‰[ø’íÅPV¡Fÿ¬g<Ú¸œa·؃¨ûŽ€S?ïLž£Œ)⾩¢Ka?E½kÅl°N1;¦É"Vn>—­£þæ­&‹a½]TÀ,&5wm›àTïxÖK/P!.›ÅDÔ;êDßÌ,‰‰ð¹;A+zf˜­¤+;Ã6Êea®*³Üš•+ÛÈK¢#Swm¸¿Ò~s¥dð ™3ûúnÄVþ]ŒÝ”ΊfÖF[÷MâÃÈ8vhûàžÅSÕXŠD)õˆÐ‹|ÍÀ·eÉü2œ<çïÄD Ag[Xú-jö^J¤E[w«]¾írKà|’¾¬àîĻ΀ ÆXKRîiä•bXú‚¾ª6ZO‚üÛ 'Û2Œ¡°/z!LïP§†k¥n¿w׃^¦ä·ìüµøD‚µä³¤Ø¦dxón=©½LUCÿAÜ[Y %4œ¤¶ùŸ oxã¶ñ< àH±ÒýŒÔ‚•p¼Î®Ñ^Q:1ÃU·P¨ÞðWr{!SÌðâ{™ýz\)ç“?4Ì5 {ªä×*V(vÎM)4#ùðB|Ó÷éĬ&1gpÂQÞ¦éÚ¡HFª\=Ó‰B!ýPÜ-WV'ðÑÞ¶ŸÁµ—±Ç‘áó†cwÌ2ûE)ÊqûÔ=pǤ[Ð68ûX´@ò˜ëƒ;ÀHYº½î°Ëv ùÇ+½yE£ÉÄÅ1„‚i ­m‡pò½DJ–T~GÅmÛyÔcÈÁ_4‡˜ýbCNüO»¢­FÌOÈûbÓ¦ž§ëûÛ ¦æ~]"T`W{„Iü¸îÈë{,µ­83ö¿œrûWþãL’ÆŠ¾i¿œÁ`WT—üÉ—U‚>¿Þïg†Š îï L5Ò ô·Ý*Œ¹¡qâ«™³~VÍîÊLRºW›Â4 ºµ¬xað“eX×îÇQW6åMó´Ž‹µi冑 šG¯Qíàô±×­ç’RjdóŸë‰‡îùš^K»/Höå:€g|”¹†Kß4ªâ¬"êD‚?Sº•¨Uê²ÀÕsJrñt×Õ‹“ñ¶Ìü× oggœ£îŸùMjä÷.Ü8bÌTòí{â%nW¨Z]QnÃÇLáà!’è<6½3]ܾ´)±Rwƒdø ,{ÝÍŸ©„ã½¾±ŒY¡3¼›¡]„.dc!‹r³|;Ù|ŒÏ-©ŒHßÿâ×ÄœÉ$B:¨ªªœâjXS‘ÿÉÒg8\¨HŠ×·lBæùñX}GpåX5¼0r åÉËÖLÊ ûVœÃV•Ë5”¾eàf €¡b„¥0Z C‡'ñYPêÄw%iîœã…•~°>ÿày(<»Cž V…ž¼Ý´“+açU£UUÒâ$£ä‰=Éb~–L!‚­±°²˜á½T»þM§ eº¥mÓ̽ô²éèÀö >YYÐG-ñqÿÒJª3ö½- ‡¦Cƒ¿-žz ßѤ‚‚ÖˆO0ù<$cEÉbs͉ÆÄw³_¶‡Hâ6uá²ëÒôL®…ê¾t+‰ÙªmùI1`ZÞñ‡*+1¦äŽÁë¨XÈ…£BËo¿ôšé¯?ôƒ$‹ó£”Øg'[Œ·ª %ý8ó@ÅV±5¯•Av„y#lúVrÕ¦¹F@™4et# ùæÊò$OsÑ™fÁÇ‹_]˜ñ¬TÃì^T½ÜGåÀ3[z“fWô–'£ˆb´Úç I ëktBQñ+‰.¡@#ïñ$.aÝKæÜ¹¯@oCVÑ4Ò…©Õ] +î…ñ+ÚËb-…¸N(ÅÐ]'âl}¥:…§•qöí’"aÛav¥z@¿u×ʵ+{›‘X÷¦é“É„í̃p+ “!ÝŠÂ𘚬LU~£+Gª€e†ö<ã}°·¤ÉÀf]kÖ|ø‰³å^x‡ô0m"¸\O4E™g ÞTqx¿ìZ]•k_ÿ©!ôà}®¾ëºËèµ|'È^W…ל0ÌÐÑ‹•LÈÀ†­.HÅ›Íl^_¯ópV4–‹éa'umÔþL“Ã`¢@µ´zo÷VÑ> ¬5Z†FZúæ¡Â’™]rR^oqè@énÝIW{ì³o§À«Ÿ‰E½†§ä°RššÙKtç˜;¬½çT׉D`ò‚~FZZÞêÝ\mºÉóp8˜ôüýê[$*ÔÚoFü…´à_TQU¼‰¸CÆL=Á®¡Np ü’¥C}¿2ªH“òÊï~ÑY‘-9]'‡PMMÆ×Ù€ßccí– ÉÙ ^yä´Í{‚$?pý"6‘`ލÙU]°5šèHÔɈÛ'ž®²ëÈí]ãi&•Á2¤³÷YøŽg´%´'Ø~ öN‡ÁyÁJ‰.·piþ£M‡ÉãB"›ê8 9¥ ѾbÕØf(o}Ò=åuÜO,Ñdú&ÞŸ d‘|€×]T‰nÕø£Ô3.Íù|¡†U±"énàÑ·Q£ÌöbÞÕyZU±MYÙ-£Ë-{CÊ5Uò Ã()ñXycô'õmô¶À†\„3Ö@@ ²û>®Ð¨—ÓÉ_¾bÊ÷:ïVí»®?b kœ¬±—Í#£‰©ôòùuixF´¦“0ôbTúÝaÖHq  B0˜'Z˜¨ó÷SD†x“•]!&Ê"½åRæÑZàVË¡wNФ‚‘)Ï/£Fá ÷Ç—%†9nY*£(¤c[ )šg|ßq9r’Uã_ýŒ. ™”¯ÃÁ°‘ œÙR¯‘àô´êÿsýÚôq®=þ·IßfPûd"f…^^2Örâo@ã³O ¤àÉPä­‡¾1ªÖÊÖ†³¿{Ÿà‚»úƒVó§›AÉR˜‘ì®~ÕÁ(2ôŠCµ'õá>ø}a™"YTZk Êïl\*èúvk&0Ãô Ü®¶è"°A°„19)7­ i.¤JY4^MÉàÆó3J 1›VùºM<ç `£sÄä®39™nÉbÉŠ¦X¹õ»æ™÷­AB…°¨ÚÐÛlÖþB¹£Mé½hu™þÏ,GùX=X¨RÇŠ]«.7pŸî©îE :…ÒoêÚ·êæU4 8d ÓøªìÛ‚ÞÊqÝ…‰rÅj…ÙôúJãmÖ õó¿TÛxŸ-Í- –Qs©p4:À^¥ kû”hqw»Py?Á+ó»õ›ïºÌ­Õz «¹f\KGõô1~°GTÔªXðpÂ^ödeS‹WÜù.9Û¦D±yÐtÄ—KÙëјÕÑö‹ŒLàcf¹J{Z¦ÈAÌôf«_2ÒEÚñ+ò3¢û Š¡kkŠR¾£úKklxrÎ-|7õ¨Ãžë “só€APüÈ%žÚëj÷´”T¸ù–…ƒA`–:d¨&÷+gíú•UF»ÉÜøO8Np.TA"¼˜‚ļ¶aEÎHÀè`ZßÔ¦HÍ_ñŒ$Ï‘¾ü9].‚!6Pcëp5®ìj£égéãÞm]hh(jÖAU_oá[$_Eƒ%õ‘߸Á³ ìràï&Î.rÑ}±Å“!êê ˜m òyQXzÿR¸L3×À…`0WËŒ,{‰zN¯B^pÊ•ÚJ–UÐS¬h™_ENX0–‰k?ZA°Ðƒ5Ké)K}!2‚Ê›é+tÇu,¿9x„s¼TÃn‘?½¢P(ËDDË‹sŸ)½sk÷N®@ˆí3t_ èoö°¢1£õ£ñj>«ø¾a¾BÕkEMF!`'ðË-3ëœ; ˆ6õAÉokŒØT„‡k[õºøŠ²%£);õ˜œ¨Èvî“É©ÊP„6g½6,Ç£-½‡?ÈMÂÃX‹A& }›ÒhCβÎUM?¸_*¦ìÑŽŽC­¦YËnšÎÄ„xGÉ”y×k-¥¯ªæe¾¢;âæ2ýúÒ¸PðxéÉ) éóºÉÿ—î%è0kÇ~å!èÉűŒ L«‚g\?ç@,ÛS¶á®©34¸ŸAó|äM‘?÷ý;YÕ“"ãt܉ ɵdÂ#„ŒèïhkbìÈ"A}É(Ùuß’‹‹n$Kå5è9%uÕ[¼–hìH¡“ ?_½¿waÎ °7$Â5åB3x– »yL&“õªQúPæè?aA‡BªëµôÝÚ§Á<´7­˜`nìa3Zùª¶Ô¥žíC•xq [ÕBß_ÀÂʃk&—ü÷ЉF$™ÑOÒÕ»y¥¦ìa"¤Ñð’šVw–†Í£Œ~û¯ƒ`øÎÁ«ÚÆÈ8Ö›\â’û±é‰ëÏ Ô¶Xg˜Ê†‰¦’­•_Ó&·¬ ÒaqhÀ'r¼Åh»pAIóXnèü'ßX&å“β>í*M;£gF­5†lAâ|š¢‘I’%ãpWŽ%o¥O——ûD‘¯–à™t¨Ùe¹“DÃçYÂæXIƒ7±¼Í?Ë¢¤Ø« UGul³”~Wñ§CwøX2˲jx ŽÙêAk«˜]ý2ÕÀàzØ7XW·ëÇŠÂ…„¦Q²’fP7*3øÈ½uåçð–в³œÈW ü)wóp•Sm]7 hØAÙF ‘ŸD–ç¸õ´»:ıMêäÍ!ÈA}½y.ÉsúR®}¾ÈvK„l—‹ ©UšÁ1¾´YzŸfµ_òëX{¦Å)BÒ*]'¸þþ†=:M«gþÆè)õÎÇY|‰Xçà þËô¶ë7 ^§!0ΪÀù‘ù»q¡«÷ß'W.c0WÉ»„x…˜+ù^#ÙìSIa2‘°sq¡èÌÐ"ß˨¸][ô[Òïq¦ùeèVpr§±Kj[‹ÃÕ&ÎÆ¤ƒÅÙÓ’äÞÇ_×Y ²N@J‹tßÒ¯Ÿ¶Ûp¹!ò=lñ€ÓXø~šõ.iZ`è•g…t¦¼áÈmt¸ va×N[&. ü?ìôÌ \&ÄÁX‰ŽÆ†ÕôÖìvÇiJÚ_ýí|˜‚—do¥Y¹,ø«¬k±ob çÌ‚MÓ× ª¡P»ãt—RÃSvþüF«68ȹu?Xiù¾¡\ÚWÛxW¯(9\Â{b,9†‚,â˜ðÅÝ¡yÄHøëM9'QüV~ÏR Zý#ƒëµŠþ! |4¥š±#±¿ŒüWá.“˜×«îs(„Ö]þ# ºyènmlZŽAR#M÷ÁÍ~sµëäDL0QútV.(`Ì{æ%KW¢„è¨pÚá™—VôÕæàÄÚúàNŒäŒ;i„Ú²ð§©cÉgüP›OrÞÕñ”ãÑ¡ºc’‘w:´ˆôd¿üVøÐ,ÙLìŸ×ÿ¸ Lf6/;Ú\?KÝsÖâcìè.¥”ö7<ûä*ÇÉýÈCÃM§Ü…”z´J…Žoi‡Ð¿ÇABAöík®ÍŒ f0º2–ßÅÈù©¼õ=RÜ@À+‘¸­øöÀ/ÜsF¾ð >).Yj~@"ø!þÙê*‰Šùcx“XsQôL(ã׿ÿÆà¢P â› ¹*ÃÎZ‘±+aµð1*ÉáŠûIHLÄÎ6ÁêþÞ2c´=ÜmÒ5Q¦p{LP**ŸÊ˜Ü@ðzƵtXˆèHòó)Yå§sÔˆpG¬˜óe`ØzäïRÂŒ-&ËRû ¬»È‡aeÍ6K„wvæj2JCf¹ý éáj²ØT#ž‰(?ÕŸØI–hĽΉY7¤Š#“XÍùµ÷=\0qç´„õæ¥YåøS‚«g ñàî ¸S¸Íìg¢©¾[¤H'('«Ú§ G{Ó2ÄR†»˜ÜYÙX¸sb€A²ƒ¿Î1lòK²xYr’!¦Œg“íÄøé¯T?(V¦92ZíôÌî1uÆ[CM¼• –¦+ðÇ×÷ýÊ—ôH$dR]ê‚OâS4B†4>àúQÿÇk½R;Îá8¢½7_˜¢ÍÕ˜šÊ©«+·›TðæÞÝ¡áÊ! ^H¡U‹§G¸•È] =À¤ÄhŸÌxÚ.8ž­pPìE³OŠöŒý‹šY‹g;!¬ ûç±"%öDH¦Ò¢jíX6èêwó ýÞÞçÃC‰O$·ëz®b,âfTJµò'bÐójû|ƒ5KvªÈ1ó+0‰óõÍ 6pz⣇£Ü,® ªîhpmÉž¨_çRß EÒÇvXÏw/¬Tžƒ‡ežôJ:t\'u‡+œø U<á­ãGºƒ_*uµä!àV85êüu°åÇpêÎ.ütÚ¡•îò×¥ïCž¼ÅzTÊ‹çŠÉÇXzòÛc[[tÿ¦JÏå*ª:œu§;¸ç'—pS€3D™=´Ê;8O}¡VÞ~¨Vî])`‡•ãÕ¥k Æ ö®¨` Xtð†uKIz…˺ ç[Ka“»á€ôh´ÍNá_ŸíÛ™ÊL¾†¬,V=Úfò©¹ýüümP¸©kj©–:aà×üxBT2~v¬ïÑUܱÔlÇÚ†6{ëlQîê2 *ZÂÑÌcrëÖ_Ó(§Üî4‘ì³XãG¿sF<–¢|…ýòÎä3sùÄnüÌ×»æÃ Mí!?lºÁy_(á¦Ñ‰ì9§êGÅÏÀœëöæç•MŠÎÔ¶P ™q!µVÄûRÒ'`§f‚G?Îí]Ë9 õ½gâBMš>ßbÚ!óa¡)º·Ïþ‚b”VrºÈ½ +hpB•rä#ûú¸Hh hUÇüvn„_–/郰3¾Ÿê†ß§¿~:vÔ ÿºWCF0¯áÝAlNí+r‹ÊQöhìíxû‚»0÷uLŸK@Åu¹À¥á2>C¤±ùWìQ•*ÑŽšxý×Qh³c,h6*NÖQß,äÎÅ &HlÑácmœFÕBKÓ¯ËFRRçŽ.ˉf.fà9íß´KašÈmßK-wÅh®*"Ïjq÷Hy6 âž æÍ€…-ƒJyÌT\³¾›Â$H”ÜjÂUþÊq’$”õýQ&ù"bshÄó«r‡A—ìÐ{þ0EwßRÎ'©Å ð/³éà¡§ •ɱÔÚÂÌË< {ÎS4WÜ;×ëÇ¿·ß2 hzft¡ñ3¦¨%˜|/Ì[ vòŸ^0â!óQÑN@²ÇÇþžŽxŽSBŸ«Xcp+òö^1X«2éb< bl èä”4¸{Kf°¤õø,k;ŸŽ¦ÊIdéð̧,Ë8¢L`WuE¢âϙܢØÕæ]›g‰½š%½¿¦Ô#P¦Þy³“ÙÙ²½[…é1fƒhŸMÙÁg”'ýA^¦‘ïÏ™Àw§\·ãk»xߤQýcMIjK×ñóìL.•ÿ¯~ˆkË‚|è'Ò¹ÇíiMÞêR¨NòõeÙ¬ÓI°âd5ŸftR8‚¼ø¥Iá/u&¸þGç{ÖG³1iÇ.%ÜÊ·ÛþÞúÐ/ft¯fÎ-Mö;Âõnälîdk £¯ã»×tvr&ìµ)ö·¹6"(TŽk—Î8jì*ÆîPw&C),f`æ-¸³,ã–aoÔ«®/÷nXÖ‰~`|"è–.˜µ+H!fá2E„¼í..X`»c¢ñ1-(Ñ.6[Ël5’NEž–‹Ç¸rgì¨lXK)~ÐÜÌ›ÓF”DGî@sÑᤘâ<‚(ÒâdŠT(J/¯ E÷=eE²+¾“Ÿê2íøP›§7  {˜¨£^§8/±³l‹!bTÿò”¥ÎË BÚ{ÄŸhÙEP‘½SLÚV¨É&³ü†|rs/‡:ìOüüÂgLʆ¬Aœ*ÊtÒ£&­iŒÅÍZn‹—XVh@`æëBÔQ™¿ ákKúèÜ\ ;´_æ¦)Ñn½A-qt‡V×ùj»at¿’Ûóø :óÊ>j`úÀ`È ¦¬L`‘#%óº@¿WÓ\ID÷"Ãô8™Úk@fU³éü¢ˆw3©›®ÀìB„Wbù"Šj0’—¸3¯&G÷Çø§ûåx¡þ,aZ¥ºà¾aÌ×µ#ÆNìT@,þ¢´Pï§­“yK¤Q®M®˜>¶»³þÁü™-ìøS1B‡e”eyX"õ]â1Î{Èÿh= °ÿ)™Ž»„wS<4«5mfµ]ß«_öϼe‡;+EhM*hÎÁC÷‘Q=r7ÚÍRnú¯Å ~Ï„“Öy§îT[­8¬-çubÊf€Ã¼Íµ^² ë~Dõµ´{8¤à>¯Žsæ_Ñ$ày›½Ë`\µ°IçÜ-µˆå 7F&¢ÌwIè KÝ?rkÄrãB¼Ûîl8xż fru%ËA-¸¢Iãór'Áðb<%ðÊÁ—XzbÏM¹jZC®~ƒºO*.@óL‰tk¶I8'ú&ÓR¾èïFF{óií?ìý³3Ÿâ‚2ÿ-Äc6o8Ùì"¸'CŽ»Äâo;kay±,Â7u%,{ÈM!ÑDÓFR\ó¶Ë ]Fš —énÖUÞÜ)˜3Ü .á³s€o}àågâ%~,üäŠMŸKrï˜È­³2'm Nùí{ ñÓB=úãXøB}èÍz†ôhý#žýûhœ‡v±µôÀéètšÌÌ™€%YYLi¸†ÔAZW $ž-LtUõE.ѯ†™XªÚFqBÞ— UMðÔÄýé*)ÑHiòËyÚ|)¥0ãW;R—¿•z™ñÉXmÓ»\G“J+ݨÈðX<%A ºìlV$Ï#`ãÁ²â¶ð£Ÿƒ~ß10υεo•&²Ð—ɳGßÀÔR´¾µ¤ûXñ~4 B¿çE(b+PÎÑ4 T€ \àÚù‡LÕXö^¸@·ÍË“ü´ JøŒSj`À2 kZ+ú–î]ËldAØÅ·á;]‰û =TŠ¥Ïø@!+‹åÿ,@Ó¿žòiš+( Dµ^KA„0‘¼JÛ*Dtä­çÜgxv çÙð{ 2¿óÍžù×ñá›9”¦ì˜ŸžéÂW°7ù¤ã»‚»Iœ…>×üÀ@…cóŒÇŸ^=Þd¦™Ö­21ëaŠZOóM†˜‚³qÀ‘¸ÞJð~,WÏÆLl¨Q>>P~Ò1*EÕ¼!H‘A‡‘¯æˆ”®/ù“ûŠBNx^®¹@o-ð EÛ)º”ì:—|Öc{]–ñÈÅD}'*Eñ³Ø<<–zœ6 1ÜÐ*¤Ž@ñýÞ/úįÛú·þáo\]aÚv¢¾2A³biÎxeî—ÀR0„žíuÄ°ÄæÐ‚1“Rmlaγ©á2†çNuÖ6 íÏëjlïV¢û·ÿ×]V‰H÷ ý¶““ ûq^oþ®¯û1U+„PM;j\Ò £ìôŽÌ¼‹Z£ªõÂ2­8™iB *÷êf±G;%É#ZÔòk7ÆüÚŸÄâ–†ElÿÝ|‡¯Ð·BØ1»Â/ާÔ=ÙF->ˆÙ¢"™ Mt»éš¨,ËuèŽá$%k4 ?š:k¿RzX$•<#Bò£<3u»&t¼)-ïZJóÄ(ÔÊ „hGÊ–$Èèsµï…‚˜PÊþ0`ÎúÉXÜÞY˜åØQTìks&ú¥q‰Ä‘¾HÑ»s³—fr4·ÕC‘ .^:¨æ øš*–|Œ?irN[›‹ÑwǾ®¸Ž 9ç’Ëu´µIº¿Ò·Ähéçý”mNse*JÒ+Ü`'ž›„܉Et–‰ï+ɹ¥ƒ¿ ­%§tœÕ«ø$>ý;Ãù(Þ>[›:•иßdÃÚŽ€Œ´´ÿôqØs‡7+÷ò£\€s*ŽJω‘ø÷-ŽNêîâGOåwŽ’!¼ÑàÂ=dǯ_/¨”i÷™ ùŸ¨Éµ6‚”õå÷ì*J°ÚÏp[‡ôÏŸ«‹G}6GU:¦Õz¸6kÕ cIŒ…T`ú޳By~ @ÁY !Ÿ~£ûLjt­È=Êis ï0qpzð7ϸˆµ)Â6ÏCÑn x ¨%7áü!^Ü!ðs¤žÓæ÷«Hr-ݪt›mB…7÷=“Þƒbá3>4Øl7ïKô”B¿L»ƒ¡çù 2 ÿûÙÃK 7ºWy¥&H”Bâ…陣;=îãË¿(RgÆæÊ.nP…^sfk¯·È0nç|Á®°7ýi†0IAð{¾‡»»m4æt¥K\\(Ä9õÙ`1ÉÒ׈5tú[.$ã'ë‚–›Qϰwj±)güâÄÖfF&[ØF_Å>Pñ™§`÷o¯)°¾è6-Ñ—&B…ËG¾ŸœR‘PBYûŒs‡’CÓSâ X5žŸÏÍS/—ÎRzÔôK8ñ­+l!!÷ûªróÛ”Ó(¨¾re-¨UM>MãHÈžV¼OA…ŒäqÍèY³¹1÷iκë)­o:¾„E(ÂÃãÞn€ÿ€¼à èß4_w»eè5M©*úó»N«Ýb. ”] AY‹ÄÆÔðaëܦ£üC#ê1L@<Ô_†¸+,M+ÅõÁ[Œqý/±‹ßü¢)+&ùä`L•€‘gB*}tMKÎ qa6C0§¨š]Uä”8‡]’<±Ùd;õXg^êøO3Uk/ŸnÛbÐ_¬?“#jï¼]©¶}AåmQHõö ðøÿiôôªÓ$$ð»P–‡°DÛ>b¹¥Ç¯×î9fBêR)Ë¿iã—ª}8ŒëƲ}#!;Æö¶$ÏcP(P×Ðfü•©ë’í^iMEWÒÈ m,ˆ³]ØÝ0EmóüD™*oëΣä9M¤W:ëpYNµÃ×Âjrf\§õ‰0ã•àÎ[QO]Ãе &×À‡ÇÑG5ÕTÅ&+pƒV”2UÖ/ „þ 8ŠÅ¢…ÑtX&¬ùh©•XÆ ÕÓ‹Å‹‘¿÷8€ÞÖHÕ<Øq ͳx4ê±lmŠá·7!ÞêîJÉþ–×>tˆ2…îª@YQàO¨ÚÁ@>©L›–Ì" #hB’ãK‰©¬•Rde¿!Cn™|VB{—¼D&ñr—ùL»Uü§aT³ž50…ªÕ’UU•Ù±îpã6 ÃÂ9øÞºÒ±°¸ÎƒF”‰Ïà ÉÞÏqÇË!Ïî¼Åëaa,‹2ç f—¨ÉêøcZ„{#„ʄʵT±ŽÁ „äMW;ô „"¬Â%ü¨é£Êæúû·Ã¿C ¬¼6Ž@a¶‘ua!JýÑtg=Æ»)Æ›ëiC5ØÛ¥{[¾3[\Þ& EÕ½eáMo õ¿æo„“A{.{ÎHx^:Û4&-mþjµ±òĽ.Uõº’ðª¿`·_‘Ì8J“ò{,}îè~å@ÉgÝd?𨸺ÒO·ÐpL\#ÇÚs‘÷yî·ÓÃs Fsð l:µ«;#c0T_çëh¹nÄ¢:´z LA öú1ýN‰6Ñ×ZE®|)iϾ55p¢]T1–d™¬ô1ÀÂÜàw¾œ±“’1Ñ›¿‹=–PÀŽà·(Ž÷\•1:õ™‘^}jÃØMsȰ #ê¿;PZ%$buŠÏL>¦ 5V+ð?ˆ´rÞßÕzf%öï|(”ÞêP³¢[!ÊX9(qO?£AÝ„#ã6é…v¢Aîu}\(òCáí_”ðF¬¡bا5 ÃÎ]†!¨²Ÿ£]KâcߨÿìôÄžx5„—Þîö¸GX¨yïÚ£n{+¸”P3’âÓ÷Ãi«‹%hÚPQð,Ç-¡‹Y;¯ëÙùa#õaV à.Ì÷ï$UtÔË`>sŸ8ZoÖ0¿Ÿ¤–<Œ šmÎ(_ž·dâàHhlVѱL…KоüDJë ¦ Ï3Ŕե"8‘X9™SáT/LJKN‡$|ê~o9¬îÅÍÇ’Ý~ ý½Gê»/ŒR†÷ª\÷O÷W¶Êfe`B¶üµšñe,}¸CŠ?ÿ Sr#9ä3köó‚ð{Ï€ì ˆo++½Î°†äלּÿ{Nß´é5ÉÏ…Hòl{&pyÕ='×±å¿k§Ó‚CšñrôÐ>¼r  ýeȬÌ3—ÞPhC–x«MÈÁÔÀ=|Kœ5¿µâ›j[ö¶g÷!hÚÈʤê"úÏIÛ~›lß:ê<¦N±Û¹`ÏRŸ[{ËâVG*­¦j Üøiôÿ/d»¶º²ƒ TÅÜö;_Ó$™Ê†Lõç#¦;Š?Ö & €&ü]÷ßQ w'i\ ø)àòmu¯|‚vÌ‘Ë6þ¾Ú Ò‹ñÚcy’ü`™<À]Ýe÷x\y¥Œ«ãPbîhîç2¤g“DQ9É+I¬ÂØÞtüª¢Xñ€Æe³Æ“kOS盧¶*³úñ©Àä28í…#ˆÇ˜ŠxxîÁû0iŒÚIƒ°È[m›ãb]$i¨I…K ¾ŠX¬auVÛsø¼%Yï˜EsCü9 ˜ [Ip(ý 0obÞåš´4hÀR–¿¡‚4 ÏÙA~¨Ö•ÂR”…ËÌ‘”§Øû¯ð€ª›—]·ò±ÜUädR2ª>pú¿wô’¦nÈâ!ÝÒHeL/‡2vÙÖvŒ" „‰3ñ½±sÃOjµ~Âѯx%Ð)Ãø'‚œ%ê*hÈäS}ÏÎàtƽD¸6j\-ø3Nÿcr8«í9ãš1P¶l½÷ž(?E¤öÝ9òq#•Øâh2qß\Ô¯ DœÛE<•@JsÆ,ÑHUßC8—Nƒš­b&‰Y}=ž4qk[©ùo‘Gl$åaЭɢ_Åo_7X!¡ š•¡{{cE…óR_ óE·gþ' màòû½\þÖ0#ºöâBæ„ “C¦b~BA¹è Z〰xU^:!KVe2¿²dl«¿[xõMM2ăfØŠ¥”Nr0.xÝØÔìRƒ\°ªa<›tQ¢ÓW—¨UƘ‚É,:uªš|×vì´ú؇¶Ìa+™Ý!¦B\ŠˆÅ$¢ ¾?ü>@AÞmpzÍB¦0Û(Òó£ÉÂÇ$x—ZC^?â}}Á\ ¬mSXúX0³9‡AýÙº…Ø^ÁÑåé?¤ÌYQ:¸´Hˆ|–3§ã‚Y BD!¾¿Æ°žÍ¯jÿn\g[SO:-çoûh—ýúBGû9—1Éþ•B\ç‰0=Ï3;\¬¤âXbA4æöô#ªˆÉÂCÛï‰và3T4ÎÜ=Ýmƒ !h'¥|ÓlŒ’ƒ 0ؤ·B=Ôzœü­s½'U”_"…™'Þ:Py°d±‰p2è[)©ç÷”3?{$C©=³ÙáÓhä–¾¦M±¨fïëYJ`›’ë—ºÍDïò5 Õ¿!ÒPÍ\¾äÍî¦X/àF=„T7ôZ{ƒ+¥ŸAC“@&fli!&˜ƒs3@ˆæe»«’i}u”íáŸö™ÞqœÝc$eUWÿ¿ˆŒ˜£Ùw^ÉÜ=¾ÖeëW†—¢OWfáçÌrgôê«;`bZ˜ I1"´gy)•t{m©¼äBq;zÿbѦnjÓ4½X´R‰êª H\pWùÄ}Z$H/q/’ˆq°!{0‰œN¹Ò:µÅSçð, R´5—¸å­§:Jß{%ÚPâ‚*ÿjŸ8¯P1B_yîö&¡&€1+±˜ µ ÷]9N!¨ºácpE¿Ð¥Îå½m°JÌÑ[âÒÍÈ,’v¸Æ~BѰÕü옠VS~eˆçMW!NºÑÉ’q›5"'8„gÄ;V½j[åô¸¦’H-¸ÀÃÑt7×£ ¬`š êœ{öýFµ‹¨À†!µ,(OÕª³&¤…Ç$ßc_ŒP¥ ¬üQ?}$ÐAJ·š · …{(bùÐ\.MBm Ê¹>i'é®éÜÊY{³'|,Õ”fÒ’ëœk€’PáFüí-ºÎe"§6ê\OåòÚcŽ]@ˆ(0O²w‘éÇž!_¼ÍHc6]µ`û±G¶Bz£ÄŸösgÆS7?.9RÎÇM”Ÿe@H9¾¬Yß‚)ë:à,!bÓÚ øFÊAxCÎ×"¾Ù7úÎ(jIÁ …g0髚D^¥Lt*TÎr£g9™ºs“*ÚFë}(–áÕ­ç\çºÅ×åêØ1ïu!JÛ7ÉA ~Tò²¸Ý}…,@„DûOG#¥Îoãèf 犜]1‚æ,ŒÉä{Ë4ó³AæVkÏ_S ùÆ\ìeò?]´„L=¬¬Š¼bdU¢aÈ™€<üKŠž7¹†\±s´ ><* Mƒqºvi×_NïÔÉД¡âçE…R—œØn£p˜kÂÚî‚ÊGEÈ‘ÿž˜EY(ˆ—Ør®âˆI'£DHôÞÊÁ]*[¶Ú¥DF„í•´Å‚Ú&¬ï~™kþ¡Q©® JÝ—D¨Â©­q\S¸ƒ ŸØ¹ßGd¨]„ríܽ™Œ÷-6¬$ÙeíßDYPDàa)œ7¥“6)JF˜r]¹‡Õ^ü*àþ+êÊ4/õþ3¢ŠÙxƒóÍ_d•fé7-¿ãÌ\•ýH̃¿®£czJK¨Aj‚É¥Jþ˜& y\y2õm!êøûdVi‘1Õk2ç™Ó5OgÊhÀÕNóË´Ö¹ÿ•lóHz Òù&DMÁUtå ¡/=Óïù œ2a?¤o»«lC±ñáÝÿñx…!=D,¤&ேºË²"]ò/{dôï _=é"´5´×Ù±)$µÞ@oÕÕÙMè=ž‹ÊÕn'Nؤ¸´[-¯cÜô‡ÍE×ú¿xÖËJeÊ—4 ½ ½ Ú??paÁT2±1JésúŸOâ™ä—ÌY±AªAù  ²ý¨V,ì÷*|çâ«F0-»gû&ö­c´»¢ê}ƒïG.¥M£ÃPHó>•è‰âÔ€¦4æ>…Kö²ò’ÂgAõÝ`SÜ£õEØAÛIGiõu2† Ò#îö;<ÖF¹–fjnJá~`΂£}¬Ù š3hgGJ‘E@¤­ýµÁ¤AO·•ìMWs ˜ß•壨JM'áb¥ñ;ûh^³‹ RùäMCÛÆ¡*/ ÁEÊ7òªúñZÙ×öÔmaZƒƒ¦`Øþª·g †vimÈÅ•…ŠD¾Ÿ¦_îä;™ÂÞáÄÙî>Ÿw—´M°¶¹›&N'§È,Ó©–HÖÿ´Ø„¿H®¢à;¸dûþÈaTÒsìÆK8”ç|VSëá$ GÃw¬Âpób™²=Üf ÇR oúެ)ú# Oö’jÌIï:$Ÿa—R3?„øÓ“Y½mY‰ $”4“f}S{Þ]9|¦@ÿ§phâJ¡o“п¢XáûXßOÖµ+]ÉЩÅ\pH bOT…äfƨ_VqaRƒ”ô6 Èë-¤w™o¶þÉ«_fê»vAÿ.d—«3ûñ 1ä̳×ìÜI¨ÞS¿¦{hYžñÇC¨±”,uºGè0GGU7зD°ÅÙ³éñ¸‚€áúpíÛcf±H"‰‰Ït„—¨\¯ š¨Ñ'M/¡¨ÎGOÊGܳ‹KV [ÏMp,3 ç%‡LÊüŒÛRçCGÍ̧þÊÁîSV lj<žÔ-i"éni`CcÕŽøÀ5äì·³¬Ù•÷û!V¯y\<À•wCø”–Å&gô?‰Š“_iƒcH¾^ÍNÓ'´mß凜¬+¹X!KuãõbÖlIò®3U&îÄ«»„«e¶"4fÿež¿M«û‚ÀÆ¿y…Ò˜­!Å6Ð6Çí¢U´à±øu½ï ¹•¸;/¿‘€•R×’>¼vtæ† îä*jlŒÓ]³)ù7GëÕÜaOêÈKúD34òŠò\”£íÏœð¥*wY›Ë+.I_:WåLQHZÖ;Ãà呹wyaoc¨wœ Ò¥ÍíhCa;Ðw}š><6»•F6‹ê Ñlga¸Œ(óš<¯ p P%K±ü?r_+Ï9XŸbßO"½û¬z×Ê«CJ'%{ïÊ+÷£xÙàX¿xâã _V°°„¨eìËš3ÓÆ®ß9%I<(­Ñá…: œ39IËm+‰:îä€I ÎÇ2—c§ñôàÌ«VB_§æÈqɆZ$ê¶±ò&Ü^µÏÔ†¬wð„Þ>àA2½ÊZ¶â¹ÎÁÎÆ’Sµ3ÒuHB9”›ØÆÚÆãøèÚV#áóWÍê.¥‚FÐB­-³²…Ë#P'ß»˜&ŸÊºõ66â=¾eØ‹¬tþÃä¯nÓ¹0_‰™qßÂÿ¨ùÒÍÞÙ%s›`gî©çºÍ„žÏjŒ ̤ÃÁâç\?S7ö\‚É¡ý<î¥O%ÕÇ5l ·£S¼Ý—Î@AããŽ[‡Úáº#Ä—O ”’b¢)®>K ëá,Áÿr°Ó·Ê)I‰ÿΡ_Ã˱槬Íg’T—YTHl'Э[<É]q=…n€TwÆ*ûÇ Ð” ©ê’èù>ˆWòšUu8å·Ò ¦ç"Zë@È_^!<áÐC&~ЧäŒUåF·,H9\Ù&#{¸Êá x3ñfgÊiÂ:Äûò– Hô¶O÷î «|qÆÏ5¦OlÝ©¤WšÆ¶ñÝOU¶ð¿#t+•G}àÙí¨¼–ò;¥b„ ó±Ü\@ûŠ·h*£|j›a‘6?ˆ°<;–Üd\:WuBûZOZù¤³ åÈW‡4Ä>XD‘/_vÄ&±6ëYÐGë]R½h*BH„as'lOÀ0º ´|¶8¿M €S Ž;ëžÙÇåî3|ñÄl‘Ñ Ì—a=y¬¤ô]ÀLî%MÖìh¦ÄÚ…WcjGuÏ’ø¼Qæ slJöåùóçqìKt[[x÷D>ØŠ3ü žÍÊÝaKæ™ÖõyD츟#…¼øä!Ì0ý=Äc%h Òã»ÌÄåDø°¡|ËÄ ìF “„¾#}°¨T­(mî¾ÀzÙrdIxU0j¸‰LEfŸVÿ%¡,-eü ~ÈÁgÙÓ>˜.Á„{T*­=[c¤ßH¸xŸÄ›†aIþÏÁãŸ!»À ÛèÚú±°È2Y”+F qá?"XÕ KÜ èÅrã釋ÌA½F?º§Ç™}¯ õOÔç;¨á\(!¢cVŽÍo2 ŠºÀ0ëÎ,dáÏ­œ$+ŸMS†hÊŒø¡ŽÌÈWæÉq?»x3*Žfê‰:JjlÃõ¹>îE0ûì]­ÑF¤G ,DšFªág¯ ‡ÚÍÍýÎ3÷*C%I½lšj æVèñ7…£/›v½Úþùz[ qßrÔ-|Y„Çy~OŽöZç¾ê-"$ß×/B]‹ýŽD@\d‡.>ïØ«¸‡ÓÿRÝRGêWšä]3Ìh~ÓñOh_}ã5tü†ߨÞë£uºJ¿ ‹Qdê éèɱiŒŠa†éÐü Œ¶§ýæë­é¬(»,Ølz’#&éGZ>ô¦ªˆúîO$«^€ôZçuX•ãQ®,L˜žeû[iÉåùvÁ<{AôZl Y "\2ë­ðÓwü¨[¯¤—Ép‡ÉuR(f/e/ø£H\Ò”6Å v`têð,ƒM¾%9co›õª ù• â,TG©Wûb]Hö‡[ò‡Á9˜ Óôˆ¬–¶mHàtyešòç3h+VÜE½Ró!Xå%"ìg±[âBÔê @NÅÚÃGsf$œe%ÃU‹d7ü¦(!ãÃÂ~“JoO+ÿ PUc}šshLw ©ó}åÀ íÐ[šž¿B;‚°!P#½¤;AœbÏ¥¡%Y sÖßÕ½›<¯”½‡<1Ñ&m‡+,‰›gv91–zšµŸëGr†ëy·¿çn:êœÑhÀ<<åØŠaI¸ðañ×j3—J4~Òs—?K²'ˆ€Ó2‰P–ˆÕòÉÒãò+`Ö©žÇ&=•vά]lj¤§ƒù‰Îý#X©ï}2VÆz¯ç×äûÜ\}Çå|ý…Zjo‚î²õ× !A–½:- eY½}«kg~{9ØáµEÈW§Kå‡+Ç8TÉ\÷tÆ2;–FŒÂÀñBKHÕÁ?çª#›÷­BFb'~KÙ:§Yçh 3´7eu3Õe“1ˆ4S8·_Î~Y'ãeŠí h¢Ÿ’w§Ù"åã­õ ,$¶ z[F·ï‡e€ËÝ ìx±\Ê/ÞÜmf»C…Ç ½u …øÒ¡z ñïÛ:uˆz½VŒ–À42“+ÝØW5ÝGÿºˆéCiX8¬•ö–¥ý(©Mò®eHÏ•†'¯u]&tî½&]ÿTeñƒHûøM¾±khšˆAeI¡Ñ1aé*~M,b4²S£m‚‡3úâ¹ÿõã+.l¯˜îêðÌQö0< `·IùfTcÁMA*Õ¢kH^)qèþý‚Ð+ál±­*– ÂjóàÅM ÛÆ»³ÛpØÊH$á§Sbë¬MÃnK’µƒaù ÈÛ×#=~ã«th…¡Âç»–âmþ¶d‚ã–ò¿ žKáÞì8Â¥›_f†•aÚÊ@(z'nKl û*ËöåSæ›fË .]‹Ë§dÛb= 'ÊlýÎt'5õ^¼N èL6ÓS·ÐТb(ã%t…~$ò^;CÌð©†×n—¥ïÑÀxj“‹dÂØËmád89+KˆVYÍ â)zRÕ_¢ØS”žØ­ ÜWhÚϬè,@æm©­dQ KÐzØSM–Þ6ýoÀÊjJhakö¾ç¹…‡z›Ïüû‹ŽÁ ,Ášîj´¨ wQ3c+³ru7ãV…Ùˆš_H÷E³Ëü•U“ws—±,;LN[}#gv"PÐùŽY,…[’ò÷Û•/ n™ø|B%xd nE½3£øÏüÉ«ºGžËX?8 ¡Êòøy—®»ç@²¿5ÀvTÈ!–fK°Ô£jÅOð„௺¿Å$^Ñ•ÑÃæÖmC?ô#kÙý«Ì,üŸ)÷·ÃõêvÉö fÈ]ÇhAN*¡"É1UÇ~ŠÉ•:5x‘ÊðçÉÏ;î³<×õ$ˆÿpþigù€õuHŠtÅMÄàd¸xÑ.M©áÿâ;a@§v#…ìaYpQÛT¦Ž}õ¹B&QB ³18ˆ5péÒ†ä%1òô‘®‹íž=~쟧³ºŸ¡@ –-‡(*„õÇkXÉò¹X%eE¢°#Þá¿•u¡9ƒQ ¢9*)Oy=qM†³»µZÈiwÝfÕ óî`ìÝ>Aú –Á0þ´Ü^y9ê ñp£„É”+¯ñ-õ³¥Hú¸8ÐB¢9QÂiÁؼé9ÛÙâCù~àOMšbÎPvGiã¦!v•„·››Xg9°’u«sBbè’¯¥ðÅ÷Óú1%½’z”˜< ”,Ñ/Ùyd+Ô õ$S Òq-ExЗFÄÛXU‰ÿbvS"SZ) fnæÅ&é,¾Ø¢ÆŠMR>Ú¬(¼–²Ê1)¤•Ò»W4\³k, CTjÕw×.ÊüXà€%ŒMÅúŸ wÉß.…Ž®RãT‹@C¦|ù¶AXÌ˽J´4¨ã]°`úÓdúÞ×tòÀ馊PèçL¶¶ƒgþ.œÎ·‚|_oý¨«{Øk&ï5@»ETε]ÙˆµéçGþ´TŽ'§cÔfK?àøl2 ed]l^ E4°éç|°+„þKúŸÏVËÅ×7‡`‡äצUk®â0óS<6W,â`ù-6ÂFªnðÌIT­iywJ¨!}(4JjõÕ?ô£õŠ#·a0Âר‘Ð mwäo,m‹m¸§2½F,ºpÁ9ÃÏGeò׍ÂÍ¡3j0Ýx*V=Áˆ^åË„9¤çŽË+û©êê-¢9ÎÓ¦ Æü¦°—è@þrb0@Ò7q1@ñ;)PÛIäw[`Ÿžå° Üw2艮*ß4³ä!ò¨¤xÔå"rÌMh8Bå7†ËW²VÚ>ù¶ƒdüÎÙÚi ûvªÏ¤Ëü–¨Iµö&ÅmÝÑÄ3ç¼!P¸•yDMä\†¡%ñ·\z!áŽÌs1>ëN9^S¡ày¿ÜÄO|ÃßHæ#©Ÿ„ÝV­ó¸Ár®(ìÞÀ„ÝájVN˜p—ôG¨¡£·&¥‘†Ü¨ë# $º6•· oßE½Ã׿ê²â‡ÆŠ`„iÎ p³l›ÕßãÕe<·Q @ã8 Ì/å¤ ðW[§j‡ËµœE09Î*osìÆÄt9lÄQ6.»g ë\ßÊï<å ïYΙv|+ʸŸg¨GZƒSýŒ]»²­Üç{ܓҫÕ&k í¬£ÑÃT¯%€kŽàÁã‹¹Ì õK¼£‡j¸|PÁË)Žž;‡SCJ‚ÄÂkÂŽÀ¶.A ÔT”a¬˜4‹{ZÂåIH} _¿gçß~lÞÄ•ãLVñK‡ß+—«¸¦C½ï›:bò+ý!£ˆö7ÖƒP``ïJå’焃Ôûó»M²¼ýN„†¯ÛW¶‰ÄeaiÑfW­j!=Iל“92šèä;»` ù;ýk”±¥LDHoY*£ht$–/:(­ÙçŽTÒfzÞã종ë¾OõTÊÌöÅÃåà½ma›WŒ£3…ýUÿÑ{CW‚ÌéFߺAé *têÂV ¯N6¤ièºq&x¨_ %Àé´æózœ D® ÿ—¿)3•ˆ;4Ó<^ë:^ìkw¸‚ú__x‹,w¥ø´ŸÉƒUÙ(M§hh®‰„-™Êÿ´(û®!üÁ¯÷œm¶‘gms OW#>î”é*Õ®ÙÙÓ¦’!vCÄ=9³îÁȦýá •Ãf¯Ó*Zã^ XÐüddâút~é¨‘æØ¾3I~âÜ8åîRÞqý˜erŸÑ­©÷®8ÈûÚkfx^‘ùl¶?Ÿ$@…XR‘6ºžÑ² ݕǢ»Îh¡»âL   ;®G°;Ú@H³T¤öèOrLG›päÞgt½½ÈøÂ>'…Þ¾á9Çz9MJ±äü»¹àÞ_~ÈŸ‡Ò޵@Ô)H–ù¶¾šÏYÌôeÕ“!ím /Eˆõ=⦩]v›ŒmŒLKŒÕ„i£¹\~.1ÊW‚xóÄ Éóe‚Ý\Dú9“ÓÎïé¯_§zi…ø¦“C˜r>Æ£ÙøN"Y“ÏC° sÅ.nêy,+ãs‘Åßc„{‘>&@Â-b™›ñìç†K–ߎêªè’Ì#”ÈYng'ίÔAã'75ÇãoZ•ˆ0~‡¢>3ÅS³„||–¶MC?ff?/H œ[ŒÆL$L!÷¡±{Ù+×ãå«fKx¢mèeDº }@ÐÇ"Ëpó¡õÐ{œ3£žaæ‰,×¾"‚V 8üU~„¾±ôÿK¼+‚Ò/Äh{J-úA­™„åGê…鱺^@ü‘‹ÀQ¤ýVóÌâaæW 2 Þ¹”T£F9Ç s½Ò{Žó"ðRóå#CÁÍwqu$rW+ëDý™V!“ʘŽRºmÔw¤ O«Š¬È1º{÷‹4VŽù®FaT“9µ•’•r‘I‘V4•7-JQeìêçŸx¯sŒñ¥+¤Ãw$D/]gzœæ!…ê9 Ѭ11ë‹£‹ªþ—c×R­«/ˆ=Äõãùè7h¶$$oB…$b«póR“Ùì|’˜k 3ƒuüRj¨«? æ¼y»Ÿ¶k¥ôWΔøßL¬$^ºì…Ê0Ñ[–wŽƒ#[®°+í6ÃB!¨ a‘eŒFWaFå§a-S'#ÿÃÿbc¢1 GÊxÛ´,H˜• F<à±!îÎÞóñ¥Aã1]U+ò9tÿ¢ãIä\ê°óÿúqä½,µÏŸ ñ»>gèbößœìz*‰Œl4uŸP$ ›2býàÁk«£PHH#lê¹»MJÝž¾{Š#`²3V£’u¦¹öÄ[ßÍX™$PNC¢Ñã²cÂõ$ðõI,ikÑg½Ð~²v\æ7Š.Ù×áí©É§µ.ÐÝÜ7e9e6ª@;´¬‹ÂQµ›}þ⯤­¤»j>nàc¨Ž…?ÚÀÆc ý8 èÚE§p³ûëe‹ÜOìò ó‚X¿ _»wϹɥO,6/!lF Hìµ<,ºIµ;UÑOã·•>zÊ’Ž#°ÖÛWûí>—8œ™žÏݳôîMåð.Sý*ä¡1õÊ‘­ˆ¤•q³Ã„Ð]ë8D‘ƒ [€Ã?nŠL¤ w÷Á²(Yмè#ãjæ,œç8å²&[™/hd-¹éç vQd 'åSÇL2…à×€úQÜ¿h·4NAÌ<'˜¯=²eŸ{·»@ž¯×'×’wôÒÑ.ü#a#u`š¡¥%뺶ð ð ¢ü… Íy ‹aI­J~;ÒÊ0»±µ?5xþî™öNVbÒðVÜ+ÄòäÕÊP*É?z¼+0`3•ô„ÔŽ÷C {â ý£&nÎó L¤Þ©ã¹´Žô¹UÏ…8¨ ¯«ª-ÇÇXï­poçÄŒ€ ¿\¥®åGV2Z‰/õò¬WјI’z ·¨Ó¤ÐJz$º{A p˜j]×}‰>6Å!qB3;4÷Aþè²Ýº»%›bG²•ÁÌýy½ÉRÙ@HQdŒ”wÚŸ³D_’2š’)~â´0M)¡Ä+l(“ƒŒš•J«eŠàH¼Ü[S¾‘ZÕŸê³êo÷¶ÙØ7ü¼:=÷[{¶´W׸_¨ó>ãÊmó~ <ìV¶7ÅžÀÌÜ_È#Š]ÇÚ«S‡å•~¦@¯ýåÄ.@aS~(6ÔØj¡7¿o£ ¢»êG^ .ICMÌ*2‘½£ü½è¬ÒFJn ï•ÕŸuæPÖ*Úúo¿Ã­ÿÎ|C£+é8Ó²ÎÀ`uV†®TfwE²Ú€ÝŸÉÒ?Ê’KLå 3`á4ɧœ«…Këè<´:`‘ô€¡$Ÿ1uæYdø7Øb 0žWQ‚¡¼3—‡‘œFva[ÕTéå¯×ÒyÜ’xŠŽIöŒõ»•¶¨I–&1ôÞâNa ¸P—¾·`ؤpy»µ<ìUsƒ/H!r 1R©h·É}†˜À$½vÊ&ÜcNÝ N⬠À%ùO© ´·wwävfYÜ5ðw¢Îÿ`~VN`Ž%Èg-HEÔ¼ªÒâ°@Ì»£2FXC _p¦f'GÔ°UÛçŸjí(Þè·À¢myLr*ŸOé?Ícœ³_áz\ ocóp­IV»ç[qKÊVŸ;n¹’ØZgÕ}bÕ (á„Ãmâ1<=Ö©ï6 Ð8ÖÉÑ’´ÜðÌêÞ1HþrjP-ÿ-S-Ü6ÄØ7—`ùì¾x~zh0ã =n’êŽ&‹J‚Ëy€0&¦OÚâ ¢´\÷ÃT“³TÎÞGs¨z@oW¤nA6h[cÉóÁ&¾§8Ôs»3mpí0Þ]Ä™«œüá b©},)FÿMøô[vPç;šJÃFÖ»lD¥è!KvÈ¿©—]ktˆ"¥†¨|°¨´7w…‡”É)ùÜa×ð"´d¨ÕFÀOQ3óZ¾ÓñlÕ7@'BP8‹éõÈ×=Ï×BO²ÇÁž&õE³Š t‰ážt7*µ|¡\=ÃlúfÌ´?ØÙV¿¹mAÓ·ÿW¹n|¼æ±·±‚;5/.¢ÞÈJ¯½’#Žx箸 »åð`òn2ÔyžHòe@[Gc=½Tr6¦S¿åhÈú½¸4GÆ –ÃŒ‘´À‚t ¶¬f(ò„wRa€1ÎÑž0bZÙˆ#\œƒ%¾§f¢¬ß×Ŭ—'=WT²h<à¦:ã…m‚€š@`°b“n²ÒÌŸƒ^)cÆgÒ:°{TüÉDàØx>ÕÇ56Gº+8Y^¢„EµwSZµ±NP}NÑ 說Éåóí] Dåãô‡Sa8zñ§øÝDð!'€d|–³ˆ…𨵗Ý{ 3 ‚*j<¼êëñ7@¬½‡ ôÁtŰ,;\úì=£àÎÀüîµÇ5ôÅ£â  r '¸"…ìß÷ 𬘿×rȶe@È`¤î%€T˜#·}ýBÀ}m鋯™TzäìÆãC+»yq]6[PIÅO:É%x?Ê@’>ºßȸ²ûßÑ:Ô[aÈM¡ýˆ®K€¶T„Dj4æÞ 0_¤î—!¤'³ÖÅ;ûg ©Y½ïNÔÈuMïœD² >‡œµ£„a³LŒ›GmpøÞæÞï]ì°|ü]^$×n×=&÷uýœ˜_p"¦¦¼²ŠHÓñ¥•ïoÎ}¦%ñ=œPŶáaç/¤Í;Jñþ|‹!W jÂÝ[F¬*Т¿{(›iøn2¹±ãcê5[vRR3m«D1¼çGÆfÛøR¶FÏ:#§ï×k(×`‡2 ®µ:ðØ®øYÈlOâi¬• F_0ïð1ža×mQƒ'ùPÌ?hòLµ[vŽ ]¤ËsÜiãÙ¿!›>Y¾EIZ¤1Q˜§F„?ßécHÔp×44'Z"+è´¸PzL¡7·S@ g"¦•ÚàFÍ&%»m4ɦI§3aV¨/ë@ U¸ü»¨07ñÙ=TI×™aO(ùÙÂw^ FÒFì<\pï˜$H€‚¥SfÂÊoZÕ»ˆÞÍí¤’mN wß‘QaË:žcßý¥9z­oÓµ‘ GJL3|ÔtA/þ‚Ê„íÈS¡êo÷¶Ð´˜Á\åU¥ÃŒ^„OSsÃÎ oˆ#tRr½F0QsUö)ýBGi²ˆAò™úe9äx Å žîö«!~¡÷Å{Žþ§©®ØYxÓuJx6$ò©'ÁŽÓ!Ös¾" =â¯~232WTúª aµqÞ'Ë`…¢MZ!¶27">lšJ_u.š|é" ÿ£êlšZ7k ô](àoTiEÜÄóÈZbUâá“×9!ÀÎw_†«àìJÐx%C<„_»ja}bÍ;%_r‚²hpÁ¥I½²åÏ-š»Ëðê  b=°×¿YUùK(4»‰0r'D=–í¾-š˜×VDP|©¿¬Í«„g_%íôÚºËCÚ › L·¢#Æ:‰_ænÐ×­LüìŸ~hdI„× ]ãá¯NG¤”ÛÆ»Þ·Áð¼æ97vô¡^[J‰Õç¼Û] hÅ‘ŒdzÙ° 3Ñך—øR7ÞiíOФBÌG¡¦ŽìM¦!™©ÙŠy›‘.T ÅëÇŸKO3ž¼ˆœ¹ë¿t̨(· ¹¹ˆ«•oàN¬þ¢³ ܲΊˆ±‡e ,S~Ô?…°Ùé<wø>¦O'wã"œþ{ns”Õõw–RºS¬_mÄÂÚo´µ§ÀPoÔ”7«M¼-“¦ò\Aií¼»ósG°P  píOè%/­jмÒ=ÕÌT~™_l £PBÞiW÷ÿx×&“èØQÖo´±M9{¤ØÔ|ý|RçñËB_ ýÉà¹Õæ2ÚLíBq½?Û‚mC]T(f`¼/h$3’ErJýroB=îÃ7lÛu€ˆ¥¿8x@’E€6¸•|}íóÆRhwÊÊyY3þÁa-Êb•P¾­™ŸÏ‹ÂãÆ(/¨TÐr§îZwhFˆÔ«ÃÚ+2d„w´óúáÁ"‰„ï:£ÿm÷T"¥Ýí%ÖŒƒ*ù9c{&|lܸD…­rEˆ<¦„b¦û¸-.¢;ù zX¤Ì€ð—`ªÞGÌÈØâ>LqÓã ù“ñ\[épÙ¨Žrùñ_¢ÊLÛŒwîoìË.²lÖªJbÜr_=ÌæºÖ‘Aª2ùãÔ9TètøÃ7)mÞÆ”?´SÏÚ˜) lÀ–åLÈîr¸žnK·°sîJ N©5ýš?¯~ kø’ƒår­´ˆ)iro&¦æ×ßh0&¦ˆ4ùÏѾ¹qúVff8ZV¼{Š'‹ƒÄ‡#Êü“d\¾ÀèGæ»úo;å$ñÒΚê¬òœ 5©˜’)rM,óœ]Z.NÒ­Ê3ËÁtó?IñÐG8B\þ³™Fºéþ~ dE­ºsD©ÊsÒÔçÿ8µ¢–Õw‘JI `›«ùûp›ý‡N˜#Í"óšÓ\ÇJÝÈñ°2¡ñ$ 6vã4Ö|Ó=˜ ¡g•ð öiŽý¥.Òú¨Æß½²;gyh°V9c5NðRtàÓ(›U”)ýd  ÇöïR7( 9,>@?ŸYÊR¤öΠµ­ìvXá:éÉF»ze°±!u±Êˆ¼º~g2ÚáO=>ÕÞXmÉÎl™X±KY‡çâï6¹ýǶ‹Aœã:N[ÐAcäe1©k{õÆVù ÛºLìšË-lÄ'åÿðE¹®—Gsº*¸Å}S£>ðÔlÇÒ§ÚÓL÷c/ã=u«*Ù»I ª"²/™Þ¸¹ S²?üÙ^ò‚R'VTMªä®ð[¾¥”ÆS0£±=*2Î-»ÈTsÂØ#)y€ÈDñ«õV!h¨µô1±åR½dE3˜q·ŸÉÁ ‘nìˆuÍ>Á›‰CúÿBô'U”x&eV¼ËTádD"ßæ·FôérŒáØ*Tàž£n…4hzŠ0+7õâ¹HªËv¶+ETÆ>ésçËì¬ïadÐÃ÷ÔXàÆ™>Y·=€ëûö@Ù>”3!Ú6ƒÊ_Ccbðú·BeA]F*cÇð=á{ ³ËÍçãZÕKo-[¤žÕ:‰H¦[I“<Ôr®å5¹êKn9D1·¦3§=‚ˆ‡õct Å`êæÿµMH2!üöÎÜ?é&zßÜb¨¡¡lœP/ݶyn”씕ÊÇ|0ö¾vå³Ï%¾yüŠ&åÖÍ{ÂBð§…14WsÂû^òâÀä^ß,üe(àzTLÑÅ€_30 .']Š ¦ßT©â¥ûtõ*¬u[ªV]Ç3ãÁãáìù;ržàØdnOÈ/™k¦—û(å(ùTS#¢¤uI{ËÓf}_`nˆ¼†Såx@èÄR2ÞMåý¯ÿU48¿è´ÁAèâú–Býݺe`— yÛëN¥´í^E,vσÀNÝIÇÖiÒºÅS½BD8â=S$J"OXÿWäÈÌ,žSªL´JŽÃBabhS…ép^/6œn¼ñÏžH .PÔ&¾¬‡¢È†^ÉqØ„²K0B¨ãa.}H!¥ß8x…í æa„‰/AÚË8б‘Gú(tÖ:2…lÂc¢”f$ç=Õ8B&XB¹Zï¬Öíä€EÝîKG¡ïdÐh˜¿¾Ý¥µî¯ *B%åi”Ç®ª2îÉÒ‚“7rÞ÷÷Éj¢izž¥:¦â©9Jb!¶m`1ôO¾†›4ÙÙo B*; hcØ=™Ög+Kgý:¨ÖÉ_W¦â—g'¹  e{ÙNIÇÏ2»ñe'Ë#Ú®ç¯dˆqÎØÉ‡t‘Èy¶Å³MèuÜ5¨ÈCL²jªå!Áv6};%Â}Kñ÷óH¶3³–¾pŽÔæ ²Ý§]æ}¦h/è7—K¤¨U@‹ ¬Ï¨/ ò#Kç-FÊóÌÊöY÷Ébèäìä9ÊäÁB9BføÔaïHx4sO汸 jK$9G}ʈ*ò'`žÓ;®»fÉw÷”¸H’[›ËÿìÚR.Æèèçù½»"íètŠo@§;ÒÛ˜éz¹I~÷¦âs0s—žÎyÍÕ|~€ÓÜÖeÌ6«ˆ:™ÇWŒ»…l“­¡Œìz/$˜DLpÁ6ôq?G?µj¢H0÷‹–3S/;±óœxŽ#ô s5ƒ®¤ ÝÒ‘>AuTvêR$I¤«ŒÞwÔv*1®.È¡—zi˜îp{H)+'Nx¾rà è¦Í®Ÿ~¸­qáÏbÓŸ|XÂ.}Ç¥t“ChíVU¿+Ê)í#ió) 5Åöë2CÏÉ—$^7ÙÎùíì~œN• dJ%x©Yȹ)¹ÈØëC sÙ^Ùþ+Ú׳9ìº|!á:¨Ë¢¢ ]T»î9x'Ý=ÚýJíú;à ýë;(7uïÉí¹7¸g>Õá6;ìè·ñvGX§£”ƒƒ¢þ݆/“°:$ÉA„¶Ö¬>oGèæ'=ÏÊ¥|ÀðD¦ïn=§üÃ,-nÿ¬!ìøÈ?5dã '˃HV¢¤mƒtHã\ÓÙýl´ýaø{Q³ £òäSË™™ß;BÄö€F>kPR¼Ðã"Ï |¡9ÞHRþø³4­$æ[ÅËfO¦ê˜íCw%@·†Óá‰m»cëD£ÃÛ¶ŽmÛNÇvǶmÛê$_OîýßänÍw j­½VÕÚõ>Ù£ÚHáæQIíßèe(Zµ‚=¤ÏâD†/¼Rº®tBdzúO)›³åÏYb¡ª[S¼Æô>|Q޾Upã²Åuïqü¨pgPÓõžEûÈ]ÞnF›$&SrÏÓ$ã5\W%…$xJj“2ò­NX¬¡#³h0椬„2À®íèC¦;BV߸§í‘L»büuçñˆ»‹ rºR f«÷gìñŒNDÓ“>kºïáÔPÓ)}Êœ²æ:|îÔ;ýöžê-I“¿PU9±…î%¹<õiòPØÖÑ1¸©=¶vÂG"8ƒ ÷wÖHbKKþnÉßf1ÊßD`Ö0< åšohÿ=§žE&¶JTŒþµ/Ü8ÕŒ1èþàØU6=”—.²YYɲ€Ș¹^^ÏlA,ŒFà Y¸ÙNý—ï mõ]\á ¨Òzî¼Xx(§ö>ewËÞŽa•ú¥Þæ«ý ¤o8Ó¢Ú:ɘqV/rÉe:*ÔGŸ>ÉA“£«Æ»†¤Æ«^agÉt®‘{…œº`óžÓ&´(pAÏÕÍÎöOˆw­}ßù¼啼`ãÈ0GñlÛ“}ÞeÖßíoHÁ¬/ÃÛP›žªLîp¼¾¢'P1'l·fsÜpvc4å–tr޹ŠÏN*CɺÍf›Û;÷LYcɲöÁÍj~Âs6 ^µ)žŒI¢ôæÉÜ2f´(|@¦…¡ÑRH‹–õ€Ö°T<Ï¥/ãKB>œœ Îz®ŸÖõ2©É ®øpMÌqÇâXVSL•7ïüú€kg¸Ì•°e eâç5¶Uë@û”v_ƒ‹:¸?ÝlsÔXà”Xž£YQS¶–t·…´òt%õŽPÓ¹©&u¿<3¥Bß³ÛqÐVd¼«ã&þüæ]]Å&Ö÷y…2êåË3ò¬×Ù1Þv‘ QÝHBÄûpÈþ¥ÛCÿÁÌ(s†Î&ŠìH@}ó{UßÜ{1g‹¨¢öŠÆGüÑ…pƒBà]ƒñòù\­$TßñèÛ·X>Åp>U±hôßçöþÅãý +@Økf %ÖÔÂ[¡›CùJ«>Æ+µFAê ex~„ó¢~‚æÇ+õ‰ßßëñ±¦èU>Y‹~®»àÁ ,1¼¢¸B¾ 5 éJ.¡ö&V4¨³èä5ï.à^ΉޟóÏÄÐÃ6÷ƒAÒÅv οç°ÿØK•ͳißžVÎê¡ÔÇVe:Á5×(ᑦ íß¿ù¾Ú}Rï<{åMJŸ³ òvª|T/C5oIÞWÝSqu$xy±`‘ï*а[§Âòj½M ^èË„£M 1<vÖT‰ÈMév%g¢,c5P<»4-ÌŽ `§¾gdãyüY |é¢Ë½?Ûe· "¿ù;¤·†òÐ4€‘eæþä>Au(÷î V­è†>ø.õÍ¿\ÐewK(Õí׿è•㮕_ @=o™O¢íÚô–DXSœnÊ^þø¥mð‚uáÛeS±'z=׃qÃ(ƒ íÝÌÞ%9»¨«“–޾o%_ÊókR|O ‚¬V²;,±Ò?oÀ~A“²ßyÆ~Êf­yk’Z'µ«›[¸|õ”+ ²ÛÏ—Ǹ~ïëAƒÎÚHà ñœ²_ª'‰;MÜû2¡:›cÒІ¹ ô†‘çs¦ø;ÔÝó`ª‡(Õ¿¯ˆ•èÓh#ò ñ•UpoýFZÌ^¢îu4@0iúݙ߾èK_`ÍÉ–2ðEÉÒRSjK—b£¬åïÝÊ^½K¶0E·É¸'+òÔN¦Chžth|n"Ò¦B6Ä ¶0»3uá„8ýÙ¼ˆ¶!nŢº…xþД¨ø*lì W¥Aš*J ì¼mÈ)2ŠÑ]Iå i >T]~4¹oZîôæòOÂ/>s÷°<)“ͱհcïrEeýVÏiÕÁof8ôÐy`ÊÚ>8E Ç€Ešn~˜=xf0e„Ñ•Ö#qëQXçØSÄ”ÜtE€rD}ÍáKn9\­¶š¹Ò猑ϵ‡ùPÔòoAAWgæ¬d3Óá.Þ¿Ö…Xa ûLò1&üû…”ŘJ¼ÌƒúÃéá+ël@Ê“^>IäºÀ+ºß°DâÈú tqÍý×ÄÇIaïÝí¾då 5¡ÅÝÍTr®œ©é.ˆy¬;ªx™þͪ-±ŽW„ø-8Lô¢L>šÜe|n°iÃa§ S[ñcLSYõ0'ü¯“Å)½YHÙª˜ /dô-ú©TVŠûlö2u·¤š(R@ª‹Ù³°.È 5}‹+æv‹gôOmWÌñüó3_hïúLoþ0¡krx«–5)éÁïÀr¿ÚH!3¡W u.XÒÀýaªa@GV¡Ã¾ÚKƒgîò~·ï'Y9&³D4¾µ›·z‘ÜAQ‚)élzäm÷ÇÆð­ˆ=U¢nj@R¾^$‹RÍ3SÈßeTIAú7Š·ô¡d‰m&p½EB(1ÒŒç–ìKó²åét">æ¯ç«âÆëÝ¥ñ®_s2P*Û Àu™%tÊbc‡\QÃÚn‹øÛìqµ½+xXØ­¶Â—8_z£ß}ˆ¥Ì6³\˜v(ˆšø#¤NW{ˆõ!Œ@ÈVj^ ² Øhö©âï9¤; jkN^í!Ñ|¸ïN ÄùÎñàÏŸ¼0Øæé ÈDï~&Ê¥M‚:bÎj»z¢·?Óð+·ÿW ^]âbh}õè0=øífäT&Hpç^Ò¿?)“G²§¿ì;cK:æå$ª,¦;dñ×…àÛ˜õS’o?×çpŸ©O2öߣh•çø”¡GâÌ窸o9pïëº ÿÃ)ƒïÂv=2•'…” áÒQXG[Éþ„Š=R‚¸͘l îÀóÏ'_å8t>Hàœø÷ À©¿:vý³.fÆÜ¢³zŠjȸÙàüF„O³Ú8ÞñÕŸSGèx°?Ø’ Âhú P+µõ6…ŽfŬ°ÏT7´×ŽÈæ²Ø£Ñ_[/´—ÿuÈy²›‹‘f(­„ÐÁÌ!€^¨k¶Ÿ*~jjá&™ ¬¡#%#0aðÊû3Iš¼ßðTqn+‹çÇlJ«Zfù³á™Æž$ÛAM~ó£gGÔ,qQE VÆÓvI¯]úê,kÒî @ö2JDà°þiéaŒÓSl7V:þ+Æt¨7ïùWaü-©W ”×möïÓ * g–Ã0^*N lĽÁ¿4iMËîSü’Yå=îÙCƒŽBRû|Ç9AÓÎàV@VñÂ9ÅW¦Nô({ûaÉJÜ7ËÄ€ÍZô÷ïñô05äÜäÌ—1‘Ðó¹fÓÓµá~{(¨Ô¯§>§iC¨ØB‚"&è¹ÚnÞ ÔlµS‘\Y˜Ý…^¸šçC!ô€;‘Ť+fxµ‚øˆK¬}uÉ4U¤©‰O=w‹¶KY¬ëwÄ}åö$x­„ÊSÑ?ì@ˆÃJ5^ΉI[é()´îÜEEºcÐËÇP¤ÇuE³‹~7å šp·KXþù«ˆ²?\Ä(±v=nC§'жe%ý¦ÙÓË-eI™‘ %\„•1Õ«âÇ…Tk2ƒ%“iü©Ž±°‰^í°™wÔ`x³ÆÝ‰ ý$–Ò‘Oº±û*b¢ûŠ(¤J»°ÜV‡P0äùª›aÁí1[’˜?ž€QK¨"L>Úâ1¬Z%Á}œaØ%ÃO|ùÌpÊþ$$Šà9Nb‰†¹•Öë# {4#îænÚ£uu;w+ãåo€šF7™7`—HcJÒÎÔ/,%+v3ûÙàcžŠÆÝy‰ÝÎø< ÏñQ}ί±Ú_ª•7Ò]H&Š~ã—öB¬Ç‘Jm@ØaqQÞ—#Rë_[Ç$¬C¡gÖ¨‹qÚØY Â|Ë Eƒw<ÅÚ5:ÓAÙ6NO5ŽtVRtOØÇ…¿Îº“ÄKƒiÇïQvÿ@wø-wÐiË™»¾?ª‹º @"IâÁ1orâY©Ýâçý/NÆç !#¯±’Ú ³¥°jïÅ[¯¸ï3zÀ„´°¡$Áxšâ,©}Z២oÁ\(47º*IHE‡x߯—-ÊA/Z<ð–V¬þLj—âªñmÿËKN}ÊsyûÒŸu:DfЉ¦õ·FÜ€S.¢Ëys^ ¸nnms°Jš^ÈCN‚Jªø€/K ©¦OeâãE¨¬¶HzuV× º¸Æ¬a/…ÐK|L t׿a\t~i±WÄŽ¼sWr´SÞ c>U:} ±ÿ»÷oL–zèo/d-ÄÇXÔ-«a¾ülù|hY†ZqøÙUB‘oð €ôô(¶þ“9…A#Ôùù|ú–ˆþ›=øßx_AUsCð·¯;î7J¡-£øÄ£ïr¤éO|ÕýûeËÌclíÀpî?2‹—ÎY¡¹l[êv+CGçD‰¿ýëêdìs‘5ƒTÕæüì=¸?ÞzØÞ'‚ý?ÛÒ£H¸D«IHiºC{¿¾p¼Šºœc’j/x3ÍUëWGê£çÉP´‘*óôòÓBçža•<£Ë‚9‰Ñ·œ §~²‘{>P¥-ðúoÎÇ|lËx«zi zþ;ì@ÆCßhVXÌBh\Rø-üì‰ÙXåP§¼‰Õ<ÀÖªVë6œº›é<Ç¡Æg-{)Q° ¾ØËàeaÆÛªGâO¿BVÁ 6ü1\tC0å|i` ± ÷°´Æ’BH‚jõ•Šebš¯RæÇy¤”»ƒiô+ê^Ô ½ÿÚhëJîì~ðƒ¬—ÇœÏˆI;„þ×n ¹YœÃ²7Õ›4ò"»Þæáx8†X|޶ÓVÕªqøz]©º8—Ëc³:ád CÄÄÄûµ¯Ó‹hï>›í§gç©Êü%¯x Þ.' n.«.áÅ’%ºjðg$O2#ù̸/ jÄ­¹ œŽ±à¯‡“ïOµQÁi ÐK©2ZoJ,Ÿ© e| g—¸»ÎNædÁÉc DÚ+»gYSg/×ÁeI6îOŽKiµåí—‰^cæ†íHa!È7‚9Ë©™k vÉ/J'pß.§±}îaÔÔüË"”w=mF‰Œ©¬³rêÓËb“<ìégˆutBjTöÁ)ž>;à6õpÛz÷$†Ÿr0sF›«43Ïj}à`H8ÛÇQUÊ|¶î[Ò‰\¥k<ÿ©]߃^è±ÕÒËbÒ”™þô[DBd=\”}o!(s7:>×Cèˆ7SBÞÆ>ª8ÆJàw!Ç#š×ÈâØÌÈC¹—l;J<¥ÕßË™ÚS.0¡’¸_®õý‡~Y3F*z"ÿƒ‘ÄÂ\;VÒí`t#º­=6t¹óãÊcþ²äý"GT#W¿¶Yô@Z†g!ú L Êá6Ó6Šis×Ô”³³5|Lï$WÚ³®“˜Ò:—Û g2&Y¿æÇQ&G¬kBÅ’R=E²iÏK›¡Ù~¯®(?¨T‹Ò+r=g¡fkð9 BÏ‚g2³á–=e7PŽ­Št‰®?™áÈàkw;äá½]¹ÞøtN´½/µ-î©üŠ8”þ°.é’ªú=ÖͯÙ÷r Ú1rdbÂÂFÈÉT¹ï®Ãt¬Ã!§ô—±îÚåw A¾Wæøvô:cÇ9¢¥ ½Õ„ f(Ý÷í:wåyÄO)£•C EÛåêõ$=Å—ÔâSÞ9g–.QÚ°vHð‹l<%ÉN„¨×Ëú:Xn*Ÿ.L)èÝv4¤­É=u N­JüIÉ®"ã0r?Ñ4W!*£‚Œ½å^cXïtïóß,{6¼ÊÄJÇÅ8åßç3™ü»i{aŠþúÅš]¼^¶ t@C^ ¸'1{ËVF¥+@_ï»ê¨ˆ©ü$——ôÿ ‚à,t¯Uô­÷ñCZ‡Ácõô©s†Ïó¤' ä¶­ùݾU!s{´.ÇaöëÍ%.h÷…UÁŠÅ%ûí±B@ÝX3`o“‰æŸ«y–çÑàµÑY¨?°–‚wg¡cŽ›+OÚ˜ŠŒÅHÆá™Ýì§¥&É\8 ~þöÈ]–àö­'[ÿÞÈ+©õsiºø,*ž…”Ý(‘áîv€áA5ðAÂãÁâ¾Êì™SìT˜³b^ª@d•pº Aby¨ÜQÖí K_~‡ðüàì ’ø›”ƒèÞlp“;Õk¹¥ÑåÏö´˜|ïâÖÄíÈ›Y øâŸ;–¨¿ª¶°Î×eWñ¿˜}`G‡£ç/\[±aôÊ‘‡e¸EÜsñåÒC¶õjpÔn d‚—AS‘@4Ô‹±öuRkû5-E¡P®Š@ ªÿ9éÎtF4R;\SÇeÚ})8f¢ÄÏÔ÷†2èf£Ž”ÑÇd[ÙôæXxÔV¸Ó•¶*ñÂnô9Ž|'±«¹Ï¢ùtA\©Ð>ñÔ77ž‰0i(Vxúñõ=j_Y²ÿyïvÜŸ:ÁmÒ¦6^¤TK>7lìµaãC'‘wë-ƒ–î1V¶üdH(Zà‰(—'óç;2Í¢¾„C‹Ôk›L×HqêÇøýö­³y&s) E³3Õñ\‹Ï x:L‡šé½2U=á„ØÊŽ¿a2yuòð–1gïƒæx3ò8¥àà¬5œPárhÖ0¤¯D×î¬=¤/¢6Èw°¸¦WÚÕ«Á¾xfý+î“ÛÍóÓ‰!l­óa hU7sÒiÎWsÁ—o©ÑÖfo€c÷e7䢤YóQUÎ"ßœ†$2Þi%ñÛŸ¿Ø©¾‡]Èæˆ¾‰1ÈЀòn$ëߥ„é ñBÒË…Ü®ì‰3Zº¯´ÇVš5;£5‡Àd…jÏ­†“ì°aíQ®uJUfýk½ˆù<ã§fÑŠpÝÎ\ˆæûŸÄ'“Éc™Éý©DAφ‘£Ò#ÓÖ.*Me¼z"-Õ|ÝÞjöÁèþžÑ~ õûÑ?Qð´Ìø>z’‚–L:`¯ÓArýñ¶ ‰&n~‰7.ÂXv²„¨3cø3 x\²60€ ꈑáñ†ª:9p­yC â$‰ ”^¬(¿Yfk¤É[¯îÌzë.‘"£%t.Iæ;}¹@ÞDB Lüg’Lñ¿13Q?Ùâ¦$0ýê£ab 0„Žú¹v× &ÿT6¹¸„8' ÔŽ}µ›ñ9ü}ñvz?AÇmUÕ ÿ—®Í¦ùiÉ_9Þ‡ÍhE…$ZêÚ8?2|p(÷)mtùCñU|rö'ôqSU¾¢,döñ–É‘ìQ.ÿÎd¬¿Ÿ>)^ÆYÑÝ£¿ ¦{a²¥7OÁQŒ›ñâÄB¡ên\)M¨AIi©Ý™QÃS. ´…TœT–`œý;ÈÃêÕ¸-ýïÊKçѳ]*¤È¶._™“k‹ÑSqør ÄtÁÆ= ‘Áãì(²ôÞë!4Å…ëÀº…ãÊcŽ«’—[C÷/¹/ãCó:½1ŠÉÎM¨Š])”F¸® —FôA[¼Ÿ°l¸¸Ìoo ´i–æó¹ÅÐüÂSëoyÍQIÐågAµFìúø¨2?á‡4ÂGkþ‹²I|ÈÛißþ|¬oR·÷°´þžˆáö“jë±Ño„›¯!Ä\MWË_Oü)2ó‚6¥,Wm¢$h»Ò¯YÜ›sûY¸ööçx!À׫ôÆÌlý•EÄ f ÑbŠ Ü­_Üå'‘ÝÙÄiÝ:Ë*¾ƒ|Ü-v’KW2Û=xmv½½÷5 ®iÂeEš²ç8 <}¾[7¸>cž/n$¥¢y “™EÜújDî‹õÿ °‚kß“Ò##‚{~ä>cF¥¼g ü;„ÈçÏ‚{É{Ä®–f§ZÅŠïµg²àïÀ'¢•Å–UÕÖ#“²FçºÀÒ{3E13–Á–ú~¦î̸Ó;"b¦¿Dæ¸#nê§6ƒÐŽìÛ,îÑk°ç¤+v€û+/)GçÙñIsjF`…8}}§tI"5Qˆ5¬Hƽ2y%;à~55;Ç(‹þQ‚J¬€—¯ >ôf½Õ½uÀÒiñaoIdôIaúŰrPƒ÷ð¾Á“ÀÔ¬Í %‘ØÊص·6÷Á»È«9ÝÑjÜ•Òʵâ¿ ûeÍŽõm ÏdÑ÷öïñídØó÷©+œY“‘Õ]'çeɶI/œ‘\Ù·V‚ô€ü¶“çXvÛ]«‚†»û^’G–ÅCÞ[Â!Ênø|•[ÝÓjüꘞ)¨@+®_r‹®Cª0Þ`qô²èÕ]Kð£aOáýÜuô»c§câeÒà\ Y‰ŽkfGöŒöVjx 4É"#:’nóð/À;›yØ‚:‡ð1úX.: 6šµ”tß;ˆ._ µ#Ѻ—Ñ`)Õ¾”Jv ¦Ðœ–L‹ÄÏùßðl¥;Vx®Ž­[?œëëÁ–Øù€M–‰Ygâë³¹Í+F, ÍÆObа_ cXˆÌ‹½‡umC[¬i(űͷÄ_YŸÇRÍñÎí BßU½-RŸ`k¥ƒK›¶µŠecÁÛ‚…4âñÖC.LŸ‹GÏcqKÞ7'a~Ò “°¢sü­Bù?0ûǽ G]…͇©™®<©”Ë \®!MœÑ´S'R¨’7I)N‡ª‘r¾}\,›ëopðß>.ÝÉèjÌdÿÔËõbš–àÇV€·çøÓahò³å´}·ÚÉšìu§Žœ%¦M·u7ï×f‹œãL4âÊ l‡ŒÃái·ÚŒ´þZ—·ÌÌx®ÜQ¬ÓIùTKñ™³ë>öú1¥ô}Rñ‡ÛFáwÊZ«#uK,Àö ‘ÊCÃG¨Ví˜Fqv¢~Œi‰Ø‘²ê“³±,Ñõ®õz…ÛQ´àW´²HÓ¨WOózòšCê™$‹¨àŠ˜/-ðlŸFMCÎ0ô„0”ìĽP? ‚þ)B«V޳Ƅn›&o‰k(¶c¬kšÎ¥ƒ0] iÑÍûñMEG_èÚk!ÊÖX 5UíXËI;}ÝVK(è[rF6G5ä¹õ§I‘¿†Fè>¦rT"cw¯ü¤[¶õ^Æ›Z \@+ºÊÁ’ŒIæE:Æ—õ«GFÐòÚ‘úñ"ÑkÇævQ´¨íߊ?Êy“ÉZ‚8ô­âzK´Eyï>ðbƒÄ®Ò:!‹7\T¢|’@ C±×X ¼`Ðð!Þc€2–„þ~ˆBÝã.+j)¿…—î³";V?™¹Ã›^!•SÀÈy÷ÛÃ^Ý*nØV¡•›ôG;5)[Û*ÆA¶y'¯µÜdwK®§­ìôá{Leé)C‡è°_<•ÞýçÝ¿<Š'Go–2»µ•Ù[TÄÆ/U‘A5Ño¬ Ô]h½ÜæBõpÉ]°Ð=9o•š5X%ˉŸãí|ÐÊï pP:Û%\ŒÊþ5†š–â.#ö«´ :¬MT¼¿ Ÿ ßqoG:j}@ìê½í'åõãÉOæË—`/ › :ܬgk{‡h«XÇ•ùU¨÷s‚ ]μ) PgÇÃ+~#$Ü‚åå¥cv1DëEYWÀJ3ž±µ†e@<Ûý¬]õÐ}rÐ~½g•â5‘Wµ  h‰GúÓKX`±OA¬÷I*bõŽ-ysïŸÕA±[­gɦi9ˆ\i@‘m2z¸pÙW,ËR;ô÷§rñ¸…§%û=o­–pÊš>çÉ7¤ôÛü‡Áƒ„ç¶/7yÂ|Zà~ã@GìÊ`=VûÕ/eÔU•½tL ß #ªíRbt!$¶yÛÞ GÖžmx¸Hó#¼Ž;C5²sIã2¯­toš*P¨!U*×oÛùsÆZSîæe:¼ó6Þò5÷‚ ïÞ%Ú³ƒ¢Š&¶%›M<è-«àñtG]pzxpäJJžå²_’Èoe²gŽ*±¨IÚ˜«X¥ä‹c±~þùÇÒû…KþS(|ÜzHpMí¦F`2Èn E}l°¥k’l À°ƒ;bÄÈ6ÑtÌ¡–ÅëÄ»VÎiö‹æ¸t02,[©¡Z¤¦EâDFæc‘ áeïêeq¥ÏÉòOÁ¨”¾˜¹Àˆòë0b ×~‚‡Û$˜|“Ö3*5‚à¡)»7tW{Ú)áìn~nŠèvÏ>Û³âï²i'KVéîÀ¤*3~{F3‚©¿—¦JgˆÈiA2WB&Üæ9g¦Örfvò"9.|êXU7¼ “ÿÊßE€áMKW…!‡Û'œ~ÇÜÝŸXDwU4 †uŠ|ç_Ý…„}O±óik‰æ¯G¯“µÙŠ y€¢íb•Tr½@_2ͼ|Ó5ÅäùP©—±^g®wØQƒ$C®ãûÍ_­k»Lø†_€n,Ƽ·d¨Õ¼¬XQuöü-cY Dô4+©Ýlp¿ï<®6£X£ ¶££á*Oj}ž¿,ËbgæŸZîôEV·˜‘”Wröñ8â_Ã0 †X´zÂ!Iu[ªˆ<¼S"®ÿÊjÄá…„*ÏÑ…x†;& MØ‹6‚?6U4T‰ «"ñEŽK×ÑÆha¦Xe¹’…P €Ë²u/€E]VQÃj1ƒx޽ܹ\÷lÒ‚ïy®ŠÈ5GžöôiCòRsöM,“­(9û)bÛJ)AÎ1AæÉæúÔ«ˆ¥›ÖAåàɘR(’}Ø>v·>ßB=\†-¥ (Çù 4æ±G¹ÇØwo1ðAéÍš²|¼ÄèÇlÕ/ÊôiëÄuiu•*¼ƒ;í5íæ`Ï!žsXŠ×hsìçSã2^æÃ#ûÅÑ>\šè‡˜çñÃ_lØE ù‡]0í$HŸ.B|Š. ‚šþI5´¦ˆRƒ/kŽK‰ÈÇ! ¢Zà{ž}ÜWJR&JŸ¢Ò±rë¦Eµ‰¸ï ÞÏC«>endobj 23 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 250 389 555 500 500 833 778 278 333 333 500 570 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 570 570 570 500 832 667 667 667 722 667 667 722 778 389 500 667 611 889 722 722 611 722 667 556 611 722 667 889 667 611 611 333 278 333 570 500 333 500 500 444 500 444 333 500 556 278 278 500 278 778 556 500 500 500 389 389 278 556 444 667 500 444 389 348 220 348 570 600 500 500 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 389 500 500 500 500 220 500 333 747 266 500 606 606 747 333 400 570 300 300 333 576 500 250 333 300 300 500 750 750 750 500 667 667 667 667 667 667 944 667 667 667 667 667 389 389 389 389 722 722 722 722 722 722 722 570 722 722 722 722 722 611 611 500 500 500 500 500 500 500 722 444 444 444 444 444 278 278 278 278 500 556 500 500 500 500 500 570 500 556 556 556 556 444 500 444]endobj 24 0 obj<>stream x¬ºcpåm·7ÛvvlÛv:¶mÛì˜tlÛ¶tl;Û™ÜÏs0ufª¦êóaW]×ÂoymýɈ”é„LìLÅíí\è˜è¹’¦6n¦.–Ɔ€ï+ ™ˆ“©¡‹¥½¨¡‹)7@ÝÔ jj `f0qqqÁDì<,Í-\”ªJêT44´ÿMùG`äùŸœoMgKs;ù÷ÁÍÔÆÞÁÖÔÎåâÿXQÙÔàba 0³´1ˆÈ+hJÉI(%äT¦v¦N†6W#Kc€¬¥±©³)ÀÌÞ ðMøç0¶·3±ü'4gúo'„œ†gScËo5ScS‡X´S'[Kgçï3ÀÒ`îdhçò{€¥±«É?|Ó;ø âàdÿ-aûÍûS°wvq6v²tp|[Uÿ·Ÿ.†.ÿØv¶üfì;%Mì]ÿÉÅ7–á?0ß\CK;g€‹©Ç7Ñ`d 0±tv°1ôü¶ý æàdù/7\-íÌÿÛZ€“©¹¡“‰©³ó7Ì7ö?Ùùï8ÿQýÏè l<ÿ¥mÿ/©ÿòÁÒÅÙÔÆŒ†‰ùÛ¦±Ë·msK;†ZDÊÎÌÀÄøoº‰«ÃòÜLþ• Êz†êÛ C{;O€‰© ƒœýw;™(ÿϪLø_+òÿB‰ÿW ü¿RÞÿÅýŸ5úï‘¥ü¿ÿæùB‹»ÚØÈÚ~7À­–ÿ‡ˆ¡­¥çÿ‡ºé¿—Œ’©¹«¡ÓÿÄr1üîl!;óïMÀHÿÝ¢ÿ´ž¥³¸¥‡©‰‚¥‹±ÀÌÐæ{èÿEWµ31u²±´3ýÑmÓÿà©XX[Û}O€íßp¦v&ÿ£µÿ™‰¹Íð_±ýDá{z]T<¾}ù7Aý‡ý÷ªøË?jÂÂöo:&V3'Û÷T12¸ØX|ÿ_Lü è?ÜûG÷‡¡‹“¥@û;Îo¥¢ý×ßtÿŒ˜±½É?Ó®ìbhgò½"þ‹ð ±tt5•ý•™‘‹ñ{ò¿Ógìêäô½”þµ¾cÿÏû¿–­©©‡©1Ìi ô²AA?í öˆe:ñ€¶6Û…U>[∭Êx‡^ÍÓÒæÏ3™ËL—F§­œXÛ²ÇÁ[°Ó›a®Æõ²ð êAì/š-íØ}r¹M3!:y¢aé!â®wU—»CM@âOFª,;jíOxMÝ1?4èˆéÀ•åg¥¯Ì /\\ÍhKTüK m! ð<ÛH~;c67ÚX©6Ù4n·\YR›ÂõÊÔvîYgâé›-€aâQ’òêûÈ~jÉ`&O¢ ¢±”—léÇÞ»µ„çaI×m]n ìIAƒV¼™qˆæE{S/,£ ¡ŽIÆÊÌqðÊ-6xåûŤH Ì1&IÀÌjB'÷DÁä§‹s„¹P#¶ÅÎ<%<è¹Ñ(pÓ®ƒ@ %~oÇäþ6ÿ:Šùดg—óÓýi^æbd×ø”s}&É56‰° à*û"Çú*¡k«“SÙ÷Íúb¾—)&Á¿á ×@®Ü6rÜq+#›ÍP5%öê(á`öCò?©†¼À£Þ5È‘ÝÍt:•œ•ÎïZ?9ä¢ç"ûóõµÃàMhŠ.±l©¶Íbrü£ªYsM¤lY¢|#©n‡=«nÏÕŠÓÅPÎb´ažƒ€E™Úàip~ù_ÈœzV¦!ñv`p'*²ošÍæÙ°"w üÌ4.±h±Pm­Ý~icÚçŒB¿Ÿ»œà-Ýy2)]~yap×þv‚7úºhp28©ŠÃ½¤G¢Y%—Œô»Ñ|²á9øÂ÷˜})bìjWíX²ÉS)–%OtëÄz1þ«^m%ó¤ž]‹Ôcù }ìN‹œ^k§ªøæ=d/n…Ö)áÝ'Ð@¢¯H¬àã@&¼1øH§&$hУüã´ ád”­V}1™§h¬Ü­–VOìðÏókœ¼@}äÝûrÆ!ârõ•˳ÑûxÈɽ¾¶Œ¸gU6€)ŽÌñAŒc%Ê=Vu ôSË‚9}®šA“a /íú~±+Zî雀…\!mcz¢)ûÏkéFˮ۲µ«žô€[O‰«g¶Üª7ÓË9—P8ׂÌrðäÅ+­"êI‡1Ü0+IH¨ZØû•†š?¹ ˆ‡€MPv嬤wµÍ;!Ï¥“ˆt^œ"uJ@WimUÜøƒ¾Áì>¥ëRRŠ·à`\‡e]…»ûξ­0àÌŠ÷KåèË€ú‹‘â°àº?ÂJ¤¥‘|÷³*˜fÎÐà œ·z)Ò‚Ñ™áŒq@yÊ]”-ËLJlS™¤Œæ’B'ó¦Žj9aϸPž®öD0Øw3ƦK’)=i]^TéµN Ê¥)7œøBïµ%QbpµuƒôÌY#)'#Míe‹°ÇNêåØIÌ×ý!Z ’ÝeBCH1Ý‚Êð¦‹OÔ8¡÷„D§¹%[¯\häVÒÈ™=)“"‰b×B{rTçPOžÂS‰Ûª싟¸C5AUøÃ©`#›9cßéI2 &ß—l®>5ˆJû‡M*x×ú\U ZËM….œ¸4,¹ã;zlï’äSÚ"“Ðs° 1O2)Ú†'ƒ&<›]ìBì¶õ(°yhë©‚ogXyÅK'¶F @ØÀ!Ïâtpkø²J æ§Jë˜SÙ' Ín‰îè#ßxÂ…©K@®gx²àSžn¾$ €ëf(^’ò&d†(¿Üdzïî3 4á-àôÒfÍ(û¹£éù®È ”<£{ŒÄ_–•õè‹o2²…G<ž›½Â:ùW»mÙÒJäy’ý˜Š?È(Âpì!ä7Ë»ú¬ÛË¡‡žxï ¢xìÅŠÒúøÔmL’«pSÜÚ|©N0üC+¥â#ã¨äºÒ`§3c&ÆÅôk:lÅ*³‚'.tlÌâ<:; |VwNUñC¿Pð!ðÚÄ©éì |.#G!7G®K¢†„F6‡#Éwñ½²Çtc—lW¿ˆZz‚‘1Z²JÒpÒÒï]$Ò íÊ­¿èÙÐ/µåÆR°sÖÜËsÙ ¢ê¶ý+½o}ÆÏ|¥fz‹§Ê¨÷ s¦ ¹#¡‹+ºO׆RS™}# Ò¡¸óŽÉ1G½ýµî°y\o×T0¡‘dÖßÜiòÕÐ9”Óa/â×ùu¦ÂÅšÛáòÏ—‘ô ä·Ó-A¾$è<â°ÐàÍÆ {ÄãÈÊÕUvçÅ5héqp˜•½ .Œà€ íI.ä+e@ÂYâ#•½M|ëÓBJÚ“Áí¹ºê\­÷ãHr‰/ª"O‚Šr:³Àͳ¨`ج,ú^f¸n“3K†_›1^Ø*ËË»ˆŸ•Ý6(t’/õ#Å9®Ä`7‰§‹?t‰…*×­(Qm«ùF_EY«n÷ù¦q•­6‡=_“ïSw¦Òç<ö¢ü¶àÍV8†²†ß™9î°;ÌçA¨³v¾Ñ¡8Ü$x(Ž¢‚xÿB†c’tä}*|éWÍ„¢è¢Hb˜¹R,E"¨­ñá&U[©Œûœè~ÆÃ¾¹KSó:dèÞšß ÅñKý¨#Éë’½Öm”‚ù¹¹KäÔÞ¤gM5Ù¿+=–Îì¥Ã¨Î}xäÆ: 8T¹ÙJèGdÌÃI¨Eˆrf"·®ôp"»8o[Öà%‹…Ôb½ÂNU'кŸ&ì’?²ä3¶k,Ï_È5•÷ˆû*üW”%ZÒ U£ž2yä/ªòN»­€©5Ksð5˜¡ôúKE|®©1Šì ¬Çof„¨£ŸÅ‘ütgäDa»ãÀ`~w¡¸P"H˜’×ÛSLÚ†Oòå•'L?Èl`mæ+h”\Ao·#<*Cƒ¯s¿£xÇ{^†”Ž·×ÍM÷;Á’ŸÀø@vI~˜cÀiº+Uo…'ƒ&L—À®h`*¬ä£”Ò߇-¾ÉœËïñ$Y…hëߟ ^ïJÒö5ò:ø‘ÍB…ìxÚ¨r3'ž^@ ^³¬sa°é5”¶56W&¦éžŽ[©:kžÔTºD¹?ê•ÈÖ–Á©ŸLîî3' .añaGöÜ9ÿ^ã·1{xæÆ7˜%ù …Ãù\øû½ é÷NnV$gÑ~•/ªo¤Aщ Ì6T쵋÷…ÀH€ qSr˜Â"¤°ÎOë_AÑ !2UNûºbn ÏéjÇ(ùÓ“ì6»¢ñÄ0¤8ÖƒXæÖŽ"ñ–se²J¯ÃrÝÌ ×æsµ;ÿNðìf ¾Ð-ƒu–È=ŽG[ž¬Ì’@Ú5U{œ2 Dñ³aôÕl°¶·µ ǽߒ•$)ÿg¤p£[2Ãܵ¢–Ñ:àé¸Á¶£J‡Ýº&¸4G GŠn»UV L¤f¸ˆMñÎx¼«WBÇ~<ûs ·ó¡Î=¦HÈv+êoý-Çæâ=²´?˜¡wqþCŠÈ úˆñ!É+ã«ⓩtÑÓ!¾nƆ#„ûQXœÕ©Z¸P‚ÌÐXÈl#gËØjþà,ÒU mĪ¿põ¬¢1@/Év„’Ѭ:lØà°—ôiaT‡£N N­ÔGž)OÄýu¿oX9F)à±´ö5‘½i’(ˆP´{˜ïb¸þ‚·`ÃÍùá2n×­¢}Ò'vî5 &³¼YÐÀÑ»sï×Ni'¯ìa\ªDś؛ ;›¡ÊÒ1¯Wãú0®þàgFÞGƪ¾¯‡Ž)S9Þé­ÃBõÞ³’4»VRŠº¿¦Z ý¬e=ÇŽìR1䈭©ÆLµÊùçº~n½_¬žoòF×Ù>Ý‚9ƒ7?ùÖÁE|M †T3òÓ E¦«ÐÇ×¹³üÝ/ÀmaÍÙÙŒ z´…òK=aPcBÆ1,B–ÕЛ½õ¬‡Îg?H~ËšíêØ’ji2ŠXqjÙ†©À¿Ú“ƒ¶æœ,"¬W¨‡±FO¸±:íöDŠ8–;ÊCkützÚ„~Äe¶—MË倔×{Ø!h5òòiá¢Ã›ÿ†] Jl^{ØlŸÙ€.~/3–ËιwàÚ›â¤D³U—Î$”µc1O°É¼ù™8j#`« üƒ‰µÞ“c@°Ð˹·ŸMN\f1êË(ó3ðOj7ëÍ{ˆ…6(lOL î)-Ÿ•öìTÝÏ×>spÅ ½/þ‘É÷<Ï×·ƒ9z }¢’¦B?ò è“'Õ¾¬Î€ƒQ)@Õ–Ådu@í×óã[ð6ß#pH¿€Pg´ÝÚbªÃN^ìpr”WLX¹ÐæF[›â¶ XñE¨–¿°v-^OâæŒ8=¹ÄôfjˆÑÀ j×>S ä:¶ËÏw¦?(Øvßx ôäÞþîûY\ëê\˜^Ž dêÃ`àÞ¶/”sá%˜ÂÁ x8?ˆI4H§¾Ûv°è8È\÷‘P…ráh¯ Žx-2å'få[èFîÊ“ÁIl[K)¨Ü¶çæÇpKÿ|ƒ”$Bì›û³i‘ìŸ,nñ•÷)ZÄ)‹¢ó3„þŠ×¿Ti!·Fz¹MÍ4ðŒÕ˜y×óOƒÎ ›ÂvºÐ&ÖÍ Þz z’ Æ/°1¹Ð>¥)AÎîçïX•'D†óh ´ÀŲ¸Ú›ê °xÏv+|\ ÑÍ…Mtá3E ^¢þÑ£Q«‘÷›ŒFS©¥}|‡¹‹ u|\H&¤Ø ÊYÒÒû_Ç[õwE¥žíCÊw»©”]Š£\µI¾J4²ÀïéÛ&µv8E KgWEtéi…¾H‘t Î9üýj¯Õ{Í!Á †5O²¹*œjb]E²‚éÛf).~e`®nÌõH^X€ V ¼£&Øâsʼ3AÿLßõýÆ"|E°úŠiÆd L.R y¥Ï_á¦$Käê%”ŒN †CŽø4slš ÜúȽžØr_EDU2ǹ î·=WªÃ61ÑKä[K9»S “T"FÿO`Ç£qÖ6þ±+`UžH°1}”¢.º.=!^×Ǻ“–›µƒºqMKÏ oâ¨cŽEecÁ¦õI ©Ó—|LXB P¦ú­ž\Þ~9´¾~w½KAM7ßýßö8éö0ب÷ê¿xrb;`ÞLtf¦OJªåùÏ®“†•ÛF ÜNuýÍ4•†žåN8 •ïÅ`a숸y FV¨…T_¥„Ò ºø,~4¥PIÜrAoŒŸIЉrzHüµ±+ƒLÁ­©¸•ƒVº¾Î%ŠK3æv£¾u\Ô­ßEÀÓ8šn©š£–ñÛ°ÓêµìÏ:—Yƒr·8-M,Ág9Ì{Ͱ£õb—~tæ1Ç;hiØÝ†.,COLß Õ­5çXò’œIŠZ»sï†q ™%ÿ)owÀì—@‘PµSÿ5¡ø~a{òL•í@Új‚oú­µÜ‰ù÷¸@s7ñóÛg_›õÖ_´¥7^/_¬•ã©ó–l@0bTA™AðŽ‹[\q>ÆÌíq51‘ÊQb»-\À¼¹Êú_¾Fnéû\ÌGÒÌ®± ï6ìÉþ8:”XPÌɃ؟–1Ó8§×žô³C*S[qåQ¡>÷éóŽÌ=4ÈîšTu¤F&ÊòÓƒ¬¿à#Ïòü5f_ø³VÐ m@¨Ân–bÆþüzôʥȦ$'~µq»ÕеŸJ\S[fŽ×…®+uñH?YÅ @ªûÙH_„f‡Ù÷ :(”lN9hÇ ”;ô ›’;h¿ÕÞžìõã“‹y?qo?{xˆaãœh9éC»ª€ZÊ•=H¶)¼$<0ÿ*ôkK'ª*k W‡êŠÈýY)´¤IZ8ò4i N.•1!€–£Q_B‰f•{b¤5Ã/I(Ó•&aUê :оcÕ§”5m±Ya« ¯à Ócšqš}¤¢Àòź#ì*m}߆Ö2â½öCßëóª\åñ °×æÀ|§?ÞŒi{1sªÈŸÇµ¹Rqô†ýC3µLâ‹»±æSíʆCG5òåƒU†£ìö‚¹(>•Æ“\i§Z»1 UïÚGȼÒp™ä$ñൈ“ YfâÁŽñ½Y ³]€™ÎŸ7zxú·úXuƒ gÛ䉖êI/_BÊi.Ü«)A7ÀÞ®~ᯨ¨ß´Em’Å#y‚´Z…ÖÍ“ Æ3¦þÄ:¡½ñìù\Z^ýù=küÙhÚƒzë'àÉ#Ø©sIÈ¥zš©FN÷mŽß×¾£QJ¾bý-šï¢k`ÝíB7ÅvBH=scžƒ9÷` gt¥Óà;¤oK0»FÿAY~µ€(p¨2—žwÊVàðþ™pø—äÆÊâ¶× ü8ïrÔùSæµ/æ|ÈAYŒGyOî=“|ÔqU“úß$üÍŒ7M*'?¸iÄÂ.Äïˆé¨Ð/#21yu.Øõ('‰"ži-æÏö6[ Ï*ä,ñÈW³ÖH‡øƒiå ?°i¡Œ×IÆgÑ™·ÀJ!ÅT º\ÿf¥j–Ü­–Y,…®AgÄ©^WhCÛu¡½“Õ‹Žaºtd†Ô/áÛîAʈ<ÌPMV]öMµË'Ÿ“i{°ÍPÚëÍ2ÿe–@I)Š.+ш?⃠%š%ñÀÛÿŽ"R6‡ÂÊòpˆ”G6%ûOÔI·íá´®ZÖ[@­¥Xé¢äœ-ÔgÝ÷sÄ ò¾—c‰d ΋¥Úþn)"~6S•!æÅ.5«ý"X챚¢_Ñ®­Ø5ØØä2sFí×7jèzÓ|ø"X³Ä|ͱÒ+©q¸\M ñq°CÖ|I¢çñg!§KÞÌe΀ ûûêbÚ¬,òìqKWR[Æ5PJóÑ(§€-PpÒnVÀà!.QË›§’”«é÷p4£d°)S2èma^iH+í]£7•ŽÒ¡¢Âö[‡­Ÿ“@S䞟+˜êj]ÉçØÈK€vŒ!´{s¶3À„àŸè¯×Bйv’çh=º ³þê¬ì¨ÚöôÍö:‡»n¬€X»îmÞà¸ÛvMµ:°‹~f6†G²1:u_å$Ÿ® ó,Ëž*ˆ1Ž¢yhÆ£;<½Þ7&ö.4Ð’n`ãX0tßί?r Ë;¦µïÞ³ïŸàmõ­ç67>$²x1½RgÅÈ3\u̧ ß…»;‘¾ÆÇc3`Ò·ƒ¦KHH8‡Ç^v•ŒÒ`ÏâxUÉíY‘åÐ|ªK£ôõº,V¢ãÚ±à§]ž 2&ˆ%¦HóŽëLD´ó:ßb爈mµmÝþžfr§ò{=µzwû8”ÙM¶~)½èÊ+Hiíqõ—nÞƒÅÎͺÊY†ÇÈ9]ý)L'äÔ\ã±ü<5XU’ãªo5Ê/E–̬Kw•ªx6·Z86Üh×Ö“dW{[¹Mˆ¬wõaA5Ö¦²‘ØYìÖ5ê°s§Å?ŒÐ~À~OteaÚê÷@S–.瀎B—°àiv k%HÞ­NGÌU¯…b[šsX³]ˆèa·kVE^©Ô+è ºâ8Ï5`¹^ÛŠ²èŵKß7BÄÙ’ÚO‹#²>ÐI)”q’_ïì;à^=ò°£‰vD/šªBh»‚FBè¼¶»2e ý¤¦ÍÄ–%•ŒóÇT¦VÂã‹Ñ„göÞ¼LÁÉ!wBÒ‰jª.×+ÐÁmè«£ß`òzsþrþôø©‚î »£UÀ-ðsúr å‘jN³êpñº÷é7 ‡Í“¥²–óu7 áJ*£¬]õµHQäÝ:Ó¯í?}àöbNCgÝϪS¡ÐÕþìG«…U™õ¡£Í½®[0›3ƒ›˜ldÎcÀî£2êäãÅÚsšcZû2¥¿W7öšoAÌná *ç~5ðË1ÏÉëi}ª¶( BWøCè-_&OPÀ½ÍX‚ÇLÍÓ)`Û6Q¼ÝJtš®¤9v¨"U%²æL$R~è^Í{8þJ»%ºÅçʧÈçÑ5aqe¾P–/òQ 8k¥¾âoë©©G˜YËêv æZã¼½]IöA1dúŸm²c¨T–gÚÔäͰ@OÚ ’OP1vàc`¹B£ÎLPȉú/íLó hƒlRŒ1²¬Ë(yɳèyÆà†4xñã^â!§kŒdG¥É6OÇà˜L­â¤`#³˜]ˆ½^e8Ó ÉÞ‚5mÌ1ÿ=!ð’jFêA7ö‹)Âë ;á}"âÔ X¬ÇYˆ9-xhÚ¨®opzƒH/ÎtF3Æ8NIôêáó¥›Ì _ ±-±Èye¾Ý^:*_Æ.Ü’¯™$?é×Д\–X°öZÀyYþæóX¦1Âö~ÐÖ ƒ=Îу"­©µ¿4n1áļžöŒ/‚üÕšÓY:=zZ1aµ½®Y*D(•=‡bVp+¯t;ù×óç\¥ìÌohn•µ‘¨E`ê}§ªAç$e«‹¬æB&jÜ4!œU/ìÔ(S&µ¾»Ì Ì@$¿{ËM7û•‚WΤõnÚ“Ôé•÷»EU+?Rƒ‚¡‚ ¶#<ûGœ~U’bA˜'ë;(^c´Ô»Ô\z Ç"Œ²Óꃗ ¦ºKhÏ*" w)b3Õ‡VüMõw¼£ѩƞKèĪҰ±,ï‹ ìî (…Ê”ÛÏQc]j¥ó‹þ¸‹3Ä[R©\(MI`á)ÑÜ~7žÄvg#|YäËýZ0)𹉒¨ÍÓ¥wj ÞÏ}·F"°u]Í`¨ÎÇÄ‚0Û‰&Í~RŒ_š§ÒÐ:+V¾$•¹ÿM©…œEÕ cf™w¬§ ~NÄ~¬ ‹(–ëÁ<+"ôàLò¯n+‰Z6-}°\˜}„×?¥áFäÛ‚­:¶·ð!‰BƸe-ÝŠÇ¡üÏw<°û§Ó”=^dù í^s×…0/d\3÷~sn÷¨`òéÀÛXàæýYe8Üëk'7ýdœ§5PÒ)é.*¡†wò[<9õ뤓)SM‡­…Ž!{}4Z$§™:Y8Á9Œa}¤T‡ŸÀðxÃ#ë¾8E {õ&‘ˆ¡9J‹˜FBªÂ5ô“ZieÎ:ÙÄ'È¥‹ê›ÄS¶HØ6^U!¨¾‹Íþªz3úNÈçJ/EðKo ³¯“JlqÎ1¦eŸÌ¯Y±Òbrü……4(\Ш:,dÖ‰p ;fjXe·­ô@Ì-Õòä$BÜPâ’¢Ùt:½a¢ø¸aU 1­[ú[¸îë o -C¥ Eg&r‡œëàVëyO,ª­ÐB„Åuz)žy¬¯¶à¥°ª¡Ëá÷M¹\¾cZž½p¢xC{O¾Ô@&<ÞÎY ·[R5Ñ<7¨'wœÅvJBÚ*EÉ•Ѳ Aõé. å‹÷"ºÕ$%lŠ?P::~[£¼â£â¨¯lBäæ%aೈU§sÅç4Ô7ËõÔÞÚâçk\w@Izù·|/ÂÍ öë"Å–0 £ÔŸî¾D' wàß®ž,4óÚA-Ä ÂâPa nå8ªìh¥Ï)ÏÔèƒÆ=‚ÿiæ?³ÍU>KwùœUÁoørš^¯¹èy€eŒÿ$PrÀ•l%Ô½)tÅ:ëðÂÕ0‘âÐ*7sÐ+³+Im-7kL|(›ÐE/ElV_åg/¡0Oœ-“‡‰N A˃ÓKø[d·(ÎÊ#X ˆÌÁÊgE—LRßÞ¶|Ìùc±y »b€J=û*Vº•«„“U»º½€ª®o!gž2[—èí³q¹ê–”+NZ!{@@²£³Ç»ÅbQ«ßª¦ÍØÃl¤oÞ›ðßîPè_–/ö‹oiK.šM¯.4RàÕ:¨‘D›1†‹¸L9Råhì^™/šÅ=wõ?ËäqÕÞ› ¸à!ßklvÚ¶4ëÔÙøªÒ@ ,[ih>ÀìBOŽxƒÛô˜Ö¶@R_E5è=<^L2ïBCª¼ÞÙñ«>u«²r5ópöM²§s<%§<}*™£‰}oRËÛøîëú%ó/ŸÔÌRùÜèRv©`Z¥_´ÒN×#™³Þþ(7¨ÇpÛ(¹Fµ;D–ÓÒ9_ôíõÚŠäZÁpï;º+LèzdÀËî’튄¬È!1?æJ „,XsÎHÕƒqÊHF²T,;Ô~‰¯4Ãܘ/nÓüÜÚâ0%ƒY³ù#© Ñ=˜´ìȳ .Sjø & pÏ'‘ˆ›æB5¹ŒÍ„=3û3jr¨¥U/ŠR9j~žÅÜßÛUßì"Üeã~{±§Ž@-_‹ND-‘-ìña€üˆ+¥: âŽ,O½jXcx"¸D+º²K/’͇;ñÆx/²£ü¼òËÕ–K¨à­Ý¡õ™f(KZA–¾uõèéÜ>—éÆ AvãfbU6A}÷íGý€Ž‰qžJj. ¾j\X^¢ƼÓG?•㢙éMüqoW‹oj]G¸@ž>´)0üÌBs*÷J±m¿H±b¬Ù»F> ‚»±`A¹%°¤mq¡ÂŒò`»ˆÝé¹ 6¬žjTÚS©k^"‚£Ÿ þöÏÑIç-!U¬}3) Ô09^ßk8Ü)fßÎ3m s79Á›«ÂžáKòzµ$£ÈÁØÆÏÜÖÇSÜØÍ|rªê"ñ¢Â4_ª½â¦ßTôu$íá÷vQ'°,õDú¸Å¤}Cªa–Ù áΉ<Ê·ƒêB¥ò&œÀ°¯Ç¾w’œ&ûtïðþ>å¹ï­"Þtö ÇÐù§yv“Šþyb"3{_>Lyà^©¡9ºá;Ëþ™« 6´·]/Gvʕբ|˜ôo(*å¿ç¾ßK—š›zÛ^R¹1öÆD¹Bí,©ù vó‹ÔÍn`ò(™~`Q'(—§[£þá}™÷Ǥæ?a ÄŽä܈!~© (Æ^‚‚‡³t¶dVà%²¿õ»õtSŒ¸bxšï{r®ØÉ8T7’Û‰ù‰˜mß©ÅÕëÑK’ýàêîÌ=Ìô4ÐåF Ôxú*Z,ò1ŒÉ–­ÅËñ/×ÃE¯ê;Ì4ó\ùX™s>~y§ƒÉ¾‹Æ„h ïì®Ôü±Ô–Dç”ûP-OȀݤª÷°»-ñ4³U…6ÛË:„x é¸xÏ9ú¿Ô^ÛK䩪9‚ ë2*:aãOŒªRßxB$:mÎiŸ<µf³ÛRÿÆð€ M?HkE±¸Ëm¹Q’À„`û½ùŒ¡Kƶûy… ¼M…Õ­¡E?ÉkæÕð#Wˆ=UÚäFU2̬½ÅQ_ -<‡e ëÔýDjlîj§³u,¯ÂC>ãÂÛÿN'úÁì"Ó²½@–f2¯w•Vƒ’ òˆ!ôpIc¹»6Éñ‡çv4 ô­SÁ=>ï¡D/‘öÞµì›×®À‚€+ Z†¯râõÊ~-?o@5´‚b¬ì±T|ÞÈhîÁgjÄ/„âNƒ¾‹ÃHd¾-&¾oá#P»¸ÄÆúþ §°+±¹a¦SÒ¶®ë—ip»1Û¾|ÅÂm9§¶Bû ¡v/ýq²5¹}Ìܼ&ª™$C4GŽCC·’–Y¡M&uقÊÕáNÕs g‡ »E&•«Ž}vä­¾*Òû_$r±î%ã•ÍQ&Äšœžï³³ž$¼°ïÕ ]!#çt9RgzÏ&ƒÎ8ÕÉ`†n‡Ýé­`ÿÀJ*uT÷9Á”ª¯âߘ8-W$™mKõ’³8h§Ì_¹£ç#‹)yé͵°l"ã4YÂT÷³íIiŠw‰…én3ùÙ‰@)ÅkÒ7kñØtËHE\_ŠÖ˜UQ¯f“â‘D¦œý0〠€9Æuvd$+ÏõÄE<¨ÆÔú©§%´œ 1e8®Œ °e·­Qx¡–ï6i¡Ý½€d%³÷(YHrê”4Çaû¢'l"ìÁWúMF®2qÅ*æ§E< Œñô©Gvÿ;.±õ§É›j¢Tñ¦p5ÈÊ<Ð}vr¥®/ÉÅ ŒM‹ Ãîþû¯PY-ÁäÐŽZÏþ-øNÂ`kÌ–wŸuôÊÀ@ýsÏæáÍf6КCqøÛä±)®Uß§H±;ܵ<“ý;´ËKñsÎDà*á*~BP-x?Ïl'UQˆ8ºpí4=³"wâÚy×Gžz^¨ÚSPrBBõäÝR úF_žjx×6ö>mÃT³zÿfˆÎ%Ô©ÀÄrEs ç ¯<óB%JD=u‡$9F"¨$™'èû‰:âAJg‚çùtp{ëõΧ§ª‘ÑìöÔw ´½ÞPuÈʈˆî¹Ú5’V޽j7/Õ4¸/ÒÅïïN'Õߨ:`D”M²óh‚ù DWwdš ((¢dxçÈ/£)H¶hì®Ðƽ8ôXºˆGsjl /9ð1n²:Ù:tbDÿ¶&ËrÇ_z é¤èè¹”eÚ‰}é¼!´¡¢:Xß#¢ö¥jz¢x|Ò6uë|©šê;30ÍNÌ8•¨_‚A Ëº ý´zT^4‘/½°Üo l^ë¦MÁÿs1}4íoe!d39$!óª_çmÛe©ŸŽ“$yÖ¦$ª„KžØ YìsÖèEìû2s'½¢5}T3¸4×®„p‘ê7z T÷—všÎ¡”ÚÀ¨)ë±aÏ'ýSüŠP#ïåÇûƒû Ý¸¢(÷…0E HO X²ð¥Áø³‹¢P+×Ûs躡M\7˜{qY[õÉ›æÛµvlÁŽ}b§²Íç£l„w¤Bì|°Ž˜Å,£ŒVÂI:M “[E.Àz4Âö°ËT¤Æ¥KˆM 2¬v*´l_vèU$oÞ9p,ê!gdÁŸòcjŸLMw³!-{ìÁwTÔéEéÚ°n·#-q_JMŒºÍH—ÐóºÎÓlÃáãÃŽâÄRfv~.ƒø&Ǭì~ÿßjú$Í ‘ËBáo †¤ĉtóüV3Q‰Êk@6Kºº¥kU“ý.1\²¿Œéû¶Œ é›Ë˜k!¶TÞÈçCD7Ë܈òªvm»Ý#;¼cï/7ÆiÚ?¢T÷Ί´>8:rJ€úXpL]L¬y‹°ñSR\Øa(§æ ÝtKL»ül7ù´Ïp[|µe fÖР×À]˜Ï#´^!32÷…B—r3{Öõe!“(QÏȘX燌ë*$ö ZqKËþQ#}ázÊ1 õ\Íãm*Ÿ÷¡ƒ9Â÷CS>‚D%QŠIXÜ?ðòÔ ÚÝR…ƒœ@^+2ª òFLÈ'²öw»0ЃÈ\A\|íeóÚ÷¡1Ѹç_FqÞ³ÓhÚê%êÖ`—î[óuqõ™'³G k:m+ZÉj,”ý-çH ÀÏ­A63ÊÔªë÷\³Àʯ ^½-Y®~Öö®9ëbô¯@!ûZ:¦(T´oÃAùÍ¿inöûrP1â8úâ-`½ªû¶0Ö(’Ê ö¤D¸ G<å¼ vï®9á?ï%e\8¹.œ.èÇàò|?ݯâAË‹"wú4’àFmøyM˜Oçê½+öBÉI“eÏ~µlñ§ÖåAÚ’¯³¡ª%W§?~‚­>vjÉ´±‘{b`õÌÍ9žQ!#"œÕý„ï&n<ïÁ}D±ÜŒEÁ€r0ÂVpÈ|²¨£cBÿyï0„ß´ìÌ®{k÷‹»´Ž<"„Å>šçƒ¶œE¿±ïV7û’ÑZþ¾Æ;,-úÂ\¦<}ê8[phå2kÑEmn›¾Èz©Øæë†4-qŠÝ ĔaaÔ-öõ*jmºuúEÖI#•9ß·ËLwG2Ê/èDã.V…s—}+è…5ôšˆ3¾ßq‚»Á–„YŠ1µÁ&œéUAÇD1ž n§â`åêäz‰Ô y²Öúꛨ%¿lëEþˆEЪš=SZaBõ÷Íþ4×ÍŽ"ÕShkL=]æX†uðkóièû€±q†d–… Q?é¶(R9Ftºy÷Ó­H]ÜýàT›ðg 絟*w‰ø{©64A \Me`A—dϸD\mylc#ÉUÔòA÷a|Š*©àœn(Í,ŒÄHG/óú¨Á½íº¾¼KGØðP ]b÷Z§„2Œ5K/¬°8”H¢tÒÏ2ƒ ¿Âì•ñ>ä?3j¼úîRþ%ìæo4ØlýìJ},\{MBbà9>Ñpthá·]ì7þ,'¶fZìÊ…šìðÐÖòvI-+ž}2 פ•aù¤ûjÕ[lk{ '(~“}ÊIåú§§À‡ÚÀîÉv•Í—‰¨¸gîž Ð´ïdž—­&rE7QÀ7Á¿.ØÙáY5â—‘Á=”If¹µn”§ol)zBî?-¨0T^Nº—¡@ç½¶ÜnÂN[‡âÍ3:\î”Ú»·šÈ“5z‘„i ŒvÍr-öÞ– ×O¬d¸IFR“®iæ[p¡ÁTÌɵä5U¨ë$ÃqF4–¨P¿hê|(nèÚƒ¸¹^È(3õ²€»äYÁ°ÝפÈf¤žƒùƒËeSI'++ɾŽÎ®ÒS² í¾êh¾.° j£'Ð0ŒH”¾Ÿ³sà¡>Ú¯Yß»ñP•÷¬aŒ[#œé\Ö¾í³"ݽÏp“­Ø ¿ž÷¯ü ýÅd±  &šY‘_¥#ãC{qÜD"¾Œ@ôòªê}ÀÑ¿E‚ ñ4,:èÄׯ¬M%¦«ªðß`¨KC/]Ò7R ¢èó­ uVaÃu öO&ýFòÙc‘,ôb¡U ‰ò`ú(Ž)áðÁâ]'›Ìßk …(¿½â-¢}?¶ rm? mêN÷>Ø>ÐÖ@®¥š0¶ ÈtP[A"VÌ;z„¢úÛuMó´9"Ø Æ[PgsN+9k%F„\ømýÀP´¿–|A‰æ!"ú\fW¿v5øÞ$–-ÌgÆo2b0 š°ÚQ~½IíƒpÄá°ðü5å¢\Rdäq£ÅZ|á–€ß0 ùq`¹X…;•ÎC»(K¼Pr îÝô7"™zc«HKˆÒ_pÊÕ·t\Ë'[?Æ“ò@¦„ÅÌì!$d„­}l-2c×,q–þ˜¥F÷8­‚þ¨‡¿Ùá~—Mº@üÍ'³÷ v‡C¨>gA¤h+†©LV¸ Ì©?áá·sZ3•rèŠÈGNvÎ…ëîÁC=šáQ¶†vñõ¢Ûc!$" I?o]œn½{g‘%“*ÖTݵ®žïa"¶›IÝhɳ *{‘ˆø‚nA6Ýß¿ÌÀ 5éGc%¦´ç?l`dÜüt=L˜&š¥çgád°a»J±Ë¬…²ì·OÑ? ©’–¸8* hÁÒø›ë-ãgÃh¯Ø&çfþF)FíÕö¨…ѹZW¦ŒÕm’‡*»j¤XÑ¿)cqäÍšZj9˜ÔLɪÉùƒV(••ļ Ó¤×öæpFúo™÷@´V³—j¤z=d—ð|Ç6Žg†°¥ó™Î ZÄûµ4:q7îRëÙZ#&­ ½ ò.å îþ öÁTã}ÍÖ»_ÓlP Ø_<*4ÜIÔC5*ƒÌõ·üm¢wŸ(ær–,QÉõ¥Thkˆ2"×T?VÌk‚3o0il5Í *h:¿ñÄ*+µR®`BœdÝT­Ê١ٽÎO ¡h!¨ÜÂðOÎüž\xšûª»:Û´è+ùCO/ݵÚêN·6AoHñSØ+˜¾Â_/I¿×¶ÔÃaÆó»TÆ49twæùÇù$Ö‹éÏ{žÞM´¹jƒÄK¿B\±¸W/×ÿ‚/Kîž8R ²Î[qOk¾·#>>'o›ä¦áæÞ&,¢6h~²JÝ©…nºÊôD¹Ï[÷/ ¾OÙ_pÔBÁVµá›û¬Š XôDxÈæ®­ë"XÀÉ›C/ÑlªÇêaëÔÔ^–ÉV_‚#­ú|AV¢’‘.¹aàf|Lmª‰‹XOê¶÷ýÜÐk«.çn*º‡ˆJÜÆ‘žÉk jˆL‡ !1VŽ»…Lü“ã¸MNûÚäXòኅ 57.NjÅõÝíyÆ6;#ú¤ÃANýq‘m/9±TðŽ.³]iˆ“/Ï´8T‡À?í‰ìõ"%„ês¥­–OÞ/“fÓ£éý® ‡—W¹ÙëˆB›v•q81ËÜ£ï]6CI®r÷Ü M9¢¼ ’Áº“Q®šýš¶è,&JÖ1àˆ¶~J^9Û›Ú_UŸFºx ã:‚=]Bx5 r›ì¾• ({X$IÖ³pɃ:Õ¯Úq¾ÝSÃQ9«Åsue*Í^ J3T_”šº`„«›þ¯£W_;Ç(@UûÉVªí z½œåŒÕ¥û¸Ü%iYܨ¨R#Ë;sÜ`[À{fÏ,'åsUç–1/¨’%• gP—2¼ÜÃßt—T»žDî³hÜŸ©õ—Å  èÖúµõM@ëm*Ø8æ3ª Ì5°,|LÃ6Ðí7sn††+åõyÕੲg 3º*½~~Ódå˜5QlRzI¤ºÙ+˜~‡µ¥?D8ÉâÝQa˜Ø#-^dwûG3†h> Ÿó·/™Ð9¦¢Ø²•m!Céã+#ýä<¶…ŒB•Š”ón¤Âȧì^c;‰ f€\`Äé™LÕÜí"ºì¦D±G3+”'j §\Χi(bû¶ìËíYlçvÀ,öÆ‹@Ön™4¦IéHM=ý®—ÁŽ1²ôíZø-HNayq„œ^´>øÖá饪"BßæÁÜ`?_aórŒ“H1ÖØ÷¨Š"¼‹Ì®Ûú§…b] D qp-¶1¸ëùj ‘ú#-ùaó&1uU8vÈ=­ /uÈçÚÙ¹häš¹Øè” è+½e˜Öƒ|¡zàWË„ôšèßy@/´Ö­E ‚óZ Œ,¢7:ÎíÞµõr0²f~âÿ­*’ÌÌ:3šƒ«<³}æ.¤ð›hç°/ËÙ¦@qšÅ‘P¾Îëø{¾õ£•2ÕØ|TõÙÔV¦§Žµ$,¸—;Ÿäàj©/,Æl+l3ƒGt]/¨sÿçð²mÈþ kC"ƒä pät?ÌGäF´f0$à m–u¥ãJмzÒ­öÇÌJ—j0bƒ‰Ø‘ÆŠ\Y Õ ¬³q2‘wg¨Æÿ9@Æ¿x˜ÒBêeÕCw=Tæ¡;I+p²ÚÐ I±´Ã šê ÚôYØX5“p`vÖ/L@ï0Wm"怒öªz€‘B.™;Wx•šµu)TTŒ{G}êc~ ï8ž”Ë%ûù)ôÉ“¨þ,KY]™ïkçèô‰Ì´èq8Ó6}÷–¤i þÒǰSªSË)Ä%\‰?wIÄF¯nÑü»À¬¾§íuŒzöÅc£X®.ˆS×®•Á˜‹‡¨#9>IÒ1pŽï$wWeÅ6Ïö[_e̼Åô.§$23M}™ìLžòÃ$SþÓ®©ð"ùÉô6Ëb >èɦóYw±7ÜH᥎|—QdNSã.´r¦·`ØJ”w\×ÀÚEi‹xÉÕ !ÒÛ²‚ZfÑA).L3Daˆ¬—Pc±:­ä)Q kÜm+®Á»ž«.Ê®˜é˜‰&gZ—b.Ò~3=§˜…^Di ´ZÿÊ„‚¦Óß"0ð¬t¨r¯o++S}„ÿÄwc¬ c¡W÷lv¬¤Ü»¡Ï¤ÚÉëV/eÃÛ‰Ò AÉÌ=KnÒò7+@Ÿ!!RB¿'€ûF‰m:Ç!CßBã¬=”ÈælZŠ®óZƒt0:f ‰@cƒ,ÕÙ÷9ë½0-ò«]ƒî;~Òô¢‘„<Ýñ“*&¶™£Ï îH? UÏç¨I'¸OÌSÉÉÔávµ ¹ƒAÈfêíŒfïß`2Uµ¨*jߨÀ]î¤Fx(FÃ#÷'ÕF³Õ8¢®ŽÁ¬ ¶“A;·£š 0ùÙS3ȱÞ@×'Ÿ7?sË|&TN«\ÀRfU mȯà<ˆþAÅÈØ¹ÎQ«r É@û[ øØÈææŠò›þÊÿ1’ðñ#Ë— ‚ä}Á€×ÚVb˜¼}ϹƅBvÿt±uÖœQY úÏ ÊËŸ[š&ÜB²R âà·T;-¢¦+ê,,œì UÚT [¦]š¡a,ÜÔD¡vˆæ£ºþ¥²<ƈÂ3åM¶(ºU¾f¸‚‹C߸ÓÀJ–l„ IkѨ²Xë㇛]½óýµùRapïêíÕ|¶ñÕŠ-íÛ 3ñyùœÀË;rC³\:ö@«ôµW´Í”ûlÿ´ÿpWN¬¹¯M#M#[_÷âK#ø¿+F‘%iÄ{¨/Ä&ÙƒEþæÀUAÀÛB>åÄT^yöv4ç Á‚ûñ„eyÛBÊO~¼&MÞr:E¼I„Ø[ôýQŒ-È¥3YÞ,ؾО––o§ Ž/óûÁpÝN¼Ý ünhÅ ÷?À˜ëŠºTž?oj9}‡!¾öjà,áQÉ2·Ú9ÀƒØñkæ^'â‡vs†„ŽQ²åç^ÃÊÞ[Ó‹Ï.dÎJÀz£–o–”Uıè>Z ÑÇlŽš:ŒÎ€eƒ¼‹TŒ=è˜ðŸâ~nû…y.xèœàL\6 Ð(ž .ìy«®¬?ç?¼='Aìš¾²½áö…¢Ž…Š«. Äâ†ßõ4Œ”3òS]C¹ÝxÛ™.{ï¨{ž)¨ow¼<öt¨8–æ)<ú'ÄÝ ìVÓò·8º¸²+‚£SØ÷B‡W|û±BŠÝŠš\Û}èc£”€]8~ì­oÒ^fì‚=Ó1ÛY¸‹")#FX:Š¿5Õ¡zÿä*`]rø€3üŸ*Ó™»HjB‡vÀ’iЂ‘ÞAýH{–O0snùü»ßjÄ ½gMwÙVP¸ŸýrËdÉÅvÈ* ?Y|ry•X#ò¸Sð»åf±óA;ði#c~è´ÑÐ>*}îeSºG›Ä=vkd›Ú/•Æ®ƒ¾{•Ȇ±Ó”‚D}zÔ¿(šÞòWÝ{æ×[é\q*Ê?Ň’4­aø†6œ¿ðÚ+B¡øªô>SLJizpÆQ²<à T9ä¯)v<Ф¾gÏÊ*X’6T Nnüµ°/CGûJ)<Ž5OÊuiíÌtHÈ]г MÖÅ{ÿÃ6bOºÒ˜a©±¾ÂÁÝ/!& l—³Q t¹]Úˬ ¼æÖê|›­Ê4?AäxÖ“pŽ^ cßím«ôgal ¥ãÇš‹À­†`òÁÔâ´äûn îBÂÑ‚î ,}òÅŠ'BðßÁ<üYa3${ØBܶS³"ã©äXSEõ*A¤Sæ$ÆÊíøŽªÚ˜E÷ÔÈ}‹,´4ÄV/ꋃºÈ募O¸ê>Ár-#è’©û ©$M‘ZÊåH´£ìøÈ%•;zÚa”að%yñ˜ðj?+]„4®÷(Ýôëî.ðß&<岯 (~D–Íq>Hd¼Âu‰•i¬jù|%×ÓTôÞôËß+ÔàQG­ÞvLäæeòw¸í[°mö‘ÂQÚ:Âõuô©yÿñ][]§@aþŰ€ÅÝ n áÆi(þ%Áš .€3( Ò-1ZGäm¢.’ÀþÌ+X²…P3m¡KžÁg?«»zR-z"à…íV‡|_f§\v‘·*ÔB½fÓ69 ;¥ˆ{´Ïk覊Ù1v+p',m4ߺœÚ`¥Óü¢M ö‹íf)Œ§o0×þƒ^<õŽM,î€^$;Ó î2éorÎ$gãî š‡K¢3€ÖÒÚ@„‡®^Ï>Ã0‘®A'´ ²÷zÃU¹°-å²™àËÓ{‘IÓ®bþ”(Ô…\£Àú‡šÇm†‹¯R9¸ù²Tz(ïÐ{œ\eÞ¯>õÞéW¡§3†ÁýXsV~ޏgÕó*[ÍE‘¢ÿʈz„ÝP}™üÅ©NH>Ÿ*ñx4Ìš\2ºtuÓÎRXþ›:¹\à-¤šœb5¹  %øè×¼÷ŽÓ"ø°ÁОze}që—6gË3n˜E©Dq²’IM ¡{àyPB¥Ü=º–l‘¿ƒ¯F2Æ‘_œ~GŽICšV£ÏõnŽ8Yéê[·Á;è"“”zDÒÂÄïdo›aAqÚ¸ø¥Ë­Š³$ÈÚg‹ö¢—“Š<˜nч"ž-È.‘)Òòød‡Ð­¸ÎɺÁO3¿—‡±ûŒB‡,B$^#üg^h —Ù@·«”Ì4¤èã$¾DÄqù]Í땹^>”~à”`ã=°f¸è„º¨ ¡üZ®"HøH;4ðœk…ºú¸4cL —ñ®Ét8Lúòäf„û³óY©cœÀ˜@GÛã/”t;w&ÓÊå¼ib).ºgUãXðíJû¸¹1‡¡!~šž2œµšQ˜`^y EˆZdã=…àœˆ­ë­µÞcQm¨j(ž“òÛ,W™ 3w––}Qp·ùÒTÃmL¼YºÛŸ2eÂ-µù05tòó,”bmö¶+{…ó‘è€S•Ç(RA9¶—Àt^1Î&ØóNŒgð°¶.ˆ`Gcη7M±Ó½ä,²j"•ég46}?¶É¸x~¶_§CP±¤tN’ýÕKÝŒ„H뱄¾KÏ]mXž¹n½»7{»À™2Nn‘ñµÿüäiô–ÁÞåУý¤—HòUc%ӥРr‹†¢yìœä0(?Ü=˜¬X¡ØRÙéß5ª©nN9Û« _$Lî¤xqSíš’ÁA€íÚ-I×Í84¢K6NÚ[rÞýÌÜ^X»µ!Õ9öQ|赚¥ÊƒÑRŽõ´3ó@ã ȇˆTßÃÑ2³V˜œa”'(+ã?ÞLþòuö.r«7»/(îíÿCPÔ=o_[ºÊ]dÉOS3>o m“¤åAØ …ùKÐR#9T€Pf7]°-^‚óùTMš¤ß™¿Ãî_*âvÚ;ûu™°.Z0ô3•E ­M¹Æ§4üßÞ}Ú§)’Á˜¬´~>ø»§ùËv<Ø—ÇüF3Ðä¤]AÅîòZ*m\ùò‘É6ÏL1ä]]⮾„éät¶˜T7aÕ"x4劌‚7‡ÜÉð3èÄb„ÅÛÕ.dú¤7ΪéU“/͵à…ÁÎtÕØ˜l ¥8ÈW'ÆOVDgdнÍÂVÍÝõ9@B^C´!Ðyót)×ÕÖ:ìVØc-®Õ&~°dÿ˺²Iz¹P\vî¼ß14Ù(ª¹»ýöƒ—ƒD8°`=¾u2’YÇi“ª0Ñ«2·ï¦jX#á=ƒÌ„šq½¦ ¸A¦†q³%²“çyE•ë9@\­„6’餋Ù犅ÂÒÌ TÈ«{_›¬è¦d´™ÐŒõõðPm˜×û§ž çá bfÕtÙß2-Mà­÷aó!SKH7Å6 I¹Ì†ÒÿäÄœ-4,§T×K& 2Zèê_§Úîÿ΋ ðçCDL?°°?‚ù‘MÛÎèíº!7ï;oš\o0LdÇd N´yîî fˆ )M¼8È_™Ò2Š[‹È»¤ì§«ï •34°´¬À­¿L1o†!¼ Pg{GD4ÐL2ô>öèØOlêÓéŠ1ÃOÑ€³?dTiüGk2%£SL­Xèœ7P€-ö£¶hc¼h§B=F:¨ÁbÆ“°ó¤CehÒ8|à‘ÐcPwôáíQç–D7 WCé±^Þ»x¡@`¢TQt¬Í}ЄœKúÄàYÜ!®ì;j‡Á« ˆÖÕ¾îrKo…Ðd0üRÂ.À`ì4|ŽöJK5ˆOù©e¼ikm¿Ü{ý,à¶;B•UÃ, ”Yl€ ôj|i¶¼° òÀ¡Çhüò:Žÿó…Ùxln8´O3Ó_ÛßÉîQ ÷Uýý²ŽäÉÊ‹ð®‚þvéè "è^Ú>OØ Åï:îÆ7]â=]íSÆ\Ëí5ç¹À× Ðþùp`,;vÜy[TZbUí'‰½Ó,+#„1ŸŠ&¨6òåÙ;Š-¶ VÆTv^¡4Hš½aQBå1jzF¯Ë©r×D { Êb”M+‘T¢©O|?A†•ÑÝ—p­ÓZL±¦JE*É ÜÓGÂ&á6‰t¬4âU¸nšbf¼jµ–tYOïu»ãã‰ÝQ6Ù¹€p';\2ik¥m¼ªµ(C¹³?Ïí7dÑü`TäÊÞ&è – éNf÷9ºÑ÷e¨Û{>€ø±ÁºX4ö¢xÔpxÖt-V¨HH`§îÕŒ°Ilö­8 ûä QÇJúf'vâŽc¸9¸Yd‡pUEÉ@ÓôYØÖGaá/®,úâm²Ë:€W%¥ Ú(ÂÜ5O|rëkxÙb6Eõ1OdŠ¯Ð¬—‘ùa(ÛLÇ£Ì4øR¦¦–Ê›Ö<Û͞Ю…s«¥Š1c;äàZ$3½iŽûò¦[Aï¶Þ¢ ò˜ —7*οìMÆÚÏ‚?ÅÑkÑ'§0ÊÈ:Ò_Àm"†0ë@ÀÚ§M]N(Ì£J”¡¯«ÑŸ„ÂÒÛÍ[ð\7îË–T|:µðC|çn ˆÀ+Ü—uFgC"6sw•›ÁW0.ϹµñùrRÜÝ  qó w¯ÏBd½;ê¹b­*ö~‘ì+‘‚D ü›7*ÃÛèZßÏ/ÒC,„ã'ñ^ù' +[Žx¯ñçña§QÊn;¬|*Ïõ©Ž¹SÈý(3 édW¤VZ½B+Ç TÖ2»ÄMÎ=g»"uت³å€èœÝ1NXééÆ˜ÅVkÆ©¢N/ź\1Hî°„»ÅYÒ¦B0¬mƒAcTÁzšþˆM “ãD|’n KìÎò…âÆÉH¯!ЍEƒ_¥â‘à‰<_£"!‚RßTe×3Ãìxb³»t•ݸ“öÉ VÛ=éÍx¯.ê”)•A6˜OwÉïNjܨ(ÞÛâ?/<&™$ dç/È®ƒ[ãY;Õ÷ÄÃËoqy`KïØé 1²÷&=`NS {MC~ï€1´ùËKFÐ*°^!¯“¤\»P^î½AkæA á´,7ª{© ´¨vLjRN*WéݾV ŒVUßùÅ“+ }Çn¹þpj1ts>­º3§‡|ïv_å6F™¾t‹Œ‡-¸9ˆ¢V\{R#z7Rs5?@€sTì¡UI€á;7þ&¸WPäs*û€¿E$7syèvˆsÒéÀ…»°ƒ0òo}<­äâöîªäÄg¬–HmÌúI¬ê3G³%ʼBì óã£tƒ5|¹;þáÐ#)*¡!á{Åï®››•ô Sø·¶ê±n¤¶±ÁSXz Ñ< KÞ\~_úçhœÉ*2xd¬…d¦†š`†2Ù.Ù„Ä*W¥‰Šà—GÓwì ìÕ:"”Í Ê¾:§€™ÂÏJBb`?{°ÂšÈuùÌv™÷ZqSõ&\`Ó»Àùì€ÝkfÃXÀ±ú°bKƒó,XÙr×-R¢§Y~Z}-ÖÕhGëõýÜdŽŸúµ*–S4^ë‡;7ÝðapûÊË|ªCÛ“f.šd‚ô;êdškj÷Ôk‡E}`aù½:á®ãæã I3P„CÆê±meçÀûUÓ*ƒO°¦ªvÙjŠÛ_ ~ËÍŠº;îu0)cnøKrÞë9w¥ŠxW¨úOx°(ÊÃî<~}aÿ Ö€ÏRÔÞìEã“ÉÇÁ”è´†.{ñ•T b1s%„½±OÖR¾ä¦#J7~0·Àçî{5m?îÈþÄ">˜¯O#à ÚU«×|0ûR¥#¦¤8kr”‡w+‘¾”‹z›öô žqfP¼RÞ÷ÿ!i6îËÔ-‚B!nÄÞ|Ó)×»z»€¸¸yG)Öák­m3 {뼞ªqr¨a^8©—h¾â¹¿@ÀnKWopþU•úEU'»©Påj2öÔ:°„–vûB ÕÝ{ÒêÙU{µ«£XfÿYqªÃs&UšÄ©ÍfÊ{v(ä/‡‡cŸ‚M‚Ý’t ðwŽë*MQõþ–Û»bܶ¹YØÄCÖ4œíûÏ”‹­º6@L‡윰ÍÞù·î)ôDئPÍÔUYýÿc“Í…mg~ȧÑxƒþsy¢gBü÷…È”.î‹ ÒðQ„í̵<žnÉ0¿Ùæþjó{¿Y•ï0éÑÊÖa?:ðÆ•'ëÒJ9™‹ˆíÎ .½K³TxuºO “q[tØþYÕ£­'Gê2þ´aöŽHØKlÿâ¸"Ù$æ{ñyzF†²‚_þî·Û¸]~DdˆsÖWOÉJŽˆ³Ðn+øgª EŽô‚A§ËÃ7{yßUæ#š,ç©ç‡ >ìÆÃìõÏ+œ©'ü&­ÁÎÅÕ…„Åœ«Â£‰î¢pß¹áï}oðU…lÁïâMhw´U¢}ªáC*Ñsv’R8°>ˆåÑðªÿæ¢MY6ª Î¶ßþpo8gŽ6qx 6ôfÄú€‡£ôÞãšeÒ»L§€m¬™™ƒ÷zË'\_ñz1$ŒÇXÄB,<—`NÓ­?©â¸£1ö:òVO:ÌÕÍüM;ʼnæÇ¼~ü=É—úž÷[<6É™oõF%ÐSc>£å€¨Vßç „g/0¦"†l¬wx>Ö {B•wCç£k÷Ô£å>SÙ$é¹V‚hü0)¥õ[Çr 6ò¢)Iío©üÜ;'£wŽûz6ä%ž` P­•šÁ}V`mûÖ¹ZuMÕÛÈÆöÇÛÇjòšººÿ/U%ôFœÙWšF™=>ÊÕ?eÈ}PÕÏ3I•nƒ9æÛ€ ÙÁ÷ZG ®‡s.¤ˆ]1)§M„\Ñ«ìö%>þâìÁ£+TEíwû/˜gFûë%»¾ÑcêDåÍ£ÇÑTLô ¿Dj>_S£¬qÖÀWé!%Ç-óthsN£`|FÄ¿ðrëÜ{‘ »«¨ðTÇgÒ|U!ÚØÒ`5º=ÓmQùÐΚæÿk™oó¾î;^ðD3¢êV'^¨70Ÿí<ðöûû×ùWø^G‹ÏâfF‰½VG¨Ú‘žpå5ÙÑÖG±Ïs$R:£Èô1Ý*´tÏ%¤ÊVÙ÷íu‘.qúC§Žè|%‘+E|ÄqL¥U :ùó¹dÔ,ÈþƒÄO­[KofÌ£•=CPí,!ù#FXüqàxÔìÇz\<—3ÆÔB/¡Í‰ö‘3­vqßRÙ¶-Dñ¡àœ£@w0ïT¥Rí miÊB·žRúW0äx6ž/Èå™#Øý©öYX! þ×Ö%yrçRœÄ/Â-Ö´‚îÇØŠƒ‘h`¤T-D³ªÐòB¨",ØG±ò‹6V»Â~„´{äÂ­ÎÆ0ݺä¢ÅµÔ^ýˆÌ%(,Á}W§;•ì§~›ÈW*GjY €[4Q&MÒÍSƒ¦’ÿHÁBú d)mœ±îªéßʸò»âÅÕ4€_‚Á‘>…¹V§g…˜s` )Í:ºqâ|xè©®Dž“[‡»¸ÎL×~ ê}˜¬•AÄ==â|k¸eòlХߎ‡É/fؼ½©;hmø¾Ë'D?Rü.hï«P¦„ˆ/ZúçJ-+ C1.¼jœ™ad/S•Í¡V)ÅpëQ€³\V&Á¯Ã@¹Ÿí@c—íÿîÞ2÷«ì$—ÂØãáš ×Vc¯\7Èó^…غߦMjød]‘Ú±§ÉVµ(àÖÙ,‰¼Uîk³ug# à×X1f?´w}F˜¡—3¡ÓÈNÁÉÇþÐ<Àkï ŠôEЦ~Ñ—È$¡yÒÑA4œdíBÿ31»T£µœ“0c Ü)d£Q*ÀÑe˜šKt%ÃÂç±5¡ˆBâë´öLöcÈãv*¾™¸ëÆíï´”ËdB6¿7;=K7]ÚV>)Ó7Jý2ð´ü€Q0$ ¶VöV¾²7Þ͵ªÇCñ1V'Pµ@ÌKz¦EEË…8rU BÊõ ú%мw‰Æï „ _¯ÒQHw/Yµ!šß<_Ü¡‹Ú‘ö/¬/.)ïÒZxXÃ]Ÿ›‡}ئ7cÎ €9Ê´Ç?'¦hD/] lV[R¡\EQˆ÷3½ÞðÒ Ç>¯¹­ý‚õÏ…ø<¾öË—¡çݤ¼ú.’a£%Ÿ,ònf K1ä®Bu=u*ÝÍë:>ˆˆ$w8¦÷Š.Ô,¡›þgôˆØ“Ç2ò¬.íDå´ãÎ^a?ËŽx–^œÓÅ8&G½Ã[­¶ß~žž87Ü1hHv@"¸\6A«=r|UIˆƒp/^k)y8S- Èœo{$ÞCî> ]µl }i3N¢çYãJ§»«Þú×|01µ¹b²:Æõ=×ÂÃ?„{¶íæ´¯ÑÂ8áÈ_Wú§‰¾>$ê.¿ßleGq’…lEÄ-³À]§_ƒÔ(+#”Ä£ ²FC mpíža[×Y~œ†I2F;¿Ã8)§tKöÞŒù9cœ 4:_Êø ZfÙY¾9:Âêï?ògëã›ë\"¨ ^ž¶8ýâ<7ØYJfë*l…|ZùûyGa“S/d­ZîU_4»÷^ÒG3?öðµÐÌ G’Ò¦Iæ-ù±ÂÖœÙ:E‡ Û~<—,æ]ZUçnM¼˜}’ÌI*·‘ð«e2ÇñEù0fÂg¡[MíuoösS40bê (Ð,÷iÈ0-C¿Š&þÔé(¿°@÷ò q‚` ØFæ–UÀ\ýj÷t‹ïƒNMüæøŽšÄ¦r½cÇQ]2 °z0àS‚:/߆ª0¸Á÷áUˆó¬ÁõX”äÏi‹Bè<7ø(Ô×ÄðZëô¶÷=vŒÄŽÐÊY›Ñûäm¦‡P R(Þ–ì øÞV=²ó`¢ù GQ³’Dv.Kfþ×Ôq=þA 0ºí?×…¸Vþ¦±µ«…õV”¸‹O×1Τ5E9‹ž®uŒ5.‡õ2±tûu¡®\¥Õù3LÄpM1có+Ìÿ°õË©¢‘:'o4ƒ«l«Éä=áö.ìú³Þ@˜ƒdŸ¡º5`Æ]Úð„ZZéc7­–½dmŒúäùÔ·¸7nbùJøiÊøÂ,¬+Ý{ÙµÉLÄqëÊ+©ú×ò~°Ÿ¾†ûãÏ«ôkZÍYß´6KN-ÊžùW¦~ɆCŠÍ~ ¡ æj½2Ɉi)ºE!ßé ŠwÔ ŽCiVÃȺ?s´r_uw·½$ç‹n3L¸;@VÁ™ÕE `r~§…žþjIn|w: `– iK{™å%JƒÛøÂ›€Ï0½Eë}÷È1ý:Ò5^ïÜÝ{»bT£Uv:Cîúà}ÆVÀå RY†ÕJé¾l³yÍ„sJQ O›Eºj¦z{Z—·¿x"lj58PE—9ó·3vÄZh°i:ªl=˜B¥2ceÍÇ»ðO2P0%¨MÏ^ænÁ‡ì¡ÌäR˜c!&np]h\Evg Âu¯w/ÝF±#ø#C®[e®EŸ¹Å$dP³V„‰RJØ4ÔUD &K¿¹ñƒTtÍÙ3³Ïÿ‡ØË²°I°RŸÀ˜«jÕÑí­ÿ!‡uÛÍ踯 = dù?ói’Œ@¸b­DïšB'óñ8?*æêUͰ¢NP¯ðòÐËv¦üYX8€ÐjÇåç—ùÑý™Mþ„bû¥ÁÉ•µÓ÷Ÿ.PºÜ†&wJÛL£è)å, LKϙ݅¸µíêÒP¹yº5y¾°QEržù׆ŸÄ³Ø±) ÕŸ¢ŠCT)n×±äÏó®”zYüÛµ754Ÿ…fЂçú˜?l¿âD±©ÀƒÐÞÒBÌàìÃÅ7°z4§ñ¥)é¾»êBâö‚‹pˆžF<Åìµ»ê[èr ”Fr ùjÖ# ÇC¡¿75&ÏU Úóê|=z4 õÔ)Üý³NŒäKCM/æc<~QÐ8øåeZS`¸[8 BWjœ”¦`hÎfKÌ^žÄµ£zñ¡a› Ø D³ÄèLouä ÜJPŠA-ŸÑÄUJ`‰+¹«±a‰äŒñÓÉEȵÊKlnç@3Ö¸ â,òk©H¨Â`’ [Ô fáÌÐ"A’oâ®Íe‰ÛÃ¥§ytEšxxFzé_<ªF;¬3ö:W‹b¡(¡ÅzO²ÂÊN‰ö´{‡+úx¥`®«…󃤫rQ9X ÈÇ äÐú}M§ç¶e¬|Ô¾”›7Ý‹8s¨J*|a<´M,„i‹SÌ_éÖz37œ™ kS‰¹Ú$LN ·vþa'—ŠºfR»ÛÆ´ÈÄëß¹Ø2©pX†¼·éz>ž˜°GêÎÃ{ÈxQrù¨?ž/žb1âl˜loJÍwf¦ã« þâihkYÚiCOG̾˭«p’Ån‹Ì#6ÀQ! Z/~jú,Ķ…B`Ww¤“õÞzåM>×k̰@ëµEdp¹VÊå´™®XkÏ*€Z¹Þw6õûD¸»××ñ°H0¾C‚w A©ž^ z‰d$/‘¿®_ô9ïSL]9€ð!›iâfPÙ(/ƉŠ\±ˆîŸ(ƺbfç@ÅqO6‡ßTízÒVXï¯ì©X:ÉÞÿ‰÷Ê´PµÏNªPñ¤Æm Ø}×Õ(íÊ¢ŒÛ¾QÅ,aŸ­ïó÷ŸIûX^µPÚ9 Îúâ„êäÉÙ¢6²À»3n$P½wzç­“’>°#½B$£¡nþd3*Ÿ]|¯Ý‚rB64ÑŠ(‹å²A¨¤=i>õ`Dºz¥¸™ÜvW¢øc<,AÌ©õc™˜É°µÆ×²wùOi ¸÷¿gK²³p“7”›çþ0ÑP÷}6% ´ ,k_‘Ûûçcà`ÝÚ½ö¥wÂÖˆ«ÿxØc4ÀìÀmm¶iÅHó(VP…ªLú ^8gÆý¼mú“»y]ÿJ5tîròÿØýÅ ³q‹±i$Ï}2áuŒÄ‡÷zŒœZó— csÏ­Z^XÔ¦’¤;ºÖT¼at˜|ûº“Dd¼x§þæ/Œ5c‡A#zÛw5 #‡É®st”íyõÈtsvmLf5}|xáÌűݪfǹ.\$ˆ †Õ7ו:œ ³)°"ñÎuuÄg³xpËfb(ä˜È:k”N Ð/IXhL5Gf1-zó9Ôø§Yí ü‘Vs m¬p«CUOlz®˜äÄÈg{â‡Qކ9ÄSN«8;u”|YöÆ­/*·øú@ÏŒg¸ÄJ!3‘ÍvÚø-¶#cÕ ãE²õÛÐÊ–i:ÑÖeò—ï–%åX­ê9 Âdm?%#(18¼xñÑÜtÅòêWm&ª)‰‚Ú¿Sqá{èUe{¡ZF冰Lz`߉mØÜw¶Í½¢"[«Tµ>¤°{! ŠÀ/úMZ[•º.à$îr€ÂoS½5I\›µdžHíË>'îôï‹/c{æNUt?ÂÄ%Ýë8&!ݼޓrŸMcÃÚÔܼY(®h@:ÇÒëýÞ|¿ß's韉ZáT-ùºåšêxëáªCüÞŸò)ež¾¯«qoÿz’B£i´Ø.Çc©yn–u:¨¹Ôc1–º¢Wn_²ÿ¯ý¶  ä;² hö¯äûï‹mD,„TÀÖ[é:SÜþö$†;âŠöì³VKýç2J<€ÉqͱÑíûŠ8[7‰çÏ·AP•¶Ú$iþíS¿ÅT¢ïoáÿðÞ¾f‚@þf¼.§8ç·/úÙc}A”ÌK]ï2Žeµ|3Ûh‡ü("" ÔiŽŒÜ&†œIï4q´ØQà§ç%V»ÿ̫Ė`¼âžËñ.Äã£QB»²œlmX²ÆDÚ°&*þÙ⼓,cœšÜ|¿³Ý*8—Fv¤dÔÊP5üE môWKõH=rË·»ÎMF¢}8{@³ j¼rÇ'ë6›®©™ò @QJ#³üþ†9’ﻈ(fíÓ eÙ',lðÑ} »>Í«Èû0®1ˆ‚áóØA©­e„wtŒþ6|¸MAàSH®¼²¡¡—ÏHèn[À¾€eLËhµÔðÅ~Á½;¦6LpýDO60%'„õþ@ <`G/eÅIŠÖg¹^%N½°¿dmF™Á=¼.´HŽÔà‹w,¿®yQ¨8+q/³;ü4±Pþ)¯ABt¼Â„]Úª®hh€ÁA€ÃD˜h][ã`·b±-ˆüVóϳë^ÝLd Ô¹ÏHÜä¶P€÷Ö Ë)P‰°° õ›¶Œ§®ìâk^r{ÞÂ_·óÁ¶ÖFÇQ"†nœyS•tÂÄž_¤`¢™ ˆFhÁnWA†îDôü3ž¢ðˆóí1¤ÔS ½ÙÏXÇ£ÔGQw½n#]R5hqT–m‹B‡¡(‘Ú‘«‡v|§Q@à9Í‹ä³D9Ñ\:yùúÅVÀ[§-,„Kª .÷2úÓÒäaȱÓèU€ºÐÂPOU$Üó8úê|aæñ®”‰*/P:!³ðn•ˆ˜I0C‘xÌÊY3ƒº¤J!•ë¨^m(Â7‚ñÆQ¨è´×€· ËNªÞ&Ö=ãÁ Á‹“#îL†æöX"%´qUi ž”¯ø™Mct¸øiRé{'üªNuQ&ÃÂŽyV)§õ†ÚôxgÆe/nnÆwеÐMŽI``¸rú+Á"íöl 7nëKÍ…Ñi…JõSžÖÿ®#Qéþ:Ej,¡.}ѧò*5HL¾ ¤¨2¯{›jýM»€–gúsùirøÏH—»»v °aq€‡[y!ˆ·nþnkòslLtV6„¥¨49Á”4œÍû¨Z—ÄZ¡Ù‰FK¢àR+‹rúÄ¥fÚ.“eòÅÁ:šRà|‰>”ÓíK½^³ñhã^ëà>Ñ«=€9pv²Ùùòøº#Ñ5ÔŸq=Kå‹g}%HWbb)мû'þ*!ùr ò‘ÂÉèWÃ×çœ1ç+JŒaõ]•Séa"¬È¼ø!ùCçÞ ¿Žô~нÜÙ 7+äa ãp´Ö;xݴȈ#ºþÿ/ê€#]ì³ò^ÆéAxÌ݇¾Œ“We‰Õ*¡J!O´èG;B› @s¥cħÙsÿ6><| uiÔg%‘/¨­˜d¸«ÜŠÉ{1óókÝ¡ ( a#ýØÁâ½K^ÒœF%jëºx(lßG•¸Á•ß+CÂH#ˆÄÃÉ‘À%7.νÑÜw&~wã+  ƒ)w"¨Ww:'Ð5cÐþc c'ñ_G&5$E‹ü „ßà# Æ$íq;5=ψ¿Ð?gç;Ár»½{še˜Lɹvã_r vD,Ûg ÃKx­Žyq®[¨Ç+Tâÿ½`~E°F˜Ž?8³=é¤jÉqÐã.o¥fh¦@í„w–ëKx¹¯•ˆßè ¢7¡ŽUIŒ-÷þ12-} “!CŠÄÈëçÍÿiSÑŠÑÂW3ÔQžâ"_Í"Z±ü×îÌ:æîÐDÆ#~ˆzÜ”—Žª:¬ËFfOÑ(ÕHÏIÓ>¿Êǽ0Ä­md42÷š­Tª¶)`…˜ˆ ÑUv,hœSZF``]8¦M€»ôŒQ¦õõÝÒŸýÕ V,¦reoãp¹l`}Ä—Ysù,5ƒ‚¹L*‹c6¨! huW\â ?ó¥Á'N«ã'ÏÏèÎò?ŽLV±‘™X¢ßšR­W øðã…tQ4n8¬enÏ¡a˜“fÍÓ8A‚cPÿ•â îAYÊÁ†5<0ˆONШ6cÒ õ…>]šY8  ˜×⎠4Ô Äê8°ƒªBÙ Uðm,çô0˜ÞÙÉf•þP1•Úß —ôa‰½Yüi;:Ãàˆ¥A_2 Ù®F›ˆÓ•ÂÐX€¿›mZ6i“RØ… pê²?A€O­_Ýì2Ö×ÅœLƒàfl°ڀëúbI>AE˜,S•Ìå0€¢©ñ)¼ÑŽ€²Âs'ßøZÅ×Dµ°az×肹ëÉygͽ®®Ÿ!ó dÙrˆ*¹Z<¶Ý¥¹ û!Á™Õ‹<ïk•þZ$›YúPc÷Z2l@ÄÞg¡Æ;µD‰6Ù9ˆ!@ÿpnu.ã©Þ\v¡xòÇï4B’œÅJ”ä±O3‹Eô:ìú¢ñß‘å‰5Þöjd›ÜŤ9͘•?gÁä8K›Žø)Ét§vª(RÅ» L–¾H%‘IÈöwUë¢ñ —Ê× 3oÆ«`Š\ÓJŸïôßÛMß.n|{5†;;“aòl‡:hHíï4ÙºÜka%aƒ3! ‹íJ9@w¯ÌÍ‚ÏÂ3Æ—i9QbÐ99óQlâ¸ÑòvÏO™’j‡+sê¿öɳ÷•›èšCÓðÖÙ¬¤ûIÞfð§ÃI° Ýv„6÷Ž›ÁÒ"Ô?¹(ùnED¼ì{º´Ð\iŠ¡Ûè¶¢}/‡‡aÆŠ«²j!sÇ.N‰4Û£bdckp9¾×HfJüft;A“ €»?>`ÂqWì nZ€ B5Ïí\óÜôì|l5ø†$Í9)Ü2Ëß\øî †ý 6èÝd™¯9J?õ÷ê$i^ÊÖé(êè:UnPÄ‘;l-L92’Âã  =¾Êíî£&â¤=¿Èaþ™OËeÐD”¬ §°a\6R}‡Ó N¢’"›báöut¼4ž—¼ñV˜û¥îPúËIÐ\w'"ñDV—­Æ6Ô½ÃõOaW©Y*sµb³"ùÃjVP¸Ç}$dŽ„Q³©3$Œ§Î×QÅëa-ïEŸØøÃ†±f¡#Jß9‚ÿ<¸}T4†|e ?·g‰êÕÔxkN.¯ rqÊ©˜îK£ôûa±Â2K?JëЯv*1hÒóS.…·»:é³ý¾^wìAÜ‘‹Ñ"Ÿ[ØT”¦¼‘X¯™Æ˜ËÇ#„dUyȈŽnõ9^sAµ#lk,Qáµä·û\¦iŠRb7^†ùž/†h‘à36€Æ´ãX‹î}“²áýû­6CÉcÇi{«(±0"¾¬¬Çõ^žÈÉ„‹fÉŠ`µ¾í×–fÉ"4Õõ/!öŸFÏÔ°D1#fÙO;-À^yö”üF5Þþk‹)Å«ëéfçÞëÛÃæÃWÍ;s– Ô—a™œ¼§¥%ãõó+P†#·çÜ@$yáÕåÑÿðݤ=Qs:j„UÝë— `BÀ8v³Ý­Ä U;(Uõ0%1¡ÅÉ€ÍA«½Àè2D7Òæ4Rx ZA"#¯­â'¯ðHA îkBÓîy¥e“ãñYV 41- ûÀ½ë¡TI®G‘ý -¦s·<qýå‡'狼yÅgWj“µAãÐ9+ðþñx©¬ZÇ ‰ƒf‰«ªW2kßêÜÂõŠHJç ™{\¾Bb\qéŸý…HÂOδS¸ËÃÌ%¬aÝ—[!•ÏøfÆD˜”-"ò¬9€3=sø¸(ÙÜZy %“ ÿ"´ðNÎn$›DI}FOi§Ý´'o˜pGÃfÐã±;¢ú‹›«~M­UDÓùºte5ö&(p²ó_Ì+œì®í®aY˜‚°]%v$³îZ0½ÕyÞˆð%Ü>çBRÊ2²TpÑÓeâS0j'gè2 oðû\³4-èþ•sX£^¸M·Rƒ¸[D̬yÓós\ß8­gËÅF¿ÜÒ”o_†uÐ=SÿòÁ}cù F²fIºÖ6w§¬êô¸¹¡‰ZéiÅøX­L£M×z)>øY9–KÁö&{ŸN ÜOª"sï¬Ý¹Û® žáуJÅ0áì+À×`Úp«/øÖPÎ<äß"Ï@Îu·Ö7Ç›¶¯¹ùê¯b[ñ ™‹’dÛ¦-åF1×—Mh¤ó¼/°8ü¹–í5HWbÔPõÎðc#ÿc&MÂöÆuÁŠŸþM)ƒ¦£6 ®LÅ©C\iH)·Hqå+³©Ö§YcUçT1M5…è¾>À; ”vh/5i1 b¶{³KE×ÓÞ<\c{«‘Ã>šzWcQ<} ¨Íþ– ¶¡Ýdãpª½&¦à9PigØÒùŸ=ëÜ­-EŸ¸‚Nºðì=ì§JS:¬|Š!€X:p¾öàÇ¨Š¯Çñ—ë¿Àœ^s‘uôÚ^èbu}Ѹ1–Ì«6ªBªÛv˜-í!2IÑ:F…zÅ?µâ¤){@)º§‰Æ‹<ãbb|ôš…E§bÏx>DZ[Cð7UeÅs`Ì£¬b[z© Ç`s6ô½únª£fºr/y9·RÙ,&%Á3â¹=£pÅhd¢ßyÙA÷Õ%¢è—þ<¯±w¼©_œ¨ý:n¢nÌ6ñÇ‹Ðþ2²bxý¾\÷-Kð¯xîMYÐíȵ¸f¿GÅÿVøâX}Ô|ÞÄE—døÖ°ÓáXÈ/pã ©ôƒè’'»i&Ѩš-í6‘W)ÎíH9+{òá}!”6ËÚÖ®âÍ™AäU’ª±M~ÉVåC±âÃä½ÍôüZí¾ŒuuÐõ°á—f®OG<Žû×"ŠŽ[¢ú\?¦Ã5éÆã/¼UÉ¡é æë?$¶ =§–Æ]?¶ézĉQõHöh:#™+®•Unò¾®!Â+–VçcM¯ù¹O|¹ é—L)›[¸ôÙÑ*&iÖ)æ{Cä Cš°ØCÁŠÖðy$oYqåë¦ìGQ0ÁL2\jÕ$#ðʘ°"©m1£ŽäèœÑ˜9ó©A†• êbw"`p­eƒ!Ÿh=pœà²Õar™;ò©95O»î3EÞ<ÊŸµOrQÝÃSo ‰–§ «ï¶s§ÄHŒãÉ}f6ìOgŒ'±JýDøL¹>%}DkGÌ}½ÜÍfù´Mù?Sf„ÈDœ.á /¥"K§‰ø=ŽýbP,%.Vº;+.Äã8§Zoz!®Úñ‹'Ýí]ñœ=ÊÓKîDÔ£Rñé(Î' }\|^~±å¹ûZ9u€t*pè\ƒÔÅ'=<ì™Þû,»àÙî…Þç*{x{¯ I(ÞÊ#!QŸÈfZ -=•qÐóxàšs͡ѡG,‡?° gk<úBè^w/LÄ<(ÛI ¸ú.K‹zç?Cé`;µ Ë;eˆ:o¿ÑñR9˜Ý¢ë¦+£Þñg¡‹<³!Ï }(È‚ç7gFÉÏŸwšï‘ôÿ4ES¨SŸ]ˆoc–È‹< [–·„/{íêlK?¶ö8©€ç¬3ÅP½bqíDûRµÓ ØI£âe¾_æEj£—ÇÐß-çû-ôCÀPd“Äd²DV­§"œãaÏ12³bSâ¥W¼E‹8ŸÎB#EwoU=íœçÜ–]`gE.#åfb ›Ä†Å·KêôRGAXoæk¦.5ªÖ|Q­l›³ÃVó.èµý£øúþ1Ý¿Ad¼‡ú •ÇlÝΩÿ²#›;¼Ja¨ ÊE—&Ç:èÀÏ@Ø´u ˜¤G0Üä§–e©ÌÍÖkÙä¶÷]ì±²’;fŽ£ü&±t#Ëä6B š T·I÷ûñÒAi®ušš)žç`¼¯•Å–‰\œ¯]¸*ÃÍhúVYÑOB\W“³8•õ<š„àÃ<ÝÅFEš£§œDL7ÂF§¶‘Cs½$äbBu‹Q±ß¿nJ¹1·AÝ>¡Â· EÄ„ï8è&:î^b0øuüü8ÓÔÖ¸¾óÀJÆ€O8ü0ÀMŸtºâ£yuMg}&‰ 8#éôǾ+Xžù+ X¶¹IýѽõüHBÑÈ€Íküxˆ x!èY¸ðJ­u ¹ÐÞËš!ÿ‚sFmp>L8¹8À%n±W ö¬D¿»¬±×êÒ“Àã,@—˜XUÓLâQbͪ4·ö->)®OY©úŸƒ³1°™gmá?éè4ÔŽôd‡¬Èÿ­]‚\[óÙAÀ{šâã §º©+b†d0Õ(àm_Â~®ŸEgib³„¼K†%¬ºÍý´9ÒAº¯Š‰ÓgÊ@iæÉðà[RS1h¨©¼ŸTJYH©›¶§ånxž4ª¢­O]Åä’ßšîá,m´ûp†¡I_DŸ¡c=¸µˆõMËŸ¥8Qð/&#N5H’ß¿QsT6²ªUéŒWYV<ç;çSb]ÖUEâOL,gÀ(È‹‰8³¦ ¯~)Œ…ÆÆ(ƒÄÏHmÕ¶êpâ&)eøãÕ5 j²Hìû©‘Þ¹dV%â$â–sÁßëôciDËçmî@R2Vlî˾>Ü Ë™"´£ïšGaùÆžeÞZÐBmI´iPÔc7aŸ†¯Î¥¡7b?Vi AÛRbÄ$ñŽ£úcQrÓÿ6Ñšµi­äÑ䥑¼qÎ ‘Θ;æ0u€‰%½x‚Ñö¤Û½À&`[0ô¯1¹> p ¦r[Yyë‰ãK?•¤ŸßXjšñ1‹7½–É8ߑ泦ÝB²ï£õ¥ß–ÁíÕÌz˜‹ÌLwAL5ªjÈf±°ø5"æˆêmÌC:ì!Í)ô> æÿù6!­F§®îJ'ò©–¶q@ÁØËMòù¦°Ï˜oÙ$÷Y¤$) £v4ø#%Ïeqñ"Õu»08Î[HÍë\Áâ¤ã¤O¬g»eÉÊRe_œýÃ}´Ox—ãf5Mÿ¡©„«~ÚñL¶úlu“./)Œ]ÿŠE«îk‚—Êã\îÿhðè¯)Voô uOLOýÉ”ÀBTVv·÷ÐJÔí$'ëHOÕ:…Ä3Qám¾ "ìm=ó7ýî+ñÃð.±ÊÄQáûå=S¢‡0‚°çýŠ8È“°ü{w†Ë’幨Õq‘º}·.ÌØž,o;F̾4eN&TéÏëÕ–í!iƒ¼fv;€ _‰pN‰6©î@"d‚Éreòœø­íè ³…7‘->_+‚hnŠZz¹€22WJB 0Á†‹_=l­5»)6‘jµ&3]Ò+ €€À«Ãm÷É ³§soÕßEHŒbÐò-°Rœ9}¸Ø2‰lÑL±tÚð~ës²s´R~RYy"<ˆP^­nïRp[ûƒd:Á3zåÿÅôbT¾ˆm¨ {=ì’eša]÷ÅÊ%ãÊì$þEDeØ>hÎ^ýio^®Q«Õ’üžXLkÈ‚ˆv”|ùn×ué»N€v|ìs%ôˆÐt"ijU¤â®Ê=Ü`3®:<þ ¸‘Ñ™Jh¡÷Žn<1Z=8A¼dt!gbA#_Jp€áݰåôõ–¾ìÝÙ8ŽE«¿¦b[¡AÃÚæø·DW Ø€Y7MϪ¡úYŠ:.û1°¬dRn §‚àmª¦|YF IöLôK|׎ÄV_JenÛÙJØ­IÔ/Y‘Pgð œ&+&GªÜâŸÁ° ><ÜWãÃþîŒ0>¬»ØÅ›l?°2Dk¼ÂlŠÖáºÃ1­:÷ å}¢bbS\5†BH5k´³H[À«¸§»ÅVC}7Í`A@—oûà%ÂÑ—€À¤,0 »?¾P¨£íë? ÇŘFÓP„dØŽí™K$Œe­ùmŽ ¹Ì- ÇåÌmdX‘¤Ì¬¹E5’“KqHÖèÌmmˆÈõq EŒœó?œ7Ï‹ç÷þ÷}ñ{ó}ù J{ôÚ{—´NR?‰ÙÞDW¹XDùcÍÛSGZ¶hŸæðV ·ÃëZÌ:18Íðo¸Ÿ/¼%Ã#õ–8ô{•–úñæRÐ1âcÊRÕÕ#˜n÷*¬:!¿ñÖ ë]qÔ=Ü£ìVRX ²æÞk»¶Ypvk•uæwZFúÁ.Ù-õõ;õÃcÍHxøË?v;ߥNAf^ÏâRájKÕC2ŠQÁç7@Ždíí3e– ŸÑÛ3»‡)¡?Dm"~‰+ÊëdqýE8Jç8Äã~Ü*Xþ/ÑÕ7KF¤î ÄZµ°q±°¶åN7k½UÞèøzØ8jÁs@˜I_ø¥Õ.’gÑBÉQ­¤¾ÙŸv&k°eo« £ð‰és ¸ËBBœ9ë¤yàô¾GV\ŵ!£$‹ìÖ´pyú¼„ˆY_^CDh‰%o¤ŸQ¹Ÿ Çá_é'ÓÄWtr?Z*œ‚tó¾tm;+çÕ—ërìŠBš"HÁ/›ºTêB×?¤…ýZ£‡Gq0z&Ór5âÇ¦ÊÆ½u'šv¸,Üåuí ûižªÃ C¢XBç>Æà„Æð$ÑunÎrÐXÛM·uáøå2J ¼RŒ¬Ý:QH]É–<¬°OÎ%á_N»Œt3DêȘÁÞµNŸ$.æè/Ä|Yé‡íü*r'¨T6ß²°RmÄ:”¥Š>uâ¸+e¾ìðgêNa˜F!ÿ‘ þ`)ÄÚê ‹(8eÞï¿r‘Ç«)1ï wY©´ö+Ö¶ùçÞÜè(MŒ}j+®ÒàóšSü9ÆëÊ÷K6ªW™×p&ÀK÷‡‹“-æF>ðÞç=í·ešGfYZŸ“…=ÐI9ŠßQ—l<µU‚U»;ÇOóNr-Ôk76ch€M ƒ¾ÏÛdo·@õ.Ê8&+ê #ª€Vù Gt‹žŸ kËÏ>lÆú®UòC/²°³u{hÛ{fÆ}O9ï/YAsb©>LK63Mê-°)Ú7òÍAQá5žÃ<µb2Ú W‚gm-h®XÞYTÏéUÓìÈIθgmíû‰YÉV£è*ÝE¶—É¢6ÅnÉÚý õg–ònÊZœfâ»T07ûa›x]k5"Ú“+nî‹à”›ÖŒ|ÃFQŽRrÐ:O)׿¾e5ƒ®fš½ö-è Ê{GÌ = ñ³€àZ¯9 ï’”‚- pâÜyäuúd"$,gïÒ+béÇ6"±H²÷’Fën7:-;šÈ¢ìZ ùÔC#ìFùFÑdF ¶/`iwœ9¼Ä}\NüýH¶CE Uƒv0ö+ HÈFBB˜…ÿ2ùjh®p…©¯óuÙ„Fâ–Ââ6YÞØ·Œ»‹(éœénG5¶c!µƒÚç24Štú|æUUÝÊúGAßnËë˜Ï*–b+õš'Nh§´¯«l~Ù«êaËoùrÔ]ð:Ä]Ôó¶Y5D²C!Ð1ÚƒÑ7ÀdÂqy;ÿ‘pϽ!H Sá/SÄPžzÜ(ÐyZîl‰hÚ÷-mÒTÁeÆ‘§ÀvuðOÉYR¾vˆ*ƒ@¾XÆû¿Àè$:½°à¡YÜÎ2i®KJ «*³½ æ„oÐ72©¯ù!¬c{ïdúç+xÙª_"ŒÀ©kŸUùÛ„áÔÙ}´ ²•=à^™þôÂuÝE ÷EÝràúÒ^¾öð¹hˆû<ñôG«ñÄçåaU{ÊBY4î>`éÙ>è«Ö™NØ]}(ÑÊÞ:¤"‡rW§0O J§\Ÿù:n·Â9ošä kud OþžhbpIµþ8B_¨júÁ«o°"ù‘é÷ý~P@'tð[D*܉†ÚÍûÐ}úŒ›U¥" ÛlŠ£5ÔŒ6¨8¿4j¸ ¿û:’™Òép{{÷øÖÕú SÆŒï×3@ì¾Å+Ä íIÕ¸~²¶]ßûûLÈ,«À~ ½vÆé\‘Æ GÊäÆU[%ÿo>×Owë¡ QÛ/æ¼ó_£orD‹“ÃDOŠâ_ô/ûˆÿÿ ¯ùGÇF†ûGS@än:–endstream endobj 25 0 obj<>endobj 26 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 278 278 355 556 556 889 667 191 333 333 389 584 278 333 278 278 556 556 556 556 556 556 556 556 556 556 278 278 584 584 584 556 1015 667 667 722 722 667 611 778 722 278 500 667 556 833 722 778 667 778 722 667 611 722 667 944 667 667 611 278 278 278 469 556 333 556 556 500 556 556 278 556 556 222 222 500 222 833 556 556 556 556 333 500 278 556 500 722 500 500 500 334 260 334 584 600 333 333 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 333 556 556 556 556 260 556 333 737 370 556 584 584 737 333 606 584 351 351 333 556 537 278 333 351 365 556 869 869 869 611 667 667 667 667 667 667 1000 722 667 667 667 667 278 278 278 278 722 722 778 778 778 778 778 584 778 722 722 722 722 666 666 611 556 556 556 556 556 556 889 500 556 556 556 556 278 278 278 278 556 556 556 556 556 556 556 584 611 556 556 556 556 500 555 500]endobj 27 0 obj<>stream x¬»c|e]³/[³³b۶ݱ¹bÛ¶mt¬ŽtlÛv:VÇ8yÞ}öÞ÷¾÷|:w£ªF¹þ¿µæ“ŒHQ™NÈÄÎ(ngëLÇDÏÈ Z»-Œ é„í¬M_46822G ¡³…­¨¡3 4ˆÌÌ&...82€ˆ½‡£…™¹3€Rõ‡: íSþyü'ç뤓…™-€üká ´¶³·Ú:©ø¿>¨ œÍS k @DAQSJ^@)!¯ Ú ­Š.FÖÆY c ­ `jçø"ü³ÛÙšXüšý—BNC€“=ÐØâëÐÝhÿ‹`t´±prúZ,œfކ¶Î_9p¶XØ[»˜üãÀÝô+‹_Jìí¾$l¾x_Ê휜Œ-ì_VEÅÿÃOgsCçl;Y|±v¦_’&vÆ.ÿäâK—á?j¾¸Î†¶Ng ûÑ`˜X8Ù[z|ÙþRfïhñ/7\œ,lÍþÛZ€#ÐÌÐÑÄèäô¥æK÷?Ùùï8ÿ9úŸÑÚÛ[{üë´Ý¿¤þË g' µ)=ó—Mcç/Ûf¶p ÿô‰”­©€‰ñ?è&.öÿÉs:þ+A”ÿô Õ—†&v¶Ö )ƒ¼ÝWO”ÿwU¦üù Äÿ#þ)ïÿ¿âþ{þ{d)ÿËÿ«yþwÕâ.ÖÖò†6_ ð_øø_þ?r†6Öÿ&ùïBêÀÿ@šÿ“)gïÞ²5ûÂFú¯&ý§ù,œÄ-Ü&ŠÎÆæSC믱ÿ]ÕÖèhma üÒa€Ž‰íßx*æÆV¶_³`çú hkòoÍýÏTüËg†ÿŠî?Ðó_V¿†ØYÅÃþË¡ÿP­.g÷…ÿ{óÏYaa;w€ €Ž…‘ãk¸Y\¬\>ÿ;ÿRÄôŠþ9+gèìháÐþ –‘é_!ÿöÿk§ûojÄlíLþzegC[“/¤ø/ÂÿΊ…ƒ PJÀÆÈÌÈÅÈú/[Æ.ŽŽ_Øô/,øJÜÿÞÿ s@w 1ÜŸØeƒÂ>Úœç ‹Lâ~mm¶ ˶¤a•±½dšÇ¥ÍàS™‹lçFÇ­¼X›g²‡[ˆ?7C\ëåáÔ8Ÿ44[Ú±ûäò›¦Bt DCÒƒÄoÞª`Ïýw‡š4<€¤yÞ¬—‹¹…܈Šö2æ=(ú„Ef¥ló[ÑÆg·>FªÛïÔÚˆšº£¾è°S+ËO?>³7<ñð4£-Ð.A´…4 óm"Qùm‰‡…-yæ£6(“ìÓlβ|Â$6§ÕÆnêwE ^“aµ‡Ç.%®9mè‰Òõ˜ÈW¹eX‰|U‘j›~¤b‡hÏv½PØBà—L/ëgÞuÞ%–>Mîeà€ ex½¢My^¶f 0”ø!ô¦ü>ZWš*Ä`÷سq3Úùü«¤„þ%añ ýxJÜ?ìH]0¤žÍ(3.ª’Qªå$ÂÌ6Í‹ZKcì:c‹9'I–µT”§·…ÕÕ‰~ €¥Do¬yhaWÞ1öæW¡ þu\¡€J¢G‡¡Ä]{P€,ÏØîH~Ž13²:‚gz¿\OÏJh…è6RÀ|W0šiª,kyE‡†,(Ð7n¦p0ú•žÕÜ="N¨xsšz?=GáÞžƒZܳáþ’Ü~u=2šþj~$q$û›Xíi…´ÓQKn-_ÂÛüWÙ­Ÿ'Ø ºûO˜Ýx“E+º-(ü+¡“T8¹ “ñ[=‘$vvÊwŒÄîÁÁÏXÁ©K¨4/®x1Ú‹Co-§g»âÝÑp`ž}¹ã¤_F¡×0hî{|ÅK‡öÐ(&Eš ~ºd)RHN##i„ÿ;‚§ø#È«¤)^0-BêlÛ§–žgIA"agƒ{×TØüÈÙ€ÆÖ0m±+[šF»AmÅß)Ïyé©"YíÆj½õèŸð»ê9ÎÞ`?:‚¬^‘·÷ïw;þüé²ÑÛC¡ÓéÍ+˜å™MAÅ_WB¨ŸséõRx†üînö •oI0äV– ¨Í¹”ÏPFŒøy ]äFñÓSÅÝÄ*^ÙÚ%€üZô1Lºâ‡:1L~þDµ¤ÄžÓ¨|%Ÿ9¸IJq à÷ÏOr)«i«äo…O©vÙÞÊ|Å&j€‹-éf.@©š!nhÉf虿ʺ̣ßÛ€ÌôëeY­BÂÑ8J÷‡††ª´xw“Üx|‹TbƒÂ«‘Ó•¨û:v«?]3®Íw…„_«cܹlÙ&èdÅAåØó\½ÛRX¢i’FÓ(óôVHÇ0`ú?˜àT3€q)Úœì;g„âÖY|IyvÜÔ/³:¤Ç¹ãw,Œ6rþWx¡ì©„Š>%BÖÇ‹OÇ 7O½a•7U¬Ì²å¢la “©—§e/\GLuwÒ ¢ƒ/¶$g¬û[ã`C;ùYAµïÎϪ2v4Íe¼ƒxŸú b—ûMSKD®o•¿öTŸÓ¾½+8.`äÆ9œ'ßl£¸@ŸÄ%˜²Z¿ÛH-ܯ2øðL7¾jýð€à7ÿlÛZ‡Ær(kʦxï{b^ƒ”`Ž¡˜ƒ ‚0,:MàõPi‚/TžÒÁ9K%Ygß;E8(•SÅiúQ2Zó@ñàVfšáŠv5¼FŠQǪ„·R'ðRÝ[”Ç·aÁŸ–œeqé,_ŸÞÖfÉR“C}ëhYgKÑ·•‚ì’s{⥛ïØÃó“,`ã§l¹q¼†R)ÁÍa±§Îá‚B‹‰^ÓV†Æ=ZQOÂ3ºhÆIˆ#§æúý™§kH”³`kDº¦3ÄÏñ™è;ìÃý´?SyÑÍ¢v¢Èy —Ø •óL'8” E†NÄ¿hK´"“õ®‘z'‡ï×ÍÄ’f{ùO!“Dìwþ01ÓõÀ‰† Uôkä̺1Û›´nÿ¼¹JŸ‹T'¯,ôw Ýs£ šC…ŠHÝÁ‚¦›;4fpt˜”—µ¾X6aø‚IHŸ;º.7% ¿¼üúÓ‡4߀ÂõWïª{”ÇÓk„Ÿ/òO‘Nƒnè\êô¢?rç¯û'M>N01=ýŽ~DŧU•m ÚÄ$tÁ]Å«e'.¼«ëï´hÓ™Îf÷qÒxÝCuyŠÓlåâ«t$TºáÁ";í¶¢ÌÃß÷û«$h³|ßsó¬Z¹C"½wÉæSSŒ"R<™²ª„þœpˆ„Óß´(^Ú¯¯q7È2Š!¶d{nóÔ9‘3á6tæqK:ÏM&ª7°lúaž8húùkÇX*·1r â-â{àj(Ø×S¹îaö:8”ã#ã\»;Bõy\$ïEÊyø‹X¢Eºéšs¡T[úÔHQDÒ<ûXLåq !:ó&•D/F“*@òòÊ)|!ó˾éàòó«”Πltòzøæ OýtùqŒ¶\á´po p6oñ'B‡}_K­Pc"‡8úô(Z)'‡ï&Î?à¦O eOµäÖ›ÌïsÄ~åJ™Pþ”žK²‰Èg­mÔýðµIÜßp/f2G]ªfT°°U­2£þ–žÛßmü›Uªr”Û…$Õ¿³ ¦P’>žøÎ7îl—Ù±ÂñÞk€µýTVýìMÙ'Šž;öSÇÁ5 2–ž¨cû@/ä#q²‘0.O=Œß1¢XÀ6Dfà’Û˜„Öz²\aóS_E _|‘ »¦>aÅ÷õ{˜ïç-" *ëŸ eU¨gƒBŽ:HýÉ×4yÏ8r.vgíÒ¼q7£o.k¬ëéÈ 8/C…)NGSæ©1ç’|r“›X#±»ëƒ|á̦ðú]&Ì. $gnß©s±œ¦Ôxc‹Æ qíåÑy€e°L¤*ã#òþÌØjÁÍ{ÏW¾pä|DÂFw ëèM¹1}õ\.Ã¥%UçÞÁ]?4—sI¯Ð–4 m>ÒTÕ£YÝ7õèîÊè$Ç3/¶¦ú×Ö¹ý:;_Œò|U¸°ëŠÕœ$}Ÿ-6!ïªùNõ¯nªoýd®UÌ4JE+,5UÙô\Jtjåzâ+囯ªêüŽ'O.^< _ÑñG0R•Ag(øÀq[E—ïGž‡\‹ëˬ¢óò"1[^òjc)T’»FüÌ­,NN}ªX(7³vt,®dE¿¿þöÔ`Ùïªa¢î,}n_ßiiI¿CífulÞn§Û×1Ò±(óh(Ρ5ÚèÈ™Åïv,¤Ì àæø†aM¯©§”r×[Qm£˜ú÷ePìѳL)M‘ªú⊿ëÓ$`Š éôUuñ€ù¶a˜A.ö›ÊjÞb”å9ˆ-Ñ­4LèÂS0 Ü¢‡Æf…‰Bnõ¨ÿ„kíÜïÀ|cú&RÒÑÅA«ré9ðêžíÂô©o¿½m*S²Žæ¢L4(%+aoÖÅryõö,>´¶Þ[’[×lñn)Ó¤ñ/‘`kRŠh‡Ÿ7M©Ê@ý¹¦jn§ÙÕÄôÓÜMUÊzsknÏÎ]÷Å75Ü÷ çøz³/` Sš€hù"A@üŽ‘4¸B7t…’¾¢AV4Y€ç-Ô¡ö§¥ñ°ÅüT†p*-Tã–ªLs‚#©ã¶ë¨Ê¡ }Ðz]®ù7’zÊ·"N ©Ò•XHÔ%81­Ë„M[ØVõÏÛá~W‹É0AÐlpGŸJ5,ƒ£°þîˆéºGN’o`{X½CãºLí‹a,TõÒäâRúÙÆ´Pݱd=|ˆÖ¡š-ä`åxÃáo ²§o ]jÖ[·¿Óµ(þ%+ çIÐT/,„)¡¦'[ùØF{¨Œ){º.q¾ûþѾjD¡Ø„ÑÔŒ‹ŠÂcMÆÝá¿æl{³<ëBûòÙü‡U©ÿ iû1ÖuØÂþ) 4h sóùA/8tL 9ä:Û¸yÏE·ÒÙ1Ã<ðÇÂáˆMÌëg âøb¹˜¨ïkþƒÑ,)«ªÉeZ|!P&ÕÍ’w½ËݺȆ³NBî´Ö/¡èK/N·#ᔉxe;Üåæ!y Q6Å#÷Ì!íöõՉxƒÎç¢)¨VÎE²NáŒoò‰m!“À–Å—MѦÛN8Ë*™›ÖR¸MÙ€œòëTx1Š~‹þe•§ƒª“œUÃp½º<ùH¡ŠAiŽ(ΛuOù7¤ˆ jiÂã‘Ï®ùÏ `äø=¯²€¸ yö’ÎÙW)þ‡ÿÍGÑÔ´ÊbaÐ0íàpP¾‰|~ì2W†\UðwúP¢H1LqY»N!©!5³óÿ¬f˜F|Œ›MòêýWëÖÀ äØãtV´¡•uí&^HÚ´‘çz?H WƒWªÔBÀFq˸AÝIù'%+ZZùçI£Ù{}¨/|8ffÅäqŸ¯ÿË?F8ÎåÉ¥¿xM¯Á0ƒòö¾¾¬°±&Â47,Jô6¸—G´L镸;uU%lùY;·K;þŽÀüPÐOv”ÅF—Ù Çîü%Ñ 1»Iôò9 ?î;¾<š•ïn뵎ÚÔK3.ªñÈ­+6…Èшówþ\w 1”|i=ÈRŠi³Ú;ⱎþÌÕÂÖÆl[qø‹´@¸‰¼ß þП˜^)óž‘_5­‹g á`÷øënFµÖÊ®ÐuÈ«V-]QQx ¢’8! O#ÉyÊjìjz®ÂŠ ¤³·ZúvÑ|0MœáBRŽ>”^ ÍÎHx¢æ2æ åF§À¢]ŒxRZdÛ+'&âݳcöÔ8ÁK…—ÐÞŠ)"æöcмTÚ¼@ƒƒJ›AãDÓõº³ó÷¼Ç”¦m/Ii©r²Y Pá:*É™ƒÌFÀëü“ÿÛ÷ã9á-ßÉ9z ¯’)8uî€à¨L>®¯gùP›?g·Új^ø3dkeœØÙ[6vüŸjIݰҴÌV¢×½&©÷ CMìNûwÑlß½°»aøDkTâ»,•úß&>uaÒwÈf½Œ}ÈÒ”çQÑd§þU¥±î%*ëžHH C¾íÐÎ ØžÇN-µzÂb/ÕßùpeËÛè¹±üSÁ] äÊ£Ã%¾Á”!û¬õd­®ñ¥š{U8 <„I YëµíUuñáWaM˜µÑÁ Ef¸Ò}üý_ݯ€\ÞyÕ t)V“L.ýúu[)÷à¯NtX˜^a›ÈûúHýÑo Á úk×DQ¨Üò=p¶˜w:¹-Ky/×fnt:Z|ij-òÆLl…„ä}HP8Ú‰êáaq‡<É«ö  È•WpüÛ³#§ˆ/â×ÊÅia²nÿ#dÕaÎÃ(.Ìvîfª@¾ÍZGŸ7Ï›ƒfÜÄ‘y¸ XáÑ«ë ŽÇ°³¼ fJ ² ¦s"äEÁùv„Gß°cBQ±Õ65ëXöëzùôÞ–ÙG—l­’÷Ɯтj˜&é†j€D¡„‰ç¬¤ü#’t'GÓ|‘ìÇ Ù\?˜ôŸŒ´6ÍÊÛý-ЬœÜÑp–•TLÒãÀ´´8ËŸù¥¥©¡us§L%%I‡&¶ ”€Äµ,]O‡^ͱ%û:ŠÕ¼u¿7…ûÔAánË«ÙØÝ©u•sLBûóËùÏP¬häXÙUY9v·qé¤tÚª33JIò-ùÍA!uÝÛ'0!Ï@5òˆJa° UaŒR›¢· *N#•-rCÑZý;Älª³Ž;šÃû‡o’œ ,WßjzƒûËÙ—)K±®ˆ¼ôiL¡¯ò¿ï\ éŠc»—zÍ";Ð5ðฺU;’ÖŽ†#·ÅÄ BZdÁóù(Ò9dßAÃÝÙñtK'ÙÁh8cTGü^óF¥ñ?XC´ RsO޼ |b¢{†d­X¨Ûˆ²Œ©ÿJ…sM/7TúáA§6 ·O]Ãg픕¥ƒå £µžÝ#ºÑµ3Ë2v(EücˆÏ)æíO£µEƒYŸ"™G²¡m}¯ãú3Äe´”ÕôÏ‘Ætû¿)ž‡i/ŽƒS­Çq ëËyø¼<ØÓ‡‹ã—F°„¥+ÃÜ”v»w7Û)L”±·…%m¼‡i߯‡É»¹RB3ëLG »ÅãÈÊ(€O:âÍËHµX¯[mBÞ(vuÅ Û1_ˆR±¢]ZcË¡M])ÓÝõõðA€œ­AÖ%Tº ¯{6—«.O®ÉN¤·°Ô½Ïôx§ O³'r´…êé@W p:?I’¥ÚFRg—‘9¾Gƒ±áÖ± ›² ÂãvîDÄr¡ç¨ ¼†>ŠnP®‹ÒácH¡ëv#|JžaPk±þ¾ ³EëaªAÅA6oy¨áV=lnëÈUN3õÓÄì'Ûé-.mâõL¦Í ì–½Vj"È:M&•g.u‚¨*F¯6wIK[Î)h¡1" CVãH‚°G ¤Ü&ÍpԟȺ­½DBï$³\¬3A}±õøZ bY¿Œ÷gË|Fm²÷%·Û€†ŸÍ‹ ù«ž8YU|F‚ô#é}$QŠ9t¼0 ž Yhquâ¬Óç.‡Áð-m/¯$½1DwüN¸˜‡_©ö§ðŲû¤ÆÔ¦¶ÍGžü¸+YHüß;J…Lìõû0À)Ð’NXã?H/<ó¯ZÊ0,ù bLQVÃbÏÔÂ7Ì,#.ü>˜OHÔQà§ñ¨U|¹w¡,|ÜÑ=Sf«Ç`äúÁ¿Íæx…ð}Í&n ´oÓÂÜ®ˆî½Ê|TI™|Î*is³–ÍÀv+7¾ºUB-eýææK¢Ü‰ç7×éÑë )©G Àï&Î^„òùïT¬Ë-–Ìÿ ¶¥ëø×a-šÕ2a÷D¦h¨l (ƒCŒ@ÊiZÆœèn•ˆ¨ŽèƇù§€:ºÖN¹Ÿ°ÉYá†/Rãë—‘¨y‹÷Üê6Ô¢*ö¬Ã»«?á4¬&²êýl×aÌÄ= s»÷(¼,æï¬R3ÍÆ‘|2H=Ë´ŒlÞ3+¦’{ $/.›¸hÓÑ-ÙÖ ­’·X«;.ÁèÅ*‰Àu¸cØhÀ–Ĉ¿TQ–åµÁ†Ãg¬6ѬGo¿ì`ȇFœŒO@¼V9ЙÕw‘8(zyÔã1²ešZ<ÿÙäT…¼)ލ Ñèë£jÁ‰ À£|õüí“§a×SFjQóØïW2ùODºŠ,‰'k&3xBë©×wl½gÖ•à´Ð»Þ¡ýSàÕåqp5ÍX»%8EõÚÞº¤µÊ°d„"ô]ŸMŒJÖ8 š“"²M2Œ©u\¢E‚kýÐâZVe1Þ­5Ðó8²îˆ†3Jzœ¬-š¡I•8vJèÅ©ò-W¨@aÓó£µaªFçÔ[ìS —T~ñxÉrþ֪ÝRì¬QÎ=ØBxçEEâ;T× ªjîjh>¤N«:|ZËOb¨ÂSþ€pJ©rÈ´6IVQèÑÁ ¶ë£¤êE|ÚÞj•!ÃãþAòÈ#N®4HŠQçÏ5ןbA2J·~K»âblqPMUo›¶8ÆÇéõ¼Á›_ãÿíïuTü¨»¯ÉO¥c øó!ugn›PDRÐŽƒز–çšvZm×ºŠœûÆÉüXüÇ_‡Öaѱñ©ÌÞO‹§«d*Mº¼Ž‡ã·æËÍõÙR=ÑS˜h*Ü‘d†ëˆ³DŸµ-­»5bH_ÌaÆD¬Åßbo3®jFõ”Y¥»Ta/¸L©= ^ZÝ-t¾Ðì'Xsr Á¢¢Rë®…D‡~)Öð(uî8¼Å¯!ÎwzT §ÏEz**¡RÉ ,*Ã&v7ËkþÔz D¡å$ã†"Tƒ¸9t c Ù¤e“NÚÛš ŠËíiÈžŒHg öµZ¡àiᢼT¿õé6؈_ÌL‘óYæß›ÌÀøØÚ¡º]äéù%•·H_ èr¨ËèN]&qÏ›»‹Fì=¦2¿3k+õé›JZ²+ÆVîû-Iwh¶ª(“ÒØ±ya)„2ZËëEÓàùïŽÿEùåfVïš[ØÐ‡Pþkk³\»‚º5ÅÜSñ4j¶.P°ò'êïÔS›nŒ9Ck” 3û³û¼}”³ÿû%¿ø¦†BE]gs|¬XÍ>JQžÓ€¢“r=Îë7“Äå6ÓŒû(À.H†Ÿæ˜TßÃŒ©áyóù-{+Îåm)^9uâtÓª= Ó4§Ný ˜éÏÁà”«|/£¶¾4…(5/êlZõ‹õ2ÝlØ(ù™ÓL•^ø‹‡ZÞØºûþ DF¤êtÖÏcN¿>oï`Zàïh[`ñkgõ-j9QÿŠZYÖa1Âñßn(—Ǫ²Ü]&±=#™ykŒŒŽÏ¯ö'®Eý¬#Õ|ñ²‹ÜÊΓ†}ù~)nºÏüwñÓçj'#ÛKÔ¥|~„fYIqº *y,HÑ]käÑï4‡AøÝn$öi­4œo¸ásû̡γÕº P Ñ·2Ú ªÈ¾/¼¨…^ö3¢„0½zŸ±?þ4?Õi-X…¥¶}/ÇÞt„¥ÿ žR~ÞACÛ'㣫ýã‚$*Å¸Ñ ÎÆvî_ü—oËhw£Dg–ƒP¼¾õQŸdÛø.!÷¹X¡0f7’·±w€=Æ9H3¾ àú›ßĹîÄKììÀN˜½U}ßðŒî™=7)ˆàèñUŒ« ½Êèm¡#)=ìõ»[¸ž¸=ì–jqÕ_™÷‰$c]çT©¦Àã ÛìÈÏQg–q§f°ÀNøÓ@¯Ê-& T¬ðŸtÚ@¶ØE‡vS¨œ§î ›.§Ô %UÿC×ïg uƒwwµêÛžx Äh.¾˜SýoC3=9ô»ƒîå˜N©å3®ŽgC‚¯÷«Q{û'Äqt`ÚëèH]K†X±…µŒ'M³ pmè¯b},Äš6Ó¨PÚŠì99IøæÖÔLòº…Ú?’àë(bR‹ŒÐL%è'ß)mÕ(ÿ¥4[Ôßé$¬š©­™êc²mòµW°—bEò£ØäÆ¡†¶2Æ@÷éÞ«`õ+Ì=ÿÎÏ€ÎùI›>ýÝö$§FÁ꼨ÐY¤ýæÒÊÌ»3 Ý Oï^t¬.¯BçE2o=n.œF¹¢6¹–é*CsV1Í#‚âÄ(H¬D×L¼IÔ{‘¶ê¦íª{&ÊJE@`þɌ݆æÆþ*fk»Tjoê©Uqs×v¬&ŽY&tb{u‚Î*…bÜ™L^Þs‘Á¢Ül–CºÇõ-›—°ø¹u^zËЩ–¦·qW¹1iÛÔ׋—bÇ+¦g÷=©„ª ·Jõë†RñóÉý«ršô%/'#ÐZ$gEŒKjA¼ÊaGëRµ2 ÙÙšj°âÜ܆¶÷D§šõR…ƒüï5=¯IÙhÅìØ”9Éß¡h‚1ǃ8×BÀE”ΛÆódÿPÜ3ú”=³‰›€{õ}™øŸg)$«„IÁþ/ ÀÍM­$Ÿ«ÁæMþd!²¬p¿#GœéÉ*(g™j—(‰Xâ,ÂÆÑÅ?F¤8&ql}¿E$½õÔrfņué¶pô2»:Ä.Ää®­W-ɿLJgÄêVuEÇr*…ž6ª5À_’ñâAqÆ©c!Êü*?J¤¿Cºh5˜úÞMm @'}$ù†ö],Å$®v äweÌßèS±z¢WâlbrñÊûžésZg*jôz·È¢Y•Ú½ŒÔ Ô„o¬QDqîzŽãçüõÖYlJE¦ûáýI#‘²wÍç¿NdN ˯¥[»P"ð@1ž…,?•6bó8½FgOáÈ£!D¬V¢O©Ù4Ρ*Ñt±$† &^¯ ‹:¹m™¥•ÄŽÛøÃ*1èÚnº?ùöµˆ¨öä©ZÂBGÝ÷ƒïnÊM0IXNѯ¥Â—J*Ì!º {@Fž¦?ÔÊ ³‰&• _xVºÕ{“ô^"‚Æ:jí$1²ï-,B¸æ3©AjÚ_O NîÅ„ßÊËÔëæm¬yëüh¨agü'Qz5\BoÝÃÙ8Ö¤&Ô,•[Ü6OV†NÈ1uôAîwÿlóÑ?ŠÄüùX©:˜å9ít©#¥{>ÿ ¿`¦§àO½Õâþ-zsíÍÕën¨u9‚Aÿf@µÛŒ¦ÐLD¼ÄD^I‰º—Õõ\ üY²=3¡š©¸ï̪ w…rÃ5eœá1AÙwŠ\âêèèÖí\/Âa¹ÜÛ#1NfŸÜ‚{.fª5k‡‹´TÑÛÍ Æn î¨s)ÄRGX*i‡oÞê²G  ±YW9«ºl=ª¸EݧÛÛØŽÕòñ02¼ƒ[ÈXǯf'Š"ƒ¡8vÇgÃÛÞñp“ÂEÏYk—ÂÒ¶fäÊ$€´ Áʃtš@v‘êKI8‡<§ºVY刨²ÄˆŸJ<Œ¤6ÑÔêX#š öõ;1íÜx!Æ[ ›³ÕM¶#±Õ \Å`ñ%IÔ̾Ó™‰êLâ Ò6rºšRA:Óñ·Ã×+¿ Ë*KY»M5“õTó DVeóâŒæƒxC¨ocñ˱T9›+ŠòjR˜6°ŽË3¼UbIó Ÿƒ/Ÿ?n7tgãOdŽÝºÈ ŸíæðPŠ’ôV驌*-娗ƙu³EºqÔ…4 žÏ¼ºÃº«äqòÐ6%’¬€«ÂœßŽüp 7zùËe³R'bHš~‰(4×X¬ôä³Äåd?F‘wLñvdo˜P­h£)IO´zÇL Ò…¬Ôðé»c Ö¡©È$Qç5!©§O:UlfØêîBXxó‰Ûw•ÓÖ#“—qe• ¶•'dY ±0Ô% ~qÑ3ŽJØNCíÕl˜®^š{ÁŸÇÖµRsvöIÄB¨QÙÐk?/ÏØÂ²9WÂÆç¹}ä£!ÙØÞc¿Mv |¼ßóÄŽX(ýb9l:»Ó™üP p‹W©g½ŒEDŸ589Cô4òuGÊ.”´%pÌ;ï/û±ºÈFz³'PÖ ì05ø™.0¥”5"áLصD„fÝ ðuÍIµE£¸>)…œ;7#o”hÇÙ0ñ‘ ŒÏGM=QSŸcäpã2r"°g\ìò*·lÂÂ-LwõÀ'¹TÄmYؤÓoì·wmE·ß0qþÒV#/£)c›C‰ëñ‹\ð후ãC»§°HœmøímËà³fêâô-ÝÒé€?õêòðìGø'Ö“L@x)‰¬±i‘~·L©ÄÚ('y§X”Åc ìÅ9!z“oýº)æo ãKñâþ–øÜ14é1VŸX½p^"?Éù“ ÞÜ˯do(iW³!Û_ÇF€Œ<–/Ô,ÝÓ¡þ¥sœ™*‰;VîF’ülv‚ ’º"ÕI~}ù­œo!z&Ù<ûÉ%ƒ¿-Q0ë Zð ÙyÂ%”õŸŠú8÷Ñö Áåüy+dYªÍ­;êQMRÚX'h¾Om/U)‘ƒZêÜ­GÄX\’C°¡Umôcn'!x˜h¤§$ñÆßa{™SaUjŸ—j ßëÂtžÉ”Í–ß¿¦Ðãβñ4Éwîß:þï(­¹*Ý]Ó»¡E[o waŸJÏY{×=Tá÷L ¯b“ud=ꆘR©Fv\áýuðjFÎùµtžáª0‹þ}ôyÂcApØNíü§µ2dǒߢe@I,wC9Žq¨[nD†MuSC $§y{›ãXO$ÈŽðã¦mCÖ_Šp¢ZjY$ªOýÕ0„ÏmC:V¡}zÚuª•mÕ?©‡‡6Ù@ßc’¶FÒDØ4¦ûb¶ž v+質±M³|=‚)‘f¾Ø÷\ß.Ä0<ãæv;FüšDÕ‡ ÇA×¹ ‰¼tgà-4¦á˜¢b/98ÃØ¥T®²š›'}õE3·ñ\É 6}Þ‡·ëº á/ eUü Ë]éà%ÊUÌ0É+è HéÏÝ·ŒaŒ? ¦ä “5W ³K-H…ðG÷&Ê(L³3]ÜŠK«'ÜÖ‚«¸…K‘ÞwFã0Z”eqÃÄ>ºF…Ñ+Šš ÙWttý ñs“›~3üÝ ŽnÝÊ6æn µ-%/h¬£æbDóD¬]J-«¤Öø‰ì‡…kiW‡¶R^ÒŒåèÍ&¿.[¥ªr˜&…ò:Ê\ñžË/F‡Ù‘¹Á”-p}{‚‘¤¥LÖÿØÉTúDS[œ¹¹˜;ÝÎx²m¬ ¤^ïÓ‡€ònÔhë »zABhóñû ó(³C¯ÚVvæ‹ðrc&ð ¾›Œün#OÍ}&w³XfgÉ×½—ž®É]xGNÈâøYy—*™V4Æ<˜/Ç‘Ë3vt˜sÑ}¹ÇvC\0](©˜.u9aØøYVÓô[tz.b–wÍá“ÏÓ¤iéq4"|‡q°³¾bËIù×¹ !b™šh‡dšÃC Ý¾©^B³ ìV'0…’Ú·À)+’²ŠÍ§Å©tÁÁn£ªÝ eÓ”ÂwÀÿq¬Œ¡e`,躾ºCúp²x.ÝK-Ùʬųòƒ§¢¸·>¢p$D»”­|µPÁ‰Oq)h£g2!ìþ#b7ôȈ{I²_2ò_b$ÍÍÚ²û3 h¨ùHÔ÷P¼9ÄÇX§îŠæ”®@Û¢ aúîú7¹C¤o+Q³3=bff1Þ%E=§îðD›?Jܦ©ÞK[ްUÕ”€ÐèLÂw&"h ¸Æ+–Pþ1ÊK­Úšucê %mV:@]8'‹4Á—ïž_³@È)¾yo,SlxÓ1©t\ÿ›ÎA²%Œ õ™y”$FŒÚš •Œ‹6|~)mp´ä“’ §Zº÷öÍ-ê SK\}kû·ˆ©úõŒèɦ–{{gÔs¬‚Iqß¼( %z=T|‚ŽqÒ³Î쉌[Æ.‹P|Ë?šm©ó:H‘Ish›þ—£>wñŸ7¯,W Ù©Že÷·ÁÀ4F"Ò^0!…!m¤-»XÜ.­•¬í’^‚y/ÀÂçt÷hJ¥€òwŸßÃN“v¢k†R³Ï>Êb]ý6*(X¯wv¿·ÎOb6­œx$‡ ås³ÿ°ÛŠg[`!t3ˆl¤Hë`1ÝЪ‘­™@ÆÊ—<"»ôâ†vÖcžçi¼Ÿ‡œ àˆky6m Š‹'4®mÆòɾ6/£1{˜Ú¶*ûÀ"õÌŠáeÆæ&°B/ ¤²euŽÀ±;D=½yQr‘œkçÆ=!rÈÄѸh› Þ’f– 3N@H8ÚÆDfS¹QÿlR¸½"r™, ÍyÖ¦ÑPîùe C]¾Ú­àŒ\ÇÆÝ”]4\Àhh3;¼šM?ºc¸IåZik8þQõÌ5.!%ˆ)R˜­½œ#” ÅÍ6º$¾MÛ8>ÝÎ)å`þÊy£Ìnð1xçu“Ň£Ñ`B|ÙGù]®øQ ÂëøaC¯Àvu‘ØÑ(õîXD¶åŽŸ×dýw¸"jÇ¡_³hæ3œÊ4ú(L»áGñзbê]jñÔ_CÖ©û\äX›Ü#«Ç\,Iœ<'¿zöeƒßFpšpJ"Êø+äb;È÷àxÁG& šÉêòÿMíøPÕ’~ùM×$:.¸ô¸Ö­O_’ß‚h›(]—Qåágµ`ÍõÂA¯U0ò7”éíè6fWQ‘²ÏJnžYÞ…û¯ïÌßæj~|ù³Aiy}n*añ2ùd½>RðÌöç¡–ï|µ=v)ØY”\êÙ½«Ò‹Y&HWA7 Ùr ìºÊKá7Tû1ÂY±‰0yP2’öï¦ c.;þ^íœôÞ—,Ó”(&®Ä•¯ Ò™„ ûEd’‘gv³ˆƒL˜øï«xi‰Ìö”B‡©9jíÁ]˜äAP™êªTRO.9†)pª“Ù´¶QZ¿´ý+ ¥ã ¾  ½ì=´ìÉÁÖw¥ËðÕé’ËÓ}c¹¶Ø ôw0gĸã/ôûxæ‘b¸ükñÑÔðõÔ›¥ÈL`Wš+b´U.öëû ¿ k‰Ës¨“ðk ž{B^Ð2isäÀ *(ÌHp#G`£ôk,ÊSžç]‘L²àÖ̘m“l—æ,µAfÜà×Ò"-ÜC›|42EbüP¯¼‚uB¦M¥Šû\Ò‘}T×8q¸¼}1³ÜÖBi¾Óßâ"ª&_PÁ©ÄG/-vó‘5±+áÉšøZ¢6:þó^e6Ôû𝑬E϶k÷ŒúûÇÑlt3Ö2Q3qS^ «Õ1‡ZòÆA®!Ük›–"Ú˜Âu$Ys½ð¾Ýè¬}.¯Ž ðÙñOaÖÏA‹ï’XD)—Rÿ $@Û¿çX‡1À€ºWþÍmP·Þs½¹w㘤ŽWaÙ Ðó³ÂVu íœÎª2NzÅ5YÉ—{’ÉÔMƒp©Ï.É´´oä½Ñ¾ËOXàÙ¶@ÖÑ\òð/î[ºz_Öñÿ–§-ˆçNhõƒpoôI‚às[LžQY"FÓŠs[ø Àì_CÜ’³›±HÀZ2âì»ÑgýRáû ¬af3ý©ú¢váD¬OnEv ñ-iE¯µQr3ê˜sZíˆu2ÌÌAÍ,mrî4Ÿžœêé•3e°»#U’zÞ39h}©ûñîô§# ¡ç‹u2ø ÛLpÚ„¼s¼Ø@`—^a·ø¿è°pl)²!I* ?õˆg1HðL ê² IÎ{@CœŠŸöº5Ï@½¬µ™«ÖÜc³Ñ%À€k"{F¦H¡QJ¼Ä¦ËÔ«Š&Ý. !•L{ò¹X\øa>1¯eE[¿üzã:êƒ\kCÊÙjTÄ.l½6SVI½ ÀM ûm05Y–RèÅqp•8ã²áêpƒ¦#8ߦs}þ=‘q½.M£¢³6¬ÁcATþ¸a+E°*•bu2ö£ÈË‹cÒ.è>Áóî|Àò‹GdC¿ìÂшÍK4ê:é" E1«:œèÃd’é:ÿ'»Ú†¦´!œiã>ÃXp òzÕ²¬ãv˜+2©lzSMFoµïÉ‹FæÝ@¤œA·®ÃžœùÉZ"eÅò‚ذ¼rÌIÜ$|޼KØ4-…åÚЋ%ß­þÓ&T½ ¥ßÝÂ¥Uáé„‘ð‹˜ÉW=Æ$ÈŸùË0m…~Ñ ð®ã³\m$;ë)a¾ÕkÜ ß<\×' «—_9f jÅŽå˜ÑDíQÌ>9>`Ùá<Š˜‚Oå[x‚Ö£·$Ùq¬g'/’SwÊQ¡BÜæ`«~;+E¡ý„i‚ý™¦ÂüO2ò`êÿ_Lã >5J‘ßD# “|‚òQ¥–=xbü$ëËA¾q¬mfº qCÑGÌÓï}ÚF&[éIcÝÚœCßAGNWò‚¡Þ Ioâ¹7KàP=¯Õ£Ænôô|îÖÃ,·v}·Ôß{?YëµyŽºˆjI†Q˜Í\æ(2ž%âÀ'”Z,,–ý\ØÊ®6:݇ô±ÝÚïxôOeÛ¦Ý!ÒÒaòæVöYÆ”ŒÜ›'º›Ë—¸ÈÞøÆf!uJÅzİfŸÉBtãøn2t©¹­‹Ýv˜xÿ^nõ’Õ#kxî.¬4(Í1# ®æó0&™ëB(¡ bã¾ø©B¸¤¸ÀvâÎFT(]"sDŸçÒºÓA™$®ëQýNöC¾žÀ}g„]ÅíeCLÜ©À¸tk¯†>¤’ð…lÜï„ÌGâÅŽ}õ¼–é ÖÃjÂ,7ÄÂ_ÿõ\öé|¨{ºÈ·qŒ4L»W™Ë_A’!IKM¢ããúI¼„Xk@-±¶,u^âì âd÷iì—BÔŠŒ„€Ùåÿ÷3h¦0Šªœ£ #*\Ço7C—LŠà«4yìÄôT'Ã;ŒYhðEY°­vJ½‘€× Å}ÙÈ.H])Ùêp#H­G#AÆ1œP kÿ¥ÖIGÛ9ÃEšú9m6oÊ®¡©åñè«yÃåóÍŠ\êƒ?´n8²=·_OÌŽ7~˜Å“ ®J–¡žñu!N³’ªÒ!Ö¨ å e¦–±7JÞxʹ9VÞ)ŸáeÚ¬‰ÄVóØIXÑMqjåÈAê·n¼aÍÔËß×3,•FS-„âðCyQ cÛ*YGÿR¹çúÙàù@ãœÓÔÛ¶7L£÷ñE}åwAåô¸RóNHb+mS^q€{E¨ñø§j6{²êƒ,¬¦/¢þ½¨Ø¾bź×n©xÛm]ZQên%³Ž?fã=  vð¼tİB«¨*záF4løuN³Æ''´}Ðð¨v…äøó4êh©©ôÊfø9FÛ¯:åoåuF§N84Ζ `91T×M¨îë“¼ß ­•ô}Òq¥ïvÇÝÒëºÞlTê“m‹1æŸQ§šsÖ®$ÁQ®è`çí¹6.S²£‡fB‚ŽªUÇꎟenCÛà%6ävº{—Ϫ–¯u°‡µ}ã[ÁËûÎð(DÕ o4Þ#5bòn¥…O-¤³Z½'šV³[Ü2ëœ{NÚŸ>LÀw6¥¬Ô¸ó)õ_|Ã")KçÕçSúƒZ.·÷Ì|É¿£Ü-Š›Ó!Ô0š»0TB*n8~’[‡ $¸;€æã,™Å8 "Ì'“’ÛÒÉkN.å¶NÓØá¯s«yd[bñ]9òE±w0øÃo,iÎ^‡3ù íÚ#÷P&:’ȈŠé7”CLcQ. %tÙ­éêöK®mã¨'u‹"ÛIòHÑzÅètÉq#|ñóÃ.[n 5HŠÃ»HÜÐaA‘B¿¬pã&>Bei/å*– –B…CÐd¥|>Ó˜Lùgí'©‘!÷A/*‘{—© *·é7‰Š.Üš•ù6¼çµ€ X ‚z²atñÄcË{*A®ô:ο¼È Rø”€l¢ ê‹«ìßüþàZc3]ááW«ÈQ$fÂ˹÷#fÀL¯ 5­Š|øõäOýݹbbÇïË6 d߉BjMë‡î˜î M‚1 ð¯šo‘Ú|+T£ÓžÈ{®Mæ J®ýÄ‚µN¥#•Þx´¢Ic„˜©¤y|Ætz'…ë+[÷~7;.X"ñ(ASU`%ò×ë ØhNLXɨf Ç\*!¸¾gM ªÿQÔ€ÍT{:!§íäg²ÿЇÖK‹Ð™¹'˜/§oÜsNS³ e>Ö&<Ñx.Xa ç›ê·Ë»~ÛŠ«ésBªÿ@yZNBHBY<6®öJSÜfE^F%>¸¬7ìcÞ)]^‚ú_õ!t~t–ÅhjªæZînB…sT?}K}ÃqƒgÅŠSx7׃Î6( гÒÎÃÙ •HÏRÏ0E‰Ì9È:ÀtEGŸ€„S¼TÐÇ OEÿ ·Y>.úª:ÈÙÆTŸ­<¢ùĒȧnqêF>±.M<3t6ÛXø´”#ÌžúçU‹¥?Û< cýÄì•N™Þ$v d‰š-jï@%…ó“(Gw„õ׋ºB" Ü7[ðÅg¾®{÷Ò|ÝÉÊsM_õvF` ú‘\÷©7¾±H.«-¦_‘7J+rwn#<#ý';ûWMMåAþMgÅìcä=û;Ú£8£àÁ“w±Ul¨nÐáFÉhÇãŒÉ¥UûðŽÝÉ4•ÐÇ67~|Ëõ›çÍZdö¡ŒÑÖ>\6 N¼’ì$ÖU™GuS¹g+Þƒð#*«sÙm+ÓZkÃ}8Ýâ±½ˆMÚ• =KiŠ¿BF/RMË©ªß™°;x¸¹è>7D=É,•qt1(ѧÝu¹§ÆsƒÊãh¢ÎÂ}Í5â7X Lš‰D«°U‰v`,xÔöî¢:U·§Á¯J»IÓyf-§&‘J»˜}#NPÓ?wU{Èraî˜@¨ßÅÄÅàå‰ZJ.Œ!ÞûùŸ½¤­¶•g•€–¨J184éãIÓ:Y>çÚtb(ŽÚ`„ TœjöšÒþuA9Õ5Á‡z‚y´ÀW> tµC¿ W$¯Ù˜R”gÜûïbl¸´Yî{-šC•OÜ뤟 Ûí¼ìU™Æ‘7þN³Öz%žï:ZÞ¦@Øß‚Xô2{w£œ Ž’l¯3F7è ‹ú®wiìª!ox¶ ßt²€^3óák±ÒÿVц”¦K1Jm6F—)ûì[ ¾·ç÷ Òç”~OaçìgÏԼ鶚YVr1}‡Œ ,žº'‰ã>ÕŸ`°m¸BÞaj’ÀÇeS‡Íô™!¼ÜΫíBw+3*«Î$þ7k¦d*ÿ‰ç¢5ú^aáXs"vŸíäBÖøîDs‚¨üÜ,»[MóÛ󶳘ºÔíŠÅ4eÒ~@Î_…B:åqÄÒêŠ}T¶¶póõïÚ| GhМÀÖ$mHÏ7oGó꜎Ä(O¡aGXš-=­}M¬¸‰^\Ö +ýhbuܨ{NÊ垃©´V°´§Ðâµ1Õûæ(Ñ™çqzW‰BÂ&x™rneƘb4–QN UÐ6€ ð{Ï»Á&ÁBâsŽpÁ÷ˆ÷ò‹JªjÏ\Ÿ 7ŠêÒû^ü¼Íš)tzÉ×VsCÿ?Ó‘^” q9´cV ¬ØÉžx¨ÛQæ@rV ׌¶ÑÍÊ´îÏ}ìýx#§#”ÿ±“?ªÁñ¢9¼»¼ Ç@Š9^÷h…ùßJX‰9û@6$P¾f)>]M! áõi¤ÀèžYUõ¯ø ¨8Ôð>M ‡6é?+e?×%‘ž+`:éÏf¾K½Fè\_púyà˜¿Kä¥{Û9ð/hÈÏ=iûy…BÓT\;±6^FÜ`ÁÓBÉC"î–üÏ’…WoOGΡxj,ž{ëCßXf8µÄŸVAéþòB2B}[B=Ë—×vÍèD¶‰F>â\-4ϯ¨ Á¦Ëôn[Šß|Q.ã锩-VM­¿¨3#¬ò ±™O9¡ g´³-´«ØÎ÷ ¹Ñ¼”‘m^ò©—ãu_Î%~… ðØý‰·Û8ÑeRÂ3;Ä¡§2a•_<„,ߟéÈ¿K«/äò¶ëe ¸Œ’ÿý3§{ỡÛkÎ3jýaJ˜oSJ¹ y¥˜¬Ü4ËÛ/­tHîÓ¿´Kð?ÔW ]»˜™û=ñ‹7\€%ƒÜê¬8>D‡ƒ[Û}ª“pÎÓý⧺gC—cøÐôš"ÄŸ•‡_4b+±ÛÂTAž6Æ6¼2<v]Þt)Þ¡P rWôµ<e¿UÙXÐ,æß‹ç.àt{¸…ˆ„õ8C· ÄI»•,wÿK¶yO7ð­ÅêÝ÷h‹œÑ1·ðKˆ‹k@‘oAmù%;CíïÙ°/쎠 ²)^à_!¨O‰`]ñ´Ú×w†Çþ´P(U@è üÊøà½ýÚ³ÄN]%âkæ.A)ácÙ,–‘…¡âÚ@ö՜޿V¡¥`ç(šúbÌóﲎ”¤VjöT©ÇÃK|4ׯ1ÖkªHëåFüÉcÓÓ궛Яs±â¾?ùµm>èǼNN)r0‚8Hú«åPcNWQ(Y¸LG`Ùà ™øbqïMÅñ$ý÷AÍ¿7,Æ@Ï©÷¿-~Õ|1dv;°Ùõp3Ÿ¸“?'ðöOö‚¬ŽÞÃó°CQS`S¬ÅÀÿbŒžëq=¼µµßÙO™óPþlCF9ø”Ì\¿‡nNßi5³²â»W%ñQ#ÕŠ +M]<&¡(ã®[gÌ݈LPzZ7ýoØöuŸ’zýŠ‚°ªxIŒà™øç ±°ˆ½é ìpOŽ„èáVé`¾Õ³¡VÊùbÖ ð0iö &†=dÇ&Ƀ³7ÿ Gö9‰Ž =ÊøM oâ›Ú«ü±¶5dv›ìöÕÝ’¢ˆL-´c•-%áSœÛ&dkŒ\¯D\Ó†ÈW÷aÝîÓ|*£p-Bíϛ¯±K Å 4êTŸ{…‹U­M`…í³e.æ)òþë2Õã¦n1y’d¼œV_n/&²7d3ú4|:ç‘®5®Ä͉$Y65âªZzÄ„kÎXxãNìþ쉥IªM³ÀLTd`"d%ûs»!ëƒ**â_Ôùñ£g%óõ.å·1¯mW°Pœ¥\@úøÅ˜RR8¹‡!åï2`…òÙ¦O’¸ Í/ø…¶5·‚ÛÅ™îÖÔVí˜{ úLƒ¹g®Él“ø¥äí>k½a£Á1ÂÑåg‘bej^£ExW|ÎÑŒÕî ³÷ݲÁäqÁq9aÓɶd©¸Š ‡f–$ ûíw°Íé¤îkzss¯¨,>²Ýoÿïÿ’le2](—@ñ™n:!BGÌ(JTZÆÃÓbƒ3¬ý­RÒ4Œ¨8³dBÖ›)k€æºæ2ãðXÛCäq”L¬©fÝÌ}¸Ë1¿¬G0†êëhIÝë– îM²­ÇºÈlªªÀŒ†-áøqÁ#÷W:þi3­ó0õÒµÜr')håË|ºÔ\ØD›×­œµî€©LA­8žg˜ÕS§A¦)ósÛ’âI›G9_6B’'âtÙz4…=,ÏÓ|­¢ Ñ~ü ."¿ÕH¥Y| ‰Õs¯…H-öÙÁd»Hå]«ý³‹ÕEóáÊÓ"¯¨DˆkJ?”|±QAš¹0&ýïToš¾VpJÄgèVÿ¡h! X\W{í':ËkC âK9ç;m«ý¡’öÆ4aQ´¬HƒWo½?ⲯšßs aÔlà:)‰v¬öUÿILx7û…±¹4mtÇV¡«ÎÍGf1Pë¬e‰ÈIAóeW[ì žgïÀ ½+…,KϤ+ J¹ß™ãÞV#èªÖ—¾%¼¢ºž† œ—Ž€Ý¤ˆf?¯‹!P×h8¬ó˜‡<Ù´lÙ<›ê-Wl«Ý‘¿<ø6›ÔÃéßàap‡ë= d€a V±QÁ_‰[lšÆ?ÚÉAñmv ÎÔÑ~„KÆ¢XsÄÿƒñµ¥[Óëöåešú#p a1¹›š’¯1æ€N]ˆRJTedg¢zkÓÀý¶P¼wáÖ‡Òé±w¥mA@ ãŒš´iö©Y}/Ùq…ÈbkìƒñdÁ6ݹO¸n 4,HÙsÕÑÜû›$QÛD¼cÅå _gð]_hiP_К&³%¸@Èþ—áÊõB†e»ì¾Çhš­E|`_¥±D)™gdŽ¡oYË(¼Ñ¢ÀwÓÅÇ&{ïáôc7ý#KÈ+ƒ kWË÷Au®ðÓ­œ0§Ÿ2‡ùÿg?C¥¶ Ÿ!ëì.=y»ÉJÐ[Wb4â8ß“QýHöiz¤SÁ,I¢mϪ \÷i«¹9”Ã2xý"J§o> P†•Ìzü ö”šCd,‘¬Î ,füµ8>ÑõÐ|1mØÎ8¤îä)Õz˜©íp5{¾­ûÐøäÓâÌúÌ :÷µK¥¿¹Û0T£ŸÖ‘=ØÙúD3¶%i¼5cýŽ)óêÙ ØzöäKKÒÝÂT'×Vó*¿yÍÏ37a&À¹'Fè&§‹W–] #}äVèK2NØ]õë eœ#¥BÕ?#Ž2ÀÊ]ýÛBïEþd-üƈyªH¡ÔÅ·dX:™Ö¡ÿÛ}õiçË#ññç“åaˆ-ç4Úw—±-W'kmøYÑé"×òj8•åç‹Ê­TnÆLê–‰+ï‡ñ³%#vnÖÀûœˆu¸ œø'µˆîÚËKºïã`1‰Ö;¥O°2Ϥ.R4ÍÑÚ |ÂjГå sÁØ<”sÝö%ïx^…®ÎÕ–²Áê‘€º¥L Œ£ì9c^ØÌÅAáÇ÷ÃCËþ¢‹ ¥àn>x㬣ӹsubó$5l鱤úŸOj—ÞÒuu»× ÓS©›Ô*ÌkžâfûE” •÷»^DY¤`+Ý.¾2£6@}°n…ì:ù€’1Hf2d¾6ANòünþý­7—•Þ\n¡ƒ©¶Ð¯c,(ÂêÇZP¶ªaC¼ÖoÛÞdËë Pʯ|õ9•Lç>U¶0Ù¶ñ"Η‚¡]°{òÇ’ ôGt‹?áJL^o“ye^±C{ò†ä}Œ‰˜ÊA¥µøÐeÏ]÷„nÜî…â.–„Sœ¢D¾³ÞöGÔy¤Ô „È­¾¤1¤»TkÀþ ûSÊta[à|,‡­g ¡š´ lºÉ»ðb„šbà9‚°ç)ì…H}/",ß…–ËO‡ö±&?© O" eú…RÚ3.÷™¥²šçKS`{ T âÌ­§17*×ónð“lÛ',ŒÈö♓__ÿWÙ.xú޲ÐMCpùíCÀÏîḆE¦õjÄøúݳ@ã ;>P¹WNëUçˆL<'´Ž&>l˜žÆ£H””‡¾OJˆèXYï@”ÀÛm'LÀUª”dx·‰sàdŒuú Ft FÏØ¯¼û³ÌQ­­I;OoP±SýHa²ÇBõÎÄî{TÍßÓû0Tßôo ˵¯,]î}öxd·{Èè.³þf]ÜÄŽÈ+ÐÞZ½ ©3cª-òDM®;æT£¯d¢g¸ÑFß±a|ùÊÈ?ïyaW?ëæ×Ïà–V´Èz»â1xú_*kE™ñLï"ç›å¹ëÐÂð^üH¬).jJ÷nÑL´êoÜðs?L%žõ-r¼É£äÂß'˜?jÿ¬¯WïV¥I/tÊߚ؅[ªë•± LcÉ-H+³#Ý)#fcljÛœÉË ¢™íñÏ&ša®ÄRÛ=Ô W®¼²ŒäwH9u|S¾Ò‡êÚµ¨³$®éîøŒÁŠ˜èÙ†H°zbP‹êúLgÉ)Þ²Ìîï=ì?{‘oÁk0mµ9‘v¦èxqߤ‘„/x>(u…ƒþ$u’.V×n nª­¹e¾ƒ’cmò˜8Ï2ý:üTY]Ë*m§XônÌÚÛÎòÛ‹§ ”¨ÐþŸ~«à&*ô•‰žï˜i@¨AN^ã8+¾ÑEM h±ÇÛòhëÑ‚‚ÅØÇg 1ºœ—%!V˜D+«ž»uõí&Sײ¹õ%NN2¾¹Õ·W’à‘ÎÍŠÁ—2ƒBÛ¯>Ê¢ÿy¹“öµÒ©ê<{¼âhö¦é"dNJŒ…9öÀ½}®‘՛ɩm“K*[mû{ÐÅ‘ìÒ!£®¢K“i80ÀÇúþ®OÀ‡¹x€dð;øÆ;UŒ»M/ÕCk ¼ßÖ<7&gqz[ÞÔo¼2‘2¢áå0R=hv@üÇæ|”eöÁ+å&xOíŽkU:_hº&–[>ì™ÿ`ˆN_êq‹€¹“ãçë׉#UÔ¸%šñt¸ o$3¼ày,§>ô\q5à ,U£¯œ:ÚŒs•_~áÖ!µÄu ¶¦œ·–÷Ñí5ß!‰KPZW³ÚÿöÌ kÇ3×G`E ÓÆ¥8J@`7 Ô ´1Å 3@)€ÈïÂ/@ŽÉxÀâ ê¾2¾pF6auŽÒânªÄËClT_¡4пðM­lTÄ4o+4n@å.×0黚iݳ¼ßìë©!hœÝêÁÝÐÍ|ÝbSç‘‘ óB_zÔ­]‘EöS˜ ‘¸,àÞŠµˆö‘sTMž ޡė¬FpÅu&ªƒíJ`hfÏ+­ —Ù¿´0ÏËã–·`ÂTôÕbñ¹Ó5ÛèCÞuÆŒY"ËGcñšØŠgr´ÙMݰô‚çéÍÈñúÀ[ÆÞ=]χ'Cã,EtŠž …~Ъ99•/'ðD­6w³jœ‘ÍBþÝêª3]†fó×HúNÓ\ÚÎ&¡-‰ÌÌ Ü m^ˆ+%À‰ãTšé)ƒ»¢hI”„‘àô8?w–•É©iDÁE“‚åB§ížC*…h÷󸵵𛞢Æô>:€%ž&‰ù¼_Z¦#T¹·yL@!æU錷¸“à Ÿú¦@­MÞ e¤ÅÒ„)°×Ì&ŽÙ˜ysS‚(N«Ð¾Qðµ_Ÿ>Î&ƒŽÖíNVÖd0 4󔇺Ð*¯’Ž"ªöóÔ-O­P¥bõs„MÒ!Ȱ왋DÞ®<õ’É=ø7ÈP!wîç¹{m”û&“G^žä—.•¿¤´uõÍ™²»ÝjøðÙ賦™¥Â,ˆ§ nQÌï]1žá& ðg mâápn_1hóò%j×K_ZŠùv+‰Œü (E5…+ŒÙÅ”ly†•KñÓУÊgÜ®Z%ûšvœ9afŸü´ôoΜ+…hË7ÿ|Øgn‚ÖrHøØ‡4:vXÒå)™Ì5s·ŠÍ—O$÷5UÓJÅZ­é©]ž˜¯ÕPmws->ý(¤Ÿ)˜‚8 f·ïÃV\XÜlßs­'%8šzãƒ-<2¹Ñ§›MãšÔ–Áåâ«¿n†Ûl­ëÞÝbE¸:†ˆ–ç1~qôÏëR\lÀÚ×ii±ïž-¦§ˆ.ä™{‘Ätßá¤çïÆ³ËMk[þäTÿÅAì.+†žÎÍцzãI®¯æ5Þž¨-¾_É=HÖq¢åÖÐÆ/ @ å=zkfÈ0óXuLTð"öÿAÛ•†XÉO|ô f±gÛ"‰O“Õ^ÇþUÁ?Ú\jv£»£ÔæíÈ–×¹ §øI8)©ï[%=›]tŸÐÁÇŽð)ÓH}÷Œ)ýÅI$½Œã¬¸'?b‘'ÚÚXEü M¥'+±9ƒL”ˆ²aªñ›ëu–GX~4º¶Ùl¨›èMŽð„õÔÁýæKæÎàÛ;»YöZ‡G³ªj¸£pѹ2âf8Pl¬7˜/ufÐKÉÌ ˜Ñ`]̬v§U#OŠZY=´‰tâŒIòVŽ'¨HK.hf…zkß.ükí=èûþã”D–®ý ñ¿É¼Ç–‰lnû¦… |n ö+ÑDÕš¢:&]Ûã•°dC¼g h1­3ëvZàœœÿ{»·œë‘+Ù·B@DÀÚè6Ÿ.NSPuÈnCøà QHbý5•8L5èðDêÿÎT®.¦¤Yƒj’NKÉ×,N$¿èCÌ!,i}‘úcÖ ,ý)޹PyC‡)ç^“+Qmù Ûœý{eQm#ö×ýï$?9w¶To7duÌÐÕøTØ=e¸ã mÎeBi³Lº–•v„øm“ä9Ni+ŽÚ±<õs”•`Ñ¡ yV6'`£ã!?›·ð›«<—Û«ÄÑh|_ ¼Ê‹¦_]D×鋈ÅaLUC·þL–ŸÏéÍ],ÓL1L¶Þ±“ÕwƒµÒ ø’® f\sfNäˆ8æN3¦Ë7k1a«DlðÕL§þONxâúÀÝ^VÔ%×L0~¼Ý»&[ÌåúÕ¢ÔÊ—õÊ,éåYÎÿ´÷ÍU[ä8赫r5”ú3GI6ËOÝ[½ípy¶½Ï|õKºòÏ*;™ŸËYVåyÍq«&10‚³íiž€Hð»¾á'øÇò ¢±±ZAX) i(‰‚n4öDZ’„¾bþLô9ȹc–椋æ¿åX}w×”n‘c憡æ©7`5˜Eñd’ô;ÄXŸÕv£%°™*™¾×˜YkŸêÅÆÐO#5'½9ÅaëyµET[¯VÞuF²ÆÅ›õ·P¼‡ €×ujÚnИ/O'¡Úú+·?c”@'ŸZ^i½ótˆUÆãh–³y×þ’"µ C¥‚$«úÇ›ò –† î0o›ÏåbÇ­{}È!uröçëC{/UËJ¶Ç›bÉð·yøðjÞ6Uèì¡ö|¯'†(Sû·ÐÜù*®:µ¦åÞd:§ 4‹x‚'öž;Ø ü©uMOŠ÷åqžbü6 0ù¼ ‘F Šm·/{ˉD]ÏÄ,<€K›¥·˜’Å3¢Ÿi´e¿dhG‚äÖØó7n²ôŰÓfÏ ÿ$¾ Ã]Wœc‰Ç”µdj¢mžgV• ˜àßu8Mç‹èrl-§Q·’kC“ D¤ö䕯úB¡º__®æë`óç”;ߎ­ŒË«™ª³#މO–.#6†Ë»“n†iê¢!=.7Z7EK‹Žõ°@[Ÿ ¹@äRŸeê oO?7¯½oAŽ0f£Ð=ÔØw" 3s?”Uн.Ú‘©âCº2~ÍËV97ÿ0VZœ2ýˆOÝ}&N”’TGƒ®ÌmŸ/ÐW1¹ã %#ýX({dÐ{].Ö2ž¶^ÐìVoA×ô” ñî˜!á›À®£û‡Š`Š÷yäÈÄŸ'F”‡$wÞyØÁ9µÑÐ2+ÆøÎ’5p`(øêóRn€¢°!Ç茷øJ <ø· ¢¦õuö³hÃßC-!z †÷¹|OùÜ¿˜y] þŠ‹¢:æ!ÊwÈZ´°Q…~ŽAìeÔsô‡ÔÁeñÊÓé6M-:Už©ç¨Ýš…ù¥{ð)B— \óaÑØô]a…Uà-Có%zˆ±ÌÔ¬¼óŒÄ¡s©­†OܳJ ÔPð!d¬„¦ÈT¡;Ÿóçl6JÝX†iÀY~”éÈð¸âñå àðõ (üˆ:U ]¤aøå;K¤’§ÈT}e—ˆÓFîâø=ð¦¢J¯ªL&[ŽXJL¥°°^žA]n(AäMɇ=fûº)g[€Î…ÀÕTæŽ}†scäáoòÒƒtÏ­V(oUïðV^ˆÜfKþœÛúVÃñ¿<œYdÂcÌB@ƒÂº›kBÕw'Ó‹žâSó”yž°¶›3¶M3^Gø@¬Ž ñ.åRxh§ÂïÞ\E?g’ƒ6ŠŽ}e4<øÞî–ÊöŸºêW¹Ž¯ Ö|XÎ]¦™¤>†Ä`ÒOj0ÔZ&ÿ³ ©¨/¢?ÎL¶>;©”Ïi‰3[¯l½ã¾½Y²ëjVE¢å~ˆ‹¢_Ž’D½SÊj_’À­ÇÖ´éÝÏ—~ƒI!m~Óψ)ZÑqxf $½RKŒ2M9HÈmuês`Qºµ•Sw,¡É²<Ô4LßûΪx©d,é/™ØGfë’YæI17C)‚a „Ñ&lÁk ‰™/ÞŠòøpLBÖŽ?©Å) é‘j7ÿî3p˜oCÉ»)Ûõ« –÷a²¹g™ä˜¾†mÔÝ.ó¢”þÇÚUÎYÜ®]§„­ÿUýÞ~#ÑýÄsSF~t2À2E‰½ÊžT!(F wêTZ ì-,ÅTÐ w¢œÂ~‹v¾KtvJ·=‚Ÿ9ðSà2u;y=lWåèäy*L}CO„ú)Óš" h‘6kö@pÓ@!+_ÿ¡œ?¸ÎÂihÌ+3J$Èÿ$À9³a˜›LÈêJ:zt›ñšã0=7?UlMöºm®P­î(§ÅúJ¹9"øu‰ÄéT¼'Cjç£Øø€ PøL ãG:×ÈaBãD} £ð·ú‰Ž*5 ¨:t.ªmâ3ÊvÙŽ]ê*Ešm±êØÒ “˜F‹Pý£]%ë|®üÎ):û‹âÚ?ëÅöú޳”ħiþC Owöá-«!¡‘#àÒf5CYžkÛ3”Õ‹Ð`ˆyOv¡>œêWÏüÎÄB™×Q*{|RC “Z'Ió隥í+«ö±ÏÙƒ6b2Öçèiïñ+}ƒû*? hFÐ^¿ùÀ‰F·CÿÛRcÝ’d3Ü¬Ãøš©«ŒÐuÑ\ŽáÇÉ7í‚7…qPý÷%ˇņïÿë±aK8Ò“–Xm¤Å ·m È~ Ô Zß_5ÃáÚv¸äèå×l†\ 6È5Æ”Ž–B`ÿxb3{´¢u3 éÆEÅ_Hr—„/¢ž‹nRjнÈG! žL®«CQi3¿ â@û¹rƒÝX¼Qâ_ÞÞ`Fõ7ÕRìñzÒ‹ô„ `ü:îÅîû,üö'£{b–3GÅUa© "Pk:zº½Üà¢v%vÿŠ(*X"®iXÓK%C;‚¨l±²ÇÆ·(Ö:Ü‚ç,«‡‡qÁÓˆGWºök€0¦Ã;‘ÁW™sž´OÁC*dzڔÿÑ!àòÖ”ÍÛÑ€g);èʼÎ6ÿÏšw=ci¨ ”4•#"9¤Ц+ðêÔ4†°1P‘ó"þ,o ^òɱ.×w¾›ƒ:‘ªHlQIÆþ±dGšn<ù[Ž ÝÌ7ô¦Íªky”ÿ‹Ü’0êá‡?¸<µY&¢5ÿ[.eÌ£¬Ä*ÍÒÚ~]žÞC“®âÊ~p¢&Nrs±ŸÁ5eoîõ„{²¹mŠÄNÉí\<làŒÁä™lâ¼£w?ržÉûwÆS®¶dýŸmÀú!! ÈK;–§ôÏY,B<eÁ ¡oÊkK3;_¸Õ+ioχ¢1OrTÍWuÄLÃé9a@ýX¶%÷HèêÜ31ùœÜ+Vÿ‘ õ@ÊPûÉD W;°wP0Ñò¬ÕÁ®&Ô53 á Ë%O¯¼ t¨-–Õ275EéL4C£Î~£ü;àsþ.ëà,5‚©úîb7@Æfñ ¡•µ ¼á$‹òÞYÌ)kØÖšãRŠþ»ö¬pGjöiн¨¨|Z(n}±¤ãÄ‚.Tb|Qƒz«e—ìÌo(G|fsåÏ6=¢€Sû·å¸ƒä÷XJÎG0Š8¦ª%:®åiè‡w¸Ló:ºçÄ‘’³R“ÀäÁÈ]}ã~øA˜ö›¤Äi¯HåÝ^Sï®GÀ>˜U¡ÂT;î^GQü° #kqü ÷Ë!!b'‘hB3˜äƒui%§[¯£Ù2Ðö·»˜Q½Ó=waaöëÃbò?Œ7M„îé^ý W®,BÝsýxé¾i}+ »¨7Ð#¸»”ö¿‹‰îÈ‘K‚">÷MéIÕãàRY§ÿ#îtuÎ&–Ç'¶fX—¤AÑàÛ“½j-‡ìÝLâúå+ÿ{bû+†;ÕÛ:›9:¬~Ö•™±Ö6ÔG袺ñvœÁØ~Ó¼TÓB/;É®šVš‘¼I™‚²ÎÒÖ„çÌycäÈtûo#n _!LED±%ºUC0MO÷³ú/¼0Äè0üÙ«VäóǸarÆÿ.{ž¡Œc¹‚ºŒoã‘ W3õ˜(Ù8&]ˆØ«stä…é‚q,å܈c5 }ôgÒ»(ÕúÏò¨§`ñ|ûbLH»÷q;ÄŠY ðDnå(û‚|œgõFñâþ!áÙ€ƒ‰z®÷ºë®Î©õÇXð† K;mÄD?˜-ÔWc4—ÚËÕ²£@¯%`—ÍDû6݌ߣ¬††DK€ßv†F›ö·½¬vª^§6à³Â¾v&éÁxúï¢SÒ]•/œ-#l#N\WNñ› *Š´¥@ŒY­g|HÖÈ´× ÔeñO¦¯%»,Â… Ÿ^»ÏÎÿ´£>K81?€p N-WâÁ“ò í{l¸e#””2V’kå ‡Èà(1 ¡gx«j ÷{s (˜\C1aÑä½eŽžm…~±ƒ=Ò—;>}DO"ì‰2€€T[·¯ÝJâœO Þ‘ " ˜>yé á.=h–0ˆÒ¹ÊÖÜ*"Ø5Ø€Arº_¤Ó­øü€á¸€D=¬‹¬ÕeϪ-ÁÊNA€De;q4ð~Ò“ÇÊ!6ÇQòcüyfýŽeÉOà{;OM‰Æw-=rýØ‹ž+©µÊÛJ嵄ÃK¾²ÐE3åå³}×ÀèRÏä•0óР9K6’k“”º[y7E2NQN/ièy”7û”£tÙ‹‰­“añ€ <).·^3“§¹Xûñü2zŠXâ¨Í0 iƒÎ¥ÎñhÊïuÒ䥠kWlÃâÀøNÃb~§ñíà*µ¤ÕùL˜p®šæRIm¦‹Gøós-ßè ã|ÿÛ˜>@j™tùÖf]õ±&TÌìáÛ¥NЊ½ûì-‚#àu`s iº(œïÅ6‰zBh°Œ¶âT ©«Bñ¿îØ€ÐÞõ‰Æ€÷‡ ¥…š»P^ÔYY|Ò»Nü¦…a›«ó*D¾lœêYß)N9ŒkX7˜Ÿ¬µ´4!‡«f Š ÿÇV¿¢¿@`8$…µã#»–ý"Å¥¢GŒFñÕ«ø­v³ê´œjcJ(ëbã ¾)#b+½ÃÞÝ;¾B-]„í¢Ö冷ϖDÆ…#LšàŽ.åÇç¾z†£ÄÍkÒß®ŽR­CR_Y†´©¥ÉßȆ޵Óá¿ó*š•4Ç¿þ­é’›VXÿ¾ëöoz=gZõZ«n¾‰$#Bûº'„Ç€¼Û²úÛè\Ù/ô'éfïüŠÑfÛï?7#˜Ì£BNùîªB¢šõ5½PB‰"•záƒM´° å÷¸C‹éÆ\¹û ‰?yó›ûçÆw öLiˆl¹Í§Œ[C€LŸÌçÀÈ®¶Àדè7=~¸øéXÿ´$âGá|³©fwýì”\èã÷“Y3cñÉÊVõÇ GPö…°ÜA'×R³= =1î’£}«ÛDLé8½Þx?bð2ÓÁŽ„Þú^$G (ÍZÒ¤rHºª*mÛ#ßêŒ)4Õ÷F÷,+÷ÒÅÑÉÐó¨_*ßøÖVD¸¨?ANCÒù¬³­u¡V¦‡BŒE‹S¢$ n¡$ý¥X½¥AÔ«´;1§¢½’J@•ÊÏia΂æ|ð–£:ŸúÙb_òÝÐR”öýõ›Ïï„…¯é'Và 1ï*Á°é˜sŽ9˜…[Ê¥ ’eZ( á[vr7?™LTÔìÑùF¨×¶v!‡­7ÔŒc^432˜‚Èœu³Ù?5yD³ ï-fþþë`Ò¤˜rT¦ƒÁ?ÍYìð1¤\n^¸ïQ¢FXŸgµüï )pÕ„ERö úBοÌÀ»°( "¢å8üŒCGÓšú¹à:픑£™4-Z­âr‘s9x¿G ¹|E?õLªs÷›B3,HH.ô“Êaпµ>üœÕrÖoXïlÄÊ>b΢‹nÚ¤¼~•8Xõèå°²+tM…”%|‡MP³Ä†< {»‘e&§Ëš‚çQ(P8O+Güƒ¤(»+Iåγ„lKÜÛàwKy ¶".ú|·. ø³&ø¶É_zjÏ„2©‰©Ð´“uЬ”„;‚É ”fˆwœ1 °¥Ê¢ŠnéY7ÅÄ‚I;Ó¿#Ô‡q>ÄRjhÐ<ˆ¡-&÷ÑVøQÁ£Ö$.©vc=¸|§æÿXé¹íõæ˜4tåâ‡éò>iG܇©÷ ë¡ßª¡Ö˜ùb¦2ÑO5>Íæ>ÂZNE圷bMl<ì±ýn7éQ41à¡Ö¶3Ï*б'ŠÍ‡Åu¨¥-ÔõŠ»¡l_ëáœQ|d[¿å¶ÁÃÔ!W}7vÀ·Z6¹|$Æ$d•j†QØSV´j8´Kx ÅO2¾Uâ„Ç,|UÒ f%0C×n± 8¬ “Ñ9^.eߦeê×U Ôm“Ð<_ ½ï òt-s°5×Û{Xâ˜XlÛoSMYÑ)ƒ–y%?eÓdV‘wJÈèþ®f€ž4Æ)fo Ì™qWÅÈ…ãa“ÍÚBñ}ìâœBÃÑžÐcÄHÍND ŠPÝI3޽VÝi?È>ª˜­U¦““‰çò¬¨Ú6%'ðµ7åÎîfòž}×þ#¯|à±&¿4ÁLþbøì œÍJÈhxÉÔ¡ÆÍ]‘³®µ3c˜Î‰>aèÔÙ£C“ X'!ƒwEÒ"ݵżo˜¿ÕÞnÚcº‹tO„ÅK,õK>±—lcí]é?ÅX­+†)L’Ü QÄ0ÃP£\"¹¢!LšËe+ãÉ‘{Ü [ŽuE®qTkÈÉ1‰©Ñ±ˆÑö?ôe?ìûþÞ‡÷íý~ï÷^sË!ÌFk• N8&¼~þVû9ÿĸnÀ˜7ÏPÕ:|‰Åtœ×ÌÓÑô¼Œ¿;Q 4ØòÝ4=aO¨±n·è¬¤„CÐñÞ–e£®?Û1;½2 oÕMðf3ceqEfŸ A¡BF‘iÒûö!ÂYê£\¾XëlúѨ]÷©~H|]æx¹ [Û´†±àšâ6c ½Óçc[ObÑa®Ÿ1\=ÓMHï:Ì ×ƒ­Úñ¼é‹XxÒŒ²¿nQ©¥íñsEž£^Âé–øùÆ&ä3ê*4Í+ƒÕ‹-˜Àw­¡&˜œXsåÅñA~%ÿá)ùU³»(#ºš,LîG2ÄÊÞâ³@¾%oà›[3v AòòŒFnéWåV*ª” A‘gXí‰ÏŽú&ÅüÚ9]Þ¢ÿȈ­ŠŸ'«¼ÿóCQ5ˆ‘–xhdÄz80t²Á “ì)©f8>ýšøx+k|jrñh"øÍô4bA’ ÒH/l5nûSX']‡ ÿ€ð|‘ù캪÷¢N!YŸöölÄ7Æã**ðÃõ(hˆëû‰Å…C¾`Ëcùm5$ñ€Ú’/Xæ³,@pkî19Âiy®~Lë$¾qPËŸž3ê~B}°UtA„Qz Ch¢¦÷>‡Îs˜êq‹€î8“´ÉDq㥠8m.iëïÒ–3.ä÷¸^y ”³<$>]‘õ¦¿§ÙDIºÐ ºœ+*·’La-Õ—wœXiȆ;‡{Õ£_»¯tåx™rϹ¼Ò×+’±Y©Îˆð¶/Ò”1¾˜sÀÂÝvY³v‘Åå¼ ‘œûôRß©(ÑOAƒg$nTOj5̦«wòiá^,ÙÊA.…ú5à îG«ö¯;“¥KN?/öD#_ÚÔÿý[y2B ËsÏ­Ë"6NWPm•‚|J^ÂÀ£úÎ’RÄ$…gSûŠ+±4=2Û •ç[)ÈI·¶š—ô´¿v˜ß ²/õ*n‹E¸âŽBÞ&L«$‡v5Z&â>„µ”^—°ŠóSŸ©™v£ÄßÇÆu˜1êÿ -›4”’¶&‹Š3¿×NèÖ¹†UæcE¤¦@W/<.zdQ¸–iÚ2\ó¡1î2úùmÏ’ÖÙt 5šõ«úæôeÜÕ©”ÙU¡,°(Õ†­ÝzoÙfëYÖ‡ÞòI­#⯄ïiÍDºƒImoSV¦Ó’j@MÈSdbbV¥}Á fèæÝPIs\'Î/”>ÊV0Ë÷”Î|©q`(¶µÏÅjÂõ»Œ£F'DŽWì6èƒ`›13”Ä-ˆ#MBÈ…9è¯jv¹Ž_Þ}WÀ ùŠ^‘O¿ÎÖˆ?½h¾ön•À¼u+Ö¡ï】É⬕ƒ¦rýéRU­K©v÷T~õÕ.bÑóÍØ~gÚ_Ìï鱪í;6È^ˆ—à pŠwÇüsç´t°ífÚ7“º6†ªÒêéu†kd ›]6ëlTMž ×ö+®‡R5Ï›So @D_jì{Õ¬M‚gz2úü‰Á.»uÛ#Á…}WúN¤XVM8•¼Þ=¾‘Dý#KÉqßâ}©sçøjkÉ_»û¼(S°¬ï '¥\yó«¦Gß´ë¯ Þ9yÛãó $šœžÈ—°, cõO ¡w8ኔܜL›Å”B¼˜ˆB•£ݯ:õžÃ3[՚ƙ6]Æþ–_;æÄì••½èb pàë-Ÿ ½^áæ'Vœ …ŸÚó_bëÉÖHÀÓ|ŸA”]J«ŸŸÅ<º`¸ÃS.ÎôÇxŽ”Gß À¾ˆV”èRþ–s8Žygßp¦V.ùÆ ¿Ñ_äCÿ€þ<ð ¹î‹'†b}ñÁ¢ÿ€Y]Àendstream endobj 28 0 obj<>endobj 29 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 278 333 474 556 556 889 722 238 333 333 389 584 278 333 278 278 556 556 556 556 556 556 556 556 556 556 333 333 584 584 584 611 975 722 722 722 722 667 611 778 722 278 556 722 611 833 722 778 667 778 722 667 611 722 667 944 667 667 611 333 278 333 584 556 333 556 611 556 611 556 333 611 611 278 278 556 278 889 611 611 611 611 389 556 333 611 556 778 556 556 500 389 280 389 584 600 500 500 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 333 556 556 556 556 280 556 333 737 370 556 584 584 737 333 606 584 351 351 333 611 556 278 333 351 365 556 869 869 869 611 722 722 722 722 722 722 1000 722 667 667 667 667 278 278 278 278 722 722 778 778 778 778 778 584 778 722 722 722 722 667 667 611 556 556 556 556 556 556 889 556 556 556 556 556 278 278 278 278 611 611 611 611 611 611 611 584 611 611 611 611 611 556 611 556]endobj 30 0 obj<>stream x¬»w4œ]Ô6®G¨Q‚‰Þ]”½F/!ú`”Æh„è5‰%D]½ˆèD Q¢·ï–§Dž÷[¿õ­õ{ÿ±Ü{Ÿ{ŸëÚgŸkŸs/˜îjhsI[Á- òp’ ÌÍ{¤íéhwñòO¼‚¸LL2ˆ9 ‡Éš#!÷@:¶® ‡–HÅeÉÀ<P[$HWK¤ ·Fº›# œ×ìÀ@a…ç•ÿÿ6J!m! k¨$óPÃPI]Ī ® R€À s†«…Ô¤ µ„À\ l k8®@–p˜ô ´ 70£´ Èäⱄ¯A<,!NW.Náuq~A]@6sbBÂAP˜¥ƒ«ÕÀn ¤â„€#Lî‚t±D@ `V Yù¿p"mÍ‘Ws»@7n Œ´‚[º:B`H –ùUÀ‹4‡Â\@Hˆ`„ƒ, +¨‹“ƒ¹'07Ì ýÃÕ ³ù€„€Ø˜#¬ ..@ öUv~ó¼zõöæNNž¿Þ†ÿõ/(Òâ`Í ææÑb…áò\-¾Ìóþe·ruúÇçAüJëßÁà0·‚Ã>Ñÿøtl¡–ö0`ƒ€„~¹ 0«ÿ”ëU©ÿÂÌó·ÎýŠ®ìH¤Ž§ä¯újp`ûÿýpõ΃pX„˜X”Ø)¢¼À0¯÷ÿe‚_‘ÀEºzY͉€z€Œ–¼à_\¯øþñôø?aä`–p««-Ì'(2G Ì=A¸À„À£ È ˆÄãJÊlA<Ü08`rrEzÿAÜ«íËÏâqq2¶&`ÿËÂâÞp4ì_“ˆÇ½Úã€2þkñÀ\-®v¾ ì·Yèê}@Ù5»Ò‡ ƒx%„ùÛ&â1w¬.æÀü;RÀäjiûK&ÿ1 ð¯ æ±þ@ü·õ¯Îòï`€•9€u±w4GÚþZ çäàêòÛP³„;:šÿ¶´¡°ëcFF(ü7D€Œ‹ƒ¹ËµÈ•'üw€ö;¯‚¤ûo¿ i‹ú×?¨ÔÖpW@Ûÿ^ A­5öß­ P"ÿ°º@Ü ¿³”Ï_ûéß1T°#~¿ µ„;Ýìß!W ‡8Bÿ´ €¯ÚȿÄÄg×kk* ¶¹êûß …Ðή—«ÞùûÕ_i†Ù \¯/¿_ÚÁÉöwò…ø Èk½Œ-ôw ¼,Äáú¼œ“ ô:!aºÆµ·„ä æ×WY@.w-Š0€Z ~Ý Z'ÒüïÜÂ\ iþF' ÀU5w´°ºf«¹þ~À«~íûH4âZn­àÑøÍPÀªs5ñ¿1D¬Z¶¿+GÀª µ¹V³"XóßÓˆHuÿ“+°Q¯°>tÎ ¿§à\ÃÀÕpùý ˆÏ£ëÐD°sK{ò}) @²‡€§­ß•â?£ÿܯ¢~`‡9B µ¼jÿ˜¸^‰º‹å‘2s`°¹ pÿÔ1p|ä1ÿ£žD>Y^« 0/@Éê‚ä¨óÿdÌ 0rúãE€ŒÍ5æh\Ÿ Ì  ‡^¯*0/ó»¨À¼hû?ª Ì  ô÷zY¥ß+ Î`€úþK @ÃÿSY`àÇãô{ùÀ`󯢾ö€q­¸À`ó¯:¹6¼V^`0€Ùõ¿¨áWÕt@ýËö;Üã&à†À„º6ýä4òý]7?j Ì@·¸V+`>úU5Z}uÏø§,À|¿J8‡\|¥fï‘k€¯DOÎõšŒƒ¯4îª ×1øJ㮤ñO5 €¬ùÿ>ð•ÊAaÖPéùÓ•ÒY;ÀSü5VàFaqÍäÔ jîÜS®¤ÚBÌ×^ ÐÆÃÈ¿¤¯48ÀÝ-à× øJø~™ÿÌå•þý2g‚ß!>¿ŒÿÉç•þ²[ÁݯÁ¿RC+Ю£(]5Ü?;*Xà幺–\›  õw3ùO^¯ÒÑÕ î+¿Ç_ %pÛr‚#®ò}­+¯8+\?¬ Ö¿Ž<¿Ž6à+å´¬k©»ÒN+¨ôzî®ô8.ýÀ0AÝÑ];1¯tØÁ¸ÇßhB @}ÿn£`€è¯ô*àz÷›Ñ•¼þrØuñäÀÕpAÍmQEº"®¥ýJkDN¿Ï"à+¡U*Ñy-<ÀTëZ®î( °H„ùµnèZB–ÿ™û+¥ýË÷Çi |¥·G'¤§Ëõ‹„¯n«`ÉÿØ|Wê œ,¯uC¾+õ½ZV`´ëÕkø®Dè)€æÿ_|g`Í\\-þ| ýO¸ÿxâÿûçj¡Ú×Ï*|W:}UÿÃP6ÿu;ùgßð]i5ðÀ úG€+Å.çW'c€ú».ù®ÄÛòŸï#ÿõœ•±‚8š#ìÿë8ä¬\Kú¿³ƒÆ÷ÆkF€¬ùÇa˜ ðt€Û\uQ€ÙµÁÕ¿í×Ïå|W ÿ·ýê Äß§T¾+‘ÿK,þ¾+­ÿÇó‡Þð]©þ?žk’ÃÇwUÌWªeeáð§êð]u\ßU#p€?Àl~+ßU3øµ.ÿ™àüï*˜Xþ¥ÁwUÓÿ.Ÿ.€øï5øÓwqŽ–6?Àüßë ò·¦òñ_•÷?÷˜k[œïê&ö¯ÃâÚj𼯱þ¿æú#@ÿšë€@ ~µÉ«ÌüÈÀ¿Gèomæãð¯çXý_Žës_ÝÊ~¥þ?+(P¼Ò`oü¾ò]ÝÉþ±^G#ûÇþGt€Ù?öëP€Ëü_ ü5ë‘^¿RûËóG,€×oÏÑþ&œlÿG¼««Üé|×#^]ë®û®Ç¼ºàýJÖÿŒø÷ÿurù#õ‚¯ñ_®¿ÃýùqMn\J² A^>0?ÿ_ß;,]Àmùëp®ÆýûùׇRÄb‰»‰3a–Õ¹CyüšDßjd$¸i—)×é¨ÓSgò’ãp|:`ÇZ%š‚¬@̤G93´íb¬ítˆVLå…l²·Q^rp̘E-2«O[Ks=¼Û¡ÜNßpöTí¸uoÙC 7"ž|²9<ššÿáßÂ},î˜L >ÍT [?˜lÅÒ=E“—-FËntA`ø¸Û‡'ô“ßç‰#­Ë”¯O¨© # $4[(FÒ˜Ža$˱n·‹}Ÿ8Â÷Ö+™x¸É•ºLrYi¾ ~Fß“"¨ÅAŽS‰ŠPS³B!Oީ߰S›Ã=ó¸sÌK\NäMŸª+3Õ’qÙéÊà6Ó¯›CÿÀ`õh‡d÷m¯Ddá*CÄ.¥·ór"3¬%ÄTpûøT'eA\—…1Â:‹ö®Õé“R{˜T÷K‘ù~ÁVªÅF¡!Þ#ÙÝ óÂòj¥ÖUho?JJW?Û>}}Ï*z@™4»•ÖK)ŽŒøvïÑó:,”žÆ~ÍŠê +bä½!eéw·°Ÿ ‹‘Š›îâÜÁª‡îF¶ˆ}Ù%NE¥bGÞ@ï¦WÜÔú]ÑR5‰¬ë2Í‘õ.<ÐJ¼q{gþr7s»*(‚OjÇÛ$?ói›kd+Å v·!»æ^ït›Å«›Î÷>’Óx¦ƒõv§´xž2áÕ^l§žöéf‰·‘ XำØomŸöêÏý>Ù y6¶ê'ôŸ„1߃$ñfä.RäÂnØqYy‹¬²\–è4<н»UAžíºÞ¨¾-•,Þ¬á>G‡ä”ß”éAĪò-…yX0ZŠZ?ëiµ”rÐRNs‹Ë e–9 :nl~³qc3òbÝæs^mÉd£(ú=½@ˆpÜõCi¶“çìê¡€øøÊ…×ûÓO‹,Jݤ‚½A‡ºÆ~3—iïÑYbL£÷ƒ8øû~‚•ÓÝÖg»C¡ˆÒêêX*»pѨ½úàQaÂÙÕ8tʽm‡ò‚É…•‚mÕ\›ÍêA$œž-ÚØZ,*fñÃÕ¸‹QEn«[ì¿{Ü%ðX×Áf›Ð‚‹oÚú‚ân ôK°ì;ÊÂV̳<*‚å8ËM1.ub¢É/‘ 0O|¸^˧pÊyW&Xø‘Q”Ö”÷»¶‡ËÜú%Š3Éîä$Éö~åf±ßªØšÔÀã\£ô-™õ¤}pŸk®Ñ,O[ËÀ›`1-äÏà<î:þRöŸˆÏª*ÔRXËíôapý—ŠL© ô÷Uë§Qì JÛÏ××—iÕ¼° »J£¢_O¦Ò£òåê6ݲF9c™û0íÒåzC~—æéŽœïÞôEˆC¿RžÔÎ8fcqµ¿š.™ÇÆýQÓóZëFÂ|–z¦oà,äÊø”‰jç{Â{î.TYoïQøh}Ý]{Š&´&mKµ¼ £« Çꙺý2oM(¥|œÝûÉø|ÁZo:LÈZü÷m\ŸAä=6”/,@±÷uÔ#Þ˜?wg,é@~Cñ÷8ëHçœxm—U°i|ûxP°6#:y´âz¸<ñ¡\Þ¶Š p“Lð!ã±ë,Ð+ŽÅÚÑtwá.òbGN bA”IP4 ·ß|iœ›rJþt¹õùnøûŸ”Û {3»«ñ5˜ÈVQ”._£½"näß&/€î5bºî[µJVµñþÔ-³¦u…¶&ù5”úŸh 0Ä"ŽÇSyŒ–âØ˜tß="hØ#È¥žÎ¦è7pmäÄ@YÚívÒ{¶:GÚÒL‘ëp˜rë‚íæØJ¦1£\:t¹½`à»f¨O»PÛ Š70GÓuÌxB‰ 'Žo6FN™¤H)¡Ê¹†1›žÍ}Ls_±\#ª³f0œ}=íæbóe¾*hk¦g~»»·]¥­_~™Ô™É~Dª\¤5²w|„å‘A±bƒýN|#6·2õ—©E¥S~ªµuN3Ð+¤DYnSÝðÏ]¹Ëé³¢º˜,,¢QágŠÝ?¯~axצ¤Œ¦®ö{*6œæ2}¯Cl†«›—.åчcè̤¯í»ì£ôÎŒ¡£?Ûò ýcî.óu˜©b¦êi¥ð¿Õ ãÊuÞ£z3ù¥ÊPËY‹‰ìÝ%â÷Îl¸¥ƒË¥em ²7D×Í;o)9~ÎÞžqsM¬»¯O¯¬¯ô)“:zÁýÌ@1º:Ù´“õU«Fç.SÝ=jC•Yþ2ÅÇ÷Q¶ŒÍX”/›tJQi"äMõêR1.j—¶ÜR¹ñÕ¤«_:5ÝÐÇTm¶©®t d‘>‹¥ÄñÝ£Û©ùDB¥1vd§§¡í´X~g –Wª±xOD͸(^&«¨1&;OÊÐé1hëìé“{Zïp–M:Û†LW¬Â ‘ ^‘àF·HÝ„Ü!á½Æ]~Öð§]о§…6ÐŒ ×©—Z³x½¶oÙ+œ´]}ƒ6Þ.BøÐOn€Mßþ˜IBÁzO|ç€XOMr"ð»[{Z¶k³ÙÚ|icGk¬ÒɉÞ{ç/@ ¬UÄiÇ Íšíñ‰;«ó’ëÑ•H‡Oa‹·¡T¬Ã–î¤Ë?Q7‹·«ÙƒòŸë¹¤‰(±Ù¤í ó{dËÈâkIˆíÞOþúub·À']ØývŸjifo¬ÔⲎ§!»VäˆYõ‘—¯.×7QƒÂCÏW1G½4<`;þ¨béFnD‚"SÁ”óIQ{ÁSéyùËaªÎúRJ_3õ×öF?É£ºmz’meĘð¹'Hl–eª£Nà2èEì€ï•¶þ¶n˜ÝÀ‹Ãs‡¤ºQÊÓÒ÷E,ÂñÆE#YS†1j(g¶ŽæÙ§M?Že¦åêr£¢dD‡xÆþ³Ë¸xBmmõ¶tñþìB~ äê^¨qr1$A|øÒBY#1%vBu¤Ï;›š¼a‰Óß½¥Ï¥¹ûÀ6ŽÖºH—aTpêÛ3çA½Þèù™Û)Z²}š4RuïÀ4¯ìî‚rÚ`µ¢ltf¢ª1´¼™¤EèËaèk<^~5¬hQÌBÃ^Xr‹ˆmj©Õm¶ªæpÜ)‚ŽNËã‡õû¨á­5Xy¸G„AJŸQ5vi›ƒpTùz¢ï¸/­¡´â…Ý~t{o”·%Ý¢—bÕŽþLÄmešùñæ£ì_·ßS(Ñb‰Ù…ôÿœë½“©ß±xÚoßA1$šÌ+9Ïn®Û½‡±ÿBÃ¶Ž¸^¾õ[ºJ7å#º•)©×8û9¥·Ãܳ=y -¡ûè2ž¹ÖÍÝBIBGñÔ«]& ÃKÞ›{θì³~v½§_h䋃Ø :ù¦›ŸÀÞ Õ÷äw^òeyxMÅÊé1iÑs6ý^ž kê!²ikë´àÄ|ÍAl›Q|DmxOg+¶§Þ²P\zÇLr4á§|DÓ'Ä–@øÖCÝAm©’7¤¶]T«âSYÑÞ$]P>ÌVbaÜwe÷R÷FŒ8¯¹V…»ÐÜ¢+áÖKIȌ֙å»KŒŽ¹üÆeI²cy…öu©ïmìöS_ßiñÝŒÜ%¶:c?]AbW{X/÷ÛΘ†ÜF £n »Ø'虺UƒÊähBïÐRÉ`y‹|É ý’/á¼ñÆ·zÿ5Ç„øjÙ'âò„½Ï°›ªÛ¼ºÖ½]¢ïxå{–*=ãŽ*Ƕ´ýZï“ÀP«=•¸%ðƒ<ÌúLÞÁwNLåØV×àF{ȵ­ }Q‰«½sËó– öM×9°Êt~‚Tˆï`Ù°bµdÔ;¼ÏP _G.žtL^•Éb¸nŒb¥öƒïϲd£¶£QyVÆ ÙÞ߃Z”nü8ØòSq(‡ö ‹&‰IŠùL¶òt®ø¥}ï.?E ~Ç?ˆ7àñ‰^‰¿n„ê=Mh÷˜™üg¯ ŒÙ‚wÑ 7ä²O˜ú;úd¦ñ ôkœ-Ü#öë‹QKåÉ¿K:‡·°¢Î´–²0…•0ãQåÕ«Pì)|÷[|„c¶‹­¶qzz=‰Ú´v£1—XÅ‘åçÍUh‚\6ÕLÊrc—ûDkg}¼{¹ëÔîÿí§…—¹ol)ãýŒ ÏÉîþ´…Ô'Ä‘cåW»—ôT:ꪠQÓ/S‰õðøN¿|×"¶9­?y€_B-¨¨UêewHˆ®uYßy'1ÎØáÕÿ¢w‚_‰vù´ÇÕBãB#.¯FΗ°^¾é'—ZDqß3„‡Ât‡š,ã]XU>ÞyCP*4:¨Ë¢nq³Ëµ}ÎZ† ù0·£Bª=pqVhxAèö1dÈÑ´„>FÉÍ“|kyÄG… ÷ž°àYÃ`̳ŕxYAÛŽjÚÏ?ñ™ÍñÖµž=Há¨Âl6ù:lÓI«Ø4볺|GåÀ#f Âį§æóh÷s îkϯYþ[ÂQìâ>³HáÊÑ*þ˱’¹G^4ymøè=±^ÝüŸýiy’=-jV\ÝØTºŸ}Ôô)زù ’À`XÕÛ¹©läDÏeÍÁÆ-¼Éd‰GY0QQ÷Q²Ûp]æRíLÎ$ÛÈt9gñ¬Ó'ƒÝ“²,óý̧GçÚŒàIÚ8ߘK¼-7sšeT°êM’ze¶@i^ö-¨6h¹#qÜSV2ï^Gð¸1ôí~Jjízͤ BâM(©ö,²Ö ç+º\N¦Ý°¹½¾¶ðíÜI1Ê“Ül°ƒÁMÚ~ ÿøÜs¢ˆ´cÑ»·1ÔÉœ4?ÿ¸¿œ|¹«ûÔYd´ƒ×–0n¦:¸ß÷ó0)7yMò½Æó,ضf¥•øCmÉ º°æÈCDÊÈÉ£?“µßŸLôIâÑO†´Œß˜æL×Ä:º§EëªP°Kiöæ×zÌeãÞlüa×Ú}/]ƒÉ¦À .¡ìI£‹‰¥HAb?þfJ ~`ÅkÊÉJ›Ý5&›‹à2ìĬª çQ1Ö÷-s†ÍhMh:V<Œ?†h» Y)§ vŠTîL–¬q6-Á(!µÏÊ"Ædf± ~tw¨~\›¼Û’…+4D9á9>¥Áäv=4|úXSÜm+}†M³?Èéþ¿σÑçqÃþ±fè‘w׿N6¶ºèÖh›èî*ç ÕÜÇsáÜE¢¦_l &15•Ö¬2|¤¬Ç_h«ïZAâí ,>yâN8ò>‘»?©+C¢=šâBGÏyYÕ¹fÿ›ã GJÔBû£UdO¦"Y†\ným›Êë(z4òfß绞¸ýÃióÓÄÒ}!b(f§É‘H°ØZ‹ó Éz‚TQ» Õîð;µÛ‰e˃*6;÷xcTzt_år‘êm¤ÓGë­ÇÌÉðÍzÄ–‰ëS¯p.!ßÚê„,–o(&;.î/žë.öÙzæLla>\ˤsûÆÀ#Vû/m9()凓µœš¢c÷D+}óúB“ÃfM]„ˆ^{>k­ÏüÜñÈ ™¤0°câF…®Õ^€š}ýC¿g5q6ïsŸï,ZÛ‰õ鋜ð@h‹ûdœÛðÓ4˜áÄ+êûËŧ1瘋<Óš^xô9BZ**-H«ºC™ÀA¬ìø¦Cù€“Òá|)ùÄ}ôy˜ƒ¤R-6–87D¹cã«‚>¨¦?(%ñmõq¹¬ˆ_²‘Û>åÕŽ)~ çY©§­¾9óFÚØeÔ$më]-.ø»‡­õ®tôËC<i>Šœ6.¹âh¾ç6PÙÉ~,"fñ¥] ÔAò3Ö#šV(•Y«¹P)ÕË~ø×׌,FÙ8&ÛÑF2ŒSÞáTx”0&£qœÈúû¯ G“ÈR ô•Ü3ã|h¼VÁݯ¤f´¶Ê>Ùi+¤•øö›é ØÛ=/ôø¦E±%v¢u|î¤5U^~$ò´€RÔÔÊ=Ýjä¸"ëBT¡ê-jUxKHÇP£Ü—fFfÆi¿¢L0ÿÌX©ãz“ÇüÁã.qý­XjŸ[©e8˜ Þ ÛSâO;|Ù–~lÀo§“ªn ›@¿¥‹uÔ)*áø¶éç½…£Žl‰-9e=[¤E$KѤÁûUÖ "þcE13c( ø`a“ùÆJ)JB¢níÖ.Bk’4ýïùŸ/’q3t˜]šæ9+8´~Ú{w™4™ã÷FðÜ¢m:Žê£À±”M%GK;tpž|^`™¡É°§ËÕõ“·+¸ŸF2iôh‰ì|ÔšY„‹ÖŠL½èÈÂÀWO6´¥ÈŸÁ¼úðCõ®xç}óéóÄe Ü™î‚ 8a†dg[ʰ=ÔvÔ±äûõÞYe' ˆ`ùÞ¶ï_f-ŠË/)µ&&w0ØÛeÃÜ÷ÌÊÕ2Vü÷vP_– U¹½Ô7u#Z|àëëA:©|¸ß|Ávîß²²¶x0WSyîï]±RáCr;®c!™¿»4)¸4æ«iRÏö‚![dI†Þõ²xŸ©ðQ‰o0H÷d¯yU6§-ЉóÞ®F†ßÇ.´ço‚Lª‘“ãkVÄ„*h]1—Þ¢] šÜwõßcsbÂÖI5¹¡I,«ë'7Ù¦?«¨GfÈçïE>׋š–\[’™ÊÝêúoJãÇu_—kT2ÉÉ·LHkM6ÁKâ ŽœDEÓµ„™‰‘6³píóx“¿îÓ%ùYPP³jbSÎyÉzÙå ›•QbñnBª[¦·ÞÖËI¿‹ïFñOðs„ß(ßÇÿ¸ïö|(ÃðþœZf‚"}ŒTLàöB1Î…*£èÂOýt΀ö·"¯ªJÂqŒ’^cª¥o”·ùí鹊‡× Æ70Ê>zÆŽy»Gv«ƒ×3áH„šë•š'dqo>ácÌŒƒNï 3ƒ$òµyOm8ÂS†'ø0–ü»—#K•— î ÒŒg÷§q¤èD¶ï ÿWÿ¯ªÔ$©ñÓ™`ûæì \¡ÉÇ<²´u]÷ãɳ´Û<6Æf•¶xÅkøÙÅ•2Θžì#t´ý€@í’g…ܼ]ø:0éþˆ˜±µÝ°tO [mðÉ(ªЬ˜X)¤÷,ô¹ßÉÑ“ís*ÝlêØMÇø[ñ·5›6ñê·rc/íƒ_½ õ¤é YQpjòÞS÷àh öBOD3B½ã]Ë™9å“d»¿¶ë4Až&-òuÁ_Srî¸ZéŸðA¤trØe1Ûn¶3õSÀVqå+î^Õ=·Ï÷± îºêâêVõ*ƒm·GalÙxMÏ;Ü‚‹6ž „X#sÀYóܧÜè1õKì4¥¯‰§OñJ©êHÔð`=$Ê>¹s[ÖG8m0Ó¢j”À‰í®Ã‰èwV»ÒëÔ_gkNi&íZø…ɪî'Þm (µóM]bÜ,}á4˜vv6ÂîdÞý¢ñMÈmõ¶G„Ê"èV-ÜÔÎ}ñ©éâGge>ök@ßRääÑa᩵Êjð_y`ñ¦Ðï]S²ž4œ¿]Y®mß«ñs§Q>•®ÔÊ”I¼ú÷Ó«bÇØÒ¡Ú²Jì“9P¾AK4c¼ HÀýúibZb%(—VJ¯³©ÖÔ3[|BÝL5}Ë®åHXh_¨_»}™Ü3m…ÜWƒ´hÔþ"§-ä,4P¿Û¾xÿ&.Q8¿†ç@pFkÉÛ¢·â!sŠb=ñ„U2OGZêïCêíÛ#óÊzLü=6°ù|,o–gVâ£ì{ã˜0ž«ÓkmžGGzÒ5ýB½Øóæ0ùJý¥‚i´<7•|ýç¼ñÇ7f¾¢G¢üŒ+Û(=ÿz>1av:ã•ßïQH¼ùC7*ržXô°+Ô;Ž"D6ºH·û’Cð“Šî½8½…–Ë!Ùòç$µ ™ W‹CŸ”(c6¶†NõµùOWÑhX•¸ŒuÜ"PØ…ì‚8>$Šâ-/y‘Ôàzõ?VøÊ s'I·VÎX{ï1û)ú‹~âOÜêTÊ•Œ9yt# µsžÚ²ÎäK?ÙòÒÚš©ïl&.±´â`4ìmO0ßç+ò"Lu_•7߯UŒmíë„lße“åR¢&fÁ Évˆí®‘^ŸÂî|&œÑe‰™5EixÎ@&uï»%š° ÜUD³W0d­0ìæ¢ëg²B|4&sœ†£¹Š„™iö¼|rV;y‹=÷pbãŠ)Ìa­|ë/_“Ó¼}E›öÙ8F‚úGîg‹üêyûnôx+³ùýG¹†Š(f¦¤ü[Ñ„xKŸÚÐ6{´\äåüaU TÛmó³'и›”¶d¢ 7+Ã[ƒ.À»Öûóx$ÕÊg&çé ¯`öDÖmgÄ™|×° 3§ørxÚjÙ¹pÄ ÚpWês 4NOì\—Ž v‰®!9õ7ž) ³V§+Ú¡{Ró0w‰Ékè‡oo+-×»w<2íæÎrlÌU5é{EnC5e)¨ÒªPâ6?­ øÝ#g‰λq°dcÁ—d2¥«xIS ô5>“°d0=p›Z_¥ wÚ…&5¯DØá¸ù°ÙYÁLfF$¸kr‚\ üæ ·¦¬‚™òØUº4WlJ¼ÑΩ[Mù¸c½š%¶9_ë} 4 Ró«l;,zµq®¾ÊÙ*9r(Ó®[ _ðñøH°Ò“A³ÝÓ4ÇFÍ|×®c(ÍvGo4Þ·²èЧßÕýµï†Ô¢ýîÞ»÷±ßܾdÜ®º¢f¼ÞóŠÍ»¦Ím¯z!lg*1Bè‡æS‘IÇúþ™šÄ,Ùœ&…þakv´Ú‡¢…Zaw/ó/„œêOM-6ðoÀ‰éKeÛY±–Gž%‰žû´J`µA~Ú8Ó—»oÃnybµ0›ˆË®»v³nù1Gy—WÝvíýz¶"óƒjÍCU9™K½t¬žYs"ýÖs,³“h*û‹%m+›&‚UøÏìŸo‰Ü6´Yœ[‚ª:\¯Ùõ,o Œ€ÆZo…a>”-ÿy@7ÃP íHA.'Ë[%¡ÿÙL‹¢cÛ´˜nÝ;Û|ͬåÖw˜lƒïºçÓ;U÷MãVŒ>=˜ÖM{¿ö$b&t­„]G1á`ùÕ&ùx€¢v*á\ól"[ïwùǃÂ=n·×G0E*壬k$Þ_lp¦V¸É»ð»Ízw^{Ør'šgïÆ8ÌsáO˪å j+ø <=ñHÉVšz!¾üê`ZoŽÍÔð°šq¨[AðYâ¥?ÌÑ6Ñ@d4>Ø®6ª_]¿$£¥Zêæ?nMW‡i#”õÃzèõíô$ZfC_ÍAÝ÷34|¤EËd»iË ‹K¨x%°ÙõF,PøWø©f›|³a Åï ‘i^8”gMVÛ§„1Gc/3;­ºoª§ü€ËåÀ_*çiFpw™ Y’P[£^”Í?!ìWy´«Èû…û5QšjͶOzoÝ­íkrŠ-¦=%Y¹¶’í~ Ÿ+m;ïhÖ–ß3vò†â~¥«Ê¯ ÝQo”²eá‹CçËÕáDá\úÁ%ÞžC”x†òêüÕ­ñ™\Bº³# Ñ.W¡¤˜&¹b—áÖJцH·U$ÄäIÕ£ãì¹(⌢ ßjüÒÝB ÊHS§ÖLMLÿ˜‰}áovûÁÍ”ä¢xî˜þD7: †³Ñî8z’¸bNbä,Ò ü`À0½×†›Ç¯aPäFÐ?+“Ÿ¬i­uêw¾†:œÒ:¼žØÆSÓû×ø ½} ¡×9RD§v¿¿wÆ+xžÒúœßü.~;‘\Êgó¹ì<î¯øcᬬ'ãZ"òMÒ¦uíèÝL4¾9æ§Q¦C6ç’©Gu·ßr1 ¶Ð½R!Àí\³p(†š>w î•ÞrÇ Ö™|uš}/= ÷… }¼p—q~A‹µÊ·:ã ³‘ÞW71Óò;w­Ù¸ ŠŸgNo4nO~Ñù4ÖK¼âŸiï̺ýÅz£þƒ¯\Õ¨»Xþ‰è„Uu—äy®ûômaÐ(-¾’’’0Oò7šy×KExG!åcèm°xâ½Öšôôøý—L²åE´DÓ[âø°_Á¡uöêë EK»H³àSO…'ù:”Õ_Æ*õ¼k}àËæ’d„¥`SÒZÊz¢Ý“ EÃüqÊö[65W%Õ|õëæñA+…ðŒ§„.;Cuwðï0›Rݺ,<ù`‘òäæWk®è¶pÃ)ŠÕö‘J$†yÍ_>’RT@=bUÑ¢i)E1\|¥‹"ç >¶S«ì=û9‹swé[Áse¨1ØË“:ÿí»!u LÒÊÖöÉéXŸtÕùÞÒ$Åî“LÜo-;ŒÍ^îò̾ú­©T<‚I °¯|¦*G)ˆ*¶¤^ŽÓ|žóX5ìÆ³$jv{þ°|•>1ˆïexý‘VÜSN¾j«ÃðÅÛʈ\¬³EŸo˜£Q%-lÝ 3÷¬í lÛ"ÓÍ‚¤ä¹“.çù51IâµAsªƒ­÷ÖÃT4_߯,ÞG/höýìÏÛÈÈ3! VIõ2 •‰L#tëýî¾3yOÓ;}. 7Cd$_·…ïg\_óŽd›)÷ £òƒQˆWË“n²åîÃwÞÇ£¢ŸÌ?ìÂÌÓ¼Ÿà^VyÌÂÊÇ-ëÓÝóû]O> VÓ·Ž&ydóê“™ uO- ;£Þž§Å$~Þµ¢Æ[}¿ûÔCP4£™:Tì᱇S…Ö³/.®Cæü‰/õÄ„éNkøÄ¥1G0>àRÈÙNÓñ}{kÃÈ lûŸ>' YøìáÏq ­|äa..Óò¶}iˆlÁ²úëÞ‡%_»µX7Lž¸ “e‹CÕž´Øc5Í”øâ—.©`(®,’ gÒ—E]y7nzÌ¥Íä bU®2Y·&–L»ª/XFõáòfè>Á–C»üñµz­œ½;ªÌEäò¢w‹î„•9Ëý,§ð0ªmÑ'œ¯Må2íöÀ¬0–©æe UŸ2ïà<åðzv›Ã¨d'ýë-QˆV)_ÝÇ̉«,˜¡RCÍIß8#:_Ðéƒ÷õlOÍök¡”Btß‚oîªÆ(ÛѾî~ßÐ)×ygyœˆÖ¦¬§æ0”Œºä(êeJ„/´®Áh:3>JV-á†|c¼éi½ÿj0ã²2^z¶ÉT3ï4n‘J¬‰ ¾FC£ÌçI(OÛh¤›(ïß~¿€ó%‰³¯Ð ¯t1¹áÛÊoÖ‰’­XhL+÷GuÑÕ-õäV?íßí°U}Ñ|•ØÎ™Å ·¥'ó£[ˆr…6¬j£K’íØRSP†|I´°¨CgþSß_1úÌ-HŽÆàÁDnª‰©&†VDèí(V;i²ÚïÓB}bTØ{wt2_ò²Ì ’n &§¿º4ã7×EÞ©°NϾ+bŠöf[Sc":xµõ…¾忨¬ŸÓö|èû[$‹lÚ-'÷öîÌ4zZ[m Æ y”¨ˆ.ü‰“èâ…„o{eœ[·„y*Síjæ§®ÊIçC_¶*¡É6;Óuo¥Ë9>|±çû¹ÓÙQKoçõïz·PxÈ;us!„úi(£»ºo:àu¹eL.VŠèŸK®x*Â|Ú¦%òd¼-W'°ò6.ƒÊôQfá:²¤¡rüš‘ hd#Zúu:¹h?6*>¼Ø€âð¥ÅÎY9£^†ñïäõ½-6:_·†ˆ6å{–MMšâ=+Ô àž- ÏéLÊÛŽ‘&éaÓÓЧy´™¾³0+6míJšµ`oxla^Åþä”ññ·­8™Ö1N†¨:ÇÑ6î__K ¶€Šý\‘œâ?yöZïqÚ=,èA]'nõâüœøÉ㦌\쮸ï<8ìíNÂd.^ù˜,ãS6wý&¢iÆÂ¶S‡MåÎó2=É m` äÓ…Ù©žï46—DâÄ„$Ó„g¼u²_:(‰$¿á)X»»–[šÂ…uÔa†—~ö>xÒ˜®ðP‚¶¿,|tŸT¬°Ø„R¢i(aÞ®li„<Ìqæu5Ó²Ö‡­|‡£Ïœ;­óG…‚a!µCä1õ¨¤5¶`r TÃ.7k«J±_±Ñ…Æ„Ž®xU`XOõìªÔj'ŽÄ]zRÝá°vSŽpÔåŽÍPïrYŽ¢pæûËä–G¯Ï‘n¼3ñjZÙêB2_ÇžÓ¹°3ŒæÿL°ï-Q¾‹%¾ýæÆ‚¦ý‰³­¤@¹ù¨\[{…“¦º#®{PðÉêí$«‡<ÂÆn÷M"Â!§Ï݉XÎ4ÊiE§C‡”ßÅUê «~? Ì WØ:º1Œ7òþyÎöYÉK‡cçQqÛOÌšý!Drî¡/ˆ¹ÙO¼ô°h[NLn›ïVç¾IïooŠJØÙ¸ãxî2;þD9ÏãuvBýí³+ïöùÄ ³¨õ@ h‚rÅ¥K ÔXVßPN©vF>þú85,ÈË»QRô°Ð¶G7› sa1ð¹qˆH‹êä™f½†˜~Ž—Y·Í²ß‡(¤›$¸ÍFe6ÍIG«Tf›8eB¥ú¾Ñî‚tšQ Ûý¨ â•=BÿÔí)¥sÿ'N¦U¨ŠgÊ‹·ñö=?¨®f{^TpaõXÕà—VÆ–}{å]Ê1½?¬‰œöžº…’õC/_-r𛣖øÙÓŽ¶*OÔ¬&0©9‰ûoªZÇqyø£žZìmêrCT¹–J ·£Nš'i¾Ìʼƣbk­ %ÒU¬&V o?‹–›7åÛÇúAà= ×-ÁÆäßË÷ΠΜ¥cg.ÖŒ¦ fiÅѾat–ö ƒì%aÂäyÒ|ñWgMa0›WT¶:?f£íeÞMž-‹ÏäûÂ_eé˜Æ„:zTe ¸ÍÖˆœ;<Ì D"ÐI$ä(Šçð)”çÎ÷§Ñ™‹Ò¿N1ªTVÁø)ù®ƒB–â‘îogÎ2GßžH Ÿ^ ‰È¡x¨c_fîçK»Çg _èøsßêe•#«Ú{¹bK7I¤‰7uBïÉkÄäþÈ*;Ÿ1öcùÆO`죲‰TzçŽòëc†8¬ö IGŒ éF‰3…ðAñË#ŹMŠ%¯ïUíÆ¢ÙÖôÏ÷Úf«Šw„Ê6_ZÈ,°•à©-œÛÉr¿µ3É©ÚX¦¯l3#­G_5·Q¤1Ln½ØÂ´zóbIйtj.H‘ š©aáØû"ÁvGù8÷…¯òmÕB,»ýÚ-4MDŒ;sßOrZ|Ÿ§/«gÉ;Ãï Îõ{p7oJ§s>ºdü‰išSœM6`y0†’}‰©k½åӻ8ïº3¨,Ã/¬bŽÝ.Õ¡ŒßéÒ÷b,DìÉÔG,Û¬¨·O‡Ù¸»™Ñ”qz¼Î§â;7±gp_V´BKw=ËÞ®â @ÄÏWÈ:§xj£ÅGJyj+¯Ó¹Dd©BEFZ²)Ã5eBIe}™øA½û,Ï‚Ã%ýMK¼3 Ö,šs~$ß›s;ïa‰>67' Ùó~;Ñ.˜%ôÈŸÞ71í¾ÄLxâ-?ß\/‹6q?ëÏ.©îÇß„`h~N¬œß¶žê½ÁŸQf Â"ìµwÄ,™î”i¹u°-„.ÂI½*i¾öFD°å .#Û ÓÊÒh5ñÐhXìà@H˜z˜!ÁO "ƒÙ~;É7¬È¶Ž©bïy+W¤Iœ†¦>¥2ŠC$Èt}e<¯ ð1½] Ÿ¬ñ«cz7q„¤–zZÀìœZ¦?ù­ _œ)8 Gí²=$æìiÖúºÝR°Ã¢½ŸÛЗrmUf"ŒaÁž—Ma©ùèñRºWÌÇ&o0ªÌUS-´’=!9§T2‹å~¬Ó¥ö B›,4Ál×¶æ3WbÄR\ÿÛ†?N´qĨ߲í  ¢ú¾e!S>mVŒÓyüæG/Ù1iv åмK[ÁѰé}¶ c•þþÞKIÆ¢‰»õ ÓÏ«îJŠL–/šÆˆgÒ9sר_•Ì*«ì>PJvŒÆp³Ç‹r¥QÌ®ÿ(Â)Ú[Þ(*¹ƒ®°Æíx’À îMÛZ¢ì´˜iìù™2Ñh€<ŽÍ FǾ½ö%ѯtòf»ÈÆ{?Žn{X\¹F9Ëúcà'…©ÎlÍíÎß§±·=ë΂wfŸÍmÚëó‹V%÷guMAw˜‡¸ê­€íEnãWÞŠÏê‹.ï„©š¤YÝß×dmÅê%ßHeèl<§3ÙO‚ aÏ¥V·;×ê E™†ú8E.8RÑ‰Ü 3ªÍÌ®hŠÀ>Kص*zSØêŠÒT„Xâ¿éÞ¥†œqÝúJ("°-b¶Ðb­ibÑúÚ aÒ`Ö°çºüü {»)Ù¨s‡âì\ýu][K­áܪÉú¼;ÜÉì—¢AÎŒ-ä™)&ÍÞ²s¦T‘3/Üfbå` )Ïtk‰c`@¾ N¤sb ºî¨—Á¶"ì ñ $ O‹¢ŽáTÅ5Ÿ¼òö˜ ( ˆ·h3ƒ¼~[K~·¥eü-¢-Y[-ù~õ¼šÓ;[’çκ„ihTÈ᜹ïc’mØœRŸ¿j®| :Ý×gg÷%¦^Oüˆ‡"7ÜN>„Ÿ¼á'IþSÊÿ‘ûèÌ g¸R[¡‘s3mCtt[¿K·ÂáÆ]žm}z¿ –~à#c1?‰èã'4„–ïfmþ|Ýó<ëI¯K¥ù ëáZÊTŒ·0Rg‡ ey~7á2òÚƒìê=a\sý–—8²_-1“ü 8ÕÎŽ:àÑÙ1jÔÖóðdØD$yp%p¨y0<^ýZNk­‰BêWcúY»,”í(á6bÛµï¤3Ñ’Ž[ó'²çõ·…“š‘(ž$u¯¥’e7ÝFWC&Ë'hNÚGI §V„åêÔ=ÏèÏ}ä­QÉ$/Ä'œpßÙõ zaÅ~Ê·G)#p‹01Ó U5$3óI©W:ºxçÇ0­ØþÊêV,a]%¬òN飤,Õ¾Ó_ƒÞ“àöÊg²µ=^[ñ¶iD~üg–±dYµ]ö¶­h à?F\v™GwŸ.¡Õ­Ã¥v)ï(Kð§<£ŸA_~§Êô~/Jüãû+ªQ•¯xnv§‰ÀHjHÿëS^Ùñ»‡¦ö޵;ã¸Ò'/&©'{¿Ÿ=]™€K)‚à¦8¨‡ý㢛X]óO¦j>ÆçÜ?HÓªÇEe&· “l•:—bY/2¢ö£+J#·¥X‚½CË*zµ¥#® s¼VÅàÿMÁ÷¦ÿ ðÇà¦ËE¥"¯<~òâe—º?t,1éÖg†+e5Ä7¹¯E¿ghî"ô׿_a ©~`˜æ™‡C(ø‘3}Fúfã¶I®9¨sŒC\Šf5?ÍboWÖ¨UŽãb$‰!küÒÌî=T‡ýúŒFßsrÏY8)âU€y8î¹Ö´xdÎst˜Þ3¢á‰j ƒIýSœ ΞiX;‡Ñ_(‘”~“`'lcç¤MÑ® .Ã*g$&Qè8Ó]‹{4À\>CÉßd`WOn¼G쪀aõXmöǫȰriÚ²øvYGIv‹q—pjåtЖý½§w°¤+5¸´2#ã§o|‰îÚHÈÐ3-­ýÌYÇ, qÌÖMöX‚­˜Ýuc5nhYµmmÔ£ú"wú¥DéZòí0‚çZŠ(åæR׆ÏWbþL¾¹Ã‚¿EÏ#fô–º]›ýÁṳªtxÀƒT3À;™÷–ºLÂ(ä8"µ¹''’Ób¨J#\ørçÀØk3S>ìVélÖÝ÷‹ýúÆrÝœâ>ànÚðçŸ*Fž.ËÍÄ,weaLvjY¬± · Õ)~ýšÝu0yz#ž¢÷#¯(©öBqÓ|,å‘ aK€ Lš†d¬`¶¥TôÛ¼¤|¨xMq]ÎF€J¬ÀÇÕÁz#ʈ¡°ÌúÚ”Ôèú{øHî Šic\PÂËHÿz&UèÐÄæ`Õ©…8¯[JÖ-õrŽ^³Ç´Xˆæ=b粸K}ó#‡øx—€âçéÈzìt$8ìÉ%jfœ¤Âžu¼Øp°Kp„CbºhA™“ⲨÓÛ®0’§®K)1oùÉÛ1d­–ði7$?º±+áÞ¿ˆî N\û~v¡+ÑȯDR~}¹±AbÎâF£Bþºê™ŸBÝ ™šE|ƒ|‰m[¸1Ú)&´ªG‹ÄüÁSÝQ½KÇgÏ¥}ÅÊ·BîÅ{.i¿  mØGjÚÏ+(¥”r¥ºáYL/mm8åž73ˆ Ò8¥ô3ÁOL-ræ;Šû2<.fh±J+TÖÀ-Ú\G6IC$g; {ßSívôišÝ‡}8éð.}&¿~ŠîñZD-Îé˜(3|€7[ƒ±Ø˜½(êIy–«øy3Côƒï#ìÈìg<_åk%ƧÅïÜ(Xu¢‚ð†^ZÝÑHÍ/ë!ŸÜ-”ô!óÊFõßúö|ðtê\KÆg߯ôæî´³yv^2Ú­ñûç…ÏèУø„7è5"tÊÕ±2òÏ+Îì¦ 5o ë¥*Ýý.ÓñIHÒŠMóÃ,Jj¥#3g©'̾Uƒ°Ú¬p~¥ûBvÜÏ!KìÉåÍ7j1‘‘Íqg7>ŒÔE=¹ ª û?íC›. ¬¥Ë¶mÛ¶mÛ¶mÛ¶vÙ¶mÛ6÷WU»pOúÜI:ódQòäÍZA …Ï?²ï÷¥Z'Mëmõ¦¾ÊŒ[#‹6Né†F© å œcä€ÇXæ ”îØûŸ+ù".uÜfRÍŠð}Êq>þÔi_ñz,aŸÝIŒCM°~ÎQŸ’¡pP´±ÿ¹\ CYÊ8ñʲd™L ÞLDûúPòŸS Ftn2‚F@‰k#¿iÃÆÆŸõç^‹¦§-h@˜_ýà?»M…MT^_«øÌv2 yk(mçÎB†YeÒ§”‘J¿b6²¤b$f’Òí7æT’cËÜQêž±l0fû c!· J}Y¦B†×ë&Úº0s+Õ%CØÜ½ÉbÕ°BŠÛŒ3â²ófÎDl u¼ ÀE¹"ãvuÇ’Š²dwmz¦àï™ô»Þ¡')ŽYäPyd7¬y<€ñ’6IbrLíùkÚF ˆ ½BMê4E+ÞÓa)5Wg†‹GOÖ7¿—â2Ùk'ôÝ´.iSQìÀÞ*Xù7’½þÎ Ö€ÿ¢4TÚ®Ûø?Žèm¯CtÔBz÷}²ž­4Ÿ-Ff?O-'ùð­ÁTÝ&rÌ]šúŒ{“ák÷ØÞ yêâÚsZªD¶ J5éœ<0ðûdŒ{s¿C™ÊK¨ºÞd°1BIx%?øYȨ ·²-½V‹&“X«Ê[zïÒ{ܯ·Ä6ÎÄçÏ"ø@Ær7%9½Ú`‹ÇÕŸ»$5=Â"hq£9mçòN‘àf÷»út`0³ B^-Œá¸w÷Æ×¬›íž«1ºÝù èŠ#»%óDqºŽô¥1Ø=Rç¶ÒNÎÄ&yk’@:71û\‡óE^‰§õª®¥ÍÈg6”ñ6ýS˜à¢hð œD~á`£—Dë_©)šV5ð×[(ÿî­¬è¯S½8Ç£iÕŸsfܰ$'»ZÔ¤;±à›¾h€v×&qå¡f¨§Z÷L'×ý ˜:‡Ž­€M3”D)Œ®%“¢Â·7&Ž$ÅävpîÅýÁͺ/œÅ7’]1nLïÜ»¨2˜B vyõC ò< á´øfîX†fÑœ}ˆ°cùì-Á6·É¡yÒt§ÛÀ /èlqkù¢NùºSä‰MéROÛ,QQû¨­s‰µêc…Ö„iÁÄA>ÎãéÔÚ͉§›mÅ&£Î–m] 失õ sAy°*bĸïÂrœ‚>¦].P Î}xŠUÿ{1ÈŸ÷÷ÍRÓîT™¯Xºgaz¹²ãöøïœÇU£YîOð Î#ʾ¸+§—M@e8§ø!ðI‘Oaë=üpŽgÁÕþ:ÿ9þ½[ŒõzÆÑO=˜¯[ýÎðƒUœ%¯ŸèáæKí„9x¤–ú~ïÿ]6‰ë´^“ëÔqH„^]&Žs†10ÛZˆ##ʬ"û³õüDg‘Ä΄F,]¯Æü`Pú—'N.4|¯C€sÓÇÁ6‘ØààÔk“ªœÍº·à#j-·™k´}¨ªM;ƒ/éj¦ÚîÁ@S_:j/.~”§À²D,4Ä7s¨Ø#gÙ¡Làbe‹}‘œ6ð$Þr‚G—R°%±Hõy ]§ÿlvM¹]‹í:q#mÐ fïû ¦W.ãcRµk á¸ÛƒÝ"àc€N; åú„côpŽ7˜yŒ¤{}GŒ,&:ÞɈܸg|ÝªÊ ¸úà{`~&ý:/•ÞÌÈP®1ñ‘BEûWRZJI˜¨t^€C”Æ…ñ L¢sÔa…<Ö•i'«âÖÊ£õ—‡ò:Z«ômÓ],®Í§çFJÛ"Y*ÓÊXŠ©–ùÑcì΂âvë~QfåÂäŠ9η²Ž,”P³õ þþ*ýXªz·¿;EóéV8ù‚¸ì.𥖟“]®±*‚ÔIÿús3ïp¬ ÛÒcF_Ô›¨GÏ¥òV½I.ù±ê„:°º‘ ZPCI®»C†_ Ù šÉZÆ?­‘ªµ¤õõØ–Qr $탞™rë¢è­¡ú¾såfJ}íhÖ]âÅ*g“ïnD Z"NrGw\’J~¯†õ`ó@PÂkfCpÊD&ýî+†ÙèRùW1¦½ì?Nyy®§¥O_ŠWÁÎ Z4ÆÚXN7›!j#RRá¢é7æ&Zpë«Ê¸ÿ}¬Ì”^jpíÚÏÄ™LÕF(®*Ô¾/U­·âMO,^ÃÜÌ@ýšõeíå•sÊí±ChuÉG§ì(Yl 2)Ìó¡YäósBˆÎûÅ­ÛDH›…‚•JŽQ‰äŠŽ°Zf1ýüá¾Ãæÿl³¯ÔÌÒOû}y×1î*¾Š Á;å?lhÝæ˜•¨¡ùÝ®š÷ÕÏÇð"Ï‹#UÜÿM>+@=<Û–Âdúh°–¸¢þÐ%:Õ¶ò˃Κºè…—A Eð‡U#ù-‚cöwkˆz ã kåo{§Í±»f’(å™Â)F¥pDöÄt_ðà‰$k§£]EÚúà9:<mÜjï·Qaד)Û4€Ëv¡‹3c|ŒÚüU Œ¶°Ú½—ÔÊ^L”è›ZU_n[æÇ_=…–ú÷ΆJ:à›ŽÀZ”¬7ì¹šŽ‹Ÿû>µ1œ„°L2}QÝq.OÕ¸s' 3óÉ­cš!fÎÍQµÓÂW-æv§B3f æ?d:¶ÖYâFn‹ñagzßÍkºר*x—ød9aDüB{t{äJ¶¿4z 2ãå&¼ {Î YSÃZ4p°sN$>§oÀ_]ð¾I/¶Ï¼»{6þxÅïÉÍým¼\78ý˜©Wi3lé9‚Œãé³HH6 Ëy>dëÊ?F!+7î˹å‡=uÏ&T!ÆÍǰbÆ©ö™Â2ðú@\Áa‘µ A LÀ*H§[2Ð\uEÀ½¨úý5+:Ñräq¾Æ ÁîXÎÀìÓy¾¸ Þá2r+×3¿‚²Û®"áƒÜ™¶Ñ Œ3ñ›ì³Â­ñ_*QeߌïU¨ïók1ƒ 0÷Vüã-ÿ]}:ƒXX+<ùHŠ%qiÂvA:»&æå¬—`úMó‚…ŠùîˆÕÑʽX©ÆÛæV4¡•¡éÑÅX3£O®Í[12 ÞKj$|‡E}¾yUq’l‡E?H›>Ÿk{Õ}vvMMn¡uÑò…M½!³ÉšyûUŒWú¥§3$ê[¥Çš‰x,Aœ8Ù<©NS"¢:Æl8ÂÊž¦j*¾I×ÙtA¯2hb‰oVcjøÿŒÛÀmÄEŸÙH¤<®…7 Ä%௖áÜÇò°òš®*˜—ŒÁàJë|©ƒ& Y$Ù4þý(v?…jÒˆmuðäÖL¼S¦×¥A ;¼ƒYE K:ýöçEôÏ<0o;R+Öož~LCÉ'ë3:§Õi&®uÊ‘‚xBÕ­²8nÀJ-Î|´Ò?G@†…çoÍL@‚Àqc.¥À™U8¯æS„‹t-«ëàù†ÀL‚ù©µ‚U|¨ Ø­Ñ™;x%£ß\©ÀkVëƒx §!vBk§áb÷2"]¼§[õ §þI˲ºPÖÖ+Eÿ@w·½ïyTØ /SÀÎ0»ÜÔ¯qÁ#çÂL@´ù:<â7S‰øû‡¢¶É»¬óö`±©f„Œsmìh­œ`‡º~¨’~f ¸ < °ìÅ0ßÞ*ºû^ùPU^Zy²­¦]'6nRõ¹n~ì‘è¾á³l^’ÃZ!ÌQqmèk–´™ˆŽ¿»Õ Róíºç‰=¤òÅk"ËŠ´™æ<±ò,ÿã+/¤È°3)ˆ÷ E¯ÑÞv¡$x™h^û\çít1'ä=…&A°}Z^QÚ3‡{…ÝšûÕ¹vwm@Tÿ0˜JæhØ``³tÇ8é—>hŽ<À±[½-ƒòEœRÉ®÷!æç·kF5¶®@¡øyÜÖæè_`ŸvÐ<+þਗ“j¿¦!Ò±æî8ìTï ËËùq|ÒRÔμiæ¿ç¸pßšxY×S]¼ÖB_£2Ѓ‚Aí&?kÛ¥Ëä8ÖÍ ;@àJ‰º·ÁÇȺê%¯_.§ˆ©5^.½©vʰ ¿f¾R¯†3£ ^@Ä«žîZ;:~˜úóŒÝQ5¾·éVw\^UV»ˆÃ.ùëâFƒ%.܇¤«@úôâ3ëåj’;áµX¸ É…0uSrëL¶‰)2Gí®€æí¿š#XU=QÁUÊT.DŽ«Ô·êÍ7,•`¦ré`ÊËèh19§ÎmÉs*¾Þh\ßu“"¬ãCô#jÊÖÛ±©hñ”sZ‘Æ¿9=<6 ì_gMê|—³bcQ ŸœSihßf/û8ÇŒÏùm"Òe\õÜýM¦.v¾ÕâA¨–snM¯õjvï_-h¡) $èm¬,\^šqå`¾<¶²®¶éó4d>š>COL’C+{pY# RË9û æRÕcGûˆÅGƒ»è—"bþÝÈ ç‚ÖÙ3AÚ/¤À™Ýð—ýpG¸E ˆƒúyóQ%ë/ ø™F5_‘è"P/ö¨tÓW ô"¯¡ ,‰Ö jµÉà‘;$%šŽQQ0âl*Nä l¤Îº›{Ij]TuÇ3@,w¹ kH Îßî†bÃÊ82tº$ê~—§Êe€KV.ZBqY@)ÑàÝÀ½ßÓþG€eÂî~E¯p8C¨¬œðª###Þàöƒ˜˜ümíQëá {ÉBw~™B&ÈÀyñ©^nowч]¤•øsy¬€·‡Á¼"n_UÖîðbn&‹qÞßþ ?¹Ö(jƪo¡ù›l–>òQ­Yrª.C/Uè4\}ÈkÆ ãÅ”vE0 d(É›pèQZ§OþöÕ{í9âçwß.IèiÖw â®Q>2'O›bÔf|_ÁOò>´t È—Î1@œ}²Tè€aþ¾7Ã+Ÿý­‹,ô6ÜL6ÁVs{ÈR$XYOµNÒáWŸ¶JAýŽûöÉὬ«0F¶4t4?0Ôg¿ùÝZsm\'v“š .[V1™ÿ~M ½p×ÊrXŒâÚõ“æÁ——¹Dð—n)]D˜V}MI Ä- MØÿ•ñyO¥@•~Oâæ3Ê~· ’ž:•ÂÞ²d¾P×£nGìÞ•LÞ_^½§.+™ U*Ú9Ö¨å,šº¸Û_öSõ˜F÷šØ"Gá"¦Ñ©Œ¡‡éȸp®Cï7:XåqT–±Dj†ÍÂøõÇK"%^Xæ8:xô¡SÅÀ†kŽaËÑ5¢„fĈÄO?_ ËV‡Ši‚òA£›=Šç¨hÂöL:â¦(qÔ V«,®Î¿0Ö:ù¹úê]òƒ\ ¹N®Í7~ »Õdé¨QÂø,qÒØ´÷·—ÿþ‘Oÿû»cΕ޳hÍàÏhãs\™Ñ‹6µ{‚MvyA%€`üf†æ>h1k± 'ìÿYÈgßAÏÊ€Y7­9•Sùö’ k –`8I¹Š ÇþBpÅ È @Tvñ™’ÁÆ'î1A‘ÀÀÑ£{£€ I¥õ,œéa)MÖÎû°• ¢Øë®í€“µÝ‘ý¶Æ‘ê·õÈfAU©e+êl‡Ú„!;ƨl‘}Æ7’rïÆ¼™Šá)Ÿ`¿‘Ñ\õ°Ü–Âî0)¤/©øºSdÓ x×¶:v»~‚Æï×´-gØÝ{1mP0+8×OúuÙ´l †'(ö–þ< ZìÚUr¹RT]·òãhœ•t»¨}VaG<ŸáxÄÈAߌOδO(Ôg ë¾­ÑæNƒöHÑ—ps2Ê<ï¦Ô(iØLèþ’£`poµ,ŠÉsLv<ø·n48ôî"^«è¥¼èˆÓ€šx`2jX PdcŠÂ{•âÄŽ³;Ö¬òûü³ðˆ)×Ãy³O9§ÁS¬ðÎKöÈ‚¼3©b>q„<@Øè±­ ÅH„'3•ÀX¾]U ®ÕÓâþDŒü]ýïãcuwŒÓ\j~Hó•Ehœ·$¯ ‚´¿ å²PQpZƒ4gqÌç?5{…Þ€ßVJiÇ{ð‘ô¹ë:Ä{¢pYßAi£¾ÂÂ%‰Ãš))å)Cõe™(ºËãÔMl™¾‘ô %`>CEfë(}Ë—ðƒuÅëêtϼmÇg_³DQ¼mÅmkkÖS©º°ŒÍK­Ã•»Ç"õiÞ KþZ–-·?)U5 ŸÁÛ:š16 î„ÃÙ|§†‡p)â¤úä°ì±»¹v§îûîN9ï:®v¾Ä?€•ñ¿ÌÑØú´¿T†íjÙ:jOïŒ_óßçúá ÍNÖ§ãÊŸþMrwA¾·•Í™‡DDQ^!«–ôˆ*èNÍb{¾Uå0qÕ>ñä'¾”‹Ÿæ_í-^©¾ä]ü”4ZÒŠ±â‡ñ0¡s(ÒƒÞ¸lK¤XKe?#©½ ÌwD¢ÙÐ\ïïŒDñ.é¾4%†®·HâæP@a»-eWFv|'5:´J“WeׯxdÍ‡š ·³‚q¡~eð²°aÉ~¿Kß9{x©GpþÑï÷²§ÞÙ#>^Ɔ³0ÕV|Ì„¬ WÑñÀ U¤×⺘«Æ¨6Ê/Óªq«9Ä)|9µ‹YGÀ†t+vðŠ(Z¡Ìh!`:+ÙDãYÆ¿†c†Ý0M—­ a™0Frv:Êçýn:9³7éžwEî1•ö‡±^|Ÿ®Ó|%Ø,¬ÇC6_)ˆ¨v¨/O^½iˆ  MF§²Ð:p®ê—²µ¤Wœ DçßÄW}Þ­¥Äâ;#fy²Š‡:¹sï+m‹_* ×ÊèîÚÞºÈ 0­2O!ÐrÇ!øËr¦ÿá»|´1sJXôê“ÅíiŒ2†öÊRÙ)w«óI'S…lyúœ¤šüÚÂÂfé¸ðyäow¶LCóØbš1tÆWªøû ¨qíÀÞÍ#ÃK8r T6=I”e™äûòÎ ùÊ2ðQ%Ÿ3ÈyFž[šgµ©jÜ­ED †[ÆÞ¯:Ä£˜ Èðvtmd#_H,{ºdfreÉñr­°ü s»ÍG¼Õþ«5³dÜD`S¼Èdpõ zâögÛälåd › Fë M5þøjÇ&ù›‘jƒ‰àí¤ý³yþæZ¨ÉðUd4ßÝ»`cçK›Ì¸¬(8úŽöŠ?,‘Á¾VúÐF±ëU öþ;0ÖîÅsÙ‹î-ñV ˆï·l²Ú)°8:$;e™´é 8‡~AÊ}DNᨮ&+?½+.fƒ>­”Ž[`BG†U5«KX¸øþ|có4“¦e®Õ:z9i³=U×åÚ(+óÖ»JÏ*ª Ë‰r`ôînWç÷aÄϯ{ôZ!~ÙÀÍå1:üùý­ÑYîvß-Þ8Œ'ø6‹[Ñ% q«ÞXvþ}¸)‡÷X_¦¦lÝH%ìY¹E"€•Ûì.‰÷aS¤•¥T¯HT˜ZÍn{Â< Õ?³ öâöa¢ãþq¥ÿ« êÕ?)bQº]*ål‘ß•ÙäÈÔg†/ÚòX~m!`=}bkòŽu¬¾„ßEÏ^”ŸüÆJ©2èAIpÎûe€ú=P“Ñ¿Ábà^Fê T €6ëÄhL·†‹U[2qê7 @PJóÕµCåâ{õy‹ñt&AöÅOMfÀOMZ1»0LA¤6ìóg/F‘BíñÂ;yx´ð#Œ©é³wûËüYܘìÞõðò1O¹nçwzùÇþÏ\g60¼Kƒ÷»JZ`ôuê>òüú*Å]Ó·>!¨“ ÷`¼Ëë°S=WÑŸ?Ewgî«ÑEÌÊYXõJqÚ9¾h¼¸†Çõ2!¦¾ž3§súXBOý#§ëŸý-û×=špû”@NMÂt.‚á@ÅKK4 ãÓ¤Õ^GM~í9ÔðÆÝjš¡¥²øEÑ"?Bs$0#A1_ŽÖOx±“Ç8n›®q«¸Wb_Jºä#ÂŽô{oÁ¡³'Þ4¥'YÖåpž°ÏödÀY f-hmœi™»ùÓW4E+¼nòÄoÕJ•8v‹ еà!^Ðý·â8OÇêˆÒ4t z/AÀÏÃr6ȨWбtÒ)+4£•Ön…™²dÈÄÉó\¬rÂ!^ˆ,«D†naƒ¬k™ƒÉ𒽜f@ö²†Ã1è}”­º]=a¡mج»PÖÐÖÝ7˜Jðûÿ”8Þ‰ ðp'˜QX ö=Ë̺°û¥ey™Žs`cê(8@Ø–p,L\zšÐŠO“DñCcr(ªlâÍvѵƒ¸´Ãɼm£+5ÉSU‡5i¹Û÷CCgÚ§åÿ‘êÐzà–Ò-ŸÏ•±(Ð&'©BûR@™¹Ž(s&PÏNRêå`ð•ò±ß„ÄS­’TW!Ÿœlïçñz¥7=ÑMOÿA†g~Œ%‘5Žû<€k¦)?ü3ìv¯ö‚¬4wÓ‡ûo¯e߃üÁ\sëAÓl[ºÏÛ¾Ž)ÅbAÐé…„¥µÔÇ“ôÜ TÅóò»iµ€¤GÜ~&+NÄ:¸ŠmU‰z"ŒrÔ„÷„e³=ÏúH¾{ž5Xmˆª²·3%uÈnþâl³…¤­kžxo=šÖöÚÚɟʶ^ˆcJ¢çW\Eµ6ÄcJHþc»æ2+'°y=ðœñ“°ÂNy—r÷šçÐÉ]\ý9R»¹Z,” ccÄwpÙA㮌 ºd „³CM7ÞN‘ª2ºðဲIb‡ÆAÃTA•À¥¬Íù¥†o0ÝYJ”µÂ÷ùžÊ=Ê%ö®p2ˆ‰Î®¸\“øP /öÊÜn)y…sO—jh¹¬n{#‹!ËW‰5ÙiÚ–2¾"Œ†?Fcá!ߨã†$µÅ¬=¹À¬Êv8°:ˆƒÂ ÓèÈÞ¡ ø©P„<‚ Väbwbm‘¹Àû(H î{ÔËåiÜ[œ‰×%'xéÅð·âtÄ«£Þ&Ü"VzC‡hýAS{¾(=*+Ù8eIl ÇGº¼¹k#–%åªékËÃ~@M"¥xsçÝP³.Ýöì5…w »#þ7¡l°(¦ruûi˜‰I#†R³|·.pûÚÀ# :¤9>Zº–rÝžôNÖ~ÄN5À 3ö¹j¥’qôíŠ6úA`CoQ[1ø_EÒü³ÏÕ¼²üØë—1{ ^Žé˜-£ùôK;‘dÁµXa ¯³îé®HÉÕ^¿AØ6ìEÔ›µ‡8llH¹ÕÔ6PÍg4u®²ÑЇr@yf.–kÏ‹°Å 36"¸œªB6fK’¡¡C4W™ß©áK&J×ëgÍæäº#ƒ>Ìt=ffF*®Lõ× hû߈¦¾v²íD”Cþ¨Ï–îíu:y˜WöÃÝc‚Ù%Ÿ±vLsL9Bð–}øþà¡ 9;ƒÙ­¸†sP|K@¡Â75*¯˜ÉÒÄãíÑÙÑ@(cRe™ÂN îšãŽa„‡á‡(–Ñ?^·‰ø gí@SN![´ÚåGÿ1ºßI…*G‡ópï¿íR¾Ž/þ¾D¥4CyZß=¤püºïŒ7ƒŒ úø ÜŽðB.eR*ÃÈÏ8[¸e¨‚Þ;ô–6Ïg¼ Ñî\Ëb˜žô‚U³þíݶAÜáªÆfœ€Õõ¸~@·å¹7$¤EH|ð rÎøØ¤LÊLÎ!Qc>·âm™îq>xß,÷^rk†”Zç <²É =xú­XÈ+I­9ÿP"pM‚EÔ³Ž¸aðqcòµè”‚ É£&ÖSLï¾#ß Ú»@¶MÅFˆ\{‡)ÚY¼\ÏŠÉbð§Áujéx{ï„xÍç Ø#ÐëåÕS­Ù‘;ï—5Ìôït¡Êž—Y¬eMÄ#9²öí¤á±EI¤9CU*·ÑxH«;ÛA(â/l³ %~OKsf’c…z± F¸Ê¾íÍgcÙ¥³dgÍÉŠ˜"¼ uÎ#ÑL´ÝBqÇ bln·j{¼&/ ½‰´ƒÀ%ù›½“ô˜Ø ‚±Im‹ÉÉ«‡³xîax5T¿Ó´„ã4,Oþ¶w”´chsÔ‚” NëÚ5NEÉ·I¥ÏŸË)2/_{üi4b(6€ÖÎ öçÎû ,—ú¼¼Ê1D(ö‡‘¬רó!—ÃöÍØp+0}Èй~wƒzD=bLõOîâöXO'7J‘N—|CVÃ"9³(/Kª~ù$ SP êÌÔÏ OOîŽWþNƒ"KñSf‰ÃÉ<jfyê*Œ)dÏ’Ï>¤Otf[><‚¼yhñ¾ÿVX)9Þ J`ñ c™yü!`óY£ü†cÖëÍ CšŒ®0Ç„EKv¼¹`~Æ£ëÔð>WΙ/\y(Ägg5m¹L/ SrmÉÉ3ª©Sƒ39ö\¹Ap³»ËÅǨÏγÿ¼Ã÷˜b—Á‘…m§OxnÕ0ík<:ß²d`°j=o,vfÄ•¸øÆùh¢pt|1ÅÛåÜ…È”ªÎ>›8\¯Ëátèmi͈!~¸ÊœWm:•qÓ›Hg0÷ é›rA€è+ðH£C‹æ•O©{s›Tƒ¬j U7l–ÌZ›»®í^Wá¾únr=Ùa”f\´Vñßà1›*šõ@Ïóûv50Û¹[u»L3>±ì d>mCÁÐ$H×gôù,¡òWAätózêkð.Ã<œ]‰(nqŒDObŽ©†}au)›™Žê:zC¢ïºÙRu§Óþ}ÜÒ¯"- ºÒFdmÊ”ôêw²®»Ì¹Cl'–f4†éG§uõ[—£we¿ ¥H&†ˆ¡–ÁoØEP1½ç7?£3£áÔi–obôëMÞGvkË.À9»‹½0´§_!¿…¿¥6X©ÌQÖÛ’šR_hÚd©/n,x\âO¶—Ñ0g,y!3ŒµS§5XþvíˆSL8‚劖(¿“Ql]c"É9ѽ î‰+v².K®„ªL)Ÿ¢¬®YÅa'Ÿž9í¼¡Ç/­]›q¥=Édd  ¶eû5/¡×ì±On®+ uÃÁâW:ŽÂ6•i ò{÷›V:]U† ³§ÃÞÔ¸òê(Õ5ú±÷ÅFAþý†dkÍ8úz†U‘;LwùšHÝ^œˆŒ¸pʳ@ÒÝ|Ót¹T“?œt†'÷쟪¼çî®b¶k³§â=I3@Ù›ZAú|êm­Mw)‡sŠÄ ›B<^9)aŠ}>pížoµ§Ãn9(@ƒ)Ŷu«”v5WòeWhQîD´ü r×îÿpŒwœ£O“2É„$väG£‹×Ľ-)Sw¹W[ ÒF÷a]æ$CtTéÊð¶>‘;GÙéq-ê,æðžôP¡Ä>Ó:U"ÑЙ†Êeä=*•7ððJ²'}7rãö7{‘ñÀïëO NTªÝgÑ]üš_(uîEìuüÊ\tP‚¬ÌLµX#Ñ“ùÕc[úg'úª#&ál{W’b?+`A¼Êo’³Øêÿ­ìI—¸¡:ZßÙ¡Š-]5­Ùäj†ÎÈ¡?O$§"ÿ|%ÿá gh]ùÏ'×µS6'ÒL뢂´¼ˆ4†n­ÇYu¥²5T%‰Ž>t)ZeÌðt”2Km€}GZ“vTײh0ÙæÞ‰eŒ›¹bŠêbg›ï⊮`@IÚ÷V«påߌàÎVÐ07¾œ7U+®ÄUWDíp{œž D2N`‹Ã¬š¬”ûöŒ SÂáõ F1U975 5+¸o=ºk³Gßêlž‹+Q&‹¾ñ&ø~ÎNË)ëÙ¬tñ¬0l²ìòW‰Çê‰VÔFvJã&)ïy&‰§Û1–O’xÄø›;ѳÅôà÷`Þb‘Æ&ƒ¢ò;7œ—ÊÆßgtÍg»#0IùÚqÚê}²a|ó>Ï4ŽuàÚÀdiUˆr\Áhæj¬¾%½×Þš2c¯Ø²O·g™õx^³òÀ±~ }|B5•û5sH‹^ò¨ ÔÖwš‡w»ÈA•ÑBåQS8=Ë!m*"»ûß ’Î/I%c¼ÚQYÔ©”H¯ÖþN÷¼RõûNñããz›9n£)9öÎýJs>ýüi«t¿!w>\XŠQalcK§>\ïOxÕÞVïÙ“©­üÆÅ„ß ˆá,‘m2ØõŠùf£ˆçºÌÊ­tmqQ§o!÷*„×IódõRÛʧ=tòÀËg݉¦0ak·­wËÃûËa=PŠF ¿(r <R›[PLnÛc 3'!\}Á>£{”;š!¡ìCœO"Ž«`ò„1h9¥>:À¾Ú2°UÒ[¯‚»|êã´÷exжËoÖî·gÊ GeÛ_¨1J Ô=$7î&.u‚;4šB–‹xßž³vì«w‘,;ÌW$ó{®I¯Äq "Þ€UG‘;h¾9 -,#v¦YÂ}~ç@áÚ„ƒ†ÇÛt]s~  $:–tc&Û8V—_;"Qgï8úÿͶll$ "^'%F¦œ•|KwiÂíqi Ô$¶XÍQBôðUmÖf:k‘¥öQËe;½ +‘D6RÝq*u]øÓyd " Z'YÏ„¢´q«ˆú(œrßßÿЈ$DOj{íš¿„Ší /o»s•(ÿ Fàì'!x7©þÖ]/‹ìÂ8¸4U¤Õ’Aå£ÃFRïë°Ðlø‡]¼V;{=VÝÂ¯Ðø&%{m•‹‰Âý2Ÿ9íô˜å`«Ër«Y¢‚FàÆÀg ZÃ30` ’=©ü}¬´Emº!ó,Ãø÷2¤’±×Äà pÞ4Ž ßæ`ŒZÂ|êÁT)ÌøèÏÐÅT%.í”à´M§€ Y`C•§î6#ßêtðL^ÏöV XÝÕžEŒ¢ÀOà«iq^a;*yû ­rëÄ%$H$€&¿Úl-˜e…ÝBÔàçJkÒ;§>8é>K;Vfô¹î2\å[Kß¼5<È`4Pñ<Ũ£LöêËZ ™_3WBŸz%”ªb¦€¿vûÂÃÈ’ãïܯ†1±õ1H®5´<&I2øÚ£öëÆÿÂüÄá?5Y!‚AK^Å¥dKêׯ¬Ÿ4ý¤ðóÕ*i‘_ø7úÁ1ü C²ù$ˆ­Œ-epÚùì^Lõ/*ç'WÖˆøt1 ¡;*O®Õên-š E³ë?§)Â)õ/ qöMµÓûË%u˜`gãëp¶ %/l²¬WiXïtÁ ·ƒaï>šU;•Ná BýwÆuìÒ‰q5TÞ¨Aåå€ï⻯.F¢ÿ‚L%D8æ¢äÏšìü…JaüÞ(å‡@#rzZôú~KÚþŽêÌ n¤Ôy%¹QëE ¿>‹«ƒè¥EY+8ÓG ©&½n`qT`N]¯–µaê6¹k7YO}{S-A6¦¾ö/^JF™É¤¼{æx`OØ“ŠIÈoÌÒ™g+·]Må æjÄ`™‹RÑ~$Ì,ÅR=iŠ©†L§bßõˆ=ú{Úzi0ü˜V±zçe½Ÿ„vÇ/ãÑ€ýGß|‚ï½¥~;[ “*·[ØRkÍx}…ô¥üÐp…âi5HeÃïcéw|íX{Õãõe(«ö€íÒ"ïó£îµÞæ1NÙIYD˜ªf‰FÎpH†³µ›î¼xJ«Þ+Ò)˜N̨/€1ˆîJ@ƒZªJÊõE0’T¨5Z­ÊçØ ÏÍÒó¦{ël!¿$Úø\ŒNÍ0à"zzÝ× §·{‹Í’˜Ç„vîL¸¤ûÚäCs(ûKà·š`³½PçŸñŠVjCwyŸNfãvsóh7# š=Š™,1¾Àž´µt ÇùÀb;μ_n¯[«{fj Ð×ÿSX‰6µÙÞÜË”UB$§Ïþ¢znîqÈòOº\²Z°ì¯Ptz8`÷u2›)‰’òyWÌk!¹íg6ñÓ:èÿJ3s’‘“Å>ÀÏž‘ðF}åÿðIå2y¯s½[#Š\+ãæ½cý»tù ¡Vú¨Ý錗êÚ0 JÖ…ß ‡”£¾z½e·³'ʸ^Šë°ÈŸ÷¸lƱ…{Êï~º( s*ŽJüª÷ÀÇQ°¶/*oqâz[C æÏžL9ˆS'S´‘ üÚÌÏ•ŽÎNl?Nt@ê¡àY©AB¤'P`8` 6ÆÓ‘†|äL œÞ˘d”ýÞª3ò·Kâ”Ñór…»þ(ãÔê„5 é0ÉÒÃ~¥ÿþ[½<e0ö¡‚êîvšïvLíäíÅóŒÌ¦dÃ$.{lu„¤ž¤›ÍÒõã »6Wóúõ¾¬I<Ÿ=K¾mÉcçaú_Ìÿ2Ÿéÿ`ú¡ÿymþg[êøœaþoºa‰endstream endobj 31 0 obj<>endobj 32 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 250 333 713 500 549 833 778 439 333 333 500 549 250 549 250 278 500 500 500 500 500 500 500 500 500 500 278 278 549 549 549 444 549 722 667 722 612 611 763 603 722 333 631 722 686 889 722 722 768 741 556 592 611 690 439 768 645 795 611 333 863 333 658 500 500 631 549 549 494 439 521 411 603 329 603 549 549 576 521 549 549 521 549 603 439 576 713 686 493 686 494 480 200 480 549 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 762 620 247 549 167 713 500 753 753 753 753 1042 987 603 987 603 400 549 411 549 549 713 494 460 549 549 549 549 1000 603 1000 658 823 686 795 987 768 768 823 768 768 713 713 713 713 713 713 713 768 713 790 790 890 823 549 250 713 603 603 1042 987 603 987 603 494 329 790 790 786 713 384 384 384 384 384 384 494 494 494 494 600 329 274 686 686 686 384 384 384 384 384 384 494 494 494 600]endobj 33 0 obj<>endobj 34 0 obj<>endobj 35 0 obj<>endobj 36 0 obj<>endobj 37 0 obj<>endobj 38 0 obj<>endobj 39 0 obj<>endobj 40 0 obj<>endobj 41 0 obj<>endobj 42 0 obj<>endobj 43 0 obj<>stream xì½etUYº°ûûŽÛßéÓe¸{Ü]I!BÜ}gÇ îÁ’ ‚W…»"↻kyuUºÏ\+ ”œÓÝßøî¹ÍḇÆÎÚKæz×f>óÕi—o¨4EŠ (è®0ŠÍ7Ž+0‰Ï7M(0KÜhžTh‘\h9q“í¤-4»É›í§lq˜¶ÍiÆŽ13¶¹Ýeæ·Ù_¸ÏÙ鑲ÛkÞ.ïù»}~é·hOà’}ÁË„,ߺâ@äÊCªÕ‡bRĤJH;’´öXòúã“2ONÉ<15ëäŒÜ33óÎÎ)8›Rxnþæâ…[K—n/]¶£låçe«w–§ï¾”±§bÝÞŠ û+³Væ®.8V³ñøÕÍ'®n9Y»íLÝŽ¢†]çš¾,nÙWÚràBÛ¡‹m‡/µ»tíDÅÓ•7ÎVß:õVqíí uw.6Ü«hºWÙò ªõauÛƒÚëkoW/>_jõâÅt.™Æä˜Ç "!%!¢¸,C榱› c·tÈJÏÅåS8KnÒså+K§t öq~ûîÞíïFdFÿã-׋C¼[ÙÄn‘ú&ú/ú#ýNäí»ës_q‹|åÎcòb6Ѥ+wlyöÎÖù\R·åûÊ¢V¶Š>d ðAáfwã¦4þ›ÑyºÑÙ4=uÜ„p4Çåñfx‡›±y4Àj“)á}nÊŒûnM¹u@³Còÿ ™VÒ˜ù/s³ 4|èd¢LÃþIg:”ÄŽ>p€Ô:ŽšO±‰îÉ×”);è*d¤‰¯:ä#Ѱbæ8 ËÜ´èšZH„”x€–®&uRF-´è”ï¨']VþVK0÷ŸØþ+\ãâSWpS ³‹›B°1¢KÜ”o-Ãú_¹¾Ô?䦸 `±ÂMI²4”í-…›ÝÍN+Ã÷ÇC‰‰Œç&I[Œ6ÆÒŒ6š$Å JÄ¢oæJ­SëŒÏ“®Ó¡JÃoqd•DþÙÿN1y÷SdbÊÛNnvQC|+©~ðå¾Éaï-òHûþöýoå+¿¿çŸ¥nÈè츣ÄMð'ÁQÈJÖs;¸)ÁŽë€6¾bj" CÙÓÙátÒ«?ŠHbS7…Êù[nr|'7E—d5–’îöOn#Ÿ?>ïïöˆ»ˆ©‚0ˆù€Äe1[íV¹²¬~w…ü§L[i+¿‘÷¶’äãå 2”?ükð;ÐÁe岊þ#FEßì^ú¦ÌM¡Ôsœv$M uR?¶¦+¶|.¶Y5¯/OEÑ4õE_Ѥñ\œ. §B ûAUк¸ùî+¿ï žïl•R—DÇ:{õžAO ª³Ãï„òή­tÓÎ{IÏõ›=ï(ïïìLÇSt±o;Éø{Ó1ˆá0¡I*'6XTKIC ’›¬uvÝZ¾¬¬Ö¡lÊvK±ó=nJ·û3nv`¥K’ÿðCçãÿáa»úÓõAîƒn‚Ü!ù¹Äïl˜—e¢a7–ø®ßu#ùÃ;iK—”št î"7ðÿʹŠþ $À€¦p³[q³k<jEGÔðoâÁ”ôM†}ÁÐøÍ& ›ÍãòÌã³ÌãsŒÅ.ôM˜Z1()zJ§Þ$ ¼ôÞ“ÞÃ.X¼‡˜éŸüÚߍ&.ÎEºšP‘Þor»ŽüÃïz+]Y¾x4±T c5·–ïÛ‰ÅwÚ¨…:ËBƒžÈ|+‹EH¦ƒ‰rWá)æÐí˜C¦IŒd$—¦(•2Œdô ë¨äXdg‡¾)È‚õŸlmxÒwâúãWb“ ßK¦˜¯ø,Ã÷›02ËM‚ÚûÿŸ?ËÓªN>vRºÚŸ+‹ý£óR¬Ò |PP¸ÙÝì´Ò¸ôN›€’91×4.Ï$>Ÿ&š1yúÑ9zÑyFêl“¨4Ó¨TSUº±zAüz6k'l¡½¯cÊh{Üëäæ»q[¦I4ù𻑼ë‡|5¹o’ETè}üÉ¿?åý¡û½1–ßž|07ýÝ)ÒŸÀêºcçhî%>™ÆŠ¨þì¸T§U“+ð¤ìš–ÑpÔ Ã2ÄÔN uÀ¨ãš¢‡"È(f GvceƉnÐ8’wùÙóÿ7ó%nÝô[n¢ýNIì„&èü¹)Tþü÷pÙq‹÷.ØEäŽïÿ~:߈ÂME–nv7nÊ㣓¬xŠ Y)2Ö4zIÔZ£ˆ4¶D³XÆf¢I™«ÖŽŽÏpˆKµQ¯6N5‹^KDð{&m–7ø|„@ J£_þÈÊnvjd’¡øw&Y‰\ïBRåÀÔ.~ýò¹޲îɯN0H²E³ó¦;ïÈN!‡÷ + Ñ¢Û]Ä4]g#Úû±O2ÙŠ{ʼn[XDçZFc_}Ÿ›24¹šp€J’¡ÿ9æjù`ôS±Sb±Ä¹'ý¯ç,º-…éþ:2zþû­¸ÚŸÎþ|gÇ]~O7Yï ü]‚z¢ò>#dn!K[¾”üÛè$ò»ëËàVX©H@‘€ÂÍîÆMÉ™%TyT –1ÖêtÛ˜4›¨Õ¶ªUã’ÖûÏÞ±xWäâ=a ¿P-ÚšRà=-Ë9Qcâ¹A Iî9 š\ÄFj­Nå‚2‘ßC§ä>ëôýýš27»èÆŸ2ˆ¾Õëºr#¾’y÷þ9nJÐ|O÷ünÒsLˆÉãÈba :Ù)ßW⦧Ô7kO3U R|+4Mž7×0~ƒI\†El:2±‹N·WÑ2hv*¤ÝyMÉu(3¥“ø‚›L;ˆ¼d®ýí8ëÏùøßïtÇÀuÎ(ºþÈ“ø— )½Yñö¥»K7?¼ß´®[Èó+eÌT$ H@áfwãæ(ì„SvhF 8°MÈ4Xá2aý¼‚¢Íg¯ŸiüêdÝëâ¶ïOÔ¾b[ÔüuQËÛ“õ/J®}{²áõÞʧ©ûâ3NE¥êGe˜$ä™&æëG®5S­ùâÊË3í?kþîØ­_m'šÅeëG­7KÜHN¨œûi¸ -ÿ©®:O'šAx#Ứ®rn‹Ø³Ñ a›QÒ6[ý¨ æêõ®S7mÿõhË¿þëçUßXÅ®3ŠL'¥T ¼1"ÛTŽòåR\V3”o2KÚj¿ ;³±:ÏDc+þŒ/|7nÇå-lš$ÒLD^ªšhá<ýp2Ùš&æZÆ®W¥;ÐôÓ¡–ŸŽ]ûuåþ&Ó¨Õ¦jíl)2ª8Æó ›é'€3ŽÌµ‰ßnËŸ› ¶èĽ)n½Ilš©jYÚÁÚ£uo.ÞúåLý×ç¾-kûåXÃKvT[ªR ÃWªÖÄdé¨6˜$mÒŽÌ6Ÿ°Í@MsiB¡Ž*K—¾áP–<§hñw3Ç~Îg¨‹‰[ b !O-ÜÓê|þd+Èñê|q<žVŽ'¾+~“Q\!Mþ ˆSÀVþ“Ϧ«ÊeËNN”ßïÅ,y›Ø—O9Ì8®P?:ÏbÂ6šniJ…æI[5B׳¥óò–L6ó-·3°G¹r&úF¢oÇ,q“pH½◠fü¸»2l*øÀ% p³»qS/yLjh‘ª# #×ØÆ¦OZloÅ“#UOW>,ªy|©íMIý󲯗åͯÏ×>-m~uùú×Ûßž¬yxªîÙù?ªÿfþŽ “¨TÝðUB‹X=&qíþêççš_Ÿª½§î{4È4íÐ5ª ”MRfÉ[´¢²„E ®”*£µ¶âT…k ›Z1…šª<gË„«˜õKöoþédÛOk¿]{¸Õ0h‘Iä­•ºéFê,Î2MÚhœH⌈ï5Ÿø9C4ƒ0ƒ<¸4-Àh ÊÔÊb¬4FñbðŽ I;*AŒðžä’¸j Zg¡N›³©ôXË´“í?OÏ+2 ]ÌêÖéGg1 Þ¸‹›0Î$šI‚p\ŠÈM:ÂFƒ¦i»Êg憓 /O]}„HÏ^¹yÖ<+nù!÷x»eÄR³ˆƪTãØõzêL²~,'î0ñ*:Ÿd˜˜$Ð[G† [˜Â5@i‘,ÒÉßòÙ4q‹Œ¿?=žsÙÏÕºš|A覧bR _™‹‹ @ ,Ûb’´M±æöË 8êDfËJ±vDZMÜa™¼ÏìçÄä[ž…ËrŠ®º@[Í ݆Ñj*idó€ž\‰×zÑoƒË­Æ‰[n~àÈP (ÜìnÜÔIܬ›g2a£‘zƒIäªi¹g?¿p¯òÁ¯ZßT]ÿª¼ñIuû‹ó•w.Ô><_s¯òÚÛâúÇç뮼UÞü´¤áIIËË’öï7|=:>}”Ïœž‘ËCææŸiz}¡ùùù†ç¹§®ë.Ö ^¦ºR7|NxÆOÔ•JÜ‘hñ¹fñy‰Ð0:‹ß0q«^ÂV­è ªVñ¹¶êµ3²Ï«[rí§¢öŸ–l-7ðK1]nŽögƒB''ejEn@?‚ËŒð\Ê,n£eâfKôÊîÒì$¬Çx<¥-0P£øHÅ‘È"‰Ï“Й­T+³Ž7¡;ŸjýætÛ÷s üç…,Ñ[£¹N Xäà˰áÑ c7ë« b63ìC~óf Y±©6ª%+¿(.¿öº¼ùqyý½š–Ç—ëî^n|ZÖòö`å#¿™Æ! õB—D¥êE "Ãw´6ËÄ­6É;P–願NˆNDo4ßJÓWèFæÑ´Â²u"rGo`§EÒCu!̶q°8^‹ÆJ“f&âÊZáëùÌþÎÿpM4bÝÍã ™cpG¾•O×WoÔUåkEäèDì|`?z%(²œÅEäë 9Êäƒ9ÆÔü¸y¸¯ 8™Mj”ÙÞ‹¸ˆ0,^ AÈÌvˆÙ¦1…ºÑ¨Øb«pS‡"Îÿ§ù¦ hæI…ÉL­7ÙNÚB³›¼Ù~ʇiÛœfì3cÛØ™Û]fîp›ý…ûœ)»½æíòž¿Ûwá—~‹ö.Ù¼ì@Èòý¡+D®<¤Z}(&õHLê¡„´#Ik%¯?>)óä”ÌS³NÎÈ=33ï윂³)…çæo.^¸µtéöÒe;ÊV~^¶zgyúîK{*Öí­Ø°¿2û`eîáê‚c5_Ý|âê–“µÛÎÔí(jØu®éËâ–}¥-.´ºØvøRû±K×NTÜ8]yãlõ­óWo×Þ¾PwçbÃ½Š¦{•-ªZV·=¨½þ¸öÆÃÆ››o?m½û¼ýþËë^Þxøª»qSCc6yØÂÖê9kóÁº·§›„.y¦òneû‹²Ú{§.¶+mÚsº:kÇ©íG/~¢ú ­½_uíeéÕÛPàBû›cµ/ÆOZ7Üe¢iØb‹Ð…SÒ¾,m}u¹éñ…¦'ˆZÓk¦¦wŠNàíÀ¥:a« %ÒŽÌ) 0xÒŒ¢×S¬Od¸àUg¡ %nÇÔÉðKl’½zͺýµçš¾>Ûøú|ó7Q)…šn“4ÇO×ôž£°@7x¹~dWÆPÌÊlÂf¡=€Ä-B-"J':×H•e-’+ENL&L¤qk¹Db…¥IØ{ш1ÒJè܈úl§Z¾¿êñÙ–·E­_!»°]Ï):¾sx­Ð4ÈLÉì (¡hä•À ‰ÊWâži¡Zá?;ëØÕ‡çjï—×ßåGRt©¡¤¢¹¦ýiYÃóÒ–¯M¼&éùLÓò­º\_•Ž|,&lAM³JÚFç1S ³°j¯ ®¡Ó‘ºt ¢Ÿ8s([>[$lá[ýH¦ÑUat¼¨ó¹ ¢xÆ|«¤-@Mð1ÕÖ }\PR•ƒ>n¢ÎFJÈŠÙ‹ñTñ›!’DE«dh”2A¡8=VhÄ\ËJÝÆ¶Œa¶-;ùŠ2—Ù‰ñÙ8Qشʼnâ^âv4éÖf:¾IÂvãømBŒŠ¶Ó¹¬àフ€ÂÍîÆM ¤L` ¢Ò­b×.þâʹk?5½*m~Y}ómÚÆAñ)zö>šVžÃLÝ»·òé¥;v€¡«­§zï©+õ7_ž¯ºÎ¢´ý­Ç„5ƒÔzÞ3 }g,)»ÖaKw]¯¼õãéšGůœBSôÇOævÚ^3tRLÂV xÂ\‚”¦W٠Ȩ+ªÊ¡¡lZÄçã5#È'0Slrñ½BíØó1è”jó".è’´æló«ÓõO‹[_¨¸«ç«åš8ÒeÒHïy£WèF¬^9tºNnòU“&Ðä+« …æêµVªi{+JÚÞ”5=9Ws£äêõ‹3ÊÄt«´îaåoCgrŽá>E;hïMV?2‹>ë…oÐ ]‡¦Œ¥DZˆ^VeB4žÅ<¡ÀW&ÖiÉþ,“£–¡šÎ~ˤBýˆõì‘ øÇãÍxïÒ¹\s¹šuÒ&ë¤BÓè H_HFÔŒ‹Í—µTÈõœÈƒ‹w—þr˜ ccgîÁË’›vÄ:¾êR“9‹¤9‰T¹‘¯Ä@Xx ÈÙ&!‡ûBOƒHæhtRô\…›,,”ï’€ÂÍîÆM¬£¨]¦ª4ç‰Ù‡ê¿:Ûòuiëëò–[_´pдönéÙÏÐ¥·ËkÿuÆ6ÐÌs Á˜é˲Š*ZPªo¼8ßødtØÜ¶á£\’ô='å½ZÚø´¼îNYýññKŽ`¯á:i”ç,퀅z!+ ÂS±¬ÊzŸ‰*\ìÓ ^&öċРZ¸öðK2>Çd8Ç®:xùþ•ë_Ÿ«y€³ÕÔ-aÄèÈþVán“t}f/4 _Nn©Qt†ÈInSBeDìŠ:­Š†%aÙ\A@ä`šLOz‚Pè䡞Ÿ+ / š‡Ï/@w>uõ!é‚C—GØpŠì;Ü}æHßÅÚaé’>%sSÀ…s O2°Y”öUeeBŽýæl:Vÿú|Ë›ò–g•ן/ËùÜÒ=¤êæóÒ†{hô—š_„MKío8xLüpÏ™zaxl3“õ„­è_@Ó>y3åá‹U’Äš¤|¹ÃhÍÌPÕaý—ã¯øÌàÖЖ:V‰…pÓ0 ]2DBRÀ UÙ)È›ÇùÝ4ä#½ ñRŒ£Ö ;y²RÄeŠåL˜öpk—\F'|Ć ”=ü‰H™˜Ñ,“7 Œ&ä™$¡”c³Î8j­üê-b2ø2;"Fšþ}ôV³Ä-8@»†åƒ"V 7»7‰Sµˆ‰'±«ö—Üü…pÙ²¶7ǯܟ»n¨‰ÛP3Ï¡V¾àòCOMü>1öýØÀãcmçþ†Îs×\i¾©öÖ•<ž4"ûZ¢rxM>\õ¨¬õ†ë’º;ÚNý-CXG wNÂ`«ã‡Mu™ubzŸ¤×d3ZÒˆãµMX{Ô>)›ÔQˆ#‚*E ŠðxÒ½ˆy…ç_]hx\Rsg_Qã@#÷Þ†^âŽÒ?Ñ,t‘uô* (­1™BåIeu JÂGBßÌ·aôŽÉ°_oGÀÏZˆf·Á"n=´…5ÜH牚àÏ2~Ãô ˜ç_ž­{XÚò|Qöæ ƒ­ƒØE u™2Â{¾N(ÔÆRšl ÅÀ«#{ ðá ïj]bÖŠÝ5§[¾+iûêÌÕGgjïÙùűp.jyx¡íiyýƒÒšûs×îüLß­Ÿ]ä—dM Ú¡kPôp2j‡¤Û&n2ÊÔ ^ã4y“nè ÝUvòa(бN`&§Õ>¹•Æ·6‰¹ìäƒÃ¤B­Àå ÐnÂ&K<ÈQ¬ ˜9臭Ħр8Ï ]^M2)_Ñ ³uÜz˘Tòe,ã3¬®Žßàô" ô%Ÿ(‹¨iÌÈ”¼°˜‡p°ã[‘(Ÿ!´u¹òC"Ú.!ÐëùSÞB^4hLÍØŸµ#ÓyR–¯;V$æp;qGušeÜZ«8v®3ˆâ° 4VaV¬”Š>x (ÜìnÜ´To°‹Û`±2u_íé†7§jž7>/o{3ÒÊw©GO׿iMÏ!cbÿ¢ç=Ð1¶§eˆÆ¸ØAæ^FcB½£¦öR»«fYú$¬ïþ™±oë`# ç[^]hÆswÿï@3ï~½MØ„i¸N4 Z0.y}BƱô£×¶^zI~ÇÑ–Ÿµý|¼õï_V¿È;}-¥°ØwÖƒ0‚o3q„¡6b™sQ 6_ºöõņ‡—ïì:3ÄØu\øœ´]å»/Ý;Þðæüõ5~•y¼mBÆñ1“óßÕU­gÈÅZ‹Q/<Ã:>;|ÙáÛª ŠîîºòêpÓß¶ÿ×áÖŸ÷5|¿¹üéº×£V7m“\I¨NÑÂËÉ0¾|ëÙ‹­Ï‹ë攼8¿¡k?3¿~6ƒ“Gx¥è„¥â¤ãWÝÑDDn®fÄZ¦ÀBp*"ÝsÚÆ#ß©yVví«ÓµÖl;<ÂfüÇÚV»JšÎÔß¿Òö¼¸úö†ÏÏôÐ×ß.tà˜D€EÚa©è‰†ë&¬Úßžsú~æ‰[¹ç¸LÎõœ¹qéÞÚ/ªÞ¹ö_GÚ~Y¸³fLri,–1é†aËí“2—hÉ:{oýé›ÙgïŒMÞà9sÓ²/¿¨üæhÛ¯GZ]¸³Þ19ŸúfÑëhÆX­£2.mù69õ°YÄ´,~X ­UiK7ªT×þ¬ºéî¬E«ó¿¬¸öÕ™º'0úòÍoO^¹-,Ã-/02§}y™TBzDxOÂF­pQ²`þ–²cÕOŠ›_¯y\qÿ׃W_nx{ªõ»â›?¾rKìéúç[Î_wœ°Žø(‰¹bq4j|^ÔR|õþ•ö7% ÏÜ£Súšxõ2ñíe>dÜäQ>óÑõÈU–I²iDµÞ<ȵŽs¾Ð OC°vê,Ûð´5;kε|‹sóTíÝ ×ž˜z„ôÑ·í¡e›³§¬¸þ)‚b‚q¨´ñS ÛÞÆÞ˜jGù/Ô Y L=C«°e×,i~SÜôzWéik÷žtªé.W¶N5¾Ùzî¦uÈ|ÛÈ¥º^3‰b:{ã—Í_i~³ÿò)«·•´¼*jxv¾éB&ýöDãÛMç-6 ^l¹šÊªe{ŽÔ}s²þ«’Öޜ®Y|íÛCWŸnûöDÓ¢Î6¾âkvˆ^}¤öí¹k?ŸhùùHó/ ¾4Yi©J÷š^°õÜõ’ß©yrºõmѵoh§›^Ÿn~Y~ëûC•÷K®ÿP|ë§uGyƒdÃ_a –"ǰ÷òc©y¾rÕ…MDäð*xý`±òoÿà 7»71ÍÙ'æ¸&gžlxCnfYãcÚ²œ}} \þª1¶—iP/‹°þ6jÏÈkʘ˜e”r:Pý¤¨åõ™ºÇ‡/´^lyr¥ýé…Æû—šŸ 5Ÿkx\~㻽WÚD,²m²Ð16õÐÕ7¼ú’æ¯x/ŵ_¼¹Hò³M‡«\À›\ÿ”„£+×_¯ÚzÆ6|¾aðRƒ°Uf«öV¿Æl÷91véQiZ!«ñub"6Y4=c?s0¾:Óômæ±VëÈUfáËMƒ¥~YÇÏ7>ºró[æ*»KÚòU¹²ýT-ÖðŠ›èãé<ÖïÙ†A HŒÂžMz‚NÑHgS¸ùÁ›.ÿíYùþ*ÜìnÜD›”±x'ƒgY Ù…OKëîGL^6ÐÈMø1-ƒz›÷µ5÷Ÿi8ÇÄgšUøbó~ÓF‡ÌÑ@G¹MýÔ*ºïè„!Ž1p3mÓa2PJ¯Þ½Øú:túÚÆp³¿¹¯¦CȲëÄŽ2ØV´}ð­Å9‡C&§ºDΞ¼2yIvþ¾s57_ nÖ>¼tóëˆå£¼è…¦ê-³ŠX¾çÂÝK-//ÕÞ)½ÒB¦Ï‘²†9é_Œ‹JñIZ9smúæ#—Éz ¯ê}¦®×ð§¶J_M¾ÉÚE;«NÔ¿ª¸ö†øÞ=E Α³u]â´\b=“-<ãówŸ=Wy½¤ö^qó ò•4ýæð_¤™j³ÖuR&y¬pó|̓3Õ>Õp`æ4úÙª Ö X,Fu©Ì ø‘ÇRÎtÒvmU©æx6£ÒVî¨,kÿéLí‹¢†§EÍ}Ô}MÆö3tî£ï¾ôú7à?#ËÐo¶Žç4²´Ž7~{®í»sÍo_¼5-ÕÌMmä¦Ö§Ò·0—ëptäBë¾Òf3µ}S´SQ*÷xkQÝSÞȹúg³6ÒóOÁÛ«´Ô t™£jÙ¶³-„d£Ýf·|—Öøi&Á œâÖµOŸÏ5<0Ê>l¤}Ä›PÆÏþf>=õ\.ßx[Ùüøb݃³µÏ,f rœ0ÔuºŽoŠ÷´œ“µÏ«o}ƒ¡µ¤ú†£Oì_‡š¦7î?u\z›² îoäV¸¿µ]l~1k=#9Z°nÐґ޳ё Ç'Ùû%ÛyÅ:‡é9G>Ð:DÛ9†tÔé‹7T·=¹Ôx_Dþ¬ÙÝtì@—ÉÃ}¬¸`âˆñ3Höщ9 7ßH•Ï”nv7nÚ&f™„-;Pûælót‡¢ê»Ž•÷Ѳê¡eZ4Ì!b”sLÆš“-ßžiþ³Û‰ú× ðy­¹‡ñDã×Xü }géŸà¿ˆ  ôM¶ÛOÕõ1?ÀÜ·‡¾‹Þ˜p<›è+©{\ÚðÂ5b®îØèÁÖ!ézô4öëgÌÞÃÀãBûۊƇŕ7ªo}×ß< ‡yøÀ1I#Ý&'§î¿xóÇ£ož­¼¹çLå0c7–þïáöŸyýEÛs Mä0ë û/ •ÔÜÂiã;á}/B˜4¡TBiü“†®Ñ¨½ Ç ±ò.órBÉõû®¬ßW9À‚ù‰?ó£U‰•BÑã,ÓqaäÞö2td@îLJÎÑÌÃ5û9þ ü_#ôÆÅI‘¹÷dC·DøžñËœ#ç¢oÁ¥éŽ?šë ‰Wß\´awcϾ6Ñ}ìã @ ž•‘VŒjzðåùúþVþýlC9ª¬Cg(¿~±íéåÖǧ«o¢ sûÔˆw¼³¤õtí£S5÷è^ú¶Ó1)B¦¹¨æ:…Lfî¥eã«k ëÒ×Ȉåö‘}l£û:$Žô[ø;nŠ,) •³sðT₺D¡|ø7”€ÂÍîÆMò朒ÖEÂ&Kê–ÔÜœ½*·¯¶í§:Ž=M<ÉŽÔòœ¬^µG®U8#ë(ÃïüLjÝÄãv¼¼]8ÝÊîhŸ¤ï1Eß-!.%l‘XÁ¥Öí<4Eª¾«•wâÙÚÇèC<\vCÃ6¸§¾GOC¯>æ!˜=)_€gà1IT¨¿WvõÖ骻åÔÃ8 u$nÓåÛKDy½º' w¿‹™¹ê 0ņü‘¶ËG†ÞŸ˜„ô³UAÀ]çš±gž¯ºQÙþÊÜ5šìΞ¦þì¢u<¦:©–,ß\´¯ì†b:|¬úAÉ­¿­{•y Ê3vÁÖýç/Ö^¯n~PÞøÈÐ=ö“ A±ZãâvžoCoºÔòèBÓ#{ÿ$´°~–½-C‡Œ›¨å¿ˆ`ZªÒ‰µ³)(G½ i)j­ð|°lJÙgj+¾öúòÍg—[€E— ‰ýu]ûêyõ1ðícâ«í3Ä1`GIÝ…–ÇÆ)¾w¨¸á3-´à>ö±VÒ\迪¨éEiããŠöç'¯ÜDŒ-üú{|¬éô‘ÖXÍ1ÑCí#˜ µúËð±p7‹µW&ÇhDÁ ,íÿ¡íF96vÈè(Òo9ë/#uœ£‡Y£î/ßtrǹ¶Òk?`¨/jý¦¸õ-f^”s6LY¾‰Ýpó5^Ñ3R{ŽÿÔ4¸·]Ì(×ÉÎÑ‹y•®ÞZ5·Zð*ýØ'­ÜB\4мÜú(mÓÁá6þPûSCáö¡KòBLⲨ’~ùæ7<Ý)"¢›¨cüŒ(¯ýÅÍËó]0Ì.„„¦ac9OBõÖ ^…¾i)Ç)Üìœ-üBy´?•€ÂÍnÇÍÈåA)›Ïµ}K #t XìŒAFN½ ]ú[õ° 3^@ :©~À<]¯é:ãb Æ„cÎp•­hCÏÐzÛ‡K°MJÆ.°…ñüÕ‡óÖïûLϵ¯‘ç` ßñªÆFÂ5 Ý~¬šÀÔ¨“FÞõ·U¬;Ô)6pfÞñÚgœKÕÖ¬ÏÏ‚ •d8ÂQMàëÙºç˜yÏTÝvŸJné§Úc?ÒuïiÜÏ>FÓ}2ê*åÏ]¥öŽðöÖÍM{yiKpM\½çâ}ì™Ôs dˆFåñº—gÛ¿¥Þ,‘6@áRÝ Õ×Ñû(kÐÛÔïo^ãâM½'Ä‚cOÃÎÅ wˆS<ÎS¼®úJÕn äºFq›)Nk·Jó?/ºþýéº[Å í—šo¬+Ü=ÊdÜgÃí†ú 4 àÙ5ÇFØ„MJÝu¼¸á>*žYîþ™Æè¡¶¡}íÔ^³CùÏÊl~|¾æNþÞb0÷ŸšÎýL½{¸c’¥:Šv?›¨ÖQý,°uûz%,'Jk3ÐÜvôòPs·Oµz™ù}dèO©Cf¢ÙDH‹¾9ÀÔGÇ1¯å¹Æç¬kƒ=C=!,§¨ååù¶¯Qu‰­Å@]\ue|\ðD éèÚCÆ& ;‚º¸›/7<À5Œ>>Ô›9’ÖXÕ¦ã5[ža-GñJ\€eh’À;Ø*À;vÚ+6 ì´,©ƒ»–Ò…¤ç`»€§tûLõ~N„©ä£wÃM´ì¿XáæŸŽ¥ÊÎG 7»7âRYãƒõ4EÆi ÷ÇøÇôÔ¶ûÌÀ¥—Uð@ç #|æÛIÂÅj´}fÃM·ÈÙøKkn6Ü"äîäUÛûZøc*Ô«ÊÛS‚ZÑú “,‘®ýMѼ†XúÅÏËÆ±…eý+óÒƒÑo€U0y.S{üŸéI«÷2zãeÈ]š½÷SMò{pºM9žälÈ 5qï£?î?F:|¬7ž ~¸¡¹KÜrÆä«7ßlÚWÜGËŽcPuAùÎÒÛG¯>/»öê ­Øxhòª­³6œºnfg,‡øï*nU5ßýüpÙ7Ôyd6Ä>ÌM½€èšòæ‡%õwwž®ìo4³s‹€þvÑ d<§”+5ë(FÄjY,1Éâ Tó³£ÌQЊ­çTÖBD͵’ªÚSeU[öî)߸§*wOUöþ+ùG+·ž­ÞU*ôMá?m¸‡ÖiíGÂN›ðQ"urÆ¢p±ñ˜´8óK$ÖËÈkö@Ë Nq 2¹Ã'qHl5Ü6|êÊí¤ß^nIõø™;»úO ½‰bì”4Ôe’hΨé4·²e@îþ‹%M/Ä{¿öj&E$ˆ§š¸jNIrE/ßý;âb*RGÅÊkZÖ^åÐßV˜O¹¯žkóŒêÿBÝ}çÐéÔm°šAzѹjáà>r¾Iߥ©vø>fƒ-ƒLœ#}Ô³WäØUTï›<ÊæÙÆç¢ÎF;)-åâä§ä®$pkؘ8~oZ¢z’O+‚i•<”wÖiEßüP$ p³»q“¤’E[ΓØNök»”^½”0‡  ^F à4R¾†EL4ƒW ó]4Ìm$š¸(§þÖ벪kT±m†ÍÈ ZŸî¿cå"¡C=h{e胑Ñ~„mМŒ] @“´‹™«60&Ée XD­£ž¹mØBcŸë\=Õð3/§ûÇ/€›Ãlƒ)¤`4ì«(#'+nŒ0÷`ì®é>0W÷Ý¿gQËã’Ö‡xN/6܇>ÌXܧö7·é6‘<¬Ó(éÞ0‰›“ÑÛ_° b­á6EË{Ž–WЦç\ò€FMÒ»¼à8""ôÅÍ[ßÏÄ £!ö·ãÅôž;Ê'ârŸ2jl‚kÌ"¢^±šÒö·’›£ï?Ò!r¸]qS¨´ë÷–còåý–\icŽ4ÜØå­1¤èö²Uc§%]4ïË tRb7rêbŠg¡t0e/5TC! ª¿¢¶øÅÍd8®Ÿ=õÉ9PÏUe3fÎz†n;Éëç¥7>±?Y©iŸƒ-ž…B»“æ¥÷ÔÁÓ€ñ-â¼Ã½˜4?S$ ïk>tl2‹¡è¯„›¦TÓâ‚XÀeÓ<:Ó>zõ¦S-tøRû“¢ÊƆۯ4Þ¨i½V[ÞH¶æë’¦×˜"qêkzT„3·ñ!„›Ô|ˆŸ“N 3商K`µ²ç¯cÒD˜3:`w[*ýŽMÔö™K¨°~ð ½ åúþ‹ ¼g›ùLÙt¤ær«0¶ÃM§É„uõ1÷écÆb4b· eò{Ô X¤ï3{nöQ ×hèôØÅ»_^i~^TqËÒU=Â*¨ièßô{›…{Ä­ö‹]š´((i1)'¼B û»cg‚iS2Î]½CøÓñÊÛÖ"4ÜI=l\2zýõM…› J>( (ÜìnÜ4 ^2+ÿ<±²G*îàJ«»ùrûÁb/Õt¸†õu¸Ëã°Å–ªÕ¬6b¾Ô>z®FœS„ßT5 -)sûñFÚ‘GIÐÈØ°„I€x´éàE<Œ õ-1´n;ÝJjnËÂäàªa=̆¤N¿‘Ž¡¦ÞÉ.ê…³ÖïeÏž‘µ‘¹¯ŒåWú›ŒÇ;Æ–ü taˆ)"Žê:޲ô`è¦ånàG킸%[0ä–Q±­öñ±Š;¸_&¦`Hwâê“sͯQ¬Èš1>ÜlüH‹ñƒL\Qcé.¹‡¯ÐáÚ¶Ç<θ ‰›>TÒÃU—wàL9_{·êÆ+²>8Ù¾Ö‘8ݨ°‡s“JAòºcÔ b½„L"RÃS6RÇ’8e÷+Ûì>šœ²jÚ¢uf&/Ìž°¤`òŠÍ3RwðP³Ò¶ÍZ½IÖ7+°7Ü#-Œ»˲2 #ÂW2ÇŒ°ò¢òÒ§&ø(‡›¤ã·H?tµAxº!µpÃV›-t[pèâãÚðàlÍ]¼±x¨‰þÅ¡9Ìy"¥ôBW‹åÉ¢2 ÃW‡,Y»¿°dxHê(Z<: º”´â9ÐÂgzÚN&!Ì‹6žº`moMÛ>†÷wš8Òc¶Iàü„Å›¨àDÒÐå†GÓ—m ›¸ e³¼éeEÓ³59ûèŽhêßË4¬ŸMœ–ÛÔ£5¯È«­h%Ìé©Sð¤‘6þ¼~cd³1õhì¼4k7ÜÄÈÌTa²Í"¤Ö6\cüT ¿Erþ&ud;-qA 7?(j(«p³»qÓ&.“jáª_Q›Cٙ˭WZ süòÍÏ‹šòŽÖe®Í9Ú¸ùl;•É/Ýþ¥ìÆ÷¤Û3˜ ÓbÃ=ψ©Œ·„wbÙ‹_CÜ"2gÁºä†ô6öÁ扯Š1øDíÓÄÙ6>8\Þ4qá 3—AãµÇ„z%-™½n7×ÄJNqûWás7ô„Ú&ŒáäD¤m?ÍXJ!#ܦԮY´þ ŸÄXùl¬}’c÷^¸‰²‰‡ËlÍÖSØZ1ä‚9i(+'…¶ë“2ÂÌ­¿®Ã(‘. 7£Rr*nQNá®´àø#]¯^:ãH! nî-mÇì‰Í­õC™ÀM •h[ 欒 ƒXxh’ŠBrƒÈõ¶±9G*ËZŸ\jI—dÍØ{«‡9§ô·6Õ°ýHË¡‡¾s3!–>$-޲ô"`•uÏé@iÍõç¯$²TÆÄX…̾pó;æ!tïÄåvêò1ùØ8°Ÿ],9­ZËtÃÖŠ5:Yñ9rEØJï é”,obm²JšHŠìm2¾‡‰7±C#¤è_Jæ²,)K‚˜j¹&÷̵Òë߉Z…M“çe˜:‡öÑs"}’·I-Û ©ÌaHÞä[jFN˜'\ÆÆž‡ v›É´AÞÉ©”„byTÎ ÛOS¢¢í-5Ï\¾fï=Ìij‘ˆvà4kñ¿cF@ §«nž­í 2€P6û»åeã‹Pí™UÝüš ¶Ä•L¾Ï(ß…ÄQ÷@ᦂV 7»7©O‹–´»ò55 Ð/ên¾*¯»%ÔŸæ'¨8›(4Gý=’ÜQgUˆˆŽÚ[o1Òì<1Ìhì_‡Y£l2¨.ÌÞÏ8Ù“sñÇI‹Á“èͶ¡ªùyWîý@aU2ûjîàã»}ùÆË’Ö§Ú_¬¡ÀݳÊÛßÍB¶~æÁ˺îq¬ú˜zô5qÇ~¸åؾ"äÌ•Äü4Üý†µž)´Ž®DI"zˆç”¤Eª»ï>ß2:x:9þ,ã2ÈÜ$ ,~WnÏ$¶*m^–ýå¬UÓWÎY¿wû¹k*îSÌ–°œ’êkg.µ Ôw ½…H$â‚Ìü§'γéYEÛëÃ¥mÃÌ)à+B\¬U#ÆÏÂé7E2~\Ž^ô–+JG¯[þå…[oÏÕÝBÙd2cå–Á†î}ô\ñ ~¤3æoºc?6tîiêÖßÂs°•ßpKÿ‘V~›•ÃÍËw‹«Úù¼ˆüêé7-£ìÆ·ÂÛËú/_Q—È'fÁý‡ºÏÑ \©‘ÉÊÎøUYÌ2rMܲÔ~GD1ÏÞua¤%˜v(™Ü¦`ÔEÙd)mÝXÊ2äQEvÑç•…›Ø)¼°vó¹i[©í0}Ͷœ—PÒ\ºEÞ¦{2tìÜBûéŒÆzð‰EÔP¹°LÇoÞ舅Ŭ÷×íá²~<È Ëí–}çêŽf:…>5£v±[Ô“ÇJOàYÕÍ·G.ßÈÚS†Ò=gý—)Y{Ön?Sqí3®º»_³ZúÊüƒØo)¸ñVq;ÕHŸ 7?X^(.K@áfwã¦X˜2zCÀÜí;Jîdz팓W(g'bk0*Ò®Ô?¨¬¿_]{«¢þ•Øq-ocx4íóÑÓÏ´¦lódЖκï6…DLŽps¸“Šz¯Ü)kŽGïtõõ -¤çß:[w·½Œ8¢æ§`ñlÕõã—¯[OÿOƒñ½Ì|{›yãc5uW—4?£ÆNFŠŸ«§,¡žÚ.ÞiÁ=*¸Á‚‘6aa.àaJR~!¢a–D'»$ç cèLîH¨JnácUk#äÀ).Ý!ætÍ#\lh©@võŽóîñË#ææâÅ0H6 ^×¾ãþªá4ØEįÜÉR\ÂØð|IÎa´WlÎÔg 6ncƒv‰ÁÓ41ŸÒñæ6¢xºÏß{ôö¯Ç›Ÿiº_ÜöjZê½õ½>ÒßÛ$ä3#ÿM|{Xô6hLÔ1ª¡NêñŽ1ê9YèÚçrÛ3äfë—L \ª¬ÚYvºñQÄ<©âÊðP¤v;y¸×­ÐtÁÍØB¸‰_Õ\±jO-UÓ)^ƒØâ Œ´=M}ˆìbé7"oYÏ‹.ÇëÇo†›¢çá+}¦go’äB’WÜB{ÿ).ê¶!”’£^_O“@ªó‘c8`t<ñ3¬ÄAr¨®û$ò&,g²þ§‰gÒ(‡p¦T‚®["«¢ ·§¦ÍgFž,Ïý‰±ooë0î«ÅYÞSu='îËãô±]²V±\5‚Ò YÎú˜b±˜,ø£xV„ÌÓ‹ÉeIø ôQ2}€]To³ÐO ¹ràg¦Á})PàC€è÷)˜.‡yÌ:~6®Rޤë « Üš6ÿ¦ãûY…PÞ‡$j‘B¢«P6-Bx®Á®³†û.×X'Šû©óõ£Åª”¦±YÆ‘©Æ¡Ku}f‘öH& Âïg4`´ gèŸE:¡éò"¡úê|a§Í1‹É °VÓ}ª‘÷tLjùc#XúL¢Æþ ÛŠ÷’¤e”cTDØbOÊ£EåCaÞeÆ‘k ÃVR—ØÌ3~Û±‹„ôÔÜzƒ½7bò’úN>êaà.jÌ:{ÕcÍg*¦’Br„È0rýLo,å¾iT&g ¥®Î'¦=mT}ㇹOé9}”× *»rŠëkÑÛ:¤·(0;Äu’–ïl½À Ïé°£mĀёÕó ö1¢x²0ZÀ‰Ëoˆ(ÄZZ, ®AY*爵«üæÐ†ºLdÿÇF~Ÿx ¥ S§U€¦£jˆUp_S¿Þ¦=ÌBDÁ×Ë.Ah…~ ôS~¤ë”Añ”죾z¨:ËšØE`Ãì@ 0ý©YàǦAŸš‡ô¶S‹ò³^3DÒ½÷Lô5jîÙÒÓ*’s¬ú¡°@/l…±šŠî cYy3×PZR'6Ÿ•²õÂVjûÎç°¾£‘d‰,ù-¼¢ã&j¸O#Z¡®’;*h% o‰»ŒŸ©éUì(ñ'žÝ^Í}G¹N¤ñ1´ÌL /×ê1o¤?vKÖagÐ)J í¤D¢q³ƒÇM¯TÙÅBNÏqG÷_¡-8Ëú•d͈ĸ©¹kóˆñsŽI`Ãâh}Œý?3ôÙ¨ÛC£IÁ7ÊEÄ1ñ£Z`fÅÛ4N5 _e¬J£–”ÏÄÕ—®¿‘‹Ï³Hè`C§ÿhòŸ#G‹ØcŽw% Vä1 ±.% ˆ ,€NýæT˜†Œê6Ð.h°fË›R ƒ¸åác&Œ?[#p5‹¨Šp&yù0©8í‡3`*OªH (ÜìvÜŒÉÕSgéªÖk‡¯Öô_þ@ q h =ôÝäõ;0’ŒÉT3b6„VhUÆ& qŸ¦á;WÓ/…:)ßJq®Óß)–}¤Ûë-6 ^¬ë?¸@‰¥|Œ@ƒëa@º"÷ââ4´T$…5Ò1žâ6šÞsXQ'pUBú;Mèe¥êa þúZ¢]²¬§?Õ( [‹èƽ팛†6‡ñYÄÃ,ÂPÉBh  ‰›ÒsT§ö**ßtˆîo/4Á^Ö=¬£ðÇñ8£<¦ƒizË–vž°zÛÇSq}¸'FÚ¥TO2‰!Â3óÜd½Hî+÷–è}mc¸/”ºÞ³õüç…,ÃÂɪ` Û£~db1ðŸ«é1àÁ;ë(ùñ©f/²0ÆO¥:=_‰UZã©×Šò¨´F?Šõ(YÄ9Oæ¦q\§ Xâ>£¿cB?ûèAŽj0'ꢯÑÚ Ù9ó;¹™Ë‚àtC7pÙ0·ýlc˜rÈ ­"ÖÎæ\„C9&Â[/ËHúÀ:b,'mŸÁŠÒ4ÕŠÌWà&Žiì´)i›ÿÒ߀ìÚ¾Fã¡-–W[èôŒW°>ÔEÌa¸?j2 “b‚ä@õ¿&0dÍ0À‰3öEé.6Ëà¾Ö¡ŒoŒÌ©ð6n2(ÑðŸíކEŽ+0Z¢hˆªn þ’ºz¢Oñ„ Åp+%J(ê$¥eQI¸¹h4l›0bÐØ 0µ|æé/cJýЕ¬<2Êwê0Þ= ÀÀ;Ø1Ž$‡9FS<‡³Ú ÔÒ² I” “S ÂV‘«HHŒ@§Ï\ò.9&ö·‹¡PÞ@ÇX ËXM©¶:îKî›Æ$;qèøé<]eýM¶(¼  ËTš`1‰a+ ¦5‰Íš±èA,¸™uÕy:ÄÞ„§i,ê1gë4z˹¯†ç,4_ Iˆ©*°<ŒNÙ‚QЉNMI¸ÜÏ1‰­¨gN)?ï94Y˜È‘"j"i1ºŠô“Ø\þ7ÁA¼~pK&oP;hk© q™†àŽð˜ÆðµÜKrЉ«ì(ä ¬­´p¤û,r<©×7htÌûX^bªåØ$ÔC¹$4]¿yšRá\æ!‘+Ç&gMX{˜ dæ’(D±Mk/2n(ôô‰Î8øÛaÓ–ÄÅêáÒí–<Øy/q€S¢4Pó:À´hÒ­ÇLæ<™.éø,¤ªš5Êf—mVæ&ÛjØTö—€ÂÍîÆMÃøBÆ|áüRm0ŒHÃs§å¿€á •‡šll±[Ê£([jzƒöÃ, Cû.Ð^&PžJC³àOàBE / »Í× \‚®jCÞ= V‚3À%¬÷&`e™E®‰“Æè=Âmæ¯-¿Åà’þ°04©ý$ø£Çq)¢Dð ·àøi¤WÐDV—‰¸21 ^¸³äô`Á‰d[°E7äR(žšÞó‡»Ïä\Ì¿0-@¤€’Õ6òPÏYÃAfØåúá+iØcµó8Àh¸Ï<€¨º h ?f'7%tÊÜÌvÚȵZ!+Y5r˜÷¼až)éƒç3}>šD¥™©×›Åe›Æ<¡@Á;ÓèuÀ¸S(ž#yvDÇÖ[a²â yú.@Ô M%Q#m7%ë¥ð®JF¨Í’£âø¹Lx•_Zô¹ãxÁMaöÖ]öë„¥b4æF¼¬.S;%#I&NH‰é Œæ1‡ÆS0A†LfìbÓC|¾hûåÜÓ·Š®ý|²þe (ŠžÎÚsý \iþXÇmàèd‹mÍš*ts~` “ßÏ‚’KC÷äÀ4‰;Š×ÁÛdÁMÙ¼)]¿%úÁ«Da™:•Mz®pó'ȇùø 7»!7 °d8êj¨BMÓY®á%Ï}g¶<~2xÂ;XT0ÆOÆs†\ÖÒ"#Ž : Šì ÆÆUBÍa@Z¡²º£(·*S'#@5–É,Ó&«Ý;EÛ{Ž?šøì¿%H?,Í(2ÃH•I¬/#'WÀØHŸVàr.+ÔàeÔ—Ãò)%zÅh,rý–Á¡y…¥³¥cð6©DL¬‚¾óÀ ècš†Œš‹q5Ò¨`¯ƒ†™i|Ð _+A! ]RÔÛ‘Âp)Êú¦ÌM(Ôvuᵺé<5Lz+:,UŇàtƒ|‹24;ñ-&1™ôMXnÃÖˆ©EÈ*¹ñ™=òN,œH’ý¢HW´T)¸´k á:À¥ t˜›Jô_%ÇýÊÇó¿Oæ&'ÇP´ÎðÒ¹2DF2zKx¼\ê1uÑô_ªá¿tTà 2Aè‰È¾ ]a“±áøõ½UoO·þt¬þ-u0ŽUÞ[”³Ÿú½Ü(º8«»u¨0{Ïå)xw¼DnD"‰èyÔ:ñ˜ÁËiÌ ˜`ƇïlÅÄÀo±NÀR½`‚ŽÒ£(›ÏÚaÒj› ¦ÂÍ®—®|øp$ p³»qÓ n“Àã Å JèòjäQÖ¤Á¼s$gTO-dM*ò–*´ ò.æDÆœ~Ä:½ð *ˆHHµP¯ÐÄèÍÁXÞÂ3´ƒ©{ÃhÌ ¬Ã×pGT ¡OIÄ”¯,G¿ˆ_Ž8= €T” åŒ± @Ð%ÑÏð4ÐI˜+w¤åïH®¨ŠÆý—ûŒp·…­”ƒUô"Vˈ”T‰@Ò˜t4†}ô2¹ÉW>Má[Äx˜£“Mc0ç¿°˜{ˆhÏœ‰:<#uhð7CfUÄeIg‰Ã(A@Ó-à{Ôœ†èhü)„ }ÕµG8%ÕYHR6…Æ*8´'n#—’: ¸yÁwŠ^ `I¯˜HÔØ4]ÒX¢s˜,ñh¢”PX*’1ŠH£æ­AÈJCa=H§$‘o*baE\„:±DíšG®ÞRúøTûÏ—üz¼îÕÞKwçfí7¦T…©G?CQá•@YöÍ3S¦ ñóãa1óöÙ/~<i¼5még ‚%Ÿ©BæèLãèluž±Z2‰güCûpÆLåI (ÜìnÜÔ‹aÔÝd/šN·,ÆgVdf+sì·ÒN=U¶G‰Î’ÖÏš Ú÷hž<Ëü•vfSQ‘™=|+ÒâúÒñœHEtö@°°F+„Vb˜í0Õ¹äMˆÔ‰˜Ñø3š2q’[0:‹JG445¡¬1>Geª’¯ÎÖg¹0Â8a£@ãv¬` ƒ?J(X!V†‹LIX#4/ü•1XE:·bÀdäÖ"ï^X8iâšÐü7ó¸»è€ô \XP-g Ò ghM”î!•R p•ž7GÞEó˜Y]{Dç…$™0ˆ¹‡ &™/ÂÌÛU@pSnâú<7îâ‘Eèl7ù™ðFxYl…l‘'áµñùÌ.˜@y¦.L™xÅüä7ËëãHjôY$X'ä™G¦ÎÈ+I),žœ~0tV¶eÐ ­±jUXµü QW–Ê~®“P…MÛ¬:‡&f"úWÔ˜•¯™Wðëú±Ió&òÛ¥DëœHs’we¿Ò | P¸Ùݸ)é››dê‰áT2åuŒÏRä {º¾•èüV2úIÊ‹|‘ß\J8Ñ„^CûÍ~iÏûw‘¯ÆŒ²·ûùï_áç 4Ð:d9[¡“#L¬¸²|ŒàÎo ƒ9%À(^ÒãämÇÕ„àxœŽ«ý·ƒöû“–Ÿ¨ã¹Ø#°I/N4žK¾¦•üÔÿãöÝ#tôV^Në7¥‹Ë}ÏÂCu¾,ù¥¯:^Š8«ë‰:$#½…w/¢óÝÑOA[^:Û":ÃZ•F• S¿9úã'² é*Tô¥*©@,×2tLîQŒíRĈŸôËA2Âé:Eù HàÔÿñeÇŠlC3O*´H.´œ¸ÉvÒšÝäÍöS¶8LÛæ4cǘÛÆÎÜî2s‡Ûì/ÜçìôHÙí5o—÷üݾ ¿ô[´'pɾàeB–ï]q rå!ÕêC1©GbR%¤IZ{,yýñI™'§dž˜šurFî™™ygçœM)<7sñ­¥K·—.ÛQ¶òó²Õ;ËÓw_ÊØS±noņý•Ù+sW«ÙxüêæW)œ²íLÝŽ¢†]çš¾,nÙWÚràBÛ¡‹m‡/µS1ìDÅÓ•7ÎVßbÍJÊŠ^¨»s±á^EӽʖU­«ÛÔ^\{ãaãÍÇÍ·Ÿ¶Þ}Þ~ÿåõ/o<|Õ͹ùÞ@úþ+FøwðcûŸûîipæj]'òA\¼³I×ù]ë:àwçþæÊ¼ä>H[ ‚w´.ru}øíÿ5”?kò¹2‰ÞßþŽJÿ]7TúdåoehÊ܇uÊäŸú ®ÙÑP.µÿ©‡ÈGšü÷Üäîïõ³“³²4ÄK׋Ûòÿ´wå_M$ßþÿx?~ßyïœ÷¾3ã¬**îãúuÜÇ„°É&** ‚‚"!a a1ÙYDY•EVÙ—,ß[UÝ!èÌÁ7¼™Ê¹§NuuuÕ­O'õÉ­®{«ŠLc° —Ÿ „°~&‡½¾ßãúÕNÇÿÚlû`aSô¬< +ÿØ(Ò„§–KùÁb5Z+ÀkÈ!¼I =κõø Á}ßtÌÎW’æ)+à§Ay³¥{°­{¨ãýð»¾áÎþ‘žQ¾cýÃã#ƒ£“Æ&G&¦G'g@&§f¦¦U333*•J­VkØv>sÎÕd2ÇS7ƒ1ö3ï!BäDW‡eIîÔï$Ì ¼–uíbbeÈ~Ds2ˆþU|6üôülÄx\Ãÿ ë÷8ûÚ…8kLs]_º{ŠÿápôÄ×SW‡w+¡kxòyÝzòü/¢ÞB®4à´ïì»§Ø‹œ€6"7U Mû™Ð³]ð?K8²,³ãß>< û^"ƒïч˜¬™Iy!O…"ÀG€òæb³7ùó'ÉëÏí'´ yÝäl'gg×AmÂ<À¥˜ ;åJ š…9:%霽óydÎ \!¿æ§ç¹Ë™ £ª‘ÉÍ@ÿÙ—³æ‘±KH¹1ÍuWq`B†#MÈð‰ü:¬JœqgÄr‡íOìÖ,ØÙ ›ÁCç?: A š8õüÏϧ9¡ÓàO Ÿ,Å hÅÕvg!›–Á?Ê›Dd•[êÙÂzúÁŸÖ¡üõ ¼¹èxÓ`þáfQ.ƒæXÌ}\ Éè}?Ù:z…Œ½&1dÐªì¬ ;$s83““ÊÄZaS_ŒÅ°qv:ö1¬³°%|ۊײވxåóáCþ?›õÌbŸ9ZàÿEá!ƒï³<®w©ÏXî½³ètÆ{¢Ìq€©#ÈÝæ¿÷z D{/ƒGÏ/ EøA¯_Ák¼ oš¡JßZ†|m‰Ø?GF êC#Ã4ÐÁP+ZBø‹#¿ºN»¸Öi ¦G–Y8>ÒûNòÎꕙ߉a(F*s NÁ³4û,{¨×’y•M9=?É6aõ×5ò1e8­þ`†èÉë……4ÎÚhšÂlòy“E•ãM¢- ‹DÂ×CG ùòDðÇÿ÷òaùêˆ/X¿´5ü€`C2Þ0¼ä¼~?×{Ó m+‚0ø_ B>79þ ò<Ìi ÿÇPÞ\lö¦ÁìmøíÒÌ™†óê\´¨£<عJö»2»^ÉFV–L1Y¾àú…C4K£÷Œ9Ef]V›º9Ö¸>\F3†×ê—0fg"133gÙ±öõ\ãBêZðÀñˆ˜ñ’QÏ“ÂÀÁÌ\"ÞDB }éZÆ{’‰†H=üø’Y•Åÿ‘ôk’›«[€ÿŒ%‹vcŸtPPAtG,ÑÊ%á_Z„}aþDï•“(x¼‚›*¸i"OÒS@š Þa‘Ö¶ãBD_Ëào‘u2cçëCÆÂÜ_Æ&5z³(-Rþ&PÞ\l¼If*Ha²‚/!ÌŠú)š<ù²ª‰/áÎ’i9äNá>¨²^¿x®æfT–S˜ –â)/ôÍ6<9êäTÒeôz™=FîB»î’¹òsÌá:ÄÈJ5Û‚aø7NÚG#"È@†Œ÷ÓR›ÄêÄ·`ö] íëôdï"Yv’1#s ê0+À gqzb·Vðo%Ämþ•eø?-„Àž`{"Qì.ŠRâÙ Ñ“,…°NK|T‘#­Eèräƒ^»†ï “â{ª§t¡'s|'™û¥CžÖ¡üu ?[ì¶Ô Çr±aüPLí#AV;ˆ×8r~(c~(û?ù¯ä£ÞrðC9‰üPÌé‡r64üP.Ed¹‹³=¢r¼¢ó¼cry~(ÅØ¥û¡<Ã~(åÈEYýPª°ÊËÙ~(¥ Ø¥ û¡4#?”šVì‡Ò®ï‡Ò£óCéÐ÷C2ºŸvtŠ·ŸV÷Óª?Ó~ZnŽ"O S®É@¾Ì:Ë’v¸Ãy2†=Î-¿wü±ŽŒõÂ/7víüåºà—Ë›óuå1þ©YyÚó”À),@pº1«Aþ![ Õç—Ï™g+3-óêÍ¡ËÙ ˆ^—ÉÎÑ,®F΢”\‹ÿ#¡vØFP†œâ§ü³\ž\ESŠÀß2Èw‚Ðï­Â~°_f-\fab±ÂN´Ê>ÄÔ!jµcô:gÉðß”l=»ÍEú¯sñ;ÎÇý|A¶ûBü/e{Ýö»'ôH<|%ñ¨WÒoމǯ¦œôMáó¦]àû[o+œ‚ºÜUž}x!<ÓM˜yY”å™ís5&çª4Ï7>ÿzBa@Ò,Þ,*ÊÄŸG*Ë%™ÒÇñÙ/r_$=}™\PKü7Ó‹ë:Þ|“SÙ”_Ý\XÓ\ò²õY]kY]ã¿ÙØUý¦·¦¥·®­ùobÞléhíìèx÷~¨«o¸»ÿÃûþážá¾½Ã“#Sà‡2465<13:¡Â~(ojˆŠV­A²@Öaü·R(ŠÀ"ALNýl©UÄ2kÑr°7mD+ìĦ§£@VÛG¯qˆYçûã)Ÿ7!îð&Ä=`ySvÐC¼yÄ;ó¦œãM‹÷ P¼ép[iÈ›^‘7o>©2à͆ŒÒeÄ={Ó€7ëÛ0oöT6ö`ÞìǼ9ˆxóí`KçâÍ®!ã¼9­ãÍ Õäð¦ffìÍÏ›KÍ…T(ŠE`Ñ"°ÌL¸Ü<Âä”h……x¥eä*AÔ«èÕ6Qëmc@6ØI6žŽÝä·Å)n“³t³Kì–³Òmçã·»¢xA?_bâí»,Ûï);è•xÈ; ­Ó^“ŸðK1»žr* Cp3Ã:Hñ‚샕$^ìMnöZ|_B‘Rñ yI`JIpê³tfVô°BœÉ¬ÓJŸ¼ˆÏ}™”W›œ_ŸZØVü:½˜áÍGÏ›²+šŸT5çU·æ×´¿j/­ƒuZboÎÇ›8î±7!îcoö3ö&Ë›L܃ÏË›pGàÛBSŠE€"@XT€2DLÌ„+Ì#Vž­²›ZF®D­D®³ŠÚ`²ÑVò£]ìfûØ­NRàÍM.±›ÏJ·bÞüé¢lÇ¥ø]n²_.'ìñýz%ñ€wâAŸ¤CWåG}åÇ®§œðgxÓò¦Â:Hi{[iœép'Ó9ô1ÄÙ;žMâìÁóMgÏGZÀòfɤÒÀ”gÁ©å!(Î^%ÄÙ=¬++£3«!ΞôÉK=Þ,jL/nÊ(mR>k~ô¼9»¢õIeK^u[~M{ñË·¥u$Î^'›ÆíÍ$^^§ý“yEve÷vÒ𦧤`6o&—aÞ¬@¼ù ŠÇ›¯oæÔðf3²7Ë[±½Ù†íÍoÖwc{³‹Ç›Ì¾ Ì›‹O ûiÿ¯xs‰àî—VwiJ P(‹ P†lJÿFú­UØwÖáKm„?Ø —Û‰LN‹V:D‚À~ZS§èµÎ’õgñ–Zv–Ø›°N‹ìÍˉ»=÷]‘#{xójÊaß´£×Ó¤ÃR-<ß´ ̰º¥{Öi9{Öi‘½™ãìM›ð>”ë²¢ô>”Rx ^§­Àë´UxìÍj°7¥Ù oâ÷¡4Ü+jÌ(j‚÷¡ò&Z§­Åö&æÍêF›=õ­½ í†¼Éø¡t±qÝ>ŒŽŒNŒŒNŽO³7Id÷ÚN«'ú/­+=M)ŠE`ñ ®^03c‡/á7‚ðo­DßYG|o#úÁ6b¹m¤ÉiñJû˜•Q«£MbÖ:Ç®wAûi‰ÿæ¶óÒŸ\cw^Œƒ}A»ÜâñóÍÄ_=å¼å}’áù&¬ÓóK3óIµ¸'¸yß:èmðû`…ÇgB•.aÈ?ßÌvgžo"ÿÍëqù²Â›‰ÅAIE·“‰ÿf¹ðþóˆ ö=bìóMò±´§µéõ÷ Ñ{Ä–ÿMô|Þ#ö&¼GŒãÍòúN ÍêÆw/›ºj[ºovôá÷ˆõá¸ýmÝïù¼9Ü?4ö§ðæ7fB›¦ŠE€"°˜@¯á3@ï2ý`±Ô"r™@¼Ü*ÒÄZljcjµÚV²Æ.f}ìzéFÇ¸Ž’mN‘ÛÏDî8µã\Ìî󱿸Jö\ˆÝ{Qºß]vàrü!ðCñL`ý7åǯ&YúËþ‰VIÖ7äv7SìƒÒo§;ݹ禸þ›ŠKJwQ¦Gd–gÔc館ëÒÜiθÜ@ÙÓ[‰Áò¢”¢»©ÅâŒ2(Åó臒Ì*iVUÜãjYvUJÞ‹´¼š{O_Þ/¨}PT¯,©Ï,}ýèÙkðCyZÙRPÝV\ÓVúªƒ¼¶Ô¾hx÷¢ñí«¦ÎÚ7ïê[zÚzÛß7uô‚ÿfKgøo¾íéïììêNä¿98:84:ôalxìÍñ±ñÉñ‰©‰ÉéI,SSð&1ôˆS³`Ž›ŒÁºÊìÎÊS·iJ P(‹ Uæhf6=lj²Úâ΋е‚»ë-ïn„¬³Yoygƒ ôGëÐ-¶á[í„ÛíE;ìE;íÃv:ÜÝå¶Û9|Kľs¢_Ï‹÷»Fv“v‹þí²ä¸§ô¤W¼¹O‚ŵK_™_œ_¬Üé€x盉.Aòów’]CR.†¤‚¸…¦^¿wExßG¬¸¥ôÎ ˆÎ¼!QÆ>¾-ËIÊ KɦFÜ+ˆJ/о_,yP*U–Æg>OÌ®HzR)Ï©HÍ©HË­ÌÈ«Vä×( _e•Ôg—Ö=yVŸ[^ÿ´¢¡¨ª©´¦¥ìUÛ󺎊úvêúö¯Ûj:jßt4¶t7¶w·¼}ßü®·­³¯½«—& z08Ü;4Úÿat`p„ðæ‡áñᑱѱ  N`ÌéiößÔ.8on>æ¾é¸M)ŠE`Q!°ù˜Û–ã—·w‡të m'=¶ŸðØ~Ò}‡¹×N‹+?[øìxïøì³¾ö«ï^[ߎAnr¾}øLðѳ!¿ 9vþîq×ÐÂN^ 7»$4w·ðˆ°ô ®ˆ­¼DN¾QÎ~‘ .þÑçnH\¥ƒb/Ý’z„$€xÞMô “_ÊýD)×ũⴠ˜Œ[1éÁEHœ",!S˜”%JÎ%?–Ü "}P§È—)‹'e•È—fä–?xZ¦È/WVdVfW?)y‘SZSPQWXY_RÝ𬦩ìå›çµ-u­•µÍ¯ZkÛêÛ_7·7µt6µ½kiïjîèj{ÛÓö®»½³çmwﻞ¾®Þ¾î¾Ážþ°1GÁÌ›ŸœKsbŠXšÄÌ$¤©V/??­Õ™E‘Æx¨=ŠîãR\BÊQˆY­"çqò‰Tø¨“w+(…R(ŠÀâ@¬®9)ÇHü ÔG¦¤0­«ÔÚi`:µv’d4èjÃ)’ÎI^¤Z qС¾þ⧤>ô‚EƒºÀÖ"Ë+ wDv<úCê©UhÃŽŠIUš²wgo~§º<¾à£ì¹à¼IÀ¡)E€"@ ,6÷©YúÄ”|JBÐré5È ¬Ubvƒ0¬dÅØy,«jÁ‡ ÁR„B’‚©:ÃÌoÐ+ˆñx=« ݇Ф¤~㈄XÁi5Ò˜6¶"ѵÏu„ÈuªVa•TjÐ “«läZЇCójF#>“r¤ ™…úè ÒpØ´„"@ Pþ$T*Ä›$…¹ìID&ú¶9Dåƒ%[Í„Nô0’×q8«ÑŒjÕã ä±]¨V¡_pè“B 4© •à<´Ì°Û\ÄÇR-èF'â3`j(GW‘”"¦fË¡¦z ¸R­š$ô=oËL„|‹A8ºÔ‚©‘%t„ _Š7i;ŠE€"°h F?WUŽ¿Œe÷é3 jLÆySÆ6œ·stúýÃ)¾”&ŠE€"@ P(ŠE€"@ø»"ðoxbùendstream endobj 44 0 obj<>stream xì½phš>˜ÙÛÙÙ››ÁÝân@ÐHBÜ¥5îîîî.Dˆ»B !®Ä IH ®3Œð¾n`ؽ½«Û_ý·nk˜ª·º:!tÒý|¯=¯|‚ÌdÁ' Åãï2Nç£&‰% 1ã„âŒY¼Ì$!ãx1F¢#™×8™‹‘$BO`¦ ÒD™‰B´8aü3M˜™"HOâaÄ 2R…Y<†IÂ&i¼ô8!£AZûÍ 0’ ÿVoü½ýc„Œôac%A´°q² Q ? à&ó8Z£TXƒý”hqZ¤=gƒŸ‘ÆEM5;Ëc+nœ"BO¦Ç1JÄ 5ÎÀ«Á¼‹øïjþ€IºˆIš01ÔÐÐ4F&/5MØø,¾ä§%óPÉa`¤ò22DŒRU\ó+û–Ž3‚E)áĶ›¦óQâÅà à(Ä)À&°´æ=ÿ÷wˆÿúÿ9èÜlƒlˆèiüô 1“LƒQj쳘Iº aŒ˜I*-ÐS‚«Ê.‰*Zp)»ñÆàç§'Š˜fBÙ¨©pñ0þüÆ0ìiBÔd¸ ~ã¿×ñßÿ?Gæ¶WÂ(ÆY€™[-F‹U°O3ŠªaD^Ôó«”³ˆSuË¢& SãìS›³«:·IéîSõ‚Á¦0ÝÜ”“œj2·q&/3…Ï ê I¢„I-†íÁß}ü?Ëïù€ Ú*nœ #,dšÃC‰Sw?›Þ0šÒ0U6œÓ™×”Ý4)mš(¤à“w9±ðò¶Ãœªž’F1&Ñçõ¼ÏÉ٤ H0bíó22q¤­\3›UݳÅé1|´ØßUûßê˜!դƒÍ8 !wHé@þ…+G5™"r i5«àøì¦ÙC:Þ<Šöñ5=¾qûŽÐ÷+ì½WÖ9ŸQÑ]7l^,b$a‚h?K”ž`à›S9´¨ë‘Ωäþ;âÿWX‹Ò³à‹¹èÄÏBé H¥%ÌÏZÆ4¸$Ö+9çîg[€–XÕuËÉ7~ãµ½ò.»•ýN™^ºrã†ÛYÇÜKãÖ~g÷ÑuŽ.¼Ð?#£nqDÓéÅ5¥¼åle·¾3Cˆ›\ÝWÐÂ#g¾ó”9?ËÝÃwð =7G"@2AAÈ¿)ñ›ücÄ©ñ¶§ 2’!N;H /ê­în™Í¾8.¡í²ß8†Ë0¼zøÅ9t»4SÅG€«âs¾÷êQ ÇÝÇz®êØE@Ç«û–]# vÖÙzœ)¨î,rÆbhâ–ªm¤€~ðI³˜¦‘e}ÛÐGhZBŒD0qs¤±‚Œx¸AjœþoòCþ·zSb´x8Sa“ >J,/%YÜ4K×3·uèFçè­þ¡é¶‘;Þ Õü*n<º¡e=ËT×øÍÇM„(qÈÍå¬ãk»®ËR<öÉÐ[®\—3öR0kªH÷Ùt„²GÇÏ0ì´UJg︲‘ÿž“&q%íã·crê­Bó¬bÅhѬ<=Aœ™,B‰FXˆL¦þßêÃùMþ1üŒ˜V~jÜ!°%©¼Ôt Ï‚®kw.®Œ. \}P±UÒ@D;°²ã¦½Ævi çÝÕ½ kzçNP½„Tíš®LÐuж¨jŸiäW×u•_ÑÒ)¦0½¢™ó*·²‹ Q µo~gdA—4Ó+¥:ÿB?ÿqƒm´¸Ož¦ù^轡å˜È¥ÊCÏ2:+b”Îc˜Àoôšoÿ÷Ý'€Ïf¼âòx÷èâåá•°ÜN^eG]¯ƒÖÒªF<Ò›DU÷ÈÛóè…ÐsÈm)ªë/n®ˆÊ¾Ðp©÷ Í^Í.¢ðü•ÇL(^i£·Î0\êxIÀÑ»d—÷Üt I;E»<±ªk´NRoŠ·8#’OÉ©óÊuº{òewˆ „ £D È þuïô÷Wf =}³ZÚ‡nvÍ÷-¶ôÝà=ÁØ£îÍ¥êÍ-g¹ý„Ýyw.íhA“œcôçTqycÁ3ƼJVGÕm›5,½9Í%Ul·ÊXð«»ézdÆ—v¥Ô ÆTuÌ'4í—§Dž­N*º´÷°îV9뽆qûµ=r/õMÒ²ßvÂ\׿0ëòMó"qýÔÝ~Çå_ý ðв(¾ùí“·Û¯Þ[xžžS¹YHa§F ' g†¡ Ðèq(rRã<3ÎÇ\Ø«èÌ©éºOËKÞ,ª}ìž”žË:YA ~m_½paí Q»#ÚÖêV~jfÞNÊùŽ?xeŠÜÊ©îæí qZeÿïø²ÝÇ(Âjö±åÅMýõ½žiM|ÚAÿê÷ûþ¼>Ÿ)JWq Uø™Îã¥-ÓÝÓ;ýòÚU\2Åõý4]’Ó«º¼#Óx+®?¤½KÛ—“™ÖEŒ‹¬m3ƒ×(]Þ)«åÚ³äÂF¿ŒàÄš¦þÅ ³Õ[Žª#NÒO ü95U˜–îK#p·’ÛöÓ¶;m›Æû¦Ög]0´ Ñ!&=¼¤§¾mø”¡‡‚¾ƒ‰_æQóX.%‡ÃÊÆqÙ5øI^ݤiHxi)l#<îwÝÿŸê[²úݢЇN“‡‘,nYÒ{qàVëèbrÃ8’¿†çvÓ­uöÈ0xT=DPé îx1jþ^…0Q€#:îzf~Yçäcº®\rÌõÇhÜ:!‚ Q™¨«&Šà·Pã c… ¢… "(1üÊÎ 4ϪΙ¸²óc÷¬}Í=ã›æªÛ§3…Ôœ¹U\úäŸQµŠÖñÇ_¸Q~=hœÿÎÇLg¿©wßÑïÏß~oÇ“·ß£&‰2Rx™»)G-Óš†—zG§»¯õÏ<Þ(©.¨å+J‰Õ –Ô ×òÑPqÎQrÌÆ£‚]†œUÊq£(QMO1 Q g!e1ue;ne'P+¼Ôx¼,ì\ùu¬¾°g¤íž‚ L‹Á)Ú¯åfàãW¢ÈpW1 ¨ì¼ªë+!Ïð NÊoâÓòÍ¿0±jîÇ­`×D¶nU]Ø>/mNÚ-þŽ„÷­½}ïí“·ˆ¿ýð!F8j!Ó\Qj\tiÇÀ»ƒ×e—5 ËQSü5±å6!ù4ïL=· ûXSßt+ÿ,}—¤ÓÑG¨>šöZ¶*Vf¦N`‘}bµžw±„A¸‚U´CDApZ“mp¶±w:Í3Í4 ÛÔ;)£¼¹{b®cäzÏä­šži‡È<‡ÐŒ =mÃ3gû\“«]c‹"s\bÏÙGæk;ÄÉG¢KˆPbEhñ¬¢ 1,| èÑa†’k /-IÀ8ì ºex´uý÷S‚Û†¦Ý÷ªxàxiy7÷ßè™>M³Ûtˆ*¦ãå•ÑÒ~í1Ã6€WJmÇq#0~¿#þ.¾ÿ›ç2vé¨wtO,¤Wö꺦빧™ûåXúd1|³Ý“ªÃó.fTw&Õέ,=ûéæ½'ó«¯.?lŸë˜¸ycùáÔüêÈüƒŽéû“‹íãåî«÷ÊZ&B³üÒê\âÊeÌ‚…´Ý$)ÁhBU*Oô–¾ž —°èTÒÓŽF›é›Ú<¼HóHÜO ÿÓ9¹ªmæñ­¤úA§ÔêÖ±ºóq•]'Ì9µ‚8)¤ÊówÇøó®ß·ŸaDøœÑ¢ ë•UÜ<™ZÐVéWaQâ]è•—w~ srùêòÓÙÕW—Üzô7WžÌ.Ý_}|óÞók€{ñþäÜêøüêÔÒ£Ñ[w›ú§#Òk2JÛfî·O.6^™-k½^Ø|ÊÄ,¨–—n0èz1ÒÝš"h’°àPDIg#y3R¹uC¥™A‰…ÍÍ#—†–tí#wHé£úÔt^+8e¿’1ú+vÈZïÓ ä¥$¢%ƒm©Þ7ÿ©÷ûö#Übf&Áöa%v¡ÅæQ%¦Q¥Æ9^ ¥m×—oß½÷èñÓû­ÞrÿñÏó·ŸÞZy¾¸üdaåñÜídzËï¤'YÆ]²ôËvЬ¬nÍ)ïò/s‰)˨éo¿ríÉ£§+O.=ùiñþÏw|¿|÷ñÍû/æ|¿ºòàöÊ0ìsŸdW¶œ«ê¾µúskïxÇ•ÉsG.-NÝ~6<³Ü75×6:ßzuµ²o>©¤ÃÔ+UBÉ‚W–ià™¡é’¾SÆLDÓ”f™–ÜI<ψgðøûwi‡sixŠ*™{ÇävLÜ6õNØ-MÙxÐ[ÉSP/šØc´Ëþ{uòÿSz÷ÿï#˜c‰ê6ŠÐ; u@ÌL>Xz: ¼Œ4I“¸ØÊ¾¾™»S·&ç—Vž>ïï¿>ß9}§göîâÓŸ¾|¹úìùÃ÷žÞ¸rÿ‡å‡?Œ9ü‹+Jÿþ¯¨0+,‡£ÄÇç(ÄÌâCÙÁ‡Ð"ãëGo¬ÀV?»ûä‡Ç?ûá§Õ§?wÝÊ©í‹ÌnŒÎ>_Û1³ò쇻^þáÕè“èü .ùn1EEˆßÊ.}ÿó«Ÿ^þ|ÿÑ÷—{Goܺ‹,©´expj¶¨®õÚígwxµò䧉éÛS»¯ä\§z¤ž6 =m©lå“RSß=Ý7u»áÊBmßÍÒ¶© c+Ý#±4®¬£°íšœ©ßwÚ@sû)äæHЄŒ3ùé™Ðza#ð6™8¨„ GLŽOÙélõÈ£¦ñÕ¶«Ü’ù5|Áþ®ã8ðhüâHs0!B†ƒ@¯10ø“%n”¡åU^ݽxgõÅýûï=~þäçW?¾zuçé«Üš>·¸2Ûˆbׄjç¸rç¤Z‹èrôó©µýu]“zUÙÜŸQ~¹±o¶cj¹¼±mtáaIÛxbiÒ¨©¥À}îÎóÉå§^ñ'™¾Š6ñræQÒtoß䲞k+èŽ0pW3ó­X_Ø:UÝ·PÔ~£¬{.ª¬<-ÿi›?s)~Á§ü… :Ü:XQb ðg3“ȹÅH#Fiñ"ºÎ u¨°¤ä׈Êj+ªÂ­áËŽXÞs5'³½hJ¤“‰0lÐîc–É™]«÷î®,=zqëÙ«•g?®>|rÿùwŸ¾¸÷äÉï^|ÿÓ«_^u]™ñˆ¯±‹»èžÒêSï!¾¼;÷Ò€kb‰ïÙKÑ%½qe]á]2êÌBÊ‚Ò.×\[~øìæÊó°¬F—ôÞ±›7î¾:[?v‚|HÏïÅ72§éÊ죋C³HÏÁÆ4 ΕwO—tMwÝHªéµŽ*U·K8I9¬çõ^ù/”þCXsß'tÊa( – “ŒèŽWÏ.Jˆµ,˜qn9ê[%Õ8åí¸´Y…¹÷=rCº î‘á.F„06B‹I¬œ[ytûÁ³;Ÿ¯ çœ˜wiÚ<¢œ ”ÛÀAÓ »ß(­ZÞgQ‘Ä 2Z,È ûû®à8ðøp@T¢\ •õL­.Þ} 'ûq(øÃg?ü⿼ú£C®)ö‰Un)—\’ëÜRš }ÎI5çb«£«Ð¨YØ22ûàÎó_î}ÿrõɳÅ;G¯ßN/=ß·ðàúÃW3÷Ù>2»<0s»ûú¶ 00·ZFo]˜c#Ž(½¨uˆŸ»Ô,ª(µa"¥¢»¾}|zñéÐôÝÞ«wa4ÐKį̽¶ê,Ä7ªu¹—&“«û*û«Rj‡P“ýNéê6 Å-A9-2æq¢˜%§'‘”ž ïQ†|°ml©sx¡g⎚U —Š#/5†ßøìïV¾[À8ŸðšXz~céÁí/@Œq8q6âž¿|‹ø‹IØöÃ/“ŠZlbkœSë`ÕÁ…:&5:&79¥4[FרÅ×x¥Öy%U$•´ß]EðêÎÃïû'nv/Eæ]T°WqËT´IT4ôŽ.hî»Þ3±Ü=¹Ò>~§mìöÅ„mñªîÙòŽÅm7Î5Ofξ0–PÑ…)éˆ(¸lì›¶vƒ(Æ”¼bò¼’„”ì¸U<ÉH#š¬LR½*¢«%äã§ íöœ6y®õwÄáþÀœŸrΩî¹9sû1¼-* ·VŸ¼‹8¬:GØâ?ÿøËË_~N)o·Ž­sJ­â.0æ Uv±Uö èŠþš+‹µ½³aiÕNÁyEM#]W—;Fæ/,”7_=¦ã¤d®éš~îRMÇxÏä|ËЂóÝ®L·MÜ:?x“ Þ?WÙ5SÖ> «žwq<³~ÚTÕUЂ<ÝâJøåhm—<¦ey†éñ—}Ç¿•ÐA†ÎL º¤iš™NÇä*^Ü10a£ð™½§ìø "~çÜpày™ ×BÎuL.?ŸY~xëþ µo?zùý8ÛªñŸ^½ÊouJ¹è\kSç‘ÖPÚWÒéUƵ¤}ºe| š[V×s‚îOuOú# }c 5ÍârTyc?çØ¢ÖÉ$é­W®5N‡gGd•\¼2Äë¯Ü¬î™©è¼*ì¹æ«ÙÃéµ@<¶´3,·)"÷Ú'€»]Ĺ bŠß ŸV1÷ý|çÏ÷Én;i#f„¥@‰°íš~ûN›sÊè#pzûq“½Ê¾¤×ô÷™ÓdNF–ºKJh`˜½óxîÎÝ…•‡ ÷þq¬Ä_þô#¼ø?ÿ’U7`—tÞ.±~<¹~zzip!8½ÁÌ;-§¡¯c|~øúrtbžˆ2UÏ!*0¡ºot©otÂÎÇ›Øi9£¨Ä‚ÖŽáåÞ‘Åž‘…öáù¬òV§ ¤ó]“ç‡çjûgÊ;®•µ_/i½–ap'WöÄ”vGµg7†môO­òK¯öϬCÌ!}×CZÖÞ!ù—½2_‹rk‡!~CX.i™!`É­ìÅuÆ•[ÃG€ãMv¼7ÙÅÑ3&ÄL€Ë†ïF_(”D…i1•WFgïÍ/ß›]ºÙ•ÇKž.ÝC>þý½§/ï=þÁÂ6vp/?¾üþù«Wç硤ñ•6 Pé½}åêjXz¹–¥Ó5½¦vIb²jG4¬ \â \“´lÂNëÛsK©J(™É›‡ù$U ß©säf÷ÈLÛਬ Qõ˜ÌŠžñŶ¡¹ó}72kz’ËÛ³ë¯dÔô$–¶Â€„å^ Ȩ ̬÷K©D†OR©_J¹[X—´>Çz±¯¸¤¿Øwj.Ɉ¶Šmlèž-¾|UÓ)Qœí&så}¥¿GÕR¬ÜAϲ0„7( î€O@Ù5³²ïÖµ¥Ç7æo#l›½ûìæ½gPs Ž‘qÀ ùºýêÕØ­'n ¥AgSk¯„wY…{&VæUuÄåÕËS¤4,˜ž©T×Dy𛬾²E(Å;ËÐ'GÍ6ú$Õã–íi“ y«X%³ðÔòn˜úþ‰ùò =*4[cçàšö1Ì1¡Û9±Tß}Í3:ôôêÞ¨ÂæÐœ‹Pp î“TîXæ_äïWè‘u’êöñöƒ_À°ï”úZTKTÓ£~ì)^¹wr Zûu½ÐÐ(fžMZYe—÷DÍ…è™òG Ù˜‡æŸ1c°R`±úçž_]x€BÉôÇÓ+ÏæVŸ.¬ÞGJ¾|Ÿpn¨zƒ„yô”¤ä WoÝ{Žv“Т¢öÙ®©»-#·M|Ò0ëGq —£9I¨™ªØ„™‡–ùÒ=ÏÒ<Ó }ó ü ôý‹4ܲ”íâUìTÓOZ%JGÕõ9¡'¯e¤klÏtôK.n|ò=ùË«†î)ó°bfDmLi/²­Âº.m;-Ûh#ÿÏT}ïtFp!˜fP¥IX%#°ˆTªãS¨å]€>v5·lE‡TY«x‹øcæ©‚*ŽGÔ,BÏ%ä–Ÿ­j.k¹R×3Õ=¾Ø;q«gt¶¤®UFÙÐÎ?1¶°%èì¸o–j—@¯ÑH ¬!vÁihж H’5üxƒÐçÛ%ÿ°QTRÙÒ5¶t`öÙàô]ßÈÌm*ÛN;r!–Ã\ùûÔç†ò7©1‘ x iÄ–-F¢EbÈGfV&@ÆÐiüxæö£ù;î>a1o/À®Ã°CÍ?{ Äs†L"ª˜1MÆùÆNá'ÔÍDŒµ’ÌÂëÌ"kM#«aÌðzh•a`±A`¾®o®W¾¦ÛYU׬3ŽrÖI'-ãd-bà^ÕÑ_Üs³{ê6ʦØpáÊÜúNÌ£åUµÅdÈkPÎèûÄœ ɬaùîr¯„b÷˜s,¸³mƒRÃ2­ü“‚Òl÷Ÿ¡}°^ðÓR_ì=ö%·œ¨š½ª]ì1=ÇïøOn‘ÒÛ§é¸ƈ2ß#?Äyh¤|ŒìERcÒÂh×èìèìêøÂýѹû}úöÓ« ÷æVÐgþ /=xŽ4m•€þjþÃϯ²F ¶ÔˆÓ°rŠKœ¦m¢#fP¡iÔEz€®`„URƒ+ Ëô‹trµ¼ò´=s4\2UÓOÙ¦ÈÛg`Ûj€ˆ†«}TéðÒKÍC7 ¹ze¶¼u4*»ÚÌ-LÛÈÉÐÜÕ/:»ºm´äâpXF tÙ=&‚'NáY¶þÉö©˜;³ñK²ôŒ<¢LÿpƒÐ';¤þ¸I  sž2PsÙ-g¹EÚT@7W?’„mX ÊZ üžøqÄçèÁ#¢VÒ‡L‹>a™T98xceèæ½+có Á¶ñwž±A_¼ÿléÞ46¬>|ŠNÅç?ü’PÑG *Ñ ,7®…§…”3"ª!%´àjfx=´œRF ©4ð/×öÎ×öÎÓðÌGö§ê˜&o—zÂ&í¸UÊAZ¨¬i„z-þ®©Û¦áùóƒ·êº§ËšGózbòêBRKCÓÊàAZGæ±I¦æòˆ•‚©W´Sx±äAiÚÊ'ÁÒ;ÞÄ+ÞÚ;JAßòÃuüs¿¢{ÄÙøò^ôÑa : ÈÎgT„AÀ¾O»#0ÑO6[¢ó}ŒÔX«ÄKÅsÝWWû®ß‹Ù±ÉŇã³w¦æVfï<›Zzr¹ù 1ìàß:ú"î>zöôÅO¡y-¿s†Á¥Å!ú :ÁhJP -¨ÈÐ?ÏÐÿœžw¡Žg¡¶W‘†Gž’[>Œ¹’}ª¬uÊ1«ôýFñè/uHß'6Qܹê6mš-ÿ9%sŸhK/‚5Ó%Bw f89‡êXølä=þÕ®ýHÏÇæ_¹v»°ö²Ð)Ú9;ønôï¡ ›lý}Ÿ6ûa/=F3ø²¸i0ì ˜»tŽ-©ïŸ×FàN”}úÎÈÍ{ˆOÞº÷t¶…ÇøpúëâËSá•£¾…Zng‰éöD敧MÙ9CÅ5­9ût¬T=j“qØ<í#–LŽòéx‡wàÁ¼\œ­ï½ZÕ1Q|iÚ ¸SK/n(xPz…_b[xº†‘ãñ3†GOé*X»„`ΈábäFCéª]€M€´ÇÛ¿Ý-adà «öŸ‚²{”œÐÖò-1 ^ù¨ïQ/+|úšŒ«z"‡Ú¯ç-C÷n™X휼ƒ±H­#s=Wo“@nf怾ð³ÐtÔfo?îð®yçëÆ­cªÕS”]rੱŽIÝ=ûŒ3ÙøqÚéì ûL›ô“¶éG͘% åÆÄôBÄ ¼ÃË»{çž ŽyydFÚžöüÆÞ¬êvÀ ?Ñ9õÉhy-ñŒÉqIñŒÌôÊrð‰¡ØúÒìý¡×ÆnáŠ}¾Cµõѵð0°ö¦Øú«2\vŠÈ~ò-çÇßîùr3ï7¢ò{­±Â—Ç0 Ô"„Õ“ó>Øsö{„ ¹ SŒ¢sϵûàE]*ÑK†yÞ™‡%ãójk;0ôˆ£Kš>6¸OÝzeè,yˆÎÆÌª¶È¢ŽÐüv-§”ÓvéòÎ9ˆÃ¡Ñ'íÓ·´u*äIÜ!f”¸A ¸®§°N FˆV``~ëàâãî©Et>4öNcþˆhwC^0¥ôRܹls J)óK(" ™åŸ?©ÿêÔБ˜k™ºRaÉõm| Ê6>o 7CmSW=s/º] ¡µ÷%Ê×»Döž²Ô a5;¥¡Š€Ž«÷©>Ži¬UAÇÖå L.$£¸ÙŽ"&Úч€þ"“˽×î ŠC,ŸÎ²ð÷ȱ•mä‘¡C7ã‹ÛPÇÔ°‡Ý>a›yŸ#ÖéPíc GŒÂA›k8§XF•æwdÕ[„ª:e‰éù£Mqàêâå«×+[ÇpÌrë»wrÉEÀ‘]’^œRæ—ç–ÄÓË.Ÿ³×>8WÜØ© c¦ÎtÖ€ ®oé¡mê¢eæËo`å‘Z.½çê¤ðòú–»eíu#qušZÑÂÇGÅJš´ß›Ú‚lÝAüšw©´yTÙØÃ)"¯müNëØm"£ËPv€N>á©å¾ë+ÈЯ̬²q»y—;œûÌòýë«/òë0J([&J™%œ°L2Ï@Ñê(=$²¨«yüîèâw^/ý0uçÇáùÇý‹?¢ýØ-®¢käÖùα²ŽÉ¢ yuÝ•­ˆÉcóëwpZ©©ŒxEçØ%# c#ŽRZïäíÒúˇdÕT(–ºænÀZÇÌ p똹qS§ üê6 1µ ÝDLèžù¸ÆÏQ<âb]¾¸Á0cúìýAœ4uã:Z§²ÃæÃº;kbØ¥  è%™ïØŒ-âv€eGôî0òó+„—›#F~âæk+?àÌ”6ç\š–¶J:`'ÅÇ> Ã0³°ö‰»S·Ï,ÞXœ¾&nô]›+h›òÏhªï˜­¼4X|i §¦3³¢5±èBtnmxVePj‰oÂ9øn€.ˆ[úÄ:‡¤„é[<ß5å⼓G¼«–‘=°Ö4vÒ2qVcØ«9¨Òí ,\s+.õN.#‰gãŸð¥ÀI> W,›Â°9Æb: VN»e~s:Ž Œ"ònpk8Ø"x§ôdäS™çûšú œ¢xÕ< âÎ8Ä6ßÁôø.Øv …!YCó0¶ðuLÝî°0ò,Üï²p¿KÒ·ë÷'¿Çñ¨ïCi×af(ÚNP’Û§âgQ=2÷#á½Ó·{§WJ/Ä„Mý×ë{Æë{&+.‡gT$”æT·%ä×ÅæT‡¦•øÆå&Áqg¸„§Ù$ÙøÆ›{D™¸„Ðì|õÌœvîr{„Iʨ*Zn(5,¹Íèk1­”©Vj C ÏôsÕç[¼Cã7s‹ý‡°*ÖE’ùôëeÜŒFC½ø7ؽ žå2SåöȾé$`ƒI8×Û<¶Ø3¶„˜OÅ ëÐOÛDÖ.×÷“VaàŽÙŸKC¤yƒüm0ò‹P(;Œ0±}àΆ¦гÑos²„ïÀÝ· Ï]¼§_50ï_~ÄÀg³¬ãׇíN›ÇæÔ"õÃVÆ/… m22+»RË.fV6§–O«5ñJ”Ös²LÓ±Ø(pü¤Ž9à¶ðŒ4r âH±‘j9'£>#ÍÝWÍü%¤•Ô©Äqqè8º¤`Ò)Vîg˰ÝF}­¹ïª½{ˆØaEy}k~ý½Êì Ê\ƒHêGDǃÓÄü&™("ŽB´F–Y1S±·PÅ-Ë2¹í”m†€®'~õqòA…ETcÒ‡=’Üß@ÿw@ÏŠëô,úß¼0°Ø<¼|¾ÿ&¦û¯WuOÔöO7.äV žd†bF{ëIóRô­GL¿Óç?ež[[p!± )$¹$(±(8¹Ð;.Ç?)¥mU¦+z\àµAŸ2œ§!íBò…ØŒiç‡Kýã·Ê;N*ë‹‘W1°âo­º*ÕJƒaë•…¾8\ÅRPué´ªÈÅSz6{kc°Tˆ‚í@d· ¡_¨¬KN±óä7çÇÑÍÚœŒ’¥s“åɧ3SÇÛ§–tÜ×q¤DaÉ9—`ÄÛ&Taò¨¸cæ¥7J;§Ëºn”wÏzØy–ô‰ôÞ`KU×tm×uámc(x•¶Ž]ìGÃRÕÅႦ±ˆ¢ŸŒyÓÐu’´M‡è[÷kªZù»Æ§VÄùÆäyDd¸„¥ØÇÙùÅÙÃq{E1˜ŽzîÀœK“á l`eë™_Õ‚Þ'Š™ýæ=üÇÏèªQ¬€8œ„mLäæ:&ŽêT[K×`ÏÐÄ“Ê:»xÅŽ*hKk˜ñÉÒ95¼áÚ@¹q4oèôÃùÿí!N܈ccF:/ó,š·åí’2¯ŒŒO7´Œ ÊS¹Õü¸ É1JŒœyPaFùÈ4_qÛuV¯w.½´Ž_…”¶O”µM”¶6`ëKÑÅ+EÍ#9 ùõ=¹5íÙÕÍg«ÛŸ+kÈ­mV° Ø"c´n?å/<ŠÅT¥u¬½b³ƒœ‚‘aa²¥[˜™s0t™áà3.¢kénãéàK±ô<¡B9pR•_òøAñò (•jÐlØVj1´tÃZï„|e}sA)9áCÒ'Îh¨Q,$(;iïS÷ÇÈ"ÙˆŽ£«+D~“,+¶–a!Ð%×:“}8bôu×ÔÔ‚ºãfq.{¨9R6LÖ£O ®z0¿õf /]=×|½°m¦ åZQËdáå«-S…Í‚K£X¿–ß4œwa—Óå6öc!·®÷lM7òñôŠË©eÍ`JA§ }ìH•˜œj0×\ÇôІô ¿¬¾}ˆSp²ƒ_´…[0rm†“ÝÑWßÚpÕf:˜»§œ«ïžz€¥O¹5mêt+9 CY Cy¦‚®‰:ÍŽ[f‰ïkÙž»&"f¹~·´±œœªžÝB•áÀ)m¸û¤Å^]Òé„DÐk<‚4øí)8Þ¹ùsµhÅ7JÁ-Ÿ<ÆÙhsÂm°ûí¾:HÛ††‘ÝÃãÇàî1àŸß>“Û4鼋Ws.bs2ïÂHÎ…QHîùáœÆ¡œÆ+g²êú2k{3kºÐ£’VÙžVÖŠÔ@'ž;×ÀÆ:,³di`R²l*x4óIÙ(¬ðç=RHž™»™º†Ð‚ô­¼€ŽŽ¹ûi= 9mSy= ˜wDk¸Ùªe`¦côfP|†„Œ"ðÐðìt{cgëb«Jµâq¹ч®®À°È(ëq°1§t-qiÎ^9kNýè·X³þ­".ÈÌÛFF0LÒѲ+l’vÀ,»­¸´ýù #÷ê†!?E¡\Ô$“ 3RN˜E¤žŸÌhÎnE×bjí93ê2ê®`ü'£¦’^ÝVÕ•RÑ!‘¤Ò–Ä’æø¢‹ñç.Äæ5B¡Ô:4£< ¹Ä8Ê^‘™PgˆKhªsh¶Œ¶ífáSgÎtG˜nM—Óú¶ÛEdÖqT¤Úmæ;òÙÈaE*&L­¡_½sdÆÑ/\ðÐIE=cxv%¡\Ÿ«°22¤á@œnãSX×Yß2hjï½[PêØ-mc»CJŒ]G ¹”œ±m’ 4w<û„ýýßΣq.DRòá 3Þ<´¼¨õÚÐÂÓs­³RzîRÔ@=ÿ2aƒP²ôÒ$7JˆQ‚|r»ÒÆ3ꆰ¬ó›˜4Iªì¨æ„’Ë;!Iem¸²*¾¸%®¨Ý)ÑùhZˆÌ® ϬFµ+0¥@³Yqp)åÀ FJÆT°¦JLãjÜR§EeÔ6 ýë>©O7 ýe›Ø6!ÙÏ6 ~¾Ià/[…×sŠÍmh½2ÝÚ?U\{ù°œúº]œâÒ‡” 4ŒA¤ÃwãÀ `c™wkÄêòZ&¥•÷ J‰ST§ZâöRß%c´OÅ ùï âü´Hì¾ ™¸A´mTCNýJQˆˆr«;ÜâhÉ¥ÝK6QU¨]ÂÔ£Ê ÁŒFÓi\å`beo|ikF»3¶¸ 5y,ºŒ˜‚‹Qù"rÃsÂÏÖ†fUƒ1C!} Ðh7Qu†"°{fã ±ôŠ‚7s  Ûû£­ ÐV¸lcŸnù|›èŸ·ˆ|²ž¸¾Qˆºžw÷aü¼•[È>‘cüê㯷øÕö/¶ð× šÎÎÄÉÓ¬½í¼£éÖžàâäTõOª*ê™niuã]‡4Ñ´,¨öV£ÿë“ߎv³2M¬†ƒ†éFI+¬´?ª¨CDÙvûI+YŠ›wZ£¸šƒ¡[JZí¨à[A½`’­Pc%éá¾g/C»cJÚ¢‹[£Š.G^ÆŒv乿¨ü‹¸-@‡f×…dÕeT£) µÌ7©Ø7¾Ð;–UíŠÌr Ïж~q‡`Jµ‘SÝÞ¸€TA3N*ÝFßÌMVÛC_lýíâ}øiy}[ 9Ý/6ñ£ƒ…ãOë9>]ÇñùÆO¾Þõù:Î?màúÓFÞƒg(êÆŽW}YÅÀLEÏá ¾ÓY@FgÇaýí§í‘ŸþÆ`ýÞ&MàÄ1f…=´ ûämöjŸ±CÏðYóãŸð¼Ë‚òf\j^¤ó–,J gõ ¥­˜âË¿‘ “Ú¡9dö'$«>S!éUþ©X«è“X„]‹±ùî‘9®áÙh]GŠâ&‡R›º…:Å1fçÇfϨ–®D=œT™ö EQ?© ½þófÁ/¶í“TÈ­í_|†C±únïç[xÿ´‘nýÓï¸ Ÿ­ãÞ)râ„¶Ù‘ckÿsûŸ7ñˆŸÐίn»ù°ì|·²®1Ü·Ã'ꨆéfÑ3»OójáÖ|Doµûø¸~ÿ„K©¢cá‰856 ½)2³î€¶#Ÿž®´ÐuIâS°Ð°ŽÄÔžÀiSM?nÙ®Œf0)Š_VCDá¥Ð¼&HPÖy þÑÝt2ÜG€N*Å8'f¾Ü¢s]"ÏbÈ18S!˜B/1jÙÀÚÄ5ÔÈ9Ýh Æ ­½@ km&!L–ƒ9A6,{ç~8î/¶üÇ6¡ãjFC7¢(ß>†û0dòkL ˜{E›z Õ06éÕÖù5Ø3’O¡„ŽV Nj.£aòŸ;Å?úŽNüÓí¢ŸnØ')w®¶¡Zimëá~¶6ü“u\Ðq­.k›ô?¨8£ÖIXøvn=3 ë ¤!_Æc{3YàlCXz:ÙFþõJ¬òf €×¿—ýY·~`A⻥y¼#7þ@À¾+„~Ó‡‰g FßÊÛ7õöH£ ‚ä_ÔxÐéÿb{ÜjY€K%Ò j êÚpÓwTv­ˆ¼Éö£Æ"Qø›±¦ž×7ÖÅ iûXEÁ†¦Vc«*ô“}D¯c €5æûòíC2mS0ó… ̘yF¡­”éBšÆ5º ÙXCµ7;ÐÂ#ÊÙ}¦#4ú‹m‚Ú$ðñ:ž?¬çûl‹àGßî<¤hát@Z•ãÓo?ùëN6ÖŸoâÃbÛ× ÿÕNÑÃg =³ô¬áë?ùz7·„,œ¸¢./+¯o‰Ø·c ~Çc€•Ñ Ðÿé"8ÉgÕa[)ÊmäÂc`äœNöÌ“U󰇄Ñ""@†ñÉoy#¨Æ!ÛÞÜÆžm[„­˜ô4Ö‹°_êõãÛ£Âz‚:Èky‹,û ßøˆÌàÕ „vãoýȪ$&áÆ1)ª¿MD~PF­wB1à†`*zí~Ö!4 ãœÖ,¬½â0ÂnG»8š Ñm¨gÞ3WäPl ÙÚ Õ†É%VÅUNÇ Ù÷îÝ!"»™ÿøç[øìÚ/·q|òÇŸ6üñë]ˆÓ1¾ íÆ‘#ßlçE? 87œ±Sºæû¤T¶Hjì:m‡‘a¶e†Òý¿ Ž=ótܪFöúþk¦kŒ¢ ü;¶‡á<à`àjNì ¡G „ 2»EÖæËý´O5?ß}äãm°kŽã;Žuük6ð¯Ý(€GŽ-D6‹sl–䨲ŸcÛÁ5Û¥Öî8´f»ôkÙyrí.Ù5»OAÖî9ÍÁ¥ YíÅÁ«»†OƒÏ` ¿!ä5ÜB Ž7BÊâTø,qz6(¦Õ÷¦V´…gÖ¢i“;¨Gƒ×²õOç<Î@?Ë­¤×OEẠ̊ȧæ¢dŽÞfˆMn!LÁÐ."4t k–jkãìZ6ñѬíG¶v“G0¢(‚ P‚(j%æ.! ãØíÖ6pÕ?:ƈü“à|³àgù8Û’¿ =ÎÀGßìæøã×öP1¯Ž\™î,xBw“¸ì9·N€âÄÖýóNœ()=K€–‰ePäþh®q:`Éݬ˜KE³%D%¦ïÖÓ6PÒx×l‘âØ¸Ÿc8Ç·"ß s¬áX'̱^„õ%ž‹Á—,Y³Q˜c£äšMû!k7K}°ådí–#Ž'8vžäØ-DZçÇ^N%"\ÊœjlСæ@ü5ÜÔ_œ¸ ø,Í6ÉBuø°Q4v‹(˜ï9¬¿I†¹MÞv' g*¶gë¨Û‰hú¢)ˆ¸-ÔÚLX g¢Â"FàUrÒ·³ ÊD§Š™w†vÉ §+àd«64øå7ª pIXÎ’_áÆ÷¡Œ CQW¦bh‚zËc2Ë]üÂ¿ÞÆóá—›?[ÏùÑ·ûXùšŒ<è·®œmÞq>Ydz•ÿ0zY±4†t»1œÄOÓ·ˆ©l;fŒ ±QŸD>,þÿ à@œÕE,3‚X1f4®ÜÚ­ì²^Æâs>ù?î•áØz€j»À qläXÏ·vÐÚuoeÍwüŽoù^CŒÖ8¬c°fƒèšõû×n8°fÓÁµ›¯Ù|˜cë±×²ýd‡Ç.yŽÝ {”ˆIß§BçÔ  Ã°óèCÇ9ø)Dh£æ‚tÒìd”fÃ!j*—VÄ.µ~­Ní0X¤£ÆØœ4)­eÃyÆžSÆÆI°Qˆ Ø÷…Ž£"¬,®À 8Ešû&™xÆÀw»„!H¯ÂÖn °½6¢2˜nȸߺo[ö÷aÕ©Ö¡Éùa)çtM0è-¯mº•ç ÇÇ_¬ýÓ×àQ?]¸ªÂí/IˆþwVFˆ¹M\VÓ ”^Á¿Ä)r¿ùA]ÜcΫ׉‘a–ó%ëâI˜ýOúq}¯mŠ6_Hèpì–æØ$Á±AlÍz`*Äñ-?±Ïß ¬ùNðƒo…!~#üè5Í7˜rl!²ñW½âk6ˆÿ*›%×ÀžoÞOLú–ƒÛ¤8våØyìƒí'‰ì”ý`שw+|°Géƒ}ÊìS]Ã2él«Nà†ñ7&¶º Ø‘>{¸âqLR£OÛe–õßËmW±Ù£à@¢¤$.e%,›ÆEMÐ Rw4pµó‰4³÷3q ¢8†hÙêš»èZº‚ó„€FS¢Y©0¬Ô˜Öˆ£ð¥†½eçlÄUiŽÅMðÚCÓ+Áñ9Ç”ô¡õ ú‚‡å÷ˆÉ|¹]@¼Ü7ïçøÿ´‰ëÓ\¯ß÷§Í|°öðïør÷#ª4üF] pwÖ’gô¶HœÁ•X»e­xô°ˆ\{MzØp+:. &w¦ÓR)ä~Þ ± R-œa)Žh±‚íЍýaÇaŽâ±î]aièÛï¼1Ñl-þ‡¿â»A¯ùF$aÒ96|-[ˆ‚¯Ùv|ÍöŽí'×ìåØušc7t\‘cï¢à¶çaùq^ ?NY «.@gËAÏI+# W«“‹$`è€nÁç¤$0ÏÀºëKïd «>egAj4àfXRM¦ËñHü.¬¤câÀMRËAÓÂ×Ü-#ºÈƒ m½Ñ‚‰0*¬1–Ž“:&º`lÙšŽÅ tü¯ÚÖ‘¾ÉE,ëK=W-~\ù”S“E·"y—Õ6%½‘pƒwýl‰Ò!€û“ ÜÐnÄl`iÎPl´ŒÝtÍÝÏØ:Cß'¥µIRw/®¸Ò†j#ì$×óÑR%ð^pý47ßáÚ—dàNú–YøŠ›Ä 3"9uü¾;døïŽÍRßBEYöV!Ö»X¯z¾Á÷ÝçoP²,fýwXìwuù¿` ßMT{óAâú ÜGÞÂMß ¸åÖ°à^³çÌ“®ú6lCäÆÒñÿqržiXjDx'”ư‚,“`œ.i’¤æ”ê–Û6²xahñÝOÇ—eÈUqì@¦@“˜ØùFH†DNUWÎS¦rTwºCŒ³9Ú\Ôiöš G-¦ž UÓšÆÎpÖÏÔ¨¶ø¦Ý^û5è4»ðÔÌš5÷Œ›9øðJÊÄ™¤¡~AZùÇoö~íÞ$Oýgð«ëø?þ¸óýñ¯û𥈴š:ÓQßÒcòÚfb'ôwï×Ü$¡¿UÚŠG+˜´ñàn– ‡a'=~Fi ))"ƈ¨c„¨áœž_I²lµ$l5Ô.$;¼ MDÝ›ŠÙ‰< Ô™ä&,h:RQ\%FzÝ)QhOÝ!m|PÃV×Ê[ßÜŸ<¢/Mº=ú Ñx†â¾$–]I ü«3N‚LNXr!:p ˆâS¬,]ͼ¸E£ÜŠÀÍ:¢'4 ã àXˆsýi=ÿr«j(_mØR¿¦–䄃ï¨Ò&ùuâ`Sytƒqÿ&L4¯Iè&ô'C¯…pÑ!2e£ø=ÔÈïöS>Ú+ϱñ‰¨×‹q|'ôÁwüX/È‚•€ûÓ bD<¾QjÄÄÿ²Áeëïk 7HüŠ)Ð|+D…‰ÿʯ>BBµmÇ9¶É > 7Œù?€›[öœmÒ_§fì°íXu²Îˆ‘ ÄÛwËZ§¹evTußÄ^Seó =Ç©;rÉ2P(±HÝ« nŒKKãˆC'ãël7„Bë1„K š~;d̹diGu¬@bk˜8ÈèÖÚFÖ t&XÀ}#Gþíö KGùð{ÅõŠ:&ˆ®1xLQGìÈ©2gNkÒ5éÄþãœ@sEdÔI`Fœ87 øÿºT ÷Ó"Òª-ÑÀP¡¹³2Ãn¿‚Þ)¥õâg¶£íQpâÖ·–ÀÃHä1"WòºÉ(–¶YÑúS~EÊ×P^áµëD ß p|uú¡5‰¥Å8Ö±d½(ÁêIÎÃë(ë5²l |ß…/K >ðZþ_h1"p„³!~ûø6&ÖÛ“\Œ¤c²;Oß½ë4Tû]íæàz›”ýš‰³8+PíÄá¾ßúqÜHˆ(…´¬³X#qzŒ²s–on—‘K?Zþ¤é +ø´ýÅõ}=“«0é#¤áFæúi‘ ëX÷Ü‘jØW4=•³Ã!X nÍ€}ŠöÛŽSOQNè[«°BtÀ­N·@̦L³@«MJ+é›\Æî¬Ø=Ž^b5*!a¨ª†æ4ÒaNò5:ÑqE« ÜRH͹Á¶CP å< §gåfhìÏIÛÜõˆº×qÍ­ûϬ;HÙxÔd²N8®=bÅp^‰üzß6^‹àç[IŽoD?øVÈbÓ(Èšõ86 ¯!Ô‡I¯Ö·ûÁ&ñ6’hœDàë±ì¬YÆùWýý¯š{ˆcó¡¿Av+ô—¥Âoñ}û½M†øë_±fÅi$TS|ª­áTe%à,¸¹YËž¿å^Xpÿªàï"U%yÂ08tf*,6Š¡’”PÎÓ6»NZòhøpjcg‚]foÖÅëy™Õ}Ù&p•¤¶fø QBxŸ†Ö_$§ èIÂŽ`Q½^0ŸºÇÞô]‡uxOè¡Eäã [ì_òŠÊ´tÁsE]sk÷plÖ­¹ÔëÃ#vø˜Š!¨uüQy%Š5IîŽ\ûO¡(ħ}¹]ø«Âû$d5M4ò/uâò†Ûö«nÔØ|ÈpÓI»}j¾BÔHq£8q|×_Å 8¶øà?u‚Ý&ãü(Ç7"lF`†ÄYdLˆ@£Y@“À ´Ø‡›ÄÙ‚oÂ2¼ ±Ø&úµ•&ÊË–¿ÓßíG9 ÄJÿ°}ô»(¯Ùy‚ðìµ –AÚë8 pÃq¿öÝ7›{a›ô_ü]ĉ%'z2‚vNFÖ3â*Rƒè}zQÜúQdI Cˆâêû{&Ú¹‡a™FJUHñ!íÔXYd’WÀvGz/Ü"‚@,m¥&¬ã/¬éίd»ï¨!Ï-4® HZÞ<Øý~mÛº`ÃÑ=Žš¸¾±=—ˆ”ðA9]Ó3TKØgXr(;‰êM]ÁŸì?©õáW;Ø©÷чÎPUè wE-ì˜*Ö~¯¤ÂVQ…-Ú[ï•wâÃ…Gôð=nŸKêpì<ÂÊ|?øŠÌO´ëù µm…^o‚.‹­]/±v½øšub¬ÿp½OâD×n” øÂDo÷lÏyÎùÞùGwÞxÿ·¯½}gûÐÓk[6|óØK?ê81ŠáÍŒº£›Îüý/þå£ýó¿ùÆú®•Åjvt`Ðu¬×@›4=Ž  Ð µÛ¾“Û;÷C©œšW›Q² ^bcÓPÙÖÎåõéeõÉ%õó×´'Ut¥×ôeoÛŸ³ý±…åh7À]Ã*#‰…‘y¨•)¾(¢ÐðÒ~`ÜÌ/šlR ¬iÅikí1±ÅrÈQ;ˆ‰òÆb㥙}GYؤdtݶʶX«—GôJšqÓÎn›•ÓŽ#ÙZ'a¸Qyl›^ÇB<·ÿ&‹nŒ¤õ]Æv\Üù·óØ…‚Çšö?wá_·}ãÝ;ƒ§_^½é1 6í^¼íɪޑïÝþÓgÞýá?{ý'«ÖudmZÝp°¶ý‰¬­ÒëOevžÏk>µµÿ44[ZüujÉ72«Ñ»\Z±#§¶yEe<ÎúÝyëwåÕÔ¬ß)gWNMcjÑ#Åë÷äUí^YѰ´¼qaIî4RÝ—\Ý¿dËþÌG¤ÖôÎÛÉEÞì¤ü¹è=!F/(¤Ce P I|´(„_Œ¨êØš³å@Ô]“ 2DÂñâjÏK‹VŸ¬»l¨bëŸYGÛ ,஄»ÖCêŒN›Y™‰Ôë¬&8pœ„ì=  ·ôÈë0âÇïq„uôQn#ˆ½vû½_ýaͦ–•›‡V4 ¯i;óîíÿø‡;¿Ï\ß‚%‡Ç®¾ýì•[+6 ì:8üó?þwæ†Î¦ÃÃoýì÷G‡ß<6ò}îõºùþ£ûέÜy»At óOßr8yMKRykƦAÜ&½¶'µº+µª'¥bOJESZeã’uxÜ•TÚRÖœVÞÉgu~!imwÚ†¡ŒGbÆhÏ‚-‘Ô‡POE•ð¨÷F²-¹–Ø5m¸#"‹]Kí…2ŒÚ¯ž”슮‹iË:²'»ÂŠØ, X ¸>¾b³`KX-s"È ¾Ñ#L[”c`mL{¸A±Þ5âP§CØŒì={ï›Uû®\ýÁíÓço>Ôp­–šÎ³ïþòßÛ÷Z²®uÙΧ:Î}÷Ê­VmìyüÅïâ~Ái0í­û0ñ=ôÌÕΧFë¼rø[7ÿñãÿÌÞv0¿u˜ËN®fv0“/éy1·å¹¬æ³8ËwžÎØq*ãÑ'2¶ŸÌ¨;Eqú–ƒøb,Ýr4ãìÁ>±¤î8vJ,©;–Õx*wϩԚî¹ËkØ¿€9/È‘ª ]Œr#@|ˆçÌ=»Fn™.hë± wPWEÒaÎÊ~؈l}5ñÕX윳…Q80^â+t7 ¦ ÄÁg"í.äø+—âÌY/Œëîûpgݶue$ÒïqT^è§@³‘9ø¨Dj»Ï`‰ßkï}üø+·04zíßânqi뺊;_¬;|å­ÿ®¦ûÜÍ÷>Ú1p6­ª¿¤éÉš='r\µýd棧+›ŽÝúÅ3Öµçï:SÔ>’ß>‚ `kP”Íf/§ 7½”×þr~îÓÁÈrrÇ·p%òHAïK¹-ϼþ“.¾ùãÆ¡ã©ÕmKw>«S`;‰/z7P‰|Y"JxL4£/) ,ŠyÑ7’²íP$k+Šåȼ‚9‰y÷ÍË™›˜ Tg$iÍì……àEµÁŠPI¯.ø*C¾h jd&Ø4mp_È·M¥<+iج%58ÊqyXGUÄ̸V¸, ÊFasÖ–EÌgkËÕb*À %Š”‚“1C…¬X èJ4 1ᶈñän\ÄB<Öóp¹9{†= ~ ¹7°¨éøÿí¿ò«[R*{s›‡Áê@ÍUÚñdeÛ‘ªÖ“ùõ‡A³§m9šÕ2ŒN sB´« ·dƒ’>DÉymèdö¢¥U²ÝÌçS×2X#Ó:ÑLØyG´ÍÇb!´…ÖV®èÌ£ÅÛ<&(#õ²E“6UXC^¾#‚*i¥[C~`¢0/DOh½4KãðqÕS€i_©Õ-ûAÅÑ{£T I\&ÄúS¼z M<-uPmºQí',Bç½ä.ieÓò­ßDäe#²c$¿å[`×!;ÉÂî Î øeFg´ã!-#ü-š­ˆ×¤jG±Ô½¤ÿRAç ‰5{#0Àùä»÷¬y9š‰knåLLEßΔ;Ü–rÞÛbí(k“ƒ±’¬âZ¶JAºÅ l¥&jÅÂxŽÚö¦Ç‚ëò«¨‹(~ü Ž:[ä]ÇWv»^øé[¿üägÿüIqýÞäµ­YMg0=‘Ý7Š›é «_Ý-Í¥üΈg ha7‰Ñì´âŸŠáÌ/¡ï†¨½ ¬>XCóƒ|ìÙèZÎM"Efí:LŽÙ:KYnlj9×­lvPU™.3ë)ÑØæ#Å$&ÁV M-llç¨Qûu1|µö/e›YQG>äFÆqÅž ÇÚ=?å6¹&²,òð؆ÔuyÓ¡ëuû_\ß|(¥ºgÅö£¹çrû/cŒ «ø1‹QÒ{_’´2¸”ñÒçw]Ä®!µt‘p¿šßñ|JíÙË…EìS %›— :ÅÐ&„-ic¦kië,ÁZ:V΢©$cTÀ¢è ˜ÒÌÖP t‚WkAYÄ‚¯—nE¥Ð¡ÒXn8ìŽ7Ôï@ËÜXþyÊgðE%E‰;„ÙWÙˆi=»jÛå[Ÿ(l†""OˆÇˆ2u7í½PîœK%EOA>“4©¡8ÂÈCï¥Ä "Ë6 Î¨¸ÚdÉQO"m,Ô_z¼´_P› Ë«£ÇMï-®ÛTÍhã´MŽ8íæ„ìW(ù†ìœ¶ZqàŠ' ÄJ‡éıÿÌÏÇÊÐb=˜ …zuäú®KAhÎØõ\vã %Xë ! LÛ±ú/ã@G$šIÌÜÁÒ_‡’Š!¾›V‡ŸºåÐìŒdG“À‚¢ŽÎ¥ƒHíkmX4²¯e,²ôhbmÛÐ~5 »Ö|,Hɼ­”µ©ªL1å0ë´mPvÙ—ŸeI«Bñ…ŽÂóº;Sý×ÏŒ Ÿ‡ßÍu,dc=_1t“†ê«a˨­XFañ(ß0±Â¡^CîÈ9ºn‘>â¨aÚБb'ä²úã÷emG³ƒ)‰ÙÒžÆhê¬Ræi E–€r{~1Ë+h¬MSÃcl>‚Ü–n#½·¥ÂB ¸m¾ms°1¼‡ô&ÄŠ; ¶±x ¾@|ÜØ:e#>‰…l,/ëùX箟…l¬çWw¼ª{æ1xŽ›ÈP Ì©LÒHaÃT¦Ò@žPú‹RÚ9m¤òö\*¸¼j÷Ó_)lcV†ƒ¹›Pq˜1¬ÙØb!>XãIkÔ^ "Oj ¤©A¸Çt–•*‘|,¦9­#Ñ™N›Aµ2!ÒžP¸5Ë `5øÚ1®¨ÝîrÏè…{þ3^DýÙ{ÿ1²÷úú3qÌÙÁ–QV‹*ñËopígÏ¥U O}5¯^¸PñÞóÐû(W¬µÊ6—I̤a­DŠÚV[tà*Wæ õ¢›ÞÛè·m2¢:•sÑY]7IK·ÃZ/|”ýë8šCˆ\ÎÏ¢›÷XA•ýÊüµ},çA œ—˜¹Âœ”R,>"ºPib7ŽI+k×–+Cn°V–ÌÕYbÔŽ3¿­@»ªJ/\me±fáì±Ó(“£«§Oɸf<âk„“)¸Q0xƒM±î ˜S»oÅfJæi:dº Ülb ;J¬ËMz¦¦ ¬Ù¤vñÚð'ZSÒ pî-®;H¿}ïíûpëh ½bGÖHÏÒîéÄÎ'ÜêÏÕ´ñ;Hä4—ãJ·Îóó×¶E.t¤ÌÐÐË !—´œ*ÓÒÙ©V"ÓÓ*×›U¶ŒÀ uÆô̕تØ×Ê >4¸—›@Ó´µÂrªéjÅáþ´O`ÄO.ˆãGŒ*<Ô}žºqúð‚È×sïcøÊ@8®Ë –RŽÃk˜¶aÏ(W SªU¶¯³#9 ªñ6©–Øn×á`DêYÝrdøÂwãŸöÆ'6„/ñ¿ÆBÜ=¯ÆŽwfûHÂ2„fÌø`r³˜6Îí ¬èBO"Ü霴Ê@0,p£…íw@·Ñ‹J*NêÌ ¼• ãÀÕ®s÷áÆuñŸ¸2èÐÄœ!ház+Ì#P“&÷ÿBS ‹û ~À°ÉÜôÒ ©M™ x9\Fg‡¦Å©ljì$µfã¢43e—Ò¤Ú¹É÷ÄõuŒ×Gö^?ÊE¬ˆG ® Á!” (¾0>@AZßÈ¢‡ÿ.ƒE æMðnò*ºFˆ#-7pk;Ûò*:çªl“ £b˜R“žy°8^=Žø$}€îYe¥XªÜõ*³ñJ1‚½£IëzÙQ¡Œ„ؾ6§Œ–X˜4ò*”ŸQÿÕ‚]tæjÚÓ‰˜¢’<7Lg<¹¬ƒ:ÚLÒ<1’Ý$‰œÜ47M¡T^šl  yÜOâ'|ÄÕŸ#‚ã P²ïµ¬Îs÷çlÃÒ~ˆÓ0„C ’´DöGg.Ûb±õˆû¬À¤-Þ`r®›3p[Š˜¶4A,©"L©-´îÕØ¯¯ÇYì0éˆkØ8:¬p'”4ÏÁŠ3”ÛàfiYB 3O©ÔU9 Óx°¾LûÃÀź-»b¬ÛÉÏŒ'Wå°phnµh 7ÞW€¸>é'Û¹Mâg8½þTÆ·»_Ïé¿– uðµÂÎç¿–¿@«-Óœ5d› ¬šÄnÝ­Sü1ÛÉd)™6µÅºéÃq¯Ñ [¥=¸Ûß­÷¬q@§êàÜòók˜Æ¢•û ÷@iÆ%„Ñëì”U#ÜjÝZ‚Ñ™KZ.Mm]}cnâ€Î‰SÅ:ìÉ»}¸ãˆOܰ,ôÂ@¯a w¸ûËüݳ–nˆ`ÿª-–]Rkë#~Ôc­Ût·¹/…Ä© Ü&'1ç¬CÖÍžJ0VaÎ]ëžåîODÊ‚ÌüÂßµpS6«à– STy¨”›Úõ Òà–­þ¢d°s÷¶!ÂÕsFsbMm‹Ó¥â>âq¸§ðÈÄdèů•vÀ9s‹,^ɺµS’££†œ|Ór#*Òr…;³)Ôk…Ä­{ vžmõÀÅ¿]Ûõ^+ˆWbå¦j×¼ÐÁéq³õÉíß.'çîÍÓ@œŠ'•ÛVYj•*A—Ó½€øÅó7>Yñ×—¥ÕÂ~ç±k¤¶D¡Ou+©r,¦Á-wëpª«7½Î6§"¶$¹£wÎüóøzOñgûýß­ÄÝñx7ÿŸNô PiÐ734ƒKá}:èÞÅÒÁú”E·îÅÂ/[i±’ßž-Ô‰#þû¤·¢,}m’äö¦J°Ø6·Â¢ÛI‹®Xw,ørU~ÁÞïD_þòR¡HÍqúvcé$N¹EœG°Vgn›#n]dgæìz:kà¶ ›!ã4z›Ç(¯ ÷@§GÈÖ„[dY•¸9G§ü¬þГ›’c±pãb}3ä¥ÊŽº‚«¬©²+ÌÓ¶ØI+ƒ~>Üì'÷tãŠ8ròòN¯·©.ZzZ:+/Ø»9vTDîÒ"±y{aֺݴ—"n­{z}3êÕ¢½Ð¥‹BI°–ŒNoÝ"wu:âÏ[î@ÏX·õç3êœvoV§~ºnwwð–ŠÛñÊmffÜÓîÏÔŒŒ]lµtãáµÙf©·[hÉðm,vÖ/Žøtû樉¸ÃÑ ‘uâlÉ×@ÿÕíš– }oÅŠ[b÷t{ã37«Ô.v¶Ü”G¼·µhsW5· ©_wñxªöÿ+Dœüo¦jT€;e‡>stream xûÿÿÿ‡svŽ1Xb`8ÂL G˜Ž10 âÿ ÿþը¦endstream endobj 46 0 obj<]/Filter/FlateDecode/Width 28/Height 11/BitsPerComponent 2/Length 76 >>stream x[µ –®ŒZ5uÕ’LÑP©‰aK$EÝ–¸ †Š©@I•"åâæ¶$S0Ô‘-liVch˜ÛªUSCW­ ƒ² Íxendstream endobj 47 0 obj<]/Filter/FlateDecode/Width 715/Height 279/BitsPerComponent 4/Length 5191 >>stream xí½n¤J†Gr`Ò ,m|¤Í-ùö N°Ò—žÀÒ 'sÊÉ|Ù_ýtWW7 t1À0LáñP /4ýðºh~ÆóýíÃæúÍk𠾿ò.pÊNy{Ôá^vÊ{Ø£÷²SÞƒÀu¸—òö¨Ã½ì”÷ °Gîe§¼=êp/;å=ìQ‡{Ù)ïA`:ÜËNy{Ôá^vÊ{Ø£÷²SÞƒÀu¸—òö¨Ã½ì”÷ °Gîe§¼=êp/;å=ìQ‡{Ù)ïA`:Z½üߦágÜf«>.wÎq+å&Æ?~ü/R²êãrç7Rn´æ[¤lÕŸ®´ª‘r£5ÿDÊV½lÏ9ƒ6Ê­ÖŒ”­ús²M­j£ÜjÍHÙªOÛsΨ‰r³5e«þœhU«š(7[3P¶êÕöœ3l¡ÜnM¦lÕŸ“¬nU åvk2e«^oÏ9ãÊke«þœ`³V5P6X“([õÙöœ³0OÙbM¤lÕŸ“kÞªyʵ ¿þà”Ûä‰>ßžs–æ)ÿþh>#e«þœ\óV9åœÇ6%§¼ ×|­N9ç±MÉ)oÃ5_«SÎylSrÊÛpÍ׺ˆò õíÞ³ÞTOnRŸoÏ9K&Êïâuùø Æi¨Rf¾Ç_Y@ôçäš·ÊDùB°Þ5EÈ Ô~ˤ Q&=Fu}¾=ç,Y(%™"Ds”i§ ž½üQÕŸ“kÞ*#e… Ã(Ùö£æe¦LlÙÕ5}¾=ç,Y(snEÂe)`”2ꃗ9dÔ¢?'×¼UN9ç±MÉN9ø8ú9&ñæï8%8˜rçŽðNÑoÓ®c­Õ)ï±?œ²SÞƒÀu¸—òö¨Ãâeèî û …^0÷)¸Ï›lêcìÑÄÔa§Œ'Ìc”ññå’2_ºÓý¹Ô“‹;Ħ›`¡ 6X@M,ÍF†wò2nê€2êI΋†%D¿ió²reºÞI”ù(…ŠYSQ¾àE9Þ+ty®ª?ˆM7ÃD™’7)ädÀSÑ›´¥Šr“~Óædå ('¶‰7e βÊ3úƒ€Øt3–PŽpuÀ¼Ì:¤<­ß´yYùÊ|Y²÷8-åÐaPžÑĦ›±„r¸ç‘2‚þüƒOÔþÌûa°™Gô›6ï +_D9ÀËFŸ#eðóÐË™.D/ ..X2B~3PŽú„ô¼˜W£üRÃÜÜèeÑ'Êß§Ååwz"‹ELÏfÅD y6ý@.†Ç68*Ÿß`ïWà¦IŒùëZe9PîÒ²õ£‰=“m^°ô1 E 6…ˆ3s®PFºH?"HQU?ÝDá—qërG#BW”/ ¯ÉÓÊH~ù…ð“¡Bæ!e†¨ªŸ¦ü-@UÇ® *I™)¾P(Ž35m8ÛN9€&Ê (ŽRFa¤\ÓÏ4N#ŒÿŠ2v4ÒŽÀ¾Ÿ¸)ÃÜ# ÊÀ jm”kÐ#à"ô–žóј!Aw0Uc½vVD›†tx¼/k+ey:ŽþúU¯¡êeœÏýn£üM˜1ÇÒ€œ5å¯k‚ QVÊzÔ÷ä¼)eèZÊEâFÊŒ9‘½N˜7#~”|}Ù]ü› „7ø»gWò1-Ä8³êeìY°†úqÙL?»°‘€˜)'Ö9ÏPÊg%_X)cb Ô0ÏV©e{z~aOP×9ÄVʈÙNù0ùÂLYÈr§¬F-£ »%hèÈWÓ7xÝ œ/pð{…|q½Â9 ™Cú…ç½B^†I¨=N¾°RF3)²r$˜ysÊ€𢣑6¥„}¥_˜Š“`†´p¾Ø5íÁD¦¼œúáübàMØbåÆöØ fÆÐÙ@¿B"ެ/O “‚Tt‚Ò5V²¡ÌD |á>Š¢¯ÑÊ|ôƒKre9AL{ˆäI?Ý8äK½8fŒïˆ¸¤ÌSKÊ|L<k eèŽÁ1 ,Gó $–¼ NG-ïx¯ëÇ(÷ˆÓ/Ë)'ÖcUm>ÝB[Í”9PC+kÊòQJУƒéÈV&/“¾Öľe< ’—9 ã;Gœ—qý¢ç³axù´VñFÓ,”t%!;SûË´¿ãÜ8žÑ×VBÆý Ç4ìfpÿ(R¿âCHÕÃ<2 µÙpß#áʉmâGy™­9 <£¯S¦3‘ŒÓ-…ì²éH…[N^B9ÂÕ 3oçò´~¬u}áÆå”¯—±:öš¾€²\VVý8ôrȲÊ3úñ––Ycç»&äи%”Ãuù” 0+«£_LÌÓúqÊ«dû&ä[( >|ÆÃÐËJ•BÑOP†ûvËü+KÝ;!¯N9Òj¥õÓ㛲ÆýòÚ”ÿ´)‹>.72¾ÍÎã寧¾…šU?B7M¾-k.œî÷ "ÑíY²°• ¨éöbHp—R€øpäJ$9wc•>*e9b&S5qáô@‰ ,TŠˆæ]mœ[–rèþm™Ñòê…2ckGÊ1k¤rÁúq)æíò~f'^/(K±K0)kœ‘2Þ2 ”t‰õRéTPÖPØÓ€è½L½ì¬1kz‹â0VÊ],ÓØçÒÜG¦¼é™yÖ©.©r¯¢LpÖÀfQ…ò õËʦBÙªÏ*ƒ£uQ~bI™¦™ÿuÓ2jô¼¼µ}É÷;ÿ§\(þJ¿yóþ²nÃö1'fM¹ë2oCße²aÊü‰DÍžfpBMé2Ì$} ÎM¹jðG ˜uYfU1+jòï%²›£Ìó)³ åº^jçàA3y9·.LªR®þãìe`ëÌ\ó2S¦w¢\ÕŸ–2ô§ë”k˜5å€E Œ˜*”õ÷”1eÆN‹ˆþ”éß PûjnÀög(Êï-2k¡ŒŸuƽ>ˆÇ!A¦$Å[¯A?hÆ AÝŒÇÛ2À\R„Ë^ƒxSéé?]‘‹IXŸ–rWRž(—˜5ú¤¸…2Â}ʣǺÔrrÎóo¢ 9âi(ç=Œ¸irߥX_Ç€.Bð&’‹¿œRžU{…’òÓPΨi‚#q†9Q{qÊ#À`ro´2.Ò¥Õ%Êt“R<Üäe8y/Wzg‰áH¤0'Êœ&¤[ê•1AÊÜÇ€ÕezN2¼ ¼çúb Æ{?…ðhÅnÁ%Ì5ÀŠ'pc”ñ!ñLbÔç= š@{%>T®6ða)«6´‡‚9£†”_Z°4Ã7¢†«Oz€–ôœ78äED_lÒsQ–Üœ¨žHYŸ<'jdͤO—äàô¤¶W*Vž8_*vÇIŠÁU‰ãTWÖ8$ÊÔp«¾ µ¶—/ÅúZPKl_wA`kZõE³o¢Ì·".—NVªïÍÉ2÷8ÁZ„«Ó,Ræm¶ê‹–ÞD8âê:µNíåË’î–Z׆á€ÚØ¿—øöü©~ÑéÑü˜šeøüôŦßFù›lÜ©u*Êp@—cºR"R™9Âcpü%ß4¬ú¢‘·RFªZgN9›¥Tw+ÔÄ* /í~«SV2 E_4ðFÊš^2(ʪ¢HŽ´RƇÃñhÕm¼•2æxŇä:BËl),ª;J±‘Ú?ùÍ@YôE;o¦ ,;¼MB~¿„t|dÊO}#5ËSâ¢_™2ÿ€2¼*¿8æl¤:ü‡Ù`~x‹]f«^7»×{ ݈ ƼÑwøÎF&¸øv`Êó€IÁŸx€¦Xõ°ˆ 7Se¼b‹ë?6åvkòéŸU/„1XrWPîBǦÜnM¦lÕ<º2>²\f Z÷¡)¬I”­ú ò ^Æã_FèÒ¾ƒ‰8ý˜ƒÁšDÙªÏ[}»— %“…w|Q¯P˜²ÅšHÙªÏ!¯áe4,“ÅO<À%¢x‘îÀW‹ûË¿ð!q¤lÕ¯J™ye>÷ƒ ^Fä4`Æ>èÐxîGw˜²U_4»/ÊOR´R³ê ŒNY.¨U÷ra[ÑêM«¾Ø÷rÅÂ2ɽ\ØÅV´zÓª/¶Æ½,Æ­îåÂ.¶bÅ›/D¹¼ïúË}e§¤'q‹­q/ÃãXx~éã’Å=i§\ÆRÔÞ¤'0à žÍÄåó|î§õ5‡i’aŠmq/3VÈðÂ?ÁK¡æ” ß45NŒ‰ëÌì”›hމeËy ‡ßhf§<°iz¢±n§ŒÏ×ÒsµqðXöJ±OŸ—ßéäг l¡zB-í•€vŽN-aªè2HÔà€‡/ÆÔN–`û¾«,ÜÓ/¦=A1QÃ^È¡Ó0ð2ðHúˆõ…®~ÄR3å!?ïÉC¯ "$\R†›«5ÊZaeÔÃÓSXàaüóÇ«E8‚ß85$ö;N‰cØZ&‹Þ)Ôè³”H ×(“5Kʸÿ_¹ˆÆH¹bå[Ÿz)öÙÃKjê#dg¡Fmè3¸R@Ê5ž1Qb ®–¨±5Û)׬ì^D%ÉJ€3€2[³rÍÊN9ú6šYõ0r°f3媕r¤2s„Í“§¾ä[Tè G{^V¡|iw«—ëVv/Èê ŸÑ“­”£¾»—5Õ2þ7Òj¤,ú¸\?)寧¾…šUï” {ŸÔË{ Uu8ec³Ð)o†V­Ø)+ë‡ï³P¾¬O°añSbOCù.˜ùcÿÏsß/ºªÁ€ëIà¿Zðð,^†ÿãÑ­‡¯mM©Ê§¡¼éw\Õ©Ç|ñ<8¤Fס¬=UòÅSQNÀkó¬®OW÷<…w©MƒÆgZÄiég¢ŒÿSÂ8$PÆAž–}Ö«øŠÀT˜Pé¿ÿ©%êóžÊËuS…­â‰YNy¦òŽæ'SOÊÇf:å12<ñÞÙóò4dîeGGÏiGç»—GÑð |3d÷ò dîeÏŠfîå@«œ™;åYÊ+œò gWá”g­ pÊ+@œ]…SžE´‚À)¯qvNyÑ §¼ÄÙU8åYD+œò gWá”g­ pÊ+@œ]…SžE´‚À)¯qvNy\ûlÐLJœr‰§§›|õðb~×o r§\R†ûÖ8©SÓ5عM…NyH‡lÜ©éšò¢ôá”Í’]ò̪S:4³S^•épe](s_ÛÓF—ŸâcÈÀ /×¾²wY·Î)W(‡¯xë)gèðŽCq¡ñ½oÔ=“¬ÃμuðÈ eõ•½Ny-'Ýe|Hœ×ï^^‡3RûÊ^÷ò:ŒùÀ¹2c„Õ»—×á ^Æã_FØö¼v§¼eDÌdá_]]/Filter/FlateDecode/Width 498/Height 239/BitsPerComponent 1/Length 1610 >>stream x훿ŽEÆ-8¼G¸Ô’‘iŸÀâH2Á !ßÎìÔDûpV– ¢‹-dÖ§ .B·°ÒÍ»wÍ÷õÌvWïTÍía<%k§gª~Ó_Uu·ÇCØÅ^î‡0à[×o(ÝPº-*ðŽ—Íi)qÓÙwÄkwöÅÞ´*»Nˆ9¾¾ ¿ÏFÅ;>ŸÜ8û|R‹‡_ŽÏ«Â×½é›}±?u‰âÉbÿmUë4ÎÆ%â~b|}c¦xÏ;.Z®½˜8}ÞaUà¶ø>\4oã ñĸ_àÃY—v¤Kÿÿî;šPýR/¸ãNÇøqûCUõ4†´ËæCâRÏ+nØ ~tæA/›x ÍCu/†´k¾&þÔÃõ¢øe¨:x+>Î^ˆïàO‹'m]_0÷.Þù±Æ'_{â-œé¦ý.½xG­€ Ümßã§Ñ×Í7íg]=*ñ(þû棴¯Š×¥k¾Ý¤ñûÍgaü.^êŽW-i‡z&eébã7›ÝîËýb/>ÜïèÆiyqO±.ÙTãòŽø½-“QŽLœŽù¬û“sèÒ­'^ˆÁ¦{ü5Å ÈÝý®‹Žˆ»!7å>åä¶xz<\è„A¶‡ <^ãèËÖ—»éË(Gnˆ'¾À·Ï=¾ÆÆ¾sgÝEuø]ï–)Ĺó°Âž~{²yîÄñmÓ3±d:÷øQŠoÃG½â%±¨Æ]söñ‚Ö8t·8C¶Ël™{lŽÛ·Ë}½ãÂÏ=°qÍÏ6¹¯…‹ž^÷}=ñžÔfÝ1¤ï¬Ë¶>ëâ+lñtyËF"×þØ8C6Þïë•NOìÒ1Ä_à¶ø>\4oã Ù8÷ø¯ÈE8üjÙ÷å .Ö„ž¼È=ž6óªúl»³n‰öâL,™Îýon¼ã°W¼$–•ß9qœýGeAk|:ŽÜñÛ¸ï±òÀzqÑÓëÜÛÆUw²qßßç5ßÉÝ=ÈrñÝûžgçÛ=\òÔÃwö™®nYžaÍÛuÉ+gµj±ˆ}¥Ë(G&N‡·héK¦+Ý=—Äb`ã ú¾Kß݃,ß úÞ­|.Fö¢eˆW:¡si6.pk¾[yÖ%›yX ü^é2Ê‘‰Óá5޾dvãèöpI,6ÎO<}ÙLñ¿‡g”#§ÃO_2[<Ý.‰ÅÀÆ≧/›)^àîdwC >Gy#?ÄÃE¿ÊÆñ¦‰ù´xÈ/ýˆÿܲJ#~Èγëi6³o\²%ð¾—îUǨÅbþÜžàR˜ÂÒ½á\®Æ!Hsû+.…y!ÄßHø—«ß·W¸-ìgvñŸì  ×ÏŽ;8ž™!Ä_ýü)}‹ûßDñ¸ã¶0/„ø¤Á¯æ ŽÛYÁš!8»aþ –nþÑDðhòü‹•ÿXÂg+!ÎH7ø‹6îjþd4Æuòâä-.…á™ÂÙw°ߺxCéÞÓÒýÐHendstream endobj 49 0 obj<]/Filter/FlateDecode/Width 498/Height 239/BitsPerComponent 1/Length 1498 >>stream x훿nGÆ ¤P©0 2­ )\ðò€461‚ ¾"…J)]ðR¸paë@…J>€ ¦c‘‚Dð(Ÿ¢Í|svænæ$’AB&·ÅÝg~»ßÌîã.íÓ.pŒ¾uþ†Ô ©Û"ÿð¶™i‰›Î¾#ž»³ß/2mìø.Ÿ¢m rßůp9TêÃ…ðÅ#jG1âö(k“_ ‰§â™ÅîEྋ‡CYÓl<Äw'&1°¥æºxxBÑ3q<ñ°5ͳ‡‡†¥ŽÃÅ[j¦ø@vO(z&ƒ'¶¦ÙâaöðаԱq¸xâaKÍÈîá EÏÄaðÄÃÖ4[<Ì–:6OÍ+WÝL6¯X)Ü߇ÉÛ8\<ñ ÷Ó3Ô:>¡­‹Lý~¯ûÈ»ÊoQmfcº¸ë~‹C¹ŽÙv©¶Í—ÀC^ϧt‘‘G¹m¸ ý‘e9»T{4àg—›–qª‰]œÄCÏ®Ä[øDŠçZ—#& ïˆ' 3‰SŽ¤ç¡±“J%>ôâñ7-ü'àÇåÂ-';²»é…“ÑÑ@-¼cm\¼rá®;“Õe¨u2”ô&/MúäºKg?uîº'.ÞÂ)¼Q”´U[ÃÃidÑê€û^|Çq­›”ç}vR–Ô ñ\m¨&ªrÁÅjš/qœV_sˆ Ž2u\Š˜MiÈ&vÆCõZ8oãrÝëJ$ÎÕæ¨œ=ääÿŒãLâd±âW£—å;m·TJñ+àTi_JœÅÿP¾”æã¶x‰/F%Nέ؟•¯…ü^,µ«bu&q^¸‹rá–G4~¼ŒñEÌ.¤x)…Â;ÖÆe¿÷ü9±ovÞÇà)˼˜êÈ@±»¿aÙÀ¸ërßyïYeh¼9¶½D…} ëÎâÿÆØ×eJŽz¯36F™´Nêø8Yö•<°ý¸Ù Õ‹9¹ÄËBiv¸xVáRãôþ×_.ää¼÷êmÃ8‰Guô7­Â¥xþ™Kø¼’ïà÷.œÂeì±9ùpñR''W±'.ÞU¸ÿ0>stream xíšÉÂ0 EC%”êÖÒ‰[È#ìeA“™çƒ9xÂãëÛ“ rZéN[I„µ¶Á„¼<4á‰ÄãßðjæåVï¡ÇšTb‹ÝKLËx_ÉBÚ>k“å!¥“ÆŽ|D²^_jÖ‚—ÉÅD&ÐÄÿiÂÙeï«K¿#¾ªyž,ݵeï˜ì˜y`ƒ.å?aÀ«Ì%s_µã³Xg’føÄÊÛàeêHÖÑö’>Áw4{X§{|u/ô/µOöZöÚïÏ'ðU|Ÿ¨ùÁ»Ë„¬‚(5{ÇÞ½_55­;ß§As}‡|äùjy);+hM¾Š¯ª+p6Ù{6ÁWÉ!ŸCK¾Úº`ÕÍšÿèÚGê1·¬¢Ÿe­m á/OMx"ñ^1? /O$Ã+æãgáå‰ÄcxÅ|ü쩼^BJw›endstream endobj 51 0 obj<]/Filter/FlateDecode/Width 604/Height 183/BitsPerComponent 1/Length 322 >>stream xíšÉÂ0 EC%”êÖÒ‰[È#ìeA“™çƒ9xÂãëÛ“ rZéN[I„µ¶Á„¼<4á‰ÄãßðjæåVï¡ÇšTb‹ÝKLËx_ÉBÚ>k“å!¥“ÆŽ|D²^_jÖ‚—ÉÅD&ÐÄÿiÂÙeï«K¿#¾ªyž,ݵeï˜ì˜y`ƒ.å?aÀ«Ì%s_µã³Xg’føÄÊÛàeêHÖÑö’>Áw4{X§{|u/ô/µOöZöÚïÏ'ðU|Ÿ¨ùÁ»Ë„¬‚(5{ÇÞ½_55­;ß§As}‡|äùjy);+hM¾Š¯ª+p6Ù{6ÁWÉ!ŸCK¾Úº`ÕÍšÿèÚGê1·¬¢Ÿe­m á/OMx"ñ^1? /O$Ã+æãgáå‰ÄcxÅ|ü쩼^BJw›endstream endobj 52 0 obj<>stream xí—Á Â0 E»#1Ý,£Ð à‚`»©H«¬6Ô]/Filter/FlateDecode/Width 628/Height 183/BitsPerComponent 1/Length 322 >>stream xí—Á Â0 E»#1Ý,£Ð à‚`»©H«¬6Ô>stream xí’± Â0E 5k¤CÙ‚ ÎJš°¢f¢PP2…)03P8n¬ÜY˜ôè»ñÙ~þ¶žÍ,Û˜Ïääó[ Ý‹×O‚ZŸA+µKg³‡=-gó¢‡"âfveWWG­9UÏé5ï#¢›’'ʇ1M’pƒg‹ËÚn¶“dFX Pø^ß4,t¡Hv@âÒ€®¿¿‹d@Hþ'Ð````````þÈÀ雺endstream endobj 55 0 obj<]/Filter/FlateDecode/Width 270/Height 166/BitsPerComponent 1/Length 263 >>stream xûÿˆýßÿ÷²ÀÿÿP¸ÿžü·ÿ‰WÅßo@3þÖ£èB5ãï¿ÇËçþ­ K²G(BSñèîìÜß;z·#ƒ¦âÛÝݽ?lï™!Œ@sé_ ŠÜïwïî©âAÿy’7<îè“ÅiLâ/Œ¤Q] “ø càTñ˜  $8lU¨lìñ‚¬fTrhS2APÕñJÈûPõPƨ p@Œ†$=܇&‹Ñð `Œ–àd0šFsÃhn¸Iÿé”6rê¡-¥ÀA§x uphß“ÿé•FC}4ÔGÛc£y£½yH:õ>áV4ÂQendstream endobj 56 0 obj<>stream xíšAkA€ÅžA° ‡ª9ôЛ˜Km)5W‚·=ÔsPד$úŠäèÍ“vEi²"˜cOêdwM#õ“%F²)Ëìsf³›Ýî@¤×7„Ì{o¾y _²§€x\&óEê3x¡ 5=^#û?¡p p4pÚJb9‰²1ïÊx7Ó÷8j+»2¿ûæ5#[Ûë—kwàþe{¤çÄÆéHu=2w‹Þ‡šNÞµ­áj_·û{vBê¬ll~ªM6­UöÖ´¼Êo×îìij´èšæGvS Îm»¬×³QŸw Pßúf?©ôß”€‹[Ϻ՗×ýöÝWÏ—¯=ŽÎ²»FUé9i Zñ¢ =g¡Ý4eYh´&͈¢4`œÃÐ@h  4€Ð@h  4€Ð@h  4€Ð˜ßñ>½_!½¨Kñ>V̶2PÅ-„/gC>gtU ?ΆºÒö0íHeáUg ŽT Sù—](héÿ‰Úéݳ¬9‹‚@tµÓ¥YÖœEA P©”âL s5:×®z;9>CºüºÑÓ_ˆýE ¨Ú}¢­ñìð®Äõø'¬`ÂÉöeßü‡ù;<Ú„ âæÃ'G ÌƒFºX¢Ä§dLÅ¡ùàh»¸Qo¯OóøÛÑKÄ_"îRøÐÛ½GË Z‰¡iÄÑ{É®½Ã>½ÔèîȨ«—èƒ%ƒï³ô¬"Õ2ºN¶ó­BîÖZîBøèàÒ"-ïÓ-¹ë©œ£Õ¯ùúBgM;µ&Œø¯kendstream endobj 57 0 obj<]/Filter/FlateDecode/Width 338/Height 228/BitsPerComponent 1/Length 774 >>stream xíšÁkAÆÅŠ  z¨šCÞÄ^jK©¹z¼õ衞“‚B(È:{’€Dÿ€"9zó¤Ý¢4»"˜cOêv³M#õP“#™”u÷9³ÉLf·»0¡íAœ!dÞ›ùí——ow`—Y€Q» ¦Ð4ðá9è c4 –ß {¢° àº!L‹¨ß#ª>Qp nªª4^¿òµ¥åù‹¥;pÿú´Û52ôÀA‹¨î9k9o£dhowjÙ¦á6×]B¤V¿ç|(õk³þ§æ~`·¾ŽÒÑvœ÷þMжo»+F9 ˆjˆµ/î“Bó]+µ8ÿ°ª7Š/®;w_>›¾ú¸Ó=›MVe£> ¢½è›ñXí“ÐFaYÊæb½B•ÊëŒ|C|aÉCaõêˆþ·(óu÷YˆL}Nz|0«#æÆÜCdšC ±ÇM…*”Êå€r@9 P(”:b7Ø÷ÚêÁDÞf«DäU:ÎõÊÜRφÊå€r@9 P(þLrc(Ykö4Ð@^õ<ÚGÒµŽþ”WÝ•G-ytŒZÃM ¹n8Ú&g@æÊb/rφ¡*HmZ¹´€GMyÕÓAQbáoñ(Ù*ÑÁÁû¡²øUÓýØtK­& )o!|:*" }Ó¿•‚î.TQ¨.&$¦¤ û è`Ä&Žžªš‚Š=&êFæ™É£0 ªntˆg&€¢±¡(0Ê(*ÙÒѾA$®x«Ò…-Åäu£§ßimv‰XPt›L°CG}›¨jØ#Ÿá˜va3î791¿;{‹° á™áÊ¡¨ïA%½”·µÀÖz6-š4‚îäÊ•ýùA>únù--˜ÒðÔp‘Yp°vÏ^©Ø…4ˆÚÕzž¨z°Ý´/T«qyûAËÖZ_yµœTûË3ÕlæÖ\æÜpéZ€íœ½²i/ÅUä-~ž)OÔçБ¹Ø€õz€ó9endstream endobj 58 0 obj<>stream xí“=KÃ@ÇÛ/ ¸‰-èâè&.ÅÝEý"‚CÛ¡b»Hq±c>€nBT®Æ€/¡ƒ"ˆƒÄtéOr¹{Ì‹¡rç¨ÛsÃ=÷<÷»?áø^bšŒ' gQUpá}ñ¢å lg%ÞyO4ßS?è?ñ;£—1”qj˜NI‚ jùÀ©¯¤2î,TÂðàb}bt‹ÎïÜÜj³;§E£ÀI~¤^’i—®•VÙʉõ1õ`î¹ZwŽè”éG2zé­ÑÕÙ±hÍ»òÎë–Gfè§n¨hÍ}%d#ÌPÓóôšºA_By@2”ŸîÇ©÷¦»X}òˆÞ ʈ§]t†Ë~¼;64>k´ÎsË)G›v[JMÛxcùA®ù ÍrUÑL¢ÌŽŠþe#DÑø'ióßЖ.ÔQ4€Ð@h  4€Ð@h  4€Ð@cà ëÏAendstream endobj 59 0 obj<]/Filter/FlateDecode/Width 337/Height 168/BitsPerComponent 1/Length 787 >>stream x혿kAÇ/•`'&K;± ’ÖF--DR¤§(˜×26¦ÌÁB8•wþXRè b!ç¹þ(c=‚îåöfžß7›ÙìÎ^r¹"Ä9Ø›ÙÙOoßÎ|vrD[y–œHð‰ˆzÓRHŸÂŸ>QoªÅƒDâßbU: ¥g2$¿G´öu ·´3ÛhÔBÔ®u•DaÅ'ú¹¨‘ð®Lt»OÞÜ9sê~xùá‡3‹ÕOÞBØky*@*ê[÷|#¼=z3ºñ²ò÷Ü—òãÆÌÊ%·FÅç&ÚrçFϯ£ën%œm¾k¾ž«4Ý a§¸”GgëÞíÆh¹é3ú>l´Ö T´Ü¯–õs¹qmú[Ó-λ¹äI¯4â›/?}±V˜Yü±àõîÕJFTuНHwÌv»úJ¨;f›Gã"šÎóh(²¨­€­À>U `|Fô2¬ —|¶E,ct|™A‹Ú ² D˜Å‚g±ƒ©í§¦vf0 ÜÖ:Ð?N@b èÕš\f´ ´ƒ¨rh)…f½!£‘kŸÅ‰ÅjWìA¬€)Ü*Ö‚f,5³ˆ¾ë@ü¤Õ2¬£Ã‚ÞÄ‘Ay‹É*ÇfÚ¢¶£Õ1)ê9˜¯iG´†AÌ×óU©xèºQhZÅë@£*Ð:P¥bo+*1šVq(1ú¨R1GåjFU/FUT+9}r•œë-+r‰£2š[†H@@“GÀ HÚ =f¢‚o«_TU¬$jQ;“}ÐçšIÀ*cOвÕík[Û É ° yWKl?µË`kw&a—qmBíW¥7Ó¯lmeÂÄÚ,xAˆš³6œ¥ÐÄÚ¼Yï:@ͨÊÚSH ±¶ºYí“«B«:W¶6£ê cúUn‹£&~ÝÍ žsÝ1jýT½ Í'ÀuMØ“³ðGrÕ¶<<`W¬m­ÛÖöQ0Lm®vödÙ¿¼X¦ö¶þó ñS‚ýÙÃ.˜ý¨€V°nwQñ?.Ì"endstream endobj 60 0 obj<>stream xí“1KÃPÇ­ƒààЭP±‚³C—~?€›ƒN‰”b«D©›ˆC‡ "Ž]”ŠRò:T‚_Á7‰ƒ‹±)󒜗'‘’\t»ƒüóîÝ#ü IEËДaÜ *ƒÁ:v'!Ã8T©"|Œš }0àUÈSVû\Uó1Tº¸ÕoßcT *8€-C_Žo•¡³Vñý«Á~¿Ö5:íò­×ðwG pqŸA¬.ªE]ênÉ(nò%]›š Ú]u­FÑ3zZ ?3Ϧ.¸¹eÃÜʹ·–µ#õ!¢unVݳåA/†.Ž¿Ñkfœo<™Û’›Z·‘ý€¨à´rN~¾ÌÔJGöìôbP.¼\¶Îòé­ªÇ@IX Ÿaã§Æ¼&wB毨£Ð¦§NILØšŒÒoBÉø'S©Ê%_?5ȶ„’2@È d€ 2@È d€ 2@Èø_ãÒÅ©endstream endobj 61 0 obj<]/Filter/FlateDecode/Width 337/Height 168/BitsPerComponent 1/Length 1020 >>stream x혱kqÇ[‹ƒH·BÅœº¸tÈŸààPpÐ))Rl•(ç&âÐÁ¡‚HÆ.JÅ)½ JpÊn(W)´”𦥴—är÷ü¾ßï÷â/—¶qZz÷»Ë»O^Þïåwßû&D²Å·É #~µOñäQ÷^½Ž<ŒD/Ô¨†èGì(ô7Qž¶B¢ð ‘7×Ψë6µÃÑ]F Ah5Œ<ÚYèzwºÓùðíYe®˜_^šú¼·Ðy²ƒÞ@¿—ïÔƒG“¹0×pò“ê·rÁ«Rv•²_ÐFyar/¿’íæ~yyœ—rA½ôp•®~:¯7ËåÇa® t¾^zôéjø|%‰F \ÖèG/ÿîþÏÒlX/e‹ ƒÄÕÅÑêøõéîå9çåòÒ•K7»S…Å·ãɬê54 ›‡½ébèmV_%¨ãP´ªPãž:“ᘬr)yLÑ´iþSFÛ¨Ü}•Ä…‘—|ìMì,J‹q ¦hÚsÖ ¬â/íLHñ5kiwl!H‚U´ր†ˆÆ㦅r0ôLÐôÑ(ù ¢ß_`tY#D;ˆö¡Û¥¼¿Éh4ÆiÛK ›‚z@ÉÊ(—EÕ›c¤' ²*”§u*ªæÚC¹Ö³*TÕÊ}ÝÚåi¹Vûª¶8:\+¦ýâªKRp‡H±4cV_‰Ö%.è¾ÖL_ÛIt |/ÔÅž¢iÎD"ªaQ²d`½²:èÍÇOÄ-­#M¬WŸkme} ½v¿ïUêTZ`ôÐd% d«v ”<ÜEÉÚ‡úV um´!Y]d-Xh„¬1£³’uÍFíåâÞ´*)Qk(è˜d•fEÉiÉ´zYeÕmeÐ,Ǫuh‹ketñL¬B­úY€U+ñ´µžÍ±•ãÕ>/ÓrÑá´Ö´ÿÜ ,m ¡.ýê’Áë@]ŒÖÏò–0ÐZµÿ*!‹¦6ÐÐßÒ€uš¥P%ð;@µN Û¥` t´N ›‚z@Ö(?aÏBÄY+‚ªZ{¨6Ð'dU¨1Ð5c ºVøi¨ 4 pRÕÖ¨âŠE­å8Dµ}¨a;?w¬¯ Lä8gˆà£µ×f¶–6Eõ›ªñÚ¾ñÚæo Ü Øô_ Úk5^[gU¶Ú¶º,ºÚkKÖCS€ŠúV uQUQІ -X(+©öÚ‚Ê_4ü„è3ûŒòG©ÇŽšVÅdUZn×ÊmQè˜d•fEHЗõH¦Õk£Úk㳫Öu Úk#h¼¶6^\u –ã)RüQ‰½qendstream endobj 62 0 obj<>stream xí–1Â0Q¤ä <…§Ù?Ã?@dYã8A#¥Bë&–|qqç™»ð„+°½µÎu¬™€Øó@î/äéNêS‚ôS×+Fè 9~"AH%`'Œ6¯H½Ñ’š[q. Ÿ;¨1q0Ýh7%oÜgÓ)À¦³Ç…¯=Þ–‚©lºoÃÃs.°Ÿý´œìsYRi@e©ãšMgÌm:‘ò“é„éÊð°Á¾©­m¨|ÕtÓ£¶»yÛwžþ]/Filter/FlateDecode/Width 289/Height 125/BitsPerComponent 4/Length 529 >>stream xíš]rà „i† ø ¾B/Àƒï¦‚èÀb1ö¸Ó$MóñàÀJÆx³âGI ½l¥¤ÞŽs ;„êaníâÌG@½K<’t™Ìz g»¹\nk+fN­¹®øìÅúŽ (C[Ž4”YÐrëŒÄ‚òSê0´ÅΉTC0Ôݯ¶h¨°%,8ÊÄV\­eJYüfE?`èÝ’H0%\f’Ìœ.uªÉ'×ëÌòäAèã²1«ÃÐŒÅ`HÙ˜ÕahÆŠbI'v3 ØÏü›´ÃÍ›¿ûG2h™’Ô3z$¬ŠDHÇX È…Mþ»mòcÿöëºv çÖBCM $¬Ê¢ËïeYl?ÖVlÞdûѧ‰ZûoKG::“Ø+‹ƒiŸsFŽÎý÷IN®#›¾Cž“¡‘ß‚!ÏɈÀÐȇoù šLãÁüˆs`ìöÔ?í=¸CíciÅ^ µæ²TàÁ +tvr…!ê³JÆ-T+è±µÛÐjbøa~ ¡!44¤Ôîc$ʈ²'G™Al(Dö‡–_Í í8¹%: §êéÌáÑv¾²3Æaèõ’…!Øè/qòÂÒ™Ó%€ Z¦ôÑ?³…e Û#¹Nã»t€ØÚmh 51°ºÓšˆ2¢Œ(6Hì³ ^lÇ(G‚¿qêø«Øv2endstream endobj 64 0 obj<>stream xì}Tiö5NãîÖ¸ q÷™ø$™xˆ!Á î,H€B\'žÉÄÝ=@‚;´ÒX¿ûUÉÈî{{Î{ï?»›:÷T]]z»î¯®÷ï= ò¾¥á!ÞÐ0¦A¦¯Ÿ7ÜG›ÇâÄå ÁòÞ lM¬>4LNC<¼rŸ¼þ!^Çâñè<^Ï0>Äc ÷ Ãb.—Û9ØßÉìä ÷Âb.Íâõ1yÜþá!˜†‡xýƒ<Î 9@Ð l‰‰5ŒÏÇàq‡ð ò9<‹Ç¢óX,»7§Ä V‡-X¼!o€ÏΗ÷-Ájc3L\8yù0'.Î. ®wäpÀO˜áuH¦Œòd`˜<]òw<ÂD®‡Ç°„KPo°7—ÇÅû“?Ó×OïûôÿŽÀ"4F£B7¶€ ‘›¾Ã¯ä—BZ° r ÂNH!¶£ÛƒÄÁ& é€QL1Œa p„‡AˆÉfð†éƒLö‹9ÔOⲆú†ñÔ?4 h‚i˜Çæ ƒDöóúyý¼¾A °ïŽ=4BÊehPóăíÈ£³1Ôôõñúà$¿Å %£W€ZÁU‘ƒÑËü× fd?#³ÃhCž“HDÙàÐèOÚÀbœ! †#‰t0Ëp`Ïß§ïø·æ!ƒ# !»#ÒAŠ!4x†ÅT Ýž¼a¹ÀÏiüH'&,OÆð3Ö‘' Äd¯Ãècõpè]ÌžÎÞÞî&£—ÝßÓ?Ð=Àéæ0x,Þ ‹7 "Æþ ^¸@Sê%æ€68Ãý2  (5ªÕpyÜaÎÀÐ_@ ‰†rcI®3Aaaæ?¼ã¥¿?ÍoOùûø;þæp;´4Ûþ«¶O(ü ðÄí+ÓÀ7jÿàñàÅcÀ¼7P]†A­f® ÃÚä<î ÖIúûXœþ^:«»›ÙÕCïaÑ}½¬¾¶žŽvW7·—>ÀîdƒžCîgòéà Þs æCýÌ¡Aúà@çÀ`TÀŠÁìf³zû¸ÌþAnßàPß·Ñ3ƒªoŒ¬1Crðç‰Xs|ñv †Œ\Î7DB° #Öî´8Œ3j¨cë  ËBºÎ(h@7þÙptŸß?¿sàß‹àáb÷ 65H½}TK!¡&$õÅB—×G M?è öƒ{Ü9 Å#š ØI@C<æð0“7ÐàƒuCX<6—Ça²{9¬ŽÞžÆÖ¶šº†·ë^}¨yù±æÕÛÖº ŸmõÌîV£ÁìbŠpéL.ƒÝÇàô1@êgÑûéÝý]]S- ] m-M-õ-­õíM]½m^Ìþ>À"Ðj@z±Fmøöƒ1áÿ: 4´oà…Ä™¯¿ƒ*°èë 0¶&$á9¹Ÿ±ùdiDÂÀ~¾:•H¥°èëÞ¿¾sà߉5}CÃl mH…ߨàC ŸÃ€6°chC újúG\šØILz.m0È a0}x˜Îãv÷÷‚]Î^Ðg8ý<&sÞÍn­onþð©îéÓw·n¾¼võù•Ë0uýJíý[MÏv~|ÕÛPÓÛÖÐÛÕLgtÚ0X=L0¸=ÜÞîþŽö¾¦&vÝz]]SÍÇÏ5ïê>¾¨yÿ¤îãÓ†/¯š[kZ»Û™Ý}ÌÞ~°Â@–±ØÂIbÛä«Öñ;¹Ã…¯P%áÛ&ƒ¯¿ÿ~Wð+ü4x4r XNz·FA;^à”Ƚºàˆ¾Gb¹bx'°G'GµMrÙ÷ùwü›q¤‰€ˆ¯Và ¡«Z$Þ€ ÏcðnbÄ’‘• ŸÌˆÅ4â’…¤Â>ôáÐ/ú‡A-br†»{X]­uu/½¹vöþ‘²eY7JÒn—gÝ9Ppó`Ù½“ÇŸ]»òæáý÷¯žiüü¹­¹¶¥¹¾¹¥©©©åKCËÇšæ—o>ýr÷^Í­«ooœy~åðË•¯T½¸{æã››µuÏ:>7ö´´±é½C#ÞTâ¼á„Ç´– ,ýß¾*g~5°Ò®?Çâ ±P@Èå€3$È<˜“_ ü™?ηh3Ên'±‰öí‘¿¿sàß…¤ä‘Y;òùû»ûÔ×Ä@ß Å·cø‰3R¶À#LªAxMÀg š{€Ggõwvvw6~©}|ûŅ×ò£Ë—f¬ðIZ蘼È!y±gUôö#iqçŠs¯­¼yñä½[—=¾ýìå£'Ï>{þðÕÓ‡oÝ{ûÖ›+—_=÷êÄá—U¹Êâ®åî:›±ýlnàµÊ„—Êž=8û¹áÅ—¶ÚfzGXRÃX›!Q‘¼òZÈ9¹|ôRÀÿÔŒ®üõ“Dü}l§„W| g8„‘ù Î|…tr àÏÈ`TáÁöžüH4PÎðûôÿNÀ†?èæý\ !ˆØ€ëŒ$ð¥&áiˆM÷w ö ‘^Y A|šQóú!Í$HxËˆðŠ€°`Àº8–Á3Àáö±èýƒ½Ü&wÉâ4©y÷àúó .æ…”øOÍ^b“9O/iŠR´‡h ³x~ô<Ï\ÿ'œ+M¾\wåDñå3ûn\}çÀß’p¿P3ŽaœôØ@$°€ÔÔŒ@ðC H ¯¯{Ñ5@ïâœI„¤z°/¯€%ØŠØ?­AT°HƒŒ@ŽvÌ1†zYìÎŽ–/o¿¿u귲ؓ1KŠ¢åÌUÏ™.½g’p¦/JñHBM™i–ñƒ]Îj‚­ÓJÃJZs<Ëÿl~Љ,ÿSé[.fn½šºåBôª3?œß:cÿLýª9ºU ÊæéeL¥ÆM×J_åR¾øÞ…Ò‡÷ÎÕ|~ÝÒÛÑÆd€Ûhä48}þ`õ|ýŠQò[‚óúªùÀ–ØwÁ8ЏýHsˆ oJB lCìØPC€ °çôú2ú\f«³×Çæ±™ÃlÆ ‡9ØÇ…À ÜßÌ>Èœ$îœo|ðicŸØw¨ù[ŠÓ÷“úÇ@0b@B xuÛ˜ìv»Moev4õ47tÔ6´}ªkzû¥åc}{]cw3˜'­ z;‹ÓÎäÂ&,B)Â.x¦ƒ¬wÈ–éætr¸­]½õµoŸ¾ºyæÉɼS ++ý½ö.ÔÌ"šë‡ò|Q*öNË™$•æ'‘à'?‘’:‹š»Ô¬x­]öRóŒ…FYó óí] —?C=o’Jñ…ÃSäO,÷Ë'ï€mÐ&G±õêgöFüz¦ìɃkjÞ4¶77tuõ°ÙL&›ËÍ¢ˆÍd±L‹ J@0Τ›„r>J„߆PVdú‡¸}CÈáá‚cë„ðR‡g`m€m Ç<îÑû˜¬~6›CgÐ;™Ý휮VfS}ǧ·íoŸ·¾~ÚôæyGÍf[ë ’ ñÖÇ‚”¼K¬,Võjþñ­üý—¿7àÆ·1‘Äúj ·oˆÎæÒÙœnfOGgscã‡/µO?xPÿñ^S̓Ïïn~ùp·¡öacýËææO-í àâmïîé¦sz™ öPw²yqÒ™67È£÷u39-õµŸžÜ||ºôVIÄ•¤eçƒ|N­5;¾Líø"ÙóE«f UÏ?ÿ“ÚÙÕšg×è\£w|µá‘5¦ÇÖYŸØ`w5Øûü6×ÓÎn´?¿ÁæüZ‹‹k-on°z¸ÞâÎ ½‹óUŽÍ”/™(•è&ìoͿĄïήUíyqë—·/´´6´t¶~iiê¡÷÷õ÷uuÓ›ÛY ¦±á+Ö¾SˆD[¤ÆUX€_ÀéËÀª<å‚Ò×Ý3€+ 8\ƒÓÛËea[>‚l‚Aj d& r†Ø=ÌVFÝÇ–gz^`êxñ°ãÍóΚô¦&V7äôõq@]A/8œ9=Ø7<yJßµš¿·\}?»?q€„xøŽ©ð³XœÞž®Žæ†Öºw o×>ºöñÖéwW«^_,ýKÉó3ùÏÎ<¿Xúòú¡7wÏ~xò[Í›'Ÿ?¾­ÿRÑ¡ööΞ:ƒµ &FWwoSCcã§÷µOï=:Wy63ø`À¼’öÅóu‹§QöM/òÜãŽÒœQºJrG)>(s‚pædÉô R‰Þ’1nâÑ.⩾ʻ]¤BhV(ÀS„Jqã‹5C™6¨ÔSàä\ÕK+MO­0Ï›©âA]ëªYµñFuñ­óÇ^?¹ó¹æMͧ·MŸkšß½kzñªùÍûžºzVS+³¥ g˜ÅÆ¡|pÞ€1jL`p‡°ƒ¥›„𣀷„Ã`ôô671Á2kkíhøÒQßÐÛÞNæ"Cx…™`Ë厄¬AÀŠ~V7§£‰Õð©óõÓ·—¹S¹ïZAÚ½ý¹/Oúpõ\íßêŸ=iþ𡽱¹ÍÅõ`£ÅhØ‘†3 ¾CÍŸîãï þöB²HÝ„£¯¯ÞÕÞÕð¥þõÓÚ7Þ_=õæÜþ7ÇòŸW¤ÜÍ ¹–²éZú†+é.gn¹œ·ëFyò½EO¯~ëò»§>¼~QWû¡¹¹±££­««£«»­«³µ£¥¹îýû7¾¼uýÑÙê ùñûw.É]êš:Q3ÍS&Ë]$×S†«`¢ŠwB!V(Œ†"i(Â…™£ #´SíÐF‘æâAúü[4ÐF5´V­£¢­(ÒøJùI”N*Ÿ$S>E1o¼b8Mì'=´ÔX:iÙÔ#)gJ²=qèñ¯ß¼ôèúÅ××o|¸qåí¯Þ6?S÷èYÓëwŒæÖf?TbxºŒòdԌ˔À[2 î\F»£­·¹±µöPÝëW¯>øðìyû—zvg£³{€:¶II¨Á.+ˆ r!“™ÑÓÞÓü¥ýã«æ§÷__:óK^Ö¾ ­á3}ҖϪصñRnêÃãUnÿÖôöMG}SoGƒÎa÷‘Þ N×þk•þÊ¿ýíöýÿÓ80ª«üñóÏ× k¶ÑX\áÈ… ‡ÕÓÑÚTû®îÙý/~{}®úvQÊÕ´ _ýoD¯¹¸sþ‰õ~¿lŸ|j›ßÑ-«¶Ï¨]z*e祢¤+ ~=}èæ¥Sn_þüþ»÷Ï?Ô¼zÿéå‡/>¿{ýþá½ç×/ß;uøâÞô¡ë÷®š¼w‘sÅ"›òzå“ÕÍÔ­žkT9Ǥt–é¾™FÕ LŽ.0>6Ïìè ³Ã“Lú˜ô2«ò°*¦éåYkåX«çØR³íT²U ½µ+fføÈdúJfz‹gxˆæ—/Ÿ¥_:Ï2o6m‡‹nЇÌuKª’£NfþRQxùpé/Åwù¥ äTvÁ•Ò÷Ož}yõ×ÏŸu~ªë¨kènhØ8ö©@ô‰Š d ôsY=Hïlëhø\ÿîͧç_?¸óèÆÕ‹Ç)/½püØ›'mº[XÝ ´Ød"J Ôᇠÿ y‰mož}¹sý~UùþÐÀèùÓvÏðŠœæ5Ë'eÕÂ1a×*Ë_üúë»'Okß×|ùÜÔÜÞÕIgõr8 .èRœ¾!0ÏþøÏ’ßÿüÿþ=—üõÙÿc¨üW×ÿ¿uÕÿSÇý¿uþÿöC¢ÌIåøˆé›§ü½ Ì@hìàNf°è½]­muoÀbzíØí²´ãQë2¹F{éÄ£¦yª¥ºÊÇÛ‰îñ¦¤“Šv q vW‰œb–ðƒgÒš™Õi¡ÇòâÏUä^;YqëÒñ;×Nݾ~æÞ•“OÎ}Iz%Y'ã÷¬žâcºÓN)Œ&fηSmQFÛÔ°Þ²]mÒB›4ÑVU¨Šv)¢ ´C íF;ÑV>ä϶‹¢`Y,‚•Q¤Ž@¼•Dîx•ÜÉ*9ã’ÅvÛðG˜ n×C«ÕÐ*‘ bèG=JÒ¢ÉylYVºëçýÁrW¯Xçè4YU}Í>wãæ«{‹ž<óúòµú'Ï^½m«­]ŒÉèu×a5åýC ¨$-µŸ?øøÛå×—NÜ>¼ï|aFQTPÒÖõ{c#=~ôýß^<ïiëèéêÅ&$»ÙÇ¥÷õAqV½»¹ésS훺'w`Û»ù‡Âýƒ':ÏU—Xc¤8‡‚Üø4ZëfS°cã•Ò¢¿œyuçÎÛ'Ok>~jljiíìéèe‚O 3°þùWÓˆËû”I}ŠðË|ýxëÁ=õOè=wò*¾Îÿ°ÚØWâ6Æf,QAÐë0^æØõŽ Æ6ý?gL¹„±mFÏóÛh$1[ã¿xÌjœq’.´bÆíFþ(~„:ìpá) !lÂêãuòÚ8¼6:«³³½§écÛ«5—Ë^Š?8³zƒ{åb³ótÍÖªž¥qpªrùÙý“e÷úŠeIuMò”‰õRØåJñ·—˜j‘»vzUĺê¸íGS‚Ïç%\*ɸZ#-èbèO'·Ì;´zâÞYÖq® ±vbé®Rñ6(‰†’íP¼-УɅY£Ý4þL'ÙxC¡5¦†¢¨üªüѪq"¡òh…(¢Ýš(^_0ÞP ÙT$&™j'žh+o-g)k!nÈ¿•Š~–G?É¢T´ËB.Á];Í×(ÑC7Ô^q‡©L¡JÆVµ Újv–ùK‰ºUXô úÈû›·[ÞÕ4Õ6Õ7´6u²ºúpÚÞ¡FÆp7K‰ù¤îâÑWåé÷„ÃE•®œ”1ß;v¶WÔ‚)•±¡OÎ|vãÚû×/>7}i¥C½…IofôÖµµ¾¯ûøêÕ£W®=¿T}÷@ú…$ÿŠ ÓÒ§[ƸRÃ,vÛP#l¨jöz 3=*v¬9“¶ûvUÙÝÓÇ^ܺùé͛ϵ µ_ÀµÝÛÙÛÏdpY 6¤æp¹¸Õ)ƒ8þb¨e#‹ÑXCÜ·—7·ÃH~æWÙ9ñ„ÃC‡|î`ºÿÃlB‚Ú?˜“’lîdÙÂIÊyî*ÙvJ@Åãt«&˜žhqd¢õ±ÉÖž†eãt*¼tO2>2Ÿr¢~¹¯&Ð>Í_­2_ݲ úe Š}tòÜ©YNJ™ÊI62Ñ&bá‘FDâ©örÙÎÊÉÆryv:Eîf¹îÙ¾öÅó&U®_^è-?ûæÁý.œõàþçÏõí=Œ–^öÇöž–ÞûÖž'OŸ?ºþËË3^V¤>LÝxs×¼_Öy_Øà{vã¤ÂÅî!¬v/4%ìþ™ªg÷o¼|õä݇ׯ?¼yùþ-г×Ï=¼óüÞõÇ—ŽÜ¬H?Ÿ¸¥Úæ¾r'êdº«¤Ú*¦Û¨$Y+ÅÚ(GÛS}Lóûî÷ÿñtRЉŒ¨_JsoŸ=öäÖÍ×Ï_à´·õôt‚’Äa3ú!D ð-A ;®˜%L`È „(t ‚bSvÏ tãø5Bl•“Dˆ6Þ¯3¦}ŒÆ@g dH¨ùAÚÔ@ß!ŒpE‰Ú=‚‘~à^ƒ/Dÿ@£z>¬€ÓÈFU#@i1è4@4rûæT¿ÝN~Âü;ÔÀ¿DNDôc쿆éQnÁÉîƒø²Uû ©„Iïîí©ob!ˆB×|~ùðÕÕ#W÷F–o›ž>×8m¢b¦¯t¦‡H¦JµEI(Þí6F‰4¬‡ÄÒÐn[mƒÂmP¨5 ¶ØE‹r“§²ÃNÖßFv—»füd³´i6QöÔz¢›TQžp¬•4˜<1¦Bq¦|i4‘T{‘4Ñ$‘x{¡X{ÁáGqÀ‡X#‰H]á(=±h]±0 Á`%¨ˆ¿,Ú¡ˆ"tbE¢ô‚¨h› ZÚ‹<ÚªŒ¶ª e´YÅKTP°‚|–(ÞF:H—XAª Ð|äÐBQ4G-TúÑP!jÁ„ü uçK³î\8òôÎÕWÏï½ÿðâCÝ»ÚÖº·5Ï_>¹üôjå½Ê¸SQ ³fë[cÇu¶ŸlÎ4j¤'e••Èé„U¾×+RnŸ9tÿÊù§·®½¸{óÅý[OïÜxxíÂsÇN¤M 9¸¼è§‰ùsìr'êçx«åº)Gëð…€Í¨†‚´Ð¾Mºü[,¤Ý4cæØÇ/ñÍݾ¤:#ìÊ¡¼Û窞Þ8EŸøü®¥å ØsŒÎvv7´ÛPáòl\_R@~A„àM ûسž|Ü*ùñOµ‚±ý|2KJ.9'ƒ¯ê¡oÖÈ*©µŒª_÷BüòíWrŒïj,þ¿Ûƒ ì ï§1KFN^ònÁnÆ&òrqJYJ ›’ Þ?™“ºÞ 2ßînl¿ÿ}ƒ¤!ÐÆÀŒ3#MðpÓNè«©2]=í]Ý­ Mõk?¾ùøòùÓß®Ü=uàZIò¹¤-Õ;gXãZ¹ÔòÐBƒòÔB_™UåHþÍó•nž~õôʭߎݺvð×S9Šv‹ù¡r«Gåj‹ª†•?¨W-ÑÊ™è*ºÙAèGS´ÑK¹8`Á‘äÐÓ9©WJóoÜwã@ñù‚ôʸ¢€u…þË ×ÎÙ»Ôoï\—½SÌó¼4sœ”öÐdK\ÔöXÉeXÉä:«ä¸©%9*Ƹ(Ç× tS ðÕ‹œc—öó䢠¥Õ©;oTf¾¸tüÅo7ß™U°Ý)c²M†I†‹VM)ÙT÷àÆ•Ç÷~{ñüÑ›·/Zë›:ZZÀ³m 9l¨YƒÿªÕà€4 sHÇÏ QÙÄFŠ3È2–èm‚Ý€±N”Ö’s\fû-ìÀø›ƒÿÞ²!”“QSêéþºœxtþ~#Â4"1…$X…„V˜“Éoµzrc—H3†[ذƒS#id£:ÔaИ§ˆ@¨±ý—F † t!Oò_1ÿáNÃU“CClV+½³©õKÍǧo_{zýĽóûï/=—ŸöKvòÙÔ¨sñç"ÖŸ üñôæ§Öø–϶¨˜iV1øbªaùdÃ’‰Å ‹Æåúèåùêç×+œ`ËÊ&个ì§Z讼ׅ’g/k/AB¼™R‚¹F’•F’Ʊ&”d …T• {µt{j†ƒ:h;™®ú@iÎú‰ŽZI.ª©ª©î* NŠ1vrѶraæÒ†¢ÁÆâ[4Ñz5´E  ›› …š‹…H†È„êK‡èIéJíÒ— 5”JuÔÌtÕÍñ0Ìv×MuT·UŒµUˆ³¡zê亩帩øèM2É™d’1Á(y¢YÒdËPÃ^ú!S­“—ùîݶ°*fý™ô‡¢7T†®:´øhÀ‚3³Noö=¼Ü¦lŽî±Å¦G—˜V.4.o’=Ý ÔUq«­L°›vÞ yó&.šRùÓœê5ó­˜V±È»|žké Û}“-н ŠÜµŠ]¨%Ž*E4Å"k¥;ͽ6ê9VªÙV*Ù6*Yv*é*ÉN*±Š»T¢\ÔCÕÔ`ÏésœË6-( YWå¿?qב¼¸ó•¹¿«|póÌã—_¿¹_ mu qg¶AµZ/‹û¦‚§šÉã®ÌcRõ­/åwPƒ…ð_›H¨@5$潑ÁSHà Þ-Æ#þ8ÿŠ*†Œ~xúݦäΰþ…0!-È7WÁ]3Bð+y<¼@Hï$´ý_ñfßbñ7;'A‡pÁê¸15Ì¿C f;ùp!7ž¨Ô,&ZòâÂÉÁÆ®ÎúÆ:ÈÒ_Ä™âÔC‰;+"×W†­+ñ_‘üÃÔÍÎfó©bÓ%Ñ\I´BmÒÀ:ÀUìúؤ€6ÀsV­@‹…Ñj´JÓ:9ªÞIå¿G€ =ƒÛ!Z Ek¢h*ŠTEÑBRGѺ’» âLã)121†Rq¦rØ,²UM±UI¥QÓ´3œ 2œ “íõci𑉶WuR²S‹´¥FÑÔ!:l¦è¯+½^]tUh‹ŽÄN#Ù SùP+ÅÝ4µpÅ0CùPJ¸‘B´™r¼µzM ìµ\O“\O£=ã Òœ4âm”b­íUA£ˆ3§„éˆn‡KSµT-–FËÐ<)´@ý *0GMBdÐR#Ê.«è‰.ÛLWéR)òÿ(‹–È¢å2h¥ú æÒh=è!Ṯeòh™2ßbyþERhž(úA­W Зܥ/¶KO(\Ÿ?ÑL ¢f1À"M«Žb¨(VE)‰Åi+…©Š‡RÅc äbŒåCô$v[)ÆÙ©§3Êo•êii¯d¥¹ƒ¦µ{–KÈ,ç]sÝcVNÚ°´,aÛÁœˆÃ… çŽÞùõä«×w?}~Qßú©ÙÚ3Hg ±ØƒÐÒž‰Tã©Â5YdS3R𰙀Ëþu¨!ü±¿WcF@f$η%àH0Ißêä˜Ô¯F•”¯H‡!¶%`«"£€c¢ÙAr$ê *@„Ü€HðÀsìÊÁ5e@¸ý#kœêcï¹ÐÿÞ°ApÄŽÀv7%lðð†7}@ Š@¸¯'ùß9‚ÿØ„k¡°žH¨’j—ÛÔÔðùýËgWÏŸÎKÌõ_;Ï#zªmìZÊ ·=ó|Ê—M=ºfæ‰5ÓŽ,öª˜E+™`R2Þ oœf¦“Z–“F¾§qÉD›’IvElKül ¼,òÜŒöz˜–·Þ?ÁºÜ׬dœa±»~‰›N±‹Fƒj¦¹t‚¶@¤ QB;)(@™o—º@¨¦`¸¶p”¡DŒ‰d¬¹t¬™l„±ÈT¶øvu±MJB`ì,F @NAêÅÐt„ƒÍ@3šÉ‡¦"4áå@0€¯ðÓ‚h!šÐ"~´B­M‘h+Ì(±ÙéPCPuÄ`ãT[L;Ý7ÓëlãÝæª!†”c¥ ÕP+j”ƒ^Œ«q„“a°­v˜³a‚-wªWù¼iG–ή^ UäžÅíŠýlË&ÑöO±Ïv5Øãf¼ÇÝ4ÕÑpïÇŠÙ~Ù^´¢I޾Ö%~ÖGf;^èzb¶õáI†‡ü´x(—»)ìs¤Òd÷˜I$ë FãZm“D 1ºªÁÊ’Áª Æ* þEèË…R´$¶iˆmÓ’Úª)ý³¢ â|q4OùI#waä&†|”ø¦èÊN1Ro¬’î_]zë—ã/ýöéý³––ºžÞ:ZwôÕç¥$+kAvˆè$äB`¯ Í7Phó/HÜp„_ƒ ß`÷,•YÅÚ   ù pfLý€c| 8 5Ì`Œ"„‹<éJÍ„ !ˆõ÷õsÙ[9Àe r{û [$‰º’Ä€Îõä°˜P¦ÆeÒ9Ð.ŸÙÍbt1{»X¸'$ Šá ñö‰›ÚâÞ×ð˜&Šhƒ;¢õpÁÂ…=ÿ'ÿΫ’ÿÅŸç8‡k1Ô "°§»³éûw=ž³ÉÃzÍ•ÅâE’h Z ‹J¡ùbhúA­E«%ÐIÜï ¸SV‚EÿZÍRð{¢5"hƒ$Ú©„Cv€Î#‚6K£pM¡8C©-á*”–pŒ®X¸–@”®p¨_°Ú¥¢ô…âÌÅ,%ã-%ã,¤ <”b¯šâ ªè$;4$×+‹mÑTÛ¦¯»ÝÀ`“¦ÎzªöZU­e2*s¥f"±HtžŠ„`°H\všv Q¸‘æªÔ%Ñ@Mé(c•Xsµkõ;m°§µDvjÜ8Æ’;5ùA=Û ê™ ú¡•Bh¥0ú¡…-Æó¹-àCKÄÑR ´@ÍD‹DÐrqâÚùÑbA´X-BèG´R­Ck¥ÐY´F -F?ò£`ð Á„Ð ´²€4ÐnªŠ¶¯dQœJ5Fqࢡ¢ÝZ(F‡?RS(BK&H]j§ªx† LؘÊ@1–„†f¯Ê^¼n¨ u›¦üzÉ-:ŠÛM4·›ê¬7ÔøÙXgµ©þM5W ‘†ú;çÌ>”œzçä™×o~xø¬ës¼H zŃS“è«ÂëdtÃÓˆåèD2ÉW¨„xGgþj"^–óÇ$ö@?y¨EÇb& „Ýû±‹ŽÒ åïl\4 ßà†ÄÑ8p\(æå`è $Ö…lHшCì€Üª‹Ã„þÔ°ÎÎiDZìþ~£òŠˆjW£Zß7vµ6÷¶v²{1„Ð=½Ý &Üù‘Ø€0POßÛÙÚÅeÝM­Þ^×ÔÑÔÖØÔÖÞÃìföu1ؽL.¼y ìMF<­ñ‰ì@2'@ãœ>ùß8ýÕ퀟ä-ù{ð'ã\ .‡ÝÓÃhüÒõúù‡ '®¦EZÿcé<ïâI6yn:Ô‘å­³ÇO7o‚n!ŸVñx²ñšû'hžjX5͸jªéÁifû§Z‚Ÿ¡`<˜$ûÇ›•xè툰K·VÈu¤æØ«ãð‘Tœ™l¼¹ØGñfHI°T€R˜& Óª…B@îôQ„‘@¤™ðFe´V­o0äô‚„òcå$4Ðdf"¾H`ž°Äi…Å’Šó„¤dafñIÎâ‡Át$2 ‰Íå}f)?Z#Ž~–@Ë@ÉAXÌÕE24 _u‹V¾‡NÞ8í GådkÅXC…HmÙ8C• šn–­þnCÅP-é]9ð&Å›«Á<ÓA?ÛÉ0ÕN7ÝN7ÛÉ`¯‡y¾›Q»a±Ù>óB/£b/ãR?óCSi•SmN±­œL;0ɶÜϪÄÛ¢ÈÓdßx³½žÕ“j-M“Úë,Sà(žeÍ—f‚Ò-P†…@ª™`ª¹X†•t*„ÆL¤‚4…wj‰*h‹éë‹„‰G˜J­W$ŒV)´D»µ3p¥@³`Œâǃy"‹)rsd¤½Ý…DÇS”—Ù¹flØvsÿ‘/7×ßyñêú®Ú/ô¶–®Ö¦ÞÎ6¨÷„¤ö JÆm€@'Óˆ·„4tþqg ´-êæöƒò p°P—>ÈñégAüØ,зDL9Ø= tH#Úò°û¸ðd Ð" ,! £A5Ð…”4¨EìTêµÀ4Ò»[z{DꛚkZ[šºÚ &×ÓG‡^½Œ¶ö¶ºÚ†·o™--­Þ×<~üñþÝwnCGëwï||üàK͇¦\µ)¬½½½PÊœ›3Õ·M$hc݌ŋá¿púK¨c¸÷ʃ÷, B(xá#³¿½‰[ó†õøÎ夈X?Ç3p¢‚Ê‘a,U䤒b'ì"“9Ž’ë¡ë.—ç&“ç"ï*Sà,“í •f+‘b+•b‘bé0”l!‘f!Â’b&šb)™n#“`&¦Ç—d#f$¨Íf,g£o§´Ûši& ¿¦YKfÙÉfØÉ$YKÄ[‰$Ð$“d3Ü”“âhòÑVr»-⬨)4°DÒÜÍcŒC-´·¨l7PÝi¤±U—ºNMqEj¥¼ô*E¹Uв0^"-²HŒ¡Z+-¤&kD3Q V—تÀ¦)šj¥è—j%Ÿb%›h.y8á:òp ijV­GŠüà8Š7”‡•wQ…µĶÊ! %¡%k o"›j¡˜ISÎvPÌ´—Ív¤@hüÞ{\åsœ)¹îй.òéÒÀŸ4[©tštеÄn#~€ÓDK™$ùD+©3±d+Ñt[‘ds­‡²h"ñÆ(JÅñ ¥ÙÈ&XHCX?ÊB,–&ìHIt”‹µ‡ ^œd²³\º;É9àO´%ü’<ô3í 3íÌuvéªj+joÕÕøIUi>EfEn>Uc³‹ÛÞuo7_½Á|öŠ]SÃëíâ1z†Y=D$¨„À%ˆà¤®B È6Ùò Äô‡ì/EŒ„"¾9 úØ8Ø~b ör ÞÉ£CºOÿT@W7d9³¸Pï18€«> mÚ €ÝºÇ·øCtJlgBÛz&Ô>á–blzo§µ‰ÑPÛööeýóõ¯6¿{ZÿæAÝË»5ÏîÖë/Ÿ?U¼ymÜÜ)áS½¢gùÄÎõ‹Ÿ?!qáäðI^YKÚ~+-þi^Ú³¼äÇÙ±2ÂOm_Q¼Ø/~¼Y„›N„ 5ÌA jâB”£Ç›ä,ñ=òÓ¯éAwócžUæ4^?Áys¿óù=^s=ÞðÓÑÑNü 2pdáÈ ³¤ÐýA*ÿ#¿ŽÁË·x¸@]€°žx}t^wËàÇg­—޼+I9³iÁ­‹lm¹÷Éé–‡}´÷9˃dc!ËM/ÑV9ÒP2HƒÂÖà £¢,K¥Õ\;j¦•R² 87¤MäÀ0I°’K³WLqP„œº(3ñK©[¹xš\¼è0xžèf‘\”¥d¸¥DŒ•ìnc)pã$šÉ@Bo·~Édã¢IF¹~ÙÞúÙÞ9^&9žfyžV{½hÅžNû|ìÊ&[NÐ+¯³×O?×K7ÙIm·•|¸…l²£Fœ­J”…|´¹D®!~0e.Ÿf¢œf¤’¨%¡$%™Qª(N“/^›o—<‚Xœ&JÒEi†(Û\ ÐN¢Ìrjªáá Ú¼©^j¼4*}5Ž×†щéF§fšœœa|d²Þá‰:@°°ÌG£ÄW'ÓU%ÁJ*ÚX4–PÔ Îp–c–eƒB†­,²ãŒ…“ˆ<ä4+¹,{% °¤vCžÁŸ`‹½%b–oedàÓcÚŸÞáµîmavÖÓé-5%B$¼q"0|À±‹Û'âéŒ`O2h*¿Çì4&œÌ°XC`Tð )´&j€–_¼ÞN^ã—ö{w^?üü`Ùƒ’üË©±Ç¢Ï&‡ÝÞ—öø`îíŠü{Gw¼xÍþô™ñ©–×ÙÃëìb×7÷ttvöuÁ{M; *øa ú™ôt¶t4×wÔ×t×}¢zßúàÞãCF†®Y™1oV´[½EIˆ£é.G£šÞvkí`šIˆ½E„ƒE„£Y¤Q˜N¸µz˜•J¼5ÜL*Ø@ ÚB4ÝQ.ÏS±d<µl’Nº«rÎxýâY6e \ö-öª\;ër¬ÿ“Ò´§‡‹[üʪyËnø2ÐÝ=ÄdAw.Ú‹ŒL8ÿahó-’üoÇ5ðjæà0öžu·÷×¾mýõìüøó;—åM¶.ð5J·SŒÖç h­Ä~/µÊ F1æ*Q&aúJÁ²;”Ew*Š«ˆBŽG€,ÿiä/Ž6 £uh­Z/„ÖAØ ‘´EÃM)a&rÁzbº¢;´!¿w»6 ¾pž` ®Ð-䯊~Cë%?¸AÔùC Ä£,ävÛ*Jã7½HKÕš2ëä„VŠb£` a,FØÁ 0ˆvÍç'È&#D3!À q"„ÍX Ö_ŽÐ>´UmA»dQŠ®h±Ò7õCžÔj/µj/ÕÞJÕ^ÊG}•OMR?;Uë · Ä+Æ)ô¥ž˜fxf–1Ì«ü42­…!ç0ßQjM ŒÇàÏM(—ÑÑ Ó‚8]"Z+ˆ6Š¢mÒ(DY$ZG:ÑX1Ù\)ÅB¹ÐÃ4ÛA?Þ”¤.³QF„äÏ|8R¶ˆvA b^ø+ÁŸgÈ ?„&@>€š"…ì g@[aÇtŸ£©Ñõ®±Þw7ìî¨g‚÷„ÕCÕz,³úYLxeèø%è=Þ¡CÈš(Bïeì½7.§$ò'3¬Ã„`»“ÝÓÐÛü©ûÓ‹¦7ž­8Q¸aÅN/‡`Çî–íô¶¸†N¦…Oµ÷oöÃüª”´Ëûöß>r´õÉó®×¯kï=hyýÞØU÷©ãsmÛç-uŸëÞ7Ô~h¬y[ÿö%üÚúìÙ»K—N$%†L™2[EÙ.VBdB!ˆ~r~4_ÍãÇÁ¸ap°@mWN¶¦VøYœ˜ípv.íèÃCãÕ«ýÔŽø)ñ‘¯ö‘=6^ñ £S“ ŽLÔ?<ÕôÀtëò9öKÇWnœ_¼ñÇ“)¡—Ksn<\ûü!£½…Mï] ^lÀÍÔÀCùqDr4s’Ò=…çø±3šgÊ0ø°úú9}=]к¶íίOKs«7,K÷¡íñ0K·×€Çh¢©t¼±8$ço¤`ßü)óà†»—‡€—!´š­åGþ 8‚h-Ú$Œ)›'Š5’ߦ,“MÑõ ØQ°FçÂmSã_.†6É£Ð|F…€—ÃH<AöÐOÀFiÌÿ墨³J­“†ÊŽË*Kl5P 4ÓÜf¡âiý³­öb ð¹¾ò?üzªéå]zãÇÞ–Ú®VhÎÓÜÓÖFomƒf˽Mô¦¶Îú/Ѓ±»±: 2Z›™m-¬öVh<Èéìbw´“mN!ªEoif´44¼~ÄúüzèË›–Û—¯fÅg,žàd±ÉD}4Ú %`¦´ÝLÞß‚²ÍFq‰Ô<ª€¯¼ÐzWÚO—€É^U¡· ÷<8PÔpíÜýC%÷ªJn*¾y¨èơ«•{®È¿Zšs­8óbVByÀ†°‰žKôT~P–^«©¸^ò“Œà €ð&9AÈ׃TUˆ$«%›N¯ÔÓ´ÊÏ šž`|ÀG»Ô] ßNReN%öÂ¥Ž"ܤŠi’{LD2MÅ TJ½Àò5HvÖ‹tÔßl¥<Þ)|Îä¤+)+€fq=ÍuÌÞŽþ~x™nß;xŠ i4‘ö%91£~÷åÀ´ú‹ã{–¿CtZù~|Ü \l&âwAj|çâô$HQÀ¹ }ì.Ôõr¡«£‘ÛT×|÷·³±Qq“ü-tÔ7ÉJ¬ãÿ î[PHÀÙ#“šò.‰–K¢Ÿ¥Ð6ENE1B»ƒ/ßVË©«B8¬Å# ÷¦" 2"*VT6ˆ!iÂ¥%¸ è0V‚–` šj*•n.“a!e% µ„I&bšx[0@¢Œ„RirzÞã®UàmXèkR4Þ´È×|›a‚­j’½JŠ£Jª4a&jD‰µ¤†*ëÊ…ê+E«Å˜j%ZA8ÆxM»ÈY·ÂÛäÐD«ªIÖû}÷ºiïŠ!Jª-”j*d:*‚{$ÙV|ÚáB»M¤Ã ¥ƒµDü¡œ¨Ã‚L›M²h½Z Îg"søƒ—G™!p6ÁpD¶+¡0u¥3¢ þH‚ÿà«ÐUmÀð…«bÚe_bh«¨+°¾À&1‚? (Z] QOÂY¦2àœÏ¶¤dZÊ$‹)º)£¡ ùlå<7­"/²›”øšùZBºàœ”RTí ýF>ÒbÒ±V*À¥ =™…0SµH X[ÝÃ$£7­ W­È‰V™+¦Ù½åQUAý­‹]/îw¾~Üøønóã{½o_´<¼ûüìÉ{G*ï?ôàDÕƒSÕOÏŸøtóJ׫nj/{ß?ï~ ž–W@]ï_ô¾ óú‡·__<÷èpEç˽×~¹žèé¸UK>BO)ÛJk'èi~³XCÑ+0«]•ãÝÕ"l•"íU Þ—µü6c© Ŝ鶧6ͬXáW¾Ì·t©oùÊ VO†^@¹ =Rfڥ̰Nn•>Ý:sºMæT«DoÚj€±\´µ\o°® $ŠG+Ř©&[kfÙëeÛi¸è—y›œd}h²M¹¯é^7Ý,Gµdk¹Jš|h™Ž ©4Ù8s±CA(È0’ ÖƒØåøË¤ù7É­“ýA\`¬øq!7 Áå®´ê´¸/ou5|è¤wÀ šá­^¸ï4ŽF<î!’H˜t'qr1™ÂºÁÿÐŒ& üîпƒ—Ñ5ðB 5ÃŒ~}¿g @e$ "þ=½,°mðŠð’#pÐãîµÍì–O¬ÚgÝOn>©(,[»,ÞÓ%ÒÂ(P]y‹¤È&~x¯D«EÐÏÐó·FmÖ 2“‚˜Qš‹ 8{Œ×84žZå­|jŠæÙé:'§jVú*—z)y)æ¸Q ª›m£GSÝc©˜añT¹šú>gí4c™L3J®b¡ƒZ™³f™›æ~h$î®Qæ¡^æ©Qá­Uî¥ ƒ}^e>Zå~:ÇfYŸg{d¶õþɦ%ã } Á/‘îBMsVKtP‚b%(YJ°UÿL´¹b˜‘ì6ªÈ>T­§n¤ˆ€ÍµANÔ_A$DM,JS,F_:Á˜´[2yDÔø RÏ;lw$†x ²Aب€v¨‹†éÊF*D*G(­“ÄÚl†Û* õ¡ëåøÖÊ£MÚ‚Aæ’qò`Ôx¨í÷Ãü9è­4Êí¾ª¥žJEžÊ{Üð üÉ·ÅüIÒ—ŠÓK3Æü)uÒÊ0`¡ä[+Ûcþ”»hT¸j–»ªcþxQ+¼5ʽñ`ŸõðÇœ6ÎÀp€hJೊ6—¨ù–?ä…ÖÉ ¬§Cv žD€¡D€)e›™âzseGýÄùã˶¯º”}·$ãnIÚ­½‰¿åÇÞ´Øw§Ÿù6“Ði´¤½Š·Ì;Ÿ¸õiyÒ»#yo«sß)xs¤ðeUÁ“Š=Ësîí˼˜¶ûpÈÖ¼UËφÿ~êçÅ~îÎf•n–'=-÷h‹å鉘JﵑÛë¤˜ë¦ Žñl5h.T ä§Sy\>^T¨°;ºö놉—~ö9½ÌýÈ"ÇCóìËgÛOµÌŸdœn=_½ /í 4w­GµKù]†’[5„p½Œ,Ú@á‡zÞõr ÛlUÛVä/ïH×\.„8Gï¸àñºÂy L*@_QH¸ŠÕSJÐSÕSó——ü™"1O‚ß[WöŸät&;ºæÁåÆú7Mí ½Œ.n? ¿‘‡xîK¤ËB>v³ƒN?Aò1”ißÉûßï a(A À’[ ã,Hê†4"¨ž#³àJ!O©¯³}°«} µ‘]÷Šóö6ýáÙÏgŠ/îÞ¼gÖ¸ÔqiözÑ:²)Fò©ú2ɺRÉÒiæò¶Jö*ð¸/ž¨s`º^õlãóL..²¼¶ÄúÊ|³³ÓõŽOÔªž¨þœ,9ˆ›D[É@Ð'Òœ’a£•e©•l ¥!µ[C:Râ­5óiZ…ŽÚÅÎ:¥nºû=õ*½ôxëVzë‚ÖºoµÔ[’óOα:·ÐîÌ|Ûêi&û'èî÷Ó¯œj^Ðüqê ~Àƒª¢Œ¤6+ ¬–FK…pÎíO`G€º¹¸|ÄœPÆ–ð£åD¨—t׬Fèg°hv(mo˜`°2¡¥­¸&B(X 2gÄB4%#ô¤cŒ¡ …d’…$è<™vŠ6Š`NFë‰@Vs‚¡D¬¡x¬d‚‰T*hvŠ WÀãoß­Óuªgžœü1þ\^`vzºî±I˜?€YöòЇøyD$Ì5?ÑêR@)†ÊÀŸ[­"í'2WÝ Ì`Xy¼´öy¨–z«ž¬rŽÅ¹…¶gæ[ÿ‘?îši Þx(åÀüQâëøüYý-ø°¾ üYJ¤2‚‡T_{`)ÿ)‚h*d*Ь1RZeD‰ð1Iœn™<ôp±MùJÛüùº™S 稖ÍW¯üQ÷È Ó³¯îð¹²c¹M¾Çö:ò“÷Á%ž¥‹Ü÷-ðØ;Ç=m¼m¸³é6šyø8·xo÷x»{ó<³}N&¥4­ƒÎºU®ÚÕºÕ¾U*&—N6*bðRä­±’þñyÖgt8¾ÀæÐLÓÓŒŽ/ÀÓÌ0‡ @Ö8õdGÅšBœ­Ò&ÁŸø¡§"˜K!Õ×ùÑ2¸"âzÁ®\.€w`þƒy»æÜI‡á7÷C¤>ô¡D›IAâ:(Mñ°—ÎaªqêU^êGü´NÖ9:U·zªæ©ÙÚ§æhœœI==ƒza®öµù†f꯶ß]±ÌC­pœFš#5ÒRi§ e³e‹ŽÂf9qPÖÂ}Ò-­D‚•Ń”E¶Hã¸ò(ªTtì$i§2 ¤"°,bÍDâŒÃuQtŠÅ9udf˜0k%Ð*¨zàÇI¿Ëgˆ;æpóÀ]"³Œî%€—|«…Ä~Z'$´CBh'ôEâ(X†?”"¸K–?òmä 5¨d–…ê^GÝB'½B'¨ÏÒ+óÐpØ7Ûƒ˜œ)2Ør‘‡€Ô~OU˜ƒ»0äèTàÎÑi§fkšC=9SõôLUàÏ•#ü)§Xê¡^0N ÊÆ#¬Tv+l6Pþl’ãÏZèŠL QV? „×Iþ„¨àj0BC©èþðA¶Rœ1ÿ_óPw”?PRñ•?|äÏr~UÂ"+…€Ö‹lÏÛ*0 „·©Èl¥Jo¤Š¯WÞ¨%î©U0Yg¯|Ålµ «®¬5¸¼ZóâRù‹Kå~Y,sö¹‹ËT¯®2¸¼ÂøÌ†U³ô*¦èAëø gj2g©a¨ §¸FCy…šÊHª”^G…"‹p5qhþ á_€.ÐJ(HÊrÑhe¦Ž¶ Í‚4Qœ¹Hº½l²d”ˆnn)åz|ÁÚ¸ƒh•P³RÓrì²#asl¾Œp-Âý°œŸµÜ B@ÿä~”ýãýP⪿ÏStÈ"°ñý€={vrE4ùG¥}nê¹NJùÞZûçXîm¾Û—=A+õG‡_rwÝ:œS{ë çÓS^{¯§m˜mX™PÁÀµWàÍ€ÀN OÇßjàÔþH$Ô€ÙùM8‰WšÔÎ@òdsËxC"¯³™W÷aøÅ#æµ oöæ\ \jÔ}sÍ÷Í4©šk%ùN²%Î2.²Õž ‡ÇÉV¹KW{Hõ‘;æ'wd‚ÜQ?ÉÅNM;=IâÜTÙ˳U®ÎÑ8=I¹ÜU:ß^:‹&o­jª¸Q›²DAb¶ŒÈ\qˆþÞÑâb»Tå#4•C¨2%ÑF)Ö.èÛÀ? ¾P ´M ßEàb/Ðf xðð| tÉ“ÇÕ 1†‚à†ÝEÅw#øQ¡œ*@E6HMeU5PEi+Ev³´ ¿¬Ü&)™µ¢âk„E7KHo—•ß!§¨ ¡ª!/)‡"àFR@IÔÿÅÜ{@ÙY]gÿïí½÷ÞË”;½ª$d:ÛC(*#4½÷Þ5’F½‹æÇ¸Ä .‰mâ8¶;.q„^$TèÆúÿÞ;²Œœ/|ÿûcu× „4gÏ9ûìò<ÏV’ÓET!E«CËvfL{Ëíse¶Ý%¦¹2 géèRÛ¾JÕîRÙ\¹b_™b®Dv Bub‰éäRÓ‰%ÆKõ'Vè^m~t¸Yó[û\¥ùÒ5ƱO¡«)×õ`Øö‡á&“æf­†X÷È"²š·OkÀ)Ú'‹á™·Ï6Ò7ˆ¨ðâ1˼}²†úpûD¥´O½ËÜðAû˜ ›L†¬}Œ÷«5¢oÑj,Ö­VÛv‡³Åãêr›-’ƒÐéÖÄ­IKWXÓvÇd'WG¾xSâáÕÖϯ1}åzËר^)<ºFxôZás7Ÿ¿Iú¥Oê¿r«ó 7»N|Ìq`…s÷B×X±£3i¢G¶²Á¨|À¬½Y&Ü(Û@ô}Öê¥Û¼ÚÆ€öFƒ°‰YØÂÏÚ.lFDÑ!lÆíEíDJåëù‚R90w<7;¾Ü1ÉD¶´0›N.Ò_¨:¶@~b‘üáåªÇ®Ð>¶ZóØÊ/\)ûòÕʯß`xâãÖo|Üù•kìGªU£)±c»Ù"Ü®[¨tNi/.„≒ß)QÝ«Ôn4™›ƒÁá‚ü©²‚ñ²œ©ŠÔtebº*4]íŸYè!7Ÿ×ÙÆÆ.1Q…ëLÈ:BwZ6¶¡Ø0]a¡f[hD'j4c˜*²ôm ÙzâÁþT¤;hòÚw8L NK‹ßÙð5:íõvk³ÛÙæó°º¾¨o0 ðHúÝÂHP¶3¥ß›±ìÎ7ìÌQïÊèæŠô{KtsŚ݅ª]ÅÊýeÚ# xZûˇ«5ÊS9Âh\˜L Ç©.”](=¾Xvj…êÑ+5­Ñ~vMÖ>ת°ÏßÜdyâ&dzÏÒßÚçS‚üAyŸRû ÑÔì eò&K3ã%ééJ–h`{ØyÔ>Y¼ì¬¬}TIɵO¾iÞ>íAÑ>IÑ>ÍžØ'èip[ëfŒÖæwµÈpEûô¹UCnÙH@37O%ŒcQÝtÒ0W` 'ªí-µŸ(—=Z%}|™ò+Ëå_¹Ròø ’/ß"ÿÚmÚ¯ÞjüÒ-ÖG¯·Ì-Röæ Û\Âz“°V#¶ yn``q0p/|ò¯wiðô]½…Á‘òÈhEpfIÞÙüBzq÷’Èî%1> â< ú t=yê6†yÅÅ#®œÒÎrÇd±eæ{®v,cž,ñÂV£ÏØ— ³ß¶ »Ùç˜_í¡ßî×#ž‡V¿§#øÿy8“¹" ‹Ï}Uòã+Œ'Vãm4s‹U‡VŽ]cßµcæêàîO»wÅç·ú‰¡†Ÿ>²ÿô¿KíâÜ«¯B¦ ¬[W#¢¯Åƒ^W“M|D'ri}ˆ“ÉF8Y´¬b ¢¸øâ­÷Þyýì…çŸ;÷Ô/ßøÙñŇ¿ÜÛtlÝm>qÕÞ5‹g—ì®J]Ü]¨>RiùÂêЉEÖÝÒ™\a:Wxh™ñäíñÅÊK-W=z…ös„4«Õ¹FþåkU_»AÿÕëŒ_ú˜ñÑÚ}¥²á„HI¦WK+¨r 4b®–©nRé>)HîÑqVßoÐouÙÛáüx_Ax¼,1^T†èÄôBßÎÅ>³‰ÇÍVØGËmc•pl£e–!˜Ý¹jâ™´ª7!ï 4_z¢²¾¨²;¬ìOÙsÜiWOÜÖ04ûtí="–¾˜½3hê{"Ö¾¨³;dëZû–=÷lÊ4“ÔíÉ3.u¨ò©pì/1Îæ)fr¤“Ia_g¸dŸ2ùPòCìsÕ%ûȰϽ*Õ:½Ž‡xÞ>¡EîREl²*Tè8Vì~¸*pªÂõÕUñ/¯ð¶ÊøÅ…ú¯,5þõJã·np].ý˜ìøµš“×™O^ï:rµ‡v@w¡v£[ø 4Ø,öÒ§ô“ùmúò’ FÕ6¹1joÏqvz{Ê<=e.xpôȆÊÜ£e$[g*ƒ;«‚ ð¢ÿ5cªÚ9»À=•Å@‚‡ÌQåj†s´IUoDÖ–rºÑ<çpž{0ÇÙ›°¶‡Œ­}[ó`î‰Û/í7bçÚyØS(™J‹'d¶@8¾ØøÐrëjõ\¹üÐBíž éÑeúS«M'®0è>r¥‰,àØ¦Ï~<|âÆè¾«¢SW¦f>¾ðTÍÝßÞ;ù“¿~üW?þá+/½Œ“ –j¸ßåÿ¼eáß9™yoó߸J54›D=ê·ßÿÂ;ïŸ>ûö3Ï^ø—ŸœûÞw¾1:xdÝ}Ë+»ÊsJ“Åቢð\Yd,¬˜  M'¸ŽVØN,t<´Ì}héèbóÁE:r‡½ <öáŠc9‹§–˹BñȪSË”§–êØ[å>¾Ì=SbcvJ¸¬EÉéÇ¥ž0ºÀàÇÈøO÷é%íÊvySÔÜ7ÓGîÌ3£ñ‹ÞïD•>Ÿˆãy^™8y]þÑ«R0,8N×H®v$W‡¾ =ß±\coTÝQ6ûe|¶YÄâÆ6sVñÆF]Ýê“w”°âæž°±Ã¯íòëz\Ê«¤Å(Fõ{Ce¯Wh· |öú„þ€0&“Òé\ÙtZ:‘f ]¤; tG»N,õî«$±ElÆ>ÒÙWpưϡEÊ#KÔG—)N¬T<¼Š¥:±Lqr™æÑUÖGV¹­ð\¶¥ËÚç©ô&©çŒYXTæíó Mñöç<\î_à™ZìÿHö鋨ñÆ­—íc­$*]²²= îûæîˆ±= íñk&£Æ>“Тz-Âd@7Ô÷ƒÒ ­:a"(Ù—§:Qªÿì"óCªÃÂÑ ÙÃkL'®²YeBJúÐ*çÑ5!ðÒˆN, ×E (l|R.|2«°AÞn<úî,‹â (@S¸PJ”7å™!’€Øì-r •y¦*ƒô ÷.Ší¬òÓn†®rpuüÔ5y‡WÅvVºÆ ½19ÿìI0O“H†ãšA‡|’V¿¤Ù-–¹@Vlo`Ùó0¿_sÂÚ6ãxA}Ôó0›¯OËvh(Nrì÷TfË´û™h¼î©ÒLÓað‰Å†“‹ ǪÕÇjN®°œZ8peljeîÀšŠ¾O\Õs÷mO>þ—OýëOÎ;E”ºªXu¥ž»þg+ ãȊ¸ŠÄ6‘£vy‰þù5_æ;ä_¡À¾õö»âÏ?÷ܯŸzúýø™Gùn_ÏÀ’êöL¢.äåÛ0àíÁšŽ¦­ÄÉûÒöÃ…ÞCž™´¥?(gpROT §©'!ôÄ„ñÕt±z,_6™/Ÿ«+?¶ÚüðÕîCËM{èö.0ì.5@âeLê' |S%ѱÂXGÌWc7€©£|/ɵQü¤=~ í€u Ýd¢€8=RïÆ°‰Æ°RáP‰•£µoEìè•é#«–…÷,ô‘1õ%•]aIOT1”Ô' ýap"œþ” (¿¡7ÆYBÖOÞÓôD4u6Ëg»WÞæVPø¼Ñj3´YÌ6k¯Ç9ð=½k‡C·M%P.®× T*:í²>¿z"nžÎ¡Å`È8w•vUÆò,!‘Õ•êëM ½qa¬P5ULB'+ïªÔî¬R]m}èÑ>s EûÌ–†sU„a)ýX¡gBza¬=æÛìø?؇¾ }öõV’zýÅNÀô–†KmØgïrºr©yûÌ-òÿ7öáÝŸ(°[úûì° › ­[ÑÑ·z”dëÌ’³¶Ó¤ï³˜†ö1¿kÌïðºòn‡Àáˆd2W1‘Nd„é"aO…|o©üá¶Ï¯ñ_j§ÊGÁ|W™™ÚN_Z?Zä/ ûQîÚ씯L®6)…Y¾IOH e8iÂá ¤LÔ‘÷Ä”x˜ìÒõ!€R7»%I‡_ÝCíÓ¶¹5m.ý½~«Zÿ T¹^?(•o”)”J7H…MR‘­P«‘Õj$5€ÿ³ q±Ê}‡WTf—J¬f£Xï•ÌÛ§Á'i É|"R±+© ?2ÄŒ¼ ”¥¢­ ࢊö¡r2œ& ÓöÇLùÞ½©£ËŠ‹öqÃûPû|<Û‰ÒƒàØö€ªûd­¿µO®ñÛGù;û³b"ÀÚ§;¬ivɲvŸ¦;d äkukÚ݆½f‡BU#H·dOÇcsÖ8 ðDÝ UoŽª5!4 Fc¾|í¾BýTB:•€¤Í׎§T;Ùuަ7&ëO(yv/ð];¹:owahWAdº 4’ïCªáõNá.³pM¬ç|Z!B˜€J6x•C¹®Á\{gTÛWσ †òÍCùÆÁ´Ì'{é È¡°uz%}È€Ä cqóhÂÔ–•í Ëɧþè~ɬ}ú¦Õcú¨çöÜZVö<ïU ë´3$;¼2j•ÐL8¢½AE'|:¯0‘íI ‡ ……ª‰%¾·£Ð»%ÏKÌwÿÊÅßù죯þû¯Þ`^N†R ˜¸?o›Gw‰§u9ŒF_±u.Õp蘽óö;ož}÷Í×^øÕ?ÿâï¿öÓÏŸüZwÛØš a‹ËÖjÔ6©%M¡Å$4X%µ@^û¥àÏ+›€ãÄ@úÁ™ÈÅ>éZ…< 9H« ½»@#"ú}½MØЙ¡Ù”pÚg«üsÕÁ]eÞ™BçTŽ…E=d±ÿ@I`®Ô½³Ê>½Øºg¹Ö‰ks©B\•ܳ4NÁ“Ê!­jÔùî†Á$¥%i ji²Ìw£è1ÈÄ&¿/¡Å×1 —'¸Ö,*­^YoT7œ¶ÿ“´ˆ@,Ý@Ò6ÂùL: »Ã–®™ú «Ýkl°©ê̪«®Ýëlq9­–‹¹Ñjj²™›ì¦F›q‹JB¼Á¬m´jMª:½|»·,¡MÏ÷ÆwxŸJrŸFŠ5îDW¼_V‘¬GüWÚÊYXõ]Jÿ ¦éVÚd¡?´Ïd®hŸ]î½%—íc¥¸ñ¡ö¡‡„×HËEô 2}ÍaÝ6‡ÀÂD `ŒƒŠ®¸ãüQû¤lädF—ì“pŒäxæíC ûPŸ¡ŠÕæ5×Û4ÛÍÊz‹ºÃkksš›¬úF‹ŽÏf›ÕhÓï°hÐí iˆ<ˆ"Ì'HèP…^§@¥½›$Ô-̤ô‡Ê\‡+]ûKm»Š “yêÉ|ål±ÀÞ óîrÛ¾²à¾ŠØÁ…©Wžºª ¡0€ß;—$'ª#HOÓªF«ÜËa+áGPG“‘EÄô+D»£*Þ|H‹GÔ¼ŽH•¹Uèpˆ±¤­?B¦¬nõ¨HŽ"»v(áì ™{B–®€‰Ãвµû,ìw‡EUoûÈçᨠW ­é¥ó€Ê"¿ˆæd¶Ù » ¼ž×&¹ÐjèA ¨‡TöäºZ £ ·eR{êw¼ðã:óì3ïœ??O{‹ÃÆþˆ«m re)ÊPÿ¥ÛôÞÛ¢ÚòÙ—ßxî§O}÷KOu Ýzu]yª!ákt›´’Q—iÔ¡´«Üê¾ ŽÊ0‚~àÙÉà@$8 gһʋæ”XRupùÂËÌ.(®(˜©ÌL•ç æGû’Þ´¿3ÇÕSàíȱÕú•ä>V€ÖDäxe&ÓfQÕ¿È5—±Ïæ˜'stGVzö­ÁÇVùO^•|øÚ¼“Ë9º*çË·,=zEþTi½©‡‚HždB"W{\*~^ ôâä88†â9»XdØ£l¤Cºe­>u“K]Ãè"4üÉv³\ŒÙ”âøhGâ¯óîÈÄ=!Ê}¿åmÁå7ðû!l‚C¤íÊ&‡ªÍ¥íðè»ý¦á¨c*Ç·³ ÖŸ$ŒuÆãô†¦ÊK§«Ëv-©Ø·rᾕ f–ŒWd¦+ 'Êò©uw§B=9ÁŽ\uÎöÜßÙG.óûöÙWŒ}¬³9ÆéÍÑ•žýËû–9ÿ}n]†óD‰¿/m¯s+qÈDS;îá³â.öË8¼¬q~gÓïÛÇ«Å>ÛŒRì@Îò_íÃ/òŸDL¸D¼ÄoÜÞî ?˜#HóëvÜôt r&–/.ï*Ë4äÇ›r‚q÷dÊ5³µ;duza‡NØ¡jµB‹Cè p…€ Ð)ÆÛLeDŽ£Ë㇗$-NÍrCº¦FÀá•™ÇoY;`¼8oŽýÎ÷úñ6ì”Òr~¿œ‡‡xØêÁ9|àÿOÇF®»kâãKf®,=ú±Ê£‹ öäúOFN……wv•Ff*âÓ•ÉÙŠÌh&ÙuDý]ñàPn|º¤pߪƒKîª.Ÿ.ÍLçfR4;žös¨ 4Q›,äûÚ#¦·Šó@ÎÕÑŽå¤wíÌ·“;FÕ39qžÈÄ瑌l0WJ¶iqW¥kw¥ŸÃöùk+þê†E®);uEÉÑ¥P²}Q3Ž‚µ•jžI¤qÚñœ ܨ6N‰£È]às«Y±É(ÅÏ®ƒ.­¼´š<úz§f»MYçP·ùô½Q}–žˆe(Ï:Qâ$ žwdyòà’Ø¾êО ÿÞ º¡½¥ÝEÞÙ7Ç\±o®,6[V0˜—Ó‹tÆB=Éh^r²¼`ß²êýËEW˜ˆËuÙ>¸Sì3o"Ñ>\®Øg³AŽ}°ÌíÓèÖ]¶O«ßÔuУ錙Pü«0Ób¦6rxYêТô¾Êô\izwIf¶¸p¶¼bϢœՋ;3%µÑt?4Ÿš*NO—ä 熃æ¿Í*áBuGô#¹¶éªb©úêÑ|Ól…-#ðWƒy†Á´n4ßÂoØ]:NœóÙ«*þòºE¬©:µªâàÒ‰Â(ß'A<ùͬ·¾Ñ¡©·©ìjzpÿ×çÇ·/?§#鈋ça 79SZt`ñ‚ƒK«wW•Ì”d¦ŠrÇò“ܗΈ·#âÎw2C‡a:£){/@§v“Yy—Q‰(Ù­éÈ“'½ö“½ýš8Œ ŨKþ?—»É6 >ÜÕ jÿÒy¨·ŸƒQûì/~öô“ßüÖÎŽ“Þ4y}ùÞªNÞT}…ÛŠð±Šè¡ßÉÒÈC•‰Ã¥Ñ]™ÀXÚƒµ›ýÆ­Nͧ®Æ¥ßæ4n±jPK@ŠÞK¸®W­WIy@ï¡;I±"Û; }À©Þa•x&]Ãi¹¸r]­>ª:ˆN~…¨&—¬SN+û"ߦ‹ª]LE¥Í#+¬ŒÂ³>P{xEá±…|«±]…þ]ÅAZcª‡¤öUÅgK‚€xÇò=ãùþÉ‚ eç™2üab,Ê ÍVæí®ÎìYP›f¶2gWUzÿâÌ‘ÅÇ®(=¼¼`ÿ’ôÞEÉCËsN\Yx|uhϽ }ûùàhïB}«ÄuQäxf,ûªü‡ªÃ¬åÙŒkRFPÓãÓ5ØõÛ­†ívcӴͪ_§‘Z’`¦½_+%Ù3w2¬lc…Ï,"ZÁ¥ëOzsí^ÙžŠÐ¾ê({Ç'VZ”»·21[ÚULë0¶¿2y°:µOxªÀ?žñ²& ýS%ä¿Äâ|Ap(‡êeÞžªÂ¹ê¢=U<¸y»+s³ö)9º²ä²}¯È?¹¦û0vjf™oz¹gçïL•kªØ9‘qL度s{2É>Ÿ»F£f¤ÕÔÙn•ÈîÖj Îo±éZÂîît 77€Šk0×KW½+j¢ãÜT}ÍÇ$0mûr5íIJ§$È  ;|Ò¡ˆne¢C9è"f>t¿ÇW”]^ppqzÿÂäÑå"ƒûĪ|ž›tè`n·ë ;Ñò¨u˜¶Zt(%ë²Ê5:%œA*9ÜnG: höQ!›‹XãÎḇ5˜uçÅîñZîJëjyá¾óÎËÏ?ó*®†âÏÖ|ÐÕ\ lĨæÝ÷ÞD:%Ó·Î&žùÅðõ/<öÅ]#î¾vty¢9ψº5„Äøê½äŒ½n øXÚ+FÉvêœYVø‡NYcÄÒq4zdå< ­.k³Ã\oÑî0©)q´ºŒd¸~óf…ˆê¯7JÀóÓÐÙÆójSð´WvUE¹Ú{ÅAqó.6ÌÁ©´Í US·QlámÒ Ûõ"ŽU§K†nL‹Uèö(z¼êñ„m®(¸·$z°"y|QþÃË‹Oá4–^œwpQ¾¸–Y^vüŠÊc«ª-/Ÿ[\ ‰fÏâü}Ë ö//<°²yðSW—º, Pj¤Ü„uOJۢΠÔk„&­Ða&¢–n§¦!«¢Õ«kòúS®]%1ÎáÊÔÑÅùlöÒyÈî÷À’ÎÃáe†ÃWT\Z±wQï»>°´øà²¢C+ŠŽ®*¦"Äy ¢(ï[œ­vÍTÚf*-eºá|åG:5TêH3Â6M {wÈÑìÐï0*¨ìuºL-=µ>®OCßäµ4íaÇ]é}VU –MK1'¦FKY³=á_óÔ­Yö;ýo?;ûꋨŸB‰úÁÕdk6b%–‚‘‚@øÂëï¿òÂ…§~ñÔßãkûwn¹¿éÚ%íÙ˜ÉH‹°#*âÉSê‘4ÕNQq®Û+ç s­ÚÊ.¿ $]&+mqHaÝ5_ÛÌÐÈmÉjÑŸá®Që¨ÑJjM2ô¯B$ìúƒf<<©q›WMVbª9 Ùî”rÒ¨ô¢mEa¯A›#‰@´3ñÎÆs܃1kÄ:³7Úä|öø mE¯OG –Zng«ZجýÀUeñ×oƒßpOvá!ïaŠÓo!ý ú©šB·¤Î˜˜õÔj¸Ýn¡'­Dbt•ïBå`b8_:èÒù¥$vKcZb0nq§G4K‹Ö§Œfw³YÖjUpì{½Æ‘˜³Ãc¬3IñH|ó ‚{¡Q°¥.ÊF¯J¬üàx5õ&Í@Ð>´ö-ÜGî‹™‹YúrÝÈluIé»aŸû³…j8ا™šjX·3ã&«ÌqG­ƒaópÔÞbS`ŸN%N4LÊSHaìÛñXpOª‹¥ H£|'„<.|W|r)æ)!üØj¾i‚XòbíÈ- ¤ÕÈÅë† C2„I»r¥ÝS‘{¸Ä‹Ü–ˆGòkzÜò^—|À­lÂm²S¹è™©q\ÁµÅô­1cKL×Ñ0Í¡! lãÒ­Íï$¼ä2›Åû%pØִ݄Š~=s9¡ñÄ CaãXÄÚaSŽ…=.¬¶Ógżt%ønÙ2G‘ÿG§E2Å~ùY\Þ¯Uþ×ýâÊŠ³Íï—žBJ9Ubš(ÖdbFŸ‘ €ÏÌ¡a8 ’¡?~écÌÕâ3n³ÈIÖˆg08ß ]ŽîFò÷lÉ 1„ Éf‹b£MJƶˆ ùÖ°»Åg¥ç…ƒêË ÕÅÜ·»tWÙµŸê|êÉ¿{í¹gοqùk6WC‰Îä[ï‰bÈY‚¶("ýëwß9óÚo^zþÝ_þôâ/þåýï>ñíÁÖ±–w¯,ž¼ªd¬Ê –IÉþ\õP¾~¤Àâb(Gß %G?ŽÔUНUó«3¬jñ*Èy©«“ð"ìFV5‘ î.#2ÏßYœÉ çF2¡¡¼@wÜ 4êð8“&úΞ©êÈ®%I¤kÛ⦎$‚ÃÞ‘bgÒRç–·ÛÀ¤q< ’ ÀldÙcMm )gFJw£Õi¨7(Ì^–j›Å\ï´'oñ;øéС…Dµv0lŠØ»<úv§¦Ûoˆ8û£Î¡x`$/:³°hçÊÒ©åùûÖ”2Ø€úN`0%¶–¸å™ö¤œfô ÷x\5”RqäèÀâGóDZrÌX=Íèñ|û`’ñ—ìCÇœù6<§\¼Ñ$ÚM'òûËrçª ¦KR#yb7’‰ðÙw·ú-={IŽ3ÄoÓ<ÚÖu{E™¾ÝSîoL›[Ó¤ÞFK¨‡;=bÝ`‡EAZô¨è\e›ìóulü·i‹AÕè±nsèîU‹5[Â$âI,Ðé5±Ú=æ6·©ÕCKE×ñôE½MN#«?æI‡{ÂvRª™’ÀžòÈÉ#‹rŽ.Î=¶(utarW¡g:ß6’Ö÷ÇÀ¨zã’~R¹\uOZÓdh¦†s2škÊ8§ \;Ñ(if ÝsåÁ©B×pŽu0éQccPÑÓlv1 1YO‰ud{fE´¯ÌÞ’Ô" ;Tè/ö‘LQ¹BœM’P3Ó‹UîùÛJêÁ}P)Ýa57¹\5&ã=R ûݤ—l·H)ö¶{õœV§[—%jiúBÎþˆ—Ø»ÉA#28”Žu†]Sw–†ÅhjaΑEyì÷xvËÙýZxsûãà"D@TZÌ•¦TàŽèž¦´¼>“öùóÀ@·v,#ž0Z,††p€ð# 08“yÑ=¥¹ûª v‹çš÷e ?Йp6MMqÛ‡)PRPì¡—d˜Ì˜[”»%n¿+æÜ²¼òG÷Ÿ{öé_þò—À†Q®»2_­!ªàŸ?Q冿çÝßÌ»ò¸¬„HËzÿÌ«Ÿ}æÂ÷¾õÇüMãæ¹ë—.Îí.ñÖ†¤pÐDò/ÍP¯Ð’tÆô‹Û# 0 ”C§ñ\dõl¡À—Ù'‹ÄäýA#ÈwA;̆§² 'Ó´@Šä§¥Kwo£Y²Ã£BZÁ²ÍQmWŽß‚RÃmyGËü >p5¼qí sgÂÖ5#ÅÀq‚¶ÏŸ@ÞJø4ôˆøZ!ãOæÁÙµc½ÙÔdåE³·z½Í«Þã 6²Q¯ :D Ãe÷“Êed‰µ“’`õAŒÞ(³ÞnTˆ²]·ðd»ä[Àf#{Eí¾Ma}Ò¨²×¯ìÊv²P”b5ºD¤™?ÖªSÅΙRqzöE!Êœ“E®‘¤c dí ÚF“¾é‚(=ˆ©‚8‹Cõ_ìIݶÒTí Ûžr û˜*.ù­ðtUùZ‹] 9榔©=mgX0Œ€vŸ‰Ðˆð;àØæÙG¿l›A·A!#Vy@%ß W> W¬Ãå>¡ !!Ú÷¼øÔ<¹§[´Š­zÕƒJ ——n ™/-'^öu”F³3~ÿ|\Ôl•òp7!\æ¡ íAIGTÞW"G6¿ZP°P¬Bï±€ oçC_tYi¸ãjÀA5GU-qeG¾=dQ޾ÒÞUné«vt›s4MIMGÊÔ•"Ʊ£@:™²J$ÝËž6Ç€uŸ\‚øâ~¥¢Æd®…þi³×9¬b$à7a(êºõfE A’†¤rjÉòæÎïW­ÜjÔã–yYt¬ð4ž°ÃÚOJÍþn¿ n 1IWBÆIÃ3[ØÁ/A´m~]º/¥Þ±Bçh¡m’±;eîz¶{ÕŒlŒcf(ýÅAæ…1Th 8À0èiû^݆ò;>÷ã¼üì³Y9†wP½ì^þ¤®†åüåï‹úôðÍÅè†8/½pñ©ÿ×GNmºâêå#Ìc½ºjïU%ƒežáb;tZ”UP˜-r拪’]1@àèó-㬞²3Ì’¦œ0ƒH¦z柉£+BôൢÚY‘ܳ0g߲ƒW2Lmº:8R ÜÎÚ•Ýø%ñ›âô>$iÓ2°Éê|ˆµÚ:s8ŠræEvåÙè·$\M1OcÈ»Éf½O­Aáá~¹~L·^aؤ1P¦k@a‡]×ì5ATˆ{GSA˜q½!w›ÛVgÖm3ëêœf@‰µ.kGnl{Ê·½$º­"vOÊ~gH·>¤ßàU­C4ϯ؞gÝœÖwVøw®õ÷(±N—ù86œ,Z$ISOXƒi’蔂˜¥®‚}˜¹IÈ׿W\²ÏxÙ>ô5.ÙÇ¥"Òû€} ¯,¤“2W*ò åÙ»iŠ«k|â°þ‰òàtUd¦î•¢Ô Ê—±Vl¡?a!ý~YxHh@%ñ Aã ÅóÊО”æb³Õ!0o š 埰Î.Ü…¾(q£ýEE9 º TºxÓÛ’î–ˆ½#áîM{R®¨§#ìêºk-::ZY Ö+åµ 2ÁûUb¾VO/Û­ë ZF£îɤ0h§<Ò`ToÕ)I]l¦Fý/KWÂC8AÕ.‹ŠJ'y#ÎÝz(rÝ1lcE^¶É1'PpÄ6™s$cg¿})SGD×PýÞ}A1¨Âm~˜ÅÙûò?;ó÷e¦:Ê8f˜¼>­Q× ;8œ;u¢Öñà+êýÕ~tæ•Wa|Óõ¾ìa.qÙùü/~“!‚Ü„«y•R‘Ayæ?bïÞ®›®»#ì»/ìÜuÔ„ µq¨6æ"HSA÷çØ€v˜v4šñ¥ÜpîpMãyžÝea‚ê…þÞ™jµ8mRc¢°”#øƒÚÂ,À9ÐyØê”àU6ñÕ]À±aWi¬XŒG ¼'Ò=gM•Æhì¬HXZzbMàù¹åÁÉ*goÆÔBíMd¾  |Ÿ^~‡\|ÅIÁÛÜ'S³Ö+Õ5&t%œ«y  9 9B§ÏÞòB@nt;ð0u~_]À¿Íç}Àm¿Ã¡½Þ,\ã–­ñJ«uB’›zqìïívÉU2¡ïÊ‚{SÆûâúu~J à@uÖùä(ïuÇÌ”˜èbŒåùÆr¼ÃI·ˆ‚ˆ£†çÀ>»ÊEûÌù!O]¶…#ƒõDY؇F*ö!ˆú}Äá |ÐkkÊzSšÑ"F$8™’9Α®q§Ë#SeQ¼7¥Ë‡VUŸ\V~´ºxOaj8î£öN&>™¼‰ÄAô0r1há&nYO\§¡®.©·Hñ3M6€@š3êf6|Ÿmܵ ´DbÕè6Ô8´ÚÕ˜”÷éeë´R¢£ ªM&ÍF£z£YBM[[ëÓÕ´¬m~õV¯r»WÑÒv$̃y.~²#Û®°Žw Š>¾@ƒGÂâÖZ³Ø¦f„d(±”¸ KðÌUú§ªÙ/ƒMÅv)ù‘E¥­ZxèŠò]K3“©¾<cÀH¨ ûnRit§ç“)b’,n7KŸŒ€n¹Ü‚KÛã1ôù,ãqo/} —©; ¦QÝAO³Ç†bÆf‡~ƒMs¿Qq¯ŽýÊî׉1áF“J\%2Œ5nu­OSÐÔòÍû•;¼ˆ¡É[Cºž¸|273MC.Ë¥û’=»Ë¢{Êcà1>x_þÏçÁ%Ùèl˜xªšG¢À3]è›ÈwC e |WIdc^à¶ühç]·=õO¾wîü[ç/00ór`ƒ«ù“yâqêEvꎘʽõöÙç^xæÉ'w\wýunç æ¥êeà6q©oF >+l•§“ƒ@—ùŒÄ xE€$bï 1(Õ¶N¯D˜ ’ë†Ê÷Î’Øþy#ù~Óý)§hÿBcSÈD†2ÄxyËf«ÃÓäM)Kƒ[Qk“n1‰8:À¸)ŽlfLÑL-4ØáhyVz®‰âÄ®E`yÑzâtd0éÕööEÝMn„‚&»m"ŠŸ4¼6dé¼w÷*åw«U÷˜ô÷ÚmwÛ­·7êר¤¥ !!"R!©®M>µìá­qƒM(„Îëª:¯«¨„êln…L ôOBwÀ½à~©:R~Í¢¸îâïš·^ûƒØov) gt¸Óñ\ßÎ’è¾ê$ž ÉС¹™(Î¥ƒqL¦‚Äxi| ÇÓ›pðFŒ&=@ –~¿¹•çR7Ú dÉ"É+ç×vƒ\œUJA„2ÊÄ¢%•ó¬â1sà8ûç“ÌÞ+àSpZ€þÎÿW~ÃüïÉ6²ÅÁâp”Èà©É8ôuNYƒSø“ZM_Ü@í¢/¡‡$2žk›-õÁcM¡‚•M$«uHP"ÞÃyà;¡^*fÁ*1•f|ùV‡¼Þ§%Ÿ"li¿ rÇóBCI/€Àá”EMŒP„ÏFGšF§r‡U*„‰x)MËD¶Ù4õ= \µŠ´¬ …“ ËÈÈ#Už™Ov÷ßì—ÏßÎ?ÎÑ8?”ßÝüÞü}1Jpò¸z õÿóóÀ}LnÎÐ"Œ‚h–w9Î` •íÖ€–¨ï¥PåÓñÇçö<ýÏÿ OáÍ7ß$¢˜÷0YOó'*×:¡à)ÎRÿÍo ¾ûæ[¯?ýì/¿ñÍík®ÞTQ^_RÔUQÐU–¨JŒ-MUGƪRcéá’ÔP!£:¢]‰Jè·lÕ«kšízuZº]+m4)X;y+‰ÉQ¿KýÙÑ*<Ù ¡bÕÒ” ¨xÑö,ŠZ™³wIspeüøÇr_‘ž©ð2é ¤yøç3/2 : r¨ú’ht»‘ÀT22ò·9­-N’5ÌvŸmOª^*"ô@§d»$b‰o&[s«7 -9DÈ.¯½;èm ù¶z÷ØLŸ1koÐÉWʄłø3*” ÅfaeFyëšðŽ»ŸÛô«¿š{ý›­+ Ü’4ª½ýÕ'Nl^šÞ¶0Þ¹83º´h|AÞ¼}Aç ´Ü­ÄtjâðyûlÓI) `ú;lŠ›Bt—ÀD¢_Â>bz(¾V-[6„¯­Lí]©pYzhMìÄJ  Žñ”vÑ-Ø¡ûŹÍöÑ(ˈuu…ˆ¾èò:Çbá‰t‰Zè(ùoê#ÆíAÝz°Áx Ê,ü(pÚÙÁúé<ô<$w­N]ƒE Áÿ$™éLá(ÄÊ•q½Ër¿ÃrŸÍ´Îb~ÀfÝäpmv29Ö’ˆ·¤íyéîÂÜþòÌpuñè¢âÑ…#•¹ƒe©þ¢xO~4§)dFÛŠø–6ðÝ‘c+2@S3X;¶4ýÈÊÌñe9»JˆœÍ?œ D´¼åÀ”Ÿ,õ^VY|”«+íÈ÷vÄ­«"¯ŠÔy r„."ün³0²ç?ÌØJ<"¡FF„ÙãÑ÷ûMMƒ²ÝIΞxôN[<Ö nûÛýv+›Ýà°³Ù·÷ûMv¦û+ò†Ž-,š¨ÎL”玖¤† â¿=®&·•Â×v£j»^‰{ùÃûò΃ûèŠÄC«ó3‡«Ô‡}zBšfÔÞhéRGÒˆ>ÿF™ð1ƒâÖLúx_ϳ?þ1:6ç™’ùÛjÞÕü ›lUXt2¢Ÿa‚(³½PC~îÅÿøÖßÝSQ}£ËyBö)µäNØ^umT»É¯„E{šE†²É¢¯uX¨­í°[:|žÞpp0ì xúüΡ k(ä 'KƒÞ/ $^ÜÞæÓчjö(ÑvæÌàêÉq ŒGõŽ)$ä "øÊ/–Ñh˜*ðî«JBœÌ QQ!ŒJ„éŒäPÏÈÉÝ&¨›¥†&™a« Ø,ȶJ5 Å&…t3iHVÝ—Î  ´ëV— ñ9ÊÔôÓûýÖ>¯£Ûçj÷{»Ò‰Í!÷­ví5Fa…VXBžënˆ«îXêlX[rhfíß>Þû¯ßßÿæs_½øü7ßøÁg‹eB h+Ÿúê¾ñuW"¦þ¤[¶!j¹Ó mÖê$TY7™Æ˜¶[,ufK§×ÓíÓïí3ºd¨‘#¹/âdëMnY³Ù ùÿj8w"'Teý|Ò½ùî“e‰ãÅ©¹dh6šŒ†Gâ‘¡Tb /§¯ Ü)‰f£^ w!ÜÛõ­)·kÒ;,¢@.m Šóܦ:ñ*Øć¨4‹¼ž I,²ùÄ5–pOÒ+L¸)¸5»™äbBže‡[/ª+…[dÂ'%b#>ñRÉgdÒùðæv…ìNú>‹~ƒÓ´ÉcÝì·ß©—Þ¡“ðy¯Q±ÞªÞbÁ¦é'dIv²×Ÿ/¸ûP@Iµ0ôÁM'Qh÷©hxí­JÏ•å…FsT8‘‘Tb,'5ž“;‘—"Æ~DwpDdà Ьç3K!0ã p¿¼Úƒ)ëT£º]¿µíƒöAy4£¸8ž4ކô“A˰ÃЦ–w mCƒÎ¸Y­[+S|F"ù„Dò)úûõ=%áµp§]¹!n} e¹+¬½Í+]RoK;Ú ƒ½ù‘th:“Þ_Y4]›)ް&óo:€îXå &I“Eä‰<«Ù®âgZg“WP¢¹[#!B 2ì5ïÛ¤’;•ŒíSÝ£S1-ë^½’߃ιhî)ãØ¸¤üfä¾çGnq`lb |¼04šë¥Üä¦;H7J5žkŸ) ëqÀ+é"Àe€…Ÿ±ÎªÝjÒnÔ¨ÖÉdÄ6ó­¢ûI!).#CVM¢Ôæ5')—y'r#»ŠÓ¬éLœµ«4Å~E|xq”ªûÊE¬‹i“I„x@jr(/í×mäéåkµò;U²ÛåâN³›•Þ&•~`¿ª{õrq¿\v­8«âŽe8±œ‡ùoU<JÉvr«J"ÞÅ¥ûòQÏŠ÷ĽÔ3صbî Ù‹`µÞo¹ßc½Æ¤ÍE»2‘üîÿêÌK/eÐÀÛbÞÄçü?ÿ‹ÕàìÍ_&Jþæ7o]dÚÊûÔjÞE%ðÕ3oþü—ÿtøØÐõ×£Ý^”ÓSgàÑpE¨¿8Г uä„Ú“ÁÎtšFå2¯§-àGVÅ˾ ·Óck6+Eo€4f¸!ò¶¦=¬@N£j8ß>žqçZ²ËQ8eGŽ­À':-A Zghtzd@ë©ùÍ"ÌÙˆíùyáåX!—Ý­Ròj“1¦c½±@{ÔÛ“Žðí8jλô žÇû6•ô­ân#Õ¶°¹%âl!oòzÇ©±¼ÿ¥oìÝre@¸:.ìkþø‹ßÙ{ñ¹¯uªàŽ”´}¡¿³8Üš 4E½­q?L–¶pãtƒínO·ß7ð‘ñ5™Õ@ ëèò˜¥0m¡0 3¨ìŠ/ÍÚ'Ç:o" žã~²À>ô‹ 莋Ý^m¯KßEZ©Dôg«L»A¦½MŠ5™âÍÍjõ§µÊ{ÝÖšt¨&^›´}Ü''Z[e–]‡ÕŸrJoÖ m…ñ‘ŠÌý:eY <‰Y™°BGG’.ð þÆމ¤s$&c°‘D>Ü÷¶ ±Á£kö›klšõzùzåÕà«‘Òú½šOXo°·jºÁ••¦»òÍ9±¦d¤%iI†êC®º€£9ä"`èIx€ï¢½Po“ ù‚`HoX †Y}³@¦Þa'x’»‘f’DoÔÊÔ)8„RÆë4ªû4¢Âð¯©#æéNúñŸ`¢ˆ|:ù+\&0ê[4RÀ´ÛôŠ­:9‹nüYö Ü«?î UJUv8l#¤™H¸GböËè=¼ :WìbW³=áÿv¿©{v¿±Òd_&>_:R¡N4Q®¯ƒD»ÛÑpÝÝË¥û¢ý[ö¾|Èy„ÑÊ1r@0ÕóÐc,©ue°Ô¦j,2º aû­§øÏeQ ×J…«ÍúŒDXŽ~ç‹_zõùçI æ‡ï^®Øüoûѧe] jä󮆊¿ý½3gßúÕ¿¿÷£þj{[Ï’E#‹+ºó£5ecH[ã”0Ä,1! ÷µˆdßnÒd 2bU-‹È­7íXPZxˆtÅ´4¶f«ådF,”«ôÄ”`JÇ0GüŒ(<å— Eõ£ ó@PÛíQõxµÝn=•nŸ£Ãë@½¶ÑçjŠÅ @QAOyA{A¬%/P›pnŠX¶åzK#›r<Ÿq«– Âr©°Z%\©®T «•Âra…T¸† Usn°/?Õäq? P¡¶½Ùë¸Ã¥Y‰o¼9ó­“ÛžûÑäùçöž}vç…§ßyyæÂËSï¼uøÅ—vŸ;}ôý3]|ás;÷À-é 0U³òù¿Ÿ¹øÊ×¾»ÓÖEÆ ¹òû]Ò{ȈE±ËÁ¢ €}øéoRHDû¨D .0æ®Kö1àd~gj¼NìCÙÓuÃá@'D3£‘@Õ{û60¯Á k³:º=¡¡hNW0¹ÝÜèô? nÍË©/+n¨*YŸÇuƒK¹X#”È„jRv{…côÞU·³±ïÆêÛÆOZ¤ßk®]g7oqÙx)wЧ¡Èv! XÖ«´Ï¯¼„â6tE}ËΨ•ºëüÅçîSÍØjPˆ€dÐÔ8;ü¾m1ÿgüÚ+QÛ¸ô—ßzûÕÃïž;pî•ñwÏN¾ÿúä/¾u~×k¯Í^8}ð½WO\|õ »çÞšUÎëâ¡ö«_ûÁ®‹Ï?úÞ¿ÌõݸF/ÜnH¹Abv‰}(_S­UJ°)öiq‰ö!1IYÆòl¨˜b™¾SÖ>Úþ´™¦ÛÜÂØÎRP¸¾™bït‘Œ1‘š]´ƒ©emu(ëÝúZ›~ƒF¹N¡Ü 3l³»7˜·*ÔâØh‰°J.,”ˆÓ±KȹH&,°WÅäw-õwÞ³äÔð=ß:Þüó¯L^|á[vÜsSXU%O˜Zñþ…I}N&þXñÙÎ.Q,žƒêX§“®é´(æHÊ#:„¢(ùξʜ½%‰=%©ýyûÐ7(LŽçDY qÆs“ùᘋ¾¹þÇAÈJ³bR*`  .ïÓHýÖî¤þf_Ò9ìdÅSd£l.ª@ºÏ–ͳ$ ý üÝj˜  z"t“Ø5›Ôˆ&TDN&õ^ƒj¾<ò ZI+A, ‹m}éýr)Æ jÍ  œ$h" °Úíꑈ}"å¡\CŃ5‘aŠz˜‰cû+rö§²ûÍd÷›þ½ýæEÿ`¿"ùÅoRFµA¼/´3Äû¢#¸’QxǹÁºŠš²÷ÅNÕåCσx>pà8ÌŸ´ž„ºÑ$[¯—nÐÊ7é”[´ÊÍjÙF…”Nôä-Ý ÍOÞ‚«ÈG­æ²«™G_Æÿ¯:üM(:Pb`÷ ¦(ŸŸ{û§?ùÖähcEéf )Ú²ˆ ð–5‡Ì¬®Êվ?!†©hŒå†ÇóàÆóÀ­€"ªF—ÙBxŽ)ÙQ©cu <Ûì‘¶ùd,~]!EéCÒ4™rìL{våv¥ý³)ÿÎD`*‹zC^.×=Z¦}©×y­wù,Ÿ´«×¥ ÍB‰AÈS Q™¦Tën[èܲbªë¦/«ùÇ'†~ôí‘ÿÝèϾ?ù³ïÿàë}G†n{põ®kõ§€¤†ÜMé膈óFt¥Oª]ñó|íÅÙÓ/O¼òbß»oLüúõÑ /ô==qæµo¼~à×§O\<÷¥¯NþÅÕ!aµ_8Õÿñ×~¸óâs§.žùüß·!#´ä[šÃTOÛ".bøÞÓÂ#¹ñËöA³û ¤.ÙšO½÷Cí#‡É#Xí~S_LOGgÒ×õwå&›SñÚpxS0P“JÞîµ_kQ]mS®°xÔ ½°Ä%\™TܾÔÓyß’ã£w~ëÑæŸ}{⥟¾ðï¼óÌç.¾ø×Oì«[í–…oÕM}bÍ 0Å,* ˜ê¤ÛŒòf»¦Ïg%¡ˆÒæ³M%œ£QÐ}¼%MóÁ\ÿxA̛ج1+YõfÍ5ô¾è¿Ñ^á“ö~—Õ0¶‡íÌ7i 8ØB'G"Гe$½Iowh“ŽV*v½Å Iy©¼ }‰œðx:4š ÇÄó€·ÙÈ_g×ÖùŒ›ªu& ‹ÚˆOfqžx°uj)`f¼=¸h®A^”j _ó¿<'¯$JVXº|f@5⮣Îñ¤˜Cµ{utÿ)C¢ÇÕôlÄØ¤À,q¿ZùV” Êõ´Å?l¿ª) R#Aþþ} å'òüSù¾‰<•EU›ö5WäÖV¤îÍ÷}2å¨tÊŠ\’QíÍKÃu÷/ß7¾ö[_éýåOæ~þ“™—_>|þü©W_;ðò«»Ï]8xîìþž9÷ʉ—~¾ÿ´î^·b]Bw½L¸îG]!º…Ù®^|jÿÛo?~îÌ«#ï^˜üõ™±7_:óÊøÙ3»Þ8sèׯ¼xî+ê®Xîn)‘|yÏ=¯ýËÜÅW¹øòCŸ>T·D³5"ßlÙ%š…8&¥)” xÖHÛÅÊöÎd퓯?uuá¡Õ9û—%ç‹ gíclñk>Ô>À?øcyÁ)92Œ~­Ýði³æZ­dFX¢J$B\(7 KýÂõ…š ×%û\ú•c[¿ýW­?râÅ_8÷ì±óÏ=÷ì‘óϹxæ _þÂOï¿&GXéŽÔÞòóãwFÌ÷XU›­H s•êt˜Ñõj¡Ã$ï´*[Œ2êf"zt±YÎ;œ -rÍ별ÂXôî©„4Ùu ”ûÓúŒíN5²D°¢MN³‰]° Ì£Ï¢yMZÁ¯l·©anA/‹Cñ‚AI|Ë‚)@î™=È¥Öh@H¶];ÒFb‘´Î.K•¦ÄUœo€ž<¨ˆLŠa° øv›y«Ù°Íb¤sŠ˜Éf½Rl=›ÕЉF9»nÁóØUfÅUö B…4ØdÛÔÂÆlcý÷ö+c¿Ê&»¡'èê»z`Õ95ì·Í¡âᾈˆš¬¤õÖÏp‡”É•C)dLR#yÆÿþ<`Þ—f‡–(?¯F-ß"—P¸ãÊ´› +ÀRÖ¥#×Z4׆½_}êûß}é?ÿ“¨fÞÉ\ö0—¿ø_ŒjHÑÄÉÛ—\͹‹¿9ñý ï¾ùú+Ï=óäß6÷×ôVM”æÁ>Fɳ^KËX ږܰsÞt„ 5&éV‹PJ—ã¥Ö¹Å¦!w¦•­19ò­#…®ÒÎÊZTËiYX˜wpAþ®’8-T„éÀo€ÏlÔHêU *i“Z¶C&`:˜­t¸Œï6+Ö…,·ŒWè…b‰îÅ.|‚ÊÍŸþÆçz~ùOûžû7úD‡ž{fîÌëGÏ]8túìžç_á•‘3çgΟyéÕ‰ÿxqêégvžþÅÜ ßýbó§¶d¬7…OyäUáÚŒò‘=÷¿öâÉ çNà—Μž|çü Ô»g¦^uŠ_¹púðû§¾øÊ_m¿>øé"yÃ'ßy¬þì¯_<÷—ï?{øâéGÖõ)í§’ÛD|ÙÕÐ4ùûìajÃÊHgJÝŠèD\{É>U¡±±›ð[ûä}Ð>°Ÿv8Õ¼à뺻íêëÔ"t°B",%´ÓËC²Û–êî¬o¼úÔÌÝßùbó¿}ü?~>óìì~õÅšÞ:{äí³‡Þ>søÝÓ‡ß{ùðÅ÷¾üì“c7 i™0´vÙ¯ø—=×TB$û  ØDŠ"Jµr¡E'tX å­“n6®K:ÜÖ§¹Ëeû‡ã‘þh°+ìëŽø‘oš(HS¨K‡zVÊ>\[1_pkx­Äó,]SÒ̬«éOQ§G&ˆHi áÅBC&îõz)f‡BhPsœ‡mˆÓîÌ>@T½ê­ò:§uY ¨²ÓÕ"Ŧ@“”ŽØ|æД̅HF f$¢[ƒÖÍâ‹ùÊ eÆMjÙ6­’(Ÿ†ŒIƒ •o•þ#n¸ Ùœ¸_‡µËå.í7èÇòS9Ó9cé”XÂùý›§NÂ[Ì~×fžÎC­YÒà”½ Äe´ú¾EþC+¢Ýä<€ê侨Î? Äûâ76ÒlR‹Ý«z´Q£ØNçW×¼?$ÇÜè¶|ª½1äÜ]ûà¿~óë/<õ«Ó§Oƒ«Á½\ö0—¿øßu5ï>ý†Š QÍëß?{ñ×gÞ½ðÊ+ÿyößöí};·”Òì®uwè$½Nõî\ÏgW•#`r•’ªœ=eœ%ÐÔI˜Èh"í®J‰ž¤ØÕ›£m‰ ­ÈPÇDN"“Yp |R@€Üã¦xû@ŸŠŒ­œRL‹UßjÑÏÆ#“~ï„Ï3 ì G½Þ>—£ÓãñÛ•n)ŒmÉ>éÓ,7 k|ò[+œSõWýdÍ‹ÿ¼çüÓG^ýÕžÓÏìá*]|çÔ…Ó{Ξ}ýÕ™3¯Lž}mú³»Þ9¿ïÍ7÷¾öë}/¾±ûÓû/¾|êâ¿ýÙÞú±ëËnr «½ÂÝ«C_{´ù¥çN>ûìÞW^Þóú+³ož™}ûåÉ_ŸÝuöÕ]çÎ:wú^åâ‹_º._XgµçÔ]xVÌž.¾ýøÅ×Ù¶LsìæJT³ö.D0*o®"¶$ÍÚS.jFaŸÙßÙGÒü¡ö±ÿÖ>°¨7¢&Áe× Àç8ùvu[Ȳɩ¾Û"Üb>¦–é…kcº»—Å[Ö®˜í¾õË5ýøïÆŸýÙÞs/{ëµão¼&V³_>½û̹9â´óçv;=õúË“g_ãù™‹ïtEù‘%ùû«rð¥»Ë“SŨ¥vW擃0äVŠ$}p>¼kv9uŒ™d`<à÷º9³¡à„Ç=â°3maÂ皉ûgI¬òD`5ÀÖ˜šñ¸[ýŠ N)ð vɃ6p4ËjjÊά šcèdTÉÀˆjjÍÆf§˜e»ËÆ–Z œN=_Ÿr«ETÙÐv@V°Ö¥ýÊTšû,•­•ÉÙ2hs‘»ªFH^gÖ°_xg¬^pD¼/™½•y»Js¦ ¬Ë÷e¶"6^äæif20†¶(:W¿_>p@Rhâ<Ôê%V•è ­ÊÝ ÷tÐ:é³Í„=³aÿ”Ç9f³Ù,»‚ž]‰àt~¬'«-ˆß–ôÖ^³â{Ÿ;õôÏúÒK/ÆÛüI(´°ÞÕƒ)Ѽqñý×.þúõ‹ï~ëÜ«/<ýü?~ÑúÊÒ±ÐDQn—ÛÌ‹6±'ìh ›†zÏ‘ F·`9€t€XЙ£ûÿXû¨6Ï{ï@B-$$±÷{cÀ{o;‰ëØIlj÷ÄØ›½‡Ø{cŒ·³›4M›&ímš6i›4iâ ¦&ü~¿W¤éí½÷»ç|ç«ÏSy” ‡÷ù?ÿñ—.ùÛ]ò§åØ7'ºÁØ«>Öµ1Þ½66‘(ZÀÃcÀ‚èD›óNÌbY¥¯ªÊ[Yé&+’ ò„œ"g‚LŸƒÔô(ÇþÇvŸÀî) }½ˆHà+]‰ó»cnÕ¼øÍG%–ï›HcÿÂXûÌHó‚±}ÎØ¢®4ëk&t‹¾~f¢yÊØl«5ÖO˜†,š‰'­†ñÚá¿”¯‘#o~Ttg=‰طÖ壷ó,†ÛúñÞ)sÿ”©Ã¢mÐ=,›54"dMÛ,Æ~ræm„šd. ö¦‹þðö•'Æ»ÚïZÈ™×Æÿ£äD¢cA”<×S 6Ø À¤!¨b‹Ð»[ÚŸÌ÷‡‰ýÉò·»h¿Eðò º^ïY©*ôsÊñääû8]P û¥ZîxÀÀØ*öWúÊKü\.û)ž—Øocϸҧ¸?Ÿø/ž“:@+KÉË”³1 ;/ƒ…è’B&&¾½,g>à»HäÎ8ÚÇ‘#å`AÜáÏ*¥Êî½Ö¨Ïû2Ç[ú¼T^dGCxÈ‘qÒ —<•À`ò²b/4ß΋ xFGbQ‹ç±W€SZÏ æGÐÊ øÞÌ\‡\?fc’eÉJ=îà¨þô< ¦Æã„ÜÏN4÷*üÕp®r•J ¥V.Û.ÛžÈÂû÷C NÖ—§\Ø»|¤¿î¬}øÙ'c¾Ÿ2éç§-”r8IÉ /P¹Æ¿ ô-ý¯ÿŽ_VèÔö&'çM“䬉œ]˜š€qåüŒåñí‡ý*?ÿ ÔùEã’Ô]/,J™Qå9œ× e䯲zºñr‚¹PW+ˆäÅ ó"y—ÃÙ9‘Ü+œËAŽp…¦<ÙáwÌ¡4d0@ ",˃dгmŠò¶^pÞq¢ Q…R/!µ“þhvAÜÛ;Çß=ÃOy1Òç)3I$ˆ‰§SD™«ýúåáoÚgÇ1j™Õ5aÍ葇ÔMk-¦ZóDÖ„ii5 ÈL›ðj¤þª1Ä2ÜHŽ÷’cWG?«ùݽŒôP"û˜ú÷¿Ìš6\5_Õ6êG&MˆQšé‰†)|fÆÔIš®“îîf§x/îpýô×WÌÃó}äÜ«–ßWms"öåˆ å3„]!è”åɸìÏ‚^Sa¯ä?íOv$'+˜ýfд3@I1'ö½u±É ”DH5q.µ ʆDwj\hGrnÙ¢ÜËßýñóùU¶öûFËxû”®sFÛ‰ÝÀšÑµOéÛ¦ô-#>xi¢Ö4Qg˜h0š›-†–ù±æÅ‘&ÒÜýÄÔm~ܼ9ŽX¥ ö©mÿz/Ÿyç·­'vº{D¦?A &œî\¶}¹³à"Ë ŽÑGqܬZRyÁaþÝ<è2]Q€ãÌÇÊÂ-ŒÓƒyÌÁþ0@#ì‰gQÚPøswÞ§ì øøA(Q»6%aU¨Ýòý¤E@˜»"äYí-«öWÔ8×ú:U{:V¸Ñ[Cª!ãiG©Á@GÂÓ¶2¹~œ, PŠ r"ß˨-È(‚’åB`R ºž@0:Q¶ ‚; A-Ž- ,б5´ƒàÕe¥ìáC!…Àü  –+å6È#f³œÏñìq?Bdös2ÞIû”ˆ{˜Ã|f‹Îó!Æ1þa÷9‚Â"ö¢ÒD膘Ì9WV&0cA‚, ¬ÃÅeÑ.°ß*¢"dQ'ë¸Øö„ÄÚƒÏY3 WrVnS$, W„ËÀŽoLôFùm1Pz©þ9`œÞ0q© ×øI*ÿtâá7Sº¡'‹3óäœyÞ-O€xAÃs"Šií«PÂãÿŽh³jž33ä$Bžœš6ég''MzÃß>ÿ¬««}÷î} @s³D‚Ó,‡“L:œžNã¹rfžW@î• œ@tÕò¸…‚ŠYy’";ŒwÂÃö%ñò6µÉç4€k îØ‚ƒ8ÓëUƒ{Ü àÝg»±ÏŠiˆÛø¹ :e©ØWP°Ã ØãŒ¿â€‡Ó2{"‰K¬ñ¶9¾Ã¯G³÷³ß–Œ<ì2j{p¦a–‚ âÌ”¡¡f)Ú àXŒu“††IC“uµàÕ8Z9m¬Ÿ64OŽ5Îê;M}Cí?|^^™›p¯ïß?+0µYtmÆÑÆ ]Ó´¹ƒ§© |…Ê mÅ´±‘Ôõ<ùs×6"-€8ö¼ÿ—çO޵Ì;Hó ñ½âgep–až’2 $rÉ“Ÿç'Èõçær ÂxEá‚ÊYe¢â²u^”û„T\ÚžÊ:W…yUGz㔕„Ê BE•ñÊšd×êDei¤$?=Ž˜U½( ¶Š0]Z+„ògþµzä‡j½¶Ÿ›0¯m²Þ–}ª?üù„©Ëjš æV‹¡íÉH9Ôjø®‰$ßjÎM¹°^òr´Ã;M¯˜þÖ?úûÚ³dû|‰ÃÐ%³S¬Ü™WŽn Ÿ•á`sÖ|[Û0ŸÍ“1±0 _êL‚EY€Æ…<Ÿ@ÅÇ7oà…Hp,æQ¶íQ©ýû®ŽžÐŒEœçàˆKj’|Ê£=J#\+ÀÔŽõ¶bäÌh¨B\  «}yÇjû?û2/›R/[ØÏaÒw4Ùü¹eœB/zž;h›¶@‡B%N‚øG`&©ØaIõjWLó=8€~å¸RÓUŒ™– ;P¦3Aò£Ú`ÓÀ€&[Á„§Ä…âÚ°Vµ1ŠQ…Œ-qVâøŠ£-vm1Åæeð$çØ’ ^†´q˜ÄK¡HyÙŸ›Ì/ B× CU‚*/DxVeLdƒ¼ÙÄŽ:AµIRæ^ rA¸²8TZ,¬§,«cå2ü²„'©:Ã\”œÈs€ÃÇ@ªàO?~©/TÜ9—Üìðت%x‡´Ó~üâÕ‘¯œÿî½×Ý_8;165m²Ì[àä4RX4Qf‰€èóï 5”¹æÂ4ü·'©.ÍåæÚ‰¿|úEWKÃú•TÔªy¿¢Ô1løS83pœr"NÀËÀºÐ”;#£eá§æ+ºàátFÁ=å"8£Uó!”ŠT™BkXSeŠŽÌ3`>A=fP·9Å ´z®¡¼‚|~.'eÌWœ_’sžÓW°ˆP `ƒØe‡ÓqõÌðíO&_[œºckŸ¤®ï&j¨ÖŠõÄáÐÕÍb騣·0Þ´0ÚŒõd¤ºi¸bz¢~ÖÒd¯76Në;æL½Sºîo?/yø÷Êñ ¦‘–‰ñ‹®uÒ€¬ I;††pYW>i¨ž·´’S7?ë|6œ‘Hd ÿöÏ%Sã­s†RßÿðÎ…“~¶½ ûÊ<#²=#²É [„uNCÎ4dòÿe¨-’¡Ta¢€ÍÁÉÂ.A™fÚRÊ3\ŠQ…Û ¼Hzža«ØD02ÃMA¿ž;=Ö¿0݇TÍbÆ þ?‡jCcÿ{¨Aˆ} ‡ZHc7¾sræzwéªÄjÑ‘µiáÁ]rèÎÂmÏx{¡a%§QÃApà téíN‚3KUÊ8(_¥ Ì3:¨˜Iý \ˆ‹NtŠøæ#kó,qsÂà ­Ë / Åøm‘;?Kn ‡)hu‚OúÓóƒ§è¨q ¡UÙ À$@n{ÃV@ø•ô¯H³Ev—Ạ»LQ¬´ÉS•?JŒÒ›rg‘§¼|_êJëϺ`%V!]‘¢WOéò¯Š=HýèØ šYi5PUEß eŠztcÛ ¥¼Þ0 3å„ÕE#ô]¾ƒÚz×iŒô/0à˽åì(u“—º9çH˜”ª'TpÌ`£Ciä8ô²Ò°+¼K^ì ¥ã99+CÎÍóÏ:ó¡ö5{ „óŸÎËÒa¡ž 0ªÜp|0”q r=اƒLŠƒ™%¡C‹ ,H`ìˆïŒÊö¬㘫ýQ7æa¨ï¦E¿Y–;ô›wtŸlº[·é™ Ó”qjnvfnQPáüÑù Ye¼ûo 5H˜f¦Ñ®AA†/9=;„…áã¿ùÅ…Y—Õ^€ƒ^–ÚåJl*à* 1p9Ñ›àÝãmFX¥`HWî/€,8MàäZ‘Ûœ“ÇcÇã\Þqÿ‡›éæ|ÑCvÉË%Û[‘å)ÏP O:sŽˆ™y®ùÁîùnW| Žê&ÎòtÎõ•ëŽðI½]v+p Ú¿\ ø±'RÐpnãç¿(7ÞÇĶJÛ‡ž§q Þ6Ä„¥¼e)ÚàrG1õ_‚ ‰ó5܆€c«ž1×͘ c•c* # ÈaÐ%ž5öLj;gÌc-S†öic‡Yפ«AÎ0ii´è+j¦ÕÌÞ^ø´ãÜZ·eþDîé¸ï>+™m!ͽ¤þêŸÛ]Š`w¦ù´ÆºADzÂO˜^¢i”2õVÙþãþåòŽr©ýÉR9g»ËÝÏAQ9M¥øŒŒ lvž¿´8Ä¥$TAqAüñ’^ñ–ŸõqY# …:åÇÖÜÿZª'3Ý«ÕjP*"Ôàãÿ£ü—”ÆšØ4&¨¬f)Ô<ny¢ïD¤%ç®ÿáõcÖ:=íC!ŸÇ>í ÝÒ}X}:ÙéB”°"Zu55¼)À½',°7"´3,¨]ÐÜ—GžÌ#|úãÃÚÃýjýTåî êø­Ì]Ð$Ïu¶CΓ%$re6Ùb ð†HPõDK[Bxµ~¬JøS³`'˜"„#*B]ª"Üq›_ñ“ŸwÁ”7EÀ‘GUb:aš‹N…‚Uà‚a4ò%ŠG t娰E<–4B¯ørP’”…;—ª¥xS hNöî_¥¾¹1vp]LÿŠH¬ÞôˆîdJÁ¾=λ-Ú»#Þª79¬;>]\ 1õh‰ðÁ¤/9´')¤#Ö·-ÂçjLDWhH½šŠ —ºC$V@fM°&€B?ÿŠ3 Q|ðgZo´²CíÜ,¨ඬ:€E9h¨$Ìvw€”Í 1yö£|»£|Do¨çÁM¾ô<kõY™#@›…2Ìô¡ó™ç‹Fœc;p=|”ª‰ðø1ü/KiPOE ‘ W<¥¬@I?áÊ<¤b>ë*Ìß²ràâ‰_uÔ=øýû“ÃßNŒ?…@ßäü"³Öfqžò3 ‚ÿ5ÔóP‘XÀW¦8ÝðÜ1ñé··û®z漯¥†8É& Á¶æW$D)tS•ø8ß6[BºÒªý¹Vm‹t/ó’•)àf‹¥¹r×<¹×e÷—œ—%l] ™/pìö;Ú<϶}‘G§èÏl Ãþ&dQk+m²G¾ý!ì,]<…Ï*˜+¹Är)±=ŒÝ–³ëoåO ]ŸÖ궆šftm³@›~Z8KÎŒ®kV×27Öj]mó£X3ã­T…5Qc6VëÇ+t£è÷bºdkD´1k[McÍfm вiC«EÛ„f2:¬-x*ty0QyyÅŸ–L7“³×IýÀ;W6åD°‹ýèE®ÔÎà‘Ãk±›­Æ[$ip¯ð’^ÿ—ýq=Âu<æä3Ž£Gè,!uR%‘ äB¥yZˆû@Ü£[mA_²Y¦¢õ–>÷èóÖчM&cÛ0?T©øS¨YJi¬}*ªzZ 5&Ó¡†Jð†›¦†Ñ°j!É;Ú/«ó·+Ž'²«¥Þÿ@³øà:9öÚµ³k2bPñ¹Ã"ájBhThk_s°_K˜_[T@obèµ´ˆð›H ëŒ èìŠ ¾š ¾žÑÜìRîÁÕø9!°€Å†çO •~8%*¢ÎÐȉRQèDˆ‰2%˜Ó*ÎS9 ‚f2fиÁ—XœèµÛsmK-=WÂ+÷7…xuFö$„ÀL¹6 "ð1ÔòöXwøDhbÝ*á8“èݔԵFÕâ µXtBêã½!cر,¨1Ƨ>Ê <¨èáÌ·º@@éšBÝ›#¼ZÔ^uÁnM¡ažxxðÕ¤ÐÁôÈë©Éê.uh—:¬'B}-.êfrT_\õy=9Õ¾ÈøC@à‚ ~ú­ðºU¥J¢TNäv‹ãƒïϪ´DËQé€c‹IýE9ÈFø e«œó˜Ð¡ÅhïŸ‰Š BÀ‰ìG­ µèÚAèŸ#7Ãû“€ríNƒÊ·Íqv„7_s¨²+Ú£/Þ§3Þ hsàÏKƒ$u žË*’ü.Eù\IQ¬_Övdÿ¯[«¿üŽ¡Ï?™ÖO[ ‹ H9$ò p½TÓÆª9þÿݦ€‚h¡(ƒŒ:¥É™YŽ™üó¾¾Úüú‰½×w¥ÜÞÖ—¨h cwFr[Ã[ÃYMá̺z¥/sÊ=ˆJ_»º n³Zóte€Í‘ɱ/VÊëÚÕ‘mÑ1š8u~TÀy_Åag.xG$°Ë£+c‚«bCªãB±ðfé}M‚º*:ÿMzôå„À*‡'âg â²Ó+ÿöÛšáûÝ5³®Μ¡mÑ„ŽÚT˯Kk)½¡òd;ÿXf}Û?V³ÙXcÒ—u&}%’©É&³‰‹£_jÖ7#“™Ð6šÇgÐV2R½šÙ™ÖI#šÃµ@׋÷´¿®ÒHXIk¯Ü>ôyåôã&rá9ÖÛs0ª(šÛ³EGM€m‰µE5Œf¹‹$úÐF#9òhèý7ß-<_¿)áç/®ûéÄ»ký¯§»õ%ɯ¥ª:ãEWÓ$}©üî$^[,»Q =»J›r_›\9u…-ý°@èÎä” ‰3#ßO¢ ³2#±Õ±U1þ-)ቡuqAÕQ~vBÇ Û[‰³Ji†·ò°‡x“‘Ì#ž‰¶îþóo+Úz}¿ÞÐ…{|ÚÔ‚‰íœ¶Æ2ZްõÖþOÃ&cŽ•ÉÔ¢ŸÀj7·Y€¥¡Ö¸¥É`Ô˜´Tœ±˜4T‹Å‘¹€ '¦f*AÒ7Oꨅ¼Ñ½üƒ ]åÂtÓŒiÀ_ÜÈÌÜà¹&œhüÙø—535ó·ÈÇ]-϶­õè_%\)îM4G1ëÕôzµCM bÎÿ¸?ÀCÂFÂì¹RÌ_*!g‹S†;×»;9´;1 È;V‡•¨ƒ3½=ö)À›#2ŸÿøÂiÃ-³¥wÌPg™ÅÇüg¨ù)¥1˜Ñ þg¨Azf- –&P ÆGU‘ä-Òxí)_bƒŒxyµü/ïM}ßK,~ÑQ÷¬úTó äqd J¬¥Z4"4ùÏ ( 41^¶*¡ãy´>ÐDÍ”Ò*Em ð÷¼±.ðõí7Öùõ¤+»Óä]©²Îx§ɲ› ÒÁXIo„°-„ßÀÑø°+¼Øy.öÈ.®¯ˆ¾½.±39¢Äß-SF5T³Å (p½¢ÌWYâ„›[–‡2ÃU%‡—LXRC g‡‹R[Œ‡ð=ü    JÌœbZ¡-ƒ®—U“š~²ˆŠ“Öb¢:Ä^íµÁŽõ¡`•Úç) ,x”d¸Û¿"¥œvÓ¨B z¿@˜,ñÁñ LÂüà ‹{n>ÕÝ˲Ý'dm`16ÄZ/¢âìê?~P:2Ü?¤íÖ·ë-í“­m¸df´Œ4ס´™˜ÀÒ àX¯oÜàMÔ\{¢ÕhnE¨Ñý#Ô Oµ Ô 5àm 5TŒ2htÚJ½®p“«H¿KÛ¼¹¥FT†áZ³¡qj²Å0V1‡üÇÐHN\½‘³y…œH ø1ÔL?B¡¦·cOèëûëãØŸŽe¢æx~c §!šýÉuûŸ÷§Ø›~É8&!žÅÑÀþX¹‡("0^êUâ}Èv‚ ™‹`&¡ÉÚüçÊ,Æk¦É®‡¨-´ÿ3ÔXÃ#R;TO5XØk˜Úk¨išk\~¯Û¬k˜o&§ï¼kû\°Í±µª?¾™où¾‹4Ü ôý­ïäË>y³PÑPt]¤Œ\@ú\îݘhM` Ⱥ Ð >#³C[(\Ìú_âPbžEÁüÎ>íé^-©®=k|ûÖùu¦¹µÆ:wÅ)zT½‰®±Ê&5Õôƒ[ÓeEÉ\Ê70qÆgè?£ ’&´q^áÑPwïãÚ>Í¡ïâØ=#`¼ å¼ ö“3óŒÜᢂjM`„ ùÐ …DÃ! ©Ø|^©Ÿr»¨nŽÒ©ÀRâJµz.‹áHÃa¿â 1ܲråtHe"‡ŽÎ)ÔG•Œ¥U¨b"Q€ L %vTcö„ìôIìˆ$¥ÁÂÎUþ­+½ë—»¶nðißì_·Ö£0ɹ<Õµ"Å­$†’D†>%‚^èÛˆªý„5âܽ7„î­Æ¬Þ DÀQÂìSÅZBSã¿‚“‚ó‚7(š0¨Kâ8´Sjsó!çˆEÆàôŒÐÝàÙÀßN‹¹Ø1ò<ÅN¢ÓîÎÏõ‘Wr^Tðöº‰wù©vÇ„üêvß·Ÿÿafƈ~ æDV38TSÖÜæÿ1¯ATùï¿àA›fzHáy3J§ß~õó׳¶¬Ü&cmfO¡0„) jUôK¬‚qe¤ëLjãõ Šæ$UÛ2ׯYU8·ÀVÅ© g”5j»º»ÚpÛÆ8N[ºJ‡i+ÇÛ.ÇRE´K2ªCž…nðP,RÓmô¯ ÄwOìu¢ow²#ˆ$Øž^ÿ§_–~óeã°®gt²Ëš–4è&jÑi™ÖW.èªæõC²µ†"’lýáÛlòI·EWƒ ‚œìœÖâ]l˜œXœî1ŒÕÔÌOuëVÌé›ÉI4~1ÎÖLšêQF!àX&áà·T!¦mÀ| ¡kf¢u¢“~¸lÎÒNš~Õp¤¡ƒ›U?¿~|v¨›Ôv’Æ>räêÅXVÓ Ï˾̟ööŽí©nM‰òêÞÒþh¬û£ùÇþ4Å1;Ó¥5ñÜÂP€i}i=m2•”ˆl:‹T À ”·FHb{„ßÞÄ­ö—‡ïwèµÝ¤£Æƒ¥Þ`¬XÑ4Z‹o¥%›isó˜¾Ro¨^jSÁÇØ¤7S¯ ÚrœÒ-Nw̘;HËÍÜ-ŠçCh›ˆ_Ë™雸¾‘´ÜL(µìt€íø–úñ aªdÂÝltÌ(ÇWZ‘?+ÇsÖ~JI;çn7G¤Ä 9ÄÓàYƒçyZ†ó‚7;'HX¥€MUQ¸Kuœg RúDßÖe0˨WÁ+lî+r&>2Š—³óÁSÍ(.5gyʼnØÃ¢.âMlé.Ek¸D $AܸijÒÙSòbgØ©0#„h.Ý”y;å¸0P"a<}IH”»±Ì©õT{p«Ý9•nl˜$¢‰Zéï kü…0PÂQ©§cŠ^ìÎЪüxy Û<:Èk—|Y ¹WgÝ`wLH)l£Ã†NÚ`¢œiÏK‰ƒ®öç"¥'Â…§ãd;܈Õ‚í.Ä^•í!wæ•0—ú¤À*µk‘'¯>L^"¬ös¬a5¨™uaöM1ŽÝiα|˜Öøyž (´åsbâ¢Ü$et´Ž@WýpÌâA<ÒÛj“‡Œ ÐðÑj@!ÌæÙ‚/#а™Löà<ŸÈ‘ÙS#ßæ¸Ð.ÓÇ嘿r•€ž øéCúí»FãÐÌ"ÆÐӿɉ«§E*lü?›ÿgð'5æÅIýŒŽ,3¦Ñé‡ßüõÞµKÓžRqùÙ @«Jä’~\¾ˆ|EH?)gŸV²3ÝyWü…yülÆ9%QΩã×Ç0[“YíËØ©ÌkëÄ领xn•š^D«´ÕÐ5¾ô OÛ"Œn*|˜Å^ŒBoGØâ”‡ óÕÒç”(‡£t:´Âý¾Ó¿n똞lžjŸ¬Ó[4K¡fn¼bV[ñäI3Iv}ýõy¼~ÿE69ÝKÎ’úrò6Zµäìv‘3wHË rˆÂ“ ÷f‡šµ_—™?kh6i@¥œ´4˜ÐÃ1Ö çAÊdÖÕ!ÚL7!^Qé¡iÑÜëÂèN º¶"¤1LÚ-Lpí 5… Á/8çïôœ‚¶QH$ñ(öýÊ@bS ëÒó±]—w}}»düUMyºß•`1 ³Àí*€%æÁœj$”x §Úu¾Î(%€Æ¹ÀÁØ‚¸Я€ÈÙ ø zº$·½Œ¥°ËSÑ <ˆ38Ñ€ë\r!àVÙ—æñúæpôaz’ÝÀ—%‰ŠRDe©²êt×ÊWܹA²Ü`·sÞògO‰|‚ƒO"±Æ*ì6ÄËö¬÷Ë{eý{-Ùäg?ÎÛ ÔÏ‚¨.³kˆ ¤º]_îr{­äÖZÞÍuŽoîtº¾žÝ»’Ùžlל`ÛšÀhMd[›Ìœ¶xag²¬;σ¸%FØ‘äÜ“âÒ•"/µ*Ëû0‹<˜å^\@… e tirDάR%¿ÚS¢ñ‘iàã.@´)vqÄA£ÄË6çýÝž’ v{ýúF—öÑWFóØÄ¬ bð¼E¯ff çßjfÈ9ã‚qjJ¿h±üíO¿i¬lxfsý–4ÍŠˆ²xï¢UQ„kn˜5Br‘¾(‘wØq¶ŒM„-ÂÎf«Ež"´àÎ)i¥jn]<·k…¨g¯3Ñ·Æñöáõuܫ˙½)ý‰Œ¾8F_4£+’Ùɺ‘ær5Y|M”°5ݽ.E‘Ê]!"ÖúÑ _^ö×÷Ê,£×MºÞQ]ÓV32Q=j®Ò™«M5ˆººÉÑ:ó(zM3S]$y{n¤ÝüE-©½ûûÆ—Ó¥—â…!ÌCèvBôÏÛæ‚ÍYwâ\]ïñÔ¿ÞÈ ¿½JŽÝ GºI]*Ýp%º4 ,`¡†¢2-©‰y9Ñ»hìš× ذ8ÙKν~~“+ˆ ™Ï…þQùÌãžùáNÒ|Óð»òÓ¡ŒÃRÛ]t‡tîOûóÓh|Fõ/ûÓ•Æè_íxg opƒcçjFó zãrFc³%Ñã€ýy5]Õé%áòDÕùXç]>ô$Q—³ö›¿T¦=aê0£ûd¨F“Šª’ÆêÆÛHãÕ/saô«2$6#C•hh#Îü#Ô {C‘æõšàgˆðLYÚÈÉ›Ö?·ÏŸÆGÁñ”û+]œí–˜|ÜNjïÔîA”“\c]öu€¾Yu¸¨1F XHi€C%ÒþQýrUÍ UUš¢,Ù¹0VZãŒûB“æ_¿"¬,Á/?Â;?Â7WíƒççˆÜé1w“£íZ¨^Љt‚XNP5ÑFë#´×–hJðèIñÖpzãd7—¹öD9u'Èë—yg„Ia…³Å•xi¥,û¥Èîš=oÝ8ù÷5º/{HÝûä· R=Š0Άѻ0GêP¢à@È c_€|°Ð)­ñ•ÃxBP*–ú½¨#x†FYž+€a`ø$FaXÀ+ógS²à5ø3jÕ‚ÆÀÆ`éŨ æ4$HjWÈ«VʪÒ\Ê“ä¥QòÒeUŒo}rxuJdNl蹘À­J^šÔþ™X¯s?[ÙRrøÎµ¬Ï~ß:üåMrü#räãp‚HÃe xÊü½ÍöÇtÖÀJzßrb`-íÎVöõõŽ}+í»Ré)ŒŽdVk<«9–Õ‘(ìJw$Š›¢9Qˆ?€=;áµ)NÐë¯p˜ƒWòê‚$5~"ÛrW~­¥û53àoë•RæÊ˜ð¯Çî¢ÀU¡BˆÂm²·úª>¼Ý;öèËqÓ°nÚð0à|5 ÓÁXçGÿ/›þÛ¯¥Ê<7aÖ<}¤ÿÞÌÉlܾ¦uSJÓò0TÓ•1®UñžeÑЃu9,<'àn·£o±±ÛDØà Á Ž ƒCH •s¸õ‚™-‰ü»[\ßÜívw‹øîV§››9ƒë˜+úRéýIv½ñö=1ö]Qu6í±ü¦§Â@ûüPvu²"?Zü‚—M J§Í¾ï f˜\Ò ˜ô'¡í0nªÔNTLU(s̆z̉vk7f?u‹Ô^'?xµôéí€ý¬õ;ät1L–«Fg@zÞƒwÞsRnwJiwI->ét*MÞŸ¹ÁðÛRw{îa›é‡Z¤+Ó¦¦© ,ðžê).Pc€ºu-jÛ§i¦‡jÉ©~rúµ3k/%ˆ®J¸ÿÇó]3ülðØ+Þ6ûyHÿh[ »Í Afiv@ìÚªwVI”„0›¬ûóÖ.·W7‹_ÝâtgëúzF÷‡–•5ö ©ö51Œ–@Û«QüÆ@Ço»ÂX¾íuI«n¶=7ôm½i¼M¯oD¨Ñé«0ÎÆí«<áâxĉ áT‹ãhv¡3iCå0Ûíˆ-6ÔB¾ŸC;$´?¥`fyó ùèö¤ËnnpùÕ~ß^ð}÷)ç·v omaÝÜìxcëÆZÖÍ•¬Ë97Òø×Ó„¨O»—É¥Å|¸0%»œ ãïT)®Deöúi4Ž´ˆ„n­u“© ËhÄ…ŽPC!î¦ ½sº«¤é9~üîÖZ5±¢Ê Û…6ÏÉÙOslŸfÙmƒD?$¼\x¯¸ ¹ñŽIŸq¡¥2¨ÙnåóÑ÷ßÊ%‡ÑiérO×€FåK?Ò¦&u€ü™¾«!õP¤é"g¯¢@›}ÔõB, hŸ³{Ãþþq-B i¼NNÝ}£rç^9±‹N-¨ˆãÚ‚uöç%¨¦«³`ÐcÝŸ®åÿÜŸ÷v;¿³Cøú&m6qû6pºÖq»×ðúW8az#X7cÅ=‘’ªP^iŠë‹!œe2bÿZÕo^Ëе Š)<ê#½® Ó´yCÓ“ñvä'¤n°òlÄÏûŸi6SeEР ¨:D$jümÖÌë«ÐQ_˜¡ÚSOpZ¯?ùcÛ^ÛåJâܾÈ?üîÒÌ|7X] £mäã¾Éß”½Ld†2Î{ƒHÍwÀ¸É€ŽŠ‹Ãìé8j:ÔVD¦»Íy7ÖY… ž™ÃNÄ!H`¡-, Öþ%H¡.¹· §Ae؃¾–ÀöÄ£”¬+>Ð {Vøw$Ãä‚QÁnŽáÔ†ÚV…³ó£O…KÖJˆügCþöAÞ£oku¦¶1c³y²{YÜHÿW×ÏgD Ðg.t_à: Ú€Å%fÃbZUçA„ä;Bæì”vX0 xpl è0©<"dÐ(q!ëTD Í`@ј+´Sø¼Ð¨AqçL W=É£ðj—'Uø°D¦+í¢Âî’ V5§$‡¶ûE´ÍœÙ6]õ÷ŠtßÝ{4øøqËÌÔµÇ_jHýÝ'é<Í>æewÉ—YÀnwH“ßÝ ÀyùÍA¯·~&zíiî­Žw69ÞÞÀ¾±–3¸Š{m…p ]<.k‹áa5EppêÃx ‚–8Ç’¢.Á¡=MЖìÔ#¨ wª ×J(P"Ìeª™žj6êªÌÚj°½ÈñrêöÂwmÏ.·©»«ý¦zb¬ ÑÒjjgBÿ%ÔT$02\2=Ù9ù mñ“¶thGlOqúÝG&¦A8m¸¡fîãºÃ¡´sÁög€è/cê-"ò¼é塜<ÚY9|¨‡D ’qØ*ŠÜh+žLqŠÁÄ‘ÃYŒh!‹SyLh8ÍÁüºÀ_ ¾-4Ó(3ÓXyU8¿2”ÙÃéLw§‹›S¥e)žÝÖÊˆŠ—£‡¿€ kƒq¶];Ý0Kö<™é%o•nßïB±Š(=p°´€ø• ˜L‡1GâÉ,ÜH5‘ãÒÜfG]{—¨g`ŸÉê(öÝ)1‡‹®Ä1|^hKˆ\zq(¯§dpt"c#¦FBðH®ÀàyŽ möñ‰M\" „÷mÞ_üªÄÄ¡ôU Ö`Pw†gìmžaаvЩ´jL© Ú' CÒð´ÔþðB>‚â`Ie¤¬&VV— ë]ë۳ƫ{µ7à@·¶„Þ݉Ï;¸9¤k¹wßjÿ•×Òƒn¯T8öîÊèžÄàŽe!¥qžÑ’}AvÙ{?~3S÷¨k~æÚÂBëèpÎS9Ö4ýQnn;/šQ“ nŒ—Ü\ç÷Ú†€Ûë=ïíô¾»ËãöNÅ{Ü0´ÅÈ o•¨c™Sc,Wú0”V†·}ž; £¨_t8›c…ˆÉ‘´ÒH"?˜¸ìM\ñ$ò|ì@`G£ÖÒàŠ‚ËTøYã pGj ‘Kƒ)0Â/¬[RìˆM~žï tÿ‹aý°nvYyf~)ÍÿžÕÌxz&~¦9‹e¥†#æP sàPYß>™2¹°0ay@êF¦>ÿô÷Íõ÷wl^Ó½<¾#&ðæžH÷Þ”~eCð‚?©° DRÂ- cWDóRd-éÊÖ媮5ž›ÞÚgwxÿï®Õn+U-iòºDQy” G-ÊðãÆÃ & 5íìT:qVäpÅMzZ):æí|0Ä%щ{Ö¹~üËüG_5ÌjûgÆ:ÐøÅu•7B¸¸8hðBö-ÃUp>Äåù`®ñÜ{WÍ<îšk·´P£ÿ ֨т—a€¨ *ÁWÿXûÂ0âèfÑϯ¿8ömí¢¥¢WX™ï˜w[¹ fjºÆ²™™ú¹ÉfÒØCj_=·Ás¯Z´KÍ~çÚ!‹®cÆÐij¤*GóÍšþ7Ž& ¹-vpUXg¢7ì¡‘´¤¹á<ö¦zuÇ»·D)Z£•ñîíñn‘òê`´\Zã][aó'+öçCñPÅÁ"Š‚ånÕ´, lM êZ:°1òÕ=©$9$[ c¶¿}–¯ÝEÛ£râgÛåö„¨ÌLý®qøAÊF­±úÑâä ïo¹dWĆƒp/WÜ™g¥6¨×ž‡5}Û—E(c_àÓ¸p2|œ/Ê3}ÅÄùèè&zAœ¡.Þµ>Á­ ¨åøD7·ÜÝtogð½]·¶ú÷­qkMvnJ’t¦©ú×ø¶¥y6&©¨ëÒ«êqk UÕøË:’CråyÉÞ¤DÎÞo>*3öš':ͳ€3•O™k”˜ýmNÍFñàÞàþ¯ïK¸½#bpu`ÏrŸ¦•^µËÝkâ+"D%aI柡ģPm¬f© ö#m;åLY±\öbåøpoCö•‰V¹ø ·Y¨‘€þÒ¥Ö… ±àú@D•ÓòÜ0ÝÀQ!Ý8ØœŸ±ˆ­lZËnƒû{×zÇÜ7Mè¦f&çÁ»¦À4V™s*7ùŸ»ÂÔÃ0|à9Ä™9« Õ£~QpcdDø€4ó‹Oæô3CßLõ§Þ¾ýnÑ…Žg6V$†ÁPaŠçD:H$ƒŽó)I‹Œãðr•ÌVÌ® ”‡ñJBùej§+~ލÓOH©"ý @\,Ê)Ã_à+?Op“ÕT~‹@Ÿb+°žhÜ‘y@.Ú¢p ã+"Xeù[þþ©F÷°u^×;;Ú25¬™Õ#ÔTéM%æ…݌懑¢o¾É™ÔÀ$åϧ۟N?¤\Î"–aDž Î<˜Ø©ÙÿÞ«Yîû{ûÌ£«Óº§u[†»†¾­»ÿû¢_7h}1Ìå ¼¢¯¤8Yåüýû•ónSï‘–·ÉG×Þ«}úHq:ŠÐ,¡·_æË„¿dQ”ør¢ô…0FŠ’ðeWÖ}÷EÛÜøk '’î Àx‹†9×q!q•ŽÖb•ZPÂÅtSó}áÚƒj‚’•Êñc<L\¸Tog©‡¼s‰y ]\lð¢[ÖɈP vzòAÑü£Nì 4KN׎š5ã5 vu‘=þf ~›þjÅÏ’ìþð‹Ì¯ÿXɬ%æ»5àPüw*Ú˜4ÀWë %ˆ6Æñ*´¾!i^°?ò@´ó3aœ»-ûõ›ÁsŸ…&ªá«'{§äB§z¥+ôRàö ¨p&Ê%qθäm[}*&V©7»ÂL|CrÜèÐU¦Ê%!‰×£|Jl䌜íz#ÇÅ4ˆ†IËÔ ˆó@¥,\žÈ?,¦n¢#j•§•6§\»J7‹bÄDsñ6Ýã¶ñ!Íìd³Ñ ™šn~2ÕC>ì¼u&¹>ÕùöÆà«+©|£-Í·1Ù ­›ÊX¯ªxÿ²8LÁ²BÜÏù¹œpÉ Td9_gŠsC¥Eá²Êáà'×ÄÈkãä5qM¬PÍkK•7& +ÂY9^ª¥S—`‰èCÏAÇÇ!ßÇq?»Ö[á_6´µ†ß/·‹·%²ŸþúÃRÓHÉØ>f¨Ö•‹NZz´¯Ÿ)^ƪ]%+Ie02Ýèp#BùIÁ€ @¢…çÇd;:~pâÇÒÁg í/œ/TvÈu3¶ÜèPC‚î%šhS %ü²1œÅJ…°@j‰Q"*bu&(…êIõ*òç¢ÂŠt«J¸œ¾ÃS¾ÆKE…šï¿1Æ¡Œ7=?‡a7âË’QÔÿ-ÔPI‹µ¼Z 5 äŒ5±¦'¤D)¸1åÅ2?³8gžŸ3= ß=þõíW NUï^‘Ÿ”Çv~¶ÌŒ3Ø/^€o2ðÌKU6šc®vØà0á5!˜#€óá,$*¨þFp|P C,~‹êû<çKâSReA’Ú0·ºïš°€ºÄÈÂäÐÝŒ8ÔÅ»ý~ùFÖøÃîI]Agr¤É2‚lŸÊjôúÓLµ~ªfT_5ü¸Š$Nn~Øy(sêd”ó61±ZJœØèwµú¹ß½Sˆ`½n»6g¸¹ ´ u@(×ÌD¿y¼{j¸÷ó÷¯ÜÖìÞJ$‹)Ia58½B¶øÇÞÁó›wz;Ä/âYPûá)ãNƒÙÊù`á™0á+aü]þôeîDˆŠðr'4{¾ü¶sÒòizíÉ·}{C xNxáê!àÖßâaŽáLƒÑ•ç0§³ÂË©‹ ßjŒg ýögñáÚRC¢AZ¥VÂÿ·Rí[—™Ÿ²Ee›æFäLûæÍÂãŽéñ¦QKýR¨Ñšjtæ‹©›4Þ!‡^ëØ=ðrjºq¯sß—âSÿ#ÔP¢|ÿ5(£ª,SÕ““ÐÓh$§ÈÙw[Ï­ÝæeË!ÚŠ7>üªú`ÓÈ”jt]ù{•ûˆí,â´5(é'ÊD’#Ô¼€€Ùb‘»ØóbN¡' 6yp2:'¥zhÔàƒ#Èñ4ª™L‡ŒlWÜ8ÙnŽ2[4°°9™.ôŠPFÞm©~‰ž½lXtIíû¬—hsóFËóøñéGëjÐОžn[D‘;>píHÜÕ­××€AhN€è’¯(ÓK|ÎKrÒSòŠR¸_ÄÞŶVÄ—a'Ûv>,qhûl^ؼħâÓ 1…6ø°S„N²0h0TE7ìòÐÿ}´_dÔsÿ|?‡"_ûbw» ì†L!e#²W""™I”½œøðOµæñƒ¾]«o†ƒ~ ¤¶ïÓÚ½g|ˆ®Äó!‚õ!i[ÐM‚å.Òl/æ0cÅcU~8¢„7Ïb]ž»&JÖ–âٱ̫5ɵ)^Ñë\+«qƪ“7Å«°š±oQ x‡u%y¿¾)ú»’ßÚ5˜æ Hk¤ ÕAea.—|%'<1%$²ìRUr„šÑïîôÚÙyX=™)$ìÿÂgmÏX“*«±†JðÆš Q¢~0_y2eY0ë'ǘøËÐGw_+:–±<èYwÆs.ÄQ%ÕZÏ÷ Wø2¬òÌ2?û|Úe*¤£š¾¢´I |XÊÒÔñG5r(fdŠm G1E*ùW°áš’¼+yB<™@V?ùeë‘Õ¢õJ" w‘ÿŒúÝÎãßþ©qèûîá¡>Œuc=Ÿ@Ó4µA‰1ah×i›æî¡G –‘ÎšÌØñ³@âd÷x8ëB¢,zõˆùi~Å)>9‘*hQ”Ø®w$Ð2w ‚lO[ÂWDÄGk7ˆî¾sñ»‘¾©é×IË«S_6¿’D;KËð'ЀB4zNÜxü ”‹<”dõÔýnu®‡üH&‘<$¹>¸†äU12M¢ ScÀ‰‹‚€öG!PîºÃ¥!5² )h£3ñTïfÃKÆïû†Ú¦ÇF-µ¦€5@c°4à ÔßšûSë­c++·E‹‰Üc‘Ã_6Xƺ§t@;S²ÖPCI€¢?ŒPcœ(×jKô£@\·a8r§lÏF7"ÔŽ(ËHúú³bÝPã´&Á&úo­:•b9ŽW! ?ñjµsqç¼qLN¥^hŒ€T‚Ûé.Æ4•6Ùît\Å<ÈRAjœ—=ì!WUÀE,‚ '§Ýa7ð!(Á…M4Ũn­¸±>âÕmñ××…w¢Ï³62/6p¿Ÿø¹4Å»×OQ EÚº™ÉFÐóÆz {`y­v·&ÉèýL4µ¬,ax%,õ—šÀ@ï¬'ˆuÖ…7ÛÀÄ1ñt+þw8Ý(FN¹çÅDªPÊ+ã—† ±—„:Aßqó|Y4` ŽF÷98­‹) 4úòýåG•Ž{v†µemù¼Ù¢ëÓëÚPCMc|i¾ C_^ÞpÞ—@Uˆ8yÿÓˆ]ÐÚ‚?‘ŒvÆ™ãÉ+òæW„Iª£e þ ˪P;aŸñ<8ÖFKomzm[ø½M¡7Öø_]îÕ›î,åõµþ«|ðÿ 5NÞ)­ vÄ«ú“½¯¥úõ%yt]¥,LÙhÄó`QÙRúZH™6ш4X§Ñˆ5¾ïß¼6òý}vlna~©Ó‚Xkm@ÿ׬ÔIkiõŸ²«¶þJ‰`ø4KNšI“v⇯F?ùå§Ý%·Îì*Ù’¿Ìçâ'tÊC™AšP@˜kÔŽ T„0*՜ڧj¸Z{Ú¨l =m˼J¼€¨ÄBàE ’ 6fSŽÊ¾Ô—_,‚Ç="mwºgÿjŸþÕ^«¼Wù¡xsuäíÍÉ-k"^ñcmÂäô)Ïîž0 w˜ô]&=fÜ-˜ÕÜ]_òöD2‹@Orµ›ýÖPÁu>EçÖݺzêw¿­øêÛvþÚפñæÔ'•¹«¹åi¬¢p[l]‘7HŒ|/Û?{¬¢@fY(•&æçUÄIˆ‚BüÇÝ lÅaN•QNõI⎕òžuÊþ5Ê««”ƒ«½o® ¾·)þꪘkÛ–ç%ù¯ÃL'Uùþ@¦þë®ùáöi]ý˜¥æñTÕØD¥Î\c²´Ì™ûHý½ÁK‡8ì`À…8÷œ¿ö›v47¬¡Æ*ˆAe5”® ¦lhOÍÔéõeº‘ò©Q`Œoÿð‹òsëü$K³_VýYÉÄÌ=1(Gb3ð×»Gò·Ê:žëÛvu½z`uXÿÊàÖTLóØè{…0—Va0}ª²p^U´°4„]Æ­ŠT†óËB9øm¥š§‰äkÂ8žvD¡7­Ä¬":;«C¸í Š×·F\_í3U%ƪ wjHò¼ê¾ÓÕñÈŸ߸`ïD¨ˆ"-Gî#õ°;ïIÅ« ås±ÃÁ¿"±»,±Çü%GÁ¿â s)¤`  ’ø½0¾ˆG¦’º dõÚdÊ9IRrúà"ï£Ùãé̤-R«>zûÞG÷M&ÓSô3øß¬B èå?RøPgæªÐïX ÙØ<'›!ü;Yj`:1i³ö?yüé‡_t5\8¼¶v“_Q”ªÜòHYa(?ׇ–o ùPŒ~´ª D”UAœŠN¹?¯x³-¡âÆy‚öHÙå%ÊKÑŠŽEä¯Ò 5Û€³+øÒÕ"ÔØ;/¼±Þÿõõ¾¸\—–¸4GHkƒÄH½ÉòàÀÖ~¯”L2ZïI´ç¯íþ¡âÙÄ•¡†AS½y1"¤ u Ò‰oÃ5Ðâ‰Åj~ãÀRáá( †&é»té84z7à¥á¡‹ãO»,3Wž 5ö7‚}a2“¸ƒƒÕ°Á2™«úÇ«G¦ê»ïXû['>.4nv݇òèÃ:¬$ލ1¾ÒÍNv Y†Y‹øÄZäY8_ë_rò¥.ã‰oæüò¯¦¾ûW‡úoŒ ߆h‚&ô]5¿—VºÒ¾.Šoô§@Ó]ÆÇjY,íX®¸°Ì©5Ú©y‰Se¸°8ˆ“¥§&yUfú0‹ç‰*#®o6ÜØ¬½¾Ùóê:—öeM àbáv+ЉRÝðߨ ÕôÈ[}ý°êùÀmx|MV¡ÂôŒ–õ[J ém"š–·SÖ¹®B¦• ±Â8¿Ûû—KŸ™¯ýÏR.Ø5c•££å$,Ø‹}„€5ê¢W87,sª‰‹Ï¾0„•ãOÍ2ؤk‰t QàÇ(dƒãܯe(5ìrf[¤„´ô ã_ˆ”]Z¢ìˆ’·/"W™7 U(Ë8ƒîHDRwÎ!5XL€ µ &r›½>ûKÆHJÍ ïhØ>ã|÷ ý¤MA 0Ï3~¶Š3—ŽJ²×ê|TÕÞnF½ªDëŠÖ­ªÔ[]éOÆÁ”\ËôˆcPN×ÁJ°‹ÃäEódùÁPáqÒ½©)^ÄY7"AM¤ÀkÑצ8˜U9_€jƒ*TíZ³HYæ€Zb`#q9Úi V%ø9m”Ë•Äͪ}¨7™Ú†š‡‡ê!£³Ž]¶š¯uì1´®V6D)Z–¨jÂd~¢2vŽ+pj¦«"PÒæxkƒþÖfí­š®µníKJjCùÕÁ|Ü\C߈G9zBhêñ ïA|'WmS Aݦ¼(Ý8†”z1Ð $‹I¹7DaÅ*n·#,¼_·…ûÀÇhÈðѦG„lW«Âü=+—þëý¿ô÷õô™§ÑÆ ÀŒ?CØíÿ^jȪBÖ²Ô¼hlð÷plB;CþþC–1ë Éòý×¶×7ŸÜÙ°1¬q…gE$Ðx‡òù’òù"\ÕêùÂo»FE0‡äó+üy¥Þ,ÐÑ+Þl tD™/µ:˜ZTîOÇ‚gJ^á5Ñ¥¸¼Ì½Ì‘S¬gh%ÞtüG õì\=„Rc´n¯Êf9ò×´Ÿ¿{Þô¸~x¨Ñl®7›k±P[†ÍÕˆp"Uæ:8VY'.?ƒ pæO¿|j£±ÙÛö•`ê†?|’s`@£ƒC"ioù­¯bh²Å<Þ -3ÈÆxâi>aikxÐ[0ñ´i¸»|ìçð÷î^:‘ÎÜëF¼ _6±DJ„‹‰¥j›½+kµÜ½9ÝûæÓ;£½][gGoŽƒ'ÜsÙÚÓÕsåLY$¼M£ž¨ ¤ˆ/¢ÄÏ®2„UÂ, ¢c„]µ_¾€WÁ-g—Fð+‹V*Z×¹ç0óýi¹¾¶y¾6…¾¶Å¾´BÇ̆H/ãB]R€ vï*Q·ºû«¦±'0œª²”£Î Œ”À‹´½òôçV”šÄÅŠƒ~œ¥è¶z~ú6ħoÿ§RÓÝ›ƒYKÛ³'ícß´¬t&Gx‡7)¾ý4|°s 8ÎV–óWE{Cˆ3¶ç 4€ÿðï…go®Ul_!( ãÌcâMòk—J–Éê–ÈjIr}í@(æ€PTÂ5²Š|¨P’V2_\ŸÿÚ?ä# «i¡Se° 2XTæPêPæX <¯fîØ„#RíUï¯þ‘‹RCFzÍÙÔ#ÌÂ:ucöûƸPNÕUM 3,g® ó+Ò]k¨2;Àá|Á«±%¶ÚH1xãÜF & Øl± [ª¹2ÔŒóæy =Áƒ’¬µKÓÓ±Ò½˜Ù>\ð£Ê# F‚3$¬ shŽT¶.Â|J »ToYBr‹3±ÍŸõÁÍÄþ‡Ì´ÖMÃCµOG‚sñ%«DuK$e¡üªp æé@Z$5â’0IÅ"§¶h7œƒJüY~4Ü}XE¾´"_F‘7½@Oo wl“5‡ËÛ8_XèҀ㕞‡Ùw¢ŒH…a‹;ûGÜbF?Vu !L P0™*?Ç*_y lceLX!<ʦàÐ6SmÖr¾àž9Šó³¿úì“A³©xfædí@©#£µÿ—®ñqQÎ ¸ÉR `09z‚¨ajúÙÐÐÌ£Þ¯ó|ì™y^yÁÊò I©¿}ž7+Û‹š®µMTgœˆX$ÄrרBk`„ Ê…Ž(¬e_CþVš'nÙʯ™ïÐ-¯Žâ ,| ¿4ˆ£S9ÚÐ]pËLö!å–uð-¡”CKB…Ä•üm¿|–gzR=9´¿¹»·ªíÐDëälç ìéH¬ÆIK Œ ÈÌi{5ýìâ“'%Ö§Wf`iõ yæ cñvÕ:b±ŒX¬$v/Ûâ^‘¹î_.øâƒò_¾hì¾Û.ÖÔÐíÑÞ+Ã/Xz/¢}šºôløÒèýFë@—µÿÖý–£ù¡,££Pƒ ³0¼Yì\ŸÚ…ÒÆh9jxùBqE¤Äˆd™ùâ¢ù"÷J:Ô.u5Ηå²Óõ¶©ž¿_ÌbI8Ánw²‹ä Ôk廇î]éiŸ25ŒÃ®gØh)Á@JŠ@-×~x39mÿ‰•ë „üžÙ þüíÌ¡{m@i ³˜›wÿ~€zÑÕLÍÖ[ÊPj Ó´öBÑðÞÉhÙÊ®U¢OßOï»ß8ÔÛ›etOïUï $N)¬Â@I™¿¤È[íÅ@ˆ€Ü NF3מåÏÌ `á;ç\‰cRr %Ìíh1Jýp$gã~ÉÓQq+aÿ€m‹×B?6¶JÝBys´ m6ˆÇÉî$S”ã8…-Æ—›D ’°vû}÷Yш©JX¼}’ü MúÌëS_Ô×SÒ=9…"¸Çà ;E%sÇÈø¬½|æ>!ô„‹ü€ƒh—€û*‡±KÈ9¦œóPÄ«çÜeÙ¤D+ÝÇ-Å OÖ9$jç=y‰ìdD0ø ‚ò¥ÙÞBœv³ ‚8j9ªÌ«^t58C¡|ü‡a÷´uæ)é©ò ª Y`ðgñ+t4ScãÖ±Ñç{ðÇ7r6­JÐÃ1 2UvøñÈÎ0D\Œ¤•`Q¡?9ÈF!jˆpG^Ó€+[–x5-Ó7/7´¬ðn]éwaµ_ǺàKëƒ/o˜‡/Ú×^\Ô¶Ê¿i™wÃ=þdÝb÷ÚÅ®€=KBÅ%¡R¤ÏWe…¹õl÷`ëA(Z®øÇ;©î¡2´5 à>©†ËhµÙL|àý;6Ð4ÖÛbýÃó¾®÷;O.ñ$‚áx¼Õðå;äTb°¡¯·zb¢íуâgO;§ÇÚÈ9œX¦.“c”g7 c²>»mµ\µ>½õtúòäô%s_£å1‚ê®NÜ«ûûÕƒõ™ k²"Û«_úÓ›Çüª ç·ÚÞûuƒZ0Dèíkì77ŽŒ¶ßì­é{ [­fëìuüg§zZɈLó›»Î·nõ.áZÕ…;_\ªo_âÕºDwyu@çjÿ ˽¯lÁº¸6 }ÿ…µþ­«}ë–{Õ,ÕÖ®ð*‹ò,]ìQ®@ZGa°™,Åó™Žy‘ºc^âíZîBqpµó'Hf>2pn]S#5¦žHÚ‡†ËÌCåÖçH ïøCù«™›4Ìß·]G[ÐuÛWÆvÀ@ø…',LA}„н"¬Ǧj-cå“c€Ž«a1{·co;®hÞûo& lR^e‘æàƒo€@ PMÀn:*%ÐPs±‹SÙ%kX¨3iZ.²0R<ÙéžlÔC€ºð_…â` ‘†@Òî|Ü ˆÂ`nêã·R{;ÌcM5c“€…±uoZ-7KÖ9¾¾+ôò¿ÖUzÜ2—Ö]YtiUpÃZÿª•Ü/ÍËôÕQêòÅ®…áŽ9Áâ‚Yi„²$L™ç/+q)Ô|±rz‚’¦ˆC •™”FÈÈòµ@†áBå'Üq¥¡ŽøÒµöÇ1û°#9tqH{GBã«,ÊJøT³lµ1ßÅ¡«¹ú—_¾y<Ôm™F#3t(”Œé9 NHÿá׋RƒÊ„d…¹Ev5¨;/ cã³æ¡±¾ùæÊ…³~GUЄº©Øð*OF'æBIRÚ€ÿèò„˜ô#Âc>LRN¡~ÎEÞ#Ú ª"|Ž/ƒÔJ.$ö‰ˆ=$L|¯‘l̹W|ˆö3H ¬½HÑÛÄK\b½£î=2¿F|ýYœ‘–'C•à¤áP^ë—@ÄÈ2˜ìƳd¹Ûñ;€¸Ë:Òõeç©3>ÌÊP× †­,I)€²éˆ˜{XÄÙ+dïà2¶2ìÖSm×Rˆ(‚XŠ’bg³•Kß's–Ū• Z—ƒbÆ~¡Ý=ÎC˜î#ËðuÈôæÕTÈ¾àÆ“i`§iYñsÚ.„·ùKÓ=‰À÷ÐH¸ÀùQvN(l*(%D²¡W(?~'mÄt°¡i¬¥w j ¯jz¤Íò]ù~O"Ñ‹‚²|DJ¼B%E¸˜nC7±5pŽ[e.©˜À~€ÃÊ ¢CÄk£ƒáü»? ñ Fðÿ5;æ:©1ÄYNÄ94ŠÙÒõœDO*ª·}¦4Õ Mñ’ÁÓÿÓNö‡ìiûøv¸øëYÄ&G^×vµ·û ¿üøÅãGÃ3˜ÍŽ={Ž,:´,Ó‡ÿó/ü>ÙÒ º€XŒ5w€‚¨aì¹uüùìøøè³‘ÁÉ¿ù©«=ya`¬Z’éÌC¶¬„€„ƒ ’ëÉ+ð•9¶.õº°Üpq…÷Å¥†¶EÚÖùm ½Ó÷aÉÌ/mÖ¾Ëã=šÕÜRÞ7Ñ4n½X5QÚÚh]óJ0}ª—’•™ažyážÙ!®ÙŠLE²¯ü´·ìl¸çkÞÒ%*›Ŭ²mî7?é®Ã Ñ„&d¦Óü¤x¸¯ì¦Þ²§ˆl¸ßz œ¾ËÏÎòÖ~ìWt .wªöš~nø¥²èZ¸ö‘¥fÌÂá¾rëp‡uàZ[Â’= Ø2¢µtkϯMÓæ+dj§äÀŽ¢ýªS¡¶ñ>ÜwhT€ £{'ÃŒ@=rfÄ8@5iƒ§íy7)œô–ƒ\Z£ íË|;—û`ÿ´.ÖÐhYèÑ¥«Šò*ˆPƒK€÷[¼P[±Ä§<Ú·t‘!?B“äã„!Î9­4=PU0ß«t±éòà½Z1ζùqËîÿR? ò³¥‘Œ×3Æ N¯-툩úýœ%pº^¼â ”ú¢'ܼs>¶|ãkò^žcÇal Üæ5PæÀ¥A7c=wJœ»Mœ¯tO¤éaVF- èžz*ª YcAf“‚¯ 7;dN!° Ô¥`¡æL°ãK*"koèOŸ”Žš¯˜-ž Tö™ÊÂÃyöAÝù4c´CÉYY¤sõ ÔèíËüëëK—ë+×ù7¬ò¯ŽÖ§ù;å„©ò"ܳB]Òƒ”iŠT?§D½Cv k†Ÿ <ºÓ½]r=ŠÃôU kù—„¹dû‹€/Å«i t¼Â!îdGdTK °ÐÁÙ8$¥âmâ½ïd[ðŒƒ¬Þ–XÎ$ôæ•üŽÒŒ{ßÚÓwÏ2 $wÇ&45hgòþ/¿Ðû@Íì4qÉ5û  JyfÆòüÙøä˜uløÙ½ïïßîÈ_޹“ðÒ×qªƒ]ª#5INIu¥gixGÐÌ€{†Ë ¹¥”ä¸âÁt4`< äãë±KLì‘’ëU>±_F®íx.ƒöìH’UNÄx±Êiwâ †ùÄ+xx Lo_òà{qˆmKU~+·§çj¯©Í<Þ BšÅl.ƒ ŽQ(5ð!âyØ:öºuô톔èÍ¡b¦¯VmÿáÃâéá[ã_}•9¤õùkw«µïŠõÇú¦ƒ^çRÏͧ§/Ÿ_$Ì_éœ2_œ)K'Î_¢*X®Ž äž f^K\1úY%Ø€Ï-W°îö”~ösÒϽy#Ó5Ó3ÈHª³ÎbÓhµ4[¡û¨ªÿ½Ø7³æl‹€©šÝOâ5/b›’ØÀ'bÜ™ˆÚÁG ŒÏ —鿿⃆úr¿€lêÃ3VB"Xx®°Ú…g½ŠuÒÓþ„ŠÊ(6®ÞVxZJˆ0.¡„½‰Ÿí­KÇ,æ+Ã#&Ÿ^ìí©Bÿjͳá¸]‘Æ€3·{>ÌN\'/ÛôüÛë­çÖžŽtZïN´¤®†Õÿq€jB‹ˆRƒ8ŒY&ª!™ì.}©æÀÕ¿Ôï=¾\éN4änxümÝh_;šÉg¦ëÔÍ7KV¥-d… Ä2Á¨<Ë•™¤¤¤¸Ð£—¨`ÅËi‰JL}ãåvÉ®Ìl-Í0È{Ø9Eã$jpŠàV·OHŠ¶Ð‰Í4â6±K@ì‘À¶ÊV±ºo|;éä™ÍË.ìȸI\ñðnóäØe”š S%ÞûÌP+˜œŸµ-[í™®æ".YÎ6ú¹\ó *˜>eÜr ê,_ Ï4_m’^¤wÃJñvÃ7s4EA^ðÕ/™§\™í'ÍòážEHkaˆ°4LXÂ/Â$ˆSÂ&™ª&’œç 8#uÐxR6Xç(ô)zÑ!ú&)1ÎÆÛ¼¾û¨pdðÒèD§e ŒˆòI’'ÙþèŸ)´Ä5µòa¹Žˆp§Á±ÙÏR§¹퟊~JÍ9¡bî‘×ë5>±F¸ðPÅiÍ0(C ²‰ÃB›2;xs!Æ0SÅ9« Щ2Ò£y¥ÿÕ- ÚÖÍ3.Ôfø:×I“õàH;Æ«…™ÞŽ©:I–ŸsZ€ÛIÓje‡Ÿû›Ëî}ö~÷ýo‡GúƦG'@ø}Qjþ·¦€ñP3;EÖ™T›™¥f*ô6ÓãÏFMã?|þÃ¥ºÔpïDx”ù)+½$pTF˜R8ó=¹ùàùJš#=¹7-V·.Ñt,×^Zéձܳu™æÌ5  ˈ6ë8ž€Ž{nœ¼}&´j£¬l…¸t™´0Z’åPµÅ«b³÷ÅCQ%k½287­iXáW¾È½z¥Gå*UÛVß Ûü*–ãú¸_ÜâÛ¶^ùeÿÛ;çÝz-øêVŸ«[ ·vÜÞpy‹wÍj}ýKMk}k£5U Ý룵«j7ÌË_|4Òe±qb»î‹æš;ûûj0¸Ÿœ€OrݳÁ&ëHËãûVëuëøµšSi\ÞÊ}uöûko–ìIN€HV»ÖÇd©ª$3ÊI¬æE©©®0GÆ«pÁͽ¥V`Yc·»ÿžŸ¸Ùu•ž(‰_úãG…ƒ÷,Ý PB_û×…]…ëœòæ‹áüYlüÌK ¾Y¦Ëè-ƒZß,õ‘z r=¹àé•ÀëÞ¥y‘sËb— K]¡»´Ê£c…ªe™Û…u>-/ù7¯ó«_e¨Y®«[©Ç¿¶o ­Z¡­[chÞà{aK VÓzã7\Ø•""BJÓ6Üÿµix°îú+§L5 “¡2îñN €uœ¥à‚5šæf8ìdîëñzv‚žgàŸöâœÔqŽkX'´ì/îo^‚ 9P”,Í qª]T\æ]ä^Aqˆ[Õ|φEžÕªbiž^’?8Ì…zIŽZåÆ7êKu’2½¸ÜT!„]Ù^BÈ©J–zŸ’­w#JŽG>øwÕpßÓPã SÔŠ™ ئÏ2JÖÛ7¸µ½¤n[§½º%ðö«ao½vmc@ÛË»C»vÆ©[éոίa­oÅRq±{õ2mÓ\¢Ê(Úe: |íëHÄH)¶JÍB$ÃÝ©4@^èCÆÐ@îQÝb”O¿+]'‰UŽm§ 2¹ØC%c",Ùµâ,'@aÛeôjIé½ßÿáÆƒ¯>î¹7jÄÙé“€k¦1YúÏ¿þÿR33‰?ŠaÔï] „á¨6Óãêùî_ßuÔ¤G’=%ÐãÀt=ΞÔd帳òáRî-¨ ’A¶SèÍ;KGÒ^l_&†"5ŇšÈÌä$yÓb=È*}Þ‹Š¨µx­ V¢Æ&Á“ÀŠÇ«ùšâg—ÌÈ bgù³3øé’X?É>½=ºâå^¬Îª#COnZ:GL dfÊ@ÁÔPٴو`8’NLØÜ4en'­nÍ×M_–ïZL]ªÃIßo?ʶ>ÿã㇈3è´ÎtY'aòßt+>¼~£¢*JÔ²T^àÍ€‘ä*eAçáA­æÁ1Á"0½Gd*é¢'¨ˆC "Ú)TR!‡QUFˆC\x“˜ØìHl’{ÝívâîF¼ Ê¦x¹ë9?zr(/3\|Ô8ˆ¶Ü‹vNc¯±ÅÅ9ïi“ˆrm‹Ktƒ† nn?Ç ³w0TsĉâÓóœ6j¨árœûñ‹R€´Ãµ˜ÚOMvõ´ªÍjn½ûc¶uæòô½Ú]¡ÄBÑm}òîã?W$¯p}É…HÞîñõ_“&`Éþÿ*5P,ŽNÖ`’5ÔSò‰ºæ«_×Yb¿Ò‹H;òõ_³G·>5]˜ê©·Ž\ùñVÌ Ô#ðö €+‰ô\É£TÒF>üqB$·R!LÀþ)󆿴ì 0»„}VŽÎ¶Ð‡Ž©7é«.É0p³ýÄ9þ’TÔwêY¸Üèþö…áÄäÏe°S|èÉÞ4ÀÇ=mѯõâ\©=2ÐsÅ]+ÌÐ!Ažo p<ïd“¬¤ç Á×d‡zÄx+3Ö,üsuÁ·ïÞþí‡ËàÌäbçÈyÒÿVipÂB%B3#ÏIëÔ44—äøÉj˜E ÔðôØähïèŸþÒÕX¼$¸0е&Ä:\¨äŒa…Ÿ´"°4«C¤àv`G…!ùW!œòyœêùöu‹dM+5« M+ uK´ ÝŒó]Á©]ìyqÓR]ó2¯Îu~W^ò¿°BÛ¾Rwe“OÓ:׆uŠúŽ5ѵÑΕÑnáŠc¾¢ù8‹E¹|úvÁäÀë#Ð(™ñ´ÊŸ2åϘJàÝên5ˆŠ#ý"F®YÇïܪܼo)}‰žh+Y}÷Ë©±k×ýv¯nŸÐbÿpýpÍyëZù•Õ®¥^4RXáÅ5zÁßÃÌ%X>–:‹ ”‚TnÛR¿[›v¬ðG{S»Èøȥt™kóŽ€›Öï:j>V³Y]°XдZѺ^“ {ÀEîE‹= ¢—pa”Óö»2_VØnSS×8~°‰R¶Å®9·D‘±RýaÖ¾âåÚ´p¹q¥wÎB·ÂE-B[Ö·¬ ª_ªo\êÕ±ÖÿòÚ¤xÀ“áòŸ†5ÎMë”Í+£%M‘â¦EŽU å9áNûµ´NÄrOâbé+w[ :D¼ÞžZ‹©ô­2·cÖfµ¾¼ñôrî±ÖU‡­=ïü£!k½Ç>?›¼C>÷?Ïÿ¥¦–Ìs™© ¥7‚µÿâ̽‰eDÜvÃ×Ξî-ðêS]ú(§x½¢$Ê ^•¿¢)Xݦmv¯òU"fLZÒ!«¼*|0©„¬ ÄÙ!Œ]Tj¥?æÚmËôm+üZ—û6DyUÎW•‡;WÎw­]äVµÈ«xž8Ý@OÖR2|h ¤ ׈‰Ý T»=j¾=ˆ|Ò‘¶ç–&²ÔD»u|3g‘c®FPí£l ×5/Ѐ²ŽÈR é/€J¨)Òµnke¸"ÙƒCª /N¶žãÅÎÞëa—ên—ï#…6?Lã]s´ŸKÛ2uS”kãBH>¸ jC¤MòÆ0E}°¼ŽtVw†…f¥VZmpÀÖ*÷q@GQ¦VsÜWSÖ¶Ô –{-ýÈ@PSKÎ@'@’¼ñÓëgË–96/UÁC»}¹¡c•o[”®s±îêjß¶>5k.HÜH„Ø:\TlÄÄÀÎúè›ì!f!àvø‚“êF$»âX:ç‘X†MŠÖ!Á 3—H‡nƒnx¼°/ƒ€]A3Ó}HxzØ—RÛíqA¸…d–~ÿ]?=Ð5øá5SEVK9¶g‘¾TEB—j— k÷S«6ÛRßlÛcú­þ·»ÓO¯[Ÿ¿ [•ŸÞM.Üæj\©Ä°TÛI’„gQš)9޼s ›4{&B‘PÛ·x'æy%;]Å?#µZÿ¬+íœ'㜎yÞ‡—".“…J ~É ”÷nr¤,¡ÑTbXÆ S‰É?ÙŸ–µ'¨-}Ã/ïý©öèV?Jì ç/f¤D©7ƒh†‡A¬Ç#†Nª¶ñm0Bö8¡=ÀôM1÷˜ó!R܈sÐêÊHcI(¦¹¯¹Ú,…/zëýkq3ƒ×ák:;Úb1¡!ǨýæÐýêÑGuÖg N_ýøÂÁþŵŽ?¾“míÿÃìOWs^&°BòõûéÿcØ XØ\=`‚¸LFË@%é18óÛký‰E.Äñ—<>{+mì 8‡rž’Ÿ«@€5#YHM‘°àCXäᘋhoGÖ9¤æ)XyZq‘¯uó4p:åUù;Cd‡Ë•ë¥<ë*=¥q^É¥&¯]ú§ÆªÇŸ}l~pßÒ×?>:6=÷j3Ws^|ùû+jÍ,ÔR¤4“TqcA Ž1`«Õ4…–é‡O~µüòÍçu]××ûh/‡ê/¸µø:·¨:Â4Â5(ÝÀýÊ µA.8‰Ã䮆èÐÚ!†p‚d ¾CXøÎÐ)Ÿ *‹rVb €!æ¡ÅÞ|èDRUY„½Y>üXwÆqOÎQ_Á±êãú=½ßטÕcJ‚T¦gã¼Îô7 >4M6õO7 ähØŒ€¹pWf?ëØ  `Ûrl³öö9Ž[.v?…ø}ëØ¿B$jh;ÄÄ.ô î >e?*!æ‰(ƒìjÊQ6ý8Íî@'è¶G ª»±nvèÙžsd!‚3I+Í vÉvNQæD¸¥Éãõ¢¸ §—µ‚Ujn¤" ?_£'ެ”¥îÑ]){郮C>ΙúµñÙ½ö;cˉ¤­ºîkî½S¼ˆMbň."m0õ†+}ZÄ>%¦“e ®O¹”«;ÍÑØ€ä z9iØ žd,%X±WÇ[?ŠWü¿ÿ¨ÜÒw fÂÓãM˜MƒÓë¼G+F1ÄŸx×úðfÕ΀—ðωúòù³7¬¿^Úè@l@ÁÊûü½ä›ãÐA¦Ë™jàÿ ˆ*ˆ£û»‹žN€§g´ôׂl…ÓàPW¤'±É›¹Ä•øòí¬§ƒWMýµø¿››»µ§Wi‘™©”jF­ŠêPß–y!%žF/­ÑWWâëžgpB‚[®šžn†ˆÈ@yÁÓÕºxô“p3@ α9ÂDÎñ%¾Bøxº;x,– ×m»Išx?y'1Òqœ(ÌŒþçç9¦ËðÛ9ÀÜê@’Ÿo5xÐ÷ι,r”"6›Ê¦'È/¬OÛôÕöÖO\€ÕÆÚP"RJ1ˆ7Ž˜~k4™j,uVëe´Û}ˆ´YºF’êÈÏ” 3ä\„ÐÕ8ç+¹y6Šé#@Ô,6¹Ðôbÿ0~ß?Gà0ɶ²°ÒeÉR°F²Ý•Ø?zwDT׆êó=sÕ˜"QÎË™9Zi¶F£bCŒvþåàoÿTdŸ¡Î Ñä„z¥ø¹fºàQîçDNdÜù¥.ˆÖe ¯Ê¨d”ÈmÒ Áœsf†;Í1ÉGBLg¦ '× MJSŽ+'_+,ÐJ <¤h`Š=%žÊROe±»²ÈMQ¨Ræ8;žóÏرbÞá 1ï°€uÀžý“þ Ån/“}ˆÍÙmKÙMØâÓÿWYìE ÈmqñÿñÑðýû¦înˈÙ:9þ|z Ìï¥fΙïE©!ûš¹.Ž͜FümàÉÃSOG­Óÿú廯çm\“ TdIÄñtJ,ÚW‚ôKD::ºÃ4Ò}å.ÙžŠt7Y¦JŽò’ ±Ïtq:mÏ:ʱCÆú<žþ+6&x<Á*V$©Î <*À¸†>÷ >—–ªkç»”†»å†yÄ(6*ˆ:‘´ÝëÛ¿d‚p‹L92zÉ hÕÓ¾FäQöMÖý0®ff¦“ôÄy烢Ã[•¶Q*¢½x߃ÏëWÕó°üÙèëÔÇ©kôküŒ+ür|]³µòƒ{‘Öí$—~ž}\À9bÏ9À¢£†fÒOð81RáQ‰ýAg·=s§=m‡¾SÊØ)gí3·9Q£™$q.ÔŽtˆ]*%6éq/äÆ,ë4xÿfê÷ÿ4>ù¡Î]°sÁdÒäöwÓ½¶aO„´1a}÷—µý_ÖŽ}\ÿ²‚¤2B†ƒn|¸à ëE øžŽ_$n 7 ¡soǤožs}°²®ªþŠD­–¤¡b½ÓQ²oàÞek Ãût´uéá ûŽOwZ,]VË;–¯/¼ª!^õ#:ÛvÜ{R7‹ðˆ‰?\:4ÿ¸)#:ÊövÿZ ’0›¹®†¬0ÃeÓcuýO qÍâ å œµFaBø^Uê’Í:f”„¸Y±¥ÄH<šŸO‚Ýr|ý´7 Yi$Ì j #)ç¦4‡ð±ÁDÿ6·Qž1±s²=Á„ÏT9w¾Ø?gy¬“ ;¡b` '€h´%YÈMð`7ùIºÔ8¾±Ü»3ÌVäå°9P–¥¥©ùð…¼ ÑÞø°£Á¸pš|”q:™ŽuŠJ.|Ȳ<䜄wš¢ek•Ù^.YzU–Á=ÇO—aÐøƒýpqæ®.M­:ëê¼…ÍXÃçÄ-‹z«ªìѧÿ{rr¨ïÙˆyv|tvz ÎzègæÐ²«A‘AÏó¢ÔL=‡_Í ‰íÌý6þ9öôéÈÓñîG?|£#&Ä÷‡‘Àa%²YÉÃI’©pÈp–¥ºÊ’]âTÉ•rpÑá÷X¬ggºSç‚ÎE‘¾Iáž]ì¢åD}ÒÊÇ_V>5]$OOs=tÜÈš}XaºØc©º?\:2Ñó‡™Ñ.kßmXG2p~atÕ¾Û ͲÕú:Úþ¿–†¢&‹È¨|Ž0n:b3W3mHk ñ»æ™hÁéB¹ÙC 4ói¯H¹ÛØëD”e\"’MÚ‡±ÈäDŒÆv…I’^¨9»îfù¡ïþ\r÷“šþŸ.Ž=éš¼>9|eÊÒ15zÌ…œDd’Y` øÖÎDŽÆã+†~lþ±ÎÚ{ǸEñèI8¬¥H¹qbf"²n|˜AmZ,ipl›¯( D^¢º`׿ù: [½zðç¡“\ëõá­Ì‘'׆z›[ó¼:áÞCêÜÇÛ̃ØÃx³fïjœå·º¼õÖ‰‡MàNC+ñ·¬Í§|™a<¢:mÝ£ï#ð3‡/‡‰Ì·zQj`¬MFG ס«AbÔx£ùQ-†Ú]U¯.^a¥1‹~ý¼°¯Î6(_-àø]83?Ö—ò3¦N°Å&8ÖÚâkjÙü4G16ìR]“œ±$û'KMtrÿ¤8ˆâœÓ<Æ)> ¢ÈÃð«[¾½m¶›¤n¼‹ƒë"¼Ðø¥zˆªsÄÌ$R.øî£òÑ®þÆášÑÑúÉÁ:Ø>ÿöVân7b7ôb0½Äó‘JÀ¹Ysm¨`ø…±ÚGé¶ûq”˜;8ã¿‹ß³tü12«—KE9ͱ¸”â±Â,ƒ€lìppž±Õ!ü$ß/3]ÄËu*eù.Ži®Š³*ùA7‡•RʉU†ÜÊ|Ð9ØûˆzˆîŸ‚A1õ¶õ—®=˜cJ`º¾í ±íI)ñºÙW¢BCo7ÎWÀðªÜG€ð8ÕâÂN°4ÄzL±ËƒÜêÃ=Ã5Õ ÷ë%hir4d ç9í9>å×ö,ÇaÁ‘`玂ƒbˆçðÓbêQ ®bÖf¤ìtŽâˆë€¶a7…ç9ê Öôó§8L¡ô`MÎ<·LM ›LHË}£+~~P¼3.”â ƒËdŸå°c¹ì6ó‹v€aƒ3w‡ùtuqè§„ögÄÂD'Y—gëâàúùúªPÏò ÷$ïèy^òƒ¼Ôǹ2È­<ÀµÀ á§|äŸf¸q CìÂ*i2HlwHi¿Yb1é"'âvÕþ¡Ÿáè{Æþ(5PìÂGeÔT5ö¸lv¤ñQQÿDÕøt#nÜÖž{¼©û|8É»B?y'Ïòð²åI+ÎÃ?—}Ùxä°3ëJb­ØI„'"Èçt¸6`ÚçÒ³íö1mvò¨Û•ÂM.ÂuNœ¥bêÂ!‚¹D°=±Ì²;Ê9eOxKÎŽodüð÷ÊûŸ6™~éru¢çÒøÀ¥1SÇÈàÅ!s«y´ ±’}}åM£˜xÓjþãJ DŽU§ÖüòÏüÙ¾vëØÛ_׬Xâ [lØVzcÌç\¨* tÎÑ ÀlÏRÛåºSÕ µ”ƒx)5AÆ‚.lâVrˆ&‘s ò§"Vïš©Eã‹R3eA`4ÍcH$Þ;²Î!ZMdŸ üðã¤nKKoO5„;N§F–8Ƹè{_âˆ4g0SéÂg®1'Fª§¡i5Uáòâl…ëÐätw}xýÔJ9Å' Ï{ðo¸ÙÔà~y>5ÙÍÏ›öÇûs“\Ù‰"j‹ n½M’ÚÅrè§ÙLìœ. xÔq.ÈØ ;lžÿsÿ$Ìퟋ‹Â[Â|qâ® ò,ó×Nž¯u*ÒË¡<+¶#µØØüöHÄæŸ”3×ó?6‘ujÙŸÕ˜M—û›PjÀ¿zŠ‚áK¿Þ:›)ÊŸ¯@`Y—,Ï]œ*cA¿ŒÌn`­ç6'p”ã bj,ÏS£P PÈ žþÀ±¹6'8”3bN¢\€§?‚¢ÒxÙ ~¡«0OIÚxâ5Ï•Ÿë*Èrág(¹ëœaÛŲhñ,z›~žË:˦bÐ^¥SÖ1mƒmˆsC¾ýsåx÷ ¨çÈC+Ø’` Lܱ>z=7J Þx¾VV sÌñ—øJ+ƒ¤uþöå:Z¡;Ü{(“`aÌt²=D¹ Vž§èÚŠÀ–†¶%¡yÁº¤@Ín7Q$ìéVz¼Ž÷µš{kAE©ÁÔi|¨ ‰Òˆ¬}ð$×G‰»³TODꈼäð/(0ÏtŽAå󿇥¥«”[as,ôçO²Íݵ“Ãm(5cCX¯–Ç®ÂÝE¨6@ŒÁµ&åNÖ·{¾(?*ÝåÅ.9Öóu¥¹·ÎSÁEmÌSÚc•ãÈÉÒ’6À|á nO¤;Ùiø%ÞâBƒ0ß[e:Ñé{œËÊC•(wFoi¹#`7T¿|2zŒ§ä€»è57aæÚo$ýëõ®‡ß|‰ÆCíg(RP @øw¿ˆßK Àar=ŸÅBAsczòÛÀ·Ÿ}s¹å¬¿ˆ(¹rŽ©bqÏç$JD) i’Bš ãÑN‹9'íXñRþ).§iì"T?LvÀ¤'YŒôóÎÌ4w*OÚ¼W£?À‘¯sÀó®-ºš32þm‡”³‚Kø Dcƒá«?æO÷]³ôµ˜‹0†RSÞ¥fv°zÖR×Ó—?:Y‡ëX»uòõ7Ë·Z,BRvSæ†îïšaæ0Ñß6Š'xOÓQ5©ÏÑP`ÆU¦aåÈlŠ•ÌBGf¦Ð.½ 1Ö1 ˜?#©„ ™P"bM0f[`YâÆëõ1¾‘óõG僯›^7÷wõ÷t ^€¦»·Í2܉JˆˆÐç†FêÌãCÓƒ3õXãÈn˜n$5ž·ž?¹¾Ñß.˜C4Ä¿ôäÛRëÌ5ØS$,fœÑç] 8Õg*hi &‚HÐÕÊó}ÄHèËÕ°³Ý˜@⤴8ü\ê>–Ýç;´¸þÜ®ª}ƒ;F͗怚&dû÷“ÇáÊéÉv«õÍÇŸ•n·YDT—¯úüûœ^ 9 AÆåÌ_òÒçs7¹1ëäß”ŽŽÝ×è1C©Aoƒ3ÔÄHͳ‰–¡Þ ´=ý`ãÜþà“¢mîÄjœû ¿z7©÷·ŠáþÒç|üæàŸ²÷9‰.T(ð4´%Ò0>f¹JÇT©$^À?'àcÿ`^ä$Ž— ÑIžq±yNñ R.@Ô"ì̉P—дƒ/³KpĹ^—Ì£ñ«š§3†i1ÐÉ›§KñW4Hñ8¨Ì}õá/Cƒ]fËÅ¡t²ZÚ­Ów>oØ“Á;ïEÏÔpâ¤Dº’‘¡`$JIRªl½E© ·ixx…\.ǰ֌ã©[,܆8%ÅÁ÷˜MšŽ’ÆOðeåçí)©BÖYÀ2Ð3¢ùÁo‘Μ¶ "z®Ò!S*Jæ³°pŒJw'¤B)Ý­âËÝ·ôîGõðe†ábßaqîäÍ‘¿žÒÚB²Bç"ÌS‰ÒtD÷"Œ²Ê_\äÃGžZ¼÷”ž³™kδãÎ ò´ù-L¹Qy„1”¼2%!à‘”æ0uEˆ …4Û™_ #ƒ»ÊKQ㥬Ñ:VzˆËÕöðUóÊ4< :‹½¸è¢|Ûy(¤N ®KM!ÉEˆ±pgä©X)`žÈ(è0¶;© –37"LÃ2cÖÞí·šêÿÝÌè\ÍÉb‚)ˆÁs~Àfæ¾CÖ´7ÐAa A—03òð®ù«O~¼Ø˜¨ÉôR¤Ëù \z,ƒKʰ·gÇ ˜ÇY6û(P<Ù¢±q¶çA|±°†ç¿yÌÌÀÄM-Ìëe`1ÕFx6FzÕ…iPdâ¨èÍPað×q€:ƲÉÒ©â5Îñ^;=Ã’shñOgÍ7GM-æþŒqç‚ä*&+&ûK­cu –MM7÷·éB‚aÜKÒýóèk5ÄÛ͇L¿¶õÃ(Æúè²Ï,«]íP¤·)6ØeâÌëÃ+t£Ö$yNT\ðwÌÖÊB<Óœ´¾êLÛIò®ð¼¸Õ—*ÿívæwÿ¬yòs‡¥÷ÆôÈSOçøH|üúš¡62 7õšêFÆ0bnÁà>]$™ßRÝ;ZùØb|d)yh*)Ÿ™l³N\ú¾n½/ûãÞч $M¥¯3v>¥$Zš½ 9TÕêÑ´@Wî‘¥—Ä:Úàì ì×yŸBÙèÈ©ÉP+s`†øõr^†+ü²ïß_OÄDVØjHíâ}û$¿Ñd| òÞøµ/ÛæúÒb»®®½wWMw’Ö¦ö©÷²Óæq^ó$N®pøîï(5d‹Rƒ3ÔÈ@%J©§1ëL›¹¯š f$öÕ{%~±ü€–yj©ìç÷sF{šA™C&/&ËÖo›v#ÙÍ‘Ä.^LÊÔbÔœxí “C·;æã³Îòé<íCü“¾ß÷îÓs|ÜËØ?´a5݃[hy‹K|dèðkç©ë"tUašâ ÏÜ Oø|îv`„„†A3¶Þÿ¡ÍÔwiÈ|ìqü´¤ëà½æ?d¯N ãç *ç)ÉÿšÆ>[ÍÉÑ qLH7ˆ3ü¥qz^¼Ÿä#Œó`Àƒ?£©mªÖy!ê†Pus¸&Aºn¢lgDîò²9är`cåHÙÉövÉö´,/×Y”!ç§È8˜¾åâýR‰IàâhFÛϲ¤k½€4dÈÙõà“FËCÒ‡Š‘Þ`@º†ÿš“ÌE˜Z¹^YàYåë^ê‰ýPPN­ð´£Ín±ÑžXÉ%ÖðˆÝδSZ! q‡ƒtUó¼ËµÀuqeÊ# %¡ž9>r([á$?N…¸1Ñ}e;q ]øÅÎÜB%3_NÃʑ۸P‹In‹&ôx¼¾[„˜~ï—ÄéàeØåщíBÊ6¾Í":±ÄÝîö¸ž_/ >¹!ØHà "ƒÍÖž«1¡vçX0ÌIp¦¤©ÈEª˜çŒ"§Ä@/¬eíõ ít#ö{Ø6pc\ì€{ìÅ€ŒŠH  B:ö#| ]÷z€yM,B9_ÜNtÒ–8iC¾Ù{z¢Ë#‡S@ÂûàîÆ ™ng„v n¢xåÉ Wh-+άyô9B…:†UZÇNJѨ#Kèu;ÁATE <ÁÉþ„˜–â)ÉõVžW‹N8ó»‹vyJ¶¸qÊçÛ»ôWƳÅÏyœm‡æðé”n »¨E,’=‹½}‚Càj#$w\âg;2³¥Ô™]žÌ®TÅI—)""SI„M_¡ŽÚ6¬¨Hz$²€ñ}5½ÌÀ¯„¸AT¨bŸãÿ>ØR„é؆/ ˆ`–M”Ný7n÷w?À¯§3ÿ]jÀ¶!#æJͤuîÑçæžê·Þ¿;û¯|Q”yí•Õ¯o¾ºÌ4¤V_U½^Q¥s,×8 ³ûL9š²T™-î‘x.‘&$2ÄDºHu!Žå!¥~bø#Áº6³Hu‡ý(©sY$8 )'O¬)û¬”“ì¡8ã!߯–Eãð¢e\«<`ºÛÓr˜{« çÇfÓÃæ1°2L–þ d,8ø´CMsþÞµp¨ƒ¨.ßÈýÿØz °6ïõ}ü î!Á!@pw‡B••u•µ]uu£Š/! îÚB}Ë™œÉÙ™K×m]K‚ÓüïOèÎÙù}ÿ»>WÚîj!yßç}žû¹¥‚$ Hª‘å 54\Ðgž÷ÀâïÉ`Å£§ÅʱZlŸ!oQ+;‡¾)ÚGH6ýñö™¹Ñnbe£¸‘žlR‡‰’^Èiô³)u¶„_üР—ÇÐ \ܼ?$h€­ ”æB W~hàû(°‘ÙTg妻ߕ¨&.Ž(‰çþ-ØåAþ3‡D!UÒÕ? ñC!ý Æãðr‘ƒÜÛ¡Fdžòvª%…ag‰ …‘óã¾£²þT›ùR3:T3ßÕü·Ô £ÖvÒ:âæ§¼!ÜšgûJ Ö»í{FžboÕ"ƒI…ü’z°/w]`׳Ðëj²ÿµ¤Àîhïæ@×Ö`÷FdÐCúQínNrþúA©?ÏÖ: ²–)—’ùëW~=âBžKGh5²*|,!‡9­=ÎúBoë’·ìçݶfkMiåÖø›З5úð²l {b´ùpW&)`Ï. Ñ~ÕŽÔ¥-¸éؤ "ïã”ã¨?’tè;L׺ÑrrJ­ S]ø‰¼›/%v/ jˆôݶ0Û®ÎxÿÆPžÈß©)ȵ#Ì«3Üûb¨æøµû´…úvDvLJu'†vÆ…4Gû5Çø4Exˆ`YlU³*ðbŸå1÷º˜mt6ð×£jN-ïÿ±qFÒ£Àt¯ɇŠ’ õDן·N¤…èÁ/½ÀÇ&ƒg}ÎÃî”§õQWæ³VzkL©]âs‚¿ÁZ~K*3Þ㬯}ž·ã9;f:‡~̰ØÇ5ß×%Û×ñœ‡ Š²Ï»va"×z‚¨!«`ý`![§€£}ÆŒ € ¯`! .ìš‘{‚ sÞM>Šå~ôRwÓWãJžEóR¼gO¢wW"8t\¢(·‚Þ:7Ûµ_ܾ¦xúhR)à ‹§ù ©3¤ËŒjC$—Jõ„bN51©œ|¦¾w÷AG뽯ô­O¾´<ôÊÿ¦@û–Çö`.¨Â=qÞc½Ð^‚0ÌÇŠÍA?A c5uÁ†vC^@@‘㪛é !uƒí¢ òy¾«qs4ò^èë1ôi˜–ìr/v±ëÇøË4¹ãÕ Z$Rx"­þqå,Pˆq‹RÞì¶“(5J Ò'¹¸tF…8{ö°`÷i)ýGqþçuTö.ß{ŸÏ ]F~œ¦Ô\¯ßêÚµÒ¥+É©5ѱc ïÒšÀËkBÚaýáTîÅÂf°gUtaw¯9ÖëÙ¯ÅÿiHn%0PÀA¥ŒDÀH`(Z坯´–0k•‘ÖåšS1=Ñ€¾ S¨³ò‘VP³àµ4Fò´fz“E'šùGŸfm¡N,·¼÷næÐïüñÁfù7;¼©S`;S®FÕ\³Îè ¶F§¬õ!@.òÂ:ö):Ã4  ÂæÖé&Ð(lè^‰eÿãæ©§…ªé6ŒiððArþ9ºÅ‹¦Á*‘vß»uúp„ì=¯”mzô½hÖ£CÝê» êß.fÇ1ûö.B]ï®{§ ÌØ¶ 4)oö¸?ìßK (ć§­ê±kŸ”lËOvBWÓ^´òÑ]>–n»$ï¶øjõzç¢øÎY7…9vDò:#=›ƒ]š\š[‚]ð;¸~ºc=Z!) ±€É ‹ TBUhƒë‡*âPùP^@€ã¢ ^´†¦B!¨ÜË´kÁ¡¡Ã3a§ h*Ô*]âž·9k‰v_½Y0þìªr°{rô"؆$L¥æñ¥œ»ìxëŒPVqœ£`©×…xGHÏöz¯w"8ÄŒ aQ+ýi™/û\:³Zrµ¤)5äÊš˜–xïÚ¶¨ÍÉ!µÑ>MÑ^¦ ¡ÖÑàÇEµAi p¯óáÖûº4ðšB<›Ã½›#½#}¢pú5ÆxÕ†¹ÔEpÛ}á9S‘àu:Üa¥-…]CCMØY€!ö|ÏÊõ\«ZÙþiãæUÐAàÆñçÀž(/Á÷P€-¬´âiT |ð´©hcj±³ÙÆHnÅ”·Ê\9öÒ>gc äà)¶0o¿©îs]¼3a™ö/è¾XgëPÙ¶&À«o#!+³JWz£¯]G¨óµDßKqàoŠ¡ÖÇ®d×®$^[¢[sÞA—ùÞZvkyè¥x¯¦`Ìø½ÉÈ1éˆõlŽrkOôÆÂ¢(Òmƒ›]jïÇWº‡ÿE%žŸ¡°xBA©dC4—³„Â7¥ž’©Çe3Ê1åˆòÏß'¿øìÍcGJC¼K|íêGŒÛf++p0zßÍë[òzjTw’ÏŶnK„cKˆ (Fõ¾ŒZ_†0ˆ…]0تÈèØÝ/"ìAlTÈ B °j,p6  .ØR‹J?{8žsgïãZ¬d’ “³ƒ~ÿ—@6p(„BÑR%„öó¥fLÊ—IJ'•(5BØõO:2þƽžSÅëyKÜ©ò´˜þ¯3—žÂ­OýäbVŒŒKÜ !9ãHËñ1Ï÷§gº"ëc,:¤¸ÉŒZlAù›P¾ø±#Ч4íJÙ0Y¯™R`S k…œ*Í©—áTá–Äv îĸOGž6A{%{Ô¤xÔD‚¦nyËÓ¶n}%’Jß`ÿÛ{Y¤xþÆì½ú3ñ…¡F>†Í¬ö@[¾‹9žòx¸à=Ïáš!TNìIJÉw7@DËyO«cŽŒãžÜÕL£X êÜÖ0ø[ŠGeÓ Cò²QyJͤ¸‰¸ÇÌApQ=~ûSþ6~ª÷Ù(ö×geßw<Ò§žùDýôÖ§ù/ׯ*Yì³Ë±Ô†ê(^ûøçJB‘R3JöP Óü½ÔaŠ”!W•½Ÿ”¼rÐÏ0ŽNUžŒùãÇ ¥¼{l¤c±ÂO/Öo÷<à@ÄJ˜¡Ò°à Ó€¬" _3X:ùŽäúiv½º4è5Q=IÞ]ñ¼Ž—öHÍõÃlð±¬õ³Ñë¢8ÂœÚ fS¸5îdÄ“;Ñ¿,Ø¥(Ô½$.pâF®é‘•n?}X‚VV1ØŽg„¦Ô ¸ ]Í•Š­~+ÝÓãmOG°÷¸ë­³¡Rm©3j‘µ!ÌøÀZ—¼#1¿y=gü»võã÷^åé C¢ECB‰°]—°nƒ?sDV„‚9ƒ€9м> ìZƒ/áçEaÄwŒ©…•@ÚXÂ"S”ì#J )^ŒÀ¾eÚ}¢½h›U›¬š¨™-™©'»o•.;gr*Äò¨/}Ïx)›J¶¦¢-(|Ö«¹´}qy[; _{ïbÔßù°xï™ö¦0¢®† ìïVæUã]ã]ë×”x1>øb\ÐH~ 3àŽ–Ï–pRü¡©ó²n vn uª ´­¶kviˆpDXŒbò αhÙ6X²[”óyv†Ðé€zI‚Øpá-3 ÜÔ4C*ÑT+‰kûagëÈ¿NJ§&Ç àÆŽ ¥æÅ ¥f¤š™Iõä¨Z5:#WJdw|Ú×Ûšº*ËÙ<À¶»¡iùo8èˆÜ2¡N°µ ½XÂH׿DŸž”›k£®¯ ¿”ìÓÍíŒæ"¾ ¼VèvEAœš@lÊ,°®%ZZža–5 ÷Ãçr†N¢ 2¬ˆe1´]±#"9Í—½Þކw8oOÔ½U*Ä#²æQAW 걬9õì'jÅ;ê‘·ÔzÄŸU~ßwòrþª ATÉ®àÇŸ”ÉŸ4ÁÁxü»Ê¢åÌÒ“ ®ßE—ï¤WÀ¢2Û "^–E‚h!®< Y–ïm˜ãÍDÆkF˜Ï:³Dkªðhâý{|@Câ‰êE±LUmÑä`ì¤ë0â !¾v6ÞòW»scÜØûìþD=ú±úÇ®Ñ+yçC˜E‰î[m VYê…ëSi‰¿}]<>Ô©nF)®‹éoX p›…”q¿Læ—¶c“BºV婨?~®–Ë{•Vv½]’æM,å,+ȨaÁýçúå㤕ðºÚnK¼ç•eAxfÝ\zi÷üõs1Þù#uáLQˆ¥ yFÆà#M¬ ™Ž†Â@›Ú0·R§ ¡î…Q¾û]9p¹?µÞãç‹TƒeØÁµ  %y²ž/K_1á0T—Š1¡u¨H„é¸S§V;—Œ¼^·ã›óïÿ(|r¯Qñà¢úÙuõÃ[¯À=ú_OKÈú°4Òéac*×A¿Âƒ~ÁÑä<Öß ½ m(ŽéèU]OÁö#f„òœ„ä`âà7muOøX‹tZᬷ1Ú|‰á‡]ÐZbPÃ{[ L@­ê.ÜÚv;B¢w‡ò7§’¼©íˬ/Œé)ÞðÉÅ“÷?®ºÛ#½ß§–¾£žùwç‘%ù±Ž•Á£|ÚB<]Û‚š†Ô,ð/ŒpÍóµËw·‚>⌅6ºŠzZWùØWxØ\p¦çYŸ‡Ô>–8è"y8!výè@3< T ?„±à·`-ˆ"ƒM6YgXZ@ìÓmu‘{Ó³t{£\“ÄÒ9±?¿rqâéŸsXBMÀ tƨ¥†¸–c EJÍÈó±‘iÙ¸\Üÿ¯Ï>*(¨Ž.ruØ{X®5bCÜzœ­ ‡´­úÔËÚÔD¾šRÙZðÕÄx ÷X÷–8^O²oßÒ ÈÞ;âxñî]‰îí±.uÁl„Íá”kôƒx9èŽyö:y6:9öÄÖ øoåP1XãQ¢~ù¢"JzŒ ÎÀIXFv"@ È- `^À"1¬®Ô#o®âmr¢"8Tó…TÉ SOzG¨å·>¯ÚtÔ4T.S7ÇÎ(ÃÉø´#9™@_¹Œ,¦>¶–ø `;¶É‘æg@ñsÖßÿµE)m,ƒ!H®k2/; fj&S ž,(ÿ:SR¼®U?o÷—¨Äx6]UÜP¿¡þãúƒ·ÊÛO¥4JªÙS¶=8k­Ç ªlw̽·/(ž´H™ú¡²m§WW*·5Ò²=À¢ÑM_èª'ô4áûš—û˜ùÏvÒ:ï€HªÈ[ Þ°[HÔàüš;ÙQ«­bËã‡ÂA¿¼tHU¤˜F8Þ_?'…j©‘@åtnDŽ­£Î€(Gý¯[ê/{¿m>“™äåk~Â^ç«ånÖf;6˜Y»‚úLìG Ö@r ¤ÀŽº?¿~d]…ÔŒ…`o®äåî o<ŸzC´óþÿ³´ÿ'Ñð#h[Å-3#WÀ¨T|X²C4O'ßI/‹C+u3‡¸éœ(.HSÂþ%ßÁ Ä?l½kSç­ rl ±€[﬽¯ðˆ~ F=¶F'ì /æíjŒ{Ö´xnÇÃm¾\6pmZÙ /P»G‡ŠÔs§úkwÄS;B “Ê£N¾ìSrfQCõËïÞ>ùûwƒ÷DЇ-3’ÞÉÑkÀ£Ôï A]ïF­2$4f~±²hˆ°yÁïÕØ³c Ž%o6Û(ÝR ¨†®Oú.Ô )'ð§&T¦¥.nL†=g€]3uÓ,h´ñGYL,§L Ìsm3 Û7¬áÒÙú™QTó\À²Î»Ácü€½„Ã)<‡/ûºÇü6.“IAä›/5„Œ–fâE©™zŽR£”NŠûÞ}ëNÛöí5‘m‘A!EÈé'““\‹S.Œ4ú!ÓLm ƒk´)XÍC˜¼Ÿ­{.Φ€¿Ð.VÙÁ¨Ê‹!ôç¸Ææ)òc”»9jU{š4[5‡ÚÔ°ð› œ¦0{¤-Àùù½Ön'í#"!l/Z'y„­eòSHä´qr5xjCÜ6C®Á#(Êì–,o¨‡ï4^mE­ô£î´í›ø³ø$"ÒÔª·›wf{ë°õÒA Eo×.ÃZ–9HÚ§ô©L3ÝkDɰÖÐ)ª¡hËÝï#{qLª-9`ÅÀ®®\²¡:ØŠâÀùÏ™ÄkäR —‰ ÕêËê¹Û¿ß“·öì $:…`!º_ê%7u<Ýh3ª|Ç‚_î\†5ôå{·•®bÔ-d4†µû7¸Ð*í)¡§ncˆe}$.UAf$¯'Š ›šó>T¶ÉÖ6{ód3¯ ~HUÓ?^<4Q¨œ¬&¼»'õ@}ITâdß7M{×bubJÝÞ’Ò¸:ö/s¯ŸÅBôÀL iŒ•:T’6p­pŒ<¾û${JÒ=>Üd¥Õut¸›5@dç»üý¬p «¿m>d¸Ì‰:ýªûge?jUª®ÌŽõbbz7ûT0•é­tW<çÍÁ¿uʉqy(\v—yÄÞ|7S{³µN›Zës [븭>8Æà\AÞUl“禰TAB!Eþ&(5õ¾U®Mþœ"X #8™f´Í¦´•zTŒ•·Ûÿ·o Q$—!ÿº\ЄîUÜ^º<«‹Å›>¿™ûÃ?ªüØúøÇ†1PÐï7ŒcÚRt+TØ·´\Ü£þF †dÔ‚¶k}YÕÞ,< œõ/¸áùˆKZ;A‹blMÁßÃr¤ÈŤÌ®5D[ÍÓ;é sº'ÝÍ*ÍÁ|¿µÁv¦ÎJs*ÆrÅÏ›äú͇"iÿ ŵQÈ=d¹¸|NÙ$ý£âvujWƢγKßîýâõœûw†»Ä P.;À{|Åh›zú–Zù&܇J“ p7â»™U:˜dáyêh”îbzØNg›9"H—Ë0o1 ’ºwÒ´"Æ„Œió5‚5 ÀW9jLK3ÓBñÁr jNž-ºf–µ)¦È»@1;.!×~¬BKÄ q1^F×]Ásü¬§cäÞOÊÁ~å¨}SÈåÖ( H©AW ÔR¾'Çf” •äéÝo¿éíªZ•Ò¶tÁ?6-ï‰uo°®`ƒ¼z†g“ám•h{ÎÇj¿tak!)2xaÕŽÁîî[4"#àQ§8&™ŽŒ4¶A† ?Ð ,Mâ4˦Š\õ…ô¦Nk»3šÓ›äts™ûå¥.Iö¹!fÙqœ pv* °Íô`žqcœrc½6'Àþœç>ÐËùö=x·p=Ý=ìNÚÒ²¸†iðtV!…6ÇÙî19®¸™ÇM±É‰ôÚïÁ‰6¢Š‡Þÿ©lgô ª–Ñðœkñ8CÇr·nâÉ¥g?4€º yÜ3;þÖ&kÙ%X%£¨Êe-RE †tP\ S›ý´4+@+׉Âg!r1+¶Ö-u0*s1€ôgŒÿ%\ýjwsìˆK ˹f…NFð½©ô·jŠãv-óíZî׸€[épÚ•™æÆÁ9íe“ä|Üßn­~ˆÅÓ¥v-÷øþÁÐÃùðÅ‘!ÂDâ<ö†C÷JÕÒžßÞË}_7÷´·ÿ÷F™ìÒˆªç‰´öÙvu“cõà•á ;>†ô@Ðàß8µÐ¸a ·À…â;é6óÌÊ­µ ìuòÝ Ó] Pî²] E!¶€|ëü8À^0"ÙZoÀ/™Vhft0@Ä‚ UíˆÄè^¡[ÆeÛ˜D¬!´=h æÈd¢ÁÜ ±}™öúX%xZúÐÓÜ‹Y&K=\>鹨xðû8ÃSSp§\ƒŠü7ϳ@ ˆŽÄ*ù´tàþ?ß½Zp679´.%üÖ†˜Ë ]®%Û÷$Zׄӫ¢ØÈŽ&: †–BÙ¹Ìt/s”P\º»q¹2u°Í‡ÅÓ:¤êè-£t–RZ«(ݵÚ:´ií¿f¡•xÎÝ‹Ý//Fˆ0ëJ2£o±ååE¦ MÚ—³*™ŸÕG~âžÅf²|ö;Æ¥²_–#¬ƒ$¹^V ´ã-~‰%øòߟugŲ6Ö3Êôég%0ÑRÉàqUý´£4ÂBäa^i£WmgTãdÆw¶¬vf@’ tb‹œ9•6–PB³Ò?`­‚ä”xÎÛ§dÏ®Ž\Wuˆ‡k•*¸¿6Ì>¿"•7 ÔCU„Ð:49¶&f¤~FB¾âI*—ŠÁ0÷mcÝ–ð¢ÅÞ‡CmÄ8¬öÒ‹¶¥¼Œ)W]ÊvÓ‘—ËwÜ»‚k^­þôNÓ‰ÅZ14jÖtcˆZPÕæ——9\ZÆnO2íZdqi «g ½k±QËBÃúåœüdÄÀ”š ×?ø [¹Ð{°mS”ãý!h’´ev>ŸÝ¸qί`fÄÓO˜Ÿ ¶9àNßËc®çè­dÑšRáÄÕ'ÁÚšâ”¶7àãwOüús¾LŒ‡,Qaþî4¿w0>@X‚‡o `aâ¶ f øâ¶Pj³?µ/‰ùÍ­ó#;1ꪔm3'üÁ?B½»3ºs÷Êb¯¾µÁÂxÜñ£íñ\A¢suœâh üèy¦ÅÞtŒ'èɉs”…®<¹0n@ë—ª­ƒäµR™tÖio嘕/©\{.ÚóLœÇV3¥¬©º Ë~þ6oFuåÙsª‹00W=«Ã[¡93ÃÁÿ—\TI;pQA‰‰IÞ_C£ÂðemÏ¥=êþÞïÊ·Ÿq¤*] s¨Rsíj–q‘¥n6Œ=(fK!€žïiÌw3(9• RkäÛæ»é—±š“y])HÑ$ ù„yæGùçÇä$øŽrÛÈ^çn°„KyùÙøË'0¯MaHª §aø†y\Ú|IsˆSžh(ƒ¸Þ ¦“)d²:ÔOÐDÑpª‘ñ¨V¸Õ¾}Mcˆþå`Ónw½.ðCÂX7’¸}K\[bص¡f­‘ŒŽpFS€Q'­ÆG[à¥Íw×xŠó¥œ”š‰¡ê la`8Ï'/ªŸ´&YÃ-w¹CÿgŲAâ §ž»‚½2è[}ØÕŽúÕΆÕ.&Xñð]è¨65ÎV5ÎìK‘~géÚ{Œ‰uR8ü»âYŸ\Ëž”Ü'gZÞuÒìD÷“‡ˆlk"¢ƒ†QE#Ô…$rw¯$¼À\]Ò‡:凕¢Ô€ìHÇNºè‘B9T4J5:ô²KɉÄ>áÎ_?åOŠßTO¼§žý¬èH |´Âµ¨ØÉ DT›éD "ô›âõÚ“ô.§˜]Iat-2¹¶Ò²3Ťf J±qŠ#µÌ›ºÞòÚ³? ŸI{ý_1Rj Î&ìµgM²/ò²SY™)œ“q¬½>&©èdTDë,jK0ûXªwÁ‘„æªoÞ<ñ¯ÏÏßûýÂÐr½[00Bˆ×Ü0ÁÀ]Iì@Å¢ùRƒH©çxª7§¥šo  Öò¨;5{†~h‚­ñИƒZÒœ©Õ¶ØzÅ<ý’ ³L/­æ$§ÆDQœMe«$ÌηÕစm°>¨ñeƒ|RîcwÁÏå Ïf§µÉz Õ†f‹uLPq”vøœXõjQqúT¨é~öë\-b̨…ÖÔb;j‘µ9Æôf÷®?ÿ¨œ»bù˜ÀKÞ„™á¦Yqó¬¸qŽaÒÆIi3žxˆž YµTÎ’UÊë­몾{¢(5¹çu©"S­"3Ýl#ê;ƒÞæK êÎÌ,ì&ž«•C¿½ÞS¹eiz47;˜‘ã£/7ãv$Ø·Å;4ÇØ ÃY•tläcœ›ÜEÑŸèA§¤'ü¢Ô â5ÂÝý>ºì¼~îJ;ÁØêH¤–{j¿¶Æ%ëpT§`óçof<ý¾VùøŠjð&4ÏU¯ô-ö¡|õ4¾ Ñ^Á«Uø –øSõñ´++ÍßÚbûîVç×7Ø]]ÉìZeUžly,\?ÑŠ:¼Æå«wò‡µ½(5dÑ¥Fˆv‹,_fo|ÖµõÈRƒ—|©ÕÎi\`J­uÒN[à^òJ\oÁ«_^Éúý³ê{Mðî›P^Æ7¯R5£3Ç•ŒÕø3xÈÎÒ˜-¡Ft)„_rŒ›íiÍÕu¶ÊÔïeŽ,lˆ’ éùœóQœ3aÌ´ ‹ãf(íåìt7]ï¨ Z`8,GèTj€þ‰—=ËÏ$]m~õ‡Oó”CíÈÝ€ 8š« ©ËÍ÷‰oõÊŽfвS ‚(RéqðzZV«žl™¸{¾v³ý%zǽ› Lû‚õ.h·úÓPRD!ÚåþTUÕkØoÒiÐeÒGïˆa6†š×˜×Òký™Bo߃.ô²ª÷áÔû°|9 ¾VBË Gã2[ãJ‹R' [€ÿ@,Æ5Æúû<²™´äsa•†½6‹¬z3ÎñLಓ«µÆ†ZeC¥XÊq’ù ×úd{jg¬UöÖ`ѹ”kµ»?=ãî—eƒàvØ57Ñ‹\<‚ôŽÖ‘"®A™“ƒ¯9è'ñΓǯ^^ÏÈ‘>Ö&ýúLëAû®mÖ=Ë̯/´ì6¾i|9Þ¼3ÞL©WL•B`­_¥_¬]á¯]i‰^{œCs”}C˜0‹oV…»„!è[@S/åšã@!Rhk¿Äº>ô#§èÚxƒ)t”AthÖŽZg/Ëpùfkã8Z²‡û[—û†î?—Há¯7;>N–Üðú$õ… 5xAþÃ/À­×„J1öìÏ¿~%o[ê¶ ûÍ.F;¹:'ýMºj!ì‡B6ñYžÂ2Ž8Pm©ýÖ$!h{ÙÔA;ݽÖÚˆVŒˆ´£øpÒœtιdò |ŒÊƒñ&X´/´ê[is}5çÆjÆõÕf7V÷.7¹¶Æñbª[ÛFߣáFèjÊÅ?øV0&¾D&k "ÿ§Ô(ú!'¬‘c EJMßý³‹–9p×ê8²àr§íX@ åËæ­ˆåmF…ÛPKüŒw­pÍÚ×]½ãƒ«gþ¬òé¯MŠ\!òAä´«FÛÑs"ú £¤`]Ž>«˜U ™A‘p‡fZ!ÉBUâE­¦„¬I ™ª¬K¥¸ª–¿¥–~°Þ¶ŒHáp=`k¬Ù‘;Ú_q}VcS=;¤IÇÌŽºÑ_ób$˜’‡T )‰ ìS{ꥳ̭Âô%ït¹ûqéÓŸêÅ÷ÛFû»”’K#Ãí#’V©¤I< _V€l µÒŽ"£†¾N¶H ”ÔÎh™Í‡xÿñS@$…Ï‚¨rFE³(5ãm÷?8Ü~Ä£ëUn×JNßöÕV×’mZcèuÑæ†Åáú1&µI̦d«ÆfUˆiS¼=2ÚÚ“Ý!Zå,oÖåpêV ï,Âæ2¦àÞƒe÷icƒãzzû´i{th0ú€¯>Ì·AKž7G…ß>špŒ-‰:Ú‘Ú4?2'/þñÓÏÅÉ‘À2.ƒ›Ä ñÿœ/5šš3_j R€ô{vR¥ž›œ“<ýùÝko së¬ËY\±>\¸&´~©_3 ¯%¾Èjo^ê۸ة+¢¼ú…ž¢…^uÉ^÷,vCÆk~}i¸cžäZÆ80·/ñ·¸àg‚Hȸb™ÉœÞÛk+mo¬²¹™Ê~}çÎKW×ðê9U¥¸näR’e¤<ù Ü@ùÈ­Dè"u5¸fÐHàýŸUÔ+a`.©~.ïø uÛÁ@ýå&TÏ™eý_–ÏŒuBj ¹wGZâNêPçôµÒ´hûáNÑlÖhZÛ(üR¶P´×LÌ÷YZ¤‚ðo©W´"¾bë²lplö{¤E1ÆZ¬õ :²Š¿­˜ìo™Bô_ ê ¸…¸tG”BùljÕÕî¼¥ù/»ïO°¸-|uäÏΡ§mÒ‘¥¬w£"˜jB³½ƒz°UÝ1-Ñ(w¡uš¯iQ¸]a[e_ÃF)¾¼Òº/•ƒsuõõuŽ×^âu­ölZí“̓t}Á»]iÄáSÒ‹÷ç?¥x¾ÔÌ?¾§ÇZî~ŸŽçû£Ÿ‹‡ÿ©ú;'žuF2þ¸kêY÷Ìp˜½øY0Y@ž 4Ì"°ÎÆR;¾)€jöæXmK‘O$Då™°’j3JLžÕs7Åß”mÓ{É›†®æÙW@Y»rÖ€ýÔƒžÃ-«x·Ö‡}°%ñƆÐËëü›“¹- ]Ú—ºµ¥ðÚ—»ãEëB—¶D—ÖhçöX^×ÿŽÅ¡Õq~$îäZ¬w1  SQ®Ôšƾ—¼óŽ%µUm{ÿFúÿ¬èÿ£Uü¨S>ÜM*¶¸÷,޾Ç#M0<„ÓF¿9 >ÒdÀÆ•ƒm2ÖЬiÞäÃRqÏâÿ‘cË6Ú„Ý)5Š7Õ#Ä‚¶0¼å1oÂAe¼‰®‡sÐÁ8dº«MÏ&ÇÓñB[Q”×yàÒÞI6T¬#õr"çèæ€¬}1…ß»têÙuC÷¥Z$Z‡5IûÛÆd=3S×yc¤eXR?,/A=¡Á+ˆ;ÊË|ùŸRƒR?L* &,œùdz‚å×DïW—v ¶»–/öePî­Sá®SåipÁHü²¼t³}õ©VjQfp5Ç]·ÀÛ0ÏÃ0ÃYÆï°Ë€/Ú6Pž`ñÔ ä«Éí:n¨û𯙠BN˜RbNAíE…yY—öЉñË&&ë--V±˜áúº^ZT0‹!ÈÍEK£—Ž q™SbÓôb€ú«½™_CÁP>6ÓÓ“ãÊ‘…xôþO¾|ïú¢­KΦŸŽvnXuT$@ò†Óå\‹p™P;Aˆ]mˆ=¬;ákÁ7ó ?„N¥Þôª6ü<ÛâÜpÚ㉶â£í ï¾qkµoïRüÒ®%–Qf\hXdNéOÌOd à©-¹ß-ìùO©ùÛŠ”,NbQˆ®^-ë¼^µúU¸?éRW²VÉ~©ÇÒsnªC=~¥t“cu§škYÊ6ÊµÔÆjä¥c–Ú°D>d©·ßL~nçœØ™nÖ‡íMŽ{1 ú”¤†­ ¨N +_˜»Ø}˜ù+ªñÌùÏ"µ¤g òç¿•Rm4¥ft²yrâ¢ZÙW¼×+}CÚrëö—?é@&‘TÒ€j€'¬ZÞ³&¢ŒFKšß×µ%)Nµ©þ%QÎM ëc¼z~°-é­Ma7Öz]^îܱЦ1ŽÁ'Q­æçýéǼÌ69RÁºÔ™¾?~X&yÜ¥’RŒïu`žºLðsÍCºìþ‡%JŒ ˆäÃMH®y> /˜vŽtNÈ:@© ÙbCUE@È·«§”Âiìõ†KCe˜2ÆFH®ß¤®YäŽÀŒ¶°ãÆ.Í>l=¼½3Ì<ûåÀÞ*¯{pÎÏêñK3Ÿ—7¬rDÚ4Å:7%¹ˆìQgkãìðºa¡SS2”ìëÃmšÔîØí&ŒrÏö³ÝeoœÊ¤ÁðåfúáÈËM{ÿùVίÿæ=)±w|´W.½8ÜßXfDÚHr<—M¶ŽÃ]„ø”bô¥yZÕ€ ÞÈcàGÍ«bùr%9ȃŸ¯-“—Èå@†GõãøP”}êÑÛØ©Žaâéõ¡ågXŸõ·Ê s¼έŠvÄúˆâ}“ƒ›RÂ`R•ähzc”YÆk¡Øïôû×Çùî5ŒK¯ <¨Eï ÒÎìø€Õʼnëú¸H°Ù„5d¹{I0:–Jâ40p{#†oRr Âã3Eò8lòÑ PóbR.Bb)€ÄÛ%ëNÅ™ïv¢öZjl÷@NFlRÅY˜XuŽ; K(ÍA÷Œƒ^º£PL°§›O0AFÞMâçP[àP ò^޽Ü× œ˜%7â0‚ ×ü7|=æd¾ÏÎèL í‰`D—'bN„;îógïô3ßàN‹eR­YKe¿6 E,šÿ”ôáóz8LZyõµE†›ƒ¨½ L¿{+C-¿Ñ%œ¦&‡PaPƒ¹©…¸x!Åàɵgí粂Uq¼|oka¨{…§=´Ã…pÃ4ŠDH =¢;®Š4|%k™GQ¢ôåÏ~i–<íD7ŽÝ.)5„ºL®LM©1@03Ö0¥h˜T4ƒk=T;ü þ¬¶aÕ‚ÞȶLMÑwŒñ'U¥¤Z>P¡®œA3S)¸€j3ƒ¼9”—7›¿•ȬZ¡¿ÈXëv Ž}d‰óW×3'彃šŽ‚ÕÓ¡þNTœÀ:Ì"1Üç½ §0Èó2€áÒY*Ó]'<ÞúX´!)< âàq‰9`ɉ…N¥8Q7÷|ÿy1h±Á‰·"ôX"+e­¨0Øü¢¼@É>*ãËÆHXTÿ¨0ýÀüAÙD< p¼IUF}ÿ¢ÎhJ qüÀ ™¼T®@©©D©ùÆL´ÐØŒÜÙe”ÇÞmGñ0º˜áN?ënYåZ`{!С$б,ع&Ƴ:Á;=Ôöw]Ôç÷„~òzúƒß†úÛåòË*å%ŒHcòØ€¡'×a¬Ãv’a8`#U ®a”z4xV’'…¦¶¼¨0hf€`»è‹«ÿ§Ô(p-µÀÛ¶ùPüf{r×€’„Ø p¥#BTÜ\,£öÆ`Õž°1@xe–£qO¿ÐS¿ØË¨ÌrÐ'Úƒr«jˆ/ ¸æ•^œÖH¯ËI!—÷$_IíŠó­ v.÷±.²/ ç†9æ€×âtÆ×æd€ÓwÎ.3ÉÚ»û5±>]µ%Ã~yÖ¯’H§ÇÆJ3;39¡úŸRFÆ& ¥fþ('Ƨ¦‘³ DµùóûG_½¹òükñÞ;õO¹âٚȭ=ÝÃÙ¦G™ &uCÎ~P²Aƒ‡BßQûœ•L„ʹœ ž5Œ2À‘ƒå‘±ÿuæ{¶ù¯¯ê]âàWRèjVûüëv‘âÙµ±Ñ^Pø^”(©ÿË«!Û¸Ã7SO]«?•µÊV]9«ýFCM$ËRÒYºÎ®{…k¨{¥‡c‰'7×Ó9ÝÓማÝ.æ&–ÙZ3ýUZ;í¬¶r,V™h¯·1I4¡Öṉ̃ìW?•k€]/hB!º7j·ATbáùÌÃÂaÂh±pð„š‘«¹u`±ñ–`êðRæog@n9‡¾dGúFûsдšÿݹó¨¿~–£0ЮÈÏ.ß•sŽe º&\Ž1ßíÒ(†vÂ* |ô´Ðçê‘øÝMN&ž•ìL]«Ûóì7ä^õ 6Â<ç¯RC.WRjÈJ€‹v¨*²DÖô3‡FOˆG«ñ|'zpEõÔd#œágeMX¼¢QŸƒñÅHµl¸„hç5‹-M©àŒÁ {X¼óÍê),é®®ô °yOu§ýöAÙð“fÄOã¶…å×ðû¹¢2ˆhTа=q††Ë¸0À4×IÐ:çyÚYHͳ"zL\(5§¶3©õL¢28³Æ÷ç áM15ÚM–J‚í@ &Ðw(yÃÿkâA8Er2÷áöÔð «+£9å©Qº¥:%æ&UÁÖFùàAI$|X³B̫š{ìMuÿmégÍël©íÖÚ8éúˆüÀCBK"ý>m…¥.$N¸—!0\®±ò^åAÝjÜ÷ì^«\P®od´S!o5›JRLÈ!p4Ð4†{ÄPh58“àJEà^ªBüóß/QK±+Ç^£M-ïœû6÷Ý¢¸cÑTú2ýc´ÓÞ†§aÞN'f˨-ûp»Á³Å‚†Õê9¶NC 0‰çcœ³³8Ëå e=á‚þêÕdÎgo¤ƒh:¡ºDº,©ÿ"þ·ÔÈøSâšéá$Éâb&› xéÈD#rÑïóŸŠ+”“MÓêvådà´rPR^Å@-È'ƒµŠ~>ðv 4pP™RÖhJ Š<ž°•ÒD5Ã\küQËR*p¨9õÏž“Ïî ŸÖHF5Ý]ÿ¥¢ÅÈDvRwÒ‘¬œðä ºÓÔhl¹T¡»>X7ÕÞPëç¸AIzÐÕd+×`ò¬w!f%_ö´q0Ø\«¤MÙ€ô¢­ÒŒ‡>'1ñ˜÷ñ€•G•æ¹OŒ¼pÈ^r”O|d5OÈe‚çÓ­pw—U <) òôx+Åø¡Áîl|Qïo«‡ßWu¡.ÑJšŽ•(.]@à'øÚÔ¿ ëÄFFj«m¨­á&ïw~ЊÉòìà4»¼—J"çõ¹(þ#¹‘úYxþHj§Å"|:8ÓâºI‰€¼B)œov-WJáˤ¬Å÷üäA>d¼h)Ubþè³2å`¥Å\Ù®¦x©mÇÆ€æž(Ú®ÐÕPF™'Äõ†9Ô'yu¯ïMì‚q/äó7Wú]_áqm¯w±K÷‡Ž8›–(û×{Ý\êws±?tBoÖyÑ)à~‰~vÜ5xШÁê8ù*µTsãàf‰c1DgO~zó*ZùÐÀ¤B7©3åÖY¾€…Iå™ ˆó§¦Î Ô`˜‚P„1jR:„…ÔŸ_ÿóŸí5•‹ýÑöeÌ ¾f%8ÞÆˆ -æéÁª·ÌÓ°ÜÓ_‘€\îe\ígŽ‚É÷§#Ý»ÂÃŒnœro³j::78í@Q'4ÓHJ»›Å_›òPç²pÄŸ¶ÙÀ38¾Æû×Ï*éu¥¼·Þü¿•2=aK8­ì?ácu¢žîûð2³—ƒh/‡Üîþ­VxÅÌý&:®“ïe~†œS<³=é Ðœög§XŸ´=`“Ÿà}Ô‡ïžË]jAí gîe—íKúá&ÿû×Eç¶GoJbØÄÚRü¤qnâ2ù~4knrÍ?ª4ºKµ ´öŽñŸ»bh›ü©ÂmÃ_W«Gúr]s̳h½YEªYùbÃŒ0ªx‘yI2£2Úª*Ôª<ÀRãRnXª¯¹Àß²Â×y²éNÚ'mQ$µÑRìÏ.ŽpÉLðÛd‹­Í+Þßý3Of ìfï»ûE%šP¦`ö²7¦@“\1ÿñiJ Lö æ<!6¦­`"rwÇ"ö÷oæOƒÊ2‚œµž¹Ÿø§BtPjŠ­Néž„¶¡î#½Fº; u·ëénÕÑ~E[ /¶èkAh³>*n¯ Õ~¬üò:ÿ(6˜nèçOÅ=¾+Äß)"G¨3ÿ·Ô€dã/‹O¦²×xQÅ{¤ß ½3úUùñÝÊ%tQ‚eM ‘ À ¾ôx¯ø¡Œ²`c¼?eþÚU!Ú¢]Q´W É–¢$Ëêx‹òËÒFe»&Ù¾r©û‰Dh•i|¥é•ÁgùD­X^®œ"…†³G´3`î@Y X :sÍÅŒæð‹f¶B'_û|¼]Çœ¢“˜o€´6ûŽz0ÅÕÙ'½ý_ ¾Ž=j…źjP‚Iß®)5€æK«¦¬‘RƒArr¸ó݆½ÛBŒb`òs âÏ_*†¤uÒ1’¡GÐîc±yáŒÂ ‹ÊH«šUÍnˆ$mƒ À Œ>Q }µ Ì"ø€è dsì1ÑÞaLÛKL¸<émãËú¦ÑV&éë'3é·*K‡¾ÿZü篊Qì¯Q[ÐÉ ß_5˜ðK þ„´5èqàÆ7ïcƒ‚3ƒ]Õ,–VbñxÿÓï¾ý×¥–ú-ñÉöÙ!¦ÙÆ9þàIj²G‡OeºÐNŽ´úb?KˆèKXÅÞ–¹H?ñ2ú— 1¾U [i\kVoŽSo†:Se^iYÍ,¶*‹±-ŽqÊŽ°ÛæªeF•Žÿãk¾BÜ…u V<ÿSj@›%×9r™Çåm${h¤uî·ºôu¶¯Ä›¾ºÔúË[YcH*¡½¢þY@Ëq7MgçZ[e2X'LL½fd¼ÏÔâ ã0uÎÁå€%ã%жÉÄ`§{]ÃâëÿZ­ø)gBŠ¿~¼õzû^éÆYUn"\<ÄU˜²sŸ¿õÐQÀ×°ÌÇÝ»÷&­ô¦Ï-TýÖ©–ÜB6ýù¥–…ÄA½ÒE¯Á‡…äå|{#Í»dåJË…å‹¿?BOc$ˆ3®Œ5-‹3C‘)ŽfF3 "Y8™Ñ¶›} ´í[ïòÑ›Çûû+$c•ý£ùbE ÞÌqø@’Ñh|^X’*«•C•xž&gVÖ4fz°‰¤ÝI`QxG-¾3}¯»ÿSÁ…}Ñç¶ú]®Ú$ù¥á¹¸gFÚ1FÜàüè¤Q¹’š¦7 ½¿+-õÔÍÑoO/ç®ó åï üýÇ’±ééD-°ê±Ÿ ¶e…˜[øÃ È{¨Lw`z…æÄ„ßC?ËžJ·¢ ´áÏÝe^0稿%Ô¡f”(keÿ¤XPI`‹­@(›a6ã&EO3½Î?øÈÑ”šy’!Ø›dœi@IÔ3ÖŸa¯ªU¯«‡ú¦~iý¼F¸+&=Ùm¯c§›Å^7öÉ@Ó>ûv±8ÛÌé[Œv˜™bY¦Ù²NÙ1NØšŸ÷sÊ r9ÈÍðs>ççtÌÏþ7³¥vTæö°?.Sà$G˜i+"$p)$eè6ÉSR Ï/˜P¯)8h2Qÿ ÍWEô3Hïš»¶Œ,ˆéôôëjõ;ð~þ°Mý óCáæÜµöÙˬD[|š¶ð׸–.¶¹mÚ‘êze½Om¼uI€)Æíƒ ê‡Úg‰o$ÍYûœ B® Ò9‡„wXI€¡G²2©,W-poò½ôq`ô‘ã¢Ö–†|d ä8ÒÏ; z‰‘ao}‚ÃÚO7Ûan¸ÍÌh»¥Én¦å^+Ö![ûFæ)Z†‰Ú ,YMgÎ}{çÁ_•K†T*%ŠÌüAÍù¿¥<áùRCf&¢÷&c–zjfjT¦{üø‡Þ¿”w.;Ž›åg˜íkPÉÄÙBØBÍÁ¬ <à †N£Y [`L ×Õ“oMßm~‡¿íä2û•& å‰PŒ-Þ0÷\Ÿ–táï”>C©Á}M(p8/5êç½­ûåÎùx&µ¶uk¿øÇ釨ã"©„ã—}§Ó¼´ ƒÌ2ܵЂLs¼Ò]õ2àhÁ°ª c¡¥ç'Ð9´ËƒØ5Iǃ™/»ë-ãéô w ?®—Á0g´cŘ’äÇá¯E 7ná¿0ùùýÑ4á Úš¡ý:‹!Ð8!ùÇhC¬]Ô漢z>ë>µÉéÿ£ë+À³º²µw¾¸»»» qwwO°@)î$îîî.@p)m§:íÔ¨ BHBÜ…Àùß:sçþsgžý¤ %99gµÞõŠŸ±†_¥o”8 á#œ”øŠÅ Tä¸B¹xY~l$½û6 î’œÔm†“øÃ‰ø±“0x§Hq¸ SýZÆ÷Ÿ¿À² &„®±›^›-^œ.À+rzS™‹‚C‹ÉÛõFשÍî ‚²ér*7›«†j›Zß#„tõ*³:ÄÌc Xù­î“ÖwËXtœvo;ìܾױ{c2UÃktë ª¼µ‹QLð&-±VAÞSáu,ž.)œ×—ƒ©Ô!“J"iÚ2ÙÚr©êâÈúI7•̳‘‡¸¾Ú]³ÖS»ÆS§Ö¡QògTy÷ˆøRÂû!\Ðíàeƒ°ˆEq`àäË&ŒëÃb"으~‚"v|‚û½½ëR.ÿñÕ/GžÍÎNýlšÚýo¥æ¯yê5 µ ü¶«¡@Íõ”€0sczzùéŸs¾}¯¾êR°k–£:‡ï#KŽá#4õr4µá¬ Mßaø:ÂLTÄ9!ë[~*âF# ›Ö­¼´ÍÃðˆíg‰ç%±ø-ÁNÓÏÃ@aQßÂÁQƒ£Ë%ï§¿V,Àœ s¼pŸÏÐ3W²ÙHÐR—ÕÙÆ¥—5Ìrÿú÷{] È»AJ´›ƒæmF7óSíKžlSÙtMé$>’$Èž$Æ$Á¿UŒ/R€ÝŸn§‡ò€Ïà •àt!ò¬0 ÉÇï7üy§©Ü6âäâvã/¯ŸdûðèÑþü¯Róv†¢] Öî´Ô¬](ŽHtà€·ø¬çê“nféáߊ㛑"Ô.Nž°J;xŸ‹ì`må¦{ºšAî§Ù'EÙØ¢Âs,_ â ~,â É!¶c`¿‹‘ê¬Ðç¿—>Ξ^ÈŸ\ÈN›Y, öm¹ÃWˆj¦Žëèç7–u.Øã´ÃýÌŸ-UnKPØo#tÎKå²¿Þ G•&â±ÆÂæ ñª=º“1ó[ÃäUp~³Úò¶Ôlb5-¡0Ôà¯~œÇ0·˜ç=Û¬¹£ôÉ!?éÏœXZkƒFÔšÅ>櫊d3¾Sáýb´ûMÕáG‚'ø`G¥ øÛ§eáÎç‚RCµ âä]°Ü |x‚ yvŸš®•Ñx›F5m@Ù‹+u”È„j³) ¡¸Ù_êWšçŽÇ™vAPÍ5 I@ð¸—þÑ8{—þa/Ñä ¥¬­s²Œù¶ËRu^’$×^9á!î.¶0N6¼tp|9I¸ç69¡=*¢0»Þ£,°_Uèyþ$>¨A«´Ô×ÚwÙ³Õ"lz ,ì¬ Öç‘+Z¼Iì¬9_áf%C÷ ê!ïP9ñÑtÂùÚÙ…:º0¥‘îý#¤—´Œ6$*$N—ì0 ÔåXY.B{4òh`ÅNlTé“%F1aZÃC«A0µÝ@}ˆâ éºˆkÁvn0ir (øÚ|8 Ñà>©š ÷Emñ c…lµ Õ ò§•¤vò±vqìãÞ!Å›(Á ²ÍÝþûÔÓƒ“;JRÊ…Ÿ?PS}—·û@må‹§''_.//¾-5hiþÕÕ¼bà0ü¯Cqpû ‘‚¼¶8»<üÇêÿ»ÝwýÌ»©všY¦RÉ*l¨0§äÈ%u`¸9å›7¸¨5ºk·x´ùµx5yÔ¹êÕ8ëÔºè¨Ï7—Ï·T¬p¢Å³ÚU«ÜQ­ ôQÝ&HÜujÜt vð2¨òÐN³–* 2ðV`3†ÿ^}ÂÏäÏ,4.-¶lº‹Ô£ÈÌÏ#…vò›íhåâxÑ›ùº• Ä^ÿ~ ùˆ«Œ‹IK4|ü÷܉á*¼Ñ@g]þ,«Ù.øWÄy ͹é#ìÃF\APŽƒEwßþ°ÐDÑ ~‚ùS!ꬖSQ”Ÿ Ôæ3$EGG¿.\oX™#Ì+úV}{«oÖ=ºôÁÓMw˃ǃä¢Ì8]ŒÉÕ¶ÃkÃHÁü¡bÛ½CŽ-Á:Í~zÝæ¸JunšÕîÚ•›:ŽtK¥,kåÍ /Ýr'Å{ùZ ¼t ú¨ð6­±+ô³>î¬g.EüíDž[íšzFS™æg f _•=O›.ØØ€BÎÆMK¿Å"t[ðçÚðBßø¬àîYß“F\Gµ¸.˜JžÐÝ)Ï™ Ï.Åæ y qà%ŽÂ$Tëï½És¿C³?ȵ(Ûö-\C—>'¡rN”šÉ‘2ˆÇÉxjÁ’‚ìuøúî™gOJæ^wŽàŒÔ0?TeÙ!òà‚ 9'I£ZÏC…Œ1²±•œ4—b4 Ž"Ñ&ÙÜð Þ´hÀ…ÂECµÁÕCYöàb³ãc·å‰tÚòðÆÐôÔÄòÒÂúÚ Š ø{ÐTn Àë¯@¢¡N|´ÛAcó¶ÔÙYX˜›žýù«§7º¦nqê^l.©ÉsN–Îä+¤®€í‰¡_!†Ä}’ÔO•‚Þˆ¤¡Wˆn߯KÕpvðS«Uü½âôs`k8ød¢-ñf¢–¹{È!Acn¢-IZ;¶þ2œ7³Ð°¼Dc˜–ᔋ™ež ¨6› [Ùòx>3_³øwíÞÏ×Ò/økÇ#(ä„û¯Ÿ¦Ž>+¦©ŽÀjU0ãx~NM$]OÔ&´—g åÈ6=Þ£‘Â9cåd¥cZbÇtDϘËî3Ük,˜lr-moŒ™dŒ•Ðõê¤ÙߪVFk¨aËÐÑMM7}ŸÒ~/}<€X‹0+@Y{ùÉÅXðù;Š>ºÄ¼¼Š¢4î“d§%#¡oÙ-Nv‚Û&G¶‹‘8ÆEBqç “m’d—A û6¸§â„Ÿ;.‹(I&!"û_ ²-Lë²WÇú–žc]7÷²£åètæÂ›ª…WU¸{±³`;h@¼/à#1R9ö·ó·²ýJc5 üTªõ›C¬z¢]›ƒ2íôêI'(óy 'Aø‘r&HÉ’P-öšŽMýT¿4Öƒ) «´¥œ{»YþW©yÛËÕ‚ô‚™ñX¸Ä!Wþ(òaã.fnph<â/5 wÕù(À§ŽÆÎ9ªBéʼ0gÀ-tŸ2¹¬Bà2{d×â@ð• ÆèxÝ%¿ý0{æq5L’¡ÐGK€`j®þÒègf&J0(! †ÆQ1}(Dëëmp‹ÂŠmc¥nj¾êålÕÈ „ßõ3+ïW$˜<8Òëg§rV’B´ÅSÔEaKŽ›!Å@þ’¡|²,,OÈ×—9j${n‹ÆY+õsjgÍ•“Í”±Û½l¡–i¡VïjXd©R°E묡â!…]Fr~Ü€òºkvþùK)rEaŽ7;‹a¶pc±dj4MøÊ «W'kÞ™h¥†Ïãà3×¢Ô¼˜ÈŸÃöp©ÌgØy¬þ2?ê„:Ù~ uí!‘U»Dèc‚8ø­­ðêÁ˜÷9^²-¢2Ù¯ˆÆŒÄÃî[” ø2QŒl#Ñ$3æøab¸À-'E.èñd™ä[ˆåš‰QY¢‰d¥ƒz«i¨C³§y¡…ZªŽTš®t†žüyQ3tå¶…ësÆHúˆ‘ÄaCñã¸P𤺨)—¦¯~FKé žj¢¦œ·‚p„Ñ'÷nÍÍNÏÏÍà¼miðqs'ò̫ר.©6§*àÂÖ_½^ZZZœ|9úýWŸwÖ·Ù™ëiùd–8MžÒãË‹FŸ?ÚI#|!¸Nµùë´è¶ÒÓ@O“¿Nka£Ÿn­—f•»Zµ‡z½vK AG¨IÌ–¾h+à6í!†aƽQæø¼9H¯ÚO£4Ì8XK0ÈNê“O/?Ÿ®Ä:ê×ÿVj¤ s~¼b¢Žì·•Ÿªü]ëçßæOWaWÖÜògyûtÒQOÈq‚T“¢%|^Kø´Žð)}ñS’'õ%NêÁ*Y(ÅDÄ)m¡ ;¥dÙ#6R'¼õ=ÕX'# ~¸› ®Ú‹*Ðn7ÝBþ£ÔÐå2õÌa&Ú÷ºŠ†èsDxÉ}ú ‡™¿ ¶ÈÍ õþªÁºÍþ†¸ A:±&!ú5ze¾šEÞjÅ>êåšõ¡z­áúáz}Ñ=Fm¡†á5Q¶YþfûÔlTIÚI¯gßáìÝxÙòš2[r—çJGÇ3V^Õ Ø5an.þ^öæ·ÚágzméH2©׸è$~ؔ瘥H†¯NU‚4ñº΢ĊŸX KhŒùÒmK¶º&è𨂹íå÷UK€87Wõ–šÍ¿«ge¶‘Y½Ò|ѹ Rs¿1ù ?Žy‚1 êŒ>f´“ù¼¤ÞW­ÄDzñcö܋ʹeK㥯—ë–ëW[§à †/ãio†b©—&¨XמTã“ð¼Dä¸áW‰\ZÜ!·ÑNÖ=§/–ञØqQ¼tNlò¯.IC>œ½E¹ÀZ¹ÂVµÆ Žz¹Ž鮦Gí´µy‚-®µîþhR3ºÊÕ¤f ž ôõºÅ‰dÊ3˜Œ`ƒ³rEøõ8]„ÑåÝ|í‹—yp@©™®}³Ò mÝÕÛúâõëüüT; ý´j}4ð¼ôF[u†[4‡˜´†™tD[´…›Öé×øéÕו{ê`{[éeTãk^écÞâØâÐìÔêÒîÖâ\íkSînœ¢/ /»JG¥B+™‹Z<çT¹°»¯t€ôL‘¸©:’)µ9/Éj"Yú²ŸÅOmóyDÊð)C‘3"çuD.¨‹\RË2P¾€DZ íÓ¶úqz²Ñ|)ÍòøwÔ˜ïýURè *Î:ÎÛ}÷æþéõëµõW+«0ë[|öç·×ò·Gî…«žŽÔe-Ñ· ö rÓ“2ÿeu’¡Ç™kÌwI›\Ô&É:ä¢Û%CöTž s~ðFÒÌøÒÍù¡-½`ÀqZ‹œÒ$çtÙ/r^6æ=§Ë†=jøY‚ß=¯Ç:ƒ Á–¢àúÛˆ~ó]öÄRPµ¥ùæÿRjÊ×&JÀ¢G©y=Ñ{«xÇV#.Ox8Gk=þ"sr´‚–š¥N滊d[ÁGÅ ¹ mþuîd ®‹z‚ð)Mµºh,q^G輆~7ß\sëQ5¶cº\ïj’h%b/L¬%Hî~çѯ*–žÕS:ê(VóW©Á'u5oKÍ«Nf´íAoUé-ÿÉýlfñ3Ö{Ùš-Í”\6aO›È€’«“ØÓ­ùR,y/Yñ&[ðž2á8ªOŽè!o‘$“tsö#\+rÇ\à˜…hŒ;¨&n¬öª¤¹?»ÖǺ˜é¶×Óµ³#y`¿^¯‡'&×PñL¶ŸOP<"µÏŠã¤ç~²oF²]“=P†XrSgcC~Úö›Êö~Êß±é+Kù¨úzꎭz<®Ò¤ö¼çðWùˆ Ò­ÅÚÿ«ÔPœóÙH5Âz=TʆZ:K“ï w1S÷×~¨m¸à­Gvjí\D<àI@×EÈA1¬ŒÅ%r[àh—®Ë‘©Çƒ˜Ñ“*ä¨:눱ÀvnsQrr§þÏ_üò(süy ²Î_¯5a8š|†v®”ƒð\/330óeÞõ’Ðã‘R7ã^S£Qþ'A"ZïX£¾ÊWf?.Ú¡JöÈФ•dmËú"ÉÚBçTy¡2ƒO <ÉÓÔ8/«q¥iñeŠešH¦ˆ'ë §šÊ^6‘¼lˆÏϨS‡^䆯ŽÙ‹&“—`Ö$^¢Kùð-‚ß}½ð²ò`2ØMŽä½ÊëµçÅ‹ȼìyó{ã7WNTœµË=e6=Œ´n ôÈyê RìƒOÞyÙƒ7ב'ß/Í‚ý"n=rA„&<)&|çõ8ð€àœÖc?gÄqÆ€uL‹\6Hß"’f%rÉLè’‰ð[ÀS)Ú¹H¥›+fY©fX(_69£.vR™ÿœïYUvHÀ›—Ÿlã" =ŽÊAŽËrdÉ"} E[ì¬2_²š ,Ê3´„RÔx“Õy.èò¥˜‰¦Yˆ§ _Ö dK]qÄ žPâ?®-yÀ@ÆKœØH±oüðƒ‡K‹ó(/hfÞ  ÞPè`pàaNÉ4tå´ñfiõõÜ3?»öÇ/Ÿ5UåDx5W-¶×®·UÏÓB¨:âœÊ©’B/£"3!˜vb•_j+Rb'Rd/Tì \ê"Vé!Uë#×à¯Xí-[ X¤Zã¯Pæ)Sè"Zè"^â.ѪQí'_î%]å+עޮզ٤Ѯ—뫬&ÆMá§Ÿ Çç«GÆŠ'Æà+KØ&ŠÕ,äÿÛEK 4>ë"VoXwà°“t´:ÉÝnüûçé“/ª6@XÅ(ñmi²ož½4\,`Ø p€YXKU¸*¡×*¶UÈ5“@Â/öh[d‹Ì%.êsŸÑç<¦Ï¾æ½@ØŒyW¶ø{¼#Ç*^A‡‹kº¾ÜÜzü¥øÀ‹ÖCn$)Lû³û90´gþl­kð«p—(w—­tU¬p‘+w—¬ð–®ô•ª ”« U® R,ò–Êu)t.sn ’­t*°ç+t+ÕÎÕ;`'å£AÂß¿zve´wåE3Þ鯡-„QäÔ‹‚‰áf¾an¯?nÜéÄy>P¡$Ú¤,È ÛA‰XU.wqvc.I\ôؼ¬øÞ3È9ï×Q½ë³[—Ÿ|Y6ÿG3}÷릓;±Ù—$™{Œÿ4 ‘mÔ‹Êb@ñs›þ~ÿ†Õ`xœ]©ªšYìùqàHo’õ­íŽ·“œ+53CUòb5+buð‹õ^EvJgM`½.É@,ÃX"Ç\:×RyÐùæ"EÂ%V¢®*Xhº(^r‘yg ·©I?bõë7¹TÂö¦tY°ñÁEa^ÐèdFêA`øº:¡z‡–¿'½Ä·[³ú²Ã^~UÈL7£ç~šÃyà{Ìêõ/Ê/Û‹ÙË¥é œUáLÕ*´”/± Xjˆ2C"ž#J ‰:Å:',q°CÙÁp‘o!¶jÆ #¼RSµYi:ìù–¢5^ºµfõ±ÎgœÕÕÈ.wÙŸ>Èš~R÷ì—¼ó©–éeDól®½~¯ùmðhëqÇ‚DóA »\ùò[<ý& F"جÍ.Tñ†×:uÀžï®ˆ•¿’¤×¥\æ%Zä,˜cË[ä$Ò Ô¦Uï¯\á-Wí§Ø¢Þ¡Ñ®^¢\à&Pæ%\î-Vâ!ZŠ[Å]²ÄE"ÏN„²Ðíe@aJ7IÔ%-~|;åv*ÙFâiºB—4øÓ@à7–*4W ½lC)4xø<ßT¿ŽßÜ~йÁ—+²–À}[í®\n'S`*R` \*”¹|ŠžØes…ÓŠðÐsæØæÿßJ ÖÙo wZƒRCe ¯_Ï-¼™œ\yüë Õ{b3¼,³í4óL¤ ™¡Ä»æ·úè€éeës£¥É3á=sN5Åî) rF‹œÕ%çõÉC’lH.™“,köL[ŽtKrÑŒ\BÄ­9£KŽh£jä„ý3ç6ÿäy#’i-xÌLÀ˜­9ßW_¥½\¨‘ïÚ"˜ÿ»ÔÌQ6¥(L”,—¿‚uÿÆýžp—C©Òüãï3ãµ´ÔÀ+é³ÌÃFä¼!XÉò½Ùi9r-™.ûe]γ*äXÙò¾Lˆ+Ê„C¬?̱¢#Vˆ$Ðý¸ïäÒŸÍëHa›¥nÞÀcÿYj6¬«yÛÕÌÖ¿n:ã«mÄuöûï>.ffo0Ê/o!—ôi ‡–ÑE\|I$Ùˆ¤˜“t[¶4;ŽK[ÈE rÙŒ¤˜‘óøÇ*˜øÍ¶caÇŒ÷N}Pi|ˆIÛ­óøï©È òü¶¥ù·R³I–"=W9»R ¹ /?H½l-ˆ(ùÇ1sîw-ÙdHk¶ÿóGù gÒõÙZ3³ÖJµ'OŠ>-ÌðåÏvçOµã½l+tÀ€…¨nmBrwYM}U¹6Léý“ .®¶C¡ RÊú§%U!šX›‡ ~š…g†ÌMrJŒ\:-K?¦(ƒFËJ×`¿™0ôÂäVfr ¼‘4-Vº6[†IA¦Géa)F|'´¸O‹Ç(qr.Të×{éÌÌ ³qƒa®Sœ|:¸â§÷OgEÊæ†É†©ž´:ë$¹Ë„˦¿øO]šƒça=ˆÁÓ“å´Ô¼«°#Ù™”zpã–H³$yvìi¸7,è­‚Îÿ˜:}^Ni“³†ôœÁíaLrI‘++߉:q9qçãA3#uÉ%]rA“®ù¯ óL8OY=‰àBy8^ÖBmAaÉ1’FmIÕ9!K;à¹ÚRx "ÂX“ ÏÅÙ·Ï‹IÑeÃIV¥ÿÎÇÕÓà=§)pÎXú¡8”ø.ò<Í%¹ÿm€ü–ÓG¡HV76fç_¿|ÁŒ>ù¦­²éðšp»öË6í&™fk9äƒè‰ˆçê ! ­ÔR&N•vm~ªmþJ­ Írx#·…È·‡É·‡+tF)ôÄ© $ªnÓ¼²M³?Q½?^£/A»cU{cÕ¯l×Ú¡?°U{0Q{h§ÉÐóÆ8‹(D/¥_~ÊŸYªEÍŸÇêö•Ø{þUjÀ½œqS³zó~UÒA'ñPErÀ]äÑûÆža€‚uL;óciŽh]jµ‹ì•`> ¨EÚüÔÛü5Úüµš½Ôá}-Ä´ÅIF:Ýþ\~’å¦{.{iRpÊÙß‹Oë_O5á6nc‰ò7hµù'—cs¼‰ÕÌ7½z\wÔM2T‹¤rׂ™¿±ñe^[´\gˆTS€t£¿\G€jO¸fW„r{„\W”Lo¬ü@¢*®®Ï@¼Ê•x¥ö¡aØ,7E(UEj¤ù+¾k+j=‚û“ŠàI»4Ž‘z”Y¸ß¬½¬|» °ë'0ï_©;îr>@u»!§¿q”#žºÜÛÌÇP¦šâûc,ÛƒŒªý´O;ˆžó’>fÅ{Dq{ù³¦¢»Œ/‚í¬+tQN¼è$sMDaO]e)Yo's;Ê¢?D¯Ú[ñ¼%÷1[NÀË_÷`žoºm¬¶0ãåOïﻟï4p\ÿÖa“ŽÚÜM²¼o1´Ý¾3Á6Ó[3Z‹­élØøÕKOi©Árœah°òdïóÁ3é¶"ö²56²uNJMZ0Ì)µ‘nrR¾h0¨qÅO±ß[©×[¹ÏW³ÇG§Ã]V]ƒÁf8AÆ}z]¾­žò®Ru΢9&¤ÆU¢ÑW½ÎG»1ܤʦ‚ƒçC˜_:˜¹›ÌHûêÅOnŸ¸šå]wЬ$N=ÅY ÓI¸ÄG墕x®§æ);Yy’¾Ýø‡÷/RÁ<Äæk°Äͬ¡[«lÞ!m§JG„Äíwt0½ºUóÆNý¶P¹ÁÍÞÕ¾õ«Ût®m×Å…[åêNõÞD¹¾D©îXɶ±ÎHé¾X¥þhå®…þ0ÕþPÍ-Üðþº]š-›Ÿ¡iUJד¥+Nž(N†Žp¡©t£‹fO€Q§¯~ƒ‹ N»·V°Qw€N»F«¯Z»¿F§ŸZ›§J“«R›—f³§vGˆEM€y‘¯éY{Í­:âIN†ŸÝú¯°ðÆè^Qy¿^}ójfæõØðÄç>.K- ·;g"Š·|¥•x…P±/b(ÓTxaDƒ}%Úƒt œÓtغ˜aD²LH¶ɵ"¹6$ߎ­Àž9ÑSâH?–:±ác±)p Eö$Ï–Û³—»ñ–;sãóRâÄ[æ*–á"ã&A-¾þâòôLª!|[j°oÝ þ§Ô@ßúW©yuóýúwNzÉ¢«9â-ñãG)/ŸW¡‚žúÕW9—]8ʼ¥ lJ¶ð˜r¥é³%kÈ‹à9‰®æüæ* Ô ÌìçÔ8ö`*×ãAµ³0$¨Þ1û¸aDÓ¥V´4Ã’š ^/a–—GË–‡¡Âëbæ‡@ Ñ%áš$ íSˆ^á)¿¶Ò]]Oýå˪Ñ_Û§ŸöÌ¿è™}ѹŠÌøhÝüBûÚFÿË™ú1hýÞ ­?ªI QJ°$C„ÿ~kßü(5-Ÿ~I³·67Po×Oo—Ý›<ÆÕÊåˆ{—'ª’#Úd¿‰“#¡ÄNŒ8ñ“ã[ø[b¬èóFk±WívÛ¦Üb5¸Ï«sâ%{I‘d«risçj°åpäYò]4ge{Š´"ô\ø,m(Ï¡úˆBJ{ª) `¯òå©wçkt¬³ÎÑá¤?,y.M›Pq<èù?`;Ö UæÓ'yÀ÷V A˜ü³ûØ9#V¦>×QQê&çIó5I®!o••p…%O±)[Ž.îd"7Öa”Ù°]EcsR–v5g±cE¬µÙÏ’t}RdÅ‘aÌqR ݸÀErPóÓìmÌWõ_Vï8ï+|̉=7L&;P*×G:ÛMì’Ç9r^‡ ;£Cê¼;”9ì¸ÉŽŸßOƒ™3<Á h•úéµnü–W-ÔÂ_lOªÝÙðü¬uå)wâ(³c+²%¥¬*wž W®ÉFÿ¿…œ+R'ÈŽZ‘‚-¤ÅÄŽo!¨E¦$Ï‚”Ù’FO¾?±FOs|ý›#˜ —·4 ¶äyR Ð 4' “4Ãv¾ÿ_©™ª|3[µ2ÃÕfíÚ{µ;¹ˆ„)“Ã^âß¼w~ø †šªØý*ç¼=Évä̲d/ØÂ—oÁ—aÆuÉ ¯÷N©ÁD…2ÊpvòÒŒÎ=ªìÛÔ9ÐoA½Íàó;çw;÷¼âÍb#Xý/‡‘hF_C󓈨[™ÛL×EÆÇLÁƶ«Íï7'î±är'åƒý¼xàÇe!'uÈeDÊê“C’eDòa.KdMjI%î+ÜÀ¤Äz³ÈX‘RsRe Î ©r⬔* VØkÁé 4Wá¯ß?÷ü1LŸT §q Ôùdª¹i4²|¤ó«Þnòä«L×éàﺓG¿ªƒû•ñžÕ‰Nˆt_¶@¯÷lø×M`ƒ¼Ò2¹Üôt¦êÅbÝë>à“ý¤ŽøÅ:Ûmq³#(5t³¼Éþ7Á%òáçæ _­‚ÆÓÀŒ·Äê7>bA w]²Ë_¼þ„Ëo§~¯?¾GÓA„ÔðêÛë , E\m$Ëå%ÇÀi¢´ÆËxsá‰6&)–$ÅŽ=Å•3Í‹«$R°(„UàKŠÝI…+œÿI½%©3a5˜ Hib èÛyˆ!©¾ûì»Æ™§UÌë.쎗—ZixÜLÿ÷µIXòž’'ÜüX ¦°á.Í1f/0å,ÝÂShÉžkž®K.ªÓ—=ªÉjœø wÃìŽå:€h“óZÔõXý‡e(©gIR$ \Ÿ Êó´%9fDk“Ãj”ÒâÜ>L.²äà¦cÆ6Ä›òQ#¦´DÓßÿ–³1Ó%)0z/A]ÞÊü–[êÇVãDÊð¤l¡¯› [R»ÂŠš"sRaC=¸;ü…½¸ó-é׃;*Ó”…»ºØZ(ßB Ç@¶69’¥Øa D$=;9ÈFöƒ*tK¤{RФiðçJäˆ$ñ¥ã&ÙJÉRKálöL=’cÈÊ6áȲʲɰ¸,//\€ªäŒÿV H|ʼn1Y×mËûí OŸÌLORR åå½2LõÝ”ÈG‘` 4ðÉZzÃ,¾f–Ö6Vf&_üúâýÞº]^ÁêÅ6ün­^2UÖ" Èa±Ñ€±g‰‰t¡¡X‰©hÂ5œåÝeš=e;üåzÕoÄkÜÚ®u{‡öíjwåoÆI\Š»-~#Fêz”Ä•pñ&WV£G›'o·¿p_€x‡P{¡]c6æp%Þ†äÏ_ó&d< j}5J µóÝ,5 Õ`€…Ú;AH„Y»òçƒÔòw,/ù+œ ‘ÿxèØÈÓÊ5LO‹]Ìãʦm*ƒ»tZCåz"µ»ÂµZ‚Ô«¼äŠ% $Käª\•›½uÚL‹TÓ¬•r|Ž9«†èrù°j³‚ÿxT€ê¹qÔBS<ñ<›ê£ÿ£ÔP*-5_öîN TŠÐ"uiáÃÐZ®Ýþ½ggg´ø@Žì@´òõ[‰Z·v¨ÝÚ©ø`«ì½XÉ‘×#¥nDIߌ–»!s3Tª×‹¿ÖŽ ?¢1X‘;‰¯2¹´ÃèÇ¿§Œ céªØÄ$¬þá¥@ã™–aê2 JðÞŒ  mRºÇîÛŽs‹ß4¬þÙ´2Þ´> Õ6hu( I |` ‹’™ ðTáùðŠéY{ÝE!¯…ÛE‰º<øpý°7 œ8‚âµ š"Òÿ_©¡ŠÎ…’•ù2ȬVŸ×¦$ªV8ªxyŸYkmüÝ«‡ÿü$›yÒ½p;L¨6éO‰ž½‘ßµÍ6ÇVºÆEµÌT¢ÖX¼Ý\¦ÓB²ß^âP ÚµHõkqš˜®ï1¸µ_ÿÆ;Ê×wÈ ÅKÞˆ•¸-ù^¸ÌÃ`ùûþJ=¶’$ |~ÀÃqæ§(VUzÂ/_×PbÿZë³çE(¤³ŸwèŒÑ­°jt”¨s’(³.±¬õ@ö„bOÊ@¤FO¸z[ r­»l™½T‰­T¥½"‚)k]5J¨øj…‡\c rK¸zG„J«¿T‡ŸTW€Ò@¨î•HÓ+ÑÝ&ma:ØqÔ+4ÊV{‹W»ˆâ/ªµ‘(7¬¶EžQ­r³n¦¥ò>M@iĵÛþöqî*<ÍF©nw}±Ž’ot½ú!íÎÕ‡Û¥oNJ݈½-q'JêV„Ô0©6WÎW®v^$‚õŠtøòל± ïw´å¨ ·¡ ;$Bm-÷r‘C()œÔmæ9#À&!–#'›£,}I‘ô~¤îÖØ)¶{jµ¸©×ØË•ZŠÖ:H×;KÃü§É]²ÓO®=H©5R¯%ƨ)D§Ò]¡ÔN²Â^ºÂE¡ÄU9ÛU­ Äò¸³Ž¿‹¢ð»~.}Ey¿øtft›nºÝþï¥fþ5ƒ³°¶¶4=¶üçw_·ç„™f9IåÙòA)™‡FÅ‚•¦O Œ…SÕùÐ^£M&Ÿ©šlé:'BfCR`I*ìX5.œuîœõî¬Þ@žîŽn?În®¾@ž¾ ¾¾Þž¾n_¾n?þ_Án?Á.oÔœWžžRÎ4[A/øPé‘Ç?eOÏÃÂDîÿ³Ô`œfYmžƒOÂTËô—ùÛõÎùHùª’‡ý&ÇW—º@ºÛø.§"Fª1B"Ëš1K5¦Käƒ`=‰S*„i Á Tí}J¬=šX%À~an¼lr¬ðåx>®çâjå+¦áÅXæÚr5R„ÇÊæŸÑ]É\ eæqwùN³XS¢'@ŠÎYÿþMÆø³è77n›68¨AGWCùÃë˰Œ+ƒwßÊ˺O¯ø¸ÿÐÇ7N}ûeÎÏV7®½lg&í8¹ßŠ/Óßŵ+¾ÈODü(ú„d´÷9U ÙÒ¤©Áº¤Â„TX¢Íc«tdÕ¹qãÝÝÄÛÀÙïËuÕŸç†?ÿ]Ñ{>â·½e]å3ÕÉ1Ø?ª_âoÂ;ØrrâÅÀÂÍȘš©]ô±qåÍÓú¦=†ÅXÍXñÕZ Õ;Š4ºK¶É·‡*4ùHT8ñá~H1Á€CÉS‚“èáÂBƒ 3Â9m2Ç/š²§náÊpàú؞³x «Ø’£ÂN¸ÁU¾ÞM©Ì^"ׂ7ך«Ð–#6DÆ$Ï€°Ju8е8r4HŽ PÃY%ν2,( @ÆÈßcÿü늅)8 ÎÕྂƒ 3Wûüîîþm"7"xY}þ¬~î+|~|}Þ|ý>‚}¾‚HfÄ]ÑãÍßáÉÛêÆÕèÊ[i/\í Uã(M6L-N:"sSSœÉ4yD5eÊ å©HkÊ—èÈuzèUÚHçòCÙ]ë ßâ®Rç(WlĂոÎI¼ÅS²'P±/X±ÍG¢Ì‰÷œ)9gÁuZŸ£=|‰Ñ­íQ ñòd«® —‘(LæM;Ë Fúiebjqzzeyñm'ƒ”Hó–°G;j•…®fö 3‡ÞæÕ«Å©‘…ß¿ü®-§.ѦÒvÓ‚høó,I¦Ó#ßTÚ%5‚d>K‡dè’Ò-\%Vì¥Ölö¤Ö…½Ñƒ«Å“³Å“½Ý‡tx“._V?Gï`ÿ`°N ²ûü…»}hµAðÜ@ô@¸JM€r˜" 0"Ï~ÉAf%^¦%þK©)Ÿz–õé¢/‰t0“}5,ÊwE›’¦‚Àß.^˜i–Ë<«iW}p·j{¸x©#w©_ž ç%#r\Q¦ùV—ôYຠ8ÍFî‚“Ê!GE$ Y«‘ó‡}‘3û Ýõ#ùp\YæKpó¦2„ÌBì §ÍÍR³4_Ì,C˜Ð2íúŽñ'ù±¯Fû˜±æºÝ ®ˆH3«É…»É«Á‹ÕìÃÖíCz¼IGw ÀÕ ‘¡ Qœ.wž+H} ‘©:ã$è)GâíDÞï= chg@Õƒ.\:a i9* ØtÇÆuÑVÄG‹4]òšþºb}¸ ²$@¹™çêjLtGGS‡Ÿ&3L³†òR E<³ÐK5 eþܸ§,Þð°ƒÐn{>5R›áõâ—ÒÙ±z@^•ÚÌñÜÄ„ÿ§Ô —CÁÁ– R)dÔÎ7!`n©÷ÙBëørÇ«Z¾ ÿ:`ûÛQðAõ;Ì/Ísïgœ±ãª“©Ï‘­FLë ¹M8ÌØ¬ØêmI½itaoŤàÅÙãÅÞïÍðaòç¾(p/HôA ÔÝ ùLmúãÛªLU¢æ<ƒ-'FŸus;ý²ôåTb˜7ƒÌLwç~“÷ÛÕØ4Û uxJõ…*]‰Óè‰Vª÷,´e+væ)râɵå¼dHç‚㙫“T}ö=Vº w¶_Ž _†-OšJ WŽW¯Hµ_™-O©5…­h…ƒd™h‘-‰=o‰g‰ [‘%)5%•&œ5F<Õ†¼åFœ…F°n8¯Æ}@‘(V ©>í÷âQõÆRÿ,®*‚·&бHe¦«~îìM¸ÆÞ@ú|É`×Õ@>Zm||…D®ˆ ø‰ Úl>/"ƒÁRƒa*ƒajƒaAªÝ¾ H!it¯Ã—dÈ•«Íž  *Eب “Ó°þ[ ’XȺ!:Óg5¸Èôi:î T«°E›$Ôê%5¦t3NëZ”J‹œ(X™ŽÜ9nÂÙvpG¤cãiŠpžÔã:ç¤è%GÌ$H´‡Em~Ú7Ÿ|4ñldò|±(fsb¢Æz´æü«Ôl¬¢Ú,¿a0@1ÌÊ«õ…—Ogøè¾‚[Çüo&Y\Õì ’é‹Tl ShSí Vë RÇéTí PéTî RB ¼…ìE¥¡HÙ1r·âdî$ÊÜM¹/u=Zøf,ÂEï&JÞMÞ<2÷eCD‡"$oÅÈߊQ¸IϽx‡Ûõ&wÆÄk±BÍ9†…¼¥˜…LÁ¬¬é?¨·Úf½Ð%¢™ßüQµÏ‘}«UÚfŸ²ùêót¸/RµÝd[ãNùöÉDÙ«Ñ ×ãԯū÷E©´˶Hw)÷GhD &li³ªŒ³:䦄ÜÛ gÉký'ÿü£bÔSÔ‰q3P`ó)›†ZM˜ýg©»F fµo_¦sz°Âns®; ï2SW‘jÝ~Xãþ^•;q’W#ĆbäîÆ+߉S¼‘ u{›ÔÍÛqBw$Þ^™» r÷äÀ×éÍs3^©?A­.V夛RžÄè?ú(»6DÀç ¥fržÚþÏ0™(EJ,³:ðý£§#¥ÎÄÊ=hÞ¶1ܼNõlUØnøiràý اäm·pR!Ç5¾|/ùå³´ ðë:„ûW©yë”5W49–9;U€!.+ÌR/ØÅ£õÓ ­Ï^.7½®gºëê%Ïã.ã´ݼ³,f¸á‹Ú„Lo±¹VO¹;á:ïGéÜ U¾*s3\³äíé»qÒï%È>L”½)|;Zèn¬Èý8‰q÷c¤îGËÝ‹V¾¯w-Ö :H ¡6!Zd¿âßn¤-Ï^9\>5^²¶ã‘†zóGÅ;ºÔé˜ÿ%è‚5)°½;Zvh«òí$-¬lèz4A½7R¹#H®Õ_SÕ`¸f_¸ÆPœþ­$“[»M¯ìÔïۮݻS3x_¼j´jïæƒÐ¨Ñ¢Õ©;§Û¥Ü)×%;!%TiÀ_qÀGyÐ[ùz¨úÍ(ÝÞ pãK¼t˜ˆ©“²ÓÞ¿…÷`Ç4ÍE©)b€¾ÑöÇ•¸G?Ü-~/Qè^œÐýx±ñ’÷b¥îDÉ\ CzÑÝ(¥ÛÑJ77Ÿ—»qªwhߊU¼+#ZþF”ÜõHÙ«aRƒ!ˆ@½&{{«`¹¹~?¹Á¥¡ëaZ(GÞr¨Hµ À™qd€v é¥Ì&ýC‘¤k“²-¤Ý[øZ„ܽDõ{Àbº°;NÔêSêñ•éõUì Òl3(³òScù™ÊÔ^øõÛÏF‡ŸMMÏL.ÐõҦĒ~D‘ù«ÎP¬†Y‡°i—ÌâŠØ¬¬­Î<ûõŧ7>ÈÙß½mKo„Z—ŸX³;³·H©+¥—X… ­‹`³ì›ÐI¢Œ×:óRèðh÷ákóæìðãìÂñgëð%mÞäjßµhT›ë±B×"„ûCùûƒùû‚‚E®FHÝÄ%‰TŠ”¿£z7^ µ5B:¦P3¶áŸ³`@ ‘ìâËÒÍR–n þ…Õ Ô̎䮟œ®`Ö±é-× %D6É#ý˜å£¯r¹Ã`xRyÉœ3'ÙVð"5ö¤Î‰UçÊQçÊUçÂSï*Øà*\i/Xh/’å$qÊ^,H“Ø tÀþ»o*§¦ZAì¡"âÍ dtøàÎô¯Rc7t5(5+‹¥Ìz-3UYvPóœ»Ð¾¿÷fÖî2/kwÊôÆ£»kp%í><½Â>¼hiÚÈÕhžë1|7ℯŊ Fáúô âtøò^EÁ‰S.QÜmÃòÆpzÊëç ‘UÁˆýÌÔB1müðíƒ$¼ÒÛ”b8D(ã]ÍϯîÛkX±¶Ú±´Ú ]fr°F²…‘æçôOO}^àZ%šïÍ]â+\ì)œë f'rÄ\à„‹Ü;ö¢.ªÈ¯7þó;p–§F!fMþ§Ôl:"ÎNÁ’BÈòñÑ2ˆ^±À­žkž«' ¾šÌXSY’ÖЊ¶êþãö™é§¥‹ÏŠV~ȯLROsàÄ>®Æž·Ñ‘·Ó‹¿Ã‹»Í‹ÕáÃÞåÏN‡&:W²ßˆÄÍs3^—èJ„@__O g€p“xg„z±ŸÜ'aw ´¾2ÎN ·Ó\Z˜6ÌT?ù#‹ŧ:UbÔ[[lxëm81å€ße€Ì;r^›;’bgάj7®ÚÍû¡Ñ…¿É•.)pocõSêÂYèÄÊq"8y.¤ÆO¤Þ_¢Ñ_¦Î[¶Ê]ªÜE¼ÜM¼ÊK¬Ò[°Â›§Ú—§!@ À£³``­­`½pNDeJgÌE£”‰‡©¸ðähu;¦æêPjfÆ ã«b–>­v»wHö½$¡›q˜….l9Ö$ۜ›€.ñ°PƦ)÷n5¢@"L8nµxù¬ŠÔI8K,ÖÐR3WÉŒä5‘zWìÖVžÁHVo(é bõrvúr¶zqµzñ´y ´y 5yÕ»ò׸ðV;ñÕ8ñ´ºó5»p68²áknqÇž’«Ùƒ¯ª.Aœf/üIv,7Q£6,‹ÔÇ)¸·çóéN7PÔAqcºüÍ•àÑp–…îïû÷àzÔƒïÝ>º°<ÖH 'SMý't:“¤:¢øîÄ ßç äºÀ;,l¤ÛG¸Ñ]¸ÚE8Ç^ÄòÄ[ƒ4e‡M÷NŒvNÏ4¿È‡Vn0S3³³C¨Çn’I Æ®¥¥OßÖ¬hêRâÅOú ûæöil@˜ßJwÊuG"8˜Uã̆Må•iìš½QúØ«Ê[¶3ꢔ9ú&Í_žË^ÆGøäôuä–ç…‹“•”µÜ±þGqZ´hí»'ÜÉí÷•Ÿ³˜É*f¢ìùíí]‡å¯ï—¾Ïq%€ô{“›Á¨²|/3QÁü‘6wk×Gç¶ÜßoøÙY³¿ÑüdŸÜíÖ·»¥î°îF_‰“©Š–9èÌá?:eâoNvzI^ÚjPzÔéjiÂ?n%?*ŸiAAê+ì''ªç&«Ì‹Ê¶ú²di$Ñ-Ož^š[«X|UûôEÖ³g9S{½ÚÜ Cfµ›y58û(?c§j˜!i½ì:ñCÎârùød:Ã4oüx¹.‹=©Ü¤!aûPhÁÙmБh•é¯kDJŽ:}{ïòâhëêr+lú°>ž}õY#ókVóN©k»åoĊߊ½.x#L`(BBˆqb·$0ࣃêô#˜÷»ýذ¼èòåh÷dá¶ïpçlsçnÆWbÇQcÃQoÏÝèÌ×á%Úå#Þæ!\kÏUeÃ^ëÀ]íÈY f”-)w&5Õž(8›´Õ2ÒàÏÞ$PãÌþç¬Bô’>{?eìE2@á½3³TJ_RP^L5gr×ø³ðÈ[.4§mFGµw¹W“‹ø?Ÿõ«¡îTº¢t3Œ²#îDÈâ#vâAÂh˜;½¹è«$RôvœÔƒò“ïí”Å÷{-Rðý­2÷q)"„†Bù¯‡ E_ B½#~=JÇÕp tàú`á~;AújŒÂ⻽9|Ø®x“49.lÙnÂ1ºlþ¦Rï]k{9>²°°´0¿ÞÌÒÌÂæE[˜ÿüßÚê"|ùÖÖa;̬Í.¬t½ý`ð{G<Þßet?VöN¤ø•pÉö0ÙæX­¶íz[¥û·K^I’½š¤t-Iíz’ö$ƒ[IF×·³} ZóJŒF¤JOˆlw°L_˜ôƒw·+ἩØ,ßì!Q¾®×idÒ‰P x€‰ç°ÕÛ‹¶û¨ N"ÓQñ ™P˜yt=}ùE/ÒHá8DÝÌÃ0!{u¥¦àm©y5^¾6Z<ÔÀáEEó ½†]J…aü÷Ólf>>ÆŒ33•(5‡®”}¸Wä½D<¢ÅÆ©ÜÕ¹cÔkžæ£š´EtÜW¡–ˆ§ß–.v䜫‰euµmKë ÈKBŠââl½‘>@3”Qp6KM%¬™µf¸¥ú˜Ù¶® 絟üp.eþ‘õÞI»»{·\ÛfÚc8ip-T§/XlóöHÅë» ¯îÐLž Û*މ`ø ŒíW%š‡| Ÿg<žÿ\·¾0ˆè[˜r#|ûëÙ:@13Ï‘rØÂŒÔ5ÔlÚ.÷áËor¯ìS¼yLíëtÛG'm¾;dñÙ»šŸ¼«p'œu?”|ÍýQ÷G[eãä“=¹Ð=úo!Gw´'¼ßwêû÷ÓÿõG)n_Øò AïÞVƒk±:}1š}qZ=Qê]ÁJ}Š¡Ê7v^IÒܦ4+‹‰Ä’Zk¶"#à | ¾šUA'ì$‘£ºÃèç²!AZ˜o„AÖ›•Æ•Ì\Í›oÏwï—¿wTýz"ž;¡»ÑÂ8wbès(þN¢ä‡»åßÛ……Ý\ÄKÜ—›e(\ì~¼ò{q*bÕo‡« )]õWº v3Dój€Âõ`…kr}€8|ĥѣ ¹³Uõv¢ÊõxÅ+Ñ2=8½ÑRqÒñbwv)ßÛ¦s‡Ui¸~¤kGˆâýÛgægÛ§§«ÐŽLdNNæ1«MÌLG6‡ô$©`·r%Lª?ˆb)U6àÚ±¿}^À¾ƒ«Ï?Ÿ¬neÛœ…ÛqÚ„:Ý„ñU] ”¾*3&Û9ÈW Ã›èÖõñ;>zGõýmRl•z˜ q/FäF¸àÕ0þká×#…nNjߊ»+Šs+Fׇ^™÷’tîoÕ4ý^Œè‡ñ¢Ä{ce+£Õ÷8ÈE:ëßìïxöìùÔôÂÌô"˜^mZwþg•ÙüøòÁ¾5o–§fçŸüöûí®ŠD· ?ÕJž RiE6’vÙš3Õž'Ô_H ܸJ\xóí¹s¬¸³,¸rÌøò,2` ¡C‡ ôuÀ”@8,0#é´FÐlº‚¤¯GkÞŠ3ŠÔ Õ½a<fx+ÊôáÖ-âÍz}•ËÌyðïW!‡´¸â”‰éÍK|úUÅøŸ3q…ŒâèjrWQghW‚GåÊh ĆZãE†”f¬êãêàòxÉR#öE¥?ó]ì¡æïíܧru§LW«3-"m5k9«œE œ$.9ˆ¿kÁ‡xw?cR|Áóû2^´¬Â>ýE°ŒH(bØkÀ/efºdz¢+o€+Ó¥«S¥|‰ššl/¶¥gñËÂÌ­NäÒYƒÑ_s™áª'­IUREŽþð\Öf]Fã­AÒ5Iª!ɰ¤ŠÔ +RæÊÕ,>§p+^?lÄ`TRa|ÙK5Æ€=ÞQ ­8jø·¯-6M¼„ó@õÄ 6× È˜BBñ¨_+3Ã%Á°­ —­ “)öèÞ©Ù)÷ÿ{ °¸²t tã.EaU…îî$$DˆKÇCÜÜÝÝ5žtÚÒ6ÓÓÓîöއ …;$ç­MæÎûÞ½ó&ßþø*I¥›:œýŸ¯ÉÛÛ-?Øiueæ›[´ÚÉÛkdß !‰QycVÇ:ÝðZ?±Ýôí+§ûiŸxÒ=?Þ3?Ñ¥<È{K£-HƒZ¨›ƒÇÔr/3ÛF3 ƪ¦ç릗ú'ËþèËú:ò¬üåŒþuÝË<µ •º¶ðUé›1™Û qúëÝ‘²J®Ô¬xX±°ÜŠž™k»×³-?T¾Ì[6¤,cRæ(—c-A‚–ä˜Õ¦$×YI\ŸLÐïeºÖ¨¿¹‰ûî£wÖ£ n âe8(Ÿ±öÙʇ’ê¤àGß” >FHMèåh@ªì{{oû>­Ûõº×É__+w{µŠx ßŒU¾±Ž.œ÷ÛB x8éã«©ñ ø‰`fí@Š­‘cB2Œ¨3 zL!\¼HŽ)É‘BkRî,Ô·5D•M¼%!€H ù ™®$Û…þ|Óí(m¾ÜUºÌ›}ÚI%”Gl4þúÓ\ÜÒkNÎVŽNO—2Ó̯%Yá²Ùn¤Ä•Ôø®Hµ×ãŒßÞ*º³Q„yôë­ÿ¹_ÞÛêty•a¹“|6ÈÕ|’Á%i0uÁˆ ¢Q}‚w†!)·c•ÚÉ–ÚÉ”;¡ûb5û«´‡iöFj^‰Tº½FùÍõêo¬S»¾ZùjÈ'hc40‰ HòZÕ›±j·ÖþcÝ\«Ñ¢€Ç_gió&~¤3T¢,@挫|ĸ›Ýìn{þ|`fvizjžºC,`´ýÆ+ˆ ì°ÐÕL‰ÇÄüòãÖÜX×â^…·|é DK©Ü¦Z쯖çÌq$‹Ýå œ¸,ËHGá›l$ }4İ4Q“f4'ó)ŽÝ¤ÔªØ¾J­3‚Ó¦Ýè¯Qé¦\l¯XˆUr×ÇXJ•ÙËU:)”»ª%™É'9hí3“÷Q!-—Öõ}ß0Ñß;>ò*°ÓlóB Øé¯JÍTéËéFäÂN`΋gOß:Z§yã¨Un˜lñ:•ëb™ÁvæIÃOYAÝëÔÚbä:Ö²Ú¢BXõÁìdWå=S 9gô÷·FŸ5ψ;æÇÚfWÌ—ÆZ~̓S:241išc-—b!Ù‡F¹‰+gq·Ì]þ¦õPB¤Ž·ˆœŽ7~~/»òIÎê³Ì—:Ã[¹>ª¯$çqL3¤sš•º0¥–H•ö(5\¥¦@• w…\'E0ۉ䷘H„ñÉhýw¯G`îÜ|š‡¡Á8ÑMÎÀ½¡¡Ÿ/&ºáÊòMÏ¡Ü  ^ŠG-I²+«6ÆøÚ>· WÅ|WÅ7…bo™úPVS°½Á¤ß4ªÑkpÔ]f-"‰¿[=ÝU}d°fx¸×st¢Î²Ù¡ d½×ôÿ’6ú¤xøÈ@áÐpÑØlõô˦9 K·™ù·˜‰[Ìó+Ì£žŸ”|˜¾&ÙO5/š›¦})†›²ÉpÙ$}³ó5ˆ:ç:‘”%ø·-[+£9é"ª©/°L5 ~Ðç°àçO3¾y$njԫ׮Ro‰TëŠÒìSióWªu•ÇÝ’o¯’d¥xÉC÷„§–›I?dÿð; öÝÈ®Åabp-1Ã5?v­ïا۽M(f[W#doD³®¯¦cÐÞ5 =«±:"ä^Aˆ@›‚dqfA©)ˆ Mš`Äq)).;ÚÅG¡Ø–Uh Ÿ[)0Ê€1be‚¹ê&Sà.Uâ-W¤T®^®RÀÊ÷$’cU{*{hí3•(>Îô‡/òG€\=3Wµ°P $ºšé/R2‚%[V+w¬Rl SèW‰®äC7åB;9üÿ¹_òE2¥ö¬ '…*g<¡J¡±V¾‘f¦>2A¨á‹äš(ç[(•تV8jàm•®êÕê5^Ê¥hÜI“hKòÝáJ Ђ{ œ²˜ÂÀª]ùm´:^×¢Ô0*¬zeµð«V_Š cšSä+wP$éNrlðÇwßšG î<Òp–füþŸ¥fÅÕœv5ž˜š~òà—·z 6û—G›Õ†°ÛAºµV§wµ^}¸vU0§=Ò¨7ÆôÚ:+ÄçuÁg/ؼÙß yí¢zOA¥3·ÆE¿ÖU¯ÞC¯ÝÏðJ„qBM«>H¥)T£-T©X=‘Æ—£ÍZ @Vlôåu… ¯D˜4ûjCmGÅ\æŠíµ÷ eý  ;9üSû‚øÖ´¸“ª) ¿- -5”°J 'qó/ÍÖû•2ý€]<«xy/¯û¸un8«8B©kŸùõ£vožtþà¤Ë{û¬¿:ëݱӱY¿s³aÇ&Aózaqÿ”‹ÂzšÉ›õolл±{ ^:`ÌöFv…p[|¹øNp?×¹ñÜ aÙÔ„ìa£¯Iƒ¿°1À¸!Ш„ÛPC4-å~ll¸3µÆ·Æ"Ó“[¢Ù¡Y¤ÚfØeuÒ^= Ïå¶?|–7?Þ ‡y±8j¢`f¬˜YìXü2½e«> ªÆ ù†@VSjÇ*( z£„=‘&ÿ²_L{#,}õó¬dS $³ ع†Ü|?_ÀÍæë¤èª×=‰4ªu: ÛX…ÿ6ÄÀþ˜œnIË#ð®VÜðÝ«ÔÛCÀ¯Ð¾«‡1úÝ-Æïo7}µ@­¹µQ÷m\*oD+  ‚²¦<”o§m¢ÞSšûüÏûã3âÑÉyô3Ôâr§ÿµÜ «AÂ; O‹üöÍÍΔµ§Ô „¬õ’!%Cg£ è”4õ æêrÕ&ˆS¥Ÿè JœÙ/M@|¡Lõ˜8dX|´†j5«Uù(:ËàE>/ø“g´éà î²9fÒP¡f›Id˜ÉÀ‹xŸ.Y­B%È¥­N÷?­Ö3Úß RØÿ*5+úbêØ ²“_„ÃbD15H*ü±e{r€Ô!+rÒžœ‚÷‡dU¸&€Ö"O¹’vv¸ZfÊEÅãv’› I˜ñÑ!'75—®»ÿsÙÜÌñHÃð`rWX÷~wëhg^à‹þú±(5ÌBãJ©)Z.Š‹—ÇJ1ŽA\œEWså“ò='¼ØvZ¤ø’ÿØÏUKßW¢Ç¸ $‡ÕÈ~„ËK“c,’u!›\2@|AïݬY%@Œp} ý<#”‹7RXÇ!Ä—KJ“‚ïß+o^4:Z4>š÷Z”ì)Øk Ôˆ­¹Ò§XKp2éTI¢»QÍzcæòk”Hf æW)ð]+1¬ ѪõVä0Á‘ªMB-HKé†ÈÒêÁ¹i0ÔlÉÌLéôlùä\ùÄLFo`Î,LÕÃÝŽ¦º,]e–®ƒ-3û]îðû©Ùᆅ2(k‡’4/بJýfR°à€¡¨“ñ’ä£_”öígi øÑ„…þîCŽäˆ!%œÃ)^’ÄË‘}0ö—'‡TÉ 69¯MÒÁhb|©KÓ }•K%qlþ:݈¤IÀô3£lTHQ’OÿÃê¹Ù.€óèjðС?»Ñæ›i®û H‚ ÁÑ5+RdG…6ùŽ$Û‘TÊÀp ìwRì5®$øÈnƒO ÜÀÑW–ì‘&{`-M_ì‘"¯AZ(OލÓ÷œÒ£;þÐí–çŽÿ·“&ä¤)9)$uC˜ F²™â:Mê”°Çñ·o©í 8çâá칩âq>3ÝôäæÁ’UJík´qK»(r’̶¢XÝ,†$ûÍžeÜ!åv ™f2™¦ ‡eÈairHЦ½ û%É> úMžaK$ò¤¡!B4[¶%+Ë ±Thœa³beƒ1·#¬[‚Õêý+]%þᨹ2þÆD—+ߎT{SŠû«C%fâ0Ã9Ž0z6‰ét¹ áɉ¹±ñiœŠV\É¡§dó¿ÿBÁš›G”åò„xtøþï_ÝìLZã“(LsS϶“ʃ]&k8>[hçU™c) ŸO|oéÉt#é¾T’9‡ \}Ô ¹bå[¥r{¥jWå'ÙBWÙ9ü§’¡¤æ¯Dºè=rÔ±6HnòãÛ«ÂÔ篓ØõÖ6¸¬.ÞìøÇßʧû./Lôb¢ñ¿–L½AóN ú=HP‹UÌXÇü×9ÍG­’Bä“CÓCT3U‹Cµòâ ™¡{ÚOBéí–$R›h’MŽ’ç·™Þ½|ôËÆÆ:G'ÚFÇà"²0]ËÀRïISö^ÓË…áã¿O>.Eá ä6Š‹Ð\͉"îL”»ù9Ì\®|·å„‡¦³6驊cÆé±â,Œ¿P@pCÂ…IO2ßX¡Ê\¹ÚJ©ÂA±Ò]z9`g:ÁL‡!*.òÂt¤bH¤*ݪ;}tnÔ½öø×ò¡êщ*t0s—Àe |Øk¼œìzÿêþ5îdµ¥U”!9`Øv8ò^ã¥wRv'ùqwšöÃ^Í.ú±*¢´VéUú¨§Úʰ‘Ò"›}åÿróäÄ@ûÈSØ&ÃÅ7t$gl¬prºtj®jf¡na±qy–•ð9¼‚°•åKÿZ¸&+Lõ¼Äòì"oA¦#?ÇUOþ‹ÎÜí<²‘KöÚ²B´I0—xèW. Š{1`æ)Rï>ý­”yù&#¾¾Ïöž$(#Ù4°UÙt+úžnÎ*€ç›­jµZ½“:î·BV‰³<Ò?ÁõÅY‡n<¡ÎÈ&Øi²VÛ`!¹ÖIòNǾ¡guñ,˜mªGiþ!ÏkëwìøI–zRifIÍ>rtàȪ bUÉ—ùIz’j»bGmOD;«åÚ*g[*f[¨ÂÎ%CëK¥2½v¾µ:Ìvrm•tœdB.ZàßÊå¸HgÀÆ >Œ:mÈE;jqGJüô¡žN(D°ˆ›"¹¸ßå·ïŠGûà¸^>6œÍ,V,£«oø¶fý% j½‘ Õ¶LŽ$Oî+œiZ^j]ž©Ãyú§ÊH r·a볯³çúÁ•­|…3\4=ŠÖ·u¼ EsàâÎ_+ßawÚƒlF:JÖÀxvI51†olsm 2nö¶ûš\ Ý 2ïöå6xi »ÀÜ¿ÑG¥'\ÿJ´qO„Es°e•¿u±ŸýEWÑ!Û=^ÁÇ7ÎÃZ¦µ“SHx¬aol°mïHŸ™[e®o}^pâ>³ò³>wÊwÞ»~iæ ü¯?}p=«p“}",ŽköÝë>š½N¿ó€}kœIC´á•.ù1h·¢9?ü%Ö.âGeèà’§â¥éZxò@ÇÊ,v­$Ñ_‡¾àËž}U¬J7 ê7YTDð ¼ÙEžºð~O³ç'Xë1SÙmÂÚl"Î#îl²Æ^2ΗuÉõy«ïvÇßÿ²ðåÈÕg?•-­Q=hIU}—{[ ÃËèr€E‹—°ÞÛ¸1È'£ŽUæ­~°bC<ì@«}€ò]‰2ÃA»så2ÖúšàÿÛ¸Þ;/Úa§“jŒ“äí¶=cí3sÈg¡¹'‹£uÌâf¨¥ù¨èý‹¾—w˜\ß$ÊúÖFáÛq&ol6xJ(ø³Aúѧ߫Ӻ†Cg‚k¹«¹m‘¼¶~c0¯ÆG¿Â]¯ÒU¿ÜYyâyVšVšy"Õb;Z/½æ@~}€NSˆᨄs¯7Ψk£AëZ½†(­†HíÆ(ns¤ ÇAµÚݰ)Ôñ¸ 'N¤P~11. ‚å>Ô—º8Y¸4o±®'=šÖ"õµ6@ Û ª‡îhÓ¶0ãz>‚ªË\u`W‹-äS§ å±r,UJ8ðÊF²@­¿=ÄìJ¤ÍµÛ««­{¢-VDÂÞXAgŒ^K»)R£c-çòfƒÞMÂÞM¦XW6›\Û,¼±ÙøFœàæ£[q†·7½¾I!.έ­¦7·˜\ß*¼µÃ¸s½,Ú:×´®3Í æ´ÖŒâ«\/Î[x>ðìþ³9 44šr~t¬Ç£_jÐü ÔÌÍ-Ð@ïɉ¯Þ¾Uxdç{½C6 Rg¹idØ)‚Å}ÉL*ÏN1ÇJ(::´ú&2(8a]@З©p°Âß"<ÇJ*æ3†Ôëãœä“£+1Gà‚ ªÅ#,ÇZ0ÅXÿ+‘'±—­‚ÀBUÈ'å{wsǶ/Œuƒ<†CÊ«RóŠTC(_¥Ù"C0¥ÙÀV¢vi¤~y¸q©¯áÁß’Û3‚^ó•BfÍ^„÷©q×"Nlâ©Kbí%’·‹nWï~ðEñÔ@ÏP?š™Î‰Ù®¾ÁJØ02KíÃ÷3™7ÎoæzóIÙyÏç?Ázza¦  ú(Êž(Yœ¡ÑØT‰ Væ¼37’Ât· I¸y§k,à˜gݧ­È9j²?¢³l’e$S!R.·b3Ç%¢ŸF—0ãÒ¤–ñ09ÑØ¨HÖkÊùË‘@-’³Ïÿñàü·=/¦DAXF‡b¯åÔB/ÐØ÷gáŸ&>ú6{æqÃ˾vfè #¾y4BóŸÊA_åßߺ4{¯òv^än;rÈ‚7Çy‡œsWÀ59³Qÿû.ˆïC†Ó¾sòùáÕ ‡ÿÑnæÏÚOj6'„ªœö”½è¯šª“ªŸá§{Ñ]#ÁEý‚“ækYÄfyJyâ¡B|ôÉV?õ”ƒÎ½UÛï^=oÒÇ?W<ÿ£väaýt?„= PwÎ]™ø"7Þž *ÛB&ÓP~›ô h‘£P@›JgÁtÆæFޛЦWï•¡¾P"›Æ$O­³E@m7û(}ýarÿ³jpàÙˆRûS$e0OÂåâ᳊©–æ¡RP `vœ‹®fà¸?1ð‚=Žre¹® Œ ô¶%©paµ"Ö’Ù6²è ²¬²DJyVjØÔðŸDŽÕqer)„|Úˆâçø £ ág Ø"e:J@0O¤-H¡Ïë³ÎªÆiK¹)“êô5þX63‚ KDpæ3sËâèJÞK Ns“;mD ”` c\ÓOêÒ€ž8]*Óa|Q ‡³Ûé•ý‚#$ØŒ't¨]'Þ†ß]|ÿø ™ø~`ê²;¨þ$Û$Ù’$;Úz¥bÙPZxÄáj$@îj Òu¨Ý1ø;£Ÿ19×8Ì‘½X‰2ç”ãô‰Ÿ29âõÓ;o÷ýüÛÜø䕨^¡,àÛÕ¬Œžh¨%P øgÓ“}?ÿQwSÒú “~ñ¶š‰NœŠ`¼Ø¶F‰š‚M*=uþ_2‘‚AP²±t–H¥Àž]ꢋÈzHšƒZ¥·n™7§ØU=ÏY-ÅJ!Û‰“7Øk¤Y«ÀT9Ë–úÊæÙ«çØªæÚ¨9²+Ýt«ÜõJ´à%N­•Ý S\x±:$H\+Ýõô»ÊåÉËtg¡˜^™tcƒÿk©¡µÂè{…Øõ ‚Ÿ|›·mwM¢oÚ^Qü:ÍA  çev«M½n÷wo]zúmÅȃFˆ‘çæ®‰'Û~{P°ôâòà“Rjôº|µ7?ðÂ&CcR™øü^Ùàc8½ Ôµrz¦hÉ@_Îè`u¦šhA÷Ëù«/†zÎhp=¡ùÕÝóÌH/óGca(XVÅ®š¨«ÙÊ%öZµNœrÕ,k ðÕ‘NShøñ"”'ÝZ#ÍA‰TÇEÜm6~¦Ñ&¬æKqÃ?2×:>PŽ„#\X„­,a‰ßRrãX-j j‘¸¿f¼³Ïk˜)t#·R÷½æ'j-÷û·fï×=ýû¥†Ó¶ù1ìêu¼ÊÕüô`v˜)ˆ·Ÿü£¤ª GšI3ÚaÃ7­ûJ¶˜$ú«f‡ë¡ÿÉãG‹£M³B O¹jì0—X«Gµiyñâ56²'bLrø4e¯·çøOÏ{ücÅàƒ†ÉÁ@úh™úǧFšžý™Ï,\]ºWz1X)Û]1Ùˆä˜È—Ú²³EI¦Š Š™Îì2.n¡ v)’¾-•óíá%®‹3 žã9Ö96øÿŸ2e—Dº'XÄšËì eòö¹§Êg¦[aÿ‚iàË1`Âݘ<–í4ªˆÕïˆ3­òÕhô×iÔoñÓ©õf×ø³›£øÝMë#ÙàúÖ„«•)zÉ`H__{L”X%îJÔœß]½ÔUñCå.Ôå¾ÜV§ÈŒ],b×¹páÛäÍ­rQƒCK°nS°nc^C·.@¯:@¾.~ÜbóB¥3ís&Úë´¥}´HCÞæ?ï•CË6?Q57QÌÌU2àiL_¾rÜ Æ¤ivйl,ì‘4‘rŽ­zž½& l²×ÄëÍBG27^¹;×$ÏV#Í\6ßaªÙ¶¬tKÉl[98¸)”`Øä«\ \¬\…ªTÊ*U¬ˆ`WFj×DêÔEi7Fj5¬Òh SoVíŽÑkÔªðWÌ÷”-PAzÛ&Óæ&={‹£x)~ìDoÎ 7=üÄWj´¥&Üÿôsñã¾¥Ùed  Ä,2K/þKÍ2È{´°{ò™œ¡Æþüùû×{/§?åk£AÖÀ›N•lW&È…DÞq9¡GÎð1éVHµ€¨v–n¹·iž/ÃF+˓묛f¯yÑRé¼+ÁT Yoi6œ &а9<Ù@ò¼±,`=ÄÏífÑ$¶ÒWv¯BNêI¦:óâmÕbÍ$;râPj@$û¿K õ‚CÁФ2Ô„"Ïç \çÆºÄj}Sð۹ߖùåG©ßÿ5ó׿ç?û®rüÏæ¥þž£Wp:ƒ×ÄøTÇ“þJ†¹ýü)ü(ºçÁäêj|ˆÜ?WR’àÿô^9ò£G‡ëТ/-µ2K-8ÅLbû#g¢Î™›Ì’ÜßYg@ÂÙdk Â7&#÷gêË¢3Žˆž)pãdX«§ZªæÚj•;é;kf;)¥;)'‹”S,Õ‘sZÈ:.T8f¡vD¤µÇ’­'ã¢@vzé}ÐzföIçÜ`ÓÔõ4¦·èHÂ# s_ i¹ôLG-ØdMV,¿h~¹Ô—õåѪáß275ðEßos#¹_ý¼cÛÅP¹Ã–ä¤-Ù HœCzRW1#×1ȇ vÔ|]²«c‡Ë%t,Šé>Ú(/Y¼ƒR<9GT¶™“PØk 7J…øâY`Bv¯åŸ‰w®)\÷úéŸ?-ú½i¡Ñi7^Š{^Ž÷â1?Ù59Ñ*ožjBz ¬Aqy?¬Ûá!bhàd^lË)²Ó®ô6Ëvæ!p3Å™Ïót;<¶. å.š(§‹´³¬ØÉE˜=Ê;Ãc6R=f¬yT¤É!ÎÊäø:Á“`VÖ†Ä7pŸh:ùH-3Ð8ñUÎùÖ97©47ÅlÅ\+@©À1ã@—´*Ó•Uì«Rè-Ÿç.M90HÏ¡Ïz,Øga•º)å;ÈàïÕ ˜äY5‰“ ä¸<9 o Ø*4<˜ |2÷"£÷³üÊ’£÷.´²w5‡µ[S6@Ž’ÞÚOî×à ²Ù¹‰ÒœžÆê˜gI~*g,d’Ì•ƒ…D„ÛžæI^Ð ÊWûéXjÄ6Dº ’nÎH$I$K!= Ñ.Z…®œ'5¤~$[ÉâÆCPŠ­d2:,‰$‰‹Ž’pUMu”Dë…º 1#LzÑáSŸ1!ñ†ä(0C;ÉsNr'dSõÎzqÎúèïwÒZmÄrÃ' ñ~ðÉGƒ¿ý>+¦bÔØF,ÒØÊ…Å³ÿîþ„ݨ5y/Ò¨å©éù¡þ—ý?ûðÝâ´œØÀcvüsvzù¾fµá™vúù.FEî&yÎF6¼‹–:g5OðUŽó”÷sdö²¥qåO«ž‚»™úyKv’HÑx4ÏLã´¡üI¾ôy Ï/déÖáuVê†ø<g”8ë4ø›^‰t)t6Èòñ Õ'¹‡<ÿø¤pâ„~Tô´‚ÕP­ß?ºšZ¸µ Ô ÎÀ\l:@ÇP ÍÔN5LÍw œ¶ùãO'Z'Çz°f‡{70ó¼kv ‰™0žj¿ÿ¸páEïôDè4‹ØtóoÞL I]ËÝá®;…Ƭµâû­3#ÝxÃÐ@%xt8ȬŒÂ{˜‘®ù‡Í0¡b†ßdÜʵÙç pp-÷‹S™ùwúÿ–›¢í©}B }-.*ª®ÌY¾ü9#ÙSf2§E ¼;o­sÖ†sT¤¶G(¿‘O¢t¬•‰¸%¯¹ÿöQÑBçìpæé˜ìÃÚßó¿”jW êK ¸#ãÅð¼~žGÃAf[*.Ú¯ó"yg=¿ü žZÔðyÝ× ›zÛuî¶©e¯1~¯hÛ¯¯'_ÍŽ);d[¾ÝªeÓåužï\ýîá˜êh›7Î9o Áü( åÓÉ{€€ÄØKÆÇð‹Ï´•núðÃK_ß+yø¤±ï˜Õ #=KCÌ‚%×ìóúÉþšñá:Àìâ™æáù–±¹Öþ'%8@}Þ¾;=’“篃t!xõ§™iç)îÑ–ÚóL¾ÌQ!딩B‚©B²¹jª¥n†?C¤{ÑDýœë Oîœ@å¼;Íݼ(Ê=1X„Ìß“„_ÝM~Z·8ÓÞÿ4ol ˆv5K7™ûµu¬·Ywl²¿›ôµŽ¯<ºá¸ØfZ*,æÃ8~Œ5Áºuaº «ô›#xX­«x »(”¹«Vº«U¸©V¸œT¯qÑ­s2©²5-˜êä 5rLÔ E¥ö:eNz|©p3ªò4¯ö²¬ò¶­ô´+÷¶Ëö0Í÷³.õvH´¬Ö‘ÞéÍ{ÿFòÈ`\…Ç†Š§ÇJaļ¼ÎLßIôѨµ¸h®™bÁ¹d®uA rš/wš/sÞH6É„Uécˆý‚U즗n¥’j©R袇Lêÿ¬† «¶UÑÎ-«ìëB¬Ú¢ZV;4GÛѵڦiµMóëÆµ¢†5õk„1¦A(ë1‚®µÆÝ±ÂîX“+›­¯lwèÜêX¿Áº|­UQŒ(UR¨Yö·ÔX÷XKv êŽ@×Ú´¤þ_šèXœ¦é”š¥ÿ«Á{ççgQdð¯@^XXZœ›{ôý7ƒß}õðý;o¤®;ë$óüÝŸ/gæmrCÞô[ÅÄýÎ?}”õ½¿4ÇŸóֺ䬓d§{ÚLû¨!û„笱Ö¡ú ‘ú {Îök¦ìí¦ªqÙ(.ñ×&žâŽiµ“üÑ ¦o´Æü Åt3ÿ>õåÒ¡ÿh zèOL¡º¢£ƒ:lp8oz¾<Þ™é:(ÂsöQ϶3â<á¤ÇƒŸòtýËLt\±v¯9yG¶j“H5²ÅŒì´SXÅ#Úd#ð}Ù²d úX6y)<ÈÙÄ-«Ä¡MFƒ®7½öõÝKý?V÷µ?îošéŸë·Ž5-Œ·¿DÓóКÍã8S õètÃàTÝ z-0ï½Y¶¡(΢4Ì ínŠ…N¢Pï¬9÷€½ÛPm‰f¼ç¤H;ѽ ?ÑŒ›(Ô¿h¦—&Ò˶ãá—ï.Ìó´¼`g°Ó@Ѯ’$c¯ËÐÏõK£=COË_‚á?ßD‡ò£íß_=°Ë†làÚB£y>¬LÍ~å|@‹ìFà¬*5Þ§A(ƒ±$º.šjÚ·§#O$ d2ÛB*Ý„j’ûKY| 9Bv‘ˆSh­•Ø5‘jŽH)ÓRh­Š\¹%Æ%î¦N¦Y6‚kƒ v¼Lä`:[3ÓT!{CÌþòfæ4/“íi+C³Ju÷O{wÂnÎA?ÁL÷‚@'A¨•l®a«ŸíÀËsÒÏsÒ]Ù/ƨ8]¦Z+‚éšãÈ®òž5dá+œæ±NêËÇkIÂEp«,Ù(Mâ/ŽÿµÖ˺äÉN6ÙóI$YÃ- úštÔ¢ Ø.m²C6h‘Õˆ3V'!ÄK™†ŸZËC0µ¼î´Ö?ý釅‰±éñ±…t2ˆuZ&¼wÐ…)¯æßüÓo¬¼D¾W\>šà=7G‰ÅüvûZë±}GìMcaÂ,G‰‘$kdhpòÁå$bå%# ®ÄŠ’"A„®@BcÄñ"RЬ•"1ø'ø>eW’ˆñéÐø(ÈÁ6?ÁXå0(C g¢¡L_¦ÒÉ(×J¿"Ð&3Ør£…üZ{Ù7±Ý—hÂI2c”TƒƒQ€±p’A©¡adÃô+ñ+¥¦úñxù31B“Ë\¼ ß´ÃAÎé84n#Õ cu‹+žr„ƒiz5;Û™ƒÜä¡næqorˆvš7{³ñP ÁäZÑNñ½&dõ2ÌûÌü ĺ1 ·˜™Û ßW]MŽÚm#»jhž$¢p‚äˆ Ì¢ã¬ú¦ŽYþ¼3+.΄â ÈDχ‚8õh,äª+( ¬*å*G¬1— vª$TD®æ¦um-‰ûëísý6LtŒöW#ºÖ[â¡|:‡¢f¨µò•šŠ™éš™ùÆ1BñµÆÜýã½Ä¤­Fî’Ÿzÿ—Zä# ?Ä ñ;·Ï•®ãµn¶€(•Uv„mæjg¬ìǬ › †»õYë‘XÁ!…ÃÐo{ çàzãŒs~µ¥±ïÜ:ùó7…Æž·ÒFq¤íY_å ÕÇÅÃh`[5-M4̘¥WTÈ`a €íðP5óòÎ\_g¬ñQ$±J$NŽlGôĬ1ܺÔß4ÛçeQf¨C‚¿M¼«ÙN;A_ÃKOm­“Õ‘ 1]%O~ùitðùø˜xjfrvµf™žž–T ¬f•çßø}ÂÞp ÞÑ%Òçðnðqæ§æ¦úGýýáûïÝÉNK_åÈ’Ôœ{ÐHë”ÿ¬•Ñ1îaþAC½jJ¨-Á„øÁ*»I’D*Ê­UWÞ¢ÇÞgĽ 2;kbtÖ(}ô6‰·Õ+t7Àêœ@æ¬JM–•:Â:q§z‡$Ù¦DÖª79b!Ošr6ˆ¶àÈ€3 ÐTÔŠ…Ž6NŽ7™ÁÓaÏñ¡j46xƒxº²®ršé˜YjÅÐs±¯jñQ9¬$à-ƒèR̈)7Z>9X0öÖ,k˜k˜ª¥1|#u0ñ@ $ ‚ÓðP‘x¤/ ã€ue2X½,®Y7QPkþýCa:HW?(PÚ¦,¹_S}EØ“W”)‰E¹UÖvmõCz –çŒÎôpÿ$šë$Z"ßÖ ÛÃ|¿¡Ê*%â%K»š’ãþÏ¿«B žG𲃑)ÂvU^Î\ɪ‰±MŽ®öÉ‘]Òd‹Ù$M6È“Í ´|-|MŽþíqMÉSi ª’#Êtæp&Õ@:S “e,›-”É6‘G‘¹À“…†Ïi} ¶ä³Ô²Óé¡—çÎÜ”jÇKDKo¬s„ÇÙ¯§¶Û@娵þn=•í\5Erf‹Çç+›¼ñøIžný…p/g^¼½ô{g¼­F<_=ÉÂøœ€wÎP7ÉD?Ù\÷¢9;ÕZ+ÛQ;Ñ\ᬱ8´‰fRé¶ŠvJxq‚K6J‘ ôãlG#оE’¬“ u´x¾ãA¿^–l%±2´nÇHÐ>aÙ(Cß¼MŽ<Ù±òâ ¶$ZMj1JÄOšÆ^8Iwù(KÓ->µ©—>¾}󷯿î.Ï.ÍaÍ¿X@¾ áÖ™_j€ ¿:=¡ÂÌ/¿@;Cùf¶» õå|_ÿð×_¾™›qÔÙ*BŽDKÓ°Vš~Ï«¾‰íš*[ÔU¢¥¥VˈIÐ~æŸ åkd¥ðÙQ¥7੄·M’ä (mÊåîFåî•>Æ87Õy—9éZkç³/ò”/ÕÎ;èræ€Ç[|&è‹»ÉÈ©aÅèµöÅ“V› êçðê…RƒÝgoT›§C…PøÒ¡çX3P7û´b| zRLÙÅhЧ«¦pÚ*›-]±|Y ç)fþö½›§ÓW¤øé·V>i¦§Kªwx1?^eßyôvæ%›N­ÖÚîJöyÊ®’²ÃLzµæÊƒXŠ„(kBDÊ$?y]ßýKC6B;Ê"Ñ2´ŸYE$£‰t‘À#;L铲¦xa¡s$æ“›Ù¿´‹Ÿ´ =m¢adÓ3ý5ýCå ÍD÷¥OÏR3a4rhêPTñañÁ±ÐÞ¼œæ -•‹ ȽÎ<ºÖs:x›¤;4—mûá›â¡!´:wýNnÄx·ÞMNeþœ qálIL»ÂÙÒkÍÕvE˜žØçÞP¼ûÝkÉ_ÜÍ~ômý|ßµÅËS[gûÛâ6;Ö:6R?4R½'NF(æ Us¹Wá»B÷… Ýäd ýоq¼v…‹Ãu/†™ÐwáŸóþˇ71· W£Ò‰-Š’»”é¦U%ÒxTù¬<°^u5k¥%×JЖ&mÙ¸rÿ ç?k¬žç-:m§À–É'µçBïÿ=ÎÆˆ„.žŸ,c LXºòëµ5ÛŒ;¶Zµ€ nѳÊîJ”So”K{„cÓ*;´4õ-á. ~ö宿%ÎÆ¥N‚bG~žH+ÓL%ÏJ£ØŽ ¿Ü‰Sf§IÃd-r,‘ù˜$€lV¹Õ˜eØ('šÉÕ'ñºô ²K•leѼ^Šnyìt´úѬ¢›U"¶’pmuþô¯…âÉýCõÀÓž?+d÷õ>ÿKñ)l.l¬8t5xàJQZÈ! „›+”yVxTzñk} êýŒj¼ù¥.ÚKå;Ë‚”L+ÇLnª%碩æ% L^¶½ ÀŬÔÓªÄÛºÈÓ"ßÍ4LK/Ór_“š³ÆQ[˜u{¸M[˜Ms¨mÃ*§ÚHü`÷ö&aÕP.çttD[zê»­-»qS|ÿþÌðÐÄððÒÂ"Œ:§æfQa°–h ¯Ì•`îùÒ÷h³ƒƒÓÊÂ)‹.Ô™ù—ˆ¸¤C©—óóƒ¿ÿ2üÕ§•çf†zúÛ×8–¸ØdZš$pÏpuÎðÏåqèhïÖPÇ×C:zµ´°â9ZÔTwÈHŸÔRKèZ™äY¦i%sÕ“ô”“tXyB­|Sv ;ÏX#Ç@)UGæ‚9£Dâ¥(iC¨x#éS®Z«$Êšüíúà$3PB¶Aõ3#¦tPÄ?!–JÇ+XÈ0ö EJÅ•xÈÎÕÐ`XŽ¡ ª1×:>X & ÎMسâÒ! Û³<ÞÅ öõW8æ*¿Ç”\;zÆ^锹DY ·aea ?Í›“n\e™lœbrÜS·½fœHe•‘”—qÑ$ÎúÄCB‚oß<'~|ÿå þ!+­#Fš‡uÔŽë°i±ª«îSWÙ£©ºMM>R‰8*5.ºö\û£gè:È´ÅÏ‚P75Y3Ž´©é:öö eŒLæ¼`j1dŸ™¨šŸªÁü‚ÐO¬èÙg/!ó¨›yT?øÎ…wÒCqDJñfnÚê%u¹aÛÇ•OŸãô„T讬µºHý–@£3\²“CÖJøÍA¼Ü„ÕuE[?û ý‡O³Þ«{Ü2?ص8ÜE‘®þàôì³²þ݉éþà`,4?X Ùà …ÿÔ Dš‹sSïñ·»Y‰gû9R;¯É“פÈVT]BvÈÐÑÛv²Yž¬Ã™T–ž4ѽl…TDA„¢ÝxX1e‰d]å4¾jŽ€]*âÖ:×;7:79 š ö¼jK­c•|¾\޾t<¨û€æºprƒìå·:IÝ(Ýòü‡ÊÙÁÎåé+‹`šM6Ct< C WRBð+œ•šCô¨Bó#4 V6™\Ù(5˜Pô?Îe^t0LW__áðhõÒbw?ˆ4ÌÛóvûHŸT:ê¡Â|ß{Ì^¡Ø_¯I»f•Á‚/n²³Öi{öa­BV˜.EÌl”ˆ›x›‘M!Ü#¯ÙU×m¬¨[ûÝ—¹ÌÄ[ÌÐÝFd3›Â ¸\»VÖvB3w+m,É"Nx†}ývîËÑÛ/@n™í˜1Î8¢$+&FKRoœ´ ùƒCÏSgÆJ—ay &ÆT#â혱6¦¿‰yPû俉¿æF×n5É QÏ TOñRŒ7!QšdOâ·Žô571Ì ”šŒpe Y*ìÕÓŒåN˜²RW[µçïxçVÊ_–=xÔúìQ-¼È&0‰{1ÙþbÍ^ÓâxýÈ3Huj^ÎbÊßùb¡ 4ž™©,jî—0œI§êæa³9p©»žºÇÏßdæn3cW™É[@Ú¿ýLê#@Ž (®“[9¼xQ£·â›%ÈvœpºYy±³cE²S–þÉ5ùD}ÅtCE„8WX!e•Wç*¨÷±Méí6TÓ£!1ìÙ%ó-cãå°e@ö/±¦†:«ÕaYï._b#O¤lK‰4s©T+9@²UóœÔ/™I@€mMý=J\Ô±2Eò`ª”¹iN Kü6ÇV)yö¬b6¡°ò]Ô³”ñŸºh.7RE ¨Br't¥àsá`"I^“!»Â(“`EbDÈéÝN_~^ ž¹úp¸ö§ûÙ/—{û~ÌeFo\²s;‚ôidö 5XÒø*9ÍR+½Z£z'£F'ƒfÃÃFG^•§T¨Œý’Í“)³Ò,µÖ¬qÒkõ6ÆjpãƒðÓäÉï2kõ1hp׫rÔ,°”õ d“q냴ë4j}•ky044 ‹a²777‡ãêÌ«_ÿ³ŠüG¿CKC»Ÿ•_^¾@t ~!•°ðÌÌÀwß|ÑÙØ~hG¦· ÜÅŽzù6ÊÐmåÙ)ç:ªå¹°sÝ9¹º9^z—5ÓÙ©v)Öª¶jùNœWm:¿ö2®÷4.wÔÏ2UN5½Ä“LF8©6¹¤G]5 Ld Me M$±J-dk­ëÌ•ó Ø×¥”ƒºd½ À8Xù×» 3ë0Ý Npø.ž„º+q«¯¬•‡69¯^P—< ¾âJ,4ö°œ¯šŸ«Ã‰i`0bpÀ•ÌtØSÔ–³¯;ÿ°]|{•Ę‘Oj1ß^.Œµ©Š±)äåŸwçl6ñc%j:("›ƒ9§vÙ§EÝî>ñÕG™?|™þì’…ç­ÌøëÌð[9áü†(›\[íb^™¿Ø‚—oªW`É+´3ʶ៴Ñ2’8¶ÓùÛ ÁX?À¬l¨/Ójˆ©Qs0ž€…ûº—y˜¡¨Cu… š=ü DEöª¶Ê‹ÄÝ^ì¨R梊UâªZá­Qê­¾²TK½U‹<se3ì$O!åÇä[éG[ œµs8Ù¶ÚÉ&š)¶Ü3özÛ-ájuÜëûïŠÅ3=ÓM‹Ìeœ¡¦W2Û†ßɺè­]è'¨ñþë~Àð’¾Äÿk¿˜H¿Ú/•6ŠYB LÇ  ;ª¹"]gµHˆ‘f²EÖ `Š‚aØè­uy•àÎ:«7ãœÚÂAÄ5«õå•»jÛÊ勤rE ä(xîá±Ñ {}Ð^'~ðËøàã‰ñáÙÙéùù…ä ‡ð¶Zñ þ Ì¿ (0‚éyk™.ཆÍÔÐ03>>ñÓŸ7ÕôÄïjŽ ìŒro DJ Nµ·^­?¯Ò—[⥗뮓ƒåŽh§‘h£vÁR霩|‚ë¢9+ÙŒ•l"p®¥J²ÄtÐcbbÓÎùÊ'y°¬IUØÊÔ9+vøjÝæe}Í‹ßéÎmðÐFÔ`Ž·f¢·Ò©…÷«7‰¿+˜ï«{Tˆ­·0 }e>¤ƒZó/‹öXÐ?â+˜üÔafeMÑ¢Ðcv©zl¼ g¨e1èë½Ìó­´]¾+ؤ ÒHåÙ°ûwK˜GoŸ 4<îȆ£øØˆ¦Šä’},$ÑNR;#µÏî·«-^÷î­Ó°Š;Ößµ4y  ³¼ü½iù³ªwÕúÕ¶ùŽúy"Ý\st#vW%Ñ@-Q¨yÚH}‡,„™ˆføñ ð+zpâÀaäÅ,웇çQ iùæ¹Ç• Ïp"€°cÜŽåﲿ®ßÐoU¿É c‹ùåMVk,^ßáùúN¿ÞÍ^eaÖ§8›ùÒÑ:Ó™„÷¨{ú¼}|ê*³xç壶x+’m«Xç¤_bÍ9Ä“:î¡s§rÿÐï]ã—‡úZA·ìŒÄš}9)kùèh̦p Ã‘ÕVÏp!ƒurîÄO ™N‘ñ:ÚeM£ËBDB#ó¸îç+û›ŽÚd®Ö¬ßnҽϮ}§¨m«Eç«¶5¦áÂj~™—n™»>XU%NúEºùV:Ù"bëS„ªéBµt3 õt¡F¡7Ï”“Ê“=È Îz¸y$ÏêIÅsd’œwX¨ïõÕÿò­Lñ@îëúG®Žô]_îCÿð¢~š£€ÜcŠ¤Ì‚]!âTÚRºo±•v®©z¦¡ž}ÖªÅêx€BþŒ¯x]`«–g­”­j°Æ«‚Sä \`§˜f!èjäh§YKÃ5³>T·!L¯.D¯-JØiҼʤ1TXdZd^dQ$*õµ(°Iñ3Ûç¬cÕŠŒ˜_¡a|ú«&fZû•PõÇÀÕ/ª$xpŠ„¹¶ìsÕd¾Ì«ýí"ä`’@÷ î—"‘t¹µL­“B»»7„w#Ê´'LÐê¯_ï©UëŽ(öƒËá¦WV™Ýб¾mŽ¿jðâ€#Ýä£ÂsS MµU©‹Q®m­L2t†` &XjîЗ_ËSm:ï½ÛÃ~¸?;7†¶Öyÿ¬x_¨6ÿü“ÿüÅ?¢Zèã¡ nZsÌÂÜòøø‹Á?Þ{§|Ïö½&º{´¤÷+’Ý’d—yM–ª-€]ã¬@“J, Ük$è €x€ïÐoE',Ip^Ø!AvIRÝ}‚>)±WoöáÁŶÎM½ÑK£ÉSQïM^ªZWƒ¹=îúκ]Þ†×#D—cl:7ØVǘq”ÈÝ,¸wëÓß ÍãòsçêF§+F‹Æ¦*1”¡_éªÀâëØ¥´¡ª`póÊn*Z:ºš¨œ[®ùíAÂŒà^¬ðc§ÞBAH ÖÝŒ´  ]3hüßí<;þëefî«p†y´2ñư4cWéeókªŠûø/)ß|›ûçƒÊg}µý}uƒýuÃÍÓ£0Ôº<ý¼söxþ¿ø®+B“„Ë’ÕÒ$vÞC!uŒ>Ã刋(ô !÷ïÕ.N]â:9¼·v~UY-/Ÿ¶0C½ÌÜ[t¨ôGåÀ» —;ß<àvuÓÕÝÎoò{ÿdÄAmqΧ¬”Ž˜³^Ho1”Ü` k(× 3$uëŸßïxü þü×0阾W}nT’'`A¼E…Ä€‚cøùDfâæÄ lë°²*!$‡.,öç}´“Ÿü‹©& dsÈ¢(úfâ*3~ƒ½Š=õeÉgõ»ùwÚK13D¯ ”_"ïìVPgÁiÁ±wH”ˆÄ)PLx#f +×ã]€¢¸2Ìñ'xÛÚ•iÂNBö²£²Ôe®ÂQÎëšæ{˜$¸í´ÖØâªùñí”ñ‘«Ožµ/.~Ì,~ÍŒ}Æüpk§Ô^M’ÄS,é¦jËS¡f øODŒ5‹œV¤Œ_̘RŒ¤²ÌXØwˆ—Mâá·Ø€Øé&ry"È¥’_ù¨pq”[!´kÚä‚4aHòØX¥®Ü<;NŠ©Êi]iÞ÷€*,C¶ÉÐùõŒù »S!æò$ïBÄýŸ›‡‡¯,-¿Ùß.Ðefì63õ×`:ßÇØLþ±eö­ì—Rv£âÓjM$ó6yiÑå©Ñì¥Ñ ÓƃԢ=ØçkDomr~k“ëõ›Ž`“b;µw]XUºpª\µa UïÅ-qÐɱ2J3$ ´/è²€çœP'Ç5È-²W[jƒ¦TœPû£†ò¾o>~üËèè“ef–šÌüK©yUXþ¿òŸZ´Pg^•$íb!Ú1 ˆÝ}úè‹ÞŽc~nÉÐàaœ aÜ¡ˆCºëOsà¦Lñ|̦Œð‰ß«çAæAÒ -2´€¯, Ó8x¾œGäwÍäÓ²ù>Øt´23= äÌó˃w3®&Ÿ TGz×&@Íä€19l@90û•éÔ&*û 9àŽ€;'³OOi¿®òv5YÜT˜ÑlU”ßÃÖļ{Ka´ ¾¾¦©qHO÷˜cßlBSÍRs +õ&Wž !œZBDÕÁÖà‡kµ"é¯]Xër¶l¢Yñ›ÌÌgÌøgók=j¡@oHüO›NކYÃ6 VÙÆ¬bºÜh`9$ÕHþ"OÎKÀWaÂí'‘ñP8êÐxkH‰aÙ„‚mìI¨r´é¤;Ä?Uú‘- À+piŸ"Á‚d›åU©‰•¯†Dk71“!•Ék'žÝd?œ¹ÆÌ¾Á,ÿ…y{þÓüKÂl =MÓͱ_tÐÚèÕ¸ :ƒ­{Âl;C,;LÚ¨9;ºM^ÚØ2Ø/85øèT¹±‹í•Œ‹’RïůpÖαPÎ*fš(Á¯Ÿ+AOâ•51÷˜jm“¤|,”ñd' 29ª<³/TSþ󎆑_¿Ÿí[X‡¼‰²1#ZùõŸÔ“ó´B´úGW³°ÒÒÌ¢Ô,ŒŽÌ>}8ðùÇŽg„{V­ò(÷4ƒœÞG­|'0³uÓtSíõR¸©Ž§M5ŽñÏ 5Ò­¹¹ŽF¥Î¦•Î ö­¾.íþ..Ý!.mþ6uB 7Tüb¥Zl£Rf¯^î ^b«’o¥tIÈʰTÃÏn¿*9Ćs 9m¡´MŸ„«’^Êï7Xèë¹ÊàHóøBÏh¨SµÿZj†gʰPjüˆüGXf¤‡>º Œ6½ÇD¸·ï7ÞcžÞ„­ôf-rÒ‘½ÓFÙY“¬öP½Út¬ÿA÷üÈÍé'xÏPõcÞ\ÐÛn¦ï~ö×꥾ק‡® vˆ':Ô N4L͵-̵Qädiž{ÍuæÏ¦‰»©7Ny·m·O‡ç;ÇA;Ï›e£šÄyMX.$:ò@ˆÝ&dÅX)¼Û~nê·fð63ý3u›™¸³ü[óÐGÅoänÍÝ䘿Á©n_Ív÷ìÓ‹^ܳ–z§M¹{ô”£‰—$q•%n éCâƒÆ,ΰ$%èzÃöÏß:Ó÷cé<ÔŽCmsÓã3­pÆŒü—º5 ô H‰‰L‘…b¶+ûŒ‡Ú^¹”íFŸvî]|‚øïjŒ–°Æ &«¡6XC¤Ò®Þ("È/3ãW˜ß«ß:ñ·’5¹Ñì̵$O™6$fPæP Ë乩Uú馚Iã'{gÓÛ1Î=>¦­€sô $S­”Á}-ñ0º +Lfõsl Œ5NqYªéVº9öü|'ÃRã GA“»Y‡y§¯iO Éå`³vaÿv›: W yl^ü Šæ ;O>.Çœëÿáí=Àã¬Îìñozï½÷ÑŒF½WËÝÆØ€¦Ü»$«÷Þ{ïŲåÆ!lØ6! é¡p¯²e¹{~çŽ !»löÿ߇¬žû|Œd!éÞ¹÷½o9ç¼ÿ |"+µ(M´^ƒ214ŠYå "÷¯+N‰T „þÕyø:Py:F64aèÄ0–Y8øâlYCðy‚…p:£íÑrÔªÀÆŒcPÎÁÉ‚ÄP©“¹ H£ŒÌw.ð Ïõö%»;b­ ÑÆêsYŠeg¤üY/çÉî/öú¯¼í?€ÖÓšúUßñ—+Úöµ-õtÌó tÕëJŠžHKÜ3/arAÂþE ó"Ý]Ñ&ä¯ aˆÅ÷®(9ÎKKˆG¦ÊÍ ÜB–ú?Œb'WC4†*^€;€Uy!°ŠZË¡¶àº‘”Àš7±3òGUì…rÞ¯î9ó§ßÞ˜:wÇcú64¯H…bCþ?þªÛ„Ü Ãu÷&iÔrû:éwysæÞµËSŸüÙOßêÍØ—Õ´8€ð´ °JÛãu05µ‘ÊÊ0y‰O‘ï‘e»¥ ¥lÖ0ŸPëÑ`]Åó}“˜À¢²¤üÐ2–·µÑi©¶kŠt €ÃÒZà'´ÁuÃ%/à=‘Þa¤+žc㼔Ê7[¨§õÔ\UµÚûù;ÍÀx€2|óúá3gÞ@ºLÃ@ôpfàÏÀ½™æ Ùà>`-ÀL¼ í…}„T8}Ìû'Ÿ|œ³ \Ù²(tGŒ.VHÍa=÷ú±’[3¯Ÿú¨ŒWo|ÒÕ±-¤j¹«jiðdæòÏßh¹ûù —>;ýy?T+!rçÎ>"çrUò½è–ëÿ¸ÿίš¡.×½Ò4òhPUœ°± kUlåT»$zn¶’½UL[‡[OÎ\¯b¯R iTáÒðSoúÏýÄñÇþÏ|t¬ü@ñý›S$¢Å m¦0‰}@NÍgRóhÔ (y¼T-‹_1\j¾…zfž.ãéàΪåû†Ÿ}çíüþÒòå]'NuEP´„ºoj†ôìößÛ÷›½YôÃ'}‡çZ^}0|ü¡ÐÖÝùKµÏ'л7…]~¯éö™¬* â¸ihôŸ™ð_<è?>vòµüW Ó:5t€ ü¤{ßjב5!/?và1÷àbMß\ÅèãÁ‡<ß2æûÇ[¹g®u(Z}`®ã…žžåpªÒ+È࣠#LGQb—Ù¥ Ú.µìE z8 Ø?Ù F¹QØä–7{¤ÍnQ½ƒS9ôÚS«ÙÔCLêßJñŸ}ùÚÏÊ?>¶¡y£êyt˜K5= ê¼_©–Vè_Ya;´Èpìï+«B¯ |È;´ÄÞ=×О¢mO4ŒÍ n7c4‚¡ç5û´¸ ½qÖŽH]{„Þu¢©?Ñ€1bèMÑ Ï7-²Í×÷¤ÈûÓT“Ëí¯¬áá`ŒC…î_áÛ³Ôƒ3˜æìNq¶Ï ‚bØ&'o[˜¬ê°éŸMø/ýü¿÷­[ý°›zÒEmò2yÓÄcb§ˆ¹K&(Õkë¶·­Þ…ó¢+Ô p^ï݈bÒ j3Ú‚ãÿ$à–l…¢…š*0ÀõâTaæÃrªh02kp¾ Ø‹N­…RŸÚ¥¡ZÙu!’žÍØ<Óäd{÷>Õ½427Þót°íÃï;õûß]>{0à«Àè}›ùßY0¦Ð&*PìÆ1nÎ ÷MO½}åÜÅÿðîÁÑÞO¬R=§a‚Ä”cf(YëÔ³lŠ>Ť§V0˜@-"EC§!KƒÚ.Ö„ÏŒÿÊ„™|V:ŸÚÊ¢6Ј+ Vl‘–VeãÖ¹øõnA½.ü’`nE¤´>VÕ«®TA £Ê+F’y—ƒ¾ÃCÍSìË›÷ÙÖÏ—\;Ñ䨕ó­(C:æÖõþ™«Ýh2{cšˆ*\<Õ‹šÎ¤Ý¦ŽøÏ¾­ÿÚ;×~wàPéS…sìe ö¢*\â{0Z\]úÀÏ~ÙqñÚ+Ç¿D³d8¯ÿ¹r"Ë·ocRQ¼²~™ãý‘7ÞïŸùkßÓc €“„íÕ=„ÀxvÏ©7‹å¦T/‘•&‰ŠCÅÙV6dKQë$I-\+ U6P´õcƒ³ŽÉ_Ãä=É䬢X+èÔ»uE7Þ:pü@Ký范þ°‘zPG­ÔQÏ8¥›C ›CMklrè€b¿ŠŠf™OüÜ<{ÉšäÝÕOþô…ÂÚ|æÏ§?é;w¢ïüÅÁ3Ó½'gºÏt|1 #Ó‰6y§/ÔݽÙã?Ýôñ‹Ïüû®¸wׯ¾8ÇttihU„|›—“=G‹Vh[¢9‡K—]úèG!szçÄðî}:ê¿pø?FÖîËJêzÜ6ñ\Ø«[’Ž®‰:üDøëÏÆ~À~¶e©éØ£Áo>2¥÷/± $¨àíO.qM,pô%èq믖•»äÌM85¸[qppÙ‰uxäP¾÷ß@Ëí<ûd‰¨t {±¯hT‡Û'CCÛÝkº—3çŽ<ÍÝ¿Q~,Ãòv¦û­MÖ7Ÿ6þð ÓÛÛô¸ûèJGOŠ´2’]'®JTEŠóB„Å!©€«`lòªŠJ»¼9ÔÐcõÆ;“]có<{‡ì¿/tÿ2ß¾û‚÷.q-²"ê_d}iUèkÏD¿üDØþì{–™¡Ú7Œ´É}øÔ´Âð“%5>ØLÏKÀè>)£g‡[Š’Üë‚$K…ÔJe}LL­SëpA®“¢mg²fÏ ö ÎK†€ñ÷óÂùûyÎ'’KXpÕ›ByíQÒŽ(`¥M!b¸jÕ.(¤—›C”íÚîã@¢ …­dËževôãxíéàןò¼ñ”çOùŽ­=ðhxﲈuvéê óû/üõÃS7¾8yn&Î 85ÿùñmèøÚu¨}‚’€®s~B˜ºç¿ Ç ãÆÍ«wn^™9sü㟾ùJciÞü˜G´ÇøÔó°s „   y`¤‚rÄó)²OuŸ§hh´ .+‚Ãl*K@Ï—² •(%0À¾/P¡”ÀlÂav8Ù€tñØ<ËØÛè}ÖñŶ‘yDAº?^Ý«êU^Ò'J‡vb땼ä³oäù?$2Ú—ï^è¹~ªíògõ>­ú¢é&ø/—GNÿ¥‰à:îüàî™—ü×Þößy»*Ö$­‰P¯2òð.#™GQ)dR^ðÀ{¿¸póõ_þ¹ÚK_´ÝCm壺5j'òª‚pšøWýÏúÏB-§ý&ÿôòÖîÍA[ã¨ÕêQõ$<: µ;‡ûU>Ç'›gEÃýÛû$E[MÑŸ XSìÇhìGL0ŸÑòÖZ„Ï[XÜœõös.ÖsAü•Úc&þj‹l•A´BÉX*¡–+©ûÍÔöG‚‹vÌo}ößýåÖ“ï^þ`ôÚçãÓ_ŒþwåÚ‹›î;9ÝuüjÛçSÍg/5ܘé¸qÀ!ܧ]¿h_ðÆsîï-³î‹Õ¼º,fhqdíÜ ŠE¾uÁ¢Õ(˜ÆË?£†$*¯½  ÿ¯ã¿œØšwŸ @£ÌxÎó6jšJ·RÅNÒM>òÈZª Oè}¨¶ f‡‡[=+Ç9DÐ:ƒ‚rk%` ü†¯÷.£Ù’Á +ƒmƒ1ûE|=ðO´µ Ú>{ —§pµ|Ñd.!HLåK¨áhÅdªöÕ¬o>eyõ1éÖ©~´E±ûAêà£44¼~}•ð­UÊw3üèaã«+Œ/®°ßoï\h®J5Çk³#ÕÛ‚¤[Œü*N¦Œ…p IH(~çªÙ«#“Œ ÎÃ6dx pÈB» #= ¢’(Á qÊúoù’ÀAëô}ГÔ1"äjžgSÏc3°©§Y{†ò8€í‘þþ@ n Rß[ú¦h™@-B 7 R‘'c*Yù Z–tö¼Ð‚øˆIq(SôP2›gسذw‰qr‘atžv(YÑ›(í‹—‡ŽDèø|óhª~0E‹'TÐ.uZa‡¼çþä/¯R¿ú¸öµ'ô¯?aF³­ñåÖ¾e!;B´ë£¼oöõ]øËÇwgî½0CºQ’ õ·|üfåÛþß45027!ÿy Ìk×o\AÈvíËŽÿøõ#U¹yiËé èÙ$аÓñI˜›@c1w(øÛdÜ2~¶Z\ •çk¤…Y™^Z g`WäŠ(€Ë5ôJ#«ÊÄ@¢ÎÎl­!êÇÅ”¤Brxb®nï]o»7†ÑÅì‹` „1‡ÂXãѽ‰’ñ8á¡EšÐCï´>ê].x·:íÊÒý£ßñ(z@ûoî# (È!œãqû ÔIzöÜûjÓ®G#æX¨…V"ZĦ²©XŠŠƒ/Ù| có'ØýåÙC>1xÅ] @î\ŸÿU΃qs!¦£ž€yŒ£~\½øW=npd&S9IT~"*å•Ѫ`~©•¯§e#ò5Ò² à¸vªéDXOJÛ"¥ïTò¶)x›eœM îV­h»Q¶Ã¢ØaUgYžU ž×ñ³ÂµÙÑt¥y4 @€¢…2j®?;7c³û}G›ž}÷XÎ~Vvê¯èLHè÷ݹ¸ƒÈ×\»~qlæÒ(pÑ—I’ô“½p¶¨€/?+¹u®Ù¼ápºûíç‚߸ϱ?Ú¸;Î]î1>£`? ¤ {û(΂Œz»níÏû¶ôoHzÜI=b¦¶„s²bÅ%)êõj“ŽJ×íD(HA^IUi¨"ˆåЍJ9U£¡ÊTDì…TŽ ­y¸å÷:Äù6Q¦‘» u=k‡‘“¡ìÒ wéÅé:áv•`‹œ·IÊÛ(ánU·ÈxîÔˆ3õ²]Y¶I™oVgÈD™b^ºˆ™#e–¨9ÕnžU«¥Õj©=1êÉXÉKi²cKÄ/Σ^^F½¼œ:º’vô!Æ«+Xo¬þd¥æ•¦W¨†bÍaü–]yŒÄ«gu¼gõ¢Õ*áà}}oÂÜmÂ„Š‹Ì’<=o—Š‘©¢02”T¦š(–Ãf¢ Ñ.%•m(Ýh[v"”ÄW‰¤1·!Å)(µðòu¬l#GÅÉÑâç° Œ¢›<Ï %|+åíP2wȘ;D´L!½@Î+“‹Ê¥ÂJ™¨N-.–Ñp^²ÅÀÀ¥Z '¥ÊL,¤ÎÁhò²;#ýqÔPFç(wÏWïY¨î‰G+zw ­#ŠÂ³?=ű¿ÚûôÔÏËünó:ä?3IZžý®×ÿûá×[žÚ¹P¿ÔNÅÈHò?nŒž·@Ä@¢#ôaˆ =~¸Çï œ9±ïÌ¥ýfö I% ÷A@>ÞõzVÈÇôCsÑiW ß»<„ÊtR‰Tû2IÝ|Nãši»ék"„á ³SÑmMk¸(äë#,¶Hó¢°s´\³´À¡„kãPUƆnsh6Ùåël¼‡¤í Ï Oš”Zåæ?9œóè÷{vüöHåï´_ü]×í“}·NwÞXú @ŒmN¶^:Ӊ˫§‘c9?eèhÝ<E÷ñ›g†Á³¾;34uºÉ‹¤…÷<«;r¿®B@Ȇqbá!6)³‚°„!nÛûàæ¡že¦àäì —×/pÖϱÕ&šêâ m‰æî$kG´®3 õP´~$Z7® •öxE.^“SgaÖZ9¤¾ã‘ÔËêBUPKƒRäÑJÃTåaêšPhÇ›"ˆM±C‘g–æeùfE–Nœ¡`}rM’B›¬È.Çñ,³©š\¶F»¥ÁnhuúB-ѶÑHÓH˜~ȧxu¾ó…dõÑ4Å÷K_šC[D]H½¹JôƒGÄo>(yk…â'~„f@ ÒV7ÜÉm°Á̉/Øø02ðµ2U„Ðénr·DÚÛ£í­ÑVP¢Zcô@®¶Ç:ãõ] d¤»buÈ5Õxxª…Š`¹ƒÙ[ÀrBDDÞªßv03fl½§ïI[ßöÖ%iÒ^êù`ú(Åq© Í ÝE£¼ɤ–y„[¼ýråÉÏö;µÿ³ÏБä:Ež=‘8HÇÆ7û^\ã{á×îEæ:¤Kè}sä ”i¢¶TAs7ßIU¢WˆÍ¹8z#qŽPk“QÖlR¶Z´µ:y¹B\*V¨¥u-FN]©SU4õz÷àEžN¶Ý®y@F-“ y"—Z çaŽ|ÇRCoö4÷üå‘ò“¿šþlòú‰=3'F@½óy“ÿT£ÿBÛ½©¨©;„f¬ç®ôœ›ê;©ž H7O ßûbÔ|·ÿ‹±é/û€„Ac>hòûw< :²X3`¥uiÙ[1/ª±8z@³Ð/µH[ÅQ½‰×`7[´Mfž.CŸÏ<fðé{½ª·üPŠm"Vy0U}l©îÈBá+$¯? :´}ô>ñ÷–É¿¿LóæRãk‹L’4}a²2 {ƒˆÀuÝ…ÁzÖTˆ<¸¸4UånCc˜­%Æ öe{‚³;ÑÙJ¼}sƒóƒó,ÃsuýÉÒÎX~[»#É"$‹ëC¨Ú`ÒÎMèî å5BŽØÃ®rrkƒH ¨A™ùÞÀÐ KTÂF‹¦Z#­TIë ʳªÑ oÐK¢6«ðëó"ÀIŽP „J»}‚N/9&Ý¡üŽ0Ngç¥3œÝÉé cöD0G„ûæ)&ç*‡ãyý1ŒÁ8ÖîTÁh{÷ÎEÂCK„ûÑ`nûÅe‚#÷‹Ž.¿²œÿòý¼c+G— G“±½)h?.%®þÊ`×›“Óg.M_½}é XJßYE0„û„z÷mÿ]’¼"7W ùÎ+ÓSŸ~zñwï¿34Pº0±Ô#¡¾5µ@C ì@M—èYMŒL¹Sg59e6!¶S“™W‰,RªZFÕªéMN“‰]§eV©(ŒÕ`b4ZÙ°ámAü.Ÿx0LÖj ºŒÔ˜Wôb²éðûžxÃhœn<Õ|ø0@Ç¡\”e&êÐu±ìÆ$ºm¶.Óõ­rŒ>4ø°}t¥cò1ßžG}ƒËÝÖÌÙìâ¦1¨%ù1JB9”¢ì(èxå[ o[ÿ»_t<¹ïì…}¾~s«A²÷ÆKþSûƷŬòŒ.Ú½Ô"&‘ÿ‘Œ-6î½ß6qŸ©?UZí¥°ÙzähØį³ šÒf«¢^'nÒË›ôÊ­¼J)*ñ „œ© \%Áy©7¨ëôªF“ºÕ¦ÇÙ©V‰wÉÙK9T8îY@5[·&MÖ>ôî‘ÜS¿î¹úÉä•O÷^øtâ̧ƒ§?ëðitäá…6ÿù¶{—àú4¡ü\šé½p½*=®ôƒ ÒÍSCwO Ï/G¦?ïGØ ´Ç ÿûu“«Ôo¬0¿œpœ¥LJAë !µ Wã°ÏW¹+ÄTnçù….Y~¼ TUo.K2ìôrŸÖRÏ©ÌPvy‚´:QÖ˜¬ìJÓ÷Ï5ö$k›"%>ÊåNQ†Ï°J'Ž¢Sd€õÚUÉaƒÍ[@-üèƒÝ§ÏøääÀŸ¿h9hßÝÝþ»“7€Ê8wp|SÄ&4ŒQ—øTÅiU˜¬%^9¶Ø9±Ì9ºÐ²ÿ~ç¡Îáe>ÞzPâÂöy³—2VCT¡ f¬wÈêìÒ»¤Ò",5ñ1JÌü| û»d$›Z¤eªh•= Ùÿ¹Ž²5‰ã«ÞžÜõ—ŸÔ~ôõŸ½ß|ñøÐ¹/†.ŸžÚ{}f/´õ.M·Ÿ›ª9{¡úöí¡ÓCSºÏži;s¦íÜù¶ó—ºý¡ú Ý€Þ” =’öpW‡n]º3…þM/øï#N Þ|;÷ØsÖ=ÑŒ5jg·X¸•Áj(+GÛ󽯥ùÉL¹p›¶¼Z{—†"È¡äê§“ÜæZµ+}—ƒ™ãåEðJ#õI¼Îyò¾Eê‘û •IîýŽhÚÖ@·ýÑ,T“ƒ ›PoeU¢‹œØ9^CPcãU˜9å&v™‘ƒQj`£¥jLhUª¥•ª ¡DJ5«ùÍ2^ŒS'ç6hùõzA•–[¬b"Y )< Ô¶  ú}·F3[¢©Á¢ñÅ2ä(Æ“EÈïLjG¢Ä=`„¡¯ÔÉ+ÍÙ-þ*R±‘ÿÙ¥!S´–Ç)\TTamÖðH 0P³ÁF‚@_–•eª’ v‡ ¼Ü@2*ņ}÷YÜoG—Òž !r¢]² Lji çáƒÍZdb »@ŨF^]nf•¸‚[œ2Ì¢9LUˆ\·††*I6BZ Õ 5(EuR~\Ш#ª œÎßÏ‹„^"c–)Ø•j^µV0íôY1Æ"ýÁ†¿ZÃÀ?¦lspš¬T‡›ÞDµ +º‡‰æíMLQJïOàïOäîON&I‘/mŽÒä«T²ó9yä…™Ó'f®MA’ó[í ¾ømyßÿék$fBÝév`Z@ì”%¦o î¿wíÆÔ'Ÿ]ÿà£w''³R⋜©j½°Î$iq¨Û\ÚV‡®Þª©5« ü<)§\+ÁëZƒ¢J-©QKÊEüb63›Fí¢¨,:•Ã$ÏL¼fR¹*—O堈ɦv2¨t2rØÔ¶}ƒBI”P7ˆ3œjÄ’«µôÊ…¾ýÛݰdWŠ ú§L$ã—Sëõ¤8’n¡¶i©Mjê)õ¨˜(>¥ ¨¥fî†EÍ™OOteÿú×ýŸœ˜8wõÐù«“gv߸»çö1t>BÃú÷_X»ítá†EHqíÔ°¢lÖ!°[DÀJ©é€Áz¢èã |{ž‚jñÊû¢Ì­^M…YÖT…fN…K\çSÖ‡)ëBU!²2—?'×ÈÌ7£_0¯ÜÎCy„ó¬oí:÷›±ó†h´ã u ø1ѵ›>75ŽÚÎ_mº‹»€ÊÓ¨³ƒz€BgØ”vnw°t(J5§E»L$=zCECâápñx´|o"š§«&æš.q¢¼¡sÏ|2_gfç‹ËnJÞR8@ÅÁò+GFÏ‘ÀaU[È~¼ó­³µHaZ‰F“ìJ“ Ê.ûz?ðËœ²Ú`ÕW”R,޾®°Â&«vª+š"“ì«ùÊRþ×çEŽ#óõya’ó"bó‚$và¼dó!¡¶zéî0s%Üt>}3x^4R=0 ˜ v€âT¯¾ ,OH¡Š×áàw9yHæ·û$`¼Ö%ºÖXeÉZÑÿìåÏ|òw—§Î›Éø_ $MÂÓ„a™µ3³ 6èíB*]·gü·¯ù/ž¸öÁû¿Ø7’=?¾<ÚìAÞPL––M•J˜$üD…EÅ I6dñý€Ùá$1© >‰UÑØÔ¹"»LǃTg•5ØÍ.uG°¡+̈wïB–[a‘7yÍÝQA£©1{—¦N>8¿gqlujPVœ>;Í4žµü£ï5Þýø¥/Ùóþ›•¿ÿaí/^*ú^ïæ›žÛ[±ú`í³/·oø·Éœ?þ°}ÿ¡õÄÇgNöœ¾Øÿû//û÷Ÿšê’âuþÝþþ_×ÿ¨ eüÓÈb]_¢ áB™ð†í8\èEΠvp©\9£PÍÆQ1WNǦ؃È É3eJ »Ô&ÍÑs¡˜ 7àŠB¢¢:[ž…“¡¥ãŠ|Œ³ÙHõ(Qr òO:×ú?š¸ð§Î[SàSŸ¸ÔöÙ¥ÆS7Ú¯Üë¿îž¹>pÊ™çºÁB:}®B¦×oï½pÜê1ÿ-ä—^÷Ÿ9ŒÿÝòeÿÇ/|~ wtMHÚ¨¥ ­ƒ>Ã`´4¢"TÜ€ÊÆ-)ÀâS2 á ®¹áX”=¡|8ý‚Ý)òñdÉx²l¸cñßï»öÑÞë'öO}²çîÙ#wN½ôù{ÝŸ¼ÛùåozÏüièÂÇc YC»œëógúÑ"äÚͽWnBÐkhŠ„–d£û§ž±gûŸ zù™ðמŒ=¼V!tK<òÎÓ`¬£Ý§©4ñ Æ€ƒ_kåµz$}Qjô¿ë !ÙˆÌüPŒb<˜I!Êm¡yCÊc<Å8žlÚ7ÏõúÊ„<šzhaØD²grŽo8ÖÞ`—áÀ`•fQgpó2°l‚ ‡¹†µÁÀ‹íòÖf'ˆØp|àj0D-_°4Á/‚ ø†5ne…]Ún j8Á‹m3‘:‘‚çžä°óãFC±Cjœ†R«¶È¦-u+]¦r3Nœ¡?:h8ÎÓj®1‹òp#€Rmtz¤ÃQÚ±hMWØletºy£ÑЉ”Õ|w"Ò30vò©m=ó­ó¢)XWëRÂw†Yz£ påKO!‡e"%ô›ç²rÃQö¡HN;¹Q£ !ý‘ }¸çÅkÖáhJtolHg©Ý7‡Ù+Z .à;UÙ´­^K”ÿoG°‚c™jÆZõ¸Š“À¦rWÞÿ«cG§¾ø|æòeˆãý¯22ÿÄ6ÍL$ÏüM¯& gŽbXÞ—ožþìÎýõÍ— \P·4¾iNpÏüˆñEÑ£©!{æ„\1>×3±$ddqpÿ"WïzFôÏs Î…QØ©£Éäðˆ±v‡ÃuÑ÷DGâm£ v<û£Œm^Š8V‰–‰wd;¶¨œU¬å+p±™·òè[E¬µÐ“\YÇ@#ƒ9 êI'}oÞŠ³¿íó_|éÌ3hËxýÕ©S·®º}õ…[Ó‡Ð+äÌɾ‹ç†§.^¾4|ijdæÚÄÝ[/L9‡&õ3ÀľþnßÓi‚òhZ’ Í1vG*BT®_\³'%ïÈH¢ùXŠw"-xï¼0<‡}1–f¯²+ÂÐkˆ³õF[»"La†öcW¨e Ú…í±7-t"-d÷Ð=ûz÷/ ٻȋ¶ÝC)ÖÁd È@ÓBoÏ ÚTcF$7=‘ÿ£ž'î|:tõ³®{Ócg¿l½ÂwâÞõ14 ˜9ÑC #h\''ý'Ž0§Ž¾?™]²Â²ÊJ=c£ž6Rϧj¥í²1¶kÒܸqÒ Ð”¼=TݪÍ„0ÈZC:²ÊÈ«4sªLœ €1\ÈIBxªÕ'¨uÑ!“R3À}ÑÙìfxZª3D 9¸®CO˜erNø¡1»“|àBŽ%yÆ“½{æOÎ Ãs$ÁÙmiñ(ñ^ÆÚ0ú¢ð¾›:C Ð`ÄE<åÜ“èÛ‹Í“âÝ“4™æÙ¿À{øÿ^ô4G c|Žcwšûª/Þ6”4ž<’ÔmÃÿŽbž@mkˆ²ê€89á,ãŽæÓ²dl`Gkm*ÀW üXg“#K_m‚ Í«1rÊ5´´‚ű¶ÿƒùF;±PÿÝ|ÇSìЛ˜ãžHó`;a¦³óÅ6ë ×w†êpd¾y^:<Š« JÏ*×0‘Ò„‘GŽ´D-B²övAß}s€ÙL,òüHïƒ?šU®RP¦äðo.E±[•îÖ< ç,1ÈwW–ÿÍ{ç¿øòêÔ•‹—¦EùN­Í·›šÀWïܼ1}ïúÔ­s_Þ=óÙ_ßú^þÃKª–&´-ŽXßìk´ìMó½ò@üÁ%!ÃiÖþ9Æ®9ºŽMG¢"ŠPBÒ ê•vHúEä^ˆ™%Wb‡#_a`–éè…H»ÞÂ'—ñ¤ÄÎT™Õ•e¡Bœ'(Ä€gkEà*¢ÉæfûY-õ´²œÊï¬>>6sbüê)Ò§õÔ—]ÓWÇ0™š ¾Ñéú(Ò§/ ž?Ûwé4œ^ºóɸÿöÛþã‡^Ì^Pœ(®ˆ– qCŽŠdwQpÄ*µHI8pÅaCz£ÍP˜Mvާzðˆ³ôDðf–gÖí_=1' ýÈpçîÅå›<C.˜Óýó‚Üý·±ûcÕÈ^Þ!E—y IJ,Œ‚páf•;OôÞÞPï¼{r*v÷.Œ\ÿ²ü—'ý7ÁK:ìÿtÂÿÉ‹oW?‘Ÿ$ƒöEQ¢&+L²ÍÆÎt«" yv Ó(à­*7xÀ @عJN®ŒµKŠÌy†B5Ì ¯7Âi;¸‘c)A»SûùŸüýÊç°1ø×@t3 y?Të`Z®jðïÞ$Å@²ª;F؄ƬMk¸}Õ¡Å€PQÃveÍb5H#(H6§Ê"ÍSÁ8Ó· éÈó âÞÉfÀ·É”°7¡“¡lÔÒŸ–R+„Ô†dÞÏ_ØNTbü¯ùgöÊ ÙO¿¨þüt=°m(#÷{éL`´þk‡ü·_óŸ~Éò•/–·?ì-KTuÌw–IÀ,VsëŒr¢")»Æ$Ï•27BäDLmDt€R1È?()di€Ž€Ã¿n ¨#ÿ4âÇ€ þfüÁ(¢m‡".Ù0¤…€FX¨vŸh N=” …cƒ› ©FH ¤:w™(4QÝFÌ5¤vOìöŸóŸÞí¿pÐáÅÛ¿é|³åášú’X ¤· óBXð(CCT\ëéZ–˜‰X© äNkt"¤RK´B$·‹tbfq‚¼NÿÏÚÛñÄ¡Û?oùQíUËT;#¨¬hôž–:¸€„¡Ž“«b€ŠÂ4R5Vu[°µÓgïö¹úBœ!®¯T˜üaHããr„ó :a±QØa¶oQlW´U¶r«PùÚ U­KUëVo‚õ˜i[¼@ij'`Rr/°*;Yà9òPUÌá²PEuµPÁ,Ö0ÝD¨ÂÈE韩As\\†be‚U¤âáÎm°ëpí6@%Û­·ã¢?0' 9Ÿ±D”ÉGBŽR5@VÝLE#\ãd`óT»ùnA¥“s£GÒä‘5)Pô´ +Ü2ëHôuÅ5„ZK]š"‹I‰\­W²¬yT¾AØnÃ|'ÅvG;ª °f5À­Âønç[c‘~s¾õ6íì|[óE õOæ‹Y×: ˜o½Ê]l¼¨CÓLŸwt£GôÍó–&¯Ù!n°ÈJ•Ôh`®Át(׊íšÎkW¨­-ØŒÑa눰B"ä‚Ë*2H`vOm@(áÙpë†PëÚøˆŸØ{áÃO}òéÅó—f®ƒvíÿ×PðjîÌz5³Ž 5h]wíú4tlnO_¼uéŒÿò™“ï¾s¬²(;Ò÷ " ´|®¶lœM1U©LIõ—ªIi ÆF”ò-tÐ{AUÃ(DR'·>XÜà•‚ Ó¦ž¿+TÝ¢jV´yå]ÁÊ®* F%B§·¥/ÌÓî ó †{w'ÄÉ…p2Ñ# Ö›FÓ¶ƒhìf=c¦r父%QÙ+ø?Yyò½¢{g»ïGãô£Üí?³‡™/Ž|¸?dm4â…ô‡ŽZƒ‚8Nö$¹q¸€r¸‚B©<“ÇÝŠ© ™ (qQTBá²DÏycƒ&*/Hã*Çåˆf³®©OIyr~†€Û…¢¾Þ._Üž;‘/•RÈ|‚©¾3Øye¸°l\ã(é^lUB3 Êíå±ÐZ± ?áXån¹O_*kš«¸ßÙ³ÖU ]ÜJ-»«É¡î µv[ºClcña#±!€3eóhðàSÁf€QeCƒ<år›¤Î£n 3"æÅ1£.HJN½G;+¡ÈL%k«ŒAþM—ž¨ u"¨çãÌj",z¶€["—ɤE>@ÅJN‰–ó’#%¹ßÙDq ùCËÑp§cmñÆåIù2~¾ŒKÖ'PÔ›]ŸYï2MX‡\-…XëSng—»¸ J½èДƒ©A¦¦¬ —Ån”qqp ny“È››E¨ùÇCs¸¹)ÌØjÀ|aX0ßZ¯¶1X_áTäk¹ æw5_ø0ð]‘F±žÛì|ó¤¼oÎ߃ýð_ç‹F™³óÅA»]ÐúÓZ+¦ó÷ÿt^:½YÂ`e¯OÓh•#Œ­1ˆaÀûÂý.ŒHwO¸£Ê$A:‹ª7ÞA¬6C‘†[m•!]åVÖDkÜa懌âû]ÆwMžüÓŸ Tƒ¨ =S<áùO’¼ÿÿÿ‰deB˜Œ¯ 6³ OBR¸5sïæ5ÿÌÔõ³_^ùð—~ö㣹»Šâcò<Î ‡£Ú¬Ø>€¯–5:È]Ó¦ë ×öDêbÍC ¶†04gHÿJŸ´Æ+­t‹JIJÏ'™FZ™ŽQ®gVh•:&P”€Ú6:ÑuÝz]YS®‘åIØðÒ·Ó€«dU¨ÅM=JuhÎÞaßzpiü¡‡ºÓìµ Jɼ$æ†Hj[2U¼RøÎЪ?Úøá¡ôk~b𩨒Du~¨´$BÝh®7¢9WµÏPí6Ö:ÌvG“Ý]¦ÔgrE):ö Þ—µð~aUÀ}ðGÉ.Ñð\ÅÛ×àРñA‹Û„D\ž?W,(IK4Š­¬Ì¦©vè‹á£$¥V%îÙr§ äÁÊ Uôå"ŒPäkŽ1·'Ø{“\-^]K¶)HÓàQa}ÊIÓOÆ.V>^i0 ¸2ÄY.66!!ÔØùM6Y‹MIHfi…†˜JÀ[}”`%ÅÍN]‡×ˆÑŒzè‹‹ã÷, Jƒ"œ¹!TÊWµG–g`ãT’ r¾ÊB*SÉ(4 Í€*ƒ­5¡<ˆR€R¦½T«Ìp`Áþà~ÜÁ.#›ÃDõùŸ¬O&Ÿñ߯²À€µRÖGSæPBë²>ÔÐanŽ´4E[Ú≎êÙÍ"¯Q †ªq‰à[±,P3J Ü' `x­‘Öž$Ïè‚ÈÝ‹"†Ò<ÿu¾°™¨5#;º†Ia¾*:ŒRIü9_Dvß:߯œou¨ }¦j¾>/%ö·žpÄ@kq¨0PþÆ~z$ïïû?™m>ta3tFBP"ø…¥Ñ¯­LCQ¦Þ%­ V”‡(‹#k2Pcîw›ÞÚ¿µ§W/ß½sëúÌ4𦹼ïò㛦æ«È ÿoƒ'IàܹPç»qgêÂõ/?¹ñ×?€wY–ºZ¡x^$Ú&eð¹»lªÈAA ² *F––kà囸…va‘CPêC&¨)ªlG”¹=ÒTí ÐýZ›„è.¯®/Ô4aí´W¥Èháã:È2P)Ø6\àš†ÁÉÀÝ€1D_¥´|)З d½ˆÌUéåÕD‹Š£ÙeÉüúû”uK]8FW‡­„¯oxQÔåi¯¬Z<¯¿oD:›‘Ëçg³Y™6'G¢oYŸz›þoëÓh·a}Š•ª…,]ÊÏÔHÒU¢ úˆ °–G#¢.0X"!}³œµ]ÍË4ˆ³Í²<@"m|ÄŒØ?µ!ʶ=t/qj:Ãí¡z tÖ!JbA˜J xô9:&”r1_H=ü§ùö n˜ìîNtã݇ŽèÀßÈüˆ‘yQÿ7óÍT~5_² ¾m¾éFñ.K`¾vQƒùü‰óÒcÁ‘ùÛy©³þý¼ EX1€Û‡—[¦âà,1…ó‚{oy³è$1E8BØiF(õ© ,Éd¤IAÎʵssBU|š¥PöxxɯßûÕOßíß9? $д@_`d&!Œ¼–CGæ1&µ’N!¼Z;`ÑBÿß@$³çŠ K'œ|> Cˆ ¡è#Á="Ü`‘ÛUƒ"5¿Â(mpjÑ; g`$Ìï G:‚TKÑ´¥@þ`Ì»šÁ`#ZסÖÓkl ——z8hžüü:HœF#y°ÀU¢–§áTÚ•UM‰A^¬SÖÚ­¡¡½Q1ÍO•ÍZe3W:Låv=Tå¡*€‚{‘FF Ó‡;šü9àÙ"6ê øûI^”AÇØJ§mbÐP‚8!º §‡´íìÄÛ{†IÀ{øt6—‹çì@:AÜ:XT$xÑÙ&&B3ò‹àEìÄqæP<ËGz¤×,øÖÄkt)àCöÄZ P‰'º&µ†i;" «Ä§Íè¯êö&GæÚFç:ñ¥–:¯íþ‡œ&&!C>™„í\ò™™Íì$°pI냬m!îþ¨ÐÖ {ÙZz³ÕÜh64 xÊþÛõÁßÿ·õÁâ`lfÒQÆR`3`}€ÇÁÐàk8¬'±hdY°gÐwÌkð¯ñ)¾ >²ÇÚ•X%¸Ø<ùJfƒGÓim 7 ƒ®.¥vÁì|*ø‡ùš˜³óÝŒ®(ûâ'@7Á€OÈ'ÿëç‹MøÍù>ýÀýj¾˜,ÖáoóÅ·aÌnP_q^à|"Hœ=/ˆF¿y^2°`ÉèÅ*v¥AØèPC5í‰÷ôG9Ëú¢%ÔX;öCSˆºÖ!ì –M¦:+èOµ7&šÑfî#7UÍÚÛPúÅÞ»zîô5˜Hqår÷: g¾;S;ó¦æ+c†x5ÐF—^T¢Àļ=uyêäç—>ùó©_ÿ|¬0ûÉ çsFSºÅš®ÖXÌ…6SžC—íÖç…YÊâ½Õiáuó£çE5%úÐ8¸)ÚÕålŠr‚¡_ç5U»õ Àa€ß‚žB˜Y d­@à‘_Dš$×ÜÖSE®é|…# N :PPžŒ z)5ü…äÐá0èÕ€O€É4°»¸ßQ|}'‹–Íe§3ØùØÿ(}¢ï[µ]•ƒÔ½„]jR·‡{Ë-:Ⱥb£¬Ó?OÈu"¥c’Î¥eœ¼œWª#Ý÷&GÌ‚Í Xz&%àî’ðw)D¸²1v*ÄéJI–V…ŽÃyf}©ËVâ´–¸-•>gC”·-!¼#)£=.´=ÆÛÔánp¶…Ú[<Æ&—®@Æ.ÑHŒí)z©˜Q&ba)ø$_„9’g&´L…dže6.J6ýI¶d;ÔzõÃifX›î8=Z©VŠe•€4œ;$Nag`ëàü¬Ã¤“äU¡N óh\Zš£@2\Ûêª6i{ßJ#õ”Ù0 ;âœßº>ð¿u}2âJ)Æv…d‡Bž¡Ugô9&c‘Þï°8meÁ®º¨Ð–„ÈÖäèÖ”ÈæDos¼£)ƆB@s„­ ¯ÉpÀBP/5 CÁ›2<×™ý>˜Ö9æñ9¶ž8- ›/°”¿š/þlS t’uÿêùîTH1f盩ùj¾_Ï·ÔçªmNüj¾-I!-ñžÆr^" ö:²jœ”qX²ÿñ¼`?ÇüÀÀñAñ ;vt¼% 8¶pÝì~è3 Fé&’l}±Pà16¤Ø×;ódÔ\‹ô§/£ãáÇS—ÏOO_+ò.LÍ"3þÝ™š¯t>g½—ÙAŒLÀ¿ºùíÙ>t·ï\¹:}òħ¿úÙñkwήÕÓÃ\Þ¬­Æ^’œ0ñ¡<_BÍYgäo0‰7šÄk¥Ìuæ1s³” ™>4EÝ&blа‡ µ¤‰›­’ …»˜«Z»ÙrjÀ‰†úJÀ÷ Ç j İ<(‘C…`Vy Fjµ°+Hßç³u¸í.K‹ÓŒê„•ZÝŽ¯»ÕíB°S¦"ò"µWà{qf…¤50ÈV(ÓÀçÉWc‚t¾ˆ“-`)ø(òÖ€@jUV›åˆï¼Ö*op¨šÝú– ž©p¸²Äl¸%k„rBêqD,ẂÀÕ¼ŽËØ*妫„Z26ˆ… `wH8n¦€ ˆB–¢7õ:yZ„%¤þêʧ á'4‚JÌ\Øœ@â}§‚èL™ ‚R ¬IR¬p›¡œŸ •3©ž&Ÿ-#†4«‘ n µ4ù,H+Á©v›«]–J›¾X/à d ™»"×Mp,p®P­æBù–T  dÜ2µË’+â@Ä@޲>X XN,àìú¬gÑÖ0êÓ_s—ËÇÎùê*Ǻq™ÐåÛ¡¥kÅ:Ézcƒ„¾ADC? íRÆN)3]DÇG¨")dÜàìa¾$Ÿfn1U¹t¨ºUn3&þ0ßçáë|WllÌcv¾pçfç»M#Ú¡ïÔKpXÖŠ%¬-2ÎV)B—Ûtœ—b 9/•ºož-<™g£hU>b#‹ Ü! ÀY›]´•ÉÓl N ø\¹VN†K¼TL-Ðr¶=¸àýŸ¼yþô‰ËÓW!]~ 9th"ŽÍïÐÔÜ"Œn¢Qƒê+;#ƒŽ-„áý “6tÏ„©ùòË3ïýLJǎLlߺ-ȳN,ÛÀâm¦Y<Þ& ÅíRÖf)ýy1õ>Æ!ð„TèŃæt£ 3ˆýÁv¬ÔKê,ʯµÉ¡mv° Ã1¾‘Øà¡èàáowØÿ¼>ÀÀ|µ>$pfâ­¥3Ö3YÙœu æó æZ&s‹µ‰ÇÙ,ân³‘æÂ>!qV`ef÷^ÃâÛ‚ýâPåß3?ª/ÎUlâï™…sdfQÞèF:TXïä£÷n½K XBŽ’úK†”œY\+_Ï—FR=ØÿÊùnç172x÷±6æ»–ÎĬ¿9ßõB:‰ôbË£è»+ðÍó’-&ç»â›ç. bm1ËŒ¼:§ ’ñ‹4¤x’œÑ¦j$ßÈ@².= UŠÊBµ‡tŸÑÑSÐ/%äPwÃ_~ûÞ™sg/BRüî=¨—Ïܺyç19ß¡©¹ùµ©!?sÖ¶*O05³ŸÕ¬[w 4|ùä©Óøã_^yù{%¥µi ²-ÎB•¡B¢©*š¤Š"U)á6àá5BH»¨¶„[j£<•áAðŠKƒ¬en{©Ã\j3–˜ÑCYf„…¦T#Ïò²¹¬|ú‡DÇÄ%Ê´V.² ÀTT{…Õ>A‘YêbAˆ¦Ê#¨póª  ïxPéa¡Ÿ;@;•V4L@z\*P%¡o¥˜È—â*Á%Ô:x €ºe iµa»SÛ¤…­€{Yë‘´Ej‹LxJØÀ²ÈfPùtª”É(ç³Á=‡m}àv|?Ëõ Š­MAÒF· ^­Òе"tB±U*Ìäðv2¹YºFa‘™Œb« Ì!¬€ñH`~¡l T6ì 2ɵNa\bü‘Њ'[Ë. ‚0JZ–œ†Ê;ЧƒÊ&Ç®ÎÂ&& #ñÏÁžnujšð÷+õ"H… žU¡#œñmlD› Ç™wÿëú`q0À}ÈE/H¤ñëƒïÌ“ Óy¤â± YÉ.±8K"ËS(¶rY¹*IµÃØèµbYj<šº`mC˜±>2¨*,¨"ØQæ¶’Åq˜*mÄtÕM­Mdf”§eâV¹ >^¹Š£¼:•ÍÜÝ5*€Ù0ßÍQTÆIÀÈ m`±!ö¯žo¾L˜ÁåìÌ7G,ÉýÃ|+Æúà¯æ[ëÓÖ…q^*ÂÜ8/%ÎK™Óò·óRa ç¥LMÎ pM…|n‰”W®ÀÈ‹ZmÃã­"r£pz«œb¨XT¡] ²D¨GPå¡!/D<ª|*4TZ. –øÝÙ›>úå;'¾üüÌÔÕs7ï(Œ ¬ z6ÝFžö»û€©c¯æï¦&àÕÀÇùÊÔܸqçÊÔµS'Oüþ÷Ÿþè‡?m¨oŸ?w³D¶](.(Šyâ:‘×ȃ©‘ ÚmÚŽ }½MŽ Ã,Û¡l’žç±`¢‘›UÅÇo:Þ}¸šZ ³HÊ}ô$¯ÐLW3š ¢» P(EV0•A|˜ ê¼"˜@¸ M¤á×ÿ£î=à£,³¾ÿ;ÉÌdJ¦ezͤÍ$“N*¡ƒ4vlˆŠ€H/!”H PBïXPQwuÝuÝuÝu×]Ý]ײ6DEj€„4š¼ßkÐÕ}Êÿóñù¿ÏËçÚÙAB˜ûäºÏ}®s~e¦[€`ê•;Õ mºeN[Ï·Üé©tÅ•;s–ÙKY²kaºwIл<Á¹"ÞU›ìæ´‰b†á À(ðì›m’‚Zn7VèõåѪ9 9Íp¶\Ð/™zPÒ °Ð%[žýŒö î,JN=3£Œf(›1)2rªBÁMÊ#‰¹SrÎb "E||±“Lšñz5U4ñ¶ƒ¡6"ÚJT@MƒBá¼¶|¦Y6ß©¤¼D=G˹ÜJŽ(Æ $Ér¯‚|²<™Ï£¯M5òʽ&8e6„²…F"› ( ô©¹ô^s¹Ë²Èc_à²ÏµYç9±&I®NM[™šB2\î³?[•×”åg­Lr-r@8ÒÎ2BpE£[ä2γh¦i£ø`°¾5è# xâSîŒ">„…Ò”/è»j¿``•[µÓTrz>”m“åôÒEúš¢”q±³-1À W¤¸Rm–¹” |úïLJ–;Y"œ9@qër“Ö |šVie’®Ü%#.´ËQ*QL³UÐÊENq½eNÛ«iŽÃ¶$!±*¬¦€¬Ê[ª.Õc‘µ‚­â4̳ki@ph]î6霪“‡¯wCM§X;ûíG× ZHj ÀT(ðUq1‹,Z£B%}ïz#f›uË’\ׯw¾;t½f5çG¤ºÆÉ#Ø\¯h¤³(äd,Ð@c"ËÐu7É  grÀ~p(ˆÛ£"Q»40(}3"Ï€Lð##Éðs$Vîàˆ.eËõH8ÐPuøïТ9Û}áÔ…Ëg._¡Aªfçõ“ýê¾ÒN5¡!ÔµC@A4$Bç¨ §Ž_8òÅ•c_úåÏöLž°û†þ÷~ah¿]½s×fˆÁDsq^mfJ]VjuZ"$”–yMk¡ïJž §RÜÝæ®A]ª¸¸Š8yMŠnY‚²Ò§à ¦ºàýXh6²ê‚±+ü:žÚ<¿xVRŠ0Ë£ ,TDtòéjÙÔè(4‹Ñ<:#‹I)ò«âœrŸ\F¢»/2r¼‚º=òQj†Ãü„Õ8>:’úœOø°<’³HQȨ±mÂQEü¸VAåSÎ1E“‘€†"¢A xµ*9veŠ¥&ÕZhnÎJÜÑ#°+?}wAƶÜôM9© Á"®&®Ï ®ÆÓÕ¡¹AU@ÅEך1|ûð†óKXsƒ ½àïpÄ ÅGN|jƒ¦kñ‰® ¨„—Æ)y†AÂ…lÌr°à®®LÖAd®p2¦ ÄUàY°ˆÏ4M$]—É (–*æ\Wÿé(vµ¸FÖ}2ùµø(8×f˜e·O·˜RÈø„,Þ<¤ˆä -)‚6A£ä¦¸šŠYñ±InhC1 tÇ©­:Õ(Ľ“c‰Ïö©»ò3wdmÍ Å';@B«Oõ­ë‘bj©ÏLJ§±ÉA’‡? €hqˆ<ÃtOÈs ¿&¢*ÑÆÁž#à¥^Í—ró}Nߺà%³A) î¢Žœ!æZ% ôpš¢yÂ],¾-ß3$jÊo¡Þ«ˆ|D¯™î´>a7? ’s]ãdQ÷+e÷G+xŒ‹Š| ZöhŒœÃ&m7DÛ@_Ã-‚ò@–,º*ÅÒa[å7Ô&jêý´ µõ‰(cûÂûaga×»±Gêºì@cvJ}ZâºAt0@z£Áø#¼8Ó 98Qg²"@y±8˜óùáTbo„C ´Úä˜:QA½¸_â•"Èðà|*twŸvÀpÜ`ðÕmÕàÀ‘…w¨fé¥J³bªtVõfe‰LgùÃsOýô£¶Ö3纺!=} —¢£"Z'?Ù¯®o»Â©&Ô®ƒ§kˆªšKB´ãì•“_wþãí7Ö¯^{Ë ÍéunãBS4§x:3˜k”IŠùÔö">Ú% t²ć÷ûñ™«Xžìœç²ÌrYçyì‹“<ËSVf$7ôHÝP˜Î ä; n„ÐפØAgUÇéQ³¨ZÙ õÉžºDOm‚»ÆçYæûa†Q쇙vÃÁ‰F õõä°ùÔÊuš;å‘wÈ"xjÛ2ƒfÔÎ,þnY{†}ò JõpŒj‚FñhLôTSÌ|…ëe<„±8O™Ú+ªÚ•.Å{du\ôê$-ö+]ZöCEh?PÐУŒç§O2‰ôƒÎïímêyŽXjÎ¥ ("&ÕSÖf¦oÌËâQxmf\]G åŸ|1%=Ä:ÚÅ2ø`Ñ™H†Çµ¼JÞ,ܪYJhäP¸SÐÌlŠ·×ºÍèK”gûo´ëoÎô¿q`ÿüƒTÓÑÕ‰H¿B[R ‰ç'ûÕ}ùjª ©Ó\M5álxïrwû• íWΞøø•ƒõßÿP0aŠÍ@” ˜UîPˇFkÞ  ´ª‡â<³Óó‰s½ŽÅ>'ãîwæ¿5ræ2T2Ô3ªÓ>ª×M2êÕ>aPcH$î:Ø`E%ˆèðp×3ë1D‚m¦¿näÒ2bÔNÝ Óaz¬†ùβd/¯>ç|¬²@Æ9øž“þ5>Áå驼.LºŸÇH)zíd]L¨¿­~ܨ$ŸÇbu“cµÀóÐ(Ç!,²øH¸RÒ¢› £“âó‡ñ¡6øq|âíó⃨ãdòau”Ćòá™8` èQ1çä¼Pœ,™±øY\Û O@Ñ%`ò¯íº^쇥kû!=¡*+P‘ž¸²G°®0sYVÊÒ̯`¦ÚÍ™ôb¸´IFÃ#Úv›Guš‰zõ:N±àšŒSE (Jšn”¡œÆc‘ýàŠÃÞ…˜-€µØ ¡zLu•O˜4‹þe?̳h—øl0¯'ÆÈ&?ã’C.±á Š»† )ö¸´¾éü7äY›Š¬k Ìû†elì ü’r ¹.|½ÃgI>ö» 9ÅŽû…SjˆÖ>k ¨<6&*3Íÿpd-‹œ~Åõø0Jǧ.Ý´4Ÿ¦\Ÿµ9">T Äâƒxñ WÿI|ăÛüßŇG9²p|x Çg‚*"|³"Cù'Î2"˜¡øDð@árÈ]`?øqPË…ãÃã@haKð]|PˆµˆøxQ0þ·ñ‰!>iås]¦éVÝ”ØèÉFÅ$ìQôp ‘À¢sý[K2a\֤إá Î@£Ó,H­ÈY*øÁ‡æ ð24Åûáõ2Œð9ÊÚë׋+Cñ¦•:¸ñÊ5²¸p¾’{XŸ pÂã:±±YÀ$è§Q„Ó§çz¹µÃûažîê~Xz—ǹæ{sÝb?Ôfÿp?Ìq™¦Zµ“bU r¼ÂÔ׉¤h« Ù²u9ÌÓ ¾>ß¾®7æ^l§ÀaÕ½ÌLotöœh5Ó¢Y‹RP¯·LC8Æ4LW½4V¾šÃ0¹”MöÜ÷í—_<+DËÏÑ™ħPsµªÁYû§ûu N•8œ…~1t %R \KÞ¢šÞuöĕ֟ýþµåÞ?8ÎÙG­ê¯Ö”¨uéRDZ¤âö¼‚úÉO쮨¬~à¡ EÅ£ìö±Vóƒë#ãL§¥*˜°µ4C…)«ól|Uš{9ž.@k€Ä÷ðaŵEÙlÍu8 y!£Tæ‰.s+€…€ ˜â"àáˆx,ÏÇ9Žè¹öh¦”…+à€`oa\†àU…YÃ|cŠˆ+“,«‚v¡[•ç«Î°/ö$ëg¹a‡jŽ``Çš BA»£X¢ô Ñ+¨l™ÑLކz=I§M:Çi(sÅÒ-\ìÒ/õà¸AÞDZh¹GK£ÍR6ù\úé1ÑOhT¬©1ê©:Í$нh9ÍÐqô…&ƨq"ÆF?—´Äí¥ù›zg6!—› yEÐ]êôòƒø,¤õw-><݈mpâà —ø0T"2@)x ×ç?Ϫd"«ßÏ¢dCY’Žø@-¹ŸIôÁBñ)ÿ@š)CñÑFƒZœç0-`¤èŒ]ì4"Á±"βÜcÔÁƒD<ýäP|Dˆ®ÅGóýøP5…Î&ºéfÝ\§•´¼}@Ïæ>9MEékòRj³Ù?ÕŒ8Ó<¨CÓõÍç¯íÞ²ô‘ñcÒÓG§e f JÉ7è†m+ëß{í~ûæ»öêº u÷Ý÷PZêýnÛÜ`"…hy’Òòx=SžéV"« 6ÁÀ#† ºEGtQ0ÀÞÜòssoÿÆ’Àæ’H:5þ"¬dFŸB¬4à¯Ï²*+üø¹ŒŒ¡íW†¶ô2·n©7V¤Ø^ ŠOÆ1u™’UFluv,ôÏÊ ¾¶‡ ݆•Yí¬Êdã<— ¡ò96ÆXÚtèB0d)wèÇ[%XÆ™æÙ!nË„¸ Ô ¼u#n2Ød„ÔéÑ@‹rÆoFŒjr´œ®2‡ Ï}ž‰Ô*"&éјUMÒ+ç8LóÜfXË’…1=à©ñ@ËT9W²®Ç‡èUÂÓ Ú«ÐÀL5oèó?Ÿé±ËÓ¡øññ)tŸ•YßÅg–M.¦íNm™SÏD†øÐXàŒÃ‘Û €hjD†5+€;ZÕ Œr”sX”ÈÑ gf4U´€m?>áºèZ|ÔÄg¾ÛZæB×ÂKnA9–QÔ—N¨@8#ÄA‰‘=Gü(ŸLQ ã¨m+RßßË‘=geˆ(ƒÔ¬6@?ÜYXZ° µE âj2l,ªTÂ*>vhV¨ƒ>œT¹Äg[ÂË~ÐÒºçÏñ¸Ôg_`7@cA㎣ßP N,`ìÓ9’+~¸¸ÞûÄ~´¼ËÒ\sãM3½Ú)¶¨ éAmhƒrKeõ vQP_‘a®È°. ²R6õá~ 6'"” 5£yH'i‡{ í,«Œ=Æ-Vá·a«7ÓçºßiF$;øÚîm-ŸÒÕvæÂù®‹/~/ÕÚ]+B~ŠÚ&\ňï$d$Bös!@ UM×%:ÒßÀ'·¡ŽÕòéû}õgïüâÕ·^úùï^üùkö»W_ÿüýñÕ'ïþí“·þôùoójÃê²!¥wÚô÷y`&Hö†U‡ò ×ZñÊ«ñQñ+ÎP0;@@ØHªp|¬FâCu7ËŠIŠ’øp£_J–&8L| @¦à¹ÌLÑÂ#“t@æ‚/jðŠC"(G <¾BŸŠ©«èùGN‰‰ø=ˤ]êqþ`?„¯÷1]4Õò÷g´qÌ")ªÁ#¡ÅꉘUæ—ÏIˆ¨ÈÒÖ8Wæ9pý«L·.L1c%CÙÅê€)8ÙHT2üǦÂdDZ¶¯E5ùIË3}•9©e¹÷ú½5Š›{¤ýñÅgOú¤ýÌ)¨Ž—.]‚ˆ$†ÐbøÌÿóÛŸ²-|5Õ„¿y(…ÿ)þ³í—ºácžïkÎ÷£nJ|˜Ä…iÔ–„â{ñÑbiTfPTØuUàè íz⃨7)cwHUaQÊÀœè"…O¨Ú!> ¬ù±†½opÁ3kI’&äEáá ý®ï•Ê„v¯œÍ+ö)ÆHÑKÿÑ~XéVU;ÿÃýP®Æq#§U†ªdeu’’ÇÓr¼.IW—¨_›jÙœ¿½0°¹ °)/°¥(X•`ÌÉ©ËE¶\®6  P˜#\ì|Œ8†¥ÀxŠœrNÅŒïí‡i±¸^ Ú´.¯_ïõt˜ç9TIöôᚆUù¦¦ÞÎ ý|½½Ô`Ó삽.zðõ"€GÊØŸè}U3†ë‘°®0°6/©*ÅYŸ›ÔÔ3¸:ß_•îAÃd²1ò.¹4R&Ýj53ë ´ÑwõëùÉŸÞh;þuWH8"œgþçR g&Ñ ¢;ÃB‹lÊjXêB  ¡o,œ§ “ww\éj¿@÷¦³­³³ÑØ9ñ®ã\G;õâéc§ßçë×^øÃš%‹ûkz'ÃúÇÑliP ¾u#^!Ò‡A!Í3ÛÉX#‡&¬‘Ü„÷DGÇà GÔø!¹0‘yþ_ZÐ~.Ø: £E¥Ä+ß F•Ïnž«w¿e¶¨ºø˜EVt/¥% }³ÊDiEªbMž¡>ò2² ™nT‘Q“#d~³b+ãeôô-ñÀâáų ¾à6B­ QòmÐsž ö x^È>o*ìxtêå ÍÐE2ûà9H+’/lD9ÂhrîDä»7†Âe½Êo¬÷˜„B³%ÅÍw š’xµŠCÁ÷ãCáG1 o‚ã§0NJûÉâSëC‰‚ý/ñiÈ7bL¹,«”Ƨc˜à Søññp§ƒŠ£[+ˆÐ÷à‰ Óè †ãC©s=>´Iç z->ùátÈÍ…€IÞ¼Øê“Mk’¬†TÀ*"ƒZ»¼Ü<Áá=aoD{–3¬IêCz³:Ÿ|C@¤4Ó**,z—ÚeË¢üèz«‚Š5FÜ™+r6ÒˆLqs^—aZŸfnðë Ís¶5*^\;‹ÍÀÓM 7¹ jTO#„:Ül¢-«8…¨{Ëbå<|¸^Ž{×öÜث×{}?€ß¸¾SÌhᑊmûζèLBm›m‡c½*ÇR›…O–hG,J4Ó³BeQÐUBhX}”ßœ(9`Ò?ç(¯Ar¿@@À8Iõ@¢·ZQ¤~lø ƒ›×ýáß:Ξèî:G :Õ„ò…Ià§®j©F¤²k©&LP¸žjº¿½pñò‘m.Rù¥óíç/´wÓùØÕÁºÐÙÙÝq¾³õʹ“ÝŸý½å—Þ¬™[–ç]˜f$± ¶ßXäX]`«Ë‰Eí /:«rlôÉ+“bhϱGI:™Œä(ÿ¨“aj qS™n_0MsD?†“ˆCU–hY–Çbf–QY‚mžÏÂ+Ô9zwÌ:Ùê¬0Qˆƒy¥%r©?;!èVÃo0Ç&-JwU îÈ W1’–¯ ªë3;û%î. ìé—º ñí>½ýÛ …ƒÒNĨó ó7ä$mÏOˆÜ}Äg gxI`¶á‡Ô¡CXZ™Šâz|vôKÞÕ?° kŒÿö>~¡X^”L|võ nÊOZŸéÅ+dg~ÊdNóƒ»òRŸ}³·§oÍKÚœåÅòµ!ÅJ|¢,õÅ  Dj‰¿«ñYb–Ój‹•6æ;šòlü€Âº— (ë³EÕ·c@VcAGï•8rfxq©›é@&HRÀ[XÀ‡ýÀ8r^ g±øÓï]ïÂx Àù’dYmz âÀA‚¾kJÝÝ+qoŸÀþO î±k@öæ’ô ½Ó6õÍ<0º×æ>Ú ÞDÊõɾY;ò’“mû ¹dÿÎÜä½=ƒú廯$µvþtKvü:¬4R¡ë5öãzÃûá×Ë%ï홲»8ˆ( ŽäkÓM¸ç4PI¢8§_žÌÑR´+Ål”ú ¡캆RMÀ³:Ë€µ±©&ͳf&•dZBmϬ兙s‚¾IëDåÑ$O¾$èbîíÓk{õÒOÿòCŸË:.²Ï ”kUÇÿ@ªRE-s5ÕÐ!ÿ–8«!cNUÓ!˜ä—èÛm€Ùm®\ê¸|¹ãÒå.ªÆ–s™¾sßž=zñÐ{g~÷âo–O¯,Ž_–eBƒ”Q™KZœµÀ+H‚<Œ–$F£Ï\™¤ÙÜ‹¹6ðx‘•ñ1 K5âÿ·Ù¹^ÕL§œãê£lcä4+üÖȉº¡×Ílš©t$¸VÁÈ"ÕÂ7\ó€y.óÍîùñJ3ö•¤lÍ[›iiʱlí·ïÂIÛû' ª°¥oÜîÁþ£sX»ú'¯/pmÈwí<04ggÿÖž 8†s6æy9,ðÂŽyœ ñ; ‚¤š-Y%'4§'nÊNÚB]]º¥(usÏ ¯Í=â6åÄmÉßÑÓ¿·KŽ ¸%óP„NT*7K˰®vIËyNÙ%ÀŠˆ /ô0”gç|ŸæP| ÌqD-IˆÁpveÐ ŽÓèOŸgûgî­ÀŽÏ–^Þ] ñA„áZ|²¾‹O¡{ï àÓC³I5[ŠvõÃH%¡E”1„Z—éjÚš3¼» »ˆFv".ÒÍéñáøl.$8©ÍÅ©ŒãEpr<ÛzÄï,Jñ)IÙRœ¸¾ÀSŸn%^†aw(>ÕN©Ú&-#û9¤¥ BßÊ6À*z)xÑòœ¢AÁl…7P?Ö„ 7\:û4u9ÔŒë’c7dº·åÇ?ºÞ­ÿz½;ú·õ÷o*Á &~ÿàÔƒ#s^‘»¿o`s®gcŽkO¿à ö É]ß'muQÊšÞikK³êz&Wåz—e:Wf{`zâJ³«8¸³( “Í™ž-YÞíx3v§ì,ì($ÏıHü6¼8ž¯Ïó„÷Ãõë ﮷ұıÀ Íä©Ú¹d¿š€¦žó$&Ÿ þÅ t>Ñ,G³4fz¬Šîßdhà1²û”ÂjÀÛTŸíA³v¬F>"J&“F¨"†)#K$)S’F%'>6ò†½«jýõíóm§(::Ï2ý W5" òJ8ähP?E?øê÷ ¥PÀ]®f›ï¥’ÌÕTÃ×|ËIvw·8W]éÃð+ „ƒÉ¡àÁóbË×ç?ûÛÉןûÅ‚‰ C²šð05eéð¶^ßÓјkÅ0½: æÍÆ"ÏÚóôeÎJD´ë—Æ«²Ä‰¼Y&kgCf±EΰGÍõR(ÆVìœÇ ƒk èÛ¤6¦m,ÉÙÜ7wSßìÚ çSÊoágW”Š‚Íæ üp×Üï[ò}ër\rÝ[{%n,Š«Í0£Ôº0>š>'+<”QºƒoJÌí>ŽKá‰RŸ“ð0¦œô‡±Jº4¡ãÖÉ⌀¢Òtƒ91…î¡ufÎÝe¦Š,Þ¶ôò?=,拏zíÜÙ7°½WÂöžÞ-yÎMÙæ ¬]›rLCñ!8×ã³&×FoDØs W+áéL|Ö…â³<² ñ¡?L|&‰øÔ‡âCˆˆ-⳩ßÿ·ø4÷ôÁþ >8ƒü›øp ac`š £"\È,¢0bØ$ç…YÊÐùè_ã#Ð&¡ )Τ³uR9e§÷j|žÞãà-½vîèÀikñÕø4§7fÖçÄb0±.ì’Õ”km.dÿ8¶—¤nï—±­ofs¯´µyþÕ9‰MÁ-%ÙL ÖàÅVà'ícˆ;åVs¶·)ÍA×%¼ðdÙÒ;‘ëe4I?…„JÊÝQЗ à…Û5êЀ9¬1-[u›BºM-ݤ’nÒHwš¤íòñÙý¡²€ó GªiÌF5BÕ„¿>›&?ˆSÔ¤©Ó~´ÞTüãýÀÅ⢾5Ǿ±\·ý1«ú ü¶(Ð& “àã¤ñ15øjå'ñC¯/H[ŒŸg}Ü;Åk™šè˜™ž<¿ «¢ïÊ!ýç•߈溜îgÔÜŸ“1ÿ–ÑOÕ®üõ“û¿|ï¯Ξ&Ïœ>süLë©óT "¯\DßW,Ñ¿BEÇO–lHajßùjùDMƒM·áªFd¾€.±èሠè¼È6hœ‹Tƒ(2^º®œ;}ñ‹¼úÌþ)wo]´wLþÎ~ñ[‹›p>*p°CrÌdæ•)TñYåto`µ‡POPu E£¤A}H6¨ÐGgUºº.ÇŠSöï¹½4gÛ€¼Í}²hvqD]–êÄ:ë^˜Î°ó˜r®Js×Ò+šEæhòSoLÒç™"監ÐÙ&§1îá!È(™qÏD QÆÐ<?-‰S õ­X % 苜Q€üI(f׬t[” M´:Q¼ åd(˜RÑýÆòÄgm˜%:ì )„Ø*£ß‚ÚäæÒàþáyì±çGàž)rNoßÎ’„M=Ýœ¡ˆnw×ビ‡€‹› p˜MwÝŠýˆÏªP|BF™Öu¡ø ×I|šKD|Pe!> |Æéð+ÿãø0ü½ŸrG4ñÓ÷Å¢=­$&t œQnyåØx-ñ*ÌñZ­Ï§]dU,4Ó*¿Ÿé!Ä,ÚæŒ®P’EÉ¢‰þXø°L|¦Ùd°Œ×'mêÜ3"ïù›‹Ÿ Ågg¯„]Å {úøðÇ®eCc#-5ì6Ø?É,Á¯ÇãGŒðBÍõÃs©¸ñ¿.së–„öC}º§.E0ÙèaÒJBc?|½f¡Ýq½a ö™úƒáƒ öÐz\¿öà±ØÈÇìÊé~ëŒtÏC ±7ÆF ¸Á$c‘ßåÐ<äÑOtëù¾ @ß %0ÒD‰Ÿ™™Î[øzÅá7´¸^ŒM׆öÃÞþe?ììåÛÓ;aW//W½­ÈÕ˜m^•j€< Muq¼ŠÓôt”= «/€@Ó=ŠÙ¾˜™‰ºY)®YéñósüeéÓ²ü÷'ÚouÆÞè6³‡ymƒãì¥^û”ÄGFÞ¼xÁïžÞwâ£Z:wòxw{k{gkkçÙ®KÃ’¡T# B¿~ÚTÓ)r‡èÿ8Õ„ÎPWH5@I5Ô;∊l#$nBNˆ« ™¾öÖ‹_~òÅKO7Þ}C㈞{ƺ=¥‰°îè› :!ý“±]ÃôyM†‘…-Ëj”šÒM«‚:œYМ©pE.öDqÈâ†EÎ…ÃSNZŒ;©“y²0ú„Ñ@÷žq¾Ïsši±z†ƒ°m4üècDÑ­ Ž¹FX‡2,T˜Ï"„YÒl›ªÍç8=·! DîrЪ˜bSå± ¬[Ñõøÿ¿ÄÇøýøp`ÊBR…Öb"¹x:ØCñ1IË|p?U Ì”y¨PÒBýx±“k §ãE ª¹Þ(Ž~ÄGÌp- Ö£†«ñ¹“k!Q;d‹Ò¬kAUõKÙD£¬ßÀ ¬úö•úv—¸öôõîï¿»$NØþfàÎlŸ=ÌÐÄŠ7LmrÃV²þ 7>‰šý@{V؆á=æh6#ö_º^!‹Ñ,6L8†/õêÑ›a– œEAž™å{8Ù2B'ä/ÔJ}mšâbG»tà ²áJé&¥tz>ã\„ô‹“ ‹’4ó}rF!%0쇩¶èïï‡ÀŽÚe‹ÿu?ì˜q`xö¦žŽí}Ý;ûù¶ôŽ[ßÓxE¦YÉLj~’qI†½²‡{q–mnªá‰xÅ£qŠ»mÑcmÊ1±òþJ©GèˆT}cšodzb/Ÿ­(ÞqÇÐÒÆª%oýêç‡>øÛá~ÐÕrº³åô¹¶³Ýçh€ ì Ö· šCw3Ç”‹h‰‹TCžùiS –r$“«ãóÐMtjBI…Ä}?ÕDyï[´ˆ¿å¯ÀgK$ËÏœ9ø³ì›×?nNAvrm¶uy¼²>hl̇ÅÌ¢ lÊ4Õ&«—ÇÉV§êšsí›óíë³-M™ÆÙÖM…‚ÕXà„›€q t`sír°©L©È `Ùü€(¯Ü\ò&ÜuGéå+¸! ¸tøPÂ׌Eˆ8‰ÑÙV¤×£¾r¼ÏY‰—±S æà± ¹+±¤Ô`t ñ“5ɇ§vS–»1ݱ…G‹Rýb»b]U‚¦.ÝXŸcªÉB{?j¦Wš‘ ‘`ž™h™–hžìÕ?h–£vK”[¾‡¬h•Ow«Ø“•©ÖšÝݰ V¥ vâ³.Ǿ6 Ÿ#õRŸŒ$L|è †ã³!çj|î$>‹D|€ ê|âø{‰ é忌-ÓëñÀÅl«Èý÷ã.—š-ðÉËúU R ´M°äó#å ¼uª@kWøpsˆFÆaY¢‰c€ø•3¼Q³|ú™ñÖY ¶ ÖÉ^ãùíéF™4’ÐV{Ô4è™>B[•íæ ´<1ª!M±>MmS–ac¶Š ®ãMIaZ€0îHRë  DÓàwÝÇ~ EL+˜¹9¼¬%Ûá\L¥Êõ‚=`?Àq&V4“aT!±Èì_ aižbUò£œ“å{ÄomVPÌÜœl];ë±Ý+cˆ¶«|æÂ1ƒÇ¥xoµÅÜiVNp⨞ƒg=ÈÞå9ÖòÔ˜>ÅŒxý÷ÃMQByÓÀ‰6ù4ÏÕýP›í¥˜_’DáªdØXdk(ò¬.Œ«Êv–Ì3ãtÓ}ºÇ½ª‰nùD¯lzjLE±såдY½SîI±4ÉJôÒ ñÆIƒ{®ŸóØÏ7­~akÓËOnÿí+ϽýÖoÞÿே¾þüdk CœŽsíÝ]ƒÉ)ÝBhïbÇ%8 BäST5ÿšjB™@dƒŸäW¸ª ¿Âu˵T#’ɵ’&\Õ0!]#1V˜T;"ÕÐFºøm÷é3]_~÷É]wãÇè"Ç„ôÓ@"á›éŒ.‹×ͰÉ1$Â.º¦ä¸a.OTqjØTèàœµ³ØùÔßóÃû&ì,ÅÕž:SW•¬X–¨À™Žç{¥OÉ}]é^Åt=f…I¶bÙ&î¸ïa¹CÎY©Ü,ãÅQôR¸­J€Ä<­ÐyF¢éhZÎŽU <°MWn J:¢—È)‰;]hÑõÉÆª8u¸§ÄÙŠº#ø®ežèš$üÔ¢W$«ÖdÅn-ñîž²oD`KÏšž¶ù©ÆÙ~ã¬äØy Ñr<¼>æQÝ£Ùævй  ʨ 8ÁÁt©çÅ1¿›ëp…H>0ºÊ5ôŠMÅ–m}¬ûyŒHÞ;È»£¿{cOôuËý d-—sïÓ0¤Þà8ßÁ{R›z&`ø»"h†ÍŠtÉ¢¸áfe‰BÅ…€çiühÌ®Åǯ™@ÔÏsêfZT3ML78ÇÁ[Ò¦¤ãP¿ab¦uZs~¿„ò¡é÷dÅ ôûº´ôÏßX>íÏï>ôöëGþñçã‡>8þåÇG:~ò›gOµt‚Gé:wé< zÜâT  æº  ¶g¢=BIsñÂÕT(ü$I&üMøGC©åß|Ëð]½ú¡TÃ{þ;ùç`´‡!Oœkï8üÅïwí˜?jØH³v„RËÓ\¿›l $ ¦õ-O.òùƒÈ ›Õ0ÈVd9wÉØ7Ä¿«Ô¹o€í©A–ýb ²>=Àüd©yOÃöBí¦ÊõéòÆÔÈÝ=MÆ¿0ˆö¾mkvì®|Ûž|Ûæ,]Cjäš´H^Wû#R”)š:Ÿ|©EŒJ«Q«â4«ã5ÉÚÁØ&¿®Æ­¨²DÕ»µ«}±5n]­'vu‚­!ɵÊg[áŠEÙ ,&‚UaT¬°ªVâɈ•M*÷ʱ4¥­ °Ô1®‹UÙ²¤ˆ¥‰Òª åîÞçoL}fdòήæ>¶†bûš÷Ú¾>ÖØpEÎÊÓÂ4}Yš3Í f‰õ°9ò½t—RÄgXTT©$ë'EŠˆäýEŠ˜£âTÒúmx`çˆømƒÍ;‡÷×ï¢Þ?L»o¨nÏ ÝÎþú-½µëó5 ÙÑõéÊ­½O I~z¨Ÿ7ëó ›ŠÌ›zÆ6öЬJ‘¯Fב4QÖ¤i8ŽaŒ¸Ä"-7GÔÚn͆ÝÆ@ìúC}’z™7Š*=CZ1dõºdýº u]Še-‚<ŸÌQeÀûµÒsJÎ, ¼È·<5X‹½œ» ³ —m¿îqÌõÒ ÃÓGgTMX>$¾jHüŠÒøšÒ¤•¥IÕýýËúù–$Ìíé›Qà™œå¼Ó£ëPÜ“nPIl¡Á‘Ò(MäHuäH•42Z£n¥”¥¢ùÐæÕœïÞß/qg!­TíR_}ª‚hpÕ+"W%*Öú5«âÕK­‘+½êJG´K%P‚újr“§zfÄ›&Åi§ûcË‚–Š Õ5©V†˜ÕÞ SŒÒ½TM ±½£¤R‹zß²²¼rð‹·Þ:þþ‡mŸjýäóãï½ÿùïÞü˳Ͼ¶iã£ýûÜŸŸ~wšçál÷‚¾)µÃ³ê†¤V—x›Æ­íçªïm¯)2¯,´¬,²×õòÔ÷öÍIROqFbiÊ~¯ž;PæAæTÕ«…4\™/I’ÔC=,˜|ß ’U3{yËê—7×l]8qñ½ƒ'”úGfX5÷ ¸½¾ú÷??øá[oùèï-_~S»ûìi¼:p9élï:ßÝyᢀÇ1b¾öëú}zsí¿‡ÿ뵯ù¿úÿLéj(gD$ ‘j¾½tá"gÀ+§Z>þå/WM?>'cbFÊcþ¸[ôÊ›b •ÑC•Êáªhä&H;(ÛP=\œåUÔç[7ö1ïèûÔpÓ û©ž®?0D»¿¿rOIô®Þò½}TOöÓ=SjØ!í.Pí-ÒnËVîès ·í…R÷3},[ó[óå³¢Ö¤ÕÉRƒ_¾&YY'kHÔlH5nI7oN3mN3nN34%©jR½[Þ¯]—d\íÓÕûb×ú ŠÄÕà¬1 tòù1åºÈ%ƨ }d¹^Zž¤¯ôƒ+Ö.K‰ËP•G±<5jEP¶ Tm rM¶ª1W·:K]D7[±­?öÞ ½kŠ,uyÆÚ¼ØúBÛêb÷†þº¢¸å9Î¥Ù®%Ž&šåã Š[M¶á:ûÀh}™ªTŠ(•¤A!Së³åu= ëúj· Pï¡yj”rÏPéÀHÅÓãöŒØÕ/rGIÔÎõž~†}¥æµ‘[ uÛ‹sÕ›òÕ{û™ždçus®ª9O¤£ÁÈêyMšº6E½Â§XãSnHˆÙˆÝ‘jÞœjh Äpc.KˆD‹xi@SÁ1É'`\“Ûd\¯¯qiPUZ€)þÚz j2àúÈ4Ѿ^‘¢i*t!b€¶dU¦uežoQ¦cq®g¼[v‡UºÃ-Ý䔯ú¤ñÙšÉù±³ò- ûx– ð/*M˜šk½×¯ë•qËÆ-c<ê‘nÍh¯~”S{sœùV¯ P+ý‡¼È¨"¹¬·BÖO5@&¦·£åÒJi-ž~©`'°Aß^l[Ÿ­"ÍEºM=õóbÖ¤+Ö£×¥éS¡¨hV&hW§Û׿'"B;Ýg¸ß,»Ãq»)j”VºË.Ÿ”h˜ˆåT[›î`nز.Ã=?!v¢×p‹U•/—îéÕã—;7}þöÎ~úY÷‘ã—Oµ]<ÕÚu¬¥í«cG?üø³·ßþËK/üjÛÆmeSÜT:!Ïó@PÏÕ-îë©êc]Ýßµnh<æ¤u%Žå…æ¥ù±Ké8•&Õ'¬(J¨)N^ž—4;ÉúQq§:âf•b„Fu‹ÝÎ…»¹_üþù?²˦çöìúí«/¼÷§ß|ôç×Þ}íé·®X2ñ¦›z¥N}ð®kêÿôÛ׿øèƒc_|~æÄÑ®ÖÖ®sm8%á†ÛÕ6“ Áu¼–Qþ¯æÿæ?þoS¨jΟn»rîܹ?xm}cõ·NɈgq£AÝ?J¨Ù ÝAÜG6h›#=„Lœ…ª4icoÕS#b_½ÛùëqŽ—o3<3Jýä ª=Cä;Dmëµ£¯lW©rO©jCž´¥ ª¹GäÚ4i]fÄÎ"ÝÞÞ±”=[‹u;Kb·÷2mÌÓ1[_—m\—enL3q`3º!ݾžVºmc†µ1ÅX— ¬òÊx¬ÓˆæD† 7¯`óáu8Û J¢±MÄ´ï °Ub!m!ä»,E·2ÈíxUªª©ÐVßÃô‹›we 3µèšT58«­½=[z¹›‹yæÕÙ†5Ù¦ …îÍ}’¶ö +½®( DýÚBÿÒTç,§v’Y}s¨M14d›E?gTHŽ ÉnÄQ1[Y‘&5Góú]–7î5ÿ궘GE=CÄ3ƒ#ö÷—öö•öô‘‘÷•è6fElÉ•7g˃ÒúŒˆ]=5{{ë¶©6é¶ô1mîci(4¬"%æÅ®ÉE,N a×L[Òìø­7g:@‹Ñt‚‹:\e¼–:$GF9([B`„Ìα¢Üª)3«0à^`G§Ë€—_S™j^Ð/L]f™´=á7wkÇšeC5Ò@4Ê)¿-`¼5 ¿--ö¶4ÃÈ8ù„lû£9·Òí7Ç©úë¥~FéÆ$ã}…ÉwdÅÝœî¹5#þæ´øQ~ï˜@ü—=U’Š º\­:C”$Oyžõ}W´´° ¹¢Ð÷¸7ª’ÎÀøýÝúÁyÑ4ö47÷qlèåXWä¤ï·:dz'—•=<•…É33ÝwºÔ#Œ#-²[âôã‚nz·{´7¤;˜!b5Ë…3o2Éï1)î·öÕÊú»ÌÌ=üΟÎ~y¨ëäIôo¯tt^:×~âNg÷Ŷs§Nž;òeÇ—ŸœüÛïþôÔ†æ™ã¦L›=Ò*MÍ5Ï)t”÷ò²¹9*z{WôK®\Þ+±²À·¬(ayAâ‚t×´8ýÃíÐ(©¯L"¯&HR?·»yñ¢·_yå/oüöøáCÇ¿þüÔ‘ÏZ~Ò}ò“Sÿþõ'ëLzó僟¼÷·ÖãGá1kiénk»Üy‘Ì%¼NXóË]K‡W òþ›·ùÿ†/ãCà W5P³¾;@Ѳ¹túô·ß|ÓòΟŸ©\tW0¹g¤4X9"&r”Zº=Vy·Yq—>b",é¶¥™¦úó¶ÎcæÔ:w\ÚGc#&[£f¸E„&h>3×€ñ7WØI+ÅË…¹YàÊh!"Èà^µœB‰'ªheÎдIÒËd˜ÎÒ(ñ=Áý‚¥'ßR6ã€T> • CçÇ)–ú•MyÆ]¥¶ƒ#\¯Üdí6Ëoï4½>6æg£eGD¾0"ú¥QšGhŸª{z@ÌžÞê=UûK ä–-¹ÑMiÒ†ÌÈmùÑÛ ”ësdu‘ëŠ[ú»6ös5öqÐMªÏ5“U ¥U¯¯M2¢Ê®  \œß ºÙq¦Ç\†IN=)™Th§@}D‰bu¼BR™bVÍpc=;3!ö>½Dkî–é–XY©Bê-Ž3ÝôNRÜ<ûÑŸ7-ÿņ/6Vl.{¬ìö¡ÃâŒ#|¦á¾Ø!>ãèT÷”á%ç=þêæ5¯ïÚðÆ¾­zfï›ûwÿr˦Wš7l¯¨˜sç%Éñ½ãú&Æ òÇHóIKäé5³DOÈpÄÞj‘ôEÍÊ1ÌÊŽ™“©i˜¸n`RÓ€äU½½UyÎ¥¹®eù¾e=“çäÄ=¯¬‘Š£¥!nÍcƒóš¦=ðܪEOVÎ]?u‚QïÍôÝâÔÑ–¹ÙEoäN·q¸Q1*Ζ#»© Ç¯ŸÞwò‹ÏÛN»ÐцŠÓÅ®NÁÈéè@ã…_h­|sø³³G?¿|æË+'þyüW^^¿xÁ%c3Íã²c|j’j•4H'±GÜá‘ßåQÜãVŒsÊïµGÞk‹g”nW‰â~R?Mô“1 ICRSŸY¿îËüãÈgŸœøæÈÙSÇÛÏooùê|ËáK§>k?ü×oþñûS‡?9wú$Ÿ \ê>Í–Ëç»Å‘ãÚ §šðëÿ†$òßû ?L5´kD¯FhÛ\é:Ùrñøñ+ßùà…ç+î;:Î5ܬ¾Ç¥¹Ï!Ÿâ7ÌN3?¯ž›¬ªË·­-¶±ÿw4íxþFËs7ꟽQõ̘¨ý#eûn²ì¹Åýä­^Ö¾›=»G9¶ 3odÚØO·®·vM¡²>OÞØS½¹Ÿy{릾æÕùºM}=;'o˜¼©$ k-áó˜åFRáq}̸‚ƒ7ƒ¼Ãôj²Q†©”{TÒݪˆÛ”·*"n–GðS:Ùñfˆ$QfP`Ü%|élƒÔ‚íH~€&0×#CŒŽäÔX§bú‹á/-`0¯Ÿ°Ks}‘ ’sâ™7ISÒ»ô¨Iz„9¾5b*‚0vÙC‘[èNwÉaÏMwDðmÁâÖ´"Uµ:+fc¡nO©éùöŸ¶ül”>”[äÏ “e88ÚüâöǸŸézf˜}ÿ@ûžRËöÞ±[zê7ä©›r¢› ÔTz»úš¶ö1¬ÉWo,ul¿iˆ¯‰Æu‰“ð»IC^FÁpŸ™ïTKÔx£tGŒt£Jºßk½Õ¨­–ߨVÜªŽ¾E96:ò.µ1š´¨fQ‘Ž-¥nŽQ«•ÑWéÅ X.õ³èêß{Cùì~óÜþ/Þ}ëôgœÆêýÓ÷¿þëß>øÌ__|îo/>÷î Ïþñ¹§þðìSþùÁÞüÍáwß>ü÷w|øÁ™C‡@}ÿçǧ>=ôéŸßýùþýo¼tðõžùõsO½öÌžƒ[7¬[4牱#ÇeޤÊöÞžåGTèŸk—¡}0];9Ã8?ßµ¬_ j`FyqòÄTËíåHsT?†Ôƈ! ±.¨›ûÈ+û7|ø§_|óÑÛßüýíCoüúíû^l¨Ùrñô‘+Ç®\j½p®åbW;¹…%0'WgÔߣ0¸ѨÿßK5”4a$ŽHš¢3,΀"ã\¹t®óüÉ“íþô“Oñò޲¹³÷ž˜æ˜àUÌH1TÅ-ͱ,IÕ0nÌ3n*6íêoyz¸ãÙQŽçÆXÞdyáVËÁÛÝûnJÜ62yÝ@÷ªÞæê<ͲexU¤E- F.I•*Ó"VdÊWå)×jÖhCæ,ªÊTuY< ‘ÀŒÑa|²l”jÔÂGBÖ‰)¹6ÄSrP ¨šsûpË vq»Š©´X$Ÿ± év%`¨ &ùT‡|n\ôÂd\`´¹ÔäıÝ:(qÿˆàÞá)»‡ú÷ õ?3*óà͹ώÊÜ5(°}@`Û àöÁi;‡f°¶Iß:(­y`ê–ÁæiÍ26öO_Sì¯ÎñTg{ë @*bʃ¦=Š:Äj2ôuYúµ¹¦N;ûÛžî:8ÊýÂÇ‹7Zv“õç·¸žãÛ;hˆ¼M# SH¥r©@wAž$•F+†¨ÔƒeòarÙj%içMô1 ¥t›J§‹ºß¬¾îŒàíGOeÔ¹™«g>ñÊÞÿxû÷_~þÉá/>?vôÈ7_þâã[¾úºûø©3‡¾8òþG?üèä'Ÿž9|øÜÑ£'Ouœji?yêô±';sât˱SÌL:Îvv¶v}ýÅá£_}ñÕŸ|õùÇÇ¿üôÔWŸ~õÑßÿþÆ/ß8¸ïÝWŸûÝ3[_ÛÕðóMË·,œ8ýÖ^7çÙ‡'knMÖßäU¶)n°Ê†ZdC¬Ñ#¼Æ®¡IöG†÷Ý´lî_=ðÕgï?úñWG>:tèý¶cß´óMÛW_œþäïÞù#šÞëÊgO½mÌ yé½RâåfÝwëÍ;6lüêÐí—p¸((9Lm8¢€Á\ ãÊå6q“3*>ßÑ}¦µõHÛ©ÏÛŽÔq⣮ùðwõÊ+{¶½¸mÃÏ·¬Û_»¤rüö͹7?ðPaÊCyÉãRwÆÆÅëò[ÆÝÃ]¦l|`‚ïÑ1#žmÞ„¾Hß|},oèôC·…9QוîÖ+]§¯t´D~ËØHÀkùSNJ—„ùõµ’&ô&ôŸÿŸ;@ýkªá‚©†vpŽajìø•–SWN?üÆo^®¯Z{ÛÀÙY¶Ùi¦†ÁMƒ3Öõ‚o’¸`Êïîîû«±…/Ê80$iï@ÏÖ>Öu…Zš ˳lå©–™^õDàß0åÕbÜÀÓô×T›¸GæÆ]]ó|ÒüxéXà(°±cmƒÖ DT‰@Œ 'tŸuôà€#ÙÇpaÎA° ±A4DÁÙç®oŽS7ÇŠŽn^œ=.ú3µ™Îú|gm‘©¾Wlc©eÃ ÛæaÎ#Ý{oôî¿Ùwà¶„çîHzê–¸Ã,[™v³ífß=Èjqw¯äE ;z&b޼§$™7ÍùÞæî ˆvæzÖe{V¡¾èÓãK㈥A„š†¢äõ}R6”¤6—¦nüåýƒ_º³ÏÓ7e“жòm,±5êjzh«r,‹‚¦Y^åD³ ¿‡ãC 3ƒ cn.’É,Ÿ4Ã'Í"«$^dƒ)…ò8R< ÁŸ„¦fzŒS¬÷8t£Mªávã ·sD²ÿ¾¢žSz´gÏ[â}c¬–Û]öñ>ÏÃ>÷c>ç”xÇ´$çÜTï’ì䥅å=óf·™oHðm,Ÿÿá[o~þñ‡_9|â\Û‘ÖÖ–.TÕΟji=׊2À•Ëç.‚ÓºÔy¡Å–ÖÖSgÏž>wöL;‹û¨³ëbç…Ëç/µ´¶·¡ wù ï¹:ºÚN¶;zŒì—§Ž}úøWg>vèƒoþù×–Co=ü÷ÃûÍß^?ðæ [¹gõ–ŒskVBo{ÌÀ8ë„A½7Ìâ×»šµgË_~î£wÞ<üùû_}óÙ‘–/Ovµ´ÛÙÒÒÒÖÒÚvê4B(m'Žž>òÅ¡Þ}çÍ×^{éÀ›¯ýì׿øÙGÿø[{b˜º».>Ó 4x-DØeˤö+ß¶}{éD']Ü©ñÅ8¶«ãxÛé/ÛNî8{êÌñã§¾þòôW‡OñÙWûíŸ?÷òÖõ¿ß¿ýÕæzÚÈåc‡O(ΛâbSj¤Ò8ëƒJ4®~ÿ·¯}ýÁÝg΂„éèì>ù[¤£X¢óB>¡ÑÝÌyæÂ…nr‹Ð–}®. ÔLøaýÁúï^þ|9ózU˜9”C1tIOð±:¨mNœ;|èØ?Þ;þÖožš>~V¸ûl²²LgmAâÊL7žÛ‹Lš…,d|8 uI´/8° QÐkxQ{Ð :-Qâü2Í,œ¡æbüäœ\Ô®*ü²e©P855Zž,ÑBçt¬hÛŠÓž˜¿¯.äë§xÓ' Æ4ÛÆLOSÄúlŸ°Îò•'ã%´†æ'ÒµÐ-=˜i­éa«Ê‹]ž««Ê×®*66õ·®`ièk¬/ÖÔ*zijó£ç%H“-B%£ËSå¨ôT3#+0ptmÈ3צé„]¼®.'¡±$oIQÖ‰î1.Ó î53¦m_^õêÖ-¿Ù±mû‚3nvoVƃ™Á‡ÓýS'âžHrÏLv–½K2+rƒeEù·û⊔Šñ¥}_Û¿“Ä|ð^ËùŽ£]_¶·Ÿý+®´·_‚¸{¾íB7ÚÝß9'‰°mBwf5qùJk'°Uåj=±¥£ëÜÅoÏ]¼|î<@n/ûöB÷…®sg[ÛZÚZOu´µœ;}¬»õøÙㇾúä¯Ç¾x¯åë÷¿üà­Oÿôú;¿xþ·vcBý—W¾ÿÛ_r:;ñÉg~ú›¯Z[Nðhµœû¶»õòù³ηwsg ©fgδœm=uæì‰S-GŽŸøQ¸/¿úìä©£"#q÷  Tñ©EË2L¤°!Û`˜ÔþíåÖ BëàâåîËH¯\jïîj#™¶µµ‘¬Î9Íxèì7_Ÿ;öõùGZ?ÿèôÇï}ýöïßÿÅ oìÙ²eÅâûn»»¤àÅæ¦×ŸÙ{òŸïŸþâÐÑCŸq*;ž’å*†M¼áŸç#Ÿï•”øD±¡ iX…o?ôý.Q\Ï3Wÿð»?ù_ÿŽO,Ð4¡W‘j®/>¹à ã÷ ừû"§KJÓO?ú`÷–Õ7 »Û®Äeœ›ìzܤDh®Q1O pžÀ}‘猣ãiK§”„CU_fšE†är B)Ý„2ü¦>ñ[û$¬Ë·¯íaY_hkÌ3q£‰Êjì’Ä´6'¶!‰˜Ø-Eñe¿xCÁ³ƒs÷ô n+ð#èTá«Èð•ãæ¥¸y:?쉽Ǭ¹Y/»Iu›Eu#æ^wÌ]öèÛÌ·™¤»b#2E?«|ÄÍ]ôˆ;ÐV÷SDî€=Zã1F¼Ú$ø¡óãKÒ5€cqä\ˆ^PÌõ…z50øÐ°µJ“­ÒÞ`9yäHÌ÷ O“¥©Þ…‰öv-âÕÈŒàæLÊY—WúÆÌ¤¹ú û <<„„M¦<`Zœf^–íX–m«Îµ×yJâ×öM¨ïå]U`Ggmޱ)?¶±ÐĆڣkÈ4À¯ÙY|rxï ƒz•å¦÷”¤ûí^RþçW^úÛ¯ÿóOo~úÇ7>|í/¬]5iHßÌHi€^vƒ^>66}ÅûõQãaÙë£2)ïvY‚’42Þ»{YŇ¿{ýØW‡ÚÏŸ;}¹»åÊ·§®|{š¼qåŠpgÞ8¡‘+ˆ Q óp5/Cçoñçá/a{]Gªw Ýá›ë"‰ç<9£Î`Wû™Ž¶“­ÇÚÏ=wê›Ö_Ÿ9ö%õÉ鯿äÖf:sîÄÉ ­mÝh,u yÒȤë2?ê¬çQ¨2ç_¡ßAÅÒy”öÖÎ3m]g;Ï·uŸoG»@I¸Qd]ƒ°ŠwW?óõÍ1ê k¿„¾’ ‹Âl ú½ç;Úη·^h;Ûqòxûñ£m_Õrèóo>üÇçïþùŸxóý7~CVl9ôiÛÑ#]gZÀö’U¨gDþö;Üþµ…H¤•»þ×g’ÿò†S ×*.÷û×Èß}'~bñ³ì¤1~îëCíyëµêŠùýŠïóX0«1=9ï0œ(!ìƒõùLãfK™Ï²*×ÛT¿e@ÊŽÁÍ}“òÁ»" Y £!Ï…jÊs#z•‡GÉžþ){žþìÈ”ÃýO IÜÓß»£Ä¹¥Ø¾©È~`pêsC3ž’…”DSºu±5â’(äS М¡ý °Kß¹Lqh„Ê{TÜ"“Fj¤[„Êît2Ìx­l*sÞ€«:'‘ „ÆòLïŠÌ¸ê ᅢʠeÆÝœS )¡ëe“æ&J‹‚Ѭ…©ŠÅiJÞ”§DÏKŠb-ô+16 ‹KÓ¯SA…Ó=üM‹l¶[=îšl–M4F=fŠ~ܦ™åŸ`®Ëõ6öôm.M¡#„O7&æËÒ “T('£LÜöŽÈÙ?2o÷°ìíƒÒ·õOyndöó#3Üöä0XWqÛû:·Y6å[÷õKF‰eïð^u½s&|Cº S'½ûÂsÿ|çOŸþóïG>ÿ¸éÉßûðõ—wU-˜}óìH©ˆ„% V*¨D‰…3»ÃLulVðåæuÇ?|¯õÔ7¤š3—pÚ¸Î3T5¤rKø¶äÞø®‹bÞ…n˜«ÝLîÓ0»˜f÷¶ŸÐÃy‰ï#–øJ8"å„rN¤–jëîhï<×ÖÞÚÖv–E9е ð5ºb]›TüCy†=,Þ_ºýN g;/w²:.µ“pÎ_ê ¶‚¢ãñéøË¡¿•’(z(×WZßý'>"йÐÑGœt΃téîä…ôs©û<§1ê–ö³­àyÏoi9zœ!WGË ÆI ¬¹ÆP@Ä£Eb ¿ ß~ß¿û~üþ¿¼“ÿ÷ÁwA‡%ñ+Þðö@(ѲÏjíl9vùËO¾~í¥'ç?1­8à%Ǩ•¹žª âLÜÓ¸pu—-òÇ,E4»ÀQ›§^•'¯ÏW®-Ô4—˜võ¼)ý•;rŸ“ñÔД]¥¢½è l.rCŽÞÛß¿»4°ñ޾ö-½ÌÍùú¦lµÇd‹‰ˆG¨—åFi¹S±*Þ°Úo®IŽ-ƒµñR&¼©¶Š }qб0Õ¾,dz’îêlúb×:X=bÁÞ@XTÕeÄÔ¦k€ß°êÒ4µA ººT ´ÁÆìØÆ Êoõ€œÊšåª4ÈzÊ3þVU º<Ž&’]’¤FTöS}¦ <^S®zCvs±qï@×K7¥ýú®ÂýƒŸìß?п³O­!šB7PƒÙÓ/°»_H€Í½ìëò Ùª5Aù*¿¢Œ)¼¡­È;ÕRi¤4&ÎõJÓÚ–Þ?qäLJÖÖ£mG/¶ï>q ûžÚú\MùŠq7>w·÷ÿ´÷ÝaQ][û‡ÎÐëP‡aÊP‡&¨€Ø ö5ÆØK4±+Š¢Qƒ½Å$&Q“››¨1í3×ÄDcK@)Â0ÌÀPå÷®}fF¢7yüòÇýâïržý6gNÙgÏ^﬽ʻíçË¥9];¼ÔaElØ8™¤›‡óüô~ð.iîß®W•?jiDÔ¶¶`ZÁ/ÉJ&M äE;Í&¿ˆÖ5Â~ýuÚ¦'4Koïèa@¤cúY@› îÌšI´@ÙÇÜC â­Ü£x,m‹ö™¤‚hÁ„ꄸ|X}™á·±¥Iˆ|r1îÊ 6Xc„–ê=•0u/ÇP X†ȇÒðÌr4«BDÈ#4J>Á @ހĬÄÖˆ û•&zTO6^&_ÀÔx„Áu!õûçAæÏ0‘}9vO;ý¨ÝèËbPCÿ³þEOµ"Z_¥)¹Sw÷òåØ:uÄ‚¤ÀÌdÿ­=ò»xæEÛl”›­•qRn™?·¬¿0ÀvJ.>>F†rt°÷îû¹Q†/÷NŒÍú`“e !gùþ°÷b-Z"®ö1Ìò£¨Ýl™Ñz™Av·Îù8'Ë›õÐB¹ ¹µ"A®Ã©#ö¶Yï눵)ÁźÚÏe™ÄÖàL™¨Â±}¶Ü_p !HާµÑ®YÝ×wåvåvv_Ÿ Ìîè°6Æfq·XÆ- äVÊr:ZåuqØÜÅys‚óÎdñŽÎž[c„oGØç†ÙäÊ­™¼»Í‘öàðÉvÚ›R”3’”wF;çË-vFY틳{?QŸõÎöÛbyáF›#M³ÚìQ $vÊh‡í ·ûz,ýnLÈ·£ƒÿ1Àkog*â•óä@*Ó pcÂøƒU«v Kn%r©@à–åoB½Àåøs¹~†,À‘8ÛÍtˆ%׋¾%w>óñ¡×®T«µ ê†FuCƒª>Žz%ЦêÎ%ÕµŸ Nù纥™ƒ{Mñéå8%PeÜ#NO\¡ñGS Þ+Äö$ä€-w@ sø2¹åÁŒöÈÝk»1ø <Å܉¦Ûbó;a-Ôê9¬®S˜jðT€4;â]¡GæjqyÊ™Z"4µÖ4Á·E…îMæ zm’%\¥ àôÈðGpÎÿª¼(ÃZ‰÷¢.dß ß%l^¤©˜püA ­m‚I¬´©æ~ÙõÓ_lÏX3²óÜx—…ÑÖY1–»{ºîî긭³ Œ«ow²Þ”d¿¹‡{~Ïí½ó:›fʹÅ~Ü_nu(·%Îzw²ë;ñΈöAܾd)¦Q ñÃò—ˆŸ_"â–ûdšmǪ[ùqŽÇí .;ãÝÞéà¼YîŒU ·†{¾ì.ýENæóÁToi6C6i³×f“ÌÌÆ64㳉Áxs,òe<ÙÚøu+¬0¦èÁpˆÃwÃ,ÂcÄ܃¹>Øsoør 8œ ni—fL¦ãNN¹ Âõ ®¥MÉž[S¼ò’<6Ä;gÇØg†Y® ·^i›mŸa¿&Ôze€ùrnk´Åæã·Ã8„&n‰6Ì5Þošg²+ÙjG¢Å;ñf›ã̶ÄYmëlNØ×ÍcOŠp['ëœp.ÃIÜê@QFº»ïè$D|ìöxŠLÞ—´+!hS¸7¡X",•`¦f¹>ÌÔÊÛãw'8íMpù ›ß†(×e¡®ãEvé÷]KÜ¿t¡¼¸ˆ­rXEC#¨£«yûºÊŠ{×·.|âÓ¼µ ÷íå- 7æüXðü´AΟø¢U©h­‡.èh®V©16Ø áUæJ€¬aØŽxiÔ0ŽLLt3’Mº'Ð@Âtª­ µ‘6¢=„³°a¯Ÿhð \{F›‹Ñ ƒ*‰6ˆ ›×èy@t…`šI[èãQŒ¿PkÃÑ"šþtfv"ÀÑA fè&G¤±`îý‰í‰Ї@&_8 ê³Pá0.Á}ŸÙ3IÔI!îö…ú…=–½åß~‡7Õ·™š­ïRT Ç¢W Å>Á‘`€ô\ó,6jMCyUùõë§~´vzæ ÈŒd-½%y‰Ž[`–`³!Þ*'Îvm‚}V'g$Œdu´ÎŽµÌŽµ^cµ:\°$Àpž'Âz¹™à‰r£$_êfN¹ÏnFÓí¸ àÍCÈ.hµàò5D˜Mf ­½"5[èa€Ó–‰¬2Ä”Ïëá´Dä1O(œjcûª…ÕD ›Wmì_³¾æ$œ`‡¨W›ñÖ–í¬¦8ÙÎqwœïé4ßËiMß‘žKÂ\…:. uX&·_.·Yf¹0Ðxe”Õú8Çõqök"­WÉ-°Ïîè¸8Tðfé‚óåQ¶«:Ø/54Íq ü ÉŒ–‡šgÂaM^-ûU¡V2ÓÕÁ¦«¸¾\†Œ[a´)N°¹³Õ–.–;YPI°Ú˜`o—ïÓÑq},*ûu±ö¼fµ*B°HÆÍƒì…›íÅMC  37ËÃè-›>ö³Ü¬&Ùr¯(›¶h\,õ3X)3Bà ʆ»Œ Û©b‹žV\_©Ûç»óÞ­ªª„kµ ®Í4mNÀN}C5¸F`w-/¼Qtõ—_¿?‰½EG¥ÅG¿šžztß^„¦ -¥¡RÝZal}T5RËô ^xÈ®‰=››ÆÂ£ y– G ~ѵl“:˜j#ylãÿ>³Ç¿x¿çe2Z©"¡ç2(c­"b729󿟺S gè<ŒðàCäQLÛÑg˜.àå›O‚†]¤õóóAíˬQ¡ÿ•F Ÿ)&^ã‘}Ê7€vÏZÄpñ…ÁVÏ`ŽjpÏçúD÷γ¿h§þ«ÄWAÝŒ¢}ÕfjÐ jë±¾BkcUkSy½ªJ]Ruÿêåcï¾·`ü†ô˜¼^ÁÒ¢ÞK ?Ø_¾¿Ÿ|OŸðm}¢¶öŠÞÒ;zgÿØ­ÝÃÖÇIWExdÈݰ_-Îîà½:Ì}e°Ë"°xˆ­3dB˜Yز!pÎ9qð¹lïæ·³GÀ޾TRrc°t—`Š=ñG‹Ž-¸Î_±4anŒsØ6‘õ ã0 ƒûbýñÞ6µ£á8¸œœ“„¤§q ('VPÐALDÀ OÄÍgrŲ`Áb™ù›R£ù>†(KC­Þ 0}Kf¶2ÚêMv¼Kv¬3ÊÒ@2g [Sn‹xé·cݰjN¤ëæÄ€‰²Mɲwz‡îM>8¼ã¡Ñ{ÒÃv¥Ë©¤Eì±-5jkßÈM=ä¹Ýå[úÇ­í&_çóV´û¢XOdÖ¬èì½¢‹×›QÂA¶½MÇ‹Ì_ó±æï2#Àc¦¯Ëâ•Xl7^œ—,…e 1û»yîê*ʉrÜØE2'T˜dÉu÷sÿâý½×o\)*)…®$d '0ìA3ëG…F­A„bbëÔÅî]»rþüO§ÏþpêâÙ3U¶657Tk4å^ÅäI÷ƒÍK*aÈ2M` >jrÓòó¦ÀèŒ-4· ñ$yÀèâ “5º_ÑC þÕ×QáŸI$U@[ØÝpCl¤ 1ãOSóŠÁkžˆ¡Xcˆ5›®£§`G£— N޳ŸWR×XÑ›gµÇ™Ôãí¨Kñ/Jx¨ôºÝˆ0'CÍâÈà ­ Þ8¬3ÿ¢B'Ó«óð‚¤b1Ц={.ÐO}p‚ëÄS_Ñy´ÊÙK 5ì»`c² Ô°îå9‡8Œô¼µQÕµÊZdkœ>öîÒ©3ã}G¸qC­¸n¸)7Ô˜dH.!$ò²ßÿ‚× kìSÁ´`À 0ÒŠ<‚{áû@PØà¦hnVæ’šbí?,0ºØ×t‘”2HL—؃q®¢Õ!^ËýÜx ÇÙšŽ³¼âh=ÞÉfŒƒõ`Kà ž‘uÞÕë-à9 ^ñr˜,N–8OÛqŒšOp·œ*µŸâ¶8J´8ÊcA¸ËB¹Ó$'†ØÎ²žh5Ç_0Sj:]b¼Dî8ËÇt’;÷ºˆ›æm4ÅË`²'7Ul8Å“{Õ…ïÀ!chðʆËuþеù`˜Ä—Û‘›€Ó„Ô?(é\ª×Ë„,·h$\Òpu0à:sÉÖ\?7óTóžN],‰- ©ˆð™p fT⑎„{:w3š&1Cf¹8È ˆ·ØÏë—!Þ/3V43Ô­3äÒN|z¯ðv5Á Y ¤Âž…Åh·€ö¤²F­Ô °¬º ‘"j%fÈpñ€¨áeLH¡þ}uô#ŒÃdÉo®AŽXåõŸ.ݹkîè7×ô‰\Û=x}Jp^ù¶>ÑÛúÄlîµ>1"«[ÂÒä¸9#ÆøKº9X"ܽ§Ð~tt„Ômb€çÜpŸŒŽ:Ëòàê×ýή¾9\à8ž‹(9kàÏ2\*6BRäb‘12+‘#‰õ‹±P&˜0¡ÕÀM¬ Òa.ï4¬oF\(d„åÃåÈüDäòsI†\/ ®È͸BÃAî¦#Ä‚±¾6ãì'R™ê<-ÂmF”ÇÔp× 2»‘ÞæÃ°¼H¸Ç7ã±æ¯ùÙS´yëìXÿÉr¯Q>¶c}í&ÚÏ‹v[×#x[ZÔæ>AkDyÉþPlr:zgF¹æv’ì캧oèÖ®>Ùñn»ú‡å÷“g&æì29Ò7ƘKó÷Nñrí-“¤†ú÷ ’öôõ ðê-ciožìí2!¹ãâ‘i³ôÂÂ…ý‚}ÇÅ…MõŸ.wŸâ87Èá ?›ÙžÆ8œ‰ðWã7dÀ‹é⣽u•ªæM$­ºoÙÐÅ1ˆ¯ÖtðNcæcF¦!~éš®‚'ˆ‰…~<ènÂÆöSÉ×þÆ<Ëgöÿæ†üm@Ê›×èðC ÿæÚ6WñUý9P¡W{®<½Ësmyú«ýîÚ¶hsÞïÎùý³Úœ¥“Mþ‰mo¥¯ÿîìÿ¯þ¡qúøI}S]ecUqkU¡ê꿎®_051$ΔënÍ¥;q#\ F8pC¬¸Aæ\ ®·à\rSÃîbÏñI KF_ÿú«;uè/Ær±ÐLhûÃt¬u È wØëùvŒG® r9çÇzíK x7)pO‚oh!e˽-{Òµ šÏðw‚†ƒµ2K„ó<Þ;Í’ _óq^ q,uN÷ õótèél3DêÞOdÝ×KÐßË"Ub5Pb=Pj“.µM÷³ë'ô[¤Š-±ïëiÞÃÍ8ʼnK¶Gû¹îx [ƒ~.‚N‚®Ž‚ŽÖÆAÔñ°èç<Äß©—«a¢€ë î}gn‚ˆhiå‹Päw#ˬ³X‹ë!HxškËõYŸ»y7G“Nö–KGݹbNÖÞìÕÙógOšÖ§Cx°Ð^hÀ NŒ_7oÖ±ý»¾ýðàWïïGÇÀÖEs¦u‹šÔA<^æ<-H¸8R².>pk×È}âvöOÊî7ÒÇ9ÞÎxJjÊ¿Ž\­z ¨,†…†YøÑJc‘ ©ö_Ôñ)¹kYô;àQç7ÇÖöä¿Õ@æöüþoÕÈöÆüåÀ…† ´/1ä­ª[kŠ/|v0wʈeCRõ›Ó%xz´tª\üz¨èõ A¢.м ÖoDzE§½{éÄçç?ÿ$ÑÜÌ Ãg÷è8+18£gDÞÀ˜wúE¼°<ÌqM¤ëú^—]à°JæÂÌ=‰Á›#Å›ÂEX;i¥ µ›±µ°­°8)Ó\$Ì rEPMf°ÇJ¹dQ˜÷Œ`p¯£9nÎu9 —õ ô–ˆGEˇÈ}‡û §f\28L’æ=(Ô;-T<ªƒlx´ÿÐp)ޤ‡ˆqLJ„‰G‡x—û’ºuw³ë' ‹ #G9¸jÑñüœS»7^=/{L¯9ÉASb½fD»mM܇Õ%uÜ›µ»·|_ï°÷ûË?L•qlLâ§£“v¥ÅoJOš(—¤ù‹w,œ÷?‡þròØN]úöäW¾·'gmƬiÓG=²3¿à§Ó𢻝_)½z©¾¸PyûƯ_ûpÝ’œ é“cÁÍ2ÜÝz’ÄižÜ{y\誤ØÑ=\¬„úîÏÍ|øÛMé#¢þÏ¡#G J[„a0£Å™¿-ÚüåaÜ~áß¿5¤p`Έ¤û:õãZek­¢ªàâ¯_>°rþÒ}Ê<M{yÚò*$¿5¤_þ’7NîßqöØ'·~:õðÖÕò;×ï]þ¹èÒOç¾øè`ÖÂECRFËÝÓD&ÃÅÆ¥óB„3%¶X”g,ÖÛµàÆšS?·ç:›.ð°Â.leÿ/“Ø­öwÊX.ó2]"2³}V¸hU¤d¦¿ d0ÅÁ¼L3tßôæ¬Oó7ß½ëØ®Ýßúàô¡ƒ?|´åÇ œ9ü®¾à (VN½¿åûö Žºo·o:³oû‡kVlœ9Ñ&_îÛõѯ|sâú©¯ >UüË÷wþçóÞßr0cÆ’¡É£<_ó·œh½0ÔyIˆóB?k0bY¢5–«¥æàý[.³ž`77Bœêf9$X|bGÞ•ï¾¼wéÉÐÖ_Àâ~ÃÁmÄ…Z¿Ö¿×ÙoŽ#+P©ª¬ÔK%¯œèG׿…ŽçÕƒgŽè/o¯´÷À 55eÑ4»§ìU³º¢µª´þþ­+_~öᆬËÞü(7ë»»~úðà‡üüÅáWÎjŠï–ß»YTpµôÁr¤ô–Ü{ðÛõß®ýrþ›Oåe.}%õ•ÎCB„ÃSM†8švµž"uYæ·"*0#ÂEˆÏ[ÞÂ¥~™A’¬PŸìP¿Üÿ¼˜àüøÀ-Ñž ˆ\DÈH÷Í]×ÄL’:ö°7°~ÆÄÏöä­ÙÅ‹ŠßŠ*îÝ/¿û[ÅÛŠ»jH7äR GyXp£üöÍŠ;·ôÿV\W^»Üú°XUpãö™ËnÜДÀõV\]VÚ¤ªBžâÞÍŠ[—+®Ÿ»{æË¯l>°|ÚºA Ù}ä9½"à}Ëï¾µs–&y¿kðá¡X.j['iv¼tEç A^6ÃBÅ_íÝ‚E Ë#SQ†ðu”U%Jmeù㺚êòRTxwF}•ç<®Q7#ÑænAñ¹3ç?=üáÚU+F ™â;P.{%%qÖ°AïçoQ•• £°øay-¢ka‹Ô†|kÈ¿…šçO;Ô<ß'íGþc=¨­µX* vrØ‘…âö:Õ#•BSZX^píÁÕ‹%×/+îÜB2xmÉýúò"Ä÷Ô)k«+4µUåþäPk*ëê”ÕeÊ¢kç¿:²{í[ÓÒ’„‰Ó|ÜGù{“IÇøxqu&t˜èå6;@2×O<[â9C$œáî4ÛCø¦Ä}I%Zb’š»E`û vÉŒñ9Û(o‡gó³^=ýé÷o\¾sóÚýÂ"ÆlP§x¨‚³ž–Ú*ò·Ô©ˆï@¿¯¯©nÒh('llµp ;4,UUMJeuÙCPhªÔ¢,))C¢8¥‘V£(©­(RÝ¿uç©KÇÞ›Þ5d¤%è1ÁU8Ò’Ò9Ç#Õ›ÂqXas&V.ó0™ìÚËÑ`€ŸËgù9¿]¾Pzÿ7´¸Ô@ ~knFîÚ€ô¶¸!¿FSšB˜¦ e³RÙTVVöë•K_8uøØs~øìè…ï¾þõ§AH C?ÒdUHrÆ„÷¯B U[´ù±öµ÷=¦ÏÅCÂA#@›Zª¦JQ_£B–kCªFYN•¢Õе²´¦¦¢áÈM(¨ +JÀÇ ¢3eMe…¢¤¢¬PY|»´àµ3'ÏûøÜáνwðd^Þ¶Y3f§$¥‰½ºYY¦˜™: °±êmdÐÀëcÄ 07L pDë¶ Ø] Ç ÍÒÌz ÍSDÿؾñÎÅ3j𢔗–+«ªëU×=©©£„7ø=!ƒ|pE}Á¨AÁm0;K4öDR’"ä #ÁQÈÙ{Æ[Ã}©i«üˆUoln¥@iñÝ_oŸûzßòésºÉ{ØsÝa¶âÆ!a|ë(-w5Á|càjœlÊö;÷ÏTÅ…è%·ü)|²a}ƒöaykµºªZY…NÆ9M Àmhn„;ºJ ªš‡DbP«(m®¡Aiª.kä$V×Ôª4õ5Hb¯ù×´šößÞÿ‡=@¨zf«iI„T”ßÛ#dY®çê²ëÉD@ƒ8ÀÆÇ-VJÆd,d×XWrÌš4#xfp•(KïUüvKqëFåÍU×nŸ=wîÈ‘+–ÏïŸ:,8¤§‹KW[›DY²…Yo[ë>vVÝ&] ¹ž&X-K·1€U0Œ‰n.ÖÖ,ÅOôÍGo_½ ¬*W!^¶å ‚IŒ†VRÒ[Õ‚rùÛæü¥ÀVD¤ÍÀ¢X ;ãf$T©Ñ4¨Aªô¸U…ø[(xˆ—mj¬«Ó45Ö žXe᪫ÿúxí¼iIáüÆ„®êœä“-aí³mѾX˜~u¬t^iowó´Pѱ}[‹ n*Ê+ÐFZ8 ‘hÔmôPd k3–ŸÆŽøž ™' @³â)YªÔ ã¡Ãœ`ÖÇë 44œþ9Ô´U]Pÿ?]ínï¶=Ià õûZI`!U¤­ëâ£aBYü|R öÂM!REAM((’ â QIs½ì͘ÑTU”W>(­(,zXPpçìÙ3Ÿ|r|玱]‡EE ”õñ•¤ø ôàï“*ñ|=6²¯«S/gû1!²Ro¹™Iœ»Ë¤þ}ak-.¼[­Q3Þ&RÃðéÓ5ûE+ÀK*¸½*5^߉:Ý èú,cªQ6©JKnœ9¼lÞ‘¦\<ÈÙdHÈšˆ…­­ÉÊ=Gæ²0Ižäl24&àâ7ÿÄjËÄ< Àf@åÏÅ]ïw¡Ìeô)X (s¹ñq=EÌ‚Á‰Âh.Ú µ¶ß]{½½^¢ ¸k td!èÚ/BjxœaøB™l"ÕÆi·P…ý€ê°‰)ˆ &2D&Mt!ƒ.'—º¶®FSuAòÅ7oœ=ûóñãÿ:zäëƒNìÛóÕ=ßÜ{r?ñðï^¼hFï¾=¥~É"q'7Q¢D:kèˆvìãY¥¢\Óˆ,[B?¨+üï;´/ Ü îqšjè}ik«bà3:Þ±ññ#°5ªškMŠÂŠ?ŸÜ»eÑð ~O63éof‚Hæ¹b—2Ñtç)A£ƒ=#­¹! a§þù&›uu`2ÑB âÏ¡F! jk¤ÍPc±²H,¦t$>1‡Åà‘2Ö¾µ÷ÀKØ€È.ËÛå#«Il©à¾0ƒ &TDü!Qû|ÂS‘05B +88„› Q4 zknÑPTw L¡ˆ“‡Õ¦ ui Ô(¿wGQH¥ìÎMpòž¿|þÄw?~rüü±o/ÿæÜ±¯n½XýPz¢:Pºá–fМPE(:”)´öÅ÷hãPjjqU÷¥6° wÄž4iZÕ5Eê»×¯|ùù»ËW2%*b€“–Кä=7&x|¨7Ö£r¶˜?~øµ³§ëÔJX˜µó&Šçgù¸ùôX‡#Šž¸¸ŸæY³nÇô©jtßWûß—¬tPC)|È£çe–”=Ô´©Pfyb9ÓBôgáRè7Z©E„® ‘®iy “|µ°ÁÒÐУæVÕÊJÐÉÂÚŒ½öÞ’âÊ+~+«¸[Z]¤¨-©¬.V¨Ë*Õ  raX  †UáÊA³y¨¡ Ü Q5LÒÁ fUÐ?P€3ì†è â? ÓEà¤V*ʯþzíøñ£k³æ÷î1,º W×S^gúœajSY—A=ã ÒuÉzÔç|âaw;Ô¼dòÕÞ\}à7èÁæF„3ZŒà%ƒtÝÄŠ*,‰Œ †/¼•{¸n)w“çÀE"Ü€6üa:“Nz-D Ö„³@`‚†âQKSCS½¦®–®jimÒ4Ô*Ô UXu ô1 (O¨à†À5:‰4+À#á/ÖZþuxüDoð…€—/ÍðÁ!óïI]}ŽB¬Z¢¸~íò‰Ï±øÚžoÎNï5*©Ãì1C¿8t@‰HøìëIãÂ=yôÐUô}ûL…ïágsÚö<¯ö Ëðêí[{¼”=€¡«—SÂýÏ/ý Ô`Óa Uˆû” ~‚Äç×CÞQ{3àÙ‡×r0ÙD ®…²”Ò‡¸Œ9Ø5PPFZšA «L:œÃ¦J䣩ªÕðvÑÚapÇ"Åbž§ƒÇ¯0ÍAw!{;4[‹ŸDýÄ,WÚI:‡Ìx LÝJ¢wS]W¯(oÕTVݽvãǯþ곋§¿U•ãL¼K  xÁöPOêPE ;B]…׌˜r]Gß‘/åhkoôwh…ƒ"¬Ç’1:¦áCv˜)Sf"aæJÆÛ#* ã@#4%”^Ñ!³UØBÄLÀ«@ÐZPÁ a¦ ÔàJd",EwÙ>b{ƒGñ'D:K”,µ­Í˜Ž0oùŸPcºÓ‹ï¦Pè¹¼Ü2Öîðμ˜ë'„:ÌbúÂX×PAŠ‹ {ÓúH {Ü╊2¥R¥®Ö°ÎÒ΃ô¢?®Ë‰9˜èÁÔãO ßùt€÷‘Õ3ʾÍÿÝ#¶ýí_ÊÐ nÂ~TCiÀØæ‡<ƒüà3ó/”phùLta,dhå :¦jxD"ƒÎCìn4É"´ÁçŒO"͉Ô<°YÆ4ЏŸð4(Rv`Hn€ã ªF1`sè1¤Ù0ã3ð‚Üã¾ mž^Åm$éº 5ê öÚZšÓ¡7ªë@‘ߊ•°®#´5´·±I£V?¬Õ`åÒL+ÈÚ€sQyÁ=ú”Ö4as#÷CýN¯Æ÷ ?x_ÿ  O[©kmûßöx{€É2ž™4ñ^7T>stream xíœA–Ó0 †;°èrŽ0gàáf ƒô( ÞëšKxð(cË’ÙJã¸@gP©-ÙŸåßR:Í 8÷D_ß7ñµ]µ‡/°1ˆñdu8±Ã\wšF€*ªÖá‘Ū³0ųÐa’O›5ù`„Leþ%ù§îXæåŸF€Ã¼¦·—5§i„Üú*Éo†w°za *¡;>yiÕ­&³ÇŽ@’›Õå9ÔIFAèìšÝÅ ¡cs¡y„…uJ¨lFðnMÓÁ+—íÀ:VY †é`:X]øØš¦ƒW.Ûuì> b¬ÑáÎÕ*iPÿötÀ¯L%ßP\u>ò¡V‡ñ u]m+ó(ùr|ÜP•F€t¸ü³£ªÓ4œæ èÐVÇ`8ÌÅ:øö²;×ÁÜÂäaõµ Ä5°°r?™ÏÄ`ÐîÆu€sÎ^;vø3»ÈÎUƒbF©ƒÍa \[žÕê/hÏ‹²<=7[F†Ð³Ñ e¶sÓ ‡é`:ðʰ|°|°|?3¸ öyjØýá¿?Œµ‘u˜$ã;ü¦ g¦&?‹[;6üéøŽ¬}ë ßx[³CÂ…ÂJòæŠ %–b?É<Ša)!žzP$$ãšò=Ú•„‚ lŒ!‘X®ýzB1`Þ` ‰Ä¸¦|O8`1¡Ãdæ%#Z¾«„јår&öiœüPˆª¼«„=(±Ð3¯žÐa Rb‹žyK ñÔ{i7š¥S®7î7Ü'endstream endobj 66 0 obj<]/Filter/FlateDecode/Width 528/Height 327/BitsPerComponent 4/Length 3528 >>stream x훽Ž$¹ €Û¹¿BGãÔ‹ó ær/&¹sdà€‰]jà€} 6¶ÁЉ³~¨#©’(©D•XÝUCõnEQõ‰RW×ÌÜïVŒÀÙ Ü.ºâN D;/­ýQ°i祵7G!àãÔ®¯Öþ(4´óÒÚ‡£ðqj×Wkº[‡Ë œ’ƒvRÆáNÅ8OÀ8œÃœ/šî„Ÿ·t¡;d´7÷»qðÉbŒƒ'`ŒCJà¼ù0ã{æ)?/Üx¹›´?ãçÎKS3mŒCØ;¶/<‰rÐ~ݾ ‰3rÀy) ÆÁ\ã¶Ží OÂ8œ˜ÃŒÍSæM*ìüŽ+Øàdâá`ù`ùOÛÛsžÆ…ëh³wÚ¢Ù#òÁ ñiõ‚˜”’4 |JP œqðdŒƒqðŒƒqH X>¤4Ü~Ÿ›ÊOôKfZ–a^ªö{Ý?ÀPÕÒ£—lªµ÷PH8‡*Y Œ0hr˜“\2rlqBs^²©ºÄyiJ›ƒàIDÒ³Çb&õè%›ÌQ¨L‡,ýÕ8‡4#,,,R–) ;,,R–) ;,,R–) ;,,R–) ;,,R–)öùPþ8rè‰jEÒ³­c1“zô’Mæ(T¦C –þÚæÛÞï%—²^Úên¤ñнdSu¹-‡aHiIÏ‘JséÑK6ì=‘¦CIŒAD{'ýœWr¦Õó0Tµôè%›ªC)Ī1(ƒ'cŒCºG,,,R–) ;‘é»K×oJÆõÕËOË8‡tßX>4óÎ'ç¿„¥Ð‚,B’>ô;äï‘BðÒ¼´zãÀ¼äJÅPïÑK6U—ÒRUA‰öŽ.ÔÄ $¸ŒŠ4ˆ¤gŽÅLêÑK6™£P™%Xú+Ú»ÇsHï—RB‹%•£RžœC:ÇT–æ˜êSYš~Ô?9i.=zÉ&Î=Œƒ§±HXÏþ–¦n컥ëRÈ’A^²)†ðÕ58DÇ’3­>:<Üßã´¡‡y|,˜€—\©ê=zɦêRJÙª1(ÑÞ¹äL«÷Þðݱ˜I=zÉ&s*Rˆ¡½¼èC!ÚHδúèð0‡ ’v¾’=;ÍýëôR_ö’HÓ¡$Æ ¢½Ë÷HδzöCUK^²©:”B¬ƒÒ8x2ÆÁ8¤{ÄòÁòAȼwpiÛ'þÜÌ0‡‡ô!¬Õ3]Çb&õè%›ÌQ¨H!†öòZœ“y³äL«g¯ŽÅLêÑK6™£P‘B íåÕ8x"ÆÁ8¤{#ä|f¦j†4Ö꿇²Çb&õè%›ÌQ¨à¼4%p¨ö‘œiõìܱ˜I=zÉ&s*Rˆ¡½¼¶9ÐcÅ[é}Tw#Wôè%›ªËu9T‡°}°H°%}èwˆç“l”¤yiõÑážOr°QÒÎW²Ÿ›„j"IóÒêÙ¥c1“zô’Mæ(T¤C{yE{W*C]r¦ÕòPR©>•Ù£ I! æá9m½Yr¦Õ³wÇb&õè%›ÌQ¨H!†öòŠö®T†ºäL«þ䡤R}*³GA’BÌ-0–Ä“sHï饔®êO¼/ªó•”j@ÜiIƒHzö/ ¥Õ³GAš%ïØ´—µzÒ±˜IZ}Ö¹V‘B¬Ù¢®i/5jõ<¸c1“´ú¬s­"…X³E]Ó^jÔêypÇb&iõYçZE ±f‹º¦½Ô¨ÕóàŽÅLÒê³ÎµŠbÍuM{©Q«çÁ‹™¤Õgk)Äš-êšöR£Vσ;3I«Ï:×*Rˆ5[Ô5í¥F­žOï‹zdÇ]u’¢ä¥i/5jõÒàêo=”S›V,ÚùJö­1ŽÐ¶*Ô#LØb4FÀ#`Œ€0FÀ#`Œ€0F`M·ñ“ù'xšzqŽ~ÊN@yºŽÅ(Õt±±_¸ŒM+ª±Q¦qŒn̕۲>­ 8qw þ;ù׳ЮR’(ƒ®ÝþóX ö¬”õ¬±^q8}ìÿÝ8a³N`»F¹±“ †+·LIŽ †~¡{yr’´“?ÀǦÜåûÞ6¼ð RÞ/aI`"ÅÖ-t/¯>ÒfJ°ŒŒLà ÿ ývs6XÍ/àfïßé*zÁ€ªrsæ|q— ‡B 7¨"ë9XaU #¹‚ ¼h•@S‡Ø®é…–ó D aÂ;½ÈMaPêãœÁ$0Fï9Ï(è‚\b–Bà .¾ZüH3܇.ä‹§ÐÕæ\Ób²­I¸qã§äPÙòŸ’Cåîúsr'ÄçäPIˆñ‘ñ4ã„ø ³®ÌÑUt¦2FÀ#`Œ€0[xï*ÿØ:Œ‡û _¿„òƒå‘¤˜rýÂÿÞ~z åï;`ùò%ŒõÈåÂbD?„ˆÊë þ0¼ Σbàòþþ@Õ•úÁ #D7qÚ‡Q~¼/„½(FäPF8V,\©ö‡‡9¼ýæå€!”b„¯¿¬a•8,ZÐ…a¢<ír¥ÖâÃþæáF‡Go‹‡aÌØå°“¤ó’QŸ„O»\©•8Û¢àðömà°ö¢îµŒ]}¥2oß<‡„þã9d¾~+õºÒÎ-`>†|x¢ã¡ˆp­ŒÍ8|ø|H’î 8dŽ2v¥•¤ã;³C²-‹ö÷ÂÎíJÁëaqS1 ‡¿_Cùæó!Þ±þm8/ÞªMÑl¯®TÌXÈß”áŸþLe’ºH9Ó€åú2pxôñFHçC÷J‡Ã²•êä€I—åÖßGùÐLÜ1 4tÛÉÉF!Eä0±ù6&@î®”†¯ÿpæ”}<,X©>;Ùc”{¬»40¥ú9ü'ž½×+žÅ?Çú_¯íÏ$)ôUõ“È@ÌØ>èƒošàcê?¿†ò¯ƒ8Ȫ®;ÃŒ-WêkºRþ6»Þ´ÝâÉëÇõÏâÿ ?úüx¢GQú•ꉙÃ?¯/P¯païÐEX[)\ºÆJusàmñoâÀÛâ×ë•‘nYw p­F4ÎX9”.ˆ2røð“sxôñ#|WêF_¦*oñ|¨´y" ¼- û;b‚òH8>s˜±R=ù%]xž1æ 'Ýö-€¡½R„¦±R½¶¿WH¶Eȇíg+Ž­Tx§Z©YÒ ÏuÆ°Åø×j@ËVªƒCÛçC{ȇµæ4ÇÏò•ÒrÏužŽCr<;÷ÇïÃJµ2¶ƒÂŽw¨ðŽŽœÈá×a”Ö s–XÓ'ËØy+ÕÉ!~Æ'pÈoÝŸCßJ5ïó¦9 lþ>;<‰ä?ß¿ÿŽÍA4k;ÇÇ_¸Rgà°ÆJMs@Øü çÅi>Á£‡,²ˆ¯]{´qeæ­Ôð½Â‰Û’’.Í•ñ?ÉñFˆ9×!(ä…ûüêÆü¸®³R~ù£×‚ÍÇó_V-fcOCÃÊëá,£GQÀ&|L=‡‰•º®²R%ÿ¸3áð;…ѳn[ÙìÂÁÂù0l Îâ°ð ZDhŸ• †ëûˆ5œžÃ;Pàk<HµhAvbŠ'ù&+%r`"(-œÊ’îb„ùJ=2c—L¯»¯ÈáiVª{*fØEà:!ýendstream endobj 67 0 obj<]/Filter/FlateDecode/Width 633/Height 333/BitsPerComponent 2/Length 5914 >>stream xíÍnãÈ€iO FåÞ9 x`n‘–±ñe.£Éˆï£L`àÙWp¼AÇs‰6Æ^„ô¹ÎE]‚€¥ªÿªÉ¢Hy{dw³ªûëªêf“’íÃá)§õS†;žñ†øçÙzÏÖb!ºÏ±÷l½Œ¶™úѪ‡Å o?vÞ€ŽyªÃðXÖãÐR?iÞÉÐÕ3áÙïÄYŽs×Ö±*3=!_S/¼!3ðve„W[>–s½Þ:²0ÿÎêz^¸áwã5¡k%ªåëèO^fÆØÍôôµm¶Æ“¦©m`¬ Úͤ¹m´/<ÝD![Ϲ;ƒ&Ï6üºðš2ÐsÛ‚i.ÃqnAsÓzÞw(Ì9ä°úÔ{ž•ÝÌõâÚ—5^] è~,熮EÀ™âëÀ˯^·s#×Jû)¾v¼tÉ“ªòð¶óÚec×:>EŒÈ×Tx¯ud2÷ÞÉ ÌQ˺ˆ7O‡·†E¾þUQ(¿ì2Iª#Þ–ÖåÜ4ðt_{xÒ3µ«ƒÓ6Ÿ$•­mê~o›Œ2Ѓµž¹¡h³ùŒ4ü"Ø~ŒÔº8¼ÌÍÁljó07Æp.9/T_oõZ†SÙ2/0ñ®Šà>Û^š±x-CHµtÍS6%ïoK·.³í×õ{m½¶!dÕå\g.œl€WÀ–NíéÖí!>WxǸ¶L<»°ÈÈ䀧øTZfÇ®.95Zn]-ú}WêÖ•4?ÂLᕉ«B8·qr#çKÿfð­×°`R!Á‹=íܶû·i{º‚tWšbý~ÇhZ†ãÚY…x«Eˆ÷ƒ.^ÛêZeáèÕ¹«: ?}—¨§o$ÝjUšKs´ž¼éÍÏnÎLm]CvVàñÚÕ%¹·=œËYæ•Æû“éj^¼ÙüÂCìõ¼´ïŒ|pfâI hÒ…ßkºÕ_íuÄ+±t]Gxe}cñlÆêa¦sË@“.¼1xK+ xjâ^{4;/ëkKe3A»=¬ÇZ*ƒç‚ÏâMoZO/®ÏfõðêúÝuœE=» —w&.,föË mx³²žþª¼¸.®ÁhåY=ƒÉ1—gs¬ÌYO†ç`ð¼†T޶ÎáÙ¹a­w˜ Hr3ÃIxóú¢ŽÇYæÖ·«¯ž–ïb6ƒ¼Yý3ˆÂزÜù¯cqhǃ…¦Ã[à-x¿J™ÇÓµ8´9–bÀ›/Àr¾õpKkÅþÂÂ[^iº²±wëĜœ3bo&—#œùÎUx]‹C7N³9ZO­{€Wþî"k=Ön…×µ8¼Yé´´V°Ö»–ÖC¼k9sÁÇ7hÎyù³á ‹ÂëZ蛚±+ÚŒ‡BeqâŽáÜpqñÞƒõÔƒ¤ÐÖŒ“W3ƒÌ]ÃS·ÙÐpGõ’|Tb…<`ÆËÉbî Øp&~r-ùõï‚Øˆ7†s}8/Ædµ|¿WBÖsiÀ*{%93ÆÇ :W/o¿?¯Ç- ¢ ôÖš OÎ ®õŽÄSoæ#ëñF*gÆøxAçês ù†ÁÕ»\OþW•S˜Õ&ö ùÁÁ1xÒ· .-ôйTúž\4mLzsØòҷ컆z5AƒÎaqÀ»†Ä³÷²àM5u}n¸h@ oN ð`q@<9¯ìF«ä4ƒŸŒõIGáAI<©¬­o¦I(6öÌ•áÍh×údçЉÓæ{/ÉÌÛ“âáÂ/ñÔØpŸüŽ·Yî9où{ßz؉ÄÓûÎg„Ò´hv™ØÖkÛQùÞºñ ؉Ä;X3Ç ³vb°ñ¸w5O. oWêþyx8®žÉtŽ3)yxÒ ï°ÖöcáM{¢¡ø÷ YQºZjÏ|ðÀÁó¾ÚÊw.seqxjq0xú#9žù˜sÃáMdÛïð2«»®ÊéñŸÉ“ä|"éïÙõ]Óâ郻ðäG˜¶[¾syÁ眧‹wÀÙ; 3ÓM½o  c¼]‘´–VX<³²:<ø>|¥ïÕYªdjü/[ò3zq0­Qg{Ó0äáð‹q"$öû¦|¢@rG…uoj}<¬Ûš9êA‰¢ëá\h»Û|ϸ֭{w{È™ÏØÛHö?w›Oã¹®RëeæXßý1Eß×ÊY7è%øn£M=Hò±»Ÿs¸xÁw§JÅBoΙ <:F`Ú O»`M€©ª›YGãQæ÷0¼5е̑_ÆFˆ­·Ï¨“¾å-ËØb˜~ȘOÕÛ&ñó½õt\¶gìÅÜâ0FœI<"úEÓÀQg½ÿˆŒ8o’Æh¼Äú#…žéž6ß×<¼õ$Q§C{2œ ˜>“æˆÆKl=”˜¤w¶ ^Ô)¿˜Œü<;À@špîxØ'w6zfðqIÂ|oï°¸íáˆx±ùO°ðŒû̓Ñ×Õ8Åhó ¼¦Ûzry”òñj¦¾~?“׊}±£îmÀÿ›°3ʹ~:Àþäװ͉½hñ‚™!W¼(,A¿ÐãöÁöÚŒ n°„^° «ž)<=5N‹&[÷öõ0o:„£×yŠ6l‹’€:3Ñ¨ËæÅüx|JxûCAµDÕ Æ;˜w`<9Žn¼]ñšB9U]qŽÒ ëí»m#˜,ííÌÓ¶¶6E8wØ?2ž5à‰ŽýÀ~g55xᆀ¸ç^øÑ/îšpnŒ–΄ÏJð¢57Á“K7«[V—}„/v'7éÓêh²¬7 ïl^<}òý³¬Ìu.‚íÂ'¡t; B‚½%oßt[oýãøÀv¯C:ÒznÏ»Õ\a„ed¹â]xr3Í —-g÷°Om`œ±Ó¦ªM t”ÌOá·Šñ¢EÚ$2L2sÃG‘¶^á÷`jÑ×Ö!ù߯N¿“` "4zŠil„Ó’ëŒá\ˆ½Ø(NßË^ôNáá4‹æ×Fœe¹{‚ÇkëváÎ\Àk“püiìõvW¡P¶´å„Â^}¶ {A@.RoËw®m|pFößNI¼hñÜ3«Ò$)²¬å+…nIæ`ùxݽŽ-AŽ‚´7˜G%¼8ò¨uÄØx¸Fv&Ò,©ˆ}Š–• ^Ã}Û ?Ì…ŠÔ¹¢Ü©‘ÚàøÀk÷ÒxÜNF¶^“,àOO€MD<Þax\+sä/¹×?-¼2ú)ÅSwùØÊ¾ŽË³¤˜ ‹ã%íÑx‚·tœÃs¸`D¯F¡†Æc쇅-ôÁ¡j¶”8ƒ’ä9÷‰ãá Ò"œ &‰Þ²Q6»ñ®&q«4žýÐ/Ê#:ñDÔ<95 O‘ÀH#âaS"é’¶/iîø À‹ÞÊc[4Þ£}è熃xÉ=-ƒÇ#:W ©<`Æ$Âz¼uÊ40ÖYàã5Âô”ð¶ñC8{h½T0•.ç\\*¶\ëqŸ$ÇÅ{¸Jl@;w—ìªÅ‘+Ðz{HîÒ942NÜœœ©­Òx^‚RÄúdy<çÒ÷4jÝC;?z¼¦HGK[ïÑé Öáó IÒoo’J&ªP‘—’aEÔU€÷ ¢9¦EóšÕqÐA¶ÀÅ[‹´‰ ^Ãj3mîèD#î4ÞáÕc;ñˆO<Æ wh¬Ë‚Þgð&¼ž#㋸˜=ñNáØc?‡#Þá² ó9œojAŽsï!˜Ái/'­‘Î}ºx’ŒX-2Öcšj4熓^N–É‹qšËÉ<^ŒK•snÈáa¬v§\«fÎ2¾P®¡ Þ>yê7fó¹V­f˜xÄ;òI ÛK~|Ðáø‘é’²86ùP|¢ EÀ#?DÎà1ïö£9·'ÞžÚãµ€qO짨G¡>j#A"^üý$Ùtƹé§[$ÈXÎ<êAˆž¹8Þ~o,ë\Bò”UÙaf¬×çû™#p÷Ãé‡×œ^³Íúʌع•‚´ô:¹ò{8uðèAdñXÖ ð>‘måðÖé7rý‘œ+× @LS/cì¨ñ诚“x[òBD7V­G…—¤¼ô“_Š…´žZÒ]¨»ÕÖ¡„ ß¶çðȽkÒ|Š5 bW(yž:ÉáÑÒ*Y°x%H ¢R `B\Éám B˜Qu,ùœ–¹çBôð¾| 0˜šÉN¼ºš|“‡6F“‡O¢y½»z¡žF» Íd Ië^¯Gµý~ûiò°±Ýî·ßBnÿ°ÝŠ­ÄgáíÕ„RÃuÇ^ÈUh/öÑàŸú/sBhëºîèˆeÌ‘ÃÛ¯ÉÓ¶u÷ðíj‹·~¡Aáƒ/õ³Ûz‚¬Ÿõ¨§: *;î·º…ÄÂûDOEÒz°$t·)»VßzbOx2mY”³?Èæð¡®N´Ùv»Þ¾†Ï‚×ëíäJM.m|®†5r’Åc½&б ËÕäÅz-v¯×ðÑaaY?pÄ+ȵÌ㥿õ¤sØ‘€›§.‰EÀË=öç¬×ô‹>Ù­ƒr9’'ª„8? ^ˆáJ.$³ èí^ƹм˜Dcì.ºOu\Ž‹G>„gî¹ø‚…55º‘9hëÌTÓ±‡ >”ëL A+»ŒPµ› /·ûÍâñvó¦ãp}ËÍL 05ÿ§ÿ9­2€—ÙOåñä­¦µU¸8Žs/³mÁã?*t ¢ãºôk°9:öpñ¶T®¥£s™°“íåðšçc«QœÛ¯È›0Ì1 ^fÉk± ˆµ™gŒ [ä(¼ô'OˆŽ~<ë±VQðÚ¡§F[´FVÕïÑÖ<ØéðÚZ¶&m ýÓâY„!Ñ¢LÇÞ£:÷tx£8÷¼ŒQc7Œ×ºJйýMŒ7F¹žþê7ExÇÌ~GZ¯K§cæöÁû§ú+¢ËÂü9QóWEé‘xZ'ÒX­–Z§¯õräÜJwrwöMÞF'VYet<§øYÒ!^cºø`2æümÏêQ{¦uvöºÊÜëò©B¼U£cÊ+º+tno©:Œ¼wiÛT™[]6±PrñtW®*Ø!EÝBðQxÉh  !Þ&éÂTƒ·œÑX<ÊA\<²+¹ÙÈIë„ÎMŒ] ôó£Rê”%ÞRý>Zù×vµ:‰‡ ËF ,'@wø¼¶Ý™!…έÜõE‡GY€vî_Õ¯¡­/][$ÐY¼oÿûYþëÐ"•–·8? Ïü2_û§²%'JÝÝ_þþEþ󦦈D±Xiñ•¼1-/½!NZÏþYqï¯Üºê)Fã-û/ò_cú7C¢{§8ïŽÀ3¾­kÕvHâ5Œsï5Ý—Ä:4Þ-6 É›êD¤õþ¤C¯®ßÈ&ð@è"œÃÓ¾ýò÷M«N¥¯š³ô±ª²½à0 Ï›¹Ö3ãó7À ‰´Þúh2F¼ž›ÄЂØû3®*òß¿l‡J‡Ä»5B·•ÉQ"­‡k²Jx8Òj^ºÏnêRCÒ4·Jkµªlî¤x¿Q«Yxš û㹩Ña=/önժǿAŸ*µ8·22ƒðÞØV(Gk­s¿Q«c<)ešKŸ y·ð +d3dì9ë½}•]ÕÈF]wëž®€jJ§Râú…þxìeÙsî}vÝ#g®Ã»D•ˆÑÖ;ê¦fÖ½ÿT¦»6ëÝZ!—ãâ­Êd]!»’ÞÝèŽÔ~êàM\R§Râ·Z V›cãýAã½±ªdWÁfþ[½*oÚu*uÙEœ®€j6žÞŽúÛeB7¸k¬–—råÛ¹îtdìÙ]¨½»õÀ[Ý¡ùæ—0$“H¼¤‘¸û-ÌŽæÒáLéTúº9ßê2œqrj€hqVw?Ö Ã[-^|éÖ©4Îu^š2{à­â—T„nè\hŸÖ! ± +µg–¹~xZÅžH<0žg=+k2”Ne.Êí²Þ3«:B<ç\Óˆ=ºÀicE’ŒᡲR x}viK!ćáyw ¿“WýÑÎ52Þùÿ ý¶ñÆe s¸ÉHŠ„ø#8ÇàR•b™á¤L.ÁSwwÏiѵ K»Î¨±§–Ku4hx&†¦†¸1‹O§ÂÆèDt1Ìz¸1;JeC¿g¢¿¡±§:áuev,í:‰sÛÅqÈ6¥Ö[^ÞU‹ûÕÇ—ÕÝË–—‹ZDX•(ƒÖÓ:÷——:U¯XHñîïoo«_¯Î?ÜÞ.^¾¼¿?¿—£mÅÓ:‹»ûNÀd=èjµúõêè~¾\B©Oß5;uÆÀ[H¼Û¥Ä[nƹ>è fª“ÆÆÂ¹ŒŸÅ²=~Hç®À¹`=¢«(î°¸o¡õ@‡Æ u*%þqyñS}Õ?$Æâ}”ÖS+"ìÅ•4ê ^—ŽÆ[ôP¼lŸo!ga±:??¿ûꫯ^^ž·àiçÅbAè¤ÎEc¯JcÁ r Þ*M0àLìaS¼š¤ k^\P10žz¨Q7¶¸A8åu*{àÜJ97ë †õ>ȾI¼ìvÔ×Iœ+c¡ñƒÁÐ? QB¸qð²±ZϽ،ǘÇ멳‰[ƲŒª‹ax{xaì‘x¹¯‘rÔú,XúöÐSg^~³øb¡Ž5¬#u69ióÕƒ ‹Ð¹ùi(-S@çvê„ÎÍÇå Ïýåsa€ˆs?|,PÆŽðkÿ‹9./,[ÕO§ò‡íç©>b<Ûçx™Ð¹‡úH^þŽêqžŒ=ªÙ–ºÜÖ&”Î0<ªÅQë~Òx,çF±×˺?iëõ²Ä1Âì÷ìÜclþh:ÜËÂüñf.+öXcÈ=‚õ2=³ªŸñXfÊ=uëýîâ)Mendstream endobj 68 0 obj<]/Filter/FlateDecode/Width 192/Height 194/BitsPerComponent 1/Length 890 >>stream xí—1OÛ@†‹2d«ÿAÝ”P[î'ð#:0v`€Hi®ˆ!KÕÌ]êŸÐ‘‘H V%ZF¦rTHxªœÊª>[õùëûÝÙ%f@ R)œäØytzï»Ü{ï9Ìmî¿@Ê\̹b¶ßm2—1s2`ªáè[&àóI Îà?¯p¯ówð.×П¡¥˜?ãqÙA|qEÌ_q“±]¯4ž"æ7¸Oò8—ú»¸\CáR¿ðŽƒøÏÛ—ó4üvïãG×Z(#.ƒKû*›¯ÿO1¯€µI-ë€Æ}7MÌkÈþÑáЖ½„.V€÷ÃÀæ=¢d©áÐÁZ;.Sº>Ékýn@ì¹!¬¯Écß'¦Ê:^`\©§›P^‰Nug޳יèŸLrì §Ó‹iŠc9Þh´îê„Î_×÷¶9Ïwnüžù¹M¼¶±¾{–{mï(ñÏKBpä›—m©áçN(|‹x <…Ÿ×l®SJ:‘ðŒP¹§Mûâ˜Òá ^€ðöPŽ'Äh”GTi%ý‡Ä1øx ~@y©…+B_à <W”‰,‚‚xÐà \S"² G®:ÞoxÙ¢jðÓËࣧ¡èÏÍ?·íÏ›êßK?»¼‚Ÿ‘WS~.7½Ÿ{›Ó~Fn;?#·½ŸkßJž‹oÁSïÛ˜øþÙ?‡þòY‰Ä‡à’“‡nܪç¸÷^ëì Ÿ9’þž»†>r;s¼Ì¨Â>~Š}ž$Ò¿Þy ýeËÈÿ$ EÇï߉ùŽVݸ~öÇÓ]ÿR ¼¯ÈûYkY÷cB6›y&xß8¦‹uÇ}~Â?ý0¶¹øç¹ã‰O¼¯Ì‰óUúÂñWÄû5ßwœŸ ¯ÀW /~[ÿñ¾±åxJ¼zÁ·QÏ"å$¼ÎU9/$Wq^dg¡Ô‰þÄïψ~˯Qžþp©_8êŸâUèy?¤‘rýïú:άÿžås‹ðGÌÐÖw×–O$ŸÅ'õ9ÁÏÊoáç_òÁÝ~ÿLrú å|ÿõ ùì9ò°}á-Ë༤DÇ¿Ï8Žü|ŸÀÏ2nÃŽ¶Z»’Ï5/‹&Ÿë÷zý®-rÔC‘èoþˆBý¦c‹~Îd\ˆàßÀvü~^ÕÂgúá¦çû¿Ö–ŸeÎmN¿Ào©äWÊendstream endobj 69 0 obj<]/Filter/FlateDecode/Width 519/Height 226/BitsPerComponent 1/Length 2778 >>stream xíš1ÛÈÇ×q¡T§ú³Lm¸° Ÿøä#\q™²ÙìiÃ…p€á p͆•o±±ôÁ@ÔÈ«ÒÕ‰ÚÛÀª.ÔFðRóòo†”HJE¹bDŠœß¼y3ÿy#5+ºYu¢kÀµ8†®ãàäÒâ^Ù¹˜nÔ.ópçÊ\ÁbѤgšp„ûX¸`CPÌXÃõoþ⎰\¢·¨ù§KÀ|Ï‘ó›¿p?ɶ—4\‚b-Ð-wä‹Ü[×_à@n‡-ì×Ü¢ð ‡0kMñ—€.0vŠ€©‹këÁGöŒ;D!£œ\)ßÄRÇÄ8]ñÁ§\zòž}ó }/~”Kt&Š8‘ÃŒ Û·k€ok±»x¶V¼kmÜ/×V”“!\gY/! dlË?ñ¹W,÷îÝëØÊK€'5WÞãK¾ƒ”;Å7riÁR|û™~äjäNù¶„mª Óšö W#wÊ71`9›ò ™’«‘;å[ è¦4ï{­/‰3ž\Nˆ¾ÛŠUm¢WuŸØX÷†>ä[³9wÊw³q?c>b@L§uÒa ¦äµ0Š $ŠÈóßéØñ’'½à¢#aÁå@ÿÜ={x¢q-‚’ýKòœ}¾ìdž u¡ƒîÐ={ÜÖxzD÷)è¾£3ç¾YÀÈza@ªGzfšWKF®Ž†^Æ‚Iz“5€ R-à<ДÌÛØ;YxáÒÄn[ÀmM½¬¬7(s·Ôk×;±€# 8Òº“LÐ*Žt €˜rM4æa•+ 8s €°ˆÛ4uÖ)Jbé¶Î<<7šîµJk,Ó››Æ(‰<×ÓöJt‡0}À¶¨t8ÇM$Mh`A)ÍÚ>­{j¼0>hX 0ø‹ââX¨hÁù @Õµ`ôµÂ5qP­ ÚŠ>@}.¥q€^ˆáÄ€‚y?Ó„y"Ï[ÕY]pBAXt¢1`‹¤&´×*õ©¡i vÄY°ùÕ³§_‹u»°Î«àáŒ^X³P¬ø”>X¯ç?Ê〖€Ó|M{¾ð³›4án=€J‡y@ Þº^ë¹Æ”Ñ Ì‰c¨ó ßjóèáÂÆ)ÏΉkĵ¯¡Î¢Qu¸žêðE?ð-`¡ºòt5=Þ×tºª µw"¨ó$˜XÀ,@®¥*¦³Ù‘FŠa9¨‘ëù3½ÏB ¦€i',È›…YV„Ù"Puδ#ŠLðôVÖÕX5IQH'3õS„5äù¨ó0ÓºÀ»åŒ4u®œô5]€`UWPgy$ß#½@Zĵ³\Ùú¤àNOãé2î@Mê˜[dŠÐÜ!èë`¯¢†¢3·ƒFàãt€¯Cn‘)÷nbà¤N\&K@ÏzY€æ©T|°ðæW74=…Í xêz~OOÝZZ ÓÈFLñ¶ÌÊ¢GM ž‹7¬/ØSö–Uƒø*.5 Õxz V· ½ÀÏ@)·ÀQƒ?à®@l‘ é[•9Q»êü‘ ªbR£?Àr4&&Th×üZÌ[*ÖǼ¤^¨óöÜY]Í#^†Êr+1>óYÞ Pç™ä3ÔÉX°‚Øà kyr›0g£CWž*‡Uz)ÍÓßð”«Ý&êÜGŠ©o5 D\1[PÝÔWœÖó¨³îæö|[7ºaŸ cJÔ™3Vçç;¨3õÿåêD?·êüùNê 9NÔyߪ3’ïÔ€D\qÝGîœ×2uþ7ÔùRÓ“ê·Ìáƒo!T'ÑŠÕfu¯MÞišÀNÄ~œH½ìg³:ê%rg³>ж £}¨s¶ eêüÚ¥ŽŒŽtL9Í7¨3!wÖO°ÄyíFØ–\€0Ì6©3©®vÍ* ;ÑÂ*m„Ü9î/¸E9w¶ëÄe$N?[qâÿ_Kŵ҄r‚™]‚›q”-•íDÅY€=+Ÿ&‰:Ç TXu^“|W Ùq]¨âÄþê ôï M¨€ë⹨sΉ»¨3°:Ïy¹•-U,@ïOÀ`oú8!Z°ö”Ç’ïØóÌï¬4f `öôÏǼãº&w®fr Ééš©3|ÐHyÂk¦Î¼J£»+Ž“ÃŠqu–ubQEXZHVXF²:;þrRE y¸ÊJ1w¦þ¿æ"Qdõ»uÆ^¦sG‡'½ À·€bîL]èÂc“7²›ºWtv º0t2€Í¹³ËìƒÆ&…–OäAX¢á—@™:c_yvªé' øItîgeê )šÜÕô­dez'B¨^eeêü#ÔùPÓ|À|£õý`cî,K’/lâ‰`•Ä3Ôñ¼•i¶Õ7¨3ï8œ¹ÊÄzAq (gmVgÄA@qø˜ÈØ „maJ#uAñMkA׺ØÙ^µ W¥|BqJ•Ó— Úoˆ}5ÖâÄœ°pã«4aá«ÁWˆëe®H<Ïÿ&€ººà«Ñßô_â²Q& >v)»¨ód£Lÿš-UzÕùíþLTw€!âಟSËœb¦^pîü$‚(Ú)äÎÖžòш¼óÁ!‡mM§¾&ñüõ@z¶ÅÉ[pšV±ÕºQrgÜ­P.| €Ãà{$Ë­gØÙv£Å¼±–îøVvZ÷ì¤ZTçà»=4[¢W³NÐâ³ò ©Ác÷(ߊê¼âÜÙ.f=ü -Ýá (ªsÚ”7÷™,&=Þ”=û/Ô9(ªs ˆ‘xÎnjüU(À[ò ÁJÖ†&dÔ9­+!“7 bý€W+y#EuN(Á%ÿîÊÞº°·þ’wžðÄEœXÌÍEÌÊClØ%wz/¡Î°?,.áDˆõì`,rlTç ‡8pïÀkp"â@™Ó0.› y+V&UöF®{È»ÿ`B$ºÄšO§Ÿ9K¶D7,¯t4þí’&T™ 'ã鋱Zt"¾÷¶­P;?’^èêÎ  {~h½ž~Tiäddêu-äDÿþÏÄO`M)í¨yã³ Qgþù¹ø§°½•…á×§üs¯Ä‚Ô'ÅΜ;7 x¹Éŧ߈ÃÇø' ƪJ¿¯|ÀäÎ&¹Ø$®%8ÛA6wæ¸Ù±0`ÁóÒ"ÇÚ¸= ‘q³+‚ø«“¤¥Äãܵ0€€t„v.ðÀXöìJ€oðæÎE©:ï\Ýô‚äöÜ„a]@ªÎu©:×$¹smÍMî\`sçTE$Ö(5ê¥U®v,¤©qpíÄk'rØèÿ çF`endstream endobj 70 0 obj<]/Filter/FlateDecode/Width 497/Height 226/BitsPerComponent 1/Length 1228 >>stream xí™±nÛ0†SdÈèGÐ3t,P@’!©“ tÈèµ@ øAZ€ :tôtPƒõÐA <Ȇd^J#Ã<’›¤ŽE€•¨ò ÇÏ¿HSÞÇÀD#>Ø£ëF× ð@XØäDuB´Áרg¨5ô…MˆÖ3¢{‰ä®)j .às¢Ÿ_¢¦¸Ä)'ZWÆE]¸Àk@?ð—®ð5 +ÌE(:l¯ûž? ÆW×@¼?w¸@áw“ýð{íºBø^ëž·ë®<¼Ywû=Œ¯ÓváNíuj k ‹y+ªþÿø¯k 5¾@GõÂ¥Bí5ƒæÞë?âÆ“ÇຜDHÚ l®6º‰ ye JH¬§i¢Bš0ÍPiB¬çi¢Bš(ºf(®¬%àÊøº5^5÷ÂõÜï5Žf. 5ºÔøM‹«f.Ój|‡SküR»îmk¼j¯qµîÊójÝá ÕÜuW0ž"öºcƱR1®;l|̰¤ÞÐ1¿›W 0Ç™þû»NN üHt—Uð½im•õ¼À/pA©¤ßi)`šaø¢€òøø-pÝ Â×À—Àa|üpÝ Åa±Ø'à?1÷®òë£Ói¸Ì*ú\7ƒGŸ’¨€¯¨¢/Àu3OIÜM$Œ®è¸ná+¸.k×}ëÞ5ƒpµî;¹œàÙŠLŠú´ý[ÚŒÞ5Ñ»ý]7…4„,fŠ7M`äzÁÀ[êÃþÊàïoâpÄW¼Æ«sŽÆB\GüV©:$¨ö‚cm¹‚«OLy|uÆŒM4fX6lî´ºÏçy®–£f¨¶rĸhÔ¯&Ü®SB,qiX7!Ö‰V»†uãZIñ20 OZ!¶«aKìf9ÒDf‹lsëx ["eæHNÜeüøb8®Ìó༆mæŽ 8g5lÞâ×±Q—´8k<ÖÅ‘edN­ñë£kœ_w·ñç£ÎÇìu˜û Æ=6nîôXøK‚âŒyâG×_"=øv†í;*}‚ÑY×Eν1}¸ëþîÕ°ìÜ›ÑëF´Ú5¬÷iX7¾ñiX7N¡h…’ÚѰg%}@’ʹ,37¢Õ¦aåEI¯Ï8<1¢Õ¦aåeIo€' .3#ZmV.KzüœÁñEÝ)B¯ñë‚›»_”ðl^søêÌ­a§%Žuó ‡û^™´„×sh™¬ ÒÝœ»ÌJ|p¬»{¯‹ÄëÓR¾>ºc§ÕnpÎý…ãýÄ¡5›º(Ϩ<äÙ.Üë~àøv†Õ±Ò»x¦×Óz{¼¸Ê°ùðsXdX·†µºÛ<¬‘"“ˆsX¤HÏË@3ýfÚâ §aí”yÚºŽ×°¦£ý&Òø8\}â†ëx k·¹{ªÖ8«a»~Ì52ê|–õáññ¾ïÇí:O†}ûÝ6$á™ µçŠÇ?ÊÜîàFÏÍÁësXšy4¬=TÍÓļ9‡¥ssð:äVbî. k¬´ßÔÀ#Îa7À#Îa!»ƒW›†µ›Ü{š»5l¯§õ6ï^¹sXë¨æaï?¸7ŽFãŸk£ŽÌ°&üvoô§ØÝÿ{2âa~²ô]gqJØ£gvÝ_ÿì̺endstream endobj 71 0 obj<]/Filter/FlateDecode/Width 483/Height 196/BitsPerComponent 1/Length 1615 >>stream xíšÏoÜDÇÛ®D@Bí_€,N=¢ªˆ*uþþ•8ÒJ=Јh_P{ ME‚øÏ@(m“¨¨¹„øÈ¥7M‰Oì,u¼¬íÇ÷ÙNvÏ:ãE¢¤Ý'íÎŒ3Ÿ}oÞüx~Ó2Ï.íÙQæ9\Ó{s‡u‡¥€Ìâ]||æÈa“L™ç}w‰ù J—9Da)p]÷ˆy¥S³÷P~6 œ>dN}æžb“L1[4ÇøzÃìÄGéâS– 8@oÀÑJ·LâIìa¼Kèâ3keYªálž}ŒY•I<©€Wh´ÂP5ÈØÐÓðè‚ÏAŠ1nIbòoçý'¾ía•¯l K…)o¢6)fo«9LgÅa)N°ë$eO÷˜ºr •dÊè±×ò¨yûRXÌv ³]n†µÌø~ßgª'¬^¥Üa«D½æ~¯ð:Ñš=|P‚•=\˜=ÒܤÚðnaö.Í· o·y¹QKóì' ÆüŸÀi¡tbÃÔÐŒÜM9‚¥ÇR^xò­¿$Ûèôs;Ía÷X¯”Ên·Þi`މ=|g¯óÅ7)úË¡Q³¼–Ab¼ß$YM^+ÒóiçÕ0VÅm…FiÌmÊzÄ!û ÷²`jwÒ!»é((^UOj¼"ÕÇðó™fzšÁÒÊ¥ZsJºŠæýΟÃÀ•V.Õcl_|¡«¤«ÀÏ:_­ç9îÿøì-.Íáöå‹ÃñV9Ïq#yþ‡Î]?åÐ7x[TAbÞ~žhÉD³ITüàtÓ˜ç0|PÀ¸qµ×|FÒÁl˜Í‰M£ìOÏuÇkÀ®<~w)÷l2±1Nû—² ¿»¬òáöʬf¯_Ýx°>WÞ>âJ•¹‹ñ@äZ¡GÇàÊ{Oܰü‚ v£k›¯'Óo\Ï9x_=û,m‘S4gðbÖ_®Þx8nvõ˜ƒ½wo}~}i_û~®ÞÁjçèòã½÷Än;îÍ,LŠªXaAz±ÛŽ¿úpÒé~ôøÄÛ“&K«ÒìdïèææÉ<ÿî)9¸°¼„\Òñ¶%J~;:=ƒô­îfK S” ²k¼xÙÃ+{$Jª<¦j:-È%ÙûArɺ0/ÙâC‰’öp›T½î)/FR6¬Í.n™3ÍŽ7Øß2w½ã®÷ðšý]ï$ìVöpqE}šqEíÞC”\²3)q/æ9 •çJ”¼j ÷XE„æ`…‘wN¢dË®^Û§¬°9 Q~¤¹*¯š¶ä™uØëË%K~5?0{ÛÜ·ôt—\Rý`î°jÿ”þzVö7Öyãendstream endobj 72 0 obj<]/Filter/FlateDecode/Width 187/Height 187/BitsPerComponent 2/Length 716 >>stream xíXAnÛ@ 7>ú= þzèÄgèè»}pÂ`WffErEJ[Ð!ÈnÇ#N¸Üñj5&óšñw ™¹Ð—ôúêÊü~³ñž–ø ¦¥þÉ: ï^ÁB­æB™25Ý©¤‰4ƒO¢Foèquô†8Â'r щ®G§H'BÐÓ›HoHg— ¥TË^sÈ^3‡¥ðõ²còP ‡ÚsÔ²‡¥B¿Tâ‹$éAHâ»('µÆÚ±pŒùç¢×,jÙ\è"CøT¿2Õžwݸgއ« eÏô}ÇÕHïùà­5¨ô®ï¹ÊÙA°3¸ =OöÙ“bÒR{_{€ˆhÔ”Ù·;Ooìw…ÎGîð“o¾E˜é§H_€J×-½ÅIðêA:y̦bVŒB_«(S”ùÌW••÷êp\ Joqp|³ìuô™51Çýh‰íÀnb|æR¯âN‘Nq2D#}–ýá‹ÉQËÓëã5zŽ\O'>ÿtKÍÑEúä¿§v?:ðœN¦ žÐye‹þ‹PQ „SHw$K‘ù=endstream endobj 73 0 obj<>endobj 74 0 obj<>endobj 75 0 obj<>endobj 76 0 obj<>endobj 77 0 obj<>endobj 78 0 obj<>endobj 79 0 obj<>endobj 80 0 obj<>endobj 81 0 obj<>endobj 82 0 obj<>endobj 83 0 obj<>endobj 84 0 obj<>endobj 85 0 obj<>endobj 86 0 obj<>endobj 87 0 obj<>endobj 88 0 obj<>endobj 89 0 obj<>endobj 90 0 obj<>endobj 91 0 obj<>endobj 92 0 obj<>endobj 93 0 obj<>endobj 94 0 obj<>endobj 95 0 obj<>endobj 96 0 obj<>endobj 97 0 obj<>endobj 98 0 obj<>endobj 99 0 obj<>endobj 100 0 obj<>endobj 101 0 obj<>endobj 102 0 obj[74 0 R 76 0 R 77 0 R 78 0 R 79 0 R 80 0 R 81 0 R 82 0 R 83 0 R 84 0 R 85 0 R 86 0 R 87 0 R 88 0 R 89 0 R 90 0 R 91 0 R 92 0 R 93 0 R 94 0 R 95 0 R 96 0 R 97 0 R 98 0 R 99 0 R 100 0 R 101 0 R]endobj 103 0 obj<>endobj 104 0 obj<>endobj 105 0 obj<>endobj 106 0 obj<>endobj 107 0 obj<>endobj 108 0 obj<>endobj 109 0 obj<>endobj 110 0 obj<>endobj 111 0 obj<>endobj 112 0 obj<>endobj 113 0 obj<>endobj 114 0 obj<>endobj 115 0 obj<>endobj 116 0 obj<>endobj 117 0 obj<>endobj 118 0 obj<>endobj 119 0 obj<>endobj 120 0 obj<>endobj 121 0 obj<>endobj 122 0 obj<>endobj 123 0 obj<>endobj 124 0 obj<>endobj 125 0 obj<>endobj 126 0 obj<>endobj 127 0 obj<>endobj 128 0 obj<>endobj 129 0 obj[103 0 R 104 0 R 105 0 R 106 0 R 107 0 R 108 0 R 109 0 R 110 0 R 111 0 R 112 0 R 113 0 R 114 0 R 115 0 R 116 0 R 117 0 R 118 0 R 119 0 R 120 0 R 121 0 R 122 0 R 123 0 R 124 0 R 125 0 R 126 0 R 127 0 R 128 0 R]endobj 130 0 obj<>endobj 131 0 obj<>endobj 132 0 obj<>endobj 133 0 obj<>endobj 134 0 obj<>endobj 135 0 obj<>endobj 136 0 obj[130 0 R 131 0 R 132 0 R 133 0 R 134 0 R 135 0 R]endobj 137 0 obj<>endobj 138 0 obj<>endobj 139 0 obj<>endobj 140 0 obj<>endobj 141 0 obj[138 0 R 140 0 R]endobj 142 0 obj<>endobj 143 0 obj<>endobj 144 0 obj<>endobj 145 0 obj[143 0 R 144 0 R]endobj 146 0 obj<>endobj 147 0 obj<>endobj 148 0 obj<>endobj 149 0 obj<>endobj 150 0 obj[147 0 R 149 0 R]endobj 151 0 obj<>endobj 152 0 obj<>endobj 153 0 obj[152 0 R]endobj 154 0 obj<>endobj 155 0 obj[154 0 R]endobj 156 0 obj<>endobj 157 0 obj<>endobj 158 0 obj<>endobj 159 0 obj<>endobj 160 0 obj<>endobj 161 0 obj<>endobj 162 0 obj<>endobj 163 0 obj<>endobj 164 0 obj<>endobj 165 0 obj<>endobj 166 0 obj<>endobj 167 0 obj<>endobj 168 0 obj<>endobj 169 0 obj<>endobj 170 0 obj[157 0 R 159 0 R 161 0 R 163 0 R 165 0 R 167 0 R 169 0 R]endobj 171 0 obj<>endobj 172 0 obj<>endobj 173 0 obj<>endobj 174 0 obj<>endobj 175 0 obj<>endobj 176 0 obj<>endobj 177 0 obj<>endobj 178 0 obj<>endobj 179 0 obj<>endobj 180 0 obj<>endobj 181 0 obj<>endobj 182 0 obj<>endobj 183 0 obj[172 0 R 174 0 R 176 0 R 178 0 R 180 0 R 182 0 R]endobj 184 0 obj<>endobj 185 0 obj<>endobj 186 0 obj<>endobj 187 0 obj<>endobj 188 0 obj<>endobj 189 0 obj<>endobj 190 0 obj<>endobj 191 0 obj<>endobj 192 0 obj<>endobj 193 0 obj<>endobj 194 0 obj<>endobj 195 0 obj<>endobj 196 0 obj<>endobj 197 0 obj<>endobj 198 0 obj<>endobj 199 0 obj<>endobj 200 0 obj<>endobj 201 0 obj<>endobj 202 0 obj<>endobj 203 0 obj<>endobj 204 0 obj<>endobj 205 0 obj<>endobj 206 0 obj[185 0 R 187 0 R 189 0 R 191 0 R 193 0 R 195 0 R 197 0 R 199 0 R 201 0 R 203 0 R 205 0 R]endobj 207 0 obj<>endobj 208 0 obj<>endobj 209 0 obj<>endobj 210 0 obj<>endobj 211 0 obj<>endobj 212 0 obj<>endobj 213 0 obj<>endobj 214 0 obj<>endobj 215 0 obj[208 0 R 210 0 R 212 0 R 214 0 R]endobj 216 0 obj<>endobj 217 0 obj<>endobj 218 0 obj<>endobj 219 0 obj<>endobj 220 0 obj[217 0 R 219 0 R]endobj 221 0 obj<>endobj 222 0 obj<>endobj 223 0 obj<>endobj 224 0 obj<>endobj 225 0 obj<>endobj 226 0 obj<>endobj 227 0 obj<>endobj 228 0 obj<>endobj 229 0 obj[222 0 R 224 0 R 226 0 R 228 0 R]endobj 230 0 obj<>endobj 231 0 obj<>endobj 232 0 obj<>endobj 233 0 obj<>endobj 234 0 obj<>endobj 235 0 obj<>endobj 236 0 obj<>endobj 237 0 obj<>endobj 238 0 obj<>endobj 239 0 obj<>endobj 240 0 obj<>endobj 241 0 obj<>endobj 242 0 obj<>endobj 243 0 obj<>endobj 244 0 obj<>endobj 245 0 obj<>endobj 246 0 obj<>endobj 247 0 obj<>endobj 248 0 obj<>endobj 249 0 obj<>endobj 250 0 obj[231 0 R 233 0 R 235 0 R 237 0 R 239 0 R 241 0 R 243 0 R 245 0 R 247 0 R 249 0 R]endobj 251 0 obj<>endobj 252 0 obj<>endobj 253 0 obj<>endobj 254 0 obj<>endobj 255 0 obj<>endobj 256 0 obj<>endobj 257 0 obj<>endobj 258 0 obj<>endobj 259 0 obj<>endobj 260 0 obj<>endobj 261 0 obj<>endobj 262 0 obj<>endobj 263 0 obj[252 0 R 254 0 R 256 0 R 258 0 R 260 0 R 262 0 R]endobj 264 0 obj<>endobj 265 0 obj<>endobj 266 0 obj<>endobj 267 0 obj<>endobj 268 0 obj<>endobj 269 0 obj<>endobj 270 0 obj[265 0 R 267 0 R 269 0 R]endobj 271 0 obj<>endobj 272 0 obj<>endobj 273 0 obj<>endobj 274 0 obj<>endobj 275 0 obj<>endobj 276 0 obj<>endobj 277 0 obj<>endobj 278 0 obj<>endobj 279 0 obj[272 0 R 274 0 R 276 0 R 278 0 R]endobj 280 0 obj<>endobj 281 0 obj<>endobj 282 0 obj<>endobj 283 0 obj<>endobj 284 0 obj<>endobj 285 0 obj<>endobj 286 0 obj<>endobj 287 0 obj<>endobj 288 0 obj[281 0 R 283 0 R 285 0 R 287 0 R]endobj 289 0 obj<>endobj 290 0 obj<>endobj 291 0 obj<>endobj 292 0 obj<>endobj 293 0 obj<>endobj 294 0 obj<>endobj 295 0 obj<>endobj 296 0 obj<>endobj 297 0 obj<>endobj 298 0 obj<>endobj 299 0 obj<>endobj 300 0 obj<>endobj 301 0 obj<>endobj 302 0 obj<>endobj 303 0 obj<>endobj 304 0 obj<>endobj 305 0 obj[290 0 R 292 0 R 294 0 R 296 0 R 298 0 R 300 0 R 302 0 R 304 0 R]endobj 306 0 obj<>endobj 307 0 obj<>endobj 308 0 obj<>endobj 309 0 obj<>endobj 310 0 obj<>endobj 311 0 obj<>endobj 312 0 obj<>endobj 313 0 obj<>endobj 314 0 obj[307 0 R 309 0 R 311 0 R 313 0 R]endobj 315 0 obj<>endobj 316 0 obj<>endobj 317 0 obj<>endobj 318 0 obj<>endobj 319 0 obj<>endobj 320 0 obj<>endobj 321 0 obj<>endobj 322 0 obj<>endobj 323 0 obj[316 0 R 318 0 R 320 0 R 322 0 R]endobj 324 0 obj<>endobj 325 0 obj<>endobj 326 0 obj<>endobj 327 0 obj<>endobj 328 0 obj<>endobj 329 0 obj<>endobj 330 0 obj[325 0 R 327 0 R 329 0 R]endobj 331 0 obj<>endobj 332 0 obj<>endobj 333 0 obj<>endobj 334 0 obj<>endobj 335 0 obj<>endobj 336 0 obj<>endobj 337 0 obj[332 0 R 334 0 R 336 0 R]endobj 338 0 obj<>endobj 339 0 obj<>endobj 340 0 obj<>endobj 341 0 obj<>endobj 342 0 obj<>endobj 343 0 obj<>endobj 344 0 obj[339 0 R 341 0 R 343 0 R]endobj 345 0 obj<>endobj 346 0 obj<>endobj 347 0 obj<>endobj 348 0 obj<>endobj 349 0 obj<>endobj 350 0 obj<>endobj 351 0 obj<>endobj 352 0 obj<>endobj 353 0 obj<>endobj 354 0 obj<>endobj 355 0 obj<>endobj 356 0 obj<>endobj 357 0 obj<>endobj 358 0 obj<>endobj 359 0 obj<>endobj 360 0 obj<>endobj 361 0 obj<>endobj 362 0 obj<>endobj 363 0 obj[346 0 R 348 0 R 350 0 R 352 0 R 354 0 R 356 0 R 358 0 R 360 0 R 362 0 R]endobj 364 0 obj<>endobj 365 0 obj<>endobj 366 0 obj<>endobj 367 0 obj<>endobj 368 0 obj<>endobj 369 0 obj<>endobj 370 0 obj<>endobj 371 0 obj<>endobj 372 0 obj<>endobj 373 0 obj<>endobj 374 0 obj<>endobj 375 0 obj<>endobj 376 0 obj<>endobj 377 0 obj<>endobj 378 0 obj[365 0 R 367 0 R 369 0 R 371 0 R 373 0 R 375 0 R 377 0 R]endobj 379 0 obj<>endobj 380 0 obj<>endobj 381 0 obj<>endobj 382 0 obj<>endobj 383 0 obj<>endobj 384 0 obj<>endobj 385 0 obj[380 0 R 382 0 R 384 0 R]endobj 386 0 obj<>endobj 387 0 obj<>endobj 388 0 obj<>endobj 389 0 obj<>endobj 390 0 obj<>endobj 391 0 obj<>endobj 392 0 obj<>endobj 393 0 obj<>endobj 394 0 obj<>endobj 395 0 obj<>endobj 396 0 obj<>endobj 397 0 obj<>endobj 398 0 obj<>endobj 399 0 obj<>endobj 400 0 obj<>endobj 401 0 obj<>endobj 402 0 obj<>endobj 403 0 obj<>endobj 404 0 obj[387 0 R 389 0 R 391 0 R 393 0 R 395 0 R 397 0 R 399 0 R 401 0 R 403 0 R]endobj 405 0 obj<>endobj 406 0 obj<>endobj 407 0 obj<>endobj 408 0 obj<>endobj 409 0 obj<>endobj 410 0 obj<>endobj 411 0 obj<>endobj 412 0 obj<>endobj 413 0 obj<>endobj 414 0 obj<>endobj 415 0 obj<>endobj 416 0 obj<>endobj 417 0 obj[406 0 R 408 0 R 410 0 R 412 0 R 414 0 R 416 0 R]endobj 418 0 obj<>endobj 419 0 obj<>endobj 420 0 obj[419 0 R]endobj 421 0 obj<>endobj 422 0 obj<>endobj 423 0 obj<>endobj 424 0 obj<>endobj 425 0 obj[422 0 R 424 0 R]endobj 426 0 obj<>endobj 427 0 obj<>endobj 428 0 obj<>endobj 429 0 obj<>endobj 430 0 obj<>endobj 431 0 obj<>endobj 432 0 obj<>endobj 433 0 obj<>endobj 434 0 obj<>endobj 435 0 obj<>endobj 436 0 obj[427 0 R 429 0 R 431 0 R 433 0 R 435 0 R]endobj 437 0 obj<>endobj 438 0 obj<>endobj 439 0 obj<>endobj 440 0 obj<>endobj 441 0 obj<>endobj 442 0 obj<>endobj 443 0 obj<>endobj 444 0 obj<>endobj 445 0 obj<>endobj 446 0 obj<>endobj 447 0 obj<>endobj 448 0 obj<>endobj 449 0 obj<>endobj 450 0 obj<>endobj 451 0 obj<>endobj 452 0 obj<>endobj 453 0 obj<>endobj 454 0 obj<>endobj 455 0 obj<>endobj 456 0 obj<>endobj 457 0 obj<>endobj 458 0 obj<>endobj 459 0 obj<>endobj 460 0 obj<>endobj 461 0 obj[438 0 R 440 0 R 442 0 R 444 0 R 446 0 R 448 0 R 450 0 R 452 0 R 454 0 R 456 0 R 458 0 R 460 0 R]endobj 462 0 obj<>endobj 463 0 obj<>endobj 464 0 obj<>endobj 465 0 obj<>endobj 466 0 obj<>endobj 467 0 obj<>endobj 468 0 obj<>endobj 469 0 obj<>endobj 470 0 obj[463 0 R 465 0 R 467 0 R 469 0 R]endobj 471 0 obj<>endobj 472 0 obj<>endobj 473 0 obj<>endobj 474 0 obj<>endobj 475 0 obj<>endobj 476 0 obj<>endobj 477 0 obj[472 0 R 474 0 R 476 0 R]endobj 478 0 obj<>endobj 479 0 obj<>endobj 480 0 obj<>endobj 481 0 obj<>endobj 482 0 obj<>endobj 483 0 obj<>endobj 484 0 obj<>endobj 485 0 obj<>endobj 486 0 obj<>endobj 487 0 obj<>endobj 488 0 obj<>endobj 489 0 obj<>endobj 490 0 obj<>endobj 491 0 obj<>endobj 492 0 obj<>endobj 493 0 obj<>endobj 494 0 obj<>endobj 495 0 obj<>endobj 496 0 obj<>endobj 497 0 obj<>endobj 498 0 obj<>endobj 499 0 obj<>endobj 500 0 obj<>endobj 501 0 obj<>endobj 502 0 obj[479 0 R 481 0 R 483 0 R 485 0 R 487 0 R 489 0 R 491 0 R 493 0 R 495 0 R 497 0 R 499 0 R 501 0 R]endobj 503 0 obj<>endobj 504 0 obj<>endobj 505 0 obj<>endobj 506 0 obj<>endobj 507 0 obj<>endobj 508 0 obj<>endobj 509 0 obj[504 0 R 506 0 R 508 0 R]endobj 510 0 obj<>endobj 511 0 obj<>endobj 512 0 obj<>endobj 513 0 obj<>endobj 514 0 obj<>endobj 515 0 obj<>endobj 516 0 obj[511 0 R 513 0 R 515 0 R]endobj 517 0 obj<>endobj 518 0 obj<>endobj 519 0 obj<>endobj 520 0 obj<>endobj 521 0 obj<>endobj 522 0 obj<>endobj 523 0 obj<>endobj 524 0 obj<>endobj 525 0 obj<>endobj 526 0 obj<>endobj 527 0 obj<>endobj 528 0 obj<>endobj 529 0 obj<>endobj 530 0 obj<>endobj 531 0 obj<>endobj 532 0 obj<>endobj 533 0 obj<>endobj 534 0 obj<>endobj 535 0 obj<>endobj 536 0 obj<>endobj 537 0 obj<>endobj 538 0 obj<>endobj 539 0 obj<>endobj 540 0 obj<>endobj 541 0 obj<>endobj 542 0 obj<>endobj 543 0 obj<>endobj 544 0 obj<>endobj 545 0 obj[518 0 R 520 0 R 522 0 R 524 0 R 526 0 R 528 0 R 530 0 R 532 0 R 534 0 R 536 0 R 538 0 R 540 0 R 542 0 R 544 0 R]endobj 546 0 obj<>endobj 547 0 obj<>endobj 548 0 obj<>endobj 549 0 obj<>endobj 550 0 obj<>endobj 551 0 obj<>endobj 552 0 obj<>endobj 553 0 obj<>endobj 554 0 obj[547 0 R 549 0 R 551 0 R 553 0 R]endobj 555 0 obj<>endobj 556 0 obj<>endobj 557 0 obj<>endobj 558 0 obj<>endobj 559 0 obj<>endobj 560 0 obj<>endobj 561 0 obj[556 0 R 558 0 R 560 0 R]endobj 562 0 obj<>endobj 563 0 obj<>endobj 564 0 obj<>endobj 565 0 obj<>endobj 566 0 obj<>endobj 567 0 obj<>endobj 568 0 obj<>endobj 569 0 obj<>endobj 570 0 obj<>endobj 571 0 obj<>endobj 572 0 obj<>endobj 573 0 obj<>endobj 574 0 obj<>endobj 575 0 obj<>endobj 576 0 obj<>endobj 577 0 obj<>endobj 578 0 obj<>endobj 579 0 obj<>endobj 580 0 obj<>endobj 581 0 obj<>endobj 582 0 obj[563 0 R 565 0 R 567 0 R 569 0 R 571 0 R 573 0 R 575 0 R 577 0 R 579 0 R 581 0 R]endobj 583 0 obj<>endobj 584 0 obj<>endobj 585 0 obj<>endobj 586 0 obj<>endobj 587 0 obj<>endobj 588 0 obj<>endobj 589 0 obj<>endobj 590 0 obj<>endobj 591 0 obj<>endobj 592 0 obj<>endobj 593 0 obj<>endobj 594 0 obj<>endobj 595 0 obj<>endobj 596 0 obj<>endobj 597 0 obj<>endobj 598 0 obj<>endobj 599 0 obj<>endobj 600 0 obj<>endobj 601 0 obj<>endobj 602 0 obj<>endobj 603 0 obj<>endobj 604 0 obj<>endobj 605 0 obj[584 0 R 586 0 R 588 0 R 590 0 R 592 0 R 594 0 R 596 0 R 598 0 R 600 0 R 602 0 R 604 0 R]endobj 606 0 obj<>endobj 607 0 obj<>endobj 608 0 obj<>endobj 609 0 obj<>endobj 610 0 obj<>endobj 611 0 obj<>endobj 612 0 obj<>endobj 613 0 obj<>endobj 614 0 obj<>endobj 615 0 obj<>endobj 616 0 obj<>endobj 617 0 obj<>endobj 618 0 obj<>endobj 619 0 obj<>endobj 620 0 obj<>endobj 621 0 obj<>endobj 622 0 obj<>endobj 623 0 obj<>endobj 624 0 obj<>endobj 625 0 obj<>endobj 626 0 obj<>endobj 627 0 obj<>endobj 628 0 obj<>endobj 629 0 obj<>endobj 630 0 obj<>endobj 631 0 obj<>endobj 632 0 obj[607 0 R 609 0 R 611 0 R 613 0 R 615 0 R 617 0 R 619 0 R 621 0 R 623 0 R 625 0 R 627 0 R 629 0 R 631 0 R]endobj 633 0 obj<>endobj 634 0 obj<>endobj 635 0 obj<>endobj 636 0 obj<>endobj 637 0 obj<>endobj 638 0 obj<>endobj 639 0 obj<>endobj 640 0 obj<>endobj 641 0 obj<>endobj 642 0 obj<>endobj 643 0 obj<>endobj 644 0 obj<>endobj 645 0 obj[634 0 R 636 0 R 638 0 R 640 0 R 642 0 R 644 0 R]endobj 646 0 obj<>endobj 647 0 obj<>endobj 648 0 obj<>endobj 649 0 obj<>endobj 650 0 obj<>endobj 651 0 obj<>endobj 652 0 obj<>endobj 653 0 obj<>endobj 654 0 obj<>endobj 655 0 obj<>endobj 656 0 obj<>endobj 657 0 obj<>endobj 658 0 obj[647 0 R 649 0 R 651 0 R 653 0 R 655 0 R 657 0 R]endobj 659 0 obj<>endobj 660 0 obj<>endobj 661 0 obj<>endobj 662 0 obj<>endobj 663 0 obj<>endobj 664 0 obj<>endobj 665 0 obj<>endobj 666 0 obj<>endobj 667 0 obj<>endobj 668 0 obj<>endobj 669 0 obj<>endobj 670 0 obj<>endobj 671 0 obj[660 0 R 662 0 R 664 0 R 666 0 R 668 0 R 670 0 R]endobj 672 0 obj<>endobj 673 0 obj<>endobj 674 0 obj<>endobj 675 0 obj<>endobj 676 0 obj<>endobj 677 0 obj<>endobj 678 0 obj<>endobj 679 0 obj<>endobj 680 0 obj<>endobj 681 0 obj<>endobj 682 0 obj<>endobj 683 0 obj<>endobj 684 0 obj<>endobj 685 0 obj<>endobj 686 0 obj<>endobj 687 0 obj<>endobj 688 0 obj<>endobj 689 0 obj<>endobj 690 0 obj<>endobj 691 0 obj<>endobj 692 0 obj<>endobj 693 0 obj<>endobj 694 0 obj<>endobj 695 0 obj<>endobj 696 0 obj<>endobj 697 0 obj<>endobj 698 0 obj<>endobj 699 0 obj<>endobj 700 0 obj<>endobj 701 0 obj<>endobj 702 0 obj[673 0 R 675 0 R 677 0 R 679 0 R 681 0 R 683 0 R 685 0 R 687 0 R 689 0 R 691 0 R 693 0 R 695 0 R 697 0 R 699 0 R 701 0 R]endobj 703 0 obj<>endobj 704 0 obj<>endobj 705 0 obj<>endobj 706 0 obj<>endobj 707 0 obj<>endobj 708 0 obj<>endobj 709 0 obj<>endobj 710 0 obj<>endobj 711 0 obj<>endobj 712 0 obj<>endobj 713 0 obj<>endobj 714 0 obj<>endobj 715 0 obj<>endobj 716 0 obj<>endobj 717 0 obj<>endobj 718 0 obj<>endobj 719 0 obj<>endobj 720 0 obj<>endobj 721 0 obj<>endobj 722 0 obj<>endobj 723 0 obj<>endobj 724 0 obj<>endobj 725 0 obj<>endobj 726 0 obj<>endobj 727 0 obj<>endobj 728 0 obj<>endobj 729 0 obj<>endobj 730 0 obj<>endobj 731 0 obj<>endobj 732 0 obj<>endobj 733 0 obj[704 0 R 706 0 R 708 0 R 710 0 R 712 0 R 714 0 R 716 0 R 718 0 R 720 0 R 722 0 R 724 0 R 726 0 R 728 0 R 730 0 R 732 0 R]endobj 734 0 obj<>endobj 735 0 obj<>endobj 736 0 obj<>endobj 737 0 obj<>endobj 738 0 obj<>endobj 739 0 obj<>endobj 740 0 obj<>endobj 741 0 obj<>endobj 742 0 obj<>endobj 743 0 obj<>endobj 744 0 obj<>endobj 745 0 obj<>endobj 746 0 obj<>endobj 747 0 obj<>endobj 748 0 obj<>endobj 749 0 obj<>endobj 750 0 obj<>endobj 751 0 obj<>endobj 752 0 obj<>endobj 753 0 obj<>endobj 754 0 obj<>endobj 755 0 obj<>endobj 756 0 obj<>endobj 757 0 obj<>endobj 758 0 obj[735 0 R 737 0 R 739 0 R 741 0 R 743 0 R 745 0 R 747 0 R 749 0 R 751 0 R 753 0 R 755 0 R 757 0 R]endobj 759 0 obj<>endobj 760 0 obj<>endobj 761 0 obj<>endobj 762 0 obj<>endobj 763 0 obj<>endobj 764 0 obj<>endobj 765 0 obj<>endobj 766 0 obj<>endobj 767 0 obj<>endobj 768 0 obj<>endobj 769 0 obj[760 0 R 762 0 R 764 0 R 766 0 R 768 0 R]endobj 770 0 obj<>endobj 771 0 obj<>endobj 772 0 obj<>endobj 773 0 obj<>endobj 774 0 obj<>endobj 775 0 obj<>endobj 776 0 obj<>endobj 777 0 obj<>endobj 778 0 obj<>endobj 779 0 obj<>endobj 780 0 obj<>endobj 781 0 obj<>endobj 782 0 obj<>endobj 783 0 obj<>endobj 784 0 obj<>endobj 785 0 obj<>endobj 786 0 obj<>endobj 787 0 obj<>endobj 788 0 obj<>endobj 789 0 obj<>endobj 790 0 obj<>endobj 791 0 obj<>endobj 792 0 obj<>endobj 793 0 obj<>endobj 794 0 obj[771 0 R 773 0 R 775 0 R 777 0 R 779 0 R 781 0 R 783 0 R 785 0 R 787 0 R 789 0 R 791 0 R 793 0 R]endobj 795 0 obj<>endobj 796 0 obj<>endobj 797 0 obj<>endobj 798 0 obj<>endobj 799 0 obj<>endobj 800 0 obj<>endobj 801 0 obj<>endobj 802 0 obj<>endobj 803 0 obj<>endobj 804 0 obj<>endobj 805 0 obj<>endobj 806 0 obj<>endobj 807 0 obj<>endobj 808 0 obj<>endobj 809 0 obj[796 0 R 798 0 R 800 0 R 802 0 R 804 0 R 806 0 R 808 0 R]endobj 810 0 obj<>endobj 811 0 obj<>endobj 812 0 obj<>endobj 813 0 obj<>endobj 814 0 obj<>endobj 815 0 obj<>endobj 816 0 obj<>endobj 817 0 obj<>endobj 818 0 obj<>endobj 819 0 obj<>endobj 820 0 obj<>endobj 821 0 obj<>endobj 822 0 obj<>endobj 823 0 obj<>endobj 824 0 obj<>endobj 825 0 obj<>endobj 826 0 obj<>endobj 827 0 obj<>endobj 828 0 obj<>endobj 829 0 obj<>endobj 830 0 obj<>endobj 831 0 obj<>endobj 832 0 obj[811 0 R 813 0 R 815 0 R 817 0 R 819 0 R 821 0 R 823 0 R 825 0 R 827 0 R 829 0 R 831 0 R]endobj 833 0 obj<>endobj 834 0 obj<>endobj 835 0 obj<>endobj 836 0 obj<>endobj 837 0 obj<>endobj 838 0 obj<>endobj 839 0 obj[834 0 R 836 0 R 838 0 R]endobj 840 0 obj<>endobj 841 0 obj<>endobj 842 0 obj<>endobj 843 0 obj<>endobj 844 0 obj<>endobj 845 0 obj<>endobj 846 0 obj<>endobj 847 0 obj<>endobj 848 0 obj<>endobj 849 0 obj<>endobj 850 0 obj<>endobj 851 0 obj<>endobj 852 0 obj<>endobj 853 0 obj<>endobj 854 0 obj<>endobj 855 0 obj<>endobj 856 0 obj[841 0 R 843 0 R 845 0 R 847 0 R 849 0 R 851 0 R 853 0 R 855 0 R]endobj 857 0 obj<>endobj 858 0 obj<>endobj 859 0 obj<>endobj 860 0 obj<>endobj 861 0 obj<>endobj 862 0 obj<>endobj 863 0 obj<>endobj 864 0 obj<>endobj 865 0 obj<>endobj 866 0 obj<>endobj 867 0 obj<>endobj 868 0 obj<>endobj 869 0 obj<>endobj 870 0 obj<>endobj 871 0 obj<>endobj 872 0 obj<>endobj 873 0 obj<>endobj 874 0 obj<>endobj 875 0 obj<>endobj 876 0 obj<>endobj 877 0 obj<>endobj 878 0 obj<>endobj 879 0 obj<>endobj 880 0 obj<>endobj 881 0 obj[858 0 R 860 0 R 862 0 R 864 0 R 866 0 R 868 0 R 870 0 R 872 0 R 874 0 R 876 0 R 878 0 R 880 0 R]endobj 882 0 obj<>endobj 883 0 obj<>endobj 884 0 obj<>endobj 885 0 obj<>endobj 886 0 obj<>endobj 887 0 obj<>endobj 888 0 obj<>endobj 889 0 obj<>endobj 890 0 obj<>endobj 891 0 obj<>endobj 892 0 obj<>endobj 893 0 obj<>endobj 894 0 obj<>endobj 895 0 obj<>endobj 896 0 obj<>endobj 897 0 obj<>endobj 898 0 obj<>endobj 899 0 obj<>endobj 900 0 obj<>endobj 901 0 obj<>endobj 902 0 obj[883 0 R 885 0 R 887 0 R 889 0 R 891 0 R 893 0 R 895 0 R 897 0 R 899 0 R 901 0 R]endobj 903 0 obj<>endobj 904 0 obj<>endobj 905 0 obj<>endobj 906 0 obj<>endobj 907 0 obj<>endobj 908 0 obj<>endobj 909 0 obj<>endobj 910 0 obj<>endobj 911 0 obj<>endobj 912 0 obj<>endobj 913 0 obj<>endobj 914 0 obj<>endobj 915 0 obj<>endobj 916 0 obj<>endobj 917 0 obj<>endobj 918 0 obj<>endobj 919 0 obj<>endobj 920 0 obj<>endobj 921 0 obj[904 0 R 906 0 R 908 0 R 910 0 R 912 0 R 914 0 R 916 0 R 918 0 R 920 0 R]endobj 922 0 obj<>endobj 923 0 obj<>endobj 924 0 obj<>endobj 925 0 obj<>endobj 926 0 obj<>endobj 927 0 obj<>endobj 928 0 obj<>endobj 929 0 obj<>endobj 930 0 obj<>endobj 931 0 obj<>endobj 932 0 obj<>endobj 933 0 obj<>endobj 934 0 obj[923 0 R 925 0 R 927 0 R 929 0 R 931 0 R 933 0 R]endobj 935 0 obj<>endobj 936 0 obj<>endobj 937 0 obj<>endobj 938 0 obj<>endobj 939 0 obj<>endobj 940 0 obj<>endobj 941 0 obj<>endobj 942 0 obj<>endobj 943 0 obj<>endobj 944 0 obj<>endobj 945 0 obj<>endobj 946 0 obj<>endobj 947 0 obj[936 0 R 938 0 R 940 0 R 942 0 R 944 0 R 946 0 R]endobj 948 0 obj<>endobj 949 0 obj<>endobj 950 0 obj<>endobj 951 0 obj<>endobj 952 0 obj<>endobj 953 0 obj<>endobj 954 0 obj<>endobj 955 0 obj<>endobj 956 0 obj[949 0 R 951 0 R 953 0 R 955 0 R]endobj 957 0 obj<>endobj 958 0 obj<>endobj 959 0 obj<>endobj 960 0 obj<>endobj 961 0 obj<>endobj 962 0 obj<>endobj 963 0 obj<>endobj 964 0 obj<>endobj 965 0 obj<>endobj 966 0 obj<>endobj 967 0 obj<>endobj 968 0 obj<>endobj 969 0 obj<>endobj 970 0 obj<>endobj 971 0 obj<>endobj 972 0 obj<>endobj 973 0 obj<>endobj 974 0 obj<>endobj 975 0 obj<>endobj 976 0 obj<>endobj 977 0 obj[958 0 R 960 0 R 962 0 R 964 0 R 966 0 R 968 0 R 970 0 R 972 0 R 974 0 R 976 0 R]endobj 978 0 obj<>endobj 979 0 obj<>endobj 980 0 obj<>endobj 981 0 obj<>endobj 982 0 obj<>endobj 983 0 obj<>endobj 984 0 obj<>endobj 985 0 obj<>endobj 986 0 obj<>endobj 987 0 obj<>endobj 988 0 obj<>endobj 989 0 obj<>endobj 990 0 obj<>endobj 991 0 obj<>endobj 992 0 obj<>endobj 993 0 obj<>endobj 994 0 obj<>endobj 995 0 obj<>endobj 996 0 obj[979 0 R 981 0 R 983 0 R 985 0 R 987 0 R 989 0 R 991 0 R 993 0 R 995 0 R]endobj 997 0 obj<>endobj 998 0 obj<>endobj 999 0 obj<>endobj 1000 0 obj<>endobj 1001 0 obj<>endobj 1002 0 obj<>endobj 1003 0 obj<>endobj 1004 0 obj<>endobj 1005 0 obj<>endobj 1006 0 obj<>endobj 1007 0 obj<>endobj 1008 0 obj<>endobj 1009 0 obj<>endobj 1010 0 obj<>endobj 1011 0 obj<>endobj 1012 0 obj<>endobj 1013 0 obj<>endobj 1014 0 obj<>endobj 1015 0 obj<>endobj 1016 0 obj<>endobj 1017 0 obj[998 0 R 1000 0 R 1002 0 R 1004 0 R 1006 0 R 1008 0 R 1010 0 R 1012 0 R 1014 0 R 1016 0 R]endobj 1018 0 obj<>endobj 1019 0 obj<>endobj 1020 0 obj<>endobj 1021 0 obj<>endobj 1022 0 obj<>endobj 1023 0 obj<>endobj 1024 0 obj<>endobj 1025 0 obj<>endobj 1026 0 obj<>endobj 1027 0 obj<>endobj 1028 0 obj<>endobj 1029 0 obj<>endobj 1030 0 obj<>endobj 1031 0 obj<>endobj 1032 0 obj<>endobj 1033 0 obj<>endobj 1034 0 obj<>endobj 1035 0 obj<>endobj 1036 0 obj[1019 0 R 1021 0 R 1023 0 R 1025 0 R 1027 0 R 1029 0 R 1031 0 R 1033 0 R 1035 0 R]endobj 1037 0 obj<>endobj 1038 0 obj<>endobj 1039 0 obj<>endobj 1040 0 obj<>endobj 1041 0 obj<>endobj 1042 0 obj<>endobj 1043 0 obj<>endobj 1044 0 obj<>endobj 1045 0 obj<>endobj 1046 0 obj<>endobj 1047 0 obj<>endobj 1048 0 obj<>endobj 1049 0 obj[1038 0 R 1040 0 R 1042 0 R 1044 0 R 1046 0 R 1048 0 R]endobj 1050 0 obj<>endobj 1051 0 obj<>endobj 1052 0 obj[1051 0 R]endobj 1053 0 obj<>endobj 1054 0 obj<>endobj 1055 0 obj<>endobj 1056 0 obj<>endobj 1057 0 obj<>endobj 1058 0 obj<>endobj 1059 0 obj<>endobj 1060 0 obj<>endobj 1061 0 obj[1054 0 R 1056 0 R 1058 0 R 1060 0 R]endobj 1062 0 obj<>endobj 1063 0 obj<>endobj 1064 0 obj<>endobj 1065 0 obj<>endobj 1066 0 obj<>endobj 1067 0 obj<>endobj 1068 0 obj<>endobj 1069 0 obj<>endobj 1070 0 obj<>endobj 1071 0 obj<>endobj 1072 0 obj<>endobj 1073 0 obj<>endobj 1074 0 obj<>endobj 1075 0 obj<>endobj 1076 0 obj<>endobj 1077 0 obj<>endobj 1078 0 obj[1063 0 R 1065 0 R 1067 0 R 1069 0 R 1071 0 R 1073 0 R 1075 0 R 1077 0 R]endobj 1079 0 obj<>endobj 1080 0 obj<>endobj 1081 0 obj<>endobj 1082 0 obj<>endobj 1083 0 obj<>endobj 1084 0 obj<>endobj 1085 0 obj<>endobj 1086 0 obj<>endobj 1087 0 obj<>endobj 1088 0 obj<>endobj 1089 0 obj<>endobj 1090 0 obj<>endobj 1091 0 obj[1080 0 R 1082 0 R 1084 0 R 1086 0 R 1088 0 R 1090 0 R]endobj 1092 0 obj<>endobj 1093 0 obj<>endobj 1094 0 obj<>endobj 1095 0 obj<>endobj 1096 0 obj<>endobj 1097 0 obj<>endobj 1098 0 obj<>endobj 1099 0 obj<>endobj 1100 0 obj<>endobj 1101 0 obj<>endobj 1102 0 obj<>endobj 1103 0 obj<>endobj 1104 0 obj<>endobj 1105 0 obj<>endobj 1106 0 obj<>endobj 1107 0 obj<>endobj 1108 0 obj<>endobj 1109 0 obj<>endobj 1110 0 obj<>endobj 1111 0 obj<>endobj 1112 0 obj[1093 0 R 1095 0 R 1097 0 R 1099 0 R 1101 0 R 1103 0 R 1105 0 R 1107 0 R 1109 0 R 1111 0 R]endobj 1113 0 obj<>endobj 1114 0 obj<>endobj 1115 0 obj<>endobj 1116 0 obj<>endobj 1117 0 obj<>endobj 1118 0 obj<>endobj 1119 0 obj<>endobj 1120 0 obj<>endobj 1121 0 obj<>endobj 1122 0 obj<>endobj 1123 0 obj<>endobj 1124 0 obj<>endobj 1125 0 obj<>endobj 1126 0 obj<>endobj 1127 0 obj<>endobj 1128 0 obj<>endobj 1129 0 obj<>endobj 1130 0 obj<>endobj 1131 0 obj[1114 0 R 1116 0 R 1118 0 R 1120 0 R 1122 0 R 1124 0 R 1126 0 R 1128 0 R 1130 0 R]endobj 1132 0 obj<>endobj 1133 0 obj<>endobj 1134 0 obj<>endobj 1135 0 obj<>endobj 1136 0 obj<>endobj 1137 0 obj<>endobj 1138 0 obj<>endobj 1139 0 obj<>endobj 1140 0 obj<>endobj 1141 0 obj<>endobj 1142 0 obj<>endobj 1143 0 obj<>endobj 1144 0 obj<>endobj 1145 0 obj<>endobj 1146 0 obj<>endobj 1147 0 obj<>endobj 1148 0 obj<>endobj 1149 0 obj<>endobj 1150 0 obj<>endobj 1151 0 obj<>endobj 1152 0 obj<>endobj 1153 0 obj<>endobj 1154 0 obj<>endobj 1155 0 obj<>endobj 1156 0 obj<>endobj 1157 0 obj<>endobj 1158 0 obj<>endobj 1159 0 obj<>endobj 1160 0 obj<>endobj 1161 0 obj<>endobj 1162 0 obj<>endobj 1163 0 obj<>endobj 1164 0 obj<>endobj 1165 0 obj<>endobj 1166 0 obj<>endobj 1167 0 obj<>endobj 1168 0 obj<>endobj 1169 0 obj<>endobj 1170 0 obj<>endobj 1171 0 obj<>endobj 1172 0 obj<>endobj 1173 0 obj<>endobj 1174 0 obj<>endobj 1175 0 obj<>endobj 1176 0 obj<>endobj 1177 0 obj<>endobj 1178 0 obj<>endobj 1179 0 obj<>endobj 1180 0 obj<>endobj 1181 0 obj<>endobj 1182 0 obj<>endobj 1183 0 obj<>endobj 1184 0 obj<>endobj 1185 0 obj<>endobj 1186 0 obj<>endobj 1187 0 obj<>endobj 1188 0 obj<>endobj 1189 0 obj<>endobj 1190 0 obj<>endobj 1191 0 obj<>endobj 1192 0 obj<>endobj 1193 0 obj<>endobj 1194 0 obj<>endobj 1195 0 obj<>endobj 1196 0 obj<>endobj 1197 0 obj<>endobj 1198 0 obj<>endobj 1199 0 obj<>endobj 1200 0 obj<>endobj 1201 0 obj<>endobj 1202 0 obj<>endobj 1203 0 obj<>endobj 1204 0 obj<>endobj 1205 0 obj<>endobj 1206 0 obj<>endobj 1207 0 obj<>endobj 1208 0 obj<>endobj 1209 0 obj<>endobj 1210 0 obj<>endobj 1211 0 obj<>endobj 1212 0 obj<>endobj 1213 0 obj<>endobj 1214 0 obj<>endobj 1215 0 obj<>endobj 1216 0 obj<>endobj 1217 0 obj<>endobj 1218 0 obj<>endobj 1219 0 obj<>endobj 1220 0 obj<>endobj 1221 0 obj<>endobj 1222 0 obj<>endobj 1223 0 obj<>endobj 1224 0 obj<>endobj 1225 0 obj<>endobj 1226 0 obj<>endobj 1227 0 obj<>endobj 1228 0 obj<>endobj 1229 0 obj<>endobj 1230 0 obj<>endobj 1231 0 obj<>endobj 1232 0 obj<>endobj 1233 0 obj<>endobj 1234 0 obj<>endobj 1235 0 obj<>endobj 1236 0 obj<>endobj 1237 0 obj<>endobj 1238 0 obj<>endobj 1239 0 obj<>endobj 1240 0 obj<>endobj 1241 0 obj<>endobj 1242 0 obj<>endobj 1243 0 obj<>/XObject<>>>>>endobj 1244 0 obj<>stream x=ŽÍNÃ0„ï~Š9¶RêØŽÓâc+(¢’á¾$†&u~ºQ¼=Nh¥9ìì7³W¡¡Òhì Š-ªN\a­–æ¶.´0FºäiY.vþd Ü8‰ƒJªDÿ ÿ ?:hÿ 8•BM)íÎÂ×·C_­ãðE{fúð1Æ+õ3ŵoE~´ÐzIØwÒ©³MoøzõL.xk‡êLÙB íeîˆñÂÍtî©ÏðÙôC ߌ#1µs†ÏUÀ;Å.ðÒðàÅIü†@AÂendstream endobj 1245 0 obj<>/XObject<<>>>>/Annots 102 0 R>>endobj 1246 0 obj<>stream xÍZÛ’E }߯è7  ÏN_æö¸ ìVªYS<;ö$<ãelC…¯çH}“í$ÅCÖREöÌéVK-µ¤îÍ_WZ•ø£Uc”­Õr¸*‹_Òÿ^ß]Ùº(UUé©AU¶ÐlÔ›+ÕVE+8¥u[T‚”¬k +YÁ¶®¨ë²ma PS‘B H!h®*­¡­™Qbš%‘<8éºa­ò€¹ a¿™pP<]pË;ìï[M2Ì*CèR’ÌÄ ™MÑN@ØWu°=O¬xÁÆð¾øäçWHœ€j-išH‰ÁÖ¤gÖ/ =JŠ 6ŠA^2p~Ä ¡†…&Rb°ðp­\bÉÃÎö”!UùgZ0#Ì"ßgJ@Ža9 1!ö».:1O@’ÙÀ—RhÆ`kMÞHÚH ¶-á‚xPÆti®GÝq踎ôc@V uÙÌÄ ˆ%ËŽ6.‘ƒu˜‚ a®\Ôû¹¬Eh1È~\Ç%qbISS0'Rb°9ìPbyIÛÙZ¤%#DZ”9ill&%ö iÉBÁˆI!ÚÜ<×+ÔÐÆ ªnð—e@ H‹ÒyȤĴh·dÖ‹­kh/‡A8]¶ð˜M¤Ä^,Í,–‰ØÛÒ –‘u%»“R¯ÿ™–̉IÓæñ0¢Dä™$‚8±š·)Mäp¶¬Ð€äI;Ç€& Â9sÎqíÌOýšotê·1paŸ)1éÊîH¬[– ²ØÀµ5©“8!”S&Y¨iÉ€ «Ihä´qÈ?™ôÇŽb« ƒ<1pÞ¨LJ }PÒJ%XI[Å+0mP‰-άW ÉŽR¾W‰AV)pA…DJœTʬW)Îõ*dV`RÉ!0Mb½JŽŽdT‰AV)pš“¡I¤Ä´tn3K‡Äß÷Pmñ?“ÐŒ0‹v"SBšœ9iµ,ŸdJ ¶«p,óT>B€znP< ™†%sÜ ÄymKÁÅBho-”Š·ÌQ-§ÔÌ E‘3]ÉfLF’ô>©»ÍöíbCŸ©ãèLø|3M‹,‘>cºý뮟¾áÏèRš¦ Ÿ_.ÆC‚¶ÀÙÅåþÓdÀŒ® Çž‘mr_p..¯ÕÿµŸµ¿¾­¢çÇar‘.ØkÑ/Æý´]–ûõv$¢.‹æëµ­0¤£Kk³‡Ð%³i6Y ^ýÝO¯ûè»s>¾þï>^`œ%½fÁ/M{#wÑêÙb·^ÒXBNÕíad·-6ëýbq“±eû„V:¶G£u1G±Ò ûiû~Z Ãz|OS(/Åòr»ê9ƒ˜6mÀ\qºDL©N|Ã&JSn6ëå"ždöœ÷îk³{NrôôðDîakDNШ´s1+N è¢~›Ö{8ç{Úgœ»œŸÖ›Up›Ã“M‰Å¸¢Á¦,\óþëÃ8æ±'PÝÝÐP´Li½qŸt]?z‡=É7 µæ<›ÜoûwÛiàMÁ1›LÇCTm£‰?­ßN‹‰¥E ¡rúš÷CÿЫ~\®}P ôÛXùNcðKc]’æ3Í­[*l0Ô›j¢ÛÞ¶Ó¾g‡¢¥íšX-âx?¡U2Á®/­êçåéP¡P ® òh† &£Ñåf+BøIwÅŽðt0Y‡¬£ñœqŠg ôè"ëè¡`÷bÈ}~+›Õ-Çž`Ž-:Ë£2WàŽsÅ'…2i‚ñm˱|oÙg’ª=Mª/F$UTëSµ Â—\÷± :ï'É4¾*CŸ|¬Í…îÈ¢Óº’YÈSø²ßíïù®à©1,Õýb· Å2d܆ ò‰eŒÏ,'á /[5l}zÇm;§ý›Íf››TüZ?~%û„-…a­gºM2î¡§¦|$žG*=Á_ (ÊþnXõYË}CJü¶8Ë%§§×ȯãNcÚüYSæºe‹êÔ!ϧ'Þ·ÆGѵH÷h\˜kcCO1#x,Ññà úà \ޏéøì¦>i8UÌ:w”ùñÛ±/o.Û‹×ß'´×†–ääîƒêtjîýWîÁ¿øígÎGþÅ‹ûSºÏ$Ë›«ó…ÁÖ¶Wc?Ûá¶Ã=#v‰ðóí0FqE¯nªØ7ªWýÄå[Jt¦MwÑúf9§È‚V‰¦Ò—³ûÃþú®ß“W©l}yåè[·3¼Šý§x³\†î4\›ñˆØ•ñð± ’Òjb¶—S?àâ·&Þ뎴ø‚À†¶¸Áä‘Ig¥ì j゚®ïûßû‰ô«éõë ³ ßç+"ÿØ€³ÊußOëíÊ?öà]H¤t¼ÊNïËð”@Éý¢çäÄλÿjàÈ!g¥ìçí8{‹–èÏîèº[Å{óöèìã¢i»ËûÊ[r}‹_J„—î¢BÏV!Õ¦¡—_ÿ¨­ü#¶¢Gk%ž¨ñÎ_ÒÕ¦ÁK¯)è~œ_ýrõ'š<+endstream endobj 1247 0 obj<>/XObject<<>>>>/Annots 129 0 R>>endobj 1248 0 obj<>stream xÕZÛnÜ6}÷Wè1…±²(êÚ7Ûˆ1âÆäY^˶šÝU*oœ¦_ß339’ÝRošéqÄá\Iå%øÏDeÙ"Zm’8Áˆÿëý9Dynâ,ÚDi\¹ßëèê  MdŒŒ(7ŽÉ"‚81/OãT‘¶ 9¶Âà&Âÿð>4QÁM„y¹âÄ Ë¸Vœ‚›¨.&óÄbÒÊ¿^˜gÓã…-†sâ… õœ‚xa=áD&ö§ò2™Ž!žS2óØF¹çÈDY•³‰*0ò›$J<(7QUaǧ v%Ë`@²‰2X‘L”ÕPD½OAìJABùAâ„ЄT¤Æä­ÕOåMËòš 9P—;B[ÅF‘“& ­ÖO±Yζ(JâXÙ†ØÏÁ¤#„PËûêIjÓ¸ÀöÈZ„µ:ÎXãI!¶âõxVÄšš,‹cÖê8c,m»'5†X8)ÖX…Á"Œ°ïUlÉîX…‘RÞ\Ïj ¶ÈA²¨“ìD¢ƒ ŽãLF;™yRcZù„b['äda®ÂXò‰f5[Õ–bÌB:…-͓ߴ܀ ö>P ræÑÓ8ŒlQ°ŸpÜ ‘ âmä&S"6sEò¶Úܲ—°ó ¡ b¢!KRc°°H©X m(äÙZ–·–@LDüA¬'5&±–ÄzVĦ%‡µˆeÄ:Îdm¹'5†Øš²©b†!‹”XÉÌ9YÌKÖFÒ¬,F^ fì8d¬Èsä<)šj¨Ía14MA¤è û8ik­—B5‹¸«ÔTÁbá™f¦ýK°Cáµì™iIYßÕ`¼\*É#ÇE7p ÂÛ+8¶â¨”¦u ®” ™\YGŽë\à¤-0´w,E¶ `°HÄV±l¬4£4ê*´š)bgR*ÑÔb3*)ŠU˜¼‹Â?°òR›Iö§¶ e^ê8÷OÊÄTú,Yƒ0Ñqd÷Ù–ž”M˜6!#ò,yžA»@Ûu­4QA$ y8vSI—Â}—æÈFŽm8 (Ž\ÀäÎLv‘cí§ ôCLRh¢Æd+Ú•0Uc°5å¼Àž,çíîpÕx0ZÞr¶J-É2Me£å 7ÆI´\½Z6×ë6êo£Ó~»k·»‡_–¿åXM]˜"‰³ªŽ)šd ݼÊcz}mކ8zƒ‰Ãç¡_µ<^%©#¯¾mW÷C¿íþjv]¿¥©TÙêJøx²J”Ä:%QCÊÊ’²Š†žÄ¨(ù¶_}¢1t6›í ?O°™{ðâË®ý“F-ŠLAÇ܇–áYMëY”‰Ö0å•+ ÏÚíª¥AœFµƒŒýþÊÙ tP+Ù¹äm¬ 錴߅뷋*¶jÞýEJG8I1¬Ó~½nW»î‘Í‚V )ÈyÙñ04ßHS‹`+Üà»ÏíÀQÆaˆÒŠøI¤4±Œ‘èæ Ã;Cv™jŒzV'n3ôÆ¿Ôï¼$‹,L‚ÈŸê(Z¦£2ïû5Û‡¢¤ÍÇ6eÓa8K]By©Å?yOÁѵ¨èšAÙëIÞxÛmÛf˜ûâñú®½ÎãrÏ~åJI!ˆ ­LÌJzü^·ÁÅ×îíƒ6Ÿ §”€~^p¶åãEÓ{™‰ƒYNoJÖB—+®É· J2ÍÀ¡Ç"§Æ®§!\¹ØÄáhyß \Épz!wÙ ;Î1¨yu9fž«þv÷µ$%ÁOGï½lVŸš;÷>ÛòòÇïMY°þ©™ï‹Öÿ¹¶#‹+_¹©Ûú5‰C‡ˆ3©ÛœŽ;”ÉæÌ²0näü^LÓOžüȺ)ºªjs<º0W+Êy­ø°ëÖ˜‹nZ|Þœ-MÜhõ'¿·’û“£3_%p£§Û18%»¬jVÐŽ5^_\%ë øf{ÛßkÒa¤ú<ñ{öªâ”¹ ë•I¡Ô¬*\†š|Òûê\%ذÞSsSIM0©.OªÂE»éθ$ ÝññcÓ­›kï«°1û*Ù•K§8ùN ó$›^àdƒÜ¶¸lºí9#n s3æÏCóÅAr-nEçqÑ3¹ö}{óeÕ }s³jv$¬Âñ£SÐ4bémìú¾ÇéäÙŠO:‹çßi@=I˜ïv÷Nt”{ò®ÒO´P©®­a±1FçLJ‡¿r.ǧƒ|L§‡‡4–R ‹ßI·½ÅRhÇg‘4èYS Uw¾î¯)¸›ÏwªKËã"{Ùsì¸>S|ÏÒ‘ˆÖC¥Êwíðص_i<ÛwS97qÅ©Ú?q¹0ׂLLcðÎ$­yºF@JË–(Ë_Ös]× -r¡»Lºdgm¨‘–NòͶÛu¨_áN¤¡­xîZaÙ›nËu=ì‹U§}TQ‰ŠI Š_‘³¡í”Eâ«ÈO‘KjiþñN»Èg–Q1¯»(zV 7¬ÉOàg±h¤r¢AÛ7IŠÝ0ôCË}=ÝŽ±M2¾)ýø>ý¹Èø¾±š€j ±zÞÁE›íÿ’äjé3fI5l®ÅÄdêlÅ£‡ÿýò£°/›E¯£3\¢»{TäSâ, #OKºÓ’xr.}xh‡è¢Ù~qe5Á×”`ÜLâžúæUבޝ—¿ü dbÔendstream endobj 1249 0 obj<>/XObject<<>>>>/Annots 136 0 R>>endobj 1250 0 obj<>stream xÍ•ËnÛ0E÷þŠY¦ OøÉekÔFEQ>@±eC…,%²œ"ßêaÚû"‰——3äá ©—…A? NÎ`{\42ÿû½á°V¢#‹~5<,yoæ‰ì錥ö¨(ÎZÔ£`/‘G‚s3£ó(KáGpš£`/‘(5†ÄL5¹š3\BSMnæÐ%®‹JIxÓ¢Q\=)ÙÙL5¥5mê&šõ¼á96.j‚ÇlZtó¢“'…§#º˜©¦´V¡LÜ!­·±bŽË`¢ˆi/’S_ÌT“ë±$n¢ÉõU&q}%ÃUæ/ùmcu¸_ò=ŒF¥éhœEé5仨‚òí]^<Õ%´{XµM_6ýéSþgq¿¶T~]N±K¥ÐÓÐîîg×nËÓ©íx&·¨´ÔãdÀ¦kÏÏ1ugÐã0~¨¿ €f4:KœtD !JžA'¬&®¯e÷Z•#°`? ÔÀ³¤:ÐvS uK±êÊ¢¯šC¤˜é‘šLI.Ky¸”R  ïÃïu)éFßÀé[¸‡²ŸØ4=[jbÛ•ûâ\÷<ÆUp)*‡öø‚ñÌUáÌ-Û·æå\uo‘.ð\·ý¹ÙöUÛÄgø5‰×ð:³¯úºŒ0!Cïúü eG©ñäWm]—´ï×`Ç_I~?Úç²£ˆ<Ú¹Û&žŠæ»:L¾êPëÇÂþGøò~í§ÇC4B‚¢Ç1SŽ!6uûTÔð¹ëŠ·<žÊ¾͹¨cù}J‚„¥ô0ïâá¯ùâ×â9¤¹Wendstream endobj 1251 0 obj<>/XObject<<>>>>>>endobj 1252 0 obj<>stream xmVËrÛ8¼ë+¦t’+-ù)'víiwS:ꈔ¬ýúí¡—Ë/ÂL÷L÷̯ٚVøZÓÃ5ÝÜSQ;ng«l…‡Ã «—GZ?Òv³w›ìqsG[™Î¬h[,Öýe£w²)¢vöbûs–>r“]·ZÞ¬²ëþ‘8»¦¿Êµ:µoi½îŽ]ßg›[>µ-½— COÞ‹s ÝâõiwAÑ9ó¦#ÕÞµT…Rx%©R•óg ñl¿>xQUÚHÙ£öÎVÊFÒ–"®.œê=’Ûs +Z®Sªr!uˆ^çMÄ…‚“!¢´Æ#ÞnQcðz~hóKÇœv½xW¥MPžjåC­@ÉQ]"Ïñy*„¥\ÎÉI "Þ žðWˆŽ±!í(3z2†¤ŠB›)fÊt€: ¨”È9€üme FžžšDV¶uhŒ`ôS’vùOÀ \í½ó•àb“È]S¡`ä‘BÞtx’ެ‹T¶¡A-Ý~ AG…JÀÎ=·×àBÕ¬ˆž²J¥°:T¸½PÍ n¾{\^¯ÂKúD£íø€W]fp™…:Y,nÍåfi§TXÖ@ÁÖÒk¼-Å$x×& ÓËmÍùJÛemàý¦R‡”:áÁz²‘¶Öû¢ø­¹ {ÒùÏ#–5¦ jBdK”Î2|ïšC™h€6z¾YŸi‡­Ã@Ø;»pUmTr|/RÞõ Ðä!êwOWà Æa'‹¤ÊIeZ£„ý"”;…t0¹yt0’ì>Ê= rzqšI ôx.uD5Ì«Œ=H[­¡E‘&éh:©wU4ÉtÇ3mÏCG Ñôœí§ÌgðÅ®b*\Ò^æ@Xáu HÅ"ãéZŠˆ™#>/XObject<<>>>>>>endobj 1254 0 obj<>stream xWÁnÛ8½ç+æè¶l'nìô–M·‡v±îÍZ¢ln(R%©¸þû}3”lÇé.‚Ø¢8óÞ¼y3ùq3§~æ´¼¥»{*››Ïë›Y1×§_aGÓçšß·´®qúãjQ,îh]É©­ËÑ?«hJzî\™ŒwÊštü°þûfú¼ ù<¿:¹½/V ¼9Zï5må…¸WAWÔèÆ‡#ùVÅïGŠ]Ûúð̸Òv•ηÝ÷·h§Ó˜Ú¿b©RÒám¸)W½{G¥ý»ƒ!#!*}ÓZÝh‡¸üîŒ&y5Úöp>žPÉ7¦¼´ª&=AÚA®“*Ë®é¬J=ˆ’mF@ß egð”ö*q.[ãt¤J%ÈúRÙ©ƒIû« ‡s€Hoiå7™ÕÍäòž<~É0Ή+QÐoþ _u#/õ9ÏH¥rä=ÒVSQ«ä¯’tÏvÖoB… Ž‘Pk”ƒá:|µ5)(ˆ ×Ât,è1‘Õ*&ÄÒtyíUDL²ÁﻪéÚ£®B nw±F¼36Ðç.2¼¯—X)t€ˆØ“h*]M;g¸–¸ŠÆÚ+ÔYGISÐ;*Ëø:ÈÚNêE;Ú™Pik¶RˆÐ é*Æ.ˆ¢/ÚÈð“õÆ vAÀ»K¬LcÑg OKẋUÓ BP;Sƒ¨rƒj¤ÐŠêÎZ4†KÁ[òФ«LLÁl;&—i¾TÔónÇ< ò¿ÈØ„áÙå=QüàdB0­é/;õŽ *ªtRÆÆk ëœø¿J}‚,r h¥N`…âsÐT­JÔú²/ªŠD-ŒIé[[DpUõïΚ4¨”,cÒMdŸÅå`f %_H±åPb5=L$Rìp3âë§Ï'Ë…ÙNáºùÛ“U@­ç†£fWIt8è‹A€ÔÎ# ]š:7BR·0âÜS|£þ üH˜¥i2!“(¸·ØôEy! W9£^> dJ• X0´ ±¬lovlK¦×[ÚÃÈP Ç?¾Á´UÛÚ7‡lÑ0WÈ Æˆ5hùÈ‚†™ŒÀŽS.ªjÔà»ïÄþ‘zc"7¢¤)þ±:4@N¶oË_…ÆFÃc¤B­ÂPöÞ^xÉOT/qGØ÷»XGö–ÙøV§ƒÆ×€|L?ÐÞ²Í.ÆL~«ÉiþO—Ñ1ô²48úËx¨­r„Ä}>1,#¼+ˆ/d pFh‡3»•ŒteîHeÿÏè“ÛU18ãeB&É{ûšÎkP„ãt°^Ò±…”`‘ÏØP Oø;éhÌ@;†ówLjÐ[õJ†oóhú)ó ®tó$w‰hÇü紺ݘjë>ô7s5rÓ ßô·ŠiªWØ"—µ ï°·à0˜÷Yà ÉEtT¼ŠÏ´ ÈNtÿ$pÁp¥WzØâ!Y&l ˜ìx†fŠ=Ô¯ýJÀT=‰cçÚ"O9ŽWÑ/t} ŒÎm[îAhtˆ¼ÕÈEÿT¼-²µrÚOÜVë»/§S\Œ&;Ó¯÷JÑ7¶dÈãv 8P’áå$¯PFÅÐGSK¼ìò%Ü$ ¼AAOnnf•6ƲÂ5­NS®×qr,ÞÐÜNS #’Ý‹®èØz¸åLök,þÀüëvÌhc3—ºây¯zW›˜7ÈAó„ d@9ZN.r G2p¶´¾Ûí%ð…pûþš¾\‹Õ}\,‹ÙbÅ&ð5o¡y ýγèwå:%kÜdxa²œ=ðiúo«Y±Âÿ4â-· óËúæÏ›hj'endstream endobj 1255 0 obj<>/XObject<>>>>>endobj 1256 0 obj<>stream xuUMoÛ8½ûW zrX±,%·»Yì¡èpo½PÒØæ–"’Jê¿o(ÙUÔ-'1?fÞ¼÷fø<Ëi‰Ÿœv+*¶T5³gZoЬHË«bŸmh™-iµÉ±†íû¿·;úÃÑ—ÙÇà ;¸}ûåOtÿô@y‘­èpD„Í~í :ÔéÔ’Õ\âüãÝÉ«¦ÑöDŸ\ÍæýáßÙýÓšò¼¿¸Xm³ý÷æ‡3Ó_Æ•ÊÐïÕ%Ñ¥WþB¡k[çc øê¨E ñb8>/XObject<<>>>>>>endobj 1258 0 obj<>stream x­VKsâF¾ó+úe #`yäF6±O9$…+.£Ñ#ÍhgFvø÷ùz„°[[ñnÊU2 iõã{´¾ŒRšá/¥õœ+’õèçýh6áÇËÅéáqIiJû‚}Ú¬¦Ûí‚öy<4£½SÝVA7•"§DN¶QNm§ÃXx ¥¢\A¹UžŒ $KaŽê0¹Ÿì_Féæüüd=ÝpMû||˜¯ÖÔÝî³Ç›Iº˜ÎùÀ{R!e‹ DP7©Ežk®„´'iëº (ì•3“0ùÿ’=×þÅj¨iÃM~´î'·¦:á¢èÍé Ö‘²$U©Z™p˜L?TM2ßLÓ-%óÕtÉÓØcÆ¢i*-ãäI Cµ0â¨Ð·ñÚe$*(Hß¡áƒkeh0ц,—Š0¯Ù‰Z¯Í±«hugL••ÝS&œÓʱëÁ¿ô= @RõogtApÀñ*t%20¥0_*9áNq(¶ÉTJáDU©Šj›ãŠ û¶i¬ *çòy4 4S’1§¢5øÀ¬ ¥è¡e +›‰ŠÐ—8*ÇhÇuªå‡sô”žM®œQ"”÷¤ ô}BÕµqV*ï©fz,PœS_ZíT~?lCưª-˜[ãŸ;!²9]%>Œkë¹@x˜pyƒ¦j$Ș‡gÄ<óiK` ÈŠI³b¶¤ ÍnÀ–§VcŠÚ(&¿éæÁ1ÐÙÛZÑñ‚ŽÂåœÝ·:ˆLW:Dª1O»Èõ\¤™„Sƒ®Í€®þç?KXžžðWï¹Ð`W-tTÖZÒg¦7é‡OÂÍ.'‡Iè !”ŸŒà8蟴CípŽkÄ€§9OãÝž 9 £ó§ÅrvåOÝÝ ½YâRT’)Ò.w ¡azö5Tv ê #/rûÎlFùiÂ£È Ž^ð} ÄòkÁw(ÿH¦RˤR¯ÐÞ@ÆÐì08µ,ij 0éÌóà¼IÙ6$¶H¤©âÙ$ƒÿ ®‡—tG¿ü±;L¾^u¿·€$«ö x³Øv¼ö=;¿Íš¸­àv"2¹n!¿L‘ú›ýU8ùů¾^Ódȳ\Ú(øÞYËÔk9Sðh%XËŽÒ™»ºÙZ"eLÉVÍ‹¨`Oȸ¤tï˜ðC]E&Úì¶ ³ÒÐ|·’X}@<: }U®Œï }¾»ûX¾d»í`í·Bt ~¯ø^Çxxìß@Ò%¼q³ OëÅt¹Ü°Y>uÛa×m‡g5ù›0­¨¸ê$]¯c@²žmãkÉ·½u¹™M7½‡,ù ¿îG¿þ²Âàendstream endobj 1259 0 obj<>/XObject<<>>>>>>endobj 1260 0 obj<>stream x’ÁnÛ0 †ï~ S V­Ø±cW¬=í0̽åÂÈ´£U‘h·"1Š[x88Cáåªèü¥¥¶ðK¡Á ÕËîæQèÿèÊוØÖ äëZTIØsÀ½6:žàÔýÂÏQ:š¯ðŠ·À’ÈOÞ)êgТ98èÓ•LÚgœ3d®ÌŽÎÐ0Jæ|‚ýÕ»Àk0hÇ9ùæir>²¯ƒVšl\°¼ÛÏ<9:f}Empo¾Šq)k±‘5ȶåan’yOÆíÙ†{ïñà9°øhg4Ia.›FTm ySlS5H¶ü~šx¨Ïóó4³ßF[ZÜ®ÚB´muÉM:á{—ýÌþÆþŒendstream endobj 1261 0 obj<>/XObject<<>>>>/Annots 141 0 R>>endobj 1262 0 obj<>stream x­WÛrÛ6}÷Wì£2#AâUT:#;¶ãÖ‰UIqÒ¿P$d3% •«úûî‚*`zIÇ3´ˆÅžÝ={øÇ…ü³`jƒãC”]LØWNåí…o±)x®AŽc3G½¥°:ß^<“.ØÍ,ð=6Ã×)¶´lÏVK½ÊuÜe­B¨Q‚€y­…^$ËsþÞxK©H+3—ù­•~0Ûõ1:SŒºNMéH;¶5cnk¨ÎÅH=£!]ç ®N¹îø,–-ú¡åÅ hLy‘îrÙäÅ€¤h1H4•.V?“ý`Šƒ]ǘÍ‘ýp*/CºÎœkûØ+¶5Á6ÈÀõf§7Õ_`KÀ.Ù” ]vdÖ\•Û¬™­B¨—i)0!5¤™$šŠ1k2Ž.Ïý` e&3ºNí;4÷Ã5Y3ÒuβÖG§\Gp®âà;›r–AÐ* P—½\6’¢Å ÑTLyiBï2Ù¦H1˜Ñuºhß!²NåÅ`H×1åEªàC£SÒìxjÊçÅ hº@øfC“È.—À€¤h1H4•.V?“ý`Šƒ]ǘôƒ&áô± p*/‰îÂÜåúb|3Ñë-^8¼iÀfëûd‚7Žu4°|.’*ÉŸ‡pY'iŒ¿ ÌcXÖyN¿oç°(Äsfå›õWÄsÁ²¼‘ Œxƒõ ‡=ßÀ.|æÍ6œ”rÛ`¿ß3ž•)Ûå){¯ãXDåø9›0—õn'ŠŠ½TYÚ…D"¯Â$/¡ÞÅaÅcHò­(²°JDN›'0²h£ýp#ê êRy,rˆ“í–<¯`—†é• )KŽCEP ¨^’Rú Û‚ÿQãîô¸2QV¸-"ýÆzI¬œY="뎱& d]ºF¼O>yI÷³…òF’|Ÿlа8À;¾ãyÌó(áŠeÔ?±Œ·W©#Àª¡ é8B%¯”#<Þ¤ÊÝåX-†pµœÿkçzüól2^=ZCXÝÞÁC‘<'ùnê¯IUÖðøeü¸ÀÝï“*Œ^’&+P®Œ¦, ›+òdûShÄÇrÂS>®Òº¬c±…½(~/+™´ò-¬DI9„»åÝ—!Ìéñ~ñ ŸïЗ Kb]Ô¾ ŸòäÏ!Ü'yÿ>®À•U…‰ S‘s¨ód”Ý:­’Ñ®/K|×]$yÉ‹W^(ZÿSüÿÀèç$žÄàþûTdØè§Ì?¤1òOa`·””ìdÕnX;¥È8„qŒ­/ò0…§A.ö 6¥HyÅŸÞ´]ƒJÑ „˜»»Åêj¿¬–CXÌ—óÛ‡Cʋֆï®ï×s¬¶‡×_àT8@ˆW¤\Tší$ÇÆÃE¾GŸ?9ÌBƒ'W7< kìÒ=‡XHÕ—ð([صg6 7—¬7jñÏÔÞ‘È2l¨v ØØ8š½Ç¶-e¯Rªn¹9v>²Ež6éã Xª½h} _Ã$ 7) Ž=Ú$•lkߥŸ?Ž—ŠÄPîx”l±½á5L“óA²ž/o¯×‡n ùóŠM"êR#íä‹0žêJËbžíÁÈ2ß‘³ð8ˆ'ÛÒ¿?Gk$ÆÀƒ*Ç(9@ëx69“©añ ‰4=ü²¿U³ð[̱g'{Kž…Øü„Øž#DZØlæ"í6ó}:Ž«:ìÉ"TÆmŽq®O/c²6Úñ»rÒüm€ËÕØ§;Ñ•¨Ôfo\ùQ®gÌ>¹‚G’f¶IÜý|qG‹šUZê˜UôçâÈx%IÃyQŒÊ]q¼5dúHÒ‡Œ¨F4õµ*’LÄ6‡c¼ã›ÓIg»³ƒ)U8Þϰԩ´þíUÉ &,À“[Æç“×ë‹_/þ£¡yendstream endobj 1263 0 obj<>/XObject<<>>>>>>endobj 1264 0 obj<>stream x•YïOÛHýÎ_1ˉ¸±8ŽîSŽãGª–r`**!U&1à*±]Û¡å¿¿ÙÙM=»Ì^¯ª„ÈŽß{3ï­×ÁývÂÿ…0‹ N`µ=c\ùù£}T`:Oñg2 æøqmêJ˜&)~TK±úHëxU$€M¤¤¦ÁtPð2…Óø×â6—Ê|$ƒŠŸ,š$8°4#ÇØlC:Q8&ƒŸn‚“NE!Žqè|vÒú42¹„Iò\„Âr¡„m/u.“±E¨0ˆÍåwÒOfLd8FÌ…¦qŒôÓ™\!Žqè|vÒz8Æ›CÝ/“a÷‹PR.Ô”í¥6Y`2¶±¹üNúÉŒ)‚ Lj¹Ð4Ž‘~:“‹ Ä1ÏNµ>™„AL¹$s<†\¤Âr¡¦l/Éd‰IÛ"UÄæò;é'Ó¦H2#æBÓ8Fúét.’Ç8t>;i=Œ|D½~¾L„ÂrÑMÙ^j“&c‹Pa›Ë蘭̘"ÈpŒ”‹žÆ1ÒOgr„8Æ¡óÙ©Öãy‚?U.ÎóE* )jÊö’L–˜´-R…Al.¿“~2mŠ$Ã1b.4c¤ŸNç" qŒC糓ÖgsóÍÍE( !Ý”í¥6Y`2¶±¹üNúÉŒ)‚ ÇH¹èi#ýt&Aˆc:Ÿ´žÄž\„Âr¡¦l/µÉ“±E¨0ˆÍåwÒOfLd8FÌ…¦qŒôÓ™\!Žqè|vÒz¼ÿSÀùû% @Ê…š²½Ô& Lơ 6—ßI?™1Eá1šÆ1ÒOgr„8Æ¡óÙIëã©ù>ææ"€” 5e{©M˜Œ-B…Al.¿“~2cŠ Ã1b.4c¤ŸÎä"qŒCç³S­G³Dþ>&€” 5e{I&KLÚ©Â 6—ßI?™6E’á1šÆ1ÒO§s‘„8Æ¡óÙIëø¦"•¾'+>·0„\tS¶—ÚdÉØ"TÄæò;é'3¦2#墧qŒôÓ™\!Žqè|vÒz83¯šœs, @Ê…š²½Ô& Lơ 6—ßI?™1Eá1šÆ1ÒOgr„8Æ¡óÙIëãмht¾'GBaH¹PS¶—ÚdÉØ"TÄæò;é'3¦2#æBÓ8FúéL.‚Ç8teoO'†=@Lb|[œâûϲ5ac|ÿœ­Þ×Û&ÿööï“ãÃìëÁh £P½1ËÖoòMó”«EåÙ8‚ï0\†ªî!ÎËM~¿)T=©£jHóñú”˜È„¢(H’©*Ÿ_B“Ú²[1Z <¿¼¹u¸ÔR2Q‹ó8H§ø‚—$’Éè¾ìá¹h»²® _¯‹5”œ- B’V¦¤Ó)Ž‚=ké÷eµû?ÒäH]Á¦Ümú6w›ú{Ñ6¼Ë½)ð~yñªÓWŽŒ†áçAÄ:pÄ— = ëçîͲϫr·½;t»Âî¿×lú¢­«»CEn²Š|!þ«°hM84<™Û–¹»@‡ðjb¶­ ÎìœÿNîO ì˜zk wh¹Á¸¡Íû§¢…þ)Ǹ/nðõá·]Ùk ¡·ù dñ 3æ×í_-> h°eØIDún>þ}ÒÏ.©ÓŸ›Ã!½þDÛ×Dø?;5 ¡Óáv£Nos…–ºçH5j§·¬à6<¸uñã}sÙâêì$ƒ²SKe…7"V1#“%Y7fWVнt}±…¼®4»>[ÂòjI§£Ú–MǬßJxõ—‹8:üE•Ù~Ä•ìÔq~0é¼ìóÕS©0Låú*ÅóQÐ9_f‹ãsWâÕí ÅqºûZöÝ>]R3LM›"uzón™]ߌ †MÅ*¿{Hª±Ç+.NŽášüæ®hË‚zÛïÛ½çx=Qí×ÙI¿hýlØ×h7X½už_íªJí ,Â-Ô-<•x—›&Ó Jgxãë|~ቖ՗kÜoÏŦn¶EÕCý»Í暺íÕC r”/W¸ãêª;Rsôlëu§xU‹'HYõÅ#'ë¼Ïû—¦PÛX_×°ìñ£êÅ´­ûò9ïõ.ïÕN‡ªÀÏØ—„¾†]W¨gÔ lòö± èúv·êwmÑA^­!BŠ}3Ë‹ìäìäêT=^ÝûBùc@†âf¬¶h­ ‰U7ÒlùJ¤›²{Òì:E‹}·uÓ–8—#»omþÂÃ& ÔEoOÓý׊Ù,˜¤1L§i†± èlSßçX´x’tpƒÛ>äÕ.ß(ä(4€Ñl>/XObject<<>>>>/Annots 145 0 R>>endobj 1266 0 obj<>stream xXÛrÚH}÷Wô£S!qËñ:.jãÄ $»®ÚÄ€f£[$áË~ýžž ‰‘wË6†™éÓݧ{ºýºriˆ—¦#ò&ÄWCgˆ•ÓË·»+w2t\ògCǧ˜Üùäô)¢Uû|¾gaò‡ü:w>N)—´ã“äÍy³xXö¨’ö‘Çi²SûA,~JpY8¡¶m+K¡"s¹\Ç÷FÔ÷]gˆÿ¸ ôÙ\v•ÜãQ€^¨k ¦žãñ­Wï6é®BÅ®ÁÁø­­Lñ@@£¯+¬?E š8ÃÉXŸ½‡9ÒúÂsYx¹aÂêËÍï·ë=^§ß, ¶r'QùøîÒ. ¸èú VW_W/«1_b›« |-@Ð…I·‘H¼Á*Œe|a××ï‹ß¾-oV\n?->{—¦|RÉáf‹( Eïe6é-ÿñ]npÄ/–øfÑRW¸FØØ2ÿÂ&¶C¯6Ãs´ÃhïÔºDV%jƒ”æ# u?ƒÚîo?Aß—?y枟}îÔZU¹ •w÷ ÍÒª¹¨HîTx§ö‚Ëjã/´þX..ÔVk6½ÚC“…ƒÇ~†â¹ÑtŽ´÷qIôýãÆæ]6¶˜«Ï^ö3Q\j»>DœÚ¢ ù‚;†¾À3MŸôz<èÔ7L¢¡HÝ?,9?×7w÷«;²kS²àv.P¥Hô]©µ³’7€]ïߺyr稴˜.ÏzŠÙ ÔTtv·kGåE+¸pí øJô±w ݰª¸'s®\ªã#EŒî® 27]w¢({hf¼ ÉèÀÝJw%†¸Ó¡®ùºUE™« Æ-tßL? hÚ%¿'êÅÐl* {¥ª¡ÍÃ2°ÅØGéj,2¢ ¶HCŠ6Ñ(êO2Qè}Q¤ÛÃð„GËRäº! s" ü•aŽžpÜAãá¦pÈ«ù†‡£ü$œUœ<Þ‘“»æ±=&Ȉ÷|Ï O™wÄàŒz¼Î¹ñ OƒÏÇwïi‹I/(a¶Vƒ#ºíꎧ[©“ílŸ‹˜Š0=D<3`°@ãpË&Ô ƒ’ú§ªáˆªij}ÌÔÇaÖuˆÅgÕéŽGŽ?óhŒg#=K6 úÎLÞ‹ä ô Üw§x8ŒóýépÎ}‰/¨KçFö€Añ4ùòãöÙ ùäŒß®¯¾^ý |Ìèendstream endobj 1267 0 obj<>/XObject<<>>>>/Annots 150 0 R>>endobj 1268 0 obj<>stream xW]OãF}çW\ñ$lâ$äc«>°,¤‘€¥KÐö!Òj°'É{Ʊô×÷ÜñG\W+UDQ<žsÏ=÷Üᯓˆ†ø‹h6¢ñ”âìd1Ò~}[žŒ§!fŒgáˆ2º‡³ú!¥§“ÎcF‹y;‘ßu3Š&Ç]øeµéh‚#Šü¿û¼î£°;º¸PÑzË8/çÓp±Ó:ñP‡´ŽË+zWÅžÖ×Ëû§epÿ¸¢Í€¿…NêA’Y™ŠBM©z±Â6gŸº T–§2“ºpgë?O†DcD½NÛRǼN¤ª8Ù6–Néá”VšŠ½r”™Džã§¤L:'v’ráü´úLÂ$¥U¡°Ùß2¡jÑ;± ƒ/n§uäzü|³\=l·›³êUCÊ€b‘¦ô¾Wñ»Òkz +·ÒJKQw+€þ±jaôv imHjñ’ÊnT‰Ü*-ynÐ!‡c•úMY£™;‘Ò›°Š×öá??ÝüÀ¹ýÃü†HîxN™íQH<„‘àrÎyèßÙýD‰²2.`'çý`MnÍΊŒÜÞ”iB±<¥ëìñ0ÚœT a>]Ø@k„0® £K ¸·Æú|³ØÞ¤uЉ«`òJ4?¡Ç±r~Ü­>{ú„<\ß=¹!a%tq†rƒ|ö°b˜YOM\…"ù™4*cÙ+§e"k¶ŒUÒóSþm ÌñK]Â&ä®Y»õfà6g!-¯zjfß‹m´>•ôã½ÐÊeØ[Eƒqë¶K!¡yÁN½IM9ªsklæiGjFÓpÂÙ_cò qµs"ÜëßPåBû}Ûê­8ÙZ“ÑÕÃÝŽBýá“犞 QÈͽJ™·”í¥H¤í¸U)³¥QÐHÂÐÀz¤‚`„ò¦@pl²ó-½[‘çÐ@7#R±bú,r:9Ẕ&G’~~xH_q`Œƒ?16ø^K3×ÕËËäGDlG|‚+,ÿô H3s_ÕŒ±Á€Î¡>šÔ«S>ª[Ý›ÁÁ”>ËõUu -àu$ðÙÙ‚sÓ2²MÅ:'T ÄÄJi‘¡BÇaTyY§Ä.à&!Ä…2A§ç§žp¢å+èçôõC¾¸æµM³pQ£Íh:óvrì þ}[¾5—$Þ¹êZÜm&e¸ Ï ùc¿¶³§ò¥T0aÕn¨K9c{1¬ìç‡Õ=Y½û ØœOë€7VÓŽþë[_0š‡Ñâè<™x•t{ýk éÚÿŠãŠck`%ʉäMÀÿБj»p†˜TjÜ7˜X”+"⹸œÉ¼“w2+—âàÎB™ÿEûIÕ3 %M(C»¨›RKìfP1º|xfM<.W¨GôD6\¤¼Ô¨E-Z9²2{VEßîÖ¤)¤'è’ãÜúŽW´™tm•ßÁ² +ôÅ5âÌ­ˆëž‹ø•ò!õNÞ •¶iG‡än:FŸËf\÷}•¨vˆÃÄqiYÚèàNéòƒžµú¨ýÕU¥Éï4ÄðvžC•x‚z§ïâF\6âþªé»Ò‰ywô°®zNEÃo’s|¾£8 g¯Mú;h<‡œ±Œz¾ ú'!öφŠ{Hâü•;‘z<·‹ZŸËÆ•.Ó™KäSÇÜyÀEeDÎl .6ÞýX•Å+M"IpEÂ’Ú2ëËTИPÝæÎ}ùÉ¿JܪzêÚäýß%ü@W©ï–Ʋ‰¶M¢Ý@ìZìøêÂdñ¬w7^ôK…°ç,¸n&ƒNu6…VÙJS¾WßbîC“àëG–•ZÕ=»iÔMÉÁ‡¼‡ªÂ‘-uP¨ >î»ñÿÃÑø_4Y„#ÜŽ/ç¸^«ëSj^Õ•µâàèÙq'º)D³Y8™)˜ Õý+Â…ü©Ìs¤¸ë¶ìï“ù0œÏ'ø¯,,xýÍúä÷“7·Çgendstream endobj 1269 0 obj<>/XObject<<>>>>/Annots 153 0 R>>endobj 1270 0 obj<>stream xV]oã6|÷¯Ø{:°y’?å^[Ôq#¸ à^\Ü‹-Q2JôQRŒô×wIж¢(iÀ‘,Š;;;;ÜŸ½|ü `6„Ñ¢¬ç9}}[õFSâÃ8ôÉ2˜†øÏÞ¸ë5n3˜HÐxÖ¸Í GdÒxx¹iGR)|ºCÀ&ÑX&á”Ìç#ØÄŽ›È»üº¸ÁwŠª'à(ö³âŠÅHåžã)Ë )™šÇÒû8eYöùbóOïÓuÖûf$Ô©obo;œÎÀ>vÑÍÃA0"C½àëb½X~ywØ­G¡¨v+5BžG¢Šá‘—{X-úpÜóh¯í*.pM^J}ê°ùpй žS‘ú¢·½èÎÉ¡îHk0 I0‡ÁdHBØj}* 1;0äKæ ‘G…LÊ#U¬~ÀóT?Ó'U•\æ˜Ó?W‹‰‡5 §(³õóœ¼feZ™ôÛKuÑ8MÛË4gŽŠ°«â‰’™Á¸¾ºY„ò¶ÞZ0Z0ˆd^Ò¨Ô;7H^1©R×4Ïá׿Sþ‡T¹ ©|üèNV%˜·SKFoPn•äX¹[.¬˜ú°¾ÔZî‘âÕÒ¡å “DÚ›ÊîàGT÷FåmŽ:–F{)Xñðdh4ÒFš…(ß½OqˆïyŽeè;·ª×U¿.pn]¬ƒma—k.u%‚Å}SÀœfØ3 M­ÖŽ\\3ýÞŽ}¤\Ð`F…/šÜ‘ßѶ:£9™jÍ":™38ÊJĈðA)íDcÀkE#†0Qw™Ádˆ-!ìBhs– gír`ÞfŸÝŒÚÖkÕq°Ñ7øsµ¸ß|[,¯°)ŽPAS(ö0QXÄ^ÛáÒhëZ*Œ˜£ò³LÝ$uHxöÇ»ÚúÆ8°¾1D_ü®0Uì~$`­dªhf8ËSAæCãK8Ôk°¸´Ô–+!wHÔB)úT¸$j·„¡Öû9WôàGj^!û}€)µ?2Å,ÞÖ¹9÷Àeï·8®©sQ»uFÎhbc$:¸‚7¶À§€ä±•B½dUa4zP 1D¬@³ª}ì@U 2i!²e2ú2ÉX ‰”²]§âR>±þ¢wjZð÷=Ëõ ªB«6§ÏíúÆâmù×ócÐ'C=Œq*ÐS€9–ƒúhšŒ‰œ]¾¦×ä`'|ËÌøVh˜½—v…Ù¿Îûâµ9îîž7Ö=(X4^n=B¬óÖcÉ´dT‡ÀÜîoº»iy`>­ɯéx™/ž¾Ãól° Wj(œ þ/ÛâáÛÈùœ½Ä䌽ªñt¾ÑÂäY}£ýë–è¦ ¡é§ù3)«~Ãü\ÀÝZp/˜mÓt9pË$]XÑRª·ñ9Ó%øX¢©ª3du”p¢mÅpÖ\ýÚþfõiÎpÅ)™ÊpN*߬M-í¢¯,oçÿJ?Au€÷Õ¦--atêÿUMCÄ×ÝË_Gvêj7ÛNHŽ`2‘ñØÎyϬöï§§[šWÔ¨ÁlFÆøÂ`æÏõ1oØûx6&³i]­À×5ºÚôþêýv4]zendstream endobj 1271 0 obj<>/XObject<<>>>>/Annots 155 0 R>>endobj 1272 0 obj<>stream x¥V]oâ8}ﯸ}YQíà’„òQièlA} Û¦š$d'xHlÖvÚaý^Û ¡ ®$ßësÎ=÷:ÿœÐÃoâ$åYôðÎþçÛÌÞp’>”†û?<žÝÄø4´ëñ'°A*‡Ë)^gÉ®F2G§.m⤣Ü^Ä?Þn†ÁøÔçØOn`«d®hiS]Nûõ®Ýw톧ïk&`6U /ܬ!þ:›?Î@ªúª;¸ó‚Q“bHFVŒ_„ƒ!x¬ŽhÿhP¯¿ê“^Ðî9•Ê(*jM‘aŒÈÈçþz÷C»Á @{2 - Ø®XÎE¶è,.Žì^aCÿps;»»_.:„,.j¯êÛJ~ÚPejQ>FÖ \ên‘ЊãðätÉ7œü_vU4º8T³ÉòîXDƒ®Ö Ê ¹¢P¥èNçv؃C:¦Òð” áˆd-¸¹÷$+Y)Õ°82¡FªÓøgü@*áEªÍTm!ß/~ÛvMmšôMm S%Ôœ,M¸/L|¸ü„cÎÁðtÕ>Wšö…ñ.f"=a––ùÃíýŸh`´ï 4~h´˜L’—:?V‡×&þÕatzÙØuƒû%^3‹ér:¨G§1§¿ÝŒ°8 )7 ¾apí4“µ×ÎOý½ÙÍn˾؞N6KùÖŒnÝ¥íÄ<ÈFE 8XWTs a?*m —Lƒ‘`Ö þz»(gÆõá!—v³ß÷{½%Æ V2a4È ,,°8  2@KY ƒl bj¦LÝ^óIÓaF0–ZD+ºÊ2žpL u,§â"mK´,l jpU© <ŒjŠeLÙTš%†K?µÛÍ#‘À%¶ä!e†òB; pc&jƒl~œÈ!}¦"Al•¦9³$6w'P}Xiâ%ƒç—aëuC;Ñ !‰à¦âEjÌ&¯Åóe¹cíI¿sÒìþ Ý´©ï·V²*Ù”®zx|¥¨WXEºÝXhG¡Ág¹< þÓ-˜óDI-3ã¡´¾m…‘ß¹Hå‹'yPC[_mT•˜ UBY¬šôµ¤«‚9Ü/°°T9ø2²?õ˜ÝÌ>¿öÈš¿ÔÉc»u¹;Ó÷¦šcÆ©ËX#†ûxãò&Rd<¿l’õµ5MæNH‰¯ ¿¡’¢JQŽ]¹’EÝDŸä`Œ[i¬¬•Fÿ–¶Mõ–%<ÛA<ù6»Z›Ï\Ia›ž©âNïEG3ƒOš¥žp3]:>ÁRÐò­›p c¡¡±~>¨yg †CÒEp5ŒH¿?²nžùC{âm@Ë*˜SQÑÂFv›€î°7>âýþ°O†ƒúm)lØm|ö÷ÙBåׂendstream endobj 1273 0 obj<>/XObject<<>>>>>>endobj 1274 0 obj<>stream x¥V]oâF}ϯ¸DÂæ³DÊ¡!mHºqº­„ öÓØ3ìŒM–þúž;Æ@ØdÛj%’í;÷ãÜsϯg-jâ·E½6u.(ÎÎn¢³fØÄËý?»¤Æ¨K­E ØþÒ¿//;%Þ¦IQ\OŸ2ñ"*•d,MùJRl²Lè„R¥%½®¤¦X¤xX‡4‚©ü&²u*¯Î£¿Îš´:ažktøq2—zCÑàóø6¢Oƒ‡ Ѭ¶ÀÙÉÍ]ãñÖ©Èñ˜ÍÎ?t2«;;§…5Ù›¬ê´äTvί½ó“öEØåL~©Jˆ_6FʃP+“yÖ"“å— žqba•)¹b½66—É>G4Í%Ì £J ÄÜl$åbž•2`Á›ŸT%RgØCª{žoß Z¹ÌÍšR¹‘)%°³j^ä mI”•qnì–{ô4üA ‘© r[8ÌœïÔIÌøÊ‘69%h´–IH÷š¾(˜WW§/“i§]§áŸàÂ#qÛ'Óèö3"Ìjk+7 â¼hóªI8º_K=Ða=†GÍßÃzÜñ¥jÆÐdkpÌÒ£ÌsÉÁ¹Yse"_yFýüº§%ÒEš3¼3ç³­ž¨ô‡—ßçµ–±Z(€Ž¾ÅF/Ô²QÑ>\1Ìù"M(^ ½DG1¨}‡ãö£À‹T  ¸Nà_‹øEÀŸTðh¹åΧ²åY«ì݉wüòsùNÂS“Ë+’‰b,Ob~gî™-ôvÏe²òk>9 qnhh¥‡RiLl˜xøÁfˆ!rÙÉFè_ '­ 9$TçÐÞ!ÄCÅtdð†û&¡Ñ2\†Ø8®ÿÁwÇTN}cÔêï†3è…}V5Ìî¬}ÑÛn5 þã^fF§GW‹ ݳa¶×«Óâ²Y'fÑëqx”—ÿTøáýC„Š™q™ú[ø …ßD΋%•t`?@"XÖ)¸ïüT£ÿžÁ‡Ð”y´þgA»¶.½‡ûjÔ™µ=fH)×£á5ZAÃá5À%\÷]b4¯Ïãþ ŽO3ߦÈOõ¼œ¸Ã©ÆÖlTn ÕÚÕ ïžÏ KRd˜ÈÇòA¯QÕB–•ê¤rZAùrs2j;½Ã1W̱î˜ý‡ˆ~J£ËýÞK;ì„-zÒêÝê²FgRç%xßÄsàˆ¿C,K“¦ râ°ðÒ‹#O0þò••.ãœÂNB ¬u…õ u~¬ç…J–ŸñÀ'x§âe Ä–õ¬†•ƒ›€—i æO³U¹€°Ç æ¥D³<–2a´vû†p ¨jé „,ú¼>]YýEµ¨ .Ÿ?MnÞ‚Rã–½g9™?=ýz{jýºRñŠÜÊkûÚ€œejâÝØ–‚ŧjn6,ï@¥ã´HN\µ…“h°þòK·eõ¼Soªõ7 ®îÇÏÃû»»GÊ —_ªõ"|ñG{ô`{Í8„hÅÒæVÿêt:»›ÖáUSýŸÚ‡>GÃ1N°Û^ßUÆé`P÷ÚÿïÍmŒ*Aoõza·ßÁų‹;iŸ…}œš¹Hi`­Ø:zb¶Ü ]”´ªA¯yÉÖ˜º9ð•»ÛíuÃÞE¿\­6¿ºÎ~;ûˆXX©endstream endobj 1275 0 obj<>/XObject<<>>>>>>endobj 1276 0 obj<>stream xVmoâFþίª–H`H€œÔ@“j¸KQ¿ E‹½~é­w¹];ÿ¾3»61NÚ»Dì—gæ™—ýÖ@€ÿ˜ a4†0ï^€oNw©7‚Áhâ !‡«À»¬¬;óM[^'àߢÈ61¾šŽ½ëël"k3€MØ}a:c;ÁÍÅæŸŽ;®„»ð´¾y^=,ÝëÚF·×C‘çûå¼%LFïI.?/îŸþ¸iKÒ,LÁ¤ªìU& ()¡BVdJ‚Ší3º‘í4ÓÇÚGýÁ󱉺™ Eqˆ2ÍÃBéŒïÆ•wýáó†âpóå{Á?¶ñ”&“ „&õ ü€m—SæÜ{İø™4‚Gø üÒhŸ° @¬Ëù JE¾½pö‘YKDÿПá—/uÒáHÀ¬L-dSþeÑpêç{Làu–sø¥Rö1‰¾6ã ü0}6iÎóéWä½Ú¨Òí¢«KQ×ùyp7ƒCV¤°YÜ­Öwa£`WfÈ28–ı)ѳ,—†kÈKS@ÄãLrûó“i%s.1ïP×m 4:y^|Y­Ö¿;-ú‘€S±|VÅ[æáï”Kh[éAξr0¥F( J‰tµ1HÜv¹—Xý¦_«Ý {{ae0o£koLu9‹ Œ?L•-CjƒPÉ8KJmë sTg‘Î0“UGàÁqÏu± ‡¯SçduAi8Wè&oÅúd¥fué:ÑјÌJrcâRˆ0(8Ò¶×*Ñ,o›¦3ï{Â!v¤æ¬pýÔ†$ÔŽ ŸÔ±1Ûúu·=xä1×tÞè5Žš K)©¯1a>óÚ5Uʨ.%rŠY”•‘:èSÝ2 l¿Y5–vÌ b4B,Ьz¿Ÿü_=ˆ‘î†‘ë€†ß H–[âQ!ÎV’PÅ Û¹ÙvßL‚·H~Å-ùíÅÏÔU}Cã[G¶’Þ>«§Á1‰ ‰:â!T8ÍþŽK M×ÛÑ&¾ë Ÿª•<;ÿ^×]—l5Ú6eÜ*]hbPF°89ª¨®—ˆs˰ÕÑƒÉ æÂ(w‚鎪ÔÍ’ð`CE”³£«é¬ÀZp ¹Rû¯CüOÏè#²NR&7þp²R ¸Lp VûW%O0WÃ]ƒÍ_’ºn—x®cZÈëÝÏf‹??R¹’ª;ҪľâXù N2@PÇ3{a™ ;BÏEAº¦ã4Ü[QЊ¦Ÿ,y0çXàÜ©¸tUnç<ÚÒü[‰+Û†üþÄ?M×Ö=¥ÑtaY/f.ÂV]cµ#¢ö…â„~……òª1Š—*ºzÁÀÉfk—œoñŸ»J8XÓê"0˜ ¼ëé®&7 ¦4úï윀™Öìhà‰öàŠÉ’ Òì&ïú“àš¤‡xœÓ:uI%™ËÉ¥7Oñ¶‡çƒ½ºÙtþêü æ£endstream endobj 1277 0 obj<>/XObject<<>>>>>>endobj 1278 0 obj<>stream xmVÛrâ8}ÏWôÛ000!ÉÖ<$ÈR›ÛNÈf¶Šª”°EÐF–<’ áï÷´lCÆC¨€-õítŸnéçAŸzøôixDÇ'”d—Óƒ^ÔÃâö˽Pw ™>MýrzÓ4 2=š&- ^Ò¼ÑãÃèù!¾¸¾¸¿ˆÿ¢Íçé=êô£#èü*ºûÔ¹îfÞ%ÝÜÙÿº>Zä"yí^O.»ÎŸôz½½vJÇüÝÑ[%<çs-<~ k5ÿâ5ñãá0QFõû§½&? ¸¹Ÿ”ñwǃ-þÎÑI4`$Ê$’Š¥t’þé ¡ )SH—Ê\šTšDIOsY¬¥4¢ aÒò©Ž§MÞf’üÆ2£L½, ròçJ9ÙˆÐç2Q •ˆBYCvQ#¡b­8K õÎ!ÑÊ8é­~“)ÉwÄc„†ƒlÎéX8›±—¤ÕÜG•“×ÄøBh­Ì ]]P|xH—ʤxõçt¹¡T.ÄJízs^m†[Ð|¥taš àù•¬ÑR ¬>ÇÏñÝ÷QaXÄ‹Â-¬ÖvíÏB\çª<[;{ –¤ìèÏ»›¦¶‰WKKaþ(Ÿg­éþ×Jë-+ã­XRL‰åê¢bœ†Êy™}næìÑ—éªÀÃÂ'_23¬À…Êr-3 ‚¤´ *cë 'Lé"¢§%h²ÃàmýF.jÅÅÊ$Ì¡Uª1ÌAw¸˜o¸ÌÊ+¦e.\á™'R«LQÓRx¯%â}+)gê!UnÎCBªUya~ ÊÛ.@ź ‹õt%Ç›åJKçiÖÒêU¶áÇÿ’킘N84OÃsºrS•xåçÑK”„n念s`­Ëµsæ—v¥‘¹å>y–[ˆLé su?+S+=SÚ¯ò…ª“²ÏŬ%£—¨,ê©æ¢—XúRåêö±±|x¸#ÓÇH:Ž¿*¤® IBñ__ïYëCP¨óǾâ–gfŸ‰Û2ŸQ•ä ¬6ãÙ¡N _¦¥Õîøì÷ixñ€B‡ qévÊî÷ŽÍ©å& =ºìÚtó@÷v-]M:0w¾búGùÚ¨"#}áuðKºv(6+‰8è4 Ér’í·!?VÎc*/“¾Œ„чÑöÎqå¬áQEoèC˜{6t BÀ€ÞJ9ãÄØÜZy5vMk>QbÈ—YæÓG¨^¥hD –ò‰T6~D`JjQåqBJ-“‚«Ãæºã“Š-íB-·ê£ºE…˜ã LQjž±5…³šî…‘ÀŒ%Ó‘³ä1j’[è\ÞÑvwö"þvèmù.x®‡ç'F^§Ê¿Ò·p€í.O¸amoì ²Ï“Ûøúñۈ뚞ÏfO˜³Ù¤L ‡×ùpwªup)úUÐpØW©9­RÓô£Áé1}ö¢ÓÞ)_Y®´£.œO™¹­<õ‡Ã ÐöÎX-M—Ì;ž²W,3¢áÉ)®Øïxi4=øûàCz3#endstream endobj 1279 0 obj<>/XObject<<>>>>>>endobj 1280 0 obj<>stream x…V]oÛ8|ϯX÷ ¶lçÃvú–~$-pÉõ®J‹üBK”Í‹Dª¤d[ÿþf)É–ÜEÁ‘–ÃÝÙá,^ÌhŠ¿-.éjNq~ñ>º˜†S¼<>ì†&÷×4›Q”"öf9oo¯(J|Ì”¢8x/’\e%•[Qâ!)©µÈUL™Ò/x¬­°J:²òg¥¬Lh]û°BØRÅU&,©¼Èd.u)Je4™”¿~!ÐØ KJÃKÞFÿ]Li<» /‘C ŠÂšÂ*QJÊLܬMõè®v¥Ìé㟺îñò GŒWÓwþ&YƸ«Õ^i]†-øå<¼fð/ÚÔ¦ YÇ¥±5%fÄa“{ðæ9qãn…ÎAEó¹£lÜÿš‡;*¥+Cy„ä7Vä MkIëJe%©fÛá.ÁfµÚdf-²ÕŠ—+½îœr|7\ÚOÀ'x܈0ȵ‹$QÜ‹y>ýþH…ˆ_ÄFRjMNϾ}D£Ri@eJ—Ò¦"–Ô•ñMk«´,)Î*4Ä’Ô;eæfŸSˆØTÒ±-üP:1{G·7“ÛeHŸÍ^î¤QRAq^ôñ¯oä¶íÌT® ¹Ñ±}dôq{R•«h¯°P›’öƾ„„.Y ÉIo[ýÖ´¨Ópj¹H x ùBxY͵ªÒ‘ÙëSApt2šsãÐa£wR+4–üÉdö”'­¨JªMe;@¿’åbÎha÷hòêX°™4ÊŸüNø¡_yò®i8Ÿ ,às'Êzxzö4²ÒTÈVÜ„}#;f œ!ZÆUÉý¡½*·¯$ù&<¼é üqº¦æÀ^iáP/NsÔèühª>ENŒþ5,Å‹o+v^ϹITZŸ: –†„ôDô;j`ÇèbHÞþäA°ÉŽH¥Ã^ Eƒö· òÚ—7¯;c ¸Ô×#ïÏ#!B~ÕKZ "c£P­ã!գˎ8kΊm¬ápÄV²Óû#A¯§r8O¥ÕcYòÝ/kZ÷îåÙÖþ ̹qµÕì6oe@¯ „€3`³›°2›Vÿ>Êrì oôØ•ßj‚GÏΓˆ¶€><³Þ)S9¶§fÃT §XÑð6ÂÌ…·~Îþ þ[P^e¥‚Rú 0œ¤ë†yH_¼.{d5 ÂÉAë`!;¿øÑŽê.¬ýpq~Pxgè¬Í¯þÐBã·ŽkýQrÍ ?¹¢Ò,Àïr«Gç}ÝLÚ˜vtŸ$¼g«÷b+ªu† Ë`£Óíƒû:îUÚ2*Š¥ƒñ§'}¸kD0[¶*/Â区Ëêr¾hóëÒðßÞ‹±‹{&‚ÔÀfÕ7xG;ÄuªñÃPÐ*xÖê°zKí|k¢;ì!­6Å-ü-=$þÜ+߀Ù9Çg› –LþXÑÝ?Ÿ¢ÕÛó%œk圃?Ü(Æ…p>/XObject<<>>>>>>endobj 1282 0 obj<>stream x­•MoâH†ïüŠº#Aƒù2ì-3JPVÊnvB´.݆žØÝžn; ÿ~ßjã „‘v“H㮯§ÞªþÞ‹i„ÿ˜’1Mæ”–½‘á—_W½É\Œh2[ⳤd!p2<ôؾ“÷wí¿û¼þèÌíhx;¥8¦uÎáf‹¹X.'´ÎBÄ­Ó(·Ea_)µe¥ Ykk(/äÎSnÕ{E«kª•¯©rvçdéIZýõDÒdô¯6™}õTÊg•ëBù«õ·Þð†ƒ â XgÑn˜Z“ëݰ;+öíá.¿HÐáœB*¥2™ÊHV*Ó=iOµEà´h2E?qÅIlãØåiÔÁÚXñ¢Ë,‹˜Æs1åÜ6ãyBçÙ Æ /i0‹y¬¥«µÙÑ«®÷Ìd"â>Y£@®)2òº¬ ¨Ðæ™ÏÙœÓ/t˜zÚŽùãeª£tIº2Õ§¨@Ã_  ðϽð…mY]ZÖ{YS†n!uY¡ÅF¥Ê{éìò$ ‚€ Jò•Ju®ST·uÒi…~°é¸aßíÐ-T¸ºmVKjûžCìŒrÎ Ç ýµ1†ñ€àÃQP­E'>?‹Ð—kï›R…? ´âíÜ0¢½„d §dv ­R†¶.jÈÊ’k éºÏ˜ŽümèþáÁ÷É7P™ôôÅÉ­'7h­£»ûÏôøÀ6èÓ’E±:Áã`²l«zòÉB­œ–µ"ðµ*y Jè¶¥whQ7åV9V,˜ºu_XN§9O:¼ ÃvÄ9?ʶ…R¿y“Pœúã#Äc'<Ƙ‡“(ü»~/±ñªµî”8³w6Ä% ƒ·¡)ù‘ù Xt$@Óï!‰ŒJUZw8¢ð¡²MdTýjÝ3jß\óõq66Q;Ú, ÌOóFtPt›«3úãKúwù/ËK$‚¶Ò#GุëDÜy Ù½B „4²çøøoo & ‰<®îN‰ôé¢5¼Ô:ՆΠ5ºmMõóÞD¬áóþÂG·Ž[¡¥è.\œëè$·Ç.ù­¿¬îW謭ߗ[Ÿn›oºöM¿•ÚŸËQ?(àxYô1›©DLû‚ M>IÎX2Æ æÀº„r:†ºêó@©7•6µ“IaŠOF¿bôC¯Ü‹âãEí÷aSÑž„c7ç ¢O•tØŽªøtÎ%ê@›¨.ž:Ý•~7ÔUú2£Í• ëâ jËXÓ »Ë8ˆoKJ9DÖFèn¦x‹ébB³d"¦Óp÷¬ »•];,'OO(Šî¥id¸ÅI Éhù?[všLE2_WÙœÍoÖ½zÿª>†ðendstream endobj 1283 0 obj<>/XObject<<>>>>>>endobj 1284 0 obj<>stream xmTÁrÚ0¼ó{ƒÌÄŠÁ†:“v¦‡´iâN/¾#°Yr%yˆÿ¾O§-S`<ãçÕÛ}»Ïüš$ˆé› Ÿ#ÍP5“˜ÅTy¿m_6×8Ö’âl×.FHÀhÕcÛIZˆabU²â^}aÞ–;ÊÉéÓg†çN–ÿ…y,o†ºãÀ‘÷à!¼&ˆ GG%ô`ì5~Pí‚)hÓâ´•´›CKÖ†=bÑŽ¾¥ìœgvÞ|z%yr²éþ7­º4>¢w(žoËRû²<ÐO™-Weé…óR>ÀyNFÞl*ŒvìÊj$Ës¶X¥XÌ—,‹W!™ÍÐwÖòÞá» “sÝquR~>åñ: ç”>Ù©‰ ›;<\t»È,ÏVôWB¸$¥ûbòmò?éEendstream endobj 1285 0 obj<>/XObject<<>>>>>>endobj 1286 0 obj<>stream x¥V]Sã6}ϯ¸0ƒ I }K· ݦ´¤Óf:²¥$[ÊJr‚÷×÷\Ɇàm§2Ìpäûqî9çêëhLøÓ|BÓ•õèÇÕè"¿ÀÃ×·¡ó›k_Ój³WóE~½¸¢•Œg.hUžLsúbtТÒßDÐÖ0’VÊÕÚÄÿOWO£ó›KSl²@ü ‚œÜXG[íƒuº9%¼5žOÂVÑ$“ºVÆ#$¾Ó&(·¥¢ƒð$Õ^Uv§$­µóáñôŒjaZÂ#“zÚŠ½¢p°ooú3.傲ñ4é­Q´F |ê8—pN´>öÁ…X|¸xP¸B'\ûîøã e,y”e²ã/Ïøq¡‰k뉲lP¬z<¥”0§²U" S*…A—k hUÕ¢#«ÔÏßUêé ª*ðÍng]6R1ÈÚBg.Vt~3ë¦rQÚ(÷~X'±£cÒ6ÊØ9UjÌð8c–ž½EîÞ)m½«ÔËàœn+[`¾Ë„ùÎÙ½–Š>EôÁÀmŽFˆ? ãà(Gƒô¢V`M­_”Ì*a6Ø0®ˆ¶q¢æð 㱈5ÙâI•Á'Ä#•«Zm62Œ µM í©8‚ÓüzWÍ wi †ÎÄÏ»o&³ü’Ù}»ì ð ö×Fcèµòµe;á=çc­Ü)ð¡¥eUÙR@ èän‰ê+]€kóBý랇`¡@Gp…z I3}ÓÃŒ¯BB´eBúŤE&cá?.Tį %ÇòÖÎH–CÒ˜Ù €ð­ª¦Œß'ÙQ³¨¸'ß!0†½[æô”­ùMåK§ p¹¤ BWžìº+c³ Í®Ý@6‘ÀÅcºýH¢ƒEɳ‡±ýÌx<Ó| èã8è>ƒ£¿·ªY¾ˆ³\¡˜h5´WŽ™Ï%½§.û’S< ”AãëëKH£ÈÙ¢ñîî¿fÀ×BçS…*™½bFÄÕ³¨7©e0Ã33!’ÐtØÒêÓíÝÃí‰ïؕԦìv(šaEÑ„­u쵿ÙB¹0ögÈDÃŒA퀓¼úmjˆ=Ÿ¼:£În¢?r ƒRÉœ(Öí£ᮅTÀ ŸÄÀÔ›¸@|`"hSV Ø+N=v’hc²B”Ïá$±³ÌBW:´´kÜÎzå´Åè6^‘Žrö53ÜkÉ@aÉ Òk,\ö!´BØcï}nßx'mäjØÎszkñÉËí*㎳¬ïŽéµäi À*~øü'¬Ì^;k˜B´‡?ÄHÏ8`Ài <ȉÚЦ­žuÈéCI¯ØØnœ×HæCoÀq”Ì®èQó\ñ?ù2ö\ é;H,ä^€îLÁD”S…0º&z`šmÏûÞ=W‰§]Åd”’‰ k :pëÆD•ãêÁG'áKÀÐñz€®Áõ‚Eó׈[OÖ§ãE;¸ÃÀOú; È”° pm`)=Üßýùp  =­š¬nª ¡ðŒ—ïÛ êéxÑ-Ülž/øþzœÌæÄå¼9Oüòõšâ[Sn1ñî~õ‘P=bŒ$<Å©Ð8C¦©aï{dñ°}3Î]ç¤åGrÎ YBl â”lâÌû5'æ+³×—¨ùîÂÁˆ×(éðÿÂ_¼mB¸”Då‚L&Â#€R=Ù"R€ÁG”sÖEfŸßüKîWf`?ᾯ&ùôrš6Õ¸b_Î/óùl‘85^p¿ŸW£_G¨SÐfendstream endobj 1287 0 obj<>/XObject<<>>>>>>endobj 1288 0 obj<>stream xuWMo7½ûWÌQ¬µd+–Ü›Z$nÓFA{0P»”ÄfInI®å×÷ ¹+íÒ ‚|IäÌ›™÷ÞÐÿ]Íi†_sZÞÒÝ=•úê×ÍÕ¬˜áÃónO7ï4ŸÓf‡³oW÷ÅÃÃmªxfF›r²9HÒÒ{±—ÓFx¯ÌžjµuÂè <K[Iʨ D­¾Ë ÿÝY')àâ‡õù¬0é´2"àØá?ùåßlþ½šÑt~WÜÅär¡è¾¹½/ü "7ξ¨JÂÑÒ®5ePÖxâƒ7ïQx,j²_Œ±8–>ï‹0žÅIUeGÃAr2´ÎDĦÕ[€·;†P¢7ÀËCÅ¥¨k´ˆ# êèΑªÐ)Ô‡s²æ{'tAŸ‚p{{Tá€dµ^Ò]1»æ”øç¥À/´$zuËЉƒT.˯´­A_!ø‚ Iáj…zº¤ž C{­¡²n=ŽÆOŽÖ}õAÄGL¤EÓ0â­ G) îd™¹'yöK'<%hb¬ N½€<L*¶Dñ>eùÁjåÞé«IõM¢ç‰±Ç ŽÝz[Ë Ÿßнj=hö‚¥Sà0V”¡u_ZNºu¶Ý^U•¡0»Û%^+ähh+œãæ>OöµÝ"´?™òà¬Qßc/¦Íõ*Dh¤‹_eð+‹ðÆ€W 1*­nPXÏ#cø¶ ˜‘©x,!èÖ¨2†»ÆØ7IœÁÚú+¢EÔ`§¼hg@`¨ˆ§ïÏ:CErH¤¨W¦Æ2¾¬Ôë(d‹¶è¼r‚DÏJKcì|áš².džD#ê"ãÜnÞ?Pò¶¶éíª¸gã`gy’Ú¾ÖumSoÆuñéûbÕû ˆ‚Ö@fwj¥Uô/Í2bÌQ,ûCæ€(I§  —Êü&¦ŸPøÜb¹(–÷+ì~lýùônsõçÕÿ{©_žendstream endobj 1289 0 obj<>/XObject<<>>>>>>endobj 1290 0 obj<>stream x¥WMoÛ8½çWÌÑb5N²±ÓËÂY4Ý t·Þ[€€–(™[‰TI*®÷×ï’’mÕí¥h‘66?fÞ¼÷føõbF×ø3£ù ÝÞSÞ\<®/®³k|8ü°½{º£ÙŒÖ%Öþ¶¸Ïni]„5×´Î'˺&S’ßJ*öZ4*u½'ü0¹ð² þ·¦F6ÆîIiú¸¤Ü4ÒQiMCÊ;þµZMµÚXa÷WÎ[…=—ë/®i:»Ínp1ßÇK/“Õòå²ß“ÑjIÃµŽ„.¨ZT¸é$„Î)]ñ¡ïžîSfr^ä_â‡}º>b¼n+E;^V(—«¶VZºŒ–zÏß¼éÊRÚ!²"DVÈ@ Å¨˜VZ῞´”…#/›Ö0&”Îq­Èå1‚ŒüjyèÑõç2V±Þ scQ:᧪ÍÕÖŠ}(LªÓéñ“30ü·À…â,pL)…ï,Žèœ,;üŽªµÖTV4# ¹éª …!×å[î=/-§7÷Ù×½–¥ÐYUm=U°…{Ï<|Èy6-ðbè éeîI•6Ïɪ‘ÚÓš7iwVy/5|1ŠF´('H†,®bæ³E¢Êtž-fÔ“ñåæ~NqEO›ðý5 œÆà®Ì E“ÚßšŽëÈÍy{å¼Ê!(ÀËl@ž ˆ$¤³³üè¦éÍ"›=`ÿð¡PžKÈ:‡È"dtäj’Ø‚ µÑS:Ê.PËyÛå‘ Ê‘6Lï\:‰íGøK›Î"»"£G™ ¾c'þWjbL°æãò ·sal$x ŠZý‡µ Td€¥TÖ8G lj£›Uf´Ž´8v‰Õò•Ï>-ø„ÊNçA·V~í€|Ó†h\+sUî{ –¸ùà©Ä+Bw¯x™¼\Æ/ûYd’"öZ‰¬Ñ D–Šl១h£˜f`áZ¡§‡NÔx¤ÇCÉÉQʰØ~i žÝÖP>ÃE†äeYbÎà-Î5RcgûPKïûy­ý5¤À1IõtU‡)ˆ‰Á¯…OÏä…û‚7ÏÖl4?ùkK|0á©S[ä–9ªO#ÎÍ a¼ 1Ä )Îö#'9~¤(M`I£ˆ^&`Z/q4`KÓÕàák+|¾±t¡~<9fWQÉ&Œë‡—à„ñ6˜£1&¬Ym'Ï“`Á5\ÛReLzEõ#ôl>Ïî·xMÞ᡹à‘÷cÊ–ñÙñþ•ИA8Ài¿a:¿~àÕü\ÅÌrxåÝü.›ß/ðRŪ›kþèÃú⯋ÿøc¬-endstream endobj 1291 0 obj<>/XObject<<>>>>/Annots 170 0 R>>endobj 1292 0 obj<>stream x½VmoÛ6þî_q&V-[ñK€}H‹µ°Ûê~Z†€–h™«Dj$×ýõ{Ž”mÅÃÖ{I€É{îÞ=w¿Rã7¥ù„¦3ÊëÁ8ãËñÏÏïéuš,(›eÉŒjšLçøW}¤é$™P†3vÓå"w«Þît3¸;fYX…Ý`yº¸w'@S\îNçlw'NÅUo÷z‰;ËÓ°:íNæKx~Ø+Þ}³:Ó–ôú]FiJ« ø¸^Ì’årJ«"P1¦U>l¤Ý[ ˇ Z·ž”§ÆšµXW{r[ÓViãi-©’Î‘ß MÙý›„V[I΋ü3¹Fä’”#£q©u² ÝVjôþ– „2úbõÛ`L£tŠÀWŧ噷^é’„.߬ô­Õ0à ÝßF3ØÉMÝTÒK—°™×ï–ípX£ÉüÃä4™2àV^‰J}¾ƒí“0šÌ’EÆÇÙ¯ÔÚ »gpu¸ |±ñÒ"‚A‹RŽá»z8Î.¯%¸“ð3¡»à}cph]Iöþd (gÁG㈯#ØÌ±•ˆ³–ºÅ »V·ÎÓFYüý'ƒtØZill˜<-eáðbÂÂ{cáÕh-kƒ8á2žÍX¦PÎ['8`kÅž áExZÄKäw†D$ 6Ÿ$mZ3­H„³o\2á:ËP|\l!Ó.SN[¦?°ÿÎXoEÌŠc]† —` díȵkk8O$•âñÄÂÃðáâ[˜£^ƽ‡Ÿ Á9…Ÿ'£ ðÿxẇ—`õ¡./_ÖAÝÜô±”ö ¿Ì¯(ÇsÒ«WX<Å`O©^þíp˜Í³d>[`„ÍIÊ—¿_ ~ü¼-Ÿ^endstream endobj 1293 0 obj<>/XObject<<>>>>/Annots 183 0 R>>endobj 1294 0 obj<>stream xVÛnã6}÷WLßÄV,ß I6m\ ŒLÙlEÑKQÙõ~}ϲ,;Ù4ÛPBq®gÎÌès+¦~cšôi0¦D·zQoêÇï÷­xGS ÇÑ4õ'“¨_2zjÅqŸÏƒž¸L¢qujÜöa·ƒ¸Ç²þäo½åx2¬tcH…ÓQ7ó[Mñ,†ÿpjÜ'ˆo§¬ëO|{»8OÄnèj1-RNu4G³Ù€kŸmI[8R¹rJdê›pÊää”–ô¼§Dd™Ê7t±ø»u5‡'o¦½«ZA®2·÷7í˯º‡‡u;޼ »1#†W"uÒžûm¸t[Ii™'ω­~°H¹`CÅUóyÆU€Ý|tû\9xšë¬ðFŽöÿy¥:+¢¢|¶¦t*—„ éVZjc÷«Liå–íðç‚Ãy×}ï˜æ/Aø„\çnÉÿ¼µ¦û›ÕÓ+·…ú&WŽ>è½éüòòƒî+ç××÷7OÒ¾¨D××àÑçÿ)¤ÿ:Ž#µºñ Œ¬1¥ ’ybJ+6rMn –qMËeO¶ÆÒŸSe GfÇuî|‘`ßÖ”›mCZä JàgPº¿¡ÜX vîIäž~´·WÅ ¸Q&À·ˆá¸š*ž@ç@’»¯Bï2îk"_ÓÃic˜”oØ%Á ˆ²AŨ駤Â|©]p31¯Ž­³l/™Ž|Q)-¹¿S±Ò‚%–œMhtpÝA6`­®,ègÒÁö²ý¸Xýzû©Ã7É?ÚJ±»ÜdæYd¯ü±™>ñ„ß·­Ýjýœu‚i¶ü¦IÖ?ä ¤Îú2—LZ‡¢ë ðÛ¿wœ³'îi²2_«ô÷ v¢Ü¸¨‚€EQD?}A‡j‘´Ö€uü|ÛJ{Vê.#{ ÃsùHÑ!ZË{G½y:‚Æ/"+%YéJ›ƒÞ©5ú)Ù¨â³:„褂)‚‰•"ôC&­¯Ý™*1vg0»)µÌmOhSâºVäL‘™„µb_`D3‡½´Ÿ,Èv—©D9ô”Ûbn¶ÁñË z¼9CÍŽ#¡ÑŸàm£C£e°B€µA;Ê%OSÅàC@(¾9=4‚´©H$àÈñ„A™#KL.R®8äí-àÀþ‹ØGhgz6n n¢¿:>y-¾*]ê#dgÐVŠäs©ì¡Ô„ÙI‹u NûÂIÍ/»dª*x’ëŠúÆ ïê1ã§ôÕ¼–á¶ú‘PË3¡j¼Í(,ÙË·ÛŸâË[pÀËPZ­r¿âO5YnMýº\ µ0\ qY´W­ù5+|º¸ô]Îp4—õ{ÛñtQüßå\ÇTÏÐ÷¾ôùñ…|@M~ÄUÓÓoßW®®æÓjÄ#|bN4Üá”KzïG:Ý„~þƒë£ÈK‘qq»1¾X‡PèNz³ï`ˆOÐÉx¾Ñú}V»[´~ký ¾bendstream endobj 1295 0 obj<>/XObject<<>>>>/Annots 206 0 R>>endobj 1296 0 obj<>stream xÕVK“Ú8¾ó+úH&à±ä'ÜÈT öTeÞBжïúÁÊ&Éüût·0x!$[Im†—[R¿¿þ¬{\ü ˆ$x!$EÏu\\9>þœõD œ?t|(@º¾¤Þö„Ž„ÀóðY€Ç=H]‰I7pÉK´+E„²/È>îÆÞQ:ízq`ýFz°[ö$Úô‚[£ø(utňu½($]–N»Òw9fOzxÊJ]×Å< Ô 1*ÉÒɯð)rŒÙ‹ð”•NºÂ£¡.ûµí¾˜Ÿ—×là~êƒ0_c‚8tF#æ)·À…yÒŸo5ìLµ1ª(´D• òºµªL ̾,³rÓžue@RÅ.× jØ)›ÿÝsa(¨…ó´ŸêF'NAƒ¶’ªL³&«JX=¢÷œ4Ôz_&´ÎêŽ$Ôø%‚§%iaÙcJ‚ZŸbÔF”ž@•Ð×¢PïW¦Ú7Y©a£–Ì¢_èºV=À°R½xö-ßÃN^öðUŸÀª,…Ùdù’ °è'[eàîè9+›½w?~£ûƒóñx6y«Í‡,Ñõx|Èþj$÷ÓØ®±™¶è1â‹î9< '£kÂQ~Ô¤˜»„ 3üo¶FkøHM ÑPê3cÎêÔ^áÀÔTÔ‰QM²0Þìs¬ašÕÉVØÓªÀ¾Æh:E ¼s<å0µ çhctá¸|¾òjCø?âJ„™MpÑow2D~1À°‹z`ó]–ªÐŠaˆ|(OCcsñ™l÷å?Ø,U‹W†à•tÛI”?5Ý/²Å<g+ÿk¶h†³FC´Ï›v?}JmÚ·*Õ›Æñ5+ã[/ú$ÛîÑ›í ½Qß½§À”áÚiÞñXœúۙ݋]µ‹d˜3}÷¾mîÍYÞÌäj6ABÈ«•Ê'4¾wv¡Ã¸³ÉY.dÊ%á1º ¾çµƒn¥¸úO pÎ'L'ø¡ÏÒoÇËÌ`ÌØàü~¾­Bí|Vy•ü¯‰ãiy:ôñÓʃ…An±•Á‘b¬(™–;cû‹™§-ÍËþáÁ»r[ê$ððËâ~·—µ/´±A„—b?¦/ª¥`Þ©á¯/¡¯T¹W9wYà½ÜG…a䎾qÕð#߉Âï¹hUz¤þrÞ{Óû ïc*Ûendstream endobj 1297 0 obj<>/XObject<>>>/Annots 215 0 R>>endobj 1298 0 obj<>stream xUM“Ú8½ó+ºrb66_s#“…â°‡$lí!¤¦„F‰-9’< ûë÷IÆ P©ÉÌTQeµ¤~ýÞëÖNLþcš )SVv"aåüóiÙ‰g1KiÙ”JN"6>}ô¹G#|§³¿%ÅÓ»š¯‹è4 íÙ4|…h¸9¥,B4IgþløòÑ”&CÜ&1Tì‹§36ñ@«QL4}ì¼_ߢ69 ØÓzó£é˜Íf ­w¡²ˆÖYWídÙ»[ëDÔ6D°Kú“ÊÎÙ/_{”¸¡·Ü~|T¼=òÑ’WÞÜëÛBgß¿|ÝÜùØ`‘žÁõ‡cƒìCFï¹;ÒŠ89QVw‚6]®Hü’ÖI•SÈ·¹£ŸÒÈík•9©UHɆ^»•{ ñ)QìÉ=çZhã ?ºüŠ:0Rè\f¼8硜?îêªÀ¢›nþÈ{”?n{ ²ÀDScô˜þé òA4ºx—óÇÏÙýJ@p+ÆŸ!\"x÷î•÷÷Ëùgažd&ìýý/À¡>iã³ô!ZËõ_§[»ýáÕP—sÀ,ô–soÑ·`îwf\Î7Ý+Ø¿Áxaòì›0ú$öÂЛóÙ¬¥¯VoÈŠàS ~vâJÁÆÒRë÷‚£•øÙ˜ uF:K¼(B¨2ºÆIaÉÖÙ¸E;Zgä¶ö=УwÜ+A\í|§ e±niotéo8%oé¼n«[hÿ¶=fD^èöë\¾û(7¼,Qv†6µ•ÈäþØ4;YùúSRZ ÈPÇgPŒVû‹7ÈÀˆÒît¡ÄPðÙ ¹5ÜÑüà£Ñ“â¶qGÜùéÞœEVæžcASŠT]nVïIü„]7É·( þ¶€½éú",˜•æß\ÅUæ%®jSi+ìæŽÑ9xæjL ^äÊJªýDC5Ûv´Dmü0ËlËw<š°tìGà™qi^Åy÷·Ã •†¥!:P‹'ÂÎN öåãÓdìú¡Ý,]LåÖ"aàÂyíÉjŠluh$€=1°}“=Oe´Þ´W°‚6 ‚Ó7NX‰s`yWgzÜhpyòTŠWå\ £yëŽ ÏÇk!<Ϲ| ßé,À†rZº‹iû LÀú4¡Ñ/Ãxêéoæ…aé ŸüÍôôØúñé@Íüî„è7­î÷¦“”Mp'^Z¼k©_úkÝùØùŒuUendstream endobj 1299 0 obj<>/XObject<>>>/Annots 220 0 R>>endobj 1300 0 obj<>stream x•VMsÛ6½ëWìQîH´(Ê’Ý›ëÖ®M'zŠ3„H$$À dÿû¾IYâd:­=#‘øzoß¾]èû$¦þcÚ,)YSVO¾ÓjuÝvÃIaA´ ÕzÝñôõóMB¿ú8Á0¶ž>þzš,ã 6Æw ,­)Ixc÷VѧIœ,ùý&ŽÌò7Ö†·0{ö^Ór³ˆÖg³¿lÇp¶ ëÇÅ1m÷àºZ-£M²¤m(-h›MŸ÷$4 kÅeBkã)•”Y)¼Ìg´7–䫨›JRÞJò†jYûF®4Ö‹B:Â!5Öf2æ3£][7^}µý:YÐ鄸~ÄÏ»xƒ{â6¡›Û$Z¬o¹—?…»|袳×þÝ‘5™æ›Å¯Fï}ï¼ÔÑœžyíj³Š68?€òéò†‡~ÛN>Nþl†endstream endobj 1301 0 obj<>/XObject<>>>/Annots 229 0 R>>endobj 1302 0 obj<>stream xÕVÉrÛ8½ë+úhO$š›$Ê7«|˜©Jœ┠! ÐhÅóõÓ jadWâ¤r‰,±6zy¯š¾Eâ_ó’ðf!>9\Þ-GQ!L³^H’Y0Û­jx?Š£yÁ4F²Îdõ+²ö¾i)Z£i„{û•·Öè…A<´ú¼i<Ljû¼ýŠ|ï!Z$Õ—G˜*ˆ’3 Œ‹Ûé^kx;ú;GL1™S´0SÂÅMQù£Pùn±H /<ôr~výêÕyþù”ï‹q¼ï>R9X^]^.¯Þ ó ¹°——ÜæÄòjµ¬´uöîŒ6¹ÇVŒîT!›1¥˜ø&Q‚ðóÂïB“ýøÉ[C8XÙ¾{C±·²p^11cØãJ±¦OíqÃòªS_>~º;ïñþ€«I¢žÓbJ‚†mC-Rå=>; ±Üz2NÒ÷>/XObject<>>>>>endobj 1304 0 obj<>stream x…VËnã6Ýû+.f6ÀVüŠÝeÚI‘E êYh‘¶XÈ¢†¤ìº_ßsIʲ5éAlÃ&ïãÜsÎÕ·Á„Æø›ÐrJ³å‡Á7šÎ§Yúz±Ì4ÎÆ4ÏñŠŸïŸ–ô‹¡¯ƒÏ›~ÁíË‹ÝÓýÓœ&ÚìôaµÈÖëmd83¦M~÷d, Ú—f+JÖŠ3´/h_ç)Weé†ä ERxAR;oõ¶ñÚT”‹Š¶ŠŽÚ5¢Ôÿ(IÂÑΔ¥9¹Ÿ>mþŒi4Yϲõê)ネ¼ Úš\9‡¤…)¥£×éxŒz[*|œ„,CʵÊÕ¦’ºÚ“7]…*%!·º fv„bŒÅyaÏ©¶„Y6åü¡¹!ÎɵkðG‰+¥÷ÅÖX®£6ºòŽtʹÌÂÓÉ4\\,B—g€Ó+¡­ÞPÛaá2ÚÚEˆªM À)² rH_*^–ŒFµoñ÷!IårL'›J"("÷’昈ʽ>*2µ²‚gçÈñWÈñt‡»ÒäÍAU>Éèwã~Dg|¢Ã+Q¡©K£FÄ2t,ì%OáypiÜí…¶‚k<EBŠÙ£îâ¸pS †áÚ ~ýüÝ4À( ´˜=}ü)xS…±‘U ¹:2ªŒ=J‘š»ƒFx<0C"g*pR½ä·°%¤ Ä…Oá23Æz CDM§ÓMàý‘F¶È&gºZÓ$›Å=š®` àù o?[…!rá¬hG#z~ŽW:7MÙjÎ7ñØÑ_$×][uÔ¦éˆòz÷á¨^?{°þgtâtàhÎð—œ?ñ;q®‡RÛ#á #éãìŸU.°D(]q‚ ‹3†%Sjæë5—\ò©^ÖÜT;½o¬’°¨ÁsäJ 8¯ìN`´ð2û`$oc°º­R0ÁЧL^.“kjV„£ ÙwDÓ'5FÿPŒb û!ŒU~À( ‰™Þ^^^"Q·£ šß¹rSó“ ®ØÜë«õ®±}O´Âœë!iÏŽ‹Êt®ë°ŸÚýÐKoE}û Òp4­7Ü…O@>Žê¤ÁfõwÍ A ì×ÍžÛ»kÀ°áqþ‡²®bg¨¨R¤‹øÅdè42¼Âx'ð0Ã\'ÂN8¨ƒÁó÷œŽÊà —Ãqã÷O«ô5Y.³ùj†G­i¶˜ñ£ÖݯñÙê1:ñŸ¼h™øæ¨½0ZŽ×ÿçÞóå<[.V0{„.ùþ—Íàëà_viYendstream endobj 1305 0 obj<>/XObject<<>>>>/Annots 250 0 R>>endobj 1306 0 obj<>stream x½VMoã6½ûWÌÑ»ˆµú²d÷–.š ‡~® äÀ %ZV#‘YRjšýõ}C*’ìèö°I€D93oæ½êó"¢¿å1%í" B¼ÿüv»ˆÖa°¡uš)µÇù¸jèÓb› ­“$ˆ±m"ü÷+ÞŒp8¡t!ïæ[ìú•Û SÞMÙ=v³$X«É6I7>lã”_M¶I²öq·y‘_M¶qÆñCö«É6^gª¶~5Ù2š ¶q˜¹Ã–ÑdË%ábDÛ>üŠw¿ß]ÖÏTôá&¥(¢Ý…^o²`»MhWº‡´+–»“$aŒx¡B(Rú™’z+KªÉ¿DÑ5/Ôá­¤gœÖX*ŒNö¶V•;ÔQÖ]­•h¨{`ÿn÷Ç"¤UÄLíÊe!šÆÄÁÝ#µ!}èD­Ø• %Ÿ©jô~<º“PeÃP¥sĬ“é³X\žÑ˜ç*fµ ŒZ•t£ P)ou¦®³ê, w²’†Ž½*8‡Y>úÃòáÝWDMò#M?ÿÑéözÿÑÕtÆ”~1úIš®––ôÑÕ|^&pÁ •ÝŒP̃/w@?éNÂZtÎ…V ÙÈÏ}m@'3Bµ¥N;Ôkƒ›RtÂs§! Ãr1°ÑOžúà+Š”l¡ðÿM‘íF÷]­$“Ã91˜‡eµW¤Êº½"ü±WÔ½<ÉoHÖŸº.™­O#¦Ï¡à„ŸÞ{8ü8ƒ4ÑéÛ¯ê[éí]ÝÑsRÉ‹Á'øz~¥²¬ÒA®M|ú|2ðƒm†sA¥êÛSWhõÚtÜä£×Q]ƒ[tŸK` ²û¥<‚?1j–„U+ŠRhóÓEl´z+ŽÃëFbüì켂 =Væ¿IÙBÚ£œò¾{C±¹bì&á ¹Ù‹o7æb›\Ž’+NÂÐûÙ–Ç2W1Õ #ÜÖ•BùˆM1ãA™íŒ½3ép¢˜ŽÍ¹ ß²¿‹S¯‡jûç·)ôw¬±kæ9€yuq‹1̡㆞bKjq£µ}K¶þrÖ^Náö¤û¦äyñ© w™¢Þ§U…ÛîÉèBZ«M@wnØ_tÒ™s¦Sö«ÈݧVƒ¾a:LÙx’êŸØj¢<Ò ¾u×y…¾9oýçÑ5¼YúÝ¢‡ª ³µz5Xåá–Oó¥û¶pMîmVtwLJÓ< òlƒ/DŒ7üê‡Ýâ×ÅßÚNL{endstream endobj 1307 0 obj<>/XObject<<>>>>/Annots 263 0 R>>endobj 1308 0 obj<>stream x½VMsã6 ½ûWàèíÆZëÖ|L»M¦ïL7î-3Z¢d¶”¨’t²þ÷H}ØJ¼=´ÉL&<|xäß³–øBA¼†¼ž-ƒ%~þ|}œ…QÄ¬Ó ‚Âl,»•„§Y¸Lhw•àWÜMãaåvoœe~w!øÕè§I‘ïš"û•Ûã`Ñjí}³8Xw+Ú]­(J"ï¼_ÑæÏ»iº‚O „!ìJ,x•­ƒÍ&†]áj]Â.ŸïŽ*¶7Üî…Ö¼ÚÂX ætÐêdEÃ!g 8œ /À*0-ÏEy‹ŽÎVÐN5 J(˜eÀjÕTÐj•sc”6ÆAªü¯»?gKX„16uWÌ™Öì ¹j,q7¨´(P‰v˜Ž3©¤:0 Þ™5˜†¯YÛ}Â1X ¥E¥ÐÆNðESL’L8Ë>G`’òw:ßêÇZ~»/Lž8ˆÆ!¸ »$„í}õ[¶xçmz÷±¦ù˜2n_'ézÜa¹L‡ê€}CÜ.›ëFè'Q É´<ßðŽ+¾[Ñ—‹Šnå^jUÃöcHLÙ~ü‚íàè1Ý‹Êè£ëø»Õu¹¹òèÀä›="C«£kÕg¤b̓A ¹w££²ˆ78xÈÑfQÀƒÒV³Æ…4©ÅÕ€Íárlº¡ªŽÊXó<¯öì^EaÏ| ïÂ÷#ò Œ?ÿŽÿ¢pfï÷O8Í=°h,8púç§Ë `­ƒ„ŠÜ‘89“òͰónæ¨ È9Z¸7‡d<†ÿ³Ém…Th»&·{¿úÛÜo}ÕæëÞë33FTÈU6 %*Aƒ3²×ˉSF½1ÅCóq¼2OÝp¶ß(žó8Õäpdïè)ñ@sfÑÀj>/XObject<<>>>>/Annots 270 0 R>>endobj 1310 0 obj<>stream x“ÍŽ›0…÷<ÅYf4ÂÁ„Ä]:m¢.ºhËìFŠ<ÁC<5'£¼}¯ I£J•È?Çß¹‡Ëïˆ#¡›C¤˜-°k¢„%4s}üØD|ÎYŽy¾` ¤\\G?#ÎS–b.RšmÀE~ݬΠ¶ðZ‘Ð{F~õSI¸Ôã2òà™¶ÆtÍÁ9Ê7ðb$Å e\%(w“ug•íCùþ¯Ý §³‚~ôÇWÛnj¹­Tïlw~™Ô[ùò0ÈÿÄ|Få”ÕäéÂuêt…ÍjûùÂÒ­Ã]¼[ÜããÀ·\nL÷*ÍÊZy^.¯…UN×ÙI2ÓË|Yn/œü¥zÈÚÑËÖÇF‘e‰½l+£`ÕÁªž¦t[ÓìI*°4ÚM8†¯š¤x“N(k; ×a'¹‰Úí!Ca“)‡ŽròÒɺ8`”\\¯èX¹súDõƇ7¯0–®*èæ`ôN;sÆÇ^µp{…c¯lðÕû3§ëk«Pw8eÝJ§†¥Kd6ŒóKW Á²|F?BFm+|ŒCöá÷xöŒo²=Jã•1±H ¿{ÆÆî‘ú ƒLdL,rúý®Ä‹¿”Ñ÷èŽlûÝendstream endobj 1311 0 obj<>/XObject<<>>>>/Annots 279 0 R>>endobj 1312 0 obj<>stream xµVMsÛ6½ëWì­J#Ñ"E}¹''­}jÒ¦ºÅDB’P@Ò÷×÷-ŠKϤ‡Ú36IûÞ.vßî÷QH3ü†´Šh¾¤$Í‚¾\þ˜-Á‚âxDâï«+2’ö#·­XÁ·p¬ÝB¸Y1Å+#=cþŒÅ‰æó`Óâ¼n.ž‡Ln¨{¦gîuve¾Yyð9âÐuhh¥sdØ!Ë­ÇÀ‘2çZéžé™{ƒ[Y‡¸˜¡š¬tŽ :ä¸õxræ¼C+Ý3=smþÙ§O£pµFþEór-§(Š‘_î-£¿šÕ(ÄÅa5\ÁßȾÙÕùYÍÖÈÑœÂuÌ«ö­] ×Q0糫öº7^}·ÝÜo(ÜÐvBY€Æf½ mŠxÎP)Ûdô±ÓR¥2¥÷:ÏëB%¢Rº 'iìSùfû†b Cgh­q>‚¡ñC¦w"£;cÄKI'£Ÿa‰tcrB….e2©Ô³¤ä A_¨:ŠŠD–é3UšD’Ȳ¤TT‚TA Â,f4 ¹–,äYUG]W°|±Fü %󱆴ieé Ž:Kí#÷ÒÈ"û Ðö(ËËIlO5|¨zàß =3gDZ’yÉLª ésA¹Ìµy!­ÙIU¼O€™ú„cxܽ Bs]uðú]©Ÿ¥™ô0K––óð*iöê$]„€†kÀZªà6òâßC¦\žX3x˘Y¥Tã|%ù,‹øQàö`Ð:ÞPiI¢H‰ Û”A¼ßç#¢Ú¸h# Nœmeà¢%ª÷‰¸·yC—Ëô÷5œg‰(]*ë<Fýˆ".°F"×âƒ6J–· µXt‰i³¹Í O.ô;ÐÿvV¸ô³Q•¼1R¤|àæ½Åf|GÁ:† 1σx:ÕÕ„ðÿ m^´¥1mvN£(X.¹ÔÆ…D¥s•ø«²¿¡%ª×! ,sy½÷sà%üCætrŸšýcúW”±‹së¬é¤Ï¨‚:ë¦Ëõe«£ÉÒ+Í™4[rN|_{öÚm"s¸›¼ltæLjŒôžÓ™«»7*DUi‹é:QüTQ!‘„]-ÚÛ:ðtDz||ƒ/Ðè§·l•‡}ïh\)sQT*)=:Óh5Ó)¼ø^+_l j¸LË“LÔþ¥’cãg+5d­…°`¸rÒÆV6¼W*c…Ò«_I¥ÊOÔÎê6Ž ¨ážTE¸‰ë òˆ;K½:4!nJy€˜P©Îà%fN±œªÙº–W¨ä¶¢‹‰÷SÙNåûïÒO‚oîæ’7hŸÓ·˜þjý"Þ£ÜytÄ»ÁÀ=ü7©áe®´Ön£c‚í ‘*$N#LJ'Hm¦'tTÚÕ{¼¤o¦TI:~—FýO,¯H*f©˜æW~ú:@¸¤6ª=Âï;6óx?ZtùyÖ*¥wOÈ‚Ç1*—lÔø!ÓŸ¿€žŽŠŸìÖŸmñ‘Q =|¡M­féç/M|_§{}æýÛ·ÿ¯¥ðpw{ë’{noO õ×°µ.^·]Ðdè]–YæP³r£ß¢žPÈ4mÁjkÓQõ7µÊÑ4p;ᔲ014c²du'Ž_®¡;–y5oI8û\êl¥¬ Ï‹ª`Iv3„ÓY;Sd~ ¹6?FôX;1ꪅçëÆ9ê²ÎwÐGí¯Û–¢s-bÌÉé˜~x$ŽWq°Zú±`2§ß¶£?GÿÏ mendstream endobj 1313 0 obj<>/XObject<<>>>>/Annots 288 0 R>>endobj 1314 0 obj<>stream xµWKoã6¾ûWÌ­J×VDYÛEévì¡EÛuOëE@[´Å@½¤'(òß;CJ²¬x·´  ˆš×7Ãá7Ì烤!LØ”£ÀðK÷øínÄÒ™B”¤ø,! #?jV|h¥qä$e©?ƒÈ®¬t:󈢿–ÀfIíª'eämÓ€t튤¦ÁÌZF!w 'JdûÐ;` ó õi€ÊlŠ-`;bs†þQLZ'‘³Jc |ÙʉV®:³Â)zb ŸJûFÊSþ+Ïaø¿ç9œS"•?ÑÜÖñ¬>Õ%ÑËa? þë[Ä`¹ÅƉg‰?ŸOa™Ùž `¹ñ2YŠÊHUàê\À®Pk^ך?¹:ŠG¡Ç k÷ˆÅ3þù|ZdP+ØkaDU[ÛjÒùCRAm­¤P^\-FLì–.3¯¥Òϰ>l·¡Îy ëgŒdD±…Rîr\ P•è»õ/a‚µF/KĬöBó³ Œµæ•ÙsmA)ý`(À1—›Jâî+dJ˜ê›rþ((£Òˆ‰¯Õ¡FMIEÑb‡Þø3±•1“äúϦ-µW(÷¡­½¼Ê†J¹*!xªS-2Þ×°áå/+ ¡©Ö JCÉ+Ú‰RÕ7Am„1ÂŒALˆ{ØnB«‹špo+¬ôf±ò°f5ínõZªÚa™Ô«6•˪ÜÂ/Ú«+»W×·ØæM NÚ]»õ…ì]©±ßLOÕÖ©¶n<؆֮`j/…Œ×¶Z•ÀϺŒØØ&Á¬(’+‡mtk.­—# KWÈ®ö ! ÙØ«Ë¡šüè B"—wŸLÑÀ&‰Ç+ón•¦vìùm ÛúíÀÖ{ ª¿ß‰zåíîùQŽ!—c:&¸ÈVW=gA:|Ý) ÿ'”g %¡”óÞ.éú¶­êðÛ^‚_ªt?JfðóÝÍ=vÔÊ“HD¶jôR¨Ÿ¾å’Þ¬ê·¶Žø‘¢L܆¶„Td?µõý2Üs›·oÞü¼ÂÝÍbqgYö†Hv±°›ýuÄÐZƒqŸè»ý KYpǘìOñÒ ùé_’˜Ù‹Ü>÷ÈÓžG“«C‘ÁF•8æél6´Ö7ãK4Ydû‰K]:é¯)ÎÔZfÄ™[¥K7úȵa¦Øƒ>,Ï ‚ 7æPº¡ç¦ªŽùT‡r-4Qg7þ•œfÝ…áêÃï8–®ÍŸJ8Ï_›?ðOœÞ,Æôöù³‘8¥q¾·Sè¼kP½¥ïwO¼Übq^¶ž²¯w,ç(ãÜÆë o;¹ñ. eµë±8qõÊclÁâ1[Ä««ŽÈ±âÂþíX O,8ã} ØOµBþç@’æîÎÆN8 {X`È÷4•ªm79z8¤^ʯÎË÷06f/c—ðiz½ü¨ã¢w| ^Zƒ/ˆxs C¼Æ L\f‹I>™âå=MqKb?@! gAc˜à]Ev±S/hvgÖ̶ þç0Äq⇠Ý=GR`YÊØn…Ÿxup¹ KS?šMa’sÒŽð¶þË¡¾nF{”F~šÌ\ôiHáß-G¿Žþ îs˜endstream endobj 1315 0 obj<>/XObject<<>>>>/Annots 305 0 R>>endobj 1316 0 obj<>stream xÍWMoã6½ûWÌ­Ê®íX²ü ‡tÛ{hºh‰¶H¢*J üïû8´¬&AÓ¢À&€!Š3ÙǙ7£?G>ÍðïÓ* ù’¢l4›Îðæòóå~ä¯ÖӀ¹ýÍ(Âix^¥ôµÙ Ó™ÝõWÓ5…¼â]h-i¾±¿ùë»nÕÙ]lœîj…æ¼j-þ:°¼Xá\·êìÎfl9XÌ ðªµì¯­Nã•[uvW¡‹h³Âù>¯ìîÛ!å‘®ï6Oڀ׬6sÚÆ ÕŒ¶‘gÝFQÕ©¨$‰<¦RŠx‚‡‰Ê£Rf2¯®¶£ë»|ßšËé:„ïsEÊ>T2§ÚÈC’ÊIPQW¤ YŠJéœ*M‘Îö*—T%’bQ Êô“ŒíŽ}S‰ò(+*JIcèYUI+Y%¢‚SFÅÒXOf4q1Å6x÷¬8&˜ÈÒêæP)R©üØ5%K9uá,ÏáxøûzŽ¡°EçÈ EìA—™{½¸Ø•ÎÔß„«Úu(¤UéÄH€Ÿ¨¢„vž ##ÆP8Œcª÷"%Q–â´»²`[üºoùEN2å»{I8ISXÃO2=1¶‰>/XObject<<>>>>/Annots 314 0 R>>endobj 1318 0 obj<>stream xµUM“â6½ó+úÏÂß6læ0™ TIU²ä„©)-ï€LdÃîT*ÿ=Ý’mlâMUUØRëu¿×ê–ÿ9`ãÏÈ/„ô8²™+íßo«‘ÅÌߣÿ#¸Íâzv€OÕ ˜MVß§½z¦­ˆ ÁwÄ ŸhÕ³«Õ‹æ“u¡3#+’7 D42ý°¾å©ö0[†à8°Þ2 ˆC6Ÿ{°Î´Ö©<±ÊóK©òSU&w0¾‡\¦wëÏ£ÙÒ¯ÁSžº!’Zg—(Q•,¡zP¨|ŸK~€ ?œ$Ö‹ØJhÛù”ñJ ßbg|6„ÚÀ)ENî˜1ÏÁñP2¶aêR¦0 Ï<ø”òªj¶âRõ}ÑÖÅš\½¯/ƒÉ¬±`ßñÔ(µ R\–;¡€Cyi¾ËE¥¨Pˆƒ8 Y¡ê‚ÂNGØŠÔΕâï%ƒõ«xÇ fEŠi™g"[`"xyIª˜Y¨ã²B~W„R™ÅI(^å…¤€´tRE*Ê2±Ï—׊/R[”@žB¦ˆm™åÆÔ%¥3kRÔ͜ΨDÓîÂIZOÓcæékÃß´AX7„®1§.gŽ%ÝV‰œþ¨3—öúØQE€.#¬½ÌZŠÒm¶õzLûEˆ®] +Tç*—äž?—¦kÿÌ'p™½ÔR& “»ŽÃA¢†c{îÿÄtˆh>ÏŽ·¹™-›dÞp|ìhj2u›$hÇ¥È3øeõð\wCb岓,2}À”Ñʇ6o›­^¡(SsŽM™¶ùü6×>àq<þd5ÕÕÃb±Ò}ô@m´X´¬Íƒt¨’¤šîõòêTþO`wáÝt,ñ–ÂVyYTºÛLÑ\ïËŠ²Š˜Äz»·?ÂÛ÷÷ ñ1c7þ©…^[º9¾¡Äj-›·íÆÞnû Îí‚»Ý2Æà.¸ÿã€×¿êµæ~úʧƒXÐò ÖúäõEü£Áû«c÷ÚÜ8jZîFþ“¹øð£ˆã‚zi@ïW™6ÉÄew€<í=¸CpoÞDwÉÇecöÑûîÔчà´spôàÚ«ÿ/Ñ=òqÙ¸Ä2¦÷+Ü­£ÇpÚ98zp=€7Ú}òqÙx=¢÷+ÜëjŸ-ãæ6Ž"æÇ‘Ç|?¦ûÖ´èº,á÷¿r?syæ >/XObject<<>>>>/Annots 323 0 R>>endobj 1320 0 obj<>stream xµVKsâF¾ó+úˆ×hÐ<ôòÆâ,TIÕfÉ (—l$–5H‰$p¥RùïÛÓ£7R*—à*,õcæë¯{¾áÏ ÿ8x¤ ¯ç‰Í\´Ô_Ù¸m3 ’;Ìá»Ìî¹øœEO8³1½þªÃ†H.C¯ËxŒï†]Ž`jÄåùc.a«Q—´éà^ÂÑE »|ÁÜa—´ýQ—T£.×Fª÷2¼* ¸!ªáŸž4«†º¡à†}zjZ 7- f*ácOúÝÑ]´™_¶×)é nvn6åâvårLO‡‚iÑ. ÓÊ¡à²)C®\—è¡ì×nfÜ X0ârå€k„19Ü€ª“Êc~Ó[ÆMæ`p³h¯RÉõ‘íÁ[Ìtähp³|ƒÙˆAweÛ¬LC„_¿­&»¬'PáHœA”ó¨ßNð¥ò:6‹^éáôF^ì)æ¢ÕCo¹½µ¼BáAÆ\Ü[JoÚûãe¬ÂAOˆ|¾D)â°ŽQï”± °ÞãÀÛ¹~¿}òËK¾È²ð¯ÜmøÁ»[›Ø`q‰0×{JÿtŠÎQR€£—ºn”Žú¹IW»­Íî@ºŽütÒiw©ÓçKUWda›•Ʊˆ‹(ƒâkùkXèçô( ‹cšÌȯDiïéå´‡—::ÚÃ1)RŠ<œÒ—ð¡®Âò¯é{‚níìÁÓÓ)}?&ˆ‡K1íçuÖ ÀR8B’X²µ]O¤’Ø„ËûÑ7P©íÕpú"·BÍjü~J  H¢ÍP±«Ñu4@B¬û\[rÇB˜9ÞEÜÓ÷)f$«2µ’©Ž‚&¸2¡dÁƒÂq_Ê%°–ÀëF*ÑÞ˜ ïlLÛt,Tzµ/-Gà† Ô\WF¬p”áx Y¬ÊÔªƒøš/Ýr-ÎñºÅ_–@53½^…8'™æµÔ)sš­,ºFY5à ilB«%§Õ÷V`ðs‡¨0ë˜á¬Ç9ÎÒ3„Й\šçNék5Ì4¥BЯTR^–ÔäúH%ÖOöL_ªü½ƒ 8Qe—iVdabÂÆ•UÏr–^ŠcArŸDÒvzxgp‘RPM3H¶w­õqˆµ‰ÿ èÎã ¾µ ö™™/+*{ŸZ%U>/XObject<>>>>>endobj 1322 0 obj<>stream x}TÑnÚ@|ç+æ‘Jp`lc'o©šDy¨Ô´®T©ªªÃ·ÀµgŸswáï»g †¹²}«¤ÓÄ~׺›C„2F8¸†£59jyvG;ŽÅúç .(%Ÿîœì:žHßq›.‚.˜í…ðŠï´1½çX¸#.â~ßúšÀXûw\Û˜IlŒ^dÓºŽl³»åÙ^ ku{x·x¯ó½ñ¾ç C"ì,ÎF÷. l~Kìtؾ!<ãrñÚIQŠ<-ã.W6ކ¼gÖN†z‹Ÿ‹ël2M®Ó_“¸Î¨yäAyrLhÚŸ´¼°áв§:NðäÀÃoínØòwûzÄ¿E´Ö›ÞÑ! òPR"+SdËDäå2z>ÿx¾{–þ,Û^šX9}-˜ó«ˆþÏçÅ›„o¾Ô8]ÆòÛjô8úrsendstream endobj 1323 0 obj<>/XObject<>>>>>endobj 1324 0 obj<>stream x¥UKÚ0¾çWÌ1ˆ$àÄ›ê¥U—Õ*uÛô 86Ĭ4˿߱“E¬Ùª]5(#y2óÍËóñè˜â@BÃî౦։¶Ò$!~,Ó”k~Ad+dâžéÆ”8YÎ& IÐY7 â8ÒÄzW‰-«à£.GÁ…Ôò…ÕØH€ÿâà'Ó¹¶¦Hêgò»¯.3¶ã†óiBƒ$žáÿÚE‰v¿MçbÎÀÛendstream endobj 1325 0 obj<>/XObject<>>>/Annots 330 0 R>>endobj 1326 0 obj<>stream x­UMsÓ0½çWì1@âZþLr …f: áD™Žb˱À¶Œ$CûïÙ•íÔÍÀÐÉ$#yW»oWï­Ìøøe&Õ³Ʊ·î' WIìÅ…‰ÇÈ~qúðJÁ‡™ïùxöô÷q7c1óV¬t­!ˆS/v|š1xé c¢•¥äëvk`2´®SÏÇ´#+¦–†^ˆ¶ˆÂô2½ÜŸ#ÑG¸¸Š€1ØXGİŠ0…}îÐú°Ïæ—Ö¢±ðSh#Uª€ÝL×¶J[¶ÔB@+´T¹Ì@áŠ[ô3ìKq\‹gûo3ìØ2H°È}>?9…]ñ⊭ K,•n7A’Bo:ã’…XÕ4NÛÙð&ÿ±x–yOŠ“Œˆƒ•ÇÖÕ½[±ö1ú9dª•Â@Î-‡B«8+uà¶Ió{0"£ÞUh©T6ð«”Y Ò¯je,¶]€áµn@‹cWqÝ'aÍ»{–ß5Û‡SÅ]&Ú>–lðöÞ.…n¹Åtoà @uÖÈ\8ÃAuMÎ5U®‚£W.kÑ5\ß|/ ªGHBâ»#ZÅÖÈã ®?š7„ñAnåaÇÊ9 ÙAÈF@sä·#n±Â›ùñ–/°W (å]›üæYõïP–“6\>¸ÏO%sx·ÛÞŽ—ÜC(ƒ•úòu´*%­Ü™çŒÒ,ûŒvò/_ÿ øñ¹Ë/žÚ¥ßm7›#Ú–x¶ÙŒ-DŽÞÌÿ†úx]®¸ê„z:@N—ûIÖ9IL~:=µŸn¾Rá!þœD~îTÊs'ÓŠL÷¨¡{Ö:Á™RuUŽDÕ8ñIr}ª@7FÎ3TÃ`§Ð„:}*Rƒ¤Õ¤ÙJ×n Žzšhù<:2㬀aþŒJÆtãä|}Çë¶›Ç&xÞ AªïÀ{eRã¼ýIĆHqŒ “ƃkKS%†;üÙ_êAÍ8{¦ñ`kL‡­³åØÕDH&ù±˜Bh7§ˆîÂ~® 3ž¥ø¶[…ã;ÓOBå=9Á±ÓÀgƒ1Þò¦ãÅ_Ž–©¿&ïß#£ằB‡ùFiä¥Éªƒ„+zôz?û0û sz¤endstream endobj 1327 0 obj<>/XObject<>>>/Annots 337 0 R>>endobj 1328 0 obj<>stream xUKoÛ0 ¾ûWð讎bùgØ!ë– ‡ íšÚ"pl9ñàX©-úïGJvÒ¸-ZPh‰¤>’郃?¡nÉÎxî†Ì×Ûî„E`3íÀñCæuoÜœ;ÌÇqXH§<$]õv<åQÀ\:B¼T¿Ñé÷åðºj㹜Ã2C¬žë±‰ïÁ2UlX&欨%Ôu»ÐlãælùÏÏÑkg2â.âY¦&tO!oí{ø#nÁ6ײc‘™{¯•9){²’”ã2}Ï"Õ.= äˆ}ä˜(2ËQ!V{Qå2Í؈ÆR;…LâÖm–¡Êi(&îžú4áI¶E kÑaéoèCíÿyD’ãѪp½)ÑãØjõiõ;¿Ã¬Ð=< ÕáJþH7TºŽZuÁ±H=ò«>#WmWÉ>&äµJQ%EU ˜½¸ÉeYƒìÓ;4áàg!†~\6È}.jHã&†¬’»É«*~†Fª­M!×Xµ7° ÝnÁÓ6O¶ˆN3ÌŸÍ<䵌¢/ïÂä¶ÂsY5U\j£“SF=‘¶u»®dÛ䥀r¯zÒ¬ömsgnV±…$*[Ä|IïÎ>‚2zA€‹O@ ¢<Ê<…ߋ٪Ϯ†— ($`wÝ[@¶ JÊæ £kF:=Ÿ”Az{ÿ1àS»‹óóO‚V×/fÓéBrFµNû"ÉîÌ÷P¿WÅ¡‚+¨_ާCqoò]^ÄØä*±iQ$oãù‘ X<½uà·5ì÷B4hê •¶ùP©Þ‹$Ïž‘–¢¢‘(4Ëë­š‰ø_QûëÐm¡ÆÛ16´[jrY7UžöcçXÎLV;Õ“Ø’JOÏ3ÝRèØÈ ¦·&}ƒ„ø)™¸àó½€f¥.¨ºÕð·Æ©ø+.Û¸ Ëï F¡‘¶‡¶ç&\–8f³8ª1½Ðc!~Óð“‘šnDæ?—ƵñFmÌDendstream endobj 1329 0 obj<>/XObject<<>>>>>>endobj 1330 0 obj<>stream xUMoÚ@½ó+æH$06˜äP)‘’´R+¥­{"9lìµÙdñR¯šß7ëÀ‰Ò‚°µö›·óÞÌ¿ùø´œÒlAñvà{>žt—·ü„æS¾n)œzÓf¡éçà*ê㋌&7 ŠRÏW ïü|FQâ8}Šâá,”ITLwU9¹•åYô4˜Ü„MÌî‡B[C*u•H*7²F´¬CºŒãj[iQ6oºØí7*ÞÐ^iM’eãÊZ™ƒ p‚NY²2.•ÉïÏ€xQõ&´e¼á­|3茒¡ÊKô‹,„&»ZƒÅ¥õèÆà3Vö_á©(I’LUÚvƒL›GЈB¼ŽH$O•-Y²‚Y™ÞW*L ô(âgBFe •©ÜñУ˔ºy:L!3ØTôýkŒŸ|àc iRÀJ'v„M{¹Åf»Ó9¹\v0ˆµÞé^°rºðB¶òúàˆ‹ЖìÈë¯Æ<lcBYƒ‰ÓwÝ/™,=º’±¨à¸* …4§?ø±µZÀZç-3¼­’ÎÚNN¢¶2·È´'µªÃahz·ËœšOKþ­º ùCæ-48úüÈ™éÚŒUªÐ©§i µ3þ7$‘¿mTÕº zOìv¸å¥~å6$“Ö=!ò Ý“Öåí45uù‡ü-[çäYÿôh<þD´vKK¬i_ˆ]Ý«ür~äTëØ{÷5S½ ™çÆé¶Z|'€Øiâ½]Vn9笺øöd·ñÝ×´hèÞQÅ©®þ‡âõ³Ìc‰s³‘¨D;RSof…h. æf}ަxå*ž¶ÃÑmâî€rI£GèÆ¯¾Þ¯:øAÈÑa?ºŽßö(ìendstream endobj 1331 0 obj<>/XObject<>>>/Annots 344 0 R>>endobj 1332 0 obj<>stream x¥UßoâF~ç¯øÞê\ÀØØŽé¸ëtmï}:ªh±×Ø­íåví$ü÷ÙBP£‹TËÌìÌ7ó}ÃAˆ€Þ!Ò ¢dÍà¢8ñgîç8ðc¤Ÿ NRßÚÇÓ¿*|~@±§Ç×ÛA˜„þ”¨ ‰§ßa8ñ)KÑ“¬aʾötf #JCÖYêˆì‰­”á,¦ˆ1_ãlz·º¬Do1^ÆC¬ ÂG‘O&Xå¶Ú«Ìû£@¦zm$ºR"—¦«ZÑUª…Ès-‘ª€Y #šº–;ÑÑ©jmH­2QcÓYDmZ)st  2e}-:™ûW«¿FnÉ*÷¾UMU Í~ZnÉG³Ãxys¨ÖÃ"ËúÆ;ˇ7¼ôü,u¥ò*{9„06›ª%,¥è ­š3Bk±ÇCÕ•ÈÙìÐЦîs×¼m­6Öù™qŸ†x(+n‡±Îþ„©Ó˜váR8£©°TºÓ¢uAÏøcƒè;50ýF«žæ"ÑnÅÝî€öNdÙÚÛÞ‰!j5DY y tȇ4‡])ÖW?«hD9b{ÿŠJ`_÷ªÊñûíâîØwWIÕv°Õð—Z}ÿkHMì¨.þfcÞp}œfäZqLmrr³%<•äÒ½ðt7¾èó|ﯯ_ Ö&¹]Ìç·vò &É|~œ1tí½„ö?pZü¶)õÿF{®ë§V$a§GGÑÊ@Ó7N¥¸w*'•úfCŠ%mçU#[CTfo+‰sžûøÓHm«ö—î¨í‹A=(­÷b)©Aj—†tÍ{$—Q7Çf/OÔ®•ûá¤lˆ6¿t*«K'Bµ+÷¦¢åRï-^Þ/£/úÖª4£BéÆŠòi«LýIò$AœãÄAÏX{޼LÜõ®ß:5á Ë‹k=¿åãhvµœ?/óLYŸd‡ÞP¡êˆÎ6ØN@º@n:µî^úŒ€zGø<Ò§{POá½s^®…,]w¸Óž‹8K}XË̃ñcäV—ÝÅãåô¸—Rú{šFH¢™Ÿ¤S^tŽî°|7–øM´½¨-þð0Jƒ{Ç´Ò»ÛŽZ/2i[§±ŸÞLéχýBÿ°|ü mp æendstream endobj 1333 0 obj<>/XObject<>>>>>endobj 1334 0 obj<>stream x…RËnƒ0¼ó{¤ áª6U•‰žªÊ Uýûîòh•TQŒ´¬ì™õxvO?Ò?„loœ€û’Ó¶±\æ‚bÆéØY‡<6°1ROýÚ8+œCZ"_ø‚E€41.¤™™è®®öô•êïÒOÃYáý3Åæ>ó`¼tóî~À=ØÜ‚ªžrÏ"š‹{ÿÁœÀÂyˆ¹:ä×ùTR°IÉŸxÛ I7ÓªÝdJÃv(Ë¢…sÍ&îžsM¨»ùº¥ÆòšåÏ) (Ä3òò)tFKP)7>< üR(¢à]èD£°-Åå•>V }õ®»””=Zƒ\ªè¬Â¹£&¼mÝäuI– ûA«~†,V›Ð‹Võus°ÆËwºÙ¢ÛªmÕ7|ÕŸ)Ñ\•KÉDäãPâxŠºô<‘"uðÖ¡˜u”&=öB°¥Z`k…­(½TY1vMHÁ$Ž8N+âFƒžRccü¼$endstream endobj 1335 0 obj<>/XObject<<>>>>>>endobj 1336 0 obj<>stream xì—¿OGlj(.¢ÈýÙ“\¸ãXñŒV*¢¤HE)(¨"Ç¢§ &eþÈû'¤LyH.®±=eªxc!±Utg¢Ùo÷å;oà0?N‰IŒåA{w»Ÿ}ïíÌ~÷»{qc q£}«ÑºÝXývâ^#nÍLÞ ‡§§&§q{ŸÓµ'å„ÅåæÒ_5—›Ÿ²Øˆç¦d|,cáëæ—x¶ÕüŸ·›‹Ÿ-!óò 1~qœßs+ó7KÒg®ê̹b.{[ˆØe¦³˜ TC€³)H˜_žHU0ÀŸgTŠù€ql¡"Ø ÀSüŒO{ óoøò0Œ¨4öÀ÷)@8žøyt° #<ðóð õ*Èk'3ßü8;Bª³G±90Ã2úAøéG]Rypf`q*VŠ5"ðåd׿v`ó¨³NûDÖÝ`#22s‹»o +íRñ çZ8/LpÀñVT§âIæ²P°|ªÃ§™ëŸ€}€PƒMæxJIªÔòc6yO"81®ZÅ# !™Å‘ªR‘ËAQ¼:¨ÒÎÀ©­:È‚ØeN"d¡†;,Ž™ãQó5ï×ÜÞŒ>¾ª‹o-Rýë€GÕøOÀÛ®öšŽj¸çÛDQ µ‹ªUË5Ô>K…‰ ö¬…íüé´C‡æÇ0À§Û³ÔQG‰®2[ቂQêÔÿ”º<ç]Ñ.wzTͧŽo$¡P Q^s|S@¥Њ N¯v\Ù18¸ùT4nÙ«àù{4¨g~ ýêâgpôóqìíú{»7ó"ü*ìzë»^zÙæTn»ä-ïí*õ.ºê ’”Ö¼ÚKÚ\ƒ·GÖƒ¦µ—ù¦÷ö͆ìD]2@_Ðs¼+û¨±âxAjðŽƒ·‡â¥xµ@H•¥Úȼ·¿RCL5:™÷vÈ:x{êÁóŽ€,p4yÄ%oô\f#Iunæ{3JÀ5»ƒ—¸Ü×Wûÿúƹºâ}ëÍ\Ôîàí]7ÐZ\ÔXÈÛœ:ʨx¨ÜÁB»Ö›9tµõÐÉ(—Íð³í·èôWx{×ñ|w-«#ð 1ý  º‹·2hÏuÚ&Nº._ osÒ¦º ð=:¤j €!Û‚åC&hDr¹}›Y6èÛ5^€QÁøyƒ!ØCl ÔOf>Ú¯nÙßÐo{'3nË<2îwÜóû¡oÿNäƒÖà%äÓ{µ£5xo‡b½ékû`ç™ÞòÀ÷íª{J÷Hûžá€•P³å@0ŽNfÅQx¢Ð-•Å}DLü¨îò--Úµ¶ä$€‚ª¤Zàó€ €9"Ш»‡HÕ¡Àøv —»†(Žy¤-*rüÿ1RÕ1smÜ €|€·Z'€7T‰Wø¨½¾·£g9?|'sáxÎ-Ë%–ä/ÿÿârõä äÿÿ…UÛŽÛ6}߯˜Gˆé›|Ù¼%E¶(Ð-ê / ´DÙÌJ¤Â˺þûž!%íFY /»5œ™sæœÑR,—ôòÇùVû½8P±Û‰5ž÷äÕwéÅz×|´Ý‰m>/v[ü\½ñæÓñnñpO« ÒkZÒæ~%vkis&Û)'ƒ¶Æ¿;~E‚‚V«|}¾Þ‰CÛ³ãE‘yû=ÎÎ*,º²,cÔã;’Q)§ŸUEµ³-ä‹j”«e‰£ɪâF$]¤©Åm,ižaT3éαU& ¤+üÔµVž¤!m|Ylò×£SߢòA0ܱð Z\ÖA£Y’“Š¥mÛht™X¡R6 jU\À`!:C¥5ÁÙ†‚Mue×5}¼ CP‚-™¸˧´“ºÚTœ„¡Q«À_ß!f3¬dœ4¾VŽ:gK噉ÔÝË(‘ÝÐI¡Í¶kTÀ0mà¸1Ý­Lsÿ5ãìñ\¥¬gAæ,ÿ1'>ÁTA%úÎ!QéNMÉZ+3–ï¦bš{L±zÕmßœ ¿Q%iÒäk#oY Ê^$÷*ô>:°˜“»J2¿ª’©€öòÔŒ WëžFú¢§§X3µ=Æ^Á㤨Sˆ¬ý”ÙÍÚ… Þh!U¿ ºtÙ cñT¬U­u0æ§=d#«ÕÀ‰—™ŒIåQ½¤LÆh †šø(û‚¾˜F?)Ö¦¤Áê¥ì»Nºàß'ᾩMîvŠÙX ¯:\ЬïÀç ˜ NÓ&9FÐð¶umræIå*†ç­Éu-:sý CO£‚6,\˜âæÊœçʦ(#”€ÁB“Ù›õ^Ÿ@E4F±'$hµÏÊ]À)A¯:ÃB˜=|‹^KTMËŽ«‘aÂO=‚0¤Rzˆì ý nXÆÞ\Ô ©‚îÙ|ìBŽ“T«´¸†ƒ¥†µwĺœ„prXþ9¹§×p’~c!ÕÚµ€ÓZ¬´ÞøìD]£#Ã¥ ‹‡}¿ßW¢Ø¬i¾-Äÿ±ä?ÿ+y[|`»>lF¿BÙ¤IJ’×”À'©ê¦‰‰‡!è/yK%ü€¿èÜØOÉ9yƒ ”Í\¤gòÁBHÚ;±ºGcfµäX ªMœ!@Ð/i↑ÆÀcÈ›»ƒñ0¡Ç˜ñÒcb&bÇf”‡ŒýN,·+ÚðñÜ2?ç¶?æ¶¿ðjøMš(¾8ç/vqØÐ|¿¼çèÿùªûBìw|’9vÃ)>ïþ¼û?\Ÿendstream endobj 1337 0 obj<>/XObject<<>>>>/Annots 363 0 R>>endobj 1338 0 obj<>stream xÝVÉnÛH½ë+ê("ÅÒѳÈÈ!F2£œ¢@h‘-©²©pqà¿ÏënR¤iˆè0c4‹µt-¯ªëÛÄ%?—büˆ’|âØ¾œåAä/c¢vLžãØK*9í'AÚáeV¯D‘íiM­Ó[ÖoÿÜOÜxß÷lŸròüØŽZ*£;®»Äñg®¦\'„Kg®¦ 7€M/^ÂKpçLõº^è·–]„i¨׋²²¬¹šp‘ ã³æjª?×ýÖ+6 ¥¹þº¾ò-'w¹À¹†RÜ?Ö“ù l—Ö{ríÀ÷(\ Í­S]‡ÖÉôTòªjP&SªxRàßéÈ*NI‘ŸššWTóüÄKVCjs3£úÈ%}ŸI*žx™±“’Í)V‹LQoÖ‡,×GEÖéÔÓle¡©è(R!”±šËäÙÖòv4 YÈT ,þ³éÅotŠ‘‘¶=ðš6S<·]€[V–ìys£T,xöÒ®Ò‘;Èo¦BŠZÀ«ŠRV3ªK&«=/©.HYdØœéÔ•ù‘‰<ç©RÏÚ³^ÕæõìØfº¹¡ùí(!d’*ª.Û'žê·29¿·¹¸×w&Ø+ÏͧŒ¿ ögiê|˜Rî_,ËBQ[àñD˜¯‚¡–¨ UT|}ä$ ií²"ùª°r÷á]E XMy)ž…}Yä t²æåž%øôL,Õct¸3@¼<49—¨á‘Õ$R¼Š½@¡™E dU3 +Å^ÛáFÉ¿5¼ª‡u¸ÅŒ›¯Ü®ç–ö¢Ç™´þ҇؞Âx€NU“ º`èÅtº*J…6#ö:SPѽŒV¨š]Y4µÈF.:pß²eÅ PšÑ®ÙƒHg$w&ü¶Lf_tØôĹg¯ë±îµÿ”Ç,I~–c–afÎTá¬@]R}9ù.nžp xU¸˜ðdxvïºáâ]É»–…³Ph~ToC\ÿ^²ukv—ÓU£éqþˆ¦ï7Fc:ä ÃhÔW­Ñåvê~²[ži‰ý ƒûÏ_Ùf7QϧB¤ôp·}Ø}PcwéA¨^²âó`oG¡Þ´ð­æêŒùjÑ].!¶Û…O!vÃ+7¼¸ÏŠËèNí9}ª°³¼g²a™n7Ží Vì,•t€Uöax»zÉÃþV)ù ì8Z`£W²úô÷zòqòdN$endstream endobj 1339 0 obj<>/XObject<<>>>>/Annots 378 0 R>>endobj 1340 0 obj<>stream xåUY›0~çWÌã^qllÀä-]5<5êAÕ‡Ý*2GZ/lIR©ÿ~ÇvËöR¢j«ª `Æ3¶çø>üÅc@ñfùÀCÈïm KïEêgX é ÈÄ1‡´°5¢ægU½]Ü|¼:O?yFŒcÀiq¿¾VjQgëB/¶€‚ª ]ÞžÛ5ˆoZ p[Ó o™N&‰n2¥§m«¾M&uöz·‡ŸþêÌúOðfqè`z<Ñ4<­Côÿ™tükÐqòÙkð3^Zp÷?Ø“ÌGÑİô;–̳ßQv<“‡S!ˆH(9ð8ƳC¢:Æ¥ÜÞoÊ^©z§´£WF· Ì›z”é&ÿ\Õ+hîËVm«¦Þ …@㘡—©÷Æ{÷õendstream endobj 1341 0 obj<>/XObject<<>>>>/Annots 385 0 R>>endobj 1342 0 obj<>stream xVÛnã6}÷WÌ£3’,É—·4[ZmÜ·#lv%Ò%¥4î×w†”â˦];€™äÌ93ç ý×$˜þ˜§0+ l'±ˆé›÷·ß&É2¤i"–ÐÂ,ž‰bxjàq’Ä9?Ç 1§Õ4™‹Åðt\M–¹Èyu9”Ì?ñê›Ëtv ·ë%mMM¸òùB,9l*)†Må~ÒÚ½5%:]î¬ÑêÙ)£?mþœÜ®3H’bš.ˆLJ!¢‡Æ¼ÈtþUUÝÎ"½öèÀÔàÎÃA)›ÆAgÀõû½±Tª®Ñ¢î¾ÙêºCƒNÅ€€j7Kaš%"¦OBñ³)¿ÂߪÛñ¾i Ód&üBÛwø¶:ÇŸÏEº$2Ãå wX÷ ÔÆ‚·“+h±5ö­©°ð«FB­¡áD|Øf€oeÓ;õŠÍdé‹'9Ý †ÒªNepXr5/ÈL<Ó¤qÁ}‰Ö¨Kü5µ«Š¶½´’úEÕív²£7„ÓF€Ù£õs œë‰NmMë÷qí•Þr«ZKÌL»o°Cìù†âÖŒåâqdó},¢ÿ!A$¼HkZ’ßië‰×þ“ºÔ®·#["ð_l/Û?«„—ô9Ò4 wä5ðöñš¨aš“õÒ‚tTˆEX.ð2c<¿°¬ÎUEeKisv”£±_Yä[¤öX¯Î OÖ³wŠëþæ[,wR+ײ0+VU£#¨T°QL¥µ!E²‘Ov޲#¹ö¬Ãæp†ô¬+ïÂÊ mýµiîéx6¸·(; øÑù2‡M£‹€^©2z´ÈJÞ4 Ä¥Ðx__Œ×軩N¦ƒ|/ÌôAôqŠLÀÚ[˜ãMüÁy†_ÐuÖû¹Ïïùü+hz“½Azímí#‰”}F™§½Ÿ¼É8´–4ÅY#þäšÆy8:»ü! àgmä¶­Ÿõ¶ò¹ôíxà=Eºo_Ð>}ú†ÓÑxEn.%€¢¹üp÷4pLÊ__›ød¨Üþ|eæwµz¸{Dûªh\­V¹÷º§èñKÓä¬Þᇠ<ÝI¥y’Îíz4 ߎ—^Ä÷ØPo¾hܳ‡8ØqÁ_ø¦9•ÍÚ©E4¼øþxñÉNªpU~ÊUAudyé •úàf罊4ÅÞƒÑ5àÄjñ +þæ*"ÓdtߨÅK߮ǫ>™Å"Ïè·CÁ?²y&æÅ‚~gЬ`D?n&¿MþDºŸmendstream endobj 1343 0 obj<>/XObject<<>>>>/Annots 404 0 R>>endobj 1344 0 obj<>stream x¥VMsÚ0½ó+öVgËØØ0ÓCÒ–LgšCzc&#lJm‰JrZþ}W’ ††6mÉ@Xöãí¾Ý•üm@ Â?Y £1õ #üeÿñùv@&$L ÍÇa5ŒHÆ­TÁý€Di8†4‹ÃµqœX['õ´émÐ7Y['9mJÐ+I™Lrü丹6‰|dB7q’Ó’ØÊqæ"“4G\/õ´ÄfSÉZ%Nêi#sNÉIí(·Ù 6Í0†—¬©Á:r—Ó(ÓV°ªžXCY7ghu7sôŒ-³ ÒméUk¸š æ+  h)žLF0/]"˜ÁL*£¨¸˜?žvÆùc,çTrÍ ZÁª…áRÀš>”L%wucئÁâÂG:ŸÉ0‚!!!ó2xûXp/. Ü^?¼û{¼>Üë×/ôpÓéíõ=SO¼`z:mk½;.õj–´ ÇÃxŒ}ÁÚZs fÃ@3r-MP(F +á;7°)]ÍöD#«^Ý‘êõJº`s#ÿ>PQ>ƒ{ÒÍ` ŠA!«ŠaûŸÈ-SÔ‚>MiŽwS¢]ºšÌ}5/GÝ,•l Ìb%‹¯‹À¶ÍqìǰíH7m~’üç±w#žõÿãBýà ?I^ÚI~ð7 w~ÿŸê‹— …<Þ‚Ìû8È™"»„KÛí‘tóê€úƒ\>â /™0|Åq›–;·k~l÷ûäæát”ES/™ºÅôÖsµ á†Ó°¢Ï9¦”T°Â7…­’xøY>_F5†Õ[øÕ'Úf÷}Ë l(†«pÙË,Φˡڰ—€ 'åiÜpÊ­âÕlÜžI=ßÿ õ¶b@¦ÇdôL¾hÜjÜ'G™ãÜž]®®š*¸®m¥È8SŽëBqã.mY”¢…ïÈïÅÞ/Ÿ6Ô4ÞØ­=>èi¯C©{7¾Z!RzÿÅ…Å'Äì­ñ ^U•Åpm\¯~Ã6ó ›ñê2:ËDÉWçP:¿‡(ÝèöÓ ÃJ ZÖÌl¸XÏ2x«WѶ1‹`ý@/ƒžO=Pï›w>lä#|DÂÇš(·÷Ôm%—8í×JÑwT4´²žCÒ: ³hb­ÓÀÇn#ïìUhÍ’, ³qŽzh’dö§÷óÁ§ÁO]¯¥endstream endobj 1345 0 obj<>/XObject<<>>>>/Annots 417 0 R>>endobj 1346 0 obj<>stream xÅUMÚ0½ó+æ¶ mL¾¸Ñª zh7½!!“ê6ØÔvvK}ÇNaÕÝîaÕ rìùxoæy~Œðñ@B”ByùÄÇ7çÇçÕ(H’CÌH £Œ¤Ýª†»Q„mýŸf9ñ»Õe7œEøm³ Oµ+·ë<‡A„þq×Ñs»Øú>¾EÏi‚>B·ºìyÖf•øè£]ÙÝ·Åc jÓ%"  Ø!â$OÉlAQ9°>åúOIëötÓˆZ–ß×c=™ßF>x˜jˆ6—£|·! ÁãÓFÉÓæÐö“éõx=YOÀ|eâ)k ØÇbJIµß8c@йcÕÍ›.érm‚Ûº`2 æ´d¢d6ÌtŸAzaJòØqÛ°µp´ÍÈ…åbG%K¦54Âð,n»[QCÁ(*ôŽ) ¥TŠé£•51ÒYÕrKkX(EO LQåРrÁ §†U°=¡×爥<kf#šÓ‘# K&¨â65Àñ§aËlØF£îüO—i‡1! X”$@@àƒ Xó_èýîz^RÑÐÚºôz/ógnª]M’8‹I–æ8ip€Ä¹µx_Œ>~Š$0endstream endobj 1347 0 obj<>/XObject<<>>>>/Annots 420 0 R>>endobj 1348 0 obj<>stream xÍVÁrÚ0½ó{$3 0ØØô–΄L=´¡·Ìd„ Æ–ˆ$—Ò¯ï®lcC&é©0Ã`kWÚ}ûö­^zŒñ@<é Ò¼7fc|súù~Oo šÐoÓ„EÕC½Öcј…­5rÇ›ÔnåÃÉ­^ónÍZ,`²:ó·)ŸÈóóò2@³…ÑMXn0“(™±ù| ˵Ob Ë´¿åO¡Rq³üÙ-Âʶya¬¤<ËÄøÆ ¥Q½!ùJ%]ç naő :üìQ¥G€[[a!Õù>Njz¬¹ãd>†a0EÔ–ë¾3\Ù0¸ZƒD™J—1bL'³  g®IµíàJàÈÒŸýÚþ¢iÑ• ö\;@Ø3'1y»§;BïÜ݈—Bn'Àò\€*ò–ŒLµAX÷ú£X´Ð&XÖLƒãè-*ùÔ¨,Ä'%¬kVb6;Qs8™aƒ`Yï~s*>ŸJ“š‘Kmò ±À¡ì wîƒ,–Û#\aèô®">ákžînÙF*Ž4Ãàðg^¹Õ»¸þ~´E@õÁŸ½7:ÖûÀ¥ƒB9™ù¦D†§®@û#äú—XWÈÔ¡µæCƒ³}ì?ÞPR­sÏ7²GûÛ'>ÆØ{<ë½G‹+%¼ø[Ô9hólËÖ4Æý-è½0¾H¶âÑ…M¹C™ü~ૼ£A–Hhª?)_M®ƒD>`ÿ­2>£ä”$ó#RåB9‹:$äØŸ°:‚;èk*ÔÑm$ÄZU£¤U“Z²±ÊD—}Z)¾¨ê9Ç©F*OCk8IpÚ¡2Dl ¯ÆFÉþÄk­µ *N­ÔÉ_¢! ƒ/Ž: ×Éä4œ;™î€ÒÎh%ÿ "Pï*TàÑLM¨r<¹ÇJ“‹†ºÏô uäÖ~lNFwÔ:G@oÚœ*äóm50UnS(ŒgŸ´~w6¡;GˆxÒÅÃô ÒÍ`Ž×“..´¡¡X:]TÎî}[¬ ŽG©µ<¥Þ40Þ/üu!ˆb6K¦ÅS† U …ÇËW®P™èÈaÇ,D‡a<ž“5´ Æ!‹g ÖWÂ99Ü-{ßzÕšendstream endobj 1349 0 obj<>/XObject<<>>>>/Annots 425 0 R>>endobj 1350 0 obj<>stream xR]Oƒ0}çWœG–I×£ÀÛ\O>è0¾,1‡a0[6ÿ·EfŒ‰‰mÒ¤=÷|Ü›¾; Ôlî#ˆPJ¨yù>3‡1ŸøXÆ¡8‚-cN·Û+Ê}ÔC[;Þ,z›Aß †ÆÅªªW,6 Œ!¯ÀòÊI /G_мp׳üíw”‘iTF¦‹i]ººD¶zÙm±sw³/âß– LƼt×óù?m&“4ÍV[©.u!ušêŽ‹M8óÆžW•TÄv±ØÄסrNÂ8‹‰(·)²¦Û‹+¥Ä ñd2à^´gÑX¦Ç&‚Çib«±$lp‹†<$<ŠÍ'1È’Ú§»Üyp>å1uendstream endobj 1351 0 obj<>/XObject<<>>>>/Annots 436 0 R>>endobj 1352 0 obj<>stream x­VÛnã6}÷WÌ£ÓuK–oyË.šEŸí(Phid³K“Z’Jêýú‡²¬¸’¢M€8ŹœsæÐßGLé7ƒe³”‡ÑTLéIÿç·Ï£¬˜‹òb&–p€l‰¢[iø:ʦs±€|6ó¸;ÏĪ[ v³¥˜Ñn^1¯x—#g³•ÈúÈiu>›å…X÷‘Ó*î~Ü\–êvps¿¦aSSOóåJ¬WsØTÜÎ6åx!à“ÕË žGøÒ “AYã¯6Žnî Ȳá:_9EÖv+u:á¡qöIUuk(„Ú:(Ï‘%G¶}ä év”ÙÁÖ†=<ï­Fàׂ2*(©ÕŒè4jð™¶K‡ÔOÕ5@Ç Œ*§¤ÄMë±n5j‰E;Y¿€‡ñÆB­ºbL’Î+U×è°‹¸Çd<"TÔu‡c¹·–úÇGžì£:‹3éeÑÉ} MË Îf4&ô^Ì^ä¦ïÏówQWgÿì´"#°®RFºc7»@cÚÍÛÉgHàLK¥· .fÀiÇ{µ#¢©2;R*#›Ð+O£>‰ŽÀáO·ø[ü?ŒGWaÆ'±ÎG{┯¿ó74þïBe'Úï9YG;%Œjø©O{sß_°©·,Ÿ‹u1KWÐ[ß”Še!–‹Uº–çYDïçÍè×ÑßÉÈ{endstream endobj 1353 0 obj<>/XObject<<>>>>/Annots 461 0 R>>endobj 1354 0 obj<>stream x½VKsã6 ¾ûWàè}X+êe;·lf“顇v½ÓKf<”DËêP¢+JÉäß%ËVw“xufì€ ¾ä?3>þ X&U3ßóqåøõçÝLø±—@¼J<*¢È zIÃ癈…·‚ØOðµ~D{Yb-ÛFëW+ñÊ‹ÀI'Ú•«hûè#b‰µQŒ‘ˆlÐv-ð×I¤]¯10¤-¨t[Y•Aà2—=d‰ý.ÊH\T±Ä¨Nbmãzà‡Î±ÛËÒ¨k,eœDøë¤Ñ³Àx”T°¤_'ØdƒÚP-K¤ý¸ÁÒTú—©þMn›ºA_Ôƒõ:„MÎòa“ÍoÞ½ú¼Ùü=í;@gì`¦ÌáîúêêN›Tê릑OWW»Rëû9ëÞ>H}ÿ†}¸z³^T¬M>ÿ­«Z Rkh÷ ”V•ªq¡¬Y–äZúë”s—ŒYH}aîÁfÿŠmPu¶…½|PìÜÊJA.[ íÓA´¸*[0»1kÇä1 ìº:kKS³ê™zû£÷Ö4m#{£3~Lj ¶KÓµe­ [›I­îçÅV¾‡cqŸk3†å‚c©1ì 5?.ÙçâRÿî®·Ÿ]¼²nÁŤ–m½ òE¨ú¨úsU'Õå­/*}‚ÌZÓ8èÑ„5u+\L×…ìÁˆZšôÕáJ=([«ôîk8’©a 6jÄž¢AL*©Ëº€Ç²Ýƒ!0ÝP‡ÔxOÜ@:eÁ4íe] €§ŒÂ…BŽ5Cø c£µy¤0 ]ð94‡'Ò09RÕ>*…ä|4.;¸(ý?°âœ”_ωb›ºs2/¤Ä G;2ÂýsyTLJ×q2c݉3Sã›pÞS§S÷„h["PØØºåa‹CŽ€u¶²6 ¤}—Àïât\Öùù(¿îªT54M‡ ?… ŽÄ3d[N©GÐ+öš)­-"“,3Y²’.•ÓåÁlq‡le¦ÎÞ5¦šæ§ÊbŸš†ö“)kMc¡³´àÜT•¬s×°o_«'…F\Õ‹üõ£þ€ú¢YÿSͤzÝî–ù>p¿Àð8ÀW˜»õ~  ݇žÚ2Ú,Ϙï/uâËyÔ~Ö œ^ /8|F«qøôù°î!ü³ðÅ"ã~—uç.µ…è KM¥I<á øko´êM¨aÑ2ò–É ñ¸%héÓföÇì_Iq'ƒendstream endobj 1355 0 obj<>/XObject<>>>/Annots 470 0 R>>endobj 1356 0 obj<>stream xT_oÚ0çSÜ#U‰ÁIHo];Ð6i[ª½TBNlO&Ní¤Œo¿³C E[§¤€ã»ûýñùGfø¥†%PîF32Ã7/oëB2ƒù"Áç¢0"ñq¥àûè˜â_& ÉÜ%C´ˆu妟æÜiøú—À0N1Å&t )%!ÐlN2‡gdq\9<Î&¦í LbDVg»±CÇÜ(! P¿r»r”:y1&9¦‚銥oQ‚™eäÜ[0ƒ¼Àíõ5>‡ÏUþóÒ#_ ú"ã'-9¬o–˵ÒS7ưÃrÙ·œub]kÛÙ‡ñÕ+3]¡#r&(+çã¼–t+ ë¤n`ȳÐÕ*— ¥P °ƒâÌÚ~'› 0Aj.Ë h{ÃÚ€Ý7|…ûaæ¥n¸tE-X¹“Н¡ßÝi„ÑV¶4²¶XÍA7Û-zaèF€Pb'šöN&ʬ¬±:¾³xŠÄW|³ƒˆÎHùÃÅîm+mPJ3侺鯻”®d‰¦oû¦ôèZ`°gãÉÚ —æa\mØäDjr!Sb̶ŠUCË¿}ý^:ÉÞïþü›¬D—¾¬o6÷þ–½âç¶>/XObject<<>>>>/Annots 477 0 R>>endobj 1358 0 obj<>stream xWMoÛF½ëW z’‰%Y²]äàµÑCƒ¤UP Q`¬È•´ µËì’RÜ_ß7³-1)’Æ~ìÎ{3ófvô±—Òÿ)ÍÇ4™Q¶ë’Þ´—ßzé|ÅÛ¾NF¶$Oüõ§EÎoèòRZ¬Áëêz–ÜÜLh‘ ¥-²þbk­k›UÆYÊ”¥•¦:èœ*Gu™«JSµÕ´ÙºPQ¦‹"*œÝ±¹Ù›¼VåÆk1’‹Åß½ Ç3_äý_+‚ý\³±°¹v»7Λj» 0¬`˜n¯}¡Ê0Ðß)s»²®Kh{åZx;mf8†7 pÀ=lMÆœÏeZŠÌ®±—â]N˾N6ɉїÈàÁÕEÞëP:+áà8<‘ú Æ’¢êàÈùrµõú„!¢žB¥wË‹²u€’8_©1Æ{UÔ:ЋËaJ ­stØj€{1LÎË9ñ¤âWa` D™vé‚©Ì^ƒ/Y½APq߯d pÙºP1­Ñ”·¸JÌ Vû@ˆ<[e*ŸáhãZåAU‚mlVÔ9$Њ+b^×®(Ü÷eŠõcOôÇÚ (ÚÝ!àüwȇ»Ç·’ÊG)äÌ*£ñ°•I›„ÛfëQžòª½¤W#ˆÍ²¿yTƒÑ`˜ÒåÅÔoÚú½;S}( Ow=]œÖO“é@AWdÄñMQ ’k;üG{·¼8æjm«õeÿl´²†…H#Ôžu·Æ Fë²p¢f˜xŠcŒÚ+SȬV*ûpP>ïæS Êke SAÙµÇÑ«CsÐ5sODªà`KŸqÅ€`af?t2ŠžÔ¼jÓXz½6Ÿ ? ð:œÎкžJvIú*â€ta?GEõ3l‡žL€Ïº=™‘¦¶¢…`2æÙx åó€,ÃjÚTAz“\?O‘f⦳Z6µ¥Cüê•w5¤Û7eQûñ<çéóÎÖ,/¾FÜóäÏbZ._g!§Þ_'Ø(ây‚ë ÷î½ÜlÍ»÷ÿÈ‹G*ßHäáîö6v2i]··‘×Üq—ýÿàry}ÌÃÕ<™]OðÓ¿9F×JRúsëPgq /›Î§É|vß$Xr5åW¿,zozÿ ì&iendstream endobj 1359 0 obj<>/XObject<<>>>>/Annots 502 0 R>>endobj 1360 0 obj<>stream xÍWMsÛ6½ëWì‘I$Фø%Ýœ¤ò©mÚ¨ÓƒíÑ€$Á“ I%Q}w~À°U;N2SkÆ$°Àâí.øi䃇?’f1äw#Ïõ°§ÿ÷çåÈ#׃(ÀN¸ƒ JܰmIøØY½ØM•ÕÃg¤ZÊê§Øç37&«ï»AÛ2¬©‡Ñ:§g¨ZƒçYšêu㟺eX“°]—PÍTkð<‹ývÝGé–a ÓvÝ„<«Öà9ˆb#^Ý2¬Á\{Ž(Þ@µ”5ˆÐgàÍ0NŠ—ÖÕ­a]?n³¡âÕ-²¾]Ùɯv0]†àû°Úb•¢4vçó¬6ª@¬rGÔP‹;!YMùju;š.ãv‚;¶þ‡WåX÷wŽà_s~h Ù³DQ7œm Ü;äI;ô¼hDÅU;A4P󦆲'œÆ¡â;Q°á[Qð d'{iÙ‚eÅÆ´÷‘y0ñ)s«ƒºK°;ˆ1­Øý{Á!g°º;|`ðÅNrøÌä‘n&¥È%¿ÃjŒGX“ïá‹höʸ=yƒøÊ»Þëq»ëUÎý6… M¿x1y¯'46DÚÐTœà,˪©Xa¸ëH¤ÜáHUB >fUyl0kP`y¶BʵBwíìÖl ²Ã^Œ) ëW†¿Gáù ~,¶Ç  Â&Ü-½ÝZ0í¤L—]'ÎwÐÿÑÉÖçRl~»¼X/,‰¢•)z‘åÕ V¸Á”]Ý`Òp<¼6Rw“ éÍ›Óó ]^,—²Ì˜¼ z,TÅ´Å®3¨È±ÞÛýpšï7Û YF³¦Ë~÷<àRÊcSÊi9Z«¯y@ÓÄ–wL›îé)‹g q½ç6 eÇ&šÝ Ç£»õ§“©Î™äÿS6=Äör:éDv'%À‹8õÑLÖ'¢únZ©œ=Í+ƒÊOpJ94™0è—Í£\ƒ^¿z>>äZ¦ÕqËò¦¬l—È#EŸ³tÏË镪òÀ+FHÂÅ噸ˆ\Û06& l+þéˆM”åcBÜq±vá'RSE6ÜÕ›%t–ÎQh3I)ë±>Ä) C%¡ÃÃ: »Cù¹ÏÝ:CF³½xZ<ï³èIýü¶˜Q?φ¬ˆÏ”2R҇Δ박ÓL9Í”ü½/ñ¢­§è?¶’8ÅïEÔõËjôÇè_¨æ©«endstream endobj 1361 0 obj<>/XObject<>>>>>endobj 1362 0 obj<>stream x…TMo£0½çWÌ1•ˆ„©´‡®ú¡öÐ]V{©0àØÔ6m£UÿûŽM’¦dÕÂfæÍó›7~šàã@Â*†¢›ùÄÇãëûݵ“:ˆBî-ü˜=Áj“dÄHü¯ñDâÀ–÷Q ×f_³)²ªayA@V!À:Éf³‚¬tÕ}ÈŠy!{Î4HÁ §¦h d¬„|ÙïÙò©»ü9må¸s@œåYTçQ\8üº•9m*Eϰë-d‰èÒ4Lý—XþOböù/.lˆ‹`…‚fåüPïs¢ù„(¬aP J1a€w}Ë:ü¢†KŠ= \¡À¦¡_ ´TqÛêW2m¸Ã] &¬ºAÈJ%¯*æÊœRÔŽÁ\ mµ´Ñ®í”ÔP0»ž<]äÓa,hõ:©«Ú3íA>ŒÄÅÐåØ Ì`ã dOíPŒÀõ ¸¨]õ^É‚i´Vh²n{nß(*t/5ï™rçžT.¨°Üðo%U7zQ÷¬à•E™ÎáM{2®ã£õaL"Ûã›Wj;t9I€+­TëÐ&…Ÿ/Ò×îFÛ^YÛ¢câq(΋==‹É½I½’k£8J‹§ÃÆšF1<ð¨j3NÌyË :) h½µoþyûùãüV*+à89¹/<¨”ì>¢àÝtT Ou 8ïðþ„„oІÛUäÁúíãa§™fG:ýWoóKÀaµ8+"DÅ©´«Øƒø3Ô3MOaÅÿ„ϲ}Fßã,¢¯ÑFî¹Þ¶èat!ö¯)käJ¶­|±F«x=(FFØtï¥ IH”®`•„$õS멟§ämmæâ°HüŽI@øÕÈ–Áh*%Iâoi YÇvë&›=Ìþu#Æendstream endobj 1363 0 obj<>/XObject<>>>/Annots 509 0 R>>endobj 1364 0 obj<>stream x•UmoÚ0þί¸´¥.!!¤}`/­*mªº1íK¥Ê$¼;µ2þýîl^Ú¨Û:N|>ûž{î9óØI ßФ9”ëÎ#¤ÃœÑ\$lù‡tTàˆë×Ù>¸íôY÷†¯W$#×]G°Æg†Ï8Sð­33ŒTd,§Åt„Ï8;.æ Ëpq&äf´ø~Öf—pq™A’ÀlP‡£œÇ)̪¨³²{£8/jX4Ö¯„í\€A#WÎÀ†kÞ@-ìÂØ5 x˵«`Ð̽4ªÆJ½ Ë¥©·øÞ;™ýì\\"ø¼ ’ m{@]pÂC´aÑ/œï iù†óúpž¤ÈݬêJ©(c@É1¡ è0Hú¬Èsò¹^"+@:Ьp+^NcYôêc*%×0°àÎÃ]Wj/¬æJm¡Ä7·ðê%¿¯ßB{wÂà†bo¤HiX†~cUQ§ÌæÕ0m^(Ž+¹G8­X=Ø„$Å/ä¼\‡|]-°tXšÚŠšª#u%Káß ¼ãlOª6ë»ââak±6vKôYñØH+*ÃÊ vRÂî0RL‹D©ŸŒz´ï5ÎwB£¾©š¤Íó)‹–£å³Ô‚[D¸sË_æK®9eä{¥Ìœ+àÖò­ÃLÍ“¬(+j"œÁãG­º ðª’$Û¬åe­$æ\WPOGUMé̵5ä7m¶P½Ì;G—´ÝõBã`£66ȶa:—3¤ Ø{ÔÜ—+áŽlìú” ù!,á iHò5.‚+= Ueºv2l,º{ÂMn‚C¸4–š.nzqW…M‡Î×Ì­i<ò (F$ñ®ËU½â=XÞã0>¼Îi^Þ¼Å^wˆâþðóoOFVWÓû)…§w8ÝÀV@¢5À‰6D_þÕÙÙÛ`ET“ITd¨Õd(z±D•x¸š¾p< I¯6b³e þ:>/XObject<<>>>>/Annots 516 0 R>>endobj 1366 0 obj<>stream x­VÙnÛF}×WÜ73ŽD‹2µyÝÚ(tU>0†äHbÃE™¡âèï{î )JCÛq‹Ú€¸ÝåÜs·ùÒ hˆÿ€¦#ºžPœ÷†þoŽ?¿ß÷‚pì(œ ýr æ3X?eôGo>çÇqˆ_|þ¬~j?¡Ñ…!Û1Oüñfå:Sºº )hµªñlâÏç×´J  !­bO$‰¦ê©$¡”8è>½[ýÝ»ºCFÉÛ< û¦1ã‘(’®TäHõ˜_%Ìo%)©÷Y…û²«;º>­ Á t¹W±¬±Q, Š$éXd2¡è@±T•H VÒ ¸«Ä[‹¸*•f+©¦r'•¨Ò²„/û8 ž®õ-°šOZä’Š}IEåšd&sY:ÇÍ¡˜ï‰¨ 1'~«ÃN‚Àh_1K¨mL'éz-,‘Þ HR©(Iu¥Rh[N¸w™aD¦K¦¡#ÜIt„Ü<ùôSE I1¸™8l"a{ÍÄ@h½/bCåÓV&È.BIJ„™"M”ç1Úª0ñƒ) ¶ ’ÓJ;~åzÆ)` ħrŸ!ç’2©¶þèëWp´”wp€›¿ö;š ©P=Ÿ2òrTkºË„ ØV' q£0s–Š9ݨ%×%¢æÚ0I‡²®+K&)ªG#¿"³uÝhüú#!RÎÂôkpì×Aƒ‰ˆîJU)aKý8LÌX1JÇš!½T‰‚*$mÄc²‘yþ౪ËרOyŸŠ>}î;œÃÏ«"Ûma3`'’•¹éáÝ÷â æeÇ&½µŽN”̘gñŒGP J¼¯Vôl˜BÔ+Óä~ùøƒ3Þ E ÉÞaŠ~C´|AÄ|±Q¦#ÒÂy9ì¤ÜGkCg&|{½ÁÚŽeÌjÿÖÞ¿g¤ÿk‹Å}VF"[òè_,êZy‡ìß!ð_ÔÒ9£1&_E÷Ë30—†dæ[rô¢ À ï¶FO7â±Ã~• =œs›yĽlzüÛÀ^Ú=¢ŒëênR÷éhDG3tK‹¶V.Ë݃GK4†½»Á½7sykʦ28µÙªä¹ Þa쯺ËZEG·…|®qj°U¦°A¼xðÓXä—¯2¥o7/Fœ]ÁBaúa'ž;FÈ}º±ïÎŽ ®ØmGvÛ±ú”V[WåÈhGÕÌÅ6%å®Htp­}>9Yä6ã5%MšëpÏq úŽigW¤FÜRT—Ù3„v™rÃ}C´/øóBo6Í/N•:Ô«BÞÉ8]§õ©‰{ëÚ æÅÄàð€5ˆŸðI m*g½Ö šœÀI£Ì²òɶÚIfxøµ•ƒC ”/~¾°o[fKåÈÑEá =Ÿlî^Ø\:Ò¼-"¾{ÕÊuˆ&ì ª.ú¶-¿®Rã¸ÉÖ Sêô8qu7«‡R0úáìšÆÓ¡?ÎxÚYOfØkúSãtûI{‘1ŽA£0˜ç,=ÁYú#اËl##eŠ<œ†þtRoäñŒ5\õ~ëýl]q­endstream endobj 1367 0 obj<>/XObject<<>>>>/Annots 545 0 R>>endobj 1368 0 obj<>stream x¥VßÚ8~篘·ÒîâüR趬*õîš{9­„Lb UˆibJé_3v!¢\VÇJ ãñ|3óyfìï#6þ1pHw#Û²q¥ý÷×óÈñ<Ë?t¬vàĶå5R_GŽoãºïÇV µ¡e7iYÈpÝ÷´A[Ç#[-u´®‹ø¨e!!i©£u0˜‹VK­è¨\Fß¾–.Z/¢Xwà2=©£õ=Ühµ¤µ~„V®id“½‘´–Ѻû&_ÛÃo#u´‘cò‰ GK™‘­Ýäë2ÒjÉØ:%C~)_3Ä0RGš|ÈE$¦%Ò~Hðà:8O“N¯ÚÀtÁ€1HÖxÂ~XqìB’éõ!Iǰ•ªxù6ùÖ?xmXÚ·æ¥Q¡MóYÊT岄 _æ™T/ãÍ’?Âf¹zy«ñ`Â(Ñ$#Gí'“‡U!`_‰4¯ÉüŒƒ0Ù«aR¹Ûâ'¢Ÿa(œ_·pîÐÃb$³ö Ñ‚» YnÚÄày¾ü¬#UHra~4|L®½\ìNš%ÄùxǾDÚÅø¨y}jø@Œîa 'ãá½ ¡£OÆlö\È/æUÅO³™)‘T–µB¦®”ï¾Gò7¤ŒZÊ®g3SI¿÷Ö!ñ¿<õ‰íy2µö:OÓvvÓ˜‡f#6 •òA‰ÔV€(ÄN”jrÌkå¡°_dvHÈ5¨£N¤Ö|V×Àð¼àÔVœÌ+! {^q%Ú¾¨!•U%ê½,³¼ÜôZTIZ˜.‚¶ã›†7ËçxÇýmý~îí^f¿1iz·oqÅAö¢¶H…'uf(AbÖ²(äã¿$|¿/N ¤fΙd9RG£…pÜJbE³²,N JEKJyMɲq|ž_kYÁž«t r/Uľ\3µùëä„<|C}XUò òRИ«O»PUþKèÙÛôË}¿ç”Ð/Í9ý1á^]û½éÿCæÍ·¯Í”{µW=PÐï`¯½†ëfmœßl§Ë¶vYú s¨Þ;56–Èà˜«-l±<±@(–颽íp$˜•¶ìgFn»cïÁòἌçó7ý8Æ7K÷ÿTǺ®‹ƒ®uìõÚÔF÷^~u >©erqzû¼ÿHi¯`,§ÁuÒg¯L:ùßÎtE÷Ï­Ó˜­=Ž×¦€úE° æoôYOøÓ#…¡åE.>‚C+°#íæ"}ÖðwϨ?xyà1;9LB;¦Ý>š¾`‹ó æÅF¬*]Œ^èYaáS·ø1Y~JFŽþhˆ endstream endobj 1369 0 obj<>/XObject<<>>>>/Annots 554 0 R>>endobj 1370 0 obj<>stream x­VÛnã6}÷WÌ£³k+–íø’·´ÝÚ¢÷i³(ж¸•HU¤âäï{†’|ŒEÒ6FJ3œ3sæðò÷ ¦ ~1-§4[,“h‚/‡ÇïƒxG º™Í¢)4½YFóö-§?Zë|=ƒ['ÑŠš·`M£ Íã9æÀ/ÙÞŽÖÙ즉¼^·ycëw›~2ÕŽ®ï×Ç´Ù"ë›Õ"Z¯g´ICÂÚÈá"š"دÂËL¹«Í×Áõýü0a<] M:Üd浑^[¼Ú‚mé(nçÅk$}˜jÆ?pØI4eß9¸c®0axo+_ ÓXÏH áàò¹:©líµQdvâI¤éSÉ?E^fbD»'þ™‚cCycb6κòFää?ï1ï`mÞÔ’³`¶Æ1…ýÔ‘ÊU¡ŒïµSä÷–¬ “’Ïøˆc¢R®Î½cXX,¾UgDŸž•“­wim½,q¸0š²ÛϧP{í3Jõv«*¤‚‘ó•Np4 ‡d«“ËD‰Ü‡œ”©‹iØmW„£¢vX MÎNêñjÄyô°m©*Ғ°»z)•qúYádPRÔL*Tt°ãL#iK ð}¦eF ÙJO…*lõ “quQr̈6È åõ@­+©ô¶‘¹J)y%A[!=ªÝâŸËë¸kv5iG®•'eåhHª<瓤¹‹¾ôÙvè, S/Ë_Ã&Š@wxÙ…µE‚D×cc´¡¦ýè)ƒBrðq€ÐǶK¿W}oϤê]Ö(ø‡^«”;U7áаPÂö:Ï Šƒ°X QøôSèåÍϽ.sD½Qœš5€9vZ ¹Øð™ðL*$Ž(Ò£ ”g†åj,‡¾Š¼WRj ¡ÍQ1С†ìðu¥C{[ý嘞y}¿8Ü}R['yPåq]yÅ]v„¼Š2W/µ»É )^ÅŠh4RVÖ[~G=請½-q_]ÍpYœã¢¶âÅßl¢vQG:¬¤Ÿ…©›2Çq;a¼œ¬Ù7Jú W5QÑ]¾SI%`¾œGËÅ ·Pv™ð§O›ÁoƒgÌendstream endobj 1371 0 obj<>/XObject<<>>>>/Annots 561 0 R>>endobj 1372 0 obj<>stream x¥VmoÚ0þί¸o¥Ò$„·HýÓ¨&­Ó^²©R©“HâÌqÚõßïΆPRÚ•®•ŒûžçrÏÝ9¿ØøïÀÀ…n¢uöl|R ß/Ž×³\è û– kp=¯Z¥ð£áØ=«½nŸâ®íU«Ý®çð)îŽl˳¢ÝI€t.ÑyèqÊ%œOp葎F]bí’ AÔ€©J²ì4¸«»«íKÛ7¡(C)J•d–l¾fj]¦óœ©h5kBÁÚ Ã6°4_á2䊵52tz© &Ê·ü-çhÏ’TиJp¼Óó;œ¿ 5b¨CþbÔ†H#F1Òˆ"ÎNM@_ÄYÃ]T>˜˜ü[…{‘Äp9ž_íÅ?Z1 ­­ ´jUZEk£áwö™5`ë8¥’Lˆ&F$3#¡ôLg5©ØQÁ&¤åU£‰QÎ̶4FÁç4ÇijhPJš9ÍlKcd}Nó.¡ÏÎHê7 íû—©Y:–’=ú¾)»¯¦ê^Qý¡·yïQ=Y¡à‰;Ðz1 jºSöeltŸä\.„\äÖùÔ…µÀèÕ¹7DãëB™æî-ø¦f[0ßPùºR}*£[hi‘ ØÁî:™ÜPÕøºÝQ| Á-ÌNáL÷æÖanãîWzXqÉÍn¿ºÆc¸‘ÏšãÙi&“Íj‚«} &°,®[“!\£Kõ³I¯±Ø·p]¨"§Yµ1r^cU IÑžÔ`µK§ ¿XŠõž¥%/Õ2}3&—åšgÊßw¡ 'ÙIN¾Ð hê4YØ;Oƒ‡X(ôK¦‘A$Öy©RáO9ñtd¥s¹qáù¨³&úS$aú¸ó.FõíË 0×eȹM…dX#É9>/XObject<<>>>>/Annots 582 0 R>>endobj 1374 0 obj<>stream xÕWÛnÚ@}ç+æ‘plïúo4©­zq_šDhm0265FJûõÙ57 Ó6EM„ñìÌÎÎåìÙå{Ãÿ pL`6‹†®é8²}|5L›k:XVW³a̲5^H1|i˜–®¹`1¦™¤åô­$©•s¹íÚ.Ú*i7—›ŽòÌlüVi Ë@k溸úLÎч’¤–[$s£A-#J’Ú®ãLwÑj]Fž¤´›kb&ä™6j•´§Õ‹Œ”VJ;ÏfMë2›¡%‘ö·) ]T1›ÂÍÐÃï Êßrm­ÛeàMduð‚fÒ\ysœkÒ4Ž.©8·9L³<‰Ò´FzFKé¹ %y8 3xZ'A¥ $S1Ž&i>^Š<˜=4§cц?"Nñ1‹Úäµ#ƒê”®7A?çýMÇ>:ÅONýYôp%c…*“tíÇ!,³0ˆVæ&^ wr2\½Òñɸ7Ô ],ãðù П—TíʣȲ˜´1UÍ×€S%š" §¨ÀS‰¹šAÏ]S¤ŸÜ€/ üù ©L´L ¡â¢±—á…‘G×?¨ó þ)q×d·ÛÝVƒ™î fú0êïö62ö )˜‰¬%©‰†žîÛÄgGø i •¥nƒ©xQ !I á !e qÝ?Ö¤.Jcpn{Ûâ’) $ûÞŒF™|«›I©æ²S¯ßŒúˆ¼¾.º_‘£~¯7ŠS_Äý,?z=:.?ªÓ2H“U%‹ÖD‚Kµ©€áv¸ ït(Ÿî¿êR¢½Å'K±‡êÿ¼ ‡ãE5ˆ‘÷«Qjxɾ%i­d³í~ÅK PÜ ÝÍMÒq4î2¼Lâå\wéÜW@‰ä|]áUð½HÖ"V˜-&t½KÖ6^÷ÞEI(2èÇÓÐÏ™q‡kŽíâO21iè­×øÔø”Ÿý—endstream endobj 1375 0 obj<>/XObject<<>>>>/Annots 605 0 R>>endobj 1376 0 obj<>stream x¥VÛrÛF }×WàQq$š÷‹ß\Oì—v¦MÕ¾ÄÍ’\™Ûð–Ý¥cÿ}]’²å$’{† 8gõ×….þzøÄP4 ×qñ/óããÍ"BÏsRhÀ c'­þ^Ä.:ƒ,ubrú©“9½0  œ¼˜(-ò¦™ƒ ^fIŠŸÖ"g”P¤ã+E„b,›7p\ð3z6à»j­9¯º64¬5çõýÄDzæ%k=Éë¶Uì QŒEÞÌGÓ ©0,)‹0¿µÈ‰0xAhyH"µ9ÛÒ*ïàü³y°Ù!ÿR“elJC½ ›b G~ŠŠÉ3Ðù D«!¯»OŸÇ¯•øôùöݻ͋óëpÆXû1Ò´)—›ŠCÏtQqªê†º„œC·Å%Ó ôc/°¶„ŠÝ󽳚œKzŸ×¼á­Vü.¾Ø7˜”ìXY -ºvE5¸°F }B#F7Èb_ƒI_ŠÝŽKÌ¥PZŠ| ê\U ËXA'Ahüë $–¥%kUß)¾2…u=—ŒàÛÔOÍ•:€ç»(b¬°á‚ ÊÔ,9SÀÄhö¶~ް o:ùˆlÕÐôâXj3T˜ú"×!¶ˆÂËÌú›@Œ¹8SÏ¡$é¤É³7±ù°Ñ–â^”«Ár;‘Øp…:芵օb)h’W`õ¢µ’ñ–ï -gò€Vßñ;Þ—èÀ5öÌXÓ× j-úZ–Ý'¢ƒÒä%á˜Â @š(Póú…%=Álí¦â‡$C1N0­"|x´ÌÌD/Ï3LÇÁ Ì{Ël01MÀR ¹ìðXµîØ–åj{Ïêß.ï¶ÌŽËÀé_Á1,/v¼ÌÇs'bãûN”ps¹½ÜÓàž¾ΓtõþýÑ"Fä÷ââ¦îrV_Ò„^\`ûÿÚîŸû5Áp¦ÇÓf2Ætõ 9Þ¦aÔ?E¯ Ã刄œªü@°­Y^F¶@Ýᣯpå˜Kx!Û?ò†èôm¿~÷Û÷Ôãt²–?—òOÚé·ËVózi ѧH;]-o•¶,·´_5kõ¤+«ûêµcùUŸ!Ï’Ñgð†~Uʲ¼šyxYÄ^Á˲4ëY#Ò ï°vÜÆ9^¾Dè8+ªi€uXOX6Óú„Øibpխ̼VbõrëtðsžÛï2ëürVV&ðì‰ôç×éxçxIâ„iQ8ah®{»SÁ,Uÿ(¼ÿ`-ÞÖTëz X'nvÒ?a:IœÚ‹#(ɇÍâ¯Åÿñ˜4.endstream endobj 1377 0 obj<>/XObject<<>>>>/Annots 632 0 R>>endobj 1378 0 obj<>stream xµVKoÛ8¾ûWÌÑImE”hI°‡4Hr*°ÛÕžê ÕJ¢*É üïw†¯$NÚØõnÈ’óüÈó}ÂÀÇqay3ñ=gÜçãÝ„ñ…—À"‰<‚…©†¿'É’Ä5p‘±ÇŒD‹‹˜4ý¿¸ès'Ñ"ã!*ñeèE´-œäìrîkÕEäqÐ-F¾·ŽÎ”fyK#=±Ë8ª ] U•äì†3N—^ˆ‰“äì†<ÑšëѦúôù¸Hþ ÈÌûi(÷ì$¾S'"IWyÝlë±êêÁò~ÍÏ}~\VBjá¼yîþ * =sd$'ÂI5ùàJ‚l8ŒûÔ çÄ 3•-8¦q§õ´ÁÏ=f/XáZ6Ýv,EĆ—5;t½,¶¹#éñA‚¢‡a/è‡M…”Þl1ɬ´D2ˆ¦„qסaÑ ÄvÛde;ö ¯ƒÈ&€~æE…ÏÃPÉ™Iû´•²§õiòÕ´šÁ×ÕÀ HÀñyfÆ™ULñ}êËÑÕ4b‡]-r ß ó&U-–Å8ÇâKw1žøDQ-ÜuZ[ž¢£ø†"GŠ æªNvFb¦Å|9ŠùÑ j먪äº=Õ÷íu ¯Þ8᳋‡Z*ÅpíÐ]G çö äw),Æ£3, ÍaYìVç7xý€Gc~ý)~J†Þ©9z•þG>p5¥‚¼‡€¸¸ML;ÈâØãIˆí$ÇãPÿ£;BP-áÿ x‡?ˆv+jÒœ[…yì/iwäظÞà9ÆO3‰ìÊ^Œxuð˜{q”`ËJ»9¹I'Mþ Ë.­endstream endobj 1379 0 obj<>/XObject<<>>>>/Annots 645 0 R>>endobj 1380 0 obj<>stream xÍVYoÚ@~çWÌ#$°±1RÒz¸•ªRY‹YÀ‘j¯ƒÒ_ß™õ±B…TÉÚÙkŽï›ùÝÒAÿ¶ýøQKcÎÔŸ/ÓÖÐa˜¦Æ†n9Ì*¥¾¶,§MÃfZì[Ì,%ZÔÍ>ÓÀÔMœÅUÇdýR¢Uu¯a8ÅQµXH´8ÐP¡Š“¶ÁœRÚß«;h&ÞkèC´¡hõ½Ût"]ÃÅÄ]w…ÞZÃsœ>¸K娮߆ÿü$Î$L/G£i˜,xx™¦üÎÖÞ¢ A,a&?u;îMKƒžÞǻ˓›¡mT¤túJg1¤Ùy§ÔoP QÿUms)2"‘ˆeod¶i²Ì} ÉJ­É][.ýÈ.ì6¿(G÷¢ÚžñH€¼ÛâÍ<^‚ã—2b/`2ï…št~OCv—ÝÃÒhD±ùP†æÜÈZ¥ãÔ;®£ÔX½ýqPýÎ Ä*˜ªŠoý‚ AP «áð«@³Õ!ð*6 ©ÐƒBÁ‘&]â¢F¨ê˜®ˆ=ö4F j`„!1S&$Ȥq³þz<½tgßÇÞìz2»ž¹?žó>U1äÏ×ÃÜó­Vhç!: |ƒŒFŽãë}ËÇÈê]ð7ÁËAy^×yUñTTSFÀCF¨í¤£HhnUkÐæjøv5tb(öŠ04w¤Ð=ÎÕ5WžŒ˜>Q…œ·á[¨À5RpõSŒ|”7Õøˆ¯ÿ®óiäÅÃép1Á¶Qõ„ºm3sØ ÛÙ6$$m¨¾-ƒo–¾<ÎyHÁëUz¶æÐî3°·¥‚Y7LÉV¤\bÓ¢$Ó6™=b÷I»-ºdì¶>·þJîâÜendstream endobj 1381 0 obj<>/XObject<<>>>>/Annots 658 0 R>>endobj 1382 0 obj<>stream xÕVYoÛ0 ~÷¯àcºÚ®o;öÐm°‡;¼§¦0dÇ]øH}¬-†ý÷}’í¤1ëºa ‹”D~¤HŠ7’Nþ:¹™E¹¤©8›áË\ò¦ªA–ãbÌIwÕ驌¾JަÚdÙ–ªñEÛæ[ÅuËß²4Õê¡k|UP|UÈ5ð¯%ÝJ9¹MÒ(¡¼­ ãa{Íò˜šû5D³bI‚,Ú<Œ+ì èõÖ*•A‘²L¤NË‚eĪŠÝ×rÆp`4”Òƒ_´˜¤2]/ˆÞãæGa?ôah×mÖÐbáLÎîÁXg,øbc@ZÀ1½rìdw  YªÁ#ƒÎÃE8ÙÞ8Y1§„)MÅŠîÄNlí\Ë„ê6¬Ê¶I¡zÅî‹ gwiÞæ‹É*`2­‚Àú õÃ¥œ<­xëÁïeº¤ùqpº£ücÑÄ+Ü—±%fK¼Ôá!=Wh6›geȲc³wÎùà›¨,qóã=ôNÀJ¶ºµôµ³ýJ½Ö!D÷çV¿C®ðÔê¢|„ûÏg½"³€úÇ]2 éõsdõ”_A ZŸf,+e"– ”t)G¡à„='’)Œ(Iÿn*np¦(Ç"ùx/.eÓ$¦¢XpÀƒ7 ¨×†??/„?TÇpœ3€€‡.._æ£gWz²4|â¯jûžúð¶bð°¸–­GVosƒ¾îûˆÍ(Ûo»@FA²'6Hù­ZùOÚ´AEYDè—²øî¥MÉ#¥óèÌë›ÝECç™èÓÐVjo"º×ÄóWÓ·¯ñ9+Z–ñ¼V†Š«MùnG5T“ø#¾éâÊu\±”hÚ,×R]ÇC/Èw;\È©/}–~øÛêendstream endobj 1383 0 obj<>/XObject<<>>>>/Annots 671 0 R>>endobj 1384 0 obj<>stream xÕVKOÛ@¾çWÌ1ÇøÇIÕE€8TêÃ=­%Yd¯S{]@ÀïÌúE,  ‚CA˜}Ì|ûÍcç×À m˜8àú§Ë´p¦ý|?LÓ‚±å›¤`O&渖ø1ð-½©kú´8vM¯–hÑö\<êΦàX­j‰Vµ^×!í¸h;¦ •Ôêuñ„6&âÓR§× œJ¯ëà®J¢ÕÏaÿù N<°m/ñ¶ãÀ7§SÂ¥¾¨a<<»µæ°dŠºÝpÄYºIøA+r/¼X0²]D.‡ðè'ž…Wó=3ç,O²›;¸g4³÷ÜGÍø®×"á ÏБ²Ç3–Y[p|¼ZN΋2Q0Æó=Ôz‹›„Å2‰d!7¥–çì¶Àì B­Z—é'ñc“;5 ã–„‘;EŸ¡“,W9«î׺]ÀmC(Ê(ÏJ%Ð4¬Ø‚'<]¤BŠ´LçÃÕ‚8½ˆôáþ‚†Ë£ŽËêÔVöPüÎÄNÇ[æÏ¤â+ž#„Ñ ˆ¦ÔÇ]PíïÃßáôÒlvšdKɳñó¥¡'Îd¡öÖø ñj#OGVGÌΣç-b|Ô&›P;Â@//t n.ÕèZjÿ6±¦®³:Òz 1¨ã5¤%^2j#³`)×ÉT“KТ,Óý”]öÔV 0ì,Â(D&1¥ªè6z°sÒæ Pþ y×dŸMî=ôŽÿO ¶Xl˜Š×už±$3€­±èè„‹HŒHD96 &âµØ- 5=/V€Žð§²°Å(¤ª‘ˆõüÂ=\‹fH¸{Ð)݈, ä£jRh‰iæó‹Ýøy»zðµòås)úŽU¡¡öIϼ±C[õÿn‹eºóÞû¢ÐvºÐx&"`ôªÒ¨s”½¼}m=®ØÈLÖ½ÊûTÊ·mް8¿®9:8 êö…ºP/p±“ó°`ÔÆTo/èÇ·€Ÿ>2_˜,YBÞ5FkJ»}“ÚMê!Ú§.Ûðœ)|o´ÿ¼‰gNü»EÚ=!%ÇáàÛàdÂâÛendstream endobj 1385 0 obj<>/XObject<<>>>>/Annots 702 0 R>>endobj 1386 0 obj<>stream xÅ–MSÛ0†ïù{ ÐK¶e;7ÚB¦‡ZÜK'3‹àŽcS[¶¿¾»’?b& LÓ0ɰ^ißÕ>ÖJ?G \ücrð,×#×qñI÷óu6Š„@à LJ509ncåp=.9=ÏáäÄAac‘“ù æ½ÜåNÜXäbœä‹ÐNÅ@acuqýÀ73™;X«ëû®‘j`ÖêâzåIù†˜·µº¸^èÛ™˜¯ Ö²q|î æ“o@^cuq9Š‘“ ãZ‹œå·Eò"\ŒµlÜ3ä‚LuðÉk¬>®Û–JÆENS_Û 2Î1‚µú¸,f5T_¬EÞ÷ÉS˜Õ ί|` ’[¤ Ù8ö I p’å8¹SP©z“k˜—óXËßøà>—Keß[Ð8&+î7dUÉß5Ž”ç7óç$ù’œÞüaôÉ “œ I‚㫲ҕ,ìŒÁ7Hr õæ¦*7:Cé•\ÔwÙ­^¤™\•…ÌçãÕB¾LseFFåÐ}v«?”Y ³‹ÅõݬÐ`´Í€Ó38;ƒÝÒ”e#?ÎòòFæTîéÔácWƒa 0á´Sq¡iZ#«¬†eYÔZbʺ4ðÚúÊÕZºn©®¥®²_ûplE^ËQ=£ˆõ|8Èp‡Ñþs þ[1*ÝC4ˆðM †Ó­¢ô`¯£MøG$nAÒ+ ™¾3£ŸÂ~PK]V¤pàTUní[ÊìßÃþ>mh¦»7ä6+-¼ÇÜh¿‰%E¬÷AöÆ^+Ót‹˜év¯Þ¢»Û]Ûi±km©iƒ¾Ôi±=»C¶hÓ{Õ³ÛoºgÍØœ¬ƒ½{È«¦6GéÁ€wƒíNÛvƒ,fæÆ‹‹ÞOE­ªCš0.Ô½=÷ü*jîF,Ä‹jäA„Žp#:Àí¡æÔ¯áJÂgYldNEŸ´&¡ÓháÐõïÒžè“Ǭƻڽª¤Îðô§>ÞzCáÅFGôè2}ýxËendstream endobj 1387 0 obj<>/XObject<<>>>>/Annots 733 0 R>>endobj 1388 0 obj<>stream xÕWÉnÛ@ ½û+xtË–Qni=¤@õfÀËŠ«BKª%iþ¾äP²´Ž—)šÒPÔ|CŠþ9`ã¿€ÀéCœlËÆ'ËËíõ@…–žò-rž‹w–2¸ø¶å8–"¥t,¿“H)\EZ/ħ¨ ½¥DZc× %+=Û’ÀÒÒ®«lË¥Ž²B`‰íJáúC­òðÎiC‡”¶`¥ôÑ>KÆ®D7R)Fxˆ›%¶käÀåhðî4i ^i+Vú>ÚgÉØ¥<8¡‰Ôõ7Kl—ð:ÊáhBJ(KÆ®O[…ÏJéZA'­ìÚ’ƒA¼øª‘VvEˆtQòÊi?EH¦CdâE£ÕÆWÑ=²N,†¡„hn·!ЇWeÕTº8Š~¼> f3Ú7›‡P·³ªl›´H`¡§u¬³dZ•Oõd‹©>¼>NŽØÎ[ F‚b‹æÃ Øîú¿Ç2ÃõùônÍoZ4ìš{z?9ÙÙçüìì:+g:;¯*ý|vfRpk2—EÝ ¼oÀñZJ`äß¶‰ †æ{”>(ïqÖë¦JQ@ÐÖi±0o<&qSVdÇÚ!³’ê]¼‹Ó¸ÌÓ)݃QxI)»ý Jß ô„ ¡D›ÿ’S[ƒPXëx ^•lQV¹è˜-òí¥:êl­,l-©,:ÿžá/ìyIî¼lgYÇ;¡è,®Pì^¸§¹Ëà V%zQæm“p‘Šmà2Mú*Åš\–¦þ€Ò$Ó´¸O‹´yî«s—ôÙ°JÜ!ÅIÔ­9>ˆÂu ;“·™»ÏË4ü7â3Oæ©.Ö>–³Slë±ùnîPŒë9쿉۫q¸l²7û%¼@¼@ ¼ØË;ùì³²¹ßêSslWÇxýþ«Ínfã&îÚî†^cã)‹¤hžÒ:æŒ;O•$ iî¨iB £ƒÔébÎg¯²TãØÐ50¼·Yƒôòœa¬à¶ÜˆhaõdˆÌc{{Æ™Ž@ }×K 쇽ÿÉPgcŒÄô½ñ•êFD–«$N™ø‹ÁVôây ÌÀT÷:©àF­Îí¨ß0 ìÞÆgõË,É1#“‚ò!©t“âxE;\Í¿ûºù!=ºŒ_¿’!Cºendstream endobj 1389 0 obj<>/XObject<<>>>>/Annots 758 0 R>>endobj 1390 0 obj<>stream x½WYsÚH~çWô#& ëFò[’ŠýäªÝ ûRÔ M$%:X„ì¯ß¯g„À”ñ‘µmW¡éééûëÖ蟑E&þ-šÛäø#Ó0±3üüu3 BÃ&Ï Ÿ ²|ßz*§Ï#ß4<òGÀt]f*Š™–ë&¹¡£EÃ\M17´Átæ,Q6{jÐëø––´=#„Li½¬:n`¸,øxjйÊ_Ûó¡õÚЯ©A¯íò&˜ŽIMi½ì¯m÷¡*½šb®ò×ræ:Ôyh8PÀԠײÝÞ¨‡mM)½Þf—Dsà~X é6'?g¾NèòÚ#Ë¢Å7TÇã$‡-bU“ÑøºªÛZ”‹ï§SÂn/<¦¦[×U×f¥¤D¬ g¢\mD¥Ëq²Sy…Ÿ4›R²ZOiÍäZ“ÑTé§™Åõ]Äcú+Ž´âbZ0U¤ÙòB‡ðPüƒùó¾­²˜nÞ¯n•-âDؼ@è_¾öË4Û/U˜T`ïR ”ˆ´Q¤do‰Ù/“UñRF‘eheóHõ—¯ÏKö»wôx–¹"}¦¯®nòj-ò÷u-~]]i¨ý¡‘UeÓ¢wÎЄ¨Å äý—~õ€€þ¡æ¤×ª&oéG²:‡Œ×ðBáâ^„ÐÌæ™ŠŽÿX›®• µ©¤DUʲý™5’tcQõ ¼ZJRC'U{©&e !aq@ÓVu¯Ï.o‘qèÉ­b…A´€Ñž½Ë *Ä/llrI‚'Ú¸„,œXŽÅåú2Z^Ê胳uæð°G ¿3Py¢6­Ü¬ ±ë§)’xóyͦ=½óê=ä0ï'ÛçÁ4Wr˜jʃ¸êÖ¹¤Éó=yrÛŸk|6yË 9ÓmÊÑ{ü;È£.{Øå¢N$ÆG˜dDÈ ÚÊâ|7¢¥H”´–$âXž‚­­4´A?Ó Š~H¹ÉÊ„¤ˆR’¹,€m)QŸ>ÁmY•¥LD›måk£ë\örŒ@w»)ÌíV=v»<ï]§!g¿æà‘;¥Wì×~]Û¯Ù»ãr?ÓË'ãñþ×PFø{Žpü¤Ê‡^ûÝÕYcªloiî\úm7Î5Ù¿R÷W“V]s‹uŒQ]ôম¾£ A®«Ã>çkf#E¥¯Ýdz÷i÷Ù7žÊ{§tô7ΉºmóÖD]¹5³¿{LøîÍи¼úû»5ŸnààË øE¥/Y¤nY ýÝÈšnEÙ‰œ%g{ÙÜ ù´oØøNù¤GÛL½·«¬1Àp#c `s?ÀgNÏMÞú´ý9úÍuendstream endobj 1391 0 obj<>/XObject<<>>>>/Annots 769 0 R>>endobj 1392 0 obj<>stream x½VMsÛ6½ëWìÑq,F¤¨/ßÒNíé!3i­Þ2£Hˆb  ÒR}ßÔ‡•Æ®=nì A,vß¾·Äî·^LüÇ4Ih8¦¬ê ¢Þ~þ¼íÅé0Ðh’DSª(Œ¢a·Rtכ΢„Òñ¿ÅÓ$Š»oŽшÒtОŒ±št+Þ ~ÓAÜMb¶ +Þ¸›D)ܦi4î¼õËü¥-èÃMJqLóÒMÇÑl6¤yÞf2 yvA¯ü+µ§Ë庼¢ÜÔK%éÒy¹ùòîÝü+P$Ì~b&¬E1: èÇCd6Ï/~}ÿž‚ù#jøSætûñúúV™¥P­»ëkøIl? Ÿ­¿\´€„2WžZf´óçé’ŠÅòªL0¯¤á»c—Œ1Dýd ™¡²Z /ùµ$%l!ºª•/7àÕ¬HP#3o,㆑ð” MKI"Ïe~–ˆ70)Ýñˆ ‡u G÷RnJ]Ùš¤’•„„ð~b}æJ­e!|ÙÈ(õ¤®ÃÊÝë­ÐáÄSÒºziMíK-© –sQ‰m²ØE‘îv{EÛ-³¹Ý² Å¢i®š†_4MxmõÖb²ÏT©}”ºfuÞ×5¿gÈÉËjþyVŽ%Õ•ýâîœ.²@N[n¡î‘q…}Pu&èÑõKŸZçøîñ§U |u{â:,-{oгf%ºAŽÃ‚1^G/Ôæmî£äôB:—æÍÈ8ðÇ7ª °u®Þχò³ å?só}-ý_¤<[¢Ï6¾kÈ•ËÐÜÚÔ*ç&P;™Cat‰5_Ñ(°\šZç¤øfuRØlÝÞßnfš/Oýdîë1—ßµ—v%2îB†æëÒæôYX¿£;³òÂJ,³{ÞÄNÜɦm+›¯%ìØ¶zGr[:\î¹½‹Mç‚réÊBè ]ÍÕ´I,-?# •‹€EîZŸn#³R(0pÞò¼A¦påÌþ(.‚J”Úá–®Ñð„;ÄaZÂî@i…}YR÷C‹ÅR€¹¼\­‹öHò[ˆQ¦> ð88„‘„Ú™Äqð¦Ì%q'|:ÙH Oè¶PO:I{&Žºt3Úa.€*˜ï2¡ÿë-é BÓG¯—Jѽ6úH»*—VØ]Kv—¼P…ÄKLHÕ¦ö!=ˆÄ·å²æ"ªdep*˜HëžÎeÈ @ìáXZ”``šÓïÂ(mÙÊah1 4aô€€JÜÇÛOJ¨ZÕ:c-Ón¨Œ'ˆ§¿1J'锇Dzü夥OB×BñÉþþ@2˜±õ+>t’F“ñÎObvûÛ¼÷GïúANendstream endobj 1393 0 obj<>/XObject<<>>>>/Annots 794 0 R>>endobj 1394 0 obj<>stream xÍWmoâ8þί˜oGoKš„@é>°Õµª´»÷RVÚHÈ$ølÖvh¹_c; $ômÕê´»ítì™gfüÌ ß;øø?€8„þ’MÇ÷|üKýñ÷u'^ƒÑÐóaaya)åpÛ ü7„Az#Ôƒ‘9k¥#í`ŒgðîÀÇS+Y­µE毨}<å¤ÃÝ(ŒíÝ`á)'iƒýÕw­dµÑQöÃÐab¼ë$« B£õGÖ¯³ì¤ƒ6›¸*ËN²Z‹9ˆ‡%æ-9Éjm6‚aßavg­t¤Å,8Ìö®•Œöã “šäGXS¹‚‹«‚fVÉ”`<îÃ,µòa–tàJH- ‡³Ù?íêYhÌèãš®¨„¬à‰f‚Ê,”ÈwtÞ]-È9¬Ëù™3ó4Ž`Œ‘÷‚>Æ=K»—Àü;ºeßÞo8ÇšGÞ`ãìr ×ÓÅ­ƒƒH Br¿¼VÏz8àúðá=qM&×¹X’|*%ÙO&eÒÁ•AÝЯ¹ËãÅUT&¿1a½pˆiÀ$X ¨½Òt"ƒœqJ$Ðï1µQ&MWúxS4ü ~ƒNQîzp£!c‘ܳ<‡„à‡^S¸\‹œª»=d$ÑB²­]¢Ðè O1½™­Æ¡žªHªTVäçΜEjí•Pï™^7íÛ#qdŸLÈ2§ E ¶¢š¶‚97–90K [Ù§¯µÁ“˜žC yOdz±$ÉùT±TšiLˆàüÁ>0Ý‚x’pë¡ MpMwy¹5زóÍɇº•&ª'1Ã’UÄ´ªg{Aý’ð•¿­ä¹~ß&И&`;aù§ƒÏgºx­— –¿ì¸AÔÉC¿uýæG˜ì`U6’+ïHçs(ã«&Ú ÆfAxëuÁ—‡/­©(~å3«öVÒ„)ÓüÕ~³¡Z²¶B1ÍvRš1Î4… AÅCÛG›­0ï®± ÅÝÉzÒ`l™ŸY~ÚËë©Xœ—È?»°›”Íçj^T¬2ÃY™áúö¹7¾,µìª]'kœ(¥ÿÿ‰h%ˆ6ÓŠŠhGˆ,éÌ;é5wŠW TìKö™™÷úq*¶óît~öØD…%Q4¤ˆ1Ù6”WqOAH˜þ‚¼ÙRž"mM4Gý©4±%’ ÕpójñÄ–ÆkQåócÜ;¡X+Ê©$9HŠ޳Ñ›~†€;ݯ·é‚q GÛ%ûmûõsì7ü6 ô‘¿’ò/û<ªáGªÚ¦7e´Õ <*WØ`€ßäF}üþ‡?ý‘YƒÝR v«VðU!C>^Ü<‰^Ç^„z±?6§‡^n0ÅÇ!Uf­œ­nq©÷p+2KE1¹#+jmG^<¹oqhÌþ>ëüÕùXpÿendstream endobj 1395 0 obj<>/XObject<<>>>>/Annots 809 0 R>>endobj 1396 0 obj<>stream xÍVMsÛ6½ëWìQI,Z¤>(ùTÕ±=94umõæ DB4’PH¶òëûÐeMÜ™{F$`ñv÷½]üÝŠ©‹ÿ˜Ò„zCʪV7êâËþçᎿP˜F U”ðƒ’[ñ ŽFÔûQ“ñxÄKýÈÏÆ ¯îÆ~k<aUf{#ÞSQ2èÂR,'£Ä[Nzi4¤0:ìM†ã`ÙŸFG³ýayÔÃ3ñ#žýuzê£)èò¶OqLÓ‚1 £ñ¸GÓÜÇ¡KÓ¬éj¹rÒ’{–¤êµ4V’^ \¯æ¥¤¥‘™²J×T gÔ+­¬ª ¿üúY—Ò~ÛÐBdNõ]8^Ç»?Lÿj]Þ·G·éëë×ði‡¦ýÆ>Ïw©÷àú4oÛMUI˜ÑR[åÔZR.ªVNn‘œž19=Á”Ìá•G[”z.Jƈ  ¿¬¬æç ;A©v1Û:°GÑï5ÙU–Ik«’ä«rïxQeÉ‹ŽÜÊtíÄÑ6ÜQ°3Þç©“ AÄ`q0î嗻ǰf‡wk¦È›_A¥ša6´=ÂïÜè•S5һІ¬.ל;ëD “TÈZQªïˆ„‘Ð!ðRaÒn¬“•hòÆ?¿å öÙTKæ‚¥LÔ4—ãù•GÄ@w•%U-KY… J ª]QÈÎRXO²Jçò4€|¶/µq‚ù)2£­%ê -Káà(£½…¿Òìó—ÀLÞ+ÖB•b®Jå6àËî\rº™Ò£¼-D]êmê2*ôº™ŽvDw]f—…F$8˜;‡öÑ—!F4HKàek')]¬êŒÕä§¢„+WüäòåU¿e {í v5êßµgø7¨³\‰â‰iAÅÌòÏú‚$P>}x@>/XObject<<>>>>/Annots 832 0 R>>endobj 1398 0 obj<>stream x•W[sÛÆ~÷¯À£ÓZŒ(Q7¿¹9‰Of’Æ'VúäÏŠ\É[“»êîʲúëûÔeŶ9m2c qû€ »È©ÿ9M4SÙ\ô³>¾9þøz{‘òlJ£é8ëSCƒA‘{©¦û‹<dMx¦¡|Zð³"%ÚÑ,³í¤ß#‘X‹0Tä쯡b’Mö«±¡é NÛY—ˆˆ8œ½HÄD†vR »“i*s¶³³ ©Œl‡Å™6•¡M³Qây_ýŒjrÚñ5Ÿ²ú92m`©AÊãÛᘿlhgt²Mdô%—ŒŽÚT†¶àI<Ù¦2´¨ hŸl¹Á÷ã3ÛT†¶8·Meh§Ó³œ‹1ó¥Ä³?>Ó Šý)ž`„Y%‚u…3E¢g"æaÊ­8Ú%"|‚#@á¨Ìû}Ô=˜„‰Ì÷Ñ^’‰ [L]žj™{3ĤµžÄ6‘ï臵© -¶F7•¡1?Nžšc} xUØ_¼¯üŠÞ~È)Ïi¾ÄNã…5› i^É:ëÓ¼¼$¢ÎG¯,½™ÿÚÝuâÎÄÁ%…Í»M4VÓJ=VF­C¬.WêŠV/W¤_Týð¦õô÷©ä¼½zËr¹|‡øßÿÿâLE·7ÿ9F66"0¢·<óÃ?Ë£Ÿ$ñãÿ.‰ëëÛÚ-T}ã½Ú]_3÷Œ•Άˆ,ÏøAàá {çÅwÓ}û¡8ö«zŒPpõ‹Ÿ4…¨l¥|E—ÖÙÞJ[íUm~×ÕÃÒ2:°Ñ´önQ놼Žo]‘ªëýºŒÎ‚#’‡ÐcçÒ€$*”ÚVlê|¥}FsÄ7v½‰Ô¨èÍ+™@VXzr/Úo½‰Q[²ÎS¥Côn§«Œ½¾ý0ûsU`8ˆüÑFíÝU,Lm⎶pH_ØihMÿzìQˆç¼ŽnjÝú9©šœ$X›…WÞèpE TP9¤¦SiªŒ*­—¥*öEÀ°É˜dô«jÖµ¾"&” N£žùC]£IN€­}nÓ`/AÓ1þõÞÙ¡¹wïç÷åÃ%ž¢;0Q¡mWôþ@ƒÏ4w®~6Qº/¶Ñ,MIï\ƒN¨hœEçщ åÖ²¢ÅNF寝œE‚?ËCªîñI-œW˜„]F’;QฉšÜ’*!E¿)1A„ÇeÏþ#:P4gŒq\+€]kП0¯Øβ›pL¹_­×µ)%·@«tݦ?Ñ m•Y.µçr!èß6í“}Œ¤¾Ûµ„ø|÷ñD nÏy£CP+Ý[«0ÀÈ¥kšÝǾ’ÂPúazÁ%Å÷ŽÅ÷¬Þ`E¯ ˜ GÚJÙZ…8¶AƒŸe­_M;ÇýÙÓNpî³Á†`\Q 决ÜwAæÿöFxƒ%‡‰·ä·Ïß>ýr ß¹˜sïÌ‹¦o }6+¸ˆŸdÖw22¿˜°áѪ9Éû¨µG¼¿›Nº_Ô3}5Õê4F”Î!«vtÜ2n™ŒK¯½uþs¨Ð.Á*0ÙfÓ€Ù€°ÈØÒù5W{µ©c/ºüµ%è ìV ¿ü© ôó@ô(ìËc#ôéÕ¬ƳvŸSÈèÐ/áÎßtèPnÚ#ÙmrÊøÆö þÓýâÕ}¿³å“wö€þ;g±kdú‚+mA_xùÉ,pnÝ0• p{ƒ»r´P|sH–f;C²y%5àKK#ý Ž)½v`)¯VcAìn`Ókœìwnm;Ð0¬µŽºÞ‘’3%]`X&g4b²1J·ÞïŒ~Ò¦I«ò‰;¿7R%À:11Ûvcb$;,& ʧ[MÍWtÆ.j§Šõ¢Äì gùð¢Ù@€BÔ|°0}V`™ "L`Í`ÙîøáµFN+J§°c{(*¿Ò©¸[a-­ä5^¹'‡ü×û˹}2XŽÇTQ°î¦Ž€Â¡MDÖ¸ ¥€oÂÓ1qÆâ{#ð¤À ƒŽ™Ç"t&oíмpM*všÌ©-ôÊØöëñ—(€×7<všÛòUeFò2Üáèq3·Oâ{ÙóN`¾–<©ª|Ó{ü›gÈ´!±P1ONÎ8œTE¿ò ,ÚáRß z…3ÔÈ%C -³¦û—|‚¿ ¦CüU>ÌŠBèzþFÑ.Ue±9Ù²w0èMú³–Üû—ö]\š?¼”Ýávíèþ°ïPΜ€wOÆS¬,‡IÁnßÏ/þwñ'Èendstream endobj 1399 0 obj<>/XObject<<>>>>/Annots 839 0 R>>endobj 1400 0 obj<>stream x¥UËnÛ0¼û+öè ±"Ù²,û–MNAÑF½(‰’ØP¤KR6ܯï.-?¢M6`„]ÎìÎl¾"ñÁb ³Šv!î¾Ü¢xLa&A -L§q÷+ £tŽÛñr$x%ó ìW§Ã4ÄŒœ‡tÕ¯èðC6|ÌÔp}CAV!ªyšËå ²Ò !+ÆB9n“`wªhŒVâsB+Xk<²PèN–sh¹©y Úßp†·zƒkQAg¹‚)¨;fæãàæð‡C-uŽÉ™1lw‘}…0‰fÈ>+Ç%s „¡üÍB++¬ãÊuÌñ2Œ—"7Ìì@W7¾#%Tˆ‚²û—.E+ù]ÃËN•dÈi g˜3K¼4Â^7HåEé­äeÍñUŸ­×R¾,AŸcš` a¬:UPÉüQ0¥nÇ(j¹o@Ô7ÀŸ"ï´qX­}Ð+‰ø Là»6°]ntç„Âr²ç–Ù—gjÖÓxm„6þo¿{µÖÖ–Oï:kÆ-àç},-J¸¿y~8@•Àˆßû0——fµº÷"»!­VTŸG_ž·¡ÚÑÜ«ð¨Å·*wîœc×2 è57{“Øæ`”b [áÔ¨ÔŽ¤S0Ã)Ò–ÜÁ¶A×hÏÔ„î*94Ì¢Áð´äyW“É(Ê7ã䖣ܷÂ6ܪÞu¨E8舖©]óÀ^„ªN¡kù Ñ=ÄA¿`¾m9°}«oWLZ¥°,—ô4’N‹R#–Þó9¯…R‹P cò”’£I6x‰ÚtVB24Rf=ßûû¯8ðˆv—ÌÈÝ´¿§›ïˆz§ö°_÷ùŒ7Öɳ0ÿcÎ\•=aªÎ?’ö­ 84Ξë»ô0K‹ Ng0›á4 SE{€·…¯4”˜ê˜¤Œ“¨˜,Â%ݦùE69ù·Z9£%MäÛSÃ>Ôn)Q¼ˆƒE’âL²˜ÓÖÇlôyôÞ,Dendstream endobj 1401 0 obj<>/XObject<<>>>>/Annots 856 0 R>>endobj 1402 0 obj<>stream xµVÛnã6}÷WÌÛ:]YÑÍ’í·´[ -ºuŸ6‹€–h™^ITI:ûõ¡®vãÂ@vD‘œ9sxf†M|ððχ$€0†´œx®‡oúŸ/“À‹Ü¢`áΡ„ ñúQt³þÜ i6Ž\";¢Yß›Ój/p}œõ¿ ³ábá.iï>/XObject<<>>>>/Annots 881 0 R>>endobj 1404 0 obj<>stream xÍWmoÚHþί˜ä Æo؆o\rD•®']~*U´ØkØÈ±¹µIš3³^lé©§œÚDBŒçeÏÌ3³ü=ðÀÅb‚Ò‡ë¸øäøññvàÅžA8 ðóü8vüV*à/«M\'d­K¶,±6HHŽÈ}Ã|YêiC×IPx,„,u‘ƒ01‘ƒ#©§ ¦&rà¡oÀR9ð}|Šçº!ù²ÔiýÙŸ¢vFØÔEöâˆQÌFêi£6ŒÊc©‹ìMÛlx¢3RO‹>œYŒè<–Hûëê<ùz “ež«œÊ3M"g6 `•q…\X¥Ã\•4; ™ª­6‡FU%T9ˆ„ÖâyO;•î`/tCÏÙV4‚¿¤¢(T¹…½®RY×P=•W«ûÁÄèÙÐs€dÇ^€¨ñ‘HÙÒ†91÷_šW{©E#Aµ.rZŠlò¤U#O܃—îZRÔ’}íÉ•$N¼Cã=ö§0ö#,)‚]abòC™RRȸmì¨mqN°×&Ø›aÁžåøÆ8ødb¡ˆè0\VºÑ¢Î2†Ã¡%×kP6ºÂ‚”Ê­¸ëWh=ÜÞ‰(JüŠj;µ¾ú7ˆnWÿmñ}ž"|j§FpOßî;¨çÉ™,m6{P¯{¯õZªàø÷X© þ¸]ÜÝôz=Te&eø¥M=+ªÏ_\ÙÐ!cSCÛª;õù‹MêëHO}®ß½û¸ öv1ŸßÕF "Ü|~ZhByøX2ëw¼ÇDÚn&®#G {p‹¼2ŒÞòù†ñLÙÉò؈˜6:¯ 9lv¢9rÞèŽÖ õÌžæBmèÕ‹{É2ű³‘8|žáQXI;YÞß8´÷6?7ÍliYièH]•ɯÈÎÌ6wïÅáÑk÷·gæ ¸—d¼ôõnïý¼\ð8c䑆ÜÃ#`bü²oôˆª~ÆKæmöc¸isÉììè¾—_ƒF v‚Ý‘©×ÑØë*“5˜C ©p<ኅŒö­2°ÞËTåJ"5Dƒ{¹%0ïjâ*6ÏLð³åì1暀H‹”wg¯œ[õˆGÓ’ÝWlCç³Hçïvwu1 çÅpD–iº Š)ë!4Q¦r}¹®(nKg;lµÌ¥–h6Äç J{LË^+º|XÀ†¶ykA7~ïõÕO=ÚKÈÉtøÁ#áÒË™`áýŸƒà£¹›õ&Áéø> o°wI¹„c²LìEoŠ?%’¦1þðpº'šõ ¼¿køTK Dy…ø›'D‡qìÎÈ:v<øˆ­šgx_æ•~ö†Æ¡G‰¹>Ç1ùÿ¶ü9ø}n‰endstream endobj 1405 0 obj<>/XObject<<>>>>/Annots 902 0 R>>endobj 1406 0 obj<>stream x­WßSã6~ç¯ØÇp—„ØÎ/x£\ÉÜL{3½æžŽ›Œb‹XÔ±RË!ÇßoWvâ()S˜ËÒj¿Ýýö“ü÷Y@ü4 )S¼>ôx³ÿóuvL‚þ˜F£Kü]Sx9è«QFÖ³Ã1Þbvõ§4’‘ÌFS¶¢~ȳ£ Ûʨ1Âf£hÌke$³£{ É÷+¶~$³Aˆ=‡ÁÐû ù¿5føöÊHf'S¼‡!ö¯#ò#™ÌaXÅ;`•5f±#GEŒ=”Ïþ2o'°XÑÅí‚€æ÷Èôh:î_^F4O$ɚǕ'çó‡Úp\¥_ ƒÊ0¸ˆ^ÈIž'¼÷É„¼vˆúq½`йµEY¨¼±]]MÙ+G‡Èm—…Ý–&×”¯Ô¢Ð™VN/¶›D•ú®³Z¨.e¶K©¹;ol÷"ºõΠ…ÿ/´×†fRÓ¥~z8 l§äâ¶ÎaåM#¢×DûŸGkú2»^|m%Éä%I¢ø!³ßžRóýG´Óà|üø œÙõÕÕ,³K•]…zººª ø­ªßkˆ\ìsª²w¤âX;G¥%E+ñ@Š]ôižjº·ÅZätIÆÑÖé„v©Î©Ä˜£h§Z%dóì‰@m™ÊTY›IÀÞ<ßÄãÀþpI»Â€ª«~eU7‚ÏI €p9*¦ÖƒŽóy:p¨"–k“T¨·ÖejŠ·E¡ó’¹ø¨L¦–™¸¢98%ü\¥ÞcežÅ̹&ùÑ©Bg«GM‡µ- ÿÑàCöeiõD^ˆ™cV2ù¸›°tÏQ<;³¾‚͹›¦‡U*ãÖzjûÕ.©uÒ%ý3Ö›Òƒ5%±Ü ºÊ!L‰þIwEÑuqw^û±…Y™\z^3« ÉJu–ÐF•qÊ3ÍTH‚êF¬k"Áˆmedò8Û&šóÖÌ¢ôÑ=‡³3HþR“”ºˆí #úÓŠü£Fz}Ó‹Ôñ3ˆMúä ‚ƒ¬uîöPí /!w¡i‘7ƒhyVIE²9ö;ðÁu9<äÕÜXç “…ÔKêÒ–©äãÑø)! ‹ç¨!µ{ÙllÝò¿užºðdreU³ªÉœvVœg0ûó6²kÿTµË{ ½KñžÕ©’<”9.à¶‘¼-MùñØëã[®¬íKÎþ§½wã]çHŸâ²úæ,ºì¤¥?r´&§ñþ¬¨{äæd½dØ/k¦G?ã¦pû; éf}”ÎÁ 9¹?˜hÞ¥Šÿ¢©h‹‰7Ç·ÌsYE™YSµŠÓ“÷»ÖÔì„.¡ÜN/*ÎyˆéýSín©¡d9ª¦òŠ$mV56_H2Ý>\>d|`S\…Þe B½0ÿ‹TˆÜ4õ¢) þêä·‡¬V—Œƒ—[QÝB'Û#¦|ˆÎ¼]Ëň¾9\Ý~W90úv¨ z“Á%¯žôúÏ lMŸjÄ‹‡“a2žâˇNùÕ¯ó³?Îþ7¤endstream endobj 1407 0 obj<>/XObject<<>>>>/Annots 921 0 R>>endobj 1408 0 obj<>stream xÅVKã6 ¾çWð˜Å&Že;Ï[ÚÝ æÐŶ뢗ÅVul)+ٓο/)ù‘I±AÑÅÎF(‘"ù‘Ô§¯#!þ3XF/ «FaâJÿùínÄÖ,H ‰–Á*ˆæa°j¥¾ŒX8Çõ„%¨S[̃°•®vCDƒ­“Ü®;9NVÎÖŸì¥Á6ŽçÎ֟쥫Ý(BU’ Vì$·;ge”Dø¥˜—èÁKn—E¨EkŸQD±{éj—-\Tq£Vä$ÚEh€!”-µmý”Þ¢g ˜í` Ò#Â<_-‚õ:†4w‡f㋬O ë“0p·…c£²Zjeƒw韣ÙnÝÛNç”Kš— |6:Ö½:jSq²ñƒ·iD Å'¡ O¢Ôg© àp6º0¼š€V@ ‘[¨54V@vâ†×ÂH[ËÌ‚>‚¬-œq±,E B=I£U%T½:ÇGña'}¡B˜²aA¿WÏŠ°À€‹6B­ áræ*‡Ë‰×€’4pÿÁéMàzµÏºÔÅ3EÓºÄóJ­¡”"€T߸æÊ^Q<SúÚLAÐuYê EÑ£ Wp@îÇ,:ôR2iËâ¼ H‚m@SâJÌÚ2±5Ö¨·ÜiSîê2 ”ûåŒð×c©jQ`¼(øƒÒ¹ù~¼÷š×éæ?{å3|ë ÜúÄŽ{øôf7×^Þ¿£ïe³¹Û~æIb·l6/3dú>#êÆ(lL,ŽÆÐkÔ´˜5Æ`v;¾i\…D¹(+ûýËõf7ÿk¹úľY­Z”¥¯•jªö2)ÓÕ¹©iÒú©ýÓe›ƒÑ‡ ¹¢Pºe?.øÄEç¾üfºü^#÷¤eŸpè>¿Œ†¤xÀ@èÅçeZ›\*¼íëý§r»€î¶8ž¥>ðrk Þl(‚´½r÷ãWbúfCäY¤BÐ}W\%Óͯ=‹L¥È»öÐp£$S7¼¼Ú( b—iœx{Ö a!!]w ¤“#qyÀrkEŽ8å¤4Ûõ7.‚íW:¶»¾œíßv÷ÿÇ¿xu.ÅæÆ¶ ‡ZëBZè}cJè}=„o‘£ðÆ"d‘µµñlè'•FWø>À„VÀ-XäR…Pܤ殶¼dÑä¾û£cñ WÈÓxÂQ"¥b»û+rh!£÷ËGz‡çï>êŽÝ+å°ÞL× dž~oÁëô•Ù£ óÝ>tÖ.²,‰ ='´$=Û­:Þ]âoã+ANvÏ$ßÑàZÚÂïo¤_¸Âž"‡SÖL—áúÕGU²L‚åb…¢çךø˜Ž~ý Üqendstream endobj 1409 0 obj<>/XObject<>>>/Annots 934 0 R>>endobj 1410 0 obj<>stream xÅ•ßOÛ0ÇßóW|‹ nœ„¦íƒñÀ#{CBnâ¶Ùì´ÿ~g'ôC¤ik¥(—óÝ÷|þäòp„ôçH#ÄcäuðÇlÚ=öw<³c$ã„q·`tAkÏ4®ƒ…¼¹|;ø”³qâjÄaÌÆ½Uá&àá±³)iLÞˆ§lÒ[;Þ($òNS"ö–÷úÌQ”²t“¹³¶±O¼n—¹³v¼!ït}æÈ[Îû){¹³Âh>çÈ–Ô‡$ž²Éx‚¬ð› ‘僔qá´jm# .ÔR›Z4¥VÙ`4O6ÁÃhLÉŠA¶–XêªÒO¥ZaÙªÜ-·È…ÂB¢µ²@£¡(ÊmFTåO ÕÖ RÒK(]H‹f-ºHܽ2¢FiaZ¥(¹+!ÄÇT! kuäcpqæÂû˜\Z{¡ hzb÷[ÙEióÖºÚ’JŸõ¹÷6ÕoÅ»XäˆHˆ(‡…o$ï{Á©‹Ø´c®Mc„o×– çƒ(ïþÔŠF®¨Âç–a%îújï”oÆíàöà-õáNGN»Å{ð¾T…ÿ‘6ÎOîúsþ€Ü®Úáá;õ:µÙìüäFšÇ’Žh6ëwúeo£[À†û]5²i Aå¹Ñ¨> ´bøªpsyaòuÙÈœÒIâ¤Y^OeU9T+B‡žô‡·š|hI“0~­"ÕQ§e¾![2÷nüOŒè•*‹‡Ñ»åþ:F{ÊïÂÈ÷çèxB¤Y£Ö†è#>(?ŸºSÇ=½Ä.Û¬|”FT=σªçƒ†[-~¡CÙƒdEMÐÕQo¡3šOžçMš²dã8XûQx^éñybŒøeñÝRA—B²®’!ï†i8uƒÓÍxŽ«ŽÚ—3>I–ÒG¾Å`ºŸ³à:ø øè±endstream endobj 1411 0 obj<>/XObject<<>>>>/Annots 947 0 R>>endobj 1412 0 obj<>stream x½UQoÚ0~çWÜ#¨Åµ“@ÒXUª=¬êÖì ©2Áo!éì¤Ýþýîì$P¦jfEckeË'Sev5ÔeµQ«‘G{»š1‡±±þt3¼>#5¸·‹Çë¿rÖXÛYyÓ^\œ™Ø§ÏoÊ<ëLÙù¼å}×Ó~UÂÕ2je;ÝÇÁG¹U7¦´Pï”Í~jV9ˆ²¶2ä³Ô…\ P_âä™1W*ô8)žïÚð/­_Wð>í¦Šõ¦m÷%h²ÿSש[É1=ÎÅ™%¼÷Ü÷B¼UÍY³@¤Ü€Þ€ÄQÈ´¬Õ^t½;åÆÍNQe²8Ì r0ø”Ç串»vvº}ësÙKÂ*à³,œ¹ÜÃ@¡•…µª_ú¹K{7n ¯–Óv¶QËn®o~ÉýS¡æ”ê@x+ÆzÑ¡` Jf;ò·+Â(ø»ÌžE0XXÛìô§Ñ×í?¡Fò„G]œ@]ˆ§FXiÕüh÷hÇì‡à²U uÄÚPuÜL•ÁWÈj˜ã¯ò }Ê]å'¹[Y‘2"ðÕ#O+×íVsŠŠœ°nËÙ!ZìmQ­±»x)ÿ ŽR'в»êÅ_ׇ;a±dÈõ¶!)QYØÊ=ó£âÀVÓ‚»‚iÆÚóT ÓL±îZ§ÜóQŸ—àáSîñ±Õ¶‘¤{íÄ1‹’Bz‘ò„îÀ[OoaŒümá›E¥?˲‘ŽE{`óEÇL0÷~?°‹Ø‹½¤·EGqÄâi‚ÂÈDУ›tðeð‡ã>¯endstream endobj 1413 0 obj<>/XObject<>>>/Annots 956 0 R>>endobj 1414 0 obj<>stream x•TÁrÚ0½û+öh&EX¶ÁÀ¶rÈ! ^˜a„-ƒ:¶d†~}W²M줓¦0ƒY¯v÷½Ý}:9<üRˆ|&çÎ ÆtL&öµïyd C’‚Y„ÿñÀhFðU£ãƒo?ßWŽQ2?Šˆ9ÞŒD•Á“ÓµÑ;‰×õvlôN_2™X:£$ŸÒ&s€kËzý1zélŠsðƒ½µÕñNCĆ™éŸÔZÆûyóšˆ:Àh9`­MŠ–7 `“Xºlb×P|à¹TWXœ™ÈØ^dB_›_Îh‰Xh:ô±Zˆ‘îý™ ²:ñÁ!¯ƒsV°Ïy¡!‘¼„Bj8²3-aÏᙫTªœ'Åc]aµñUÉÕ'XerÏ2X(Å®¥)îá¼,ðÄ}Vò,iUÄZÈ¢49Ï\‰´ÎÐ `ø¤ŸÚp*³L^DqQLÌ䜛š¸9†§¥ùM^ ¯âcËïÈJäÔoÒbg˜)f_jüÌâ'&Ù[‡N\JÀØhÞ½N.JÈxªš‚&'b™BÁ/¶Kïä÷ßæÿyäÈTuç#Š>FˆeŽcJ•ÌͱvjBcë>,¶ƒ>“àm¥5+q`Å2‘ mÛi™˜f7³©J\޶k½Y÷+„u…¡rm'±13kFo7ƒøF®!ÊÝhÖn9m•ÎP·È¥TZ±¢êiÜa»Ý.à&h~À†µuàÀv¢8U¸«»šÂÖÝþU}ØYÚ/¨ öSŠß|§aµØ­ÿ»b·àÝÝKÞ Îç«ÅWgór>oøÖ›ÐÒ}¹†ýÞ²¢¼pe§©P¥†SÅK#%k ŠëJ¥âðX.+¼p››mغ¸û«æåvPë m{òïzYÏðE@æpŒJ1*Fm#þR*»J£%^Šv¬¯ïpÀxâ“(ŠŒ{›?ðæVT,3¶ÃÈ›™Óï\a’h2­>õMøýÆytþŽ´ØVendstream endobj 1415 0 obj<>/XObject<<>>>>/Annots 977 0 R>>endobj 1416 0 obj<>stream xÅVÛnÛ8}÷WÌ£ƒV²n¶¤¼¹Á&(°F»­}1`Ðíp!‘)%u¿¾3¤ä[‘ô‚6&$çÌÌ™9äÃ(„?!¤Ä3(›QàøŸÃׇ»Q˜‡~ÓiîÏ (V Ga4%;™á7®FÉÁ:Yc?ÂÕ8FÇ0µ­’•ÌR·–úyoÐR|4ÚãvZ¯6¢$íÉhš"¦³ìj0ÅHã,Cïx6 ÖÉjšøÅ; ð7¶ÖÑs”“Œ7ˆñ×YdzQ¹\ÃÏ:ëdu–»¨òñ#kÑê›â²´z “ÛŠ r0Íf~žÇPT¶ü常ç°édÙ %¯ŠÿÐCDä$È1d=„½‡0Çh¼ˆx(ªñ­Ò­fý¡3Fí!t`aÇB¶|Ëõ¶lÕðFéýŠ=2Q/ÇË«ïa{x!qŒÈ7nó‹˜`ÿŒøÂW-ÜÍW‹ŸÄ;…{õêp××wó\?Š’›ëk—ëœReëšé©ñÎ+ˤyâÚ@‹Ü^*YÁCÇ Qä¼mAó¶ÓÒm`êd j–c!a½o¹Y^AÍ7-l”Vתdä‚¶JþÛZ­Ym3;––iÍöp—xJÈ-ì´ÂDŒÒ~¿}èWU˜;çJ/Ç‹9¢  j±ÖLï N«®’SÀ¬…R5;- š ª½d(‡Øû(1^й3›×Aö÷pO#>óÊ«™ÜvlËívµpyú¤éÙ ð¡«Û•÷'¡®yÉ”r\^`W 㖪ţ»NÀ3Xªn †ä aäÖæ4B,0RÅ%ßdI«S¦z/æ¨Bš–³ŠjI±)] IµmlÁ¨¡€€6šÛî:|Ȩ?È?ãȆ3ìÌc1'jEűX:þÓJP«-²SpH °àfÕ°a*^ _”R€ç—†ßÁœO>å†ÑëÛ³ßòº6ðtÏ‘\mîg'ùÎŽ)ÌÝ`âì o¶U¨.çÎ öèå´þ_ »dVµhDË«¡ŒhW”Ÿûm|»Lÿ>OôYÚßÊŠ´ «BXÒNÃQÏå ‰!aû¦^”èÉm{ß‹2|dàÅ™ú1¶Œ!‡Þ{f¬Ö|Ò¨Hö¶QðW]É'o´bUÉL ïpÅ^†é˜ùœù™}œ7h¯'(òò‘KÁéj:8±ª¾¼O´…C1Þ2]Õ× oØã6Æ]c•\ˆ" @3QÕt'%‰ç“hï¿×ù“ÛlxÓ¤ø´Ëb˜Î°“Œªtžʆ†“»+½°?à¥AþjšàÃ0eÈ"g1¥øW1úgô²%uendstream endobj 1417 0 obj<>/XObject<<>>>>/Annots 996 0 R>>endobj 1418 0 obj<>stream x¥VQsâ6~çWìÛ‘d Þ¸¶dîášé…ÎÜLéÜ[·¶ÅI6¹üû~’lc¸I6É"¯´»ß·ß®üµÇh„_F3ŸÆSŠòÞÈáIûñé¡Ç&Ì iN½åä3æùõ*£§c¾YÏ|ìɉ͘7­Wëdާ8;™yMìÊZ­ç á)ÎÎCDp«Žu:ƒÿÖjW'ÏÁ$°Y±InÕ±#—•³ÚUÇêÏ\VA€]u¬,°YùÎjWÆú~ Š|CQÞ OjG÷+FŒÑ:ˆš«ù|LëØ29¢uÔ_IU*^ܬÿº¤Øž‡/{¾O¤«­’U™‚vüËVÅ‘.7ýòå ´­’e¢°ßJÊrsã<¾žÑpDC6ÂuÜÿéŠðdŽ2éaùå}Þ®omØ´(ÛÌÿ×¥ÑÍâîîÊ<ê,‹‡å“PÇ4z±h¹*¥ûUP3;´¥úS”T€cG\—š%s:( ïÚ¢¡RÏ2’å^¨Æ"4qʱ…ï„©C“L »rOÐýª­¢+‘{Ø$Ð÷,jjSXï%U•©,´cäõ2žgÿÕ”îä¡ÑÒ·ÂÚJ¨¦§QŠSû<düª‹öÅtøÁFÍd±£oü9 #Œæ¹‚à+eûðÛ^ì‘У6— Ö:»–´sx\`P¢n,Ô`‹ÄiÏ‹8= c ©z™ä/h'ÊS>ŸH•£½$Ä«+ô%·ÊµàÁµƒ½à9^7r‘KõbÈŽ¡sKWÌKNîeÑ›‡)¦…ÕõYç7#(¤½È†œDf™|6ܵ÷ž¥©Õ>ÒÂæÝjæÂ1^/Ç^„F?g%§ß5ªõ‘ÏÌÉ!« g£ù÷j f7›†®ÇÂÀœøeÝû­÷Þz`endstream endobj 1419 0 obj<>/XObject<<>>>>/Annots 1017 0 R>>endobj 1420 0 obj<>stream x½VÉnÛ0½û+æè,’Eíö-Më ‡&]ÜS]Œ$;*,Ê¡¨ùû‡Úœ¥N4 `h8œ…óÞ£t;bàà?ƒÈ/„¤9¶ƒ+ÝÏ׋‹bÛ… m pÇŽk ßZoäâjç%‹¼^l‡SüE/c:Y¯çá*z§VÈ"oÀ0§ïëzmfc‘—ÅÚëFM樳^æÛ>Æzއ»|²úÌlŠÇÔ™½Æ2±.öâê²0À]ÆxCQwaF–ö¾[´ ›)Ê Læ ƒÅ‹`N=ÉéÔƒEJsv`‘Œ…õþhñ c]æcAƺ!æÇ-éx^J%¹0Ûö0¢B%ÆU}-ËZå"±á«\Üֹ̖ãÍŠŸ‚ºße§ Ò¼8ü©–GƒŒO6mQÓ(aî·ŠM2jÕm[}8£~¾Z=­ØÃaA÷wWæ)\^œ­>¶Ë…š~86“£G3>|Ô,Bµ›Ꟈ{Ý:?9ù‡f©Õ‹³Ùìb[^óí™”ü~6ë€~m»“9r™Xä€%â-3UKê&ƒ”+N‚rM \>.RàÛª4›ò"U^Šjo—MÇëÓ.0ߺ‰ÂæäÏÃjFE4|­z¬/Z1Pÿf¡‘ÁóMX8“V/'VrÃå1ô¤jªk„Y‡Y2,úbŠ´EŸæÇ%ß”îoXÜy‹¬aªGöW4¯ð®I¸0,hxH‘EV”ò®³\l ®²~çê4â“ywg Ð1Æß²q¼§y•Ô•Žç×å]¶<">MæÓÇ„ÅoªÏ²‘gÄ©n€ð¯`'Ë»<íùX*q±I€ïÅN I)T†Ø5üßüLݲ…ê¡ËšÙtD4;Î B9ì¸Jnt–g3¸3h VŠ«š”E½C¹Ë$תöê{£9¾Š‚#X–o·e•§–ð~°o‚-7èþör¥ùÓKë°J^§Íæ]U–ãÿ«É§¯ìæÄH»”ˆ?’ßå5ÍÒ†"å2ÕrÝÕÊ –y†¼‚u) ®ëF2qû1¡Rb‚¿°œX¿ÛYœ'à{•IøÄEÍ·†\M€9S½[ú\a’èãG¾…±á舋їÑjt´£endstream endobj 1421 0 obj<>/XObject<<>>>>/Annots 1036 0 R>>endobj 1422 0 obj<>stream xÅVM“â6½ó+ºöÄÔ‚±37ò•C²CN!E [€6¶E$™Yòëó$ÛØ3µSÃÖÖ&3U”äVw?u÷ëÖß½€|ü4 i<¥$ïùž/ן«^0 ¼˜&“¹7¥œÂ‰ÝeôÐ ‚Ð iM½ÈJƒàºëHÇc|…tîãÔÄíœÔYŽ‚¨Ò"œªv­nä[‹Ðõ#ø¯v­tÇžo¥ñ¨ª]k9 kÌÎoµkuàÆìnTí:R¿Â<Oá7t;+ýaý¦uêÂèÓ:é/¨4"æBû²HŒ M'%Ï"å)‰µ( ¥Ì0™#§3Éq@G‘qúný ŽC›› ³ rŽƒÚq0G†¡½Ò:í/¥2Š•Ò“„:%phûDºÜ) t§âÀ¶ÇÖùÞô[6 Lè(@ȹlî^2ôiØØÆ7 ÷w–"¥ßV‹í{ˆ€‰a™üãÏ¢c§YÝ ª‹éíÛQ9L«Åýý*“;–-”b—û{¡÷69›þ °¬ýa•&] Ýj¹fì°‚¸@òéOÄþBŒöRåÌ” Òt* múš²>FËk"+ûÕǦû¨+YðÍŠˆ+¶êkí²"%%õÈÅ¥Í^"³2/41èflÇ3žHª À-þmaÿÕ‚O¥Fy—ù‰X–9$0—óÂh’{ì-lL­Š­~m€Š©æÒÏ="H¸ ±Œªâ#ŠƒWŸjH°††oNô}ése°½éWª\™r³³o¤À⫳H¸®ðнd[ÑuÙ7)pdÑ.£G"›X#´‰•«ÜÙvÈv.Ÿ–b$O\áDéá€SNP5Ȱm™€ mH²2Å'›XL®+Š2ß?(*«×U=­”(Î ¥å) VJ¹6J^tà ®Ä†Äp…OÌòp€98K·XláõY%¶X­`´ ⺵g^ŒÞ\7ÂM8Õ”mØéäWØF–Ëe‰þðnäò Ú×ó7@4saÄ™wBôE·:iÿEŸ ¨—¼½àɆ2“Ä.Ë.ˆI. ú‚DÀlª¾GÎ>‹¼Ì)‡Iu¡ÖÐ(ª)YP]7Ì–o›Ã›£8‡È¡¢œ©¿Þ|•óa{Á¼­˜ÿ«M¤ ;Ì^Y¸Yûß4Œ§në ûºënû°ánŸˆnõüYÑ™ê_ž ?=¹}åÿ+Úˆ+êÞÅ£_ é#æjât²Ý¢>dUu¨ûׯ͊Ѳ!r0›yQ<ÆxæMýØ2«z{hú]£ eEÉ2˜a£0œùs{êôÎ6Ç‘hy³iŒ‡"DñÔjü¼î}èý cÛ.¾endstream endobj 1423 0 obj<>/XObject<<>>>>/Annots 1049 0 R>>endobj 1424 0 obj<>stream xµTMÚ0½çWÌ‘í6!Îù¸ÑU¡—UÕ’ÞVB&1IÚ$ÞÚ U÷×wl6°BB­ hì™yóžŸýÓ"àâ—@ä¿€¼µ\ÇÅ•ÓÏ×µEBâÄÆ Ç…<Ÿà¿‰ØX„xŽaäaN $!N0F“Ý0qª6ŠT®Žô®î„éºØÃD¯µA V±–¬5Ñd׋Lç$ÂŽÔî‡ ‰xŠH€ìQÂ|E€Èö8æÈ(I|È Í×…,Ÿ­¸èíî²ï—Bèzì¥ëgrØ >ôuÇ ¤[9´-õ {š˜ØqÉžîL“ëCØ.ØÄGRY1{¸ôçÀëÖËíæ±îz¸u z#욦ë冉C3™¦Îo&˜¯‚Q*[km{ ô}˜+¡îöèÚ4<§=+€ AKÔPóÕI둚Y=¶AN;Ø1`u_1Ï xa‚;&3@ÂGðÈ k™³¦¡ãƒ<ï:ÉÍ*û¡Ëûšv¸~’çÿÖCyÅòÛŠvEƒ6*·ô=Èå*ÿ·•΀ÕYjð¼¢ÞÝ:Â?úªá;Ú,•ÒT ñiÔáíW½¥ëÐ[{@K@©[SQõÒYHóüüg س`’)R8Ð/›¶¥Ê&ûäL£Íe‹Zjü–IIK=W6Õ¾g…Ê(õ«bNçÃÒºÓ¨£‰ãヅ/gûà{êiŽÕMZ~Z3 ß$^‚GÚ ´Q6 ìÈMT6–ÃguS´ãƒ(p¢EŒ/?nÅ‘ªø˜Y_¬?—+‘¥endstream endobj 1425 0 obj<>/XObject<>>>/Annots 1052 0 R>>endobj 1426 0 obj<>stream xTMsÚ0¼ó+Þ‘ AþâÃpsRBs(MŠ{c†¶ÀêÈ–#Éþ}ŸdCZ¦v˜[¬ÞîÛ}Ò[/?LCˆ&•½7§>±‹¸<áSàÇd Qã3¼çÉ>Ixí! QׯoK»‘?%”F3woÖ½‡ôvƒ:€÷4ƒ`é&1 ÆŒ§1™ÅcHsWÙ‡4ë/“Á`ƒ<ð*çÕAÃ^*X ¹£¥èYߥ?z®XDB[nØÕ†‘O&Q„ûÈ_L9;µøEû0 '$Y”eƒZÉ#Ï™êxye˜ÚÓŒ‘phy©ã… nØÜà;EgšÀg¦p ¦`Ë÷óUø›5%« 5\V ÷àz²":pšó~aL=÷¼ÓéDX©©+Aòèáfíµ¬ÞºÀÞIaJ)’,'rw1‡"?AÏLÁ®áÂ@ÎËŒ8’Yßð¢«Õ–Kâ~øP±T´DG¬õþmû}Se¶=ž>/XObject<<>>>>>>endobj 1428 0 obj<>stream x­UÛnÚ@}ç+Fô ˜k¸¤i%šš‡>TqÕ‡ºBËzÀÛ®wézMB¢þ{gÍÕÐ$mU¸Ø3;çÌ93þQhBƒÞMèµ ÝÞú…†× ‹»3‡ú¨Í&ø3Š=ëw½Á  ~˜Å4Àç%8x)c²`a<„‡²ÿ­Ð€Z³íµ(#x\²$¡„4KÁ1yõwyRO™ÃVO%þÜÞlu½ŽCs™ƒÊ‚ˆcúŬР"–€Ò¢K ר0 DƒÒR›2”A¨ˆB,Sƒr,Ò1ÌR¤6€w›ÓT(…š{G”®–T’«*ÄÚ èé7ä–’Ý º³Tqw“ :õVH S†V©*2‚SÃÌ fŒ‹£ð4Áð¨ô­°‘N-°ÙŒ*6°ÂÍL›ØÑñÀeÔGX çt¯µú^×u­ïµáZ&v¿î˜Ã⣉…ÊþŸ¯³÷ž©QÇûYËÉ{ƒ¸F»Ê3-¥¾u8ˆ|G=¡[†ú|RÌÁ83dH²F×GÝc]õMñóór JAùéÖ©û¸üéÔxË3í8RHn†ô‚YF\LÊmj²ùC<ð#Añ 0àDÞ©°Ä#•4 ’µóiBÛ¾?ÃçJ´dò˪sóBˆDˆV¯%ð{¼Gè,²iôv´÷RÿÙ.˜Œ‡“è__N`©E¸÷±£îæ9¯˜—ô{ùåkqK# ¯¡”·s¾Õr çàûŸ*¥*sE£)5y5B¶¸¡ÚUÈ]?R‘Í¿¯Ã»ZPÂÿœSþ1Îë¥WŽÃÕs‹ïñx!—iˆp!4YYìEoŽDç¬Rñ¢â&bo¢~’3¡NUÍd¥åúp¿Wo<ÌÏñÚÎÕçäçnï]\@ñ=ÒÃgmdª˜µ¯>êoºÔìõ¼N¿ g½¶×éôÝÚËíø” LÑw@kÛ„Z¯1X/É=Î*¸tO6L\P§×ñzÝ>íR·EîÒ•_øXøN€-Ãendstream endobj 1429 0 obj<>/XObject<<>>>>>>endobj 1430 0 obj<>stream x¥TÁrÚ0½ó;œ`b ›ôD3I&‡NÚâôä‹(±%*R:ÓïJÂà!æ3fäÕêíêíîûÙ‰`ˆ¿’Œ§ÀªÎ ÑrúÓkû£iBƧ‰]ã«9¬:Ÿ³Îà.†(‚l… “tJf³1d…;?„Œõ õÜϯ¯3®+!iÍó^ÞÿÔÏž;C£1á)tþÖ4¸›·YØp”’©ÝNI ÷ó×{Á¸¹öžçðáhJÒØã@ËXI 5PñjÉ5¬v’ÕBIõ†ÖP(n€–%~qX—jIKP[®©÷É{RÉjM-sÞ‡R¼ð‹|Õ S3ð WJWðüÎ…U+Øú}n‚f ¢€¼࡚ëð苃d­¤øÝÀ´P*ö\„]jE FM`aŠãGä5 ˆçir*“åÉÑ´¸ýþãáævËgÎê·„žëbùI+n¶”qÇ%m¨ê68]PoŒW­[Ýs½ºöНÁ6À¨„%‡áÔ „Ü«nÙ¿¸Ö¿x')8Vë\ɼ‡ŒÿE«mɰ\ÙsAy+DJb(a Iû˜õEì <Û»Ír'0·M„hÇ«ãJXüS#Ѣж#qÛ9'퉸ˆìé(`¥UT^7§MH# G’휵¦ÒŒ«=Z‘E^]‘ ¸ X¹³f— vøJ”|p<–÷Éÿ‡l÷~æ¶åßvÅi̼›ózwÆpœìD84Ô™SZyðç‰ó ¥ÇŽ ’Z•BÈQ†ŽÉ7sï6[ ð(yh¹* 5¯Û]]ðAÐU–8#bÏáR > ýT‹RÔmm  5¸ïðаÅ32Bñ£„LÓÔ²Õ.£'ƒÊ÷…Ê--a”$$NÇ&ÙW×1*¨-É¡²€F°Q.]ñâ$&É4õõ˜ -ÔmÖùÖù ޜԅendstream endobj 1431 0 obj<>/XObject<>>>>>endobj 1432 0 obj<>stream x]TMsÚ0½ûWì‘Ì`ùê-&39dšL8æ"$jõA$9ÔýõÝ•Á% 3€åÝ}oßÛÕGQÁ?,k˜-@ØâfËëëYÃj˜7¬j5eSz?yZ,à‡‡×âû¦À3L¾Â&k¨Ö°ÙAµX³ù| s,·^Ía#sä6bô¬CðAI¸wñnó«È‰3„ÃÔò’[63ÖLñLŽÖÈéç§ ŸZúx$UQôÊzÁV E …y. mTÀáÐmƒ–ÀÇà¹8@òÔÑhÁ“É.‚„icRœ—*w¤Ž)èm{ <étЖ!ˆ]eÒrD9 žaFˆNýYe}è`çLÑnAè *uÒŸÈ`À@>/XObject<<>>>>/Annots 1061 0 R>>endobj 1434 0 obj<>stream xWÛnÛF}÷WÌK¥•i]-ÛoN}p“Öê›cE©MÈ]ew)EùúžY^,3ESÄŒÜë9gÆ_Φ4Á¿)­f4¿¤´:›$¼éýu6Ï’ Í–ódAÍ.ýSIgÓÉ2¹¤Ùb’Ìäë|Õ?|­p§¢ùTNÍâSü-Oç ÜÁÝÅ_›'ùúv= Ætq· é”Ö9¢^^]&××sZg1à ­ÓÑz«=m§3R»³*Ý^í” :­KåÊ#Õžóº¤Ü:‰MÉ•§Ã–“rXW;ë‚2‚%oË=“¢ÊfìT`òúgoÖŸÎ&t>#äu6jÍP¥Ì‘‚®ØÉ×p­<)#‡/î.ÛÀG¤6¤Ú6ºŒFðQr ’²£Q•N=¼Uˆg Ù|FwºDäµ)J¦T•b°»¶LlØG„•IRö¶*ड “« ‚ƒ£œTiS¨ÔQ¾ùZ…rÅÊ)*ØVÜqàÙîPý-ú·umÌ6^²8 åAÙ©µãlL›:vÀ]–¨¾¥Òš¢õ÷}užƒSŸ8 ÖůFù%©Ö8uÞÃÖÖÙÇP<ÒUÞ«²Ž1û„î€ ”Êsq &° Ã,íj¿% ÕF—: VLÑÎjà©AYÛ›a#^×¥íÇŽºTqF@€²8D®=§9%ôˆâæ„Ç” Î_µ¯Á®Í¨BèÁ$ÿ´ÒUµ+å%²÷hº— ”QåуG®‡Œ½j1š›~ï¼”P‹,‰Þ€­6Ï >³IZ˳Kh hõ ³À@âÔÑS¦óÉçÎV„fgÂ!‡#ðmÌ(SA¡þ[Èá`逻hªv>Œé×6[ÿЄ!©qEüT›4Òç Ãøà4PÚ¹¡-±lÝ]k°Ô$ìë(Š˜Û³óBæJ?w ÛAEc‚} @Щۦ E©Sei( ﹯ÂÕ¯ŒŠòo1ˆL´I­C îlü¶eR#hµ#UÖ!ñJ!ö}%ñÓ¥P"!†>’nÒÓÈ3GøªÝóÀm®‹ÚñÓ›†xþèCTd©·"`¹`2uµ‰°FB³¼EªÈ7š'E2¦ù,v$Ikç|+„c±ôûÛzü8¸F¤ ûº„öô°0:œp¿O«Õ_!  ,¡÷{6‘T¯Âµ0ŸÅ šðúhÇÈ^û÷ƒ¨Ü‹gTLW ºŽcF A£H-'¿ª†n42ÿ û!‰Ö³ö*Û##…ZBk‘ ½PŠÈÏ~(JgK艠=VæÓæhçEŽy ±¬Ñ´A~]ÿ[°v’&³À="’Üs“RØ\¥˜¸b[••…fáLÚ)¡Z#-¥˜É~G¡á‘c}q¼Á8Æs+FÍÈgå£T·DBÚ~.z@ÓdÃáÀhûëê rn9$ºéÞ5ëßbÈxÇ&‹’kºQƒJ¢Ê­¬œà^ít]¦o)[}qw­IöYŠÎÓäJ´óo:­»ý¤½,Ř“™lz ¬²îÅåjÚî(Ók9‡ _EÆ<„ÜÆ=æe5Œÿ‹·`!®d#J@j¡žw…³õî¹àðÜ4ãiôôæGŽÏO–ªwÍáWûèÐÅ­?ëñÔáo¿ýO—âðþöææþö‘Ý^Cnnš|ï94Uï²}ÙXÏûªÆ¢Fýλéâ8Ô2­mÑ{t¹Ýz÷]#e޽,8àN\Ýd„ɵԱVÖ¶ý1©—eµ(íC3µÅ²5Q„…Üß>¿‹ž}‚!Î2ÍËlXàšå¬ÁßOa¦ÂjÊ-\8{ϪÁÌÅÝUÍ%þĸšÓr…?Hê÷MBäžþöÀ߃2µ*%ñóéj•,pá|5¹nˆ1¥PÓ½æƒX¬Éêò ÜÖÌäÕûõÙŸgÿVÐ7Bendstream endobj 1435 0 obj<>/XObject<<>>>>/Annots 1078 0 R>>endobj 1436 0 obj<>stream xÅVMsÛ6½ëWìQžH4¿$R¾¹iäÉÁIë²§(£HHDB A%õ¿ï[€e×{2V–5±Ÿowùm¿€’¢9åû‘ïùxrú¹»þÌ›Ó,{>í) /ívýÑŸ&!žî)ò#–µ;{…Њç‰²nâãÔíÝx;Ëq )·;; èÀr0‡‡ØîË‘ŸZ¿a{1¼ónÐ ùž‚E nwvš†ÐATi­ÐîøtHÞ®šÃAaq&iÂkü7’¶£_2H‡ŒW 4H_. ʶð à ÜbQVXX}ÊòñÛ‹ìË~.—°b5ÇÔ}TÝÒÍõúV6;¹Þ«¦Ñ,Vc~¼[‹Õ…3ô|>Mƒ°fÅøí›7¯tëœ^]ÝTz#ªë¦÷WW{áös}¹Œ»ˆ§6Ùi8¦p–•Ê9n}lU-Éjê3 ÁF mîI…ªwÔ–’r]·²n é-I‘—NŠDÞhc¨Ö…4el¤9V-ÁE[ŠÖf5äÉ–¬öÉY®÷l“O¬c8À|É‚c”ΘDÌû^TÕ…ú®Š£¨NáÊ-ð·vlB<ëƒlD«tíÑõ¶•Í 8!QUÿÈ^À–üûœ’ÁW„ª’y«¾Kì½tÍü!À_ê¦mDýŠÛÆMË­wbm3ZÊ´Íú Ú¼\ G®ñSª `ÚLhÃÛM©^æÞôœ|¯ÆQžÉ÷áDùát¬Ÿ/Ö§ÏݲTÝ’½tLìioƒfÞ+ üOŸ_NÀ¡Úú7êçWF÷7î“I<‘€ËÈ?ŸÄP†€üA n5¸¤ë¾™†(%m‘ ²Í„ØIööWìUãÔ ªK{'jsäâqåIJ›Õ…g/y¨ÄžÔFìQx†™#Y_™’åòÐNP) ÿ)X¬÷á±÷ÞQ\¥­*QÛÒúQꪫèç\ÿOõ„rRæ¬_¿ªWÿ|½Ø÷çîeþÓñ ÌOφ¼”ùWt¿ó ô$é8¦Ñ¥tëÑlMmðÎe‡Àcî‚V•á)á$ýçÉQéÝýaŸ½\¦ýüN/N#¼±Ì0åmku#‘ìL4ô§oEî ·S˜&þ‚ñ“·‡ÅéÐÇ~FÖˆ“ØKæ)^ÁX:âGï²Ñ)!©Ôendstream endobj 1437 0 obj<>/XObject<<>>>>>>endobj 1438 0 obj<>stream x}VMsÛ6½ûWìÑ™‘I–eéèvêÔ‡6¾@ D¢­¨¿¾o’¦©$ãñ„ýzûö-¾]-hޝÝ/évM²¹úmw5/æ8~øŠ>?ni±¥Ýëyqw¿!|ÛÍíÊtsN;yýìT!8O_¼káÓîß«dy[,Ùö¦7¾Yo‹Ígåõb^,èë›òoZ²ÁŠ ¾>§›åºØ¬øÚ®VôŸ½0ôà½8ŠÎ™W©¼’ÊFs¦½R–Ô÷¨l©J\¡ÐÎGª²­È¶¥:h‹ ÎÒqȹJ9œ"/RÎ9ðôiK_Ø´ò¢ tpƸEäø×ó‰#>².ˆ³æ“ RXäJm@ÚNBŽ\ô§;) 3£ ­ÌÞB¶¾¤Ò©@ÖšʃhMDÙFyÛ3êÒ|Ó%nÉèáù‰Sö“¨]Ž”` FXQ5•Nµ–5ìÊÃ…k¹ikuÔð O}|m+RöM{gÙ4ÐIÇ òåIxE)»Ð|»-ÖÜÙ'K•²HØÌ.q°Øò¹­ûRsU ôÒ5-‘ÎFüæ,¸Ò5G£¢êêþq¹E½\¿,—·lrrÞ”­^>¡9‰D{`¼¡“ `-"q#…ŒÔ8$ ø£R™‚‰“°rVM ´­V¾èIÝ3ùd¬¤W"þ˜¡ÄŸ; ¶—:D¯÷-_³U@óQÆ(IÀ‚“IðÜb(‘C˜/(œCTMA¿ƒÞJFýÆ­G£¢v6`Ê”L‘aŽJꃖº2&¸I\¯8_ÉéÕ‹,;êq^¥K©yõ­Õ¨VØ3£Œ¸t𮛺6†ž©®‚0+ÜúItõ]ð)iä‡Ú¬¬ÁSý_*ç½°‚ž ÄŸÞ`ãâE—wê;ŸÄ í>"LH6ã››k~Ð:ÌVgÎmß'__k£6=Öì{ùçE>÷²V‚1¯yž´mÓŒiÿQžk˜ñt(ïÛ#w¦Ÿë»e±ùµbwsÊmµrFï½ðg •1£ùê…œQßFKÍRŸ'¤cÜlR¹AW—¼„‡‹`f•}3ûFGZ©u!I9ðçö‚×ȼQ²V‡f9É¡Š.«K Ö 3KJǾÅ\@¥²5‰‚î9!úþÐ:‰3·ˆa$ŸFø Ñ8P€iþ( ¤F17’8B¢xA =)»ýxò:bCbûôI~ܬ}€–o!œˆõˆ Xz.(>Œr.¯ ¿oÇ9œºúGµ£Ä!Ñw¸ø‹ ¼1QX¢tzQŒ–³xÚˆ=†zãY¸AÀŠ=~Ì…ñ"‹uZX&¨û¤i¡¢ð2ŒøÚcÌç÷ùÉ;ÌiA­GLÏ+3춪 O”ÇÛºSµrÖ½=Š@òùqݽ® M?ç³þMtM•sx3”J°†6âu*s¡O…çeÍH ãe•Êb]¨ À1¹ÓM,„§»í¼®´Åc,mÌ 6id°;½:Bñù ÁƒÞ-cëìC]¯$†_ò?üG§Ž=w”y¹ ï]¡¶—O}å§Ê›þØeæ}zbwsÌXÁXLyItb½h›¼–œ”-Ä.£Ò{ðÊ Mâ¶6­ý‘8ቖ¶7óङ`GlXá£=?n×$^’©¬\™›÷'òbyWlW·,¹Ï=)GïïÕýª¸_oð˜Æ…íŠÍÿØ]ýsõ?g4tendstream endobj 1439 0 obj<>/XObject<>>>>>endobj 1440 0 obj<>stream xuUMoã6¼ûW¼£ØŠäo÷¶Å6EÅb÷f`AI´ÌV"~ÄõþúΣd9V° Ää#gÞÌðùm’QŠßŒ¶ Zn¨h&o´H· /¦´O“íÉšVéÿbûùu³¤¯†¾O~?LÒ$EÙðÇVôü²¢,£Ã §×»M²ß/éPÆš”ÅÔ´Ò ¯Œv3r¡8“p䮺8[£ÕÏÛNn( á<ª„.Éø³´ôñ¬°’¬tÞªÂË’¼!”P)O"Ôž*kB›<þ™¤4Ï–`~(§¯ú-({¥SÐňŸÕ¡ÉäDÚ”ÒEܸŽOôúu8¬¦wQl[Yƒí»C·ÖÒ9cGè%:œ÷D…¨ëÛÑJü`HUÔ袰ÝŠ’™¥t2–¤€nšvªÒ‚e€˜Lù§´f/¿(Vº»äQ/û(Ãp„›åÖ•®À RË%É· †ñF°ŽŽSá\hø"æY˜¦­¥¨Ñsó.m-Ú–7 þ *‰ˆT\æÖr¬‘r¤øL#5OÑãç—uŸ¹Í.Ù®á8"»YîØtzQU°ò7æuÏæ”¾YU)œô¬l¶¹+ê )S3¸J#[K>>ïo_l’U¼š~õ3n'zïziÖ‹$2ûâ(a-gŽ´6ž¡ï!AaW…‡mk,èoˆré/Rê.ð.‰]_?+„FÝȘÀ½¡³Â´W*…t²¦!£ewï °áwÇÄØ. Æz~’·?ZáGÎs‡ïàkD¤c„o¨j“C|a­¸º¡ 1}'3vã]•L’[¿óBn±á6öBéèSäØ1>N‘q£:Bõ¥CŒÕ1ÈÔ)^p“W4þt1¶.»5„ååöLÊî~.ùKÖʃž`maqóû6~pv!¥üO#ʯƒ÷©×@Xx0ŒÎ†6ø8 Ø… (AúO;…onÄÒ U1ÿggǗΣ©T§2‰löz§ÁñË„³xÒ6²Tñ†À…ǧÙõÑ>–òn.¹3ë[k‰Õ† ψ –Πo!ýÚ´ÈfÐ?[×ÜŒ$-/ÃÀÅXÑE8o˜|üQ’ZåVໃycÞVå(Íem.ýÚõ#(Ûn“Õn‰/¿,Ù­6<(¯úÛÁο„ÆôdRóÛù6Ýsu–&}Ãü{Wò«í*Ùnv½ØÝ¯yéÃäûäÖ ‰þendstream endobj 1441 0 obj<>/XObject<<>>>>/Annots 1091 0 R>>endobj 1442 0 obj<>stream x¥UÉnÛ0½û+æ¨1£}é- š ‡m£90h‰–UÈ¢KRIÛ¯ï )Û²tµACrÞ›åqôu€ÿ²¢ªíÌg>®ŸîfyÌ|HRŸE°… ôY2ZÜϦ6îF9Ë&»QÌRH¢Œèš%ˆà òœ˜[(¢ÃAÚK3„‰’Œ…èWÈï Ú›˜H„‡“´û,€istLs Õôh"¨Ø“=" ,4¦èè,rœÚ”ãñì2@®”bß´71 6fñdóMy^lÕÀÕm [¹Æ¾$Xû¬ˆ ¬mK|(+/ Èo”à¦íèÅ4J;}Q~™]Ý&a ÖyF,Î tön¥2Š÷îH¼?âþÚÞˆF(X}eZÙC×;‹¸¬ˆD,¼®ÕætûC,.^` Y”fÄrCxö÷ŒYàîzùá˜V_9tz2`¡‚1$dA˜A¹i5T¼ëðÑÃJÀ E F‚‹8씬„ÖR¹¢€\[T÷¨doxÛSÙÌFÏj,‚]áJñï@1 .JǹëGíYæí  Q‹o¢ ÒcÉ(¢óÍæÊà%Ì z ô¾ÜÞ×€…·ÆXÏ¢±Ž®Ôv³áæ¸3Àendstream endobj 1443 0 obj<>/XObject<<>>>>/Annots 1112 0 R>>endobj 1444 0 obj<>stream x­UKÛ8 ¾çWð˜jùÙÇîvgÐÃbuoŽ­$*l)•dÌο_RJlÏãÐNÇФ¾¤>JßWüq)d[h‡UÂü2½þ½]•9K ¯r–Áý ìKéÏs±x|¤a»ê*mê/MÆ£¡hZÒ*îÃÁØ!hš½ã¤„Ž‘øå³}™Qm~´ÚQÛƒ@ªÓã°Ç½5‡åH<w–­:¨8´$´1ï—¼¡ò+]uopΟ“jàó'b%Àiá^ÑŒ‡±®çrÂË Ï~A²Géw—ÓånýFzý Ì× •e­±ošóA¾>åp<¿iÆOËË-Ã…`y™A‘ã]SâéÖ­o{³ozøhmóàà«C‘ÿÙè1jqs]°IИ|‘ÞÓ óèò!æ"gb[â ‹À• OÔ«VÿíÉ–Äendstream endobj 1445 0 obj<>/XObject<<>>>>/Annots 1131 0 R>>endobj 1446 0 obj<>stream x½VÛnã6}÷WÌ£³°µºP–ô˜.š -Ú®ú  %ÊVK‘Z’ÚÖß!©›Ý½$½9@„áðÌœRsôaAˆd1$¨ºM„¸2ÿûùqsÈ‚ Ht…$ˆG‹ÃûÍÚFo”_y=4"A¸@µ‚NÞ8r ³×ArŠê­:{£" °x=4&Aº@µ‚N^$|€dözhcS­‰³VÐÉeHxñ’g.v‡øð†E®ÌŠÃ¼Ñú<.Ͱ„ çŒ7ú bcÎ>KRw,>3ÜèCxö!®€8´-Ã|ž­7n1‘g$+ß7åíÝP'xû@ðСlð¥9V‘@Y»ûBYmË3Ó šAT¦•BCE šÕ`$œ˜sfp¦¢æLC#hÙ1ÐW¨ªá¤äÐk ¨¸@/[a Ô+yR´  <·Z}Wþº a%xxe½í©2m5pªøÅ&lîâŸh+ZqZULkËÂ2ø]*>fƒ¶qK5kèÀ§€52gV„¬%PÐÃÑñCnõMvʵüB:_°Û`³w­RXºö´Õ ;À\Ë@ŠÑ‹£JÑ‹~º ài’Ķ,ð>v†Vf ›@ÇXX+0†¶èJv=gÏÖ%À¾ÚÎH¥òíC¾§ö÷$Â[ˆíB|½ÞIÎ-Ï dÏõ‡Œqý±yx ¾cîŠìã$ YañRE…ßB¦-[À¶OÝ G8ÑçÞÅzÖQ=mûgß¹§»OÄŽƒäÙØïl÷ûKø²­áñþùÇ—F%$È{©_Íø¨êJ›…óÎ\z¶;ÍŽ3aJJóo2%³ïÇÔ¤¸jߨ\ð¹d¿¾ú$ûUuàÊ—l,/Å—ë{õ%øÿS¶ÿ<å˯£oéõÖr8rþ­kjnu¦ Þ|­Å/OÞ¸J¯“7\âŒùïsû._ç¶Ö+3§A»éôÙªã4 i‚ƒ _è›YÀ?Q?—¨_'1‰à~ ¿OíÇ ¦(þ­f#U pC[3òDù$@‹âqyÄå³sSZ œàuÛ4L1Q1Ô»yþ_PœpûaºR@öNXÓÐa²q;ý uvR?L­‚"|Ô'~(ÚÈ[Yµ‚gT[/ÙV1n…ÂNÙUÞJ ƒ"‹ûG¥öR¤XoµSØ@GÔòq”pýXDçÔ Ú8*¶ ÈjцIòñ$Êð*O ÅgNœ<úVÞ;Ù„_4Sð=(†–ð~ì³Ð)*ïć¡U—å“Ån%ŠÀ!÷·¦ÈíÒ·åæ§ÍŸTE8§endstream endobj 1447 0 obj<>/XObject<<>>>>>>endobj 1448 0 obj<>stream x=ޱ‚0„÷>Å8P[A £ur0ù}€ …`*ÅL|{A£¹ä†/÷%÷`bŠ„Z!ÉPÞÙ–˜àb‚ÿò –‡R‚jH¥xš'P¢UŸ••ÑѺ«¶Øx¯_—`>endobj 1450 0 obj<>endobj 1451 0 obj<>endobj 1452 0 obj<>endobj 1453 0 obj<>endobj 1454 0 obj<>endobj 1455 0 obj<>endobj 1456 0 obj<>endobj 1457 0 obj<>endobj 1458 0 obj<>endobj 1459 0 obj<>endobj 1460 0 obj<>endobj 1461 0 obj<>endobj 1462 0 obj<>endobj 1463 0 obj<>endobj 1464 0 obj<>endobj 1465 0 obj<>endobj 1466 0 obj<>endobj 1467 0 obj<>endobj 1468 0 obj<>endobj 1469 0 obj<>endobj 1470 0 obj<>endobj 1471 0 obj<>endobj 1472 0 obj<>endobj 1473 0 obj<>endobj 1474 0 obj<>endobj 1475 0 obj<>endobj 1476 0 obj<>endobj 1477 0 obj<>endobj 1478 0 obj<>endobj 1479 0 obj<>endobj 1480 0 obj<>endobj 1481 0 obj<>endobj 1482 0 obj<>endobj 1483 0 obj<>endobj 1484 0 obj<>endobj 1485 0 obj<>endobj 1486 0 obj<>endobj 1487 0 obj<>endobj 1488 0 obj<>endobj 1489 0 obj<>endobj 1490 0 obj<>endobj 1491 0 obj<>endobj 1492 0 obj<>endobj 1493 0 obj<>endobj 1494 0 obj<>endobj 1495 0 obj<>endobj 1496 0 obj<>endobj 1497 0 obj<>endobj 1498 0 obj<>endobj 1499 0 obj<>endobj 1500 0 obj<>endobj 1501 0 obj<>endobj 1502 0 obj<>endobj 1503 0 obj<>endobj 1504 0 obj<>endobj 1505 0 obj<>endobj 1506 0 obj<>endobj 1507 0 obj<>endobj 1508 0 obj<>1<>4<>9<>21<>34<>49<>54<>79<>91<>94<>97<>]>>>>endobj xref 0 1509 0000000000 65535 f 0000000015 00000 n 0000000371 00000 n 0000001723 00000 n 0000046348 00000 n 0000046528 00000 n 0000047567 00000 n 0000096922 00000 n 0000097107 00000 n 0000098146 00000 n 0000141413 00000 n 0000141604 00000 n 0000142644 00000 n 0000187522 00000 n 0000187710 00000 n 0000188750 00000 n 0000232333 00000 n 0000232520 00000 n 0000233563 00000 n 0000277870 00000 n 0000278061 00000 n 0000279101 00000 n 0000322611 00000 n 0000322805 00000 n 0000323845 00000 n 0000358739 00000 n 0000358925 00000 n 0000359967 00000 n 0000394790 00000 n 0000394981 00000 n 0000396022 00000 n 0000426411 00000 n 0000426594 00000 n 0000427638 00000 n 0000427772 00000 n 0000427911 00000 n 0000428055 00000 n 0000428195 00000 n 0000428334 00000 n 0000428475 00000 n 0000428620 00000 n 0000428758 00000 n 0000428901 00000 n 0000429021 00000 n 0000456863 00000 n 0000483912 00000 n 0000484136 00000 n 0000484415 00000 n 0000489835 00000 n 0000491632 00000 n 0000493317 00000 n 0000493816 00000 n 0000494337 00000 n 0000494836 00000 n 0000495357 00000 n 0000495729 00000 n 0000496191 00000 n 0000496987 00000 n 0000497960 00000 n 0000498507 00000 n 0000499493 00000 n 0000500045 00000 n 0000501264 00000 n 0000501644 00000 n 0000502390 00000 n 0000694918 00000 n 0000696323 00000 n 0000700117 00000 n 0000706230 00000 n 0000707307 00000 n 0000710272 00000 n 0000711687 00000 n 0000713489 00000 n 0000714404 00000 n 0000714440 00000 n 0000714525 00000 n 0000714565 00000 n 0000714650 00000 n 0000714752 00000 n 0000714855 00000 n 0000714958 00000 n 0000715061 00000 n 0000715164 00000 n 0000715266 00000 n 0000715369 00000 n 0000715472 00000 n 0000715575 00000 n 0000715678 00000 n 0000715781 00000 n 0000715883 00000 n 0000715986 00000 n 0000716089 00000 n 0000716192 00000 n 0000716295 00000 n 0000716398 00000 n 0000716501 00000 n 0000716604 00000 n 0000716706 00000 n 0000716809 00000 n 0000716912 00000 n 0000717015 00000 n 0000717119 00000 n 0000717223 00000 n 0000717431 00000 n 0000717534 00000 n 0000717638 00000 n 0000717742 00000 n 0000717846 00000 n 0000717949 00000 n 0000718053 00000 n 0000718157 00000 n 0000718261 00000 n 0000718365 00000 n 0000718469 00000 n 0000718573 00000 n 0000718676 00000 n 0000718780 00000 n 0000718884 00000 n 0000718988 00000 n 0000719092 00000 n 0000719196 00000 n 0000719299 00000 n 0000719403 00000 n 0000719507 00000 n 0000719611 00000 n 0000719715 00000 n 0000719819 00000 n 0000719922 00000 n 0000720026 00000 n 0000720130 00000 n 0000720355 00000 n 0000720458 00000 n 0000720562 00000 n 0000720666 00000 n 0000720770 00000 n 0000720874 00000 n 0000720978 00000 n 0000721043 00000 n 0000721123 00000 n 0000721210 00000 n 0000721255 00000 n 0000721342 00000 n 0000721375 00000 n 0000721449 00000 n 0000721536 00000 n 0000721640 00000 n 0000721673 00000 n 0000721755 00000 n 0000721842 00000 n 0000721917 00000 n 0000722004 00000 n 0000722037 00000 n 0000722094 00000 n 0000722181 00000 n 0000722206 00000 n 0000722308 00000 n 0000722333 00000 n 0000722426 00000 n 0000722513 00000 n 0000722609 00000 n 0000722696 00000 n 0000722784 00000 n 0000722871 00000 n 0000722968 00000 n 0000723055 00000 n 0000723155 00000 n 0000723242 00000 n 0000723330 00000 n 0000723417 00000 n 0000723505 00000 n 0000723592 00000 n 0000723665 00000 n 0000723764 00000 n 0000723851 00000 n 0000723953 00000 n 0000724040 00000 n 0000724137 00000 n 0000724224 00000 n 0000724316 00000 n 0000724403 00000 n 0000724499 00000 n 0000724586 00000 n 0000724674 00000 n 0000724761 00000 n 0000724826 00000 n 0000724914 00000 n 0000725001 00000 n 0000725092 00000 n 0000725179 00000 n 0000725276 00000 n 0000725363 00000 n 0000725452 00000 n 0000725539 00000 n 0000725628 00000 n 0000725715 00000 n 0000725807 00000 n 0000725894 00000 n 0000725990 00000 n 0000726077 00000 n 0000726172 00000 n 0000726259 00000 n 0000726354 00000 n 0000726441 00000 n 0000726539 00000 n 0000726626 00000 n 0000726722 00000 n 0000726809 00000 n 0000726914 00000 n 0000727006 00000 n 0000727093 00000 n 0000727188 00000 n 0000727275 00000 n 0000727372 00000 n 0000727459 00000 n 0000727555 00000 n 0000727642 00000 n 0000727691 00000 n 0000727787 00000 n 0000727874 00000 n 0000727974 00000 n 0000728061 00000 n 0000728094 00000 n 0000728190 00000 n 0000728277 00000 n 0000728379 00000 n 0000728466 00000 n 0000728572 00000 n 0000728659 00000 n 0000728755 00000 n 0000728842 00000 n 0000728891 00000 n 0000728942 00000 n 0000729029 00000 n 0000729079 00000 n 0000729166 00000 n 0000729212 00000 n 0000729299 00000 n 0000729344 00000 n 0000729431 00000 n 0000729483 00000 n 0000729570 00000 n 0000729621 00000 n 0000729708 00000 n 0000729755 00000 n 0000729842 00000 n 0000729888 00000 n 0000729975 00000 n 0000730028 00000 n 0000730115 00000 n 0000730167 00000 n 0000730254 00000 n 0000730351 00000 n 0000730399 00000 n 0000730486 00000 n 0000730533 00000 n 0000730620 00000 n 0000730667 00000 n 0000730754 00000 n 0000730801 00000 n 0000730888 00000 n 0000730933 00000 n 0000731020 00000 n 0000731065 00000 n 0000731152 00000 n 0000731217 00000 n 0000731307 00000 n 0000731394 00000 n 0000731487 00000 n 0000731574 00000 n 0000731672 00000 n 0000731759 00000 n 0000731800 00000 n 0000731886 00000 n 0000731973 00000 n 0000732059 00000 n 0000732146 00000 n 0000732235 00000 n 0000732322 00000 n 0000732420 00000 n 0000732507 00000 n 0000732556 00000 n 0000732642 00000 n 0000732729 00000 n 0000732815 00000 n 0000732902 00000 n 0000732991 00000 n 0000733078 00000 n 0000733176 00000 n 0000733263 00000 n 0000733312 00000 n 0000733398 00000 n 0000733485 00000 n 0000733571 00000 n 0000733658 00000 n 0000733747 00000 n 0000733834 00000 n 0000733931 00000 n 0000734018 00000 n 0000734109 00000 n 0000734196 00000 n 0000734287 00000 n 0000734374 00000 n 0000734468 00000 n 0000734555 00000 n 0000734653 00000 n 0000734740 00000 n 0000734821 00000 n 0000734911 00000 n 0000734998 00000 n 0000735088 00000 n 0000735175 00000 n 0000735268 00000 n 0000735355 00000 n 0000735453 00000 n 0000735540 00000 n 0000735589 00000 n 0000735678 00000 n 0000735765 00000 n 0000735854 00000 n 0000735941 00000 n 0000736033 00000 n 0000736120 00000 n 0000736218 00000 n 0000736305 00000 n 0000736354 00000 n 0000736449 00000 n 0000736536 00000 n 0000736634 00000 n 0000736721 00000 n 0000736819 00000 n 0000736906 00000 n 0000736947 00000 n 0000737042 00000 n 0000737129 00000 n 0000737227 00000 n 0000737314 00000 n 0000737412 00000 n 0000737499 00000 n 0000737540 00000 n 0000737635 00000 n 0000737722 00000 n 0000737820 00000 n 0000737907 00000 n 0000738005 00000 n 0000738092 00000 n 0000738133 00000 n 0000738222 00000 n 0000738309 00000 n 0000738398 00000 n 0000738485 00000 n 0000738574 00000 n 0000738661 00000 n 0000738751 00000 n 0000738838 00000 n 0000738926 00000 n 0000739013 00000 n 0000739101 00000 n 0000739188 00000 n 0000739276 00000 n 0000739363 00000 n 0000739452 00000 n 0000739539 00000 n 0000739630 00000 n 0000739717 00000 n 0000739806 00000 n 0000739897 00000 n 0000739984 00000 n 0000740075 00000 n 0000740162 00000 n 0000740254 00000 n 0000740341 00000 n 0000740439 00000 n 0000740526 00000 n 0000740624 00000 n 0000740711 00000 n 0000740809 00000 n 0000740896 00000 n 0000740988 00000 n 0000741075 00000 n 0000741148 00000 n 0000741243 00000 n 0000741330 00000 n 0000741430 00000 n 0000741517 00000 n 0000741614 00000 n 0000741701 00000 n 0000741742 00000 n 0000741838 00000 n 0000741925 00000 n 0000742026 00000 n 0000742113 00000 n 0000742210 00000 n 0000742297 00000 n 0000742384 00000 n 0000742471 00000 n 0000742560 00000 n 0000742647 00000 n 0000742737 00000 n 0000742824 00000 n 0000742916 00000 n 0000743003 00000 n 0000743100 00000 n 0000743187 00000 n 0000743284 00000 n 0000743371 00000 n 0000743460 00000 n 0000743553 00000 n 0000743640 00000 n 0000743736 00000 n 0000743823 00000 n 0000743920 00000 n 0000744007 00000 n 0000744095 00000 n 0000744182 00000 n 0000744273 00000 n 0000744360 00000 n 0000744457 00000 n 0000744544 00000 n 0000744609 00000 n 0000744696 00000 n 0000744783 00000 n 0000744808 00000 n 0000744898 00000 n 0000744985 00000 n 0000745082 00000 n 0000745169 00000 n 0000745202 00000 n 0000745289 00000 n 0000745376 00000 n 0000745466 00000 n 0000745553 00000 n 0000745651 00000 n 0000745738 00000 n 0000745825 00000 n 0000745912 00000 n 0000746002 00000 n 0000746089 00000 n 0000746146 00000 n 0000746244 00000 n 0000746331 00000 n 0000746419 00000 n 0000746506 00000 n 0000746597 00000 n 0000746684 00000 n 0000746782 00000 n 0000746869 00000 n 0000746956 00000 n 0000747043 00000 n 0000747133 00000 n 0000747220 00000 n 0000747318 00000 n 0000747405 00000 n 0000747492 00000 n 0000747579 00000 n 0000747669 00000 n 0000747756 00000 n 0000747854 00000 n 0000747941 00000 n 0000748037 00000 n 0000748124 00000 n 0000748223 00000 n 0000748310 00000 n 0000748423 00000 n 0000748521 00000 n 0000748608 00000 n 0000748707 00000 n 0000748794 00000 n 0000748897 00000 n 0000748984 00000 n 0000749082 00000 n 0000749169 00000 n 0000749218 00000 n 0000749311 00000 n 0000749398 00000 n 0000749494 00000 n 0000749581 00000 n 0000749679 00000 n 0000749766 00000 n 0000749807 00000 n 0000749900 00000 n 0000749987 00000 n 0000750080 00000 n 0000750167 00000 n 0000750263 00000 n 0000750350 00000 n 0000750448 00000 n 0000750535 00000 n 0000750629 00000 n 0000750716 00000 n 0000750810 00000 n 0000750897 00000 n 0000750994 00000 n 0000751081 00000 n 0000751179 00000 n 0000751266 00000 n 0000751359 00000 n 0000751446 00000 n 0000751539 00000 n 0000751626 00000 n 0000751722 00000 n 0000751809 00000 n 0000751907 00000 n 0000751994 00000 n 0000752107 00000 n 0000752193 00000 n 0000752280 00000 n 0000752369 00000 n 0000752456 00000 n 0000752554 00000 n 0000752641 00000 n 0000752682 00000 n 0000752770 00000 n 0000752857 00000 n 0000752948 00000 n 0000753035 00000 n 0000753133 00000 n 0000753220 00000 n 0000753261 00000 n 0000753348 00000 n 0000753435 00000 n 0000753522 00000 n 0000753609 00000 n 0000753698 00000 n 0000753785 00000 n 0000753874 00000 n 0000753961 00000 n 0000754050 00000 n 0000754137 00000 n 0000754235 00000 n 0000754322 00000 n 0000754420 00000 n 0000754507 00000 n 0000754605 00000 n 0000754692 00000 n 0000754785 00000 n 0000754872 00000 n 0000754968 00000 n 0000755055 00000 n 0000755153 00000 n 0000755240 00000 n 0000755332 00000 n 0000755419 00000 n 0000755514 00000 n 0000755601 00000 n 0000755699 00000 n 0000755786 00000 n 0000755915 00000 n 0000756007 00000 n 0000756094 00000 n 0000756186 00000 n 0000756273 00000 n 0000756368 00000 n 0000756455 00000 n 0000756553 00000 n 0000756640 00000 n 0000756689 00000 n 0000756784 00000 n 0000756871 00000 n 0000756969 00000 n 0000757056 00000 n 0000757154 00000 n 0000757241 00000 n 0000757282 00000 n 0000757375 00000 n 0000757462 00000 n 0000757555 00000 n 0000757642 00000 n 0000757735 00000 n 0000757822 00000 n 0000757915 00000 n 0000758002 00000 n 0000758097 00000 n 0000758184 00000 n 0000758279 00000 n 0000758366 00000 n 0000758461 00000 n 0000758548 00000 n 0000758646 00000 n 0000758733 00000 n 0000758831 00000 n 0000758918 00000 n 0000759016 00000 n 0000759103 00000 n 0000759200 00000 n 0000759292 00000 n 0000759379 00000 n 0000759474 00000 n 0000759561 00000 n 0000759659 00000 n 0000759746 00000 n 0000759844 00000 n 0000759931 00000 n 0000760032 00000 n 0000760119 00000 n 0000760217 00000 n 0000760304 00000 n 0000760399 00000 n 0000760486 00000 n 0000760584 00000 n 0000760671 00000 n 0000760769 00000 n 0000760856 00000 n 0000760957 00000 n 0000761044 00000 n 0000761148 00000 n 0000761235 00000 n 0000761340 00000 n 0000761438 00000 n 0000761525 00000 n 0000761613 00000 n 0000761700 00000 n 0000761791 00000 n 0000761878 00000 n 0000761976 00000 n 0000762063 00000 n 0000762157 00000 n 0000762244 00000 n 0000762341 00000 n 0000762428 00000 n 0000762526 00000 n 0000762613 00000 n 0000762709 00000 n 0000762796 00000 n 0000762895 00000 n 0000762982 00000 n 0000763080 00000 n 0000763167 00000 n 0000763269 00000 n 0000763356 00000 n 0000763461 00000 n 0000763548 00000 n 0000763646 00000 n 0000763733 00000 n 0000763854 00000 n 0000763948 00000 n 0000764035 00000 n 0000764132 00000 n 0000764219 00000 n 0000764317 00000 n 0000764404 00000 n 0000764504 00000 n 0000764591 00000 n 0000764694 00000 n 0000764781 00000 n 0000764879 00000 n 0000764966 00000 n 0000765031 00000 n 0000765126 00000 n 0000765213 00000 n 0000765311 00000 n 0000765398 00000 n 0000765496 00000 n 0000765583 00000 n 0000765684 00000 n 0000765771 00000 n 0000765875 00000 n 0000765962 00000 n 0000766060 00000 n 0000766147 00000 n 0000766212 00000 n 0000766307 00000 n 0000766394 00000 n 0000766492 00000 n 0000766579 00000 n 0000766677 00000 n 0000766764 00000 n 0000766865 00000 n 0000766952 00000 n 0000767056 00000 n 0000767143 00000 n 0000767241 00000 n 0000767328 00000 n 0000767393 00000 n 0000767490 00000 n 0000767577 00000 n 0000767677 00000 n 0000767764 00000 n 0000767862 00000 n 0000767949 00000 n 0000768044 00000 n 0000768131 00000 n 0000768229 00000 n 0000768316 00000 n 0000768414 00000 n 0000768501 00000 n 0000768597 00000 n 0000768684 00000 n 0000768783 00000 n 0000768870 00000 n 0000768968 00000 n 0000769055 00000 n 0000769150 00000 n 0000769237 00000 n 0000769335 00000 n 0000769422 00000 n 0000769520 00000 n 0000769607 00000 n 0000769698 00000 n 0000769785 00000 n 0000769879 00000 n 0000769966 00000 n 0000770064 00000 n 0000770151 00000 n 0000770288 00000 n 0000770381 00000 n 0000770468 00000 n 0000770564 00000 n 0000770651 00000 n 0000770750 00000 n 0000770837 00000 n 0000770930 00000 n 0000771017 00000 n 0000771113 00000 n 0000771200 00000 n 0000771299 00000 n 0000771386 00000 n 0000771474 00000 n 0000771561 00000 n 0000771652 00000 n 0000771739 00000 n 0000771838 00000 n 0000771925 00000 n 0000772021 00000 n 0000772108 00000 n 0000772207 00000 n 0000772294 00000 n 0000772393 00000 n 0000772480 00000 n 0000772569 00000 n 0000772656 00000 n 0000772748 00000 n 0000772835 00000 n 0000772934 00000 n 0000773021 00000 n 0000773158 00000 n 0000773253 00000 n 0000773340 00000 n 0000773438 00000 n 0000773525 00000 n 0000773624 00000 n 0000773711 00000 n 0000773802 00000 n 0000773889 00000 n 0000773983 00000 n 0000774070 00000 n 0000774168 00000 n 0000774255 00000 n 0000774347 00000 n 0000774434 00000 n 0000774529 00000 n 0000774616 00000 n 0000774714 00000 n 0000774801 00000 n 0000774898 00000 n 0000774985 00000 n 0000775085 00000 n 0000775172 00000 n 0000775266 00000 n 0000775353 00000 n 0000775466 00000 n 0000775564 00000 n 0000775651 00000 n 0000775749 00000 n 0000775836 00000 n 0000775937 00000 n 0000776024 00000 n 0000776122 00000 n 0000776209 00000 n 0000776283 00000 n 0000776369 00000 n 0000776426 00000 n 0000776514 00000 n 0000776601 00000 n 0000776692 00000 n 0000776779 00000 n 0000776877 00000 n 0000776964 00000 n 0000777056 00000 n 0000777143 00000 n 0000777238 00000 n 0000777325 00000 n 0000777423 00000 n 0000777510 00000 n 0000777601 00000 n 0000777688 00000 n 0000777782 00000 n 0000777869 00000 n 0000777967 00000 n 0000778054 00000 n 0000778147 00000 n 0000778234 00000 n 0000778330 00000 n 0000778417 00000 n 0000778515 00000 n 0000778602 00000 n 0000778715 00000 n 0000778770 00000 n 0000778856 00000 n 0000778943 00000 n 0000779030 00000 n 0000779120 00000 n 0000779207 00000 n 0000779305 00000 n 0000779392 00000 n 0000779485 00000 n 0000779572 00000 n 0000779668 00000 n 0000779755 00000 n 0000779853 00000 n 0000779940 00000 n 0000780013 00000 n 0000780104 00000 n 0000780191 00000 n 0000780285 00000 n 0000780372 00000 n 0000780470 00000 n 0000780557 00000 n 0000780621 00000 n 0000780707 00000 n 0000780758 00000 n 0000780845 00000 n 0000780896 00000 n 0000780982 00000 n 0000781060 00000 n 0000781146 00000 n 0000781217 00000 n 0000781303 00000 n 0000781355 00000 n 0000781442 00000 n 0000781494 00000 n 0000781580 00000 n 0000781660 00000 n 0000781747 00000 n 0000781852 00000 n 0000781944 00000 n 0000782031 00000 n 0000782126 00000 n 0000782213 00000 n 0000782310 00000 n 0000782397 00000 n 0000782438 00000 n 0000782527 00000 n 0000782614 00000 n 0000782703 00000 n 0000782790 00000 n 0000782882 00000 n 0000782969 00000 n 0000783067 00000 n 0000783154 00000 n 0000783250 00000 n 0000783337 00000 n 0000783433 00000 n 0000783520 00000 n 0000783619 00000 n 0000783706 00000 n 0000783804 00000 n 0000783891 00000 n 0000783972 00000 n 0000784065 00000 n 0000784152 00000 n 0000784245 00000 n 0000784332 00000 n 0000784428 00000 n 0000784515 00000 n 0000784613 00000 n 0000784700 00000 n 0000784789 00000 n 0000784876 00000 n 0000784965 00000 n 0000785052 00000 n 0000785144 00000 n 0000785231 00000 n 0000785329 00000 n 0000785416 00000 n 0000785506 00000 n 0000785593 00000 n 0000785683 00000 n 0000785770 00000 n 0000785863 00000 n 0000785950 00000 n 0000786048 00000 n 0000786135 00000 n 0000786248 00000 n 0000786345 00000 n 0000786432 00000 n 0000786529 00000 n 0000786616 00000 n 0000786716 00000 n 0000786803 00000 n 0000786901 00000 n 0000786988 00000 n 0000787084 00000 n 0000787171 00000 n 0000787270 00000 n 0000787357 00000 n 0000787455 00000 n 0000787542 00000 n 0000787638 00000 n 0000787725 00000 n 0000787825 00000 n 0000787912 00000 n 0000788010 00000 n 0000788097 00000 n 0000788194 00000 n 0000788283 00000 n 0000788370 00000 n 0000788462 00000 n 0000788549 00000 n 0000788646 00000 n 0000788733 00000 n 0000788822 00000 n 0000788909 00000 n 0000789001 00000 n 0000789088 00000 n 0000789185 00000 n 0000789272 00000 n 0000789368 00000 n 0000789455 00000 n 0000789554 00000 n 0000789641 00000 n 0000789739 00000 n 0000789826 00000 n 0000789915 00000 n 0000790012 00000 n 0000790099 00000 n 0000790199 00000 n 0000790286 00000 n 0000790383 00000 n 0000790470 00000 n 0000790567 00000 n 0000790654 00000 n 0000790754 00000 n 0000790841 00000 n 0000790938 00000 n 0000791025 00000 n 0000791090 00000 n 0000791187 00000 n 0000791274 00000 n 0000791374 00000 n 0000791461 00000 n 0000791558 00000 n 0000791645 00000 n 0000791742 00000 n 0000791829 00000 n 0000791929 00000 n 0000792016 00000 n 0000792113 00000 n 0000792200 00000 n 0000792265 00000 n 0000792347 00000 n 0000792434 00000 n 0000792531 00000 n 0000792618 00000 n 0000792718 00000 n 0000792805 00000 n 0000792902 00000 n 0000792989 00000 n 0000793038 00000 n 0000793133 00000 n 0000793220 00000 n 0000793318 00000 n 0000793405 00000 n 0000793502 00000 n 0000793589 00000 n 0000793671 00000 n 0000793757 00000 n 0000793847 00000 n 0000793934 00000 n 0000794027 00000 n 0000794114 00000 n 0000794211 00000 n 0000794298 00000 n 0000794395 00000 n 0000794482 00000 n 0000794582 00000 n 0000794669 00000 n 0000794766 00000 n 0000794853 00000 n 0000794950 00000 n 0000795039 00000 n 0000795126 00000 n 0000795218 00000 n 0000795305 00000 n 0000795401 00000 n 0000795488 00000 n 0000795575 00000 n 0000795662 00000 n 0000795749 00000 n 0000795836 00000 n 0000795926 00000 n 0000796013 00000 n 0000796103 00000 n 0000796190 00000 n 0000796287 00000 n 0000796374 00000 n 0000796471 00000 n 0000796558 00000 n 0000796647 00000 n 0000796737 00000 n 0000796824 00000 n 0000796914 00000 n 0000797002 00000 n 0000797096 00000 n 0000797185 00000 n 0000797284 00000 n 0000797373 00000 n 0000797469 00000 n 0000797558 00000 n 0000797657 00000 n 0000797746 00000 n 0000797845 00000 n 0000797934 00000 n 0000798023 00000 n 0000798112 00000 n 0000798204 00000 n 0000798293 00000 n 0000798392 00000 n 0000798481 00000 n 0000798588 00000 n 0000798683 00000 n 0000798772 00000 n 0000798870 00000 n 0000798959 00000 n 0000799058 00000 n 0000799147 00000 n 0000799242 00000 n 0000799331 00000 n 0000799429 00000 n 0000799518 00000 n 0000799616 00000 n 0000799705 00000 n 0000799807 00000 n 0000799896 00000 n 0000800001 00000 n 0000800090 00000 n 0000800189 00000 n 0000800278 00000 n 0000800377 00000 n 0000800470 00000 n 0000800559 00000 n 0000800655 00000 n 0000800744 00000 n 0000800842 00000 n 0000800931 00000 n 0000801027 00000 n 0000801116 00000 n 0000801215 00000 n 0000801304 00000 n 0000801403 00000 n 0000801492 00000 n 0000801564 00000 n 0000801648 00000 n 0000801736 00000 n 0000801763 00000 n 0000801864 00000 n 0000801953 00000 n 0000802057 00000 n 0000802146 00000 n 0000802245 00000 n 0000802334 00000 n 0000802432 00000 n 0000802521 00000 n 0000802575 00000 n 0000802676 00000 n 0000802765 00000 n 0000802864 00000 n 0000802953 00000 n 0000803054 00000 n 0000803143 00000 n 0000803247 00000 n 0000803336 00000 n 0000803435 00000 n 0000803524 00000 n 0000803619 00000 n 0000803708 00000 n 0000803806 00000 n 0000803895 00000 n 0000803994 00000 n 0000804083 00000 n 0000804173 00000 n 0000804270 00000 n 0000804359 00000 n 0000804459 00000 n 0000804548 00000 n 0000804642 00000 n 0000804731 00000 n 0000804828 00000 n 0000804917 00000 n 0000805019 00000 n 0000805108 00000 n 0000805213 00000 n 0000805302 00000 n 0000805374 00000 n 0000805471 00000 n 0000805560 00000 n 0000805660 00000 n 0000805749 00000 n 0000805846 00000 n 0000805935 00000 n 0000806035 00000 n 0000806124 00000 n 0000806226 00000 n 0000806315 00000 n 0000806420 00000 n 0000806509 00000 n 0000806610 00000 n 0000806699 00000 n 0000806803 00000 n 0000806892 00000 n 0000806992 00000 n 0000807081 00000 n 0000807184 00000 n 0000807273 00000 n 0000807381 00000 n 0000807476 00000 n 0000807565 00000 n 0000807662 00000 n 0000807751 00000 n 0000807846 00000 n 0000807935 00000 n 0000808030 00000 n 0000808119 00000 n 0000808214 00000 n 0000808303 00000 n 0000808401 00000 n 0000808490 00000 n 0000808588 00000 n 0000808677 00000 n 0000808775 00000 n 0000808864 00000 n 0000808962 00000 n 0000809051 00000 n 0000809150 00000 n 0000809186 00000 n 0000809222 00000 n 0000811137 00000 n 0000811182 00000 n 0000811227 00000 n 0000811272 00000 n 0000811317 00000 n 0000811362 00000 n 0000811407 00000 n 0000811452 00000 n 0000811497 00000 n 0000811542 00000 n 0000811587 00000 n 0000811632 00000 n 0000811677 00000 n 0000811722 00000 n 0000811767 00000 n 0000811812 00000 n 0000811857 00000 n 0000811902 00000 n 0000811947 00000 n 0000811992 00000 n 0000812037 00000 n 0000812082 00000 n 0000812127 00000 n 0000812172 00000 n 0000812217 00000 n 0000812262 00000 n 0000812307 00000 n 0000812352 00000 n 0000812397 00000 n 0000812442 00000 n 0000812487 00000 n 0000812532 00000 n 0000812577 00000 n 0000812622 00000 n 0000812667 00000 n 0000812712 00000 n 0000812757 00000 n 0000812802 00000 n 0000812847 00000 n 0000812892 00000 n 0000812937 00000 n 0000812982 00000 n 0000813027 00000 n 0000813072 00000 n 0000813117 00000 n 0000813162 00000 n 0000813207 00000 n 0000813252 00000 n 0000813297 00000 n 0000813342 00000 n 0000813387 00000 n 0000813432 00000 n 0000813477 00000 n 0000813522 00000 n 0000813567 00000 n 0000813612 00000 n 0000813657 00000 n 0000813702 00000 n 0000813747 00000 n 0000813792 00000 n 0000813837 00000 n 0000813882 00000 n 0000813927 00000 n 0000813972 00000 n 0000814017 00000 n 0000814062 00000 n 0000814107 00000 n 0000814152 00000 n 0000814197 00000 n 0000814242 00000 n 0000814287 00000 n 0000814332 00000 n 0000814377 00000 n 0000814422 00000 n 0000814467 00000 n 0000814512 00000 n 0000814557 00000 n 0000814602 00000 n 0000814647 00000 n 0000814692 00000 n 0000814737 00000 n 0000814782 00000 n 0000814827 00000 n 0000814872 00000 n 0000814917 00000 n 0000814962 00000 n 0000815007 00000 n 0000815052 00000 n 0000815097 00000 n 0000815142 00000 n 0000815187 00000 n 0000815232 00000 n 0000815277 00000 n 0000815322 00000 n 0000815367 00000 n 0000815412 00000 n 0000815457 00000 n 0000815502 00000 n 0000815547 00000 n 0000815592 00000 n 0000815637 00000 n 0000815682 00000 n 0000815727 00000 n 0000815772 00000 n 0000815817 00000 n 0000815862 00000 n 0000815907 00000 n 0000815952 00000 n 0000816928 00000 n 0000817131 00000 n 0000817413 00000 n 0000817609 00000 n 0000819771 00000 n 0000819967 00000 n 0000822130 00000 n 0000822326 00000 n 0000823017 00000 n 0000823188 00000 n 0000824556 00000 n 0000824758 00000 n 0000826381 00000 n 0000826604 00000 n 0000827544 00000 n 0000827736 00000 n 0000828808 00000 n 0000828980 00000 n 0000829519 00000 n 0000829736 00000 n 0000831368 00000 n 0000831539 00000 n 0000833397 00000 n 0000833603 00000 n 0000835614 00000 n 0000835831 00000 n 0000837413 00000 n 0000837640 00000 n 0000838943 00000 n 0000839170 00000 n 0000840307 00000 n 0000840509 00000 n 0000841830 00000 n 0000842011 00000 n 0000843199 00000 n 0000843390 00000 n 0000844736 00000 n 0000844938 00000 n 0000846295 00000 n 0000846497 00000 n 0000847591 00000 n 0000847772 00000 n 0000848471 00000 n 0000848663 00000 n 0000850078 00000 n 0000850259 00000 n 0000851773 00000 n 0000851975 00000 n 0000853621 00000 n 0000853837 00000 n 0000855180 00000 n 0000855406 00000 n 0000856614 00000 n 0000856820 00000 n 0000857824 00000 n 0000858062 00000 n 0000859088 00000 n 0000859326 00000 n 0000860579 00000 n 0000860807 00000 n 0000861788 00000 n 0000861991 00000 n 0000863223 00000 n 0000863409 00000 n 0000864502 00000 n 0000864708 00000 n 0000865820 00000 n 0000866016 00000 n 0000866557 00000 n 0000866773 00000 n 0000868290 00000 n 0000868496 00000 n 0000869825 00000 n 0000870051 00000 n 0000871373 00000 n 0000871589 00000 n 0000872591 00000 n 0000872797 00000 n 0000873980 00000 n 0000874203 00000 n 0000874920 00000 n 0000875123 00000 n 0000875885 00000 n 0000876134 00000 n 0000877069 00000 n 0000877307 00000 n 0000878153 00000 n 0000878334 00000 n 0000879186 00000 n 0000879424 00000 n 0000880357 00000 n 0000880570 00000 n 0000881037 00000 n 0000881259 00000 n 0000883459 00000 n 0000883665 00000 n 0000884661 00000 n 0000884857 00000 n 0000885435 00000 n 0000885651 00000 n 0000886707 00000 n 0000886913 00000 n 0000887893 00000 n 0000888109 00000 n 0000888946 00000 n 0000889162 00000 n 0000890106 00000 n 0000890302 00000 n 0000890715 00000 n 0000890942 00000 n 0000892088 00000 n 0000892294 00000 n 0000893328 00000 n 0000893567 00000 n 0000894377 00000 n 0000894583 00000 n 0000896071 00000 n 0000896277 00000 n 0000897402 00000 n 0000897615 00000 n 0000898400 00000 n 0000898648 00000 n 0000899560 00000 n 0000899766 00000 n 0000901011 00000 n 0000901217 00000 n 0000902286 00000 n 0000902502 00000 n 0000903658 00000 n 0000903874 00000 n 0000904843 00000 n 0000905039 00000 n 0000905835 00000 n 0000906041 00000 n 0000907117 00000 n 0000907303 00000 n 0000908324 00000 n 0000908510 00000 n 0000909431 00000 n 0000909617 00000 n 0000910464 00000 n 0000910650 00000 n 0000911545 00000 n 0000911731 00000 n 0000912581 00000 n 0000912767 00000 n 0000913711 00000 n 0000913897 00000 n 0000914972 00000 n 0000915168 00000 n 0000916274 00000 n 0000916480 00000 n 0000917711 00000 n 0000917927 00000 n 0000919121 00000 n 0000919327 00000 n 0000921190 00000 n 0000921386 00000 n 0000922232 00000 n 0000922438 00000 n 0000923549 00000 n 0000923745 00000 n 0000924837 00000 n 0000925033 00000 n 0000926429 00000 n 0000926645 00000 n 0000927751 00000 n 0000927989 00000 n 0000928753 00000 n 0000928959 00000 n 0000929816 00000 n 0000930054 00000 n 0000930886 00000 n 0000931092 00000 n 0000932182 00000 n 0000932408 00000 n 0000933584 00000 n 0000933791 00000 n 0000934734 00000 n 0000934942 00000 n 0000936057 00000 n 0000936264 00000 n 0000936937 00000 n 0000937166 00000 n 0000937949 00000 n 0000938140 00000 n 0000939009 00000 n 0000939201 00000 n 0000939993 00000 n 0000940206 00000 n 0000940958 00000 n 0000941175 00000 n 0000942752 00000 n 0000942949 00000 n 0000943906 00000 n 0000944087 00000 n 0000945480 00000 n 0000945683 00000 n 0000946689 00000 n 0000946886 00000 n 0000947751 00000 n 0000947948 00000 n 0000948848 00000 n 0000949045 00000 n 0000950115 00000 n 0000950276 00000 n 0000950505 00000 n 0000950564 00000 n 0000950667 00000 n 0000950821 00000 n 0000950944 00000 n 0000951054 00000 n 0000951176 00000 n 0000951289 00000 n 0000951472 00000 n 0000951619 00000 n 0000951731 00000 n 0000951855 00000 n 0000951970 00000 n 0000952079 00000 n 0000952250 00000 n 0000952379 00000 n 0000952486 00000 n 0000952607 00000 n 0000952722 00000 n 0000952845 00000 n 0000952969 00000 n 0000953076 00000 n 0000953252 00000 n 0000953374 00000 n 0000953498 00000 n 0000953616 00000 n 0000953739 00000 n 0000953852 00000 n 0000954022 00000 n 0000954151 00000 n 0000954247 00000 n 0000954342 00000 n 0000954510 00000 n 0000954646 00000 n 0000954750 00000 n 0000954868 00000 n 0000954997 00000 n 0000955144 00000 n 0000955282 00000 n 0000955441 00000 n 0000955575 00000 n 0000955686 00000 n 0000955809 00000 n 0000955968 00000 n 0000956064 00000 n 0000956239 00000 n 0000956362 00000 n 0000956465 00000 n 0000956601 00000 n 0000956717 00000 n 0000956821 00000 n 0000956975 00000 n 0000957098 00000 n 0000957200 00000 n 0000957340 00000 n 0000957463 00000 n 0000957573 00000 n 0000957699 00000 n 0000957821 00000 n 0000957944 00000 n trailer <]>> startxref 958347 %%EOF ga-5.9.2/doc/style000066400000000000000000000513131500715745200137570ustar00rootroot00000000000000############################################################################## # This is the Artistic Style (astyle) options file used by Global Arrays. ############################################################################## # # The command line options have precedence. If there is a conflict between # a command line option and an option in the default options file, the # command line option will be used. # # Lines within this options file that begin with '#' are considered # line-comments. # # astyle looks for this file in the following locations (in order): # 1. the file indicated by the --options= command line option; # 2. the file and directory indicated by the environment variable # ARTISTIC_STYLE_OPTIONS (if it exists); # 3. the file named .astylerc in the directory pointed to by the HOME # environment variable (e.g. "$HOME/.astylerc" on Linux); # 4. the file named astylerc in the directory pointed to by the # USERPROFILE environment variable (e.g. "%USERPROFILE%\astylerc" on # Windows). # ############################################################################## ### Global Arrays Style Options ############################################## ############################################################################## # --indent=spaces=2 --brackets=linux --indent-classes --indent-switches --indent-namespaces --indent-col1-comments --max-instatement-indent=40 --pad-oper --pad-header --unpad-paren --break-closing-brackets --add-brackets --convert-tabs --align-pointer=name # ############################################################################## ### Tab and Bracket Options ################################################## ############################################################################## # ## default indent option ## If no indentation option is set, the default option of 4 spaces will be ## used (e.g. -s4 --indent=spaces=4). # # ---------------------------------------------------------------------------- # --indent=spaces / --indent=spaces=# / -s# # Indent using # spaces per indent (e.g. -s6 --indent=spaces=6). # must be # between 2 and 20. Not specifying # will result in a default of 4 spaces per # indent. # # ---------------------------------------------------------------------------- # --indent=tab / --indent=tab=# / -t / -t# # Indent using tab characters. Treat each tab as # spaces # (e.g. -t6 / --indent=tab=6). # must be between 2 and 20. If no # is set, # treats tabs as 4 spaces. # # ---------------------------------------------------------------------------- # --indent=force-tab / --indent=force-tab=# / -T / -T# # Indent using tab characters. Treat each tab as # spaces # (e.g. -T6 / --indent=force-tab=6). Uses tabs as indents where ‑‑indent=tab # prefers to use spaces, such as inside multi-line statements. # must be # between 2 and 20. If no # is set, treats tabs as 4 spaces. # # ---------------------------------------------------------------------------- ## default brackets option ## If no brackets option is set, the brackets will not be changed. # # ---------------------------------------------------------------------------- # --brackets=break / -b # Break brackets from their pre-block statements ( e.g. Allman / ANSI style ). # # void Foo(bool isFoo) # { # if (isFoo) # { # bar(); # } # else # { # anotherBar(); # } # } # # ---------------------------------------------------------------------------- # --brackets=attach / -a # Attach brackets to their pre-block statements ( e.g. Java style ). # # void Foo(bool isFoo) { # if (isFoo) { # bar(); # } else { # anotherBar(); # } # } # # ---------------------------------------------------------------------------- # --brackets=linux / -l # Break brackets from namespace, class, and function definitions, but attach # brackets to statements within a function ( e.g. K&R / Linux style ). # # With C++ files brackets are attached for function definitions within a class # (inline class functions). The brackets are also attached for arrays, structs, # enums, and other top level objects that are not classes or functions. This # option is effective for C/C++ files only. # # void Foo(bool isFoo) # { # if (isFoo) { # bar(); # } else { # anotherBar; # } # } # # ---------------------------------------------------------------------------- # --brackets=stroustrup / -u # Break brackets from function definitions only. Attach brackets to # namespaces, classes, and statements within a function ( e.g. Stroustrup # style ). # # With C++ files brackets are attached for function definitions within a class # (inline class functions). The brackets are also attached for arrays, structs, # enums, and other top level objects that are not classes or functions. This # option is effective for C/C++ files only. # # void Foo(bool isFoo) # { # if (isFoo) { # bar(); # } else { # anotherBar; # } # } # # ---------------------------------------------------------------------------- # --brackets=horstmann / -g # Break brackets from their pre-block statements but allow run-in statements # on the same line as an opening bracket ( e.g. Horstmann style ). # # void Foo(bool isFoo) # { if (isFoo()) # { bar1(); # bar2(); # } # else # { anotherBar(); # } # } # ############################################################################## ### Indentation Options ###################################################### ############################################################################## # # --indent-classes / -C # Indent 'class' and 'struct' blocks so that the blocks 'public:', # 'protected:' and 'private:' are indented. The struct blocks are indented # only if an access modifier is declared somewhere in the struct. The entire # block is indented. This option is effective for C++ files only. # # class Foo # { # public: # Foo(); # virtual ~Foo(); # }; # # becomes: # # class Foo # { # public: # Foo(); # virtual ~Foo(); # }; # # ---------------------------------------------------------------------------- # --indent-switches / -S # Indent 'switch' blocks so that the 'case X:' statements are indented in the # switch block. The entire case block is indented. # # switch (foo) # { # case 1: # a += 1; # break; # # case 2: # { # a += 2; # break; # } # } # # becomes: # # switch (foo) # { # case 1: # a += 1; # break; # # case 2: # { # a += 2; # break; # } # } # # ---------------------------------------------------------------------------- # --indent-cases / -K # Indent 'case X:' blocks from the 'case X:' headers. Case statements not # enclosed in blocks are NOT indented. # # switch (foo) # { # case 1: # a += 1; # break; # # case 2: # { # a += 2; # break; # } # } # # becomes: # # switch (foo) # { # case 1: # a += 1; # break; # # case 2: # { # a += 2; # break; # } # } # # ---------------------------------------------------------------------------- # --indent-brackets / -B # Add extra indentation to brackets. This is the option used for Whitesmith # and Banner style formatting/indenting. If both ‑‑indent‑brackets and # ‑‑indent‑blocks are used the result will be ‑‑indent‑blocks. This option # will be ignored if used with a predefined style. # # if (isFoo) # { # bar(); # } # else # anotherBar(); # # becomes: # # if (isFoo) # { # bar(); # } # else # anotherBar(); # # ---------------------------------------------------------------------------- # --indent-blocks / -G # Add extra indentation to blocks within a function. The opening bracket for # namespaces, classes, and functions is not indented. This is the option used # for GNU style formatting/indenting. This option will be ignored if used with # a predefined style. # # if (isFoo) # { # bar(); # } # else # anotherBar(); # # becomes: # # if (isFoo) # { # bar(); # } # else # anotherBar(); # # ---------------------------------------------------------------------------- # --indent-namespaces / -N # Add extra indentation to namespace blocks. This option has no effect on Java # files. # # namespace foospace # { # class Foo # { # public: # Foo(); # virtual ~Foo(); # }; # } # # becomes: # # namespace foospace # { # class Foo # { # public: # Foo(); # virtual ~Foo(); # }; # } # # ---------------------------------------------------------------------------- # --indent-labels / -L # Add extra indentation to labels so they appear 1 indent less than the # current indentation, rather than being flushed to the left (the default). # # void Foo() { # while (isFoo) { # if (isFoo) # goto error; # ... # error: # ... # } # } # # becomes (with indented 'error:'): # # void Foo() { # while (isFoo) { # if (isFoo) # goto error; # ... # error: # ... # } # } # # ---------------------------------------------------------------------------- # --indent-preprocessor / -w # Indent multi-line preprocessor definitions ending with a backslash. Should # be used with --convert-tabs for proper results. Does a pretty good job, but # can not perform miracles in obfuscated preprocessor definitions. Without # this option the preprocessor statements remain unchanged. # # #define Is_Bar(arg,a,b) \ # (Is_Foo((arg), (a)) \ # || Is_Foo((arg), (b))) # # becomes: # # #define Is_Bar(arg,a,b) \ # (Is_Foo((arg), (a)) \ # || Is_Foo((arg), (b))) # # ---------------------------------------------------------------------------- # --indent-col1-comments / -Y # Indent C++ comments beginning in column one. By default C++ comments # beginning in column one are not indented. This option will allow the # comments to be indented with the code. # # void Foo()\n" # { # // comment # if (isFoo) # bar(); # } # # becomes: # # void Foo()\n" # { # // comment # if (isFoo) # bar(); # } # # ---------------------------------------------------------------------------- # --max-instatement-indent=# / -M# # Indent a maximum of # spaces in a continuous statement, relative to the # previous line (e.g. ‑‑max‑instatement‑indent=40). # must be less than 80. If # no # is set, the default value of 40 will be used. A maximum of less than # two indent lengths will be ignored. # # fooArray[] = { red, # green, # blue }; # # fooFunction(barArg1, # barArg2, # barArg3); # # becomes (with larger value): # # fooArray[] = { red, # green, # blue }; # # fooFunction(barArg1, # barArg2, # barArg3); # # ---------------------------------------------------------------------------- # --min-conditional-indent=# / -m# # Set the minimal indent that is added when a header is built of # multiple-lines. This indent makes helps to easily separate the header from # the command statements that follow. The value for # must be less than 40. # The default setting for this option is twice the current indent # (e.g. --min-conditional-indent=8). # # // default setting makes this non-bracketed code clear # if (a < b # || c > d) # foo++; # # // but creates an exaggerated indent in this bracketed code # if (a < b # || c > d) # { # foo++; # } # # becomes (when setting --min-conditional-indent=0): # # // setting makes this non-bracketed code less clear # if (a < b # || c > d) # foo++; # # // but makes this bracketed code clearer # if (a < b # || c > d) # { # foo++; # } # ############################################################################## ### Padding Options ########################################################## ############################################################################## # # --break-blocks / -f # Pad empty lines around header blocks (e.g. 'if', 'for', 'while'...). # # isFoo = true; # if (isFoo) { # bar(); # } else { # anotherBar(); # } # isBar = false; # # becomes: # # isFoo = true; # # if (isFoo) { # bar(); # } else { # anotherBar(); # } # # isBar = false; # # ---------------------------------------------------------------------------- # --break-blocks=all / -F # Pad empty lines around header blocks (e.g. 'if', 'for', 'while'...). Treat # closing header blocks (e.g. 'else', 'catch') as stand-alone blocks. # # isFoo = true; # if (isFoo) { # bar(); # } else { # anotherBar(); # } # isBar = false; # # becomes: # # isFoo = true; # # if (isFoo) { # bar(); # # } else { # anotherBar(); # } # # isBar = false; # # ---------------------------------------------------------------------------- # --pad-oper / -p # Insert space padding around operators. Operators inside block parens [] are # not padded. Any end of line comments will remain in the original column, if # possible. Note that there is no option to unpad. Once padded, they stay # padded. # # if (foo==2) # a=bar((b-c)*a,*d--); # # becomes: # # if (foo == 2) # a = bar((b - c) * a, * d--); # # ---------------------------------------------------------------------------- # --pad-paren / -P # Insert space padding around parenthesis on both the outside and the inside. # Any end of line comments will remain in the original column, if possible. # # if (isFoo(a, b)) # bar(a, b); # # becomes: # # if ( isFoo ( a, b ) ) # bar ( a, b ); # # ---------------------------------------------------------------------------- # --pad-paren-out / -d # Insert space padding around parenthesis on the outside only. Any end of line # comments will remain in the original column, if possible. This can be used # with unpad-paren below to remove unwanted spaces. # # if (isFoo(a, b)) # bar(a, b); # # becomes: # # if (isFoo (a, b) ) # bar (a, b); # # ---------------------------------------------------------------------------- # --pad-paren-in / -D # Insert space padding around parenthesis on the inside only. Any end of line # comments will remain in the original column, if possible. This can be used # with unpad-paren below to remove unwanted spaces. # # if (isFoo(a, b)) # bar(a, b); # # becomes: # # if ( isFoo( a, b ) ) # bar( a, b ); # # ---------------------------------------------------------------------------- # --pad-header / -H # Insert space padding after paren headers only (e.g. 'if', 'for', 'while'...). # Any end of line comments will remain in the original column, if possible. # This can be used with unpad-paren to remove unwanted spaces. # # if(isFoo(a, b)) # bar(a, b); # # becomes: # # if (isFoo(a, b)) # bar(a, b); # # ---------------------------------------------------------------------------- # --unpad-paren / -U # Remove extra space padding around parenthesis on the inside and outside. # Any end of line comments will remain in the original column, if possible. # This option can be used in combination with the paren padding options # pad‑paren, pad‑paren‑out, pad‑paren‑in, and pad‑header above. Only padding # that has not been requested by other options will be removed. # # For example, if a source has parens padded on both the inside and outside, # and you want inside only. You need to use unpad-paren to remove the outside # padding, and pad‑paren‑in to retain the inside padding. Using only # pad‑paren‑in would not remove the outside padding. # # if ( isFoo( a, b ) ) # bar ( a, b ); # # becomes (with no padding option requested): # # if (isFoo(a, b)) # bar(a, b); # # ---------------------------------------------------------------------------- # --delete-empty-lines / -x # Delete empty lines within a function or method. Empty lines outside of # functions or methods are NOT deleted. If used with break-blocks or # break-blocks=all it will delete all lines EXCEPT the lines added by the # break-blocks options. # # void Foo() # { # # foo1 = 1; # # foo2 = 2; # # } # # becomes: # # void Foo() # { # foo1 = 1; # foo2 = 2; # } # # ---------------------------------------------------------------------------- # --fill-empty-lines / -E # Fill empty lines with the white space of the previous line. # ############################################################################## ### Formatting Options ####################################################### ############################################################################## # # --break-closing-brackets / -y # When used with --brackets=attach, --brackets=linux, or --brackets=stroustrup, # this breaks closing headers (e.g. 'else', 'catch', ...) from their # immediately preceding closing brackets. Closing header brackets are always # broken with broken brackets, horstmann brackets, indented blocks, and # indented brackets. # # void Foo(bool isFoo) { # if (isFoo) { # bar(); # } else { # anotherBar(); # } # } # # becomes (with a broken 'else'): # # void Foo(bool isFoo) { # if (isFoo) { # bar(); # } # else { # anotherBar(); # } # } # # ---------------------------------------------------------------------------- # --break-elseifs / -e # Break "else if" header combinations into separate lines. This option has no # effect if keep-one-line-statements is used, the "else if" statements will # remain as they are. # # If this option is NOT used, "else if" header combinations will be placed on # a single line. # # if (isFoo) { # bar(); # } # else if (isFoo1()) { # bar1(); # } # else if (isFoo2()) } # bar2; # } # # becomes: # # if (isFoo) { # bar(); # } # else # if (isFoo1()) { # bar1(); # } # else # if (isFoo2()) { # bar2(); # } # # ---------------------------------------------------------------------------- # --add-brackets / -j # Add brackets to unbracketed one line conditional statements (e.g. 'if', # 'for', 'while'...). The statement must be on a single line. The brackets # will be added according to the currently requested predefined style or # bracket type. If no style or bracket type is requested the brackets will be # attached. If --add-one-line-brackets is also used the result will be one # line brackets. # # if (isFoo) # isFoo = false; # # becomes: # # if (isFoo) { # isFoo = false; # } # # ---------------------------------------------------------------------------- # --add-one-line-brackets / -J # Add one line brackets to unbracketed one line conditional statements (e.g. # 'if', 'for', 'while'...). The statement must be on a single line. The option # implies --keep-one-line-blocks and will not break the one line blocks. # # if (isFoo) # isFoo = false; # # becomes: # # if (isFoo) # { isFoo = false; } # # ---------------------------------------------------------------------------- # --keep-one-line-blocks / -O # Don't break one-line blocks. # # if (isFoo) # { isFoo = false; cout << isFoo << endl; } # # remains unchanged. # # ---------------------------------------------------------------------------- # --keep-one-line-statements / -o # Don't break complex statements and multiple statements residing on a single # line. # # if (isFoo) # { # isFoo = false; cout << isFoo << endl; # } # # remains unchanged. # # if (isFoo) DoBar(); # # remains unchanged. # # ---------------------------------------------------------------------------- # --convert-tabs / -c # Converts tabs into spaces in the non-indentation part of the line. The # number of spaces inserted will maintain the spacing of the tab. The current # setting for spaces per tab is used. It may not produce the expected results # if convert-tabs is used when changing spaces per tab. Tabs are not replaced # in quotes. # # ---------------------------------------------------------------------------- # --align-pointer=type / -k1 # --align-pointer=middle / -k2 # --align-pointer=name / -k3 # Attach a pointer or reference operator (* or &) to either the variable type # (left) or variable name (right), or place it between the type and name. The # spacing between the type and name will be preserved, if possible. This # option is effective for C/C++ files only. # # char *foo1; # # becomes (with align-pointer=type): # # char* foo1; # # char* foo2; # # becomes (with align-pointer=middle): # # char * foo2; # # char& foo3; # # becomes (with align-pointer=name): # # char &foo3; # # ---------------------------------------------------------------------------- # --mode=c # --mode=cs # --mode=java # Indent a C/C++, C#, or Java file. The option is usually set from the file # extension for each file. You can override the setting with this entry. It # will be used for all files regardless of the file extension. It allows the # formatter to identify language specific syntax such as C++ classes, # templates, and keywords. # ga-5.9.2/ga++/000077500000000000000000000000001500715745200126415ustar00rootroot00000000000000ga-5.9.2/ga++/CMakeLists.txt000066400000000000000000000067611500715745200154130ustar00rootroot00000000000000# # module: CMakeLists.txt # author: Bruce Palmer # description: implements a primative CMake build that can be used to build # GA on Windows-based systems. Only MPI-based runtimes are # supported. # # DISCLAIMER # # This material was prepared as an account of work sponsored by an # agency of the United States Government. Neither the United States # Government nor the United States Department of Energy, nor Battelle, # nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR # ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, # COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, # SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT # INFRINGE PRIVATELY OWNED RIGHTS. # # # ACKNOWLEDGMENT # # This software and its documentation were produced with United States # Government support under Contract Number DE-AC06-76RLO-1830 awarded by # the United States Department of Energy. The United States Government # retains a paid-up non-exclusive, irrevocable worldwide license to # reproduce, prepare derivative works, perform publicly and display # publicly by or for the US Government, including the right to # distribute to other US Government contractors. # # -*- mode: cmake -*- # ------------------------------------------------------------- # file: CMakeLists.txt # ------------------------------------------------------------- # ------------------------------------------------------------- # GA++ header installation # ------------------------------------------------------------- set(GAXX_HEADERS src/ga++.h src/GAServices.h src/GlobalArray.h src/init_term.h src/PGroup.h src/services.h ) install (FILES ${GAXX_HEADERS} DESTINATION include/ga ) list (APPEND GA_HEADER_PATHS ${CMAKE_CURRENT_LIST_DIR}/src) set (GA_HEADER_PATHS ${GA_HEADER_PATHS} PARENT_SCOPE) # ------------------------------------------------------------- # GA++ library installation # ------------------------------------------------------------- add_library(ga++ src/GAServices.cc src/GlobalArray.cc src/init_term.cc src/overload.cc src/PGroup.cc src/services.cc ) set (_gapp_inc_dirs ${CMAKE_CURRENT_BINARY_DIR} ${PROJECT_SOURCE_DIR}/ga++/src ${PROJECT_SOURCE_DIR}/global/src ${PROJECT_SOURCE_DIR}/global/testing ${PROJECT_SOURCE_DIR}/ma ${PROJECT_BINARY_DIR}/ma ${PROJECT_BINARY_DIR}/gaf2c ${PROJECT_BINARY_DIR}) target_include_directories(ga++ BEFORE PRIVATE ${_gapp_inc_dirs}) target_link_libraries(ga++) install (TARGETS ga++ DESTINATION lib ) function(gapp_add_parallel_test test_name test_srcs) get_filename_component(_test_name_only "${test_name}" NAME) ga_add_parallel_test(${test_name} ${test_srcs}) target_include_directories(${_test_name_only}.x PRIVATE ${_gapp_inc_dirs}) target_link_libraries(${_test_name_only}.x ga++) endfunction() if(ENABLE_TESTS) gapp_add_parallel_test(ga++/elempatch_cpp ${PROJECT_SOURCE_DIR}/ga++/testing/elempatch.cc) gapp_add_parallel_test(ga++/mtest_cpp ${PROJECT_SOURCE_DIR}/ga++/testing/mtest.cc) gapp_add_parallel_test(ga++/ntestc_cpp ${PROJECT_SOURCE_DIR}/ga++/testing/ntestc.cc) gapp_add_parallel_test(ga++/testc_cpp ${PROJECT_SOURCE_DIR}/ga++/testing/testc.cc) gapp_add_parallel_test(ga++/testmult_cpp ${PROJECT_SOURCE_DIR}/ga++/testing/testmult.cc) gapp_add_parallel_test(ga++/thread-safe_cpp ${PROJECT_SOURCE_DIR}/ga++/testing/thread-safe.cc) endif() ga-5.9.2/ga++/README000066400000000000000000000073271500715745200135320ustar00rootroot00000000000000GA++: C++ Bindings for Global Arrays ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Overview ======== GA++ provides a C++ interface to global arrays (GA) libraries. This is the second beta release. Here is the doxygen documentation of GA++: http://www.emsl.pnl.gov/docs/global/ga++/index.html The GA C++ bindings are a layer built directly on top of the GA C bindings. GA++ provides new names for the C bindings of GA functions (For example, GA_Add_patch() is renamed as addPatch()). GA++ classes ============ GA++ declares a GA namespace. Within this namespace is the GlobalArray class and various "service" functions. For backwards compatibility, the GAServices class and its GA::SERVICES singleton is preserved within the GA namespace, however we recommend using the same functions found within the GA namespace. Although namespace is part of ANSI C++ standard, not all C++ compilers support namespaces. The configure script will automatically detect whether namespaces are supported. In the rare case where namespaces are not supported, you will not be able to use the GA++ interface:: namespace GA { class GAServices; class GlobalArray; }; The current implementation has no derived classes (no (virtual) inheritance), templates or exception handling. Eventually, more object oriented functionalities will be added, and standard library facilities will be used without affecting the performance. Initialization and Termination ============================== GA namespace has the following functions for initialization and termination of Global Arrays:: GA::Initialize(): Initialize Global Arrays, allocates and initializes internal data structures in Global Arrays. This is a collective operation. GA::Terminate(): Delete all active arrays and destroy internal data structures. This is a collective operation. namespace GA { void Initialize(int argc, char *argv[], size_t limit = 0); void Initialize(int argc, char *argv[], unsigned long heapSize, unsigned long stackSize, int type, size_t limit = 0); void Terminate(); }; Example: #include #include "ga++.h" int main(int argc, char **argv) { GA::Initialize(argc, argv, 0); cout << "Hello World\n"; GA::Terminate(); } GAServices ========== NOTE: The GAServices class is deprecated in favor of using the same functions directly within the GA namespace. GAServices class has member functions that does all the global operations (non-array operations) like Process Information (number of processes, process id, ..), Inter-process Synchronization (sync, lock, broadcast, reduce,..), etc,. SERVICES Object =============== NOTE: The SERVICES object is deprecated. See GAServices above. GA namespace has a global singleton "SERVICES" object (of type "GAServices"), which can be used to invoke the non-array operations. To call the functions (for example, sync()), we invoke them on this SERVICES object (for example, GA::SERVICES.sync()). As this object is in the global address space, the functions can be invoked from anywhere inside the program (provided the ga++.h is included in that file/program). Global Array ============ GlobalArray class has member functions that do: * Array operations, * One-sided(get/put), * Collective array operations, * Utility operations , etc,. Note: In order to build GA++, configure must be run with --enable-cxx:: configure --enable-cxx Testing ======= If the GA++ interface is enabled, the GA++ test programs located in ga++/testing will automatically be built and run as part of the GA test suite. See the top-level README for details. ga-5.9.2/ga++/doc/000077500000000000000000000000001500715745200134065ustar00rootroot00000000000000ga-5.9.2/ga++/doc/Doxyfile000066400000000000000000002041061500715745200151170ustar00rootroot00000000000000# Doxyfile 1.7.0 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a 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. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "Global Arrays C++ Binding" # 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 = 5.0 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # 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 = # 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 cause performance problems for the file system. 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. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) 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. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) 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. 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" "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. 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. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then 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. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then 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. 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 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. 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 # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) 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 comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) 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 behaviour. # 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 behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. 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. 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. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # 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 = # 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. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. 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. 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, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. 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 that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = cc=C++ h=C++ # 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); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip 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. 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 (the default) # 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. 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. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) 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. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT 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. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_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 and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. 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. 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 (the default) only methods in the interface are included. 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. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) 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. 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 (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. 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 (the default) these declarations will be included in the # documentation. 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 (the default) these blocks will be appended to the # function's detailed documentation block. 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 (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. 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. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. 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. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) 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. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. 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 default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to 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 default) # the group names will appear in their defined order. 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 default), 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. SORT_BY_SCOPE_NAME = 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. 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. 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. 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. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of 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 initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # 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. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # 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 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 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 , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. 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. The 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. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED 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. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR 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. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = YES # 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) 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 stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be 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. INPUT = ../src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. 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. 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 pattern (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 *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.c \ *.h \ *.f \ *.fh # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # 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. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # 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 = # 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 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. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are 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. INPUT_FILTER = # 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 # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # 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 also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. 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. 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. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # 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. REFERENCES_LINK_SOURCE = 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. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) 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. 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. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # 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 one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. 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. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. 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 the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # 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. # The allowed range is 0 to 359. 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. 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. 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. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # 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. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # 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, 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. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # 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. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, 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. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_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. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, 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. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, 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. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, 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). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, 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. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. 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. 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. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace 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 # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. 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. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, 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. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, 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. 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. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # 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. GENERATE_TREEVIEW = NO # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # 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. 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. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # 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. FORMULA_FONTSIZE = 10 # 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. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvances is that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans.ttf # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES ga-5.9.2/ga++/src/000077500000000000000000000000001500715745200134305ustar00rootroot00000000000000ga-5.9.2/ga++/src/GAServices.cc000066400000000000000000000113541500715745200157360ustar00rootroot00000000000000/** * @file GAServices.cc * @author Manoj Kumar Krishnan, PNNL. */ #if HAVE_CONFIG_H # include "config.h" #endif #include "ga++.h" GA::GAServices::GAServices() { } GA::GAServices::~GAServices() { } GA::GlobalArray * GA::GAServices::createGA(int type, int ndim, int dims[], char *arrayname, int chunk[]) { GA::GlobalArray * GA = new GA::GlobalArray(type, ndim, dims, arrayname, chunk); return GA; } GA::GlobalArray * GA::GAServices::createGA(int type, int ndim, int dims[], char *arrayname, int block[], int maps[]) { GA::GlobalArray * GA = new GA::GlobalArray(type, ndim, dims, arrayname, block, maps); return GA; } GA::GlobalArray * GA::GAServices::createGA(const GA::GlobalArray *g_b, char *arrayname) { GA::GlobalArray * GA = new GA::GlobalArray(*g_b, arrayname); return GA; } GA::GlobalArray * GA::GAServices::createGA(const GA::GlobalArray &g_b) { GA::GlobalArray * GA = new GA::GlobalArray(g_b); return GA; } GA::GlobalArray * GA::GAServices::createGA() { GA::GlobalArray * GA = new GA::GlobalArray(); return GA; } GA::GlobalArray * GA::GAServices::createGA_Ghosts(int type, int ndim, int dims[], int width[], char *array_name, int chunk[]) { /* last argument is a dummy argument, just to increase the count of the number of arguments, inorder to avoid conflict in # of args */ GA::GlobalArray * GA = new GA::GlobalArray(type, ndim, dims, width, array_name, chunk, 'g'); return GA; } GA::GlobalArray * GA::GAServices::createGA_Ghosts(int type, int ndim, int dims[], int width[], char *array_name, int map[], int nblock[]) { GA::GlobalArray * GA = new GA::GlobalArray(type, ndim, dims, width, array_name, map, nblock, 'g'); return GA; } int GA::GAServices::getDebug() { return GA_Get_debug(); } void GA::GAServices::brdcst(void *buf, int lenbuf, int root) { GA_Brdcst(buf, lenbuf, root); } int GA::GAServices::clusterNnodes() { return GA_Cluster_nnodes(); } int GA::GAServices::clusterNodeid() { return GA_Cluster_nodeid(); } int GA::GAServices::clusterProcNodeid(int iproc) { return GA_Cluster_proc_nodeid(iproc); } int GA::GAServices::clusterNprocs(int inode) { return GA_Cluster_nprocs(inode) ; } int GA::GAServices::clusterProcid(int inode, int iproc) { return GA_Cluster_procid(inode, iproc); } int GA::GAServices::createMutexes(int number) { return GA_Create_mutexes(number); } int GA::GAServices::deregisterType(int type) { return NGA_Deregister_type(type); } int GA::GAServices::destroyMutexes() { return GA_Destroy_mutexes(); } void GA::GAServices::dgop(double x[], int n, char *op) { GA_Dgop(x, n, op); } int GA::GAServices::duplicate(int g_a, char* array_name) { return GA_Duplicate(g_a, array_name); } void GA::GAServices::error(const char *message, int code) { GA_Error((char *)message, code); } void GA::GAServices::fence() { GA_Fence(); } void GA::GAServices::gop(int x[], int n, char *op) { GA_Igop(x, n, op); } void GA::GAServices::gop(long x[], int n, char *op) { GA_Lgop(x, n, op); } void GA::GAServices::gop(float x[], int n, char *op) { GA_Fgop(x, n, op); } void GA::GAServices::gop(double x[], int n, char *op) { GA_Dgop(x, n, op); } void GA::GAServices::igop(int x[], int n, char *op) { GA_Igop(x, n, op); } void GA::GAServices::initFence() { GA_Init_fence(); } size_t GA::GAServices::inquireMemory() { return GA_Inquire_memory(); } void GA::GAServices::lgop(long x[], int n, char *op) { GA_Lgop(x, n, op); } void GA::GAServices::lock(int mutex) { GA_Lock(mutex); } void GA::GAServices::maskSync(int first, int last) { GA_Mask_sync(first, last); } int GA::GAServices::memoryAvailable() { return GA_Memory_avail(); } int GA::GAServices::memoryLimited() { return GA_Memory_limited(); } int GA::GAServices::nbTest(GANbhdl *nbhandle) { return NGA_NbTest(nbhandle); } void GA::GAServices::nbWait(GANbhdl *nbhandle) { NGA_NbWait(nbhandle); } int GA::GAServices::nodeid() { return GA_Nodeid(); } int GA::GAServices::nodes() { return GA_Nnodes(); } void GA::GAServices::printStats() { GA_Print_stats(); } int GA::GAServices::registerType(size_t size) { return NGA_Register_type(size); } void GA::GAServices::setDebug(int dbg) { return GA_Set_debug(dbg); } void GA::GAServices::setMemoryLimit(size_t limit) { GA_Set_memory_limit(limit); } void GA::GAServices::summarize(int verbose) { GA_Summarize(verbose); } void GA::GAServices::sync() { GA_Sync(); } void GA::GAServices::unlock(int mutex) { GA_Unlock(mutex); } int GA::GAServices::usesMA() { return GA_Uses_ma(); } int GA::GAServices::usesFAPI() { return GA_Uses_fapi(); } double GA::GAServices::wtime() { return GA_Wtime(); } GA::GAServices GA::SERVICES; ga-5.9.2/ga++/src/GAServices.h000066400000000000000000000631151500715745200156020ustar00rootroot00000000000000#ifndef _GA_SERVICES_H #define _GA_SERVICES_H namespace GA { class GlobalArray; /** * Global Arrays Services class. * * @author Manoj Kumar Krishnan, PNNL. * @deprecated Please use the same functions found within the GA namespace. * * Collecting the global information: who am I, and how many processors * are being used. Initialize the communication library (either MPI or * TCSMSG) and Global array. Allocate momory to be used by GA by calling MA * and create global arrays. */ class GAServices { public: /** * Null-constructor. The component won't really be 'alive' * much at all until after setServices is called on it. */ GAServices(); /** Destructor. */ ~GAServices(); /** * Creates an ndim-dimensional array using the regular distribution model * and returns integer handle representing the array. * * The array can be distributed evenly or not. The control over the * distribution is accomplished by specifying chunk (block) size for all or * some of array dimensions. * * For example, for a 2-dimensional array, setting chunk[0]=dim[0] gives * distribution by vertical strips (chunk[0]*dims[0]); * setting chunk[1]=dim[1] gives distribution by horizontal strips * (chunk[1]*dims[1]). Actual chunks will be modified so that they are at * least the size of the minimum and each process has either zero or one * chunk. Specifying chunk[i] as <1 will cause that dimension to be * distributed evenly. * * As a convenience, when chunk is specified as NULL, the entire array is * distributed evenly. * * This is a collective operation. * * @param[in] type data type(MT_F_DBL,MT_F_INT,MT_F_DCPL) * @param[in] ndim number of array dimensions * @param[in] dims[ndim] array of dimensions * @param[in] arrayname a unique character string * @param[in] chunk[ndim] array of chunks, each element specifies * minimum size that given dimensions should be * chunked up into * * @return pointer to GlobalArray object created; NULL if it fails */ GlobalArray * createGA(int type, int ndim, int dims[], char *arrayname, int chunk[]); /** * Creates an array by following the user-specified distribution and * returns integer handle representing the array. * * The distribution is specified as a Cartesian product of distributions * for each dimension. The array indices start at 0. For example, the * following figure demonstrates distribution of a 2-dimensional array 8x10 * on 6 (or more) processors. nblock[2]={3,2}, the size of map array is s=5 * and array map contains the following elements map={0,2,6, 0, 5}. The * distribution is nonuniform because, P1 and P4 get 20 elements each and * processors P0,P2,P3, and P5 only 10 elements each. * * * * * * *
5 5
P0 P3 2
P1 P4 4
P2 P5 2
* * This is a collective operation. * * @param[in] arrayname a unique character string * @param[in] type MA data type (MT_F_DBL,MT_F_INT,MT_F_DCPL) * @param[in] ndim number of array dimensions * @param[in] dims array of dimension values * @param[in] block [ndim] no. of blocks each dimension is divided into * @param[in] maps [s] starting index for for each block; * the size s is a sum all elements of nblock array * * @return pointer to GlobalArray object created; NULL if it fails */ GlobalArray * createGA(int type, int ndim, int dims[], char *arrayname, int block[], int maps[]); /** * Creates a new array by applying all the properties of another existing * array. * * This is a collective operation. * * @param[in] arrayname a character string * @param[in] g_b integer handle for reference array * * @return pointer to GlobalArray object created; NULL if it fails */ GlobalArray * createGA(const GlobalArray *g_b, char *arrayname); /** * Creates a new array by applying all the properties of another existing * array. * * This is a collective operation. * * @param[in] g_b integer handle for reference array * * @return pointer to GlobalArray object created; NULL if it fails */ GlobalArray * createGA(const GlobalArray &g_b); /** * Creates a 10x10 global array of type "double"(default). * * This is a collective operation. * * @return pointer to GlobalArray object created; NULL if it fails */ GlobalArray * createGA(); /** * Creates an ndim-dimensional array with a layer of ghost cells around * the visible data on each processor using the regular distribution * model and returns an integer handle representing the array. * The array can be distributed evenly or not evenly. The control over * the distribution is accomplished by specifying chunk (block) size for * all or some of the array dimensions. For example, for a 2-dimensional * array, setting chunk(1)=dim(1) gives distribution by vertical strips * (chunk(1)*dims(1)); setting chunk(2)=dim(2) gives distribution by * horizontal strips (chunk(2)*dims(2)). Actual chunks will be modified * so that they are at least the size of the minimum and each process * has either zero or one chunk. Specifying chunk(i) as <1 will cause * that dimension (i-th) to be distributed evenly. The width of the * ghost cell layer in each dimension is specified using the array * width(). The local data of the global array residing on each * processor will have a layer width[n] ghosts cells wide on either * side of the visible data along the dimension n. * * This is a collective operation. * * @param[in] array_name a unique character string * @param[in] type data type (MT_DBL,MT_INT,MT_DCPL) * @param[in] ndim number of array dimensions * @param[in] dims [ndim] array of dimensions * @param[in] width [ndim] array of ghost cell widths * @param[in] chunk [ndim] array of chunks, each element specifies * minimum size that given dimensions should be * chunked up into * * @returns pointer to GlobalArray object created; NULL if it fails */ GlobalArray * createGA_Ghosts(int type, int ndim, int dims[], int width[], char *array_name, int chunk[]); /** * Creates an array with ghost cells by following the user-specified * distribution and returns integer handle representing the array. * The distribution is specified as a Cartesian product of distributions * for each dimension. For example, the following figure demonstrates * distribution of a 2-dimensional array 8x10 on 6 (or more) processors. * nblock(2)={3,2}, the size of map array is s=5 and array map contains * the following elements map={1,3,7, 1, 6}. The distribution is * nonuniform because, P1 and P4 get 20 elements each and processors * P0,P2,P3, and P5 only 10 elements each. * * * * * * *
5 5
P0 P3 2
P1 P4 4
P2 P5 2
* * The array width[] is used to control the width of the ghost cell * boundary around the visible data on each processor. The local data * of the global array residing on each processor will have a layer * width[n] ghosts cells wide on either side of the visible data along * the dimension n. * * This is a collective operation. * * @param[in] array_name a unique character string * @param[in] type data type (MT_DBL,MT_INT,MT_DCPL) * @param[in] ndim number of array dimensions * @param[in] dims [ndim] array of dimensions * @param[in] width [ndim] array of ghost cell widths * @param[in] nblock [ndim] no. of blocks each dimension is divided into * @param[in] map [s] starting index for for each block; * the size s is a sum of all elements of nblock array * * @return pointer to GlobalArray object created; NULL if it fails */ GlobalArray * createGA_Ghosts(int type, int ndim, int dims[], int width[], char *array_name, int map[], int nblock[]); /** * Broadcast from process root to all other processes a message of * length lenbuf. This is operation is provided only for convenience * purposes: it is available regardless of the message-passing library * that GA is running with. * * This is a collective operation. * * @param[in] lenbuf length of buffer * @param[in,out] buf [lenbuf] data * @param[in] root root process */ void brdcst(void *buf, int lenbuf, int root); /** * Returns the current value of the internal debug flag. * * This is a local operation. * * @return 0 if the debug flag is false, 1 if it is true. */ int getDebug(); /** * This functions returns the total number of nodes that the program is * running on. * * On SMP architectures, this will be less than or equal to the total * number of processors. * * This is a local operation. * * @return the number of nodes the program is running on */ int clusterNnodes(); /** * This function returns the node ID of the process. * * On SMP architectures with more than one processor per node, several * processes may return the same node id. * * This is a local operation. * * @return the node ID of the process */ int clusterNodeid(); /** * This function returns the cluster node ID of the specified process. * * On SMP architectures with more than one processor per node, several * processes may return the same node id. * * This is a local operation. * * @return the cluster node ID of the specified process */ int clusterProcNodeid(int iproc); /** * This function returns the number of processors available on node inode. * * This is a local operation. * * @param[in] inode * * @return the number of processors available on the given node */ int clusterNprocs(int inode); /** * This function returns the processor id associated with node inode and * the local processor id iproc. * * If node inode has N processors, then the value of iproc lies between * 0 and N-1. * * This is a local operation. * * @param[in] inode * @param[in] iproc * * @return the processor ID associated with the given node and local processor * ID */ int clusterProcid(int inode, int iproc); /** * Creates a set containing the number of mutexes. * * Mutex is a simple synchronization object used to protect Critical * Sections. Only one set of mutexes can exist at a time. Array of mutexes * can be created and destroyed as many times as needed. * Mutexes are numbered: 0, ..., number -1. * * This is a collective operation. * * @param[in] number of mutexes in mutex array * * @return 0 if the operation succeeded or 1 when failed. */ int createMutexes(int number); /** * Remove a user defined data type from GA * * @param[in] type - user defined data type * * @return 0 is operation is successful * -2 if type not registered * -1 if type reserved */ int deregisterType(int type); /** * Destroys the set of mutexes created with ga_create_mutexes. * * This is a collective operation. * * @return 0 if the operation succeeded or 1 when failed. */ int destroyMutexes(); /** * Double Global OPeration. * * X(1:N) is a vector present on each process. DGOP 'sums' elements of * X accross all nodes using the commutative operator OP. The result is * broadcast to all nodes. Supported operations include '+', '*', 'max', * 'min', 'absmax', 'absmin'. The use of lowerecase for operators is * necessary. This is operation is provided only for convenience purposes: * it is available regardless of the message-passing library that GA is * running with. * * This is a collective operation. * * @param[in] n number of elements * @param[in,out] x [n] array of elements * @param[in] op operator */ void dgop(double x[], int n, char *op); /** * Creates a new array by applying all the properties of another existing * array. * * This is a collective operation. * * @param[in] array_name a character string * @param[in] g_a integer handle for reference array * * @return array handle; a non-zero array handle means the call was succesful. */ int duplicate(int g_a, char* array_name); /** * To be called in case of an error. * * Print an error message and an integer value that represents error code. * Releases some system resources. * This is the required way of aborting the program execution. * * This operation is local. * * @param[in] message string to print * @param[in] code code to print */ void error(const char *message, int code); /** * Blocks the calling process until all the data transfers corresponding to * GA operations called after ga_init_fence complete. * * For example, since ga_put might return before the data reaches the final * destination, ga_init_fence and ga_fence allow process to wait until the * data tranfer is fully completed: * * @code * ga_init_fence(); * ga_put(g_a, ...); * ga_fence(); * @endcode * * ga_fence must be called after ga_init_fence. A barrier, ga_sync, assures * completion of all data transfers and implicitly cancels all outstanding * ga_init_fence calls. ga_init_fence and ga_fence must be used in pairs, * multiple calls to ga_fence require the same number of corresponding * ga_init_fence calls. ga_init_fence/ga_fence pairs can be nested. * * ga_fence works for multiple GA operations. For example: * * @code * ga_init_fence(); * ga_put(g_a, ...); * ga_scatter(g_a, ...); * ga_put(g_b, ...); * ga_fence(); * @endcode * * The calling process will be blocked until data movements initiated by * two calls to ga_put and one ga_scatter complete. */ void fence(); /** * Integer Global OPeration. * * The integer version of ga_dgop described above, also include the bitwise OR * operation. This is operation is provided only for convenience purposes: it * is available regardless of the message-passing library that GA is running * with. * * This is a collective operation. * * @param[in] n number of elements * @param[in,out] x [n] array of elements * @param[in] op operator */ void gop(int x[], int n, char *op); /** * Long Global OPeration. * * X(1:N) is a vector present on each process. LGOP 'sums' elements of * X accross all nodes using the commutative operator OP. The result is * broadcast to all nodes. Supported operations include '+', '*', 'max', * 'min', 'absmax', 'absmin'. The use of lowerecase for operators is * necessary. This is operation is provided only for convenience purposes: * it is available regardless of the message-passing library that GA is * running with. * * This is a collective operation. * * @param[in] n number of elements * @param[in,out] x [n] array of elements * @param[in] op operator */ void gop(long x[], int n, char *op); /** * Float Global OPeration. * * X(1:N) is a vector present on each process. FGOP 'sums' elements of * X accross all nodes using the commutative operator OP. The result is * broadcast to all nodes. Supported operations include '+', '*', 'max', * 'min', 'absmax', 'absmin'. The use of lowerecase for operators is * necessary. This is operation is provided only for convenience purposes: * it is available regardless of the message-passing library that GA is * running with. * * This is a collective operation. * * @param[in] n number of elements * @param[in,out] x [n] array of elements * @param[in] op operator */ void gop(float x[], int n, char *op); /** * Double Global OPeration. * * X(1:N) is a vector present on each process. DGOP 'sums' elements of * X accross all nodes using the commutative operator OP. The result is * broadcast to all nodes. Supported operations include '+', '*', 'max', * 'min', 'absmax', 'absmin'. The use of lowerecase for operators is * necessary. This is operation is provided only for convenience purposes: * it is available regardless of the message-passing library that GA is * running with. * * This is a collective operation. * * @param[in] n number of elements * @param[in,out] x [n] array of elements * @param[in] op operator */ void gop(double x[], int n, char *op); /** * Integer Global OPeration. * * The integer (more precisely long) version of ga_dgop described above, * also include the bitwise OR operation. * This is operation is provided only for convenience purposes: it is * available regardless of the message-passing library that GA is running * with. * * This is a collective operation. * * @param[in] n number of elements * @param[in,out] x [n] array of elements * @param[in] op operator */ void igop(int x[], int n, char *op); /** * Initializes tracing of completion status of data movement operations. * * This operation is local. */ void initFence(); /** * Returns amount of memory (in bytes) used in the allocated global * arrays on the calling processor. * * This operation is local. * * @return amount of memory (in bytes) used in the allocated global arrays on * the calling processor */ size_t inquireMemory(); /** * Long Global OPeration. * * X(1:N) is a vector present on each process. LGOP 'sums' elements of * X accross all nodes using the commutative operator OP. The result is * broadcast to all nodes. Supported operations include '+', '*', 'max', * 'min', 'absmax', 'absmin'. The use of lowerecase for operators is * necessary. This is operation is provided only for convenience purposes: * it is available regardless of the message-passing library that GA is * running with. * * This is a collective operation. * * @param[in] n number of elements * @param[in,out] x [n] array of elements * @param[in] op operator */ void lgop(long x[], int n, char *op); /** * Locks a mutex object identified by the mutex number. It is a fatal * error for a process to attempt to lock a mutex which was already * locked by this process. * * @param[in] mutex object id */ void lock(int mutex); /** * Mask the intrinsic sync operations during collective calls. * * GA Collective calls has Sync calls at the begining and ending of * of the call. Sometimes there may be some redundacy in sync calls, which * can be avoided by masking the sync operations. * * Setting the parameters as zero will mask (disable) the call. Any non-zero * value will enable the call. Initially these params are set to non-zero * value. * * @param[in] first masks the sync at the begining of the collective call. * @param[in] last masks the sync at the end of the collective call. */ void maskSync(int first, int last); /** * If GA_uses_ma returns true, then GA_Memory_avail returns the * lesser of the amount available under the GA limit and the amount * available from MA (according to ma_inquire_avail operation). * If no GA limit has been set, it returns what MA says is available. * If ( ! GA_Uses_ma() && ! GA_Memory_limited() ) returns < 0, indicating * that the bound on currently available memory cannot be determined. * * This operation is local. * * @return amount of memory (in bytes) left for allocation of new * global arrays on the calling processor. * */ int memoryAvailable() ; /** * Indicates if limit is set on memory usage in Global Arrays on the * calling processor. * * This operation is local. * * @return 1 means "yes", "0" means "no". */ int memoryLimited(); /** * Test for completion of a nonblocking operation locally. This will return 1 * if the operation is completed locally and 0 otherwise. If the operation * returns true, the local buffer is ready for use or reuse, depending on the * operation. Once the test has returned true, there is no need to call the * nbWait function on the handle. * * This is a local operation. * * @param[in] nbhandle nonblocking handle * @param[out] true if operation has completed locally */ int nbTest(GANbhdl *nbhandle); /** * Force completion of a nonblocking operation locally. * * Waiting on a nonblocking put or an accumulate operation assures that data * was injected into the network and the user buffer can be now be reused. * Completing a get operation assures data has arrived into the user memory * and is ready for use. Wait operation ensures only local completion. Unlike * their blocking counterparts, the nonblocking operations are not ordered * with respect to the destination. Performance being one reason, the other * reason is that by ensuring ordering we incur additional and possibly * unnecessary overhead on applications that do not require their operations * to be ordered. For cases where ordering is necessary, it can be done by * calling a fence operation. The fence operation is provided to the user to * confirm remote completion if needed. * * This is a local operation. * * @param[in] nbhandle nonblocking handle */ void nbWait(GANbhdl *nbhandle); /** * Returns the GA process id (0, ..., ga_Nnodes()-1) of the requesting * compute process. * * This operation is local. * * @return the GA process ID of the requesting process */ int nodeid(); /** * Returns the number of the GA compute (user) processes. * * This operation is local. * * @return the number of GA processes */ int nodes(); /** * Print statistical information on GA use. * * This non-collective (MIMD) operation prints information about: * - number of calls to * - create * - duplicate * - destroy * - get * - put * - scatter * - gather * - read_and_inc operations * - total amount of data moved in the primitive operations * - amount of data moved in the primitive operations to logicaly remote * locations * - maximum memory consumption in global arrays * - number of requests serviced in the interrupt-driven implementations * by the calling process. * * This operation is local. */ void printStats(); /** * Add a user defined data type to GA * * @param[in] size - size (in bytes) of user defined data type * * @return handle for new data type */ int registerType(size_t size); /** * This function sets an internal flag in the GA library to either true or * false. * * The value of this flag can be recovered at any time using the * getDebug function. The flag is set to false when the the GA library * is initialized. This can be useful in a number of debugging situations, * especially when examining the behavior of routines that are called in * multiple locations in a code. * * This is a local operation. * * @param[in] dbg value to set internal flag */ void setDebug(int dbg); /** * Sets the amount of memory to be used (in bytes) per process. * * This is a local operation. * * @param[in] limit the amount of memory in bytes per process */ void setMemoryLimit(size_t limit); /** * Prints info about allocated arrays. * * @param[in] verbose if true print distribution info */ void summarize(int verbose); /** * Synchronize processes (a barrier) and ensure that all GA operations * completed. * * This is a collective operation. */ void sync(); /** * Unlocks a mutex object identified by the mutex number. * * It is a fatal error for a process to attempt to unlock a mutex which has * not been locked by this process. * * @param[in] mutex object id */ void unlock(int mutex); /** * Returns whether memory comes from internal or external allocator. * * This operation is local. * * @return "1" if memory comes from MA; * "0" if memory comes from another source e.g. System V shared memory */ int usesMA(); /** * Returns whether GA is using Fortran indexing. * * @return "1" if uses fortran API, else returns "0" */ int usesFAPI(); /** * This function return a wall (or elapsed) time on the calling processor. * * Returns time in seconds representing elapsed wall-clock time * since an arbitrary time in the past. Example: * * @code * double starttime, endtime; * starttime = GA::SERVICES.wtime(); * // {{.... code snippet to be timed ....}} * endtime = GA::SERVICES.wtime(); * printf("Time taken = %lf seconds\n", endtime-starttime); * @endcode * * This is a local operation. * * @note This function is only available in release 4.1 or greater. */ double wtime(); }; extern GAServices SERVICES; } #endif /* _GA_SERVICES_H */ ga-5.9.2/ga++/src/GlobalArray.cc000066400000000000000000001202061500715745200161370ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "ga++.h" #define GA_DATA_TYPE C_DBL #define INVALID_HANDLE -1000 static int sTmpVar = 0; /** * Constructors and Destructor of GlobalArray */ GA::GlobalArray::GlobalArray(int type, int ndim, int dims[], char *arrayname, int chunk[]) { mHandle = NGA_Create(type, ndim, dims, arrayname, chunk); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int dims[], char *arrayname, int chunk[], GA::PGroup * p_handle) { mHandle = NGA_Create_config(type, ndim, dims, arrayname, chunk, p_handle->handle()); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int64_t dims[], char *arrayname, int64_t chunk[]) { mHandle = NGA_Create64(type, ndim, dims, arrayname, chunk); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int64_t dims[], char *arrayname, int64_t chunk[], GA::PGroup * p_handle) { mHandle = NGA_Create_config64(type, ndim, dims, arrayname, chunk, p_handle->handle()); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int dims[], char *arrayname, int block[], int maps[]) { mHandle = NGA_Create_irreg(type, ndim, dims, arrayname, block, maps); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int dims[], char *arrayname, int block[], int maps[], GA::PGroup * p_handle) { mHandle = NGA_Create_irreg_config(type, ndim, dims, arrayname, block, maps, p_handle->handle()); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int64_t dims[], char *arrayname, int64_t block[], int64_t maps[]) { mHandle = NGA_Create_irreg64(type, ndim, dims, arrayname, block, maps); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int64_t dims[], char *arrayname, int64_t block[], int64_t maps[], GA::PGroup * p_handle) { mHandle = NGA_Create_irreg_config64(type, ndim, dims, arrayname, block, maps, p_handle->handle()); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int dims[], int width[], char *arrayname, int chunk[], char ghosts) { mHandle = NGA_Create_ghosts(type, ndim, dims, width, arrayname, chunk); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int64_t dims[], int64_t width[], char *arrayname, int64_t chunk[], char ghosts) { mHandle = NGA_Create_ghosts64(type, ndim, dims, width, arrayname, chunk); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int dims[], int width[], char *arrayname, int chunk[], GA::PGroup * p_handle, char ghosts) { mHandle = NGA_Create_ghosts_config(type, ndim, dims, width, arrayname, chunk, p_handle->handle()); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int64_t dims[], int64_t width[], char *arrayname, int64_t chunk[], GA::PGroup * p_handle, char ghosts) { mHandle = NGA_Create_ghosts_config64(type, ndim, dims, width, arrayname, chunk, p_handle->handle()); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int dims[], int width[], char *arrayname, int block[], int maps[], char ghosts) { mHandle = NGA_Create_ghosts_irreg(type, ndim, dims, width, arrayname, block, maps); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int64_t dims[], int64_t width[], char *arrayname, int64_t block[], int64_t maps[], char ghosts) { mHandle = NGA_Create_ghosts_irreg64(type, ndim, dims, width, arrayname, block, maps); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int dims[], int width[], char *arrayname, int block[], int maps[], GA::PGroup * p_handle, char ghosts) { mHandle = NGA_Create_ghosts_irreg_config(type, ndim, dims, width, arrayname, block, maps, p_handle->handle()); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(int type, int ndim, int64_t dims[], int64_t width[], char *arrayname, int64_t block[], int64_t maps[], GA::PGroup * p_handle, char ghosts) { mHandle = NGA_Create_ghosts_irreg_config64(type, ndim, dims, width, arrayname, block, maps, p_handle->handle()); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(const GA::GlobalArray &g_a, char *arrayname) { mHandle = GA_Duplicate(g_a.mHandle, arrayname); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::GlobalArray(const GA::GlobalArray &g_a) { char temp_name[20]; sprintf(temp_name, "tmpGA%d", sTmpVar++); mHandle = GA_Duplicate(g_a.mHandle, temp_name); if(!mHandle) GA_Error((char *)" GA creation failed",0); GA_Copy(g_a.mHandle, mHandle); } GA::GlobalArray::GlobalArray() { mHandle = GA_Create_handle(); if(!mHandle) GA_Error((char *)" GA creation failed",0); } GA::GlobalArray::~GlobalArray() { GA_Destroy(mHandle); mHandle = INVALID_HANDLE; } /********************************************************************* * Member functions of GA::GlobalArray * *********************************************************************/ void GA::GlobalArray::acc(int lo[], int hi[], void *buf, int ld[], void *alpha) const { NGA_Acc(mHandle, lo, hi, buf, ld, alpha); } void GA::GlobalArray::acc(int64_t lo[], int64_t hi[], void *buf, int64_t ld[], void *alpha) const { NGA_Acc64(mHandle, lo, hi, buf, ld, alpha); } void GA::GlobalArray::access(int lo[], int hi[], void *ptr, int ld[]) const { NGA_Access(mHandle, lo, hi, ptr, ld); } void GA::GlobalArray::access(int64_t lo[], int64_t hi[], void *ptr, int64_t ld[]) const { NGA_Access64(mHandle, lo, hi, ptr, ld); } void GA::GlobalArray::accessBlock(int idx, void *ptr, int ld[]) const { NGA_Access_block(mHandle, idx, ptr, ld); } void GA::GlobalArray::accessBlock(int64_t idx, void *ptr, int64_t ld[]) const { NGA_Access_block64(mHandle, idx, ptr, ld); } void GA::GlobalArray::accessBlockGrid(int index[], void *ptr, int ld[]) const { NGA_Access_block_grid(mHandle, index, ptr, ld); } void GA::GlobalArray::accessBlockGrid(int64_t index[], void *ptr, int64_t ld[]) const { NGA_Access_block_grid64(mHandle, index, ptr, ld); } void GA::GlobalArray::accessBlockSegment(int proc, void *ptr, int *len) const { NGA_Access_block_segment(mHandle, proc, ptr, len); } void GA::GlobalArray::accessBlockSegment(int proc, void *ptr, int64_t *len) const { NGA_Access_block_segment64(mHandle, proc, ptr, len); } void GA::GlobalArray::accessGhosts(int dims[], void *ptr, int ld[]) const { NGA_Access_ghosts(mHandle, dims, ptr, ld); } void GA::GlobalArray::accessGhosts(int64_t dims[], void *ptr, int64_t ld[]) const { NGA_Access_ghosts64(mHandle, dims, ptr, ld); } void GA::GlobalArray::accessGhostElement(void *ptr, int subscript[], int ld[]) const { NGA_Access_ghost_element(mHandle, ptr, subscript, ld); } void GA::GlobalArray::accessGhostElement(void *ptr, int64_t subscript[], int64_t ld[]) const { NGA_Access_ghost_element64(mHandle, ptr, subscript, ld); } void GA::GlobalArray::add(void *alpha, const GA::GlobalArray * g_a, void *beta, const GA::GlobalArray * g_b) const { GA_Add(alpha, g_a->mHandle, beta, g_b->mHandle, mHandle); } void GA::GlobalArray::addPatch (void *alpha, const GA::GlobalArray * g_a, int alo[], int ahi[], void *beta, const GA::GlobalArray * g_b, int blo[], int bhi[], int clo[], int chi[]) const { NGA_Add_patch(alpha, g_a->mHandle, alo, ahi, beta, g_b->mHandle, blo, bhi, mHandle, clo, chi); } void GA::GlobalArray::addPatch (void *alpha, const GA::GlobalArray * g_a, int64_t alo[], int64_t ahi[], void *beta, const GA::GlobalArray * g_b, int64_t blo[], int64_t bhi[], int64_t clo[], int64_t chi[]) const { NGA_Add_patch64(alpha, g_a->mHandle, alo, ahi, beta, g_b->mHandle, blo, bhi, mHandle, clo, chi); } int GA::GlobalArray::allocate() const { return GA_Allocate(mHandle); } int GA::GlobalArray::deallocate() const { return GA_Deallocate(mHandle); } void GA::GlobalArray::allocGatscatBuf(int nelems) const { NGA_Alloc_gatscat_buf(nelems); } void GA::GlobalArray::checkHandle(char* string) const { GA_Check_handle(mHandle, string); } int GA::GlobalArray::compareDistr(const GA::GlobalArray *g_a) const { return GA_Compare_distr(mHandle, g_a->mHandle); } void GA::GlobalArray::copy(const GA::GlobalArray *g_a) const { GA_Copy(g_a->mHandle, mHandle); } void GA::GlobalArray::copyPatch(char trans, const GA::GlobalArray* ga, int alo[], int ahi[], int blo[], int bhi[]) const { NGA_Copy_patch(trans, ga->mHandle, alo, ahi, mHandle, blo, bhi); } void GA::GlobalArray::copyPatch(char trans, const GA::GlobalArray* ga, int64_t alo[], int64_t ahi[], int64_t blo[], int64_t bhi[]) const { NGA_Copy_patch64(trans, ga->mHandle, alo, ahi, mHandle, blo, bhi); } double GA::GlobalArray::ddot(const GA::GlobalArray * g_a) const { return GA_Ddot(mHandle, g_a->mHandle); } double GA::GlobalArray::ddotPatch(char ta, int alo[], int ahi[], const GA::GlobalArray * g_a, char tb, int blo[], int bhi[]) const { return NGA_Ddot_patch(mHandle, ta, alo, ahi, g_a->mHandle, tb, blo, bhi); } double GA::GlobalArray::ddotPatch(char ta, int64_t alo[], int64_t ahi[], const GA::GlobalArray * g_a, char tb, int64_t blo[], int64_t bhi[]) const { return NGA_Ddot_patch64(mHandle, ta, alo, ahi, g_a->mHandle, tb, blo, bhi); } void GA::GlobalArray::destroy() { GA_Destroy(mHandle); mHandle = INVALID_HANDLE; } void GA::GlobalArray::dgemm(char ta, char tb, int m, int n, int k, double alpha, const GA::GlobalArray *g_a, const GA::GlobalArray *g_b, double beta) const { GA_Dgemm(ta, tb, m, n, k, alpha, g_a->mHandle, g_b->mHandle, beta, mHandle); } void GA::GlobalArray::dgemm(char ta, char tb, int64_t m, int64_t n, int64_t k, double alpha, const GA::GlobalArray *g_a, const GA::GlobalArray *g_b, double beta) const { GA_Dgemm64(ta, tb, m, n, k, alpha, g_a->mHandle, g_b->mHandle, beta, mHandle); } void GA::GlobalArray::diag(const GA::GlobalArray *g_s, GA::GlobalArray *g_v, void *eval) const { GA_Diag(mHandle, g_s->mHandle, g_v->mHandle, eval); } void GA::GlobalArray::diagReuse(int control, const GA::GlobalArray *g_s, GA::GlobalArray *g_v, void *eval) const { GA_Diag_reuse(control, mHandle, g_s->mHandle, g_v->mHandle, eval); } void GA::GlobalArray::diagStd(GlobalArray *g_v, void *eval) const { GA_Diag_std(mHandle, g_v->mHandle, eval); } void GA::GlobalArray::diagSeq(const GA::GlobalArray * g_s, const GA::GlobalArray * g_v, void *eval) const { GA_Diag_seq(mHandle, g_s->mHandle, g_v->mHandle, eval); } void GA::GlobalArray::diagStdSeq(const GA::GlobalArray * g_v, void *eval) const { GA_Diag_std_seq(mHandle, g_v->mHandle, eval); } void GA::GlobalArray::distribution(int me, int* lo, int* hi) const { NGA_Distribution(mHandle, me, lo, hi); } void GA::GlobalArray::distribution(int me, int64_t* lo, int64_t* hi) const { NGA_Distribution64(mHandle, me, lo, hi); } float GA::GlobalArray::fdot(const GA::GlobalArray * g_a) const { return GA_Fdot(mHandle, g_a->mHandle); } float GA::GlobalArray::fdotPatch(char t_a, int alo[], int ahi[], const GA::GlobalArray * g_b, char t_b, int blo[], int bhi[]) const { return NGA_Fdot_patch(mHandle, t_a, alo, ahi, g_b->mHandle, t_b, blo, bhi); } float GA::GlobalArray::fdotPatch(char t_a, int64_t alo[], int64_t ahi[], const GA::GlobalArray * g_b, char t_b, int64_t blo[], int64_t bhi[]) const { return NGA_Fdot_patch64(mHandle, t_a, alo, ahi, g_b->mHandle, t_b, blo, bhi); } void GA::GlobalArray::fill(void *value) const { GA_Fill(mHandle, value); } void GA::GlobalArray::fillPatch (int lo[], int hi[], void *val) const { NGA_Fill_patch(mHandle, lo, hi, val); } void GA::GlobalArray::fillPatch (int64_t lo[], int64_t hi[], void *val) const { NGA_Fill_patch64(mHandle, lo, hi, val); } void GA::GlobalArray::freeGatscatBuf() { NGA_Free_gatscat_buf(); } void GA::GlobalArray::gather(void *v, int * subsarray[], int n) const { NGA_Gather(mHandle, v, subsarray, n); } void GA::GlobalArray::gather(void *v, int64_t * subsarray[], int64_t n) const { NGA_Gather64(mHandle, v, subsarray, n); } void GA::GlobalArray::get(int lo[], int hi[], void *buf, int ld[]) const { NGA_Get(mHandle, lo, hi, buf, ld); } void GA::GlobalArray::get(int64_t lo[], int64_t hi[], void *buf, int64_t ld[]) const { NGA_Get64(mHandle, lo, hi, buf, ld); } void GA::GlobalArray::getBlockInfo(int num_blocks[], int block_dims[]) { GA_Get_block_info(mHandle, num_blocks, block_dims); } int GA::GlobalArray::hasGhosts() const { return GA_Has_ghosts(mHandle); } int GA::GlobalArray::idot(const GA::GlobalArray * g_a) const { return GA_Idot(mHandle, g_a->mHandle); } long GA::GlobalArray::idotPatch(char ta, int alo[], int ahi[], const GA::GlobalArray * g_a, char tb, int blo[], int bhi[]) const { return NGA_Idot_patch(mHandle, ta, alo, ahi, g_a->mHandle, tb, blo, bhi); } long GA::GlobalArray::idotPatch(char ta, int64_t alo[], int64_t ahi[], const GA::GlobalArray * g_a, char tb, int64_t blo[], int64_t bhi[]) const { return NGA_Idot_patch64(mHandle, ta, alo, ahi, g_a->mHandle, tb, blo, bhi); } void GA::GlobalArray::inquire(int *type, int *ndim, int dims[]) const { NGA_Inquire(mHandle, type, ndim, dims); } void GA::GlobalArray::inquire(int *type, int *ndim, int64_t dims[]) const { NGA_Inquire64(mHandle, type, ndim, dims); } char* GA::GlobalArray::inquireName() const { return GA_Inquire_name(mHandle); } long GA::GlobalArray::ldot(const GA::GlobalArray * g_a) const { return GA_Ldot(mHandle, g_a->mHandle); } long GA::GlobalArray::ldotPatch(char ta, int alo[], int ahi[], const GA::GlobalArray * g_a, char tb, int blo[], int bhi[]) const { return NGA_Ldot_patch(mHandle, ta, alo, ahi, g_a->mHandle, tb, blo, bhi); } long GA::GlobalArray::ldotPatch(char ta, int64_t alo[], int64_t ahi[], const GA::GlobalArray * g_a, char tb, int64_t blo[], int64_t bhi[]) const { return NGA_Ldot_patch64(mHandle, ta, alo, ahi, g_a->mHandle, tb, blo, bhi); } int GA::GlobalArray::lltSolve(const GA::GlobalArray * g_a) const { return GA_Llt_solve(g_a->mHandle, mHandle); } int GA::GlobalArray::locate(int subscript[]) const { return NGA_Locate(mHandle, subscript); } int GA::GlobalArray::locate(int64_t subscript[]) const { return NGA_Locate64(mHandle, subscript); } int GA::GlobalArray::locateRegion(int lo[], int hi[], int map[], int procs[]) const { return NGA_Locate_region(mHandle, lo, hi, map, procs); } int GA::GlobalArray::locateRegion(int64_t lo[], int64_t hi[], int64_t map[], int procs[]) const { return NGA_Locate_region64(mHandle, lo, hi, map, procs); } void GA::GlobalArray::luSolve(char trans, const GA::GlobalArray * g_a) const { GA_Lu_solve(trans, g_a->mHandle, mHandle); } void GA::GlobalArray::matmulPatch(char transa, char transb, void* alpha, void *beta, const GA::GlobalArray *g_a, int ailo, int aihi, int ajlo, int ajhi, const GA::GlobalArray *g_b, int bilo, int bihi, int bjlo, int bjhi, int cilo, int cihi, int cjlo, int cjhi) const { GA_Matmul_patch(transa, transb, alpha, beta, g_a->mHandle, ailo, aihi, ajlo, ajhi, g_b->mHandle, bilo, bihi, bjlo, bjhi, mHandle, cilo, cihi, cjlo, cjhi); } void GA::GlobalArray::matmulPatch(char transa, char transb, void* alpha, void *beta, const GA::GlobalArray *g_a, int64_t ailo, int64_t aihi, int64_t ajlo, int64_t ajhi, const GA::GlobalArray *g_b, int64_t bilo, int64_t bihi, int64_t bjlo, int64_t bjhi, int64_t cilo, int64_t cihi, int64_t cjlo, int64_t cjhi) const { GA_Matmul_patch64(transa, transb, alpha, beta, g_a->mHandle, ailo, aihi, ajlo, ajhi, g_b->mHandle, bilo, bihi, bjlo, bjhi, mHandle, cilo, cihi, cjlo, cjhi); } void GA::GlobalArray::matmulPatch(char transa, char transb, void* alpha, void *beta, const GA::GlobalArray *g_a, int *alo, int *ahi, const GA::GlobalArray *g_b, int *blo, int *bhi, int *clo, int *chi) const { NGA_Matmul_patch(transa, transb, alpha, beta, g_a->mHandle, alo, ahi, g_b->mHandle, blo, bhi, mHandle, clo, chi); } void GA::GlobalArray::matmulPatch(char transa, char transb, void* alpha, void *beta, const GA::GlobalArray *g_a, int64_t *alo, int64_t *ahi, const GA::GlobalArray *g_b, int64_t *blo, int64_t *bhi, int64_t *clo, int64_t *chi) const { NGA_Matmul_patch64(transa, transb, alpha, beta, g_a->mHandle, alo, ahi, g_b->mHandle, blo, bhi, mHandle, clo, chi); } void GA::GlobalArray::mergeDistrPatch(int alo[], int ahi[], GlobalArray *g_b, int blo[], int bhi[]) { NGA_Merge_distr_patch(mHandle, alo, ahi, g_b->mHandle, blo, bhi); } void GA::GlobalArray::mergeDistrPatch(int64_t alo[], int64_t ahi[], GlobalArray *g_b, int64_t blo[], int64_t bhi[]) { NGA_Merge_distr_patch64(mHandle, alo, ahi, g_b->mHandle, blo, bhi); } int GA::GlobalArray::isMirrored() { return GA_Is_mirrored(mHandle); } void GA::GlobalArray::mergeMirrored() { GA_Merge_mirrored(mHandle); } void GA::GlobalArray::nbAcc(int lo[], int hi[], void *buf, int ld[], void *alpha, GANbhdl *nbhandle) { NGA_NbAcc(mHandle, lo, hi, buf, ld, alpha, nbhandle); } void GA::GlobalArray::nbAcc(int64_t lo[], int64_t hi[], void *buf, int64_t ld[], void *alpha, GANbhdl *nbhandle) { NGA_NbAcc64(mHandle, lo, hi, buf, ld, alpha, nbhandle); } void GA::GlobalArray::nbGet(int lo[], int hi[], void *buf, int ld[], GANbhdl *nbhandle) { NGA_NbGet(mHandle, lo, hi, buf, ld, nbhandle); } void GA::GlobalArray::nbGet(int64_t lo[], int64_t hi[], void *buf, int64_t ld[], GANbhdl *nbhandle) { NGA_NbGet64(mHandle, lo, hi, buf, ld, nbhandle); } void GA::GlobalArray::nbGetGhostDir(int mask[], GANbhdl *nbhandle) { NGA_NbGet_ghost_dir(mHandle, mask, nbhandle); } void GA::GlobalArray::nbGetGhostDir(int64_t mask[], GANbhdl *nbhandle) { NGA_NbGet_ghost_dir64(mHandle, mask, nbhandle); } void GA::GlobalArray::nblock(int numblock[]) const { GA_Nblock(mHandle, numblock); } void GA::GlobalArray::nbPut(int lo[], int hi[], void *buf, int ld[], GANbhdl *nbhandle) { NGA_NbPut(mHandle, lo, hi, buf, ld, nbhandle); } void GA::GlobalArray::nbPut(int64_t lo[], int64_t hi[], void *buf, int64_t ld[], GANbhdl *nbhandle) { NGA_NbPut64(mHandle, lo, hi, buf, ld, nbhandle); } int GA::GlobalArray::ndim() const { return GA_Ndim(mHandle); } int GA::GlobalArray::overlay(const GA::GlobalArray *g_p) { return GA_Overlay(mHandle, g_p->mHandle); } void GA::GlobalArray::pack(const GA::GlobalArray *g_dest, const GA::GlobalArray *g_mask, int lo, int hi, int *icount) const { GA_Pack(mHandle, g_dest->mHandle, g_mask->mHandle, lo, hi, icount); } void GA::GlobalArray::pack(const GA::GlobalArray *g_dest, const GA::GlobalArray *g_mask, int64_t lo, int64_t hi, int64_t *icount) const { GA_Pack64(mHandle, g_dest->mHandle, g_mask->mHandle, lo, hi, icount); } void GA::GlobalArray::patchEnum(int lo, int hi, void *start, void *inc) { GA_Patch_enum(mHandle, lo, hi, start, inc); } void GA::GlobalArray::patchEnum(int64_t lo, int64_t hi, void *start, void *inc) { GA_Patch_enum64(mHandle, lo, hi, start, inc); } void GA::GlobalArray::periodicAcc(int lo[], int hi[], void* buf, int ld[], void* alpha) const { NGA_Periodic_acc(mHandle, lo, hi, buf, ld, alpha); } void GA::GlobalArray::periodicAcc(int64_t lo[], int64_t hi[], void* buf, int64_t ld[], void* alpha) const { NGA_Periodic_acc64(mHandle, lo, hi, buf, ld, alpha); } void GA::GlobalArray::periodicGet(int lo[], int hi[], void* buf, int ld[]) const { NGA_Periodic_get(mHandle, lo, hi, buf, ld); } void GA::GlobalArray::periodicGet(int64_t lo[], int64_t hi[], void* buf, int64_t ld[]) const { NGA_Periodic_get64(mHandle, lo, hi, buf, ld); } void GA::GlobalArray::periodicPut(int lo[], int hi[], void* buf, int ld[]) const { NGA_Periodic_put(mHandle, lo, hi, buf, ld); } void GA::GlobalArray::periodicPut(int64_t lo[], int64_t hi[], void* buf, int64_t ld[]) const { NGA_Periodic_put64(mHandle, lo, hi, buf, ld); } void GA::GlobalArray::print() const { GA_Print(mHandle); } void GA::GlobalArray::printDistribution() const { GA_Print_distribution(mHandle); } void GA::GlobalArray::printFile(FILE *file) const { GA_Print_file(file, mHandle); } void GA::GlobalArray::printPatch(int* lo, int* hi, int pretty) const { NGA_Print_patch(mHandle, lo, hi, pretty); } void GA::GlobalArray::printPatch(int64_t* lo, int64_t* hi, int pretty) const { NGA_Print_patch64(mHandle, lo, hi, pretty); } void GA::GlobalArray::procTopology(int proc, int coord[]) const { NGA_Proc_topology(mHandle, proc, coord); } void GA::GlobalArray::put(int lo[], int hi[], void *buf, int ld[]) const { NGA_Put(mHandle, lo, hi, buf, ld); } void GA::GlobalArray::put(int64_t lo[], int64_t hi[], void *buf, int64_t ld[]) const { NGA_Put64(mHandle, lo, hi, buf, ld); } long GA::GlobalArray::readInc(int subscript[], long inc) const { return NGA_Read_inc(mHandle, subscript, inc); } long GA::GlobalArray::readInc(int64_t subscript[], long inc) const { return NGA_Read_inc64(mHandle, subscript, inc); } void GA::GlobalArray::release(int lo[], int hi[]) const { NGA_Release(mHandle, lo, hi); } void GA::GlobalArray::release(int64_t lo[], int64_t hi[]) const { NGA_Release64(mHandle, lo, hi); } void GA::GlobalArray::releaseBlock(int idx) const { NGA_Release_block(mHandle, idx); } void GA::GlobalArray::releaseBlockGrid(int index[]) const { NGA_Release_block_grid(mHandle, index); } void GA::GlobalArray::releaseBlockSegment(int proc) const { NGA_Release_block_segment(mHandle, proc); } void GA::GlobalArray::releaseGhosts() const { NGA_Release_ghosts(mHandle); } void GA::GlobalArray::releaseGhostElement(int subscript[]) const { NGA_Release_ghost_element(mHandle, subscript); } void GA::GlobalArray::releaseGhostElement(int64_t subscript[]) const { NGA_Release_ghost_element64(mHandle, subscript); } void GA::GlobalArray::releaseUpdate(int lo[], int hi[]) const { NGA_Release_update(mHandle, lo, hi); } void GA::GlobalArray::releaseUpdate(int64_t lo[], int64_t hi[]) const { NGA_Release_update64(mHandle, lo, hi); } void GA::GlobalArray::releaseUpdateBlock(int idx) const { NGA_Release_update_block(mHandle, idx); } void GA::GlobalArray::releaseUpdateBlockGrid(int index[]) const { NGA_Release_update_block_grid(mHandle, index); } void GA::GlobalArray::releaseUpdateBlockSegment(int idx) const { NGA_Release_update_block_segment(mHandle, idx); } void GA::GlobalArray::releaseUpdateGhosts() const { NGA_Release_update_ghosts(mHandle); } void GA::GlobalArray::releaseUpdateGhostElement(int subscript[]) const { NGA_Release_update_ghost_element(mHandle, subscript); } void GA::GlobalArray::releaseUpdateGhostElement(int64_t subscript[]) const { NGA_Release_update_ghost_element64(mHandle, subscript); } void GA::GlobalArray::scale(void *value) const { GA_Scale(mHandle, value); } void GA::GlobalArray::scalePatch (int lo[], int hi[], void *val) const { NGA_Scale_patch(mHandle, lo, hi, val); } void GA::GlobalArray::scalePatch (int64_t lo[], int64_t hi[], void *val) const { NGA_Scale_patch64(mHandle, lo, hi, val); } void GA::GlobalArray::scanAdd(const GA::GlobalArray *g_dest, const GlobalArray *g_mask, int lo, int hi, int excl) const { GA_Scan_add(mHandle, g_dest->mHandle, g_mask->mHandle, lo, hi, excl); } void GA::GlobalArray::scanAdd(const GA::GlobalArray *g_dest, const GlobalArray *g_mask, int64_t lo, int64_t hi, int excl) const { GA_Scan_add64(mHandle, g_dest->mHandle, g_mask->mHandle, lo, hi, excl); } void GA::GlobalArray::scanCopy(const GA::GlobalArray *g_dest, const GA::GlobalArray *g_mask, int lo, int hi) const { GA_Scan_copy(mHandle, g_dest->mHandle, g_mask->mHandle, lo, hi); } void GA::GlobalArray::scanCopy(const GA::GlobalArray *g_dest, const GA::GlobalArray *g_mask, int64_t lo, int64_t hi) const { GA_Scan_copy64(mHandle, g_dest->mHandle, g_mask->mHandle, lo, hi); } void GA::GlobalArray::scatter(void *v, int *subsarray[], int n) const { NGA_Scatter(mHandle, v, subsarray, n); } void GA::GlobalArray::scatter(void *v, int64_t *subsarray[], int64_t n) const { NGA_Scatter64(mHandle, v, subsarray, n); } void GA::GlobalArray::scatterAcc(void *v, int *subsarray[], int n, void *alpha) const { NGA_Scatter_acc(mHandle, v, subsarray, n, alpha); } void GA::GlobalArray::scatterAcc(void *v, int64_t *subsarray[], int64_t n, void *alpha) const { NGA_Scatter_acc64(mHandle, v, subsarray, n, alpha); } void GA::GlobalArray::selectElem(char *op, void* val, int index[]) const { NGA_Select_elem(mHandle, op, val, index); } void GA::GlobalArray::selectElem(char *op, void* val, int64_t index[]) const { NGA_Select_elem64(mHandle, op, val, index); } void GA::GlobalArray::setArrayName(char *name) const { GA_Set_array_name(mHandle, name); } void GA::GlobalArray::setBlockCyclic(int dims[]) const { GA_Set_block_cyclic(mHandle, dims); } void GA::GlobalArray::setBlockCyclic(int64_t dims[]) const { GA_Set_block_cyclic64(mHandle, dims); } void GA::GlobalArray::setBlockCyclicProcGrid(int dims[], int proc_grid[]) const{ GA_Set_block_cyclic_proc_grid(mHandle, dims, proc_grid); } void GA::GlobalArray::setBlockCyclicProcGrid(int64_t dims[], int64_t proc_grid[]) const{ GA_Set_block_cyclic_proc_grid64(mHandle, dims, proc_grid); } void GA::GlobalArray::setTiledProcGrid(int dims[], int proc_grid[]) const{ GA_Set_tiled_proc_grid(mHandle, dims, proc_grid); } void GA::GlobalArray::setTiledProcGrid(int64_t dims[], int64_t proc_grid[]) const{ GA_Set_tiled_proc_grid64(mHandle, dims, proc_grid); } void GA::GlobalArray::setTiledIrregProcGrid(int mapc[], int nblocks[], int proc_grid[]) const{ GA_Set_tiled_irreg_proc_grid(mHandle, mapc, nblocks, proc_grid); } void GA::GlobalArray::setTiledIrregProcGrid(int64_t mapc[], int64_t nblocks[], int64_t proc_grid[]) const{ GA_Set_tiled_irreg_proc_grid64(mHandle, mapc, nblocks, proc_grid); } void GA::GlobalArray::setChunk(int chunk[]) const { GA_Set_chunk(mHandle, chunk); } void GA::GlobalArray::setChunk(int64_t chunk[]) const { GA_Set_chunk64(mHandle, chunk); } void GA::GlobalArray::setData(int ndim, int dims[], int type) const { GA_Set_data(mHandle, ndim, dims, type); } void GA::GlobalArray::setData(int ndim, int64_t dims[], int type) const { GA_Set_data64(mHandle, ndim, dims, type); } void GA::GlobalArray::setGhosts(int width[]) const { GA_Set_ghosts(mHandle, width); } void GA::GlobalArray::setGhosts(int64_t width[]) const { GA_Set_ghosts64(mHandle, width); } void GA::GlobalArray::setIrregDistr(int mapc[], int nblock[]) const { GA_Set_irreg_distr(mHandle, mapc, nblock); } void GA::GlobalArray::setIrregDistr(int64_t mapc[], int64_t nblock[]) const { GA_Set_irreg_distr64(mHandle, mapc, nblock); } void GA::GlobalArray::setProperty(char *property) { GA_Set_property(mHandle, property); } void GA::GlobalArray::setRestricted(int list[], int nprocs) const { GA_Set_restricted(mHandle, list, nprocs); } void GA::GlobalArray::setRestrictedRange(int lo_proc, int hi_proc) const { GA_Set_restricted_range(mHandle, lo_proc, hi_proc); } void GA::GlobalArray::unsetProperty() { GA_Unset_property(mHandle); } void GA::GlobalArray::setMemoryDev(char *device) { GA_Set_memory_dev(mHandle, device); } void GA::GlobalArray::setPGroup(GA::PGroup *pHandle) const { GA_Set_pgroup(mHandle, pHandle->handle()); } void GA::GlobalArray::sgemm(char ta, char tb, int m, int n, int k, float alpha, const GA::GlobalArray *g_a, const GA::GlobalArray *g_b, float beta) const { GA_Sgemm(ta, tb, m, n, k, alpha, g_a->mHandle, g_b->mHandle, beta, mHandle); } void GA::GlobalArray::sgemm(char ta, char tb, int64_t m, int64_t n, int64_t k, float alpha, const GA::GlobalArray *g_a, const GA::GlobalArray *g_b, float beta) const { GA_Sgemm64(ta, tb, m, n, k, alpha, g_a->mHandle, g_b->mHandle, beta, mHandle); } int GA::GlobalArray::solve(const GA::GlobalArray * g_a) const { return GA_Solve(g_a->mHandle, mHandle); } int GA::GlobalArray::spdInvert() const { return GA_Spd_invert(mHandle); } void GA::GlobalArray::stridedAcc(int lo[], int hi[], int skip[], void*buf, int ld[], void *alpha) const { NGA_Strided_acc(mHandle, lo, hi, skip, buf, ld, alpha); } void GA::GlobalArray::stridedAcc(int64_t lo[], int64_t hi[], int64_t skip[], void*buf, int64_t ld[], void *alpha) const { NGA_Strided_acc64(mHandle, lo, hi, skip, buf, ld, alpha); } void GA::GlobalArray::stridedGet(int lo[], int hi[], int skip[], void*buf, int ld[]) const { NGA_Strided_get(mHandle, lo, hi, skip, buf, ld); } void GA::GlobalArray::stridedGet(int64_t lo[], int64_t hi[], int64_t skip[], void*buf, int64_t ld[]) const { NGA_Strided_get64(mHandle, lo, hi, skip, buf, ld); } void GA::GlobalArray::stridedPut(int lo[], int hi[], int skip[], void*buf, int ld[]) const { NGA_Strided_put(mHandle, lo, hi, skip, buf, ld); } void GA::GlobalArray::stridedPut(int64_t lo[], int64_t hi[], int64_t skip[], void*buf, int64_t ld[]) const { NGA_Strided_put64(mHandle, lo, hi, skip, buf, ld); } void GA::GlobalArray::summarize(int verbose) const { GA_Summarize(verbose); } void GA::GlobalArray::symmetrize() const { GA_Symmetrize(mHandle); } int GA::GlobalArray::totalBlocks() const { return GA_Total_blocks(mHandle); } void GA::GlobalArray::transpose(const GA::GlobalArray * g_a) const { GA_Transpose(mHandle, g_a->mHandle); } void GA::GlobalArray::unpack(GlobalArray *g_dest, GlobalArray *g_mask, int lo, int hi, int *icount) const { GA_Unpack(mHandle, g_dest->mHandle, g_mask->mHandle, lo, hi, icount); } void GA::GlobalArray::unpack(GlobalArray *g_dest, GlobalArray *g_mask, int64_t lo, int64_t hi, int64_t *icount) const { GA_Unpack64(mHandle, g_dest->mHandle, g_mask->mHandle, lo, hi, icount); } void GA::GlobalArray::updateGhosts() const { GA_Update_ghosts(mHandle); } void GA::GlobalArray::updateGhostsNb(GANbhdl *nbhandle) const{ NGA_Update_ghosts_nb(mHandle, nbhandle); } int GA::GlobalArray::updateGhostDir(int dimension, int idir, int cflag) const { return NGA_Update_ghost_dir(mHandle, dimension, idir, cflag); } void GA::GlobalArray::getGhostBlock(int lo[], int hi[], void *buf, int ld[]) const { NGA_Get_ghost_block(mHandle, lo, hi, buf, ld); } void GA::GlobalArray::getGhostBlock(int64_t lo[], int64_t hi[], void *buf, int64_t ld[]) const { NGA_Get_ghost_block64(mHandle, lo, hi, buf, ld); } DoubleComplex GA::GlobalArray::zdot(const GA::GlobalArray * g_a) const { return GA_Zdot(mHandle, g_a->mHandle); } DoubleComplex GA::GlobalArray::zdotPatch(char ta, int alo[], int ahi[], const GA::GlobalArray * g_a, char tb, int blo[], int bhi[]) const { return NGA_Zdot_patch(mHandle, ta, alo, ahi, g_a->mHandle, tb, blo, bhi); } DoubleComplex GA::GlobalArray::zdotPatch(char ta, int64_t alo[], int64_t ahi[], const GA::GlobalArray * g_a, char tb, int64_t blo[], int64_t bhi[]) const { return NGA_Zdot_patch64(mHandle, ta, alo, ahi, g_a->mHandle, tb, blo, bhi); } void GA::GlobalArray::zero() const { GA_Zero(mHandle); } void GA::GlobalArray::zeroPatch (int lo[], int hi[]) const { NGA_Zero_patch(mHandle, lo, hi); } void GA::GlobalArray::zeroPatch (int64_t lo[], int64_t hi[]) const { NGA_Zero_patch64(mHandle, lo, hi); } void GA::GlobalArray::zgemm(char ta, char tb, int m, int n, int k, DoubleComplex alpha, const GA::GlobalArray *g_a, const GA::GlobalArray *g_b, DoubleComplex beta) const { GA_Zgemm(ta, tb, m, n, k, alpha, g_a->mHandle, g_b->mHandle, beta, mHandle); } void GA::GlobalArray::zgemm(char ta, char tb, int64_t m, int64_t n, int64_t k, DoubleComplex alpha, const GA::GlobalArray *g_a, const GA::GlobalArray *g_b, DoubleComplex beta) const { GA_Zgemm64(ta, tb, m, n, k, alpha, g_a->mHandle, g_b->mHandle, beta, mHandle); } /* recent additions */ void GA::GlobalArray::absValue() const { GA_Abs_value(mHandle); } void GA::GlobalArray::addConstant(void* alpha) const { GA_Add_constant(mHandle, alpha); } void GA::GlobalArray::recip() const { GA_Recip(mHandle); } void GA::GlobalArray::elemMultiply(const GA::GlobalArray * g_a, const GA::GlobalArray * g_b) const { GA_Elem_multiply(g_a->mHandle, g_b->mHandle, mHandle); } void GA::GlobalArray::elemDivide(const GA::GlobalArray * g_a, const GA::GlobalArray * g_b) const { GA_Elem_divide(g_a->mHandle, g_b->mHandle, mHandle); } void GA::GlobalArray::elemMaximum(const GA::GlobalArray * g_a, const GA::GlobalArray * g_b) const { GA_Elem_maximum(g_a->mHandle, g_b->mHandle, mHandle); } void GA::GlobalArray::elemMinimum(const GA::GlobalArray * g_a, const GA::GlobalArray * g_b) const { GA_Elem_minimum(g_a->mHandle, g_b->mHandle, mHandle); } void GA::GlobalArray::absValuePatch(int *lo, int *hi) const { GA_Abs_value_patch(mHandle, lo, hi); } void GA::GlobalArray::absValuePatch(int64_t *lo, int64_t *hi) const { GA_Abs_value_patch64(mHandle, lo, hi); } void GA::GlobalArray::addConstantPatch(int *lo,int *hi, void *alpha) const { GA_Add_constant_patch(mHandle, lo, hi, alpha); } void GA::GlobalArray::addConstantPatch(int64_t *lo,int64_t *hi, void *alpha) const { GA_Add_constant_patch64(mHandle, lo, hi, alpha); } void GA::GlobalArray::recipPatch(int *lo, int *hi) const { GA_Recip_patch(mHandle, lo, hi); } void GA::GlobalArray::recipPatch(int64_t *lo, int64_t *hi) const { GA_Recip_patch64(mHandle, lo, hi); } void GA::GlobalArray::stepMax(const GA::GlobalArray * g_a, double *step) const {// CHECK all Step Max functions GA_Step_max(mHandle, g_a->mHandle, step); } void GA::GlobalArray::stepMaxPatch(int *alo, int *ahi, const GA::GlobalArray * g_b, int *blo, int *bhi, double *step) const { GA_Step_max_patch(mHandle, alo, ahi, g_b->mHandle, blo, bhi, step); } void GA::GlobalArray::stepMaxPatch(int64_t *alo, int64_t *ahi, const GA::GlobalArray * g_b, int64_t *blo, int64_t *bhi, double *step) const { GA_Step_max_patch64(mHandle, alo, ahi, g_b->mHandle, blo, bhi, step); } void GA::GlobalArray::elemMultiplyPatch(const GA::GlobalArray * g_a, int *alo,int *ahi, const GA::GlobalArray * g_b, int *blo,int *bhi, int *clo,int *chi) const { GA_Elem_multiply_patch(g_a->mHandle, alo, ahi, g_b->mHandle, blo, bhi, mHandle, clo, chi); } void GA::GlobalArray::elemMultiplyPatch(const GA::GlobalArray * g_a, int64_t *alo,int64_t *ahi, const GA::GlobalArray * g_b, int64_t *blo,int64_t *bhi, int64_t *clo,int64_t *chi) const { GA_Elem_multiply_patch64(g_a->mHandle, alo, ahi, g_b->mHandle, blo, bhi, mHandle, clo, chi); } void GA::GlobalArray::elemDividePatch(const GA::GlobalArray * g_a,int *alo,int *ahi, const GA::GlobalArray * g_b,int *blo,int *bhi, int *clo,int *chi) const { GA_Elem_divide_patch(g_a->mHandle, alo, ahi, g_b->mHandle, blo, bhi, mHandle, clo, chi); } void GA::GlobalArray::elemDividePatch(const GA::GlobalArray * g_a,int64_t *alo, int64_t *ahi, const GA::GlobalArray * g_b, int64_t *blo, int64_t *bhi, int64_t *clo, int64_t *chi) const { GA_Elem_divide_patch64(g_a->mHandle, alo, ahi, g_b->mHandle, blo, bhi, mHandle, clo, chi); } void GA::GlobalArray::elemMaximumPatch(const GA::GlobalArray * g_a, int *alo,int *ahi, const GA::GlobalArray * g_b, int *blo,int *bhi, int *clo,int *chi) const { GA_Elem_maximum_patch(g_a->mHandle, alo, ahi, g_b->mHandle, blo, bhi, mHandle, clo, chi); } void GA::GlobalArray::elemMaximumPatch(const GA::GlobalArray * g_a, int64_t *alo, int64_t *ahi, const GA::GlobalArray * g_b, int64_t *blo, int64_t *bhi, int64_t *clo, int64_t *chi) const { GA_Elem_maximum_patch64(g_a->mHandle, alo, ahi, g_b->mHandle, blo, bhi, mHandle, clo, chi); } void GA::GlobalArray::elemMinimumPatch(const GA::GlobalArray * g_a, int *alo,int *ahi, const GA::GlobalArray * g_b, int *blo,int *bhi, int *clo,int *chi) const { GA_Elem_minimum_patch(g_a->mHandle, alo, ahi, g_b->mHandle, blo, bhi, mHandle, clo, chi); } void GA::GlobalArray::elemMinimumPatch(const GA::GlobalArray * g_a, int64_t *alo, int64_t *ahi, const GA::GlobalArray * g_b, int64_t *blo, int64_t *bhi, int64_t *clo, int64_t *chi) const { GA_Elem_minimum_patch64(g_a->mHandle, alo, ahi, g_b->mHandle, blo, bhi, mHandle, clo, chi); } /*Added by Limin for matrix operations*/ void GA::GlobalArray::shiftDiagonal(void *c) const { GA_Shift_diagonal(mHandle, c); } void GA::GlobalArray::setDiagonal(const GA::GlobalArray * g_v) const { GA_Set_diagonal(mHandle, g_v->mHandle); } void GA::GlobalArray::zeroDiagonal() const { GA_Zero_diagonal(mHandle); } void GA::GlobalArray::addDiagonal(const GA::GlobalArray * g_v) const { GA_Add_diagonal(mHandle, g_v->mHandle); } void GA::GlobalArray::getDiagonal(const GA::GlobalArray * g_a) const { GA_Get_diag(g_a->mHandle, mHandle); } void GA::GlobalArray::scaleRows(const GA::GlobalArray * g_v) const { GA_Scale_rows(mHandle, g_v->mHandle); } void GA::GlobalArray::scaleCols(const GA::GlobalArray * g_v) const { GA_Scale_cols(mHandle, g_v->mHandle); } void GA::GlobalArray::norm1(double *nm) const { GA_Norm1(mHandle, nm); } void GA::GlobalArray::normInfinity(double *nm) const { GA_Norm_infinity(mHandle, nm); } void GA::GlobalArray::median(const GA::GlobalArray * g_a, const GA::GlobalArray * g_b, const GA::GlobalArray * g_c) const { GA_Median(g_a->mHandle, g_b->mHandle, g_c->mHandle, mHandle); } void GA::GlobalArray::medianPatch(const GA::GlobalArray * g_a, int *alo, int *ahi, const GA::GlobalArray * g_b, int *blo, int *bhi, const GA::GlobalArray * g_c, int *clo, int *chi, int *mlo, int *mhi) const { GA_Median_patch(g_a->mHandle, alo, ahi, g_b->mHandle, blo, bhi, g_c->mHandle, clo, chi, mHandle, mlo, mhi); } void GA::GlobalArray::medianPatch(const GA::GlobalArray * g_a, int64_t *alo, int64_t *ahi, const GA::GlobalArray * g_b, int64_t *blo, int64_t *bhi, const GA::GlobalArray * g_c, int64_t *clo, int64_t *chi, int64_t *mlo, int64_t *mhi) const { GA_Median_patch64(g_a->mHandle, alo, ahi, g_b->mHandle, blo, bhi, g_c->mHandle, clo, chi, mHandle, mlo, mhi); } ga-5.9.2/ga++/src/GlobalArray.h000066400000000000000000003216761500715745200160170ustar00rootroot00000000000000#ifndef _GLOBALARRAY_H #define _GLOBALARRAY_H namespace GA { class PGroup; /** * This is the GlobalArray class. */ class GlobalArray { public: /** * Creates an ndim-dimensional array using the regular distribution * model and returns integer handle representing the array. * The array can be distributed evenly or not. The control over the * distribution is accomplished by specifying chunk (block) size for all or * some of array dimensions. * For example, for a 2-dimensional array, setting chunk[0]=dim[0] gives * distribution by vertical strips (chunk[0]*dims[0]); * setting chunk[1]=dim[1] gives distribution by horizontal strips * (chunk[1]*dims[1]). Actual chunks will be modified so that they are at * least the size of the minimum and each process has either zero or one * chunk. Specifying chunk[i] as <1 will cause that dimension to be * distributed evenly. * As a convenience, when chunk is specified as NULL, the entire array is * distributed evenly. * This is a collective operation. * @param[in] type data type(MT_F_DBL,MT_F_INT,MT_F_DCPL) * @param[in] ndim number of array dimensions * @param[in] dims [ndim] array of dimensions * @param[in] arrayname a unique character string * @param[in] chunk [ndim] array of chunks, each element specifies * minimum size that given dimensions should be chunked * up into */ GlobalArray(int type, int ndim, int dims[], char *arrayname, int chunk[]); /** * @copydoc GlobalArray::GlobalArray(int,int,int[],char*,int[]) * @param[in] p_handle processor group handle */ GlobalArray(int type, int ndim, int dims[], char *arrayname, int chunk[], PGroup* p_handle); /** * @copydoc GlobalArray::GlobalArray(int,int,int[],char*,int[]) */ GlobalArray(int type, int ndim, int64_t dims[], char *arrayname, int64_t chunk[]); /** * @copydoc GlobalArray::GlobalArray(int,int,int[],char*,int[]) * @param[in] p_handle processor group handle */ GlobalArray(int type, int ndim, int64_t dims[], char *arrayname, int64_t chunk[], PGroup* p_handle); /** * Creates an array by following the user-specified distribution. * * The distribution is specified as a Cartesian product of distributions * for each dimension. The array indices start at 0. For example, the * following figure demonstrates distribution of a 2-dimensional array 8x10 * on 6 (or more) processors. nblock[2]={3,2}, the size of map array is s=5 * and array map contains the following elements map={0,2,6, 0, 5}. The * distribution is nonuniform because, P1 and P4 get 20 elements each and * processors P0,P2,P3, and P5 only 10 elements each. * * * * * * *
5 5
P0 P3 2
P1 P4 4
P2 P5 2
* * This is a collective operation. * * @param[in] type MA data type (MT_F_DBL,MT_F_INT,MT_F_DCPL) * @param[in] ndim number of array dimensions * @param[in] dims array of dimension values * @param[in] arrayname a unique character string * @param[in] block [ndim] no. of blocks each dimension is divided into * @param[in] maps [s] starting index for for each block; * the size s is a sum all elements of nblock array */ GlobalArray(int type, int ndim, int dims[], char *arrayname, int block[], int maps[]); /** * @copydoc GlobalArray::GlobalArray(int,int,int[],char*,int[],int[]) * @param[in] p_handle processor group handle */ GlobalArray(int type, int ndim, int dims[], char *arrayname, int block[], int maps[], PGroup* p_handle); /** * @copydoc GlobalArray::GlobalArray(int,int,int[],char*,int[],int[]) */ GlobalArray(int type, int ndim, int64_t dims[], char *arrayname, int64_t block[], int64_t maps[]); /** * @copydoc GlobalArray::GlobalArray(int,int,int[],char*,int[],int[]) * @param[in] p_handle processor group handle */ GlobalArray(int type, int ndim, int64_t dims[], char *arrayname, int64_t block[], int64_t maps[], PGroup* p_handle); /** * Creates an ndim-dimensional array with a layer of ghost cells around * the visible data on each processor using the regular distribution model. * * The array can be distributed evenly or not evenly. The control over * the distribution is accomplished by specifying chunk (block) size for * all or some of the array dimensions. For example, for a 2-dimensional * array, setting chunk(1)=dim(1) gives distribution by vertical strips * (chunk(1)*dims(1)); setting chunk(2)=dim(2) gives distribution by * horizontal strips (chunk(2)*dims(2)). Actual chunks will be modified * so that they are at least the size of the minimum and each process * has either zero or one chunk. Specifying chunk(i) as <1 will cause * that dimension (i-th) to be distributed evenly. The width of the * ghost cell layer in each dimension is specified using the array * width(). The local data of the global array residing on each * processor will have a layer width[n] ghosts cells wide on either * side of the visible data along the dimension n. * * @param[in] type data type (MT_DBL,MT_INT,MT_DCPL) * @param[in] ndim number of array dimensions * @param[in] dims [ndim] array of dimensions * @param[in] width [ndim] array of ghost cell widths * @param[in] arrayname a unique character string * @param[in] chunk [ndim] array of chunks, each element specifies * minimum size that given dimensions should be * chunked up into * @param[in] ghosts this is a dummy parameter: added to increase the * number of arguments, inorder to avoid the conflicts * among constructors. (ghosts = 'g' or 'G') */ GlobalArray(int type, int ndim, int dims[], int width[], char *arrayname, int chunk[], char ghosts); /** * @copydoc GlobalArray::GlobalArray(int,int,int[],int[],char*,int[],char) * @param[in] p_handle processor group handle */ GlobalArray(int type, int ndim, int dims[], int width[], char *arrayname, int chunk[], PGroup* p_handle, char ghosts); /** * @copydoc GlobalArray::GlobalArray(int,int,int[],int[],char*,int[],char) */ GlobalArray(int type, int ndim, int64_t dims[], int64_t width[], char *arrayname, int64_t chunk[], char ghosts); /** * @copydoc GlobalArray::GlobalArray(int,int,int[],int[],char*,int[],char) * @param[in] p_handle processor group handle */ GlobalArray(int type, int ndim, int64_t dims[], int64_t width[], char *arrayname, int64_t chunk[], PGroup* p_handle, char ghosts); /** * Creates an array with ghost cells by following the user-specified * distribution. * * The distribution is specified as a Cartesian product of distributions * for each dimension. For example, the following figure demonstrates * distribution of a 2-dimensional array 8x10 on 6 (or more) processors. * nblock(2)={3,2}, the size of map array is s=5 and array map contains * the following elements map={1,3,7, 1, 6}. The distribution is * nonuniform because, P1 and P4 get 20 elements each and processors * P0,P2,P3, and P5 only 10 elements each. * * * * * * *
5 5
P0 P3 2
P1 P4 4
P2 P5 2
* * The array width[] is used to control the width of the ghost cell * boundary around the visible data on each processor. The local data * of the global array residing on each processor will have a layer * width[n] ghosts cells wide on either side of the visible data along * the dimension n. This is a collective operation. * * @param[in] type data type (MT_DBL,MT_INT,MT_DCPL) * @param[in] ndim number of array dimensions * @param[in] dims [ndim] array of dimensions * @param[in] width [ndim] array of ghost cell widths * @param[in] arrayname a unique character string * @param[in] block [ndim] no. of blocks each dimension is divided into * @param[in] maps [s] starting index for for each block; * the size s is a sum of all elements of nblock array * @param[in] ghosts this is a dummy parameter: added to increase the * number of arguments, inorder to avoid the conflicts * among constructors. (ghosts = 'g' or 'G') */ GlobalArray(int type, int ndim, int dims[], int width[], char *arrayname, int block[], int maps[], char ghosts); /** * @copydoc GlobalArray::GlobalArray(int,int,int[],int[],char*,int[],int[],char) * @param[in] p_handle processor group handle */ GlobalArray(int type, int ndim, int dims[], int width[], char *arrayname, int block[], int maps[], PGroup* p_handle, char ghosts); /** * @copydoc GlobalArray::GlobalArray(int,int,int[],int[],char*,int[],int[],char) */ GlobalArray(int type, int ndim, int64_t dims[], int64_t width[], char *arrayname, int64_t block[], int64_t maps[], char ghosts); /** * @copydoc GlobalArray::GlobalArray(int,int,int[],int[],char*,int[],int[],char) * @param[in] p_handle processor group handle */ GlobalArray(int type, int ndim, int64_t dims[], int64_t width[], char *arrayname, int64_t block[], int64_t maps[], PGroup* p_handle, char ghosts); /** * Creates a new array by applying all the properties of another existing * array. * * This is a collective operation. * * @param[in] arrayname a character string * @param[in] g_a integer handle for reference array */ GlobalArray(const GlobalArray &g_a, char *arrayname); /** * Creates a new array by applying all the properties of another existing * array. * * This is a collective operation. * * @param[in] g_a integer handle for reference array */ GlobalArray(const GlobalArray &g_a); /** * Creates a new array with no existing attributes. * * @note All attributes must subsequently be set using the "set" methods. * * This is a collective operation. */ GlobalArray(); /** Destructor */ ~GlobalArray(); /* access the data */ /** @return the array handle */ int handle() const { return mHandle; } /* Global Array operations */ /** * Combines data from local array buffer with data in the global array * section. * * @note The local array is assumed to be have the same number of dimensions * as the global array. * * global array section (lo[],hi[]) += *alpha * buffer * * This is a one-sided and atomic operation. * * @param[in] lo [ndim] array of starting indices for array section * @param[in] hi [ndim] array of ending indices for array section * @param[in] buf pointer to the local buffer array * @param[in] ld [ndim-1] array specifying leading * dimensions/strides/extents for buffer array * @param[in] alpha scale factor (double/DoubleComplex/long *) */ void acc(int lo[], int hi[], void *buf, int ld[], void *alpha) const; /** * @copydoc GlobalArray::acc(int[],int[],void*,int[],void*)const */ void acc(int64_t lo[], int64_t hi[], void *buf, int64_t ld[], void *alpha) const; /** * Provides access to the specified patch of a global array. * * Returns array of leading dimensions ld and a pointer to the first element * in the patch. This routine allows to access directly, in place * elements in the local section of a global array. It useful for * writing new GA operations. A call to ga_access normally follows a * previous call to ga_distribution that returns coordinates of the * patch associated with a processor. You need to make sure that the * coordinates of the patch are valid (test values returned from * ga_distribution). * * Each call to ga_access has to be followed by a call to either * ga_release or ga_release_update. You can access in this fashion only * local data. Since the data is shared with other processes, you need * to consider issues of mutual exclusion. This operation is local. * * @param[in] lo [ndim] array of starting indices for array section * @param[in] hi [ndim] array of ending indices for array section * @param[out] ptr points to location of first element in patch * @param[out] ld [ndim-1] leading dimensions for the pacth elements */ void access(int lo[], int hi[], void *ptr, int ld[]) const; /** * @copydoc GlobalArray::access(int[],int[],void*,int[])const */ void access(int64_t lo[], int64_t hi[], void *ptr, int64_t ld[]) const; /** * Provides access to the specified block of a global array that is using * simple block-cyclic data distribution. Returns array of leading * dimensions ld and a pointer to the first element in the patch. This * routine allows user to access directly, in-place * elements in the * local section of a global array. It useful for writing new GA * operations. A call to ga_access normally follows a previous call to * ga_distribution that returns coordinates of the patch associated with * a processor. You need to make sure that the coordinates of the patch * are valid (test values returned from * ga_distribution). * * Each call to ga_access_block has to be followed by a call to either * ga_release_block or ga_release_block_update. You can access in this * fashion only local data. Since the data is shared with other processes, * you need to consider issues of mutual exclusion. This operation is * local. * * @param[in] idx index of block * @param[out] ptr points to location of first element in patch * @param[out] ld [ndim-1] leading dimensions for the pacth elements */ void accessBlock(int idx, void *ptr, int ld[]) const; /** * @copydoc GlobalArray::accessBlock(int,void*,int[])const */ void accessBlock(int64_t idx, void *ptr, int64_t ld[]) const; /** * Provides access to the specified block of a global array that is using * SCALAPACK type block-cyclic data distribution. Returns array of leading * dimensions ld and a pointer to the first element in the patch. This * routine allows user to access directly, in-place * elements in the * local section of a global array. It useful for writing new GA * operations. A call to ga_access_block normally follows a previous call to * ga_distribution that returns coordinates of the patch associated with * a processor. You need to make sure that the coordinates of the patch * are valid (test values returned from * ga_distribution). * * Each call to ga_access_block_grid has to be followed by a call to either * ga_release_block_grid or ga_release_block_grid_update. You can access in * this fashion only local data. Since the data is shared with other * processes, you need to consider issues of mutual exclusion. This * operation is local. * * @param[in] index [ndim] indices of block in processor grid * @param[out] ptr points to location of first element in patch * @param[out] ld [ndim-1] leading dimensions for the pacth elements */ void accessBlockGrid(int index[], void *ptr, int ld[]) const; /** * @copydoc GlobalArray::accessBlockGrid(int[],void*,int[])const */ void accessBlockGrid(int64_t index[], void *ptr, int64_t ld[]) const; /** * Provides access to the local data of a global array that is using * either the simple or SCALAPACK type block-cyclic data distribution. * Returns the length of the local data block and a pointer to the first * element. This routine allows user to access directly, in-place * elements in the local section of a global array. It useful for writing * new GA operations. * * Each call to ga_access_segment has to be followed by a call to either * ga_release_segment or ga_release_segmentupdate. You can access in * this fashion only local data. Since the data is shared with other * processes, you need to consider issues of mutual exclusion. This * operation is local. * * @param[in] index processor ID * @param[out] ptr points to location of first element * @param[out] len length of locally held data */ void accessBlockSegment(int index, void *ptr, int *len) const; /** * @copydoc GlobalArray::accessBlockSegment(int,void*,int*)const */ void accessBlockSegment(int index, void *ptr, int64_t *len) const; /** * Provides access to the local patch of the global array. Returns * leading dimension ld and and pointer for the data. This routine * will provide access to the ghost cell data residing on each processor. * Calls to accessGhosts should normally follow a call to * distribution that returns coordinates of the visible data patch * associated with a processor. You need to make sure that the coordinates * of the patch are valid (test values returned from distribution). * * You can only access local data. * This is a local operation. * * @param[out] dims [ndim] array of dimensions of local patch, * including ghost cells * @param[out] ptr returns an index corresponding to the origin the global * array patch held locally on the processor * @param[out] ld [ndim-1] physical dimensions of the local array patch, * including ghost cells */ void accessGhosts(int dims[], void *ptr, int ld[]) const; /** * @copydoc GlobalArray::accessGhosts(int[],void*,int[])const */ void accessGhosts(int64_t dims[], void *ptr, int64_t ld[]) const; /** * This function can be used to return a pointer to any data element * in the locally held portion of the global array and can be used to * directly access ghost cell data. The array subscript refers to the * local index of the element relative to the origin of the local * patch (which is assumed to be indexed by (0,0,...)). * * This is a local operation. * * @param[out] ptr index pointing to location of element * indexed by subscript[] * @param[in] subscript [ndim] array of integers that index desired element * @param[out] ld [ndim-1] array of strides for local data patch. * These include ghost cell widths. */ void accessGhostElement(void *ptr, int subscript[], int ld[]) const; /** * @copydoc GlobalArray::accessGhostElement(void*,int[],int[])const */ void accessGhostElement(void *ptr, int64_t subscript[], int64_t ld[]) const; /** * The arrays are aded together elemet-wise: * [for example: g_c.add(...,g_a, .., g_b);] * c = alpha * a + beta * b * The result c may replace one of he input arrays(a/b). * This is a collective operation. * * @param[in] alpha scale factor * @param[in] g_a array * @param[in] beta scale factor * @param[in] g_b array */ void add(void *alpha, const GlobalArray * g_a, void *beta, const GlobalArray * g_b) const; /** * Patches of arrays (which must have the same number of elements) are * added together element-wise. * c[ ][ ] = alpha * a[ ][ ] + beta * b[ ][ ]. * * This is a collective operation. * * @param[in] alpha scale factor * @param[in] g_a global array * @param[in] alo patch of g_a * @param[in] ahi patch of g_a * @param[in] beta scale factor * @param[in] g_b global array * @param[in] blo patch of g_b * @param[in] bhi patch of g_b * @param[in] clo patch of this GlobalArray * @param[in] chi patch of this GlobalArray */ void addPatch(void *alpha, const GlobalArray * g_a, int alo[], int ahi[], void *beta, const GlobalArray * g_b, int blo[], int bhi[], int clo[], int chi[]) const; /** * @copydoc GlobalArray::addPatch(void*,const GlobalArray*,int[],int[],void*,const GlobalArray*,int[],int[],int[],int[])const */ void addPatch( void *alpha, const GlobalArray * g_a, int64_t alo[], int64_t ahi[], void *beta, const GlobalArray * g_b, int64_t blo[], int64_t bhi[], int64_t clo[], int64_t chi[]) const; /** * Allocate internal memory etc. to create a global array * * @return TODO */ int allocate() const; /** * Deallocate internal memory etc. to create a global array * * @return TODO */ int deallocate() const; /** * This function can be used to preallocate internal buffers that are used by * the gather, scatter and scatter accumulate calls. This avoids repeated * memory allocations in these calls that can reduce performance. The value of * nelems should be set to the maximum number of elements that will be moved * in any single call. * * This is a local operation. * * @param[in] nelems The maximum number of elements that will be moved in * any gather, scatter, scatter-accumulate call */ void allocGatscatBuf(int nelems) const; /** * Check that the global array handle g_a is valid ... if not call * ga_error with the string provided and some more info. * * This operation is local. * * @param[in] string message */ void checkHandle(char* string) const; /** * Compares distributions of two global arrays. * * This is a collective operation. * * @param[in] g_a GlobalArray to compare * * @return 0 if distributions are identical and 1 when they are not. */ int compareDistr(const GlobalArray *g_a) const; /** * Copies elements in array represented by g_a into the array * represented by g_b [say for example: g_b.copy(g_a);]. * The arrays must be the same type, shape, and identically aligned. * * This is a collective operation. * * @param[in] g_a GlobalArray to copy */ void copy(const GlobalArray *g_a) const; /** * Copies elements in a patch of one array (ga) into another one (say for * example:gb.copyPatch(...,ga,....); ). * * The patches of arrays may be of different shapes but must have the same * number of elements. Patches must be nonoverlapping (if gb=ga). * * trans = 'N' or 'n' means that the transpose operator should not be * applied. trans = 'T' or 't' means that transpose operator should be * applied. This is a collective operation. * * @param[in] trans see above * @param[in] ga global array * @param[in] alo ga patch coordinates * @param[in] ahi ga patch coordinates * @param[in] blo this GlobalArray's patch coordinates * @param[in] bhi this GlobalArray's patch coordinates */ void copyPatch(char trans, const GlobalArray* ga, int alo[], int ahi[], int blo[], int bhi[]) const; /** * @copydoc GlobalArray::copyPatch(char,const GlobalArray*,int[],int[],int[],int[])const */ void copyPatch( char trans, const GlobalArray* ga, int64_t alo[], int64_t ahi[], int64_t blo[], int64_t bhi[]) const; /** * Computes element-wise dot product of the two arrays which must be of * the same types and same number of elements. * return value = SUM_ij a(i,j)*b(i,j) * * This is a collective operation. * * @param[in] g_a GlobalArray operand */ double ddot(const GlobalArray * g_a) const; /** * Computes the element-wise dot product of the two (possibly transposed) * patches which must be of the same type and have the same number of * elements. * * @param[in] ta transpose flags * @param[in] alo g_a patch coordinates * @param[in] ahi g_a patch coordinates * @param[in] g_a global array * @param[in] tb transpose flags * @param[in] blo g_b patch coordinates * @param[in] bhi g_b patch coordinates */ double ddotPatch(char ta, int alo[], int ahi[], const GlobalArray * g_a, char tb, int blo[], int bhi[]) const; /** * @copydoc GlobalArray::ddotPatch(char,int[],int[],const GlobalArray*,char,int[],int[]const */ double ddotPatch( char ta, int64_t alo[], int64_t ahi[], const GlobalArray * g_a, char tb, int64_t blo[], int64_t bhi[]) const; /** * Deallocates the array and frees any associated resources. */ void destroy(); /** * Performs one of the matrix-matrix operations: * [say: g_c.dgemm(..., g_a, g_b,..);] * * C := alpha*op( A )*op( B ) + beta*C, \n * where op( X ) is one of \n * op( X ) = X or op( X ) = X', \n * alpha and beta are scalars, and A, B and C are matrices, with op( A ) * an m by k matrix, op( B ) a k by n matrix and C an m by n matrix. * On entry, transa specifies the form of op( A ) to be used in the * matrix multiplication as follows:\n * ta = 'N' or 'n', op( A ) = A. \n * ta = 'T' or 't', op( A ) = A'. \n * * This is a collective operation. * * @param[in] ta transpose operators * @param[in] tb transpose operators * @param[in] m number of rows of op(A) and of matrix C * @param[in] n number of columns of op(B) and of matrix C * @param[in] k number of columns of op(A) and rows of matrix op(B) * @param[in] alpha scale factors * @param[in] g_a input arrays * @param[in] g_b input arrays * @param[in] beta scale factors */ void dgemm(char ta, char tb, int m, int n, int k, double alpha, const GlobalArray *g_a, const GlobalArray *g_b,double beta) const; /** * @copydoc GlobalArray::dgemm(char,char,int,int,int,double,const GlobalArray*,const GlobalArray*,double)const */ void dgemm(char ta, char tb, int64_t m, int64_t n, int64_t k, double alpha, const GlobalArray *g_a, const GlobalArray *g_b,double beta) const; /** * Solve the generalized eigen-value problem returning all eigen-vectors * and values in ascending order. The input matrices are not overwritten * or destroyed. * * This is a collective operation. * * @param[in] g_s Metric * @param[out] g_v Global matrix to return evecs * @param[out] eval Local array to return evals * */ void diag(const GlobalArray *g_s, GlobalArray *g_v, void *eval) const; /** * Solve the generalized eigen-value problem returning all eigen-vectors * and values in ascending order. Recommended for REPEATED calls if g_s * is unchanged. Values of the control flag: * * value action/purpose * * 0 indicates first call to the eigensolver * * >0 consecutive calls (reuses factored g_s) * * <0 only erases factorized g_s; g_v and eval unchanged * (should be called after previous use if another * eigenproblem, i.e., different g_a and g_s, is to * be solved) * * The input matrices are not destroyed. * * This is a collective operation. * * @param[in] control Control flag * @param[in] g_s Metric * @param[out] g_v Global matrix to return evecs * @param[out] eval Local array to return evals */ void diagReuse(int control, const GlobalArray *g_s, GlobalArray *g_v, void *eval) const; /** * Solve the standard (non-generalized) eigenvalue problem returning * all eigenvectors and values in the ascending order. The input matrix * is neither overwritten nor destroyed. * * This is a collective operation. * * @param[out] g_v Global matrix to return evecs * @param[out] eval Local array to return evals */ void diagStd(GlobalArray *g_v, void *eval) const; /** * TODO */ void diagSeq(const GlobalArray * g_s, const GlobalArray * g_v, void *eval) const; /** * TODO */ void diagStdSeq(const GlobalArray * g_v, void *eval) const; /** * If no array elements are owned by process 'me', the range is returned * as lo[]=-1 and hi[]=-2 for all dimensions. * * The operation is local. * * @param[in] me process number * @param[in] lo [ndim] array of starting indices for array section * @param[in] hi [ndim] array of ending indices for array section */ void distribution(int me, int* lo, int* hi) const; /** * @copydoc GlobalArray::distribution(int,int*,int*)const */ void distribution(int me, int64_t* lo, int64_t* hi) const; /** * TODO */ float fdot(const GlobalArray * g_a) const; /** * TODO */ float fdotPatch( char t_a, int alo[], int ahi[], const GlobalArray * g_b, char t_b, int blo[], int bhi[]) const; /** * @copydoc GlobalArray::fdotPatch(char,int[],int[],const GlobalArray*,char,int[],int[])const */ float fdotPatch( char t_a, int64_t alo[], int64_t ahi[], const GlobalArray * g_b, char t_b, int64_t blo[], int64_t bhi[]) const; /** * Assign a single value to all elements in the array. * * This is a collective operation. * * @param[in] value pointer to the value of appropriate type * (double/DoubleComplex/long) that matches array type. */ void fill(void *value) const; /** * Fill the patch with value of 'val' * * This is a collective operation. * * @param[in] lo patch of this GlobalArray * @param[in] hi patch of this GlobalArray * @param[in] val value to fill * */ void fillPatch (int lo[], int hi[], void *val) const; /** * @copydoc GlobalArray::fillPatch(int[],int[],void*)const */ void fillPatch (int64_t lo[], int64_t hi[], void *val) const; /** * This function can be used to free preallocate internal buffers that were * set using the allocGatscatBuf call. * * This is a local operation. */ void freeGatscatBuf(); /** * Gathers array elements from a global array into a local array. * The contents of the input arrays (v, subscrArray) are preserved, * but their contents might be (consistently) shuffled on return. * * @code * for(k=0; k<= n; k++){ * v[k] = a[subsArray[k][0]][subsArray[k][1]][subsArray[k][2]]...; * } * @endcode * * This is a one-sided operation. * * @param[in] n number of elements * @param[in] v [n] array containing values * @param[in] subsarray [n][ndim] array of subscripts for each element */ void gather(void *v, int * subsarray[], int n) const; /** * @copydoc GlobalArray::gather(void*,int*[],int)const */ void gather(void *v, int64_t * subsarray[], int64_t n) const; /** * Copies data from global array section to the local array buffer. The * local array is assumed to be have the same number of dimensions as the * global array. Any detected inconsitencies/errors in the input arguments * are fatal. * * Example: For ga_get operation transfering data from the [10:14,0:4] * section of 2-dimensional 15x10 global array into local buffer 5x10 * array we have: lo={10,0}, hi={14,4}, ld={10} * * One-side operation. * * @param[in] lo [ndim] array of starting indices for global array section * @param[in] hi [ndim] array of ending indices for global array section * @param[out] buf pointer to the local buffer array where the data goes * @param[in] ld [ndim-1] array specifying leading * dimensions/strides/extents for buffer array */ void get(int lo[], int hi[], void *buf, int ld[]) const; /** * @copydoc GlobalArray::get(int[],int[],void*,int[])const */ void get(int64_t lo[], int64_t hi[], void *buf, int64_t ld[]) const; /** * The function retrieves the number of blocks along each coordinate dimension * and the dimensions of the individual blocks for a global array with a * block-cyclic data distribution. * * This is a local operation. * * @param[out] num_blocks [ndim] array containing number of blocks along each * coordinate direction * @param[out] block_dims [ndim] array containing block dimensions */ void getBlockInfo(int num_blocks[], int block_dims[]); /** * This function returns 1 if the global array has some dimensions for * which the ghost cell width is greater than zero, it returns 0 otherwise. * * This is a local operation. * * @return 1 if this GlobalArray has some dimensions for which teh ghost * cell width is greater than zero; 0 otherwise */ int hasGhosts() const; /** * Computes element-wise dot product of the two arrays which must be of * the same types and same number of elements. * * This is a collective operation. * * @param[in] g_a GlobalArray * @return value = SUM_ij a(i,j)*b(i,j) */ int idot(const GlobalArray * g_a) const; /** * Computes the element-wise dot product of the two (possibly transposed) * patches which must be of the same type and have the same number of * elements. * * @param[in] ta transpose flags * @param[in] alo g_a patch coordinates * @param[in] ahi g_a patch coordinates * @param[in] g_a global array * @param[in] tb transpose flags * @param[in] blo this GlobalArray's patch coordinates * @param[in] bhi this GlobalArray's patch coordinates */ long idotPatch( char ta, int alo[], int ahi[], const GlobalArray * g_a, char tb, int blo[], int bhi[]) const; /** * @copydoc GlobalArray::idotPatch(char,int[],int[],const GlobalArray*,char,int[],int[])const */ long idotPatch( char ta, int64_t alo[], int64_t ahi[], const GlobalArray * g_a, char tb, int64_t blo[], int64_t bhi[]) const; /** * Returns data type and dimensions of the array. * * This operation is local. * * @param[out] type data type * @param[out] ndim number of dimensions * @param[out] dims array of dimensions */ void inquire(int *type, int *ndim, int dims[]) const; /** * @copydoc GlobalArray::inquire(int*,int*,int[])const */ void inquire(int *type, int *ndim, int64_t dims[]) const; /** * Returns the name of an array represented by the handle g_a. * * This operation is local. * * @return copy of the name of this GlobalArray */ char* inquireName() const; /** * Computes element-wise dot product of the two arrays which must be of * the same types and same number of elements. * * * This is a collective operation. * * @param[in] g_a array handle * * @return value = SUM_ij a(i,j)*b(i,j) */ long ldot(const GlobalArray * g_a) const; /** * Computes the element-wise dot product of the two (possibly transposed) * patches which must be of the same type and have the same number of * elements. * * @param[in] ta transpose flags * @param[in] alo g_a patch coordinates * @param[in] ahi g_a patch coordinates * @param[in] g_a global array * @param[in] tb transpose flags * @param[in] blo this GlobalArray's patch coordinates * @param[in] bhi this GlobalArray's patch coordinates */ long ldotPatch( char ta, int alo[], int ahi[], const GlobalArray * g_a, char tb, int blo[], int bhi[]) const; /** * @copydoc GlobalArray::ldotPatch(char,int[],int[],const GlobalArray*,char,int[],int[])const */ long ldotPatch( char ta, int64_t alo[], int64_t ahi[], const GlobalArray * g_a, char tb, int64_t blo[], int64_t bhi[]) const; /** * Solves a system of linear equations * * A * X = B * * using the Cholesky factorization of an NxN double precision symmetric * positive definite matrix A (epresented by handle g_a). On successful * exit B will contain the solution X. * * This is a collective operation. * * @param[in] g_a coefficient matrix * * @return = 0 : successful exit\n * > 0 : the leading minor of this order is not positive * definite and the factorization could not be completed */ int lltSolve(const GlobalArray * g_a) const; /** * Return in owner the GA compute process id that 'owns' the data. If any * element of subscript[] is out of bounds "-1" is returned. * * This operation is local. * * @param[in] subscript [ndim] element subscript * * @return ID of compute process which owns the data */ int locate(int subscript[]) const; /** * @copydoc GlobalArray::locate(int[])const */ int locate(int64_t subscript[]) const; /** * Return the list of the GA processes id that 'own' the data. Parts of the * specified patch might be actually 'owned' by several processes. If lo/hi * are out of bounds "0" is returned, otherwise return value is equal to the * number of processes that hold the data. This operation is local. * * map[i][0:ndim-1] - lo[i] * * map[i][ndim:2*ndim-1] - hi[i] * * procs[i] - processor id that owns data in patch * lo[i]:hi[i] * * @param[in] lo [ndim] array of starting indices for array section * @param[in] hi [ndim] array of ending indices for array section * @param[out] map [][2*ndim] array with mapping information * @param[out] procs [nproc] list of processes that own a part of selection * * @return 0 if lo/hi are out of bounds, otherwise the number of processes * holding data */ int locateRegion(int lo[], int hi[], int map[], int procs[]) const; /** * @copydoc GlobalArray::locateRegion(int[],int[],int[],int[])const */ int locateRegion(int64_t lo[], int64_t hi[], int64_t map[], int procs[]) const; /** * Solve the system of linear equations op(A)X = B based on the LU * factorization. * * op(A) = A or A' depending on the parameter trans: * * trans = 'N' or 'n' means that the transpose operator should not * be applied. * * trans = 'T' or 't' means that the transpose operator should be applied. * * Matrix A is a general real matrix. Matrix B contains possibly multiple * rhs vectors. The array associated with the handle g_b is overwritten * by the solution matrix X. * This is a collective operation. * * @param[in] trans transpose or not transpose * @param[in] g_a coefficient matrix */ void luSolve(char trans, const GlobalArray * g_a) const; /** * ga_matmul_patch is a patch version of ga_dgemm: * * C[cilo:cihi,cjlo:cjhi] := alpha* AA[ailo:aihi,ajlo:ajhi] * * BB[bilo:bihi,bjlo:bjhi] ) + * beta*C[cilo:cihi,cjlo:cjhi], * * where AA = op(A), BB = op(B), and op( X ) is one of * op( X ) = X or op( X ) = X', * * Valid values for transpose arguments: 'n', 'N', 't', 'T'. It works * for both double and DoubleComplex data tape. * This is a collective operation. * * @param[in] g_a global array * @param[in] g_b global array * @param[in] ailo patch of g_a * @param[in] aihi patch of g_a * @param[in] ajlo patch of g_a * @param[in] ajhi patch of g_a * @param[in] bilo patch of g_b * @param[in] bihi patch of g_b * @param[in] bjlo patch of g_b * @param[in] bjhi patch of g_b * @param[in] cilo patch of g_c * @param[in] cihi patch of g_c * @param[in] cjlo patch of g_c * @param[in] cjhi patch of g_c * @param[in] alpha scale factors * @param[in] beta scale factors * @param[in] transa transpose operators * @param[in] transb transpose operators */ void matmulPatch(char transa, char transb, void* alpha, void *beta, const GlobalArray *g_a, int ailo, int aihi, int ajlo, int ajhi, const GlobalArray *g_b, int bilo, int bihi, int bjlo, int bjhi, int cilo, int cihi, int cjlo, int cjhi) const; /** * @copydoc GlobalArray::matmulPatch(char,char,void*,void*,const GlobalArray*,int,int,int,int,const GlobalArray*,int,int,int,int,int,int,int,int)const */ void matmulPatch(char transa, char transb, void* alpha, void *beta, const GlobalArray *g_a, int64_t ailo, int64_t aihi, int64_t ajlo, int64_t ajhi, const GlobalArray *g_b, int64_t bilo, int64_t bihi, int64_t bjlo, int64_t bjhi, int64_t cilo, int64_t cihi, int64_t cjlo, int64_t cjhi) const; /** * nga_matmul_patch is a n-dimensional patch version of ga_dgemm: * * C[clo[]:chi[]] := alpha* AA[alo[]:ahi[]] * * BB[blo[]:bhi[]]) + * beta*C[clo[]:chi[]], * * where AA = op(A), BB = op(B), and op( X ) is one of * op( X ) = X or op( X ) = X', * * Valid values for transpose arguments: 'n', 'N', 't', 'T'. It works * for both double and DoubleComplex data tape. * * This is a collective operation. * * @param[in] g_a global array * @param[in] g_b global array * @param[in] alo array of patch of g_a * @param[in] ahi array of patch of g_a * @param[in] blo array of patch of g_b * @param[in] bhi array of patch of g_b * @param[in] clo array of patch of g_c * @param[in] chi array of patch of g_c * @param[in] alpha scale factors * @param[in] beta scale factors * @param[in] transa transpose operators * @param[in] transb transpose operators */ void matmulPatch(char transa, char transb, void* alpha, void *beta, const GlobalArray *g_a, int *alo, int *ahi, const GlobalArray *g_b, int *blo, int *bhi, int *clo, int *chi) const; /** * @copydoc GlobalArray::matmulPatch(char,char,void*,void*,const GlobalArray*,int*,int*,const GlobalArray*,int*,int*,int*,int*)const */ void matmulPatch(char transa, char transb, void* alpha, void *beta, const GlobalArray *g_a, int64_t *alo, int64_t *ahi, const GlobalArray *g_b, int64_t *blo, int64_t *bhi, int64_t *clo, int64_t *chi) const; /** * This function merges all values in a patch of a mirrored array into * a patch in another global array g_b. * * This is a collective operation. * * @param[in] alo [ndim] patch indices of mirrored array * @param[in] ahi [ndim] patch indices of mirrored array * @param[in] blo [ndim] patch indices of result array * @param[in] bhi [ndim] patch indices of result array * @param[out] g_a global array containing result */ void mergeDistrPatch(int alo[], int ahi[], GlobalArray *g_a, int blo[], int bhi[]); /** * @copydoc GlobalArray::mergeDistrPatch(int[],int[],GlobalArray*,int[],int[]) */ void mergeDistrPatch(int64_t alo[], int64_t ahi[], GlobalArray *g_a, int64_t blo[], int64_t bhi[]); /** * This function returns 0 if a global array is not mirrored and 1 if it is. */ int isMirrored(); /** * This function adds together all copies of a mirrored array so that all * copies are the same. * * This is a collective operation. */ void mergeMirrored(); /** * Non-blocking accumalate operation. This is function performs an * accumulate operation and returns a nblocking handle. Completion of the * operation can be forced by calling the nbwait method on the handle. * * This is a onesided operation. * * @param[in] lo [ndim] patch coordinates of block * @param[in] hi [ndim] patch coordinates of block * @param[in] buf local buffer containing data * @param[in] ld [ndim-1] array of strides for local data * @param[in] alpha multiplier for data before adding to existing results * @param[out] nbhandle nonblocking handle */ void nbAcc(int lo[], int hi[], void *buf, int ld[], void *alpha, GANbhdl *nbhandle); /** * @copydoc GlobalArray::nbAcc(int[],int[],void*,int[],void*,GANbhdl*) */ void nbAcc(int64_t lo[], int64_t hi[], void *buf, int64_t ld[], void *alpha, GANbhdl *nbhandle); /** * Non-blocking get operation. This is function gets a data block from a * global array, copies it into a local buffer, and returns a nonblocking * handle. Completion of the operation can be forced by calling the nbwait * method on the handle. * * This is a onesided operation. * * @param[in] lo [ndim] patch coordinates of block * @param[in] hi [ndim] patch coordinates of block * @param[in] buf local buffer to receive data * @param[in] ld [ndim-1] array of strides for local data * @param[out] nbhandle nonblocking handle */ void nbGet(int lo[], int hi[], void *buf, int ld[], GANbhdl *nbhandle); /** * @copydoc GlobalArray::nbGet(int[],int[],void*,int[],GANbhdl*) */ void nbGet(int64_t lo[], int64_t hi[], void *buf, int64_t ld[], GANbhdl *nbhandle); /** * Non-blocking update operation for arrays with ghost cells. Ghost cells * along the coordinates specified in the mask array are updated with * non-blocking get calls. The mask array must contain either 0's or 1's. * * This is a onesided operation. * * @param[in] mask [ndim] array with flags for directions that are * to be updated * @param[out] nbhandle nonblocking handle */ void nbGetGhostDir(int mask[], GANbhdl *nbhandle); /** * @copydoc GlobalArray::nbGetGhostDir(int[],GANbhdl*) */ void nbGetGhostDir(int64_t mask[], GANbhdl *nbhandle); /** * Given a distribution of an array represented by the handle g_a, * returns the number of partitions of each array dimension. * * This operation is local. * * @param[out] nblock [ndim] number of partitions for each dimension */ void nblock(int nblock[]) const; /** * Non-blocking put operation. This is function puts a data block from a * local array, copies it into a global array, and returns a nonblocking * handle. Completion of the operation can be forced by calling the nbwait * method on the handle. * * This is a onesided operation. * * @param[in] lo [ndim] patch coordinates of block * @param[in] hi [ndim] patch coordinates of block * @param[in] buf local buffer that supplies data * @param[in] ld [ndim-1] array of strides for local data * @param[out] nbhandle nonblocking handle */ void nbPut(int lo[], int hi[], void *buf, int ld[], GANbhdl *nbhandle); /** * @copydoc GlobalArray::nbPut(int[],int[],void*,int[],GANbhdl*) */ void nbPut(int64_t lo[], int64_t hi[], void *buf, int64_t ld[], GANbhdl *nbhandle); /** * Returns the number of dimensions in this GlobalArray. * * This operation is local. * * @return number of dimensions aka rank */ int ndim() const; /** * This subroutine can be used instead of the allocate function to create a * global array on top of memory allocated for another global array. This can * be used in situations where it is desirable to create and destroy a large * number of global arrays while simultaneously reducing the overhead * associated with global array creation. * * @param[in] g_p handle of parent global array * @return true if allocation is successful */ int overlay(const GlobalArray *g_p); /** * The pack subroutine is designed to compress the values in the source vector * g_src into a smaller destination array g_dest based on the values in an * integer mask array g_mask. The values lo and hi denote the range of * elements that should be compressed and icount is a variable that on output * lists the number of values placed in the compressed array. This operation * is the complement of the ga_unpack operation. An example is shown below * * g_src->pack(g_dest, g_mask, 1, n, icount) * g_mask: 1 0 0 0 0 0 1 0 1 0 0 1 0 0 1 1 0 * g_src: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 * g_dest: 1 7 9 12 15 16 * icount: 6 * * The calling array is the source array. * * This is a collective operation. * * @param[out] g_dest destination array * @param[in] g_mask mask array * @param[in] lo coordinate interval to pack * @param[in] hi coordinate interval to pack * @param[out] icount number of packed elements */ void pack(const GlobalArray *g_dest, const GlobalArray *g_mask, int lo, int hi, int *icount) const; /** * @copydoc GlobalArray::pack(const GlobalArray*,const GlobalArray*,int,int,int*)const */ void pack(const GlobalArray *g_dest, const GlobalArray *g_mask, int64_t lo, int64_t hi, int64_t *icount) const; /** * This subroutine enumerates the values of an array between elements lo and * hi starting with the value istart and incrementing each subsequent value by * inc. This operation is only applicable to 1-dimensional arrays. An example * of its use is shown below: * * call g_a->patch_enum(g_a, 1, n, 7, 2) * g_a: 7 9 11 13 15 17 19 21 23 ... * * This is a collective operation. * * @param[in] lo coordinate interval to enumerate * @param[in] hi coordinate interval to enumerate * @param[in] istart starting value of enumeration * @param[in] inc increment value */ void patchEnum(int lo, int hi, void *istart, void *inc); /** * @copydoc GlobalArray::patchEnum(int,int,int,int) */ void patchEnum(int64_t lo, int64_t hi, void *start, void *inc); /** * Same as nga_acc except the indices can extend beyond the array * boundary/dimensions in which case the library wraps them around. * * This is a one-sided and atomic operation. * * @param[in] lo [ndim] array of starting indices for array section * @param[in] hi [ndim] array of ending indices for array section * @param[in] buf pointer to the local buffer array * @param[in] ld [ndim-1] array specifying leading * dimensions/strides/extents for buffer array * @param[in] alpha double/DoubleComplex/long scale factor */ void periodicAcc(int lo[], int hi[], void* buf, int ld[], void* alpha) const; /** * @copydoc GlobalArray::periodicAcc(int[],int[],void*,int[],void*)const */ void periodicAcc(int64_t lo[], int64_t hi[], void* buf, int64_t ld[], void* alpha) const; /** * Same as nga_get except the indices can extend beyond the array * boundary/dimensions in which case the library wraps them around. * * This is a one-sided operation. * * @param[in] lo [ndim] array of starting indices for global array section * @param[in] hi [ndim] array of ending indices for global array section * @param[out] buf pointer to the local buffer array where the data goes * @param[in] ld [ndim-1] array specifying leading * dimensions/strides/extents for buffer array */ void periodicGet(int lo[], int hi[], void* buf, int ld[]) const; /** * @copydoc GlobalArray::periodicGet(int[],int[],void*,int[])const */ void periodicGet(int64_t lo[], int64_t hi[], void* buf, int64_t ld[]) const; /** * Same as nga_put except the indices can extend beyond the array * boundary/dimensions in which case the library wraps them around. * * This is a one-sided operation. * * @param[in] lo [ndim] array of starting indices for global array section * @param[in] hi [ndim] array of ending indices for global array section * @param[in] buf pointer to the local buffer array where the data goes * @param[in] ld [ndim-1] array specifying leading * dimensions/strides/extents for buffer array */ void periodicPut(int lo[], int hi[], void* buf, int ld[]) const; /** * @copydoc GlobalArray::periodicPut(int[],int[],void*,int[])const */ void periodicPut(int64_t lo[], int64_t hi[], void* buf, int64_t ld[]) const; /** * Prints an entire array to the standard output. * * This is a collective operation. */ void print() const ; /** * Prints the array distribution. * * This is a collective operation. */ void printDistribution() const ; /** * Prints the array distribution to a file. * * This is a collective operation. */ void printFile(FILE *file) const; /** * Prints a patch of g_a array to the standard output. If pretty has the * value 0 then output is printed in a dense fashion. If pretty has the * value 1 then output is formatted and rows/columns labeled. * This is a collective operation. * * @param[in] lo coordinates of the patch * @param[in] hi coordinates of the patch * @param[in] pretty formatting flag */ void printPatch(int* lo, int* hi, int pretty) const; /** * @copydoc GlobalArray::printPatch(int*,int*,int)const */ void printPatch(int64_t* lo, int64_t* hi, int pretty) const; /** * Based on the distribution of an array associated with handle g_a, * determines coordinates of the specified processor in the virtual * processor grid corresponding to the distribution of array g_a. The * numbering starts from 0. The values of -1 means that the processor * doesn't 'own' any section of array represented by g_a. * * This operation is local. * * @param[in] proc process id * @param[out] coord [ndim] coordinates in processor grid * */ void procTopology(int proc, int coord[]) const; /*void procTopology(int proc, int *prow, int *pcol);*/ /** * Copies data from local array buffer to the global array section . The * local array is assumed to be have the same number of dimensions as the * global array. Any detected inconsitencies/errors in input arguments are * fatal. This is a one-sided operation. * * @param[in] lo [ndim] array of starting indices for global array section * @param[in] hi [ndim] array of ending indices for global array section * @param[in] buf pointer to the local buffer array where the data is * @param[in] ld [ndim-1] array specifying leading * dimensions/strides/extents for buffer array * @param[in] buf buffer array */ void put(int lo[], int hi[], void *buf, int ld[]) const; /** * @copydoc GlobalArray::put(int[],int[],void*,int[])const */ void put(int64_t lo[], int64_t hi[], void *buf, int64_t ld[]) const; /** * Atomically read and increment an element in an integer array. * * *BEGIN CRITICAL SECTION* * * old_value = a(subscript) * * a(subscript) += inc * * *END CRITICAL SECTION* * * return old_value * * This is a one-sided and atomic operation. * * @param[in] subscript [ndim] subscript array for the referenced element * @param[in] inc how much to increment by * @return the incremented value */ long readInc(int subscript[], long inc) const; /** * @copydoc GlobalArray::readInc(int[],long)const */ long readInc(int64_t subscript[], long inc) const; /** * Releases access to a global array when the data was read only. * Your code should look like: * * @code * g_a->distribution(myproc, lo,hi); * g_a->access(lo, hi, &ptr, ld); * // * g_a->release(lo, hi); * @endcode * * @note see restrictions specified for ga_access. * * This operation is local. * * @param[in] lo [ndim] array of starting indices for array section * @param[in] hi [ndim] array of ending indices for array section */ void release(int lo[], int hi[]) const; /** * @copydoc GlobalArray::release(int[],int[])const */ void release(int64_t lo[], int64_t hi[]) const; /** * Releases access to the block of data specified by the integer * index when data was accessed as read only. This is only applicable to * block-cyclic data distributions created using the simple block-cyclic * distribution. * * This is a local operation. * * @param[in] index block index */ void releaseBlock(int index) const; /** * Releases access to the block of data specified by the subscript * array when data was accessed as read only. This is only applicable to * block-cyclic data distributions created using the SCALAPACK data * distribution. * * This is a local operation. * * @param[in] index [ndim] indices of block in array */ void releaseBlockGrid(int index[]) const; /** * Releases access to the block of locally held data for a block-cyclic * array, when data was accessed as read-only. This is a local operation. * * @param[in] proc process ID/rank */ void releaseBlockSegment(int proc) const; /** * Releases access to the data. It must be used if the data was accessed * for writing. NOTE: see restrictions specified for ga_access. * * This operation is local. * * @param[in] lo [ndim] array of starting indices for array section * @param[in] hi [ndim] array of ending indices for array section */ void releaseUpdate(int lo[], int hi[]) const; /** * @copydoc GlobalArray::releaseUpdate(int[],int[])const */ void releaseUpdate(int64_t lo[], int64_t hi[]) const; /** * Releases access to the block of data specified by the integer index when * data was accessed in read-write mode. This is only applicable to * block-cyclic data distributions created using the simple block-cyclic * distribution. * * This is a local operation. * * @param[in] index block index */ void releaseUpdateBlock(int index) const; /** * Releases access to the block of data specified by the subscript * array when data was accessed in read-write mode. This is only applicable * to block-cyclic data distributions created using the SCALAPACK data * distribution. * * This is a local operation. * * @param[in] index [ndim] indices of block in array */ void releaseUpdateBlockGrid(int index[]) const; /** * Releases access to the block of locally held data for a block-cyclic * array, when data was accessed in read-write mode. * * This is a local operation. * * @param[in] proc process ID/rank */ void releaseUpdateBlockSegment(int proc) const; /** * Releases access to a global array containing ghost cells when the data was * read only. * Your code should look like: * * @code * g_a->accessGhosts(dims, &ptr, ld) * // * g_a->releasGhosts(); * @endcode * * This operation is local. * */ void releaseGhosts() const; /** * Releases access to a global array containing ghost cells when the data was * accessed in read-write mode. * * This operation is local. * */ void releaseUpdateGhosts() const; /** * Releases access to a global array containing ghost cells when the data was * read only. * Your code should look like: * * @code * g_a->accessGhostElement(&ptr, subscript, ld) * // * g_a->releaseGhostElement(subscript); * @endcode * * This operation is local. * @param[in] indices of element * */ void releaseGhostElement(int subscript[]) const; /** * @copydoc GlobalArray::releaseGhostElement(int subscript[]) const */ void releaseGhostElement(int64_t subscript[]) const; /** * Releases access to a global array containing ghost cells when the data was * accessed in read-write mode. * * This operation is local. * @param[in] indices of element * */ void releaseUpdateGhostElement(int subscript[]) const; /** * @copydoc GlobalArray::releaseUpdateGhostElement(int subscript[]) const */ void releaseUpdateGhostElement(int64_t subscript[]) const; /** * Scales an array by the constant s. Note that the library is unable * to detect errors when the pointed value is of different type than * the array. * * This is a collective operation. * * @param[in] value pointer to the value of appropriate type */ void scale(void *value) const; /** * Scale an array by the factor 'val'. * * This is a collective operation. * * @param[in] lo patch of g_a * @param[in] hi patch of g_a * @param[in] val scale factor */ void scalePatch (int lo[], int hi[], void *val) const; /** * @copydoc GlobalArray::scalePatch(int[],int[],void*)const */ void scalePatch (int64_t lo[], int64_t hi[], void *val) const; /** * This operation will add successive elements in a source vector g_src * and put the results in a destination vector g_dest. The addition will * restart based on the values of the integer mask vector g_mask. The scan * is performed within the range specified by the integer values lo and * hi. Note that this operation can only be applied to 1-dimensional * arrays. The excl flag determines whether the sum starts with the value * in the source vector corresponding to the location of a 1 in the mask * vector (excl=0) or whether the first value is set equal to 0 * (excl=1). Some examples of this operation are given below. * * g_src->scanAdd(g_dest, g_mask, 1, n, 0); * g_mask: 1 0 0 0 0 0 1 0 1 0 0 1 0 0 1 1 0 * g_src: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 * g_dest: 1 3 6 10 16 21 7 15 9 19 30 12 25 39 15 16 33 * * g_src->scanAdd(g_dest, g_mask, 1, n, 1); * g_mask: 1 0 0 0 0 0 1 0 1 0 0 1 0 0 1 1 0 * g_src: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 * g_dest: 0 1 3 6 10 15 0 7 0 9 19 0 12 25 0 0 16 * * This is a collective operation. * * @param[out] g_dest handle for destination array * @param[in] g_mask handle for integer array representing mask * @param[in] lo low and high values of range on which operation * is performed * @param[in] hi low and high values of range on which operation * is performed * @param[in] excl value to signify if masked values are included in in add */ void scanAdd(const GlobalArray *g_dest, const GlobalArray *g_mask, int lo, int hi, int excl) const; /** * @copydoc GlobalArray::scanAdd(const GlobalArray*,const GlobalArray*,int,int,int)const */ void scanAdd(const GlobalArray *g_dest, const GlobalArray *g_mask, int64_t lo, int64_t hi, int excl) const; /** * This subroutine does a segmented scan-copy of values in the * source array g_src into a destination array g_dest with segments * defined by values in the integer mask array g_mask. The scan-copy * operation is only applied to the range between the lo and hi * indices. This operation is restriced to 1-dimensional arrays. The * resulting destination array will consist of segments of consecutive * elements with the same value. An example is shown below * * g_src->scanCopy(g_dest, g_mask, 1, n); * g_mask: 1 0 0 0 0 0 1 0 1 0 0 1 0 0 1 1 0 * g_src: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 * g_dest: 1 1 1 1 1 1 7 7 9 9 9 12 12 12 15 16 16 * * This is a collective operation. * * @param[out] g_dest handle for destination array * @param[in] g_mask handle for integer array representing mask * @param[in] lo low and high values of range on which operation * is performed * @param[in] hi low and high values of range on which operation * is performed */ void scanCopy(const GlobalArray *g_dest, const GlobalArray *g_mask, int lo, int hi) const; /** * @copydoc GlobalArray::scanCopy(const GlobalArray*,const GlobalArray*,int,int)const */ void scanCopy(const GlobalArray *g_dest, const GlobalArray *g_mask, int64_t lo, int64_t hi) const; /** * Scatters array elements into a global array. The contents of the input * arrays (v,subscrArray) are preserved, but their contents might be * (consistently) shuffled on return. * * @code * for(k=0; k<= n; k++) { * a[subsArray[k][0]][subsArray[k][1]][subsArray[k][2]]... = v[k]; * } * @endcode * * This is a one-sided operation. * * @param[in] n number of elements * @param[in] v [n] array containing values * @param[in] subsarray [n][ndim] array of subscripts for each element */ void scatter(void *v, int *subsarray[], int n) const; /** * @copydoc GlobalArray::scatter(void*,int*[],int)const */ void scatter(void *v, int64_t *subsarray[], int64_t n) const; /** * Adds element a local array to array elements into a global array after * multiplying by alpha. The contents of the input arrays (v,subscrArray) * are preserved, but their contents might be (consistently) shuffled on * return. * * @code * for(k=0; k<= n; k++) { * a[subsArray[k][0]][subsArray[k][1]][subsArray[k][2]]... = v[k]; * } * @endcode * * This is a one-sided operation. * * @param[in] n number of elements * @param[in] v [n] array containing values * @param[in] subsarray [n][ndim] array of subscripts for each element * @param[in] alpha scale factor */ void scatterAcc(void *v, int *subsarray[], int n, void *alpha) const; /** * @copydoc GlobalArray::scatterAcc(void*,int*[],int,void*)const */ void scatterAcc(void *v, int64_t *subsarray[], int64_t n, void *alpha) const; /** * Returns the value and index for an element that is selected by the * specified operator in a global array corresponding to g_a handle. * * This is a collective operation. * * @param[in] op operator {"min","max"} * @param[out] val address where value should be stored * @param[out] index [ndim] array index for the selected element */ void selectElem(char *op, void* val, int index[]) const; /** * @copydoc GlobalArray::selectElem(char*,void*,int[])const */ void selectElem(char *op, void* val, int64_t index[]) const; /** * This function can be used to assign a unique character * string name to a global array handle that was obtained * using the createHandle function. * * This is a collective operation. * * @param[in] name array name */ void setArrayName(char *name) const; /** * This subroutine is used to create a global array with a simple * block-cyclic data distribution. The array is broken up into blocks of * size dims and each block is numbered sequentially using a column major * indexing scheme. The blocks are then assigned in a simple round-robin * fashion to processors. This is illustrated in the figure below for an * array containing 25 blocks distributed on 4 processors. Blocks at the * edge of the array may be smaller than the block size specified in * dims. In the example below, blocks 4,9,14,19,20,21,22,23, and 24 might * be smaller thatn the remaining blocks. Most global array operations * are insensitive to whether or not a block-cyclic data distribution is * used, although performance may be slower in some cases if the global * array is using a block-cyclic data distribution. Individual data * blocks can be accessesed using the block-cyclic access functions. * * This is a collective operation. * * @param[in] dims array of block dimensions */ void setBlockCyclic(int dims[]) const; /** * @copydoc GlobalArray::setBlockCyclic(int[])const */ void setBlockCyclic(int64_t dims[]) const; /** * This subroutine is used to create a global array with a * SCALAPACK-type block cyclic data distribution. The user specifies * the dimensions of the processor grid in the array proc_grid. The * product of the processor grid dimensions must equal the number of * total number of processors and the number of dimensions in the * processor grid must be the same as the number of dimensions in the * global array. The data blocks are mapped onto the processor grid * in a cyclic manner along each of the processor grid axes. This is * illustrated below for an array consisting of 25 data blocks * disributed on 6 processors. The 6 processors are configured in a 3 * by 2 processor grid. Blocks at the edge of the array may be * smaller than the block size specified in dims. Most global array * operations are insensitive to whether or not a block-cyclic data * distribution is used, although performance may be slower in some * cases if the global array is using a block-cyclic data * distribution. Individual data blocks can be accessesed using the * block-cyclic access functions. * * This is a collective operation. * * @param[in] dims array of block dimensions * @param[in] proc_grid processor grid dimensions */ void setBlockCyclicProcGrid(int dims[], int proc_grid[]) const; /** * @copydoc GlobalArray::setBlockCyclicProcGrid(int[],int[])const */ void setBlockCyclicProcGrid(int64_t dims[], int64_t proc_grid[]) const; /** * This subroutine is used to create a global array with a data * distribution similar to the SCALAPACK distribution, with the * important difference that individual blocks are contiguous in memory. * This is distinct from the SCALAPACK distribution, where individual * blocks may be strided. The user specifies the dimensions of the * processor grid in the array proc_grid. The product of the processor * grid dimensions must equal the number of total number of processors * and the number of dimensions in the processor grid must be the same * as the number of dimensions in the global array. The data blocks are * mapped onto the processor grid in a cyclic manner along each of the * processor grid axes. This is* illustrated below for an array * consisting of 25 data blocks disributed on 6 processors. The 6 * processors are configured in a 3 by 2 processor grid. Blocks at the * edge of the array may be smaller than the block size specified in * dims. Most global array operations are insensitive to whether or not * a tiled data distribution is used, although performance may be slower * in some cases if the global array is using a tiled data distribution. * Individual data blocks can be accessesed using the block-cyclic access * functions. * * This is a collective operation. * * @param[in] dims array of block dimensions * @param[in] proc_grid processor grid dimensions */ void setTiledProcGrid(int dims[], int proc_grid[]) const; /** * @copydoc GlobalArray::setTiledProcGrid(int[],int[])const */ void setTiledProcGrid(int64_t dims[], int64_t proc_grid[]) const; /** * This function creates a tiled data distribution where individual tiles can * be different sizes. The tiles themselves are contiguous in memory and are * distributed in a block-cylic fashion based on a user-specified processor * grid. The interface is a combination of the setIrregDistr and the * setTiledProdGrid functions. The mapc array contains the firs index of each * block along each axis, the nblocks array contains the number of blocks * along each axis and the proc_grid array contains the dimensions of the * processor grid. The product of the processor grid dimensions must equal the * size of the processor group that the array is created on. * * This is a collective operation. * * @param[in] mapc array containing first index of each block along each * axis * @param[in] nblocks number of blocks along each axis * @param[in] proc_grid processor grid dimensions */ void setTiledIrregProcGrid(int mapc[], int nblocks[], int proc_grid[]) const; /** * @copydoc GlobalArray::setTiledProcGrid(int[],int[])const */ void setTiledIrregProcGrid(int64_t mapc[], int64_t nblocks[], int64_t proc_grid[]) const; /** * This function is used to set the chunk array for a global array handle * that was obtained using the createHandle function. The chunk array * is used to determine the minimum number of array elements assigned to * each processor along each coordinate direction. * * This is a collective operation. * * @param[in] chunk array of chunk widths */ void setChunk(int chunk[]) const; /** * @copydoc GlobalArray::setChunk(int[])const */ void setChunk(int64_t chunk[]) const; /** * This function can be used to set the array dimension, the coordinate * dimensions, and the data type assigned to a global array handle obtained * using the GA_Create_handle function. * * This is a collective operation. * * @param[in] ndim dimension of global array * @param[in] dims dimensions of global array * @param[in] type data type of global array */ void setData(int ndim, int dims[], int type) const; /** * @copydoc GlobalArray::setData(int,int[],int)const */ void setData(int ndim, int64_t dims[], int type) const; /** * This function can be used to set the ghost cell widths for a global * array handle that was obtained using the createHandle function. The * ghosts cells widths indicate how many ghost cells are used to pad the * locally held array data along each dimension. The padding can be set * independently for each coordinate dimension. * * This is a collective operation. * * @param[in] width [ndim] array of ghost cell widths */ void setGhosts(int width[]) const; /** * @copydoc GlobalArray::setGhosts(int[])const */ void setGhosts(int64_t width[]) const; /** * This function can be used to partition the array data among the * individual processors for a global array handle obtained using the * GA_Create_handle function. * * The distribution is specified as a Cartesian product of distributions * for each dimension. For example, the following figure demonstrates * distribution of a 2-dimensional array 8x10 on 6 (or more) * processors. nblock(2)={3, 2}, the size of mapc array is s=5 and array * mapc contains the following elements mapc={1, 3, 7, 1, 6}. The * distribution is nonuniform because, P1 and P4 get 20 elements each and * processors P0,P2,P3, and P5 only 10 elements each. * * The array width() is used to control the width of the ghost cell * boundary around the visible data on each processor. The local data of * the global array residing on each processor will have a layer width(n) * ghosts cells wide on either side of the visible data along the dimension * n. * * This is a collective operation. * * @param[in] mapc [s] starting index for each block; the size * s is the sum of all elements of the array nblock * @param[in] nblock [ndim] number of blocks that each dimension is * divided into */ void setIrregDistr(int mapc[], int nblock[]) const; /** * @copydoc GlobalArray::setIrregDistr(int mapc[], int nblock[]) const */ void setIrregDistr(int64_t mapc[], int64_t nblock[]) const; /** * This function can be used to set the processor configuration assigned to * a global array handle that was obtained using the * createHandle function. It can be used to create mirrored arrays by * using the mirrored array processor configuration in this function * call. It can also be used to create an array on a processor group by * using a processor group handle in this call. * * This is a collective operation. * * @param[in] pHandle processor group handle */ void setPGroup(PGroup *pHandle) const; /** * Set a property on the global array. The two properties currently supported * are "read_only" and "read_cache". These can both be set on allocated * arrays. "read_only" replicates data across nodes and distributes it within * nodes and "read_cache" stores recent data requests and uses them to satisfy * new requests if there are repeats. * * This is a collective operation. * * @param[in] propert property to assign to global array */ void setProperty(char *property); /** * This function is used to restrict the number of processors in a global * array that actually contain data. It can also be used to rearrange the * layout of data on a processor from the default distribution. Only the * processes listed in list[] will actually contain data, the remaining * processes will be able to see the data in the global array but they will * not contain any of the global array data locally. * * @param[in] list list of processors that should contain data * @param[in] nprocs number of processors in list * */ void setRestricted(int list[], int nprocs) const; /** * This function is used to restrict the number of processors in a global * array that actually contain data. Only the processors in the range * [lo_proc:hi_proc] (inclusive) will actually contain data, the remaining * processes will be able to see the data in the global array but they will * not contain any of the global array data locally. * * @param[in] lo_proc low end of processor range * @param[in] hi_proc high end of processor range */ void setRestrictedRange(int lo_proc, int hi_proc) const; /** * Specify the type of memory used to allocate the global array. This * functionality uses the SICM library to select between different types of * memory * * @param[in] device type of memory device to use */ void setMemoryDev(char *device); /** * Clears a property on the global array. * * This is a collective operation. */ void unsetProperty(); /** * Performs one of the matrix-matrix operations: * * C := alpha*op( A )*op( B ) + beta*C, * where op( X ) is one of * op( X ) = X or op( X ) = X', * alpha and beta are scalars, and A, B and C are matrices, with op( A ) * an m by k matrix, op( B ) a k by n matrix and C an m by n matrix. * On entry, transa specifies the form of op( A ) to be used in the * matrix multiplication as follows: * * ta = 'N' or 'n', op( A ) = A. * * ta = 'T' or 't', op( A ) = A'. * * This is a collective operation. * * @param[in] g_a handles to input arrays * @param[in] g_b handles to input arrays * @param[in] ta transpose operators * @param[in] tb transpose operators * @param[in] m number of rows of op(A) and of matrix C * @param[in] n number of columns of op(B) and of matrix C * @param[in] k number of columns of op(A) and rows of matrix op(B) * @param[in] alpha scale factors * @param[in] beta scale factors * */ void sgemm(char ta, char tb, int m, int n, int k, float alpha, const GlobalArray *g_a, const GlobalArray *g_b, float beta) const; /** * @copydoc GlobalArray::sgemm(char,char,int,int,int,float,const GlobalArray*,const GlobalArray*,float)const */ void sgemm(char ta, char tb, int64_t m, int64_t n, int64_t k, float alpha, const GlobalArray *g_a, const GlobalArray *g_b, float beta) const; /** * Solves a system of linear equations * A * X = B * It first will call the Cholesky factorization routine and, if * sucessfully, will solve the system with the Cholesky solver. If * Cholesky will be not be able to factorize A, then it will call the * LU factorization routine and will solve the system with forward/backward * substitution. On exit B will contain the solution X. * * This is a collective operation. * * @param[in] g_a coefficient matrix * * @return = 0 : Cholesky factoriztion was succesful\n * > 0 : the leading minor of this order * is not positive definite, Cholesky factorization * could not be completed and LU factoriztion was used */ int solve(const GlobalArray * g_a) const; /** * It computes the inverse of a double precision using the Cholesky * factorization of a NxN double precision symmetric positive definite * matrix A stored in the global array represented by g_a. On successful * exit, A will contain the inverse. * * This is a collective operation. * * @return = 0 : successful exit\n * > 0 : the leading minor of this order is not positive * definite and the factorization could not be completed\n * < 0 : it returns the index i of the (i,i) * element of the factor L/U that is zero and * the inverse could not be computed */ int spdInvert() const; /** * This operation is the same as "acc", except that the values * corresponding to dimension n in buf are accumulated to every skip[n] * values of the global array. * * This is a one-sided operation. * * @param[in] lo [ndim] array of starting indices for glob array section * @param[in] hi [ndim] array of ending indices for global array section * @param[in] skip [ndim] array of strides for each dimension * @param[in] buf pointer to local buffer array where data goes * @param[in] ld [ndim-1] rray specifying leading * dimensions/strides/extents for buffer array * @param[in] alpha double/DoublComplex/long scale factor */ void stridedAcc(int lo[], int hi[], int skip[], void*buf, int ld[], void *alpha) const; /** * @copydoc GlobalArray::stridedAcc(int[],int[],int[],void*,int[],void*)const */ void stridedAcc(int64_t lo[], int64_t hi[], int64_t skip[], void*buf, int64_t ld[], void *alpha) const; /** * This operation is the same as "get", except that the values * corresponding to dimension n in buf are accumulated to every skip[n] * values of the global array. * * This is a one-sided operation. * * @param[in] lo [ndim] array of starting indices for glob array section * @param[in] hi [ndim] array of ending indices for global array section * @param[in] skip [ndim] array of strides for each dimension * @param[out] buf pointer to local buffer array where data goes * @param[in] ld [ndim-1] array specifying leading * dimensions/strides/extents for buffer array */ void stridedGet(int lo[], int hi[], int skip[], void*buf, int ld[]) const; /** * @copydoc GlobalArray::stridedGet(int[],int[],int[],void*,int[])const */ void stridedGet(int64_t lo[], int64_t hi[], int64_t skip[], void*buf, int64_t ld[]) const; /** * This operation is the same as "put", except that the values * corresponding to dimension n in buf are accumulated to every skip[n] * values of the global array. * * This is a one-sided operation. * * @param[in] lo [ndim] array of starting indices for glob array section * @param[in] hi [ndim] array of ending indices for global array section * @param[in] skip [ndim] array of strides for each dimension * @param[in] buf pointer to local buffer array where data goes * @param[in] ld [ndim-1] array specifying leading * dimensions/strides/extents for buffer array */ void stridedPut(int lo[], int hi[], int skip[], void*buf, int ld[]) const; /** * "long" interface for stridedPut */ void stridedPut(int64_t lo[], int64_t hi[], int64_t skip[], void*buf, int64_t ld[]) const; /** * Prints info about allocated arrays. * * @param[in] verbose If true print distribution info */ void summarize(int verbose) const; /** * Symmmetrizes matrix A with handle A:=.5 * (A+A'). * * This is a collective operation */ void symmetrize() const; /** * This function returns the total number of blocks contained in a global * array with a block-cyclic data distribution. * * This is a local operation. * * @return number of blocks contained in this block-cyclic distribution */ int totalBlocks() const; /** * Transposes a matrix: B = A', where A and B are represented by * handles g_a and g_b [say, g_b.transpose(g_a);]. * * This is a collective operation. * * @param[in] g_a GlobalArray to transpose and assign to this GlobalArray */ void transpose(const GlobalArray * g_a) const; /** * The unpack subroutine is designed to expand the values in the source * vector g_src into a larger destination array g_dest based on the values * in an integer mask array g_mask. The values lo and hi denote the range * of elements that should be compressed and icount is a variable that on * output lists the number of values placed in the uncompressed array. This * operation is the complement of the pack operation. An example is * shown below * * g_src->unpack(g_dest, g_mask, 1, n, &icount); * g_src: 1 7 9 12 15 16 * g_mask: 1 0 0 0 0 0 1 0 1 0 0 1 0 0 1 1 0 * g_dest: 1 0 0 0 0 0 7 0 9 0 0 12 0 0 15 16 0 * icount: 6 * * This is a collective operation. * * @param[out] g_dest handle for destination array * @param[in] g_mask handle for integer array representing mask * @param[in] lo low value of range on which operation is performed * @param[in] hi high value of range on which operation is performed * @param[out] icount number of values in uncompressed array */ void unpack(GlobalArray *g_dest, GlobalArray *g_mask, int lo, int hi, int *icount) const; /** * @copydoc GlobalArray::unpack(GlobalArray*,GlobalArray*,int,int,int*)const */ void unpack(GlobalArray *g_dest, GlobalArray *g_mask, int64_t lo, int64_t hi, int64_t *icount) const; /** * This call updates the ghost cell regions on each processor with the * corresponding neighbor data from other processors. The operation assumes * that all data is wrapped around using periodic boundary data so that * ghost cell data that goes beyound an array boundary is wrapped around to * the other end of the array. The updateGhosts call contains two * sync calls before and after the actual update operation. For some * applications these calls may be unecessary, if so they can be removed * using the maskSync subroutine. * * This is a collective operation. */ void updateGhosts() const; /** * This operation is similar to the standard updateGhosts operation except * that it returns a non-blocking handle after initiating the call. Completion * of the operation can be guaranteed by call call the NbWait function on the * handle. Data in the local buffers is then ready for use. * * This is a collective operation. */ void updateGhostsNb(GANbhdl *nbhandle) const; /** * This function can be used to update the ghost cells along individual * directions. It is designed for algorithms that can overlap updates * with computation. The variable dimension indicates which coordinate * direction is to be updated (e.g. dimension = 1 would correspond to the * y axis in a two or three dimensional system), the variable idir can take * the values +/-1 and indicates whether the side that is to be updated lies * in the positive or negative direction, and cflag indicates whether or not * the corners on the side being updated are to be included in the update. * The following calls would be equivalent to a call to updateGhosts * for a 2-dimensional system: * * status = g_a->updateGhostDir(0,-1,1);\n * status = g_a->updateGhostDir(0,1,1);\n * status = g_a->updateGhostDir(1,-1,0);\n * status = g_a->updateGhostDir(1,1,0);\n * * The variable cflag is set equal to 1 (or non-zero) in the first two * calls so that the corner ghost cells are update, it is set equal to 0 in * the second two calls to avoid redundant updates of the corners. Note * that updating the ghosts cells using several independent calls to the * nga_update_ghost_dir functions is generally not as efficient as using * updateGhosts unless the individual calls can be effectively overlapped * with computation. * * This is a collective operation. * * @param[in] dimension array dimension that is to be updated * @param[in] idir direction of update (+/- 1) * @param[in] cflag flag (0/1) to include corners in update */ int updateGhostDir(int dimension, int idir, int cflag) const; /** * This operation is designed to extract ghost cell data from a global array * and copy it to a local array. If the request can be satisfied using * completely local data, then a local copy will be used. Otherwise, the * method calls periodicGet. The request can be satisfied locally if * lo is greater than or equal to the lower bound of data held on the * processor minus the ghost cell width and hi is less than or equal to the * upper bound of data held on the processor plus the ghost cell width. Cell * indices using the global address space should be used for lo and hi. These * may exceed the global array dimensions. * * @param[in] lo [ndim] array of starting indices for global array section * @param[in] hi [ndim] array of ending indices for global array section * @param[out] buf pointer to the local buffer array where the data goes * @param[in] ld [ndim-1] array specifying leading * dimensions/strides/extents for buffer array */ void getGhostBlock(int lo[], int hi[], void *buf, int ld[]) const; /** * @copydoc GlobalArray::getGhostBlock(int[],int[],void*,int[])const */ void getGhostBlock(int64_t lo[], int64_t hi[], void *buf, int64_t ld[]) const; /** * Computes element-wise dot product of the two arrays which must be of * the same types and same number of elements. * * This is a collective operation. * * @param[in] g_a array handle * * @return value = SUM_ij a(i,j)*b(i,j) */ DoubleComplex zdot(const GlobalArray * g_a) const; /** * Computes the element-wise dot product of the two (possibly transposed) * patches which must be of the same type and have the same number of * elements. * * @param[in] ta transpose flags * @param[in] alo g_a patch coordinates * @param[in] ahi g_a patch coordinates * @param[in] g_a global array * @param[in] tb transpose flags * @param[in] blo g_b patch coordinates * @param[in] bhi g_b patch coordinates * @return value */ DoubleComplex zdotPatch(char ta, int alo[], int ahi[], const GlobalArray * g_a, char tb, int blo[], int bhi[]) const; /** * @copydoc GlobalArray::zdotPatch(char,int[],int[],const GlobalArray*,char,int[],int[])const */ DoubleComplex zdotPatch(char ta, int64_t alo[], int64_t ahi[], const GlobalArray * g_a, char tb, int64_t blo[], int64_t bhi[]) const; /** * Sets value of all elements in the array to zero. * * This is a collective operation. */ void zero() const; /** * Set all the elements in the patch to zero. * This is a collective operation. * * @param[in] lo * @param[in] hi */ void zeroPatch (int lo[], int hi[]) const; /** * @copydoc GlobalArray::zeroPatch(int[],int[])const */ void zeroPatch (int64_t lo[], int64_t hi[]) const; /** * Performs one of the matrix-matrix operations: * C := alpha*op( A )*op( B ) + beta*C, * where op( X ) is one of * op( X ) = X or op( X ) = X', * alpha and beta are scalars, and A, B and C are matrices, with op( A ) * an m by k matrix, op( B ) a k by n matrix and C an m by n matrix. * On entry, transa specifies the form of op( A ) to be used in the * matrix multiplication as follows: * * ta = 'N' or 'n', op( A ) = A. * * ta = 'T' or 't', op( A ) = A'. * * * This is a collective operation. * * @param[in] g_a handles to input arrays * @param[in] g_b handles to input arrays * @param[in] ta transpose operators * @param[in] tb transpose operators * @param[in] m number of rows of op(A) and of matrix C * @param[in] n number of columns of op(B) and of matrix C * @param[in] k number of columns of op(A) and rows of matrix op(B) * @param[in] alpha scale factors * @param[in] beta scale factors */ void zgemm(char ta, char tb, int m, int n, int k, DoubleComplex alpha, const GlobalArray *g_a, const GlobalArray *g_b, DoubleComplex beta) const; /** * @copydoc GlobalArray::zgemm(char,char,int,int,int,DoubleComplex,const GlobalArray*,const GlobalArray*,DoubleComplex)const */ void zgemm(char ta, char tb, int64_t m, int64_t n, int64_t k, DoubleComplex alpha, const GlobalArray *g_a, const GlobalArray *g_b, DoubleComplex beta) const; /* New additional functionalities from Limin. */ /** * Take element-wise absolute value of the array. * * This is a collective operation. */ void absValue() const; /** * Take element-wise absolute value of the patch. * * This is a collective operation. * * @param[in] lo patch coordinates * @param[in] hi patch coordinates */ void absValuePatch(int *lo, int *hi) const; /** * @copydoc GlobalArray::absValuePatch(int*,int*)const */ void absValuePatch(int64_t *lo, int64_t *hi) const; /** * Add the constant pointed by alpha to each element of the array. * * This is a collective operation. * * @param[in] alpha double/complex/int/long/float */ void addConstant(void* alpha) const; /** * Add the constant pointed by alpha to each element of the patch. * * This is a collective operation. * * @param[in] lo g_a patch coordinates * @param[in] hi g_a patch coordinates * @param[in] alpha double/complex/int/long/float */ void addConstantPatch(int *lo, int *hi, void *alpha) const; /** * @copydoc GlobalArray::addConstantPatch(int*,int*,void*)const */ void addConstantPatch(int64_t *lo, int64_t *hi, void *alpha) const; /** * Take element-wise reciprocal of the array. * * This is a collective operation. */ void recip() const; /** * Take element-wise reciprocal of the patch. * * This is a collective operation. * * @param[in] lo patch coordinates * @param[in] hi patch coordinates */ void recipPatch(int *lo, int *hi) const; /** * @copydoc GlobalArray::recipPatch(int*,int*)const */ void recipPatch(int64_t *lo, int64_t *hi) const; /** * Computes the element-wise product of the two arrays * which must be of the same types and same number of * elements. For two-dimensional arrays, * * c(i, j) = a(i,j)*b(i,j) * * The result (c) may replace one of the input arrays (a/b). * This is a collective operation. * * @param[in] g_a GlobalArray * @param[in] g_b GlobalArray */ void elemMultiply(const GlobalArray * g_a, const GlobalArray * g_b) const; /** * Computes the element-wise product of the two patches * which must be of the same types and same number of * elements. For two-dimensional arrays, * * c(i, j) = a(i,j)*b(i,j) * * The result (c) may replace one of the input arrays (a/b). * * This is a collective operation. * * @param[in] g_a global array * @param[in] g_b global array * @param[in] alo g_a patch coordinates * @param[in] ahi g_a patch coordinates * @param[in] blo g_b patch coordinates * @param[in] bhi g_b patch coordinates * @param[in] clo g_c patch coordinates * @param[in] chi g_c patch coordinates */ void elemMultiplyPatch(const GlobalArray * g_a,int *alo,int *ahi, const GlobalArray * g_b,int *blo,int *bhi, int *clo,int *chi) const; /** * @copydoc GlobalArray::elemMultiplyPatch(const GlobalArray*,int*,int*,const GlobalArray*,int*,int*,int*,int*)const */ void elemMultiplyPatch(const GlobalArray * g_a,int64_t *alo,int64_t *ahi, const GlobalArray * g_b,int64_t *blo,int64_t *bhi, int64_t *clo,int64_t *chi) const; /** * Computes the element-wise quotient of the two arrays * which must be of the same types and same number of * elements. For two-dimensional arrays, * * c(i, j) = a(i,j)/b(i,j) * * The result (c) may replace one of the input arrays (a/b). If one of * the elements of array g_b is zero, the quotient for the element of g_c * will be set to GA_NEGATIVE_INFINITY. * * This is a collective operation. * * @param[in] g_a global array * @param[in] g_b global array */ void elemDivide(const GlobalArray * g_a, const GlobalArray * g_b) const; /** * Computes the element-wise quotient of the two patches * which must be of the same types and same number of * elements. For two-dimensional arrays, * * c(i, j) = a(i,j)/b(i,j) * * The result (c) may replace one of the input arrays (a/b). * * This is a collective operation. * * @param[in] g_a global array * @param[in] g_b global array * @param[in] alo g_a patch coordinates * @param[in] ahi g_a patch coordinates * @param[in] blo g_b patch coordinates * @param[in] bhi g_b patch coordinates * @param[in] clo g_c patch coordinates * @param[in] chi g_c patch coordinates */ void elemDividePatch(const GlobalArray * g_a,int *alo,int *ahi, const GlobalArray * g_b,int *blo,int *bhi, int *clo,int *chi) const; /** * @copydoc GlobalArray::elemDividePatch(const GlobalArray*,int*,int*,const GlobalArray*,int*,int*,int*,int*)const */ void elemDividePatch(const GlobalArray * g_a,int64_t *alo,int64_t *ahi, const GlobalArray * g_b,int64_t *blo,int64_t *bhi, int64_t *clo,int64_t *chi) const; /** * Computes the element-wise maximum of the two arrays * which must be of the same types and same number of * elements. For two dimensional arrays, * * c(i, j) = max{a(i,j), b(i,j)} * * The result (c) may replace one of the input arrays (a/b). * * This is a collective operation. * * @param[in] g_a global array * @param[in] g_b global array */ void elemMaximum(const GlobalArray * g_a, const GlobalArray * g_b) const; /** * Computes the element-wise maximum of the two patches * which must be of the same types and same number of * elements. For two-dimensional of noncomplex arrays, * * c(i, j) = max{a(i,j), b(i,j)} * * If the data type is complex, then * c(i, j).real = max{ |a(i,j)|, |b(i,j)|} while c(i,j).image = 0. * * The result (c) may replace one of the input arrays (a/b). * * This is a collective operation. * * @param[in] g_a global array * @param[in] g_b global array * @param[in] alo g_a patch coordinates * @param[in] ahi g_a patch coordinates * @param[in] blo g_b patch coordinates * @param[in] bhi g_b patch coordinates * @param[in] clo g_c patch coordinates * @param[in] chi g_c patch coordinates */ void elemMaximumPatch(const GlobalArray * g_a,int *alo,int *ahi, const GlobalArray * g_b,int *blo,int *bhi, int *clo,int *chi) const; /** * @copydoc GlobalArray::elemMaximumPatch(const GlobalArray*,int*,int*,const GlobalArray*,int*,int*,int*,int*)const */ void elemMaximumPatch(const GlobalArray * g_a,int64_t *alo,int64_t *ahi, const GlobalArray * g_b,int64_t *blo,int64_t *bhi, int64_t *clo,int64_t *chi) const; /** * Computes the element-wise minimum of the two arrays * which must be of the same types and same number of * elements. For two dimensional arrays, * * c(i, j) = min{a(i,j), b(i,j)} * * The result (c) may replace one of the input arrays (a/b). * * This is a collective operation. * * @param[in] g_a global array * @param[in] g_b global array */ void elemMinimum(const GlobalArray * g_a, const GlobalArray * g_b) const; /** * Computes the element-wise minimum of the two patches * which must be of the same types and same number of * elements. For two-dimensional of noncomplex arrays, * * c(i, j) = min{a(i,j), b(i,j)} * * If the data type is complex, then * c(i, j).real = min{ |a(i,j)|, |b(i,j)|} while c(i,j).image = 0. * * The result (c) may replace one of the input arrays (a/b). * * This is a collective operation. * * @param[in] g_a global array * @param[in] g_b global array * @param[in] alo g_a patch coordinates * @param[in] ahi g_a patch coordinates * @param[in] blo g_b patch coordinates * @param[in] bhi g_b patch coordinates * @param[in] clo g_c patch coordinates * @param[in] chi g_c patch coordinates */ void elemMinimumPatch(const GlobalArray * g_a,int *alo,int *ahi, const GlobalArray * g_b,int *blo,int *bhi, int *clo,int *chi) const; /** * @copydoc GlobalArray::elemMinimumPatch(const GlobalArray*,int*,int*,const GlobalArray*,int*,int*,int*,int*)const */ void elemMinimumPatch(const GlobalArray * g_a, int64_t *alo, int64_t *ahi, const GlobalArray * g_b, int64_t *blo, int64_t *bhi, int64_t *clo, int64_t *chi) const; /** * Calculates the largest multiple of a vector g_b that can be added * to this vector g_a while keeping each element of this vector * nonnegative. * * This is a collective operation. * * @param[in] g_b global array where g_b is the step direction. * @param[out] step the maximum step */ void stepMax(const GlobalArray * g_b, double *step) const; /** * @copydoc GlobalArray::stepMax(const GlobalArray*,double*)const * @param[in] alo g_a patch coordinates * @param[in] ahi g_a patch coordinates * @param[in] blo g_b patch coordinates * @param[in] bhi g_b patch coordinates */ void stepMaxPatch(int *alo, int *ahi, const GlobalArray * g_b, int *blo, int *bhi, double *step) const; /** * @copydoc GlobalArray::stepMaxPatch(int*,int*,const GlobalArray*,int*,int*,double*)const */ void stepMaxPatch(int64_t *alo, int64_t *ahi, const GlobalArray * g_b, int64_t *blo, int64_t *bhi, double *step) const; /** Matrix Operations */ /** * Adds this constant to the diagonal elements of the matrix. * * This is a collective operation. * * @param[in] c double/complex/int/long/float constant to add */ void shiftDiagonal(void *c) const; /** * Sets the diagonal elements of this matrix g_a with the elements of the * vector g_v. * * This is a collective operation. * * @param[in] g_v global array */ void setDiagonal(const GlobalArray * g_v) const; /** * Sets the diagonal elements of this matrix g_a with zeros. * * This is a collective operation. */ void zeroDiagonal() const; /** * Adds the elements of the vector g_v to the diagonal of this matrix g_a. * * This is a collective operation. * * @param[in] g_v global array */ void addDiagonal(const GlobalArray * g_v) const; /** * Inserts the diagonal elements of this matrix g_a into the vector g_v. * * This is a collective operation. * * @param[in] g_a global array */ void getDiagonal(const GlobalArray * g_a) const; /** * Scales the rows of this matrix g_a using the vector g_v. * * This is a collective operation. * * @param[in] g_v global array */ void scaleRows(const GlobalArray * g_v) const; /** * Scales the columns of this matrix g_a using the vector g_v. * * This is a collective operation. * * @param[in] g_v global array */ void scaleCols(const GlobalArray * g_v) const; /** * Computes the 1-norm of the matrix or vector g_a. * * This is a collective operation. * * @param[in] nm matrix/vector 1-norm value */ void norm1(double *nm) const; /** * Computes the 1-norm of the matrix or vector g_a. * * This is a collective operation. * * @param[in] nm - matrix/vector 1-norm value */ void normInfinity(double *nm) const; /** * Computes the componentwise Median of three arrays g_a, g_b, and g_c, and * stores the result in this array g_m. The result (m) may replace one of * the input arrays (a/b/c). * * This is a collective operation. * * @param[in] g_a global array * @param[in] g_b global array * @param[in] g_c global array */ void median(const GlobalArray * g_a, const GlobalArray * g_b, const GlobalArray * g_c) const; /** * Computes the componentwise Median of three patches g_a, g_b, and g_c, and * stores the result in this patch g_m. The result (m) may replace one of * the input patches (a/b/c). * * This is a collective operation. * * @param[in] g_a global array * @param[in] g_b global array * @param[in] g_c global array * @param[in] alo g_a patch coordinates * @param[in] ahi g_a patch coordinates * @param[in] blo g_b patch coordinates * @param[in] bhi g_b patch coordinates * @param[in] clo g_c patch coordinates * @param[in] chi g_c patch coordinates * @param[in] mlo g_m patch coordinates * @param[in] mhi g_m patch coordinates */ void medianPatch(const GlobalArray * g_a, int *alo, int *ahi, const GlobalArray * g_b, int *blo, int *bhi, const GlobalArray * g_c, int *clo, int *chi, int *mlo, int *mhi) const; /** * @copydoc GlobalArray::medianPatch(const GlobalArray*,int*,int*,const GlobalArray*,int*,int*,const GlobalArray*,int*,int*,int*,int*)const */ void medianPatch(const GlobalArray * g_a, int64_t *alo, int64_t *ahi, const GlobalArray * g_b, int64_t *blo, int64_t *bhi, const GlobalArray * g_c, int64_t *clo, int64_t *chi, int64_t *mlo, int64_t *mhi) const; GlobalArray& operator=(const GlobalArray &g_a); int operator==(const GlobalArray &g_a) const; int operator!=(const GlobalArray &g_a) const; private: int mHandle; /**<< g_a handle */ }; } #endif /* _GLOBALARRAY_H */ ga-5.9.2/ga++/src/PGroup.cc000066400000000000000000000040751500715745200151610ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "ga++.h" GA::PGroup* GA::PGroup::pgMirror = NULL; GA::PGroup* GA::PGroup::pgDefault = NULL; GA::PGroup* GA::PGroup::pgWorld = NULL; /** * Private -- never called. */ GA::PGroup::PGroup(void) { } /** * Constructors and Destructor of PGroup */ GA::PGroup::PGroup(int *plist, int size) { mPHandle = GA_Pgroup_create(plist, size); } GA::PGroup::~PGroup() { GA_Pgroup_destroy(mPHandle); } /** * Pgroup Methods */ GA::PGroup* GA::PGroup::getDefault() { if(pgDefault == NULL) { pgDefault = new PGroup(); pgDefault->mPHandle = GA_Pgroup_get_default(); } return pgDefault; } GA::PGroup* GA::PGroup::getMirror() { if(pgMirror == NULL) { pgMirror = new PGroup(); pgMirror->mPHandle = GA_Pgroup_get_mirror(); } return pgMirror; } GA::PGroup* GA::PGroup::getWorld() { if(pgWorld == NULL) { pgWorld = new PGroup(); pgWorld->mPHandle = GA_Pgroup_get_world(); } return pgWorld; } GA::PGroup GA::PGroup::duplicate() { GA::PGroup ret; ret.mPHandle = GA_Pgroup_duplicate(mPHandle); return ret; } GA::PGroup GA::PGroup::self() { GA::PGroup ret; ret.mPHandle = GA_Pgroup_self(); return ret; } void GA::PGroup::setDefault(GA::PGroup* p_handle) { pgDefault->mPHandle = p_handle->mPHandle; } void GA::PGroup::brdcst(void* buf, int lenbuf, int root) { GA_Pgroup_brdcst(mPHandle, buf, lenbuf, root); } void GA::PGroup::gop(double *buf, int n, char* op) { GA_Pgroup_dgop(mPHandle, buf, n, op); } void GA::PGroup::gop(long *buf, int n, char* op) { GA_Pgroup_lgop(mPHandle, buf, n, op); } void GA::PGroup::gop(int *buf, int n, char* op) { GA_Pgroup_igop(mPHandle, buf, n, op); } void GA::PGroup::gop(float *buf, int n, char* op) { GA_Pgroup_fgop(mPHandle, buf, n, op); } int GA::PGroup::nodeid() { return GA_Pgroup_nodeid(mPHandle); } int GA::PGroup::nodes() { return GA_Pgroup_nnodes(mPHandle); } void GA::PGroup::sync() { GA_Pgroup_sync(mPHandle); } ga-5.9.2/ga++/src/PGroup.h000066400000000000000000000173051500715745200150230ustar00rootroot00000000000000#ifndef _PGROUP_H #define _PGROUP_H namespace GA { /** * PGroup class description TODO. */ class PGroup { public: /** * This constructor creates a processor group. * * It must be invoked by all processors in the current default processor * group. The list of processors use the indexing scheme of the default * processor group. If the default processor group is the world group, then * these indices are the usual processor indices. This function returns a * process group handler that can be used to reference this group by other * functions. * * This is a collective operation on the default processor group. * * @param[in] plist [size] list of processor IDs in group * @param[in] size number of processors in group * */ PGroup(int *plist, int size); /** * PGroup destructor. */ ~PGroup(); /* access the data */ /** @return the array handle */ int handle() const { return mPHandle; } /* Process Group Operations - static */ /** * This function will return a handle to the default processor group, * which can then be used to create a global array using one of the * create_*_config or setPGroup calls. * * This is a local operation. * * @return the new PGroup */ static PGroup* getDefault(); /** * This function will return a handle to the mirrored processor group, * which can then be used to create a global array using one of the * GA create_*_config or setPgroup calls. * * This is a local operation. * * @return the new PGroup */ static PGroup* getMirror(); /** * This function will return a handle to the world processor group, * which can then be used to create a global array using one of the * GA create_*_config or GA_Set_pgroup calls. * * This is a local operation. * * @return the new PGroup */ static PGroup* getWorld(); /** * Return a new processor group that is a duplicate of the existing processor * group * * This is collective operation * * @return the new PGroup */ PGroup duplicate(); /** * Return a new processor group that contains only the calling process * * This is local operation * * @return the new PGroup */ static PGroup self(); /** * This function can be used to reset the default processor group on a * collection of processors. * * All processors in the group referenced by p_handle must make a call to * this function. Any standard global array call that is made after * resetting the default processor group will be restricted to processors in * that group. Global arrays that are created after resetting the default * processor group will only be defined on that group and global operations * such as sync or igop will be restricted to processors in that group. The * pgroupSetDefault call can be used to rapidly convert large applications, * written with GA, into routines that run on processor groups. * * The default processor group can be overridden by using GA calls that * require an explicit group handle as one of the arguments. * * This is a collective operation on the group represented by the handle * p_handle. * * @param[in] p_handle processor group handle */ static void setDefault(PGroup* p_handle); /* Process Group Operations */ /** * Broadcast data from processor specified by root to all other * processors in this processor group. * * The length of the message in bytes is specified by lenbuf. The initial and * broadcasted data can be found in the buffer specified by the pointer * buf. * * This is a collective operation on the processor group. * * @param[in,out] buf pointer to buffer containing data * @param[in] lenbuf length of data (in bytes) * @param[in] root processor sending message */ void brdcst(void* buf, int lenbuf, int root); /** * The pgroup gop 'sums' all elements in buf[n] across all processors in the * group using the commutative operation specified by the character string op. * * buf[n] is a double precision array present on each processor in the * processor group. The result is broadcast to all processor in this group. * Allowed strings are "+", "*", "max", "min", "absmax", "absmin". The use of * lowerecase for operators is necessary. * * This is a collective operation on the processor group. * * @param[in,out] buf buffer containing data * @param[in] n number of elements in x * @param[in] op operation to be performed */ void gop(double *buf, int n, char* op); /** * The gop 'sums' all elements in buf[n] across all processors in the group * using the commutative operation specified by the character string op. * * buf[n] is an integer(int) array present on each processor in the * processor group. The result is broadcast to all processor in this group. * Allowed strings are "+", "*", "max", "min", "absmax", "absmin". The use of * lowerecase for operators is necessary. * * This is a collective operation on the processor group. * * @param[in,out] buf buffer containing data * @param[in] n number of elements in x * @param[in] op operation to be performed */ void gop(int *buf, int n, char* op); /** * The gop 'sums' all elements in buf[n] across all processors in the group * using the commutative operation specified by the character string op. * * buf[n] is an integer(long) array present on each processor in the * processor group. The result is broadcast to all processor in this group. * Allowed strings are "+", "*", "max", "min", "absmax", "absmin". The use of * lowerecase for operators is necessary. * * This is a collective operation on the processor group. * * @param[in,out] buf buffer containing data * @param[in] n number of elements in x * @param[in] op operation to be performed */ void gop(long *buf, int n, char* op); /** * The gop 'sums' all elements in buf[n] across all processors in the group * using the commutative operation specified by the character string op. * * buf[n] is a float array present on each processor in the * processor group. The result is broadcast to all processor in this group. * Allowed strings are "+", "*", "max", "min", "absmax", "absmin". The use of * lowerecase for operators is necessary. * * This is a collective operation on the processor group. * * @param[in,out] buf buffer containing data * @param[in] n number of elements in x * @param[in] op operation to be performed */ void gop(float *buf, int n, char* op); /** * This function returns the relative index of the processor in the * processor group specified by p_handle. * * This index will generally differ from the absolute processor index * returned by GA_Nodeid if the processor group is not the world group. * * This is a local operation. * * @return relative index of the processor in the group given by p_handle */ int nodeid(); /** * This function returns the number of processors contained in the * group specified by p_handle. * * This is a local local operation. * * @return number of processors contained in the group given by p_handle */ int nodes(); /** * This operation executes a synchronization group across the * processors in the processor group specified by p_handle. * * Nodes outside this group are unaffected. * * This is a collective operation on the processor group. */ void sync(); private: int mPHandle; /*<< process group handle */ static PGroup *pgMirror; static PGroup *pgDefault; static PGroup *pgWorld; PGroup(void); }; } #endif /* _PGROUP_H */ ga-5.9.2/ga++/src/ga++.h000066400000000000000000000033311500715745200143160ustar00rootroot00000000000000/** * @file ga++.h * * @author Manoj Kumar Krishnan, PNNL. * @author Jeff Daily, PNNL. */ /** * @mainpage * * @author Manoj Kumar Krishnan, PNNL. * @author Jeff Daily, PNNL. * * The GA Toolkit * * The Global Arrays (GA) toolkit provides an efficient and portable * “shared-memory†programming interface for distributed-memory computers. * Each process in a MIMD parallel program can asynchronously access logical * blocks of physically distributed dense multi-dimensional arrays, without * need for explicit cooperation by other processes. Unlike other * shared-memory environments, the GA model exposes to the programmer the * non-uniform memory access (NUMA) characteristics of the high performance * computers and acknowledges that access to a remote portion of the shared * data is slower than to the local portion. The locality information for the * shared data is available, and a direct access to the local portions of * shared data is provided. * * Global Arrays have been designed to complement rather than substitute for * the message-passing programming model. The programmer is free to use both * the shared-memory and message-passing paradigms in the same program, and to * take advantage of existing message-passing software libraries. Global * Arrays are compatible with the Message Passing Interface (MPI). * * The Global Arrays toolkit has been in the public domain since 1994. It has * been actively supported and employed in several large codes since then. */ #ifndef _GAPP_H #define _GAPP_H #include "ga.h" #include "macdecls.h" #define GANbhdl ga_nbhdl_t #include "init_term.h" #include "services.h" #include "PGroup.h" #include "GlobalArray.h" #include "GAServices.h" #endif // _GAPP_H ga-5.9.2/ga++/src/init_term.cc000066400000000000000000000016121500715745200157310ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "mp3.h" #include "ga++.h" void GA::Initialize(int argc, char *argv[], size_t limit) { MP_INIT(argc, argv); // GA Initialization if(limit == 0) GA_Initialize(); else GA_Initialize_ltd(limit); } void GA::Initialize(int argc, char *argv[], unsigned long heapSize, unsigned long stackSize, int type, size_t limit) { MP_INIT(argc, argv); // GA Initialization if(limit == 0) GA_Initialize(); else GA_Initialize_ltd(limit); //if(GA_Uses_ma()) { int nProcs = GA_Nnodes(); // Initialize memory allocator heapSize /= ((unsigned long) nProcs); stackSize /= ((unsigned long) nProcs); if(!MA_init(type, stackSize, heapSize)) GA_Error((char *)"MA_init failed",stackSize+heapSize); // } } void GA::Terminate() { /* Terminate GA */ GA_Terminate(); MP_FINALIZE(); } ga-5.9.2/ga++/src/init_term.h000066400000000000000000000040431500715745200155740ustar00rootroot00000000000000/** * @file init_term.h * * Ga Initialize and Terminate calls. */ #ifndef _INITTERM_H #define _INITTERM_H namespace GA { /** * Initialize Global Arrays. * Allocate and initialize internal data structures in Global Arrays. * The limit is per process: it is the amount of memory that the given * processor can contribute to collective allocation of global arrays. * It does not include temporary storage that GA might be allocating (and * releasing) during execution of a particular operation. * limit < 0 means "allow unlimited memory usage" in which case this * operation is equivalent to GA_initialize. This is a collective operation. * @param argc,argv - command line argument lists. * @param limit - amount of memory in bytes per process [input] */ void Initialize(int argc, char *argv[], size_t limit = 0); /** *Initialize Global Arrays. * Allocate and initialize internal data structures in Global Arrays. * The limit is per process: it is the amount of memory that the given * processor can contribute to collective allocation of global arrays. * It does not include temporary storage that GA might be allocating (and * releasing) during execution of a particular operation. * limit < 0 means "allow unlimited memory usage" in which case this * operation is equivalent to GA_initialize. This is a collective operation. * @param argc,argv - command line argument lists. * @param limit - amount of memory in bytes per process [input] * @param heapSize, stackSize - all of the dynamically allocated local memory * @param type - data type. * in GA comes from its companion library, the Memory Allocator (MA) library. * MA allocates and manages local memory using stack and heap disciplines. * [refer section 3.2 of GA USer manual for more info] */ void Initialize(int argc, char *argv[], unsigned long heapSize, unsigned long stackSize, int type, size_t limit = 0); /** * Delete all active arrays and destroy internal data structures. * This is a collective operation. */ void Terminate(); } #endif /* _INITTERM_H */ ga-5.9.2/ga++/src/overload.cc000066400000000000000000000046061500715745200155600ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "ga++.h" #ifdef FALSE #undef FALSE #endif #ifdef TRUE #undef TRUE #endif #define FALSE 0 #define TRUE 1 /** * More operator overloading stuff (a lot!!) to come. */ GA::GlobalArray& GA::GlobalArray::operator=(const GA::GlobalArray &g_a) { if(this != &g_a) { GA_Destroy(mHandle); mHandle = GA_Duplicate(g_a.mHandle, g_a.inquireName()); if(!mHandle) GA_Error((char *)" GA creation failed",0); GA_Copy(g_a.mHandle, mHandle); } return *this; } int GA::GlobalArray::operator==(const GA::GlobalArray &g_a) const { long isEqual = TRUE; int i, type1, type2, ndim1, ndim2, dims1[GA_MAX_DIM], dims2[GA_MAX_DIM]; int alo[GA_MAX_DIM], ahi[GA_MAX_DIM], blo[GA_MAX_DIM], bhi[GA_MAX_DIM]; NGA_Inquire(mHandle, &type1, &ndim1, dims1); NGA_Inquire(g_a.mHandle, &type2, &ndim2, dims2); if(type1 != type2) isEqual = FALSE; // check type if(GA_Compare_distr(mHandle, g_a.mHandle)) isEqual = FALSE; NGA_Distribution(mHandle, GA_Nodeid(), alo, ahi); NGA_Distribution(g_a.mHandle, GA_Nodeid(), blo, bhi); if(ahi[0] != bhi[0]) isEqual = FALSE; // check process owns data? if(ahi[0] >= 0) { // true => process owns data void *ptr1 = NULL, *ptr2 = NULL; int ld1[GA_MAX_DIM]; int ld2[GA_MAX_DIM]; int num = 0; NGA_Access(mHandle, alo, ahi, &ptr1, ld1); NGA_Access(g_a.mHandle, blo, bhi, &ptr2, ld2); // number of elements I own. for(i=0; i * 5 5 * P0 P3 2 * P1 P4 4 * P2 P5 2 * * * This is a collective operation. * * @param[in] arrayname a unique character string * @param[in] type MA data type (MT_F_DBL,MT_F_INT,MT_F_DCPL) * @param[in] ndim number of array dimensions * @param[in] dims array of dimension values * @param[in] block [ndim] no. of blocks each dimension is divided into * @param[in] maps [s] starting index for for each block; * the size s is a sum all elements of nblock array * * @return pointer to GlobalArray object created; NULL if it fails */ GlobalArray * createGA(int type, int ndim, int dims[], char *arrayname, int block[], int maps[]); /** * Creates a new array by applying all the properties of another existing * array. * * This is a collective operation. * * @param[in] arrayname a character string * @param[in] g_b integer handle for reference array * * @return pointer to GlobalArray object created; NULL if it fails */ GlobalArray * createGA(const GlobalArray *g_b, char *arrayname); /** * Creates a new array by applying all the properties of another existing * array. * * This is a collective operation. * * @param[in] g_b integer handle for reference array * * @return pointer to GlobalArray object created; NULL if it fails */ GlobalArray * createGA(const GlobalArray &g_b); /** * Creates a 10x10 global array of type "double"(default). * * This is a collective operation. * * @return pointer to GlobalArray object created; NULL if it fails */ GlobalArray * createGA(); /** * Creates an ndim-dimensional array with a layer of ghost cells around * the visible data on each processor using the regular distribution * model and returns an integer handle representing the array. * The array can be distributed evenly or not evenly. The control over * the distribution is accomplished by specifying chunk (block) size for * all or some of the array dimensions. For example, for a 2-dimensional * array, setting chunk(1)=dim(1) gives distribution by vertical strips * (chunk(1)*dims(1)); setting chunk(2)=dim(2) gives distribution by * horizontal strips (chunk(2)*dims(2)). Actual chunks will be modified * so that they are at least the size of the minimum and each process * has either zero or one chunk. Specifying chunk(i) as <1 will cause * that dimension (i-th) to be distributed evenly. The width of the * ghost cell layer in each dimension is specified using the array * width(). The local data of the global array residing on each * processor will have a layer width[n] ghosts cells wide on either * side of the visible data along the dimension n. * * This is a collective operation. * * @param[in] array_name a unique character string * @param[in] type data type (MT_DBL,MT_INT,MT_DCPL) * @param[in] ndim number of array dimensions * @param[in] dims [ndim] array of dimensions * @param[in] width [ndim] array of ghost cell widths * @param[in] chunk [ndim] array of chunks, each element specifies * minimum size that given dimensions should be * chunked up into * * @returns pointer to GlobalArray object created; NULL if it fails */ GlobalArray * createGA_Ghosts(int type, int ndim, int dims[], int width[], char *array_name, int chunk[]); /** * Creates an array with ghost cells by following the user-specified * distribution and returns integer handle representing the array. * The distribution is specified as a Cartesian product of distributions * for each dimension. For example, the following figure demonstrates * distribution of a 2-dimensional array 8x10 on 6 (or more) processors. * nblock(2)={3,2}, the size of map array is s=5 and array map contains * the following elements map={1,3,7, 1, 6}. The distribution is * nonuniform because, P1 and P4 get 20 elements each and processors * P0,P2,P3, and P5 only 10 elements each. * * * * * * *
5 5
P0 P3 2
P1 P4 4
P2 P5 2
* * The array width[] is used to control the width of the ghost cell * boundary around the visible data on each processor. The local data * of the global array residing on each processor will have a layer * width[n] ghosts cells wide on either side of the visible data along * the dimension n. * * This is a collective operation. * * @param[in] array_name a unique character string * @param[in] type data type (MT_DBL,MT_INT,MT_DCPL) * @param[in] ndim number of array dimensions * @param[in] dims [ndim] array of dimensions * @param[in] width [ndim] array of ghost cell widths * @param[in] nblock [ndim] no. of blocks each dimension is divided into * @param[in] map [s] starting index for for each block; * the size s is a sum of all elements of nblock array * * @return pointer to GlobalArray object created; NULL if it fails */ GlobalArray * createGA_Ghosts(int type, int ndim, int dims[], int width[], char *array_name, int map[], int nblock[]); /** * Broadcast from process root to all other processes a message of * length lenbuf. This is operation is provided only for convenience * purposes: it is available regardless of the message-passing library * that GA is running with. * * This is a collective operation. * * @param[in] lenbuf length of buffer * @param[in,out] buf [lenbuf] data * @param[in] root root process */ void brdcst(void *buf, int lenbuf, int root); /** * Returns the current value of the internal debug flag. * * This is a local operation. * * @return 0 if the debug flag is false, 1 if it is true. */ int getDebug(); /** * This functions returns the total number of nodes that the program is * running on. * * On SMP architectures, this will be less than or equal to the total * number of processors. * * This is a local operation. * * @return the number of nodes the program is running on */ int clusterNnodes(); /** * This function returns the node ID of the process. * * On SMP architectures with more than one processor per node, several * processes may return the same node id. * * This is a local operation. * * @return the node ID of the process */ int clusterNodeid(); /** * This function returns the cluster node ID of the specified process. * * On SMP architectures with more than one processor per node, several * processes may return the same node id. * * This is a local operation. * * @return the cluster node ID of the specified process */ int clusterProcNodeid(int iproc); /** * This function returns the number of processors available on node inode. * * This is a local operation. * * @param[in] inode * * @return the number of processors available on the given node */ int clusterNprocs(int inode); /** * This function returns the processor id associated with node inode and * the local processor id iproc. * * If node inode has N processors, then the value of iproc lies between * 0 and N-1. * * This is a local operation. * * @param[in] inode * @param[in] iproc * * @return the processor ID associated with the given node and local processor * ID */ int clusterProcid(int inode, int iproc); /** * Creates a set containing the number of mutexes. * * Mutex is a simple synchronization object used to protect Critical * Sections. Only one set of mutexes can exist at a time. Array of mutexes * can be created and destroyed as many times as needed. * Mutexes are numbered: 0, ..., number -1. * * This is a collective operation. * * @param[in] number of mutexes in mutex array * * @return 0 if the opereation succeeded or 1 when failed. */ int createMutexes(int number); /** * Remove a user defined data type from GA * * @param[in] type - user defined data type * * @return 0 is operation is successful * -2 if type not registered * -1 if type reserved */ int deregisterType(int type); /** * Destroys the set of mutexes created with ga_create_mutexes. * * This is a collective operation. * * @return 0 if the operation succeeded or 1 when failed. */ int destroyMutexes(); /** * Double Global OPeration. * * X(1:N) is a vector present on each process. DGOP 'sums' elements of * X accross all nodes using the commutative operator OP. The result is * broadcast to all nodes. Supported operations include '+', '*', 'max', * 'min', 'absmax', 'absmin'. The use of lowerecase for operators is * necessary. This is operation is provided only for convenience purposes: * it is available regardless of the message-passing library that GA is * running with. * * This is a collective operation. * * @param[in] n number of elements * @param[in,out] x [n] array of elements * @param[in] op operator */ void dgop(double x[], int n, char *op); /** * Creates a new array by applying all the properties of another existing * array. * * This is a collective operation. * * @param[in] array_name a character string * @param[in] g_a integer handle for reference array * * @return array handle; a non-zero array handle means the call was succesful. */ int duplicate(int g_a, char* array_name); /** * To be called in case of an error. * * Print an error message and an integer value that represents error code. * Releases some system resources. * This is the required way of aborting the program execution. * * This operation is local. * * @param[in] message string to print * @param[in] code code to print */ void error(const char *message, int code); /** * Blocks the calling process until all the data transfers corresponding to * GA operations called after ga_init_fence complete. * * For example, since ga_put might return before the data reaches the final * destination, ga_init_fence and ga_fence allow process to wait until the * data tranfer is fully completed: * * @code * ga_init_fence(); * ga_put(g_a, ...); * ga_fence(); * @endcode * * ga_fence must be called after ga_init_fence. A barrier, ga_sync, assures * completion of all data transfers and implicitly cancels all outstanding * ga_init_fence calls. ga_init_fence and ga_fence must be used in pairs, * multiple calls to ga_fence require the same number of corresponding * ga_init_fence calls. ga_init_fence/ga_fence pairs can be nested. * * ga_fence works for multiple GA operations. For example: * * @code * ga_init_fence(); * ga_put(g_a, ...); * ga_scatter(g_a, ...); * ga_put(g_b, ...); * ga_fence(); * @endcode * * The calling process will be blocked until data movements initiated by * two calls to ga_put and one ga_scatter complete. */ void fence(); /** * Integer Global OPeration. * * The integer version of ga_dgop described above, also include the bitwise OR * operation. This is operation is provided only for convenience purposes: it * is available regardless of the message-passing library that GA is running * with. * * This is a collective operation. * * @param[in] n number of elements * @param[in,out] x [n] array of elements * @param[in] op operator */ void gop(int x[], int n, char *op); /** * Long Global OPeration. * * X(1:N) is a vector present on each process. LGOP 'sums' elements of * X accross all nodes using the commutative operator OP. The result is * broadcast to all nodes. Supported operations include '+', '*', 'max', * 'min', 'absmax', 'absmin'. The use of lowerecase for operators is * necessary. This is operation is provided only for convenience purposes: * it is available regardless of the message-passing library that GA is * running with. * * This is a collective operation. * * @param[in] n number of elements * @param[in,out] x [n] array of elements * @param[in] op operator */ void gop(long x[], int n, char *op); /** * Float Global OPeration. * * X(1:N) is a vector present on each process. FGOP 'sums' elements of * X accross all nodes using the commutative operator OP. The result is * broadcast to all nodes. Supported operations include '+', '*', 'max', * 'min', 'absmax', 'absmin'. The use of lowerecase for operators is * necessary. This is operation is provided only for convenience purposes: * it is available regardless of the message-passing library that GA is * running with. * * This is a collective operation. * * @param[in] n number of elements * @param[in,out] x [n] array of elements * @param[in] op operator */ void gop(float x[], int n, char *op); /** * Double Global OPeration. * * X(1:N) is a vector present on each process. DGOP 'sums' elements of * X accross all nodes using the commutative operator OP. The result is * broadcast to all nodes. Supported operations include '+', '*', 'max', * 'min', 'absmax', 'absmin'. The use of lowerecase for operators is * necessary. This is operation is provided only for convenience purposes: * it is available regardless of the message-passing library that GA is * running with. * * This is a collective operation. * * @param[in] n number of elements * @param[in,out] x [n] array of elements * @param[in] op operator */ void gop(double x[], int n, char *op); /** * Integer Global OPeration. * * The integer (more precisely long) version of ga_dgop described above, * also include the bitwise OR operation. * This is operation is provided only for convenience purposes: it is * available regardless of the message-passing library that GA is running * with. * * This is a collective operation. * * @param[in] n number of elements * @param[in,out] x [n] array of elements * @param[in] op operator */ void igop(int x[], int n, char *op); /** * Initializes tracing of completion status of data movement operations. * * This operation is local. */ void initFence(); /** * Returns amount of memory (in bytes) used in the allocated global * arrays on the calling processor. * * This operation is local. * * @return amount of memory (in bytes) used in the allocated global arrays on * the calling processor */ size_t inquireMemory(); /** * Long Global OPeration. * * X(1:N) is a vector present on each process. LGOP 'sums' elements of * X accross all nodes using the commutative operator OP. The result is * broadcast to all nodes. Supported operations include '+', '*', 'max', * 'min', 'absmax', 'absmin'. The use of lowerecase for operators is * necessary. This is operation is provided only for convenience purposes: * it is available regardless of the message-passing library that GA is * running with. * * This is a collective operation. * * @param[in] n number of elements * @param[in,out] x [n] array of elements * @param[in] op operator */ void lgop(long x[], int n, char *op); /** * Locks a mutex object identified by the mutex number. It is a fatal * error for a process to attempt to lock a mutex which was already * locked by this process. * * @param[in] mutex object id */ void lock(int mutex); /** * Mask the intrinsic sync operations during collective calls. * * GA Collective calls has Sync calls at the begining and ending of * of the call. Sometimes there may be some redundacy in sync calls, which * can be avoided by masking the sync operations. * * Setting the parameters as zero will mask (disable) the call. Any non-zero * value will enable the call. Initially these params are set to non-zero * value. * * @param[in] first masks the sync at the begining of the collective call. * @param[in] last masks the sync at the end of the collective call. */ void maskSync(int first, int last); /** * If GA_uses_ma returns true, then GA_Memory_avail returns the * lesser of the amount available under the GA limit and the amount * available from MA (according to ma_inquire_avail operation). * If no GA limit has been set, it returns what MA says is available. * If ( ! GA_Uses_ma() && ! GA_Memory_limited() ) returns < 0, indicating * that the bound on currently available memory cannot be determined. * * This operation is local. * * @return amount of memory (in bytes) left for allocation of new * global arrays on the calling processor. * */ int memoryAvailable() ; /** * Indicates if limit is set on memory usage in Global Arrays on the * calling processor. * * This operation is local. * * @return 1 means "yes", "0" means "no". */ int memoryLimited(); /** * Force completion of a nonblocking operation locally. * * Waiting on a nonblocking put or an accumulate operation assures that data * was injected into the network and the user buffer can be now be reused. * Completing a get operation assures data has arrived into the user memory * and is ready for use. Wait operation ensures only local completion. Unlike * their blocking counterparts, the nonblocking operations are not ordered * with respect to the destination. Performance being one reason, the other * reason is that by ensuring ordering we incur additional and possibly * unnecessary overhead on applications that do not require their operations * to be ordered. For cases where ordering is necessary, it can be done by * calling a fence operation. The fence operation is provided to the user to * confirm remote completion if needed. * * This is a local operation. * * @param[in] nbhandle nonblocking handle */ void nbWait(GANbhdl *nbhandle); /** * Returns the GA process id (0, ..., ga_Nnodes()-1) of the requesting * compute process. * * This operation is local. * * @return the GA process ID of the requesting process */ int nodeid(); /** * Returns the number of the GA compute (user) processes. * * This operation is local. * * @return the number of GA processes */ int nodes(); /** * Print statistical information on GA use. * * This non-collective (MIMD) operation prints information about: * - number of calls to * - create * - duplicate * - destroy * - get * - put * - scatter * - gather * - read_and_inc operations * - total amount of data moved in the primitive operations * - amount of data moved in the primitive operations to logicaly remote * locations * - maximum memory consumption in global arrays * - number of requests serviced in the interrupt-driven implementations * by the calling process. * * This operation is local. */ void printStats(); /** * Add a user defined data type to GA * * @param[in] size - size (in bytes) of user defined data type * * @return handle for new data type */ int registerType(size_t size); /** * This function sets an internal flag in the GA library to either true or * false. * * The value of this flag can be recovered at any time using the * getDebug function. The flag is set to false when the the GA library * is initialized. This can be useful in a number of debugging situations, * especially when examining the behavior of routines that are called in * multiple locations in a code. * * This is a local operation. * * @param[in] dbg value to set internal flag */ void setDebug(int dbg); /** * Sets the amount of memory to be used (in bytes) per process. * * This is a local operation. * * @param[in] limit the amount of memory in bytes per process */ void setMemoryLimit(size_t limit); /** * Prints info about allocated arrays. * * @param[in] verbose if true print distribution info */ void summarize(int verbose); /** * Synchronize processes (a barrier) and ensure that all GA operations * completed. * * This is a collective operation. */ void sync(); /** * Unlocks a mutex object identified by the mutex number. * * It is a fatal error for a process to attempt to unlock a mutex which has * not been locked by this process. * * @param[in] mutex object id */ void unlock(int mutex); /** * Returns whether memory comes from internal or external allocator. * * This operation is local. * * @return "1" if memory comes from MA; * "0" if memory comes from another source e.g. System V shared memory */ int usesMA(); /** * Returns whether GA is using Fortran indexing. * * @return "1" if uses fortran API, else returns "0" */ int usesFAPI(); /** * This function return a wall (or elapsed) time on the calling processor. * * Returns time in seconds representing elapsed wall-clock time * since an arbitrary time in the past. Example: * * @code * double starttime, endtime; * starttime = GA::wtime(); * // {{.... code snippet to be timed ....}} * endtime = GA::wtime(); * printf("Time taken = %lf seconds\n", endtime-starttime); * @endcode * * This is a local operation. * * @note This function is only available in release 4.1 or greater. */ double wtime(); } #endif /* _SERVICES_H */ ga-5.9.2/ga++/testing/000077500000000000000000000000001500715745200143165ustar00rootroot00000000000000ga-5.9.2/ga++/testing/create_irreg_test.cc000066400000000000000000000034321500715745200203210ustar00rootroot00000000000000#include "armci.h" #include "ga-mpi.h" #include "ga.h" #include "macdecls.h" #include "mpi.h" #include #include #include #include #include #include #include #include int main(int argc, char* argv[]) { MPI_Init(&argc, &argv); GA_Initialize(); int my_rank, nproc; MPI_Comm_rank(GA_MPI_Comm(), &my_rank); MPI_Comm_size(GA_MPI_Comm(), &nproc); if(nproc != 4) { if(my_rank == 0) std::cout << "ERROR: Need exactly 4 processes!\n"; GA_Terminate(); MPI_Finalize(); return 0; } int ndims = 2; int64_t arr_size = 4; int64_t dims[2] = {2, 2}; // 2x2 GA int64_t nblock[2] = {2, 2}; // 2 blocks along each dim int64_t pgrid[2] = {2, 1}; // total 2 ranks int64_t blk_sz[2] = {1,1}; // size of individual blocks std::vector k_map = {0, 1, 0, 1}; int proclist[2] = {2, 3}; // restrict to 2 procs int g_a = NGA_Create_handle(); NGA_Set_data64(g_a, ndims, &dims[0], C_DBL); GA_Set_restricted(g_a, proclist, 2); //NGA_Set_block_cyclic_proc_grid64(g_a, blk_sz, pgrid); NGA_Set_tiled_proc_grid64(g_a,blk_sz,pgrid); NGA_Set_pgroup(g_a, GA_Pgroup_get_default()); NGA_Allocate(g_a); std::string error_msg = "GA create failed"; if(!g_a) GA_Error(const_cast(error_msg.c_str()), arr_size); if(my_rank == 0) printf("GA create successful\n"); std::vector buf(4); std::vector lo = {0, 0}; std::vector hi = {1, 1}; std::vector ld = {2}; printf("p[%d] Calling NGA_Put64\n",my_rank); NGA_Put64(g_a, &lo[0], &hi[0], buf.data(), &ld[0]); NGA_Destroy(g_a); GA_Terminate(); MPI_Finalize(); return 0; } ga-5.9.2/ga++/testing/elempatch.cc000066400000000000000000000232051500715745200165710ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include using namespace std; #include "ga++.h" #define N 10 // First dimension #define NDIM 4 // Number of dimensions #define BASE 0 #define PERMUTE_ #define GA_DATA_TYPE MT_F_REAL #define MAXDIM GA_MAX_DIM #define C_DBL MT_C_DBL #define C_INT MT_C_INT #define C_FLOAT MT_C_FLOAT #define C_DCPL MT_C_DCPL #define C_LONG MT_C_LONGINT #define C_SCPL MT_C_SCPL #define GA_ABS(a) (((a) >= 0) ? (a) : (-(a))) #define GA_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define GA_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define THRESH 1e-5 #define MISMATCHED(x,y) GA_ABS((x)-(y))>=THRESH #define OP_ELEM_MULT 0 #define OP_ELEM_DIV 1 #define OP_ELEM_MAX 2 #define OP_ELEM_MIN 3 #define OP_ABS 4 #define OP_ADD_CONST 5 #define OP_RECIP 6 #define MY_TYPE 2002 /* Integer _ga_lo[MAXDIM], _ga_hi[MAXDIM], _ga_work[MAXDIM];*/ # define COPYINDEX_C2F(carr, farr, n){\ int i; for(i=0; i< (n); i++)(farr)[n-i-1]=(Integer)(carr)[i]+1;} int test_fun (int type, int dim, int OP) { GA::GlobalArray *g_a, *g_b, *g_c, *g_d, *g_e; int me = GA_Nodeid (); int i; int dims[MAXDIM]; int lo[MAXDIM], hi[MAXDIM]; int index[MAXDIM]; void *val; int ival = -2; double dval = -2.0; float fval = -2.0; long lval = -2; DoubleComplex dcval; void *val2; int ival2 = -3; double dval2 = -3.0; float fval2 = -3.0; long lval2 = -3; DoubleComplex dcval2; int ok = 1; int result; void *min, *max; float fmin, fmax; long lmin, lmax; double dmin, dmax; DoubleComplex dcmin, dcmax; void *alpha, *beta; int ai = 1, bi = -1; long al = 1, bl = -1; float af = 1.0, bf = -1.0; double ad = 1.0, bd = -1.0; DoubleComplex adc, bdc; char name_A[] = "A"; char name_B[] = "B"; char name_C[] = "C"; char name_D[] = "D"; char name_E[] = "E"; char name_max[] = "max"; char name_min[] = "min"; adc.real = 1.0; adc.imag = 0.0; bdc.real = -1.0; bdc.imag = 0.0; dcval.real = -sin (3.0); dcval.imag = -cos (3.0); dcval2.real = 2 * sin (3.0); dcval2.imag = 2 * cos (3.0); for (i = 0; i < dim; i++) dims[i] = N; for (i = 0; i < dim; i++) { lo[i] = 0; hi[i] = N - 1; } g_a = GA::SERVICES.createGA (type, dim, dims, name_A, NULL); g_b = GA::SERVICES.createGA (g_a, name_B); g_c = GA::SERVICES.createGA (g_a, name_C); g_d = GA::SERVICES.createGA (g_a, name_D); g_e = GA::SERVICES.createGA (g_a, name_E); /*initialize with zero */ g_a->zero (); g_b->zero (); g_c->zero (); g_d->zero (); g_e->zero (); switch (type) { case C_INT: val = (void *)&ival; val2 = (void *)&ival2; break; case C_DCPL: val = (void *)&dcval; val2 = (void *)&dcval2; break; case C_DBL: val = (void *)&dval; val2 = (void *)&dval2; break; case C_FLOAT: val = (void *)&fval; val2 = (void *)&fval2; break; case C_LONG: val = (void *)&lval; val2 = (void *)&lval2; break; default: GA::SERVICES.error ("wrong data type.", type); } g_a->fillPatch (lo, hi, val); switch (OP) { double tmp, tmp2; DoubleComplex dctemp; case OP_ABS: if (me == 0) printf ("Testing GA_Abs_value..."); g_a->absValuePatch (lo, hi); ival = GA_ABS (ival); dval = GA_ABS (dval); fval = GA_ABS (fval); lval = GA_ABS (lval); dcval.real = dcval.real * dcval.real + dcval.imag * dcval.imag; dcval.imag = 0.0; g_d->fillPatch (lo, hi, val); break; case OP_ADD_CONST: if (me == 0) printf ("Testing GA_Add_const..."); g_a->addConstantPatch (lo, hi, val2); ival = ival + ival2; dval = dval + dval2; fval = fval + fval2; lval = lval + lval2; dcval.real = dcval.real + dcval2.real; dcval.imag = dcval.imag + dcval2.imag; g_d->fillPatch (lo, hi, val); break; case OP_RECIP: if (me == 0) printf ("Testing GA_Recip..."); g_a->recipPatch (lo, hi); ival = 1 / ival; dval = 1.0 / dval; fval = 1.0 / fval; lval = 1 / lval; tmp = dcval.real * dcval.real + dcval.imag * dcval.imag; dcval.real = dcval.real / tmp; dcval.imag = -dcval.imag / tmp; g_d->fillPatch (lo, hi, val); break; case OP_ELEM_MULT: if (me == 0) printf ("Testin GA_Elem_multiply..."); g_b->fillPatch (lo, hi, val); #if 0 //g_c is different from g_a or g_b g_c->elemMultiplyPatch (g_a, lo, hi, g_b, lo, hi, lo, hi); #else //g_c is g_b g_b->elemMultiplyPatch (g_a, lo, hi, g_b, lo, hi, lo, hi); #endif ival = ival * ival2; dval = dval * dval2; fval = fval * fval2; lval = lval * lval2; dctemp.real = dcval.real * dcval2.real - dcval.imag * dcval2.imag; dctemp.imag = dcval.real * dcval2.imag + dcval2.real * dcval.imag; dcval = dctemp; g_d->fillPatch (lo, hi, val); break; case OP_ELEM_DIV: if (me == 0) printf ("Testin GA_Elem_divide..."); g_b->fillPatch (lo, hi, val2); g_c->elemDividePatch (g_a, lo, hi, g_b, lo, hi, lo, hi); ival = ival / ival2; dval = dval / dval2; fval = fval / fval2; lval = lval / lval2; tmp = dcval2.real * dcval2.real + dcval2.imag * dcval2.imag; dctemp.real = (dcval.real * dcval2.real + dcval.imag * dcval2.imag) / tmp; dctemp.imag = (-dcval.real * dcval2.imag + dcval2.real * dcval.imag) / tmp; dcval = dctemp; g_d->fillPatch (lo, hi, val); break; case OP_ELEM_MAX: if (me == 0) printf ("Testin GA_Elem_maximum..."); g_b->fillPatch (lo, hi, val2); g_c->elemMaximumPatch (g_a, lo, hi, g_b, lo, hi, lo, hi); ival = GA_MAX (ival, ival2); dval = GA_MAX (dval, dval2); fval = GA_MAX (fval, fval2); lval = GA_MAX (lval, lval2); tmp = dcval.real * dcval.real + dcval.imag * dcval.imag; tmp2 = dcval2.real * dcval2.real + dcval2.imag * dcval2.imag; if (tmp2 > tmp) dcval = dcval2; g_d->fillPatch (lo, hi, val); break; case OP_ELEM_MIN: if (me == 0) printf ("Testin GA_Elem_minimum..."); g_b->fillPatch (lo, hi, val2); g_c->elemMinimumPatch (g_a, lo, hi, g_b, lo, hi, lo, hi); ival = GA_MIN (ival, ival2); dval = GA_MIN (dval, dval2); fval = GA_MIN (fval, fval2); lval = GA_MIN (lval, lval2); tmp = dcval.real * dcval.real + dcval.imag * dcval.imag; tmp2 = dcval2.real * dcval2.real + dcval2.imag * dcval2.imag; if (tmp2 < tmp) dcval = dcval2; g_d->fillPatch (lo, hi, val); break; default: GA::SERVICES.error ("test_function: wrong operation.", OP); } switch (type) { case C_INT: alpha = (void *)&ai; beta = (void *)&bi; break; case C_DCPL: alpha = (void *)&adc; beta = (void *)&bdc; break; case C_DBL: alpha = (void *)&ad; beta = (void *)&bd; break; case C_FLOAT: alpha = (void *)⁡ beta =(void *) &bf; break; case C_LONG: alpha = (void *)&al; beta = (void *)&bl; break; default: GA::SERVICES.error ("wrong data type.", type); } if (OP < 4) g_e->addPatch (alpha, g_c, lo, hi, beta, g_d, lo, hi, lo, hi); else g_e->addPatch (alpha, g_a, lo, hi, beta, g_d, lo, hi, lo, hi); switch (type) { case C_INT: max = (void *)&lmax; min = (void *)&lmin; break; case C_DCPL: max = (void *)&dcmax; min = (void *)&dcmin; break; case C_DBL: max = (void *)&dmax; min = (void *)&dmin; break; case C_FLOAT: max = (void *)&fmax; min = (void *)&fmin; break; case C_LONG: max = (void *)&lmax; min = (void *)&lmin; break; default: GA::SERVICES.error ("wrong data type.", type); } g_e->selectElem (name_max, max, index); g_e->selectElem (name_min, min, index); switch (type) { double r, im; case C_INT: result = lmax - lmin; break; case C_DCPL: r = dcmax.real - dcmin.real; im = dcmax.imag - dcmin.imag; result = (int) (GA_ABS (r) + GA_ABS (im)); break; case C_DBL: result = (int) (dmax - dmin); break; case C_FLOAT: result = (int) (fmax - fmin); break; case C_LONG: result = (int) (lmax - lmin); break; default: GA::SERVICES.error ("wrong data type.", type); } if (me == 0) { if (MISMATCHED (result, 0)) printf ("is not ok\n"); else printf ("is ok.\n"); } /* g_a->printPatch(lo, hi, 1); g_d->printPatch(lo, hi, 1); g_a->printPatch(lo, hi, 1); */ g_a->destroy (); g_b->destroy (); g_c->destroy (); g_d->destroy (); g_e->destroy (); return ok; } int main(int argc, char *argv[]) { int me, nproc; int heap = 200000, stack = 200000; int d, op, ok = 1; GA::Initialize(argc, argv, heap, stack, GA_DATA_TYPE, 0); me=GA_Nodeid(); nproc=GA_Nnodes(); cout << "Rank = " << me << " : Size = " << nproc << "\n"; cout << "After Initialize()\n"; for (op = 0; op < 7; op++) { for (d = 1; d < 4; d++) { if (me == 0) printf ("\n\ndim =%d\n\n", d); if (me == 0) printf ("data type: int\t\t"); ok = test_fun (C_INT, d, op); if (me == 0) printf ("data type: double\t"); ok = test_fun (C_DBL, d, op); if (me == 0) printf ("data type: float\t"); ok = test_fun (C_FLOAT, d, op); if (me == 0) printf ("data type: long\t\t"); ok = test_fun (C_LONG, d, op); if (me == 0) printf ("data type: complex\t"); test_fun (C_DCPL, d, op); } } if(!me) cout << "Terminating\n"; GA::Terminate(); } ga-5.9.2/ga++/testing/mtest.cc000066400000000000000000001163121500715745200157650ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include using namespace std; #include "ga++.h" #define GA_DATA_TYPE MT_F_REAL #define N 4 /* dimension of matrices */ #define MAXDIM GA_MAX_DIM #define OP_SHIFT_DIAGONAL 1 #define OP_SET_DIAGONAL 2 #define OP_ADD_DIAGONAL 3 #define OP_GET_DIAGONAL 4 #define OP_NORM1 5 #define OP_NORM_INFINITY 6 #define OP_MEDIAN 7 #define OP_MEDIAN_PATCH 8 #define OP_SCALE_ROWS 9 #define OP_SCALE_COLS 10 #define GA_ABS(a) (((a) >= 0) ? (a) : (-(a))) #define GA_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define GA_MIN(a,b) (((a) <= (b)) ? (a) : (b)) # define THRESH 1e-5 #define MISMATCHED(x,y) GA_ABS((x)-(y))>=THRESH void test_scale_cols (GA::GlobalArray *g_a, GA::GlobalArray *g_v) { int index[MAXDIM]; void *min, *max; int imin, imax; float fmin, fmax; long lmin, lmax; double dmin, dmax; DoubleComplex dcmin, dcmax; void *alpha, *beta; int ai = 1, bi = -1; long al = 1, bl = -1; float af = 1.0, bf = -1.0; double ad = 1.0, bd = -1.0; DoubleComplex adc = { 1.0, 0.0 }, bdc = { -1.0, 0.0}; GA::GlobalArray * g_b, *g_c; int me = GA_Nodeid (); void *val; int ival = -2; double dval = -2.0; float fval = -2.0; long lval = -2; DoubleComplex dcval = { -2.0, 0.0 }; void *val2; int ival2 = 4; double dval2 = 4.0; float fval2 = 4.0; long lval2 = 4; DoubleComplex dcval2 = { 4.0, 0.0 }; int type, ndim, dims[MAXDIM]; int vtype, vndim, vdims[MAXDIM]; g_a->inquire (&type, &ndim, dims); g_v->inquire (&vtype, &vndim, vdims); switch (type) { case C_INT: alpha = (void *)&ai; beta = (void *)&bi; break; case C_DCPL: alpha = (void *)&adc; beta = (void *)&bdc; break; case C_DBL: alpha = (void *)&ad; beta =(void *) &bd; break; case C_FLOAT: alpha =(void *) ⁡ beta = (void *)&bf; break; case C_LONG: alpha = (void *)&al; beta = (void *)&bl; break; default: GA::SERVICES.error ((char *)"test_scale_cols:wrong data type.", type); } switch (type) { case C_INT: val = (void *)&ival; val2 = (void *)&ival2; break; case C_DCPL: val = (void *)&dcval; val2 = (void *)&dcval2; break; case C_DBL: val = (void *)&dval; val2 = (void *)&dval2; break; case C_FLOAT: val = (void *)&fval; val2 = (void *)&fval2; break; case C_LONG: val = (void *)&lval; val2 = (void *)&lval2; break; default: GA::SERVICES.error ((char *)"test_scale_cols:wrong data type.", type); } if (me == 0) printf ("Testing GA_Scale_cols..."); g_a->fill(val); g_v->fill(val); g_a->scaleCols (g_v); /*the result is the same same as g_b filled with val2 */ g_b = GA::SERVICES.createGA (g_a, (char *)"B"); g_c = GA::SERVICES.createGA (g_a, (char *)"C"); g_b->fill(val2); g_c->add(alpha, g_a, beta, g_b); switch (type) { case C_INT: max = (void *)&imax; min = (void *)&imin; break; case C_DCPL: max = (void *)&dcmax; min = (void *)&dcmin; break; case C_DBL: max = (void *)&dmax; min =(void *) &dmin; break; case C_FLOAT: max = (void *)&fmax; min = (void *)&fmin; break; case C_LONG: max = (void *)&lmax; min = (void *)&lmin; break; default: GA::SERVICES.error ((char *)"test_scale_rows:wrong data type.", type); } g_c->selectElem ((char *)"max", max, index); g_c->selectElem ((char *)"min", min, index); switch (type) { double r, m; case C_INT: if (me == 0) { if (MISMATCHED (imax, imin) || (imax != 0) || (imin != 0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_scale_rows:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_DCPL: r = dcmax.real - dcmin.real; m = dcmax.imag - dcmin.imag; if (me == 0) { if (MISMATCHED (dcmax.real, dcmin.real) || (dcmax.real != 0.0) || (dcmin.real != 0.0) || MISMATCHED (dcmax.imag, dcmin.imag) || (dcmax.imag != 0.0) || (dcmin.imag != 0.0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_scale_rows:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_DBL: if (me == 0) { if (MISMATCHED (dmax, dmin) || (dmax != 0) || (dmin != 0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_scale_rows:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_FLOAT: if (me == 0) { if (MISMATCHED (fmax, fmin) || (fmax != 0) || (fmin != 0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_scale_rows:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_LONG: if (me == 0) { if (MISMATCHED (lmax, lmin) || (lmax != 0) || (lmin != 0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_scale_rows:mismatched.", type); } else { printf ("ok.\n"); } } break; default: GA::SERVICES.error ((char *)"test_scale_rows:wrong data type.", type); } } void test_scale_rows (GA::GlobalArray *g_a, GA::GlobalArray *g_v) { int index[MAXDIM]; void *min, *max; int imin, imax; float fmin, fmax; long lmin, lmax; double dmin, dmax; DoubleComplex dcmin, dcmax; void *alpha, *beta; int ai = 1, bi = -1; long al = 1, bl = -1; float af = 1.0, bf = -1.0; double ad = 1.0, bd = -1.0; DoubleComplex adc = { 1.0, 0.0 }; DoubleComplex bdc = {-1.0, 0.0 }; GA::GlobalArray *g_b, *g_c; int me = GA_Nodeid (); void *val; int ival = -2; double dval = -2.0; float fval = -2.0; long lval = -2; DoubleComplex dcval = { -2.0, 0.0 }; void *val2; int ival2 = 4; double dval2 = 4.0; float fval2 = 4.0; long lval2 = 4; DoubleComplex dcval2 = { 4.0, 0.0 }; int type, ndim, dims[MAXDIM]; int vtype, vndim, vdims[MAXDIM]; g_a->inquire (&type, &ndim, dims); g_v->inquire (&vtype, &vndim, vdims); switch (type) { case C_INT: alpha = (void *)&ai; beta = (void *)&bi; break; case C_DCPL: alpha = (void *)&adc; beta = (void *)&bdc; break; case C_DBL: alpha = (void *)&ad; beta =(void *) &bd; break; case C_FLOAT: alpha = (void *)⁡ beta = (void *)&bf; break; case C_LONG: alpha = (void *)&al; beta = (void *)&bl; break; default: GA::SERVICES.error ((char *)"test_scale_rows:wrong data type.", type); } switch (type) { case C_INT: val = (void *)&ival; val2 = (void *)&ival2; break; case C_DCPL: val = (void *)&dcval; val2 = (void *)&dcval2; break; case C_DBL: val = (void *)&dval; val2 = (void *)&dval2; break; case C_FLOAT: val =(void *) &fval; val2 = (void *)&fval2; break; case C_LONG: val = (void *)&lval; val2 = (void *)&lval2; break; default: GA::SERVICES.error ((char *)"test_scale_rows:wrong data type.", type); } if (me == 0) printf ("Testing GA_Scale_rows..."); g_a->fill (val); g_v->fill (val); g_a->scaleRows (g_v); /*the result is the same same as g_b filled with val2 */ g_b = GA::SERVICES.createGA (g_a, (char *)"B"); g_c = GA::SERVICES.createGA (g_a, (char *)"C"); g_b->fill (val2); g_c->add (alpha, g_a, beta, g_b); switch (type) { case C_INT: max = (void *)&imax; min = (void *)&imin; break; case C_DCPL: max = (void *)&dcmax; min = (void *)&dcmin; break; case C_DBL: max = (void *)&dmax; min = (void *)&dmin; break; case C_FLOAT: max = (void *)&fmax; min = (void *)&fmin; break; case C_LONG: max = (void *)&lmax; min =(void *) &lmin; break; default: GA::SERVICES.error ((char *)"test_scale_rows:wrong data type.", type); } g_c->selectElem ((char *)"max", max, index); g_c->selectElem ((char *)"min", min, index); switch (type) { double r, m; case C_INT: if (me == 0) { if (MISMATCHED (imax, imin) || (imax != 0) || (imin != 0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_scale_rows:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_DCPL: r = dcmax.real - dcmin.real; m = dcmax.imag - dcmin.imag; if (me == 0) { if (MISMATCHED (dcmax.real, dcmin.real) || (dcmax.real != 0.0) || (dcmin.real != 0.0) || MISMATCHED (dcmax.imag, dcmin.imag) || (dcmax.imag != 0.0) || (dcmin.imag != 0.0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_scale_rows:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_DBL: if (me == 0) { if (MISMATCHED (dmax, dmin) || (dmax != 0) || (dmin != 0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_scale_rows:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_FLOAT: if (me == 0) { if (MISMATCHED (fmax, fmin) || (fmax != 0) || (fmin != 0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_scale_rows:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_LONG: if (me == 0) { if (MISMATCHED (lmax, lmin) || (lmax != 0) || (lmin != 0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_scale_rows:mismatched.", type); } else { printf ("ok.\n"); } } break; default: GA::SERVICES.error ((char *)"test_scale_rows:wrong data type.", type); } } void test_median_patch (GA::GlobalArray * g_a, int *alo, int *ahi, GA::GlobalArray * g_b, int *blo, int *bhi, GA::GlobalArray * g_c, int *clo, int *chi, GA::GlobalArray * g_m, int *mlo, int *mhi) { GA::GlobalArray * g_e; int index[MAXDIM]; void *min, *max; int imin, imax; float fmin, fmax; long lmin, lmax; double dmin, dmax; DoubleComplex dcmin, dcmax; void *alpha, *beta; int ai = 1, bi = -1; long al = 1, bl = -1; float af = 1.0, bf = -1.0; double ad = 1.0, bd = -1.0; DoubleComplex adc = { 1.0, 0.0 }; DoubleComplex bdc = {-1.0, 0.0 }; void *val; int ival = -2; double dval = -2.0; float fval = -2.0; long lval = -2; DoubleComplex dcval; void *val2; int ival2 = 6; double dval2 = 6.0; float fval2 = 6.0; long lval2 = 6; DoubleComplex dcval2; void *val3; int ival3 = 4; double dval3 = 4.0; float fval3 = 4.0; long lval3 = 4; DoubleComplex dcval3; int me = GA_Nodeid (); int type, ndim, dims[MAXDIM]; g_a->inquire (&type, &ndim, dims); switch (type) { case C_INT: alpha = (void *)&ai; beta =(void *) &bi; break; case C_DCPL: alpha =(void *) &adc; beta = (void *)&bdc; break; case C_DBL: alpha = (void *)&ad; beta = (void *)&bd; break; case C_FLOAT: alpha = (void *)⁡ beta = (void *)&bf; break; case C_LONG: alpha = (void *)&al; beta = (void *)&bl; break; default: GA::SERVICES.error ((char *)"test_median:wrong data type.", type); } dcval.real = -2.0; dcval.imag = -0.0; dcval2.real = 6.0; dcval2.imag = 0.0; dcval3.real = 4.0; dcval3.imag = 0.0; switch (type) { case C_INT: val = (void *)&ival; val2 = (void *)&ival2; val3 = (void *)&ival3; break; case C_DCPL: val = (void *)&dcval; val2 = (void *)&dcval2; val3 = (void *)&dcval3; break; case C_DBL: val = (void *)&dval; val2 = (void *)&dval2; val3 = (void *)&dval3; break; case C_FLOAT: val = (void *)&fval; val2 = (void *)&fval2; val3 = (void *)&fval3; break; case C_LONG: val = (void *)&lval; val2 = (void *)&lval2; val3 = (void *)&lval3; break; default: GA::SERVICES.error ((char *)"test_median:test_median:wrong data type.", type); } if (me == 0) printf ("Testing GA_Median_patch..."); g_a->zero (); g_b->zero (); g_c->zero (); g_m->zero (); g_a->fillPatch (alo, ahi, val); g_b->fillPatch (blo, bhi, val2); g_c->fillPatch (alo, bhi, val3); g_m->medianPatch (g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi, mlo, mhi); /* The result array should be g_c due to the value I chose: val3 is the median of the three values val, val2, and val3 */ /* g_e = g_c - g_m */ g_e = GA::SERVICES.createGA(g_a, (char *)"E"); g_e->zero (); g_e->addPatch (alpha, g_c, clo, chi, beta, g_m, mlo, mhi, alo, ahi); switch (type) { case C_INT: max = (void *)&imax; min = (void *)&imin; break; case C_DCPL: max = (void *)&dcmax; min = (void *)&dcmin; break; case C_DBL: max = (void *)&dmax; min = (void *)&dmin; break; case C_FLOAT: max = (void *)&fmax; min = (void *)&fmin; break; case C_LONG: max = (void *)&lmax; min = (void *)&lmin; break; default: GA_Error ((char *)"test_median:wrong data type.", type); } g_e->selectElem ((char *)"max", max, index); g_e->selectElem ((char *)"min", min, index); switch (type) { double r, m; case C_INT: if (me == 0) { if (MISMATCHED (imax, imin) || (imax != 0) || (imin != 0)) { printf ("not ok.\n"); GA_Error ((char *)"test_median:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_DCPL: r = dcmax.real - dcmin.real; m = dcmax.imag - dcmin.imag; if (me == 0) { if (MISMATCHED (dcmax.real, dcmin.real) || (dcmax.real != 0.0) || (dcmin.real != 0.0) || MISMATCHED (dcmax.imag, dcmin.imag) || (dcmax.imag != 0.0) || (dcmin.imag != 0.0)) { printf ("not ok.\n"); GA_Error ((char *)"test_median:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_DBL: if (me == 0) { if (MISMATCHED (dmax, dmin) || (dmax != 0) || (dmin != 0)) { printf ("not ok.\n"); GA_Error ((char *)"test_median:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_FLOAT: if (me == 0) { if (MISMATCHED (fmax, fmin) || (fmax != 0) || (fmin != 0)) { printf ("not ok.\n"); GA_Error ((char *)"test_median:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_LONG: if (me == 0) { if (MISMATCHED (lmax, lmin) || (lmax != 0) || (lmin != 0)) { printf ("not ok.\n"); GA_Error ((char *)"test_median:mismatched.", type); } else { printf ("ok.\n"); } } break; default: GA::SERVICES.error ((char *)"test_median:wrong data type.", type); } } void test_median (GA::GlobalArray * g_a, GA::GlobalArray * g_b, GA::GlobalArray * g_c, GA::GlobalArray * g_m) { GA::GlobalArray *g_e; int index[MAXDIM]; void *min, *max; int imin, imax; float fmin, fmax; long lmin, lmax; double dmin, dmax; DoubleComplex dcmin, dcmax; void *alpha, *beta; int ai = 1, bi = -1; long al = 1, bl = -1; float af = 1.0, bf = -1.0; double ad = 1.0, bd = -1.0; DoubleComplex adc = { 1.0, 0.0 }; DoubleComplex bdc = {-1.0, 0.0 }; void *val; int ival = -2; double dval = -2.0; float fval = -2.0; long lval = -2; DoubleComplex dcval; void *val2; int ival2 = 6; double dval2 = 6.0; float fval2 = 6.0; long lval2 = 6; DoubleComplex dcval2; void *val3; int ival3 = 4; double dval3 = 4.0; float fval3 = 4.0; long lval3 = 4; DoubleComplex dcval3; int me = GA_Nodeid (); int type, ndim, dims[MAXDIM]; g_a->inquire (&type, &ndim, dims); switch (type) { case C_INT: alpha = (void *)&ai; beta = (void *)&bi; break; case C_DCPL: alpha = (void *)&adc; beta =(void *) &bdc; break; case C_DBL: alpha = (void *)&ad; beta = (void *)&bd; break; case C_FLOAT: alpha =(void *) ⁡ beta = (void *)&bf; break; case C_LONG: alpha = (void *)&al; beta = (void *)&bl; break; default: GA::SERVICES.error ((char *)"test_median:wrong data type.", type); } dcval.real = -2.0; dcval.imag = -0.0; dcval2.real = 6.0; dcval2.imag = 0.0; dcval3.real = 4.0; dcval3.imag = 0.0; switch (type) { case C_INT: val = (void *)&ival; val2 =(void *) &ival2; val3 = (void *)&ival3; break; case C_DCPL: val = (void *)&dcval; val2 =(void *) &dcval2; val3 =(void *) &dcval3; break; case C_DBL: val = (void *)&dval; val2 = (void *)&dval2; val3 = (void *)&dval3; break; case C_FLOAT: val = (void *)&fval; val2 = (void *)&fval2; val3 = (void *)&fval3; break; case C_LONG: val = (void *)&lval; val2 = (void *)&lval2; val3 = (void *)&lval3; break; default: GA_Error ((char *)"test_median:test_median:wrong data type.", type); } if (me == 0) printf ("Testing GA_Median..."); g_a->zero(); g_b->zero(); g_c->zero(); g_m->zero(); g_a->fill (val); g_b->fill (val2); g_c->fill (val3); g_m->median (g_a, g_b, g_c); /* The result array should be g_c due to the value I chose: val3 is the median of the three values val, val2, and val3 */ /* g_e = g_c - g_m */ g_e = GA::SERVICES.createGA (g_a, (char *)"E"); g_e->add (alpha, g_c, beta, g_m); switch (type) { case C_INT: max =(void *) &imax; min = (void *)&imin; break; case C_DCPL: max =(void *) &dcmax; min = (void *)&dcmin; break; case C_DBL: max = (void *)&dmax; min = (void *)&dmin; break; case C_FLOAT: max = (void *)&fmax; min = (void *)&fmin; break; case C_LONG: max = (void *)&lmax; min = (void *)&lmin; break; default: GA::SERVICES.error ((char *)"test_median:wrong data type.", type); } g_e->selectElem ((char *)"max", max, index); g_e->selectElem ((char *)"min", min, index); switch (type) { double r, m; case C_INT: if (me == 0) { if (MISMATCHED (imax, imin) || (imax != 0) || (imin != 0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_median:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_DCPL: r = dcmax.real - dcmin.real; m = dcmax.imag - dcmin.imag; if (me == 0) { if (MISMATCHED (dcmax.real, dcmin.real) || (dcmax.real != 0.0) || (dcmin.real != 0.0) || MISMATCHED (dcmax.imag, dcmin.imag) || (dcmax.imag != 0.0) || (dcmin.imag != 0.0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_median:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_DBL: if (me == 0) { if (MISMATCHED (dmax, dmin) || (dmax != 0) || (dmin != 0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_median:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_FLOAT: if (me == 0) { if (MISMATCHED (fmax, fmin) || (fmax != 0) || (fmin != 0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_median:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_LONG: if (me == 0) { if (MISMATCHED (lmax, lmin) || (lmax != 0) || (lmin != 0)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_median:mismatched.", type); } else { printf ("ok.\n"); } } break; default: GA::SERVICES.error ((char *)"test_median:wrong data type.", type); } } void test_norm_infinity (GA::GlobalArray * g_a) { void *val; int ival = -2; double dval = -2.0; float fval = -2.0; long lval = -2; DoubleComplex dcval; double norm_infinity = -1.0, result = -1.0; int me = GA_Nodeid (); int type, ndim, dims[MAXDIM]; g_a->inquire (&type, &ndim, dims); dcval.real = -2.0; dcval.imag = -0.0; switch (type) { case C_INT: val = (void *)&ival; break; case C_DCPL: val = (void *)&dcval; break; case C_DBL: val = (void *)&dval; break; case C_FLOAT: val =(void *) &fval; break; case C_LONG: val = (void *)&lval; break; default: GA::SERVICES.error ((char *)"test_norm_infinity:wrong data type.", type); } if (me == 0) printf ("Testing GA_Norm_infinity..."); g_a->fill (val); g_a->normInfinity (&norm_infinity); // GA_Print(g_a); //printf("norm_infinity = %lf\n",norm_infinity); switch (type) { case C_INT: result = (double) GA_ABS (ival); break; case C_LONG: result = (double) GA_ABS (lval); break; case C_FLOAT: result = (double) GA_ABS (fval); break; case C_DBL: result = GA_ABS (dval); break; case C_DCPL: result = sqrt (dcval.real * dcval.real + dcval.imag * dcval.imag); break; default: GA::SERVICES.error ((char *)"test_norm_infinity: wrong data type.\n", type); } if (me == 0) { if (MISMATCHED (result, norm_infinity)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_norm_infinity:mismatched.", type); } else { printf ("ok.\n"); } } } void test_norm1 (GA::GlobalArray * g_a) { void *val; int ival = -2; double dval = -2.0; float fval = -2.0; long lval = -2; DoubleComplex dcval; double norm1 = 0.0, result = -1.0; int me = GA_Nodeid (); int type, ndim, dims[MAXDIM]; g_a->inquire (&type, &ndim, dims); dcval.real = -2.0; dcval.imag = -0.0; switch (type) { case C_INT: val = (void *)&ival; break; case C_DCPL: val =(void *) &dcval; break; case C_DBL: val = (void *)&dval; break; case C_FLOAT: val = (void *)&fval; break; case C_LONG: val = (void *)&lval; break; default: GA::SERVICES.error ((char *)"test_norm1:wrong data type.", type); } if (me == 0) printf ("Testing GA_Norm1..."); g_a->fill (val); g_a->norm1 (&norm1); // GA_Print(g_a); //printf("norm1=%lf\n", norm1); switch (type) { case C_INT: result = (double) GA_ABS (ival); break; case C_LONG: result = (double) GA_ABS (lval); break; case C_FLOAT: result = (double) GA_ABS (fval); break; case C_DBL: result = GA_ABS (dval); break; case C_DCPL: result = sqrt (dcval.real * dcval.real + dcval.imag * dcval.imag); break; default: GA::SERVICES.error ((char *)"test_norm1: wrong data type.\n", type); } result = result * dims[0] * dims[1]; if (me == 0) { if (MISMATCHED (result, norm1)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_norm1: mismatched.\n", type); } else { printf ("ok.\n"); } } } void test_get_diagonal (GA::GlobalArray * g_a, GA::GlobalArray * g_v) { int me = GA_Nodeid (); void *val; int ival = -2; double dval = -2.0; float fval = -2.0; long lval = -2; DoubleComplex dcval; int idot, iresult, ldot, lresult; double fdot, ddot, fresult, dresult; DoubleComplex zdot, zresult; int type, ndim, dims[MAXDIM]; int vtype, vndim, vdims[MAXDIM]; g_a->inquire (&type, &ndim, dims); g_v->inquire (&vtype, &vndim, vdims); dcval.real = -2.0; dcval.imag = -0.0; switch (type) { case C_INT: val = (void *)&ival; break; case C_DCPL: val = (void *)&dcval; break; case C_DBL: val = (void *)&dval; break; case C_FLOAT: val =(void *) &fval; break; case C_LONG: val =(void *) &lval; break; default: GA::SERVICES.error ((char *)"test_get_diagonal:wrong data type.", type); } if (me == 0) printf ("Testing GA_Get_diagonal..."); g_v->zero (); g_a->fill (val); g_v->getDiagonal (g_a); switch (type) { case C_INT: idot = vdims[0] * ival * ival; iresult = g_v->idot (g_v); if (me == 0) { if (MISMATCHED (idot, iresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_get_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_LONG: ldot = ((long) vdims[0]) * lval * lval; lresult = g_v->ldot (g_v); if (me == 0) { if (MISMATCHED (ldot, lresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_get_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_FLOAT: fdot = ((float) vdims[0]) * fval * fval; fresult = g_v->fdot (g_v); if (me == 0) { if (MISMATCHED (fdot, fresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_get_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_DBL: ddot = ((double) vdims[0]) * dval * dval; dresult = g_v->ddot (g_v); if (me == 0) { if (MISMATCHED (ddot, dresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_get_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_DCPL: zdot.real = ((double) vdims[0]) * (dcval.real * dcval.real - dcval.imag * dcval.imag); zdot.imag = ((double) vdims[0]) * (2.0 * dcval.real * dcval.imag); zresult = g_v->zdot (g_v); if (me == 0) { if (MISMATCHED (zdot.real, zresult.real) || MISMATCHED (zdot.imag, zresult.imag)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_get_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; default: GA::SERVICES.error ((char *)"test_get_diagonal:wrong data type:", type); } } void test_add_diagonal (GA::GlobalArray * g_a, GA::GlobalArray * g_v) { int me = GA_Nodeid (); void *val; int ival = -2; double dval = -2.0; float fval = -2.0; long lval = -2; DoubleComplex dcval; int idot, iresult, ldot, lresult; double fdot, ddot, fresult, dresult; DoubleComplex zdot, zresult; int type, ndim, dims[MAXDIM]; int vtype, vndim, vdims[MAXDIM]; g_a->inquire (&type, &ndim, dims); dcval.real = -2.0; dcval.imag = -0.0; g_v->inquire (&vtype, &vndim, vdims); switch (type) { case C_INT: val =(void *) &ival; break; case C_DCPL: val = (void *)&dcval; break; case C_DBL: val =(void *) &dval; break; case C_FLOAT: val =(void *) &fval; break; case C_LONG: val = (void *)&lval; break; default: GA::SERVICES.error ((char *)"test_add_diagonal:wrong data type.", type); } if (me == 0) printf ("Testing GA_Add_diagonal..."); g_a->zero (); g_v->fill (val); g_a->setDiagonal (g_v); /*reassign value to val */ ival = 3; dval = 3.0; fval = 3.0; lval = 3; dcval.real = 3.0; dcval.imag = -0.0; /*refile the global array g_v */ g_v->fill (val); /*Add g_v to the diagonal of g_a */ g_a->addDiagonal (g_v); /*after this line, the g_a should only have 1 on the diagonal and zeros every where else */ switch (type) { case C_INT: idot = vdims[0]; iresult = g_a->idot (g_a); if (me == 0) { if (MISMATCHED (idot, iresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_add_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_LONG: ldot = ((long) vdims[0]); lresult = g_a->ldot (g_a); if (me == 0) { if (MISMATCHED (ldot, lresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_add_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_FLOAT: fdot = ((float) vdims[0]); fresult = g_a->fdot (g_a); if (me == 0) { if (MISMATCHED (fdot, fresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_add_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_DBL: ddot = (double) vdims[0]; dresult = g_a->ddot (g_a); if (me == 0) { if (MISMATCHED (ddot, dresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_add_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_DCPL: zdot.real = ((double) vdims[0]); zdot.imag = 0.0; zresult = g_a->zdot (g_a); if (me == 0) { if (MISMATCHED (zdot.real, zresult.real) || MISMATCHED (zdot.imag, zresult.imag)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_add_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; default: GA::SERVICES.error ((char *)"test_add_diagonal:wrong data type:", type); } } void test_set_diagonal (GA::GlobalArray * g_a, GA::GlobalArray * g_v) { int me = GA_Nodeid (); void *val; int ival = -2; double dval = -2.0; float fval = -2.0; long lval = -2; DoubleComplex dcval; int idot, iresult, ldot, lresult; double fdot, ddot, fresult, dresult; DoubleComplex zdot, zresult; int type, ndim, dims[MAXDIM]; int vtype, vndim, vdims[MAXDIM]; g_a->inquire (&type, &ndim, dims); g_v->inquire (&vtype, &vndim, vdims); dcval.real = -2.0; dcval.imag = -0.0; switch (type) { case C_INT: val = (void *)&ival; break; case C_DCPL: val = (void *)&dcval; break; case C_DBL: val = (void *)&dval; break; case C_FLOAT: val = (void *)&fval; break; case C_LONG: val =(void *) &lval; break; default: GA::SERVICES.error ((char *)"test_set_diagonal:wrong data type.", type); } if (me == 0) printf ("Testing GA_Set_diagonal..."); g_a->zero (); g_v->fill (val); g_a->setDiagonal (g_v); switch (type) { case C_INT: idot = vdims[0] * ival * ival; iresult = g_a->idot (g_a); if (me == 0) { if (MISMATCHED (idot, iresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_set_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_LONG: ldot = ((long) vdims[0]) * lval * lval; lresult = g_a->ldot (g_a); if (me == 0) { if (MISMATCHED (ldot, lresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_set_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_FLOAT: fdot = ((float) vdims[0]) * fval * fval; fresult = g_a->fdot (g_a); if (me == 0) { if (MISMATCHED (fdot, fresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_set_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_DBL: ddot = ((double) vdims[0]) * dval * dval; dresult = g_a->ddot (g_a); if (me == 0) { if (MISMATCHED (ddot, dresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_set_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; case C_DCPL: zdot.real = ((double) vdims[0]) * (dcval.real * dcval.real - dcval.imag * dcval.imag); zdot.imag = ((double) dims[0]) * (2.0 * dcval.real * dcval.imag); zresult = g_a->zdot (g_a); if (me == 0) { if (MISMATCHED (zdot.real, zresult.real) || MISMATCHED (zdot.imag, zresult.imag)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_set_diagonal:mismatched.", type); } else { printf ("ok.\n"); } } break; default: GA::SERVICES.error ((char *)"test_set_diagonal:wrong data type:", type); } } void test_shift_diagonal (GA::GlobalArray *g_a) { int me = GA_Nodeid (); void *val; int ival = -2; double dval = -2.0; float fval = -2.0; long lval = -2; DoubleComplex dcval; int idot, iresult, ldot, lresult; double fdot, ddot, fresult, dresult; DoubleComplex zdot, zresult; int type, ndim, dims[MAXDIM]; int dim; /*the length of the diagonal */ g_a->inquire (&type, &ndim, dims); dim = GA_MIN (dims[0], dims[1]); dcval.real = -2.0; dcval.imag = -0.0; switch (type) { case C_INT: val = (void *)&ival; break; case C_DCPL: val = (void *)&dcval; break; case C_DBL: val = (void *)&dval; break; case C_FLOAT: val =(void *) &fval; break; case C_LONG: val =(void *) &lval; break; default: GA::SERVICES.error ((char *)"test_shift_diagonal:wrong data type.", type); } if (me == 0) printf ("Testing GA_Shift_diagonal..."); g_a->zero (); g_a->shiftDiagonal (val); switch (type) { case C_INT: idot = dim * ival * ival; iresult = g_a->idot (g_a); if (me == 0) { if (MISMATCHED (idot, iresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_shift_diagonal:mismatched.", 1); } else { printf ("ok.\n"); } } break; case C_LONG: ldot = ((long) dim) * lval * lval; lresult = g_a->ldot (g_a); if (me == 0) { if (MISMATCHED (ldot, lresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_shift_diagonal:mismatched.", 1); } else { printf ("ok.\n"); } } break; case C_FLOAT: fdot = ((float) dim) * fval * fval; fresult = g_a->fdot (g_a); if (me == 0) { if (MISMATCHED (fdot, fresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_shift_diagonal:mismatched.", 1); } else { printf ("ok.\n"); } } break; case C_DBL: ddot = ((double) dim) * dval * dval; dresult = g_a->ddot (g_a); if (me == 0) { if (MISMATCHED (ddot, dresult)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_shift_diagonal:mismatched.", 1); } else { printf ("ok.\n"); } } break; case C_DCPL: zdot.real = ((double) dim) * (dcval.real * dcval.real - dcval.imag * dcval.imag); zdot.imag = ((double) dim) * (2.0 * dcval.real * dcval.imag); zresult = g_a->zdot (g_a); if (me == 0) { if (MISMATCHED (zdot.real, zresult.real) || MISMATCHED (zdot.imag, zresult.imag)) { printf ("not ok.\n"); GA::SERVICES.error ((char *)"test_shift_diagonal:mismatched.", 1); } else { printf ("ok.\n"); } } break; default: GA::SERVICES.error ((char *)"test_shift_diagonal:wrong data type: ", type); } } void do_work (int type, int op) { GA::GlobalArray *g_a, *g_b, *g_c, *g_m, *g_v; int n = N; int dims[2] = { N, /*N columns */ N + 2 /*N+2 rows */ }; int vdim; int lo[2], hi[2]; lo[0] = 1; hi[0] = dims[0] - 1; lo[1] = 1; hi[1] = dims[1] - 1; switch (op) { case OP_SHIFT_DIAGONAL: g_a = GA::SERVICES.createGA (type, 2, dims, (char *)"A", NULL); test_shift_diagonal (g_a); g_a->destroy(); break; case OP_SET_DIAGONAL: g_a = GA::SERVICES.createGA (type, 2, dims, (char *)"A", NULL); /*find out the diagonal length of the matrix A */ vdim = GA_MIN (dims[0], dims[1]); g_v = GA::SERVICES.createGA (type, 1, &vdim, (char *)"V", NULL); test_set_diagonal (g_a, g_v); g_a->destroy (); g_v->destroy (); break; case OP_ADD_DIAGONAL: g_a = GA::SERVICES.createGA (type, 2, dims, (char *)"A", NULL); /*find out the diagonal length of the matrix A */ vdim = GA_MIN (dims[0], dims[1]); g_v = GA::SERVICES.createGA (type, 1, &vdim, (char *)"V", NULL); test_add_diagonal (g_a, g_v); g_a->destroy (); g_v->destroy (); break; case OP_GET_DIAGONAL: g_a = GA::SERVICES.createGA (type, 2, dims, (char *)"A", NULL); /*find out the diagonal length of the matrix A */ vdim = GA_MIN (dims[0], dims[1]); g_v = GA::SERVICES.createGA (type, 1, &vdim, (char *)"V", NULL); test_get_diagonal (g_a, g_v); g_a->destroy (); g_v->destroy (); break; case OP_NORM1: g_a = GA::SERVICES.createGA (type, 2, dims, (char *)"A", NULL); if (!g_a) GA_Error ((char *)"create failed: A", n); test_norm1 (g_a); g_a->destroy (); break; case OP_NORM_INFINITY: g_a = GA::SERVICES.createGA (type, 2, dims,(char *) "A", NULL); test_norm_infinity (g_a); g_a->destroy (); break; case OP_MEDIAN: g_a = GA::SERVICES.createGA (type, 2, dims, (char *)"A", NULL); /*duplicate g_a */ g_b = GA::SERVICES.createGA (g_a, (char *)"B"); g_c = GA::SERVICES.createGA (g_a, (char *)"C"); #if 0 //test g_m is different from g_a, g_b, amd g_c g_m = GA::SERVICES.createGA (g_a, (char *)"M"); test_median (g_a, g_b, g_c, g_m); #else //test g_m = g_c test_median (g_a, g_b, g_c, g_a); #endif g_a->destroy (); g_b->destroy (); g_c->destroy (); #if 0 //test g_m is different from g_a, g_b, g_c g_m->destroy (); #endif break; case OP_MEDIAN_PATCH: g_a = GA::SERVICES.createGA (type, 2, dims, (char *)"A", NULL); /*duplicate g_a */ g_b = GA::SERVICES.createGA (g_a, (char *)"B"); g_c = GA::SERVICES.createGA (g_a, (char *)"C"); g_m = GA::SERVICES.createGA (g_a, (char *)"M"); test_median_patch (g_a, lo, hi, g_b, lo, hi, g_c, lo, hi, g_m, lo, hi); g_a->destroy (); g_b->destroy (); g_c->destroy (); g_m->destroy (); break; case OP_SCALE_ROWS: g_a = GA::SERVICES.createGA (type, 2, dims, (char *)"A", NULL); /*find out the diagonal length of the matrix A */ vdim = dims[0]; g_v = GA::SERVICES.createGA (type, 1, &vdim, (char *)"V", NULL); test_scale_rows (g_a, g_v); g_a->destroy (); g_v->destroy (); break; case OP_SCALE_COLS: g_a = GA::SERVICES.createGA (type, 2, dims, (char *)"A", NULL); /*find out the diagonal length of the matrix A */ vdim = dims[1]; g_v = GA::SERVICES.createGA (type, 1, &vdim, (char *)"V", NULL); test_scale_cols (g_a, g_v); g_a->destroy (); g_v->destroy (); break; default: GA::SERVICES.error ((char *)"test_function: wrong operation.", op); } } int main(int argc, char *argv[]) { int me, nproc; int heap = 200000, stack = 200000; int op; GA::Initialize(argc, argv, heap, stack, GA_DATA_TYPE, 0); me=GA_Nodeid(); nproc=GA_Nnodes(); if(!me) cout << "Using " << nproc << " processes\n"; for (op = 1; op < 11; op++) { if(me == 0) printf ("\n\n"); if (me == 0) printf ("type = C_INT \t "); do_work (C_INT, op); if (me == 0) printf ("type = C_LONG \t "); do_work (C_LONG, op); if (me == 0) printf ("type = C_FLOAT \t "); do_work (C_FLOAT, op); if (me == 0) printf ("type = C_DBL \t "); do_work (C_DBL, op); if (me == 0) printf ("type = C_DCPL \t "); do_work (C_DCPL, op); } if(!me) cout << "Terminating\n"; GA::Terminate(); } ga-5.9.2/ga++/testing/ntestc.cc000066400000000000000000000144651500715745200161370ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include using namespace std; #include "ga++.h" #define N 10 // First dimension #define NDIM 4 // Number of dimensions #define BASE 0 #define PERMUTE_ #define GA_DATA_TYPE MT_F_REAL /*\ print subscript of ndim dimensional array with two strings before and after \*/ void print_subscript(char *pre,int ndim, int subscript[], char* post) { int i; printf("%s [",pre); for(i=0;ihandle()); /* print info about array we got */ g_a->inquire(&type, &ndim, adims); if(me==0)printf("After Inquire\n"); g_a->printDistribution(); GA::SERVICES.sync(); /* duplicate array A with ga_create irreg rather than ga_duplicate * -- want to show distribution control * -- with ga_duplicate it would be g_b=GA_Duplicate(g_a,name) */ if(me==0)printf("\nReconstructing distribution description for A\n"); /* get memory for arrays describing distribution */ proclist = (int*)malloc(nproc*sizeof(int)); if(!proclist)GA_Error((char *)"malloc failed for proclist",0); regions = (patch_t*)malloc(nproc*sizeof(patch_t)); if(!regions)GA_Error((char *)"malloc failed for regions",0); map = (int*)malloc((nproc+ndim)*sizeof(int)); /* ubound= nproc+mdim */ if(!map)GA_Error((char *)"malloc failed for map",0); /* first find out how array g_a is distributed */ for(i=0;ilocateRegion(lo, hi, (int*)regions, proclist); if(proc<1) GA_Error((char *)"error in NGA_Locate_region",proc); /* determine blocking for each dimension */ for(i=0;iadims[d] ){ map[offset] = regions[i].lo[d]; offset++; block[d]++; adims[d]= regions[i].hi[d]; } if(me==0){ printf("Distribution map contains %d elements\n",offset); print_subscript((char *)"number of blocks for each dimension", ndim,block,(char *)"\n"); print_subscript((char *)"distribution map",offset,map,(char *)"\n\n"); fflush(stdout); } if(me==0)printf("Creating array B applying distribution of A\n"); # ifdef USE_DUPLICATE GA::GlobalArray *g_b = GA::SERVICES.createGA(g_a, "array B"); # else GA::GlobalArray *g_b = GA::SERVICES.createGA(MT_F_DBL, NDIM, dims, (char *)"array B", block, map); # endif if(!g_b) GA_Error((char *)"create failed: B",0); if(me==0)printf("OK\n\n"); free(proclist); free(regions); free(map); g_b->printDistribution(); GA::SERVICES.sync(); if(me==0){ printf("\nCompare distributions of A and B\n"); if(g_a->compareDistr(g_b)) printf("Failure: distributions NOT identical\n"); else printf("Success: distributions identical\n"); fflush(stdout); } if(me==0){ printf("\nAccessing local elements of A: set them to the owner process id\n"); fflush(stdout); } GA::SERVICES.sync(); g_a->distribution(me, lo, hi); if(hi[0]>=0){/* -1 means no elements stored on this processor */ double *ptr; int locdim[NDIM]; g_a->access(lo, hi, &ptr, ld); for(i=0;i=0){ char msg[100]; sprintf(msg,(char *)"%d: leading dimensions",me); print_subscript(msg,ndim-1,ld,(char *)"\n"); fflush(stdout); } GA::SERVICES.sync(); } GA::SERVICES.sync(); if(me==0)printf("\nRandomly checking the update using ga_get on array sections\n"); GA::SERVICES.sync(); /* show ga_get working and verify array updates * every process does N random gets * for simplicity get only a single row at a time */ srand(me); /* different seed for every process */ hi[ndim-1]=adims[ndim-1] -1 + BASE; for(i=1;iget(lo, hi, buf, ld); /* check values */ for(i=0;ilocate(lo); if((double)p != buf[i]) { char msg[100]; sprintf(msg,(char *)"%d: wrong value: %d != %f a",me, p, buf[i]); print_subscript(msg,ndim,lo,(char *)"\n"); GA_Error((char *)"Error - bye",i); } lo[ndim-1]++; } } free(buf); GA::SERVICES.sync(); if(me==0)printf("OK\n"); g_a->destroy(); g_b->destroy(); if(!me) cout << "Terminating\n"; GA::Terminate(); } ga-5.9.2/ga++/testing/testc.cc000066400000000000000000000050041500715745200157460ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include using namespace std; #include "ga++.h" #define N 300 #define GA_DATA_TYPE MT_F_REAL /* using std::cout; using std::printf; using std::sin; using std::endl; */ int main(int argc, char *argv[]) { int ONE=1; /* useful constants */ int n=N, type=MT_F_DBL; int me, nproc; int i, row; int dims[2]={N,N}; int lo[2], hi[2]; /* Note: on all current platforms DoublePrecision == double */ double buf[N], err, alpha, beta; int heap = 200000; int stack = 200000; GA::Initialize(argc, argv, heap, stack, GA_DATA_TYPE, 0); me = GA_Nodeid(); nproc = GA_Nnodes(); if(me==0)printf("Number of processors: %d\n\nCreating matrix A\n", nproc); GA::GlobalArray *g_a = GA::SERVICES.createGA(type, 2, dims, (char *)"A", NULL); if(me==0)printf("\nOK\n"); if(me==0)printf("\nCreating matrix B\n"); /* create matrix B so that it has dims and distribution of A*/ GA::GlobalArray *g_b = GA::SERVICES.createGA(g_a, (char *)"B"); if(me==0)printf("\nOK\n"); g_a->zero(); /* zero the matrix */ if(me==0)printf("\nInitializing matrix A\n"); /* fill in matrix A with random values in range 0.. 1 */ lo[1]=0; hi[1]=n-1; for(row=me; rowput(lo, hi, buf, &n); } // g_a->print(); if(me==0)printf("\nSymmetrizing matrix A\n"); g_a->symmetrize(); /* symmetrize the matrix A = 0.5*(A+A') */ /* check if A is symmetric */ if(me==0)printf("\nChecking if matrix A is symmetric\n"); g_a->transpose(g_b); /* B=A' */ alpha=1.; beta=-1.; g_b->add(&alpha, g_a, &beta, g_b); /* B= A - B */ err= g_b->ddot(g_b); if(me==0)printf("\nError=%f\n",(double)err); if(me==0)printf("\nChecking atomic accumulate \n"); g_a->zero(); /* zero the matrix */ for(i=0; iacc(lo, hi, buf, &ONE, &alpha ); GA::SERVICES.sync(); if(me==0){ /* node 0 is checking the result */ g_a->get(lo, hi, buf,&n); for(i=0; idestroy(); g_b->destroy(); if(me==0) cout << "Terminating...\n"; GA::Terminate(); } ga-5.9.2/ga++/testing/testmult.cc000066400000000000000000000117651500715745200165200ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include /* utilities for GA test programs */ #if HAVE_MATH_H # include #endif #define N 256 /* first dimension */ #define BASE 0 #define PERMUTE_ #define GA_DATA_TYPE MT_C_FLOAT #define GA_ABS(a) (((a) >= 0) ? (a) : (-(a))) #define TOLERANCE 0.000001 #ifdef MSG_COMMS_MPI #include #define CLOCK_ MPI_Wtime #else #include "tcgmsg.h" #define CLOCK_ tcg_time #endif DoublePrecision gTime=0.0, gStart; void test(int data_type, int ndim) { int me=GA_Nodeid(); GA::GlobalArray *g_a, *g_b, *g_c, *g_A, *g_B, *g_C; int dims[GA_MAX_DIM]={N,N,2,2,2,1,1}; int lo[GA_MAX_DIM]={1,1,1,1,1,0,0}; int hi[GA_MAX_DIM]={N-2,N-2,1,1,1,0,0}; int clo[2], chi[2], m, n, k; double value1_dbl = 2.0, value2_dbl = 2.0; double alpha_dbl = 1.0, beta_dbl = 0.0; float value1_flt = 2.0, value2_flt = 2.0; float alpha_flt = 1.0, beta_flt = 0.0; DoubleComplex value1_dcpl = {2.0, 2.0}, value2_dcpl = {2.0, 2.0}; DoubleComplex alpha_dcpl = {1.0, 0.0} , beta_dcpl = {0.0, 0.0}; void *value1, *value2, *alpha, *beta; char name_a[] = "array A"; char name_b[] = "array B"; char name_c[] = "array C"; char name_a_[] = "array A_"; char name_b_[] = "array B_"; char name_c_[] = "array C_"; switch (data_type) { case C_FLOAT: alpha = (void *)&alpha_flt; beta = (void *)&beta_flt; value1 = (void *)&value1_flt; value2 = (void *)&value2_flt; if(me==0) printf("Single Precision: Testing GA_Sgemm,NGA_Matmul_patch for %d-Dimension", ndim); break; case C_DBL: alpha = (void *)&alpha_dbl; beta = (void *)&beta_dbl; value1 = (void *)&value1_dbl; value2 = (void *)&value2_dbl; if(me==0) printf("Double Precision: Testing GA_Dgemm,NGA_Matmul_patch for %d-Dimension", ndim); break; case C_DCPL: alpha = (void *)&alpha_dcpl; beta = (void *)&beta_dcpl; value1 = (void *)&value1_dcpl; value2 = (void *)&value2_dcpl; if(me==0) printf("Double Complex: Testing GA_Zgemm,NGA_Matmul_patch for %d-Dimension", ndim); break; default: GA::SERVICES.error("wrong data type", data_type); } g_a = GA::SERVICES.createGA(data_type, ndim, dims, name_a, NULL); g_b = GA::SERVICES.createGA(g_a, name_b); g_c = GA::SERVICES.createGA(g_a, name_c); g_a->fill(value1); g_b->fill( value2); g_c->zero(); /** g_c = g_a * g_b */ g_c->matmulPatch('N', 'N', alpha, beta, g_a, lo, hi, g_b, lo, hi, lo, hi); g_a->destroy(); g_b->destroy(); /** * Verifying g_c: * 1. Create g_A(=g_a) and g_B(=g_b) * 2. g_C = g_A*g_B; (Using Gemm routines) * 3. g_A = g_c; (copy the 2-d patch og g_c into g_A) * 4. g_C = g_A - g_C; (Using add() routine by making beta=-1.0) * 5. If all the elements in g_C is zero, implies SUCCESS. */ dims[0] = dims[1] = m = n = k = N-2; g_A = GA::SERVICES.createGA(data_type, 2, dims, name_a_, NULL); g_B = GA::SERVICES.createGA(g_A, name_b_); g_C = GA::SERVICES.createGA(g_A, name_c_); g_A->fill(value1); g_B->fill(value2); g_C->zero(); gStart = CLOCK_(); switch (data_type) { case C_FLOAT: g_C->sgemm('N', 'N', m, n, k, alpha_flt, g_A, g_B, beta_flt); beta_flt = -1.0; break; case C_DBL: g_C->dgemm('N', 'N', m, n, k, alpha_dbl, g_A, g_B, beta_dbl); beta_dbl = -1.0; break; case C_DCPL: g_C->zgemm('N', 'N', m, n, k, alpha_dcpl, g_A, g_B, beta_dcpl); beta_dcpl.real = -1.0; break; default: GA::SERVICES.error("wrong data type", data_type); } gTime += CLOCK_()-gStart; g_B->destroy(); clo[0] = clo[1] = 0; chi[0] = chi[1] = N-3; g_A->copyPatch('N', g_c, lo, hi, clo, chi) ; g_C->add(alpha, g_A, beta, g_C); switch (data_type) { case C_FLOAT: value1_flt = g_C->fdot(g_C); if(value1_flt != 0.0) GA::SERVICES.error("GA_Sgemm, NGA_Matmul_patch Failed", 0); break; case C_DBL: value1_dbl = g_C->ddot(g_C); if(value1_dbl != 0.0) GA::SERVICES.error("GA_Dgemm, NGA_Matmul_patch Failed", 0); break; case C_DCPL: value1_dcpl = g_C->zdot(g_C); if(value1_dcpl.real != 0.0 || value1_dcpl.imag != 0.0) GA::SERVICES.error("GA_Zgemm, NGA_Matmul_patch Failed", 0); break; default: GA::SERVICES.error("wrong data type", data_type); } if(me==0) printf("....OK\n"); g_A->destroy(); g_c->destroy(); g_C->destroy(); } void do_work() { int i; int me = GA_Nodeid(); for(i=2; i<=GA_MAX_DIM; i++) { test(C_FLOAT, i); test(C_DBL, i); test(C_DCPL, i); if(me == 0) printf("\n\n"); GA::SERVICES.sync(); } } int main(int argc, char **argv) { Integer heap=9000000, stack=9000000; int me; DoublePrecision time; GA::Initialize(argc, argv, heap, stack, GA_DATA_TYPE, 0); me=GA_Nodeid(); time = CLOCK_(); do_work(); #ifdef TIME printf("%d Total Time = %lf\n", me, CLOCK_()-time); printf("%d GEMM Total Time = %lf\n", me, gTime); #endif if(me==0)printf("\nSuccessfull\n\n"); GA::Terminate(); return 0; } ga-5.9.2/ga++/testing/thread-safe.cc000066400000000000000000000264431500715745200170210ustar00rootroot00000000000000#include "mpi.h" #include #include "ga.h" #include #include #include #if defined(_OPENMP) #include "omp.h" #endif //#define N 500 #define N 5 int main(int argc, char * argv[]) { #if defined(_OPENMP) int x = N; int y = N; int return_code = 0; int dims[2] = {N,N}; int lo[2] = {0,0}; int hi[2] = {N-1,N-1}; int ld[1] = {N}; int local_buffer[N][N]; int local_buffer2[N*N]; char name[20]; int rank, ranks; int provided; int stop, start; int writers; int handle, thread_count; MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); GA_Initialize(); rank = GA_Nodeid(); ranks = GA_Nnodes(); if (provided < MPI_THREAD_MULTIPLE && rank == 0) { printf("MPI_THREAD_MULTIPLE not provided\n"); } if(argc >= 3) { x = atoi(argv[1]); y = atoi(argv[2]); } for(int i =0; i #endif #include "typesf2c.h" static DoublePrecision gai_drand_(Integer *flag) { if (*flag) srandom((unsigned) *flag); return ((DoublePrecision) random()) * 4.6566128752458e-10; } #define drand_ F77_FUNC(drand,DRAND) DoublePrecision drand_(Integer *flag) { return (gai_drand_(flag)); } ga-5.9.2/gaf2c/farg.F000066400000000000000000000005341500715745200141360ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif integer function f2c_iargc() FXX_MODULE f2c_iargc = F77_IARGC() + 1 return end subroutine f2c_getarg( i, s ) FXX_MODULE integer i, l, ier character *(*) s F77_GETARG_DECL call F77_GETARG(F77_GETARG_ARGS) return end ga-5.9.2/gaf2c/farg.h.in000066400000000000000000000007551500715745200146120ustar00rootroot00000000000000#ifndef FARG_H_ #define FARG_H_ #include "typesf2c.h" #define F2C_GETARG @F2C_GETARG@ #define F2C_IARGC @F2C_IARGC@ #define F2C_GETARG_ARGV_MAX 255 #define F2C_GETARG_ARGLEN_MAX 255 extern void F2C_GETARG(Integer*, char*, int); extern Integer F2C_IARGC(); extern void ga_c2fstring(char *cstring, char *fstring, int flength); extern void ga_f2cstring(char *fstring, int flength, char *cstring, int clength); extern void ga_f2c_get_cmd_args(int *argc, char ***argv); #endif /* FARG_H_ */ ga-5.9.2/gaf2c/gaf2c.c000066400000000000000000000064031500715745200142370ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STRING_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #include "farg.h" #include "message.h" /** * Converts C strings to Fortran strings. * * @param cstring C buffer * @param fstring Fortran string * @param flength length of fstring */ void ga_c2fstring(char *cstring, char *fstring, int flength) { int clength = strlen(cstring); /* remove terminal \n character if any */ if(cstring[clength] == '\n') { clength--; } /* Truncate C string into Fortran string */ if (clength > flength) { clength = (int)flength; } /* Copy characters over */ flength -= clength; while (clength--) { *fstring++ = *cstring++; } /* Now terminate with blanks */ while (flength--) { *fstring++ = ' '; } } /** * Converts Fortran strings to C strings. * * Strip trailing blanks from fstring and copy it to cstring, * truncating if necessary to fit in cstring, and ensuring that * cstring is NUL-terminated. * * @param fstring Fortran string * @param flength length of fstring * @param cstring C buffer * @param clength max length (including NUL) of cstring */ void ga_f2cstring(char *fstring, int flength, char *cstring, int clength) { /* remove trailing blanks from fstring */ while (flength-- && fstring[flength] == ' ') ; /* the postdecrement above went one too far */ flength++; /* truncate fstring to cstring size */ if (flength >= clength) { flength = clength - 1; } /* ensure that cstring is NUL-terminated */ cstring[flength] = '\0'; /* copy fstring to cstring */ while (flength--) { cstring[flength] = fstring[flength]; } } void ga_f2c_get_cmd_args(int *argc, char ***argv) { Integer i=0; int iargc=F2C_IARGC(); char **iargv=NULL; if (iargc > F2C_GETARG_ARGV_MAX) { printf("ga_f2c_get_cmd_args: too many cmd line args"); armci_msg_abort(1); } iargv = (char**)malloc(sizeof(char*)*F2C_GETARG_ARGV_MAX); if (!iargv) { printf("ga_f2c_get_cmd_args: malloc iargv failed"); armci_msg_abort(1); } for (i=0; i #endif /** @file * This checks the functioning of the include file gaf2cp.h. */ #include "farg.h" #include "typesf2c.h" #define ARGLIMIT 256 #define PARG_ F77_FUNC(parg,PARG) void PARG_() { Integer i; Integer limit = F2C_IARGC(); printf("argc=%d\n", (int)limit); for (i=0; i about PEIGS) - BLAS library is required for the eigensolver and ga_dgemm; - LAPACK library is required for the eigensolver; - MPI, SCALAPACK, PBBLAS, and BLACS libraries are required for ga_lu_solve, ga_cholesky, ga_llt_solve, ga_spd_invert, ga_solve. If you do not install these libs, the named operations will not be available. BUILDING GA =========== Please refer to the README in the base distribution directory. TEST PROGRAMS ============= Please refer to the README in the base distribution directory for details on running the GA test suite. TEST PROGRAM NOTES ================== - Program testspd.x should work only with the ScaLAPACK and MPI. - Another example program that uses GA is called jacobi.x. The Jacobi iterative method is used to solve a system of linear equations generated in the solution process of a partial differential equation by the finite differences method. - To run test programs with TCGMSG that creates the GA processes on the shared memory and (network of) workstations; execute them you should use 'parallel' program -- built in tcgmsg/ipcv4.0:: parallel testing/test.x For a single processor execution 'parallel' is not required and the program can be run as:: testing/test.x or under control of your favorite debugger. DOCUMENTATION ============= The documentation is located in doc/ and on the web at: https://hpc.pnl.gov/globalarrays CONTACT ======= For suggestions, comments and bug reports: https://github.com/GlobalArrays/ga/issues ga-5.9.2/global/X/000077500000000000000000000000001500715745200135735ustar00rootroot00000000000000ga-5.9.2/global/X/README000066400000000000000000000023361500715745200144570ustar00rootroot00000000000000xregion -- GA visualization tool ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ xregion displays transitional access patterns for a two-dimensional array. Both sampling time for the animation and slowdown factor can be specified to animate long- and short- lasting events. At the end of animation, a cumulative time lost due to contention in access to individual array elements is displayed. The color coding (from white to navy) reflect access contention level for the animation, and the access contention integral for the final display. Usage ===== The program was tested on the SUN and SGI platforms only ! It is a 16-bit display version. :: xregion [Xtoolkit options] where: filename - name of the input file (data processed by 'adjust' program) The file must contain: .... .... num_events - the number of events to be read and processed from filename (might be specified larger than teh actual number records) Example ======= xregion adjust.ed ga-5.9.2/global/X/adjust.ed.Z000077500000000000000000004160341500715745200156220ustar00rootroot000000000000001`Àp` 7räP „ 3f(°a¢DŠ Î HÐ`GŽ1ÒÈCAÈ!‚ôR†  5®œ²EK4h@òcJ–oÈÐ)ó'MŽ2nÔ¸ÁçQ•)âÈCA §F³"U¸ðVŸ`Oƪ€ÆW¨ayÎQríY–@gИQuÆÉ§qkä¨aòíQ›äÒm #O²ªüÖÛÝ+,y½=ˆ—8FCQa%Ø\&É6G8ÁÀT ¿-’ 5¨WÖy€Q”DfQgà ÑÍåŸ S-´›7ô&‘[æ 1 hRi¾8TU›¡H—N3LW£R6H¤R‹QÝ"÷˜Ù 8ÐÕJ)]E“‹1Ôa¼u&š‚hU’ˆÕ¤ YÇ%|Ù…aH8V\sDrÔ¦^V)ˆ_T° eafVbI5øx'G’a4˜9D¤ `bVSH‰z٢ʙg9PXÑhˆNõÐ v*ù(TåÀTNˆšÊ[þCˆÇ úÓ 0¡¬1JÙ•¸„SU•­Ù‘ ‰2ti¯ÉP¨}FP¡ñé(H1éYœ€qvN¶ ¡u **µ›ÝÆÚ¸)U U¼¢KÐX4ð%íîr–ü;AI”iAX«‰â6 [FB$íS ÖäDô œÒuÄ­&,„’-d«v-Ìì´ÙÊ¥ç­Q"# ÌqJ4àÄ”qÃÃBÇŽ›íh2kòiû&*Q¬!Ó6ÙH&nŒóXœz®ÀÙÞ´I…ËMŒÃf%Ý×s¿$UÙ¶)e*PYk[08ù²°lÁW„hÃT%¯W'6˜u É…ö`øŠÈµC¢±UVÌH·D&l¼/ÂÛâðЫhoFVŽ{5ÚƒXÅíÑØÖ­õd¢ÉEÚÍb·4cC.ˆy} 5ºCAVf%Ú6<ÍP¬2·$”“K¡½BV5|²T/óÈšå™Õld[€ÿNfå ½:™‹mvp'Áp]™ SÏSoJ1tôïvÅ( Õ‡ÓfŒ„Mº÷Zv­—<ÎkÁXí«5TšvUDJãØnýr‰ˆDXµ>FѪ,ôŸu`°(ßÁ/PÝ1ÉÿÀgά‡gåKK@´š k9. ó´·œ_EëƒÚPD*—ÁÄÐDÜzßê@•”ª¸äƒ™JÑD¾‡³ň4, Öꤞ¶‘_ºq·&ØCWf^Â*×RÊ’=´çeI1XçE†mqwÓ—A#œL„FƒNh²˜¿º&^SiŠ‘x9ÔŒçe9"ÂâE¬ºapŒº Òxp,ýq„Q0ˆØdôH]™ñttÑn6SðÌqéU†çÆää`@B" Ÿ#eEí’´P"ÊHŽŠ8Œê  éž± lŒL‹ur²ž6Ž23ݱ I ÷KñÐÅzB¥öÆ3’ºÀ­“âaoÊ2Kh¢géj$aV«FR\¾ÞeMª"¥ãÁjžêÂȸ½6Þ„¢[C’î,j±DÍ’diESÞ$¤"‚imŒÐÐÀ²é:^ºØhA 8ÞTDª8kRhtBL)y¶I‰â j§Ê¦¹P"MÕÞÔ„¢Ã2—·Ä¹—t|ʦÑD$;¨<.€øbIʶ0súä&¹Ë[b†iìE]>eÑÕ!ÄH:©ïq›ôÞ­™÷º°Q\%¹ZY6I†,g³-o÷¢¶)˜$Ö[Tg[HªD¾×nÈ]õö_œIf9§²®‡R«o†—Â÷¢ÕÚ†zÝ{¥pìMÔlãiHÀ$ê¾4¦k¢2U·åz؇å$ðy!•¥.Æ»9Èël%o‰É‚)O}µç5×i´Ã«ËThVdO,ŠX$‚ˆˆ³¬´Á¤ôÁF²–K¬¸ÎÌ”Íò^ŒIâ7FM¤Ú‹7Õçe+þD fFª‚ ²¦²· òm̑ؑW…ˆ¡×uUi"äH[od<´õ–òâ­lÖ[¤?í±6Õ³Õ4=8h.G„šÖë [ º°¸ÏyÏÎÆ¬=_ÅhU>^¯àóǶšXY¼u@ǘ­dõh²Ë¾çOŒt×̶šI„mŸÃ)+ešVzYÛê©(9¶­ÖÙzºíÌÍzLÑí¶]T4œ,¸ívÖ»‰»hzsf1ôÖAÈlTû+‘Ðí·¾EcÆ1œVÓ\ä‰ñÌ™ðÔÌVŒ$Þyë{.߯·ÁãU¶õNœÑ„ aØsrwW†ÓÑ/…9SÁ”)@æÌN‰4‰s”5:þÍ·ÁS4\¶\ß_œÈg~W­ÜÓù¦Bâ•L,_Mã:«Û¨£ž®Lu†ÃBŸ9[Øò‡‹½7S3±Õ3þ0Š=HÙ¨Œ`¾Ãî¸F»ìîõu¬ ‘‚‹]«ëiÕu¾e’JãB=ZuÏÝ=tëq7`1Nñ¾)9wGØRTså°W^0]üèÚsž0bAÍì•¿·–žú&IOᛚ™ßñ©£!Ê$(hcên°K™¤nz£u›¿sZ¶(ˆ¦5JØ¡}TŠ5®ö}7ɧA$Ñirê,þyž1²¨ Q1 ò~š-÷#«bŸ4z¨·çv*2‰Rd³Ö¦Œ *·”Ïi©”QšZ˜¼ç‡7´ ®ÊLÅ–£›ú2z¡IÖFª‘J"™mµs¨`6@3êªJ³!(È«]“§ê¤†ú2²wÅ ª¶6¶z§¸W0<¨¡dSq ð©Ï#Ô¥„5Ö‡t4ÅŽv•j®•á '«8£®t–…ò®Û"$‰¦cc=ÄÒRìEvI(ëGvÌSàª=dgZµ¨eHv;_÷ª°®Æzè°áÄŒ2HÅi®Ö¡,ÉI˜Ýá—A—ª«)Ó‰±.R”, û°½Á…ÃÔ®„I"çÉDáªz²§›J0ž ³FbF`g²„Ù%`Õªóê•¿ºŸ:*l¨+2­ÿµ/³b4µ9±(׺´ïø9Y«aê¬ :6¾b-´[4ê4a;[ÄU¨eÛœÑ&-õ¡ÊãxAÕyF‹7$R?j¶ÅÈe%êYƒ±¼3¤j»²¯²ygÆ«‚±k¹t;U2¥}ûkç‰S”ëïʉx;¯ü8©]J¥zé¹¢9×¹/á7lª²Pû32%·yK,J*…Kaƒ"‚*‹»FÅd›·Š“7´+ºm"#eõ»ÝxweóO<›·{á ”z¯†K6:Ä“}«±WJ&YK0ÅÂWÌ;6xsW¨:š×{<•€;/ór@±Ú¾ó²%alòK&굫°Û¼P¹.Ú¶¿ä{L§²¯sË7Nø)Ã;¯’s@2À‚3T[z½tgZÀó"d®×{–ò°I±AäJWó¢4AÀq½J¡]•9Áº[3îk¯.Œgó2u‚¸ÆEd2†ÐÈEˆzšü´wñ¬»´2-ÕµUÛQïÅr3Ìhƒ7+ºK|Ô±±ñÒ»£ñDœÂF<°ä•ÀK¼ê5‰Tj¸üC~˜¸ÆFœ%½ój½VÚÆÁ@ûÄÅc5óaœÃÆÑ&&¢´KžS›»¤‡[²È̫ǥ ,ƒ¼Äd"JÌÆ¥µ28¼ÄScsоFLÿ™¶~פKvÊH1 ·Œ Å(›Bņ‹„ŒÁ·¢LÔ4HêØ‘a‰}rQ™¬»Ãu@ŠËÊ-Ò<±\ÒͲÝàºXIT¬°é´Õ¥ë§›îÛRÔÝà×áºÇMâ¶§>6 ò®Š9}xKÿ,ÖAA仆ÍArÍà+KQ ª’Îâßè’Ò3™ãÌdj»ÜS½=…?c,^A'ÝÚ=~–1ÈãP»›Ó¨ÉâÖY7#^­M³þêvªuc“5îç´Ú^_S ®Žëd 9úá’±Î=Òì¨\1ÖÊŸÈï¢qä=ÎèN&â¶çnïɵÕO¶ïè*hå¢ ñaXTËïjC¸`Ù×{$> Ý4¼E$f¿zñlNñooe’ìèNtÕÉü¾œ¹·ò-Aw,¦YAÎ=?uããƒÓ$"ÎóQ*šóºì ñB*óq! Ô/N*fo/®¬ÛóÊäåí¥®bÈe’_õâc" }ºGÜ&•Ôñcawõ¨gŸk•HJNéÛ³AÇ{XR^÷ûS6®—To÷®F8â{øƒÎbþÛ´’Å1æƒ(dqæÒ÷ÙA8÷ô«ö[ïU;Â'ö¸ÞQ0¸ÔJßQ#EE«O2?ÕŒÏw—^$ø´¯r öèÁ «]6å¤è.ä*Y´­™úá8nû¾ÿ,<…é´Ï¶³òÍO1°åá3Ëñ:ûÕo¥Ûÿ´Ñ?Ê’ê½/iÃëg SNÏŽÍß’ëÑÉô-JV^¢ï"êOòöŸçÑ{qHýb!ÿI¶ã]÷¸lOÿ5®s Oˆ@¼c´¼ò÷S²s€v!ex5–GEûøyÑnº?±0‰n+Cv%Ó§õ&­whø¡VÄýÄ‚RHPð€4%îFàD.߇÷ùÀŒf(ó ’ÕѼ!ˆ«œ ¯£€Å¨­²ûçÇÐY{P°(áŽ'8•aÑe"Pë®qãßT1uƒüuÁݵz0àüú‚KP î°ôÑþÄ ó BÐf& ¬´X ag5'Hç' ·À‚]°­ˆ¬‡ÿDƒêzàï$ø5ì€ã¨`7Z /a1ÅÁFhÀ "ƒ$D4Kèû’ÂÀ9r¤P‰ÖÐHH KPme ©¡%Ô œ.üÓeÁzX‹¬‘„l³·qB£Ç(F(²–Öx?ÈË¥°Œ¾™Í;·]HhxS}L)†¥RãQN`\˜ bUøDÆø;Ô?»Q6Ú1µ©£Ý³\®CßÇÎ*ÐÖq°Eæ€påˆ ·ÖÅ›ŽÅñ9’µ–²ããw ^âßuGûèãÅáRyû1;Çîð8~£il£á–¼<ô¸T^O€Ô‰j¨ÁG©õR%p(jÀ¶)$þ;Nܬ6zÇýHvcx¦ ò@ˆ) <[’D2‰,âðt#ø0D“ŠSdÈÖ¸ÐRÔŽ”Ed#±—„øZ !?IˆV ǡǩÀ;ÄãL‘»á1jôøIÂÙ‰ô2ÁQËÀ'š"LZQITRqÜ-©@Z–‰¨1’ö›…qeÆ#O(nêçGº4A~ô£Ä5{ÌA\ŠHx›“‹„o²qe*2 aÉ×Hfnr™0Ãg_ÚV[|ò}…BÉîNÂ/2EidŽl² Gä%´DÇËCÒÈ*†¨,§±;„2a ù£š<ÓìNÎ`2©âW£ÜcA†R¦ÊAuì×­ìwRV’t´šJ£«´”Ñl€tH± ÐA“:›<5J©A¾XщKò‹ é*}_‚ü.2([~’Åq%+å›Ô ±’UK–4†Êå°Ä lEBAZÖÄòΜŒWŠM%Êqb-äM£$:«ÂXîÆTÙâ~ #zoòXņô§k¦ßø²”éÓE²„ùI虜ÔzÂAŒ:½WÈÆÊ÷ÓP…̯ùHY™*‘KUȈ°™ ”©&íÖʇŸ’žÜ0¿WÈ£“dË1W™"ìf÷ô”’ué;å;èg))Ú@£–(P/G³',“&UP'æá¾Éw”Qò`ÏT¢ðšò$ß'S^Bxis哌ˆðrš€Æë ;·¨›ÏóŽªÁ‰&(Íb²S5αG!¬ØãR°“%º!I!FÖòËD̃y.Ëi7mJ±9Ÿ6áĸ@PT s‡=‚BT› É,a§vk ÑÎÖæƒíâ ,³lV•ï¦+Í`ìp 6€ ¼35(]ø' Ôm«„4+'¨$ÿT¤!bäMî9I­QsŠ›¤t- ~ª9_Ëã§äµ<ÁÉSiß s?Ž“5jNßÒ`ÊbS= ®ÅÑÑ©Z-€ÅV{ŒîÒÓ~šB]ŽŠ£V¬À—êûÒ8«DÀ8™VIN#bo±’Æÿñ—)²É&Op=ϪnÓi£Ô#]½¨¾GñÕÊ'óSİžƒuuN U ”âTíGÅ]ºrB' åUý«KáòÖºªWD É«ûÃn¤ÉuR±j?[ü­R š!±â_M´±‹Á<á;EéEŪ6ʆLÑÏŠè¢Ô3ª­‡1±"ÂQ:^)p 0W ¨Ö{ñ^ž(qe’àh¸FVüGÆC]Ю6 "ªÈ–:çØ“ *bÉJ¶÷aÞYE†#<‚×ßʨ+¸h¤ÛuÈÍÁ¹Zk}=¡Ê彦H5E\iSõ}Ÿ½N:’ _½k1•®«“ÁèÔ±Ê^s粨X22$FX¶>OŸ©(FGÂrבRPb«|GŽj¡’ªB ö+ùÚKÇèàM½„ !vÄ“³U9qŠ(°À]" ¸BÉQ±eÈ•aÂ/¡[]ƒ@P~Ë'„%Wf!mlpe£2YI²*ŸÐNͯÝȇü¼ #e)Œ™GVVžhB§9PÛµB•¤,Wê|ß,?¨5ÌÖŠc…D쨰+ó¢œ8ÓeWØÇWä±\ö¬üTšàYìJ•ÅD–•Ú›i¡…7§-l L»bnŽœµ+™â;ˆÔ5;86ÅWÅu ÄTõYK+iËŒpYˆ6*€’HwhCéÁŒu+9:R\ —¶h` -)ð1Ø[ëj¢Ã£³>3âÇJôê,3…hš5œ`FzBMÖfŒzs¶~\[òƒÏázœCrH9åh=¢òV( øåÐíŒ<­~VÏÎÈI~†´¨· ‘ˆì®Xiåm_"—7Ùz%Fïø éJ JÜrYsC^”ö "#/çæ›KKŒŒ­<°èöÃÐVûgӓรrܺ Øfxlë€ø°ÊÅ·fØ5«ðEÍžT£2”‰±:eS Â(!c*^W# äe²M~ÏFß[Í@åq•…TCªÑž–ëº ÔÔº¤ÛŽ£4nµO)¯RUÒfC&w£®$± ×2ØZ 2Þê¦ý7f„¼¹ $”SU_Ƚ"dgv¢’Ëë·rãЭ³ŽG¾æYÐ{LÔ«š}A¡.˜Ø[ !Zœz /èx˜ÞžAzMq¼“7€ºÏ‹^…ub‘æÕnœ[ÒKnñ…kб:,ºû‹:…U¼¥ÔH@I£Ž\¯¨ôplj{EÐ!»fv ‘,{yI_AáFÅ÷oUð«|½ DFŽƒ6M<çJwg(Bk²ÉAüÍ@ö‹z S˜©é×?Uüg›à˜œ˜1£âÙÛw©‚@Ãxü 5Pv}Qíªe øh,ªÓ&“¯´µZåè­9¡ÞPP¤/¡-_äâz)Í@}ÊV»µ»«ä”àÄë#'pÁ’ö}J[˜íc"ªuv~ù#WH50ÄA!Ø …!ƒ•p°É‡½Kmr“¡ÁÂK¾K†5±ŠÂ4ø©ð9@¢“ÁŒŒÇ‰ÝÞ]{êž‹)ý0¬i!aŒôÒš<Ž‹²¿¢á?K&º :äòvyb䛾õ×42PÖbŸ"Úîdĵ)Äò[†Øã[åù²-irqÜËsÂõ­­…ž¡"ÌÜVëVe÷8EªðÏÍ ÷ke5¤úònÙZjN0g`«,†6Å•LÊtÃ2“_›<² àm´ñ©-X‹Lšõ ò-Š‹¹ý\3ðyHÞû>Ž(#ÜÂX“r(ôÌø2#„ ë—¤Uo”¡7×äã(^$6hÜ ®&ÙXB½¶/ÿ³ÉYP`ØØ D+ =MÊi•8#8ì|Øbqçz )CH dY;D2VD¯Ì‡që0þ‰ÜÙ c‘h•³qñÈŒ±?gÃ)ÈÿÔH¤x" 4Œ„ëô}+hNxšû´8K[‡¸3¨ñÙ«©ê áÀ$RÐ$‡dLžßFrríģϚØ|í¼œIÎ|#Ǩ>t×ýÜ„S´îË4!׎ïLˆ‰Ã!šÀwÒêLc73òó¹@ëèØ ¤ñs‹Î._*’Â\¿3ˆþEq$ááМQ(+ý—ãûC^úÏbV¬–¿´åʹMÈz¦³c™¶³žöCëi\Æ(µqC}ƒÉ>ÌXäEžöÓvä5 ;n±Ésƒ×5xW>-¡% rbÚPkê!Iq™®Fï¹’i;Ršï±ÔL͉!=ЦÆ5©9P´`§ðQ¤MŠfk+b=‡Ù@Asè›Þã#¯ #b]Í+²¨þÒöfåôii[Á¾q§–µ´ì?1aI:¬cǺØÖ0qJ“ˆÜ WuŠþ[-å$ßZú•„^Þ‹Òè#U?À7¹G8ß8º°ùJ‹·4x‰ þT‹y©à‘œÜåôH–”¬ù'/<¦!uŠNù+ éA'®õ”Ï%Tçks GK Þ؇zØ…äcÝ̤Ä~Òm"‹ødÖ2±ÃS¬äÏpd\-¾^]¯óD'ÅÆ)Úňár¸Å·OSÕ{‹¥Œ´´5õ_d•¼ÚRÖk?UI¨2ˆþHÏæ ³ì ¡†\þ³"íàשݦ„›Ö’d,™9°µ_4Õ¨¡›´¨•ÜH&J÷Ð6 [Ú~>÷£®ÍRW?{^³Œ¶Ý’ê—è´ºQ††:mŽ;5‰&nÈéyåî4™¶WÝE¸ 4»m³½ò™ŸörRå:hÓ6œ|%"7h° `ÍÀ"nœ NqŽ® A*9ÃãÚ°&=¯ÞöÕ)C;çäyHÚ§E —Ïk»£ïà#B³ùuÙ @B®ØuL=þŸö†êňHqëÕiaAìÚ\7%d2³Ã8|¦'O¨±v3k‰lç-¿µÜÌmqë½io”Eb_=J¾˜½»kIYµÑ㮡»*Lh•–xä·Ö…H½<¢ÖÉè{{ÓCmˆÛ‹™M1c;š_ªu±«Ö9ˆv1UÂ~ÍrI+cv8'çìuö¯!œânÐ[trÁi¥|²ãåð–^óSG ‘AìòU~c̆°ª3èK>.«óýﺠ sÞ¦+&›Þvz ïm·ybRŸï,c1ÿÅà¥Dk&Bœ‡Y—L½ã¸vïïn➯nüXAÔ·öïÇãïõ!òŽº0E×ಖ*¨f‘ŽXÁ;IØ€™[Úv]p!ƒ"w£Å=n Œ[o]"šŽOwìxFm¬ £ŽtÅIð *J=Mr‡K¯à()Qéƒf­Ix%¬ÝCÙˆœ8ÚxùѬßôõ-Mº¢ûxWÖ9‰ï–C§IIoùJÄ“ù+1àÙG…W‡DQ?!0±0VF1hŽV"Â<·ò|“¸ öë‚&Žác·Mì1a«f}6âÃÇ«Œx#ã÷vXBKåu<$®¸­e4hBÇþù2KzBš@Õ<¤¥ä‰+TÉà©M]•ÈYÐn滑ªÿUˆþw¨’Öå…›-®›õÚ…´wK'/¢ÖåõÀhTúNO2´çô˜Þ+•ÖNåpbÜôõC¦ZlÉŒþ9Í]Èèð!쑽‹Ñ$ K¹Ipß¶zûÖ•ÑCœÍUû75ãÍsÏíÍ^ìâctº|´¢ öѾîòêþà¿}ÝåÜ~Á¶úh¥’·m½µ¢ tx–!ým½{‰'EcÎüå<¤Éšy÷$G⬅oAÎ(½÷cA~ü[_·Ô ÷÷/#̨{¨ðèS/3ßð³Oz¿ÿõ~í•úËÛ¿’d Ö¢jW¯%âЕ|~V…©ÊÐ!3Ê>¯ˆ fuÝC_<4.Aµýþцþ6ÞG³´ºa ˆX+µ–d—¶¼áuÉ»‚'ÇczoØ_Êe½ŸG¹>Å“ÃÔY ü¹Ÿ YJâ@8¥Üßù‰qå4z>?B˜ÞgˆÄaÆ »ÿÕ7L¿ÓGÎ#5øFâǶ<(7££øh3w8géÆ÷{©ŸSÉ ³'ùæ¦TÿõZâu(C3Ï. ö}u÷ ±ÈJ“'ÊH(GdŠÏÄÇÛoö»Ke¿{>?/ Â.ûÕ¡øç2Ž¥-ÐîÿË™ãßäÆPVØÈÿ¦ýó%ÁwýÆÓ=ö¼å¿Å"í~Wß7–på_ÖÁ´tT¿—þ£ÙÒoõ´Ÿ™ð‡ßïÓ¿×èëcÅ.æàáý­_ã; ö±t wWä‡é%pŠIõYñ4Þ:d~{÷Ž@ÓâÁ¸_¿3ó Öô°!@~jß·×Q”àøçt¿JyÁš‰«Íñ¨=í«óÞ¡€ê a°z€î‹¨…%#¼„ “êu§Íy7BiBôfœ5#dÞËóÓ™~TL·y€ G³á-|žCÉ1€ì€¿Àrº,™âÕÕq TdãóQuПð¤ýt¯ˆaFxèß|fÌs­(\ 04…_!ǬZ;`ÊfÀ|ͨå‘Cˆ ñ5ˆ‰ x©}»E5b¬ B  å\'`5’\È>ö–{ d!‚ן8áC[çߣgW z”; 0quñ׿°Ý‘^é`ÜA”D6¡6dwD–PVwæ:’ P{À·edàŒW† #ÿÔá‡*zyØA¢ Ò‚§`j@Üe‚Ä êswµº`G±WÔ%¯„F—ÄsÁ8ñ ¦]ŸåÂ|1p`5¨mƒ‡C4¨´™$¨²!Þ3Ñ„ƒC†%h¹äJ—Ò‡™±ƒd «÷@;lÝsÀ…™ß0Øéq!êE@(D,|þQáfIy’ ñš„‚ÑÄR±ÀA#%êiMz’ mólvà·§‘Ûˆç öýDè ÚcËMôÆM%ˆML mA uÞ¨^8¨:e‚L( ÷8è Z{“DP‘ßSMßp€Áq„¾šw#Ülzƒ¥« 3ئv›álµWòDFµÚ3ýÍ~– †£8@µÚ±Š„ÜGS%ˆ¡¢”hàÂçìÁZýŸK€5vRªKü'€ \¾à§×­×dx ûWH3Ü;|!ïÕìJB <Í6†VFñ©JZ‚OؾÍÕÁY{¦…T'çÕ‚#K»–Ý9z8¢Å…–_QS7ÀhÏ:’[¡†©D ô­Í€âÍ@ù=M_–å‡À[‹!g8ؼ|°¡[H’ &aYè­)k+.1N a¨ZmH!®‘À!•Ö‘Èmanx£¸aŽ×Ñp"Ñ!ú÷@"5!tWÿeB¡TØø-6íšÈ7ÕI‹Ü/è&hÙa§w &J®†MäFGàO¢dL"*!zÐ:H+‘µG”ü*h`½—;í{ý 0±-Hb ÷jµ(êá£w;\`n×s@])]ß…ĵ‰ß^‹ó5,Qs§!Í … àW“ë.îABo™{ïBuˆ6¡–ÈÑ6a Gb+]Ì·œPTâ­×?Dmi¢™4øâ¹§ 6lXïÙÇqÀ#Š‹Ác¬áˆÖ^c1©˜ˆ½!0± ]KïÅ7#My¡å7RtaúΩ¸yȱÒrWÿáVw"á%+vx®›°ø :8P™˜ãñÉÈ˦ùq¾ÅgÇ >‹BR^d‰óij ™„Ýâí‘·q‹ý„³8 vÀD›¡Ô4‡‚á‰B0‰žâÀL'Kà+1¢X Ñ¡À7¢ÔŒß£7&è~x`ýÇ_Ì–Tê{é€È µ‡ôhÎ"¦g£`pçŸ.~kôŸÂ¸8 S`˜.ncˆâÔO@GÊŵH#N -QJˆÉÝz$£ï— R|“²Ò0~{“ÕYØ)N_ I¢°)´ƒQ”Ö)ŒïAÝÛŒãa™.ös˜(θÒù‹:–ÒH³ùnD£?w?)‹0#¦ÂüCUâp&fŠñb‹#híužµØ‡À…Kc gÃX£ÀøZ@Š}á²-‚1£ÛÈÌÜOm#̘²]q£Ýè^„Š‘bÖ(3yˆGc?Ñ^­Š¾â5w(åŠè߇ñ%q4b¡à^寄¤’&¼Œ‘cã‘QˆŒßž‹a-†‰èÁ½,m‹m ~P}‹¨!ÔØò¼;¢Ò6ê/ˆé74*MsCa3î}¢JüB5Ò±[] cé<‘Rƒ‰ÜŽ#fu2qŽÜ„5<Öƒsž×@ÐEŒ¿ µ·TŠÖ^¦Ðd˜Œ" 8‘)t7X æ‚ô¤¼%ËX%òúÉÌóyIØôT<ž7¤Á­!ð© áLxXï¡AŸžûHñQ¢Š1T>ÌK ."Þ“Ýz,–¢lœèÕŠ@(ΟÄÙU6Žïˆ\˜Ž\Ad§ž.É+³n… «ã WjŒæ7!”‡žùH‡„N­U#Œ‡±œ†hà‡H‰áBâˆ$Åð~AŽ BðaF„á02ö!|ãÓÈÅeµLDžŒ¿ ²õ‹ŠHë’>‘YõCëbøù]ëbXY§bëbwõŠH$Bñä­9vŸm•P€7"ð8BÎ2üA¹œ‘$ ßÿÂO$DC°8’$ªÌä‚¢‘$Dêø† ÄŸ“Cqœ‰&}ìzø,qŽ#d3ò2ác™Ð…dGŠy™Ð`ñGd&tмl‡œ%ùã8qIdÆD\,0$[£e1’1ËÅè‘Zͧ`ðâÊöã&ñdGuØEŽ üÇÒôƼ‘zÕˆ^ä52âDä€Â#¬/çèÌHMÙd‚ô¸Ü‘é‡ÍÁêUÙ¤<mL‹Å¿°Mâ5bÛwABª‘Ûa&EÝõÙ!’ÿÁ³@^ù=|ÓóbnÕh|ˆn™‘ùäþ‘& áߤjæaBIsÀ+Z }Å7Q ІÑò&!ÊÝ,Ù°$OàØP†dæQnˆŒKò”T”‚Q>i m £á é±$€þ¢@, 9R…¤G…Ȩ?]…gãð”`…'Mð/J%ù*Œ,¥ÒSáU6éPt7e¦†PŒ ‡â–N7™öAÔ¤CÁ…\7€XÍAiye´äþ÷5„€þŸ ‹ø3òã Y“EhdSP3“—×MÄ,Éâ©~¥‘Cc7ÑpÙŽc=IPøæ^Uæ¶\XN‰Ä„“ÜT‘üˆz•“þ‹¥C‘²APŒäAÀ£>äâç³M'%´â¨“>äØadüŽA%Ù¡IHv^€æ&À)ã$IR8/ŒÞÊh®•n釰çCýîˆ`*ΑLdç m”5Œ;ÙV¾”5Ì*&>â:¥›ç8PV–¼åòpPzss‡]1~w¼å Ñ–®åõpÂ@…bäP@2Ä„•à -Œ”bi†Ü_¥c y @åIKŠ€CÙZÞ–ôå³!5%QLÀEA‰È$YFu•¼eú'ÔØ–[àö\HmüåÁãË Ž¤hp|í—8ƒNB²¨K‰A”±¤\…¬"Bá×8fBÉÛœ4¿%½VIº¼ z‰ko XPΖÓ[ε gÌ¥!ú¥•³¥ÒΗkä"\¥Ž#Ôùbwi·l ´[†P3Œked`I\º?؈°~·Q&ªßøRü$*Ÿ÷'F¶IÈ.édf"E›ey^¡ F#ò}ú‚5Å qØõ•áRæmâhÖ/©:vœ5Efd†Æ›‚•"‰TŒ†ØR™™†0/&šÂÚœÁqe#2IWª ãâ †ª h£ªI@R²K6!|P¡ûv‰ò.¼'€éCzk;H|ÈƒŽ &z…j«âቮ•o(uåëu˜:ErÁ-™/¥{¡N\œ–hM{X0„MYUÐ[a¥ uÚ€’Ìef ^I•dfÂ;°bR¥I±H­»£ Yš Ì+)ÀJˆ†òA)#ÙèföA%/× ˆ‰–¡IOùõé’ofÒ:܈Öh 19> ¼‹Êçhê˜nŒêZš0ZƒRü'ˆ"}—`ê,ðÜóR~ŠºÊR09‡¢÷ç‹r”G¤?ú!ÔˆÉ:CÑ\ )JúÜ$]AúL é}ùþ+¤\ïy<`NŒ9*?„s…dš¦=Edt°mWed±ZìŒ-©UWg’…B;w< rbdi!ÒPŽ ÂÓ`-´0Uä « ÚÃÎqræ™&¦{ù6V¡'-Y-r‰Q)ƒÀhLieb‘†U1X_é„rh–™!p(FCS¦‰À•fŽ¥Dƒ x†&vÿA—À&¥lié… ‘hèNQ6”1J%™VýQsQ†Ùf4£CRœ(JFtè—† µTø±S0'~“7Ú[„nŠéVÈü¡½§¼HCÙ£h“@¸¡ë'\šDñêã„€ôx @©iz]Þfpõ=½¥>$Lè~T¢V˜iz1¾¦Ó ‚i4¢iBI2*Q•Ÿ)$r‡‹šRaã‰Ôç9àÀŸ&Å9¨=U¤yiÿˆov‰cih•>œR‚©')G¦Ré`á×ñ¤®¥ÛÃ¥@§ÚÜX:‰†žwd›€ôl§Zit:&Æ•Z§|QÿxŒãš²šÅ§ýq˜o2—9ÕCŒú¦LŽøY•Ž¥‘Ńpò5§€{jl¥v ‚z‚ÏF5úYù*¸Ñ…—Š¦æÆæø‚®‘†ã žv¦^·Pù¥Î, ›††/™"Æ 'òŸEWÄ]Þ–Ý•‡×t"“Ñfú0¢Z¤Y›8lÖ—GpêZÖ-j *Ô oXa¢WÚÓŠJK*`i‚¹GSâJ£f0"ê‰jR¤Œ‡QE^*¾Œ”Ê£²:ª–ºQ†*Yš j.=0ÇäR•NÉ…d1CŠ‘6cæµlÚŒðD]ˆ 6NEéáY•N'‚RƒZ© &í(ÐLеkHÀqá½Á(ÓË6¬ìÃÐyضZL8ñ‰¯^L¯é½²¾,&¤²m¶ž¬gñÕ¼Z¬k. Ù²‡Ìe{·ªC…«Ž @ðg€öõ­0Áë2‹~\±+\¡r-®‰Þ.õtŠˆ«ÇÌ·kfĶ^«\°±”y0lX»} ¹mX›9¬QåòÒâêÊê8 ô‡¨ÊØ6gÄ…¤Ja TŽ¢6Å. jSP‘+ƳTÞÂÐÝ¢³è­½q*àSñ-™gÁnP¹jÚ&¬ÖLö’@ °—JñеŒm ô¬² Ä„ƾ±n÷Áܺµ.ÃR˜ÒžpÅb€Q´ûmYKmc±ÖñZhD ŒYà~«óãj<Á+K¬Øšýqšìú™þœ­‡ëöøŒø7Þü·Ÿ,¢“ÜQƒ2î‡å ®¢­©²qAnCmX[‘œ´³KCä Ê+\Q1‘-®“;AÉ]ë^Yë ô=G­+s‚@(+•…# cKBPf­xsËÔu ݯ¤…ÊÅU:¯DD6¶6·uLh|†w+ìUy¢´¿ƒŽÙ;–b]á ý?kÃJI lŠìñê8 ¸0éxë8pD§¯Vf“¹Xfl3LË̸¹+b—[3t´ mšk|ø«Ea· E,¬£L‚¶ý¬"r¬€ßd•+»ºG?¯šæm>kq8ëîάŚ‚2Wê+П™dIûUê¯d­¡ûÒp²—^˜»KL²f]Á€ÄºýfšK¬­ È­¯ZuŒ.Uñ(Ê ½« KÛ@»ª¯koÀ7Í® 3=š²0¬¶»:TA†kè±à®ùZW¨&®Lûîr-nš;BÕuùj‘`@`¬@¬A.D¹únÄ¢ƒÆŠQ«! ¸#­íŠ#Q»dn’ñ“š©Ú®·±Ž¼} ÈSµ^ŠAE‹䪺“aÁ'‚YiîdÕ©â¹(ì$1¹­Ügæ&¼É_jÓ¯˜çØîºYäZ¨ˆI =ÞZ¸l½¹-iÁÑ,U7œÇÏÊaNÑÚŽ·×Þž$ÉÒ¼0Á}ÄÎ…!T·›°¾4F¦Û°BiúÊþš]d^˜c‘Ðd¨G -Uáëðd®i‘Û·[oKD4º¹§uòVê­ÿ«u"4SS/d¶2À½j-BJ¡BïÇ‚»oN Øò°o…Ñš­@ÂÕƒÝr¶[/]ÉË¢¼Ó× •yU³4oìPÂ.¸+ë8ñWd­Q+§fx¼KoåÊiX ŒÎ'» $h ç{õê,ŽÚÔ;CE³Énèkñê}ª¯ÐÛ—, ‰eç+`ÀëŽ?ËT®‹JoªkúÖ6¯Q5®0;/Üû¬0©K¶¥С+Ì9(ý¢°ÕQö’­ ‚q£#éDw€. ™—6ø³sÖº¿f“×øµ²$OØ{¼²$SÏKò¾>¥p“ºN…œšJÒâ–•¬ÕûþŠ—ëAêY$°˜ä•*Xx1{Íþ;~H\Ù-ù›ºOï\ç…#S-À!z³­`q›n?ÕíS’Ex©ðíû[dX ¡bAË,¾p.bAhxhlÃëËÖO·‹,»ÖOÏ…ê Æj  ZÉIØÒ•7æ+XÔ°m°K¥o]˜l»jÆV§¸üš_óm&Û÷ ±¤«cE Jð ìàPN0õ;ðj5wÞˆ0>°øJú å®æ+´ Àóoo!‚zy»ƒçéÖ«øï·*Ó©­7pM5§»Üo¡ð ¨‘fpãù•rOð€‚ í¿þÝ,èˆöÁÑÜž2¸\N#\­Ž  )뾜 a‚ªµ‚ÔPz 7¦ËJnä¼KaÏ’Â6l"Ñ‚RºãÌŠ—e?(*,ž\aca#bÀt0µn!£ÔoI;_mÇkI;M8©²ë™–žÂO-P«ÿ²Á¤RÛG»˜p—TØÀT…oòö 'ÃZœö /Sd…ÞY$”’“󫯲„×$4Œ_€ðÚ ;5¯‘Üzj鉈 ƒJ+Ç$<¿Þ%:ÄïHà®B…Æ*ó老«RÀgI0Õ­AܽPÃÍðX2ø¤°MQ§®ÑÃi©'¬ç‡×ébÂoÉCG­ÇÃiÙÒbÄýŒ Qî-—ÃKïÃjÊâàÔþ´j Gôƒ¿ïÄ}¥7Ã'!îîÃýÎÙïÃ2FL$2'ÎeÌø"tBBñ+ÐB‹W ¬~hGÝ0G­®ÅÆu ë]Dù"®Œ‹Ƴ‘ŽëRèZÞP<Ì“ÅIm4ç¹°fŒø~W Þ[°¦U%ÕL¼$¤UNÆT,»/˜âV¬±ö%nÌöûYL!+ƒ]L2\pêÚ[$u@¬g ëéÅ–…r빟•÷€] •ñ;ÁwÀ¤LXg(Å(,L¨7Æ„Ú<÷ÓB=ªK_ÅT öñïÅ|†Cæ.):•a|¼ª3”“æl·§^fTøl] šûYü I±_ܰŽEô D¹™–p lü®¬äܼ¬òmAÃί—…ž`óÁŸÅk1<ÙºŸ´ÈìÂÁ-Ö9ÃÎbÇ$ãù…òrµoŠïh[ž‚à‡ò.¿–H‚¢ÜQÆïD£ÃÕx·ï¸©—À{ñÓôD̬LRÃÓ¹mµ„-¶È¥ïÊš¨H âðñmÂVó1õ{âù–*1Â+»^ȱs<ýF®YÃÓg®èUãôç¼IPøf, IÐV“â.Â/‡áˌȈk¿c}R¼¤Å1¯ÈrÈw‡Þ¼«qãy99¿kÅp'fÈ…hŽ|µŒl@òÖ5RD\0ä»$T þˆ(›R¶sshÏC^àÁ°ür©èOLU (f˜Ç(ˆŒ+®¬ <¿jŽcîÌ›#²i°%G>ò±œŒ"g@`ÔL—Üɱòk!C ŠåëÏFžÂ$ i^D•.~¼¨–®ñ)ÏelsMÃÊÉ$] Ë\îs<%Si²ÜÇ‹g¼± ´þ9oÛ'<­Z_‚²ªû4†LW‹bQÀÊñOk51D29û“¤pï—Œ‘}µ rÂZ*”D#²h»Ü­r‚"/}ž²ƒÜ¨LA¹oŒL¥ªÂò˜ ¡½?qŽ\%y48²³U!™Ê&Ôx(nŒ‹^ 1IB ÏËœ²Û$›Ëƒ,¸öÊxB)a¸e˃Ύç¢Ô|Ë™QŠ®T˼…¸¢fdÁbBËíœÈ¥kv†»É³á•.»º÷²æÒ.?ÂÃH–À÷Ì>-£‘%Œ%w ÿ¥Z Èð»|iÜòrÄ<¿÷Ó´Á· Ó`¾ÝE÷ÀöÓ‘µÚàˆmÑôUñ2ÀÌ0vá ¬/KµñŠ^øh-ʨJiÙËU|4Ï…°FÜ]¼"«‚þL\ó}·™€Ý0_~!éè…@¿4¹ÞÖ!AïÚÓ8+&ìªÛ =ó4óG=ƒ-)F|9<ã Á„Nå`ïÖꟼV.OÒ# ÍŒ^EYŒX'äÄ“EĽÕÿ5%A\ÐÓïrLZÒ4ö×ÙÈúФDz×ð”Kós¹üÕƒô?u$S¾W3xkËÇQ”¯’(“Ô›GIdDKLesDL«c W¬¬2·l†’öU*ÁÞØ97ÆœmϾʡ*?{Φs¸MÿÐDGbxt‹ ýÈ»ƒÏ¡A,¾ùiܼ…=†™¢Ü³Æõ„Òt‹“®zÛ¿óh;^_×»5ïB'ŒW3qÕÅ\]w,ÁõmÏká¤"lÏÐ@ÂN‚e#Ðcðx¼cwÝ‚”zÌuû Å9qÒøµzx‹ÐØÆhg«ÐFEÃCpCÏï£,b|ÜYw‰oÒÓ×C#>z$ÉÍ&”«¯Ác¿Sv¼Í&øs:#dp&KO"G:˜…ëMË¢J&µ~&Ò#¹ŒuÃųïýDß„žh:ß™ƒn¼4w ;ƒ_ Rcl«ÆÓ =ïU8óxÆ[¸dÅÒÝdv[Åoó¦$j®±²-'ü¦’g§-;2AEOKC¾){ë$ï.væ/ÂLN¨G®µŸ°ìÐ…dÛÑ,nl€‹÷Õ\L ¿äo1§l©cýCµƒósý;W TŸ‡m^2˧ssQ)4Oá›x»}ÿÆ‘)±[ÃÞ ¸¼B\ß*5á{}Û(aòßmÛ-øLùgÇŸùí9ƒ;ü† Þ8_s›ËîM§Ôà™ˆ%ÂêrÞÇ)s¨ÖÛÒEÖ°j$hî6 õv6î_òU5¹L^4(/§ß@®-Ü¢)t8Ñ"ÿª¶õ0’€JI^·$uÃ¥RÊQyJY‘0, EN*\tºä) )8 `ÿM}´OW;PƒÎh(ŽÜ0.”§fЭêý•÷ywM¢Úï ;CRî" ­\ û—&k •ý Ù7þð ´£b¾cåzT^¾/Cæ®;îCæ0G¡©‹?féÊàulšìfeäF¨â˜æ$FÏ—!çž«™ ­ùšóÖ4ž9à,ëB PÊã`– 噄‰°7ž ÖkÞɆ[>›ß×ã@¾™ó8ØMŒ«+€án®”+#º$YI(ô~.• ®Ïœ†›unc( %G@›‹Þ7´é•“çRôΚà ÅÏ´‘»/}½uC½…Þ7S’æ5Ì{ÑÀh(ÀPìÇgèå{‡^½h\*AK³€Ée‚ý_nTµv›Ã?09-½œéEK&9HTèETC×ã&éEyë>™ëEt§LÝFg ºpcè °PS¾7¯ƒ~ÿ…(y´ )ô ¾¥‰]•ÙWFÍúA]1ª& –G]±ÃÛâ®=û5rêj>¤/•£ Îg (Qi:(AFQo{Ödµa˜Ÿ.Š©@:žÒº)ÄáåCú*Q8ç2yóÙ¢$:-ÅsÞúÓ`1Xç3º„R:ͼß9ÆÆÆ]Âl2"ú’š#èãÇé‚”.Ο¿JWîqÓ¡.Fçe¨›ÕÛù5WÖè Ïž¡DßèVf.£´@ž£A\ë¹à¥«P8mºñ1—Z¡ú=~ŒGÕ¤Âï“>(-7:_«§à逭ꢢGmx ¸ Ã¥MQ²ä˜P}«;åñC™l—ë Cˆ*{™@äцA„':µe›ÿYGÖ—’æèGj<›OnÌj¢­`Œ¦êc¨‚:Ðãaõ[Þ%Q«”«¹ž9X {z§Î¤Àä:ªž\ìxdºR^È0‡õú_ÞÞZµ.:¤À+Ñ…!t6bà gŸKÑt¡Ò¹¾.¡äýzªÞ%ܹ¬v¨NN°—\ú_vcˆ…øˆn¯óB‚|>}í ¯|›ûë/¹Œ-¤oëÎ £±›k;†f.u;ƾÊYžì°9(qžü3üWùôU«i&‘Áæ%ÇJºSîÃ.]÷@Ž„X$"w}–_ŒV†‘þ—ŸœAÔ¬>œ;!—Ç”žŽ33Áн¤çaŸr^³3ªdL®”;% ÀF Ë1¡‡\n´!®£¼r£§ t‚I¤ªÒŽ`§Üî_Já©>žmǘͬ#8™ÔncHÆÈ•NŒ‡Íð ŽS´?íZ^~ŠO/ ED{o{6Dºþ—Ø›N(@õO›ó}ðú ƒ©;~iÅN¢7¡{_ЍŸžÍ¹×>œ˜:6ö½4ý¸FpÏîðĤ>¡ó4äG»º¯Ò­W9´¯Úµ;bmc$#yC¾N«+’}¶Öþ–,Z›åþ¹·ü¦jͧªyþ‹ïH%ÃÎD‘ HÛç%ä:…ù±Ob“Çž»#ïªÝ©kŸñB)èj"/Žb0k!¸j}»óåÕÙŸ¾?%*ûú¿—¹Æ®/‡å·þ~{ÏïYÅPõ‰3ð†Þã<ŠÏn³Ù`bÃÎ`¬ã"úe®¨léòÞuáixúG’DKàú8 ™è{B±“dÚˆúÜT1ãÿ«èîY|±wůì[ˆŠ/”­wuÿ3´è>ü_ÞMè’+êuè˜mm>=œÏª›ÂÌͧã[P|†ÔAºQŒ*€k0*møeÔ¢WO«ÇN¾S 9·B™ß©¯šg¼õnÇ›]ž ÿÈ'`ÝШ%í-£ø—ÁKòñj¿–€®h¼RÞ>Ýìå#LŒµšügžQ±IZ¦Þ¦à,÷YÞÜ´¶zÏõØ7á²J;®s=Μ’^Q4ÈÛDÚ¢Õ¾¤ÛD{F.ÿÇÇNÉ” ßÅ?p“¿ÇgÿSd”C’{=N*h&ÉøŽp•óF¼·†1:í°¹æ ë=Ó¼¼Xt«ð=®²8cñ$ú5½ë·Ãcº`êÔÅ™ùÁOè%í^„ãÆãÊpØéwêËj] ‡ -Ž(d‡ôìƒ/Èßî__ÃY”ëãï ¶¼7|XÇH>²wô6ŽÄùÛ|XL½h­FôªFù^ I›Û‰¹êÍù^„M)¸˜¯…Ì•†6ß@’Å.)~“õªR¶šÊ¿õ­ÚÈ$› Y{£—ñõšÞ~þ‹Ýª”[¸üÕRO«ÏPî¤ánÌëÂÌ6×gôJE$îå© I³*‡kÀ‰ë¤—§qDA{jéÀOãÝjéÃGž$wôªØÐ«§öxŒ9¦ý‡¾'_¸=Pïótò½¼LŽž 2ôpŽ5#3¿ú_Ž5/ƒ®»‘U9äøgÎèè u=K(D:Õ²>#-êo+¯¯ÐÅD÷ŽH:Ñý|^¼è'Ïúx>Þ×åÎûv1,~ä5¤p¢Ü8μSÞ—l@TöÞ[Ì;cót/]†óżRŽr®–Žýuï1?ºÉäè¸îîýp’'{òÂ=%ÑÍ3ø þÝ‹%>î“ÕŸÏ ÷|§­Í+÷ÂARÇãê¸ñº°ãmÂçýáË÷ E}ºY`¾´íWÇþ²„n±·X‹b/Ü—RìkìÞû޹}c豚»r‡+ßOt©ÐL¬°=Œu7#p¥Âº-šȸº^“oèõùb˜ÇãHjŒßÜ;åIžÀtBEÒVG£J9`žÃ÷öyÅ0áØø6FŽä2øm~ï>¢ôiþ(4ñÃ9‚RÆ7zI–åàcµ8?ª}sùoÈ©@Zêã –‹,HÛVRâ†áüRé^Š»èOènT˜6é[ìZFOÆçè™BçÙGã–äté§ñ^é HCäªBÜ·ùjJ–ªÝÃæ½&ïþ«‡Wyo.8yD9ƒ8^‘ól»/ðy†ÂÞ§÷°¾ìñOŒúiþt¡û„‚ˆ·Æw‚m¾§0¡ùˆ>“$†Éø$úÀ(¿úy¾Œáúõ¢·{,nÓùˆþ¾š‘‘û°¾—T±¹èm¾yMËða¾ÖÉÿD4÷Ÿa”„LkAðÁÁÊØ>bõØaK,Í~X=¨*4¾½Ûû¸ñfì«éûÀ’Á?Õ3ü©Ü¦o®=>6Á¦!ÿEö×Ó5ÞÉlë#L…þ$y•:´€ìsü—F1Ä„?"€Fiñ¯öûr9zs1ÿª£^%ª1ÿÜ8òû@1ÿd¹óûûþж8âóüCLÙ‘Öò(GÑßYÁÝ(Æ0Ð{y„FCòòWeQæeë£C “fÊx·„´¸\Ãû°×çö͆«QÂÒüOê¸õOæìëç#Á‡ãìkZ,ñg©÷õ»ð¿¦yì½Æ Fœ¹~UÝŽZˆy?{xósô;šQ†œRƉŠî¯›,f¡à•ý›[ íhKÎþ¿îh Qø-×ý‹,ðÿHíÝå'ýÉÂmæ¹çýîÐm¼‘þL€Ìí—ýÓEZFÒ#;ûãÿgàÆëd¿ò3IÀ.³ÿgÈ;]DW˜û¢ÿçÿÔ]áyýí,|Ïj~¨—®NDýj³jÕ6~ò[!1$:š_?Hò_®»‡7êùÿÜÿE?kšÄÏ#$*slVˆpxw¼s†þþ“ÿG%‡c`/ÿT^q » É-ÉYlù—‡ÁÌÿI@^‰øïôÇñóbñ"t  è*pÌù ~q–ŒA’nô×h¸#LgË¿÷€ÒÁ×´Ï8ÅVl+u/éЗþœèÔk†»ÒPùU¨$ÈE®‚hÍ —dXœõx͆±Ñbÿ×h¤€• mh(IýçÓ…“4¬À¾/§"WÁ'c¶k.)€H@—ЮZ[ø‘"º>s™ð%w]¢¯¸ bPª=sÉp —ÍÂþÝ×f =9 Ȱ0Xžœ—)/@7 Ófcüû´X¼fx¿ŸÙàÜôøk6 òeW¾/E¨¡± ÔRâtØ™…KX-ÈÖµëàRšƒ²) ˜ô“;ì¡È~‚À¯0á}±L¼rî Èý ´ðR’N©À@¤è}ü]ýX~Q ³™ÔDÃÐ;yÜyx] (‡ò­¯§žOÞ£Ýø?þ" šºÅ_pÓð/(ÞÙV4 Þן¸¨BÉqÅŽ/(ÞÏ8i»ù1þü†–'ßÖElÁ¤ì ý êQÐëøAá¬ü(þ Û‹8¸0…¼;ŒYð?‡ñìÿoð®È¥{‚@ÄBè hU°1í/q@¤™é†¤a`t› fÁA¥ËlUT|ÙÐýÀ’škïà,iiJwÇDþg=¨òMÿ)AEL«× ¬jï F)A0dÏŽ–t\œÿø†@éÑE $ä}Qu)AÞ ºdv©ĵ@²ÇCéGþk{¬U˜}͆ÈGÔÚ7+H$`³TxUÁµ ¿Ž*ÈýÓ*Ä]\_Ï¿v¦ÙU<ÛÍQ€YAHj #øäùúäÀ~ų…Í[8+àf•×F¿€È9Kw–jÐAоÎ[iÁÃÜÄ‘7ð‹ðŠ sa‰TÃZ ¨‚K ª?lm<¬¬ “àÄ ¬ öÿø€[>Ï  ABµ–š@ÛKTƒã`eà¾Ó?´A·'šjPj¤¸ –ÅaÛð£áÇÎk ª n½(à^ª2w $ ¦j5ù°à õo/E⪇‘æ,¾×å¯lPéIò £‚¤V]0ñ ^pñ‰óäI<”)šÐÕBGž.äIÈÙrp`×YÛðÍ 8hÍÝ{°{V)3z%ÜR õcà¯/¥•|h¬Ú´yïAv˜ö.äÅqáÆ³7ÊÚ€ÌÇñ³x°0 ƒ,?ÇС—ôó!p[€IÀ«G1Ðx™K~ä’!ïšé á!t R  Ê«ŸÀ bÔ³úS@@„R'‡Mòã0í¢Ó¹ó«‹”ÒŒx•#|6qü®jd#ßßó¯CºðTý #i ýկؒôýÅ1#Áþù/44©™Á]hGÁ\ÜÅTÒ(£ ®ä[s˜péŠiA“Ù ¶bðK¸µø¿ˆÉT„nÇ/LøÀÙ.žà„@…ñI'ù*14˜à"N#¹6 ž8Y …=  _0WÀ»°bùT„±5ךRØ÷A yT„‘@—n0ÉÀÂxÃýe¬AŒ@/ùq+X÷>…Ç—J§HŽ˜PÄGsÀ¾äljǪ'Ììå&|xÁ>Rç€Fs¬£CÉ éLªÂgEIáü4àGd‡·%a3¡K…„ƒÿA_n3H²k ñGFƒ+ þL=Š…äÞµÐ+±€æìÙß·°R- ^ g?¬8Á¹°Že&~69ì¿o‹Méf-Ì5h-¼Á {–§8øð£hÍhè…æÂ\ƒúãçÆ¼òœiv¹º‹rkxÔpî+,T Gv°õúÕ] Ã;I½/×à„нþ8†« á‚cè±>æ.†“¥C› ¥ßq »]®AÐ#2Ü: }4C6Q!AlfxO3J ¿°žù_F®îÂh¢5숆†Œš!ÌÏ+ØBZoݽ‚±£ˆÎÏ+¨6±ý¼‚ôˆ(¡Ì°îB×!—©Ž††é–V’!oäæ0¤_diœm+Њ2‰ŒG÷K· ˜N‚jµº‹AL޵&l60š†aêA×`Æ$|#üËè{Ò+cCX!ðZ(\pI ¿:m8 ѯ™É¿™L¸0‡Ca'¼cÙ ó›¶¡³0¾2õ˜jý{<‚Rë¡^ÀqRfýXH‚¾¡ŒðŽþã"µ›†¡†7Xepà— ±‰é3!B—¡¯!Ô€Eú: ù&ÃJ¯¶Ç$úJã¢oà&+ò1‹›þ± —J¶‚l™¯”ñIÓn²Ð ²Ž f6{²w™8¡<ºà}Hö2'LUïžF`CÐÃ8Ä8·(Ü|ñƒÍ†Ü qXé)ä”÷–„‚—Ç=Ï5HÛPJô㇎ÐÔìðt˜‡1ôÝÃf/j÷O‘Ü“öú„bŠmà‘aÚøÅ9 ¶¿£á±õQo‰úû´ç¸òNpmg)þ1×&u$„ÛïPeàÛpÛ-g|š‚| ©ÎoHp¡›h ‡+ÞÍíÐf *= 'Q 3:þð«Á¹ꘜPÁ"—Ãphä䯾@'CîA%o9[$üu6CŽ_($G¢:ü ÒÅð=¤À‚Ç –>¬@¼ù~:¾=é„8®™{$«Ò‡‰Š4›šÅW@Dd£j±öÒ<ìÚ..]D-b•dmp Äbó®ÄÀϦ o˜ôKŒÐ ¢…ÓAËÕ"Ñ8»’6ñ: íW)¯ÙàÑcw ~A¬ ßo/‘-"óìžbÕÆ UêÅQ¦]‰{bðv 'h Ê#ök/šÜÞ´!7qd%zfi¶AP"ˆá_{òqˆ´@ÏAgÿ[uyƒ‰Ó‚PÄð¨óP–Ü«µ‰€-‘ã×s%úw†Õ› FNl©Lë…áÄ/š:‡•è¯Án}›‰ÝòÂ(žˆÌ¸± g(ti H“Ø  q=½¦ Í@Ž›¤Z€Ü#‰¤"Ø›“8®¸˜ª˜ ÈüpÚÀÇIÊ£#àŒUË ¢˜GÂ"~Ê«"&cGœª)Ò…Ä[âc)> Ä$¶{Ôr¹ÂCÇYéš8^:µÐ.~nô¦ø¸JQ}æ~ð¿‚”bžÈñK!4-dkÜ?ŒÊohÔm Pnt‰9Ũ(‰¸8Qä2’…ÓÁÞF©DxK¤á½=Ú{9Å` ùªØllultYEB‚ާV¤V cŠW—5œXìQÞ£ˆù #ƒÊÖmˆb¹>„9ÅIÂ÷­8ñóJì o€§¸nC5ÂLUT,²˜|¨Äß¡ðœÈì)Á,Šc=Ë¢4ã­82¼´5Kp.Ïâ¨'ß•S„Cét‡¿+¾\‘èö8£ c…¿+È3Jdð»úk”;„²“ƒf&\SV^™¿ƒ²pÞ^Qr\ iÉÀ†Ã@Bv‚ÆjI ûS¦ÂƢ섢 2užSÅ–b·¡*æ tv¡o”Å3a3 ˆ^ô\¯;´@zÂC0—Tì®lô^‡Ý†~Ç!¬_¼ Q€=EéQn¬˜_ä1@¬"‹Å¸·Vð'‘¼銈 °TJ‘ã‡^ùqxì"ƒ*‡¥ÁA±°hZ„8O=™É÷ORÁªÐ Z#8&6Ì›óÓ0[6ȹhƒÎ¿•€æˆG œ#0Fb`ŠÆÃA›H}Gòó^õ¯Œð¿\qaè—c”ˆ¬O7Ï‚(]]@8×lØsÈ xŒä¿PEÄ@„(Œ>~cƒÖFpG`”îÍŽ $¨@Ê߇ XÂøí¸Œ+Ð Ùppب˜.ø9eFÀ€¢FS={Vùâ:‹Q¯Œ‘8à+¤‹Œ¦…Šjq_Eõ¹ž%c u‘ÕŸ1Zœ–uCŽ;ï"ƒq)àå{0²ü¼Z?ã†1€Yì–= ¾`F½¹"—Ù ¨Ð þ9BlÌÄ$£Ûí¯(.Ô'µDvYÄ ã*¤ˆèiÜ“eå]ú¤ˆ…lø4QÄòTŒ½´Õâ§!W8Ùøm s³O"Ñ?x[Üâ;¤ ŽÆ\¾£huk,2âRÂç'£tb0Z€º‹1ÆO¦ñ“Xd¤Aì †eÆ}•oɸad0HG1σ‘­˜n´aÍ€ú¾›È„‘î§ï»qý $NĽQßðAª“|ñ*‚ìbÐàXš¸2Â[ϸy#·wF)Û7Þ#$ f†ŒñP ( ™º¥ ŠH&Â"½ñ_à6û^õ ¬¢ß¨I(GAßù@Ò€ Ñ ÑÌßq/Žo1cgñ8ømP2!R t¥Î!*Pž.9Ê=„~ºÛߌ°žÀd¤þÁ]Š!Z/|cmgsM¬»`LŽcÆ›#E‹Weäù] ñ&ò9œ ׬@c´:®""{Mǘ…\ °XoX 8£ŽøF Á"i·Po°psOÙ3NÖÑWHG éDÈ4ûµÖX„ìâxK ‰0DН•wã:0œXÈ¢/–“Ü>¾c&äíHõë6[ЉE¿‹+ÔÈoD95x܃(Gù‚À1òØL ÝøŽr®>Ù1Èx9›;&õI"/³“¾!æG±{È Ù1!®#|fô+tŽ|ÂSÍÑßNŒ.&ß}ÅCØï1|–¬y†‘Žu ›Ã›ñN>ërÔ‡G¹fÞ/Ðð’ÎÙþQ .:v£¶àÄCj6<‚¬ã}°âðSB,â+“%©¡1]ðC,YŽ#ƒ]ÚÑ0B1ÇÙÃÂg©’‹¦ÑåЄx2& ³!ãŠu"ôñ×±±õõ ²!È#OЂàx08ßµ#Ђ áã>|Âg$¼·ãÆéé3òð ©Cí£à,ô zD>Ò#¾ŠTEö£Ñæ9¤9L–ÄsZÇ…ÁÎ ”ÇÈRæ| Jˆ ‘Ì>K'DOüV=Èô*^2-ï%)ò0‘Êê]ðŠ{äô°¡Ø.ÿ“­¤ÝáòáØ:@0”%=4-FÎp&™¾lk …˜ü=£óÒ¥ðŽt€ —l?Hs^5G” ¬–3K1àÏö¯’[î%˜…§Úeó ‡ª¬,É—NˆÒ¶S /ÙAx/—#GÒM €É¸DLþ h.ÌæÓÞf@Qúž7/¦æ²¡ ¤»2‹Ùâí²Ã‡µ°a ð>ê–$^’’®´†é¢ë>&È|™MB&‰4N¸‘‘—ꊾР­. E%¦Ôr¡—ø ƒ9R4N'¥b¡Ø²ï³Ï\êÔ`B1Æi8ãÊ [žz×#æs aNÃ_z 5ws‰9ñ[Cl)I‹4ŠÂz†Ç*ä.}˜LÒ‡ÛLɸ48œóLi嚃LÑe)•$_:&_ˆÌ7f(°a™Üd²¦ü”9ÈÜ#“~äKl-\93ÌÛ} 'µº,Xö>‘:©þÇ™¥´]QB˜|Lz£-S›¸ÇÄMÚ.à Ä'qå/3‰ÉÑî0dßË ¦ j–Àfºûä ÉLÙå~€Ý5äà 2÷„cs4ƒIo¼àd£™ G;J,Bè˜yw8âXäʦÆ!3¡N¸;¦ Ö4t£*ñ…˼asTq#É~„B=Δ4Lf•˜Ê¥áÀù.hÈUØ•ïÌçÃû玙дKÅ%®–ZÇkè Gq(ü ‰aëLÇå¢Iw,iõi~…ŸžÆCsfÑ„aH+•M7 TÍx÷Îì(Œ3ŒMÀ¤h‚ysüjtödM£# !?’B‰h:ОŠÐ°¸é<9}¼d¼×´˜pGxÁBN9Ù+hn<þ™/ËufzFB´ü7Tq®!>ÌeÿÀpsgú¢%¢žfGó]˜‹ÔjÚdô¾ d%‡]Ù¹¼K\j®^M–c&®‡†ó³%¤Š š•Ìuf\¯Ù¤,hªc˜šÐGÄ‚R:ÎÔ±ð šãÇÝA]L9Ȉ ‰i$›wÊÄÛa“›YÓ* ®x–rM J¿³I-ˆýõd@u}ÌIAª—©‹$©˜õ²šÿÉ–ÅŽÞ—ØŒVðršÍNK®IÙt90+ºY§>µæ_#©„Ú mjé<•4ͯ&ºÊ¥f­ÒmR[ô•5ÍËQ“µy§ô- hRú†ªX[ ·itä ­ ›þì$oScHwD9ñä@šc‚ tóáH2JRQ,oŽF Áâ2—‰ÞÒ¼2…cu ¦’~ ©Ùx7´YL˜.‡ËR{L)uo6óšéØÛt“©¨YR¬»›êÉòdS`ÈC '5ô„Ôã ²²ó}(RB8OÂGúæG2€ƒÁYoÂ'/œ<   lÓçpÍAÚÑ6_˜}L:DÃÃ9™<ñàÞˆ×Ȭ½´©u¬H<ÆH›!Ë~çĹf ;ªùÂ[Þk„_rLÄ:D-ˆšÓH,BIRû×|8bè*œUM,Â5ç¿É|LáÊöö •S¹‰PÔHT˜.‡MDÕŒÆ9T29™ˆÉC‰´)¶Ü1npš$Mç AÉv T$)—qÎÓ£}‡Î9LÒ„¾œ Ìåô‚AÉæ48@îÔ„ÌM£äô¢‹ £ÑÏr-=:e^™ëEºÌs2"ÿ"Ø! ®ƒÕèD%šô*‘pÎFæ#¤çMû'¾ qL0§¾ÁÀ¯ÚGà*r,•NQgEÓkÓ]¦|Ô[΋¤ôÈ|ÁÏ s2óRÎ…cR:Öç²xkú–HÍÆåŸC†ÄâU~›“½Mz£sãÔUèuª žVø${¶áÌ™ì:P  Î§?ç¥ù©0Mh!òÿ)ðY(QfŸìÏèÈ#ñýÉ÷Ô€z«GРËú“ê \EdA À¹W‹0ð)#5æHÌŽ…âMT’ä MÎX—uö8áŸQ ߤ(ôòÉûA?gN" ™èÒ3óÍäˆÞ#`Å&£¨ÔÔ]Ö€Ô ,Îk`ˆƒ‘B%î+ZQ˜ßûb`Õã=  Œ¥¨ÐS¼„7éu&@ëCq(ãûp<±‹^Eoɹè^‚Cx½EÝIÌ`äaôgÁ -¦"á ,Nœ`ã˜,J,f0´V–žç[´€Â)R°ämQµ(#’eùœ‹Ž¶VZ†ák÷Éö„zÂ!¨ŽÀF÷,2ª vÈ5(šPgÌeS»XÃÀ<%£ žçÓ3VÈEL~<Õ¡{ÑÃt¢8]t¶B Ÿ‹O+~ÃSˆþÄ_@”ô¢èOƒ™Aqöd#¡) ƒ7ôây>ð%µGIŸDý$úpñYŒr’A… %ŽŽÒ)±2¡%…Š8­¢ôÑÆÃil” í,Gq~%ŽŸDïÐÄ” 5>Ì75£ ƒîÔå ]u´ï˜ŠP‰ÏÛ÷3Hʈ: öâU4J·T1’r<[a»îh”— ,5&´E‘Ñ©ŒFMHÝEžBœ„ÙÑ,K$ ªuUF¿ƒƒkÔ‰ÔÊ÷ì˜È#Y¤Ò\Eƒ­±xVh ÞO¤¤KR/ÓøYy°8Iá¤ïÑIº#xVÄÉõx•OR_Åf3ÙÉý¼ÜÐm¥ÒËMÙîê™÷´HA%£yOlÖXŒ>jO_±4s¤#…“f°o£ÀÂnHítÌL6iô;ILDj0s¤Ÿ ˜«tù$9Âì]µÒåAªô(Š ¼hQn¥3ÄÃVØíÌ‘VLœÑÎHi<“™ ̓RYìzÁMô§+ƒw+z•†”œ‚HÎÒèÒSÙRUÚÈ-9ž*O"ÛO¼¤ Ñ6#Á¥Ïrg—TEº˜‹A) Œ”¤§ªt[šˆô”ª±0 Ãt¸Pb0…JKá#@2éâ:ÒÙ{(J1Ô¤û <éÃÀrqOb÷-LWSOi 3Ò.ÅŽ 3¡ëãÂôØuý”r°†)¢«¡Bڅ² ÖGp)ÿá#zh¸ð1&\PÆÃâ±"/u =c7ú‰uìSÒ ½”jB‡6ç8êf¯”¶– ±˜‚KUWÓ{i"-ÕìÜ™¦Lv/Ë…) `‰åägtø¬¡ûQéÆ"OU3-Lð3*…æÒ"§Ô4Ô’4Õ;ļ&`Ò‹©„b-ÈÙ\˜†ŒlN³éCFíót›–[ü ûÒœJ‚ ¥é64 O¦ÓƒpÅbê6õq…M£¦ÓÝÝ”@z]МMü¢ S¯A…Æ+ÚLˆŠzE]ªK)ÔÓ©:Á€JFe4ù‹®æ|4a`k‡ LùŸ|N×”ýéøY²q!uö™¿iÖtéùÒè^dIŸÙ…†Ço4 ºK˜îÄFÓO®S£Å·@!4òO'XÓYèÅSÿ38mˆÒN·7#öeçÀ_1}›’NešÉSúe$4±š /ºH‹TqÓ½„‹Ô‘6åxšЦpO,Ј²Üù-°FL[£ÄÑÒŒRƒÈ)R‹‡OÉ”ˆ:0A–hœ†OVñ{ÇŽ´)Õ¡¨c‰n–ç4”Š‘ô)AKM'&fì¢óCø¤î×!pŠ!ªò”óiS(|P¤ñDeÚulÑ¥jO[ž¥›¸Þ…4XºÆùƒ|_p¤Ãzt˜ú“PBMH¡ÌIù%uêaÐ:kÀ& ­%µ“qÝ@SCL%,~±ô À¤ÚL—ž ‚Ä7õ*ú”¸0°¹©y‰¢b´ô â+éD»žKP*qÜ–vŠ£20DóŸ·X’X—ª ²4RQpêØG…TAÁ>î ñ¦ÈQ‰ªlD*õ=(§ÕS£wpÉñ¬Ÿ8ªüOXSéŸJÄüdN K{ÃMê.UèÙ•ðKhNªï!™ê õó ‹u¾SÁ©? qé°ô0g"¸.:¿<{)÷Óüâ©t¨b¢tª@TË Â`jØ\_ZI¿`WPƒ*8”bT宕lÓ¹'ÔSS`»&¢˜ 6LJ‰§·˜¹J¨îÉÐ{ÇGnꞬò # …hk0 $P¥‚¤7zP%À‰Q3ªT—‰ÞŽîù·X2^Çà©þ Âü±JkÒ6œS‘ CKj'CgN•˜’° Õ™*b5 ¦OÜŠ>pr/T^i4 ¹] þ+êwFÓ‡"ñ!…}~Hõ½âÀâmèk²ÎV‡a[Ô¨‚ƒI†'åx R”EI¡g'¬uù¬½<2ƒ1Oߪé¤yNõ*Hr«WQúUS•Oj{ð*4-Š”ªÕŒ_W•€Z{1®F<éžA¬Ëmþ¹Æb®RW—ž˜ŠöôÕBõY¦ŠE5YëɪoÕ–µ]Ý ©¥×Té*A™ZýpªVÏ çCëiíÅ»„¤ô‰R²4ªAÇ”öE¯Þ;d¥_Ï„Á‘@™¤1•°®6„£óRôçWãÜ€W=‚~}dNRÕêã£Ê> a\M«VQߪÍ¥ÊD±ÒÌ¥ãRäLPrOˆ¢ž6T9©ªU´ÓÔcªcB$ @ˆ2Í'!U¿êAv6WU4>Uè $«Êßô­ú>É Öˆ>aœêdµÃ¨ ‰pŠ@” jÈÆU%¬£Ãg¢qp ±Éª¯ªE™—šþꙕtã'd”Šln!ÈÓ©pu%O¤ùM!:/0V†¦„u\SP-®N!È€BÖŸjtÕ¹ºc=¯jX© aÕbäK+rgd¦ª'þ5©U ë2¥÷‡eM¼}W§’V×L£÷Éœ¸\üF¡žØ†É£dUµ*˼²"JI­ÈÃ)xÕfç>ނ๑檗ä:µ^ͱüŪÄфٺ‘G 5™6µªWÑ·Qó-¿ºô œÙ¨W©ðpÓå ˆòìB(}Ñ«'ul!ge<¤Ì0„Íb+çGçØlÄÌÔ¬mOè[ð–™0h3(.é¤Å m‘š•ªÝt¶ŽN[©õnk‡Ð¤ QZ¦n #1B!<äÃ`LPÈ·>=SÏMtkKdE‰Ð,·R,¨lÐOF¯$¶: ZP­< ¥¦#y6ÕÛÝK[F—Öjkô[å[šV@¿U-Šñ%Çó”¿Xb$™ W\+ÿSÜ\­vVŠI_¨&´Ä×Ù[¦^\'QªhiìAóeM”žª§"»Šaj&Aò§:ôš ÿÖÝA¯ÉºomõL„®‚ˆÚºô¡±ïYý\!6'J¯èfá±ÉI¹ªuž®–Uèi†-ÒŠ‘-HéH›ÖÒmÇýÇüØY¼=DðØ®[QOA¿•ìz{ˆ¨ã®ö3k¸•÷Ðz zZ[y"³M´+­tßêî º†]ÿK`+áõä\]zÊã²s³RfÂ`WZ%;Þ;Ô~·A*ñzNM ýù¤§)²ƒfçü¹ÒL»~ªN>Ìo©æÕé@¨Ó$@†Bêëô“>0±©^'¯CÏ]é@ÀôªŒ¼‚^™8Qwˆ ×9 Õ¬¾^KЇqªæµAZÀıâ^U3pWµ(™Œ—™z¼â^-îTûªçH5ðÓÓ¼VG¡ç&"wˆæŸŽ_ÙTVïk‹˜DÕ@MΛŽWuþ ý:yå•´J+ª9ðœø‘8z i•6X_¯Æ“ÓЗ•bèS&;õåä*`Õûª3h¾Yã¯÷΢M”á1v(l¬K«$Ä b+ú“v´!˜ îÜ4Ó$?%ÃŽV|=¦CU_¸ð)€4žSúЇòüãÕ^é¥Ìÿjh½ŠþEî)ظQNzEGtF“«š×,‹«F[úz%é^S£´N$Óuò:QØ‚Ö`5¯TŽeä5! …½?üœLØ`Pâjiz=ƒí'°9òƒ6ÕJ6`@o¯nÐ6Ūy-lq˜ø3nÌÚÝõL=PÍlag–hØ:,h"4ym,$_å«mØóÆ–Í=ÀJ‰þ¯:Xh‘y•iiz5*ò/­‹ƒƒ{ÏVÖi{ÀÀ>Ÿ%OüÎgƒ*[•I±±O Ì%"س›k¿ V§¬üÎ’g JWs¬ñEARG½¶,ŠÙü)s”à“<Ëz%½Iú®¬YÉHe½¶ßd®IÀ\Œ)a3Ký;¬_ݳˆ)Œf8Ëœ­ˆZi²ö‡ÅR#n=9 p`hÿ±$†S…˜1ˆzYgÁN¿^Óƒ”äGÑ~ée©°ÜY&èVÖùúhÓ½hµ„ň„Â'¤mZÜh¿±NÅüì\öúPD"ÿ‚1PЯ¬M4Cªº2ôdÀSËíO/ÙÍ2•Âf½ „J TÁm,6@Ê#Û1*TAûACeÑΜüìF¨BÿÀÌj¦%fÁœÖ³w‡‘}Ö{wè…¶i‹–'„ëšÊ>hå˜=³¤Yˆ8‰CKž%—o̱Q´ÿ a¶{}°\fÙ°â`YâDË}€.¬„Z þèBMËÏh¹öbÿ±dªš-þÐ7è…Žeù´˜U-W¶s°¹¸©ò 1!B ìÉ*™Íž yµ…ºíì±2¼Žfq´0ª®v?a˜2Hµ& Æ"¯•U»D|m"j¢Ó;ÇdP{ ‚KŒgù´—Ì•&LqñCHÓv]¯]‚A6Vˆ¸¨_Yi§èZ»‹sˆ¸0 &hy´ˆ ,±VЊ®—Ý>{ hŒ!Æ¶Ö²ŠÆ¨H\¸µ“l!+Y{é5­Në´-mÛ4Sëóxªi× ©ø+yV—Ö<Â~cÍZCXb¥òA+¨°-5vŽ/ÏÔ%­øÁ,X[jÆv ‚³tZ€mø [é0]ÙŽ F©ÛWÄð“Pk¡ÝÙl3éœWí˶øS¹ÛÓ¾l1¶;Ú¬ýaÕa¾XÉ’fÇ”Á+­ÁÝØÂ˜ÔbkG¶˜˜›ÔÏge ™€[_¶jˆW¦Ô¶æ-¿be "näç¸VEr {bJDrK†‹mêÔ\ ¢uÆî1®(ßÚ—í¡özžÝØÆø¶ÍØ‘-Í …¨rýزkFµ×Ú³­ÒdE€t*x(ÕcíÙÖ&béñØÆ6v·Q‹£A¯º´´¾lŸ –Êdçy€ „ª`_¶0„ö ÏVî•VÉ–08>Óm aöYÙF8Ù‘-µáøœ=Û¢Ä:·SZ¦—4qË}(4´Õlµ[æ §(Nû™Ò;V5£t‘»È-¿ve‹SCÜ h7¶E„¡à·vd[LzóYm϶nºá÷örkÈVU™¤’gò¶»¬õÏš*à¯Ú‘­IÎzK…ýØþ5Œ³uÓ•í¸DR8¾ÝØÒŽæ›ÄÊ—-Ê£çzAu4Ð:AzËÞIéfM:½5àž%ÛÙÄ$éé`{®e?Ô¸¡KôC¢‚ ¯µà ;ä²u[å¶Æ›á@é¶&X¬¬gDôZÁML’­Ô¶ç‡¼ ;Öl`;ñ` ·5\‘Ù –xk8¸R!r·ì( —ëŒ ˆôÖ³ÛŒf ÷=»ÍgÄnY´+˜Ž­Ôö€Q²MƲ°¢~Y.lö˜a±Å×bí¬ªSÜÆ¢Å{tÂÝNkɳÇÛ©ÑV„ûß¶p¨#g·1Éxé°Úcå¯!,÷aH鯽Þnq¦\Éí·¯ÆÀÒbfø·5ØŽA gÇ¥ÊÆg¸êÛAîÓ$ëR@Ãz°’?@¸£ç`TµUÙ^& ‘‘\4îä³tÆ-âÒ2|mZÏÁô¢&9,#4“(¹aÉ2‚f)‹«´Ý↸ X®)‹ÚØÈ¦gdHõXG®Ê ¡ZÂe?h&«{Z].-D_ªÅÕåvQç°–\D‚ùB ë¸]š†NŽ´{ÜøUwYá:ÉN¹hÜUÈœ”}™ÞÉqk¹Š\Z!WdI}¼kÁ¹[\á‚÷Ö“›€p—³=ÜŸÄö-;s2„XÙÆ[2 ËDÊÖp­pz×l6_¥ÇE㦮¹[öƒÎàøµáVI,PdÜ¿ÈέAÛ%Y>Ioý«âè‹—E0{ù³c\Œ˜¹è\åš T:qµc<{Þ¹ž[•®ã¡…õ?@ò$]{lâ§s×Ò=vuizºf4ÓmQmµ7%ÔbVjñÛ®¤´²ZËUéÒX¼0æØÒŒf]{«eN§Ô´ÞJÙŸ6þ`-q‹Euw ÝTnsYµÆ éæNðmÇYš9ÅšÒ5ër ºô\Ÿ%MWj»^Át§´EC·ì_6ûÒkBÞr˜JEÂç-S)©ƒýÇ*Mþ;Ø\›®€L! ÒÝ?ÀBŠ\Ù„)± ›]lÔ²Zö×Jµ¦[Ù•i̱• ˆ eWùPáÑž\ @»¤§‡˜ÔvnS#´Ü’gÝíšÓ.¦vÿ0…@vš8#ÙÖAó«´8¨aÙº•\Æìþqq…$âv –'e'Êîq” AméRÛž‚ÅÜ«Ï&Ñ¿uÆZ)vCÈÏlûd¥Ü%íšÒWíØZ(×c‡ Ï=îÖ5 »øÚæ†8IApÛ¨ÅZKŒAßç»à:iïjr'QMÞnñAÇr›!|)6:ö®{`á·A° 0u¯³¯ÝóÆSf´ €ZL#»ƒ©Ç…}×Lû–ÈÒxÅ Þ„{kujÉ­ì)  ¼ñÖ—Ç„`kËœéeöÖH.G÷Sä!ƒjwxpݶ.Š×4a½¤ðÆâÆ­UDk™Ð¼UÝíd€®Á\¬,À¤µ»µ\Íé >^˜Ÿ5#0ƒr1[:cû !ýnüá3qäí¸ˆJOÙn†6;ÉH:&¼ç‡ˆbLt=‹—ñ-iyƒ»¤YZâ§àJæ}ϪÞ‡mÞDLÏË»ˆóþcG(Y|Œñâ€Þ-ï ™Ãîž·däòn·x¼ÍÜ+¯oW×’Ám¨lsŒ¯¬ØžûçM4D4Ô´ä°÷á60ïéÊ@ôúy¯¼†/oØkÖᦉ õxI½ÔC¦wAèuÙ š§Å…­7¦¥Œèîga†ÇûîHV›[Ü•ÚN¤Q§Þ+oûdëÆÙ5@T¨»ôÜÝßóÌÛØ1`F™œoÜ2GdH×ûBÄã¬hO^¾Ω€a|y¨M<&¼£ÛwB秥ˬÀ´…w'Ö¥Ko¢×ø’`¡Ø{倯ï·wÀËÎÅ­hvʺìÞb-³÷9+~˜ ]»°ÙCG!×Kï5ÓÒÞf“Õ]åÖ¬”øê÷ á’_…/ÚaU×Ýuø²š ½”^‹/Ðuß+b<&T$ôzZžDÊ扯åŸýZs¼ð›è)?W¨hÔøv ‰ ÎæË÷ìóÙÕâÎ|{êÞŒ¯È×¶ø²»ö{q®Ts,]ìM·ž Ïtßd½ñ‡ÀNê/ûrö¾qÕhÐ]$/n·¤;G[öqv_¾.Íšï§7ñÑú¢y¶ò\ÿM—Þ;’EêÄ|» ßÊNÑÖ»#h\x}i³”Ú~ïâLÂà@œòÂ-Ô^{l~¢$bzÃÊÖþž~iÝHã…Ã{˜D¬ r¾__¿/¨dóV÷µqTqÌ-¨Y'ÛlÒÎË}ÐêÐ!ƒ½ˆÄ%Ó$ï+#¼ˆ„õ²r>BûoLÿHù%ϦlÏ­ïEDFñæd+ú¹O+»6½à&Äkd~2wï°ØHú-øl  !xQñ!“Гi韟R_÷®è7ð¨}Ðý~cK?bßäïá-Ò…áøú Ñ"]*áoÒ·ÓnAùF$ˆ¾H_ÑïÂwó[ð ":yA¤ ¢ÃòùB@@èÎïó÷çÚq Ô‚€™OKn€A©F3":¬_é_ ­Ì¤Öëº<¡RdjÚW칡ùòmIå¿•ßPK5lŠ]°@€E¿¥á$ÔÏTÑQâødd,E©m•oí+ý-Ê&A@‚ 0lVÌ'W„Ãê42ˆÒ¯3öÃÐ=Ø÷ndã+5Ì@H"åƒü•w ¬xyƒó—û ä¼XæûhEƒúﺗ†p|A~4„:– ø0{©``ІÀöƒÔó ü†÷†* {„¿pBD®¨G$;%æú¿Óß8•Н\Òu2r *¾ˆøw›*~è2ÎËÀãsïØ7ÉkÎŒ¸¥[ÙLÇA¬|H°ÐÀj@p“‹àË®ö&äìn|(ò¬žÁåAó7…n„‡Þå——ìÛVšhø6Š6 ƒ\î©2‚§¾öØë‘­f܆ÊFvR*àð°bÜÖVôÚ¨±àBðrÂ:èkœçu:ðÍòˆiâR€^ïxÁ0YU沬*`‚µt’EÞ¸ˆð vf þoµ=¨–kRáCÔx‚ˆlAWQ¢aßÅ º2ì‚õ]z/ýχ08 —K„R“‘ a!œæ²‚9j@„ –žb¾4œÑæÁBˆMÃ;ëìõó6®‚ÜMê¨EkƒçC€d¿{`‰¯” ™ׂéÁz žo A<ž¸x…»³‹S ø"¾ÉHf$I@\‹\ã#÷½ÙRZÜ«—¿iË8ÝYÖ‹Ü5„ ×æË#d›¸Î2²žˆ`¹%û   GF‚0ÌO±”ZÑÊ#T’¤µŸ—Ð!¬v¼ óh€õ|ïpb§d€£8€ò&„ÒÃáĉµ(¬F'ö—¦ŒrðDX$Ç;è ?…‹=:²p’·<]Ðð•ƒïƒÐ¶ˆ1X<4¼º`%÷ÂI^˜àÕÅg“. +„…•O„¶I?ufÖù^]>¼\¤àcX,ýÍû—(ÉNz0î¯1œøB\ í`ȼ 7C1lþ÷çx˜ÉvŠìy\=Ãêñ0'ØoÈ´Ûq)…EŒ#IGów>,#¼õtÀøá›íš—ùÉ€KF¸áñÉ !¬G,Ô0⿆aL ƒÁðvHÞ-G0D¦’Ï™ö /h ÂÒßuðþ'm&: ;5äDA 1æTivjUcF•¤[oHÚá8!F9ÊOw µpsØH„ÓýÈÀ+q+¸?Ì=Ñ„-Äꦜð°û2±$s| ®qiø@Ä">£jˆñ¼-3æ°pÑu¥<ß#0†5šư‚å†R“t—''û‰õ·9Ü£'y­¥Ø6t^/8F,‚¨´$B"¦9À0§ ‹'^è‰ Â‰-·»ÍØ\a"p‡@Î¥ŽxK¬Œh JàÄLœoMã–=ü¦%ÜÚw!Å1 |ßqÖ Qça¬ð‰ï½;/q"jñ¦÷~Š3Þá1=ø}A òº!æ¤)/ñ¢†qËbóò…sÃÅÑq†Rlf²_…·®ÅºïUífGOùƒè¼ÄOò°kB˜t¥å—ÿ_bìCd ü& K1)ça\1¾ |ŠÑ¼„/E§Š»[‹ADÊjñž˜%ËÌ0"Í!Å-o´xVÅ^Øw±µX©pìS _ 1I3}qK¸#¨´ä‚ÇÅTŸÎ5!T¬7v—N¿õðªxA6o¼J#Ì/td‹a0¾³‡#Æ ±Ž/üŠ1ú‹cÂínð}Ø,œ©4݇ûÂP…R‰˜d<ÍZµŒA:¦H““XѰ͞Â}‘µ-MêEH™G2.»‰% ሆPÒ±f4Ç’Åc‚éÔå‹2ˆÒ)%lÅsa–q¢ …´édF†ÃÝâÃÛráEKˆ=­·“1}˜H ~ç"²A¼?±È D• ¨„‘vHg°,^?…["éˆf¡ÂâàMèVtn#¿ñà„ˆÏE$Pp4 c¨ç6Œ>‚Gn¨+㤱ըqÜk`˜bŽÕ…`)È¿û$>&Èî7ã¯à°l¼ðû:¹}WÕc9à×)õ;n7oC¡aã»±(cÃ:žÓXXc|pç¸!Š`ÿ9LóÞ¬qÒØOa†”hà(;²“×ï“Ø•qú ØÁG^Ï¡à èÙstrÛLÆ$Ê¢Œ u\#¾·½È¢ÌJŠ©X”ÉÞ‚½‡`ˆ„ê‚|¬<ÞŸœôKïc-a‘Á4g|sò!R>¿V°B·Ø=F%~¿ rcsðÁØb=vCpe¦ÆVÞ&ÃaÚë6~ çS7šWábî-wì‚ÈÃ$uT•b"!i9œ°…*…óÁš£¦ö¸E‰  Ç­Çb§áËG»Dñ!¢ÀǵȠá+öÓú-;‰Çy<±1˜p.HJ%ÌÈ$Z¢òÎ#Yƒ,ãu¸äB„^!’&,ÜÍ&WÈG‹pýØ‚ìÖ< Dž±"Õ8~̬(Ú†×Á†ˆ…å=5~,`hxÂ7_5´42Üñ5°ÀU/ ›,8Q˜d‚£­Éýò‡éÁ¢ b±´ Ñ]‘%+ŽCÈ…¦“ 7¾óö|b>hð2€ä!1,¹q§HF÷‡¥JµñXm¤r% “…Ć›Bù”좩•L“çÆÛ]²« 1¤yç;DZ1Yrq• q×4%/…9UH&à07ë !~&»!ª/;ưð¦dØW´O·¸Æ¿"+aÊ™lGöA €V'd¡0b¨ ŠóKícîïdÐéSù[ˆ@ð¬H¡Å(ƒ`‚{‹}¸…À¬èÚ€Øa¿B7ëÿ禡¶|?Ý4“\ùèÖ7 A@æ'S“ï;° =9k›¬@1¦Œ8›‚ë©L+P¦•È>\Ê#‹ûdê8c9(õÓƒ£ QŸ24YÃtS® û“ Ó°²ÙJöB’5øˆå…òN‹ÔxKn(×ÝÊ…'C²†­1*ý!lÐî'.åThT0,Jê)W•#Ê ãIÒL¾÷“ßÉ…âTw‹sf¹ âÅ·Ù°qF rŸÄn1º-å@2Êà#q9ËéþˆQHš¸ªò¢jC7D ¶\vÔÑB€àvaX~”¥ge3DÚÃL(TCh&¨ÊäÅrû h’Yž-pæ@Ëa„Û&1¬;´Íàã^ÆäaØ®D w¼žU•s”ʈ7ÄI$:X¾Ü–1‚3S²ÄV×B¬ÁHP€DË-cÒrhy°L¥+ –Íž-岙䗸×!CçÝmòK•“BvR¯HŒ±ËëŒÂp`¸_€GþÒ”eÇߦõÙ%Ù,Ü HçÜݾÄíeÒ$ÛÏBÒQ/ ¿’è­Ó0¶løz“êD-$•Â4ÕBB¼ºþ½# Ì¡…ý2{8 ÇYÊgƤäFÃA,Ø<«/Œ©ÐŽ+MÛ.ù–·ª“ ;PY’yÁÓ°ÿ»‚¹c¡°x#óyE¥÷!/°eöð3ç;µ0C€ÃÊz嘰Ikeÿe?º‚Êä'q™áHQiB+XÍÆã~†hã‰k#6a–SÍqfqâõa|—ØŽâšïw„©ñwÙB,÷òÚBR-Í_ŒÊkˆØ,ü)º0¤EñÃ@‚tœù1–ˆñqa9 §²…+ Ekv5÷}Ô³ac8ñyÃÄQærLwÌÉÿâãÁ΢wœn¯Æ4RÇ]ýxdœVüD˜¥¿|âUNcÞlÀ -Üöe5²ƒ—•·lVˤÎV-VëЭ,p¡mD›ÈžMBr¸$<#æ‘[µX&;ɰ¹aç(Œ)…óÍœìâÆ9ïä3À›S fåW²«ù¦&´ïº~=͸‡öQæXB|è³)…G¼[ßÂ×ݦª´Í_b×BÚØ‡H^ØCÌ[b Š5y¯'¶ÞA ÎIçÄdy™d –“¼/gHÄÞxêŒæ µ-LÊÁ$C~T*Ì2à‹(îþÒBÆÆÁp®  \çÜp›áëÐv6›[ Î眳¢åÎü\vTŠTÓÎzgja\"ï\‰…F›Î+%¼/й—9<³‹… 5°i³ÚÙŽqkµ0»l'#ª¾0-äUdN^—7Ó…õdµ3¢Nùk:³ÖÌ„YçI‚ødÂÅPÎ9'~vX–gP é5„s®Dh”Í„ÿbr¬ììyfH4–ã†å'hìÕù­tÖ²ö••6©gÕŠÎXË\t~µ`shϾߜ³ ãø<^D|Šªi-¡*aC™›~Gªzþ hiä¬'¬ „T%4\t”b@ÙÍJx2ýšæPd81¿r˜wS:vÌó‹N£¿É{ _ågg÷è,ðUòÓT?9LN«ÜzVE@²ŠÊ‹d›4Óâ· u–G¼…èšMMlhìôšŒŒh&QBÚÎ0Aiš;}š0ˆ“‰römnú(£‘fÌk“Ót«u~:2(£9N\aÁ[Ò×dcqK*M¾ýmI¹-péX!¸‡Ñ™è¢8 {Fiýè×ô=mL–IÇüþÓ^U]ÄÌlÙ"VQÄì¥eœ!A”¾ˆ3\ECý|4áå¶3ÚWð I2|ôKê"ÃÇbvOo%É ç!MwF­’cA´îŠô¿nò·6¼/p€JoÆ}’ð_KZ ]‹(Ž: Ôö@Ž ¹J½d*Ù#û$A—IŒºÑѽP8$j8=í"t@,D[¾Î…&ÏÈZÂ'&’NKC"–¹é(u¼ÒS¾pX™l”&ê‘§´/÷Á(¦7|tÙúÓ7=à2þvº ¡:µŸz„’á9W©a|jÏ¡ãgøô¨F^~ò”¦é™AädRae®R³ÞÔÁÒ—F54'}‹¶I“èCÌéîtÓ¡ ¨ŽK«/t14?G‰û±TMý‡ÒH ¬ªjõÔKæ¼<{Äz9ALÓµˆåJØŽ¬v{ &ýÔלÙr®ú¥‰‹†¨uÕPºéD"4»Ð\dRo¦ßÔ²(Ô(£ú;–~J«ªcq¶dvô¾€l0áÈ¿Í LKš0Vô¼úÉ)R@/Ö|MŠà3©£„T§ †NzÊ}µºÅGÙ—†¾w’Tmt5Jpä´36h-µ€³nºQŒOö»v§™8@×GµoÐ{À¡R¬#~7,5xÎj^J+ üÿÜw2 ¸  Vþ<—°ÈWn»°: ƒx ¥ª)ÖHž t¿9-½¹}/%ç¸P.·z~q>þ"à%âjËêÓÚ³§¬L ÕójuHÚê嘵ž\Ь‰Ö‡¦U†ÊúrO®ÙŽ+[’IuKBKà¶I¯­?ëy5§Eh-°Æ‹ö‹m½¬;¿×ú]‘¼Ñ&w fÇ…4uÙ5ê ¿$tÑð³†z‚ü®”ÆÚ¤_kát uÉfþXìY€·gBÁ—2õ㌕>«Ú ¸  Ý:Jmß 3Ÿ‡Òö&IãõcÍÎ|G›ŠY² ;,U—3 ‡ÐL¥“€±ëƒ(ÙZ *­­®—Öê iÍZ´µnêΫ…RÒåµÛšf^·„!CÚeœ5OƒŒ„‰ðT¿+èjµ •uÊÆhí¸6`ˆ±Ögê +'F݉™=xc×È/ªÔ24sµ>]Ó®ùŒGã¡5êºÒ§¾†ù©J¶[êô´/õ§žŽñ®!V|o0!TVÞCöؔ#Õ°d0¡Ò“¼.O#ð>óM—³ï7Jp>0…Å­½!dM±ÿ+Œ)a-"ÝÓ ‹E¯ZC]ÔÛÒI°ÇË3-F‡S_ÁÙ™«‰Ï)ì»Wó„}_&F„ n¸ãd„Yc-bLF¨)|ÑlÄÅMDc •Fcà ×ÂéUÇ"si“V‡\β»°k‰ÈÕ˜s½—%–ˆØ)lËØøšÞ[{&FðJ–Õßê"v2’ÙØEÙ˜ØÝiÔÈiØ}µ%FäaúƯë*6wn–Ã.#$®u—j¯âñ ºÍö9Óª•×ûkèÀixƒ-Çæûhâ¦cYçœrÒú‚=²ôh¬Ù|²tz3ø>$dó±GÚLbµpNó½î§ý¯Í&íjNA;^)Œ™¢A²U ¡lùµLÚ2ÖSFdGŸ)Ø ÒäÑÚâ ²½F±å1àæ>u›¬ «>e»‹©l£lÌ  RÕjª¶fluŠÔ—?‰‹CGž~5 °‘dÁìJiYæ-K éQðd×½’)'1»X}LÐ7–žSرRX5¤zGà bq±ç­K6ö*Û1;ös°Ÿ\]ʵºéG£ž–‚˜OÐÏk¦ð9g)¦Ìd¨‡Òµá.´:; q1Òm%ºÓå‚zġᎰ¡ H£PA, Â4gÚq°Z9hs«WN=4/³ÒÊA&E¬`͹ãvÄ0Òæå¯ffÔè ÒmÆ+S£¨Å¤{ 1›ž“k“Ž«a5+à êlÆm‰ë¡9l.Úμë¯SL­Ù™Óöa%Yûã”k¡š™ Ý×^ “Óž6eú”ºhû"x ëE…*3á_Ïî;'9îDÝ«6Ð` !Õ&h˜ >jÌÈ™9M–I§fþC»k¬to#g½x¯\×çê«¶P ¯ ˆž9Œ°’Õë8ö%;=ã5Ù2Ö´3*qê1õ[ɮйöMmð@m¹¶…ÙÂfdb(DÉŽm_XW[}Ѧ(ØRE·r@lÂcQã,ØžhGâÔ1S„¶.Ìôò¤x ‰#Í¡mÂI›,ýVbá¶óÚPi±s 'µ}ÑöŒ¡Þ6m.efR6š½+Œ¦æ5®4JÁ”å`™Û®Ì6¿÷­Ô¢úm«k5„éD]»›RëcVO´92|çZ5B;R€õ_×j 1Òm6R©ÑTÊ%n/;SÙÓíZͤáP¬¥nIŒÒ…‰Zp!¼ ŽöE¸ ‚À€i޵ˠ»]ʨhXË{lÎtXšY Xp4(e99ÿöC–\Ìd@,pBKíòIËÙ •i|5Úª„îÁ Ûý¼öU*›s¹û‚¦—ãsk;Žh6Ég³Ó>!…¦Z8]ñ)[W­^Živåz.d±îhŸ#ŒXÒíðt²òÛØiÆq¬_Ú0nÉåŠ{k(e›÷°°_Êp‰·‚ûj!$IpÛ¤Ç^Dn5·ú/ÔkœA,Ù¢ìHÍ€I±Î%P® ¸Xêô‡¥Ý¶\¯ zÇl_z4Í =µn3¨ywØÁò4âŽUÓ `fôé;·:äèLÄ5p3n¤ž\÷S%e{›âîMBBò…Þ˜Ö?ƒmèžoØ{3oÛÀÍàSƒÜÑŒ-ë»õŸBDXç"#ÂBàÑ‹µw™äVpßZ˜VÜçC±nÒ=Á–I'HÖܧڀD õÍVp“1»ùÛ—é£ò‰âv¯§¦ØlŸ¥’ÖëàÖYd]i¸mîJ œHŸ\³™ÝnäBš@”læ¶qj·KÛWîþœÚ!Í?ŠÀY¹ýÛ`õ›Ï ™Ìºkq«Åxù»5€#°Ÿw‹h¨¦íé€ÄDhÁ{¦JëPÍÝnçìô`^g†i›6~d]R]èºÞ½xÞ€ä"d×_ÈNp=ÇíðùLÆÝ´í_¢Â¹\-®bk5øLyÛ(V2Jždw%|¬¡É”寲ä-“Ö²Fžo›…¢ÜÝ"ž4åÝÿPŒwÇÛäÊR;À-?%¹±_ò«è#ã#LŸ?f÷´ækãã¦Lï¶£a`‘÷ Mç ȆJïp2×dµRáhsxgfÞœm©wZ…êíÀžGøàÈÞé-Ų‡ÍýînÙ·°ÔO'Aa×&ýÀ9m«g¦3C¹å5!wb©³ ‚ZNìRšè`d˜{û"$åØ'ì¾€  ÔØ{$ ”á„·@ó2£Î3?’ŽÝiTV¶TÊhl|"»C¸‹o+–Èhtp €g»œ†Kô¾×7ÝâP]ð¥©Ò6Míl"7J@Dtäd;9 OŸÏ7{óñ½#Àƒ0¦ˆ¼¦¢Ð2CÄ—¶Së˜È+àédv"GÅë¾~|Wq,³ä÷€‹võ ò{ÔXDï`@%ºøÆ&ˆzñÙÈÚ÷¶cøLâ[¸š5×d¶ lå 7®ëW"‡ó@~ÄŒÃTùæe(*×ǃÖÝ›ö}$XŽZµ¹Õø…;ǦtñmÂx‘¢MEû7‡{ff GŽ‹†l“ÿ£éñööjcúvæíØŸ)!r-“¦€0·Õ´oð蛯ø+Ú!Ýâ Ò¦=è5‘„÷Ùy}«K lç ¸×¡[è…Fï£mÆoˆÍ^Äè½7„[V} ³êR{¸WõWËéä’F¨ôU*ÐpŸ,Á]ouþ¬îNæVÐT»Rk²ܺù ‡Dl¿¥‡y,V~:1Í 3LN¢€Q ®SÙyþN«°„x6é6ƒöd ÚN·Þ'ǨÀÓŒ…úDñŒƒsc¥±“ÒÆ4 \ñ¼óæL‹In"pp ø¦«å;®Q í†ðŽ‚Æëÿ%¸ýüàÏë1ÁD§ól§~]Â'ÔòÜõ€Ú.^)' •àÿ:ú7ÍÛ ~¹ÙySb!dï–× n’¥z¹aáB™Bow&Ä@†XË’Ñ,ï8øq$ÊW‚·T£p 8ÙÎ<ós‚“„>ê˜Qïÿí 7Ía¿Þ*@2 H9¦»/ˆD] „Ùïv9ÖÝÌ€Nz¼JðÓ3ü¨ÚÌXJÁIàËðóÅZÎqÖ †ïÂswðw%œ;‘­‚w“FßðŸ±EjG®ÃSÔṲ̀,|÷ ÷·ÂÙàñ¼Õ<Ý / Ø¾ÍÖ|]A#[YÍwïÂÝ ÖŒ`öÛ¦¸…WÇá`ðœ >ƒVÛ®t?ˆDÂï^Þ…ïÉŽšƒkâ›ê&šµ\Xæy×yõÎú¾¤5ìÁuϪðš¸<Šò‰åÑÖ5l"ñ²÷y· õ‡]Cv‚ßNkzÂRe \Å®ì`cÔõ1ß·ñÔkhHz·Ä/‰Wjx%\Ý1ía¿bûa§B x‚Å|2|d* •wVp¾r¸:Í$Éh °±+cÁF8† Ϋ\‹Ëìò±4í]xŽ¥yì¯NQ!ÚrØE¶ÂŒ*¨ˆP m£Ñ&Øjß–[MsŠgsaŠ’øÑººF±ƒN+á,mæ\Oä®À¯D¡9߈Š'<-^1MÆ3z Âø>3n¬l‹S¥ý u&o\ñu?€ 9aumk`è|ݾoPÅ#Þ)¯ :#±Æ™Ý$©¡tƒLFö€‚¹’†^Jw ÌqÎ8`+œñþk41ºã…iôâÀpÝ·cÄwã9ƒ‚H¶óz‰‡wÏ:ø {8eäPÑ‹ªÞìE=¬²l“Žç¤¥ÐØûq¿i#—3mŠ!3Ÿ»9ã Ó#Çuܯà /öæ îÕXØøc'ºFyH½¶Éb…’„\ÀJ·´èÇ1ão,E›·8Ø÷¨¿yñŠn΀Ï:"w\¿äJDúñŒï‚ý}±ö½Ð…ÌÛh½r5W8}ít…[ÈÅ.•·#ǸÚ#m.†g$Œd[/@N²À¦X‘ÛŒ~/øÞ Ò|^œÈ Y=m½R¼C‘Hö…ÈB‚î៓€ã¡ŸE•|¶4‚dÉá1r‹q×Å¡ÅoWé–pBaëæl%ž´ð¨Ž´¹H~û /—#ðõpÈuQ'IpÎ ³qpbÕ¦ç#(çA²‡œ‹›ñb££±«+Œ;%z®™D¡š•D‹I»`I’“½§£ü.ÔÔesø¹5¹Îw²\Ê wÉÙÂàqø§<•ìðkìzmE\q>¶×f 4ßËf“Ä““+mJl² ?•ã;„Ë9ò[yŽÅ^L wJ0üÌ3FáC›\9[^<»|ï¿­å„jù*|(Q=k“¡}­åÚÕmæ¬i‡(£Ë£t è*8¹œ¬.ï û$üÃ0srÖYâ7è“CÄO壎ˆÜ˜êÇ.‘D@±õ䈉KÜY@ÿº¢èË_ÙÖòSGšÃÝ%'If÷޳žŸ‰¿¼üý*w40vŠåÈZŸ’ä$H}(ÏMæÊäs?)¶¼ï\áyY€Çåóâ‚EN~8†=/ÀcæÀdóÌr_®uNÁAS'ã^.¹µÜ33_÷Ú GMùÙ‡ÔLÚe'¡èÝr†¹°[4S-œÑ2HÀ˜ßI›<ì+ì&x+2å0sk¹C4S\ãÕÚª’ò…Þx# N.—œÌ=â þWFg^·m›c°&'¥å‰‚Ù‘Ma½¦r€[$ÊòáG“ “ S`)òæW ¸ø·|™™á›Ó¶½SiŒÍ%ï:Ǽ·®™(¬k¡ÙëhÕbò|¸~¬(ž²Ï±ëÉVùú9hxFÖ‰ë‰ •¯º¹‘[ÚÏaô°ëFûœ« ¾aÒ¯K›Ý@ »°+ÁÓ°#׳I7EyÍa Ûæù%B%v’߉½P×@,TˆXØÌvŸMŒÛƒíãš ÜÕ>uP^˜ÝAî÷…¾{ðð¤Ï ª&Þ-~wß{¸v"~2ü[ tî‚÷‘Äf!‹ùu§…·Ë‹¡RèÝ󔸖4Üíä …¹ù,}ì>u‚³Ì÷¿ÌÒ̺¯b/÷˜îßo:4ß»j§ÿî‘07€èîøWÄöCí© ‹²?éŠÒ³Iµ='!Û$?ÓðŸKš8 {ìžîΟ3Ìó ­lT ¯å~ßÀ;%Š.) ¢º@=Fõà i÷Ò½qþe'è–BUÄ}ß@Îà+ÿö.ù‰§®(ßÁçßÏì§ò&ºÁý¾®ƒ7i8 ÿëcæ¢~’~POÂßDãM@#Q‡ÆnTóm¸ÝÕ:øLåÆï“èw0^díÑó~hÂ-Ì•80L.|Yx0B뽬~‚?¡-á»ï‚0I¡G]R’ž®ŸàƒÖæt#²áºuáJlQd¥ys½Ho…©>VP ¾ámð œúÌý‘.cX77ÐUÑWøG;Ã|^'Ä?l/©óïz~¾@~ ïn Çu»Ÿ*ôd‡+l}ñCÃ0%™5Å“n¶ðëu|,!\ˆ‡àpÈÛJ"ÐÙÃä>»k•.ØÅѰxI|Üœ|'@ì½ Åסx{ëG–ø 3äKù…€ÉÓê8$R†÷*Ḻð'ø¡E˜yä>‰Ws¸Ä‹ÿ Ž•¨üª>Vx›ÕZÔŽù)5>_L=ÊÛ=Åxp{Á‹/¯zûá Hçx+1ãäzÈ7’½V«¡›ÄzüAQš×Š˙ՕzyÂ_QÑ_'Fðx‰¸\ìC¾º²ŽïM?¼&DÙËŸ‚±D_È›*ήÅpåÄóÂîyÁ&:ôú2ò¿±U.£#o…¤¤WÚÙ'Ãߪ‚d.™°04=líŸtAŠä‚&¿Gî*·ÝÎIî"ÁU È©lvñMv‡›¦OÛÞ‚é`XòEy.‡ úãþrÉ3Í|ųp«üaâîÒ%=h4énÛ ô¢<8çG{ Dá ±kçªDê!L~¹†…À˜€ i±É³ÙWȉÚÌ®=n[MÔ<5á_bCÇÑÿñì«»µé]9av&UÍéWª?´¼Wçbr·SR¨`—v>¶GïVtG óJcT ‡Nß‹>P®YØ­¥èƒ€ Ãq›Á˜ïƒƒ%ßRÝwÝ•˜çƧ&ŽJàž|oZ%Åü.…–žöÆ:aÞÆñ›íŸ—'67ùÅyÕ Ñ„—LàŒ{ÀMyH„LA1Ÿ©´®]} óa3pçaÞ‹Õkw½±r¹ù°|Òàå[ˆÌsÖX™¦¿<Ñ F$`?Èó#D^§pÂ|#©MŽŸF¥#NáÛy<„=9/?€ÉYŒ­F:KSA=\¿Sùóc&â;`^oV:0 ³WãÈ!ùýߢŒÄø;ù3ÞïòˆWKìÇ?Ÿu~…)Q˜óÓ‹¶Sb/Ã)¯Šïeñ2q ý@™Õp‚Ð{& ï2|K^]‘/Af§äKŸhl|áGO£ÀWÙUz©Åü ”4€WN9 «ùÞ†Þ8ø48ÊGÙOÓø‘CÞøôÇ~,šîÖ­o:AK·€oRCqdL}ÿ¢J~÷L¼/Äë.÷½&cè¤Ó1ÜÃôå .BÁ€ÌÔf¾`£üó8A7ýÿÔ5ï£WD™Ø—å,wRk} /^Z~ÚÓsòäÒ!=|¼ðCr€rþé›ðo¢›ÅžþFÿ5=c‰ú(… ¾±ëÅiŠ¡êÓôQ†Ä»•ž·¿øÔKæïçɱҲÎ2p’?¦úi\L‚ÉŒDh»ýc gÔ¿P0‚±~ ñmШ{é Tbk.|¥¾Kp~±†÷h½¨µè®óŠë#õΘ½*;M/8[ŸëËòÏЗ÷\¯Ô “‚ˆÁïz¼•)ý†:¦×7&4@™mWKš&œ·ÅNRðZo´R/l8tzC‡‡ý êԻƘë!õey¶Œ ;_Ÿ’Wä DQž‰øp ~î2…i\ä«/öÒúbak1‘nƒN¤@¶æózŒ½Ê> 2ߌ 5x(Qez=ªaÃÚ8iãà&›úQë?þÍä·ž#~5¾\÷ïW˜¢¦ö×Ë&¼6g”ýr´Å‰`ïêìg£-x‚Óc33…AŒaËéõçA?¬@8ß} ‘°yößy­5f™cO÷ûú{šö ûÆDŒÂ9o½þúXô¬ö ûÈà×GŠŠßŽØóvHøz†=Èïëãn䛣íCösë¢0Û>m¿Ìø û‚v¡û:´ÏŒô¤÷„0„¥|ù*ð—¹Ï¾éy§¦ü¨À=ä¾×C ]|¬Á0_H V5¦I탥Ö0D'æѶΤí!Î*"Õ—±&ÖfÃ#Ýà)Œ'øtŸo0S6î`{9 xƒá¼ÏÛcíiÙr±e x#=V^Ü—'ì¯'M8,r† £r"gžç›vñFGqï¼ÏE¼'þ@èÆ÷ÞÁŠÞ’—”fïÙŒ—fJQÌr¶wf3K7Šc£#Ëç¾Ð ÀÛÓï•÷èÞµ½àžä×(Ô/({;žBïëzoß¼eæ_ ÿýÖw)ˆá êuko@°ŽƒÃѽçAr0RI;›(AúùÈÎÉßdrãÄÕƒò]¦ ªJøˆú¥‘YøÌñrºÈ5ò+ÂE)þþb”ßK-uL߸>w…Þ q¨"ß§ìý†«fÈóT/ð|×Äó¢®úW}?ï\˜ qæ‹ ‘½~½Ì‰¿µyè‘µÇ ‚õÿ;?zÖëDwûË­½¶^9‘šM€‘ô½ 8¶nþG`EŒÙMø5%(»õ¾%Ÿž©AþØø [ò?5¡ÁgøêÉø)Ð0ìþ8±.ºãÇð“n©¨Íßrâå©’8Ì&Ͳú#~ÛÞ€€úÕâkòÉø9÷åpiŽßÁ;j°Hù¡ïò Ÿïsà¯{u²§|RHÚ¬OÊ‹!úåžü°d*î2Æ/O”ŸˆŽ|[~N¥ƒÐ»OÕãuvCÁr2~ã©Ù>È7NTIdîû~7úÈkŒNÓÂÍ|6¾}R•?ÀŸ=F'L©Áü=ôeãÒËïMœˆ ÝÑ|-áZ¨(Ê$›ó“íãës=ýo-ôX„Q_í)k¡œyÂÝ3ôòtßóÅùOIE¾±žs‡Mk&o ²¸v>R¡¿X#»Þ@ì¹õÝUþ?:Jçô]ž‘|¸_$§:Cд%B—ÜgÂHŽz¤œŸšøEàò?÷&ý:+¦>U¿)Iá5u¥îŒ;¿¤êàSoÝ{‹?þDŸ©Eÿ–èÛÚwzr»r‡­ö@Ó·ä;CžÈÃüÈBÇÔ¼K\¼#ú[|¡uâøP@&LZ'°Gô‡Ìü耼òÆÏÉcïtÅx°¤Í@#ìÃÿÏg“úØóOÑO ûÍŸèˆ*q[‚å#¨/¬× iÆùî”ÐC.߬ÿÁt¡å‡úö†Þ•6Rxª8ÛCêçùuÝ5}î×§o‹Nƒ“÷Þü¢¡˜•H*Röÿ÷[žÜ¦­v—‹?ð:ÔF¿F>°ƒOç¿#]`Ñ>‡?ñƒÜ¿îÇ÷ëºFfÛ<]©KV’ßñ×á!ûßyíFg@‚?ÝN=4tû}g#ÿsjgÜÞÏU,Q¿§íÛÅ^ß¼/Ù•c¢„1±ŽshX³81÷ûû§óê=¤ž4o|ÖÄmøSZÖ*sY4ßÝÒÑð !HbJÞ°V—çs÷ùXI[txÒà –v ò“ùi‰Ü|¾›ÈÔS†î»ae0ƒþ+‘ ÙwÓá‡EþüêE¤—ÍÂ/Š úI‡ˆþý|ýÓ’œÉÿùG ÷æt~œ>Ò~@…oƒamç ýN™±ß_Ðå+¼:1Q<É×fÂ3ñwÓSþp>'÷Ñ ær‹¾Ê¾°>/r«›-oïv$F>r¼®ïM`o.æ:פÁ"%Øoç÷Þ#¯:Ðm}§L(L»ïê§þùa/4fÉD)¥Õ 5÷{.‘ülx§ VJÿ”VN ²*Ÿg~i¿ûŒÆòß8¬²bw'¶.êçògí1ëŒýÙ–ñQØÄŸm…úåùty:…ûÑ/ÜUOLÏoÿª™Ðý!ÍS@gb̓¾g›gÍÓø¾rÙ}ßý D ”ñù°Ñš}Ï„u"I%ônø @ñF}èÄ #„¨âOɯ0²w•÷T=ÀáÂÐòo —94iùþ¸1±yìó  ‰mL€%€a K¬Á¨lãÑ*tC²~¹ˆmñt ?Fb€8€Ýîq€ÿy°=D&É~T @öGßN¯"gÈÑZu€ó|Sk~;{zSdm"~Õ‡lÿ|%€dw6zé]i€‡{½ y= €íK¡}ògDdj€¾{×Ú"jú˜C¤e—€© É,)B€7 Gx¢€M€<SAF{]*0o!z¯}ŽNïít¢{Õ 88p<…jÌ zx_ª€Iz !ihDÌ ÂP­_ª€Ê ¤ Ù¯vÑ€dB¤_Ȁр²BC,n€0Qwh(‘V ‡d½€’ ‰ÔÞ~ :ÄH߀‚ïomÑ€~ {®yÉ€V&M6{ 2Ð~:ü5‡€sU™Oø€;{q8·x_€ [ÛM.~z{è+tMÅN)pð:_~ç€ ]':B&Y=5dŒ}]倯T%Zà *{-I2!É€µw@~ é yF;&~ƒ{ö$å{©z¡¥7•„Žzý€ü< ](AÄ"Y(•yÎ $ =’y¾½ p‘‚M¥C­x¸@$Ø|ü}}Ê`çw8@>"ó€€Iy"˜rTzX»],l¢Z†{MZ@Ì"n =5 C ÚÀ€pGó =Ù8!ÆhïHÙs,r@YÏ_˜}J $+ß)€ôþœ}`(%Gp ?C3kì?C3)fq; _$¦pE i Fo_÷z‘œ‹læ€aœ' 2~! ®‹ žm}$E|Šz4CÚ7cÐ"¯xÕk>}ŠXÊ Ð×@¾^ ~Ù~U1ç8ÛaŸD1ñ_® x¹£N5µ6~Î ´;P}Ý~›Ë§a°F 8˜@Ÿ3%[²€n Þ+CNöje;Ÿ|Õ(zàk·3·(ЃB®?ÔÅú$hn¶ÙŒN~¨]1eü€¹a1}@H4)`ëâ%„n\?™B^NªP5 É7oyI@dwÌB¦~Ã]n /|K|ä—ä U)ÕP`s]z; c§¿ fw1`ëu=‡s®l‚åQ(€Ê‚ž)OcÍp¿‚Á1ƒ¾‚mƒ“Ou2ƒmƒð%ëOô?”4ÒGÓH”4E”4Ù PwƒjW¡j<ƒnW{S}Å9úT'I©4ôn­~`Hò"«\etèp5 /&‘/ÙPºC½4%®‚$ƒ‘Š7‚ÑÂ4ˆƒäD# õ9ÀsD‚™ƒT%¼Gƒ íîGÂh¯ƒ/ƒÅƒ ˆ(¥ešpûiÉ~cnýxèƒñA4ÿ?¤(îa Cž)?xs¶>RÎuý~bƒ>RÐoUsž)†¤hă~o+m®É:9ƒCêhyŽ1Axr€Uvå4øUÅ}CCo D ÒCÂÌC~ð‚p\Ìñs…CÆ hÖR¦{óƒuv –R’g™ƒîMU"[³ƒ°)~¹‚2BüÓ8çƒH€„ÉY:ÕgV ƒ!ÌpÈgŸt{^s‚4 ƒDg*„n W&hpƒ³ƒ ヨ!üƒ¾]·V‹‚´l¹l h®¤C}x™ƒi~~èƒMv®8ÈŒƒ'„ò–9]Ø7rmh-aØ7C'ôE #2»h³ƒát {Wƒºƒ¯œ~é|£lH%#Lc„! yO7˃¯[²?…n`„äE—ƒ”ƒñ^±~ñÂ]#›ƒGC6 •½‚‚~,YÁ†ãÛƒN7e®ƒ³ƒõ1„ñiÈ”Qœ ÅHÈ­C¢ƒ±B“„p@~+ Q`jŠ„'4Ä+‘„mf!5ÇcàD&@ÈÝ@­ƒ²ƒâv{ìF%5¸a³ xsn @8E85¶tBs¤'|k§tz„{Y´L/J³„©‚À„c~ƒ„k &’‚â íƒ[%G{}üƒWÒy.ƒ@ â%àƒ[¤' pt€Ê„¯‚tX„ ƒÑ ò‚6„Å-Ê ºKä7Q„m– †tâ„Ü$#)3$Ygó„fpP!Ó„µpÃl„5 ™Ø‚£^ mBù„Ê„}|h„)(Õ¹ƒ@ bg®ÖƒS Š€£‚d l%‚ç„7sƒl„î„Q2"¹wþ„¹Ëg?ƒ…Heb„”ƒu{}.~@ .fŠfCƒ©]!U@ %…k; ÿ. :ä@Óé^†„ç„+-…§dÊ>l8yéƒT:nxƒ¤'k#‡?ôö„†7á|·„ýƒK…, …Î$6nÅUÉ5Âueƒ Qœ?($ƒ ht{‚î„Mt…þ„IIe>„{„Shâdˆ?RHÛlƒ¤'GŽgY„¡s7éQ–„ÍPçLß*€e'ph…“\©®.Ü-®‚¾BAü_Í„HÙ[,{fbƒaiÜNÖkE´™r@€~<…! ™¾G³dÔ`Ú^‚ô‚»i&1Ñ]oSè}ðƒ "¨/… „äj­^Ïu _/F`Ûe¥BŠtš+„§'p¶^ç ¶ôc…5 ˆ!(|›…À…- eë@(6ù ZK ã! }¶PmÖ}ý„±…7i4…|ƒÿD P!*…À… v=¿…Ò…¡Ñc€P jCO ˲H5…E‚>%mw±…<0ä…\{Q'VD~qôÄpTy¸…::o…¸oò›…mu] )Ã…±…\j´mr)îz÷ƒéƒl !,ÿ…À…fDÁd“€ƒ#q'{{øƒƒ]äI샅ÝÞr „ò„rRöq„J >ZR–„è…„ö‡r…0W49ƒò„($ES[…E A#°2]RÝ[_”zm‹ GT™„_ 6TŠÇrHpȃå$þ|Û}bƒ1q+†n 18…žC%>!7 d! ÝytE‚5 üs§„´wß$J)΃ɄY„? :,‚Ý‚<„qä$ƒÖtó}g„N{ ±s""~n†½ÀIþz=†Zl¾…®"_WÉ UC‚J†¥„h€ð€9…‚;~RXÝ… "ZІÑ2fJ#í„5 L…„v ë6p{΄–„¾ 03&(ö„[ ; ðtƒ å&IL…øƒ` 2„s†ð#ª†$R–„zu~§†™_I† õy_âyÎ$¢yÜ~U„÷~IP\HÃ(‚ØsøyÓñ…-UL1†n ~"G?ÐTò u#ƒbƒä-tÒƒ „1¯…Nzòó,ïpôüƒj7©neƒ‡e.-LÙ…d åêt—…; @|ß)ÕhÏë X~‚Lp†¶a|´*{Ù…»†omrÞRƒ„ý ¤_…؆±snØ€ÿ†yB;kì†ô„!5 Ø|%Å:/Ù„2rõ.é…m7¥i†Ü†Ùe‚‹‚"}‡égò„‘£TpXzS.*i†ô‚ü0‚‚Ž„y71‡²A9€ï-’C9bhmv‡œ†ƒÁR—kÍ…¬„—àâA ©"ªTv†Ø†{/æ„E‚®"þ…·…÷†ßzêT ?÷Ë-yV†F 4™†·†3® D~j‚­>…m†Ø†c‰cH…iejOP‡‡,gSdš†þo®" ÜWL‡ „o4YLKƒò"íLp‚{þ‚·†€u‡E‚(8î‚Ö„5 ÈR•„¥„©„£Tï‚u0!I@‡qæwÕGro .0~¸ÜkÜUo R#(‚¶ß*``x‡J †_^OŽ„ó7f\5…‡äàpƒÁ&£T°}@ L{׆އïœfC *P¤„ò„ 8¶‡¬‡Ä&oX8Kñw*óTЇUD‡ž†Ž‡ v˜E[C×-©€††©‡Ÿ LJv@¤€Ë}ò´‡Æ0 xï†! 9fXÞ‡ž‡j“pЀ  ‚^Õ 0e ‡©‡…6ck‡–µ1†š%À ^T‡; ž#5†©‡¡ €â‡m-*ÛMƒ‚Ú8R†*9IÛ†J 9h€[’†ƒ9h†zg‡¥„ý»*ñ‡n ²édk‡U`ìi–† ñUˆ~,Xµw‰‡"ˆÃƒE¨‡&ˆžh|@|E]¢r®ßø^‡8]‚ׇ&ˆÕnÆ…j‚(- …ˆ§#wƒ·†’SÞ†š†.°4ˆA(ƒ%NŒ1…ˆ€†±ƒÑ…x lVPˆ²YˆR „3} €+k„U„×#ý‘e`ˆË')ˆ`$ˆ; %*1‘c°€«¦€ˆ¥ %} @|ևχò„h=„b9"ˆ: k)jÛ!L.\„ErK„'C7 GÄvˆyˆö´0žPx ö^7"sDt Zp+ [Ë€¾‡Ìcfeâ‡} ¡vÃ%¡ˆÉYU9¡ˆ‹Gl}q{A†,È 71"ˆo ÷}mˆE‚úIw}ÚE„Y%¦†ÜÅF>wˆ'^„Yã ‹…C„YÔ*1…} ®Hq¿Xt¶ Nî@‚¼v®DDH%ÈåE‘Aš7· ™ˆ«ˆª¶pGU8>Ô n=&ˆån·… 8>™TˆÆˆlLÑc ˆ"ˆ]ä}NPþÙ‚\ûd-ˆŒ V^ø^é¢VˆÜ¿R'".W }$t} ‡‰'3lz²Aw*e‘ «gx;IE§Ùƒ¬„‰wôEEùR‰Oƒ÷ˆ­TåE½‘qFN]@‚<ˆVEÀ7i5ðEt z)jtˆóÖE1‰uzTnvƒß^)ˆ8‰dÁ‡ôÚ8ê ‰– VPP*Nûg0qL*Nk0ýˆî•q% f‡ ÀA4@‡8AqD¬uP‡$‰{ lˆìS”,ìF vC.öˆ‰¯[ †|ˆN‰œwš(R‰àïfÙ~¤ )îC‰@¹„&~† ‡…Ô?5*Gø‡Ñ&T)bq1ã2­'‰‰l6U˜_µdy6n‡$‰!tWˆ`H>x±^oY;xN\AR‰EމA³Bá,l9†º‡þˆ‡õˆh‰^LˆLEþ)|$‰ø({ˆ]Mth"FBxŸˆ‡÷ˆL;€!ˆ‰s ˆÝ|J‰ã%<înC‰ d#ˆ?‰"9Õcz^&9ÝA“ƒ™‰±Eò/$‰äAeʼn(‰0;^„HA®–q1e®£c}‰'F7m‘‡…Jk– ¢I\†ìˆ÷5⸉…‡Šf°j>N8"/ÙVŒ ”j…¯‡r@»XÊEÚ„»X^Z'Š•4Èi¨'`±Gæ‡(‰HŠ|E ç%†‚g‰@Š/kòh<Š7QÓl i÷5 r°F d0-Šó‰×RÍ;%ˆHŠkêHßA G>huê‡OŠö\;Š ŒˆTJsp‹‰‡ ¨…GC6N>q C§W^MIN‰OIŠ|HAh¶z Š?‰Lw²ˆŠLR|Tÿ‰HwimŠÊ-¸wS‚—?íî-‡ ŠÖ JXCJB ÂCJ*}‰`±ˆNPkY†ï‰@BŠ„ p FŠaIP*Ó†ë^P*ÿAû‰p xTGUþ x< uo‰ÏGèDþ ^Eˆ‰b,†yŽˆmŠtvH…òE¢=ÿ‰c}2G‰ÜyYf5pï‰o£Š.…^xnPìq‘ ó`)‘eŠQ Îu®Š|¥…ΊmŠÝÓá……±$ÇŠ@力‰ñ€ï‰¡ ^Ô‰VŠ¿Ä^ïŠìŠÉmlm¶~ò~0dÕŠŠNwïhÚƒ.Šûw\J¾B¯›_ÄŠSŽ@‡£lT*¼¼Mvfx”‰·?fxèSN†@Š#÷…7'- þŠ †L|‹Ê(/YgN‘ iï‰æVü„®‰ì‚Ä|Ew'Miþ‰‹ Ü-å= ‹š%Õ4‹ŸtÀ9% JmŠò8IsÀŠ÷5Pt l}=‹Š”Š)\ U‰ ŠÈ,‡|wŠø#½ˆpŠ =º†X„Bº…¾y(‰HGxEnÇq]tg„9‹kU›D]IQM†ŒŠ?÷ [8d†| ‹L½zúˆLú }‰ k‹ŠÑ~f‹Spm‹ž~N‰å Ž{Á„ÈZ÷ O`5RK÷ ‚ITjJN^-{‹„‰‹k"‹b‹My@T?÷ t4ˆ‰D[Ñ{]†|lÀO5†6QÀ.Åq',o‹ÄT5r½\Â]\2 G– % fÜn/Dý9ü(Áˆ#‹:]t–lþˆP fp³‹ñsuA‰E:¿ŠÄ‘ gç‰eq‹øŠQ4Ì‚¥ez †C…‹|QÄ‚c‰¬‹Xmw)ÝgËaUóŠœ QhŠJ‰dq{kÔí«O0ALPGdŠ)CCö:ë‹£Bö7N²ˆf‹²z狊~ ®{YŠÃ‹s,‹Twf‹? ¦‰2_ÿ‹—)‹vP:- ï‹iŠ ho‹rŒV‰ …:þ‰ Š+%’iŠf‹ d‹á]Z`ql}‹‚E7c:7;V‹È¢‰M‰Œ Æù@ÈŒ(Œ[‹ôÏg)ŒÃ‹À?f‰®‰Ë^¶l{YŠhø §Š'CŽh2`‰[Ñ{·‹?‰ªrÅH›bŒJ@Œf&co‰šƒ>ƒ®lUv O8‹sbNR CŒÉÛ‹@‹bN€jºKc‚4 ‹€òb´I2~3ƒ…‹]TI‚yä‹ç](̉HA1X9à‹N‰ÁÈi‚‹¨-ý‹ú‹– ¡Z(ˆÌr†Gô%poŠ^ÁŒvÇ€î‰&×»Q¼ŒÑ °Qü‡!]_ k Šl;}eZnª Dìi g´#7'9õÁkf‡ë†N‰¡^è|^Œ†/Zªˆ&]["ŒMj´#q0Œ! w#{G´#´-œ‡4+ˆ­‰‡t;ø€ ‰µZ$q{ª Liêˆ4C¥ 4‰ÿ ¹l^‹ÿ AeÂWˆO`Š÷‰& gç†$? [ø Ãe!H n‰^F6wj@O.hÒ„ m|-gaŠã`í[”ŒS ,uUDsZE*HsyI@ö¹ ψ¹x8; ºê‰o{_ ‹*b–‹tþˆºe7mÁnrt!?ŒV‰}|‰!á 瞌 ‡Ñ® ±— {(‰Ò\-d>}ËR÷M}ŠÞzá‡Ö!×(Ã[‰q‘ ÏokzDñŸ)æŒ0?|8dŒ²Œ ½ ¾?‰º4cX¿ÔÙŒo‰Òêaa‚,\Øn&¨7›Jð‹ÚãvE*G j[X†7;¹ˆ…‹Iú ̉¼XIv„ÁR;÷÷€*“„gRÆC¼¹TŒŒ!­=tI²‰x·ŠDmeŠQamí ‰'Ž€Ií^8 “ŒiЍJª[³o&;Ó)ˆŒ¿é?TxŽSÇJ¹X šJNmC LŽb@ ýyiQ*Šû\EŽ?‰˜'4]^‹?i[:\úµvaŒ³.h^Œ!n²ZAŽMa=SŽ ‰¤•†€‹J‰éÑc‡‹}B‚ây¿;‹Þ„5³u,Sù>û*hZƉê*dWû*ÀÐxÓm‰Ž¤†=\ްU™u-I{ ‡7óŒüJô‡lŒ‡ ¶ ÊŒX-I’Ys:ŽIˆ‡„N‰!2gªŒÖ˜‡Nz`·,ŽôßÕÒ]Y¦v}‰é[„Sƒ– o.}J´Žö̋ᜠ ±‰ Š© 5„¯{ÖR(ØWπ贎ˆ…|‹(‰£C’f›ŽÎ õ 5Zrµ&f@Uyæ‰fgŒ¼ŒÃ!,u](‰–]sPïVc· ¯ŽTŽÝ$Hw‰P^Ç{c‡ ŠîcÚŽ‹µ‡5Ökn÷=¡_ó‰ “zІŒ wRƒ4Œ]Ô<Ó^rŽ?‰D ¶kùŽJ‰ÂtXЕŋ¶^¡ÄQڌ ¯€Ži‹ÀŒÌ6KWAZó@‡óŒuQ Cñ‰XˆZx…F€bZ+2gBZAƒ…†/ó6`c+Þž€ÄŠ((yŽ>èŽs+ŒÊŽ|~-rÔ‹ éH Ç!'7ÚE>~3&gŒJ4*zCtŒöŽ ’… J8ÑŽ7J"Á‹{<ŠÎx`è– `ŽÕŠ ±8K6BPËNCg½[N‰vñŽž\%*ßjVïH=þ ûŠË bA7Œ@Œ[0vÎŽ‡ ý(o4Œ– ú{ÉŽ :{‡›Œg"Òˆ-j”8Á_ó‰ŒfŸ‰x~ gŽ’3k? P„^ŒC&n.Áˆ¢§gŒ]RÎ >o‰ j˜PbŽtR‹ÏyŠªI(\[CP=ëˆÂ‹Ž¡`BheŠËMOYt?`=O8Ôo—=VdÄŠ.r„…j¾±7îŽçAÓXËã‰Ë &«o<ŠýmjlŠÈ,"]‹/‡Œq&ŠË ÔJ¬q™‰w ³ ]œ#91„¨D>Ô{ž‹®ŒX3^Nó‚ÈL1•xúÐ ^A%~J‰Ñ3c^‘<Œ àýŒ5X‚žJpXOÖj݌Ҏó‰B7¦4!‡ ႌ/sg?ÐTß=‡$Å —[<ŠGw5ƒ †AS€ – ÿ=Q†CŠ×ƒFf‘L~ ±NƒLv Jfâ‰=¬PúyAq°§†¡8q=î+Ód{85^VêŒ?‰çNžqtù ]w›‹ù |ŽòŽ;…ÓÄŠ¼5|jcån3‹Wø#ÜGWä4~Ö÷7ωœ‰¸1>p`"´Á†ŒŽ,Wù†ò‰fá æ†<†¢uÂcT³2 &¥Ag ‡Q5=ò‰òÊG¶„Œ 'qXyø\M»`z|‰Ð Ab~Òfo€ÏÙ‰ ÌÄMÎëTŽÀ7dƒ²iÐ ' ½ß¼HWFa,v×fE«Ž“uDô… ?¥IZ89~õm ® öþˆrW˜‡U¯X…mtF>:ˆ‚oÀŒ×ò`ų=3Y)\†V„˜„Y‰oÆW‘ %ç )Q/iZƒŒŽ1*уe]Ñ&È‚á2y~‰;xÇN‰MtJfÅnû`hyXØ(0“‹ $ „²(‰Õ&Ñ{¶K9‚G6Nf„‘‡ þ áʈ ‚¼ŒÅŠ_{¥ŽŒ €9¢‚ŠñæŒ@`ùwÄŠä9=„é…ÃÀ„Gr]×-À]®‰\2­\*]ŒÌ2Sƒ‘èTzA‘›.‹ƒT‘`šŽh ç°2ÆxQUÁˆóŒ ' =ŽJ‰«Œqt6#03mk7‘ލm)YFG?h Ì á}-\͈¿›q‘>X[ˆ#Œ-I, ?ŒÊŽ(ï"‘84Þ(‘7‘ö×x0€‘ >™>‰K‘,&w3‘¿`å ‘Ñi6Ph Ën‹¼‹_ êZ}‰mÅ1@‹ò>O|6‘ó‰/0Y2ɉçßLA‘¥9’Žó] Áˆ3‘6# «Š±8í܉ÊMÁz"‘=[i‘$~7€Ol)$©_örÍ V±$­Œ$l~å‘8El¯$‘ü^ð‘6Ue]l|ì}Hî5ü,amlÝ ¢`GŠç‘_*ù‘æ jB?ŒÓÝ 1 QtYy„ q¥,«o» 5ñV•|ìˆÝ Ý@¥¿’- E]Sž ºóCž z^‘󑀯8'’·Wƒ@/…‘’¦t'’˜7ƒ@y$Y}AŽƒ@í‘æ2ƒ@-5¾Z3’O"9’õ[+ O"Uº†ôjâ –‘ÏeôjÊ5©ˆ¢‘ú‘}$”aPO³n¹þ‘$,3ãxØxO ¡‘L„P $ì0x÷ŠÅýF‰Ë‘O—h¤‡‰OW$„ŽD‹O"’o’?’Dk ‚D’h%)…ÝsO丑0@Ø Ø(k’O’ý ‡"wHM/gØ ¨K8ŒYØ r0Ȇao’ÇoÐ…_’x’~”’çtØ éΑ²ƒØ 3‡;Cž’U2«S]lNE`w]¹'NEˆOÍ_@‘NE'3 ’ - ð%Å=¨ ïXHôWNE)&¬’šŽNE ˜’¥’fÊG‘±’"g!P‰’«Ž/zm}&Ox%´’’ º})ˆ&O°ˆqZtsÔ’.‘V&OJ‚Ö’•’µ’;9²d8R&O´‚¯Ž$’_Ý3Þ’p’_ÂPß\z~î dB8…â‘_1^ ]ê & M‘=YzHYÂZÁjóJ’lx.OüwË’®î ‰\y£S.OþÀO’“¡¾Z;’‡¨*-ea„ X<8É‚ê L “Lý Ô*V’ÿ’ L.“¿Te­’„Í^’ŸR“¯“tƒ0“C)OÈ’#I#“D’J¼ƒ*i6O}ƒ’ß’ú‘F,œK i6O§ÅŽ=6O6?‚ayî èfÅtu6O_0Q“Æk6O`¨’U6O¬&A“ë’¶Š¼ƒŠ„6OØy»’ÖŒ “µ‹ƒŒe,zÛ‡sas,zr0–…‹`’o%|‘C“ Þtz†$Ž“l ×t},zúî’€’Á¥!‚s“;“íŠr“““•I}+“ =ûW¡’?(ì{C“D‡“„’î –y*rR“5lÐ9ÿ2“l‘Ð9t4ÑWç’±}þ?J “.·Œ{“£{œ“8’CÐ9)ÌsŸ“û {*‰·“_Ð9†S1œ“ó"I“dFê Ü3“ˆE$±$tsÜpÅ“ÈS£“8€%œƒϓ4$Å“AL±‰t(Ö“G û^7“@ ŸkçŽç‘x gl]ô GD…¤zô â4‹t“BNž“Ÿ^ô be“„“§Œ`WKþ†ô Ó8ì ö“Ø’ê$÷^B{ô ë(³“’ “Ok‘}MCO$—“ý Ø ßŠa‹S“GOýë“e’:>LÄŒ„ Ñ_ó’#Ñ/PCOlLÔ“ë‘¡ØW˜yŠoCOØCÛpГk“¡×h”P þ`û,ý†|’)?wlÛd¦%†I’r33/ŒMsÑ“—BZú“˜Z¦%;)”Õ“?”1›q ”@“æ %‹Ôscô%*}rR”ªE9”±’÷ü>2„è“ÞÏvÈ’¢¶ëtV| `”ü“Û¥ ¿ŒY”£#ÏQCV¡]}¸tXO?7Vm]XO¯xv”Ó[XO@$r”]”7”þVÀ>c_ 놄”ç…È”þ hˆG’YR ŠD’/-hsŒ$cQ,ˆÀ‘ÿ’Â`)òŒÖWcÔpˆ«“N”ãÔ|“ ”s#´Šl’-AŒ*Š”„ª%Fˆ›“„ ÑyŠ ´”Ì`}Ü‹ª%"’ª”ÞŒ+”ßW”A…µ’ cž…q†|“dOž”p´”„HZ‚/“Oˆ –‘ÀŠdO… Ï”÷“O©=áz½dO "Œÿ’)¹ ž”q_ †“ýt*”°“´”±,×”>”h àß”D’Ùea»”Ž/Œ’’W ÂÛ”¹‘s”;Ï”ç’ ® ÈOØ”{EP”í”ï„u½“À% û”B“ý { &\.à”²gƒbè”±’L Ã0 Š´”–o>jd]4…´!•G%ӔܔÔAœUY†•Út甯”ÔA÷”8Z!•_~§”ÔAH{}á ‰q;:•’ZnP>•0  !@u:”%i2••è‡äˆ/•…~  1¯R¡0kx¯t•ž)çwØtd$#¶”FŒ/”«_ß‹(/R•„G%x× Q•Ÿ”Ï S6•ˆ“Ä”×#l†Ã”ú‘åÕ Œü“ '†æŽÛuú‘›K…–¹(, €Jœ–¯›,­§–V•©–×`¯–“T4çu¹–ú„È’î$çuý ý%‡êdRÿ–ü“ctÊ‘%v±’"t§Z÷5Ä{Ø%Z' I‘ÖŒ©–H`Ü–„“L7•º 1 ˜x¦Gp)°‚´=„æ°‚¬.¬x©’DjR=o”DgÅ‚©–¥­•­–µD>VbE šÔ–Ö•/”ç)'=Œ5 yNó’š/  ŒZç‘ãàV–” rӱ㒇f.‚””“¡DÊ@û’û5âk—‰^« N/“d$—ûm±¤`a3 Bïv…ƒ‰ü” “ɨ#*—K’Éë)2—†–„ æ šbò.™{.B“æ ^©3þ‹=—Á •Â1Œ‚yM—1 ö‰3—¹“jº$Á=E—8NžjM—ÔC;—’–· d¶à [—dià ¡5O1i—rGY‰=—šX"Œ’¿‰Ùw•ý”:gÍŠÊ•5Eü ¤–ã%™S—õ– c>𕿠áÇ“À’‘%}d)P S7—s|Œ—þ]ok¹%`ylkKŽ—Úy…—™HC—K mk7o———Ư‹kñ“&zµ’õ ”Ô”°p%Ø“¥+k X†´–Žp&—þxDQX•—`—á4Åh‹BDQu]“h¯t…— T+½wDQ ³—>K”>Z²„`’è7Óp¬–ý {_Ð(‡”Å/z‘’Ê —H—‰HjXŽo©— Å—Ì’3oÆ–…—ƒ Ñ+“%>9æ’—v ,{–…—­o[—敲ñ@]”48ô—´“"|ÖŽ;P}*—¬—•q‡/ä"?P 0‹h(?Pþ9ƒæ €9Ê@s•˜NEAd[“r.äJ‘–P#RE˜(•U`üŽõ—cÞ+ûr—†•$˜!kÕ—¹:v‘Ö„ý |‘ x¶" ˜T—e’U-‹‘>“t+?˜m† t+F‘–˜í0˜—ç‘ãR ø—`’y`ât8—ƒD.—"–”¼Äów„ †`êtŸvZ 6"€?žP¤bm$v—4—Ó Cu/—Q/ð_°‘¢•.˜q,Î@£|¤bQ=¯Žd—Q/¼KňT˜Ü ­5˜/”Ê#Â…µBQ Åeû—f“˜íövKf.˜šG—1Šöéqe˜Ñ…z˜p€!|^ —x]J“SfÕ?z;$ÅzGNdn‰˜õ–Z:2 ˜é–yyÓ±’,> ê‡Q ò~Œà“.˜—2Sí~T˜\™—–—ËUPÕv–F˜|Ý…^å-åK’ýg—±xž “Àh—@)»•Á9@l\v|’¥[¾…Ñ—t È@Ö:ºŽÔ˜ëÍv>“)A!+ºrbP¢`ò“™’fˆ&D~d—8GltO˜è“Bµ¥<Ô˜ÇÐ|Û˜®«H˜ÅhBº å˜ËÔ9@QtI;’¤B@镨 ²B?Œ‰’NGÝy{–f l»gh’\Cöa¿G¹#i“³ŽÄ˜u]P‘f˜Ô˜J†fb“¾—uV˜¾M:¢=[“Ãe‘Êë$»˜tDyPA'™Š+’”Õh™2C½W$F~‘Œ Ù"Ç—“Ô˜·*P—•@ý’ ™¸Ζ”ŽG<Àt$’ME4¿–Ę®™´“‚é7¡ŒÏ˜"ìNG’Ï6&v3’éRÏq…fP/‘ë‘Õ3§z™Ô˜¯ ìy°˜fPyÀtý˜é~zWfPyB"^Ô”éFqŠt ©%,—f |BXŠ»”C9£¯ŽR•E$X©~ϘÿôdÑh¶,\væ˜ØDSyŒ™ÄGõVlƒÀW †y‘’ÀWôP€—¯”HÚm˜™˜á3匑”z™¯w31™ÿ- D3•á3u™ù˜G9g@‡™ï ¥•]L ¢]š”Œ ìH½yÙgº™)&¥™÷’`n‘w˘›[Î’ž G%Î5yp‹Ñ/u™ÿ•Í™.wfÌ’ÙHÉr\àCÀ"~•’ÙÊwÉcš—Í™€\ã™qàCêu¦‘~™A éld–å™­thu oàC² Nf† ìZU‰G™*jY‹s•~™}!ŠR™HdWu”sNŽ&iÍ™à-^’”KÈ™¾™^×™’éKU ™NvÚ†áss‹_ š:–s‹TzÓ™Ã4 ,å„…Í™eJm šðPyz™: ­]%± NL2\—ÿûBK‹ƒ”u MÑ•ì™z™Úni=šÍ™À7&^ü–>š? g§”YN:+Lu½²@š–öqLHšÔ™x®aOš<—UšOvtPUš|6’½“3]&ZÛ’æQ2*w“t ƒŒNÉ|Œ g¸fšž ¯shÄ‘f â%—3û™ ˜!%àí‰Pšö}x˜Ä‘gš˜WšA†PèümûuH!%¦Ozš“gš‘j¯˜N“oš pUd”èH•™1˜dTx›€ŒšEŽš†šsšÜ ¨<Õr~ %D¤m“`9t<µs~ .ø–oškˆ‡—Ϙd ß“ð™’Pæ$.‡Æ’P‚XjG™jƒk`Ê•Wiö†™Â~ÀšÍ $I]™šoš.7ˆÜ—nŠGñ—ÉšfG«š›P Ð¥’ÅG¼"¡—<”lЇ øÌ‹€ÃKE5š‘à™o„טž çŒ(Þ˜N¯Xox…™TZ#d­’ ¹š°—µ”ÖjÍ–s”!“ùqé˜P Ž0ýšê˜VcŠx˜è𦠠f†S“G%ÍV„,’ Ô(Äšt € Xk‡ # ·xg…蚣C¡šŽ]rIw›=U™Ï˜-L‰2–û (^y*&™Æ[8o’™ñššÆgIHy›e(K&š‡ Ô=.“Öšz ‚Z9›õ˜Š`rÆ—áy‡šB™;ø…Œ œÎŒ+›õ˜h'G›Ç˜˜#0 ‹Ï˜*É'››l'R›±‘ž <|Œ ›ê˜beE‡š÷ca›™c›‘õxd’™Û û^˜›Ì ‰;µ™²™cÚeÇ”é”ÁP˜Z’†P›‘-q›â˜-m6›„›R?¯—é–  H^“d™˜R››3'p;ój“›JJ±PPÑ“ýGîe‡”WȔZJ— ›w›±Z*“Ì™3' mtk÷“2eR ·zs›ï´‘æ•?Î%’¢lr†3'V$¯›ž›ßê:j¢™Ç -p–™#'˜GzÀ›ô(Ëw3'qYº›aÏ›&5á@™Ž l’b!EÁ™ hÕ„™úšµO…¤›š›ÊÄoY™ ¼„f›w›»N—”ÔP x™…™¥Þ}²š˜iì)…¼› 6Þ›`–¨ r.ЗXgù›>é›~›k "ý0Q– “Ú̉°›ÍJà]ús šœ¥›$6î^š×k‘sg™¨ ŽA‰–»›k“Þ[5Õ›·›ê“™›œv>ªtˆÞó,€ Á sZeÓ☣i„—¨ Á—št Úä.&–çPæ‹á˜Ï/ @Aœ» /cs–‡;œÛ…œr›‡šKÇwb›sã!&œ*–± ð;4pS’KÔ*ŽFœÌ7oõ›)KŒ–f ãF76œSœRšÕšÏ˜²@—~ò›ûUMœp’üt&Eœ üyD›™$<‘n›sÄ7הؒbI`D~ œhooòš^zœÅš$ ‡¯Ž°› cR{ž›g é H aã›Ë˜j0š¯“œœ‰f œî sÓ’„9*K›œõ˜þ}ßoS›™Ff1šœh `œ–t "r‚–¶œ]€œŒ Ëk ðž ›´œVœœ#¿aŸœ¾œƒ.2œU ?§÷oØ™O ô]‚c®œš¿”£{f Rv­Kü›sÖl³6œéO‡Í•#š*Loä– ?± Œ•œÍmÌœFœÑœK¡rziˆœ~œjµba íOl›¶œ™tqޕœnÆ:¼Eþœf—!šJ–8Làž®œ„¼‰"š,›t%¸ˆÆ•þœÏ”ý˜av¦”µœ&¨*°–œvzaY™±×8ýœ& ÚˆdS#œ'œ¾œ(9r¡D š_0¡îŽÁ kKyôFy+4˜)®œG®”BœCw,kÊМ6›Oo;©œ± Af=œ¶œò"Çv ‡Ôi9Õ›\lù›^°`€“9HZ{ Vhbi3-‚_ל 6 I*L} | 2;nœÙœhYP9 Ixâ$š®œ2Ni˜Ù–† 󡙥—† Ͼœ.}cèœi8Ç“$•?òœœ;ýpIšî9–Ê—cI’y\yo%Š“ž yÿ™t €o=¬˜·DX.j›f +—蜛³+Ìo$›³ÜB, Œv)®•˜DI_u˜‡š·y¶—-˜·IH|˜ï‘DÑXšD±r”ΛZ°XcÓu-ÙŠ­oƒÒ·ŽµÉœÀ ›"èí–¶¼Cô‡» ÖsäÞp=,˜A™)ýXù{2H–ž^K¤.“9» ctûW‘³ˆ'žÐ·ZViq¦œ1à.:„ žÈ³‹ž›†t‡ÿÓÿ ߊ@—Ê~1Æ•¿¥{ž øå·‚€%)'“H Þ\ ‹žBu€ ’!!C2ž^UQ•6ž¥›è‡žžÅšz€Ñk}.Q@$ÝéÔ+œ˜Jœ³88t/¬LQ°N©K5™‘¼CÒ‘¢™å^-ƒ¼’à€—ažÍ‘=žÈ8» 0^ven:®‡½š÷iÙš¢ªË˜ƒr(žt Y]ƒâŒ œ_˜|S-à9VX[žæ€ž­ ˜› _þÝ–ž X3k/Üš$u žý˜Ð"d•‡ø™¾„‘¡›¾Tr¤}± ÜÁŽ!žf >7jv·›ÐVÌ‚‰*HˆOOž:žŒ¼.$@HÆ:€’M™Ô“ôoë " ÑŒ¡g¡žš%Og“˜‚éÙšÐ7žMM r£žEž¡ž²Q’Õ“ê º•‡Y–ê 诖d™y™ž¦¿ž¤žÆ“Ø™ò" •žº ê ë~r~¶šV õ%‚žçž('¬æžk )Éž¤“'4©\²)Ж¾xž d¤.'Gëô ÕžG™<   ™W‹žª…«›Œ K lõžÎrMDK "•y‰ .Ÿòêžïž¥]¯–昼1é—_žb†»éJŸBZN ⑇r Y™B‡øž½šËGºpÅš–«/ž‹ž‡œ$Ÿ ’‰ßåf E53Î W$Ä™žÎ †I(OŒ $ªZ½œ± DPˆ3“#Š.2ù‘:Ÿ4O~O4dh2§T IdhÍV@Ÿ]™>Èq·›z 'ÙšÔŸ¹™X|{ Åš q™à“t ]Fh‹*™Èh^[Àž‹*Ž-ö‘Á“P@ŸBœŽ€™ ší^ DŠ›™QŽ×€Tš¸ ùF™öš(…'aŸ8(”sœ^gŸ:ŸGs©™+œ•>.îrw͇͎ґŸ.“ŸyŸã/Bs}Zm“”U‡š|=ùP£”ž |=Ê ìœ œï&òŠð§Ÿü Ÿtz«ŸØŒŸî›t‘|`E bªEšŸ¦Ÿ:ŸËXCc b\@=z«Ÿ\w:{ž†|V-vŠP ž HYzΟE}>œƒ@‰;xqä ¥‡_¤˜Å𒑉\kÅ$˜˜›’‚_cŸÿuÅâL”%íŸÒ3¥Ÿ9 ô ¤ € „”¼ŸÙŸþ œL3IÅ7lÂ÷’c#ÀŸ3ÿŸL矠-A7˜¨ ª%yc-™ù”¼wœÅ1 Ç“3’áàŸ À(.…ƒ+ Mæ’9 kW÷ŸP  óŸí–ã•+G’HÕ H¥OKlh’¹Oš¨›áŸr¡€Ô”kñQÉ—Áç’3 w ¤ @   ,j2 ÝŸø±Ÿ¼’E $.9 ÒŸû55`&™$—^ Õš¼›0—ížqkD[j  ü Å{Z È”‰H y  Šb= Ö‰* ¡—V i)i b /X¡•èŸÎŸ(‹éo’Ê×-S ü 2™à.d”¶,=‘”ÒŸr•’s‹ À“|ø3z –™©š‰ š‡ w‹‹ 0›¡Ÿj›‚¹™[ U,š÷“)ü(† € ¢mv ”˜s•g²Ÿä üi ~[“rk4¾Zd—³ ùV9Ÿœ M#n”EÔg¦ ® Tf±V ’9P{y® +‘R• žTN[3•Q {Œ ˜ 2m¤—¶š À0™¯”Ÿw"?Œ¶ ¨†0šK’/k*p6”dh½'| RN¸\6_œ öà’\Q)‰Ë $z±&½™ (zš »˜Åh­‘ù˜˜‹ ˆ­’Ū{Ož‰’¡”wW bP™}˜| k&h ›î ÑTç–0Bü çP¼œ§”0@”…œ|’¡÷è ä”XQ, všé–\Q½gŸ‚Zi›S’ó~pÌŸA ;'e›¡!CN&¡û ï 3Sš –kQaŸ+¡]Íž¥’¬bíŸáŸ¸nãš#¡tš€™àš£s!W‡”ZŸ˜/ ƒãœrš+¡óC¡¡÷ïß›ø”=¡S“O”Â:œÅ$ÄŸ¡S+,Þ ÿ ãS|‚š©H±Œf `T¼™ê›H¡ ¡Ü”“QÀ\›“QžÙŸ‡ ´™ü›ó~õ’<¡+I/¡2D ä ùHYœRœ¨bx 2Ÿó~A!”$ % û5ë™NœŽ¡& )š’ÈWŠzÏš6¡ކ¡% åd_¡Ï 5ݘŸ¡€Íž¶ =+ ¦¡Â"G¡ÒŸ™4Ðh²š\63˜: œU™d¡4Ä*¡ ¡94šæ•B ësHŸü [&œg|Æ ƒt`¡Ó¡&‰›Ÿ‚ßW¡TšgQ¾$¡ “gQÀœ¦¡Ò –Ÿy¡ ™ŒÛr²ŸÚ¡P sƒ ìô0œ( pGŸ»¡ÄZ5~ìÒ¡5Å­œÎ¡ÍD ÝÆí—Ó ŠÁ¡6 ü—à¡9¡BFîx­ Ú¡B'èë‘Û’YóŸ¢:m裠  ò¡  ®\ê¡P e½:“è¡C~ŸÑ¡·™‹@ŸD¡v v›áŸv v|”¡ë=' ¢0¢ "°gÌ’Ôž2›[ j]õ Þ˜ù 9b>“ØyTPž¬bÖ‰¢–”gîN ͡ڡôÁû ^N˜”œk“Ä‹T0$¢Î¡¼~D¢k™Ž t­¡Ú¡Yõ¡~œ¸¿R ,¢) AŸ¸¡óŸî›¸Ô¢ 7’u¢ƒ”® Æ 3¢|¨ŽX¢ÍŸï ë‰.“¶ z ¤ u¡¢© k ›d‚¢ÂƒH¢r¢ªT0¢'¡Žbœ£s˜—\¢ È@æ¡?£sŒj¢Ñ“â% P¢b“tšR/¢ Ó h:¢*»!Ÿ Y¢ >˜Ê¡'Wjë¡ ¹q¥¢ ¡?¡hœØF?¹Ÿ¢¢» T”»”¤)š•ä ‰³¡g¡-mô›}¡Ê ¢H(¢R æ4j¢Ø’zh% ®Ÿ›R ˆ2º¢¤“ö}i3^ R aL~» â¢Ú°¢‰¢à ¢Šžqžr¢Î½~3¢¦.µŸW‘ ÀЄ¢ÿ Þ1Q”ÀÄ+6¢ª¢¢/m¢ð¢‹(£¡…|lž¢ ÞŸmQ¢ £ÓN’D— G )ó‘ü õ £ü ‚E g¢ÃÝKN£‹š à £ ž ÷^žØœA ¶x‰W€h¡£.­Œ¡Ê/ #Ù:$£ôŸëmé‡ £±8ÕX<£, "]²H=›ù%74C£å¢ð0¾x·¢o*’ô˜ Jj,£ÁŸ‚ "?m3¢±1G…g¢"‡BpO£}ç.J£+ Š×“‘|h+iw £ó r<£Ë eý¢h‘i£ŒwKp’TÈ #£d£,’•±žI¢áHÙq¹nÒŸ²IMPÝœ¦zçqŒnLkÍ@££ò ›åÓ,-+4£[ ÿ‚Z{£M+ w/õ+c£š£;’%T<!oi£‡(“C£w/XBž££ £KH&V\Ó,è2þ’  )Kr°£ ¤-òp£| fœÙ<Æ— ÙHw££ ²@›ímW£}&»£`¡¼‡$ˆÊ£þ‘&Ò[S£¶0ÃŒ¢¢KS™££}£~“RXs÷­Ò£ü ‘ º£d£$•Ž©|£֣ÀFÚ‡òt´£}qÉ£æ£ YÌRK£ “壟£L£ÈhM•ã™>ñ£Ÿ£Á ø‹z}Ù£Qï€@ú£Ú£|  Ú’ö£÷¢Ý/YLù[“QW$z›¥—ÒTB¤[“Q –¤Î›‡ ’$ê’U !¤÷ ¤%£¡j“Q3ÚΣA ML¤Rt‚ô/z¤ês<¹ |¥]ð&ïä )T›n;¤êáŒù$š£}£^sÓlîÁ%ð%¦ §£¯ëšÛ4¤âp –CX/¤áŸž%=bû P(4X Ž Ü9™UÈ?¤ŠZPF–ÍT€!'¤°– O¤ö Úm¤ç£W¤ÙŸ&w¤|£Ÿ”k l¾&t0c¤6%É [¤ D u¤¤Ï!„!—n‘4¤x†Ì'Ü1—Q„Fq¤æ£Y–èT¶¡¤¿¡ôÝ —¤¤/ ž¤A{F¤ ¤#¡´ ¡@\X*x&J*¶ p(Ú­Ur± , ¤¬£¤O'û\<£¢ÔE¤·£G›]<£®¤ì(ÿ+X*'7"Á¤™˜!(6&¤C’ÒŸf«"ɤƒž!(ð«"\š!( ö’n¤¾'Í]„£3 Ú—Ò¤ ¤&h‡¤ß¤pȤd£®¤l È!㤤õSyç¤?¤-"®| oPŠñV'’, ßf½yÿHPЦ{¨’»¤·yÖ¤ “(&O5/ç¤4¤K{ÒYCƒ ‡€º¤M¢ø« ó¤4¤â1¥d£»¤}Yê@®¤('rO¥Ó I(çr¨bÖO«£o”µ6.*®iϤԈö¤ qߤëCZ¥† º¤v¢f4U¸’ö ÷¥¥˜¡ÿ 8¥ë¤Ê¤¦”f¤² ™ ÑJ•៖+÷l€S™¡x@N ¥|ÖU\‰”^:ë šä |,Syפ˜ K>1¡M¥æ¡¥U¥d¥'•¡e„£$[% ½›üžØOU¥‹V)–V£r¥ HS¥?¥J“tDš·­‹hZ3m0¥‚©{ }¤š£$’! å- œ¡Ë{õ¢7¥z¥ï ]tT¥­‹h2[¥‚¥yOË¢¥¤  ¢ä“ ª “f¥ªQ´ €¥€’ ]’<š†š‚¥a3¢”ç¡ü ü0ù;’¥Qƒ?]…¥¦¤Ï @Ï8~]ÒŸ]VCP¥pv) !¡¥PŒ(ð” ©"s¨šS‘áÁ¥§mat´¥£% }”oO¡\q5Ÿw”/-±# ¥d£D¡»[b¥Å¥Ö-G±¥ê !Ä¥®¥;Ó ä¥­¥ä 8ý¡¢¦Z$[ˆû’ejO¶'¡E-;)ɦ֦['V“Ztƒ¦6\"(¦IaB¤r¢ê) !jV›¦¦T+ö“ƒ¦?^ —¸“Ö¦o2 ä¦ÒŸò~bFÝž F­¡—¦±?n¦¢ŸÛr0tˆ§ü¥;¤Ö¦ŠܦB”ÐS.2ó¦a¦ÑwQz覯¦2ˆ§¡¦‡¦¤ Ħû£÷£ž*c¦ç£¯Xabà¥3 Lq;󦢢t˜š‹¤ ÷>ʘ០v@¢,§ä µqU(x¼¡ßw8§ DŠ~š|²Š±¥=1`o÷¢^8ئ¡U‘9b`£ô9¡[Á¥u;§€¢O§·y¤G§: ]Tñ ÿ¦3 ×F§¶H§¼F£[§9§yOW§Ü¥Ö¢%Ë8k§ÒŸÏ|qj1£ WT§l’x§š~¦O§ „¤£¡´€GK \œ5yC£× ä;¥wc§=§YKí£Y\,$‰§ò£} V&¹Z>§D§Z Ý£¦3“§£mwЧ/ 9"Žï¦¡È¡ ›c§‚õµˆcj> åd*xì§ò>µ~ ôˆÝ¤;¦í–Ͱ 7vöÏXðXLš±HI*Y [ŒNý§ [&ùD‹¤[&öž•Y - L‰Ë§+ *Ö§$§Á¨¡4§ G I`æd ’¨ÑVü–cm"/æ—¨ê äQN§ ÿz-.x¨üŸOg’¥ZA8 ¦‰’YY𦒝û¤F¡¥¯Îá¨DÄ7¨Ø’¯$v4¨hWEø×,¶E] @¨ zy8{½yï¤-¨9¹S¨ƒ`Ì’¬§§s”$  ¦[“ö|ϧ-¨°_C«§¨Cš`¨3“¹ Z¥‹¤Á fA^>ÜšZR?Åy-¨ Cq¨R= É Ž§z þ%‚Jƒ¨j™öxz ®­yD˜¨ÿ<ƒ¨« 95# J“« ¡É ’£ R1p(§ ¨ÝE™¨ŠE˜¥‡¨Y õ,ES’|-§%‚ª¨ÞUR¨×§è“3[Í+ù§Y–3[!¿§Ÿ££¨t‰O[˜€^M ¼¨‚§H¨BMö o1 RÔ žº3Q bCm¤˜ê¾t¨ –{õĨ5™{‚¡•a£¨{g˜ö0Q Š/³¨š£d—ù] ¦ë‘ña8"6P ña¾#v¤l $I¯(g§Q 2]¨’¦ ˜—AŒ(¨ƒØXtk!¨Ém‡¦i¨ù?T¨¨ €-p¨ë§û ©^ ©û8tpÊ—2MÛ œ¤Ù“©È¨›§H€--œ|’ã8”v¤¹¨âR>w¥—»BK¨À§¨±›ÄŸ>“åššv§¨pd…šÕ“ñVP¢x–9©n.ãu )cá(©Î›í©,ó™¸m¨h˜œ¼›´ƒI*9¨*@xg¬–I©|G©Âx%¨?,:¤ƒX7©ÏqŒ—;¨ø”Åù¢‘–÷“R«b©þ‘Á&•´¦´¨P -þj©¨i:ø›È”Ái=C¦o©¨ÁSc4¨w7ŒD yZßo—¨ bG^€©Â‹R@¨¼¡•¦žo&Ï8{©¦¦ù¨¿¢,E‡©?z;å¨÷2¥¦Å¨e¨÷½“©£é¨«'•¦!¥é–Z ÅZ¢§Å¦}©x9™–”^ Ü(Ë¥|©í¨Æ›y˜ŠŒ¥,£©o”^ ©,³˜8©`TïC©ÛŠÂ˜&x1R!(JC_©kÚC¥š£8¨õHÔ—1˜˜%#´©†¥v¢Ô©Ö¥¨—Í ÄZ[⤭©©ö s¹¥©¦Únj¶©¦y¥Ä“ŽAN©œ¤Ø© ù v¤» ñŸž©XšÁ:‘O-™Á:ƒš¦!©‹V9<¦„©ì¥×¥Ì“Ê]í˜Y |4y's©ªØW¼©p’•tœÞS5aT,©×©ƒ”äZÕ• S5ÿª£›©Ê[Z¨á©ÿ=À¦µ©å©ü¥Ö©¬£ ˆ ˜5„¦‘z 5e‹ ª´1Œ¦ì©t©E-¥ªí©—P®?¨©p©Æ"d¨µ¨ItfŒ™[ôiõ©šÜ+3ª7\‡©Þ>$ªD¥ƒžh f— ª€’wùDÿlt©ù¥g•h )~ ªïFfBª„©Xg_%ªå©±8³I©¸â'©¨P  ¨f¹swª ‡Z½“Ýë l¨ø¨Ÿª*Û)©õQ’™ô™Ý«QHd ÝÆ5x¥G¤Ÿ”'[M Шةj¨Œè¨$ < þø©ŽªËo`©ù˜>R|CFª ©œ¨yªB}Y ±YÀvW˜«Âª;’¯ªuý‘¨ÔY©‚ªp©Ò¨“œkªí¨{ÿA4¨—A¼ªXª¶šŽûg““¨ìY•gh—ŽÄyÞ˜Jl"l¦ª¨x2„¥;¦$’«[ ‘a„ ç2 žª²ªqŒŒ°…ARW)0©—«$.4—¨Åþ©Öª©-uªæ§þ§gZƒlX7P `}ª©êK'Moª%¨^ NŸ¨šêD£)ªz ž-“œ™eöª2R;©k™òWç ȪӪdVµ ©Ü©Bl:¦ùª©Ä]”kK’êE+˜˜Yªe{¶”Jªª‰©:ªÓªVÙyè™÷Sgrƒª¯ª[¦Ü¨¯+«” Wm¨úªÅC@¨S[Àªà©«]Ц&«¾ª•ávª¯-]*ª3’¢Zöh¹¨üߟ˜VP S®6««ß–µcEŠ­«Ï©H¨èroßr\.“v«Ñ ýp®ª’{@zšªªŠŠÑ¢\—Šq\m¤,’ô*¡d« 5{ c©*«“Rl1u ¨€ `z=›2 V©©¥«ªÖ…)«’–E·–t–>Fœƒ«ý?^B” Kéª=««O r|Aš¹‹ã]½il J Ô€«ªèQh:«>HD«¹ª B¨¾ª|ð*ꪆ̪%ÛQ «} ¢é|ɪ8˜«”©.«&3ýwR•›;£)ªþª²«ÄªÉª[Ä«“ªˆ) ½«ÓªV«`ǫ䨣ª'ï8 P H*ýwé¨à&Ô(Œœþª%;g«Æ‘cªó‘½Ë';ˆ-£ @²d—¨B®«/—èøšN¤È«z *Ó¥£vü«Åë©\«e¨WÂ’[©ýíϪœСE«Ý_¦4¨ðRàN¬½Wðmé¨\ ¬=«%¨Ã¥ ¦!©Ž‘¬œàšª"Ž«™’Â-bªí¨µYœ•›ê ¬]_1ª‡«ù ?%ú«Á«p©l;|,¬– × ¬©¬ “¦O2¬¯”Ê-Ñ/¬««)¬/ñj–•Y ÿ„ bô«Ã¡~;¬xK¬¢wM¬8–«û«M¬‡Kô«;]¬©|·µ¤ù© p^b“ž"?¨že›’d[Z¤«M¬}}S«v¦k“eÅ@\™›ªßc¬Òª©Ç ¾ŒJ¦eœP>¬Ÿ©µ¨•V˜L¬Û÷~U¬§aÕ¦)¬Ò} ½ª©bfw’’¬m L¥’¬Êw$¬8¨à g©Á54] ¬û7觬t@2«ü« Œ[©°—H¢7gL©Þ‹›]P— °NŸ¬H¨ J𩪠ª‚\¦“ª ‘ç“8¬‡œW«î›?«iK£¡]SlÓ©Í𠣬ѓÛßžªÅ«nV_Æ—v | z¬›¨Hi°¡£ªLi€˜‘¬©ã|ö«4©Y ¦ƒ£¯§L©¾Jáª@¨×¬¥.gð¬’AÌ~‚š[‰P¬e¨Ýdóy[’{ I5«r¬ë="o|Ö¬|ߦQ”ŠnΩ†¬p©‰P”ƒ«™d¬I¢€ uWoªz »/v¢ªYªÉ,¶vð¬Ö˫ϩ ­A '­­ ß]DE«Ÿ|¤t ­7þ )©¯nl¥¯§H¨ºŒÜ.;¦ö£ì¬n՞ة[ë$0­)(¶]O£÷«+[«,­í¨µõ/úrð¬ É–Ô”L ”—m¤K£%¨R6Ëšï¬w¬@Cš —¨c}º˜šL K@C©¨“ñ]<§­½T6¥v¬›4/O p…¨ôê¬O›©s}$VÅŸ?MŖ𦠭ÖU¬A«è¬ , ©?¬ © ^×d?­iR´;>¥(­x©u]œ­ §iRÌ è«Ñ‚Z­j/­ƒ­š­8‰uð¬RwV «P…¬•­!ì‹­À§Z­-åªú¥è¬ü¥À«‚§ð¬É ßšt­¨¦}\­n«ª}˜¥Ý¬ q­Kç¬Z­¡Jëh¸­y­— Χo­u­`=‘§ˆÐAÖjÅ­œ¢Ï­Ã0©Ü©o6élo^­»(ª«­Ó­ ¾Þ#Y R$†fÚ­ß­<4Í­_©Œe¶§„ =(«ŽÌªãVdŸ§é-(§l å$R‚ç¬z å$TT«%¨-sXn«b%µˆ©û­ZgÝ­¶¤º«ç}Q_T«®ë)6¬´­w¬5 ®ð­c§-©¨Í%=¤±œû­- úGþ­®µ”¬Ê­¨…£Ÿ©þ}ïC4ÕJ‚¬¬$®)®ˆ¥˜’ú§^&R,kŸã­ R} K¤]m¢4®Ã %«½¨2«Ž“K€†•Å@z¡šG® 'B•®'6n­û­› #®B®©#u±wÖã­Ëœ†T¤Z®Šu¬¨ O 3a­x©O $vß;Vë8ê§Þ­¨lV{­o“¼??=ÿ­bœ~«R®6a`®-ª¨Š R‚tb®h“vã­°V_x®…®Ѥ0®©|¹l«ÿ­y“G¬¡¤4 ÐIw­Ì¬š¨³–V®KDCgÍ ÄR·—ù„)£®*‰/®U®®£C‡®ñªn®Ê}‹®m®%®:%}[[L4˜U®h–c†|È¢û­§‰ˆ!¨ZVSV¨ ÿžl®!ª™˜ ¨*ª®¬£¹¨S0Õ;ݬҥÊEž¿®$.2}Ù¨ÜÊžû­K!¼‘ ­ÅªJÁ®÷­b:®ýªÝ®ï«Ù®Ñ S‡ù§ä®Þ$ꮎªø­ß©’ªc© (Û®(®Ü©š/ "H¥£ªî<Ó)ˆ®Ä‹À;I®€“‰Oñ®²ª;Àˆx¨ ® Bóå¨Xñ¥~¬®¿†²®Î®•˜‰e± ÿ®–Ì|¼’ÜA@·¡û®í®®ã­:^@¨OG #¯ÿ­×N͞®Lró4®Vw¯u¨«U¸™:­S“Nc.=|¨Ê(º˜ ŽRZ¯’ íI£­’.õ¦L©®U­fz?¨Œ®’ À¢—¨Î†Q¡ú—­³®©â ÷oœ“ª#|¯…E«GD ªe®Ó­“"ÎY+—yOQ†¨Œ®%¨`M9­§"¯süPQ’ €§7¯ù¡¸˜(÷«j}Žf¯ Õª˜ 6—Ш±©/Õ•Q®Y 1-w—D}¯”Cp¯7¬ + § £r¯Ü :¯@—£&4‰¯²¬=‡ú­Ž¯Ëh¯î®F A¯Ó©13Œ•¬r¯œQ*¯S¯¥¯fØ¨Žªç … ªH•ä(]¯É®)©æf{­I­‘0èWÜ—¨&)¡ ¯@©¶0ú h¬9D¤¯o¢jR¯«®¨ø78¡–¯aH¯)£Ž¯îí¥Ú«Yª‘¥Ì«Ø¯?Ò ƒª‘}„]­j¯7qj¡F®iFi¯¨Ø¤ $=s¢©ž¨3Æ`ª–¯fE1 ¯s¢Þ¯¾¯D©j‡_ׯí¨°uwžŽ¯_Wz4¨F‹isžý¯ ‹‹å¨Fùr~+s¢&@.Cs¢üPÀ¬¡’쌊‰·6¸p‰²¯•¯_¯þ l¯Ìª ~:eB¯QW­W(¬Ü©&¼¯G’è^„)_„%°´;°Ê®ίH¬©¯[ °ò¯7r‚­a®)°I-ì ¯¯’ X!E®Y Ì"kGZ‹©ô š ¡}ãi ¦é¨Ð"˜¯E¯Q°x §;¦å¨“žu‰ù§.ªx—B­›¨®9(‘mä”qk<õ®|®¨ ;ð¯}v–°Z¬kkº:À z d)%–ö¢ BEËŸŒ°k%ÿ¯ã¯í¨œï[¢!Ÿ5^>8™í¨¤ ð:D° ”W1¯ ¨îE}^˜ƒ]±Ù"Ò©c°£ª $â—´“B›{Z›e¨b>2­€®o±q`G°¤©›ª²Šf°­¬]±tœŸÙ°J‹¡—ë n „±µ¨´9Ä^ã’H`«r°Ì”×5cH¢×ªƒ ®“o¬— ¢‡”4’^H±ð«©ž#j±j«í©ž#Øžh±› ¡±D¬Bˆ(•¬Y±åš¯­*«_`A}—¨²(W¢Ê•f`ÌAžÓ­6#ô¯û•d'Š+•AŸÑ±d&™à&M†¤{°µpà*P± ©qa“I±±6l£à&á «¨]±0–†­M­©†`ÒH;¦Å«‹Nx€Ÿ°±±Áf›°r«h᱊˜Äc»ž­¨z×…2°Y±p+f…š™Aƒ‘ªí±%¨c`­Š°î±MX… @¨úEIˆ©±•õ±Ü©T¨¨² ²Â Ý¢Ð¥ê& '•r¨/9Ú"§­¼±%-ø§Á™u• K˜®!©€=9­ý¡ ²ÑpHƱø˯ ¯Ê`W¥Ü™ÿt#»±ò±_=ª(±$²Hí¯X°:,[ÿ©<¯çû‡íªù¨05²Û±QÜȰe±”89ƱÊ`ó8¨ÿ*õ°²š^.‰†¡¥ÏRE¤“XÃ0»±3’XXª÷“ÓRŒh²$ ì`sÜšì`ø ø”— ©7¨Ñ“@z²›<á'²¥’<•†¡&p 2 Ë —M¶¯m²Þw¨zª……‚_°tQ†M•$’› § l²|²VÊVЙ ?¾˜Š¬Ì]^²œ²Î^µ«ÿ«"T3S°þ¤ YŸl²|’˜Ù"X²³¯tšwm¢²°­å ‰3±‡­Ð ñ’·VvªA9ôgÍ ˜9Gª>ªÁDzL±–”"A•列²ð0÷\¢±³ ѯԦ{°×²ºJβ°—?aúH²«²,C²@°Š²î'B²÷’³ ¶­Ú²é.*^› “³ = ì²¾²ö m3ó\ï“ J  ݲŸ\›êV2°»²sŽz’¥\›ƒ±¡—'®H£¡'^„—¢LaÔ(¯¥—LaZ€>œ*äBô² ²Š²±ñžÐ¥*ó q¢Ø’³ò¦J¦¤ °Æ²¡‰pHe²Ù"-'²,’@c,á²û+¡ ³¯ ŸR£­ z9(³Î›baý0L¯k“ga³(¢Ã ÓóŸ.³Fx”Ab²¾$𢫲Ç=[¡S“D/S&³»²So ²ö¥#B¥,m¤d—ya€ŠX¡O³Í@Ó±R•m.Š\³ù˜ƒa †²Þ+bˆ¢Ë²ÂJw0³nǯk­"A‚ €“TW$Úb²rCx³¬›G; ™J²ñ²þ" £¬ ³|Yû±Ö²IϯbúC†²µTv°Ž³³ƒÒ²A³^,d²O£7bm ¯«­ ühG¨s”Ý÷լȔ”,[²|²VsM²”³®6'°C²+ B6ñ/³?Œ1³½ 4ž³å lw0»˜mÜ—³¢ùrŒÂ³Ë²±}æ¤Ú²!º ¦Œ­CÓª‘Ÿ³“æ®q¯Ë ; (”³n*$ «÷c’?²Ÿ”''XBɲ£Y­Ö³ÉE›°m²8¢³„³À7 i°üw|³å²v³fŠè³«³NFÔ³±¼³ ;ø±’¼¢*ž˜³u ˜q²kH³@°ü³£‡ì³û coÚ±±Ö³ãˆ]¬¶²´‹Œ”¡´œ?ŒÎ³÷NË[Ü—Û ¸²w²þ‘I\ ¯u³¿¢Á@´û³Ë 7–*²›³u–Y¬Ê²Ð Z ¡å³"Ú–‘‰’³"²Æ³²³Ë—ꤚ£Î³¶z“Û¯ê,^ 4™Õ“T;r*'²˜²›PÙŽ¦B´Éât.´(kÛ³ô™7u™•P´¬­+´V ô±ê¯Ë²w ´Ã³yZO´>­[´7´Öaá˜ë‘ dQ¡ë¬øRÿ”ÚÄh¶»±î›mŒ•e}B´ŒV´[’kžf©|²5 >*³“§¥d£B´ ©æ/œ>uá­¨`´î”M•6´Â vWÙ¢´'3´ƒ”ÃH§ ž³+´=­<£“´WŒN²y­ tQf´ã°+´òcCÞ£\m»±$•`<]ëŠ7´äd¬´Ÿ§ÃØX³9 Ãv ‘•Y³Æs®Ð°P ÃT•°´» Ââ;=›X;é-«² 4¤B´Y9­ ®7´“ °ö­Æ ¸£-_©´×´x¢Î´>´Ã!®–ol/­¢´! mH¦´  ²”® ´ ³´âª…^A}´°­+´Â(ì±QW+Éd‹Øô.Í®J–DB#0Ç´ … SŒ™B'C î°>´špû´¼´¨¦¬4±Ä´4ªµH°¦´ñp¡ši\ÕJ–™µ¯¨µµà«µó ©+&µ ’©– n­´7´þŽà–AŸùGA`B±•˜Ðwm*µÈ´m|¥ð³m\T@d”æ‚·'ܰ HBpKn¡³œ¨®ˆ´-©"œ¨ã–Í¡å ãqx¡¡´MµR .E‚–Rµˆ,ï>õBWµEº®g°[µ€J }ˆT'ÜžªÎ³cZ]·—‰@n*±nµ0{Ĭx²¼ZÈ\œ‰†R//ƲKª“®°Ë²Ý{²A³:R÷lUµû£Rµ µ•›-åZpµˆSÛ¢Ÿ%V2ª*‘µ†Ÿý²HR²h‡µ-ª‰µR ` ¤[Kµß¯=³m €+މµõ9aµ±´BFx¡’£-|­¯öCÀ¢ µy³Ã *Îv¢C“ŸµÁ ¼µË‰þ®IµP®Ã´|µffæb²òqíXî,Kg³Ãµ^?¶µ€³]^>”­°–ýTª ×µ+µ­¸œ_´²³{  ºµš´›šµ²Ë ÷7Þª· L {k”ൕ.µ²µ «•=µµ­ … fµ¤µ#q~µå´ÐAÉ,—µ² TvŒ#µ|µTÑ_\±â¢[Jl ƒ¨ }òµ5™>m:#ÛµM \œûµôµ’Šw®ÆµRµÞ;¡<¬‚³¨¯¼ —άZ´$§Nµ%-þ2®èµ²f±\šó \&(¶|®*¶¶“?¦¶«æµð´àµV—+­5´h´×bæ\¶àµÝɵڲŸ@L+×b.¬´ä³A¶BmÉø¯Ð j( gY×b*‰ÅB[“ÎÂL­ï‘ÎÞ™v´¼›`¶eUc¢NµžÓµw R xM&l—Î!NN¶Š¡Çc*ª¦J“U|쪛µ€µ”^¶’–d;’h¨™˜¦.Žð² ´}²@´d±Þ² GdͳVµr “q¶fµ³²lµÔ”r "’“ û’x\©74¶ôµžû–ߵĴj.?¶}¶7‰W ¶h;P–ø$Àć¶ÊícªLµˆµË /zEh²Ï²J¬Sy%ª¶îi¶Ö²åŠ7´³³Adí–EÍ@|¶[¶Žw"L´¶¶¶ÄK}±³P;}9 ^ª¶²£µ¶‰¶}„.Qø¢G ¶°¶5¶Ú¶¦µË²+M‘¿±"I5:6ô¨ÿ„,ʶֶK™m²—.´< ®æ¶ƒ¬¶'µï˜y¶²³i\Ͷç®ô¶~$[{]®ÀG Ä\s§Æ²ÉahL°æ¶ƒ è² ¡^='²Ã¶-JKu訶¶BF0¶†¦·l-·h–sµ¸¨¯b˜ ¦;’;d '[©;d15ì¶ý²`Bä¶ô³1+%)´ ®ß T¾9 ³]|Šx-¶æ¶[ V‘ó¶1+Oã³o”îf$·¶)(–³Ú´ ”O‰I·…¶wܶӯö ¥^wµñ±ÿ³Ú‹ò¤÷µå |..z¶š¼SG¶Q··t6a·3·Ú ý$*$b²sdn12²²¶›«#³6f̵´´Àwe··ß7 ´Ð  ?è¢â¶¸~±ܳ pKª.³Žç>´v®‹²•Jµl£ d<™æ¶d2dð¶ <¶H%" ”²¶ê«þ޳ÄX ¦d³viV¶T°Ë rº‹(·Î™”­¥©·þ‘!²sjÞ;ð³ÍJ ¶Rœþ~b²Õ3Ë´™l›²A³®'^u_²7 <³É'·²å "­£¯Ý¶Ë²~ú?”²ˆ3T ³è¥ ƒµ›a“ mµmÌ‚ú¶Ä´£¹Ž-E’a3Â$½·Ýu›³a!²·Ñ·Ð _­‘—9ê «zr±ö K(³·íDã·Æ²î? µˆQûIÿ·©· ,Hƒ¤È┬·â‹±é²|9ØšM·ò·æ¦ ·Õ³"–#]Õ·ã”2¶¯¢´¸j5’3x‚·y³ï Iå¶é‚$,jT*¸2c¸”¸¤Oe¾–´±Ò·PÃÊ·:p· ´©·ç 3?Zµ>¸E>J%·Œ&>Šà®7¸Ñ?‡³£³÷I±é‚ H½¯¸ï=2¸ ÇŽ Z²©·“w<¸£µôµÙúWÞ±ò·½9%¸3“1Û¨«Î·-3¨÷µZ¸ö¨¸õ²¸Eœ­<¶ä· 2۳3þ·=¸¸îW•³·¡ ®>ý°’JF\¸µò·y`Ò¯óµ²³%r‰¸”[ @Q8½·®w¸E´¸¿*„¶‡¸)À¥Ú²ã!W¶²³ã!l™­ {r=D–¦Ë úwh¸¨¸„B#±b·¬¸PÛ·ôµ–$§·S’í‚9(˜®c¶ü­¸Øµð/SH¶€Ѷ÷ ¤¸§Œê¥´ogŒð³Qq†²±6]¸"îC9¸œ¸]ß1›¶ ¸d¸|¯„m; >réÜG›¸*´Ñ¸²Ú¸Xšùeiý¬ñ²UÁ;†§f¼"$·ò F€˜и½µµR¸3µà¤X¸$¶ ´Zvµ¸›³C'$·È´õ'?ÿ·å B~ÿ¸•¸] ˜2=Ÿ†5X¯¹Þ´÷#¼· ©Ð q?2õ¸°5¸¸Ù2¹ê ~¸/·IC¡ ¹“(Z]¶¹ e*tµ3•ߺ¹Æµ¨¸üyƸA´P¶Jx…ʸ%-?2ÞGÍH)(ä²8¹±¶Ñ¸Ç`3¹µ,¹QIޏOoÛF‡H¡©m©U²Þ²¡nPžªi²;gy· ˜Å›î4Ù¬à³à#YL޳+i)¿k¹âð±O¸÷ ø¸Î³õ)¯:׸ĴJ"¸À¸TÊŠfµü ¦1´q¹±§7¹†¸ö ¦«·Z¹ý²xR¹é–Ò\~¹Ë ruF“ΰ6ø·­ 0Q²z¹Æ²s+«¯0±¡’ˆùâ¸~¹–¹£K¹Ž¸šb ·¥Z<ô¸$¹ ku«¸"˜ é7ø¸±´g"b޳k¥B¹Œ±š´nzS°i¹å,³¹¸~hÅ@‘¸¶‹¹Ø¤}¹é²&žª]³5=•™¹Ë²5=‚y¥¹íHb§´-^ L±q²Ö-į͹Рâ A*Ò´¶;r™Fþ¶ü å;¹X±­¹t›•§ã¸ü yç¹µ‹¹c±¹ñ¹ \'¹è¹í¹U“³@¶)¶í¹t™k‰í¹Ó@³Ã³ýç °¹Eµl=UKp¹’¹± ,ZVSÔBû¹bµ‹¹w“ ¦¶/2w1¶š}n§C¹œµ‹¹#v˜kʹ¶¿·Î¶=³ A/±ã’T8Þª¶67q¬ö®0¹é­R¹?¸8[*[ü é.¹ ·Ê— ’YÔ¹0ºÎ¹^#󹲋¹c#¯(¤ ªº˜ Á@X­P¶1[b²ØlkŸ¸ö ±?nɹh³±?,º!·3üYa¸Ä´j›¤/¹Î¹s£T乄;,^º<º¾€iº™«­¹Ñ&,[¹‚î4­.³{2:#†¹² ‡2pº@º²³ö^RºDºrº„8|º²³+3ŸmjºA¦zKX7°²Ri¹ÉVÊ4¬¹7ÇAø)¸/ø ¯ý²ü3”º$±"™BŸº•¹å ‚ì.™º–õ£{¸–ºÀ—ذg°Ë ð°å§ºôµ† I²U”xBTwyÄ´ 03xºÿ³‰‚®§b²Õ«Ø³ º¾e+®º˜²×¨fºÐ 7 "¯³Ë Ñ&í®i¹;xÁºø¹Üm㦲k“N ’\ªI¢œSç Ö ÁS­ âº+ C/ô]¸Vµàb%êºD¹ô%È`‚é–ª“E[š#—¦íU_ô%_Á™²%"¶¯R•²Н·p Å5Wк»*÷¨€ºU Å’t_ zô%6Œ9{~œ2>6؉™’ÛÊ k¬çºc˜©ª¡¥f 3»’èƒi”¡1nuŠ¥S“öÞy* LC »µ¥ É$»ø”6Žx&´Ê×µ÷“?jÅ]d”Cí`»?»”Êf!»%žJ°ƒu×µ»ŽȬ* ©£‰„E´š½ O»Ÿ”б” Æ—Ðѯ׵Œ­I8^¹8»×`.Uô¹p F,!iƒ¨T/A»€“†® fQ”†®Wf»þ‘^Í©¨š "b^»Ì’|»h>»|’ "Ò”åvª„» ‚»,»b Z»g»t$²n»§Kòº¡—|M3~»’‘j/N=´U c› »°S zq ‡»Ê~üª­­dn±H—ñQ>»Ñ“/ ósª»˜fbŒ©{°/ /Ž»3•67œ»«»x yØ—g» 9jª¬¢Ð ™jà¹ý¹ 6˜»– oC£»¾¹ q´b»A¶kR1˜c/|8³¢c0Šõ¸ÜJ¾$¡3 G»§¸p yZóMn» :1j»}W¨~}3’Skm»¶»™¡=e)¸U % c´ãº{y·è»–«‘»§»ÀbjB¥¹™kû»çºB¢ê»¼T¼õ»Ù a;ø»1+’d‡»u·—GO¸€k]*¾º8»à1x¼ý‚¼¤ºÝ» ¼¼G]@!»EQŠ¹è»„e"¼·¹¡’–kú”²‚/ŸV¹$ <–ûg.²,»g”»Y¸è»úg)¼Ò» é7è´ëº¨ˆJà»Ì!G¼ïº-6?æºU DYµg´O¼P—¸Þ«"­R»/—"­§ <¼ “:lq=å«@ -i·§»«tH‡»½­d—™!,B-¼&ao¸í±@ aC¼]¸çºs `¼; E¬µ·¶»h[Œ·h¼IÅ]k¼šy¼ž¹@ &}g¼p¸˜ï:U¼n»?Mæ»ä¹sá9{·P G] Y º@ =ì­“´ÙlóS¢…¶šÆ' ¤¼4»Ü«=¤Ï¯Õ¿+T®O¶Ê»CM®Ò»Ž£XVº«»ÓRTªñ·š¼Š¶ó”b“bJQ@¼¡z)º§çµp qER,¬¼>,e[Ÿ¶Í mL®½»˜½ RX ¶¯nŸû ± 'ø¸?»Ø6ԼƼTÃvܼ¯»—.Èbø»døª«º´^«ã¼Ûº:mª§»¼¸¼D°¬Í·¢–{aç¼¢Œ¶O¼ø "´ãº½bºÎ¼$¦&™jmᤆ¡%¤-¶Ý¼l ̼S’vmd ½»ö¼c <8’¥[}#Ѽ½@i[°v¢£[eb¶È”Ø;%"ß³8»Ò\Ñ٦Ǽɠ½ƒ”Ø©|“Ù¡+½²¸ Ž« »¢mY´U·ä”–š€½Ç¼ÆÒ’!žö¼½Û‘W»Žo£Ò»ºH&¼Ág2<½º»a366;¯a¼Ý¨6¸çºÊŸ"½¢çm?½ÌºU ÝhG?‡»]Žw!»Ð, &½x»ª·‡/¬¼_/¿‘U½; Õ°2L»eßxƒ³Ù¼F6d½ñ¼¢–ª“V¼ºp Û…€½*½©£öºû¼Þ¶bRÕºãºî-—¢Ô´@½©£‰°³ºçº²@=¦³›“b—u¦»«» ñ2õ¼ƒ½³En»Q Õ½™½*‚g½Ò»Ù餷[»>+ɻн›¯|½¼~½ß<¨½¶²îY“OºÊ»)†¦ñ¼˜‰Kre½t6P½à»¸Åî»6T5½p b.¦§»½q½Ð½c £5¼%Ž¡½Ç½N?°˜½U X%C½°±«»6Ð œ¯»ñD09¬¼ç}C˜‡»Tlê¸Ù¼Ÿ6†©}½Ô½E ΌڽǞ—½3» ÉTٽһĖ̽¿½³¥½4»¦ fn»? °a½Î¼±lÎyL»D œ½ãº "¾¶»ô Â½Š½€†¾ì¹£h ¾ ¾ûž,ªÐ¥o½¾çº¿r8y½2¾¿‹Tš•q=kœ,»bD8¾è½¢TfWN·Ç½"Ñ{½ý ø¸,’¡¹15n»#° ܼؒ¢/E4£¡£½¿*í¼½Ë Ù;c¡è“›üZ¾4»Ö ç8…œ¼›8~Kg0/3¬u]q$O¾ú½éY•…f¾#½§mœ~k¾J¯”Œ&wmš°°F¸˜rl¹Ö»K¿¡¾ ’òMâp:y 5´<œ¾Y–¹ ƒKѾD 9ah’£rR#^¾; =*5‘¿¯¹ è2iµëºG.ynm·y jIµ †¿Ù¼22,u°£>9 |‘¿Ê—>9’¤¿y¿%†Õ_'¾B20«Ð½FSc(nT|DøSÈ¢œ‹½y¥¿4»(góW,§¹¿ß«?Zz¿z2 ]½½¿Ð´'¿è½§It>cq¿™:húpy *l[ˆu¿ãº]9<¡¿]^¥Ò¿“|=Õ¿¡¾L¾~*yn½ $ª»±c»iŸ£+¤J\iˆçºƒD¬¥¼¿ UbY9½Ž#§°¼‚½ƒ`·2ó¿š¼Ó W þ¿y¿» ÔŽÀZ¿¾(êWŽ«¿ § Š»a¼Ê#>Ö¿ô¿{m À•˜ qWŽÀª`i¿5¾Àò=ÀÀÞ®Ÿ‹££ï€p%ÿ ¬¼Z:uS»U jÕ` D:[—”¾o¼>g®»#½Î>~o•›è+9À¡¾[“¢–ƒEÀ ÀH ,èJj¾y¿ µ8¥;â‘öõnq»9Œ¿B¿ô¿9>”P5>[À¥ O3§Þ¤7O9SG’À·Ý” ’yn$ÀHirM †žÅ¡¸O ¾Z¿R•V$ô] qÀ zEuÀcÀWÞ£Z$é„OÀzÀŸ”aÚªžT¿¿™‡ zI]¿vÀµGÓn>œc$~ÀPÀnÀû„جÏ ²»gÀ— •ÀžÀ4 l$~­Y¿y¾d—l$sw­À3¬p$‡ÀKÀnÀºeÀn&”$ µ¯yÀy¾,’Ê}T‰©¶å/NJ¦å/á ¾À–—™ª×nf¤gÀ[U:À™˜$(Bh£‡ áuãn­’–$È ïe€À{¡¤F”¾˜U—32޳ ‘?ÈR»!¤´оFÀù˜ô°–±ŠÀy¾ë‘ô°ÈÀ`™ô°£+ÉÀ9 ¬$;8³€ÀÇ i›–ÀcÀ ^¼ âÀÑ·Š¸’ñ‘cr¥—y§WÀD½¢ÀÎ)ÁÙ“^nPœZ¨ Ù$^Y ¼cÀ]>cš]Ì‚_À‡˜Og¼m€ÀÇXÁ•˜c ÔSJÀòÀ–”Õfï¼3•w쪌­1ÇšùÀóÀE™¦ ¾™ ME#Àª¿¯Û½„)1Á Þ½U(ÿ¿û H=\š|#ŸÇqÀ7%,uþ¤ï!ã“W¶ð/¬PÁῌÀˆhÙÁ• ghJrÀdT]d ¥&%=¬9Á.ÁÆUÛn”²0%V Æ£¬OI½Þ-êsôP±áÀlÁ«ß)zÁ°—>%¾ ~¼>Áx¿Z¿âÀI<Á~ÁŸ-sG[ÁZ¿¯ÀGaX¿ŠÁ® i ¿ ’)‰ ¹¸k“D0Ú©ùÀ[“5I’"—[^RÁà PQ8¸T£.·¿o”5]TªÁÕ“@säN°ê½œyjsÁ#ú¦ð 1m>áÀv¢1å0͵s%E±sÁê^}™RÁÃ^<‘ÁðuOg½Áƒ”j¯²ÀúÀ| ùN¼Þ¶—6VWRÁ«SyÏÁ¡’„–É ÁJÁ+ nÁ º 3ŒP¥ûnž 9Á$ ÷Žë³ùÀ ’¶»Áö‘TRÕŸëŸ}E”U T°%t¿AÁÊ—QXoÁ&ŸjQÁsÁäûI%ÁRÁ»Xdl’–Ÿî,ÁÍ~eÁÊš Â\Á䔟65(ÂJÁOëSÕëOú’úX# Â/“:T¹;žÁÓÀNEžHõ¸õb” / Õq!N“Y–Þ)ÕÒÀ ÁRM§<ÂΛƒx<ñ ÂÕqsZ#²vÀDQ™ÀÓÁŸÁ~ äNIÁ7›ŸmÀQÂb§ow”µÄzbÀû ”H^À “n‹ Ò‡E`ÂñRóÁ8Â?QlMâ‘¥«’e¹Q¹‹WÂ0¥ ·™[ßÁaÁí¢TEÂÉTt!Ã’™ 5 ZFÏm¬Ô§t¡¾$’³û,¼Á„¾Á%î$ŽÁ¿ÀŒÀ÷ªøn´¿s §“.Ás¢EuÂ}Âã ÆT”ü‹õè–® ø¢ØŸÓÁ3’XL‡}¥XL7öVŠÂ¹#Á ˜ÒsqV¤ÂðÁ:nPÂÒ›xj÷Á[/’r›CZŠÂ{¾Q“Á}Â$(àÀÂ2Á ¬FU”˸EÂrÂZˆ©¿‹Àø”±6 FùÀÀÀ!(ìQkÁ¯Â^4—FÁ ǪP«tA4—Ápï²×ÂÂe¿‰ÂJ“B&é7ˆÂ†ÁÁ5¸Ò¶¦ ´ïÁÐÁ ’÷Â5Ì!ËTØÂZ—E”âÀÛäÀþÂ^Š®Â‹Á4ªûº§²ÌÂè§åm¢Ÿt†ÃñÀ³ÀÆ$Ϫ p-hoâÀOÏnÓÁÀÀ€^‘ÀyÂêiÀÜ—º]p ;¡¾‰’”<%"¶ÀŽÂÂR!Ã(¦µŽHùÀ¥’D Rû ÄÂÁ•-½†=Á?Ã裺À?'æl’"+ÃÛ‹ÁL[pS”|’q°[ 0d‘ÀÁ^!K^‹SftØ’JTWDyª ''Ä^&ÃÒ›oB”Ä)Â#ÃV6dušèTW&Ñ›cÂÄIþoàšônÃ:œ¬_l˜A”¾.ÃÐI·Œ•›ô±8ÇÂZ¿ÃQ7îŠh—€†”)9ÃRà ¾:ÃS“";%8&Ã1…Ãy¾:ÃGÚe~ÃŽÃ ¦b“Ë.Zì ÒÀ2ˆ(™Â?Ãê"ȾÈÂFÁ6É, ¦ “¿œœ›Ãp’œ<Ó‹µÃ…ˆ‰!ËTÃgoA”]Ãs”Ì&î$±Ã® ã«x…6ÃÃÜš¨¤{†ü›´ ( ÀÃ×’¦Ã>DµÃ{À]=¸Ã̇‘ËQ¹¦® „²¼ .Û M‘~ÃÑ“ 4‘–{ÀX›»¿„Â.ÁJ1^Ì“ +0.•ÀÀ®¢ÑCÙÃŒÀž Âû¦¥ >,æÃïÃÁ5·&h÷¦›ÁœØÃ·”a‚TëÃûÃ/K†½¦Eh"9ÃÑý p»š/uG,ÃÐÃÄÈ9œ¿-ÃèÃkûwyÂ[F¨Í¦¢g®M;ãÂÂPÃç»±µ =à ÄıH@çÃÈ”O'nPÄòÀ¾ÁHÄhÀK6TÂÀ Wο÷“['ÄúÂÄ“GM1¤’ÄX›ÂÄ)ĈFÄ!ÄbQÄ™ÃRç<™<6ü ';Äœ¡7½gÃftÄœo_I¢V ôPˆ_ÂnzE$Ä­ÃvÀaÑ<Ãp~Oººû í§¼êÂÂe6®O£ÙA=NÄ8Ä w¯"¤’ZÄš¿’!ÄŸ—&7Ä›Ä1ž²Í¼bÄ('Hw’¥L‚Ä·tè“ã.¶¼µÃî›Ñz’ÄÄSª(ªì¹ç6s ¶Â¯ºÀ Ó ºú¿aÁø»!†·ŒÀjFùÀ“´…B¯"pÄRÄFÁ¹JžHŽÄaÁõ•)L;sÑÃã|J§;¿¬tv°¥Ã²Ãt®‡”¾6Á»e¤ö¥–uô]Ç’ÃÄ_"ÃÂ3Ä)ăW3KÏÄ ºÓÁ¶Ä „S¡¾ÞÄÆ꒞įgkƒ¨Ã^½PjVâÄë”z”•­ é‘òÂy¾ðÄæ?ËTÏÄ×,í†ÃRÃÁ·dÃ-Ã^ýki«ˆ’èÃ< à §rÀ< «úÄA¶2!Åý¹Z L¦`Ä™’Å{ Å ˜1 óÁ» fùD±²’ÃÆ'»mÝÄÅr ®?ÊÄ® r®?1£Ñ'mÅ:Ãò¯…ç¾™ Ìo;hÇÄë%^5ÅÁ ½AÃ=Åû @CÛ›S©ÇÄY B†§&UÝ óÁÿ V6ãvϯñ¤Q^  q;ÅþÁKahäÁÅ‹ŸrÃÏš´ ÏÃy¾$•_Ũ÷ÚÄc DÅß§!(NÁà¿þÄÂÃÄVÅÁ ~0ÅLÅÑ >^>C§BÅ)ýÄbÅÌ ¸¶*Å6Åo M‘Ó§š®ª~kŪ[ˆK¤’/ÉIvÅÈ ¥0mÅbÅÁ¡ù]¨ÙÂQ-- 1Â>KËªŠ–7{¡5‘Z•@0dLšt%F$^»x–‹(創Ã;’‹(Õ5Á'Å´7“ÅB–FhÿvÅ”Ä@ï§Ú åÀ~Å À‰N™¯u"M¨Ä{À-L«)Á‡]õ¬EÅ!( 9ÎÅÔt7­ 8ÇÅŒÀt$jYìÄE(½ÄÅÂÃå$ÚÅ{°g 9’- ÎÄVµ'v«‘ÚĒ%£Âb¿§ ¨Ä¶ÄI#ÆG‚ÅÁ…:¦ž5vËÂP¬!t‘†õÅû …tº©Ä^4ÄZØÄ À8ÂõC£ÂäÁÔÅ€ jYWKòŽh* ZÅÔÅÙaÆ.ï¤TÊ–lv‚Z§’Ãä6±Âvx^4ã†ÅÀŽm«Û§^4åu˜ÃNœÔÅc@y Ÿ¡'CÄ^4ý³ãÁÞÅëÅ’S¤Fü5Ƽ¦j® ¿UûI@ÂaÁìL!iÆ7™ ¿Å³À[0š8Æ ,a;Ýœ_0k}KƯ”_0PöÄÊÀvˆËQ Ƭ¥ Ø»¥x0ÆÂDÆçPWª/µ¥Î€ZƤõ@ö¶Ÿ¥UC2ÄnŲÃC&s 1Ãt–˜(ë"yÆ å@–´vÀ-oi/—š 4™wþÁ™{FÆfÆ™ Æ{‘xðc(&Z§˜E´ê(…'VÆ.Â"—©¾¹š “ˆÆsÅÍXÄaÄ_Æ[·%u8‰-~òjùHJ¨èIHÆ[S–ÄßÿÅ8ÆP ò Xq OÆHÆcu!u¦Æª›[J–¥ #‰_ÆO­¡Æ¯Á×g ‚GÆÃÆÊ¾Æ°–¤]3tÆ-ÁÁøDïˆCÆ‚ÆË¸ÅÆcÅz!¿(&—‰åÅ³ÆÆ[ ÙEÕÆæ ^¢·ÆŒÆÁÇ%"VÅÌ ùoW¡¹¶¬¤óº´Æ~X}š¡¥~±ÅïºL•ÓÁ®ÅC ¶C‘¯u,äs,€gUãs󯯉¾Û>ÃrÀe lM£Å2)k`ÉÆ(&beÇé±Ç4 ÇÄ‹Á ø ËÅöÇNP¨ÄôÁj·,˜míÆ†ê¸Æ_Æì°yÂz€ä‚-ÇhUÇzÆèãvIÁPÆÁpz.Á‚†v´RÃ&Àw)øW·˜BÁOŽbÄ^ÃyžL ž;^þy®ÅÇ=p“Aƒ»I¯À HÇÂUŽœŠ·—4ÉÇvcÉ™} É×GzÇ`R‚6Ã`^È$É 95AÉ” z?E–™§*»!îž™ z*»ªÈF¸VÁÇõÈ×£GÉðÈ|ȃ ÚTÔÇ!*RXHÉ÷!JrÎW¥k;%³ÈÈ´ XôÈÎÈéÕWÆ0$VäÆÕÈç6þÁÒ#û`|Ä“ÈÅéÇuÆ® >#žÇ·Â<Ð+•õÈbÉ’ÈaÁ%²*É{É|È1wDÉtÈ-=£­ÆÈ³ ayÂÈ »0ɰ/…Élȇɤ"ÅÆ.ÃöI“pvÃ^n.‘È2Ãþ‘ÿ ½u«ÎÈú¯É‹È³=Î¥²ÂïÂô#î§‚Ã8§a–ÈX•ȋɄù¬{Àø#<È…Æ%1Å^Ã/0*ÞÇÉKwC¡ÉÖ>‘É[ÈKÄûÚ/S˜ö =*ÊÉŒÀr•šë*üœÐ™ë*&ÈJĤÈ#ÙR>É9•¨š}è2N 4ÆUI`¨¥¯”ùÉ-šA€LÃçš *È\—¿(¹¯zªÚ1sȆÉd„· ¤É ñ`=­LÈ„¹ŸQ”‰÷ÑÀðȶ›þ»¡—ŽD+N‡”ëŸd”e¢f ÊŸÇ “ <¬ÀÊ{)G©¡Á?^Þ«D+U{G’7Ê˜D+O}Æ÷“D­$çÆºÄÊŠ¶¬Ãʨ &y¥ ©¢HÚ"–™6¾$ÉIʉٴ+ÉØ’.yúŸ`³£Q«ØÈ3ÊßY¡Ä›¿UlK©k“7ILÊ,C=<¾ÄÊÞoeÆDÊ[6yNÆ/ÉMÊPCT¢l£:yø "Å,Êó•¦nÊTÊ‹ ýÆÊÊ·\ÉÔÈÊ…qÊ®Àù˜J¥â»µ¬gÊ1 ‰!²É¢Æ ‚RˆÊó°—‰+}ÆR•‰+–Æ‘ÊcÊ¡‰¿»”ðL[\vÑ“ðLÈ»qƶ½~°Ê‰7Gʙü›‹óS­È”ª+çsÞ˜³+]*SÊÊ,<•Ê™’^y!/—^y*Êzà E‚ àšÆ·,ýn›Ê¿+O¨Äî›Ï+c{^ÊÊÑ5ɴʹÊ* Œ¿ÊÊ΋Ûvªý6%è±–ÊP^gzµÃŒ­P^³Êì+­KøÉ²B•ÈåÄA¶‹yŠ­¨š‹y¤ ÕʪÊtDW¡Ç”~ƒ+ÉþÊ|±…ʶÕÂ8íÉdÛE1È"Ã’¦«‘ŒÇè“‚¹Ã$ ñOÊ•°ñO8 ­gÊQŸ ÆšÉ,˜ÈË:šh}Æ­MÊñM}ÁÊAŽzž ¿$Ë ·­ø´/ËB¢Á¦8ºyÃI¢1÷)Â¥’18ÿÉ;Ë,3#ÉÒ­3ÊÉ,cvþÊ÷Ë¡š¬yg$°K!ue³/ËJl’Ž•›K!Ì…HË/tþÊdkœšÃÊd ÕÊXÊ -O‚/Ë×\zœíÊ#"³=›¡DŒ.bÊ ð.§%MË Ë£g9Ë#橽˜?Ê|’A,YCþy“´áUxC6Êj²TóÊ÷k§ÉWܾ/ËÕe‚Æ—1w³x¯ËMh­Çp’k,{ÂùÀËڞ؅¿cÉ Ë qËþÆ 2‚ÜQ ËÃ}¥ËŸÊ’Ki+õ¸X^KµÃ³¾$ËÙ«ÉËxH€ËS“Ó(Á¢ÊvË =?'P…[NvSË3Ç+%›%±µ•­˜ .‰2·ü¹û¿`¥EA˵ÊD¹ ŠÉÆ}fÔ(yËcÊ…Õ0b Ë@ŠË#ËáË­t9Ȟˮ¹‰,µÄ÷Ê>BÄöÊý¹¯ŠIÑɽËÂaT¢æ­ÁËÄô^èË¿jÇËþ¤z®ÉI^µË ËN6ÄZ¿…Ë *Õ¡3•ÎgêËacoËDhoÌ/Ë8|ËzV t—h oЬz«–I^ÌαÇÉüÉÁ uÌVµe^ùD2À§Ë~ ;HrxY¶ÂÊ@Êl€ÇÙÄvËÝ RunË÷Êø’k`1£ ,â>À¢Ã*âUëËðË!•7°¨,8ÊĵÊ)P<̆§*=LÅP ¬,CÌìÊ:ÌÞ" ËA»,™ÊÝÊcÊÁPu½ŸéKy)Âí–¿,NÀ¦Ë.ÌlY‹…Ê,íŸþËõªûUù2í€[ÌÁP±Ç À£s?§Ó’b:xÆuË=73jB,® (Ö¯ÌIÊn¥©Ë®žË= ª  ÂÊU nz_•$­]m̶%ýŸ‚°‡ÌÍZ@šWíÊARiË7D ø«]Ì_˹ÊÛ›$Y ª­»f¯-=7Mo ¥Ì1Pް[© :¯æÇMƀѭ Ì:Ìm.ŒÊt–Ô3É©ÀSÌe:1žÃÌÄ©ÊgÊ–#uÊxÌjÌ&ħÌ Z7S,±Z7Á5̀̊5¤FÝœo"uKÀËÜÌÊ5´äŸPÄ}iÌ6Ì¡Æw¡Ê­ÅY–ŸÉÅVÆ‘Ì}) 0nÌÑ,ÆÕÌ·©¿ZÌ >lìÌ•˜-þÉRÌ€Ìï{0Íó åÌýž³ C¢‹‹õ̱½ÔÌfÌŽo\!ˆÍ–¶yÍΛt£¼«ˆÍA,¥ËQÍÄƔ̒]-G6Í4 x^B Qo5²ÅÆQÍÑ˖ɶ [Á\šk-y Í ¤ƒµˆøÈk-M ýÊgÊ€&ËvÊ,) OÂvʬÍÚ,ÌÛ’A=” ØËÍêuÅK̺Íßî§„ÍìY¼ÍȃžÖ¹|Êr;ÍV ðÉÐÍ´Ww§|f:kȨÌAÌïªpC¯Cf:ÈCŽ£[“_ #9Ž£ Í‘©®ÍmÉK{Q(Í.f:( ߯šÊé–£-\¼T˳œ®XÍÙeoÈ™ÈVÃ0.¯¬Íˆ ˜ÈÔȾÍ]_Áì¢I=eÅ}ÍMÊvz£Ì6”ç(‘ƒüÏ}"Ï©’Ö AªÎ­’W}õÊÁÏùÃWÊÏ4–R,ÎÏøþÍl’)#·È“ÍáuÎYã’Ÿ"PPÚ ‰’j}f{ÏË@$f9Ï ¾#1ÎàÏ|9fÉ8Ð'_£FЈQ¢ÎÏ9U7ÐTšâËlªŸYVV!PÐXʧ\¡„ÍUT_ÊãËÍ‘ÏÐêȘ’|Ï¡°+6Ê}ÐNÎ/Ä1Œ¦ÏÐ[DÎYÐý ÄÜÏÒo>І½ 1i 4ÈÏ·—ƵÊF¸4¡[Ëp÷IË×£ð± ÇÁÏÊ«¯±¢±¤"IÄo´š}ª»àÏ@‘êÍBÐk¦3N‹9Ê Ô(1Ì—Ð|=?¼(¢7ù2~§çÏr7±Š˜*ssÐQЄe˜¹<Ê–]>ƒ¨LÁ˜gÐË¿¶üŒ–йÊÇre›Êô/0«QËÍ?ÐIÊsBƧψ=Ï÷ÎÉTÚÐo“3ÇLåÈÏÈKl†‹Í>&„ÐcÍzÍÂtreØÐŸ´ÛÉKÏ Lþ™O£ˆm ²XÊ«<© >˾'IøÉÕ'ÑQÍà¤ÎÁ‡ÏÉ¥ã`Ô“ÆÐŒFx…ÐW°-ÏãŰuϯé¾<¾ËŸÍGÏA}Gš± )¹»ŒRœ·[87»ÿÎÞ:ëÐÑò<´Åó‘q09ͧÐà#ÙEÌИÇɳjÆJWÐÕÏ3Ê…U>ÐÏr¹ÜÊâÍ@ÑëNLËÜ<<«ì¹]¶¹ÍcÊ‚ËyÎ_Í`k̹JÑì‚2Ñ|Ï-ë¿éÀÔ/]ÿÐÏ·Íõ¥šËv{q¦ãÏ™{œŠåÊ¥{0ŠæÅðËl5½[Î9“ÈBáÊAì#|NÊêfü†Ã¸`ΆEÑAŸ*)· ¼Ë,Ê:¶S°±ÏÇÊ@…Ñ¢MLѭĨÑÉ |±±ÐF~ ÚºQÑ}gÎéÊ ÑÀ¿³ÑxÁkÑY:ÔÌBË&°[©ï¯0Šé|íÊ¥£¡Ì šJã¨ÊÑsÑwË_‘íÊ2\±Ë2ÎäËA®‰;ÁˆèË ;ÙšË #OÞÐMÊÒCÏz…Ë@’·dŒÄrÀÏneWþµºÒ}ÎÉ2ÇvÑ3ÊBEoÈé|¤Ñ±?kô…ÑŒ&—±Ï€±ÐÐhÓ«&š“Xš¯hZ‹“ÌËËX%ÃuÓ^M }ÆèËÓ*ÜÃ…ÑZ:° w©XÓ·3#Ó§ÎVšÑïËwÑLrÃÓÁ6ÒÓL:Ó?Ð,Ê+MPÊ¿XaÌÙÊz…"†˜¾dOÓÒ_ƒøš] ±Ï0ѪÓ\Ó"ÿ^’£Ì"ë"úÎãÊœŠÁÓc3?'ZÒŒ­ 3ð‡ ®F)Õ­žÒ yžÝ&+¤ú?'B‰•NAòÁÑÞ¶&3²ÌµÃd—&3—3®W‚Ds·Ëø”þëWZÒ ’þÓÉVÆ[“þÓDjÎíÓ “¤ÃÏÓÎÈ@7Г´:3é‡͈4³ðÓæÓOÍ|n‘É©DÂ§Ï 15‘`ùÓ-)ÅÁÍR®ŒÃzn‘PÓÆ×Óª!ÃLÈ9¦ÀkžËX3¥HÒÔ ÁÎ.Ôö _¿*-³Ÿ”2·,D~¤ÑÇ=y*¿ ÔͨÒBÓ'ÔjβÓP Ð"Š®ZŸv¢A¦%IÔ™^A” ÔVµ“žä]ÔﺗžïHEHTŸZû•l°½²Ñ1tV!ËÓÒÒ :ÍÔä-ÏÔ`²D(1tZY-Ò‰Ê'Ôì˜ÁÓíÓ!17Ó=ÔtÔ3zºÎÔ°NCÊbÅÁ kS6*åÓ Ôd)0¼ï‘3 -ˆÎùÓ4<†ÏsÒbÔ"9GEΓÔ&Éÿ—Æ mÆÔýQ&¼z¼ZÔ¯±4ÆÒ¼»¸¨ š3w"eÌY–Ÿ3­²ƒ¤š3_Ô'Ô[9y2Ô*k ÀJÛÓr.¹¦Î ˜W)€-3Ð;’>Eà~·¼íÓ“2R-"¸·3ˆ™ÔÑÔ¨Ô Εú>ÏÆÌÎÇ?tÐzÎÜÔF?¼Óƒ¨Jg‡_6µ>ÔÉ €¶ƒžÌ3g¨ ¨ã0D¡B­Å‹¤¨Ð2ÔŠEõÔ;ºÁÐVô0.ÈÙ“%QMO_ÏO¦9 }Ô°Ò+ %Q(EIÐþÔ‚¯/·ðC¾ÎO¦o ÔkÍ ÕŠPÄ^¯a1õZÒ$’a1>5”¡4Ý,ëµ} 4B![°Õ߉ ¹r_)IJ ÕÕ ø¡ö¥dw/ÑRÔ<Ð)¸dwà&ÒÀ¿“Ÿ3’ÎiÞTˆÑðAi âÇ s‡º³Ô ‰;@¡AÕgI :·S¦R¥ü²û ÉuêÔÕŸ)û\EÂɶ¯õÓ;`F† À‚#ÚÁòÔÊ—BE*NÕiÕ ´#¹ÓÜÔ©zÈ ÔOÕ®cVVÕeãÔ°ÕÇ h¹à¼ŒµøÏ¼›FEê‹lÕæÓûD¯²ÔfÔ¯-È’Ô™ØPR žÈ·9ÕÕ:l¾ÊepÀ»ŠÕšÕ´AŲÛÓŽK ÑøÍ:Õy|¨¹Ó¤Õþ–xÓxÕÕNTÔgÓ8t£mÕÕv·ZȾ—4&×ÔÀI°³h–(ŒN~ÑA¶H Ú<‹§à‡“Ñk“]˜wÕ°—•€IR½ÜÔ.«"¯ÔH ¶0MÉÕ$x®ÕƒÏÕo#¼¸ë‘ggož‚¹:Õý#fÓœÏÕëgèÕ’§v‡ýrGÕÑJ¥;‚ÔÀT2áÕ+Õ”Uœ ϯ44SvÔÕîˆqÓMÔ-KS Út1QÌ ;0 {ø§aÕVjBóÕ ÖCÜŸ•MÌ®ÔpÔ@œÑ-¦Í$€_Õrw× Í» rw !}ÆÔ‘b A@0 Mòճ΂çPÇlÎÖ² AÔ0­€ÐÖ¨bÀÔ¯Õ8Ö£ôÌÔ÷ï³Åºã=K~0ÏÄ“üŒ2Õ³Ô\~|lÔ|pÖTýÕ™hš(b‘¹ÛÓ\E@—Gwþ &Õ4 Z5( Ö ï=2_ÖþÔlš ¿ÖVEé7ÛÎù˜àæeY½JÖ8d¶Ô•˜Ì+¸p²J“sÖŸÐ0jƒ›EmÆõÓ|QXr¥½jƒvE$ÖÅÕSs£ТÌq×µ ÕáYWd¦Ê[žiÐÔ¦CAÖ«ÕÖ°ÇuÖFÖù%U(ÕdƒÂÓÍΛ¿yöÔ`Öï»U¯Ô4:¿Žº/rbþ¤ƒ¯Î¦eoΨ˜/ÎJ7Ö²Ö4£ÕÖÍÏÕé–€1R×µ4Ö¬ËÏÐÕ¤´Ö Ír$ÔЈÖë~ °´·Ôk@WóËè“Õ}IÓ,ÖUÖŠLÈÖ±Ö[DRÒhÎJÖ«w“ïÐdµ|ÖkÖö ùŠÓ‡Õ çÖ’Ö0 C¾ÖZÕ+-Ù<™È €r '²ßÓÌV1ÔÛÔ¦-#]O¸n0ÞÓ^ÔÎV‘ Öʆ빞¹UÖzS ×–ÔFt§ÖÔ¢osî»àž³ÏÈÔŸò"„ “ gÖƒþ’Õ‚Í4Ô•º# %I.ÀÕƒXŠ\š ˜rdÕkÖõQ§·ÚÓ•f-×>Ð% '\˜ÏRZ?OŸ4׫3ØÉÖ‡ÇÖüÓ3•‡ÇCaÕ˜¨f§æ•ÿ=ù 1ŇՀ-º 1;×›[Ö§ÕÎ*©¿¥¹–Ge¡—('\m×þÖ ê rgÖTZ$ºøÈfEìkÃBÔE=†Ö»˜áMgÔÖZÕŠŒ2×ãÔ;×h5º)ט§ÆòÒ³ÔÙeµÓ¯ÕÛÓZk+¨1£nƒ ½ƒ×ŸAzׯ¸;×&¶ç$ÀÜ„š¡ÐU g@WmºæÓ¢e×ßÔ™n£ªZ¯Ô£#+¨ŠÔÜ„u;Õ–”ýƒ,Œ¤Ö™×ëmX  ×@ƒ0>·&€BŒŽÕƒ× ­¨s°X×êpWåÖ€ ò´ ÕÞG\×t×…”b ’¥ü– ×ý¹!„R ƒÖË×n/õ¢3×;×ðÛ¾;ÖÓת–Ô˜[ÈNÒ׈1,Ôl Çö4¢¤–•¾$ÂÊÔ‘^Õª”HΓÊÒÀ×·‰A×¼Ò4×§m=˘§)­Nt·Hõ ÚÿÌú×ÊMÖÝlâÏ>“…‰;1Ì|’„ÛQñÅ^Ô6z!׃×e8mËéÎ y Æ×Å»;×|=‚¨)×"™sá×BÇwh—ž19}!ו6;Î-Ð$Ø„ÃO[×ò²’Bõ¸Œ,¹ÅÔy× Ñ´¿1[/Õ+B_³×@bVʯԔ ZÒõÓùŸ®åÖcÃ~ž”1¨¦ƒ(ðÏ¡’D5rÎËZÕW5x¡±Ö ¿J÷ɽÍ$Ø£µR•^5ÞÂ#عg9aÕÃp2wðβË'»×òÇþÔùG}Ã5؉ÃÃ×ÎAc"ËÔû¿¿)T¡ª× Õ¶§ó‘t5ÕȰVÔï„L2«Í¹õJËË™’)# ,ØË¥’Ÿ 9òį”Ÿ 4x”ÏÕ_·tسÉkرõÊSÒ$Øy·Ó¸p¸œØJÏE±R$)ÔËÌÕñ2w%×Ïp¦¬4¹¹s!ÇÞÕzØØ;äÊ­Š0„q«BÔOe ´„º4ªŸKºÏÁ$q=1Ø^Ô=65¹Ó‚ÈÊ·¼ÛÓ¬5 (Ý׎oDl$Øà#¶p’Ó‡ØCØ{2T”?Ønp}×3¬&/¶:r¼Å58ÓÕ¹š¾ÕØÔxü‹Ø‡Õ h̹+Øå‹æFدŒ*$cØõØ›Å{°~…îw‘¹•Ÿ®x‹RœSE¤Öf!€%צþuý×ô¬bØÔ¤í®fؤ®§ÙÎÍßØØ©S£ÂÇ׉s²EÖæÓŽŸ®×äj¸‚‡ÖÙž öØÈÓÊ)Œ2€Øƒ”ÃÇ•H+ÙMØø0gÖ²gŠxÏØ°^¼`åÖyF²º1¼Ù†Š Ù›ØÙ<›U”Ë…G« »;&±ÄýÓÀ V‘û’ä^à×DÒÙ1r3ÙOMþá× ð ØËÔR6†´þØ™! ÉÖ×µ¡ë(·ÈuØÙíD ØË×à-™snœ”rF¥±§×Ä9“IÖfÔº4íØÌÕºØþØ&7¤Ä¾™¸¼Ó„Øœ”^šÈÍ!Öô Øv•°¥bl×䔸1 xQٱسԌF14ÜšI î×–Ù}VU2Ô› v•›Y€Ù ‰Ù`Ö‘¾”A9ÙgóØÙÖþØ­ÒÈ+ÊRÔ ™s ¶¸13P·‚ÙwÙvÓcÙ³Õrþ1gÙáÖwÙ“´ÙƒÕ±½gÕ ÓÎ SQÔ ÕOqg_YÙ†#¼ÙÁÙÛÓrRCÙ¡¥,WÆfÅþÔ§¿ã–!ÙuX ¹û7‡*ËÕ›â£A™ÖÙ-‘?ÙOÒË×$ Ù5Ø‘$©Ù‚וì!mTçÔ‹ ÚÙ Õ½±€š%>“E>±Õ“%>&•XÙB×%”o“%>'Û±Ùņ"ØèÁ=ot½²ÅÚ[[Ô7\LÈÝ}•QÛ+ µd÷£ÈbÛž–?Ö91iÒZÛŒcÎZ¿žË9Ä2ÛqÛA®íÑ7ÛÜéɾ¾hcŒ_ÚVæ4ÚÔ'ÚŒÛx—Ì£ÑÀZÛ⥮ÎRÙVù#GÛè“þìm@ÛïºF8 pÛ”Ûñ~Û—Ø„ÛB½‡Ø€Û §ÛˆÛFWsÕ‡ÛfÛo Tµd£¤ÑH*¡¹»RÛ¿ÔªÕ‡ÛZÛ`ÕØ,’ë‰g™Õ\ìÕp’þ "ÌÛ€Ûâ;ÓÊÛœbwÌ~×7ÛÊyÑ—Û{Ò=Íy×ÛŽËeÒL-¾’ÝÕaÛÕÛ2;)»Û8nÛÛ@ÙñzÁ ¦Ø,ÛS8q³ºÛâÛbx ðÛ3Û‚3É9¦ÑÛ…L<£êÛ"ÿÛ­Ž#¨)¤Ü©ÛÇÊÛAÊÜ»½Km)§¾¹p€aדÛ[°b:ºÝ×Ñq½&VÆ€Ûý ¦µË˜Û=Þ%h×dÙÅ¿ÖÚû¿z˜ÍŒ×£܆HÚÜÛÀÅK﻽¹š£»Û lºUÛ; ¼‡&Úy¾8Üy|P$–L:çKʧ±@¸BÃ7ÛËÊ‹£×È­h ”× Ã' QÜrª±c¼¡ÔP :…UÜPÎÊ2ÜP ‰:µ]ÜqÛË&YÜÜfFñ×?Ü"ÜK*Ü-eÜZܨæ¡1£KŒ"zÙ,Û¯›ž ÍAؽÔ9Œôx¡Ë(ÛªlÜâ•m8%GÔ» µ:™Øœ×7Û¯-›Ü Õ–ÜŠŠ9Üa”ÜÀÅ3ÛEË_Ë''€¿aÜbÛQ B‰Åâ:?u!ÜÝÛoܲ ¸ÒÉV!ù=íL zO= q*¶Ü<Ϻ ×° ³Ì6#Æ5ƒÜŽÔ"Ìë¶€Üu*Ô(õ äm¨¶ÆÜ ‘:ÌÎÜ'Îl‹‹¨‘^?(=_Õ-Ë•ÜÜó‡ÔÑiÌoÜ´ÜœÜåÜÓÕ×ÛnÜË%i+ºÌnNùtbÜz g&zS&/SS&ºR¢Ø€Û¡`”O¨;uBÁBÀÜC›þS&ëÜ ’CŽss¨Ä»Û>òç'=am؆ÔqÛb.\Î9 :;a€4Õ4!5)Ý ØðÌèy”D" ¾SÑÝÜû éñQÌü³¶ÿª‚*2$ÇY–¨¡·Ü3Ü4݈BÝ0ÝW æ¾w×4Ý–¡Ì† íü(ªû[¥ƒ´ËõÜ㌢Ø;’¥”m5Ý[X;šØ”Å0ÝÆ¾$Ýí–ã@$\ÝCÝnµosoÙUM3kÝ0Ý«­×&Ô >›:ËX—à1wÝùQrÝÊ—USNœ †ÙV^©¶ÞFÙ¦ÙPv(2ݯT|§¾#UÎ౎Ýžy¢‘Üÿ[pvÙÝ'by݃¤ [¥ ØË[“±zpyØ'SàÜ×A#Xa§‹Ý"Ý¥žÝÕg‘:&lsÝä0yÝ'“ŽºJ°)Žä(½ŸQ˜‘ÝÚÕ”*ù®ž‰>¤Ì `I‰bÛÈdDzÒG݃N^;±ú·±®ÓYÝõºÌ‚<Æ`˜oV¨þ½ ›ÝKŽ+±ÚhvÙí±'>ÝXH4ÎÖÓU 7XÞÝØÜ8Ý™!˜‡TØ|FfŸ|Fõ!Ÿ`ÛÝôÓù˜”Ž_–ñÝbÛÙ·ÚTxÛLmf°ùÜúm©Ýs„4‡u¥Ë“´ 9]3ÍrZÜÓ!ÀǰÜoÿ“»€;ØË»Ûô »!·P,³ ÝW¶<ŽÎ–Í#ˆÔ¢Ý™˜*Þ LæÝ=]€Þ0ÖàÝÍhsPƒ]PÔÞV¥Ë]Ý4(ÊÏvÝÿÜ6”˜·:bÚ¤Û?$¹ÎXØK7†=Þüa Þ(¦^ÈÍCÝÇrÀÍôÜÑÌLÞ¼@›YÃ3p…ÕFÝbܬÁXÞmܸÜV&brJ gÞ( ´ÎÝ8ÝÕ%ÞàŠgÞNî/[¸ÂGTž¡ƒvL†Fó€H<èÖ¥—¡O)Ð ˜‚Þb/—¡!z„ÞŸ0é-ÎPL×Ìþ݃žy r*€Þ¶šÿh‡Ý1˜Zo]™ËÏÒ#¹%~Ú–”€É8 —Þ3Û 9(`«»Nœ¦ÞbÛÅønyÓV&ípØÆ‹Üê]£ÖLÂÿÝvùÔAÞg Tr›Ç<ô=1³×#ËÔ ¡¥dˆŽÞiÜíÑLÓ“ÞΛeH©ÞzªeH¾#‚²|’¥§õ/ÔÞHÛœÆéݨ·LÞ5€\ ϯ²!–ͼÊޫà ̧ÞHqÅÞq¾>±Þ«¾*9tUÃ=µÉ ÞÆ— =ÆØËR•OЇ”%²4÷ÞŸ”%²kaÞbÛáUqÞÊ•i–tlÔ/9~$¢ÑYÞ~Åí¨,âÞmÛ×(8bž[«^ yÜPßz滕Ýû.)8NÛ#D ‹ÞbÜ{«Üw°X¦Ð}ǰ—¡ xÛžÙâÔ!Ý1á‰Rœ¡>^ Üš¾q(ÖÓÝ¢.sܯf©ÓW×3ß‹\ƒ2½ YÆßDß´ž#ßqÛp=ŽÝ»”|=æp ßdÝ•(k¦€='Ýû•Ø©Jß7Ûš}1ßUß3Û*•Õ±Ý^ö´‰ö¥öI-»ÖK¸¯œÃ~ÔÐT¹›¡ê>¤ÞõÛ!1M’¥±§±À±Þ©>¥,†¡Ê`óÖ߯¥ûÌë‘¿M†È½ÕÐR…» ÆÞ ¦œÐb×0Àû¨qØ3߉ͺqCÜ#ºÝf¶…X=®.ßÞ`ß³;‰ßÙ“hjrPdoß,ß|#ƒ¨ï&î-Î7Û.±osÕ>™=ZÒøÞ=¦ÛÞ™¿¸§8bß{°×Ãh ØË$’[¡ÝiÜC>˜æ-4Æ Â”¡$ÂŒN­ÝZÜA‚#Ù´ßÜzÛMàÞùË_ÙðÎùÖÆßKÌ[©(_ŠÓÕÞbœؾÑßDuݠܱߢÜAÔø”~ ™ámÛ6ØÖßêÞ1#ÛÞÆÞ‰…õkÝ˦=4î»Ø,ìÖk“ò>V:ÇÝÀFïÜ•Ù[CW&ß­ß8EÂÓÉ—ࢷ2¤Ðí— UÞè*±ƒØ¢ßC ‚|#Êßïg›vªMÀ:#Ê×Þ¶,Ë\ÍÜt–,àfÂßv¢ç mÕÝfÞúIfàF®4ö –?”Þ¼8Ü×,Ö=(x ¾Ü(x àéÕZÜ‘qÛMÇûP‚²¼›Màa˜/Ýf±ãVÚßÕ&VàAÞžÈNÒ¾Ìý¹X ŽZÒ€ÛEhîË2ß7ÛJ¯LmÛ:Nå©ÚíóÍm¥mªÿÛN°|ÂBNàMÚqÛ÷ 5(}Æ3’\‘ç_ßíÑq;€ÙÖQ ©&CîÝ‚h°ØHà[P:.Ä”]›š›Û à‚à<}“à5ϘޛeÚÇØBà[&Ê©Á Q˜U¢ØÃßê6nnÞbÛ:Õ3à{9ÇAàK€¸÷l…Ý6#«ÜõÛ™‚cÝÏBà¤Î‹àر8Z!]Ç ß8ÝVÅWàSÉßðܰàm|âl£Þ(XÄÂà¨àà ®à”à»à'ÂȬà:Ô̻ۓ21ùÜÈà àBàjÌà+ß‚ŽUQ6Èø {[\àE èm£à$  ÿÒÛà´ ?ÄW“¼v àBàR<àà7ݨà°*ŽŠß^óÓE×Pn•H«àƒ”YÀ ¤ßÓr7Ô à×#jðà˜F3ÐøÞ>R ·Ðà>Ý ¦×åàEëà~à); Û à°àw \WàFrÒÁÙBà“Ó¼ÑÛàü3ÅÆÆÛ(ëSõÝÛ*e‚RÞÌÉ„ÇCßfà6ÔÌ4á¥&àêÞ¬ˆ:1™ÈC¢™Ë£Ýmª‘ï“ÊŸ‡«ÑðßБÛÒà¡àmє߿à.ÉÛø 7XCáä½fàÒ#rOCÄÍ×–Ò˜7Í;%» ‹ÍÍ %fá9Í0 Ò¯R#×ý0]Û#×ãQámlÿ§2ŶÏX˜ÙzáÁ8)vá€àÍÔzÃßQ‡kÓ$ÚQ‚TÀÙáÛZÞÚQªÝnÜÚQ"¤}áË—ᓘújÚQ$xˆáApܺEþÞq_pÜuáÖWpÜWSná=pÜÀ,«®á›Ù²ÑpÜejáÖ׵ᡪόYØXÙ¤¬ádF½áiš{ÛÕ®‡áUÖ€ˆÒרX‘5cá${.ƒÈȱH!N‹ØÝs±HX}Çáø~?á9ÚáƒéÅ_ßÄá~T»Ådi[O6Tß-Æ_±6#à¢ß¾¥_s*1I—@Õ¨FÞá—¥E1SÈóáâ ›3Îá.Ë[&+öáâ JM“‡«b-@WCˆÄáó0íá=á â+éáD‹[&Gá«=â»¸Ö ¨’©´áâ 0Úµ|âÝ æÞÛá°ØuãQ y™ÔïẗÝâ3 …ßÝd{€!â&â‘WÑ1â¿+ÉPØ8âjxLØZˆ €ýáRAC's׈W™ÎØ&á¨~ßJâ6A„݇Ϩ›uSâÛ¨ÎÐòбá«Þ\Ą̊]““Øh(aâ¼")â¤za⌇â}y (·‘táé”·ÊwâUsâœcâ .·E[âhò–Ï]l·¦ÏgáÐ?Ĉº†·œ†zÚrâj`Pâj }âÛá‹ØÒ¨!7Öá¸t¨XBëÜâj;3âC¨è2žâ×ájÔâgÓG ‹¬®ká“uQ4⇤-WâæÛúá|Y³ßRÙG R®âKÞ»â']ü×iÒ»âr¦â*â„ó8gâ"âíI~kóÕóXߣÛ5¶ªÚ”o—HŒ$âÃáÛ¿ð ‹âBâûm ™áFâÔXg_Ú÷X§ªâóÚÜâ6$ÝŸâ×k*Ÿxƒ÷XÀ1 Ñ÷XÀâßâ\â׊•_Ï÷X-5yâÊâ“e›§àƒ?ax5IhaÂÊËã˜.½Õ5Iþ'ûâ”âG&ñâçQÀjXݸÜëQ_'Ùz‘’ëQ%yßãMZ鯯âÂa Þ4VAÖ±—ß¹˜‚Ë ›YYÔ'ã&€ã&xmBHæâêám)“ ãâš%—ç_@ÐsÔˆK2^нh_sÉâ”â.‹€Ír†hü84ã4$KãÂßt(h˜5¾2½Sãâ·âV±Mã$hÖj^Í«Ûã \ ^Æ^ãÚ0â84 Þ ÉÛp”þâj؈ £/ñ’c–Ô™tãt&`ã‰á±C©wÔÛ¹Q C¾,Çtã%8ããÞ+ĉ £/áDnãìßïáå jã4ÞŠã@zãPž’ãlw…á3'à묊ã2 ¡ßèÜŽãï çÜØÛÕ0œ†yÓŠãb³aàeҨ㤠?ãâ©ÛÞŠÔ/V­Ë©áÞFoÔÐà¯ã9ÖÐßúáPŠ…u·¢³ã@Q“T¥Âã„´áÚ»ãyÜÜ5ãÞÚpn³Ûá%Œ(àUß«žqf¥€zšâhâÈ@v–.àžf&E$tÓ«âxùã£ãlãñËã•ã›’µÚã"⫽Ლƒ•Äãr¬šƒt@c±£m\†æãFÑÐã¶íAZÌm\£¥îãÐãÃ*ý¥Ø/­|ó߸áð¶õãÜ äYýãëã ¿äÂâD‰ ›hÕŽãË…ýà]à‚½ƒ1P¹Õ©Äƒ¤·ß¼áV9G#ä6´Y‡ÆøÏ§âx§ˆ¯âÐn.@ÌUpÜÅà—â ý  äÜÕ]YP½ ÂwQ¨/äÆâÑ4áÐ>ä{ë ä*ÒkDàºâ_$LSÓ½NäŒ@+äh´ðQb™#ä/gðQ‰¥ÏáHRßo`äjŸp!¿$YU“[äs$Y…{m¿Ûá÷—éÛ«ãÔA«xÉÛ¹ÐI¸cሠ‰›6àß?äÏzK°ú½ÑG´sÚ§âlÀâá{ow‚ã„ÒOqäã:.…°j»1YŠÕçâzäàmùá?äìjåÛ‘á3Ü1Y´ “`äBA-Ô˜äâÔc!EÍâW H…äÆâÝ6áMâWÔ¶àãh'°ÞîáÕ¬”Èã¹>,lMÉÆõQþ$—ääBâµa‘‰«ä³¶ŸÓýÚ1×g:_Ø:Y—kùáïá@Áºã¾äƒ¸ŒääÅ ªäÌäÀ­t£ä1â|›àÛ¦ËÓ䜛äÛ-Ú_âräjCÙÿâ¾ä”g¸Ë¯ä€”ÙeÞ~äè䑎™ËãdBDëÚÛá™' µä ädBƽâÖäÓäý¼åöä"â•Ø¬ÙIäÃSëä¶äÝdòä5lÓ¡è_å«Ï•ÅI½äºäÄÆÁâååæ$_äMÔÓ¡ù å åˆd Ž ÀÑåjÇsãâX‹úƒ‰äŸ..½Î¸¼ü^Ÿ.yÒäºä] Ÿkïä>åJ‚(åŸä.åîEزäˆPÁ8åÌ”ßW?ä÷äÁ”æƒ)Â?­ßW8®äÛãIoNøâä¦ãä`â^å£å*å *çä¢:ºåRåä"Ujå åËž¶Ëá}$̓Qå åi…Ùäå}$ä!žäääºälq7å3„6Rµ#þä†*[Hî6Rt4Då}åèäu¹àä_Í~å]T$åÜïáÄ#0:S(Æ“õäEåÛá˜SŒÐMå@Ævƒå$äÓBaÀãåöÿ^‹å3`ÊéÔ‹¾¢åæßÙ¤åÅ¢å·Wt2¤*Gߟ¥UåXä¿å—Åj¾´å$ß_lÄ£å ¶",–å­›ãäºå-Hs¸å¯å§-IØÖ!åÛ 6©5AåÜåS+oÈÛYUYÞLåÖåf-ÕYÊåµåÓkìÛØÛÏå0 Õå9åìå; É1ïä–åË«¯âÉŒæåóåÇå56 Äãåú'1¾•TzìÕÅ]¿ø/åsáCp ~Ëlôx5ÈΤ¹F6³Ò¹å(äLm%Ãæ¥áp<~¥½YY;Yö ætÑ«äÙ@d#+æLmưåÿÔ*…áæh†À¦39¨5¸æ9åÒåÍã~À´å×åc]ÞåÝäÄáo ¤ƒaÏ…åæ°]dã~Ô¤ƒ @å§äúáÞEÏá¸{W®åóåæö…•À@æÈ^A²`æ¥,Jæ–å —« YæNå^Aã2о´ålkæõÏäkæŒ?æçå¤é^ÍÀô‘ì®°Ùžå•FKUæ1âpÍÝnæPéùQmævæ"â¿"Cæ•å æ®Fxåç㑈4hæ æ‚á¬n¿Üå!ŒX8¨:”æ¶azæxîÛ‰ªåŒæs4ÑÀ^æwæÍ ) €Â”æÒžæ¹(”æËÈŠæóåoæu { à æ•’æþãâøp*÷å æÎá†æCp-Ö¥õŽ­áž–Ñ³å¯å·æ\EÃÎÀÜåQ‹–æÏæ‡æir8Ù—æ£{qŽ:–]Yî€N¤ÛZ$qäw ¢@\]YPPemƒZ<8½>ââl$¯(iæ‚åµ–æš>¤Õ©T~k(n=¨î\øþæeq]å&%ÜNV\=¨@QæéÎçÙGrÁ=¨¢y VïáŸ6ær§ã×CÎæ9å~áÛCÔ¡½Ôu-‰Y}àoá,"® çßã,XBÛâˆÔ<ÔÆÛæ(çêôæ¢åvâ*]Lþå{æ*å#ç”A¨¥‚#ç…âËçEúå­Ù俆/¡ª:æ%cêhíX%cë²t¿+çîæŽÉNçúáG,Å\¯òm(žPWç "²¾RçúæEæJ` æäæýÆ' ¯º]QçåçjGYÅRççŸwaçÓægç@f€kç‘9ÃØ)¶I¨$·Æ/çšáÜÏÛ|ç׿§Ü?]çæ¶ãJn…Õ#I¨Z‡1åÇM‘Â1I¨²ç¶æØæ#e¿}çàâš/Í JçÜ6ati3_I™“¾¢åP#{Ü1 ¤¿™ç\â6 ø ïµòæÏ˜çjçþá EAeçhç…žãþà¯çk4å ráã†ç9å]ã¾çi¾Fç£æYT¿ªçBâΪ…à„ªEÀçGç r: ò‘¦ç—·ÊêæPq9çjæòyÑ¿usÕ„orO€O ˜InåÛá0â’˜Z nGͤ4ç ËÛçöæÚvah/P.Mm XÎ..Mõ+íç[.Móß^çÛ/ÌÓ>f+ük·Šµæ+I¨÷4;Â訉•Ã¥çRãU fâñçÓ¨ÕY è‡ç!æGLªñçÒåñv€½#ã b™ðoq’pZ!yÁ è TÈ¾ÆæBâ;MrutP’pWS)èè‚áwfÞ-…Éßèd⯠±ì´ïáô(|±ÿH;è.•Âçeèoæ™^¢ÏŸæ¯ r0Ü2‹B;èÓèåJè ‰Á>èp);èë6.•æ2L àÍXèçþá>/G?õæbè²aþ è¹$fp3ç¡á4z_`EL ¥ÏrèÕç1tñç0ç¨ßo2èzQÕ 4•¦èWÑãèËæ[Õ©æØèÀ>lA¹üçkè^gøžõæÕç&‚t*[lSNèsèƒ)ã]q‘ý•7é™èã=#é(é;Cq‘äJËÓ*èÌòè;æ(䀦‡‰\q‘9J>é çif°!éÚQPèÆè"9þ—Jéjæÿ=TõæÒå®Ï9ÀGéý-Né’ÃXé?é |kééd'CVé9´n°˜qE ÂnçéÉa®âyéÛ$#+o;éc p¤ ^vé9’}ªï’~é|¬0ÓGéÐŒè•è^ èáŽÉ~éÂ'eÇ}éj¨¹Z„_!æñp§ÖGé͵Çã»+ÜIqk˜ª2uæ9å\ä4PC¾þx@ ºoéÓÊÁèLéV ÿA`é¶ãÉ ¹˜èá©EÇ¢Åå6èU5†2Ëét€Åæå¨éÊjÃO0éèÎ$|µªˆƆEØ–é²Ëü×Gé„=çvÂçÃ|òÕ¢åÈé·xÓ×éÂŒIÓÝéjäÍ$C‡ãéBéÀéÞ+׆¦¹ ”.‡é èýQPo²éšé!>Ô¨]éèlxéééÐé3.]æûéâ×´ÓèKéõé)¢æÈé@ ?‘wã×+R¾ñçŸâ–õjê§âæ)ÊÔs|^+ðé8éÔOÂ$éÀáÚ+dd僯Xê„é" ÷éöæä¶Ž¾é§éåéÒ‰mTõæþ†|^OdéYè»ém5\é)êZ:…²i|^G~ê.êMÀ2ÑaåÀ?ynúèš7 êVãÖ‰LÅè2êÖ‰Š5ê…è¥+ˆYHsÜ“§â4ÛIÑÓéa‰Öæ-êÆâ'y—qÛ;xÕªëtˆY_”ÅÈô£ãhŠ„gêêúãÖ‰Eþ¿*èN8UR1ê%<×_®™é‚áSŠÏÙuêàÎW<×è¦ÓTêF·Q_<×+étêÇéJãF°|êU{90ÒÝéy꺓̙Äåqà,êqêT2ëS=YZ pÂñéÇqÞ‘ÁÃÛãý9Õª1êåé(gC¾àÃjêU±ØÉuê GÚ¤ÒåÖ$SÀêçþ›)iêâ]N]Ѷé!æîW0ê[ê›\ZêŽê|“fAl-0§eËêêSê^èÞ+ \£¸Õá)áäé_êCMò‘Oê~*!`ÂÄÊêt+æpàé6ê‡êã%êƒD|NÄŒâÏ|a•ËŒ}¨2‰Ñê §}¨–²üç8çt+×—g¦Xêa3`íéQ/`_§ÍŸfŒy1˜V€ÂŒ÷É[ê>ê×þùU2DZê¿ê>†ÀNhéN¦éjæ>ôêëã%*áª0éÙè7rVÊëúá˜=ò¿êBâZ:°Ï郿M]Ñíê“êÌ ÷…Âê}ê˦§eÄâêج]Z ×Ù×öæÓÉ‘Àq¨?ë¯Uãõêþ%+¸ìÑIë ­ÛdË‘Hë•ã%?ææ–êÎê'j­Ó©¨¿qÝë$äì+´ªTäÀ˜É,pê_ëïXžqxêÊêÇ?)êJ㑨ùD^ë,äÄg!^ë\ä¼3u—´á@¯ñçr뺓2äWërâR@® 'çûg@v6롾#uëyê©@=,N2ê¼@¿Ñê*âb Ã0,NVå“PAËjä^P@å¢åãÅ3eë8çªAëëyqé&ÅyçÇAšæ>L}ë‡ 2ëvÇA#¶Íâ8G“ëQÛâ8Gô„ë—ò$IÀñç`âò$¾ëÃë«ãøAÓË”ëåøA×ËôézáBÕ°ëXäìÔëc•jëÀ‚ÓÐâ-®¢z(ĉ뼵ӡëò˜µ_õæãëEðª_áb„B¯"ëëWÛçÁÅçÄëäþ :ê‘ë‰ÖÚêhâ²BÚ¤MãëFê¥ëòB\­Âæ5ÊžêìÜ%—Ó8åê9åóëRVÈê9å™ëotœKWKì­tWê¼ëm3ÙÚÅçvë›,—úÖÔë›,•<|ëMµœ¨±=ÀëÜëáT\Ã.ãëvÉ7éóëf¥êâÉ!Çë’á0DõŒ%ìD¹œ¨6"®éå R.‰ëýÒÈë‡Îßëµë¥DSÖ>Lóë»[ë.ìa‡-ìu¥ RùQ…¿¢å}ëjÜYì&ì Ë¢¬Ë뫎Gf£V6µ[VYÈ8ìR夨^ëæÁGDìFìB"Pì—Ò »+0«jè$^™Ué3KóëÎÛÕ¿ÆåÃÆÔ\­sì9ì˜hÈúëÎH·Åç}ëó:οìkÜoì|ìøßþë–áÞ"nì9xìôè’ìÔë¼@}Ëìµëb –ÁLK™ë’AçÐÓÅg áªØtstºòå<—st=½åt´Ù±Сë=H§ìQì¡ìd9“¸–­ì$Š À´I’³ìx¯™õ#ºæ­ì¹O-èÇéë œu:†Í èËÔHC\{ìÃèH˜A(I£ìÔè¥ì6R ǃì-9náÈ×u PK-Ò èIAé•çLû?7épì0›èÈÛ§ P~$SÇó8“þÌèì McûÏòjÙÓ8ãÁðìfjYxÙ¥‚¤uØ3[—kí—­ì „Ñ–éŒËuíHaþé^êÌëbЍ ìºåÿg•éÒì¼ë*¨¿­ê•ë‚s±Ó(ÔY'Mç)í!íB2$99'­ìríŸæÆNÍ,¤ë×áþÙå^ëbèÎaHÃÙìȱ·y›aìµëŒO1Ôªæbì} ±Óêd»ªÊLí…ëš(í íQíüƒvÄ£ãs`x gž­Ó.dÒ¨@ê4í[ícíx N›s{ºENêAíEk'À {A¹téÚ•cíºZí8E{›+ú#¤ÜÀëX*²ÚPíMí2®Rê0@ÊÍi‹é5íQígZàÀ¤ìˆ‰ˆíËìwª¼Ú…í’íß«ˆÕÊÍÝež¨»%ÊÍlZií{æˆE)ìícíŸ)À™í‚íJ++Ç‘í®í•‹:}GäzoáWŠËQYWаufç×R !¼Á.èMí'S…¾Ñ4Ä;Fé²çFâxšVHí¤!%QZ ¨§Í4ýë´´åYÊÍ‚8ºìij蚈š™êEšÙí¥ÁÄíðˆš:ê~ሚQëèȾe¦à]èŒì‹PÕöèší.¥‘wC|Èíóìäb†›ýô_7LuCøXçíšáÎÍÖÁ}èóáèHß¾ ì]ãdTs eëEí&g±ÅçîaÉÅèpì&:¨¶ŸC^-‚›÷í,Õ6wN™Wíýu 6áóí‚íõC°í¯å|ì±ÊŸv'ÃB†«â`9êíÑ–cí¿­RBä`9Üfºr'ÃkÉxüí( ɱíæÿ¨lØG&îïéwhovâÆU ç3Çù¯R¼ýA,R¬lxùQ-5îîÆwJîPVÙ;MªÃ! 9(rèâÆJ—˜š&ùé£ âÙí¡VQîîû¡NËédâµ›fî-· +±—Dña’„è èIé§>éRãI&7éèíÃKÈïícíãÚFîäìXí $~7çèî[\è“îÀë÷X)kÏeQ×LŒÀ©¦ç<@í¼ë€Å—*iQ×-5'ÉKîVŽ{ªîOI`ŒîWI¦îûëín :É--c²RcáfI=ig³žúåGî-+7=ìY¹Z$s±áljHeë£îÿªxä-í¸î€¤ç‹ótfxîzâŽxeè„î«k`3h±:{i¨è\®oºéTê\Xo@î‰IÌ&kÇîùÅîÿéÑÂ-=‰è€-¿\ë«îe…cé­á°yë Ö­ Ôî*ë -¢ï.îÕCÙg ©÷ºZEíÞ‡Ñ î‘èST•í£âS_ÁÍíŒìL O8ØUÇîžéøÁä‚­d&î -îÁçoáŽ]Î[ ìèõì‘«äV2MÌCúíþèWDí|ì Ós ìkî¯ȉ z©-+wEèí îÚì%êP€©J‚9ïŸRQïîÝí…zLñæœî—çSïÒî ©Çuéì^ê3n¼îàÕà…ß§ãqÐXHï˜î €iç.ïï«í¨Òî’í;°[Xkãq!;<†Y€çvÿí7 *ïÇ†_ÈãqrÚëïO®Fá*î5¬/ï­±ÓÇûìí`k3ïpéˆÑhÿY“¯ïÇîÖL—ïÝíâ`÷âAïëb‡f"©%Ö­ÍÓMÛMâ#¶s ­ïþ%t—îæ8G'épïCéâwm³Ègá<“CXì¹ïÂP®ßå¹ï6€Àï/ïA5Ó‰2î•Á`}ÄïJC)]·-íbeØðìÃp/éï’íÊ ¤ïÅçÁïÐõáîr‰tÖÕíTBR‚ˆëµëÞ æõî­ï'f°žjåï$qΠÖïÇÁëï…ëÑ Éèï•¶çï©íûï-—ZËØxL¼‹ÅÿÉåšC‡Óãìï_ Ëèïñƒî:å?vA»×±í­ï É–æá^e‘—·íte›]2è¦ef6¥ï¼ë„m¹Wu9©y$•ívë…|xäÕr9©ÈFDy­ïïÇðê8Z9©~$ ðÝïR®ÎÙï\Ëðm+ðn{’î5ðÖï¥|hïä™x2£CÆ© âš ŽŽ‚ïx2ƒâÇïðb„·F‘›užwÉáï¡-ïúãZgŠ—NîSðùË|íèŽJð:öëAí“eʵsWð‰'À©sá¶Š5Û·WðÇ7éoð :ê%íˆ;`ÕtïÑÜm½fêÔySØèïz>ôîjí’í¹JÇ*î͹J5ëðìã|“ïL¬‹ðæêÝîN N×ê¯ \ð5ì 'ðvëz¸ëH/ïÎRð輚Vð*jûÛàŠ- LŒÒá çºdǹ²‰á³ðdÛÿu- ÙXQð¿é ÉJ<îÌäk%óSRéï ­þ2ÕîŒì>]çvRîX/ºÐ›ïŸ _¼Nð2k•ïÀáHÚ/O‹…ë™f^ë%íÓ-rã€í«'&wîéá`ð¨ðÆ?ðAí±kë$Èð¾ðø ®asA©ƒxïÞç%€å‡ðÀë>l)™Ÿî!œ4å!œŠΘ•ëÍ©»ÕðyFSÜÞ)c9‰³›ûð.€0|Ìëк”ð~Ú":êïbJƒh1ão%¸Fð¶ð m4Òáðñuëìðm›Šž@}2ªãão%ÿ”ñbìî æ»½w@}1%'ññyÏåábïÞ>fêî^èµm¿Úˆâã´Á‘¤•|ïìÉêë “­@ñ3ñ1÷l«éÏwÐÏð߇/ïðÚðÙ¨±%¤•zAÀíKñ ñd·½Ìžðyíañ¾ð7Ïdñ£îô;N›·ïÆE©¡0gðñî<ŸÌ¡ápå# î¸ïp`ÔâÌÚðx+&ïð©«[ШðqœîTë{¾ÒÏïp×Ð*ì¾ðpíõî$Ç“oð›ð lŠíKpUæŽñÚðv‡†›Ìð²{ÌCãüXOïmëXëð#ÀñÀë™®•íoð‹ ÷Ìñ ¬$3Œö¬Xë( Xñ«ñÍãºñ–ðŒìD0:e÷Ë´ñˆfcð%íGs™Ðbïs%ÊvÃñ…ë^s9­YñÑs»¨£é‡ Ê›jëI#eíÁãÖñtvšð&®Gºçt)fÝ“n‰ãøP?³èð)ŠŒ<íßã taA-&æ¬Z¬êñßä£bätÓ)õæ™ëÈ]j¼³¯êñ\BOñ´ñä ”ÍñÒñeB•ðìÔë"4Ôiëë?@”sñÌëÙAXæ«ñøñC§¯™¿)ŽŠ¼Uìêœõ/¤ënëã.tÐ]ì­ëÀŽv‘ò¼ñ@ìñ¥ëZvòã,I*ÍÌë¹6Ä%ò äðu4öë"ò}Ð4ð´ì@LA±JaüñQ Ê4úï¬ñ© Ê»ñøñÙ$IòðÒñŸ­åJòtc­Ì ò2<ñ9Ì&ò~›mIË´ñÍ Ú˜’ðjsXÔ~ñOò;{®wìÎñ¾ þºô^rëÛ(A»òøñ»Ñ ëNò¼ñ“’èÊíñøzE3òññ0}¯¼iò¬ñDÉ7ò (/±6ñFwÜßr-cá§±ðTë[DEòÆî-c꙼ð ò=ì¯w½® ìüñé#àðì|Vò)òøñ÷1²$ñ3È\"ä| ØW·îbìVÝR‹ç…xy^šò òjNù¬üñ|8±çúðøñ9$ˆä")üNñ¿ñµë+=­ˆò¿(Ô|mòÔëòy–ÛåŒðǹi™ëdÐ%åŒÈSJñìÈëx+9äÖä6û5f‡FòͶ‘òÁò’íš+ÌRjèÒí™Dô¼Ò_rëýPÎ×8òP^Ûí’òDaT<ðå¡ëÖUõРëšìA,ÌíAïn륗1Òïüy_¦æëØòù‡¸ò?òÝÁ²àûò/ñÇzÜׄòÂòÉ¥3ÐvëÆ0Á§ðZØ í÷òƒìãwêóò_Áîkìbì¿{=è÷òØò<•gñ§éëï ñxÌ)óB¬7éóhYÌóäò[cvñøòu‘ñìÆåàòQ¬=ó]ðÏΆ›Äëφ›‘ë„4póó°5:óüò(–C¬;óš:dñÐòIºHó-ó†ë«Fó·gRóØò¨ Òkîæbá ó är}ÌÌBòÉòmç(óó¸% óâñV¤„îÚì*0Åêuò6 €xþðJ‡_¦ö]Fóð]»ïõêâ03óëæ Úæ½ð…ë‘d´x±î«FóÒ•óŠMâaãÑi#íì‡óSÔtkÝìóŽX?©%óÌ<8ò O±ac{ìàò v¯›Aí#ª ¤í5ì“éÏ0ó9ì ˆ«ðJßï¤ó ƒ1˸-Z޵ædÄëú¢Å$ñ¬7þÏ™­b3³ßðì ;ßïj@[±÷ìóB*_ßòÊóƒ@/Ç ëó–Ìp[¤è-æVhà—â¶‚ßóµëÅ ³1ìÈëèóˆJFòØó»Brè©óØójáeóbìF½êŠóÑ‹1r„»ó’íš)Mîó?4Õ‚óä핃‡5€t2˜°Ã«ƒåëóòH¬¾®?y ô÷ž­×ít5T­ Ï?A ›]þóûóºߊHKN?ª{:êÿíÌÊ€½çòf¹9pKæó…놣óíÿó¤°è'¯T¼‡* °DgICÎ ôÎÆöØôQ‚&˜ÝÖ?6 Îò?Œí,óXë÷™OÏëàó&×òúóÍ-ë JaóÙ4¥¹i¡ëß7¼`ó?óÊóÞ8Š«oð*ì„ûËšìêôxó?j>Lys|)2­!ôÍéCX1íW x$}vô5çpôGðW =ô$Äóò*9/±:W ’ (ôKî $áB!ôH’± ò2Câ‘ôËì 9;,Žô(gúñ­ívôÕu±cX°VƒôúóH¦z6òhñ’í ©Ð€d©“PªžÝíÍZÚÑñHÅ…ªžÒíÒE±²ƒ %„!òjë1;íAüëªs/ ìƒóöØó¸ 9 ¾ôò‘>Ê4ò.AZÜ(\’ôÈ,­ä°ó¥ë ßÀÓïšôÝÁ h@$zT™8ŲÊSïèíZ:MÄzñÎî|eÚô›îHúAômôQÔS¬gäí<À¹Ú}ðîô\¤ôCð m$ò›îvôqÚLô‰íÍûÍ¿QÆ8Sïÿí(à+ó•èÇ»¾îóßÿ+8«ÎNênëQ¼"tó-ôâ>¾îÛô8«Vg<³ áΚô®˜eëRî͈?Ï–(õaÂ*õ îôèYLoñ-üNdÐ4ôÀë¸ëƒõ¶‘hµ§ñÍôš‘°á…fÑ74h „ÃóMôrâÝ ¹éA¸‚€7 Ÿ]Qõf¢ú®’fBÐ èi’S‰í}’Fr™ôyŒ—$ _àŠ’N£šŽbõFõ2ïlª’oÒ¡áNEÚûU#9¢0yŒ¦–×ôJã“¿Oõl©‡PÅô²æ6Oõ30ô´,z× ’®…“æÒ9õ¥·ã9ÇIôPZ9AœlõÈ@Ý9@õq ¥³›õx§H´æª%÷Ý#ðå ÓluôXõ ¾³ ‡TZ;l#„«õx‰—õv[•–ßìyêã•=jù5-$.yÇ–ék°ûôUìéO˜r8,PõÑ'pó8çñOñÔ3zIkO,y©È,ô‹æ±õû5KjîµõÉÖÅ$=y©C¢„ÝôXõíðôKõb' ¿õx%P•!«)PÄðõsõ±‡ÐõZæf‰H0¾QôÒõá[ãíbè@`S:•§ùõh ÑîáÖívèÖî-þ;bE.;°óè7P½ àËPõ\*TôáõÒõÑ’Jõ è2˜1r±œõÔ‘‘Óæõt+UkõYt+Bëí˜õo˜„nÜbhÙ2lwî”õóÎeõé%WòûkN›2èGNØõï­áGNо0Mli¡»õNòXõS²Ñì¼ï$ö͈Úó©ô…õþ%Áß;ô^=éñbP÷ öšÔ9r ’lõ²B*æ¼õö$]Æ}ð¥Cë¦ê繪kéô˜õ5Ý÷ô¥ô‚iDÞòRòcZ{.öY2™`tƒcZ yö—F•ÈÜïÃõ‹¢Îbö£™çì ïiö¢K;ڟ溙_°ó]ãÑ™Jê•èp‹'Ä î{öZJÁ®[ö¿ú‰ÖSPEbGöés‹+y³ñ6öLku,ô) kåõâ kkz è®ÿãlϱõu «`ÿòÊê½Õ9›õ’öÕÀ¶öLõ~P*_ÞG‰tHsöâ~‹WÍwö0Áö‡H‰tM3­Œøõ sÍÏÝõmö~Aè=ö×R¡ßæ†P ¦ »ö€Q'óö£öðºoÏ@ŽPJöRã~ nÔâ’P·¨Ý…‰t? Ô“~áÝš‡°óºåÃKÔõôgZ\Öòö!“âÏÀŠgZ„–õóïï»öê÷’gZx‡îdâ ’Ñ™ñ‡ ó,Ú ¦ç>›!v<öÀáÜ9ëõgÍ7[|¡ ¡kZrFrèóöâ:òá™ã7ë\ä`ÌÀ™îâ­›89ìõöà ŽÝaõÍPÚ/èöTêÍPr* ÷zâoZƒO@î=÷#ñ¨–möø¢‰kZ/™ÝömöõœùðúãÔPôI]X¼2¹Cöáö…õ|kKópöSœ|èäï³ hÔŸ‚1cdZSï*÷#HEç5õ÷Vˆ¢æ£âr ²ö£ö¤ ò[õ?Ã4ñêôœ,âú1c¯xo’÷öÍòæ»Ññbö^¢É‚Œt°0÷~÷%IL÷èH4 ø$rZ¿ÙÍ.rZÔBåõ÷±}ÉÎX÷œ9СÁ^rZ„8ؼ÷°åE[öÌ óp÷&_éóHÞë”ö ÷˜‚K}÷»öµl÷öé1Cõ8÷¤'‰ Ýð©áØ 2$†PÃÇìñgáØ Kî¸ï/kãÐUbö‡88ÏwãÎ "/‘÷‘èÎ   5ööU`þâ­í»ö Ìàî‚åŸsÊ$eësáŸsú»œg°;Έ÷)—ˆâw^ïéöƒ@Ø¡M÷{õ]êõêÿŸ¥é¢ö×”ÛñF÷ 8Åæ÷T‹iÉ~ö¥ü½^wáð6kZõíô‚‡@š 4÷,—qÞ¦ö(öÔ9êhõ0 H×÷÷ö‘b¬œ“ µ¶†Vã0ÜS÷’ôø_æ¯0'9Ž£¾÷­›ÌâX÷ÍP˜¿@‘øØ#Ë”Nã¡‡ÊæCé5ÖÏ8ìö9ªé oøÒ3tòT÷þ÷ô‰ˆvâ\Qï´õÎêî xòÜ÷2?À㲃 ¥‹õ·öPŠÈȺ²’;'ÙêÉípñ¤¡…ö¹òø=+DøNäqÓ[õ{šÞý° {_ø¼á:¡=ѳþ÷åTûõÈkQyX–ckQmË‘gø¹ojîjäE¡˜È’òÐ,›¾‹ðUøyOœÍMø’ î·ƒøé°Pö® ¥öõï ©Á`ñüôU×ÀXºöø»sIø·÷û~k›ï,¡æ¡´ô˜øo¾†ó›â®@[ü› Dç[ø´ìF}Ïš©øšJ•£Qø wx…¦õ˜ø`µ#õ±õ€”íãò…õ7Æ«òÓSÑ×cø† žöoõ¦z=ê¡ø +Æ/ø2êÓ,!Åß÷s¡vö÷©H°Ä¯øó ¸yÆéúðþ÷µ®Ûøþèc¤Ÿ’øí÷ñ¤ï÷Tï»™&NØø³UþºÁ㙡F(ôõ”ø9ºF²í÷Gûωã÷¥ê›÷6ö\ƒž‚¬÷ø¦äö«ö¯Ðþø E²/øŽöµ¡øð™ù¤C”çð÷K&:ŒGñƒ´&Áô÷-HôLöUøoq…i÷<Üæ¡ò`§ÄòøÅQ¶}0÷UøróøØ÷ØXœéù‚UYâèLö¥j¨Ò*÷—AšÑX÷Qצð.ùd*máãøßãX›X_°d*ß¿Öé2ù’¼Zß¼êë/ùœ”^áiÛHùëÝóÜ÷>Rìô>øHù@[È÷@¾/ùñ’ÞEøëÜ,‘FðÞ[ùuBE»|ø ¢Þ7ó…õQ:‹M¾ö˜õQ:ÙùA ˜|[ëÛõ%•ôVåªÌ‰ê¹ÑHùc|é<ùù Èo<ì äi”Æ’‚ùnù|]ÒÛ¯E=Õ}â÷Ïø"¢x½^ùv |ðå,äWRœôDö/ù«¥„Ѻâ÷ªÞ†ù½×‰ùŽÓýÈ ˜’xùªI à¸ýÈ…< ù6ö½IÇËPýÈ^?Áà¶âHù[ñbøùtÃoïÀõÐA×ç¢õ"øj%"{}–ùdäBã|¨ùQXÓ÷•ùXäV^N;ò·öÐÜjãøÐÜ-ù“øjùL0§ùÍù;ÕÂâŠ(Àù¡ùU¯Èù&ìŽ ’+ŽHùvˆœù›Úêù°ýö ì¸4^ÁˆÅù‘d¹ùVù/ù1ëßRæêùyÜ»ùù/yŸö.å?ù¾÷…:÷nùoU$Ýã%°%,äß*â}Š$ó±õ5“úU¬RQ³hâ’«ööjø;âúoá°¥Þ2Ÿìã*¼ùâ® Ç(ú®P„÷HùË…ÙÌä-±ÐÞk¨ùÕ ïõEì’áU±pØ}ä(ö¦ ¦§$í·÷‚±Çøú5òlö¨-%˜ê£Fø~ PÔQµHùÐŽÅÖÆèŠ¢îímóRå© àI'ù/ùðkqí¥øÊÃ5ùµõ[cEÚø[cßçzÅùSÞV»ù¡¨- ußíøê&Êé%å×–½SïIú$<úAìþ åõ¨ù1gª³ÑùôˆÜÖŸöX®§*ã}z™'ðøÃ>|êóãØ„ú¯{ú uª¾öó±õ› ­Ó×ó‚uæó ïžú‘?nú¬ø³ŸÙ¥÷ba°uLó[ú&júŠùâ%é°óZô±ú3fe}`â±úq~^ú*le8¶šúÏr ð|øHÜß²ò=ìxQº$~–úÆNRù·÷òc#Ù5Ò«ãTÔ‘ú­úÝ^æøÇõRöøzÊúª ßøõ’=5öåý÷àú¶uõ³”o&€8_ä~öäúu•Ó}úá³pÀÛó.î¥cÙsƒÒyçË‹,í[õòíGùžúÍ“Ÿ–ú)PàõýÒbôžú73‰ˆ¸úË„¨ú™ùô¡ÏlØø&Ä–îòúÿåôú:«÷ãúû7ãÐ ûަãøî¹œú¼õî¹ùÜ‹p.øûÑúD‘À5ÒÜóµ ,×ôÂú4ÝÃŒ1ûEù?Œ´úÐ!Éú–ázhB©/úAcÂé¶ø~Ü07û¦Ìîú–ùÙ O®ù…õà ¿,ûóúàú汓ŸØúÝ罇ú6;Ê¿NåšZOÍ·ú;ûéŽCêKÐX©çÌùÆúkdöçzòóžúKd‰öâñu·ÎÃØu·HÔ¨ørûk|jûûª!e Lõ¼dmû«öEV&hÐóê a¾:¤Z æhpóŸõ[·ÞÁŒõàú‚)½÷÷AV öœ©~²2ï®¶ð¹žú z ·3£;ÏÈÙ6ö8JÒiùàúàžû˜û,Ÿ õMä¿ø‰r»ª©Ÿh€íת©ÕYðùýS+ö¨·ûËS”f?ùxù™…¬ûŸõ™ÇËûñÍù\»8õ†öµÐní:œ\ñ¸qø^±ÓÆûøúÙ— m ÃTN›Ö´²©ä!mÞÔ÷‚M)Õ¿×6öHkÏÚŸûªë ?ù3»ÂålKû¥³u]ù ø±õ:l‘À#ãßS·ÞôûUr ·Þó¶ÿªMŒüûXûèéûíõûl'õ$äT_¶¯íûmPÔÿûTÛvKû’µéiùñûÍ5ìñøõZÙˆ1íw/‡á=ï.U4âø+¹üR€wùÇûc]¸uÝ'ü¤™Wïuø“>üâûïû\üŠŒ üeõ»±Óˆ®¾©À¢·ü¾ üCü"œŠô1üV sïÑõæûV Mܦûâû:$×Y·üXÆ׭ؾ©XÅè–ùô Ü7éÀûŠ|Ãû^7Eü¼ûö9«l¸âûe/±ÜíYÒo”ùüæûæÁû­ûQ u†áùæûeWücÓâûB7*$ÂôŽñ‘Þú\ü(8ö€òü0!¹{Ž@™-õÃ$©nüâˆü|]MÛÁíüû¼ºáñ öç]àüü²}ÄMá8^ 1/bü®ž`WøfèFâ~ j?ÕFüû¢XØ–Öâûõœ¿¥÷^N°óóö´ü¯Tü‚t6=4‘ç‡ {åb˜Á©ØiüÂüXI÷§üYøÇ}æøóö­ ž‰höœÏ ÈÆ9õ’Ê}3÷PüÂü9GXÙ‚ø»üésùºü½:P'ÛüLü…õ1wîÜü½:r­¯ñjø³K±ãœü»ü|Yæø öB%÷fÝì ›†üÆö)ŠÍø¬Á”Ý}.½:BÒеÊülùüÍü]í°ÝÍȽ:Š‘÷öMXU¼ûûÃ’%´[Ú±õPòã•üý½‡¾„ºž-تözõ¥M Ð÷¥®6%ÞLïqø¹z¢µ ×Õük ‚P°ž-›’Èü^õ=vˆ\±ž-¬îïœ9®¨íøùÔ©.,+·"øs&ó«Â¹»ü—\޼,ýFýˆ8ëü¥ápÃ’¼Óåü ÷¾Æ¼Ô©ö¨£äýüt¼°¹lõéÃVUÝû¼zTmùœ½¬ßTý¼ 4è‰×Õü¹À¾ß«Ôåü…|ýb·%ýŠé·üõ((þ¶˜¥âÝ´×Õü%q¦4÷Ú eÂÅúÑâ*Æõ/Œë·ö±ðÅ4ü¼ZO5=ý€ò%ý+¶iÞCU¼Z wóØøzòŠìeûƒùkUønS-˜Ê«1öæõ[D°Þøô»üèóîH¼û2Ʋü™ý˜õïÇjY"ë=öã…'ñéökò£ýø÷¡µ€úÍù—xËûÄZhZ)÷µõ!ÉôÅáûÄZ(´˜oåü#&Ëý%ýq•“Úô³ÄZnGWý«ýý¸vÊìíû+;€±ñ…“•ýñôò´G6úªÔ¶®4+˜Ü¤ýÜý,—Ú{¸Üý²È]Õ±õ‚ç‚ýbÚ´Ø#¼‚Æ÷…F茾 ª¸û,BÄaöª’LÏýpýþ},XìÊû6öƒ%ðêìæù—ùǼõ×yÙPý¥vˆeä˜þ_4ÓýÒê"ø}f\ì þÃx¶Aýñý‰fë–ù¬,c¦.\ù˜Qþ‘öªr4÷-=ÁæéýMO:êxùÆ0™< ·\ù*‰ÏËåöQ¾¬¥ iÍZ6&‰ïÍ÷{†ð1þ}Ò9ÒøDDå¶ÍZù¢‡÷*þ"¢Þý>ýVΜVÍZ)AþŸû^Î!ë¾ýb\}phýùýìöüñýŽeÈ¥÷“V÷…Rþ5VHâlEŽA4îñIþnþjþUÓgùÚ ºŽA)䈶<÷\¾?Âø‘¡å÷ùý´^Xþ‡ü‹þ…«Á÷YøNÐöÚøýö©Þ”åû…õy~Œ4þäáà×ïüÁ:„‡÷Bø.Äåܹ þ}Û,ó@ú©å—aý|þ6ô9ª|üý©JH*ðµõ}þñ•þV·:è/-ö´UÕý¼þ ïö~X€ÇØü/-Ñ?n¼šá/-RNþÔý þ2ážþ½øþ?›ÑþÉü˜õXÒŽøÎþ™þ6)•í*þ¼÷|Ûþ]’Íþ–òƒùVÍ3þèü|4·qþ¾€ÛÒûTñ`êÇø*þÖãî›ö¯V”ÊDKïþË-ïIþû*ù¶íý?3”r’Öïþ›’˜øõJmþ’ñùý/0̹Ùˆ |Îcði÷vµøëú³ÝæÿöþiÚÿØãþŸö¾*Èý’ÅþÒþ#ÿûœÖþgƒ¯õ úF‚Çø¾÷Üz!ÿõ'ÿN¾èÙ±õšù¿ÿxQþ¦Ðý*myuó÷¥ƒžØ<Ù8ÿõH¯Æè¹/ª.ZÚÇÅü¯,ÂåÕ.ÿÐA˜A¬¹/ª]’Á¹•¹#ÿÅHÿþ³K.Dÿÿ¶´ù ü†3æèŸþK&WSŽóö?W0ŠõçÆÙªbÿÜ÷÷Ö˜‘ù8ÿá4ìŽþùZ pÿ·÷æòëjÿÝÑ€õ–ùÙjÇðX÷h²h9ö¼8ˆÿeö!2âr’ðOËÈÛâý‚È/ƒØÆþã1ùÖþ±q¼qº[l@d¼“ÿG¯€õ÷öB‘Lìàïö”ê6óÚþ¥»Ó­K¿è„Ã:þæùú‡*Óþ“ÿ¡•ÔØ÷À´¾’sôt7ýxÒÁb¹œ·ÿmábêFl±Sðû¾ÿÛ—Ì[õ.õh »ZªZžV¹¨ÿÊ͹ÿBøx/òUo¾ÿ~`m}ZªnÎöò×ù¬ÿ‡ ó ÿZª#ó³(°Ã‘h ïvÒ8ÒNð#X#Aÿ¾ÿA ,¶ÿE¼ó4ìö“23ôßý—2ýó õëÝŸ-Ñÿ¢4½ÛýØ“ÿ oÞÿ†øËްèe÷¿,†¾½Úw@P‹Ö%úÎ#‡5tùTn½¿þëÞûÚþ¾ÿGåÞÿaûМ™ÿeªšêú«ÿ¾ÿF¹B`ûÁzŸî¼ßóÚþ¾ÿÉ q½ïƒŽ}êú ûG ¾ú6öOþ:Y#¢’ÿ‹ª.ß–é5åÞ'½ÿjßɳ½¥áÂ4  ªö×á@äßó=¨£/™¼´.M,™¼ÕFxjaõq‘xß2új"9"nrÑ´|^•8rÑÕQ/Ðщ½g+™dãØü±YÆŠ‰½QŸÝ„Õû äáµ Iœsá{^%âáºX­rBä:ùjËø«îéb×âŽe.ßó*©UôýJÝ„ôJlñ‘.ß–éL¼^ØÞå ðÖóŸþÅÀÚ›âj |ÁPÙØÞhâE©ïß6ðšñW ò‚ã¹îŽ ïßzá¹îlƒØªeV{ø›âÕ4ªxüÕk{Nð-cøæÕûÑâZûäÙgõ}ìŸþéšçxüÕÂaUREë@[êšÄÿ窤äÙ'4®T[ëÙ48 CùrôðÝùžj÷5s »úd©¹÷ä#Õôª8ÍâbõìpöÁŸ‹îóáÁÿòËáy©ËÅçvâûö\­¤¯êVÂòÈï íTä —uø¸~á;øs`ÄïÌø¾ÿ÷þEòôZkà(òúNlNšòËøŒüs`ù¸Zr»úÊÆayõÊêBlÏbþyçwv©Ôâªß ÊÿïuãõrNå'[ŽìïöЃþ<ù³ª­r˜÷تûÿEú’W+«¦o þ>ý«C&ªýþ–ÌrEú1 . þþø}z ô1 kAþ2êNlø_úü'[$`Åã+«àOàÊêHRàSPÿû9L³½¥áS[V`òãY«&þáà ûÿ¾üç:gÑ숯-”.£¸üT;‹»ú ´&ÁýÊê;ÈHþ­á;…:!ê´{¬Åàïoá{ æ´ ¢U*Îìl¼æpæÕæÜ°þEÿû±t¬Å¦þÕ4™Oësá½ã×–éèÏ‹eëVåèš=øŽý#ùaIÜËøÃ &5²âñà Œ@×ó„Á™¯#Ç øõ"¬=Ìôɘ¥Ëø-¬HÁýÎêÜ: Áøád;à.¤¯vzK¢×pö+΋¢TäÊ-6€²ør⊊ µÌÚ 0ŽÕ£ÊêžÔBPør➦Üö½Ð[ r*)þ&ìe8Ô)þX䃬³f2ú ’ÅPørâòqt¶nš¢5¬Ìô ú@½¢]¬½áF!$ºg§Šü)Ò2ê0:ÁýÎê0Tä6Ît¶2ú Xt× è?«.ÙŠý„íëºå-¢—æ´.]‘êÊÿjj Ùÿ¸æÕ "˜Õòã•Ù;EÁÐÓ-ÒéÚþ#&:º×㥭1²ÿ=ìÞB‚yð¼áÞt@Fùãÿ#Ù'E¤Ð¸„«Z‡²Ûü¶!UÅãe­:ôÌÚj£ò½ôñ46®ººåq­>çôý o‡5Õû ä4b!òkï `õØü‡ à˜ºý=ìåÿ˜réñåÿLèfä‡ Ò3ùÕÿñ!übùÖNš‚Áûùµ¥ÙTä5](ºýyçÄI˜r-¼>Fp¶²Û# OöôýÐ|cUÙõÏ­‰ýœàj,$øn”äjd$Üõþú[yã²ÿyçjOùÕ‚äjÌ…¢Øºådänàòë«ãT`ýMäTŠ®ý$äæk ´á¶¯ßbù Y©àÙÿñ–1¥GúÕ:TÄýLú›¾Þ[ï(UšËù×ò’á€cNOàÊêWø ²ÿ=ìn/qÍfän/±öú(/FùØ7Ü«ã-˨åõ`⬽Ýxü´¾]õëbù–«‘;ìýKù–«ãùØ7Ü–á„cDǽÓûâéñKžÜ«ã´lSÚeÛj"0ÆG×ã¼p '²`âr®À݋۴öWk -¼ ØpHÈï’Æ.2mÆ`âNc ?ùsá‚b?ùºå{äáý¦æC&ôn.ñ ä¿‘Ôxü´¿Ô(?ùâm§þ uåÐÇ A<ú´n„"/ësáyOí7.ñRåyO?lÿAì ¨K•íã +bÏõ‡¯°•í`â£&g»±ã£&v»úüøD¶ïûg;˜¹AûäÓEú’W{]Lÿ7Ü–áÎ^‹ »±ã)ï ìxû¯4’ÿ ¢l ^É–éÃDÆáÜ–áYï™òtÿûLܲŠýPg!ëºåü{ssžªZô쌌²Êÿj{2Ø]2újìõ.Y, žªxûñ^w»gRw•É’òÆ;‡ñÒ¾æ´X¾Ê­þÕξ…æ¡úÿå#³Õ;ù ‘Ô~ãüNgôxû€2P?ùã€29æaõ€2ãw»Œ‰Ð™úáãÕcÒõM_ü&ìÔI®Z4å|”22újo4Ó.ÜÜó¼&.ÛôýнäWtÿû5[:Äï/.P?ù8ç/.׹ܫãßIú ¤øåŸ)Á ‰ÔpöæÜ:é;¦xû«íŽÀ@¼Ð[‰÷¹7Ü–ám_ÁCxüÕ´ܲ@½Q:Û „[æëÜó |7 ¶÷’WQ:/å2¸8çälóÉ ìxûr`Õ¤øåÖ°*ÑÑoS»ÈØü¼y‡_¶÷¥áè°èÓþ=ìž_Ý,¤øhâsÛÜÜóÁ‰Ô¸á€ïssø¸xû›šhòâñåËøj(=*àʆÕ;¬úkà†æ¼ôñ/‰Ô™úἺ•ÉØüÑ«6ÖxüÕÀ7eë,ä’ä‹AÜ«ãÿ_A4†ýrâO®Ý,m¤`âp =Èûj_ê†ýëï[å6JÜü2=eÕûÑâz •BåÕû äL`…Ì ìãÈ qëL`…Ì ì1@Ü€"`À3r̈!#‡6@ÌÁoÔ(¢ÅÁ„3dÈÀñ0âDƒQrü¨p sH¤Â"Svd91gÞ¨x‘èJ‰ sЀQr¦Ê§;‘*1´éÄ¡5uÆÐ*•†Œ@iȼj«Í€»Öð”lÖ›9"TjcF «4ÏÔ™vn 1`Bj*Ë[IŠ%ü§@¾‡gÀ<îã¼\ý&t8Ø­Þ­û&¬1”³I7–Z‡ m-Äš¥ c;ž½;sBœ4fàÅ(PõÞÚRm †Ýuñ•¾s¼Þª;noäsmà¸aZhjèØÞñRhÏ¿”¸”ªkóÑ;׈}©ŒUÏ4hôz—0ÈðW ð9Åxh´TH3À¦ŸY)øß‚õ`aFàR¸(ßjnÅÔ…oAÕ×R?ÙÓƒêe¨àd®ß|ªex"n?Éà!r` 5¼¶#qô™ø¢ "$c6Ù{1PIKÊçd} IFåiQT£—rÑÀPp[f—1wb ’å äiîw¦b.„ÛLF8çš U%mh>éR`lâù¡ †Cwýye a–e^Õ➉’†Ž^šf¤­‰¤À yÊ–™˜8MÊ)œžª™¨Š9Eª|×%¥ 9È@ c-yˆ+¨˜7Ð[ 2”ú(TáįÀFôk°@bi‘ª³Ò¶l¢>Ùàg²Vª§¡˜ÛÆ)¸E$ƒžŸN8%A*­¯Ñ©›¤ª¬¸kæÚÓ¹N©Ë®«P*¥À |ok°Í+m½c+ $eƒÂ Š™C®0![êÅ ¬[ߺ÷uŒnœN¸nH°YŒñq¶º´îme‹òË ®;Ð]Ð^'&°§®kQ£'cˆ){ë*E´wÏ,²Ê[ýë±¥M¿|¢®ÚÁdsaVçLÕxó˜2º‚u×Ö%ÂLö”`÷×Ï,ÒÆú~¨öÕæÀ”Íêጴ ÚQyr«r´7ÓEvtHtóÝ´Ú9#´(§i+näbÂäöeÔ{ß !Q¾ïãWov8ÕEÞsŸ¢O¤«DZ-f¼›ÏÖùÈbÕ \°®;î+Yyœ¯K¦€0ÐÍ;mÅC†tk0ì>|a?¯·’hÛT+Ù8å[;Ä7­[?£¾Ñ¶H+§êò(«ž~÷­óØüñÞFÉiöm5F É[ùuöŠ™W0rÅ ï;ËÚßR¨tÀçÌO€_1ן iHñÛ–ód†ƒûä{¦:ìÒ1ü‰l>AÖC¦÷¸MoäñßL®£¿œ)¥€+,• G&xÝot©»Z¬”—­ºiÓÎ{;9µk\’y ÏHŸNH2 siJõ@:a'9”Ïí®˜¼ƒÅ 2qBŸK²?™°‹'bc]dh>XŠi ÐuÄ5¦ ƒå£Íñ臜ˆóãÆ,T™ùuQA!™R£L8¶8ŠŒ[¬ã#Ùà |Š$$§ªC2ÑxpåÆ&IÁù®> Ð]LɼW^±M¬¬ãØ ¹])`Fi¼'YF @Æ)€^Œå'[9È86WæA£QR9ÌñPÉqEBT5AM.Ú2™¼ 0ËÂ/^ÂKœ3YˆD*¦ q†…6Af—¢DZï›ó4–ŽìùM®ÑXbùe:ÂNºd(@ç[Ôi—vzREtä2!éIF T> MdpÖB©0Òf{\‹ÑØO|†10QègNeèuS“ß ©³"z%y†Ô":2¦QjÈI$AS¥¿3(’rQe´u™?hÒžp™u)'Â`~ç¨-V bÕÇùÇ‹ŽNߢÆà€…;EÕ ,;Mh]‰§kìàÁÆÚ¤rrÒ5ù äý×î ”'„$£¦äѽ†lƒÁa”'=zÏ&^Q:b¢)íêí%ˆ =ÂØi6µ²KHgKÄ×—$¯A45ÊT§¯Œö,Š«l ØJ¾äb€y 2+@bEO·]§‡¥±ÜºUµ‰[oÏX,44\^eW$VÀR³²ä‰¡1ûvXÍ ˆ°˜Vµ‰] 1g·%¨waåÜ@7ˆ¬ÉBÚ܆:íTÁ‰muÛ—°ñ¯¥¥t’&à¦W¸½KXÐËÇ9±Ö%“{®DÔ{ßú˜°‘pœ°J'2žõJ®FÉ„7ø‰'2-ÃkT wxv:Ò°Q8œâÄHƤíî„^âÉb2øJ4fíO`°àVîRºžÌí}xûbFǰÕñ¸ŒE1‡høª.f-bîdâG‘÷%k)ðqß2Qé®I Xò{G$ˆ^ù9A6³E¤æ ×¶Äá›'e-/ &äa2koƒáÔ’9b½½Í¦ö¬šþBä/:ªóQ¯û’éÜ¥Îβtë'LÏØÅ)h=ý<+t^³…5òž1k/.; QSmççjº²,4©mBé$CÓXFp‡ï¤œ43ø¨q®¬ObèêüÁzÊ>á,­3-lÖ«¹À†3¨­ :Ød›>x¦TCœ âî†þñ§…b7YÄØ¥â0ks…mñÊï³/‘ÎúÑg·›;€V÷B·-Ý$ÍE#«0+»17Á»ŽòfÏ_ô¬r—õ/!É´ƒmßqaÛ?æø°þò'¼®¤Sb&”k›Ý*gˆ/E’Έ,e¸aBÌHžlñ…ëb÷14Êá+ñ3ΗæâµùÀêƒé´üäñQzÎg&hUøêtBVhÉÍä!KL7®\ƒìÔ¬.²H§ÍØo.@diGGVÇX‘¥¦¤e}Ô'Xœ°>K|$lá÷ÚËîõ¼‡ß–ú„tדHCçmw‰îÞÊÁ眄U/7Ö /¨ª´ºcS?cÅ{œ\<ÿXõ$?@“WÞô^ß(×´¼¯å%p×üÜ=9BÉVÍï¬UêíöU¯©5\‡¸éU¤µP^—_6¯žkù¶#)tˆ¿Ü©‚’Žá@‡ÉÝþkðã{>?Íã¯)¶[¹{~•{Ëù­?ИžÅ &ì®tlÖ‹å­ä±¤1±~¿w~^W@µ<–Gi¡ò[ìSzø·x#¤GÆ£j¨»ó}b$b9·D–AÈv·(ò€¥S§700z@uB^—+·Á)(HØvôvc3by'yƒ¥E寀Ò1nìsUË—‚ã‘P5‡Õ—u“âpCg°§ ·1$Q!uÉ¢<Ö…‚.¡h\f…‰t†$‘ƒX…ìqA¢$3xLˆV†¸ÁY-¸„Rˆaùb‡'‚!u¨†_È&>ô„à†oÀZH†Rz¶ˆ"؇i2nb¸n׈H‚ñFp_(‹%†DȈkxRó†;õlRH‰ˆ…$]Ev8Š@be±è^‚ AT ør—¸†Z’¥W±Jt‹WH†}¾„Œ³ø…½(‹œXŒ`‹¯§ŒRHOn(hkøsbŠ —rk˜qᘄlWZÊÁ82ˆŽ)Ñ_ês0z¨vEø…4Ãó8†‘(…ÞYùÈ5À÷Š~ÕŽ}g€RX,½—¡(‚IZô† ÙŠlå}F¦Ž#/ Y€ipiÖ‚eU,ºBgIY?ñzÓ‘3±C¶Uw>’¡’øÆj›"2ò±Zì¡ [å¥h:)MãHt=Y")Ê5.C¡‘Ö‚+Ù?™’áWkÃRäG“µ¤'ÁhÈh“¥’Á)•±CYx ”6á•?¡•VV9dY?q'e•WrGYÉV-‡Œ+9Bµ“5•T@*ò”ö‹µç–Q¹„w©7UXnjÉ+¨qz“zÓ\~©vÕpK!–­™Œ2_¤×B€‰”úcåöŒ&yAžYGv© s6™ d·–d€3|ô¨ìa^Ñ–ŸY𰉿ò<™d@¢$$å‹e|ù&”@°%•}Ïñš»%‡G}py'#QLÙ²Cjy'C¶*3q;¶u'aFž½Â”ÖA ‘Sß•Ö asã›Ê§›ôÙKêIƒÂ&Îi”§ÒAí 9äWýCœz”°³ŸÇTƒã2B ±;pâYú÷ ê(j< Š{œaBóéIùæ'¬òaú$VØ´ái,'Éb·³ƒ&ªðù›Z¡kZ Ê#á $›Ò)*J¡'rNÆbb5Zð‚$’¢‡¸¡»É}E¶ý9!×CV£ÛéLñ£uYšGš*œ!pñd¤°9T†roX™¦ÌØe–8,®ÑOæ^ÀÂŒ™¦Kaev:¡WJ'x¡,Fm7¦Â×eß±¢ž4œe!ö§GŠ¢Ù§!8 ã±.ˆZ¤ ›`tŽšŒû¸›T`ƒ*rþ9ªwÁhwú§œê˜æA©‡>þy‘9rÊY”⡪͛®Ñ{ªªš”"k¯`|›nBÁš‹‹‚@$:ê ì¡‚$/´WEbª Žù/ŸB+¨8­:³O°új—!@¹rFx­¡D¡ÚÚ>Ô­–].¡ C ðLä8¯=Zðz;j9D­uÔ«ÓZiöj§þš­KoÅj¥Õ2!¢çZig/¶%zñ2±CiŒ24Ï'Hq¨­[¡÷j;¯8áZ+Q;­8ÁM#Kš;ª­Hr1 ‡²j®%÷tÞ$¥&K¬™-²ú¯ñãšò*Öô¬ê ´¿b® £k§±Å‚Vw—¨+;¯Qb?k/%:Dóõ³6Û´t2)Ñ3"Å9)l¢³{¬Óz'ض´ËV¹ä±7;.“B@{'ÿJ)ÐT´.¨­Ã wK+Uóº(ˆQ³%Õ¦Óº­œRž£š+æÑuÚ:û串j£3Û+›‹ —‡$ª°v±‚€¡$ÆZ·eº ø¢L›%»2¸þ¶º€Q­•c/É6qÈr¹{¨ºë´‚-k­­¤Fr|K·bÛ¢":â·Ï8 O›·V[·Ì Ü¡E®ë•B‚É«i=ºk ¼¥Kù‘ºË»ºäÑ’){%мí»"›Xmò€“[˜‰ô ÁMâ8t‚¿ëb¶å+¶«Ë2s¦;z½øÑ™ÿk´BÉ¿7D¼ »“Ad2ƒö²¢Ab KóÙÁÆ2K}š¨ì¼Æƒ%¬¢l[ºÇ«1%‹q¸¡EÙÈÁ»rñÀ¯+—&~rÃá‹ÝòBÊ!ŒÞŠËÌ»"±« @W»r#q޽<ÿ±rvâÅʾ[A”¯uËÊ<ÈÑœ¤“ÜÃ˼ã!åÆÁÜÜ™ïK²ž¼rügʧèË%€ñ¤Ñ »ì¬µù¨,»ýs¯&¼©ë%øá +Œ­1»ÏŠb¯§á3r%õjÊß1Ê 6|ʉŒSb MÏsD—ùü°¹% ÜÉúl 1Â1ÏôÆ:Ç&³dÆäZÍ¿L]O6ÌZŒÇ $€ÌÒÁ½ Q1–Íõ 8s”‘ôÁÐ_Q­ÙœÉ:·HÎ\SÅÌ&§5Í«ÊÑ¿¼ÒÁX }цW…>]»ûK±ÌÇð<ôx3Ì&96Lm¾ˆÑÀ9X ݵ]šÊÑ뜅¥0zÐ T)ñš:ë\\.)Q®,Òb‘<JNùŠR át‰Ø~½ÏÇ×z œ €üÏy½ÀëŒR$aÒ¡DÓºS­žÕœ&qØøÁ4—-Î6}ÓŠˆ±USŒ×æÖÑSÌDÓ*–¶zÒM­gôpç’Ñ?=×ûëAä·ß- »ª8yëÍäx>ÛFÖ嬿4KêÔ¼ê2E±3ȲhÕl€’˵ŽCÂL¯¾UÎÇëíü€s1™wë¾N”šá 8@¢¶ü)Õs> áÔsí-õö¦ÀÒ:èü×0ОØ_:J[žÈÁÝi?Á>°òÝÿrweA=ÈÞA†Mïdöêr5€‰æ>ÑQØåÛîä×iÏí…ôìêÞŸ3¼s[eçäŒåƒN)¹ï,Û QFåw>ö>çnr0ŸÙáŽ,¹ò_qà AÞæê!­;Ø{i&„Ê’! €éo5ÏMïnÞý óuñ,.oíëÔ&šòɾm¿ï¯;"bwÏ™óPOÒˆ+å_ô}i•^ν$$#Ÿâš­õ`—fSŸ6ÌkŒ?aöÃÑý+ZïO¡˜Šôý–ãºó9-‡âHÝ}%f÷ðKõ~ïcGŒ×ôgúò,ã¾]?÷çNí5%ßÊ„ðÅ›õ:¡Œíº4Ï w1¨Ž½¿1žžOV‚oB÷‚­ø ’+v¾Ñ ï¬ /÷t]ô=!$€Õx?‹Õóoåöùu^Êbùº³•„/ÈUY›ò¾®/Ûƒ˜a~Û>ÇÿPañíªîÃޙ7çšHÞR“áílÞä:=‡â£l‹Í)Ëž^¨;™ø¥])Ç r_Þ”Åþjž©Ÿ6Ç_k_-ëٟׅýQ Å@ý,ƒ+z¡x$Á‡:ħ:vǯ^;e`[ã Ȉƒ50ºQª°Ú‹€kaþ}8×φTúëO;Hw°`èF  Swë!d@Égúÿ#mR‰ùu ÎP‡v m“B1ô¸!—Ì}9 Cé¹ 0ò©@Dèj2ðûY@…€An —E:O)(¢à÷‹žÒØ{.ô tr$D =ŠnF¤ òzÂúgP…CLž&¸zB``è|=ð|€8û *ZP% „D†Œ9Â3ØF, !„ëd° ïWG†Ü^ÛU~Ë î)ê`9÷ÚS\†÷!—€FBdŒs° ì”Ix4Za’I+Fã‰B vcB¡ûh…øëÆÂ‹™N`µa¨Br©Â ¾ˆE¼ ƒ½ï .ƒú&+\ ~Ž«ÀÁZˆ K.,”ð>?ýš!)¢Æp]==¯p'-D1P2èhèX~98ÕpnºLh'ÐaT‡Dóô@ã·8ØVbzôPÊÕ¿¢<VÆZ<¦C@C} MÔ™æ "Þ¨è°öW¾9QV@*°0¢úS\$„£ð@·…” ‰¿"¾"nq LL€a¡!C)&V¾ÝÄ ˆºî ´&mAõJR ±‚‹éºÞ&/L" TŠq‰éͦz×IHöÓ͇%ˆ0¯_á(i9 [8Jñ01ê`A•Ø?¦bŽi?™pQø’šxq Y‚Z-½#)ür‡kq‹Q.ÞEœ Z[9  ~±˜¸‘jMM¢(6!¸ãõ ~)-a(Þ ¾HP„Eɇ|·A¢¥Óð–-ÃXá×Á¿y$°‹äH¬‚ˆÇd#•(›§3R ΂ƒ’Ë8U 0îEÁˆ¥CÕ©Œ$/6F‰ÜðcJøZk2rœÄÂîÀeq¶õ¿´(ööaKo”ñvÇçxFúÏDtz@1׉ é˜@Ô s;’‹=:Aº³cã`È‚9qÂg&ÛL^XZúoÀ¼Ü@×]9ƒzd–:ÑIpS9ì’gôü õ§;ÎÌK#¬±A$ î8ÃTàÚ«FÄÁÈÆsâÚÞ =.a…Hx8"µ`S’C’å;ã‚@M4ò9ê*ûôccp hÏêÝrãI1‹J‚Cɳ«4Ï/b¡>ÒŇȳŽ¡8l¤/@¶”-ÓMd5ëŒ_åÈþ˜gä¡‘[r|ÙÇ V¹Ox\ŒÓñ³Ÿes!sáþªEýGAR2ѸUXß„¬’ABË}/ª¨˜÷vʰÇæS@ã‚ÄumfIðþ¤Ï&¼í‰nS'OJ”>åAóœA›&Z(\d¬ŠæEoG;ªXX‚ûÉ‘PH„f‚¢|ŽãáéÉ¿% ÇCÄó[ÔQSt¿)™¹”"i0Qö1SzÇiGr8"0^¤áœtʳÀR-kA¨ zä–‡F(c£xê§AV~ÊÖ 3°$¯¡>¡<K9Ù ›sY€ZHÀ ê@,ãZl PoP¾EoÙ’$+ãÒ1=+Œø¹.ãVfæ2NŽK|±;>Fvt7+Ñø È\ÑýL¥Â¬‘}éËs (OcCqCŽØ›] -¥òY— ssEÁ¤'ÛM©t˜§‘ДDV9}Øm8„ɲ•½¼›ÙÿpZ{ _7³oùLê8…ú·Ì’…’ (šªV‰&ÐÄ §‘ɨņ&G(,‹·Wd˜DaÎàmHhõ’Ñ=Ó÷åðÑД“÷H‹œÍ*é, ›£Ü“¸îd‰ L9ÓÔN ÈM¼d Y(ò¦f²_¦PÄ®‰`Ø`"*‘‚Œy4Ó4mB ìŽd‚¸îq¸,æÏl™ÊNsÈϰNc÷{›Ú,—F=g§ˆY› œãEÂI¸ÌRE@9šŽS)f¢È;ËŽÒÔ‹¦Éβ!ÞaC€#§Œ$w} èDj®èp…MÖ9ÈœÔpЯsÚ¼á3„ôUÎ’½µÌ‘0njglTmCiúFBUã}õÐf¾§ÁY¿ SpWþp" ²hú¢ß!Ѝ’(ChU¾b]@~“Pí¯ºÐ,½§$ ’ky¢4ú §Òl®Qw¶±úù4љԅ?¨þIa°¦x˜~\0ivN±ìô—Íl#ÅÄ'†ÆÖ¹§4ÁcxLE4‹2¦z0¡éÓUX±Ö?ç5ï@æä §q.¸ÚJj’Ïú¯Ã:Js-}©DøG(Ê™—a”Á L5)«¨éÛ2»bF(Kª™m]AšÃ¯â´ÏßÙ£rè³Ü¡æÅ…þËÙÂ*h’âu»«‚F¼jˆ@„_#OÔv*m&å\™øS„š>*VÔ ‹ šëÒ¸é-’Ÿîጳ’ÆO …ä^ƒ›x“!„ƒy ?‡Hw Ð/ˆÕDÈF)º7øP[%àdy_£®QÊÓ¼ SZCÁ+PTXHS„êä¤×¡È£Ç³»JÃ¥$ªU ,¬ NsS?e§B3mà¼T°`ÇL­¦CádúPwV€$Rªfƒ+]ÕA&kªBTå˜Ó.ä%Re©@@Jâ«*‚‚€J\ `h}lÕ¶‡ÛY½˜ãÙŒ±Ê¼¸Uè~‹{E²MØV½i.Ã)Ý*±ÿ¶"^bÛqóNÛÅK·õ«»‹b` »—×AdŽy8{œ³ª’"ÇÃOe[¡i§úœÁ2/OkkÅu~¥²ÄÙWU! Ëvvûбd+7¼H±.3ZÅØˆj="—4C$'M'\ñC„r¦?qù6!$ëýaHák»æ}ç8÷ಉš&$z-›µG^‡èc í±žÓJIÇ’ÔtMŽ®µ£ÎUš_O^ͪT>l“ 7ïš›BUU¶•ø×ЃX³â2ƒôä§FÃ/¹NšTïi¯ *¼fMÈg'l÷0$”äIº¡aïœkõ)•5Þ<×9¥Gä«Ä{sJ‰ÜWæIP\ƒÖü¯àð¸âŠÿ˜^Í+®0#!ö«ÔX¤#fì Ûøá"†Á›SS•Ø‘:ÔÃýKMÙ£C”~ •Q B$aÓ'n-­~SU1‹§¥ž,þ €χ°Gõú«,Ëü²gQºNÒǪ7>äi³´5¸ ¶à=Õ+Ç;*ö26+®È£¬@:Éý™ddñØ¢xt%öóù°ëáZúì„!¾¤ÏV1U™\™óÒ'Ëÿ4÷øi1L±Y³Ïõ2¡M›gÝ`è®>3z%«ëúüê_²ª3•ÝÚ7ø]Õ¢]<˜äG‚/Ùænš RB—öŒ`ª¶š]'­²´ò:†¼þÊ&íº ,¹öæŠ4GC1Ê¥%EÔs¬Ž1ÃÕƒA¬Ù¢ÇýEo"‹"K›¶!@”j‹ë¨àtú“¤vÜ6ˆîGosYNJRØ5 .³[…f¼mµø¡Ñš9p{Âbµsu,¶þ£n¤ZÃQå¡ß~ÚW”Rí¯½!Ï𶲂Vn=s„ÁÀ¤*)zªÕŒ5^ƒÅ’Ô6±‘·½ŒŠµ\ˆ¾<í·ãHü¹Ea£P_ *ÍŸ¸±ÜíÌ%¶´­ŽU°I»Uìë’­·[%™\$'d‰„ÝpøT:Ô´4Ósh\’GgôܺPÕ xâº*¢ ^ÍÓY&‡Æ·•”ΔÝ&ük6;¹ã!Enݱ¹Uƃªp»òIŽÅŠ…t£íú[¼¹SÜJÜ,ÂJ#÷Z¼éöß]ÃX1;"í¾$¶Ze§*ØIËQŸ¶Œh±¢9-ÜÝE¶àÞ˜¬·¼‰Î1´Eª˜Ý¸½:·2,©—ж¤¢/´¡[…@5йK'E¹´¼Š7…ÜOk¸,'®ÀGºO)Å»ÅYxÝåÅp—fsÝGww+Íå[hf$‰Û¬vÇï5º0D·MÝzKo8Ãëõ·ôêYiÞæëse¯•¸]WsÚ^ü«"Âïèe¿*Bɾّ«oSµc·ÔW3Æ©I›DܯãE‰„†bˆÒ¸¢»ŽßSCš6£€×­\ 4ÓaÑ1\ <™Ø‚õUq2µPàÁv3kÙ¹}gf¸Ònæ{Á/i1Õ#K㺙úñÍ)„ÁÒŒÈ=D˜¨`àd3…R®™yê Éf3<]ÑT2ó73M_B¯–¢‰’O˜ùn¥©d`0Ž3 Øÿ°4ϧxô'·a€è];†'ƒŠ¨¼–7ÑN“…?0ËI‘è"#BàÉÀ("÷õìaTáD†ö´g'üšÔ´ƒ1L²3Â;lX]ù~#1wh9”?eÍhÞo Nck¯â-ù5›&>À~X+ ‡ÒH¸¾¦\„S×G܆kKͰ€«ò$`Œ>r‚ÎÐcħ…nD-/D3Ü%-Æn4£ÊÕÄ«C^h†v(S¾x߯Škêù Í( µJ<>#gaZeuüëÚ1¼Ú¥’ÂS‚ÈY¸7\œøx«³ˆ_÷óÆ gq=ò1µD“üØáŒžä‡æ†?v¯1x9²@Ul£ò¤P-z6„Y³…îÚÁØiòŒ¾z̈=š Êfãx¤ ËnŒI'r¢# ¥Qœ,ɰNOŒ XÉ*d#ûZv Ô„Ù¾B;5Ov²²â[ÅŠôdõc†üq2UãØ¢`6ñì•E.˜ön®¼4ÃÃ"ÜÝ„  –?²aîjQRVÂÂa©þd‹“ÅÄo±e½.!‹ í²“kp`XK…b1ãCè£@2F6Ü$%S–Åi?¸["eǼgŸ˜LÖ=VPн¹v±0«Šl%rÞ Õôx&kâI¡ü²îº5Ý£æu¬ Æ“ÓÄ3ÉÊÃv1ì—»¨d¾Åɵ»f/œ&FÈ~Ñʺ¸53ÇÑæ•>ìäÁX`Œã–áf™“â•ÄíÇZÜþœ·ÛQcÍ™ˆÎTeË'‰WßX.Y’ø5 f )ž$n¨c¹6Cä“’óÅÀp ì‚¿–%ÿÂóBÁÈé´ÓPg‡§›A‡ÕmÌU±eA„é|ˆ·rC •¡úxã¯!¨¾Y óœßjá)3ó³â‰-„±M˜çò*Š'Ú¿ðÁÙ'šžñDs·py8!c¸ ’1ó»x7hùºŒÎ÷€–ÃaœE–O朄’Å v‡¬ýçòø÷Žñf.;–ääÌY 4ÌD€c¬Aâ+ôä«fÃTÎvèß<3”ˆ_6±HšÈD<Æ ‹÷±Ð(TÚOÙã¤A~|tTÖÉ&èh‡’ÒíÕÕÅiŽ‹ªPžûËÆ<ôϧsfeU9{F5qÙˆkºMK4GÕ–èLÇà$ñ‰HONJz2gO490,t¾$4+Dà‰+ö+XkÓ_á™ãG½sf×þò6§Ø›1Š©› »ÉñoPܘã§ïƒ u.KÚ0]Q#/>õõ)­T¹>k`²˜AÚ–Þ ÝµOß6$L¬ÞrŽÏÙe±”N]ʈÉ0…Õ„•Xÿ„sÈê&qF_B˾f½–È×Ý£°ñ/.Ù+a WœÔ ²Ô°yP3³i9ëÚôÊiyºšX7(¬ç““êTûÇk'…d³3érR¹b–xÉ1 8ÚÙÆl×Vz ù®o­…“ý(ˆ‡údᣥU›Ïñ™ö¨Ñ¬rS®oS£öÕÍZ;ä•5°wuàטXŽK²ÛtÕ¶š”°ÀjÍ9þsL!ÇÔÅÂÃjñW¸>ìzXé¡P•Žô®Þ;1†Äyaî9_rvŽCª¤ l;.a&[ÚðHêêA CøtÊ&ÒŸ4®Vè] ƒ(AvÄ+†d`®-óu:Û×…L…Mm+M{su&¦Ûrå`“åøä¬êýmò0!=žÚÜ骂ϬÀ,YMÏm Œ7Eè$ÜZÎÊ(í8MÕºßÕ¦Ü÷ÁùôìDÆÓ^B»ÛŒ»;ùí86„ LÐÙÜO›À`¤rg…‚®èly »îÊɺ±äðGƒÏŠv¸Y°î›¢CpÃ×'3Újñ ³—{<«Õµ§­‹468¤¥®mÙ5SÕhSÉøL×êK͆5¨Ö½Þ{ÙY­˜Ñjî¦èqйöÑÄ -»·ò¡ò*©(UçÂ÷»Ñ8sVvkA»3gcp{¨ð[(ÝaÜ©Ž%-¶½§-Ûãáë÷¿^õ±vkb³¼˜c÷;“ÃgÖâ:ÁM8#Îd,€¿äÌq’fîÛ×¾…so ¬«¤ Aö I”~Çë Ž¾>ÈâößjN1È2ç5):ß ×X„xom$œ_ Q¶}ÚðUÏéåø DYŸßǸ'AZp ){–Z³…âÁ -qþÜV~”º8ß6Ÿ©’©Ì”’¬XŸØDË ãnLÌ¡“ˆ‡6i”¼µ0@’ω³<¬=8Ã(Å+ÜÏ]±Ãu‘»=ÓDE4”³xGiÞPš…!Áƽcÿ=#L^¦Öþ»ñà8an¢Up‹pZüþò.I•3£ðºãmöêÇI£u£p %5w ÒÜÆçŠâpdr¿àŒK“a±À]¿aZBÁÕµ å î/5|ÞäÖÓ©òa.zs_á©·J¥k"îµà¸D†Ϙ‹MK.ú1úTˆù Vèq~ŽËùù¬µñ{â|1ŽãvÚrî ­µÐ–¿9J¡D¶z9Tßl,Ürêö¾^¸w12Ë*À¿òæšØ¡kÃõ«‹s]™C­ZØ¢ks ŸÝgÕÄ™>âágžÑ‹šæ ™„9?æ;xÞÐíÄÇ$Èy­Tò1ÌxbÝ_GWmÛëá7!¹¯èUU0€tšn]¿”öèfÙ¨/îz®1y gêOî Gr¨d ú™Yµ ý%ë: Ò;:Æiì[Ÿç1:mÊÙðq¢7ý8™U–Έ…”‘œñ<ôd˜ª¼—SåIêúœ€Â´®"ʃڮÀAbQAµrÝ||ÔQ7믡{fõˆ~ÅnÌ×ç!_PYyjÖµ› Çë4Žô²®%*[èꛜ塸ÞÐe aë8ZžcÚñÝ“IëÝbŸ@t³Žd3LX‡ zãpõsæ²f>Ïè&nAå12ANÁÎe©_!Ÿä@;qhŹà8ŒmPû>ÞÅÝϧçâ,±c9ͬ͒¸ÒÎv Þ«P óC˜R›÷›^<½=_îÙå·îp¨=¡ë¸&æ¼ù½?“yucx' ÔAº_÷‰<‹Qõø™ä2œ¢swTáÝW8v^*d× ÷éžX¬÷m—ÎI»óÿžqÄ4A_GAÔ¿[råpƒÕ¶JV6͘xæ<<Õ|ÏZ©ÊÀ_ãlýá¯mÝH˜ªXÂÇÞ 0ÂtIî$ýGvšµâã³vË'_ªOÄãGiÖòœ=Ç7„$Uäƒr·9\IÞ:£;¦—åb?êg (#aÚz)»V¾—fÆz%þÉÒŒš']>ž¹¸yò-("»Wð6Fv°Eöà1ç Xf ¤Ï×Aì@@p¯ Ÿ°þŒð³CÂ7H®\ô ‘K„K¥_=:JÝ 0äË }e=ô‘Ð;ìFÙ}¶s†óÖïfØÀV©ï ß-EzÐ ëGõ+ÔÔ£í4Û ¯RØôÊ?ô’‹5Í =Uƒ(³þšËQ£Ÿ’}×¢&Wáz6ýÞ)·uö¯þ ¼ÓíSÈxÎÓ"¡Ä4Ž®HÁý"«Q~ÒG›ƒƒÑ }Ö6ö¤Ôk{U ™ãývŸ÷Ɉr¹p-TÚ“÷áZdMçbâÎŽ#†±áÙ÷…—§M¶ø‰! ü|Õ+DÂøã‹¹¯!¸S;$ø¨÷Fu„q/s.ûþ? §(‘aR~ÇO‹å‡|ª€e~ 5á²½›ï#ð]QÛùœ}á Ó÷Ä{>€HÔEá!ÎâBþ¾êÿíÒmö kÏ ®™;Ì’¯FˆR(4` ãà  h„œÁ7y9íßû€ï!íŽÛ§ø*ÙclwNnSšÙûc³I…Å¿ÿï'‰4½;¤užGø<ݧ‚-çp·0x<.Od‹Œ<Й˟ã£`ldÜàÿOcú)~Rq8AÒ×>sÔøÓýê÷$³dR D[?¹ß 6¶î˜‡¿Ë÷ßWLý þ üª²û ¿cd¼ì»A?9Ôùý¥ZµÍç[“¨¹àÀ5bú§ÀŸa+Øo¯D¸óoYÂûKÿ~´"tyj•²1(ûß~å?§8ƒ+ç÷í𲼜ï¶R®ge…”‚%‹•ûïóÿü6…ß¼ä6AlŠ_“"îÑFæ ßÒÃ= ø3¬4 `°7åà¶ð- ð(1õ®ÖÅcÝu|K¤ÄÈ}¹‚‹¤}Âôµ,brA؆©Ä Û'Ũp ãÝA°gEÃx×ìY™È³þý&SÀæ>|¿AF‘ð|ÁRp,ÿÈܧ~bUÈb·  ÂÞè!F¨;$´{SàÅ`%9\ x1 cæÁÞõ$ýZ—p5u•Fx“i _Ádæ¥ã°;L|ÍÑéè$x Á KœB•Ç &Êó""6¤$ˆ;d¡à#Œ H…„Vœ‚ù7na¾¡*„ÚßEøsð+T”€1Ÿ¥ 9]Ç&EP(Ò„IÜ£†„ƒIáúyþ I‚ö‘}/ááM,…h¡T¨|…N`öŸœ…ÎÛ4Ÿ!ÝÅS<²„!AE¡Æ!ä †vÅ£1ÀX:=âI.p!éö ®#É ^¨¥Êe^q4á´CZp®ÀeD†=¡I(QuOjÛ1ˆâI&›acÀÕ<g__ø VP`8 ¶xið½"ßÅ,¡Ò„AϾaÛ …‘Ýfà\ ‡ Ë´¦hŒ¶UèyÍ¡+3­™ Fs¸²Lk–4b ƒµ!ûsZ„`®äã}‡ÝÍ}“#pW7ÆÝ—^*ÃyøF•‚×âŠaâ`7#Ò̇¡Þ@ØÍ]ø¡6b;ÜSÈ‘!‚ØÎ@2v8~9‚EШH~ÜáHà‡ˆ%[hxáâmC>-MT…HÖ+¬††˜¦g¿Å˜îía›’†˜õ4Q`ÇQôIˆøB‰LƒÜîcpƒÿaL¸ ½ˆ<ß…¸Ø$)ga-5.9.2/global/X/xregion.config000066400000000000000000000011541500715745200164360ustar00rootroot00000000000000# configuration file for xregion # # select colors for the following: # foreground - foreground color of windows # background - background color of windows # selection - color of area selected for viewing # rubberband - color of selection rubberband # grid - color of grid lines # canvas - background color of display area # # colors are X11 named colors (see an X reference for list) # # Only colors you wish to change need to be in the # configuration file. Others will be set to default colors. # foreground navy background light gray selection Green rubberband Green grid red canvas black ga-5.9.2/global/X/xregion.h000066400000000000000000000136071500715745200154260ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #define DEBUG */ #define INITSLOW 50. #define POW_BASE 100. extern void exit(); /* function prototypes */ extern void Error(char *message, int err_num); extern void UpdatePixRegion(int ilo, int ihi, int jlo, int jhi, int increment, double time); extern void DisplayPixRegion(int ilo, int ihi, int jlo, int jhi); extern void DisplaySlowdownValue(); extern void DisplayIntervalValue(); /* JJU: extern void ScrollProc(Widget scrollbar, caddr_t data, caddr_t position); */ extern void ScrollProc(Widget scrollbar, XtPointer data, XtPointer position); /* JJU: extern void ScrollProc2(Widget scrollbar, caddr_t data, caddr_t position); */ extern void ScrollProc2(Widget scrollbar, XtPointer data, XtPointer position); /* JJU: extern void JumpProc(Widget scrollbar, caddr_t data, caddr_t fraction_ptr); */ extern void JumpProc(Widget scrollbar, XtPointer data, XtPointer fraction_ptr); /* JJU: extern void JumpProc2(Widget scrollbar, caddr_t data, caddr_t fraction_ptr); */ extern void JumpProc2(Widget scrollbar, XtPointer data, XtPointer fraction_ptr); extern void DrawColorMap(); extern void PrintColorMapText(); extern void UpdateDisplay(); /* JJU: extern void TimeOutCallback(caddr_t data); */ extern void TimeOutCallback(XtPointer data, XtIntervalId *xtintervalid); /* JJU: extern void Exposed(Widget widget, caddr_t data, XEvent *event); */ extern void Exposed(Widget widget, XtPointer data, XEvent *event, Boolean *bln); /* JJU: extern void dismiss_dialog(Widget w, caddr_t data, XEvent *event) */ extern void dismiss_dialog(Widget widget, XtPointer data, XtPointer event); /* JJU: extern void Quit(Widget widget, caddr_t data, XEvent *event); */ extern void Quit(Widget widget, XtPointer data, XtPointer event); /* JJU: extern void StartStop(Widget widget, caddr_t data, XEvent *event); */ extern void StartStop(Widget widget, XtPointer data, XtPointer event); /* JJU: extern void start_view(Widget widget, caddr_t data, XEvent *event); */ extern void start_view(Widget widget, XtPointer data, XtPointer event); extern void Setcmap(); extern void ReadEventFile(char *filename); /* JJU: extern void running_coords(Widget widget, caddr_t data, XEvent *event); */ extern void running_coords(Widget widget, XtPointer data, XEvent *event, Boolean *bln); /* JJU: extern void running_overview(Widget widget, caddr_t data, XEvent *event); */ extern void running_overview(Widget widget, XtPointer data, XEvent *event, Boolean *bln); /* JJU: extern void draw_select_box(Widget widget, caddr_t data, XEvent *event); */ extern void draw_select_box(Widget widget, XtPointer data, XEvent *event, Boolean *bln); extern int in_display_region(int ilo, int ihi, int jlo, int jhi); extern XtAppContext create_overview(int argc, char **argv); extern void create_main_window(); extern void setup_drawing(); extern void dialog_box(char *dlg_str); extern void set_config(); /* Globals needed for display etc. */ #ifdef FIRST_TIME #define SCOPE #else #define SCOPE extern #endif /* FIRST_TIME */ SCOPE Widget top_level_widget, box_widget, start_stop_button, scroll_widget, interval_widget, scroll_widget2, slowdown_widget, coord_widget, dlg_top, dlg_btn, dlg_form, dlg_label, view_button, interval_label, slowdown_label, map_widget, quit_button, overview_widget, select_widget, overview_title, overview_shell, canvas_widget, title_widget; SCOPE XtAppContext xregion_app; SCOPE XtIntervalId timer; SCOPE long first_time; /* Used to set scroll bar on first expose */ SCOPE long interval_max; SCOPE long interval; /* 0.5s between exposures by default */ SCOPE double slowdown_max; SCOPE double slowdown_min; SCOPE double slowdown; /* slowdown factor for animation */ SCOPE double oldslowdown; SCOPE unsigned long int cur_time; /* current time */ SCOPE Arg arg[25]; SCOPE Display *display; SCOPE Window window, window_map; SCOPE int screen, depth; SCOPE Visual *visual; SCOPE XImage *image; SCOPE u_char *pict; SCOPE GC gc, gc_map; SCOPE char title[80]; SCOPE char interval_string[10], slowdown_string[11]; SCOPE int top_edge, bottom_edge, left_edge, right_edge; #define GA_MAX(a,b) (((a)>(b)) ? (a) : (b)) #define GA_MIN(a,b) (((a)<(b)) ? (a) : (b)) #define MAX_COL 16 SCOPE u_char cmap[MAX_COL+1]; SCOPE Colormap colormap; SCOPE int grid_x, grid_y; /* The size of the grid */ SCOPE int scale; /* No. of pixels per element */ SCOPE int pict_width; /* The size of the picture = grid_x * scale */ SCOPE int pict_height; /* The size of the picture = grid_y * scale */ SCOPE int overview_height, overview_width; SCOPE double overview_scale; SCOPE int *overlay_row, *overlay_col; SCOPE int rows, cols; SCOPE u_char *grid; SCOPE double *ltime; /* last event time */ SCOPE double *integr; /* access integral */ SCOPE double maxval; /* max value of integral, zero is default */ SCOPE u_char *flag; /* access flag */ SCOPE int working, animation; /*** trace variables and constants ***/ SCOPE long int num_events; SCOPE int *record; /* tracefile data */ SCOPE unsigned long int *ev_times; /* times of events */ SCOPE int cur_event; #define RECLEN 8 /*** end of trace variables and constants ***/ /* color constants */ SCOPE Pixel DEFAULT_FG; SCOPE Pixel DEFAULT_BG; SCOPE Pixel RBAND_COLOR; SCOPE Pixel SELECT_COLOR; SCOPE Pixel GRID_COLOR; SCOPE Pixel CANVAS_COLOR; ga-5.9.2/global/X/xregion_colormap.c000066400000000000000000000026071500715745200173130ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* This module contains the functions for displaying the access color map that appears on the right side of the display window */ #include "xregion.h" void DrawColorMap() { /* Actually fill in the colors */ int i, black = 1; unsigned width = 20, height = 350 / MAX_COL; int x, y, length,index; XColor color; for (i=0,y=0,x=0;i= 10. || maxval < .001) ? 1 : 0; for (i=0;ixexpose.count == 0) { if (first_time) { /* Cannot seem to set this before now ? */ ScrollProc(scroll_widget, NULL, 0); ScrollProc2(scroll_widget2, NULL, 0); first_time = False; } DrawColorMap(); PrintColorMapText(); DisplayPixRegion(0, pict_height - 1, 0, pict_width - 1); XFlush(display); } } ga-5.9.2/global/X/xregion_fileio.c000066400000000000000000000037151500715745200167470ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDLIB_H # include #endif #include "xregion.h" void ReadEventFile(char *filename) { FILE *fin; long int i, k, act_events = 0; char errmsg[128]; fin = fopen(filename,"r"); if (!fin) { Error("Input File Not Found",-2); } if (!fscanf(fin, "%d%d%d", &grid_y, &grid_x, &num_events)) { sprintf(errmsg, "Unable to read data from file %s", filename); Error(errmsg, -2); } if (!fscanf(fin, "%d%d", &rows, &cols)) { sprintf(errmsg, "Unable to read data from file %s", filename); Error(errmsg, -2); } if (!(overlay_row = (int*) malloc(rows * sizeof(int)))) { Error("couldn't allocate memory",-1); } if (!(overlay_col = (int*) malloc(cols * sizeof(int)))) { Error("couldn't allocate memory",-1); } for(i = 0; i < rows; i++) { if (!fscanf(fin, "%d", (overlay_row + i))) { sprintf(errmsg, "Unable to read data from file %s", filename); Error(errmsg, -2); } } for(i = 0; i < cols; i++) { if (!fscanf(fin, "%d", (overlay_col + i))) { sprintf(errmsg, "Unable to read data from file %s", filename); Error(errmsg, -2); } } if (!(record = (int*) malloc(RECLEN * num_events * sizeof(int)))) { Error("couldn't allocate memory",-1); } if (!(ev_times = (unsigned long int *) malloc(num_events * sizeof(unsigned long)))) { Error("couldn't allocate memory",-2); } for(i = 0; i < num_events; i++) { for(k = 0; k < RECLEN; k++) { fscanf(fin, "%d", (i * RECLEN + k) + record); } if(fscanf(fin, "%lu", ev_times + i)) { act_events++; } /* Adjust from Fortran to C base addressing */ for (k = 2; k <= 5; k++) { (*((i * RECLEN + k) + record))--; } if (feof(fin)) { break; } } num_events = act_events; printf("File %s has been read. %d events are displayed\n",filename,num_events); fclose(fin); } ga-5.9.2/global/X/xregion_main.c000066400000000000000000000030701500715745200164160ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #define FIRST_TIME #include "xregion.h" #if HAVE_MATH_H # include #endif int main(int argc, char **argv) { int i, argn; char filename[128]; /* initialize global variables */ first_time = True; /* Used to set scroll bar on first expose */ interval_max = 2000; interval = 500; /* 0.5s between exposures by default */ slowdown_max = 100.; slowdown_min = .1; slowdown = INITSLOW; /* slowdown factor for animation */ oldslowdown = INITSLOW; cur_time = 0; /* current time */ maxval = 0.; /* max value of integral, zero is default */ working = False; animation = True; cur_event = 0; /* First read the argument list */ for(i = 1, argn = 0; i < argc; i++) { if (argv[i][0] == '-') { break; } else { argn = i; } } argn ++; if (argn < 2 ) { printf("Usage:\n"); printf("xregion \n"); exit(1); } sscanf(argv[1],"%s", filename); ReadEventFile(filename); scale = 1; overview_scale = 499.0 / GA_MAX((grid_x), (grid_y)); overview_width = ceil( (grid_x) * (500.0 / GA_MAX((grid_x), (grid_y))) ); overview_height = ceil( (grid_y) * (500.0 / GA_MAX((grid_x), (grid_y))) ); printf("overview_scale %lf\n", overview_scale); printf("overview_width %d\n", overview_width); printf("overview_height %d\n", overview_height); /* Realize everything */ xregion_app = create_overview(argc, argv); XtRealizeWidget(overview_shell); /* Enter the event loop */ XtAppMainLoop(xregion_app); } ga-5.9.2/global/X/xregion_overview.c000066400000000000000000000312241500715745200173420ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #include "xregion.h" XtAppContext create_overview(int argc, char **argv) { XtAppContext app; overview_shell = XtVaAppInitialize(&app,"XRegion", NULL, 0, &argc, argv, NULL, XtNtitle, "select region to view", NULL); set_config(); overview_widget = XtVaCreateManagedWidget("selectCanvas", formWidgetClass, overview_shell, XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, NULL); overview_title = XtVaCreateManagedWidget("selectTitle",labelWidgetClass, overview_widget, XtNlabel, "x, y:", XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, XtNx, 10, XtNy, 10, XtNwidth, 350, XtNvertDistance, 10, XtNhorizDistance, 10, XtNborderWidth, 0, NULL); /* Create the Start/Stop command button */ view_button = XtVaCreateManagedWidget("viewbutton", commandWidgetClass, overview_widget, XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, XtNhorizDistance, 10, XtNvertDistance, 10, XtNfromHoriz, overview_title, XtNlabel, "View Selection", XtNsensitive, False, XtNshapeStyle, XmuShapeOval, NULL); XtAddCallback(view_button, XtNcallback, start_view, NULL); select_widget = XtVaCreateManagedWidget("areaSelect", formWidgetClass, overview_widget, XtNvertDistance, 50, XtNhorizDistance, 5, XtNheight, overview_height, XtNwidth, overview_width, XtNborderWidth, 0, NULL); XtAddEventHandler(select_widget, PointerMotionMask, False, running_overview, NULL); XtAddEventHandler(select_widget, ButtonPressMask, False, draw_select_box, NULL); XtAddEventHandler(select_widget, ButtonReleaseMask, False, draw_select_box, NULL); XtAddEventHandler(select_widget, Button1MotionMask, False, draw_select_box, NULL); XtAddEventHandler(select_widget, ExposureMask, False, draw_select_box, NULL); return(app); } /* JJU: void start_view(Widget widget, caddr_t data, XEvent *event); */ void start_view(Widget widget, XtPointer data, XtPointer event) { create_main_window(); /* Realize everything */ XtRealizeWidget(top_level_widget); setup_drawing(); XtRemoveEventHandler(select_widget, ButtonPressMask, False, draw_select_box, NULL); XtRemoveEventHandler(select_widget, ButtonReleaseMask, False, draw_select_box, NULL); XtRemoveEventHandler(select_widget, Button1MotionMask, False, draw_select_box, NULL); } /* JJU: void running_overview(Widget widget, caddr_t data, XEvent *event) */ void running_overview(Widget widget, XtPointer data, XEvent *event, Boolean *bln) { char loc_str[40]; int x, y; int scale_x, scale_y; x = event->xmotion.x; y = event->xmotion.y; if (x < 0) x = 0; if (x > overview_width - 1) x = overview_width - 1; if (y < 0) y = 0; if (y > overview_height - 1) y = overview_height - 1; /* scale_x = (int)((x / overview_scale) + 1.5); scale_y = (int)((y / overview_scale) + 1.5); */ scale_x = (int)((x / overview_scale) + .5); scale_y = (int)((y / overview_scale) + .5); if (scale_x > grid_x) scale_x = grid_x; if (scale_y > grid_y) scale_y = grid_y; sprintf(loc_str, "x, y: %d, %d", scale_x, scale_y); XtVaSetValues(overview_title, XtNlabel, loc_str, NULL); } /* JJU: void draw_select_box(Widget widget, caddr_t data, XEvent *event) */ void draw_select_box(Widget widget, XtPointer data, XEvent *event, Boolean *bln) { static int x1, x2, y1, y2; static int first = True; static GC gc_rband, gc_select; static Display *disp; static Pixmap pixmap; /* JJU: static Window *win; */ static Window win; static int screen; static int button_state = 0; static int another_state = False; char err_str[80]; char loc_str[40]; int i, ul_x, ul_y, lr_x, lr_y; if (first) { first = False; x1 = x2 = y1 = y2 = -1; disp = XtDisplay(select_widget); win = XtWindow(select_widget); screen = DefaultScreen(disp); gc_rband = XCreateGC(disp, win, NULL, NULL); gc_select = XCreateGC(disp, win, NULL, NULL); pixmap = XCreatePixmap(disp, RootWindow(disp, screen), overview_width, overview_height, DefaultDepth(disp, screen)); XSetForeground(disp, gc_select, CANVAS_COLOR); XFillRectangle(disp, pixmap, gc_select, 0, 0, overview_width, overview_height); XSetForeground(disp, gc_rband, RBAND_COLOR); XSetForeground(disp, gc_select, SELECT_COLOR); XSetBackground(disp, gc_rband, CANVAS_COLOR); XSetFunction(disp,gc_rband, GXxor); /* XSetLineAttributes(disp, gc_rband, 3, LineSolid, CapRound, JoinRound); XSetLineAttributes(disp, gc_select, 3, LineSolid, CapRound, JoinRound); */ } switch (event->type) { case Expose: if (event->xexpose.count == 0) { XSetForeground(disp, gc_select, GRID_COLOR); for(i = 0; i < rows; i++) { int _x1=0,_x2=overview_width - 1; int _y1,_y2; _y1=_y2=overlay_row[i] * overview_scale; XDrawLine(disp, pixmap, gc_select, _x1, _y1, _x2, _y2); } for(i = 0; i < cols; i++) { int _y1=0,_y2=overview_height - 1; int _x1,_x2; _x1 =_x2 =overlay_col[i] * overview_scale; XDrawLine(disp, pixmap, gc_select, _x1, _y1, _x2, _y2); } XSetForeground(disp, gc_select, SELECT_COLOR); XCopyArea(disp, pixmap, win, gc_select, 0, 0, overview_width, overview_height, 0, 0); } break; case ButtonPress: if (event->xbutton.button == 1) { x1 = x2 = event->xbutton.x; y1 = y2 = event->xbutton.y; button_state = 1; another_state = True; } break; case ButtonRelease: if (event->xbutton.button == 1 && button_state == 1) { /* erase old rubberband box */ XDrawRectangle(disp, win, gc_rband, x1, y1, x2 - x1, y2 - y1); /* get latest corner */ x2 = event->xbutton.x; y2 = event->xbutton.y; if (x2 < 0) x2 = 0; if (x2 > overview_width - 1) x2 = overview_width - 1; if (y2 < 0) y2 = 0; if (y2 > overview_height - 1) y2 = overview_height - 1; if ((abs(x2 - x1) > 4) || (abs(y2 - y1) > 4)) { XtVaSetValues(overview_title, XtNlabel, loc_str, NULL); /* draw final selection box */ XDrawRectangle(disp, win, gc_select, x1, y1, x2 - x1, y2 - y1); XDrawRectangle(disp, pixmap, gc_select, x1, y1, x2 - x1, y2 - y1); XFillRectangle(disp, pixmap, gc_select, x1, y1, x2 - x1, y2 - y1); XSetForeground(disp, gc_select, GRID_COLOR); for(i = 0; i < rows; i++) { int _x1=0,_x2=overview_width - 1; int _y1,_y2; _y1=_y2=overlay_row[i] * overview_scale; XDrawLine(disp, pixmap, gc_select, _x1, _y1, _x2, _y2); } for(i = 0; i < cols; i++) { int _y1=0,_y2=overview_height - 1; int _x1,_x2; _x1 =_x2 =overlay_col[i] * overview_scale; XDrawLine(disp, pixmap, gc_select, _x1, _y1, _x2, _y2); } /* for(i = 0; i < rows; i++) { XDrawLine(disp, pixmap, gc_select, 0, (overlay_row[i] - 1) * overview_scale, overview_width - 1, (overlay_row[i] - 1) * overview_scale); } for(i = 0; i < cols; i++) { XDrawLine(disp, pixmap, gc_select, (overlay_col[i] - 1) * overview_scale, 0, (overlay_col[i] - 1) * overview_scale, overview_height - 1); } */ XSetForeground(disp, gc_select, SELECT_COLOR); XCopyArea(disp, pixmap, win, gc_select, 0, 0, overview_width, overview_height, 0, 0); left_edge = (int)(GA_MIN(x1, x2) / overview_scale + .5); right_edge = (int)(GA_MAX(x1, x2) / overview_scale + .5); top_edge = (int)(GA_MIN(y1, y2) / overview_scale + .5); bottom_edge = (int)(GA_MAX(y1, y2) / overview_scale + .5); if (left_edge < 0) left_edge = 0; if (right_edge > grid_x - 1) right_edge = grid_x - 1; if (top_edge < 0) top_edge = 0; if (bottom_edge > grid_y - 1) bottom_edge = grid_y - 1; pict_width = right_edge - left_edge + 1; pict_height = bottom_edge - top_edge + 1; scale = GA_MIN( ceil((500.0 / pict_width)), ceil((500.0 / pict_height))); if ((pict_width < 501) || (pict_height < 501)) { fprintf(stderr, "pict_width, pict_height, scale: %d, %d, %d\n", pict_width, pict_height, scale); fprintf(stderr, "Left, right, top, bottom: %d, %d, %d, %d\n", left_edge, right_edge, top_edge, bottom_edge); XtVaSetValues(view_button, XtNsensitive, True, NULL); } else { /* select to large a region to view - Error */ XtVaSetValues(view_button, XtNsensitive, False, NULL); sprintf(err_str, "Selection Error: select area 500x500 or smaller"); dialog_box(err_str); XSetForeground(disp, gc_select, CANVAS_COLOR); XDrawRectangle(disp, pixmap, gc_select, x1, y1, x2 - x1, y2 - y1); XSetForeground(disp, gc_select, SELECT_COLOR); button_state = 0; break; } } } break; case MotionNotify: if (button_state == 1) { if (another_state) { XSetForeground(disp, gc_select, CANVAS_COLOR); XFillRectangle(disp, pixmap, gc_select, 0, 0,overview_width, overview_height ); XSetForeground(disp, gc_select, GRID_COLOR); /* for(i = 0; i < rows; i++) { int x1=0,x2=overview_width - 1; int y1=y2=overlay_row[i] * overview_scale; XDrawLine(disp, pixmap, gc_select, x1, y1, x2, y2); } for(i = 0; i < cols; i++) { int y1=0,y2=overview_height - 1; int x1=x2=overlay_col[i] * overview_scale; XDrawLine(disp, pixmap, gc_select, x1, y1, x2, y2); } */ for(i = 0; i < rows; i++) { XDrawLine(disp, pixmap, gc_select, 0, (overlay_row[i] - 0) * overview_scale, overview_width - 1, (overlay_row[i] - 0) * overview_scale); } for(i = 0; i < cols; i++) { XDrawLine(disp, pixmap, gc_select, (overlay_col[i] - 0) * overview_scale, 0, (overlay_col[i] - 0) * overview_scale, overview_height - 1); } XSetForeground(disp, gc_select, SELECT_COLOR); XCopyArea(disp, pixmap, win, gc_select, 0, 0, overview_width, overview_height, 0, 0); XFlush(disp); another_state = False; } /* erase old rubberband box */ XDrawRectangle(disp, win, gc_rband, x1, y1, x2 - x1, y2 - y1); /* get latest corner */ x2 = event->xbutton.x; y2 = event->xbutton.y; if (x2 < 0) x2 = 0; if (x2 > overview_width - 1) x2 = overview_width - 1; if (y2 < 0) y2 = 0; if (y2 > overview_height - 1) y2 = overview_height - 1; /* draw new rubberband box */ XDrawRectangle(disp, win, gc_rband, x1, y1, x2 - x1, y2 - y1); } break; } } ga-5.9.2/global/X/xregion_pixregion.c000066400000000000000000000062541500715745200175050ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "xregion.h" void UpdatePixRegion(int ilo, int ihi, int jlo, int jhi, int increment, double time) { register int i, j, k, l, index; register u_char *from, *to, *tempk, *tempkl, value, *pflag; register double *pintegr, *pltime, corr; for (i = ilo; i < ihi + 1; i++) { for (j = jlo; j < jhi + 1; j++) { to = pict + (i * pict_width * scale + j) * scale; from = grid + (i * pict_width + j); pflag = flag + (i * pict_width + j); pintegr = integr + (i * pict_width + j); pltime = ltime + (i * pict_width + j); /* increment == 0 means animation is done and displaying integrals */ if (animation) { corr = *from > 1 ? *from - 1 : 0; corr = corr * (time - *pltime); if(corr < 0.0) { fprintf(stderr, "error: time =%f ltime =%f height =%d \n", time,*pltime,*from); } *pintegr += corr; *pltime = time; *from = *from + increment; index = *from; index = GA_MIN(index,MAX_COL-1); if(increment) { *pflag = 1; } /* calculate max value of integrals */ if(*pintegr > maxval) { maxval = *pintegr; } } else /* done with animation display integral */ { index = (int) (((*pintegr) / maxval) * MAX_COL); index = GA_MIN(index, MAX_COL - 1); if(!index && *pflag) { index = MAX_COL; /* sets the "accessed" color */ } } value = cmap[index]; for (k = 0, tempk = to; k < scale; k++, tempk += pict_width * scale) { for (l = 0, tempkl = tempk; l < scale; l++, tempkl++) { *tempkl = value; } } } /* end for j */ } /* end for i */ } void DisplayPixRegion(int ilo, int ihi, int jlo, int jhi) { int count, x, y, height, width; y = ilo * scale; x = jlo * scale; height = (ihi - ilo + 1) * scale; width = (jhi - jlo + 1) * scale; XPutImage(display, window, gc, image, x, y, x, y, width, height); XFlush(display); /* for(count = 0; count < rows; count++) { XDrawLine(display, XtWindow(canvas_widget), gc, 0, (overlay_row[count] - 1 - top_edge) * scale, pict_width * scale - 1, (overlay_row[count] - 1 - top_edge) * scale); } for(count = 0; count < cols; count++) { XDrawLine(display, XtWindow(canvas_widget), gc, (overlay_col[count] - 1 - left_edge) * scale, 0, (overlay_col[count] - 1 - left_edge) * scale, pict_height * scale - 1); } */ for(count = 0; count < rows; count++) { XDrawLine(display, XtWindow(canvas_widget), gc, 0, (overlay_row[count] - top_edge + .5) * scale, pict_width * scale - 1, (overlay_row[count] - top_edge + .5) * scale); } for(count = 0; count < cols; count++) { XDrawLine(display, XtWindow(canvas_widget), gc, (overlay_col[count] - left_edge + .5) * scale, 0, (overlay_col[count] - left_edge + .5) * scale, pict_height * scale - 1); } } ga-5.9.2/global/X/xregion_scrollbars.c000066400000000000000000000077161500715745200176530ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "xregion.h" #include #define GA_ABS(x) (((x) < 0 )? -(x) : (x)) void DisplayIntervalValue() { (void) sprintf(interval_string, "%4d ms", interval); XtSetArg(arg[0], XtNlabel, interval_string); XtSetValues(interval_widget,arg,1); } void DisplaySlowdownValue() { (void) sprintf(slowdown_string, "%5.1f times", slowdown); XtSetArg(arg[0], XtNlabel, slowdown_string); XtSetValues(slowdown_widget,arg,1); } /**/ /* JJU: void ScrollProc(Widget scrollbar, caddr_t data, caddr_t position) */ void ScrollProc(Widget scrollbar, XtPointer data, XtPointer position) /* Called when the left or right buttons are used to step the scrollbar left or right. We have the responsibility of moving the scrollbar. */ { Dimension length; float fraction; float shown; /* Get the scrollbar length and move the scroll bar */ XtSetArg(arg[0], XtNlength, &length); XtGetValues(scrollbar, arg, 1); fraction = ((int) position)/ (double) length; interval = fraction*0.05*interval_max; interval = GA_MIN(interval, interval_max); interval = GA_MAX(interval, 1); fraction = (float) interval/ (float) interval_max; shown = -1.0; DisplayIntervalValue(); XawScrollbarSetThumb(scrollbar, fraction, shown); } /***** slowdown **********/ /* JJU: void ScrollProc2(Widget scrollbar, caddr_t data, caddr_t position) */ void ScrollProc2(Widget scrollbar, XtPointer data, XtPointer position) /* Called when the left or right buttons are used to step the scrollbar left or right. We have the responsibility of moving the scrollbar. */ { Dimension length; double fraction; float shown; /* Get the scrollbar length and move the scroll bar */ XtSetArg(arg[0], XtNlength, &length); XtGetValues(scrollbar, arg, 1); fraction -= ((int) position)/ (double) length; slowdown = fraction*0.2*slowdown_max; /* need to add small number to avoid domain error in log(0) */ slowdown = GA_MIN(slowdown,slowdown_max); slowdown = GA_MAX(slowdown, slowdown_min); /* scale current time according to the slowdown factor */ #ifdef DEBUG printf("before scaling %lu ( %ld %ld factor=%f) ",cur_time, slowdown,oldslowdown,(1.0*slowdown)/oldslowdown); #endif /* DEBUG */ cur_time = cur_time*slowdown/oldslowdown; #ifdef DEBUG printf("and after %lu\n ",cur_time); #endif /* DEBUG */ oldslowdown = slowdown; fraction = (float) slowdown/ (float) slowdown_max; shown = -1.0; DisplaySlowdownValue(); XawScrollbarSetThumb(scrollbar, (float)fraction, shown); } /**/ /* JJU: void JumpProc(Widget scrollbar, caddr_t data, caddr_t fraction_ptr) */ void JumpProc(Widget scrollbar, XtPointer data, XtPointer fraction_ptr) /* Called when the middle button is used to drag to the scrollbar. The scrollbar is moved for us. */ { float fraction = *(float *) fraction_ptr; interval = fraction*interval_max; interval = GA_MIN(interval, interval_max); interval = GA_MAX(interval, 1); DisplayIntervalValue(); } /**** slowdown ****/ /* JJU: void JumpProc2(Widget scrollbar, caddr_t data, caddr_t fraction_ptr) */ void JumpProc2(Widget scrollbar, XtPointer data, XtPointer fraction_ptr) /* Called when the middle button is used to drag to the scrollbar. The scrollbar is moved for us. */ { double exp_fraction; float fraction = *(float *) fraction_ptr; exp_fraction = pow(POW_BASE,(double)fraction); exp_fraction = (exp_fraction-1.)/ (pow(POW_BASE,1.) -1.); slowdown = exp_fraction*(slowdown_max-slowdown_min) + slowdown_min; slowdown = GA_MIN(slowdown,slowdown_max); slowdown = GA_MAX(slowdown, slowdown_min); /* scale current time according to the slowdown factor */ #ifdef DEBUG printf("before scaling %lu ( %ld %ld factor=%f) ",cur_time, slowdown,oldslowdown,(1.0*slowdown)/oldslowdown); #endif /* DEBUG */ cur_time = cur_time*slowdown/oldslowdown; #ifdef DEBUG printf("and after %lu\n ",cur_time); #endif /* DEBUG */ oldslowdown = slowdown; DisplaySlowdownValue(); } ga-5.9.2/global/X/xregion_util.c000066400000000000000000000060341500715745200164520ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "xregion.h" void Error(char *message, int err_num) { (void) fflush(stdout); (void) fprintf(stderr,"\n\nError was called.\n"); (void) fprintf(stderr,message); (void) fprintf(stderr," %d (%#x).\n",err_num,err_num); exit(1); } int in_display_region(int ilo, int ihi, int jlo, int jhi) { return( !((ilo > bottom_edge) || (ihi < top_edge) || (jlo > right_edge) || (jhi < left_edge))); } void set_config() { int found = True; Pixel pix_val; Display *disp; XColor xcolor; XColor spare; Colormap defcmap; char *bufptr, name[40], buf[80]; char path[128]; FILE *fd; disp = XtDisplay(overview_shell); defcmap = DefaultColormapOfScreen(XtScreen(overview_shell)); if (XAllocNamedColor(disp, defcmap, "black", &xcolor, &spare) != 0) { DEFAULT_FG = xcolor.pixel; } else { fprintf(stderr, "unable to allocate color 'black'\n"); } if (XAllocNamedColor(disp, defcmap, "whitesmoke", &xcolor, &spare) != 0) { DEFAULT_BG = xcolor.pixel; } else { fprintf(stderr, "unable to allocate color 'whitesmoke'\n"); } if (XAllocNamedColor(disp, defcmap, "green", &xcolor, &spare) != 0) { SELECT_COLOR = xcolor.pixel; RBAND_COLOR = xcolor.pixel; } else { fprintf(stderr, "unable to allocate color 'green'\n"); } if (XAllocNamedColor(disp, defcmap, "black", &xcolor, &spare) != 0) { CANVAS_COLOR = xcolor.pixel; } else { fprintf(stderr, "unable to allocate color 'black'\n"); } if (XAllocNamedColor(disp, defcmap, "red", &xcolor, &spare) != 0) { GRID_COLOR = xcolor.pixel; } else { fprintf(stderr, "unable to allocate color 'red'\n"); } sprintf(path, "./xregion.config"); if ((fd = fopen(path,"r")) == NULL) { fprintf(stderr,"config file '%s' not found, using default colors\n", path); found = False; } while (found && fgets(buf, sizeof(buf), fd) != NULL) { if (buf[0] == '#') continue; /* a comment */ bufptr = strtok(buf, " "); if (bufptr) { strcpy(name, bufptr); } bufptr = strtok(NULL, "\n"); if (bufptr) { if (XAllocNamedColor(disp, defcmap, bufptr, &xcolor, &spare) == 0) { strcat(name, " Not Allocated"); } else { pix_val = xcolor.pixel; } } if (strstr(name, "Not")) { fprintf(stderr, "%s: check color name '%s'\n", name, bufptr); } else if (strstr(name, "foreground")) { DEFAULT_FG = pix_val; } else if (strstr(name, "background")) { DEFAULT_BG = pix_val; } else if (strstr(name, "selection")) { SELECT_COLOR = pix_val; } else if (strstr(name, "rubberband")) { RBAND_COLOR = pix_val; } else if (strstr(name, "grid")) { GRID_COLOR = pix_val; } else if (strstr(name, "canvas")) { CANVAS_COLOR = pix_val; } else { fprintf(stderr, "Unknown entry: '%s'\n", name); } } fclose(fd); } ga-5.9.2/global/X/xregion_view.c000066400000000000000000000323701500715745200164510ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDLIB_H # include #endif #include "xregion.h" void create_main_window() { Display *disp; int screen; disp = XtDisplay(overview_shell); screen = DefaultScreen(disp); /* Create top level shell widget */ top_level_widget = XtVaAppCreateShell("xregion","XRegion", applicationShellWidgetClass,disp, NULL); /* Create form widget to hold everything else */ box_widget = XtVaCreateManagedWidget("box", formWidgetClass, top_level_widget, XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, NULL); /* Create the label to hold the title */ (void) strcpy(title, "Array Access Display"); title_widget = XtVaCreateManagedWidget("title", labelWidgetClass, box_widget, XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, XtNx, 10, XtNy, 5, XtNwidth, 300, XtNlabel, title, XtNborderWidth, 0, NULL); coord_widget = XtVaCreateManagedWidget("coords", labelWidgetClass, box_widget, XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, XtNy, 5, XtNhorizDistance, 10, XtNfromHoriz, title_widget, XtNjustify, XtJustifyLeft, XtNlabel, "Coordinates x, y: ", XtNborderWidth, 0, NULL); /* Create the Quit command button */ quit_button = XtVaCreateManagedWidget("quit", commandWidgetClass, box_widget, XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, XtNx, 10, XtNvertDistance, 15, XtNfromVert, title_widget, XtNlabel, "Quit", XtNshapeStyle, XmuShapeOval, NULL); XtAddCallback(quit_button, XtNcallback, Quit, NULL); /* Create the Start/Stop command button */ start_stop_button = XtVaCreateManagedWidget("start/stop", commandWidgetClass, box_widget, XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, XtNvertDistance, 15, XtNfromVert, title_widget, XtNhorizDistance, 10, XtNfromHoriz, quit_button, XtNlabel, "Start", XtNshapeStyle, XmuShapeOval, NULL); XtAddCallback(start_stop_button, XtNcallback, StartStop, NULL); /* Create the scroll bar for the interval */ interval_label = XtVaCreateManagedWidget("interval_label", labelWidgetClass, box_widget, XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, XtNvertDistance, 5, XtNfromVert, title_widget, XtNhorizDistance, 20, XtNfromHoriz, start_stop_button, XtNlabel, "Time Interval", XtNborderWidth, 0, NULL); scroll_widget = XtVaCreateManagedWidget("scroll", scrollbarWidgetClass, box_widget, XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, XtNvertDistance, 5, XtNfromVert, interval_label, XtNhorizDistance, 20, XtNfromHoriz, start_stop_button, XtNorientation, XtorientHorizontal, XtNlength, 100, XtNthickness, 15, NULL); XtAddCallback(scroll_widget, XtNscrollProc, ScrollProc, NULL); XtAddCallback(scroll_widget, XtNjumpProc, JumpProc, NULL); /* Create the label widget which displays the interval value associated with the scrollbar. */ (void) sprintf(interval_string, "%4d ms", interval); interval_widget = XtVaCreateManagedWidget("interval", labelWidgetClass, box_widget, XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, XtNvertDistance, 5, XtNfromVert, interval_label, XtNhorizDistance, 5, XtNfromHoriz, scroll_widget, XtNjustify, XtJustifyRight, XtNlabel, interval_string, XtNborderWidth, 0, NULL); /* Create the scroll bar for the slowdown */ slowdown_label = XtVaCreateManagedWidget("slowdown_label", labelWidgetClass, box_widget, XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, XtNvertDistance, 5, XtNfromVert, title_widget, XtNhorizDistance, 25, XtNfromHoriz, interval_widget, XtNlabel, "Slowdown Factor", XtNborderWidth, 0, NULL); scroll_widget2 = XtVaCreateManagedWidget("scroll2", scrollbarWidgetClass, box_widget, XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, XtNvertDistance, 5, XtNfromVert, slowdown_label, XtNhorizDistance, 25, XtNfromHoriz, interval_widget, XtNorientation, XtorientHorizontal, XtNlength, 100, XtNthickness, 15, NULL); XtAddCallback(scroll_widget2, XtNscrollProc, ScrollProc2, NULL); XtAddCallback(scroll_widget2, XtNjumpProc, JumpProc2, NULL); /* Create the label widget which displays the slowdown value associated with the scrollbar 2. */ (void) sprintf(slowdown_string, "%5d times", (long)slowdown); slowdown_widget = XtVaCreateManagedWidget("slowdown", labelWidgetClass, box_widget, XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, XtNvertDistance, 5, XtNfromVert, slowdown_label, XtNhorizDistance, 5, XtNfromHoriz, scroll_widget2, XtNjustify, XtJustifyRight, XtNlabel, slowdown_string, XtNborderWidth, 0, NULL); /* Now add the actual canvas ... */ canvas_widget = XtVaCreateManagedWidget("canvas", formWidgetClass, box_widget, XtNheight, pict_height * scale, XtNwidth, pict_width * scale, XtNvertDistance, 20, XtNfromVert, quit_button, XtNbackground, CANVAS_COLOR, XtNborderWidth, 0, NULL); /* Add callback for exposure */ XtAddEventHandler(canvas_widget,ExposureMask,False,Exposed,NULL); XtAddEventHandler(canvas_widget,PointerMotionMask,False,running_coords,NULL); /* Now add the color scale ... */ map_widget = XtVaCreateManagedWidget("colorMap", compositeWidgetClass, box_widget, XtNbackground, DEFAULT_BG, XtNforeground, DEFAULT_FG, XtNheight, 350, XtNwidth, 80, XtNvertDistance, 20, XtNfromVert, quit_button, XtNhorizDistance, 20, XtNfromHoriz, canvas_widget, XtNborderWidth, 0, NULL); } /* JJU: void running_coords(Widget widget, caddr_t data, XEvent *event) */ void running_coords(Widget widget, XtPointer data, XEvent *event, Boolean *bln) { char loc_str[40]; int x, y; x = event->xmotion.x; y = event->xmotion.y; /* sprintf(loc_str, "Coordinates x, y: %d, %d", (int) (x / scale) + left_edge + 1, (int) (y / scale) + top_edge + 1); */ sprintf(loc_str, "Coordinates x, y: %d, %d", (int) (x / scale) + left_edge, (int) (y / scale) + top_edge); XtVaSetValues(coord_widget, XtNlabel, loc_str, NULL); } void setup_drawing() { int i, x, y; XGCValues gcv; /* Set up the drawing environment */ display = XtDisplay(canvas_widget); window = XtWindow(canvas_widget); window_map = XtWindow(map_widget); screen = DefaultScreen(display); visual = DefaultVisual(display, screen); depth = DisplayPlanes(display, screen); (void) printf("depth = %d\n",depth); gc = XCreateGC(display, window, 0, (XGCValues *) NULL); XSetForeground(display, gc, GRID_COLOR); gcv.font = XLoadFont(display, "8x13"); if(!gcv.font) { printf("error font not loaded\n"); } gc_map = XCreateGC(display, window_map, GCFont, &gcv); Setcmap(); /* Make image to match the size of our canvas */ x = pict_width * scale; y = pict_height * scale; pict = (u_char *) malloc((x + pict_width) * y); image = XCreateImage(display, visual, depth, ZPixmap, 0, pict, x, y, 8, x); /* Make the byte array which will hold the access data */ if (!(grid = (u_char *) malloc((unsigned) (pict_width * pict_height)))) { Error("failed to allocate grid", -1); } bzero((char *) grid, pict_width * pict_height); /* Make the byte array which will hold the access flag */ if (!(flag = (u_char *) malloc((unsigned) (pict_width * pict_height)))) { Error("failed to allocate flag", -1); } bzero((char *) flag, pict_width * pict_height); /* Make the array which will hold the integral */ if (!(integr = (double *) malloc(sizeof(double) * (pict_width * pict_height)))) { Error("failed to allocate integr", -1); } /* Make the array which will hold the last access time */ if (!(ltime = (double *) malloc(sizeof(double) * (pict_width * pict_height)))) { Error("failed to allocate ltime", -1); } for(i = 0; i < pict_width * pict_height; i++, *ltime = 0.0, *integr = 0.0); /* clear the array display */ UpdatePixRegion(0, pict_height - 1, 0, pict_width - 1, 0, 0.0); DisplayPixRegion(0, pict_height - 1, 0, pict_width - 1); } /**/ /* JJU: void Quit(Widget widget, caddr_t data, XEvent *event) */ void Quit(Widget widget, XtPointer data, XtPointer event) { exit(0); } /**/ /* JJU: void StartStop(Widget widget, caddr_t data, XEvent *event) */ void StartStop(Widget widget, XtPointer data, XtPointer event) { /* Toggle propagation of display */ if (working) { XtRemoveTimeOut(timer); working = False; XtSetArg(arg[0], XtNlabel, "Start"); /* Reset button label */ XtSetValues(start_stop_button,arg,1); XFlush(display); } else { XtSetArg(arg[0], XtNlabel, "Stop"); /* Reset button label */ XtSetValues(start_stop_button,arg,1); timer = XtAppAddTimeOut(xregion_app, interval, TimeOutCallback, NULL); working = True; XFlush(display); } } ga-5.9.2/global/X/xregion_xcmap.c000066400000000000000000000030021500715745200165750ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "xregion.h" void Setcmap() /* Make the color map ... */ { int i; XColor color, spare; double cscale = 1.0 / ((double) (MAX_COL-1)); double hue, saturation, value; double redvar,red=0xba/255.0, green, blue=0xd2/255.0; redvar = red; colormap = DefaultColormap(display, screen); /* Linear interpolation on green */ for (i=0; i=(MAX_COL-5)) { redvar = .4*red + .6*red*(MAX_COL-i)/5.; } color.red = (short) (redvar * 65535.0); color.blue = (short) (blue * 65535.0); green = cscale * (MAX_COL - i); color.green = (short) (green * 65535.0); } if (XAllocColor(display, colormap, &color) == 0) { Error("couldn't assign color",i); } cmap[i] = color.pixel; /* now set the "accessed" color for regions that were accessed */ color.red = 65535; color.green = 65535; color.blue = 65535 * (220.0/255.0); if (XAllocColor(display, colormap, &color) == 0) { Error("couldn't assign accessed color",99); } cmap[MAX_COL] = color.pixel; /* (void) printf("Colour %d red=%x, green=%x, blue=%x, pixel=%x\n", i, color.red, color.green, color.blue, color.pixel); */ } } ga-5.9.2/global/doc/000077500000000000000000000000001500715745200141315ustar00rootroot00000000000000ga-5.9.2/global/doc/README000066400000000000000000000007261500715745200150160ustar00rootroot00000000000000Documentation for Global Arrays ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - user.pdf -- User's Manual for GA 3.0 in Adobe PDF format. - Supercomputing94.pdf -- The original SC'94 paper describing GA. You need to use Acrobat Reader version at least 3.0 to view and print these documents. This program is available for free from: http://www.adobe.com/prodindex/acrobat/readstep.html More documentation is available at the GA homepage: https://hpc.pnl.gov/globalarrays ga-5.9.2/global/doc/Supercomputing94.pdf000066400000000000000000002746521500715745200200450ustar00rootroot00000000000000%PDF-1.2 %âãÏÓ 2 0 obj << /Length 3632 /Filter /FlateDecode >> stream H‰„WMsÛH½»ÊÿGjKbø%ŠšÓ:_Ž3IÆ{jñZd[ê„dsؤmí¯ß‡nR"3[;•Ä‘E6€BÚ_^¼º¾ io./BRty‘ÅþvMiâg”ĭµn©•—ÿº¼x}ï# éþïR€?EÛ ZoýdC÷ÕåEÀ†ºÏíÏçË /\Ü¿¼xwÏnðlûlü”âuêÇ ­’Ðß„3G£ùáåÈ&Ö½›»»/¿P¤ñj»Ž×>½ÑͱUûCG^þ° p»MèæÝ»w>]•%ÙGŒlŸdáÛ€\îß^^¬?â ö›w+[£kQRo$éGêÊP%:Ù*|‰Ïl+Õu0Cô³|’íbø½¥{bŒÒ5uÞšVÕÝ+üßïJe?™zÔ-‰Ç;eT½_üyÿñòâ÷çpÖ.ux¥iu¥;eCjú¶ÑFÂ÷|±¸ÜÄ.6%î€êޱhP¥êŽ P`†ÛÈG›0%ëßmìw|) åº`pˆv²\{Òâ]U [ª;w9f4?² °tð–£T![Û¼8i,´H“‰ygV#ILY)J׃E“‹Ò¢¹-âÀË¢iJ•[>ÝÃêÐù«F뉽¤`I½šš ƒh0ù“sôù3b.LÃà™‹ˆ¼™ä«ã-gF{gô(;2º’ÓðŒÍa ºaÚÐHlÕ”ò…)9–œÕÑóA•rf4réôDi4uíqà%Ð\ÍTH‚Ðñ¢Îmn›Ñqe©EÁ…  RÚžÄôpXZ„ïŠ\׺’Š[Ÿ# Ó± EýØÉÚúÍØïêc¨´¼“ugJ=]’ÆTC"ÁG§(sà€*D7ÃT˜B/Õæçrm:ãß3Õ7Âà&ÝÁ™°ƒÅ´`ÚzZä‡%å Áb–…õhuFÄ×W‹%`/lšw˜|Æ6ÁNb¢c¨bžö0î÷xÌ>ÎbÝ Sy§»áØìš%•R"ìS›oyêZndÛà«IvƒxlPt}GòEæý¹~È#g5—Mǰ¿¾â:ÙÀQ2—€l,Â<¥cßu•x9°—[{ºßäù ƒÍÀ+¶NdáüùæóÛ™ÝpLê©ë]u3G¨Ž™c¬1 ¡Ö½A ‹ÜZ-õ=XB èü‡™£ ôÎ#5‡£á×p®˜Ì9HVÁ̼€üÖP`ÕLÉnnÕMS›%'òÓÍ@}´;UãÛ`{[‰&ûc1„’ó´8(([>Vðq}uŽ•µbèÚ×+Õ®­bœp¾žøswtdí¦1™£éd… U—}1RîM=ÃÁz;örúöݧ†aæÝ/6‰›_ Ÿ[ä|ÏËžý™în!_<S®±Áià@8ÜKúÝýÕ ›&¿Þ}]El ‰­ûZ±ÖÊ6ë«“AËHñÆÒÖÈH€„†bGq-;§C‘ßkõâTi'Vs¥ËS?w“SõqU¸&~è1òÞ¸fäÕÞ)”SejUE©¦nFÙè`(ÜÒq/ ϽÂGUË‚ÖJ]´êV–=ùNZÏà š""°º…ñ€®q’¨yéÎ8ƒzϲbDgãh9ŸÎÔHyS§‚9`$±éE©þ㮎YVúƒÉF-9-±‰ÖÄÖI¬ìZ]ô9ŸNES0›”•O[Ua5L 8ágQ­í#-¤}oã=èg¦ÃÆI[ëc…=޵5ìñ†3ì^á0YOªÂXBä4W>ýÖƒ…%¥M8Âp.ÂL›*š’u©ëý §* s@µÝ¯\!]Ãl_ñ…°åªÁžK·§ z/YL•ô{ÝN âDøHøø­B+M;='È(EnjÕ)aw¶ïÃí›77X^¯[{üÀ[cyºšd`fÌi'þŽ¥fIÉ9ßt¯Ý¾ŠØRKÝØúˆr¯[Ph¥ò©±í@j6?˜ôÏXQîÑÐXW™·d~¨Õ_=Ê\K& s}y›rodÎó²V6=Æ*ÐŒ¼¡ªݘ‘-Üš:k”ÓZÐõåEDÆïð¦ôL é”>Ó·?*Õlíov 4‰¨²²~ø¥¼¼¸› y~Æ+Ä(âm¼løDÿcá4 ªú²Sœ©»Õ®ÆV~‡V}ŽÈÞdá vÜ,æWãwÿΧ·’qÇ,Æ}ó®–<ݽý‘Öka `Ñ›3ÚUá¦6ê0p Ò*¨Ún&˜%««7AºÚ¤_?¡¡3ìKó7 åþq:Âø~¶ñ7“}—©uœ·#É-R(è*Ë«ddx§*pê³´ýÁhÍ5®&ö *aß›.2,d†DkÂ?,–è¡Û/ŸÐAv„‹iŸs3œ‹4eŸ(³ÈµU£Ï *í˜Ïøp/³Èµo7=ãç O“^z"¤Ö›v(^ѹœ*"ììÑçä?½Õ¬1<7¡„›€Æ`«Õäl€«Mw8º+PÈ4žƒIVqoâ¸Ù¦ ôíŠvmd#k< À$o4‡±[÷Ðà9¬³% !Gq™ú“ÎÀÙæ²î˜VÝ`ÿŠIa¤QŽîø´;¯ÇO¦ž÷l÷èíÞ‘ùÉÓ÷G$#.(û´ÝA?´Ì+ œµÙ;nIä†í[×HÅÐ<]}<ñ^3#óZú‘Dã÷Uj–ëŸÜ4¬bðDíöhæAÓêb|5Ùb/Ú•šaޏÀ6ÌG­îƒöÚªp»hpÓ gR]LWŒ9!‘V>n Òê»Ò¢ø¿Bk°Ó1íú6Ýþí‚lþF©,Vs,|R80¼õéÜ|\Å›zƒLš—$ú·b\Àc㼚“t;ø6ó”óSE½p/õŸ§s×lSs†ñ»j~±µŒÂtê`:ço®ê¨ézƒÆš.\>ÚØí&l¬è¡„ìïL”–à¬\–BöŠ—kÏZ;á†ÈàtüÕ¯—v9JRæ>*Jh‘Þ•|6äÇÏdnzäã)ÞæÜ2håH þ-¢äÔc³Žâûìp¤%›ƒoÞÅ F=Z÷ ¶Ó{åƒAÙ6—wMq£wÝ‚ä0AQÓYAŒEt·e RCUþÏo.£‡)8¿•ì_Õû]Ó€Þ@áJpx§ªxƒYSöò1ÚaŠT.•R'SæõÀÂ.ŽþÈËwØíP8†Î!n^—­¡«Èh`þ]f‘úD)e‘kÓúÊUžIŠV‹^Jš4ÊÙVz؃Èlt^BÔÿC)…åªU˺)„Åf‹¿0Ð/c©{’žøÁP¸ ­áêIL¨úH‹¯)ñàÁJÕ?;L[ðBE²µ Ó±ç$Ð œÐ4ÉŸß>'/«×YòüåùþëËß}ûãéeƒ¯ŸQÜv¨N_g‘\ÝäøZ¢OÉKö:óŠ@µ-¨—åéM¶Xq\ä6‚»yth. h8cyôUÓ“³zò•œ”9ç (›W#±¡UW8LÁêÅ3´lLÕq¶®È` ÌUêoõŇJɶÓt½¸™²BÁÄ×Z”ÔD˜YB'F1*3ÿÎdÞï°-ɽµ|âÉ> /ExtGState << /GS1 8 0 R >> >> endobj 11 0 obj << /Length 3496 /Filter /FlateDecode >> stream H‰´WÛrÛ8}w•ÿÔ”Åð&‘zÌÍ™ìLfRkm탈„$$$¡!HÛš¯ßÓx‘d§¶jk+Uv,¾œ>}:d»ë«7ŸîB¶Ó×W!“ìú*Ký,a‹ÀOXGl.üpÅq}µýåúêÝn#²õgY€‹ƒ.% ?HÙºº¾ ÈpÀÖ¹ùùt}åE³õ÷ë«kzß-?^²4Z±(Î|zg™d'¯„1Ž_ýÙÈã‰u? "cûÞ;4*Z3Y3ξ|þòxÃËR” _í^±œ×øºu[giâgÞÍìÛúdqÂÞ‚­?Ë™3Ìõ±Î÷ªU§qe™úKï†ñº`b; W0ðÇË\‹ŒçÆ‹RídÎK¶)UþC3µµo¬!Óaº´ 9욎á^!uÛÈM׊‚Uÿ…™ö$Û=««>Þª†‰çC)sÙšLZkAbSàåJDÃ[©j¶92ÕîEÃ\Z„öÙçšµ{©‘a}y{ÃdËð§–•,y3±Æ.ôVá†`zÏQÌ+Q©æØg³’õŽUª¥Ï~UOâQ4³Eä§È îLà bî§·öã¥VÈÔZ=•¢Ø +¼…c•j+xËc%Ì6¬UÓ`CkÊ¥×jä)´ADȦzÒÖˆùF¶GØ`1$UœÚB{”I¥DªÉV§Eá’&´è³¦ÏÒF¦+¡ç£Egï¨5Òä›/#?Zflùaä Æ=Æþ=ËOÌíù£`²:”¢ àJ)7 o$rDNµJ•šÞÔÝá šÖæyîlOñ$ ë Upȼ¢îx${ÈŠÚ²\U`®aú¨[QQtu^v¼žf}¹êcŸáWÉ>|ü}¶Z Þkòý-ùN~EÓíèr–¿ûÂî¾ÎCöà¡26U6æ0…cÂæ”0Ñ臙û J·îþê€@öÛÝ?çBKÔ]-Ñ`4ŸV3M]3}Ư'‰‘Ü˧QËdäÓì9É䟑bú¸#ÞE)°EÅ}\Æ«c9°s!»Á°+yÑ…”²0rÞNëu¹|ÅXײ“ÉöÕk ´S/H§‘F«Õÿ=Ò$\À“H“ÿ-Ò$^ø¨ÿ«‘âùn„]l0R¬æsÄùIšž2I0Õðˆu–ü6/ÓŸ9„ÏnáÄ.³tå©Yà1þ¨dA-Œ‘6je"‚Àj}o£Ú¶ÄÄÉÜL JDÐ0³´ÖÙ}ú ´aV½)OÅŽñœÀ9㤪ùmÈ&/ŒûÐàwm¼¯:3‰Î)Þ¨ÉNÙÓYoò>ûfi}à_7ðMt$;rK Q¿l˜†Ôˆª.æªùéUÓÛ&Dï•ú8:RŒIŒÝ[kµQ¥ZÒæ”æ°ÆØF¯&QúëûÞÎ,i¢rÄÎîÎÌ{ó(-X.’¢ÈTŒ¯€KÒØ”nS­ÅгǯjzO(‰wŠ2‚çÌšX\ _*(˜ 9"8FÊÙ9ºèÙÜA3ÚÊã¾ËÒ𠇚l/_5MmÔ?QjrêXT6·çä¶&I¸Œ£Yl…)u`Ô"p-e±ÏÓ}Ÿùçq;áÌ¿Tuž\¼ÀÓÿS)4S¤;_¼•ªâp‚Àó…Ø]ô–Fáhh„Ú¥… ç‡ •¥–œ&á”ZRZT{fNÏû-ãj1×1¬¸èë$”ÆôUßÖÔ{—†¦nÖ5€HQBÅ7íjß ¡ÑÒEO‰ùóŠà[Ú-¹/ lx=³¸ëãÙ‡Ûû†mœSw¶F5 .s¦3Ú‰‘zÚO¤¯bÅáHy„­–2›´(Î:6«*óäžL•:G°Óî´Îd§ êhÒ{—4ÄSA¸+ì+¡^æøºx@ +–r|máÅiFõÇìï¹L ÕaÊçW©Ó_¯ø{´º^~ë´¢¢µ´¨¶ð5ŒÞoc°³h½tM#Õ¦| Û*/|¿Y}¾ùpG6èxzm4ÐAÜF“»àÑÎ $¡D9bÞˆ«:Y[à/Úž1cyšC—槺;EYçIýµîºƒ;8`8‹eJÛhóqgzNté uôH&ã[΄uN…ëäZÚ5ß?¤wÎÃÉ2¿Ò !±Ø„ Z©°ÌJ4Ζ™ÕM.´Š~îm;OÈ«HMZ¢Ãz*–^ìGK4škOÉ´°1™´s³A؇³"ßdÄàŸ68ÁÖÝ‚/¿Þ¿û+À^=‰ endstream endobj 12 0 obj << /ProcSet [/PDF /Text ] /Font << /F2 4 0 R /F6 6 0 R /F8 7 0 R /F9 13 0 R >> /ExtGState << /GS1 8 0 R >> >> endobj 15 0 obj << /Length 3133 /Filter /FlateDecode >> stream H‰„WKsÛ6ÝkFÿK)#1|èA5«´y´™f¾ÎØß*î&! I°i[ýõ=)R’›éÄv%ò>Ï=÷܈¦“·Ÿï"v0ÓIÄ$›NÒm®Ø: Vl•Äl­ƒhÇ´˜Nöo¦“Ÿïñ§˜Eì~gYˆÿR–„½´Zá–Ý—ÓIH†CvŸÙŸÏÓÉ,™ßÿ5|¼'7øn½ ’ ÛÆ;'i@~6«tä% ­qüꞃ$9[_†A%Öø·ÙC¼ZÏ—Q®f_çaÍ~ûúÕ\ó¢…4%“†ÕZ=É\ä¬5²:0ÎʶhägÂÆëåüÏû/p¬VH;ˆÙýø Â4v)àIžLVìù(³#ƒuV©jùù=ËyÃì!I6…`¹0™–u£´Y0^å6õû7}-Œbªb\ $\2ã ¢Ršµ•ü»¬QLÀó‘öõeVºN»Àâíuú¸—„a¯X¦ÊVÉ{–ÍÑVÍQhöxb™¼A1^I|ãG Œgd– GÙJÓhùØRÜ%ÇŸøÎfŠ’ ¹‡™Ü£Ë«¼¥ùÃÜzVÕ“¨©*^°–ùA,kn=¼šp½Önƒ­i}<d\§Qœ…ʾ?K#LH*À+I¯“™‡¶àVÐn^a¿pÝ#QXô&o³†©=“Ú?êÝ]¥ ÿÈÓPÏmùù‹|­©£Ô>@`»)«\Ô?ªaÙr›S•µªTkè£ì&†í¨ì\0ëÕ‰5Ïj™ËRTÆ5¡æ AcÁ¸êð %º¾YXT©¶’ÿn¥&|dJÕBsÊ™:Mu㵃9>º,N¦rAE!9\þö·†þN<ÁeÁšS P™Kx0m]+ô-§ÉÍŠ6§ ’xu ~­¯a—ºéѺuÚ÷`»w`¦è3 c¿UvX­dÕÀe¹Ôdü0¿  nt6(l+›hGãÂŽ8ý0ØýôkˆÆÄK-2j hâQ\–S¼4B«„À_¯×q»¾.äsð†‰lKçìÈŸÛsDá‹‹ÏŒ*Å+åKVž5Q{‚šbÑÅ/Ž9L¡žÑýÞöráÎGJ`B•8M‚ž¯·ÁvFåm®‘.¬ïçqlf{¡EE” båJX M»x}'º \¢˜‚¶ }ú.•ëöî 5@>‰â4ß®àÿWøÍW¨îlA~F.’ÎG…J‚¦\Œ!B÷1Âsó,Duᛊ2vî‹3°î'cl»ÕV›¤„ÿ£ñávóƒÆÙ䢺”(œ[v7úøÊÂŒ’´gˆaŸ)Bƒ¥(òs¦Å‰ ØGðÄDa'Àðp^ÖÞà5vÈæ¡Å¢Ç´t3qQGZ§/<e^‹Br#¶ ƒ4N/ŠÕCÄNa º*F4l¯U鹋ö-jdŸ¢áa{Uø*-ã ÞXã1™ìh¹kÐoöÿªßûõOó4ŠAY É |Én¾/;M3˜Á¾(Ë!ì¢ÐÏßhEŸ °§'|# õ/@Ó–Ãh_’®±ÓGø;&Ûè„-ņHp“I·š¼Ô¢ôõ˜ -öaRÂ}B·bé*cŽœðó$uÓZåPªárŠc€iÏ3YÈFRÞØhnA K XFˆ¼šãA‚¡uìÇkHr/ˆ0d làŸžáÍ0Ï8îL¢_¼Ä£à§¾ƒï-w²¯Nó¹Þä }©ö[5’I7„ZÇàŽ™¨-E`sÚêój$”ž/㢦¾^ÏÌ<œuªdy›š©D‹ŽèˆöCq9:é+‰L í«`7B;È¿šM<¿`<`—¾Å(€ó'YùÍ·IcÊ[n¬SÇ$£0·^qaΩ͹¡›BT„[µî‹6qâN¹uwy½± í“çã*ÚlܳIÿìÙáÂ’Ë[/ç@€Z@øØå‡ —~Å€ÓÖñÚ£ƒêasw¤òТƒæ¥ñ7Øç÷]Ñ Ù-.¬•w)ù ¬Òó%Ròï´í¤Žsá÷¡Ýê[Ư …g <ç\_`ýì+¢©NÄ‚·åþD‘¡eП¬!û<Äì Jö é}fInØWöíÏå(hº  x¢$ v;Vv®ÿÿb:¹sWíÎÖ}çoÚ(JmÉmcƾ¨cÅîxQCc•ùùˆ³(u×å#];=`8°«kDH-ˆ"iô`†[bÍW¥´<åt:Ÿ°u.¢š(Ép=¸ÜœcU›­6±}N¥¦R-†_!&Ñ}#5SÏgÑ?ë^\8~þmê+^Ä5j‡|´”©;L'I„=“²m¼cq’bÌØr`h1ì߸ò¾ý(³û=ÞˆÂ#Ö¿YØM¬Í0yŒÝuÇë·Þ~Š;Ký¬$IB]Û†15Õ6ŽYÝt>xJ~"ÊȨÌJËœwÒÒÈÕMÍm=°ê§fÈÞý݇ÒH°1èÙ©"áQ¡¹Ø±#þŒ;~¹»±.·Âà¢$i¤Ï¬œ±nFd™$=WƒËþ×ÿb”SGkð¶¹u0°.ò±úöcÝ]±òk'`ï™í~WÉq—9•ti¬x1Ú×ÉÖ¯ŸÔ9V–nN~Ó•nÒdØ’vshqYÐå%S¸ÄÜzÔtª>©ï¡ YµTˆ€hwöÓôÓyÓ•mîHlãÒCZÔ®5?Í7)´ä‚šØhÇç0'•Ã4½ÆC0ï\ ñdÕ‹ú×Ü…Ÿaæçbb!ÕtÌâæ/ðCÔ{C×Ϭ‰‡ù»‹³¢‹àÚ;ˆ éœz÷ïÎâÏ¿¾.Í b\ÒÀ½ÙOM瘦Æã|öÃîšé'µS×ãHøôpr-'(ëÒå·¯@¥'°õ½:õw“Uv•ê±>Ø`^j\Bݽ2Ò/DìUn5Ù´ß=K#.‰ÕÞZôNgoTI÷4Äa²ói%~JFuß h»…»Ïœ”´6H·ekña-2¬Ô>Ñ7¯¿N˜’R¤3Ž5Ïj™CVFÚmæ p ¢H8pO—6°@C[û+ÑÜ„‘Ÿ‚¶’¼¢b ú³›{w™`”èºT²"iŒÎ?’f }EÇfwÝŒfµSÓ$¢ßÝžª*¿=PvÜ*IœöÐíkb3³iá+ã–EõVÝnf'ê7AìÀ‡ÀBÆjº–\v¦™Õh™MÈ£W„I×é`=8iœ+ûßS a#l“~þýýÝÒJã³Cgÿõ©ºëh<ïGŽä… e ùŠ­~nTÝ5©çŠá©ÕÉØ}<\ 1v°E7Š]÷òVØü4”R•¤ãîa~s(ÃmzÝì'´Ë}ä[‡û®’Ü øŽX ³Øø˜SnÉ|˜D–ØdŒ~ŸêeØ<äLÓÓku•ØÜ=ôë‚‹Oç RÂ3,¡µˆuü³›$tIÄ.y%AÙÆlÉðÚQÔ¢‹¸« ðk‹»ªª¸1šGë0°xš('B²Å*hÞ7¨ŸM˼}ÅÓ5_$åX¶Ï‚Dš`ð~·aë‘Z µ$4ÙÒÃ] œx««wÜû‘+\£H(ÕjI¬cD«1çëÍWê§Á³·n~éDA¯,`:Í/Ô^ýÉw;jÐmÚyâ> /ExtGState << /GS1 8 0 R >> >> endobj 18 0 obj << /Length 3217 /Filter /FlateDecode >> stream H‰¬WmoÛÈþnÀÿa ô•J —¤ø’â>8ñ%uïœ3b®@VäJbÂu—²ëûõ}fw)QŽíC€e‹ÜÙ™gž™y†³ÍùÙë·œmôùg;?ËR?‹Ù<ðcG!›ñ¹Ïs¦äùÙúÕùÙÛ¼g‹5Þe~2œÅs?HÙ¢9? ÈpÀ…ù}~æÅ“Å×ó³t žÍc?JXæ,Œ2ŸîIâìäãøÞ ý(Y÷ƒlnmîY·fz_laC(%ÚldÛ³J³n¥¥º“%ë;¶’¬•›ºÚT«Z²ª5NÒ»$³Q³ÍN¨Jw-í·‚.1'^¿O<ðCÃOsÖžûŽ3 øðݽâ-½GpatxúñÑÑ`žF~ÊYÊc?Ì ^d^=¢ÄÓÔ¾ ¯Cùì-'¬®Z@3 ‘*o&ê\)ÁºT¢¯ºVû“/‹žŸÍ8ÁúNá?Qû8Г@4j2'’à…BôHÈ$¦ºŸ²Rôbv¼"ðy’;(\±Žzº¾2UŽÎ)ÿ³S(5 N3ª´Ú@ÔËøà0ñH£%PÖ ±Ã`–Öv½¥;Õ¥ÀxpO=äÁ,æt ¬”,¨hJ’>Éb?ö|öµi"AÙá.`Å^)¸2flÎç§@æ15Ê®!(%•›’Bá$á‡ÔŽcÎçàZÁÀ}Õo‘wÁ®¯®/=l¥:xߢÖðͬW’×RwÇký*÷†TMCæUnIPŽœr]Îú㇥”ÇéPJQè¼#ê­»ºîîÉÐIÁSëÖ&”ª40‰Âå³J4°Q{£eɉ \Aï±$÷禅Ð'B?‹XÌshŒQ7åÃ{‚qP7ÈFï£q&ñ‹7Ä™?OÇ'šg;Gs “±7‡ÈÐÑÊnOJÄ/*m˜J` ŽbZÕ]ñÍ´5Sîçù%d¬¥€<áÁ3Ñå©q5H}LÝ£Ÿp:æøÐØé9+¶ûö›ž²ûm…ñ nÿ!U'Ë¡ÎZÓš¸‰â\ýŒÇ2|èìÔý@â’‚ †¸1- °FáýÚº&‰·îD½ÀN´ ½oŒø—_h;¥·:Lü’ÉZË{4.i{¤«•—Ò åEÓ(Ê"?´0xñ»åà’çYøÂ4ñAçÑA v¨Â”fh¤ï‹d`û^µzdwAy™î9Ú ‹R£Œ“¿‹è˜:Ç·á™@ºÚ²>ˆ¤MÝ­øM·¤úZvZ¥ÖgK4 AÕ®ä@·…ÔƒMkÌuúF”ÒbŠœ©Ÿ'é9O…s?Lü#YÁo$ËâaNY;ePqUÝá×¶š²¯ô×Wú«.«fPf¡Ïƒ4·%ó]ɲ-=þ†NL_¹¼"ÊŒšÜø¬Ý8¼ÂJãÑó$94­ÁKêÁlD—ëÅï—o^Fa|_ó)’2eø@4îæGþ†¨Ä–Þñ5búá!(nž=EhyµV{…Huè…¹ÑQ!•F)˜öŽÊÇèxÀ´n*ˆ$¢í©ê°Ù¶É[úg묮¾¹¹o&üŸÓ„'‘Ÿ„ÏÒäÿÀ±ôÚ) ›þ\ñþ²Ý­ÿ:ìdÅ4tÈ–ûg¾œœ&~ ÓsdÈ8ŠU°þ?òw!â70â}ƒp—“Îãëÿ :–;ÒßΉ¸mÎÏ"”;Ò•†9Ã2ãSÃJâ ¥}~¶ÆoO³(Š"Z°’bÏs4Ú¹¦|¤ÊjMÚ;ó\» Bõ¤°zRúN’Ú¢ÕkÓšÕáYÁÀiù0í¨Û×¥U”²0½ßÈ%#þri-ô¹¥†»…''K¤å¼wÒ QfGùÊž¼‡DÙÁŽY†¹y¸º¡Ò1‹§‰U ühdacV—Cÿ4…9”-¾ø^ÅQ¯užÑ¨•z誇-´y ™Kg‡~êæDÌ®h5#TM@Ïv—Ó$Ä<µÙàÚyï âþ<³¢Ã]…N—K¥Íh³ë¤ÚïúY© ÷[2ÐìÛª°+ úva69¢Ô1íÚz¡±7]ÁNÍ>ˆ¦A¾”u/Œº_¢ÙÕÛëZ„$ÕîíÍ µçÄ9œÿ.uÃó¡nB‡ÍGÙßwŠä!}h·éõ^÷‡àЗ‘à¬g0 L,—žÝªhn/Þ}¸¾ýÀ(«$Dã~Åš¨«•Ÿyô…Á¾ÛÑ&±xwóúêæ…(1yR_äâ»5Û‡KûãüR„Kï§ÛO³pÊn?\îŸÐzöëÇ«‘Òƒ7£bá–ð|(–*‰g®X|ÎØ÷|4Inm*†&N‡¸%µ7¦þŸÕW %—ñaAiÇOkTT·r¼1>refA~j¥:4Hõí¤(íª‡ÃB¡ŠIS i¸¥RfÎ[ÅŒg'[©³fjÜî.myN»6¢m¥‘VRû5’}äùÈœ¥·G1ˆ‚Z¡,íµâN@[¬*£'àÖÓ{âYºïXÅíøZækÐy±h†–ÞT€aoôª<‰“çƒ2GTzëIJr“jhy &iÈßÉŠÝa(ê±k Kf3î‚î;E…Œšuè‰Ý®v޳݀Tútº8– 9¶¦ðè ‰M;û©ý‘Gþ ¯z¦¸a(øWTRÆppå¥É¤J‘" °ENaÝXö0ίϾÙ’ç Ãø¤Õ{ûv÷™Ÿñ ŽÚ ½s]ÕŒ¦9è›#Š”þ¬E—õi(žØ¥„y¿'Œ(>Cƒíª~Üê£)ð`ÅsÙ1ñ¸ó]¬3TÒތâéãXž¶Ó~Øñ< ŸgärN×}© (‡A4mÇNÉå°Ü€z²_‡•Èúߥ+—˜òC%eP­ZÁàÒ9ö´ªâOìI Åä}—¾jƒg3?WÔ»Wt¹ðëäig£¤¶ Ñ ¡˜Y¯¯KpOªó—8Ê4ˆ”=˜ã×?°{PCTW¯Ñ#•Éÿ?'ï1$Do ¡è:/;‚ vvC5 ZAà¡*yš‹ÕHXX6Ö?Æø5'@Üĸf¯óQ\¹íßÚ„7kÏ@{kuܦÂäpΊøÙ`Þí¼äVg¾•1m· =«ææ“Üøá  +èälœç»H©ÕœW¿¹Ãâ¶ ÁÙRŠ­·£ª ƒ³ÐA<ÈÆwGê#GËÓ¸½—•¢KÝ®)IR°9«ç+éÑk“(V¬nö[VÓx( ŠŽ¶[¾¢¶ÚrL¤dmÚ QŠ—êéÕ.i)þȼ d .hOoâ4䴺̌¤€Kqô T<y4³¶ ›5Õzà“èXæ]ø í…™˜˜¬ z ³Y=‘?±‰>ïv­¸VW…™âÄ2ñÑÛHËXoVtK׳âô• ?æÃ¤¤-¢ó3Y˜Æ+ºþB½)ÄùAkQ‰k¯›Þ•80ï;P¶:bThz›AâÑÑißË5,÷ Í}{Âmþ¯æ\K:M TKy PöáÅÉQX6Ú›x°÷ endstream endobj 19 0 obj << /ProcSet [/PDF /Text ] /Font << /F2 4 0 R /F6 6 0 R /F8 7 0 R /F9 13 0 R /F11 20 0 R /F13 21 0 R >> /ExtGState << /GS1 8 0 R >> >> endobj 23 0 obj << /Length 9732 /Filter /FlateDecode >> stream H‰ŒWÛ’Û6}Ÿªù}:ûÛ›ßÞÇbooob¡ÅíÍr-S1›D©H§‰dz(^‰JÝÞì~¸½¹[ã›DÄb½ÃY1ÁŸ¥˜Nbº”΢ÉB¬‹Û› žˆuÆŸno³áú?·7?¯éü6K£é\,’•H¦ËˆÞ™§ËÞ+ñ„ãŸp6‰¦ÓŽõh’&ζÕfŸ+Q(kå^Eb}h?ˆ¬4µÔÆ mveUÈZ—FÔY‹­³OÄ%“™X¿f««¹·šUz£,+8ö½Q¶V[QUåŒH³[YKaõŸj$vež—'œØœÙìú¶;cd„ëÚª|‰W°)»æè ]ñ{pŽÎg¹V¦'©ë®ÅÉÒ™D8Þ7{,õqgMUÑ­cUÖeV悎]=UšŽ½x¶è¸¨m¨Š´PEYE!³ƒ6ðë ­Ø(eɇétÑ(“=‰v–zß”¬ ¦Ü±?z7þ{[“£´T2‘ëM%ñWD‰wwº~M]¾ î?Žã¿!†Þ?ªü,l!ó\< –ïï†ÂžQ˜¢5¾ivÃd­àCe©Hïë´.ÒK°ÚŒËj«ªöêVåxUMîÓé OÞ"£™²ãn¸S¤øXÚšžÒžMv¨JS6×3“ÈÊ „UfK¦d°Ûw¹êוà‰w.7FÕ§²ú&”yÔ°] Ä£.2aµ‡:_Sç3ùGOÛËÃŒ^Ü7Áq±ÉËì9ç}nÍÉÞæBÆ«m‹]¼¦ÔZqÒ(×Fuƒ ­Û 0ͤ9ÓK^V~Ïh7 Ÿ(Û;5ða–!V±þéí‡û·H¢d¾¤æ^‘û¯û‘‡{ +N…Å+5þß6 RR"¬Þ˜®KQ/lwÜî°Fê«§ °+YSlÈâ.dËRj—mŽM@%p@›­ú£ŸpÏG©+çŠu1nÎlÜ–B>tvhs/T®(cª«@•[ò£t•ó`°L|;c°O/o³ÍÕö:éÚ )Ä~=òåS?¾Yú 8˜ÅcÏâé|¥3fñA%BÜðÄvìɦoë-…Ys™‹Ò-Òt­–nDq²ôdA 㺚JX4 JÇ\Õ<,’î:–5çÍÄsUMB‡— MFÈ7‡‘8q!‘S$¢Ã>œUÌœ¥N„ý╆˜³á'…mKA/l{øHb¯ÇÃ>/72G¹*y¼:\`ˆúc¡‚h¬²]~äÐÉÍ{Gªÿ ùúP«”|L“”rü;Šõ»6YÅÎàÛ—~†Í¦h™â¯/¤7B³c6\·zÁÃ+“¹ÜP«_åLÈ|_Vh¦‚0‹„0æ Ls=tê¯n´q GÄ$ÍÆÖ’ðˆå4a-:{±OC›rWÈÌÜPç;ÆÉ%^:›ÒŒ½.|‰Ì¼@Pv2Ó¹®Ïê:sÍ$+ïS, ª«ˆÑƒZÑDÏåÌ‘kŸ6=ö¸-¤%êaT³#Ãù2š/_å˜e N¦yê_PüÙH ê͸Kšqê‡õ‰‡ÖÔà Gì-Ë¥.\t' vÜŠ<†£¨},Ó QqÝs¤ø§_™4-êt"0î+tM°(ó'©Ó¡LS×s:qÓ5ñs:ž’"usz&>vºMñSW˜]v¨x>_pC>VÀ qòÿâ«y4 3ÜwÝÆ´Ú ã%D<Õ<Ó<®ý*áõ í }Xÿ‚!⩯хéЋ£gzn¡G“ ÅʪrÜÛ_`§ñ$Â#ÿßÛ~²Â†Ž¿U4àŽýL3´ÇÜÇØ&ôK]–ù7]G=èõóp9¨ád[à*R«gì[Ò0¡\{IZ®ð4œÞ8loOõN~2ß™Zå]ÿa«[Cž J ~[—À÷k•×rĵ`~ùôá•gßžõ«Î ë\÷M(ˆ®Xp¡íGÒ$Öäµou_6.Û)NI' 1´éeäÉKø£Ü–êÓ¶u—ÕöiÔÏ,<pêöCæT ´Púµy,óGüà³ÐŽãSÑafXœ ß9ç3íCçAgîØyʼnCÕ¬~¸qPð—p]ˆÐ‡˜v2Ü~½òûü™§ E3ç ×I2óV)5ìÍ_“v„é`Ô&&ÃHuíMZµÆšÇK³ R”ÑèÇ;bE»ÞGËR_VÃdGA²È/SXÏѠدQßkÚ Ôš$*TùEW²]ä\ešBU„òëÖÍ1%¾½ÂöŽ$”p¸¸Iû_i5½õ|çòÐ!ú÷“Æ•Ÿ¼òÓbÔã[¶íŒ+7¢-GŠ]c=¯ ¨ ð‡O,GG¡²´$‹E§°œ+[ÉÑ–«¶º)€”?û ó=3\ÄÑ’ŠÜÙôX µb(šy<°Ý7€ à³&m9\€¯—Qò”±Ý«â’'CIkÈ11åÏ¿1½¼q÷AÜDÅgÚ¿zÖ’@òm‰´›Tô{Á6é™pT”$Uµa*¿@ Ôd®;yªñtܨú¤”{¤í>»™­ûê¬U•ûNgx—øCyÂ(¦V(ÄEZ¶Û%}Qõ¢ŽWóÐ!†´1ÁÞhÖC’×ÄGYiÞÇX”óÎÑjmÚí§ÞIòÇ+pˆ¬$Žƒ-3,UÑA¼€*ÓA‚+ ïºÞósæ³IS’Uaé`:ÉzI¢O€•…ºêâ/î.TúoØ2É×¢Bj#÷ìuçòÂær‡—Ä£¶´žjcªí²6² ×(³v-Äò³²qâ«tÐÆHFÀ«þS¥ö Fâð‡aÀÓ^rEÝæØ*ÇïN7B,Nêdº" f”§G½ŸÇ‹l¨÷óxÁõz?L®÷óxÁÓz?,¬÷óxÁ±z?øY§Ÿxf [©…‡ž~y»~÷C”Íí-] «ò •í?±ú­è:ÓöVízkþ¯û¿Áÿ¤ÒXC!QÏŸ½L$¤{7u*×ûMEæR #´Ó½Ñ‹æÎBÑûYSD¨" F9ûˆUļ>Ä¡Šˆ˜gš7„(bµ“&ï€PÅFä+ó˜x©ŠˆXçj7„("3ÇÝš" 2Ífq·¦ˆB/[?QEDÌsÍBAlËq·¦âT½íVѯsÜv«Šˆ˜œY!Š€ ¼Éqv2E@Ì|¶ÛnUë·Ýª" ˜¸õcE©Z¥çYŽ/çB€Ç à4Àilg1Îb8‰p;6€S§°6ƒÑ‹œÁ€À àüÀùk§/N_8{pöò:ä5€s箜º8u àÌÀ™ ˆë×Î[œ·pÚà´¬uXk'm4E©­ôeù5ÄeO_•C|‘æ†ø*½Ë—ù¥ˆ¯È-ˆ/™¥ˆo_ëÇf›˜êàäfûvwHî×Qi`ÌM{£|háuBÿ¢E0Hß2È0˜ÇMI“S4_ã,E#yÿÓ¿þúÇ¿_þùënɶÃos¢qÑ…äHK4…ÛHB'Ê…¨ŸÂäJ“)½\Dž:9Ó?î·ô¢qìò(‘ùœlrié¨Ôy\I”SºÒ‘²>Œí•È̳dgN‹|ɋ͢;7…¯mÖ,þ°ÆÐEa…xâ]†ÅK ¤B¼P*MÁësõ5…Ç…Ö¼«$pªmcìï]¦ ·V1x7V7kÄcXÛcE>e bù’½°¢,¦W "ùV@A«Cܘ‚£D„o•d1¢ãû{ÖG ¬M„wWèên-¡ÝXCÏ2èˆ\¯~.?ªañÒ†?Ûˆâe±™‚*f.ǶÎ|`î]$SR”±¿¯`ºn~WXFR ùaCJ¸àšŠPÖ4,ÚTÃUÐÅbEÒYjè¶N\`Ý{²\SÂûwEÜÚÄíÝXÖ[Œýa#X—Ý)³h<ÖÊÅŠš/ͪ¦w ±âî¡(Ê 7β|ç°Ø7ø{‘ÊäÖ&nï¦Àê°ÖÐBà.%»õ÷ši–Ê%šš'·&¸ÇBjÚ!VéÏÛ²p÷vÏź;+æû{Òl5kˆð ÙVvc ëöÆf¦Þˆ•æÿ._j@cNIÝ™8±šÉÂq×PÍy÷­ÐVËØß©Ic•ܵŠX–V ûa¯ÞØ’MTšuäH×Q+_™¸å[5i »LÖÄÞv’÷î´#oI±ì4åûTzÃÚÄíÜXÖëñ>ìq#¢eÊF7ÓTëx·+Dn]jk ¬)BeH« hüü³ŒzÛØE877k„"Ø»­ÊCNœ³)ST"mé"HŠA¼‚¦ µ$EaÜe†ƒïމ®ÒÁsLþ}èÖ*ï¦Àêfm¡…Àö"%‘Ú²^ hr%¢aQ|Q7âS(ïׇÂmñÁ§êÖ”tææ\AwÁñyÈ„îÆ&nç¦Àâ°¶ÐB䌞]¨F!TëlÊrÕTšØÔx9!QñÓÄr™êϱM)–Ã=³`+ Áíëö»©‹ðl , c‹*FÍpšaxF2†>U‘¸Ø¨¦ÒPÃSòÐwY“Ù›B_…ÛZî½ÚHNÃÞ9ÊØß©àc•¶oȺ4L-°8¡u|hRsÛÅÄ,Ð43ä`ÈŽ®5ˆIÍMnD‚rlc 4î\$[mµ±¿óŒ¬Mtç.ëÚ0¶Èbä OT€Î-L¶’È@ŽU4,f6˜g£»u¯KWÐrÄ‘mÍUÅÛõQûor®ò•ºÞ–&¹c“m]·´¨bÔŒ¦¦ÉŠyiÕjÔ&%Q°ÄA’Àu"[ë C1¥î¸1SøVIàÙ‚±ïÅ"Uc•¶o‘}i3µ¸BÔ„ýùûïÒñÓaÛIôΠè­,QpݨǻQ° è–ß-»4WçŸ&þ¥ .+ZÁ¦špÅÔbÁ½mHÜä´[v5á¶­Ó^W:«”d˜iK$=#çƒ!˜BÛÏ’ë3=¯ÿˆ™ò=MÌÍí¦óM=Ÿ›¯´z3Sën‘À`t²¤`?õ¢î_ÀûçN^]³sgg†§J¶øÔÏâtµ„z† ÃæùÙ{õ[Ê¢ð ?û¥Øô™j‰›P ø!ºB&@%g¸~„uäÿ3¯CT˹–í XA …å*MáK|·GX¥Ú펇_! ëJXeƾÛ¨ßjš!xPv?ÿa»ê‘^¹q`î*ßAñßf†<«6}7XN4‘_» ¹6’ †ЮL°Vø¤ÞEÁIäš¹YÆŽÁ\Œ ófBœÝµÞ‹óÑHÐs/Ž Ç+¥­^·èãOjìÀ'ÿÄ«{"­N¯÷¡n`Ÿti%âÙˆ³æŸx/¾/ L©l.ágX= yžEGäßâ¤Ç7Vµ\ÓŸÁíIWº' áȘ; 7FˆJ å“ ³©Ç;–®—ú‚ù!aö;gLF7s3r¡–A:v Â|øi^EÛö  þZFGZìø$:PÓkƒ£ËÐH**«Ÿ)¶Šˆ4ôI%ÜjºªŠNP¯ôõè 6Þ–ÏÐsÒ ™µ3§ä,:%2 PÏ\Jxo¿’¨ÑõmQü¸#‡Éè*8]íAÍ ïUrd6¯î:ƪÕQ¿à`a¨pæ*Êœ0ôÌ^¿àq¨ÝdØý'_è×?~vÇr‘„ªÆ¶8ÁR|™äSnBç“BHƒlE6É¡OµÎ ûeæ[‘-òuà~¹€ày~1€­Hù†ñ㜠\‡Š7°?)’E~´‹ ïÙ<àZÔH°ŸÙ$g²Ë¡Ú)¡}!~R$‹”\¸€âð ´ßŠl’3¶†ŸýÓSzýüGŠ›A®xöÖ,ÐÇ%^5c|Rd“\X“ïÙ€jÃø¤È&¹Z';Á³áÞÆÍŒîI‘Mr ¨âªõ«²oÅ—Iî+'›Õ³až;¾P=)²InV#|âG4ô@ô¤È&¹Vѳp}LedÀxRd‹ÜU­Û>α#æ4Gï¤È&¹UãüSålòwFî¤È&¹ÿ_ÌgÁx=ZônCí,g‹Œ)ƒüþY^\àHˆÙ$îÍÑÚà»–Â{ÜhÉâ“°oªo¹+ÒÝß@@Ù䓵ˆ{µ ¨~é¬È&ÑÚp¹ hÿþãW}ýù×Þ…|h™ETt®šÉGÅ –|CD·‘¯„º•ô”•cUJ lÉIZ=›w×¾@Øk£â§å‚«ˆ¶e˜®õŠÞj æ§~SËtÁ›ßÅ‹E}±Ì–ÉAnAÀ u'é§™sGÆé ßõ ìæ­ 8DW®-c,|;ø%Þ|€AÙÓ{ÛÇR=ȈGëÁöçŠÕ»Ò…KYâàe¿}»êtYã!Á»KÜ~|ZÛ땳زޢ¼‡B§‡µbûŠý­[p®æÃeCvìBCqûúaî$¶e/Ûà ëÂx–÷²¢C’ZÓÒúM¦¿­%nïRÄéa­Ørìo½±µªât†é-ÉLw–Š2Ê-]„lCdñ+¼ò¿mEZÞm-"ÖdèÒòêb†ÎŽõ­|Eí…ÕkÁ£Æúò¨Û׳mÑÒûí<ë®æï<ˆ[óìAkó.IÇÙÖ:9r²v1yEœîÖ+¶û[ÕØ½üVÿ¸ry6A' RQ­JÂ4Äð²[*Çü“F–aÕƒ´Û:¼4_·+ qÄ•…âö+]Ö—]xx¿âAè‡9ë“€°­%nïRÄéa­ØrìoÍ ø¸^E"{¿~:oÕ5½ÛG‰4n‰E4o)£ËšQÐÚ½»¤ãæÙö:à"[KÜÞ¥ˆÓe±åØßš‚ ßN";-_ÛtÓäK:õ¦»qÁmظսvå ˆ-“$I·¡KË«‹qdz8;Ö·:pp~ȉ“)úe”†ãÞ¤§-:IÆ8ÒEØâöÆ+<›`›Ñ[Iš´zs¢Ù¦.nÏKöƒe«¨RÌo ©o6·Çerqi:Øw_ÖìB:ãí$wk²Û´3Âs¼1#_ˈ2Ùº´=»熩‚J1só¸,§@ÿo#&}œžÑ®éà9·¯(«%¢³û……E½­âÖò~u€Â2:ÖoË÷e-q{—"NkÅ–cçv›m^õ¶ö¸¢°×¸¦ÏÓðC@g¯qhX ”{MX3)Æ+¼»dÛÑ“Økb}0ìm-q{—"N—uÄ–cÇöXŠRÁ‹O#&w~ÓÜ5§1 H8)šD'6Kq™Õ¶>©^ÞOÑ&*èg­O²­%nïKᧇµb˱s{™6Ž®¶{‚³LÞ­k(6_gã[bS,EµÆ¹­ Ûêò^ÔvOp€Î»Õz5¼¬C\ÞC¡Ó—µb˱s;ГŠy¨fN ¦…ãŠÍ×™µ!Ž©¬^ŠaY¿¬‘ì“ÖîÝ%Û^ŽÖ›Ö·wW¬Óe±åرý×ï¿#¼þøý·òúÏë÷ßþýǯúúó/}e±>O6MÖ ÅGÚ ¾eKa°Ÿ† š¦`-¢:&éëäÍNÇeAZµQeº‚ˆ¡¡Ï}Ò›M^GqÅñSÊ«`ª¨§)Ȇ)ºŸRÌ}™è+¾ƒÄ¹¿ ºÕå>8œØk±6ô4ì¸ðGã(v`ˆ=ìkm`…STÀ}¹¢ýtÄoœî•6Xn|‹û@ŸW)wÕ ¶ÓÆ‚–ÑnW?O9¸ñãÏóú“oTøFÞèàEüíOUì©ÚYù²z¶€‰‰~ñhåN~×A–ì­þû/*Ñ’j„ó¤øðJÓÍü&SÀÚ1xá¹Åg>é» ßÅ.â``é"bG燄D{µ¸¿'](êS7|³¹æÖ{Šå‡@ó¤—ûW¸ƒôt±ã&}\~ÐóÎxñ'¥ÀÅþofÛù•Ú1I+Â;I‹TzRnÝJdž¾uÝ9Ù´cœ>Âê % }RÒžÊó6œgÎâØvºî§¨‹Éÿìj@O©~?(¥6Ry¬zAúÔíg’諪žTf9bf6æºÓ𑕆™S^®OªßƒïøñQµÌ\б£éCÍf5¤—úÀ“ÃÍÉâ£Y±äNá;*>on?è„|Uo0Oê8xfoRœÔznA¾ƒ~î}?f‰Îõ¤VvñQÌÌ;hêmÚ19¯…0YÔ®Zâ“zäMýǧ”k榩µÜ ÚGtÚ'µ^âÁÇgÒÝÝŠcCSù™¶‚üI-èê-¾3wR‹ S:Ü ÷‘‘B†'AÅM~ùqÂÜ®ŒÚÁ7Üo5ì9O wCÆš1)6ôt5ӯݑìIÐÖU7¤Š}d¬‹·j†~È’®"„|b¼³“4†º64uóBz2x3õSò¡Ú24kF•²úŽ1ƒ™= þ ?H[úñŸ´c¤¾œøÓéïÿÇÉñk§"}"2{|Ó†Â÷M/‚Ïîß/ÝT‘BÈÄ£¥feüß´¡·¯JjÑ‹¢ˆÑËmìò©©Œë«UµŸ™[ Òi–¯ÞZÿÇwÕ´ÆÃÐ{ ÿaíÍöŒí™k¡zÞ?–¥PvH) ýû•¬?%›°'içÉ–lë=ñÌr m]^3ð…t 5þ*T‰±íoôBjƳ^¨¡~màDß…$H,$ÑÊ™ëèZàHòÒHY(0¶êBfef@Bቻ…˦N¥™$Xƒ ßceÆ ÎPŽ€E@‹„àÅØ8ö!Š`(‹  GBð¶-ÌeƒüëÐ0›‹,"ÃÁ+Ü´¦¬ø*ÐZBfFtDH”Ò‹„àÑL^Q~6|¬Ë½`òçbmÚŽ™êK°á{|µ»Š;fý*$Ÿ«“|t [uQ%æÌ2s“ÏFð=8û˨ñ!lÏM+ÑIjÃéÜ­ÐÈÒ"è˜ék=°±÷è@H$é&1è­ö¾XyŽü‚íuåýBò¥ )ÐV#õ5:‚={hïCž¹…ŠŸœÐ×è@AáØ‡°{Ýå§ædÞƒ!È.EE;s;눲³_©¼FBn¶‹òøÎGÆd¿nÆã=:ŒL(=t°:;Šsöåè@²q²ÌèüEw¾N£¿%ðd}X†§Bï¯èP•†ä‡QÈÏü3èj´}芨g!UЇ‘è’šUyÒ¹ðÇbÓ!SÜ ]¸¨zXYkßûü?sŽ6Óƒ›C×v´l,lü¢I¯˜ QC+3¢©é9—S‡mÇвY‹îÉ•:“áé²L´™Ýºº£eoaï—q>ßÎý”èÇŸ/<%)ÓÓåòz¼Þž^®¾Wߟ¯n¥›«i¥Ü úˆE¦»4·o‹Y¤£<þ) -³¤“?ebqóŒJpzË2Ͱõq¤­ŸM”²ÎF –5î­ößÐÓÔèæÐÕ ­{ {‡F0“a`›ÉÉíáԜ˹C¶£hݬFŸÉðSÎ327@OsóÏ7XÝк·°÷AkÌAãŠý½Ï/×Ð `x»w7Søv.‹òƸPÙî‡7¸&òBÞGȪ6õÉxDÿ`eÁø¦KÌ0¶3£wƱ/0ÎÝÂdMXìŸMµ¶e &'Ž»s—}±óXsÌ›Jb«¯GpÈÇ=¤’^ÙCŸ4‘¹¾^.XnçA"ëÿtÜè endstream endobj 24 0 obj << /ProcSet [/PDF /Text ] /Font << /F2 4 0 R /F4 5 0 R /F8 7 0 R >> /ExtGState << /GS1 8 0 R /GS2 25 0 R >> >> endobj 27 0 obj << /Length 42042 /Filter /FlateDecode >> stream H‰ŒWKo¹¾ ÐàQ^ìp›lv“cx9d% LJö¨%ÏbÚ™žœ_Ÿ*ÖƒlI£»°¦ª¾*Öãã£y¼¾úå×[gO×WÎlÌõUŠ6Ó56˜Ðz³ruƒ9N×W?]_ýõ>zãÌÝ`Mÿ%Ó6Bg›hîv×W nÌÝ:ÿû|}uÓ¸ûýúêïw¸ غ`ÛÞD?ß&‹ëô!-VqMë}oÛ¶Šn›(öqús·fþ>™§ãtšöëÉÌhö‡ýv³ŸÆ#DÞŽóæ°?}ß<™oÓü3x toÆãqüaž7Û-Ô{ÚÜOX9„˜žF¨z2Χv¤ çÍ0@3Çýéa:§{ªýðt8â‡eÔ*˜G‚b0j¬!¸ê3æªù0œ(Lè?¹†é­Ž­ÏÇ?‰ ówÈùx†`ÏÓ13ºY~Oð/±ÃÀ\Î#vsUR°Gèð ê0»Ãý´ÍÁæ8Ñi÷´9"oÌý86»{ëûd€[Ž8\å†d#o E¨l{@_d4S÷qš6Ogø•ãz}¦œVµl hŸ§ö}¹Ù1jÍ{Lp;?ø`ÃÍ#4žXÃ6ØÝÓ÷Ãóúôáëݧú0H¸Á;8šçA°.šUèm_œ;éŹ#ŽÞ.,üã&ÏÀÿ%óËÇÀ¾Ý0VTµçÇÿÙœ´‡­\96Ê©Q7f„ÿÍÃy¿FQÈõâ(ù6ãÑu zA Ú3ÀëÝt¿9ï2µ³õ–|žö÷èr›i­áøÒ³¸1¿â >A;~7íͳimêÍ?Ì—¯¹¿¾àô‡Û B‡;ö©³}'Š­*¢ƒkc@y¼”‹ƒÈ­F|iwMª¬$©Í76-Ì¢PD ¦¶FˆBH,W#D¡ˆn€ÑÖQ(~µ]…" -~‘‡(1Ý«ˆB[ë;Ä ¡ Ex˜V‡*Ñöv‚eµ‡ÁvuªPDïmX„…"b‡7x…ÅræÑõ˜Üî5GpÊÅJ’ÚhÄ•YŠ WQ(‚F\!D¡q……"hÄBŠ WQ(‚F\!D!qA¨B4â ! EäW–ÕN#®¢P¸BˆB4â !Š—û>ØäßÜç²uðz/ËÆTÄ«*ÛN¯ö¡l*E¼Úe²eñzÉ–PÄ«="„WÄ¥Ð-ºñË»e7Þàp·ìÆ í–Ýxƒݲo°«[vã ötËnÄ-]×rIÞЊ>ä×É͇•Kð7®i@ÓÞxþÝÚ~þÝßtº¯ç7î–”àùÝëÛD—|¾aõÎS¦vËaUÔí­S"¾A]¶'x¥ÊNreìÂŒb±ùíRÌ$«½ozœ¿ÚY.vH¾Šäb‡¹ U|–‹M¡º€Y.öàð5Vì$Wv˜z¬íY.vx›¹Šê,{ß㻡ØI.vŠ«ë'¹ØS>ã‹äbðùZÙIV;PÆ&WmE’+û`ÛPÛ³\ìÈ–þÅVîéEà!õî“)”>Pþ€ù£v&›•@bW 0ˆ…B P @9Äå”D P @YÄe‘”F)@xÄå‘”H P" @™Äe’”J P* @¹Äå’”L(d’ç°²IÂ& tªÔÄ×h½{ï4"û¥Ó&[ß9MÈ~ù´ ûåÓ€ì—w;Ù/ïf²_Þ­Ù~y7z˜U÷?¶!.o§lw»â½í@ˆ÷èNˆ÷èLˆ÷èJˆËt|qõö1¿†údéæ½T¼*ñ*lŒ·®õ]‘Ùà+C­•¾•¡¿à*}߃«—†ª‹áÂÒ.”O×,ˊɬ°Hx–ÈÞˆx®p;ÎÓ~ýÃ|9}Õÿë«.àÙý`|›¼ò¡µ7Çéúê_f¿ì›‹Éö>ÃéÁòùö·«¶…7 =30ê?u ïìîÛ|_ ôÎmé¾b¹³±Ü—(€ß]È‚ƒÙˆ”}×™ýY†e¡Mêéñá"q³°Î;'ÆbM6øâÉ’Æe™WOʨΰØÿO¦AÚ>ª§'»çú\Š¢ÀŒÉÚ ­JGÀõô”UˆF=ó‹QâfëlÅš/;õdIã²Ì«Š'eTç»Î#r·ì}‚²¢Ç?ù ÕÁq¢ËC³áÏšàŽ>]ZGvònmFÏR†·À—ÊçÊPy³X¢«‚VoέΖutT$ÔGVäl‰t î½J,Å0 }4ÅnI£‘QR\«VÈ´veQ#«L ‹/eUå,s‚¹RŸé²T4¦h;×±Âá NEö¹1,fï¼¼*šíâp7hì,eø`]SÙË»„¼U”è¬ÕÅ›R«—*k<}›a:>Š“¨C«Rþ†áRHnB4ŵTò¢&¶‹B*_‘’‚cµ®¸rRUÎ<#—7V.›f$ ¸èòŒ\€ ܱ%Ÿrˆ€]1{¯ í·•ìäÜåõ%vÇ 8×ó Ù­ë+oKtVÈêâM©U‰óŒ@Õ§¬j¸8ȧ‰¢©«çÝj€yŻà \£g‰« ][ìÑ6µ7‹u5M½ºxsnuî<,·*qÒåËI‰_áÎ ÖwªqnFâ-Ž¡]K)°¢‡›íâ kF£giMË¥PÙ~>¨·ˆ½(òêêM¹-rç¡®'] AF)Š&çK\[‹pâI=ĘOfõj£Ñ³$õÈU¨€Ê[D^yuõ¦Ü¹ËÀ‚Ôi`¬HøÂá£tèDƒbK’Ç bö_¼åS*’¼{Û`F=K7vªì½õmåÍb‰Î Y]¼9·:wX°DÒAÊk|df(^,EºDìaÓFS{K+%þ-UÄVdåH’F%Q–GJ§ä*ãðq’÷=$D1ÀV¤ñ ùÅçáåE1R³Δø„vñî‰ñ½gÆ»¶±Ñ{ëèÄÞ"jô¢È««7çVç.ãèõÜ$Ò£-k0ߎ"Æ®-bÄ7$×à ßPޝè.ž%©§mÕ^ÕÓ,Êã襞¼ºzsnuî<°À;0áCdW©A,4pt¢A±¡¦D<¢DÌþk‚³Â²gïÌ']MÈ<dõv¨NvéÚv™[“ Dz㌖`ñÁÓ•¹ˆ˜³û7.(ñÇî›ì©Ög\¿Öé~×¥®e6Äkw'¾eß­g¼§gzœ€„´úõô÷~Q±U3¢jˆ›¥•€üìÃ×÷í4wC;]ŸGnã®J±:‰ ñšÀ­A¦cÙqDK1ŸžæÅ›ƒ_«ºïþ¶‡ Š#eíbÖ+¸÷$û'Áo…“dêä/ºâ ycpáUòAšõ½‘ïýDg{¹ <ÔYTS¥V®è|u€€¾][ý]¯ØÎ$Soï9Òº\ÌIlˆ×º±;Øô-ûŽ8)ægÞ¬” æ/îD§ˆºc‹óøÏá¨ìöÛ‹Ö]âyØq7?@fßgÑâЉcýñ>lˆ×:îNöóJ.#H LÓ=q¨²óÔ×»^ÅŸ“ø‹_zÍÁnš¹ÂºI< R˜­?«Ë†øs’¼;Ùð-ûŽ õá¦k’ÿKÀóîÕâÔÅúHëÍ{h1­—I±£H$ ,]'»iÑ ë %¨uyV#­?^sÈ‘Öpw²á[öSlÖ<`ê‘]¹!æ¯_Š^UˆE"¨Ë¶8†õ†e!`¶þxÍ!;Äê+ïN6|˾#`#B<=`¤öyÀ†üšV i£ëÀ¦F Ñøê½c]'»koÖM2õ‰ëÒ%²K×¶ËÜšT8–G´ÛŽùá$mZMSÀ\Ýnoõ+Ž÷=‰§ÜÓÖër¥çx…eâ=V§´‰ 1,CæÆäºWÉgé™òJü!zÌ÷ö =øýQ±·¡7EÑøW¯^/žåë ¹U’pX7ÉÔ¥s]i]¦Ð–دuÜlø–}GœóØ-ÿ«Ì£EÄüElõ]S|ô=ó<þŠõoºìn‰ŠÖ;•y„ÆÀÖÿYgC¼Öpw²á[ö›ÛkçÔ‹øKÀÒßX6eØl‘6§¿5½”'‹ÅKÇZëËV—_aÝ$S?6QÅúñ´@6Äk=ßlø–}GÀ†ŠGÖ¢þ¿€¥/æŠ÷<ÇßLÖe˯Þòyf8XÐØúñö€lˆ?çÉ»“ ß²ï˜4˜cV¶‡ÀÖÑN&3дÄË0‚eª+‰¶¯;û‘fàÖMRuíšVZ¯¼/cS ë°Ýƒí¾ýøŽ€)6³ó©C:ÛðsšžÅúÃtMgÁ•ßì–$7u¦öX•0Q)Òò•mãàšWÙgSÚþý¯#”._õ42¨•Ûû±)Ë&O®ïºüÑ&ÿÿÿÉt'³Çy |,äUf-'Kšè¥ZµÔw oÒsëÚ‘ÇVMkÁÎÖ~AítЦõ0®ñMK{zÚ©Ë:b1>ÜŽ´ÈúŠ…Vœ&MroÒXšh§Éü 'í׬­è¼gÇhNÛïߤa?FØ‘twÌø;Ú%¡UкÞ'4¾IcðÙéÕ½^o‰°Ù9öxÅŽdQ£ÙC…Æ7iìdvF³ÞY[¬£Zï«Û{¼F±Ï5¾WCÞQ¿ñÇúÑj°³¬6hÓ ›Æ7imDh發´a¿T}†_ëR\iô4¡ñMçíC‹Ù™¡…xÍêkÅ#Êk¥†žëÞÏ*ê¿ïÓ휷æÔeÕËhR—Cã{5ÄΚ×Îö×jNªu¤æc,k±î1¨¡ï=—üÓÝ/÷# áø-â~´:…Æ÷jìšïçø;PrÚ9xeGS¥ÒŽ ‘Tø^í™Í´ò¼1¾b|,ÃnCv¯^’åïž¡ñM hv*Z'™ ì9Û¬:üíõ¶ÚÞ=4¾ICzývt¶òAÅ‹˜ +~ ›íBã›4 ›mùÝøè?W{§éÈñPã›4Æ{_;Ú“zTžŸA é›ý¢oÒx?³£ÍÒ¦ñÜ«ÆöÚ¬í´}êÛõçÖø±~Z¨t˜zˆ±ˆ®ë¦C«0h¥ÆOe<—>ÈÐøþ4÷\Zr,§Û?*F/ËÄT_¦³T_¶âÿ£”“Ö är"y/W“éJžœ´vȹtl a*ZŸ×ÄÛÖJr.çò{Õ¬hé õfëCË‚E"—…ÊšóçUifï\äïs×*ІùíÉ@rþ„˜2þ¶Š›2þÚ¶ÿñ­$Ákwà@NðMÛ”àñ¾*hrƒ@ÊçúP.MžåñìxaŸÔ§/í °W”Rš;-=Ksf§mö÷uHIMâx{³"ÿà_’¿–Ý€Vþ~ºJàh3……É ¢üH¢4 À‹6õûc[¬q¸¶­B’)lBƆ$º/ÙÓr¨ML±[³“ˆ–n½^Œr®ØõM}Ž›Wwùm.µiÛvMúx:—ÀÜuC²!^ëQ,}w²ŸWrùƒ„îOÓƒ¶³•ˆå<»}®¸´cû >`píêÙS{7áãÕ¥´s—^ù%‡Hã¸9Ùp-yþA‘úä8½½O­5{ÂvÿÇvµäH¬ã°ýïu‚Fìø“œ`€Ù¾#ÔÖ}ÿíˆ%«:µê#*.Û)ar³¸l3ŠÏ}¼¼®Eš~ïçÓÚ‡³îêÄÛɾË+-ùM½ú‰pâ)┡¶†¨@6ýðîzç°Ú{‹&MÔvòaŒâñmóº÷ó¢3­³=ôâ;ÖwÙVö±ò75kÖ­ÛiPÉÆyôŸ ;²Cž¤èƒX‹“O\Œ¨}ú5‘Fl;ÊçÃãl†»zövgÛÒÒÂß4JoðÙ±à~+ ƒã¥Á¡•ô¼7ËKßÔu%NnPÔ`VýŒ½rõÏ\$ÿÔ-åóåÚl†Q;b{³“¹¬¼ì7í¥&ûþlì¨VèߌN¿ðWtÿ Š™n¯(­‘fC¶îý\-ífGèÅ ø»Í…å…¿Íób-Ó'19So–"j‘q•DíÆÃÊÅ)åµÙMŸÅ›¿ðª·ÃKÈÅ(ÙÃ(¾}y°¹´´ò7m8~øÍ–—S DzOoë^¾Lë:AÚ“¼öð]#_Ï/¸ŽÍf¸«ð·;Û––þæ` q‘_o“ƒÖ¢€ÎUƒŠR …Ì>䀜–âd‰Å—zm‹4}ü ] Ÿ«ßl†»ºñv²¹´´ð·*8?"è±Ыnf9A ¸©ŒŠ«[Äê‘‚z¨ƒòÒ‡;,ÈE»÷ó¡_A°#ôâüÝÎæÂòÂߟð³;u¤§š ":o¡\ÓváùCzăMÆÛ_Q¼r“ÀdÄó ²Ù2›±¿ÛÉ\Y^ù›#Ò/L1¿½8¢3àШʎD8Ùú2†Êj‚-ßÏ+Š&Ë¢Ž;žöŸ’˜íÂû{ÉUåU¿9bê2m•¦§;*ÛUÕæ£0lg„ƒ¶4€®¶uS;LmÔí4½UŒQç~êó® /Ø wuþvg_¯´ä7‡Ýרð™øç©„õ¬˜.†˜Ï*º‡M@Á*œ,ÖWš!K[ Ù]ç‡xܵC9B/NÀ_N6W¶—ÜÖµmÊO3!¬MF |§†ÔvþbäQaÝ1¤lS]”>(„n|¤þ|è9;B/NÀßíl.,/é}دî<±ê/@„ŸÝlK¾c놳êv{áêÇѧ™ >µk;B/NÀßíl[WZ5’E€Oă«ù¾t1ŠH¨É09þ?‰S_s´9º†¨+å=¸•úüÆyl6è±½ÕÉ\P^0Ò/5G2A4kOó6KaB|_uJ#¸sÈöæÀø9Ñd‚-gòòÚó*f(øÐ¬‘3=ŒÊÐ7ÙÖ•—-ÙÿþóŸãõ_š×ÿ^ÿüGÔF>×.²?ԛܗ•SÅtíç qWÃþx®|‰ æ†þê—z=(+ü€¡É‡ÜŠÜ¯oZCY•Œ¢â‹ÊæhøQ9Cz>YØ× ­@å”ñA|Ã4[VtC»8‡s|dèô§@Õø’¡5H8déwéx*ÿ8 v)ãŠåß2¬†"œ{ðZQñ·fÌþ‘!{J@„ük†ÖPà†ÓÅhk×Ó+~Ü[è_‰>…/±ÉM׬“æiã Ì8]g´ãLýž?ƒ€þîGk,)/dÈÛo+zãö¡†ìBÎèøËæÊö5Ck,Î{]ßÂÁUúÕa+•߸3@)¤#É`ÅáËæ'é—¿6òÔ®’8Ýœqàþ.›¸Ú× ­±ljÓ&Ž¢‡Ó2æG†Ô p‰!û–7ÎÞ†ˆ‹E¯N‰í3£ùµ”Á£|Íì ê²9\ØéËwqË8?2ºŸ­|[ýk†ÖXæÌ§¹gžþ­—ΟgË qxúrAÛ—+±hš§Uj4ÕÖÚYGÆï{™Q_3NÞ5‚f¥%ñ—¶Ñœ|êg–9Öók†ÖX4“Ý\\7?ÙõFImkÀ‘¡–¹Èö5Ck,¼n–u˜ÅëöÚÉ£ŒÁÃW[׿e\<|µ-C-]S¸œ1ÜÚ¥Œ¢óÜ2“u}˰ˬO¿Ì)™æÂr˜ý¬-gH‹€ÙÜG†ÕXɉ4/*_ǤãúÌ(^t |â_2š9k&÷<}ˆ²É=ÏÖ3¾Þe†àøš1yú[\Mö\œû¯‹qÄ9ÿSHç§0nY{7)Y7'úCÖÞîË¥v™’eòF?ú”Ú9ÿ(«™²Ô¶„0žÞ ¶¸òËßZJàƒ’u°ãEYK«·F—Î dJ–½IÓÒÙ¼ºRn S²ÊÉïžI)‡{¶ c2e‹Ääh„±¶?:è@¦d ;Ñ©·"Ã>JÊ^2%K–àª'ð‡ÆEœò³:ùhnÖ?‚æ@¦$1Âáµ,hrzËú•€LIÚSdŸë‡~]e²\m S²ÖTüù”«û:Ý÷“‘•¥ÿ‘¦¿BTÿdg™¼Þ!B¢üóCs6)I/°QóCsv%æ;%Ƀ| óü9•óCQ6)Y äÖ¬(hí5 H2%7ÿFëœÄœrÒ 2%÷úÝ7ë…&fyØ@¦¤Ö~êJ³<\¼8¡|P¬ÛËG¡sÓ¤W„õgää?¡'£ï‹e‡·ï¡ r­¬œÞõœò6eÚU/ÞKä’Ë!°íØ) (rQ‡ùÐn¦²¨W$°0|ÖqÊØ½u¸ÀvP’2X ¸qÓ3ålŠìØåvF‹JsHö†1.ø  1DÊ‹^¬³»ÒHã&°pñêåE„@þ”Û¥G>[+Õ»šSÀ¦ ý¾ÏË{g»Ð`¬ eôLy›"¡ýG•¥Z V.|+™ò6åÒ¦.Z9BmäòXè…–Ú) (ÿš¾œ`…;™ªVäc¬.IJyAÁ/7ú /@IB»7`m@4µ~PÀ¦ÜvÍF§.Ò •¬LzÕ <§@— „ÒkÌTø’ X˜…_SÀ¦ˆX ùÆf…Q9k*4¬4,'S@PD½&€Î6U¥Õë::{NÓF§<€ œ'Ö! ·^.xpk¬4¸ŽLyAiMÅ[2B¥[¼R<\3œðœÑÍMˆ­¾B#^‹ù=®Lx›!w¢Ê]¹½GAE¬\!¤<€ ˆÊÆN1µ¹^ÊÖX ¨XN¦<§p|’í-zDŸ,^1>‰HN{Îøä*ÅEË­@\L€ÛXVì¤vÞÚS×]hB|ê2`¥©ë†býnàÖ?¬Ñ›¥y>uÔÙoÑŒƒË‹ ¥¬˜ÜàäÙNmr#°öä&;u1CÈ1{´Ô¨¦ÏÕäX[g=vAÊŠéo:âýÆôG`Åô§£3(“¢oþz…i¦`žùy£ÿŒÀÔwµ°bÙì¹NŽësì³i9c9gç¦þí²Ú^A°¯92ÈÓ¥ÉFOÈ=û°ž4ƒ³Áå»M ŸßÄSÌK7Õ V¼(…'Æ R†Qè!Ô­Ê—mÊeã+­q›Á_ÍŒÛïeçaãÖðýwS ÷Ã3šï‡g ß±èå¢ÿÓ2Ä`Ç ÐŒ¢=ôœ¸•¿€)^½æ¼y1ä Ú•0x1,Jà÷B Îr´eŒÿOxµìXrÛнÿÃ]öð¤J’´ ðÖ½‹³°;=ƒÕpÏ È߇¯CQ·îŒÑ‹¾¢(©¤Cm ÛÜ [ ’>Ëh†Ú =$^š…mQ†¬0láàèãYxˆã0®úÒl»Ä£ñYÚEÌ@òqdìQT`Z*Мp´ÄZ)óPCðÐ%¦ûD“ÊÕÞTæU7ˆGSý½`¼•x’d0\i–Ó$¬dÀ£¡ìÀcù¡$Æ`™˜Ìï Æâ ”UâØ „wrµÉ Å!“±3-éGôPCðÐ%&.iŠT•_T@Œe¾¨L¦ì×y×÷.N™jŽbÅ?Q9Ýô+û¾xˆ!zÈQšUÄELmªV“tIj,ÍÉÝ<Ì0øUMÏr"C­Ø£²Ä´<Ä=†i…D¼ÍéÚ²Å0«ÕâñÈÑÙ<ÌP-xXw•њмs-Qsd æÅŒ/_ŽbY!åÄBÈ âQXðÜö¥0 K&Þ#g1h˜³HΪÍU;¹G3uåw!‚8TŠo¦£³|©Ä£ çwTQ7dÒGë¡ʲ:±Föi1D UMR’u¹idâÜÝ âq0œ,Ô‡y˜¡±$´=jƒaƼMcoAàà}9ÈÓá,¢H!j¥ã*®ñè¢:J ¤!Ó=X’tÕtgéÎ1Îý@‹jºÄ¤{§«؇T›ÄccMÀÇõ€¡r¨ÛÌÓdÈ{0c1ä¶xˆ!zÈ’sêÿj!Žþ ZüBÿ'&ü{ÿs‘ñ¦ß¦Ð7ý]ßA†Ë’3Hð E‘.†©ÉY¤ó/KÎ Ÿ; 8vG‰‡|®œ—%gпÝ5  äîÐq»®09b–˜ÀÊ–I\5DE; „–%gŸÍ„Ôi30µh¾øŸS4–Œ.Çd¥‚Št!µ.9ƒäë;. »E—K¾j´¼.9ƒ\+è¥ èŠK3Ój‰í—%gÐY$€M¯˜‡¢ðªV@Ö%çTIÕ;6ÓQÕû±©’ ÷â’3hœC««‹ C««+œrñ–ñeBÅ%klQJe7ˆÌØD™P-©ãºäœª‚®©õßTGµv š"[÷3¨j¬¬º›\CÔSÓeÉ9‹9}£f;ʽbu ºeÉ9 1QÓ>´fj©VC¬ÌÝ.¾.9gíòVo³Ìv<^(¢íÁ’3À•Ù¡!P+ÊϲäôÖè%Mi‘3ìiuÒ~]rzj[ã2ûæ%Ê ³"5‚çH×%§Fxåy-/fˆÕd0¢—%J]Ô@Þ~p_&y‹ý6¤nU»Ë>}*6ä=¼%ñ`q  ²!÷)³´Xø±A¿Èš AÊWºRLŽ [ 9\YÁä@ lØÀ6µyŸ+ ÆcKr¥ÅJ¡×F`ÚJg)y³!ÇšP)áG …† ¥‡êU)‘5lPI%jNX•%mš&•±øÓ?¥ÛÇß(r ëd(û‚I¶‚0IuÁÄõýÄÄ2˜ì¸0q&,ÛX`ÙŒ4,Öë,ôê* ,ö¦À¥ƒÑ'.Ö`N\4ž.åÄÅhâ’/¸¤{\ÚŠK IÄÊÅD´a@âÑ0NÂx¿æD‡cÐÐѲžT~‚ˆAµøq ÀŽÁçb¬ÂŒû’Üôøþ¬'—÷Ÿ©…÷hxÿn¢Øß¿#ðþž9xÿj¡3ß_ïä(Ê‹=æKŸ“ƒ«J¤àCEVàuRwFãV,\¡‘†ÔFYÛ¥ª‡Z¹CYñ¥jgI¯˜û•kú€.ÝÉA,®¥:†JCŠÚheBp‘€®KZ«·–úº´°Õp’5T(„M*Ô;¾4cí/S•¨Ó¾@}ël|v€dï(€¤­8$äØH2J ÙùÕ&ÔM(½“mlÔJì}¤@¾â"”»% µMc) ßíˆdt€ÄÛƒ¤;$Ä[Z Aoè¨l†;P©³CйFkÜ‚„¬HP}žÞÍ!Ð2€ÀAOŠ´È¼wL‹ÝPGa¼/wYa‰4³¢ 8Ó¢­ “'BÓ̼ؖjAy±ïwy±·*y!YóðÄ’ù[5úÔ\#.ãÓ.ô!uI І}I ®ë=âÂ*¦E\¨Ž™ø2\¨ë) ] (ôÒGĄĬ‘»R“±·\>.£&¡9ôz`')õ-‚Â!iõ@Aa^î138¾J…Â) Âùº/iA0çñ2ö²¢Y¸Šp¯±¤Œ{[¡PYÊGój@²dèŠjăz³ŽocˆpûXHÈíÚ°$ÅêžC²¡¾’Š´$;ß0BB-dŽìÎo€¤X;$›e£B¾¢’WµËýn$+îoòX0p…¤¨ÈÃ`àTOŠj H±¤¥„·Ã.ëI±óSGš…çE‚” ÄÌ [ÕŒ‰a P(-_NŒbÄë(T¨7$ÆÆQ8À¢@aßî@(õ„}‘¶ÃÊéO¥öMâ°Ñ‹mR=ÜP ¨&º§Y¸«,âß©³˜#ZýBGÙ8‘2¯·¹”¢nÜ|k½èY½…y!¹چØ<øì¹Z>lýprÿ@ pûñF¤žnÿ½Ï¢žEïLßIm€YÂ)5ÂËÝv›K¥ÆûÖ2ÂiŸO|Â\mCl ¼j®–[?üEüóó·ßôÛF]¶ü@’*Ïoç§ó·—_ÎÛÇ×Oïžÿõí7}vy-GrJVf—Tt5HùÆîp‡Ãbô×ïô;š|Góï ]>ÃNÿ÷çyºKYÝ”‹9ÿÆ#6¾é—àbJÜÎøåååóÛçó—O¯á¢‡oJ‰ByÑöÜÜ¢‚‰^# 9…]Æ”u$[çRâœ6æßÀœÄ&é¢)¬´!6vƒž;ËG-ß<ÏЇ#åˆ<Ã^ß~ûôêHo·ï7›„Üó_ælDb“( L±g¯*@쉄U¸"š †G5:âàxè [œ_übj IàÆ/~€œo–˜âÂ÷xëiÇÏVtYñøxŽªõþ½n??¥Ÿßùá´5W„–¸ÚÙû€á †j¢+îÇq ªyi·¨?|<Þ´·1oÁ@Ž;¶Ø1$Äð¦…,å¯Ü—äÇÑ– SÜ]Ø÷–½âæC»Ô/ì]çc(¯o*ΜLoáE2t .Œú·¬˜ºîcäs ¾Q…ö6QÚ{ß0¢¾»Šó?XrQí UÚ¦Q³ñ<}øç§O¯ÿ¸#ÕUsî½±lf*L X¥c†/žÅ‚ˆÞvåp;/&ïýA,nµ;¾vPâL~xÐ#.² ”x *9Š~pVÊø ã†6 .[AäH·Í¬NEr£•m|ú¸ S™¤þ÷c÷Ç8VÞânz'9xÄy7Àa]`€C¦PY`€CI¬Ò‚ p «“0ÀXa[`€i龨jÔÅ<+Uð=á8p3wpèÇò’n€‰áå%ÝЉå%ÝbŸå%ݰGp¸‹Kånàí<†þtpƒ{(üÁ÷Püƒ î¡<`p€àƒ{hÜCc xÀàÁxXL7¸‡†Að€Á=4‚ î¡<`p„àƒ{h(ÜÌá©$ç¼DÇ• ò ý5ÓóŠë5‘ó Ú5OóŠÈ5 óúÜ×,Ëë[^“(¯uÉ‘²0æƒ (+#>ˆï²2Þƒè-+£=ˆÍ²2ÖƒÈ++#=ˆ«²2Îô¸ëš:½Ä-'Ò2Rÿö´½û~o$pÓ Œº?%ýUèÒOÙ¬TXŸŠ{Tÿuø¯Æ¿þþü#S é™ÞDŒ¹«ÿõŸ^÷Òê±[V¢ ¡šXOz•¼/z6ÛŒç|¹ t‡ùÁ§…ù±žÎ%;®×ñœ?vþè9¯ã0ß¸Ó ó2žó-³F˜ó:ó¤6[œ—ñœ'àF˜–áœek\­ã0O¹Çyû|¡Ê°…Ç·ñœ'œ÷°ÞÆaž˜4®×ñœOÒRÌy‡yjŽ8/ã9ŸËû×ëxΗUâœ×±Íw×pÿ‚ñœ¯$Ï[˜×ñÜ¿vÖósÏù£p£6çu<çÛvW¤ÿÏw•ëÊrÜÐÜ€ÿaBC€[µu-_ @©œ송dZ‰ ú{s;,öø½ÍM²XÅåé‘V ßhPeæƒêê‹Ò¾Ï)õ /Hx…¸JÄ%Ö‡^$ð*„— $¼N\…â¨Hx©@ÂkÅ%P,.jʨ|÷‚€WŒK d\5c»h¾^5ð²„×K p\• /Hxí¸ŠÇ%P=ðò„×$¼€ áäý¨ã!!Œ q?æ 1¢ÊÈ%PGðB‚„W$¼”|äIÏ<ÞÅôÿCQû>Ì´¯0 }Õ ÛWý³}ÕÜšÌ}ßo>íËÎxÃý¢3è÷¯jV%¾ª&‘ø2ÓUâ«T‰ïgÇÇàBÏäDl€G—¿ÐÊÁOéUéÍÊ©“†|(òáo?€ntŠôt[q~ÇbNO ¹˜à|ý•ìåèôl³É|ÿó?ÿþíúãõ÷ßÿ±ç»÷÷³±ÐH¤­»ëó“a¥«Ö&%Bɰ–@E™8äýXÊ(‹‰#?FꎺJóXªR†Î¢ëP:-¥ÓA—¯õ<æÔU+ë”NS@¦ö«´è'›búÔ9ŸÖ—¡š-|‰oÈŒÚu†®ê„ŸÁwæ›5K´\È¿&ŒSÝ*ùÈ]M½ ÆØ–0Ìh.P©Xw—Ú¬Z´ô6Uè„ù8½~~éõ&kYNr&ƒA”ZétœdâÉ,ÛÁÅËÕ ß F…¸äÙ4q+L«ª“0r,”Õ£èï¥7S# ™Å JprªÇP…DAvRFôA?';íÊç¸u¡Dœ¬Ú¦'ß;;½µÜÖÓ¡m¾Eß/ £&î_”}#ƒ©¤Y0çŠdÑ—£½æ×V®G¦³Ì´vÔ,k®Ú@¡lä6î =ÚêÙvûÒì´¢±dÊõµCBdûQ@ú£aêÚúä°î!¡-5·"{ÚFnëÆÀéÐ6ߢï—ÖÞã.%yLXžÈ¬ôÓÀ0o\[}5ã~¢ù\U.ˆŒ‡gЏ<¿B[9mæÌåôZ'“J”¶œjܽ.V¿׬«Vî­nZ(;K‹Ö¾DTµ‡¸È=»{É‹cÑñË:¦ôm¹QI|!QjåÖ}9K¢œ5=Ð…sÜõ*OånT(‘fÔ^ûû<Æ ÚN¸1p6´û+ø{ÜneÔœÁÉM‘¯K«uZ+Ý4·©­Ú¸‹¹i¡Dš0¤¯ý2%j; ãÆÀÙÐ6Ç¢ã—aðU.²hoÈà0Ù´6V_‘¬æœ1È·œ_[›^øÛü_Ž¢d.Ë?ftqÑé–7CNveõ+º})fë¥5ŸˆÌv ŠÝÙµ]P#ç°‚<ÙuQîè/“Ó•©HË ¦…išñíòy"^ª<1À¸,z8´Õ³íöe£ˆÊêÒ™ivÔ“J­Q'¯ — «×‹ÃU¥nZ(;kZZÉ÷ŽzVíŽj†qcàlh›cÑñKg¬¬O>G9ΚÁa2é\Wé5œä™ìRi`.Eb+s2¿Ü¸PvÚ˜kçj+ƒ2ÓJâ`hš[ÑíˆǮõ¢õASðª&›Þƒ«ÌÉ“c~©¸1¸Æ·6OÊnœ .G¶kð×rŒ  †•ƹÐ4¯¢×— ű]Íe¦]ÈÔwÏé=e3¤ç¸¶v$3î ‹èYvâEÏN”7iÆÁ°Ã][]‹ž_6îkÓ›ÐÔ:­ñ²ô´àv®5§’¦‰ÑTš\©®ÙxÅpÃB]¼i0ì/p˜<§k; ãÁgomõ+x}٣ײ’eepJ D´•ɪê8Õûe™¨Ý8æk*Øãl>~—ìóŽhƒ„ñMëÙP6Ï¢ç—nhÓ¶>] ‰¡ã·p gC¯ž)¼N¢s;ƒòádo¦þ”n°Pòòñ›¿ólV¶6H·N‡¶ù}¿lÿZн”ée2_L%­ªÂ)àq©´1gˆ+ËôÓ嘓^© U Ê›4ã`àðåU2_Ñm–m‰rFS«®£ŠÂ)ô3õ'‡$E[Ïqºpr¸² nj3Qônk¤·mÕMªeÐv°+«[Ûg–%´—>LÉ ‘ ¼W“ÖAEÈlúž'•øÖ&/aºã ëÿô¥X»P¥¶IÐz"ôÌŸè/Kwñ¦­Lg G»g8¦LýNÛ œÑekØÚ2EÃx·•£ôê3…|®˜8TÙG>7‡¶¹ñNŽKµâf(÷ûDæÜ!•¨{Xnu\Ûr 4g¸™v"xwÈOD7·¢€ì *›êF¨>m0›ú2HüÊ¥þÖµ“uy”ÊOíLþlt‡ŠªœOoÛ ×£ÄLe-vx7ª¨Ì;”*/doÝÌŠÏg±–;Gf¨-•ŽŠ¿whÜ(Þºå[‚ Èj+´ `N]:ë[ciœsoÛIzì4àeÂíðXŽ•ìýªYeòWh`è¶ÂcpóUâÞmÐÃ3ù³/ªz¯XÉ O~‡ÖŠ ð8ðڼ؀]ç`[ö!C6`Ïi/ªà:K‹!²€ŽÓ.m`hêü€Áa‚â1Œ¨òÕªŒqy*ðeÇá5žxz”ÓCêK×óÁˆ*O4톿ºTnX€ßò`D'’Z÷+7鎽‘Œò1þ‚·ÂðÐ`5 nFT‰IÅ{¢®d“$DuÔ}ÐQ#&ª á­<,ë2ײ®}C剜SF_U9»ƒm~0¢J¬¨Á¸y›ªŒ¤ÍõÁˆ*¡J‡5F] ä(›úƒ4bá$An7àn@Ø:Œ¨» 5$…ÅÌ©R‡Ñ5GFÔýir·¹iO½ÇÊ@Vƒ%0‚Fly“ç<Ĥ‰ÑmÀ`}ÐQ#¶ÑÉÏ-X˜ôñ:Œ©c>Q%öæÅKÁ­ÛBÔNsy0¢Æ›‚W³A}ðÀ4Òˆ!ë˜ £EûðvÀ0šÆƒ46’ál(U(ó‚öþ`…D7Pözk£L ›4Þ²jQ0~þøÓ/åõëïi#1Æ8x¶Ž%®ÑNäª=MkÓº«=w#þŠËnãq6¤E£æ¦fZ£v8"v¶ÜQRZΚ”ÔXKŽsZ¶-%0¬,Z7ÎØ`&•ÔcU˜õ<Çb[{%oÆŒ »/ìBÔVÀPd¡ç²áÙЊ›×à{WûÛÕ²kÉm÷üg™xqÑzt«µ5ð6ó“Æ`€à4l3ðï§Š/±'Æ]œK¶HQÉ*úø˜+ÏPä°Œ ‰SLÉPgß‹+¦Oˆ%$qya­É,äò ËäÆýŠpÉ>“𬠂÷–.¹_—mW·Ôˆr¼—›±Y•RQÙ¡TCXçsì¼pu!âšÔ<äm03†Ðw®’,GÛ;æú~HÁ‡uˆ¶ÚdßÛŒ=²ùeoÊWïZêd{+¦(ÈIYŽq!6Íà?x†e\™Já[$Ûlæü{“é,¬M\ÞMá»»µ†–¿þ²Ú%¹dCoå9Câ EŽHðyÌ,VM W€1cº³âûH—ÒªbñÉwmöamâò ÝÝ­-¶ûµJ)ŠHêѱ™éé7y¤š‚c%ëYý‡èû›âÔøÜ;2zó®Ò¥ÛÇ\ß«ÆçÖ&†÷¥ÝÝÚb{Ä~¥nTÙno%ƒtlšRåN˜Aïs‰y·Ø«DaN†cÖ'{¯;Á6cPþU®x™ª˜\›Â·6c,~i£eÒ¢©[o T³G©†"ó¬HêÓÄ1­|—,ÕíÆã”Ú7ç*]J•dc—bíL›¥‡•þ‚tÞí iSª!OÈǤ;›Ý±Ëøa(a\‰aá\$YÞµMù÷]/É­M ç&ûÞnl‘åÈ/CG}aK.ôÙŠk(’õž§¾°‹Ãí]qh†„õÁôqç‡Å ÑXÇöµ g-Sõ+lë0ÖÀ_üÌ6ïÖ7©i‰³^E~ébôSWX¿ cíÆî;º5 ¥[¦Ë÷)l6¬M\ÞMá»»µ†–¿”Ò40Ô”Ž.sðЪ($‹§ð“ž–hÉ¢ŠXnŒÄŒÛ”qP]›péX´Õ¹>’˜a,bv Ý\­=²öeDkq¯zŒK}×”×"wòqC² 3#S#LïË=7»¯Áœžñµs¼Y¦&.Ï¡ÐÝØ¢ÊQ_F@Á? nØ_C5Aº`ýÌbÕÝL±M9@X“¿Ì·üétXå:õc%-K“çPèÎfìq¥°/#Ö æ…NÏ1…Ž˜¦œÊ̬ƒ´D+.Wà ia­]ŽGW‚t$¾6аv1¼/…ìÖÛ#öK§††åµ9Œ }$¬i(¢Ú:`Vº¹‹ÅòeÉ‚a¼ L¸óÍj·"Û ²Ý4G/)ÖŽØxYZX9ìËÆ!*@Ou–„¢J(¢©Û`‹£TϹD§=¡6ÆâÖ»áÝI:ÏÇpô;2§-c“·ɾµ›Z`9ðË=¤Gób§‚˜fšŠ·;tdÑ%ÑnÂZ´ËšέÞ+È!;VÍ7u1\/…níÆXüJ3,ÊI‚3J¦‹¢â~¸¼—×âæ÷°€«W·Ékuß*Ùf’èö½mRn½Dóî ßݬ-´8ÃŒYÞ§ÂHÅMBEà ô”NžÄͶ2Å$XƬÐWø> G¨Š}/’éníbx_ Ù=¬%´8÷Ù’9ÊAü¢¨$òÿ&žT²TFËgaÙ8¬†ãf£+‚þ½ R„µ‰áܶwXK\9j.æ ‡ÅÀã®Ç€‚ Ã4õ(äðÍY ‰EoÁU&‘e]ˆá]$YÞ„aÄ÷.wÖ&.ï¡ÐÝÝÚb˱s90” €ô©É…„£š e°*kÎE²kµW ÁšukNH/w.‚,Þ-ýzh¤nªbrm ßÚŒ=°¸MŽJÌŽåõÛK&È*$Wž’ÏmøsAnÌè×Û²’•]ظÞVo]úéÐVçNQ°×±ëGS“Í@~PA„èB¡Ú©2z×>FÜa]g;³ _ ­"8X2 Ì) rKxà¹M± Ń{¯Yæ`§ÎxpÖÅÑÕÄÆ8!­ª($½ • —Qܸl´[SE™D:†çjNqPà¤L`—“ñ!^_ù…¯±á56vð?õQŠÖ¡xææEz§ª­ìoùÐ\RûŠýc[~° Œÿ½zÁNTzëÀ…ü]ÍÁ¾ãžW1`Ïýô†r¯sN?F–~ä–ã=¨X´âR«Þ©îÔº:Þ:p”™{™¯8¬ÛÑ)3v³x¯–¸YÏ$[oÏ© * œ{)›wÕ{µYpXíàÍç™Únôáb•ª>¦p\íÖwjßÅNDÒz¹ŸûŠ–OÔ$tƒ;á‚ô··RÌšqÂH»p7èiÅÑåNp³3ãÞÊØŒîþø DõM΂D2غŽm¬€·Ò«²g`óÕv?Â>ÖnÈWFË€i ÎÔmˆÎç‘QÖP×s„D^tÁ{-©.€£j10Ž`f­¾=wZJ·6ëã5:™m~åA6û„ñ:rJîT¯Lg?+¨{Û²ÊT³®›:Ÿ¶Ïè§i‘5ô ,Ì‘ãXž‰ Æ‹Àö0þÏÙ¾ 1˜[!¼)D+ ž·™ålðÄÞ¢Ý{;عâœ×c<Ù$Ÿ£yWÑÅpòQ«³˜×ç›i:Lµ¦ÕAyžÉ"_¶Àý­ˆÝŽ-iˆÏõ¡ÈO¦‡ VŸò~Êv›r2È)!Yt+L#©‹¢;*ã!³"›¤<#áVðb.(;˜²Ä²œ-žôPÌÁ­1 à•]7g¿™“\Sõ6pžR–îÄâòPd“\e“?·s/²Âa·³³"›¤Ê$}·brë²vXœa;Šd‘›ÁΑèVH>¸ÍÙ'˜ïœÙ$7˜Âéæ68&ØNgiDß’“Ô´¤«ÝŠÄœn·nɃBe\rZÿÑ^/7~"ìjÜ}ÈÙ"wÖ“üN r“a®x'%äŽó¡È&¹]c…¸°±{;!Ø–úP$ƒ “[߆¼¬g m–TÀ*›‚lew u@>oã¡È& .ûnDœ€ËËB1K¦ÇC‘,TÂnÓ›riýØŒ\é!)’Å[»$æ¦~µ¿µÓ¢¨Máíþ·—}Áµ¢ÈªHŠk(ò9³€ ¸HG¯KW‹Ì᩼˜Mäx¹s•.Ým¢AÄ÷.¤1¬M ç*ÇÞfì‘åÈ/;õã(@ƒ3¥HW w&Æn.[0nl¡šóu”‚“ŽRº¾‰[›ÎUŽ½ÍØ#Ë‘_ò<¿~úù§óµáï|“ 3à` |ÂÑþöþýúü~}ýòíïŸþóóOÿø¤4*=}·vOß­ÞÿÅ¥_õý…Éþii@Bwt#Í|IM)ë¹ÚolÁ{-Ø)¡kBèp:¹ÉpV,Zæ'{öæ+²;^Á+z ”»´ ^ÁÆCØ=üñ=ßÃWã2™Xú½\Îu/.cÃ_Ñ3!›lÃ1€ís÷k8ÚC‘MÞñt¸ñYºÄa¾®ï÷÷÷ço_Vô¸Ìo)>;¢í‚fE6±âVjtZŽÏ.$,ÓNÆy¢;ít¨m!h œoI÷¼Øöb ^+”]q›!Â)øñ¥×%ŠTHÛƒc ˜ÇË}«día‡ŸøÞywa¬Òò-rlm¦WŠÚZ‡OÈóðÖp”âŠàp”@ÄCÄ^ÞZLì+ÚÄ2²Ö½«dÕ¾s}×âk—wUÄîfí±åد¿Î°*£ÑÐÞðß/÷ïß¾<šCµ~Ì &^©§¢PÙ3©xZD·HÙÑÙüþßÁæüoB¾<ú‚jT—u.çoÙ‹Çnpá{öò?Ϋm·äˆþÊà&«dÉ ð{÷‹1õ×^ V†ˆ:Ó`™Ã&Ã˃55×rmð“1††©´g}5¿È†mu~«Õ ÔÁ×rmPÕÈœ8ÂfasÎ {Þú9Ï9Üåo ~u·wK5cC7%ìï—”øáðõù1ƒàLÏzûP·÷s‡:ît§»NòèñK·þÞñ;Rr¶äq+޶#yÚBÞ€¿¾c2Ýéi>ﮇÓñÒíàw÷|Ü£ˆ«\aÑãóãçùŒÒç×ùò¼›Ï°ùKw8Ò ·HvyyÜ==_áú:H#ð¨/óñËV¢`¢)Ú–0`Tå`CT8éìçÃ7”®ßñ­_.»¯˜ðÛ×ùH… "®óùüüT…›Š<ïN.^KÔù´W§ó ΄›w¼Ÿ%tð°`c:¹´ã*,˜<ež-£AXç²WBlW¸m®(º€Œ.£]Ñþôôã§w=d€‡êjê]ñ¬{÷Ø_Á"X½ÃR[RŸ~U[ÌÒI3Ä9§Í7ÿk…9p®þÿķ¥Y÷°;…èsþך`uxòÐÝÞŒÎ÷yszÞß_®ðö8@H› e‡ªL,}Ç(Ãá3Œj4wE7Q4Xñ`Š 9¿Ð &&/‹‰) ¢Ô:¿DÀàáj€Èö=glÙ+€* 1Q¨ªPD¡ýˆÂ†–„Ú‡) E04>Taøª0”ÞXŸÅb,{µS¬.¡B¬¯Én2 «b^]Ÿˆlß9Ä@†àWU(BB¼ Laq…P…!8ÄB†àWU‚C\!T¡ ñ‚0…!”ø†øÉKˆÞÝô:Ï•¶†XóØH©ˆ—,UÊâ•P†xÁ0¥‹"~ΟОå~„ö,¯Ü~hÏòÊ݆ö,¯Ü\hϲ d”±ôí¹Z)51Ú 7[WÀ nÜ€ÿn¶cpa¿¥Eœ6E…1L›q`‰2(ðz(å%yi]¨Z:5^„6¸Uè ;q–ß^%Š"J?ÅAŠ Q ]k+*Ä!Í5‚ ÞîC5"ã¤U#HQ!àjy|U+*tu¡¦’(jÄ„sD E…ðG® ÁŠ 㣫O+Š @ÍiYQ#JŸ›Ó²¢B¤„mE…`E…€§åšÓ²¢Bè°šÓ²¢Fä¾4§eE…˜"tA5‚ "Ç­ê³¢FLø j)*7¬“@àDRúÞî¡L6„2YÆdE“aLV„1Y “±0ÙÊdC(“aLV„1YÆdE“ ¡L6„2YÆdE“aLV„1YÆdE“ ¡L6„2YÆdE“aLV„1YÆdE“ ¡L6„2YÆdE“±0Yó©1ÙÊdC(“aL®s²¼[˜ÒÒÛ9™oæBF¼•ƒñÖÛ'Ä›o.÷éí'‡€7™N€·F€ŸßëºèºÚ˜”ñ=Ò0ãÙ@%qè4c.r©u¬ÔÐa‰z¬Ñ_;—n‹{ðcÆd’ Ž3i}ž¿~tŸ.U•õ P1`¤Š!<CÇ‘ƒ…ÊI‰íÀ8"j®6 ~øÁ4×x®/Y[8aà±’\bŸ¸ û‰ûYØÅÙ .>hƒKé.« x¤8ýv*ÜHM˜"<hÀ¥³Â–o¢Áp1q²=@fGû{Üjô™üA<˜€íÅ$”nì§¥¬S•´Í% ”Ñ#'pïG†EJÐF¶Ñ…°bÀ¨Å2ö…Võð)Ã6gK“ Ôž´vÉ¥/â:˜OW‰ï‡†| QD‡i$æÐ{yç°í‘ZßÁÚÐøu|DbýÑÉ—DÐ`>{§É7Du¨Dr´g¸(FHË‹õ@Öê¤=¿|_ÊòÝ5Ö‹(ÞU!«›µo¼{ó{ŽCä†{ÎN5(rTJ)•+ì<ÒRλn1Ù4J>ŠrÊPCŸsc›Wžs³®š†ÚsPÏx™|»…/ ž‹N5><çÀc*îžc  xR³vl-ÞI’å¦EãåI™µ_{÷íêjïѼ—žØQO‡Ü2™ÆÇ„ƒ&R;ÔbÄEÈ^p´µl-ÞI"8f“²|ŸZëií}jWWëÜxÏê=¦=?2H˜¿UñÒr¢úiB¸Zd­ÞI¢åFX¸,ßÇÆZEó® Yݬ}ãÝ›÷„eK; „©”5ÚŽ‰Í ¹3q{Q8hž®Ö#[‹w’ž{?”å{n­óÚ{nWWëÐxâ]Û”(ýt€ÆŒÊkPIÀצRÀ×¶g0·4øÊSǦì™{.oú94¶aå94ëªi¬=Gó I!Q36É! kÐS!M€6ðwGÞDŒØä‘½( (Wkß‹HÞI"8<ŠRì;4ÁµµŠæ]ººZ§Æ{RïØYj5uáa }!~EœÌ`†Œ€31!…÷ 9TÓèºÅ:²µx'‰àЖeùž[ë¼öžÛÕÕº4Þ‹zǾ™in¼P1,š£Sà£aT‰œ7L‘©Z¬[‹÷$Y)8 V(ËwßZûµwß®®ÖSã}2ïÖiJÈrò5毉„œ‘„„³ .h+b [#SòÌÁÌ•åóÔØN+ÏS³®˜ê¦ª=ïeÔ¡Ñgâ3x/g ø'_PNÑ—HÄ_2v²rÂ}¨i)lJžY p”3ÈçØØÆ•çØ¬+¦º©jÏ{ßPá±`Ò! ]ÃrÀÇ@ó^ o"ŽX|÷ 9(…ö¢ÖY«w’h¹vY–ï®±VѼ«BVÿ/×UŽ$An}EècËXñ>^°r÷jCΔ%CßW&ØÖ ÐHE‚‰ÄA×+{=Ù§Ì©U¿#@–ëOÃÐ%xH*µ Ç êv’84+tú¿º zÝz\r}å]תmoÞæy¡úA^[ž=÷/-@=4³)½–ðÓGƒ³¬ƒñàç……jf1$¸i ðŸû…í_™ûµ®C{ÌÜOæ--qÙG,mÈêi]*¬’‚Ùmò9Ž&“Ñ‹®Š¶ìÕ&Ÿ6’6dûfD»y²»ÃV?èqe';ú;ë ,ßµ?¤¸éhû›¿‚}ÊzM°»‚ÍÆ”Æ6{°]°–™†ƒUÇ:¿Î:¿2Ï{aÇ®˜yYæ)WÜ7¾M¾ê^Z˜8he…N¦r (øØ‰9Ø!XËLC‚«¶/ûµÝÐö•¹Ý ;vÇÌÛ3CF·Ÿ‘’NQóÎR Z¿7‰Þ’ê˜YÁf/…Ë+™Õ°•ÖXþ+z€ºé™_[V¬ï*ìY‚;¹j¤ì² gÉGˆ‡fÖßEظ™\ø¨ƒ‹u _VŠbveÂåÂM»YQÈïóFÏïìó^]Ñgoqï óÑ®ò! o¶.šm±}HÊÊ1 R[Q'ÂÓ<°ôãù’&/Ò°ô‡rAÊ•ÏL[ìÀêÏ»? ƒå:, ƃÙÇÓP¯C¶ÿÇLÁ,ί#‘T:+Ú²gçžx[ïïûFïïìû^ÝÑíÊÞ,û_ÿ[§š§¯ÛÄÔÕ‰§SÐ+†3K0u¦9Ž*3Ï‹.жìÅf:tGö{¿Ñý;{¿Wwt¿²wÏžeiØÈÔ“N£æ)P,ÓJ’ÎÌn£Íq4™|^t´g¯6Üô¬Óèù=_h7OvwØê=®ìãdŸäÞ=%!ÛE?d;âéPô›¿cŠäÛtsFoÌb|ÛÝmÙÅ’ð%Ä~_7z}g_÷êŽ^WöåÙ % ÛF§EOö7ͬp/Çœ6ÜÇáçEE[öaÃM/M:Àù½ßèþ½ß«;z_Ù÷É6†7VµÌ å ·£žÎBƒí&f½Yw }±Pͳ¤©ÙÕ²åöëé5]h7Ovwøê†ö½Å½+äŸý¼_ÅDY¿JÊZŽ©ÿüZEê¡ws@wo)’\Åá/²r ø5ºÊPjƒÇºÐÔê¯u2m¾Ulu[š þüͱ+«Š(¢ †¬*Rk«ÜÑU #»*7ô¡_—ò؃YId&æ19ÄTªÂ9™éxYÀy3ËÌ·u×Y;$:›Mém(s|MÓ=‘a7ìŠÝv¦¨¤6qà½ÓRU+~šØu€¤¨ÐØÅ”iRìL&éÐIvQ! Úçð¸TÓ„¨(÷„m@Õ,ŽFáÇÙFKšzÀ¥þü‰?(‹Äëüßã¨Ö<î ÀÄÛa& kü “éD`ú÷?èĬ¾'ìç ¨†ríÐð ÍÓ$n°âšëö¯{Â碕ë©÷Ä'¾ß#äþ|O¨YAŽí 爭èUôÁ‚Žk¢î}Sznâ\ŽJ®å ÷„âèzqUê\œG൜]á+(õºŸpÿ^tu@#îX±y.'Ï’f©uô¼u%º_P MXˆoT6N«óyËuÊ­KýtËX¿^ÐÙ®äÁ«ãWiÙ?á$>j-ùª÷axD ‡C6¨þœžð¾¼Œ@y_ïÍb5Uñgú¼ÏT¡v…wìÉ< [÷ôÇÿ6*+j’«"=X„ߥæÑ*SçhæÀryÇ#¹Ûó€¸Kuºz1Vän¡yÄæã;y¶¼žåMûãä© Jÿ"J  'MíÜ­ñëó.`ɸò ;%ÛmJš&ƒÒöó²xrÒ'ÿ¬@ëö{±½jŽ%ç§­àñÆù@ÒµÕ¼bjywÑ!A½³<¡Õ¶_é=…Ï(ôèVà’f$vYkYOèa§s‚aò¼ššEŒ?Bôæí­ð ½±á5‘Ìj§YzÄdz0]ÊÞcŸÐt‡ ¨¥ŽØ…=bÇÓá+ÎÞ¼ŸÐͱO-4pËW{—ÿdz|_*u7WÏ‘ ÓSå;޲ÁuD¶v$ip+cºÚx‚üUëW |R‰zÄ#L°hœü2ßDÍ ,˜ r,@Ôeá,Óºk£'ˆ¥b½¨ þOa¾y%ÇPi®'ˆ°ÊŠúÕ)fͨÊêBdAÝ2D¶x^A¹D-:°ÞÕ(öÎü´:Ƨx x×»ÇÛmÛT†×L›N_VŒÐ:ÊÊ^äÍ[ª½žQ­áÛ«µYç¶×>*†ÄhdàƒVune°2Û 2 :@ô9RóÈÓVTÆÇHÈ~ü¶ÞËÜ¡GñŒ¤÷±Ð´ƒZKåkÚW§ÞÃ¥…I°Ú Êç•W,h(ú\-ésAh¡¯)8ø¿äá®_ж}ÉйUqf• ÉU":G¾$.2(e%ÇÂDDoG¶ÞŽ‰ß˜È¬BÄ”®UÇe‡øxåijw #`gW©ëvDH<ÞÎGð¨VåY,ï’”¦ùvDH¼3”´žkYE‰©eêÒËqAB>‡G5êßí¼)I/;ÄǺ³V1*\Ç€æZ´^vˆ…ºXÊÊÒ¡ýØ®o-oÐæ¸ ±ú‹Éjҭͯ¯£AoG„„ÕHÅ"›´}=">/;DǺX Qi^Ýšº³ßŽ ¯]Å0")7—œå²# Iå6•Ÿ5kD;jóvDH$'Œ*šB*h`­é:3]vˆl'ÃÅ£š49´É™ÄL·#Bƒ6jd‘†E~5¹Cuí}ØxrryTgâŒØ÷–—}¢µƒë‹ª8ŠLrýrÝ.È·#Bb¿(ìñ)Ì-ýÂv2H·#BbêÆ””—kH„u:КvtDHllÛn’Ê’ö°›#ìh‡øØ)3Oà1I¹% /WãvDHl¿“ÍQrhù€£­êÑ_ÊíˆØÓ—Í©Ô’ÔK¸I_eËSzŽ¿^­Ð3ûú£Z² 2%N˜·#B¢iLÿ¨’ìE"L™æíˆ(j QTìe.Ç€îšqF;ÆG•Tj(ùBYeËÕbº„ÊšªÅºìÒK• ¥b½TÊn¨ž£p\²ŠëÂ)¢×>¢Rü矕Ÿÿü—1ƒmp‚.u¬ëx訇™ªÉŽêïíüÁÃà3vµ¾ÈK@(‹2üfy†«;íá'R*ºhñ³-¡bÐòÓ™Ž ¢ñÅM"{×nÜpS¨)‘lÌF¯ÔÀæÍ»Hk¡EÄjiNdKKVgÆÑE›j­´¢ÃÞ´j0ÒuV-ZNKPñYOW!•—»Iž"µ¦ŠlvB¦ágkà[Åæ22b‡æ‹Dá&kùƒ'Íæ©Ûª8й¤#§K™ 1]¥ËLK©¢)pSµ£Ž–A“é„„ž®ÊÚÀø`¿ ‘•ÚQ(g«î¡™– &Û`B³}4\+€zÑ™¼}²‹õQM ‘óþ^Ø„ÚÍ“ýuÈê­{»öþ±¯ÒzÏRïr¢¡›~«aU÷ÐÄÃì¨Æ)éÍ”cûh¸9²lÏÑoÑ“‹!Ñèy¾?O=Ó“›Ãw´m-ìüî¸v•Ñ{T÷ÐT£ ‰[(«‰ãÂö’ú¹üxfµ>*Èûžïï•ýE›yr›í+Ø··ý±Ú2 ™šìSÎÇ<Ôöm‰¦ž’ÎL–úG¥¿ÙI6ãàF>ÉÅúè`ÀÃ=¿oç‹>¦%7Û×v°í,îücïrˆøÎú)àb)~õÐäo’Ü|Mäe´šor³}mûÎâÎ?Ê9KæÙ®‚›g¡ÿŸíªGÂ%7ùVí¾ØÁóh$ÍÏ ¶jÓ½¿ÀÉ›ÈÁ^ßÝÐ æÙÑH F‚¦1¥¾ ]ý¬â¦È¤Zný³ŒmD ×]5Êé`žÇòn£[‡˜Î—ÂOk‹¬„ýM(=¶Èÿøxuƒ7JCq?Ljó ÑW¡Iв÷OX#ɨMúú°6ç¹Ö/þÒZâò~9òÇén±ÕØ¿¥QL>Õã³Þ µwMGš(ê­ˆ›?i(Xž'­9[¥s ¶fýÌUk¾iéÒr²Ÿ–ŠªFý] môHk‰Ë»qzX+¶û×94v÷]2paLWiF»Í$fiŠÍ“8e7JãÝÔòí‚å^rÙŽXÆÃwÈ::ŒXû«á—Úx”Ýyœ4c7’GÉ®%Ä׊áךÖÝ.=¼÷x”ÝY`®ï[Ö—w)âô°Vl5ö¯Fú|y¥à?/d®4wÓUÒ&ž#Ì~2Ùô£ ?áÙÛ ˆ»Ï\Þiêbñ,E,㈪FýÕHùß<„éáŠOã<)§D)wcLËØ:Vøî¢[A’hÄ2‚دebøYG§±+q}Tåoƒ‡Ÿ¿ƒÛC3†¡4¥(‘£­ßC(˜ FÖ(€:áÝ%w3;bý´®–Ö—wWä鲎Øjì܆)á˜# ŠF¦ášLUH–ª!öHåPìžêií9Þ3°ï$߈u€]1viùv9ŽSV·Ý—ÿøØ56HCÑ~ýv ñ¾„|:ü¤ñaàίéœ#Ö#ib89ÎcEV#·íÆÑ0R¨¿ž·ÒP$Kã‡OâM8Í›tZoÖÂå|‹xÖç²åù2–¸œK‡‡µB+‘s7)>w_š©Øˆ?®˜6ðçÇi#[H‡Ð(EŽ{Ër’)¥ç©aqp¶ üÄ:ˆÖQ¬Stß!ëä4ö°^aÛöÓ%¬ô$ üÖ¦]30l°‰sÜá­„ØâÖB±ù­†µ [Ÿðî’m¿¼MÇúe}5­%.ïRÄ鲎ØjìÜ–oÛÏ`^˜.nV­k(šÁáüg‰âG¡˜ÎŸÒz\…ó!ê… c²lµ:ÙŒÒÒ¥å8d?7,UÚvßF™9)ù_ Œ‡)Æue†À'Å-:„çmƒUó*qgòí·c¶Ø-_‡x/c—–oÉ::LW‰Úöüç „ÚñTßH“k†þ5næ&.±).)º## k4_Ü™œ›`»OgM±|Z'Jc‰Ëy*üð°Vh%rìž û°;ð»ösݺbbèÀ‘x)!â6vYKqÙ•¦1ž ÷¾]ÒaŒ)×;£XÖ—wWäé²Vh%plþ‹û‰ÜÇÍ|´_q\wÅÜ óÛó3Å®Þ ¯‹ele®M°ÝÞrÙºÀ2–¸œK‡‡µG¶ÂæÞfŒóÄÕ:åšÍ©4³]=îÛ0<ÅËï!åÓúGSƭʹ$Ûît6×}>Kk‰é\rœíÆYœÛwö#(ÐîíO0[\ö'TPBó¦Ðx)!vRƯï6ˆ/5ÁBpåáÛ$Û¾[ÎõÎ%.ïRÄéaí¡•ÀmóAÞr⥦þä0Ê$ÍääÁý€'ÞË››KFíñVÓx2¿ÓùŒšähÑϵŽú(Æ.¥ëýà°TX5l/’íÇüüñûoíóçç÷ßþùÇ_ûçßÿÑ:~yw±p꟟™•Ï9L§LatøSHÒ­Áí¦ Ô´Ûºõm2AÔùÐ%êc‡®`Ó›FÏÚp…a2H¤‹NTÀ¢§Ÿ f#€+œ_r¸9L1 îúÉWÿ©qf·‘Ox¦³à?5„Âçn½ßÍ~ ‚`©@Iᆆí>pø/¨Gw¹‘ àH¹£´é´Y7Á¸qc×é #uÕ\vJ„dgh®Ø8HÌ»ñCÅA,ùLB©ï½'¸T÷1mïd‡³À;îéhŸÉÖíq`f¢ ÿWËÆüØFÿö4i–&{§ÁS<43ºÄ·ê2w =É¿þAïp…)ÂyJ|ƒ©g¶¸éwÀÚq²ËYÇÄÑò?Ÿõãèú—ÿ8æþº‰¸šÆÇO?÷žø”Ý•â‰9ßW¬Sïf~Àeï|™§<Õ‰h¯·‹¸¼–~p‡¬iò§ä€ý™Ýö_+)b‡ÝHú¹XNÊ¥§$¿’ËŠ»f›v Ðü\ö.JÒ§dí×û¼^iì;t„¹îán™ýO)Pc™]ÆUK}hǶ¸ŸÓ ÉËê)u6#Øo_…;®’?ƒ=!ÊõYå‹&í˜ÁèK9gy§ÕÌ€Å8ð,\è /nÈ9¦â„¯ÏWÏ`  .OA›ƒ™oðc·Rà'v^ó±¬Â0‹‘;t=Ë3Ýlò ÜbÊ5+c ÉôÄħ€äPÙ,Í7jjÇYÊ~ô‰'Àö)è‹÷pôí»u€ÇÚÑÑU2£`hO ¬ïªŸÑí…+ÎkÇPÎ×wÑ$žÕ4&{‰5Ò€ÒD´nó[„4X¨ó<«á\Ý@h¾zS4« ~Ð ·liOéq#~múdXšžvåׯ­òY­óTeŽƒ½´R­ctÍ ®îû¬fì‡zféÌZCk_Ö—½œ7ó§t÷Al1£I€®í^;N‘ óCpM–ð,Ú¸ÞÄ#v¦sáA,š˜†ùl^I?žÂGöH>ÀÎö&(Ú1kòvfК§Ðô§=ÃJ*iO¬ß?²Ê9ÙÌäJÏ"OØt‰MÉ+›JzµÒÏÍ÷ { +ëzGkÞ4M;¶ùi†÷bwO¡{—ž†c:]áÚžzç[‘ToÁ+‹l~äð{e•±ÎäK›77*Òé™;~–¸òÅ1Ñ%pÿýÿè.ç|jg×ßj‰yo·2dªìâ¡Ù¶ç+}NBOÍË™«„gÑ﵎&;Ì*K”Ù6^µ~ñì "=ÎppvÑøQ‚DÔ=xú‚jïC¥ìÇ/F¨¬Î^v¾[âÿ6Z³ÒÁÛþ¦¿ðÝ{¾˜Lz Š¿Ð])Yß­$H²8^èîqÝeŽ~[ɪQÒÝ9js.@z‹¶ä_Ùme¨îàä[sÃÁi[)ŠÅ››wTÅ;Î:¼}ÉÕâMK»ñÍvH&;‚È‚j¹¼ ©¼ðÆ‹@͉[%yEQMÞdÔ›„ÙNþêbüµÊ/‹š§(¹,'`츂ºòrŠ¢šÔD3vùˆÇâIî‹ýX´ÕL–¢šÔìNIaÛnõÁX‰EQ,jA˜úqöJ†|öd$YÇKQMj•¡y8¿ìfbê˜êK.¥lO¡¼ÖË鼃¡^ý¥( œ@>ÎWÙ9ÀFG°Sv¿%Wƒ .“ýíq’zNA¯ä¤­*^&±nM4$¨¼LèåÕE5©0Øÿãä”xÞ7þ€¸(ß´(ªIÅÖ“ð81íF2Ï`¡l"K®û X#ÿÜü4òOf9‚yžûK± *úOï·d¡Ã¥.H1æKQ j?¹5û µf\ò˜Á6lUQ,J‡ê¬GÄ9E¶{Ód1E±(=ïä‹=¢§ñÄT“›rÝ_›hSYœF̪2FE±¨}y[îN 5t]Ö-«¢šÔfë)n‹·¡›¿ b«¢Tú€$“Ïü®3>c–í¥¨&ÿå»Zzó¸­èÞ€ÿÃ,e špH‡ÓeѺ€³k´ ²P&²“B²\YF‘ßsŸ¼#KŠ|:—÷EÎ}Æ™«Ê.ó`¡Õf³•“ÆJ á@ˆaÎé¼bÝÉŒÙdÌ>R¶ýDˆ"qxZ)sîd¾¤ÀÇ+öbã$-±Eâhˆ”‘ ­P#¥¡¯¯6K.õDa*܋݄ªÞ_F§*±žQäVÚŒl‰ÓB›1Æ=%Ø ña’L"jnó¾âJ)âJgÉ>@8ˆ m§ •'“n{iÖ®ˆÙ ©óõ,½>Õ¾ž­‹´û}?ôÞ9^†v }\9ÜòP7 š‚»£Òæ¬h—iTÇe01Ÿ¤·§Ú·³u‘vߢ_½}³M ÿ¶‰¦*HH/dÓ.wq{\ßNŸnß]ýçí›^ÉÚàŸŸâAÇ ùüJÐÏô_‰ ÿo’çÎå9AnåsÓéšœ£Î¾³ÒE© 5æ«ÁCSf 5ÆÔ<{û…Êt¸ý—oãöééædZQÄR÷Bpƒ­+µZÊh‰èÆgB¹}ñCѼ¶FW¯ãÛÝ·ÛëÇ÷Øtóg–`ÚΙEì[®ºÈ¢è<‰\÷fBØ!¤ŒMBóµg/­0¤g¨\, ºœ¡DÍP«@•-3•Éå€Ó(‚´ ŽÇùJãΔ+Ál«tÏSðw¤¿yì-I½§ÄîtJ0LTT½ã”ºe\€;3 º›ie¯Üß—óý <F7w÷7§„×/¿Øg<tØý”ÇBx¹„@4ø^CöÄ—¼p•ÎÁ%;”‚õ䉹æz4À‡ãˆzž}™…JQ|™S1/cåà»÷¡:µÆÛ !äÇ)3˜£S«µB±ó(µoö < By¹P@ ­§{¼R)†}í6ÃÅp;¢ˆØÇep€1,CfŒ9³ò%ÍÊÛôpóöÍG}ðß÷ ­æãÛ7]FÏÉŒ&´JJ¤ðâýŸŸ¾=ÜLëߨßßW•ü¶cÞLÓÕ?ÀuõÇÍD—ú|ü5Ýœ´*~þ}ý}Ø(pÉ/UÑå"šHËtÿåæáúñÏûÏ_§÷Ðüðéæaº~x¸þkúrýxüq#0ûÓÍç߯oo§ŸÿûíúA^õ ·¼øéç_æy¼2V”ºO¥ Íðì…?v÷Žë†}Fª±|Gp Ç™¾ÅÝ3 kr`0‚1 ®¬' F0?ƒ”¡.¼¹ 'Ci”ƒÁÆ€`ßNŒ` ÍûIƒŒ­¾Ç[8ÁžvÉ´ê'œß>0<ù8þ-0Ä„—ßF^v0vyÙÁ`ØäeƒacЗuÇÎ /; ;ƒ¼ì`0ì ò²ƒÁ°3ÈËÃÆ /ë ŽÁÂÜ^ûR ­PÏEµÅ¨3< ZAcø.&-œáiÈY9ÃÓˆ²ø0†¦žoñL@Ôó5žùàõ|g>h=_ä™VÏ7Ú9¼ —V¿’ÑD¹ ÿr‘Þ]æŽ}u½È‰þ{w¹Ô\/êhWÍaI€ÝàR÷‹% úõêúv”ŽÉê2ͽ¡ sÕýí¯Ç›¯^+=(êùyc”À¿2ªé÷µQÎkš·S !p¬‰ZkàBàhélƒq<ßç~:'Îñ]±²!޾ÏËÉ!ƒ+GMè!Ò„8–NÍ8p!päºvàBà(žC£nóròCcm4X!LëÉ! „åi^ij"¾Ö×ú¬œ{dƒG†qxd‡G†qXdƒE†Ÿkdø¹F†{dƒG†qxd‡G†qxd‡G†rŒÈ°,òÈ0 ãðÈ0 ãðÈ0 ãðÈ0 ãðÈ0 ãðÈ0 ãðÈ0ŒX34_˼¶×j†œ¿œÏ|þj¦ ÇË9°æ÷—£SÏ_Œ9å›>­çTÈ¡ 7ú^¼ ¤¥˜JmšÐÞ f_.Á ç@nÅÉõyî¦d”r²V„&\}¶dù/_µ]oðJ4dD<ß±ôHDå–9ö5¢–•V¼Ò7ÝôÖĉS:ÚŸ¼#Š ¯—Yû`ãoÛóœ›î3ŽÖν¦lF@ž…6Ò… Ï­a! ¶IåÀ:D­ô¾I;ÞI;},ѹ’uÚ¦v±‰=ˆ¾%œ\cH¤e­ÎI`_¢k´…|!žWæiR yNTŠº +é\-È+^ÖhAlY…¾­R§±`5©nYÂzǽpQ¼¿´V’„LÀ#¶2¹4âdÊ’Â>ŠžÖ¹ä ªp¨V‚™Vas,:~è¥+q|ªoÅ(sæ7ÙY›ÃE­)!Ï…½1é…´ªnú›y9Ëæ‡;§”K*š –MXý nò1òC&+„ZI…!E¯‡m>Á•¢˜%•PçD\´r„™^Fjh/Û8GHaAC·`3m¢Ûü=46ɇŽ9T.€âÁáÄÊ-^ú\è ¢ÖˆÅmnôø.ÜU“+gÄÖ–9qHè9>GÒM¹aµíÂâÙÉóCó®Ëè"WAmi­¥4tÖ¥ *‚+«©ìƒÊÀ‰NŠNAÌ»Íy߯9—³!­Ð5 v«*lE‡-%œW¨©¼áz1 AúrTz¶mÀl‘h”àZ¦!½p8«òÅ¢¥».Û8FTõ ¬p(W‚7iu-x~h•ÌLаB+É©¥ Ÿ¬UJ7…E¿¼á,ᙵšòlaB§ÐÖóŽ5„™j…fØ$Õ­èö¡å?sÎ'¹:£¸Âj©ìh“×0HÉÇâÅ®É_Â…%M9#µfÞðySWEX«h†MRÝŠnÚÖ*—2Ý6ÐñeªPhšà@ÅC³:…ÔÙ˜bØ$W\ífL9#¶†öJ™jç™\Z¡)7¬¶]X<;y~h˦´Påá« òSÊ …`f(Õq?„9sɯ$b¢¤}2Í ˜—ÆÈÍOwZ†¨À Y fX…Í«èõ¡“H—Ñ„¿HMY¯Á‚™A£¢ÿÂ+wD„MC2Q½tÍŒ˜»ª+z¾š£"­Ðu+6Ë&¬nE·±2÷ŸÔäxíHIéfxƒ «,¯„Œ‡!oLCÄäÊ ó‚h+Åé²ÌAR‘+V¬v]R½Š^:9šû`ñƒu©â$ò©ÈK+añ@ÓFêÖÉu3bö¦Îèù6GaAC·`3m¢âWðúxá.¼q©Ø²öe2”)o%Ôp‡uW,¢ éF Ê”7íV„wž ô˜_~+Ê•`ÆMZ] žaÖçŠÎWÙdÐ A‚0À êí!ì<$Aa.“K“u|~Õ.ˆÙw”伤!J½ŒÌ¨ ™KÑå#,0™^•²Õb”Š”¬|¤nÀEmet ;¢Òp¬‘¢]³Ó¶sÄF Ò ‡v'ˆu•6ߢg6ÉöâŒlígi'mNn=yó¹\îý]Z_P6jÙ^´½TͱÃq•¾-ð›kU½û÷T_”RCïºÙ,²-×Ȩºm튽iY¯Ü«ª,+îè÷ùƒ²QÛ¶Ñ~´«Ò±è8¤±?è’Ò-¹d½(ºYÇÙäÇ÷Ä o’ž9ã–7_–v” íN$ B$ûØõ-M’ÛòbØÉ®L¿‚Û–õ+‡½¶ Ýp”²Ø×q)$±©™úf\¸R×–ÃQ’fÜ=LJ}áçŒ^¼”7Iãdøá®M×¢ç*Ý1W°æpMéæè¢ÆÉ-Q1“Ù1‘3’a¦¥})¢rë—#.Y" ©Šq–6Ém ?ݵé[ô]ÄýùG“M8VÌÖÄv%ÛôŒÓ®K÷@<¨ÜÊ"‹5¤Eg]ô¶rJ]Æ×Ä&»„nzþ½(˜]Ú$—qÒ~¶+ӳ蹊ëx)ãŽÖP†Á'ã´K§Ur íÞdãm,†Lë o\»âv—õÊ»n²_(|âw!gÝÚN.ëÎàéK›¾EßU¼)Œ‘T74ÜdáPe¯ÆP\êÔMÌêtW<»U^¾–éN(ÜdÃPåßoÅWK›ä6N†ŸíÚt,:qÙ)†¼âL†Pz©M–©ª"%Pí¤h6j’1É/Õ¡IîvRq™sè¥þ]š]P6jÛVzMUõjù«rÚñÊ,x= À:"9-£[‚Àk:•‰sœNšI[õB¢-ÓAh¦ë»¶Ï­Mr'ÃÏvm:·bO_­ª”nþ&h§Ò$è×N>aÈý¿vi‰1i ¯Ý6ÄÓ[%¼·C"|YË¿‡¦\ìËF™öÕ&i£ »¤å.UþÚDšÃe*€(Öȧ©}u›žÃ蘮e*-˜ÙP„E¢Û€á¥n|ËÎðNL’!ëÅÔõ£˜[rS·ªÛä2ÐóuvÆÔ±…I¨»”¬AÁæ&Þ°bÙMÐCçåW©’Jëtz™hCnÀæ`‘mÇBƒCYeºM9Ž—ŽÁ¨vÁÅ_­Èæ”^¯k1&L¤B©U‘Ï›„äÚ$#éËÿƒxl…cjTVÁÓJdÙ&Œ!™öQ¢>ƒ»1¶a´5\nˆÖ%³§4àÞOj#\!6H3Ú“>ܾS¿ä4Rã{|’0ñ¡ BR£·ª„—£„ä&âóG µÓ¡›^^G~¸DA-+#éS¾KtæíJ:i‘E ʨgN?eRå6oj#dv“f6;*t%º'¾ô £¥vÒûwU•ãëúß8JÉ%."é<×G‰âOäõÙzÊœú¡`)Q€Ö”Ñõ™ß%ÔFì~›õÒLÜmaIÈm’‘µ¿½Kømz¯Áj;YÒ<‹Í‡Ýèæ3×Ksõ]Âl„†&Õ×ÌæY¸œ}—^ËF-ø¡½7ýØ1+– ”áZêÉ[½ŒöþQBmľ¬Å£F§¢ÁШ)¡Å£ Y›>J¨Ðýe\fTºÅ8ÆÏ‡ƒFEÑΛ„Ùˆ3&»QÔñ1t(Ñ'rHmL…of# ²úm¹$õR޹fŸ;H• m¾C=ŒÉÎ6_%G›ü^Øäk˾v6ø5…o–O•ÔèçX¦DcùÔ¦=÷ƒÄÍòùµÇý¸|M”—ìÇüw@ X” iwŸ$ÌFXn5*—4”A l«dh¥¼K˜]ÒWÚˆ X†¨ qïÍKÀRGÁ¨|jbr‰âÑŠùû£D÷Zu6 øcën:p™K4$­ÚÂÿ(¡6"Ø“lµô”f8OôÇUszW’E7}’0¯)Å?CÚÒ GĘkã› –&>of〫cì]ãD²”p,‚½ïú(áXdÁã›+Ð<ð²KT€ï²7;ýáÃ* JΔ»@cÍc=ªå“Äø¦IýÄ:ÅÆÐò…íf²Nuaù A‘!¦JØê%3DV¯Ê—æýÁæÇ+©71žY+÷w.Ø…Îê‘KG6ÆNP9õ½åHkG_h!¡ivD›råØú“Kø€ßõ˜bÍÏä¨,ÇžÅé9ñ Ê6®°IÐK:–@¦Q v3ô…ô*[Ë“ðu,ÖHÃò ß„åÁÚ^Z=I—‡‹Õ·v…͈*ñ”ŠŽ»BÆÏ+¬›U¢ërqãX *svm›UÂ}È‹Ïvlƒ gÿÅ8Tâ%\~þáÇÂù›UâË5B©…óóBà„õ›Ub:(Œ°¾±/¿Q%äXÓÃŒ°ô ¸}3¢FÌÛÊF;W… ÔUb1è5E ^œ.ߌ¨*lCèËó†çò®Bpâ•›N@â÷x?ÀûjìÕg1wòÉK ½é Û‹ôÉþÚ6š®vFT‰-KºÇ«y!ôý&û_Æàó½ÚÑr`D•ØT+PXDËÙ×Ç‹q¨„F}»DÇmÆÑð¦ƒünüRùŽpøvx§ðwSA:ÎÉšyÀ߉Çx´»Q%§æ/I¨+|8"ÛÀˆ*qâuÖùB¶ÅóÅìfD•0FËÒdoÔê+àÖ͈*q6/0âV‘ÈD¶S#ªÄß0U~NTº‡JDÃ÷ZG¥ ;ÝŒ¨²¡ ±J¡Šz"æÜŒ¨ñNÆÐ˜Óñ΂˜›U"ˆêDØ„˜èLÖ£ˆ(##ª¼0h%Úßø1ÐA> =ñ~–>÷B‹›U¬qJí~ýõçÿúëWþúÏEFÐ@Q7ku·¦¹EŸ´ƒ+ä4úB%âÆ;A}uà›øŒõ>+GO:Q+,YIøËÖ‘Û:Ù­ÀYíë™\Ž‚ÅÁ=òÖÙ*Vç+Xé–Óà#G“\(†pñ$û¹$¨ÙHZ©è焹IË£‰PY.CÎÇÔ)œ»Ã&bóÉ,·1mDÖ5ÜÑádî^ý1þiCnCb=iOÃP‡aç€~®ú€Tæ‚Ó0X$èë þŠOUÉŠ¤Yq~ü Œp)9ØA`ààÊœTý߆=•!?3}mmûoHÖ±èM~©Ú –Ém” ?ÔUéRtYÄÿ-Ùÿõ÷×…‘ú¿¯PD{5Ʀ±TžrÍMf@s†Vée³pLùÒôrÛJy€ú4ü.1e£¶m£ýhW5¿‚׿Ÿï_û'0:W!]Op©Æ®ï*ãuUsÃhY`ðhKµã.ÓJ©4ZÃÜß§í!®Mr_ ;ÛµéXtÜ^1<ÜÍ…fNgTù +?ãÊG`9Õg`ý ¬Ÿõg`ý ¬ŸÕØñ~¡|íýœ1½IHÿ(h=ÆÁN…™.ûPÅNªþo۹ȸ’­¯¦Ýìu³Z¤€`iîïM7¥½H·N†ŸîÚô-úþ|B|Ä%M SüÒý’1T3zR—DÉ.p6†TöÓ»]¿^0_\€YÚ$·u2ütמ_Áåç³mø'0’—@O™ìÎÁœÑ M‘M2…È«Ósmñ HëFý¶ãæ=÷÷¤ãʵIn뛡§»6};|>[lÐÖgï&ŽÝ‰ õz¨ÝqÍH^cE5̯ƨnFeç'èå•çþœ¬f©Lrß =ܵͳàöÛÛÅ™ú•à ZÆ,ÀH“¦ÜÓܤê«#‹‘#¸¶]º[WJÅo›^þ}èl[Ú‹tëdøé®Mߢïoo§8‘Ó“‘^>}ºFRû§Nê8e`dh@KÛF¼[WÊ»6&€ãAÙ)·m´íªt,:þ|¿$ýFÜW•-eŒàwÛŒÈ8ËIÕ‡+Î@3K_[»·/ëJý¶ã}Õïõ»e£¶m£ýhW¥cÑñçãݺ¤ñãE•çÕÔ3¦#¤<Ïò#¤»!•3¦ò ªœQ™öŽ*‡¨ÎÇ‹÷'0¦ÌL{¼±¦lïŠó9‚/=‹¤ê«/·ð®¾Ž5áá„[Wê·g3Ú¾ËG´j;¹¬;ÃOwmú}{¿i×01h~,0{å¨ãÿ'»J²daØUr‚z@âóôö×ý·ß“lB­ºe—Œ„Ãd• ôàs̺nseÔ¥¢ôÂþ.º:E^T÷dFq06Ø.l~öïõ†ùn&§¨¾rø­h¿×S5Pq%†‹è vÒßKÒ_óçB5o{_°fñØØÎ†°]øÙTà¶–_Y›Ú¸7¦¡,lã‚éªvÕ?Í{ô~&wo·ä)ç–6oÙuÑ"7M½L2ª²[*_¥x€'Pv½`ót‹ ¯®èφ#Ÿ~Í“Þ[‚í0«{£ƒíÚví?Í{ôü±ä׌uz{û:m½\5]µÓU»êoWýtÕß®úÛUÛ\íýë…{n·c;ó"€3¯¹UDziÏÍÞ"}QBåÿÙÏ=PZ½‚Í£÷ûBuCòóÊO²›2_u݂Ы#ࣃíÚ^ÚÏÎqr<–ôα´QQáö&ë"ÀB‰Ü˜¿øú…â `«Pd뇒i( ;öqÁtU/Õ?Í›ö¾ºåiôÝý3nm^¼ÝVDÊÖĺi‡ö´Š@ûô§^É®òT‹êÕn½.î5e~ÉŸd;Ìê°ÑÁvm»öŸæM›NNs:¼yQáÓJOÚaëalª´QÝØ0cÔ®¨®È•Z<¿>´‘ em`T¶ ?û×ø:`¯RžïàM¶jÿ¢³""°.E•7¬€Êÿ³ŸW›Ã9íçÆîº€ÊW)­]”‚]>õ¾¢º"ýùäež÷Ãgc;ÌêÀè`»¶]ûÙ=IZ®Áh]Ñ=¼r£„Õp±õ䢫Ëk,>ÑÕ(ÓK—nFmÇd¶é>»×›Ìš$ÉŽºL“t¯ó ¥"Ò{ÑíkÝ:o•/R2 ]KvÕ[ªW¿EIà”ù[ú”쀨kÛµŸÝ“dGòëÆêÛX{k§±ö6V߯úil¾Í·±y›ocóm¬oÆÞ-|dÛºwÙÎ?ø µó¯¯Ïz""PNœ%g %TþŸýÜmÚÏ-;ÿÕéÏyÇ»)ó$e²¢º0:Ø®m×þÓBNjde«"¢º­lsèpÄG ù4»[‡¬x²°F™&k?ÈQÜl—¶)?ûÇOÉv[Òú‡À#÷péßàÓS‘Fúà¯Æ<¸*_¤ 0õ¹—ì!¯Á¨®èφ£A™|®ml‡YÝl×¶k?û'ÉiI3ÊÊn÷e>º.”s6ÜÖ]Ñ‚¨ ae~Øö ¶Ã¬îŒ¶IÛ„Ÿíã'ˆÊ"9œ¿xø ¢ÝcÜd§Ð@—$'ŸO:ŽAc«Ž4Ñ ² =¯L‘üœïÖsRæ› ; W÷FÛ¤íÂÏÖIîñœ›”6Sómjž¦æÛÔx›zSRg3u¿<݇¥ûåH©éèIGï¾ñMH 4,;n,;¾9µ'"¯º:W·}ø€ j 'ØE—ª,;,Ùø>÷½±fu`t°]Û®ý§wÓfá–ìëÆFED…O+Ý)!Ïk cÓ¤‘³Ö_Qº†%›|3°q eIÃ<׳ë=ÛÆ'¤,~mØmÓñ”'›4Ô.»ºEøßÏ#µxuËuPé"$U®‹Á–ÍåBq65Ê4_éÚFˆâÀà`»´MùÙ2ÉyÒ¶JF‘MVxR ·Ã”áZݹ§vEiE0Õ)ó]ŸjÁˆâÀØ`»°]øOïHŽgÉ@ß-ðHqíÿ'O5‹”Ó„Q“ïp…tð]B¾²`ózEqòc^hm²Œž•TÀ(€d¶ ÿiéQ"IsÉÿ 7e&¦.”pá݆ îil÷Ú˜_v—œÙM’ì(j;ö¡ƒjº6Õgã¸^MÄ7AµäªÛ*ùk¸å0Ö@çeñ'B…/ÐØ*#EU:™‡îbÁSôgƒ­‡2ÏwÃgcDu ÄèÎvi›ð³o™+î2]É#,mõçmkž¶îÃVÙê?¶úËÖý¶5[6zÚRm»ö³}]¼Lˆ“¥ÆàOÚ?áÉb‘.ƒT%½`³³ ÞÝùo²ùóWTW¤?çHîˆÈó¶?6¶Ã¬l×¶k?[(IÍÙ®)®êA:ƒ·]ñ®€T»"¹èÒKÜ/EV£LsÕ¶‘fq`p°MYÊþ龈/ vv;–Ý×:篋”7ÿždwNØì£C€gOv÷`ó\ó¦èÅè¯ýƒ´½q‚ì0‹GÀÛ¥mÊúöè¡"I;XØS+"›Wn´ÃWÍ„Ééä&‡{W[“2ϲçÆvÅÛØ »²]ùѼQú§Ë·Ê{³ÞV"À;²^WF¹ùêS¨Â'>´Ãª+)ƒ¿žz%›OçûŠêŠô烉”ùÁÇÂÆv˜Õ#`£ƒíÚvíG5Ù,ÙÍ)ÌTQáV¹Ó†j [ª‹×Ï•TV}Eånç’ªêÙa{2¨³rl`]Õ®úl^-bôæÛµ9$ ^›Ç›³îƒX‡¢{RÂǯ±XªõJö’[qT_~Gæ%ö©D™ç=‚’ ÕðуmÚ^ÚÏæI²!ù5¥Ö4ööuÚz¹Bß᪮úÛU»ê§«þvÕß®ÚæêÝ¿õ™|Ú”j×°À·|Ò¾Êï ¹·[D ¿°zi¼’)aõï.qYõJ²œÇW/~Õ•_8réGþÑW^°Ưcl]Ù®ü§yœlš,î“¿uªˆ¨n­,× ê·ŠÄréHjÑåÒÅ—‡êê”yVÝ7v@÷ÆÛ…íÂÏæ5~Vˆ0]¾ß-°äžóÏ­TDFkŸ)hò‘*_´d È‡lÝ¢º"ýyç)§Ì~ón쀨kÛµŸý“äcÉpÚFED…O+Ý)!ÝÂØT¡Dnl¸±+Š p[«PdÇçÞ˜†²°aŒ ¦«ÚUŸÍ»K0Õ‘ã%ß…¶ŽŸ«"2ÚƒZU'ÏáÄW‡+ÓÉ{èâðâ;þÍ_£œÓHWûÊœ ÅðÁƒmÒvågã$בüš­Y7[ÿ)¯v$Šqx•oðʶþ'˜ª9Æ8Ç{ý¥¡øM2¹À! šfñű(Ʊ¨ˆc™u«þž«¿ÏÕßçê¿çêïsõ÷¹j:˜<ßùùósýï'^Lœ]}V«ƒØ¹ÁµQ±ÌY­9’.4p¿ƒ¹‚<úÌSÉßï)³©#iÓùõ²1eHr‹,̬¨Ü%ý0a]¾ ë„!؉Œ¢éÚ2$\Û¤j£¹E½¾'\õïE?%Œ+æw&?óò‰D•<ÍÊ3'®gòÉëR7RõÌ÷'ÀÉ*+KЧäŠð5çÍZZzVHOª¬Š‰Pͺ¾V*5®èëèP‡Šµ }RÉv&XÌ©†÷Š…ÖýAÅnZùO ,"È ?†Õ†‡Š3%rtËåI£©fÍ@) ‡+ê·ì¬Ò´|‰TO‚®â~„=œ#c™¯èÙT³:6&v¼‘š ;w€$W,°ùˆ­O€-VøV”b_Gã•ánjAf³Ÿâçמ¦(Žê¾ ò˜æf}gñ^ðDo(„®*¥pÎW³°=e`Eb5ï0Oj9rƒv¥‚;˜ˆ¢ùеtm‚ì[Ojd]\mxJêl\1NÂÍÄŒÂ~ø¤éh\å¡lrÇ䊌ÆUî©o´O4^=ˆ6by™;1è]¸ÙU Æûyóhäx¸ˆÔà¹bæhäw™Î žD‘«Ä)1˜W,Œ[ÛÏ*¦Ô»~Ñ›TJrh Jâ+„´Äc æ ªŒÉ<Ál$ÇìËò&Q[P‘oôÒ@®ºó£'¦ŠTU©Á öŠAŽ¥~NÌä]O"bƒý¡ âœ-33[¡C\7{"µÒL- ^GneÆÇW*Í=–æfŽ—•¦Ž+™Húÿ†^ã^iy‚¨ìhy$Ї ¢~ìw qɼöMÞ?ߟ0[ï>ÎùÖàókø½ú+w¶Ë”<,YÏÊ‹ß龨º½Œ:Ò0W§ÔYÿ­úòÆfºcxÿô±XR/ßÔ¡z2cR`BGs9¯Ÿ®5Þ w}›lo?wþ°‚ãüaãMcÙÙ¦GÕ•yS4ÑÛµ~èíøå·eü_üödÝtöd'Étv§Þ¦³ÅYékqRä‘õL¿v¶’ÍX;;Ç>á"…}1Ö ¸}AUÅ› ¶dF‚z‚=‰ž$F‰ÿ#­N±}V'Õ1–¬ {Á>¥׬ùñ…4ÿ šr—ÝÎeÌÒäœC‹áÅ,pÌʉ¤*r*6À‘,öbÁÍùNéÁrþá‡5î ‰ö°^ò#uÐÄJº¹ia­¯ +)A6/””3ZHxYQí:môò"ò±¶çœO¹ÞGJÄùªuï xÖŸ7„¬Å=&Š7­9¥#º8þ”4žlJ§-ïI®p¬Ø8ÖÒ‘(Ü4¢”ÍZ­ÃáäãÿP6Š`häDUIÛ¸Gr4Ñ")™)28_½N¶ì©ƒ‚­úù†2þ¥` ÿ$ÊÕØâ¼WŒœWA¹xûÁ°xûl9`:‰©Ãƒ±I¨L‘[—è#Æ TÝî1øS·À–HªÓ§ÆItÓ¥ÆÁÓé$ο蒟l³£à9Úá~i ØtVJ†¨Èm½°Ã¾Èla\ˆÜr& ·ôK}ôyŸ :as IˆReȸ™ ©•+Ñ£Al¤¹·ž‰ °½æØûæR¼ÁµñºH©²â=P$ñ|Õ*Ùº(³%jd‰YÆÚßÉZqØžr˜Rsí(]©ëu8j%⎠¸š+ÏŠqá(P¶óEl$gú 0‘°3î02‚áSÏm@Nx•ÜNr¬V¼- âÕ+w7Áò«æ‰¶\r§ºtöWÇž$“Þù¥Ï®+3ÁÉ2_D¤nb3œØ¼ —5N/è÷+4q`yºIz bÁÝÞ¶œŠSùš[_ZúA°DÒåàE=þkR…õÝ;¾»[3¶ûm§Äõ2 ù–ä×ܰ¸E쥇¨ww=+Æ«x›[ƒA•{7I— IÕ°ø_³%¬MLÞ©ðÝií±åØïütúè.i¤ÐçKïM5à4z-‚CpìbE¸?Rq °m]´%¹w•nÛn=þ·ÙÖ[tïTøînÍØrì7SÚ8^Yva±sk@™ÐAïp;.N<ùm¬VÃέE&sº¶t+>ô–ù_0+Y»¸½»‚»»5c{ÅžÏ WÁ“$z…¢ÎE€»¢ÖùmËU!PªáÖÕØ½«¤Ë/‡ÿ§Ýœ[S ïTøînÍØrìïÄs€ÒKQ”{2vâ·ä¢ÞÒmSþFnÍ'¤w“nÛn•xsá>=[›˜¼Sá»ÓÚc˱߄_#À~L>Õ5 }Õø°¹£XövT\ Ç­O –ÞÏ}á7£ïÿ“<šÖ.nï®ðÝÝš±åØo6½ðµ|&굺¦ÌbÍ ÝCÔ±BÍMѪ4ZK¢`°0ç&Ü6I6Åïñ,ŒMLΩðÍií¡¥ÈoöMPõ=íŠâD–˜F)ªqû¥Ìo‹çeöTHƒÆslëb-ÞUÒåÒ;JÿE3t[S ï[a»»5c˱ßd6 ØHŽQzu ¦ÉÎáWB$ÂÌ)ƒÖÏ6ÊÕܹI· «Çèñ}G¶¦¸›¼÷¦±G–#¿Éx¦¦gˆœ¬º.8ýÿî!*AR{*ί¸ú„õ˜ÙÞ‚èÚYzü×É-¬)†w*|w·fl9ö„Óàumê»Ã%—rU×@<Š&™<ê':ºmK…T¯Ÿm-AÞMÂr;ª†Åÿ6»ºµ‹Û;¾»[3¶Wì/œVþ:u,;mJ<¥ˆz,ðÞâZ~ÛrUVÎú k ëg{WI— ÿ§…íÖÃ;¾»[3¶ûMv>5?/ 1@]Õ5õ2¾„iDÜlQɼÚS!mM¬ÂºëœàÞUÒåB5Kÿ¼ ·¦Þ©ðÝÝš±åØoÎU«£7;ÌÔ@ &é˜MVëY´ªp=ј1üc®2ç”°\¦>íü/âHÖ.ºóuoïÈräwš«„HYíˆâÔâT ¦¼ŠÚÃ,ÕCD.™=B‘ýÛZ~|¶w•tyWî¿ÿpذ¦Þ©ðÝÝš±åØ3 `zDÎv;Ÿ[S…½·¥@s‹—aê–O…Ü0>ô ÝùáW(Š¥%ÀÿE_w[SÜÎ)ûÞnÌÈräo8:۴Κ¡°]´wjªpñÕ´ð&ü…xX½ºB‡ÙÖã‹;WAW/mïþ»¡½„±‹Ûy(ls·fh)òüL ýYkÝHB•FƒiªL8èסm:‰ÖÆ]q.móÛZ©ÁÚ¼›¤Ë›µÿß´«lk“÷­°Ýií±åØß%\Vø@dÙ±ú¡ì…š*Ô·•.uL­Ô6¦Bæ;¶­O!Ÿí]%,*: ™úÿ· kŠÛ»+¸û¶¶Ø^±ëònŠIN ……£š*C;•nq6nEWZÖ ¤s{o$¥U¦ ‡ÿ§ëÖÃ;¾»[3¶{N<¡‰B‹j)ÞYe†) @ª©ÃÞ¸\ÖÛ\<½÷¹â°Þ¸­íœîýðÎ*<ô=þWP¶°¦Þ©ðÝÝš±åØß‰7ÀÝ%ýXS[>5¥Ë6Í1ź7¦¢00·.6½?Ö<´åûaug²vq{wwßÖŒ-ÇžßJ£+Œ2eÔ¸Žêš*SZ„Hšÿ£¼jzäÈmèÝ€ÿC½IÜ.I%•êKì{oÙ=8g '6vmù÷á·žªjŒsè!‹¤HŠ"Ì^"ÎHZB¡½H¹õÅ {jã;µIÔ6rX7†ŸîÚæúÎ⌦¤7wí  » á0™ªP‰oÜÉz/úvAM™ë-´‹ŒB¤é¥×6>“¨lä0n ?ܵÍ5ð\¤éåiÏíŠçL‡®X2@Cø 3CxÑ7F’bh/¼_„u¡Dœ& 'X9uÑ—dÚAºõ`øé®m¾¡ïÓª„D¨Or³ß%¬JNµñ°óÁY²AT&ÃA^´ƒAZâ˜kó†y ëÙ®„t²6ù^u@¸¶‘ú1üt×6ßÐwxB•€‰|pì]iÃQ·„S—®N+ú ÒÑq0=mÁÖaݱw%ä£nÙ÷¤N›¶“aÝvzh«o“ï"NŠYÍ.Ø®èØdÒ ‡IÞ«ˆÊµ ’r£êFSŠ×õ6”éXÙŒ %âý¾Ê˜´ï»ÀÖÐ62Œíg»²y†žÏeGãyÏ2¿irÌXÄ/áTšæe“Æudå:惱Ê+ÚÒxº·!*.êm|§–ŒÚFëÆðÓ]Û|CßYœ[ѹ–’C€¦§Ò<Þt`$=ÎÔqvǵi/%m·.”GsŦ´|OÒ¸]Ûɰî ;=´Õ·Éw¿£Êw”n?Þ^¿z÷Ã_óí—ß,J9†ÆÖ3\aåƒV¨U*‡&ÚÞ¡Vþ¢ü`‹¼ïÞ™!Iz]æÃÆE‘”á%ñYùUײ¦ êâ&Jš¯…Q¬ê3¼«‚iÐ? ‚Á´v<Æ–Q"?l•*²Í45èK—T3x¥¬²î*ÁXs§uuclÆŒÄë1²ÛàM)ÝJï<š™A™È¤Ò³E^(d´tI™0xŸ½ºIf¡9^3Ñ4%w…2A÷]¨¤{SÙ‰±ð3¨~©Ž õǪ6è²v ¶P6¨†Kä»Ð–D‰.eçšFrº„â=–ê"I]„÷. ZïUB‚@„1É ±~ð&º«ÑÇ6:fžvν0š$è$¡60Z¹6J™Y;†ï „‘yy½hžBÏénËOiR9d—ØÂFç¸/$ÄÞ\3 X÷çù*MbµÇVx︔X‰ƒf£T[ëT0&Aþ5c$þï,¡6° +o‡l”+x.K“Èöb˶1B¾XëÞk ÃÌ©øM¢IçÆÊkìYBmàƒ¢¦.6 þ|`þFÒ wšówÖÇ› Û]m~Â*Áá£ÉÅŸ$Ôö)@F™Ôv6ŠX¬VVF`—^ÄÑ}¨¼d>­)s„väO±A=îRBl`£÷PÕèÆaÓ3‰d×ÊM6]Jˆ ì¤ÝÐ(] n´V—¨%Œ,ò,!6°_g¾o6J“¬Í \$ø«16¾¨“„ÙÀ©@…¹ªÑí^¦1á«OšYõRBlàì)žSjDuF.±X£äµm»”(žSpÍúÓÊ]fxú}õ‘»JœšœÐ¼pl‘zN›fªÙí¾CàþYBmà „ÀF©_00“Û%&6 œ%Äbšj“þl`_É{¥«¬¶‡¯¬ 8#ß7½ê q‡~§:±Š¡ÆÓÏßULå.È»zÈÆ$ŠÁ§uë÷t)Q­“\¢Ö¦Y¤½NøÉ%.Ô8Û¥„Ø@PÖ¸|Ù(uNºF@i.gWTy–hù%ïs»T% AùN¥·R¤ª<W}…%.Û=+B¥—Ön†D±éÅëê²^Iˆ`ØÎy|Öí­O Ö%*çñ¡Ëær)ÑíÞ)žÕϺG-3t6‰ÅæïmùR¢Ø¼<Þ¸©<ëC7Ý%è©#K{9KˆÇý™¯áY—‰Í—øîS—W™ýâ{¶kÛÅi¯ …{E±þí‘ÒsßÊ!ãŠ'ãÖ’?®¨ž|,Põ žA^-á{§÷Üö©ljt›Ìrîz63£³–Cï¶ígLˆ’s¨§Ã´+ûa¦ör˜Ü}Þ,’õÞ@!ÝÑ÷Ø,ÔñStlžY±yÀp·BÕ¢# ¬}åÕÚ5®*á õÊxi¯X§µ"ßMÝ–ˆ AÎ[“#S_!üÆÆ0¨‚A41¸1¾ê,ƒ*j]ÚAbAجaÄ>ŒIÓÝî[›öb5àÿ`  Ü!—mCøO§'“P´ TÁÂX¹‚íû#p?¨ÕF‡åŒàž‡³–Ÿay`  –påE0ïã5Ðû`  ¾‹“ø"XLBá:0P›t „ë2ÿ€ÎUð âFtÞ¬<ŒcRÁ¶ Uˆ`|q(äØ{0P{ÍÆ#±÷j d@íÁ@•ÑÀÖ%y, µ9qæº"kd  vÅzßò„¬³#Òƒ*Øj3Ò”IüŽ›*нÉ&Âæ¬Û` ä ‡4΂î+—cäæ–CâÁ@0ÅŠ< ñâcÎp0&œZB`rÔÆ˜¡ÞÁ@•1 iò/; _úž5tEº@ƒ<ŽÖÕ÷Gº>¢Øªà¼NÜ]ØRïî&¡8¨‚ `5086ûHwØ:¨À‚:ÂVjöa*Ð ?#ÐT'ŒºØžt0PáO³Ætµ>4“ ¢ÇÄE”z)8*«-8³í/U®V[_æ}Æ“F£¼ŒNY1€ê0Yµ¾•á¸âÇ›}ÉŽMŸuÚ·¥8‡'ö®¦vL·' LHÊta¡Lï©1dPãJ=)ØÈ´ªÄw©“Pj˜vR6Mw Ý~²§8èVwˆƒwˆcz˜ÑîŠ+›Ÿf|ÄA÷_!ê8”…¦ÔƒMÓÝB·Ÿäbþôþõ«í¶Ðß& ƒkV»Ï{ŠëÍãÓÓ‡Çí—_¾{ÿÏׯþBÂIoÒ.Êj[ñÒ•ñÆjs™“Ú/%’Íe¾Ðåep¸‹Ã=¦ )1àðç¯Ãáp5!ñ´â1.ŽÐ”K wÄ ¾+Ư ðÁ_ÅøV ¨òxñ~hlQù@¸žž¾>}|øòñõ8Þ¢§YÀ@•Çå¦è2ŒÒÆ­ U‚ý?¢4¯ç õàHJvÙã'EÿÉR¦Œ\Òmh7A¼n]({Õ©ìãû®£ÄµƒtëÆðÓ]Û|CßIüÚÖ¤Ëýû†I²~¡’Þæö)Ê4™1¦9Ä}1C\ç×9Äõâ:‡¸Î!fqºÌÓÕ;#.³rA0©Äšådø2âkhk$n="­¼#ŽHë®ØÂµƒtëÆðÓ]Û|Cßõ2ç"ïxìZks`íX›«s`ýX]¦ÀÒW:„•¦¨TuDÕçû»j „¥ƒp‰Û}óëÇçO_>žZ=í‡âöÌ-1Ãí],V¥m$nÇçá]ÁžÙÑB!·[tÆLÆI¡þßf̳aÆ ,̸ï`æ*?E€­4XK ë‰ßÌN!¤¸O± ãÿÈN@ù<’¡ H#ótÌÆP³à‡š»†j—ÑÓOŸ¢¿˜/ÿK‹N÷+´ãÁÎÓ‚<¦õ´$¼¶Õ9 ÿcgz&© ’¶¦æðP´),ëmh¯¼Ö†u¡nm|'ˆÚFëÆðÓ]Û|CߟFÁ0ÊË#Æ€e˜s2Žs†¹Úê¬[`¨´2‚¡j(¨mä°n ?ݵÍ7ôýå6B«uT,ê·ŸÞ䟾»ª“‹ I’5(e@œ°-I=#ž • ÷’`%ÄÆË+ˆ;>ÿC\A¸cøjæáŽ®³¸óà¢ýr)S¦„ñÍLe^Z!S´’¶‚‰ª¼œ˜eãÝñº5¼” ðÂ4µ|U†ï¾o7ÚGhD§E¢ŸBmŒ`*™KZ<Ò½‹ÆÒ¹!&Ú^uÔQ4ÞS9¿ñ!æZ½×ÉÜ¢¢£´‚6þ¦Ñwßg÷ö^öÆ)|ÿç°÷Öd<"ºï%g—ak« þöåï_¾~>ø)/º_øù¯ÓÙ©®föM>Zwÿø~Š‘ÅrŒpÛõ§OŸÿsth“étvè`¶.×C⊛}~6÷ÓEó¢K¨ð|ÀïEåm! Ô-vÅ\ýÛχ„PåîóUüápæÛcäÌÌ·{ꑪ?FuÿwÒEh endstream endobj 28 0 obj << /ProcSet [/PDF /Text ] /Font << /F2 4 0 R /F4 5 0 R /F6 6 0 R /F8 7 0 R /F9 13 0 R >> /ExtGState << /GS1 8 0 R /GS2 25 0 R >> >> endobj 30 0 obj << /Length 15111 /Filter /FlateDecode >> stream H‰|WK“ã¶¾OÕü%×Kð’>n9vâd+NV©f÷À¡8Ú’(“ÔNæß§h”v×[åQwÝ_7šFíïïÞýòѨýtgT¯îïªRW¹*«xžXüÌø+ÕO .Bmà&yVBôu*®<ÃÓ龉¿/úp9õm3÷ÃIÍcsšž»‘Â Õ?š¹;µ}7©á™Î [o xÃÕñ}Η㢚¶½/X¨8жT3vê©™à˜ÃáMõ»î4÷ò˜6ã0Ú󠺦}Qœ6þHˆ4ÕÖ•RmŠš.µú´J?­Ý…½5MÙ ®Aœù²OàÕ®^CzêÞëyŽp“fÆ9×÷ݬ¦þԂǧn™oàôêoDAÕÆ°ËH ùu€EÓÔì;رíúó­µú'Gò§î07ôó .ûÚïæHr x8šÃf‘,°‘·úêÃûwÕ¾*t†‹­NYKî2Ź(*ÇD\Rö¿§s×Îý—îð¶¶µÎV”ê ³5]3×L ÅÓOP„ó¢ô*Ë>¾öp!×ÄÀ‡cC$æô8JnÎÍ4!c|¡/n›Ò )³7¸ïJ°?uÆ~öÁüÛûêãosÏ…_¹kI_‰qÎ*fÉÁ,uIÊ«¦ð¸ZÆV}7Ž%D‘‚v`+¬Ø:÷5[»Ìþçtèÿè¸ ¸c]¢Ûm\á÷Èsd ·®Šë4²eš¾ T¡üýã¿7)ùGÍKM/A¿í*Sb)]æfYÂcî—=ÇüS–æ¡¡‚TDU‡üpyïvêéÍõ›Ez+i„T@HßJÖE¥-î¯Ãk÷Þ„Ü`¯ãü‡C\O¢»Å©NשæTç Úà>uÿ;wÀÚ/ᆗKâð‘ N»(‘L*¸{îf$Þ9<]žÎÀ_Üþ0¼‚Oàä¡9Ÿ—1‚«eãÂXÔ¾%LÃel»w;Ê{7n,ÃeKv=ŽÖú–6/žžšB÷ `Yèþ‹–"“3m- }L ˜«]Ì\‘€ƒÍ‰ªgÙVÒE‡¥Ž–SÃÚˆ …b2פ%¨mÝô_¦ÅmÓSjHøªÀò§ÎO?#Ö“K/y&¼ôàÃíàBNñ;zí™öóëŸ_›^S(iœ^ÿ«N2áVˆ)rè‹%Àrm`ºÉ­†!GÆÜÉ<]]ÍÓ²6Õ8ââˆùs¿¿@MÚÝœ™»¹Îëªró*qò@³åà†Ò@Ïn·aÄã(lÜFÃ;]~˜çC3î¡6ãØ¼áP4 g%Ä)Q¿`(~ç~W‰¶êUÁÈlÕõø9Q;¸mJ_%])¸d c9°Ò)^Q³mSTðŠk9,¹ÐEŠ;^Û ~§Øà#u¼…WxD3h#Dá|D˜! (+èé1BQ—8œFQøà«M¼‡WxDVè4‹¢ð rºØCW)ˆ×I’œT¦úãmŽ\ˆÀ+<‚C!Dáâ! àGQx‡8BˆÂ#8ÄB‚p!¯ðq„…Gpˆ#„(eo/+£:vrd‡f—Åv’ƒ³}U<\}©ÎÔwßWøä Àgß|öàÓÏ€~ùwŸx8€'€<À3@žà) Ïð€'xÀ³À< àiàB± œÙó@ìž D:öL€0Á„  Tˆ1%×µù^ ûwêíß®²›‰ïñ]*â»<`Ä·“pÕ£m†¡)ÊÍ4Dã‡%õÎDY˜¬¡CROu©«JÔ&F^+`˜–ñ, w@ÏáÕ)øÁ§ÏQ«&÷¿™ ´Ûj¯À?€Ç%-Iá(Ö¦Ë7²7%…eŽå….+R0F¤ª 9ábÎ2š1¡ió´Ô¹!…áBÁ·d9oar|WP‘3 áP$\ÿ -ᇶÀ­‹Ê¸Lå|0ø!C j Fex*ƒï#CŠŒ/ºh‚΢âKT­uɈ¢ÔuJ îRBQ#»*]’ßBa3Rä…Ì€…n†Ï¨3–®ü ¼ÌôL–®+ᔑÕ4¸”]xžuΓŒ|JTÛ°ÒI²«݉²‰]mùŽn"낦*oZf¥ \'¢Ià|YTenK -3qžƒ¤Òú•AâmEæ3ýJò&ò´ådq¶Ñï®^eN†·ŽØy΂Yå#œ ì@da†\u›Òo„ZxJo´8:ú…NòÛ:Ù*+ÉŸàjˬÒCæº/Z¼t&yˆÏê"AJs^Í2TôȰÂi•ßš$BÃS—Áž!õÃj'†ÍBΖÕαØñ6*¨Ò½Â&)…Ic«9UYFRâ|³®.Ò}‘¥˜å·&ÉU»›½Žój'†Í½‚Ï–ÕαØñÖõŠ”Û 9Á´#‘hŒIùc¬„ÞY‘Z ­w ¬I³d£»ô¬í¼£¥úH'þµ&+yDn‰Ž)sÝdT1êy RëÞ†hTL .YOÅŹñÕ·ÑõL7ºk8Û{ßÁõ½û>;Àƒ‘»oȾtß;±ÀbàR(ð2ÌiªÐ¢&5aefߤ¹4Ùƒ(J—æ·‘Zlò{†dÖÙw/ñASܾ)ûÊ{X1ìÏᆢèH,Q(5 &µ®´Á6®|DÇS1­l´D7~6ñÛ4bá_×oDB:Ž]ƺDzT1ê9o6ÞŒª%òc»P…œ®’e5oâÄE%È8aWH›ÐkU¬?î’™+[ç{ÿ- éø6y/M(ã QHå9à2úÁ€FEPÿ©g²%–ÈšÀzžÕjøãžM°•¹¾fB)ÏGa ;˜Q]Q8¢˜Œ—aMÕI*=÷¬o‹<C¡ù«Çé` TJ1]C0k©kiœÏÓFº †œSá‹ÍÈNØŸ0ye%¾ºù×í2L“¸‹®¯Œ[ …B_§cD–Ü'$³­F0ö÷ŠÁÌÑgʾ*ÁP øÃa²š¢áUˆÂº14ir8•k7="›Ž+©; ÑzºýǽCRs™Ðñ}%ëaŽvq{? [ÝÑ[ŒÝÌ›N‹Ó(²îEF‡aYeS6{MÍ-&'°®c?håóî[ÿ›­ä\ûãÐgpg*|e3®¶Xg%õÙÕ²*+­Šk²ŒÜ÷/G˜’¤¯ ÒvýðȲ̉EÓ¾—ßÀ¶å ‹:Ž!ÅÍz~ïaÜ{è÷úµ‡~ïaÜ{˜ß{X÷Öµ‡÷°®=¬{ókBá.fÙR1š…Jv§Åhÿ³Òþ¬™:hð£ô`û~H²ŒÊòö÷®oà )ïTøêŽFh!p5o­°ê_(ÐÀM“e–¨p¦Ïk‹™4(ôqtÒ·¿½›dË%6b~Ï¿ì}SæÒŠÀ®ÀÍz˜uå䑅ηY\“e²X¸ÂeÞ¶˜¸Ùf‡ƒNÊÜy"aÈ2Id–~ûŒlƒ)ç[ÅÍÐBäj-ôÝ.°qîÈ29d+¼¦É%á«]ì +éVdK«ƒÎšuÛ{f{VÅdé´ïUŸÊAS<Þ©ðÕÍØbìf¾°õÆ.¢Š§×d™%°yõr$GO¦€îà@³³]gMæ‡ÎަßkB"ÚÅíü(líf`1p1ÿ[Mª¸é&6##ĪÅ5*f`’y¤X•~`ž‘Í"r´¤¼ÆKï&™¹$áçûPÂxÐw*|uG3¶»š ™7âÑ9ƒ¨¬×4YÆŒw: l±‚o¹Ø qÀzÚ?Ûyá’e’謨ö½ü–†´]Cô…ɰbØf=AkºÀŠT‹k²°ýŠKz,.V/¯G‘ôX7Ú&¡í½pÐË2Y ÖSýÞ@¢Hï®ðÕÍØbìfÞ„D›b`3Bò+7#!Ø  RãÕ‹ÃÅVÊÂqtÑËßÞM2sñÃÍØwY¤4Åã _ÝÑŒ-Æ®æÂô»3´êf„ëO{À¦Q7Yµ’Pj^c)ËëÔ:´¡Õ²À]WÏ2êç»ÔÞÐs*|mG3°8Þ~úùï·ÔðÆîG6G&7øÇò™< 1[¥¸/Sè3DàR þ°@6ôX”ÍZ3…ö‡„v í}¨ p¡M½™Bìþb¡’¤…ô€eíªD‰%§®dcn¦x¨à< ÄÕ:à`b, !UˇUUè@JN_M!Okaœy¨H˜P¦;­:Ñš¢`Y9<²ÕY»á»4æ9L5å,ŠÑõœU!‡¡¯“<Èa¤iŠk ñÕ%šö[‘å( `Ù+ò\–ÆCž°‰p9cÛ¶ä‚ûÿȤģsÁÿ%3&½˜Ù{`Žf­Ç׺ òÿþ’Hýˆ•߈ ƒÈ¡ä?wMR÷Ð-q±—÷ì­*R÷Z5‡Î^ý»´ w‘$W4p>ï9/é3È™#J<@7°›Ù^–{æÄå×Pý¢„ÊÛ‰œ{¡Å`‚™¡¯}øu¾á~›Û…ìi_¸[ Eº¡½zž¼!qšg–PÙÔb&¹Åd€ð3m´ü{C>6MlC1£w‚Ò@˜Ì9c%QÕÓú=iÞ•ÍZÚ k1ïi DòœMÅ@‡ÇòžÇ#x$Ϫ]¯ ‹Å^º<Á7¼ÉÁê‘„§ð øHÝB¼h¤kgÚoxëC ”=~)%)>~ZÌçøXV–Q0ÞPAÆ/.»1ÁOET¥¼¶mLË Ñ*Ó¤U’>Ùc¥¢V»s2RìYÏÐŒwÅ“»ÄAô…Þ%:ªœ{«ÏV9ßSJuðƒGk+‹­Qy÷£m%{I~OÖm&U&—X´½ŠçídIÉ÷:ÿžÂ/>˜öRfzº:[CŽïru46k ïé(âgÁÔšRb‹ñžãmJýä§ØK@gzC«z”tXïJövïÚ™‡?–Ùèxoh7ÉghËŽ=qúxPv™ÈRnÆòßÐ[}YÊMI±ÙºEQná~dvMÕ{ô{z¶U!C-5MœÊ#— ¶aî T@¹A9Ü€•ÕÐü^…QŠ7p ¿Rg¤Ã-êï~:0é³UywÉlb:¯õ‹ËРñ2áfXz'*|Ô:µI’[4Öó#çέÞ@¶ K­Î(mFöåM[ûéRcÀ6Ù'RaF¿8Ý>‘H½iÙI`d…•MJ‰øS"Mt‹³Fhè\‘]:Ý,:þ)®nð't­Ctó(9IFNºq“mµ~ïð†ÞqØÒÊß§´¾Ž¿_kúΖö•†x£žÞE7x¿&ÜyŽÏºž¹(ÒwýX_œuÞœUJÅW}.Ê:l$”ýÔ¿k¿«]ûb¬³ÞŒIwëš_Œµ­‹WHAúb¬£~1VöÛÍXÑœc-ߌµ}QÖrsÍn .÷V²Ë&i/ÈÑþbš=´eI(æà"“”hËÑü‹d¾ÆQÛ°è²sR½ç#ßœô°ËE:šm{^‘ðÑÚ/E@ÜIJÑÅÂ4í–f1pG!7©DAWbÚ@üºÓм¢7Ì %BI[‰Ô1Y’E.!7™`|Ò!žòÛœ~Žr)"$äçÒK{ÁE+™quî™ú¥ˆ‹E¶ÂøàFœ¸+ï¬ýRÄM NSHèÀy§œuE9.î¨'ô‚~‚\¢2*ÛœùRDÄM‚öùà)7'›cF9bùH¿¸ ©«‹ì4;Í—±%=À”s`Ÿµ;ì-*.H,rYÇ—t“¹Ùe—"B.šè„Y[&iuwfYꥈX­^#|´Øqg–:E„Ä_ô¶_ÐÌš‰þ¦¬²ÖK!±oTž‡RÌZB›PF™Ú¥ˆØŒªÖƒô²EÞ%#çK±Ámv¢“즖d’õ_™ä¦+lšó²Ê§F¾%cô¨ˆˆØ‡›ˆ”Òûîti\à("$6÷¶i_Åæ‹^'Ù㪗"BDñw FUŒö1?* e#]ЉD¤ó½äÚñ$+Ÿ‡RÇ^/E„Dv3|Th…a“)ýb3dó¥©ÕâL`ec:eœýRlûÈý¦ãûÛUŒ,»sWùoð,R"EžàW9õ V“§ÈÁ^»ÈWåh¦A„@h8TÈ–’.–ó‡`WÙˆÞØé‘w¼œÂBQ~v¿l!Yüã×_õó÷?¹ç>šŒ¾ñBzOéóóPdú¬ªì wÇ©³lwØÉ¯-7:«Ñµ%\'ÙF¡WÎý]tDilo­÷ÝßogØÏ­(ô–…Å+M¿frG+_ý &­šØ1ãx„Uhû‰;8·âÝÙÇÖ:üW-òÓë;¼mõ2‚X#ìø ³®ÖÚË¡ÙVݺ#þ½l ?¨"âüà)ˆߨÕ£/.Åâ8vj6åñbz펔Àà°Œ¼F¯VBÀ©ÕBE8Ùpö`¯0n^¤&ŠÆg1Sß ÆzÌXk,±¡å M:Ôy¡å¾ìž>þ‰Þ¹ºuudâ5Kû`qÀ`tÓtˆ‰j€(¹2êÀ™× ^ÇUi ³Âq¦4åÎîì£øûƒ,ö yVIŒ9#6Ö·Û‹ÊBè”™À(l}ªUzk¸r0f®:å M¡´+¬SC³}–›'¡µd›$èÃJDÌA¸ÃHBãÉï–ïé³”1ôOÚ6ôø8ps­W»àP¶ÇÑ¡ê~m^?ÛÓ:ƒñ£ç[&»¤ÝÒ¨Ž¹`MÇ$@…<éJh’ù´nHÇYVǺŽTv´l;Ž£CUŽíŽ?ªȲ9Xu^ŸU:“Û%„ÅŠÊ*šÈU#”ÏÒkLˆ´Û”œ‰ “;֛ͩ-xÇ^G:5TåÒîòãu‚ÎJ¯ùkš/’ ü1"BËñã›WFSªmNr™z|0s_´nT;µ”ñèli‡c?þµë²êS¡Óâë“@H8Ó±›}.ØÒ_ P  më2aüÊHÞhås-¥Xʂ˸qxh˵Íó«_lp•¯¨¾>]66Z—§ŽD…ædù<^$üM÷Bû°a4¬;zü¸9æZ¿<2¡íp³.Aœ.íðm÷ýQ÷F,Èäý[Ð×™ƒ& :íÿÁâ"téÚƒf³0¥¦qÏ4\#§.’´™ë¤Ê×Ò˜ÆC ³SÛýÚ¼~DJH[ÀZý3À™Í—ÒS”ÆDh¨½.\ÑP-7;u˜vd»»<Ñzw?C;a— Ζv8¶;þˆoáúêEúôº”4$…DÞùØàË\0ô%ÀÆ—Ú…þ¤õÞaP¸¹Ö»¥²£e;°ªrlwüq*9èÉk¡ 0Ñ]RZ'‰ :Ìœà­&ž‚ndiwò“´ÞE_JÃÈDõÉŽ±´—u âôЖo»ïˆ2îé<ãZ 8˜%.)il<ÚB“H.ÖT=,æaúˆ;Á|Ò˜"±ÞÉ|–¶à2ž?;´åØîø£~Þb2}qëÃÙNlÑw—ÀÈH*_ÆTÂöMs$ÖË×\ʉd[XG§ªùµ{ýh´ÁdSã:À•„K X6““ÅdAÅ,‡Ç4´OàdÜíF’°AÄò°7›Ê‚Ëx üpi‡k›ç¦6îî,àüUc.!,6Ô ÜêBjÂ(`PYªx¢Ÿ´L`{/ïâZµéu© .Ë)ðƒCY^í^?>r\=¢'[ÇvIç×û’ gW©K0¼«…60{žwÀݘ?¬ckyÚ€›ÊÓ¸qxh‡k›ç¶»YÝiK?ÃÆ(%$„fí„ÂÜ¡ QÀÄÍ›ÐFË¥¯²nȶƒJö¹Öb/mÁe=~zhË·Ýwl¯ähÜ>Õ+G–Q—Ôû,ËÌ$¼•ú›€Ýmiwö¾´ÞÕ)0¶ë§ßzh' ëÄé¡-ßvßm;ø¶£šŽË?ÌåÇ%„G7T¯¹`Mï$@¤gù,m¤Ç'—/mƱ¬?ZÝÚ4-ÃŽãÜДW»×Ü ŽƒnÐG\ (ÐÍdw !;S¿=({ª§À‚šÚÍB.ã-\EªLöXnÖR9a— m¹¶yÎÝ -LÆ»:y$¶m‚JÊs ƒKXÄðRpLeôððíÈ+Þ¢c½ZR[0­‡@§‡¶»¶;n›1Éwq• ÞpÛ—˜„=ˆl!a‹g‚ËØÆÒ¾ølÓú¥G\+ÇÙ¹ÖQ¸Æ¦-¸¬K§‡¶|Û}çö“n…µ‘ßfpÞ)ÁÀgÖ‡« xÏ/i'> ”ÊèbhX²í@g &H,ÛŒ»”j·ãÒT¶Ò™Æ³öf]9×;{öÒLãÂqv(˳ÝsnG½W¾Ù™ý.PÏÇ,!!<º?5¡/Qøò‡šª_qX¾ôÆQëÏkæjõ„ªà²,AÊòj÷Ûÿúý7æ÷/ð³ÏŸŸßûã×_õó÷?ú¾†X¾ýªϧòù¶4á›ô¿%žsó5¼A«Ô¨J.`miVO[sx¡w=PÝo5²býlöÛ[²S¤Ð·ÈG3îrÝ& =éÎÁN€v‘„5’¥obçîÍ%c¦¨çé›.9ñÜÓr|¦ì—  ìCé°O±¹)_ÑÌÍ‚w…œèõâ lg«öcŽ~šŠNA8°_Tݬcý°ºFŒ6<k|Ðð/­s~^Ò°øè^'Má*{+¸6·‰<;9íL줭iŸŽP ¡ÚŸÇ$è³wÛ~ËŒÂÌ8[Ûÿδ€Gõ®ä·†7÷öѱÎ& +@Fê]‘Ë$ḶPƆ±} x³ÅÃãÿn‚™Í•n ÈvAÚPA¹Ò xë5ã^ßí¢ÑÄýâOû¤uñ±¡ó †Æ«E¾¼[Ý|˜¦6-ö+£bòº,;7#§D|·ÌD„]«ñ]왪 §^ƒ™9m¬S‚¿+áÉ ýTvëíh3 Œ€®ô|5ïzE§ï)§‚ýUùzÓ=º‘Î&è/ñÝžæÐ%Ò–{«Úq ”™iV–ôÄßõæU•‰fåkÕ€( …å9Í ·G”Žw«%g„ó(ùþV\´ƒí)í°YEIz·5t—e”y-íg>–;¬ù=jÝ»?¤¨' FÆxUÃØqéŹÁU}·ªÚ££ ð¾·2«¸‡•}¨œ³¼:¿[¹>â¶ð²Ù5VýŽu¿-¼AR/ûïÖ¬©›Ú ½1hG¼y·Óé®úÉ»Ì59¢Zǹøoë8Ñ‚Š>”v*9F‹FõnË[!ùʹõ±\î¬ idÚHâíïÝúá òvµÕ µczI23x½D[}·>[¢wã‘Ó­Æ;ί¶¾©z~{¿~Wÿ6±iM˸ÕеáR p+ƒ‡ˆ¼-è^þ8 ’pn4AÆVþ8®•ìâÝèjœS¤4ðâ±<Í\ž7ÎZÞÆ FäÛg5ÍœÁk|šT_î ­ÆtèÝøQe|û°t×0ÅŽ‹év0Œ<ë݈W4UN+ˆËÆÄbÇ`U ;`íG7öö‘ë*Kœ浘ݴ9ëØtí‰8 ÜiaW!!+¯e㉱ÝneÂ|—]:Ýìè>rüþ?ßUÓ7rDïüú`ähd6¿ Ž /b`ƒÉÁÞ5¢f¸’Z’#YùõyUÕÝ,ޤ@€¤nvU×ç«×‘ÐñÈ©î$Å]iÌ>¾È`‘beiaá|œÃãÊV:R¿ht P`ù‹¹ 5“JÍ(+ôbá°åŠÃ¢Ëü¬#‹3 +V) [V+`bˆÕˆ—¸.×PšœQXÉqÿÈ5°*I~6­$~ ÚÂAÐ2|£lMaÓxÅR2fáyyv¾[žQØ"]SXéÑ…Âæš•§œÂ …¥òX8«; d3cÚ‡äIá–\’j¯¸§LFT l$¦*c6b~¨µ>¯Lg\꘷Yˆ¨²¥¨Â*WK}zE8Mˆ¸ð2¸P垢¹TJBÇdÉ©¨Ø}8âØÈù¯6´ˆNXA}Õ Y%/n žéµÐ5`©ð:á©eÁ,4“ò¶¥¨ +]X™'˜4M‰‚¦ž“²È²¡EVìR4`\ò½rÅ,X 6Uü©½ü™[ëÀЏ(ò¡7”„î'ö“U ³,³ULj0l¾ÚÐ"ºIc2¶VŠNaš{JØ 6´ˆî|žP¬´Bhé hºÞÐMb7ù-ˆ˜ÉfTxöI(§6´ˆ†¨ÌMH‹AÅ _bžÑec%¢pC;v:d8ƒd:–d(“Õ†Ñ` ê%:ЃUÆ|ÒáÕ…Zéóž+ÿðšÑ¼¨<Ó$n¼¬µ€Fü˜bÆ žÖ8áEÙÕZK¬ù¢¼E‰pR£>„]Ç“ÞÐ"j2¿Y¡ ؤÀA À 6¢6´ˆ&ŠÖñîØ‚%ˆGÃh~« -¢F(ZZø`L'‰Œº)m…/J@ÏäÊó[À™"ÍÍø«6´ˆžóüæê„_fð´\6!¸Õk-¡¹CLäƒ) å&Á𲩧’TjC‹hBRÒg\Èynf$«µ–Ð$Ǻ9g l8àIÆôGoh‰ÀšRPIjšÙÖóÇ,^m„ó ,‚œÞ–j'w|1!”ÖZD3AfÃLÆG™vDm±ÚÐ"Gp«ˆ˜wÓ@.„nòÚs‡_Œ|Èñ!w}€É]`¨¹ ¢½$†Ä…~EóoÇGyIЙ˜ ÈóÔ«å¥ìákE$$Hº•Wë–îN/(öhkwÎOe= õª æÓ2‰ƒ®°t7…µØ±³‘^·wÁ‚Yƒ †ÏHX¼¸–^·_»«ƒ°3LÙ½ãt|¼­2~ðÙr"éu€½†c›ã°«fßÌ׿¿}ó·ky„Lóû£Z2íÖ.ÓÀÓ/’pæQôŽÀw‚»gbþ¡TÉC4¨ñß+¢<îyB>t%Á¯…BšJM8¡õë»^x×éáÊõûÓâztþ* j­k\o®uùñݲ‡ øHÉoRBÁÅJ˜À²VÇ!Eçv– ¦ÊÎz·;u§c=7K¦¼uîf*¢)á* S^®6”„ObæÈ¾¼ÿü ¡M”˲VÏéóðH ¶Jb¹<áu F*kJ:k(Éa•®Ûi Æ 6HÌrãUòÂ5{Z…¯ŒÎAÒ­‚Ú°æ;½$[£,õ½ž/–ÓÚ&Þt®´E‘ZF¥ïõl1cfƒnï¥(+ƒTé¶\¤ý2h_6øö -¦)Ã_möœS¥TÒØtÃܬºÝ¥ßúd®‹ ð<ÛAáIô+Ý¿TCIƒã¹|áˆ|ŬaÑç¿W®¯ØòEI°Êk¡à™RNh=/µWA_Eã@P)óy µwïV‘ópñ<~¯)ðM¤Û‡$î‰GÓŠ¨Qä”ó;gYëóÇW“Ÿd‡öóÿàH¸œ‡L¥n㉖®6´ÈrÿûÏ(js}G'ØüAvâĤ j…CÞk¾ºâ•Añc‚¥ ‡hQúá™þušÞø´,êȵX«‹ää¼Rjñ«>ÓŠ×ÓJëûϱ7÷*Í@¡ëSиug¼Kp>„vgH]"§¹çÓý¹¡SPm)š"‘~zÏ ŸçæºÃ»áþéLyŒ÷lþRÖzã˜)÷yp ¯·ëœÕ³´ÅD-’nø3‹l‰<–.9UüþÛY`1Ç€‡«¸^žÝ¹V#.rƫϯâÔoýU‘“ýÛ7 Yƒeú›m6=6Ò˜R‡ñ3‡¼.¨$hâ$Ρ=¤D<)v‡ÆüHâ‰?Ù%/P+²¨û[ÞX:ˆöÍpߌõÜýdê±1·”ÍU #·è$󮚑ìêuƒ„–p=¢K(+¼ò¶kçöuCÆÞ ?¨È–’‚õl`œùø÷ÿÜZ3?Ý7*:ˆ õâ4Gf8!NM_]=íOƒd‰ý="<«Ð:?¶JVós»ÒæBX##Í¥ùÜîO¨”ÂL‡áQÙülv'ŽðÜv ‰òDÓ Ž:g*nIDàͦ˜RŒ+îþmÛbóc“ü¸ÆØÊ)ˬW0"Ž åêc=QÛ‰{¸› ‚yŸš#-©¢õüˆ¢iv1,bÔñˆË}Ü·œ¨r=©Ü>KÈÑñDUy3œfg2wõ„n¹ÈÓ«rã æýçÒƒj, ‡Åq–ù÷Ê÷Mnþuñ”mÚéTÛÿr: ®ªÍ09œ_ŽÃ 0èÃ8ÖO“×ÿ*hÇ °¢¾é¿öHó-Õ%³•?õ>&ºðQ׫VzÎ &±‰î^̬ÙLÌó°rŠ îÛNV3œÓuæÁä€,Þ4M0zhލð[ÊD;±€94Ç{©:4â~¬». pë†*­Ëes‡èòÙµTôs=ýµyhnOG2yš C÷-:Žýíë·Oj$¬¦F’ªZS󱤅¢öæDe‡úÚÞ¢zª„ƒpæÊ|‚®úÊ:uâÎä j7 ˆ{þWɃy25³ËÎYß‹¶›'j`9ç"üB{ú4\±HÈiYå~àúNçhc¸`,`rø 3k瀓£€s{F;ž¯Žrëa½6äÇ*Nc(=Éã‚"Òxº–Ìq:›ZçìH#F´=Ârmß7½¤g¼d:]ÙöÈó*¥sÛÕåã àë¼Ê¼˜\~¨,­g´½J5†uèKm$'e¶ú^€&$‰ˆâÊqt‰ÂÉ©öô+û*ç­—,ÀÆ~v(…䌀Ú'Àêï§iFç13S]JøÇ‹„0¦©BøoÓÖh¯ªÔd)ØfÁ€Y³Í²Àÿá©£G9ÿ˜ ’ bÇ„™l¹ÁñGhS'Hd»,<{ýu D&7I01–y§q^ÊΩÙÚ€P6qU·@¿k-4†2ㆱ&¥2»’(ŠÎhwœ€ØÇÙÂÍIñ8OϼÁ³'-=Ó+ E¬–R}³u ︲±7ÚÏ8æqå+a'¬ó¨Q¤áøõš¤ö4¤ ¥_{ºÁü¹Ã¸bÖ¯½ÇÝ`ùÚ`Û*3Ž ²¹åk‡ÉÍ`¾òµƒ»,‚|íÁ F¯f0‚|í@0ƒäk‚L$o÷N'Þýæ·áÛÝï„»ío„ûý+üq¸€Ûð·5à1n‹À·UàŒÛ2pÆ]ôþYÈè}èømÈø'u „›:Pq7u “ß׺ß×F¸¯%Ü×^êà›Üà~o·rà~o½£]³·IýòµÍ>ßRž_¾–…ioit¹w9w܈ôNï-g¹ ^f¸¿^ÆZ—›:cöaWéÛ3º’6©.i™K>ê%Úïoóü‚¾·¥,Šš¥k€*÷x:Æ‹{êð¯çIåtÚ…Œ†2Pg§{›bxýÝ»¨p„è·}=Bj?ŠïäçîW%Ld=Ap$àgFã@XÐ ‚#É Œ7¡cã@P ë"Rq $¼gÃ8Ã0œñHa\p$`ár$0”ÞE¢àHÀJÄq‚a¸¢;,a\p ´óÈ1Á‘€ÓåB`O©‚#aýB`£5&!8Ö‘ã:„Ù®"¶é ›Eq$tzãM'*1¬„âHÀ©p!0t²a3ŒÃTBaØc”Îÿép…á%e ¯)gXM9ÊÊ^TÆðªr†U•3¬¬”±ËJ»®œ±®Jwaà ËVY6î•e/-gXi9ÃjË^[Æðâr†—´ºlÜ«Ë^^Æðò2†×—3¬¾œaf /0cx…9Ã*ÌVbÆð3†×˜3¬ÆœaEf /2cx•ëÌ^fΰ2s†Õ™2vÙÃÍ ÍVhΰJ3†Uš¿ý´Ô|\Kí¯oÃq”ñéíÅ„Ïn!|rn á“SSŸœXL¸?/ åóゟV)>+&|¶/™p¿^[0´pÔ4œÔéQöF- wç£(]mk`nÁÜ‹™S`—”ÝÙ˜ZÍYØÿE‘з‘¢‚³ %Uû©rþøïó_ß¿ÿÃ!Éðv#ÙÃe¡Ë’’Aï×éôD“Ùâk§g媓—Žn·'i¥±i/Kn»¯ïhÜÖ }n?»~Tm„{‚Gx)tü”Ø[wzZ#ßó¢¶w·æ™¥bŽË©]²`xÂá/Û¶ É,¥q]*mmÈÎïDWC;Þ@Û{™à´¿ÌÇ@ûà:J® wh5ØÔæ­Â\5¸ÿ¦eÿ 3¶ÌŸ™ê$_©“Ã){*à äθ 0¥O6T@EûÃ%a㙠ƽ:›±Í­Î¦,*—Õ"[íbë’•d–[%b_NýüÁ’¦S$&3RtA*0͵ÇÑSÌàíТ«ÁfWoÓµëj].½g0`×ÊÅŠ·DQ‰ô ÑŽÊ(a]²?I0ve'ºyãöÎè]¦œ Žzamo…;ºlvóVmQ».l*^Ö‹ ·°\ Ø×†]O7 aIšÎth‡hð¦„ðÌkcæà¬pWƒMnÞ*-(×Õ¢–%iËò ¸ÒîО&M·ÔÜŽ¶˜¿ho(r 㺡ÏçΉZ$ΈéÔ”­=Ž£¡o…ܱÌmΪ,*×¥"›™4»4®ÉÌk2ó%™yMf\“yÍ%_sÉ×\òK.ùšK¾æRùöÚl>ƒaj³Y«Ôj-\4'â¢eqØMí6$œHÛ)c£XlFï2—…w.÷vhÑÕ`³›·H ÂuȔ٤g ôp'VËáøRÔíŒrÌG˜»6þ”ºÙHbêÚã{;÷vhÁÕ`s›· ‹Âu¥â3á¹1âÈ+g+m#6ʓŠúRÙù]ØjÈô¥Ýï)L¯¡»Ÿh–Ò{Äœ7ÔàfÐÉÝ[”mÙºJnÉUÛ™ÀRéõšJ~I%­—Tb&I$ gÏG~Ƀgy°® [—èò´{*nÈã¯`»¥¢w"gá7ÀÌ}±Ðbf(toZ.t‘²™ØB0‹€‚åÝ߅ΆVzlïó ÃXƒ3`öÒý¤ÃKv›9+ÜÁÕ`“›·J Êu±`j¢VvÒÓ’YX­øä¹6Lv8›‚ŠfÓ4›úðè§}}t®=¾ŽœíØ‚mjsUaQ¸¬Tâ¢mkèóÐ “ºŽ2àJĶPKÃ\33¢–ÅáÐ–Æ [žíÝ©!òè]&2LŠcã•:Ôí­pGWƒÍnÞª-j—Õb›¦¤—¹‰EsG ŸkC{L¸rš$#Îî Ýõ2a5yíáJÍÝvV¸ƒ«Á&7oQ¶eëJ%êŸ$8‚n@@>úÅu&³,¢oâ 0ñ1åœV8ƶ7WΈéxÕ¶µÇç±ZðV¸£»Af7oÕµëJ‘Í?ÐSΕÌÂz%à\éÇWÜø»]+5ºj›ÀrêÚ㓞±Û[áî™Û¼UX®«Uðò¿I]™¡aÇòj<Æj2K+¨hùž™v´Aö' fÀ.¡ÊpïB©Ggô.Ó͵öx9ê Þ wt5Øìæ­Ú¢v]-²É'“Å"AIÓùYâkÃj{ß Ejà WŽÅ.öýIO_{¼È)`Þ wt5Øìæ-Ò‚pY¬óh_þ)=~{œ8íÿ|üüÓß~ý–ÿùîëÆGì eG·!®Ü?1©©ÒÚË·BÒæDn|¼p}xg†@¹Ê[ …g«¾âèNÂ3£òf±‡mÉ:i%=`”Fg9 eBdÇ)hÐ]þè-‘42ÐÁ?&}ôÍ9QÎ'猓W“NœtAK–Ô¡PÒâÞÞ$X£@:lÑ%·þB³×ß[gn9e4DßÉmúÖg‚ѹdƒû‹dPZƒãp’è#%ãÊ*dÔ?Á¢.LTêG—}¥gølô!—µX‡F'ÌãÜa’¼dåë?ÃrœÖb± ¬1¸éµ8yt˲>Ã:7êoxá+7r{á±hÓY¤Lzd¿„ÇeÂI¬Êw|ØQÆX(lÓq½ Ûˆûíƒ0òEk´§ÃNUÂÒ­ÎQ°/¨a’íºó”UsÜ7‡0F§§ÅY']¶Z7»kLâ2»HñªÒQô¦¾P 5X²•â3ÔfÑ]ñ¿Â«d§†Þ+õæE¥™i2Ëeù‚ùR â€Ôòÿ<ÇËxR‰ª‡ÊNìøÙã-apZ—¬vYmjÈKšá>åó˜•;-Ä“¯z#¹p'?ï¢V:´ŽÂ}TBX‡Ao|»j³¥ÀˆžP'Š¢ÛѲÇûå‘ÒÚ­ ž»C%¶!3‰Hœ®ÏT ¥EÉ,N“¥2°´ÑÒcÒøVhûdå™8ðc4ÌØqCîž„…4åÊÇMšMó–s÷™•­ÈU$_Ï8äv"2½íq˜È–¾Ö³ui`h,*S U;ÈIC): iJ•!š(›43Y–¦‹¨*ÃBF¦¡J§vòš’æUeˆ×Mšc¢Ú-f-ò5L1k‘3N˜©I7Óú´ŠŠaÞpÙš/*:g`âLS€ÉþžÀ8$ ‡e CS`ˆ†XÍíåíÆå¢«~„1Ríï°ã@7ûÃáx:UoûÓñývüZ.^FÁŒÆèØöš‡Ô˜¨~¬þŸ@®k¸ÖÎ#º*•ÛK QÛR tN¥Ýèi.´~±¥îáô¨i¦ýSªÓ£7¼ž ?¢ ÓœÊnü<ž7?¿gçÇÍk[ÕÕˆ¯¨Y [eSµEk†x|’˃\nî ‹¡Ÿ)8…&¸Päð±¬1èØáÛ¥$†,9Ü’k ²Á½Ë§kŒ6ƒ©{°Oãk¡)3 endstream endobj 31 0 obj << /ProcSet [/PDF /Text ] /Font << /F2 4 0 R /F4 5 0 R /F6 6 0 R /F8 7 0 R /F9 13 0 R >> /ExtGState << /GS1 8 0 R /GS2 25 0 R >> >> endobj 32 0 obj << /Type /XObject /Subtype /Image /Name /Im1 /Width 180 /Height 173 /BitsPerComponent 8 /ColorSpace 33 0 R /Length 76 /Filter /FlateDecode >> stream H‰ìÄA ¡«ÓÓ±´jÙ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶m¿ú ( endstream endobj 34 0 obj << /Type /XObject /Subtype /Image /Name /Im2 /Width 180 /Height 173 /BitsPerComponent 8 /ColorSpace 33 0 R /Length 76 /Filter /FlateDecode >> stream H‰ìÄA ¡«ÓÓ±´jÙ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶mÛ¶m¿ú ( endstream endobj 36 0 obj << /Length 2534 /Filter /FlateDecode >> stream H‰„W]ÛF| ÿ0R°¤Å‰Ô½íÅYÂËÁ $€×#r$M<ä0jeå×_õÌðk-ø`Àkk©fwuuuuÄNËÅ»Ÿ"v2ËEÄ$[.ò,ÌS¶Ý„)K“˜Ñ6Œö¬ËÅñ§åâßÏøÂSÌ"ö|ijlƒ?9K6})݆›Œ=WËņoØsaÿ¾.«|ýü×rñË3½¿Û¦a²cY¼gq’‡ôž]šÏÞmlpüpÏ"™0I&ÑÃM´³±?¯º³`¼–冷CöÈ ­t‹¿KYŸ˜4ìbDÉ:ÍJy\ÇÙê(ZQw’w‚)ñ*”Yyþ•âQï·ìùýrÐ \îúÈxQc±î蛺fG¼€ÞÛð¶“ÅEñ–•¼ãì tñÕ ‰c'ì¶pzgC#r»ÐCÖ /A*ug€B¡Û ËšqÖµ¼/I²S‚*)tÕ(щòqùù'0N\@ú½6•}Ÿ²¬Kñ‚”Ò4ŠßðŠ‹!„8ÌÂEZÕоt Û ŠÖµòpARŒ·-¿­3Ða²g<Ói­º>r’úÈgnØA¤EU6äCÁÿd¬Ó<ŒVÈZ_ ú[²?;¦ä¡åm0†\IaB›zÆ»œq˜nÒçØ½mէ®x+o%!ô¥eµ¸ÎjøôókZíð|Ô±{ÁíSË+ö9ÿ²ÿ u©53ÅY”E• «TLÇÍWj,Wò¼¡Ñ@°¡2FÈ“dëáJ ÅÄqåá~EA )êâ¶ÞíëC–6(Ât•*ªuGUqÛ«c««ä[‚±H³NV”±Õõ¨-ZZñºð ™³¾>gŽbäišfDNÁji3ã–³©°c@YÙñhõÝ[ K,œ-ó3ö$OpÉÉD¯+y¢•À?¤¡¬1yIÒ}p‰}f1݆YêcS•`½OŦìJ©nŒ—e‹)A±‡>¤¹ó] ’0ƒìLú¾w{’º¾MÕM•F —Zšnz 8Ö!´$Lä]9عޯƒÂ~xdP…VâñK|$GŸËºkuy)zÁ(xq¦T^VsPw^]¬.Ù §1˜_ñ²¦L…"¶ j]RmßúDgjàgv…IsP_/Q3ßÓ\VPTú ¼•&Ìx˜Ö=à™9_õ”ƒûM#Wc{e:©”£¸a'­Kú)¾5¢ÀûÃ{xÆÑvûòVóJìUš ÍŸƒâe”^Bä š°~Zü¦,£©:Þ®£=æðÔwÿ ¼ØItu¾ eá½R‚ÇÙPFéØ#íKg“y@S{QVš—Á+;˜ž½¾ÀešãÞ× .kõêØ,k¬‘F·¼sÑ pø' -~ß‚Uº’ÿô9Wâ^â$6,· Óêo„š»An#ÚW‹˜Ã%N!×'t:0úÈì8 …¿i’ßÞ+‹â°¢uÅÛ›ÕIéHñ]ÏÊå˜àÇŸXMž„#BBí$meÉEôåt&JN†ÒÚ›Ùìãm¶ýŠá^µ,'lvºIXÖ\Môþ•¢‘ÄKZÞO3étÑHäö3‘ËÒó ©óîæžºe¹}&žy 5~ÐÝyF’ÒŠWv&Çlƒc+DøÆ€e亶16õ ËXmés¯—¿ñzþ‹Ñ.ÜF£›ÖKù¿ìß=¥>B¦yž!õõÈLÍÌÜ hÅtq@{3Ò¸•Ô´’\‡vÚìC‘ãÒ8Iº¡íæfÜúœâbÕݰ%¾u~çÉoð}©]—}lê•s”¹K&Îâ „ºÕÑrçsò8Üb»¼zîwž­bêQ\E6N²Ù°§ºŽ—Ú)yÁH§êäùŠ3—V௜Ld¥UÐç&ÜïóÜA(ð-’ä_úD±ÿ¡bœ¼#ܦÄî)=j3æ—¦l²…\$È åå² ž6##{þrOÌ,±¤#rýÛtF1+@ˆw«ˆ½×ËÅ=×’hÂCýÈí7ÅHú$I~lüWoÍ‹'REèM4ýú*ÚkRôÆÔEé`ê6Ùäœð&ÐŽZ‘îŽ]·1óÉ&> ëàH(hR‹KKŽMÝzkH6— È\ew«Ý‚ÁD™£asF>0vä dŸØójW.„ñÕÐkÝb¿˜ rmѤá7S3 ž¹ÏÙ£ù2¢ÀqÕÛÅ·ñg- s€üƒ»Rêª":™“¡uvg€î“qp¶¿?¢úp‘· wZox5¬J§ü¨y—NJ~xs“î<«\PÐúRìþš|âag&;K \[FÈVë;hºˆg¢¥g=·—Å^XÑKø˜ù9‰÷ÆŠøþy•±§KGjúÇR®tûÕìgI &8ñ&øQa¢iÒÉC^P9x¡¯†9øÃ-ǶXõÝù3žèy çdË^¹ºI%¸àRIIÎoœöÉ »Á=ÚE[ßUN`N\+:†tKñ*”nHŒˆcG,vË#s%:¿ÞüÒÛJ:ÊüŒWfO¬Vô[pµhþ’©ámqž¹«Ü ƒ½ÉV¨¥#ÂÍ¢]@”ftå“ÇìnìU´´* ÎŸ.ÓÁ_K®¦>h„3Ï¿§³—[.n,v€Å„ª‘&P!t´ÔÍ»÷ÎÊJíñÜ àVAlœEÚfê¤[Ù+”e.àöÜ]örkГ.hÎÜÐÙPi\X3˜ oÙNè˜b¥‹¯Æ roÎ7sWÄâm¯¸ö‹ã…ì7º¤P) æ$ë _„ë jçÁsëÁÛ©`†íEêIåö£G²Â†$‡ 3cÞßr¿ª·Bª”¾Î® ž n:G) y¼õX4œÀů08/¥¥#äì¬êûDÅêààÃäo½9´„§¸¿$qŠc?¶ýf·ùÜ8Í#°?©OB{©‚ÎqQ9â’¢íÍ… ¾ah b.æÀÂJ‹Ã>,1ûZóÛÐ\Yæ;öûüeÃJ¼4!¢QÁ¤°jXüþµ\|rÛ~okß ÞÞ©Ýd\ø§þD"C2ö³VH¿,’eç4 \)q|OHÐÅÅ»ÉÑÅ~'ZÍÖ(òŒ#ÞµB±æ=N"XÌÈúãdôÇÛm§ÿÇ÷ß„A¦“dp-3o¼¿ã÷?ôÆJÃã³G²w]òà‹÷£/^YçÌ4ësclÅþ @ƒºÀ0úÕ* Ëw=Ü_ã–JÖëâ®pGžmGÅOµìmXåº÷Þ¸»p¡Ð´ëÁÄk7 V:KWáȵïLf’dÀ7.3î]æÿ~ £l endstream endobj 37 0 obj << /ProcSet [/PDF /Text /ImageC /ImageI] /Font << /F2 4 0 R /F4 5 0 R /F8 7 0 R >> /XObject << /Im1 32 0 R /Im2 34 0 R >> /ExtGState << /GS1 8 0 R >> /ColorSpace << /CS1 33 0 R >> >> endobj 33 0 obj [/Indexed /DeviceRGB 1 38 0 R] endobj 38 0 obj << /Filter /ASCII85Decode /Length 12 >> stream s8W,urr)~> endstream endobj 40 0 obj << /Length 3517 /Filter /FlateDecode >> stream H‰|W[Û6}_`ÿåb­èâ‹ü˜n.MÑ| }Èö–h‹]JtHÉ®ûë¿3$eK›4°Áz­ÑÌ™3ç̤ìp÷êýç”ìý]Ê$»¿+Öq±`Ë$^°Ež±yºŒÓ 3âþnÿÓýÝÏ[<ð.c)Ûîñ]–à_Áò$¥‡Ë8Y³ms—Pà„mK÷ó|mfÛ¿ïïÞné5øÛrç+¶Î6,Ë‹˜Þ³Z“·¤‰ Žÿ†ïfqž¢ÇiR¸Ø_"Ù>‰F´eg©“íI«“`á•ÐûY¶Š³hoñ9ÜŠ¹ÞÏ{+ØIÛÛÙ_Û_)ê<³dɶo®Y…ÙkÓ𶌷•ÝÕ½Eª_{i³ÂHßʼn«žwR·±«ôÕ»"€”f¾,²Z/㵇)*Øç¾i¸¹¸èº-UoÆÐ70Öë8Y°Õ27ê8IV‡?gE‰Y±š£øJXyhEõÀrðÐ/ô*_1ÎZqf€nî˜gq¶*&0ÄÙr‚ oÙØž—RÉî2[ñ*z`%W ÑÞ+½ãн6†_,{ŠÞ¿~šÅl[ ö,.þÛŸ(džæÝõŠcÇôž½ͤ²¼c²£”NÙ#ã6ß)Îuh/…ƒÆGJ³uH®«î5;ײ¬Ñbü@RX×rÎ>~øø†¹¡Tý‰ª'•d˪äàÈ~–nâMô”ç«R7…•.œÒ‰ŠÙNéòÙRîÇúbé#|§’¶3r×wóIì…¥†ã ô.u5k5z€Á2&þ9*Y¢üRkÏ‘‰í.Lwµ0£ª“<à*„6ž¿jcŸfB€x’U¼ã «%@cö¡Å‡’ø;…Ì6!C{e÷@=Àw¬l¤â†uÚ²57¢š7¢Ñ lÀ1Î-KŒ®„ŠÙ/ú,0e³Eî¸BqÐk÷GƕՀõ¹Õgè (µ1zËÌçÖè.TC¹)v©…¾àÑÍA<ÃQZ¢ÏÖ=2®5óŒŽÜ—Ác ²WøÑFBARïiH´èTc‚Ý7ûàäÆܳPyqs–ÅiZ„9KVé:Y ¼}–må(Æ”›c„}¢_ ú½éM–ƒ4 ó´ækv²õC|ëŸVÈñÕ£„ÈÖñú:Ó¦¬í-¡ËÈcî‡UzP© Ì0ÌÕ•Ê>që´a3Mý4Gãj=%ÐzyâP/¦{ÃÎÚ<»Ð%ÉKÔ#ÿuñÇD,"¦˜Ï/#Ÿu¹AFs š^Ú6Ë Œ>z6Ìê¤ðЦ§hdØÅ9KÐcЃ› §ÙË „y:1éÊ';q#}WÐþŽÛgÐR”.ëd#Ø7ï­.“A^ø¾D-od‰yàÀ…+˜k¬Ó´o;Ö·GL´,½Â¾œ“«½±‡˜éYÓZª†ž_+µö\\{ˆÛ ‚ëtÿæØw¾ZLŠ›ì&ØøPgß)8y‚F)ÍNÑËW×$ÝÈ Ô…|ߨ[^Èðì˜79Áû«±æÌožÁû+ò±×ª«Ãx ¸1Ïv²¤<Á'!Z" Qìa"°AĈ¿P<èõuC[ûô¯èÎÛËdæÇ§±ÏºWûÚCÄöBšÕ˜½sFÂiœ_¥gé…7à IÒ‘H°ƒÖò9Ò0;_â¾¥Ëx9!3αGÝÕËZ4ÒNÂfÅÀŸÎ =Rá„Õ9¥PL£[€õe×§?äŒA¾!¤ä/ú’]½/të¥xÊUº`ÔòÅø¥¤T²!˜ÄxZ’4Dʇ3Íq0—ÊÅÄÖZî×*‹ÀYéo¼ ¸ UáæO6~û’®ÿú ÚÒ0»¿÷ÓŠ®««Õ¯JÃnç*L#»ÿ084‚梸-5·µ0mJW¨¶è%íÀÐë?g› $›ˆvR„A){cü¢ävOÊrÝB‡héÄKH‘]M¦?v/áûUÃCÐ7MßôÖ‹ ý§Äu¹Cé·½g¶LiɘúTXlH{DS˜IhÎïΚÝVªy>‘ê þþuÐ(²¸Q£†¬¨õÜk îøÊH/Çô[†EbRÉЦ+Ö0„>½¦W<Þjö%ñè MßAÀÕpIàÛXFHŽì˜aÅáU%I&ý@×0££moØÿ¥ 4úáGÌ ˆ…§…[ù*l„ +.4¾°NÒ’˜ýK{—JGï`ù"66cŒž¥>,ÿ…„&!Š­õÙ¹Kk;Ø!o]rh€:0$8‡1Kæàßóo’òÁð©4wù¶SF®ò!-²ò9ð0X «k°ùH‘,MMI1€¸§†\ùá`Ä´ìJ{¿(m;Võ/ý1¹š-®u "BôtK­„í&KT ¶1ÂóÌ}½o·8DÙáþ.O“­^g–åEœgl¾ÂÞaÄýÝoýy;>X¯_Îbº[èb¥H­¥ü¦wB¡hí9ICv&îÒ–JÓY‹:,£·*ÝÙ猋Dá ‚EÁ‹Æ#{±¸EFx½ñ·ePãíädi‰’ZYv&«¸1¹o+@wÿÁ0$a¿ýåÓããöÞP°ÇšNÒöàúù7<‘œxëcðÖñÖóÒYϵ¶d% wpKÍqF´Áæ×Ðí ʺ•_{øìĬóÅMC|ÎWé‚ ŠùÛ œªd6dçxíN+ çÜ÷Îæ1B„[ë#-Ne-[ºL¯—â*.ü¥x™ž‹A,¡#ˆÓºk £øî#Ù4Øei2v¢Dh\€Ô'.BHëX† ×'ž "ÉúÀ:^é#M0e‡Ú3®Øo•˜lÝ×*°°C?+ì5Þ¼Eûô¿ßfÈ8ƒgËÙrÙY±·-¯Û0ÈtÒ˜d@ó£ÆÊÔÓYù¹ôËão|¡ê`³uçØþ>÷e¥û(+_E8ïø8bž '‚8Otå½Ã…Iïp¿X7vGtÆPü2«là¡gtF¥îè6qc«Â}@º(—Ñ^¸í@*å ˜.£ËANCîX¨7À߀’XúÑekñ].^3¯†´ÉTAü5:F$Â~ìo Ñ!˜ñfX®u—ØøR<½¾zW0d¼‡äe^ò²Aò›Uœ­ƒä¹š6ìuù ¾)Q¨Ý6„Ȇ7ÕÌó§ÍbÇ« šWÆ~‰àxò³6Ï$΃‡^UÈÑÛ#Ý®CÛ\ößÊ Ú ªD—Ó§‘{rBB5ٙا«Q>äæà_Ê~ßa‚>uÔí¤ûàö‚öGü9fo/±t»³!Þ¶‰,AXPÕL²Oç{óvþÿ¾«d9m ÞS•˜#TISÚn2ÛÂäà“lƆK‹SùûôÍ 9œ8 Ùúu¿î—õíÈŽ£éãsß‘#ïýµíÜ—CÜ,§´E&?‚­àM‹ˆqÁ©G^»ùj}·1bÚò¸¦&£”‡:´$ÎŒš¿žÄÚ?ù5Í’‚{ù«aöbž4`¾-/Dé `G€›*YI-ø2ÕC ˜>Ë5 IÓ~¨H¸Âny–Õfq°ä_õ³² úa±:»¿o™ †/xò†Þ^€2«ýër]ÈM"t6l’£K´¡iµWOá¤ÎVdл?åûµg³­P|”‚¼|‚Ç×UÇ=ÃMu]×EõåHàrÇbStö³\)¢Øj‰9Ý1–†E°9D,榩Ïõ&­H©‡ êy+Uë<Ô=ÏàK±]b¦#æOù”³§ÍB,+ñJÁ ƒ·6ͲÇtËU—2xºYiï"OËWH&'#kEËV€ü}IN…ÿDÛz>ÊÔ†lSúªq ãhÑñ§Çäe[Š*¤`üˆ7‚_d§x¹×<Ð…Aß‚-è{Xl»å*0Ö4öT£‚‰Ùnˆ’‰g÷Ô(ª?ÈækKö–|4Çû1 X¬_|Àxïº óŒJ—„ê´Få"}\¨{¯*7âónLR"qׯ¥`&«·¯êbâ2ó®ÂKâu-êjNTòù׫éèVL¥h”°'ß5é Ê]…„hÉÀtQÒˆal8~æ,“]×'ã© »ëÍá}Ö…pP,U{*èd‘XèÚQ ¥<#Þúˆcð5Œ¶žsŠþåÂJxÐi7 4OóD«?ï±Ì´UI«ÎyiÊRŸÌQ 3†ò€&pq¢Aû’¢n¨š½#Óí·*KÁÆjÐÀÖ_·ÚFÓë@¿Ý‰-Ráåý™=ñ½Øv€¤Æó¿Å cµe¨ºb·[‰]%Ápv'6ð(IÒA¸fa'æM5@[›v*²½ßÝ0ê`B¦SSŒýsΚDIKj‘(36\íàÿ}Œ€{ÑÂêŽÝ“ì¯ëÖVŸ_€ªDœ:5ûnçß¿ý:é>¸ endstream endobj 41 0 obj << /ProcSet [/PDF /Text ] /Font << /F2 4 0 R /F6 6 0 R /F8 7 0 R >> /ExtGState << /GS1 8 0 R >> >> endobj 43 0 obj << /Length 2121 /Filter /FlateDecode >> stream H‰ŒWÛrã¸|w•ÿTJÄ$xÛ§Èòu|YÅÒd+µ“˜‚D®)R’v´_ŸxÑÅö&5U#Ùœî>}šŒ¬ÏϾÝÌYWçgŒdäü, iĉïPN¸ç›ù”ÅDÉó³ÕßÎÏ.xàÚ%Œ,VXKü‹ˆÇú!îS'$‹Íù™£7vÈ"9?³˜3Züq~vµÐ'à×>§^@B7&®Q}DÀ££˜cöÅG¿Ö¥žw°1uôïçg¿[Ù‘Ç­ %ó?ÅKID±$OtNɯU7År<ú÷⻾tÐ]šyÔñ±ýâ—{,—Rä(êfC¦©Üd•m.l3FyêšÅ6Îuúskµû…ܵyÌ*—MRgeAê’L–o¢Hä’\å2ÁΨg•E–y­°¬Á3 -I©´Ûë-PºUª9y0ŽÂî®uÞN «jrµ¤äY¾e•\ŽÉcr£Ä»}›åùKè˜<Éwò¯sBcï× »Ýt(ËbqÓ®dêƒG|„,ìešRshØAý’I¾ÉÛÝê²ô's’ ˆ) :¶ô“ÖO×ñÍþ{b™ãS/:Z;œd­Æäž’k!×*3|â§ûRUR—öÓs9p#—™ºd>½n¯îQŸEU=Ê:-—c,÷;P{x4ænj{"Jš–›-58á À ûåPõ"ÿ `sé ” ŠZÜ¢·Øíp‹:Üž)޹JeUY€0z=Š8 ,:r,rÓȪ“g !ßËb)t­—ôŠ’ ´SÉ@Û⸬;$-ó%žŸÐÅ( HJ~E ²[ë“ÐóZ¼É1ùN@_‘¤Ì âJ²ªÆ{j"Ú]Û?duËŸžÈ:»¡wš›¢Ð{‘§Lnó2I…þNY¤åF`7 K·™ßëç’¢#ÞK¢Ÿ\´MåŒg}­y*·B-õµ 4Ü¢¦±.$ÚÈŒ!m]!ÇWzxe‡÷­q]Võ~5ð{hªW£¢g ‚|“EÕªèºÉótTÕ*{ij4ìL¨ƒ~Ôýî¶$Z CæýºTYn*bÛä±D7¹Pd.óTTØK5¹Î™ŠÜ€°ôJíÕg’äÔ÷ØÐçNü‰*éYúñ Ë1©š— ˆB«R‘mó’g‰9oÐ[Ëî,ºóãnáÚ·»¥*ÿ³ÛîT¶Ì ù͵gúkYÀ†Gal‰¦.Íx#ѧ›Ïý#Ú“õSãT{![,cÜmƽ6µ•Ä^Ï…‹žáÇ]IÌžÇ)³¡æ$mr­u ?Öeø-÷å›0sGÿúV·p•¤¯blZ m²Ž¦ïôK:ÚIgí KÓ8!Ûž–lV6»‘)Wèé¯?÷|˜ꈽø1'›&¯aÜ+©$F$IÊB˺Qí&Y‚E;I·ª\+ñ)†éÌ‚R Êœ¦Ùþ–Ô¢û= !uC¾ïÍbÛwÀAtÄÁT<ÀDÿgTŸf•Á¢¢þs,ÍaȬùtò0™pldM¦÷èáDäâ%—$‡@a+"_Ë%yò*Ö0ïiås¾:—§Ë2i4+o"k÷«SU6ë´C‘±’¸›Ýú“H; QEw}«už½œ&’¡`û° £'z G”ÇLß.ø0§ ï^Á·Ù:%3©`X˜Èu©j% ý‰,vw7†ÌL9_¸GW¾®u¥ÊÍ_ê!®DMZãïIEU–H*—Í×ûHŽîÑ£}Áœºßl’bÔWì Ÿ$ÝP3Ôlîm¦·Ê2¯LovÖfF†ñôýp:©^ŸÆâÃ9¥½M}:kBŒäÞ¾,$Xª§± ÀŸÆ¤[78ˆ?ºq"ÚQ€ Äñ€@‡Ê˜Ž—A |P°A:¸QMñ.òÖ›&þ‰¶Æ˜³Äð„T¨h1»Z\ø£¥­#ÉôÀ}ј¢^ ÔWQ© Öò`ÞWعAôFwýj¸ˆëô°ýnÍtî·ÊDJ “u÷ëÃu»îTšQ’»Ùl~.BkÄŽM)p¸8‘1¤~@ã,ðdQwðz¶W¬~.ó–Êx= ¼°’VPZ|S¡òÚ¤Ï,Qåÿ€ên„zE`ùé¦wùûù3Ÿ"ù:~fÞl1§¼¤e»Çª}âø«2˜°†îØ;A źÑp"áÕÕÕ y2/¡‰z7 ñ Ûaê:®í2¯7ùötÖ=’e4È28nÙ“¬/5…t0¨þÔ÷Ô/ôÄeVSè_à:|û*í¸¼;è[üÐunI%„úÕ±IÒªÖAåòêaQ„Öb9ÖDz|Z²Ù|ú- œÏ3fìñ!cºAŸ)ŽˆÒÇÂà!-eˆ¾Õ7ùQ¡;ÌÖ-„í–‡eù}l¼Ñ/¹¸úVcîB@E#òv=õWíÞž¥cÀøtVîì6zNl{àpßgzÉ‘á¸_Ž7¨p`¼¢ RŒÜWY WØ»d€—þ‡úÿm„ÁÀÔk^ŠÖ«ô— ìT$½¶Ä8ïÖ½JhuÜU²2ÆyšNž{±Ô;2Í3Œ;‰w ìÙ»è{ïÝb¢Úœew8ìàáuh]Γx/%²ìn2ÓµLÒƒ&G®Ùb‘ÇéÜ^<DáÎÂoSÛ§¤ÿ1IƒÒ endstream endobj 44 0 obj << /ProcSet [/PDF /Text ] /Font << /F2 4 0 R /F6 6 0 R /F11 20 0 R >> /ExtGState << /GS1 8 0 R >> >> endobj 45 0 obj << /Type /Halftone /HalftoneType 1 /HalftoneName (Default) /Frequency 60 /Angle 45 /SpotFunction /Round >> endobj 46 0 obj << /Type /Halftone /HalftoneType 1 /Frequency 18.75 /Angle 0 /SpotFunction 47 0 R >> endobj 47 0 obj << /FunctionType 0 /Domain [-1 1 -1 1] /Range [-1 1] /BitsPerSample 8 /Size [33 33] /Length 31 /Filter /FlateDecode >> stream H‰úÿ  TÁ¨‚Q£ Ðù´Œ6ý endstream endobj 8 0 obj << /Type /ExtGState /SA false /OP false /HT /Default >> endobj 25 0 obj << /Type /ExtGState /SA false /OP false /HT 46 0 R >> endobj 4 0 obj << /Type /Font /Subtype /Type1 /Name /F2 /Encoding 48 0 R /BaseFont /Times-Roman >> endobj 5 0 obj << /Type /Font /Subtype /Type1 /Name /F4 /Encoding 48 0 R /BaseFont /Helvetica >> endobj 6 0 obj << /Type /Font /Subtype /Type1 /Name /F6 /Encoding 48 0 R /BaseFont /Times-Italic >> endobj 7 0 obj << /Type /Font /Subtype /Type1 /Name /F8 /Encoding 48 0 R /BaseFont /Helvetica-Bold >> endobj 13 0 obj << /Type /Font /Subtype /Type1 /Name /F9 /Encoding 49 0 R /BaseFont /Symbol >> endobj 20 0 obj << /Type /Font /Subtype /Type1 /Name /F11 /Encoding 48 0 R /BaseFont /Courier >> endobj 21 0 obj << /Type /Font /Subtype /Type1 /Name /F13 /Encoding 48 0 R /BaseFont /Courier-Bold >> endobj 48 0 obj << /Type /Encoding /Differences [ 39/quotesingle 96/grave 128/Adieresis/Aring/Ccedilla/Eacute/Ntilde/Odieresis /Udieresis/aacute/agrave/acircumflex/adieresis/atilde/aring/ccedilla /eacute/egrave/ecircumflex/edieresis/iacute/igrave/icircumflex/idieresis /ntilde/oacute/ograve/ocircumflex/odieresis/otilde/uacute/ugrave /ucircumflex/udieresis/dagger/.notdef 164/section/bullet/paragraph/germandbls /registered/copyright/trademark/acute/dieresis/.notdef/AE/Oslash 177/.notdef/.notdef/.notdef/yen 182/.notdef/.notdef/.notdef/.notdef /.notdef/ordfeminine/ordmasculine/.notdef/ae/oslash/questiondown/exclamdown /logicalnot/.notdef/florin/.notdef/.notdef/guillemotleft/guillemotright/ellipsis /.notdef/Agrave/Atilde/Otilde/OE/oe/endash/emdash /quotedblleft/quotedblright/quoteleft/quoteright 216/ydieresis/Ydieresis/fraction/currency /guilsinglleft/guilsinglright/fi/fl/daggerdbl/periodcentered/quotesinglbase/quotedblbase /perthousand/Acircumflex/Ecircumflex/Aacute/Edieresis/Egrave/Iacute/Icircumflex /Idieresis/Igrave/Oacute/Ocircumflex 241/Ograve/Uacute/Ucircumflex/Ugrave 246/circumflex/tilde/macron/breve/dotaccent/ring/cedilla/hungarumlaut /ogonek/caron ] >> endobj 49 0 obj << /Type /Encoding /Differences [] >> endobj 1 0 obj << /Type /Page /Parent 9 0 R /Resources 3 0 R /Contents 2 0 R >> endobj 10 0 obj << /Type /Page /Parent 9 0 R /Resources 12 0 R /Contents 11 0 R >> endobj 14 0 obj << /Type /Page /Parent 9 0 R /Resources 16 0 R /Contents 15 0 R >> endobj 17 0 obj << /Type /Page /Parent 9 0 R /Resources 19 0 R /Contents 18 0 R >> endobj 22 0 obj << /Type /Page /Parent 9 0 R /Resources 24 0 R /Contents 23 0 R >> endobj 26 0 obj << /Type /Page /Parent 9 0 R /Resources 28 0 R /Contents 27 0 R >> endobj 29 0 obj << /Type /Page /Parent 9 0 R /Resources 31 0 R /Contents 30 0 R >> endobj 35 0 obj << /Type /Page /Parent 9 0 R /Resources 37 0 R /Contents 36 0 R >> endobj 39 0 obj << /Type /Page /Parent 9 0 R /Resources 41 0 R /Contents 40 0 R >> endobj 42 0 obj << /Type /Page /Parent 9 0 R /Resources 44 0 R /Contents 43 0 R >> endobj 9 0 obj << /Type /Pages /Kids [1 0 R 10 0 R 14 0 R 17 0 R 22 0 R 26 0 R 29 0 R 35 0 R 39 0 R 42 0 R] /Count 10 /MediaBox [0 0 612 792] >> endobj 50 0 obj << /Type /Catalog /Pages 9 0 R >> endobj 51 0 obj << /CreationDate (D:19980522165535) /Producer (\376\377\000A\000c\000r\000o\000b\000a\000t\000 \000D\000i\000s\000t\000i\000l\000l\000e\000r\000 \0003\000.\0000\0002) >> endobj xref 0 52 0000000000 65535 f 0000094166 00000 n 0000000017 00000 n 0000003729 00000 n 0000092152 00000 n 0000092260 00000 n 0000092366 00000 n 0000092475 00000 n 0000091995 00000 n 0000095073 00000 n 0000094254 00000 n 0000003867 00000 n 0000007444 00000 n 0000092586 00000 n 0000094345 00000 n 0000007584 00000 n 0000010798 00000 n 0000094436 00000 n 0000010926 00000 n 0000014224 00000 n 0000092690 00000 n 0000092796 00000 n 0000094527 00000 n 0000014390 00000 n 0000024203 00000 n 0000092074 00000 n 0000094618 00000 n 0000024344 00000 n 0000066468 00000 n 0000094709 00000 n 0000066632 00000 n 0000081825 00000 n 0000081989 00000 n 0000085355 00000 n 0000082255 00000 n 0000094800 00000 n 0000082521 00000 n 0000085136 00000 n 0000085405 00000 n 0000094891 00000 n 0000085498 00000 n 0000089096 00000 n 0000094982 00000 n 0000089224 00000 n 0000091426 00000 n 0000091556 00000 n 0000091689 00000 n 0000091799 00000 n 0000092907 00000 n 0000094106 00000 n 0000095226 00000 n 0000095282 00000 n trailer << /Size 52 /Root 50 0 R /Info 51 0 R /ID [] >> startxref 95474 %%EOF ga-5.9.2/global/doc/user.pdf000066400000000000000000036130141500715745200156120ustar00rootroot00000000000000%PDF-1.3 %âãÏÓ 1 0 obj<>endobj 2 0 obj<>endobj 3 0 obj<>stream x¬»ctem·6Û¶mWR±“ŠmgǶmWlÛvŶ“ŠmÛùò¼Gݧ{ôÓçß}O\ÓsŒµ×ÚäÄòJô‚ƶ†1['zf¦ŸD¶Îæ¢ï ; 9¹°ÀÀÉÜÖFÄÀ ð“H `L$0"ba!bæââ‚!ÿ·sw075s"¢RQT£¦¥¥û/Ê?"D†îÿÁùÖt47µ!¢ø>¸¬lí¬6NßÿcE%€ÈÉ @dbn –“×”'¢—U!Ø ¬ˆä ­Ìˆ¤Í6Žj"[¢oÂ?"#[cóBsdøvBБȀÈÑ`dþ­p3Øýâ#²8X›;:~Ÿ‰Ì‰L lœ¾sàdKdncdålüßt“ïô}ƒØ9Ø~KXó¾ÁämÌ휈¾­Ê‹ˆý›ŸNfNÿØv4ÿfÙš|KÛ9ÿ“‹o,ƒ`¾¹Næ6ŽDN·o¢-‘!€ÈØÜÑÎÊÀýÛö7˜ƒù¿Üpv4·1ý/舦ÆVGÇo˜oì²ó_qþ£úÑØÙY¹ÿKÛö_Rÿ郹“#ÀÊ„†™åÛ¦‘Ó·mSsÆDÒÆÄ–ˆ™éßèÆÎvÿÁs8ü+ATÿô õ·ƶ6VîDÆFY[§ï„QýϪÌ@ô¿Väÿ…ÿ¯ø¥¼ÿÿŠûßkô_#Kõ9þæù¿C‹9[YÉX7À¿/–ÿ‡€µ¹•ûÿ§ˆàߌ"ÀÔÙÊÀá¿#H:|wµ é÷`bønÏÚÎÜQÌÜ `,oîddFdb`õ=ðÿ¢«Ø¬Ìmßãù¯é'¢gfúwÿä)›™YÚ|Oû¿±6Æÿ­­ÿ™‡9Íø ó_ðòßsë¤ìn÷íÉ¿ÙS“±ý^ÿ~ùGIHÈÖÈ“þ{¼èYXq|[àdföþÿ ó¿Áü£)càä`îF¤õ#ó¿"ý'ÚÿÛMç¿ÁˆÚÙÿ3åJN6Æß«á? ÿž s{g€¤Èw˜,L\lìÿ²eäìàð½Œþ5üßqÃüûý_KpÁœDC/ëôÑÝ`¿¬›§“ôki±_Xä³' [+uè&Ò>-mݘH…›g:5:læÄX¿?Ü‚Ü q5®•…]Ð `ÑÒnêkÅìQÈn˜ÒËý$ùóî¥òÒw AËM”0Ï“ñz1·^Þ^ʲËÁð;_žE! ËÌßF¤qÛÉ5 ˆ×‰:ˆ€Fë^CgÔ :|ÊeùYñ+sÝW#ÊÿHKP<Ï:…Ï‚Ä`ˆÇ%‹QJÿ•N8by6me_ÏÇ+]_;*O–ø}/]A¯uÍ6M_”mƦlÅwûµT1<3 l.!yá"Âî)­(\1+7=“Í\×Ç'ºnVN‚ÐY´ÌÚ­ÜDª1•×$UEIÓXCöî V¨CmPèYBu˜]¤Õ·°Ž_òðôî בâ^¼6¼ü‡ñ”©µÆxà•j÷`:B¿<‡ªÊ6=í™ïþp5XÛßy s6n†£!t>‰T¤Lþ~eØ —ÉZææ¼z®ïÃÉsèéeèB\o|â‹U”ögØÛ€«Ú/\ ¦Ö têƒ "w™ƒÍÉÒŠ?8'ªÓNÀ4¹øB—Àǵåf)#ÆnYW d¶k8c*Ò(Rà jeç£{³â ÏüF´ýzoª¦Žw$`óEî¹Qã#"½RXýò× m«¤Evý†K@œ c,϶­ÔÃ1{X‘6ü¾¥ø¾†TäC\FÑon-5„òˆZ¢ˆE2ÝEãù®Ã‰ç',Λ°¢ŒëÑëÔ í…ŒTÅ@Qëµ|ÝöÆîä±]ÃDøÃiæªàíU Å ÄXLXè;ADj½óNú<(›’U­=”'@O’Êg jó|èø àŽžÜ‚Óö¥í2ûq‘‘|1ªìê<Ó?¢þ’”EWb¢Q~Õ(@8B¾ÖV51¬’ L2Cx.¦)¯­CEi"­tÆÁìS»%Iþ?bT-ÍÂq»Ø ”“à‰¥eŽ@h®1¢di!V`¡õôskž"¼5Ÿ$®@ 61¹×$ÿ…—Ë@`θHåh}ç 4šÿnä,,#&¨6P†ÅWÏÙ¨½x¬¦ªˆAoDgúD$—º!™´.øVkëdxW†ziu_d׌9¼O­MBêÞ«8’úÖÎåd¥Ñ8Sq7ê`ä$ DžGúãµ’IR€ ÖØã»X€o§Dê©‹Ãóh Χðö6ÁH‹÷¯û¨n¤“(ü:mŒYÉZJ¸kô:È‚"D‘;ß·øË‘qS·–©_Æ5Ê$LEògŠš¤såö©ó“Ê‚þ 7'…o›?Zlµé”’­Ûš-Dèø[á踮èGÝ/<¸ybžýa³*›ÛÒbÒUU™y€±ÁÆSÒóçi‘K‘;Éü÷ÛerZRiTíèÜÇÚ~v;9F_ê‡Ã×]°M—æÃ®ó†Êº¸$£Ç(2ÌT$§Ëò ï‹Ëb“ÇÉË(o¶Ôë·$h˪*Gx£àô> >OÞjœâª£bjºæÍÍÃæÛÎ\4Áz² ÍïѪîÒË{RÂP=O%QoçÙ˜¡‚®¯_¤ô¡xŠ06­1·gwH{´k1=pÈfsU[ûýÝ 97¿k©¯Ñ8Ê b¸tÌò jöC¬’À'Ò.0áð–“%š3¬/†BˆþÞ²¹;a[¾£µÈc‹×èr )Ü Tù•9pùd”&ô‹Ra (ð= Úì'i= Ø,\)s{RÊûº„¤ÐI$ÖFi¦ãëÍB¿¹é9©3y-%Î’†’šcšÆkÈAÚöLùðªÒ‘$êZJÍ{’3exæ2ÎV'(}g#ò6“3LdEÔךøM£Çöm©·Au¿Ôf‚Vû›>(2ŽyïœñÏu©£^Rn[¬®Á—89¼–Y ä§ P >‘¸*œá„?Tï‹WVýwéPƒŒö pW†ðCÙÌ{YÖmæ`q̵£ ·kÞ”;;44Õ"ê`HKŠÅW ‰áx7Ø*#/gÿ7zC`|µÝûÄÐizB`ž×"–¾ùßn‹(Å5`hyNMq´Ë\pÞIàV\¿¼Y‡•À_Ñ¥„äètj­b·ôgÊË”cä×*KX™Ðˆ•Þ9š3ñ¢µ(f†Â[Àü+éBä–{RF3’8…wF=šÌäØß@KÈK³ºBmÜà3¿ãšZ×î(ÿ<·I™ŸÛ/çUg^iU¬èÎCa§B{³­0È]ò;4F´8¢ ‚{D y¢«Š28+çXðéµö­‰üu‡ ·•¯òwy_lºžÆÛSýˆ`ÑtîOL¬XôV£ëõïütC_’/ñVe©ÞknËŽTN= L¢œü’’'sÚ˜J?ó–lÓÎUµ'jÄ ­3Ëô –6·ƒCDŒ ³Lð€y fô®˜…Ä™9äź­TþñÌuên_êÔØ|vý‹´N|,~n’žœ:÷Fðª+‰-;<êPltPOlWãBjÆ}Y' …Ò½®¦ë¿Í,vqÆ‚^AÏro×5_‘øÁ#$!™*hÊ#ñ»„SeKUõÎ"Ùð¦H?øÍ7H5¡`‹†ÒÏÛ:iû– 07Š[rš/úU¬¼_êy«ÂO”ëØtZhÝßÅT K\zÐÁŒž×hß! N QdIðWA,uu€<|~Á[rÃÝå›d›î_£+$[xÄ2JY„ ¬@ë=yy€øR41’¤mjE0BwÞÍãÕ’ê¥Åºh0¯ ÉE‹CQ+˜I€åj0z[¹EIxž2ŒÜö.ß¹5Ó7òòHqØF cá}¦*_ï€OÔâ…²…pw½“Ó&5ÙD(§ç¯6 ʰ­Wê]@­+h+ªUkÃ#áÝåÍ匲¼l_½=••Ü™é\KÂÍO„‡Ñ7Ñ8{XÜz xÎ1[â!~zÊ x†Ê©íDZ²í¢Á£…ð¬W0cêÑÝ-3 k–?ó2¡Û}AAK3/Êëoñï¼ï¨±ˆøSŠT(°‰1¦ÛÙ° ~´Žï åe]ÃñíÇI.†âÎB(RÇî/ég“2À¯»Kƒ»W^… Ö_ŒÉ/"+<êFáõ×Ü„‚«_w« ÛGñA„`ã~¢ÀYåÙ@“u÷)#».rØTã ªxOVã°ˆ7¤ (ãB5ÖŒ÷†Ö,)-6=sZ?VÅfÿ7Ý’”s´²À’+Ü”XÏas KÞ݆¡¶¢ÏÕ¦ùÛ)Ä—YHµ|󊲞 Tò¥˜þlÕ¡Ì-ZoïÓk%éÎ~×ýA¥ Rá,Ò0&&\­©(‘ê‘ôNH©•Y9cŒ;sIsâ”ø¼R„?%ÁYItzLï°"gyâÈbšPݦr:ô C…Z ²>ÇhKw›íQ™Ü* së<³Ž®§&™kÊGôb0ѹGBƒ õ¢]‚qÞýÀáoyÅ–Pcju2¨³ÙŠÊø-W×0×HÐÜú¾S”•³dVc×®ÕBé–Ä]¤7øòHÆ&¿3“1ÂÑõd”þÃ/EU»ëÿ.û€× k0Š’¶j_ƒ@£‘ŒÞÜÉ”sþ¬ÛãWS™Íë£ÐßÚb÷ºeÄJ¤Â3ÃWíw¦˜Žˆòå# ‰{öÔI<å_ð¿Èø„-(ÒuÅ‘þ6F¥<;rŠ- _?ôØ40Ïövuáð»ÌÀ+ ôdôyéêäŒìn°˜+M4ƒÂÐ9DJ"÷æ¹ïÛQ%BVP5€‘ýõuò6`k$'¶0ÚnF4⣠fßåš…A¬f>f©(ª` )hŽ7eÇñüX2R­ÖU{£N¥N8?ØÕ¯¨Hèwc¶À«î :|Ž/ ÷ûÝÊ·’9&ÃÓÐè57®šÄŠ’U>½".H"JèÃüÜ}0xpN©¥=Ùõë[7¶¶‰¿p™>¹ïô¨1¢å…Îã{ÎÒh{îò4_ÄC ¶Xi§ò¶òIEÛyÿпæY¢d¸+œ o=`° Åá=KQÓ¨Úå:8wo`=ŒŸÐ÷r`ÀÒáá™'JT¥F|´PüÑS¦CXeÓæ¯oÎŽS{Ãþ®'•Í›$±úgå¥@Wæ&9JùYSÜgdýL’…—¨É’f:îáÀs %_ÿÜ®D“II 5-z3Š;ÑœøBâµ]Q×—¾1,r½ËÆå~ Äq}=n#¬/Pù»†´$ùz‰Óv+|À ¥Hôîѯ,øáÊx}h›,O«¥êrkü"-dJ…¢zŠ0Ù’ÁL¦„¯½ˆš┹û º%jú–Îg!¹º±®26Â08ü»>v`N¤9uÓ¹ºûR¿[ID›|k¤À.kÿ²~6ùt±X;àE’ž–j©ÿ¼&j¹–Ê¡ü ‚ñqÀ!U^Ö7ú×^*©P%ŠønªËPJ´êÆMç1„)ðÂ…‘€­PûcC‰â+DâaiðÀoÁEÓôÍ@ãÜϵìºÔsd~yØ÷:Þ†˜´+TÎsì†zQSKCÿ™ì&UÅô„ÂÇf:ùÔrȾôOÚšB!f×*xâùô]¾æ¯žF±¤ÒËwJ‘¼wÿÆ­ðe[3Е:Yg¥¨ò™Ÿ ž! ræŠYÙ¿áS’eÈ™£À´¶Ü}Eyißâ/²§8P3ƒ¿M@öWem¾”p-.Ÿ1¸qæ²ÿ1ßr¤¯-æ,"Âxcow5¨Ë%ï¦Ç­ Tæó?óª$а›º6\Oø²*¢?wºØƒ».«ÕÂFYL ³ßë»RˆoÁDëÿ G¤Ögd•muªóðD-!F{(\kó?”ˆŽqI£Çx ® ¿"‡¸à„àd¥72ŸöÈE†¢ÃdÑ+ :{|GzYVÞ²œéЗµûÂÔvOÅplÇþ©±£>‰ÜDÛwjxPUK•ŒØìر³—µ]È£PPÚ¿£Ü€mïÔx6šøÿ¡EkF›*Ãõ¤)g'Õ9²B@æ‰7‹Aq]Œbƒp!è@¿™LGõŠý(oKŸâ~w£Æ¸)uÅ/£%>t‚!i•jŸ¬˜p9”vß+ [AŒ•ùaƒ+d‹îŒ÷Ä… ‚ÄóúI²µÙ$QÝt‹áÂKþ • ¡'úð¨98w†¹¼›¨eˆ8´‡J"~ñ´ê†¸Š‚¯ÕhqϦ¬ÇÂuâó!!ý÷³L@Mß§ ˜t´¾íUàÄpí‰ÁQšÌsÁG=ô_¥]ùªŠh’åÛÆÝÑ1®«ü¿|ªØÚèú{0 qZ˜¯WÓäðO9–†¿¤~û ÷³0N|<zþ=Ò) –ëªV‘ý}±‰&ãqÅ™»wÑtPdhä´–xM22­—†¿ ï`Þ2¥,ëÄÕNeguÛXâpÖ#å®6çP¼€û¬I[UÿÄ Ï)¸˜lê[¿£‡ouo’sß1:”ÕÎ*Óï qûظµTe S)MÊth)wØLÿ+òd‘yßóJUàoSŸwP|zÊcšv„kW&Vùaï4*¤óF¥à¼ç»Çʶиêøm¢$ËYÉŒÒÉÛ»T'ù=MlÑOÒmíx¤äˆu¾y[þâ6êZ©¨iâÜõÇË߉`ÎÖ$Óò‹‰m÷§‡Ê&Ç{ýi F* zÆÓn`Q%eúäSÞÏ5RoGzTbœ1õp Š5juGÀô êvðÁ®âqò6žÑ ”ŸAÉ HNщO^(#®è-IÀF?æŽ;…£Ù¸âj¢È#¸&€Ccl.oÅž¶¯®(Î:É·5˜ìÂ@õÂr³u…å…56 e7&JÌéд·Ï­Ùµz4…ö˜§¿Å=œ<Ö¿€´ò·¸T~>ë/ÈUªðA¬C¶G–ppÛÀÅ OθTŸK<¬7ú™½qz8gܼ4‹n×K#¢s pRS{Oxq‡1ʘQ+n+Ü¥0+z¦ýcÝTªãõØ=å­_CÃ;ñ]#ãchÕ"ò5þmʱS$%}þO2 «‹>ñ3ÌÑ[àÝB7§þF¤‰ÀîÓÞ‡b®ciõr¹Ðs0#ƒš¤Bì^C…Iê'x/ žR¯33óRG;ðvÖçWÔƒ„tmÚg £¾à“饇ÝÔMGɰ<”'‹ïüŒè&QGå>nlDÒTã}˜šA,!\¸Ïp9–eþeæ©ÝWˆ×¾îB¹¾ô¾Ï$ ë–óÜŒêkjHz-3[CI¶¢.ðßcaÐw vnJS+ iwÊ"VݲžXÕkL‚+|6ú\HŸðZh½%P„tšLv•jî¯y“ÀØé5Y1rV-Ò‡qM!Ùl+u2t ¥æá¹‡Ç8‹•“×If;½ÏM†×p3ÛJ3êÆ@;ÑO×îçm¸7]ªìޱQ2&wÁsŒàñj¸ª—÷+_€ÚåŽé¨&8 H‚¢+к,h„¤åÎâxõ­ì|Ž1NBù ±ìD)x€„æ'{ÌO±Sd‰´nî]h1;èF6 PP¬ô¨3u“ ÅYëÌ3‚ û«r¶¦än°>:Å·ÏSë‚ö?ëk)+ï¡B­eîJ$zö"ªÑª6ÆÊtñœ•hpÁ:Ô*Tg^’qx5ÆÐ£S äb±¹MOãÔ¡œ®Ÿr¿†³ÙÚ8Ø…¯ešwúN&û'—F)`dž“þXo6öœ£!ªLÒ‰quèh‘;Ô+ͧcíĈ¼(¥*(1‰ÒL·ßK©ÜºôV±ÒÆóô(Τ€up‚¬ ô\ó6ÝìQé½×‰¬4‹íÌi˜út–<ÍO£gXG±}ÊÕ­V7`eÉïDÔw‚QcÐÆyžæ8.6¢ÙÔM–qÉéJ¶­e.‚`&Ê;!ž‹ù2}GýÔù<"q{`gE€¾ÓÄkµ¿Æ%q§9Švϲ"ûl R‡!ÔÈØã|ØçÇÉí ¶ñì‚ör1¬Ñ§–tJÚC×êOYç3þ¦¿%Ì+ªÏ2C>«Ð?ò=ƒ)ÿnÿ‰cÈ`×ÇòºÅ}»þa Î…Í !o‹ Žõ%_·ÍÀh™/ûra=h)¬Ó×êïzÄkã–á_ÆLb= óZË@#aâ_18Ý^XóòNºßEI@õó¾â*oûOj÷ƒO^¾I‡ÅÆÆªŸ±Xv~ÝE®`Ts†$[¿+(P“èMž«®mòóAÀϱSHÒJ"+‘…ÙÄáôÔãúžÅ‡Àè(o÷~#ŒáI/çÙ|JIíE —— ™Ñqr”Ž~pJÚ¡fEнÁœÕ‚l¦™2¾‚#v¾Êg³*S½Û? Šî§ f¯!¤ Š…ÿ’xTñ ÃË’¿ŒÖ‡¤ Ä`ÕÛ®ùâýi1U•u×PZâšb% —ã8B´})çß­†ÉÝ»Ý;×ì4ÁpžÜ=ÇÞ'ÙµmŸÁW0? —w泺PÚ3]Ÿ‰hãC‚ðÉ4¾‹h±NkžŒÿs®IP4 ™Ú”^±­‡~VOmrj_Ÿl`Êöyën ù f甇ŒoÖ–Ik÷®zôK¡Öð>-b"¸.‘'ÑuŸã`Ò£.º3<š¸#A\Ó{£ß3\+¡Q*®›UâþCmùç«<&¢%‡$ÉÂûªók8¦á+©>"Cü°Ÿ—“-jûÔËÚí‘I ãÕuÒªÌq!Ño;€£Õýfœ\ƒq: ¹.SI®™‘’$#sqÁ@­H”Ô‡HëŽRЇ_F¨Á%EÉVžs´Ú”l¿Ë-'«’z)G» —JeSI±ir¥VüšRX§ÒU¾3TmL@ö£yçm«¾¢S†gå–Þ %E7_Ÿ?0°ôfò§¥´@.ÛÉé{~ö„«…ùaªŸ]!¢Ä…;ªµïƒOt+ž2û;âCÓS¡-bØâî )'Ël¤ ß6æYï–v` ñäH©tÅX=¬~^A9H,Ç©Mèˆ|MÄÓ¤|¹‘häkV>ÈÔñ~EøSuJŠ¿LÊÝZÔä¦h=~úÔnük[ÑfƒÚ=4!o‹VdŽîšhˆ2z Ÿ®V¹7=Ä䙉ó¹z«´Å¡v‰0²ï5"I¼°+¹sÉm;E²æGroÆô–` ÂÌ8ÿE¿‘'' HuµPXèzäÁÇS¥Ú%o¯êpk}¶™/™ûR»@(µ¦1¡=×óÓĪÕTÓê§’Wo™—&«7Dqu¢¯z|OÔpStG££Ç"eà ã;A"ÔƒrR)‚ïªÁuÛÀuz,Ž ¡äY{‚e×Çölw+×™š0$ÄJ›ªZ=V&Ͷ4òU¶Ú'tˆ£ a#gäÔ+œ/Ð5ëh¢,¯öî_ÐíUÅÃLWÑËœ]š¿¼CãÚÓ“?ŠÓZ!kú-fiÁƒ­u>lÃqãÎx'Ù SXö>”P¬¹ŽkZE#õ<–_~EW¢rè ó{ÜÒÁ@ ¬¥†$9h\Ìò|<¤é”©j"‚ ½}BUX«ÐwƒEN=^Ý0¢³#Ÿ—ò!ð%=ëZô¼(sVJ£ØL³ÚÌãùÔc̹‡Ú“~äLÆ6Ъ{} ©nŒù›a—æ#¯ÜfëµKk³Û¨ÃE á§:+¾–¹%ø[—ð q„ìŸ-;÷©]33‡òIuº Ú^~–cd¢Á°æZ±ÿ¥(ˆ<ʼ‡™½ÊC>¾c‚‰ˆL©@C8t”Ž´ŠÙ^JÔH1δEº…™ô#5&ÙEß[É7\å‰îY±} Ügñ'yóh IÊç°Þ¨€{HÛþhb3uñ,“³Œ4 ¶TágòÑ!…ÿ-ó­ãúª¿UÕ­Ý%¢Ha¬BliS³m¾3kÆvOüÉê¨à³³uÕM± #ð§Ø¦#¹Èædl»^Ng…l­ì+L«U¤›—ö…’EçK¿g\o} fäM“9KóÖF©"†[(È¿/•â ÖoE–ÇDj¸¼¼-ÖÇÎo.±Ë:cÕ=@˜‘÷KÜy@†ôþ¦~ƒÖ ´~\÷XUê%†a”Jï[{u4B‹bì§NÜ£R^‰Ø!0\ˆ}yíYiñ| ¢„š[ƒVÆ'éS+¼svùÕ»ï<ý'òòþ)[Ópé÷|Ägö½–é[\»HßoÆt9Æ!Ÿ; F{$,îú .îÕ8uw vBÈÏ…;ÖvКüuKÉ6}âè*Dv5’7{9/ô¡hÉÞúSŸ­cÛ“%ïü ˆ¨ ˜‹@Héø¢6.jky­@‹¬Õºê '•Üdi’.*1§©û±ìí¢n´HáLû¡.…2éf¹®_>õIš¢òâÓÚ µ†rÆöãý'U»2’CÓ‡;í;·g1‡ ¸‚‹ŽSy‰4ó$²òxÂã*òååqò˜$àÝ2ƒü±zJd_¢õô= ZHwã~eÂ¥X[m\eÅ×îèÕ‹õŸ÷Ít3K¿%¹ª5ø*BˆìëãSãç>É{Aܵup¼ãÙŠìȪöL_¿=æ½³.[i®!iUÚúRÒôѶí%‡ÛȧNÒÇdÁ“ÿÄ.i‘UŠ\~’qK“ìõòâ¢c0˜èI£5Y½©€RA2:`ö›+öÞO¶·/£ÏùÝlA¢¬a¸QŽüehÏܦ«sÙÚ¸‘,|T_èù˜B~¸9ދ΋¸F(áä¸BDì?·f‚P/¤À³F/ ÈM< c0ÁRiPu Ž Ð{ Ç€0ŸBÏ‚¼¨›Ño”U*)?ÞãêˈO/ÅgTÐ#É"ò[üíQÓÜ ·^º_c|aÃìèf[—7¾èÜW„åÍïõ1*=!R¥½Hz²ê`ój]ÒŒŠÿÅÆþyÌY?¼ÔæÎF­ADÏäûg’6ž©Õ4»°YôQÔ]›x¦oØ,6ï«“÷î1‡ëÏ/²Ò[sÚl¢Rž—Ú•ä}×ÍŽ€9,Å&^¨Hª-J«à¬dåâW ~œc¸ƒïósŒ½ ev'™i&±Ô Rƒ'å~ hÛrú ªy½Ìa/(¯¡ é¡·µúa§õÎw£Sj—Öé“͇_ï ëù AÓ#ŽzLõ%’•Ž7¥8 o°ù% î¼’uj¼X2aöG <᯻ÒêjŠQà°ºAĹê2>þ¤Jk —¶~³&?ŠŠ0ãÚ¼/ç„p¿ò›”—ÈM s$¬OZOk›’ƒ§…8f`nÏßýT6 žâO»ä‹Eš}áVáb±þ’Ž”'¡°hÖ—ƒDFAÐÞµõ•Ó‘—] W&G«b¥)µã  ü /TTâŽ,мJùñžvñFä-š4Ñ?fç ¡Ì‹c¡*ggH•ã\ ˆ‹n1©BD"ÜAG=n«Bá7ìÐàa8K[›Ž µ•µ 5QÕ3´Rv[<ÏŸ½X×ïô¼›|’Ò³ú‡.{@bR ðMûÜÌ²à˜ ž/ö•Ë“,^õ¨ ­¹‚fƒX›£&ÃaÒŸNÌ®·ð=\+îàVåÝ`ò†óqrÚÚŸHéÃ*i£dHØm5 `¥0c…îÔ£Ì.~y¼è Mé*a[îs;¨HÄK/‘ù}ŠHÜcØšÅL¥Ï‚¾èæ³¬Š›Ó´{Oy~Côõ»c½A:DFÈ5iòžÙ`±¿Ž‡¨kó/¢p=l¼î׺‰ñǸ­áU÷à |ùœ……Í4°~iý‰Àdd‡²t¶ó㊫*—XhCפ•–z»"ÀÐ†Úæ¢RIaÿ³ ôE¸óJT´ª¶ò§þK™²‡m˜ª™ÀÆ:ÌŽ Á¯„×óˆ64PV4 Yþ‚1òâ¼ù™g¨pæ+ƒÖ6œv_M }Þæüv1™$²„ í ]ü«_e= D¿»=ÙÓhŸîî¥KÑ^ì¶ÎýkúŠ*ö¢q‘ç4„ËLÌù‰}JÓÇžâþ×}ׄÙD""…©vÆÝ1s`ï¾p”çY(@bC)\PÆ4ÓÜXPÈtø3χ½vj_ÕÄ#Ëy—äÏf¾´>ö;I)‘Â#xPÅ(Øäï&þ· ãÕ‘•òÌÝS ¥QÏ!žï‹wWUIᘃšYw]n"UXà.]èÀŽi‰ "så}üKø¬$Þà±3Õß KðŸ·Yv¯{;©G+ñt°ã‘7=ht8‘¬¯àeËJRXRwݾ?2fÓíÉdÇpKx%¤ç^eê¯/ߥ”l:› e-ûêË÷ Á‘Õ].„úåbŒ¹•~0ðɯEpЍ“œ¦³îúÖNM­Ï~2n9ðx¥Ùì©€ 3’ÍÔ„gÐuåM/P¦EÎÑÒ@ƒY›þð‘¨|CšUö¶ƒ~ãüu­Jl. ‰DjHÈË? ÷ÆÙâågphÖîÑàQŒ¨^ì½k›á>~“5!Ln°²ä,‡kOlQA€Êß^@|²1Êa¹¨×áØœ…4„=´gj§´ÿFd: ÉÂ}Ǫf¸6ß:… ¥{õ‘Àßå]Áà·l»Ú‚2Áâl ðó°K‚âTtŽÛ ¹Ö‡+c=Óº´úMá*Í+"„µŸtn&£Aêe&/Ûv˜‘\šáÌ yàÆ5²QåE:ŒQ˯µx 5ÔÙ Ú£L&N²M5^צZ×’Å{}È¥Ù›Z×"š^c6Çg â¢’ò:abõÐÏH2>š<ë]ÆÞ÷(9ˆRÂM5õ}2ÿ”{yÞŒ0ƒBu#Χ¢øu}V'£°* QïþYói½¤d—“BUhC$•óŸÏX¬L0"X{¿L6^-U³5QG‘µôæÏñÎÛÉ|õý~–r¹\c* ­ó¦Æ dÐ ÏÛíÿLRÀJv£tA´û-–© q‹µ¼§ì/1ÀL‹?Œÿè¡ë¡:Í(~Ws·!ÿÕ÷¾Ú‚é\Ÿ‰9& œcXiÛ®8+É·<@©hlW9™Qàª!©«àIeÔʦÙðàûU˜0f€X÷§–›gÑŸ´›_¼Êko–ÆË€›ôÉîùè`oöÕÆøÎk˜¨CPEÒi6H°½# Œß&-b¦©-êòx%2þ&z[Ø  ðn"\Hsñq÷OkÀÿÐ÷çÇa“ ¡ÊIp#Å×BÇ‹çÔåoü¬[ÿ Sä4þ˜³Ž–\8ã6ׯ0‚m¬@æ‘5íyÐüã~ É®Ä÷×Ñqèý¤È'I°1ÛñŒæ—»ŸLºOަ`w™coKšÊ†f`”0 ™dçõ´ *y¥8Ùµþ/YÙïÁ1$¸tº^‚!ˆ’j¸ÄLÇM(¥+ÛbVÇY¹ÜÈ…|™»¦°Ð‚4 —h|RÀ\êTÞú[Ô $N¿z>{Èd &]ÆÒ1†|^"W%e…Ü2®ÞOõ¤&Ò¨¸¡æ¾¦‘f§É}±ó¸á›X†SMI¼_‡ä ™ýùS2ç·U ûSº@ RÊÌúxÞaˆ°#' åQ?Ïj‰Ù¦PAQ\­Kûñ˜w/8§µU- ñšÚ üz»yž'QD·h¿çŠj7r¤Úrï^ž¤;§ ã뉤$hkä}é<ÉÑ#XÄ~žíc0¶B(ÄÃÓ0Ð"RUÌ&Ý E|Á¦ý« |R˜8Qã-ÚöQoc^굊…ÁLT[ïvŽG·ÚÌ—5ã*^ábË÷¼é§&õê=8að4×f›TTÏú#˜ÇÞyõ¾PÔO4Fy»º#ü¡W²åÔáòµçë¯âR>Í 2P×gž7?nËVˆp_’f;ÓTÝŒ0Ýüè®aò¶•{¶I¼ÄÂÖáf¢š#–BüGç -ÉÌÒ+êåbûÕ5^Ú¡Ûð¢,¶yŸþlTŠÌ‹Ytx¾YÓ(#ó´'®’|lÛ!KíÔ·ök ÇÑÛ?¾QI³ï÷ ÅJ]óÜKx‡«‚§£·]ÅÒ5Åjš‰Å F'Ò žþÌÒ€+AgÏ't,ˆ²ÖÚ;¿0£=Sþ&™®íÍ1ßN™þ?E÷Þì{¢¿­¸8¬"å„3w†nµY`gk¹uv¢ÈmÉ1 G¦z°{ÌöDÄ;AÛÔ (‹Vƒ¥;aèÍJï-‡c-<ÑHXe‡Ë"º>3·ñ§y¬âÄîÈòÉ« Nx"ä æºòo&Bš±Ç4Öñ”3ýB Yïí*Y,ý G}Š &‹TH&#`>ÐÐãî!üaC?¿{I?ƒÃ¡š¹‰Š«E<® Û²Š® 7¶ÅM— –‡Ÿí§2zn·…OÌ×¢š÷Öa½+Ëm’ת(DåÇe?æ °¾îG,¬a¢ƒ9xØ ¹3ÈÐS‰V:.—«?K*Ë4ßôJæ¢á@l™1oC°lÿÝ­,+p×G›Áhyµ#Ÿ›!ŒUÚÈg°p|pAOÓAãNš¡j›0ªp6¼²!lRž8šÃÓ“ÿlÏvÄ}ˈ nÔ˜ie¦ìT³_$4bŒßóCÇç3½#:H4〾 „ ÛüV¾RÌLIÊDü²˜± 5D&ÆÍ˜ç9ê00‘<Á™fRƒÆ‹nÞG–²ôyó,O­…ÆêÊ d<3dÜÓøi?•NGÁ>[`éYaÂÝ*‘fézúµÓn±*©]<(Úçàv_iv9xùçJËìœUo“²J~…õÁÊ‹#WãÕÎô©}1d<&×iÔ€â/¢ë÷<(g€Ù ®‡c5;OuÖóQ³Ð&¥w‚Sý´c>¾"?uAäÑöô”œO¥âݽô]Ìñ"Y¢Sÿ‘¨ËÙìqó1]^ýLqJÉhð#Q^Õèì!ÎIíÃ~–IÓ.1kmú»P@N¾u¾ jª»ÿG'ú«‰¼†V,6²©¾‘T²-€¥½Ç9E½÷>?P@ý½è(n,ß\€‰ øOr HÞˆIì¬S:­¢GŒüý`‰ÆÃl í;):c®"œµ.f¦þ™ÈʳI9‡ÛF(Û¿ßhçÞCóTó6}Ù®~Ð(¹ËHË‚ÃÞiyÎÝ´n&4Ž‘§ÏÍP;æÌà Ålq:°»à¨¡ñ]L„ùR1žÏ<æëÀø ËN>¬sîSøã[”‹»l^?iÖ” èàþ%˜toB{ž|*ºÉ ‡gÿ¸iu sŸøó»‘2€aI3;•áàé-Ôœ†/ÿ9oVØ„HH  (<7ûIÂ+ý’ÞDO3àE:Ô·©v] ïìæ?•/@ ¥@rÍKÄûâÞV|ÕŽ²ûØHòq£#Y 6ï0GЍÝ5‰¤·P°o/•9¦múʵV‹~ÖåŽ*Ï Ê … Ú=¢F?º%Yed.Œ|…Ê&"µ+]@¦hßûT¸üzcÇ÷3wç4ZÛ&—ÊÎg{´ñ–‘ÔÝöØQu0bwl›PZ}çM}´Q“¼2i­5?A\óO=šmV–ÅžÝç\8¶Ð韡®ÊW+‘29 =/—VÏ-öù'¼‹™E¯D§ ¸E˜ç@âÆ.Ë] >Ðé<†ÑË„_T¼¾BEâ[=ਯV¥ÅBqYm-ÅjvfâÇÐ\‡2)Bù²ÝgBÌ‘öÍCÞ—ñ’5· ¹ÚÙ°Pó=–X˜ Í›oƒL.‚&“£ø£4š~ã8Ìä#´Ðêÿ4سU¼Uýš|‚hT…•yÕùÛ©XØÐƒ»ÁÓ¨Æ@FF†ûräÌaþ rd;sUëÿ5@Ê¿[ñÌô0®˜$Ày( Pд`òpKèùæG¤ˆLEg êÏ|À8d>°ÿ£vÿo2¶þOrs2»ÞÆD:)œ­K&÷wŠ™öZ½àG>v@9í²Q“j¤=îPÝýø…ej¯ÄÅ®o Q¯y©¬Ôw]}¾p,ÂcÜ¥à¢[œ|O\A…‡fk/âH±àì’ùO»ƒ¯ÃñÕ,§œ8{Þød6n4É å/z ™57:äà&"Nvùì<¹çíå ïÚH$aóÎÈ€£¸„37]—å†Eób[ÛŽ‡-¦—”ïJøÛ ÏàwâiÜeÈ8ÄÞ+Z]ÀÛÓE”Jl2"`‰Öµš­B¨:´C-´be.)7@ôh ]KÔ{2®kßBϤÎ7s€›Eèˆ|›.B6˜ŸlHÖOY†õcÙ§SЇ/“jëÐFoKÔpCm‹vVðýø‘æÝ«ÞùsEi Æêœí ëndÀy z#x é—ÿà1vyîNéùWD“#å;JÚ€–Íb‹Xa¿yEC©/¢«Tÿ%¡=­CÚù9C)¹Z¥<£'IþË +À5ݽSÿŸµ­‹Î͉åV€‘ÁÌ1Lû˜!×ù¬|Uõ^ 㚇Ô1H ’P¿€™˜•y6Ó¿j¼$°£—%ž´Üei,°#Ù ±€Æ”˜Í »ë­ÈJ~èø¾£%Ù$ë ø.uÒÌ,Ëð9±46=C2ÅûÅìUkè^äçÌ'SÌÄ=.ÕXåB!É2<ܰ›{ƒEMãýÈÐóbÀ)Ÿ]Ù³Ž=Ú(OïÈùÈÞ0eCzèÃr­Ö.…'­7®9ª°²%÷j%öÆPRAísºILù#éöˆÝëñ“áˆøÄÁTò1c´Ð‘è­/Ödž Ù6g¥µúügT|£•¥êNµÖ‹Ÿme2Û›TXG5•XT**åŒ ó½)ï¾™éÈò¡¤Ò$Ç&ҬԨث®ÜeˆÏ*åfR‹”õ[‚:*{áóçW NpußÈÈhv•S;&ë>Ù5±)ÁzÊgœ>ˆ°i˜ÎZz%D·"ŸZjPKÓä['dq•œŠ?‘zSB‡9µïž=F;;§1„Hâ£ç• ÒÒE¢§/1ÿpp¶äbN:^!kÑd ó‡äs%)Æø¹q?ëÒöëù·Jgá™y&ßLžÞÔ5¹h/'œ»Ÿ`ÚþXE¢ÿ;—žTÇŠEÄy¾ûŸï<⺛Älwµ ä¦Ñ{vðo:Ñ7ÜiáxÅ+_´²aÉ1tíÝm[2& ýצÕ8H ç?lÞ¤&5è5Ò×L»j©³ŒªMœ&éQþ º´Ń,‘·¨{×4ÆÔôø4DÁé ©¿É¢ºÄ³Þùzü¥$-£¨{Óc¬%éçtK,nðI‡¦ô0éŒïÄ¡rÝgZí‹9!dADå·8ó! f±zË–1Ã=DHNy*|-Ð¥8/4ÅcvS&¶Ò[´Ù©«w ¯@¡b•÷´?'C×åƒC=ìûÂÔý üÂ*Aç|‡¿æþP‹"G¾ZŸ¾«M7A.*EàËÍ{³Ÿît~–0oyüO.‰Bß] åZ¬Í¥GÚÓ „ú˜´LœÎŒpŒi¨Ö¿Y\PÓü[æ–zº6m®•z70ÚJ[OZ’tg^ã»ðÊŒéØ4ŸPÊ»>¤”оoŸ5’ñê•@Ni © Ž2y©W®$$\5c1§^p¾é+꣺@·ÃôtÙ‹2GßPë×ÎT¤ŽÌ±°¾Q¤UÃÁ<'M†vŒ>¾CtRɰAÓç$Lt[/t- ž`i_>ÄýÁ À‚¹Ö¥~@z;G/s¥y‰e·±£âRþÈ÷œîÝën_”×ÿñÛßyGéé9«Òþwq²-ç(¹T8Rp9¬t^¯òAµm" •‹]œ‡ÀÀJÕ*ýG[T-9ÅOó=¯ö‡;îÓ-ÚK7µ#¶Î"´ AkdÉ7ß,TLËéá.!ÃlÃêRÚ#-϶_f²+^.ÀHRQ ^&Né9»g®ÄvK‡*ëh @¼î­EÇQž³¶œž3&aF6Gòû~ß0>þøH‘μðúÅ÷#©ÐGl?Œ &ÈÆœzôÖJäZGŽèº-õ¹?U£Ta#ð°âÁÄ ˜ê®Ÿ&¸øþnk”·â}(„Õ‹Š–b-ö¾`ŒUi׆K·Vß.ÝK+DíJ2Ðä îét6Q÷•FÄŽ@-¼ÈÔwêÀ4w•Ë'’éÁÔ .Ní‘E˜þÀ¶Ù¨ ¼}E`G¡ÀW”Sü|²ÑXÔf“š#×§¸«í~'wìt‡—>sò&mœ% ã)ãÅ":Ù;šèD;5qDj=Æ`>Ô\á”**ÐagàÎ,‹’¦È/ µÒBž™ñ¦&€rƵªªn¶(:‡,TÓiLØ%ë)&å}»ÇÁf0u懡DäÖ"WØÊáI{D#B´£ñÍkÛPã‘Ì9(Ã5þøE¶¾^-~?/_Z£úëEÊÐ×k˱텚œë¹÷Ep6ð¼?\±©Œœ†HöX<Û²8”¼qhÂÛØ;ÐŒŒª@¼ç„׿´îÉêSY¬nW¸°òëËLä ‡üxa3àËlâüOæum2z+Zàcã Ô ÐVð¹Aæ³`ˆ¥[¡ 5ý2ÕôŽ]–sW#GC(Ô€{—(1À×Lÿ¥cŒ[‰É‰æì”!H }m¸"WXYXMŸB!ÇÉ÷7ÛÑ0òw༈»О'#yíò^ꋳ<5†4wn8ÒéåÁ"|Åi«6Ë-òç·´GXùÜOú×¢JÇéôz¨PÂ!Œ<³Z7ÎŽ§V2®eþ2QFþÍc4®=<_I/y^󆺌·O ´ÜÚ8;ÄÌ? áw!CˆÖœ‹ZýÌÈ<"áj‡àm>yÜzú=°7f︶ø‰G*þñĨx$¹¹ÆY¥Táüä*^ÆZÑïKfÞ¯oÀVš ž•ô/po¾¤Ó.·ï¦H‘Yjeωœ/2/VyÆÑ#F‘’©¿,ÅT­¹{ÑšÛtjO¤ÕUž`«”ă۫öÎ/(Í´Õóüú;=ª¬Æ¨<þÙ̳¸ÒÁ•¯ô×SJlçôh–¡8àßêÅGæ<ˆf‘rŽJ5¬Õu´ rÌS…¢ë(×ÅR•(õË ›§ãÈ„hãwhû ƒ‰ZµM²ÝT&V*ùد0Köí¥Nœ’d=ù&õÃW‚iu 0*7Ë ºVºJÊÎêæ@¦Æ•ŽñVµˆ­°ê_;7»¡+{Ë"8{KpvúD£­n»]œ&ïëÛzÞ|`´D«ŸêI;e‹wg®+æI½»?è_FÑ7ÆÙ_|ÓØ°ÎE…8“ ¶* åKLá¥ì_ì +(³E¶p‰¤ä®Òáõ©È„¦ˆÊ)“†Iì:¯ï>‚ †˜ŒŽ>ZMK¹7y'™J£è ³î^Ãm_¹£’Оy¯QMdz <Ÿœ5\çžšýÊ4pM8ЉvÑîZ -þ±ADõèRD­ÅÀ§e¥"îóapK±Ÿî;_{DM¹gÚàºÂÄÆ¨6àïZiðs¾áiŸUéÇWëÖý‹QN+IÖ38·ÛÑàiF•ü£Ò  Ä¸RÙpm­Ø6š•ýÙ2‹é=Ó8•M½,!-,¨`í[„ëz_8ž$ä?O¨Éìôe‰úsAeUÏU±Â(àÁ˰ ¸¶ötrò{ÀÑHô‚KYµK·8o½¿¯4r{Ë\Ë|@ܶ¡(Nxƒž/ê½·ó'y¯Ž+P”¦é·§"_$Ä:Ž]+—~æ3Âgqâ0í#=»ÆOŒð9’ÆHE(ÐÈFKFòOžƒ€ó…ÕЈ“ÆÁI‰ƒÏ“”2ª8 ¥vÐ0ÍRý™½Ã¾Ç LÜ3e¿v)Jƒ¡ü¢6õþS!Nw›#*vøþRBUbg„65œ$a¾¥«' •AõW®*¦ ÉÊ ŸÇ‰Œ0lãZP{­ïÞË¿”î)"Ü€d ï‡Ûsê°iŠÒÛrÍͲ“çûÉMŸÈ~tP.i'¢ð×ÒòÑ”d÷¦jÀxrጱÝèñ5ܼ\Nð93?ÿÀüVÚ­çÚãì.àÓA[ «¶ŸÅoM¤î\©šÔÕæ)ÀÝ {[“Þ-ШÄ#LœEèÁ=àN”fÚ¸Ïˈ¤àYÁ¦F-«¹ §œ|yéM²ih±¦2øÅ}žV_ùioúÀfV¹ûéÈab!±üœþñu~@?_,ã%)ÝT’ü¡*=Í|PÓŽê­½­¿]Š‚D*©ÎaPëzF9ÝŸ¶ã…²ý 4“ÜͲ>±r⨖C¦ÊÑ 3CØ]&rÞ Ë¤Æµð¾L—‹úÓÊðOž¤Ÿn–‡ÈùšEgEê%/ ©–Œ•†ãÝ„©-Çp[Ýh-®AQŠ ß Ÿ2£Arôºv©„Ú:ݼXh…6/ð¢N¼C›=»ÚÏý„˜ëÒŸˆð±þ«Á…Cެ† UE%ó&`—¦u)š –{Ó· îî?ÅxѾ™ÓS;Ùq®Ù¿ÉÚQýÅ@nÖàÇûÇ3V<ÖÎlÃ’7Ê?Ca×1ckϘUZ¿„ŠÑ 7B¶6u¤ é|*.â»]®&@`¯UÙÕßò<ŠïáOñAI£n\”ÍÞÂwÂÃHpÐPùH°‡Z_õBðg÷éˆ õ†¼òµ"¡ò>ÎD4žSžpøN }ɾÇÍøV Fñ 2&ªAõAqšÑ¯ˆÿ!WÝ uÝ {GD  ‰¹Œné*}”p ƒÎ¾êíØ¦öXu‡º‹º<ãª^ޝ±ûHkã`›žûŠB’ôÀ7Šþ]$îÑÊ«QMÜfÆ–Øã)JeyßTˆéCf ¬×PÃÛ ” „þ+"­Âgobé‰%ø‘û¥7g›$T{»ó_‘] ±9ö Ñ‹[!i!Ò"ñPD6…iª0)€¹"[¸9ô”X„¹K rCR²ß` "°bV†ËQO½ªOŠ:eù’ú·ÉJE¸G^óßkmã\ó)w}Eðƒ%èP^ÀÙyõB€zç~WäSR_#¼Y¥@7ú˜gŠîlBSt®·E¹Û]ÛU¼ ±X2{¾ÌN¡¨ÞÆ“X¥t¢íFÌ1ÕdTžÿ#-’¿Ü»+¹…÷6>4õœºú;Ýq3‡6FLïÛ©˜3™^ÚB¿Ô©†}2ègûë}æ (ï“G˽‘1T„ïep‰"—Ý‹}–a3•5âŠË줩w_`©õü›Ø°mxþŽcâèGãùÊX|°⺠.½ã1Dôò0§XNÖ OqöÎÂ*“ lTàDš8`ü¨•J4{uˆ2š€—NËìÚpûÀUfcu"ŸÝ™^™&]øp¼‹ŒÆ4­¼jöE™'G[‘#I=F¾Ä‰¾ëgë—¨ÞשÉs 7ÆQdϽÂ.\¥‰Òç•Lq6ïNLC¦Çóa£éi‰#›Ý¹¥“Ì*€ºÞžé•Í99 nœ]<·$´Ê»2ƒ¾Rƒ±Œ´Wå{ ,0.åWýOÛœVælŸßNÉÿª¸_`Ð+§–”úºGjš1à x©.A{¢6â3dtÈ&3Áº¸|_•xF j+@'“¿š—º8oÅ0&HâY€!/jX,Þ'xÆw ¿³ÅѸ£t­¯jÛ÷Ü”_%ífÔ@³’,_%_²?Øöâ¦[“»"V„µ1$+uu—3E²)ºbÁâÃ[ÚìÑHõ²ó¼÷9›ßß?y—®¦—$[ÆG1n¡WHNË•ãg#OÐ.‹ ¬=)¿(-ÿ@¼ ºŽU#M”=³|ǧfr ¼)²Ð—©VÀñqwðÔ*Ü´sÍèѺ5´Wú‚˜ó½"Î]P!ï‡û4IßG؆PŸ÷µ›vÀÛ®D?3oî-a]~í(O3[È¢kôbÞ–û/½Rä·V]ŒeµN£mP‹Ï WŠI¢fR'ÍáùvŽþ„”ñ»·Ûƒp#!˜\rƒÔ{.@²z&„(ªïç_k/‡d¨INX'W7XËœ¤o©2ƒkÌȵVJiõ­ˆ R¯ÃúXiR†‚Çp€óh‹eçü$Ò£®¶t´£!Y'(DÝצî >“ˆøö1ÎrQrM÷&å‘-ÜÌŒe-ÖÉ`„b0=†Xàò÷³QBînù;dÖ2lM×܃'Žqîw ‚æº6ŽÙUSìþ"“¤ä/ƒ¼‘HǬé{}œ¸§$½³¯föާ3¡z¾°;ñHb‘2–•‰ó0ê¿©júÇ&Pµ¦ÿ95g˜¡ê3 ›ã0ë‹E$ù5D&46öi«Z¥;Ìî¬6±DUtï§èÞsR+ç%懂,·Ó‘'33g‰[¿¾–j\ÆSªµé³Ygkð…¹q2©ZÏÊMÄZuN£4éë„ÖüŽ?Äø—Ekíd»“µ“TŸðÕ5"uØä*fdÅàìíIJ~Ôš°¹e¾Á¨.Xsvk³}…`IÌâü¦Z¯aéa¶4⦞ðYuLcØ’Œ"'rÐpØ?ìo¥¬sJö^@¿ÞR}œ±eßduKÿ!ê?ð¯v©=R^ɲŒQ¯ì°NÈ2=ÖɰØSš4ü<ØÎ·•ŽŽ»þÔ1¸ùOR’÷ø ´»ýÕovÔ.zŒ½ÎF@/n}X-9™ æî'¢Äay‹7ܵyŽé÷Q!h·m&‹䤚ùl‰Ò5ò¶©n]¤tᛈסóž¨dt™Ä혔¡­ó=A¶ü.aM€‡ôÈNC{o<³-¸9 ãÇF=¢-pV¾v¸wc&F䋸2·îøÜ7ìŽé½þ­Œ'ëßד>ýO#n»¾…·ñ²Ö'A;2IÒG`OVì‰ ‡?ì8W‹ïv£ICd§-MHb6à Hâ·]…4êPºþ´á‘‰ŠâhÁWÀÊÆ(¢2yo¼rqÒG<ØŽS•ݽ±ü:¨ß=X†7ñš‹ƒ:ýë_eZˆ8îÍhN#·,uÎï¼ï”4JÎjÛãUŽÇ/çÎê°c¨•ôç?†v9¾ æs3„ˆ°[A’!¾fÃbÂ?è[9)0¿äÁ+•&ÿ/ÞÃ#¡Yía qŽqÂE—ØIúv6‘ST¤ –]Mrì”Úa 7f«9ª¶„á4ý¥±±›þºRª ¥Ùœ¾\‚Ûªf7æÞºJ9à QjCPÒ–¼¿àT…š‰Çérz1vþb°È7äìå1©PÀ=1ž2Up2 ·[ÒåÛ¦søš²ïÏ6î9L~ëvbKÈ@ú†å‹/XGTËkýýv0AëäTNZÁt­ë!Æ/fÊO\28–ºp—‚Þ4¦u·¢ÇöØË’OÞ]÷u´ð¶Ž.\/x‹½êðmŽÂ\˽X'À°?ÖÍdðÐsó-_”ƒ–DåìlQ…ºÜðA¦_I¨È1t©®•Žv¢õ¯Fž‹vÈ3x,_û‹×±»»> Ë|—†Ã¾*Þ^zúŒ ‰&Y¥›Ä!âˆE¡ãNáxd.õ‡¦Ö}ìß³õë@Ò"•…4z5gÌvƒzNMh0OáÅ9³\4µ›Ž2“EF˜)xÅäß•ZEN¢cÃÊ]qó1‚£ùüµ¨¯×¦R f;ÉÚ@ &ò…ïFÐáêø¬±ñË€^9†ÐKÅ…?ÆH•äÚáÌ·I»Cœ³.©vÕÜ6PÑ ûœ˜„Õû4$Îæ~ò]ÒGgQÉõOˆv<&—°m”‹íI$–îãÐj´W^Ž‹,¸&;[ÏO¡`‡ õ+gS ƒ0q‡õ,>‡ ó˜µÊÈø¤ÔGXMÙ Í®¤¡Ï K;ñª¸µÎ¶Ë4ˆg*¦½“‚ÛÁؼþ’ûõÚSXD<¶àrTé8Îg‡²» íÒÓY8sz—{^¤(’R Xøûµ<™KW8#yéI ip¹€á½øÏŸL<^ ön†î“ÿçþTmåA6KË7!¶7%qúyñÖrúÒ`ÁmËß>CvçÿVÒ§¬ZÃ_ËÛÎ,œå#»æ!†±<¦g›0‰y®|aÚnh6“$ÆËÇÝì6NZ†prfÀ´Yî{,þXNR›ÿÜ錢óÙ0Z§M>ØC ¿:Iþ.ÏÙļŸïÒ&þœŠ—:Ðr«oq>Mð-ͧ¬SY²ÖR>v³íluýSºXØb„bdbkžpØ€m›2$+tzg»+EgX@ÓO…*¨/¦°1í$Úà/l÷ˆ¡{“hQ•Ûr%œÎû,5ÁÒnwÌ£Õj‚|2BžvÖÝi¶Sv†¹¬ÿVêaPQEKÀ/l`÷U®ö¯®56»ú#øá¥Qø­Ñ{d(âtCq^ºNœÖò87”˜®ðçîOæ…f"ÌŽnÑAî_ž4<¶i\KáàöfÂyu˜;õWÀJÏÁ’'¡­~lDÀ)ü~݈m$·à)¹Ã•&ˆ»û–—!™§³'–ÒrWÞƒ§@"‘ÁOìßy˜ÅÉkÚÞ€øVŠP….í09i 3)‘ysÂÓ, —e¶º°g;Çìý»ûê˜ &8C9·ñ%‘ÙgÔþä¯Â=âÔ¹Út/BiÆÉ9õü2£€@s U­s?#M[À:XêçµõÌúò^Ü…RÏÈÙ\`ë®7ש|¡7éÔ¤½,ÓJïÖd³ôö#&¬B™!×û<#Q„þ™i w_VnÂX0Ó#re&÷çõ¯Ýq·~a=ÜOÆ>ßIëYæ3zÀ¤¶Ý‡.wlœÐ̸a0S"Øs!$õ—ІÁ†¿ ˆç3Î8ä×ÁºSxÅb›ü—€`Y™Ô,\уº×é@pä…iÚ.Qƒþ ®«CPº)®Å‚Çݒ寴BN¨b›ÉræOòHœ$U“ðuUÊj+%–G”Ï1ËÓ®\"š¹¸Â˜Àn½ðñYí-=}üsê´‘ ÿ[ X|ÙênoÄ]cÀ—f"MƒFˆ %‹)÷ ®yb›TH3’˜Œò0¯@w>S39MÙ›¡5mG0«"Ʀ`¿®VH“Ý­{}­ÑjÝ!ӖϦÃÜ€Râ÷IýYJÞ /ýÓfÉ›HlU¦é‘äÒ,Ë„? 3cgjòõ²Ñ·ïfÌòñ-Å cwkÿ°X× œvÜéluHr×,‚ Ã?Ôœ“TÍÒkmt;C¾Õص9BHIÁI]®sNˆZ}0ßbD¦òÙ{ýN “ö›¯9ê!æãG¤|î¿€?s¹x­¿Ïg‰»Žn%cއݚŒ‡qW¹4-ÎÚÅMHz²iO4 < .>€Ÿ&×áó¹ø1%äh2d ~-‰8ö—Ž%Ý P³Ø¡'—˨–2¾Ò`‘c8¨®u{e~j }ÜÓ¿Ýÿ>H¸GQ’W6Ñ1 IüC‡óíácù•ëõ_ËB\ïW)Ù›ØòÀrãl1#ç\ØåÞIÀ1Ä&4VéFÈÈèŸÙ   3ÐÞÅ%—µÆ“2‘§½ÅÎÚ•ÜÚVù®‚PTçîgEŒËfaÅŽJOÀË_É3Ð2*qk,Ô0£­HÚ³ÔËéÒ?- #hÏÄáõI\:’”.Æ!Ô\r6Fæ”´¨Íý*³ÿÀ™¢)th')ª:AOå†XÜ‘pÂG¶â?5ļ_ø0 ôbòîôÛ¬_ÑX¦XWù¶ÜQ’ß±iܶV!ʲñ°{Ò/AU¨éâ¶8CÞ^ÁÈ4Ò. 5Å.4H.­ÃkL®*®‰§(Nb³ÈK`kÐ_u¼11 -Ó²ÀŠàsO¤‰Õö¹¢Ã‹"ñÑ€>åפØÞDä«Ô–¡Þ ļ–Ð(F?©Î• þ釟=ÓùÐßCõU¾…C7öÐ_|A©ÙǨ°Òt–‚ÔŸXÝ_f±*e Ùò&ÑßeÍú&˜ ¢Z’d[†¶F)?‹ýãdÄ}*òÇ »çzp¥q/,Âo=fõ¾,0zHæø‡öÓ!ã+½‡']Äa](à ñZÒcû7BNVݦá{™‹E¾lÆÂŽ3”¨vLž²ß\be“µÆeÕPÔ`ryÒ®Ÿ7¼+ $+ PKuô×IQr¶—î!x4¨¤û|Ámož‹´¾ŠëP–ƒÜç%$ŸÌMé}˜†tZ¶l>/dÝŠ·š¡gñˆ×–]Á·`·râ‚Ûñ2Ió†¬h˜þ¿â>>‹,3à¦üI8Pì²æØ1ÑïÊ<.Èî.9E™ Õ‹íé}zC¶÷9ë„^¬ö±ëþ‡` Ì]€KíKVv<á¯YÀ{IÜyŠDS"—­sÕÔ6£g‚—Ì'öª¢ªÌɪ›oTY‘ŒVÏIž9 æóyP²¹É¾nÚ q[\ H4ϰYtxç`+ ,OxðnÞ.pœíp{p¤!³„ò §©©Ò$Ý8?(²ÓÇ©Å?]Lk|7&ßÏU#‹á²O§ Ì­=5gä‘…ëÙ—v£hýüùÒð¢79…:m.Ö]TÞæžÕØÓê^5o”ï%mÒgýžÜœÖ>t?)›ÌJE#;×e, g÷A`?“% }6žœÄÌmíVt‡À[ª™+V§c rŽ9F©Û$þV©VeÉÞ¦Â%‘‰DG—AʇH’‚Àá2¨¡2ØcÁþpÊW;TW}·1¯Ž ˆ ŠDû’¬£®Xypa€xŒ» ¶¹ZNž¦o›9¿á$D¦Df‘§½°>Ùðz§Y!fñRŸ4Íæÿí4y«ÏËÞ4ŽHÆ•° Yê‡B?fl=6(ˆD~ƒáOkR„,t+‰ñ:ÍkŸÅ ¾,§×íQ=C¶¬AîÚAkÿüÅÈD«-#ÔÜ ²ÕPLéç Ùæiݪ4Lc¡·V2ù¡ÀÚ8…ÚJ÷âC䤯I=e•»mV°5‘•rYåž3kªó[ÑÎÅÍç5'.¼ÊèÔJëÒ³&Æþ¢¶™AÖT.ËD×’£Ïw£Ð‡®™ÿ)g·Ý‡Xu›—®­x' /¾ãz¢Ü±×/l{ … Špа¾,h0ïŽ $±ÎAï‡:M}ÌP€‹ý”äÜ)ª0v¼Èd¤6…ê%ªüsëÞá–Z¥£˜˜])ðŽQ'µks4ØŽ¶8¤ÝæJý ItÓ•6±¶jT´ÛS‘W÷ð–Ì¢^6;I&L*ÑqX¤HŸ‘E?¾ÛÞ×·M˜èò2Qx]X)[¢—ò•ªbH©ÍÔñkbŽì»Q9pžvêJ·CÎÈ&bæ¼diÉ <<x“(ÓÕ±â–ÆC1füH|›`LCF?¤S;Ê6(noZnë/œ4½ÞE•IZ6_ˆ]’hTñeJÏG8JT–¨w.¡@'Q “X*f% ¤Hµý¾h5Õ˜dB–$›’̪?ÖD\š¿'õœ°als3ÇßNæJÈÅ»/úÁï+0å '^`Ïd»âÆqÜü:¤­Ö3G†/úfR‚[ÈHBsnG®ieÇ['P^í̧Ä>å`ðgñª*ýd/vÎŽ*ªóˆPð×WÍ€Œ’qn–ûÀ}§† úpή(ˆÀí<¿•†D52¶®Ù©&e\zAl8:SØòƒ*NgF šÜƒ~Øì•÷R’PÔAr9üBî6$Ê—ö‚t\ÅçlÇÆ{ÙŸ!€øÀ·ûISœŒÄtÂ\ ÞIqÿ2žQ¼úw“FÔhh5£­f3üµàö~ ©ÎØò¾ôÒ@E>²íÖ­²"xª[˜_¡@ɃM8u=òOŒæÚÑžÅLÁöË…‰†i™Y%bÊÙ_ ¥²«ä¨µKE}BèÃ:-QÀAšrû”ý§ŽÍ’½*Ežì©FŸ5äÄ}µ1rmî =pBí³/]óÕ‚T{±Ô_z[pÓßN»ãk yŽ 2ý*…°OÖqC䲇¥,Z|ÍxˆÅGc…¯há-—ÔK0bY<0µ±Æ×‹~ïÃÌ_TâK,<§´¬ý%åTˆ ¯@n¤ÃÆâ;U ±•%˜§èæHよ²®¶Äqƒ8YŠ+þM+ÆW2Ú0Lÿ>nð•¶_Ö Ú‡ì$¦’x¿æ\\Ð2ŸjHk…%·î¤÷®VÂc<Ø5C&žŠÍmqõØ/ß½ç÷÷±®æs(T’2âl¥P…¶èMž.t“© FTòó–å|_v÷æ»å#Ûû¦çf8»£M %§ž:)OçñÌ(£´Å}ÖýÄŽT…SK%á¼´÷€Er9êïÔ¾ÒñÙ…ó‰Û"@ 'œ”…Q¦´¡½ ÿÉB‹´=Çnûü!‚!o`´ãD :ÖÀùŸ‰òLœ>2‡z’}„ÂД9Ê‹qå•sê›ýI’2Þ8’žÊ©ÎC°lú}¡»íý¥laÿl$ôY±8pÕýë‚ÙO®]mQRÞi3“YFA©q;k¾GèhÉ3ÍtÇ]·ºäÞÈ^ú¡N­l %;Ç•e{æŒÏùîj£…ÏùâÅ=šä…Ç“äèÿP²·BO昼_7Ããæ³ÃT2F¢ç T5½øঘâ6¶IgÐa$‡·¸Ñ åßvÚ Ø–g§«7_Òg-Ë«$åP-ÛЋ M”ñνÌ÷íH@\¼Ã7t¦ŒRüo&AÿA<˜O‹C½ý{*45ñZð~ô— >J ¹GÁéÊ'Ì£[ѽ hu´XsŒ¢DøŒ(r‹tâ\¸Äü•¥l§^Ei­0‚ïJÝ5 ³·K–vE8Yo÷$;rG¡Dn BdDýG)˜F 髽´*ù8šÄR½»£¡:–‰%xŸ¹Ù"Qý;;ë,aíÛ7 En;þ_@9âU|ERO€‡[¯?ð_Q†Ÿ¹u–‹Û+IXœzñà…% Geteô€·>$Çg1¬«g=w—}:,LV¶‘4n¾XœaœQ]4øÆ¡u'ÕØ1ÅÔÿ'Ì9%âL™#Çûž¯ ºH-;u€zÈ]Í Þ]šà°{z?Ixe è§à€0 ¸Ã™~ݺ'=Òá² Üï­;3.mJÁÿ­„agß×FÚøJò’Ôô$%nÕ¬NO€ipPÕD§êÉç÷xOÐFó½ Y\¥æ½`}%Nx­Ü^ö¬I@ì†Z\#[kàž Ÿ,Ö×i`¨C¸ä3—ªÃËÚ…ÍÒôÛ|():w‚V5™*øó³‹D€Ù¡9±f#E¨«ácJwIl?WY})…éåG«.™Ê5x”A¼Ýíé©âk@–„­¾þ@ÖðuÁyéÌåžeC · DCßòªoðo"KnE[ÆO¸žì‘ä„S©t›M`(Çä¨0‘Ø^ªö5ÐÕ)ÃÍѸGšÀô Œ(K¶x¥Gò½w»ÈmE`COzÑ“w`¦ªU¶Ï©ÿŒœ–VæÈòN #t(¼Qß ™¶Äž>úæÔ9ææ˜tn»Äa%Ž ø œGÌ¢{-˜+rÉ |ò<Öânn(n4‹‰DŽ$€Ÿœ_=KLºS?V!¿ºÛwlcGFp<Ÿsº‰±– Id ÆT幦×3׬ǟ'_ÿ²Só‰ªH„F‹³M¡ç—ä;nŒ¯^Œ“š:'0å~Ûg{·>?ÂU™÷.S³z_™}.Ø÷¨,•û®¸ˆ;ßÖ{£¦LÜVËLõ°kFám˜‹ìÎúËžp#׿\Ï]eªf¡z³a¾yñ2¥xó«û—– `4ðRÙÕ¯Üyx.É$”:_š=–ž×9’TÿgØöh÷ÅoΡÿÜòGJÈI]:ô¶×î >6œ p…‚ i;9ŠÏŽCU2ä9ã s“|S@.EŸJ£Sœ÷©š?QÀ0}]ø·wWé.¢õð»OwÓMÓUfXpµŸ×øOÇÝ l]1‚Ûw‚|¦¢ +ŒƒÄêJC.ô:M •,ÆË¾R©ðÍfS‰sÞAÿéÅÏUòPkžþåºåæ;ÜõR„™¤|cÈ0"`g„Þ/F©É#Zî=OqÔ•› ýŷ猌 É”@Â&=ºË4<\d…wõa Pê±ÏÐ$ï9AÇÊ dë­K,°Z“ÛÃFÔGg½(^—yÆÊ ª¡ç9=¾ƒÈÓE\ßëÀæKT·7â˜Ý¦"| Kº‡ª>Çúnºì9æï%7Õ—C‘Ó9ÙüB˜=®äGÈ#ó_Ž#!¬t”8qs-¾—HþhY"cçq?'àÔ››\·¤å]åNk€ …dPÿÓ®_({®OC‹ ®ž'æÊ >®­¾C&„ýù“’UD¦€&£Á™}åÐc Õ S^fáÁ#=#–k5EÇC9¹r•¿¤ FÄ,… ^êË›GÅ“¼m¸cS:†ºà•ÞŽÊC-MÝIª` '~uÜ?\f1â S‚Ëx%úÛwCϧŽ%-y¶¡–ÕL/ë…×^Ê%ÿ€±Ùᇖ  üGÄ:!;ñA´‹Ãª©?¨bEFh9Fe¿Õ-þLãžýuC˜©DÃýú˜ì3{ª¯‰5èÏAj×þEÿñ?Æ5@PCùèBè9ºj(q,¨Z' ±5·/P&è!u†)éO/x élYéö'û_%=üsG銚3³¦¥5¼a†oQ=ë˜,J­¿ºÃÿp§3_@ÕHžHåííïsÙ2Gœb;%îŒOvÔñ®EÚêJ®Ýžø$ŽøÈ¥¾ÍÑê˜x>·£IQ±”,–Å=àm;üå9ìgÁI·‚h…>\rô…ø¢{v…iåBAöXuâ—2’ÏHÿ‘Ô^¾Ö'®ŸfÒ"² ýÔ[Ï3)™à½¸nFë<ü·Ü­ÏÕ l~?7樒Û?WâP r¤ŸrwÝj,€Bi‚3ÓZi›ì¥ÛÚm%Ô¡ỏ;ðÜ.3û½}íê<9`<‹~Zƒ:ÍÕW Јt›²2aSfhybÇånЉÍ{ %ÊúZ€õÄÏ3­ðÑ“~%g9Ô‘÷¿÷¥#Û`c(Áa켿œ@D'¡aŠåÔ퀟ŒÊ÷"QL”ì[î!ù1Û§œîåìö˜ë„«-bOX³b*x„L嘲ÎóޝâYÒ,{¦e† ¼Ý¥ŒÄ_f>²d(Ú¨^w…6:iÒy –ï]–!Õ0Bô/yIbþ¤nFó{ŠÑûvü\òhCøÌbQ9Ç_´-Úq§RºÄ)NL ‚‰üFÚ^ýœ‘½¦Ð'Q‹~è²uZ–b}X¬¦tÑRz…îÔãÆ(n}Y±Êæ¹·ééYše#äiÍ3¹½œLDãÁ«–l–x½ פàòÐz 8ÎÞp6ÐSMhJb¡zÖJòy q±Ë¦Gg*!Óùdl¢ö¸$©o¡wÆ'‚wPKº“c%õúJ°¡]Á,^“Íî¥+îÛx¥x(Ø>l8¹úóÜN·'>Î>¤H*bB£5†,,7ùÉí•÷{ Ni²/ÇÜ÷мšPBŽÄø½ ¯ˆkM—“ Óä)mˆ÷Çm– Á.pòËzŸN>6fe«?Kd@õ^ê&Üžà k·v71t SwfI ŒG#¿´ Sp­²CjÜâ"Á:Äéñz戌KNo:rNcfºb‚!£‡Ø«àJÖ“‡ÃÓR‹Òú¥²+ÿ?¤”õûúÄ÷qÉÇ@+•X ZÄ»:ö’§ ²ÈQú\ñ>ì>›Z ·(»»QÓ·­‹ ñzcè/¯Ù?‹q·¯ãR‡ONÆÓ4«gGQš¸èG·¼í3ëTX¨(àtçK¦!½Íþ¦{j½ ÇQK¡¯ðÔÐÀËNBÕ¨~9]šÍÐ,Ì" qSB rXFš:ÍŒŒÝ¶kƃk@&ýŸRj¢uÐl1yå’O&¢P”罨½&¯Äδ‰dÔüJñß°Y[ÅÖqL2ñ]ÇNš:sàü•ç­Òµô,Æ0$bKª ´ÝDx2´áÛ§{ß­Ò#˜ŸŽ•Œušª7“dÆïìp$Š*zóÖVÝêý›S3…Fæ~^XŠ…qg­ù"\öÆAõáœ>gŠ(™íÚÈ1µr#±¾êßܼo%Ó/Ê#6È.8VèGûl±9iW$%ª¬ƒ 3Þn†£¨]*f§ÖE«E#—¿Û‹&án%gFÍ x©Y6OÞmíÜÐôA¦º2Ǽ+°•ºÉç‘·ÕqÊÒÜ›ƒ²ù›7&`}œ¾X´™¡7S·üÜâ ÐRŽÐï¹²É'õ|xÆ&$Š¢¸5 yDʼnf†¦as쟀,Lc:B°†3M*HxSÐ ${æNRŸ–­/‹9"¦ }AMÞÇo•Rù¸Õ‹Ž4óY­y++å &Û^t—.|IÃ<Ù¹b ¦t’¾Zü£¨p€\ Q"?§xP*.Œ_î¢v™…„©XBÅòûhhmYþ$ ÍÞ›dc¼l{Ò ƒeð …y{¹õ>ÎÁg\~æ6ÐÙç}½‰g…^äÔ€óö•ÿ »ø Úö¸$(@Ï¥ºs¾¹Qô§‹ÌØ^ÌàÀòïo2ŠÑÝlÀIZ3´¤Ö¥ ¤mzÌïöîVEÒkY¯51ù_e‰Î­o A¯0gøŠ /ìÑ5Åf‚Û+E§§7ÀdîšO@K·*p³¯05““$~ÇQ$‚W˜e$ #Í„yò,D¡ëÄè0ت†æ =Ø­¥ þƒë ÞÏÚ@Zï´~l§Ý³vܲ7÷¬ÄÓéòk¨Þ‰@Ê:žu–:8ø,H`rö êõuZ¦~f8‚m±4VM¹“ÎíSw&@†c{bÛž_lÛ¶mÛ¶ídbÛ¶mÛ˜`ç?ìÍ^lßWŸÓÕ}ú­ª÷¹´õ4ÕîÓf1[~+”½¢Ó<|ƒ:YˆÁV3¶bÿèN¿jíxÏõÉ”Àvš`fàR|i.ÐCZ %ÞI¶È&5®©æÆc³üÞ¡¦]£W°4µÄªÀõ'B&–ºÞÅfF äç'&-ºÑå]\ ÓÞ£k7¿*˜RUp¸_)®øEQV©–œÇÖW Í…›ÃN2ª—#ðpôÓŠò%ö–fÏëi«nd\ym°&ÄÜØÓ%—ÒšÚx-x6J~+ÕÝ$:¸ÁÜQ!¾ÕH› dƒÈÞ^‹?¥rk# fdJŸ:÷}ß¶! П1-ÏÌ`$Þ¶Àdñ›HìíìZNm®‡ ªF‰±–‰ý«ø0¢hî$Ì)eôkøO|Øz5é±KfêeUµògOËÖNU¡ ž”«¾Ž^–g Raï³ðVl,Œ\CjÇN ’4Ô=¯ÌNP¤ø°px(U9ßVöÅóé&ûö”W¨´çt:úSÀ€*0“ÐïȵB˘A†{ήIØÍØGÈ&+¸áuÿZNp'®›LxëÆŒ¯8¿É ?=ü6¥‘QbdŠ¥õ« ×“‡;ÜWŒ 5°X¦ÀiFáæ·Š˜µMÇ'Óé 9Ÿ7íÇÄ·nM´piçfÆBõÎöû Ízäì}gì4y?‹¦ðwGõ…ò*-ŠûýñS'dl‹kÒAÔçµ#8UÍ@#aà$¬€‚$*¼Û:ì}þDÞ/{|YŒ‰ã¶¶¢LNoÊPz§Ò‚4fèSCµoÇs6›ña1öŒö²ÙÖ.Lè4phGi~"0'”£^– =pcqœ ­9Q„MÓ¶û¹Ò}†,µ¦ûgé+¯‘O6’§Æ €ü4} N>6äoÕ¹?Â=æÞ’WV$³ùÃâÑ×»—±&àCc`¸H½€ilr\³—…èÉ‹¥LÔÌÙØäÿ>ÃÞÑlã­}9? CâeyáYý08úÐÁQ¥2¸.ë.IÙAÍ)‚hšª…<-?k°¹µ0žP6_üeŸýòdlýX où£r¯•Fz"åò÷Jç¼0ìê~8o„1äp=aÞ ¾|*Äßû /è³'š¥<·X’ø®‹U'¿"<Ö"ºp¸Ðx9¬s€…ßh$Ç‘$¤ºª2ïÏÜVp>ÖÑM—”ÕãxN,ù>¹:eÞ>( u!œÒ1wV)鳫ø6JΗKe¢Ÿ#k­´ë:HÔã:֘ í¹Ú´¤§×.ze.w«W²¬͈87€û†™ Ó´ŸPx´ny¡Z”Æg-K\ÞàY›ücLVda~@ ÇÈ’&±G¨õ£BɾòÉ©DFÆÛ1öûUSyœoøwq¥RÍ+P€S|rñå3ÇE®a׆7á^¾SÏÁm5ËÂ-hG·‚˜I®|³˜}[N#¨sØâ³”Ð÷ ÊçH ÄpYí9ãz?ƒs­ï¦ˆÀ—Ž‚8ÁêØÇ^Ü 9_×µ·O#OæóÇîæ vŽÍXHÕD8!X]²0›Y»'éÙŠb!¿W~”ê(êͫκ÷ÇgbÄçžÑâú|)Uå–Ëaï?¯µ¨ÓC£ë•0F“ñÉ!§uo™ÅÝçËÌ®ma”A”8}&ýʵ7\[O™‰G=®Œ¦ ¤"!—&·Þ`¢²‹nJÈØ$~j¬8>Í)ÞÖÃøÎ[vè‘¢¯ô¡LÉÃ}XƒèqzÔ–`ª¢Zÿd$úÓe¯ÓÕLjR(ÉW>›W‚®Eš—.¾*²w7w?¯ºÁ¢³ŽÆOºº¦@ ycÞù=îÕm‚z‰"ÃòDòÖI™Z&ÊHeû#zû aÉXÛ E#‘*ñ“gtØBn­í¯R°Ü!ÙyQ\_øeÔz[††WBÎÀÛ%x?ëØ8éGÍQQ¯O5³’L†…¡®éµZ^Ÿú§?~tôPÈ»9À*¶;òÍwŠ÷r–mÁ?ÂkþÖ—“×Äk:/© Ê•iWk–Ó®{­Êøa~·jåäÝá·\’„¢´&)§ªOJT¸ùÂ3kH¡pÜÀ]§ÜWe¿À ŒkÀ PRj˜¼à© ƒ²/ÆSÐÕÎý듼lQ€*'6•‰€p\þ ä<ª•è’ÎöØ1z ±¢â°ðëF¥A*«…FY¼“/ti‹aõ› Ó&‰×‡;‘ŽÍÐ*TôUû•ñò¸‡]#íXµÛŒî>°Ýñ7%Øâ’&›ð$/!kszâ‡ç~\^ÿ3-úX^¯F»…;L":hÉAª#’4‡Ž[Œáˆ¼”ŽRØù¢âƒ”«ËHeÖ!x­- ~TReà“¤ÐÓ‰ºFÚïˆ6ü* {[IJQf{ ö°Ÿÿ–¯è¢Nô75ô„ÛV+ªIØ6kBS›qw'¿ L°êùõ¤:&±dU–Chè%lÅ qøÁ˜¹/-÷•œHۅؾ¨‰èÓóìvœ‰í¿ŒW^ Emg`kRƒáÙýØ÷޾[°Ê+*=g¿ÌMe O3+_½à9÷/¿HQŒ’PëÑ7›ÎZÛ£F$™Â0œ*U]dAù9Çø’½´&úª+OÕüÝôEÞEZË(]ŽqœÒ‘ņ HbÇø©£ ð³^œ,8³ATèåêÀ’©eÔö0¥˜ƒó"v¨>°Ÿ{ñZ?nêƒÄýy'÷â|»†¿ê»:y˜=óD`sU €WÔ'˜îdPž߸k Íy9B+¹ù9ªtzˆ¯€@=Õ9_<ÙXw{ïâØœSé®Ã¯^˜)W„JdtuR_;ç—SÞ&Ü\\±™;=<ûݾ.²zngF‡‹á¥ŠM KÅï² <’ýÏÈ|w¬èÁSCñ@Ç¢GëŽú{)%äGNš$‚ØÒY¾3Pµ….¬‰Ýè;ëÌ*‡¼73<º­^ÉfìÅÅí‘ýId±>ÿ¸Ë‘þáóÛCüSwç¥w£q›«-1KI9¤êý©Zo=KªRÌ¢ ?€±Ä>näÅ‹¾*é¿g‹ÀFT퇻׈P”2•°ž Vã¶Åò€Øgjãšk+²€?qo >JiÆÉØ è…kîÉš \=»e™¦ÖJâ'@%¤ k¿Œ/`pF=0xk‚3²=c‚eëÉrN;µÈ¦AXÙ‹KH/La‰U#$NH×¾ókØŸfðG5Tn>$Fz1A{ÂÎÇCõ_Úµ¢Ñ¾ÃY'Ðx)Nú~àJ¨Ê*0uÈîîeB?=|¦2nÒÀ¥²©é!¾zÚv^¦Í{IÊÕ×ßúw¾fjq$cŽ/>õ¥¯fá)µö`*P.ZldØkè¿YB¸{×¼÷²0³Ä3„n&Ný€ìÚ(æ6æƒM§ BåÕ=˜`¢0âéó,9b5ó؃8Îô6yNª“zÿζjŸCa$Ã`ÃæÝ3^âÁ| ôQrÏžå9¸<(­úÀ¶ FaƒÑ%®mß:yÎ7lnzïÄŽ×ÌÑD rÁÛ+W›Ö†HÖm]*,íEa¤F“Û‚XƶçþgXäÆ x±Î)¼rE¤)¢Q=`¯IÄ¥&Á”ùv¼ÑïQ«u¹&®?RI‰<Å–%ïá.¨Š,Ú G¨2ðm%Mzl‹éoÀ!ñ»8Òyâx|6‘êÎ3äo*JÐfϨ7þž½ ¤DyÝ*õ§ÁÑþ¸¬ÖJ m䜡3”YžW üT!&žÊ·Ùåðb0„Jš³Â±S²ë?*hjÁ|™E6_ü£!´WºS§1ǃråc}d7ÃÏîñ™ö¡±ê­<îlÜÝòÕ׊“¥ƒ?­ÿìþþ'–ÛÂóx…¬"í êiÉ+ÍJ‰¥7ºEDÑþ,ÎË㞎MYÔæ[‰Š,ÒI«„:Enþ{ mȨŒß=ŒècqÒ'å¹`GfÛªæ§>õÓ5YR¡Í;l<^s®5RÂiÔ°S×ì9Oc‡N¶vÓ®«æXyt8fjâSר‹3(²þ¶”fååúsè͆®BœA Zd‡Å;_0.öËŒÇW:@$¡…m”î\ú„LÈù=Ë ÚÏD|@î„…‰°¶Œ¦•s±V ™¶Çx°!y¼Ic§ ŽNP päniµ’æ9ˈ•+àoù½»Q’URœBW0ôÉs÷`f9[öjçNbW¸ƒôÅ*ÂêˆFjÕzLñà6ÅÊ3²š3ó€3ç¦Ï³­^8µÝÕò.0³¶ÇÊÄá”">„•XØ“”‰1…”L~ÈAöG£kÞ»>O~Ýñgòt½©®Z t}+‡ß4u@>Æú<yèEYìÜYâZOÜF&t±¬úªõ¼B$]ŒôvWÞ£þ ôÚŸ‡îâóÙ‡ÎÂAoE'‚È:¼îƒ É5àŒR¦ãµºK gÆ£[DÿO1šÃ`sÊ3™>Þhùß^.·üx]gzkyüÂ÷óìÁpr‡ð¯ûÁÃR‹ˆ|hàKz—_R¼ bƒ›‘†x9¬‡n¡!ìûjM~†6V#Fð~—Ôþ6¿ƒ¦±ë÷i"}HG×±æè[>Ʀ’5ž5hk0¿™CÈáô64ðXÇågŸçA‹ã}ö*–\†œéè<Ù[W-#ŠÕBøÏHd ¾5hÜ}mi AqH;_Ð]“Ò`ãtØÎV(g|­p[» ›>¿þh­Øª˜™A´sJ'âhaþR1Àa:»ú6nöÕæGñ8#\~¤)XÑh-°'·¯Ùc¬Bõ»üâ©öhM ‹?!C…úÙ ,)8°"ó <¹´w>-æ„È$Ý!ñâ¥лˆNËpýÓq6 EaÐÆ'ž§ÓPǾYêåLW€¬³!ïÄzD_Í/a¯©ô×ÍmÜëžµUÍ1+àæ¹ùy¿f‚ÎËÜûe}ŽPÀ9S;ü=þMý€ðÒ ­ìðDX>y…ãõ«òg:Mò£mYk¯ËÞÕ9\'µeA>ÊYDÔŠÇOc 7ëò¸Åhe Žd© æÕ‹ùb³í}90 i5±y|¦ór¸Çõ¬Q`y§»q2(èÅ•HQXGn}zªç  I¹Ç¹Þºü¬Ì#û†‡ñ tK¾Ûɾ¥a쫽Ü'|y T|1]–s$©åIÜ&CÌ]ßÓ¿…ÒqÛä,ÉÛapÖ>2 ãüæãïöç0W…±–ÇqvT(»vhÖõûì"Ôú𪲠ßó6gXôÕðjoãý+”CH_:gOpÃÇ"ÓÐERX[ž.`Ôã,^>ƒîk6¹K?]C¨ÎóŸ×(˜Â]°4üž-Æ¥|̘yî(9?¼¦ŸÝ%Ïhמ4q”wÃ0'›ñÚv¦Tj=ƒŽ}%]ÂÄ9×_=š»JÌ m¼‚2¨[ÖÞ8¿ïñÚþÁ0x(T n N¡[” À´ }`ÁãY™¢œ¤eˆÇ*¶Ó•YdRû¢Å4¡°ßsJÉÒ?(+?E 0ÍåÈÙÑ*Ó÷uKÈ9ą̀­AÇM®wéxñà¶¶:ð-VxT±^Ô=x¾˜=uª'º›‚,,t©@b°¨„ãD`Xk,GLm êúÑaÕlØ s÷ßRb0¼{ 8m¾H$K”¼Öíû…˜Â ¶l'Â]³YÝŽñâòj'””'ÿZw«Ç”ÆTœ’ Ñl&5£½{×Û€t=޶ÀÕ2”X2œˆ¤4ÊcÍĪBl+wáþ¯îµFIL,Iªà: ÄóÇ3ÀW/…ÝiZY€"ÙTʸgNõ³:K(£Žw®BÙ!û¼,w<èïKޝ¤Š”° z8ðl‡(ÄS%Dð¼bU˜÷¡bl©“¿ÿà¢o¯&û°’KˆäR³*©g·Ñ-RØ\Úà™õªBf¹“dÈáéÏ‚)ÆÙ­¼núks’S^S¶nžàŒÞ“}¡Nïcýîï+ŽÕ‘hÇl~pª Í—½Ùï$ Ñ?ØfÙ¤6ÞÉ÷ÓÛ›ó< á°^b“S—ïyÄgÌ«ãß4Ê“•øKê;|l$Q®u'®Â…‘6[@v–}¤ôR¦®œ§Š<]ñùªÈIýµ,â¸ôO 7Båôø½²¼ú8Vt)ÓI*ð3§²Ž/‹pÌcïØçÆõ/–³ƒé~)K qȶ™™ÁΕTýýÁ=œÜ[[·–vH˜›úË„¤9±ÍË:Ì2NYbì^¹hu–.!êy³Š%¢Á… 6_/hÅ-©WoiC±ú$€b——o)Uœˆ´NZµšV?Ìô,Ò袋öG!\µ²¢Å^LÝ ähØü.™gÓéœ ¸ϯ£d,ýÁm/Hçíj̳ªÚ8¥=žž—e/áé7"`iA÷/…FA—¤y×䦪™€§ìÎtiu÷é»ÁüÕ¯Œºö¢î˜¸¢”ðƶ¹éùõ†â*‘£8 5Q¯D°DþÃAùá ŠÂS F¥+ÖÅû :Gué´rµ»ˆŒîdCm|:·_±Ÿ•µ ^ߘžÄ(’áØ´–Æ>·F®×þ\†oü €°Ršd%îy×!5gw‚šáÂN±Ô¨×àî^sÙd€6n÷OÑÊtªY‚¸9(ºEÏg—G$B£(š®õ¢„¾l 9çà»Æ˜!òg¼î¤äîž)ª3#£S”X’VÞ^ˆLëqšÂ_Î0VãzEêsxáÀØpNP¾>ò€[š Â9 G÷“.‡k2#e»˜,áp©trŸŸv? ÌÞÕiaÛØë‡fGê=b“]猑A¹£Ê4‘¾Æ€ªðS™ß,fœ¼Îü!5n ˜¶|S.ÞÀUܰ¶mFôÍQÖiÛnC…ýô¬í mȵ͎©°0s%–b%vO*Îã\‡P ‰<÷ Ñ |•+%âd§‡A«G˜Œ:0sÀ}”©óþLèVY9ÀTIˆ ¹ W”°´¤G¹^­jæ…7xå5=K‡ê»8‰\·ÁýÝÌέLÀôðBÁ´mQͲŠÆn¯`³—”¶€â aÍ¥U‘ÐÏGúÔEÓx¤`6= .¥á¿J ½§]p5C`Céò‚+1ñ¥[¬L7Ì2*‘DrIÚëŸãù3=YzuÌ|R„Š)·â7Qš3!@rTÁwåvšãËFßÈ‚­Î¨qøuÅ[ƒ,ñŠJ\>'fT›¤m qNúÈxÕ/‰Iã ùJr€ÜDÒ2m‰ræ‡(z GC1È–íw2cšzø\í[’r™?ðÏ‚5€ïmM²¿0áYJãÁ3Y·›·-ÁÔó¨<ÓÏÐ+¢ñ=Pó$B” PUÆcz´ÀM*A„íÐMQz(ä²U‚€¤ ‘ä]ÅbŒJHñ©Ï5­,ˆ+µW©¿˜}íÂV~!µÐ7‡ê­Â ¸$úN¯Ø€ÏªÃ¿6M:gÖžk`µ¡ £™Äö)‘ÊÜš÷ð[—z+ÚªK'‹(¾qA’†õ=´^˜Žù¦7 †»¹`] 3ƒ:åß!ÑY’›Û¸v,P‰bRøJ÷ºÆ*`sdððÇx¤>[6šLTå-AvÀuù~‡)„ë¢aCÞjÔŒc‘ó œ”Ý ¿TÓ ÏÒ7<Ô›ßÃì: Aó ¹âÎ.Ó‘B' táÐ Ù¦‹kn-ÆóDlsÿqL B•‰òþ1ÂrŽ£Ó‰gDÌêc²J¼ºÜÓ׿’äGÀü‘6ߤwƒKêmìýFkó÷ø,`ù§4¬õOkˆhï€àÖ­ãZeM´b‹1u5³†Ñª‰U›|Eª‰SÄt·wY¦¢Ï¬(þïšñáM  šÅííèŒõ´¤®tJêò0†wkNÁ5ºd¬K÷–?ì%ÏXœn@¯:†’ÐÝdZj¶)šÌwÙ¸Ad¦;кCI9õï‡Ò>!CàOAåÀ}4]â:¡[/xbfâ[íLœ=ò‚ÁE—ëÚ÷”´ýc1Åâ³…êç9Ô. -j †R‹ âð€ƒ[š¨”Ò€És8½ô߸!Ä v0<Œ@¥z"Sk8ä¼È·Le^ö4{‹’ê{#Ò;E°™èù¹ÆõÉÞAÒ uÉËŒÅ2»g­-Zú¢|"6ÜiÉ Ñ’×"ÄÒð¼¥HÎqÙÁñ̘ß?Èì>e/÷­ØÔà W;)rän|œÕáh‚ws©Ùœ)ƒ ™±¯°‚„kVw87SƆ!º^æ¶Ø?¸m_Q’û{5É5KÂòvY¶Ol ¶?¶úÚfàú­b“3ËΦ,éà !ÔN¿Â®pâ·ÌÛQ9Ŧbç%¿¶zã¶ )ËÌÅÞÍAŽ ^¼o¾Ž4ÃÌ8ƒ>ÁÆÍ„7+ƒ¶‚bŒ8…Fu&s‘Äd¼XâÈÁïªâÃ"øÏD‡Ï‡¿£YÕI¬p‡À,$ÚäÍ&z00ĤZáÛqw¹ƒÅ‘]þ ³vÏ÷ŠÍ¼×,Ô6—ÿbíì³L^]ÎòÝQézyõ¼ArQ™ž n5—¦ ƒrv)ÊÌ)eñ:˜Qeýƒ1éHŸ!ïÕ®cá“øaoš›ÈãnIZlÀ"›Ì°™Â £øVësÖ¿åìóJ=ýHƒÏ#®ZÀÎö2®Iu¥u¦|Î S´±–ûKBd’/MMKï…t딄&¼ø}ÔŽŒœ­¢Ô9BFl_¾6l³B'§¹¶e1Õ¤K€qW‰/¬@µ t¤E²sÎÅ}¬) †%öh%+Âv9‡ ®ôN/Ú*ŽÛ¼Ìa->ºü&M3 Ñ> üí{BPÈŠîA0+Óà^kI¯TbšX“œ&î»{Ô­—ô¡,ÀÑoÁv¨#º—ÔJà Vꬿ€Þ´”J¶:a¡±„»ÎP-[h϶ídüuŠü«—øòž÷]ðØØßñÛùúŽïH| !Úø¯r»t‰ëQÀ‡jC}ä=œÃKFKï’³Æô~hâ"D¯x¦àqÃö¬J§U y/Öä/é²{>¼F»è §rfÕht2XíMô]ÒÞ€¥<-và¨E$rú2ÈÈÐÈ:"ë« ™Ï*üL‰²ÌJÍàfôWpàz«Uè ¤ºãõ÷ãçKž”X›'܇pÎçj0&ã0‘Õ /…<®"«‘¾Ø;þ©gÞ,›h8ìàUÈ+µ¢‘êLÃ*v2là’kþ¯+;­“CiË„9u¦dN·Húã96=ÁoQžßÌ>à?}“w1ZCj£$`âi íMs«>½H‹?·QÏ$¶ý62+sköëË—ÚÆãÅ–rê¼rA7ÎuÇ\ÐU_‰Yk/ÓŽíÙç*xOæœÆ%‰G9˜v”ŠêÃË/D©!Ì‚xN‡zz7#û)´ªtÊV”q7=? %~2!òDoZp^ƒ~S6& ³Œ­2–0S¡èT¢ÙÏ]ù©£ã¿ýÃÝÙž’âz%´«Qÿ–ÇDPÒÅH¬c¿Mþ‘–—ÈŒÎ/xlÖ´W¥å7á&®@ŸYkΉ(óÑpYœƒ k]Á²L§….µ+\é&õ]¿[ºW~TÒìÎãÿþëè®ÇÁRýK~¸2’¿ƒ«uš‹mp@àqÓãD†xiä§ RïbUQWGAóñA§(å ¸.?ÜOÍe´‘gmnZÆhŒÙùÐóu͆9Í}ü;@öOS{boÍ_è͹‰?mÓ9…ƘÞriŒ™Ö97ËŠÊ'à#š‡l“ÝOÇ«¯™séïn”º*­âß•ieJûUæ«4•±G?¦ ¦.D@:vê_a0°Ëý*æ2B-Œ%äµÜè¤V°9ßádAê.¶è t¤¨v>aç}÷ffS³´gò4Çñg~XîOkuNE_Ú©‡,U>Ÿ™Çîã-¼O-¤Nùú…uª.À™œ\¶DÂàÎ]c´¯b}Ùò(ϤøÅ},ÛN¦Ã°+ÇÈÑòúÎ|€óÙ`ü1¬¤V¤ìÅåa|jciH(M&•MS´kªÄ1žStçn«==Ú(õ§Õ ÷¾‚ÃÓñ,áí±Nv1À8"Â0î5×$‰$~²^'Öð×ïú(äÁîw»`¦;žŒ^<ŒÛÙ˜þá}Ü45Ð…VÈð cæÍ¼¥Ö!N^¥ÖIÑdéúÉ5À ‡e´NO°°ë¡•Pw¬a­qœ\B¿†cò¨þ©¸îªìÝpêLÀÐÃü U0,“uCî\ž}ã`¢DEªSRÖ:Ü §©Ùj¿vû×x—.¹5¦j0ÁýNש£—JâˆåcòVgYM^;ÎYÛ“oE_ù¾RhêCD ?«QyYqb_ “ØuäIôàÁL“ˆÝ®©õ?d‰›ó'åXx3}%Yès w_FÒŒàÓ0AÜJ4`£+¤v¬8„­=d iº&e´ nîë0W9Ù¹Û‘VÌÃi+ݲéÌõv,É!¼”WüÉÅ×’Ž®;Îîà-mŠþ„Â¹Ø SÌÿ® ŒÍñá–¯7.ž~…›äÛöÍ׿& ŠÚ?†ãO?ÍÅ›~õŸxÒtÔ0¾#€JkYõF¬‹2ÝqW²¶ÉVÌŒ˜ O§^ƒoEm@äÄÓ™³-ïvØãõ½G†ŒïQPubúZýÈš¸®3Q„š´ &ˆcVp+©‡k¦b‚‹'øÈ\¦ðOœ‚tc'¨Ó¬Â žZ™ÙaM9’ðLŒ–C%F8Rá˜þ‚¥UØ¢`¤¶ÿÚi¨l-ª#ž³cùúlCÊ—Õ/þî¡”f€Àœ_¿¹%âÎ$u£´Ší³ÚC’çØRß-­èäK—»þxŠ2|fÃN)ks/Úô¸207¬ô¨› ç‰%¤Œ“òo¡Ë§·Ê– Ö$M?,!ÕI£w¹Ÿ¦ØÉ*@Š@†Uj&aà²Qž´=åÈS]¦o)Þ‚èý ë¶Oz­ÈCj^XÝŽêää~9¶ƒzzýÀfá#†×Äÿã`>*#Ѓ×_ν¢åØøôÚ(Gúî—;^„ ÁÏô1ûP ATDHÊ¡' ;çƒFïŒóî º®P$(Wö|›$=1 Pʯò$PÕfð³bɹãî"ë\w¬¨yƒîÔ–/ŸºMLk¸þM:nö4ezÅÅ9Ù‚´{šÉ-TaëXàwVJ›·M̈dšL›}ì¢>G²¡çÌ”Û[Ä{~2jãM¸ Ý¢”S‘Ç tc¨TD¥ªð!dªìž4°åÙÕ+ÕªŸs<¼Wd“÷wÀ´‘0òLÙ—^õ ûi@I»üé‚ýðqƒÖ?*C w`€"ÚUeë¢õf¾$Ñ RX6$Ý÷ìP3J&6sÕƒ$ÕàÜV¾Õª£'ÃjlÚNT½sq|p‰÷rËÉ-Ðîì#÷§©‹r½Æ®uÏnË_Ún<ú¯Ð[Hp·Êz»÷¸r|Òõqh‘A²a}ý2˜9|U>õC“в¼‰(0o¨_90[È”Gô1p*PÒÎꯟ©·ìÂv$£x¬hª|(c¤è­þtɨ*¥„xÊ>ò«ÑD]ŽÉ“©ò´Ø\ˆÓÆ£¸Ž´XjL[U¨¸àJŒ(ý#ÇÜ3³zhn '3kÜÞ%„V§zZÒ4`ɺ³VlÙÐ"÷Jr 4äžÃY^ãqqquú_ÊûúZÆj|´ºÆ0‹þT¢ô¾±²Ñ`MŒ:K¢3!,ÏP‚§2h›pŽóã##jÜÐ’—N­e鮳Ÿ†è¼‹JÈÅÞð7‰rîs¸©«ÂUH?²³ŒÅßûegÖÁ¤)ËÁ#Gùá°T kÜáCf„þùõ=l½bƒÕ§”Ñßåý¥w’É~ÝV\e4ÖNåÃî Sãv$ŠLÖ¢^íúç-âZ…›ÝNñ¿ lÁS¤FÇï\ò4óð»>²ÇcÔ™JÃÞpР° ¥Iÿ=ã…L;ï'éKƒ…ZvÊÎÏ=–út¿=ÕóýÖñÛlðœÂ“ò¹,î¦ù pw¿-6imçŽ ðTß@;áûlýÎ498 ( ½pÎ?ÞV‚Qh+Ž'£v¨ØŸ~Èd˜s\8:—U8ÅórÐQÛS»¶îb3fÂE*ñ©Õ+ ‹cA:DŒ u+‰N±ôCˆVWË>ó»#ÀŠÓ±ŸsDûYeõúK_³g%'hîvœ6°¸ì2'¿a؆º.OÖíΈSuÎMHˆ‹ Ù0£¸ª6RÊ =àù{€¢,mŽ:?kòµÌBÛ;cá?àÁ¸¸!¯E)|!Ûýƒ‰Ç®Ñ]¢²9íd+¨V-ܵhSÌí˹ӳÈf1(–ðiìéýÂïÝ夃>ôËd¬è*È¡®2 ¦ÐðZüå ­õB×>"ælí®Óò7˜û«Ç[ˆovõ‘j½Ry²?ñ~UŒÊ]ÀàÆò†*M'V/MXÁ3šbÕ.ƒÊÛ¸9Ñ™ ¾€õˆ" ¨B3uï5Asvá¡X!¤½Rï7U‘4ËyRìÍ j‰ˆ*Ú­ L¸¥Ð Í` !*@ýãî¦ù’ 9F¥GõÖÑH¥UËØï~‰ó³¸fÈÇØñú1ƒjyÛ#Ab©Ó.‡k JqÕÈ´ ¸l€8,Á¦tFDï2ÃÃcØLðTE‡‰=yüÏ6p`~#m“RÝ?¹é0ªÊüˆÍáôÐZ;ÎȬBzƒ-ÔQHE ¿TÌ—¦J*ɉ͒âiƲ4ð…ôdÎ~þëk" pá|@{sŒ1 ñºxR Êš%Ysåá”`eö¨(þä!;DÚ8ACÙ= – \´d°Ï%oŒ< ¾67¸1Òà¹ö¬uš¡_4uãõbÌJºùÆ‘*ÕWÓ4í uæ?^øˆA~}@n³ ôÝß48§ƒ"B‚ƒ“*ÙD&o·XD¶ k7¾J™Y¯¡•5îJa#öàÀf"ÿô.×H˜FÞ &¡× YB ÀÞ¼6ŠC‰%If¼¬i™?m¿…dºÈ%SÖFTÀc*–Bþ@AœAA’_ËXàI‰õ‹ Qp]nwJZøÞ‹Og1Œ+&1ª«ïÌ.A´eåñmT«åúÏ2ãwÛ4¼N¬]ƒ5•2¡…Ûˆ¬  Zü¹/Äó¯á¡c]ˆÈÃ!¥IfÉ…©Üà-@ li¦ÙN³UßÒ¥#žGè=6uíÖ¦ï <ŽQㆼ‡8éq†É©¸x<›ÊBNîç”úßÖ}’\i‹ c@Wǘ‰B•ƒñ$œŒ¦ÿ³PAê°ÞÿôëÓ—›ŽsäH!ƒ¹b›bmü@ý¦†'…Ö™bl€æ#N¾«puÍï1ÜÊܘ=øDtÄ&<©í‡¶4ˆwY>/4M½²Ž¿Å¾)ß‚Êþé祘$±‡„Q\°Vz4æˆ^¯^ïAf¤Âì-ûJ@ŸEnw/Ô]¬ò•V¿5øoäùÚk›Tv+r‡£ýqÿœfûJû£r]îÿû4.(qZu:šwkÞ©¶ -oÝ€þÒúèðˆZ0ôŽ«8½AŒ\HÛrG’)<3‰ž¦<ÌÃJŒõ¼ü±×í[0]*†RŠêäÜ"(|€Ÿ;~7Ͻ@§eˆÒ`Í—Yò¦G¶§Û$ž[”…%ò{iØ…Lm‡h®t¶ÿ©"®Ü¬Õ¦ôˆ4Ùâ9á M†ó,ɸ˜·²Þ3÷ãd†FÅ#íïœJ¡Ÿ!Íj[=Âøó<"¹D©èWªä~=r£–h¡âÐΑ¬3ù¥çƒl$'D²†OcÁ·;и¯†JñÝQ&Ü õº>ym™ƒ#€O‘ýYwÍtü¯$ƒKªÂȯÂÙGCèÝŽ;io}ÒáßçŒ-RÐyõµ`uzíæf'·Ïšn´J AHé¾H“ïa‡î{¿¨^y•·À‚1|xx ^œÒRôî'dßjÐ!ZÁTÈtŒ°}lÛi¾:Pó(ªOϰWL/²¸5­Ššwå…?¤xc×U²õÏ£û™Î$Ìž*"ÍÙ–o¼k}æ_eòC±’|8¡ÌëD̼û†JY\|´Ë¤Ý©ª€¯‘¥Ñ¸È™QiØ^$ÁºlÇ+VÚœ@aåÓp‹o=‹ë%¿$Ú8ÑÓ’{ÁžM¤+Ú¡È¥Ýn†þ¢Õ½·Bâ„Â`& ´û7?GÆŠ²ÊêwØ¡dùχ¯Am}ÌZt¿ä/sµýD²áÉ¥x†˜âL×aTÕÒS]xbб =QH¹À!%ã¹`þËxÆÿoÀø¿#kGg;G+˜ÿzìI¹endstream endobj 4 0 obj<>endobj 5 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600]endobj 6 0 obj<>stream x¬¹c@¥íÛ/œíšŒ•m{2&Û^Ù­lM5a²&“]“mÛÆd»Ýý´÷óîOû}¾çßác­ëº(I•TEÌL€’ö FV&>€˜ƒ«³ЙQÔÁÖ ðIáD ¤sƒ¬ìÅA@>€&Ð 4°±Xyyy(?u=­,,AuMZzz†ÿ¢ü#0ñüΧ¦‹•…=€êóà´up´Úƒ>!þŸU@È0·²Ä•´e¤4R ê) =ÐÙØ äjbke ³2Ú»iæ΀OÂ?€©ƒ½™Õ?¡¹0}:!â0¸8M­>Õ€¦@ÇX G ³•‹Ëç`å°p6¶}æä°²7µu5ûÇOºùg?A>%ì>yŸ`J. Sg+GàÓª’¸ä¿ù ²4ýcÛÅê“ p0ÿ”4s0uý'ŸXÆÿÀ|rAÆVö.Ðã“è0̬\m=?m‚9:[ýË W+{‹ÿò€à ´0v6³º¸|Â|bÿ“ÿŠóÕÿˆÞØÑÑÖó_Úÿ’úO¬@.@[s&V¶O›¦ OÛVöÌÿt‰Œ½¹€•åßèf®ŽÿÁs:ÿ+A4ÿô í§Æfö¶ž3 9³‚è3ášÿ·*3þÇŠü?Pâÿ‘ÿ”÷ÿ_qÿ{þkdiþ·ãÿÓ<ÿwhIW[[c»Ïø÷íøg»ü¤Œí¬l=ÿ¹ÿ.¢ ü·-óS—öµˆ½Åç`aúlÐÏÊEÒÊh¦d2µ˜Û~Žü¿èêöf@g[+{àç€þkþŒ¬,ÿ®óŸ<5K+SûÏ9pþ hoößûŸ‰ø—ÇÌÿÇÞü— ¥Ïñ©y:~ºóoF5å>wÅ¿_þÑuðx3r°Ù¸y\<¬nVßÿ‹Á°þÌ?šòÆ g+€îg ,¬ÿ ÷Ÿÿ›þƒ‘°7u0ûgØUAÆöfŸâ? ÿž+'W Œøg¬l,¼\ÿ²eêêìü¹“þµ>ƒGø÷û¿v-è4E8ކ_4Êïa¸Â{Z³J'ëÕÕå<³ÎãL´Si3H¤XX¹2ÿa•ªwÞÈŽ±{¢¼ï»†:¾à­_- ?£ëÃû §ß0ÒÙ¥RX7aT$í'kõQ‡xê½Ùצç$Ì üz>›™ËŠ(k-aÛ†aŠËSbSÎĵ ´¯ß¹' z²ÐfÚÓé¾#këûaÁGL.->ª|d¬yhGYaƒéŠhAçÚýÀ²&U]©ÔéO—`Ó`­µ é]-Sä;G® …uõáÙ&ú‘yNrvºÒ´— ÐáÏ5•¸A˜4Æ3Šë;~a¢ïù¸ÈF™ªûN‰>ln~q’³" á³-÷É |o­ðrè*\Œ¼+d<â·É¦õ)«¸f F?iËT¬Up§?:¿C# #%AÑ! bd#E²‘£/÷éé´ d.ˆtò9˜}h›e)eõLP}[-í„kV{ò‘üeKARÌÜÝØhq%R°—ËŠö‘~Ñd€¶ÛÁã«!2¨0VyŸÚäײU¶‚«´§*K§G”(‡K_gL‡WÛ~°–{ÐvfxÍòñµw» Ñ‹ê”vò+‹æ\5­†2 êV§²ðO{ÒÙ'¹‹3O3$þzÁ8Ýg¾û›8º²ÝÌ9VWÓª]ãsäñ Ÿ–aµ…S¾h2âÈ÷0?|]Gknçœúþ…Ú)%ÙðG0QpN-Q|ù:Óœ¼-?ÿòïâÉ,+hÅi!!®;ÊsâX–Ùü>›rIy å%ž­í ežÊoÖá#­ì‚.>ÙÔ=BFÕëÌlÚÇ&fí73T§žÉ-~ã ••S½Zç­ÕT [Ž¢cQ‚ø“;uF…©¶¯¾â’‘z:ò‹ôEB.1#ß>¾+he»ü¡pñúÖªŽ/2¬0›q!¿™eXIìÍË@…ÞDWftµZ§DÜxö:æÿ¶@Š)š•n :ÝݽÇâ<ÂeTþâI4›>‡DîP/cϺÒM¸±­³’]²ì+8AÎT48˜¸%ã‘ ”\ļ ì¸hlBÜz“ 3•ëÚY…ACôʃËÛ ÚK$yê2_œ²qD—߯Øîã' ò…»”öúCY»<ðH+:åsGnμaG—¶MBŠáaôõº*FQ%åâ…—¹Ì|{W3‚à•{’:~vpCû–÷«S›œZUnlÝ ×a ü@£äÕÕÂj–´r)²Q"eièK7kÎs|JýºªöãሓdÑyÅ!Y5å4bö”.Óçž4äü\ Rÿl´ï–ËðÂÀŒý¿hæ\:FÁøÆ…àÐý&íæƒ"ó´ýݨBäªØrÀHÙLëÞ ×€X¿ÖÃc ¥<µØ­\°Š“~2ƒ[Ãt«Uù™‘N^ÚäRÍæoÅSÇ ao–ßkn£êœÍ…–Àã­^Árà@~*r’ðWLhK¶¢êR‡ð0¼ãÇ´ú¸5Öü‹åÁÆGÛ‚ˆàß ¦˜HÚÇ\²Úntwê¡qNK+Ê•qÓM(©è#® ¼”¨00LKtp¹Ç•_Ô+)脆à‰F÷¼hÏè%y2uÚ#/*¶f.Êo´K8P>­ªAZõ[½~|ðºy«ÏÂѱå|.åm¥ q”€ç3`a t Óžâï ÊÍ~Hý¹òì÷ž”lbG€ÈœÆnóC Ý¥MG%cHªS[”O·ÐæûA­Ÿ²“ÔpH&h([dgŒ>%̓—†Ô‡î*Äàpš1G3u‘7|fÐoÏi‘¹ù"e›Sà‚Š};T‡é:ƒGzÚQJÌ“Mº¹Yaך½öýðh¯‹&EÝÑd—±8 Méš)#ëÇÈ‹ÄbÕ‡’í-»fYhQÄ­hÓ8/Ú­êÏt=•cIÞsygždx–Gj}E¸›¤pŸÛñÆÝ€R~h¼'ö_„iü C#Ñ7+ïÀȪàÊT”*¾­fuÍÊeE!Ê£&šc…•Ah^¨Ÿ5(¶.P)1}…³ð<ܯ¬ÕÂmÉ‹g²»]‰‘Ô] þ Cð*ɽØLë'Ô¥i~®änqê:?4 Ô˜¿®îm®£øáÿIb´¸ ™"ƒ÷R- 2½ÿ=îQܨnžNT -p;èKF€(›Dº«&ùa>8ò~Ûˆ~®çYlƒ”˜·3äX‡Ê§=ÓÏœtÿ×Oç¼G¬‹@iZ;·g½ø~C= s5ùˆØ•ÍŒ Žã©Fp)Ç:.‘Ÿ«ju± Ï8pó,c[%•i²ÿO®²Ày ZâÍ,‰ëhf^ëÃai¸á|ܤh`%{%мâZÛ·àÚAáfÞ#dÐ+²»«Qj…k!ªßê¬Ëõd3ÓÑsëAÚmk_’zõ ïœ Ÿa[Çйթ( Yÿ—¼;÷¶V •‰ Ëã½¼²]!S$P›]_/‘Ö6£pÒ8{ðÜ/ï߇¹\‰Ö ÅEÁ¥ ©P=ˆïŽ T#Ëò`=—BCK"Ñø}Ÿ{ãÔ«›ª Iv[vxüPOkçD>L$óz²Li°Å{T±t6¶ª©×, à!ÿÇ*´RÖ$±H&”žÆo¾Ø;j07þø™#{„|®è 9X´s½kp.}ÁØ Ç¸r2•ïY ¯lHRoϵ¬¡óµ¦X`¥uA7Eýa…¾±¶ú-Ä^Z©ç(•ªµ‘­Ý[ƒv5 &ù þÝ•%$¯, ) õ6lÛ—†j(t׋ó²î­YÊì='¬‹‘PøÅzŒÖ^ÛyœôÆàÊØbFWD »Ç$'ª`hJz]d„>ʱ\¿# PŽ8ÆQ7 t‘%Ë ¬/¹R¡*ÝĦ7Þ\G=´B¾‡…$’))È*‚Ëí­ØÏÿd¦~_݇»H+†û¹kšÚæú€AŽçCgº³G"”$:Éô³¸ž‰rÁÙÀ‰·{2ŸP@mHõrÛ—Y¥Y»ÃÏmKkî,N/zÎé|†nþb, ÇÚíªµékA3¥ÿk@1±ñhtS«ðWc1B¦Ö›öŸn+‡VlK<‰`CGVמ1­Â«ØÜÁ¿1Òé¾È*-°µæùPDc†T8x»Ã ã–±¨$ܘùPJ»:×ånc¥éÀ{.Ó…K}̹Ðë°>Ôs¤@©Kz F”öìŒËÀ¼vôAþu.¬2yšÖÀ‹ô"oOüpÙ­1¿qÑi7‰Õòlo2h­’Dµïª“ð‡ Åa6îótÏSa­0HÌœÇr¤´ü)¤¿%«7›w®Â®}­­æ¤®ýLª˜¡ì=Ö]Ñ¥áßÓ&ì8vÑp(vÙø9ÆÄ$Rz°.ñ’†i&N6 0ˆ6WצèT…«q §à†ù—¶Õ­Cxæužìªü@çÑ6aùÅŒž„k$þnTÂÁ,ç©éAÖ~B ¦ÜÞödÊü:1¯XÄæ7Œ¹/U“òA0Þí _ØF7#¢^ýØAp`f^à•W/yþ€ÿˆ+ï¡¡Sî ª¯ÏPÿQ >#c×Úc®ºÚ$O Q¢ù—ê´jô4¡ð'”4/ŽQ´9fY=æì ÓÌ/KÁÌSOê·„’Qä&œDd`QÞ¨Õ¡nîrÂwDá:%V Xkºž,Ë´‘R („«íßàÉE^V¤§;—#{¸·«®¢Ÿ=9©¾ÆÚ\"ßF°B˜è° k™db! ¤ÿ|K€ÇØëè Y^²÷ûR$÷ŽË—øH ÿXÛT¥jÿ\y'/ÒúÕ  %ø]pqntðúdå¶y¡O…­éÆU`y’{KP)ôn4EOS‡[VÚÒ•Œ‹Á˜»¿Oöçmo¹Fk(‰Áö¶ßN¬þ–aê»86§|“ã-LR® ‘Ó   %H¿¯»)zO¿P™£ ¤R‚j3[YJ,^˜³ e‡5ÑÒ0;:Œ‘{y„ûU)¤0ˆh,®cYV€÷·©¡ìÞÒ¼xuËZæø@دZÄ«“­œ¬š~‰º %"”ݰÒÒ¾j$™¦uÆ÷Zm]íÈÍÅöïDï¶Ã¬"ã}Õ`éM¶N?¸YæµÝJ0#<ñÖƒó2^&«A§V,0jñõš6Âé'ëiwü,ý =m;~8€Î¿m|@>˜ Ûü½Ñµz2JiA;ý¼ûš¯»œJ>Ä“ŽázÕ2´œÀ?ïzŠ;˜Cmjà <Ë#¤ušVûi!A‹ƒMÙ C!'”„–^KÛ±+áy`ºoÑ0ø Ïçßp76×øVÔ å{p¯@ÇN|ÙÑèDÄN÷ÑZÚú·±te|0 ÕÀÌÐÞØ“±¤ä'ȱž#kùj ¯9 ›Àž[¥ógÐW|o©W„æÔM¿š‡âM˜lK³fŠÔÂB&}ú]ϱ½ ü³Í€ L³zëê«2oÏgB )ô‘ƒ—k8Ê®•:­U3 ßéÓ¯r®çœ·t&m„hÂ>©qŠ>Ú¤Åjqµ’·ýJ5/¹X.N¨Â¢ÐßÈZDÓévã2ñ¶LÕ©Ù] º“2‰rœ`^ר,yxèÈŸ¦ Ëf³(j³RDÍÐbZl°˜´³ á w9÷‚#ž¨ºÊ4M”Ñå@â£'—¢µ3At¨Ë\„_CV'R‚ù`ú\ßõÁµcð­à›Ð{Ý êÿ¨Zka«çÖö´$˜RpL–\xyÊ¢ÄØ¨ÎL¤²F ¯£üaUËvô­m«¿ŸhSFkñý@Oä £pt¡q4¾Uxs‘=DcH‡‡‡Ým$ÈœQñ)fý²ûx A·e)^Ì:mZEËð“å45Œó'žSì˜;HÉÑ3)°©Tå·^‚†gVÎ5x»Út]Îm!"òB–5•h“II¸#kº¥BVY:Œ,ÿÌ6þÜÚ&_гfcWðÜñí½ÇÅ¡¼¬x8µÂrÀ¹ý+,5\ç¢üD3W7ÇÞ\fúQJè¥?ÍsÖû%C‘Ù8jvÑ~'‘DnK‡«á þžnÃ6L[‡%ÓHë 2”¡¨ùÁÒ*&Bi×f%+¥BÓWÊt®ùç2S„cÿÚ ŒAƒ€U ,°Âjû[…Û0޼üÁìj?ë‡Å¡jìà0K¤±(+à;ô( šð>Ôˆäð‹FK4îöž³å¯§Mvtmš²2%·ƒ¢mfF9Ýlp—úH Qü¸“‹>N)ºËÙqâ»öý™ ‰Á>Ðip× ¹_g2a_:ŸZÞljˆ»|âí œœ<õOkØðËÜ—QWçEDXÊØÂ×^èç#×É_ʸ¡­½åDDN]‘—qÕVXààò³t·Ó-ÙÌòP¹ ÔD‰ÍlÄ 8æÅëUs¦¡~鑽¸ýãøúªØt›¢]4I½n¨v±9eT¾Æ>5ý8MˆaÏŠÃ’/E )x2N8MÇa¸Ôü[a,÷}«àáÛF2ÜJxó”þÔÀRm>y¿Ääo˜}fÝ÷¯;:nP/ô·¿èïU|ú®ž}ÿdÙ¯ ð é.+çÔß´SH±Y à=L;Z%>/·eßJ›G(@œµ'!±ƒéV]¹ý ì)/ÙÂ00õ²Un3«"ó·öñëPxæÕÎýÂl.3$yÞ‰•‚g°¥ ¯ ŠÀç]d&®U ò4]˜Ð‚J¨+/oAÇM·q9E-o|¹\OcÞÆßàárŸ¢Ž/4 åá× °?@ÇX` à ªÚƒš7I³T)„}®/êü°³ÝL!òàä­WF×1Š|ò­ÊrÉL°»ø¢ÅÜMf:ØL¦«ZƒÞJñâO‘Ö¹4”ôTÓУr9!¦ø1ymeæ¢î|&ê†Mûm•ÆÄµsb×g=ßjT×LåêœeœÅuó+ž¼ª]«è’f®Ú@“µ«ÄbÊ,´¶çxmQ_üšW;õ^‰=I8Yg+ÙÕe6±a\†šeCÚø·®;%4V@õÝŒ‹¸4/в­†G™>­U“{mʬ+ã'tCS]ü÷¿`&Ú¤µö&·©‰Ymªª5±zlŸãÚ“§Ló‰(Uù)!`NÉ)½¥Bo/1™…Å«{šùE×ÃUÎý¹Ý“ìS¥8ï¦]ûòü⃠ö>Ê=y–Igévdck¨$ë ßÃßåƒáEHìë ñÛ,^Õ†§ŸJ¡4¿àQp»ÔK/è¶Wtë|Ñ&àŠjOiÒ(I¢ýÁÅû¥ÞþÇ[%yhBï›VVŸ{óÓàö©ŽÒè ßü·îQ[ã>8-âîEBx¸×L¢R 87ÜãÎf#ò•§â$…d‰²¤É ßxîqĵmRj~üº7l~IÛß…tceoë¼Tü8hµó˜C[¿ÚŒu°8ž¨Þë„Ùd¤£%‹0_„Át€XUçÖ~c 0Pé8Ç‘^Ͼf½#êánA˜8°ž8êJMàë©:‘öê²èå ¨¢yø6¯æŒP-ú€¡3óÏN ’¼6#ã–ÖÞS¸Õk7=eRã’SPQ²¥½ñ|ÿ %ÝŠál;Rïu3>€Ú˜¹ÄBÆp/——ø|ÿ£jYñÏ;‰OT]Ä៮èö› T!S`5ç·÷ìä#ÃʺUHÊ ø-—£¸h¬ê)•®#Û“.Î3l?s/¤!M.*¢îé2,ª‘ßRO,nÁ_«zºQ ',šp–IA&ÐyA5¶o&§%M‹`õ“¸4­\ »°Ç׈Z2òÇÇ7õÖì¹sF–(Ô¨äÿÓz§ðÖ®õ¹Ž->M¿¸­“Ö¥°ÊC9ZÓÎä"Çô"›@‘qÈDú}Ík##{=8² Y]öçqúØßAÖnQcÜ3ÚD3?‰ÁØZ÷Ãjhdfžöv»|úQ)®á‡'ëlŸH„<'!h„°öÖøV–ˆéVŸðJŸü£ûI¢ûÓ‘ÿ°æv}%iéÞµwÈd—\IžµF–Jñ3שÔÃÖä­%³)L…-ØW<Ž%Ê|¾c½ §åfDëQœQÉ·Aíöa€¹wä•2Æ8â~;õ‡ÌDOC"ïó¹lùpz –ðÞ Ï·V[^ÛC ¦ :¿mLmÃÑÒ©l¨gL+”,íGÔ ósó°û3Pè_ [8Ô› üc‡&%š\ÊÃþ¾¿±"wnÂWwàìâ}y€<¤-õùÈÍ%[.G|Öò-ò.3T¦Žáƒ)Vy´Ó‰dFö #ÎKgàw6z1÷*µ²0¯ñ>¿Òµ8£5¶äÁQB4æ Ô"ŸG©5´äâ[[?’mÔ€ ¶:tóÔ/u"sUŠëáäLmØÚ/Gæú™/7 ™ÆQð¢Î{·¢§ÝúçÚÌÆÀ¡yþBMJ‡KY’õù^8yb«¥ëX6Bvˆ,šì¼½`½áíK±½iÓ` w»«AmhSDôaœÿ²qCy&šOˆèû¦©O˜|†ç× '¤3žéçjóµ ˺ç&Æ y9Ûœrb¶yY·‚€–avö™î¾‹«ÿlÿªgµ*ЉÛðû ÝÍßÍæ‡ r$,ô¸¤š¸ä‡©ð/qXc´+'Ÿ÷—Î êžn?¼öÄäð:TDEar@0ë»=ü7Å_·Î±w‰DW©áANgÕn-6 ¤o°#"àßoGå…‰gÆlqhðz%ï¤Óº'é7®6Á«fŸgxÐ8ç±§/€àÐGðÔ%ãJc´ZX(Ó'óíÛ¶Øp…AþWíæ…èÕâbˆ)dÞÆ8~±O4kO=«Ð¤Øü;ô†Ï7,Ã]¼ebv@…òÉûoÖ ºQƒÏè3£€_€{÷ò€h)ÙK(âfTD{SC6ÆùôD «4º-Ob:¸6äñ|sQ<):4 [«¯ ¢¨‚#£Åmí–Ò¯#ù67 :Pqœ³A‹Ì ¶ëúë«\±êrPVò¼[†tD§à`ãC×ϼҦªÜË0«¬².Ùv«MÒSÒû)Ú]ûm gÁ˜³•åwäjqnIF à·&´&ÆáĽ¼§= —óùEÖ³„ÒfX0ª½k·Kå„“ö9²fT⫇>“Yûjö§Å ÓxÚkl¹]ÙY2 ’µolãÔv€+ŒÞfžƒ£ tAhÔ‰žfªÜF4>’UÙ3Gèïeˆ\›?7ëÁlÎ{ª]Ç›­gÛ4*6˜MóÓìÚlÜ Ü[¡°˜@_âñˆË6 ¬0[­* ’&쟗¨Ó+áµ; ’ETÒZ©/‡í>:u˜:AÑÏºÓØW•·°ú„ß ²ZÀ¡;±--­V̈èëë7jÌݰ[½¯`˜Cyä°\qu9˜£Ñ&x~{@:¢(Å‚ùº’Zvs§'vnäašuérß#~…vE²Sq¦uÈà߇[RòŽ™è¼!|Å>7p¼n3•vza<Š d›¸g¸§(ËÒÀÝI8»Ú4—Ø1:GÁYŠ~#Ë‘>Är¥JUœÞª´Zè>5›¶nï%vѹµr+ûéôŠDú:j¼…ÄZÓÚz*º¿ÄPY×1“4Hû‰©‡7ÉôÎx¦i}¿âªUÖ™”Ÿ…4ó¼m‚QyØn nÖïllŸC:I—sÑñCð´á‰/@µ½øÖù3µ[}§áðÆù{]Â9òàb&½}„¢@LJ‹¯26/2Â|¶ö|XYÔ Ù„ký-OLÞëÎlJšbM¶çކQ—=Ó<.¢·,¤=p—ËÆ©Ï1fľÒtNAƒ‹‰r—Ø”˜¦„T¬µü67gæ¶Ç 4uÜæäFÿF®©_Zž¡9Žâ#ö]I¦÷åäݬ–oAC5)È—ï 8s[ôî?~:Wë²” M¦œ$š¯0¤;NÌy¬}§¯UkZN³ñ¯ôÂðg ÍNÐß®ÒñzIm°´uRZ6Ïb¶h3j{ã_D+º¾œ£Ôíí Íá1ÿ9†žvhÅ€ØÓóžnÕѽ½Õ(@&›bs.¼óßg7ôžY”0¹$`®ëu•¡¹ikbOž>Pì{½*ôžQfÁê«Ï¾ÜÃf t©À¢¬Ú„È+clDã1æÜ—T?C¯|{x%’ìa“‡ÿZ´9eîÛ×ÜTþ¸¶ºqW“K@Üw ,}+…õ*ì 3õ.;âÃÝšò‘dÞ.g“–“÷¤}di¼XŒ¨%GoÄÂσïINíê¶ä¡“ hñ³ýY5@þ¸ãîÀÁiÍÖuh,Ô)C0YVÑçvÊÔÕÏÄ\ ™”ç/»^ÊÛä·IÓª0²ûƒ äÇ„ºäÿ³—Æ–°ÑÝÛ’€Š²Öª.ÚCwU&ïªÚ{°ŽOݪ ͰÅ|,§¨ãÈ@è˜èÉŠPÇÍ4 ¶uyåšN<ý"Wæ_À?°`µdÛ¤Ÿg8HÑ16#•füŒŽ%_½@µË|Röžý@)Ç@3Æ·×Íó~­$&°À‚:ì7ƒ‚(ÍôÌC凄WQœ4‘s#ÏÚØì¹²›Ö $1^ú`@Ü®Ýãˆðc ¶ùÅÊ/)z@«X˜gŽ\Iåüõèm· !ú9©L·˜EÚ‚º¶^ 1ÑDCZï|<7#3«Í2H%åw¾Wþ~G__J~7Ê÷¶¶ùƒá$Ä[½šôl½rÅ?lޏEöeØØ¿h¸êõŒA3ì’{cÝz޳Ýé\>~QËî*p¬,LÚ6 Fñ=¿p®(ìåÁ¿Ê ¹š½Z«ê ¡ß~8‰xIt¤Ûe~8ý–á<Ìd9ú¹{àq Új…ÓVi|# öøöÔŠsF\=:×ᬩ?Òi7TJ®¨Ö ›©=ßL<±óö‡eù&U :èZÇ9ä;ÑÎ8hItÊ´oánÊÝ'í-í®Ýë¯ìψ”ÆÊBYvÛK-†{5³˜h#Œݹ:²w÷#ªœ9èË“òã¥WEs”sx줛_? ï_rçuÙyð5@R±6ÇôôêœÂËËmQ“ÄÈ,c¹׳êÌ¿]{ŽâihÑV…¹u,ײåŠe•©EúŠTjÉW)GhgœÏ(ðå˜ÙÝÒ¨²i‰è$ÑôT7öx㜻ñsÜ¥¿~Q9mψ,?¨_¡:|2aWßcq yþ¥jîÕè Cj }®†­??&Ι³ÿ.Xc…å(Fµñt¼‹Lßy@z3޳ ÌÁÉøåYž²ÝiHñVtÆžÈ^U Ç+Û1/Cpü‚9În<> ¹~ïûùïÁßKSreÒ‰ˆ[÷ï5Á•u;'îír¤žÅVzì0Ps˯‘eàF)Wr¢kvzêX¼(+›]¨H æ^!eùòwS•cèj—ê´Fí&4X4Ï_«t ‹H³ù±ÏÍf pFlCñL—ú'?®Y×_“Qæ7Ïž45»³ƒÆü…èì°Êjf3cíZ/â7æñÔG.XG‚éX:¾3gŒ%?ò½ëÌKù¼o]’H‘®{Üé>`|¶­è_xôÄ HFdšB”k@sígXÜš¨ü$̈…ãuÍoAIÍzç³Ü>#¬g@o™8à*ÃúÅ,¿Ê-šÓú >î3Õ5SK¥Þ01> êÀ•7¢ÏÜê q³¤HmþBÖÐå˜ê’eÎ7»Üep;£.nxùa ïÔ Æ‹e&Hž”³sÅÕÅd8Ì<·õ [ŽA(h(d†ï¹ÍôNwŠÏc>ººÃb䎛Ý&ŽJeî·Ü„èC@KoQЗôþ…¸üô²–D…¡a¨&.ßùB=øÀA§H_À» ÓuþW»Úzúfx“,}ñ·°UÚßWUy°ôZ#=›lÙ:þBë¶¢Á™5Ž£/¯šWÎu¸ÚaX$”¾Èv±øIïh³˜Í]!6]~ó²›D\^´]*µØð¤Äûn²ßszFlQ¯VZëãÄ"ٌ§ís?ØRÆ…uô.x¤›8Î;ÊH)ý®üT´-Y¤ƒñvC°mµ)ýª%dï“ ÂŠ5Òõ‚¶—¯þ,‰yÆÖ×£›ùMÞJÇvþ ð¥5ª¼£+I¤ò Þ7-ð«ò>§MÜ…?’)ÛcêÔ£NVC»·‡ýŒ?Åâ_5Üÿ¨ôàE^ö 6vÍ|áM.Óc'ú•ï@ >`h k›TúÙ»d`‰ôèœMÞ¹UC€jDâêyŸùëtjX%]™LÊÞÝìnG¤ÙvëÐÊ’kÈΡHµ*B€­fO˜¹lzUÇa» _ž"HRô…WŠpm‘Óuv‹â²ýêçÝÅtGí‘æ# ¤Il,n–["¡5Qú<¥aQ#R¢T‡â61o™+Ô x’Þ§YŠ`ªéïœðÚ5ß>¶iŒPBþ°*†oú¦¸¹Ÿ1¦Àèlð¬ëúnp:}Ø3¢´¼‘ÓúC© (ëÀ#­³¨† ?’uø‡MÔûºMÑþ‡ï¸Œ•hÓQ/; G¡ -u^}ó×½ìýh~1Éð[unbÊ ÎfKçm…MÅ»t͉Ôw ¼B'â'.Ô“œ[$#«i]]2E¶à²ÿ\ ô-ߢ<\umÇÅŸºYð•Gß<š#ˆjJ…‰Ô —®€•gøÍ/-ˆ!_d ŠÒ˜Ï¸@eéY` ò_9ù7ÃΚ„ŒYä@Vzóü<~:ljàAÃ;û‡î#†Ž¿Î½áb¼½¹„IC‘²Ãù¥– | .hÓ–?åùaÿ6ã@Œ9¦Y>îÿÃÓ4KÇÅ÷{D{ÛeÞŽŸÿÚÓmàO’„sr@ü±çUø¥¾™ ~öˆDû-;‰aäjoÈU×ß‹à’ ÿÜïs˜‹/^™4¸çûùÐ…^/¦º?ïM qF!‘iÿNgìd,³AÊ Á“ ȯós :x”¬APv£¿–Òà†7K¡a$iJ•g?R¸9 ÿ`’M_÷—¬¡} îa)ÎÛÖ¢.t"oº½_M–¸ê0Ù„¤J…d 6—½ØêÏZ‰o£Ü³DÚA‹ryär"ÙÐ+Às¼$f"°í·#ÞeKKs árŠ^ÑŸ²½þÕákÃÇãauå}Ÿ |nÑ-BãN%-Í¡Ž[ͯ½R›æo§ˆæO´ÚoË~‹¶êFápw TÿUì¯Ôí¼Ë¸èµa`*ùù‹lìãûÕ5)ˆP.óðÜ)ƒŒÇHÈ+c?éwåôHOǯ¶2·æé|ÞUètÄ;È^ÙÁ î}ÞPôë#ÏlUØÿéyÿ`Eý©a_vØ£è{ÄWsF$úH˜0é;­ðqžÒ!yN 6¶?  õkþG9w-×xñÊ{²Ÿsѯ°ûÌÆãh¶a·'ñPWI9ì[.ó q·Ĉ¡€ßÝÚ‡'ÉäÃ}r•q+W4{»\{zns›‰fEÖ]Î×ÞLyc¦¾ÖYt3‘#+ ú£¢†i9MMÞôž}w‰Ý[3²xɤµ¤8ÁcÝõk+ui“$K¦§{tÜô`‘Àr}³ÈÀð0/mÏiì8ƒž!®¾Ù¢,þاOFµX˜g'Tp íÇǪw’Mmn½P̘?@Òx›ÊÎk’Kqj˜@âeÎk½«¤LޫųhôocPI“l é¶ÒÔ`&¾ž‘²žK\Ôùa\š«œXF „L-ÃË…«“¢w†=Æ´V&{rʭ؆‡×8Ï™s’¡¶„$ÕšY1Tkê˜õ5Æ&;ÿ†y@éßD÷>5Êún é…|ñ%à[EóÓÝÀ‹ãß7a™ÂZÜŽØ•Q¯FJNþyKIœW´ÂypX­…œ-EAű¦¤Ò¡Ðjš™ìà*è*ìh%‚ÆÅBÎ¥5LÇ"½ž0»ÞÂ:tål·h$\A%„“IY‚ÉÍÙ›ÉÅ?ª¿‘Û+A·Žy‚˜Nò$~‹$E•¼UO¨òn»¤«bxš1ü- :—_y"£FW““›ŠÙ¸CY}æÍÜK¡) ÛÄ'Jõà= u²#¾•£Wð«KdÖ—»™îTè¿ztì1…{1¹5 çj2IÇiÿaF H¡'Þ+E\Ë~dSÑùI!Û;¡°íš6‡6˜—7oX?h”-v¾Qå“<×h‹ã·GÉ,zVPÅk„ÊôMæ Ð{ì­Kô¾J:¡ß—ª—@Èjþ‚e'ï–@Êbá=l¿Dú¬ÙÕžO¡¸—{ž¿qïykðtï‡Ö°1>»×1”~§ÚömÌNžµy1ÙÏH»¦¿“`KÞT8¦¹ï.ƒ~«Ò˯5 u=ð.DäÔ©W#äʽülBé†$"®ùb!½ÎƒB¼ s›LŸB®Œ÷,Û 0æ oÖÊV¿kêÏÑ×õd¢Ysêy^ÅLž/!®œ:1Òÿºä¯ŒÁöA,,Ïbp:(ÃFåé·ÞÅȃ.ÉýIX39¿©Üf½ÞhT¾yÑi4AÆí›:‘ŽÁ™ï d_VþÐ'¸kEy\8„Ú>[ãë/ê>ÈoEn®*=ˆ~õÃ~¼ëÊ,j“,ø7• ë¨IÑ¢tZ–ÇÄ5еI ñÂoÀ¼•ôðgáçŠÝEö§óY /ŒËÁ¸©ìæ)ª­ô ¶‘Ž¡ôŸ7±!ïè8IÊ1Cå£Y´2³ý!Øë¸ÞßÑnü´³¦3Í"Y°@Ò+9Hßhòï× ©Ð¢ªË‚o_NWõ׋ó*­Àõ›`¤Ð§-Í™W¼?õi;˜Í$†iœàÇ;I BbÍì7[ßÁm¡Ø¬r¥ÔŽt %"~•ÿs'wºpÍ”v|Ú9þÀLŸvNU°÷bÕhNÇw%Ô+G Õl)›38rˆ®*mÌöÃ@Òe®ÝvJ;׺si[“Þš¯ì„ $¥XWêëÿ‚íÁï{Ýfu;ß‹O0Œ [-s?d ÙÔªÕ•É¡ç&ëËÈU±8>õT»²ëÑR½‘¨îªL¦ëÇh™9ÌQ|-Ø*ÿi'_~ÌntA/{•`®ÊQ_ë‡#S.ü×B.¡Ò:§Ü–‚·é8ÿ3?JìaƒÎËÞ‰ø~È´íàïgB»éÒµ+ñJ˜£Ý÷J¸"G-_ò9rÊ›Jééw«Ð6¸G ‚„¨={«áª º„ÖNCüDè<üÓ‡ä´À¢QDÌ$묕†p4©$£³yÓqÅð#gÑ? -œùò Û€ˆ8\¦­¬wZ܆ HIR›9çT‡ïsÙ| ‰¡òíP¾ã.¬¦¨Á€§Þ®»î¨R¤R ïnèüvÙ[Þ×/‘饨SGJ5JÁêà*d: dUYúg—<×?Ðó U1¢•¾}“Å2Ç9†ÎÏu(“Ö·Ö”)†ñ:d¼@¨(s¨Ùýc>µ‹à]Çw«u}šÕˆKí—ùwÇ]¹èî¹ûK⽑^‡itzj¹-¡°-+e¡o¿²Öj^»<×{BhìÂkæ×~u>ó˜ë…»zÌhžªvMm@vVË8á9jÇ„Ýa]¼]‡s0O&HÙ«b$ì&ùÐÉ—í’Ç'vã+…/h‡±r$ÈpWOrõ»k´o\ejbç'ÇÉœ·„ nðo¶} `*Ø)º¼ÉKÄÞ6z”ŸjÔúÔ°Õ?´º¨9Æ5åFs±B#~ÖÇï¤ÝPåtvmáí4.Ä[ņ؉9¼¾-tK8D,ªË¶ ¡7`.­³=ðG2rma!S`—cª)JÂCÿÅ1û©ØK²dÀè>d€K¬öOp¤}ª ŒR%@ÌØåX°Î‡TÍa¢8O,– Ɖ›•Ÿ‚bÛÄš“­½eä\9+“üÌ™ÑàÆB‘L_yû,ŽÁpøî©…£‹<ê^WpÖ¬Þ¼¶²2–‚P{xtöïÓIºï3ÓÉ/¿#T?JÛ3´4nec¥S-„65t©`Ýl¬ÏGÈ4ldÆ,P5úJmÏŽ™°! úöTáã­Ï3×¢:ó G÷ÜÐ&Ú&ru¸-Ï9bÆÖ?¹ÛÂ×°Ê»ÔSØ[6|œÕQrPdë÷¼ß+-,ßóvìTªsQƒ?DÆiñ§¤Hàè<Û\·c,ìŠnj ­woføWLŠ9®™E×/FÒ¥£(o´Òza(mŒàß'ÊG4½äÝ`^»ú¾øÐ€»ÿäå'NÍÆÆ±?Ç·2Lö{÷ƒ:÷I°[÷a´pøhÑ9POŒš7NѨü4¾lò'€œAdÿK‹ ûd*|ßH}Tâ?ÃQ‹‚^âRv¡][oY|üum·ñ^ÇÈ@À?‡_V‹€zÔëÚbô”%“¾-×B§¿Ò çw;G"®TЧ–Í~ݨ´Ò†s‹ûbÒF.Gã~â£aÏ‚SUß8ò‚GP¼Ypì![¯&È f2º1š9w~*ÐAZ”߬c°»Ž îÁÄœõ"}ÿ©Î© Š0˱¿k›‹™ûÝ.ˆE­Ü€„A¦¶Û¶¶÷A:Ó¿·PèÅ]÷ùÖ÷ŽÈz)£‡rÓ&xçm;Å1a¾äðެäëœ p8ÌkÖ1ùEšèÂÏ,K–ðÉŇÓ÷9°µ·ŽsÿoR‡! u™rÇT›Ô¼†ˆÿl¦Y+–ÚGëpuì_3’}JñîΑ0 [ܪ8“v0òÐu£µöÃþw«^y°ot4f¥ã{»á~ÅÑ©­žøN¨ñƒ©‘\øì*ÀAØžK”oe<êžÂ;ž1û7ÝUNΞg™ßaÌÒl¨àÕaÚÓ†ŠéÿÖë,ÊÞÊÛèP€w'í)⟻8`s™Ú‰-óN .×ñïÙµOóúñy®&l¢ W6ìÒAz¤é¬DfÖü0âk3T·§°cÙ@|¾Š3ùÍ¡æÇ¿ìÜø*4äïôñ®;§¨R¤*t°PnÐG-„Œ—Yb»©:}³sÉ(WÜŠ½ÜÊ¥öŽÇ¦ØÎeïQãl•Q3¯|tÜ?¦Ç3„µ„"»T"ÃÂ$yL0B7ÕåI"±û¦q…œòܸ۱¨S‘ÀÀ1`ˆcöB§Ó!*]ñäy¤´ žÇ±vrïÆ £Q²Å V»ì…‰ÆD›ogHã^PA»Š.5¼Dä$‡ä¬ú‡°"ì5{*ð¬§9жUQSUEúýÞÚmÑ4¯_ë¯4sФú-XL¬W©ýÕ3†µ'îP3¢ î}ê!Øxí§-[øö¨%PYj×qfß^!"P+×¾“m¥µÃóÖŽÌ­ˆ Xõ‹jÓØFM »óÈ)½ŠÑ‹óŒê ˜‹¦@j6ÇÅÕï—è–xïOTQcŒÝ ÕðÄØÚg"‘‹p²‚¹1º¶ý‘XÉ~>ž€$Å‘{ß ßr¢p*òÙ’__xƨÈôvb+Nˆ¨¸ð –ª˜:è£gaoçßôg´NjϨ,Gäɸ2jMhI#UhTþEÐsmNÿaîØ´zCÑîË–´4z¹ÓGÊ`å÷lºà6 Å`Ù]“’RíÒÃ÷!}%RT|ó=svy-áÆ Ç†ç§XÁõ4Ì6çIwÒøÎÿ%nµÊÝ$SŸÓÄT®MΓ›·¥DD€°}Ö»òñ_Ľ„ ¡cB ã:hÿj¡¹ËMc”=ÿˆ‹¹šêíï™;ãê—[Ê.? ä²”å›ìx|„¹VO­D<‹ߣ@(;¹|I0à—ͰmSœé»Rû, $YÂ<¦ WO}AÚøn¤lPènì C!í«d'z ˜Ã%àIs½%ÜIØæC°QMþ’~ è¥Hú4ÞÅ(q)Oy“Ó;˼"ÿñ=,îßQ—‹ý7ò:4½þÀFÉ,fDzÌ%‘cl÷ECÄÒ`Ÿ$òÍ­jÉÏeó| Q™–ä“ù"çÄÛ¾–x•FD”í³å¶¾Ée§¢±£zˆó%ïßÊÈÎxoû±w)’Awé¡ìöˆ™/[MYŠªB?ð gT’‹Šô–ÑwwÔ PÆ=¤|Ã.,þÓ¹©ÈWóÖ‹ýy'Óõø¸úÉ#vÙq„ÛÁ× ºyZÙÚ°Ù™¦ðÅ·§ÕßEWÅðy{×€.t‚ýu¸ÃûŒ]5U¸qà—©¹M?k'(.Á„I^Ö§e”w^›ŠÄd}QæoÖSìÆøœL$ $Š?ðNüåVâFd¸s-R"ZËÔb¥:vè¹EvsäE… ᫆¤¾8$/KÐ7o‡…. c•ç‡2”¬M eоfÕ™ÓÚ?Ýð‹jÑÿÍð‡ žñ™NŸ]ŠÁ;jfPGØòZzŸVöû•bexhS,U‚†æn‚²Ÿ«6½;=’;J¯äj׊x7ü¾R&äaÕLÍ’urªÉW š|„Ë šœhYÚà2& 0ö>÷hEÊ4å1ð©ÿ/<@ÿ¿Œ™ÞÄ6g‚ÛS\³FðËšŒ5²–Rçc0v(~ѽŽä@Xo,'óÁ|®ž±;Õ!©$Å.Øùåá"±YúrG¯Aš#xê ¥rs–7*}m}S= 9ÎÉ¢|k¬ÉKøë¦ ¡ƒ°œ£ÊA¦Â`n )óìa*üÙå0&€ÑüŽ¢mƒU1ñWéÔS É|¬)ÐÞ;¸y—³×¢"°°úé~b3üG×±”ærÌQcÄ.눣³£ù¥Ÿ‚Ìrž']µ6¸Í•ÏÙ‚h„®kž—ttz ð9§|zÈ4¹Ò:÷–A%PRMô‹¬Š$ãÇœFJ%?ã2£Áƒ™û¬ôâ Æo,Ÿ‰•zb‹5pÇsë|{tP(‚Zú°[ ‰¥˜Îë4¡ô ÌaàRp¾'žPí‘ó ê¸ ðSµ6i3nïE ]ƒUòÅ,¤fÍ BOÀN=­n¦kóóª¼Æ<Õ¤yzv˜¤†êõ"E –Ö-' W²;«óz€‚¿›¸¼‚±ŸÙ„Ù¬#¬sŠðæ$€†S4ðçu­³aã0Û—W¶rá"ªg.¤‰éóu>•cÖuºçy |§F€B ¾ór3š^5Jîþ v܉bßû´C5¥¿ÄKè¨ë}  ø[1q¦›-Bÿ\ø¨‰¨1àÿRÚôO%÷àFưk±°ww†RÜö…RSšAˆO†Û‘.W×Äàm}È¡Îe¶(9œ²õÝ”¥¡TŒVS£>A+SØ2Nm³ñ]ø>…ËL_-߈åtŸ>ú(ù">1Œ ƒ‹/’÷ò%Nd›{xu~”mö]ðLGç;×õÌWrÁ%?gïT5‡­Ùò{a5Æ4Éwaäƒ@wæY¶àùÓzøU`ÏNª@ ‚ ‡·/3~²ú;Œ?o!ÂË” ©úX’CÓ'•Z®ÚÙ×κ9›¡Qø¯c»8 ¼õåœ+¬^¸¤SÈë¨jæÈFÒù(‡Š”¼œuÙQËI´¤ÈÔq»s&˜L~ ­ÏDªËõ˜:TÙy› )Œ$!fÄG¸.uRJa]! 3–[L÷g `Îæ¯äÑ(¤áU `|ª€ “Åf‹áÕýT¹Ì].¦ÂÙ(5ä¾>ÕÄ„¹Hª³Ê9oLºµ¶Ò”=zQ­ß}ɾÇîO•+Ë ›- ±­Vº™™K­&ëÚš¢bklØÆ·Ê}Ény…פœ~éÐ5»Ý?ø›Û-YÙŸÔEÉg¯11•£Ï›½âB#8w?\´»÷û7#;Ó˜£ŸÍûˈ¬°€ÍáêÛ€ñQã±úçhGɘzûMôe4Eô} ä~Ä(\[ø(ú;¢ã£>¾´„ôþ¦ªÙ'Ac¦¯ñy& i™ÛrR"±¼Ñî0öÒâU™µ6ܱ ç°ÂƒòT<·iW&tG~ ±Ñì_Xæ¨ê‘à^Ú0ðönÖó§¾qCÍަd›ÏÙÛ" ^Œ&LͰÞжæ)¬L@C(ið¾C jßp¸õnÕ(Wú Œ¢¿ž ˜œJTÇI‹ï[b{ð´-]Ì?Ž®Í92•gi6|r @CÖKhª™b‚îâ–ÇôA¢&íY±f᪙m$¬¦^΀¶té}o~9*Ö¿Y:´óŠ; l Ì1(ØG„Xs½´)0C¢)RÚ·˜5-rÎþøÿà,¨ö Ó¨«#Ša‚ùøìŒÊú× |ä~ósÀÅ]{®›!‡÷¨ÀçÌ‚Þ_&×6=•=ᇥp˜ ã{¾­n€$aRõ̆[)Q+–zD}˜ùqôÄÂŒ)yÐ|°æì@)ÜdªãÛW6!&ΠΣêDNA SéåñÙÕ"ªæú4ƒËÕECÍÒ?9Ôáæhe¬¦$$9Í›M>ÑýµÀu+^dâäƶM#ÈäZñ´X–ì‡|-‡øOJ…Ф±ÑSe Fid˜ˆl.°Ií×ó¶võ#Eúnù«JA€fàŠSóªû_»uÑ,úÇÙÝ«ºÆD”JDioiÑ0ã'šäWÜ™eâ}ΊG–m«]èáÒf·üÖîòØ À)ú`Ý2€„úø®;kÐõp…[üU~L}I1ºm.J«¥'àИ3L£ŒšxíÝÀ†Í»¸å¬t a@ö}t–T¯éMÚžÓ¥\MY4ìÂrwûÖ!««|–™ÁˆÉ{ÄmѪÖO ô,%`{U¸…DEZ¨r’27%QoqÏË\r´ïß(¾â˜"Í»êL¯†':‚ÓðPáBáึ(ãO(ô=W÷IFÛ³âÏþÑ;u„WTh¡M s¹ÚC:اÿãƒHÎm•ö%ÈÈr‡ŒFØ| éÍ=,B"Ô[µM>¿ë0å<úgµ*%’\ïÑy_xß;«Ú+êæ²Á™WQÃ4úÞË¥˜U¾½×`4‡Eqö‚æxPYË@F­ ¬˜Õ¡åfŠ/ÇüÊ;‘XuüÐÕ)¦5ÖL³C‡Í”}<Ÿ=2Äë@¦0Ãáø½Š¢ 5ÛÃݲiÂbp$L"L:“ºCP1lfK,ç; ê×Ó›ÄnðLƒp>‹tæ #,A *}àZþ”ÚÚ‹æ ¸~ïŒeÕ‹Foö—ÖFf ?4(NcB² %¦¥V«LŒB¡ÿg]•Zu/$yÎ>פøDˆÓŸb.å:Tà -N`ƒ'ÔS8¨ÖpvBÇn²\kcsôö{ÖûÁ¨m8ûÑD…ǘJw÷ ø\õÑÒ;òSREay¤½X‰AàçÓfPüài‹°¼ø1Ó`o>s í ‡¨Ó(,Ì_õøðÜqÄà÷Ü[ŠŽ·%à›i*Úæy? ÖÜÙ/^ÃqŸ a·át—wf‘ ] ¯@&NU5è÷·{‰»Ýì^MÇýT¸; ô_§å–F“SA½8r±ÆK‚G,ÉVÕ­k]ù@ˆOetÐ0N[Ì(‡ZŠé~óþ4Ž.6úFª\½=Ø’î‹V9”¡OPúÔ`NFÂq ¼ÿ%ãÑzœ v—ã±D§ 9±ûéÌ©¨Øc,òÂ…Õ«M -Î{‰¯IÑ¢(uËz¦ØÀ;TN‡+¨­äaÉëÿ‡FA»{žhÑ:ÜAâàŽy°Ü0Õ—eJG¦Kj3;™”Ô+v~D¡§~RAÜZ”d‡+“ß1‡Æ'E ´lM,ÐB„µç7G4DNí9‡GÚé(¸±ó š³hLçØ'¦£ßIçaôÅž>w¡¸¦ 5›ðvèŠmØ:YlTŸ¯Òâ]ÄNÄX¯^&¤Å;~¯œÇ_‡­‹Ž^˜H<®~†…8×EˆÈpxÅ‚7ì—ÛþŠ«„ˆ„ËÚ\‡kC€ï³W4!x!xyþöZ!—œ¯ø0gYØ´uÃß×PtÁ…ä"-ëxÊ.{eã×¼xŽ«Ò§EQżÒÐv•vsȳŒSËB^ä&áÙˆÄrno»˜Ž¢‹ÚÊQV92©Ë&ê®{•4âÒPƒ7ò¿¥tPJOzyF~—QàRXbˆ²à•Ä ÎwMRêßÛóð”×Ñ_=½¯?ÝñÔJQÔpìg#ø+Àèt½\_0ö^kg_ú³Íñã¿j"n>ÏÒµ…9_¿»åˆbß.RáH7”ÄLeH `yñ¹˜¿1w-µË¸Êö“"SsØ‘ášOæ,ø¤0•a'J2š;ë¨õ‰¿öËÅ¥Ö-,à8Ó¶Kï– A…yoPlšÛôw8Çl0ìÊa BMjÕíF¾Ñ2 f\H@µi/škÐù£e¯%¥Áë&±; ®¾# ÈÛuìÖˆ!›}³¬ôå]PBäsDe¦ø»f÷0ò‚Aó™Sê<¤¯“9[¾ŸZ2!~ÔÌáÅ õ"»'IuîØ1 ö¦v(m°sŸ]žú¨=8ÍìmöìQ3²¼n\ˆ`_«Ïë„-áÒ|È‘8wJE³Â±t¤$™Y«9Ö7*ÝÒ) ÿp¥ü<Ê¿¡f8G÷>>ø55aà7lÔóŽRZîœü¨bÈÌ ÅH¨F’‹Pä¡¥¯§Ê·ÝT Yê©0¿ÿiÍg)MG Û }ÿ(zìL•õNλïE®hà{ æÉfo²5Z—ê¯]wG‡ÕysN9Â" Vuo} yçÇ(Ø®ÓN(´úíµäK(ÓÞöóQÔ«ä +‡ŠGG±­K´:”Œbã½m<£,Ž%R«AhŽØ8ÉiÛèC_3I©n/ kœ}±٣ÕyjfN¿ aUå —j•ÆÚJ€Ê|®nFo´ÎNËÉÏÌŸFó‰FK¢QH†þðU1tÚy a/óÛA…W”Už§3Ìðùdbm£)²Ie$­7Y_Ò§†šÓRBÐó9V ¸+2äÛy(U­ð(¥|`Ìç2>p$?éËÄV9%Áæ“’’Ÿ¯l;QnÁõ_ÖTE•Ñê•)ZÌp’Aã[iXlU_Ýútj&vOÑþÏcÄòZu²d’ü\w=…%:û£Ò¥¹ D….æ¶‚~oqD½ïìzh²2½H5eÓ£èav²Ýð¤/Ép d<¦i8{„+¥ ™¾ØwÒ¢¿!¶žšÊþi¦„ôú)I¹²Ždý³xàænçVqB–œ\¹/é¡[ \5Jí÷¶. i$‹m>~ï{&Òeua¤Ú\³Ò|©¾Œ(># S¼™’êX:9¦)©MÏÚ{úGÈR£­¢Ö1¢÷MÑš–¢=a§>r©±.¾EóÀQ÷¡Q}_Ô< Å–ËÀ˜‘‘úŠËý—@•.hê̽×9kkk9•—oØ¥qå%>èÏÃõ#Ý"=Ô®bé2&Ø4yÖCR×¥s¼:X¢OÁ :Ì^½-4¤@±ÝwZ´ÝAY&ìÕ©mæ 7Ïg*©B ÿO“Žåq¹ šCa;'Xo@ ¿”Qa6:õ%¢C>í0„#Ò{Ö$`w®dÓ†%D?Ñ>x‘_ˆâ„ V‹ž* Ù÷P¹.µ´Š`ÑÙ®"{¤|¶þI±ÅÿZy}æÈE„eÍ3X»ÛµOÖ&<ºÆ—¬8tßâSfð§o¬tÂI.RO·CûaLÄŸ‚¿YþåB¦Þ4Ã>½ShÁKw½r°¥y/ PQ˜8„ãFÓÓ¨ÚG¥ãA5c¬u m-c:\«kðôÄáÚUUpymׯoO¼ÞïçuDÕ‰ÌLÂWüCwÌN4(¨tûÍŒ-Þ5Y¥ëY¦q’¼©íIÿÄ›`Z¬¡”ê\QÅì¼³§ ?¿M+—@úvwíuõŒ{“¥5¿Cú{ikl;øü‰ôåîØ #›Ñœ•4’,'†ÂùÿÒáà‰na‘ŠLJØø·ž)|ò¾®Èzû¬©Q]ɇâÝÜ@òZpÒ©= bnna„¢Ïè_†c Ò7ÑR•ÃÅo{Õ¢OY¨¼®€›–ëïßlâÅ‚;Rõ§j߸jf“Ì,¶T­ Ï«¦Ž#8õÎvü\u…~C¬ °$ÌQF)N ÇS^¥ÂñjüuöbÔGZ/ÃA£È.ãD9Ãå)¹k-å•JBÿ0ìwê£×Â>M¥Ð¥;í«ÖädWªb’X]:Š1ðUž…Ud¿.ž b ²Jå8*m%ÂJ]T²g,V‰Ãÿt5Šm!MDá…ÆKž•*ÂÖmœm^ÖbÖï³€“[,Ì£fýxù´ÓÃ}JlØ•77íüõéIñ¼Ã¥1 Æ¢zÒAºõМqÞYCœ{‹j—l3ÃC¸s—ÜZ“„˜&üLó¬ÜÊ:,°‚تE9Ø$=¿6<ûÞã8‡¢d).ß~ßwv¨Ñ+ô•?ãv kbuÛNѲq5 bëKzϲ½¨²Ý+©RM‘Ä>üùàŠñ¬¼ðîàXì[ì°J­éÂ=^Pß÷¨û,?O’â©d‚é\¹vÆãKêÂrê6)Ð 1ëHT7_h†m¥ì.Çþa+¹ø0Œ. óHÛO—øÕ&ëÜWÙ¨D)gQk}k¨”X>tÅÉ¢F#ΤMñw.*Ê»\ÞÅ‚±C sXÕñkxF‚ÀÐ*€. Dÿ®«5€óä %t™H2Er¾vQ¸ï×+H_•ÔÆQ^ÌíÄ[›¼v²v¥ö©XPhùl°¾eÞöbͱž¨îûÏ@5;pYGj¯£;¢jù;`•jêmxŸóØÓ&j-‡sî¬ïÓwm¦^ÐÏ›CVÂjS‹ða»;RO*jäÝ.›¢ˆ²Ó_îÑ¢ó%Â"WÛh@MÕ¹®¶=DA­8_ ÝJ¥Y)jÏBfÇã»Ê[“n`(«ºJDÇ›G²Ÿ¢íeØ'KTÁ}éôñQíÅwLÅ6Dwæu5]«^T±1ÖK»ª¬y%àö+½©xÆìé·Å"W7»½!†ò§ÉÈ««&+’M6­¿Ï6e–õãOYùêÓŽLÂNødW˜c™Ó Î,·þ,©!ñŠ‘vnäS€õGwpx=¬éÞÅ™"aK؉ˆ|jßÌîá8ïÂ4cüˆÑ­øa~NVÿ§ ’‘<&5„›N!BõjôGgŠEs >Ü;d8À9Ðk±zº˜õç¹ËÌ­ÿh³jÙA™bþ“\gÿžJ¤¶²…ZÒˆA²–„Ÿ;øož;Y×ç{KnÚ1¯„Áõ~P ÏŒ[5/ÔN4VÑê?ÿ¾$¨iq·RUODâ`U=ÖYx¥ë÷îfǬf羕ҹèi1º‰i‡ù|®¥·ëáÅ@iÞ§KaWñ›ü¾¢åšÕu2‘ë6’?14ÄÎÜ>Ð8?æÝók¥&RÞè"V‘  Iª?¢];pf3E&?µ›ð×g¢x9Mqacáø²3r·/¢–i­=B©8׿ùéòc D:m—|da5ƒ ‡Ò‚Ù'sŒÑd8Kôžß¾XYpRH:8_=l#Èì1îžÅv…Öp+ä#•þ5{örqEM‹¥A)õ ’‚=X×ÔPÉ’îÜ+Y§ ÇWDÚáG·`ºœƒön'tÃcRÊ$-KÆß°}‰nß]b¡¹IÄô¢ìâ ÄN¸ó¤â¾‡ÈWóúçÁ³)×U:/[Œîn3ž\2ÇZ‘NÙ3ÆgmÎuñŸv—7VPüq ë|ˆÏ“°‡‰ß–-”šÀÆrÊZcû›nŸs\º¹)~Ò1€™½ÃÒë&‚ù /8ŠibÁ!›óMãÎQ ™/0¹øèµ *Ð=Ðà…ÌFGÙ³(Fì#ûÈÉkºzI<³¥‹±=Õפ;¶b*ÖŒ„6ø•±Ô”–ˆzÇ­Ä”D€:J ®Â™Ú¹dûu1K¹×Ôªÿ³WVA÷{Ý~x·ÍÁzˆ!Œçàø)jÞ!ûß5ºè ¦¾ÈŸ*lm¯?º„Z[ˆÒÀ3[h{@0@–ˆ–?m¬TE MfÓ=x§c¶õýL›È€M@õ,ôºëÆÓ]©Íîz0·ÞÐñ…‡¡~@p°£à5‰ôKlÖfبhDù ˆË%zJùÆP6è3ÕõÐüxÉ‹Á·LB‚.tvSÐfgÚ§ªvTq’žÔãœÖ›Ý´p±Ñ7³¥LøÁÆ(â=éîþÌKÞB—µ)u*:„¹Fuiê?o@—ˆ–¸dÁ$Í£4GõÂßfÂ\UêBN:mÜÒ0æIÿÈ+;À‹¹™¼Q6¿æw®«lÇDÙ”{Ũ“bÇš&ŽiEÄ-ý‡·}œyAÌbST}©0†0ë{ÓáÀòŠÂŒÌè  ª`òkº·W«`/ûÙÉœ8ØRñðx–s›$n¶N Œ^Ñ—«ö&žá´­üöŒ{>R1xM«=C¡Š‰ŠãÓþ@SRHÄÎr¡Sp)‡âËûÏîøjí_Ðfÿ“ñôxFÌ¿!mBC’«¼•äVÀt‘§Áäo -€fIüÒ4ß/‰³¶ÀØd:Çha§@Ë'™äeR¤”¾QÆFÕÝ‘Ýèsj—ˆZC/%D¬ªì£tÅ vwªB kM¼G{Ff‰ÿ þ¸)«ìÖÀo¡­·o'þS»›Ð[«4j-e-$x°¨Çj¦É˜Érn‡ËÚfù¼büDºÇ4p²n„óV¸rKê¿vÓBcøäZêV„»`RÀ‚mW§0‹á'k“Ákÿ„oµów4Äg¿¦N“X—·‰Õ˜\ãÚšŽ¸ —6]ò^^‡^Ñe„Vjú9)Øi£úœ×ªô{x“˜ûá)!³“׈÷[ððÆ*[ÒQ$̘„NNÞÌ%Ù7ßVp exÄÑ‘£=¸ÂOû/õPJNaåû_fÛV»×Òô3þá¸ð×€ìOv@´ÿÀ5.³˜·Äoô?“¡àVÖØ ¿;A§Àž­ÄÿRBiÅkE™8~°u ji7°lÊCR£ô–ë9ïY-r÷§€•¡î>—‚¼•êh2ñ²q^ñ® ôr.…ý­ô\Òa!ç‰*qøl鉔ᴵhYÖzQ23Øm6¬M¤ÌäÊ×xãf' ÝÅ0Le·P¸½¹5Ο µ3}.PqZ!ÑÝ’ ýºÛjAi2½Æ—ªåÌó.™pÿ¡ŽÇf„Ñ%`~&Óqïni+ðf÷ëoRqx ›@w®þ¨¿ Ÿ-H6‰·\ýWSCR ì™^l,xŒ³h}cןwÏô!Móæ<ºYÁg‚3ubnÚl£ùÖL#ÞzŸ.#½2¿>eƒ Ø£düS<쨶{Far× ‰K?ãZÜö>wq£’K{¾€C¹_—w¤ðy-mo™BÐ$E°Ü´Q £5#Yª1b2 4pÐ{h¸©àߌ.ZvÒ½}XÈÂ×@>u_è–ªu";L¸F½ ¤éµk—©Ð‰ãçAnkÐ"›ý1šEþ0’æ¾$[QóçdÿûÒíž´‹ÑCBÊÁã c?½k‡Ò…)ú)¥Eg™¾ÈX<è'üÚ!A›šJö€”\1O€ô׫óosãçRyS6þ 4N˜Ãh$ žCfÙæjatëîo1̃àTJÓ!Ï/bøÅ0¾Ìú‘ÅóÅd]bË5Þo£Ú×idÛ:ƒè’Œš¼·-‘Ÿîpñ¹uˆÛ\ákÇE5—¡ò z‘1°¥mL6köž‹­N D\vûà¯*÷ÇM·š¸÷gýìïú° b ÞƒA­h‡T‰HhA)):?'åüÎu)»J'råAW,"S+õ ¢ü}KM¹¦³Ž&hÎØý_³K&÷í_)ßC¥±Ÿáù3ã.ÌØòU`ñ`t±Ü¢\à†6=>½a9Ó%”ÍY¢Ø¶¬žçÀ Ò+JÎvb³I‚L’m)ª_ÜœÄü°zÿqûÍa?û¤9(Xb—:•XjC(¶éJ±…ÓþéÈÊïßü•žçíQR{!Ü¥ºBº.:ŠwÛ¨ndÎ ²~›]°ÝÈÛaàY‚|pcÛenŠðZhÒod!ú†ä®be'—³…ÈX\r“.GÒ=„­÷æøÏB ¤çŠÑq¬RôCÇi‰ïQ wLqCëìTJ+t!r‹yP.‹ú*9LÐ̯q;\à»x牢»æÅò[fæMÿe 1?5($`äÉLÏ…V›å„Ö8ïQOÚè±±èwWœÔ{‡…¹‘dH ïFX ŽÛ”N'ôé)5î~*Uû¯ù’÷ž÷²Bh¿“²K¦Æ5-~©³7ÐŽÛÇ_ìP­HA®eQvASæàŒ„¢&O* "%ú9¬3ƒ»QìÐvíÆz€Ù®U—[`vâEðŽÃdEØð]Ñ7H™Zö_ÿçU—醃戢@SÇišáaÜÚƒZ*­ÍôÙ¨'P•Š9®hPXÎtø‘bn™I{È-bÒ5IMØ?‡äj7V‚>#CMG®O5¢Þè1F€_µâŠùâßxI~ö¤[­fß{)ä•€1Í–VC#ÇZˆ¹Žs3öøÞŽu&û_Œÿ®KÌþ2å2ñ"z Og³QŽ q‰NÛh‹ù—Q#ËáÞ•ê ˆ'zi¥×iLD±Zäµ’Ï:j¶éi=€Ù±¸›Ó\؈·d’†^~šÐè&oNÖRQ‡ÿîÃdB/Wbþ© Œy]D¦Î "b:¨¯ZË#ó5ç6Npml5}ýð‚Óëã°P?H·J­ÝSX±íXÖå}~nc‰È2k£.¢·.$š²Ü1ç:#ºå–6ñc}Áù÷wÇUç%ßN†À¬¸ûÚ?¨p„ýqSlí% 1… ›AI¤ÿPnàÖµpeW¸1àœh{«ÎxMʰĦ,óò¿‹%è)øáµ¡ôóëV¿ŽG1–wè0de}Â"ô¸Ÿ^Íoù,“ÑbGÎq®çÈ´dw"ÿ§¸ìÉ´ü Ã-ë`^h_nçÕJÐdBNyâ¡ì-B±Ò“ã–î „2¥ Ü2å)š§‘ ¥Ò zô+K.a}€¥ªK(º„Ðårìÿ4ý¶rc²[Š Jò'/³ˆÀÁÁÂ:Š‚o³5;öl\©Šþp}Á¸„a¯t@¨›æÙîH³b–ƒb¯ZÕùø{³y!A.N1{ O¡ø¡—1R2Ü]MÒ££±l‡gú3âò'Ì·ç_¾˜Qâ2$&ºãZéL¨sVÅÏ!}Þ­ðbS†èOÊ!x!xyþ¯u3´¥ÛX#zåNioÎPIŸ»OA_2ÆË¬¡àìàŽo56¤ËÓj·çÖ\ž~h(Ç ^óGì*C?Mî×àiV8øÃëƒCm]GÝeXÂ_¡¬®upqÌ–‰idrPË*ëþv?SHP­{ìÆœˆÃjåÞMÀ1ÇK#¿À¾ bÜ ô 6¹†ÐoujjÓîQs¹J!e2¨# fÞ¯ÅÝðÇ»ÊnÚN #–ѷ×X9Uw¥Ø4΀Œu×AØ4±„ÈeÃØ¦\xs©û`—wQRjs„ÁSÙKj’²eŽàë}©Î±_y3„yÈzJ+çeQŽ’ÅÍ%}4ïÕñVE0¯yç dzïÐáûÏ !6™Hžgªë9Y‹q‹ªØf0;VêVÁx,%µËX^Á*3k÷K³æU•žo…žcìrêÅéIfû,há`´oçÚ`~¶ãqâ¢=.¤7T;Ým£àÚµòÌÝè]åög§F'ÛÈÉ2J2\ÌRÂÎÓ f½p¿Y†œÉ¢Õu{j¼%–¹I¾uÃæ£8íñ±<ï?â -ç)ßV̵Ï%véqXJ—'¸N®nÙÓSõB L;üšQɆó ÓZZÝž1Ќݤ”$G¢]–Ç‹÷cÕÔM⫌•»8tŠ“ná¸Å¶º·É•þß¹¶7_êgÉ(=lj%/>‡ ™T¸ßfd#å&² ¯Â‰ŽÎ²r î’ªbwñ‚rgï=þàªJ»2ÄWDvaºxRatO|Ufý«]ßO¶ÀŠ ·€ŒY»BÞIÈ­Ô÷Y5 Î׸óµö„Šç¦– ´²¼´w—bdâ¨JCÏhT¸(DÕ nB*9¯ó¦ö'Ľ½µcgóEМÎKO éÂùÏ6§ÿ±/,Ž£iEÀ‡ù½é~Z§àxLç¿^úúqP×P[÷A+¹2 ’³qSœäDr>¬™ªPƒ™ÓíÞ¿OL©d»¤J¾s¥bì&‹*îfroüâ2ÝHhÍ…`d¶1n®Òôý»ÔËu_Ú‡9kªnXxLÚ m‹Ç™®Ò*…ô³êÝFÆœÂjhs‹² Ë&˜ù|«I9iüðqN7bœƒQ2h;O^MÏ+x¶Èòò Äï_ƒîwÕ2Öªp˜È“æñK wz¿|éˆLîUi7ƒV½ùœXáa”ü;+"¼ä„.ÏÞhGÛæ•ß%ºp ×\.ÂÌ—ßâ¤ç\UŽ^Rp×0^Sy½°õ¬0ð‘§|d·»ÙOÍH†]†oM$!ïR‘Ä®s‚FÙ–\¡…Ã[%ƒ„b¹7ÉÙæIÕ,ÛÍ ÏjŠ02\— ø3ðÄ#eðãtƒFï‰_é$S˜‡nD‘@¼Mv' ‹ äf*¤[—–¬¢^?øbOî|j†7y–¤ú޽Kå2/ìÒñÿ…dZšD]Ç2Šzi¿õ&:÷–i^J£¸Xóøú¢ÎFY‡±òV‰Ø´ÿoyÓv£êãªÇÈÌd6åq:a­ÆÕŽŠžž—‰“‹óÞ5ó­]/a¶»]5'}'Ð ¼4«Dîë™5ô×qƒG2ü2zc¸v—Ûùê5º —a#[ÐûÉìíÀ*Vò2€¾öÅ|¬Ïþ.dymA…íÄi§–ˆždFÄ p9›‰_ñå¼ôjÂ0y¿~‰_¿d‡ 7îfr_,Vq-¤Ô€¸¦8,1~ï &‚fH7À®Hæh†àI øe mêá6(ú;4~ÑzîHãø2@–»äîN0ùדµN÷.ðo½é×®hý®_nzÜD¸™d<ÂÅ>Çf\êA2«È‘jÒ÷còhˆa6«&Q]¢“H¶»M! >`øPI5Θ·xìàe¶¢F™n ü0ï(­¤Rø —í]¤‡OÕ@ŸpV .P•Àõav‚­•§@Ù_ÏÇ¿ ;_ïè­í'u Ò$¸6;ç´ïöìrÄä„Uj]}bÞÆO,·ÑZÝ-|õ¹eSq8Lì§6®‡„—TÆEü çÞÔãȺKŠ™qnvz/BB‹aú*ÂèýÊ!­¢Ž¥«¥2Ía]G’lDçÔýkô`c®rTw»éÿÐÊl].5Ë‘Ck ×énáˆ!|¼`<:ÄSø>•€c®ÆÕ&ÑŸ eGçÄËZ q¼]ß 6:æHÔL·qfRYåsÿ”[R)‚%"ÀÝ%ZÓ­BGeŠÁ½÷,‹¶/CÂó™D¸˜„‚¿–<¯¤XšAŽa1Ëù¸~ò6rL… Bù¯/6¨Pr»‘xÎ5µŠk:—Œnî³y¾DQö&#¥ksþTÛ¼O4ý_³t¢ô²íx8Â$wøëLÑÅ;L¥­txšDhϧÏÝÝÒ=²+¤1\p7cÖ³ÚWŽÜNÀ@ÔðHBxïö²ô±ã¥uÜS½­²KèŨHü—ÉcZWò¼‹‡’+?\«å·Ÿo¬;¸ZOö Q'ä]›8×f æíÒîU©šì‡™_T€Ÿ"a{(Ÿ0:Çh—ÎùÕEozHZ¾LÛ\þ,Ç”•¸±‡µ?t¸.ªÜ©kعÅŽÑ»^އ» £TËíƒu:ïÞõN2Fª…÷–þ;éÍ]¶™,&GäÓ+¡Ký^ðË_±÷]×T¾+àêll§P$ÝL€’Äe‘0̸ÙIâÝ\Øzf@½ö@$ÌóÊ·ð"¦ˆ¹Æpæ´ûû z¥O5-ö[0¤]_ïær)óO¸e` 1ÑÇQ«ØÜ¨ÌnÃÇç5£¬Ëk@~(º”v¼Òâ!ðj¢á€ ÈŠuà,‡QPX¡ØèݤÄįײ…øw‰ÐèO‰­Ê–_û7ÇOcàžZËZfÈ[„­Œ>…5MàT”¿¯’ÝN‘ï¦üSþ’·ÂËÙ<æ›5ÏDºÊ£:YÞ€ÕF¡$ âañû5^Iù±[VÊP2z,bÀ¹Dï}D‹bûu€Ve¥ Û0œßÇËsh*uBú·ë8`ˆt“ B7QØ\"ŠÆl¡Ÿ?{_+¹3²Ý2¿'æ†9v+œµ%ùÐGާínés^v)²Ó,®AYÌþw=šÌ—&rð”>+̸̿÷ÿÓ‰ÜðO‰ä!¢èØ r¾ž(l>‚N `O‰W‰†lÛ&êÚÔ´o¯8ô¬—ÿ*bkÀðYÒ:°Z­”E‰¬µáT,“’Ø#Àrª‡™” \˜8jִе6R^0V™ôs$!»X§Uh­NÛS&Œhù¾…,†êÓi1]Õ’69ÜF—)ðñ¿úô¥Òžç ¦2¹æ=ÓƒîÍN£!d,¶È—·•Ò“aø%<&}Ëe/`Z–ÖI-ܬ+¿NˆÉ×^Çsd»u® a6&J”Çà3»†ÔE—í9±­;{ÎŽeœ­¿ž³ûñ¨­Ä·L90j…Õ66í‡)œG̨G ©÷q‚`o†·$y)6rUg}VI_"ªu²ÿ¯ïÚ:‰„ÆÔ8Í|·iI{šö!”‰\ly“W&L} Lç¢YÝ–çsÍ"âŠ[êþ&ÇÐJØgˆjÅÙÑø=þ—öp Î…ž eF%ç|½ å‘”pųœ0˜$‹9æP7k/½—P»'E´Êáa]{jÞDÕÔ¹°„’çÒ*ñiº ƒ3v›ù†*ÔnÌX¸÷+XþÆÝiÁJï]z4?› ê¤û¿Õ0·<ðþïLE§ÈǶŽSà“fr‚G]¥ÛgeÿV`i![ÀШålÂÁ5wµn±:ê`5ÒÜ:Ýzá_𩵠dÇ·ÁßÕ[Ûÿ¬Ç[„%H¸¾I³/E®;wL¶:›£E¤C/òRv†ãͧð5G´½ßNNÙ0žp‹àCS)ËLnx&{V‚!ÏdXõ¢îÎ˨!Ù¸rµ¹…¤]°Ë³ ”2½¬-·<)™ÚÞJÁa¥ßDÁ¼`ßÌáu¹ü¥þÜ4Ïzœ‰{7…}Œ±ç¯ôXj#¿É† ˆ¢‹\Ý¢[t´»%Sèíyk#åÆô»nîMž_>‹KxÕ€š+ä¦ ãš ¬»«–aš4ß–ªô.Å»É.K•% ⪜*@š–oEKÅð', ôrª;ÿ-UsOž>“Éïƒ×ÃS³4Ì áè°ÊÔÇOY“Ê·'OÜßèkÛ»óêv.V“yÝô ZT$3rwøäñ¿ÛeD¼`Žý¤Qw¾´± ž;a!Û_ %Þø¡h‰°ÒÚÇΞç31µÝH²â"LÎfj[!úí«k¹Û£kkXŽÐí8…øg.¡zk& öSÙŽe‡dRʬëÔøÓ™èXꉜYåõ2ܺ ¦ ôpGó:¢ U1×Ür*&"°;~‚ÏLü‡E÷·ð@¿ ò Û ùóR#:ËPÑBº[è©Wñ²¯O>e³? ºÌ“HcÒ„^Pqúá㘞ò( V¨/yîàZ¤ëßkaÂY××÷m }¼ˆ?aO5Dà%Iœ–{ÆmÕŸÇþý›­>ÂIE¼oᵈI¨¼TL`Ôß +ä·eLD»Â¼1¸gÆè#zñ&(Sl}B‡çË;ü`ýÈDo1 j­EB ¦Út’mÉi×RзÏo6­ØŠÆg¿I¢7K 8ü¢å ùi—ûBu›!A Ðm]õf4Dì@µ‚‡"ðúu(T¤ú'òáz=[fv–¨‡òüî%…R3¶pçŽ@¨e¦Í·óI.мM#õñšå±[±Cª¼Þ 9‹ÍÈË3žãúH»/ű¦6³„³BÄÑeº )ê¨Ôøz¸³kP¥¢0^hhz[KÈUPR5J©¦ý¦¢³{²H#~t!l8Š*jòxˆ½-W²dc³½)$£˜h|ÝGäêqëÈ×\rE;rˆÈÅxb³+ «¹ÎkÖíåc'Ó7º<™2)-wê µÓëá;[·ÆfXc¬OëƒLUØÍƒ‡÷ѧ‚¤#¹öp¥œÛ0ìÉM*OO³”c¢[Ë–{]Z¡¨—£¨<ÖK±\É¿1Í}M§Œ•uz`zm<äÞN„þy ¹@R £ªuàÅV} 'ç°ÂÊÂÞ ”í®‘]ZvÊj¿_óRñ=ì&ÛJÆ·(k¤]MåíÝ8b­çö¼tÿV#ü'IìK_ØèÞÄ‚QIÿˆªg§izA·Ux¦`T«?øß`ïƒJà¨j›PPãÝ2$µy~ø+°Ìr£w)ŠqM€ì$¦dcèÑ Êf½Ä¿ôq‹‰‰ Lâ£J­™IÛf”®çÖ;2y$åLÜ5ªNøý·HÒÆ…ä"-üð°=<XC‰V)$Pç¼`à 6óºZ%XÁS³§³å¶x™AøÈ?e°æ¿zbh$°é0É›mlróHÇ%„¦~†-„4„²µµ†H⃪bæ âg;Þ$¬±váíœùÔd9ÝÏ[ è8M¢ºîÄ<ûcÿ7ƒ ¹¤çì ójñu_û,iðèô€z)‡×£Œåÿ=ißⱚԸ¤ÃX/ô¤£ƒ±^±_åóa4¹±k#¬ôz½R«W Éd—ØN‡T¬µå¼'spï. áDhÒÅæÂ2]%áy°Ý Øˆa§ÐC‘ eðnr’M–KÊTƒ•ŸoÔ7×ïàFügMÛó¹ô­Ò±L å"® i°±Þ‰³§”µþ´ÖFKÑ8Ýazµ:¨ñ»–$RY4V1lö©­jÈ“0½¥Œâ­[0Ë6ÜŽ+b„^|–[ðRîÒœÄ:ˆ°D`D_;-I5ÎMFù,™I‰Fa©,$&§D@ ïõ\¢Àìh ÅA.u:Vn|²Ò0=µ_3qÖº|ô¬Ë:N¬©#Õ¡#ò•Ã}ùJ†p0TdVtiÕG"û‚£áŠel­C¨r–=ç´PÛ æ—‹„娘‡ºÐØó‹º¥röf |Å&TÇð¸á˜)lÂÆîea{8:?IV'Fü½M·&¥ ODæNÖÕäõˆtÞÚÒÀ;œ©•ôž&Bç0}|Í6êËÕ`fªë#&`¢¹_n|YE?]lèE ŠÛ:7+@·e ~°˜ R èÚ±”L)e‘D¯ï,ß_À_1Ç9D×nžâý’k%¶(W´’²:fÌÿ U•_¡g·ª~¿_¨sf•QƒA§ ¤DïÄ£odr\†ž(%™`8GŦÛK/Ù¾Ó›“Ñœûä¹]dƒ»øÓ|·ˆãµÓë."ì{H6z‘m÷rdY»FL`–/x0P(‰¬,“D¸úEìûP™UXK'Àä‰þ~Åêm üãP€`sr¾Öjã=±®f¢ïW;RÝ)´îÕ6îK X{‘ Ì=Qr´¬H˜¦ì½ËïòÉhÒnÜ3šÍû|0ä±>›Sb@&KÌ*4¢ê½džšípo&lIjÆÀ"²?ß å€Ÿ\¯˜‡k©+W{Ø ÿĶª›Œ§G·ƒ.Q@L¾¶99µØ§=}§ò?²vÕP Y£”¢¨TÚ6Ž@Ñ ôÆ}Q4 h|+ëÍ¥»¤iLÃ’0(ãÂÂ>®ªôIpàÛ.DæŸ7*ÿg÷5±ß"‹äßÓëÀ’-õ¾Î,±¼ŽxC1ª-@_G+¹í´€Ç„õ§§ó»Ú…h·²êË¡ úW(õ¦`šÂ¥·Hƒ ±ñ-Ón TÎ8Œ\04ø…¢™yYŸæÕž“sŸù‹.¬yx_ùVB$Ö´TüÂC ÒŒ7”u¹ßÙ•^O&†£zCzï>@ßq5>“%Fÿ’¹û·ÄM±Sp`éJBdèÞ2ÑóÔÔ4V&¸¾ãˆ–ÿ3PFª)1…IÌÇ‹~Ë-Öü%[iÉ'ù¥aÝØ½àŸÄ7L%|˜âN.ät¸`"ÙÜÄI·0–Ï›e¯ž¯Žß{Óñͪ^¶3@‰Ü†.?Þf3úÙ@¿ˆ\—a…­Vg™u¢Y08üŒ^ÚÈ/°ڙ p"D›vza›¾éžeÙÊSΖm Ô ªŠ§Æ Î&^ý’Ý«¶À«¾Tñ‰ @EH]O ‡KR|ÒiU+f4`“^÷‚Lì·×¸_fd§¿ìDŠ~Y¯h‘øø°ÅøÈ$Õ,žöƒ¼…¾N$ä ±â=NoŠ1ôdñ¥Ñ9‰Edÿ:PÍáIVÛ;$àj÷½µRz*…>•5‘MF$Í9ê¤ïܬ®x7 0žó` yöV¿Ô,îøò§¬ofGH$Dõ‡‡pkéÇ«„D(ª»fy丘'¯ŒùU“°÷êx¥<¢KŒ‰g ®"0… ,[\>I3 +lZ-NøŒÒF—)L×Jz0W2wpTLî}ØT ÁnÅôÞÔÔ=Î_#+`}w(£zLч“%õƒÁ³pÑn¿º•€j¶+ðþ“¥ŽhüWÌá¾d‹©jöôòˆ¨>uÂ&=Ã*«“Ôí,Ùù-Ù}Ó/‰Z~v l¼¨ì(l1ÕˆP¬·ƒä"~æ¥^µk¹ª¢_äd!šˆÃ¦ ?ƒ‘¬‚,W¤Ø?ŽÓ9Õ§ù*GÉQî© sh“&AƒqÅÍL¶Îÿßz˜5…_SØ¿áåJ˜TØ÷XNÔéw0cT—`JñVB»{¾…L‡uUo û<Ï<Š~ñ®¯óq‡ZÒ0àåÏ¡ÑìÎZÄ+·/ôc3<ï²7{äÍqÌT}Ît‚z`»=,’ ‘e°ôíCl:9˜ye 4™r ¤£Þb³Š!h‘K'œ”U•œQòÜI1-&ó‘ä,üx|Ӟ؄†u)Œ™pÎk¤9®ÓFñ¡ÄÅAÓ¢…õ›¾#€r`Ž!Ýnµ(%5$lxÀj¾âމh#om©öÑU.ºÒ¢†3¬óòk†ÞeZ54’;õGXó”FýŒ– Áÿ Ȋп”òOŒDIa¿BÞß4ƒšN†–Q)LŒšƒƒ)[F¦ÃȲmttM;™¼}}ù¯æÂ—.ÖÙ¾Bá¨RrÙj=ƒFÒ³ýló¿ëušvŸ<ƒ£r±j¡}'SQU¾óG}ÑDØPšV\þ K]ƒ%:s‚¹ê[®Ø0;zµ!ZLÓ¬»ÒŠ?ÂOØæëY>³}¼V %qÑ""Úé·ÜOPRAH`Ùªz¯‘CÜ.6¤ÔÚz”ÃRÓE_U¡¾~ËáŽþ®¨›mÑØÚ7!¬I³þ·^A$²-¿>ÆÎ›:úSzä=|][¥d…]ûXu¦Ìv’ó;†áwj­’&¼†ÏÔíÌŒûe[†–Ón£ÿø}Ⱦr ÍpINf49͈1Qˆø¾Dô|Þ»?L„¢ÙÅÚîå¢5ó|ª.…@Qœ˜9ˆI͆ ˆ%ËëÒEÍ–2ßàrÛKõ Qyf™«Ž]šÊ“y¿úï«2¤Í³ô¶©mÀËà3ß¾äÞ›Ž¤õpÿ{^~è ’ªÒ$#{xÔ´ õïŠóЕÃgÆ«z¸9¥ëíT­^€e’ BʽIÀÂ(nÚó ’é?–ë¶rXÛ^ÝMP}†¤[P—{~ay$d÷‰¢sÞóŸ¢ê™6Wc© 6½ë(2ØÛ É.u †w•À'ý·¢ºì¥ÀUÚ§º-è]:9Hi†õùîM.Þ{Q ‚—Œ˜æ–Ãcyy€(°Ï“LÉA 6»Z ÖÆv4áw&· óµ‚ñ%@Ú6h•—mšMSÿë×Â5DÅÈÕUæwnAé;¬°s¸a2„}Ú=Z”¼Ø ]³3‡¤ìS5ÕF¬¸¤T….úÇ9¶ŒèWKç𮟷l1Á[æfòÚèH†­Ý3—›.ëìQÕh×>Ê9ñ*§.ÛÓ3Z]Å¡è½b äçÛ\<ÅNŸùÂlþK‰Q„w;†C…§D‚‡lKu$(óa6ί1 ¢xÁÂ':t¿°©å¶_¶JCÄ­ÍúIw3ÙâŠ1›Ì¼Ó[­ùÚ˹(É«¸ƒƒl9@Óh#ÌÄ/·Ü~ÌhšxÊøJ¾«‹a03æ¥>`éni}ÎɪH?øÓûŽOFGŽ6Š ?DšÝM̾tHî ´ÖÊ`vôPw]›ÍÍra…è,Ûήà ^¬¿S~0S0hlÝ•˜—xà®DMRTÛ¢&Ö(У˜C‹Yc¶¥[ÒÀ’o¶÷°JÐcdº±RËÅuÞÍ•лšmËé •lxBkË”D|îQJšHœå½±é8Wð¬Õ|C™ÂÑ„›ßê¯Ç{å·¶as—@:´o©¨©ÊJcãØ×-ýÄßa§«%èpš<Ìæ‡ÿµ&‚ÌWJM&V×\:S¤ >#—w7 `@ÞJ÷‡‚Yòná•!û1þNý \Éc3or¾‡y¯Vç¸ dŸ"´è§T¹çp ß$ذsïÅ¡ELŠvØ %´UyÍW[d ¤ùU¹H2‡e }6Nµ—/ci´h¦€ÊN<ªrü/°m–šŠ ¯u‰ð{¿à÷®y €¤‰ÏÐË÷÷^Hn'_ æ¾}ˆ6š•ë÷;±o¿Í}'EBC¨ƒ`Ñ$ Ò„•@o=7Ò« _•°>iVM‘ý^©.\{Þj^w—W§ºñ2Ť˜;ü0QÍ8ÈàcßT¯²‘E“Ç´Ûo_rSBa3FÃÕönpMó1â¬õb:ÿbo˜D^O‡™öå–YRi|h4;‰0Ól_þ.1€ëØÑ×ÿ.£±ÈY¾]¥¼7›a˜ê_p 2£]ø¤§é…•eJö§ˆMYÐ6…!´m›_í)´Ò ’ÿŸ¾yYdµDª FJ®D•\¢ý‚* .¹ÀjÃøg‚ ¿kée"ë~;P$¤ßXYæ§‹•ìûß!y”F‡½ÝÞwÜa>ï\‹gG½cÊà¹kO ¯ÁÛJ3Ä¿e¤ ¤ /Ú2޵Z¶A–K`gÆ—8ÃÖùÐ`À=ËÚk¶"ÛvÞÓ@³ê—G˜ö]7C¸U+_°ƒ'øï*'ŒBcdv’wÑ×¾5ÿ’·ÿêÜÔ9¡kº¾¢eš8„”Ý‘Äó!2G§3>ëîž[’eQ§ùé3Ö..o_*lÒ{–¶ø›ªæ[`Ûša±8CתöZ2XÖv.•'BÝA;-)_á~ÒÖjâ^ˆ36Ÿ•Gwz(š¶NjöÉ€ ?¾.¹Â›¤üÚ»ïu-À~R¾F@87«CÍ4ÎÇ iáÌÊ®$kV»u’ß/üÌ@`x»–€W¤½J.7Sw"¦ìVUž[” ìã´F‹EV\SfÙ}ý–Žâ’+†7¡<±¹Ì½uÏç{¨µSη[ßrKVÊ(F-ðŠ=ä ¤Ý‡‡Ü0w¿FO kLn`§ êˆMÎ Å—¥P>°?`~ΉRû +‰Ù’ÕT½^‚Ó®/ŠìøÕǯБSÓÑÌÁˆíe€eëâa÷%\T¬Ô‡.N„½1Ib8ÃGb°MWX{aôâ÷ ߆¬ñ@ãûñe\]8Jñ&)”۹ܰßMVw„é#æ&g£{Š’ﺩ=6E¾šòh…£€]œ(¸¿œ:|¯tÐ`ûܯ8Ì “zÝà[y¥±¡ûö°xVÄaSçãý‰‚8"φRñpæJZ‹Ieþ³Õf×é¤Ù¡º^Öÿ‰™Gjð„ \&I¯ðh„æ\Ó­'pþÎó³Çk¤†‘Íáp@Ä{XÁiÄjáñdDI€NÅôú¤è^\¸ä¤g²æÆÊ”Œ]ÒbÎ{ˆ&Þ¯d½@‚:½Mm)S¾+½h¹ôù|i㪠¶Ä©;Í{ôšÌƒxR%WÎHK .õ9“»jzátäÉ%(Ù³!wMG–Œ¢í©Ù½ òàu¼,=ÓÆÚ#»³q]|ƸžH?±›À&·0WÒ4ky‹ñÅWbŒfè´=ž/®š…l(ð Õ K@ˆ§Ää+Žú„ã¾-8ÇÁ+#¬°˜JÛ 7OXILe` Ü?“Ö+À¦þ²B'˜—°Õ掎@Ÿï2Þú]p.ä3DACºAŸânå$ÚsfžNL°óãá¡Á»”ç†A¢ïò<§jø•W›!Ç!Ë» Ô’tÒÀ¼ã}c´w)ÜYÉ7ñ‡ »ä87wOƇ½ aƒ?ã>ÃÕôõ¬­±kL TóÈ7ä?ø\þÂW°YLi!xõ»š ©5R…$¶~½€®ïZûXH˜)ÎÝ3:$]ù :}rª¤¾ô«k¯œ¦Jâ½ð´¯‡>æ| N ¾;•}p‰åÒ=òÊs_’-Üô.ý‰Àd.â÷ËÉY¼:ø®çÂ}˜Âm…dÕ¯ÝÕßP—¯tÀ)l„ÕËÓ7âÛþŽ,>;˜ËÕ:0\ßăÝj7ÈZ¥R86ôªìgçq·öÁ¨¤«>þÃ×_‰8›Ð Ç|» øó\âÛ‘€§˜‰Þø”,þ‡,Ë Žì ‰!{ü'Œ47ò'™áÒaùÞ‹Œÿ‹R‡# HëøiÒY™z«–Ù"~M´^ 7EC÷ÙB<î©ônÚ÷ÍXŽÇ/è3;BYÔKôæFGv ßMv5\ÔbpçrAãæ|éűô/cýmV¶ëàŽmv I_ƒpZÞ)ʱ½Þ–Æ"ÇÒÝÈ£ðw6ß#ÁE5ïÌà‡¯Ôàåbý” ä%›cNéðóÇüûD8Žd¨D+¸öì± )…¤’ÛüiÖtŽ>h)ÆI:÷[L±’ˆË‘œÜhUÂR6ÐH*FÇÔšhüɆ¯P»9{}J­´ÚK|5-^ÍOç[~G¯&##ÃË(”Û©+5Y#¦ïP ´aGÔ<R¥ä)ÌB8†un¿¹}f{eQŠÃZàpÞŽ—Aws÷§´äÏ ¹—³S÷у7µ¤òzï O*¤@ø@ìæzÈæç:F¹[óÀ 9êï¹Z¡ Üh&Y‚µÏ‡·¿6(õ¡çÞ¼Sa±l·ºrô%+>Á@çs›9ÇGªOöLÞ§iÐE( T,Š„yÞØÒ¸O¬ŠH5‘bõsêoí䢓Šáh(p`ª~mªï$Æ•Ô/&îVÑ>_ëVäu?Ži±Ô'¸3s¾x›ÀF$ÈÛ¥®#ÈñjC3ÍSœž8á•ÓèîÛa½ÙÄ-Ms5OñåR@A™Þ4ss—Ô™SB§®døº8刭ìðíQ±9Ÿ7t a&WËüKÙ6÷òÎ2 ©R”+åR‰–ݘ ¶^)~·5ëÑcÛáP+µã8§3òzä©SÒáú˜ï¦æ×F¡žÅmzÐ>Õ٩͵óÛu)Q@‰?»¬XÔ•:bôHAM?Ƽ½e‘þd&,W¦Ï¬íùhLYY0kXBj ZÂ6’2[yÿTJ’êýµôå/ç7"žaÆ–¿yϺª@) jˆÍº¿LÙõ“œ²ö³*J¡Ð Kó¹ni¦¡R¬I,Ÿ'5Ùßh¨S¤.½Â‡Xð'9Tòì­·Ž:Ȥ”(¯Ï})WõQá$m¢æ‹Wä|4¬²—©¨ bž`*¤œô\#Oζ÷ëy±Q¾à¨L Kos×SŽ!LF©¶*¹›ÿP ~0%Äžii«RX¸Ö€b ¨éfv”Ã,¼£8áH¾]zÓ 4 ¡¯ø?Z~0îÅ^oa¿Þ¦ô}ùSˆÅ­ïÈó6ÕrµÒRî‚4Ûh‹ #l„–# .Ñ›µ=ÁL?J<¦?ˆ®’.Ût0˜¡\”YÑV²I-¸aaÈfb–³Õ’° œúþTJnع밵ðÛ4ŒT@X÷Z)OÔÛR©h_6 Âk–T”ç3‡neÀláe6­<Á`M•Ǻ'†ŒêÖ„ïçÞ ƒ÷ÐV)JöÉi|Ê%J.§ÊyǺt@Ðv#¯$çCM°NóEi'E´‰P¨^D@®Ü9ö˜¤šá"*m^ƒÞHuPÏ«¸Ö‘²3]¨1¡‡x7GÓVñÐÕÚɺÖ™!¢U€Hz‘-_èÓoš‘Ì’Uköš´hÚi¾«î•ü?ÇÏ¥Ôò Ž~wšßLþÞ ¦“väÕb”Õ€ 7÷@(Á¶ù!d×°N€ ç. ØhQÖ«YðÂÅòá·47:JûºØ%F¬8DDÿ)¬·¯…fÑÞ‚ &ŽÁ@È!qÕ(Ö–a-Àö_(hãK\޲:›5S•›»ŽÍq‹“š, NŒ]C{µÀÏž$ ¥î#àä^nYôœl íŽÎ±ð|¨Úk¹W؇[{rÁÐÐÇ›ê'+Ù4ôŽ”ê˜—ÀÀ¶2èe¦£Z.ášé­s Ú*e…Oq/³‡îŒùýü—§T³¢×Éé¡ÎcG†P+EsÙ§zúËZ"7s’u]ꥻw\tiðÈêWßˈóeA:‡“›LjKÆÇÊ9âÚyIìÒùä3áÐ7¡܇úEZŽÝ Û˜ñäxzvï Ã'v 4°8 Ÿ^Sl¼bL¶‘ WB7ƒ¸N´œÕ•*!›žZþ%õÿ“ŽÛY§\çϸ0kÕ°€O¡˜ÂhúJnÑ· „GO<‰ê©Ýºêˆ‰uLÇ›.MòºJB:7“R7ˆm˜{´’ÙR,±2>‚û& ĵC #*aþKÆæh!!¾ªU(wQ8žÍ—y3tKðºÅd. ²+²²qVëaKô÷ù¦ž¦¨H5[Œ)ቷ#jÃQœ¸ý7|F‰ª†^º,‚0I]Š" ØWê2BÊ%,t$?ÚVÑ .9átÜŠ©J%TÀ}¾„ÇRǸi?5”YåßÏ ;v.¨…¿×ËJU …#Í1†Ø4T­_樛 éW Fý q1Ù ÿ/ €˜­fŽ”®´=ÖPýÊR–²7úõÕ–p…!Ú¥9ï´ªJ²ÞW|çÍýøë MItÕÿÂÔÛgᘀԧÍpï×ÝõõýtÕ|NXµ#¿ýztÿà EN…¼àL‡Þ,í›kº=VWqçPTnÍxö-_ƒŠ?ºÿŠÝQ«‰ó6ÿ-A-¼ñ/%x59µSáîèr`¨µúÀE/õÖ…IÒ¢Ìû}[ˆÌSuj¯@5þq"'WÒï_VfYņæž±y‘ Ø€FcQ’þ7³ FßõÌ ®Ÿ@àteºÇë>Øã—=ÁÁB‚Ž\Í#X ¸¢ë¢ï,…Éç+ÞÊ‚V3˾CZqà¥I6 ¤x¯›o]× <%н]ùO‹÷q‰`ß%iQm&öIè4ˆ¸½Ò;¡ÇÕüöÚÛe”gwOJé©ç-Gà¿Q&CJa§tºÛ§/Þ ._ô£w/@qÝö«0}¯ÜËkëÍ–x¦YŒJ¸€»^ÈqÏœOàSW¹®Zˆ3ÂÊÌKÑbþKñSK PgŸa#;s0°ÊǬi…d¾ú,í:T–R­C³9”ˆÛÀì›ÎÉ0ø YŠÂwY ò`‡Ûà%°üïôI^¡„ð×űiÛ@1Í’!—Åuúô¯B´h©GñÃ*‹úD)…ÔŒï=Eìf€È™ ã9¾ã)·¶Ü1)CP^eëƒïo8˜š¹y$h]kÖ¼m*ˆÕÙ±%„+È•%Ð [qFyí'©T7Þ\v…ÙÐåÃ-Wp5çîäMdVþS18³˜ØÄ© ?UïØ…q¹Ž0þ>‰bºÅ-åõ‘^²Åtnõp æNÊ`ÚbPpM¨ɚߨçšä bBR.­`6 xÔY‰ëTM_à#“[.WN Ÿ¾7 ÆŠöå0úTT·ùFùu Ê‹ÑU,º¤£è¸÷úPR^ ?üǵ—Õú #³ÿr)>y °<ÊD‹Ÿcu&¥’Qñlø´ =:ƒb²oø™,‹iÍÊóTTæ–)st2q{·x„3"%7w""†ãu‰Ä´¶Ú‘ÏnŸ=ÄÝËJ8öß×t]•;&ƒÚ'àuÛ•ã\ÍÅRobçVÒç)w#+4>#C©&[Hÿb~Á`1åSu–2àSƒxÕ™X«¼A6dÀDn¥(ÔçÒÒ ƒ*î'š,ôê‚u|Š!^fTœ$×dÐàÒ„hÿ¦£¸ß8¸‡—LÿW{9}S7Ô¶w>SßøMñÕU@PùÁò{¡—-éÊùºAû}ÿñ'ÜfæÈÝÑE;ë1±ÆDÜ“6ž@“i›‹h;PP§Ã(üú†ÔħRjoÀÔÝõý˵g÷(çR"ÜG¦‰·ý°Û$yû8Ìóå&€ôßfBîí¶ûÑÉŠ®™¿Uægñi%Ë·5'+—f“—d†¯À*"àÆ¯°žfësAkoÚkýþ•—ûb¤Ò¡®œULŠ‚(»æeµØí¼öH褉_„t$¡Û¥ã¿ûKᘫDÿ.-!š9-ÌæÚqær(^Ïp%9xs¢ `}îû¸ÇÑL9·¤ÆxkuÉ¢p¯”‹Œ{Ñb+vèõ] —Ì 3Õœ~Òž™ ÀÑ–•S™$›¥Gh5Û[Ó7ºH âßPâšå|Öᦻ”Ì(ãÕR>°ØX»¢Œæè*t®@œÃT¢™G»×Á”>U 5؈‘ÞÆrv$¹€ÓÏæ«ÚÁËLÓÓ%½öòÏ*#JT†yÇ=vÁª0A!… %1L†“Ãʶ¡Þô>ÌÉ}?#ôù)Ÿžâe¾@ª’ô1¦1ûF™Ðc]¡ö;y÷®|3äm­Tb¡%Æ’[§až°JHk)Æ ¬~Qz°«ÔÍkÄ1Çw^d›à Gþ5£Ý=a†l)¶Ìýô´%ÜYJ ®.zï ¯ÎéHRb ªn=BÒÔ‰M1‰‘™Gáo´Ã³÷@SÖ.šyÄJcX:]ÌÒ0e„“è•Ç@¶ëì®§ðÈr~{Ыëe8Ff%‘oú9 ¦ÄÖÎzÜ;Ì*÷¤'-ųº3~œ7n˜AÞ.Z þ÷ºj³5õQ“æ\õóׯ‡DŽš9Sç=æÀëçÊ5žQojÎÅ· »1‹ú^–hÃ}$ÐÕàV™_I/K.¡…Ûæ•ôä$~rñ0´ûŸ½Ú¬’A'¸=†Õ5ó¹*1šþů¬àÚ™5lS[º³‡§[ÎUyûí û'Ž#C>Hß3:ߨSå¹§“ÂòuNGù}”0<-ŸÅžÀ\Kês¾—Ù&@ø>XÔ’šV ]Ìöz!—†áˆ7¦•€~ %ø…Ôm†´FÊv£áoW?f-X®òY'€§òƒãxçéæ‚¤Æ¥ÐˆQeîkhzÉ.Á0<·‰wt4Ä\çêðÆìnQ«ç?b1¹ß¼QÀšV=[¹h0rIáP)þ̱»Ÿ Ï-â×V9?|ÐÙ¥Ph6‡³Šç˘38,¦«³ëáÖÆ¤i›¨#;–‰@ÅÓLñ6×g÷-C†ßn¼„—°ê"æ»ÍÌ5Tœ—u¨v{ü7¸žƒ^`ë×~Có%áÉýØqÞu7îÓE#jâ4£c#bð&wÆ.œ¢änr£ræÜçZ¢ ¬­ìúÖQïÛ|ÔºÊOÌý¾½½Üúj¡·þuŽ"ë0ZÀ ¾² úßíL·ø±÷´üÊgE?Æ¿Wß5.PÝ&.êš+9ž„æxÇä TÚSHA‹oò=s“§¶ñ (HVñU£¹¿ÿ6¦íÃýqÉÓõ(EÀ£Øþ”ѦX¦‚Ou®¶>£«eîðI%¡Ì{où*~ ÚƒMÈ{ÃãtTÉGO$½Øé(Ôv˜p îÜê• “İ«áÜvÞT1GMRú¥^(Nò}‘göy,£ c¡ãµÇd³š‰Ÿ‰ä_Š_ès5ä·-wóc`¢´€$bµ|æìŒHAkUCç¦ßæ)><Íï.$„‘z.IÃäž>¡Ùtà ¢Ëº…Ái„»Êªˆõ¡é1=ªŽçÚ2€5[•õJÅ>›D;£÷x1Ï0ÎÎ| W¬mx*gRZUKÖp’$wš?m;Äk¤‡Éé–¦=ÊÚ³Óòï¥wt­ žÎ§Ñ†ÜÖç²–ÐìI|‹Ø€QІèiòLö0•ûŽô: ªÊS ãP†Zì‚gÖT TÇômÏ7¬zÖ|s?TÓ2û±9]K)†°˜…¼!oq®­ÄÐ~I»dÂ5GLŽsK-ô?‰p¸˜ìixŽBwïÀÁA»XƒÓðMɌڬ›ÂvÑgt‘|T ÂdU½Vµ×7®h}Mp¡b÷§“fa¬[Žýw—~5$•Þ±¨þ¤±›¥ð»¬(¬-Øus¡ÏÌLÃþôW_ì"]8uQg•5¡ðôZc¦i·Œý.¶ ,êÒÅ)C²†‰œŠ$ÍÁÚq½‹w—iš.`³-rÔ,@iƒGsß’ ½&`4 €I2‡ó.-@€N¶\Vlwrºm‚@h›¯ëtÍÏ¢~2-M$úÚV8>5ÇáÈÑØü7Š#K!Y×°MI¤zžMBÓ Æjç)Ó‰_£|Cð—xïæpÚŽJÔÒ$4ô’mj™ùØ«¢/ÃE«üëX…AZN.‡çÎN‡ÎJ+c¢’ÏG2ñ`ÎŽ$´ÐíbÏoPö™w…ß­°ÐÛ Va]wù£¬Ap$Z³´Å%Kk–·Åþ£Щ¿‰ý•tÉ8—QÙë•Êý/rNFzÖ'̨h8dËõ…•[A§C­é>T,nþ²mÚîcìköäÛØ&È¿{…ÒJ7jK<Á‡w,}óI€¾q~®à+­G0M>í§Vƒɵÿ”3*=ܤ3ú+3 ‰Ž·2¾%N$ü¯³©:ÿíŠñðÓ®áŽ{¸SQö¿ic§HÇ]¯ ÌMkU7Ý E9T;ß§BáSzNäÊýì&ÇAËOÛVØ{‡¡Æ‡èªèpê~¡Eò#NXÔ"}ÀŽ{rœç-¼"bÉ×Ðãj.•Ò8L½²­Ói7¿Ÿ¹ñ±´Œu[¦Lnn·èfŸÆ8X­Ù…è@S™QQ’¤ñ§yùG²ÔqáæìH­ãy!FÁþ—EwÍ\ƒ™\0Îvð‘(TQEÌK`Èž¦8jQ¸) Ùª0¾úì ’ª¢)J ãó_USFéóÈÚ_¦y Ô×–Y{®´窚Ü$¬ñðwý‰4jÄìùGP2°ãë!6·×K¹Þ~F¢™oMÞ²¡ÛüFâ°û`b/h‘ržiF;y×Ïáp@Ÿx{^¸;Õ·f44P[_Ùw„L¸Dº›zñW5„D)¥Å&‰‘Rôð ãíØ.6À¦&ä^‡/O& ¤!¬çagEv½ý7`w_ ßXñÄ¥¥ŒÌ3}ǯb)Í®”cjmË +Îrã«P8ÒŒA”ŒÊšcyÀ† »Ð4259ƒÕžþP•Œ‹.ï_,Âà$ c ¼œ÷œ~ "UÜö ^ýà€píÅyH8Ù!0‘EXcâÀÚûï s‹g¨|ðä(j‚æPI9ù-È~ÅÅ¡ÙQ­ð" y¾%an–,=ÞàÊdÄðpšR½{ë•­§êßÛìdSj;gäÏs Eð[÷Ÿ–à«”¨¥}މ¬n3ÔZU“Ò¬-Ò}âÈjÖ‘þÉ¿ êôSqJÆÌ»z¿g`[ØŠ@Ê-Ï¢D7øê: ˯òPBÅ>1CAÝ®FZ/;ˆÅ ŠnÄJVÛÚŠE ùx£Îê_N僓E©PRß¼Y¦é+(ySü0 ²_µK•J‚ÿ¼^1 ¼V ˜œØàJéžjÈk3©òÖ‹Ý9™Ê¹V¶c›°$»iÁ.ÛO4el.Ò³h´ÅøcÙTqŽ›4 ¾`÷÷n=á…°õê†ÁT€(wG@‰hxÍn_›uxN‹R.޽Ú(¬Ñ":J!!.²yæh…d¯p6è£ÖÉø Öø ¥ÿñîeåaÒõKoÕ_vk9®€56›¸ŒÀ~ìœ=H¡hä±ö¤“Afã*»'GÍ£#ñR)“σW )ÆÃ‘o_Ó÷»»Ö½a#¨ýªÊy+'âŒì®¤Ð‡TðÝ»~-Á9 º,¾FíÁ¸X'CŽ?©,VâÄ2_þ^ÞÁð‰ü¹ë ب¨[9„a"ø¼Ü‚NžˆX’É´×)<_a:lEFG…+ VvqÀm—8¤ØUôo9ë3a© ?lRˆÀ›ˆY—ÆÕ,&ñÄæzí+.Ú'ÄMô!LÁºÒ êÀv†SkmËÛo‹©ü-žîu6I¨H•€*!}þ›?Ûì@w•— #8!6Þ‘~š>ι/?[¼ "/ØÁÿe&Z{îr MÒœp8¢~ÖlÒ6£0#ø| ½/9ΫÛóýgòón¾Añ«5SUþ…HVMþ8ååÃ|-•³eëït“Œ"è\EÔ–?¢eÆsQÇ¢ÜÓÈP8S/U¦ Ó5<(BµÔNa&ŠRÀC3Õù$þ©$xú*ídÿ'¹6öæ–i5gX3ª|1ï) Q?f”²R€M“É# –ˆ½1ߢì.$zô@hZHí?—§&–tÊ)¢‚ÿOA=r<1æ!þ‘úЋnòá‚}…„ÎÔòöåó’ ×fä„o‘”ÓLzÃÃ÷K- é›  ÒÕV%dk½j‰TjJóóhÍß[-Em5Ôïâ`в¢‰ z•h¿+8:Œ(G¡AÞ|Aó/ Ðv ß9| êšCÝ&Žq3Eƒ£‘0µVÕÍœl¬T=4°„£RL±ÃBR7@ö)÷¡M`*ÞbF¬½ƒãƒî¥ ·:Ÿ6,}`/(›„á|žBíαÉÈ îs~Þò—ÉŽý$nq?ÑÝcð®ÐÜX%øOgÙ]É8©¦ûÿ¢‰÷gž#E„?~ÌÙ•?Lf(rÉf­ ã C íVýö^bCÅ}8Eª_CtFªÄÏ‚®ÓNê›ÊBQzS’fZ«Ð H3[ÒAjzž÷u$H¼f\x΄²¾é?ǵçºHY–î.i±"0FĤá6Å&„ìâ¼@ºûÆÑ{“Š y•k¡˜î|0m.Ê]ÿEáÁi‘ðäÆÿ¡e†h™,æ6/ÁŸp!çF€foyY\óƒú9·mKiÊü[6µ:>ϲ¹±DÖª#|Hk•²¼UÕô”ìFUòî¦f©i=#ö½«Þv)ªEí2Ò´—ýnìè2AsU&ÑÎl¨\ ¾‡Q  ·o×?ÊÇ:“ú'¢Ûäs¤ûr{QKiþÒ)5Þý]¦Rær³•O­Âv3¶Âű™äº݉:ß´{W Ï òž¾87}¬W_”³Ê§­lCÿ í£‡Šå´9¹u´Þ¦ƒ£³ÕüwRm›dZ>†ýò!ÌkÃNi¶¾ ˜ÞÌý. Ä<Èå¬ ¨l$£ô4 ‘gv_]C¦äÒ~á½¢/â'/Ö¾‰˜õ?•ýÿö•æ<à¦ÝEåÈÅ8¾lùIÉq¦–’µw0¤LÇÎ|]±ÌQ‚¸fba5žöb,¨. Ô·›ÍJŸ8«ë¥×D. Τ¿BÖE]˜¾MGœ… é1R{› ~ÜÒ£eêŒ?í:×-«º4»Õæ<¤ÿ;£¼öÿQõWX¿Ï"åUëUáï„öͦR÷XÖ©¦KNÆ%J餄ìÀmcä.YóGä=c»S[«¨ Æ:EA|¢[ª¢éðL‹ì"o' ¼S0G‰¢›Hx¥ˆŽþ±Šdß”¥ÒéÔ!TãлÎì‡Q6~×ßB†ùx˜ÖKàsÏú5Ș9 ^ÙjP1UE_`Í1@¸{õ¨Ý`«<ô›9U¹¿å¼iW Þ•¯ÌÂþ³”²R=ê¡%dÚkÕaD4§•¥æeÎë‚QQq p´R¹, Ç3¬Áé6™¿ª\.чÈè`‚‚Ÿˆ.Gln×~¡¾±ƒ 3æëßt €²è«–†’t؛ﯢUe‚%“PŒ\>' UH Z¹ì‰ ”wæ—AëWR^cø^¦™^Aa/|¬€²Ì¥”a(y9áÃ8á]n…‡ƒ7лD¶£›¯Ó}›¼mâÂØ8œo¯á»?ˆaø›©R/ŒÕøBZxŒuç]‰e‘MÝèéŒk«ß3˜òò ùsÐWå>ƒ”vÍcûlßv&ð‹¸íG<’}Çc‹žX)@Œì/ YT`{;Li—ƒ—kå:žŽ|è°Ü”Âà•y»‹.‚º›¨Ÿ!i£(%:”ܯT‹ÓU?’í¡¶y¯p‡½¤ätK¶ÌäY¿óx‚”R€RÙºk¢8\ŒéæK×XÎå[”^Œ&ƒ©÷Ë*æ¶YÑ£.ˆà† çkuòêÿ¨n¹¯æþ”f±}™¯(Eïë8üÉë6?J\I ÂüUô@u9?3¦Ìo`Þ‰:Û/QºÁAs½ *bô#æóbmk¾%ÐÁe’P#TÈorHŠ;,‘‰¼– `ÖaZ©«*—’…pÃ>‡'¯þøs+ÁV‚‡)’++//˜âÄ…m¿ÏCèùÆd%dtNÞ* Þýû%X~j£¿pÏ÷p‰ˆi|ô`ãv´±õÛ¼s¨,N¹AÛâ<™¾dˆŸ#ì_µí€¸©ãSnÕP½›ùÇ‚«õlˤ#ò‹¢À™ E°beà:û'A=ÏÝ5!ô_§F ™Fæj ñßó9ÙTÆU샡²y•#€Éjçô$÷óÌ}b+ E=ó£Uºn0b LþŒ”ˆ‚±Écv„¤5vM„àî ai¿¾ë˜ÖIPÇYÕþ¡›¢Ø'ÉŒùE#Ác_‘æÖ*ÆKþ®”)4@0ý³º¶IhÐN®Í¸4q iÁÀçíó§{V®p°öœµþèY°“þ± ÊIæ³ûœr…L‘ŽìÑÑ;^߈¿X@F%ë¦só-b?¢û± +Ý-½Z©Âö ×t‡øŽGCŸ–ÅÞ †©áD‰8úOCiKÄ•qI¸æùnÜ÷ÅÔÄü\åóŠ= ä]®b“Ú!½HÔRo£ðoø DÌ&Q®<ùd¥ãH¤fÊ®/æfUIÝÕ¶ž÷`w¿àòã^CîK§ý<œsï|“£ÍôŒï  M?†ÖdµQ¢ïðÜø2¬VøŒà#,ö­cjÇrnÀvF’t‘®’¯PÒnTRGÓh“»>38€‚ô&æ3ÅGÛ8 cuRÍäE Åj©\ QÜÖHÎñk¼é°ë/+~ºEq×Öõ«Dá‘`n|Á¢Á¯J÷ jÜàçœèÆë)Jî20øF½ýd´®t[‰J› F)XZp„-=)°Å·•Ý0™ñKZU­ê¡DÖÀK}Ø}èöÒH_–ëü ü™cVу$ UüÛyOäù}p±ȉíWWÁ¹ +ý;ú¨W%÷EÜ)ßð¼´7F™‰S㨋Ÿ‹øZ*x»L|DNMgmÇ÷’öM¹Iç“v¯Ô…¿ñþ¯ZûÄÊdVEZÝé—¡ÔxÛJµßŒÉ6ÆÁ>-h‚Ç-ÐÏ·N$.D2½ ,kšÒMîÖõ WÚ´•éõ~3ìT:WÁT«’$Úùëy£ UJ»ÿ(²B™-®ÒI_Eå¨G+”_íÏuXlÔ\–ÝÕ_º Ù=¯ÐÎ,r¢±"V„ƒ àäÇ5÷ò‰ê¶Õ­ky<ÜG:³p$Àº9zXʶÎWÀ9éS½ä¡âµE@Ø|¡ GîÝÁå7¨tVÿpù5q¿ƒac…?ËëÙ°KÔ Ì’²óeµ·T­mÁŒ|ø?âH¢€ìUÓjzΆÏÕO~kà ]é: BUÇÿêƒhG5FïŒ R.¬Kë°¨QvcŠóY½¶á$(Ñyj)ì»±` Iaig‚ ŸÿºYqvÊÜN†Ò¥€gs}«P·Åw†-ÆîX…ÞR˜«™Š^ÛÕ‰§D!l|[3Ð;éëý<¤ü_šŠè„×cèL'Žq'VáXla2ªLAº.Uà{%qSU—Öµ7ÚW™ X¥\w Ö]Qb²¾m *±‘¬ÑwЊø ¾’É%©Šgà ˆÖÈíŸæënª' ûŸé=¡ܽÆÕ‡^«¢7ÏlB#‹ºªLË.AõcuÀb¤× c :™¦ÿ‘„†™;ûõGoשz‘¤“Øø½>qMÑUØ·¶zÞBÓ÷·YSKíte¥[k Øl(¿²7DWGâ\þÊÚ,rZ‰{w41àT´¥ÚÝ4Qr³ÉMfÅêÛ±†23Ñv¸8ùúŠˆ'Šì?\» =dxq³-Ê$¿fwF&³‰Zy¬£–Ñ8 :`H\T¤óŒD>ä.Äì½)°*ÝÔö©µ:#‹/|âJå(»Ôè©¶‚U~Ôâ01j¢ˆï,£â‚=W"‹ Ë0ñïÉ~|ëŒÛ' %EÎe5êzJÁßÁ.*h¡î5âó‹ßPý Æž?'Ü÷ÖMÉF_}ì4Éñ I3ë@'QªÍд,ÇxÊH0ó&ÜsMùºt½&øÒoA{—ÑèÛ,ü.èÑmº>âU¸!Òd’ø9f60ZË*eº¹žyáfþèæ­ÑzI$ ±ñ´@S›ù¥¼­k}Æ•ž_®…¥¥ðL°×s‚à„¤ì}aª½ ³ï¨³-ûÔµM!ÄŇ¹MÏÏ«ÑÏ3ܶÒ*ƒÎ¤Üiú,P*±ôÊÛœ*OÑ3÷¥•íqHrã-:ÙP—Xs‰[ôõ"çÕÖ¡–(€—NÒsÍÿϧY«ññr˜G–búp_D26ñåГÛlFî(z¸Õ‹‰Ò’ZTY|ÜãŸÂßjpwƒqMW ÊÒ\T_äE]]Pî4ðL¦¤Ôã¦jñŸs8HåV“Ulî+8/€æ_ßé1uìå«c=\Hø÷¾×ë¿,×;Fº>q7]S€s2\µíˆØ­kœ3út°jÍÌ–fBæ:ý?ª!l۴ŨE¦X bÔ”ÅÆAlR.ò²e†2÷?ØeÑ®öâ/¬6¨…Q-GQÚ%‘?r#Ø«fø`Îý¤ økßš€P£ø»˜ååJ±öͼ·—N..ëQL £ Àç‘èÐ á~½³”ë÷@Y½&΃èÇtws° Ô¬‰Ðä)‘¢!…4ËÐÝêÆªû·rÑÛ< þË 9ÜWìeΪHGVIS¯I‰tš.ÉxhØxAdh!×ò™!¥Ûÿâ&ü¸9þ|ú߈®Ø÷Q‡†%j±&âC«d 4š÷§ø”²ÿ¥¸ý`SÛúlÆÉ±oÉеLLÀΚJºÓâ#cý2LøJc‰¸x_ï }½øñ=fÏ숌È6bñÆ ²Ú‘ ¯î O“,È´-¼XìÈ «¯ " TˆB»ÂŒ'õÊè’Ëu¢×Z:! È‘–g IüwyQ»®@øÍrµWˆ*#¥ür¨ä­P`zHU-¯H‘bœç&qŒ£8MÙ÷Â^·Ïyv9—Æ©0uJ<{ìºvàþ¤,›ç¹(í:3‘W¦¶:o_„ªX•×fzŒ’ìRzc^–Û’KIOqYßù;[”¥]ž²ÇgcÓS¶ôÕDZm›˜ôõ¢ˆ+o›Hû¤[¸3–’6ÝzÊéÖWZîcx§Âhº0ï.+OÖpÕáui(9¢€o\Y­(ù'Aðûìs,Ž–V¯ùÝ9ϵll÷VÒ–¶ï^—ÊJî”±9ª°áøVdZ¥Èó]Ϋ’ùÙ²m¹/˜´ ©Ù²UäšñæBâÕRÁŪ&óIÓ÷%›ÕÝ€§K©x\üœí{âd-Ð8^ÑÉCß¼¯+ÓKÅ®EŽXý‰4ñB×È"ÝZÈÌ-Iª9J?u,‰­•‹äÄ”wdl*M ¥èÖ€[ŸÛBh„Ll|ÿTÅ7.Z;­™AÈvÚs!× ümQ¥Ãóæß·ú®ä×=7cXm?¢ÿ©i¡BÉ|ŸzCš“:iÌ"4Æü©bÇ–k–É5 m–ØIy޾Ìp*h`MµËØ£Zšò@žUÖ–²½‘égNsÎåÁ†FZ•È`€4Æ'óÊu·å‡µH^&Á‡ñFYÙ8bV±Åí9}e<àîóªE¿xMßGz«îCPȦL3ü/Ôÿr>Ãÿ_€áÿ…=0²61pt¶³1p´‚úæY’endstream endobj 7 0 obj<>endobj 8 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600]endobj 9 0 obj<>stream x¬»cxd}Ÿ?»c³bÛÛ¶mÛ¶í¤c;ÛìØ¶±¹Ÿyffwv_ýw^T]ç÷5?çT]U¤„òJ´Æv†&¢v¶Î´Œt ?Bv.Ž&Ž´r†Ö.&€o"+ )©£‰³…­°³ÉO€š‰1@ØÄÀÄ`äää„!ýV³÷p´03wP¨(ªQRSÓü7倡Çr¾5,Ìldß®&Övö6&¶Îß&þ•LLÎæ&S k€œ¼†„¬€BLV fbkâh` wùÎÆ madbëdB 0µs|þ9Œìl-þI͉î;'€ÀÉÞÄÈâ[ÍÄÝÈÄþ ÀÞÄÑÆÂÉéû`á0s4°uþ®³ÀÂÖÈÚÅøŸ¾é¦ßeü6bïh÷-aóÍû6&oçäìdähaï øö*/,úq:›8ÿãÛÉâ› °3ý–4¶3rù§ß¶ þ1óÍu6°°u8›¸í†&c '{koß߯ì-þ†‹“…­ÙG@p413p4¶6qrú6ómûŸêüwžÿ¨þgöööÖÿÒ¶û—ÔÅ`áìdbmJÃÈôíÓÈùÛ·™…- ý?ƒ"akj`døº±‹ýò\MÿU Šf†ò;c;[k€±‰) ½¬ówÁÿg]¦ü¯5ù¡Åÿ+ þ_iïÿ¿æþÏý÷ÊRüß.ÿöùšu±¶–5°ù€ àßóÿ4°±°öøˆþO5“ÿE3kÇÿÉ–p6øžn[³o4 ed¢ûÔÐÂIÔÂÝÄXÞÂÙÈ`j`ý½úÿ¢«Ø›8Z[Øš|/ê¿pà[‰áß:ÿÅS6·0²²ýÞ'ë°LlÿÇ€ÿ³ÿ ›þBè¿ÜÈo²³²‡½ àß¶Õdì¾aã߇”íÜ¿hÙ´LÌìvv#£÷ÿ‡›™aüðÿÑ”1pv´ph1Ð100¾ßÿóõß'ÿaFÄÖÈÎøŸ½Wr6°5þ‹ÿ"ü»(ÿ€¿„ðwºL œ,ìÿòeäâèø Oÿ‚ƒïüaþ}þ욘¸›ÁœDC/ëöÓÜ`½lXd hi±^X°&Û(uè&Q?-ý º1• ·ÈrntÜ̱y!}üs vr3ÄÙ¸^vAõ닚zS_+fLö¯©­áä Q×»— ÈËÀÝ5 qž;óõbn!'¼¢½Œi—‚.¾@žI!ÓÜßV¸qÛÙ-ˆÇƒ2ÛŸJëó‡†Î¨*tø”ÿÊò³âWÖ†'ŽF” Þ%–€:x¾M2¯% ih¦_¥­¸ƒ~s]†3Ëú&髊Ø<ÐÕ Áüþ*èÒÂÊÌûìœ Ú)èöÐ)ÿ=¡¥^ß‚£)×;îkðÌAo±EnÄ» þ@šæÜRƪA^E÷÷í¹\›öÈzC<ßÜ౿Fn¤ýc/*¾[AT?JòÆp„éž/­{¨AƒƒÌ‹L«ÔÇ»8ÐêY¸8ê ½4ñÞÚл¨ú¹…4Ë€ÞI)ä…Ò/Cÿ/&™ÂŸªïižÃóþgdG§¯“µéOj\²lž¶2$µˆáE¦þ«¾@×{§·úºGÁº¬Í¾¥Í@cv⽡­ßÔR _úþáR"£*-@ø †Hõ–Ë…3ÎÓ |YÊE0lp­ š˜Ä…õ°H&í]ÑZd@åƒö¡èV£äxPľ=½ß”4ÈjôŠfš›¹ðúðƒi®Û´m±æWZ"ïéU›ƒ~{Àa¥£Žlk}JHø1hÔ­–åa’B‘¾GQY¬j1¯â2¦Ó1/P~¨,Ø|=ã-„Ãoÿ¡4¥ð&IR'§š™Ý5|™âݱ°$ÿÓí=Ú4€ñÈ￞ú?T—ÙÀÛD¹ ùõ­˜ Úo©ždŸ3FÀŠ=¸‘×hQî;pœ®Æð ñçJtL5«aæ(ZöVt5CàcP›Û&°jz9k¬0¦ƒÁ°Eª¦þhÅ[›°»ç×EL©#ßÀÌ;IÓ.ÖüaááØtrÓÉàªcæÒa>” Ö €¿’Ej‰øÛL”[”a…FZ]}ã@Ôûõ€ UáyJR%¤xÖ†?Ó§¶+èßÕ÷aœ|Ÿ|loX6ó̦ÂgÇÖ¿S/ô Q+•1hßB8 “Jq{Þ‡$³’1-2Î.{l¶#øÚìçù!ÌÔfjëºëxÄfôG¸¹ d™&Ráš„pZm W°=FÉ"ä2 !±rÈ÷“·JîÂF]õl3‘NÖŒFª᯵{ÿŸ"œáõ ÷ë­<[âì#Vó/±¼O•ª¡;‡tü/Tž¢6{Š”üÆJ.à—w/P£gIò6µ4¯»á7@à%û´´SBNØž{æªR” ÑÕ¡ö†Xq˜œZ½ï¿)<©Áàž˜ôòOÊýÂ=ê?oâRÚ"#`cÚb© ç¹$öúaOhG+øˆýkü&…PÔ£Tõ"Næ—£}Ž)ãÐþ99Æ´N&/%WBà ð‹?º.^$›ÒÏL-uúÜkóº*ÁÈth6ö¬"bÍëÆÅs/r”U”î:¦øEðÅZN•>x~¶[oWéÔ±t½rݬàâ­¨æé@“ló¹’ž‘,¿r­¨¡|SpTýxºÁlý/ô6Øc³ŠiP?‚…Ǽ”€»·CÏ6¾¸(ŽÉd‡?tJ·ýLjËQ„§(Ö˜Õ]NžëY<…÷x^ó͈©´y-Ž›"½Ñ•ÜÉGÙœqBßÍ]xkŠLSð€ä×5¶“»˜=Œ}£>çuÊ—§i/D΄i{NðˆÎˆ"ÿÞYE{·“CÐêϑ㗠РÆ5ÅjíÚ]Ûk„ÒgïoV´ëVJ€µÈçÚ(xÙ†;!ÿ]tX¶è“›wÿ-¤\wZárJضݮžˆµ£ã\ ºÌ0peå%/*·"[BiŸÚhCæ÷Q ëIªb4­!O¯â¬*‰â ÊæìÁ+ÎòiVÚ#þ裡¤òY1[qì(KÞ™R-.q}Fàü7h€ÌK/䉈†¦¢ùU¨sÙÏË®Ÿ,•¬#Л_Ú¸í 80m&XæêxERÜð62;ÜøôSd¥T29œ ¦xa ›bêÛ(§ßsåK’¬€ªry¯”¾Ö±Ç˜“zÐ9îÒ1j‰8¥]òÌ–¤Â‹dUÀ7—W‡ZÛ­òÒnøG{á¡ðªKÝ9}‹ŠRн6YvãºPà0Ì™ºz ­¬E±'f"€8üjD_³ª¨ù}P`,÷eŸ9Ò;ƒN„òHÀåï>'·u|G”mZä† R¤Æ¿ÛÁêË:Z‹–ŒH“¤>'¶,¢W¸®×@#ñÖÒ»‚º«³ZêS³k%} `¦GuŠoºvõ ™i·V”/þÌ¿Ã,aJBòâºò7 ëMÆPÁCÐe]Õ—Ÿ:ÛkZ.e9 º!G‡VõÉþ¿ŸÇMVfy¨Äx„>åóÔæ‰‡ ©@ÈšçüÀñÞ•©&êøÜiè‡Zø°Tßt_Ê+A¶ý¸äÂNEÙ<á·(¿.hH(¸‹ÿ3¬DîÇ¥t«8‘sÍÄ”Yä`-Ï¡êÿ¥%»(A€–ôbå…œš„ƒÅa/½ª¯ÄÓi}_ñ6^ š3N×5‘„xTOž uá@œ$êR¼>Æé€ñ ºÕ‚òìŠÀ……­½~Š›§1R±ÉÏ¡â¯ëá7evïSÁÞíÍ|ÃîzêÁ­ˆ±ÌÆZ1~ÐëpÊ?Zcó|_ð+ må®Äñè§@ŒvjЙµÝvz‡-²Œ«!˜hœo:׎ÝÁï©!ÿ‡`}|LLº*†€ß©Ç ¼™ˆ²aH4¬Ÿ¶ù0‰rK1TÓw¯—z³ä°”giA{9õàu›‡Bíý¼ãW¹‹0<­4£©ŠPÆÍà0ž ‡]\Î «ÚÙO‘¼ëͧg$g®=ÄeêH7P‹aæk)åÔf½c3ôõ߯9T! Œí£ÀÐ’¶9-a÷[›íNQq§¤æMËCªÛ¢3,Xÿd©=O è&~Elo¹ìmSU™oAHã¸gšëíè*€m›æäĪ˜—®º:Ô ƒèë ï'rº`¦±«ÛÕËC¾Õž ”P’{±åÇuÿ–¸âCàX¨žkÚÜNGoI‰;ã6:¡yÄ0ð4Ý£’0ÆË‡PÞ«æð›Ñ¯¥’´vi—MÇ(h^¹ZC^Ì>(Û%Ú𠸦'.Ÿ_dG“sÈDE’é@¤>8smX>I‡„£óK¢‰¸ÐăÅêÜAkJÈhÛ¹‘0Súk…‘Æ$§Û73|k7¦ó«Ñ .½²H·OdÜE¶Æ;ìŒãWƒÈ«}maDã÷Ϧ¹Œ\È­:9EÁ„rco6YïäY쥥`˜¶!䲄Òô„:ï írïm6kWV½BkÁBÝFê.™Ì¨.kö[õfÐû©¥F?¶ñªÑ!«k³¡œkÀ)‹‘•†ö$„äï»0ïxs315Æžñ§]]‚nËÊ{åþ°SÙ˜†€x4},Åä·ÄÅ'§Œås×2î…Yð˜.oÛî©6IüiŠæ¼øm'§ãø§(ið¹ãU˜ˆ]9ß~£RÙ{–`±ŸÊdÏÊnÀ …ÑUÝY{w±šÕpwê Ó4UívÈRjØò\Ð7µ9»»ø ˜J»sî2ì²XôëÑîdðn̾qïø%{[*¾ï;-„JoG µ ¬Îc·!M2Í«`ÍúSq¡ügèd6Yð8¢_犱ÚO‘2ñêTSj w{ƶN!‚V•ó žø]1HêMKEȪSütãxeùR—¬ÅÈ”FÇR©ÝθÂêUËT>Ju×QÉzÌy¼NPäp¾Ï¨uH"éÁN܆Ç!l¤Å ŠwãwT¹,º¨MÁõ¼Â.ŒÊkO÷øª_"Ï©À”¬r©;øú%]Ôºü¥žð… !Lϵ->[Ðrø|P«ù Ճˠõ ™ê4’Íô .‘ß)oª½ˆ …C(Ùê'KÙñÐ똪8*§K"$©A°'pÎYhZF”vÀŠ¥ÕŠë3­ZéÉ;¦þw±ë[EB¡™ÚÒ¤”Û½ÞÇè¸l˶·!ça‹^Î=ÂëãrWOó]#÷»Ì¬^âèW{ë9úWè14"M¢ [šãzqO ™ãèSº¹–Bñs¡ë#;´¹9©ëØš óÙ¤¹Ü»Neöq–ÆÜ}Ñ!»|H:T¬›dàßJµá¢rŸågË•­ÎšéŒ¦?÷ßé¹^ú/ sh³ùÔtþœ‰|ö¶‚ònF‰äV̪Éò ÉÅÀnÊ¿ûÇýüpzþŠ#jÐÕTbìíÃæŸ-pÇØ £&Þëg:_гɩ ¸]úä«·Æûnåyöf™+¢Þ8mbü°<õõ¬úá_ñ7ù§¥o £º«%yFÝöï®öyïß¡@{ \À¦¯›1GA‘Òj“vaªÞ’H`…"ÍÚ)cçï÷fãö#ßwáìf³™Í%ŸØnlÇ%½þá ÝMòÝL4z÷ö²‰£l{†‘Ó Ðä^ãÁY7Lâön.3Ÿ,nŒ=õ­âØ_-§£ù³&^—D¥›+îÌHÎ~†ÙŸ-h: —Ò"ôþ[}a 0yÄô3Sšh£Š|>êÞnzý‹àCLÁnlUwÔtÛ·½w c C¨òZ'bLôT ïšM qÞ ñ˜Nì>žê¨p4ë8ðä—£cò è?‘‰¦|‹fü„À*Ù$+t}ô¨Â@>þ,: å÷ÑW—5OŽ¿i"s 䂱‚(”¡ÃS˜.i È'òòo:#ãJqMÁ™q£ù›JRygQêËŒ’ov÷„ ÂM”¥¦¶°`7™)ù9 Í3Ö­ØâÏ4•ÑL¤Á"-õ¼jG#ÿ‡œ¦­â‡¾0™àóˆßvLAMÔ´7ͺq"ýNråâÇ ×qÍßÈ‚gªÃènÞ%äìÞ?Ýò‹j’ûŸ&tö÷ö\Ÿã¬§›X†IŸ¼ÅfÙí6h±÷Ò?°ã¸$œk‰½µ‡ÈH@æÏ Xá÷¬û2`ÆâÌÝ f°+€U-)Æœp­‹gæ—?Ü-6­iQGôó}4ÞOüÓÁ®Öðóig®É³ ›}¶$-bš:›ao·KmrÈÆ#HÇlÔYŠ+á¸(}ÐìDцxbÈãÙÛíÍð›T©'”ÔÛ²‚‡ó‚*I[ª‰Ø;/Ž–‡áxFõü‡G9Zwpà€ø%ÿv$þ4çé•‹Kê=F3[Æ* gÈøYàÌ–ð19 Èû. ª;uTñų„­÷ÙÁî+}Ef† 0U XÃÍ&]‹*ú¶ÜJü‰¸9qåC$Œò %:o[”>]” ]ôb)²‹YRäÍõ‚]Oéç¬Ò[÷6æo>ÏP^2ÊCbÊÛ{›ljhîmJ&ð0{+£Ýº§ŸZÓ0´ÛéˆÜ²PÖ³øêF>“wŒÓ½“4캪4!Di™Ú›,нÆäBÊ‹ë±Þšî  ù mû.@31^ }wÓ¥÷Ül!…ý’>MÀ‘éªr¡»ÈS0UæKߺÛ>̃fê«H²Zà‰•&¤`,ITéUå:†¡áø¶ƒ*á‡Ð¶Þv˜îÐìGJ´ýåãaŠ:üÑÇíçvf±,ˆcì@h c î~ ý¹‚;zRÚò+«NJ ÓÄÉX;ˆ]³«-AO©þ)OÆ@JµÜä<"€äÍÒè2ñ'ôNýÅú0# ÒÆ¼s,§ `Šö[\ƈL‡BáW©Ôi˜ëG!S™ä)òád@›äŒ[Ô|zeoPòc.DµÝsõû*Ò³h1êGOEþÈvE®ß î!šB†˜îM¾·/¡3•Þk$ç-pê—õoRXËõõ,\Eyû‘ùÀ›<.Ÿß}š!6ÈÙÄÒÕ ê•Ž~o~‘ÒnX1ìÎŘ·J%°/ݾ§¿mÜÒÀûëe!äV­ õÄ>‹z‰“rÑEóÁ #ó¦\·Àä¥î°§Îë?JtùN‹0ÅØ U.eÓÀÔæA™þ{LF†Jq—»꧃w¢èg7ñXTŒè–½¯–p<{üdÀ²{mòšß¾’0­6$ïf Dñzé×Ñpeù€q¡ƒÅCðH`;J n×*³¥©OÑÞºžÌc§¥ õñü4ç¹ÙÁ—C¸ÆK‰ÛCE¸ìÜì$#Ý Ý>ƪ¶Ä¨©C=_íŽSL8ÕÖ§á9.twó$TVÚf4‰÷ÓÆVAr6›á ø1‡K5CŸ’…Æóát,{m.?û“·ºf·è &K¹Êg Ä,ós¡¨ÙÍÏÈ2ƉOÂ>ž6z^·&íÝøG,غ٤B¾eF…Gì.Ú¸Ì͸£”®tZ ¤uꓵ#…®ËÏ¡Kà¶X¸¡(pÌ$ŽP*}0ϨŠ8ÎpV†ÂƒÇ.—Q+Rt5öQÎßw§=÷¥ëÐýÅ VpÖºêöã¤á¡>÷rARJM-BhSŽs§ïF8&è´6’#5äÇõ ´Š€œ8ÒñbbqY¨Ñ^á0üd×]‚iÇÉ÷xÍ»%,ŠsÿD«Aá` ~ù’Ñãj˜y\é1<鲪äÎðæ)®„=𜬟¡ÆN¦uªyY€x$ÇJ >Miýmõ°ñÎ+¢§™4G¸_…Upß+!8r%œGž’íöמu„z]%7(æ´2;f%"ƒ~&vŒÄMsW›>­!¼­Ö-_ ¤L”÷‹/*è7ïÁ‹Î’¬LL×õB)|–iR"û€î]Ãg&¨íÆO7éQ87õC§®1jVàvž l#ÖH âLÀ’_FJ$)û5eÖ¨ —u³•WFî¿{KR¥VÀmÝ@ø¢6¸#+ðLoº³ &™ó o ;Ç?m.’F†ž:ŠSÅÞ ‚ºˆ8—ª†Âß&=]%$­HÞK¬<¿¢³+â‹•áûQ25*tk˜ ³WØ Ç¦ȳÜ"£“UÒ.M´Ï62'c[™  W1Ûš\§±; nØJexŠíû&a¶±[‡«°¨‰¡˜óP}w™‘2stœk~áÏÁ¦„#%z‰T!À'À:FsÌÔã~þêU —k|–0bó>‹FݲPɈ×ÇÙ¤’âk$ ­Í¬PZÄs6—Èk•ëžý›ýJÉn:E›Óó¦å%âÙ¸øîž¿¥ê ¬)™_„Íýà L!É?œ™—^¬Þä\Nûƒ–SDhEzÉdü ‰%“Q3x³~Ö"²v¤q˜Ìð”RãO ýub…îá-AxÃD-jÖÕ¦J[Q4E z/Zþý)–ËÀŠŸ¯µ:Æ:)†¨$VL÷á¨=垀 KafrdT°à‰8«¼oÿ|\dí9»šlNwè1¹?*æUâݸh8Î;Bƒ„Ň÷©ô‘¢LÎÕm£ÕE\Bý6Ý;:‰1]MþXß™¸À6@‰.ì c$îpà^Œ¥-•èÄßdvæãL[Ùÿ\(ж§1&·““P”&.Mi´#?Ñçzíš~KÖkÔ/p*¿QѾç} o¹ß×Çeqs*`˜n4¤–UMÛXLÙg·¢¯ë‹™&!ML}WÔ\TurîŽè¡ç¹]`nò²,•m òUE qéúÂ>­ìœ£¦‡˜Ð£KêÝzÔä¦H{e¿Y«Â6* ~·Åþ*1k๿fþæøšds"àA× ƒ aºµŠ–ÚÉÕ¥‚Ò×7JjÀ~Jš¨ç\ï 䤒$à©N麨®F5_‘[H´Tüïv"ÛØì³ïÀò4„4¾†8L<á6ñžjíNûÐö"é0ć’Ì~§Ÿõ»”¾!M‰^"‘Ö‰ùúäa–Q½ø7Y,£%PmšAÍ{¨Ê }ÅÚ-ugÞ}–}'œþE6çËG×Uª¡Â«Úþœ¡äûpêäþ¹#ß/?W¬ÍôßjÅÞ¶ }£‡ŽGrâý(…ÓU·Y5Ž,š øx,Ñz¼½§›ø®e… ¦ 4w!9Üxd… ²Ê?zcŠÖh7yÏ0úÌâÖdo Kmæ¥Ü#B#o‹ ×ý]_zS瘀äKsJþ”K­!UNN’­çåÎó1Ô\¤úDj®RÜ¥øê»R'Qn4žŸ{ àìi‚#b4±¦©2e³GmÌr}*yJ|ä4oáÿü­-p¶§FøjÎÜÿ UÁý5 æã;Õ“ór¿vHÿ˘ä?psD™>R”xn»›Ìh¥^ÏÖi6—%Å=(™½LE¬V¡US?îµqD;7eXg9+mé*Àžéjý÷%n½þÝ7k½ÝEË–çT(vÉ"‹¡<#’Ñýp ¥"Ѩ«àÌ8çÚ™_é¬ôi«XÝJÍ£TÈ5!á —ìÌx~ÏÁÈš¥8Ÿ`ÓÈ_ǹ‡¬Ý¿mQ7m“reÎïË´®à(8P;äÈ« ‹ òîZ¼/‹¡0ÒòòhÑ/¹8þ,WË•OØj‘D,K“•ähñ;å=/Âý‰m[rªHZ«âcOÿ}¥ÝRÃQºÌ@¿XÑwÑXÔŸxd]ƒ‡Ð xCˆ­Ž¯«c[9‘]ŠYíªŽq¬çÊ3nXŒÔ Šià4T&J¡=ªTvƒ‚ûÚ ˆq)×ô ‚{Û|íA'ªÏ Ž!=«ècpé×þÑaæ#³ÔjPNx©×äKIÞ…=­†ùͶœÜÉçèìIR˜v3 ¨V§2Ùx̘j@lÌ¥K·ŽÈk_BøYv…œ´iï±Ì/WŠ5E×Þýnj&Rk~lÓ½¦ÀÃlâ“ò¨Å[Œ`áeô¶ÆGì°"Lj۩ ÈÞ€/ã’ðñ^;¾,}v C¢Õx–å™òžw£Õ’Ý"!³ ¸ñæAIs»óÊÁßlD{Û’àæ•R~5Ø7÷½4©:•4m‹Î‰FÜ&,—Ëü¾FJé&ÑéZžœgÛκTŸØ¤R‡±Ú쪀›¥áøWw ƒ¼gܬØÉÒ¢Ê#-åšSgÏ¢[U& t:fGÆ"/GŒ¹ŸKÓŸFõ›Èó˜Ró; /*îVÑõqÈ1ï °04¶\ÙqéûÜ”%­µ+Q+* #²^–qÕ~_/¦ ¥e]/‹±k’mäÌešVŠ­›3X‹÷‹KÛ%ÜqáÄ&ܺ>œ>œ2”å#¯›¢3÷?ásYÄÇÊÇéW%åN*§T¶x£dßœ1﵊–öul\é\ŽÉ^W“Âf‘+ìð>™ãÇÖ€ÅO—ô ¯W dsÙÖÑòžŒ¡e”z“']ÿafÑBÑ*az†¢îÃÏ-¬™/]0©tP?®J}Œ$èŒf‹!–é‹yÛ< u_IZ5~¥àÖÜS)3êø1è¯þ´eJ VÜG,vÔ¥%ÍðrU¬kO§üì;ñƄ٩– Ý‡±‹]´W´Õâ…”óÉf“xÛàó<>:Pšëö™®u¸Zóú¼}‡ˆáÇ5?̾¶xSÂxv.9gïnrÔép‹KÃ'løˆ°é2Rè6-ÑÎ(j4tÞÄQׂCOèÊkMÁU P/Ǥª W31[é[ IÂý%Ýèã86pš=~Ű­3*⌮0y>í§+±KI/vgK V^:½åø:·2Aí[Š·Køœ¢_‰=‡ÜÀM&ÛN xiØq ¤—›8šúù»RSS¸(ÄK"÷—D³LÆï? éæ¿³Ø¢Ö(ÆI@eZ8íª»«9•OÅHY "{‘›1&»ºÄúã¶ß~5â02XKÜyDZDˆõì(üY…ƒ©*ˆ2ÀV´€ðÀËAóQž’Å™Ö2·ˆkh ÷6”½†¯L¯"Õ<Áð.¿^1›×¡™»cGý¥ÃÛqÛ¦Èêá/Û`ïSÔPØ)9Žõ:L•¿°ßJtý I»ú~þè’§Ê„ë܆• Ž™”nÆÞÑ93eÁà2òx1§ 7bØèà écC–9Å%% KÙu¸x'¿'é"—˜m‘ŸkžÐùB;‘7*x‰œ®S±äi·¢Îy;EÏiû*ü¢ý ¯ó¹î¡y–ÆÐÑ…Øò³ÿ<7Y ÕÅÝyévÛ ²;’’Ýļá~œ@Ù K{úÔÞà9eýÇ%®[ŸÜVdÂ}„j_óž}-û8E);_€è7{,Y3%Xî|ƒ¼s™É¨7³bUŸj: Ð&Y5áhÀ0üÆg3Zbÿ«ÍÙ-·Ü¤œÔºÓ97Y‚àì’£aË7‡[ƒÜ(I¿×Ø„Òé°$¸BbÉj$7 Ê:š¯ajª¼{&IÀƒÙ€T—†á|¤ECZ\Yj*ú4ú‘r=÷4›q–dN>9 À!=ËO s}Lb•Í1Ly×QL¹è*÷h}î×û8ïÍM‰â­{Òϰތ¢¢YÃÀ—:ɶ+œÖàreP1ÄÆò5é°²¼DjÌYÎ’PÞÃó\;ä»ã[ïŠ,—u„rÑxì4ê¦0Øw‰¢ê¨%s ¾q5î œA1~‰’Äñ³…åŒay± Ï ûQ(ƒ»ó7µ¶‡Ÿm½KµÚQj0±Ü`‹Á*E*$£±×ñ­ÏED·Û'¥-_7Sï(ûô¸®‹A 6dV%ѺYå'Áròë45KØU¿]"¼~dqUô05ÇWŸaàCU äv«[*îoÒ¬X1¢‹vDJÑØŠs({â˜*¤È ¥Eù±%"†ÅIu¨g•uBKÿ(ÇÆB[Ý7§¨Nù@ãüB.é÷dMdqòËñ¯?Ù[àb½U05;„ü:ËAÕüå-s¡Bp‰Àj0ü£~)£=ÅŒ—sÓ—/öà>²2biû"øÁú‹ä®i£J"âÏ "ó~j ¾y!™LIäÀ «;AÁ ¦˜6V®Ðxé|Þ¢Å/&œjN)€ÛÎŽ•€‡„ËqïmýÎ_è´ãi~oý|8Ìä#¬"^!iŠ+×ÖäX ç†Ù e=w!}Ö|&ó¡ˆkê»^­è1©OzŒÃA³È ¥¤¤›õòO0ÑËÏ ‚[‘^ôáÕC³?Ô"I¬ù³èSC& Hû`νU#O¬~Ä›Lx§ÿ!× uÖ¦pQ:ÃbùA?ÔI¨eÀgñš”Õtž¦ãYæ•Ñ÷Ò9’.mÝyòËލì§Î!^;øÂoó Q=Ñv_R^Xbö=äx[tîßÙõ窃‚qŸN—JûÔ\ùÓÆô µƒ•‹”Mæ'Õ_!ZÙ}q$0ËsdaRÜZÒµ{ 9~Úf7ŒÑ˜&`«€`W)ûM˦J‹¸ÿ®b„dC–è4øaS¨i( kRm“*Ú˜âéZáÝï_ ±?[°€ÐYD+­•2†sx%"î½QÛ[,$³øSõ¥”sVŸAµt¡¨84«j”¼ÅÑËD"7QÙeÞâ¶±f¼*­¡¸ç¬Ðϱ—jÐ.Î}Ž{.ÿùÈmAú¤ãÚ”JŽ‘Èþ¨Ò6ýÉdäFG±ycb¼-¦¸ÈÛÇ»¼…> ¿r”ÍíxÕ\uøÍ;±_ˆ“ˆÜ‡Ùû üøèiËMJ»/²m •{8ê}Ú*"p„eŽ&½®y |«ŒXÈÕsѳÑ÷SK¥7UC(O$tÑA—Å](¢˜ÖQŠpåüê<ÞoQTÂíó@ND(pîjÍÿÚ ¬+(:ûªÒäµèlŽ|æîDƒ×>äøQ„•ÝdR&K2"FÓƒ¡˜mvYæ­¨vÍ úä|†'ÛíFËÃmXµ¸ÆQAÍtš«3 Ýüõ8g’úl‰ƒvV˜ðù.­´¡Ç¥kŸòžÐO²y!éãZEl\L™ÕYkXë «ôòu¤‡¦²ŸJq#tÓ ”P–ªIû:*æ¢%û›ã —b9ÁÊ]38Á¦öeFEð…±+Åü•ùŸÿ¤·¢ê7Íïš§ ˆ ù¬Õ9t V ä•®lÿJ÷ ƒpYœ2aÙÀ°Y×ÿ{‚`Éæüèlx厥ýåü¦Pë±¹GpB~c½SúÒ…®@ï·»›ƒ˜.uìÌ- qÏs?“â¤e:3…hg†£WÐ9ïmÌí^ç3~wáXc³ïŠO‰ßò"‹Ê(y4ÖÌ,Âj ¨ùØŽ»ÃgÌŸz#ôfW’ÔE*l¼YÜè8*ÛZ+€å²Vé…}ø‚€ÑVž\§¾,å¹6üˆZv«;GîBç[k×p<-Áç=©ò§ÆæûD]^àö„Ù¯û+Ýø2lX&i ,!E%+‹ ⹄ Þ£T¿ú9M;-ëÅõ;h L¹”ŸGNψ_À©´Ý¿îôüC‚'ÔÙdCu—djðW„z|À»I‘½¨AùžÂûø0£fè!½ÉœìYÖej–-âú´6:Õz‚íÈDæ×žÿcka Ö¬Î)%‡ö{úñiaæ°ÎúOÎì•ëNLŽû%ç¶î“ǧn™¬ÈUŸh ü/f8´SÔü¯9-®IÎD0B‚í"::cTŠæÜõ]s‰—–ãÅÝTá¶™¤|æì1Jñ¡Å2ujh’9wY«.‹Åé8ºÓ©MÓ¶+þIÎt'X›±6å~Ñ”ªòXtõ°ÈÖ  iW«¸.çDÚøÀ]ìLiêÁV?t’b@”)[­ž ‹æt‰ž¬w-L^Ÿ›¥ QÔZ>³©ÓÛ”<ÉZfœ£À>­¥©û¢¿¡ó…Ò×a/DXà ËÀnˆÓ ß¡d±1É© bkÌñ˜ø·ÁNØéÚ&·mŸhŸUÕšÓ ùcR|딘Ða2æ6ÍÝâ-3H#‹laxe BÔdrÆqŠ1Žª ªž,’ï<̽Y®Œ²ó%y°mvi#<’­ÐþóbÖ•Ï7šæ-æ¾m0妒ªZ Ü ž²bkóT~Kn»pô‹;¿*T¾µÊAøÆÊ•Ø!Ó½§§S¡*&ÐV &õ6í”ø¿Ÿbu¶gPõ±a˜m¢o:v;ð–ï²ùO†'°Ì~æ€}ªû0-ÌŸEÑMû[£Å²°roOý¶užr(‰ÈÄÂ,_×*Gê¿òØŽó2 D¾Þ­$ƉZ%‘ó`°2X¶!WÔñ2®± ô9ñRùÈ·’›Íû2Uxz‰Gm”V^¹Íæ6 GtÌ‹žtRDtŠð÷æOÏôˆv:+×ë’ŸÄYòZ÷˜E~ŽÍÖeÊ rFÂ-š³¾!éhÿ¸¾Õ¼ÍÌRÙ¥õÂycPf›\­l²[Ê`b‰ôJ‚¸å+|%Æñ G´„Y íÙ"+Ú®¨T =RdaÆ‘´^õ ¹S³Köø5ŸñóØ å)¬«ßâÂ&¹mꈨk? M%c0Ýþ`JÉåÄ¢ïA›¬[ðSZ0›ùUmS´Fð &¯rcG <i’ÔÅÀTdû ‡Î’Å’rWYlq Øz\v „ãÜH¤Òzq:²™|a#Èrñ$u[SRýP4M÷ú„Yàè/UwüÅ`5œš,v™tì³0Z’X°¸²’•å™;=Óó«§?pšS•P6¶ty2Øæû‰¡@X\¹·;ˆ$Þðwô‘dÔ=¼âé-¨N½í¢Ø Rtˆ5›¼¶¿ËUŽ:¬DˆÑߦLz/m­^'zBRQ’NgAõ¦ò´¼ éK2µM­n &ð3­ò:è#â3)ûÇ8C\T¼Ó½é“¼È£Ø&neÑv*óâ^žá0!§±»`j`MŠÙu£u9;”ŸÆi©P¾V›)@^ÙÀSY¿]Œm5µ˜zNWÙ¨‰xœ-„ f²”¬_Ÿbôqúç®[Œªé~zAd›¼Üv]8º+àN!ñJr£¨hp‘4vŒð½]½îâ€ë—|` ð nÏÂÃ'Þ–•î˜u˜\/ßüÝõË-œüHݨÃaâèë…ïZ9ªÄõ­¥ÛL…äŽP×gÇögêæÄ¯Hm&H µ;Š¢ˆ¹Ý‹Þ˜ (žî(O*—ÄyÆd¤Y!D{Ÿô"A«. ÞÁÈÑñ6ß W’ª@% =µÔc59x…ÙJƒÉÕI?ªÑJ®LuAL‘UØgÌØ¯û;Zë ÆN“\Y’¦)Óm3ÄŒ&WÁPu>o鋌s›u1ašPÖP[ìLg}NoQ¯ÒyQj¸yD¬ÛžŽû%VÑ'–í/(á1 ™#þ#éZFºz×sðу 2Lb„¿Ø£rèj¯Bv÷É«°ßϼ¿îƃ¿Û)M"`¶B™¹¼Ë$¦{k-©ÕÙ¥CU»?©ùä“ÏMñ"3…[Ñ£éËÌpf‡Î¦Èî%ol“ÃuyËB'Üd\J¬Aá•Ï% ‰,ѵ~EÔˆo“|´–ä±ÝÿnÇ"ëQRÐS0²d€Ìê}p­øUz„¾yƒø‹Ö™P_!j¾Dsã€Ñ Ò:ßPÙt{HÔ>_URy uC[mN™‚¯³ça1©ŠJõR½IñIÝœtÿ ¿Ÿ/€Ð=adÐNœ:ôì‹Ã‹)‰«Å& î]éB„j? ln‹w‚êWëtI(L;ÇÜÄSHN¢Ñ̪\UÍb5“Ì@ógàQƒÜnF'~µacîÛ¸?özj´f¿3ïOðÀP»êJ»k‡N\.?â¨æ/ñu˜ºÑ!çÐE¤èäpšHÓjë,CÐÇç&”:$ŒºeŽšZÂìMe)¿ÝWT—€±lý C"׌÷¡>6»P—40ýOMLÓôa Ö`#ÏCѨ3Ø×S¿W5gEck€ó|9ò÷¿J–Êñ ÈÇ4§f_DË–›] —”îhK®jT)9ô ¸>6âbðÀTÔÀáíL8œ“±i¿-èV&ôp²z©ai 8S°×Q'sT9þ®®˜›y¥sŒŠ éâ;Òkín³¬ãRö{*\tÆoÂçåï'¸½ðN \ õòÆŽ:¿KÚᛎ’ýhˆ LJÿÒ^µ­”[½ÓáoÃPëEK¤@†Oœ& çC¡ôµ$­K,³ÇõÍÏÜè~?î\‚ +~Hzÿ4>÷µŒÜ:äÚÕåÅ—Jâ!KÂ5ån”$ ª|²ì+yµK©ùs²Hþî5ËnÛ]óMc~‡UÅôyTBfI³×Jugv/ø9§«šŽ8ÔŒƒ—Õ ñ)ÛÊû«[ÉÍCEÐBu4i}¦[ÉîK‹—:åëÕ ëÄ]³äã0ÄQEÒŽr¥ô1fÆWÝ¡Š3#b¾À<’:Âùo3¥:¶B“öêŇÄqÜi¸Æz¢weà°ÛmY›Ùoõ°Ÿl¬-ÑÆžKíÂøD4É1ÅÑIzîx>÷Ðn8áõ ‚ÕÞýW4=Üz#9£g—RCìÐ5Ìͺ{иçݰA>ȃª#u¨ûú´Ì ÕŽ]ÒPe˜âý@TPLÖwµ™(à?Xkâ= vôe§\:Ž‘JÑj"žN³Øq˜hÁ(.îG?Nõ}ìwš $Ę €O¯0w3·U¼©¦ úäu#…¤̧ '*:!ŸKxÞ:ÏøøE©Ê"ð•nÂFO÷8_Û/-!ˆQЬ~9w)¸ûr£4• ?… 9#=l‹álð^Eõ$‰‡ñ¦Óó¤/op๶´œ J •æ«È6ä‹“­ã“‹ˆŒÂ¶“yþ‚þFŽH»æÚÊÓ§é¨TæLl9ª¤P1Ó¡/p@î§}…iéeŠÿ( ŒqOây$C?ÜÊ9cÛš.?{ç(s¿;J8îŠÒ±yP+^`®×à–IÿŽ!ìÆ1õ{ש'!"‚u³ÍÌëÁŸk!¤¼A:Šr«'îÕm:¸™®œ›a¿oGHÝ ¼ÿþ£3˜«ÑuCXàÚ"Å3ƒÈ—¡ ‘Sý'LÆâ¶Ê¯þü´|ÿ§ea5ýñüùkÝ¢6õ¾-(â®:yüE¼Rm1Ny§•´ø¬ÿA@¾¿ãö}³%žNç:‡ÏÙo„â!yes•6JQæ5üUGŒœ¿šê·ØÂ_!Ïä·õ˜TâIhƒ; ÖM3*fmú*?×¢k«}£‚{Þ €‡ñ„Ób+b=~ ¼2¤öê.m¤P©êÓmSýìƒáþó/O®Å°hm† Ó†gñŠLFGõ—„„Ùã 7¾ ®„À…F¿’œfž“fY &'¬˜7º§WWõ°¨.ŠéÏ!“8¿ÉGDâýãøqB¬aR†ä¬çòit: Tc¯S}”#̨YØÉ›|np¾á¶˜X›éáÈéX*Nõꔵö( É ¼*bÅ6àl¸¾u&§‰™mt­˜€£8¦þ’(lÍÉË@~³¹]œŸJÿ8³p‡¬X,{d§ÃÒ +Ýb鮳Ê…ÄÏ2?Ú1‹‘÷Ü &f0÷DNÐhµ\F—U#f¨,.t<ì5=QÜõ@;;tÓy¸.¶œC€í@#ž¨k.\†‘âg̵p^;|zñ”`Tµú_ÅOкCfn{«ÒÉ…Ÿ9>ÔŸq#íûdŠ=aRòÁ~CŒ c‰h¬´û?wöO5Šàc‚ Ó?È\@äÙ~Ñì-¡Âáê%‹ñ¯gZ™_`¿£r"¹Â³%À+¸S}+'ÝC¡)ÇèÿBu{:É´Gp=8!ÚR ‹;³èÇ)[wkDí(øc¸áø ÑÚîŠ%Ð& ÀMÓ!ˆ .Ìy(-×±w*œ»Êpi ®‹ÇyŽnÇ7Q‰¶ÏΊ‡XInY¸Zûz3ÊK[@X`<ñúy¥hV´=SŽžÎ}™¯§;Wã6OU6DÞí´b4ï¬kn~Ìc”/ z׬ð¤\©RcÞ”9¨€ÖµÅ!M) T×þ›.b~ôž›YüLJEèxä[FÀ¦H•]>¨É5=”ù.ÉcölóÏ:Rk§ß<ÔÊiï«·8ž3qh’¤‡+ÒÁàˆšB×ÿ´šØN¹Åtæ°'¼uào_”½7x¾˜^† 'ä@ˆÃË*ëÄ Üã¨ôz•HH^al@šÄÝAä¶¢)Б²SëhðnCQŪnÊMnHÖª hÚïÊ7v¿¨µ!Q‚C-Vƒ)Mg§ÁývÅ"3|¤ICìr€^“_^zåÁƒdá³gAéd|MÁöŠXìD Y ýÏS/`7€÷„ ~õÂMcâ`@ÍßøßÖ]‡¹Cõ Þ„ rï3ýйˆœ‚ùOa æ„" óžÇ²2ËøħDó1YC.‚pÇÊw䀤Лj‡aXŠ1ãùŠw»Ö*:#Ñ@Ó”s¿|»¢3ó{ž¶@UŠ#è‚NŽb1VŠ›vô‰q˜§ ù1?JÄ'ØÃ§*¿ð+ýWÓ /µÃ›…¯T Ú Ü'¨[1iN{aùÙ´vW"Ù-v‚FU ‡r“óÿnÙ¤˜Ö¦">a‰ I¬û`&XgΔdùÃ,YéOvAÇ?´ún²7øí”W™W' oÖ@½•Cæƒò7,Í{`ªÒià:rÅÍ·2±(A¦ÝÒ¼Ÿ}9ôŽ&=Ô®ŽLᦆó  ,¿H—†1Í$5fâ.hø×9q4£S£t^O¬´Ö¥ý„Ôóus÷ù# S’gÔx k %{÷­”È?A ,}Ò£æä±NÚ/Û³k:>D•ö&°î¡FÒ*Ã4WôOAgYgm*fì+rm/@«ò%3ŸôÔç¦.R3Ýò çÈl ÍÕaóÞÐ5/ÆlowA¥B_Jœ×x¾V¹I,ÙQÁ a‰¢ÏÒærYe¡=ésvZMšZKàaZø¿j^¯øDh¸I•M¾®ÝWÄ5x‹3À!µ ±„Pj ïtc0¼˜éÈšª/€C¾¶¦‚š‹Œö{/=zôØ»à†%LÞSv^2&º/•®€cdŸŸ”½•A¯Š‡0s5fcˆ┄¤ÿ¼F±€±bÂ;À´ÀÝLůã@$1²ÈÚ†ÃKÉܤâÕ††ˆ§x|Ñ »šÊ·iÞ‰‘<ØúµÈEê*¡NT·¨ÎGO!>Ï-âèˆ =îÉ7Rh·–íuúž.Üžn„Vœ»Ž9ªí¿Tf0tUSÖOùROìjrdøŒë~Ã5Ž’;9$t㤈eVD1f)ˆþ§hÎUZ°ÚH½Rj„°Ë´X@fÁd #Ùxiï Mpá!¦ãÈ2'IX®×¢³ `(ò!Z¤N†ØRýÆ}¥Ìºyî¨cºÉíÂS[f«TìMD9Þ¸ÑûÁt?öÉ*†Y稒Õå8rêQïQ>‡Âš'éǹÏ7D…Ò÷:å…PM›Ì¾¿.9Í;“Ü£úÃíÝ‚0Wfà Œò–utlZw¿ãÍuÒ\ñ“Þšð*ø÷¦èøKT€XÍÓÆ™ŽÑ4cúÞs‘&Ø=<âÇ •S‚ƒ.2Á ËÌ£X5˜[š“øã° ‹æé$(x|GÓ€Šw¸ñ×nkö¡øÛQ€à8 Ð680{׿\í¿dN‘ȬSy’‹d€öã½îG,º˜S á±¥ò>¯2½V…æCn«9]¤wǦ׈#w³üAi6ƒWØ€Î|‡^•fÛ}›5Ñîf¾GnØLÀ"0'h)ÂÀ ˜ðQP.‚РŝØÃß)=¡PŠÄÒXf¾îk½Z#œ-ÍÔðhƒ“côvÝ͆ÌþÂÎ<2CáHËæ¸Q:|禑 f© p‰åÌÔ6‹ïÿ+ÏŽ‘‹þ š²©Ê{¹ óÀ°`úö^‰åI1X—ÈFD–Ìmê~:ô?úL2`gX,¢U±Ò虚:À@+½‚L;µ$[‚¤Ru€{Âó e^¢ùh²—ùŒ6‘’Ȭ¢kë§ÜEûÑ0^.úM¾Su(ˆî-oÈÀ§Uç)4´µmÓ½¯qq¤£ÇvWg5$’¿©§u…u ·ó‡Th;péâ“Ëײ;¦+×9z»„×í².öÃõ‹>êöVãat~Ð@ 2SÑÏ?[^…ø?ÿ0«'xÊä7f|eÈý@Mk’©ž§jù®6cP¶k8G mʃ.¨ÜJný¶tµ¢eR§ÇÕL'™ÇÔà<$öa©mã© wJiˆ4vV4Lû `e«"Gk°Ÿ¶™(À_'d¯d<þðQm‡ýæÛù;®()±vËP{¹˜5à­^UÂøyŒ“ú5ë?ïÏ£=!°0T†Ò{”HÖŽ+ZeÇB½)™Ú Ã®ÖDG¹Ìg÷¯3¶:úò_<÷ï†eèùR(Œ¤¶‘Óièñ“\ÚD¡€¦vu`Âí?/Ì8¶½y‘Ô |Vmi Š%¾!*€‡ÑŒÎÿ$f#æSv1ò’'ÖGT² øNTw™æ‘ÊG7€ÝÕjæ Í•=Q3Ñ5ãÕ#pxþë·7Tîc<þ#Šêcù™ž2…lv‡ ìNYÒ¡ŒSIb~3jµã[!‚(`:KRŸ^èIÕq¸8}ÎŽŸéO­ï‹¨;Ý$_Œ'Á.Ѩ«-m`&„,îtNçªÆ¶ú̪9Ûõ³±Ôs9óúV&q©Ï}Þiþùñ›>ŠFMÓP£­m$sµ*J‡hùvr>nGÀ`EÄe‘¾òl@x›s31¨@Õš¾Ó˜hø ìª!Ä ½ Ã&цÿùë7hÜ2¬xù’€Ð{_òãTþÕEŸ¥ú=Cÿ!à'`/Û%"ðI»T_ôÚ`$0øN4ƒs/1HÞÐ8¯º&Õ´ŸÀ-é€ ‰N’9ÈŽàíä1ø6—Ë ã´—G4såqGÉ¡©&s $”þ¢a^÷+Û “3‡¨’7wî¿bÒlÕƒîd;ã#‚yÀÜ–dÓjãÍöF¹]íÇ×ý(éPxñ-ü d³'·CÅH £QzZ~Ùcíukzç~$Fºp,Ôân-ÍÁ¨¶—‹[^éð1ò Ç-JëÅveˆ|ŒydŸ¾”$ý±MvîøÆ÷£ á®Ë“ÒáKÞGô¡Ð^Õ±2ª9‘’ÒL“—jηïYzu#§;,ÇaxT“Ðîܵ¯é_<kACŠ…Éb°pÞ+ЖËc¸GÈu9ˆ £Óü\4^ ’×¾wÆÏô”†ýÚxLÅVR+×uœN¤$ZA¼ó‡[ãÚâÚþúé Í+ll v/ˆÀÅÀPS\IÛC`üá]:Ž0íZtúùÇ{Š«Ýo½Z¯%]² ×ò¹")kVû^,É\¼_z`D‹€D­Îs¬ý‰awñ÷­/e4Ü:×U«+¨q&Ö<¢éÄAß e½Ýkä”åkPW¥aÑãÓŽ’ËsÁFZö¹À÷"Y¼¤HGÑcžN/sdµeH¿ÐëF1k$IŸjGm6Ö·ÀÁlòé¡$j;[!çÈúÆâ;‚ã:wƒäó@éniÉDN}z’†6ÏІG]ñࢄd8{²U¹øD™´Z®ÜÈKÅÕ«6t`»¶QíçµßȤcÚ´ypm(\Oc™zÈÎó\­Qûåõ»?¦Ú,:¿+>’U4—(ÖÚ YÁïdDS—Bîš#¥r Ï“wôøMêBZ0ûc-J-Ñ»ü:„üI›1V¶u¹Ê<ʽ‡ý²I|gúpë¤:ݶϖ0èö«Ÿ„áÝm³1K4·÷ª;¾Õ½Ç^Êߨêáž³‡¡üXo0ÛiY&vKT ‰ñØT°ÿ†RŠÙR<¯V7)IŒ¯²ôÕ &pÈ4é0?4ˆ$¤›ù;„ý<ñ žï}I”È0$6ÀyD—Fõ¸º+¬¼ÀŠø¡_JMóïûr'Ê—ü!ÒÐ5l“9 tœ¾—P¸!ñ§¼úâȼmš'øDØ­ƒ y«ðêØìÔêr„mþíWó<ÎL¾É˜¶)LI3¾³u¹¥= ù&þn‰ç"Ç,²Ý¿±kïzOPßx3XLj½})I3;")¿t¢(†Š³¥Ãè|xÃð–!™H ¼­¾õ;ÝäXIÚ…zOØ[–h/Þ¸S„’îJúøLQ Ÿ]W'g=ˆPp0?ÛG܉‡N:ž|‰AßÝ­TœsuÔ®ÜÎ.RÁ>Q0´ o|ZúñùîƒÚˆ× G2iÊ( jÞ…0 “ؤö´û]õ'G áÆ>Îòïª9jf€¦tj 8-•)µŽ|愳šÀp†¼´|"0ßC¾Ù¹*aÉ‚jïž¡E-‘0\ðTÔ¼ ­ÉÆü¿©?­Rè¡•hIuWÀ‡>½ÏaÌÝ"5JOV!«3±2\™ ›h…ƒ1îxuÊÈUù…c±Nùá êñ•¯ÿ”rŠé5E48Ú³Q#Ðâi”uˆMÚüs¥Ì’54(ØYeõº†òaÏü±â›BŸ—ipÔ-æ¯lKy+C„*ï$Hâ*XÓªwCÇ$êÐhþÑJÁò*O äx{ÈsŽï$wWe€NÓçH×.‰ÉKo1 çHúê1î$hYÊ÷¡ê̵²FÈ~«w*ÅÓxe‘ÝÂÆhxãvl² ÄŸc'C§úì¾¾žZ<µ}­ò¿õï/Î#¥b˜£ ./êï½iùÛ™&pf“Ëúöö ‹µçÐ¥ 3’¹ƒó£¨FÍJô1éðüÓ¥±c‡U-UÚêh=6%tª ç¿Ší{«M|žlqÁ”k×<BáéTaQ¸S¿®1næ§Þä˜÷qè ÙCYiÁdr^ô àƒóìnÂ{ŽÓþ2ê^jÃlK¹¬^Ñ¡x3—ñ¹)“E®±òQüę}Èb´F=U#¹²\[i÷ ¶²œüñì«‚'ë>Ñø‚é àqM@>Ä;{TIF¶£Ýn¶Aï¿ï Eèsç9€%±ËpeDSoŒI©hÀÕ˜à˜êÜ–¢ø¶ýniGd/“â$% kY*Õ 1{iÀù@G8jEåëÉPOåTQ ²ßšAÔºØ_ÈL‗ƒOÎôœŒ í,êȉñX˨Àpx€Lÿ7̈ ª(}ƒ‚ƒúAÿ<òò,²DÁqä] l"ÑšRNx ìׯcBw‚è^F=Ë37‰ô–4ŒÿˆZšBP‹CœqƾQ©3‹â’QªyNÜgÞìc7úcÊ›ÁÉ÷^s:J‘†Fç¼—’Hlµ¤¼ÅøOº½þ3Œ7’%J>ê=ˆ<,G¹bYÜÌ‹ÓÜ©ÌÈ2ÀœEbBkþ0)¥bÃîÌÜÔ\òeíœ=傈N¹‹:÷4±³'jÄC8N¾s=Î9ÿÌ•ƒ9GCDù…Pá8ùlEd´”åHGÈS½Ò7âsõ^B£¾Ñ­¡RW+{FZX¿çÛ&6\ñmq¿ b¹üðKøÐ…švôn{Wòu~Üã2Ó!;Š0¬,ç×y~ïo0I°€]ú|£m2¢ ˜˜XIzfÎrI“Ýy3 Õ\ ZZµßvKåÀAú„&=`NS {kIbEÛ¾éjcñ—™æW1(h“fþ‹ ­¤‹P[†­ã`"ºÔ'O%B¶5öÇ”KïütÞñÖYŒ´ï.­çëí·ÙºýÏ&5S1PZ8m×9Ÿ¹™S]`aê¼aÝvï>´WDo)лnÂü «¬ ²z<<'¼'§c6Р¦ÔVÚgM•šJþBŽ"¥¿È9ìÕnuö—†Ú €„ØfdMÙ‹`hp­à›Ù¿=ÖÕР:âmÏRTžEŸá³¨wk,„hÁL¨±¹§¯)VPz;pZ×Z î§þ’s5<ìÐ{¸V4eìì ëBô?ãfNµó$U9²P‹Í^ÕvØNÓýc}Woy'#D¯°%Z‘ÿÆ”ŽN5ˆgéüv©¯úÊëÿဌ2¢»°Ûï?EÿÇ?"(ñ.˜G‹'9}EgížqòéîËÃþa÷I?ßYFh©å/“ܤ»Ò-î£9æí£Öëîß•áÊIÈ9,YÚgdˆ.ãf?b¥Zå ~‘´þ­þ P¬Ì]uñ¥ðÅ6¥ñIßÐâG~@*°ÏŸgÕ¤Bbœ…“í]%§.Û—F°/+YÌœÜ´ÉØ´QœŒa~V›C/ ö‰rª‡œ§ÞFáÙ^# ORÏeT6]ô£ä ž/Óy´}£#= ïûέ‹ª8ˆOjiþ\7>8®Ð$€ò¾|ͯ…‘m' IwŸç9Ä}4„TÏò.³þÌF•x˜µd4d„TEÂ\ }h\°B¯]X‚Ct¬éƆx›Ê–¬`.´1{ÖR)>æ(•udaì†¢Ä Bèð:†A õŽUCe é‘PF-µîð æÕ[ ×ì[®¢NÓé }j‰v"¹j‘:íÝ;h%M‰ì÷gÎŽ'ùfBj‹O±´S„ý Ö>(„·ŠdoDÈ~î"ÈYk€…GŸuö==—¢œƒý }¿Óíðÿë}Ø *2’߿֭ñ³>…°0ž>ÀXüÙ"±2_îqïòÛ¼.hÛRÕà$ÀGÏe{¶—4dšJ¶< ôrÃïÍ‚Ý<¦è ¶;Ïè ÆÅÆ;vþhÀ²††{áBOÃI7‹o2=–XoÂ1]”ž}€*C øÒðΙò¢7l)SïÄCUäÑ/B,ž%Äß8Þ£4ÛÈ›TÆwi×qΉhûv²}c΢IgƒL$ÔÌm¨„ÙSÚ?›:ø“‹Æ¿& q•l ~jU ê/]€åË똬v vîu´pÜ ª;£ö<(vë(õ;´ÖVQz¡±ú1@<Ê>,¤ì̓‡MœüмLÀùšúV„‹ò"4 ÅVÁK4‚H-Î] nñ«R*¡+ÝÓ9~2~Á'HüHB›— .$áÞ | ',k§;7Ó 0ÅÞZGÙiUË]í:Ù)¨´-(9Xõ‘ “ayíÛ'‰Cßá|qmeùe‰v“Ió¶j·¸ÀÌÅžöˆt^ǘe§oœ-F`ÌûM_A+£7*'Ë^e÷YW\ñJX™¨Óð9¬ÛØ93—BŠÀÕq|Ry…5HÈ…›é‘Ol¹Ætñé©d†WµN^RulO˜-÷NsöäÔÇ$ ûtbþÉäWÀAJ–äuƾ,Cp–ü ÉBé•©­§‰«c{d‡Ó-ûhæ.‘ÂÎ~h1ìøÈ$ˆ_§a‰Íû3Z¡´ Åžã7ßN_Ì’ _ÀÖ Üò ­côÿ^å¢ó¯2+§x!)å¥ãí—¼<ÝÝEnsœ§‚í¿KR6ŽœsK x°¸ãø²Ýx(b·C‡±ïx(Ì={˜üõ˜¸ïÞM›ÅDHHÉ€)ÈTó®FFÝ­ŒæzR<£–Ë€óÈØìˆ0S/·ùbO|®Œmøu33Þ²Šªªô†«Œ“%SÎi{ˆ^qìŽ@ǃ\ÕÕš,i]¹åÿ›w¡[ ¦7ÿ%°[“žEϾIQé4B³.\-áMvßÕ„PˆÞd[u*aØ/±uZ kh;ÅŸó.ºÿ÷쨵@ð“~ïóƒ®WF,{ó±þ$Òºþ„QRpàcÎ<€ßI5ä ö.´õJñjmC)—+›ßûFhŽÔ™(B’&ÊÙö|cmuË·°ŠV1•kâœÉï¨×\žIÈÂÅO@. {û¤ PÊâÒÆóêX3ŸcüL•xvóì,€läLøâŸv`lÕƒonZ.B<ב¾Í?/%e}þÙ6oÄ­ù h\ÎJV˜åþ×T+‘?¼²ˆÝô=²±íŒË€½É 2]øßT|HQÊ`oMdwA&„[Ey7xx'¨ž7Í k±‘fšÈK†êá2Ñwóü8I€ó¦ä9°H£ kœï óò×2ªq½iÖÐøÉÑFÙÚgtuY¨³„?a´B~xGjØðøŠk €`ÿ}Ö†P?—-lBýlœ:Ã×.7Qò.D¥rîÈîDɪzú¼Óìë˜ý@höäÞÐæÎÅ#É M¨ÖJ&ù¼% 0$’J².r"—ˆ‹…¹Á/ý*tL½?›%ÁËéˆÙËNwÙ .ý\*RƒU£_|Jð9ÇÑ×V0Yg¸GÔ¬»&=I’Àâ¢b¹þâÑõ+å±^g¼R"sDî‘,Œ·>_GÎÕOÒbww»wªÏ>æ²'û/©+î‡â,Ò€.w2&HÚ Æ$êˆT0_tnSošŽa Jv$„&ÛcÉ1šˆ£úDœ?¸¬”ÆÝÖûëSJñ_tönøÑN÷}Š](L‚ÝZ`SΪ`kù%™#‚^ôïú¨Øxe*L¯.ä>ÒkóYhl‡o†Á®•nRvÍÓÄ)½LÃùeM,vÝAG÷FúŸ£y±mùkþ86Ô?Ì Ž• '7I¬¤¬1>P×-òVk‹)úœœ ÐH‡Cr*vd6wgƒ¹9ÿ €Z‹X!MOO{œ<Ît†®¢¼È•ìÞ€•½åy1Š“ò0ðV¤!·3ÄY4«»4¤¤¶¾ú¡ÝÒ¤šlDCƒ3ÍÔŒ…ÍiŠÚóý†vsžD@ ˜µu¾5vùlúTÔK¤uU¸Ñ#t¸m_¥ô¾/õ˜™›H²úó!.ÕK.7ZtÏhѰåÍ'›¼‹ºïiJ‰¦Ä?Q»N„ªß4én›?Ì«L¾¢t?Ù­÷¯28wq„Zù™e¦M£5ï¤6¾6>3§CÁÀ&s òfÝDú™Œš—丫•.ìþ*Àœ‚Ùô—SqÌÂ}£xì„1“1MŠzp|!ü•T~å¶Ñ·Èí…¾½?K› jxå÷߈É1ãó–—9tENY4 ¥¿êÞKdc&ÆÖ£Y;‚ˆâ}°kò¨~ï&Š¡±5¬ µ,þS¼ÈŒìVW½GöÈá¦$‘ahOÙJÂyõzO›½ Tj‡áG¶*È`‘i¢›Š¢:ªÐdæ¿,èðÅL ~@3X%¶,júØ ×­)òÎÊØ+Þ8—SvŒ}:¹xí³ÄZŒkw%ê…–¨íP¸5_¸Rû‰fGáb#±æR:eû¨Ñþ%Os q‡h«Ÿ÷qJŒ6; ݧ<ÓøùÀÍ; €ló·»ú·>FúÊÒHë©zæŽÉM=yªì g…Žÿ©×·ù—«ÒÎZª‡åIž…€ÄhÆ>êS»Gå^Ì[¢§£ÅG-ð4°"ôUÆÿ—X:)â¨qc{ ƒÖ–µ†éCغ¤Q¥íÍ嫚XUÚÈßÛå‚«ÙEæ™­ÄE)ˆ©…ž9É÷”ÅÄæ)—[z ÙІºZÖcw4VbÚ}¤ØþøGî]Wã¬T¹( •|ÁÉDçÿeä¦AÍ&ÆÀñ6§YPvKiÊPž<5Ek"È|UèÜĬÓáPÙ63?ód™­k¶@=àñ)0Ë.º2K§+X j¸ó{ ë] ‡œŽ¨¢ëpè\•CEdº=ÿH‰rX~ÿt±O³+Š„¸üBëì¦]%èÃ,ÊYbƒ+ô]ý¤èqPŠì1„• m¾‰œê)ä„Ãn3×kubUSݲL²¦JG½º?ܱõ¹oŽì¶ X4«pt˜r ûnÀ4EÌ5µy‡–ÆÇŒ¾0A¾Üi¶ûBÎÅÈh;UŠþ0$ï÷¡-ïY§.mñ3« ŠŽë%ÙRœÐ­Nô{oñeB6G=û“õ—3^&ÇyoReÉOà{;OMÔ4ú Á‰ßÍÿpÂñÆÓß0®>*ÅÏòd«“LåÁ–“) qÉ;Õ8> 1àMݯ¶(õ×G.E{ø`díúëÿ‘pP8Ë0†]a"j3g2ÛͰ¦%T‡sÐ6H§o»Üœ(MŽÇ¡®7¦"yAœ:/6 ztÖýÐãmÊ*‹Õ™E¤e˜ö‡„È”…,¯êKã¹ö¥!žf(ÆfiÛØúš ü-Þw£VüO²qØ¢ÍÜF„ÞDsU¼z(}ÅhR¦8Åwx&ën·/¬Ì†ø íÝ dšˆ<þïMtur© ]Ø%%’üþÿªØhé•bÚêçõÞ)n÷µ~o77«¶ª1yVXD#~fJg8‘—­Ÿw¥•Q˜Éî@ v9æ8Ή ôhd&p#_sÓ¬kCµwÀ©–à•蛹ýbaMäVÎzýk…Q6usýŸË½JOœgng-bÝÓJ›þƒ¶Z<ÑCÀßäÀ´ŒsTnH®íko¢Ùô „!]ÿ(wN-ßÚwJ@ßĖU>•¡Æ]ñÖ{ÀÆ ºM \ £Ú€"ŸÝq…Ÿe­Pk0Œ+x89óþD%&2$ðŒ\~˜¢ÉÓÜŽ¥¬ ôã•A2bàƒkÀ ’ ÞÊN²ßkc9*èâcy åü˜°¶˜`þŽ1ÚµÅßxÑ›ê4¬à«ÆöQœQ${MçÙÅ¿nñHümøEéõþ»H÷/ñ·"½Ã».ƒ[.|Æ2K9–¸Ü-MÂy:OiÁŽcÚðJ{µÀ©n-Z4>LÃÈœGVÿÂᘅ&Ø Ù(亊µÐ%æ:„k{ì³F!,½þz˜¾:$‘óÄiqŠXçƒí‘ù'O´—‡é*™¡èñBê~,F¯ð¢ûPôÑ èê0›H Èø Par¶ êK´â»®˜Øˆ(û'>i ©¶ œÚ ¢A‚F½®gØF úZÂXXZÝ¡¦wЇåš*¾0 }¹»«1Ë[– ~NùFˆm‡•Ü6½ÝÞëÕKAô&÷úƒ™' /Tɘ¾’ÑF"rp¨èšù HºüNÌÊæ2*üWCG^u/9½®Ä’—)ÿ¢d4* þ)…ø]îÆl6ëJFh>çÅ‘¨›Vš…f¯¼¢hݰ—w§nÈ fb&°ƒÛ.lg2YÇ67×sÑ¡€ªÖjÚÚ*e•µôåõžQ˨vúÒgLú´jÒgâ42M‘ç„|Æ›üUòê÷SrkΰÞìô*Ú–Kùäu•3Âü¨· w”‚Ü“´àœ–F áÁš-É¡8ÏwX÷çu™tMd{ ±n¿7:‡XÄB“6wD5UHQéQ›o ÃòklÙ—ÚúÚ‘ú—Yñ{pyL[G¹œËz‡oº±ÐÕÑá¢h<Ö‘Nk;uY9ÎñÉ0²sŒCIe ølÒT/ɹ6¤”À5¡Èm×J¡nkžÇª±eT¾Ä6QÓ «ÐÍIí«–÷NÒdütŒ½žáî®âM£ ¶øsKR‹:^Q ¥ÁNB˜>~6Ë©¦@BÏ”§NKRãzÀ'ÀÜiºÄ”LÎç­®ÜæBì4Ê#ãÿ¸Í5ßñŸ3ÈÔÛµjRSOŠ‚{äzԮܭƒ²s8@ŸÑMÿ?Óßy­Îe²ôEÐYÈ‹ðfA>#Þ'Ób¬-ÌŸ6 ÚÐô±¦èÉæè­µRáë,K*;s˜jÕ>Ùí‰ø/u ð\Òë£á°ß Їû^ÔL2–¸ˆ—vÑ3PÍ)Ã÷𵨸UxK§i¾e‰ªWž­±öWq-òWC×Åÿ@ ÔÒ2ÓT}ȹ.ÕÄÛw·bUæaÿ‹,a‘K×–ïŠs÷Ÿ–OÞ(·’º™¢ ?^ÑýŸ±†|„ÜÖ¯CÔ” ȱóÜçÛ®qÞ±äVDÛ$OD±|vŽ»rTôÚÊd麇ª|ÐðIJ"ûž½Ï=ÞMÎÓ“™ÿë4ˆT}S ¨‹H C£9âÍ/£`Zªèy9±×e¡‡›ËL[á¡yçãqñº.DøŠû®›xÜÊG®Šíõ½=EŒ}Jz¬Ëø€ÑñÜiÆ6bñëÜ\Bÿˆÿ‹f5?r×å/<éNu—S¥™ýØcxŽ™…„˜¶k“å;ÃP9©º’‹ v<(‚oÒ7éIï ¨\Êš¢ @]¶Õa-·÷´­1ÒS®0nM|¶ÅšæhÓG¤æÿ{?‹ÐçËÛ*v;芴ïkÂEr‰0­”þR¬¿WÄ|Ç”'«ؽšœïkÊ}¹*§±°«µªè·x“ #ùîîÔ¢¼0ù"(*zâñ¬ÿdÖ¸¬.ªåq¡€¼ûµÞ"ëæ;ø0@b#×LË«?#Ϥ¦s^úŠ&¿9~&óºœ&)ÏڄߣÑ~»ݧâ×n0Ü.¸‚R‘Úýs”U`¨¦Ü‘¾uã힌¬…¬ v†ŒÒÜEÞ­Ì‹Yª¾nå²ø‘à×Ë®‚ƒG“2¿—Hf˜þlrïrµ/T1C)üIqx+ñ`á ¸ Å‘ï §öW´:|ÆI¨H‹uŸ{iKOÏyÚëÁÎRÌ€ó$ÉÔc ôNQí¦Øa9ßÑÛLóŠ"¨Ÿ«´÷_¸–°v˜ ÷cHn†f‚SÌFl)¥©$Ì3|HDˆQ£Üïc £¶RÖWÑûÀýjïH‘Ip3œëZHþD"I”Å@–ñÐòCmž`I] Ïùg§A³gÒJư"fk•ýE2I‡± ïJªñßh‡7zpõUß¹ÿÆt8¡g  ÒÃpeeQs§íšãBß¡I±òþÖ ‰$š]1¶m»jÂ^ñlk oóGÍöd4Ü?Ú­è ”-R,ÕšôÃ!W³Ð¹Ë†âª{J[óŠ ZŽª,g—ª fäÃENßÂIŠ×n8šö],šm†u)ŒF¬ë}¾iÃsÀn_q9›.Ú @«–è¾™Ü9ù/$yžÉòúâVi´7TâIŽŽ¥ÄKl?³è÷§¡Hu¤aòTšüLÇ>(A{ØÅ!,^«–}ªgšè"¹·[7*™Çè-kØ:¢º1M¤¬Q¹Ê£ €Pu¾$ºÐ¨|]]s6íö ÊLì‚ÒCÓ/tÑqâ@xš­ §»"ûmë’®3ÿê‰|HMt9óŒ1~£– û¹ñZ'ØËàU‡™º·2¶åúò  trN¬‡ØfÛÁFLõÔ™BˆQÿ™$iDQô,?’HQ>žQY?%=‰Æ8Š×-jžØˆà ¬‹¦òáë¹ywåØÄSe=9qÌŸœT "¡æ[¥x®rbü„_Ì÷{3ó>ï&d‰¯‹¦Ö¡4d¼¥‹ìÂ?1ÖxñJ“‹ì1'-¬<Ë-®Wz&¾ØRüY„1v¥üüú ·ûÒ0å\‚ËIôO¦12§cÄÏRšDV—$–ôjœ×• ‹\@a…0@<1žh³ˆØdÆ•}ø³Оj±&òï\rÇz«ºsá+ZæAj±ŸecÎkq[ÔË+M1_BÑtÍíè+ÍÕ$¡¥F !„ÏÚ¿ç,È7Tx´¼©5–Öü/6åøu8_NËP÷1'N½ë¥ÛÙ¦$`Bãy›³¬d|ÚrD·™Šäó¿¾\vÒ.HQŠBYžÂÏ^†ÈÂpÿ‹ùIîjC›ËKÇ×÷È|:à§å–‡û‹öó{ê¡dTޝÙ..?ÐôÉœÌ4üتEZ UÞÈF¥‡K”ü•Ïñ»8k*"Í9&Km[Ñtir…|’5-w¦ÀÝ0ø£ûîc£ï°9²$‚vÿ¨@!ñ¬[t\Ti ?õ‡´±q ÃS:g¼ïÅ­ñô¶+)³eãdîÉÌC|ħ2 Õ+éÅF¸ñÜ‚C!l/Ø#.+¸ÔâãTÀ—r£‡ùR3VÂiõŽj½Ÿ¶¦‹ý_Ur°»Ò"{‰YËѽJî« MÑŽ‰aû“< vMœ dEÓLÜžØ^°†Á;â}÷4ë¹ÏŠÿeC…IËé* %ïä¥'Øo‹NˆpǬ]l–C£)€yÌ 9ƒ$ʇ';†í€W×—ÙÃÏ/–ÆPîfÍ<øelÅwÓ‹ƒ¾qɤ”&w¥•Fîí¿–¾Y61eKcŒu¦º¦HÍz©¬¦LÖ‰ÜÏø³ä0¥~ÖÞŃ~x•nÙ‘6F!›êéNM“£=HÊ›ñ+T§?øiÐîí~ €"•ælÝ€‘sÅ}_Ί7 ÔÄGSEé´6;cC|7LYT]=(BSÏ6©Í÷%!ñÀ†Îî±…~©ƒö·\âEØu¾­ˈåFEàG·‹Öã5ð­w¸2¯V‚**Ç|ÕíÕÜ7.äVÉm8¿‹óHÚ´ë»$@òΗ´³7òâ2GãèB;ú’7Êlëo¹?– ­‰J–ð7h£2"-é³¾°"«•ÀÂCHn5w!üU1Å_‡¼rЛl” £nf@®¶l9JYI¦çñ]Î:‹´ï(@£WóHD;MÎKLåìRäHÉ…ú¡ÃÖRbp±Ìi •šv ɦ &¡›äÿ{Õ5¨²d ªEýË+³Qbd—裗ÔùàGˆ2*sÈ|¶CÏcÉX é ï÷*°J¦à.Ì÷éŸùóï"X§VVŠªÉó¢lA†-RlÁN’¨Ö5ùYn Íeœ¹µ¤V=×p9¸ÅÕ±5e𠕤7Ï—Ÿ98ã–Y¦MÃÖmžöeDÊø¢c]ô™&£îóý¼Ìê(†ì…_!ĹΡš»¾ô:}MSgTŠb€/d¯0»ËêŒ~U­VÈØ¥iÈ6§ŒÓ“ÊBÁñU”„WÃ5ô¬üx +’©ƒí$»…ñU`èò|)u+ÜÛ'@rw’SÖ×ÜÜbjWJ‚¨£Ow²üŒ÷Á§2/"ßËEYëE R· óXJ|TÈÛAãÝ%:°+ÂR4jð5W—¶ø! E=×áçVÿÇæ¥ôøváßóZ yŠW·­ÙJŸãðUÏ—Nº ¢±=îÊ.êŠX¸‡CFFÇta¼a¸jß-]ŒE¦¥F³D—üÍ çô{út,sø{%{Só ¶$ÜŠÆ|$•¨#n¢×˜Z^êÆ™×·æÓŽî.“±‘º¥¨¢ÉÒÆ?æ4'ª¯í+W„'oÂïò×GÈT ÌÃà4d+p7•Í7câ©:!‹a帛¼ÿøOV~7£›4—¨Í¹É7rºƒ÷F›Ä†¶Nò¶Ù%À¿&ãe)ô@‰õtøoãÞŽ!âßSeØñšÍºHHM0êÓ¸NЇîGŠK$3FÓÝS@ÏnG±…æJ½ôOŒ¼Û‚”I+‘’›þ¹Ú+<:ÎåTðñ§ø¥m÷Àj5ƒÁéÅÊE@åPýóâò羃Õþé…C8ŽÜŠÊ)ñ¸+z´­«º?,3ÿ5!²¹/™Ä7z¸'©‰´#u 6­Ý.(n;iN)¸¼@6“Æ÷ʵþ4ñäŒA´x1èÃõ¾^Õ.KDÙ›¥Í3T4 5ÝÔf«êNråü›ªå#3¨Î}— äÁû[ j=œd÷}ɘ†ƒÝÄònŽ8»Ý¥ô0Œ•œ{¿6¤--¦†)7ë Ôú.Y'tXÚ\¿ý•S¾ÿ½’æÖHqز]Iôçp¨ÿUWÑÞƒÝ$(ml>Gpîù¡ °<™™JìuèMoœH„4ÑóÜý ‹îžØ%|©~uè±(WGMm"(ï•Ô ¸Ú%#«Ó\ƒ˜üeh²”Ù ³§g_Ÿ@𠌈­ ¡Ê,ø^Mú;‡c_˜ë}¼fj%ŸÛgAÑ>²0]ÖÝdÄ8@šñ XMûŒœÖY5ÄÌ<ÆÞp‰n#ãfÁ¸"‹&¥Yé—øíØ†? tîOò;å…º÷ ŸŒýëBûŽ·L¶×u~—>MŒÝ'Â?†Ž5ìÙP»C5•‹Á/ÚuîµkáDÁ÷jˆÀ!&÷QË¿ë=É™ú6r¡ïuL\·‰bº«vìHFI/¨˜— èW̃tȺäÔÇŠVp$sJE_“¢|«aJ÷G»ÂŽh€ÔÐ ló¡Þ[µ­úK_¿àÅu˜ÇŸ%®W»yzHQøZ›œø¾¦B“øüÄ; T„ß™Ûá‘Ri,çVöûèÎX1ÏKŠZôu$ârÄ\b¬¿½þ~`°[±¡¦¯’ i³Û´žÇ²:6?¦„·ΧJÊ.6øŒmÞûp ]ó«|tÏeÏÐø_¯™ñrh—73Llÿz)—rô‡ûö%ñ{ÚØ›JÀv”‚w´íhx@0·­LmøˆÇ[z×$â4ãŠ8&“QX"Eãa¤,u¸%jýV$µX­¢– ‰kºç¨Å~vÚÜ)替:¨bÀ¢{9ÁÑ|T¥`†¥ÏS籉kðªé8zº›.±é-ÓuJ7>IòÆîÏGë¬,üáÀÅË6rÓ'3V?>‰`ss›Å:ªCüEéA=ûÔTÓ ¶íp§D:#=yãð8ÒiuXn\Ý!ªaG'±öÝ@$¸\ÌüyÑ {júx³{ÐèÄ#Á¤¨h_ó©úa¤nFıƒm¤‰&JK%žKìß7[ÒjA/à½À3hü¯Hª‹Ö¦ ÐÚÞfÑL›r…V’0ú·h5¾Cä·Hú™ú1ò9‘`ýÒ }¢’U6òUŸ~ï?·Çý´æQÃÐ:L£Wµ‡$R6ôÿ2RV?kàŽø£ì ¾+ò{‘ ÷Óxö™žûŠÑ †˜ÌYή,üÛö½”ÞÉO~¿3¯õ,…v c• EUÆ«“˜ÐšÂˆï žcY°?»Ç[½¯Öuÿ¬Ìø­÷ZJIñK÷BÛ sGµ(N#L$!wÛ½WdŽ9¦µEq£{¨É‰P5”ÐxÑäÇ4öNâñ,ÉV,½ócï¯R¶s­X?ñ¯ö¶€U¥ò™E-nªû’ÈOLŒ" ^­ed£7 êždcÞ—Ôácð‡ü$œ¼ ->ˆJEbÌ'7 —8Žý#~dJsp¸³ŽÑÃwõ"Ç_˜‡Š^‡bùØ\r»å¨|Ë`“¢å/fÈ:zlæÐ´¶*øÚGÏ—Ô» úŽÿ©Àö2Z—­¦”Eò:±þ'¦l'9ø9 0@ÔKìÆèûèˆC³LOÕ-]¡°¯Â7t±œžõ’õØ„™ a” ‚“¨mµ*á~ˆÚ‚ š¡kÒ›‹/ý`ª%üú—Eõ|‚(<n¡ÁË›WÀRÿ½V9Z>ÂÓ ŽÓ8žü$ýUê3›8’V‚)ØÓâ¨!è!ŸËwüóE‹ï—l ¾ÊG7jŒ§=ø[4 gïäSME–™e&µåÓÑžÌTIåï+‚²ÄÂø¡ŸÏ框©„ ‡-…Äe§ Ngî&¸¼’Ã[DÈø¡¯Ã†}–ÜmH½E;â[ˆ.›ÀДŒ܇i%Žá}3bñtëDõÀí7£¾¯¯MFøWg(¾Äa®dhØzserôÿ•µ‹VJ<"uE‡ßIZ1‚+ƒ„avKsH<|¹0îÌot$„á@‡éQ /èŠ9uÉkìD€2ŠTt÷A[£Ÿ€8#P{ˆ]FŒa²¨«®t€ã›¥#¡‰,~ÅqkN€vü:Ú"&•ß7#…Ú{z)àLÒ¸ 2maÉcûÚþ’$å7|Iè.@Q–ª¶!µþ@áx*t~ù5틱Ú9¡AÌ ¤-®3ƒ¼•¡Ð:…©Dùa0dÿì§a—á ºPóX7í›Í}åæÙhªËoÉx ¤gïoÞ·(ár“܉Ýå¥&ª•¢°ÇPç9ÔØ‚GÚFF­Â³:a2ÙjÀáÅd8_ûö$êv¾¢©‘`c-Òü+™>Ÿ/Gr´]o´P ö°lÙK£€]¶¹Åæ©Ç‰¬çjéÇœÓD4é^PÉhc:ù?ôˆB§% 4YË6î4 ”šõüô‡Óßæ?­ ¿ û`Þí¢¬Ê5écVê½%?ùÑcý¼ÏÚ=¢å Î<`\™21%Šqt#öKþëÃíCc%L”†cÛ¶mÛ¶íŽ;¶Ñ±qãŽÑámÛ¶ms¾ÿ0›YLíO-juªê=Ï#vSêÁ“­!@"Öý¦ò÷€Â6"C²©}B|uˇäó{ǃo~hî?^ónÙæq‰EÖ4‹f³ÊÚŠn^âÑà· ÈRùzñ¾µ¹Ê컌çlÛ³š‚&#¼ÃO šÑ`_‚»t—jŸXäaeËkA>„Ž®=/Üõñ;KtßKäûb`Mí¡â1'# 07.HÖÂÛà=Ô °ŸÌ|üëÒ×ÐW^u¤!üÕ§ÿÁd•']Y±r2 ¢¹5ˆŒóôt­$ÓŸ—LZ¤–ao¾Ç7>q¿ŠÇÀ:Ÿ¿2†ÊÓ‹–žÌEråç“—£°Ûï?m‹sΉáYHòt¦ÇUÉÛ†í7$ûgðÌÏmégfOFR†ó<]XâÓÿ‘?õt€Ç‡5Œ›Œn+Y?‡G)»ª_…\-9½TqtQÀˆ U2þÖá·ìrÔÞ†Î^TçÙ2Ö W¦©Ùyoµ~¯?õjos‰¢ça#ò 5ÛÜ…[þ9 ¸™¡+Hù0È{“t¯½þNÈhs©,î2ÆèÏ_è(5YÒø‚ÆyBš0|:±"‡ÒQ[/SÈZ^‡.ÀÝAÐÜ÷¨Ñ$”ME«œ*^æÔ…A’¬$”½•õCÑL‘\zȶ%=ÇnÓl˜„ß+Ϥ`üUœ ûX ¤ds`*¾\j[{,ò2¯Æé!WÊic~!å)/Îs=¢>+•‰V(‡©ïL]N™b@àô§1¶ù]ø†hú1îxë *D¯ÝÄ:ÿEpX~øë½5ü A',Œ/?ž¾ç®ÄÝ‚›º§j0ó‡ã@üd7]QqÒ‘TÄ‹Zǘrͨ +Þ)–¬>(ÿ´#Ý‘œÏÊо§}1N<_hÛ*ž¤ÆÆeöy–ÂË^{ÔñâNÃõpѶðÔU6£©¨ÖUMìWµñíÍ5뺭ƚ™$Á¿5w‡¤îpVcKŒöQÒë`ø¦Õ½Ö[c‹‡yÎEŠ—ÃM¾¦à¹íæeZ¿mŸrê(Ø{›ï•lÿ/^`/{<è’ÖàFNQ¹vô´ÐÀŠø•|¨ä¡šQ,«±~†Ž<ÀÀévÓ)6výU¸`;J‘íjž¶0ïð¡Ö½Èž×ULi±R憵õ89ÓÄ]¨¶š¢>b_J‡É&ȶ¿öâë:6ˆ™Q*k¤ëìÝ¥ ß²a…ùm+wq«Þl50a>÷‡øÏyÕ"[Ço>lZœ”ÿž·¥ÝN&Uc*[ø%"¡(óчaÎ’«±+ª~‰Ì q6ê6’½pã+ìüóO*°5ž]<J¡?Û°¼§‚Þa ³ÝÝ*í¤]!³¯¯YjlåÖk)©®ûZb+U–71­Ž¡uZH|¿tTc±Ï.¾e[˜€2.íò›tcÚc=h:·…‡nN•H?‰{‰Å¨0Ir­}Wv¨»ÖØ^j8N§XµtŸ.Õ¸ÕÍŒ¼¼ ±}«Å»¹®Ê—E‚) ¼K£Ú¼BêœÙ‹HÚö¨güò hôcÀŽG1ñZeø©®÷më!ª¦)·ýœƒD³öÐÁ(pQÔïxÅšÂ[âÜ”èuÆë*$¶¿b’ÂO}†—t籸¡äc§ß%ú†ƒî„LLX͘Jt[RrÊû0ÆrƒNÎ +UÏ(·±®–½®ÍBjâfu5rPè$™šEOÁ{•‡áG`EˆDÙѹ_ƒu-oó׳ÍYËw!ÀYšêù{ óPyô§>–À¤ó(ŠST˜#¿/5ÔW …3;”@~âÓY‹\ˆb‡éÝm«Zð²Ÿ¿Šµ—[kÜa“QÙjža»ÎA*Ÿ{á]¹SË&õ2qä•?u`ñ¾žc§¾Î=MËHÈŽÓÒÀE‚()bgÙá™a£N¼©9FËb²v.º„åñ*ÕÿÒ§•ƽrR‡,Y㣂–¹[…Z“[s=o•\d’ÊQ£d·bà¨À²Ò¤«ÐœÚDŠ,ç»x)Þ1‡0çX+>qÑí^nl».š˜=/ÚØ2*-Ÿl·:5WÞa«ÂÔ‚@kÒ‰{ñ9¡b ‰-­:KŒ–•º˜+h>ÛÑÒ~›0Gû® Þ5MSÁÚ‚gSÔ;&ªHÓr‡6µ5ñAƒŸ„9Í!}‡p—M±–ÞdTÀ<ä“dNTc[¾oÜ·XÃfAîÿ¤Î¢S©ÈÊÞ÷ÒŠ9×ùìÊùr€M€+”LÚ êŒë¾TØ­£Ì£Ô_[¼ †Ž!ÚÔJvÜ̓üÑþ› 3·ô×ɰ…þMåÏf,/dZeI6¯š6*&+k­_i ¼]Y.¥…Yádµ°¢»wÁx™†o²“¬vò\ö)ä÷„Å6Óä.­„ÿý þÅ/Ö»S‹ú‚ˈ~É–’:™ ŒÖ“‚b9q+À)žQ ëÊ©ßv¦â£šÖ'Õ†@Ïl°[ŽÒœ™\¥ö‚È ìë§z:8Mªô·Ä@•Œ®hÙ¥QVb´Té)טचÞÚ8žŠÊ`N“h/Z fGâ²­‚¿F 4Zˆbž“Æ:à×§¡qø5‰’**Mþk/µó2ö.ô­ú§.4ñîõN¢|uìy齄 <hE‡WÙ/H6rIÉ£¦=´¹uÑ…¬(M‚ MÆó+CÃÂânÌy›Rr·Åp :“ã°Æ–üæ°‚u¨ñk¾l:Dë”øB-=óBô÷öPÜ«J.Ú³{l–CªÚ;¶N•²BWÀ–zÖ¼ÿ¥MDà ì=@BA™5*Wr£r{âpм#âR¹ÍI3«÷ãØ`JD¼òKɲ'†f#Díìáp¯ÝêB:ÑnÂx?‘éÃÃH¥KfaÞéoKX(ǧÛ]¬ƒí/Œ3GÊÉ»‰ÄBìø ŠúùœŒnÝ=ÙáFƒ€™Ã ·A' ¸®ôü ó‰¸ö˜1Èplôk†4qm(\—n*ROÝOqÝXøQHF/ϺòœTžÌ{¾t×"diØ„öA5˜ùϯ ÝF³õȲÓE ­;×ö´)¤f%‘Á_ÃÇ‚ÅþkÒ\; Š×sÛnŽËf_”(uHÌ…ñìüpÔ¡ÈûèÞ6ç{ËhÔˆ²Íóš9 O>ùqG˜|RB¿Y1ÏN:ºëc[¤5«ÉˆÒ«“0çLÒL¼ÅãK‘øb÷-lµ¹ªQózÿ»F"0&@«ãš–XYI¹ƒÁÏóÇ„²pC“fåÏ xlBšWœÿ â~ÇRH ¨Agd°»*R?±p}¥ò&rwóµDúœÃÒœ'æ-À€DAÂ3bì’ ¾Ùj@!~p¥Ø† ùd£žoç¼Å‡¿‚Hì⬶¿7©ººËcÅ­ê_Uçë%&%`(ñöG\e&϶bÚF3@š\b Üà¯; 7àbêU-Û¢ÂÉI ±XñþN¾Œ;üœ dé”|ìá7iÝôªèzjÚÁêNj¾ÛèKùêv”êrU ?Qt¦Å¦V!ø=xÃ@÷6þ0¿½ØCžvš™K<¦vbk& ªºé?êí0Çú¤I­Ü(‰rSÕÓÙú‹Ò¡f%‚®%SaV Ëm‚|@# ðë hmEè¡aC ©."€­ÛX}RÛÞO̺â ê¬×­jA9ñ„&UŠØ¿Ï~Çá>OÖÓu´úIÖÐÑfš:’øY•Äç/m›£|ËîÇK7ä—óšv¦wÿ%ùÛ¨ØZ÷Ó™l§x/ºK”ÄY ¿ÑÇÃøubËÛ”½lÞ<`K¿Á«þ\ûÏ_ÆüñS%s?¿¿-Ëzž¬ž^¢%Ww,kA¥vÜv)>!Èi"íû¶<½s6ZT8 ^–½JË£Ü=is/xª­ ï ö¢èpúµpPüÎ( ð:S¦_Zz Ø/³„ÓØT©ðŠæÊ9\LOà¤^<ùÂçòwæéÒÅRd·qÀQö¼5È=êdHÏ2‚àÃ*åK}æëϤÕSöŒð†rÛõOj¶U/3boå=2lŒÊ®K?kaM.äÌ]BºèB óï¸/á¶«hªn/«ªÚ"¸ClýÆñXBºÆc£BB!=6ˆ¥”^Aÿ¹º/Ú€gHlZ]R*Á¶ÝFåöÇL8³ÕëÆBùJØÿb%¹&¥ñúÁ¸ŽǹpïLwnŠÃyìY.Zm}ÖX¾àªï×m6k©zŸ=IIX€ú\f½Õà}æŽ=ÕÄÌ ÜOïZu¸±ú„AÍ9F?k »ÎªœÃ²ývÙTvpúÔ³ÑN¦V½ $Iõþû„†2PÏ' -ù¶J´Í´"¸¡gãñ§6•³Šë¸ðßzާõŒªøaç:ra¤ÄÔµQ8›z¯?Œ\ðvãñVí óÓ¶”'§©PÖŠå““=³aÊÝSþÇžC9`Œ@¬8ã}×o0œ¹=Ìqâa¨Ú ˆûü›$¯»Ùô°í"bp¹¤Ê\ºÕ›¯ ì÷ÇÝ Üš¨¥H0ðô¶þ\VŦ`ëŽpÈZ{>}3Ü[§1ÚÛ•÷‹ìF~íað~ªxŽ¡@‚5ŧ™ü­W¤–0ˆ˜Ë§kÜl/:Qeìw»ž^`µÄf ¶Àµß ÷ÜôÓß3k1µNÄ-r$0þH2ŠÔ¾ÑÞH1–JÔ96ÊÚ;}E¹¥÷†ôù§C9wçz+DU8ÕcÿH«>Üküè^•¢Áâu®®}õ ´zy€ýRʦÝY:™iEÖQjŠÈТAjÅ?Áe®éYߎ`rI¿e >õ†k]ž áƒ]ÿ®áª%ƒš˜=¼/í’':dUqî»L†¦ÙÅÁ¸ÔûêMþQŸ¢p#0b/‰fÓ-5ÁøD½!Úæ³çºgÉL¥G*ç[X9v¾T]tŽQêae÷D±é l=6Èc¹UH'J- 6©Z°Åbô²…ÙŠûÌ7)_‘y„7AÎqFB–ȇEìˆy™;zŒ1Žùì~õz¬ÝBoèa÷A;V­ šÂ;Š q˜4Y/µê??èzæ2[^rtöˆ¶E,u6&Œ7ÓÞËÏBÔ y®Eÿ9ž¥–.ÚþôTïX׷΄ÔÚ³Ÿ„?èSç–1¶Ã‹UávÝ›Þ5`J²ºÕÃóhþK£\äØú3ÿ#œSˆÒ/÷Íiò 'RMùì/¯ ÿ¦ÙùÍ«Hž¯ÓùJ¸Ú«P߯"‚¬  0 w˜P"Ýo’ÁßìÕMe®×o-m~b cŸ’Ž‘°ÔÒÒ¾¨ ÂÎ;Z‹5¸Ø­×ÿZSû¹  ÂX)é”WÆ#ï(”GK‡PÆVÁU`]¡Lƒ5ªIZ]èÒ,‘£;"ÿ[àñî·|gWç*ô<íèéÁGþoÄ¿<6þÝ·ƒ6éÐå‡Äýƒ²Lo¦ ¢?Ââ±F ŸH2>¡Xd@œŠnØeDÜ“f#èØ V8Ë ©"ô˜dg9Äk ðè‰j3ª ®å°a[Š6¸Æ 8¹Ÿ¥µî!Ž+ñ ÀåT{‚ü©Š‹îdùu{_U¹ t^è–”…rSÆ"Õ¤È)€<Çz™˜å†þ¨&¢1<ªkÕE„dÌ4ðÍ&}Õ& ù°¢Uv!‘ÜňžèäjÎÞÀ½ÆnºtCôð‚„óåâ¸ívs©¾TŒìÈø âã©­¥WY*r©†“ö«ª!ƨ®Ö€/B¯è·GDv½«'X&K‚(ˆÂØúÛ^µ{tï/{¼ï;è Ú)¨Hª¿1'RØK"½ð¯,ÃñÎMvË~sLê/³¥Œ–qbÎ^ œà¶@U@"ÑÔà]Z§•^˜5ùü!6·ð^-_²„“A/bè"Ó²g«ÈGõE DÚ†¸Ja¸¿êâPDŒ‹•®i¤7R\†&SˆT¤ÒØîÚŸ<ŸƒÚà~ʵå:~¢ž³¢ñ« [†•ž¦ê ¿»«*:Š øÁRä7F|æ~"ÜänaCøûÍT ¤)¶tŒ°×É‘}tLëv™á•dó•v¯«Û`¤soB]8†éKV5'x7ýÒTÞf_^B–Jþv<Èÿœ1Ž[ÅXŸ߃¨‹ þ’Å¢oš]¤Ù¼_Š“R¿3ýRÚôçBr0+Âu*èCüíñ:…Xº³„¬ ØìQüÿ_ÙP7‘C›®±n¬ìäù©7Ò¢ú}üGÿm±R땹'aÆ}„cމ¥—K9ÁÅ;ïJM­Q‚>Xð©0'ììy ŸÐ¤äùç½òÕk>϶^mÍòoÇPaoì(œ4 •~xÊÀÃõcžšÕ[Þp·ÐgÈ_@;ÙZ*‡ Ü¥[-1c@åfS>âŽDO;¿NA jR¹Ô."¥£[–a€š¸ñ ŽfÊ1мu T# ²¶D†V ®Ë,ÜÚ}¼ÏÆ]‹ÂU6Æ—¶ÕÎ÷}9åÛrz$øá3èl¤å¬I?U4©ÀIÀ<î£ÑæŠp•R„5çDn·ÎóÎI:Ÿ¾·*ÿ…Ñs] ·|lÝUèCò1§u¤S´ze5×*wêk6삯 œÇ pf–œ®Š%ÊÙ›ºÖAškt±Ð}Ë1Š>ÙS/À@Eͬ`ž“ûÃ@+bÂý†ËS²GæŠâFÓô[%v»—h<êMš7tÃÓŽ~P„tyoBtP_z»ÇÉëAö¯ï~GMàø¸Ó }BáF®m”šaÓ¼*:Þ^¢ •±6D~¤Œyjp¶8ÈÙ÷ÆðÚòZ\b¶‘¨·s'ŒÒ ¸öÄÉOl %ün¿uc®³TV*œIˆ u>n)¸ì>-{ë®á¦'7ÄNêa6/DhÃnˆÞIs§¢Gÿow§F:Æ®xÜh„Œ){Š iÎJ_£HzAììÅKl['> D©þö¿9'aH¸ÀŠ=þNÃÎ0n·q«Ó¨6IH¤Y`q›Òhy?ÌDÚÎtrX”Wzâ¤d{›ï¡ìëþÓ¿d^mtð3¯X©“—(MýÏMካ¸òêìâ¾ÝT+ñ¤c¾Œ¼»}ùF ¾üÌÃû•ôµ jg¨ÕUüZæp³©Î/"éSÐØ. é&aÞ­0Œ0ëmª=‰?‹`Ͱr_ã F…Ä v–\Þ0€í`ÃH׬¢Ðuq`?gUDûevåVºö4ýUM— õç[ݪÎ@Q‹¶Zô¼a±ŒuáyñVø}ë5˜Ä×Nv‘UÊJÄ*ÁCXCÛ9jž©s}”5ùõ`89,û$öpmøAð‚3.̦éò;ÁrŸo €$"K¦›Áo~ w7¤JÛº ú^ÊJ{µá‘A¨+‚Þ›~6Ž9GÞ+xQH8l kì„»76œVïßD2}˾¬ñNõêÔû ´Þz‚[¶0HØCƒj>IòH¥©ÚäF´8¼o0!)KpST¬=™‡=}²s÷1©a¦•þx}gÊlÆé#IæÌ2cIVi‘ÆžÖÞ½÷êìU³PË\hÌ0cåáõæ'å@iJËgÙpÕì~¸u៬À·JÿÉôç(§*×*®­9öµ£šë+ëüùh2¶Ÿòde`§ÓdýÏSÝ›G ²[fÇËEK'X¸õô/¶#Ûp€i}R†z9lÞíX?Æãƒ÷eÉÄü=Mˆ£ƒœØG!ab¯i…(Pnà¡TR'ØòYiðâëhr+‘=þ•ØYYŒNoß9s÷+ìC·ÁÎs.¯Äåpt„²t†IdjG¿j"1u1ŠR\Bgf“*½Ö¸w¼!Rú »­k·E-¹àu,êSÎܤ^"¨Kw"Ò'³î‘µ)ÄÙ·ìò{OTÜR^Ýy´­~*¼šgPMda@3öÑPÔqeê?‘=JÖ2”£‡Ó;„Å·Óò¼eêTÖ °†Ì´†Ì’6Ô†¥ýI«t{ ºÁž|õ¡~÷,­ rfM^“‚Aá‘êÞtM%µ½ïÖp8Ê cÖ/©؆,/L³ ÕÔ—ÑîdpX¾®É+E¶FÚ–N)$È«-É"d5óæ‡zÐ)‡0\©%æˆ*áTµLÍh»¦D³/ãÌ·£,ÔˆP\ųFž`ªÇ…\ãyÈ¥Èí ÄAšöT »`–:TÜ5Ö#óuÅ““wܾ•»Úî¿~ý‘F Tl¼#¤YB6¯çLøu„*6OºËÊ„†r9ýÀ ÒI»~—³Ñ_^CŒ»U1Z…«²ÍᩪIDI;7µ” y;øûì¾õ}ŒMl䇢¸=΃pä"Cr·Q¤ÛP[Ý’T‰WnÄÐÞTÌc³uó±Ûÿ¡¯?&ÙºØF¥vHì­àì³î6°ð¨¾Â„ñ×(P°¾žèhJLö#B™O£Qç˜@ºêâ¥ßVT7j&Ž÷}ŠîÛÑ'Ð3L:Ø€¹\ÞŽê[Pâú”k©­€Õ´Òª•wùùë 7e¾*â×¾&º$S娯 _ ŽÞ.D5º÷NIH(ÀB»FùâeÿeQɶ¤@&Ðà”òÏöœœO/'šF•RQûìQüv!ȤÚ/l@‘ü˜"7Šܲ×þÎŒï"ú[‚)¥Ö͇Þ9ó:‹-å%X¬Tš‡ì½™:9 jÊ´á,‡ÅE—‡~ä³}öë0Ê„Rt£ó OËÆM3_Ú$+ÒI+G8A7Mÿp;¤Rg2kO ¬Bì¯8íî1xc³­³Ò;FRY½ÜÍ^ÂEÿ˜à·Jóhþû:Oþ…¶ #HMáÚ™«RPůÈÅý>:±NS³ÏÈ2q£=6¾þáÉ¡y<ðTèÆAZü g ©éZjŒ`ˆ pí¥«üs=ƒ}¹ÐÄ\÷žÞÿ,perþþ{„ë|†¥S2l‚‰JÎvFF°¿'¸SÔ¯œfäW¡0xP£Bç½ß ·(dK׺?y›š!ÁÃ+/Èòí”ëµhLKø‡·Nª7M<{Žb¸ÿÆc8ù±OE8TžE^kbË~ü†EAÂêI Dæßæ[…á”5”ý‹õ–ì áõEJÝãwµÜÔ¥bÜ= ¥/ç”86ï·Ô`Ï· „Ïzá qà¯Ú»®Ò…C†Ó0EÖÈæ”èöq¬ÅÙ±³'0ã‘«tuµãÙ³– ìûeð Rð‡½S|åÛ#Ï$à¯ß¿uÇï ¿sÙœ¶oS‹tk:«]ù®ª½MTzR­¶ºz8ò ñ¤«¶ Hrù,í(º‘ec=ÖöÇñØ9#¨Ž ²Š€cì%ùã°TINP9=3a¿ç¯ÛÄÌ€$™˜wL(y'æ?ÊÓ–ËÓ¯þtphMúQµT¨­­*£"ļP½A T8k6ѿ؊áþU(éoÍÒ¬’/7ƒ7ÏùÏJmã¯E#5Ž›½ü—|Úe+">îL0Xÿ'çâÐJ)TÉÀî·“U´w²¦î0› á·šÎä)ìò,Íw£G“ÍÕ§V‚pÒ%/+üÓh[Âì}þëÀì+Y3«øMŽÙ+ÐÞÕð}óþ6º6£A}\ ºB™òV®TÏ©L—; ÉR_ÐòRÁ|ÿ0ÞZ&6³@èLvWHÊEuÒ=—Œ¡§š /b|Å»^çeÿÙd=òünF'SÑ}xÒü.â«M6§‰;¿H7ôì :nôÍä¡ nƒ"ŸË%‹ˆ&1Hh>jŒ Ã4¶¦cÆ¢J9J6?õ°þñèžå©E‘é†Ik7ˆUªV:œñ'鳕u&­¾Æoƒ¿0)†3Y~i ª:Ý„{¥—ÎùÆZt™BØX¯Tޤƒ’ÑBhð+bê£-Æì¶o#,l6–8ED„) >U-û|£>Ëý‡T¸LÕâƒ5ÂÄ}«SÔò)«¹ÛÌJÁÑÅ?½¢äLìQ>]–›w~û ä‡ÓQû×vU?˜) RÔø’$²'ÆUVäbÅ8@{Þäì¨åôa›"\³ÕDÓôÿå3)ñšU :NMÃø²)Ì Q3qC©³9MÓÞ¿ê§ï|žh ð´x 1skàY3HõRÊá¥øéÆÍEƒËœ_#}[ËçTRªò–ÎDŠv„õ¶­ýq¢3ÞY¹ÓQ~"Stôݮȵß#­J–eþfŠCNZ±×é±…ËM1¨KÈ;6Àö~þ8L{ßPÿ˜™~h[:ö‹9W[Šô¨Û„n4¡ˆ5èûåZ²>`¿ƒÉ˜¨ô^CØBîɤm“ÍÇý—ØÞ~¾&Þg ; žÏaªº¶è¿k$^9Âwâ}YÅ4 Õ'0æOû;-ó.¡…§‡ŠÄÈœœF ”r8Er(&Š’{jîw|zª"ªxRüŠ.kÖ˜3óï˜3SvfM/™¨ó£?ˈï”5a¼—m¨ô‡+ÜÒ}£§>™¸!N²¡Œu=å£Y”ÉU 4°Ð° —Õj%¿ñ¿$m¿¡,am”ª µ#%íàá·üpÙˆþ6ÿÛ¬¹EÖF&›’S'û˜N¨H¨iN…I3sêÇí#,õÔ¤suå¾4,¯xh >³^ÿWïNЋ†›» h”ɰÿi?\DëÉ›xü%欗f“BùÁ€ŸOƒuʉæyDuc¿c‡„þ–¡6;šÄ};™œìÜïÝÄ Ôã=-&¡[œ…êãÍ–ÛýYüü×Eàž÷C@\“Ê} ˜SrñÄ/²B£i'<ò"4BP‰u h2œMÍ\ÝÏï»i†²ûµ†¾ûøw>>­U%  LÅ;¨Ìqýކì)/‰ÉÌ.ew¾§ñ Iã °²¶YQ´rP2m1 zjœÑ]™g¡*[ÍVsÑÄBaµpÆ|‹% «™ŽÀ1[#dÃkCJæ'ÞÞELŒò©&wµäÚßuBF °×É~6¡]IªQ¡I%8³´lB”‰ôUÇ(_Âiî ÖʱÆFî‘æc)’¦€F/¡< | ŒM6C…z yGÞ(ûИ>[â^(4du8ƒKw- GÝ_‰ }[æÐïÓ0Â+l§Z‰Ùÿ¼˜4°¼Ÿ†i‚jFð¢-Ý8qÞRì+NóŠ9KüQz”,{ѬÝJ! a,ÿË÷¿¬gùÿ Xþ/œ¹½¥©‹ÛoS;¸ÿó²endstream endobj 10 0 obj<>endobj 11 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600]endobj 12 0 obj<>stream x¬ºspå_»/ul;;¶mÛ¶¹cì˜vl«;¶m§cuìNÇN:Öä÷¾çÜsçÌTMÕóßZ>ŸªïÞ‹’TEQÔÂÉÌRÊÉÄÈÊÄÂÐ:Xº1ª99˜:> œð””â®–¦  “£„)È’ mi°4°±Xyyyá)âNÎÞ®@k€FSM›–žžá¿(ÿˆÌ¼ÿ“ó©é´vP}<,íœ,AŸÿÇŠê––%À ho WVÑ•U’ÐH+i¤--]Mí*îfö@s€ÐÜÒÑÍ’`åä ø$üs˜;9Zÿ ÍéÓ Q7€)ÀÍÙÒø©féenéü‹àléêtsû<€nkWSGÐg@N £¹½»Å?|Ò­>Sø âìêô)áðÉûSqr¹™»A€O«*Rÿödc úǶð“ p²ú”´p2wÿ'ŸX¦ÿÀ|rA¦@G7ÈÒë“è0³XÝœíM½?m‚9»ÿ冻ÐÑú¿<`¸ZZ›ºZØ[º¹}Â|bÿ“ÿŠóÕÿŒÞÔÙÙÞû_ÚNÿ’ú_>An–öVLð¬lŸ6ÍAŸ¶­ŽðÌÿ4‰¬£•€•åßt wçÿäyXºþ+A4ÿô í§¦NŽöÞ K+xf%'ÐgÂ4ÿgUfüù Äÿ#þ)ïÿ¿âþ÷ý×ÈÒüoÇÿ£yþïÐRîööJ¦Ÿ ð¯åø×rù™:í½ÿ?Å´-ÿ½hÔ,­ÝíM]ÿ;Š,Èô³»E­?· Óg›þÓ~@7) —¥… dn°2µÿüÑ5-,]펖Ÿcú¯-`deùÿÅÓ°šÛ9~N€óß,KG‹ÿÖÞÿÌÅ¿gþß—ç¿L¨|Î0HÃÛùÓ›ÛÔVtú\ÿqùGQLÌÉ àËÈÊÅ`dãaýœ­OxÙ8üÿ_Œü ˆõß@ÿè*š‚\^ýÏHYXÿï?1ÿßn†ÿ FÒÑÜÉ⟙W™:Z|.ŠÿEø”]Ü-e%>ƒecáeÿ·-swW×ÏÕô¯Uð=üÜÿµr--½,Íácá–M ®ñž6€Ydƒúúœç¶œÉ£ãF)ôK›¡×Vò‘ÀP“ëV~œÃåýÐ ÔñõoÓzù·sº!¼zú-ý¸=*¥M+QFeÒ¹a²îW?Mˆ§ÁÛ}]z~@ò‚@öóùü¯¼ÈŠŽ2¶ßÂÐL‰*lª¹¸6_%šv@ž©`‚Þ,´¹ŽÄtúïHº†c˜p‘Ó_W–Õ>r6|tc€D`ú¢:_~8D¡ 94ndpäÌÏf±’QÞûdçõZ»?¥uL§·B5DÉv-êÕ+ÆÝ%î·eXe«“‘Tó$FØØï£1q1M§µúlú°¶±—Õîj&%¾$w.ªýB•Ư fíýý¾mšg¨0‚ÕMÁUU ó\zÖ^1 á'[ôч>ܲ·xJíËÛ2, ‰ÏààÐå×”ÊÅÏG ææM¼âœ©ºx2HõL‡K{û‘eŒÆyq/=ÕG—9@:Ó”á9gAþ¨þãôk }Åvfˆµ9‡æÞü|O>Aþªj;±åÁæùéÖºñ›Xmþ§ýpËÃf«ˆ!43µ$FÞq¶Í—³C6øb(ZL¢tϬ&3€f©8 lim¶ÃQùO@ß!’ø>\7`lzk—;t ± ýaG4*Á%ÝHX¬—®©åqD~,rW|pà+òE4³ø±&]t~QÍEýiª/®}Õ@qóNæ žúmŽãW"á·c/Gzº(–§}­å(&Dž¿ÅòŸ¥%Ĥ«4$%‹¿ærO¿òýQ”“uÎC6§¢0ýÂN­§z̯‾ʙÛ)„(/ÄþkW²ú›šèÏ” ˜'Š~pô¶]9cBéˆ%; Ñó3Ü> €\d–Ÿœ=$î>KåZM íB:Ï–|ÞH¬$6y±›Ï²V ªý­i—Ø:&êÀW:”@´ó¯Þwù”³U­‘¡ ¦èÒ©vm–͆¿þF׸x~SÝî} ÎæßO XÖØ³‹©m‘@Dô,¼lžô½‰)i®Ú~Z7jgç¬ã!Ë@,c ZÔõÑ·–×Óã>Üã›}+äÙ*æË%Nþ³šücÞý5|† 瘔‡¶ù¡œsn¼b-C¾Qýåè…,“Ÿ—|^«WÆ&íŸ7jßdi­Y­_¯ïÑômùjoè†áNG©¡lu“hå¸êÞ£o 6¯¶t´,úI»0,y r~Ï‚&cS¤õÍ__lh¢Í‹Ñ\G<%³3ݹضŸÊZWªòtÈu'Ù’[œuÙbUyÚÕ7S(6õ`í>EXRà‰ãlšfðœÝ\Pú¾9†®?‰JJ ý kJ¾áÂbVî…Q¢Ñ|/9[ôÞ¨ï)D#_#°ôEƒ…'ÄHc,ýB‰îJä&`=„wÞHdZþ}€ƒ,èÛ½Sƒß†#úìÜ·ámS&ƒ$éK ¿êP ˆVrºV oª¹Dǯ- nªZÀäp#vT}dêÉ„»›vP©½tü\‚™2sÍ–Ü¥B,@¨S诺†¥Z|ª£¢M_£zÞó ©¢‹4¡a¡•¾ª+жA*¡¤]cÏ/<-JX<þ¼.ÃÎÙ¯yl¯ˆ@¾´;åH I>:°­Uª‡²IŒMHcýfµ<]K;Ïíe R&K…Bpý€ ¦Tm]q,g@hú ³“þàúa¯M~ìÈíÅ€{I#…n…‹Y³Ì<á˽û26‘Á5É“!Ÿ$tD‰º#%œ£°Ù·RÁ‚—Ù©v<†Î" «ž›‘ÆeÎvÎãjáÁ “ ÿL‹aýh©Ù¨B“øÊik­q╳SS~A#$^®Îr8õz‘ÎF\Š'!‹ò„·¨çŒ¼¶Dq5l-öÃì±lwïÇ@€ª¡¿-9Q¯ˆäàÁ”ˆëL߇üybc•ú¼ÚýĉÊ!Oƒ~úZî©(]†¦bFÑ0H=,Hw ©(êòÍ\è£t‘'I2׌Yc ×OÉ®„×á CÚáüãæJ ª½–w;nßnÐßC®‘Å1~ŸÝ¾m`æhšÎx ÂÿÒ‰?å˜Óø韄.^XÓÈ:¹2\.ûÒL{­­Îuiõz#BK¼×=Ÿ&VgJi|¶*—§ìô=ë 3­¬§øoŒÉž³»”Uã¥'vlx{ì¾õ ÷ÑÂÅ_¡IÊND¥CÄJ?ïK¼ØDðO~öûcÖ »»Aãφpõ#j0·ose£m´‡­T; »´Küƒ7©I€”l¶¹vJ¾&¼têQÓq®ÑØÜN²nVgáêžæ#ë X*Í­£÷BC«Géß»t"sç5öV›%/q¦rSÖ£¥Å£›¯Û@~ÚYj#Ô'M^8ø†“^jÇÉï dHluwÌ'󺛀´Ì!TÅÏtŠü‘T)zLžÄSàÅå÷á˜|ˆsú­ê!xXM~,]ž)KæuÝ¿…µñ½ˆÈ+·‰±DI`›¦øHr?¦ -HÐ_é$UµëÇf97žÐÚE‹Áf:ò-lhÎBçÝûÞ­FöœÛ•ñЇ›yåGWž¨þ˜£˜l×™yØ(0Zʪ@ûtïú%º•O¨F\ .ïòi„2cú\Ƚ¨´)¿†8èéG Uà¶G?ƒT)ïô&N*BâÐj€Gšø-'HP¹ê›nßYˆ£ŸÙƒiÇn;µfÖ@vÅ5Å7fM®óƒ#.+=ô®ÈmÍ 4E?<7–ܧ@yãG´Où0@Í‘‚a[üÅøõÖ±¿º”—qͽjζó&êT¤×ýõY£_Ý ,@À@¢–}c¦ÎÂ;ŒS§¿Ä¢VAU•ͳ]°i÷ˆP¬¶)êÎ3ü8¼Ma?<0Ä ó-8ÆFsáQ‹N ­ÈæYm’áÐLiÔBß»¼­å•'1[Ì’œ8>LíódšE‚B¡<%ú•Ö®²ÄŠDâû .î%ÎGËPÒ¥£cKù`Ñ@ÎMx;¦âÂwB;Nø¼.Vì)G†:êm{JégÏÿya”¡¹²´•‡Ø7»62Gš{‰Öø@]Cï{ijІØ8¥ ­È2F›Ø%„‡js°0V4§;J³a,×%(J9³¸lŸ=&qK7žbÂ/ I׿"t”õŽ›‹§» ˜(>äþ¤ÏÀHE ðVš2£Ww4N£Ù*dO!4a*ð.˱k¦ŸUM´Éذ£øçV2±hè[»_59:EK>ÜÕc@F;¢×v¨3(«Ý:,ØÀ,á vcÀ Rœ 3G[ço€×À§ˆ±É‡Vq¶Í-5¿ñ†z3“¼ÒkïÛø#WÉiy*è/¿Ídbjصûs½BÙq1r‘E;„¼RÖE߉¹¿Ffƒ'Lå%&8HŠžŠËu(íQ®rdÀÖÖ¥nWu_Ï?ùwmÉc„biаßãJN)lL¹ƒ*b÷êØwír§Á&sÚï=T¦}\üëýœßÃ7°êÔtÖ.Ze‘A¾Â"Á4$ÚÕÊu|ICum÷8¬ÒßñÄ›‘4ÿf9Ø8œ«Ž ¬ëw•ý”Ic×ÓJb»)¶·¹:ãCM}{Œ´ž –ž&Á?ù ç,ÒÁo³û¿ o´š{>ÿ'´ú^Fâþ·¶?)aÝ=–ØUöÚ¨áû²¨×AEJÿ«/Æ…Û÷AËO1ÄŸÿ5ñ²Ö¥L~g§1Q<ˆ@qb ‡YIr A¦í4ZCàÉIßï4˜’P÷Ý#;k]Œ¬ŸñxüÞ=£#ô²ûÁ£ÅH_­î£¯%ÈuýZ®®Æa›6ÿû`•;e ëR80œU¤³d<5)÷f^ø4]щ|³k0ýèƵoãAÉz#GÝ|(Â×*ãŠbz*³ÊsžZX!̇ñm[ëq»ü¯G~޽¦…🎼œÊB•‚YÜ_AŠ^;HWî9rÇz,¿+ ²2æ¬ÉP=f6ÎeÅê÷£ ÂwÊÿ}ïºÀûíí`ŸÄ·'Üõºì˯(`6?±ÂåÁãLƒ<7¬TyŽ¸Ä xÎÄÍd —“Z\Ó®-°£  áCËôç #ó]ã¿jàCX›'BÏŠt§»©ÚÀ àd)ô:áuH¿Ç®÷÷áÀ×Îí´‰K!«ÆD÷öy‚_—ô`\ðæjñk'Ô*çwÒ×O†×òiýw³ÂÃ8¹ÞßMcx-t¹Úp2ÞƒÞ¿–7 ÙÀ˜ôƒHK…®¸¨& Ÿ¹²µD=Jr§x-“*o©¢Ñí²Z÷ÃÒÆAc­Ç„…HßQ¨òÃÂ뤎RŒ÷þ¦•"`¢³.jÙÚÄÞÑ©•£Ðú4¹„ÉŽÅîÚ*_× ¬ í*vG»Öá†y“Â5¯œ½ÙaÌ»Bû¿^¨ýî«D0Ü(Mà {£âÂ1oK7–¹°–¾'ªâZºQT~W ÿK”=!jRu’UÂ¥GHïƒËs3üëkÈßÕÓƒ·ƒÂ¾‚ªB€¦׎à)6ó Œÿ]näQæ”NÇ™#²ÆñÈʹÊì„.”¿È×á+ÍÉjÔµ5¹Z_8N‚"é°,—ñê;<šÏUø¸‘xs±­ 3-[Þø×õø4Á®ÈVRzOj’è9ÕS†øš»Ñ9P›ž@äF¼¡þD˜Kzm8·(ËÎay‡˜,AýŒCoËMQ% æÉ‚é¼ V{CùkÔ •ÒMô"Hœ4Îs=…ª)dUøzüg}iõo’¤ë¼7ý(×D5*¹»#j? î1H·›à˰ˆ0“:ȹcÔ S™TZf9ºßÈä,àÉŽ‘ÿþp6ròFqDCýå»%ìÈËŒ‘˜{€˜/ê´q×µ]dêV’û]Ÿ÷•¯Ô(ÁÔWŽÄÆœ>o‚ˆ/ºkÖañÉɵYÅÇÒ¯AmHÍÇ¦ÖÆ}Õp%¢«B{±€ž{Xëò~ŸÆ½ªÀž/by½¤À28&_ı6 04¡ÝÁXx3GGÓPˆUëü­©€ B¼ º3µ½¢GÜÑØ_®ðB?¸)íü'¿W0Qp^±3ünÅSyË"Á*°‡ðƒb?o9Á𦠧­« HÞèÖ°Zƒœ}w ,Ç·ûÕfeOüÀ_4úZ.Çk¬T´k2U?šÓ;ïŸ"H"gǶJ óÁ}àZ{;ƒ‚ÐëÞUHNÛóºÀDÊ“&1­ï;‰{¢i3ëv.5œ1ö¢¤ˆüûV¥y*K7ˆ”;[‹™Ãè£N=±&Ï×9Î[”̇ñ½0v<>³`?ÖÖŸ -®íév€ÝXÓ{@Šž9b3},yd'{“yKVUµXÌż‘ :Ñ8†cÜQ£cql ™³ ªÞÚ2ý£¨YÑöÎÉR\æ'áôVèh´i¡>¬rû\‰Ð¢)ñû·_8µÍîc‹ö-¹Èkð^ÁyÇW„hÕ)°ËÛ¯§Ò1¹±!¼á™F+6¹Wzº¨Ì|5àÎØïºO¢ HˆRKùžwçb”šÉ ²¼ZÈžO•Vˆ_±æ£<*¸µ¬ý¬óŒ. ‹W6ŒP¿ëž×¬ôK¾$ž·%MÙƒ¬¼zG^x}44"ŒÔ­í]8-9bÜgvöÕÖªyýdXË¢<Åå–Yú¦‚]¡Æ–Å×EÿSD9ƒ3Öû .IÉR¢I—Îfj+³ÓùL˜”™ÑyÎ~ßn–`ƳŸ·(³¦wÐÑø¦#x¸Ã0Ub´Õ€ÏfÀ _G¨6œ¢™ µ²U;†¶ïö#¤qŸtÓ•’µÄÙ§7_]4‹S&ÂÖW™8ßC{þ€ƒ;¤ð=œp…î·óh2§êÜ[õéRÉÄ>äëøê–°Ã7îC_CJneá5߬Œ£™+´Aq-T}ï¹gBÙ&*nÔÿF¶¾É ›t»äÃñPšCï¯Ë0Â?bÍvˆÊâëY9°ÎRß º¥Âbd›ƒ×>¯¼–ß&Óí£ÊFëæñ’›ëv’ö~óÑ™)ÅUO•¸—d53ÎCìhäõ0ÆH`,ûü}â=Œƒè©d>«…•gï{W(ôiÉq¨!ø/Q ù³¬Z¯ÚÅXØ…ÌqUzÕç– ±ûè³–¬ê_ÝpŸ´Þ$ÅìǦQãSÿé? ïÞ•´`œÌ'ú6ûy½'É^Ò¨ß.(ñê²eËâQ;©"ðá–Îj–nëy¨ÙžkfÅÜ›òvý¬ßΕƒ3ßHçÞ¥—gÁµiÜ,4»æØdeù×U”ø~U#Wýt<á”o:ÁB턲·°ÂÉš¾›/¾u±Ôwã+ûn©0$Q”~©ëí)»ßâÞ vF0|éc‘–‘’bóy-YXúFwäzû7…7ú‰ÊãS^‘ûÑ€êâkÆö²LlI\v±Í´±ÿ%W,EØ~útù'dêæîàD*þ“}‘t¡ìÌAÅB¦ˆö-où‡ìäœC¢=8¶%–°Ô©í/¾ó8i Ûob¾#µ¦°'AÜ‘h^Ù5ä„‘Ñ2¹y®–~ßT ÓÌßÂOœ-CÓ3Üý*ÅßìÓÙªýᶸÚG`‹<Á%5Ä 2¶K.b ¨fƒ(~§¦Ë Î2T“ÆYè¢åî¿ÁkÊüAcjV1ê€ ¨C8+ DZ,©– ‡ëëRÃxõ Ì£ ‡Š‘¼Ä"ª|zI%áÚ{ÕÑÆÐI°ßSéRC©;´wRï¨uBI¹óÙ¦³þ*J~nîÕ¹:~#4ŸFŸâÖM”ÛÉÑE}.l5°@ð2g¢M6÷z^ÊW–öíñêO™oiš‹>Ä`†¢N¦*¯Â–€y¾º%'s].ïg%·%Ë&(cî/Ð,§F@ã5È3ÔÒ-IúÖë³,Χ^ί“‰²¸‰¹RùbsZÆ]ã« gÁK ?iîL×ñq(?g*1—«çð°Ví Y1å cìîÚø¦ÀàÉûvßš×Ñ8÷ë¯<}n™…;93ò#³D˜îWm3Š/ó ‹k6OIM ¢PÉ!E=ÉG=¯€¸íPͨ‘L)1dãš5/EÀ•]‘­c¿OüÑ+:TŠyÍ×uÔov­˜Ä‚/)úôQïu'8­ø¹¤¼bdÊ¿È?Îâ¼Bó³ð "¶û€º3­ zU¿„7(c’PJ\ß}¶Åùšý6PœíéaI ŽQ¾œ³îC˜;Ô&jæ¢AÃG°à™3a©y}Pë¼ý:Ûsû÷é«;bBÙ%Œæ(ùz> Ϙ¿cí%Þ²Ëh Sö0£¸9jøÔ?Ål6¸\Ÿd§±’ý¨ä¥éu¶c [lcøpìúÅ%ï7à‹«>‹ghúõmÚ4¿¡·{C‰è¼]Xë)-@<5´·44O›ºýä©]Ôåg-:.y¥´m!ÿ910cKÜ ïì ¶µÿ ?B.'†Á¡GpCi(¯lÝqÈ{œäXG XŒ{W]ÛÚ##[Íë=ÿ·gd™w9“‰ƒºœ9gg"÷Üw±ÿýƒ‡¹TÖx“þ¸ì÷_¤LèYß•ƒŸ²½íÐ/Þyà•!¼÷a’ίõ¼˜cX+æµÁ±R!Ñ6u@0h8Ùñ{»kqa)‰¼?¹”yÔ‘ç ‰ks S~Y~q:;v²¼Vœ©V£¡ò%•jôÎ]Uø—sb†–emŠr‹üÙï}ùxÇì–2}$V’F¯¿“¥%ÇÙ¼Æ ֖壞bÃ6úÑBÞp= Ó^²¥ð_NBÏã2åRÈG´ÙÕ¼yV˜ŽÿþŠèE|6{ÝÉkÓ{¬c1îixû6’5¼Û;‘{È<¥ÇõH÷Ó¥]¾*N™µí1ÇU³~)YãA±%C‚NéÇ ²Ö] iò½1ýâ ð\hÎÌèÏTÂ×ùqq×ò eÂMóvöpMµ–ö§ ÷ÅRî¥àÑ=fµÒÔú¸7xešt˜$ÓKÎ'Í‚)iø”¦wá¡ å{íc5ézƒ¶í¡]t41-Ô$?§afE_Sã|Ohq™BògÒÍ &ÀâšáêW¸ô´›Yú]®œ¦¢RüJ¬càHþä×À¼JÖ좪:FÅ¥q >öŠC×¢w´²U“U9%9Ä#З*øOĺ†3FÔìó’|é¿SùBÛ>ü€i`:gRr8*/ í׿Ï:4§´‚ÂÞòׯ†(Œ= ŽJñ{eâ <ým4õÂÎOC^”¿TŽq-(–Kd#îÊmíÝp.õÕPQžû`ŸŒ˜ËÝy²5O‘¯³ží:š¶ ?1_§9±qîcõ ¿;‡ß$÷”kMwqÁ4”ÊúܨŸ‰gZØÌ@»‹ùç>)›v´í(•Óe0/ú䦢TEiMfi®Íìíj%ÚôØ—"+ ž]˜‚sçd¤!A0ú àD¿§Raõ¸ÿX ™ÜjŽZ|¶ò¸î[ >¸1&ÄoãôѰ‡ ¥Â ÀÓ1ãe‰éÖ‡« ºÎ÷Ñ9)’>ô\\ò‡å^a/7·»~Ýc¢äõä‘}R[N3W#Ô™0Ääþ¯ÅüCõ@prAÕnRyœø…úBæƒýY4¾œ[ÑŠÀÂ~VÆÜ±ÒÏ ;ø…§T55µ_øO³¿üƒÔtÇu‰<€ÄÆPºÂV­’ùgô±Ö6³ºrèdKãÒSºèÞ-àB.Ñ _u2Jþ,ºŽ¥=ÞSmÓåd·½Ч…ió¯±åæ*ÚµB‰*ƒC˜Î±6Í|ÿ’8ÿq%¢2ym…W vŒH+Òúvƒ«¢£EDÀþ˜ç)¬Ò1}ÂgIh]7†Ö=a¹×…ùרU2L¥4´à*tž¹gÝyKÊ’pÞWdpF÷Ò°ÙœãÅÜÝ2±")÷ZAôe•àWcêð:¬U)½º qFå’ŠÛ[0C2—â°ƒ_êì:nv@e¼×sDÜf„,ÛF¯ks¼€ÖKx’ò9D7¡ŠX˜¨3o«ß¼«‰•L”^oIxüW»_Wè<»ÖófºÏ8G° ÒéuI+µá‚$Z­CŽÀ4xZ¦y§Ú§Û-¾©£Â‹$ÛØZìɸŽb¼¨÷b¾yd.#‡ˆ6„RÆ]O¼‡¸¹8o>;Än^+òº´ÚH¶žÕ»«q«Ý!€'ÞšiÝr‘?º–ÊL>@1ͼ¯DlC•£.t“‡m¨ÔcêK–‚ÚŽH”–„2уújþ— ÜŸulþ ðwr|~Š?€ÒSÌ3ÐŒ¤:[æ,œáÄ¿K´ IÑPCe&÷ˆwÊ2ÛÊvî]~BÈ-z®gg„ÁŽE2k'qüKš;lܽkÛ{ kT:{©rdî;k¸?'”ý/Ý^Ϧô¯³‡4ŒÄ/Tp¼˜zäàiGô‹E…ÉÇžnEZ0ð z ò>\Øuáq€=µeš´ŠÑö=žŠ&%¢à bW-fÄldÄjo¥I*¨„*“¾*ξ €QÝV“¬ ýÅÚ H¾e+MêEüìACÃ-d&=i*@É7 ‚›‰û» ð•9§i¦T‡Çš"Š W¿ÉB‚Îø$2‘×™qùý‹¯ÌATÑ!†éŽJÑôõ#wØ×†aî™þvQ»PfdÄøŒ[¿Ÿó$'"È-9ÛO~!âl¸Å[f_pM›ÞEƒ,´›cÀÅ;ñ\|Ýã¼(Þ!R¿Ð(*.#/‹5ÆIñ#-juÁ1\ø£õˆÛíšíŸ“w͉-åNjïp›\BÞåtâ Ê*öà´ZX–|GT™h×@AY9 ¢Íç$wø‹|i_¾gÍG×;“ÞÇTYÿ4µ•ßÅú$‡SW‘²T Ä´ @Ìv$½~¿fWûqC‹ãਬ½X†MdÙˆœÓ¥!=fI/{9:'AucDiŸjƒ€:Ž›HÁÊC¾¬öLL÷¯ù.R¸*ÜgÚ–7¬…@(Ư`«Ô:W´±Þ æÆå5ç:RE9ï‹(x/>õ1èqò/ŸÑMâÜà·©Ånø¶z#‚¼Ø%1%-qÎëÖ¹ß:çÒònsÙ9h½VS,~9=q}÷Zª¡èWE«9âû{YÝ­“ä—éƒËJ|-ECP»ÂUœ$(äP$÷ðGX¼¶{pdÍ•iÆ¿^á—âÝ3w§ÆPm]8ýnwNO¹‰A{³Çßï~iÜ EYØF\mÊP­“¸/Þæ; î»dÒJ clç³ü_bÝ& ôÊÚ´êùr¦iu9­ïrs˜0ŽÞoÙ… °ºœ’¼ÒíZeÉ7VhŒ1Ú™êSg‰Ÿ|l¥è|Åžëà6dJCœé?©ç¦-CÀ†SÞ …+l¢tf¤<õÈxû.“m|Õäxmh¯ßÀõ ²ÜèΚ¡ž2q~$0«ÿùñ-[cc¬’ˆNŽ‚Ú–!‹¾øD íåÞ¨«p6ãÒV¤0Ý õ.ý«€Ôýú{a<‡q€c³ÿ¸>(†É áYÿxGnê iÀN—ÙjAݯۻ”=sÏîÑ$ivÜ.¤8ìÜ2 Á4¬Ò²ïë¾ ~5ëšôȯåŒbÒª»ªúÖ* «†‹#ñФ½â¶á"GçÈq3â¹ àˆþ‡©®žµ#Üú˜ýê5ågs(,^³tå±Äµq§æF0¸Iǃ R=NôÄžËX2p®i]¡Ç('&†#5ý|·³7¨íËõ­;PÄ÷>šçŠ$d\V/•Ç¢C7C8ßÌŸ™!¬½ êç·ØæÔSŽÄ†t$—¤ìþR3ßd?#'´ç …¨4;Ø€ï¥:¤»NQÁ¿’Šæ/`ÔŠóÜØ$ÒèÏíª$Šê0•¢`+ ËP}NÐ’ßñ¨b]‡¼§¯3:êh/n4 Ø*CˆÊÝÓDmÌtd/æ¿ÈyïœlÉ @"á*ÂÛ<pVŽ?¾9ˆåÕb[‹â-™®k'Nœ¦…“vX\0¿w©4âܪx ‚^µ>GÆ-º_Þ­@Xv\±¥’± "ŒIbbSŽó¶2¶ÜÊóæCÙ®|b×=’¢OéM¾pçn“Äg6Ë-ºõñ¼uubHu™eØ÷PŽ_™>QÂE †ÒS÷ãr‡šÆÁˆ?ÙÍ2ÕÜ«¡Qæž]8ååOYbd¿šEH ߦÈw*!QhdOoì×}G©ÙN‡æô$þpuPx£Ë¶Ø·«wCÈã‡Ì¨]EZí?¡Ñ¼xÂ5a\²ºžÓÁÞ9^?paÅþÙî¥ðÒ˜OÏß—Ç':}1˜~OÒ@0ÄØ$^Òžªk™žngùdÜÚeÞÒTðxëg)Á«§+2"c2_!ïéžX`¯žE5Œ«A7ÒOcë0ùÙÚ)´6ÑVk×æÎ¤ìñ;áSO Áì˜+óìKÿž€l²™0·EÍ<-b”¾m\ÅÓðTÁRˆqü©;ÑRñ̇ó9ï²…\ÜiW)@ÚWÄ}ÜmTÙ¨ÝcãC /eÖñ-ÿ¯¨ m9TÚÕ4NðrKMì5¬àMÏý Bgã~8Ž8¤_M)6{ž²~_¢ôäã)t æDD7#»ÁéÖÛQ/§Om  Áêq§×%2ÖàküÖLZ®Š<ú¼$u†3;²ÊTsòSà1^xŽ@Ÿàͺv“œ^ñ Ó~»jn?Þøà=òFžL*uëë›ñ^Á¤ÂMÃsÊ1lÀ¿; wù1p¾].˜M‰&ÏN¢Zt±qû ìЂaD.(ÕTî]þ+JÏ#ÎÆn‡è>êu}?¦êäÅE¬ùé¼X`ú]hjn?%IÙùë57{îžjüÚŸ\k{O;ª’Ì©´->mY…ÊïæäÜZ.‰´‹±8&£hE(‡ï*~YÛtz–9‰qD ¿Œå¿Qç8ë§U’ÒÓKŒ¸Pd¾$8ÇŽSº4ñ ·8d`Û˜ç¾ezßYÈÊL šªF³9š"Ylv<±çÌõ¼ ïöCFŽtJ¤tÛx½€ÙßHž“ï„ït‘œŽ ­MTMaId\_Ú\‡b^¹-Y@3²™•“EÂkh³¿×b µù¨åXÌ3Å>³‰X±×]\Ù"å9ú-GY·0:a, Q/»˜C­“I,ªzÒÖÀ‰'>ÚW†¥fûÙ˜K'*h âÃA¯]7Éj¯XÊ*Hàò&¥WBIËêTצVÌõ–&Ï93¦î¨»æ»æ’S>æ5&éŠF;á›9G,t¢}lÓÔ}Òàïä‹+K-AB lÕS­ƒÉ¨i¿°wZaÃZƒžçXH£©7_ácØPõ*ôÍ.¿Øm)½$ÛÌк £öÜÀð˜èúÂnRÙ¼Þ8tfMiéâ>“‘ãñ¸Åújj!ÕÏ›’ÛZL!ó›Ì4ay­ÿ¨  <”ÓØšái¾”9"û…¬ÌqbdÄý3—Ä$SÍQù]ÆûUæ”>(æ›22 Ueeï€>»ÂóßKñ‡Ñé¡Õ%)<½c¥™ñëAR}.nI-Îo *0®M>s[ÂÞï«ñXmaèRÓ`t§^õTÌ÷r¥]B§¿Ì‹Ló])·ziAõ™”wg?nW‰Dçi—·Ô¾³Rþ9^¼—Ì•(£(úñé˜4ìl>jËA# ';Ã$ÐŽ…2Çéebwd‚é2™5Sï"hrW»nà%²´‰"`²<»Ù’ Nn‚†¢v°iÐã9½Ä¢Ê½¹Ýî]Ey룤H”€¶}QA]8¤@s½xŸabnñöˆÇÕh½R€˜¤b(U’K¨ˆJ QdXÖ™¶–ÛÌü%X-%}ÀJuó“gô‹sºnMO¡pï…Äà ƒŸ$£ýûìÄFåžø¦~Ï~¾—‹?bkeà8çײͶSV6hYË/ôÀUx1,;'ÿ²qÎQ8Ì·ÑW‡œª2 Ω:>[à-™G݈®ùC%jR~Ga»ï'3ŽÎœ—íBÖîÞúy»Ï´ôñ‰ß,ñÇÏíF5Œ1ì èòþ*±VýW3w?(ùQÖº‡ Û[Cºr4GzÍàåFJEXú¥Õu‘žòníç…¡žƈ¥ž&Ün¢˜ŘOÇ4t•¬?NªØP qK.yár Ÿfò Kÿó@ôHü*ç©È-1Éì€Ï¯¾ q¤Z³Õmvõ{e8¡Óðs.ß¢Øø^c‚çGqŒ²¼¡"3|üë±<ßìæ ÞÜ¢[šod…‡xŒHNyÄùÈù¹¾YLŠæ¨¿x–åM}e°t=ûR“ØðbId)Š{êí{,[K¾o´œ)=ù{­B EùVU§Å7½9Š­0H¡h’޹dPâ–Ð,@}EeޏÈz%¨&œ+vú-šáÊ^K.Æáƒòã®r«&G@Á„ %8ºZÃJf\ôaÀ0Ù¾ÂÁpµàJbˆe­’BäÃ~ ü­vsAöû¿ýé@÷jŸ1¨Ã$‘Ö­ù³ºT]ÒûÍÃÌÏ_=Ü©YUªFúćüNM‘qÆ€Áád^ígÎqc”Ù#•z•Í®·WM„EÛLéè­ðU¨ù$Ò8ŠõXŠ‚ú KyĶæ“M³äʆ*¶bªqžŽ›ŸÇóé¾$n ¤ÂÒõzMÆ™2pŒgy£{*,×»ÇA9Ç=÷â ÍÁ¼sþ@MV$¾]öD»0I U£bZKÆÞ·Üçþ`éAWkªp©5wÞÁ|g-F¾÷Ài›n¯P„Ë‚/?| PÊ{'³äÐ> ÐÿÁ•˵äX÷ž% `&xÕ0‡—œïù|5䳉Æ-K×1‘Pææ±ß)ë’+ bÝËb€$¹Ë|P—¼ƒê°»ä¬‚Š3,°­œÈÌ‹>¸Ii¼qÊóW…¸HÛ5m™*j[ô€ #0cª1úºr|Ñ ¡ß¿[S€ѳŒÙȯꎨ0ŒÄ•Pç´IHôÞ½Êöm-Žeúû]3¬/ž™Îïnz:ÒÙ(™]AÞ-zà ̇­ÿZ¨µÚ;«!ßã&³ÝÜß5³äâ¯Wà Ë/yª;—sò‘à‹.|ºIÙLk÷·ýu.È2»—üB½k–<ú­}‹wLÎB-·7žäHÜNœ­îó8é)¬H¦ mó×Ëæäß[Õ£r¾Q¹FÁ>qò§Žß^" F[Ê‹“ÒSHZ?4nî–»Ì(DY'Slj”h ôŸOñ–vO£{Îx7” 5ÏÏ‹ø‰dØÕQ"%Up¤Í4¯²•¦ùVWœo~r‡½'Ê.Õõ+êÝUŒ¥tF ßÚè¸n äGÙHfHɇÌ¥¨OÿüçóðWâ« HSƒ‚ݵÉ'tž6N–cÄí&m0?œ¦_s´S²ÊãVß/ǺžÇGAÜø!ÈËì[þN,ã+g¾’ •”õ\ØÆ"-D­TŒ7Iµ;¨™\m™¹3’R¨Yßì.'›[ïÛ—Cßõ,ª±p€.ñÉ<ÅñQ!}²ÙàcpSnò£Ð èÝýþ8¹Û'.[¶9i#ˆÎ÷7ÌR¤ÆU5E,Ì`ñ^kÓz}ÎñMËT Ño+eŸ“auå°¡tí¼‰ª¨(Zs{Ô¯}] `­ÿÜU;¨`l¦NÛ­ã×ÐÙÏš¾Dô7èÞfÉ­Lçý‰/éjŸ™NÛ?­›Äׯ¶<àüö{÷hÂÿ:æoßU)àš„4&ý…,!¶¿[_€øˆÃpO¡<Œ¶oímZ7_®ßL†ruvÿ½…•Z%u3²Ikøl„%¢,§ÿAÑúÖÀSϼ÷T³ùWù¤Íž…md$ ,ÙÕ(ð{ÖrOª±Ê.ÛžlãtÓ“sé/eS;Š>íÉr¹â-‰ Šõ'݆ŽøˆùìIMoÂÑ 8{‰fÐà5D1Bh³„Üô‹(”€hÞG<ò¸ b›»¤ƒnO³2;¾Î*p‡*/öC;9(šåÛV¸¹?KdB̃téÌI­¦ÄÈj?x¸†Ü¢mÕ>ÕeÞž¶ò4OCaFQ˜Àܧá¨%B.Õß5;¦A‹¿~Tœæ¿ö˜‚"‡M 8ŸƒwSP{ïÓÉuõ+èv\CG!®„$—5L‰ù0¡@Ã'9@5 Ohnx¶š¼þ€Í$1ÖNÎ5ü²/‚®ˆÇü%¥®2¼¾}¦84Pâkr! <Ñu]²ç ͈Ú!›½Òtúœí%]TJDº:é¦dnÌúpñdz÷ÚéÝú B–œõCT¼ÎÀž–Öd~l—UvŠžáµ°[®{Óý65öœ¥GQL‰*â'·E,nªÓáOÀÒŒxO‰DZ„Tµ¤£¤>$Ã`Fä%Wå—/OËpÛ8âè4YS™ ½ê©˜"sÕ¦jü}Ä] ºJÆeˆpõðòC!gÖö2`J¤ÚNhCœ—r©ŽÂpÿ5ù[ƒØL¶~òä QZÁÕb¯­ä˜}ØÌ&<‘Â{ÒļÌÂår7 q×tøòd†@ªÝÉÌ!–ËkÙ!«‡?¡¡k•e\MCJ\HU“ßÉm†áèw"A†)0°Òöô±… ¨-Äí`‰kâ_óòc~^m¼Iäd¾I ;væøŸ†Cp ð`•Ø"/ïp3””éßwŽ!@_H¼6“í‰ï ~Ôñ†ž¥†:›¿dÒ§×ì¨âDÎ}3B>_V{Ù2ù)3zÚõÙ¶@v ¼2û«§}¨X»ÑÔ…ß=“‰Îf‰h,¡9¨¢1NrpféÏÂ‘ÉØvk•EލqáŒâžÇ=m5›*8¸Ä)|þkyuÕÝG{ö<¼;Î=³ô3@©™š§_qp‹h©R NNÂÇôü‚²3=pÛ bƒÉé^øš„ª<½c(<ÑÕo¸}L\ŠRÛˆža*îš½¶.6_Î+Jh ðqVRƒÆ+›`eY ïBq³/彊´ºhñBÀÿޛӮàJDï}õE'µÐ;,4Î˜Ãøçå–›3!Ò¢ˆØQ² ²˜M]  ¼ÇD,U?"ºë·]ñ¾>¾L£åèi_ ‚Gó=’óò;#5rééR¢ÅÌdT¼9ë»ÃѳÈIî¾ÝÞ ¬D&ÏÕ„k O·½ýdä+™G@?¸Wăn4@}üZ#3ÀNúE¨BŸg=«T~/<á/ÛVØ*˜%JŒ˜øŽhñ¯“G­úO_ òÀaí¨;GŸ‡ôS\ãmù„¬ ù°û¾x²:B±0ÍÉ݇ú_ ùXPÈýþ 欧VSGue¼¸H¡‹ot»mç¡GŽ¥»þZ­ÎôP3î§æ#ÌÚ”áÐn”U—Û,C`?¨¨)î²SšŽó²TÝ>±1‡Ü¢E$<}³'J™0e0Ëóm#n{3¢_•:ÎA¡f ­´AÍlhœÉàHKœÙ!¶©÷ÙLƒö9ĪGEBq@, Ø.çØ;/ ozñ ®{‡¾`™V¦½þ¶3ã2Í`f+ç¨{S©¨©[1£Ï©œï$SC¼™+ð˜AívN“ß“$±žÎY+h<ËYA”Ò_PŽßd±³jç46=Êÿ«.{Å’WV|UZ2á)óH>Ha®îmŽô½J†3°Z¹=…9ݶOOÍï 6kƒ r ^xúr\U`ÜroMAH"ÍÍIÅDðr¢»ª*ª?À›#º­ð”Þ>ÿŠªºŸô‹¼UÓóÐÇgz{#gbá¶œš+A¿«äÈü]ñe8Ìz¥ÑLYétØOjÃ϶³ù Ѹ癣–åÄvðËjpàu?ê²é©ª?»—1cÀõÁ°Ó)éÅâÐf¦Ô\¿?bãþqI???p‘d®hc1ŒM‹%Æ~„#Ï/ûGEûfD uþí;5r«ÂQÏVúþÐ](Ô{SÅ„Cú"NücmHqïœkÉt¼†}wï²Ôò/³>[­–¹.ðKm¿ã¢¿lŒbPM(ò·¤®Yu4žk÷`NÜBÕ×1ú؉»“RC‚ÝÅAܲ[ã¬ä[̪¡}bö»}dÑ•ãm§€šˆÝiÉ»`u;‰ŠÚ[¡lôÐõ‘~mËŽ{…)…íëËGäêJ-m¶.tª+Õ’?þü1ëQKžŠCà™ÑŸÆÀ!KôÈ Pú©þ_*@Õ¿€û9©žìL~AÒÀ0Þ­(ïÕ)hÁ8ÈYWt‰\Ä DèØ [Ù=øŽ`ìã§+jèÅÑulœd毬¯›éštz¼u°GW&¬ ¹.Ê¢ÎZø lSö‰©Yžë êgIíMU¯ ·â(j°÷Žf„…¯]Ô„²÷ª2)üo1:Τ?kk¢"Á–_Wô ThÕ3W\˸âBBÅŸ2(#k½Ë¤m>AýÊÊb‚á€OÛÛk}$fçü8ÙJÈLÙÈßgšåº¥i&-U7ý–YÄ=X&sÊšhozX×'»ìÊ2( µ'ÙÇé/ç’„òZ~\Ú!ç×……ÂVN¥ÕØÛ¾ê‚cœsjñ|é8ð ª5áâ"ú¤Ò(#ÇÜÙ© ¼éjœéÍ QB9]4O X¼j6놈×èNC‘û¹ £°‚9]åëïxÔ_B7¸‚)§VøÀ½Wô÷q3m˜\žÏ=é¶ô?¦¢·Þy(Þkzx4YGO…2Ók2š9M’Xþ^Ní‡ø¤?å? ã)ò ‘@Œš^n)a…Öq(¸3äÿµL1œ÷˜áˆ¹ýxA·Jʆª½áŠZøûíëJ~âªC²ÛÏÜ&"\r"Çs±G )IVk§ÈZÓ-þ<çƒÑpŒ{›Œ›Cs,|ÐÞùÏBò8䵑~KcØÃÆáÜ¡$Ÿ1uæYdš òÓ߆ý¤-€£½F—Ú[ÓF³úîÒÈÒǪGº>*íÞUœ”†HéÑ·»æ¨{NÊ垃‘yïA€ä×3ÐàTê¯ßµQO3„ŠëЗ ö÷GlCÚöÆ-ƒ›µ.$rs¢ó"3ÂÆúè—>ɇ·ã)©ëg6­æã÷Ó„U¿öŸ½òÍüAÉB¼)z½?7ºËSc‰GÒ¹ws¶ý Ó±F£Š!Û@n•‡t[š+éëÌñJP\«'ˆ Õ8/o™d;žbGµû±ùàÈÑ]Gå…L« 5Íò²¶¬%z!Lj‘ö@&í³×<“uîx_DKþÎ닊W³ ßÀé¼cÎ|û›ï&9¢ÓÂ×Öfhׯ‹¥#ãà²Q¼–ÊÝHx¢UÑA·exí7Z¥ZºæuÙoÄÞa̱˜ÎãzÄÑùÞpÕ; I1=Îïd©µ-n|tçZo(ø/¢Y®´Ÿ¸NM09Rë}½X7îÊ!‚4yçkãtŒæÎäK©”Ê9ݦÖÉ´š[=¯åEFIÙÔ9ÔU­!ýÕ£å?u;³_>Í +<êO¦ OŒtû^U±·Û~"ÌƆ;WÜÛÊ¢qjrxÚ“?yxP†˜.°'VÛG% ‰CoÒ鈈&(ì)ær<ç¾/k åàÝЄ¬­·w¸{óƒ•[§ÖÜgÖè Ñ8 vçÅ2J§s!»³ çÔ|xQƒÅálœ’Føßú P ›X¯¨‘ÙÝux9IuÉ(ø´ô²Â°±]øË²Ì ±}Œ?.õ7íàÈí^„ )RFŪ!Á»—ê°èúÞFˆS s-ÈÃ\°†qC ”¥«‚Æ_Œ§ÀäðAU-ÿ³ÏaÛû¦¼Ümÿ!»°¤2Áf{e\–÷|mjˆË²È™7!5(ªȪ°FóUÕ%íÄ8q·nwµ¾;ë§dgm¿ýÆÖú![å3Öþ‘ŠÐÆ¿/ÛÒŠ´åÅ>?ƒgc”af³ n¬¹|óëlŸL* ^#ª(à{DMx¢';ö³¤z[м7Úº‘:^W€äÂc&gU°|–ÄX‡ñ«ˆ4‚¨’¬\^~¶¯qݶ¢V#‰yÅøÑÕú—áKÃ…3J§|‘4]Iùgd»òg\!"/ƒˆ¦Û¢gRô!®ëûÖÄs›o!Y¢GíA®is_8Tø«ÞºU"70ãmœÔÊ@ž*}»x¿Ø\W$ 0 †!ºþ+P%â9IyýÌñd+’¢CcL“Bk-íýjÈ^›  w{ï«$ý•TðÈ D…¤}Mæ=U“©¨7X ŸÙ#±@õUO˽N.8¬Ç¡³×T}vþÞï±tÓ0ôý.Ÿì#Ñ®ÂÝÌÂMìÔ•IOÁø Ͻßtõ'™i‘¬²ok%hIÄ.3a<¿žJ_F{DÛ(©AøÚðÈÑ,CèÆùf]~iþÙr Õ¦vÞF!`îB£¾!Ag°±š£jšŒ´; éÞomƒpkazŠZ(Îþ®Ï(/ô#@®"¾l™û+‚€Zý7ƒxçyÈJu¿éSpWhÈ(:ÉÇbøC’î¶Õ{™X‚n£ã}qBû?g}Ä\ùÇ LN6hJÇ…˜IíYª@r èÖL™éÊN¸ÍÑÚ mð@r?çuI~àUœ>—Sú@WîX|ƺ¿øÏ; Ô»ÿ@£¸ F¡V¹öáë£éÞæûebA¸GÿP_é3¥jK³³h˜ö¤ÿCÓŠÊW®%Lï2¤që!0SY(„À–ÚK¡_ìñU¯Ÿ/¿Kks`„¸6“Ê ¼tש½Y6ó{”CÈbFo†2þ™†¾ëƒöF­t…ŠÒÈXN÷§è}í OÅ‚çБŠt𞬠1Iø,Ç¢Þà¼|ûbÇKP•]²gÓª^qŒÅ¡ð­¶Œ|i²à0ÐèÑ¡i¬ —Kt’ö,ÍVµËÙhy%ä+Ï«e¿ñ'øïWc1[ÛèÐÀ÷ØAg0¯ä,“åÖ™‰a %H%ú6¼74bÑ…†ìM‡/‚Y(í )(îõšp@Ë‘ª~ìVnœArÐáß}4.¤ºï"¡—Tt¡²—f"Ѹ̱¾¨ß.+@·[Âlè»Ëm)"’"mX/jvLÐÚÏ©©ü˜‰’¹ŽÛ¤ÚiñV\PS9ïb¨ ðÁ£}äÞ¶D¡_[ÞÃMiýÅ· OWóÉ®¨÷#ÂE¿Þ¼}á3T¸jý*ë>|BÀtè†5¶ÀÌ׼ȫ“=(÷»º÷zw×(öŒED]«€j›¹8³q*¶[>¦±qF—Ó¹mËgÂ,‰yV8¹Ó«æŸô¹²uSUfŠã)0BûÁ’·ÔŸ\å*r©b‰Jº+»¹µgêÔ[¬.$Æ– ÷î´…zŠîÁŸÜ3ˆõ¯…F[gØ4 /–áSï[íèZì}¯vi«5´¬ê|˳ùÚÛ¹¨å¦Îו]®)dš:ŒwÌÔʲé(—W@k²Eœó£57ƒ[{|78m89Ö‹;  ÄW•3}ŠEjõ„ç†aäd@Röf¶R·<ŽßÞÊiù‡ý(7Aç#po Tã¦p¹Â°Î¨Àžd3„uèál¸YÇ¢Ço='E—û6eM¸%uÕzø'_°E;iTï-KšE·0‹”_ëg¨Ž©žf¾ÙB Lsë㣚èIâß/÷á?ø’ŒJ¢Â&:zŽ‘W!ŸzÐÐZ‰m~‚“ Þ€ÿ-*~ä÷®Ø¢\Øböƒ{‹g_‹>x'fjåUœŸIF`í!JÄŒ¸“X‘äì mŠ}qÖk <È’þ*V€ã€M­E¹ÀÚ'%YŽêU²TG’q¹ñ=Шžùe¨ÿ¦ÌKwêÙüú¦[u§t«©…\ÖûŸ¯F`¢ÈçøÊQéÛâ–IS‰°Ùä\í¶^ãLžÀŸõÜ%Ž!DtÇìç3OsÑg»O¤øªÂ5¼†6ˆ´>ü*ÞŽ˜ð•üK3„$mcr9FŒQ‹Ku*Ó(¬á›–Áº¹÷Ü™aÅ*ÖšjÒäçF–@þ±³3s f ¢DäÄsŒý8dšqw¦ÅfÊíÛ5æ¹Aîz·6ñ«.*H`·`þê^úæ9ÃΧ؃÷!Å-jc(R‚ €| ê·æÖË™qíǹ…3Q³VvHHœ­ýÝì~Ìc[œ@ÀÒ#ÿþ"\ˆ¦Ùì{ú¿²·_›Q½m~l§~6k+ÖyFÊËéPˆ°È!‡“ Éÿ+CïeâD/ú0Zì•r°4è í×ñTm¯‘§Nõú©4¢= ¿dj¬N²ø©õü£›íÔ1D†ÌŒR¦>àä\êj(ÎìÕQÕiTí´{ì3Ù-×táºùî*d UáHCêfËdC4†€Ãw ÿöL¥\„n³êÖ Ôãü øß*^¦î» 6YAX™ªï~S¯ÕÎ0¾“òÜò5V¦êòŸ°Ò—O N5'%[òí±ÝáVï*oO~=ù"¹FkíÊš5y*!±!÷·õ œm*ÿZ(=œ`سša‹ôMÈ@ì¸I¿Ì¹§ƒvG¥·¶ìJØ0€X#·§ƒxbÂØ†P£Ó‹ùÁ.77o>@`òЕáò”’ï€râXÜÑibAÚ5_,‘¨RPy¹}v K²4ù^Qu Q‚Tl¥öke³ý•ß0uˆÐ¥a,a¤­^)Jo=˜ë½‹*/«ÿH§ž—ü~z« ñúŸœrJwßÅ>RQ<ñÙÜ•Ê{ÂXñP§ð”çÛ‘é‘Eñq^µ·…Ò«hcª‰—+øRO€Â=ƒšu¼Æ­›¬W&êšKÔNŸÍÐÿ‚ BD òò)i€MZˆÓŒ #ÑE8]‰cƒ´•úWµê¥€d®#ågük<ýv—>À'ßHv…së¾ÑtÍ_LY#3ÆÞ gà0»Ò67 –®£º¹$Ñ–eŠv:ŽÄ(Îà¹GÒåšÔü¢Àu99Íæ*–)0ÓÐê¼u¬bÀ]ÀY"MžU—{܈öv2?ÌÀþÍv! ,~P„ÅØªlÓÂQÄ9»×º8Õ: 2)›¾;Ÿøfg}_ísïúý&fç…án‡„ð™ÝQpíïbªC‘ëáQ<†Jb*RÆDôÜ3Õ^PP¢ÏV=f1§rÐáTÇ=ypµè'! 1´WǧdÛb= ô7¸@YvöDÖ<#ÝÚƒúMÖგYå¡EÜØý­5½oqÏ’h°CwéÚØé¾Ãß’ÂñçïѬê%¬¹!™Ù± ‘QƒchÑ¢!Zrë Ù‹lªÀ(pciXüŠ®|¬©Z;Ψ¢$=ËgÓ»Cß’ȲSÖE!è.ýx QÆ)¨…"T$¨º\´N(`åö@ËóÇ.@xSÑgÔ]ö?dB¡Ð¤ö‰Ýn§ÑÞ—ø\WÞv\Ö²ó@0/]¢ZåCÜ‚mÑ´Õ¡Ùÿ;±R9ØÒÛÈm$Vo* ¦ë¦<™ý>‚¦lùàèÄ3Æ/¿øi°„ â3ÀãC»D™aƒ;¢’=ªâXäk"Ï€yuiªô#Ãã©þRîjÂDËhö”U–o‹X¯~JÄsÖSXòà<Þ¸À6öR¦« ˜0ý.(âónèÈöì1p€à`ÄÉO[TÍâN4Mß÷/»ˆŽí\]I*„8ïÀ 7Ôz&Â3£] 0`x’í’\Uä°ßÎÊ`‘À/Üij…©0HÁ ¬×òT…Àw›>aýyM¢™E_,Ää‡BÁn]æÿÛØ× Y0 ´h+µ¤_˜¨ÿpŸA=ñ0ÍMS´‘5ÿ§£ü~ùÃWNµÉ¤ƒdôÏ´›ûÌùB¿q¥ZÙ,®e¿þW<Öfž&iÒ6ªÿõfjж÷0šüÀ2õ–-Ip±„›O5 Â7aþƒÌ…ÉR%“í|ž*´°ƒà¤ áþ5¾À—M"Ýy®ÛS{jÄÓ˜.À^ªḧËrY^5—á{ üP Jý•¸á~ƒiù1„-M^®¯¨"­D0@2vV°ÒÜjM$…˜—ÄuoQ‡œ±“z[#:·l dɯ³Ü]‘:ݱ¼4˜€9‘áyð›J®ÿÛ Af»Ã¨wQy¤§ š|Ëñ߯•$K }¾á*>¼8 öމDlý?¨ïs¥5ǹ‚Qä·¾ú_j…÷N G̉GìAKŸ(p²õÍ÷3ºÕ ÅeòTôˆ/0³W>a=]äÏH;Ïy•u €Ýϗ悸qËî Ÿ/—5lóüè9: ™€ææYÕó£CÊà_êÃQ€·§3"‹þ*úµéð=Ï a>Ü÷;›úNäø¦ô¶ž¯ŸFz‚òÅÒZ-1daPª›ø†c¹~ýJÝ—D¨ÂÑù×_e XÍÔµ75:˜oã¾Zwåzyd†2À+H.6PÙ oµLí[Ø"Ò'º_üY÷iÐg1Á¾g“°{~xŸçÃ[X…Šðœ¶Å„;‡IX7O.:~‡*èŠv<š¹Îfú¿QTEr™AmtíÖ*wÓ pꀞ³·—²\øóãzÇG™òÚèÙ%˜^·Í·§ŽÓîîªòš®u|9pÖ÷]G¾îP]M ‡ yj> õÒÓ‘pÎ~S<ú§øHnó’~  “þ™¸)|+˜Æ!]KP’S8ºYWŒ™e^’.´ïÄ´&Û(Å3<äö ø‰gv͆†¹Tð<áÿ™hï:m–›(3ÿoH¾A_³E¶?üìØl†¼³¤ßÓdOIÿUg ^ûÞbÀÓéŽYMºz´Öþ¯zÈî ß²· nò†Ô× tareCÿ Ó­ÊF"™ÙÊÎhJZÂ`4N­¨™ÏAL¨„çX´vƒšëäA-ßïŸð°¡¸ÂM))‹"Ë(Ç K¼8J…6¾>âõe{…M–¢¦†l Zöî"lœ Í]…»nᣠ2àêàµo£V(¶Ô¶„¶Çk4ú­¤‚rS=ä§3­¬ÔÅê­Ç2¢WBŒ\zánø[UŸ° épèñi‘ .^Bü®ÝqÊ6¿=IÀÜð¨ÀÊ[i®1O°´‘¢²)I;5ö:â]»fY'ù{(RMfnÃ>K¨¿¬ÚpPÒ=”TÚØZ×±j ÁL/5í®Ž”s?çë$"BO>k؃‹•hp\aÞÅÞ7HÙè‚ð3EʆªÄRÈWW%ª%ߊʦÀÉŒÚmÕõ#²oÛ¨*ä7jPM†÷ò™Ëcèíbɤ%ØÛŽÀÈ”@d!•îîÍÿsxæé‰o9j[¯¸ˆ †!ºþ+1¶ `‡` J‘•¥0é|졦Z~=¥5•.ø•%’gŠÎiýg*gËzˆ3_µWÒ)ÿÐ8Æ—¡®æöbfæ¿Óäš3¿ åè'[tûZœn³’=Ø'4ÔG¥ßsüà6—ŽŽÑCâ\büÎÃð@·ÁßšÛÈ?%]…'n^G÷„xí”9dsÂT6«²Œ}ÖORÑ»Lv3îþ}ãÛšƒ'èütÉml nåBÝl‰ë£”І¤*ŠÊì›è$Ñ"y‚Ãá'MËVq>ÞMí|™á òw0Êc¬uX‡·æ¢%9«ph€Ï8ój,R˜9õ¥ÚƵçü¨tŽ…ÍOç+’x+Ú÷·áÙQ™n¦£ICÍŸJþÇ å*I‘“LQ¦‰6M-ÿn[î7[~¨ Ū6ޤü¤4Ñî£Cž¦9Šr` 'ôÊÁϸG1¡ _¡\·\O®ç„?a«>h„wØÏ#D£°^bŸº=¼“Œnµáðkí®ì—èü”°%|jÅÍSàdÁ‘<Ýßëx°ãÀ$Ï5 n›_{qát(ïjY/ƒ> ó¯àNGJ6v¬­Þ0ÚäæG ë3Xþ #°«ødÊÇ^KÜ8Û÷dX¤Y¿8®ûÃÄÇÂȸ$I»v¯ÊÎ@‰¹¦ƒ¾Äbb1&DÃ6Dõ°]õ›°YÜ1¾oÕ!u„í›e£)À4 ù¶ [äê_%R¨ìœû˜cIN ‡§%õ ‡'¥]Å8KáhEGJ_Äà˜Õ§¨uÚ…ƒoäÖ:ö†ŽþÚ߈­,ûY™c0|q†ÈÍÉø¶&–pÂ2ÕvBe_íFÔ•*OÖŠ Ã/æ9Åf4È=çädë¤XtUßlÆôµ™“ÌcG[»ŒFÔvÝ]½ÓÙc85±áMµ-Ä£yý©÷¿¨`UÛj HQ¦ÕZ†Bõæ9Wyç2-ç‘—gÉ%œSS~—¡Cß„Wª „§ñï³rÙÎâÏÞY€ge/@EWÛ‰Pù™¶ÜƒËßÿ ú`†¡ õKëá ÍHÉAN@Bßµ “ïT:áN*´ýý@ÃÑ´ 4hìÈá”ò(NôšsQÔØ§ü°vMÇÓç€åD°ÒÁ6%¹8ö:¢¨~©"ú–ÊÂöGcäffìo‹ÛÌoÁ#Ë|‘;u@bý„*"8‰äW¡÷ ×’’¶ ‹’—ø¶ [ä¬BxtcµSòëYÛ¾åãs^;¿¥[sÆÅáe†oÜ„+ÒU5цô÷Wú5†æ¦øp³#ôߟ«[©ß~Œíë¼4Pë—-ß)=˸ßÞ3ðœ0S¦ÐútxSúmB. Âu4I.9¸ñE iŽa3ÌãÒc»Â¨{Xä/@‡Õˆºú5ÿõm´­Gï }”bpodšn›ezÊÁéšµ–íºmøG„^ÿMëfœÛz·$iŒQ©ïô½ÃÄ#HémV‰Ü]êö8À²@Mfj‚ ì.°Õ¨¥l0QNyVh2Ÿ+8?·ä.„›£ÚhŽG[æÎúC©¼ÒÿµóãªÝlGzÞþñÒÛÃ9I÷ûÉò‹¾î·d…Rô8ÇÌcä=T© 9ë[xe»rÀ¹éÏâáÂÌ–t¥Ã9øŒìîªx܃‚*‘pƒ,šÎ |€7›ù’ÞXˆ¶ åÚŠóéçš°¬Cxôý¤m}3›|e!³¼N¾Aï_“g?‡îC¼[]én=`šÓ4rz€¿QITÑñ“ñ»•#ã¦zôø†œ­‹“Xñ›þ6æçôL½cËF`q¿ya4›$§<ÒI)©u£2Œë%JÕc¨0™Cà€l\‘>^Í·jKަ(A„Ü{>RÍ{iW‡-*¦|UMcÊ€‰IŒƒO `U¡ƒ8|ü¬SMªßÐXn“°³“~Ñɦ–ÏWj¤,2‰e¸Î<£õÐvj ÓÙÞ¯öu¦žÇqeÖ ‡Uo`û‘)'7|Li­ ·ÂÒ*>1‡¿Hû}ºT&–mê„)(aÒä\p<=8Ò oˆæL»ávJ¦ŸÄ1YìÒÞŒìä˜ìøŸý2Çoçê?Ø45 VLËn£–û× azˆ/%ÁµôxI£t‰Uàè%®Ú`_;‹@6 l&”Ûºø‚øÞ°@”O4íÖïÞ˜•8§× b Ò ÷œë ¾QBLг‹žêÿ,IbÝjóûèÇôo—}zzÒ|Ð=våj#@rq!Ý_¬o­+q­4ðîDÀˆõá×ì?b/RQz‹">?—¦È o÷BDs»þ¥TŽ/&B^ßÛ^³'¡n`Hü¤?¦£µb²¡.þß¾N”ôÈØ±{ðÃzkûá2P& È«ú€°tš òîfà]zÏii}ŒŸS°ta”’IïɆÕVÅãHç£JR‡-üfÕ…®1€5©^j &.Ê”Ìa¥&O¥½©Q¨ªZ·DŽ6<7 þܘ?3’¸6P»õ Øéo~Æã›‘äã\Â:ú¥ù¸Ò+\w™M’ùJ„©+j¡Ö:¿@ûbÜkÂ%BS†u¹]Œò¶Éœ¸†8 II9;ðn€›kŠWì‹"mLî óLº£\㔃=½>{‰2ÜË(ÚHjYë›yæVBVà°ˆ{u—"§´€Ž/¸Y¾nÛ|»Ó\­]˜ÿåÐ_j%¹&aoÏlܘŽQH¤64Ó²±ƒ_F_¾.8²l(ÓØÅø(Iêmã¶ä22±c°FV0z~ݳU¤n?ÂÒ\˜1F”Jj~äÓXóã†Fþ‹:d ·7Ð$ŒVLÇ\M~ÁÇ#³0d>éË)rš©³6”•IAÉþ¯Kï—ø‚¼k}ØÅ8–湊éÏZ³,›ÚÚ¾p^ÁÔQ_ H| &DyšºQY“Ç>ÓJMër7÷ŒÍ>>W$D9NÙ®˜ô±ã‘ß6”V=]I$•øÇ$š$ÂBŸ}±Ä¤1´¢±èܾ(E|ç´$^:z ‡®­Ï–ôu}[Ë׈ÇzŸ³hkÛÉ'Fxø>ùdhŨʃ¤l{æ&c»­½Å[ ¾mÁÞW5 Ð&0”ºWÁUž;†¢`ü{? ­­¥¹ûVš(#WõÏÔÎà+1¬`W7dÝþ´4Çò¹^È϶:£[eQBçq;ïGÒFRî5™ç?Ó’8øæˆ5DÑȧ—;CÜ" R¢®×ó0x‰¢"¨0…uÅ¢¢)°i+i²ö±x\¼¢«ˆø–`ÁÄf«£ìvöâà•ÆDNé]ôEà–W™ÑÑ2[$Ž ,ô òYî6$Ÿ:2¸d–Dy¢ª'’µ[·B.„~Pcfõ±[Ážd'IgPе5;d(«ºÇþâÓ!ÉÆÞ?è£GIŽ_Wñ•ÍÑàåN} £Èøþ#_G>48(í3QOñmn_ØL®ˆáhÐÁm­d À˜­Kb‰Ë^9hõÓÚK([t›*W$¤37¹öÍü}˜þpÜÛ˱ÍíAª$Šœ7>,"¿WnÕX¿é…Õ3ô^Xí„óGôòMœIÏŠISªèùNÈ™òÆ9Rƒ„µ&ÿߢ02ý±ÌÝ¢*õ(ŽñÏ’•»wÿTðÌÊÂxºÍÔ©„ì¹o0Å3üHt½;ŠP¶)šþÏ$*tz’®Üì>³„xœ¡ ÿº™4¶ø~O[A¦®£¦E˜DzÇÝö0†SöIz±3㪅UÍ›µ­\\Ô¢ô„7[`0:Ÿ(JC7Âïªv`µò½a¥« È+µ‚Î*ü4W~¸‚»‘ÛúÖý¼åuªcV•&µ;Òÿ»þÓ8çHHöJjG‘T*iôpû˜ÃíƒBðêîWüêldl­ˆãârEʃª’­¥¹ÞMO;‡“™}[€A©™] Š*c`a‹Ÿõ~€•m?°@.òiY¬Ëý±~¥„ºWÅP÷‹œ07‡mF6VœÕ”þ2°äá"’ ¯yd¼1£âNT7sî3s´svzJì15Å;L±5¿äº2´¨T®¿„Èzë¨È!£o£ãùiù­ Cz¸OSZígJð\GtuªÍC¬·êÄŠ;${âøØ,¢—á?cA¨¹ú«ûÜhÆë]¢-r8|ëàaeÆ:¾ÙÌ’·jË·x£ìvA¸x½vëÒx,“ë«RQHØ>é;sK¹KQ7ëY^jN¥žQšIT1& B¬¼bQà[ÝúšÕß³`c®mË+W+ŽV~$oÿeмáÂ…œålB$ûS_±ulqɵïwé‚_Ì~ƒõÏ!ñ8¥  "@ùº,5I~VõS¨vr¾ò‘BÇÇÄÇ_•ï£xºÊûAÀ›AîÚËÜ@ë¹B³®+ÓÙ&Û«‘ncÑ↠S³Äc¤äYU$GE’à˜y7©@ ÝÝÓ©ˆòàŒÁA(5ÓÔb§ÞõJOÚ zYh—…6–(Lù §”¶VäÅþ•E nÕ»~p®lMí9<ñtN+ÁIÍ®½œK:Özf¯ ÐSdeðgupLs­Éû-;ˆÞ¿D䋆õ]S*ß3ÝWg´î¶‰ĪGi¢¡Ëü¹EnšP~W`˜(’äRÔ¦#®|‹ÜFèÆfí€sð¬‚yh¢]Z¶®Káâ¸ýˆ¾%@e!Ôæ <â@îñ’úêž8R)fiùwÐ’×Nn¿!“!É Ä´—¾E/šº•sóraßLžèüÙºG¢©ÎÚæst¾ù]”Ž+Ñ¿gXv÷Šk™2é$î#o bÖ"Ú ´çÛÙjm º¡åe?Æ•¬3VW?<ÇÔJþ+¶ÃȤƒKyäC‘A?Bÿ9¿ 󒙵݀ >O…“Kêoüö^{x(0=>Ù5Sd7wR j þõŽ¿åDÐa1ÓK±Ð¸^·Yeηû‹ñOÍGÙ#Šá݈›èB3ÌSÿH¸'Õ Ò­3ƒÁÍR ÙqÄ~€&tGí$Ë4nŠHFm`®¸b03SÓ Ö<éeu=]Íñj"Oe€U€ôëÿ‡¹ˆüìâ A#·ºzçJçFbp»TO†ñ7ÍEý0¢p1ÎBWZŽXÿ{é]oD× ·oIoÚ :YÏÄÅÎ Æ—^×VYm‹&ÞØ:>¤¡2§Z%XÆ/0"jzdÉ¿0­~rX}ä=\nf@ÅÇ–<«05ðóU€gºµaf|èÍ( „·ÖÿPjûþ˜Fë¦Á—,ÃÁ¹-Pö±ðÑè[®¶7b=ØÝ…æ\jEmîoHð…ï)'¨³Ä0N:è¾-2Â2ŸšPص‰éWYg_»¨„G‚ ÷; Ï…Ôàù L›³7«ì4Mî¬ÈíQÙ:…[c óÐðw‰¬ Ä9A‹Žó X6J™÷dB ~1~Œ'Ë0KM’%ÐpNÑ?ôªiIøER0ž²#÷&öê-(Áv` Öï$Ä)–ã mž³=wKg>ØÅ–™Ž—’úlSZœ²Ëø¥ü; ´ß'bþŒÎÀ¡Õ± ± =!µÉ“Y/((­ŠKžÆØ‘kûkµµ“®Â÷_Ôó×l ›†§•ò{ Ø(^ø†3üÏ:ðõý!7swÙããa>oK-jÏÝ@\AÙ½ÂkEô¤þyêÆ3’SkGLØI(‚6±þ¶ïM—s­iÉÈ-—h°ì9@ö–¬eÏø¨¥ŸÛ΂ôÔi{ç+{ÕfŸ¨¿ìŸu&Tiå{ФT¥N{7tâ‹«Y]Ã,ŽftJ[ÛômÛ ëvx\Ã_6”fÞÀÏÆŽX5з·û^äßåÖ-=J“‘馷SÃÙ çJ~þfʶÿýÊ’÷Ä"®8ÚâÉ1/âüt:©ä/×”J«©Ñ_ú£†$?;š"ÿ†ùŒœã´¨9”<’¦1ËÅô“ž2 hçòè¶+h*æðˆ)€ ;"¡ù\$%ßB¨œv¯×Y-~uG¼[mw²³ÊœÑguû3gu@Ýv{Í4;0.E-õV#dçÌÇÈ›py¢°Ú¯>fíÍ-[+he™°²ªúž[Š·£<+ ÔÒr½~Ï_ðÉøÈ‡§œÜÝÚ ±¢_SÕˆKÏô§“¦¢ø9²,WTŽª–}<)„£zßR¿ÛIk*˜½8h$ æ(H ÓEºˆ¬Æ—wwÐršV©§ù¨)—GrÃK$lbâÓa²()Ü«„ujW% Wy?³k¼#V8ànêÉ%Œ5Yôà ,0dCt¼.ñ ¶¨–)ǯm[´Õ÷;/WŠbœ[•…M{”-wœ%g(s›Š‚BK NÎ#S“ŽŽêEžèÌTž^2D£E0©ˆ %š 5mH5|5ŽÚÁ£lÐW¥–¦q,Ô[ YUý7®R¦ø¼†ª•Ù0ˆ¸£ÓM"=öè‚ô/ž+cY±MÞŠmõ¬æ€-ßM„j[Ë–c÷×ÖÁóxÑ=\P u·ä´iÊ–°4ºâ-úÄzý(­Šóþi$æ¤H„ÚÈ™à02M X.“‹·ê[†›W“ç¢gz°Aà°ðö°Ø‹÷©˜î<ãX¯?ºóOn&Œäá"§V#MKÇþ¬(~G¸R6„šXh²*†“¹Ï'`Sá(å·*{Ý75ýRʤ‚a `EØø$r‰&53ÆV [½&ÔÏ[¹d€…"/…iõªàSûG8ßÀ<&än<š‹§ŽÐã øKUÎP·^É}bª,ïmF¸ÈÅn±JËN«4¼‹¬_|–<üN·Æ€ÏQÓ¥ûð á°¿\r0'-~£¿Ÿ‘¢ïkæ"vR²÷Éé2¼ÖmYÔ1ƒcÙЇ “Ë}h[oG &¾éÎÎ/ä˜iF!7^•óHާ$«/=‰]!#Œ\æèÓ¹û\f¯ï{Ì¿8Ó=N£ï%®ŒJ â͙ڊˆºÜUû›J£ßrÁ“cÙ*ÒÀžÓìÅK½%îø„ÿµYn«ñ¹KP^*Ïã¾JJG<Ó¾Møœ0:ǃæÓê\³‚{ÀÜ೸N!¶Êc'±8Ø¿¬}-C<%\æÑ-mñ_nõï‹¡­ô=Ml§Ü˜’ wpÙ;g*ç/2‘¤ø:VÓ‹ºÆ®iä:?@£bÕ…ãÕãv’BÁqƆHMPã‰Ê»ØŒ¾MÿׇÑ'±gMÔ FÌrQºzv4K¾þè ¹ ÒŠè6Ÿ¿³§€$#öyQïMµ²ŒN¾Aæ—ÖÖ±ƒñu,’1¸EËšÕb3F¢Dï¾ÓŽK‰g ²Z)É^P{Ò{ûZæ-šDoÌ”ù&7‰¤©•¾eÃXâl"wnæÃ,|º ðŽµ¼„¯i nšZo:Q_ç$'gÑíƒUß´fðÿD‰/ÒÑ ØÚ¿ôoÕ—ž4ëÁlc&í|áÍŽ€( O“^U¹xâfÐ¥y~åó<†6!Ëf¢ÞßvHÁ¥’`è.çvÃøU”úµÃ–ˆr ?5ñ.'¹Nf½ âV’› !¬|/«+‚ô)a;ÐÌÄ€ñoþŠøÊïT¢Gç܆¦Ws®ú ãPç­®~!”ú5‹¾Ý5.dèÞÉ ÛAð„¥K@´^=å©Ä³ðfoµð.ð¥9ÂM !Weê'ó´?˔̫Ÿ™² ž Üì}úª×ÆZ¦j.N'6*`Ø<ïð_îú¼ ý÷·„*u+‡a¿óΑMTL¸&ÚÞûýª&5¼àÒG$š5þ0úÎ å_à•Mé-VT~mEÈ?)õ\‰@åÔ€]3Ëå‘YÃ~iÔ3hö‹‡¸ ë›Ï ³ÊÍ–¾SìmB`}©$ÿº0yü| îºÅm8®Œ^{Ÿ“êyUÕö“~ƒtVIŒ£³ï˜ŒZº×4w9€@Šnç©è(wdrÑs•Ôýu¦(6÷ûüG©’È ¢Ç„/Êí.7ãU?í» ¾†n¢zËc<šP!ŠØ¡&x–¤ƒi³þÌÇhXžøÒC†öýóU…4†Éœàk"Ï|èPwgâù¬]PˆeA:´œ;±#3sOkïë}PhpµÐTHîÂz<ßË;˜—Òúˆ¥5_0W!áÃó§rEot_æåW‡.‡¼³™Qæƒ\w5.õ1Ç_Yƒ¾{|ì¦Z†€»¢C© ·-‘K›D<¡Àe˜Í+úêó7'¿Øm=EáâõâxÁ¸ÍÁ©HVæ fß8Ž!ŸK]±cÙò»q^n^rÈÍæÕq´=ÜE«îšNàå¥,ÀpÒj„êY­E²§=¿?>íË«Àá qÿÿêfÆ^·¢ró+vßDEšà˜žœ6üosKÈZCƒó€Çp­¢nÝuTÐF—ÓM…‘å÷…Åà☤äLþÐÁðÖ5Ù%í.ÜNo®«‡™ü O…¸\Ðm'Ãl¹9¡DU‡“Ù}g£@gtyjò‰ÛlÙ šZ£Íhpí=ˆ’+Äc³ÉX µo3浉 uɃº®öè’<%†gýAÉØfA¸"#o“ KÕÌ;2ã`ezX„á_Ë›&Œ–tw€8#ùgf‰<,*[¸{ñ´vfXÔ\Û¢™Ô¦›¿~l°£HšÓGERå¢ý Ú-ÚýåY'ݰ‡«Ñ×'åxL˜6–*â- ²40Q–ò+~J^Ž8'wÕ¼BéwÂOÊŽ_ÍKÇGR_£‘ÞS²”7nÓÒmÇÔJ»ÝpàIù•öÂ@Èðæq´z…9_@)2‰ŽÏ‹ãüWgÅ!md›4Ûð]që—‰¸¹ ­Ðq'ª\QúÈ‘\Ù6?¸uœèÿlÈÒnÕ2fó( ³šÑ!6âdïúÃ3/Vv‘³8ñF/ ×CdâË!í3°ôZCªþFŒ ØÇ«êœèBr¦ç*!R“C¸È»š*z³{j]Ëãá•YL ó²È«ŒõÅÐÅÔÉ=Ív"©­å½!ßrv.­»¹¬ã6Gß*ôKᮋ®[KòVr }Í%Èdqiÿqéq”+¢FƒÀÓiìôb ±+|ä±_dHíIvŽ’tÚDÚ=U.¸mÒmÙ?,²ÅLˆZ6t­¢U¶¿3 çø¹b2M±=ßkq´niäÈÞ·¥Q{ÛÝ”´Ó•¸<ˆZ4êð„0‚lN•”gj] d±‡ÆúŒ"€3vò´¾Q_š, »4ÙdÖܸ¬di†Kü*'¹Y6RDv<&Ö7wš«¿À7¼B_+ÝÈÙ›ó†ÄO§^”+ +©e •¥ú!:TÅàIS h­¯Yãß”K‰3}f¢øVéÉ,³„îiŒZY°ç·ÝT•eì”±G‡&€ÛÕ`Çúöö™ìcUÊŠ-Ebíc 3®Wñ™–REI`(Üf¡a5.¾f¶…šíûÝ|ˆ†^ãºÒ”$ðhÏÞCÉÁóìmƒ7Âñ%Þñ®Z ”2v eK’l›Ê!”¡"s÷dV˜“ž_5(=%Q{¼g2°ýæ e˜6CgX‹‰æs‚¢»á¤Øq“ñï–*YÔÍ)ÄwÞ€!z=xŒ.ÜÌ«-×äpnÑYGÿÂ5ÿslôµ‰Ý5õí:ÁN‡ €¨ÔÓvgÈB¿¿ÆI½J&£ÎNò̓n¿BO77 wGŒLCž§a³„$£G–ì㿃¯$g¨áøˆÎßRÂù´÷zºd û6'¿\6çš¡kìÜœÝT9À?×"IbÞ¥>9¶ç.´¨Âÿö%ÎzñFWÏì¼S-¹é÷Ò6x_†\ÊÌ"®|Üù©·«=ýŽÌ`¬Å™}‡”äÈéÖG7µá•ã27HX‘(…eIð3æÜrŸ\9ç0òh^^ÕO=“Y¯?¢ÚBV™–ÑdtǨˆƒÐu¸€K¯ÈXvK3—“bKÏŸÚ Üê¾EŒùgÛÜœÐî¨&ÍÁ<³Ã]á´z¸p–G€ˆlí™Ê"F)gï@U}á3´Xëü’ª&dÏ<1¹%Ä–ôƒ.9Õq{0 ‰¶]ÉÀKÑ‚Aú°ï± V9ÀÏ ª¹•„ë³RÕÁ‚›Êîáª÷+G[ $zú)ÅEÀ˜‘¹ÜFÕ8¨£—ʘ(¤Wh.¢ê¾¶Rû;k]tpß ÛOmGÀ$/œ€® R+ùaee¸iX_!ç%Ee¼íµÝ“¶A§¨7ÅôãLé9B¥.*¦äÇ ,Éιå]jò·†=›z¢f¯1DVN€—ýy4: æù4ð·¨×Ni7äÖÁušOa9ÌdÐ0°þJ!>aŸMFzæ-èßœáÒFÀƒÒüëZyÚÇëÍðY¿¦‡Ôa›7~]7Pc 2‘Å¥(ÌÕA™Ëçûøo îjkªC4G |})Œà«&|art¿~`ƼŒªñ¹ïo¾ÒÎ)õ£ýˆévP7­é8»ãVj¤îˆ«Y5å Ÿ½Oncss,d¬ä£hîPàº+ˆñ‘¦ ß9!ŒÛL3;Üá™dÊ1¤Ú[¶”‘œc7äWÊø ÂÃÍûWÌ-ñ̳-¶Úü¤‰¿?[¨Ö8d [° ²N7; -ãþô³ ÉALÈ[·ULBM¼§r§¤N݉”ß÷×8lÓ#0‰—\f%«Ò®=DÔݳ•eƒ³‹ œOã}§pj½´¦'ú/2´ò vÜ §&©{'¼Òÿ=ÿÇ̪köÛº!²w(¡—ÜnáSÇ›s&tWU…áášé‡Ç"ë÷^aÆfçuº*:/=YFAóÔ˜÷¼#j•wΉÿDUꟇ¾z“R'RÀž™¾5dÊãHiÞ`6y‘-¸í“St‹ŠJ¸†Ë‹4B6üœJÖí ÝQ?úÉuÚ40‘Åucõ5'5ï‘*g'GÙµñú÷D#èËg=Û{d¯ãgJ7 Âùûy‘à¤W6ô(¢ÃÃv}š¤"pC¶ìsoºSoë2æo?BŠS»“a¦ÝxxÛb”ÄòÊïaçI¢KôÂÜ\œãGÕë{Kù P°]efìrt!$Žloí—„X•8eõ»5#^Yþ³HÐÿ ò¿¥ º&{ù;gš&7X³Rl›&Šùë’hj“@/)©¨‡Ÿ\D PîEÐ@axgô/‘ý£ãIXeûUÇi+Mˆ8{àð­D-¿Oì1|wäàtí¸ñoìÄk¥QÙœ-ƒ§Fm'„aÒq@àÙ? ½Ð»† µïsnhTêmEHmÛ•4TÅ"4< <ÏêFÔ¿Çg %oÚèªÚ;H¸¿:Ãáɯ%t(6‰$8Ï~¢½\Ìá<Ç;è¢çÙ—øÏÕ3Ák5+C]±A¯e·°0–5Æ_¤,¯@ <[ÎNXœÅÞX7ÌŒm߉Ÿöû'¾¤QÇÌâÞðº¼Ö¿˜œ°HËrÑÓ¨Cñfc.æCɦ³Áµ½mikn¦Ý§ØÕøÐ©e@j ‘ðô|â kháR×gÍóµ–]ìµâÝÇÃðÚÄõ– u($ñUˆðmù|ò5ÒíÁ¡!ÒîÆ|^wªÙ¬#%˜F8½ž‰ó;Ù¢~VCYT³}AÈpÔ.m¾ÖÇu®[EOr<‘ºâ ìPŸ]îq\cçPcŒ:eÞÉáQö«û‡[žh"<îüÁT‡£×âŠÙlg¾ï´dÖ~5ƒÛò<Ü@Ü.ui†L¥C Y ™ÃNËXù¾ó¦o³õåô½2 b$çžšr>¨³žoÑjº Ïæº¢¿†NϤH”T$ìh€Ì­ÂsÙð{©èVž6ÔÞ+n°M%O“ð#îƒV’#åôM¿Æ•á#M)¾à|l[,[ð.ÿìÁ‡Ž/ºcƒ“¨i‡aÑ È“*â,¤ ãÁ¸ t¦ÁösÒ ìÝ1ñ,‰Æ‹u¹ÐŽ< j¿x×~ö;$ØÀ¥|7yx€džËúÚÅ3uiDy.–Æ}°ˆR9žMd·˜É2+4n©égß©(ôvº¤iÊã8’mn»m³ŠÂÀ˜BÜOÉìz@S@3ƒU7ˆ¼~âàq`ÙI´W²C€6Çëã –¢]“C yñ àd)Ñq ‚g¡®Ý'õ?¤ƒÝ1!–Çî’D„«”¨YIœÌ/Xz‘27x;‹Ü—EâV_E]ȳ8³t‹ðÂÖ$O~…%Ö5¦”6Ÿœ?ù–ªtÌŒqN‚¤Ÿ¬ô ¦œË,|-Ã+NûU1"ôQúMSké˜=(z€ÐX÷]‰ï)æFYΩÉÛõ£P0‚Ñíå‚Q ^Ä‘¢;ª ¿UÛg£B)û%h /ÏêÆk˜‚S‘OŠ+n—æÔóÎtGá&Ðmu\÷›-iÎ^ ¦’Zþ-Î  ñeI¨(ˆ˜éŸ+å¹4àyX½%­åoñDS ·áÐw% '¢OªüPþ-錸 ‘‘‡ÅWÑ€Ùð¡ü^îAsêVÞ¡U“­Á¾ eS]pÙô£œÌ-—°`ØÒ¤6wñHÉùˆæÍôšÙ–çe\ÔµvÝÔˆï^;qúqò)ÝŸù¡Êµ3ëW à,ZÃFþ’BqƒA>ÿ×xÅo¡l”^DéfÁ½·¦Žcö{Ÿô¡Ýv»{š•Ÿý 6¢t2ôü™ÝîÇr– £˜0µË†pÆRÏͧV÷=¹kÉ á«PÝE˜ÿkêF&´£O+hEýiÒËòÓ¹˜"Gï~ öh>lüüƒBñ‘pJD ±/gZ›¯Ê—ö“À^Î)» X²í#HßNhª}± !*ÁJÖ¢C"ÊD2š‡‹;nþ·±j+2´›-q½ÈvÜ€gÖ $•M›T2Ší΢/Rþ#,sáGŒ²w|êØ7 ùÅžóRù®Çµ“²Y4ùȹ‚lñ;«W‹¶0Žù²É´”áœ\spcÜ Ê‹¿‡KS%âíà¡)XáE“†´R¶Pê„©+uí#®ÐûÊ-ª4ö¡)Çß<›¤Gxš§ô{9úßÿDP¶¬’à*DK_ôUÈݫْmIGÜ«Ï{‚# ÝØ—–‘{”Õ°šocîÌŸg¯Ûn©—Ë4eÇs¨ðõlø§ÂÜ<„.^†GÊj–ª‡-F†fg BF.(4M-©üãÏ4z›-½ˆ8­GØî›¾[ôì²” `pò&º¯IàmÇÔoô)pG’áÓJx”Ýv.|fTä'âÇOq–Œµr(@pW¯Óü ¶Ÿà*öçP)4˃4D¯_¸»°.Œ Ó&Ê@wtô*ó&QOù>…¢odvÀ#sa†G’»ƒZþÁÔ?¯5 ˜,5W3`Êì9\Ói¿)Eb†˜Ž¨ÚÂ(,ÑŸtLÁàq¡Bnd‹ÚD°`•¿µºÛ¹âoP¤­Œ+DÚ€nÚ$tCêªQç˜ÁÜçA„ÏbÓ.špQÊ‚B€þñº“%H+ñ»j6 €…{BaOùM¼Úhö^ÇÚ“ÅÌ·i]~Ë„Dá…  Œè RÙC‘m]sª—X¸Êˆ&Âß,¥³,&-g»sq€¯Ò‡œ>Šøĵ§›”ì’·8Ño_"§!Å”)BD~Äß[[ÀU6¶á¿+ÇéÐÒ=J÷]M'_Cb¤ô=’‘,JÔ<<_^}Ú ÑBM²&‡¤6wÿ˜j3cï¤åF1ƒv{àAðç#—APÔe"y*KVŒ´0t5íXÍ)2ˆ'Ú¡•c;5»~ô¿ø%?™ìï’^!ëü¢´3¤Ð“`Þ(Õ}¡(¬6LË`Tå7§¹e µS=U÷J/Hçþy…©]ƒ¾¾«o$š‰`qã·[~°dh±f GU¬dž+ÂÓ?–_JÌ …µw7ϾªÂ°cZÀ'Küåà¡¯Ž‹K)$GB{‘þþWT©ScpœÒ¶ª–ù!y"›²{õC9Ø<È»=‚Ò8ÿÉ€¦Ù‘wæ€BÍW} ì¶aLÉhµð°{º†¢ F‰ù #¾À¥]~ƒ“£ä8Ò¡›˜_¬¼¦ä$äÊVÙ\ó>…° /¸ÿã,ÆÜÄiïóZ}§ç¼*"¢9¶Æ”EûE€Á÷tˆ Ñêçâ¨Q²HïJ¯è„¥œ7Ž–&ù ;ÈdZsÙZèbq9gÆþ{Š-R):˜† íyä¢dÿia-~¸6ÿ:ØS# Ð3—ÔÍIãbÍ,En`U$ñ™ë±ÖP@Ž 0 |þ®×e÷á׫3ïŸ2¦*zbÃl·ºdÚÎkR¶Å+…Ò»ÒâTq?2.±è$Ή‚9 û³Wptg°.¦P‰·éq_ûð/ õ=ò„Ç$nÏ¢~o-ò -”ØŒ6Û߈£-;ï-fŸ:3{¾ .KŸuÞ2aމŽ«½…ªì’U 8:hxU p ˜€dýYý… F}(ͨã’Eõ`ã –VåBìÖ˜ yÅñ{ÿ&úU˜ãçGªjºª–å‡9ý=Z“xŠ™EÆ ”åM`üDæ'1:ÖÉ9´ ºt¦]ˆèö§‡›9aß˾¦WË(‹IÛäsÄ •Û:Íé!:ô£WUúð€øD‡P¾¸ÿZÚmÝAáUÄâÊfØ¥ýþ=èâŸ;ÙÞzí‹Ç¨a½4x2æ5’\âHú¡ûÞB§Oø² K^«pÑ€Ô—q}TeãT‰³\üÛ¾’Xvˆ$¥2)`íÈËé’Xæ1K=GxcÌC¾—t•Xã0•‘?Òèy Ä“Û_‘Ar|)]¾¼ÆÛÀCûæcŠ4$£¿M[Zß×(ën u±Ò*2Ùºz°dí´ÃînȲlÓÚMån“³¶¿ÅEM»‡Xð†ºq}ú5}C¤_z¦«ÐPÞ~cµ ^‹|¿ #Ø)Ö(í]Õ¿èCz¥äYeŦ°P •ßðCÆÙ¿Ÿ%m²Þ1b°dk Xâ4Zs^ú"®£²Avš·Ì·¾P& $WLÆ}ñËÞ:– ð®Õ>#z´ÀùÏhJWdÓ+ðcŸèÈñDö÷vhHùXôV±GÆ\³ùù¾„RסP칕 ðCÏcÁD¦ž3~$ø·Â«»FÔMöò¾â.\t8+y_+Z¢CéDèIaA ¥+ aÅñpM[Çw„#úÞ-ÒšdDI ’qLç_m¯N·§™]™L:ç³-¥|O%˜cÝ>9Tö.:03y(-(­ fªb‰’âˆË 1ò*98¡»cÉ•1®æ4ó)BÒíâR]ˆ¨—„þ³%–Þ®7 #°_“\Ñ ¶Ê'«?³MRÆÊUÉóÖrÌœt¦{°ÌïŸ;!æ$ŸŽ<Òhè1^BVzî—û¶úœj?%!vqÌ¢b:?†Ë©`&£rUsó>%ÏÁn;™d7»¯’…›¦Ôã–9I~ȲMA…-à$Ä8AWNÉ\±ÚQ¡ÑÀFÌìvíŠÞK%NËÞ–Œ!zP "uk÷çÎé3kb’h©ó&…Ã.ý0Ý`W¬d z€ly¡*ú¤d¿Àqùª4“çû1ô®”Ï×2‚8Læ$niaO­D|Tùj9EpQG¤æk!õ-fJ1?·ãÔ™/üÒ Ý´z[nááÉ@ðØw IZÏnºÔ‡ ˜¯`-¶-Y‚Ànš‡æ“vë÷7T_$Ÿ5ï•) ™ïÙ—Þ&£z»u‹>)2¾'Ê&ÛÓŸÊj0±vm­»®”Aûûlœu§ˆ­J5° 2¤Ÿo9¥il mZj#‹ùíj|gW&r“þ¦RuUì9Š•çÁ­º ãx‰•9SUW)С–…h@JÏ%r»Äô÷Ó³Þe «éÅý¨úì a®+ÞNŸß×ÌjcÐrÄ«ã®XÛ”À†>HЮ×È ó[¿@4±wð6öU÷š»z+”Ï Ó«gFpWOzŒ·Ž#o 'Í›mc‡Œ´í>Ú5æ:B~—‚ùM„àÐ15ÉeíªLÈ2Íÿ~ÎÒU¹Ñ'(Ô%ÈL8B’–¼"ï·å¾ÀœèOániH^Cíá`ÝX_éÜdaé~žªj÷vÖ½ä½ Såßú\3ñÁý)PÖf`\-à·i ópBêÃDP¥„—Ý̬=,ÒͺÅ|äZ¬´93‘þêb¥Ž—÷©¦ä»Æ†‹u[ÖÂvŽƒ(R' lÉXˆ—¨™ûªÐ`¤Þ*n¦H¹K|MÿbÜIg¯Ð‡=_¸'«ÙíÑ—¡^Å m™9‰ ®æ´.ç¦ÔVð@0zøØXR™º|Ûûà†©rÍIA‘b`Ír×Eç%‘þ°+ܸáxš¡ÃðÃ^Mȳ±šŒÌFk’5á‚64õëùN,xŒb÷ÞÄ·õòª¼ó9å̲ŠAhíõÞs4"Ö³Øp/hÀ–%DþõQGçéÏÜÛ+¯­ç"Ý_”A,÷Û ìb‡áÅì©#v‘6‡måß¾/†’=Ðag„¶6ød"«AÎ×.ƒ“0¡'½Q˜Ìwj“YËáêšÆX‚My5‹£éa(Q³LüˆzÑO0O¤³¢ÿ"èêÅØM¥¶XÔe"C4j¬?^Èk6¦¾^‚+·ôÃpǶèüÿý&íV V†vž7€DÑ’p ÞƒÃ߃ j2¤d:±¼¿[iûþfXœT_±ä.*T¼mð®»r(ðë]x¼!£ž‡àÖNv²øÛïømss ©Ë¯ÿêËÍ/j6$ñ#EÎ'ç,áöñÎn4íx5ÅâžÚ³šý¡”]ã!ˆ,Î%õæ=ÝHaLl‘K×}ý¹‡\]Ä±Í º×sQ)7 Ëˆ`õ¿ÃQ¿ÙeÞ FCs|üèþ¹¡þÝë°ÏL+5n<ýwF^_dåuþH¹' \Ãüê¬áˆœï1kÓÚd ´260U{²ÁGÙ,:,ƒã¶N1NÑíE5¢5ˆLœZu=›Ü‘¦è^ò0sŒ>SƒœY9  Ïú^žˆ§ÿÂ&Iwi|>RGL5î>R)j66,Žáwú2Êþè>ûïžÊ&¸‚~Üñ¯Á´ÀЄç{@·Ò¨è±£?êüX¾ÈGƒ *ÝMö{XóÐhÉk¸0 ö˜7r+ôæT|FE˜óƺ¢@HÃvA}âP$F%À 0 %8;2 •‡]-v$ŠðÜÊ3Ûr+ø˜Lý}ÀâB0šùÕŽ–˜óÁÚøÇùRtöaó<þ¶YŸ2`ˆ_VKxøÂ)^£Ê’F'nI£f $y>4Ü”„'iän…ØjSú‘Ô¾Må#6¦ð*øž˜ÿØïÚµ?ý)Øsvw{!ÒR°ñlÊ‹²¸ú7—Š8úûÐÌý’!Ò‡ûGË I7üI…lÓÔîØµ‘LƒX= ¥‚›¢0“¾ñÖ˘讙õ«oç¤ýÄÙoŒ1ë’˜û#ReªXÊ â‰ÿÊ Öa…’ðaì›8l\«Xàø•#ÛÁzYJ<‡ª3Í_QÖY±s”l†Rõ½š‘¤Ú>Åq±ðDYÓ×`‹MkÊSødÜïŸ*\…–zÿ \Šf|[šlVÊç¼eî ;÷^V2_éÍ@8Zˆ8°á¬Ðÿ×èêa+¡eÌß^†)Mgê+_þnI³&ݯíÃ0{Àç'þjÿYñâXt"ŠöÈ'—9§ˆYé„ûöˆ9ƒ/ðÔD¯v]í)…×lÅ,¢kLê¼Ð‰ÒˆÜY:Y|¨ØhC2?í¤xã)?ñ«d5ÓìËUvÚnUå+KªH !=¹y´Ö[çnö‹B–¸Ñ×PeUŒ¤aÊþé+'Xé&ßBè•yaÏ"ƒ;ÇFGdÎ.M´®“v Ü/ˆ!gv£Y–ö›(íõgÅ ã‹ä{ñ—i9ʽÜêÔ/r föö|?è0þö^ùu>x±dåfà•bó&Ãàj ­Ÿ¿(f…XnÎÃ6ÞýD|=Q²r÷üò¤”~£+Á©3A–®2Øš’JÛ6)~v«ÐbØš¿ŒòÛk÷ÆÖÁM÷u©óbËBZê\±l6ú䉀Töò¡u-o7Û Jve¶ù„.éñ4eâà`µ”ìÏ@|ý˜EpmB^¬’æœo+äXYõŸþSjG§Yª_K '.ÄÚxÛ Í 2+Ä‹ ð¸ø|QàÒúuG×Ó7òoÙŒKŽ?à\`®ˆú€Q‘_þ6=Öé‰(O¶> ªù’2 çzb¸»#˜F¸4öÑ ahêÀo /ŸˆiL嚃ò¼ÌSRÝ:ØìÍ¢çHy†ÞìŠ4U ÆJ|u3Aoˆèz¤±ôWB¿¢ÇVB)qŠ÷ÂÀªÍ£þ›Ö¤)¿þ&†ÆOí·–Ð× zÉ2E ÀŽ”Àáä‚ô:4çÄcAçp]ýbºåºñ0e÷6̨Þi¼‡åv|ðÁRFK?/פµàs«§5³Ý7øK›b›E¸lE›e´¬xµÛùîO¹Q)rd)FM?•hkm™ˆE}gí…ÙýÛIýd…úÁ†´šÀÇ~Ü©X«Î.æ2U´0ÿ5ê †úÅ<tÀ˜À¤&&bÏxöÑõG Ü¥Kš+ÏìÏ™#†£ZÓ¹»¯(í™Ì§„Ö5¹±ŠX+fŒèFŸSW܉Î@G<5ü%ëfK?¨ÉŸì[ÇØ/\}'IÛÝaõmîÿE1'u˜ËšD¯ÌàÔ\…³%,!<`iW®)‘8!?Õt9ñü¤_ï?K#Û0Øß÷¦ºp0d¦œêð%T5eÐÃ<ꢂðY;t”?2×#t-ÀÀþyˆQ>ìâšRcàN|Ò V„±¢±› 08b¶)Î =ßߘQÊÇ¥ÈqôH®ZU?r†êøz;_ˆO‰±p Ï×g±ôÜ:=Dè³I‹ÅÎõ·*Ž%­Þ¶ñ1ÄE™¶Ó'óÉ»¬-Uq€/kF>Ýh±ËÑö©,gE)kéÂÇǵ_Œq;í“eÔ½8ƒËóÆVê¬ô·RÉæëˆm”0⺕7l•V`JH¤]ôꀩAR`³ÚncƒtÎî W<НtÖŒJŠZæ ·7!KÂUì˜*’ëA¼;wmv™ëµo@¸‡C”fg§ñƒ‚Ð$½¼ÙPQî±~­*†Oõ_Dn™Ry’·Æ‘vjíµ?_ šRX¤/ë]N]½åÂOû3âù=cSožsñy¦TdÊ+Åh-Ö̵ŸÝ\vùŸ”…%=.e]‚+k”ºäÎU2±=#Ü8³ ÕþÍf'Û©d—]­›M¨šFD¼ý^rª\e3 ¤úø…!—Á bÿ”ÌðfûI–XÊŽ^ž”…z ›š(áªOO³š~¤*䊰´Ìêڢ⩜5Hv4–sªUoŸÇ>4Îâ¸T÷Î »äwO! ý0ºò/ò +5"3ަq $@.†v:2çI£ ïÇ€_õ,ü5r˜Uö¨'P7ñK°ª­Ò>J¿³g@·ÝèAŸ¯1Û„™e¤"›®e°]™!¨”Ÿ+¿ËÛ[<¯ZG©”ö"ò+ÄïFKƒ…®™ô» Wx=·W„Ú’æ9MZþÛ]6 IÒáêåÁT,DÒ`ž‘Ëp¹»ŸÎ!ªè”“—i«Œ†ájm ÜBé¡PB•ÈeÿNÞ¥î·æo\?}aH_6Ó­ŸhÍñ=§Âú̳ cJ‹~‰¶<_z7OˆŠ8¢®)Ø8­U¿F¨V,xìþkZMPè5µº %"6YH6 ›‘Î>ox ÑÑ@‚>EYÙtï$ÆX‹,c†T€ÿeFh5wET?àUÇè²vAöQ|²®Q… Þ2Z>¯nk#ûÐ9Yš¹°2/›¢yÔ²4H·k+ÉÑ £Fzºd€€Ù0ö™Ø-¯á,»¨;Ðiv¶~†j}áÓ·ý‚`úÕug½‹7À(ñ^r5"Ý]ô‡QZ[HT&C‡‹7ÀU䯴€8j· ºü}NˆC”Kiéí›4ݽ» è¬ÿz€"m{/Xíl"X†ŸŒÁÓ8$bN«0û ¸'1ùߦ“;7ÆW–@˜WÜ#ÿÙ#ü`’q"3R’à0ýîö©— :Ë 7âÎ#«ƒÉä>¢tKA} 7ìð’ºØ k¤=r¡,HS]íðœ²eÇ8çû‘/ÅvŽÏÏmî%¦¯snrÍ1­Ü'?yëÖêé§ ‹”ŠRöËÀEè,Ônú>Ãy› ½_ÚËgob5%û¡ið`þ§¼~ Ø~ÜC³]ÐYÎÌ¡{E—Á é¼€3{ÝEs¸'Bø¼µÓÙÐñ$ÒºV’Ë3xâÍáë[êÝèé]Fe@„‡¦5lyŽZ©×›Tñ{w€_›·T=ó8‹Äà„­’v#I½Ê½¯ö]÷&À§_uèušapÇM0Í¿ÊEJÄ&åM¬äé?Õ€|o íc-žëêXá´²ŒÜ p•[‹:RbïPAZ×Aö~©}CÕùÜw¤MÇôKt“p¤QÆ,tÚPg@¹z%;ã& ì²F1@VwÚ¼Š @?¬Äüël•j5[Õ¾™ jr1›ê†Òªù–à_ús^áÇ”5<jtET¤©uE#™È6UØxÕª8hÀá)ežKM<úñX­‹púVUV”Ém´©ibgiÖb±E¥{Ú}XÒ4R›–ؑߋ€åzñ(º´w±œE؈|¨àÂüIè–¾4=iù²'°Þ=r*¹¯lRˆVeP®tzRB5Z@*Û‘'ÞÚ3äÞ¿±t8Kú›5Çý=±»!¦¿è£‰)7DvÌ¿ö¸²Š^T­­ôeŸ‰@§Ä$uY͘ 4ˆH@ž«EÙ4˜¸Èv_€3ˆǺâT¥g8)¿¾†©Ñ™þ<å%ÌÙ‰£×0•pµÊÇ‹ey<¿Üjé΂Úŧ[£‘ÓÇ×€ÚùØ\{µ†¾ ÷a쨷ÅÕ—ÛE"XE^•‡é”î;}IQ"IÒÝHcb¼2Òÿ‡ ê?ÜOÿ¿èÿîÀÈÚÄÀÑÙÎÆÀÑ êÿ®âÊendstream endobj 13 0 obj<>endobj 14 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 250 333 408 500 500 833 778 180 333 333 500 564 250 333 250 278 500 500 500 500 500 500 500 500 500 500 278 278 564 564 564 444 921 722 667 667 722 611 556 722 722 333 389 722 611 889 722 722 556 722 667 556 611 722 722 944 722 722 611 333 278 333 469 500 333 444 500 444 500 444 333 500 500 278 278 500 278 778 500 500 500 500 333 389 278 500 500 722 500 500 444 480 200 480 541 600 444 444 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 333 500 500 500 500 200 500 333 760 276 500 564 564 760 333 400 564 300 300 333 500 453 250 333 300 310 500 750 750 750 444 722 722 722 722 722 722 889 667 611 611 611 611 333 333 333 333 722 722 722 722 722 722 722 564 722 722 722 722 722 722 556 500 444 444 444 444 444 444 667 444 444 444 444 444 278 278 278 278 500 500 500 500 500 500 500 564 500 500 500 500 500 500 500 500]endobj 15 0 obj<>stream x¬»ct¥]·&ÛvvlÛv*¶µcÛV…§‚Šm»R±ŠmÛè<ïQ§ûWçßZ×ô÷Øcm %U3 ¤ƒ½+ #3/@ÍÊè ê`køºsÀQPˆ9]­ìÅ]¼M @h `e°ðððÀQĽœ­,,]Ôê*š4ttôÿEùG`âõœ/M+ {å×Áhëàh´wý‚øVT®–@€¹•- ¦¨¤-£  –RPHíÎÆ¶%7[+S€¼•)ÐÞH0wp|þ¹Lìͬþ Í…ñË €1ÀÅhjõ¥ô4:þâ8í¬\\¾Î+€…³±½ëW\Vö¦¶nfÿ8ðE7ÿÊàˆ£³Ã—„Ýï LÉÁÅÕÅÔÙÊÑðeUI\òßütµ4výǶ‹Õà`þ%iæ`êöO.¾°Œÿùâº[Ù»\ž_D€ `fåâhkìõeû ÌÑÙê_n¸¹XÙ[ü—ôg …±³™-ÐÅå æ ûŸìüWœÿ¨þGôÆŽŽ¶^ÿÒvø—Ôú`åê´5g„caý²iêúeÛÂʎ韑±7w°0ÿÝÌÍñ?xî@ç%ˆúŸž¡ùrÂØÌÁÞÖ `4‡cRppýJ8€úÿ­ÊŒ€ÿ±"ÿ”ø¤Àÿ#åýÿWÜÿ^£ÿYêÿíøÿ4ÏÿZÒÍÖVÁØî«þµ[*vÆö€6Ìÿ!ilgeëõÈþw1Mà¿m›ÿ„Œ«ñW‹Ø[|ífƯFý§­\$­‘“uC¼ŠÒU úÚŽ¯L#l¦Š¦E¡„nV&\=ŒÖ^ BóŸXà§ífÝMÇ\ òaÈ$,œÈû9‰‰ùÒe/ß ó„08cy”'TÖ.ƒs~c®™L†a0fÖÂìÑÇþ=(ù¡åIàŽÐvN_å}tή8ºÀ{…232)‹Å묑†ô-ÇÞe×Å­)²¬¡«&´•5íòÓ8}~¯±•"ÏY1`Øv­Njð¤Wxâ¡aÍø}vÏ¡À¯ßÁl֯܈Öy2:ý{ƒÚ[ÑRÞ+©åeíö}ÌS?]HlÄÿÀ_!M€§?þ‰ÉÌtîú ÷ЂVf¤ E3Ä¢©/±ÿ§Ðr×Åš¥©þ›á,,qÃÆýű5ö»:A›–…ëXšŽfèäÄo¶O\ <ÃÞ}åq'1bN1M–„„"–tøçKv&K ÷F°'>ÈHÔUg„@úÝPüÊ™`9˜ÇbU |¹À’§YŒÕ|Ùq >'²¥:o ÛÅÎîÙu”*®¥2*ìNäîÉØí·Ó8žþ—*¨­t.ÕÍZƆ²½'B^³¿Æ{™X¥ª Û³.„à%©†4s~FÚÚº!ƒDgEaàÈ“5ug‚Öæ\ï³qi^ÈW XÌÜ4û5$Òj·ä Ã?šÆ•üöͦ^ ‰ Ù‹ºAúLbª‹¢yiŽÝ*qEcØ^ÿ¸Aºßšìš³a‘Ëq¨F2…•Ç‘AŽ}¶UÔÌ|oÞ&P±ï]¼³»5õ LáÒÂj Nb¨aØÏÅ êbWξËBW(6ØÎ`‚¹F13öÿmwµKŽÈ$Xÿ>¯˜Ù&)Þ·]¢±t[@3/ÅGŸ"etðË:¯,†·µÙ;»·8œýZf4ÑtnLDz¨š Æ+}ÞT‡9÷työh›Ø7,£mœ{ Ïœ: v¨Aû«ø#;vôzè‡|€bŸFœŒ”¼GW˜“;UT„ÅM.Ÿ*î´E>ÜùŒ•NÃdÄŸ\ÞÞx¸Æ¥1é… YiÈX¬z6ršs¼Œ|jQMiœ¥]f|,úÙJÍAÆÉ‡(Z^åA‘EH"©ÓÃÄøÇÌ*8ca½c´©÷Dn#¿×'4ë]ïç‚¥.»ˆ¦°uvy‡{êÿЬe÷=:Hxïs!¯=qètä“ÌhŠèŒµšßbDíÐ~û=¸}T‚™ØHbѰ»›OŠSâ S`-¥É!o·Ýö‘ðZz6m0K ï¯M`ì6š\¯Ù\ªm{ z/üŽ~Åœ‘w¦TÕ;dõýr©‚ÿ?Å© ÍAÖgi”0ÑŸ&gPe(C^ &M3`ÊÓ<>¦¡!¼_Íè ß–ZÉrúŸ‡„Ï?ØÆÔ Þ¾0âÍk(êúT!æ6MØQýÆÆXVYÛ„ â¶z8`>N]ŸÓ<‰f®lûa“,îf™xò^†œ)Ì=?%À%«N/‰f±•Qxã`˜TPï]„ K~• +€IŒ°’¡zQLåôó·;ÊJÈ@¯y$;a…9„²CÝ€RêÑ3Å~È#ƒ`‚b‚;ÔR`à1ç…Cm‡åà¶(:FmGÓó`ž˜ø¢Í}fö”E‘æËí÷  ¬hmNÃHÍž¦ÏBZVQõ…‡,™vR÷¹u 8ÈT6ÝÍ÷£ÀÓ)ÌQË¿ê}Q³©T.••ñ01Hj ƒT÷‡îS :?Š«6`îa•*óx«Ì/ílÖ”QF+d×=SéßøsµKbØG¬Q†µ¨x…’Ë(ð^ƒ”>*õ%k1hQý RC¸Ñnس.¨Ö^·y ÖÖ4`„+¶-™Q"ÚVEF<îpm §¢Ÿ‹'“ì³ÉCfmaÇ௞#³¼Áýåˆ\4Ý©b´i©OÆå²þUéé”ÑÇo_šR†µ8¤¿l“Åxb“jN‹öfñ&mmÎ#SzÆd‰ý¹Í7$m$à9VwEP\^E¯É–Šca¥ùÍ LßôèD{Ã?mï¿ÂG!ѽJTHf ‹ÑC5C©k ÖÍé^Té.kz¦{ÏØZÖ©—Ï;îºBèuŒm0¿Oº¬güb6ùÑ·Y1Tök÷ý8|ÆŽÛåh¹˜^{T•GQ³¹O M.’çtĶÆÄìJ7y XkéxX;TË®]ÂçüæHƒË%½ÃëuO3!1½ëšå@/!Ê\¹úgiô¸RO»ät/ø©"G½@íúÞ[¥öY±Kõ,]¤WÆûÙæRÜÊO¢e×ßÙð;û2©@zO¹!¾ ?¶ê}µ&5½M þ‹!G%… Ù Ýñí8bqÕB"ˆé¨l8½ø¡“Œ ¼„‹Ð“b”T­:=S2{¼!Ó²®ï·?ÍÕZëâº7ô‰ lU3lR‚×>-µì*î^ñx‘a?!ìeù=µûŠzÕ;ÁÎ{üvEN@}%).j+…Äùœ-)¾€G²Gaºß‰¨šŒÆ%4 icøÜ )Ý_ú*ZoÖJº29‘t½'¥Föc‘ÓK¦æùEyâ*Wôõ±Õƒþ¨éî$kj!L!ÊMâ+$§´™)j™ä ¸]«uƒ:XqVÕ ÄÙãEx,ç¼i9ood¡bîÓJ ñ >à«™§´²½]ê4ç뺩ób°»ëPð÷™ àˆØ$ׄŸ¿bZˆe<âÐ5Aõñéï~;G/ —©ñÅ∳ˆ®R¢¼Oçϳà WgÚÈ’³‹¾Ï¯.P»ë s¡§¾¸1/xÄâèÙ1)/ŒU±ŽÞ̨mh”?S¹Ÿt “ )OÈÀ£šbCiØhMÓM¯o6‡¸ÒV¬˜ä÷}¬4¨Uæÿ–‰`؇[ÒvÙ&$´·ã” ޵7K ²xjR¨4áM‚1º%¹¥ïÁ[ÿ.Âeh3¬6m™¾A Ù0[no¸{ƨ!€.šÃ<°+ŽÜ«sx¶88£z³j§½ç{¨«òH3GaC¡¸¢AžÔ'fe]÷‘ óqO’ìàMx5RòDå6éåÎa¬gyU4ø8Wô˹—Þb´äîoØàÞpòƒB±hÞlŽM¡3~·ƒ€D™-å‹ÛLXÖÞÌø³ÌÌÔØÔ©1Žy_½‹©ž¬’޹°üp…®.NU væx±œu·tåeüyAÛ&p2º&àúãueÖrkDþ©’5×õ!C¤á­Y’6u?‘ lfþ"é þQùêf¬g¤ÊòJ4W6'k)þËr<öñ“f…MV¯ŒÊ‹Â×W‘/eZäø(µ4æ\möl\ɸªn{/ ¼#Ñ¡Ž²8z¸ž{$, —PõØÖ±yãË%¦ nåŒ 9EzÛ `Òñ rºéGBiŠM.ÙJIØ•TEKËÏ7æß”‡¿«¹©¥©ÇfÁèV¦l1›«å§.e¢·–Š¼ì´•ê2™P6FRéV¤Æœ=¯ÅuÌbMIOî‘q×–q:MüýˆµŠÞ¿_ÃG ”_æÕ»—·Ð¶3΋¹3 ïT¡úB4JèÞm!š çê4MýÕ®F=K¬˜»·Xʟн|[—SÈåÕ„ßlà®Ôôª~à.ÿ1fÄ^€ÄÇ~@ vcÎWeçK» R30=4ÜŒáE˯0Ê5JþH“¾>Q)P"©ú¬¢;ÕqÆà*|¬­ãèü Ó'¬ IáGä‘c~+¯Î"íˆàÖ]H±¨Ò®m3ÎD€©ž¥}©çûOžÝ­ N&N¤¸,KK=”å7Baš@~*·¢6"NÒ t‘åEá:ʶ<ž‰œÕ\”êÊ-¢Ú–ó®Lz˜Z³®!Þ™RT›ÉD~‚¾¥Vä¤ÄB@ ¤Î €]ñþîš·¥=ù M?³–þãÐ××2¡ñ4'9¦Õ¨îümÏÅq—§'Úh-ÈfN©84ž&„¯vkñ.ûÀò.xÁE™Ÿ˜+q1 óÝ™ü¢‚Å‚-4SAÎ¥s—ÞiøbZÅ™Ð2¼HŸõö^Ú¦½„?ƒŽÛÖAåÛ¤7Ñq08mDVnŸVJO¼ì‡ß'Ù8C7ƒÙng¬&l13CîBN˜­%•g ˆþœS…¿Š ¬™)·Ž™~9éì`üI‡í1oY•ûèsp•ŠÎ¯Ùã‡Ô\]¾á5+Y™‰G´„Ó%ûƒE«¥¶áOÉÀ°‘b›¦´yÁº­9ïÌeŽÅ0iš,é}9h¥‡å}Û4„ÄßïµÉ€…ÊÌ`z90¢sTšI2y‡©I.Õýxª²k ΩdûåP@K¼\n€Ãæg›=dnÒ´8j“TDò»X>± P\Þô$!Ë>#½õû­nCcöØ}|úïÛßâï·G¬ ÷=b¡ñЬJ6"÷zzÃg£Aç„SÆ/†'küË}i'èýT“eZßß7Èc9b‚œ»x¢wûv§,: 4äžãçÁ —p¯¦§ØšÔ5^gxI`øš®=Çb¬MÄ:}»Ø›Ü…Gyl‚i×mg­Œb©h÷ÈÛ9¼<¤r=ó‘šaó3W~š³N›dìaCbîxÔ×W ¯\PHŽú–-¾ðì}¢3ËaÏÅu8ð7ÊÎŒ¶§ÅGw Ñýn¨´íG,[ÞâB3Ϋ ÇÔ0NsJ¿Ïšq½Ë>à÷<30ðHØÄºî#0Ñ·ûœMÿ¸ê¯Ìo1”CcÞüµÉ"¥ÆX‘«mÜúõÂvHéL¬ôFD*ÉÁÒSnöT“­ “2¬Šg vϸSWÇÚUg˜0[øQ{Am × þ¬3J0>ÑËKqá°£@UvóÉœÞ'‰t@M ó¡3Xý^‰¯‡=úc" R/Œ§›^üœþÒÚáhÝž&ÌŠY œ“)§›Ùìxß=ÞXy ¼ ÿWâTNà{éUë÷Ðf ŽÑ‹1m³²€Uˆ4TpÝå¡*ýTû[MÑ6ËŸ¯VEГ頡âîÞÅßQHÎ ƒE³'oW¯Y*-636«½p.Ž!ð¥xÿ^ƒÑ§h9ÍÎK Ò͘ûxòß„¤k/¼wÆ|¦òñöÉnŸŠØô¸HäùH>ýP’FI-Tê IY†xu ÷wSì^FrPßVÃ¥n0««’*öçwã¡UšöÀË@Ea´dì&« õìÚV_ê3žÔ >³Gzlw ˜×ÎÇ·TG£C2¢¼ß#È5Zï!Ô\ÀÞY+‘Öe„#V‰®… Ó‹átÒ]Ê]ܰ5?UƒŒ±\Aó‘>ü®–,_ÿ µç7¦B!±±ï‘%ÉYj 5{ ÌÊçDÇŒf€{VZ]CdPŠ)õPÔ¥|—Äœ._è 3è¼úžOõ©æ~[ KÉIm'Íðä«-¸'r´£­ ñEÜ}Úñ ‚y"ØØÁﺬÊiWÑŒ‹+à|[~‹÷ëN¬„Õ†@;åÂ’øÞn T„pT¯ê…:Jöù]k²™Ëóv µ|ºþgVc‚±c¹D2ýþ—Tt­Ùbõaþ¦ÐSòe÷^Šøí¨ó@£¨eĨnß3LFºs53d×ZŸ‰:ÊÓÍjû'{Ö÷÷×?3(‘Ó€øáø¿¡A:°óV†dù/Ò 3:Q‘mÞ*¦ÐhèTÇ&½8ªÿÑ;…Ã9-{åãh„ûŽç·lÄ9Y™™öMÐ ·C­¾¡`>ÿ6‘d’¥Ëí~¬°‘±ÏÂd´gST­‘¡}D‹Ø\5ª‚'qÿòÀ³DùdeÚp)ÊÛ&¶v¬hQ ]r8”%Vl´ ’ûû<"Ý£ ”<ÅÖ½]ÔI)óïo¢Æ 2hH¦»m¾Ñµ1k/~ßË!>¤x…þþô»6:»Ü·Fc\Ä>=¦¿â,—˜ïèp?0mÈÔ"À¶©”ÒŒŒÇB™]غãåo‘š³çÝ¥U @)ÕÖ˜ÑF7!¾lÍ_)™#+ÛêNÆpê6 £Ñ©V“ñ¤„WUžŠ®HCüœ‘¡³ˆûÖ2<4l;¹›,uû(>B¤äPÆ\¿­$öÈ& ·üêÙ{ÔÙyüñsó“\ä¹0¬s ÚHß]Ê)b3®Ô¾eĈ¶ùDÊh5Qþc!˦ôñ=û§|Üñ€R–‚(ˆ5¸3½· ».× ÜÍn›)¬H‹ŠÌx” ƒÃAÆ£Ä:ßÕGšµG‚NX…7²šuT…[^ÏþG±P9þ —<§DÙdØÖÊc‘ø;b¼¹3îš«ê»ÓRü· ë$&‡öêÌreÄsýÜÊwŒBê!mÔì t9«²Nyñ (èNù3€AèÏ¥Âa)œ³ÒäMç´»l®Â^«ðx ”_y£xYKÊñá œ‘isU¥MèM‰êEëG#¶8ÊKV9©¨+û@ݩ͛ec¸Û÷\HÐ èêÓɨ”2SìŽs­ z"ÅhºHË’‰ùÔ\Ð#¯þëU„ -ƒtCL€Pb_»\P­¤™Á*q¹×UºÚÚÈã -€ÿ$‰Ô7^KXVxÏÅ?xú´\SÞÓr·£™ ›©jÞ­!àåž™¦Aº•4Ú›v‰ùÕg¶» ¤Þ)\Õw¿êC¬Óô”SÓL”$(ZWì2+UÒó\[ÌZC¢Ž>¹-½\rüC³yV!ëd3‡çÙÙ%ÅžgJjÛj¦"ÿ2’ 1`^ü],ÙÀYuݹ"›¢TóH±º&Û·Û›‰Yã Û#Yj[ =´Ü—¯ýz™™8ü»Í»vŸ÷[…âu›ž——(‹WÔfI«qz»2ûR‘Ó0M"8jPúsa®¨†ä­ý[…Y6úìÛ'ÿ¾‹‘§Fוּuž?ck„èi‹a{ìI9jXky»Vz>ã°°L&ÚC¼nA‰eyñ‰4#Îg°èA݉Ž?pñtSŽØc‰–E'ùb5T¡$˜D‚Æ‚PDÎãE§ÀpFw¬U&/8`õØð¾•¬þkzvrÏø™½«¶ ´­'­ŸIÝ•—‰ƒBËØ("QüÒ‹Ð@ÍTåÔùÔ&Ö¨ôCâhnÿD*õeïXG9]µ@¢•K×…“œÜ﬈ø™ùÉ–òϱø%Ì'gøkèà̼gp/"\…i»ÏMþýNhÐ ­ç“¡·¬¯Jú ëI; ºÓ&ìÉ „Ѧ јöF‹³úUe¤ÁÌ; §fEëÞæ Ø(—ºÓã/¯Ð+F$Ð[™:¿Y$|–“i%w'ø™='µ7cZ-’߈-o5‰X7ÿqîk¨L›Í1 Ž^çÜâk`µÇÙ·o™¿FÜŽÃ~s§ÿÆmš:âpžU‹¶è ƒRüºÃ¤Ô[؇²Ì[>ÚÆßJS· }Jÿ2²iŸY” ¯þÄNÃ\¢œdùaZƒïV^z«ª¢ì;±ÊÙ©®3FTì( ØU»À(bt®âƒßq‰néb0Ž;MÑ-í;Òûν»$R¶ôüî|qq·N¬Ÿúª[³\díàGr h”;”ý€˜eMæÇ€zVŠéiÔy(¦ ©h œ‚dßû¨áEÈ|öNp~”Î?öê®6ãÿ´X>TÉ’ªÈ”ôýTW­{ÑéW'G„Š"1³”›ÍÀ°Â¬]ÅG–ÕqWØþS´psÎCô=—‰×á]3-bfºÔ¶ ÃËê2·±¢–kH©Bêm¬r‚:ñjªÎ4R€"8ñÉ8ãöÄs½Ó8–NuÛ]MJ\X®ìŽ&êâ<+T\JYSËf Šôh`9£yŒ÷¬³€5 W¬®FyBÚ¥¹ÆåZêãz±+•µû³+í5«Ø‡ßnwÏäÙ¬‘…mv/ÿSŠ #’¾N~«I»$xbºm‡Ø€WH\2ïqÔNê&0ÙÖõžÇé‘*;Sãu"“\aiLlž:„‡°@Œ*íÚÈ+@@—ª&‰z ë[ÜÛšž]ZŽ$xÚÈdưБ”etÉÛWÓ÷GÐÔ©@\k4yNņIí][p„눺I63 Ë>ûZÿœ´ìˆÏ7 O «‰ƒdhLÏ„*9×µ;þ¤ÿªØ=47»n9‰wÀ+9;)«aª¶Í´O6-ú;~R#©[$»ñÔw1f2/í />L FT#o¹Gw&uã˜äjÛŽ““gãÝÛ_Ú\ûðö¹^=JŸàÃwñ'©oAQ¯¹öBRU™I\eÓK"¡-ñ½”„Nsƒuÿç¶Ç‹{“ù×äòÓ1Ü ¶î!±Gž<‹£ç\%¾“Y‹Æ\|<¢Wµ±ðt»$ÐSˆ)õk»‘%P±íhoîN^¢Èk¤é¤€Ñæ”,Àç—SÀV+’æ·>ålÞäýO49gl¶xˆ7ÑÏdѹÖÉ1å>ÊÇùðÝ7Ú¡U„e©!'…$çf¨Ê!/ñ!/ôÙÛ¦ñ˜Ñiîéf¬‚ƒè W°q×`áô4æÞÐÛå,ScüSá§ÛPöm°÷m£¥ãétøã‘£1ó vžGAn<¤XŸ·Î¤IN†²L_׸ÒAöj[ƒpÄêä!ì ¦¦Ý$Nîǽ†ÏB’ $=DͶ[“'…fÙXŸ—!-_ð„HZ<Ì™àÔüe °Q¤DÿÇ%Õùã~©¡ Ö¥²W†YøqœžüÔk¦(_`ľäà[Ñ@Âå½Z|½ÿݪ‘…õ,ªÕ (”i¬ùëq~µàŒÕÒMû)ÌŸYmð£Ô;TÀ„•¸1E®×,6N••Ýz•ž§‚+2]í®$F5_Wú)T©Zwe«¼/ú;Y0-ä'b˜«³ÎŸ íîb1¿ür;…ì‹Ï¿uï¤lø?~ù@-*r5)hòév\¸\ÀUacD'þ)èlÒΚ£cuàhû9½£_hËv]RL\ïñ;G†ñº˜Ì]ò“‹ü~üÛú¦Jl~nïY3e¶L¿|ÒûñôÍXÒO¨øq¦“š£&knNI®V-Y±²ðWÍY‡ÜÈ«×hšù½SýhÇÙ€áþÎ$„ßMì‚‘à€L"Ø=Åhê·ÑåöI¬,^Î#7{I'éƒ ¶VÞP×8Ê„Ïðâ­®ÕlY°ô˜y4ý´GŒùî 4©³2ß#·¬Ñz²z¨8i¢Š±ÑáŸìèÆ?}8cxS˜ . gÏBßÄê·ëËÆÎUþòàv! Ö\Dš¼?ß‘^ ×GL–}•fîKEàÔÞhzBáVw©¥&uð‹ë³WÛ~çÀâuq~ü\F}ì*šª© 7и—a"…€eèEÔÍâ°R¨xZô s#šܼF >ŒË—²ÏÇÛð÷–ðI‘ºhê7O±yè}u E2^’rer¶C- —Ñ)¡ûcìê2*MXÜå’ÜM<4m'4±™ýäÇÆmcÇ”“ÛRsÈÃí¹[ŠÈ&/Ñy-^¶ªMÄt ÒÊ™Ygi´–ôk+çHF¡Ý§¬1cà'=õÏ÷è¿Üyû 4™ùM‡˜DÇÛd™ðIL£8Bx;ŠS 1#ioàfQ çß2{°äê’55…TÜv¯ÉDÒ®áùpr°nn·Sç [“Õ–§rÃ|RHq{דßÀȳU¿ è}‹d‚œöR.”V²K_ySö¼q½-ª¡4¿Ve‘ƒ.M-Ï¢l¸ïJÏZÉv­@EQD`h&·=éû|‡“‹ë‡‹Îæ8ª~‹và¿wKÇ9$mÈÒç°6Ç7®\¨aôµŒº¾ã‡Hñ*ˆÈ µº'‚ß lfÃ>]ê«å­ Ël­ìèï 6£¥ÄÒÞÅŽÄ! G+iXzç´Ã>ã{?a5ëóøÇ£“>_ÃBÚ²3Ã/]#1=#JD~×:›HMx7Úåÿd¥ F»2Ío£†ò‹[ÚO| ëeؼ"ì)ø€ ôá!9óÖ8ÀÜ›5d\ òÏ©nÏ$Ä…¯T¶)µ;ãÝ .MyÊÓZÇŽ=©Rãá†aê½è.KúëÓô܉O~Û±uo¶ÀBÀ%t·êL çJÈ‚ê$ÉU7>ÙAÜ00Hz†L“…ç‚ÞB¬9ƒ0T>À+1ã)Ç¡êfÛŽ®ÛMYÀr?¹þMò¹*ä@ïétÏX)ÑC R—cï@ϳ›ÆÖ¹¨×Àƒ ç͆‚²oœø+.øv±ˆ… j ˇ4…qŽž(Lǵˆ@@ŽTŸ*Èo†—Å5ÈZ€œÄ4%ޭÛ»™OÚ‹µGê :„L è¶& Ts¥ ,[ãMÏضõ@üÌxÜÑΫ§û*L¾}†P”¢Âñ‡®•+ÕV¬üÁÓ5¾¸q0†Á{ZÅ›Ñ2ëé öÔ¤º\Q™<¶uŠ,ê–­¤ \°”W -Ÿu^õfb˜JÎõ”‡¬)ž…y)\{ž–´¬¨½¹gѫ霛h „úÑ>{çÅmòߢiè w†}·N»ãeæ¼QUä«Gn½w¸t¸Lö Á6˜Ø*OÓt¸`WëúS´ºEæT“®Vî¾ ç’Á7¤D­ØŠÙIfÛ¼§Ì¡0‹eã»ù7ÿåà&Ýð#RùóSŸ Iy ýÈ. Åî°Â;åÇ¿?@ª(þAãMFˉbÔP.@å-–Öp \O).›)É, k€¼Þ3££MÃŵ?„†¢á²Öm±7Üve^6÷/Í£1dZ‚Kp£u¡ƒ^“W¹hEAFñóÀq.qËXÐ#ÏQ¡±Žº?Ó៽*{Z÷ýý;…|i" UÚ9íqý=äxðQë ®'[îTduÁYµ‘Tâœj¶ê…®NZFÒpOS£H†Æ_|‰Ôàòg<•t´)é‘òæ&:柴ëÓ§V °„«Á‡ò·Ã¼œŒ‰a ÌÒVUoçÄò%"]‘ð# &:œÉúZ]Þà2Ùxy ÛÆIì¡paëõ•’o+9*“–ó )FA§dè²Ø-%l(ì¾Â¸‘T¤¡äºes§F6AA¡XßU'ˆõˆSŸ°¤Và,%º 'æßÀ¿&9»TZCa*ûu-w=fKu®µP«&@c×}ªo´ôæ&#¡×¼ sdäÄpN ^p…Z9‘ënv)9=ÖÓ‹%Ê?í³ç[^K¯Øïu„¸nà×öèû½t¿·KÊ|Ëí÷ˆVàbF6먎l‡]Î'ÍekQ`õßzX/û Iõú·«Nxzóšm‹{müên‹áß‚ºÖž£ÉôŽ£b±ÅpÒëëØ7†[†Óû<‹“ûÆ.C?½˜G" ÄòG\çÝ?|ŽþÙ¡)ðC@UJ-O=z|'IóäWʵ„ýûúeFo³Å¶¬ÍQ³á]ã&;™4z±ZÕmj¶¥ãõ’‘žüÝßNÅoÄǨ–|üh˜Zbß5hz¬%I‘T¹úL<¼“Jeqûü”8úô·Fèº×XëQdUÓ |¿Ileÿ†=[5“KÏ06éÈ‹ÌèCŒçØŒǨž_ 4/þlíY¹‰ß›pÌèP-“=—„‚Û^"‘wÄÆ¾ :Ô¾}zÚš°¿¦¯%®zß÷¤Ê`¢sbÅ3©:Èè,ˆtÎà+YŽŽ…Œü¶{Òú™)šÔuz8ÊùLܴȸq½„„ÅßÐÒé -अ€k v&¨3a­~NýâƒÒnðK‡cA¬Ì%QߡѡVù7í´m¤£>„6~Àa2Èw™¦â忱2ÄpN<‹Ð‹%‰%(e¥…|.÷üé¶­0Ì™HRê^…#r‡|ÀÙ¨¨¨S4ç™ ­­@X,õ׸|å8½Ïl¢œlqöSpr˜Üf¿Ìù˜ûÎy N& F°ò}Æ@ÂæÚ·EŠËQÁï%/a¹=Åö&>¯~Ê?˜ß2¢•;b%0º(¯D¶.¾Å@õİèÍò ñÿèU˜Æà`¯xy27­¹] ²÷o„||†%ër¤]šý‰6„AhÕ'ÿ¤*örдyfÕ1¾Ópö4!U,’` Ó"K‚ùm þÿƒì½/ÑÎÿü­ÿÞïçfPP˜ƒ]2¡êsSì£CÙ¹|äIàâõÒ§á°Ne,åA¼ü½Â~›J‘+ ¦½ä†„ZïJMáp/›žÔ÷oå¯ÅlÌ'¬Ý«Ž¢4Èú8•Þ='©oËõ+@,W ®²‰üqª@ð‰| šâŒjñÝã«&EX‡G¦·-0ÅÀؼÄu#% ÔùV%'ù ßÏPcuWÑÌTÌ÷‰ø!H h†¨ô ü Õñ6Bû‰‚&q· Ùâr'Ed†~û&;¹ËÈñ­© ˜|‹—&œút÷ŠGNSbWJ|Tp4Cn–#úc¾²™ä,aÊD»€)<ÎEz«úŽ‚¥‘4p [¨‰f¾ü¦p››M)¯'gÿÒqaBL—¬H¢ÛEEkT°°­äsó•ä† ÐÞ4hõÊk&¬¢ßaAhJú—ß÷Ô9QÈá‹)-dÿÞÕ°«ªÔ?;¸7Ø¢á·}­´ëæ ÒÌÕAçÑDy@iæùg¼•=Ø»½ ›ún­6öU°OûÀHÈË\NEé^aCSoÚ«™2’U Ú&åî™5x–EÝ¡ÉêÀô:-ƒ+9ÊŸcrpœPŽ1¦ê™v!C¿Ë‡>­s௦@O¾{œ{¼‘ò zI`à›âQ®ôÓß8&š}{×$K-WÁ’Êÿ@ز÷j²”Žf´]ÿw8èû4·ážÎ%ë1t}û“{D©±ÜÍXÓC×=àlZ³âÊ1lëáÄ¿ƒ~a¨ÿ.úó<á—:…Ñœ|â,š“_¢ _³›ÔõQæì¢ø™R DeúÐmÊPì+=˜ÞÊ‘Û&£¬ÀJ‚¶ø¤`Z$âr‰±\ÏìfòÃ"qOKè —èH[a8Å÷ „ÃØí¸êm<,è£íg¬ËòjXÄMjtèà’ÞÒ+fÑÝ/_ÝßÄáæû™«O9Ah~á+sÕ¶°Þ{/‚´Z?nœ×J¯Pºï*¤YñºxÎæZð=Ïcå¡}ÊÙ2ÿºbb½õr÷Ÿ´p0Á³r9ÑkþG7ƒÚX>Åסñ¢aúmkaB:…sú„é–p³G/©«Lªš-ɺV1C¨GzW[R욂ԣEF¨t!-wj·Ý–¬F†ƒ’ôwg©ÚC`¶Gêþ){ÎÛrAFVÍŠmD-¨#Þý‘nþg|{KfÅ¡‰ÕðÐO]¡´9DACœþ>L¨½]¶ðM¸&Õ]SD9"?gå”júÍó>”F¦¨hIß Cï~£á0”eç~–Š~ÜGã³,6bIŒ¡ß€ gjn້pÆ¡`‹ËQ‹…H±† Ñ äJÕFó0c䙜rG€3ŒÉt° Yoò¬#qMaEÏyð)&O’KìJo#¨DOf‡ÙøgrQ5ÎÈKˆf™£P %-¨RT†xÊ,CÞ’UÝy˜‰ìétêÂÐ:Wpæ¢ÂlõÃ) ¯Žq®µ¾Pìz©«}Å[§@ט7™í=Ç’mW²ŸeÀizOºÄúI‰ÂÂén\‹eÉ ôL·)eå0²&‡”äç$W°Ð šç†‹mïÆ p¦.2’°˜Àùãx¡Ï‡AÝ–(,eBYëI\}!HŒ¯?_éfÝÞDû›™çW£õ^QÅI1Ä6 ‚çד2¶`Tþ¦Å"¦Žð¦õL/Nqnv'Á¦yÔ‰S\ìB Ê.UF|úlÞe51¾cª9V—"šÙ”¤‹ÖT¬Û>ÄÓüJl¡½æÍ™$á*q]«Gª€ÙD Ó»—²4sŠMÕî%ì]‚`¥ƒ~òF®›BskDoGüf#Iæâp/ ×~ênõë0Êß~sŠñàvXÖB¡(ÖCLZiäûõƒ„Ra–„zϸnòe•â9M½—OêÂdÙ\”S‹a»Çç/«P¦…J•PõѪ×(éM¸ßHö•D8™˜r'о$%ý¾lä)éB3èÖùµOx±«?`8Qx†òÜ77øv½¿ôNW€Ÿ—Ë ÿWÎÔ—P^úI…ÌØX;ª{5h”ž ÀÖMœGàV·6'š¿òx&þDб|ˆÁÚ$`À–Y3•p¥Z^\<;æ/Fº­ûærúäTó¡¼ 8žIˆ™)Ârû —¡©Û©Ôf(*‘vc'¥véêä_× ¤å‘ÊGr1Á„ÞSTñ=÷¿ŽDwæ;sISÍê#p:åbÚ¨Fòþ9GìBèúªX]a?0Wý¤vƒApˆÙæ­}¦`ñȱÑ:¿`ÔúÛĺt&üí/jÀ¸KXñIè®Ì<Í E˜Çt4±R/vA}ðîÔÊ«I»EÆ·oö~£½Š c †DA²<ïõ.Í[ñʇsa×' è–¥“޲Û}¡þæ}¹Á&á+!9DûÆzóøŒ%ÆÜ W) <Ú0‰l9é"ÞŸî­EÑ%è…Ïá¾¥À‰°ˆÙµ7ìé²–¡ .þaSrCvsÏ»ð±V—Š2•$01ñ»þfË»Lcµg¹»*ß…ÙIàRÚA+á§æØæŸþo!©‹Ä6¬³ûã·´íú÷Þ˜è#„ú{§ýñï M=ݱΙ 5W÷“!wzÒgj9Ç¢Î`EÊÀß5Ø4WŸ&Y˜zqŒépíL³+ìºéE „$6ÍCçÖüŽ¿þ“…û+¦y÷®MÛlJÆ&Å\$£æ7UWϹá€?ôt´Ý“óeéòKXÖ©dßÁ»+z ìîÌyqƇix_¯÷äé(éÏöl|ÕüòÕf_¡#ØlôXÔF‰]«rå’‹²í‘MË DÀ) â¢{ Gð¶ÿ;@Ä¿w1[vŒ´ˆ hødrNº„“!âTeç°«³‡¯–y]ÇP8Äà;œ=cœ`èÇýÅ¢?–¾™&ÿãN+Íé÷”äØÐ}ºìóy®å‘yÝLÍO9ÅÖàu“p3š®¸w³üò·3"ÏóàS¾†åY³}"å‡f»5Wˆ'µfúÍÈž±å‡ŒóyZûË(E³ŽiÖLá8V'[ñœ} ºÉÜÑQv+óœ~KývR’lMÛzŽ3¬]Èžo!ÓïLçÞ暴«9BÐW”ê®8¼Ž~¨\ Œå¡ªžTDׂx°›ÿŸït¦=ºßd‰ÁúȽ|4¶˜HOîíi9Š»"KàB°,ŸŸ?üꬼ–Vªûúó©$K¨¦‡l%+¥ý™‘9¢ÎNùÍ’2„7¸òbåª\‹ì‚VðPÛ:ƒ=•—ZÎ*”eÕ±R>CJg­[=ÂÍ å\U7°Ã[,7aëÆ:9¬,´ŒÑVl/Üò.·ljolHë•Ò08íbЦ/˜åÞ:™ÃÛ*€C@QFÈà S€O“‘ÿúŠ}öÉßF˜ø÷°[Á¢8È?.2Ð<àœ÷âˆdÁérf¨ËMÿ û-–Èù–ú¶ðÿE} T}.¡n© "öÛ\žŸ ß2 ¯¡•á4Îb>k\àL#‰Ñª2£Œy×àÊ?4‡ÄäôÏ_ QJUNB–‹™‰0×d{äÒìx°Û´Ô’ºü6å×TOúþBêE¦‘4ùlÔ#†Â4©æúTîv5Ðñ~’²ŽpÖÚQp–uX()7Kï….–dˆRýp{@íïÚ0Ú•µ¢NIt + „€àê{`žoÃ38å9æž•¶²¼“EÀ”QX$U¬üÁÛ5ÆáNñâúõ:”8ొôK”]5õMÁÑœ©è;™IÐUYÐjâÉ™ÁµÈ½ ×EÄÉðA/›ú‡Â9žaö©g!séHhÍ#ýÝÿ²,­)€9´Ç'nË"€¯è>Âû\uè‡ò‡§ÅÁ0«¦6(ΟµµQϾO2{a†º¹¬xµ ú\ºüÉýý÷üe¡1`¸K!ð§sð ƒè´Ì©`Ñ)•ƒŒû:á;†çvÑqÅ ?A„pUR¶öÀ‹±â®KÁsí ö‚l•ñFŠ8,Šik?Ô4ýá2›ÅRi9â?*b !ft~7¤ËL¿ç¿1WsœoWÎcI0??j[(¨Ãaa:…rÖú¸²²Š:/†Õ½­Œ-g5ŸöàX¾T¥†wªU–Žºè®’½ÁèÚ{-~ÿµ¢ÒYÆ' *äaAék3Ö1Å`ÑïŒoËÝó2a_*—Óö`î#0ÇžKúMw uá ³sAä}4á–_’¥¯7;°åù ¼÷°.Ö,¾W\iš"¯£ ]#q~Ì^E±u_·xÅ@ͬ3 «ä|-t©¦ iĬµ…äSxCÞ}»ѤmEð¸êb¨Jŵ±qœ|(ÓiE›eÍ"GŽ c÷4`B{-϶¬IáÄ;ß©í+…º€Ö,‡ÿ,uJw˜é|ó+Û®¯ütóS]«I «ëBÊvǿˎ{Ë#X/#Ô¤_œ¿èTGJß„W‹Ë¥šl1müqʳ÷ÍOÓÓ:5t> œ&¹zíˆ×·U™,4ÿ–ëŠðù9gµ§A²R÷Ë¢f§ôþJõ;à"°Šièd8«nZÃÊ’Àe Æê\Ƨ”÷ýQX ]@&^€.—ó¡ƒ"é œ¡ F,£á÷J¦ì.–Ð$©!9H#7密gÀÝ¥u^n_&;@ðüŠ’ä{³Œä{¯#¿^õ-œv Pg9²RËiMkDRn$`’í»ïdý°K<`±Ðž_w­] rú6ëµ1cì8L”³Çln¤öù6°Ĩl—Xß/¦üb·Gb$ãÕZâÔÊoífq‚³œl":…›b(‘]?Q’>îWötŽãwÙNT@ƒMQDŸÒ¼­ë›q¶ý` z•zŒ_—¶éŠœÚþ?©òc¬³€ /ßmá¨tÍ2OÑÈoý‘ÞC“©àV}Ò’Åó¯€HˆÕ% HMº Ú°‚¼‚Ô*uŸ·óÅvŽ¥ø4;Ì$³´-„•±“˜83o[_õq]›\‰|ëXâÐàFôQ·Rfß-–¸ôL­ö³*Ìi/~}Ñ#ÕŠäMõ¶ª\ÏèïƒKäÕì^öM'æìó¿0•[ãŸ?í¶Áx¹ÏÇõ—9ònsȯá¡ 4,©¥øA˜i@Rµµ•Ä<ˆÒì2´fÒ›/±¶ò€»h¹NØ8Vb x• ¿šü¦Ôa÷ÛzÂ"¿OuCP^E ðX<ÎPü¯E¡Ë™Þ›4 dñ*bý§T‚2¼vYñ¸ÖsÉëXÕ0(^hÇIaþvu]§¡1ß0_Åûõ\ $™‘±3~÷æü[f™€÷³á‚*ù£l'ËCÙTN¨tj S)l"o5©Hc­[$Ñ=€ªÚÃK{œ˜Öɾ½.zªc¦ÿ>“HÖê¦xþÏ :55 c}E»Èç!Á^ù^·O»Qe!¬×¼bÒ¾¾±÷±ÿ‡J§ÌÍšdq´e™F2ñ©Q#$»Ñã¹³¶x®§-Ñ øl©1SÞû¼tÕV­år"hý5[J˜™“<ÚØ¦gzåÜ£›Iališ0Vñ›¬¤ciάNÙJwfNS*ݬHƒõµÃþ‰ ©Á”¯rÙV²¢f-`•·ÕÞ)ö-\ µ0¬û>[ï{”$D8î:Î4 ~R!dL$û‡ éw»¬Á±ä,OrsO‚ŠÈzÀß9â2ù’\¡¢íC/ü ·Çn:>y€›í0/5WÐPbÄÙ{ÃÑñÉ:n-ÃðåêÛ¼³1åzṪQâÞ Vp  Ýž¸6è ^­6 ÐùÛ”sp±”µ] ¿¸í®ò!YÚDzw}å\ Èw¶õ}„«ù,áW=MÂü§®Lxµ —åõ–ÜÿñéÉ‘¹ÈÚ§+6hÊ 4Â9ÄþéÀ€þ/±ŠQ€D€I’¡ƒª‰ÄJýr²9êµ±Lj2é£Ff ¨Ž¨vN•íG‚´¯ˆAž)x=‰ÕnN•ÓÜØAì&Ÿé†ÈÔ‚4õŠ{¨ßé9Ó`³“܈’j±Æ©çIf]ÜÂ&Upsò„hÎUòXwáDwqâËrk2[ûr‚'=«Od‚øÕ]ùÃ\ßiï›åe*`Í¿àÈõœÉ>Û Ð§¤"°dí¶¿¦¸êÄýhn-Z[F}ú¹KCs<­hFևт³N ¤y4ëen‡à6·fi@$«ñluŒîI§Tƒ…è0ev$òÅ5DÇGhʼwÌð£gž÷K|³‚H„¡U°©îÔI$ñCQuÉ(ÎéG­/ãØ·&;ùÚ' `G˜É'Òĉ ®û<߯d÷‡(Ö%ðšÊÅm5³)aºMÏ:õÚæFñçD~ y6¿2þœ'¯›-nÀ¸ô·€lÊóØp¼7δ¹|È&ÕΜ¨ D oÜèžÒä[‹“<¸$ÿòTÖró ŠõϘ£$Cü»6„æÀá?wf0C¹PÆ¿`/O ‰{Ö¹ 6al“¿v:‹[Ð_:)á “>‡6LðNÓ,1JÆsk²‰±gU[ºS˜ËxzžK Eý~Xl¿-ú3çÏ$ôÝUwúD³QÏ›_nSŽSÇ=PÔƒ{'K›¬ÔÉ1‘Ç}ºÆªÏÄ‘¾ÿÀe™Þ\pÇlïãÓ.[|@5pºAÒm¿šФ…¨-ß|Ö 6s±ÁìÆ4’–+h_è²} ™]%f©+°¬'+€ææóÞ¼¬2aMÿÀñ0þúÎÕ¯µ„D—l5×"äýp·;q/Äur~±[k©$€úr¡ÑÖ£±Ö¶ZJXï3«*Îç–Ýfê5›Û† ±Ó¢]±çØŠRgïpˆ÷j!£pD&ô°8~2€&ÉËñxÒß±¼l,8GÇ$”óÏ_Þ‰ ÆŠé£tä¦Gk^»þWJ_ u,úÕÒ‰kªÝoÇ<|^=ä•N_~›Ö6øÿqÓ;rÈ>¢Å«{‰ýزÔ9•åïŒq½Yˆ뮼iäÇPMP›Á‡qúvµ9NZ¿“ÚöTo<ÃÛG*.M‚Y­Ž¯%Û;mg–S§ÑdÀ4w_`ÚÏ`àÎð²¶á*ªÅ±ÎBèÛ Ú3¤cÐ:³6SLâL/‰Œø) 2[¼½uC¿ô~•šXhÖm¾™‹÷ê„LðÜ$ ˆ“HCÿ¯a,¸¢ÊΤª2O¢}²üÄ7Ýî9§9JàHû-Ï›_‰djˆ™cר‹J¢¼è†[‹7¬0T“*'ÍT?ã/ýs’óÙ<õµž ZêqŒ‡}„F4 …²š2_žhØè@?¯€8À}úŶÀ™äØk5€å"Ïspˆ{<;e.Úß7k<¸VØ›EWÇ,n¯Œ >;èC É~òÑŠZè>H)a(iÜ~‹S9ÇG+iŸèÿÒÜ#Ëâà¯'„|B”‰±×j[ ‡¾ï^M¡ÚÍðÄ3*ºkÕ°óÕ"ä Šcm3P&‡YƾúýåŒi[²>ÏNø–Òº¾|¼¸åø“ìsY¬@Æ(\ú21tE¸±r÷ÛYð½ðج+“M¸ù$ŠÜÀ ˽oËh²””˭_,@8ãµüèü®åŸêdLM½–‰Kq÷„©‰Æè\î¥a]QøÀs¾N¸±ÃîÛK`¯f¡)ë)ÙE¢KSå:E {óiüd$†¤þA·×IâÛ2Mùv 3YרZ³=ǘá'k&­•„.µV?hV".¯L VAA&<þ7K¼´¯ø$5ƒ ³°2†% ¡VBKéËŠpˆyPŸ¨C†XÓ´( "¢å8üŒ,ÐL—Áem¤.ž†ñÀ0NïÊ?IÊP8¶8”Ó¹û1a¿zÖ¦H#•KJm¦a~ªwvÝðÁPç Ù{ZZ»‡úóWx¼•¨ì¬š›SS,¼Þ³D6M¥a„¾–wCæ¤)Á <ò&ârþšðæ}–A…¸Ty\˜q÷ºe1Úk:ð(üÚÀ¢VÞsüÇEjmå}9PúmÎäÌɧÛÚeu"¬‘$r⋲™¡¤(…)F‹vI´Ø`JN7¯ß³ò«YãmãÙíä0X˜K°±€ìÍ€¥ÝKÁè>‡šf[ .þ,c±p‹xö«ïO¿¢ØÁ-3KåeÁóB<,êà‚ºQzeØ]}ÑjGÃeÇSY)8åS5QyŸûgNã}~ ã8Þö‘Þ XîŠoY¾8WpϹª{ŸNlí±•U½øÕ>XÚràñgœ Ž™œX¢ °é8¿ÖX•Z>相0¢öš­ä±"o)» ‡Í?´‹ås&á­ÚgNÌ{Ó&ÐQ‚É>Ï“DÆäHiQfçdô6>ú¹R½—ºy/m`Ý; yf!~É Š}ÒË`]i88Ï’?8Iᕞ¿ܧ L:°·×5ï#¹åÈ”ÎUëÇ©òáò䲿¦O°¯ÄÀƪEÁûéï­þpžZ[XSª{›+QqòuÛÏVœ@&ò¾Ýdê]]dÊNù[lñŠ c•‘ȃDÑÈ/°¯ÃÙ3ÚÓƒþâËéV``âÖ©—e±-³‚\° iÛ=s–„Ü©›:Çi(ã)è+@üßék„ýxßÝxªÿ#ø¤˜N\l…ˆòàË8c¢L–ÙM#ÊÌÖs±X¤â «á𬲟Hé個$|µ‘…T'ñÑW®QËân,àí…6[7k /“ú²ÝI£§ÕVWw=H®¾Õó–t’ž'Ü&ÁGaN°JKâ…‚%Û&¾åïmÝé…rÙkeP–¹,©>'} ‘ê5‹€Ëáô.s›d…íaÄ;>^d"‰OµM‘ùü4Çõ·ö%·ü3ŸŠÞÖîÅnUÞ¯6¶uYŽÆÅ7Å\T݈!'=I¢ð2&"’© ) 5›ì8Ñ3•’©8ÙG3Yî6$Ÿy¿^ëI´ lh© nNëÜo‹ £ÕìÌSz…»?7§CÃ4ùV÷5HãySýˆùxäÉ<0}ÜLg®Ix¤Àg¡Áìk TÎ*vNŲ“ŒÈ7« Ìâ«£/˯jw?ö4€ñ$/ó!ˆ¶]:=š¦yÙÎ{L2š}õPªµMGâ©/FL䯷pä 0¶€…W>C:L¸Æó¬þGu›A<ÖŒ=ÉÈ w.ŠÏК*O,°Lt²] hà)u¾"Ôí—3`MjYÒ0øH‹ý ÕŒ<6fa Œ€¤ŠOÿÙLôM(([óƒ=gПÄP¡Ç^- ÙÀÁ{«>±”^7½WÇšÒ¥Â+˜Eò“\HØ¿ˆ±¸X÷x)Ü/RR 1;*ê!¿%)ˆ†”bnB±7Q0é䥟)Ie&G®ÐI‚~ß÷ͺ¬ä¢>ÄÚÓLÎ= _8@°é­îŠÖoa© ï³ ywzd‰b“¥h™.åŸIðÁÁ_ß›?}È"†…83Æ­Û ¹Ý/þøÆ³sátËúB¾sBœKõ½ú}ÌÛ°¶`}db¸û†„ƒì®î£A;6ÃÆ…SÉè›ÿe_9J=j%mÞ‹˜ý¯À˜Å ¿’ Ï•Ì>4¾›Ô|”ü '±,6A :Þàúav;Î!É}¯ý2  ša‡ÚIg7ðîÑW±WO_|çõäÙ+Š¿ëÐΙñ^þ\\_7æÖÈEµ›(2pn¹¹ÏÅþÖŽËã!•½òƒÈûð{ƒ3rªì#ù‚ÀÆÁd PNg™¹ºA¿`x…²i $ýæeÙç߸ôÂS'øp£æŸÉ»Ò½:}È<Èd“:ç¯,ȳVgñë֌9qT}k×uÇk†‚"ãR¯yâOsZ^]ö÷U/‚¦ÃÏ*áBÏç–oΟó›fI |È «¹@ûLrFæ­[‘æ‰ί“VÝ™ö3QR8ÁÆËعVUüDr•da?ŠkÀÒ^°ëœy¾$ËdÆòñ“0Ò²@ê®î2á÷ªk¾Æï´ÙÔ NŽÄ™ô6™œÅ¸ —lN 8ׄgÏ—­û—Æ&ìøåç%„Í‘×Sœ«KFOظöä¨ä÷\,O#:·Ý.Êögâôá •Î8c¤>з!|Ð5뼎ēæcØ€«êxãô%4§3X®åÂ\Z%cÂt%« o©ü˜Äl’«ûW‡ªœ°6»‰ÝâHˆæ}t6±'ÆÐ;uSWýŵ 7™†'r²AÖ²™º,Þd µ[Û™¿êØ7ç×»è²álÌ^ý‡‚mBj_‹»7æÞßas¾öÓ|Dæ©e¹ e¨S1.r±±Ø¤ vš5í#å¿qž &Xh1ó's=r»yÊŒ—7Ù Úæ­Ç"ðÒþËv‹¸SåYçê&㎃{÷¶=ÂêQ±jæ‘éÛEpc±l'´X¾]î¯}ÝÑšàÂ(×÷„ ö––¦hNžw ]Žm§-¨fˆ)n[T¦½Àk ÒY±×SoþV­íDì˜'q]Âí–é9©·NäV{_NØ´ä6±h…““¯š¥æ €ïjz­XœWƒáïÞU ?|‚ÐŒ V"ÍîÆáJ’lâuÉU\œ{`Šæ3;ÓÄlRí/ÓŠW« ت¯§ß^ \í峡ó#Ú¬C™»­©£Bî ÕKð³·ªe×ïŠÌú‹dsˆ1w.¢xaï=·]ã¼î†Cô2S¯¬# ìi­ŒæÐ0Š-LüŒ‹IÄôCʼ…t×ûY±Œ+³;Ÿœ2Y¸üï1^ÀWÌtÅô±{ ‹>{P‘ ‡K©ßIwóJŽÓ€ºÖ?p…ú?]&8Z"0´g6’£‰Gn»*¸fÍûÍúbÏ“‰-ü­-%¾å>Þÿe]Å뻄°`rÚì(pŒÍÏ\ŒÊQk…&÷6ÑjdÔÜwŽø¢°ÛDÓŽK d–×—S¨àbÚ7¨Ó¼ŽZ ARÕd¾Û'1—ùñ›ÛÇ>bM•O‹ðƒù¼ŒÕDPÛѩ؆ŸqÌÿ@îRÿÉf¶—6ø"Ñõk_Ä*\§bô¸^)ô˜o½æƒ ˆÙ곓 |FF¬‹@\:1#É.>RAJ›á ”Šå\Yk·¯G¿Î.u^š×-‘ýâRÑ@†*0 wÞŒAd¼cVT«%ÏWó5¤æ"ÑeI)ËN7# ¬9”;ï\œ~4z1Ô.̬ ­µyÓ'ÌÚ¢™ •&˱l•>WÆu½Ð„q%²#ÔÖ"Ö~Ñ-Q°4*—F:ÅMeÃÕö-f­úÂn<Ú,©¹ …un»–µ†’Ü÷«¡ž¢«±<âù´Þ´!¾åQ-ps$Ë 8ZA³7b˜X‹ö¬È;¢¾{@½Sa±)hFa^©” ¶´ÅˆxœÊ³TãäätB÷¥.0<)0âÆw H¼\gAq9Á! H‹)ŽÁ*âTßË+Á¿¬îCh¶i,ÖÖe]—¯ ´Xÿ½§Y"Rÿ  …Ï}½Aðïr5v”nöBTQïè¶Ñ÷GLÈ<ëì€MÀþõ»<û©ó Sëþµ•¹›HŽK~èì+­#&òƒÀté_ü`ý{LBø‹,°Ô{xpK[‡D —Œÿxö?—tczVî…áþëbímjœKÔ«4”hÑÈ:SuÙ-d]%j~±TdDÀQ¤qò¸Ç:éÒYl™Eq°•æ$Ë\ÆÆ*—’ó+oÌ]œ7Aü‘†<Ο5wÑw…Që™ÛËàï}oðU…l׎Ç Äýv¶Uê¬Ru€z"_£_;I:ÿ|@üàTCÃï)LÏ¿·CÅ·,Cu¢€œÅ탥xH¼¸÷hþÃpe)Qò£+ù{@Ö~}ê¬åðÅ:ëMÝ՛˿歸?®â6™b’‡°%˜™rIeftÒ /Žf-žêÕ7q÷€P¢À1°ÈÃH/]ÿ™¼…{´×*˜Ç-j…ß;g%ã+Ɖd× Âþ§E"gPÈQ‹ÿž°9€³†—ϱ¦€-ì…u÷ìðu®õéÂ>RfÕÔÙ³U¨±„&˜Yž¦MöVÅ$á\~]Ј¢^÷…Ô4MX¹xØV[\), üž®â~§Í£Ÿš:f¯?û‚0;Ù=-¼°nFU;ÑÐiç!Çž|´3!BíØ[N“ £çG¯/ÊPt±îìý^FÄ–óÀ Ý8íéŠ/M+qq&8p–{UûçoÊãolkÊDÃ÷§&D‡À-Òw·™Hœ¾'¤z°5cBäHt&™r5=&]¿o[RpZ±™ÍtáÀã6PâO¥ÝÀ2¨qûÈÐÓg+¶íM.V”·Hnðò-‹Ø¼ª„ƒçúr+B°Ã íŽ °‘£Ü;dtJǾ4ê8tćú „f¬%Ó†,|ÛÍqW&¾êÌÞ’Àêíuäx囿1›óØ^$%v!`6 ×Ó‰ã+Q·£NK†žrv¥ËÃÉæ\zNý'f(óâÆOͧ˜ay©58›F ‡ ìd„ëá]ïqJ(U’—(”œô£ã”ÏŠD€H̘NJÞ„Ú~cƒˆažº(x•³ƒ)‹×x8JÎ4½lt9­4g»ð6Èv¡ ?eßloU5;JI½ÌÐçR ò\l;%H£…‹Ú~üÌÝ·øo¡ˆÕ²„Ò‹)•Ðß5ª%›ê}¡’Ò!DÖ#tËD/%£ùô&ãù°'®Ý:ÓÌóMJT|,î~ºAÂ,÷¹‰Ä°’ú0L.˜ï`¶Ö7Q¸Í‰)Y¥´ >CÊ—T?E®® b1˜‡7Ȧ®<wåw ¶Z<à2a«²XCü‚ò]1̇”·wÇäøt2‡¡3¿&ûÙ·^Ç)…€ê½ä Ú"ø åÃt&£kç†Áï7Þ¢<Ë÷A-ll8×'£xžÚ-œ½øEÈP“‚><¢Ô‡;1ѶÉ@bqU*êºG¶@€Chá,ípZòFì3_‘OÊ-¿ÇMÝ]“es·rVëÙêµ›€v‹EbUèÍ<é%ßNNaýÏ+¾ ƒ)Cò¬•õ¾H‘ÍXKï=ö‘›¯¤×úYâε.&|>óé<Z¹þ#axO×3)¾ygÌGÒ4p³–Ÿ€ž‘Ù§µ|Ýç/ÉXÂ¥%á@]nô‡v‘Ò¬ó ÿi]Üá¥` Ó˜"g-°L0¶.W6S۪„èö>9þ>­†%¨)FÍý‘’Ì+"F}Ê„[®ÙIu1wþ˜ M_Yós¹ zç&ÜÍTòæ°<ñ&Aµ€X<„k*;%÷›EdؼñÁ`a7«ˆãQ´Ïl ™Êl'„ÍÉÀ÷(((é1xa8ÙìÕZåÿˆ )#»Ã„Á9ËPç›aŽK9 B®›.f¥äE?é.^gÏz=džJ}|¡;emì`Ê6Å<ð­  Õì͸µ_ìÖcP ›öÙ›Hw3‘»D“L^ÔCONšÇ±NRuéål9hN#׌«€»/ËlÝbœƒúTÖÅ2¼U?ÖI.3iy¬Š)yœÞúª(bo¼ìÉ-77€Ž©À§T¦%#Vú–è?7ç·¿Ϲx4à¡èŽ953ªKk™zÉØofSÃÇû% –ê@-œCÔhaj$¢tåð$›ˆx~I™“8_iïÎñ*Á¼6ø{°XÐì¾ÖðÎn­Âd.iB–ò"à0Xü-éu}¶„–óŒ!*„è ¨ùzœ¿ú„žË5\m~_{î‚ðytb,¡:e‚×<3#O‡ºPiŠZ§Û—Ž“~0Oz¾t Qnæƒ2,RWu‰|ÇiíÏ2t~¡Jw¥6**Š£Ê ×0vcüéR}°Žñw¾®;Ÿü ªÆÿe\ÂìQ-!ÓÁ‹C“;»Ø0«tãa½WB†¹ðoò6è9躮t¡ `Vûv-xϵ÷+) 4Ø€{o3цá$è(›d®_Atí¡–RâlGû½Ú¢€LT¥køËñE“Ú~øÞEmí<-Æ>ïìc+&ÊZ ü8yRQxA©ÿªû®ê<bìô¿”žã²Ö‰Îð»r(uýO_:b„f‡)ôŸ¨ÖaýÔl̪ÖQæöW…?' )¥^Ió Æ¥—ׯi `²<{aˆÍ®;âÐ1otϹq Õl+—{W¾ªyDHÙN>ËÿN4÷R†hÚqeÁpmÓ.•²Vû ñ¾iüØ=r!ºRÿeÒ}–Ÿµl‚A¯è>Ë@ÿý🹹åTÉqØÔ*±C¦ží¯ w¢QP­h\°|µÉ¡§ŽÓÌ•c´î%ãߌ–©x’1Cwè`>ÆkD{¦7nJ§Yù÷u9DU@RFÆxý_)KYœÇ«§o#"‚%Á}*ÚÕ_¥ñµð}óaSB£”Mà=(sRy°)Ð9”;“»^ FQÔGëñœ¤æMΉð¡SjjžéØ µëyRd,ÅÊw‚ûš¦œsKHJÝÕÃ<´wóNÕùö+ݽ&¾TÁÒ ÁÊïbh8®Iå,´÷N6ì¬Âžª)–&À‡)Cl¡E°©bÔ)U[ý¯4CÈ£úš»×±.¨ž],è¥=Þ=MñyÆ -rUT¡V­ Ǽr^½[1Áçt(‘W8¤ 0ZP`@=£—ø3yî!¤+ö£åoO23ÚŽÌØˆB#”ÝÖB×b pá.5 û ɯ’˜ÏïZL$•5e•s¼À‹Ü:ÿ ÎfK«NÒwß“©íëZœ<°U{êKGƒƒ’Ç™k¥#ˆ3 ™°oI×±‰z²ŽÉ*Áï@€n‘_"z­³ÇSœuéUiiÞM|Í „ÿÍBÓ«ª°V ’ŸYØÚEÝš¨öG¨šDb/@_ê§øyýíjÌïÝkáÆv¦ylé—¶÷¾ðÅ-)÷êÇ`Xe'Ì01¦K½S£7Aëï#_—3ò³#Eó½ßá°Ä ’Òc*ä5q•ÚÔKR¤!=èòÑ¢~¦°4çí¶i¤>b}â­¾J£­íEHïG}Ê=c´ {ó˜æ;9 æý«ÆÕŠ«’8yÙéþ³ÖÖø=ŠÆ…J¸äÜPž¬‡à'€yý=&~vÜúƒ*™Q¨–n< Œ`y§%{RŸp´šØ}ÑÆLƄƜöw+Ølú»©Q#bü0‡F´&…(†â?&©³=E ÎLM QÇ~X¨b u2pЋ­¢Žøõ’ï¶ý‚É$¨s¤¬»ä¾3’·–±˜*«îËá ÉãðÜú—P ZÜ ŒÊÈ¿¬üðy *O6¢tämöü®Ù„€(àYÆ{™Ó¶ðé·”XjœÑ„ò vv$)?36ÕvÔµDk„Ì:÷” ÄþPÞþ¯sôVHŒÞÏiÔ³ÖC̱!‡­ëDõ³¯Y¶¯g+k³k¶C‰©eu•î/« ÂxœÐ*{PI^´â•m*ºÌa$I3«X°Só^£ëÁåpŠÛæ©=ALlÆÕ0ŠŒ‘Wh¦d¾(ßq…#%ÄáË“ìóŒCrK˜Â ¤‚º"°‰Tµ’äŸÎRyNPå8œG.`E{B½§ÑCpÉÿ|Ú'õ6Égʘ¿„ÿA}éΦCøG×Lz ¬Ïè¹ãÊbâ³´Ò™–û;’4çJå¸Î~抚ŵΠr6l”ªú´ B5•íÑÌbýêCBŒoìÒâ::`= _Ýí"É}7U™P«C¯¼A²;3Vµ:ô5Á „oe"Ú MÈíLE¸rõæ,­9NŠ€öWÞ;›,(Ö—Í$€)px³Qá.žĺ4åh»MâìÅáí;0Íæ3™UéÔV²Nάö#uϨN•³¿[|.JÏ#Ô½œtÓ^¤³¼©‡$¦mœWjÎÒ2&xôì@•ÉOøÐà›7T÷u%ÞѺo["ëÛ³¼¶ÂCš?M‘(O©Èd…}B ì×iÛÔüÂê¨Ã>#(=»H^B1gŠÕ3¡ã^J’ri‡]¿.}ªiÛ¦Á#f5çR[=²ìß:ÎñnN8ÁÕn \óS‘³% ½µQa}q-<gô±ƒ×BŒO”4a2Pø=Ÿ£RXµè­ØE˜½8©¹P{z+§WS®ý`÷CIãìZârîS©_>€"6U`·¸6âÙ µ äÓÛb)89Ûµ*Š´ ïþù¨«“’ÞŽÌ(FÒBÛµQÜ~EK¹|JRN[‰¬‡9¨¶Ô(]§ )é•¿ î:" ZÃaú§¥hcîùõX®­%lÖøþÏ7Y. OžˆsÆŠdQ¹>VIœñýð߀˵Æä:Áα *!¹ük¯·‡Ô¶†h æ˜Ï&²§T ðíB/O)8H·÷^;Ù¾n|‹Ü¯#ÛFãéI§k°oXñü¶;°h!y$ÔqN^'XhÌnfã`¦×W!÷‹ui˜yZuƒÔCÙ[óN”º¦z›œ::9Ò‡ŠÎ‚ѼÓQE=¶  °·ƒ…ç²&'Y¤“@'ÁZeŒÖ×s0ÒÄXm¨I|`Ub0 Wø°š´°M)¾‹TYS£ &qŸÆ¯#ó:]ù^Zìþçå¦Zwk•9Ò*ÙàÍwq¥ïës¹Î«˜¥å"ÿb®„˜D'gZòE.fzˆFbi_=¦á;æ³A˜EQÁ&oHññ ç7V|JõÔîíàœÀŽ2~ЀÏMò#¦$Ž¥:ÙúÀËECå°ãj"thŠ8†wTA-År8ËÀ¬.ʾ/þŠ ƒóî¯Ó{§0G×qÉŠ;«Ä¤O‰^]CÂXåm,§¦˜Ók&Éöý€L+Ùu§Æ„«Ü³‘gñ¹-5©“PV>iêÎ\lAÓë?NÎ&,UÁ4w-vïÊ$Ùúªá3ê„&èhZê„2–-ž¼{£æLù…Åí·”l“½9k?ðw•@+Eâ!ÀMf. 1¦)2üxÒ.jÆöy¨öh—ñ½²gr´ EmËchpÎ~NÞû€TTЬ¦^%ÖÈûzH7x;fÆ`§CuClùt'qg‚(ØÌ/CP…q= 4#é1šS1<þ`Á…5Wœ•ÏÓnèú¿ò©îaí!‹µ¼(ØãzìãI¼g]ÇÍ?l´J¢B>ÄJ±`‘…Ö1T6^oº!{N†xË;i׳ŸGˆáÿ y·°ušuÎ'A$èZlsr¿†@$ ^“7ͲìX­D,þnµ¨6äΤ¥²š‚çúàÍ´Tw­¬RCtvèM‚»»·êm@R¬sÝÇuÈý8²®í <ñ:Ù¢, oVk{¨Æ”¶ U0ñ¼îè1U4’ÅÑŠ}»&â;í„Ç>»÷âÞ.´i%mk e¥Ñyj³ÞÚM go””q£Äpâv€Ò¾ŠM‚Òo$ =k<Ð>p »õ+ù º1Î%">JV.¿¶Ô÷š£,˜R,WAR/æ¿ö¤^6úq‘´/þ@™ –aVÞ¦â÷ªqÄqŒÆOâùÌsµ ÿ /™™z¹œøµ½-÷s:èz1>IŸ,‰Oõ:˜O”‹„W«1£úŒg?â¦xr€ê îHþæN(ОR++4]^m’ÿUj«ü3J»Ô–ÅÔ‚Ÿ +šÒ]M'™XÏX+oßRDè’6.¦³À@ÑÚíÝzÌuCó>J3o´Úÿ“Աάøf;¡5ö´XÊ­æ¤ñ)üŸý~Š)®æÇ©&9u¹˜KÇ ¼õÇ„‰º§®z§ ½˜km•¶#—/7ë9î!uA¤Áï Ê¢9±ÑÎË›Ê` ü­uÅÈj#Ïw¬î.v¬‹Æ·Èñ±L`‹ø|r *@ ƒÚŽ=y®”•§&V C]']ù˜?þA<žÉS(å4Ø;÷™! ½æ¥Ù®ˆ{ &ZbÛ&ø KZ†ª‡n_FÈW³ fÅ2¨"’ª¥1Öà’;Ÿþ«C;žéѧì‹eSBŒã;Tó|.ƺ@Ë´}…Sµ·çťЏïj&nN5añ)˜Ÿ|Ž ŒZð ¦e¦#Ô3Êâ« T—ÇÝþ'ãÁðÑó'ñKÆ^ó4–Óâ£äOF ìål;ÎM@)š[÷F%lQù©¬Ö£Œi½KÓ&Ü ‹Üµ€.žžÀï Z‡Ù&IíµÂ¢0™~æ =}c§LTNe/[žŽÐMȽóÛ}‰+[“lÌ8€›Î0b‹YNÉAÊÏÞdSèäµ<á0°j# ö*¥\^AÙÆëN‘ÕÝ8U¨™¾g>|vW~›–FØ~¡Û#Ÿ” u y‹ù„u»Ž2þ](qÅ"]OìÝ. [Ýφɻe·9q~®¤Zïÿ‚|ïÏi´¶÷Ø'vGÁ3Cœ¬;¨Ì wR¼´Ù&÷TeZ<†(°l8í!Ë ¶L·ªØ Í~%}±ƒ¢ 2„ôA$ìqøŽ ’QÝLh"¨˜50W¦Úë.ŽËLã*3KÊ•;6wˆ>µð±PV=Õ㎵Î4ÈšœC ˜g¤yœg}âAàdhlÊl7^ÌòO¡]`˜~|¶ÓãˆÇŽ-}šãø»6ᵫdóVþñ¾§ˆ¤5ZÕúE%žQYŒp®GéÚƒ¦½É€K¥í1q’åË“gú€ûå±ÐB=i7'dí¶øH¡Ë=žKŒY“´€‡Áô"MËjq¿hrÌ¿€½ï3õ.ÊIä?gU¯Ÿª8Þª$¹µÛ\ž§e3—ðIbrâH„ñ‚x‚ŒHÒ„—4åùÝÌTµÆ)ÔèJY '‘ŽÍLSþÜÁžjúSmvsƒ¥ùð~c½Ëþõ³”Ím£ÕY Ðíæwošûös´ƒmâ+˜’·´šÁ (íŽ7‘ý¿VÏ?-® ´‘SÂÈN[ãg°C>S²ò%Ð9¥·Éš£c—RP"¹Šì“ÅÜ?ˆBw5‘1Ÿÿ~'·­ÀÍÕk ¿—sOD86%”£iC¥:3û™Œs$¾îÝ`@kãÜ™ ?¦d3ËB [}¨ÍÜJ»r…ýSs ÒKc$ßõ~Å4ùšÙÛÔôjÝZõr1Ö@•'n–ý¸ÅŸ­u9Ô[—ÏÞs<»¥ÛO«+@ä]±BHQÌY„è– “¼ª†¸æjßšýý|Oô6µçA1dhv|®d Ôî½,:EÀŒÏ?XdáⳌ(„¢ÒL¼Š!d~¬üüc^ÉŠ¢ø™*HVuõsóZvèwÚ{Iev‘Ƥ¥ %ðkæs“oax˲L~Ðà3ÄâÙ©´ÕfÒ‚ya‰ÎD'íhÞ–XÎ3syŽY¦F Nˆç—ͨ‚nc¨ñ6P‹¯ÂB’@¥®qï>*ˆjØÃic»á.ø—úê…Ê¢'3‚½sÇ8Èç÷znÛs?‚¹Iíy° êõ 9…•µú9ÜÊ;Ôj~ýB»0|N¹Óå½°Ö› 4õÂbE°2Éç6 ¥ÖlvVÄ ÕADI–.Pƒ¤È®m¬Û ÑX! áÓl4ͬOÆ|}<Sw[ŒrüÕ–²4Å{,_ô—úÁýæú$…¬S…~¹‚:¤ô¾6*FÛÂßši¯žoÍ£×cýËÇùÖ& ÖþRK–`¥zG.Yö9®µÌˆ£Ï”¯:¶”dbR/`ו“ãâÍÍPˆvb! ]=¿Eq`o:±}Ê |QöMÂד &&fBóÄI$ ”çT¸ôÀn£HðåѱNáÇõ‚yJÿªøèC|º%Š¡BìÙ57“/ Fœ¼3\S9x0†ÌÆÑ\7I S¤K³/F[áõ=žî–1%ÛÐZXÇÜ?NÁœ{ãgÁ›<»¡§‹èN¥uH¼zK§ ¿~OdÑY eµ©<‡%”MÐáëíÔÛ™(‘ª×µ%— ÆH“ý@ CÚ·§–tN†<é-j@²¯S?îÉ®~-’«CÐ~…VüKÞžú}Ûâ\t\Õ@ÕôV¸ì8á·ôʼnWEêÿ–*Õ­ÜüB²hà0¼1þ&÷¾,–5#ÓëL¢Ü͹ ÂÌZˆ'¬OQ—“ÁGžj ´2œ  ©B9X«<Ù ŒSR±UÀž—%BŸÝõÏWÍ„]'“©Z¥ÄÚ!þ pª4+CÊø,D¥&Ƕx–ðÉvWŠ×fÚ¸?å>ü¦¥ "Íæ¢¹=Й»z"!ù䂚Æd`Y6;˜·Ò™7|6[ÎÖØRsz3Æ„Î3#T‚þž?þœ—Ô L+®w<ܱr(ͦà½urXÊÓzÊÖ—h¢ì"p9â}sÈŪt“Ÿ9aîÐÛ_ÐæT!£‡m_’7”Jáµ%•aßq¥¾ø×ÜbjÅ9 ¯ógK²t^¡Ç2rJfyfa.:bš‰U' ¤ª2lðîš–Lm"¸Sã–q¸‚EXãërð@胷出ËWo<ŸuK²¦‡™4zÈmu& ŠB& ŠcœŒW0b¼È±‡Éhï0Ÿ;@‹ âU9Ö„,V%æø¾ ~àêð{‚SYE-~_}±áýñ/µ4w´Q.ìã›p[ªh¼ „³î¤Ü ë@NqÌ­ð'Z½KÒ³+_I=O“–Hõ±{]Ì8[çg|B±§òçÖÜLØïá²ûêr¼2Õ&Ù9§.Zxo%7,ëΔ1§o‹Šý"ùؼ ð¤Ý,°8¸Z+C?ÕÍé“ÒðÃT!Œdd ¬5šáG×þ`$•Ó %áú2ãO+ Ä4seí¯ðs®6úÞíçqÍxmùHèÕëå^  gÐõáUa¿oa©köM¼åˆ§º­Ì,`½ô’NÏ|³4yånW‰b{àœÕmgNÑ9”‚Q‚ºO·"f\;`ºÍ¯©È Éד>™{P`¿Õ÷=6À;ÂMÓ-Îýy¶*ÖºT޼ó«Ã?B×g"öëh›£¥ZO”$ó¯d¦ðcÞœKí>möŠúŒYUî8å[#cüÜåË™ÚæO¥}@1\*ÃÍ#2UÍÂ(YvrÏUÆwÖw9dÒ¸&c1ÌGÉ‚0ïØô%±$Øå úKP(êï$î=.”=KÄ–\›§»Tý“>®¶Ptp/yJy2ѼÄ2æÛ@^' Äü¶„÷Ž-Ug¼Ÿsè¹Çl:<'ï²O¡âü—á§L0ûˆÔàrmÐäˆJepõèòçu€¬´A½õ͉Óøw/nP˜ ~ob”­ö‘ª1T™àÇ îè:Š9¦Ö†Ð4W'3±Y#.F­¨?Ç^ŸÛÀõÓw§óÕ WU*=Fxy#ô_ßrËHJ>²/ØÐf×x9œ/¼bvø€‡œŠ)ÜÀ–MeSál÷x«ŽÕ‡“еsýÄÉ<œéi}¿¶9!†E4&K+жÁçÙÀ!à4 Ä™À[%[¼ùóê\np Ò3ß0]’+uMÈpG$"CÉž5ø€Ì)@®™è1fÝ4«‚^-Ó 9;AÖhO9 Ctáe¾%¶øÈ¥PWƒýM‚x¯M…®¨õA`Ôc#50‡{¬e¬-§›;+²luÞÃåî<¶êv„!ž‘lˆÈø ¯¼Šz%ó2ì6’ÊilÀ¾Iö“žøïo5§oyvJÜ?)…}®Sªýø¥%t³Ý!¥†;›hÁ^'‰Üj¼­1‰þ4MeÇ{ˆ ͦ{«„fŸÛÎÒì)ç¯èŽ2r#üÙË,ÞªŸÀ¼šòPóYÏò‰Gè`ÃÜɾ°ô¬^\"3§Å¡¦¶Å¡NH8ºßϳÂÍ ¬›<~ôO{ W娄¥üËoÔfô}¶£e ¦o ýcjW‡®{Lî­<·’‹V}T«%?4ªäfðáÅŽßav vϺí÷kº‰Éì¡­…2”Û ôº¬Iλƒ%î¿Gs¹òu|‚Å2½¬Œ6|[øŠÒË£þB€ÄÌ_Ý›*% â²~1JL1-ƒkU꜡csÏÿ3Ȥ VÿÝ šºAqTÓ†Tsèpÿ|•ü¹ÕÝ…o9„û¶=We±§ú;¤‡¹=×2NæUïË‚jA¸mõQ‡ù)Æ£_y¨•»Ë—B–•0O“ ´» Õ˜íÓdFÓø+OºŒbÀ/\ý' ẖ4(êÀ„váv[@€•ž«ÀÛz‚qI~¾¾‹£lƒà)V–2%RÚØŠ™E¾ÚØùyg⌅ꙪԧŒË&lÛ¡P•¡d_¯Ùù¥£<«" a‡b¡<¤…·ä ñM/$å,9(ú&´¾¿»A¾yö: ɧ@ÓH;Äîf¤˜¼l3ÖH†œZ¯¤£õa•82é`ÄŸQ-$»Ñ„^"!uAF'1]¨î‡+À\1ˆ14éYWYô Ûþ Ló=®¿¾æSEdžŽ¬EGk2®ÿ†ü DÈ +GªáùY8ÃØæDy}VÒ Ì”8‰.÷.{4`à*.êN[e™v‹à˜݈}jCŽä; ·±¦ÂP}¦ÝP4î°)àå툤ªÑ\ÊfAÀ4ª~·p&Ú–ÁÚuÜ\VÓ’tu¾m-¢j%÷†IBšö"úî¯þðV'•9pmLŠG7°=v·Ç.C¹aiñŒ¦´žÃ|OÙÅ»4å†uBÅNÙl·^J‚^~·íC%Ój_‰|ÌUâ›Dø±æÂ¿?_1ÉÊEÞƒ5ÔHÅ«%gw€ÿÑD©»"ÁÍñóXòTœÂH*Ôvy耷br¶¬íL˸¥A -õ®ÌÅVø±NפëӜެɓÑà:>C(yþë鸢âù»ÏâEÀ'šYƒGêÜé[,_aú[J\KêÎ!­cVƒ}ï󃨸@¿ùtù9b3Óœè®1ii¶£Á¼£ø8U5…>Öôzþ¤,Ö¸¸¸­Ÿ ¢7ÉH\ISf±“ »76ãX­[ß"°$›,·_Z5›üJ;o¨‘‚†¬29¢‰|×…›Ý4O°Ú hEB7ܳӳš¦î§ëû 9C=îN¥ø4 ×{ÎçÆo¨z½ø¬œ ü¹ôzìê-i†ûºz˜á ü)Ò”1 èÐ`óºÁäÒ”âGž Ò§°Óox?«[¿+×YRªÖ=–9T³FëšUHÏ´ 5Ô‘oÁýwÂý?é-ËXê½jTã­ïõovêó­õ¹²µ9~D9¨šgý f®ÆÂ,Ǭº1=;}þÍUÀõƒ½¾'Næ` Ѳ̿¥Þ¹þ!ÂÞ´ÂÖ=¥ wû3äܶ$ Øæ&õí¶oý˜g¢û„äV$ ÈÐDŠÒIª§ÿX”cè9`õȇmýzu?œN9ÐÍìpMc¶­2å ±JAôÕ?y^¢ï¬’³¸dQx%·äÞaìVúDžé‚9$'ðû»ó¬gBœ6aãiìϺoáîååM dÕ–èWŸ ‚pìGË¿¿s)Jy ¥aŒŒ`KÏ»-J½ºXR ÊK}9ˆ»J˜ÇÇžÎÍê¢KílG]$9&wË"õÏ`¦Êëµ#ek9bˆQToc„ɤ fVÕÒÂeåø~©çb!£x =AÏ ÛtW†?i’µO\¶ì…°-ç6DKI±Gõ…“‚½- ¥ìäË|Jq`̹± ¼á GâèÖWþÄþ¶)‚3ëкÐüù_¿MÝÝ* ªÑzdEq³„FÛX…;åi /ÃóŠEg:B4ï/ÅÚHŸµÅŸ¥QÈW&g<<)€_)£eÁzb˜~]:ë 2ß,ÏÿÒ(š&…J*B+ÜT`~]²v½ú53éº:6â~X:MÔÃÐìÅ0—R¤K4;ÞßO꛽%6wÀWkuÙlºÔÇ=ŽøE‘õV U¶)YP¦9¯&]¨Â% ¾‘d#ÚÈPe©”þ&lX«¯ÆHžË,'êâ€1JÍ ­:Æe[õ%h…uJäxêÌÃ*ª »®a‘kŸCn4­§bõe–Ò6êñ…½0u4¥M`'lä0ïâE”9æyŸ$ ÂØ0lüŠÁ|jàÔI;ÝõæÞV÷T[L»Mpå¤cïÆ§–³´Ñïâ/‡¦€aWÍ¿ñ2-¸Ö$^g ûη½z–m„J ‡9»Æ¹ÅeQÈ2SHe·&—ù0S’D™Ôj,ÕZß³ /œÇ†52é‹ÈV¦‚“sÝѵV7Wè‘m½p¦—¥Æ–àaK»Ú kåîÈί?äÄV¿ áõØK Ίëp„:"+4 zQ…;«~ ¶íð1+‡r<Í}%eSˆÙÖôÎòU¯‡U&žC³ªwéÎÊ|›±^šN×^,/ä!ÿ‰•õl—o—HÇÍT¹Éd;°]y÷Ý…çR›ì]†H~’Ũtƒº™*™¼­<¸7k›Å&àcKëT-ö錜"=¨UÐMTfêØìš~ÝNõ¤YÅ~dVŸy©b‰»¤ù³ƒ»DW¡é|µÚ±>ÚªOÔdð•hÏl¹ ×x·óοA·ÿKÛ ßZ Ìæ?ñÙ Äᄚºñ=‡þíb¹ /¨›ÐSQ·|)çB½ cë _î`]¶ˆ§\ÿ”¬hB»¸IòâàÊ6}šá¯`nä•-ºT8^¶œR;x –WB¿d#$ÝïxJàüÕ†üí²AçPŒfÖÄwUvglH¦w7%ëC!ƒïÝ~w¬ÑŸâöQœj†¦›|>ìÃÅ›hJ ¯H’ihQy˜È÷ ›På,È1ëdmuàõÉÝkûSG7ö÷‚b·õh'õ{Á¨YòÙT½˜WL¾e3¸Ç’9ôCÿ¹wÒ‰¡A4þùÒm¾¥ÈjÔ‹Nw®‹æó®ËóPláxÌ÷jRº— ÿÊ;?`}ØÒûŸ‰j˜¶b«ƒ·‚ LPƒR{Ï;dÚì¡"e¢%Õ¶Ó„°† ë¬ÂÇñÁìM?²Íªg5ˆ Ô»òêu±€„—¢Ö^þU[œÄ§q(ÿl[%c¡Ó˜Q?.aýqµÓf¿%DÊ **Õá3cN¡Ö@F1FÌmjžEvœH©ˆ‹ÔP>£dŽ7%ã«™)Œù˜r˜¨ùµÍ ^9³*ÓTt°ü"õªýWHʾì»~ N&¤û;C0Q ð1›ýx<2òn²dŒ=<~¿ùúð¸5\·ˆ Åõ¹ƒÃÍCÜ@Q%j‰ >u›çp9jiǘËÁ ´v²c”ðcòfµ¸ñ_/øH-+__ ´+Os2ͨXE˜˜ªö¶ö@nV }A~ì7/î{ƒÕîï²c¢.““XIøh¡;ðÎïËɼ`yÜ¿×ËÅ,¼½/:áY(,[’8ª-„p†òÆ]Á>£[ÅSŠh^ûÒÝÒ/’ß„¦gÆÝDN¾GÞæÿI |…¬ëŸ!Š®G~”wñ‡/‰ò•,Þ7ï|”Y@Öþg=ƒ÷gÿ®×¾(åÁôŽ·ŒÃuT `ddt>¯‡¤ö'Òa-…Ìú4¼Èm¶˜§jYõéK9ë;:qêBfä{Ðï ªájóÌgèyaÂkªLÁnu0"\-LÿÔ!·"|t[°Ó¡,vXt¨ƒ<𥋠»HgÑÊ-Í”Õþè©DØ;Š<ö¯T{ëÕ¬åÁ·m„{?1íkÅ›¾3ãýÓŽ>ß³,[¸§7RwɬÜ9ž2í5gWËÝûu3 "æ ;Û=™Á:^;$ûX &D”1øÍ®Æ#ˆ%GSÀ· ,+˜DF¬Œ$­Ò­!ºÛý"ò¥x‚Ý$®‹Y£ûÓP,4¡k0þì( ô{D¿²+KóJÿeñ³ûð1ƒ…7RÅ¡®°~ËÄy!C£±É=™’cåå ’ö*MµVš2`-Nó-2¬µÿ)ÇUû÷s¸Þ*{X_û`À x¬‚n úr1†ËdàZ-Òúž0µ¸l%Õ)É?•M¶d"æÍ6âÝÇØ²x0e-â‹=ã"õz·Kö:)Àìu'9Æþƒ_Åûò’ú[·s’̯©x=<¶:,- *W³ÃBüXnk¼PäüÊbnòâœ&XºfÌð­Ó½hPü}Ú³Õå„%mKêÓm=ųC(?¢ ùÔ—¢Xû`öúpëë+ª)@«ÃMl§„—+¡eæÔ(™á…Il‘9e&6iýâÇiœ`€—(«¿9Ó#Uü¸”Ý}µ4ßÎЇøÝ¨ã?¥È½î|]öñì‚ÏÜž9å3Z hDz'Ê„þHÙ»- uA´¼ ‰ÈœÍ×°ºdŒJ[eŠ¿Øâ˜© 9!nûŒüXà#l,þÅVÿFX®Y°öFˆAæYëÀ¼ ºú4H쮫š‡‰bœ¸0^ÉÈî‚èM†8e‹'ù'âRØè‘‡Vxô‹k,²Õµô‘bkýñ›Žhz~`B£ –„kº¾|¿¹Ê‹âxeƒ”„×·ÎΩÛÂ&ª%„EUZiŽ!ÛDû0mæNš¯5;²þ›âzõ*(%.?UÔæ›Î®FÛþt3üuÎôs´/Ìeo¯ÙŸò' Ìø8LðÑ¿êÚzIŒ;!L¼»ä#¸WH ñC«¯-ba¡ˆ*é ꘥#mª¸í­pÖD“9F£I*„M”Y]Ÿ\Á±¼›Sž¨~„äÿ–æv ÇÎB²ÜxH0 ëTÊŽ •^ÒbÛÄû]ÁÎ9Tòà 1ò5LøÜ,F Ó ‰«“ôóL“qe¼ùë·hå³4R…Ñrþ´í|—áÞW>úÓPþ4ç¦E2¹`ÕËi6R”oBÌ¥0I¯J?¿âÏ£±¸÷=cõN0é•[÷Š‹_4†óC·?ß7É—Ãú‚¼P1Z LëF-j&3A„iìŒ ¼>¥ØKµó×È‚¤c´Œ¢c1ULífQ1 [Ü¿¡ÿœ:‡aV3(sÕ ’z—h²’M¼Xi= Í¿ÚÊ¥!œ°¬Óà˞ìlí¬Í½Fñ€H*Ô-Ô–}nÓжkÑøÊHÆ;O˜N®¸Dïœ!ýÍMò¼L “›ÍªNæ`Uè{4mž×<ägÃĽ„—Ž\P\-5½²ÑO³÷7‹òê+ª„øÂGi) Nr?†~±#bÚéöÚ³«íJ³*íFò°Zeë'Ú¨}Åçàx påt hþå6VLälö\ÑQ¨`²¦ùM‡Ç&BÒï´³ ·†d²Ä«qÜgåûB6¢ØäŸ©ƒ‘á? ¤Çñ è[ó²àìÃ(ù’'áýô›;íKÍJ´^˜È§Åê ¶oDˆˆ›xö»ì ¾”h{eÍšfw1‹;¨§ÊµºúÏåPšƒ`É6&#´xè†À#ý¿íwŒÀ `6‡ÙùöSã Ñ'W„T@É&똲]ú)ȆD4mŒð¿¼nÓáýø¦áö[åF`zºý¥ãFÑÍòšÍˆœMÙR9a<’&ög¨ˆM[£)$èhÜ>òGÏ2íZ´c¾†9œ4j=Õg·Á|.¤qsîƒo… îÞ\R U÷·ÑÿJïYÝø4â¿62þм•ÆJs—n*ÀœúÚíî#MÈžÁ­ºÎÄ$9‘·’A ‰¿ï‚[2é«F­]î¦nŸ°™òÅ^pÊ u™‡ì¹5x˜’Ya>7z{§1u•ä2Štg(^>äîc”dD!£Ån˜¦°ðÖ.b´Ã—ŠêëÌËýàM¢ÿì.,t¹É"²ßàhŽuks0’THcf‹ûÅcŽ‚YÜá4,z Y*˜MA?8±+÷ 9—fl$›Íã…c³90f2Ò‡Ô]·í—æ©~‡r©ýó%uBÙÄÔTìMZ]¶DQU\s?´Í'”5àäÞE ç‹6„ArO9¸°µÿ}úÝg‰ÙÁ1îVr;À‘9“X%€ú˜BÂ7z‰m+Š®N–úv¶ÕPif¶Û¡‚mîVÚÌC„JX˜„|»K Œ—‚e‡t:´µÿ¸«ýÈ›;ÊgÌÈvmÏ’•Þ˜ý)X¤k2JVIäÛ:®U‚¨j¼¦8fª§QÙkj­,ÉGRwÊä.kc†‡ æ]L’{8UξD›ÖS¹é(âý¡)ѵ¬Ó²B7k)ü¨Ë¦›_?Ü›p­ð 7mRŸv'hU2«™áBñ÷Š`ÿ:<ÀÃr*Dƒð”5Rr»Ø¤¿Ôq Ë)RTÕnœ6бç¡C ŠèpîˆgïBKpÏùü²éƒe¸ïúÚHBOAtÈ,ÏU« l[˜\_/nó$.˜—¦ ÷Cç•üˆâúwïÝ šA"â ) Å4³A„›)»0Xò’:UØYF;ºöì©¢7F'O±G³Mƒ¸hýLMbFãhh˜*¯^ÅÉ£Ÿ‘ g ƒÏ ¥Ú[/ˆŠ É{zÍÄë•(|•ü½1»Û[ªÒÛXù°=è9r•—2õ¯¡*åÂu¢Ä¬ƒ)“ºÂyÅê¡õßÏ&0„„Á`zX¤X=ë„Ñ;׿uG½ <·\’=P›\žwAÐövJi¶À`!tJ®r)ûà_þÓž9uî^Ü:Ù¬CfÕõH«ÏºzÂýŠQª±±ýañwû}JY¶²cBëâœâ?W„Êr{½µÎI‚VnîèðÛ±É+Ш&qzÑ’éR¶áwñð‡Q*tº•å•ë«ZkU12{n|'–ŸŠ£Ù¬  {ü´Ñüå½üÆ#gÝLŠOšªÃ%.C’["ù¼(âû GÕB z,‹É©bJS²…W»­%Á5`—vbj¢Š hÍLÀc^ƒ­öþ.úŽÄŽñûðÖhˆ-ý‹]¢?ýSZÕÆ³!–¨ÛÓf•VQ¢¯ðD† "1ÄêOÞˆ®ô€¯ƒ7˜QÞ¦ewŸaÔ=Y9ÑËÿä`"_Öí™J{:R»6ml°~ÕúÌI~õdÈ ó¡<Ú̉stùjî ;8ÝØNïÓ×M݇Uf­{±Æß7Ñ‘Z`7 º-ÐyÒ O: $]™¬sY7eÿ¬Øã3e h³à{Ñ¢xtÜ‹ûÚ-šIžÍ‰ÞŸs2Œý¤³hÛ•ßwU)ƒ?Õ‹NA“‰ÑPÒEõÏÁ1G‚pU#Y—©·¡ÑØ®–ܨÑÄIt”‰¶txÉzŒ¨|€Õ-|Ý•lp$´$ôžzšúø$}›Ðœ6ÿmÏ&µ%Á\Ï_u€ yÚÅ^ÊÌÀ˜=„P„Ö¶ËzmóŠdR[Ü*ˆK òùËî%Ì'÷rÁìåÎ#JW ‡ŠC2Ù ;‹†”õdR§æCµ§¹l šØ `m`Ùˆ•wñ×Wjã*ìÓŽ2Ùä:¸WT‚‡Š ‰Ð±¡ß¹œ…<çÒP¢ ª0¡s>ÒÍÚäûTŸ£—ð ×Jò£'w¯¼Ñe¼°N@H³Taÿöõ”Ü3Ùrq| !¼h^íÞ(£»CËFȲôÿ¨êృ®âô„Sß~¢ž±Òê3黼㷖hÀxóôñ{Ìz#3ë~Í»ë‡^WA\™£m–jeÇFÑÕfÍ!ôêŽVCŽwz|•Äeqõ亿sÓá8‡˜”¨ˆŽ¬KC±?Þ ú?Ã+ËÀŪÐ*€:¤;Ðþzè:#=g!oéJ}ÉŸ–¤ZÁîþ¤I£ŸPA ¿°ŒIÞ™_ØBS3Ä…Ž/¬–«Š)s ¿|bšÆ!kQM‚ÞË £cÑ Ý+½˜!/Ëå—Ø)ˆG€Ž¿6÷w¨¡³ò ZP%àš[Tûi_1ç5–³E/GdäIç”9ÂB©­žfOWH‡ýéNKÈjÃ5CÛ¾Ktrpé½±O+OÀÓOSÈ^üK̬ÌTQgW_Ÿû˜ÈóÜ€I=„$r56Ú%D¾qÅ™º1óºè¤PÂ,¼¶,w ð6Ë MGÄôÐ4‰3U½³%ìƒËb4êÈ¥>U]â +}$|71Tn™j‰p±Ï{εJ(¿7 ·Q€åc­(ùìaæoªª“ÌäâÝo”zé‰o¹™ã:ö9ãlAÔlk¤é #{Ôeù2½p²×¾JŒÀ\dÈ#¾øˆ™o©‰§/\¦SÛ…›Ï²¨ßÁO’¥òƒÿpØ4Ô«ì þLMañ‹"',Áó'9qØŽÌøB\ów/6’±×…¶1º‘÷wÊ×<Ö¹PV’³¢4â ç‚'±e¦á‘·ÊÚhÕG‰ñôḛ̂­Að¾’D”À”4‚ËQϾM»èèj@vA|ìv¦½ÃC'ò '|ÀR‰~§E²V_×v´ÃOÎ {ÀÙ—ŸÒ–Ï䓊½¾ÊÁw][ªÓ7§KxvAÐ÷¨åGõŽÿìGáá–†û·² Åuhp ýÚ0qoüEJŽóSúžŠ10Ûï KÕië•´Á&)Íʆ5£ª$ÆN Èÿ¾¶}oQªÎÇÄûtÇ¥£Ý5NÖêêYÞõÓàÄÃÏE£5ØDÏfˆC6L±íä >ƒS95Åã³f!Q2h& ø-SzW|åìÝÿÉöïgµ ú_®5³¡çáÌ’ÕÊ‘Žä¶ŠTØÇSlÜIQŽZˆK–)³‡(gÍ|ùžœ¦C Ê'Ð%ÅD 8ú¿¾ÃÓaÊ5p¥~«3èˆe‰¶T5€b>ëR’ÈÆAÓIÿQYΫì2H+81ÏÙÊ/<Òý¹Þmþá"÷N*WµTO°ºåH<Áˆaâ“ær’! bƒ’šEO¤º¨»”-ûhÒ‡jLŸ´Á¦þrÂ|ŽéŽŒ¿a¿ ã®h§K*@´*Ã'~Jïr 7ºÓû¸!~{9àGþ­byL¶ð«f¥g¬Ê¿)õ ’)ê’T|+Ô7;&ùN@ 5m{ͽ­ÊšT`Þ䮹žÐÒ»u𙞶è7°F „âÞáYÇÅfŽn‹*§j®­ÎHçRùŒÉ ¸¸æ¾‘pz(¹P ¾Òÿö9-Ï©¸,s ”ÜElµ77ë¡#œŸLëKö Z0±gΖ`Ì?•´0?¹áM¤Us"™­Âºåíòçk‚Et±WîE°.3ƈ¶¥B[D¥¯îi¾åŠé¯èzA;毡€Û:õ‚ãÕ¯ZÀó‚!ŒÞ„øÆQžS um6Vš¸^¯Äk`ÿ’IDB-‚ÃQ°fÝJ5–‚qËo¥ÊS™q£}º`š»|¶çUϾ“–™<«“®ó?ôFl>ïþÙOqúmþ§ê>|õ‡LÊd>þf«å{j·j9Òp*¬s·Ëãîu?mKs{§Ûš¬y;®ÊsÖS  A) >Jî p&ÓÊwœò~MŒ#êËü¶Â3Å©'üâÎzë™r >®<°~$b÷¡ÕøBÆ0¦^±%IŸ¸ªÁµêLª\Xô>æDá“o2= }>?0Ñ×mñ{“ùxÿ%hê>b-é=G_3(ãÀßNøÖ·ŽË²Š˜«-lÊŸ/c3aÅ“¸”¬ßœžO•@¿$ýåX¯ÕŒü`ãÜ<¨XJ…:ué%Ól9Љ‰\7ÀLÆ2ÿ•ÿ_ð0²h?ˆ|å¿2ª»Ò5q›(9D: w¨/½^BÖ¼Ú†½äŠ Ý‰QÂS ¬†ÆÕµX?`N™XË€Ïlþ_{Ùu×ï2y )û`Ÿ|½gbñÀVIÄ™RåïàrYé2ur#AÝ’öHýrž×ü…y_ÃuKÁöý&.‘˜ôEñòÔ^ÁlìûРӢgžñùVòË~ûÀüõ­Xì•'eQÁ£&0ŠidZ,RùzAä‰ÌhÊ J¶}ž¾?ª#ç9ëç5@c}°»%(gÊ:í[Áù4‡=9²”åŸëá³e£‘ìYÈNѨCAÇKr9Ò ßJiíb<Ù«~‘3‚ûS2‡¬QÊ-åZH _€G“7 m§É 6e†¿O‘Ü×"=·¨—²ìcàØjȰ¤æo‡ƒ”ß]!?”™3ƒÀÊÒáª_Ú)êçOþ>dmľ3œÝ9ºwTTÈW‰¬xæ­ÐyT~—PÖm?Ów†\ñˆ3‘“š?‹`êyÀ3—‰G´s ÎâXØíú  2#C§Z<¦bãj^'K„µ¸9OfàµìJU¨òmŠ(¬±ë†–Ò ÏñÛY‰órýþ±NÈépl÷2:ì`ÊI‘zûARÄ<¿´D›x¬ Æ4¹¨$¦þU¥æš»VÉ:ó*Uù˜ØcÄL{5Òë7ÏPÛªÀ5,©¥%çòðÔ•ýM…µ}½ªM>çÚS•‰„d5HçQDWÞßqæ<…äšË¤‹šzc˜Vkª»ýÈœgkm×ð˜‚&†Cpóû˜½bhÁ¥ x@ @ì‰ñ×å›ü8Ó5 ³½¥³úx·4(@·ÝÞHÕ®ª°úæ] ¨^¾»ÂÍ#?#¢e}!ç?÷. ùõ̦ûëyïµpïÔ&,)¢r¤X=K®¢,*?;(Ñ¢À¸Ô§$4Ÿð]7Í,†ƒ•Žt?›ÙÃwÛ^_ ¼I¸ñv`Ý“üÁ©ZÑ7z”:í?ñÓ ~²¤LoÏȜ˵çUÎSs|@…hny¹š¹E¿OR_ ª‘öEƒÄœÈ¶M^)`¦òW¾Ø˜{?6¿Óô&®&a8írø(òÁ.ë^q­° õMX‹³Ò)ƒ4ÚqWÖ)ÙâeãâÁb€–q}ÿ¨T®ìÍWË]ôq9•BÁÌòÛH®‡n×Gæ öýÛUŠWý4ÂÀæ?ÕïÏv+î³Ì‚¬Y \ÕCÿMûT¼j!Êñ†“yBÐKŸ­7>ÆV÷ºÞ0TלtoñÅ_lس¬L¿ q^ÿí!ƒ0¸ú1½d¯-©iÜíFjö ¹TeòM¿cŠ«ç#&ç乜x{E·NÚÜÁ„é‚ Y¸¼…n5)1ÚôÖÒ³|øŸo†RôK+=X ­ähËEZ# ±èM]+(ÄÓÌž2ð®.(wt;öeqY™f«É©Æ2ÃÃùµ" ”™½¹ÇíÞÙTŠJôšð«ªÿó uã³… ™‘æóPªÅBÑVÕ_Mê@§„ìcþüWrÅf:+wI,¤Ô’† QöÏ]: %U b€Ö4„è’¯-`øGé!mTÜÓ³â6áøŒÊ+rcù‰3Ý|§MûD UŽœÔŠÙÕ' \“wƉ+µÄƒvV˜bˆéÿx`þñLÿÿ¦ÿÞÀÔÎÜØÅÍÑÞØÅæ°ÃIµendstream endobj 16 0 obj<>endobj 17 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 250 333 555 500 500 1000 833 278 333 333 500 570 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 570 570 570 500 930 722 667 722 722 667 611 778 778 389 500 778 667 944 722 778 611 778 722 556 667 722 722 1000 722 722 667 333 278 333 581 500 333 500 556 444 556 444 333 500 556 278 333 556 278 833 556 500 556 556 444 389 333 556 500 722 500 500 444 394 220 394 520 600 500 500 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 333 500 500 500 500 220 500 333 747 300 500 570 570 747 333 400 570 300 300 333 556 540 250 333 300 330 500 750 750 750 500 722 722 722 722 722 722 1000 722 667 667 667 667 389 389 389 389 722 722 778 778 778 778 778 570 778 722 722 722 722 722 611 556 500 500 500 500 500 500 722 444 444 444 444 444 278 278 278 278 500 556 500 500 500 500 500 570 500 556 556 556 556 500 556 500]endobj 18 0 obj<>stream x¬ºcte]·.³b{Ŷm[;+¶m³‚Š“J*¶mÛªØÛ>y¿oï³ïÝçþ:wÿX«ÑñôÞGGksŽIN¬¤J/ljo ”°·s¡gf`â¨YÚé¥]Œl,M_v8rrQ' ‘‹¥½˜‘  4ˆM,,fnnn8r€¨½ƒ§“¥¹… €J]Eƒš––î¿(ÿˆŒ=ÿ“ó¥élin øZ¸mìlv._ÿ׊ª@ ÀÅ0³´D•´¤$T’ êI ÐÉÈ äjüO,r–&@;g 5ÀÌÞ ðEøg0±·3µü'4g†/'„Fg ‰å—ÐÃèð‹àt²µtvþZ,æNFv._gàb°´3±q5ýÇ/ºÙ×~88ÙIØ~ñ¾À”ì]œMœ,\_V•Ä$þí§‹…‘Ë?¶-¿Ø{³/IS{×Îâ Ëè˜/®‹‘¥3ÀèñE´¦–Î6Fž_¶¿Àœ,ÿ冫³¥ùy@pš9™Ú¿`¾°ÿ9ÿŠóÕÿŒÞÈÁÁÆó_Úöÿ’úß>Xº8mÌà˜Y¾lš¸|Ù6·´ƒcü§J¤íÌìÌLÿ¦›º:ü'Ï èô¯¢ú§f¨¿œ02µ·³ñ˜Íàì]¾@õ—eÀÿX’ÿRü?’àÿ‘ôþÿKîÏѵ,ÕÿcùÕÏÿZÂÕÆFÁÈö«þ5]*ö¶Fv€ϘÿCÖÈÖÒÆóÿþïbÀÏ ¹«‘Ógÿ[ØÎük(Ð3³3°ÿ»-%,=€¦J–.&3#›¯ ð¯úT·3:ÙXÚ¿úõ_ãàK‰é«Æÿ©ÝÿÍS³°4±¶ûj+û¿Y@;ÓÿVçÿ4È¿\güÑá(}u³‹š§ðÀòö_£ã?6ÿhŠˆØ{¼é™9¸ô,œL_]öÕgÜ,l¾ÿVþÄüoÿÑ•7rq²ôè01011¾þÿó÷_;½ÿ#ngboúO÷«ºÙ™~ŒÿMø¸-]Òb_Ѳ0q³rþË–‰«“Ó×ú×Pø î?öÿ¾@ Ðî8vÙ0¿îçyÝ2ƒ¤_G‡ýÜê7{Ò°­ÚX»~2íãÒFȵ™l¤e–KƒÓæ¯8Ûgò‡ˆãë!҈sšœOZÚMC¸= … 3azEâ!™A’Î7u°çþÛ-Z^@Ò<_æËùÜBNdY[ Ë® CÂo%åll‹@;±†m÷Ÿ üžLÔÙv„4:Zz£~è°‘S–ŸT>³Ö½ðð´b,Ñ.@t„5!ól£Pl[³b Ê› lÒ!'/LŒ­‘ªÉ\ÿ4Æ€¿O‹ö°¸.Ù´Y’ü,36‰/k‰÷Éê£al!âí3Ë[{œ£¯+ÝÊ—fÛ Ú±–dÿœ¿¬é€£5øíÆ_ùÇд³%ð…u;èÆãão´ÄrÝwµª€o Ôö½Âs»µÉÇ©M'‡)¶ŽŽ,¡·““Ñ¿ŒÒµeøf¼™&)WD5Ñ+§†Oªƒ*oTîÞxÂVÓ63bìõ*Ñš.‹N›D}Ú^âä™xÆìn3£|唼ԷúBoòæUïâmü ß´Œ{ÅØùä_^Öþ˜»CÊ?rjØíçNÕÊ('yªÞã–ÁÈâ5gÊTFU›S~R7ûÑ*¤Ûjzm£Fñ`“É ®áqÊ=V´¢£ç—Ô8EñÙÄ“ÐÇùû¼n¬ŒïÎãsEb§Ç½'ïºRu´Â¾¾Í·ñχ˵QÄ•¡D¹¯¼JáÔ…ô&A75OÛ<Ùdªê«`ó Vu#uš¼òîó¯ý2êÙìí3œÇƒº‡”}¦Éwo€èúÏ=Šƒ&ŸâS qš ‘HL™°¡½VuØ œ²éGè’pCíp…!‚‹Pɬ)Ÿ5UÁ²€%òk­>ÿ0áÎaà# {gG=F>¾Y8 u1¢ÿ´‘T$û{“Ó™ÕïÑT)qCÒLì<ﲢߟüÆ”[„11×uè9ÕyÆ×ÑÚGÒÃ¥–¶rŽÕ™t>…’xØíé7Unê˜/à­áß›·ÜàfZŸBi;ÌÃR>³~ú˜Ö%åLÚ®c‡êm›KÖÝß¼A(?ü*B>³èFLäŸæhôeI¼I¸“g,jìòWîãÛT×}èžÎž†Dòkw3üÄš´Í£ ¶O!q~3Wé§ØÆ… Ùn¶ÒA׃CÓG3Ë/Qd Ÿp–ËCcˆìqÐvìÛ&ð²9IMÒå+ñ»9ù&z4&úT6Í,ûÛ,’«`⮘ ˜©eoÓ¨2·=T/^—¦Op?ÕiiªŒ@U‡LVçjÌìz«0!¾·&ÄØJI4&ÃWn¶¦@æÝU*Ø›]±øÅjK„_¬Ciu8ÛÿSš~µcˆÇKyW@âÛÒªˆ€¡(I‹#"õ⾘¤§ýÞ‰ÄØ·ðŒb®w ë‚Ãç¿Dœ"™]ù¼¡UMe^eCå‹ùl]]¤}÷jz"½Êh¹¨™K&³§#Nê¡W\ý„®&ö{uu£öF[‡ EPJ“*…ÓeŽb´þµûx‹ÞWë]¾ G¨‚HµäRªE„H7µI“ }Ǫ_Ó«ïל‚ iéÏ¡÷í­€ªvÝÒ†ÔÊ‹ŒÍ‘ñ@™E Cž¢;3–›‡×¦€v¤šðm­^'&´U¨Oaì³åÃa¤Â¨o;t7Œ±`]ЕYßIWUÅvþ:Å@õ¿]vÇðÙÏ¥\XeB„" èB©F‚݆žù5¢%_ÚQî¯Tã?ÕM¨3`ˆ÷éR'~O,×RïS¾{µ}h»æmIµÏ:Mü¤5<#¦0¨V:Ë +/š²oâw€%%`bí2šµ/ý}OvM Øo¾oT~ìÜ,¯æÒ©ùlbÓñàøÉ„(m´/(àÂÞüS#gOy%uUeRÜjSµ$›Å9ühWô›ûTj…{}ÔA;ø^‘ße|tEÍÎФ¤±1)½YÁ{Å+bF÷**ö†±ŽéÛÁB9$\~®ßT¯y¥S“Çá» ië÷ñ€øÌÔaöüòˆ5æ’Z9š!ßÜA,Å_ûxFŒô0½nå¢k»PÜ»žÙµ‡Ö†¼0·2Ä¢ e^ ø/§Ø©1Å«»V&A ~5l¼@É·©,çõ}ìõ¶ÀÙ\Á©p–M5¡Ï4v(¶<ò!©ÀuHéÝŒ ††êô4ûweƒ.ùa¤¿9~7•µ¯ à\îv–}£Ý `½[V}˜@?ü¥­E 4ÞiR!WFrÂt»%oÝðéÀ3߇ ›T$f‡¨™`ù¡–î5Þpuï¾(j:_¸Ö ]ÇŸõNq²…Ó­2{êvÍ •ì&c•ƒjä­xE­œË…èHz©\uù’ l¥‚Ø<¼û)<µÉô£4º"ñ`ý}8…ŠË˜gZ(Ø®^ç).«“~„Ú$¶@Ž€(XÇ»'i1L<ý‘Ʀ_‡Êi‘• ·:§R-ì+*lÐcØkIEd?æ)¼<ÁU“ý¡Ï·r‚úƒ®%VŒùWÛÉcð~ ?åÐ IÅ‘*ÝÑ7oêÏÖp 1:ôäxÏ3mö Ö!U={(µ•×cÝÌR Ç‹z<쮆ïàùiÛšÐ&®`’eÒyñ¨Lšž»LeÚ½.¤Å½Þ]ÁÕßP‘Nr½ßó~ߘMѲEP ìKÁîPP=ˆXt¦ößÙ“¥ôk@‘˜èÌw ûl †NŠTÈ¡æ¤ðäâ ¥¤5?ŒD0¬þ1rý“mÌ Šˆ°C³hQZæHÈ<èæáYf»ÚñJÒåwû'€lÌ%o=¦ënR¬… ÄžõE¼ºK]Ê¢Úíé¬`+yÛqÊÔd¼§tGWcÝõ×Z+14<¾JqÔ;ÛµF|PÚ©jQ%(=òaDð·ß—š3ªÆÝl:ôr)[’xQå½@Nåç¥uüÜz0u=~9É0ýù«êŽ•ó3| /$%×0‰Ú~~ÀlסÃÞIÛâî›$f=dȨ )NêZ=µP&V Jm%Š1˜ÜIo8¨¸’Œ1 0~ú¹G>“!Þ9ªö`Ú)gúÔ:nûÆ)W‘ß„Æ`)nDª®´ ù´½Ú¥ˆò§Ê!ׂþæTT¢èÞ¬YêàgÇéÀ@gÙù;C·¯ÔZ׊rWÊΑq^kzVZvÏdSnø`œ›ÐI4‹XÙ§[i‹)›þìÀ©C6d™~ϼUѺ  ^[¶T§Ou‚ôÛº öч²N,"úo¨»Š%ÿl9ùQQ –eo š)tU‚‚S2 b¡¶«3!~bÃ`5›üINØ-^æ\µûµd(ð'Kît7–Þ‚;è|½!£npmY]å›l޶‹mÁL@øNBœÆa®çÚÙó%šNÔ¿õ`©ÆrÊh òû^QmÛwfDsÓ[ì•b$u17áy*|ì寸qSöß÷Ço}M‚a~nhp!0ÐqñC?^³¹H÷Yÿ%{0ÅÛÈÏéSÉùt1£±”¾Åoa”ïrˆ nK'’ôºOX`:oQÆ2çBÝÆoaöZk ž‘ˆ ¦nïqãoS¡߸>é;ßvÖÓé]WécsÊa¼¡Ö²EF+‹íñܘ-ýMs‹ß®53\vôè*¢8à øØq2a¯7¢ç–/Úôâ‹”6cÏ6EKÆáä&]´šÓ[(ŒKØW²ì¨Éç<_´9GAÑ~ôm Ð|7¼ÏžßŒþ¦¸ÂSóºöÖJÎ8±Ê7WÚ›4ŽH×FgW‡ÂñÆ·²û¾W@’ª¶z*1µ¬|¬6‚K°lÄ­\6m6Ë —`AÐ(ö0vh¶²CêB. ó 5ç æp#ˆ? ÐL}¡DFöfó-@[`ÈR‚ zæG[Ëá¢ëO|MûÄ‹¨VYèüÉ/”öVmP^vrœµîy‡ ÕÜ¿Ìgg ¦ê*:´—¤vþZ1¸w,íͼ¾ÿGBx¶@þ™µòs³úxôeÙA/öe§ÀF¾ 65«¡WpÃã|w¦ÎpNƒéÉsÚ¼ø®\b½*b[³œeÏ1~œkDº…2*ðJ­#FÐ2 8ÝÜž¤ƒ‹âý©¤)jr^‡è¢C1ÉXº< PÐ…Úö!7ÃÝ.À-¿VcÚ®où{ %îÛŸõ\Ñ5!œŽ@ "øs÷ó ^¾ÙMvr{‚§¿Ü+¸wåö+ÿÑ'Œ}V ø:îHÏŠÔëmjcÔ›•'ƒµ‹4ŠUÐ ¤ݸÊ_9¥ëDŒ”Éøšû÷ð?lãôýžq{+¸ØÇ}¸h¤0²7åß:œZhöÏ7æu6¸ÜÃ2:~£þÒï“Jþ<[­[ )[U2"ê¹Ñpä7À÷Tùç¡ÛÓû›U]Ônê2§ªkå4«ëKÏiE¿Ö‘Š/ÔË(sÕ-8Ë+µïè/ZŠ'a¬ïßÕMfmŒtÜÅ2õ c¬Mðy6”à‡öˆ u[íûº5äZU-òd=ÒÈé IÄêjÀ/ÿ€‰$Ø1ÚBõ]ßܨÖÍš[jš¨Y˜?pœ<´î•œ5¸¢²I£ˆ;0÷~ñ²4YW1Fm²l³ª¾B{KdåGÄq—¢ãðBdÆ®¼l {N¿„ˆyX¸PpÏ÷MÊJ¬U¥ºá=††Wø=c0J’ðßÛ4[{DËÇ)ÌO¡7rÅìT'1oš@Ü|œþHß—Co/äLŠ·u9ˆ2C`3†Vf9äl½a—’ËîŠß®Ìa¯K)ØÀ¡ý™lìlJ“&«#º¥{4Ÿm¯èfäœHÊÿþý‘ Š ¤ÎsE$¸}KV‹åä®3œ8™+ksT8y€G©ïÅîëðæ_ú„³°8¶j£0Ï\ãl…51Jì…D;›oYôo€ÅNÎõÆ`¶ò'm–¤ï1vÓ¿jÀ³ Hž™e>DB‡™ÙÑxðC8–Q©I£…`M ó˜vª[Z¨CS¸?îN??Û]LAxðöÿzT[|® [²wÏÕ7Ë=¼Ä"á$ üIeõœ‹ÍØ0ùœ8®¼ÆÌ"¢šˆdIÏ2›îñÖRÄM âÆàÚ¶(/ ©Ðeö—ºqà²PÀ“€eìºô@Wä$Â,ð 'â‰l@{K? ì°¥Ã}z$¿.¦'¿”¯®÷CK &šÓÎð™†ÆØ®`­·<}Á+ïå‘=ǸÑ99®fâÙ*T¿Îý;¸‡…'œdeVKDo½?°¦›fS93c(ã /ðÞþÓ–ÞÅž¦/‰å7–˘ÄEöbëSÃGÓrAÙ:'!GÍa‹ÇŸC‘8¯½f|®Ë‘˜milr˜.Z (B5”KÎ…9œ#S'Ò¯–Âv‡º çʾgÿðÁÄäo6h<Êžm) ´ {@âVñ¨óMá×'¶ŒªûW _‹cI$pÿ Œ¿+ï Nï‹üRwW¨°¤frÝÔ¨K¡„$†$Óà‹–Î1\ì I½V~ ìÓ}rTˆqºo[ÇáÈËÌÅ>×õðÇg¢Pº>Fp׸M®½Ó@~žm_´s¬S´H8ð ã~K¸ÐÒ­@`_oRIRzgÃh<2 ä/¥pÜ(”(n=üâ÷\‚íiî|RLo¹êÊËzOq3–Ãw\²¿ê–fG®œ©vÊěԠÛЗWTýb ,©¢"eꉾ–ª·W`Cíž#,,Á 0ÏÊ¡âí¯”*M0KVà'±ŸŒÀQR’?£Pw¡Hæu—ÐCĘÉ#%¿§¥X 2qfrÞ—ylE2úGåCÈãÕ=z>~,¿ÊçvÜ¿÷ŽëýîµÂ)’XyHî‰ÝŠãË”E&cú¯º1c,HèÒóÛ(Ô!€YåáZÂùÅ}Ö°^¢nÊ9(ÍÅß‚òŒ;ßx`?IR !QéxÛS@FS‚{ºêö¶öWIÓÛú°\CŸ›_^#Uð=ªî|w,e…ÂÀsÙÞùôR3f«MÊK¡›˜=áF´lp).Ä…0³¾p’&× ", ÅUŠŠÕ髨dÇÐ YPE«õyS¼Mœ®=]ŠàÕhÖ–˜«ÐDeå§Ä‚ŽÛf§äš*©pp>[wÌ&ääQJcÚM´°hxÍqàÞó]ªžIë¶¶Æ"2S¥9!HȉòTÆvŠ=Ül3F_‘Ÿý(³h-´%@‘59H€ƒ¢OWöbïÇÆœå¹ßŒÌ(HHû&a-ÐÆiâIª`ž2œÃ.I·›\?Më“6+ÙiOû‰Ý(ÍïÇÍ—ÚIÃ]‚ö&•Ðó–/Óå—À3=œz Zâ>BÆp±åÒÛRHµ˜³¬ 3¹XÅõ¢—ÊI¤¬øÒ[†·'ëñú K$Vþýž-þÒ;à Õ^‡”²P×K‰\¼ý[µü°Ê_äj¿<Ý|2¥@h(§Œ†Ñ¶“ö<ÂMø=µ"""k?Ã8(“96¿`ºø’Žò5 ™fƒÏ?_òÙ7¦¶Ê(«¬;ŒcmWÎél)Vyú\/¸ï(»ÛôÂðhÏë[k‰YH#viÍN¦ °„¿y|_jBw^$¦ „¬]¶ˆƒ½§ÞkÊŒ¤)üþaÚ¦¹Æ`¸V Ýq`K¶É€}ŒÝt€p:Í/ô53‚`•ªjþäxÀD\£x*vˆ-à-ÈxbAv¬9Câ;¿ÕªjC f1“¥U|<¼TØ V‘'(z‚“ëp»Ð¤ŸCH’õØÚœi|É¥q´0cÎ¥ÑÒ¼]†lóv9 ¹xÓ¤ã´z]óÔK±O(r ¨‚rwa­žå(¢‚B,Þ9X&Ði.fáqþòt!”¿^Z„qŒLBnå,Q&©V]`8£ü{¤Ó’„T^ø-É[)ìç5Ÿ{n ÂdØß!â¨)›Y§¦®,¤+”zÄ0ßLÕ™€„Õx-²1Iv´!AK~aÆî(Lëw°;àl¥,€±¬Ft‚’±z rI]ÍÓ€ëƒñOãð Væh$ÇZ)Oææ£d”C4ÃÝ»‘¦»T´¾X¸Àûï$.xmˆ‚¤ÎÏv ú;›(@|?ºê˜½ꂹ=/®áú_x¶mA ‘X½RñÝÔJ!wŸŒÎ$¶Ä®±¢Q‘û!¥ÂÝP[15 É%Q¦ä»~Í!RRي犪¹ËRÐÐ$ éx¡@âø–KXEÔ|Hƒ÷ÄQa73m›V»"0âµÀÂÅÿ»§U¼¨ˆ²ÿ²znì”9ÚƒŠTy5t"¨[‘Ž®YŽ'‡.Ÿ2Å;ô˜0hØÝ¸Â‹YNÇ &阊[$‘†Nçœk¨R•¹ðåyúôÀ¾Âb½[Dî®"z龂le}"yî06¨çA⦷ÓÅ“•O­u¡[¸_‡Æ¢P·°UسÛFz \V2 ™Jo|ì7ctõ¼"\ªì†ØOŠÛº!» „&o$þÅò{ÝÎH‘¡òÄNÁ9MËö|©IÔ?Ø[ž‚?‡Úë͆!8+1’ý&¦‹ ¼q Ð}pu>ÌPc‹ég/á=cޝ=¼šE—‰® lå#W[·Ù¿×…Ì™Më2K7gçI!;Ø¢KÉ.&ÊÖ uã&¹ü„4TœKÖ›¼â†ÌCÀ‰i+Eâ½c0Ó¸°<™§ÜÊ­j\x½¹Ð E´±y>;CëÖÐlÍ‘K¨<îbÂya±ZI|reþÞ/î³Lv‰£š†¶ea¿‘P\[¶zªƒ¶3A›áþn͆ï+ššƒ/qd¿MX›$b‚þÍÊ¡ Âë ë!iSÈ7Z¹ÿ®ÏY¨Þy Ô" ýGžuF=Ö'C|ÙŸ¹øÜ¢{†Ø‰…Ø?k· 9­Ú^¸hÍvA`­³Ú¤ÛðÈh e>ßû<*Ÿ¸X%ÉoCÿr<ÑYKûÙ1Íç8ÒÇT«,,  ^] ×’÷ØhX¹,,m\ÜÚZÓµËÙÇš'x§£ÍþÌÓ¯´Ù’^ƒà€ó¤$D³ zÜ%GÁÜh°êJ„®iY” þÍ4ðm-œ»ó޽`æ€-¾á ¸q¨htÇv°œØx*•$£Ó.skŠÌtIÔ‚*Å%G[GSµÌ;Ó¥¬ÓOëÌœ)oî V’|n¡,çÏRt‘QÈ7)MHr:1úì^—bp‡?óýff—Fækªj½óbï¬ W÷Ä¡þùoÍæ]oÀ:Ñ»Eªkwº¬–B¬‹ÊbyÀ-@1ˆPFÇ)ŸœWR'çÇ=-ŽWý·Õ™g›ó”î So8¯pGYfþt•;Ê€"¨peîµÎX@óå|Uj¦gÖ 6‡'y-ê;c Û£²ªó§µÐÚ_«+s­­²}…*B™lx­ªŒ„³"?Íó„Dýe/^ ËßëXKÎ"€¼MÑ®¼ÁXP%lQñkøD}Ö'†çùý97yänîb¬)b‹Ë´; ½ÜN½¦%ïá=™±Î' ºXÝ£íñ7¦f³è»ŒžßòAóByì$ÜM:¤q{:ÇUÕê™ ­À¨‡õÎÃDÞÙK—*ô±§Ç·EdvûQoim’hõ*bæîžB½]VD5ÆŽ“I¦{]ØeM¥yÓ4($“ÅÄ4ÉOsÛ:ß{{þÖIý—8I“[Óçò¬érB¹‘d3>HPDÚÙŒ×ÎîáÕ´¯» ãuò:ÿ<Ȉ¿’b(”6=éf%Q¬ðR·Ëø‚ÐK•˜4¿¸d†ÝKgQ4] WÎ!3c¾Ùí$lw ¿YVÂê}à+M<€—SÝ÷ã¥Z¹‹èbõp ÝØïu¯ëïtÈ~Ĺú…Å46Ð ¹&_™,EÒƒÚjR¸ Û÷¢gbÚ‰r§ÑFå[ÙµèØÐ¬},‘c;ÇŠ‚ʯ"è¤#’üd÷µµýB’%7¼±Äð±ˆ?8Þ(;5e³&ÇŒ°šëÐʥȎGRè r)´&ÁøŠãñŸ…fã³HŠÞ€¤?áÙæÏòo™ˆ>”]rK6ùøÃf7U§M™¿ÅÖüg)FÔ:¿ž-ãðó1ÇŽ›‚T™2`»ð¢ŒX"¤5oÀìÝÝ"~Qžh³xoj®°±À#YŽ'ÏZ²RµªèÜk¸‰Á§™ŠŒ3ˆÎŸÙU;Q‰ð€ W½ÓF¼|¼ˆFwõ³¯²íÀZæ´O¯nÜÞr°Ày Æl‹ÉB¨ÚÄ?ò(þJo_!Þv8„ê"¼Ï[Yîymýv¢-â¢åj wì˜xÆ{ÙÊÄÝ)Têv êóGòlæêö•šR9¾.,cØèÿ‘yªØœeÅš^$øó¼BEök'P‰Ya¬`7áí°Ý`†<Áãàš“è0À?8¨=áÉRs©w,üY±™Ôæ³×cÊùÑ¿ã8ž4oïûw4”YXÖüy0ÐVá'ãß¼TëÍ’]{¾Ö"fnUwá’TWïû¿-—X~÷ðˆß[SÎMšT×TD‘›4Mì²BýŽfòB]„&ªÒI¾ßd) Ͳ‰”äìþ›ÏÒÒGd·œ_=Ž4µËó“ÚDѪƒ3šòÂË-¶>zàr’cÿ6°ÏIŸ•õÅR…|g³úõ×´Ä,YR˜©+E} ‡=.Îf[*Å£¾G½›ž~‚žË!¯„쨵 Š1ës§EéŒ\C›DNßõðy½Þ¥Úaïatl0%w`Ä=\hCÏ¥¿Ê4a¹ÈÐØCM&ý­×ö:Š ê™ÒPí(]êñ%§æHM”™š4sb1¡Û1Œ5’ähU“ zdoCîÙïãVY!¡%Á님Cί×CŽËwQØT•» °õT‰ ScöNüÍÿ$Ý„=3­Â=1 ºñ@Εrm¼ªüðØÍoÉ÷šq;.À¹Š –âÊfRa“¶…Ã¥Ö˜ÿü­žg+øÃ*[_[8ÔáUq\õó—Ÿr»2«‚Œ«Ó5‘mŸYÚ‰öž‘;Ész™„A …íµ5ÕB*³>rË*qÏ¥Ü-¯ Ì¶/ T˜}~ôH=_/oDÎÇRÑ$CG¹Èls²|/¤ "Pjíìº pò¹f€p’‰CŠÿŒWŒS¦ï‘<ù¡P•n•G€³UºÁ¨åμ;ˆ…ˆý¹Nd‚VÃ@è5)S$…µ}½ a,°Ç·¤J'³ð²ö:róî)W³=êôêjm1¡ëÌÜ™/rÎó|Ìâóµs)xÕìè$ž±û÷PsxFgex$•9ˆ îü÷ƒÀ,‚¯‹Ìa:´ ý˜ÔÆ`zÏhÈ!ÁÐРb5w†r›)Óг&†ÊHç*÷zZ†Àú;ÚdU|­FF¨:C[’PºßœÖ3}·´’âÔÆEÈÎäz ­ìûkB`nÎpê¤Ëï™)ÅŠ)öóªºròM¨ÎXNkéVËÈ?–?ß­ÆZ»¦çü,økUñ§paJíhS0„ô3¹A °¬~Á¶qåö0݈µá72R”>]ž¢™Áû…‘Tªâþtžà>*ªïÞ¢¥¦n¦¼šKŸÎkèðéÚiÎÉ='ú¼MüE‘žþ9@ ³K(Ûžò:5²HÆÁÙlPµiˆ-êXËÆJÄ["¿MƒÑI´;¼w9þ®ä…³È{ŠÅØWå&ˆjÆÞ&ºVÔ¼!&ŠWÂòæ[ÕX`èøN¼þ€¨›o188Ý¡øÓ3Q€¨·ÐGØm‹ì½û2ß‹‰ûÓ÷ùþôݧ1Uû¹)ªÓéçÊ‹Âíëì|Ž –¡!y}îY<œ<ÅO>” äl`ôœÇ_· {¢U^¼¼ Aí~O/ç¹Çò“JQωŒ±9ÐDJä·f ®‘KL³±ha‚Àɪy¤Ü=QvF[H¬µêqõî4fs¦‡éÖ“*ÏÀõ‰–ØÄF`Xé-WÛK ͽ㛴;­ÅTI‰º¾Ò^ýÛ°ŠÂjh!ôš¨.ä:àvÂËÅTð<_‹° Mjö݃%ʼì…{qG57z  Lº—¢ †ÐåÜÅtjâ§.ùø7ý^WQüÚ&§´’ÅÍ´Á<J®_„á¶{36}SÓ.ðÿÄÛ»=rþI*·j*ó&ÒJ|íõ¥ƒ–N­¸‹Šã¨qPl#$ù…÷‰'¶\˜xTHÒ´íGX¹m§èµQ¥†aã3Ò#t/ž;«Ð[¯cä¯ÿäž‚#êÆøZ)„뤪 ±•nÎÚ!Æ4›×¾–À08éCeââ&€…Ͳ +:§öaÚ…©žýkÌ[Bèÿ[I_>¡¡ÈÚd'Ng“YN…®²fØ‹M‰&±ØòÄ‘¿ôpª¶ŒDÎâ…Iµ¹šÖÑ*›SZ>š?ZÓlOºÚUŽ»c+Š’÷ÛlR¬¸ýahÊMMUÂoT/º»šqº2çEèÆâƒ–ª–;¹ŸáV­½›ÄÒNrQ¯àÊóùh6)†…-g·Ť7isÁ½=÷l!Ç>µk¥#•í"¾÷w=^tªh˜ÄÃÝò’Íi À5 ~ì2Å?ŸX¶Ñ¨Ü“áú«HGƒÅ¿ZmÏ9Þ‚õHA“€÷`Z§ÝêÛÛíÊrêû¨uî€FU†câ S”v-ËfÌêú«ð ù¥U2’ƒ›Ý|ÃÁTzj@S`̦n°/¤]'y–ÍP‰„똘ùµ?ûÖÓ\ˆ´kÒ~p¡ŠÑÉ£Á¤lý7¯ q"@ {sAêªÓ õp®‘Ö†a:Ö¯j‰Ô¼V*Åfô¢ìž¢4ñ7LÌ3˜—AH• ¢¾Äª—$ïø~0\ãÜ®UP•œ¾j'(hàêhëwq_oÓÝ ’®¦=¬I-%غ)%"}ÖöÐi=´¬U«)=«1,AÑɇaz…I™/¢Ç#S[X£5 á(ýD>9—…(ÙíÂ<çG ªZÉÊr1áè±³·âv{eEîa¼à©‹”&§ /^…]'zo9’ߺ'^ sÑ7k~ zÔéúÆ"«<Œ.8Þš»ßÞ‚y\T`ïÎÊ›õz%ÿ0Ñ] ÷,ùÏ|½.H*l‘¤M7Êè­šx™â‡]ZÝ÷]j–L³9¾ó=ƒ5ÇÎ"ª·„M¦7£~˜NŸ B„,ñZ)ŠÄ£)?"Wp·½<ö¥ KfèY'<;Êo‹,j½f Ð9IÀÐDÃâÍÚ8ïEp•ÑjšóËvi;!LÚÒ+‚“®á—`,„ dý›w9g^NŒ3ýÔí«ùðÙ {R’æ’göðˆë¡Ôï3xGìz2ùSmglˆ˜nI·çÏG&g½¨«Âý qÐÌ¢Oß;O‡ŸbH+µd!›0Å|:ÂHþ½Ë²›ÅžLws߈DQC]¯õx¤Eú ¶ÅÌ}†AŒ,wU¡¶M-LÆ.“Ð$@_¬Ãm-F ð|:R‚·ÏÊP=Å.)C¨"MâP_ŸßZZÃ=ß(q% OÐÌKÆõyþý žörõµf•µ®ïÞëœøé>@öuÕøw2Û£Œ*}ò§öóH’¡ïÓÁB0QøÔCª1SsëÜLZFÍ0ü/ÖWJ$CÓ/¯ˆ£‹fÓcÁÊ™|öJÏWÿ ›|ú?]¨\éV¿„Š]\TÇ<öCutáó?Š(FÉÐÒ5›ñ£å_ÐîË{É€‰Ïð^Ôoœü<šVÊÈo‚!% ÔÙÝLMH×.e¦ÌøÓÅÁÚYµ·´öxÅ5×$æ™êË#omCaÅ Ve¯Šp~ÄpðçTË7°œâéy«û÷¿jˆsç ÛZ!ïi#§p‰û|wói…囡fN¢Ýõ‘Ï•Ñ\/~äÑ@ýHaŸXÑbÖ;œúŽ$fÙaãg-Çs!,$MA˜¹ûhݸÚ"g«^ÂJ  6÷ƒ%˜RÁŸ}Å!<Ì'G¾Ç)·ó u´2¾¥ÌJð`h.Æ"s½Îï3ÝB5 q'¿rõ3ƒL¼u1ÙÂ8õ»]v¦W»\ÅìøÍ õÖw~37þYìØöõWÞCY†O(¯×ÕjŽeð;\{0Wüьݟ}Ùᚸ*“ MZÞwÜø¤dˆ)AêO†vKùfœýµâ'0"â&‚?(Ô•²%¢×ƒuÈW¶ÃbY>›N¥´HJœ®FZ›ôN¨ÒŠÂAw…h(µ$)¤U|%ÕO»ö'¨¶Ù™ÁÑ,ȯܕÂñdMÖ!bãcpá7dšƒaEùuHÍBu˜’)Z iü²õÒ]ÈíÚ§RÒýŠÊèWû4¸PoH_Ð̆þAx“nb“¦{w9a>“‡L¾ÏF¯èßœ–vüŒín’ß%ÖxÎTJ#5ßÚwdò¹˜“â’7}oKT,í?Ú›Ú°æ«eÇŸaË%ª49ãç-3æ|m._­¡©NÞ#Ò{Y6YdVDé_`>ë$‡‹÷YÁóhbx–q«vÔp›/IP÷M›k‹ùÛœÎ{Ê,‹ÿt¿š>û›wœbY /`ÝîÆaÍl‰8÷ 7?#˜;^ƒ·beÛÏ$þJåV{)q°A"C:g‹iíÝ×+ªãA,í mö¿E Wç]–êhĺ”Î;´ãb%Î=ŸhûXþpѱ Rø<–£§·£c'z.4ÑQ{’=Òñø äˆ*½uнS\h¸*];ÌÄ\(ùÁ,à ¥ÐØ-ô+£^I£±f1ùÝH¤{€o!XL2ΞKJº¼ò ³ëBÄ4-­›ÂŸÞ•mPÊTêm lV¹k´ ¯_€Ôx±d?ª:ôê uYÅÑ ”˜ÀL«”QÍâ¢ëa;N9\c~ kEAÇÁâ«âui»õ*š±M鲨ÀM;TF¶Q¨©¢åˆ´îNRkŒ2À´Ë{ÍÓÛ%ŒÊõ“~Í4‚Z’ñ5´!ZçÖ^ÞÔó”Àé0Ñ…›4BÁS© W"$P¡ÊýÓ8Q2¬8ƯE%ë’·Ýû a·zð‘Rdͱˆš3R® ÁC³ž÷$Öç-âbó¤yw-=숢i%VëÛ.fÅ’Âz8ëI .zQãgra‚ÁõÛhmôÖ¸v¶lç6C!ÃØs,Èû;–²åoÎlt‚ ÷Åxû(dÊ¥ ×_ó͢™™lÒ·¥t¿,åúw×hÕ”šIÈóô°» y‚JãaèTm üÂÕµÌR¼qC‘[ÏRéOï÷nn¥ ‘”‘æY¥h–o‹ž Ã7Ш£ T¤ÌqÈâp|Ïp GFÐßã x¤Ï5gNÂ[fì‹û¦‰÷EPñ°¦¯"=Ëü¼Ja™—s¶=ŽÙ¬ú°Õ~„ü¥kÿ®æß`¹¶U®]eâÂÞ‰7ÞL>±Ä€%X7á¢çM¦qA6ú{¸3_Ãõèx¸s"ž4ÊFn4:pĆm‡¿&·J¥*K†˜:ðC‡šK—ÛC¡Øelî¬@sãZõjH÷eïUÁ’ ‚–ÇÛñyºßi ÿ]OŠè XÆt©d@fO¬w>®˜ÿ=æ÷\Ju¨ÊšòìÕëòàˆ~V© $°Ò¿eR¢w|íÁîR‰’µ_æˆ;í5BpŒ‘U±˜žÔrrÌiåá~ðŒË=[g $*nü üÉL‡1/ >ªÓày†ÂÂ>©/;4G{mA[V4¿‹·C¦(¼öLK*ˆË68|Ln°˜Hˆ‚fÙCS±¦Òƒ‰Wª;Õ ¹ûþ2ŽmŒåPFìM=Q‚‚–âräÍmåzŸWÛ‚‘Hý³G²ffyäÐú—®®³&à £JüMw™$vŸ…J-$Á~‘pî$ ³úJC¢Ý™(òëA»"µ©,=DÐO)®j²À:HA?Ð7ÖŠ)ôÉWÎeÍîON¯áݶóõž;ªNfƒ]tàEƒÜ˜8‰Ùãq¿ø ‘«UÉýUì4 ºJÎåXò5Ù+$] •70cmgMšˆ? õÇE^Ÿ¸._¼k€ò®n5Ô‡‘Ì£é§C½Ìݱy )îÒ@¡4£‘MŠæ€%Á_ë‰7½8Ê5Ì£æ¢\¯ã1÷4Í|‚VÒ ñö±>t‚OÿM 7x[ÆþË Ê}ùz¬áãÑÖ -„ŽËZE)CtÕÝY£«X(c ìPüBæ6äíìºtX©U:R² ¹jÞ?å½àøþA3ÖÄß•Â*ï5åœL;%ѧ7"“E;p¹œ/Ö´©â'ÔÆdR¤“ÇíÈ’Æ;¦ŽFqw‡íGÍ¡F,…¬‰z·»ÙRåJhg ÿ={V«Àn(Ó®ðçW²£›ûH˦‚¥UØ-óYU æ•0g5·vc?ªfPçùõ;\_—ùªo÷1ŠÖIºa0þ…P’Ï_oÄßVö3ž«XÞ ”óQ 8²›smR evÝ^\ûOä'f]b j9uu ú!ÀÓlwEbÊÚSþß/45J8^ÕyCïšl—7Îü¼Ñèé¿X”YÙ¥åçÏ’Ø ³‚˜!R‰6ç(¿7.½…Ÿ€ÔüúŸÛõ{nûÐ=WæI ´UÂç)ŠO Ù8¾gÓ,œžrÙ"°A« W仞oîü³V˜ódDrœL&#óu…ƒ<Ò¶À-»©ô‰XÇ‚fŒƒn4—J«GO°í- G¼>Šs,Du?Ñ—ɶÿxüf™©±Æx•Ž£,›^$™^SmîLf’o­ë ³øͤô Ù•b}ÕèB‰Î,.Eë¡ùCÿÛö»í’Ç]…¡k´€¤™!°q SÏçóER‹«.¯ -ž|Ý2;”$BÆuµR¸—£IÚâ׆a( (d¿}µ&?rKÿ¨ú’£5¶iáR y¢¹¿q,@œ 0àóa¬ª¨ÒO`»åü™Ð°'ôRN_=ì©‘Ô]3¶Ñ^õEÕ¯}¼ÃÄ]Õqç´ØW°¦3®O üÆÉio[Fè+Î¥wêkžb›ãºèš\KI8íAúÞ­?ò[hÅ.ƒ~È çÒ>ð¼Ù9Ô»@=C6/­öð¶s{š`87yç=bo¶g‡qÀü fôØû#ŒK~ãÌ^´ÿ„*.ÅêDƒ¨/bC-'ßC—.)j\aµÂe{Á³5|]B)øƒ†Bç/ ㈋éd¿34x7¥2í jÛ“)h>„‰²ÁeðÐé6šÒ1Ÿ“Ñœ±#s«¾ÞEËSŒÑmrv±CÃõÏäi¸5ä´ >G À›Êdý°¯~)Ãv°› ™"JŠ Æž¯ï·QÇæ%%0@ 9óV± J†PÄnzóˆi¥DYºˆ òŽ­–½³æ`öPN<¤r#–9qQ\HD ¡ƒDìtJaÒ{¤}E³®4l(º3LÄëÃ÷°ÀÌÚ[× #ìÝR˜å—Ù7Šââ}?ÇN§ýÉž\}Äg¨ ÖØ,„× kÍ¥p,M.­ñ<ìTå6¨°g²£ró+”%êzÀô0. ŸU­² OgÂ=Œ»í"è5¦‰êÒ` i§\_}ó »2Ú‹´¿¹‡Ú[ã7ÿ-Q¿²lz #Cò½G\ð8ÅX‡.Ðùj ö]Y¾tt׊ù6ZI³¡Oé}Ëë U`ÈŠâ þÉ:çÀIMçèng…3><ïØÎlEfŽÂ”Æ+Ç·…Αt–ä …Š>´;9§èЮû–§Œr:|¢Çåþ-¦s4aF%¦Ù8àã^”êdÕçO#£r¿¾VV1d´âªKz…¢oOìy6ÉÂÈã"I©8ãåB‡œ1$ã qBºæUÛF»°.BïÎ ÅÉ©oq_‹+¹D~tvp¡±®³çGNF÷´VH]w)p§fèO$ÅëG|°;² 8Ë\eCË^³VÅ×òxm+B`ØZ(ÊÒH…Ü‚.7Wšf‚ü•gÑ,-P„' ʼ7Á²ô’õúQGïñ¼5òÚ¸w ­‘ºsPÒ`þ«­¯Zót®eiÅüü{ò›+ÍkVê¯ds7a®Ü–Gê•âró™þ)/Ù#;óã;ÕëB 鬠÷æ*¶ÓàUôÙ½}•;ñ¹?Z8ÄB-V5÷³ŠÓðãõ‰‰e]j7M‚•¾ÏB£‰ü|c#ZWþVÑ„?ÍÑW4MÒgrGxÒ{{þNˆ»;1:A4ºÎÛbD¸fvÚ>ÙÏx-ÛDAsT*cLÁyúm‹?*§îo¸rº_Êóy–š %‘¯É(Ö…A…Ry,ýê‚3êØ×'—‰ÀäÊô«ÐËà3!õ¥‚ùÑ\«±O¢7¢'M^&e}‡æy65MNzíFg¹;ÄX&ûÔ¾ú:ΡDýo)CôÉèwà҉ݘLÞüˆ¿C½*L©ú­Ib+¬Ôír(á]Mæÿþꘈþ›%_Mg ÿ‰í5o"… ¶^,ûB€öK•Mñ†÷¾ ¯3ë^h¹]x~+Îg°âL‡®Ø¸øïÈ×­ýŸò.?æ®ËzììÑH–!‡OS“èrŒâ¯ˆÏÃö,”F†Âýø<àCJZfÅŒŽ¬bšžª ëo¼´$Äm_Ä›Ø;W¯'5=`9VŸî–¼L)ÝÈäWè!§¤Ý¦pO(ôË´6÷¦:¼a'8ÕÇðÚ`óX—eŸF‘P,føób²ZãA—€)¼9f>2[ –R&ÄÁˆÎº\EX#øPúñþ^Ö¬¬¦»Xe•Ò6ùq¶Æ#…©‰t/:_ê¤l,rÅ*£G@Cqœ†βž(BU{qùþ²G]\k.[Ên¼”³™'ÍHO$ɲt}ºE}'bXPp ¿i4È &”ëJC¿ji:Q rñäاk„,v³‡:ìlÏ%ÄC‚¾Öî>”!£R‰ZˆïêU³=—!]Ä6v±Ï_ÝüѧýF!±ÀmD7Öæ¥ÎZÎ~>•·™zlX‘¨Ê~«“$.okPfÆaN×b2 ¯-êt¯8̆¯g~/Çè ”5ÄÍìQÎÿ–ÙKúu'À£Zåµr§5(¤NŠh“ZÁyWûWè¤ô³ yö—!fd3ï²2fŠ}ôà{„ VXà=_ýe2§%)»—ñÀòé²­³¿t>L£õ>]Zíö«žíÄÞÆËZé„D•ÕÇ8Cî™í#€,‚O_º)ßkiŒ3¿ÓowÚcð¤¦â<˜ñà(au¿ê¤«<¹­Îä…" 0ï𣔠AI›Á¹þ³;M>zŒÞsÀžA$¤£®Ê•oRAeÉCšáʄ߹Çq … áRE>IÂ4ˆ4±‚•&@*zkB‡¶ÃÕhWQB’—â=ÖGΠ±âù"ø¡–¸f_µÓûdJ»©Cô¦yº²¶ÜäÓ)ªiŽæãÇz{^4mÌí©?¦D"ÎÈ/ÜoC[鶇½HnþÖÞ[TÙË_ôÅZ(4ºŒ¶òm™Ön±6LËù)BwQäÜû 4Å@Z5[\8§ðÅ߉÷Ù= ъ׾f£ïmO;i8\.ÌПZ¯±_¦@ëæåsqn•­zâøî$ÆU³æÅÖC=Fiìz(È4ööÄ¥`DË6qÕúöÍæ–SH–÷©vÃ-“%J«Ÿ¯$¶ ©$ܧZ/ÛçÙtHuüË þ£‚{gUܹ%L`mê%€@ÇÙIíîÌ:ÆbˆÏ¼ýSŒÝŠzyš!kô?„âžiÜï5ýP™¶€ÜéZ•å÷QŠ”¿‘Þ…ÐíÛIèw/ðæ~á”;ùT€(™ï<¢S®R* ùÐ0ÂÓ°ÉÕÊbp¶™*·aV “âB?#8‡÷"ƒu ëÙ¼ÌéÐÝY‰˜‹fY‹–‡vÀÝL¥ûÅÞ$)ì >z÷î“¢æp&všO K1;I:¡Ž®¼†ž.´4Û÷,ËôE­”o5¨ƒ¿ßQ“¤K³CV埛¸Ž„GC X™7ÇpÖGœ{B_ýÇîµýsÄ]‘ Íe><æ<_qÚ?BcMrÞõT•{i«}Æ鄪Þ6Qwľi~ÛZƒ\q±ØÇiŒÀòœ`.×GÊY0(ºúR!Åo ySïŸÞHÙR´²ó9Y.n¨å›jæì¥œwåÈ´|c+Ʊ¡´’&1*¿ò»žÉÇ!í]ÕÁ¤”vÆœ®Pݾ ^¹(øÔÙRè¥p©ÇãEü†ÙL¨³“2²/,CÃÖÌÖÁ¬öÙp`a–[6E®2B[ï,µ®gNæ²±½_&¡ç'ü¿¼ƒ„ ){Í)â•É¾ë© IU'KóI«pÊÀ…ÀvÁžÐtARP)¦%ôWe8C ]Î#éD9jIùÙp§¶¬÷š ˜M”›µRNðú)Þ©ÄoVë½éf éòãå€Íøé¹êÏ<0æ–½Óäbü\Ÿyž5ÂÿäZiBÏßpŒ°¤eÐEçYnûA9ËlçÔ\kƒ!$Ø•HŒ#Ö4ÃãáîP\¨¤´:ާlÌ®¸—Ñ`ñ&ײ ’IßÖb ƒï©" rö@K¦6Û¦ˆ‡Öi’ Xn%Y³kÏ+ÒýªöuÛ]zÆõ›€Ü'%{Àzi¿¸i–S±’¼þ«ÂÓH툈'Ü/çà1éMN`ç.‘Ø-¾súm–¦V×úÇl²B¤4ɾÌ)«.©ùüÆð ÝÛÀãÖê™/ø«s^eÚùÙ‰_\LÊ?X¯fÖ 8„gßÌzM0Èðü^)¼AKðkQ!’„íE\šSW*ÙÙѦâùS¼õYÁ)‚‡VmKÐv묶4Z *Þï–åS(d*“ž\ºbÍ"!Öî(ÑPƒä­ùI+ÇÅëî¶í*^(ðýŠ;T5õ`áÛï¯DóÚ÷sa!ÞÝ^œJëç7UbZÑiËBþа€à ÔJžeŒÖ7ÑafØqiKV¯Y(·ÒlÃ`#8ÇOñ;kà-ü»ÛoÙmÃÛ½‚ŸÚreè¬ð® Ç ŽRú£Qg¤ÇÀÇá‘òV 4ÿZr‘Úq6’šµ6ôÖ(PÅ×"á<õº(n$ì½P„7ÚÚ símoBñ-h–±ÈÙ9k$pR*йÀfáR|½“…_Æœòk¨â¦¯ÓdÌÌVäñz;[Íÿ ×`ûBMÞga××"Û‚þ4Û¢NJ®Ÿ¯À$–iÛjD™¤;´ž ÇøÑÇ=“‡kÆWX¦‰ho?&;ÀÈ6we¢‡³ðiIÐ%ZƒÂså‹ÇSº©MÚ„óÀ´â™0°Yk„mqâ-|2Ýj%¯îvèË=PM-%Q.òÏ¡};äꎧåºÅŸ§;H ¦}NÃ+ ‡êÌ ¤NŒ:¿LÁƒ$_s<mº yÈDEM”=Ê»¥nò†Ãbí-l4õÈimÇ`™ÅT}ÀÏ¥€oøÈfž²Þ„·ΕE /ÕÚ5–>Ÿ!”Á´†Bòó• E!Á Ý ü ‘UKiúùïz€úN«£»Tl5wèzÞ“Ôãû´‹ëç¨æhÀµLjçqw>†‡\¤üÆG]7x0¸#JŠUÔDÒ;_!(9Q¶ÆôwuËÕž~½r4ØäYmÚŸ67ç6./ƒä zßëŒß§lêÀj*ÛrçË’ªÇ&¼¨õ7Ú~w?ÇüºÀ5'3}ÈÖ$€Õaê«Wp¾×c³DÆ QòVh·ˆbQUòœ¦Ö«ãOtÞv¶­² ¬æá¾1[`ªÉ3"kÌå2$¼P@»wÒˆÕkÑTókM©G)Œârîhë Ø!ø¹•‚c[Ã!ÂÅÜ/:ò‰fKÍ.ŸßçB;¦'‰ŠNT⿦¼ô"ɤ¤kËBx†pH¥Ä‰¶²(“îBœßHª‹c›Oõh}Ô5àîéW70ÛçømDúeaòÓ+­¦]¿ ë©ÞóÆ?¡×‡i ÊZ^ü%蟔)d¯r‡…ª:¿Z ¨ß/pó[ÜîÚëQMDPÚ x7ÿ7huc‡ÅÕßå¢63‰ª´âÙ¿O'ájþJÃX5ëF›HWQ—. ”p'¼ªCAJÆÔBÉïr³¨êIV ZÃðÏŒjc¿8§cò aYÑUó&{V1äËp8KZ»e…( ®~ïÆ\}(½Ê-2O^žMÍàì;º›võx˶XQ&õ0+r­Uw<|¯ci —NqG_×Çi ¤Hd ¤À¢ä cá9*‰Ù£jü, `²†É~‰O©aTô¶ÜO"phfg˜—(&ô‡b¹[‡`F)³âÌšMLç?PïÐ$‘zðzÉî‰H™¼.=Eòµ[ Z²©oèl™Ñs59Áð:­Ó‡Úu@úðpùGìdçIC}ÿÌÈÖxM‹Ív”䵤ÖåòÖïÛ&D¹ž~T,»nP”I{Á•6^€¥”é J‹OÈ{VWðÿ"w|mΣ{8îSä)ðLïhYÎðÍXv7ª¾—8i–ˆ¡á²»¼žñʰ#òöú°îi?8T–C§aJ½3‚º«Î ¤ä·“X_ôÏg íŽö2õŒŸÉ©Žä¢t?z5ÇL…K<7`c"ûÿñJró¶ØÁ ä딡/R Ç$Ë€\ž(1ÓM&ÅüN¤Gfk‚èNÆEM?#3•G¾¼«2ï>5—ƒ³RT­Ð  áKwÊDJ}qSï¥e)p¥Éö$añãB-x2c×ix¹!ªñH˨±HÑçÓJõº¥Ý.cLˆ%ŒƒÐØ{„ŒbÝw8 IW ¦[­*ÍrÍé7U`¨¢^ÜR)!T¬ú[Âã‡¥ß %Ý“Ònâ µUâE Ú|ˆº´ {#SLâüÑë;ý—ä4›ç}˜u¦á-–$”¼«Xª¥mø*¨'­¶L\ÿèi^4×/ˆ¢¿n‹ƒ±6´ÿð%k¡ŽAf7[Ðá‹·'n‘žœÀr3´ŸüqÜfòú‘í0Ÿ­õÍ•néî5¸®’yùÇÅ.pã‡!Fb¢üõw.¨Ýƒ"l%Ã&CC«—Ù¨ˆÌ€´/ÌÃVÀªó'}Þð3æµaùñ €xASö÷/€ñ×™‚«h ûYjð \}cˆø[“<Ý댒Ùs„õŸ7¼ìŒ†žÖ>ÀO¢«àF£®—U~€T±É;L,ãKûãÂ.+›2£üäBQ›OCÊÂL#¯£!ÊîÒ¢<¶lÓÉËM˜È‘¹§òŠÜRGöy&Ã>°5J}[1]Ÿ¯ÛO Gl ¦ó#‘°zèZ£í¢¯¾9ˆ`%WªŠ¶IÈ“Q‘rÚjÌÀ¦ðð4qa§‚uu ¹—›æ€´³ÕÖ6àà]J°¿õ§ˆ±¾ŠBF©ŽØ* §#ÏÝøvÐÍ/º ÂÖ!jû-¨Ÿ+˜b¡$E6ç‘FUcUSíéÌÒ}èΨaMQ…Ð-êØÿ7—AGjXƒÄ†„:ÛÈCŒüu—ûq—ƒñ¼ÆqÅÝÆl 7ä×G¾Áp”!ðÉÚê‹!Ç¡Ýè¼íúeü®ñºpºû,•ÄdOtŽ÷ªSI‹ì19*‡¥—!›v…þ$`¥ß¯¸èLtdÝZï jƒÖiÉCÇ8÷÷…C~Ôù.®¯>¤ê¼œ,eœ8®œd5ÇÓàíQÀ ?É::öÇοO©çØAÃÚn)rÑ”sØ8ƒ-(Wþ/ò‹G¾HBùg»q¢¥©eèxr`\¶ Ñ)À«‘ÛIÞâ—ÑÙЭ÷l~œKÃPµª-® é¼I”¬¢lâѱ٧—«éÅŽsÁKœ=kó =CZ?÷JZéÒžÀ©ƒwºt;úô›hC7WñÉ<µ{JvWˆºýù-YWS¤ƒ´…L[¾6‹‡?øùp6›vÜÁJã©ÞŽWœöeþ@tâò| ¡´n¸Ÿñäíº“žBù=¹uí›ðNˆî¹ÉT¦¶0-,¯ãp›3|'ñ“R\ c]º})iVCª5W*»]z$Ú±oÙДTÅ6¬0)žÒ«—’N4»[”_ÒXó&¦½?p²€ç[8×IOö>ýO|=©‹%¸ÙÓU¾,e{“ìÁ\Ì‚„œ\ê–ô$q¬[CE¸Kàï¶óN‰¼’'Éf :%?ÀnëÑã­mì0=»]äi·“:ûð¿jT»qk•#\’…CnXhçVx'ºpwFã[¶"¹>:¿¸aétDVÆì^°Î ‹{žÅ’ð—i)­lapžö¨‰™þ±TlÒÉ¾Ì JÐE¡W,F‰C®]ØÔŒZß°’—¯XÒ%Y¿¦ìJÝÔ[àê,³U¥ ó í4™½€p²ƒÎpnüZ²)UTúìÿ85eܲ¥–¶`ñÅ"Ïm7çLRL÷ÈIü#Új]µ’ÙÈAÇ…”ï|Œhü»r-bQÃØ°Õ3Lõ‹%IÍu51}Z3Ñ–÷Äšl+ØW±ö‡†M¦W, ™˜ +´îRj.®$fÆ@ËI '°½´E¥Ùµéïö“Ò>ìG½%{`¨Cªj05Óº`I£¹¨(Z,DÜ6Â6'/ísKDí>Yçi ùí¯ÐÓàR0^[‰FEÞ¯r)¥NHkY†Ð©nöÖœ;wä‹.1ìò5Y5¬¶£nkkLšÛ`XA³ò¬p^ÏïÆ+Ñ"Œr\¦Á7Iö:ð–©ÓòºËÐf ŠðNŒà1YZQi×·ýÈ øÄ¯UœƆ,†%úŒ©ƒñ*û0 ’F2¡WEÙ&¡Í³¶f£‚ )ÕÃZ¨¥ƒƒ ™mÜt©{—½ŒÎ;Rc§Œy¸¡•Œ®áYŽ Jå …‡*® 0¹euPDÝXÍš…8Ìä¤ÏªLÊÕCÛçðig¬WÒ^M-„²p”žOU• ˆt©ÎëaÔnJ.ÈuÉõÝÆôYjH_«ÝµaÑDAÇ]w¤´ÏKäÄxÙbåo¥à³‡6®Ðʲ`ƒË®¹8ò†ê‘Ní8ÒèeöÕzœYcF6²h{@±r™&z c‰°¨Î0åî6ŒõA†ž:éÌ{` ;f¶Ëq¤¥$ý˜³Ù¥~¹-¡jË{qü„-h ¼4‡ÈŒfï1×-½6hÂ…±lÜm…5ÈŸÞÝÿxÿ»0‚TrËpœxk ï?¿xû(wcnEÒ$³…ª_éîÇYO9ÊÁ2âÚ‰æÓ°1ÈìûZÖT(m\,-Žóƒ $:µ·´’jU–Öà¤g^éËBšr¨Æ~ÀšÑŸ+“R®v èÚÁIŒº¦BѶÇP¨m7,ÀèKʆ'‘O½‹OŒg[²´û†×·È®F¸ Y~ Fû±í)•ï½À³T<رÙFýç,hh¤VÖøiˆª$ÑÓª¨†¨JÖ’ ‘.¯4¹/<4 WÁ’žš¹k!þB–‰ì0¡ÿç¦Û®&íGmH­Ê³ãâÛÈ{¶dcx¨ à–é‡õ{’Éô¨Í>;a€§çé¥z6Ô^êȹ¶ëâ†üŠ}Ó9¡ò«jüB·Ò4x Qn%Th’¸Ô§õ=£¼„¤° {è°”Ï⮚þxQð*-UIÝÜö#»åÒ!`Kà¥[5?a5h–cÌ@@½¥¶`0 m¶ð½rö'þ±^hñ°KÜ@µLL&ĉû“G„y³ŠY‘Šåbî2Ù1* µ®¯kô²°¯áµ£ZnŸ¢D‰]³Úx˜ñ>•1þœ»´°æeíÄÆµgüê(¥ï ¢H™S/PÉÍoç,Gú 2òSöt˜÷×nd]Á¯Ñ"®k‚xáF‹?†s%±¶ûdѹKì–~döì:c¯sO9ÖM~²ÄÉþ1dsnk³Õ²ÒF_KyL …Çbˆ-‡¤ÑHýÕ7!¸Q&þX#W»I‘ýó3&ß;$€àŽS!]ëQ6Î «Æ@ª*gi¬7‰Œ‡V®!{YÆèû ÜlÜù ¸å„þ¬‘ë§K>á¾ì ?¤O£@»Æiöbìð±k¼ôŸ8þ“>ê½Ù'pôÞýüØDYmÀ•ù'Y]p¸£éu”³:3hŸH|ûzls¾Äb5hÝÏ£˜Eaë(UQ¯Þ58ë‚V½;¥lÓ¤©È=AKÚ[y&(þZ ‰M3ëyv¯êjº›ËLòî©ê3vO•Î#]ìI(å`õ)µ "aéZÄô(ÅñÈczj‡âM*l»"ªȪ°F¯3‰¾?û]lÙ˜C[Z÷Ú™¿;{ O Oâcé×(t®! ŠJç²Þêî6Ï5‰›uÝbsÜPŒw–‚<¬‚×o3YøÏiÜ$ÿvE98¦¬‹¼*Ýò'ɺú*ú«C™z“3ö¤–b†ç˜A/¿¼Ä8éeí!:¥ÊdÜžaÔQÑC°Rß>Íîý&B½Ó~㟊#Õô¸÷ßm5¢úûÒˆåU$rICLtóJB&gêôR¶+Ú`éÀŽšr9]|Jiý*Õ¸ f°H› yb±1þ¾vA‡tÔª=¼G´¾š¡ØôÆ#´LþØf0qM¶_^ãƒûXß" B<ˆ>÷nÝ`Å OÊÆÀ°NMt fzòtÍÍVÏOꧨr×Éw¡D,}-­ñMxë5çs™|SÿtÛ0ñ”>!æäxr4KÕÙµ r¼ßbwøžå÷T™uêr9²V¢ó쎮¨´ÒÝYíEcCë§þîÐÖG „OÃôbZÏÛ­o½:7{ô ¯ðˆq«;\¤è8_û1v© ~ÎÒƒ‘:ÔÅpåp¢ÍŽ ^M°°‰™vê$Ì\ç?–¨ÍÓŽš¹tßîø„(š±é÷,ÑAz¸cÂa_ÿèÕõSv@öA[Yä ·= Ýu¹¸x¬­È«á"e:ôÜdÅѵͅE)Õ \´69•äLÔÜY"FD»V5á[)Ó‘“-ytŒ© ~ì*ãäq£&LóÐé&ž¥~Z8z®…­ \ºýƒ„C†•=y°¬ó‡˜ ¨‘ d÷dÈŸ¼E²q*ÅÞøcóÈkÐN?Õ†8¼ïƒ-Œ0ÉßÈ0sÂ8›m()¥.’y¾˜f¨Ö#xг|àœé,¿Ð¤`’¿Ë«þÒ×Hª©ú MD޶Îc«FkØÿPÎwv$1+ä3n~ÁTóôüÖ¾áAº°]·q! ¡`æ³*€¼•¸hkS%¸áªAø§â&n¬Ùµ‘$ªÝ{–;—ß)à—Ü+ÜG@e`ò¸ÚÍN9 ±O¹a&ë—‘äfK$Îäê ëÅÿª_8·o5NI—ªB €51EF ÉJ*\ ÇrTò:ÇÚÏ‹HÁJ…'­Ø6i-<ðˆ’ÇžcÌ ì¢›˜ÃW7LÿÊ vû3—m| ŽìK$,`³ÖÀfDÜ0(ÀIš<¿A›JÕ ¦h©ÖwÚ GÐBD]ÃIšxíÚÔ=Ôa,¤Ôqå¤N ¾ @®âµÆuÛu<¹"&86ûVyO 8;LRŠ€èœ±‰¼Igþk„5êÏx²$å¾çPnlP±#Á‚QWQƒ‹p…þó=1“á¥v2Yü¶:ä%Ií¡í s640 .ER¶UãŽæD—DDÚšB?é¶D 't||žX\;È»‘ˬ3P%KfBv+7ÇÄèÊ”93Zf ^»Ê\{+F:&°‡;}ÊyO¢š*}FJ‰¼ˆ‹–›qÚ«’¬I·iö®¾ç¸€èòG0zåYƕܯ_»\£j{©+Ÿ0þÖkÙ)‘2ëè$zS×ÀqÓå1c@ƒáäJwíG¯{•4âÒPƒ7¤â7>k„ê4™X ½1Ï™ÞGÌ4>ç¬]yV†ÿm9Fö§¢´á8Žj6a;Р錯VCO°øÖAÜXôu£‚²Oᄹ°bØÚXuÒ’„y™œÀÊû ëÕ¯ÄËôÑÒóxy¡wÑëQk ‡Nª¥¥?´ ã7Sâé“a‡äÞQHhrG ¿TÆt[C†Õh[§¦%û[¢Î›)u‹m¥#¸•Ö|Ó˜ßMîi)d8ç ¾8t&ÇÖVÒv.ZË©„m.úŽI@t¡Gs¼Ê¯ü² ‚×÷(©Íî¹ÄÇ1çtO”Wõµuë‹ ÒYÀ [6\ª5I Q[´¢áíµÚ²ÖA§_6B7VÊô£¶ÔµR^¡L|f›ÛbU?šµÑà7ÖÚÙš=pmä›#N ¬cÛöí׊}âÜhþÓÞ¹²u†àF·ë);ÀKfŒ\d¼¾þœýýHiÖ$»ˆEª¸ªÚÙ±‚^ƒÆ¿'–Õ›}  §Õ@|:\Þâ«tTáWöA›{iûÑUkþpRµJ+‚êðÈ¡¡a´šäËq«>ü÷¾5™YMÚkÇ 6E9úoú-stÜ$½†ºù¾®[¥¤®Mî2üfHRos&,ç)7ØÛ 4ª‘²0t_Jg‘N€°HdŽÍŠŽ@¹ êër¯ CB/f㥑–*t›•Gà•wB1…îTNsÑT”ѱŕóbët%§s#ØIÛΉã•õÎÿ?$‰d `å)«€[“aH¤îËgëó›“x¼ø¢-é`„×¢ÐD{~UÁ ŽmªeM–Îö½ÐEä Ø…4=mïSWñ¯……ÏÍm#Ùý X‚Tša ya€¬„.:6ÚŒµåù^CØ•@qß º¬£–#,YàØÈ†›ØrA±$TVÉö÷Dg(¡¬!Ô¨8ß…–à ˜­ÈˆzTÏÈa~¯/ëf¼1Ð*“­¤ÌX(ÌÖêU<1WøÇ$Ìoc—2·Œö¶?Ñî‚;œuð2àY<ÊwuÿçÁae6ô[š^)…9ãC6ü§²±Óö(]ìõU*y¸‚N+{ Ÿó›=AðS>©×ê× en bPaè(#_-cc*Ý€Œ&‘Û¡ÇQ³xùÓ(èAÓ”( äÙ!ò,ôUk› ]jô>,bæº4õ°@&‰a¾Ví‰ؼçx›ä <Ÿ!FûÙŒmj‡ÎaöP|´oôPGm¨®dSˆNîGÍ…f³êUË­r% ¡ž–k¿Â=Ÿ§=Ô5~=Sù'c#©9Ég²°Ì³ÄZè=Èà(¼ÂýC)*¿qZªPpÓJ”X·Åú17ïäœÅ¾Í*a]dÆý¾ÆÜaõÖã©fðþ™ìbUa#ƒô|üʺ:eÒû0’ÂÑÑoéŽtY4 †ÚVìƒöoY£~­Õ#%µAá¹]‘¥‘à8Ò ¸/j­öÌÊ#fžeP´<‘`sõ<þéÜ]Ȇ&$·=[Ã15ËrVj~døÞÃí¶ñ.#òZ¯l; x×¥3¼ñýžOí ¸î•<7¨#ŒæÿCÙ·àð¤¤d¥òþ½CûoÖüÊA•Öù³Vö·]˸êj ÿPåvߦÛÍ×dtɲ;ê¥Ðþqc­ma-T&¦]ó^¿¶ÛQ¯Ó§¯'Ê<ªÜF¾Fu~¡YØ@eSx¦(‘>‹ž Kv×/v>Œó„õÏeØ©ï­tïÊüÂ`R4$WEýËü¦ Õ‹É<9—2YúÖŽ˜V¢ëd’[^ÿ̺ÉÜ5 AÊgtt‡l Na`T¿ò=sþë†y³²ÒûѪѫ°àZ„c ­d¿ÿL¹³Š''uëÒÓHbÈO,Ž>¤a’‘u¹Í†ƒ²ƒÌüž€qF³› Bš€ÂÆOßpQn~w:áõOHÆ}i·ê`˜®Øûƒ—zÆ¢ ê!)}²ΑIКî) àŒcú»éÁîÆñ${÷Ä»àÅ.0êsc¤v–£1ŠoØã/#Ús, ÁyÈ-ùçbØ`¾LK¬4ýX e–œ!þcïmbss½•'“ Þâfß6Kš‹óôÆ…ùK?Âáæ{\.êÚ»÷ éݹ±¯¤¡=ŸÀA,+åš»gQM- 0«G}(=áÐg}Æ“<<õ*ˆ–zåìÒ.´Ÿ Y*3—°¶­gÒl.¶ŽôGäLÈ¿<ìú6S¤µÿÇ}kÍ€ê¥Ä€lgîÏib5‚Í~´>I@Tšt&¾ÝÖ mÛW@%)©ºdp‡¥o–Z“QkÏ.'«#~+ŸaÌ‰ÕÆËogÑB¾èšøšVÿMÂie{ð¿Wñý{êÂzóûè[óÿñUf¬)N#õ`BWK·­88îæÌ  ÁÝÏqz’a¤íV¥ºy¯(6ÓÂ*Gq³ð…ƒ,ݰ–T ¦>¹P3Ðö Fâ\˜yß÷bÒÿc—øuù¦)ƒ„T;u[JÜ=ݹ1݈¢È·K™”!cÈÙ<aBÄ1Vö¤èâ­;Ò‘4›«Sš¨ü<ð+¸÷+ÇË•:¿'ÎqÊchCNÕßZMû'²¨Pb¹ 1¿²Ü»%êjP’$Ï#¢Œ\ô¬•HàCBa žŸ7Ò……¶Õ‘cí^?j¿)â ‹•/-*Œu+x¬ˆ(Fb%z^!ƒ€Ó]5flé,7Ê7ÁÚgv€¾cöyîMñUξlÝɳç¶sH‹Ë’*×ñ$-ÙO—‘Bmf¢ºAÅcÉžWeGÍ%â Å«ì"›C6…Ût–j³¸-ÏI¨Ù…ÈŽ 1À'o¹ž_³I¾%œÕé´[GÞ¼‡Œ|[Lˆëàqíÿ.$éÝZ´Ià èE’OÔTög“k2Pç1[‡”OròVfà'Ë–Y™Šµ„ˆ¶%+Õï]Ãa—Ë ¿ˆþS>úШ=t#¹Š%5ªG·é¥C­]Ç¡Àà;qÝÊÅ[–v ÿ/ÔÜŽù»n\D„¦^òRŒÓL«¿ßh°ÝéOï:IOòâ̱È-9i§Êòî_e5*E[h))*ÀªdV‘°¥¦]ð|ˆB&ŠñÍá&XסٗVëõxUÿä"]­>wZü÷ê’¿Í7ƒÂ_e…A®”Ò_^x¿S¶Ó ÁÕ@åE©¥ä˜Iú^®£ ñùZLM˜D9•¶ÿ„×t†e@ÃdrøÞb®JX£š:ËBņ9'ëX(ç_V´¢‡ö:™ˆJamwà_KÐêÕö‘¦'wãºK(@†»ÎêÞƒ93ä ¡4#Ã`ž†Æ‚Êíünç*M—ñ,lý‰©8lñ¦}Àm_”ƒÜD wrv®ÞŽð‰^—¶¹ &`™¢§’70±³©AC/™ùšÄQ§es8‘&à/EdU&ç­8uüu]½éܹ‹çÆNguÂñ3êg=ŒOzb!ïðؑϓ”‹ù_ˆß1Q°àILô!d®^óæ5³'àâ§üŽõ~º1µt>ëè©àßRUNâ-Iä ¹kS[ú±ÒwD¾èÉw/¹˜‘ÐÆ’"þY·’Æ•à‚ßWøh‹ÔõdxX­CDïкbô8à#PFÔð"±Û«gGòûe'`¥à‹ô[,w.©¡^vtqîÂÕû2;ÐyY»¥Û¦»"ðz;Ñr.0ö†'€öêicíBKõPóʶ4ì¤UœG%‘ÃÉÕNÄF.n¹$`J]%æüÌ^hžùÏxê²aeº+Ûé¹Â!OwãQu7œ#s¯˜.AYÿ/æUoÃeÅ`Êáñ£K[¾ Hz škkFØ~äÍþqHïu ýZG|Äã k ÒiÝífjE[Ó(©m‹>¤8P>é|m°#¶õ¶Ö£»fˆ6Ùó_o]4 s!¤ÍZ/ùB½R‡Ó·eq½ÙÀOÿ¾ãêkï'á¶ëVaâúE"ÔI ÒiîåÛ}q…Ê2òQ„ݮƌ©éèÁñ6Ù8Û.èåÿráþ¶lµ®qCë¦A’¼&ÛžEeìv¡†¨¬ïsw­m’yü‘”â„fÎf|8gìWd÷íÜU즊ÁŒÅ1Zp¹ŽqÏ'Q˜ôÛbvë½.7Øk ÆjÅ¥~Æ ÷€]®¥„,-€Ç2ÁîŽ8ÙYòÇw¡‰$®‚:ð亊cÝG¯Õ{6ÝB†"ï$òyÍi´ôá̈ç=ŠýçdþÈAFÎ’SßO‰é¢}æH¯`5CF¨TZˆ3|‰Ú*,¿Ø2“˜ á ˆ5+æcÎ×Å*š/ÌÕçÄý:?ø¿ÜÝ–g¼‰s .öËéx¬›ÔË”²368àbYý¿yQéí¹3>PQɲe§œx0 ïBîBó¾_´­ˆ³¢¶rû+Óá߸ëÇ£Ò´Y£»T­LU´£Âý²˜tc`ë~ò­\™{y¸›ÙD¡éÉCç6_ˆwBMÀ(ߨ‘‚ø›/æ:AX[æà™DˆQ¹½Nn%#ç$»Mp;ÆÌÜ CÏÉL²USv©Qd_ŠØ–-QZÏgÐ6Í…Ôÿ*Ý–¼RüYJ«o[2Œ ãa†­q¢ ÂE`öv¤ÙW=ˆ‚¼[ÆËöÚF‡ µ)l/¹S)rÒ’-–GÎÔ}£# ÁFÏTL¤Š9"#÷_©?…ÆVcRw¸?û0Äœ ‹ódï\tçíÜCæ…Þžn–§§Cè©0_„u—¥°ÅCx•Ùs©ÀŒ+çR» 'îž”^ë"®4D¦ð0´êGdLSl§fN¾ðh ¦÷w^s À–Ú»+վʣ]k€Õò(í¬ñ6pÅDúŠJ{ƒ !žÓà©î¾/²>³ raþj@-Ÿ/·›í#qï~/dðQ4or†kÉÉåÊP„›A}b&‚¯©zÂç¹N2÷åþãS#ïÍ! )þöýjïSAeÓûæ«*Ü›P­†û€ ;ÆÑ2P¤eo¼|HôRÙdñ×nk 'xÖÞV¨ã‰ï»Ç«¢BZÎ+nÀB¿þVYÈQoš0Ü¥ß åÅÄâ1Áª¡g–ô‰º½E·ˆÓx0›Ž'‡"ZóM(uÔæè>Ú~Äh…^¯c;¬=ÊpèkGâ&%þÈó*;¥¹Üã¹á%Ô•fw“—Nhdžˆ7m”RšW92ìshUÇxë¾vÅUí{ ÞsáÓÔa¯BÛ·!äÿ)SO}&¸ñ”4Í™~;ãYœ „ ‘ñ·¾²ÞX}eL“¸¸S4³z¶fôF`n²N8g/á/ Òé8>£Þ@÷^»WlØ)Üþž¹6qØîi ÔE#ªM•râƒðu¡U•Õ¼–!úK&ŸX,jІó‡ÄS¹= ÚCÊ3¯Å»UæG¦~ͲðOÉgF£?ÃRûíA¯Ê ®´çÍí×–%C¢Aš7¤!¸Â‹l#ub¸ksZ§¢¨á^µ€¸È9QµP´O•!Åfwýg[›üP”ÛêBÌ8÷‡=)ÌÝãú}·a~F—ä¼6Þ¯×,ì‚dœêOÒýóR±(,ãZÏÂJ?æ!†¬‚΋þ«›P`ˆÛœØ×(¸Î*z)<°,Éæ¦kîîm€dì`Ef9Ôœ[7eAí§¬É ?yŸB3 S“H¯¤Ö)~Û´â,„\g‹&¢§Ú)œY ”Ÿ8O$z+[òiˆ·gÀÁÿŽâ/•¯f»7{Û0ù~kA½­áz?ÚyŸ@õAañ·G¹E€u%ÿ^Kßù£H|Ê:KQ²…´Ï݈t}¼Q¡Ô·¤ÙñvÁ‘Fjd Ç t½3é¹&äÈ@½üÍ ²«Ù¤Œ÷…ÁÛ:üIVZ­‘_¬"‹›sV(ÎaOP"·¼Ž›ýô¥VgÚžD‘˾1{N1ÐÞ[쇕pªõ”!-Ô {ëÁ•Yh,§Õºf'yU¯=Ò^X.#r˜{m¿Éøj5¢ÿ§¨Žé‡ûL]ÑÀòµÓ·5|mã½ð¡µžaMfL)Ý·ÏÅÕ\6doâÀ-ŽÕ3Ü…ùÚ]%óº­"tÉqs–hÕšh€²ÇWw±î †4u@©sĽ¤0jlth¾‚A€Õ8¶s묋@bÅo>wKìæ}ß­)~Ãþ¯债)¿ÔµÈí^};‡!ÄÓŽ† EWäqãÍ‚¿»Ñ=’eÕýE>Ø÷]MàC2oq |øTÂÖþsb!`-„•|KÖ¾î}Û—T…÷­“ —¯–;>À%6qA·@Õî3c=|6 ªmrh7ô>¬gìýo¬®3aõLÐ7¿‚ÿ Õôà’ÌLËpz`PwÌ(® b[4èê]* àñ_¡qÌÍê:T…‡Â©|;ïz=òï<¤u½ÀÉD¸MºÆš`ˆÇÑÚÅñ÷Nï…/{¨­U¸hà€Gê=ݠаìº8“.aÇ6ا5ÕÀó dÈriŽæŒbD¼o„/ý”@¤ `ZL1È ¡uÐï[¿ï#yöIÉ-œ¯”£§j߀”c.îcÁt²þU=ƒË"±è^Âð”«ÔˆÂ{'Èç´cãΟfŠÓ~Œ¢}œæò’ ‰îÁ¶1tb[ª×Úsìën€}be{³tíÂþTl7ý5›hÞF%©j· ”:B¡ŸºÝ©j ‚ž€Ãýs°õ!?KÁýN¡Ó¦#cCºyåØóéŸrŸàÝ–½Ò¡ ×(ÑQO!ŠpGTpy+ÞóýF÷9§hv† ™Ýé3wù…+CȯGDGE’qì¤*£râÉ)Kt¸¨°Q⃠?Ÿ›ð鳓`MA‘Ú žò§†Ófd—›{ƒ‰œ3[ÿag¥L0ñ"K%oÜiK-ÙŸt™`ðVÿ)D8m˜§Œìá÷(pOõÒgÔÆÝhîWòÉãÛÎ;jx¥vìbtf¤¥<ìté K扜aŠ;ºoËÊ䉋“:¤ÁA þM¾~DÕ „3vÎ!$ªHJæÒåFŒ1HÍZšë§ˆZ¯2æ\§ÁbÂ[¸æÃ£—,m´º¨'VPþ(Móë1¼øOÌGG¨Gmç° " Æ>Ž “¦±‡7ÜE°û4GŒ@xýå@º¦rć3pà ¶Ë¡ú”Ÿ!.qìÞqyd ðØÔ»t‘S»X ­ ¦7•ÄÆ®€¾ïPvÙ±-íîS$ryêmu[¶Gj+m{ÎÒ±$0KìBÍobaè˜ûáèªáÈüËdüÇõ|à"ËÜæŽ'?|®aa¥@[S²­‚7!íÐí3—( „ǶmÛ¶mÛ¶mÛ¶mÞ±mÛ¶=³ï?l²Áv^TÖ}ê|õ/¥u €rE:~¬Açíë΢T£€ÐMPµ[îø! Òãj%œ/õBÎì!XWø[Wü7l5´/©ÀšqË®£?yÏ#B½à¨lÊv·@qWÂFOû‰œý ¶¹ µ¡È’°$Ïiè+À‘ (/ÍŽ§’Ñ Ù¤%ñ~ðseui5æÆÎh±]2¶’Sl®B34Æb`±õBkéöM‘ô˜EÈ ¢·àoû½²02¤Bó^ùÈGNŠ~“q½¶lsÄî0ƒ2 € ]¹£ß»çØÜéVª%2£…Å`°+†0í¤™w+–ÜÕ,=eÀº/ÎCŠœqÿÀR;£‚a•ñ¢bÉ«–C‹\ˆÛ |RÒ„@Gds ÂE¶gŽgЍçw3ªt0òïº/ŽÕ#vÿ÷bG™¯¬Èû‘¡ü>Š寱«Úë³Î§htrÏRƒe9wz©Þ'e]pÏUn¢Wûm1a_ðcÌ+E#'ydؽºÀ62£Þ )úŸª{bã4ŒFC¼éÇ&™_°ˆxù¬hý„öh÷¡]‘‚çîËôCÿÃ,ÍD9àK„Ž!Ï|ÙÞl@’¶nÅUÌÏ6jœVÞ=Sl æ[KÂAïJ’Jn&>|‘¿2“£ëM òêüFT_`EÿtEÈ6’ñµ»=j–±°aÖ§/·œB#šù¿9=ð%ÇÙnI¦=?V],¯A>b/«Ðx/Ôñ£æV#pÆ#W©äœJ$ªïÑG´‰tz¹SFïJ‹9|R¤J‚—d<ž-ƒyAW >¢!Fz±J£i°®z°Ð_EοØlȇÈZÌ~ò†Æ””'² LõÀ$3ËÖ|ÏÒ4h‰ú&µi¼€W’•}¦4*Óta=È™1æöjÌËg)&_Ó‘©r×¶ZhI‡@ÈÌ[qžQ<Æ2z‰ÈUÌJÓc­õ@–¾xÈpÔ ®_áÑ«£2yKmÖinØðoì%ù+Ó͉׿E@¾'9–öc´š©‰Ç€œdYѨ/1NöøÓW*N—Ž1„׬»–ª«¯ô£ÝŽ÷õ¬7„IÅy ·y ܱÚâÁõ í>zè@ñ¤0ý~[¥›B¶,–‹Î6¨xÄ ¦GÔèux2uBòÄreypÁU]`#¸ƒ#Ÿƒ™lÏŒcþ¥Ht)ªÒ% ¯/#°ó~“¨=YÊŽôH­aXPAq„ßêžñg·n±×Íx`šrh¼Ú{w„[@¦¤{Q=ï&`œÂú6^ø¸0D™z‘¿Úb‘EœçŠá±Ú%MV¶P‘ENo÷²)ׯÐ‹È ¼¥¸Ê35çõ9R&h›à$o§o‰\SÝ#a°Ç¬%ˆÆ±;Þ÷‰\)]çf·-K'ú§ú‡ä3Dà t®ú¦\£ˆx´nƒcSïêá´ÐŒ±¼éhf7ñéU’gþü/â¼£ò-_Ïb·Å›#ßtzòFE.T°È¨¤ Û‡MQ§gi—è—rŽÆX„`‘ZcL@7·ºÆ‰ÿ.wOãYBG°Ne†eF"ÐGG]8uŒ[ÒÙṲϩ’[kÄ9|Ì‹ùÀ…D «kË^Æå™ÈS‘òœØ¥rò³Ò?¡ðG®Á`"˜’r: +m=ÚTý¶Õ6š"àJø½!ÓQ烗´G5–ë@1qŠ›ïðšòVÅmÐ_5n¤P¸9d{ߥâ¿–hµ=çI3pýó#ÔUšFœtŒPàåi´|/” “lí„À€©¿ÑÒéÕõ=ý#ÌR]¸ß™­Bµï‹ÞAPÅha‘ŽGÀƒI~57Ôy ?šÄJÁ}ïG€âe«È[¹-ÿiGjW—cêÓ‹¼bàè(ëM@>bVÙ;Kjt~íO;uVõñ`ïõÚ!_Ô3Êç³}Ò±®ñ]}3¸È#”èJ¨$ø£²üÇÁž­Y"º&ÚÆ\Ç à+ð˜¶ùæ´Ò„a_ƒ­l!ïšžB*5¦±þ¾ÎÍí­Æ]eýX¼÷Ô ëÇ#^ÚUh[ö=!9 ͯõ",HÊ»ìýͽ9еù,è­Dü_=¼A`Û/ÜÅtÛ>æø(ã•a݉‚„×¢¸§k1*yéŠN àtóŸëR dG»œç Äz|á·2-‘aK¹')÷·\lj¾(5±… G³og)ZêgÏJâæ‘ùÔN±ŠÒdP­ ™p._G20''=,Ìú¹óÑSÒ›ªRˆžp"q¿=þev·A5ì3/{1ÉѨKû±,WãëÓ\^¼£ºY”„VŽiåDþŽ]³M€îJÛ¬D7€{T/´X¼cìGWÑ÷l1òÜ·;+E>K(ǯ+/ýwgí[ƒq…ƒAé{öW„ÏÕ\hðY‘‚ ÆÔÂÞ|°s®LUÿƒ`]y9}™ÃØÖ¹ÄôB)µ¢(ÊV`iî¨ –„ûsª«•t  ŸûŒ‚ƒï 8 #¹óÎ0˜k:*ˆÜô«¸EŽÕ§¨è¶׿?wU\—C¼4Û"19ç’Rp]ú˜µÏÁZž£t$ÅHa –NGé65–¹‚+6r!Þø ¹È«ÎxÛ¥žzGÇ’•¹ª¾^½xÍ"æµ[Á¼ï“y_66‰b=…urÌiÞF"V=¹Ÿ]xï.þ:¶` +-Þ2D¿V£è†RË8Ìɳ‘+ïô°õ cÈêñ%VS+|dÃ÷ð¯8Eâ'wËZªE‡ˆ;1%8ãYˆQ~Æ“}…¡‹'Ìî·«OmËt²¼R%¶cúQ Øc¬y·…îB³ºÀº=bÖ¹€ŽÛüÀî@ä]¼bd|iG#¿´8ýTVV-lÃìúß›—<¹É8Àƒ:–ƳäÅ>E“’Våk›å{|£MGѯˆŠÿdÔÇÐ bŇçW*‰xÕ\ø‰Ùj{ Üø9`›IÕczQ>5ágOÖ®] ö½›aK‚›¡;7Æù¿ÕÆÅŽV#ÞLQ¥‰d2‘œP¶™ÔÅÁV¥ -(vÚ§b@GÅSp&ôøíeCÎÔ ~ðŠ›gãóí»h0òð6ܶ«¯&<±vèÚÍðI8ÒØÔÎçÇUúH`‡Ô+÷–\òùɰªÎ}Ó;’Ö¹æòb<³š€ÙÔÅ®ÝRtÍ4寅ïÂü®ïôV;æö¹a··#Œ&>AÂ‰âø¤yÛ÷š¼L®ÇúÃ$BÕèæçƽsѬ bê>ºÜàîkƒK,&YZMHû9Þ×…Ñt¯Áö2X󮯠K!QœBÙôˆ÷Æ«SGGpœmìƒõçGQ¡¾jWÆcÑãM¨"c3Ö½´yÿO[±Þ–!FÅù÷p ­±<™Ue E£:“rÇKd·þüwsRfUk|yTâCž]ýi'‰nÑ ut“¶àëÚ-µ¯V„CôòDöóÙJy0j«//ú$¶×BHôÐ’õè{=p1¯B{‘6ëТ¬µ©åz‹ ÊfÂ\dgî¡ÄIAýˆýã2Ÿc”l YS¿:¶Ô;1§"äQò3BÓR^ö˜­ðÓNõ¾ÃÚ£Ð`„±Wx8‡ípõÆÅ±+7¿ÕŸ9,ýµ’±|Êq6˜:|™«V/T¶X{¿… WÎMë‹lfÑäSì#ûû@S¤j¥75ÀÕp"c_þ3/r¿õ{œŸÈ›-Á3VµL-oIH¨!auºRµ±œÑ}‘Õó—úJlD>¹÷‰†&Œá¤zWþmN“îŒ+…g®f2i-òÖÞ{ó¡$Ë,GWŽ5·ï¤ß®;0“ŸfGó'9@³lhá¹ä*çÖ{͉Ùú›i[‰wZ/i±e2úå™j>ޘNJŸ´ãÆÜÔÁKŒåH¯.íò¬)}1?ÏÔ³~Ä54 Üúüú…N/l6›T޾ÿs/íf¤@áÝ;û¤Íƒß°'/¯ý0N.²ä=NfÒ àûª}’G~A<©–ƒ¼.É ä6“xô" ¹#µrÓð—rÜãOz ñ_9K’r«ÍeÃïŸï„ñ³ô4ÒÛnözºO³nxg¹>óç+Ü;•Kr’¸Â]–¶uÆGÏlŒÀüI‚Q'{-€ws“Štó™ŸIˆ²"Š#ãݾ«tÛ$tŠd¢Á6ú) ¹fŘÈHùÖ¨hÊÒ/l·”ɺ–q°´èF]‘‡bÞĺ|îÅߌ…ú!ùà (jWd†T™.òA"a$öñLJ×=t$ãªdß¼„ÅO zìýéaZ.c¬‡wMú4§*–}f2À:PÓü‹Ó¾¡'*ý(r«±O·ŸèX,þïNÉ“´Çï|:X_ÛÂY³ÂÃnV£ú½ð˜nk.ªa¬$ÝW¢¼táœ=ê%«¡°¶+s»³œA…ÔGDà¿€-dÑ#Ã.m ýHx£,­Š<…8ósì’3“¨ðsX¡o ëç°õ¼ŸÐCTC‘+/c«\5à$cÙ¼V olHí™%céúà%[¶1ˆSn.Orn0P‘耘’U÷cœ”³Ø´lÂÁîÞGí,¾ì¢jAØIì¼. ÔøH±á2¸_öse+Ý€Ón~,ˆ¾%„û&—ßµÀz*OICmcí`oZêd'h‡ÝàÖsÍ`î¡l‡>É•Ûóö»øÀ#ðos.·‘K §9NCG°»Ž MÄëH O¯õ&f=¼×_àߦ` ã„ý mÿâX°8¸®ÄJ‡âŸFwíAä[Ø:ßM6hPĈ/GÁPÖÁn[%J÷g–pf*™/:Š]°xØá>8Ðx¢ân4PÓÃNp¡òõšôU·¥Ÿ40r•´éd;ËT#Ë!¥Ì>Œôˆ!fMµ¥_øKbRI áSŽNÓƒQ2LP+úÈ£xàÃC5Áÿ²«8d ßr)€Y;­bä?:™ä]¼ ¨òÎ,”øàK¯!ÿåšÄÖQöÂëØSÍ›Û#.|ck1¾õð =ïSû|1Õ›31¢QɨŽÝù5$š0Cý«j¾†=‘ßਫ;‰‚ë+°¿‡òêRM1N\zO#ÜGiƒ>Íå6AÚŽŽ!Z2ae6v3}@gLµFµnëÆç1s-ÓM|;µpà®R~ŒÕá*ºf3¨Ce€E<(Û9H F‚çêä¾²aé½8Ý»®’0¦r€M;¨VÚMùµâö¼¯ )2 £yÊï²y '|– exTav”Ò5åÄÈ ~À€¦Q©Óæ"ΡîÖ—_\ùQE9éY2ï‘îQ‚Cõ°™5¿CMTÒ7²Y©Ñ—ÅH"Ñœ„ùíeÌ'l«ñ©¾}Õ~œõª.aW§Ç¥³‘ *·;l³jÈÙ˜ÊëÐäà)¬#d«p×÷fYhCC±Š1}—ܧj#¬‡w–šéh9ôlÆ3z<Ko‰ûq­3úq¡kœ¢ êúº’Xm;Ö~üŽaȲäÓî5Çÿé½(Ž^7˜ v ¨Ð¸ íhŧ qSrm4Àˆ~Ún= Ù Š™ÉRܼµhJâ@õ¿’¾YÃ¸Ž¢!m£ºó™v‹%nAŸ˜ÀÖ&e"“^æ°l9Âk'E-Í·W“MÅ1C ò5“ÊÅq‡µ/Ú鬢 ˜o‹âŒÑÞ¬NK¸iR°4 o+C+Qá“Møžæ   {ãÚf¡ëÕÓ‘ÀÔ··.E° ŽÛœ/™yîœîÚk›´èW³c挫’k§1îkÁv;§ã¬@þ–HlÉ‹þtT›“–§+•Of’ÓSWƒ\&µ Fñ|Ø’MÈ×Î¥˜0`8ú‘Gd5ÿ0ٮꀎȱ÷7L=á㙘•ˆ6^K¢ÌAŸ¸4Y{STäŒF)S*LÌ_Xò}qÜB~”a>Š_ÆJÓ½ôQ{±ñITäOà@[ÄD+òÄ ÌW -fJTY>¢Á»ÆëÁÉo \˜ÅƒØñv°ko¼ÉkƺÒÅ¥Rèîb„½rÒ‰ÔÓI›m4¶Š­?”·¯¥j\ýø î(¼€èé§ÅUÇ/Ü*T-•£þB¬‰E}¸P¿u¡ÿÅãµ*麜0ÁÒ¨Ÿw§p„Ÿz{z"ñ.AÌ>÷Ô_褑+}ÌpþJ} þÃ÷RtM…•ÊLF°SÝãÄVˆQ¢VFïת;˜Cñ.3 /E)Þ|©' ¦’x?ÃDAeJ’£p¸ÞÌ&T‘æCø"³ÖNU¾©YX{ â!ä¿úû1VùÊ.þlÏÆ–qÂÅ[Z$Rbc³ÅC1 ±ûµ‹Bur ²Ærµ~Ù½ÎíŸG#)n%½ÐA?\Á]L2 iÈT©HìËQLØÁÝÙË¿i—–‡¥ý÷À»)«AvVà³ö¶h–_x;®ž° ÄöÕ„.¬ïfŠó¶­õç½5Çjœ#dTVõ"z9öölLÈ'Ō̗ï£`CAüYÏI¾f­SôÖEOø…6ÖÔõÊ(6º¯D9´Ü¢s!E‡qR1áϸP\ÇqSžÂ÷—½¼lôÍ5Ôº¼¯€¦§|ý¾üP'K~¶]eªžoXœ{nT²d?*WIú4½E¬(´KÁY“ËU˜¸EoO…bhó§ýérWÞâ…€²Æ µJl]àÙG]—!ÕŦüMGQzÖÉí3Óë6_šXì¶“?&7V•gÇÌðð÷ª1¥b%×Ï«"¾ÿÐov<Ó¨q^\¨‰5Ð2SÿM©ØŽâýþ÷M®] ¢²²ˆÅeB¾Z~”€v¥Ù½†6YeÔ½Ö ]¯öû޽>{Tæ7S)ÊÁ5ž€vO䎎t¡€ÜÐÿœ’-K‰®µJ¥Ö"ͳǔ$e{©-ÊÊŒÁ»’qÍþmÌ¡‘a.ubC£Åg +ö¢rDÏÞ˜›T3Ö¬Ÿ;eOyÚž §j „ú£OiñàN,ƒŒša‘AV­JF^忇ê§r•HP‰|‡¡3½—²´¡Vñcs9¤˜j¯ ÌÚ¶­ïž 6yÚë͉!-mUÖ”±ÏRKÍ‹I›Š»æNh›Ã«©ûNú‚€AloÞÀÖ«N¼F¿ì'À¥9ƒúକ(ØG’2ãMWÁ§ñN‘•3h?¤Eަ@™2ûÓpéБþ±½9]òº·¦3}2Ê‚VÅîôNWóŽÊ 'F}\Î )A ØVµ=ãÑ^îµvä–ÿ`‚Y‘)Ÿ,å&$§/ólÚ‹*ëO ¬¶Ès0¹#¡í7­§‚º£{Ž*dÊ…w)Õ©‡ßÃêä'Vg~_L-fÛaNàM]œœcã—®0ss*Þ•‡âsÈ& Ôý4ë›Iû)ÁP-?ä…L!üz¹ÕÍï±”ÜøT¢^³T#sÃݽöª¹9•»Ý9tÚ»Q¬T—ÄõÏ@ËR2_‚Žðädå6¥¿ÄŠ-\}ËF9Ë$yÅiÁª"lDèYà\3Kêó*Ý[äòR $–Ç=tÆÓQÖ>äÖz̘E7d÷ •4û•×Îßi<•úö:®ÙߌJ`Oн[Yê쪑šùlˆ›;,»µËVPM$ã^9³Ã¬¡¶p…»wTTëg;ÛñY,„XÇ:óû £ŒÇÙN( á›G§§5(R¢ üÔ;&ë†qAÖv"f’Rui*Â!¯‹–gM¤`]ßµOŸ©¹ÌèJ¹ò.k$kJÜŽï *\uF©'ÄŹMaÔå¬{µº[™©ú²pݰ€ÍßYºÅ×¶Tå7äP~—Šz8cÑ¥v`ÃëÇ' (Ð#ƒiOëä ðgý¹} ¤ýýþd<½‰¬ió/ogEÛïÏš ¤¿&jæßŽ; ±‡Õ}ð±è ýä’ÿpF­¯K¶Ÿ`s;Ï@9!$ä™ö´jÄ'ÏjßL‰äˆxGéœÒÆšB¶ik=õ¶³Éà/ƒ;\# JÆ|dMÛ—ÕO´QåÒŒHXúE3— _?Ö×t+Ï«\mY—tv×]7¬"³é+KEÝý–aÜížÊÞI(æžË´u/ün­ë.÷´"?$gÒÄÐæ†l ùò6Þ—Š?Ioç4‡÷"qÞÖC}vƒ]{ _Ç}•C;É Ýi³ÑÇG¡•XvŸ´+‚Jg”ùX¬çŒ7×vµA-TV$lo ˜Ë•G—(Y«—”Õj¹zÚvPaºŒ3à óÃ!5$”S¢Ø8‡ØÒ22ò!ØS»@ʈ—~˜±ü ôí+?`éÊ.Ʊó¾ÏŒ^/Ô¯F€ÀUnôôD ¨zïhPÚvÜ|¶šoåÝ!â_I„hù_» ìÒ¦ëeà›ú–ðÑDc¡xònåJUò•+eLϸ’läP6Â-Ioð ŒåÈb×ËêkS®´¬˜`ËUó(5¸ýqÃð¨IšÀ?>Áx"ïîkB’L7 ½VwÎÖÄB‘oŽmë¨ÿP'‹p²>W¸ÄÕäõ\¤‹ò[%ãÃÞ³7“²:ý.} Nš{Õ¶,°ÞÆûä/ÙühÄÍ! ºöN‚ÉÁtoƒ“´¯É=혙„þÈ•°™qn€a¨ë'ú²ùõ@¡›gÚE¹OêT+jIRUBƒõ&VÔÝã:æ3 ×y‹¾pãµáó|¿~g¯I¾Ð›Î98"LKRWa •ož5q÷ü…‰9ÿÉŸäÈ÷WZ{³V>«¡˜*åHL<·RP}{Xªò—‡ŽÏ“Óz2IvìkDõ/8\Œ£M ¤Í좫)E\yvçŸï#¾Tã9þ¬bJeóeÙC"«³?æèÕQ¥€ûÑ#¡ôιòÚÓ0b;óÈ×FEå Á¹²§:ªÁÇG·;Bvñµ‘„ñ9Z[»z¼ J¬câäæYdP6À 5÷eQ]…©mü½¶¼[ïßäÊåÈ]þÿav¶ÅÏ~ë Vƒ‹¬{Œn¨ó–JÛ¢¤Š`À,›°°û,›"€›»½TÌ·Oá¨Kýu„\2àÆªUÁ €3ä`n:5X1öçÞ›k Ý'Êecÿ6ÂÆˆî¼Ìt¤õ¢Ë´$²û@‡úUU˜æzÌ´,jõ6ù$Û®èÈ;##MtLÒ«Ö"'ãö;ZÜÙ3øü˜l®î¨ìŠÉ—ªWfhý²ŒŠ)ëø¤ Ææ ’„èRÕÛü*ÅoÜiÍÖ£ƒ®OÉ)œ¶˜‚N9lãR~ô›BæW½KYH< ª]áÛ=Ó»^d[<ï”~×ðf+"‘<[@áâé¥ À |Ì}zfpæÙÏ8|o7ü AðÇ¢”æˆì®ã%nK%ÓÝà €d®¸ô*9Vù\«ÞÀ™¾]š)¼Yª§ÓHêÚƒdµ¡¬µõ²0ÙEŒÇ|sºê;a¤×qÍðškk¦¯>RK ÒÊðáz~—ÕÃñ¬@TRïí„ÐüÕñº¶•KÈVGJÓ ~P¤Pì=ä{Âo“‰:ÒŠ¯é|Õsh.|U%¸é‰¢èÍižx¸°7sfâ½ÞCtÞþCïtH *ÞÖ¢s;²£¢s?XÇØœáÞ1_wªˆ¨Ó9õ n4ÿÓ` ò9Ø‚êV¶úöù4Ôã¨0ïR½4ÏÇiX !ÿ/˜¸' RÖÌI˜N6yF•+XÍ´R[F|ì%¢á¦dZ¾"ü—¶\ºØî#ãôç|¾j !¡.%Ge‚w,?#÷¥qeµº7`ü_.»^ë¾WzÓ²ÛÇ›‹ðÁ¹ÙÐßa /!K¸®±{ËÜçÎzL”5½ðÖYe˜‘ãhòkhg4t@‚nŒN””¾°‚¸>\• ‘üÕZF—}%Fù•å:5X´ÒXødÌúhJ ™)h™1ï©-(Í÷ãÇß0½¬Ž<ˆs’ê]@fžl2tfv­`e þÁrs:ò`¡ò¾›á†»³Èô‰mÆ´ÂÞ†´Ð’(ðÃáƒî†îŽi µop±'¼Œ¾Ê^Ä\ggAw]xt/Ì¢@GE ›±¦Ó )].ã¢ÚëÃ'9 8Þ-ìr>×f”Gç?öžïjÕÒ”¯ ‘ž2IQù^ЀCnÚÌ ‰-ˆ}0‹·ð߬—d¬>H ŒQø¸¸Ù'ë‹á”‚‚Þi¢H›C`VÇ„V_êw°åXYŸ¤Iù'VnÛ#I~Wéý#aÿÀx@|aKþËc…ÂÑàž-.Šú [倆‡19tjÝ{êhH•­aâÞ0ÙZÝ1‚"í<œ™H«ZZÓËW—‡°•®UaZù63ÑŠ{ø¦ÈÛHJñK˜rm°!Ÿ#6ó9}m&õ$.?Y×ñðe’ª Wî%›·W™ë Û¿Á©}F%c5߉Îù4÷lîIGç~ç?]”BÖ*•/Ö™_Op×7¹à‚°3ÏBú~홀”ÒV’Ú/‡vnPËÖsBypÛ ºe€8[Pª@L¬sÀDZzá» ÄžgSxvPF×[#ªÐìn™ÚçÛbA«{ûß %œŠø ó2Åø6"qd:ÃLMm“ÐJ»r^#J»÷Çø,m\µ·zê-d &º§ªè9Î Žñ€Þ †PBȧÇÞ%pú¦ `ùAΕÛðÖC9p°_h’:)xÙ—Fpw5(­ÿøús פAvÝsm°ïæ½µ§\÷zœëGNH•|£DàJS}eJ„ZeÁqqD<ê‘°# ÑÏíeUúML#¬ÅeGÈï¡Aâ_e«Éñ¶sX+¢ ×^ ¦Tø¼ýøéV㽯ÀÊO´Ò ÿËú_êþÃÿŒmL \ìm ¬¡þq8ÁÙendstream endobj 19 0 obj<>endobj 20 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 250 333 420 500 500 833 778 214 333 333 500 675 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 675 675 675 500 920 611 611 667 722 611 611 722 722 333 444 667 556 833 667 722 611 722 611 500 556 722 611 833 611 556 556 389 278 389 422 500 333 500 500 444 500 444 278 500 500 278 278 444 278 722 500 500 500 500 389 389 278 500 444 667 444 444 389 400 275 400 541 600 556 556 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 389 500 500 500 500 275 500 333 760 276 500 675 675 760 333 400 675 300 300 333 500 523 250 333 300 310 500 750 750 750 500 611 611 611 611 611 611 889 667 611 611 611 611 333 333 333 333 722 667 722 722 722 722 722 675 722 722 722 722 722 556 611 500 500 500 500 500 500 500 667 444 444 444 444 444 278 278 278 278 500 500 500 500 500 500 500 675 500 500 500 500 500 444 500 444]endobj 21 0 obj<>stream x¬ºeTdͲ-Š»»S¸»»kãî^¸»ÓÐHãîîîи»»;4î oï#oœû~Ýw~T•3"Cæ«*)H”TDÌL€’ö® ,ŒÌ¼5+;  ƒ¨ƒ­™Œ«±­•)àKÊGA!æ 4vµr°7vò4fq )€•ÀÂÃÃGspôr¶²°tP««hÒÐÑÑÿ·äŸ-¯ÿÔ|YºXYØ(¿ܶŽv@{×/ˆÿkCU àj ˜[ÙbŠJÚ2 Rj)u€Ðèll Pr3ùç,rV¦@{ ÀÜÁð%øg0u°7³úçh.Œ_Aˆ¸Œ.Ž@S«/3 §)Ðñ=Àèlgåâòõ °rX8Û»~åÀÕ`eojëföO_ró¯<~8:;|í°ûÒ})9¸¸º˜:[9º¾¼*‰Kþ;NWKc×|»X}©æ_;ÍLÝþÉÅ–ñ?0_ZWc+{€+ÐóKè0̬\m½¾|9:[ý+ 7+{‹ÿŽ€à ´0v6³º¸|Á|aÿ“ÿ>ç?¦ÿyzcGG[¯Y;ük×Å`åê´5g„caýòiêúåÛÂÊŽéŸN‘±7w°0ÿ[nææøŸ:w ó¿DýOÏÐ|alæ`oë0šÃ1)8¸~%@ýWeFÀÿZ‘ÿJü¿Ràÿ•òþÿ+îÿ¬Ñ,õÿëñÿjžÿ'´¤›­­‚±ÝWü‹a*vÆö€xðo¢ù? Œí¬l½þ“ÿ¹MøoÒùéêþ ,boñE ,ŒlÿîD+I+O ™’•«©%ÀÜØö‹þÕ¡êöf@g[+{à×Äþ‹¾Œ˜¿ºüŸîý/𥕩ý×`8þ­Ú›ýNÿgDþ7ÓÿA¦ÿÂRúšiW5/G à?À5å¾ä?ÿX‹Š:x|X¿¼3°±²xx8<œì~ÿŽþ…Ãòï ÿ1•7vu¶òè2323³¾¾ÿóóß+ýÿ#aoê`ö¨ºÛ›}ñÆ þãèVNn@ñ¯³2ó°ñüË—©›³óSý‹¾2÷ë10è 4…;†]1*ì§¿Å}Ý´Ê ÐÕ帴.àH±Sï0H¢{^Þ ¾5ÿn•åÚä¼c÷Jñ4xqz;ÌÓ´Qþó’v÷“ŽnÛH7æ€RaË\„A‘dXvˆ´ëÝWìuàþP›Ž¸ÀŸùçr~1'¼¢½Œu_Š1¾@‰U9Ç2Ð^¼i×Õ#DÀ‹™&ÛžˆV÷Q[Ì6|:puåEå3kÓ_;Ê ð DWD 2ß.MЮCégûn…eeþXDŽR!é²ìãÓ%SmùÓŽ¤d›ñôï6DAƒè5‘óžn÷`t­€9%—T§?k¼7df."ŘýGËÇÊ|>ôD~ Š–Îç&um`×7q m‡¼‚óž—ßÅR–ú#M9O,ª@§X² 7Úå¢ÿÀüo Þ×W¢Õ`Øw±Ÿ(ö%ÛºÅÞ3ül"Œì¬ö(ø4G¨ãÒr²d–ñ\AºeÛáçu“VdûÁ%ĘoMC/ëÝ]æ)qð]OÅK¦†r‚„žP¾›6Ð}ì‚#£ {Swû—úF›0ç ˜—ߺ¯.…ËÝšMÒ¤™–ÍÎÆˆn“rÝѱ‰ˆé‚@ÛoIÓôëáfÈ•y÷kŸ¥ZŠâÑT;^†%™Îûû( Ò6SNŽ¤Ã ¬óFå©Ú¹s}å>ÈZ¶ž‰ QmàÌ[õ”49åë¹~p°¾Q„¶­NzCô0îÞILÈ&Ó̈́#ö[¬UMüdkµs±6~êj V¦—ivS̺vc›­ÍþNÑ»âx?BŠÇ¦nYâ¯ªÑÆZg2©-,%E7¨OÌh@½)«=;F·W ei· · ¶)f5îCßÄ•{NÙ<Ó Ô$¶bQïoÕã ãù픂£{+"J|ÉæÙvOi´œ˜‚—þ±ýù¿%à«ñø£G*«—¡{aÓI­Õ´ýïÐÞ¿8pâDl¸?Ú”|b(ý8„o§O›’ßÞÖ¤ÚkcA ·æÕ„RÔptsøî‡ü=M#ž¡5Wð‚r¦m‘“EOÀ,Þ`7Cèäp{9wÓ½¨ã —Cv¾IYPïek.ŒpÒ¬§ƒí.¡{™&3¡Å 'µšA±´øùAxvS"ïQ4KÙ¤Ôn0oG·(Ä Ì³{já÷µ1odXólOb9\Ûüé>Š)=éíéBÄsaþ¯n’©DÝA¾²dôýð”¤'êœéàPx# ŸïÊE þN)£KCe ÷P<³d3È|—‚²¿©m|àc¼<„ß%qŒˆêº/*-GšLÒˆkž|WË„ô/H½V–ÿsog§‚JieŒ”nÒ‰zÇh46ð·XÑÙ±>ÀJ<¨NÕ=R–ñyŒ)< Þ¦Ð}«¢—„9¡íívMXpší.x Ÿ2…ÜFùíFú]ÞÔ*jî²RÅß@\™UgÖÞöéÌâYöA X]ŸÕ@ü{c¬©ü™È×™Wóù€€ïu99Ö¸Ÿq.„ZÌ®ED'ÚHy‘ásdVVù]èÚªRož~ά‘àÎ+¯6[*Ô_òˆ,9ê¹¹LÞ°ðÞ‰yrê83PISåÿ‰Ã8cƒy5Š•¬ÉìOš3ó3\ˆ1D¾´Uù¿³i3ûg?M³e±k+Çgd*™m|ë3ºbb”’-Åã—§]+J'=õ\ÂäUÌGN ý0&‚BÇS‘9TÏ¥Q9ñ_U',ïHr¶ã‘)}ÅëL+T²F©¢Õb‚;- üD¶G;Ò¾ÍéqÔ’ÓeŸµÌê¼´”fTáÞ§¹;G“­ãž^¨Ü7ñÿ Ï„Q€£~Äz@.óc §èÁñN[GŽ•¿áD5ˆ³ºÛ¾J²Š3ÒùÜ6¾+•šè–׉ÂbóÀhºß«ÇŒ×ŠìÆh:eÉzé,UHèÊBšQ¥Ä‰aˆ·„5¸T“fÉÝ‚£;λÖoÄ þáðkøÄk_ÈK#eƒ.FI ‹Lnn£#¨9T]“££¦¡ÜÜаؔøøh(±Q˜ÄyOò…<ªš¨ƒkìî‘[ý<“¥£96²Á'«5–©1‹À äÛJ^?ƒlMcýgb}Ó@i.É«•ŽÚ“§¹}ÝŒ´‰kIÆ#¹0º„VµGª’±úv1!`CB}NþÉ_Líàv…Á>4Pr*⦘–+ÆôÉ'‹X`C¸OQ-ô|تhÊ€;•’ôÔ Xr¤$•“€›ÎC¼n˜‘­ëä/ kÍeé±únZV|®›'F†#·T8äÇ$=¬Ùê#ÉâE)5¶# 6zTÎôû`ª3 [RžÉãàR$ýù-6€öFH4i†ž{'añ)À‡ I™q8lþ´;¸_9Ú–è]õ¶è©ü#€ŠŠé(&YæÌΆ·J®È†³‹Ya©Àšf6³˜åú®,ñTû;e.n‘èj> '}KàÇ•ø‰*ý"Ú©ytl‡C‹ÜùÌ_pã,´FUûiÃB7x¥ši|Ó´§³Lrå ñNÓ¡C„ßßzË „ŽøM–KY½óÇ.ニèù>§“µÃ¯„aÏ n×nÐ ÈGø ØÃèCNÔSë í$MÛÁµrz œöÜÅpm‚‹È‡°7›¬”¤<Ä©à¤ÿù6™µ8ü³xˆ ú}€}W@p¶çq#£F w·IhVK‘O™TFÞSk­ä‘êöpT›Úžž ä î4·ì|ÎѦdùM!La/NS¤¬ç}µ9‘=S±©¬&ÙÔ„J]â§ Ny¼H1¶Cãúú!!'½YÉÑÚ–#íí±]áÒÇ׃ï$–/íH¸ &ÖI8›njn§6iøsvrÉHeAºZ½U}jºíŽcï®—T Ò@ ’‹'[ƒbhÓâáçÛôÁÈ&j“×ìë…ˆ]ŇÌW;ÁIÄ»[6üˆgÞ»ú­pÜàÁ–Þ{ ´£B>~FAþ9¦hÞš”¹»r~Ú—ÞÆ†ÜnÃÈOÞáTøÒwËÑp é>°Ö¹Þת¥ Ê [틊 Õá÷}gWö—r­G@¤£uëQ’ñvhư¶þ–:æ Qû2Ä«AiÓ{fJ._Ó£ç8kÝ<Ä>7Ö²H08&ÈøÈÏQaá–R °Šð§dã™Ý‰SmY›°&/èJÂdŽ ]sª;LkkXó‡½I”ø"ÈåÏÀqJÍ2d‚7êua·'üSí[‘Xµ1í¼~Ü_žë,Þ…04–Ý*w¬4…ùùñ}™[Ç/ô¾Œ®µ`4±Ë*Ótìëæ‰GÃÚG ~p‘"ï{€1ËÉ"¥'BŸ­¡Ï4Kea] Ž{œ‰Äw±O?;º—Æô•¡Ÿxã$Ïë.P)[LÏ|Oôú³€“®šš2϶-©ñ{²ÏB|þÄ¢ðõ:¹Be©;1 ðTV†Ö7 ævâê?™ÜãrÖ“-L#s=¡´€ƒäfk':| <1ÄNï¼~om|U’chÔŸß^{ÇŒ¶KU‚î±çÛeJpðhà ƒ¯4©(Kc¹ÅWĨ+9DÞ6ƒá-ho4\w¾R»òRmyúŒÒ·Ú°Íš YÓÜÚ³ÐBt,¥ò*Øš„¡’Ñí! ÍmðE§ÎÛñ‹áHxã1Cq•d:×S´ù?ê?tXä´©zôòûñ/‚§tVr­ûÈÐRž|¢mwšî ýÌR=¾)Éfäû•óÂåüµêÔ[¥w]]#À·"g)˜Ÿ'.ZYg×¹Ž7ºÛ:ÝÕO·<“<̵)ê”û#øÊM† Ëðô 9È]üúAšyáúFËY°?˜Ä.LæÁÎIâôöÇ»Ç÷â‡Íy‰ö¹”“bï÷|YŒ$AÛÚàs ÐÝvs`Ÿ]G¤he§K7=5³‹Ž·jx‘„ü7š³_&;FR×üÑ6ñ‹e[·‡ÉvÖöœA.S/¢¶ƒaâ®ãi%=ÿh›Ó‚ê µiý·«§ ûtLrA´^cŸ™ÀG¡¨§;‹FML¢éPˆÜïŽB¡k:½£óê?o-È·9ˆ¥aOPwÇUòÎéøùŽÂL síŸ"ÓÑP&duêñ±'î©ãPäãk„­‘UŽ+ƒ ,³®.vODtþËð}ºJʲ´µœPþÙ|.b„`°m”ÜI‹^µÐÅHz‡$ò3e®[?¡©…ňXȽ¢BŽF$!•·¬÷£ÏÙ„¸2y{TŸC ¨ÒÃ$+îÜfêßV>!Ó0úÈÆM¤žñ˜:“:kc”õÄ5ÑÚ¯g9<ÍŠöä:‰û?Äþq½SSø ©WåôßI9 ó >?0ž®×Â9É*e;I L…ëæñÔ^9Ý¥ Û:ÀÈÀyÍδ6¢ð—Œ:¢ž%ÐEŠ}|mo:,Ø?jÌ? ÌY…øÁNÒ¼pœ*#<¶wÓÎa†ØP“=×nä6P€Zw«}t–$J7´˜ÒJéÚ¾™«±5o ­.ë„WÖ¶äkåÉ7°õ3Ô ev«ú¹¶oØYpÙÔü†BÓçp²˜(f]ŸÇ¤,GJ >zó¡üêµu‡òGëÚö€Ô;™5OEA¦hy8ñœýÑ…)"6 `´¾:y˜÷B!ƒ¯\t(ð×ó7EÇò¦;>³–ëb͘D—¤Ÿ‡Š¥ú×T}‚¥cDÐÔ\Tœä$~ÛC…n]UšŒÆÉæ†t‰U•_^1“x´·|T‡ròªÌ˜šD\Ó>)×åöXš:Æ5>žùû«¸‚ÖcZ¨êÿX¦K#øy±RØ|ˆ¼­£6븿æÄ„]“Î=B]!› è °ønºb©9Ê sT}ׯxÿ½¦îÙ4TÞ¾{Èä[¯·ÉãÙsÁ `UÊàéx²¶@pŽíÍÌ ZÈE•Tíy[¶‰ñïñá×Ýô—gN-Ñó2 žú/.µ5…À𴇾¸¿ÂNuüåèZ­V‚Y›xó9»nœ$E¢ªÅ<}L†Ru$†¼‰F[Y— +‡tª†ü@b,Ùÿë|qÞë¯Ù¼œÆÁbÜ0Ç4#uO§~?)„Gª£án«ÔòÜ?í÷]´O“Õ¶ÝòÆ o¶óWˆžDNƈuL>9*0ògŸÞCÿu!ÿ«ùÊ­bÅ1K®AP†‘uÚv‚\á² FKÿùÜu½ín}à‘/âf-¸Ž¼œ^¸'N ôc :.ðû¥'†ÁËrç7ˆÈ01ìöÖS±ðvî'·¤ì+¦Rº]¾šÇÝiÂE#Pƒ3ÙtºÈÓZñµðΚÉÖB±î£6«E±ˆ´íÝò'Qs› wþ;g¸ñì,?Ÿ|Rd¬?3tž ÃW¬jì÷T,„–LD8úWÞŸVy}³äVë¶ç³Ø^¿˜;ì8ˆwŠ®H¢åõÐ TÂßX•#ºéO´šßcDìÓ²ü(§­¯&åqëÒ…4VF4y üE}í¿ŽÞ}f¶#NnEù8õ3,§z×jzÉŠ×ó‡¹7#¾4VUè”õ°¢Ñç9ßybËàó[Älþ÷-ÅD ‚ª¡¤KääÝúYE¬þ4ÎWÁtÑ_sý™|%e°iPµUÞdê-g¦àn>ÇlÓ“5ÜYPf&ä§aÁ%‘[Ù\m^iýM ßw§ç\Ƙc ó®!Çÿú•[l³${ë)ô| ~Â' tkÿm®¨çOç¤À;Ñ ^–GZríHmãu¸`zå3|º©¢9 -'WG‚@Å@Ùo'‡úÁP*ïëÈ£½Ú4á ¸{@-|šÚõŽÅ+†>KÓÜŒÑÝ2dMõ N!\ÆÚ?zá¼"=ÔÞ´¥¾¦¬‘Íœé€t“D=²mbŠ 鎗è{ªíÁÂ@m5Œï0ãû!Ÿ¥†óaó¸XÆÃP˦öJ±Ûg­šú Æ™(‡MaU³²œÄŠ¥ùs:½°C6Ì1 KzªÛ§…z™ç·@¼á!Û”6¥õ£ãll&5u³ÂXìúÞØÜoVé)<÷÷‰¿ÙÐ`€UÁ’‘ô5· (°©ŠïÆ’,Ñ¢¾z)ÇÕ¤ìg:™M(* œÉ5›ðfGR<ÇòD‡ú«ËuO¯îTsÝG04YÜ™*üaA’cÐ*¶_îÖ)à#Á~¸†¤N©¤f-ع“ãÄÓµ½Èo6F@{hGù-“£'å¡g—þžSVŽÀ˜D¿“>OR¥CdZV;ÈÆ©ÓV®«O«£– ËçOF¿ó—·©Žs™g þrÕîK¯›¾Yó¢×öû+=ý„“‹ˆK?¸ÁêЇ€¢¬ì NÙ®»lü*x[g~Ö¹\¸â•ò·˜Ì¼¡d½ 㺟‹†ªÝô_iãµ-é|¥^úì3CÔ%ùŠ‘:®ÂHØzþǽí¡@³ˆâ³ŠÒ“vI÷PDäfO‚…tðÞ²*lr|ŠlÜDɆI$ åð1K–ÔÄ\¶3¦x„)X•¥‘^Wz9âÓ ŽÌQ[Ê]ûuªv¢ûÅ ÆÓ8p ò'{º(Õ_Ÿ¾¿|\ß—3ewXlÒ™Jùßbµ–ã‹çw }U|Œõ=š0ÒÆG;Œã!„%ß§I'{eíM³÷Š úž#e½ýŽ˜ cøÊT{j4ÿQ4†®ª ÕÆê¹dj¥çç`íÒ«ÚCŠ­‰ ±AŒYwùü›½ jñù{m¤>ÿPmA¶æÔN`€áðÙ»›£aÙMšo8¢žhØ ±¼Dš‚^Ö’ÄšÕG^¨ ²v.úÑ¥‰+W RÄ=¢ “Ç–¥BêE¾Æ_vlS©ÌîhmIœnC—\ù¶öÍÁøÙ—™ð•ìrÍq˜ÄÅ3ÿÄåÄZO×WÊœ`ÅÏ;t¼°¡Ì"=OCP¢hê=NNx%‰=—†R¢$ZL{-LbBgw[P&;N¤ö$m1@Ê“ÔÎÑV­ÒX*­ó}ñªãƹ¾à/ %¥07'_/^¬eJrÎFFõªäïÛ#]4Ký4×#œ+žVÿ'T/ÆÝüZaŽv-°P­wƒœê‚㣩ñ{½»BÒÇŒ°F*ÅÕ bþΉïŒaÞ˜tßÙ¬ÜÜCü4ÊPHÍœÿÂ;#•ºx¤w`¶Ÿ(ÄqÂç‡û¶òå)mHJsC2ê̆4ÄväèhHbnïâ"P·ƒdüLüì'õ›ß8&Ã9q˘˜ÿ(sœúåô^WÐ;ç¶Ÿ×ÙæŽ_³¢|0$cnRøbH‡ ždj´k÷G:ê”xé§ùªcôƒ6ÛýŸC ÒvÆÝ z´É¿äíÚbŒ ùϦ^Xê•NíÏ+ƒzßn Ï[-×Îcèƒf~c8d©¥¯©’÷Ž‚§²\Ü?¥øÕÂ6ë”' ¦ Ð_‹ßFé “¹û^aü†‰vNç•Ô^ë§<ïd[>bØ!©l„`+ˆkúŒtŠJ“‚9åB“oX£–f¾¯ w e½gÛÖ!dÛÈ^¾Eq´—ÈZ}³×­Xßú땜§ôå=ÈQ@rV˜Þ¦Å²9àfè¼ãɯÊj©3ÈupfâPÄ/©~œ¸6€«#Ãú×\!?YQ‡|¸€UÕ¤8[UWod5¨tm£F¸w iÍ™j%ÑÏÝ•´L?ˇõ‡ûLJæQtM½ê„wæÒlöôSÐÚ¦ö‚h%öÆÆ"fö “bv@NÌG×ÈÝ·ö×DbÃôeVCÀ³÷Äá!j«(SifoŽl¥^(Ïœz ÍD_©qc6~dÑOîõ!¥Bš †`Æ‚ÂÒ‡¸ßÞ¹pxTI-KXÉתãBÀ£ÐAûÇ6@²NA2 §t²¿”ƒº7F®Ë)aí¶ÔiÁã;‚øBØDšihÍN§&MwçžÂyŒ›…*jcgÜõkhˆA'Yú:ÆìY$°#ÕŠÅÙïŠjÜÒ¾p  ·©1/‚˜´N¯¢›s… .FL¿jpk»ø¥ˆA6—Gð”A‹WÆÊ¶â‚K9î/,rLOȹŸŰ(„ ¦›è06i˜õA6”6%Ü/ÃQ²7•Ü*ñ¸ßÐN6á‡)-Ni©Zîc‹tÂ6meW‘–Il¥(œA5°úègæ´÷|¯Þ„V“@ôŠjML>> SêF?r·ÂG¢Ç¾<ûè^¥Š.@ˆÚA”kh–Uºøè2|ÎN~k²ÆÿÖìòÐäÁ£@k0ÿÜ­²fs¼¸ã[DyjÒ²ÎTÕt$9ÞÖÕzñ>–tÇcû• ë%bÊ-E 0ü —:0…€ÜTÁy}ÞHÐvµi‹ïbeo j:½Íò%V™L§a&”±õ\n‡™éê#«ÙÖ¹Z°Ý-gùqŸú<=ÓUpÜÁ©ÏeòßsúÃ9FJ¸ë±;g)¢ Xaª‰îC’O4FÜmß̓^1y ^or×Hõ»%¡=`¦ë'æGõaØc­yéÃ,'y_óú–»l(ýzpë(ÝÞ"æR*rï’Ûía¤ê(Ba# Ûö¦lït·sÄsòx%*d(£0~ö¸ôG;*°Ú8«µ#óqÂ`¢÷„ÛEcdöVþ\)/"òñÊæ‹]S|T HÔ»ÞÛ6k½ßdW¥l4ï£bê:»ó•|ñîγÇV™ÍFj»ÚÿLKÂÛ#4'>bþGšdHGû®¼Øc€þ; ð±LÿÁU°C`p Zpfþ®íÁ’4 ðE4P2•ýöLˆÔ¸F0:m,‡wŠÛÞ¥«ÅÑ[¶[Œ×ãg{ÙÚ¼ ¬Ô;–4ÿ'Va©Ý¨™¿f£x‚tÔ\·hÖÞÇ@˜•Pà#ðÃús ËÔ¾·ï Áê+BZ2 sÌþ÷wøÞÅaöåÖWhXq„]ïíô¨kͱ6~M=N¡ 9—2(–Ø=ôþÝntÇüÛ¼?ÙƒØ{l€hG?.£>2 ‹Ò¦ç:,ÅZðª)Œ€×Y’ëp³Ó€¬¡þ_¶\ˆ®SÕ¶ü‰½N. 1N²ªá+⓾›b#Üäç;x>fØPÈf<§“%hMÛŒÌašÓg1wT€›_NUv úêºð;œºÚ%N†Õ[ OõvÀ¿4|C§oì¿ :Œ· ÿ-\UÖ7¬`ÔUªröŽˆö± }â¿Êd[ÝvK¯W^óg HøÞ?Ë£x»IP&À!¢§-±Én©%0ƒ“à!ó¸;S~t8Øwɳ=1ýž=Rœ~[ å1ZÆJyQ‰ßþÊÇ£K×û„ç?•½† ¨RêWi ™KŒãk∣ù}^öÛn*K4.Œ„ÑñFÓŒ{B‡Ë^Ý Ûw ŽÁôñºD9ÉÒk$Á“{÷u¢ÙÆSŒl¦™z³µå:kÖìÒY¾Åjl€†å˜~¹B4¬K0ZjìG¥ãAýÖéùÓ“%ˆ KJ~±å2C ¶ÙŒ:ÁQb¬¸&íæ©•–®ü‚ç'É¡‹vµsqË}-nÃÃúµÚÂKnŒ þF]¹Bj„Ne]g?ô>ó¹;¾½þ²Xc¿&ã¾É4ò× ÷Õ¹M¬´œ¾.tÞ0*ÕÉ ~ƪSádÿ…Qð±,„úõ1›í﬇¿5ÔF©è“Å™Y<*ƒÑç(ÛmrgÆRÆóºg±×ì*¾I3¸$#î­@BÖBA~îÏä?›Êû/ßâ…Ü&Æ;Yö›þN­Ðº¸Y)ÒèçôÆÁ¬TKezÕ”4oý¸ `ÔÿFßþWì²Ã­Ú]üÕSf^åmñV¯Lñ…(y&ã„ɈRþ¬:‡D¨Ú¥Ã²ƒºßf‰~ ÅGµ,ã%„YaçÖ¢3Ë0Ð 5xŸA5ùCEg‡‡[‡TÈÅ”Í-K–Çjö=GDàóRpï˜Ä.m‡ÀÞ¢W.üÒú[Nê¾61?¬'ÈSú·‡%•½Ìè*†üY‹#±×P(qð* èÆê6ä¨!þYPßÄY*éË:gJOW¯Æd‰[ø’íÅPV¡Fÿ¬g<Ú¸œa·؃¨ûŽ€S?ïLž£Œ)⾩¢Ka?E½kÅl°N1;¦É"Vn>—­£þæ­&‹a½]TÀ,&5wm›àTïxÖK/P!.›ÅDÔ;êDßÌ,‰‰ð¹;A+zf˜­¤+;Ã6Êea®*³Üš•+ÛÈK¢#Swm¸¿Ò~s¥dð ™3ûúnÄVþ]ŒÝ”ΊfÖF[÷MâÃÈ8vhûàžÅSÕXŠD)õˆÐ‹|ÍÀ·eÉü2œ<çïÄD Ag[Xú-jö^J¤E[w«]¾írKà|’¾¬àîĻ΀ ÆXKRîiä•bXú‚¾ª6ZO‚üÛ 'Û2Œ¡°/z!LïP§†k¥n¿w׃^¦ä·ìüµøD‚µä³¤Ø¦dxón=©½LUCÿAÜ[Y %4œ¤¶ùŸ oxã¶ñ< àH±ÒýŒÔ‚•p¼Î®Ñ^Q:1ÃU·P¨ÞðWr{!SÌðâ{™ýz\)ç“?4Ì5 {ªä×*V(vÎM)4#ùðB|Ó÷éĬ&1gpÂQÞ¦éÚ¡HFª\=Ó‰B!ýPÜ-WV'ðÑÞ¶ŸÁµ—±Ç‘áó†cwÌ2ûE)ÊqûÔ=pǤ[Ð68ûX´@ò˜ëƒ;ÀHYº½î°Ëv ùÇ+½yE£ÉÄÅ1„‚i ­m‡pò½DJ–T~GÅmÛyÔcÈÁ_4‡˜ýbCNüO»¢­FÌOÈûbÓ¦ž§ëûÛ ¦æ~]"T`W{„Iü¸îÈë{,µ­83ö¿œrûWþãL’ÆŠ¾i¿œÁ`WT—üÉ—U‚>¿Þïg†Š îï L5Ò ô·Ý*Œ¹¡qâ«™³~VÍîÊLRºW›Â4 ºµ¬xað“eX×îÇQW6åMó´Ž‹µi冑 šG¯Qíàô±×­ç’RjdóŸë‰‡îùš^K»/Höå:€g|”¹†Kß4ªâ¬"êD‚?Sº•¨Uê²ÀÕsJrñt×Õ‹“ñ¶Ìü× oggœ£îŸùMjä÷.Ü8bÌTòí{â%nW¨Z]QnÃÇLáà!’è<6½3]ܾ´)±Rwƒdø ,{ÝÍŸ©„ã½¾±ŒY¡3¼›¡]„.dc!‹r³|;Ù|ŒÏ-©ŒHßÿâ×ÄœÉ$B:¨ªªœâjXS‘ÿÉÒg8\¨HŠ×·lBæùñX}GpåX5¼0r åÉËÖLÊ ûVœÃV•Ë5”¾eàf €¡b„¥0Z C‡'ñYPêÄw%iîœã…•~°>ÿày(<»Cž V…ž¼Ý´“+açU£UUÒâ$£ä‰=Éb~–L!‚­±°²˜á½T»þM§ eº¥mÓ̽ô²éèÀö >YYÐG-ñqÿÒJª3ö½- ‡¦Cƒ¿-žz ßѤ‚‚ÖˆO0ù<$cEÉbs͉ÆÄw³_¶‡Hâ6uá²ëÒôL®…ê¾t+‰ÙªmùI1`ZÞñ‡*+1¦äŽÁë¨XÈ…£BËo¿ôšé¯?ôƒ$‹ó£”Øg'[Œ·ª %ý8ó@ÅV±5¯•Av„y#lúVrÕ¦¹F@™4et# ùæÊò$OsÑ™fÁÇ‹_]˜ñ¬TÃì^T½ÜGåÀ3[z“fWô–'£ˆb´Úç I ëktBQñ+‰.¡@#ïñ$.aÝKæÜ¹¯@oCVÑ4Ò…©Õ] +î…ñ+ÚËb-…¸N(ÅÐ]'âl}¥:…§•qöí’"aÛav¥z@¿u×ʵ+{›‘X÷¦é“É„í̃p+ “!ÝŠÂ𘚬LU~£+Gª€e†ö<ã}°·¤ÉÀf]kÖ|ø‰³å^x‡ô0m"¸\O4E™g ÞTqx¿ìZ]•k_ÿ©!ôà}®¾ëºËèµ|'È^W…ל0ÌÐÑ‹•LÈÀ†­.HÅ›Íl^_¯ópV4–‹éa'umÔþL“Ã`¢@µ´zo÷VÑ> ¬5Z†FZúæ¡Â’™]rR^oqè@énÝIW{ì³o§À«Ÿ‰E½†§ä°RššÙKtç˜;¬½çT׉D`ò‚~FZZÞêÝ\mºÉóp8˜ôüýê[$*ÔÚoFü…´à_TQU¼‰¸CÆL=Á®¡Np ü’¥C}¿2ªH“òÊï~ÑY‘-9]'‡PMMÆ×Ù€ßccí– ÉÙ ^yä´Í{‚$?pý"6‘`ލÙU]°5šèHÔɈÛ'ž®²ëÈí]ãi&•Á2¤³÷YøŽg´%´'Ø~ öN‡ÁyÁJ‰.·piþ£M‡ÉãB"›ê8 9¥ ѾbÕØf(o}Ò=åuÜO,Ñdú&ÞŸ d‘|€×]T‰nÕø£Ô3.Íù|¡†U±"énàÑ·Q£ÌöbÞÕyZU±MYÙ-£Ë-{CÊ5Uò Ã()ñXycô'õmô¶À†\„3Ö@@ ²û>®Ð¨—ÓÉ_¾bÊ÷:ïVí»®?b kœ¬±—Í#£‰©ôòùuixF´¦“0ôbTúÝaÖHq  B0˜'Z˜¨ó÷SD†x“•]!&Ê"½åRæÑZàVË¡wNФ‚‘)Ï/£Fá ÷Ç—%†9nY*£(¤c[ )šg|ßq9r’Uã_ýŒ. ™”¯ÃÁ°‘ œÙR¯‘àô´êÿsýÚôq®=þ·IßfPûd"f…^^2Örâo@ã³O ¤àÉPä­‡¾1ªÖÊÖ†³¿{Ÿà‚»úƒVó§›AÉR˜‘ì®~ÕÁ(2ôŠCµ'õá>ø}a™"YTZk Êïl\*èúvk&0Ãô Ü®¶è"°A°„19)7­ i.¤JY4^MÉàÆó3J 1›VùºM<ç `£sÄä®39™nÉbÉŠ¦X¹õ»æ™÷­AB…°¨ÚÐÛlÖþB¹£Mé½hu™þÏ,GùX=X¨RÇŠ]«.7pŸî©îE :…ÒoêÚ·êæU4 8d ÓøªìÛ‚ÞÊqÝ…‰rÅj…ÙôúJãmÖ õó¿TÛxŸ-Í- –Qs©p4:À^¥ kû”hqw»Py?Á+ó»õ›ïºÌ­Õz «¹f\KGõô1~°GTÔªXðpÂ^ödeS‹WÜù.9Û¦D±yÐtÄ—KÙëјÕÑö‹ŒLàcf¹J{Z¦ÈAÌôf«_2ÒEÚñ+ò3¢û Š¡kkŠR¾£úKklxrÎ-|7õ¨Ãžë “só€APüÈ%žÚëj÷´”T¸ù–…ƒA`–:d¨&÷+gíú•UF»ÉÜøO8Np.TA"¼˜‚ļ¶aEÎHÀè`ZßÔ¦HÍ_ñŒ$Ï‘¾ü9].‚!6Pcëp5®ìj£égéãÞm]hh(jÖAU_oá[$_Eƒ%õ‘߸Á³ ìràï&Î.rÑ}±Å“!êê ˜m òyQXzÿR¸L3×À…`0WËŒ,{‰zN¯B^pÊ•ÚJ–UÐS¬h™_ENX0–‰k?ZA°Ðƒ5Ké)K}!2‚Ê›é+tÇu,¿9x„s¼TÃn‘?½¢P(ËDDË‹sŸ)½sk÷N®@ˆí3t_ èoö°¢1£õ£ñj>«ø¾a¾BÕkEMF!`'ðË-3ëœ; ˆ6õAÉokŒØT„‡k[õºøŠ²%£);õ˜œ¨Èvî“É©ÊP„6g½6,Ç£-½‡?ÈMÂÃX‹A& }›ÒhCβÎUM?¸_*¦ìÑŽŽC­¦YËnšÎÄ„xGÉ”y×k-¥¯ªæe¾¢;âæ2ýúÒ¸PðxéÉ) éóºÉÿ—î%è0kÇ~å!èÉűŒ L«‚g\?ç@,ÛS¶á®©34¸ŸAó|äM‘?÷ý;YÕ“"ãt܉ ɵdÂ#„ŒèïhkbìÈ"A}É(Ùuß’‹‹n$Kå5è9%uÕ[¼–hìH¡“ ?_½¿waÎ °7$Â5åB3x– »yL&“õªQúPæè?aA‡BªëµôÝÚ§Á<´7­˜`nìa3Zùª¶Ô¥žíC•xq [ÕBß_ÀÂʃk&—ü÷ЉF$™ÑOÒÕ»y¥¦ìa"¤Ñð’šVw–†Í£Œ~û¯ƒ`øÎÁ«ÚÆÈ8Ö›\â’û±é‰ëÏ Ô¶Xg˜Ê†‰¦’­•_Ó&·¬ ÒaqhÀ'r¼Åh»pAIóXnèü'ßX&å“β>í*M;£gF­5†lAâ|š¢‘I’%ãpWŽ%o¥O——ûD‘¯–à™t¨Ùe¹“DÃçYÂæXIƒ7±¼Í?Ë¢¤Ø« UGul³”~Wñ§CwøX2˲jx ŽÙêAk«˜]ý2ÕÀàzØ7XW·ëÇŠÂ…„¦Q²’fP7*3øÈ½uåçð–в³œÈW ü)wóp•Sm]7 hØAÙF ‘ŸD–ç¸õ´»:ıMêäÍ!ÈA}½y.ÉsúR®}¾ÈvK„l—‹ ©UšÁ1¾´YzŸfµ_òëX{¦Å)BÒ*]'¸þþ†=:M«gþÆè)õÎÇY|‰Xçà þËô¶ë7 ^§!0ΪÀù‘ù»q¡«÷ß'W.c0WÉ»„x…˜+ù^#ÙìSIa2‘°sq¡èÌÐ"ß˨¸][ô[Òïq¦ùeèVpr§±Kj[‹ÃÕ&ÎÆ¤ƒÅÙÓ’äÞÇ_×Y ²N@J‹tßÒ¯Ÿ¶Ûp¹!ò=lñ€ÓXø~šõ.iZ`è•g…t¦¼áÈmt¸ va×N[&. ü?ìôÌ \&ÄÁX‰ŽÆ†ÕôÖìvÇiJÚ_ýí|˜‚—do¥Y¹,ø«¬k±ob çÌ‚MÓ× ª¡P»ãt—RÃSvþüF«68ȹu?Xiù¾¡\ÚWÛxW¯(9\Â{b,9†‚,â˜ðÅÝ¡yÄHøëM9'QüV~ÏR Zý#ƒëµŠþ! |4¥š±#±¿ŒüWá.“˜×«îs(„Ö]þ# ºyènmlZŽAR#M÷ÁÍ~sµëäDL0QútV.(`Ì{æ%KW¢„è¨pÚá™—VôÕæàÄÚúàNŒäŒ;i„Ú²ð§©cÉgüP›OrÞÕñ”ãÑ¡ºc’‘w:´ˆôd¿üVøÐ,ÙLìŸ×ÿ¸ Lf6/;Ú\?KÝsÖâcìè.¥”ö7<ûä*ÇÉýÈCÃM§Ü…”z´J…Žoi‡Ð¿ÇABAöík®ÍŒ f0º2–ßÅÈù©¼õ=RÜ@À+‘¸­øöÀ/ÜsF¾ð >).Yj~@"ø!þÙê*‰Šùcx“XsQôL(ã׿ÿÆà¢P â› ¹*ÃÎZ‘±+aµð1*ÉáŠûIHLÄÎ6ÁêþÞ2c´=ÜmÒ5Q¦p{LP**ŸÊ˜Ü@ðzƵtXˆèHòó)Yå§sÔˆpG¬˜óe`ØzäïRÂŒ-&ËRû ¬»È‡aeÍ6K„wvæj2JCf¹ý éáj²ØT#ž‰(?ÕŸØI–hĽΉY7¤Š#“XÍùµ÷=\0qç´„õæ¥YåøS‚«g ñàî ¸S¸Íìg¢©¾[¤H'('«Ú§ G{Ó2ÄR†»˜ÜYÙX¸sb€A²ƒ¿Î1lòK²xYr’!¦Œg“íÄøé¯T?(V¦92ZíôÌî1uÆ[CM¼• –¦+ðÇ×÷ýÊ—ôH$dR]ê‚OâS4B†4>àúQÿÇk½R;Îá8¢½7_˜¢ÍÕ˜šÊ©«+·›TðæÞÝ¡áÊ! ^H¡U‹§G¸•È] =À¤ÄhŸÌxÚ.8ž­pPìE³OŠöŒý‹šY‹g;!¬ ûç±"%öDH¦Ò¢jíX6èêwó ýÞÞçÃC‰O$·ëz®b,âfTJµò'bÐójû|ƒ5KvªÈ1ó+0‰óõÍ 6pz⣇£Ü,® ªîhpmÉž¨_çRß EÒÇvXÏw/¬Tžƒ‡ežôJ:t\'u‡+œø U<á­ãGºƒ_*uµä!àV85êüu°åÇpêÎ.ütÚ¡•îò×¥ïCž¼ÅzTÊ‹çŠÉÇXzòÛc[[tÿ¦JÏå*ª:œu§;¸ç'—pS€3D™=´Ê;8O}¡VÞ~¨Vî])`‡•ãÕ¥k Æ ö®¨` Xtð†uKIz…˺ ç[Ka“»á€ôh´ÍNá_ŸíÛ™ÊL¾†¬,V=Úfò©¹ýüümP¸©kj©–:aà×üxBT2~v¬ïÑUܱÔlÇÚ†6{ëlQîê2 *ZÂÑÌcrëÖ_Ó(§Üî4‘ì³XãG¿sF<–¢|…ýòÎä3sùÄnüÌ×»æÃ Mí!?lºÁy_(á¦Ñ‰ì9§êGÅÏÀœëöæç•MŠÎÔ¶P ™q!µVÄûRÒ'`§f‚G?Îí]Ë9 õ½gâBMš>ßbÚ!óa¡)º·Ïþ‚b”VrºÈ½ +hpB•rä#ûú¸Hh hUÇüvn„_–/郰3¾Ÿê†ß§¿~:vÔ ÿºWCF0¯áÝAlNí+r‹ÊQöhìíxû‚»0÷uLŸK@Åu¹À¥á2>C¤±ùWìQ•*ÑŽšxý×Qh³c,h6*NÖQß,äÎÅ &HlÑácmœFÕBKÓ¯ËFRRçŽ.ˉf.fà9íß´KašÈmßK-wÅh®*"Ïjq÷Hy6 âž æÍ€…-ƒJyÌT\³¾›Â$H”ÜjÂUþÊq’$”õýQ&ù"bshÄó«r‡A—ìÐ{þ0EwßRÎ'©Å ð/³éà¡§ •ɱÔÚÂÌË< {ÎS4WÜ;×ëÇ¿·ß2 hzft¡ñ3¦¨%˜|/Ì[ vòŸ^0â!óQÑN@²ÇÇþžŽxŽSBŸ«Xcp+òö^1X«2éb< bl èä”4¸{Kf°¤õø,k;ŸŽ¦ÊIdéð̧,Ë8¢L`WuE¢âϙܢØÕæ]›g‰½š%½¿¦Ô#P¦Þy³“ÙÙ²½[…é1fƒhŸMÙÁg”'ýA^¦‘ïÏ™Àw§\·ãk»xߤQýcMIjK×ñóìL.•ÿ¯~ˆkË‚|è'Ò¹ÇíiMÞêR¨NòõeÙ¬ÓI°âd5ŸftR8‚¼ø¥Iá/u&¸þGç{ÖG³1iÇ.%ÜÊ·ÛþÞúÐ/ft¯fÎ-Mö;Âõnälîdk £¯ã»×tvr&ìµ)ö·¹6"(TŽk—Î8jì*ÆîPw&C),f`æ-¸³,ã–aoÔ«®/÷nXÖ‰~`|"è–.˜µ+H!fá2E„¼í..X`»c¢ñ1-(Ñ.6[Ël5’NEž–‹Ç¸rgì¨lXK)~ÐÜÌ›ÓF”DGî@sÑᤘâ<‚(ÒâdŠT(J/¯ E÷=eE²+¾“Ÿê2íøP›§7  {˜¨£^§8/±³l‹!bTÿò”¥ÎË BÚ{ÄŸhÙEP‘½SLÚV¨É&³ü†|rs/‡:ìOüüÂgLʆ¬Aœ*ÊtÒ£&­iŒÅÍZn‹—XVh@`æëBÔQ™¿ ákKúèÜ\ ;´_æ¦)Ñn½A-qt‡V×ùj»at¿’Ûóø :óÊ>j`úÀ`È ¦¬L`‘#%óº@¿WÓ\ID÷"Ãô8™Úk@fU³éü¢ˆw3©›®ÀìB„Wbù"Šj0’—¸3¯&G÷Çø§ûåx¡þ,aZ¥ºà¾aÌ×µ#ÆNìT@,þ¢´Pï§­“yK¤Q®M®˜>¶»³þÁü™-ìøS1B‡e”eyX"õ]â1Î{Èÿh= °ÿ)™Ž»„wS<4«5mfµ]ß«_öϼe‡;+EhM*hÎÁC÷‘Q=r7ÚÍRnú¯Å ~Ï„“Öy§îT[­8¬-çubÊf€Ã¼Íµ^² ë~Dõµ´{8¤à>¯Žsæ_Ñ$ày›½Ë`\µ°IçÜ-µˆå 7F&¢ÌwIè KÝ?rkÄrãB¼Ûîl8xż fru%ËA-¸¢Iãór'Áðb<%ðÊÁ—XzbÏM¹jZC®~ƒºO*.@óL‰tk¶I8'ú&ÓR¾èïFF{óií?ìý³3Ÿâ‚2ÿ-Äc6o8Ùì"¸'CŽ»Äâo;kay±,Â7u%,{ÈM!ÑDÓFR\ó¶Ë ]Fš —énÖUÞÜ)˜3Ü .á³s€o}àågâ%~,üäŠMŸKrï˜È­³2'm Nùí{ ñÓB=úãXøB}èÍz†ôhý#žýûhœ‡v±µôÀéètšÌÌ™€%YYLi¸†ÔAZW $ž-LtUõE.ѯ†™XªÚFqBÞ— UMðÔÄýé*)ÑHiòËyÚ|)¥0ãW;R—¿•z™ñÉXmÓ»\G“J+ݨÈðX<%A ºìlV$Ï#`ãÁ²â¶ð£Ÿƒ~ß10υεo•&²Ð—ɳGßÀÔR´¾µ¤ûXñ~4 B¿çE(b+PÎÑ4 T€ \àÚù‡LÕXö^¸@·ÍË“ü´ JøŒSj`À2 kZ+ú–î]ËldAØÅ·á;]‰û =TŠ¥Ïø@!+‹åÿ,@Ó¿žòiš+( Dµ^KA„0‘¼JÛ*Dtä­çÜgxv çÙð{ 2¿óÍžù×ñá›9”¦ì˜ŸžéÂW°7ù¤ã»‚»Iœ…>×üÀ@…cóŒÇŸ^=Þd¦™Ö­21ëaŠZOóM†˜‚³qÀ‘¸ÞJð~,WÏÆLl¨Q>>P~Ò1*EÕ¼!H‘A‡‘¯æˆ”®/ù“ûŠBNx^®¹@o-ð EÛ)º”ì:—|Öc{]–ñÈÅD}'*Eñ³Ø<<–zœ6 1ÜÐ*¤Ž@ñýÞ/úįÛú·þáo\]aÚv¢¾2A³biÎxeî—ÀR0„žíuÄ°ÄæÐ‚1“Rmlaγ©á2†çNuÖ6 íÏëjlïV¢û·ÿ×]V‰H÷ ý¶““ ûq^oþ®¯û1U+„PM;j\Ò £ìôŽÌ¼‹Z£ªõÂ2­8™iB *÷êf±G;%É#ZÔòk7ÆüÚŸÄâ–†ElÿÝ|‡¯Ð·BØ1»Â/ާÔ=ÙF->ˆÙ¢"™ Mt»éš¨,ËuèŽá$%k4 ?š:k¿RzX$•<#Bò£<3u»&t¼)-ïZJóÄ(ÔÊ „hGÊ–$Èèsµï…‚˜PÊþ0`ÎúÉXÜÞY˜åØQTìks&ú¥q‰Ä‘¾HÑ»s³—fr4·ÕC‘ .^:¨æ øš*–|Œ?irN[›‹ÑwǾ®¸Ž 9ç’Ëu´µIº¿Ò·Ähéçý”mNse*JÒ+Ü`'ž›„܉Et–‰ï+ɹ¥ƒ¿ ­%§tœÕ«ø$>ý;Ãù(Þ>[›:•иßdÃÚŽ€Œ´´ÿôqØs‡7+÷ò£\€s*ŽJω‘ø÷-ŽNêîâGOåwŽ’!¼ÑàÂ=dǯ_/¨”i÷™ ùŸ¨Éµ6‚”õå÷ì*J°ÚÏp[‡ôÏŸ«‹G}6GU:¦Õz¸6kÕ cIŒ…T`ú޳By~ @ÁY !Ÿ~£ûLjt­È=Êis ï0qpzð7ϸˆµ)Â6ÏCÑn x ¨%7áü!^Ü!ðs¤žÓæ÷«Hr-ݪt›mB…7÷=“Þƒbá3>4Øl7ïKô”B¿L»ƒ¡çù 2 ÿûÙÃK 7ºWy¥&H”Bâ…陣;=îãË¿(RgÆæÊ.nP…^sfk¯·È0nç|Á®°7ýi†0IAð{¾‡»»m4æt¥K\\(Ä9õÙ`1ÉÒ׈5tú[.$ã'ë‚–›Qϰwj±)güâÄÖfF&[ØF_Å>Pñ™§`÷o¯)°¾è6-Ñ—&B…ËG¾ŸœR‘PBYûŒs‡’CÓSâ X5žŸÏÍS/—ÎRzÔôK8ñ­+l!!÷ûªróÛ”Ó(¨¾re-¨UM>MãHÈžV¼OA…ŒäqÍèY³¹1÷iκë)­o:¾„E(ÂÃãÞn€ÿ€¼à èß4_w»eè5M©*úó»N«Ýb. ”] AY‹ÄÆÔðaëܦ£üC#ê1L@<Ô_†¸+,M+ÅõÁ[Œqý/±‹ßü¢)+&ùä`L•€‘gB*}tMKÎ qa6C0§¨š]Uä”8‡]’<±Ùd;õXg^êøO3Uk/ŸnÛbÐ_¬?“#jï¼]©¶}AåmQHõö ðøÿiôôªÓ$$ð»P–‡°DÛ>b¹¥Ç¯×î9fBêR)Ë¿iã—ª}8ŒëƲ}#!;Æö¶$ÏcP(P×Ðfü•©ë’í^iMEWÒÈ m,ˆ³]ØÝ0EmóüD™*oëΣä9M¤W:ëpYNµÃ×Âjrf\§õ‰0ã•àÎ[QO]Ãе &×À‡ÇÑG5ÕTÅ&+pƒV”2UÖ/ „þ 8ŠÅ¢…ÑtX&¬ùh©•XÆ ÕÓ‹Å‹‘¿÷8€ÞÖHÕ<Øq ͳx4ê±lmŠá·7!ÞêîJÉþ–×>tˆ2…îª@YQàO¨ÚÁ@>©L›–Ì" #hB’ãK‰©¬•Rde¿!Cn™|VB{—¼D&ñr—ùL»Uü§aT³ž50…ªÕ’UU•Ù±îpã6 ÃÂ9øÞºÒ±°¸ÎƒF”‰Ïà ÉÞÏqÇË!Ïî¼Åëaa,‹2ç f—¨ÉêøcZ„{#„ʄʵT±ŽÁ „äMW;ô „"¬Â%ü¨é£Êæúû·Ã¿C ¬¼6Ž@a¶‘ua!JýÑtg=Æ»)Æ›ëiC5ØÛ¥{[¾3[\Þ& EÕ½eáMo õ¿æo„“A{.{ÎHx^:Û4&-mþjµ±òĽ.Uõº’ðª¿`·_‘Ì8J“ò{,}îè~å@ÉgÝd?𨸺ÒO·ÐpL\#ÇÚs‘÷yî·ÓÃs Fsð l:µ«;#c0T_çëh¹nÄ¢:´z LA öú1ýN‰6Ñ×ZE®|)iϾ55p¢]T1–d™¬ô1ÀÂÜàw¾œ±“’1Ñ›¿‹=–PÀŽà·(Ž÷\•1:õ™‘^}jÃØMsȰ #ê¿;PZ%$buŠÏL>¦ 5V+ð?ˆ´rÞßÕzf%öï|(”ÞêP³¢[!ÊX9(qO?£AÝ„#ã6é…v¢Aîu}\(òCáí_”ðF¬¡bا5 ÃÎ]†!¨²Ÿ£]KâcߨÿìôÄžx5„—Þîö¸GX¨yïÚ£n{+¸”P3’âÓ÷Ãi«‹%hÚPQð,Ç-¡‹Y;¯ëÙùa#õaV à.Ì÷ï$UtÔË`>sŸ8ZoÖ0¿Ÿ¤–<Œ šmÎ(_ž·dâàHhlVѱL…KоüDJë ¦ Ï3Ŕե"8‘X9™SáT/LJKN‡$|ê~o9¬îÅÍÇ’Ý~ ý½Gê»/ŒR†÷ª\÷O÷W¶Êfe`B¶üµšñe,}¸CŠ?ÿ Sr#9ä3köó‚ð{Ï€ì ˆo++½Î°†äלּÿ{Nß´é5ÉÏ…Hòl{&pyÕ='×±å¿k§Ó‚CšñrôÐ>¼r  ýeȬÌ3—ÞPhC–x«MÈÁÔÀ=|Kœ5¿µâ›j[ö¶g÷!hÚÈʤê"úÏIÛ~›lß:ê<¦N±Û¹`ÏRŸ[{ËâVG*­¦j Üøiôÿ/d»¶º²ƒ TÅÜö;_Ó$™Ê†Lõç#¦;Š?Ö & €&ü]÷ßQ w'i\ ø)àòmu¯|‚vÌ‘Ë6þ¾Ú Ò‹ñÚcy’ü`™<À]Ýe÷x\y¥Œ«ãPbîhîç2¤g“DQ9É+I¬ÂØÞtüª¢Xñ€Æe³Æ“kOS盧¶*³úñ©Àä28í…#ˆÇ˜ŠxxîÁû0iŒÚIƒ°È[m›ãb]$i¨I…K ¾ŠX¬auVÛsø¼%Yï˜EsCü9 ˜ [Ip(ý 0obÞåš´4hÀR–¿¡‚4 ÏÙA~¨Ö•ÂR”…ËÌ‘”§Øû¯ð€ª›—]·ò±ÜUädR2ª>pú¿wô’¦nÈâ!ÝÒHeL/‡2vÙÖvŒ" „‰3ñ½±sÃOjµ~Âѯx%Ð)Ãø'‚œ%ê*hÈäS}ÏÎàtƽD¸6j\-ø3Nÿcr8«í9ãš1P¶l½÷ž(?E¤öÝ9òq#•Øâh2qß\Ô¯ DœÛE<•@JsÆ,ÑHUßC8—Nƒš­b&‰Y}=ž4qk[©ùo‘Gl$åaЭɢ_Åo_7X!¡ š•¡{{cE…óR_ óE·gþ' màòû½\þÖ0#ºöâBæ„ “C¦b~BA¹è Z〰xU^:!KVe2¿²dl«¿[xõMM2ăfØŠ¥”Nr0.xÝØÔìRƒ\°ªa<›tQ¢ÓW—¨UƘ‚É,:uªš|×vì´ú؇¶Ìa+™Ý!¦B\ŠˆÅ$¢ ¾?ü>@AÞmpzÍB¦0Û(Òó£ÉÂÇ$x—ZC^?â}}Á\ ¬mSXúX0³9‡AýÙº…Ø^ÁÑåé?¤ÌYQ:¸´Hˆ|–3§ã‚Y BD!¾¿Æ°žÍ¯jÿn\g[SO:-çoûh—ýúBGû9—1Éþ•B\ç‰0=Ï3;\¬¤âXbA4æöô#ªˆÉÂCÛï‰và3T4ÎÜ=Ýmƒ !h'¥|ÓlŒ’ƒ 0ؤ·B=Ôzœü­s½'U”_"…™'Þ:Py°d±‰p2è[)©ç÷”3?{$C©=³ÙáÓhä–¾¦M±¨fïëYJ`›’ë—ºÍDïò5 Õ¿!ÒPÍ\¾äÍî¦X/àF=„T7ôZ{ƒ+¥ŸAC“@&fli!&˜ƒs3@ˆæe»«’i}u”íáŸö™ÞqœÝc$eUWÿ¿ˆŒ˜£Ùw^ÉÜ=¾ÖeëW†—¢OWfáçÌrgôê«;`bZ˜ I1"´gy)•t{m©¼äBq;zÿbѦnjÓ4½X´R‰êª H\pWùÄ}Z$H/q/’ˆq°!{0‰œN¹Ò:µÅSçð, R´5—¸å­§:Jß{%ÚPâ‚*ÿjŸ8¯P1B_yîö&¡&€1+±˜ µ ÷]9N!¨ºácpE¿Ð¥Îå½m°JÌÑ[âÒÍÈ,’v¸Æ~BѰÕü옠VS~eˆçMW!NºÑÉ’q›5"'8„gÄ;V½j[åô¸¦’H-¸ÀÃÑt7×£ ¬`š êœ{öýFµ‹¨À†!µ,(OÕª³&¤…Ç$ßc_ŒP¥ ¬üQ?}$ÐAJ·š · …{(bùÐ\.MBm Ê¹>i'é®éÜÊY{³'|,Õ”fÒ’ëœk€’PáFüí-ºÎe"§6ê\OåòÚcŽ]@ˆ(0O²w‘éÇž!_¼ÍHc6]µ`û±G¶Bz£ÄŸösgÆS7?.9RÎÇM”Ÿe@H9¾¬Yß‚)ë:à,!bÓÚ øFÊAxCÎ×"¾Ù7úÎ(jIÁ …g0髚D^¥Lt*TÎr£g9™ºs“*ÚFë}(–áÕ­ç\çºÅ×åêØ1ïu!JÛ7ÉA ~Tò²¸Ý}…,@„DûOG#¥Îoãèf 犜]1‚æ,ŒÉä{Ë4ó³AæVkÏ_S ùÆ\ìeò?]´„L=¬¬Š¼bdU¢aÈ™€<üKŠž7¹†\±s´ ><* Mƒqºvi×_NïÔÉД¡âçE…R—œØn£p˜kÂÚî‚ÊGEÈ‘ÿž˜EY(ˆ—Ør®âˆI'£DHôÞÊÁ]*[¶Ú¥DF„í•´Å‚Ú&¬ï~™kþ¡Q©® JÝ—D¨Â©­q\S¸ƒ ŸØ¹ßGd¨]„ríܽ™Œ÷-6¬$ÙeíßDYPDàa)œ7¥“6)JF˜r]¹‡Õ^ü*àþ+êÊ4/õþ3¢ŠÙxƒóÍ_d•fé7-¿ãÌ\•ýH̃¿®£czJK¨Aj‚É¥Jþ˜& y\y2õm!êøûdVi‘1Õk2ç™Ó5OgÊhÀÕNóË´Ö¹ÿ•lóHz Òù&DMÁUtå ¡/=Óïù œ2a?¤o»«lC±ñáÝÿñx…!=D,¤&ேºË²"]ò/{dôï _=é"´5´×Ù±)$µÞ@oÕÕÙMè=ž‹ÊÕn'Nؤ¸´[-¯cÜô‡ÍE×ú¿xÖËJeÊ—4 ½ ½ Ú??paÁT2±1JésúŸOâ™ä—ÌY±AªAù  ²ý¨V,ì÷*|çâ«F0-»gû&ö­c´»¢ê}ƒïG.¥M£ÃPHó>•è‰âÔ€¦4æ>…Kö²ò’ÂgAõÝ`SÜ£õEØAÛIGiõu2† Ò#îö;<ÖF¹–fjnJá~`΂£}¬Ù š3hgGJ‘E@¤­ýµÁ¤AO·•ìMWs ˜ß•壨JM'áb¥ñ;ûh^³‹ RùäMCÛÆ¡*/ ÁEÊ7òªúñZÙ×öÔmaZƒƒ¦`Øþª·g †vimÈÅ•…ŠD¾Ÿ¦_îä;™ÂÞáÄÙî>Ÿw—´M°¶¹›&N'§È,Ó©–HÖÿ´Ø„¿H®¢à;¸dûþÈaTÒsìÆK8”ç|VSëá$ GÃw¬Âpób™²=Üf ÇR oúެ)ú# Oö’jÌIï:$Ÿa—R3?„øÓ“Y½mY‰ $”4“f}S{Þ]9|¦@ÿ§phâJ¡o“п¢XáûXßOÖµ+]ÉЩÅ\pH bOT…äfƨ_VqaRƒ”ô6 Èë-¤w™o¶þÉ«_fê»vAÿ.d—«3ûñ 1ä̳×ìÜI¨ÞS¿¦{hYžñÇC¨±”,uºGè0GGU7зD°ÅÙ³éñ¸‚€áúpíÛcf±H"‰‰Ït„—¨\¯ š¨Ñ'M/¡¨ÎGOÊGܳ‹KV [ÏMp,3 ç%‡LÊüŒÛRçCGÍ̧þÊÁîSV lj<žÔ-i"éni`CcÕŽøÀ5äì·³¬Ù•÷û!V¯y\<À•wCø”–Å&gô?‰Š“_iƒcH¾^ÍNÓ'´mß凜¬+¹X!KuãõbÖlIò®3U&îÄ«»„«e¶"4fÿež¿M«û‚ÀÆ¿y…Ò˜­!Å6Ð6Çí¢U´à±øu½ï ¹•¸;/¿‘€•R×’>¼vtæ† îä*jlŒÓ]³)ù7GëÕÜaOêÈKúD34òŠò\”£íÏœð¥*wY›Ë+.I_:WåLQHZÖ;Ãà呹wyaoc¨wœ Ò¥ÍíhCa;Ðw}š><6»•F6‹ê Ñlga¸Œ(óš<¯ p P%K±ü?r_+Ï9XŸbßO"½û¬z×Ê«CJ'%{ïÊ+÷£xÙàX¿xâã _V°°„¨eìËš3ÓÆ®ß9%I<(­Ñá…: œ39IËm+‰:îä€I ÎÇ2—c§ñôàÌ«VB_§æÈqɆZ$ê¶±ò&Ü^µÏÔ†¬wð„Þ>àA2½ÊZ¶â¹ÎÁÎÆ’Sµ3ÒuHB9”›ØÆÚÆãøèÚV#áóWÍê.¥‚FÐB­-³²…Ë#P'ß»˜&ŸÊºõ66â=¾eØ‹¬tþÃä¯nÓ¹0_‰™qßÂÿ¨ùÒÍÞÙ%s›`gî©çºÍ„žÏjŒ ̤ÃÁâç\?S7ö\‚É¡ý<î¥O%ÕÇ5l ·£S¼Ý—Î@AããŽ[‡Úáº#Ä—O ”’b¢)®>K ëá,Áÿr°Ó·Ê)I‰ÿΡ_Ã˱槬Íg’T—YTHl'Э[<É]q=…n€TwÆ*ûÇ Ð” ©ê’èù>ˆWòšUu8å·Ò ¦ç"Zë@È_^!<áÐC&~ЧäŒUåF·,H9\Ù&#{¸Êá x3ñfgÊiÂ:Äûò– Hô¶O÷î «|qÆÏ5¦OlÝ©¤WšÆ¶ñÝOU¶ð¿#t+•G}àÙí¨¼–ò;¥b„ ó±Ü\@ûŠ·h*£|j›a‘6?ˆ°<;–Üd\:WuBûZOZù¤³ åÈW‡4Ä>XD‘/_vÄ&±6ëYÐGë]R½h*BH„as'lOÀ0º ´|¶8¿M €S Ž;ëžÙÇåî3|ñÄl‘Ñ Ì—a=y¬¤ô]ÀLî%MÖìh¦ÄÚ…WcjGuÏ’ø¼Qæ slJöåùóçqìKt[[x÷D>ØŠ3ü žÍÊÝaKæ™ÖõyD츟#…¼øä!Ì0ý=Äc%h Òã»ÌÄåDø°¡|ËÄ ìF “„¾#}°¨T­(mî¾ÀzÙrdIxU0j¸‰LEfŸVÿ%¡,-eü ~ÈÁgÙÓ>˜.Á„{T*­=[c¤ßH¸xŸÄ›†aIþÏÁãŸ!»À ÛèÚú±°È2Y”+F qá?"XÕ KÜ èÅrã釋ÌA½F?º§Ç™}¯ õOÔç;¨á\(!¢cVŽÍo2 ŠºÀ0ëÎ,dáÏ­œ$+ŸMS†hÊŒø¡ŽÌÈWæÉq?»x3*Žfê‰:JjlÃõ¹>îE0ûì]­ÑF¤G ,DšFªág¯ ‡ÚÍÍýÎ3÷*C%I½lšj æVèñ7…£/›v½Úþùz[ qßrÔ-|Y„Çy~OŽöZç¾ê-"$ß×/B]‹ýŽD@\d‡.>ïØ«¸‡ÓÿRÝRGêWšä]3Ìh~ÓñOh_}ã5tü†ߨÞë£uºJ¿ ‹Qdê éèɱiŒŠa†éÐü Œ¶§ýæë­é¬(»,Ølz’#&éGZ>ô¦ªˆúîO$«^€ôZçuX•ãQ®,L˜žeû[iÉåùvÁ<{AôZl Y "\2ë­ðÓwü¨[¯¤—Ép‡ÉuR(f/e/ø£H\Ò”6Å v`têð,ƒM¾%9co›õª ù• â,TG©Wûb]Hö‡[ò‡Á9˜ Óôˆ¬–¶mHàtyešòç3h+VÜE½Ró!Xå%"ìg±[âBÔê @NÅÚÃGsf$œe%ÃU‹d7ü¦(!ãÃÂ~“JoO+ÿ PUc}šshLw ©ó}åÀ íÐ[šž¿B;‚°!P#½¤;AœbÏ¥¡%Y sÖßÕ½›<¯”½‡<1Ñ&m‡+,‰›gv91–zšµŸëGr†ëy·¿çn:êœÑhÀ<<åØŠaI¸ðañ×j3—J4~Òs—?K²'ˆ€Ó2‰P–ˆÕòÉÒãò+`Ö©žÇ&=•vά]lj¤§ƒù‰Îý#X©ï}2VÆz¯ç×äûÜ\}Çå|ý…Zjo‚î²õ× !A–½:- eY½}«kg~{9ØáµEÈW§Kå‡+Ç8TÉ\÷tÆ2;–FŒÂÀñBKHÕÁ?çª#›÷­BFb'~KÙ:§Yçh 3´7eu3Õe“1ˆ4S8·_Î~Y'ãeŠí h¢Ÿ’w§Ù"åã­õ ,$¶ z[F·ï‡e€ËÝ ìx±\Ê/ÞÜmf»C…Ç ½u …øÒ¡z ñïÛ:uˆz½VŒ–À42“+ÝØW5ÝGÿºˆéCiX8¬•ö–¥ý(©Mò®eHÏ•†'¯u]&tî½&]ÿTeñƒHûøM¾±khšˆAeI¡Ñ1aé*~M,b4²S£m‚‡3úâ¹ÿõã+.l¯˜îêðÌQö0< `·IùfTcÁMA*Õ¢kH^)qèþý‚Ð+ál±­*– ÂjóàÅM ÛÆ»³ÛpØÊH$á§Sbë¬MÃnK’µƒaù ÈÛ×#=~ã«th…¡Âç»–âmþ¶d‚ã–ò¿ žKáÞì8Â¥›_f†•aÚÊ@(z'nKl û*ËöåSæ›fË .]‹Ë§dÛb= 'ÊlýÎt'5õ^¼N èL6ÓS·ÐТb(ã%t…~$ò^;CÌð©†×n—¥ïÑÀxj“‹dÂØËmád89+KˆVYÍ â)zRÕ_¢ØS”žØ­ ÜWhÚϬè,@æm©­dQ KÐzØSM–Þ6ýoÀÊjJhakö¾ç¹…‡z›Ïüû‹ŽÁ ,Ášîj´¨ wQ3c+³ru7ãV…Ùˆš_H÷E³Ëü•U“ws—±,;LN[}#gv"PÐùŽY,…[’ò÷Û•/ n™ø|B%xd nE½3£øÏüÉ«ºGžËX?8 ¡Êòøy—®»ç@²¿5ÀvTÈ!–fK°Ô£jÅOð„௺¿Å$^Ñ•ÑÃæÖmC?ô#kÙý«Ì,üŸ)÷·ÃõêvÉö fÈ]ÇhAN*¡"É1UÇ~ŠÉ•:5x‘ÊðçÉÏ;î³<×õ$ˆÿpþigù€õuHŠtÅMÄàd¸xÑ.M©áÿâ;a@§v#…ìaYpQÛT¦Ž}õ¹B&QB ³18ˆ5péÒ†ä%1òô‘®‹íž=~쟧³ºŸ¡@ –-‡(*„õÇkXÉò¹X%eE¢°#Þá¿•u¡9ƒQ ¢9*)Oy=qM†³»µZÈiwÝfÕ óî`ìÝ>Aú –Á0þ´Ü^y9ê ñp£„É”+¯ñ-õ³¥Hú¸8ÐB¢9QÂiÁؼé9ÛÙâCù~àOMšbÎPvGiã¦!v•„·››Xg9°’u«sBbè’¯¥ðÅ÷Óú1%½’z”˜< ”,Ñ/Ùyd+Ô õ$S Òq-ExЗFÄÛXU‰ÿbvS"SZ) fnæÅ&é,¾Ø¢ÆŠMR>Ú¬(¼–²Ê1)¤•Ò»W4\³k, CTjÕw×.ÊüXà€%ŒMÅúŸ wÉß.…Ž®RãT‹@C¦|ù¶AXÌ˽J´4¨ã]°`úÓdúÞ×tòÀ馊PèçL¶¶ƒgþ.œÎ·‚|_oý¨«{Øk&ï5@»ETε]ÙˆµéçGþ´TŽ'§cÔfK?àøl2 ed]l^ E4°éç|°+„þKúŸÏVËÅ×7‡`‡äצUk®â0óS<6W,â`ù-6ÂFªnðÌIT­iywJ¨!}(4JjõÕ?ô£õŠ#·a0Âר‘Ð mwäo,m‹m¸§2½F,ºpÁ9ÃÏGeò׍ÂÍ¡3j0Ýx*V=Áˆ^åË„9¤çŽË+û©êê-¢9ÎÓ¦ Æü¦°—è@þrb0@Ò7q1@ñ;)PÛIäw[`Ÿžå° Üw2艮*ß4³ä!ò¨¤xÔå"rÌMh8Bå7†ËW²VÚ>ù¶ƒdüÎÙÚi ûvªÏ¤Ëü–¨Iµö&ÅmÝÑÄ3ç¼!P¸•yDMä\†¡%ñ·\z!áŽÌs1>ëN9^S¡ày¿ÜÄO|ÃßHæ#©Ÿ„ÝV­ó¸Ár®(ìÞÀ„ÝájVN˜p—ôG¨¡£·&¥‘†Ü¨ë# $º6•· oßE½Ã׿ê²â‡ÆŠ`„iÎ p³l›ÕßãÕe<·Q @ã8 Ì/å¤ ðW[§j‡ËµœE09Î*osìÆÄt9lÄQ6.»g ë\ßÊï<å ïYΙv|+ʸŸg¨GZƒSýŒ]»²­Üç{ܓҫÕ&k í¬£ÑÃT¯%€kŽàÁã‹¹Ì õK¼£‡j¸|PÁË)Žž;‡SCJ‚ÄÂkÂŽÀ¶.A ÔT”a¬˜4‹{ZÂåIH} _¿gçß~lÞÄ•ãLVñK‡ß+—«¸¦C½ï›:bò+ý!£ˆö7ÖƒP``ïJå’焃Ôûó»M²¼ýN„†¯ÛW¶‰ÄeaiÑfW­j!=Iל“92šèä;»` ù;ýk”±¥LDHoY*£ht$–/:(­ÙçŽTÒfzÞã종ë¾OõTÊÌöÅÃåà½ma›WŒ£3…ýUÿÑ{CW‚ÌéFߺAé *têÂV ¯N6¤ièºq&x¨_ %Àé´æózœ D® ÿ—¿)3•ˆ;4Ó<^ë:^ìkw¸‚ú__x‹,w¥ø´ŸÉƒUÙ(M§hh®‰„-™Êÿ´(û®!üÁ¯÷œm¶‘gms OW#>î”é*Õ®ÙÙÓ¦’!vCÄ=9³îÁȦýá •Ãf¯Ó*Zã^ XÐüddâút~é¨‘æØ¾3I~âÜ8åîRÞqý˜erŸÑ­©÷®8ÈûÚkfx^‘ùl¶?Ÿ$@…XR‘6ºžÑ² ݕǢ»Îh¡»âL   ;®G°;Ú@H³T¤öèOrLG›päÞgt½½ÈøÂ>'…Þ¾á9Çz9MJ±äü»¹àÞ_~ÈŸ‡Ò޵@Ô)H–ù¶¾šÏYÌôeÕ“!ím /Eˆõ=⦩]v›ŒmŒLKŒÕ„i£¹\~.1ÊW‚xóÄ Éóe‚Ý\Dú9“ÓÎïé¯_§zi…ø¦“C˜r>Æ£ÙøN"Y“ÏC° sÅ.nêy,+ãs‘Åßc„{‘>&@Â-b™›ñìç†K–ߎêªè’Ì#”ÈYng'ίÔAã'75ÇãoZ•ˆ0~‡¢>3ÅS³„||–¶MC?ff?/H œ[ŒÆL$L!÷¡±{Ù+×ãå«fKx¢mèeDº }@ÐÇ"Ëpó¡õÐ{œ3£žaæ‰,×¾"‚V 8üU~„¾±ôÿK¼+‚Ò/Äh{J-úA­™„åGê…鱺^@ü‘‹ÀQ¤ýVóÌâaæW 2 Þ¹”T£F9Ç s½Ò{Žó"ðRóå#CÁÍwqu$rW+ëDý™V!“ʘŽRºmÔw¤ O«Š¬È1º{÷‹4VŽù®FaT“9µ•’•r‘I‘V4•7-JQeìêçŸx¯sŒñ¥+¤Ãw$D/]gzœæ!…ê9 Ѭ11ë‹£‹ªþ—c×R­«/ˆ=Äõãùè7h¶$$oB…$b«póR“Ùì|’˜k 3ƒuüRj¨«? æ¼y»Ÿ¶k¥ôWΔøßL¬$^ºì…Ê0Ñ[–wŽƒ#[®°+í6ÃB!¨ a‘eŒFWaFå§a-S'#ÿÃÿbc¢1 GÊxÛ´,H˜• F<à±!îÎÞóñ¥Aã1]U+ò9tÿ¢ãIä\ê°óÿúqä½,µÏŸ ñ»>gèbößœìz*‰Œl4uŸP$ ›2býàÁk«£PHH#lê¹»MJÝž¾{Š#`²3V£’u¦¹öÄ[ßÍX™$PNC¢Ñã²cÂõ$ðõI,ikÑg½Ð~²v\æ7Š.Ù×áí©É§µ.ÐÝÜ7e9e6ª@;´¬‹ÂQµ›}þ⯤­¤»j>nàc¨Ž…?ÚÀÆc ý8 èÚE§p³ûëe‹ÜOìò ó‚X¿ _»wϹɥO,6/!lF Hìµ<,ºIµ;UÑOã·•>zÊ’Ž#°ÖÛWûí>—8œ™žÏݳôîMåð.Sý*ä¡1õÊ‘­ˆ¤•q³Ã„Ð]ë8D‘ƒ [€Ã?nŠL¤ w÷Á²(Yмè#ãjæ,œç8å²&[™/hd-¹éç vQd 'åSÇL2…à×€úQÜ¿h·4NAÌ<'˜¯=²eŸ{·»@ž¯×'×’wôÒÑ.ü#a#u`š¡¥%뺶ð ð ¢ü… Íy ‹aI­J~;ÒÊ0»±µ?5xþî™öNVbÒðVÜ+ÄòäÕÊP*É?z¼+0`3•ô„ÔŽ÷C {â ý£&nÎó L¤Þ©ã¹´Žô¹UÏ…8¨ ¯«ª-ÇÇXï­poçÄŒ€ ¿\¥®åGV2Z‰/õò¬WјI’z ·¨Ó¤ÐJz$º{A p˜j]×}‰>6Å!qB3;4÷Aþè²Ýº»%›bG²•ÁÌýy½ÉRÙ@HQdŒ”wÚŸ³D_’2š’)~â´0M)¡Ä+l(“ƒŒš•J«eŠàH¼Ü[S¾‘ZÕŸê³êo÷¶ÙØ7ü¼:=÷[{¶´W׸_¨ó>ãÊmó~ <ìV¶7ÅžÀÌÜ_È#Š]ÇÚ«S‡å•~¦@¯ýåÄ.@aS~(6ÔØj¡7¿o£ ¢»êG^ .ICMÌ*2‘½£ü½è¬ÒFJn ï•ÕŸuæPÖ*Úúo¿Ã­ÿÎ|C£+é8Ó²ÎÀ`uV†®TfwE²Ú€ÝŸÉÒ?Ê’KLå 3`á4ɧœ«…Këè<´:`‘ô€¡$Ÿ1uæYdø7Øb 0žWQ‚¡¼3—‡‘œFva[ÕTéå¯×ÒyÜ’xŠŽIöŒõ»•¶¨I–&1ôÞâNa ¸P—¾·`ؤpy»µ<ìUsƒ/H!r 1R©h·É}†˜À$½vÊ&ÜcNÝ N⬠À%ùO© ´·wwävfYÜ5ðw¢Îÿ`~VN`Ž%Èg-HEÔ¼ªÒâ°@Ì»£2FXC _p¦f'GÔ°UÛçŸjí(Þè·À¢myLr*ŸOé?Ícœ³_áz\ ocóp­IV»ç[qKÊVŸ;n¹’ØZgÕ}bÕ (á„Ãmâ1<=Ö©ï6 Ð8ÖÉÑ’´ÜðÌêÞ1HþrjP-ÿ-S-Ü6ÄØ7—`ùì¾x~zh0ã =n’êŽ&‹J‚Ëy€0&¦OÚâ ¢´\÷ÃT“³TÎÞGs¨z@oW¤nA6h[cÉóÁ&¾§8Ôs»3mpí0Þ]Ä™«œüá b©},)FÿMøô[vPç;šJÃFÖ»lD¥è!KvÈ¿©—]ktˆ"¥†¨|°¨´7w…‡”É)ùÜa×ð"´d¨ÕFÀOQ3óZ¾ÓñlÕ7@'BP8‹éõÈ×=Ï×BO²ÇÁž&õE³Š t‰ážt7*µ|¡\=ÃlúfÌ´?ØÙV¿¹mAÓ·ÿW¹n|¼æ±·±‚;5/.¢ÞÈJ¯½’#Žx箸 »åð`òn2ÔyžHòe@[Gc=½Tr6¦S¿åhÈú½¸4GÆ –ÃŒ‘´À‚t ¶¬f(ò„wRa€1ÎÑž0bZÙˆ#\œƒ%¾§f¢¬ß×Ŭ—'=WT²h<à¦:ã…m‚€š@`°b“n²ÒÌŸƒ^)cÆgÒ:°{TüÉDàØx>ÕÇ56Gº+8Y^¢„EµwSZµ±NP}NÑ 說Éåóí] Dåãô‡Sa8zñ§øÝDð!'€d|–³ˆ…𨵗Ý{ 3 ‚*j<¼êëñ7@¬½‡ ôÁtŰ,;\úì=£àÎÀüîµÇ5ôÅ£â  r '¸"…ìß÷ 𬘿×rȶe@È`¤î%€T˜#·}ýBÀ}m鋯™TzäìÆãC+»yq]6[PIÅO:É%x?Ê@’>ºßȸ²ûßÑ:Ô[aÈM¡ýˆ®K€¶T„Dj4æÞ 0_¤î—!¤'³ÖÅ;ûg ©Y½ïNÔÈuMïœD² >‡œµ£„a³LŒ›GmpøÞæÞï]ì°|ü]^$×n×=&÷uýœ˜_p"¦¦¼²ŠHÓñ¥•ïoÎ}¦%ñ=œPŶáaç/¤Í;Jñþ|‹!W jÂÝ[F¬*Т¿{(›iøn2¹±ãcê5[vRR3m«D1¼çGÆfÛøR¶FÏ:#§ï×k(×`‡2 ®µ:ðØ®øYÈlOâi¬• F_0ïð1ža×mQƒ'ùPÌ?hòLµ[vŽ ]¤ËsÜiãÙ¿!›>Y¾EIZ¤1Q˜§F„?ßécHÔp×44'Z"+è´¸PzL¡7·S@ g"¦•ÚàFÍ&%»m4ɦI§3aV¨/ë@ U¸ü»¨07ñÙ=TI×™aO(ùÙÂw^ FÒFì<\pï˜$H€‚¥SfÂÊoZÕ»ˆÞÍí¤’mN wß‘QaË:žcßý¥9z­oÓµ‘ GJL3|ÔtA/þ‚Ê„íÈS¡êo÷¶Ð´˜Á\åU¥ÃŒ^„OSsÃÎ oˆ#tRr½F0QsUö)ýBGi²ˆAò™úe9äx Å žîö«!~¡÷Å{Žþ§©®ØYxÓuJx6$ò©'ÁŽÓ!Ös¾" =â¯~232WTúª aµqÞ'Ë`…¢MZ!¶27">lšJ_u.š|é" ÿ£êlšZ7k ô](àoTiEÜÄóÈZbUâá“×9!ÀÎw_†«àìJÐx%C<„_»ja}bÍ;%_r‚²hpÁ¥I½²åÏ-š»Ëðê  b=°×¿YUùK(4»‰0r'D=–í¾-š˜×VDP|©¿¬Í«„g_%íôÚºËCÚ › L·¢#Æ:‰_ænÐ×­LüìŸ~hdI„× ]ãá¯NG¤”ÛÆ»Þ·Áð¼æ97vô¡^[J‰Õç¼Û] hÅ‘ŒdzÙ° 3Ñך—øR7ÞiíOФBÌG¡¦ŽìM¦!™©ÙŠy›‘.T ÅëÇŸKO3ž¼ˆœ¹ë¿t̨(· ¹¹ˆ«•oàN¬þ¢³ ܲΊˆ±‡e ,S~Ô?…°Ùé<wø>¦O'wã"œþ{ns”Õõw–RºS¬_mÄÂÚo´µ§ÀPoÔ”7«M¼-“¦ò\Aií¼»ósG°P  píOè%/­jмÒ=ÕÌT~™_l £PBÞiW÷ÿx×&“èØQÖo´±M9{¤ØÔ|ý|RçñËB_ ýÉà¹Õæ2ÚLíBq½?Û‚mC]T(f`¼/h$3’ErJýroB=îÃ7lÛu€ˆ¥¿8x@’E€6¸•|}íóÆRhwÊÊyY3þÁa-Êb•P¾­™ŸÏ‹ÂãÆ(/¨TÐr§îZwhFˆÔ«ÃÚ+2d„w´óúáÁ"‰„ï:£ÿm÷T"¥Ýí%ÖŒƒ*ù9c{&|lܸD…­rEˆ<¦„b¦û¸-.¢;ù zX¤Ì€ð—`ªÞGÌÈØâ>LqÓã ù“ñ\[épÙ¨Žrùñ_¢ÊLÛŒwîoìË.²lÖªJbÜr_=ÌæºÖ‘Aª2ùãÔ9TètøÃ7)mÞÆ”?´SÏÚ˜) lÀ–åLÈîr¸žnK·°sîJ N©5ýš?¯~ kø’ƒår­´ˆ)iro&¦æ×ßh0&¦ˆ4ùÏѾ¹qúVff8ZV¼{Š'‹ƒÄ‡#Êü“d\¾ÀèGæ»úo;å$ñÒΚê¬òœ 5©˜’)rM,óœ]Z.NÒ­Ê3ËÁtó?IñÐG8B\þ³™Fºéþ~ dE­ºsD©ÊsÒÔçÿ8µ¢–Õw‘JI `›«ùûp›ý‡N˜#Í"óšÓ\ÇJÝÈñ°2¡ñ$ 6vã4Ö|Ó=˜ ¡g•ð öiŽý¥.Òú¨Æß½²;gyh°V9c5NðRtàÓ(›U”)ýd  ÇöïR7( 9,>@?ŸYÊR¤öΠµ­ìvXá:éÉF»ze°±!u±Êˆ¼º~g2ÚáO=>ÕÞXmÉÎl™X±KY‡çâï6¹ýǶ‹Aœã:N[ÐAcäe1©k{õÆVù ÛºLìšË-lÄ'åÿðE¹®—Gsº*¸Å}S£>ðÔlÇÒ§ÚÓL÷c/ã=u«*Ù»I ª"²/™Þ¸¹ S²?üÙ^ò‚R'VTMªä®ð[¾¥”ÆS0£±=*2Î-»ÈTsÂØ#)y€ÈDñ«õV!h¨µô1±åR½dE3˜q·ŸÉÁ ‘nìˆuÍ>Á›‰CúÿBô'U”x&eV¼ËTádD"ßæ·FôérŒáØ*Tàž£n…4hzŠ0+7õâ¹HªËv¶+ETÆ>ésçËì¬ïadÐÃ÷ÔXàÆ™>Y·=€ëûö@Ù>”3!Ú6ƒÊ_Ccbðú·BeA]F*cÇð=á{ ³ËÍçãZÕKo-[¤žÕ:‰H¦[I“<Ôr®å5¹êKn9D1·¦3§=‚ˆ‡õct Å`êæÿµMH2!üöÎÜ?é&zßÜb¨¡¡lœP/ݶyn”씕ÊÇ|0ö¾vå³Ï%¾yüŠ&åÖÍ{ÂBð§…14WsÂû^òâÀä^ß,üe(àzTLÑÅ€_30 .']Š ¦ßT©â¥ûtõ*¬u[ªV]Ç3ãÁãáìù;ržàØdnOÈ/™k¦—û(å(ùTS#¢¤uI{ËÓf}_`nˆ¼†Såx@èÄR2ÞMåý¯ÿU48¿è´ÁAèâú–Býݺe`— yÛëN¥´í^E,vσÀNÝIÇÖiÒºÅS½BD8â=S$J"OXÿWäÈÌ,žSªL´JŽÃBabhS…ép^/6œn¼ñÏžH .PÔ&¾¬‡¢È†^ÉqØ„²K0B¨ãa.}H!¥ß8x…í æa„‰/AÚË8б‘Gú(tÖ:2…lÂc¢”f$ç=Õ8B&XB¹Zï¬Öíä€EÝîKG¡ïdÐh˜¿¾Ý¥µî¯ *B%åi”Ç®ª2îÉÒ‚“7rÞ÷÷Éj¢izž¥:¦â©9Jb!¶m`1ôO¾†›4ÙÙo B*; hcØ=™Ög+Kgý:¨ÖÉ_W¦â—g'¹  e{ÙNIÇÏ2»ñe'Ë#Ú®ç¯dˆqÎØÉ‡t‘Èy¶Å³MèuÜ5¨ÈCL²jªå!Áv6};%Â}Kñ÷óH¶3³–¾pŽÔæ ²Ý§]æ}¦h/è7—K¤¨U@‹ ¬Ï¨/ ò#Kç-FÊóÌÊöY÷Ébèäìä9ÊäÁB9BføÔaïHx4sO汸 jK$9G}ʈ*ò'`žÓ;®»fÉw÷”¸H’[›ËÿìÚR.Æèèçù½»"íètŠo@§;ÒÛ˜éz¹I~÷¦âs0s—žÎyÍÕ|~€ÓÜÖeÌ6«ˆ:™ÇWŒ»…l“­¡Œìz/$˜DLpÁ6ôq?G?µj¢H0÷‹–3S/;±óœxŽ#ô s5ƒ®¤ ÝÒ‘>AuTvêR$I¤«ŒÞwÔv*1®.È¡—zi˜îp{H)+'Nx¾rà è¦Í®Ÿ~¸­qáÏbÓŸ|XÂ.}Ç¥t“ChíVU¿+Ê)í#ió) 5Åöë2CÏÉ—$^7ÙÎùíì~œN• dJ%x©Yȹ)¹ÈØëC sÙ^Ùþ+Ú׳9ìº|!á:¨Ë¢¢ ]T»î9x'Ý=ÚýJíú;à ýë;(7uïÉí¹7¸g>Õá6;ìè·ñvGX§£”ƒƒ¢þ݆/“°:$ÉA„¶Ö¬>oGèæ'=ÏÊ¥|ÀðD¦ïn=§üÃ,-nÿ¬!ìøÈ?5dã '˃HV¢¤mƒtHã\ÓÙýl´ýaø{Q³ £òäSË™™ß;BÄö€F>kPR¼Ðã"Ï |¡9ÞHRþø³4­$æ[ÅËfO¦ê˜íCw%@·†Óá‰m»cëD£ÃÛ¶ŽmÛNÇvǶmÛê$_OîýßänÍw j­½VÕÚõ>Ù£ÚHáæQIíßèe(Zµ‚=¤ÏâD†/¼Rº®tBdzúO)›³åÏYb¡ª[S¼Æô>|Q޾Upã²Åuïqü¨pgPÓõžEûÈ]ÞnF›$&SrÏÓ$ã5\W%…$xJj“2ò­NX¬¡#³h0椬„2À®íèC¦;BV߸§í‘L»büuçñˆ»‹ rºR f«÷gìñŒNDÓ“>kºïáÔPÓ)}Êœ²æ:|îÔ;ýöžê-I“¿PU9±…î%¹<õiòPØÖÑ1¸©=¶vÂG"8ƒ ÷wÖHbKKþnÉßf1ÊßD`Ö0< åšohÿ=§žE&¶JTŒþµ/Ü8ÕŒ1èþàØU6=”—.²YYɲ€Ș¹^^ÏlA,ŒFà Y¸ÙNý—ï mõ]\á ¨Òzî¼Xx(§ö>ewËÞŽa•ú¥Þæ«ý ¤o8Ó¢Ú:ɘqV/rÉe:*ÔGŸ>ÉA“£«Æ»†¤Æ«^agÉt®‘{…œº`óžÓ&´(pAÏÕÍÎöOˆw­}ßù¼啼`ãÈ0GñlÛ“}ÞeÖßíoHÁ¬/ÃÛP›žªLîp¼¾¢'P1'l·fsÜpvc4å–tr޹ŠÏN*CɺÍf›Û;÷LYcɲöÁÍj~Âs6 ^µ)žŒI¢ôæÉÜ2f´(|@¦…¡ÑRH‹–õ€Ö°T<Ï¥/ãKB>œœ Îz®ŸÖõ2©É ®øpMÌqÇâXVSL•7ïüú€kg¸Ì•°e eâç5¶Uë@û”v_ƒ‹:¸?ÝlsÔXà”Xž£YQS¶–t·…´òt%õŽPÓ¹©&u¿<3¥Bß³ÛqÐVd¼«ã&þüæ]]Å&Ö÷y…2êåË3ò¬×Ù1Þv‘ QÝHBÄûpÈþ¥ÛCÿÁÌ(s†Î&ŠìH@}ó{UßÜ{1g‹¨¢öŠÆGüÑ…pƒBà]ƒñòù\­$TßñèÛ·X>Åp>U±hôßçöþÅãý +@Økf %ÖÔÂ[¡›CùJ«>Æ+µFAê ex~„ó¢~‚æÇ+õ‰ßßëñ±¦èU>Y‹~®»àÁ ,1¼¢¸B¾ 5 éJ.¡ö&V4¨³èä5ï.à^ΉޟóÏÄÐÃ6÷ƒAÒÅv οç°ÿØK•ͳißžVÎê¡ÔÇVe:Á5×(ᑦ íß¿ù¾Ú}Rï<{åMJŸ³ òvª|T/C5oIÞWÝSqu$xy±`‘ï*а[§Âòj½M ^èË„£M 1<vÖT‰ÈMév%g¢,c5P<»4-ÌŽ `§¾gdãyüY |é¢Ë½?Ûe· "¿ù;¤·†òÐ4€‘eæþä>Au(÷î V­è†>ø.õÍ¿\ÐewK(Õí׿è•㮕_ @=o™O¢íÚô–DXSœnÊ^þø¥mð‚uáÛeS±'z=׃qÃ(ƒ íÝÌÞ%9»¨«“–޾o%_ÊókR|O ‚¬V²;,±Ò?oÀ~A“²ßyÆ~Êf­yk’Z'µ«›[¸|õ”+ ²ÛÏ—Ǹ~ïëAƒÎÚHà ñœ²_ª'‰;MÜû2¡:›cÒІ¹ ô†‘çs¦ø;ÔÝó`ª‡(Õ¿¯ˆ•èÓh#ò ñ•UpoýFZÌ^¢îu4@0iúݙ߾èK_`ÍÉ–2ðEÉÒRSjK—b£¬åïÝÊ^½K¶0E·É¸'+òÔN¦Chžth|n"Ò¦B6Ä ¶0»3uá„8ýÙ¼ˆ¶!nŢº…xþД¨ø*lì W¥Aš*J ì¼mÈ)2ŠÑ]Iå i >T]~4¹oZîôæòOÂ/>s÷°<)“ͱհcïrEeýVÏiÕÁof8ôÐy`ÊÚ>8E Ç€Ešn~˜=xf0e„Ñ•Ö#qëQXçØSÄ”ÜtE€rD}ÍáKn9\­¶š¹Ò猑ϵ‡ùPÔòoAAWgæ¬d3Óá.Þ¿Ö…Xa ûLò1&üû…”ŘJ¼ÌƒúÃéá+ël@Ê“^>IäºÀ+ºß°DâÈú tqÍý×ÄÇIaïÝí¾då 5¡ÅÝÍTr®œ©é.ˆy¬;ªx™þͪ-±ŽW„ø-8Lô¢L>šÜe|n°iÃa§ S[ñcLSYõ0'ü¯“Å)½YHÙª˜ /dô-ú©TVŠûlö2u·¤š(R@ª‹Ù³°.È 5}‹+æv‹gôOmWÌñüó3_hïúLoþ0¡krx«–5)éÁïÀr¿ÚH!3¡W u.XÒÀýaªa@GV¡Ã¾ÚKƒgîò~·ï'Y9&³D4¾µ›·z‘ÜAQ‚)élzäm÷ÇÆð­ˆ=U¢nj@R¾^$‹RÍ3SÈßeTIAú7Š·ô¡d‰m&p½EB(1ÒŒç–ìKó²åét">æ¯ç«âÆëÝ¥ñ®_s2P*Û Àu™%tÊbc‡\QÃÚn‹øÛìqµ½+xXØ­¶Â—8_z£ß}ˆ¥Ì6³\˜v(ˆšø#¤NW{ˆõ!Œ@ÈVj^ ² Øhö©âï9¤; jkN^í!Ñ|¸ïN ÄùÎñàÏŸ¼0Øæé ÈDï~&Ê¥M‚:bÎj»z¢·?Óð+·ÿW ^]âbh}õè0=øífäT&Hpç^Ò¿?)“G²§¿ì;cK:æå$ª,¦;dñ×…àÛ˜õS’o?×çpŸ©O2öߣh•çø”¡GâÌ窸o9pïëº ÿÃ)ƒïÂv=2•'…” áÒQXG[Éþ„Š=R‚¸͘l îÀóÏ'_å8t>Hàœø÷ À©¿:vý³.fÆÜ¢³zŠjȸÙàüF„O³Ú8ÞñÕŸSGèx°?Ø’ Âhú P+µõ6…ŽfŬ°ÏT7´×ŽÈæ²Ø£Ñ_[/´—ÿuÈy²›‹‘f(­„ÐÁÌ!€^¨k¶Ÿ*~jjá&™ ¬¡#%#0aðÊû3Iš¼ßðTqn+‹çÇlJ«Zfù³á™Æž$ÛAM~ó£gGÔ,qQE VÆÓvI¯]úê,kÒî @ö2JDà°þiéaŒÓSl7V:þ+Æt¨7ïùWaü-©W ”×möïÓ * g–Ã0^*N lĽÁ¿4iMËîSü’Yå=îÙCƒŽBRû|Ç9AÓÎàV@VñÂ9ÅW¦Nô({ûaÉJÜ7ËÄ€ÍZô÷ïñô05äÜäÌ—1‘Ðó¹fÓÓµá~{(¨Ô¯§>§iC¨ØB‚"&è¹ÚnÞ ÔlµS‘\Y˜Ý…^¸šçC!ô€;‘Ť+fxµ‚øˆK¬}uÉ4U¤©‰O=w‹¶KY¬ëwÄ}åö$x­„ÊSÑ?ì@ˆÃJ5^ΉI[é()´îÜEEºcÐËÇP¤ÇuE³‹~7å šp·KXþù«ˆ²?\Ä(±v=nC§'жe%ý¦ÙÓË-eI™‘ %\„•1Õ«âÇ…Tk2ƒ%“iü©Ž±°‰^í°™wÔ`x³ÆÝ‰ ý$–Ò‘Oº±û*b¢ûŠ(¤J»°ÜV‡P0äùª›aÁí1[’˜?ž€QK¨"L>Úâ1¬Z%Á}œaØ%ÃO|ùÌpÊþ$$Šà9Nb‰†¹•Öë# {4#îænÚ£uu;w+ãåo€šF7™7`—HcJÒÎÔ/,%+v3ûÙàcžŠÆÝy‰ÝÎø< ÏñQ}ί±Ú_ª•7Ò]H&Š~ã—öB¬Ç‘Jm@ØaqQÞ—#Rë_[Ç$¬C¡gÖ¨‹qÚØY Â|Ë Eƒw<ÅÚ5:ÓAÙ6NO5ŽtVRtOØÇ…¿Îº“ÄKƒiÇïQvÿ@wø-wÐiË™»¾?ª‹º @"IâÁ1orâY©Ýâçý/NÆç !#¯±’Ú ³¥°jïÅ[¯¸ï3zÀ„´°¡$Áxšâ,©}Z២oÁ\(47º*IHE‡x߯—-ÊA/Z<ð–V¬þLj—âªñmÿËKN}ÊsyûÒŸu:DfЉ¦õ·FÜ€S.¢Ëys^ ¸nnms°Jš^ÈCN‚Jªø€/K ©¦OeâãE¨¬¶HzuV× º¸Æ¬a/…ÐK|L t׿a\t~i±WÄŽ¼sWr´SÞ c>U:} ±ÿ»÷oL–zèo/d-ÄÇXÔ-«a¾ülù|hY†ZqøÙUB‘oð €ôô(¶þ“9…A#Ôùù|ú–ˆþ›=øßx_AUsCð·¯;î7J¡-£øÄ£ïr¤éO|ÕýûeËÌclíÀpî?2‹—ÎY¡¹l[êv+CGçD‰¿ýëêdìs‘5ƒTÕæüì=¸?ÞzØÞ'‚ý?ÛÒ£H¸D«IHiºC{¿¾p¼Šºœc’j/x3ÍUëWGê£çÉP´‘*óôòÓBçža•<£Ë‚9‰Ñ·œ §~²‘{>P¥-ðúoÎÇ|lËx«zi zþ;ì@ÆCßhVXÌBh\Rø-üì‰ÙXåP§¼‰Õ<ÀÖªVë6œº›é<Ç¡Æg-{)Q° ¾ØËàeaÆÛªGâO¿BVÁ 6ü1\tC0å|i` ± ÷°´Æ’BH‚jõ•Šebš¯RæÇy¤”»ƒiô+ê^Ô ½ÿÚhëJîì~ðƒ¬—ÇœÏˆI;„þ×n ¹YœÃ²7Õ›4ò"»Þæáx8†X|޶ÓVÕªqøz]©º8—Ëc³:ád CÄÄÄûµ¯Ó‹hï>›í§gç©Êü%¯x Þ.' n.«.áÅ’%ºjðg$O2#ù̸/ jÄ­¹ œŽ±à¯‡“ïOµQÁi ÐK©2ZoJ,Ÿ© e| g—¸»ÎNædÁÉc DÚ+»gYSg/×ÁeI6îOŽKiµåí—‰^cæ†íHa!È7‚9Ë©™k vÉ/J'pß.§±}îaÔÔüË"”w=mF‰Œ©¬³rêÓËb“<ìégˆutBjTöÁ)ž>;à6õpÛz÷$†Ÿr0sF›«43Ïj}à`H8ÛÇQUÊ|¶î[Ò‰\¥k<ÿ©]߃^è±ÕÒËbÒ”™þô[DBd=\”}o!(s7:>×Cèˆ7SBÞÆ>ª8ÆJàw!Ç#š×ÈâØÌÈC¹—l;J<¥ÕßË™ÚS.0¡’¸_®õý‡~Y3F*z"ÿƒ‘ÄÂ\;VÒí`t#º­=6t¹óãÊcþ²äý"GT#W¿¶Yô@Z†g!ú L Êá6Ó6Šis×Ô”³³5|Lï$WÚ³®“˜Ò:—Û g2&Y¿æÇQ&G¬kBÅ’R=E²iÏK›¡Ù~¯®(?¨T‹Ò+r=g¡fkð9 BÏ‚g2³á–=e7PŽ­Št‰®?™áÈàkw;äá½]¹ÞøtN´½/µ-î©üŠ8”þ°.é’ªú=ÖͯÙ÷r Ú1rdbÂÂFÈÉT¹ï®Ãt¬Ã!§ô—±îÚåw A¾Wæøvô:cÇ9¢¥ ½Õ„ f(Ý÷í:wåyÄO)£•C EÛåêõ$=Å—ÔâSÞ9g–.QÚ°vHð‹l<%ÉN„¨×Ëú:Xn*Ÿ.L)èÝv4¤­É=u N­JüIÉ®"ã0r?Ñ4W!*£‚Œ½å^cXïtïóß,{6¼ÊÄJÇÅ8åßç3™ü»i{aŠþúÅš]¼^¶ t@C^ ¸'1{ËVF¥+@_ï»ê¨ˆ©ü$——ôÿ ‚à,t¯Uô­÷ñCZ‡Ácõô©s†Ïó¤' ä¶­ùݾU!s{´.ÇaöëÍ%.h÷…UÁŠÅ%ûí±B@ÝX3`o“‰æŸ«y–çÑàµÑY¨?°–‚wg¡cŽ›+OÚ˜ŠŒÅHÆá™Ýì§¥&É\8 ~þöÈ]–àö­'[ÿÞÈ+©õsiºø,*ž…”Ý(‘áîv€áA5ðAÂãÁâ¾Êì™SìT˜³b^ª@d•pº Aby¨ÜQÖí K_~‡ðüàì ’ø›”ƒèÞlp“;Õk¹¥ÑåÏö´˜|ïâÖÄíÈ›Y øâŸ;–¨¿ª¶°Î×eWñ¿˜}`G‡£ç/\[±aôÊ‘‡e¸EÜsñåÒC¶õjpÔn d‚—AS‘@4Ô‹±öuRkû5-E¡P®Š@ ªÿ9éÎtF4R;\SÇeÚ})8f¢ÄÏÔ÷†2èf£Ž”ÑÇd[ÙôæXxÔV¸Ó•¶*ñÂnô9Ž|'±«¹Ï¢ùtA\©Ð>ñÔ77ž‰0i(Vxúñõ=j_Y²ÿyïvÜŸ:ÁmÒ¦6^¤TK>7lìµaãC'‘wë-ƒ–î1V¶üdH(Zà‰(—'óç;2Í¢¾„C‹Ôk›L×HqêÇøýö­³y&s) E³3Õñ\‹Ï x:L‡šé½2U=á„ØÊŽ¿a2yuòð–1gïƒæx3ò8¥àà¬5œPárhÖ0¤¯D×î¬=¤/¢6Èw°¸¦WÚÕ«Á¾xfý+î“ÛÍóÓ‰!l­óa hU7sÒiÎWsÁ—o©ÑÖfo€c÷e7䢤YóQUÎ"ßœ†$2Þi%ñÛŸ¿Ø©¾‡]Èæˆ¾‰1ÈЀòn$ëߥ„é ñBÒË…Ü®ì‰3Zº¯´ÇVš5;£5‡Àd…jÏ­†“ì°aíQ®uJUfýk½ˆù<ã§fÑŠpÝÎ\ˆæûŸÄ'“Éc™Éý©DAφ‘£Ò#ÓÖ.*Me¼z"-Õ|ÝÞjöÁèþžÑ~ õûÑ?Qð´Ìø>z’‚–L:`¯ÓArýñ¶ ‰&n~‰7.ÂXv²„¨3cø3 x\²60€ ꈑáñ†ª:9p­yC â$‰ ”^¬(¿Yfk¤É[¯îÌzë.‘"£%t.Iæ;}¹@ÞDB Lüg’Lñ¿13Q?Ùâ¦$0ýê£ab 0„Žú¹v× &ÿT6¹¸„8' ÔŽ}µ›ñ9ü}ñvz?AÇmUÕ ÿ—®Í¦ùiÉ_9Þ‡ÍhE…$ZêÚ8?2|p(÷)mtùCñU|rö'ôqSU¾¢,döñ–É‘ìQ.ÿÎd¬¿Ÿ>)^ÆYÑÝ£¿ ¦{a²¥7OÁQŒ›ñâÄB¡ên\)M¨AIi©Ý™QÃS. ´…TœT–`œý;ÈÃêÕ¸-ýïÊKçѳ]*¤È¶._™“k‹ÑSqør ÄtÁÆ= ‘Áãì(²ôÞë!4Å…ëÀº…ãÊcŽ«’—[C÷/¹/ãCó:½1ŠÉÎM¨Š])”F¸® —FôA[¼Ÿ°l¸¸Ìoo ´i–æó¹ÅÐüÂSëoyÍQIÐågAµFìúø¨2?á‡4ÂGkþ‹²I|ÈÛißþ|¬oR·÷°´þžˆáö“jë±Ño„›¯!Ä\MWË_Oü)2ó‚6¥,Wm¢$h»Ò¯YÜ›sûY¸ööçx!À׫ôÆÌlý•EÄ f ÑbŠ Ü­_Üå'‘ÝÙÄiÝ:Ë*¾ƒ|Ü-v’KW2Û=xmv½½÷5 ®iÂeEš²ç8 <}¾[7¸>cž/n$¥¢y “™EÜújDî‹õÿ °‚kß“Ò##‚{~ä>cF¥¼g ü;„ÈçÏ‚{É{Ä®–f§ZÅŠïµg²àïÀ'¢•Å–UÕÖ#“²FçºÀÒ{3E13–Á–ú~¦î̸Ó;"b¦¿Dæ¸#nê§6ƒÐŽìÛ,îÑk°ç¤+v€û+/)GçÙñIsjF`…8}}§tI"5Qˆ5¬Hƽ2y%;à~55;Ç(‹þQ‚J¬€—¯ >ôf½Õ½uÀÒiñaoIdôIaúŰrPƒ÷ð¾Á“ÀÔ¬Í %‘ØÊص·6÷Á»È«9ÝÑjÜ•Òʵâ¿ ûeÍŽõm ÏdÑ÷öïñídØó÷©+œY“‘Õ]'çeɶI/œ‘\Ù·V‚ô€ü¶“çXvÛ]«‚†»û^’G–ÅCÞ[Â!Ênø|•[ÝÓjüꘞ)¨@+®_r‹®Cª0Þ`qô²èÕ]Kð£aOáýÜuô»c§câeÒà\ Y‰ŽkfGöŒöVjx 4É"#:’nóð/À;›yØ‚:‡ð1úX.: 6šµ”tß;ˆ._ µ#Ѻ—Ñ`)Õ¾”Jv ¦Ðœ–L‹ÄÏùßðl¥;Vx®Ž­[?œëëÁ–Øù€M–‰Ygâë³¹Í+F, ÍÆObа_ cXˆÌ‹½‡umC[¬i(űͷÄ_YŸÇRÍñÎí BßU½-RŸ`k¥ƒK›¶µŠecÁÛ‚…4âñÖC.LŸ‹GÏcqKÞ7'a~Ò “°¢sü­Bù?0ûǽ G]…͇©™®<©”Ë \®!MœÑ´S'R¨’7I)N‡ª‘r¾}\,›ëopðß>.ÝÉèjÌdÿÔËõbš–àÇV€·çøÓahò³å´}·ÚÉšìu§Žœ%¦M·u7ï×f‹œãL4âÊ l‡ŒÃái·ÚŒ´þZ—·ÌÌx®ÜQ¬ÓIùTKñ™³ë>öú1¥ô}Rñ‡ÛFáwÊZ«#uK,Àö ‘ÊCÃG¨Ví˜Fqv¢~Œi‰Ø‘²ê“³±,Ñõ®õz…ÛQ´àW´²HÓ¨WOózòšCê™$‹¨àŠ˜/-ðlŸFMCÎ0ô„0”ìĽP? ‚þ)B«V޳Ƅn›&o‰k(¶c¬kšÎ¥ƒ0] iÑÍûñMEG_èÚk!ÊÖX 5UíXËI;}ÝVK(è[rF6G5ä¹õ§I‘¿†Fè>¦rT"cw¯ü¤[¶õ^Æ›Z \@+ºÊÁ’ŒIæE:Æ—õ«GFÐòÚ‘úñ"ÑkÇævQ´¨íߊ?Êy“ÉZ‚8ô­âzK´Eyï>ðbƒÄ®Ò:!‹7\T¢|’@ C±×X ¼`Ðð!Þc€2–„þ~ˆBÝã.+j)¿…—î³";V?™¹Ã›^!•SÀÈy÷ÛÃ^Ý*nØV¡•›ôG;5)[Û*ÆA¶y'¯µÜdwK®§­ìôá{Leé)C‡è°_<•ÞýçÝ¿<Š'Go–2»µ•Ù[TÄÆ/U‘A5Ño¬ Ô]h½ÜæBõpÉ]°Ð=9o•š5X%ˉŸãí|ÐÊï pP:Û%\ŒÊþ5†š–â.#ö«´ :¬MT¼¿ Ÿ ßqoG:j}@ìê½í'åõãÉOæË—`/ › :ܬgk{‡h«XÇ•ùU¨÷s‚ ]μ) PgÇÃ+~#$Ü‚åå¥cv1DëEYWÀJ3ž±µ†e@<Ûý¬]õÐ}rÐ~½g•â5‘Wµ  h‰GúÓKX`±OA¬÷I*bõŽ-ysïŸÕA±[­gɦi9ˆ\i@‘m2z¸pÙW,ËR;ô÷§rñ¸…§%û=o­–pÊš>çÉ7¤ôÛü‡Áƒ„ç¶/7yÂ|Zà~ã@GìÊ`=VûÕ/eÔU•½tL ß #ªíRbt!$¶yÛÞ GÖžmx¸Hó#¼Ž;C5²sIã2¯­toš*P¨!U*×oÛùsÆZSîæe:¼ó6Þò5÷‚ ïÞ%Ú³ƒ¢Š&¶%›M<è-«àñtG]pzxpäJJžå²_’Èoe²gŽ*±¨IÚ˜«X¥ä‹c±~þùÇÒû…KþS(|ÜzHpMí¦F`2Èn E}l°¥k’l À°ƒ;bÄÈ6ÑtÌ¡–ÅëÄ»VÎiö‹æ¸t02,[©¡Z¤¦EâDFæc‘ áeïêeq¥ÏÉòOÁ¨”¾˜¹Àˆòë0b ×~‚‡Û$˜|“Ö3*5‚à¡)»7tW{Ú)áìn~nŠèvÏ>Û³âï²i'KVéîÀ¤*3~{F3‚©¿—¦JgˆÈiA2WB&Üæ9g¦Örfvò"9.|êXU7¼ “ÿÊßE€áMKW…!‡Û'œ~ÇÜÝŸXDwU4 †uŠ|ç_Ý…„}O±óik‰æ¯G¯“µÙŠ y€¢íb•Tr½@_2ͼ|Ó5ÅäùP©—±^g®wØQƒ$C®ãûÍ_­k»Lø†_€n,Ƽ·d¨Õ¼¬XQuöü-cY Dô4+©Ýlp¿ï<®6£X£ ¶££á*Oj}ž¿,ËbgæŸZîôEV·˜‘”Wröñ8â_Ã0 †X´zÂ!Iu[ªˆ<¼S"®ÿÊjÄá…„*ÏÑ…x†;& MØ‹6‚?6U4T‰ «"ñEŽK×ÑÆha¦Xe¹’…P €Ë²u/€E]VQÃj1ƒx޽ܹ\÷lÒ‚ïy®ŠÈ5GžöôiCòRsöM,“­(9û)bÛJ)AÎ1AæÉæúÔ«ˆ¥›ÖAåàɘR(’}Ø>v·>ßB=\†-¥ (Çù 4æ±G¹ÇØwo1ðAéÍš²|¼ÄèÇlÕ/ÊôiëÄuiu•*¼ƒ;í5íæ`Ï!žsXŠ×hsìçSã2^æÃ#ûÅÑ>\šè‡˜çñÃ_lØE ù‡]0í$HŸ.B|Š. ‚šþI5´¦ˆRƒ/kŽK‰ÈÇ! ¢Zà{ž}ÜWJR&JŸ¢Ò±rë¦Eµ‰¸ï ÞÏC«>endobj 23 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 250 389 555 500 500 833 778 278 333 333 500 570 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 570 570 570 500 832 667 667 667 722 667 667 722 778 389 500 667 611 889 722 722 611 722 667 556 611 722 667 889 667 611 611 333 278 333 570 500 333 500 500 444 500 444 333 500 556 278 278 500 278 778 556 500 500 500 389 389 278 556 444 667 500 444 389 348 220 348 570 600 500 500 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 389 500 500 500 500 220 500 333 747 266 500 606 606 747 333 400 570 300 300 333 576 500 250 333 300 300 500 750 750 750 500 667 667 667 667 667 667 944 667 667 667 667 667 389 389 389 389 722 722 722 722 722 722 722 570 722 722 722 722 722 611 611 500 500 500 500 500 500 500 722 444 444 444 444 444 278 278 278 278 500 556 500 500 500 500 500 570 500 556 556 556 556 444 500 444]endobj 24 0 obj<>stream x¬ºcpåm·7ÛvvlÛv:¶mÛì˜tlÛ¶tl;Û™ÜÏs0ufª¦êóaW]×ÂoymýɈ”é„LìLÅíí\è˜è¹’¦6n¦.–Ɔ€ï+ ™ˆ“©¡‹¥½¨¡‹)7@ÝÔ jj `f0qqqÁDì<,Í-\”ªJêT44´ÿMùG`äùŸœoMgKs;ù÷ÁÍÔÆÞÁÖÔÎåâÿXQÙÔàba 0³´1ˆÈ+hJÉI(%äT¦v¦N†6W#Kc€¬¥±©³)ÀÌÞ ðMøç0¶·3±ü'4gúo'„œ†gScËo5ScS‡X´S'[Kgçï3ÀÒ`îdhçò{€¥±«É?|Ó;ø âàdÿ-aûÍûS°wvq6v²tp|[Uÿ·Ÿ.†.ÿØv¶üfì;%Mì]ÿÉÅ7–á?0ß\CK;g€‹©Ç7Ñ`d 0±tv°1ôü¶ý æàdù/7\-íÌÿÛZ€“©¹¡“‰©³ó7Ì7ö?Ùùï8ÿQýÏè l<ÿ¥mÿ/©ÿòÁÒÅÙÔÆŒ†‰ùÛ¦±Ë·msK;†ZDÊÎÌÀÄøoº‰«ÃòÜLþ• Êz†êÛ C{;O€‰© ƒœýw;™(ÿϪLø_+òÿB‰ÿW ü¿RÞÿÅýŸ5úï‘¥ü¿ÿæùB‹»ÚØÈÚ~7À­–ÿ‡ˆ¡­¥çÿ‡ºé¿—Œ’©¹«¡ÓÿÄr1üîl!;óïMÀHÿÝ¢ÿ´ž¥³¸¥‡©‰‚¥‹±ÀÌÐæ{èÿEWµ31u²±´3ýÑmÓÿà©XX[Û}O€íßp¦v&ÿ£µÿ™‰¹Íð_±ýDá{z]T<¾}ù7Aý‡ý÷ªøË?jÂÂöo:&V3'Û÷T12¸ØX|ÿ_Lü è?ÜûG÷‡¡‹“¥@û;Îo¥¢ý×ßtÿŒ˜±½É?Ó®ìbhgò½"þ‹ð ±tt5•ý•™‘‹ñ{ò¿Ógìêäô½”þµ¾cÿÏû¿–­©©‡©1Ìi ô²AA?í öˆe:ñ€¶6Û…U>[∭Êx‡^ÍÓÒæÏ3™ËL—F§­œXÛ²ÇÁ[°Ó›a®Æõ²ð êAì/š-íØ}r¹M3!:y¢aé!â®wU—»CM@âOFª,;jíOxMÝ1?4èˆéÀ•åg¥¯Ì /\\ÍhKTüK m! ð<ÛH~;c67ÚX©6Ù4n·\YR›ÂõÊÔvîYgâé›-€aâQ’òêûÈ~jÉ`&O¢ ¢±”—léÇÞ»µ„çaI×m]n ìIAƒV¼™qˆæE{S/,£ ¡ŽIÆÊÌqðÊ-6xåûŤH Ì1&IÀÌjB'÷DÁä§‹s„¹P#¶ÅÎ<%<è¹Ñ(pÓ®ƒ@ %~oÇäþ6ÿ:Šùดg—óÓýi^æbd×ø”s}&É56‰° à*û"Çú*¡k«“SÙ÷Íúb¾—)&Á¿á ×@®Ü6rÜq+#›ÍP5%öê(á`öCò?©†¼À£Þ5È‘ÝÍt:•œ•ÎïZ?9ä¢ç"ûóõµÃàMhŠ.±l©¶Íbrü£ªYsM¤lY¢|#©n‡=«nÏÕŠÓÅPÎb´ažƒ€E™Úàip~ù_ÈœzV¦!ñv`p'*²ošÍæÙ°"w üÌ4.±h±Pm­Ý~icÚçŒB¿Ÿ»œà-Ýy2)]~yap×þv‚7úºhp28©ŠÃ½¤G¢Y%—Œô»Ñ|²á9øÂ÷˜})bìjWíX²ÉS)–%OtëÄz1þ«^m%ó¤ž]‹Ôcù }ìN‹œ^k§ªøæ=d/n…Ö)áÝ'Ð@¢¯H¬àã@&¼1øH§&$hУüã´ ád”­V}1™§h¬Ü­–VOìðÏókœ¼@}äÝûrÆ!ârõ•˳ÑûxÈɽ¾¶Œ¸gU6€)ŽÌñAŒc%Ê=Vu ôSË‚9}®šA“a /íú~±+Zî雀…\!mcz¢)ûÏkéFˮ۲µ«žô€[O‰«g¶Üª7ÓË9—P8ׂÌrðäÅ+­"êI‡1Ü0+IH¨ZØû•†š?¹ ˆ‡€MPv嬤wµÍ;!Ï¥“ˆt^œ"uJ@WimUÜøƒ¾Áì>¥ëRRŠ·à`\‡e]…»ûξ­0àÌŠ÷KåèË€ú‹‘â°àº?ÂJ¤¥‘|÷³*˜fÎÐà œ·z)Ò‚Ñ™áŒq@yÊ]”-ËLJlS™¤Œæ’B'ó¦Žj9aϸPž®öD0Øw3ƦK’)=i]^TéµN Ê¥)7œøBïµ%QbpµuƒôÌY#)'#Míe‹°ÇNêåØIÌ×ý!Z ’ÝeBCH1Ý‚Êð¦‹OÔ8¡÷„D§¹%[¯\häVÒÈ™=)“"‰b×B{rTçPOžÂS‰Ûª싟¸C5AUøÃ©`#›9cßéI2 &ß—l®>5ˆJû‡M*x×ú\U ZËM….œ¸4,¹ã;zlï’äSÚ"“Ðs° 1O2)Ú†'ƒ&<›]ìBì¶õ(°yhë©‚ogXyÅK'¶F @ØÀ!Ïâtpkø²J æ§Jë˜SÙ' Ín‰îè#ßxÂ…©K@®gx²àSžn¾$ €ëf(^’ò&d†(¿Üdzïî3 4á-àôÒfÍ(û¹£éù®È ”<£{ŒÄ_–•õè‹o2²…G<ž›½Â:ùW»mÙÒJäy’ý˜Š?È(Âpì!ä7Ë»ú¬ÛË¡‡žxï ¢xìÅŠÒúøÔmL’«pSÜÚ|©N0üC+¥â#ã¨äºÒ`§3c&ÆÅôk:lÅ*³‚'.tlÌâ<:; |VwNUñC¿Pð!ðÚÄ©éì |.#G!7G®K¢†„F6‡#Éwñ½²Çtc—lW¿ˆZz‚‘1Z²JÒpÒÒï]$Ò íÊ­¿èÙÐ/µåÆR°sÖÜËsÙ ¢ê¶ý+½o}ÆÏ|¥fz‹§Ê¨÷ s¦ ¹#¡‹+ºO׆RS™}# Ò¡¸óŽÉ1G½ýµî°y\o×T0¡‘dÖßÜiòÕÐ9”Óa/â×ùu¦ÂÅšÛáòÏ—‘ô ä·Ó-A¾$è<â°ÐàÍÆ {ÄãÈÊÕUvçÅ5héqp˜•½ .Œà€ íI.ä+e@ÂYâ#•½M|ëÓBJÚ“Áí¹ºê\­÷ãHr‰/ª"O‚Šr:³Àͳ¨`ج,ú^f¸n“3K†_›1^Ø*ËË»ˆŸ•Ý6(t’/õ#Å9®Ä`7‰§‹?t‰…*×­(Qm«ùF_EY«n÷ù¦q•­6‡=_“ïSw¦Òç<ö¢ü¶àÍV8†²†ß™9î°;ÌçA¨³v¾Ñ¡8Ü$x(Ž¢‚xÿB†c’tä}*|éWÍ„¢è¢Hb˜¹R,E"¨­ñá&U[©Œûœè~ÆÃ¾¹KSó:dèÞšß ÅñKý¨#Éë’½Öm”‚ù¹¹KäÔÞ¤gM5Ù¿+=–Îì¥Ã¨Î}xäÆ: 8T¹ÙJèGdÌÃI¨Eˆrf"·®ôp"»8o[Öà%‹…Ôb½ÂNU'кŸ&ì’?²ä3¶k,Ï_È5•÷ˆû*üW”%ZÒ U£ž2yä/ªòN»­€©5Ksð5˜¡ôúKE|®©1Šì ¬Çof„¨£ŸÅ‘ütgäDa»ãÀ`~w¡¸P"H˜’×ÛSLÚ†Oòå•'L?Èl`mæ+h”\Ao·#<*Cƒ¯s¿£xÇ{^†”Ž·×ÍM÷;Á’ŸÀø@vI~˜cÀiº+Uo…'ƒ&L—À®h`*¬ä£”Ò߇-¾ÉœËïñ$Y…hëߟ ^ïJÒö5ò:ø‘ÍB…ìxÚ¨r3'ž^@ ^³¬sa°é5”¶56W&¦éžŽ[©:kžÔTºD¹?ê•ÈÖ–Á©ŸLîî3' .añaGöÜ9ÿ^ã·1{xæÆ7˜%ù …Ãù\øû½ é÷NnV$gÑ~•/ªo¤Aщ Ì6T쵋÷…ÀH€ qSr˜Â"¤°ÎOë_AÑ !2UNûºbn ÏéjÇ(ùÓ“ì6»¢ñÄ0¤8ÖƒXæÖŽ"ñ–se²J¯ÃrÝÌ ×æsµ;ÿNðìf ¾Ð-ƒu–È=ŽG[ž¬Ì’@Ú5U{œ2 Dñ³aôÕl°¶·µ ǽߒ•$)ÿg¤p£[2Ãܵ¢–Ñ:àé¸Á¶£J‡Ýº&¸4G GŠn»UV L¤f¸ˆMñÎx¼«WBÇ~<ûs ·ó¡Î=¦HÈv+êoý-Çæâ=²´?˜¡wqþCŠÈ úˆñ!É+ã«ⓩtÑÓ!¾nƆ#„ûQXœÕ©Z¸P‚ÌÐXÈl#gËØjþà,ÒU mĪ¿põ¬¢1@/Év„’Ѭ:lØà°—ôiaT‡£N N­ÔGž)OÄýu¿oX9F)à±´ö5‘½i’(ˆP´{˜ïb¸þ‚·`ÃÍùá2n×­¢}Ò'vî5 &³¼YÐÀÑ»sï×Ni'¯ìa\ªDś؛ ;›¡ÊÒ1¯Wãú0®þàgFÞGƪ¾¯‡Ž)S9Þé­ÃBõÞ³’4»VRŠº¿¦Z ý¬e=ÇŽìR1䈭©ÆLµÊùçº~n½_¬žoòF×Ù>Ý‚9ƒ7?ùÖÁE|M †T3òÓ E¦«ÐÇ×¹³üÝ/ÀmaÍÙÙŒ z´…òK=aPcBÆ1,B–ÕЛ½õ¬‡Îg?H~ËšíêØ’ji2ŠXqjÙ†©À¿Ú“ƒ¶æœ,"¬W¨‡±FO¸±:íöDŠ8–;ÊCkützÚ„~Äe¶—MË倔×{Ø!h5òòiá¢Ã›ÿ†] Jl^{ØlŸÙ€.~/3–ËιwàÚ›â¤D³U—Î$”µc1O°É¼ù™8j#`« üƒ‰µÞ“c@°Ð˹·ŸMN\f1êË(ó3ðOj7ëÍ{ˆ…6(lOL î)-Ÿ•öìTÝÏ×>spÅ ½/þ‘É÷<Ï×·ƒ9z }¢’¦B?ò è“'Õ¾¬Î€ƒQ)@Õ–Ådu@í×óã[ð6ß#pH¿€Pg´ÝÚbªÃN^ìpr”WLX¹ÐæF[›â¶ XñE¨–¿°v-^OâæŒ8=¹ÄôfjˆÑÀ j×>S ä:¶ËÏw¦?(Øvßx ôäÞþîûY\ëê\˜^Ž dêÃ`àÞ¶/”sá%˜ÂÁ x8?ˆI4H§¾Ûv°è8È\÷‘P…ráh¯ Žx-2å'få[èFîÊ“ÁIl[K)¨Ü¶çæÇpKÿ|ƒ”$Bì›û³i‘ìŸ,nñ•÷)ZÄ)‹¢ó3„þŠ×¿Ti!·Fz¹MÍ4ðŒÕ˜y×óOƒÎ ›ÂvºÐ&ÖÍ Þz z’ Æ/°1¹Ð>¥)AÎîçïX•'D†óh ´ÀŲ¸Ú›ê °xÏv+|\ ÑÍ…Mtá3E ^¢þÑ£Q«‘÷›ŒFS©¥}|‡¹‹ u|\H&¤Ø ÊYÒÒû_Ç[õwE¥žíCÊw»©”]Š£\µI¾J4²ÀïéÛ&µv8E KgWEtéi…¾H‘t Î9üýj¯Õ{Í!Á †5O²¹*œjb]E²‚éÛf).~e`®nÌõH^X€ V ¼£&Øâsʼ3AÿLßõýÆ"|E°úŠiÆd L.R y¥Ï_á¦$Käê%”ŒN †CŽø4slš ÜúȽžØr_EDU2ǹ î·=WªÃ61ÑKä[K9»S “T"FÿO`Ç£qÖ6þ±+`UžH°1}”¢.º.=!^×Ǻ“–›µƒºqMKÏ oâ¨cŽEecÁ¦õI ©Ó—|LXB P¦ú­ž\Þ~9´¾~w½KAM7ßýßö8éö0ب÷ê¿xrb;`ÞLtf¦OJªåùÏ®“†•ÛF ÜNuýÍ4•†žåN8 •ïÅ`a숸y FV¨…T_¥„Ò ºø,~4¥PIÜrAoŒŸIЉrzHüµ±+ƒLÁ­©¸•ƒVº¾Î%ŠK3æv£¾u\Ô­ßEÀÓ8šn©š£–ñÛ°ÓêµìÏ:—Yƒr·8-M,Ág9Ì{Ͱ£õb—~tæ1Ç;hiØÝ†.,COLß Õ­5çXò’œIŠZ»sï†q ™%ÿ)owÀì—@‘PµSÿ5¡ø~a{òL•í@Új‚oú­µÜ‰ù÷¸@s7ñóÛg_›õÖ_´¥7^/_¬•ã©ó–l@0bTA™AðŽ‹[\q>ÆÌíq51‘ÊQb»-\À¼¹Êú_¾Fnéû\ÌGÒÌ®± ï6ìÉþ8:”XPÌɃ؟–1Ó8§×žô³C*S[qåQ¡>÷éóŽÌ=4ÈîšTu¤F&ÊòÓƒ¬¿à#Ïòü5f_ø³VÐ m@¨Ân–bÆþüzôʥȦ$'~µq»ÕеŸJ\S[fŽ×…®+uñH?YÅ @ªûÙH_„f‡Ù÷ :(”lN9hÇ ”;ô ›’;h¿ÕÞžìõã“‹y?qo?{xˆaãœh9éC»ª€ZÊ•=H¶)¼$<0ÿ*ôkK'ª*k W‡êŠÈýY)´¤IZ8ò4i N.•1!€–£Q_B‰f•{b¤5Ã/I(Ó•&aUê :оcÕ§”5m±Ya« ¯à Ócšqš}¤¢Àòź#ì*m}߆Ö2â½öCßëóª\åñ °×æÀ|§?ÞŒi{1sªÈŸÇµ¹Rqô†ýC3µLâ‹»±æSíʆCG5òåƒU†£ìö‚¹(>•Æ“\i§Z»1 UïÚGȼÒp™ä$ñൈ“ YfâÁŽñ½Y ³]€™ÎŸ7zxú·úXuƒ gÛ䉖êI/_BÊi.Ü«)A7ÀÞ®~ᯨ¨ß´Em’Å#y‚´Z…ÖÍ“ Æ3¦þÄ:¡½ñìù\Z^ýù=küÙhÚƒzë'àÉ#Ø©sIÈ¥zš©FN÷mŽß×¾£QJ¾bý-šï¢k`ÝíB7ÅvBH=scžƒ9÷` gt¥Óà;¤oK0»FÿAY~µ€(p¨2—žwÊVàðþ™pø—äÆÊâ¶× ü8ïrÔùSæµ/æ|ÈAYŒGyOî=“|ÔqU“úß$üÍŒ7M*'?¸iÄÂ.Äïˆé¨Ð/#21yu.Øõ('‰"ži-æÏö6[ Ï*ä,ñÈW³ÖH‡øƒiå ?°i¡Œ×IÆgÑ™·ÀJ!ÅT º\ÿf¥j–Ü­–Y,…®AgÄ©^WhCÛu¡½“Õ‹Žaºtd†Ô/áÛîAʈ<ÌPMV]öMµË'Ÿ“i{°ÍPÚëÍ2ÿe–@I)Š.+ш?⃠%š%ñÀÛÿŽ"R6‡ÂÊòpˆ”G6%ûOÔI·íá´®ZÖ[@­¥Xé¢äœ-ÔgÝ÷sÄ ò¾—c‰d ΋¥Úþn)"~6S•!æÅ.5«ý"X챚¢_Ñ®­Ø5ØØä2sFí×7jèzÓ|ø"X³Ä|ͱÒ+©q¸\M ñq°CÖ|I¢çñg!§KÞÌe΀ ûûêbÚ¬,òìqKWR[Æ5PJóÑ(§€-PpÒnVÀà!.QË›§’”«é÷p4£d°)S2èma^iH+í]£7•ŽÒ¡¢Âö[‡­Ÿ“@S䞟+˜êj]ÉçØÈK€vŒ!´{s¶3À„àŸè¯×Bйv’çh=º ³þê¬ì¨ÚöôÍö:‡»n¬€X»îmÞà¸ÛvMµ:°‹~f6†G²1:u_å$Ÿ® ó,Ëž*ˆ1Ž¢yhÆ£;<½Þ7&ö.4Ð’n`ãX0tßί?r Ë;¦µïÞ³ïŸàmõ­ç67>$²x1½RgÅÈ3\u̧ ß…»;‘¾ÆÇc3`Ò·ƒ¦KHH8‡Ç^v•ŒÒ`ÏâxUÉíY‘åÐ|ªK£ôõº,V¢ãÚ±à§]ž 2&ˆ%¦HóŽëLD´ó:ßb爈mµmÝþžfr§ò{=µzwû8”ÙM¶~)½èÊ+Hiíqõ—nÞƒÅÎͺÊY†ÇÈ9]ý)L'äÔ\ã±ü<5XU’ãªo5Ê/E–̬Kw•ªx6·Z86Üh×Ö“dW{[¹Mˆ¬wõaA5Ö¦²‘ØYìÖ5ê°s§Å?ŒÐ~À~OteaÚê÷@S–.瀎B—°àiv k%HÞ­NGÌU¯…b[šsX³]ˆèa·kVE^©Ô+è ºâ8Ï5`¹^ÛŠ²èŵKß7BÄÙ’ÚO‹#²>ÐI)”q’_ïì;à^=ò°£‰vD/šªBh»‚FBè¼¶»2e ý¤¦ÍÄ–%•ŒóÇT¦VÂã‹Ñ„göÞ¼LÁÉ!wBÒ‰jª.×+ÐÁmè«£ß`òzsþrþôø©‚î »£UÀ-ðsúr å‘jN³êpñº÷é7 ‡Í“¥²–óu7 áJ*£¬]õµHQäÝ:Ó¯í?}àöbNCgÝϪS¡ÐÕþìG«…U™õ¡£Í½®[0›3ƒ›˜ldÎcÀî£2êäãÅÚsšcZû2¥¿W7öšoAÌná *ç~5ðË1ÏÉëi}ª¶( BWøCè-_&OPÀ½ÍX‚ÇLÍÓ)`Û6Q¼ÝJtš®¤9v¨"U%²æL$R~è^Í{8þJ»%ºÅçʧÈçÑ5aqe¾P–/òQ 8k¥¾âoë©©G˜YËêv æZã¼½]IöA1dúŸm²c¨T–gÚÔäͰ@OÚ ’OP1vàc`¹B£ÎLPȉú/íLó hƒlRŒ1²¬Ë(yɳèyÆà†4xñã^â!§kŒdG¥É6OÇà˜L­â¤`#³˜]ˆ½^e8Ó ÉÞ‚5mÌ1ÿ=!ð’jFêA7ö‹)Âë ;á}"âÔ X¬ÇYˆ9-xhÚ¨®opzƒH/ÎtF3Æ8NIôêáó¥›Ì _ ±-±Èye¾Ý^:*_Æ.Ü’¯™$?é×Д\–X°öZÀyYþæóX¦1Âö~ÐÖ ƒ=Îу"­©µ¿4n1áļžöŒ/‚üÕšÓY:=zZ1aµ½®Y*D(•=‡bVp+¯t;ù×óç\¥ìÌohn•µ‘¨E`ê}§ªAç$e«‹¬æB&jÜ4!œU/ìÔ(S&µ¾»Ì Ì@$¿{ËM7û•‚WΤõnÚ“Ôé•÷»EU+?Rƒ‚¡‚ ¶#<ûGœ~U’bA˜'ë;(^c´Ô»Ô\z Ç"Œ²Óꃗ ¦ºKhÏ*" w)b3Õ‡VüMõw¼£ѩƞKèĪҰ±,ï‹ ìî (…Ê”ÛÏQc]j¥ó‹þ¸‹3Ä[R©\(MI`á)ÑÜ~7žÄvg#|YäËýZ0)𹉒¨ÍÓ¥wj ÞÏ}·F"°u]Í`¨ÎÇÄ‚0Û‰&Í~RŒ_š§ÒÐ:+V¾$•¹ÿM©…œEÕ cf™w¬§ ~NÄ~¬ ‹(–ëÁ<+"ôàLò¯n+‰Z6-}°\˜}„×?¥áFäÛ‚­:¶·ð!‰BƸe-ÝŠÇ¡üÏw<°û§Ó”=^dù í^s×…0/d\3÷~sn÷¨`òéÀÛXàæýYe8Üëk'7ýdœ§5PÒ)é.*¡†wò[<9õ뤓)SM‡­…Ž!{}4Z$§™:Y8Á9Œa}¤T‡ŸÀðxÃ#ë¾8E {õ&‘ˆ¡9J‹˜FBªÂ5ô“ZieÎ:ÙÄ'È¥‹ê›ÄS¶HØ6^U!¨¾‹Íþªz3úNÈçJ/EðKo ³¯“JlqÎ1¦eŸÌ¯Y±Òbrü……4(\Ш:,dÖ‰p ;fjXe·­ô@Ì-Õòä$BÜPâ’¢Ùt:½a¢ø¸aU 1­[ú[¸îë o -C¥ Eg&r‡œëàVëyO,ª­ÐB„Åuz)žy¬¯¶à¥°ª¡Ëá÷M¹\¾cZž½p¢xC{O¾Ô@&<ÞÎY ·[R5Ñ<7¨'wœÅvJBÚ*EÉ•Ѳ Aõé. å‹÷"ºÕ$%lŠ?P::~[£¼â£â¨¯lBäæ%aೈU§sÅç4Ô7ËõÔÞÚâçk\w@Izù·|/ÂÍ öë"Å–0 £ÔŸî¾D' wàß®ž,4óÚA-Ä ÂâPa nå8ªìh¥Ï)ÏÔèƒÆ=‚ÿiæ?³ÍU>KwùœUÁoørš^¯¹èy€eŒÿ$PrÀ•l%Ô½)tÅ:ëðÂÕ0‘âÐ*7sÐ+³+Im-7kL|(›ÐE/ElV_åg/¡0Oœ-“‡‰N A˃ÓKø[d·(ÎÊ#X ˆÌÁÊgE—LRßÞ¶|Ìùc±y »b€J=û*Vº•«„“U»º½€ª®o!gž2[—èí³q¹ê–”+NZ!{@@²£³Ç»ÅbQ«ßª¦ÍØÃl¤oÞ›ðßîPè_–/ö‹oiK.šM¯.4RàÕ:¨‘D›1†‹¸L9Råhì^™/šÅ=wõ?ËäqÕÞ› ¸à!ßklvÚ¶4ëÔÙøªÒ@ ,[ih>ÀìBOŽxƒÛô˜Ö¶@R_E5è=<^L2ïBCª¼ÞÙñ«>u«²r5ópöM²§s<%§<}*™£‰}oRËÛøîëú%ó/ŸÔÌRùÜèRv©`Z¥_´ÒN×#™³Þþ(7¨ÇpÛ(¹Fµ;D–ÓÒ9_ôíõÚŠäZÁpï;º+LèzdÀËî’튄¬È!1?æJ „,XsÎHÕƒqÊHF²T,;Ô~‰¯4Ãܘ/nÓüÜÚâ0%ƒY³ù#© Ñ=˜´ìȳ .Sjø & pÏ'‘ˆ›æB5¹ŒÍ„=3û3jr¨¥U/ŠR9j~žÅÜßÛUßì"Üeã~{±§Ž@-_‹ND-‘-ìña€üˆ+¥: âŽ,O½jXcx"¸D+º²K/’͇;ñÆx/²£ü¼òËÕ–K¨à­Ý¡õ™f(KZA–¾uõèéÜ>—éÆ AvãfbU6A}÷íGý€Ž‰qžJj. ¾j\X^¢ƼÓG?•㢙éMüqoW‹oj]G¸@ž>´)0üÌBs*÷J±m¿H±b¬Ù»F> ‚»±`A¹%°¤mq¡ÂŒò`»ˆÝé¹ 6¬žjTÚS©k^"‚£Ÿ þöÏÑIç-!U¬}3) Ô09^ßk8Ü)fßÎ3m s79Á›«ÂžáKòzµ$£ÈÁØÆÏÜÖÇSÜØÍ|rªê"ñ¢Â4_ª½â¦ßTôu$íá÷vQ'°,õDú¸Å¤}Cªa–Ù áΉ<Ê·ƒêB¥ò&œÀ°¯Ç¾w’œ&ûtïðþ>å¹ï­"Þtö ÇÐù§yv“Šþyb"3{_>Lyà^©¡9ºá;Ëþ™« 6´·]/Gvʕբ|˜ôo(*å¿ç¾ßK—š›zÛ^R¹1öÆD¹Bí,©ù vó‹ÔÍn`ò(™~`Q'(—§[£þá}™÷Ǥæ?a ÄŽä܈!~© (Æ^‚‚‡³t¶dVà%²¿õ»õtSŒ¸bxšï{r®ØÉ8T7’Û‰ù‰˜mß©ÅÕëÑK’ýàêîÌ=Ìô4ÐåF Ôxú*Z,ò1ŒÉ–­ÅËñ/×ÃE¯ê;Ì4ó\ùX™s>~y§ƒÉ¾‹Æ„h ïì®Ôü±Ô–Dç”ûP-OȀݤª÷°»-ñ4³U…6ÛË:„x é¸xÏ9ú¿Ô^ÛK䩪9‚ ë2*:aãOŒªRßxB$:mÎiŸ<µf³ÛRÿÆð€ M?HkE±¸Ëm¹Q’À„`û½ùŒ¡Kƶûy… ¼M…Õ­¡E?ÉkæÕð#Wˆ=UÚäFU2̬½ÅQ_ -<‡e ëÔýDjlîj§³u,¯ÂC>ãÂÛÿN'úÁì"Ó²½@–f2¯w•Vƒ’ òˆ!ôpIc¹»6Éñ‡çv4 ô­SÁ=>ï¡D/‘öÞµì›×®À‚€+ Z†¯râõÊ~-?o@5´‚b¬ì±T|ÞÈhîÁgjÄ/„âNƒ¾‹ÃHd¾-&¾oá#P»¸ÄÆúþ §°+±¹a¦SÒ¶®ë—ip»1Û¾|ÅÂm9§¶Bû ¡v/ýq²5¹}Ìܼ&ª™$C4GŽCC·’–Y¡M&uقÊÕáNÕs g‡ »E&•«Ž}vä­¾*Òû_$r±î%ã•ÍQ&Äšœžï³³ž$¼°ïÕ ]!#çt9RgzÏ&ƒÎ8ÕÉ`†n‡Ýé­`ÿÀJ*uT÷9Á”ª¯âߘ8-W$™mKõ’³8h§Ì_¹£ç#‹)yé͵°l"ã4YÂT÷³íIiŠw‰…én3ùÙ‰@)ÅkÒ7kñØtËHE\_ŠÖ˜UQ¯f“â‘D¦œý0〠€9Æuvd$+ÏõÄE<¨ÆÔú©§%´œ 1e8®Œ °e·­Qx¡–ï6i¡Ý½€d%³÷(YHrê”4Çaû¢'l"ìÁWúMF®2qÅ*æ§E< Œñô©Gvÿ;.±õ§É›j¢Tñ¦p5ÈÊ<Ð}vr¥®/ÉÅ ŒM‹ Ãîþû¯PY-ÁäÐŽZÏþ-øNÂ`kÌ–wŸuôÊÀ@ýsÏæáÍf6КCqøÛä±)®Uß§H±;ܵ<“ý;´ËKñsÎDà*á*~BP-x?Ïl'UQˆ8ºpí4=³"wâÚy×Gžz^¨ÚSPrBBõäÝR úF_žjx×6ö>mÃT³zÿfˆÎ%Ô©ÀÄrEs ç ¯<óB%JD=u‡$9F"¨$™'èû‰:âAJg‚çùtp{ëõΧ§ª‘ÑìöÔw ´½ÞPuÈʈˆî¹Ú5’V޽j7/Õ4¸/ÒÅïïN'Õߨ:`D”M²óh‚ù DWwdš ((¢dxçÈ/£)H¶hì®Ðƽ8ôXºˆGsjl /9ð1n²:Ù:tbDÿ¶&ËrÇ_z é¤èè¹”eÚ‰}é¼!´¡¢:Xß#¢ö¥jz¢x|Ò6uë|©šê;30ÍNÌ8•¨_‚A Ëº ý´zT^4‘/½°Üo l^ë¦MÁÿs1}4íoe!d39$!óª_çmÛe©ŸŽ“$yÖ¦$ª„KžØ YìsÖèEìû2s'½¢5}T3¸4×®„p‘ê7z T÷—všÎ¡”ÚÀ¨)ë±aÏ'ýSüŠP#ïåÇûƒû Ý¸¢(÷…0E HO X²ð¥Áø³‹¢P+×Ûs躡M\7˜{qY[õÉ›æÛµvlÁŽ}b§²Íç£l„w¤Bì|°Ž˜Å,£ŒVÂI:M “[E.Àz4Âö°ËT¤Æ¥KˆM 2¬v*´l_vèU$oÞ9p,ê!gdÁŸòcjŸLMw³!-{ìÁwTÔéEéÚ°n·#-q_JMŒºÍH—ÐóºÎÓlÃáãÃŽâÄRfv~.ƒø&Ǭì~ÿßjú$Í ‘ËBáo †¤ĉtóüV3Q‰Êk@6Kºº¥kU“ý.1\²¿Œéû¶Œ é›Ë˜k!¶TÞÈçCD7Ë܈òªvm»Ý#;¼cï/7ÆiÚ?¢T÷Ί´>8:rJ€úXpL]L¬y‹°ñSR\Øa(§æ ÝtKL»ül7ù´Ïp[|µe fÖР×À]˜Ï#´^!32÷…B—r3{Öõe!“(QÏȘX燌ë*$ö ZqKËþQ#}ázÊ1 õ\Íãm*Ÿ÷¡ƒ9Â÷CS>‚D%QŠIXÜ?ðòÔ ÚÝR…ƒœ@^+2ª òFLÈ'²öw»0ЃÈ\A\|íeóÚ÷¡1Ѹç_FqÞ³ÓhÚê%êÖ`—î[óuqõ™'³G k:m+ZÉj,”ý-çH ÀÏ­A63ÊÔªë÷\³Àʯ ^½-Y®~Öö®9ëbô¯@!ûZ:¦(T´oÃAùÍ¿inöûrP1â8úâ-`½ªû¶0Ö(’Ê ö¤D¸ G<å¼ vï®9á?ï%e\8¹.œ.èÇàò|?ݯâAË‹"wú4’àFmøyM˜Oçê½+öBÉI“eÏ~µlñ§ÖåAÚ’¯³¡ª%W§?~‚­>vjÉ´±‘{b`õÌÍ9žQ!#"œÕý„ï&n<ïÁ}D±ÜŒEÁ€r0ÂVpÈ|²¨£cBÿyï0„ß´ìÌ®{k÷‹»´Ž<"„Å>šçƒ¶œE¿±ïV7û’ÑZþ¾Æ;,-úÂ\¦<}ê8[phå2kÑEmn›¾Èz©Øæë†4-qŠÝ ĔaaÔ-öõ*jmºuúEÖI#•9ß·ËLwG2Ê/èDã.V…s—}+è…5ôšˆ3¾ßq‚»Á–„YŠ1µÁ&œéUAÇD1ž n§â`åêäz‰Ô y²Öúꛨ%¿lëEþˆEЪš=SZaBõ÷Íþ4×ÍŽ"ÕShkL=]æX†uðkóièû€±q†d–… Q?é¶(R9Ftºy÷Ó­H]ÜýàT›ðg 絟*w‰ø{©64A \Me`A—dϸD\mylc#ÉUÔòA÷a|Š*©àœn(Í,ŒÄHG/óú¨Á½íº¾¼KGØðP ]b÷Z§„2Œ5K/¬°8”H¢tÒÏ2ƒ ¿Âì•ñ>ä?3j¼úîRþ%ìæo4ØlýìJ},\{MBbà9>Ñpthá·]ì7þ,'¶fZìÊ…šìðÐÖòvI-+ž}2 פ•aù¤ûjÕ[lk{ '(~“}ÊIåú§§À‡ÚÀîÉv•Í—‰¨¸gîž Ð´ïdž—­&rE7QÀ7Á¿.ØÙáY5â—‘Á=”If¹µn”§ol)zBî?-¨0T^Nº—¡@ç½¶ÜnÂN[‡âÍ3:\î”Ú»·šÈ“5z‘„i ŒvÍr-öÞ– ×O¬d¸IFR“®iæ[p¡ÁTÌɵä5U¨ë$ÃqF4–¨P¿hê|(nèÚƒ¸¹^È(3õ²€»äYÁ°ÝפÈf¤žƒùƒËeSI'++ɾŽÎ®ÒS² í¾êh¾.° j£'Ð0ŒH”¾Ÿ³sà¡>Ú¯Yß»ñP•÷¬aŒ[#œé\Ö¾í³"ݽÏp“­Ø ¿ž÷¯ü ýÅd±  &šY‘_¥#ãC{qÜD"¾Œ@ôòªê}ÀÑ¿E‚ ñ4,:èÄׯ¬M%¦«ªðß`¨KC/]Ò7R ¢èó­ uVaÃu öO&ýFòÙc‘,ôb¡U ‰ò`ú(Ž)áðÁâ]'›Ìßk …(¿½â-¢}?¶ rm? mêN÷>Ø>ÐÖ@®¥š0¶ ÈtP[A"VÌ;z„¢úÛuMó´9"Ø Æ[PgsN+9k%F„\ømýÀP´¿–|A‰æ!"ú\fW¿v5øÞ$–-ÌgÆo2b0 š°ÚQ~½IíƒpÄá°ðü5å¢\Rdäq£ÅZ|á–€ß0 ùq`¹X…;•ÎC»(K¼Pr îÝô7"™zc«HKˆÒ_pÊÕ·t\Ë'[?Æ“ò@¦„ÅÌì!$d„­}l-2c×,q–þ˜¥F÷8­‚þ¨‡¿Ùá~—Mº@üÍ'³÷ v‡C¨>gA¤h+†©LV¸ Ì©?áá·sZ3•rèŠÈGNvÎ…ëîÁC=šáQ¶†vñõ¢Ûc!$" I?o]œn½{g‘%“*ÖTݵ®žïa"¶›IÝhɳ *{‘ˆø‚nA6Ýß¿ÌÀ 5éGc%¦´ç?l`dÜüt=L˜&š¥çgád°a»J±Ë¬…²ì·OÑ? ©’–¸8* hÁÒø›ë-ãgÃh¯Ø&çfþF)FíÕö¨…ѹZW¦ŒÕm’‡*»j¤XÑ¿)cqäÍšZj9˜ÔLɪÉùƒV(••ļ Ó¤×öæpFúo™÷@´V³—j¤z=d—ð|Ç6Žg†°¥ó™Î ZÄûµ4:q7îRëÙZ#&­ ½ ò.å îþ öÁTã}ÍÖ»_ÓlP Ø_<*4ÜIÔC5*ƒÌõ·üm¢wŸ(ær–,QÉõ¥Thkˆ2"×T?VÌk‚3o0il5Í *h:¿ñÄ*+µR®`BœdÝT­Ê١ٽÎO ¡h!¨ÜÂðOÎüž\xšûª»:Û´è+ùCO/ݵÚêN·6AoHñSØ+˜¾Â_/I¿×¶ÔÃaÆó»TÆ49twæùÇù$Ö‹éÏ{žÞM´¹jƒÄK¿B\±¸W/×ÿ‚/Kîž8R ²Î[qOk¾·#>>'o›ä¦áæÞ&,¢6h~²JÝ©…nºÊôD¹Ï[÷/ ¾OÙ_pÔBÁVµá›û¬Š XôDxÈæ®­ë"XÀÉ›C/ÑlªÇêaëÔÔ^–ÉV_‚#­ú|AV¢’‘.¹aàf|Lmª‰‹XOê¶÷ýÜÐk«.çn*º‡ˆJÜÆ‘žÉk jˆL‡ !1VŽ»…Lü“ã¸MNûÚäXòኅ 57.NjÅõÝíyÆ6;#ú¤ÃANýq‘m/9±TðŽ.³]iˆ“/Ï´8T‡À?í‰ìõ"%„ês¥­–OÞ/“fÓ£éý® ‡—W¹ÙëˆB›v•q81ËÜ£ï]6CI®r÷Ü M9¢¼ ’Áº“Q®šýš¶è,&JÖ1àˆ¶~J^9Û›Ú_UŸFºx ã:‚=]Bx5 r›ì¾• ({X$IÖ³pɃ:Õ¯Úq¾ÝSÃQ9«Åsue*Í^ J3T_”šº`„«›þ¯£W_;Ç(@UûÉVªí z½œåŒÕ¥û¸Ü%iYܨ¨R#Ë;sÜ`[À{fÏ,'åsUç–1/¨’%• gP—2¼ÜÃßt—T»žDî³hÜŸ©õ—Å  èÖúµõM@ëm*Ø8æ3ª Ì5°,|LÃ6Ðí7sn††+åõyÕੲg 3º*½~~Ódå˜5QlRzI¤ºÙ+˜~‡µ¥?D8ÉâÝQa˜Ø#-^dwûG3†h> Ÿó·/™Ð9¦¢Ø²•m!Céã+#ýä<¶…ŒB•Š”ón¤Âȧì^c;‰ f€\`Äé™LÕÜí"ºì¦D±G3+”'j §\Χi(bû¶ìËíYlçvÀ,öÆ‹@Ön™4¦IéHM=ý®—ÁŽ1²ôíZø-HNayq„œ^´>øÖá饪"BßæÁÜ`?_aórŒ“H1ÖØ÷¨Š"¼‹Ì®Ûú§…b] D qp-¶1¸ëùj ‘ú#-ùaó&1uU8vÈ=­ /uÈçÚÙ¹häš¹Øè” è+½e˜Öƒ|¡zàWË„ôšèßy@/´Ö­E ‚óZ Œ,¢7:ÎíÞµõr0²f~âÿ­*’ÌÌ:3šƒ«<³}æ.¤ð›hç°/ËÙ¦@qšÅ‘P¾Îëø{¾õ£•2ÕØ|TõÙÔV¦§Žµ$,¸—;Ÿäàj©/,Æl+l3ƒGt]/¨sÿçð²mÈþ kC"ƒä pät?ÌGäF´f0$à m–u¥ãJмzÒ­öÇÌJ—j0bƒ‰Ø‘ÆŠ\Y Õ ¬³q2‘wg¨Æÿ9@Æ¿x˜ÒBêeÕCw=Tæ¡;I+p²ÚÐ I±´Ã šê ÚôYØX5“p`vÖ/L@ï0Wm"怒öªz€‘B.™;Wx•šµu)TTŒ{G}êc~ ï8ž”Ë%ûù)ôÉ“¨þ,KY]™ïkçèô‰Ì´èq8Ó6}÷–¤i þÒǰSªSË)Ä%\‰?wIÄF¯nÑü»À¬¾§íuŒzöÅc£X®.ˆS×®•Á˜‹‡¨#9>IÒ1pŽï$wWeÅ6Ïö[_e̼Åô.§$23M}™ìLžòÃ$SþÓ®©ð"ùÉô6Ëb >èɦóYw±7ÜH᥎|—QdNSã.´r¦·`ØJ”w\×ÀÚEi‹xÉÕ !ÒÛ²‚ZfÑA).L3Daˆ¬—Pc±:­ä)Q kÜm+®Á»ž«.Ê®˜é˜‰&gZ—b.Ò~3=§˜…^Di ´ZÿÊ„‚¦Óß"0ð¬t¨r¯o++S}„ÿÄwc¬ c¡W÷lv¬¤Ü»¡Ï¤ÚÉëV/eÃÛ‰Ò AÉÌ=KnÒò7+@Ÿ!!RB¿'€ûF‰m:Ç!CßBã¬=”ÈælZŠ®óZƒt0:f ‰@cƒ,ÕÙ÷9ë½0-ò«]ƒî;~Òô¢‘„<Ýñ“*&¶™£Ï îH? UÏç¨I'¸OÌSÉÉÔávµ ¹ƒAÈfêíŒfïß`2Uµ¨*jߨÀ]î¤Fx(FÃ#÷'ÕF³Õ8¢®ŽÁ¬ ¶“A;·£š 0ùÙS3ȱÞ@×'Ÿ7?sË|&TN«\ÀRfU mȯà<ˆþAÅÈØ¹ÎQ«r É@û[ øØÈææŠò›þÊÿ1’ðñ#Ë— ‚ä}Á€×ÚVb˜¼}ϹƅBvÿt±uÖœQY úÏ ÊËŸ[š&ÜB²R âà·T;-¢¦+ê,,œì UÚT [¦]š¡a,ÜÔD¡vˆæ£ºþ¥²<ƈÂ3åM¶(ºU¾f¸‚‹C߸ÓÀJ–l„ IkѨ²Xë㇛]½óýµùRapïêíÕ|¶ñÕŠ-íÛ 3ñyùœÀË;rC³\:ö@«ôµW´Í”ûlÿ´ÿpWN¬¹¯M#M#[_÷âK#ø¿+F‘%iÄ{¨/Ä&ÙƒEþæÀUAÀÛB>åÄT^yöv4ç Á‚ûñ„eyÛBÊO~¼&MÞr:E¼I„Ø[ôýQŒ-È¥3YÞ,ؾО––o§ Ž/óûÁpÝN¼Ý ünhÅ ÷?À˜ëŠºTž?oj9}‡!¾öjà,áQÉ2·Ú9ÀƒØñkæ^'â‡vs†„ŽQ²åç^ÃÊÞ[Ó‹Ï.dÎJÀz£–o–”Uıè>Z ÑÇlŽš:ŒÎ€eƒ¼‹TŒ=è˜ðŸâ~nû…y.xèœàL\6 Ð(ž .ìy«®¬?ç?¼='Aìš¾²½áö…¢Ž…Š«. Äâ†ßõ4Œ”3òS]C¹ÝxÛ™.{ï¨{ž)¨ow¼<öt¨8–æ)<ú'ÄÝ ìVÓò·8º¸²+‚£SØ÷B‡W|û±BŠÝŠš\Û}èc£”€]8~ì­oÒ^fì‚=Ó1ÛY¸‹")#FX:Š¿5Õ¡zÿä*`]rø€3üŸ*Ó™»HjB‡vÀ’iЂ‘ÞAýH{–O0snùü»ßjÄ ½gMwÙVP¸ŸýrËdÉÅvÈ* ?Y|ry•X#ò¸Sð»åf±óA;ði#c~è´ÑÐ>*}îeSºG›Ä=vkd›Ú/•Æ®ƒ¾{•Ȇ±Ó”‚D}zÔ¿(šÞòWÝ{æ×[é\q*Ê?Ň’4­aø†6œ¿ðÚ+B¡øªô>SLJizpÆQ²<à T9ä¯)v<Ф¾gÏÊ*X’6T Nnüµ°/CGûJ)<Ž5OÊuiíÌtHÈ]г MÖÅ{ÿÃ6bOºÒ˜a©±¾ÂÁÝ/!& l—³Q t¹]Úˬ ¼æÖê|›­Ê4?AäxÖ“pŽ^ cßím«ôgal ¥ãÇš‹À­†`òÁÔâ´äûn îBÂÑ‚î ,}òÅŠ'BðßÁ<üYa3${ØBܶS³"ã©äXSEõ*A¤Sæ$ÆÊíøŽªÚ˜E÷ÔÈ}‹,´4ÄV/ꋃºÈ募O¸ê>Ár-#è’©û ©$M‘ZÊåH´£ìøÈ%•;zÚa”að%yñ˜ðj?+]„4®÷(Ýôëî.ðß&<岯 (~D–Íq>Hd¼Âu‰•i¬jù|%×ÓTôÞôËß+ÔàQG­ÞvLäæeòw¸í[°mö‘ÂQÚ:Âõuô©yÿñ][]§@aþŰ€ÅÝ n áÆi(þ%Áš .€3( Ò-1ZGäm¢.’ÀþÌ+X²…P3m¡KžÁg?«»zR-z"à…íV‡|_f§\v‘·*ÔB½fÓ69 ;¥ˆ{´Ïk覊Ù1v+p',m4ߺœÚ`¥Óü¢M ö‹íf)Œ§o0×þƒ^<õŽM,î€^$;Ó î2éorÎ$gãî š‡K¢3€ÖÒÚ@„‡®^Ï>Ã0‘®A'´ ²÷zÃU¹°-å²™àËÓ{‘IÓ®bþ”(Ô…\£Àú‡šÇm†‹¯R9¸ù²Tz(ïÐ{œ\eÞ¯>õÞéW¡§3†ÁýXsV~ޏgÕó*[ÍE‘¢ÿʈz„ÝP}™üÅ©NH>Ÿ*ñx4Ìš\2ºtuÓÎRXþ›:¹\à-¤šœb5¹  %øè×¼÷ŽÓ"ø°ÁОze}që—6gË3n˜E©Dq²’IM ¡{àyPB¥Ü=º–l‘¿ƒ¯F2Æ‘_œ~GŽICšV£ÏõnŽ8Yéê[·Á;è"“”zDÒÂÄïdo›aAqÚ¸ø¥Ë­Š³$ÈÚg‹ö¢—“Š<˜nч"ž-È.‘)Òòød‡Ð­¸ÎɺÁO3¿—‡±ûŒB‡,B$^#üg^h —Ù@·«”Ì4¤èã$¾DÄqù]Í땹^>”~à”`ã=°f¸è„º¨ ¡üZ®"HøH;4ðœk…ºú¸4cL —ñ®Ét8Lúòäf„û³óY©cœÀ˜@GÛã/”t;w&ÓÊå¼ib).ºgUãXðíJû¸¹1‡¡!~šž2œµšQ˜`^y EˆZdã=…àœˆ­ë­µÞcQm¨j(ž“òÛ,W™ 3w––}Qp·ùÒTÃmL¼YºÛŸ2eÂ-µù05tòó,”bmö¶+{…ó‘è€S•Ç(RA9¶—Àt^1Î&ØóNŒgð°¶.ˆ`Gcη7M±Ó½ä,²j"•ég46}?¶É¸x~¶_§CP±¤tN’ýÕKÝŒ„H뱄¾KÏ]mXž¹n½»7{»À™2Nn‘ñµÿüäiô–ÁÞåУý¤—HòUc%ӥРr‹†¢yìœä0(?Ü=˜¬X¡ØRÙéß5ª©nN9Û« _$Lî¤xqSíš’ÁA€íÚ-I×Í84¢K6NÚ[rÞýÌÜ^X»µ!Õ9öQ|赚¥ÊƒÑRŽõ´3ó@ã ȇˆTßÃÑ2³V˜œa”'(+ã?ÞLþòuö.r«7»/(îíÿCPÔ=o_[ºÊ]dÉOS3>o m“¤åAØ …ùKÐR#9T€Pf7]°-^‚óùTMš¤ß™¿Ãî_*âvÚ;ûu™°.Z0ô3•E ­M¹Æ§4üßÞ}Ú§)’Á˜¬´~>ø»§ùËv<Ø—ÇüF3Ðä¤]AÅîòZ*m\ùò‘É6ÏL1ä]]⮾„éät¶˜T7aÕ"x4劌‚7‡ÜÉð3èÄb„ÅÛÕ.dú¤7ΪéU“/͵à…ÁÎtÕØ˜l ¥8ÈW'ÆOVDgdнÍÂVÍÝõ9@B^C´!Ðyót)×ÕÖ:ìVØc-®Õ&~°dÿ˺²Iz¹P\vî¼ß14Ù(ª¹»ýöƒ—ƒD8°`=¾u2’YÇi“ª0Ñ«2·ï¦jX#á=ƒÌ„šq½¦ ¸A¦†q³%²“çyE•ë9@\­„6’餋Ù犅ÂÒÌ TÈ«{_›¬è¦d´™ÐŒõõðPm˜×û§ž çá bfÕtÙß2-Mà­÷aó!SKH7Å6 I¹Ì†ÒÿäÄœ-4,§T×K& 2Zèê_§Úîÿ΋ ðçCDL?°°?‚ù‘MÛÎèíº!7ï;oš\o0LdÇd N´yîî fˆ )M¼8È_™Ò2Š[‹È»¤ì§«ï •34°´¬À­¿L1o†!¼ Pg{GD4ÐL2ô>öèØOlêÓéŠ1ÃOÑ€³?dTiüGk2%£SL­Xèœ7P€-ö£¶hc¼h§B=F:¨ÁbÆ“°ó¤CehÒ8|à‘ÐcPwôáíQç–D7 WCé±^Þ»x¡@`¢TQt¬Í}ЄœKúÄàYÜ!®ì;j‡Á« ˆÖÕ¾îrKo…Ðd0üRÂ.À`ì4|ŽöJK5ˆOù©e¼ikm¿Ü{ý,à¶;B•UÃ, ”Yl€ ôj|i¶¼° òÀ¡Çhüò:Žÿó…Ùxln8´O3Ó_ÛßÉîQ ÷Uýý²ŽäÉÊ‹ð®‚þvéè "è^Ú>OØ Åï:îÆ7]â=]íSÆ\Ëí5ç¹À× Ðþùp`,;vÜy[TZbUí'‰½Ó,+#„1ŸŠ&¨6òåÙ;Š-¶ VÆTv^¡4Hš½aQBå1jzF¯Ë©r×D { Êb”M+‘T¢©O|?A†•ÑÝ—p­ÓZL±¦JE*É ÜÓGÂ&á6‰t¬4âU¸nšbf¼jµ–tYOïu»ãã‰ÝQ6Ù¹€p';\2ik¥m¼ªµ(C¹³?Ïí7dÑü`TäÊÞ&è – éNf÷9ºÑ÷e¨Û{>€ø±ÁºX4ö¢xÔpxÖt-V¨HH`§îÕŒ°Ilö­8 ûä QÇJúf'vâŽc¸9¸Yd‡pUEÉ@ÓôYØÖGaá/®,úâm²Ë:€W%¥ Ú(ÂÜ5O|rëkxÙb6Eõ1OdŠ¯Ð¬—‘ùa(ÛLÇ£Ì4øR¦¦–Ê›Ö<Û͞Ю…s«¥Š1c;äàZ$3½iŽûò¦[Aï¶Þ¢ ò˜ —7*οìMÆÚÏ‚?ÅÑkÑ'§0ÊÈ:Ò_Àm"†0ë@ÀÚ§M]N(Ì£J”¡¯«ÑŸ„ÂÒÛÍ[ð\7îË–T|:µðC|çn ˆÀ+Ü—uFgC"6sw•›ÁW0.ϹµñùrRÜÝ  qó w¯ÏBd½;ê¹b­*ö~‘ì+‘‚D ü›7*ÃÛèZßÏ/ÒC,„ã'ñ^ù' +[Žx¯ñçña§QÊn;¬|*Ïõ©Ž¹SÈý(3 édW¤VZ½B+Ç TÖ2»ÄMÎ=g»"uت³å€èœÝ1NXééÆ˜ÅVkÆ©¢N/ź\1Hî°„»ÅYÒ¦B0¬mƒAcTÁzšþˆM “ãD|’n KìÎò…âÆÉH¯!ЍEƒ_¥â‘à‰<_£"!‚RßTe×3Ãìxb³»t•ݸ“öÉ VÛ=éÍx¯.ê”)•A6˜OwÉïNjܨ(ÞÛâ?/<&™$ dç/È®ƒ[ãY;Õ÷ÄÃËoqy`KïØé 1²÷&=`NS {MC~ï€1´ùËKFÐ*°^!¯“¤\»P^î½AkæA á´,7ª{© ´¨vLjRN*WéݾV ŒVUßùÅ“+ }Çn¹þpj1ts>­º3§‡|ïv_å6F™¾t‹Œ‡-¸9ˆ¢V\{R#z7Rs5?@€sTì¡UI€á;7þ&¸WPäs*û€¿E$7syèvˆsÒéÀ…»°ƒ0òo}<­äâöîªäÄg¬–HmÌúI¬ê3G³%ʼBì óã£tƒ5|¹;þáÐ#)*¡!á{Åï®››•ô Sø·¶ê±n¤¶±ÁSXz Ñ< KÞ\~_úçhœÉ*2xd¬…d¦†š`†2Ù.Ù„Ä*W¥‰Šà—GÓwì ìÕ:"”Í Ê¾:§€™ÂÏJBb`?{°ÂšÈuùÌv™÷ZqSõ&\`Ó»Àùì€ÝkfÃXÀ±ú°bKƒó,XÙr×-R¢§Y~Z}-ÖÕhGëõýÜdŽŸúµ*–S4^ë‡;7ÝðapûÊË|ªCÛ“f.šd‚ô;êdškj÷Ôk‡E}`aù½:á®ãæã I3P„CÆê±meçÀûUÓ*ƒO°¦ªvÙjŠÛ_ ~ËÍŠº;îu0)cnøKrÞë9w¥ŠxW¨úOx°(ÊÃî<~}aÿ Ö€ÏRÔÞìEã“ÉÇÁ”è´†.{ñ•T b1s%„½±OÖR¾ä¦#J7~0·Àçî{5m?îÈþÄ">˜¯O#à ÚU«×|0ûR¥#¦¤8kr”‡w+‘¾”‹z›öô žqfP¼RÞ÷ÿ!i6îËÔ-‚B!nÄÞ|Ó)×»z»€¸¸yG)Öák­m3 {뼞ªqr¨a^8©—h¾â¹¿@ÀnKWopþU•úEU'»©Påj2öÔ:°„–vûB ÕÝ{ÒêÙU{µ«£XfÿYqªÃs&UšÄ©ÍfÊ{v(ä/‡‡cŸ‚M‚Ý’t ðwŽë*MQõþ–Û»bܶ¹YØÄCÖ4œíûÏ”‹­º6@L‡윰ÍÞù·î)ôDئPÍÔUYýÿc“Í…mg~ȧÑxƒþsy¢gBü÷…È”.î‹ ÒðQ„í̵<žnÉ0¿Ùæþjó{¿Y•ï0éÑÊÖa?:ðÆ•'ëÒJ9™‹ˆíÎ .½K³TxuºO “q[tØþYÕ£­'Gê2þ´aöŽHØKlÿâ¸"Ù$æ{ñyzF†²‚_þî·Û¸]~DdˆsÖWOÉJŽˆ³Ðn+øgª EŽô‚A§ËÃ7{yßUæ#š,ç©ç‡ >ìÆÃìõÏ+œ©'ü&­ÁÎÅÕ…„Åœ«Â£‰î¢pß¹áï}oðU…lÁïâMhw´U¢}ªáC*Ñsv’R8°>ˆåÑðªÿæ¢MY6ª Î¶ßþpo8gŽ6qx 6ôfÄú€‡£ôÞãšeÒ»L§€m¬™™ƒ÷zË'\_ñz1$ŒÇXÄB,<—`NÓ­?©â¸£1ö:òVO:ÌÕÍüM;ʼnæÇ¼~ü=É—úž÷[<6É™oõF%ÐSc>£å€¨Vßç „g/0¦"†l¬wx>Ö {B•wCç£k÷Ô£å>SÙ$é¹V‚hü0)¥õ[Çr 6ò¢)Iío©üÜ;'£wŽûz6ä%ž` P­•šÁ}V`mûÖ¹ZuMÕÛÈÆöÇÛÇjòšººÿ/U%ôFœÙWšF™=>ÊÕ?eÈ}PÕÏ3I•nƒ9æÛ€ ÙÁ÷ZG ®‡s.¤ˆ]1)§M„\Ñ«ìö%>þâìÁ£+TEíwû/˜gFûë%»¾ÑcêDåÍ£ÇÑTLô ¿Dj>_S£¬qÖÀWé!%Ç-óthsN£`|FÄ¿ðrëÜ{‘ »«¨ðTÇgÒ|U!ÚØÒ`5º=ÓmQùÐΚæÿk™oó¾î;^ðD3¢êV'^¨70Ÿí<ðöûû×ùWø^G‹ÏâfF‰½VG¨Ú‘žpå5ÙÑÖG±Ïs$R:£Èô1Ý*´tÏ%¤ÊVÙ÷íu‘.qúC§Žè|%‘+E|ÄqL¥U :ùó¹dÔ,ÈþƒÄO­[KofÌ£•=CPí,!ù#FXüqàxÔìÇz\<—3ÆÔB/¡Í‰ö‘3­vqßRÙ¶-Dñ¡àœ£@w0ïT¥Rí miÊB·žRúW0äx6ž/Èå™#Øý©öYX! þ×Ö%yrçRœÄ/Â-Ö´‚îÇØŠƒ‘h`¤T-D³ªÐòB¨",ØG±ò‹6V»Â~„´{äÂ­ÎÆ0ݺä¢ÅµÔ^ýˆÌ%(,Á}W§;•ì§~›ÈW*GjY €[4Q&MÒÍSƒ¦’ÿHÁBú d)mœ±îªéßʸò»âÅÕ4€_‚Á‘>…¹V§g…˜s` )Í:ºqâ|xè©®Dž“[‡»¸ÎL×~ ê}˜¬•AÄ==â|k¸eòlХߎ‡É/fؼ½©;hmø¾Ë'D?Rü.hï«P¦„ˆ/ZúçJ-+ C1.¼jœ™ad/S•Í¡V)ÅpëQ€³\V&Á¯Ã@¹Ÿí@c—íÿîÞ2÷«ì$—ÂØãáš ×Vc¯\7Èó^…غߦMjød]‘Ú±§ÉVµ(àÖÙ,‰¼Uîk³ug# à×X1f?´w}F˜¡—3¡ÓÈNÁÉÇþÐ<Àkï ŠôEЦ~Ñ—È$¡yÒÑA4œdíBÿ31»T£µœ“0c Ü)d£Q*ÀÑe˜šKt%ÃÂç±5¡ˆBâë´öLöcÈãv*¾™¸ëÆíï´”ËdB6¿7;=K7]ÚV>)Ó7Jý2ð´ü€Q0$ ¶VöV¾²7Þ͵ªÇCñ1V'Pµ@ÌKz¦EEË…8rU BÊõ ú%мw‰Æï „ _¯ÒQHw/Yµ!šß<_Ü¡‹Ú‘ö/¬/.)ïÒZxXÃ]Ÿ›‡}ئ7cÎ €9Ê´Ç?'¦hD/] lV[R¡\EQˆ÷3½ÞðÒ Ç>¯¹­ý‚õÏ…ø<¾öË—¡çݤ¼ú.’a£%Ÿ,ònf K1ä®Bu=u*ÝÍë:>ˆˆ$w8¦÷Š.Ô,¡›þgôˆØ“Ç2ò¬.íDå´ãÎ^a?ËŽx–^œÓÅ8&G½Ã[­¶ß~žž87Ü1hHv@"¸\6A«=r|UIˆƒp/^k)y8S- Èœo{$ÞCî> ]µl }i3N¢çYãJ§»«Þú×|01µ¹b²:Æõ=×ÂÃ?„{¶íæ´¯ÑÂ8áÈ_Wú§‰¾>$ê.¿ßleGq’…lEÄ-³À]§_ƒÔ(+#”Ä£ ²FC mpíža[×Y~œ†I2F;¿Ã8)§tKöÞŒù9cœ 4:_Êø ZfÙY¾9:Âêï?ògëã›ë\"¨ ^ž¶8ýâ<7ØYJfë*l…|ZùûyGa“S/d­ZîU_4»÷^ÒG3?öðµÐÌ G’Ò¦Iæ-ù±ÂÖœÙ:E‡ Û~<—,æ]ZUçnM¼˜}’ÌI*·‘ð«e2ÇñEù0fÂg¡[MíuoösS40bê (Ð,÷iÈ0-C¿Š&þÔé(¿°@÷ò q‚` ØFæ–UÀ\ýj÷t‹ïƒNMüæøŽšÄ¦r½cÇQ]2 °z0àS‚:/߆ª0¸Á÷áUˆó¬ÁõX”äÏi‹Bè<7ø(Ô×ÄðZëô¶÷=vŒÄŽÐÊY›Ñûäm¦‡P R(Þ–ì øÞV=²ó`¢ù GQ³’Dv.Kfþ×Ôq=þA 0ºí?×…¸Vþ¦±µ«…õV”¸‹O×1Τ5E9‹ž®uŒ5.‡õ2±tûu¡®\¥Õù3LÄpM1có+Ìÿ°õË©¢‘:'o4ƒ«l«Éä=áö.ìú³Þ@˜ƒdŸ¡º5`Æ]Úð„ZZéc7­–½dmŒúäùÔ·¸7nbùJøiÊøÂ,¬+Ý{ÙµÉLÄqëÊ+©ú×ò~°Ÿ¾†ûãÏ«ôkZÍYß´6KN-ÊžùW¦~ɆCŠÍ~ ¡ æj½2Ɉi)ºE!ßé ŠwÔ ŽCiVÃȺ?s´r_uw·½$ç‹n3L¸;@VÁ™ÕE `r~§…žþjIn|w: `– iK{™å%JƒÛøÂ›€Ï0½Eë}÷È1ý:Ò5^ïÜÝ{»bT£Uv:Cîúà}ÆVÀå RY†ÕJé¾l³yÍ„sJQ O›Eºj¦z{Z—·¿x"lj58PE—9ó·3vÄZh°i:ªl=˜B¥2ceÍÇ»ðO2P0%¨MÏ^ænÁ‡ì¡ÌäR˜c!&np]h\Evg Âu¯w/ÝF±#ø#C®[e®EŸ¹Å$dP³V„‰RJØ4ÔUD &K¿¹ñƒTtÍÙ3³Ïÿ‡ØË²°I°RŸÀ˜«jÕÑí­ÿ!‡uÛÍ踯 = dù?ói’Œ@¸b­DïšB'óñ8?*æêUͰ¢NP¯ðòÐËv¦üYX8€ÐjÇåç—ùÑý™Mþ„bû¥ÁÉ•µÓ÷Ÿ.PºÜ†&wJÛL£è)å, LKϙ݅¸µíêÒP¹yº5y¾°QEržù׆ŸÄ³Ø±) ÕŸ¢ŠCT)n×±äÏó®”zYüÛµ754Ÿ…fЂçú˜?l¿âD±©ÀƒÐÞÒBÌàìÃÅ7°z4§ñ¥)é¾»êBâö‚‹pˆžF<Åìµ»ê[èr ”Fr ùjÖ# ÇC¡¿75&ÏU Úóê|=z4 õÔ)Üý³NŒäKCM/æc<~QÐ8øåeZS`¸[8 BWjœ”¦`hÎfKÌ^žÄµ£zñ¡a› Ø D³ÄèLouä ÜJPŠA-ŸÑÄUJ`‰+¹«±a‰äŒñÓÉEȵÊKlnç@3Ö¸ â,òk©H¨Â`’ [Ô fáÌÐ"A’oâ®Íe‰ÛÃ¥§ytEšxxFzé_<ªF;¬3ö:W‹b¡(¡ÅzO²ÂÊN‰ö´{‡+úx¥`®«…󃤫rQ9X ÈÇ äÐú}M§ç¶e¬|Ô¾”›7Ý‹8s¨J*|a<´M,„i‹SÌ_éÖz37œ™ kS‰¹Ú$LN ·vþa'—ŠºfR»ÛÆ´ÈÄëß¹Ø2©pX†¼·éz>ž˜°GêÎÃ{ÈxQrù¨?ž/žb1âl˜loJÍwf¦ã« þâihkYÚiCOG̾˭«p’Ån‹Ì#6ÀQ! Z/~jú,Ķ…B`Ww¤“õÞzåM>×k̰@ëµEdp¹VÊå´™®XkÏ*€Z¹Þw6õûD¸»××ñ°H0¾C‚w A©ž^ z‰d$/‘¿®_ô9ïSL]9€ð!›iâfPÙ(/ƉŠ\±ˆîŸ(ƺbfç@ÅqO6‡ßTízÒVXï¯ì©X:ÉÞÿ‰÷Ê´PµÏNªPñ¤Æm Ø}×Õ(íÊ¢ŒÛ¾QÅ,aŸ­ïó÷ŸIûX^µPÚ9 Îúâ„êäÉÙ¢6²À»3n$P½wzç­“’>°#½B$£¡nþd3*Ÿ]|¯Ý‚rB64ÑŠ(‹å²A¨¤=i>õ`Dºz¥¸™ÜvW¢øc<,AÌ©õc™˜É°µÆ×²wùOi ¸÷¿gK²³p“7”›çþ0ÑP÷}6% ´ ,k_‘Ûûçcà`ÝÚ½ö¥wÂÖˆ«ÿxØc4ÀìÀmm¶iÅHó(VP…ªLú ^8gÆý¼mú“»y]ÿJ5tîròÿØýÅ ³q‹±i$Ï}2áuŒÄ‡÷zŒœZó— csÏ­Z^XÔ¦’¤;ºÖT¼at˜|ûº“Dd¼x§þæ/Œ5c‡A#zÛw5 #‡É®st”íyõÈtsvmLf5}|xáÌűݪfǹ.\$ˆ †Õ7ו:œ ³)°"ñÎuuÄg³xpËfb(ä˜È:k”N Ð/IXhL5Gf1-zó9Ôø§Yí ü‘Vs m¬p«CUOlz®˜äÄÈg{â‡Qކ9ÄSN«8;u”|YöÆ­/*·øú@ÏŒg¸ÄJ!3‘ÍvÚø-¶#cÕ ãE²õÛÐÊ–i:ÑÖeò—ï–%åX­ê9 Âdm?%#(18¼xñÑÜtÅòêWm&ª)‰‚Ú¿Sqá{èUe{¡ZF冰Lz`߉mØÜw¶Í½¢"[«Tµ>¤°{! ŠÀ/úMZ[•º.à$îr€ÂoS½5I\›µdžHíË>'îôï‹/c{æNUt?ÂÄ%Ýë8&!ݼޓrŸMcÃÚÔܼY(®h@:ÇÒëýÞ|¿ß's韉ZáT-ùºåšêxëáªCüÞŸò)ež¾¯«qoÿz’B£i´Ø.Çc©yn–u:¨¹Ôc1–º¢Wn_²ÿ¯ý¶  ä;² hö¯äûï‹mD,„TÀÖ[é:SÜþö$†;âŠöì³VKýç2J<€ÉqͱÑíûŠ8[7‰çÏ·AP•¶Ú$iþíS¿ÅT¢ïoáÿðÞ¾f‚@þf¼.§8ç·/úÙc}A”ÌK]ï2Žeµ|3Ûh‡ü("" ÔiŽŒÜ&†œIï4q´ØQà§ç%V»ÿ̫Ė`¼âžËñ.Äã£QB»²œlmX²ÆDÚ°&*þÙ⼓,cœšÜ|¿³Ý*8—Fv¤dÔÊP5üE môWKõH=rË·»ÎMF¢}8{@³ j¼rÇ'ë6›®©™ò @QJ#³üþ†9’ﻈ(fíÓ eÙ',lðÑ} »>Í«Èû0®1ˆ‚áóØA©­e„wtŒþ6|¸MAàSH®¼²¡¡—ÏHèn[À¾€eLËhµÔðÅ~Á½;¦6LpýDO60%'„õþ@ <`G/eÅIŠÖg¹^%N½°¿dmF™Á=¼.´HŽÔà‹w,¿®yQ¨8+q/³;ü4±Pþ)¯ABt¼Â„]Úª®hh€ÁA€ÃD˜h][ã`·b±-ˆüVóϳë^ÝLd Ô¹ÏHÜä¶P€÷Ö Ë)P‰°° õ›¶Œ§®ìâk^r{ÞÂ_·óÁ¶ÖFÇQ"†nœyS•tÂÄž_¤`¢™ ˆFhÁnWA†îDôü3ž¢ðˆóí1¤ÔS ½ÙÏXÇ£ÔGQw½n#]R5hqT–m‹B‡¡(‘Ú‘«‡v|§Q@à9Í‹ä³D9Ñ\:yùúÅVÀ[§-,„Kª .÷2úÓÒäaȱÓèU€ºÐÂPOU$Üó8úê|aæñ®”‰*/P:!³ðn•ˆ˜I0C‘xÌÊY3ƒº¤J!•ë¨^m(Â7‚ñÆQ¨è´×€· ËNªÞ&Ö=ãÁ Á‹“#îL†æöX"%´qUi ž”¯ø™Mct¸øiRé{'üªNuQ&ÃÂŽyV)§õ†ÚôxgÆe/nnÆwеÐMŽI``¸rú+Á"íöl 7nëKÍ…Ñi…JõSžÖÿ®#Qéþ:Ej,¡.}ѧò*5HL¾ ¤¨2¯{›jýM»€–gúsùirøÏH—»»v °aq€‡[y!ˆ·nþnkòslLtV6„¥¨49Á”4œÍû¨Z—ÄZ¡Ù‰FK¢àR+‹rúÄ¥fÚ.“eòÅÁ:šRà|‰>”ÓíK½^³ñhã^ëà>Ñ«=€9pv²Ùùòøº#Ñ5ÔŸq=Kå‹g}%HWbb)мû'þ*!ùr ò‘ÂÉèWÃ×çœ1ç+JŒaõ]•Séa"¬È¼ø!ùCçÞ ¿Žô~нÜÙ 7+äa ãp´Ö;xݴȈ#ºþÿ/ê€#]ì³ò^ÆéAxÌ݇¾Œ“We‰Õ*¡J!O´èG;B› @s¥cħÙsÿ6><| uiÔg%‘/¨­˜d¸«ÜŠÉ{1óókÝ¡ ( a#ýØÁâ½K^ÒœF%jëºx(lßG•¸Á•ß+CÂH#ˆÄÃÉ‘À%7.νÑÜw&~wã+  ƒ)w"¨Ww:'Ð5cÐþc c'ñ_G&5$E‹ü „ßà# Æ$íq;5=ψ¿Ð?gç;Ár»½{še˜Lɹvã_r vD,Ûg ÃKx­Žyq®[¨Ç+Tâÿ½`~E°F˜Ž?8³=é¤jÉqÐã.o¥fh¦@í„w–ëKx¹¯•ˆßè ¢7¡ŽUIŒ-÷þ12-} “!CŠÄÈëçÍÿiSÑŠÑÂW3ÔQžâ"_Í"Z±ü×îÌ:æîÐDÆ#~ˆzÜ”—Žª:¬ËFfOÑ(ÕHÏIÓ>¿Êǽ0Ä­md42÷š­Tª¶)`…˜ˆ ÑUv,hœSZF``]8¦M€»ôŒQ¦õõÝÒŸýÕ V,¦reoãp¹l`}Ä—Ysù,5ƒ‚¹L*‹c6¨! huW\â ?ó¥Á'N«ã'ÏÏèÎò?ŽLV±‘™X¢ßšR­W øðã…tQ4n8¬enÏ¡a˜“fÍÓ8A‚cPÿ•â îAYÊÁ†5<0ˆONШ6cÒ õ…>]šY8  ˜×⎠4Ô Äê8°ƒªBÙ Uðm,çô0˜ÞÙÉf•þP1•Úß —ôa‰½Yüi;:Ãàˆ¥A_2 Ù®F›ˆÓ•ÂÐX€¿›mZ6i“RØ… pê²?A€O­_Ýì2Ö×ÅœLƒàfl°ڀëúbI>AE˜,S•Ìå0€¢©ñ)¼ÑŽ€²Âs'ßøZÅ×Dµ°az×肹ëÉygͽ®®Ÿ!ó dÙrˆ*¹Z<¶Ý¥¹ û!Á™Õ‹<ïk•þZ$›YúPc÷Z2l@ÄÞg¡Æ;µD‰6Ù9ˆ!@ÿpnu.ã©Þ\v¡xòÇï4B’œÅJ”ä±O3‹Eô:ìú¢ñß‘å‰5Þöjd›ÜŤ9͘•?gÁä8K›Žø)Ét§vª(RÅ» L–¾H%‘IÈöwUë¢ñ —Ê× 3oÆ«`Š\ÓJŸïôßÛMß.n|{5†;;“aòl‡:hHíï4ÙºÜka%aƒ3! ‹íJ9@w¯ÌÍ‚ÏÂ3Æ—i9QbÐ99óQlâ¸ÑòvÏO™’j‡+sê¿öɳ÷•›èšCÓðÖÙ¬¤ûIÞfð§ÃI° Ýv„6÷Ž›ÁÒ"Ô?¹(ùnED¼ì{º´Ð\iŠ¡Ûè¶¢}/‡‡aÆŠ«²j!sÇ.N‰4Û£bdckp9¾×HfJüft;A“ €»?>`ÂqWì nZ€ B5Ïí\óÜôì|l5ø†$Í9)Ü2Ëß\øî †ý 6èÝd™¯9J?õ÷ê$i^ÊÖé(êè:UnPÄ‘;l-L92’Âã  =¾Êíî£&â¤=¿Èaþ™OËeÐD”¬ §°a\6R}‡Ó N¢’"›báöut¼4ž—¼ñV˜û¥îPúËIÐ\w'"ñDV—­Æ6Ô½ÃõOaW©Y*sµb³"ùÃjVP¸Ç}$dŽ„Q³©3$Œ§Î×QÅëa-ïEŸØøÃ†±f¡#Jß9‚ÿ<¸}T4†|e ?·g‰êÕÔxkN.¯ rqÊ©˜îK£ôûa±Â2K?JëЯv*1hÒóS.…·»:é³ý¾^wìAÜ‘‹Ñ"Ÿ[ØT”¦¼‘X¯™Æ˜ËÇ#„dUyȈŽnõ9^sAµ#lk,Qáµä·û\¦iŠRb7^†ùž/†h‘à36€Æ´ãX‹î}“²áýû­6CÉcÇi{«(±0"¾¬¬Çõ^žÈÉ„‹fÉŠ`µ¾í×–fÉ"4Õõ/!öŸFÏÔ°D1#fÙO;-À^yö”üF5Þþk‹)Å«ëéfçÞëÛÃæÃWÍ;s– Ô—a™œ¼§¥%ãõó+P†#·çÜ@$yáÕåÑÿðݤ=Qs:j„UÝë— `BÀ8v³Ý­Ä U;(Uõ0%1¡ÅÉ€ÍA«½Àè2D7Òæ4Rx ZA"#¯­â'¯ðHA îkBÓîy¥e“ãñYV 41- ûÀ½ë¡TI®G‘ý -¦s·<qýå‡'狼yÅgWj“µAãÐ9+ðþñx©¬ZÇ ‰ƒf‰«ªW2kßêÜÂõŠHJç ™{\¾Bb\qéŸý…HÂOδS¸ËÃÌ%¬aÝ—[!•ÏøfÆD˜”-"ò¬9€3=sø¸(ÙÜZy %“ ÿ"´ðNÎn$›DI}FOi§Ý´'o˜pGÃfÐã±;¢ú‹›«~M­UDÓùºte5ö&(p²ó_Ì+œì®í®aY˜‚°]%v$³îZ0½ÕyÞˆð%Ü>çBRÊ2²TpÑÓeâS0j'gè2 oðû\³4-èþ•sX£^¸M·Rƒ¸[D̬yÓós\ß8­gËÅF¿ÜÒ”o_†uÐ=SÿòÁ}cù F²fIºÖ6w§¬êô¸¹¡‰ZéiÅøX­L£M×z)>øY9–KÁö&{ŸN ÜOª"sï¬Ý¹Û® žáуJÅ0áì+À×`Úp«/øÖPÎ<äß"Ï@Îu·Ö7Ç›¶¯¹ùê¯b[ñ ™‹’dÛ¦-åF1×—Mh¤ó¼/°8ü¹–í5HWbÔPõÎðc#ÿc&MÂöÆuÁŠŸþM)ƒ¦£6 ®LÅ©C\iH)·Hqå+³©Ö§YcUçT1M5…è¾>À; ”vh/5i1 b¶{³KE×ÓÞ<\c{«‘Ã>šzWcQ<} ¨Íþ– ¶¡Ýdãpª½&¦à9PigØÒùŸ=ëÜ­-EŸ¸‚Nºðì=ì§JS:¬|Š!€X:p¾öàÇ¨Š¯Çñ—ë¿Àœ^s‘uôÚ^èbu}Ѹ1–Ì«6ªBªÛv˜-í!2IÑ:F…zÅ?µâ¤){@)º§‰Æ‹<ãbb|ôš…E§bÏx>DZ[Cð7UeÅs`Ì£¬b[z© Ç`s6ô½únª£fºr/y9·RÙ,&%Á3â¹=£pÅhd¢ßyÙA÷Õ%¢è—þ<¯±w¼©_œ¨ý:n¢nÌ6ñÇ‹Ðþ2²bxý¾\÷-Kð¯xîMYÐíȵ¸f¿GÅÿVøâX}Ô|ÞÄE—døÖ°ÓáXÈ/pã ©ôƒè’'»i&Ѩš-í6‘W)ÎíH9+{òá}!”6ËÚÖ®âÍ™AäU’ª±M~ÉVåC±âÃä½ÍôüZí¾ŒuuÐõ°á—f®OG<Žû×"ŠŽ[¢ú\?¦Ã5éÆã/¼UÉ¡é æë?$¶ =§–Æ]?¶ézĉQõHöh:#™+®•Unò¾®!Â+–VçcM¯ù¹O|¹ é—L)›[¸ôÙÑ*&iÖ)æ{Cä Cš°ØCÁŠÖðy$oYqåë¦ìGQ0ÁL2\jÕ$#ðʘ°"©m1£ŽäèœÑ˜9ó©A†• êbw"`p­eƒ!Ÿh=pœà²Õar™;ò©95O»î3EÞ<ÊŸµOrQÝÃSo ‰–§ «ï¶s§ÄHŒãÉ}f6ìOgŒ'±JýDøL¹>%}DkGÌ}½ÜÍfù´Mù?Sf„ÈDœ.á /¥"K§‰ø=ŽýbP,%.Vº;+.Äã8§Zoz!®Úñ‹'Ýí]ñœ=ÊÓKîDÔ£Rñé(Î' }\|^~±å¹ûZ9u€t*pè\ƒÔÅ'=<ì™Þû,»àÙî…Þç*{x{¯ I(ÞÊ#!QŸÈfZ -=•qÐóxàšs͡ѡG,‡?° gk<úBè^w/LÄ<(ÛI ¸ú.K‹zç?Cé`;µ Ë;eˆ:o¿ÑñR9˜Ý¢ë¦+£Þñg¡‹<³!Ï }(È‚ç7gFÉÏŸwšï‘ôÿ4ES¨SŸ]ˆoc–È‹< [–·„/{íêlK?¶ö8©€ç¬3ÅP½bqíDûRµÓ ØI£âe¾_æEj£—ÇÐß-çû-ôCÀPd“Äd²DV­§"œãaÏ12³bSâ¥W¼E‹8ŸÎB#EwoU=íœçÜ–]`gE.#åfb ›Ä†Å·KêôRGAXoæk¦.5ªÖ|Q­l›³ÃVó.èµý£øúþ1Ý¿Ad¼‡ú •ÇlÝΩÿ²#›;¼Ja¨ ÊE—&Ç:èÀÏ@Ø´u ˜¤G0Üä§–e©ÌÍÖkÙä¶÷]ì±²’;fŽ£ü&±t#Ëä6B š T·I÷ûñÒAi®ušš)žç`¼¯•Å–‰\œ¯]¸*ÃÍhúVYÑOB\W“³8•õ<š„àÃ<ÝÅFEš£§œDL7ÂF§¶‘Cs½$äbBu‹Q±ß¿nJ¹1·AÝ>¡Â· EÄ„ï8è&:î^b0øuüü8ÓÔÖ¸¾óÀJÆ€O8ü0ÀMŸtºâ£yuMg}&‰ 8#éôǾ+Xžù+ X¶¹IýѽõüHBÑÈ€Íküxˆ x!èY¸ðJ­u ¹ÐÞËš!ÿ‚sFmp>L8¹8À%n±W ö¬D¿»¬±×êÒ“Àã,@—˜XUÓLâQbͪ4·ö->)®OY©úŸƒ³1°™gmá?éè4ÔŽôd‡¬Èÿ­]‚\[óÙAÀ{šâã §º©+b†d0Õ(àm_Â~®ŸEgib³„¼K†%¬ºÍý´9ÒAº¯Š‰ÓgÊ@iæÉðà[RS1h¨©¼ŸTJYH©›¶§ånxž4ª¢­O]Åä’ßšîá,m´ûp†¡I_DŸ¡c=¸µˆõMËŸ¥8Qð/&#N5H’ß¿QsT6²ªUéŒWYV<ç;çSb]ÖUEâOL,gÀ(È‹‰8³¦ ¯~)Œ…ÆÆ(ƒÄÏHmÕ¶êpâ&)eøãÕ5 j²Hìû©‘Þ¹dV%â$â–sÁßëôciDËçmî@R2Vlî˾>Ü Ë™"´£ïšGaùÆžeÞZÐBmI´iPÔc7aŸ†¯Î¥¡7b?Vi AÛRbÄ$ñŽ£úcQrÓÿ6Ñšµi­äÑ䥑¼qÎ ‘Θ;æ0u€‰%½x‚Ñö¤Û½À&`[0ô¯1¹> p ¦r[Yyë‰ãK?•¤ŸßXjšñ1‹7½–É8ߑ泦ÝB²ï£õ¥ß–ÁíÕÌz˜‹ÌLwAL5ªjÈf±°ø5"æˆêmÌC:ì!Í)ô> æÿù6!­F§®îJ'ò©–¶q@ÁØËMòù¦°Ï˜oÙ$÷Y¤$) £v4ø#%Ïeqñ"Õu»08Î[HÍë\Áâ¤ã¤O¬g»eÉÊRe_œýÃ}´Ox—ãf5Mÿ¡©„«~ÚñL¶úlu“./)Œ]ÿŠE«îk‚—Êã\îÿhðè¯)Voô uOLOýÉ”ÀBTVv·÷ÐJÔí$'ëHOÕ:…Ä3Qám¾ "ìm=ó7ýî+ñÃð.±ÊÄQáûå=S¢‡0‚°çýŠ8È“°ü{w†Ë’幨Õq‘º}·.ÌØž,o;F̾4eN&TéÏëÕ–í!iƒ¼fv;€ _‰pN‰6©î@"d‚Éreòœø­íè ³…7‘->_+‚hnŠZz¹€22WJB 0Á†‹_=l­5»)6‘jµ&3]Ò+ €€À«Ãm÷É ³§soÕßEHŒbÐò-°Rœ9}¸Ø2‰lÑL±tÚð~ës²s´R~RYy"<ˆP^­nïRp[ûƒd:Á3zåÿÅôbT¾ˆm¨ {=ì’eša]÷ÅÊ%ãÊì$þEDeØ>hÎ^ýio^®Q«Õ’üžXLkÈ‚ˆv”|ùn×ué»N€v|ìs%ôˆÐt"ijU¤â®Ê=Ü`3®:<þ ¸‘Ñ™Jh¡÷Žn<1Z=8A¼dt!gbA#_Jp€áݰåôõ–¾ìÝÙ8ŽE«¿¦b[¡AÃÚæø·DW Ø€Y7MϪ¡úYŠ:.û1°¬dRn §‚àmª¦|YF IöLôK|׎ÄV_JenÛÙJØ­IÔ/Y‘Pgð œ&+&GªÜâŸÁ° ><ÜWãÃþîŒ0>¬»ØÅ›l?°2Dk¼ÂlŠÖáºÃ1­:÷ å}¢bbS\5†BH5k´³H[À«¸§»ÅVC}7Í`A@—oûà%ÂÑ—€À¤,0 »?¾P¨£íë? ÇŘFÓP„dØŽí™K$Œe­ùmŽ ¹Ì- ÇåÌmdX‘¤Ì¬¹E5’“KqHÖèÌmmˆÈõq EŒœó?œ7Ï‹ç÷þ÷}ñ{ó}ù J{ôÚ{—´NR?‰ÙÞDW¹XDùcÍÛSGZ¶hŸæðV ·ÃëZÌ:18Íðo¸Ÿ/¼%Ã#õ–8ô{•–úñæRÐ1âcÊRÕÕ#˜n÷*¬:!¿ñÖ ë]qÔ=Ü£ìVRX ²æÞk»¶Ypvk•uæwZFúÁ.Ù-õõ;õÃcÍHxøË?v;ߥNAf^ÏâRájKÕC2ŠQÁç7@Ždíí3e– ŸÑÛ3»‡)¡?Dm"~‰+ÊëdqýE8Jç8Äã~Ü*Xþ/ÑÕ7KF¤î ÄZµ°q±°¶åN7k½UÞèøzØ8jÁs@˜I_ø¥Õ.’gÑBÉQ­¤¾ÙŸv&k°eo« £ð‰és ¸ËBBœ9ë¤yàô¾GV\ŵ!£$‹ìÖ´pyú¼„ˆY_^CDh‰%o¤ŸQ¹Ÿ Çá_é'ÓÄWtr?Z*œ‚tó¾tm;+çÕ—ërìŠBš"HÁ/›ºTêB×?¤…ýZ£‡Gq0z&Ór5âÇ¦ÊÆ½u'šv¸,Üåuí ûižªÃ C¢XBç>Æà„Æð$ÑunÎrÐXÛM·uáøå2J ¼RŒ¬Ý:QH]É–<¬°OÎ%á_N»Œt3DêȘÁÞµNŸ$.æè/Ä|Yé‡íü*r'¨T6ß²°RmÄ:”¥Š>uâ¸+e¾ìðgêNa˜F!ÿ‘ þ`)ÄÚê ‹(8eÞï¿r‘Ç«)1ï wY©´ö+Ö¶ùçÞÜè(MŒ}j+®ÒàóšSü9ÆëÊ÷K6ªW™×p&ÀK÷‡‹“-æF>ðÞç=í·ešGfYZŸ“…=ÐI9ŠßQ—l<µU‚U»;ÇOóNr-Ôk76ch€M ƒ¾ÏÛdo·@õ.Ê8&+ê #ª€Vù Gt‹žŸ kËÏ>lÆú®UòC/²°³u{hÛ{fÆ}O9ï/YAsb©>LK63Mê-°)Ú7òÍAQá5žÃ<µb2Ú W‚gm-h®XÞYTÏéUÓìÈIθgmíû‰YÉV£è*ÝE¶—É¢6ÅnÉÚý õg–ònÊZœfâ»T07ûa›x]k5"Ú“+nî‹à”›ÖŒ|ÃFQŽRrÐ:O)׿¾e5ƒ®fš½ö-è Ê{GÌ = ñ³€àZ¯9 ï’”‚- pâÜyäuúd"$,gïÒ+béÇ6"±H²÷’Fën7:-;šÈ¢ìZ ùÔC#ìFùFÑdF ¶/`iwœ9¼Ä}\NüýH¶CE Uƒv0ö+ HÈFBB˜…ÿ2ùjh®p…©¯óuÙ„Fâ–Ââ6YÞØ·Œ»‹(éœénG5¶c!µƒÚç24Štú|æUUÝÊúGAßnËë˜Ï*–b+õš'Nh§´¯«l~Ù«êaËoùrÔ]ð:Ä]Ôó¶Y5D²C!Ð1ÚƒÑ7ÀdÂqy;ÿ‘pϽ!H Sá/SÄPžzÜ(ÐyZîl‰hÚ÷-mÒTÁeÆ‘§ÀvuðOÉYR¾vˆ*ƒ@¾XÆû¿Àè$:½°à¡YÜÎ2i®KJ «*³½ æ„oÐ72©¯ù!¬c{ïdúç+xÙª_"ŒÀ©kŸUùÛ„áÔÙ}´ ²•=à^™þôÂuÝE ÷EÝràúÒ^¾öð¹hˆû<ñôG«ñÄçåaU{ÊBY4î>`éÙ>è«Ö™NØ]}(ÑÊÞ:¤"‡rW§0O J§\Ÿù:n·Â9ošä kud OþžhbpIµþ8B_¨júÁ«o°"ù‘é÷ý~P@'tð[D*܉†ÚÍûÐ}úŒ›U¥" ÛlŠ£5ÔŒ6¨8¿4j¸ ¿û:’™Òép{{÷øÖÕú SÆŒï×3@ì¾Å+Ä íIÕ¸~²¶]ßûûLÈ,«À~ ½vÆé\‘Æ GÊäÆU[%ÿo>×Owë¡ QÛ/æ¼ó_£orD‹“ÃDOŠâ_ô/ûˆÿÿ ¯ùGÇF†ûGS@än:–endstream endobj 25 0 obj<>endobj 26 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 278 278 355 556 556 889 667 191 333 333 389 584 278 333 278 278 556 556 556 556 556 556 556 556 556 556 278 278 584 584 584 556 1015 667 667 722 722 667 611 778 722 278 500 667 556 833 722 778 667 778 722 667 611 722 667 944 667 667 611 278 278 278 469 556 333 556 556 500 556 556 278 556 556 222 222 500 222 833 556 556 556 556 333 500 278 556 500 722 500 500 500 334 260 334 584 600 333 333 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 333 556 556 556 556 260 556 333 737 370 556 584 584 737 333 606 584 351 351 333 556 537 278 333 351 365 556 869 869 869 611 667 667 667 667 667 667 1000 722 667 667 667 667 278 278 278 278 722 722 778 778 778 778 778 584 778 722 722 722 722 666 666 611 556 556 556 556 556 556 889 500 556 556 556 556 278 278 278 278 556 556 556 556 556 556 556 584 611 556 556 556 556 500 555 500]endobj 27 0 obj<>stream x¬»c|e]³/[³³b۶ݱ¹bÛ¶mt¬ŽtlÛv:VÇ8yÞ}öÞ÷¾÷|:w£ªF¹þ¿µæ“ŒHQ™NÈÄÎ(ngëLÇDÏÈ Z»-Œ é„í¬M_46822G ¡³…­¨¡3 4ˆÌÌ&...82€ˆ½‡£…™¹3€Rõ‡: íSþyü'ç뤓…™-€üká ´¶³·Ú:©ø¿>¨ œÍS k @DAQSJ^@)!¯ Ú ­Š.FÖÆY c ­ `jçø"ü³ÛÙšXüšý—BNC€“=ÐØâëÐÝhÿ‹`t´±prúZ,œfކ¶Î_9p¶XØ[»˜üãÀÝô+‹_Jìí¾$l¾x_Ê휜Œ-ì_VEÅÿÃOgsCçl;Y|±v¦_’&vÆ.ÿäâK—á?j¾¸Î†¶Ng ûÑ`˜X8Ù[z|ÙþRfïhñ/7\œ,lÍþÛZ€#ÐÌÐÑÄèäô¥æK÷?Ùùï8ÿ9úŸÑÚÛ[{üë´Ý¿¤þË g' µ)=ó—Mcç/Ûf¶p ÿô‰”­©€‰ñ?è&.öÿÉs:þ+A”ÿô Õ—†&v¶Ö )ƒ¼ÝWO”ÿwU¦üù Äÿ#þ)ïÿ¿âþ{þ{d)ÿËÿ«yþwÕâ.ÖÖò†6_ ð_øø_þ?r†6Öÿ&ùïBêÀÿ@šÿ“)gïÞ²5ûÂFú¯&ý§ù,œÄ-Ü&ŠÎÆæSC믱ÿ]ÕÖèhma üÒa€Ž‰íßx*æÆV¶_³`çú hkòoÍýÏTüËg†ÿŠî?Ðó_V¿†ØYÅÃþË¡ÿP­.g÷…ÿ{óÏYaa;w€ €Ž…‘ãk¸Y\¬\>ÿ;ÿRÄôŠþ9+gèìháÐþ –‘é_!ÿöÿk§ûojÄlíLþzegC[“/¤ø/ÂÿΊ…ƒ PJÀÆÈÌÈÅÈú/[Æ.ŽŽ_Øô/,øJÜÿÞÿ s@w 1ÜŸØeƒÂ>Úœç ‹Lâ~mm¶ ˶¤a•±½dšÇ¥ÍàS™‹lçFÇ­¼X›g²‡[ˆ?7C\ëåáÔ8Ÿ44[Ú±ûäò›¦Bt DCÒƒÄoÞª`Ïýw‡š4<€¤yÞ¬—‹¹…܈Šö2æ=(ú„Ef¥ló[ÑÆg·>FªÛïÔÚˆšº£¾è°S+ËO?>³7<ñð4£-Ð.A´…4 óm"Qùm‰‡…-yæ£6(“ìÓlβ|Â$6§ÕÆnêwE ^“aµ‡Ç.%®9mè‰Òõ˜ÈW¹eX‰|U‘j›~¤b‡hÏv½PØBà—L/ëgÞuÞ%–>Mîeà€ ex½¢My^¶f 0”ø!ô¦ü>ZWš*Ä`÷سq3Úùü«¤„þ%añ ýxJÜ?ìH]0¤žÍ(3.ª’Qªå$ÂÌ6Í‹ZKcì:c‹9'I–µT”§·…ÕÕ‰~ €¥Do¬yhaWÞ1öæW¡ þu\¡€J¢G‡¡Ä]{P€,ÏØîH~Ž13²:‚gz¿\OÏJh…è6RÀ|W0šiª,kyE‡†,(Ð7n¦p0ú•žÕÜ="N¨xsšz?=GáÞžƒZܳáþ’Ü~u=2šþj~$q$û›Xíi…´ÓQKn-_ÂÛüWÙ­Ÿ'Ø ºûO˜Ýx“E+º-(ü+¡“T8¹ “ñ[=‘$vvÊwŒÄîÁÁÏXÁ©K¨4/®x1Ú‹Co-§g»âÝÑp`ž}¹ã¤_F¡×0hî{|ÅK‡öÐ(&Eš ~ºd)RHN##i„ÿ;‚§ø#È«¤)^0-BêlÛ§–žgIA"agƒ{×TØüÈÙ€ÆÖ0m±+[šF»AmÅß)Ïyé©"YíÆj½õèŸð»ê9ÎÞ`?:‚¬^‘·÷ïw;þüé²ÑÛC¡ÓéÍ+˜å™MAÅ_WB¨ŸséõRx†üînö •oI0äV– ¨Í¹”ÏPFŒøy ]äFñÓSÅÝÄ*^ÙÚ%€üZô1Lºâ‡:1L~þDµ¤ÄžÓ¨|%Ÿ9¸IJq à÷ÏOr)«i«äo…O©vÙÞÊ|Å&j€‹-éf.@©š!nhÉf虿ʺ̣ßÛ€ÌôëeY­BÂÑ8J÷‡††ª´xw“Üx|‹TbƒÂ«‘Ó•¨û:v«?]3®Íw…„_«cܹlÙ&èdÅAåØó\½ÛRX¢i’FÓ(óôVHÇ0`ú?˜àT3€q)Úœì;g„âÖY|IyvÜÔ/³:¤Ç¹ãw,Œ6rþWx¡ì©„Š>%BÖÇ‹OÇ 7O½a•7U¬Ì²å¢la “©—§e/\GLuwÒ ¢ƒ/¶$g¬û[ã`C;ùYAµïÎϪ2v4Íe¼ƒxŸú b—ûMSKD®o•¿öTŸÓ¾½+8.`äÆ9œ'ßl£¸@ŸÄ%˜²Z¿ÛH-ܯ2øðL7¾jýð€à7ÿlÛZ‡Ær(kʦxï{b^ƒ”`Ž¡˜ƒ ‚0,:MàõPi‚/TžÒÁ9K%Ygß;E8(•SÅiúQ2Zó@ñàVfšáŠv5¼FŠQǪ„·R'ðRÝ[”Ç·aÁŸ–œeqé,_ŸÞÖfÉR“C}ëhYgKÑ·•‚ì’s{⥛ïØÃó“,`ã§l¹q¼†R)ÁÍa±§Îá‚B‹‰^ÓV†Æ=ZQOÂ3ºhÆIˆ#§æúý™§kH”³`kDº¦3ÄÏñ™è;ìÃý´?SyÑÍ¢v¢Èy —Ø •óL'8” E†NÄ¿hK´"“õ®‘z'‡ï×ÍÄ’f{ùO!“Dìwþ01ÓõÀ‰† Uôkä̺1Û›´nÿ¼¹JŸ‹T'¯,ôw Ýs£ šC…ŠHÝÁ‚¦›;4fpt˜”—µ¾X6aø‚IHŸ;º.7% ¿¼üúÓ‡4߀ÂõWïª{”ÇÓk„Ÿ/òO‘Nƒnè\êô¢?rç¯û'M>N01=ýŽ~DŧU•m ÚÄ$tÁ]Å«e'.¼«ëï´hÓ™Îf÷qÒxÝCuyŠÓlåâ«t$TºáÁ";í¶¢ÌÃß÷û«$h³|ßsó¬Z¹C"½wÉæSSŒ"R<™²ª„þœpˆ„Óß´(^Ú¯¯q7È2Š!¶d{nóÔ9‘3á6tæqK:ÏM&ª7°lúaž8húùkÇX*·1r â-â{àj(Ø×S¹îaö:8”ã#ã\»;Bõy\$ïEÊyø‹X¢Eºéšs¡T[úÔHQDÒ<ûXLåq !:ó&•D/F“*@òòÊ)|!ó˾éàòó«”Πltòzøæ OýtùqŒ¶\á´po p6oñ'B‡}_K­Pc"‡8úô(Z)'‡ï&Î?à¦O eOµäÖ›ÌïsÄ~åJ™Pþ”žK²‰Èg­mÔýðµIÜßp/f2G]ªfT°°U­2£þ–žÛßmü›Uªr”Û…$Õ¿³ ¦P’>žøÎ7îl—Ù±ÂñÞk€µýTVýìMÙ'Šž;öSÇÁ5 2–ž¨cû@/ä#q²‘0.O=Œß1¢XÀ6Dfà’Û˜„Öz²\aóS_E _|‘ »¦>aÅ÷õ{˜ïç-" *ëŸ eU¨gƒBŽ:HýÉ×4yÏ8r.vgíÒ¼q7£o.k¬ëéÈ 8/C…)NGSæ©1ç’|r“›X#±»ëƒ|á̦ðú]&Ì. $gnß©s±œ¦Ôxc‹Æ qíåÑy€e°L¤*ã#òþÌØjÁÍ{ÏW¾pä|DÂFw ëèM¹1}õ\.Ã¥%UçÞÁ]?4—sI¯Ð–4 m>ÒTÕ£YÝ7õèîÊè$Ç3/¶¦ú×Ö¹ý:;_Œò|U¸°ëŠÕœ$}Ÿ-6!ïªùNõ¯nªoýd®UÌ4JE+,5UÙô\Jtjåzâ+囯ªêüŽ'O.^< _ÑñG0R•Ag(øÀq[E—ïGž‡\‹ëˬ¢óò"1[^òjc)T’»FüÌ­,NN}ªX(7³vt,®dE¿¿þöÔ`Ùïªa¢î,}n_ßiiI¿CífulÞn§Û×1Ò±(óh(Ρ5ÚèÈ™Åïv,¤Ì àæø†aM¯©§”r×[Qm£˜ú÷ePìѳL)M‘ªú⊿ëÓ$`Š éôUuñ€ù¶a˜A.ö›ÊjÞb”å9ˆ-Ñ­4LèÂS0 Ü¢‡Æf…‰Bnõ¨ÿ„kíÜïÀ|cú&RÒÑÅA«ré9ðêžíÂô©o¿½m*S²Žæ¢L4(%+aoÖÅryõö,>´¶Þ[’[×lñn)Ó¤ñ/‘`kRŠh‡Ÿ7M©Ê@ý¹¦jn§ÙÕÄôÓÜMUÊzsknÏÎ]÷Å75Ü÷ çøz³/` Sš€hù"A@üŽ‘4¸B7t…’¾¢AV4Y€ç-Ô¡ö§¥ñ°ÅüT†p*-Tã–ªLs‚#©ã¶ë¨Ê¡ }Ðz]®ù7’zÊ·"N ©Ò•XHÔ%81­Ë„M[ØVõÏÛá~W‹É0AÐlpGŸJ5,ƒ£°þîˆéºGN’o`{X½CãºLí‹a,TõÒäâRúÙÆ´Pݱd=|ˆÖ¡š-ä`åxÃáo ²§o ]jÖ[·¿Óµ(þ%+ çIÐT/,„)¡¦'[ùØF{¨Œ){º.q¾ûþѾjD¡Ø„ÑÔŒ‹ŠÂcMÆÝá¿æl{³<ëBûòÙü‡U©ÿ iû1ÖuØÂþ) 4h sóùA/8tL 9ä:Û¸yÏE·ÒÙ1Ã<ðÇÂáˆMÌëg âøb¹˜¨ïkþƒÑ,)«ªÉeZ|!P&ÕÍ’w½ËݺȆ³NBî´Ö/¡èK/N·#ᔉxe;Üåæ!y Q6Å#÷Ì!íöõՉxƒÎç¢)¨VÎE²NáŒoò‰m!“À–Å—MѦÛN8Ë*™›ÖR¸MÙ€œòëTx1Š~‹þe•§ƒª“œUÃp½º<ùH¡ŠAiŽ(ΛuOù7¤ˆ jiÂã‘Ï®ùÏ `äø=¯²€¸ yö’ÎÙW)þ‡ÿÍGÑÔ´ÊbaÐ0íàpP¾‰|~ì2W†\UðwúP¢H1LqY»N!©!5³óÿ¬f˜F|Œ›MòêýWëÖÀ äØãtV´¡•uí&^HÚ´‘çz?H WƒWªÔBÀFq˸AÝIù'%+ZZùçI£Ù{}¨/|8ffÅäqŸ¯ÿË?F8ÎåÉ¥¿xM¯Á0ƒòö¾¾¬°±&Â47,Jô6¸—G´L镸;uU%lùY;·K;þŽÀüPÐOv”ÅF—Ù Çîü%Ñ 1»Iôò9 ?î;¾<š•ïn뵎ÚÔK3.ªñÈ­+6…Èшówþ\w 1”|i=ÈRŠi³Ú;ⱎþÌÕÂÖÆl[qø‹´@¸‰¼ß þП˜^)óž‘_5­‹g á`÷øënFµÖÊ®ÐuÈ«V-]QQx ¢’8! O#ÉyÊjìjz®ÂŠ ¤³·ZúvÑ|0MœáBRŽ>”^ ÍÎHx¢æ2æ åF§À¢]ŒxRZdÛ+'&âݳcöÔ8ÁK…—ÐÞŠ)"æöcмTÚ¼@ƒƒJ›AãDÓõº³ó÷¼Ç”¦m/Ii©r²Y Pá:*É™ƒÌFÀëü“ÿÛ÷ã9á-ßÉ9z ¯’)8uî€à¨L>®¯gùP›?g·Új^ø3dkeœØÙ[6vüŸjIݰҴÌV¢×½&©÷ CMìNûwÑlß½°»aøDkTâ»,•úß&>uaÒwÈf½Œ}ÈÒ”çQÑd§þU¥±î%*ëžHH C¾íÐÎ ØžÇN-µzÂb/ÕßùpeËÛè¹±üSÁ] äÊ£Ã%¾Á”!û¬õd­®ñ¥š{U8 <„I YëµíUuñáWaM˜µÑÁ Ef¸Ò}üý_ݯ€\ÞyÕ t)V“L.ýúu[)÷à¯NtX˜^a›ÈûúHýÑo Á úk×DQ¨Üò=p¶˜w:¹-Ky/×fnt:Z|ij-òÆLl…„ä}HP8Ú‰êáaq‡<É«ö  È•WpüÛ³#§ˆ/â×ÊÅia²nÿ#dÕaÎÃ(.Ìvîfª@¾ÍZGŸ7Ï›ƒfÜÄ‘y¸ XáÑ«ë ŽÇ°³¼ fJ ² ¦s"äEÁùv„Gß°cBQ±Õ65ëXöëzùôÞ–ÙG—l­’÷Ɯтj˜&é†j€D¡„‰ç¬¤ü#’t'GÓ|‘ìÇ Ù\?˜ôŸŒ´6ÍÊÛý-ЬœÜÑp–•TLÒãÀ´´8ËŸù¥¥©¡us§L%%I‡&¶ ”€Äµ,]O‡^ͱ%û:ŠÕ¼u¿7…ûÔAánË«ÙØÝ©u•sLBûóËùÏP¬häXÙUY9v·qé¤tÚª33JIò-ùÍA!uÝÛ'0!Ï@5òˆJa° UaŒR›¢· *N#•-rCÑZý;Älª³Ž;šÃû‡o’œ ,WßjzƒûËÙ—)K±®ˆ¼ôiL¡¯ò¿ï\ éŠc»—zÍ";Ð5ðฺU;’ÖŽ†#·ÅÄ BZdÁóù(Ò9dßAÃÝÙñtK'ÙÁh8cTGü^óF¥ñ?XC´ RsO޼ |b¢{†d­X¨Ûˆ²Œ©ÿJ…sM/7TúáA§6 ·O]Ãg픕¥ƒå £µžÝ#ºÑµ3Ë2v(EücˆÏ)æíO£µEƒYŸ"™G²¡m}¯ãú3Äe´”ÕôÏ‘Ætû¿)ž‡i/ŽƒS­Çq ëËyø¼<ØÓ‡‹ã—F°„¥+ÃÜ”v»w7Û)L”±·…%m¼‡i߯‡É»¹RB3ëLG »ÅãÈÊ(€O:âÍËHµX¯[mBÞ(vuÅ Û1_ˆR±¢]ZcË¡M])ÓÝõõðA€œ­AÖ%Tº ¯{6—«.O®ÉN¤·°Ô½Ïôx§ O³'r´…êé@W p:?I’¥ÚFRg—‘9¾Gƒ±áÖ± ›² ÂãvîDÄr¡ç¨ ¼†>ŠnP®‹ÒácH¡ëv#|JžaPk±þ¾ ³EëaªAÅA6oy¨áV=lnëÈUN3õÓÄì'Ûé-.mâõL¦Í ì–½Vj"È:M&•g.u‚¨*F¯6wIK[Î)h¡1" CVãH‚°G ¤Ü&ÍpԟȺ­½DBï$³\¬3A}±õøZ bY¿Œ÷gË|Fm²÷%·Û€†ŸÍ‹ ù«ž8YU|F‚ô#é}$QŠ9t¼0 ž Yhquâ¬Óç.‡Áð-m/¯$½1DwüN¸˜‡_©ö§ðŲû¤ÆÔ¦¶ÍGžü¸+YHüß;J…Lìõû0À)Ð’NXã?H/<ó¯ZÊ0,ù bLQVÃbÏÔÂ7Ì,#.ü>˜OHÔQà§ñ¨U|¹w¡,|ÜÑ=Sf«Ç`äúÁ¿Íæx…ð}Í&n ´oÓÂÜ®ˆî½Ê|TI™|Î*is³–ÍÀv+7¾ºUB-eýææK¢Ü‰ç7×éÑë )©G Àï&Î^„òùïT¬Ë-–Ìÿ ¶¥ëø×a-šÕ2a÷D¦h¨l (ƒCŒ@ÊiZÆœèn•ˆ¨ŽèƇù§€:ºÖN¹Ÿ°ÉYá†/Rãë—‘¨y‹÷Üê6Ô¢*ö¬Ã»«?á4¬&²êýl×aÌÄ= s»÷(¼,æï¬R3ÍÆ‘|2H=Ë´ŒlÞ3+¦’{ $/.›¸hÓÑ-ÙÖ ­’·X«;.ÁèÅ*‰Àu¸cØhÀ–Ĉ¿TQ–åµÁ†Ãg¬6ѬGo¿ì`ȇFœŒO@¼V9ЙÕw‘8(zyÔã1²ešZ<ÿÙäT…¼)ލ Ñèë£jÁ‰ À£|õüí“§a×SFjQóØïW2ùODºŠ,‰'k&3xBë©×wl½gÖ•à´Ð»Þ¡ýSàÕåqp5ÍX»%8EõÚÞº¤µÊ°d„"ô]ŸMŒJÖ8 š“"²M2Œ©u\¢E‚kýÐâZVe1Þ­5Ðó8²îˆ†3Jzœ¬-š¡I•8vJèÅ©ò-W¨@aÓó£µaªFçÔ[ìS —T~ñxÉrþ֪ÝRì¬QÎ=ØBxçEEâ;T× ªjîjh>¤N«:|ZËOb¨ÂSþ€pJ©rÈ´6IVQèÑÁ ¶ë£¤êE|ÚÞj•!ÃãþAòÈ#N®4HŠQçÏ5ןbA2J·~K»âblqPMUo›¶8ÆÇéõ¼Á›_ãÿíïuTü¨»¯ÉO¥c øó!ugn›PDRÐŽƒز–çšvZm×ºŠœûÆÉüXüÇ_‡Öaѱñ©ÌÞO‹§«d*Mº¼Ž‡ã·æËÍõÙR=ÑS˜h*Ü‘d†ëˆ³DŸµ-­»5bH_ÌaÆD¬Åßbo3®jFõ”Y¥»Ta/¸L©= ^ZÝ-t¾Ðì'Xsr Á¢¢Rë®…D‡~)Öð(uî8¼Å¯!ÎwzT §ÏEz**¡RÉ ,*Ã&v7ËkþÔz D¡å$ã†"Tƒ¸9t c Ù¤e“NÚÛš ŠËíiÈžŒHg öµZ¡àiᢼT¿õé6؈_ÌL‘óYæß›ÌÀøØÚ¡º]äéù%•·H_ èr¨ËèN]&qÏ›»‹Fì=¦2¿3k+õé›JZ²+ÆVîû-Iwh¶ª(“ÒØ±ya)„2ZËëEÓàùïŽÿEùåfVïš[ØÐ‡Pþkk³\»‚º5ÅÜSñ4j¶.P°ò'êïÔS›nŒ9Ck” 3û³û¼}”³ÿû%¿ø¦†BE]gs|¬XÍ>JQžÓ€¢“r=Îë7“Äå6ÓŒû(À.H†Ÿæ˜TßÃŒ©áyóù-{+Îåm)^9uâtÓª= Ó4§Ný ˜éÏÁà”«|/£¶¾4…(5/êlZõ‹õ2ÝlØ(ù™ÓL•^ø‹‡ZÞØºûþ DF¤êtÖÏcN¿>oï`Zàïh[`ñkgõ-j9QÿŠZYÖa1Âñßn(—Ǫ²Ü]&±=#™ykŒŒŽÏ¯ö'®Eý¬#Õ|ñ²‹ÜÊΓ†}ù~)nºÏüwñÓçj'#ÛKÔ¥|~„fYIqº *y,HÑ]käÑï4‡AøÝn$öi­4œo¸ásû̡γÕº P Ñ·2Ú ªÈ¾/¼¨…^ö3¢„0½zŸ±?þ4?Õi-X…¥¶}/ÇÞt„¥ÿ žR~ÞACÛ'㣫ýã‚$*Å¸Ñ ÎÆvî_ü—oËhw£Dg–ƒP¼¾õQŸdÛø.!÷¹X¡0f7’·±w€=Æ9H3¾ àú›ßĹîÄKììÀN˜½U}ßðŒî™=7)ˆàèñUŒ« ½Êèm¡#)=ìõ»[¸ž¸=ì–jqÕ_™÷‰$c]çT©¦Àã ÛìÈÏQg–q§f°ÀNøÓ@¯Ê-& T¬ðŸtÚ@¶ØE‡vS¨œ§î ›.§Ô %UÿC×ïg uƒwwµêÛžx Äh.¾˜SýoC3=9ô»ƒîå˜N©å3®ŽgC‚¯÷«Q{û'Äqt`ÚëèH]K†X±…µŒ'M³ pmè¯b},Äš6Ó¨PÚŠì99IøæÖÔLòº…Ú?’àë(bR‹ŒÐL%è'ß)mÕ(ÿ¥4[Ôßé$¬š©­™êc²mòµW°—bEò£ØäÆ¡†¶2Æ@÷éÞ«`õ+Ì=ÿÎÏ€ÎùI›>ýÝö$§FÁ꼨ÐY¤ýæÒÊÌ»3 Ý Oï^t¬.¯BçE2o=n.œF¹¢6¹–é*CsV1Í#‚âÄ(H¬D×L¼IÔ{‘¶ê¦íª{&ÊJE@`þɌ݆æÆþ*fk»Tjoê©Uqs×v¬&ŽY&tb{u‚Î*…bÜ™L^Þs‘Á¢Ül–CºÇõ-›—°ø¹u^zËЩ–¦·qW¹1iÛÔ׋—bÇ+¦g÷=©„ª ·Jõë†RñóÉý«ršô%/'#ÐZ$gEŒKjA¼ÊaGëRµ2 ÙÙšj°âÜ܆¶÷D§šõR…ƒüï5=¯IÙhÅìØ”9Éß¡h‚1ǃ8×BÀE”ΛÆódÿPÜ3ú”=³‰›€{õ}™øŸg)$«„IÁþ/ ÀÍM­$Ÿ«ÁæMþd!²¬p¿#GœéÉ*(g™j—(‰Xâ,ÂÆÑÅ?F¤8&ql}¿E$½õÔrfņué¶pô2»:Ä.Ää®­W-ɿLJgÄêVuEÇr*…ž6ª5À_’ñâAqÆ©c!Êü*?J¤¿Cºh5˜úÞMm @'}$ù†ö],Å$®v äweÌßèS±z¢WâlbrñÊûžésZg*jôz·È¢Y•Ú½ŒÔ Ô„o¬QDqîzŽãçüõÖYlJE¦ûáýI#‘²wÍç¿NdN ˯¥[»P"ð@1ž…,?•6bó8½FgOáÈ£!D¬V¢O©Ù4Ρ*Ñt±$† &^¯ ‹:¹m™¥•ÄŽÛøÃ*1èÚnº?ùöµˆ¨öä©ZÂBGÝ÷ƒïnÊM0IXNѯ¥Â—J*Ì!º {@Fž¦?ÔÊ ³‰&• _xVºÕ{“ô^"‚Æ:jí$1²ï-,B¸æ3©AjÚ_O NîÅ„ßÊËÔëæm¬yëüh¨agü'Qz5\BoÝÃÙ8Ö¤&Ô,•[Ü6OV†NÈ1uôAîwÿlóÑ?ŠÄüùX©:˜å9ít©#¥{>ÿ ¿`¦§àO½Õâþ-zsíÍÕën¨u9‚Aÿf@µÛŒ¦ÐLD¼ÄD^I‰º—Õõ\ üY²=3¡š©¸ï̪ w…rÃ5eœá1AÙwŠ\âêèèÖí\/Âa¹ÜÛ#1NfŸÜ‚{.fª5k‡‹´TÑÛÍ Æn î¨s)ÄRGX*i‡oÞê²G  ±YW9«ºl=ª¸EݧÛÛØŽÕòñ02¼ƒ[ÈXǯf'Š"ƒ¡8vÇgÃÛÞñp“ÂEÏYk—ÂÒ¶fäÊ$€´ Áʃtš@v‘êKI8‡<§ºVY刨²ÄˆŸJ<Œ¤6ÑÔêX#š öõ;1íÜx!Æ[ ›³ÕM¶#±Õ \Å`ñ%IÔ̾Ó™‰êLâ Ò6rºšRA:Óñ·Ã×+¿ Ë*KY»M5“õTó DVeóâŒæƒxC¨ocñ˱T9›+ŠòjR˜6°ŽË3¼UbIó Ÿƒ/Ÿ?n7tgãOdŽÝºÈ ŸíæðPŠ’ôV驌*-娗ƙu³EºqÔ…4 žÏ¼ºÃº«äqòÐ6%’¬€«ÂœßŽüp 7zùËe³R'bHš~‰(4×X¬ôä³Äåd?F‘wLñvdo˜P­h£)IO´zÇL Ò…¬Ôðé»c Ö¡©È$Qç5!©§O:UlfØêîBXxó‰Ûw•ÓÖ#“—qe• ¶•'dY ±0Ô% ~qÑ3ŽJØNCíÕl˜®^š{ÁŸÇÖµRsvöIÄB¨QÙÐk?/ÏØÂ²9WÂÆç¹}ä£!ÙØÞc¿Mv |¼ßóÄŽX(ýb9l:»Ó™üP p‹W©g½ŒEDŸ589Cô4òuGÊ.”´%pÌ;ï/û±ºÈFz³'PÖ ì05ø™.0¥”5"áLصD„fÝ ðuÍIµE£¸>)…œ;7#o”hÇÙ0ñ‘ ŒÏGM=QSŸcäpã2r"°g\ìò*·lÂÂ-LwõÀ'¹TÄmYؤÓoì·wmE·ß0qþÒV#/£)c›C‰ëñ‹\ð후ãC»§°HœmøímËà³fêâô-ÝÒé€?õêòðìGø'Ö“L@x)‰¬±i‘~·L©ÄÚ('y§X”Åc ìÅ9!z“oýº)æo ãKñâþ–øÜ14é1VŸX½p^"?Éù“ ÞÜ˯do(iW³!Û_ÇF€Œ<–/Ô,ÝÓ¡þ¥sœ™*‰;VîF’ülv‚ ’º"ÕI~}ù­œo!z&Ù<ûÉ%ƒ¿-Q0ë Zð ÙyÂ%”õŸŠú8÷Ñö Áåüy+dYªÍ­;êQMRÚX'h¾Om/U)‘ƒZêÜ­GÄX\’C°¡Umôcn'!x˜h¤§$ñÆßa{™SaUjŸ—j ßëÂtžÉ”Í–ß¿¦Ðãβñ4Éwîß:þï(­¹*Ý]Ó»¡E[o waŸJÏY{×=Tá÷L ¯b“ud=ꆘR©Fv\áýuðjFÎùµtžáª0‹þ}ôyÂcApØNíü§µ2dǒߢe@I,wC9Žq¨[nD†MuSC $§y{›ãXO$ÈŽðã¦mCÖ_Šp¢ZjY$ªOýÕ0„ÏmC:V¡}zÚuª•mÕ?©‡‡6Ù@ßc’¶FÒDØ4¦ûb¶ž v+質±M³|=‚)‘f¾Ø÷\ß.Ä0<ãæv;FüšDÕ‡ ÇA×¹ ‰¼tgà-4¦á˜¢b/98ÃØ¥T®²š›'}õE3·ñ\É 6}Þ‡·ëº á/ eUü Ë]éà%ÊUÌ0É+è HéÏÝ·ŒaŒ? ¦ä “5W ³K-H…ðG÷&Ê(L³3]ÜŠK«'ÜÖ‚«¸…K‘ÞwFã0Z”eqÃÄ>ºF…Ñ+Šš ÙWttý ñs“›~3üÝ ŽnÝÊ6æn µ-%/h¬£æbDóD¬]J-«¤Öø‰ì‡…kiW‡¶R^ÒŒåèÍ&¿.[¥ªr˜&…ò:Ê\ñžË/F‡Ù‘¹Á”-p}{‚‘¤¥LÖÿØÉTúDS[œ¹¹˜;ÝÎx²m¬ ¤^ïÓ‡€ònÔhë »zABhóñû ó(³C¯ÚVvæ‹ðrc&ð ¾›Œün#OÍ}&w³XfgÉ×½—ž®É]xGNÈâøYy—*™V4Æ<˜/Ç‘Ë3vt˜sÑ}¹ÇvC\0](©˜.u9aØøYVÓô[tz.b–wÍá“ÏÓ¤iéq4"|‡q°³¾bËIù×¹ !b™šh‡dšÃC Ý¾©^B³ ìV'0…’Ú·À)+’²ŠÍ§Å©tÁÁn£ªÝ eÓ”ÂwÀÿq¬Œ¡e`,躾ºCúp²x.ÝK-Ùʬųòƒ§¢¸·>¢p$D»”­|µPÁ‰Oq)h£g2!ìþ#b7ôȈ{I²_2ò_b$ÍÍÚ²û3 h¨ùHÔ÷P¼9ÄÇX§îŠæ”®@Û¢ aúîú7¹C¤o+Q³3=bff1Þ%E=§îðD›?Jܦ©ÞK[ްUÕ”€ÐèLÂw&"h ¸Æ+–Pþ1ÊK­Úšucê %mV:@]8'‹4Á—ïž_³@È)¾yo,SlxÓ1©t\ÿ›ÎA²%Œ õ™y”$FŒÚš •Œ‹6|~)mp´ä“’ §Zº÷öÍ-ê SK\}kû·ˆ©úõŒèɦ–{{gÔs¬‚Iqß¼( %z=T|‚ŽqÒ³Î쉌[Æ.‹P|Ë?šm©ó:H‘Ish›þ—£>wñŸ7¯,W Ù©Že÷·ÁÀ4F"Ò^0!…!m¤-»XÜ.­•¬í’^‚y/ÀÂçt÷hJ¥€òwŸßÃN“v¢k†R³Ï>Êb]ý6*(X¯wv¿·ÎOb6­œx$‡ ås³ÿ°ÛŠg[`!t3ˆl¤Hë`1ÝЪ‘­™@ÆÊ—<"»ôâ†vÖcžçi¼Ÿ‡œ àˆky6m Š‹'4®mÆòɾ6/£1{˜Ú¶*ûÀ"õÌŠáeÆæ&°B/ ¤²euŽÀ±;D=½yQr‘œkçÆ=!rÈÄѸh› Þ’f– 3N@H8ÚÆDfS¹QÿlR¸½"r™, ÍyÖ¦ÑPîùe C]¾Ú­àŒ\ÇÆÝ”]4\Àhh3;¼šM?ºc¸IåZik8þQõÌ5.!%ˆ)R˜­½œ#” ÅÍ6º$¾MÛ8>ÝÎ)å`þÊy£Ìnð1xçu“Ň£Ñ`B|ÙGù]®øQ ÂëøaC¯Àvu‘ØÑ(õîXD¶åŽŸ×dýw¸"jÇ¡_³hæ3œÊ4ú(L»áGñзbê]jñÔ_CÖ©û\äX›Ü#«Ç\,Iœ<'¿zöeƒßFpšpJ"Êø+äb;È÷àxÁG& šÉêòÿMíøPÕ’~ùM×$:.¸ô¸Ö­O_’ß‚h›(]—Qåágµ`ÍõÂA¯U0ò7”éíè6fWQ‘²ÏJnžYÞ…û¯ïÌßæj~|ù³Aiy}n*añ2ùd½>RðÌöç¡–ï|µ=v)ØY”\êÙ½«Ò‹Y&HWA7 Ùr ìºÊKá7Tû1ÂY±‰0yP2’öï¦ c.;þ^íœôÞ—,Ó”(&®Ä•¯ Ò™„ ûEd’‘gv³ˆƒL˜øï«xi‰Ìö”B‡©9jíÁ]˜äAP™êªTRO.9†)pª“Ù´¶QZ¿´ý+ ¥ã ¾  ½ì=´ìÉÁÖw¥ËðÕé’ËÓ}c¹¶Ø ôw0gĸã/ôûxæ‘b¸ükñÑÔðõÔ›¥ÈL`Wš+b´U.öëû ¿ k‰Ës¨“ðk ž{B^Ð2isäÀ *(ÌHp#G`£ôk,ÊSžç]‘L²àÖ̘m“l—æ,µAfÜà×Ò"-ÜC›|42EbüP¯¼‚uB¦M¥Šû\Ò‘}T×8q¸¼}1³ÜÖBi¾Óßâ"ª&_PÁ©ÄG/-vó‘5±+áÉšøZ¢6:þó^e6Ôû𝑬E϶k÷ŒúûÇÑlt3Ö2Q3qS^ «Õ1‡ZòÆA®!Ük›–"Ú˜Âu$Ys½ð¾Ýè¬}.¯Ž ðÙñOaÖÏA‹ï’XD)—Rÿ $@Û¿çX‡1À€ºWþÍmP·Þs½¹w㘤ŽWaÙ Ðó³ÂVu íœÎª2NzÅ5YÉ—{’ÉÔMƒp©Ï.É´´oä½Ñ¾ËOXàÙ¶@ÖÑ\òð/î[ºz_Öñÿ–§-ˆçNhõƒpoôI‚às[LžQY"FÓŠs[ø Àì_CÜ’³›±HÀZ2âì»ÑgýRáû ¬af3ý©ú¢váD¬OnEv ñ-iE¯µQr3ê˜sZíˆu2ÌÌAÍ,mrî4Ÿžœêé•3e°»#U’zÞ39h}©ûñîô§# ¡ç‹u2ø ÛLpÚ„¼s¼Ø@`—^a·ø¿è°pl)²!I* ?õˆg1HðL ê² IÎ{@CœŠŸöº5Ï@½¬µ™«ÖÜc³Ñ%À€k"{F¦H¡QJ¼Ä¦ËÔ«Š&Ý. !•L{ò¹X\øa>1¯eE[¿üzã:êƒ\kCÊÙjTÄ.l½6SVI½ ÀM ûm05Y–RèÅqp•8ã²áêpƒ¦#8ߦs}þ=‘q½.M£¢³6¬ÁcATþ¸a+E°*•bu2ö£ÈË‹cÒ.è>Áóî|Àò‹GdC¿ìÂшÍK4ê:é" E1«:œèÃd’é:ÿ'»Ú†¦´!œiã>ÃXp òzÕ²¬ãv˜+2©lzSMFoµïÉ‹FæÝ@¤œA·®ÃžœùÉZ"eÅò‚ذ¼rÌIÜ$|޼KØ4-…åÚЋ%ß­þÓ&T½ ¥ßÝÂ¥Uáé„‘ð‹˜ÉW=Æ$ÈŸùË0m…~Ñ ð®ã³\m$;ë)a¾ÕkÜ ß<\×' «—_9f jÅŽå˜ÑDíQÌ>9>`Ùá<Š˜‚Oå[x‚Ö£·$Ùq¬g'/’SwÊQ¡BÜæ`«~;+E¡ý„i‚ý™¦ÂüO2ò`êÿ_Lã >5J‘ßD# “|‚òQ¥–=xbü$ëËA¾q¬mfº qCÑGÌÓï}ÚF&[éIcÝÚœCßAGNWò‚¡Þ Ioâ¹7KàP=¯Õ£Ænôô|îÖÃ,·v}·Ôß{?YëµyŽºˆjI†Q˜Í\æ(2ž%âÀ'”Z,,–ý\ØÊ®6:݇ô±ÝÚïxôOeÛ¦Ý!ÒÒaòæVöYÆ”ŒÜ›'º›Ë—¸ÈÞøÆf!uJÅzİfŸÉBtãøn2t©¹­‹Ýv˜xÿ^nõ’Õ#kxî.¬4(Í1# ®æó0&™ëB(¡ bã¾ø©B¸¤¸ÀvâÎFT(]"sDŸçÒºÓA™$®ëQýNöC¾žÀ}g„]ÅíeCLÜ©À¸tk¯†>¤’ð…lÜï„ÌGâÅŽ}õ¼–é ÖÃjÂ,7ÄÂ_ÿõ\öé|¨{ºÈ·qŒ4L»W™Ë_A’!IKM¢ããúI¼„Xk@-±¶,u^âì âd÷iì—BÔŠŒ„€Ùåÿ÷3h¦0Šªœ£ #*\Ço7C—LŠà«4yìÄôT'Ã;ŒYhðEY°­vJ½‘€× Å}ÙÈ.H])Ùêp#H­G#AÆ1œP kÿ¥ÖIGÛ9ÃEšú9m6oÊ®¡©åñè«yÃåóÍŠ\êƒ?´n8²=·_OÌŽ7~˜Å“ ®J–¡žñu!N³’ªÒ!Ö¨ å e¦–±7JÞxʹ9VÞ)ŸáeÚ¬‰ÄVóØIXÑMqjåÈAê·n¼aÍÔËß×3,•FS-„âðCyQ cÛ*YGÿR¹çúÙàù@ãœÓÔÛ¶7L£÷ñE}åwAåô¸RóNHb+mS^q€{E¨ñø§j6{²êƒ,¬¦/¢þ½¨Ø¾bź×n©xÛm]ZQên%³Ž?fã=  vð¼tİB«¨*záF4løuN³Æ''´}Ðð¨v…äøó4êh©©ôÊfø9FÛ¯:åoåuF§N84Ζ `91T×M¨îë“¼ß ­•ô}Òq¥ïvÇÝÒëºÞlTê“m‹1æŸQ§šsÖ®$ÁQ®è`çí¹6.S²£‡fB‚ŽªUÇꎟenCÛà%6ävº{—Ϫ–¯u°‡µ}ã[ÁËûÎð(DÕ o4Þ#5bòn¥…O-¤³Z½'šV³[Ü2ëœ{NÚŸ>LÀw6¥¬Ô¸ó)õ_|Ã")KçÕçSúƒZ.·÷Ì|É¿£Ü-Š›Ó!Ô0š»0TB*n8~’[‡ $¸;€æã,™Å8 "Ì'“’ÛÒÉkN.å¶NÓØá¯s«yd[bñ]9òE±w0øÃo,iÎ^‡3ù íÚ#÷P&:’ȈŠé7”CLcQ. %tÙ­éêöK®mã¨'u‹"ÛIòHÑzÅètÉq#|ñóÃ.[n 5HŠÃ»HÜÐaA‘B¿¬pã&>Bei/å*– –B…CÐd¥|>Ó˜Lùgí'©‘!÷A/*‘{—© *·é7‰Š.Üš•ù6¼çµ€ X ‚z²atñÄcË{*A®ô:ο¼È Rø”€l¢ ê‹«ìßüþàZc3]ááW«ÈQ$fÂ˹÷#fÀL¯ 5­Š|øõäOýݹbbÇïË6 d߉BjMë‡î˜î M‚1 ð¯šo‘Ú|+T£ÓžÈ{®Mæ J®ýÄ‚µN¥#•Þx´¢Ic„˜©¤y|Ætz'…ë+[÷~7;.X"ñ(ASU`%ò×ë ØhNLXɨf Ç\*!¸¾gM ªÿQÔ€ÍT{:!§íäg²ÿЇÖK‹Ð™¹'˜/§oÜsNS³ e>Ö&<Ñx.Xa ç›ê·Ë»~ÛŠ«ésBªÿ@yZNBHBY<6®öJSÜfE^F%>¸¬7ìcÞ)]^‚ú_õ!t~t–ÅhjªæZînB…sT?}K}ÃqƒgÅŠSx7׃Î6( гÒÎÃÙ •HÏRÏ0E‰Ì9È:ÀtEGŸ€„S¼TÐÇ OEÿ ·Y>.úª:ÈÙÆTŸ­<¢ùĒȧnqêF>±.M<3t6ÛXø´”#ÌžúçU‹¥?Û< cýÄì•N™Þ$v d‰š-jï@%…ó“(Gw„õ׋ºB" Ü7[ðÅg¾®{÷Ò|ÝÉÊsM_õvF` ú‘\÷©7¾±H.«-¦_‘7J+rwn#<#ý';ûWMMåAþMgÅìcä=û;Ú£8£àÁ“w±Ul¨nÐáFÉhÇãŒÉ¥UûðŽÝÉ4•ÐÇ67~|Ëõ›çÍZdö¡ŒÑÖ>\6 N¼’ì$ÖU™GuS¹g+Þƒð#*«sÙm+ÓZkÃ}8Ýâ±½ˆMÚ• =KiŠ¿BF/RMË©ªß™°;x¸¹è>7D=É,•qt1(ѧÝu¹§ÆsƒÊãh¢ÎÂ}Í5â7X Lš‰D«°U‰v`,xÔöî¢:U·§Á¯J»IÓyf-§&‘J»˜}#NPÓ?wU{Èraî˜@¨ßÅÄÅàå‰ZJ.Œ!ÞûùŸ½¤­¶•g•€–¨J184éãIÓ:Y>çÚtb(ŽÚ`„ TœjöšÒþuA9Õ5Á‡z‚y´ÀW> tµC¿ W$¯Ù˜R”gÜûïbl¸´Yî{-šC•OÜ뤟 Ûí¼ìU™Æ‘7þN³Öz%žï:ZÞ¦@Øß‚Xô2{w£œ Ž’l¯3F7è ‹ú®wiìª!ox¶ ßt²€^3óák±ÒÿVц”¦K1Jm6F—)ûì[ ¾·ç÷ Òç”~OaçìgÏԼ鶚YVr1}‡Œ ,žº'‰ã>ÕŸ`°m¸BÞaj’ÀÇeS‡Íô™!¼ÜΫíBw+3*«Î$þ7k¦d*ÿ‰ç¢5ú^aáXs"vŸíäBÖøîDs‚¨üÜ,»[MóÛ󶳘ºÔíŠÅ4eÒ~@Î_…B:åqÄÒêŠ}T¶¶póõïÚ| GhМÀÖ$mHÏ7oGó꜎Ä(O¡aGXš-=­}M¬¸‰^\Ö +ýhbuܨ{NÊ垃©´V°´§Ðâµ1Õûæ(Ñ™çqzW‰BÂ&x™rneƘb4–QN UÐ6€ ð{Ï»Á&ÁBâsŽpÁ÷ˆ÷ò‹JªjÏ\Ÿ 7ŠêÒû^ü¼Íš)tzÉ×VsCÿ?Ó‘^” q9´cV ¬ØÉžx¨ÛQæ@rV ׌¶ÑÍÊ´îÏ}ìýx#§#”ÿ±“?ªÁñ¢9¼»¼ Ç@Š9^÷h…ùßJX‰9û@6$P¾f)>]M! áõi¤ÀèžYUõ¯ø ¨8Ôð>M ‡6é?+e?×%‘ž+`:éÏf¾K½Fè\_púyà˜¿Kä¥{Û9ð/hÈÏ=iûy…BÓT\;±6^FÜ`ÁÓBÉC"î–üÏ’…WoOGΡxj,ž{ëCßXf8µÄŸVAéþòB2B}[B=Ë—×vÍèD¶‰F>â\-4ϯ¨ Á¦Ëôn[Šß|Q.ã锩-VM­¿¨3#¬ò ±™O9¡ g´³-´«ØÎ÷ ¹Ñ¼”‘m^ò©—ãu_Î%~… ðØý‰·Û8ÑeRÂ3;Ä¡§2a•_<„,ߟéÈ¿K«/äò¶ëe ¸Œ’ÿý3§{ỡÛkÎ3jýaJ˜oSJ¹ y¥˜¬Ü4ËÛ/­tHîÓ¿´Kð?ÔW ]»˜™û=ñ‹7\€%ƒÜê¬8>D‡ƒ[Û}ª“pÎÓý⧺gC—cøÐôš"ÄŸ•‡_4b+±ÛÂTAž6Æ6¼2<v]Þt)Þ¡P rWôµ<e¿UÙXÐ,æß‹ç.àt{¸…ˆ„õ8C· ÄI»•,wÿK¶yO7ð­ÅêÝ÷h‹œÑ1·ðKˆ‹k@‘oAmù%;CíïÙ°/쎠 ²)^à_!¨O‰`]ñ´Ú×w†Çþ´P(U@è üÊøà½ýÚ³ÄN]%âkæ.A)ácÙ,–‘…¡âÚ@ö՜޿V¡¥`ç(šúbÌóﲎ”¤VjöT©ÇÃK|4ׯ1ÖkªHëåFüÉcÓÓ궛Яs±â¾?ùµm>èǼNN)r0‚8Hú«åPcNWQ(Y¸LG`Ùà ™øbqïMÅñ$ý÷AÍ¿7,Æ@Ï©÷¿-~Õ|1dv;°Ùõp3Ÿ¸“?'ðöOö‚¬ŽÞÃó°CQS`S¬ÅÀÿbŒžëq=¼µµßÙO™óPþlCF9ø”Ì\¿‡nNßi5³²â»W%ñQ#ÕŠ +M]<&¡(ã®[gÌ݈LPzZ7ýoØöuŸ’zýŠ‚°ªxIŒà™øç ±°ˆ½é ìpOŽ„èáVé`¾Õ³¡VÊùbÖ ð0iö &†=dÇ&Ƀ³7ÿ Gö9‰Ž =ÊøM oâ›Ú«ü±¶5dv›ìöÕÝ’¢ˆL-´c•-%áSœÛ&dkŒ\¯D\Ó†ÈW÷aÝîÓ|*£p-Bíϛ¯±K Å 4êTŸ{…‹U­M`…í³e.æ)òþë2Õã¦n1y’d¼œV_n/&²7d3ú4|:ç‘®5®Ä͉$Y65âªZzÄ„kÎXxãNìþ쉥IªM³ÀLTd`"d%ûs»!ëƒ**â_Ôùñ£g%óõ.å·1¯mW°Pœ¥\@úøÅ˜RR8¹‡!åï2`…òÙ¦O’¸ Í/ø…¶5·‚ÛÅ™îÖÔVí˜{ úLƒ¹g®Él“ø¥äí>k½a£Á1ÂÑåg‘bej^£ExW|ÎÑŒÕî ³÷ݲÁäqÁq9aÓɶd©¸Š ‡f–$ ûíw°Íé¤îkzss¯¨,>²Ýoÿïÿ’le2](—@ñ™n:!BGÌ(JTZÆÃÓbƒ3¬ý­RÒ4Œ¨8³dBÖ›)k€æºæ2ãðXÛCäq”L¬©fÝÌ}¸Ë1¿¬G0†êëhIÝë– îM²­ÇºÈlªªÀŒ†-áøqÁ#÷W:þi3­ó0õÒµÜr')håË|ºÔ\ØD›×­œµî€©LA­8žg˜ÕS§A¦)ósÛ’âI›G9_6B’'âtÙz4…=,ÏÓ|­¢ Ñ~ü ."¿ÕH¥Y| ‰Õs¯…H-öÙÁd»Hå]«ý³‹ÕEóáÊÓ"¯¨DˆkJ?”|±QAš¹0&ýïToš¾VpJÄgèVÿ¡h! X\W{í':ËkC âK9ç;m«ý¡’öÆ4aQ´¬HƒWo½?ⲯšßs aÔlà:)‰v¬öUÿILx7û…±¹4mtÇV¡«ÎÍGf1Pë¬e‰ÈIAóeW[ì žgïÀ ½+…,KϤ+ J¹ß™ãÞV#èªÖ—¾%¼¢ºž† œ—Ž€Ý¤ˆf?¯‹!P×h8¬ó˜‡<Ù´lÙ<›ê-Wl«Ý‘¿<ø6›ÔÃéßàap‡ë= d€a V±QÁ_‰[lšÆ?ÚÉAñmv ÎÔÑ~„KÆ¢XsÄÿƒñµ¥[Óëöåešú#p a1¹›š’¯1æ€N]ˆRJTedg¢zkÓÀý¶P¼wáÖ‡Òé±w¥mA@ ãŒš´iö©Y}/Ùq…ÈbkìƒñdÁ6ݹO¸n 4,HÙsÕÑÜû›$QÛD¼cÅå _gð]_hiP_К&³%¸@Èþ—áÊõB†e»ì¾Çhš­E|`_¥±D)™gdŽ¡oYË(¼Ñ¢ÀwÓÅÇ&{ïáôc7ý#KÈ+ƒ kWË÷Au®ðÓ­œ0§Ÿ2‡ùÿg?C¥¶ Ÿ!ëì.=y»ÉJÐ[Wb4â8ß“QýHöiz¤SÁ,I¢mϪ \÷i«¹9”Ã2xý"J§o> P†•Ìzü ö”šCd,‘¬Î ,füµ8>ÑõÐ|1mØÎ8¤îä)Õz˜©íp5{¾­ûÐøäÓâÌúÌ :÷µK¥¿¹Û0T£ŸÖ‘=ØÙúD3¶%i¼5cýŽ)óêÙ ØzöäKKÒÝÂT'×Vó*¿yÍÏ37a&À¹'Fè&§‹W–] #}äVèK2NØ]õë eœ#¥BÕ?#Ž2ÀÊ]ýÛBïEþd-üƈyªH¡ÔÅ·dX:™Ö¡ÿÛ}õiçË#ññç“åaˆ-ç4Úw—±-W'kmøYÑé"×òj8•åç‹Ê­TnÆLê–‰+ï‡ñ³%#vnÖÀûœˆu¸ œø'µˆîÚËKºïã`1‰Ö;¥O°2Ϥ.R4ÍÑÚ |ÂjГå sÁØ<”sÝö%ïx^…®ÎÕ–²Áê‘€º¥L Œ£ì9c^ØÌÅAáÇ÷ÃCËþ¢‹ ¥àn>x㬣ӹsubó$5l鱤úŸOj—ÞÒuu»× ÓS©›Ô*ÌkžâfûE” •÷»^DY¤`+Ý.¾2£6@}°n…ì:ù€’1Hf2d¾6ANòünþý­7—•Þ\n¡ƒ©¶Ð¯c,(ÂêÇZP¶ªaC¼ÖoÛÞdËë Pʯ|õ9•Lç>U¶0Ù¶ñ"Η‚¡]°{òÇ’ ôGt‹?áJL^o“ye^±C{ò†ä}Œ‰˜ÊA¥µøÐeÏ]÷„nÜî…â.–„Sœ¢D¾³ÞöGÔy¤Ô „È­¾¤1¤»TkÀþ ûSÊta[à|,‡­g ¡š´ lºÉ»ðb„šbà9‚°ç)ì…H}/",ß…–ËO‡ö±&?© O" eú…RÚ3.÷™¥²šçKS`{ T âÌ­§17*×ónð“lÛ',ŒÈö♓__ÿWÙ.xú޲ÐMCpùíCÀÏîḆE¦õjÄøúݳ@ã ;>P¹WNëUçˆL<'´Ž&>l˜žÆ£H””‡¾OJˆèXYï@”ÀÛm'LÀUª”dx·‰sàdŒuú Ft FÏØ¯¼û³ÌQ­­I;OoP±SýHa²ÇBõÎÄî{TÍßÓû0Tßôo ˵¯,]î}öxd·{Èè.³þf]ÜÄŽÈ+ÐÞZ½ ©3cª-òDM®;æT£¯d¢g¸ÑFß±a|ùÊÈ?ïyaW?ëæ×Ïà–V´Èz»â1xú_*kE™ñLï"ç›å¹ëÐÂð^üH¬).jJ÷nÑL´êoÜðs?L%žõ-r¼É£äÂß'˜?jÿ¬¯WïV¥I/tÊߚ؅[ªë•± LcÉ-H+³#Ý)#fcljÛœÉË ¢™íñÏ&ša®ÄRÛ=Ô W®¼²ŒäwH9u|S¾Ò‡êÚµ¨³$®éîøŒÁŠ˜èÙ†H°zbP‹êúLgÉ)Þ²Ìîï=ì?{‘oÁk0mµ9‘v¦èxqߤ‘„/x>(u…ƒþ$u’.V×n nª­¹e¾ƒ’cmò˜8Ï2ý:üTY]Ë*m§XônÌÚÛÎòÛ‹§ ”¨ÐþŸ~«à&*ô•‰žï˜i@¨AN^ã8+¾ÑEM h±ÇÛòhëÑ‚‚ÅØÇg 1ºœ—%!V˜D+«ž»uõí&Sײ¹õ%NN2¾¹Õ·W’à‘ÎÍŠÁ—2ƒBÛ¯>Ê¢ÿy¹“öµÒ©ê<{¼âhö¦é"dNJŒ…9öÀ½}®‘՛ɩm“K*[mû{ÐÅ‘ìÒ!£®¢K“i80ÀÇúþ®OÀ‡¹x€dð;øÆ;UŒ»M/ÕCk ¼ßÖ<7&gqz[ÞÔo¼2‘2¢áå0R=hv@üÇæ|”eöÁ+å&xOíŽkU:_hº&–[>ì™ÿ`ˆN_êq‹€¹“ãçë׉#UÔ¸%šñt¸ o$3¼ày,§>ô\q5à ,U£¯œ:ÚŒs•_~áÖ!µÄu ¶¦œ·–÷Ñí5ß!‰KPZW³ÚÿöÌ kÇ3×G`E ÓÆ¥8J@`7 Ô ´1Å 3@)€ÈïÂ/@ŽÉxÀâ ê¾2¾pF6auŽÒânªÄËClT_¡4пðM­lTÄ4o+4n@å.×0黚iݳ¼ßìë©!hœÝêÁÝÐÍ|ÝbSç‘‘ óB_zÔ­]‘EöS˜ ‘¸,àÞŠµˆö‘sTMž ޡė¬FpÅu&ªƒíJ`hfÏ+­ —Ù¿´0ÏËã–·`ÂTôÕbñ¹Ó5ÛèCÞuÆŒY"ËGcñšØŠgr´ÙMݰô‚çéÍÈñúÀ[ÆÞ=]χ'Cã,EtŠž …~Ъ99•/'ðD­6w³jœ‘ÍBþÝêª3]†fó×HúNÓ\ÚÎ&¡-‰ÌÌ Ü m^ˆ+%À‰ãTšé)ƒ»¢hI”„‘àô8?w–•É©iDÁE“‚åB§ížC*…h÷󸵵𛞢Æô>:€%ž&‰ù¼_Z¦#T¹·yL@!æU錷¸“à Ÿú¦@­MÞ e¤ÅÒ„)°×Ì&ŽÙ˜ysS‚(N«Ð¾Qðµ_Ÿ>Î&ƒŽÖíNVÖd0 4󔇺Ð*¯’Ž"ªöóÔ-O­P¥bõs„MÒ!Ȱ왋DÞ®<õ’É=ø7ÈP!wîç¹{m”û&“G^žä—.•¿¤´uõÍ™²»ÝjøðÙ賦™¥Â,ˆ§ nQÌï]1žá& ðg mâápn_1hóò%j×K_ZŠùv+‰Œü (E5…+ŒÙÅ”ly†•KñÓУÊgÜ®Z%ûšvœ9afŸü´ôoΜ+…hË7ÿ|Øgn‚ÖrHøØ‡4:vXÒå)™Ì5s·ŠÍ—O$÷5UÓJÅZ­é©]ž˜¯ÕPmws->ý(¤Ÿ)˜‚8 f·ïÃV\XÜlßs­'%8šzãƒ-<2¹Ñ§›MãšÔ–Áåâ«¿n†Ûl­ëÞÝbE¸:†ˆ–ç1~qôÏëR\lÀÚ×ii±ïž-¦§ˆ.ä™{‘Ätßá¤çïÆ³ËMk[þäTÿÅAì.+†žÎÍцzãI®¯æ5Þž¨-¾_É=HÖq¢åÖÐÆ/ @ å=zkfÈ0óXuLTð"öÿAÛ•†XÉO|ô f±gÛ"‰O“Õ^ÇþUÁ?Ú\jv£»£ÔæíÈ–×¹ §øI8)©ï[%=›]tŸÐÁÇŽð)ÓH}÷Œ)ýÅI$½Œã¬¸'?b‘'ÚÚXEü M¥'+±9ƒL”ˆ²aªñ›ëu–GX~4º¶Ùl¨›èMŽð„õÔÁýæKæÎàÛ;»YöZ‡G³ªj¸£pѹ2âf8Pl¬7˜/ufÐKÉÌ ˜Ñ`]̬v§U#OŠZY=´‰tâŒIòVŽ'¨HK.hf…zkß.ükí=èûþã”D–®ý ñ¿É¼Ç–‰lnû¦… |n ö+ÑDÕš¢:&]Ûã•°dC¼g h1­3ëvZàœœÿ{»·œë‘+Ù·B@DÀÚè6Ÿ.NSPuÈnCøà QHbý5•8L5èðDêÿÎT®.¦¤Yƒj’NKÉ×,N$¿èCÌ!,i}‘úcÖ ,ý)޹PyC‡)ç^“+Qmù Ûœý{eQm#ö×ýï$?9w¶To7duÌÐÕøTØ=e¸ã mÎeBi³Lº–•v„øm“ä9Ni+ŽÚ±<õs”•`Ñ¡ yV6'`£ã!?›·ð›«<—Û«ÄÑh|_ ¼Ê‹¦_]D×鋈ÅaLUC·þL–ŸÏéÍ],ÓL1L¶Þ±“ÕwƒµÒ ø’® f\sfNäˆ8æN3¦Ë7k1a«DlðÕL§þONxâúÀÝ^VÔ%×L0~¼Ý»&[ÌåúÕ¢ÔÊ—õÊ,éåYÎÿ´÷ÍU[ä8赫r5”ú3GI6ËOÝ[½ípy¶½Ï|õKºòÏ*;™ŸËYVåyÍq«&10‚³íiž€Hð»¾á'øÇò ¢±±ZAX) i(‰‚n4öDZ’„¾bþLô9ȹc–椋æ¿åX}w×”n‘c憡æ©7`5˜Eñd’ô;ÄXŸÕv£%°™*™¾×˜YkŸêÅÆÐO#5'½9ÅaëyµET[¯VÞuF²ÆÅ›õ·P¼‡ €×ujÚnИ/O'¡Úú+·?c”@'ŸZ^i½ótˆUÆãh–³y×þ’"µ C¥‚$«úÇ›ò –† î0o›ÏåbÇ­{}È!uröçëC{/UËJ¶Ç›bÉð·yøðjÞ6Uèì¡ö|¯'†(Sû·ÐÜù*®:µ¦åÞd:§ 4‹x‚'öž;Ø ü©uMOŠ÷åqžbü6 0ù¼ ‘F Šm·/{ˉD]ÏÄ,<€K›¥·˜’Å3¢Ÿi´e¿dhG‚äÖØó7n²ôŰÓfÏ ÿ$¾ Ã]Wœc‰Ç”µdj¢mžgV• ˜àßu8Mç‹èrl-§Q·’kC“ D¤ö䕯úB¡º__®æë`óç”;ߎ­ŒË«™ª³#މO–.#6†Ë»“n†iê¢!=.7Z7EK‹Žõ°@[Ÿ ¹@äRŸeê oO?7¯½oAŽ0f£Ð=ÔØw" 3s?”Uн.Ú‘©âCº2~ÍËV97ÿ0VZœ2ýˆOÝ}&N”’TGƒ®ÌmŸ/ÐW1¹ã %#ýX({dÐ{].Ö2ž¶^ÐìVoA×ô” ñî˜!á›À®£û‡Š`Š÷yäÈÄŸ'F”‡$wÞyØÁ9µÑÐ2+ÆøÎ’5p`(øêóRn€¢°!Ç茷øJ <ø· ¢¦õuö³hÃßC-!z †÷¹|OùÜ¿˜y] þŠ‹¢:æ!ÊwÈZ´°Q…~ŽAìeÔsô‡ÔÁeñÊÓé6M-:Už©ç¨Ýš…ù¥{ð)B— \óaÑØô]a…Uà-Có%zˆ±ÌÔ¬¼óŒÄ¡s©­†OܳJ ÔPð!d¬„¦ÈT¡;Ÿóçl6JÝX†iÀY~”éÈð¸âñå àðõ (üˆ:U ]¤aøå;K¤’§ÈT}e—ˆÓFîâø=ð¦¢J¯ªL&[ŽXJL¥°°^žA]n(AäMɇ=fûº)g[€Î…ÀÕTæŽ}†scäáoòÒƒtÏ­V(oUïðV^ˆÜfKþœÛúVÃñ¿<œYdÂcÌB@ƒÂº›kBÕw'Ó‹žâSó”yž°¶›3¶M3^Gø@¬Ž ñ.åRxh§ÂïÞ\E?g’ƒ6ŠŽ}e4<øÞî–ÊöŸºêW¹Ž¯ Ö|XÎ]¦™¤>†Ä`ÒOj0ÔZ&ÿ³ ©¨/¢?ÎL¶>;©”Ïi‰3[¯l½ã¾½Y²ëjVE¢å~ˆ‹¢_Ž’D½SÊj_’À­ÇÖ´éÝÏ—~ƒI!m~Óψ)ZÑqxf $½RKŒ2M9HÈmuês`Qºµ•Sw,¡É²<Ô4LßûΪx©d,é/™ØGfë’YæI17C)‚a „Ñ&lÁk ‰™/ÞŠòøpLBÖŽ?©Å) é‘j7ÿî3p˜oCÉ»)Ûõ« –÷a²¹g™ä˜¾†mÔÝ.ó¢”þÇÚUÎYÜ®]§„­ÿUýÞ~#ÑýÄsSF~t2À2E‰½ÊžT!(F wêTZ ì-,ÅTÐ w¢œÂ~‹v¾KtvJ·=‚Ÿ9ðSà2u;y=lWåèäy*L}CO„ú)Óš" h‘6kö@pÓ@!+_ÿ¡œ?¸ÎÂihÌ+3J$Èÿ$À9³a˜›LÈêJ:zt›ñšã0=7?UlMöºm®P­î(§ÅúJ¹9"øu‰ÄéT¼'Cjç£Øø€ PøL ãG:×ÈaBãD} £ð·ú‰Ž*5 ¨:t.ªmâ3ÊvÙŽ]ê*Ešm±êØÒ “˜F‹Pý£]%ë|®üÎ):û‹âÚ?ëÅöú޳”ħiþC Owöá-«!¡‘#àÒf5CYžkÛ3”Õ‹Ð`ˆyOv¡>œêWÏüÎÄB™×Q*{|RC “Z'Ió隥í+«ö±ÏÙƒ6b2Öçèiïñ+}ƒû*? hFÐ^¿ùÀ‰F·CÿÛRcÝ’d3Ü¬Ãøš©«ŒÐuÑ\ŽáÇÉ7í‚7…qPý÷%ˇņïÿë±aK8Ò“–Xm¤Å ·m È~ Ô Zß_5ÃáÚv¸äèå×l†\ 6È5Æ”Ž–B`ÿxb3{´¢u3 éÆEÅ_Hr—„/¢ž‹nRjнÈG! žL®«CQi3¿ â@û¹rƒÝX¼Qâ_ÞÞ`Fõ7ÕRìñzÒ‹ô„ `ü:îÅîû,üö'£{b–3GÅUa© "Pk:zº½Üà¢v%vÿŠ(*X"®iXÓK%C;‚¨l±²ÇÆ·(Ö:Ü‚ç,«‡‡qÁÓˆGWºök€0¦Ã;‘ÁW™sž´OÁC*dzڔÿÑ!àòÖ”ÍÛÑ€g);èʼÎ6ÿÏšw=ci¨ ”4•#"9¤Ц+ðêÔ4†°1P‘ó"þ,o ^òɱ.×w¾›ƒ:‘ªHlQIÆþ±dGšn<ù[Ž ÝÌ7ô¦Íªky”ÿ‹Ü’0êá‡?¸<µY&¢5ÿ[.eÌ£¬Ä*ÍÒÚ~]žÞC“®âÊ~p¢&Nrs±ŸÁ5eoîõ„{²¹mŠÄNÉí\<làŒÁä™lâ¼£w?ržÉûwÆS®¶dýŸmÀú!! ÈK;–§ôÏY,B<eÁ ¡oÊkK3;_¸Õ+ioχ¢1OrTÍWuÄLÃé9a@ýX¶%÷HèêÜ31ùœÜ+Vÿ‘ õ@ÊPûÉD W;°wP0Ñò¬ÕÁ®&Ô53 á Ë%O¯¼ t¨-–Õ275EéL4C£Î~£ü;àsþ.ëà,5‚©úîb7@Æfñ ¡•µ ¼á$‹òÞYÌ)kØÖšãRŠþ»ö¬pGjöiн¨¨|Z(n}±¤ãÄ‚.Tb|Qƒz«e—ìÌo(G|fsåÏ6=¢€Sû·å¸ƒä÷XJÎG0Š8¦ª%:®åiè‡w¸Ló:ºçÄ‘’³R“ÀäÁÈ]}ã~øA˜ö›¤Äi¯HåÝ^Sï®GÀ>˜U¡ÂT;î^GQü° #kqü ÷Ë!!b'‘hB3˜äƒui%§[¯£Ù2Ðö·»˜Q½Ó=waaöëÃbò?Œ7M„îé^ý W®,BÝsýxé¾i}+ »¨7Ð#¸»”ö¿‹‰îÈ‘K‚">÷MéIÕãàRY§ÿ#îtuÎ&–Ç'¶fX—¤AÑàÛ“½j-‡ìÝLâúå+ÿ{bû+†;ÕÛ:›9:¬~Ö•™±Ö6ÔG袺ñvœÁØ~Ó¼TÓB/;É®šVš‘¼I™‚²ÎÒÖ„çÌycäÈtûo#n _!LED±%ºUC0MO÷³ú/¼0Äè0üÙ«VäóǸarÆÿ.{ž¡Œc¹‚ºŒoã‘ W3õ˜(Ù8&]ˆØ«stä…é‚q,å܈c5 }ôgÒ»(ÕúÏò¨§`ñ|ûbLH»÷q;ÄŠY ðDnå(û‚|œgõFñâþ!áÙ€ƒ‰z®÷ºë®Î©õÇXð† K;mÄD?˜-ÔWc4—ÚËÕ²£@¯%`—ÍDû6݌ߣ¬††DK€ßv†F›ö·½¬vª^§6à³Â¾v&éÁxúï¢SÒ]•/œ-#l#N\WNñ› *Š´¥@ŒY­g|HÖÈ´× ÔeñO¦¯%»,Â… Ÿ^»ÏÎÿ´£>K81?€p N-WâÁ“ò í{l¸e#””2V’kå ‡Èà(1 ¡gx«j ÷{s (˜\C1aÑä½eŽžm…~±ƒ=Ò—;>}DO"ì‰2€€T[·¯ÝJâœO Þ‘ " ˜>yé á.=h–0ˆÒ¹ÊÖÜ*"Ø5Ø€Arº_¤Ó­øü€á¸€D=¬‹¬ÕeϪ-ÁÊNA€De;q4ð~Ò“ÇÊ!6ÇQòcüyfýŽeÉOà{;OM‰Æw-=rýØ‹ž+©µÊÛJ嵄ÃK¾²ÐE3åå³}×ÀèRÏä•0óР9K6’k“”º[y7E2NQN/ièy”7û”£tÙ‹‰­“añ€ <).·^3“§¹Xûñü2zŠXâ¨Í0 iƒÎ¥ÎñhÊïuÒ䥠kWlÃâÀøNÃb~§ñíà*µ¤ÕùL˜p®šæRIm¦‹Gøós-ßè ã|ÿÛ˜>@j™tùÖf]õ±&TÌìáÛ¥NЊ½ûì-‚#àu`s iº(œïÅ6‰zBh°Œ¶âT ©«Bñ¿îØ€ÐÞõ‰Æ€÷‡ ¥…š»P^ÔYY|Ò»Nü¦…a›«ó*D¾lœêYß)N9ŒkX7˜Ÿ¬µ´4!‡«f Š ÿÇV¿¢¿@`8$…µã#»–ý"Å¥¢GŒFñÕ«ø­v³ê´œjcJ(ëbã ¾)#b+½ÃÞÝ;¾B-]„í¢Ö冷ϖDÆ…#LšàŽ.åÇç¾z†£ÄÍkÒß®ŽR­CR_Y†´©¥ÉßȆ޵Óá¿ó*š•4Ç¿þ­é’›VXÿ¾ëöoz=gZõZ«n¾‰$#Bûº'„Ç€¼Û²úÛè\Ù/ô'éfïüŠÑfÛï?7#˜Ì£BNùîªB¢šõ5½PB‰"•záƒM´° å÷¸C‹éÆ\¹û ‰?yó›ûçÆw öLiˆl¹Í§Œ[C€LŸÌçÀÈ®¶Àדè7=~¸øéXÿ´$âGá|³©fwýì”\èã÷“Y3cñÉÊVõÇ GPö…°ÜA'×R³= =1î’£}«ÛDLé8½Þx?bð2ÓÁŽ„Þú^$G (ÍZÒ¤rHºª*mÛ#ßêŒ)4Õ÷F÷,+÷ÒÅÑÉÐó¨_*ßøÖVD¸¨?ANCÒù¬³­u¡V¦‡BŒE‹S¢$ n¡$ý¥X½¥AÔ«´;1§¢½’J@•ÊÏia΂æ|ð–£:ŸúÙb_òÝÐR”öýõ›Ïï„…¯é'Và 1ï*Á°é˜sŽ9˜…[Ê¥ ’eZ( á[vr7?™LTÔìÑùF¨×¶v!‡­7ÔŒc^432˜‚Èœu³Ù?5yD³ ï-fþþë`Ò¤˜rT¦ƒÁ?ÍYìð1¤\n^¸ïQ¢FXŸgµüï )pÕ„ERö úBοÌÀ»°( "¢å8üŒCGÓšú¹à:픑£™4-Z­âr‘s9x¿G ¹|E?õLªs÷›B3,HH.ô“Êaпµ>üœÕrÖoXïlÄÊ>b΢‹nÚ¤¼~•8Xõèå°²+tM…”%|‡MP³Ä†< {»‘e&§Ëš‚çQ(P8O+Güƒ¤(»+Iåγ„lKÜÛàwKy ¶".ú|·. ø³&ø¶É_zjÏ„2©‰©Ð´“uЬ”„;‚É ”fˆwœ1 °¥Ê¢ŠnéY7ÅÄ‚I;Ó¿#Ô‡q>ÄRjhÐ<ˆ¡-&÷ÑVøQÁ£Ö$.©vc=¸|§æÿXé¹íõæ˜4tåâ‡éò>iG܇©÷ ë¡ßª¡Ö˜ùb¦2ÑO5>Íæ>ÂZNE圷bMl<ì±ýn7éQ41à¡Ö¶3Ï*б'ŠÍ‡Åu¨¥-ÔõŠ»¡l_ëáœQ|d[¿å¶ÁÃÔ!W}7vÀ·Z6¹|$Æ$d•j†QØSV´j8´Kx ÅO2¾Uâ„Ç,|UÒ f%0C×n± 8¬ “Ñ9^.eߦeê×U Ôm“Ð<_ ½ï òt-s°5×Û{Xâ˜XlÛoSMYÑ)ƒ–y%?eÓdV‘wJÈèþ®f€ž4Æ)fo Ì™qWÅÈ…ãa“ÍÚBñ}ìâœBÃÑžÐcÄHÍND ŠPÝI3޽VÝi?È>ª˜­U¦““‰çò¬¨Ú6%'ðµ7åÎîfòž}×þ#¯|à±&¿4ÁLþbøì œÍJÈhxÉÔ¡ÆÍ]‘³®µ3c˜Î‰>aèÔÙ£C“ X'!ƒwEÒ"ݵżo˜¿ÕÞnÚcº‹tO„ÅK,õK>±—lcí]é?ÅX­+†)L’Ü QÄ0ÃP£\"¹¢!LšËe+ãÉ‘{Ü [ŽuE®qTkÈÉ1‰©Ñ±ˆÑö?ôe?ìûþÞ‡÷íý~ï÷^sË!ÌFk• N8&¼~þVû9ÿĸnÀ˜7ÏPÕ:|‰Åtœ×ÌÓÑô¼Œ¿;Q 4ØòÝ4=aO¨±n·è¬¤„CÐñÞ–e£®?Û1;½2 oÕMðf3ceqEfŸ A¡BF‘iÒûö!ÂYê£\¾XëlúѨ]÷©~H|]æx¹ [Û´†±àšâ6c ½Óçc[ObÑa®Ÿ1\=ÓMHï:Ì ×ƒ­Úñ¼é‹XxÒŒ²¿nQ©¥íñsEž£^Âé–øùÆ&ä3ê*4Í+ƒÕ‹-˜Àw­¡&˜œXsåÅñA~%ÿá)ùU³»(#ºš,LîG2ÄÊÞâ³@¾%oà›[3v AòòŒFnéWåV*ª” A‘gXí‰ÏŽú&ÅüÚ9]Þ¢ÿȈ­ŠŸ'«¼ÿóCQ5ˆ‘–xhdÄz80t²Á “ì)©f8>ýšøx+k|jrñh"øÍô4bA’ ÒH/l5nûSX']‡ ÿ€ð|‘ù캪÷¢N!YŸöölÄ7Æã**ðÃõ(hˆëû‰Å…C¾`Ëcùm5$ñ€Ú’/Xæ³,@pkî19Âiy®~Lë$¾qPËŸž3ê~B}°UtA„Qz Ch¢¦÷>‡Îs˜êq‹€î8“´ÉDq㥠8m.iëïÒ–3.ä÷¸^y ”³<$>]‘õ¦¿§ÙDIºÐ ºœ+*·’La-Õ—wœXiȆ;‡{Õ£_»¯tåx™rϹ¼Ò×+’±Y©Îˆð¶/Ò”1¾˜sÀÂÝvY³v‘Åå¼ ‘œûôRß©(ÑOAƒg$nTOj5̦«wòiá^,ÙÊA.…ú5à îG«ö¯;“¥KN?/öD#_ÚÔÿý[y2B ËsÏ­Ë"6NWPm•‚|J^ÂÀ£úÎ’RÄ$…gSûŠ+±4=2Û •ç[)ÈI·¶š—ô´¿v˜ß ²/õ*n‹E¸âŽBÞ&L«$‡v5Z&â>„µ”^—°ŠóSŸ©™v£ÄßÇÆu˜1êÿ -›4”’¶&‹Š3¿×NèÖ¹†UæcE¤¦@W/<.zdQ¸–iÚ2\ó¡1î2úùmÏ’ÖÙt 5šõ«úæôeÜÕ©”ÙU¡,°(Õ†­ÝzoÙfëYÖ‡ÞòI­#⯄ïiÍDºƒImoSV¦Ó’j@MÈSdbbV¥}Á fèæÝPIs\'Î/”>ÊV0Ë÷”Î|©q`(¶µÏÅjÂõ»Œ£F'DŽWì6èƒ`›13”Ä-ˆ#MBÈ…9è¯jv¹Ž_Þ}WÀ ùŠ^‘O¿ÎÖˆ?½h¾ön•À¼u+Ö¡ï】É⬕ƒ¦rýéRU­K©v÷T~õÕ.bÑóÍØ~gÚ_Ìï鱪í;6È^ˆ—à pŠwÇüsç´t°ífÚ7“º6†ªÒêéu†kd ›]6ëlTMž ×ö+®‡R5Ï›So @D_jì{Õ¬M‚gz2úü‰Á.»uÛ#Á…}WúN¤XVM8•¼Þ=¾‘Dý#KÉqßâ}©sçøjkÉ_»û¼(S°¬ï '¥\yó«¦Gß´ë¯ Þ9yÛãó $šœžÈ—°, cõO ¡w8ኔܜL›Å”B¼˜ˆB•£ݯ:õžÃ3[՚ƙ6]Æþ–_;æÄì••½èb pàë-Ÿ ½^áæ'Vœ …ŸÚó_bëÉÖHÀÓ|ŸA”]J«ŸŸÅ<º`¸ÃS.ÎôÇxŽ”Gß À¾ˆV”èRþ–s8Žygßp¦V.ùÆ ¿Ñ_äCÿ€þ<ð ¹î‹'†b}ñÁ¢ÿ€Y]Àendstream endobj 28 0 obj<>endobj 29 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 278 333 474 556 556 889 722 238 333 333 389 584 278 333 278 278 556 556 556 556 556 556 556 556 556 556 333 333 584 584 584 611 975 722 722 722 722 667 611 778 722 278 556 722 611 833 722 778 667 778 722 667 611 722 667 944 667 667 611 333 278 333 584 556 333 556 611 556 611 556 333 611 611 278 278 556 278 889 611 611 611 611 389 556 333 611 556 778 556 556 500 389 280 389 584 600 500 500 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 333 556 556 556 556 280 556 333 737 370 556 584 584 737 333 606 584 351 351 333 611 556 278 333 351 365 556 869 869 869 611 722 722 722 722 722 722 1000 722 667 667 667 667 278 278 278 278 722 722 778 778 778 778 778 584 778 722 722 722 722 667 667 611 556 556 556 556 556 556 889 556 556 556 556 556 278 278 278 278 611 611 611 611 611 611 611 584 611 611 611 611 611 556 611 556]endobj 30 0 obj<>stream x¬»w4œ]Ô6®G¨Q‚‰Þ]”½F/!ú`”Æh„è5‰%D]½ˆèD Q¢·ï–§Dž÷[¿õ­õ{ÿ±Ü{Ÿ{ŸëÚgŸkŸs/˜îjhsI[Á- òp’ ÌÍ{¤íéhwñòO¼‚¸LL2ˆ9 ‡Éš#!÷@:¶® ‡–HÅeÉÀ<P[$HWK¤ ·Fº›# œ×ìÀ@a…ç•ÿÿ6J!m! k¨$óPÃPI]Ī ® R€À s†«…Ô¤ µ„À\ l k8®@–p˜ô ´ 70£´ Èäⱄ¯A<,!NW.Náuq~A]@6sbBÂAP˜¥ƒ«ÕÀn ¤â„€#Lî‚t±D@ `V Yù¿p"mÍ‘Ws»@7n Œ´‚[º:B`H –ùUÀ‹4‡Â\@Hˆ`„ƒ, +¨‹“ƒ¹'07Ì ýÃÕ ³ù€„€Ø˜#¬ ..@ öUv~ó¼zõöæNNž¿Þ†ÿõ/(Òâ`Í ææÑb…áò\-¾Ìóþe·ruúÇçAüJëßÁà0·‚Ã>Ñÿøtl¡–ö0`ƒ€„~¹ 0«ÿ”ëU©ÿÂÌó·ÎýŠ®ìH¤Ž§ä¯újp`ûÿýpõ΃pX„˜X”Ø)¢¼À0¯÷ÿe‚_‘ÀEºzY͉€z€Œ–¼à_\¯øþñôø?aä`–p««-Ì'(2G Ì=A¸À„À£ È ˆÄãJÊlA<Ü08`rrEzÿAÜ«íËÏâqq2¶&`ÿËÂâÞp4ì_“ˆÇ½Úã€2þkñÀ\-®v¾ ì·Yèê}@Ù5»Ò‡ ƒx%„ùÛ&â1w¬.æÀü;RÀäjiûK&ÿ1 ð¯ æ±þ@ü·õ¯Îòï`€•9€u±w4GÚþZ çäàêòÛP³„;:šÿ¶´¡°ëcFF(ü7D€Œ‹ƒ¹ËµÈ•'üw€ö;¯‚¤ûo¿ i‹ú×?¨ÔÖpW@Ûÿ^ A­5öß­ P"ÿ°º@Ü ¿³”Ï_ûéß1T°#~¿ µ„;Ýìß!W ‡8Bÿ´ €¯ÚȿÄÄg×kk* ¶¹êûß …Ðή—«ÞùûÕ_i†Ù \¯/¿_ÚÁÉöwò…ø Èk½Œ-ôw ¼,Äáú¼œ“ ô:!aºÆµ·„ä æ×WY@.w-Š0€Z ~Ý Z'ÒüïÜÂ\ iþF' ÀU5w´°ºf«¹þ~À«~íûH4âZn­àÑøÍPÀªs5ñ¿1D¬Z¶¿+GÀª µ¹V³"XóßÓˆHuÿ“+°Q¯°>tÎ ¿§à\ÃÀÕpùý ˆÏ£ëÐD°sK{ò}) @²‡€§­ß•â?£ÿܯ¢~`‡9B µ¼jÿ˜¸^‰º‹å‘2s`°¹ pÿÔ1p|ä1ÿ£žD>Y^« 0/@Éê‚ä¨óÿdÌ 0rúãE€ŒÍ5æh\Ÿ Ì  ‡^¯*0/ó»¨À¼hû?ª Ì  ô÷zY¥ß+ Î`€úþK @ÃÿSY`àÇãô{ùÀ`󯢾ö€q­¸À`ó¯:¹6¼V^`0€Ùõ¿¨áWÕt@ýËö;Üã&à†À„º6ýä4òý]7?j Ì@·¸V+`>úU5Z}uÏø§,À|¿J8‡\|¥fï‘k€¯DOÎõšŒƒ¯4îª ×1øJ㮤ñO5 €¬ùÿ>ð•ÊAaÖPéùÓ•ÒY;ÀSü5VàFaqÍäÔ jîÜS®¤ÚBÌ×^ ÐÆÃÈ¿¤¯48ÀÝ-à× øJø~™ÿÌå•þý2g‚ß!>¿ŒÿÉç•þ²[ÁݯÁ¿RC+Ю£(]5Ü?;*Xà幺–\›  õw3ùO^¯ÒÑÕ î+¿Ç_ %pÛr‚#®ò}­+¯8+\?¬ Ö¿Ž<¿Ž6à+å´¬k©»ÒN+¨ôzî®ô8.ýÀ0AÝÑ];1¯tØÁ¸ÇßhB @}ÿn£`€è¯ô*àz÷›Ñ•¼þrØuñäÀÕpAÍmQEº"®¥ýJkDN¿Ï"à+¡U*Ñy-<ÀTëZ®î( °H„ùµnèZB–ÿ™û+¥ýË÷Çi |¥·G'¤§Ëõ‹„¯n«`ÉÿØ|Wê œ,¯uC¾+õ½ZV`´ëÕkø®Dè)€æÿ_|g`Í\\-þ| ýO¸ÿxâÿûçj¡Ú×Ï*|W:}UÿÃP6ÿu;ùgßð]i5ðÀ úG€+Å.çW'c€ú».ù®ÄÛòŸï#ÿõœ•±‚8š#ìÿë8ä¬\Kú¿³ƒÆ÷ÆkF€¬ùÇa˜ ðt€Û\uQ€ÙµÁÕ¿í×Ïå|W ÿ·ýê Äß§T¾+‘ÿK,þ¾+­ÿÇó‡Þð]©þ?žk’ÃÇwUÌWªeeáð§êð]u\ßU#p€?Àl~+ßU3øµ.ÿ™àüï*˜Xþ¥ÁwUÓÿ.Ÿ.€øï5øÓwqŽ–6?Àüßë ò·¦òñ_•÷?÷˜k[œïê&ö¯ÃâÚj𼯱þ¿æú#@ÿšë€@ ~µÉ«ÌüÈÀ¿Gèomæãð¯çXý_Žës_ÝÊ~¥þ?+(P¼Ò`oü¾ò]ÝÉþ±^G#ûÇþGt€Ù?öëP€Ëü_ ü5ë‘^¿RûËóG,€×oÏÑþ&œlÿG¼««Üé|×#^]ë®û®Ç¼ºàýJÖÿŒø÷ÿurù#õ‚¯ñ_®¿ÃýùqMn\J² A^>0?ÿ_ß;,]Àmùëp®ÆýûùׇRÄb‰»‰3a–Õ¹CyüšDßjd$¸i—)×é¨ÓSgò’ãp|:`ÇZ%š‚¬@̤G93´íb¬ítˆVLå…l²·Q^rp̘E-2«O[Ks=¼Û¡ÜNßpöTí¸uoÙC 7"ž|²9<ššÿáßÂ},î˜L >ÍT [?˜lÅÒ=E“—-FËntA`ø¸Û‡'ô“ßç‰#­Ë”¯O¨© # $4[(FÒ˜Ža$˱n·‹}Ÿ8Â÷Ö+™x¸É•ºLrYi¾ ~Fß“"¨ÅAŽS‰ŠPS³B!Oީ߰S›Ã=ó¸sÌK\NäMŸª+3Õ’qÙéÊà6Ó¯›CÿÀ`õh‡d÷m¯Ddá*CÄ.¥·ór"3¬%ÄTpûøT'eA\—…1Â:‹ö®Õé“R{˜T÷K‘ù~ÁVªÅF¡!Þ#ÙÝ óÂòj¥ÖUho?JJW?Û>}}Ï*z@™4»•ÖK)ŽŒøvïÑó:,”žÆ~ÍŠê +bä½!eéw·°Ÿ ‹‘Š›îâÜÁª‡îF¶ˆ}Ù%NE¥bGÞ@ï¦WÜÔú]ÑR5‰¬ë2Í‘õ.<ÐJ¼q{gþr7s»*(‚OjÇÛ$?ói›kd+Å v·!»æ^ït›Å«›Î÷>’Óx¦ƒõv§´xž2áÕ^l§žöéf‰·‘ XำØomŸöêÏý>Ù y6¶ê'ôŸ„1߃$ñfä.RäÂnØqYy‹¬²\–è4<н»UAžíºÞ¨¾-•,Þ¬á>G‡ä”ß”éAĪò-…yX0ZŠZ?ëiµ”rÐRNs‹Ë e–9 :nl~³qc3òbÝæs^mÉd£(ú=½@ˆpÜõCi¶“çìê¡€øøÊ…×ûÓO‹,Jݤ‚½A‡ºÆ~3—iïÑYbL£÷ƒ8øû~‚•ÓÝÖg»C¡ˆÒêêX*»pѨ½úàQaÂÙÕ8tʽm‡ò‚É…•‚mÕ\›ÍêA$œž-ÚØZ,*fñÃÕ¸‹QEn«[ì¿{Ü%ðX×Áf›Ð‚‹oÚú‚ân ôK°ì;ÊÂV̳<*‚å8ËM1.ub¢É/‘ 0O|¸^˧pÊyW&Xø‘Q”Ö”÷»¶‡ËÜú%Š3Éîä$Éö~åf±ßªØšÔÀã\£ô-™õ¤}pŸk®Ñ,O[ËÀ›`1-äÏà<î:þRöŸˆÏª*ÔRXËíôapý—ŠL© ô÷Uë§Qì JÛÏ××—iÕ¼° »J£¢_O¦Ò£òåê6ݲF9c™û0íÒåzC~—æéŽœïÞôEˆC¿RžÔÎ8fcqµ¿š.™ÇÆýQÓóZëFÂ|–z¦oà,äÊø”‰jç{Â{î.TYoïQøh}Ý]{Š&´&mKµ¼ £« Çꙺý2oM(¥|œÝûÉø|ÁZo:LÈZü÷m\ŸAä=6”/,@±÷uÔ#Þ˜?wg,é@~Cñ÷8ëHçœxm—U°i|ûxP°6#:y´âz¸<ñ¡\Þ¶Š p“Lð!ã±ë,Ð+ŽÅÚÑtwá.òbGN bA”IP4 ·ß|iœ›rJþt¹õùnøûŸ”Û {3»«ñ5˜ÈVQ”._£½"näß&/€î5bºî[µJVµñþÔ-³¦u…¶&ù5”úŸh 0Ä"ŽÇSyŒ–âØ˜tß="hØ#È¥žÎ¦è7pmäÄ@YÚívÒ{¶:GÚÒL‘ëp˜rë‚íæØJ¦1£\:t¹½`à»f¨O»PÛ Š70GÓuÌxB‰ 'Žo6FN™¤H)¡Ê¹†1›žÍ}Ls_±\#ª³f0œ}=íæbóe¾*hk¦g~»»·]¥­_~™Ô™É~Dª\¤5²w|„å‘A±bƒýN|#6·2õ—©E¥S~ªµuN3Ð+¤DYnSÝðÏ]¹Ëé³¢º˜,,¢QágŠÝ?¯~axצ¤Œ¦®ö{*6œæ2}¯Cl†«›—.åчcè̤¯í»ì£ôÎŒ¡£?Ûò ýcî.óu˜©b¦êi¥ð¿Õ ãÊuÞ£z3ù¥ÊPËY‹‰ìÝ%â÷Îl¸¥ƒË¥em ²7D×Í;o)9~ÎÞžqsM¬»¯O¯¬¯ô)“:zÁýÌ@1º:Ù´“õU«Fç.SÝ=jC•Yþ2ÅÇ÷Q¶ŒÍX”/›tJQi"äMõêR1.j—¶ÜR¹ñÕ¤«_:5ÝÐÇTm¶©®t d‘>‹¥ÄñÝ£Û©ùDB¥1vd§§¡í´X~g –Wª±xOD͸(^&«¨1&;OÊÐé1hëìé“{Zïp–M:Û†LW¬Â ‘ ^‘àF·HÝ„Ü!á½Æ]~Öð§]о§…6ÐŒ ×©—Z³x½¶oÙ+œ´]}ƒ6Þ.BøÐOn€Mßþ˜IBÁzO|ç€XOMr"ð»[{Z¶k³ÙÚ|icGk¬ÒɉÞ{ç/@ ¬UÄiÇ Íšíñ‰;«ó’ëÑ•H‡Oa‹·¡T¬Ã–î¤Ë?Q7‹·«ÙƒòŸë¹¤‰(±Ù¤í ó{dËÈâkIˆíÞOþúub·À']ØývŸjifo¬ÔⲎ§!»VäˆYõ‘—¯.×7QƒÂCÏW1G½4<`;þ¨béFnD‚"SÁ”óIQ{ÁSéyùËaªÎúRJ_3õ×öF?É£ºmz’meĘð¹'Hl–eª£Nà2èEì€ï•¶þ¶n˜ÝÀ‹Ãs‡¤ºQÊÓÒ÷E,ÂñÆE#YS†1j(g¶ŽæÙ§M?Že¦åêr£¢dD‡xÆþ³Ë¸xBmmõ¶tñþìB~ äê^¨qr1$A|øÒBY#1%vBu¤Ï;›š¼a‰Óß½¥Ï¥¹ûÀ6ŽÖºH—aTpêÛ3çA½Þèù™Û)Z²}š4RuïÀ4¯ìî‚rÚ`µ¢ltf¢ª1´¼™¤EèËaèk<^~5¬hQÌBÃ^Xr‹ˆmj©Õm¶ªæpÜ)‚ŽNËã‡õû¨á­5Xy¸G„AJŸQ5vi›ƒpTùz¢ï¸/­¡´â…Ý~t{o”·%Ý¢—bÕŽþLÄmešùñæ£ì_·ßS(Ñb‰Ù…ôÿœë½“©ß±xÚoßA1$šÌ+9Ïn®Û½‡±ÿBÃ¶Ž¸^¾õ[ºJ7å#º•)©×8û9¥·Ãܳ=y -¡ûè2ž¹ÖÍÝBIBGñÔ«]& ÃKÞ›{θì³~v½§_h䋃Ø :ù¦›ŸÀÞ Õ÷äw^òeyxMÅÊé1iÑs6ý^ž kê!²ikë´àÄ|ÍAl›Q|DmxOg+¶§Þ²P\zÇLr4á§|DÓ'Ä–@øÖCÝAm©’7¤¶]T«âSYÑÞ$]P>ÌVbaÜwe÷R÷FŒ8¯¹V…»ÐÜ¢+áÖKIȌ֙å»KŒŽ¹üÆeI²cy…öu©ïmìöS_ßiñÝŒÜ%¶:c?]AbW{X/÷ÛΘ†ÜF £n »Ø'虺UƒÊähBïÐRÉ`y‹|É ý’/á¼ñÆ·zÿ5Ç„øjÙ'âò„½Ï°›ªÛ¼ºÖ½]¢ïxå{–*=ãŽ*Ƕ´ýZï“ÀP«=•¸%ðƒ<ÌúLÞÁwNLåØV×àF{ȵ­ }Q‰«½sËó– öM×9°Êt~‚Tˆï`Ù°bµdÔ;¼ÏP _G.žtL^•Éb¸nŒb¥öƒïϲd£¶£QyVÆ ÙÞ߃Z”nü8ØòSq(‡ö ‹&‰IŠùL¶òt®ø¥}ï.?E ~Ç?ˆ7àñ‰^‰¿n„ê=Mh÷˜™üg¯ ŒÙ‚wÑ 7ä²O˜ú;úd¦ñ ôkœ-Ü#öë‹QKåÉ¿K:‡·°¢Î´–²0…•0ãQåÕ«Pì)|÷[|„c¶‹­¶qzz=‰Ú´v£1—XÅ‘åçÍUh‚\6ÕLÊrc—ûDkg}¼{¹ëÔîÿí§…—¹ol)ãýŒ ÏÉîþ´…Ô'Ä‘cåW»—ôT:ꪠQÓ/S‰õðøN¿|×"¶9­?y€_B-¨¨UêewHˆ®uYßy'1ÎØáÕÿ¢w‚_‰vù´ÇÕBãB#.¯FΗ°^¾é'—ZDqß3„‡Ât‡š,ã]XU>ÞyCP*4:¨Ë¢nq³Ëµ}ÎZ† ù0·£Bª=pqVhxAèö1dÈÑ´„>FÉÍ“|kyÄG… ÷ž°àYÃ`̳ŕxYAÛŽjÚÏ?ñ™ÍñÖµž=Há¨Âl6ù:lÓI«Ø4볺|GåÀ#f Âį§æóh÷s îkϯYþ[ÂQìâ>³HáÊÑ*þ˱’¹G^4ymøè=±^ÝüŸýiy’=-jV\ÝØTºŸ}Ôô)زù ’À`XÕÛ¹©läDÏeÍÁÆ-¼Éd‰GY0QQ÷Q²Ûp]æRíLÎ$ÛÈt9gñ¬Ó'ƒÝ“²,óý̧GçÚŒàIÚ8ߘK¼-7sšeT°êM’ze¶@i^ö-¨6h¹#qÜSV2ï^Gð¸1ôí~Jjízͤ BâM(©ö,²Ö ç+º\N¦Ý°¹½¾¶ðíÜI1Ê“Ül°ƒÁMÚ~ ÿøÜs¢ˆ´cÑ»·1ÔÉœ4?ÿ¸¿œ|¹«ûÔYd´ƒ×–0n¦:¸ß÷ó0)7yMò½Æó,ضf¥•øCmÉ º°æÈCDÊÈÉ£?“µßŸLôIâÑO†´Œß˜æL×Ä:º§EëªP°Kiöæ×zÌeãÞlüa×Ú}/]ƒÉ¦À .¡ìI£‹‰¥HAb?þfJ ~`ÅkÊÉJ›Ý5&›‹à2ìĬª çQ1Ö÷-s†ÍhMh:V<Œ?†h» Y)§ vŠTîL–¬q6-Á(!µÏÊ"Ædf± ~tw¨~\›¼Û’…+4D9á9>¥Áäv=4|úXSÜm+}†M³?Èéþ¿σÑçqÃþ±fè‘w׿N6¶ºèÖh›èî*ç ÕÜÇsáÜE¢¦_l &15•Ö¬2|¤¬Ç_h«ïZAâí ,>yâN8ò>‘»?©+C¢=šâBGÏyYÕ¹fÿ›ã GJÔBû£UdO¦"Y†\ným›Êë(z4òfß绞¸ýÃióÓÄÒ}!b(f§É‘H°ØZ‹ó Éz‚TQ» Õîð;µÛ‰e˃*6;÷xcTzt_år‘êm¤ÓGë­ÇÌÉðÍzÄ–‰ëS¯p.!ßÚê„,–o(&;.î/žë.öÙzæLla>\ˤsûÆÀ#Vû/m9()凓µœš¢c÷D+}óúB“ÃfM]„ˆ^{>k­ÏüÜñÈ ™¤0°câF…®Õ^€š}ýC¿g5q6ïsŸï,ZÛ‰õ鋜ð@h‹ûdœÛðÓ4˜áÄ+êûËŧ1瘋<Óš^xô9BZ**-H«ºC™ÀA¬ìø¦Cù€“Òá|)ùÄ}ôy˜ƒ¤R-6–87D¹cã«‚>¨¦?(%ñmõq¹¬ˆ_²‘Û>åÕŽ)~ çY©§­¾9óFÚØeÔ$më]-.ø»‡­õ®tôËC<i>Šœ6.¹âh¾ç6PÙÉ~,"fñ¥] ÔAò3Ö#šV(•Y«¹P)ÕË~ø×׌,FÙ8&ÛÑF2ŒSÞáTx”0&£qœÈúû¯ G“ÈR ô•Ü3ã|h¼VÁݯ¤f´¶Ê>Ùi+¤•øö›é ØÛ=/ôø¦E±%v¢u|î¤5U^~$ò´€RÔÔÊ=Ýjä¸"ëBT¡ê-jUxKHÇP£Ü—fFfÆi¿¢L0ÿÌX©ãz“ÇüÁã.qý­XjŸ[©e8˜ Þ ÛSâO;|Ù–~lÀo§“ªn ›@¿¥‹uÔ)*áø¶éç½…£Žl‰-9e=[¤E$KѤÁûUÖ "þcE13c( ø`a“ùÆJ)JB¢níÖ.Bk’4ýïùŸ/’q3t˜]šæ9+8´~Ú{w™4™ã÷FðÜ¢m:Žê£À±”M%GK;tpž|^`™¡É°§ËÕõ“·+¸ŸF2iôh‰ì|ÔšY„‹ÖŠL½èÈÂÀWO6´¥ÈŸÁ¼úðCõ®xç}óéóÄe Ü™î‚ 8a†dg[ʰ=ÔvÔ±äûõÞYe' ˆ`ùÞ¶ï_f-ŠË/)µ&&w0ØÛeÃÜ÷ÌÊÕ2Vü÷vP_– U¹½Ô7u#Z|àëëA:©|¸ß|Ávîß²²¶x0WSyîï]±RáCr;®c!™¿»4)¸4æ«iRÏö‚![dI†Þõ²xŸ©ðQ‰o0H÷d¯yU6§-ЉóÞ®F†ßÇ.´ço‚Lª‘“ãkVÄ„*h]1—Þ¢] šÜwõßcsbÂÖI5¹¡I,«ë'7Ù¦?«¨GfÈçïE>׋š–\[’™ÊÝêúoJãÇu_—kT2ÉÉ·LHkM6ÁKâ ŽœDEÓµ„™‰‘6³píóx“¿îÓ%ùYPP³jbSÎyÉzÙå ›•QbñnBª[¦·ÞÖËI¿‹ïFñOðs„ß(ßÇÿ¸ïö|(ÃðþœZf‚"}ŒTLàöB1Î…*£èÂOýt΀ö·"¯ªJÂqŒ’^cª¥o”·ùí鹊‡× Æ70Ê>zÆŽy»Gv«ƒ×3áH„šë•š'dqo>ácÌŒƒNï 3ƒ$òµyOm8ÂS†'ø0–ü»—#K•— î ÒŒg÷§q¤èD¶ï ÿWÿ¯ªÔ$©ñÓ™`ûæì \¡ÉÇ<²´u]÷ãɳ´Û<6Æf•¶xÅkøÙÅ•2Θžì#t´ý€@í’g…ܼ]ø:0éþˆ˜±µÝ°tO [mðÉ(ªЬ˜X)¤÷,ô¹ßÉÑ“ís*ÝlêØMÇø[ñ·5›6ñê·rc/íƒ_½ õ¤é YQpjòÞS÷àh öBOD3B½ã]Ë™9å“d»¿¶ë4Až&-òuÁ_Srî¸ZéŸðA¤trØe1Ûn¶3õSÀVqå+î^Õ=·Ï÷± îºêâêVõ*ƒm·GalÙxMÏ;Ü‚‹6ž „X#sÀYóܧÜè1õKì4¥¯‰§OñJ©êHÔð`=$Ê>¹s[ÖG8m0Ó¢j”À‰í®Ã‰èwV»ÒëÔ_gkNi&íZø…ɪî'Þm (µóM]bÜ,}á4˜vv6ÂîdÞý¢ñMÈmõ¶G„Ê"èV-ÜÔÎ}ñ©éâGge>ök@ßRääÑa᩵Êjð_y`ñ¦Ðï]S²ž4œ¿]Y®mß«ñs§Q>•®ÔÊ”I¼ú÷Ó«bÇØÒ¡Ú²Jì“9P¾AK4c¼ HÀýúibZb%(—VJ¯³©ÖÔ3[|BÝL5}Ë®åHXh_¨_»}™Ü3m…ÜWƒ´hÔþ"§-ä,4P¿Û¾xÿ&.Q8¿†ç@pFkÉÛ¢·â!sŠb=ñ„U2OGZêïCêíÛ#óÊzLü=6°ù|,o–gVâ£ì{ã˜0ž«ÓkmžGGzÒ5ýB½Øóæ0ùJý¥‚i´<7•|ýç¼ñÇ7f¾¢G¢üŒ+Û(=ÿz>1av:ã•ßïQH¼ùC7*ržXô°+Ô;Ž"D6ºH·û’Cð“Šî½8½…–Ë!Ùòç$µ ™ W‹CŸ”(c6¶†NõµùOWÑhX•¸ŒuÜ"PØ…ì‚8>$Šâ-/y‘Ôàzõ?VøÊ s'I·VÎX{ï1û)ú‹~âOÜêTÊ•Œ9yt# µsžÚ²ÎäK?ÙòÒÚš©ïl&.±´â`4ìmO0ßç+ò"Lu_•7߯UŒmíë„lße“åR¢&fÁ Évˆí®‘^ŸÂî|&œÑe‰™5EixÎ@&uï»%š° ÜUD³W0d­0ìæ¢ëg²B|4&sœ†£¹Š„™iö¼|rV;y‹=÷pbãŠ)Ìa­|ë/_“Ó¼}E›öÙ8F‚úGîg‹üêyûnôx+³ùýG¹†Š(f¦¤ü[Ñ„xKŸÚÐ6{´\äåüaU TÛmó³'и›”¶d¢ 7+Ã[ƒ.À»Öûóx$ÕÊg&çé ¯`öDÖmgÄ™|×° 3§ørxÚjÙ¹pÄ ÚpWês 4NOì\—Ž v‰®!9õ7ž) ³V§+Ú¡{Ró0w‰Ékè‡oo+-×»w<2íæÎrlÌU5é{EnC5e)¨ÒªPâ6?­ øÝ#g‰λq°dcÁ—d2¥«xIS ô5>“°d0=p›Z_¥ wÚ…&5¯DØá¸ù°ÙYÁLfF$¸kr‚\ üæ ·¦¬‚™òØUº4WlJ¼ÑΩ[Mù¸c½š%¶9_ë} 4 Ró«l;,zµq®¾ÊÙ*9r(Ó®[ _ðñøH°Ò“A³ÝÓ4ÇFÍ|×®c(ÍvGo4Þ·²èЧßÕýµï†Ô¢ýîÞ»÷±ßܾdÜ®º¢f¼ÞóŠÍ»¦Ím¯z!lg*1Bè‡æS‘IÇúþ™šÄ,Ùœ&…þakv´Ú‡¢…Zaw/ó/„œêOM-6ðoÀ‰éKeÛY±–Gž%‰žû´J`µA~Ú8Ó—»oÃnybµ0›ˆË®»v³nù1Gy—WÝvíýz¶"óƒjÍCU9™K½t¬žYs"ýÖs,³“h*û‹%m+›&‚UøÏìŸo‰Ü6´Yœ[‚ª:\¯Ùõ,o Œ€ÆZo…a>”-ÿy@7ÃP íHA.'Ë[%¡ÿÙL‹¢cÛ´˜nÝ;Û|ͬåÖw˜lƒïºçÓ;U÷MãVŒ>=˜ÖM{¿ö$b&t­„]G1á`ùÕ&ùx€¢v*á\ól"[ïwùǃÂ=n·×G0E*壬k$Þ_lp¦V¸É»ð»Ízw^{Ør'šgïÆ8ÌsáO˪å j+ø <=ñHÉVšz!¾üê`ZoŽÍÔð°šq¨[AðYâ¥?ÌÑ6Ñ@d4>Ø®6ª_]¿$£¥Zêæ?nMW‡i#”õÃzèõíô$ZfC_ÍAÝ÷34|¤EËd»iË ‹K¨x%°ÙõF,PøWø©f›|³a Åï ‘i^8”gMVÛ§„1Gc/3;­ºoª§ü€ËåÀ_*çiFpw™ Y’P[£^”Í?!ìWy´«Èû…û5QšjͶOzoÝ­íkrŠ-¦=%Y¹¶’í~ Ÿ+m;ïhÖ–ß3vò†â~¥«Ê¯ ÝQo”²eá‹CçËÕáDá\úÁ%ÞžC”x†òêüÕ­ñ™\Bº³# Ñ.W¡¤˜&¹b—áÖJцH·U$ÄäIÕ£ãì¹(⌢ ßjüÒÝB ÊHS§ÖLMLÿ˜‰}áovûÁÍ”ä¢xî˜þD7: †³Ñî8z’¸bNbä,Ò ü`À0½×†›Ç¯aPäFÐ?+“Ÿ¬i­uêw¾†:œÒ:¼žØÆSÓû×ø ½} ¡×9RD§v¿¿wÆ+xžÒúœßü.~;‘\Êgó¹ì<î¯øcᬬ'ãZ"òMÒ¦uíèÝL4¾9æ§Q¦C6ç’©Gu·ßr1 ¶Ð½R!Àí\³p(†š>w î•ÞrÇ Ö™|uš}/= ÷… }¼p—q~A‹µÊ·:ã ³‘ÞW71Óò;w­Ù¸ ŠŸgNo4nO~Ñù4ÖK¼âŸiï̺ýÅz£þƒ¯\Õ¨»Xþ‰è„Uu—äy®ûômaÐ(-¾’’’0Oò7šy×KExG!åcèm°xâ½Öšôôøý—L²åE´DÓ[âø°_Á¡uöêë EK»H³àSO…'ù:”Õ_Æ*õ¼k}àËæ’d„¥`SÒZÊz¢Ý“ EÃüqÊö[65W%Õ|õëæñA+…ðŒ§„.;Cuwðï0›Rݺ,<ù`‘òäæWk®è¶pÃ)ŠÕö‘J$†yÍ_>’RT@=bUÑ¢i)E1\|¥‹"ç >¶S«ì=û9‹swé[Áse¨1ØË“:ÿí»!u LÒÊÖöÉéXŸtÕùÞÒ$Åî“LÜo-;ŒÍ^îò̾ú­©T<‚I °¯|¦*G)ˆ*¶¤^ŽÓ|žóX5ìÆ³$jv{þ°|•>1ˆïexý‘VÜSN¾j«ÃðÅÛʈ\¬³EŸo˜£Q%-lÝ 3÷¬í lÛ"ÓÍ‚¤ä¹“.çù51IâµAsªƒ­÷ÖÃT4_߯,ÞG/höýìÏÛÈÈ3! VIõ2 •‰L#tëýî¾3yOÓ;}. 7Cd$_·…ïg\_óŽd›)÷ £òƒQˆWË“n²åîÃwÞÇ£¢ŸÌ?ìÂÌÓ¼Ÿà^VyÌÂÊÇ-ëÓÝóû]O> VÓ·Ž&ydóê“™ uO- ;£Þž§Å$~Þµ¢Æ[}¿ûÔCP4£™:Tì᱇S…Ö³/.®Cæü‰/õÄ„éNkøÄ¥1G0>àRÈÙNÓñ}{kÃÈ lûŸ>' YøìáÏq ­|äa..Óò¶}iˆlÁ²úëÞ‡%_»µX7Lž¸ “e‹CÕž´Øc5Í”øâ—.©`(®,’ gÒ—E]y7nzÌ¥Íä bU®2Y·&–L»ª/XFõáòfè>Á–C»üñµz­œ½;ªÌEäò¢w‹î„•9Ëý,§ð0ªmÑ'œ¯Må2íöÀ¬0–©æe UŸ2ïà<åðzv›Ã¨d'ýë-QˆV)_ÝÇ̉«,˜¡RCÍIß8#:_Ðéƒ÷õlOÍök¡”Btß‚oîªÆ(ÛѾî~ßÐ)×ygyœˆÖ¦¬§æ0”Œºä(êeJ„/´®Áh:3>JV-á†|c¼éi½ÿj0ã²2^z¶ÉT3ï4n‘J¬‰ ¾FC£ÌçI(OÛh¤›(ïß~¿€ó%‰³¯Ð ¯t1¹áÛÊoÖ‰’­XhL+÷GuÑÕ-õäV?íßí°U}Ñ|•ØÎ™Å ·¥'ó£[ˆr…6¬j£K’íØRSP†|I´°¨CgþSß_1úÌ-HŽÆàÁDnª‰©&†VDèí(V;i²ÚïÓB}bTØ{wt2_ò²Ì ’n &§¿º4ã7×EÞ©°NϾ+bŠöf[Sc":xµõ…¾忨¬ŸÓö|èû[$‹lÚ-'÷öîÌ4zZ[m Æ y”¨ˆ.ü‰“èâ…„o{eœ[·„y*Síjæ§®ÊIçC_¶*¡É6;Óuo¥Ë9>|±çû¹ÓÙQKoçõïz·PxÈ;us!„úi(£»ºo:àu¹eL.VŠèŸK®x*Â|Ú¦%òd¼-W'°ò6.ƒÊôQfá:²¤¡rüš‘ hd#Zúu:¹h?6*>¼Ø€âð¥ÅÎY9£^†ñïäõ½-6:_·†ˆ6å{–MMšâ=+Ô àž- ÏéLÊÛŽ‘&éaÓÓЧy´™¾³0+6míJšµ`oxla^Åþä”ññ·­8™Ö1N†¨:ÇÑ6î__K ¶€Šý\‘œâ?yöZïqÚ=,èA]'nõâüœøÉ㦌\쮸ï<8ìíNÂd.^ù˜,ãS6wý&¢iÆÂ¶S‡MåÎó2=É m` äÓ…Ù©žï46—DâÄ„$Ó„g¼u²_:(‰$¿á)X»»–[šÂ…uÔa†—~ö>xÒ˜®ðP‚¶¿,|tŸT¬°Ø„R¢i(aÞ®li„<Ìqæu5Ó²Ö‡­|‡£Ïœ;­óG…‚a!µCä1õ¨¤5¶`r TÃ.7k«J±_±Ñ…Æ„Ž®xU`XOõìªÔj'ŽÄ]zRÝá°vSŽpÔåŽÍPïrYŽ¢pæûËä–G¯Ï‘n¼3ñjZÙêB2_ÇžÓ¹°3ŒæÿL°ï-Q¾‹%¾ýæÆ‚¦ý‰³­¤@¹ù¨\[{…“¦º#®{PðÉêí$«‡<ÂÆn÷M"Â!§Ï݉XÎ4ÊiE§C‡”ßÅUê «~? Ì WØ:º1Œ7òþyÎöYÉK‡cçQqÛOÌšý!Drî¡/ˆ¹ÙO¼ô°h[NLn›ïVç¾IïooŠJØÙ¸ãxî2;þD9ÏãuvBýí³+ïöùÄ ³¨õ@ h‚rÅ¥K ÔXVßPN©vF>þú85,ÈË»QRô°Ð¶G7› sa1ð¹qˆH‹êä™f½†˜~Ž—Y·Í²ß‡(¤›$¸ÍFe6ÍIG«Tf›8eB¥ú¾Ñî‚tšQ Ûý¨ â•=BÿÔí)¥sÿ'N¦U¨ŠgÊ‹·ñö=?¨®f{^TpaõXÕà—VÆ–}{å]Ê1½?¬‰œöžº…’õC/_-r𛣖øÙÓŽ¶*OÔ¬&0©9‰ûoªZÇqyø£žZìmêrCT¹–J ·£Nš'i¾Ìʼƣbk­ %ÒU¬&V o?‹–›7åÛÇúAà= ×-ÁÆäßË÷ΠΜ¥cg.ÖŒ¦ fiÅѾat–ö ƒì%aÂäyÒ|ñWgMa0›WT¶:?f£íeÞMž-‹ÏäûÂ_eé˜Æ„:zTe ¸ÍÖˆœ;<Ì D"ÐI$ä(Šçð)”çÎ÷§Ñ™‹Ò¿N1ªTVÁø)ù®ƒB–â‘îogÎ2GßžH Ÿ^ ‰È¡x¨c_fîçK»Çg _èøsßêe•#«Ú{¹bK7I¤‰7uBïÉkÄäþÈ*;Ÿ1öcùÆO`죲‰TzçŽòëc†8¬ö IGŒ éF‰3…ðAñË#ŹMŠ%¯ïUíÆ¢ÙÖôÏ÷Úf«Šw„Ê6_ZÈ,°•à©-œÛÉr¿µ3É©ÚX¦¯l3#­G_5·Q¤1Ln½ØÂ´zóbIйtj.H‘ š©aáØû"ÁvGù8÷…¯òmÕB,»ýÚ-4MDŒ;sßOrZ|Ÿ§/«gÉ;Ãï Îõ{p7oJ§s>ºdü‰išSœM6`y0†’}‰©k½åӻ8ïº3¨,Ã/¬bŽÝ.Õ¡ŒßéÒ÷b,DìÉÔG,Û¬¨·O‡Ù¸»™Ñ”qz¼Î§â;7±gp_V´BKw=ËÞ®â @ÄÏWÈ:§xj£ÅGJyj+¯Ó¹Dd©BEFZ²)Ã5eBIe}™øA½û,Ï‚Ã%ýMK¼3 Ö,šs~$ß›s;ïa‰>67' Ùó~;Ñ.˜%ôÈŸÞ71í¾ÄLxâ-?ß\/‹6q?ëÏ.©îÇß„`h~N¬œß¶žê½ÁŸQf Â"ìµwÄ,™î”i¹u°-„.ÂI½*i¾öFD°å .#Û ÓÊÒh5ñÐhXìà@H˜z˜!ÁO "ƒÙ~;É7¬È¶Ž©bïy+W¤Iœ†¦>¥2ŠC$Èt}e<¯ ð1½] Ÿ¬ñ«cz7q„¤–zZÀìœZ¦?ù­ _œ)8 Gí²=$æìiÖúºÝR°Ã¢½ŸÛЗrmUf"ŒaÁž—Ma©ùèñRºWÌÇ&o0ªÌUS-´’=!9§T2‹å~¬Ó¥ö B›,4Ál×¶æ3WbÄR\ÿÛ†?N´qĨ߲í  ¢ú¾e!S>mVŒÓyüæG/Ù1iv åмK[ÁѰé}¶ c•þþÞKIÆ¢‰»õ ÓÏ«îJŠL–/šÆˆgÒ9sר_•Ì*«ì>PJvŒÆp³Ç‹r¥QÌ®ÿ(Â)Ú[Þ(*¹ƒ®°Æíx’À îMÛZ¢ì´˜iìù™2Ñh€<ŽÍ FǾ½ö%ѯtòf»ÈÆ{?Žn{X\¹F9Ëúcà'…©ÎlÍíÎß§±·=ë΂wfŸÍmÚëó‹V%÷guMAw˜‡¸ê­€íEnãWÞŠÏê‹.ï„©š¤YÝß×dmÅê%ßHeèl<§3ÙO‚ aÏ¥V·;×ê E™†ú8E.8RÑ‰Ü 3ªÍÌ®hŠÀ>Kص*zSØêŠÒT„Xâ¿éÞ¥†œqÝúJ("°-b¶Ðb­ibÑúÚ aÒ`Ö°çºüü {»)Ù¨s‡âì\ýu][K­áܪÉú¼;ÜÉì—¢AÎŒ-ä™)&ÍÞ²s¦T‘3/Üfbå` )Ïtk‰c`@¾ N¤sb ºî¨—Á¶"ì ñ $ O‹¢ŽáTÅ5Ÿ¼òö˜ ( ˆ·h3ƒ¼~[K~·¥eü-¢-Y[-ù~õ¼šÓ;[’çκ„ihTÈ᜹ïc’mØœRŸ¿j®| :Ý×gg÷%¦^Oüˆ‡"7ÜN>„Ÿ¼á'IþSÊÿ‘ûèÌ g¸R[¡‘s3mCtt[¿K·ÂáÆ]žm}z¿ –~à#c1?‰èã'4„–ïfmþ|Ýó<ëI¯K¥ù ëáZÊTŒ·0Rg‡ ey~7á2òÚƒìê=a\sý–—8²_-1“ü 8ÕÎŽ:àÑÙ1jÔÖóðdØD$yp%p¨y0<^ýZNk­‰BêWcúY»,”í(á6bÛµï¤3Ñ’Ž[ó'²çõ·…“š‘(ž$u¯¥’e7ÝFWC&Ë'hNÚGI §V„åêÔ=ÏèÏ}ä­QÉ$/Ä'œpßÙõ zaÅ~Ê·G)#p‹01Ó U5$3óI©W:ºxçÇ0­ØþÊêV,a]%¬òN飤,Õ¾Ó_ƒÞ“àöÊg²µ=^[ñ¶iD~üg–±dYµ]ö¶­h à?F\v™GwŸ.¡Õ­Ã¥v)ï(Kð§<£ŸA_~§Êô~/Jüãû+ªQ•¯xnv§‰ÀHjHÿëS^Ùñ»‡¦ö޵;ã¸Ò'/&©'{¿Ÿ=]™€K)‚à¦8¨‡ý㢛X]óO¦j>ÆçÜ?HÓªÇEe&· “l•:—bY/2¢ö£+J#·¥X‚½CË*zµ¥#® s¼VÅàÿMÁ÷¦ÿ ðÇà¦ËE¥"¯<~òâe—º?t,1éÖg†+e5Ä7¹¯E¿ghî"ô׿_a ©~`˜æ™‡C(ø‘3}Fúfã¶I®9¨sŒC\Šf5?ÍboWÖ¨UŽãb$‰!küÒÌî=T‡ýúŒFßsrÏY8)âU€y8î¹Ö´xdÎst˜Þ3¢á‰j ƒIýSœ ΞiX;‡Ñ_(‘”~“`'lcç¤MÑ® .Ã*g$&Qè8Ó]‹{4À\>CÉßd`WOn¼G쪀aõXmöǫȰriÚ²øvYGIv‹q—pjåtЖý½§w°¤+5¸´2#ã§o|‰îÚHÈÐ3-­ýÌYÇ, qÌÖMöX‚­˜Ýuc5nhYµmmÔ£ú"wú¥DéZòí0‚çZŠ(åæR׆ÏWbþL¾¹Ã‚¿EÏ#fô–º]›ýÁṳªtxÀƒT3À;™÷–ºLÂ(ä8"µ¹''’Ób¨J#\ørçÀØk3S>ìVélÖÝ÷‹ýúÆrÝœâ>ànÚðçŸ*Fž.ËÍÄ,weaLvjY¬± · Õ)~ýšÝu0yz#ž¢÷#¯(©öBqÓ|,å‘ aK€ Lš†d¬`¶¥TôÛ¼¤|¨xMq]ÎF€J¬ÀÇÕÁz#ʈ¡°ÌúÚ”Ôèú{øHî Šic\PÂËHÿz&UèÐÄæ`Õ©…8¯[JÖ-õrŽ^³Ç´Xˆæ=b粸K}ó#‡øx—€âçéÈzìt$8ìÉ%jfœ¤Âžu¼Øp°Kp„CbºhA™“ⲨÓÛ®0’§®K)1oùÉÛ1d­–ði7$?º±+áÞ¿ˆî N\û~v¡+ÑȯDR~}¹±AbÎâF£Bþºê™ŸBÝ ™šE|ƒ|‰m[¸1Ú)&´ªG‹ÄüÁSÝQ½KÇgÏ¥}ÅÊ·BîÅ{.i¿  mØGjÚÏ+(¥”r¥ºáYL/mm8åž73ˆ Ò8¥ô3ÁOL-ræ;Šû2<.fh±J+TÖÀ-Ú\G6IC$g; {ßSívôišÝ‡}8éð.}&¿~ŠîñZD-Îé˜(3|€7[ƒ±Ø˜½(êIy–«øy3Côƒï#ìÈìg<_åk%ƧÅïÜ(Xu¢‚ð†^ZÝÑHÍ/ë!ŸÜ-”ô!óÊFõßúö|ðtê\KÆg߯ôæî´³yv^2Ú­ñûç…ÏèУø„7è5"tÊÕ±2òÏ+Îì¦ 5o ë¥*Ýý.ÓñIHÒŠMóÃ,Jj¥#3g©'̾Uƒ°Ú¬p~¥ûBvÜÏ!KìÉåÍ7j1‘‘Íqg7>ŒÔE=¹ ª û?íC›. ¬¥Ë¶mÛ¶mÛ¶mÛ¶vÙ¶mÛ6÷WU»pOúÜI:ódQòäÍZA …Ï?²ï÷¥Z'Mëmõ¦¾ÊŒ[#‹6Né†F© å œcä€ÇXæ ”îØûŸ+ù".uÜfRÍŠð}Êq>þÔi_ñz,aŸÝIŒCM°~ÎQŸ’¡pP´±ÿ¹\ CYÊ8ñʲd™L ÞLDûúPòŸS Ftn2‚F@‰k#¿iÃÆÆŸõç^‹¦§-h@˜_ýà?»M…MT^_«øÌv2 yk(mçÎB†YeÒ§”‘J¿b6²¤b$f’Òí7æT’cËÜQêž±l0fû c!· J}Y¦B†×ë&Úº0s+Õ%CØÜ½ÉbÕ°BŠÛŒ3â²ófÎDl u¼ ÀE¹"ãvuÇ’Š²dwmz¦àï™ô»Þ¡')ŽYäPyd7¬y<€ñ’6IbrLíùkÚF ˆ ½BMê4E+ÞÓa)5Wg†‹GOÖ7¿—â2Ùk'ôÝ´.iSQìÀÞ*Xù7’½þÎ Ö€ÿ¢4TÚ®Ûø?Žèm¯CtÔBz÷}²ž­4Ÿ-Ff?O-'ùð­ÁTÝ&rÌ]šúŒ{“ák÷ØÞ yêâÚsZªD¶ J5éœ<0ðûdŒ{s¿C™ÊK¨ºÞd°1BIx%?øYȨ ·²-½V‹&“X«Ê[zïÒ{ܯ·Ä6ÎÄçÏ"ø@Ær7%9½Ú`‹ÇÕŸ»$5=Â"hq£9mçòN‘àf÷»út`0³ B^-Œá¸w÷Æ×¬›íž«1ºÝù èŠ#»%óDqºŽô¥1Ø=Rç¶ÒNÎÄ&yk’@:71û\‡óE^‰§õª®¥ÍÈg6”ñ6ýS˜à¢hð œD~á`£—Dë_©)šV5ð×[(ÿî­¬è¯S½8Ç£iÕŸsfܰ$'»ZÔ¤;±à›¾h€v×&qå¡f¨§Z÷L'×ý ˜:‡Ž­€M3”D)Œ®%“¢Â·7&Ž$ÅävpîÅýÁͺ/œÅ7’]1nLïÜ»¨2˜B vyõC ò< á´øfîX†fÑœ}ˆ°cùì-Á6·É¡yÒt§ÛÀ /èlqkù¢NùºSä‰MéROÛ,QQû¨­s‰µêc…Ö„iÁÄA>ÎãéÔÚ͉§›mÅ&£Î–m] 失õ sAy°*bĸïÂrœ‚>¦].P Î}xŠUÿ{1ÈŸ÷÷ÍRÓîT™¯Xºgaz¹²ãöøïœÇU£YîOð Î#ʾ¸+§—M@e8§ø!ðI‘Oaë=üpŽgÁÕþ:ÿ9þ½[ŒõzÆÑO=˜¯[ýÎðƒUœ%¯ŸèáæKí„9x¤–ú~ïÿ]6‰ë´^“ëÔqH„^]&Žs†10ÛZˆ##ʬ"û³õüDg‘Ä΄F,]¯Æü`Pú—'N.4|¯C€sÓÇÁ6‘ØààÔk“ªœÍº·à#j-·™k´}¨ªM;ƒ/éj¦ÚîÁ@S_:j/.~”§À²D,4Ä7s¨Ø#gÙ¡Làbe‹}‘œ6ð$Þr‚G—R°%±Hõy ]§ÿlvM¹]‹í:q#mÐ fïû ¦W.ãcRµk á¸ÛƒÝ"àc€N; åú„côpŽ7˜yŒ¤{}GŒ,&:ÞɈܸg|ÝªÊ ¸úà{`~&ý:/•ÞÌÈP®1ñ‘BEûWRZJI˜¨t^€C”Æ…ñ L¢sÔa…<Ö•i'«âÖÊ£õ—‡ò:Z«ômÓ],®Í§çFJÛ"Y*ÓÊXŠ©–ùÑcì΂âvë~QfåÂäŠ9η²Ž,”P³õ þþ*ýXªz·¿;EóéV8ù‚¸ì.𥖟“]®±*‚ÔIÿús3ïp¬ ÛÒcF_Ô›¨GÏ¥òV½I.ù±ê„:°º‘ ZPCI®»C†_ Ù šÉZÆ?­‘ªµ¤õõØ–Qr $탞™rë¢è­¡ú¾såfJ}íhÖ]âÅ*g“ïnD Z"NrGw\’J~¯†õ`ó@PÂkfCpÊD&ýî+†ÙèRùW1¦½ì?Nyy®§¥O_ŠWÁÎ Z4ÆÚXN7›!j#RRá¢é7æ&Zpë«Ê¸ÿ}¬Ì”^jpíÚÏÄ™LÕF(®*Ô¾/U­·âMO,^ÃÜÌ@ýšõeíå•sÊí±ChuÉG§ì(Yl 2)Ìó¡YäósBˆÎûÅ­ÛDH›…‚•JŽQ‰äŠŽ°Zf1ýüá¾Ãæÿl³¯ÔÌÒOû}y×1î*¾Š Á;å?lhÝæ˜•¨¡ùÝ®š÷ÕÏÇð"Ï‹#UÜÿM>+@=<Û–Âdúh°–¸¢þÐ%:Õ¶ò˃Κºè…—A Eð‡U#ù-‚cöwkˆz ã kåo{§Í±»f’(å™Â)F¥pDöÄt_ðà‰$k§£]EÚúà9:<mÜjï·Qaד)Û4€Ëv¡‹3c|ŒÚüU Œ¶°Ú½—ÔÊ^L”è›ZU_n[æÇ_=…–ú÷ΆJ:à›ŽÀZ”¬7ì¹šŽ‹Ÿû>µ1œ„°L2}QÝq.OÕ¸s' 3óÉ­cš!fÎÍQµÓÂW-æv§B3f æ?d:¶ÖYâFn‹ñagzßÍkºר*x—ød9aDüB{t{äJ¶¿4z 2ãå&¼ {Î YSÃZ4p°sN$>§oÀ_]ð¾I/¶Ï¼»{6þxÅïÉÍým¼\78ý˜©Wi3lé9‚Œãé³HH6 Ëy>dëÊ?F!+7î˹å‡=uÏ&T!ÆÍǰbÆ©ö™Â2ðú@\Áa‘µ A LÀ*H§[2Ð\uEÀ½¨úý5+:Ñräq¾Æ ÁîXÎÀìÓy¾¸ Þá2r+×3¿‚²Û®"áƒÜ™¶Ñ Œ3ñ›ì³Â­ñ_*QeߌïU¨ïók1ƒ 0÷Vüã-ÿ]}:ƒXX+<ùHŠ%qiÂvA:»&æå¬—`úMó‚…ŠùîˆÕÑʽX©ÆÛæV4¡•¡éÑÅX3£O®Í[12 ÞKj$|‡E}¾yUq’l‡E?H›>Ÿk{Õ}vvMMn¡uÑò…M½!³ÉšyûUŒWú¥§3$ê[¥Çš‰x,Aœ8Ù<©NS"¢:Æl8ÂÊž¦j*¾I×ÙtA¯2hb‰oVcjøÿŒÛÀmÄEŸÙH¤<®…7 Ä%௖áÜÇò°òš®*˜—ŒÁàJë|©ƒ& Y$Ù4þý(v?…jÒˆmuðäÖL¼S¦×¥A ;¼ƒYE K:ýöçEôÏ<0o;R+Öož~LCÉ'ë3:§Õi&®uÊ‘‚xBÕ­²8nÀJ-Î|´Ò?G@†…çoÍL@‚Àqc.¥À™U8¯æS„‹t-«ëàù†ÀL‚ù©µ‚U|¨ Ø­Ñ™;x%£ß\©ÀkVëƒx §!vBk§áb÷2"]¼§[õ §þI˲ºPÖÖ+Eÿ@w·½ïyTØ /SÀÎ0»ÜÔ¯qÁ#çÂL@´ù:<â7S‰øû‡¢¶É»¬óö`±©f„Œsmìh­œ`‡º~¨’~f ¸ < °ìÅ0ßÞ*ºû^ùPU^Zy²­¦]'6nRõ¹n~ì‘è¾á³l^’ÃZ!ÌQqmèk–´™ˆŽ¿»Õ Róíºç‰=¤òÅk"ËŠ´™æ<±ò,ÿã+/¤È°3)ˆ÷ E¯ÑÞv¡$x™h^û\çít1'ä=…&A°}Z^QÚ3‡{…ÝšûÕ¹vwm@Tÿ0˜JæhØ``³tÇ8é—>hŽ<À±[½-ƒòEœRÉ®÷!æç·kF5¶®@¡øyÜÖæè_`ŸvÐ<+þਗ“j¿¦!Ò±æî8ìTï ËËùq|ÒRÔμiæ¿ç¸pßšxY×S]¼ÖB_£2Ѓ‚Aí&?kÛ¥Ëä8ÖÍ ;@àJ‰º·ÁÇȺê%¯_.§ˆ©5^.½©vʰ ¿f¾R¯†3£ ^@Ä«žîZ;:~˜úóŒÝQ5¾·éVw\^UV»ˆÃ.ùëâFƒ%.܇¤«@úôâ3ëåj’;áµX¸ É…0uSrëL¶‰)2Gí®€æí¿š#XU=QÁUÊT.DŽ«Ô·êÍ7,•`¦ré`ÊËèh19§ÎmÉs*¾Þh\ßu“"¬ãCô#jÊÖÛ±©hñ”sZ‘Æ¿9=<6 ì_gMê|—³bcQ ŸœSihßf/û8ÇŒÏùm"Òe\õÜýM¦.v¾ÕâA¨–snM¯õjvï_-h¡) $èm¬,\^šqå`¾<¶²®¶éó4d>š>COL’C+{pY# RË9û æRÕcGûˆÅGƒ»è—"bþÝÈ ç‚ÖÙ3AÚ/¤À™Ýð—ýpG¸E ˆƒúyóQ%ë/ ø™F5_‘è"P/ö¨tÓW ô"¯¡ ,‰Ö jµÉà‘;$%šŽQQ0âl*Nä l¤Îº›{Ij]TuÇ3@,w¹ kH Îßî†bÃÊ82tº$ê~—§Êe€KV.ZBqY@)ÑàÝÀ½ßÓþG€eÂî~E¯p8C¨¬œðª###Þàöƒ˜˜ümíQëá {ÉBw~™B&ÈÀyñ©^nowч]¤•øsy¬€·‡Á¼"n_UÖîðbn&‹qÞßþ ?¹Ö(jƪo¡ù›l–>òQ­Yrª.C/Uè4\}ÈkÆ ãÅ”vE0 d(É›pèQZ§OþöÕ{í9âçwß.IèiÖw â®Q>2'O›bÔf|_ÁOò>´t È—Î1@œ}²Tè€aþ¾7Ã+Ÿý­‹,ô6ÜL6ÁVs{ÈR$XYOµNÒáWŸ¶JAýŽûöÉὬ«0F¶4t4?0Ôg¿ùÝZsm\'v“š .[V1™ÿ~M ½p×ÊrXŒâÚõ“æÁ——¹Dð—n)]D˜V}MI Ä- MØÿ•ñyO¥@•~Oâæ3Ê~· ’ž:•ÂÞ²d¾P×£nGìÞ•LÞ_^½§.+™ U*Ú9Ö¨å,šº¸Û_öSõ˜F÷šØ"Gá"¦Ñ©Œ¡‡éȸp®Cï7:XåqT–±Dj†ÍÂøõÇK"%^Xæ8:xô¡SÅÀ†kŽaËÑ5¢„fĈÄO?_ ËV‡Ši‚òA£›=Šç¨hÂöL:â¦(qÔ V«,®Î¿0Ö:ù¹úê]òƒ\ ¹N®Í7~ »Õdé¨QÂø,qÒØ´÷·—ÿþ‘Oÿû»cΕ޳hÍàÏhãs\™Ñ‹6µ{‚MvyA%€`üf†æ>h1k± 'ìÿYÈgßAÏÊ€Y7­9•Sùö’ k –`8I¹Š ÇþBpÅ È @Tvñ™’ÁÆ'î1A‘ÀÀÑ£{£€ I¥õ,œéa)MÖÎû°• ¢Øë®í€“µÝ‘ý¶Æ‘ê·õÈfAU©e+êl‡Ú„!;ƨl‘}Æ7’rïÆ¼™Šá)Ÿ`¿‘Ñ\õ°Ü–Âî0)¤/©øºSdÓ x×¶:v»~‚Æï×´-gØÝ{1mP0+8×OúuÙ´l †'(ö–þ< ZìÚUr¹RT]·òãhœ•t»¨}VaG<ŸáxÄÈAߌOδO(Ôg ë¾­ÑæNƒöHÑ—ps2Ê<ï¦Ô(iØLèþ’£`poµ,ŠÉsLv<ø·n48ôî"^«è¥¼èˆÓ€šx`2jX PdcŠÂ{•âÄŽ³;Ö¬òûü³ðˆ)×Ãy³O9§ÁS¬ðÎKöÈ‚¼3©b>q„<@Øè±­ ÅH„'3•ÀX¾]U ®ÕÓâþDŒü]ýïãcuwŒÓ\j~Hó•Ehœ·$¯ ‚´¿ å²PQpZƒ4gqÌç?5{…Þ€ßVJiÇ{ð‘ô¹ë:Ä{¢pYßAi£¾ÂÂ%‰Ãš))å)Cõe™(ºËãÔMl™¾‘ô %`>CEfë(}Ë—ðƒuÅëêtϼmÇg_³DQ¼mÅmkkÖS©º°ŒÍK­Ã•»Ç"õiÞ KþZ–-·?)U5 ŸÁÛ:š16 î„ÃÙ|§†‡p)â¤úä°ì±»¹v§îûîN9ï:®v¾Ä?€•ñ¿ÌÑØú´¿T†íjÙ:jOïŒ_óßçúá ÍNÖ§ãÊŸþMrwA¾·•Í™‡DDQ^!«–ôˆ*èNÍb{¾Uå0qÕ>ñä'¾”‹Ÿæ_í-^©¾ä]ü”4ZÒŠ±â‡ñ0¡s(ÒƒÞ¸lK¤XKe?#©½ ÌwD¢ÙÐ\ïïŒDñ.é¾4%†®·HâæP@a»-eWFv|'5:´J“WeׯxdÍ‡š ·³‚q¡~eð²°aÉ~¿Kß9{x©GpþÑï÷²§ÞÙ#>^Ɔ³0ÕV|Ì„¬ WÑñÀ U¤×⺘«Æ¨6Ê/Óªq«9Ä)|9µ‹YGÀ†t+vðŠ(Z¡Ìh!`:+ÙDãYÆ¿†c†Ý0M—­ a™0Frv:Êçýn:9³7éžwEî1•ö‡±^|Ÿ®Ó|%Ø,¬ÇC6_)ˆ¨v¨/O^½iˆ  MF§²Ð:p®ê—²µ¤Wœ DçßÄW}Þ­¥Äâ;#fy²Š‡:¹sï+m‹_* ×ÊèîÚÞºÈ 0­2O!ÐrÇ!øËr¦ÿá»|´1sJXôê“ÅíiŒ2†öÊRÙ)w«óI'S…lyúœ¤šüÚÂÂfé¸ðyäow¶LCóØbš1tÆWªøû ¨qíÀÞÍ#ÃK8r T6=I”e™äûòÎ ùÊ2ðQ%Ÿ3ÈyFž[šgµ©jÜ­ED †[ÆÞ¯:Ä£˜ Èðvtmd#_H,{ºdfreÉñr­°ü s»ÍG¼Õþ«5³dÜD`S¼Èdpõ zâögÛälåd › Fë M5þøjÇ&ù›‘jƒ‰àí¤ý³yþæZ¨ÉðUd4ßÝ»`cçK›Ì¸¬(8úŽöŠ?,‘Á¾VúÐF±ëU öþ;0ÖîÅsÙ‹î-ñV ˆï·l²Ú)°8:$;e™´é 8‡~AÊ}DNᨮ&+?½+.fƒ>­”Ž[`BG†U5«KX¸øþ|có4“¦e®Õ:z9i³=U×åÚ(+óÖ»JÏ*ª Ë‰r`ôînWç÷aÄϯ{ôZ!~ÙÀÍå1:üùý­ÑYîvß-Þ8Œ'ø6‹[Ñ% q«ÞXvþ}¸)‡÷X_¦¦lÝH%ìY¹E"€•Ûì.‰÷aS¤•¥T¯HT˜ZÍn{Â< Õ?³ öâöa¢ãþq¥ÿ« êÕ?)bQº]*ål‘ß•ÙäÈÔg†/ÚòX~m!`=}bkòŽu¬¾„ßEÏ^”ŸüÆJ©2èAIpÎûe€ú=P“Ñ¿Ábà^Fê T €6ëÄhL·†‹U[2qê7 @PJóÕµCåâ{õy‹ñt&AöÅOMfÀOMZ1»0LA¤6ìóg/F‘BíñÂ;yx´ð#Œ©é³wûËüYܘìÞõðò1O¹nçwzùÇþÏ\g60¼Kƒ÷»JZ`ôuê>òüú*Å]Ó·>!¨“ ÷`¼Ëë°S=WÑŸ?Ewgî«ÑEÌÊYXõJqÚ9¾h¼¸†Çõ2!¦¾ž3§súXBOý#§ëŸý-û×=špû”@NMÂt.‚á@ÅKK4 ãÓ¤Õ^GM~í9ÔðÆÝjš¡¥²øEÑ"?Bs$0#A1_ŽÖOx±“Ç8n›®q«¸Wb_Jºä#ÂŽô{oÁ¡³'Þ4¥'YÖåpž°ÏödÀY f-hmœi™»ùÓW4E+¼nòÄoÕJ•8v‹ еà!^Ðý·â8OÇêˆÒ4t z/AÀÏÃr6ȨWбtÒ)+4£•Ön…™²dÈÄÉó\¬rÂ!^ˆ,«D†naƒ¬k™ƒÉ𒽜f@ö²†Ã1è}”­º]=a¡mج»PÖÐÖÝ7˜Jðûÿ”8Þ‰ ðp'˜QX ö=Ë̺°û¥ey™Žs`cê(8@Ø–p,L\zšÐŠO“DñCcr(ªlâÍvѵƒ¸´Ãɼm£+5ÉSU‡5i¹Û÷CCgÚ§åÿ‘êÐzà–Ò-ŸÏ•±(Ð&'©BûR@™¹Ž(s&PÏNRêå`ð•ò±ß„ÄS­’TW!Ÿœlïçñz¥7=ÑMOÿA†g~Œ%‘5Žû<€k¦)?ü3ìv¯ö‚¬4wÓ‡ûo¯e߃üÁ\sëAÓl[ºÏÛ¾Ž)ÅbAÐé…„¥µÔÇ“ôÜ TÅóò»iµ€¤GÜ~&+NÄ:¸ŠmU‰z"ŒrÔ„÷„e³=ÏúH¾{ž5Xmˆª²·3%uÈnþâl³…¤­kžxo=šÖöÚÚɟʶ^ˆcJ¢çW\Eµ6ÄcJHþc»æ2+'°y=ðœñ“°ÂNy—r÷šçÐÉ]\ý9R»¹Z,” ccÄwpÙA㮌 ºd „³CM7ÞN‘ª2ºðဲIb‡ÆAÃTA•À¥¬Íù¥†o0ÝYJ”µÂ÷ùžÊ=Ê%ö®p2ˆ‰Î®¸\“øP /öÊÜn)y…sO—jh¹¬n{#‹!ËW‰5ÙiÚ–2¾"Œ†?Fcá!ߨã†$µÅ¬=¹À¬Êv8°:ˆƒÂ ÓèÈÞ¡ ø©P„<‚ Väbwbm‘¹Àû(H î{ÔËåiÜ[œ‰×%'xéÅð·âtÄ«£Þ&Ü"VzC‡hýAS{¾(=*+Ù8eIl ÇGº¼¹k#–%åªékËÃ~@M"¥xsçÝP³.Ýöì5…w »#þ7¡l°(¦ruûi˜‰I#†R³|·.pûÚÀ# :¤9>Zº–rÝžôNÖ~ÄN5À 3ö¹j¥’qôíŠ6úA`CoQ[1ø_EÒü³ÏÕ¼²üØë—1{ ^Žé˜-£ùôK;‘dÁµXa ¯³îé®HÉÕ^¿AØ6ìEÔ›µ‡8llH¹ÕÔ6PÍg4u®²ÑЇr@yf.–kÏ‹°Å 36"¸œªB6fK’¡¡C4W™ß©áK&J×ëgÍæäº#ƒ>Ìt=ffF*®Lõ× hû߈¦¾v²íD”Cþ¨Ï–îíu:y˜WöÃÝc‚Ù%Ÿ±vLsL9Bð–}øþà¡ 9;ƒÙ­¸†sP|K@¡Â75*¯˜ÉÒÄãíÑÙÑ@(cRe™ÂN îšãŽa„‡á‡(–Ñ?^·‰ø gí@SN![´ÚåGÿ1ºßI…*G‡ópï¿íR¾Ž/þ¾D¥4CyZß=¤püºïŒ7ƒŒ úø ÜŽðB.eR*ÃÈÏ8[¸e¨‚Þ;ô–6Ïg¼ Ñî\Ëb˜žô‚U³þíݶAÜáªÆfœ€Õõ¸~@·å¹7$¤EH|ð rÎøØ¤LÊLÎ!Qc>·âm™îq>xß,÷^rk†”Zç <²É =xú­XÈ+I­9ÿP"pM‚EÔ³Ž¸aðqcòµè”‚ É£&ÖSLï¾#ß Ú»@¶MÅFˆ\{‡)ÚY¼\ÏŠÉbð§Áujéx{ï„xÍç Ø#ÐëåÕS­Ù‘;ï—5Ìôït¡Êž—Y¬eMÄ#9²öí¤á±EI¤9CU*·ÑxH«;ÛA(â/l³ %~OKsf’c…z± F¸Ê¾íÍgcÙ¥³dgÍÉŠ˜"¼ uÎ#ÑL´ÝBqÇ bln·j{¼&/ ½‰´ƒÀ%ù›½“ô˜Ø ‚±Im‹ÉÉ«‡³xîax5T¿Ó´„ã4,Oþ¶w”´chsÔ‚” NëÚ5NEÉ·I¥ÏŸË)2/_{üi4b(6€ÖÎ öçÎû ,—ú¼¼Ê1D(ö‡‘¬רó!—ÃöÍØp+0}Èй~wƒzD=bLõOîâöXO'7J‘N—|CVÃ"9³(/Kª~ù$ SP êÌÔÏ OOîŽWþNƒ"KñSf‰ÃÉ<jfyê*Œ)dÏ’Ï>¤Otf[><‚¼yhñ¾ÿVX)9Þ J`ñ c™yü!`óY£ü†cÖëÍ CšŒ®0Ç„EKv¼¹`~Æ£ëÔð>WΙ/\y(Ägg5m¹L/ SrmÉÉ3ª©Sƒ39ö\¹Ap³»ËÅǨÏγÿ¼Ã÷˜b—Á‘…m§OxnÕ0ík<:ß²d`°j=o,vfÄ•¸øÆùh¢pt|1ÅÛåÜ…È”ªÎ>›8\¯Ëátèmi͈!~¸ÊœWm:•qÓ›Hg0÷ é›rA€è+ðH£C‹æ•O©{s›Tƒ¬j U7l–ÌZ›»®í^Wá¾únr=Ùa”f\´Vñßà1›*šõ@Ïóûv50Û¹[u»L3>±ì d>mCÁÐ$H×gôù,¡òWAätózêkð.Ã<œ]‰(nqŒDObŽ©†}au)›™Žê:zC¢ïºÙRu§Óþ}ÜÒ¯"- ºÒFdmÊ”ôêw²®»Ì¹Cl'–f4†éG§uõ[—£we¿ ¥H&†ˆ¡–ÁoØEP1½ç7?£3£áÔi–obôëMÞGvkË.À9»‹½0´§_!¿…¿¥6X©ÌQÖÛ’šR_hÚd©/n,x\âO¶—Ñ0g,y!3ŒµS§5XþvíˆSL8‚劖(¿“Ql]c"É9ѽ î‰+v².K®„ªL)Ÿ¢¬®YÅa'Ÿž9í¼¡Ç/­]›q¥=Édd  ¶eû5/¡×ì±On®+ uÃÁâW:ŽÂ6•i ò{÷›V:]U† ³§ÃÞÔ¸òê(Õ5ú±÷ÅFAþý†dkÍ8úz†U‘;LwùšHÝ^œˆŒ¸pʳ@ÒÝ|Ót¹T“?œt†'÷쟪¼çî®b¶k³§â=I3@Ù›ZAú|êm­Mw)‡sŠÄ ›B<^9)aŠ}>pížoµ§Ãn9(@ƒ)Ŷu«”v5WòeWhQîD´ü r×îÿpŒwœ£O“2É„$väG£‹×Ľ-)Sw¹W[ ÒF÷a]æ$CtTéÊð¶>‘;GÙéq-ê,æðžôP¡Ä>Ó:U"ÑЙ†Êeä=*•7ððJ²'}7rãö7{‘ñÀïëO NTªÝgÑ]üš_(uîEìuüÊ\tP‚¬ÌLµX#Ñ“ùÕc[úg'úª#&ál{W’b?+`A¼Êo’³Øêÿ­ìI—¸¡:ZßÙ¡Š-]5­Ùäj†ÎÈ¡?O$§"ÿ|%ÿá gh]ùÏ'×µS6'ÒL뢂´¼ˆ4†n­ÇYu¥²5T%‰Ž>t)ZeÌðt”2Km€}GZ“vTײh0ÙæÞ‰eŒ›¹bŠêbg›ï⊮`@IÚ÷V«påߌàÎVÐ07¾œ7U+®ÄUWDíp{œž D2N`‹Ã¬š¬”ûöŒ SÂáõ F1U975 5+¸o=ºk³Gßêlž‹+Q&‹¾ñ&ø~ÎNË)ëÙ¬tñ¬0l²ìòW‰Çê‰VÔFvJã&)ïy&‰§Û1–O’xÄø›;ѳÅôà÷`Þb‘Æ&ƒ¢ò;7œ—ÊÆßgtÍg»#0IùÚqÚê}²a|ó>Ï4ŽuàÚÀdiUˆr\Áhæj¬¾%½×Þš2c¯Ø²O·g™õx^³òÀ±~ }|B5•û5sH‹^ò¨ ÔÖwš‡w»ÈA•ÑBåQS8=Ë!m*"»ûß ’Î/I%c¼ÚQYÔ©”H¯ÖþN÷¼RõûNñããz›9n£)9öÎýJs>ýüi«t¿!w>\XŠQalcK§>\ïOxÕÞVïÙ“©­üÆÅ„ß ˆá,‘m2ØõŠùf£ˆçºÌÊ­tmqQ§o!÷*„×IódõRÛʧ=tòÀËg݉¦0ak·­wËÃûËa=PŠF ¿(r <R›[PLnÛc 3'!\}Á>£{”;š!¡ìCœO"Ž«`ò„1h9¥>:À¾Ú2°UÒ[¯‚»|êã´÷exжËoÖî·gÊ GeÛ_¨1J Ô=$7î&.u‚;4šB–‹xßž³vì«w‘,;ÌW$ó{®I¯Äq "Þ€UG‘;h¾9 -,#v¦YÂ}~ç@áÚ„ƒ†ÇÛt]s~  $:–tc&Û8V—_;"Qgï8úÿͶll$ "^'%F¦œ•|KwiÂíqi Ô$¶XÍQBôðUmÖf:k‘¥öQËe;½ +‘D6RÝq*u]øÓyd " Z'YÏ„¢´q«ˆú(œrßßÿЈ$DOj{íš¿„Ší /o»s•(ÿ Fàì'!x7©þÖ]/‹ìÂ8¸4U¤Õ’Aå£ÃFRïë°Ðlø‡]¼V;{=VÝÂ¯Ðø&%{m•‹‰Âý2Ÿ9íô˜å`«Ër«Y¢‚FàÆÀg ZÃ30` ’=©ü}¬´Emº!ó,Ãø÷2¤’±×Äà pÞ4Ž ßæ`ŒZÂ|êÁT)ÌøèÏÐÅT%.í”à´M§€ Y`C•§î6#ßêtðL^ÏöV XÝÕžEŒ¢ÀOà«iq^a;*yû ­rëÄ%$H$€&¿Úl-˜e…ÝBÔàçJkÒ;§>8é>K;Vfô¹î2\å[Kß¼5<È`4Pñ<Ũ£LöêËZ ™_3WBŸz%”ªb¦€¿vûÂÃÈ’ãïܯ†1±õ1H®5´<&I2øÚ£öëÆÿÂüÄá?5Y!‚AK^Å¥dKêׯ¬Ÿ4ý¤ðóÕ*i‘_ø7úÁ1ü C²ù$ˆ­Œ-epÚùì^Lõ/*ç'WÖˆøt1 ¡;*O®Õên-š E³ë?§)Â)õ/ qöMµÓûË%u˜`gãëp¶ %/l²¬WiXïtÁ ·ƒaï>šU;•Ná BýwÆuìÒ‰q5TÞ¨Aåå€ï⻯.F¢ÿ‚L%D8æ¢äÏšìü…JaüÞ(å‡@#rzZôú~KÚþŽêÌ n¤Ôy%¹QëE ¿>‹«ƒè¥EY+8ÓG ©&½n`qT`N]¯–µaê6¹k7YO}{S-A6¦¾ö/^JF™É¤¼{æx`OØ“ŠIÈoÌÒ™g+·]Må æjÄ`™‹RÑ~$Ì,ÅR=iŠ©†L§bßõˆ=ú{Úzi0ü˜V±zçe½Ÿ„vÇ/ãÑ€ýGß|‚ï½¥~;[ “*·[ØRkÍx}…ô¥üÐp…âi5HeÃïcéw|íX{Õãõe(«ö€íÒ"ïó£îµÞæ1NÙIYD˜ªf‰FÎpH†³µ›î¼xJ«Þ+Ò)˜N̨/€1ˆîJ@ƒZªJÊõE0’T¨5Z­ÊçØ ÏÍÒó¦{ël!¿$Úø\ŒNÍ0à"zzÝ× §·{‹Í’˜Ç„vîL¸¤ûÚäCs(ûKà·š`³½PçŸñŠVjCwyŸNfãvsóh7# š=Š™,1¾Àž´µt ÇùÀb;μ_n¯[«{fj Ð×ÿSX‰6µÙÞÜË”UB$§Ïþ¢znîqÈòOº\²Z°ì¯Ptz8`÷u2›)‰’òyWÌk!¹íg6ñÓ:èÿJ3s’‘“Å>ÀÏž‘ðF}åÿðIå2y¯s½[#Š\+ãæ½cý»tù ¡Vú¨Ý錗êÚ0 JÖ…ß ‡”£¾z½e·³'ʸ^Šë°ÈŸ÷¸lƱ…{Êï~º( s*ŽJüª÷ÀÇQ°¶/*oqâz[C æÏžL9ˆS'S´‘ üÚÌÏ•ŽÎNl?Nt@ê¡àY©AB¤'P`8` 6ÆÓ‘†|äL œÞ˘d”ýÞª3ò·Kâ”Ñór…»þ(ãÔê„5 é0ÉÒÃ~¥ÿþ[½<e0ö¡‚êîvšïvLíäíÅóŒÌ¦dÃ$.{lu„¤ž¤›ÍÒõã »6Wóúõ¾¬I<Ÿ=K¾mÉcçaú_Ìÿ2Ÿéÿ`ú¡ÿymþg[êøœaþoºa‰endstream endobj 31 0 obj<>endobj 32 0 obj[600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 250 333 713 500 549 833 778 439 333 333 500 549 250 549 250 278 500 500 500 500 500 500 500 500 500 500 278 278 549 549 549 444 549 722 667 722 612 611 763 603 722 333 631 722 686 889 722 722 768 741 556 592 611 690 439 768 645 795 611 333 863 333 658 500 500 631 549 549 494 439 521 411 603 329 603 549 549 576 521 549 549 521 549 603 439 576 713 686 493 686 494 480 200 480 549 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 762 620 247 549 167 713 500 753 753 753 753 1042 987 603 987 603 400 549 411 549 549 713 494 460 549 549 549 549 1000 603 1000 658 823 686 795 987 768 768 823 768 768 713 713 713 713 713 713 713 768 713 790 790 890 823 549 250 713 603 603 1042 987 603 987 603 494 329 790 790 786 713 384 384 384 384 384 384 494 494 494 494 600 329 274 686 686 686 384 384 384 384 384 384 494 494 494 600]endobj 33 0 obj<>endobj 34 0 obj<>endobj 35 0 obj<>endobj 36 0 obj<>endobj 37 0 obj<>endobj 38 0 obj<>endobj 39 0 obj<>endobj 40 0 obj<>endobj 41 0 obj<>endobj 42 0 obj<>endobj 43 0 obj<>stream xì½etUYº°ûûŽÛßéÓe¸{Ü]I!BÜ}gÇ îÁ’ ‚W…»"↻kyuUºÏ\+ ”œÓÝßøî¹ÍḇÆÎÚKæz×f>óÕi—o¨4EŠ (è®0ŠÍ7Ž+0‰Ï7M(0KÜhžTh‘\h9q“í¤-4»É›í§lq˜¶ÍiÆŽ13¶¹Ýeæ·Ù_¸ÏÙ鑲ÛkÞ.ïù»}~é·hOà’}ÁË„,ߺâ@äÊCªÕ‡bRĤJH;’´öXòúã“2ONÉ<15ëäŒÜ33óÎÎ)8›Rxnþæâ…[K—n/]¶£låçe«w–§ï¾”±§bÝÞŠ û+³Væ®.8V³ñøÕÍ'®n9Y»íLÝŽ¢†]çš¾,nÙWÚràBÛ¡‹m‡/µ»tíDÅÓ•7ÎVß:õVqíí uw.6Ü«hºWÙò ªõauÛƒÚëkoW/>_jõâÅt.™Æä˜Ç "!%!¢¸,C榱› c·tÈJÏÅåS8KnÒså+K§t öq~ûîÞíïFdFÿã-׋C¼[ÙÄn‘ú&ú/ú#ýNäí»ës_q‹|åÎcòb6Ѥ+wlyöÎÖù\R·åûÊ¢V¶Š>d ðAáfwã¦4þ›ÑyºÑÙ4=uÜ„p4Çåñfx‡›±y4Àj“)á}nÊŒûnM¹u@³Còÿ ™VÒ˜ù/s³ 4|èd¢LÃþIg:”ÄŽ>p€Ô:ŽšO±‰îÉ×”);è*d¤‰¯:ä#Ѱbæ8 ËÜ´èšZH„”x€–®&uRF-´è”ï¨']VþVK0÷ŸØþ+\ãâSWpS ³‹›B°1¢KÜ”o-Ãú_¹¾Ô?䦸 `±ÂMI²4”í-…›ÝÍN+Ã÷ÇC‰‰Œç&I[Œ6ÆÒŒ6š$Å JÄ¢oæJ­SëŒÏ“®Ó¡JÃoqd•DþÙÿN1y÷SdbÊÛNnvQC|+©~ðå¾Éaï-òHûþöýoå+¿¿çŸ¥nÈè츣ÄMð'ÁQÈJÖs;¸)ÁŽë€6¾bj" CÙÓÙátÒ«?ŠHbS7…Êù[nr|'7E—d5–’îöOn#Ÿ?>ïïöˆ»ˆ©‚0ˆù€Äe1[íV¹²¬~w…ü§L[i+¿‘÷¶’äãå 2”?ükð;ÐÁe岊þ#FEßì^ú¦ÌM¡Ôsœv$M uR?¶¦+¶|.¶Y5¯/OEÑ4õE_Ѥñ\œ. §B ûAUк¸ùî+¿ï žïl•R—DÇ:{õžAO ª³Ãï„òή­tÓÎ{IÏõ›=ï(ïïìLÇSt±o;Éø{Ó1ˆá0¡I*'6XTKIC ’›¬uvÝZ¾¬¬Ö¡lÊvK±ó=nJ·û3nv`¥K’ÿðCçãÿáa»úÓõAîƒn‚Ü!ù¹Äïl˜—e¢a7–ø®ßu#ùÃ;iK—”št î"7ðÿʹŠþ $À€¦p³[q³k<jEGÔðoâÁ”ôM†}ÁÐøÍ& ›ÍãòÌã³ÌãsŒÅ.ôM˜Z1()zJ§Þ$ ¼ôÞ“ÞÃ.X¼‡˜éŸüÚߍ&.ÎEºšP‘Þor»ŽüÃïz+]Y¾x4±T c5·–ïÛ‰ÅwÚ¨…:ËBƒžÈ|+‹EH¦ƒ‰rWá)æÐí˜C¦IŒd$—¦(•2Œdô ë¨äXdg‡¾)È‚õŸlmxÒwâúãWb“ ßK¦˜¯ø,Ã÷›02ËM‚ÚûÿŸ?ËÓªN>vRºÚŸ+‹ý£óR¬Ò |PP¸ÙÝì´Ò¸ôN›€’91×4.Ï$>Ÿ&š1yúÑ9zÑyFêl“¨4Ó¨TSUº±zAüz6k'l¡½¯cÊh{Üëäæ»q[¦I4ù𻑼ë‡|5¹o’ETè}üÉ¿?åý¡û½1–ßž|07ýÝ)ÒŸÀêºcçhî%>™ÆŠ¨þì¸T§U“+ð¤ìš–ÑpÔ Ã2ÄÔN uÀ¨ãš¢‡"È(f GvceƉnÐ8’wùÙóÿ7ó%nÝô[n¢ýNIì„&èü¹)Tþü÷pÙq‹÷.ØEäŽïÿ~:߈ÂME–nv7nÊ㣓¬xŠ Y)2Ö4zIÔZ£ˆ4¶D³XÆf¢I™«ÖŽŽÏpˆKµQ¯6N5‹^KDð{&m–7ø|„@ J£_þÈÊnvjd’¡øw&Y‰\ïBRåÀÔ.~ýò¹޲îɯN0H²E³ó¦;ïÈN!‡÷ + Ñ¢Û]Ä4]g#Úû±O2ÙŠ{ʼn[XDçZFc_}Ÿ›24¹šp€J’¡ÿ9æjù`ôS±Sb±Ä¹'ý¯ç,º-…éþ:2zþû­¸ÚŸÎþ|gÇ]~O7Yï ü]‚z¢ò>#dn!K[¾”üÛè$ò»ëËàVX©H@‘€ÂÍîÆMÉ™%TyT –1ÖêtÛ˜4›¨Õ¶ªUã’ÖûÏÞ±xWäâ=a ¿P-ÚšRà=-Ë9Qcâ¹A Iî9 š\ÄFj­Nå‚2‘ßC§ä>ëôýýš27»èÆŸ2ˆ¾Õëºr#¾’y÷þ9nJÐ|O÷ünÒsLˆÉãÈba :Ù)ßW⦧Ô7kO3U R|+4Mž7×0~ƒI\†El:2±‹N·WÑ2hv*¤ÝyMÉu(3¥“ø‚›L;ˆ¼d®ýí8ëÏùøßïtÇÀuÎ(ºþÈ“ø— )½Yñö¥»K7?¼ß´®[Èó+eÌT$ H@áfwãæ(ì„SvhF 8°MÈ4Xá2aý¼‚¢Íg¯ŸiüêdÝëâ¶ïOÔ¾b[ÔüuQËÛ“õ/J®}{²áõÞʧ©ûâ3NE¥êGe˜$ä™&æëG®5S­ùâÊË3í?kþîØ­_m'šÅeëG­7KÜHN¨œûi¸ -ÿ©®:O'šAx#Ứ®rn‹Ø³Ñ a›QÒ6[ý¨ æêõ®S7mÿõhË¿þëçUßXÅ®3ŠL'¥T ¼1"ÛTŽòåR\V3”o2KÚj¿ ;³±:ÏDc+þŒ/|7nÇå-lš$ÒLD^ªšhá<ýp2Ùš&æZÆ®W¥;ÐôÓ¡–ŸŽ]ûuåþ&Ó¨Õ¦jíl)2ª8Æó ›é'€3ŽÌµ‰ßnËŸ› ¶èĽ)n½Ilš©jYÚÁÚ£uo.ÞúåLý×ç¾-kûåXÃKvT[ªR ÃWªÖÄdé¨6˜$mÒŽÌ6Ÿ°Í@MsiB¡Ž*K—¾áP–<§hñw3Ç~Îg¨‹‰[ b !O-ÜÓê|þd+Èñê|q<žVŽ'¾+~“Q\!Mþ ˆSÀVþ“Ϧ«ÊeËNN”ßïÅ,y›Ø—O9Ì8®P?:ÏbÂ6šniJ…æI[5B׳¥óò–L6ó-·3°G¹r&úF¢oÇ,q“pH½◠fü¸»2l*øÀ% p³»qS/yLjh‘ª# #×ØÆ¦OZloÅ“#UOW>,ªy|©íMIý󲯗åͯÏ×>-m~uùú×Ûßž¬yxªîÙù?ªÿfþŽ “¨TÝðUB‹X=&qíþêççš_Ÿª½§î{4È4íÐ5ª ”MRfÉ[´¢²„E ®”*£µ¶âT…k ›Z1…šª<gË„«˜õKöoþédÛOk¿]{¸Õ0h‘Iä­•ºéFê,Î2MÚhœH⌈ï5Ÿø9C4ƒ0ƒ<¸4-Àh ÊÔÊb¬4FñbðŽ I;*AŒðžä’¸j Zg¡N›³©ôXË´“í?OÏ+2 ]ÌêÖéGg1 Þ¸‹›0Î$šI‚p\ŠÈM:ÂFƒ¦i»Êg憓 /O]}„HÏ^¹yÖ<+nù!÷x»eÄR³ˆƪTãØõzêL²~,'î0ñ*:Ÿd˜˜$Ð[G† [˜Â5@i‘,ÒÉßòÙ4q‹Œ¿?=žsÙÏÕºš|A覧bR _™‹‹ @ ,Ûb’´M±æöË 8êDfËJ±vDZMÜa™¼ÏìçÄä[ž…ËrŠ®º@[Í ݆Ñj*idó€ž\‰×zÑoƒË­Æ‰[n~àÈP (ÜìnÜÔIܬ›g2a£‘zƒIäªi¹g?¿p¯òÁ¯ZßT]ÿª¼ñIuû‹ó•w.Ô><_s¯òÚÛâúÇç뮼UÞü´¤áIIËË’öï7|=:>}”Ïœž‘ËCææŸiz}¡ùùù†ç¹§®ë.Ö ^¦ºR7|NxÆOÔ•JÜ‘hñ¹fñy‰Ð0:‹ß0q«^ÂV­è ªVñ¹¶êµ3²Ï«[rí§¢öŸ–l-7ðK1]nŽögƒB''ejEn@?‚ËŒð\Ê,n£eâfKôÊîÒì$¬Çx<¥-0P£øHÅ‘È"‰Ï“Й­T+³Ž7¡;ŸjýætÛ÷s üç…,Ñ[£¹N Xäà˰áÑ c7ë« b63ìC~óf Y±©6ª%+¿(.¿öº¼ùqyý½š–Ç—ëî^n|ZÖòö`å#¿™Æ! õB—D¥êE "Ãw´6ËÄ­6É;P–願NˆNDo4ßJÓWèFæÑ´Â²u"rGo`§EÒCu!̶q°8^‹ÆJ“f&âÊZáëùÌþÎÿpM4bÝÍã ™cpG¾•O×WoÔUåkEäèDì|`?z%(²œÅEäë 9Êäƒ9ÆÔü¸y¸¯ 8™Mj”ÙÞ‹¸ˆ0,^ AÈÌvˆÙ¦1…ºÑ¨Øb«pS‡"Îÿ§ù¦ hæI…ÉL­7ÙNÚB³›¼Ù~ʇiÛœfì3cÛØ™Û]fîp›ý…ûœ)»½æíòž¿Ûwá—~‹ö.Ù¼ì@Èòý¡+D®<¤Z}(&õHLê¡„´#Ik%¯?>)óä”ÌS³NÎÈ=33ï윂³)…çæo.^¸µtéöÒe;ÊV~^¶zgyúîK{*Öí­Ø°¿2û`eîáê‚c5_Ý|âê–“µÛÎÔí(jØu®éËâ–}¥-.´ºØvøRû±K×NTÜ8]yãlõ­óWo×Þ¾PwçbÃ½Š¦{•-ªZV·=¨½þ¸öÆÃÆ››o?m½û¼ýþËë^Þxøª»qSCc6yØÂÖê9kóÁº·§›„.y¦òneû‹²Ú{§.¶+mÚsº:kÇ©íG/~¢ú ­½_uíeéÕÛPàBû›cµ/ÆOZ7Üe¢iØb‹Ð…SÒ¾,m}u¹éñ…¦'ˆZÓk¦¦wŠNàíÀ¥:a« %ÒŽÌ) 0xÒŒ¢×S¬Od¸àUg¡ %nÇÔÉðKl’½zͺýµçš¾>Ûøú|ó7Q)…šn“4ÇO×ôž£°@7x¹~dWÆPÌÊlÂf¡=€Ä-B-"J':×H•e-’+ENL&L¤qk¹Db…¥IØ{ш1ÒJè܈úl§Z¾¿êñÙ–·E­_!»°]Ï):¾sx­Ð4ÈLÉì (¡hä•À ‰ÊWâži¡Zá?;ëØÕ‡çjï—×ßåGRt©¡¤¢¹¦ýiYÃóÒ–¯M¼&éùLÓò­º\_•Ž|,&lAM³JÚFç1S ³°j¯ ®¡Ó‘ºt ¢Ÿ8s([>[$lá[ýH¦ÑUat¼¨ó¹ ¢xÆ|«¤-@Mð1ÕÖ }\PR•ƒ>n¢ÎFJÈŠÙ‹ñTñ›!’DE«dh”2A¡8=VhÄ\ËJÝÆ¶Œa¶-;ùŠ2—Ù‰ñÙ8Qشʼnâ^âv4éÖf:¾IÂvãømBŒŠ¶Ó¹¬àフ€ÂÍîÆM ¤L` ¢Ò­b×.þâʹk?5½*m~Y}ómÚÆAñ)zö>šVžÃLÝ»·òé¥;v€¡«­§zï©+õ7_ž¯ºÎ¢´ý­Ç„5ƒÔzÞ3 }g,)»ÖaKw]¯¼õãéšGůœBSôÇOævÚ^3tRLÂV xÂ\‚”¦W٠Ȩ+ªÊ¡¡lZÄçã5#È'0Slrñ½BíØó1è”jó".è’´æló«ÓõO‹[_¨¸«ç«åš8ÒeÒHïy£WèF¬^9tºNnòU“&Ðä+« …æêµVªi{+JÚÞ”5=9Ws£äêõ‹3ÊÄt«´îaåoCgrŽá>E;hïMV?2‹>ë…oÐ ]‡¦Œ¥DZˆ^VeB4žÅ<¡ÀW&ÖiÉþ,“£–¡šÎ~ˤBýˆõì‘ øÇãÍxïÒ¹\s¹šuÒ&ë¤BÓè H_HFÔŒ‹Í—µTÈõœÈƒ‹w—þr˜ ccgîÁË’›vÄ:¾êR“9‹¤9‰T¹‘¯Ä@Xx ÈÙ&!‡ûBOƒHæhtRô\…›,,”ï’€ÂÍîÆM¬£¨]¦ª4ç‰Ù‡ê¿:Ûòuiëëò–[_´pдönéÙÏÐ¥·ËkÿuÆ6ÐÌs Á˜é˲Š*ZPªo¼8ßødtØÜ¶á£\’ô='å½ZÚø´¼îNYýññKŽ`¯á:i”ç,퀅z!+ ÂS±¬ÊzŸ‰*\ìÓ ^&öċРZ¸öðK2>Çd8Ç®:xùþ•ë_Ÿ«y€³ÕÔ-aÄèÈþVán“t}f/4 _Nn©Qt†ÈInSBeDìŠ:­Š†%aÙ\A@ä`šLOz‚Pè䡞Ÿ+ / š‡Ï/@w>uõ!é‚C—GØpŠì;Ü}æHßÅÚaé’>%sSÀ…s O2°Y”öUeeBŽýæl:Vÿú|Ë›ò–g•ן/ËùÜÒ=¤êæóÒ†{hô—š_„MKío8xLüpÏ™zaxl3“õ„­è_@Ó>y3åá‹U’Äš¤|¹ÃhÍÌPÕaý—ã¯øÌàÖЖ:V‰…pÓ0 ]2DBRÀ UÙ)È›ÇùÝ4ä#½ ñRŒ£Ö ;y²RÄeŠåL˜öpk—\F'|Ć ”=ü‰H™˜Ñ,“7 Œ&ä™$¡”c³Î8j­üê-b2ø2;"Fšþ}ôV³Ä-8@»†åƒ"V 7»7‰Sµˆ‰'±«ö—Üü…pÙ²¶7ǯܟ»n¨‰ÛP3Ï¡V¾àòCOMü>1öýØÀãcmçþ†Îs×\i¾©öÖ•<ž4"ûZ¢rxM>\õ¨¬õ†ë’º;ÚNý-CXG wNÂ`«ã‡Mu™ubzŸ¤×d3ZÒˆãµMX{Ô>)›ÔQˆ#‚*E ŠðxÒ½ˆy…ç_]hx\Rsg_Qã@#÷Þ†^âŽÒ?Ñ,t‘uô* (­1™BåIeu JÂGBßÌ·aôŽÉ°_oGÀÏZˆf·Á"n=´…5ÜH牚àÏ2~Ãô ˜ç_ž­{XÚò|Qöæ ƒ­ƒØE u™2Â{¾N(ÔÆRšl ÅÀ«#{ ðá ïj]bÖŠÝ5§[¾+iûêÌÕGgjïÙùűp.jyx¡íiyýƒÒšûs×îüLß­Ÿ]ä—dM Ú¡kPôp2j‡¤Û&n2ÊÔ ^ã4y“nè ÝUvòa(бN`&§Õ>¹•Æ·6‰¹ìäƒÃ¤B­Àå ÐnÂ&K<ÈQ¬ ˜9臭Ħр8Ï ]^M2)_Ñ ³uÜz˘Tòe,ã3¬®Žßàô" ô%Ÿ(‹¨iÌÈ”¼°˜‡p°ã[‘(Ÿ!´u¹òC"Ú.!ÐëùSÞB^4hLÍØŸµ#ÓyR–¯;V$æp;qGušeÜZ«8v®3ˆâ° 4VaV¬”Š>x (ÜìnÜ´To°‹Û`±2u_íé†7§jž7>/o{3ÒÊw©GO׿iMÏ!cbÿ¢ç=Ð1¶§eˆÆ¸ØAæ^FcB½£¦öR»«fYú$¬ïþ™±oë`# ç[^]hÆswÿï@3ï~½MØ„i¸N4 Z0.y}BƱô£×¶^zI~ÇÑ–Ÿµý|¼õï_V¿È;}-¥°ØwÖƒ0‚o3q„¡6b™sQ 6_ºöõņ‡—ïì:3ÄØu\øœ´]å»/Ý;Þðæüõ5~•y¼mBÆñ1“óßÕU­gÈÅZ‹Q/<Ã:>;|ÙáÛª ŠîîºòêpÓß¶ÿ×áÖŸ÷5|¿¹üéº×£V7m“\I¨NÑÂËÉ0¾|ëÙ‹­Ï‹ë攼8¿¡k?3¿~6ƒ“Gx¥è„¥â¤ãWÝÑDDn®fÄZ¦ÀBp*"ÝsÚÆ#ß©yVví«ÓµÖl;<ÂfüÇÚV»JšÎÔß¿Òö¼¸úö†ÏÏôÐ×ß.tà˜D€EÚa©è‰†ë&¬Úßžsú~æ‰[¹ç¸LÎõœ¹qéÞÚ/ªÞ¹ö_GÚ~Y¸³fLri,–1é†aËí“2—hÉ:{oýé›ÙgïŒMÞà9sÓ²/¿¨üæhÛ¯GZ]¸³Þ19ŸúfÑëhÆX­£2.mù69õ°YÄ´,~X ­UiK7ªT×þ¬ºéî¬E«ó¿¬¸öÕ™º'0úòÍoO^¹-,Ã-/02§}y™TBzDxOÂF­pQ²`þ–²cÕOŠ›_¯y\qÿ׃W_nx{ªõ»â›?¾rKìéúç[Î_wœ°Žø(‰¹bq4j|^ÔR|õþ•ö7% ÏÜ£Súšxõ2ñíe>dÜäQ>óÑõÈU–I²iDµÞ<ȵŽs¾Ð OC°vê,Ûð´5;kε|‹sóTíÝ ×ž˜z„ôÑ·í¡e›³§¬¸þ)‚b‚q¨´ñS ÛÞÆÞ˜jGù/Ô Y L=C«°e×,i~SÜôzWéik÷žtªé.W¶N5¾Ùzî¦uÈ|ÛÈ¥º^3‰b:{ã—Í_i~³ÿò)«·•´¼*jxv¾éB&ýöDãÛMç-6 ^l¹šÊªe{ŽÔ}s²þ«’Öޜ®Y|íÛCWŸnûöDÓ¢Î6¾âkvˆ^}¤öí¹k?ŸhùùHó/ ¾4Yi©J÷š^°õÜõ’ß©yrºõmѵoh§›^Ÿn~Y~ëûC•÷K®ÿP|ë§uGyƒdÃ_a –"ǰ÷òc©y¾rÕ…MDäð*xý`±òoÿà 7»71ÍÙ'æ¸&gžlxCnfYãcÚ²œ}} \þª1¶—iP/‹°þ6jÏÈkʘ˜e”r:Pý¤¨åõ™ºÇ‡/´^lyr¥ýé…Æû—šŸ 5Ÿkx\~㻽WÚD,²m²Ð16õÐÕ7¼ú’æ¯x/ŵ_¼¹Hò³M‡«\À›\ÿ”„£+×_¯ÚzÆ6|¾aðRƒ°Uf«öV¿Æl÷91véQiZ!«ñub"6Y4=c?s0¾:Óômæ±VëÈUfáËMƒ¥~YÇÏ7>ºró[æ*»KÚòU¹²ýT-ÖðŠ›èãé<ÖïÙ†A HŒÂžMz‚NÑHgS¸ùÁ›.ÿíYùþ*ÜìnÜD›”±x'ƒgY Ù…OKëîGL^6ÐÈMø1-ƒz›÷µ5÷Ÿi8ÇÄgšUøbó~ÓF‡ÌÑ@G¹MýÔ*ºïè„!Ž1p3mÓa2PJ¯Þ½Øú:túÚÆp³¿¹¯¦CȲëÄŽ2ØV´}ð­Å9‡C&§ºDΞ¼2yIvþ¾s57_ nÖ>¼tóëˆå£¼è…¦ê-³ŠX¾çÂÝK-//ÕÞ)½ÒB¦Ï‘²†9é_Œ‹JñIZ9smúæ#—Éz ¯ê}¦®×ð§¶J_M¾ÉÚE;«NÔ¿ª¸ö†øÞ=E Α³u]â´\b=“-<ãówŸ=Wy½¤ö^qó ò•4ýæð_¤™j³ÖuR&y¬pó|̓3Õ>Õp`æ4úÙª Ö X,Fu©Ì ø‘ÇRÎtÒvmU©æx6£ÒVî¨,kÿéLí‹¢†§EÍ}Ô}MÆö3tî£ï¾ôú7à?#ËÐo¶Žç4²´Ž7~{®í»sÍo_¼5-ÕÌMmä¦Ö§Ò·0—ëptäBë¾Òf3µ}S´SQ*÷xkQÝSÞȹúg³6ÒóOÁÛ«´Ô t™£jÙ¶³-„d£Ýf·|—Öøi&Á œâÖµOŸÏ5<0Ê>l¤}Ä›PÆÏþf>=õ\.ßx[Ùüøb݃³µÏ,f rœ0ÔuºŽoŠ÷´œ“µÏ«o}ƒ¡µ¤ú†£Oì_‡š¦7î?u\z›² îoäV¸¿µ]l~1k=#9Z°nÐґ޳ё Ç'Ùû%ÛyÅ:‡é9G>Ð:DÛ9†tÔé‹7T·=¹Ôx_Dþ¬ÙÝtì@—ÉÃ}¬¸`âˆñ3Höщ9 7ßH•Ï”nv7nÚ&f™„-;Pûælót‡¢ê»Ž•÷Ѳê¡eZ4Ì!b”sLÆš“-ßžiþ³Û‰ú× ðy­¹‡ñDã×Xü }géŸà¿ˆ  ôM¶ÛOÕõ1?ÀÜ·‡¾‹Þ˜p<›è+©{\ÚðÂ5b®îØèÁÖ!ézô4öëgÌÞÃÀãBûۊƇŕ7ªo}×ß< ‡yøÀ1I#Ý&'§î¿xóÇ£ož­¼¹çLå0c7–þïáöŸyýEÛs Mä0ë û/ •ÔÜÂiã;á}/B˜4¡TBiü“†®Ñ¨½ Ç ±ò.órBÉõû®¬ßW9À‚ù‰?ó£U‰•BÑã,ÓqaäÞö2td@îLJÎÑÌÃ5û9þ ü_#ôÆÅI‘¹÷dC·DøžñËœ#ç¢oÁ¥éŽ?šë ‰Wß\´awcϾ6Ñ}ìã @ ž•‘VŒjzðåùúþVþýlC9ª¬Cg(¿~±íéåÖǧ«o¢ sûÔˆw¼³¤õtí£S5÷è^ú¶Ó1)B¦¹¨æ:…Lfî¥eã«k ëÒ×Ȉåö‘}l£û:$Žô[ø;nŠ,) •³sðT₺D¡|ø7”€ÂÍîÆMò朒ÖEÂ&Kê–ÔÜœ½*·¯¶í§:Ž=M<ÉŽÔòœ¬^µG®U8#ë(ÃïüLjÝÄãv¼¼]8ÝÊîhŸ¤ï1Eß-!.%l‘XÁ¥Öí<4Eª¾«•wâÙÚÇèC<\vCÃ6¸§¾GOC¯>æ!˜=)_€gà1IT¨¿WvõÖ骻åÔÃ8 u$nÓåÛKDy½º' w¿‹™¹ê 0ņü‘¶ËG†ÞŸ˜„ô³UAÀ]çš±gž¯ºQÙþÊÜ5šìΞ¦þì¢u<¦:©–,ß\´¯ì†b:|¬úAÉ­¿­{•y Ê3vÁÖýç/Ö^¯n~PÞøÈÐ=ö“ A±ZãâvžoCoºÔòèBÓ#{ÿ$´°~–½-C‡Œ›¨å¿ˆ`ZªÒ‰µ³)(G½ i)j­ð|°lJÙgj+¾öúòÍg—[€E— ‰ýu]ûêyõ1ðícâ«í3Ä1`GIÝ…–ÇÆ)¾w¨¸á3-´à>ö±VÒ\迪¨éEiããŠöç'¯ÜDŒ-üú{|¬éô‘ÖXÍ1ÑCí#˜ µúËð±p7‹µW&ÇhDÁ ,íÿ¡íF96vÈè(Òo9ë/#uœ£‡Y£î/ßtrǹ¶Òk?`¨/jý¦¸õ-f^”s6LY¾‰Ýpó5^Ñ3R{ŽÿÔ4¸·]Ì(×ÉÎÑ‹y•®ÞZ5·Zð*ýØ'­ÜB\4мÜú(mÓÁá6þPûSCáö¡KòBLⲨ’~ùæ7<Ý)"¢›¨cüŒ(¯ýÅÍËó]0Ì.„„¦ac9OBõÖ ^…¾i)Ç)Üìœ-üBy´?•€ÂÍnÇÍÈåA)›Ïµ}K #t XìŒAFN½ ]ú[õ° 3^@ :©~À<]¯é:ãb Æ„cÎp•­hCÏÐzÛ‡K°MJÆ.°…ñüÕ‡óÖïûLϵ¯‘ç` ßñªÆFÂ5 Ý~¬šÀÔ¨“FÞõ·U¬;Ô)6pfÞñÚgœKÕÖ¬ÏÏ‚ •d8ÂQMàëÙºç˜yÏTÝvŸJné§Úc?ÒuïiÜÏ>FÓ}2ê*åÏ]¥öŽðöÖÍM{yiKpM\½çâ}ì™Ôs dˆFåñº—gÛ¿¥Þ,‘6@áRÝ Õ×Ñû(kÐÛÔïo^ãâM½'Ä‚cOÃÎÅ wˆS<ÎS¼®úJÕn äºFq›)Nk·Jó?/ºþýéº[Å í—šo¬+Ü=ÊdÜgÃí†ú 4 àÙ5ÇFØ„MJÝu¼¸á>*žYîþ™Æè¡¶¡}íÔ^³CùÏÊl~|¾æNþÞb0÷ŸšÎýL½{¸c’¥:Šv?›¨ÖQý,°uûz%,'Jk3ÐÜvôòPs·Oµz™ù}dèO©Cf¢ÙDH‹¾9ÀÔGÇ1¯å¹Æç¬kƒ=C=!,§¨ååù¶¯Qu‰­Å@]\ue|\ðD éèÚCÆ& ;‚º¸›/7<À5Œ>>Ô›9’ÖXÕ¦ã5[ža-GñJ\€eh’À;Ø*À;vÚ+6 ì´,©ƒ»–Ò…¤ç`»€§tûLõ~N„©ä£wÃM´ì¿XáæŸŽ¥ÊÎG 7»7âRYãƒõ4EÆi ÷ÇøÇôÔ¶ûÌÀ¥—Uð@ç #|æÛIÂÅj´}fÃM·ÈÙøKkn6Ü"äîäUÛûZøc*Ô«ÊÛS‚ZÑú “,‘®ýMѼ†XúÅÏËÆ±…eý+óÒƒÑo€U0y.S{üŸéI«÷2zãeÈ]š½÷SMò{pºM9žälÈ 5qï£?î?F:|¬7ž ~¸¡¹KÜrÆä«7ßlÚWÜGËŽcPuAùÎÒÛG¯>/»öê ­Øxhòª­³6œºnfg,‡øï*nU5ßýüpÙ7Ôyd6Ä>ÌM½€èšòæ‡%õwwž®ìo4³s‹€þvÑ d<§”+5ë(FÄjY,1Éâ Tó³£ÌQЊ­çTÖBD͵’ªÚSeU[öî)߸§*wOUöþ+ùG+·ž­ÞU*ôMá?m¸‡ÖiíGÂN›ðQ"urÆ¢p±ñ˜´8óK$ÖËÈkö@Ë Nq 2¹Ã'qHl5Ü6|êÊí¤ß^nIõø™;»úO ½‰bì”4Ôe’hΨé4·²e@îþ‹%M/Ä{¿öj&E$ˆ§š¸jNIrE/ßý;âb*RGÅÊkZÖ^åÐßV˜O¹¯žkóŒêÿBÝ}çÐéÔm°šAzѹjáà>r¾Iߥ©vø>fƒ-ƒLœ#}Ô³WäØUTï›<ÊæÙÆç¢ÎF;)-åâä§ä®$pkؘ8~oZ¢z’O+‚i•<”wÖiEßüP$ p³»q“¤’E[ΓØNök»”^½”0‡  ^F à4R¾†EL4ƒW ó]4Ìm$š¸(§þÖ벪kT±m†ÍÈ ZŸî¿cå"¡C=h{e胑Ñ~„mМŒ] @“´‹™«60&Ée XD­£ž¹mØBcŸë\=Õð3/§ûÇ/€›Ãlƒ)¤`4ì«(#'+nŒ0÷`ì®é>0W÷Ý¿gQËã’Ö‡xN/6܇>ÌXܧö7·é6‘<¬Ó(éÞ0‰›“ÑÛ_° b­á6EË{Ž–WЦç\ò€FMÒ»¼à8""ôÅÍ[ßÏÄ £!ö·ãÅôž;Ê'ârŸ2jl‚kÌ"¢^±šÒö·’›£ï?Ò!r¸]qS¨´ë÷–còåý–\icŽ4ÜØå­1¤èö²Uc§%]4ïË tRb7rêbŠg¡t0e/5TC! ª¿¢¶øÅÍd8®Ÿ=õÉ9PÏUe3fÎz†n;Éëç¥7>±?Y©iŸƒ-ž…B»“æ¥÷ÔÁÓ€ñ-â¼Ã½˜4?S$ ïk>tl2‹¡è¯„›¦TÓâ‚XÀeÓ<:Ó>zõ¦S-tøRû“¢ÊƆۯ4Þ¨i½V[ÞH¶æë’¦×˜"qêkzT„3·ñ!„›Ô|ˆŸ“N 3商K`µ²ç¯cÒD˜3:`w[*ýŽMÔö™K¨°~ð ½ åúþ‹ ¼g›ùLÙt¤ær«0¶ÃM§É„uõ1÷écÆb4b· eò{Ô X¤ï3{nöQ ×hèôØÅ»_^i~^TqËÒU=Â*¨ièßô{›…{Ä­ö‹]š´((i1)'¼B û»cg‚iS2Î]½CøÓñÊÛÖ"4ÜI=l\2zýõM…› J>( (ÜìnÜ4 ^2+ÿ<±²G*îàJ«»ùrûÁb/Õt¸†õu¸Ëã°Å–ªÕ¬6b¾Ô>z®FœS„ßT5 -)sûñFÚ‘GIÐÈØ°„I€x´éàE<Œ õ-1´n;ÝJjnËÂäàªa=̆¤N¿‘Ž¡¦ÞÉ.ê…³ÖïeÏž‘µ‘¹¯ŒåWú›ŒÇ;Æ–ü taˆ)"Žê:޲ô`è¦ånàG킸%[0ä–Q±­öñ±Š;¸_&¦`Hwâê“sͯQ¬Èš1>ÜlüH‹ñƒL\Qcé.¹‡¯ÐáÚ¶Ç<θ ‰›>TÒÃU—wàL9_{·êÆ+²>8Ù¾Ö‘8ݨ°‡s“JAòºcÔ b½„L"RÃS6RÇ’8e÷+Ûì>šœ²jÚ¢uf&/Ìž°¤`òŠÍ3RwðP³Ò¶ÍZ½IÖ7+°7Ü#-Œ»˲2 #ÂW2ÇŒ°ò¢òÒ§&ø(‡›¤ã·H?tµAxº!µpÃV›-t[pèâãÚðàlÍ]¼±x¨‰þÅ¡9Ìy"¥ôBW‹åÉ¢2 ÃW‡,Y»¿°dxHê(Z<: º”´â9ÐÂgzÚN&!Ì‹6žº`moMÛ>†÷wš8Òc¶Iàü„Å›¨àDÒÐå†GÓ—m ›¸ e³¼éeEÓ³59ûèŽhêßË4¬ŸMœ–ÛÔ£5¯È«­h%Ìé©Sð¤‘6þ¼~cd³1õhì¼4k7ÜÄÈÌTa²Í"¤Ö6\cüT ¿Erþ&ud;-qA 7?(j(«p³»qÓ&.“jáª_Q›Cٙ˭WZ süòÍÏ‹šòŽÖe®Í9Ú¸ùl;•É/Ýþ¥ìÆ÷¤Û3˜ ÓbÃ=ψ©Œ·„wbÙ‹_CÜ"2gÁºä†ô6öÁ扯Š1øDíÓÄÙ6>8\Þ4qá 3—AãµÇ„z%-™½n7×ÄJNqûWás7ô„Ú&ŒáäD¤m?ÍXJ!#ܦԮY´þ ŸÄXùl¬}’c÷^¸‰²‰‡ËlÍÖSØZ1ä‚9i(+'…¶ë“2ÂÌ­¿®Ã(‘. 7£Rr*nQNá®´àø#]¯^:ãH! nî-mÇì‰Í­õC™ÀM •h[ 欒 ƒXxh’ŠBrƒÈõ¶±9G*ËZŸ\jI—dÍØ{«‡9§ô·6Õ°ýHË¡‡¾s3!–>$-޲ô"`•uÏé@iÍõç¯$²TÆÄX…̾pó;æ!tïÄåvêò1ùØ8°Ÿ],9­ZËtÃÖŠ5:Yñ9rEØJï é”,obm²JšHŠìm2¾‡‰7±C#¤è_Jæ²,)K‚˜j¹&÷̵Òë߉Z…M“çe˜:‡öÑs"}’·I-Û ©ÌaHÞä[jFN˜'\ÆÆž‡ v›É´AÞÉ©”„byTÎ ÛOS¢¢í-5Ï\¾fï=Ìij‘ˆvà4kñ¿cF@ §«nž­í 2€P6û»åeã‹Pí™UÝüš ¶Ä•L¾Ï(ß…ÄQ÷@ᦂV 7»7©O‹–´»ò55 Ð/ên¾*¯»%ÔŸæ'¨8›(4Gý=’ÜQgUˆˆŽÚ[o1Òì<1Ìhì_‡Y£l2¨.ÌÞÏ8Ù“sñÇI‹Á“èͶ¡ªùyWîý@aU2ûjîàã»}ùÆË’Ö§Ú_¬¡ÀݳÊÛßÍB¶~æÁ˺îq¬ú˜zô5qÇ~¸åؾ"äÌ•Äü4Üý†µž)´Ž®DI"zˆç”¤Eª»ï>ß2:x:9þ,ã2ÈÜ$ ,~WnÏ$¶*m^–ýå¬UÓWÎY¿wû¹k*îSÌ–°œ’êkg.µ Ôw ½…H$â‚Ìü§'γéYEÛëÃ¥mÃÌ)à+B\¬U#ÆÏÂé7E2~\Ž^ô–+JG¯[þå…[oÏÕÝBÙd2cå–Á†î}ô\ñ ~¤3æoºc?6tîiêÖßÂs°•ßpKÿ‘V~›•ÃÍËw‹«Úù¼ˆüêé7-£ìÆ·ÂÛËú/_Q—È'fÁý‡ºÏÑ \©‘ÉÊÎøUYÌ2rMܲÔ~GD1ÏÞua¤%˜v(™Ü¦`ÔEÙd)mÝXÊ2äQEvÑç•…›Ø)¼°vó¹i[©í0}Ͷœ—PÒ\ºEÞ¦{2tìÜBûéŒÆzð‰EÔP¹°LÇoÞ舅Ŭ÷×íá²~<È Ëí–}çêŽf:…>5£v±[Ô“ÇJOàYÕÍ·G.ßÈÚS†Ò=gý—)Y{Ön?Sqí3®º»_³ZúÊüƒØo)¸ñVq;ÕHŸ 7?X^(.K@áfwã¦X˜2zCÀÜí;Jîdz팓W(g'bk0*Ò®Ô?¨¬¿_]{«¢þ•Øq-ocx4íóÑÓÏ´¦lódЖκï6…DLŽps¸“Šz¯Ü)kŽGïtõõ -¤çß:[w·½Œ8¢æ§`ñlÕõã—¯[OÿOƒñ½Ì|{›yãc5uW—4?£ÆNFŠŸ«§,¡žÚ.ÞiÁ=*¸Á‚‘6aa.àaJR~!¢a–D'»$ç cèLîH¨JnácUk#äÀ).Ý!ætÍ#\lh©@võŽóîñË#ææâÅ0H6 ^×¾ãþªá4ØEįÜÉR\ÂØð|IÎa´WlÎÔg 6ncƒv‰ÁÓ41ŸÒñæ6¢xºÏß{ôö¯Ç›Ÿiº_ÜöjZê½õ½>ÒßÛ$ä3#ÿM|{Xô6hLÔ1ª¡NêñŽ1ê9YèÚçrÛ3äfë—L \ª¬ÚYvºñQÄ<©âÊðP¤v;y¸×­ÐtÁÍØB¸‰_Õ\±jO-UÓ)^ƒØâ Œ´=M}ˆìbé7"oYÏ‹.ÇëÇo†›¢çá+}¦go’äB’WÜB{ÿ).ê¶!”’£^_O“@ªó‘c8`t<ñ3¬ÄAr¨®û$ò&,g²þ§‰gÒ(‡p¦T‚®["«¢ ·§¦ÍgFž,Ïý‰±ooë0î«ÅYÞSu='îËãô±]²V±\5‚Ò YÎú˜b±˜,ø£xV„ÌÓ‹ÉeIø ôQ2}€]To³ÐO ¹ràg¦Á})PàC€è÷)˜.‡yÌ:~6®Rޤë « Üš6ÿ¦ãûY…PÞ‡$j‘B¢«P6-Bx®Á®³†û.×X'Šû©óõ£Åª”¦±YÆ‘©Æ¡Ku}f‘öH& Âïg4`´ gèŸE:¡éò"¡úê|a§Í1‹É °VÓ}ª‘÷tLjùc#XúL¢Æþ ÛŠ÷’¤e”cTDØbOÊ£EåCaÞeÆ‘k ÃVR—ØÌ3~Û±‹„ôÔÜzƒ½7bò’úN>êaà.jÌ:{ÕcÍg*¦’Br„È0rýLo,å¾iT&g ¥®Î'¦=mT}ㇹOé9}”× *»rŠëkÑÛ:¤·(0;Äu’–ïl½À Ïé°£mĀёÕó ö1¢x²0ZÀ‰Ëoˆ(ÄZZ, ®AY*爵«üæÐ†ºLdÿÇF~Ÿx ¥ S§U€¦£jˆUp_S¿Þ¦=ÌBDÁ×Ë.Ah…~ ôS~¤ë”Añ”죾z¨:ËšØE`Ãì@ 0ý©YàǦAŸš‡ô¶S‹ò³^3DÒ½÷Lô5jîÙÒÓ*’s¬ú¡°@/l…±šŠî cYy3×PZR'6Ÿ•²õÂVjûÎç°¾£‘d‰,ù-¼¢ã&j¸O#Z¡®’;*h% o‰»ŒŸ©éUì(ñ'žÝ^Í}G¹N¤ñ1´ÌL /×ê1o¤?vKÖagÐ)J í¤D¢q³ƒÇM¯TÙÅBNÏqG÷_¡-8Ëú•d͈ĸ©¹kóˆñsŽI`Ãâh}Œý?3ôÙ¨ÛC£IÁ7ÊEÄ1ñ£Z`fÅÛ4N5 _e¬J£–”ÏÄÕ—®¿‘‹Ï³Hè`C§ÿhòŸ#G‹ØcŽw% Vä1 ±.% ˆ ,€NýæT˜†Œê6Ð.h°fË›R ƒ¸åác&Œ?[#p5‹¨Šp&yù0©8í‡3`*OªH (ÜìvÜŒÉÕSgéªÖk‡¯Öô_þ@ q h =ôÝäõ;0’ŒÉT3b6„VhUÆ& qŸ¦á;WÓ/…:)ßJq®Óß)–}¤Ûë-6 ^¬ë?¸@‰¥|Œ@ƒëa@º"÷ââ4´T$…5Ò1žâ6šÞsXQ'pUBú;Mèe¥êa þúZ¢]²¬§?Õ( [‹èƽ팛†6‡ñYÄÃ,ÂPÉBh  ‰›ÒsT§ö**ßtˆîo/4Á^Ö=¬£ðÇñ8£<¦ƒizË–vž°zÛÇSq}¸'FÚ¥TO2‰!Â3óÜd½Hî+÷–è}mc¸/”ºÞ³õüç…,ÃÂɪ` Û£~db1ðŸ«é1àÁ;ë(ùñ©f/²0ÆO¥:=_‰UZã©×Šò¨´F?Šõ(YÄ9Oæ¦q\§ Xâ>£¿cB?ûèAŽj0'ꢯÑÚ Ù9ó;¹™Ë‚àtC7pÙ0·ýlc˜rÈ ­"ÖÎæ\„C9&Â[/ËHúÀ:b,'mŸÁŠÒ4ÕŠÌWà&Žiì´)i›ÿÒ߀ìÚ¾Fã¡-–W[èôŒW°>ÔEÌa¸?j2 “b‚ä@õ¿&0dÍ0À‰3öEé.6Ëà¾Ö¡ŒoŒÌ©ð6n2(ÑðŸíކEŽ+0Z¢hˆªn þ’ºz¢Oñ„ Åp+%J(ê$¥eQI¸¹h4l›0bÐØ 0µ|æé/cJýЕ¬<2Êwê0Þ= ÀÀ;Ø1Ž$‡9FS<‡³Ú ÔÒ² I” “S ÂV‘«HHŒ@§Ï\ò.9&ö·‹¡PÞ@ÇX ËXM©¶:îKî›Æ$;qèøé<]eýM¶(¼  ËTš`1‰a+ ¦5‰Íš±èA,¸™uÕy:ÄÞ„§i,ê1gë4z˹¯†ç,4_ Iˆ©*°<ŒNÙ‚QЉNMI¸ÜÏ1‰­¨gN)?ï94Y˜È‘"j"i1ºŠô“Ø\þ7ÁA¼~pK&oP;hk© q™†àŽð˜ÆðµÜKrЉ«ì(ä ¬­´p¤û,r<©×7htÌûX^bªåØ$ÔC¹$4]¿yšRá\æ!‘+Ç&gMX{˜ dæ’(D±Mk/2n(ôô‰Î8øÛaÓ–ÄÅêáÒí–<Øy/q€S¢4Pó:À´hÒ­ÇLæ<™.éø,¤ªš5Êf—mVæ&ÛjØTö—€ÂÍîÆMÃøBÆ|áüRm0ŒHÃs§å¿€á •‡šll±[Ê£([jzƒöÃ, Cû.Ð^&PžJC³àOàBE / »Í× \‚®jCÞ= V‚3À%¬÷&`e™E®‰“Æè=Âmæ¯-¿Åà’þ°04©ý$ø£Çq)¢Dð ·àøi¤WÐDV—‰¸21 ^¸³äô`Á‰d[°E7äR(žšÞó‡»Ïä\Ì¿0-@¤€’Õ6òPÏYÃAfØåúá+iØcµó8Àh¸Ï<€¨º h ?f'7%tÊÜÌvÚȵZ!+Y5r˜÷¼až)éƒç3}>šD¥™©×›Åe›Æ<¡@Á;ÓèuÀ¸S(ž#yvDÇÖ[a²â yú.@Ô M%Q#m7%ë¥ð®JF¨Í’£âø¹Lx•_Zô¹ãxÁMaöÖ]öë„¥b4æF¼¬.S;%#I&NH‰é Œæ1‡ÆS0A†LfìbÓC|¾hûåÜÓ·Š®ý|²þe (ŠžÎÚsý \iþXÇmàèd‹mÍš*ts~` “ßÏ‚’KC÷äÀ4‰;Š×ÁÛdÁMÙ¼)]¿%úÁ«Da™:•Mz®pó'ȇùø 7»!7 °d8êj¨BMÓY®á%Ï}g¶<~2xÂ;XT0ÆOÆs†\ÖÒ"#Ž : Šì ÆÆUBÍa@Z¡²º£(·*S'#@5–É,Ó&«Ý;EÛ{Ž?šøì¿%H?,Í(2ÃH•I¬/#'WÀØHŸVàr.+ÔàeÔ—Ãò)%zÅh,rý–Á¡y…¥³¥cð6©DL¬‚¾óÀ ècš†Œš‹q5Ò¨`¯ƒ†™i|Ð _+A! ]RÔÛ‘Âp)Êú¦ÌM(Ôvuᵺé<5Lz+:,UŇàtƒ|‹24;ñ-&1™ôMXnÃÖˆ©EÈ*¹ñ™=òN,œH’ý¢HW´T)¸´k á:À¥ t˜›Jô_%ÇýÊÇó¿Oæ&'ÇP´ÎðÒ¹2DF2zKx¼\ê1uÑô_ªá¿tTà 2Aè‰È¾ ]a“±áøõ½UoO·þt¬þ-u0ŽUÞ[”³Ÿú½Ü(º8«»u¨0{Ïå)xw¼DnD"‰èyÔ:ñ˜ÁËiÌ ˜`ƇïlÅÄÀo±NÀR½`‚ŽÒ£(›ÏÚaÒj› ¦ÂÍ®—®|øp$ p³»qÓ n“Àã Å JèòjäQÖ¤Á¼s$gTO-dM*ò–*´ ò.æDÆœ~Ä:½ð *ˆHHµP¯ÐÄèÍÁXÞÂ3´ƒ©{ÃhÌ ¬Ã×pGT ¡OIÄ”¯,G¿ˆ_Ž8= €T” åŒ± @Ð%ÑÏð4ÐI˜+w¤åïH®¨ŠÆý—ûŒp·…­”ƒUô"Vˈ”T‰@Ò˜t4†}ô2¹ÉW>Má[Äx˜£“Mc0ç¿°˜{ˆhÏœ‰:<#uhð7CfUÄeIg‰Ã(A@Ó-à{Ôœ†èhü)„ }ÕµG8%ÕYHR6…Æ*8´'n#—’: ¸yÁwŠ^ `I¯˜HÔØ4]ÒX¢s˜,ñh¢”PX*’1ŠH£æ­AÈJCa=H§$‘o*baE\„:±DíšG®ÞRúøTûÏ—üz¼îÕÞKwçfí7¦T…©G?CQá•@YöÍ3S¦ ñóãa1óöÙ/~<i¼5még ‚%Ÿ©BæèLãèluž±Z2‰güCûpÆLåI (ÜìnÜÔ‹aÔÝd/šN·,ÆgVdf+sì·ÒN=U¶G‰Î’ÖÏš Ú÷hž<Ëü•vfSQ‘™=|+ÒâúÒñœHEtö@°°F+„Vb˜í0Õ¹äMˆÔ‰˜Ñø3š2q’[0:‹JG445¡¬1>Geª’¯ÎÖg¹0Â8a£@ãv¬` ƒ?J(X!V†‹LIX#4/ü•1XE:·bÀdäÖ"ï^X8iâšÐü7ó¸»è€ô \XP-g Ò ghM”î!•R p•ž7GÞEó˜Y]{Dç…$™0ˆ¹‡ &™/ÂÌÛU@pSnâú<7îâ‘Eèl7ù™ðFxYl…l‘'áµñùÌ.˜@y¦.L™xÅüä7ËëãHjôY$X'ä™G¦ÎÈ+I),žœ~0tV¶eÐ ­±jUXµü QW–Ê~®“P…MÛ¬:‡&f"úWÔ˜•¯™Wðëú±Ió&òÛ¥DëœHs’we¿Ò | P¸Ùݸ)é››dê‰áT2åuŒÏRä {º¾•èüV2úIÊ‹|‘ß\J8Ñ„^CûÍ~iÏûw‘¯ÆŒ²·ûùï_áç 4Ð:d9[¡“#L¬¸²|ŒàÎo ƒ9%À(^ÒãämÇÕ„àxœŽ«ý·ƒöû“–Ÿ¨ã¹Ø#°I/N4žK¾¦•üÔÿãöÝ#tôV^Në7¥‹Ë}ÏÂCu¾,ù¥¯:^Š8«ë‰:$#½…w/¢óÝÑOA[^:Û":ÃZ•F• S¿9úã'² é*Tô¥*©@,×2tLîQŒíRĈŸôËA2Âé:Eù HàÔÿñeÇŠlC3O*´H.´œ¸ÉvÒšÝäÍöS¶8LÛæ4cǘÛÆÎÜî2s‡Ûì/ÜçìôHÙí5o—÷üݾ ¿ô[´'pɾàeB–ï]q rå!ÕêC1©GbR%¤IZ{,yýñI™'§dž˜šurFî™™ygçœM)<7sñ­¥K·—.ÛQ¶òó²Õ;ËÓw_ÊØS±noņý•Ù+sW«ÙxüêæW)œ²íLÝŽ¢†]çš¾,nÙWÚràBÛ¡‹m‡/µS1ìDÅÓ•7ÎVßbÍJÊŠ^¨»s±á^EӽʖU­«ÛÔ^\{ãaãÍÇÍ·Ÿ¶Þ}Þ~ÿåõ/o<|Õ͹ùÞ@úþ+FøwðcûŸûîipæj]'òA\¼³I×ù]ë:àwçþæÊ¼ä>H[ ‚w´.ru}øíÿ5”?kò¹2‰ÞßþŽJÿ]7TúdåoehÊ܇uÊäŸú ®ÙÑP.µÿ©‡ÈGšü÷Üäîïõ³“³²4ÄK׋Ûòÿ´wå_M$ßþÿx?~ßyïœ÷¾3ã¬**îãúuÜÇ„°É&** ‚‚"!a a1ÙYDY•EVÙ—,ß[UÝ!èÌÁ7¼™Ê¹§NuuuÕ­O'õÉ­®{«ŠLc° —Ÿ „°~&‡½¾ßãúÕNÇÿÚlû`aSô¬< +ÿØ(Ò„§–KùÁb5Z+ÀkÈ!¼I =κõø Á}ßtÌÎW’æ)+à§Ay³¥{°­{¨ãýð»¾áÎþ‘žQ¾cýÃã#ƒ£“Æ&G&¦G'g@&§f¦¦U333*•J­VkØv>sÎÕd2ÇS7ƒ1ö3ï!BäDW‡eIîÔï$Ì ¼–uíbbeÈ~Ds2ˆþU|6üôülÄx\Ãÿ ë÷8ûÚ…8kLs]_º{ŠÿápôÄ×SW‡w+¡kxòyÝzòü/¢ÞB®4à´ïì»§Ø‹œ€6"7U Mû™Ð³]ð?K8²,³ãß>< û^"ƒïч˜¬™Iy!O…"ÀG€òæb³7ùó'ÉëÏí'´ yÝäl'gg×AmÂ<À¥˜ ;åJ š…9:%霽óydÎ \!¿æ§ç¹Ë™ £ª‘ÉÍ@ÿÙ—³æ‘±KH¹1ÍuWq`B†#MÈð‰ü:¬JœqgÄr‡íOìÖ,ØÙ ›ÁCç?: A š8õüÏϧ9¡ÓàO Ÿ,Å hÅÕvg!›–Á?Ê›Dd•[êÙÂzúÁŸÖ¡üõ ¼¹èxÓ`þáfQ.ƒæXÌ}\ Éè}?Ù:z…Œ½&1dÐªì¬ ;$s83““ÊÄZaS_ŒÅ°qv:ö1¬³°%|ۊײވxåóáCþ?›õÌbŸ9ZàÿEá!ƒï³<®w©ÏXî½³ètÆ{¢Ìq€©#ÈÝæ¿÷z D{/ƒGÏ/ EøA¯_Ák¼ oš¡JßZ†|m‰Ø?GF êC#Ã4ÐÁP+ZBø‹#¿ºN»¸Öi ¦G–Y8>ÒûNòÎꕙ߉a(F*s NÁ³4û,{¨×’y•M9=?É6aõ×5ò1e8­þ`†èÉë……4ÎÚhšÂlòy“E•ãM¢- ‹DÂ×CG ùòDðÇÿ÷òaùêˆ/X¿´5ü€`C2Þ0¼ä¼~?×{Ó m+‚0ø_ B>79þ ò<Ìi ÿÇPÞ\lö¦ÁìmøíÒÌ™†óê\´¨£<عJö»2»^ÉFV–L1Y¾àú…C4K£÷Œ9Ef]V›º9Ö¸>\F3†×ê—0fg"133gÙ±öõ\ãBêZðÀñˆ˜ñ’QÏ“ÂÀÁÌ\"ÞDB }éZÆ{’‰†H=üø’Y•Åÿ‘ôk’›«[€ÿŒ%‹vcŸtPPAtG,ÑÊ%á_Z„}aþDï•“(x¼‚›*¸i"OÒS@š Þa‘Ö¶ãBD_Ëào‘u2cçëCÆÂÜ_Æ&5z³(-Rþ&PÞ\l¼If*Ha²‚/!ÌŠú)š<ù²ª‰/áÎ’i9äNá>¨²^¿x®æfT–S˜ –â)/ôÍ6<9êäTÒeôz™=FîB»î’¹òsÌá:ÄÈJ5Û‚aø7NÚG#"È@†Œ÷ÓR›ÄêÄ·`ö] íëôdï"Yv’1#s ê0+À gqzb·Vðo%Ämþ•eø?-„Àž`{"Qì.ŠRâÙ Ñ“,…°NK|T‘#­Eèräƒ^»†ï “â{ª§t¡'s|'™û¥CžÖ¡üu ?[ì¶Ô Çr±aüPLí#AV;ˆ×8r~(c~(û?ù¯ä£ÞrðC9‰üPÌé‡r64üP.Ed¹‹³=¢r¼¢ó¼cry~(ÅØ¥û¡<Ã~(åÈEYýPª°ÊËÙ~(¥ Ø¥ û¡4#?”šVì‡Ò®ï‡Ò£óCéÐ÷C2ºŸvtŠ·ŸV÷Óª?Ó~ZnŽ"O S®É@¾Ì:Ë’v¸Ãy2†=Î-¿wü±ŽŒõÂ/7víüåºà—Ë›óuå1þ©YyÚó”À),@pº1«Aþ![ Õç—Ï™g+3-óêÍ¡ËÙ ˆ^—ÉÎÑ,®F΢”\‹ÿ#¡vØFP†œâ§ü³\ž\ESŠÀß2Èw‚Ðï­Â~°_f-\fab±ÂN´Ê>ÄÔ!jµcô:gÉðß”l=»ÍEú¯sñ;ÎÇý|A¶ûBü/e{Ýö»'ôH<|%ñ¨WÒoމǯ¦œôMáó¦]àû[o+œ‚ºÜUž}x!<ÓM˜yY”å™ís5&çª4Ï7>ÿzBa@Ò,Þ,*ÊÄŸG*Ë%™ÒÇñÙ/r_$=}™\PKü7Ó‹ë:Þ|“SÙ”_Ý\XÓ\ò²õY]kY]ã¿ÙØUý¦·¦¥·®­ùobÞléhíìèx÷~¨«o¸»ÿÃûþážá¾½Ã“#Sà‡2465<13:¡Â~(ojˆŠV­A²@Öaü·R(ŠÀ"ALNýl©UÄ2kÑr°7mD+ìĦ§£@VÛG¯qˆYçûã)Ÿ7!îð&Ä=`ySvÐC¼yÄ;ó¦œãM‹÷ P¼ép[iÈ›^‘7o>©2à͆ŒÒeÄ={Ó€7ëÛ0oöT6ö`ÞìǼ9ˆxóí`KçâÍ®!ã¼9­ãÍ Õäð¦ffìÍÏ›KÍ…T(ŠE`Ñ"°ÌL¸Ü<Âä”h……x¥eä*AÔ«èÕ6Qëmc@6ØI6žŽÝä·Å)n“³t³Kì–³Òmçã·»¢xA?_bâí»,Ûï);è•xÈ; ­Ó^“ŸðK1»žr* Cp3Ã:Hñ‚샕$^ìMnöZ|_B‘Rñ yI`JIpê³tfVô°BœÉ¬ÓJŸ¼ˆÏ}™”W›œ_ŸZØVü:½˜áÍGÏ›²+šŸT5çU·æ×´¿j/­ƒuZboÎÇ›8î±7!îcoö3ö&Ë›L܃ÏË›pGàÛBSŠE€"@XT€2DLÌ„+Ì#Vž­²›ZF®D­D®³ŠÚ`²ÑVò£]ìfûØ­NRàÍM.±›ÏJ·bÞüé¢lÇ¥ø]n²_.'ìñýz%ñ€wâAŸ¤CWåG}åÇ®§œðgxÓò¦Â:Hi{[iœép'Ó9ô1ÄÙ;žMâìÁóMgÏGZÀòfɤÒÀ”gÁ©å!(Î^%ÄÙ=¬++£3«!ΞôÉK=Þ,jL/nÊ(mR>k~ô¼9»¢õIeK^u[~M{ñË·¥u$Î^'›ÆíÍ$^^§ý“yEve÷vÒ𦧤`6o&—aÞ¬@¼ù ŠÇ›¯oæÔðf3²7Ë[±½Ù†íÍoÖwc{³‹Ç›Ì¾ Ì›‹O ûiÿ¯xs‰àî—VwiJ P(‹ P†lJÿFú­UØwÖáKm„?Ø —Û‰LN‹V:D‚À~ZS§èµÎ’õgñ–Zv–Ø›°N‹ìÍˉ»=÷]‘#{xójÊaß´£×Ó¤ÃR-<ß´ ̰º¥{Öi9{Öi‘½™ãìM›ð>”ë²¢ô>”Rx ^§­Àë´UxìÍj°7¥Ù oâ÷¡4Ü+jÌ(j‚÷¡ò&Z§­Åö&æÍêF›=õ­½ í†¼Éø¡t±qÝ>ŒŽŒNŒŒNŽO³7Id÷ÚN«'ú/­+=M)ŠE`ñ ®^03c‡/á7‚ðo­DßYG|o#úÁ6b¹m¤ÉiñJû˜•Q«£MbÖ:Ç®wAûi‰ÿæ¶óÒŸ\cw^Œƒ}A»ÜâñóÍÄ_=å¼å}’áù&¬ÓóK3óIµ¸'¸yß:èmðû`…ÇgB•.aÈ?ßÌvgžo"ÿÍëqù²Â›‰ÅAIE·“‰ÿf¹ðþóˆ ö=bìóMò±´§µéõ÷ Ñ{Ä–ÿMô|Þ#ö&¼GŒãÍòúN ÍêÆw/›ºj[ºovôá÷ˆõá¸ýmÝïù¼9Ü?4ö§ðæ7fB›¦ŠE€"°˜@¯á3@ï2ý`±Ô"r™@¼Ü*ÒÄZljcjµÚV²Æ.f}ìzéFÇ¸Ž’mN‘ÛÏDî8µã\Ìî󱿸Jö\ˆÝ{Qºß]vàrü!ðCñL`ý7åǯ&YúËþ‰VIÖ7äv7SìƒÒo§;ݹ禸þ›ŠKJwQ¦Gd–gÔc館ëÒÜiθÜ@ÙÓ[‰Áò¢”¢»©ÅâŒ2(Åó臒Ì*iVUÜãjYvUJÞ‹´¼š{O_Þ/¨}PT¯,©Ï,}ýèÙkðCyZÙRPÝV\ÓVúªƒ¼¶Ô¾hx÷¢ñí«¦ÎÚ7ïê[zÚzÛß7uô‚ÿfKgøo¾íéïììêNä¿98:84:ôalxìÍñ±ñÉñ‰©‰ÉéI,SSð&1ôˆS³`Ž›ŒÁºÊìÎÊS·iJ P(‹ Uæhf6=lj²Úâ΋е‚»ë-ïn„¬³Yoygƒ ôGëÐ-¶á[í„ÛíE;ìE;íÃv:ÜÝå¶Û9|Kľs¢_Ï‹÷»Fv“v‹þí²ä¸§ô¤W¼¹O‚ŵK_™_œ_¬Üé€x盉.Aòów’]CR.†¤‚¸…¦^¿wExßG¬¸¥ôÎ ˆÎ¼!QÆ>¾-ËIÊ KɦFÜ+ˆJ/о_,yP*U–Æg>OÌ®HzR)Ï©HÍ©HË­ÌÈ«Vä×( _e•Ôg—Ö=yVŸ[^ÿ´¢¡¨ª©´¦¥ìUÛ󺎊úvêúö¯Ûj:jßt4¶t7¶w·¼}ßü®·­³¯½«—& z08Ü;4Úÿat`p„ðæ‡áñᑱѱ  N`ÌéiößÔ.8on>æ¾é¸M)ŠE`Q!°ù˜Û–ã—·w‡të m'=¶ŸðØ~Ò}‡¹×N‹+?[øìxïøì³¾ö«ï^[ߎAnr¾}øLðѳ!¿ 9vþîq×ÐÂN^ 7»$4w·ðˆ°ô ®ˆ­¼DN¾QÎ~‘ .þÑçnH\¥ƒb/Ý’z„$€xÞMô “_ÊýD)×ũⴠ˜Œ[1éÁEHœ",!S˜”%JÎ%?–Ü "}P§È—)‹'e•È—fä–?xZ¦È/WVdVfW?)y‘SZSPQWXY_RÝ𬦩ìå›çµ-u­•µÍ¯ZkÛêÛ_7·7µt6µ½kiïjîèj{ÛÓö®»½³çmwﻞ¾®Þ¾î¾Ážþ°1GÁÌ›ŸœKsbŠXšÄÌ$¤©V/??­Õ™E‘Æx¨=ŠîãR\BÊQˆY­"çqò‰Tø¨“w+(…R(ŠÀâ@¬®9)ÇHü ÔG¦¤0­«ÔÚi`:µv’d4èjÃ)’ÎI^¤Z qС¾þ⧤>ô‚EƒºÀÖ"Ë+ wDv<úCê©UhÃŽŠIUš²wgo~§º<¾à£ì¹à¼IÀ¡)E€"@ ,6÷©YúÄ”|JBÐré5È ¬Ubvƒ0¬dÅØy,«jÁ‡ ÁR„B’‚©:ÃÌoÐ+ˆñx=« ݇Ф¤~㈄XÁi5Ò˜6¶"ѵÏu„ÈuªVa•TjÐ “«läZЇCójF#>“r¤ ™…úè ÒpØ´„"@ Pþ$T*Ä›$…¹ìID&ú¶9Dåƒ%[Í„Nô0’×q8«ÑŒjÕã ä±]¨V¡_pè“B 4© •à<´Ì°Û\ÄÇR-èF'â3`j(GW‘”"¦fË¡¦z ¸R­š$ô=oËL„|‹A8ºÔ‚©‘%t„ _Š7i;ŠE€"°h F?WUŽ¿Œe÷é3 jLÆySÆ6œ·stúýÃ)¾”&ŠE€"@ P(ŠE€"@ø»"ðoxbùendstream endobj 44 0 obj<>stream xì½phš>˜ÙÛÙÙ››ÁÝân@ÐHBÜ¥5îîîî.Dˆ»B !®Ä IH ®3Œð¾n`ؽ½«Û_ý·nk˜ª·º:!tÒý|¯=¯|‚ÌdÁ' Åãï2Nç£&‰% 1ã„âŒY¼Ì$!ãx1F¢#™×8™‹‘$BO`¦ ÒD™‰B´8aü3M˜™"HOâaÄ 2R…Y<†IÂ&i¼ô8!£AZûÍ 0’ ÿVoü½ýc„Œôac%A´°q² Q ? à&ó8Z£TXƒý”hqZ¤=gƒŸ‘ÆEM5;Ëc+nœ"BO¦Ç1JÄ 5ÎÀ«Á¼‹øïjþ€IºˆIš01ÔÐÐ4F&/5MØø,¾ä§%óPÉa`¤ò22DŒRU\ó+û–Ž3‚E)áĶ›¦óQâÅà à(Ä)À&°´æ=ÿ÷wˆÿúÿ9èÜlƒlˆèiüô 1“LƒQj쳘Iº aŒ˜I*-ÐS‚«Ê.‰*Zp)»ñÆàç§'Š˜fBÙ¨©pñ0þüÆ0ìiBÔd¸ ~ã¿×ñßÿ?Gæ¶WÂ(ÆY€™[-F‹U°O3ŠªaD^Ôó«”³ˆSuË¢& SãìS›³«:·IéîSõ‚Á¦0ÝÜ”“œj2·q&/3…Ï ê I¢„I-†íÁß}ü?Ëïù€ Ú*nœ #,dšÃC‰Sw?›Þ0šÒ0U6œÓ™×”Ý4)mš(¤à“w9±ðò¶Ãœªž’F1&Ñçõ¼ÏÉ٤ H0bíó22q¤­\3›UݳÅé1|´ØßUûßê˜!դƒÍ8 !wHé@þ…+G5™"r i5«àøì¦ÙC:Þ<Šöñ5=¾qûŽÐ÷+ì½WÖ9ŸQÑ]7l^,b$a‚h?K”ž`à›S9´¨ë‘Ωäþ;âÿWX‹Ò³à‹¹èÄÏBé H¥%ÌÏZÆ4¸$Ö+9çîg[€–XÕuËÉ7~ãµ½ò.»•ýN™^ºrã†ÛYÇÜKãÖ~g÷ÑuŽ.¼Ð?#£nqDÓéÅ5¥¼åle·¾3Cˆ›\ÝWÐÂ#g¾ó”9?ËÝÃwð =7G"@2AAÈ¿)ñ›ücÄ©ñ¶§ 2’!N;H /ê­în™Í¾8.¡í²ß8†Ë0¼zøÅ9t»4SÅG€«âs¾÷êQ ÇÝÇz®êØE@Ç«û–]# vÖÙzœ)¨î,rÆbhâ–ªm¤€~ðI³˜¦‘e}ÛÐGhZBŒD0qs¤±‚Œx¸AjœþoòCþ·zSb´x8Sa“ >J,/%YÜ4K×3·uèFçè­þ¡é¶‘;Þ Õü*n<º¡e=ËT×øÍÇM„(qÈÍå¬ãk»®ËR<öÉÐ[®\—3öR0kªH÷Ùt„²GÇÏ0ì´UJg︲‘ÿž“&q%íã·crê­Bó¬bÅhѬ<=Aœ™,B‰FXˆL¦þßêÃùMþ1üŒ˜V~jÜ!°%©¼Ôt Ï‚®kw.®Œ. \}P±UÒ@D;°²ã¦½Ævi çÝÕ½ kzçNP½„Tíš®LÐuж¨jŸiäW×u•_ÑÒ)¦0½¢™ó*·²‹ Q µo~gdA—4Ó+¥:ÿB?ÿqƒm´¸Ož¦ù^轡å˜È¥ÊCÏ2:+b”Îc˜Àoôšoÿ÷Ý'€Ïf¼âòx÷èâåá•°ÜN^eG]¯ƒÖÒªF<Ò›DU÷ÈÛóè…ÐsÈm)ªë/n®ˆÊ¾Ðp©÷ Í^Í.¢ðü•ÇL(^i£·Î0\êxIÀÑ»d—÷Üt I;E»<±ªk´NRoŠ·8#’OÉ©óÊuº{òewˆ „ £D È þuïô÷Wf =}³ZÚ‡nvÍ÷-¶ôÝà=ÁØ£îÍ¥êÍ-g¹ý„Ýyw.íhA“œcôçTqycÁ3ƼJVGÕm›5,½9Í%Ul·ÊXð«»ézdÆ—v¥Ô ÆTuÌ'4í—§Dž­N*º´÷°îV9뽆qûµ=r/õMÒ²ßvÂ\׿0ëòMó"qýÔÝ~Çå_ý ðв(¾ùí“·Û¯Þ[xžžS¹YHa§F ' g†¡ Ðèq(rRã<3ÎÇ\Ø«èÌ©éºOËKÞ,ª}ìž”žË:YA ~m_½paí Q»#ÚÖêV~jfÞNÊùŽ?xeŠÜÊ©îæí qZeÿïø²ÝÇ(Âjö±åÅMýõ½žiM|ÚAÿê÷ûþ¼>Ÿ)JWq Uø™Îã¥-ÓÝÓ;ýòÚU\2Åõý4]’Ó«º¼#Óx+®?¤½KÛ—“™ÖEŒ‹¬m3ƒ×(]Þ)«åÚ³äÂF¿ŒàÄš¦þÅ ³Õ[Žª#NÒO ü95U˜–îK#p·’ÛöÓ¶;m›Æû¦Ög]0´ Ñ!&=¼¤§¾mø”¡‡‚¾ƒ‰_æQóX.%‡ÃÊÆqÙ5øI^ݤiHxi)l#<îwÝÿŸê[²úݢЇN“‡‘,nYÒ{qàVëèbrÃ8’¿†çvÓ­uöÈ0xT=DPé îx1jþ^…0Q€#:îzf~Yçäcº®\rÌõÇhÜ:!‚ Q™¨«&Šà·Pã c… ¢… "(1üÊÎ 4ϪΙ¸²óc÷¬}Í=ã›æªÛ§3…Ôœ¹U\úäŸQµŠÖñÇ_¸Q~=hœÿÎÇLg¿©wßÑïÏß~oÇ“·ß£&‰2Rx™»)G-Óš†—zG§»¯õÏ<Þ(©.¨å+J‰Õ –Ô ×òÑPqÎQrÌÆ£‚]†œUÊq£(QMO1 Q g!e1ue;ne'P+¼Ôx¼,ì\ùu¬¾°g¤íž‚ L‹Á)Ú¯åfàãW¢ÈpW1 ¨ì¼ªë+!Ïð NÊoâÓòÍ¿0±jîÇ­`×D¶nU]Ø>/mNÚ-þŽ„÷­½}ïí“·ˆ¿ýð!F8j!Ó\Qj\tiÇÀ»ƒ×e—5 ËQSü5±å6!ù4ïL=· ûXSßt+ÿ,}—¤ÓÑG¨>šöZ¶*Vf¦N`‘}bµžw±„A¸‚U´CDApZ“mp¶±w:Í3Í4 ÛÔ;)£¼¹{b®cäzÏä­šži‡È<‡ÐŒ =mÃ3gû\“«]c‹"s\bÏÙGæk;ÄÉG¢KˆPbEhñ¬¢ 1,| èÑa†’k /-IÀ8ì ºex´uý÷S‚Û†¦Ý÷ªxàxiy7÷ßè™>M³Ûtˆ*¦ãå•ÑÒ~í1Ã6€WJmÇq#0~¿#þ.¾ÿ›ç2vé¨wtO,¤Wö꺦빧™ûåXúd1|³Ý“ªÃó.fTw&Õέ,=ûéæ½'ó«¯.?lŸë˜¸ycùáÔüêÈüƒŽéû“‹íãåî«÷ÊZ&B³üÒê\âÊeÌ‚…´Ý$)ÁhBU*Oô–¾ž —°èTÒÓŽF›é›Ú<¼HóHÜO ÿÓ9¹ªmæñ­¤úA§ÔêÖ±ºóq•]'Ì9µ‚8)¤ÊówÇøó®ß·ŸaDøœÑ¢ ë•UÜ<™ZÐVéWaQâ]è•—w~ srùêòÓÙÕW—Üzô7WžÌ.Ý_}|óÞók€{ñþäÜêøüêÔÒ£Ñ[w›ú§#Òk2JÛfî·O.6^™-k½^Ø|ÊÄ,¨–—n0èz1ÒÝš"h’°àPDIg#y3R¹uC¥™A‰…ÍÍ#—†–tí#wHé£úÔt^+8e¿’1ú+vÈZïÓ ä¥$¢%ƒm©Þ7ÿ©÷ûö#Übf&Áöa%v¡ÅæQ%¦Q¥Æ9^ ¥m×—oß½÷èñÓû­ÞrÿñÏó·ŸÞZy¾¸üdaåñÜídzËï¤'YÆ]²ôËvЬ¬nÍ)ïò/s‰)˨éo¿ríÉ£§+O.=ùiñþÏw|¿|÷ñÍû/æ|¿ºòàöÊ0ìsŸdW¶œ«ê¾µúskïxÇ•ÉsG.-NÝ~6<³Ü75×6:ßzuµ²o>©¤ÃÔ+UBÉ‚W–ià™¡é’¾SÆLDÓ”f™–ÜI<ψgðøûwi‡sixŠ*™{ÇävLÜ6õNØ-MÙxÐ[ÉSP/šØc´Ëþ{uòÿSz÷ÿï#˜c‰ê6ŠÐ; u@ÌL>Xz: ¼Œ4I“¸ØÊ¾¾™»S·&ç—Vž>ïï¿>ß9}§göîâÓŸ¾|¹úìùÃ÷žÞ¸rÿ‡å‡?Œ9ü‹+Jÿþ¯¨0+,‡£ÄÇç(ÄÌâCÙÁ‡Ð"ãëGo¬ÀV?»ûä‡Ç?ûá§Õ§?wÝÊ©í‹ÌnŒÎ>_Û1³ò쇻^þáÕè“èü .ùn1EEˆßÊ.}ÿó«Ÿ^þ|ÿÑ÷—{Goܺ‹,©´expj¶¨®õÚígwxµò䧉éÛS»¯ä\§z¤ž6 =m©lå“RSß=Ý7u»áÊBmßÍÒ¶© c+Ý#±4®¬£°íšœ©ßwÚ@sû)äæHЄŒ3ùé™Ðza#ð6™8¨„ GLŽOÙélõÈ£¦ñÕ¶«Ü’ù5|Áþ®ã8ðhüâHs0!B†ƒ@¯10ø“%n”¡åU^ݽxgõÅýûï=~þäçW?¾zuçé«Üš>·¸2Ûˆbׄjç¸rç¤Z‹èrôó©µýu]“zUÙÜŸQ~¹±o¶cj¹¼±mtáaIÛxbiÒ¨©¥À}îÎóÉå§^ñ'™¾Š6ñræQÒtoß䲞k+èŽ0pW3ó­X_Ø:UÝ·PÔ~£¬{.ª¬<-ÿi›?s)~Á§ü… :Ü:XQb ðg3“ȹÅH#Fiñ"ºÎ u¨°¤ä׈Êj+ªÂ­áËŽXÞs5'³½hJ¤“‰0lÐîc–É™]«÷î®,=zqëÙ«•g?®>|rÿùwŸ¾¸÷äÉï^|ÿÓ«_^u]™ñˆ¯±‹»èžÒêSï!¾¼;÷Ò€kb‰ïÙKÑ%½qe]á]2êÌBÊ‚Ò.×\[~øìæÊó°¬F—ôÞ±›7î¾:[?v‚|HÏïÅ72§éÊ죋C³HÏÁÆ4 ΕwO—tMwÝHªéµŽ*U·K8I9¬çõ^ù/”þCXsß'tÊa( – “ŒèŽWÏ.Jˆµ,˜qn9ê[%Õ8åí¸´Y…¹÷=rCº î‘á.F„06B‹I¬œ[ytûÁ³;Ÿ¯ çœ˜wiÚ<¢œ ”ÛÀAÓ »ß(­ZÞgQ‘Ä 2Z,È ûû®à8ðøp@T¢\ •õL­.Þ} 'ûq(øÃg?ü⿼ú£C®)ö‰Un)—\’ëÜRš }ÎI5çb«£«Ð¨YØ22ûàÎó_î}ÿrõɳÅ;G¯ßN/=ß·ðàúÃW3÷Ù>2»<0s»ûú¶ 00·ZFo]˜c#Ž(½¨uˆŸ»Ô,ª(µa"¥¢»¾}|zñéÐôÝÞ«wa4ÐKį̽¶ê,Ä7ªu¹—&“«û*û«Rj‡P“ýNéê6 Å-A9-2æq¢˜%§'‘”ž ïQ†|°ml©sx¡g⎚U —Š#/5†ßøìïV¾[À8ŸðšXz~céÁí/@Œq8q6âž¿|‹ø‹IØöÃ/“ŠZlbkœSë`ÕÁ…:&5:&79¥4[FרÅ×x¥Öy%U$•´ß]EðêÎÃïû'nv/Eæ]T°WqËT´IT4ôŽ.hî»Þ3±Ü=¹Ò>~§mìöÅ„mñªîÙòŽÅm7Î5Ofξ0–PÑ…)éˆ(¸lì›¶vƒ(Æ”¼bò¼’„”ì¸U<ÉH#š¬LR½*¢«%äã§ íöœ6y®õwÄáþÀœŸrΩî¹9sû1¼-* ·VŸ¼‹8¬:GØâ?ÿøËË_~N)o·Ž­sJ­â.0æ Uv±Uö èŠþš+‹µ½³aiÕNÁyEM#]W—;Fæ/,”7_=¦ã¤d®éš~îRMÇxÏä|ËЂóÝ®L·MÜ:?x“ Þ?WÙ5SÖ> «žwq<³~ÚTÕUЂ<ÝâJøåhm—<¦ey†éñ—}Ç¿•ÐA†ÎL º¤iš™NÇä*^Ü10a£ð™½§ìø "~çÜpày™ ×BÎuL.?ŸY~xëþ µo?zùý8ÛªñŸ^½ÊouJ¹è\kSç‘ÖPÚWÒéUƵ¤}ºe| š[V×s‚îOuOú# }c 5ÍârTyc?çØ¢ÖÉ$é­W®5N‡gGd•\¼2Äë¯Ü¬î™©è¼*ì¹æ«ÙÃéµ@<¶´3,·)"÷Ú'€»]Ĺ bŠß ŸV1÷ý|çÏ÷Én;i#f„¥@‰°íš~ûN›sÊè#pzûq“½Ê¾¤×ô÷™ÓdNF–ºKJh`˜½óxîÎÝ…•‡ ÷þq¬Ä_þô#¼ø?ÿ’U7`—tÞ.±~<¹~zzip!8½ÁÌ;-§¡¯c|~øúrtbžˆ2UÏ!*0¡ºot©otÂÎÇ›Øi9£¨Ä‚ÖŽáåÞ‘Åž‘…öáù¬òV§ ¤ó]“ç‡çjûgÊ;®•µ_/i½–ap'WöÄ”vGµg7†môO­òK¯öϬCÌ!}×CZÖÞ!ù—½2_‹rk‡!~CX.i™!`É­ìÅuÆ•[ÃG€ãMv¼7ÙÅÑ3&ÄL€Ë†ïF_(”D…i1•WFgïÍ/ß›]ºÙ•ÇKž.ÝC>þý½§/ï=þÁÂ6vp/?¾üþù«Wç硤ñ•6 Pé½}åêjXz¹–¥Ó5½¦vIb²jG4¬ \â \“´lÂNëÛsK©J(™É›‡ù$U ß©säf÷ÈLÛਬ Qõ˜ÌŠžñŶ¡¹ó}72kz’ËÛ³ë¯dÔô$–¶Â€„å^ Ȩ ̬÷K©D†OR©_J¹[X—´>Çz±¯¸¤¿Øwj.Ɉ¶Šmlèž-¾|UÓ)Qœí&så}¥¿GÕR¬ÜAϲ0„7( î€O@Ù5³²ïÖµ¥Ç7æo#l›½ûìæ½gPs Ž‘qÀ ùºýêÕØ­'n ¥AgSk¯„wY…{&VæUuÄåÕËS¤4,˜ž©T×Dy𛬾²E(Å;ËÐ'GÍ6ú$Õã–íi“ y«X%³ðÔòn˜úþ‰ùò =*4[cçàšö1Ì1¡Û9±Tß}Í3:ôôêÞ¨ÂæÐœ‹Pp î“TîXæ_äïWè‘u’êöñöƒ_À°ï”úZTKTÓ£~ì)^¹wr Zûu½ÐÐ(fžMZYe—÷DÍ…è™òG Ù˜‡æŸ1c°R`±úçž_]x€BÉôÇÓ+ÏæVŸ.¬ÞGJ¾|Ÿpn¨zƒ„yô”¤ä WoÝ{Žv“Т¢öÙ®©»-#·M|Ò0ëGq —£9I¨™ªØ„™‡–ùÒ=ÏÒ<Ó }ó ü ôý‹4ܲ”íâUìTÓOZ%JGÕõ9¡'¯e¤klÏtôK.n|ò=ùË«†î)ó°bfDmLi/²­Âº.m;-Ûh#ÿÏT}ïtFp!˜fP¥IX%#°ˆTªãS¨å]€>v5·lE‡TY«x‹øcæ©‚*ŽGÔ,BÏ%ä–Ÿ­j.k¹R×3Õ=¾Ø;q«gt¶¤®UFÙÐÎ?1¶°%èì¸o–j—@¯ÑH ¬!vÁihж H’5üxƒÐçÛ%ÿ°QTRÙÒ5¶t`öÙàô]ßÈÌm*ÛN;r!–Ã\ùûÔç†ò7©1‘ x iÄ–-F¢EbÈGfV&@ÆÐiüxæö£ù;î>a1o/À®Ã°CÍ?{ Äs†L"ª˜1MÆùÆNá'ÔÍDŒµ’ÌÂëÌ"kM#«aÌðzh•a`±A`¾®o®W¾¦ÛYU׬3ŽrÖI'-ãd-bà^ÕÑ_Üs³{ê6ʦØpáÊÜúNÌ£åUµÅdÈkPÎèûÄœ ɬaùîr¯„b÷˜s,¸³mƒRÃ2­ü“‚Òl÷Ÿ¡}°^ðÓR_ì=ö%·œ¨š½ª]ì1=ÇïøOn‘ÒÛ§é¸ƈ2ß#?Äyh¤|ŒìERcÒÂh×èìèìêøÂýѹû}úöÓ« ÷æVÐgþ /=xŽ4m•€þjþÃϯ²F ¶ÔˆÓ°rŠKœ¦m¢#fP¡iÔEz€®`„URƒ+ Ëô‹trµ¼ò´=s4\2UÓOÙ¦ÈÛg`Ûj€ˆ†«}TéðÒKÍC7 ¹ze¶¼u4*»ÚÌ-LÛÈÉÐÜÕ/:»ºm´äâpXF tÙ=&‚'NáY¶þÉö©˜;³ñK²ôŒ<¢LÿpƒÐ';¤þ¸I  sž2PsÙ-g¹EÚT@7W?’„mX ÊZ üžøqÄçèÁ#¢VÒ‡L‹>a™T98xceèæ½+có Á¶ñwž±A_¼ÿléÞ46¬>|ŠNÅç?ü’PÑG *Ñ ,7®…§…”3"ª!%´àjfx=´œRF ©4ð/×öÎ×öÎÓðÌGö§ê˜&o—zÂ&í¸UÊAZ¨¬i„z-þ®©Û¦áùóƒ·êº§ËšGózbòêBRKCÓÊàAZGæ±I¦æòˆ•‚©W´Sx±äAiÚÊ'ÁÒ;ÞÄ+ÞÚ;JAßòÃuüs¿¢{ÄÙøò^ôÑa : ÈÎgT„AÀ¾O»#0ÑO6[¢ó}ŒÔX«ÄKÅsÝWWû®ß‹Ù±ÉŇã³w¦æVfï<›Zzr¹ù 1ìàß:ú"î>zöôÅO¡y-¿s†Á¥Å!ú :ÁhJP -¨ÈÐ?ÏÐÿœžw¡Žg¡¶W‘†Gž’[>Œ¹’}ª¬uÊ1«ôýFñè/uHß'6Qܹê6mš-ÿ9%sŸhK/‚5Ó%Bw f89‡êXølä=þÕ®ýHÏÇæ_¹v»°ö²Ð)Ú9;ønôï¡ ›lý}Ÿ6ûa/=F3ø²¸i0ì ˜»tŽ-©ïŸ×FàN”}úÎÈÍ{ˆOÞº÷t¶…ÇøpúëâËSá•£¾…Zng‰éöD敧MÙ9CÅ5­9ût¬T=j“qØ<í#–LŽòéx‡wàÁ¼\œ­ï½ZÕ1Q|iÚ ¸SK/n(xPz…_b[xº†‘ãñ3†GOé*X»„`ΈábäFCéª]€M€´ÇÛ¿Ý-adà «öŸ‚²{”œÐÖò-1 ^ù¨ïQ/+|úšŒ«z"‡Ú¯ç-C÷n™X휼ƒ±H­#s=Wo“@nf怾ð³ÐtÔfo?îð®yçëÆ­cªÕS”]rੱŽIÝ=ûŒ3ÙøqÚéì ûL›ô“¶éG͘% åÆÄôBÄ ¼ÃË»{çž ŽyydFÚžöüÆÞ¬êvÀ ?Ñ9õÉhy-ñŒÉqIñŒÌôÊrð‰¡ØúÒìý¡×ÆnáŠ}¾Cµõѵð0°ö¦Øú«2\vŠÈ~ò-çÇßîùr3ï7¢ò{­±Â—Ç0 Ô"„Õ“ó>Øsö{„ ¹ SŒ¢sϵûàE]*ÑK†yÞ™‡%ãójk;0ôˆ£Kš>6¸OÝzeè,yˆÎÆÌª¶È¢ŽÐüv-§”ÓvéòÎ9ˆÃ¡Ñ'íÓ·´u*äIÜ!f”¸A ¸®§°N FˆV``~ëàâãî©Et>4öNcþˆhwC^0¥ôRܹls J)óK(" ™åŸ?©ÿêÔБ˜k™ºRaÉõm| Ê6>o 7CmSW=s/º] ¡µ÷%Ê×»Döž²Ô a5;¥¡Š€Ž«÷©>Ži¬UAÇÖå L.$£¸ÙŽ"&Úч€þ"“˽×î ŠC,ŸÎ²ð÷ȱ•mä‘¡C7ã‹ÛPÇÔ°‡Ý>a›yŸ#ÖéPíc GŒÂA›k8§XF•æwdÕ[„ª:e‰éù£Mqàêâå«×+[ÇpÌrë»wrÉEÀ‘]’^œRæ—ç–ÄÓË.Ÿ³×>8WÜØ© c¦ÎtÖ€ ®oé¡mê¢eæËo`å‘Z.½çê¤ðòú–»eíu#qušZÑÂÇGÅJš´ß›Ú‚lÝAüšw©´yTÙØÃ)"¯müNëØm"£ËPv€N>á©å¾ë+ÈЯ̬²q»y—;œûÌòýë«/òë0J([&J™%œ°L2Ï@Ñê(=$²¨«yüîèâw^/ý0uçÇáùÇý‹?¢ýØ-®¢käÖùα²ŽÉ¢ yuÝ•­ˆÉcóëwpZ©©ŒxEçØ%# c#ŽRZïäíÒúˇdÕT(–ºænÀZÇÌ p똹qS§ üê6 1µ ÝDLèžù¸ÆÏQ<âb]¾¸Á0cúìýAœ4uã:Z§²ÃæÃº;kbØ¥  è%™ïØŒ-âv€eGôî0òó+„—›#F~âæk+?àÌ”6ç\š–¶J:`'ÅÇ> Ã0³°ö‰»S·Ï,ÞXœ¾&nô]›+h›òÏhªï˜­¼4X|i §¦3³¢5±èBtnmxVePj‰oÂ9øn€.ˆ[úÄ:‡¤„é[<ß5å⼓G¼«–‘=°Ö4vÒ2qVcØ«9¨Òí ,\s+.õN.#‰gãŸð¥ÀI> W,›Â°9Æb: VN»e~s:Ž Œ"ònpk8Ø"x§ôdäS™çûšú œ¢xÕ< âÎ8Ä6ßÁôø.Øv …!YCó0¶ðuLÝî°0ò,Üï²p¿KÒ·ë÷'¿Çñ¨ïCi×af(ÚNP’Û§âgQ=2÷#á½Ó·{§WJ/Ä„Mý×ë{Æë{&+.‡gT$”æT·%ä×ÅæT‡¦•øÆå&Áqg¸„§Ù$ÙøÆ›{D™¸„Ðì|õÌœvîr{„Iʨ*Zn(5,¹Íèk1­”©Vj C ÏôsÕç[¼Cã7s‹ý‡°*ÖE’ùôëeÜŒFC½ø7ؽ žå2SåöȾé$`ƒI8×Û<¶Ø3¶„˜OÅ ëÐOÛDÖ.×÷“VaàŽÙŸKC¤yƒüm0ò‹P(;Œ0±}àΆ¦гÑos²„ïÀÝ· Ï]¼§_50ï_~ÄÀg³¬ãׇíN›ÇæÔ"õÃVÆ/… m22+»RË.fV6§–O«5ñJ”Ös²LÓ±Ø(pü¤Ž9à¶ðŒ4r âH±‘j9'£>#ÍÝWÍü%¤•Ô©Äqqè8º¤`Ò)Vîg˰ÝF}­¹ïª½{ˆØaEy}k~ý½Êì Ê\ƒHêGDǃÓÄü&™("ŽB´F–Y1S±·PÅ-Ë2¹í”m†€®'~õqòA…ETcÒ‡=’Üß@ÿw@ÏŠëô,úß¼0°Ø<¼|¾ÿ&¦û¯WuOÔöO7.äV žd†bF{ëIóRô­GL¿Óç?ež[[p!± )$¹$(±(8¹Ð;.Ç?)¥mU¦+z\àµAŸ2œ§!íBò…ØŒiç‡Kýã·Ê;N*ë‹‘W1°âo­º*ÕJƒaë•…¾8\ÅRPué´ªÈÅSz6{kc°Tˆ‚í@d· ¡_¨¬KN±óä7çÇÑÍÚœŒ’¥s“åɧ3SÇÛ§–tÜ×q¤DaÉ9—`ÄÛ&Taò¨¸cæ¥7J;§Ëºn”wÏzØy–ô‰ôÞ`KU×tm×uámc(x•¶Ž]ìGÃRÕÅႦ±ˆ¢ŸŒyÓÐu’´M‡è[÷kªZù»Æ§VÄùÆäyDd¸„¥ØÇÙùÅÙÃq{E1˜ŽzîÀœK“á l`eë™_Õ‚Þ'Š™ýæ=üÇÏèªQ¬€8œ„mLäæ:&ŽêT[K×`ÏÐÄ“Ê:»xÅŽ*hKk˜ñÉÒ95¼áÚ@¹q4oèôÃùÿí!N܈ccF:/ó,š·åí’2¯ŒŒO7´Œ ÊS¹Õü¸ É1JŒœyPaFùÈ4_qÛuV¯w.½´Ž_…”¶O”µM”¶6`ëKÑÅ+EÍ#9 ùõ=¹5íÙÕÍg«ÛŸ+kÈ­mV° Ø"c´n?å/<ŠÅT¥u¬½b³ƒœ‚‘aa²¥[˜™s0t™áà3.¢kénãéàK±ô<¡B9pR•_òøAñò (•jÐlØVj1´tÃZï„|e}sA)9áCÒ'Îh¨Q,$(;iïS÷ÇÈ"ÙˆŽ£«+D~“,+¶–a!Ð%×:“}8bôu×ÔÔ‚ºãfq.{¨9R6LÖ£O ®z0¿õf /]=×|½°m¦ åZQËdáå«-S…Í‚K£X¿–ß4œwa—Óå6öc!·®÷lM7òñôŠË©eÍ`JA§ }ìH•˜œj0×\ÇôІô ¿¬¾}ˆSp²ƒ_´…[0rm†“ÝÑWßÚpÕf:˜»§œ«ïžz€¥O¹5mêt+9 CY Cy¦‚®‰:ÍŽ[f‰ïkÙž»&"f¹~·´±œœªžÝB•áÀ)m¸û¤Å^]Òé„DÐk<‚4øí)8Þ¹ùsµhÅ7JÁ-Ÿ<ÆÙhsÂm°ûí¾:HÛ††‘ÝÃãÇàî1àŸß>“Û4鼋Ws.bs2ïÂHÎ…QHîùáœÆ¡œÆ+g²êú2k{3kºÐ£’VÙžVÖŠÔ@'ž;×ÀÆ:,³di`R²l*x4óIÙ(¬ðç=RHž™»™º†Ð‚ô­¼€ŽŽ¹ûi= 9mSy= ˜wDk¸Ùªe`¦côfP|†„Œ"ðÐðìt{cgëb«Jµâq¹ч®®À°È(ëq°1§t-qiÎ^9kNýè·X³þ­".ÈÌÛFF0LÒѲ+l’vÀ,»­¸´ýù #÷ê†!?E¡\Ô$“ 3RN˜E¤žŸÌhÎnE×bjí93ê2ê®`ü'£¦’^ÝVÕ•RÑ!‘¤Ò–Ä’æø¢‹ñç.Äæ5B¡Ô:4£< ¹Ä8Ê^‘™PgˆKhªsh¶Œ¶ífáSgÎtG˜nM—Óú¶ÛEdÖqT¤Úmæ;òÙÈaE*&L­¡_½sdÆÑ/\ðÐIE=cxv%¡\Ÿ«°22¤á@œnãSX×Yß2hjï½[PêØ-mc»CJŒ]G ¹”œ±m’ 4w<û„ýýßΣq.DRòá 3Þ<´¼¨õÚÐÂÓs­³RzîRÔ@=ÿ2aƒP²ôÒ$7JˆQ‚|r»ÒÆ3ꆰ¬ó›˜4Iªì¨æ„’Ë;!Iem¸²*¾¸%®¨Ý)ÑùhZˆÌ® ϬFµ+0¥@³Yqp)åÀ FJÆT°¦JLãjÜR§EeÔ6 ýë>©O7 ýe›Ø6!ÙÏ6 ~¾Ià/[…×sŠÍmh½2ÝÚ?U\{ù°œúº]œâÒ‡” 4ŒA¤ÃwãÀ `c™wkÄêòZ&¥•÷ J‰ST§ZâöRß%c´OÅ ùï âü´Hì¾ ™¸A´mTCNýJQˆˆr«;ÜâhÉ¥ÝK6QU¨]ÂÔ£Ê ÁŒFÓi\å`beo|ikF»3¶¸ 5y,ºŒ˜‚‹Qù"rÃsÂÏÖ†fUƒ1C!} Ðh7Qu†"°{fã ±ôŠ‚7s  Ûû£­ ÐV¸lcŸnù|›èŸ·ˆ|²ž¸¾Qˆºžw÷aü¼•[È>‘cüê㯷øÕö/¶ð× šÎÎÄÉÓ¬½í¼£éÖžàâäTõOª*ê™niuã]‡4Ñ´,¨öV£ÿë“ߎv³2M¬†ƒ†éFI+¬´?ª¨CDÙvûI+YŠ›wZ£¸šƒ¡[JZí¨à[A½`’­Pc%éá¾g/C»cJÚ¢‹[£Š.G^ÆŒv乿¨ü‹¸-@‡f×…dÕeT£) µÌ7©Ø7¾Ð;–UíŠÌr Ïж~q‡`Jµ‘SÝÞ¸€TA3N*ÝFßÌMVÛC_lýíâ}øiy}[ 9Ý/6ñ£ƒ…ãOë9>]ÇñùÆO¾Þõù:Î?màúÓFÞƒg(êÆŽW}YÅÀLEÏá ¾ÓY@FgÇaýí§í‘ŸþÆ`ýÞ&MàÄ1f…=´ ûämöjŸ±CÏðYóãŸð¼Ë‚òf\j^¤ó–,J gõ ¥­˜âË¿‘ “Ú¡9dö'$«>S!éUþ©X«è“X„]‹±ùî‘9®áÙh]GŠâ&‡R›º…:Å1fçÇfϨ–®D=œT™ö EQ?© ½þófÁ/¶í“TÈ­í_|†C±únïç[xÿ´‘nýÓï¸ Ÿ­ãÞ)râ„¶Ù‘ckÿsûŸ7ñˆŸÐίn»ù°ì|·²®1Ü·Ã'ꨆéfÑ3»OójáÖ|Doµûø¸~ÿ„K©¢cá‰856 ½)2³î€¶#Ÿž®´ÐuIâS°Ð°ŽÄÔžÀiSM?nÙ®Œf0)Š_VCDá¥Ð¼&HPÖy þÑÝt2ÜG€N*Å8'f¾Ü¢s]"ÏbÈ18S!˜B/1jÙÀÚÄ5ÔÈ9Ýh Æ ­½@ km&!L–ƒ9A6,{ç~8î/¶üÇ6¡ãjFC7¢(ß>†û0dòkL ˜{E›z Õ06éÕÖù5Ø3’O¡„ŽV Nj.£aòŸ;Å?úŽNüÓí¢ŸnØ')w®¶¡Zimëá~¶6ü“u\Ðq­.k›ô?¨8£ÖIXøvn=3 ë ¤!_Æc{3YàlCXz:ÙFþõJ¬òf €×¿—ýY·~`A⻥y¼#7þ@À¾+„~Ó‡‰g FßÊÛ7õöH£ ‚ä_ÔxÐéÿb{ÜjY€K%Ò j êÚpÓwTv­ˆ¼Éö£Æ"Qø›±¦ž×7ÖÅ iûXEÁ†¦Vc«*ô“}D¯c €5æûòíC2mS0ó… ̘yF¡­”éBšÆ5º ÙXCµ7;ÐÂ#ÊÙ}¦#4ú‹m‚Ú$ðñ:ž?¬çûl‹àGßî<¤hát@Z•ãÓo?ùëN6ÖŸoâÃbÛ× ÿÕNÑÃg =³ô¬áë?ùz7·„,œ¸¢./+¯o‰Ø·c ~Çc€•Ñ Ðÿé"8ÉgÕa[)ÊmäÂc`äœNöÌ“U󰇄Ñ""@†ñÉoy#¨Æ!ÛÞÜÆžm[„­˜ô4Ö‹°_êõãÛ£Âz‚:Èky‹,û ßøˆÌàÕ „vãoýȪ$&áÆ1)ª¿MD~PF­wB1à†`*zí~Ö!4 ãœÖ,¬½â0ÂnG»8š Ñm¨gÞ3WäPl ÙÚ Õ†É%VÅUNÇ Ù÷îÝ!"»™ÿøç[øìÚ/·q|òÇŸ6üñë]ˆÓ1¾ íÆ‘#ßlçE? 87œ±Sºæû¤T¶Hjì:m‡‘a¶e†Òý¿ Ž=ótܪFöúþk¦kŒ¢ ü;¶‡á<à`àjNì ¡G „ 2»EÖæËý´O5?ß}äãm°kŽã;Žuük6ð¯Ý(€GŽ-D6‹sl–䨲ŸcÛÁ5Û¥Öî8´f»ôkÙyrí.Ù5»OAÖî9ÍÁ¥ YíÅÁ«»†OƒÏ` ¿!ä5ÜB Ž7BÊâTø,qz6(¦Õ÷¦V´…gÖ¢i“;¨Gƒ×²õOç<Î@?Ë­¤×OEẠ̊ȧæ¢dŽÞfˆMn!LÁÐ."4t k–jkãìZ6ñѬíG¶v“G0¢(‚ P‚(j%æ.! ãØíÖ6pÕ?:ƈü“à|³àgù8Û’¿ =ÎÀGßìæøã×öP1¯Ž\™î,xBw“¸ì9·N€âÄÖýóNœ()=K€–‰ePäþh®q:`Éݬ˜KE³%D%¦ïÖÓ6PÒx×l‘âØ¸Ÿc8Ç·"ß s¬áX'̱^„õ%ž‹Á—,Y³Q˜c£äšMû!k7K}°ådí–#Ž'8vžäØ-DZçÇ^N%"\ÊœjlСæ@ü5ÜÔ_œ¸ ø,Í6ÉBuø°Q4v‹(˜ï9¬¿I†¹MÞv' g*¶gë¨Û‰hú¢)ˆ¸-ÔÚLX g¢Â"FàUrÒ·³ ÊD§Š™w†vÉ §+àd«64øå7ª pIXÎ’_áÆ÷¡Œ CQW¦bh‚zËc2Ë]üÂ¿ÞÆóá—›?[ÏùÑ·ûXùšŒ<è·®œmÞq>Ydz•ÿ0zY±4†t»1œÄOÓ·ˆ©l;fŒ ±QŸD>,þÿ à@œÕE,3‚X1f4®ÜÚ­ì²^Æâs>ù?î•áØz€j»À qläXÏ·vÐÚuoeÍwüŽoù^CŒÖ8¬c°fƒèšõû×n8°fÓÁµ›¯Ù|˜cë±×²ýd‡Ç.yŽÝ {”ˆIß§BçÔ  Ã°óèCÇ9ø)Dh£æ‚tÒìd”fÃ!j*—VÄ.µ~­Ní0X¤£ÆØœ4)­eÃyÆžSÆÆI°Qˆ Ø÷…Ž£"¬,®À 8Ešû&™xÆÀw»„!H¯ÂÖn °½6¢2˜nȸߺo[ö÷aÕ©Ö¡Éùa)çtM0è-¯mº•ç ÇÇ_¬ýÓ×àQ?]¸ªÂí/IˆþwVFˆ¹M\VÓ ”^Á¿Ä)r¿ùA]ÜcΫ׉‘a–ó%ëâI˜ýOúq}¯mŠ6_Hèpì–æØ$Á±AlÍz`*Äñ-?±Ïß ¬ùNðƒo…!~#üè5Í7˜rl!²ñW½âk6ˆÿ*›%×ÀžoÞOLú–ƒÛ¤8våØyìƒí'‰ì”ý`שw+|°Géƒ}ÊìS]Ã2él«Nà†ñ7&¶º Ø‘>{¸âqLR£OÛe–õßËmW±Ù£à@¢¤$.e%,›ÆEMÐ Rw4pµó‰4³÷3q ¢8†hÙêš»èZº‚ó„€FS¢Y©0¬Ô˜Öˆ£ð¥†½eçlÄUiŽÅMðÚCÓ+Áñ9Ç”ô¡õ ú‚‡å÷ˆÉ|¹]@¼Ü7ïçøÿ´‰ëÓ\¯ß÷§Í|°öðïør÷#ª4üF] pwÖ’gô¶HœÁ•X»e­xô°ˆ\{MzØp+:. &w¦ÓR)ä~Þ ± R-œa)Žh±‚íЍýaÇaŽâ±î]aièÛï¼1Ñl-þ‡¿â»A¯ùF$aÒ96|-[ˆ‚¯Ùv|ÍöŽí'×ìåØušc7t\‘cï¢à¶çaùq^ ?NY «.@gËAÏI+# W«“‹$`è€nÁç¤$0ÏÀºëKïd «>egAj4àfXRM¦ËñHü.¬¤câÀMRËAÓÂ×Ü-#ºÈƒ m½Ñ‚‰0*¬1–Ž“:&º`lÙšŽÅ tü¯ÚÖ‘¾ÉE,ëK=W-~\ù”S“E·"y—Õ6%½‘pƒwýl‰Ò!€û“ ÜÐnÄl`iÎPl´ŒÝtÍÝÏØ:Cß'¥µIRw/®¸Ò†j#ì$×óÑR%ð^pý47ßáÚ—dàNú–YøŠ›Ä 3"9uü¾;døïŽÍRßBEYöV!Ö»X¯z¾Á÷ÝçoP²,fýwXìwuù¿` ßMT{óAâú ÜGÞÂMß ¸åÖ°à^³çÌ“®ú6lCäÆÒñÿqržiXjDx'”ư‚,“`œ.i’¤æ”ê–Û6²xahñÝOÇ—eÈUqì@¦@“˜ØùFH†DNUWÎS¦rTwºCŒ³9Ú\Ôiöš G-¦ž UÓšÆÎpÖÏÔ¨¶ø¦Ý^û5è4»ðÔÌš5÷Œ›9øðJÊÄ™¤¡~AZùÇoö~íÞ$Oýgð«ëø?þ¸óýñ¯û𥈴š:ÓQßÒcòÚfb'ôwï×Ü$¡¿UÚŠG+˜´ñàn– ‡a'=~Fi ))"ƈ¨c„¨áœž_I²lµ$l5Ô.$;¼ MDÝ›ŠÙ‰< Ô™ä&,h:RQ\%FzÝ)QhOÝ!m|PÃV×Ê[ßÜŸ<¢/Mº=ú Ñx†â¾$–]I ü«3N‚LNXr!:p ˆâS¬,]ͼ¸E£ÜŠÀÍ:¢'4 ã àXˆsýi=ÿr«j(_mØR¿¦–䄃ï¨Ò&ùuâ`Sytƒqÿ&L4¯Iè&ô'C¯…pÑ!2e£ø=ÔÈïöS>Ú+ϱñ‰¨×‹q|'ôÁwüX/È‚•€ûÓ bD<¾QjÄÄÿ²Áeëïk 7HüŠ)Ð|+D…‰ÿʯ>BBµmÇ9¶É > 7Œù?€›[öœmÒ_§fì°íXu²Îˆ‘ ÄÛwËZ§¹evTußÄ^Seó =Ç©;rÉ2P(±HÝ« nŒKKãˆC'ãël7„Bë1„K š~;d̹diGu¬@bk˜8ÈèÖÚFÖ t&XÀ}#Gþíö KGùð{ÅõŠ:&ˆ®1xLQGìÈ©2gNkÒ5éÄþãœ@sEdÔI`Fœ87 øÿºT ÷Ó"Òª-ÑÀP¡¹³2Ãn¿‚Þ)¥õâg¶£íQpâÖ·–ÀÃHä1"WòºÉ(–¶YÑúS~EÊ×P^áµëD ß p|uú¡5‰¥Å8Ö±d½(ÁêIÎÃë(ë5²l |ß…/K >ðZþ_h1"p„³!~ûø6&ÖÛ“\Œ¤c²;Oß½ë4Tû]íæàz›”ýš‰³8+PíÄá¾ßúqÜHˆ(…´¬³X#qzŒ²s–on—‘K?Zþ¤é +ø´ýÅõ}=“«0é#¤áFæúi‘ ëX÷Ü‘jØW4=•³Ã!X nÍ€}ŠöÛŽSOQNè[«°BtÀ­N·@̦L³@«MJ+é›\Æî¬Ø=Ž^b5*!a¨ª†æ4ÒaNò5:ÑqE« ÜRH͹Á¶CP å< §gåfhìÏIÛÜõˆº×qÍ­ûϬ;HÙxÔd²N8®=bÅp^‰üzß6^‹àç[IŽoD?øVÈbÓ(Èšõ86 ¯!Ô‡I¯Ö·ûÁ&ñ6’hœDàë±ì¬YÆùWýý¯š{ˆcó¡¿Av+ô—¥Âoñ}û½M†øë_±fÅi$TS|ª­áTe%à,¸¹YËž¿å^Xpÿªàï"U%yÂ08tf*,6Š¡’”PÎÓ6»NZòhøpjcg‚]foÖÅëy™Õ}Ù&p•¤¶fø QBxŸ†Ö_$§ èIÂŽ`Q½^0ŸºÇÞô]‡uxOè¡Eäã [ì_òŠÊ´tÁsE]sk÷plÖ­¹ÔëÃ#vø˜Š!¨uüQy%Š5IîŽ\ûO¡(ħ}¹]ø«Âû$d5M4ò/uâò†Ûö«nÔØ|ÈpÓI»}j¾BÔHq£8q|×_Å 8¶øà?u‚Ý&ãü(Ç7"lF`†ÄYdLˆ@£Y@“À ´Ø‡›ÄÙ‚oÂ2¼ ±Ø&úµ•&ÊË–¿ÓßíG9 ÄJÿ°}ô»(¯Ùy‚ðìµ –AÚë8 pÃq¿öÝ7›{a›ô_ü]ĉ%'z2‚vNFÖ3â*Rƒè}zQÜúQdI Cˆâêû{&Ú¹‡a™FJUHñ!íÔXYd’WÀvGz/Ü"‚@,m¥&¬ã/¬éίd»ï¨!Ï-4® HZÞ<Øý~mÛº`ÃÑ=Žš¸¾±=—ˆ”ðA9]Ó3TKØgXr(;‰êM]ÁŸì?©õáW;Ø©÷чÎPUè wE-ì˜*Ö~¯¤ÂVQ…-Ú[ï•wâÃ…Gôð=nŸKêpì<ÂÊ|?øŠÌO´ëù µm…^o‚.‹­]/±v½øšub¬ÿp½OâD×n” øÂDo÷lÏyÎùÞùGwÞxÿ·¯½}gûÐÓk[6|óØK?ê81ŠáÍŒº£›Îüý/þå£ýó¿ùÆú®•Åjvt`Ðu¬×@›4=Ž  Ð µÛ¾“Û;÷C©œšW›Q² ^bcÓPÙÖÎåõéeõÉ%õó×´'Ut¥×ôeoÛŸ³ý±…åh7À]Ã*#‰…‘y¨•)¾(¢ÐðÒ~`ÜÌ/šlR ¬iÅikí1±ÅrÈQ;ˆ‰òÆb㥙}GYؤdtݶʶX«—GôJšqÓÎn›•ÓŽ#ÙZ'a¸Qyl›^ÇB<·ÿ&‹nŒ¤õ]Æv\Üù·óØ…‚Çšö?wá_·}ãÝ;ƒ§_^½é1 6í^¼íɪޑïÝþÓgÞýá?{ý'«ÖudmZÝp°¶ý‰¬­ÒëOevžÏk>µµÿ44[ZüujÉ72«Ñ»\Z±#§¶yEe<ÎúÝyëwåÕÔ¬ß)gWNMcjÑ#Åë÷äUí^YѰ´¼qaIî4RÝ—\Ý¿dËþÌG¤ÖôÎÛÉEÞì¤ü¹è=!F/(¤Ce P I|´(„_Œ¨êØš³å@Ô]“ 2DÂñâjÏK‹VŸ¬»l¨bëŸYGÛ ,஄»ÖCêŒN›Y™‰Ôë¬&8pœ„ì=  ·ôÈë0âÇïq„uôQn#ˆ½vû½_ýaͦ–•›‡V4 ¯i;óîíÿø‡;¿Ï\ß‚%‡Ç®¾ýì•[+6 ì:8üó?þwæ†Î¦ÃÃoýì÷G‡ß<6ò}îõºùþ£ûέÜy»At óOßr8yMKRykƦAÜ&½¶'µº+µª'¥bOJESZeã’uxÜ•TÚRÖœVÞÉgu~!imwÚ†¡ŒGbÆhÏ‚-‘Ô‡POE•ð¨÷F²-¹–Ø5m¸#"‹]Kí…2ŒÚ¯ž”슮‹iË:²'»ÂŠØ, X ¸>¾b³`KX-s"È ¾Ñ#L[”c`mL{¸A±Þ5âP§CØŒì={ï›Uû®\ýÁíÓço>Ôp­–šÎ³ïþòßÛ÷Z²®uÙΧ:Î}÷Ê­VmìyüÅïâ~Ái0í­û0ñ=ôÌÕΧFë¼rø[7ÿñãÿÌÞv0¿u˜ËN®fv0“/éy1·å¹¬æ³8ËwžÎØq*ãÑ'2¶ŸÌ¨;Eqú–ƒøb,Ýr4ãìÁ>±¤î8vJ,©;–Õx*wϩԚî¹ËkØ¿€9/È‘ª ]Œr#@|ˆçÌ=»Fn™.hë± wPWEÒaÎÊ~؈l}5ñÕX윳…Q80^â+t7 ¦ ÄÁg"í.äø+—âÌY/Œëîûpgݶue$ÒïqT^è§@³‘9ø¨Dj»Ï`‰ßkï}üø+·04zíßânqi뺊;_¬;|å­ÿ®¦ûÜÍ÷>Ú1p6­ª¿¤éÉš='r\µýd棧+›ŽÝúÅ3Öµçï:SÔ>’ß>‚ `kP”Íf/§ 7½”×þr~îÓÁÈrrÇ·p%òHAïK¹-ϼþ“.¾ùãÆ¡ã©ÕmKw>«S`;‰/z7P‰|Y"JxL4£/) ,ŠyÑ7’²íP$k+Šåȼ‚9‰y÷ÍË™›˜ Tg$iÍì……àEµÁŠPI¯.ø*C¾h jd&Ø4mp_È·M¥<+iج%58ÊqyXGUÄ̸V¸, ÊFasÖ–EÌgkËÕb*À %Š”‚“1C…¬X èJ4 1ᶈñän\ÄB<Öóp¹9{†= ~ ¹7°¨éøÿí¿ò«[R*{s›‡Áê@ÍUÚñdeÛ‘ªÖ“ùõ‡A³§m9šÕ2ŒN sB´« ·dƒ’>DÉymèdö¢¥U²ÝÌçS×2X#Ó:ÑLØyG´ÍÇb!´…ÖV®èÌ£ÅÛ<&(#õ²E“6UXC^¾#‚*i¥[C~`¢0/DOh½4KãðqÕS€i_©Õ-ûAÅÑ{£T I\&ÄúS¼z M<-uPmºQí',Bç½ä.ieÓò­ßDäe#²c$¿å[`×!;ÉÂî Î øeFg´ã!-#ü-š­ˆ×¤jG±Ô½¤ÿRAç ‰5{#0Àùä»÷¬y9š‰knåLLEßΔ;Ü–rÞÛbí(k“ƒ±’¬âZ¶JAºÅ l¥&jÅÂxŽÚö¦Ç‚ëò«¨‹(~ü Ž:[ä]ÇWv»^øé[¿üägÿüIqýÞäµ­YMg0=‘Ý7Š›é «_Ý-Í¥üΈg ha7‰Ñì´âŸŠáÌ/¡ï†¨½ ¬>XCóƒ|ìÙèZÎM"Efí:LŽÙ:KYnlj9×­lvPU™.3ë)ÑØæ#Å$&ÁV M-llç¨Qûu1|µö/e›YQG>äFÆqÅž ÇÚ=?å6¹&²,òð؆ÔuyÓ¡ëuû_\ß|(¥ºgÅö£¹çrû/cŒ «ø1‹QÒ{_’´2¸”ñÒçw]Ä®!µt‘p¿šßñ|JíÙË…EìS %›— :ÅÐ&„-ic¦kië,ÁZ:V΢©$cTÀ¢è ˜ÒÌÖP t‚WkAYÄ‚¯—nE¥Ð¡ÒXn8ìŽ7Ôï@ËÜXþyÊgðE%E‰;„ÙWÙˆi=»jÛå[Ÿ(l†""OˆÇˆ2u7í½PîœK%EOA>“4©¡8ÂÈCï¥Ä "Ë6 Î¨¸ÚdÉQO"m,Ô_z¼´_P› Ë«£ÇMï-®ÛTÍhã´MŽ8íæ„ìW(ù†ìœ¶ZqàŠ' ÄJ‡éıÿÌÏÇÊÐb=˜ …zuäú®KAhÎØõ\vã %Xë ! LÛ±ú/ã@G$šIÌÜÁÒ_‡’Š!¾›V‡ŸºåÐìŒdG“À‚¢ŽÎ¥ƒHíkmX4²¯e,²ôhbmÛÐ~5 »Ö|,Hɼ­”µ©ªL1å0ë´mPvÙ—ŸeI«Bñ…ŽÂóº;Sý×ÏŒ Ÿ‡ßÍu,dc=_1t“†ê«a˨­XFañ(ß0±Â¡^CîÈ9ºn‘>â¨aÚБb'ä²úã÷emG³ƒ)‰ÙÒžÆhê¬Ræi E–€r{~1Ë+h¬MSÃcl>‚Ü–n#½·¥ÂB ¸m¾ms°1¼‡ô&ÄŠ; ¶±x ¾@|ÜØ:e#>‰…l,/ëùX箟…l¬çWw¼ª{æ1xŽ›ÈP Ì©LÒHaÃT¦Ò@žPú‹RÚ9m¤òö\*¸¼j÷Ó_)lcV†ƒ¹›Pq˜1¬ÙØb!>XãIkÔ^ "Oj ¤©A¸Çt–•*‘|,¦9­#Ñ™N›Aµ2!ÒžP¸5Ë `5øÚ1®¨ÝîrÏè…{þ3^DýÙ{ÿ1²÷úú3qÌÙÁ–QV‹*ñËopígÏ¥U O}5¯^¸PñÞóÐû(W¬µÊ6—I̤a­DŠÚV[tà*Wæ õ¢›ÞÛè·m2¢:•sÑY]7IK·ÃZ/|”ýë8šCˆ\ÎÏ¢›÷XA•ýÊüµ},çA œ—˜¹Âœ”R,>"ºPib7ŽI+k×–+Cn°V–ÌÕYbÔŽ3¿­@»ªJ/\me±fáì±Ó(“£«§Oɸf<âk„“)¸Q0xƒM±î ˜S»oÅfJæi:dº Ülb ;J¬ËMz¦¦ ¬Ù¤vñÚð'ZSÒ pî-®;H¿}ïíûpëh ½bGÖHÏÒîéÄÎ'ÜêÏÕ´ñ;Hä4—ãJ·Îóó×¶E.t¤ÌÐÐË !—´œ*ÓÒÙ©V"ÓÓ*×›U¶ŒÀ uÆô̕تØ×Ê >4¸—›@Ó´µÂrªéjÅáþ´O`ÄO.ˆãGŒ*<Ô}žºqúð‚È×sïcøÊ@8®Ë –RŽÃk˜¶aÏ(W SªU¶¯³#9 ªñ6©–Øn×á`DêYÝrdøÂwãŸöÆ'6„/ñ¿ÆBÜ=¯ÆŽwfûHÂ2„fÌø`r³˜6Îí ¬èBO"Ü霴Ê@0,p£…íw@·Ñ‹J*NêÌ ¼• ãÀÕ®s÷áÆuñŸ¸2èÐÄœ!ház+Ì#P“&÷ÿBS ‹û ~À°ÉÜôÒ ©M™ x9\Fg‡¦Å©ljì$µfã¢43e—Ò¤Ú¹É÷ÄõuŒ×Gö^?ÊE¬ˆG ® Á!” (¾0>@AZßÈ¢‡ÿ.ƒE æMðnò*ºFˆ#-7pk;Ûò*:çªl“ £b˜R“žy°8^=Žø$}€îYe¥XªÜõ*³ñJ1‚½£IëzÙQ¡Œ„ؾ6§Œ–X˜4ò*”ŸQÿÕ‚]tæjÚÓ‰˜¢’<7Lg<¹¬ƒ:ÚLÒ<1’Ý$‰œÜ47M¡T^šl  yÜOâ'|ÄÕŸ#‚ã P²ïµ¬Îs÷çlÃÒ~ˆÓ0„C ’´DöGg.Ûb±õˆû¬À¤-Þ`r®›3p[Š˜¶4A,©"L©-´îÕØ¯¯ÇYì0éˆkØ8:¬p'”4ÏÁŠ3”ÛàfiYB 3O©ÔU9 Óx°¾LûÃÀź-»b¬ÛÉÏŒ'Wå°phnµh 7ÞW€¸>é'Û¹Mâg8½þTÆ·»_Ïé¿– uðµÂÎç¿–¿@«-Óœ5d› ¬šÄnÝ­Sü1ÛÉd)™6µÅºéÃq¯Ñ [¥=¸Ûß­÷¬q@§êàÜòók˜Æ¢•û ÷@iÆ%„Ñëì”U#ÜjÝZ‚Ñ™KZ.Mm]}cnâ€Î‰SÅ:ìÉ»}¸ãˆOܰ,ôÂ@¯a w¸ûËüݳ–nˆ`ÿª-–]Rkë#~Ôc­Ût·¹/…Ä© Ü&'1ç¬CÖÍžJ0VaÎ]ëžåîODÊ‚ÌüÂßµpS6«à– STy¨”›Úõ Òà–­þ¢d°s÷¶!ÂÕsFsbMm‹Ó¥â>âq¸§ðÈÄdèů•vÀ9s‹,^ɺµS’££†œ|Ór#*Òr…;³)Ôk…Ä­{ vžmõÀÅ¿]Ûõ^+ˆWbå¦j×¼ÐÁéq³õÉíß.'çîÍÓ@œŠ'•ÛVYj•*A—Ó½€øÅó7>Yñ×—¥ÕÂ~ç±k¤¶D¡Ou+©r,¦Á-wëpª«7½Î6§"¶$¹£wÎüóøzOñgûýß­ÄÝñx7ÿŸNô PiÐ734ƒKá}:èÞÅÒÁú”E·îÅÂ/[i±’ßž-Ô‰#þû¤·¢,}m’äö¦J°Ø6·Â¢ÛI‹®Xw,ørU~ÁÞïD_þòR¡HÍqúvcé$N¹EœG°Vgn›#n]dgæìz:kà¶ ›!ã4z›Ç(¯ ÷@§GÈÖ„[dY•¸9G§ü¬þГ›’c±pãb}3ä¥ÊŽº‚«¬©²+ÌÓ¶ØI+ƒ~>Üì'÷tãŠ8ròòN¯·©.ZzZ:+/Ø»9vTDîÒ"±y{aֺݴ—"n­{z}3êÕ¢½Ð¥‹BI°–ŒNoÝ"wu:âÏ[î@ÏX·õç3êœvoV§~ºnwwð–ŠÛñÊmffÜÓîÏÔŒŒ]lµtãáµÙf©·[hÉðm,vÖ/Žøtû樉¸ÃÑ ‘uâlÉ×@ÿÕíš– }oÅŠ[b÷t{ã37«Ô.v¶Ü”G¼·µhsW5· ©_wñxªöÿ+Dœüo¦jT€;e‡>stream xûÿÿÿ‡svŽ1Xb`8ÂL G˜Ž10 âÿ ÿþը¦endstream endobj 46 0 obj<]/Filter/FlateDecode/Width 28/Height 11/BitsPerComponent 2/Length 76 >>stream x[µ –®ŒZ5uÕ’LÑP©‰aK$EÝ–¸ †Š©@I•"åâæ¶$S0Ô‘-liVch˜ÛªUSCW­ ƒ² Íxendstream endobj 47 0 obj<]/Filter/FlateDecode/Width 715/Height 279/BitsPerComponent 4/Length 5191 >>stream xí½n¤J†Gr`Ò ,m|¤Í-ùö N°Ò—žÀÒ 'sÊÉ|Ù_ýtWW7 t1À0LáñP /4ýðºh~ÆóýíÃæúÍk𠾿ò.pÊNy{Ôá^vÊ{Ø£÷²SÞƒÀu¸—òö¨Ã½ì”÷ °Gîe§¼=êp/;å=ìQ‡{Ù)ïA`:ÜËNy{Ôá^vÊ{Ø£÷²SÞƒÀu¸—òö¨Ã½ì”÷ °Gîe§¼=êp/;å=ìQ‡{Ù)ïA`:Z½üߦágÜf«>.wÎq+å&Æ?~ü/R²êãrç7Rn´æ[¤lÕŸ®´ª‘r£5ÿDÊV½lÏ9ƒ6Ê­ÖŒ”­ús²M­j£ÜjÍHÙªOÛsΨ‰r³5e«þœhU«š(7[3P¶êÕöœ3l¡ÜnM¦lÕŸ“¬nU åvk2e«^oÏ9ãÊke«þœ`³V5P6X“([õÙöœ³0OÙbM¤lÕŸ“kÞªyʵ ¿þà”Ûä‰>ßžs–æ)ÿþh>#e«þœ\óV9åœÇ6%§¼ ×|­N9ç±MÉ)oÃ5_«SÎylSrÊÛpÍ׺ˆò õíÞ³ÞTOnRŸoÏ9K&Êïâuùø Æi¨Rf¾Ç_Y@ôçäš·ÊDùB°Þ5EÈ Ô~ˤ Q&=Fu}¾=ç,Y(%™"Ds”i§ ž½üQÕŸ“kÞ*#e… Ã(Ùö£æe¦LlÙÕ5}¾=ç,Y(snEÂe)`”2ꃗ9dÔ¢?'×¼UN9ç±MÉN9ø8ú9&ñæï8%8˜rçŽðNÑoÓ®c­Õ)ï±?œ²SÞƒÀu¸—òö¨Ãâeèî û …^0÷)¸Ï›lêcìÑÄÔa§Œ'Ìc”ññå’2_ºÓý¹Ô“‹;Ħ›`¡ 6X@M,ÍF†wò2nê€2êI΋†%D¿ió²reºÞI”ù(…ŠYSQ¾àE9Þ+ty®ª?ˆM7ÃD™’7)ädÀSÑ›´¥Šr“~Óædå ('¶‰7e βÊ3úƒ€Øt3–PŽpuÀ¼Ì:¤<­ß´yYùÊ|Y²÷8-åÐaPžÑĦ›±„r¸ç‘2‚þüƒOÔþÌûa°™Gô›6ï +_D9ÀËFŸ#eðóÐË™.D/ ..X2B~3PŽú„ô¼˜W£üRÃÜÜèeÑ'Êß§Ååwz"‹ELÏfÅD y6ý@.†Ç68*Ÿß`ïWà¦IŒùëZe9PîÒ²õ£‰=“m^°ô1 E 6…ˆ3s®PFºH?"HQU?ÝDá—qërG#BW”/ ¯ÉÓÊH~ù…ð“¡Bæ!e†¨ªŸ¦ü-@UÇ® *I™)¾P(Ž35m8ÛN9€&Ê (ŽRFa¤\ÓÏ4N#ŒÿŠ2v4ÒŽÀ¾Ÿ¸)ÃÜ# ÊÀ jm”kÐ#à"ô–žóј!Aw0Uc½vVD›†tx¼/k+ey:ŽþúU¯¡êeœÏýn£üM˜1ÇÒ€œ5å¯k‚ QVÊzÔ÷ä¼)eèZÊEâFÊŒ9‘½N˜7#~”|}Ù]ü› „7ø»gWò1-Ä8³êeìY°†úqÙL?»°‘€˜)'Ö9ÏPÊg%_X)cb Ô0ÏV©e{z~aOP×9ÄVʈÙNù0ùÂLYÈr§¬F-£ »%hèÈWÓ7xÝ œ/pð{…|q½Â9 ™Cú…ç½B^†I¨=N¾°RF3)²r$˜ysÊ€𢣑6¥„}¥_˜Š“`†´p¾Ø5íÁD¦¼œúáübàMØbåÆöØ fÆÐÙ@¿B"ެ/O “‚Tt‚Ò5V²¡ÌD |á>Š¢¯ÑÊ|ôƒKre9AL{ˆäI?Ý8äK½8fŒïˆ¸¤ÌSKÊ|L<k eèŽÁ1 ,Gó $–¼ NG-ïx¯ëÇ(÷ˆÓ/Ë)'ÖcUm>ÝB[Í”9PC+kÊòQJУƒéÈV&/“¾Öľe< ’—9 ã;Gœ—qý¢ç³axù´VñFÓ,”t%!;SûË´¿ãÜ8žÑ×VBÆý Ç4ìfpÿ(R¿âCHÕÃ<2 µÙpß#áʉmâGy™­9 <£¯S¦3‘ŒÓ-…ì²éH…[N^B9ÂÕ 3oçò´~¬u}áÆå”¯—±:öš¾€²\VVý8ôrȲÊ3úñ––Ycç»&äи%”Ãuù” 0+«£_LÌÓúqÊ«dû&ä[( >|ÆÃÐËJ•BÑOP†ûvËü+KÝ;!¯N9Òj¥õÓ㛲ÆýòÚ”ÿ´)‹>.72¾ÍÎã寧¾…šU?B7M¾-k.œî÷ "ÑíY²°• ¨éöbHp—R€øpäJ$9wc•>*e9b&S5qáô@‰ ,TŠˆæ]mœ[–rèþm™Ñòê…2ckGÊ1k¤rÁúq)æíò~f'^/(K±K0)kœ‘2Þ2 ”t‰õRéTPÖPØÓ€è½L½ì¬1kz‹â0VÊ],ÓØçÒÜG¦¼é™yÖ©.©r¯¢LpÖÀfQ…ò õËʦBÙªÏ*ƒ£uQ~bI™¦™ÿuÓ2jô¼¼µ}É÷;ÿ§\(þJ¿yóþ²nÃö1'fM¹ë2oCße²aÊü‰DÍžfpBMé2Ì$} ÎM¹jðG ˜uYfU1+jòï%²›£Ìó)³ åº^jçàA3y9·.LªR®þãìe`ëÌ\ó2S¦w¢\ÕŸ–2ô§ë”k˜5å€E Œ˜*”õ÷”1eÆN‹ˆþ”éß PûjnÀög(Êï-2k¡ŒŸuƽ>ˆÇ!A¦$Å[¯A?hÆ AÝŒÇÛ2À\R„Ë^ƒxSéé?]‘‹IXŸ–rWRž(—˜5ú¤¸…2Â}ʣǺÔrrÎóo¢ 9âi(ç=Œ¸irߥX_Ç€.Bð&’‹¿œRžU{…’òÓPΨi‚#q†9Q{qÊ#À`ro´2.Ò¥Õ%Êt“R<Üäe8y/Wzg‰áH¤0'Êœ&¤[ê•1AÊÜÇ€ÕezN2¼ ¼çúb Æ{?…ðhÅnÁ%Ì5ÀŠ'pc”ñ!ñLbÔç= š@{%>T®6ða)«6´‡‚9£†”_Z°4Ã7¢†«Oz€–ôœ78äED_lÒsQ–Üœ¨žHYŸ<'jdͤO—äàô¤¶W*Vž8_*vÇIŠÁU‰ãTWÖ8$ÊÔp«¾ µ¶—/ÅúZPKl_wA`kZõE³o¢Ì·".—NVªïÍÉ2÷8ÁZ„«Ó,Ræm¶ê‹–ÞD8âê:µNíåË’î–Z׆á€ÚØ¿—øöü©~ÑéÑü˜šeøüôŦßFù›lÜ©u*Êp@—cºR"R™9Âcpü%ß4¬ú¢‘·RFªZgN9›¥Tw+ÔÄ* /í~«SV2 E_4ðFÊš^2(ʪ¢HŽ´RƇÃñhÕm¼•2æxŇä:BËl),ª;J±‘Ú?ùÍ@YôE;o¦ ,;¼MB~¿„t|dÊO}#5ËSâ¢_™2ÿ€2¼*¿8æl¤:ü‡Ù`~x‹]f«^7»×{ ݈ ƼÑwøÎF&¸øv`Êó€IÁŸx€¦Xõ°ˆ 7Se¼b‹ë?6åvkòéŸU/„1XrWPîBǦÜnM¦lÕ<º2>²\f Z÷¡)¬I”­ú ò ^Æã_FèÒ¾ƒ‰8ý˜ƒÁšDÙªÏ[}»— %“…w|Q¯P˜²ÅšHÙªÏ!¯áe4,“ÅO<À%¢x‘îÀW‹ûË¿ð!q¤lÕ¯J™ye>÷ƒ ^Fä4`Æ>èÐxîGw˜²U_4»/ÊOR´R³ê ŒNY.¨U÷ra[ÑêM«¾Ø÷rÅÂ2ɽ\ØÅV´zÓª/¶Æ½,Æ­îåÂ.¶bÅ›/D¹¼ïúË}e§¤'q‹­q/ÃãXx~éã’Å=i§\ÆRÔÞ¤'0à žÍÄåó|î§õ5‡i’aŠmq/3VÈðÂ?ÁK¡æ” ß45NŒ‰ëÌì”›hމeËy ‡ßhf§<°iz¢±n§ŒÏ×ÒsµqðXöJ±OŸ—ßéäг l¡zB-í•€vŽN-aªè2HÔà€‡/ÆÔN–`û¾«,ÜÓ/¦=A1QÃ^È¡Ó0ð2ðHúˆõ…®~ÄR3å!?ïÉC¯ "$\R†›«5ÊZaeÔÃÓSXàaüóÇ«E8‚ß85$ö;N‰cØZ&‹Þ)Ôè³”H ×(“5Kʸÿ_¹ˆÆH¹bå[Ÿz)öÙÃKjê#dg¡Fmè3¸R@Ê5ž1Qb ®–¨±5Û)׬ì^D%ÉJ€3€2[³rÍÊN9ú6šYõ0r°f3媕r¤2s„Í“§¾ä[Tè G{^V¡|iw«—ëVv/Èê ŸÑ“­”£¾»—5Õ2þ7Òj¤,ú¸\?)寧¾…šUï” {ŸÔË{ Uu8ec³Ð)o†V­Ø)+ë‡ï³P¾¬O°añSbOCù.˜ùcÿÏsß/ºªÁ€ëIà¿Zðð,^†ÿãÑ­‡¯mM©Ê§¡¼éw\Õ©Ç|ñ<8¤Fס¬=UòÅSQNÀkó¬®OW÷<…w©MƒÆgZÄiég¢ŒÿSÂ8$PÆAž–}Ö«øŠÀT˜Pé¿ÿ©%êóžÊËuS…­â‰YNy¦òŽæ'SOÊÇf:å12<ñÞÙóò4dîeGGÏiGç»—GÑð |3d÷ò dîeÏŠfîå@«œ™;åYÊ+œò gWá”g­ pÊ+@œ]…SžE´‚À)¯qvNyÑ §¼ÄÙU8åYD+œò gWá”g­ pÊ+@œ]…SžE´‚À)¯qvNy\ûlÐLJœr‰§§›|õðb~×o r§\R†ûÖ8©SÓ5عM…NyH‡lÜ©éšò¢ôá”Í’]ò̪S:4³S^•épe](s_ÛÓF—ŸâcÈÀ /×¾²wY·Î)W(‡¯xë)gèðŽCq¡ñ½oÔ=“¬ÃμuðÈ eõ•½Ny-'Ýe|Hœ×ï^^‡3RûÊ^÷ò:ŒùÀ¹2c„Õ»—×á ^Æã_FØö¼v§¼eDÌdá_]]/Filter/FlateDecode/Width 498/Height 239/BitsPerComponent 1/Length 1610 >>stream x훿ŽEÆ-8¼G¸Ô’‘iŸÀâH2Á !ßÎìÔDûpV– ¢‹-dÖ§ .B·°ÒÍ»wÍ÷õÌvWïTÍía<%k§gª~Ó_Uu·ÇCØÅ^î‡0à[×o(ÝPº-*ðŽ—Íi)qÓÙwÄkwöÅÞ´*»Nˆ9¾¾ ¿ÏFÅ;>ŸÜ8û|R‹‡_ŽÏ«Â×½é›}±?u‰âÉbÿmUë4ÎÆ%â~b|}c¦xÏ;.Z®½˜8}ÞaUà¶ø>\4oã ñĸ_àÃY—v¤Kÿÿî;šPýR/¸ãNÇøqûCUõ4†´ËæCâRÏ+nØ ~tæA/›x ÍCu/†´k¾&þÔÃõ¢øe¨:x+>Î^ˆïàO‹'m]_0÷.Þù±Æ'_{â-œé¦ý.½xG­€ Ümßã§Ñ×Í7íg]=*ñ(þû棴¯Š×¥k¾Ý¤ñûÍgaü.^êŽW-i‡z&eébã7›ÝîËýb/>ÜïèÆiyqO±.ÙTãòŽø½-“QŽLœŽù¬û“sèÒ­'^ˆÁ¦{ü5Å ÈÝý®‹Žˆ»!7å>åä¶xz<\è„A¶‡ <^ãèËÖ—»éË(Gnˆ'¾À·Ï=¾ÆÆ¾sgÝEuø]ï–)Ĺó°Âž~{²yîÄñmÓ3±d:÷øQŠoÃG½â%±¨Æ]söñ‚Ö8t·8C¶Ël™{lŽÛ·Ë}½ãÂÏ=°qÍÏ6¹¯…‹ž^÷}=ñžÔfÝ1¤ï¬Ë¶>ëâ+lñtyËF"×þØ8C6Þïë•NOìÒ1Ä_à¶ø>\4oã Ù8÷ø¯ÈE8üjÙ÷å .Ö„ž¼È=ž6óªúl»³n‰öâL,™Îýon¼ã°W¼$–•ß9qœýGeAk|:ŽÜñÛ¸ï±òÀzqÑÓëÜÛÆUw²qßßç5ßÉÝ=ÈrñÝûžgçÛ=\òÔÃwö™®nYžaÍÛuÉ+gµj±ˆ}¥Ë(G&N‡·héK¦+Ý=—Äb`ã ú¾Kß݃,ß úÞ­|.Fö¢eˆW:¡si6.pk¾[yÖ%›yX ü^é2Ê‘‰Óá5޾dvãèöpI,6ÎO<}ÙLñ¿‡g”#§ÃO_2[<Ý.‰ÅÀÆ≧/›)^àîdwC >Gy#?ÄÃE¿ÊÆñ¦‰ù´xÈ/ýˆÿܲJ#~Èγëi6³o\²%ð¾—îUǨÅbþÜžàR˜ÂÒ½á\®Æ!Hsû+.…y!ÄßHø—«ß·W¸-ìgvñŸì  ×ÏŽ;8ž™!Ä_ýü)}‹ûßDñ¸ã¶0/„ø¤Á¯æ ŽÛYÁš!8»aþ –nþÑDðhòü‹•ÿXÂg+!ÎH7ø‹6îjþd4Æuòâä-.…á™ÂÙw°ߺxCéÞÓÒýÐHendstream endobj 49 0 obj<]/Filter/FlateDecode/Width 498/Height 239/BitsPerComponent 1/Length 1498 >>stream x훿nGÆ ¤P©0 2­ )\ðò€461‚ ¾"…J)]ðR¸paë@…J>€ ¦c‘‚Dð(Ÿ¢Í|svænæ$’AB&·ÅÝg~»ßÌîã.íÓ.pŒ¾uþ†Ô ©Û"ÿð¶™i‰›Î¾#ž»³ß/2mìø.Ÿ¢m rßůp9TêÃ…ðÅ#jG1âö(k“_ ‰§â™ÅîEྋ‡CYÓl<Äw'&1°¥æºxxBÑ3q<ñ°5ͳ‡‡†¥ŽÃÅ[j¦ø@vO(z&ƒ'¶¦ÙâaöðаԱq¸xâaKÍÈîá EÏÄaðÄÃÖ4[<Ì–:6OÍ+WÝL6¯X)Ü߇ÉÛ8\<ñ ÷Ó3Ô:>¡­‹Lý~¯ûÈ»ÊoQmfcº¸ë~‹C¹ŽÙv©¶Í—ÀC^ϧt‘‘G¹m¸ ý‘e9»T{4àg—›–qª‰]œÄCÏ®Ä[øDŠçZ—#& ïˆ' 3‰SŽ¤ç¡±“J%>ôâñ7-ü'àÇåÂ-';²»é…“ÑÑ@-¼cm\¼rá®;“Õe¨u2”ô&/MúäºKg?uîº'.ÞÂ)¼Q”´U[ÃÃidÑê€û^|Çq­›”ç}vR–Ô ñ\m¨&ªrÁÅjš/qœV_sˆ Ž2u\Š˜MiÈ&vÆCõZ8oãrÝëJ$ÎÕæ¨œ=ääÿŒãLâd±âW£—å;m·TJñ+àTi_JœÅÿP¾”æã¶x‰/F%Nέ؟•¯…ü^,µ«bu&q^¸‹rá–G4~¼ŒñEÌ.¤x)…Â;ÖÆe¿÷ü9±ovÞÇà)˼˜êÈ@±»¿aÙÀ¸ërßyïYeh¼9¶½D…} ëÎâÿÆØ×eJŽz¯36F™´Nêø8Yö•<°ý¸Ù Õ‹9¹ÄËBiv¸xVáRãôþ×_.ää¼÷êmÃ8‰Guô7­Â¥xþ™Kø¼’ïà÷.œÂeì±9ùpñR''W±'.ÞU¸ÿ0>stream xíšÉÂ0 EC%”êÖÒ‰[È#ìeA“™çƒ9xÂãëÛ“ rZéN[I„µ¶Á„¼<4á‰ÄãßðjæåVï¡ÇšTb‹ÝKLËx_ÉBÚ>k“å!¥“ÆŽ|D²^_jÖ‚—ÉÅD&ÐÄÿiÂÙeï«K¿#¾ªyž,ݵeï˜ì˜y`ƒ.å?aÀ«Ì%s_µã³Xg’føÄÊÛàeêHÖÑö’>Áw4{X§{|u/ô/µOöZöÚïÏ'ðU|Ÿ¨ùÁ»Ë„¬‚(5{ÇÞ½_55­;ß§As}‡|äùjy);+hM¾Š¯ª+p6Ù{6ÁWÉ!ŸCK¾Úº`ÕÍšÿèÚGê1·¬¢Ÿe­m á/OMx"ñ^1? /O$Ã+æãgáå‰ÄcxÅ|ü쩼^BJw›endstream endobj 51 0 obj<]/Filter/FlateDecode/Width 604/Height 183/BitsPerComponent 1/Length 322 >>stream xíšÉÂ0 EC%”êÖÒ‰[È#ìeA“™çƒ9xÂãëÛ“ rZéN[I„µ¶Á„¼<4á‰ÄãßðjæåVï¡ÇšTb‹ÝKLËx_ÉBÚ>k“å!¥“ÆŽ|D²^_jÖ‚—ÉÅD&ÐÄÿiÂÙeï«K¿#¾ªyž,ݵeï˜ì˜y`ƒ.å?aÀ«Ì%s_µã³Xg’føÄÊÛàeêHÖÑö’>Áw4{X§{|u/ô/µOöZöÚïÏ'ðU|Ÿ¨ùÁ»Ë„¬‚(5{ÇÞ½_55­;ß§As}‡|äùjy);+hM¾Š¯ª+p6Ù{6ÁWÉ!ŸCK¾Úº`ÕÍšÿèÚGê1·¬¢Ÿe­m á/OMx"ñ^1? /O$Ã+æãgáå‰ÄcxÅ|ü쩼^BJw›endstream endobj 52 0 obj<>stream xí—Á Â0 E»#1Ý,£Ð à‚`»©H«¬6Ô]/Filter/FlateDecode/Width 628/Height 183/BitsPerComponent 1/Length 322 >>stream xí—Á Â0 E»#1Ý,£Ð à‚`»©H«¬6Ô>stream xí’± Â0E 5k¤CÙ‚ ÎJš°¢f¢PP2…)03P8n¬ÜY˜ôè»ñÙ~þ¶žÍ,Û˜Ïääó[ Ý‹×O‚ZŸA+µKg³‡=-gó¢‡"âfveWWG­9UÏé5ï#¢›’'ʇ1M’pƒg‹ËÚn¶“dFX Pø^ß4,t¡Hv@âÒ€®¿¿‹d@Hþ'Ð````````þÈÀ雺endstream endobj 55 0 obj<]/Filter/FlateDecode/Width 270/Height 166/BitsPerComponent 1/Length 263 >>stream xûÿˆýßÿ÷²ÀÿÿP¸ÿžü·ÿ‰WÅßo@3þÖ£èB5ãï¿ÇËçþ­ K²G(BSñèîìÜß;z·#ƒ¦âÛÝݽ?lï™!Œ@sé_ ŠÜïwïî©âAÿy’7<îè“ÅiLâ/Œ¤Q] “ø càTñ˜  $8lU¨lìñ‚¬fTrhS2APÕñJÈûPõPƨ p@Œ†$=܇&‹Ñð `Œ–àd0šFsÃhn¸Iÿé”6rê¡-¥ÀA§x uphß“ÿé•FC}4ÔGÛc£y£½yH:õ>áV4ÂQendstream endobj 56 0 obj<>stream xíšAkA€ÅžA° ‡ª9ôЛ˜Km)5W‚·=ÔsPד$úŠäèÍ“vEi²"˜cOêdwM#õ“%F²)Ëìsf³›Ýî@¤×7„Ì{o¾y _²§€x\&óEê3x¡ 5=^#û?¡p p4pÚJb9‰²1ïÊx7Ó÷8j+»2¿ûæ5#[Ûë—kwàþe{¤çÄÆéHu=2w‹Þ‡šNÞµ­áj_·û{vBê¬ll~ªM6­UöÖ´¼Êo×îìij´èšæGvS Îm»¬×³QŸw Pßúf?©ôß”€‹[Ϻ՗×ýöÝWÏ—¯=ŽÎ²»FUé9i Zñ¢ =g¡Ý4eYh´&͈¢4`œÃÐ@h  4€Ð@h  4€Ð@h  4€Ð˜ßñ>½_!½¨Kñ>V̶2PÅ-„/gC>gtU ?ΆºÒö0íHeáUg ŽT Sù—](héÿ‰Úéݳ¬9‹‚@tµÓ¥YÖœEA P©”âL s5:×®z;9>CºüºÑÓ_ˆýE ¨Ú}¢­ñìð®Äõø'¬`ÂÉöeßü‡ù;<Ú„ âæÃ'G ÌƒFºX¢Ä§dLÅ¡ùàh»¸Qo¯OóøÛÑKÄ_"îRøÐÛ½GË Z‰¡iÄÑ{É®½Ã>½ÔèîȨ«—èƒ%ƒï³ô¬"Õ2ºN¶ó­BîÖZîBøèàÒ"-ïÓ-¹ë©œ£Õ¯ùúBgM;µ&Œø¯kendstream endobj 57 0 obj<]/Filter/FlateDecode/Width 338/Height 228/BitsPerComponent 1/Length 774 >>stream xíšÁkAÆÅŠ  z¨šCÞÄ^jK©¹z¼õ衞“‚B(È:{’€Dÿ€"9zó¤Ý¢4»"˜cOêv³M#õP“#™”u÷9³ÉLf·»0¡íAœ!dÞ›ùí——ow`—Y€Q» ¦Ð4ðá9è c4 –ß {¢° àº!L‹¨ß#ª>Qp nªª4^¿òµ¥åù‹¥;pÿú´Û52ôÀA‹¨î9k9o£dhowjÙ¦á6×]B¤V¿ç|(õk³þ§æ~`·¾ŽÒÑvœ÷þMжo»+F9 ˆjˆµ/î“Bó]+µ8ÿ°ª7Š/®;w_>›¾ú¸Ó=›MVe£> ¢½è›ñXí“ÐFaYÊæb½B•ÊëŒ|C|aÉCaõêˆþ·(óu÷YˆL}Nz|0«#æÆÜCdšC ±ÇM…*”Êå€r@9 P(”:b7Ø÷ÚêÁDÞf«DäU:ÎõÊÜRφÊå€r@9 P(þLrc(Ykö4Ð@^õ<ÚGÒµŽþ”WÝ•G-ytŒZÃM ¹n8Ú&g@æÊb/rφ¡*HmZ¹´€GMyÕÓAQbáoñ(Ù*ÑÁÁû¡²øUÓýØtK­& )o!|:*" }Ó¿•‚î.TQ¨.&$¦¤ û è`Ä&Žžªš‚Š=&êFæ™É£0 ªntˆg&€¢±¡(0Ê(*ÙÒѾA$®x«Ò…-Åäu£§ßimv‰XPt›L°CG}›¨jØ#Ÿá˜va3î791¿;{‹° á™áÊ¡¨ïA%½”·µÀÖz6-š4‚îäÊ•ýùA>únù--˜ÒðÔp‘Yp°vÏ^©Ø…4ˆÚÕzž¨z°Ý´/T«qyûAËÖZ_yµœTûË3ÕlæÖ\æÜpéZ€íœ½²i/ÅUä-~ž)OÔçБ¹Ø€õz€ó9endstream endobj 58 0 obj<>stream xí“=KÃ@ÇÛ/ ¸‰-èâè&.ÅÝEý"‚CÛ¡b»Hq±c>€nBT®Æ€/¡ƒ"ˆƒÄtéOr¹{Ì‹¡rç¨ÛsÃ=÷<÷»?áø^bšŒ' gQUpá}ñ¢å lg%ÞyO4ßS?è?ñ;£—1”qj˜NI‚ jùÀ©¯¤2î,TÂðàb}bt‹ÎïÜÜj³;§E£ÀI~¤^’i—®•VÙʉõ1õ`î¹ZwŽè”éG2zé­ÑÕÙ±hÍ»òÎë–Gfè§n¨hÍ}%d#ÌPÓóôšºA_By@2”ŸîÇ©÷¦»X}òˆÞ ʈ§]t†Ë~¼;64>k´ÎsË)G›v[JMÛxcùA®ù ÍrUÑL¢ÌŽŠþe#DÑø'ióßЖ.ÔQ4€Ð@h  4€Ð@h  4€Ð@cà ëÏAendstream endobj 59 0 obj<]/Filter/FlateDecode/Width 337/Height 168/BitsPerComponent 1/Length 787 >>stream x혿kAÇ/•`'&K;± ’ÖF--DR¤§(˜×26¦ÌÁB8•wþXRè b!ç¹þ(c=‚îåöfžß7›ÙìÎ^r¹"Ä9Ø›ÙÙOoßÎ|vrD[y–œHð‰ˆzÓRHŸÂŸ>QoªÅƒDâßbU: ¥g2$¿G´öu ·´3ÛhÔBÔ®u•DaÅ'ú¹¨‘ð®Lt»OÞÜ9sê~xùá‡3‹ÕOÞBØky*@*ê[÷|#¼=z3ºñ²ò÷Ü—òãÆÌÊ%·FÅç&ÚrçFϯ£ën%œm¾k¾ž«4Ý a§¸”GgëÞíÆh¹é3ú>l´Ö T´Ü¯–õs¹qmú[Ó-λ¹äI¯4â›/?}±V˜Yü±àõîÕJFTuНHwÌv»úJ¨;f›Gã"šÎóh(²¨­€­À>U `|Fô2¬ —|¶E,ct|™A‹Ú ² D˜Å‚g±ƒ©í§¦vf0 ÜÖ:Ð?N@b èÕš\f´ ´ƒ¨rh)…f½!£‘kŸÅ‰ÅjWìA¬€)Ü*Ö‚f,5³ˆ¾ë@ü¤Õ2¬£Ã‚ÞÄ‘Ay‹É*ÇfÚ¢¶£Õ1)ê9˜¯iG´†AÌ×óU©xèºQhZÅë@£*Ð:P¥bo+*1šVq(1ú¨R1GåjFU/FUT+9}r•œë-+r‰£2š[†H@@“GÀ HÚ =f¢‚o«_TU¬$jQ;“}ÐçšIÀ*cOвÕík[Û É ° yWKl?µË`kw&a—qmBíW¥7Ó¯lmeÂÄÚ,xAˆš³6œ¥ÐÄÚ¼Yï:@ͨÊÚSH ±¶ºYí“«B«:W¶6£ê cúUn‹£&~ÝÍ žsÝ1jýT½ Í'ÀuMØ“³ðGrÕ¶<<`W¬m­ÛÖöQ0Lm®vödÙ¿¼X¦ö¶þó ñS‚ýÙÃ.˜ý¨€V°nwQñ?.Ì"endstream endobj 60 0 obj<>stream xí“1KÃPÇ­ƒààЭP±‚³C—~?€›ƒN‰”b«D©›ˆC‡ "Ž]”ŠRò:T‚_Á7‰ƒ‹±)󒜗'‘’\t»ƒüóîÝ#ü IEËДaÜ *ƒÁ:v'!Ã8T©"|Œš }0àUÈSVû\Uó1Tº¸ÕoßcT *8€-C_Žo•¡³Vñý«Á~¿Ö5:íò­×ðwG pqŸA¬.ªE]ênÉ(nò%]›š Ú]u­FÑ3zZ ?3Ϧ.¸¹eÃÜʹ·–µ#õ!¢unVݳåA/†.Ž¿Ñkfœo<™Û’›Z·‘ý€¨à´rN~¾ÌÔJGöìôbP.¼\¶Îòé­ªÇ@IX Ÿaã§Æ¼&wB毨£Ð¦§NILØšŒÒoBÉø'S©Ê%_?5ȶ„’2@È d€ 2@È d€ 2@Èø_ãÒÅ©endstream endobj 61 0 obj<]/Filter/FlateDecode/Width 337/Height 168/BitsPerComponent 1/Length 1020 >>stream x혱kqÇ[‹ƒH·BÅœº¸tÈŸààPpÐ))Rl•(ç&âÐÁ¡‚HÆ.JÅ)½ JpÊn(W)´”𦥴—är÷ü¾ßï÷â/—¶qZz÷»Ë»O^Þïåwßû&D²Å·É #~µOñäQ÷^½Ž<ŒD/Ô¨†èGì(ô7Qž¶B¢ð ‘7×Ψë6µÃÑ]F Ah5Œ<ÚYèzwºÓùðíYe®˜_^šú¼·Ðy²ƒÞ@¿—ïÔƒG“¹0×pò“ê·rÁ«Rv•²_ÐFyar/¿’íæ~yyœ—rA½ôp•®~:¯7ËåÇa® t¾^zôéjø|%‰F \ÖèG/ÿîþÏÒlX/e‹ ƒÄÕÅÑêøõéîå9çåòÒ•K7»S…Å·ãɬê54 ›‡½ébèmV_%¨ãP´ªPãž:“ᘬr)yLÑ´iþSFÛ¨Ü}•Ä…‘—|ìMì,J‹q ¦hÚsÖ ¬â/íLHñ5kiwl!H‚U´ր†ˆÆ㦅r0ôLÐôÑ(ù ¢ß_`tY#D;ˆö¡Û¥¼¿Éh4ÆiÛK ›‚z@ÉÊ(—EÕ›c¤' ²*”§u*ªæÚC¹Ö³*TÕÊ}ÝÚåi¹Vûª¶8:\+¦ýâªKRp‡H±4cV_‰Ö%.è¾ÖL_ÛIt |/ÔÅž¢iÎD"ªaQ²d`½²:èÍÇOÄ-­#M¬WŸkme} ½v¿ïUêTZ`ôÐd% d«v ”<ÜEÉÚ‡úV um´!Y]d-Xh„¬1£³’uÍFíåâÞ´*)Qk(è˜d•fEÉiÉ´zYeÕmeÐ,Ǫuh‹ketñL¬B­úY€U+ñ´µžÍ±•ãÕ>/ÓrÑá´Ö´ÿÜ ,m ¡.ýê’Áë@]ŒÖÏò–0ÐZµÿ*!‹¦6ÐÐßÒ€uš¥P%ð;@µN Û¥` t´N ›‚z@Ö(?aÏBÄY+‚ªZ{¨6Ð'dU¨1Ð5c ºVøi¨ 4 pRÕÖ¨âŠE­å8Dµ}¨a;?w¬¯ Lä8gˆà£µ×f¶–6Eõ›ªñÚ¾ñÚæo Ü Øô_ Úk5^[gU¶Ú¶º,ºÚkKÖCS€ŠúV uQUQІ -X(+©öÚ‚Ê_4ü„è3ûŒòG©ÇŽšVÅdUZn×ÊmQè˜d•fEHЗõH¦Õk£Úk㳫Öu Úk#h¼¶6^\u –ã)RüQ‰½qendstream endobj 62 0 obj<>stream xí–1Â0Q¤ä <…§Ù?Ã?@dYã8A#¥Bë&–|qqç™»ð„+°½µÎu¬™€Øó@î/äéNêS‚ôS×+Fè 9~"AH%`'Œ6¯H½Ñ’š[q. Ÿ;¨1q0Ýh7%oÜgÓ)À¦³Ç…¯=Þ–‚©lºoÃÃs.°Ÿý´œìsYRi@e©ãšMgÌm:‘ò“é„éÊð°Á¾©­m¨|ÕtÓ£¶»yÛwžþ]/Filter/FlateDecode/Width 289/Height 125/BitsPerComponent 4/Length 529 >>stream xíš]rà „i† ø ¾B/Àƒï¦‚èÀb1ö¸Ó$MóñàÀJÆx³âGI ½l¥¤ÞŽs ;„êaníâÌG@½K<’t™Ìz g»¹\nk+fN­¹®øìÅúŽ (C[Ž4”YÐrëŒÄ‚òSê0´ÅΉTC0Ôݯ¶h¨°%,8ÊÄV\­eJYüfE?`èÝ’H0%\f’Ìœ.uªÉ'×ëÌòäAèã²1«ÃÐŒÅ`HÙ˜ÕahÆŠbI'v3 ØÏü›´ÃÍ›¿ûG2h™’Ô3z$¬ŠDHÇX È…Mþ»mòcÿöëºv çÖBCM $¬Ê¢ËïeYl?ÖVlÞdûѧ‰ZûoKG::“Ø+‹ƒiŸsFŽÎý÷IN®#›¾Cž“¡‘ß‚!ÏɈÀÐȇoù šLãÁüˆs`ìöÔ?í=¸CíciÅ^ µæ²TàÁ +tvr…!ê³JÆ-T+è±µÛÐjbøa~ ¡!44¤Ôîc$ʈ²'G™Al(Dö‡–_Í í8¹%: §êéÌáÑv¾²3Æaèõ’…!Øè/qòÂÒ™Ó%€ Z¦ôÑ?³…e Û#¹Nã»t€ØÚmh 51°ºÓšˆ2¢Œ(6Hì³ ^lÇ(G‚¿qêø«Øv2endstream endobj 64 0 obj<>stream xì}Tiö5NãîÖ¸ q÷™ø$™xˆ!Á î,H€B\'žÉÄÝ=@‚;´ÒX¿ûUÉÈî{{Î{ï?»›:÷T]]z»î¯®÷ï= ò¾¥á!ÞÐ0¦A¦¯Ÿ7ÜG›ÇâÄå ÁòÞ lM¬>4LNC<¼rŸ¼þ!^Çâñè<^Ï0>Äc ÷ Ãb.—Û9ØßÉìä ÷Âb.Íâõ1yÜþá!˜†‡xýƒ<Î 9@Ð l‰‰5ŒÏÇàq‡ð ò9<‹Ç¢óX,»7§Ä V‡-X¼!o€ÏΗ÷-Ájc3L\8yù0'.Î. ®wäpÀO˜áuH¦Œòd`˜<]òw<ÂD®‡Ç°„KPo°7—ÇÅû“?Ó×OïûôÿŽÀ"4F£B7¶€ ‘›¾Ã¯ä—BZ° r ÂNH!¶£ÛƒÄÁ& é€QL1Œa p„‡AˆÉfð†éƒLö‹9ÔOⲆú†ñÔ?4 h‚i˜Çæ ƒDöóúyý¼¾A °ïŽ=4BÊehPóăíÈ£³1Ôôõñúà$¿Å %£W€ZÁU‘ƒÑËü× fd?#³ÃhCž“HDÙàÐèOÚÀbœ! †#‰t0Ëp`Ïß§ïø·æ!ƒ# !»#ÒAŠ!4x†ÅT Ýž¼a¹ÀÏiüH'&,OÆð3Ö‘' Äd¯Ãècõpè]ÌžÎÞÞî&£—ÝßÓ?Ð=Àéæ0x,Þ ‹7 "Æþ ^¸@Sê%æ€68Ãý2  (5ªÕpyÜaÎÀÐ_@ ‰†rcI®3Aaaæ?¼ã¥¿?ÍoOùûø;þæp;´4Ûþ«¶O(ü ðÄí+ÓÀ7jÿàñàÅcÀ¼7P]†A­f® ÃÚä<î ÖIúûXœþ^:«»›ÙÕCïaÑ}½¬¾¶žŽvW7·—>ÀîdƒžCîgòéà Þs æCýÌ¡Aúà@çÀ`TÀŠÁìf³zû¸ÌþAnßàPß·Ñ3ƒªoŒ¬1Crðç‰Xs|ñv †Œ\Î7DB° #Öî´8Œ3j¨cë  ËBºÎ(h@7þÙptŸß?¿sàß‹àáb÷ 65H½}TK!¡&$õÅB—×G M?è öƒ{Ü9 Å#š ØI@C<æð0“7ÐàƒuCX<6—Ça²{9¬ŽÞžÆÖ¶šº†·ë^}¨yù±æÕÛÖº ŸmõÌîV£ÁìbŠpéL.ƒÝÇàô1@êgÑûéÝý]]S- ] m-M-õ-­õíM]½m^Ìþ>À"Ðj@z±Fmøöƒ1áÿ: 4´oà…Ä™¯¿ƒ*°èë 0¶&$á9¹Ÿ±ùdiDÂÀ~¾:•H¥°èëÞ¿¾sà߉5}CÃl mH…ߨàC ŸÃ€6°chC újúG\šØILz.m0È a0}x˜Îãv÷÷‚]Î^Ðg8ý<&sÞÍn­onþð©îéÓw·n¾¼võù•Ë0uýJíý[MÏv~|ÕÛPÓÛÖÐÛÕLgtÚ0X=L0¸=ÜÞîþŽö¾¦&vÝz]]SÍÇÏ5ïê>¾¨yÿ¤îãÓ†/¯š[kZ»Û™Ý}ÌÞ~°Â@–±ØÂIbÛä«Öñ;¹Ã…¯P%áÛ&ƒ¯¿ÿ~Wð+ü4x4r XNz·FA;^à”Ƚºàˆ¾Gb¹bx'°G'GµMrÙ÷ùwü›q¤‰€ˆ¯Và ¡«Z$Þ€ ÏcðnbÄ’‘• ŸÌˆÅ4â’…¤Â>ôáÐ/ú‡A-br†»{X]­uu/½¹vöþ‘²eY7JÒn—gÝ9Ppó`Ù½“ÇŸ]»òæáý÷¯žiüü¹­¹¶¥¹¾¹¥©©©åKCËÇšæ—o>ýr÷^Í­«ooœy~åðË•¯T½¸{æã››µuÏ:>7ö´´±é½C#ÞTâ¼á„Ç´– ,ýß¾*g~5°Ò®?Çâ ±P@Èå€3$È<˜“_ ü™?ηh3Ên'±‰öí‘¿¿sàß…¤ä‘Y;òùû»ûÔ×Ä@ß Å·cø‰3R¶À#LªAxMÀg š{€Ggõwvvw6~©}|ûŅ×ò£Ë—f¬ðIZ蘼È!y±gUôö#iqçŠs¯­¼yñä½[—=¾ýìå£'Ï>{þðÕÓ‡oÝ{ûÖ›+—_=÷êÄá—U¹Êâ®åî:›±ýlnàµÊ„—Êž=8û¹áÅ—¶ÚfzGXRÃX›!Q‘¼òZÈ9¹|ôRÀÿÔŒ®üõ“Dü}l§„W| g8„‘ù Î|…tr àÏÈ`TáÁöžüH4PÎðûôÿNÀ†?èæý\ !ˆØ€ëŒ$ð¥&áiˆM÷w ö ‘^Y A|šQóú!Í$HxËˆðŠ€°`Àº8–Á3Àáö±èýƒ½Ü&wÉâ4©y÷àúó .æ…”øOÍ^b“9O/iŠR´‡h ³x~ô<Ï\ÿ'œ+M¾\wåDñå3ûn\}çÀß’p¿P3ŽaœôØ@$°€ÔÔŒ@ðC H ¯¯{Ñ5@ïâœI„¤z°/¯€%ØŠØ?­AT°HƒŒ@ŽvÌ1†zYìÎŽ–/o¿¿u귲ؓ1KŠ¢åÌUÏ™.½g’p¦/JñHBM™i–ñƒ]Îj‚­ÓJÃJZs<Ëÿl~Љ,ÿSé[.fn½šºåBôª3?œß:cÿLýª9ºU ÊæéeL¥ÆM×J_åR¾øÞ…Ò‡÷ÎÕ|~ÝÒÛÑÆd€Ûhä48}þ`õ|ýŠQò[‚óúªùÀ–ØwÁ8ЏýHsˆ oJB lCìØPC€ °çôú2ú\f«³×Çæ±™ÃlÆ ‡9ØÇ…À ÜßÌ>Èœ$îœo|ðicŸØw¨ù[ŠÓ÷“úÇ@0b@B xuÛ˜ìv»Moev4õ47tÔ6´}ªkzû¥åc}{]cw3˜'­ z;‹ÓÎäÂ&,B)Â.x¦ƒ¬wÈ–éætr¸­]½õµoŸ¾ºyæÉɼS ++ý½ö.ÔÌ"šë‡ò|Q*öNË™$•æ'‘à'?‘’:‹š»Ô¬x­]öRóŒ…FYó óí] —?C=o’Jñ…ÃSäO,÷Ë'ï€mÐ&G±õêgöFüz¦ìɃkjÞ4¶77tuõ°ÙL&›ËÍ¢ˆÍd±L‹ J@0Τ›„r>J„߆PVdú‡¸}CÈáá‚cë„ðR‡g`m€m Ç<îÑû˜¬~6›CgÐ;™Ý휮VfS}ǧ·íoŸ·¾~ÚôæyGÍf[ë ’ ñÖÇ‚”¼K¬,Võjþñ­üý—¿7àÆ·1‘Äúj ·oˆÎæÒÙœnfOGgscã‡/µO?xPÿñ^S̓Ïïn~ùp·¡öacýËææO-í àâmïîé¦sz™ öPw²yqÒ™67È£÷u39-õµŸžÜ||ºôVIÄ•¤eçƒ|N­5;¾Líø"ÙóE«f UÏ?ÿ“ÚÙÕšg×è\£w|µá‘5¦ÇÖYŸØ`w5Øûü6×ÓÎn´?¿ÁæüZ‹‹k-on°z¸ÞâÎ ½‹óUŽÍ”/™(•è&ìoͿĄïήUíyqë—·/´´6´t¶~iiê¡÷÷õ÷uuÓ›ÛY ¦±á+Ö¾SˆD[¤ÆUX€_ÀéËÀª<å‚Ò×Ý3€+ 8\ƒÓÛËea[>‚l‚Aj d& r†Ø=ÌVFÝÇ–gz^`êxñ°ãÍóΚô¦&V7äôõq@]A/8œ9=Ø7<yJßµš¿·\}?»?q€„xøŽ©ð³XœÞž®Žæ†Öºw o×>ºöñÖéwW«^_,ýKÉó3ùÏÎ<¿Xúòú¡7wÏ~xò[Í›'Ÿ?¾­ÿRÑ¡ööΞ:ƒµ &FWwoSCcã§÷µOï=:Wy63ø`À¼’öÅóu‹§QöM/òÜãŽÒœQºJrG)>(s‚pædÉô R‰Þ’1nâÑ.⩾ʻ]¤BhV(ÀS„Jqã‹5C™6¨ÔSàä\ÕK+MO­0Ï›©âA]ëªYµñFuñ­óÇ^?¹ó¹æMͧ·MŸkšß½kzñªùÍûžºzVS+³¥ g˜ÅÆ¡|pÞ€1jL`p‡°ƒ¥›„𣀷„Ã`ôô671Á2kkíhøÒQßÐÛÞNæ"Cx…™`Ë厄¬AÀŠ~V7§£‰Õð©óõÓ·—¹S¹ïZAÚ½ý¹/Oúpõ\íßêŸ=iþ𡽱¹ÍÅõ`£ÅhØ‘†3 ¾CÍŸîãï þöB²HÝ„£¯¯ÞÕÞÕð¥þõÓÚ7Þ_=õæÜþ7ÇòŸW¤ÜÍ ¹–²éZú†+é.gn¹œ·ëFyò½EO¯~ëò»§>¼~QWû¡¹¹±££­««£«»­«³µ£¥¹îýû7¾¼uýÑÙê ùñûw.É]êš:Q3ÍS&Ë]$×S†«`¢ŠwB!V(Œ†"i(Â…™£ #´SíÐF‘æâAúü[4ÐF5´V­£¢­(ÒøJùI”N*Ÿ$S>E1o¼b8Mì'=´ÔX:iÙÔ#)gJ²=qèñ¯ß¼ôèúÅ××o|¸qåí¯Þ6?S÷èYÓëwŒæÖf?TbxºŒòdԌ˔À[2 î\F»£­·¹±µöPÝëW¯>øðìyû—zvg£³{€:¶II¨Á.+ˆ r!“™ÑÓÞÓü¥ýã«æ§÷__:óK^Ö¾ ­á3}ҖϪصñRnêÃãUnÿÖôöMG}SoGƒÎa÷‘Þ N×þk•þÊ¿ýíöýÿÓ80ª«üñóÏ× k¶ÑX\áÈ… ‡ÕÓÑÚTû®îÙý/~{}®úvQÊÕ´ _ýoD¯¹¸sþ‰õ~¿lŸ|j›ßÑ-«¶Ï¨]z*e祢¤+ ~=}èæ¥Sn_þüþ»÷Ï?Ô¼zÿéå‡/>¿{ýþá½ç×/ß;uøâÞô¡ë÷®š¼w‘sÅ"›òzå“ÕÍÔ­žkT9Ǥt–é¾™FÕ LŽ.0>6Ïìè ³Ã“Lú˜ô2«ò°*¦éåYkåX«çØR³íT²U ½µ+fføÈdúJfz‹gxˆæ—/Ÿ¥_:Ï2o6m‡‹nЇÌuKª’£NfþRQxùpé/Åwù¥ äTvÁ•Ò÷Ož}yõ×ÏŸu~ªë¨kènhØ8ö©@ô‰Š d ôsY=Hïlëhø\ÿîͧç_?¸óèÆÕ‹Ç)/½püØ›'mº[XÝ ´Ød"J Ôᇠÿ y‰mož}¹sý~UùþÐÀèùÓvÏðŠœæ5Ë'eÕÂ1a×*Ë_üúë»'Okß×|ùÜÔÜÞÕIgõr8 .èRœ¾!0ÏþøÏ’ßÿüÿþ=—üõÙÿc¨üW×ÿ¿uÕÿSÇý¿uþÿöC¢ÌIåøˆé›§ü½ Ì@hìàNf°è½]­muoÀbzíØí²´ãQë2¹F{éÄ£¦yª¥ºÊÇÛ‰îñ¦¤“Šv q vW‰œb–ðƒgÒš™Õi¡ÇòâÏUä^;YqëÒñ;×Nݾ~æÞ•“OÎ}Iz%Y'ã÷¬žâcºÓN)Œ&fηSmQFÛÔ°Þ²]mÒB›4ÑVU¨Šv)¢ ´C íF;ÑV>ä϶‹¢`Y,‚•Q¤Ž@¼•Dîx•ÜÉ*9ã’ÅvÛðG˜ n×C«ÕÐ*‘ bèG=JÒ¢ÉylYVºëçýÁrW¯Xçè4YU}Í>wãæ«{‹ž<óúòµú'Ï^½m«­]ŒÉèu×a5åýC ¨$-µŸ?øøÛå×—NÜ>¼ï|aFQTPÒÖõ{c#=~ôýß^<ïiëèéêÅ&$»ÙÇ¥÷õAqV½»¹ésS훺'w`Û»ù‡Âýƒ':ÏU—Xc¤8‡‚Üø4ZëfS°cã•Ò¢¿œyuçÎÛ'Ok>~jljiíìéèe‚O 3°þùWÓˆËû”I}ŠðË|ýxëÁ=õOè=wò*¾Îÿ°ÚØWâ6Æf,QAÐë0^æØõŽ Æ6ý?gL¹„±mFÏóÛh$1[ã¿xÌjœq’.´bÆíFþ(~„:ìpá) !lÂêãuòÚ8¼6:«³³½§écÛ«5—Ë^Š?8³zƒ{åb³ótÍÖªž¥qpªrùÙý“e÷úŠeIuMò”‰õRØåJñ·—˜j‘»vzUĺê¸íGS‚Ïç%\*ɸZ#-èbèO'·Ì;´zâÞYÖq® ±vbé®Rñ6(‰†’íP¼-УɅY£Ý4þL'ÙxC¡5¦†¢¨üªüѪq"¡òh…(¢Ýš(^_0ÞP ÙT$&™j'žh+o-g)k!nÈ¿•Š~–G?É¢T´ËB.Á];Í×(ÑC7Ô^q‡©L¡JÆVµ Újv–ùK‰ºUXô úÈû›·[ÞÕ4Õ6Õ7´6u²ºúpÚÞ¡FÆp7K‰ù¤îâÑWåé÷„ÃE•®œ”1ß;v¶WÔ‚)•±¡OÎ|vãÚû×/>7}i¥C½…IofôÖµµ¾¯ûøêÕ£W®=¿T}÷@ú…$ÿŠ ÓÒ§[ƸRÃ,vÛP#l¨jöz 3=*v¬9“¶ûvUÙÝÓÇ^ܺùé͛ϵ µ_ÀµÝÛÙÛÏdpY 6¤æp¹¸Õ)ƒ8þb¨e#‹ÑXCÜ·—7·ÃH~æWÙ9ñ„ÃC‡|î`ºÿÃlB‚Ú?˜“’lîdÙÂIÊyî*ÙvJ@Åãt«&˜žhqd¢õ±ÉÖž†eãt*¼tO2>2Ÿr¢~¹¯&Ð>Í_­2_ݲ úe Š}tòÜ©YNJ™ÊI62Ñ&bá‘FDâ©örÙÎÊÉÆryv:Eîf¹îÙ¾öÅó&U®_^è-?ûæÁý.œõàþçÏõí=Œ–^öÇöž–ÞûÖž'OŸ?ºþËË3^V¤>LÝxs×¼_Öy_Øà{vã¤ÂÅî!¬v/4%ìþ™ªg÷o¼|õä݇ׯ?¼yùþ-г×Ï=¼óüÞõÇ—ŽÜ¬H?Ÿ¸¥Úæ¾r'êdº«¤Ú*¦Û¨$Y+ÅÚ(GÛS}Lóûî÷ÿñtRЉŒ¨_JsoŸ=öäÖÍ×Ï_à´·õôt‚’Äa3ú!D ð-A ;®˜%L`È „(t ‚bSvÏ tãø5Bl•“Dˆ6Þ¯3¦}ŒÆ@g dH¨ùAÚÔ@ß!ŒpE‰Ú=‚‘~à^ƒ/Dÿ@£z>¬€ÓÈFU#@i1è4@4rûæT¿ÝN~Âü;ÔÀ¿DNDôc쿆éQnÁÉîƒø²Uû ©„Iïîí©ob!ˆB×|~ùðÕÕ#W÷F–o›ž>×8m¢b¦¯t¦‡H¦JµEI(Þí6F‰4¬‡ÄÒÐn[mƒÂmP¨5 ¶ØE‹r“§²ÃNÖßFv—»füd³´i6QöÔz¢›TQžp¬•4˜<1¦Bq¦|i4‘T{‘4Ñ$‘x{¡X{ÁáGqÀ‡X#‰H]á(=±h]±0 Á`%¨ˆ¿,Ú¡ˆ"tbE¢ô‚¨h› ZÚ‹<ÚªŒ¶ª e´YÅKTP°‚|–(ÞF:H—XAª Ð|äÐBQ4G-TúÑP!jÁ„ü uçK³î\8òôÎÕWÏï½ÿðâCÝ»ÚÖº·5Ï_>¹üôjå½Ê¸SQ ³fë[cÇu¶ŸlÎ4j¤'e••Èé„U¾×+RnŸ9tÿÊù§·®½¸{óÅý[OïÜxxíÂsÇN¤M 9¸¼è§‰ùsìr'êçx«åº)Gëð…€Í¨†‚´Ð¾Mºü[,¤Ý4cæØÇ/ñÍݾ¤:#ìÊ¡¼Û窞Þ8EŸøü®¥å ØsŒÎvv7´ÛPáòl\_R@~A„àM ûسž|Ü*ùñOµ‚±ý|2KJ.9'ƒ¯ê¡oÖÈ*©µŒª_÷BüòíWrŒïj,þ¿Ûƒ ì ï§1KFN^ònÁnÆ&òrqJYJ ›’ Þ?™“ºÞ 2ßînl¿ÿ}ƒ¤!ÐÆÀŒ3#MðpÓNè«©2]=í]Ý­ Mõk?¾ùøòùÓß®Ü=uàZIò¹¤-Õ;gXãZ¹ÔòÐBƒòÔB_™UåHþÍó•nž~õôʭߎݺvð×S9Šv‹ù¡r«Gåj‹ª†•?¨W-ÑÊ™è*ºÙAèGS´ÑK¹8`Á‘äÐÓ9©WJóoÜwã@ñù‚ôʸ¢€u…þË ×ÎÙ»Ôoï\—½SÌó¼4sœ”öÐdK\ÔöXÉeXÉä:«ä¸©%9*Ƹ(Ç× tS ðÕ‹œc—öó䢠¥Õ©;oTf¾¸tüÅo7ß™U°Ý)c²M†I†‹VM)ÙT÷àÆ•Ç÷~{ñüÑ›·/Zë›:ZZÀ³m 9l¨YƒÿªÕà€4 sHÇÏ QÙÄFŠ3È2–èm‚Ý€±N”Ö’s\fû-ìÀø›ƒÿÞ²!”“QSêéþºœxtþ~#Â4"1…$X…„V˜“Éoµzrc—H3†[ذƒS#id£:ÔaИ§ˆ@¨±ý—F † t!Oò_1ÿáNÃU“CClV+½³©õKÍǧo_{zýĽóûï/=—ŸöKvòÙÔ¨sñç"ÖŸ üñôæ§Öø–϶¨˜iV1øbªaùdÃ’‰Å ‹Æåúèåùêç×+œ`ËÊ&个ì§Z讼ׅ’g/k/AB¼™R‚¹F’•F’Ʊ&”d …T• {µt{j†ƒ:h;™®ú@iÎú‰ŽZI.ª©ª©î* NŠ1vrѶraæÒ†¢ÁÆâ[4Ñz5´E  ›› …š‹…H†È„êK‡èIéJíÒ— 5”JuÔÌtÕÍñ0Ìv×MuT·UŒµUˆ³¡zê亩帩øèM2É™d’1Á(y¢YÒdËPÃ^ú!S­“—ùîݶ°*fý™ô‡¢7T†®:´øhÀ‚3³Noö=¼Ü¦lŽî±Å¦G—˜V.4.o’=Ý ÔUq«­L°›vÞ yó&.šRùÓœê5ó­˜V±È»|žké Û}“-н ŠÜµŠ]¨%Ž*E4Å"k¥;ͽ6ê9VªÙV*Ù6*Yv*é*ÉN*±Š»T¢\ÔCÕÔ`ÏésœË6-( YWå¿?qב¼¸ó•¹¿«|póÌã—_¿¹_ mu qg¶AµZ/‹û¦‚§šÉã®ÌcRõ­/åwPƒ…ð_›H¨@5$潑ÁSHà Þ-Æ#þ8ÿŠ*†Œ~xúݦäΰþ…0!-È7WÁ]3Bð+y<¼@Hï$´ý_ñfßbñ7;'A‡pÁê¸15Ì¿C f;ùp!7ž¨Ô,&ZòâÂÉÁÆ®ÎúÆ:ÈÒ_Ä™âÔC‰;+"×W†­+ñ_‘üÃÔÍÎfó©bÓ%Ñ\I´BmÒÀ:ÀUìúؤ€6ÀsV­@‹…Ñj´JÓ:9ªÞIå¿G€ =ƒÛ!Z Ek¢h*ŠTEÑBRGѺ’» âLã)121†Rq¦rØ,²UM±UI¥QÓ´3œ 2œ “íõci𑉶WuR²S‹´¥FÑÔ!:l¦è¯+½^]tUh‹ŽÄN#Ù SùP+ÅÝ4µpÅ0CùPJ¸‘B´™r¼µzM ìµ\O“\O£=ã Òœ4âm”b­íUA£ˆ3§„éˆn‡KSµT-–FËÐ<)´@ý *0GMBdÐR#Ê.«è‰.ÛLWéR)òÿ(‹–È¢å2h¥ú æÒh=è!Ṯeòh™2ßbyþERhž(úA­W Зܥ/¶KO(\Ÿ?ÑL ¢f1À"M«Žb¨(VE)‰Åi+…©Š‡RÅc äbŒåCô$v[)ÆÙ©§3Êo•êii¯d¥¹ƒ¦µ{–KÈ,ç]sÝcVNÚ°´,aÛÁœˆÃ… çŽÞùõä«×w?}~Qßú©ÙÚ3Hg ±ØƒÐÒž‰Tã©Â5YdS3R𰙀Ëþu¨!ü±¿WcF@f$η%àH0Ißêä˜Ô¯F•”¯H‡!¶%`«"£€c¢ÙAr$ê *@„Ü€HðÀsìÊÁ5e@¸ý#kœêcï¹ÐÿÞ°ApÄŽÀv7%lðð†7}@ Š@¸¯'ùß9‚ÿØ„k¡°žH¨’j—ÛÔÔðùýËgWÏŸÎKÌõ_;Ï#zªmìZÊ ·=ó|Ê—M=ºfæ‰5ÓŽ,öª˜E+™`R2Þ oœf¦“Z–“F¾§qÉD›’IvElKül ¼,òÜŒöz˜–·Þ?ÁºÜ׬dœa±»~‰›N±‹Fƒj¦¹t‚¶@¤ QB;)(@™o—º@¨¦`¸¶p”¡DŒ‰d¬¹t¬™l„±ÈT¶øvu±MJB`ì,F @NAêÅÐt„ƒÍ@3šÉ‡¦"4áå@0€¯ðÓ‚h!šÐ"~´B­M‘h+Ì(±ÙéPCPuÄ`ãT[L;Ý7ÓëlãÝæª!†”c¥ ÕP+j”ƒ^Œ«q„“a°­v˜³a‚-wªWù¼iG–ή^ UäžÅíŠýlË&ÑöO±Ïv5Øãf¼ÇÝ4ÕÑpïÇŠÙ~Ù^´¢I޾Ö%~ÖGf;^èzb¶õáI†‡ü´x(—»)ìs¤Òd÷˜I$ë FãZm“D 1ºªÁÊ’Áª Æ* þEèË…R´$¶iˆmÓ’Úª)ý³¢ â|q4OùI#waä&†|”ø¦èÊN1Ro¬’î_]zë—ã/ýöéý³––ºžÞ:ZwôÕç¥$+kAvˆè$äB`¯ Í7Phó/HÜp„_ƒ ß`÷,•YÅÚ   ù pfLý€c| 8 5Ì`Œ"„‹<éJÍ„ !ˆõ÷õsÙ[9Àe r{û [$‰º’Ä€Îõä°˜P¦ÆeÒ9Ð.ŸÙÍbt1{»X¸'$ Šá ñö‰›ÚâÞ×ð˜&Šhƒ;¢õpÁÂ…=ÿ'ÿΫ’ÿÅŸç8‡k1Ô "°§»³éûw=ž³ÉÃzÍ•ÅâE’h Z ‹J¡ùbhúA­E«%ÐIÜï ¸SV‚EÿZÍRð{¢5"hƒ$Ú©„Cv€Î#‚6K£pM¡8C©-á*”–pŒ®X¸–@”®p¨_°Ú¥¢ô…âÌÅ,%ã-%ã,¤ <”b¯šâ ªè$;4$×+‹mÑTÛ¦¯»ÝÀ`“¦ÎzªöZU­e2*s¥f"±HtžŠ„`°H\všv Q¸‘æªÔ%Ñ@Mé(c•Xsµkõ;m°§µDvjÜ8Æ’;5ùA=Û ê™ ú¡•Bh¥0ú¡…-Æó¹-àCKÄÑR ´@ÍD‹DÐrqâÚùÑbA´X-BèG´R­Ck¥ÐY´F -F?ò£`ð Á„Ð ´²€4ÐnªŠ¶¯dQœJ5Fqࢡ¢ÝZ(F‡?RS(BK&H]j§ªx† LؘÊ@1–„†f¯Ê^¼n¨ u›¦üzÉ-:ŠÛM4·›ê¬7ÔøÙXgµ©þM5W ‘†ú;çÌ>”œzçä™×o~xø¬ës¼H zŃS“è«ÂëdtÃÓˆåèD2ÉW¨„xGgþj"^–óÇ$ö@?y¨EÇb& „Ýû±‹ŽÒ åïl\4 ßà†ÄÑ8p\(æå`è $Ö…lHшCì€Üª‹Ã„þÔ°ÎÎiDZìþ~£òŠˆjW£Zß7vµ6÷¶v²{1„Ð=½Ý &Üù‘Ø€0POßÛÙÚÅeÝM­Þ^×ÔÑÔÖØÔÖÞÃìföu1ؽL.¼y ìMF<­ñ‰ì@2'@ãœ>ùß8ýÕ퀟ä-ù{ð'ã\ .‡ÝÓÃhüÒõúù‡ '®¦EZÿcé<ïâI6yn:Ô‘å­³ÇO7o‚n!ŸVñx²ñšû'hžjX5͸jªéÁifû§Z‚Ÿ¡`<˜$ûÇ›•xè툰K·VÈu¤æØ«ãð‘Tœ™l¼¹ØGñfHI°T€R˜& Óª…B@îôQ„‘@¤™ðFe´V­o0äô‚„òcå$4Ðdf"¾H`ž°Äi…Å’Šó„¤dafñIÎâ‡Át$2 ‰Íå}f)?Z#Ž~–@Ë@ÉAXÌÕE24 _u‹V¾‡NÞ8í GådkÅXC…HmÙ8C• šn–­þnCÅP-é]9ð&Å›«Á<ÓA?ÛÉ0ÕN7ÝN7ÛÉ`¯‡y¾›Q»a±Ù>óB/£b/ãR?óCSi•SmN±­œL;0ɶÜϪÄÛ¢ÈÓdßx³½žÕ“j-M“Úë,Sà(žeÍ—f‚Ò-P†…@ª™`ª¹X†•t*„ÆL¤‚4…wj‰*h‹éë‹„‰G˜J­W$ŒV)´D»µ3p¥@³`Œâǃy"‹)rsd¤½Ý…DÇS”—Ù¹flØvsÿ‘/7×ßyñêú®Ú/ô¶–®Ö¦ÞÎ6¨÷„¤ö JÆm€@'Óˆ·„4tþqg ´-êæöƒò p°P—>ÈñégAüØ,зDL9Ø= tH#Úò°û¸ðd Ð" ,! £A5Ð…”4¨EìTêµÀ4Ò»[z{DꛚkZ[šºÚ &×ÓG‡^½Œ¶ö¶ºÚ†·o™--­Þ×<~üñþÝwnCGëwï||üàK͇¦\µ)¬½½½PÊœ›3Õ·M$hc݌ŋá¿púK¨c¸÷ʃ÷, B(xá#³¿½‰[ó†õøÎ夈X?Ç3p¢‚Ê‘a,U䤒b'ì"“9Ž’ë¡ë.—ç&“ç"ï*Sà,“í •f+‘b+•b‘bé0”l!‘f!Â’b&šb)™n#“`&¦Ç—d#f$¨Íf,g£o§´Ûši& ¿¦YKfÙÉfØÉ$YKÄ[‰$Ð$“d3Ü”“âhòÑVr»-⬨)4°DÒÜÍcŒC-´·¨l7PÝi¤±U—ºNMqEj¥¼ô*E¹Uв0^"-²HŒ¡Z+-¤&kD3Q V—تÀ¦)šj¥è—j%Ÿb%›h.y8á:òp ijV­GŠüà8Š7”‡•wQ…µĶÊ! %¡%k o"›j¡˜ISÎvPÌ´—Ív¤@hüÞ{\åsœ)¹îй.òéÒÀŸ4[©tštеÄn#~€ÓDK™$ùD+©3±d+Ñt[‘ds­‡²h"ñÆ(JÅñ ¥ÙÈ&XHCX?ÊB,–&ìHIt”‹µ‡ ^œd²³\º;É9àO´%ü’<ô3í 3íÌuvéªj+joÕÕøIUi>EfEn>Uc³‹ÛÞuo7_½Á|öŠ]SÃëíâ1z†Y=D$¨„À%ˆà¤®B È6Ùò Äô‡ì/EŒ„"¾9 úØ8Ø~b ör ÞÉ£CºOÿT@W7d9³¸Pï18€«> mÚ €ÝºÇ·øCtJlgBÛz&Ô>á–blzo§µ‰ÑPÛööeýóõ¯6¿{ZÿæAÝË»5ÏîÖë/Ÿ?U¼ymÜÜ)áS½¢gùÄÎõ‹Ÿ?!qáäðI^YKÚ~+-þi^Ú³¼äÇÙ±2ÂOm_Q¼Ø/~¼Y„›N„ 5ÌA jâB”£Ç›ä,ñ=òÓ¯éAwócžUæ4^?Áys¿óù=^s=ÞðÓÑÑNü 2pdáÈ ³¤ÐýA*ÿ#¿ŽÁË·x¸@]€°žx}t^wËàÇg­—޼+I9³iÁ­‹lm¹÷Éé–‡}´÷9˃dc!ËM/ÑV9ÒP2HƒÂÖà £¢,K¥Õ\;j¦•R² 87¤MäÀ0I°’K³WLqP„œº(3ñK©[¹xš\¼è0xžèf‘\”¥d¸¥DŒ•ìnc)pã$šÉ@Bo·~Édã¢IF¹~ÙÞúÙÞ9^&9žfyžV{½hÅžNû|ìÊ&[NÐ+¯³×O?×K7ÙIm·•|¸…l²£Fœ­J”…|´¹D®!~0e.Ÿf¢œf¤’¨%¡$%™Qª(N“/^›o—<‚Xœ&JÒEi†(Û\ ÐN¢Ìrjªáá Ú¼©^j¼4*}5Ž×†щéF§fšœœa|d²Þá‰:@°°ÌG£ÄW'ÓU%ÁJ*ÚX4–PÔ Îp–c–eƒB†­,²ãŒ…“ˆ<ä4+¹,{% °¤vCžÁŸ`‹½%b–oedàÓcÚŸÞáµîmavÖÓé-5%B$¼q"0|À±‹Û'âéŒ`O2h*¿Çì4&œÌ°XC`Tð )´&j€–_¼ÞN^ã—ö{w^?üü`Ùƒ’üË©±Ç¢Ï&‡ÝÞ—öø`îíŠü{Gw¼xÍþô™ñ©–×ÙÃëìb×7÷ttvöuÁ{M; *øa ú™ôt¶t4×wÔ×t×}¢zßúàÞãCF†®Y™1oV´[½EIˆ£é.G£šÞvkí`šIˆ½E„ƒE„£Y¤Q˜N¸µz˜•J¼5ÜL*Ø@ ÚB4ÝQ.ÏS±d<µl’Nº«rÎxýâY6e \ö-öª\;ër¬ÿ“Ò´§‡‹[üʪyËnø2ÐÝ=ÄdAw.Ú‹ŒL8ÿahó-’üoÇ5ðjæà0öžu·÷×¾mýõìüøó;—åM¶.ð5J·SŒÖç h­Ä~/µÊ F1æ*Q&aúJÁ²;”Ew*Š«ˆBŽG€,ÿiä/Ž6 £uh­Z/„ÖAØ ‘´EÃM)a&rÁzbº¢;´!¿w»6 ¾pž` ®Ð-䯊~Cë%?¸AÔùC Ä£,ävÛ*Jã7½HKÕš2ëä„VŠb£` a,FØÁ 0ˆvÍç'È&#D3!À q"„ÍX Ö_ŽÐ>´UmA»dQŠ®h±Ò7õCžÔj/µj/ÕÞJÕ^ÊG}•OMR?;Uë · Ä+Æ)ô¥ž˜fxf–1Ì«ü42­…!ç0ßQjM ŒÇàÏM(—ÑÑ Ó‚8]"Z+ˆ6Š¢mÒ(DY$ZG:ÑX1Ù\)ÅB¹ÐÃ4ÛA?Þ”¤.³QF„äÏ|8R¶ˆvA b^ø+ÁŸgÈ ?„&@>€š"…ì g@[aÇtŸ£©Ñõ®±Þw7ìî¨g‚÷„ÕCÕz,³úYLxeèø%è=Þ¡CÈš(Bïeì½7.§$ò'3¬Ã„`»“ÝÓÐÛü©ûÓ‹¦7ž­8Q¸aÅN/‡`Çî–íô¶¸†N¦…Oµ÷oöÃüª”´Ëûöß>r´õÉó®×¯kï=hyýÞØU÷©ãsmÛç-uŸëÞ7Ô~h¬y[ÿö%üÚúìÙ»K—N$%†L™2[EÙ.VBdB!ˆ~r~4_ÍãÇÁ¸ap°@mWN¶¦VøYœ˜ípv.íèÃCãÕ«ýÔŽø)ñ‘¯ö‘=6^ñ £S“ ŽLÔ?<ÕôÀtëò9öKÇWnœ_¼ñÇ“)¡—Ksn<\ûü!£½…Mï] ^lÀÍÔÀCùqDr4s’Ò=…çø±3šgÊ0ø°úú9}=]к¶íίOKs«7,K÷¡íñ0K·×€Çh¢©t¼±8$ço¤`ßü)óà†»—‡€—!´š­åGþ 8‚h-Ú$Œ)›'Š5’ߦ,“MÑõ ØQ°FçÂmSã_.†6É£Ð|F…€—ÃH<AöÐOÀFiÌÿ墨³J­“†ÊŽË*Kl5P 4ÓÜf¡âiý³­öb ð¹¾ò?üzªéå]zãÇÞ–Ú®VhÎÓÜÓÖFomƒf˽Mô¦¶Îú/Ѓ±»±: 2Z›™m-¬öVh<Èéìbw´“mN!ªEoif´44¼~ÄúüzèË›–Û—¯fÅg,žàd±ÉD}4Ú %`¦´ÝLÞß‚²ÍFq‰Ô<ª€¯¼ÐzWÚO—€É^U¡· ÷<8PÔpíÜýC%÷ªJn*¾y¨èơ«•{®È¿Zšs­8óbVByÀ†°‰žKôT~P–^«©¸^ò“Œà €ð&9AÈ׃TUˆ$«%›N¯ÔÓ´ÊÏ šž`|ÀG»Ô] ßNReN%öÂ¥Ž"ܤŠi’{LD2MÅ TJ½Àò5HvÖ‹tÔßl¥<Þ)|Îä¤+)+€fq=ÍuÌÞŽþ~x™nß;xŠ i4‘ö%91£~÷åÀ´ú‹ã{–¿CtZù~|Ü \l&âwAj|çâô$HQÀ¹ }ì.Ôõr¡«£‘ÛT×|÷·³±Qq“ü-tÔ7ÉJ¬ãÿ î[PHÀÙ#“šò.‰–K¢Ÿ¥Ð6ENE1B»ƒ/ßVË©«B8¬Å# ÷¦" 2"*VT6ˆ!iÂ¥%¸ è0V‚–` šj*•n.“a!e% µ„I&bšx[0@¢Œ„RirzÞã®UàmXèkR4Þ´È×|›a‚­j’½JŠ£Jª4a&jD‰µ¤†*ëÊ…ê+E«Å˜j%ZA8ÆxM»ÈY·ÂÛäÐD«ªIÖû}÷ºiïŠ!Jª-”j*d:*‚{$ÙV|ÚáB»M¤Ã ¥ƒµDü¡œ¨Ã‚L›M²h½Z Îg"søƒ—G™!p6ÁpD¶+¡0u¥3¢ þH‚ÿà«ÐUmÀð…«bÚe_bh«¨+°¾À&1‚? (Z] QOÂY¦2àœÏ¶¤dZÊ$‹)º)£¡ ùlå<7­"/²›”øšùZBºàœ”RTí ýF>ÒbÒ±V*À¥ =™…0SµH X[ÝÃ$£7­ W­È‰V™+¦Ù½åQUAý­‹]/îw¾~Üøønóã{½o_´<¼ûüìÉ{G*ï?ôàDÕƒSÕOÏŸøtóJ׫nj/{ß?ï~ ž–W@]ï_ô¾ óú‡·__<÷èpEç˽×~¹žèé¸UK>BO)ÛJk'èi~³XCÑ+0«]•ãÝÕ"l•"íU Þ—µü6c© Ŝ鶧6ͬXáW¾Ì·t©oùÊ VO†^@¹ =Rfڥ̰Nn•>Ý:sºMæT«DoÚj€±\´µ\o°® $ŠG+Ř©&[kfÙëeÛi¸è—y›œd}h²M¹¯é^7Ý,Gµdk¹Jš|h™Ž ©4Ù8s±CA(È0’ ÖƒØåøË¤ù7É­“ýA\`¬øq!7 Áå®´ê´¸/ou5|è¤wÀ šá­^¸ï4ŽF<î!’H˜t'qr1™ÂºÁÿÐŒ& üîпƒ—Ñ5ðB 5ÃŒ~}¿g @e$ "þ=½,°mðŠð’#pÐãîµÍì–O¬ÚgÝOn>©(,[»,ÞÓ%ÒÂ(P]y‹¤È&~x¯D«EÐÏÐó·FmÖ 2“‚˜Qš‹ 8{Œ×84žZå­|jŠæÙé:'§jVú*—z)y)æ¸Q ª›m£GSÝc©˜añT¹šú>gí4c™L3J®b¡ƒZ™³f™›æ~h$î®Qæ¡^æ©Qá­Uî¥ ƒ}^e>Zå~:ÇfYŸg{d¶õþɦ%ã } Á/‘îBMsVKtP‚b%(YJ°UÿL´¹b˜‘ì6ªÈ>T­§n¤ˆ€ÍµANÔ_A$DM,JS,F_:Á˜´[2yDÔø RÏ;lw$†x ²Aب€v¨‹†éÊF*D*G(­“ÄÚl†Û* õ¡ëåøÖÊ£MÚ‚Aæ’qò`Ôx¨í÷Ãü9è­4Êí¾ª¥žJEžÊ{Üð üÉ·ÅüIÒ—ŠÓK3Æü)uÒÊ0`¡ä[+Ûcþ”»hT¸j–»ªcþxQ+¼5ʽñ`ŸõðÇœ6ÎÀp€hJೊ6—¨ù–?ä…ÖÉ ¬§Cv žD€¡D€)e›™âzseGýÄùã˶¯º”}·$ãnIÚ­½‰¿åÇÞ´Øw§Ÿù6“Ði´¤½Š·Ì;Ÿ¸õiyÒ»#yo«sß)xs¤ðeUÁ“Š=Ësîí˼˜¶ûpÈÖ¼UËφÿ~êçÅ~îÎf•n–'=-÷h‹å鉘JﵑÛë¤˜ë¦ Žñl5h.T ä§Sy\>^T¨°;ºö놉—~ö9½ÌýÈ"ÇCóìËgÛOµÌŸdœn=_½ /í 4w­GµKù]†’[5„p½Œ,Ú@á‡zÞõr ÛlUÛVä/ïH×\.„8Gï¸àñºÂy L*@_QH¸ŠÕSJÐSÕSó——ü™"1O‚ß[WöŸät&;ºæÁåÆú7Mí ½Œ.n? ¿‘‡xîK¤ËB>v³ƒN?Aò1”ißÉûßï a(A À’[ ã,Hê†4"¨ž#³àJ!O©¯³}°«} µ‘]÷Šóö6ýáÙÏgŠ/îÞ¼gÖ¸ÔqiözÑ:²)Fò©ú2ɺRÉÒiæò¶Jö*ð¸/ž¨s`º^õlãóL..²¼¶ÄúÊ|³³ÓõŽOÔªž¨þœ,9ˆ›D[É@Ð'Òœ’a£•e©•l ¥!µ[C:Râ­5óiZ…ŽÚÅÎ:¥nºû=õ*½ôxëVzë‚ÖºoµÔ[’óOα:·ÐîÌ|Ûêi&û'èî÷Ó¯œj^Ðüqê ~Àƒª¢Œ¤6+ ¬–FK…pÎíO`G€º¹¸|ÄœPÆ–ð£åD¨—t׬Fèg°hv(mo˜`°2¡¥­¸&B(X 2gÄB4%#ô¤cŒ¡ …d’…$è<™vŠ6Š`NFë‰@Vs‚¡D¬¡x¬d‚‰T*hvŠ WÀãoß­Óuªgžœü1þ\^`vzºî±I˜?€YöòЇøyD$Ì5?ÑêR@)†ÊÀŸ[­"í'2WÝ Ì`Xy¼´öy¨–z«ž¬rŽÅ¹…¶gæ[ÿ‘?îši Þx(åÀüQâëøüYý-ø°¾ üYJ¤2‚‡T_{`)ÿ)‚h*d*Ь1RZeD‰ð1Iœn™<ôp±MùJÛüùº™S 稖ÍW¯üQ÷È Ó³¯îð¹²c¹M¾Çö:ò“÷Á%ž¥‹Ü÷-ðØ;Ç=m¼m¸³é6šyø8·xo÷x»{ó<³}N&¥4­ƒÎºU®ÚÕºÕ¾U*&—N6*bðRä­±’þñyÖgt8¾ÀæÐLÓÓŒŽ/ÀÓÌ0‡ @Ö8õdGÅšBœ­Ò&ÁŸø¡§"˜K!Õ×ùÑ2¸"âzÁ®\.€w`þƒy»æÜI‡á7÷C¤>ô¡D›IAâ:(Mñ°—ÎaªqêU^êGü´NÖ9:U·zªæ©ÙÚ§æhœœI==ƒza®öµù†f꯶ß]±ÌC­pœFš#5ÒRi§ e³e‹ŽÂf9qPÖÂ}Ò-­D‚•Ń”E¶Hã¸ò(ªTtì$i§2 ¤"°,bÍDâŒÃuQtŠÅ9udf˜0k%Ð*¨zàÇI¿Ëgˆ;æpóÀ]"³Œî%€—|«…Ä~Z'$´CBh'ôEâ(X†?”"¸K–?òmä 5¨d–…ê^GÝB'½B'¨ÏÒ+óÐpØ7Ûƒ˜œ)2Ør‘‡€Ô~OU˜ƒ»0äèTàÎÑi§fkšC=9SõôLUàÏ•#ü)§Xê¡^0N ÊÆ#¬Tv+l6Pþl’ãÏZèŠL QV? „×Iþ„¨àj0BC©èþðA¶Rœ1ÿ_óPw”?PRñ•?|äÏr~UÂ"+…€Ö‹lÏÛ*0 „·©Èl¥Jo¤Š¯WÞ¨%î©U0Yg¯|Ålµ «®¬5¸¼ZóâRù‹Kå~Y,sö¹‹ËT¯®2¸¼ÂøÌ†U³ô*¦èAëø gj2g©a¨ §¸FCy…šÊHª”^G…"‹p5qhþ á_€.ÐJ(HÊrÑhe¦Ž¶ Í‚4Qœ¹Hº½l²d”ˆnn)åz|ÁÚ¸ƒh•P³RÓrì²#asl¾Œp-Âý°œŸµÜ B@ÿä~”ýãýP⪿ÏStÈ"°ñý€={vrE4ùG¥}nê¹NJùÞZûçXîm¾Û—=A+õG‡_rwÝ:œS{ë çÓS^{¯§m˜mX™PÁÀµWàÍ€ÀN OÇßjàÔþH$Ô€ÙùM8‰WšÔÎ@òdsËxC"¯³™W÷aøÅ#æµ oöæ\ \jÔ}sÍ÷Í4©šk%ùN²%Î2.²Õž ‡ÇÉV¹KW{Hõ‘;æ'wd‚ÜQ?ÉÅNM;=IâÜTÙ˳U®ÎÑ8=I¹ÜU:ß^:‹&o­jª¸Q›²DAb¶ŒÈ\qˆþÞÑâb»Tå#4•C¨2%ÑF)Ö.èÛÀ? ¾P ´M ßEàb/Ðf xðð| tÉ“ÇÕ 1†‚à†ÝEÅw#øQ¡œ*@E6HMeU5PEi+Ev³´ ¿¬Ü&)™µ¢âk„E7KHo—•ß!§¨ ¡ª!/)‡"àFR@IÔÿÅÜ{@ÙY]gÿïí½÷ÞË”;½ª$d:ÛC(*#4½÷Þ5’F½‹æÇ¸Ä .‰mâ8¶;.q„^$TèÆúÿÞ;²Œœ/|ÿûcu× „4gÏ9ûìò<ÏV’ÓET!E«CËvfL{Ëíse¶Ý%¦¹2 géèRÛ¾JÕîRÙ\¹b_™b®Dv Bub‰éäRÓ‰%ÆKõ'Vè^m~t¸Yó[û\¥ùÒ5ƱO¡«)×õ`Øö‡á&“æf­†X÷È"²š·OkÀ)Ú'‹á™·Ï6Ò7ˆ¨ðâ1˼}²†úpûD¥´O½ËÜðAû˜ ›L†¬}Œ÷«5¢oÑj,Ö­VÛv‡³Åãêr›-’ƒÐéÖÄ­IKWXÓvÇd'WG¾xSâáÕÖϯ1}åzËר^)<ºFxôZás7Ÿ¿Iú¥Oê¿r«ó 7»N|Ìq`…s÷B×X±£3i¢G¶²Á¨|À¬½Y&Ü(Û@ô}Öê¥Û¼ÚÆ€öFƒ°‰YØÂÏÚ.lFDÑ!lÆíEíDJåëù‚R90w<7;¾Ü1ÉD¶´0›N.Ò_¨:¶@~b‘üáåªÇ®Ð>¶ZóØÊ/\)ûòÕʯß`xâãÖo|Üù•kìGªU£)±c»Ù"Ü®[¨tNi/.„≒ß)QÝ«Ôn4™›ƒÁá‚ü©²‚ñ²œ©ŠÔtebº*4]íŸYè!7Ÿ×ÙÆÆ.1Q…ëLÈ:BwZ6¶¡Ø0]a¡f[hD'j4c˜*²ôm ÙzâÁþT¤;hòÚw8L NK‹ßÙð5:íõvk³ÛÙæó°º¾¨o0 ðHúÝÂHP¶3¥ß›±ìÎ7ìÌQïÊèæŠô{KtsŚ݅ª]ÅÊýeÚ# xZûˇ«5ÊS9Âh\˜L Ç©.”](=¾Xvj…êÑ+5­Ñ~vMÖ>ת°ÏßÜdyâ&dzÏÒßÚçS‚üAyŸRû ÑÔì eò&K3ã%ééJ–h`{ØyÔ>Y¼ì¬¬}TIɵO¾iÞ>íAÑ>IÑ>ÍžØ'èip[ëfŒÖæwµÈpEûô¹UCnÙH@37O%ŒcQÝtÒ0W` 'ªí-µŸ(—=Z%}|™ò+Ëå_¹Ròø ’/ß"ÿÚmÚ¯ÞjüÒ-ÖG¯·Ì-Röæ Û\Âz“°V#¶ yn``q0p/|ò¯wiðô]½…Á‘òÈhEpfIÞÙüBzq÷’Èî%1> â< ú t=yê6†yÅÅ#®œÒÎrÇd±eæ{®v,cž,ñÂV£ÏØ— ³ß¶ »Ùç˜_í¡ßî×#ž‡V¿§#øÿy8“¹" ‹Ï}Uòã+Œ'Vãm4s‹U‡VŽ]cßµcæêàîO»wÅç·ú‰¡†Ÿ>²ÿô¿KíâÜ«¯B¦ ¬[W#¢¯Åƒ^W“M|D'ri}ˆ“ÉF8Y´¬b ¢¸øâ­÷Þyýì…çŸ;÷Ô/ßøÙñŇ¿ÜÛtlÝm>qÕÞ5‹g—ì®J]Ü]¨>RiùÂêЉEÖÝÒ™\a:Wxh™ñäíñÅÊK-W=z…ös„4«Õ¹FþåkU_»AÿÕëŒ_ú˜ñÑÚ}¥²á„HI¦WK+¨r 4b®–©nRé>)HîÑqVßoÐouÙÛáüx_Ax¼,1^T†èÄôBßÎÅ>³‰ÇÍVØGËmc•pl£e–!˜Ý¹jâ™´ª7!ï 4_z¢²¾¨²;¬ìOÙsÜiWOÜÖ04ûtí="–¾˜½3hê{"Ö¾¨³;dëZû–=÷lÊ4“ÔíÉ3.u¨ò©pì/1Îæ)fr¤“Ia_g¸dŸ2ùPòCìsÕ%ûȰϽ*Õ:½Ž‡xÞ>¡EîREl²*Tè8Vì~¸*pªÂõÕUñ/¯ð¶ÊøÅ…ú¯,5þõJã·np].ý˜ìøµš“×™O^ï:rµ‡v@w¡v£[ø 4Ø,öÒ§ô“ùmúò’ FÕ6¹1joÏqvz{Ê<=e.xpôȆÊÜ£e$[g*ƒ;«‚ ð¢ÿ5cªÚ9»À=•Å@‚‡ÌQåj†s´IUoDÖ–rºÑ<çpž{0ÇÙ›°¶‡Œ­}[ó`î‰Û/í7bçÚyØS(™J‹'d¶@8¾ØøÐrëjõ\¹üÐBíž éÑeúS«M'®0è>r¥‰,àØ¦Ï~<|âÆè¾«¢SW¦f>¾ðTÍÝßÞ;ù“¿~üW?þá+/½Œ“ –j¸ßåÿ¼eáß9™yoó߸J54›D=ê·ßÿÂ;ïŸ>ûö3Ï^ø—ŸœûÞw¾1:xdÝ}Ë+»ÊsJ“Åቢð\Yd,¬˜  M'¸ŽVØN,t<´Ì}héèbóÁE:r‡½ <öáŠc9‹§–˹BñȪSË”§–êØ[å>¾Ì=SbcvJ¸¬EÉéÇ¥ž0ºÀàÇÈøO÷é%íÊvySÔÜ7ÓGîÌ3£ñ‹ÞïD•>Ÿˆãy^™8y]þÑ«R0,8N×H®v$W‡¾ =ß±\coTÝQ6ûe|¶YÄâÆ6sVñÆF]Ýê“w”°âæž°±Ã¯íòëz\Ê«¤Å(Fõ{Ce¯Wh· |öú„þ€0&“Òé\ÙtZ:‘f ]¤; tG»N,õî«$±ElÆ>ÒÙWpưϡEÊ#KÔG—)N¬T<¼Š¥:±Lqr™æÑUÖGV¹­ð\¶¥ËÚç©ô&©çŒYXTæíó Mñöç<\î_à™ZìÿHö鋨ñÆ­—íc­$*]²²= îûæîˆ±= íñk&£Æ>“Тz-Âd@7Ô÷ƒÒ ­:a"(Ù—§:Qªÿì"óCªÃÂÑ ÙÃkL'®²YeBJúÐ*çÑ5!ðÒˆN, ×E (l|R.|2«°AÞn<úî,‹â (@S¸PJ”7å™!’€Øì-r •y¦*ƒô ÷.Ší¬òÓn†®rpuüÔ5y‡WÅvVºÆ ½19ÿìI0O“H†ãšA‡|’V¿¤Ù-–¹@Vlo`Ùó0¿_sÂÚ6ãxA}Ôó0›¯OËvh(Nrì÷TfË´û™h¼î©ÒLÓað‰Å†“‹ ǪÕÇjN®°œZ8peljeîÀšŠ¾O\Õs÷mO>þ—OýëOÎ;E”ºªXu¥ž»þg+ ãȊ¸ŠÄ6‘£vy‰þù5_æ;ä_¡À¾õö»âÏ?÷ܯŸzúýø™Gùn_ÏÀ’êöL¢.äåÛ0àíÁšŽ¦­ÄÉûÒöÃ…ÞCž™´¥?(gpROT §©'!ôÄ„ñÕt±z,_6™/Ÿ«+?¶ÚüðÕîCËM{èö.0ì.5@âeLê' |S%ѱÂXGÌWc7€©£|/ɵQü¤=~ í€u Ýd¢€8=RïÆ°‰Æ°RáP‰•£µoEìè•é#«–…÷,ô‘1õ%•]aIOT1”Ô' ýap"œþ” (¿¡7ÆYBÖOÞÓôD4u6Ëg»WÞæVPø¼Ñj3´YÌ6k¯Ç9ð=½k‡C·M%P.®× T*:í²>¿z"nžÎ¡Å`È8w•vUÆò,!‘Õ•êëM ½qa¬P5ULB'+ïªÔî¬R]m}èÑ>s EûÌ–†sU„a)ýX¡gBza¬=æÛìø?؇¾ }öõV’zýÅNÀô–†KmØgïrºr©yûÌ-òÿ7öáÝŸ(°[úûì° › ­[ÑÑ·z”dëÌ’³¶Ó¤ï³˜†ö1¿kÌïðºòn‡Àáˆd2W1‘Nd„é"aO…|o©üá¶Ï¯ñ_j§ÊGÁ|W™™ÚN_Z?Zä/ ûQîÚ씯L®6)…Y¾IOH e8iÂá ¤LÔ‘÷Ä”x˜ìÒõ!€R7»%I‡_ÝCíÓ¶¹5m.ý½~«Zÿ T¹^?(•o”)”J7H…MR‘­P«‘Õj$5€ÿ³ q±Ê}‡WTf—J¬f£Xï•ÌÛ§Á'i É|"R±+© ?2ÄŒ¼ ”¥¢­ ࢊö¡r2œ& ÓöÇLùÞ½©£ËŠ‹öqÃûPû|<Û‰ÒƒàØö€ªûd­¿µO®ñÛGù;û³b"ÀÚ§;¬ivɲvŸ¦;d äkukÚ݆½f‡BU#H·dOÇcsÖ8 ðDÝ UoŽª5!4 Fc¾|í¾BýTB:•€¤Í׎§T;Ùuަ7&ëO(yv/ð];¹:owahWAdº 4’ïCªáõNá.³pM¬ç|Z!B˜€J6x•C¹®Á\{gTÛWσ †òÍCùÆÁ´Ì'{é È¡°uz%}È€Ä cqóhÂÔ–•í Ëɧþè~ɬ}ú¦Õcú¨çöÜZVö<ïU ë´3$;¼2j•ÐL8¢½AE'|:¯0‘íI ‡ ……ª‰%¾·£Ð»%ÏKÌwÿÊÅßù죯þû¯Þ`^N†R ˜¸?o›Gw‰§u9ŒF_±u.Õp蘽óö;ož}÷Í×^øÕ?ÿâï¿öÓÏŸüZwÛØš a‹ËÖjÔ6©%M¡Å$4X%µ@^û¥àÏ+›€ãÄ@úÁ™ÈÅ>éZ…< 9H« ½»@#"ú}½MØЙ¡Ù”pÚg«üsÕÁ]eÞ™BçTŽ…E=d±ÿ@I`®Ô½³Ê>½Øºg¹Ö‰ks©B\•ܳ4NÁ“Ê!­jÔùî†Á$¥%i ji²Ìw£è1ÈÄ&¿/¡Å×1 —'¸Ö,*­^YoT7œ¶ÿ“´ˆ@,Ý@Ò6ÂùL: »Ã–®™ú «Ýkl°©ê̪«®Ýëlq9­–‹¹Ñjj²™›ì¦F›q‹JB¼Á¬m´jMª:½|»·,¡MÏ÷ÆwxŸJrŸFŠ5îDW¼_V‘¬GüWÚÊYXõ]Jÿ ¦éVÚd¡?´Ïd®hŸ]î½%—íc¥¸ñ¡ö¡‡„×HËEô 2}ÍaÝ6‡ÀÂD `ŒƒŠ®¸ãüQû¤lädF—ì“pŒäxæíC ûPŸ¡ŠÕæ5×Û4ÛÍÊz‹ºÃkksš›¬úF‹ŽÏf›ÕhÓï°hÐí iˆ<ˆ"Ì'HèP…^§@¥½›$Ô-̤ô‡Ê\‡+]ûKm»Š “yêÉ|ål±ÀÞ óîrÛ¾²à¾ŠØÁ…©Wžºª ¡0€ß;—$'ª#HOÓªF«ÜËa+áGPG“‘EÄô+D»£*Þ|H‹GÔ¼ŽH•¹Uèpˆ±¤­?B¦¬nõ¨HŽ"»v(áì ™{B–®€‰Ãвµû,ìw‡EUoûÈçᨠW ­é¥ó€Ê"¿ˆæd¶Ù » ¼ž×&¹ÐjèA ¨‡TöäºZ £ ·eR{êw¼ðã:óì3ïœ??O{‹ÃÆþˆ«m re)ÊPÿ¥ÛôÞÛ¢ÚòÙ—ßxî§O}÷KOu Ýzu]yª!ákt›´’Q—iÔ¡´«Üê¾ ŽÊ0‚~àÙÉà@$8 gһʋæ”XRupùÂËÌ.(®(˜©ÌL•ç æGû’Þ´¿3ÇÕSàíȱÕú•ä>V€ÖDäxe&ÓfQÕ¿È5—±Ïæ˜'stGVzö­ÁÇVùO^•|øÚ¼“Ë9º*çË·,=zEþTi½©‡‚HždB"W{\*~^ ôâä88†â9»XdØ£l¤Cºe­>u“K]Ãè"4üÉv³\ŒÙ”âøhGâ¯óîÈÄ=!Ê}¿åmÁå7ðû!l‚C¤íÊ&‡ªÍ¥íðè»ý¦á¨c*Ç·³ ÖŸ$ŒuÆãô†¦ÊK§«Ëv-©Ø·rᾕ f–ŒWd¦+ 'Êò©uw§B=9ÁŽ\uÎöÜßÙG.óûöÙWŒ}¬³9ÆéÍÑ•žýËû–9ÿ}n]†óD‰¿/m¯s+qÈDS;îá³â.öË8¼¬q~gÓïÛÇ«Å>ÛŒRì@Îò_íÃ/òŸDL¸D¼ÄoÜÞî ?˜#HóëvÜôt r&–/.ï*Ë4äÇ›r‚q÷dÊ5³µ;duza‡NØ¡jµB‹Cè p…€ Ð)ÆÛLeDŽ£Ë㇗$-NÍrCº¦FÀá•™ÇoY;`¼8oŽýÎ÷úñ6ì”Òr~¿œ‡‡xØêÁ9|àÿOÇF®»kâãKf®,=ú±Ê£‹ öäúOFN……wv•Ff*âÓ•ÉÙŠÌh&ÙuDý]ñàPn|º¤pߪƒKîª.Ÿ.ÍLçfR4;žös¨ 4Q›,äûÚ#¦·Šó@ÎÕÑŽå¤wíÌ·“;FÕ39qžÈÄ瑌l0WJ¶iqW¥kw¥ŸÃöùk+þê†E®);uEÉÑ¥P²}Q3Ž‚µ•jžI¤qÚñœ ܨ6N‰£È]às«Y±É(ÅÏ®ƒ.­¼´š<úz§f»MYçP·ùô½Q}–žˆe(Ï:Qâ$ žwdyòà’Ø¾êО ÿÞ º¡½¥ÝEÞÙ7Ç\±o®,6[V0˜—Ó‹tÆB=Éh^r²¼`ß²êýËEW˜ˆËuÙ>¸Sì3o"Ñ>\®Øg³AŽ}°ÌíÓèÖ]¶O«ßÔuУ錙Pü«0Ób¦6rxYêТô¾Êô\izwIf¶¸p¶¼bϢœՋ;3%µÑt?4Ÿš*NO—ä 熃æ¿Í*áBuGô#¹¶éªb©úêÑ|Ól…-#ðWƒy†Á´n4ßÂoØ]:NœóÙ«*þòºE¬©:µªâàÒ‰Â(ß'A<ùͬ·¾Ñ¡©·©ìjzpÿ×çÇ·/?§#鈋ça 79SZt`ñ‚ƒK«wW•Ì”d¦ŠrÇò“ܗΈ·#âÎw2C‡a:£){/@§v“Yy—Q‰(Ù­éÈ“'½ö“½ýš8Œ ŨKþ?—»É6 >ÜÕ jÿÒy¨·ŸƒQûì/~öô“ßüÖÎŽ“Þ4y}ùÞªNÞT}…ÛŠð±Šè¡ßÉÒÈC•‰Ã¥Ñ]™ÀXÚƒµ›ýÆ­Nͧ®Æ¥ßæ4n±jPK@ŠÞK¸®W­WIy@ï¡;I±"Û; }À©Þa•x&]Ãi¹¸r]­>ª:ˆN~…¨&—¬SN+û"ߦ‹ª]LE¥Í#+¬ŒÂ³>P{xEá±…|«±]…þ]ÅAZcª‡¤öUÅgK‚€xÇò=ãùþÉ‚ eç™2üab,Ê ÍVæí®ÎìYP›f¶2gWUzÿâÌ‘ÅÇ®(=¼¼`ÿ’ôÞEÉCËsN\Yx|uhϽ }ûùàhïB}«ÄuQäxf,ûªü‡ªÃ¬åÙŒkRFPÓãÓ5ØõÛ­†ívcӴͪ_§‘Z’`¦½_+%Ù3w2¬lc…Ï,"ZÁ¥ëOzsí^ÙžŠÐ¾ê({Ç'VZ”»·21[ÚULë0¶¿2y°:µOxªÀ?žñ²& ýS%ä¿Äâ|Ap(‡êeÞžªÂ¹ê¢=U<¸y»+s³ö)9º²ä²}¯È?¹¦û0vjf™oz¹gçïL•kªØ9‘qL度s{2É>Ÿ»F£f¤ÕÔÙn•ÈîÖj Îo±éZÂîît 77€Šk0×KW½+j¢ãÜT}ÍÇ$0mûr5íIJ§$È  ;|Ò¡ˆne¢C9è"f>t¿ÇW”]^ppqzÿÂäÑå"ƒûĪ|ž›tè`n·ë ;Ñò¨u˜¶Zt(%ë²Ê5:%œA*9ÜnG: höQ!›‹XãÎḇ5˜uçÅîñZîJëjyá¾óÎËÏ?ó*®†âÏÖ|ÐÕ\ lĨæÝ÷ÞD:%Ó·Î&žùÅðõ/<öÅ]#î¾vty¢9ψº5„Äøê½äŒ½n øXÚ+FÉvêœYVø‡NYcÄÒq4zdå< ­.k³Ã\oÑî0©)q´ºŒd¸~óf…ˆê¯7JÀóÓÐÙÆójSð´WvUE¹Ú{ÅAqó.6ÌÁ©´Í US·QlámÒ Ûõ"ŽU§K†nL‹Uèö(z¼êñ„m®(¸·$z°"y|QþÃË‹Oá4–^œwpQ¾¸–Y^vüŠÊc«ª-/Ÿ[\ ‰fÏâü}Ë ö//<°²yðSW—º, Pj¤Ü„uOJۢΠÔk„&­Ða&¢–n§¦!«¢Õ«kòúS®]%1ÎáÊÔÑÅùlöÒyÈî÷À’ÎÃáe†ÃWT\Z±wQï»>°´øà²¢C+ŠŽ®*¦"Äy ¢(ï[œ­vÍTÚf*-eºá|åG:5TêH3Â6M {wÈÑìÐï0*¨ìuºL-=µ>®OCßäµ4íaÇ]é}VU –MK1'¦FKY³=á_óÔ­Yö;ýo?;ûꋨŸB‰úÁÕdk6b%–‚‘‚@øÂëï¿òÂ…§~ñÔßãkûwn¹¿éÚ%íÙ˜ÉH‹°#*âÉSê‘4ÕNQq®Û+ç s­ÚÊ.¿ $]&+mqHaÝ5_ÛÌÐÈmÉjÑŸá®Që¨ÑJjM2ô¯B$ìúƒf<<©q›WMVbª9 Ùî”rÒ¨ô¢mEa¯A›#‰@´3ñÎÆs܃1kÄ:³7Úä|öø mE¯OG –Zng«ZجýÀUeñ×oƒßpOvá!ïaŠÓo!ý ú©šB·¤Î˜˜õÔj¸Ýn¡'­Dbt•ïBå`b8_:èÒù¥$vKcZb0nq§G4K‹Ö§Œfw³YÖjUpì{½Æ‘˜³Ãc¬3IñH|ó ‚{¡Q°¥.ÊF¯J¬üàx5õ&Í@Ð>´ö-ÜGî‹™‹YúrÝÈluIé»aŸû³…j8ا™šjX·3ã&«ÌqG­ƒaópÔÞbS`ŸN%N4LÊSHaìÛñXpOª‹¥ H£|'„<.|W|r)æ)!üØj¾i‚XòbíÈ- ¤ÕÈÅë† C2„I»r¥ÝS‘{¸Ä‹Ü–ˆGòkzÜò^—|À­lÂm²S¹è™©q\ÁµÅô­1cKL×Ñ0Í¡! lãÒ­Íï$¼ä2›Åû%pØִ݄Š~=s9¡ñÄ CaãXÄÚaSŽ…=.¬¶Ógżt%ønÙ2G‘ÿG§E2Å~ùY\Þ¯Uþ×ýâÊŠ³Íï—žBJ9Ubš(ÖdbFŸ‘ €ÏÌ¡a8 ’¡?~écÌÕâ3n³ÈIÖˆg08ß ]ŽîFò÷lÉ 1„ Éf‹b£MJƶˆ ùÖ°»Åg¥ç…ƒêË ÕÅÜ·»tWÙµŸê|êÉ¿{í¹gοqùk6WC‰Îä[ï‰bÈY‚¶("ýëwß9óÚo^zþÝ_þôâ/þåýï>ñíÁÖ±–w¯,ž¼ªd¬Ê –IÉþ\õP¾~¤Àâb(Gß %G?ŽÔUНUó«3¬jñ*Èy©«“ð"ìFV5‘ î.#2ÏßYœÉ çF2¡¡¼@wÜ 4êð8“&úΞ©êÈ®%I¤kÛ⦎$‚ÃÞ‘bgÒRç–·ÛÀ¤q< ’ ÀldÙcMm )gFJw£Õi¨7(Ì^–j›Å\ï´'oñ;øéС…Dµv0lŠØ»<úv§¦Ûoˆ8û£Î¡x`$/:³°hçÊÒ©åùûÖ”2Ø€úN`0%¶–¸å™ö¤œfô ÷x\5”RqäèÀâGóDZrÌX=Íèñ|û`’ñ—ìCÇœù6<§\¼Ñ$ÚM'òûËrçª ¦KR#yb7’‰ðÙw·ú-={IŽ3ÄoÓ<ÚÖu{E™¾ÝSîoL›[Ó¤ÞFK¨‡;=bÝ`‡EAZô¨è\e›ìóulü·i‹AÕè±nsèîU‹5[Â$âI,Ðé5±Ú=æ6·©ÕCKE×ñôE½MN#«?æI‡{ÂvRª™’ÀžòÈÉ#‹rŽ.Î=¶(utarW¡g:ß6’Ö÷ÇÀ¨zã’~R¹\uOZÓdh¦†s2škÊ8§ \;Ñ(if ÝsåÁ©B×pŽu0éQccPÑÓlv1 1YO‰ud{fE´¯ÌÞ’Ô" ;Tè/ö‘LQ¹BœM’P3Ó‹UîùÛJêÁ}P)Ýa57¹\5&ã=R ûݤ—l·H)ö¶{õœV§[—%jiúBÎþˆ—Ø»ÉA#28”Žu†]Sw–†ÅhjaΑEyì÷xvËÙýZxsûãà"D@TZÌ•¦TàŽèž¦´¼>“öùóÀ@·v,#ž0Z,††p€ð# 08“yÑ=¥¹ûª v‹çš÷e ?Йp6MMqÛ‡)PRPì¡—d˜Ì˜[”»%n¿+æÜ²¼òG÷Ÿ{öé_þò—À†Q®»2_­!ªàŸ?Q冿çÝßÌ»ò¸¬„HËzÿÌ«Ÿ}æÂ÷¾õÇüMãæ¹ë—.Îí.ñÖ†¤pÐDò/ÍP¯Ð’tÆô‹Û# 0 ”C§ñ\dõl¡À—Ù'‹ÄäýA#ÈwA;̆§² 'Ó´@Šä§¥Kwo£Y²Ã£BZÁ²ÍQmWŽß‚RÃmyGËü >p5¼qí sgÂÖ5#ÅÀq‚¶ÏŸ@ÞJø4ôˆøZ!ãOæÁÙµc½ÙÔdåE³·z½Í«Þã 6²Q¯ :D Ãe÷“Êed‰µ“’`õAŒÞ(³ÞnTˆ²]·ðd»ä[Àf#{Eí¾Ma}Ò¨²×¯ìÊv²P”b5ºD¤™?ÖªSÅΙRqzöE!Êœ“E®‘¤c dí ÚF“¾é‚(=ˆ©‚8‹Cõ_ìIݶÒTí Ûžr û˜*.ù­ðtUùZ‹] 9榔©=mgX0Œ€vŸ‰Ðˆð;àØæÙG¿l›A·A!#Vy@%ß W> W¬Ãå>¡ !!Ú÷¼øÔ<¹§[´Š­zÕƒJ ——n ™/-'^öu”F³3~ÿ|\Ôl•òp7!\æ¡ íAIGTÞW"G6¿ZP°P¬Bï±€ oçC_tYi¸ãjÀA5GU-qeG¾=dQ޾ÒÞUné«vt›s4MIMGÊÔ•"Ʊ£@:™²J$ÝËž6Ç€uŸ\‚øâ~¥¢Æd®…þi³×9¬b$à7a(êºõfE A’†¤rjÉòæÎïW­ÜjÔã–yYt¬ð4ž°ÃÚOJÍþn¿ n 1IWBÆIÃ3[ØÁ/A´m~]º/¥Þ±Bçh¡m’±;eîz¶{ÕŒlŒcf(ýÅAæ…1Th 8À0èiû^݆ò;>÷ã¼üì³Y9†wP½ì^þ¤®†åüåï‹úôðÍÅè†8/½pñ©ÿ×GNmºâêå#Ìc½ºjïU%ƒežáb;tZ”UP˜-r拪’]1@àèó-㬞²3Ì’¦œ0ƒH¦z柉£+BôൢÚY‘ܳ0g߲ƒW2Lmº:8R ÜÎÚ•Ýø%ñ›âô>$iÓ2°Éê|ˆµÚ:s8ŠræEvåÙè·$\M1OcÈ»Éf½O­Aáá~¹~L·^aؤ1P¦k@a‡]×ì5ATˆ{GSA˜q½!w›ÛVgÖm3ëêœf@‰µ.kGnl{Ê·½$º­"vOÊ~gH·>¤ßàU­C4ϯ؞gÝœÖwVøw®õ÷(±N—ù86œ,Z$ISOXƒi’蔂˜¥®‚}˜¹IÈ׿W\²ÏxÙ>ô5.ÙÇ¥"Òû€} ¯,¤“2W*ò åÙ»iŠ«k|â°þ‰òàtUd¦î•¢Ô Ê—±Vl¡?a!ý~YxHh@%ñ Aã ÅóÊО”æb³Õ!0o š 埰Î.Ü…¾(q£ýEE9 º TºxÓÛ’î–ˆ½#áîM{R®¨§#ìêºk-::ZY Ö+åµ 2ÁûUb¾VO/Û­ë ZF£îɤ0h§<Ò`ToÕ)I]l¦Fý/KWÂC8AÕ.‹ŠJ'y#ÎÝz(rÝ1lcE^¶É1'PpÄ6™s$cg¿})SGD×PýÞ}A1¨Âm~˜ÅÙûò?;ó÷e¦:Ê8f˜¼>­Q× ;8œ;u¢Öñà+êýÕ~tæ•Wa|Óõ¾ìa.qÙùü/~“!‚Ü„«y•R‘Ayæ?bïÞ®›®»#ì»/ìÜuÔ„ µq¨6æ"HSA÷çØ€v˜v4šñ¥ÜpîpMãyžÝea‚ê…þÞ™jµ8mRc¢°”#øƒÚÂ,À9ÐyØê”àU6ñÕ]À±aWi¬XŒG ¼'Ò=gM•Æhì¬HXZzbMàù¹åÁÉ*goÆÔBíMd¾  |Ÿ^~‡\|ÅIÁÛÜ'S³Ö+Õ5&t%œ«y  9 9B§ÏÞòB@nt;ð0u~_]À¿Íç}Àm¿Ã¡½Þ,\ã–­ñJ«uB’›zqìïívÉU2¡ïÊ‚{SÆûâúu~J à@uÖùä(ïuÇÌ”˜èbŒåùÆr¼ÃI·ˆ‚ˆ£†çÀ>»ÊEûÌù!O]¶…#ƒõDY؇F*ö!ˆú}Äá |ÐkkÊzSšÑ"F$8™’9Α®q§Ë#SeQ¼7¥Ë‡VUŸ\V~´ºxOaj8î£öN&>™¼‰ÄAô0r1há&nYO\§¡®.©·Hñ3M6€@š3êf6|Ÿmܵ ´DbÕè6Ô8´ÚÕ˜”÷éeë´R¢£ ªM&ÍF£z£YBM[[ëÓÕ´¬m~õV¯r»WÑÒv$̃y.~²#Û®°Žw Š>¾@ƒGÂâÖZ³Ø¦f„d(±”¸ KðÌUú§ªÙ/ƒMÅv)ù‘E¥­ZxèŠò]K3“©¾<cÀH¨ ûnRit§ç“)b’,n7KŸŒ€n¹Ü‚KÛã1ôù,ãqo/} —©; ¦QÝAO³Ç†bÆf‡~ƒMs¿Qq¯ŽýÊî׉1áF“J\%2Œ5nu­OSÐÔòÍû•;¼ˆ¡É[Cºž¸|273MC.Ë¥û’=»Ë¢{Êcà1>x_þÏçÁ%Ùèl˜xªšG¢À3]è›ÈwC e |WIdc^à¶ühç]·=õO¾wîü[ç/00ór`ƒ«ù“yâqêEvꎘʽõöÙç^xæÉ'w\wýunç æ¥êeà6q©oF >+l•§“ƒ@—ùŒÄ xE€$bï 1(Õ¶N¯D˜ ’ë†Ê÷Î’Øþy#ù~Óý)§hÿBcSÈD†2ÄxyËf«ÃÓäM)Kƒ[Qk“n1‰8:À¸)ŽlfLÑL-4ØáhyVz®‰âÄ®E`yÑzâtd0éÕööEÝMn„‚&»m"ŠŸ4¼6dé¼w÷*åw«U÷˜ô÷ÚmwÛ­·7êר¤¥ !!"R!©®M>µìá­qƒM(„Îëª:¯«¨„êln…L ôOBwÀ½à~©:R~Í¢¸îâïš·^ûƒØov) gt¸Óñ\ßÎ’è¾ê$ž ÉС¹™(Î¥ƒqL¦‚Äxi| ÇÓ›pðFŒ&=@ –~¿¹•çR7Ú dÉ"É+ç×vƒ\œUJA„2ÊÄ¢%•ó¬â1sà8ûç“ÌÞ+àSpZ€þÎÿW~ÃüïÉ6²ÅÁâp”Èà©É8ôuNYƒSø“ZM_Ü@í¢/¡‡$2žk›-õÁcM¡‚•M$«uHP"ÞÃyà;¡^*fÁ*1•f|ùV‡¼Þ§%Ÿ"li¿ rÇóBCI/€Àá”EMŒP„ÏFGšF§r‡U*„‰x)MËD¶Ù4õ= \µŠ´¬ …“ ËÈÈ#Už™Ov÷ßì—ÏßÎ?ÎÑ8?”ßÝüÞü}1Jpò¸z õÿóóÀ}LnÎÐ"Œ‚h–w9Î` •íÖ€–¨ï¥PåÓñÇçö<ýÏÿ OáÍ7ß$¢˜÷0YOó'*×:¡à)ÎRÿÍo ¾ûæ[¯?ýì/¿ñÍík®ÞTQ^_RÔUQÐU–¨JŒ-MUGƪRcéá’ÔP!£:¢]‰Jè·lÕ«kšízuZº]+m4)X;y+‰ÉQ¿KýÙÑ*<Ù ¡bÕÒ” ¨xÑö,ŠZ™³wIspeüøÇr_‘ž©ð2é ¤yøç3/2 : r¨ú’ht»‘ÀT22ò·9­-N’5ÌvŸmOª^*"ô@§d»$b‰o&[s«7 -9DÈ.¯½;èm ù¶z÷ØLŸ1koÐÉWʄłø3*” ÅfaeFyëšðŽ»ŸÛô«¿š{ý›­+ Ü’4ª½ýÕ'Nl^šÞ¶0Þ¹83º´h|AÞ¼}Aç ´Ü­ÄtjâðyûlÓI) `ú;lŠ›Bt—ÀD¢_Â>bz(¾V-[6„¯­Lí]©pYzhMìÄJ  Žñ”vÑ-Ø¡ûŹÍöÑ(ˈuu…ˆ¾èò:Çbá‰t‰Zè(ùoê#ÆíAÝz°Áx Ê,ü(pÚÙÁúé<ô<$w­N]ƒE Áÿ$™éLá(ÄÊ•q½Ër¿ÃrŸÍ´Îb~ÀfÝäpmv29Ö’ˆ·¤íyéîÂÜþòÌpuñè¢âÑ…#•¹ƒe©þ¢xO~4§)dFÛŠø–6ðÝ‘c+2@S3X;¶4ýÈÊÌñe9»JˆœÍ?œ D´¼åÀ”Ÿ,õ^VY|”«+íÈ÷vÄ­«"¯ŠÔy r„."ün³0²ç?ÌØJ<"¡FF„ÙãÑ÷ûMMƒ²ÝIΞxôN[<Ö nûÛýv+›Ýà°³Ù·÷ûMv¦û+ò†Ž-,š¨ÎL”玖¤† â¿=®&·•Â×v£j»^‰{ùÃûò΃ûèŠÄC«ó3‡«Ô‡}zBšfÔÞhéRGÒˆ>ÿF™ð1ƒâÖLúx_ϳ?þ1:6ç™’ùÛjÞÕü ›lUXt2¢Ÿa‚(³½PC~îÅÿøÖßÝSQ}£ËyBö)µäNØ^umT»É¯„E{šE†²É¢¯uX¨­í°[:|žÞpp0ì xúüΡ k(ä 'KƒÞ/ $^ÜÞæÓчjö(ÑvæÌàêÉq ŒGõŽ)$ä "øÊ/–Ñh˜*ðî«JBœÌ QQ!ŒJ„éŒäPÏÈÉÝ&¨›¥†&™a« Ø,ȶJ5 Å&…t3iHVÝ—Î  ´ëV— ñ9ÊÔôÓûýÖ>¯£Ûçj÷{»Ò‰Í!÷­ví5Fa…VXBžënˆ«îXêlX[rhfíß>Þû¯ßßÿæs_½øü7ßøÁg‹eB h+Ÿúê¾ñuW"¦þ¤[¶!j¹Ó mÖê$TY7™Æ˜¶[,ufK§×ÓíÓïí3ºd¨‘#¹/âdëMnY³Ù ùÿj8w"'Teý|Ò½ùî“e‰ãÅ©¹dh6šŒ†Gâ‘¡Tb /§¯ Ü)‰f£^ w!ÜÛõ­)·kÒ;,¢@.m Šóܦ:ñ*Øć¨4‹¼ž I,²ùÄ5–pOÒ+L¸)¸5»™äbBže‡[/ª+…[dÂ'%b#>ñRÉgdÒùðæv…ìNú>‹~ƒÓ´ÉcÝì·ß©—Þ¡“ðy¯Q±ÞªÞbÁ¦é'dIv²×Ÿ/¸ûP@Iµ0ôÁM'Qh÷©hxí­JÏ•å…FsT8‘‘Tb,'5ž“;‘—"Æ~DwpDdà Ьç3K!0ã p¿¼Úƒ)ëT£º]¿µíƒöAy4£¸8ž4ކô“A˰ÃЦ–w mCƒÎ¸Y­[+S|F"ù„Dò)úûõ=%áµp§]¹!n} e¹+¬½Í+]RoK;Ú ƒ½ù‘th:“Þ_Y4]›)ް&óo:€îXå &I“Eä‰<«Ù®âgZg“WP¢¹[#!B 2ì5ïÛ¤’;•ŒíSÝ£S1-ë^½’߃ιhî)ãØ¸¤üfä¾çGnq`lb |¼04šë¥Üä¦;H7J5žkŸ) ëqÀ+é"Àe€…Ÿ±ÎªÝjÒnÔ¨ÖÉdÄ6ó­¢ûI!).#CVM¢Ôæ5')—y'r#»ŠÓ¬éLœµ«4Å~E|xq”ªûÊE¬‹i“I„x@jr(/í×mäéåkµò;U²ÛåâN³›•Þ&•~`¿ª{õrq¿\v­8«âŽe8±œ‡ùoU<JÉvr«J"ÞÅ¥ûòQÏŠ÷ĽÔ3صbî Ù‹`µÞo¹ßc½Æ¤ÍE»2‘üîÿêÌK/eÐÀÛbÞÄçü?ÿ‹ÕàìÍ_&Jþæ7o]dÚÊûÔjÞE%ðÕ3oþü—ÿtøØÐõ×£Ý^”ÓSgàÑpE¨¿8Г uä„Ú“ÁÎtšFå2¯§-àGVÅ˾ ·Óck6+Eo€4f¸!ò¶¦=¬@N£j8ß>žqçZ²ËQ8eGŽ­À':-A Zghtzd@ë©ùÍ"ÌÙˆíùyáåX!—Ý­Ròj“1¦c½±@{ÔÛ“Žðí8jλô žÇû6•ô­ân#Õ¶°¹%âl!oòzÇ©±¼ÿ¥oìÝre@¸:.ìkþø‹ßÙ{ñ¹¯uªàŽ”´}¡¿³8Üš 4E½­q?L–¶pãtƒínO·ß7ð‘ñ5™Õ@ ëèò˜¥0m¡0 3¨ìŠ/ÍÚ'Ç:o" žã~²À>ô‹ 莋Ý^m¯KßEZ©Dôg«L»A¦½MŠ5™âÍÍjõ§µÊ{ÝÖšt¨&^›´}Ü''Z[e–]‡ÕŸrJoÖ m…ñ‘ŠÌý:eY <‰Y™°BGG’.ð þÆމ¤s$&c°‘D>Ü÷¶ ±Á£kö›klšõzùzåÕà«‘Òú½šOXo°·jºÁ••¦»òÍ9±¦d¤%iI†êC®º€£9ä"`èIx€ï¢½Po“ ù‚`HoX †Y}³@¦Þa'x’»‘f’DoÔÊÔ)8„RÆë4ªû4¢Âð¯©#æéNúñŸ`¢ˆ|:ù+\&0ê[4RÀ´ÛôŠ­:9‹nüYö Ü«?î UJUv8l#¤™H¸GböËè=¼ :WìbW³=áÿv¿©{v¿±Òd_&>_:R¡N4Q®¯ƒD»ÛÑpÝÝË¥û¢ý[ö¾|Èy„ÑÊ1r@0ÕóÐc,©ue°Ô¦j,2º aû­§øÏeQ ×J…«ÍúŒDXŽ~ç‹_zõùçI æ‡ï^®Øüoûѧe] jä󮆊¿ý½3gßúÕ¿¿÷£þj{[Ï’E#‹+ºó£5ecH[ã”0Ä,1! ÷µˆdßnÒd 2bU-‹È­7íXPZxˆtÅ´4¶f«ådF,”«ôÄ”`JÇ0GüŒ(<å— Eõ£ ó@PÛíQõxµÝn=•nŸ£Ãë@½¶ÑçjŠÅ @QAOyA{A¬%/P›pnŠX¶åzK#›r<Ÿq«– Âr©°Z%\©®T «•Âra…T¸† Usn°/?Õäq? P¡¶½Ùë¸Ã¥Y‰o¼9ó­“ÛžûÑäùçöž}vç…§ßyyæÂËSï¼uøÅ—vŸ;}ôý3]|ás;÷À-é 0U³òù¿Ÿ¹øÊ×¾»ÓÖEÆ ¹òû]Ò{ȈE±ËÁ¢ €}øéoRHDû¨D .0æ®Kö1àd~gj¼NìCÙÓuÃá@'D3£‘@Õ{û60¯Á k³:º=¡¡hNW0¹ÝÜèô? nÍË©/+n¨*YŸÇuƒK¹X#”È„jRv{…côÞU·³±ïÆêÛÆOZ¤ßk®]g7oqÙx)wЧ¡Èv! XÖ«´Ï¯¼„â6tE}ËΨ•ºëüÅçîSÍØjPˆ€dÐÔ8;ü¾m1ÿgüÚ+QÛ¸ô—ßzûÕÃïž;pî•ñwÏN¾ÿúä/¾u~×k¯Í^8}ð½WO\|õ »çÞšUÎëâ¡ö«_ûÁ®‹Ï?úÞ¿ÌõݸF/ÜnH¹Abv‰}(_S­UJ°)öiq‰ö!1IYÆòl¨˜b™¾SÖ>Úþ´™¦ÛÜÂØÎRP¸¾™bït‘Œ1‘š]´ƒ©emu(ëÝúZ›~ƒF¹N¡Ü 3l³»7˜·*ÔâØh‰°J.,”ˆÓ±KȹH&,°WÅäw-õwÞ³äÔð=ß:Þüó¯L^|á[vÜsSXU%O˜Zñþ…I}N&þXñÙÎ.Q,žƒêX§“®é´(æHÊ#:„¢(ùξʜ½%‰=%©ýyûÐ7(LŽçDY qÆs“ùᘋ¾¹þÇAÈJ³bR*`  .ïÓHýÖî¤þf_Ò9ìdÅSd£l.ª@ºÏ–ͳ$ ý üÝj˜  z"t“Ø5›Ôˆ&TDN&õ^ƒj¾<ò ZI+A, ‹m}éýr)Æ jÍ  œ$h" °Úíꑈ}"å¡\CŃ5‘aŠz˜‰cû+rö§²ûÍd÷›þ½ýæEÿ`¿"ùÅoRFµA¼/´3Äû¢#¸’QxǹÁºŠš²÷ÅNÕåCσx>pà8ÌŸ´ž„ºÑ$[¯—nÐÊ7é”[´ÊÍjÙF…”Nôä-Ý ÍOÞ‚«ÈG­æ²«™G_Æÿ¯:üM(:Pb`÷ ¦(ŸŸ{û§?ùÖähcEéf )Ú²ˆ ð–5‡Ì¬®Êվ?!†©hŒå†ÇóàÆóÀ­€"ªF—ÙBxŽ)ÙQ©cu <Ûì‘¶ùd,~]!EéCÒ4™rìL{våv¥ý³)ÿÎD`*‹zC^.×=Z¦}©×y­wù,Ÿ´«×¥ ÍB‰AÈS Q™¦Tën[èܲbªë¦/«ùÇ'†~ôí‘ÿÝèϾ?ù³ïÿàë}G†n{põ®kõ§€¤†ÜMé膈óFt¥Oª]ñó|íÅÙÓ/O¼òbß»oLüúõÑ /ô==qæµo¼~à×§O\<÷¥¯NþÅÕ!aµ_8Õÿñ×~¸óâs§.žùüß·!#´ä[šÃTOÛ".bøÞÓÂ#¹ñËöA³û ¤.ÙšO½÷Cí#‡É#Xí~S_LOGgÒ×õwå&›SñÚpxS0P“JÞîµ_kQ]mS®°xÔ ½°Ä%\™TܾÔÓyß’ã£w~ëÑæŸ}{⥟¾ðï¼óÌç.¾ø×Oì«[í–…oÕM}bÍ 0Å,* ˜ê¤ÛŒòf»¦Ïg%¡ˆÒæ³M%œ£QÐ}¼%MóÁ\ÿxA̛ج1+YõfÍ5ô¾è¿Ñ^á“ö~—Õ0¶‡íÌ7i 8ØB'G"Гe$½Iowh“ŽV*v½Å Iy©¼ }‰œðx:4š ÇÄó€·ÙÈ_g×ÖùŒ›ªu& ‹ÚˆOfqžx°uj)`f¼=¸h®A^”j _ó¿<'¯$JVXº|f@5⮣Îñ¤˜Cµ{utÿ)C¢ÇÕôlÄØ¤À,q¿ZùV” Êõ´Å?l¿ª) R#Aþþ} å'òüSù¾‰<•EU›ö5WäÖV¤îÍ÷}2å¨tÊŠ\’QíÍKÃu÷/ß7¾ö[_éýåOæ~þ“™—_>|þü©W_;ðò«»Ï]8xîìþž9÷ʉ—~¾ÿ´î^·b]Bw½L¸îG]!º…Ù®^|jÿÛo?~îÌ«#ï^˜üõ™±7_:óÊøÙ3»Þ8sèׯ¼xî+ê®Xîn)‘|yÏ=¯ýËÜÅW¹øòCŸ>T·D³5"ßlÙ%š…8&¥)” xÖHÛÅÊöÎd퓯?uuá¡Õ9û—%ç‹ gíclñk>Ô>À?øcyÁ)92Œ~­Ýði³æZ­dFX¢J$B\(7 KýÂõ…š ×%û\ú•c[¿ýW­?râÅ_8÷ì±óÏ=÷ì‘óϹxæ _þÂOï¿&GXéŽÔÞòóãwFÌ÷XU›­H s•êt˜Ñõj¡Ã$ï´*[Œ2êf"zt±YÎ;œ -rÍ별ÂXôî©„4Ùu ”ûÓúŒíN5²D°¢MN³‰]° Ì£Ï¢yMZÁ¯l·©anA/‹Cñ‚AI|Ë‚)@î™=È¥Öh@H¶];ÒFb‘´Î.K•¦ÄUœo€ž<¨ˆLŠa° øv›y«Ù°Íb¤sŠ˜Éf½Rl=›ÕЉF9»nÁóØUfÅUö B…4ØdÛÔÂÆlcý÷ö+c¿Ê&»¡'èê»z`Õ95ì·Í¡âᾈˆš¬¤õÖÏp‡”É•C)dLR#yÆÿþ<`Þ—f‡–(?¯F-ß"—P¸ãÊ´› +ÀRÖ¥#×Z4׆½_}êûß}é?ÿ“¨fÞÉ\ö0—¿ø_ŒjHÑÄÉÛ—\͹‹¿9ñý ï¾ùú+Ï=óäß6÷×ôVM”æÁ>Fɳ^KËX ږܰsÞt„ 5&éV‹PJ—ã¥Ö¹Å¦!w¦•­19ò­#…®ÒÎÊZTËiYX˜wpAþ®’8-T„éÀo€ÏlÔHêU *i“Z¶C&`:˜­t¸Œï6+Ö…,·ŒWè…b‰îÅ.|‚ÊÍŸþÆçz~ùOûžû7úD‡ž{fîÌëGÏ]8túìžç_á•‘3çgΟyéÕ‰ÿxqêégvžþÅÜ ßýbó§¶d¬7…OyäUáÚŒò‘=÷¿öâÉ çNà—Μž|çü Ô»g¦^uŠ_¹púðû§¾øÊ_m¿>øé"yÃ'ßy¬þì¯_<÷—ï?{øâéGÖõ)í§’ÛD|ÙÕÐ4ùûìajÃÊHgJÝŠèD\{É>U¡±±›ð[ûä}Ð>°Ÿv8Õ¼à뺻íêëÔ"t°B",%´ÓËC²Û–êî¬o¼úÔÌÝßùbó¿}ü?~>óìì~õÅšÞ:{äí³‡Þ>søÝÓ‡ß{ùðÅ÷¾üì“c7 i™0´vÙ¯ø—=×TB$û  ØDŠ"Jµr¡E'tX å­“n6®K:ÜÖ§¹Ëeû‡ã‘þh°+ìëŽø‘oš(HS¨K‡zVÊ>\[1_pkx­Äó,]SÒ̬«éOQ§G&ˆHi áÅBC&îõz)f‡BhPsœ‡mˆÓîÌ>@T½ê­ò:§uY ¨²ÓÕ"Ŧ@“”ŽØ|æД̅HF f$¢[ƒÖÍâ‹ùÊ eÆMjÙ6­’(Ÿ†ŒIƒ •o•þ#n¸ Ùœ¸_‡µËå.í7èÇòS9Ó9cé”XÂùý›§NÂ[Ì~×fžÎC­YÒà”½ Äe´ú¾EþC+¢Ýä<€ê侨Î? Äûâ76ÒlR‹Ý«z´Q£ØNçW×¼?$ÇÜè¶|ª½1äÜ]ûà¿~óë/<õ«Ó§Oƒ«Á½\ö0—¿øßu5ï>ý†Š QÍëß?{ñ×gÞ½ðÊ+ÿyößöí};·”Òì®uwè$½Nõî\ÏgW•#`r•’ªœ=eœ%ÐÔI˜Èh"í®J‰ž¤ØÕ›£m‰ ­ÈPÇDN"“Yp |R@€Üã¦xû@ŸŠŒ­œRL‹UßjÑÏÆ#“~ï„Ï3 ì G½Þ>—£ÓãñÛ•n)ŒmÉ>éÓ,7 k|ò[+œSõWýdÍ‹ÿ¼çüÓG^ýÕžÓÏìá*]|çÔ…Ó{Ξ}ýÕ™3¯Lž}mú³»Þ9¿ïÍ7÷¾öë}/¾±ûÓû/¾|êâ¿ýÙÞú±ëËnr «½ÂÝ«C_{´ù¥çN>ûìÞW^Þóú+³ož™}ûåÉ_ŸÝuöÕ]çÎ:wú^åâ‹_º._XgµçÔ]xVÌž.¾ýøÅ×Ù¶LsìæJT³ö.D0*o®"¶$ÍÚS.jFaŸÙßÙGÒü¡ö±ÿÖ>°¨7¢&Áe× Àç8ùvu[Ȳɩ¾Û"Üb>¦–é…kcº»—Å[Ö®˜í¾õË5ýøïÆŸýÙÞs/{ëµão¼&V³_>½û̹9â´óçv;=õúË“g_ãù™‹ïtEù‘%ùû«rð¥»Ë“SŨ¥vW擃0äVŠ$}p>¼kv9uŒ™d`<à÷º9³¡à„Ç=â°3maÂ皉ûgI¬òD`5ÀÖ˜šñ¸[ýŠ N)ð vɃ6p4ËjjÊά šcèdTÉÀˆjjÍÆf§˜e»ËÆ–Z œN=_Ÿr«ETÙÐv@V°Ö¥ýÊTšû,•­•ÉÙ2hs‘»ªFH^gÖ°_xg¬^pD¼/™½•y»Js¦ ¬Ë÷e¶"6^äæif20†¶(:W¿_>p@Rhâ<Ôê%V•è ­ÊÝ ÷tÐ:é³Í„=³aÿ”Ç9f³Ù,»‚ž]‰àt~¬'«-ˆß–ôÖ^³â{Ÿ;õôÏúÒK/ÆÛüI(´°ÞÕƒ)Ѽqñý×.þúõ‹ï~ëÜ«/<ýü?~ÑúÊÒ±ÐDQn—ÛÌ‹6±'ìh ›†zÏ‘ F·`9€t€XЙ£ûÿXû¨6Ï{ï@B-$$±÷{cÀ{o;‰ëØIlj÷ÄØ›½‡Ø{cŒ·³›4M›&ímš6i›4iâ ¦&ü~¿W¤éí½÷»ç|ç«ÏSy” ‡÷ù?ÿñ—.ùÛ]ò§åØ7'ºÁØ«>Öµ1Þ½66‘(ZÀÃcÀ‚èD›óNÌbY¥¯ªÊ[Yé&+’ ò„œ"g‚LŸƒÔô(ÇþÇvŸÀî) }½ˆHà+]‰ó»cnÕ¼øÍG%–ï›HcÿÂXûÌHó‚±}ÎØ¢®4ëk&t‹¾~f¢yÊØl«5ÖO˜†,š‰'­†ñÚá¿”¯‘#o~Ttg=‰طÖ壷ó,†ÛúñÞ)sÿ”©Ã¢mÐ=,›54"dMÛ,Æ~ræm„šd. ö¦‹þðö•'Æ»ÚïZÈ™×Æÿ£äD¢cA”<×S 6Ø À¤!¨b‹Ð»[ÚŸÌ÷‡‰ýÉò·»h¿Eðò º^ïY©*ôsÊñääû8]P û¥ZîxÀÀØ*öWúÊKü\.û)ž—Øocϸҧ¸?Ÿø/ž“:@+KÉË”³1 ;/ƒ…è’B&&¾½,g>à»HäÎ8ÚÇ‘#å`AÜáÏ*¥Êî½Ö¨Ïû2Ç[ú¼T^dGCxÈ‘qÒ —<•À`ò²b/4ß΋ xFGbQ‹ç±W€SZÏ æGÐÊ øÞÌ\‡\?fc’eÉJ=îà¨þô< ¦Æã„ÜÏN4÷*üÕp®r•J ¥V.Û.ÛžÈÂû÷C NÖ—§\Ø»|¤¿î¬}øÙ'c¾Ÿ2éç§-”r8IÉ /P¹Æ¿ ô-ý¯ÿŽ_VèÔö&'çM“䬉œ]˜š€qåüŒåñí‡ý*?ÿ ÔùEã’Ô]/,J™Qå9œ× e䯲zºñr‚¹PW+ˆäÅ ó"y—ÃÙ9‘Ü+œËAŽp…¦<ÙáwÌ¡4d0@ ",˃dгmŠò¶^pÞq¢ Q…R/!µ“þhvAÜÛ;Çß=ÃOy1Òç)3I$ˆ‰§SD™«ýúåáoÚgÇ1j™Õ5aÍ葇ÔMk-¦ZóDÖ„ii5 ÈL›ðj¤þª1Ä2ÜHŽ÷’cWG?«ùݽŒôP"û˜ú÷¿Ìš6\5_Õ6êG&MˆQšé‰†)|fÆÔIš®“îîf§x/îpýô×WÌÃó}äÜ«–ßWms"öåˆ å3„]!è”åɸìÏ‚^Sa¯ä?íOv$'+˜ýfд3@I1'ö½u±É ”DH5q.µ ʆDwj\hGrnÙ¢ÜËßýñóùU¶öûFËxû”®sFÛ‰ÝÀšÑµOéÛ¦ô-#>xi¢Ö4Qg˜h0š›-†–ù±æÅ‘&ÒÜýÄÔm~ܼ9ŽX¥ ö©mÿz/Ÿyç·­'vº{D¦?A &œî\¶}¹³à"Ë ŽÑGqܬZRyÁaþÝ<è2]Q€ãÌÇÊÂ-ŒÓƒyÌÁþ0@#ì‰gQÚPøswÞ§ì øøA(Q»6%aU¨Ýòý¤E@˜»"äYí-«öWÔ8×ú:U{:V¸Ñ[Cª!ãiG©Á@GÂÓ¶2¹~œ, PŠ r"ß˨-È(‚’åB`R ºž@0:Q¶ ‚; A-Ž- ,б5´ƒàÕe¥ìáC!…Àü  –+å6È#f³œÏñìq?Bdös2ÞIû”ˆ{˜Ã|f‹Îó!Æ1þa÷9‚Â"ö¢ÒD膘Ì9WV&0cA‚, ¬ÃÅeÑ.°ß*¢"dQ'ë¸Øö„ÄÚƒÏY3 WrVnS$, W„ËÀŽoLôFùm1Pz©þ9`œÞ0q© ×øI*ÿtâá7Sº¡'‹3óäœyÞ-O€xAÃs"Šií«PÂãÿŽh³jž33ä$Bžœš6ég''MzÃß>ÿ¬««}÷î} @s³D‚Ó,‡“L:œžNã¹rfžW@î• œ@tÕò¸…‚ŠYy’";ŒwÂÃö%ñò6µÉç4€k îØ‚ƒ8ÓëUƒ{Ü àÝg»±ÏŠiˆÛø¹ :e©ØWP°Ã ØãŒ¿â€‡Ó2{"‰K¬ñ¶9¾Ã¯G³÷³ß–Œ<ì2j{p¦a–‚ âÌ”¡¡f)Ú àXŒu“††IC“uµàÕ8Z9m¬Ÿ64OŽ5Îê;M}Cí?|^^™›p¯ïß?+0µYtmÆÑÆ ]Ó´¹ƒ§© |…Ê mÅ´±‘Ôõ<ùs×6"-€8ö¼ÿ—çO޵Ì;Hó ñ½âgep–až’2 $rÉ“Ÿç'Èõçær ÂxEá‚ÊYe¢â²u^”û„T\ÚžÊ:W…yUGz㔕„Ê BE•ñÊšd×êDei¤$?=Ž˜U½( ¶Š0]Z+„ògþµzä‡j½¶Ÿ›0¯m²Þ–}ª?üù„©Ëjš æV‹¡íÉH9Ôjø®‰$ßjÎM¹°^òr´Ã;M¯˜þÖ?úûÚ³dû|‰ÃÐ%³S¬Ü™WŽn Ÿ•á`sÖ|[Û0ŸÍ“1±0 _êL‚EY€Æ…<Ÿ@ÅÇ7oà…Hp,æQ¶íQ©ýû®ŽžÐŒEœçàˆKj’|Ê£=J#\+ÀÔŽõ¶bäÌh¨B\  «}yÇjû?û2/›R/[ØÏaÒw4Ùü¹eœB/zž;h›¶@‡B%N‚øG`&©ØaIõjWLó=8€~å¸RÓUŒ™– ;P¦3Aò£Ú`ÓÀ€&[Á„§Ä…âÚ°Vµ1ŠQ…Œ-qVâøŠ£-vm1Åæeð$çØ’ ^†´q˜ÄK¡HyÙŸ›Ì/ B× CU‚*/DxVeLdƒ¼ÙÄŽ:AµIRæ^ rA¸²8TZ,¬§,«cå2ü²„'©:Ã\”œÈs€ÃÇ@ªàO?~©/TÜ9—Üìðت%x‡´Ó~üâÕ‘¯œÿî½×Ý_8;165m²Ì[àä4RX4Qf‰€èóï 5”¹æÂ4ü·'©.ÍåæÚ‰¿|úEWKÃú•TÔªy¿¢Ô1løS83pœr"NÀËÀºÐ”;#£eá§æ+ºàátFÁ=å"8£Uó!”ŠT™BkXSeŠŽÌ3`>A=fP·9Å ´z®¡¼‚|~.'eÌWœ_’sžÓW°ˆP `ƒØe‡ÓqõÌðíO&_[œºckŸ¤®ï&j¨ÖŠõÄáÐÕÍb騣·0Þ´0ÚŒõd¤ºi¸bz¢~ÖÒd¯76Në;æL½Sºîo?/yø÷Êñ ¦‘–‰ñ‹®uÒ€¬ I;††pYW>i¨ž·´’S7?ë|6œ‘Hd ÿöÏ%Sã­s†RßÿðÎ…“~¶½ ûÊ<#²=#²É [„uNCÎ4dòÿe¨-’¡Ta¢€ÍÁÉÂ.A™fÚRÊ3\ŠQ…Û ¼Hzža«ØD02ÃMA¿ž;=Ö¿0݇TÍbÆ þ?‡jCcÿ{¨Aˆ} ‡ZHc7¾sræzwéªÄjÑ‘µiáÁ]rèÎÂmÏx{¡a%§QÃApà téíN‚3KUÊ8(_¥ Ì3:¨˜Iý \ˆ‹NtŠøæ#kó,qsÂà ­Ë / Åøm‘;?Kn ‡)hu‚OúÓóƒ§è¨q ¡UÙ À$@n{ÃV@ø•ô¯H³Ev—Ạ»LQ¬´ÉS•?JŒÒ›rg‘§¼|_êJëϺ`%V!]‘¢WOéò¯Š=HýèØ šYi5PUEß eŠztcÛ ¥¼Þ0 3å„ÕE#ô]¾ƒÚz×iŒô/0à˽åì(u“—º9çH˜”ª'TpÌ`£Ciä8ô²Ò°+¼K^ì ¥ã99+CÎÍóÏ:ó¡ö5{ „óŸÎËÒa¡ž 0ªÜp|0”q r=اƒLŠƒ™%¡C‹ ,H`ìˆïŒÊö¬㘫ýQ7æa¨ï¦E¿Y–;ô›wtŸlº[·é™ Ó”qjnvfnQPáüÑù Ye¼ûo 5H˜f¦Ñ®AA†/9=;„…áã¿ùÅ…Y—Õ^€ƒ^–ÚåJl*à* 1p9Ñ›àÝãmFX¥`HWî/€,8MàäZ‘Ûœ“ÇcÇã\Þqÿ‡›éæ|ÑCvÉË%Û[‘å)ÏP O:sŽˆ™y®ùÁîùnW| Žê&ÎòtÎõ•ëŽðI½]v+p Ú¿\ ø±'RÐpnãç¿(7ÞÇĶJÛ‡ž§q Þ6Ä„¥¼e)ÚàrG1õ_‚ ‰ó5܆€c«ž1×͘ c•c* # ÈaÐ%ž5öLj;gÌc-S†öic‡Yפ«AÎ0ii´è+j¦ÕÌÞ^ø´ãÜZ·eþDîé¸ï>+™m!ͽ¤þêŸÛ]Š`w¦ù´ÆºADzÂO˜^¢i”2õVÙþãþåòŽr©ýÉR9g»ËÝÏAQ9M¥øŒŒ lvž¿´8Ä¥$TAqAüñ’^ñ–ŸõqY# …:åÇÖÜÿZª'3Ý«ÕjP*"Ôàãÿ£ü—”ÆšØ4&¨¬f)Ô<ny¢ïD¤%ç®ÿáõcÖ:=íC!ŸÇ>í ÝÒ}X}:ÙéB”°"Zu55¼)À½',°7"´3,¨]ÐÜ—GžÌ#|úãÃÚÃýjýTåî êø­Ì]Ð$Ïu¶CΓ%$re6Ùb ð†HPõDK[Bxµ~¬JøS³`'˜"„#*B]ª"Üq›_ñ“ŸwÁ”7EÀ‘GUb:aš‹N…‚Uà‚a4ò%ŠG t娰E<–4B¯ørP’”…;—ª¥xS hNöî_¥¾¹1vp]LÿŠH¬ÞôˆîdJÁ¾=λ-Ú»#Þª79¬;>]\ 1õh‰ðÁ¤/9´')¤#Ö·-ÂçjLDWhH½šŠ —ºC$V@fM°&€B?ÿŠ3 Q|ðgZo´²CíÜ,¨ඬ:€E9h¨$Ìvw€”Í 1yö£|»£|Do¨çÁM¾ô<kõY™#@›…2Ìô¡ó™ç‹Fœc;p=|”ª‰ðø1ü/KiPOE ‘ W<¥¬@I?áÊ<¤b>ë*Ìß²ràâ‰_uÔ=øýû“ÃßNŒ?…@ßäü"³Öfqžò3 ‚ÿ5ÔóP‘XÀW¦8ÝðÜ1ñé··û®z漯¥†8É& Á¶æW$D)tS•ø8ß6[BºÒªý¹Vm‹t/ó’•)àf‹¥¹r×<¹×e÷—œ—%l] ™/pìö;Ú<϶}‘G§èÏl Ãþ&dQk+m²G¾ý!ì,]<…Ï*˜+¹Är)±=ŒÝ–³ëoåO ]ŸÖ궆šftm³@›~Z8KÎŒ®kV×27Öj]mó£X3ã­T…5Qc6VëÇ+t£è÷bºdkD´1k[McÍfm вiC«EÛ„f2:¬-x*ty0QyyÅŸ–L7“³×IýÀ;W6åD°‹ýèE®ÔÎà‘Ãk±›­Æ[$ip¯ð’^ÿ—ýq=Âu<æä3Ž£Gè,!uR%‘ äB¥yZˆû@Ü£[mA_²Y¦¢õ–>÷èóÖчM&cÛ0?T©øS¨YJi¬}*ªzZ 5&Ó¡†Jð†›¦†Ñ°j!É;Ú/«ó·+Ž'²«¥Þÿ@³øà:9öÚµ³k2bPñ¹Ã"ájBhThk_s°_K˜_[T@obèµ´ˆð›H ëŒ èìŠ ¾š ¾žÑÜìRîÁÕø9!°€Å†çO •~8%*¢ÎÐȉRQèDˆ‰2%˜Ó*ÎS9 ‚f2fиÁ—XœèµÛsmK-=WÂ+÷7…xuFö$„ÀL¹6 "ð1ÔòöXwøDhbÝ*á8“èݔԵFÕâ µXtBêã½!cر,¨1Ƨ>Ê <¨èáÌ·º@@éšBÝ›#¼ZÔ^uÁnM¡ažxxðÕ¤ÐÁôÈë©Éê.uh—:¬'B}-.êfrT_\õy=9Õ¾ÈøC@à‚ ~ú­ðºU¥J¢TNäv‹ãƒïϪ´DËQé€c‹IýE9ÈFø e«œó˜Ð¡ÅhïŸ‰Š BÀ‰ìG­ µèÚAèŸ#7Ãû“€ríNƒÊ·Íqv„7_s¨²+Ú£/Þ§3Þ hsàÏKƒ$u žË*’ü.Eù\IQ¬_Övdÿ¯[«¿üŽ¡Ï?™ÖO[ ‹ H9$ò p½TÓÆª9þÿݦ€‚h¡(ƒŒ:¥É™YŽ™üó¾¾Úüú‰½×w¥ÜÞÖ—¨h cwFr[Ã[ÃYMá̺z¥/sÊ=ˆJ_»º n³Zóte€Í‘ɱ/VÊëÚÕ‘mÑ1š8u~TÀy_Åag.xG$°Ë£+c‚«bCªãB±ðfé}M‚º*:ÿMzôå„À*‡'âg â²Ó+ÿöÛšáûÝ5³®Μ¡mÑ„ŽÚT˯Kk)½¡òd;ÿXf}Û?V³ÙXcÒ—u&}%’©É&³‰‹£_jÖ7#“™Ð6šÇgÐV2R½šÙ™ÖI#šÃµ@׋÷´¿®ÒHXIk¯Ü>ôyåôã&rá9ÖÛs0ª(šÛ³EGM€m‰µE5Œf¹‹$úÐF#9òhèý7ß-<_¿)áç/®ûéÄ»ký¯§»õ%ɯ¥ª:ãEWÓ$}©üî$^[,»Q =»J›r_›\9u…-ý°@èÎä” ‰3#ßO¢ ³2#±Õ±U1þ-)ቡuqAÕQ~vBÇ Û[‰³Ji†·ò°‡x“‘Ì#ž‰¶îþóo+Úz}¿ÞÐ…{|ÚÔ‚‰íœ¶Æ2ZްõÖþOÃ&cŽ•ÉÔ¢ŸÀj7·Y€¥¡Ö¸¥É`Ô˜´Tœ±˜4T‹Å‘¹€ '¦f*AÒ7Oꨅ¼Ñ½üƒ ]åÂtÓŒiÀ_ÜÈÌÜà¹&œhüÙø—535ó·ÈÇ]-϶­õè_%\)îM4G1ëÕôzµCM bÎÿ¸?ÀCÂFÂì¹RÌ_*!g‹S†;×»;9´;1 È;V‡•¨ƒ3½=ö)À›#2ŸÿøÂiÃ-³¥wÌPg™ÅÇüg¨ù)¥1˜Ñ þg¨Azf- –&P ÆGU‘ä-Òxí)_bƒŒxyµü/ïM}ßK,~ÑQ÷¬úTó äqd J¬¥Z4"4ùÏ ( 41^¶*¡ãy´>ÐDÍ”Ò*Em ð÷¼±.ðõí7Öùõ¤+»Óä]©²Îx§ɲ› ÒÁXIo„°-„ßÀÑø°+¼Øy.öÈ.®¯ˆ¾½.±39¢Äß-SF5T³Å (p½¢ÌWYâ„›[–‡2ÃU%‡—LXRC g‡‹R[Œ‡ð=ü    JÌœbZ¡-ƒ®—U“š~²ˆŠ“Öb¢:Ä^íµÁŽõ¡`•Úç) ,x”d¸Û¿"¥œvÓ¨B z¿@˜,ñÁñ LÂüà ‹{n>ÕÝ˲Ý'dm`16ÄZ/¢âìê?~P:2Ü?¤íÖ·ë-í“­m¸df´Œ4ס´™˜ÀÒ àX¯oÜàMÔ\{¢ÕhnE¨Ñý#Ô Oµ Ô 5àm 5TŒ2htÚJ½®p“«H¿KÛ¼¹¥FT†áZ³¡qj²Å0V1‡üÇÐHN\½‘³y…œH ø1ÔL?B¡¦·cOèëûëãØŸŽe¢æx~c §!šýÉuûŸ÷§Ø›~É8&!žÅÑÀþX¹‡("0^êUâ}Èv‚ ™‹`&¡ÉÚüçÊ,Æk¦É®‡¨-´ÿ3ÔXÃ#R;TO5XØk˜Úk¨išk\~¯Û¬k˜o&§ï¼kû\°Í±µª?¾™où¾‹4Ü ôý­ïäË>y³PÑPt]¤Œ\@ú\îݘhM` Ⱥ Ð >#³C[(\Ìú_âPbžEÁüÎ>íé^-©®=k|ûÖùu¦¹µÆ:wÅ)zT½‰®±Ê&5Õôƒ[ÓeEÉ\Ê70qÆgè?£ ’&´q^áÑPwïãÚ>Í¡ïâØ=#`¼ å¼ ö“3óŒÜᢂjM`„ ùÐ …DÃ! ©Ø|^©Ÿr»¨nŽÒ©ÀRâJµz.‹áHÃa¿â 1ܲråtHe"‡ŽÎ)ÔG•Œ¥U¨b"Q€ L %vTcö„ìôIìˆ$¥ÁÂÎUþ­+½ë—»¶nðißì_·Ö£0ɹ<Õµ"Å­$†’D†>%‚^èÛˆªý„5âܽ7„î­Æ¬Þ DÀQÂìSÅZBSã¿‚“‚ó‚7(š0¨Kâ8´Sjsó!çˆEÆàôŒÐÝàÙÀßN‹¹Ø1ò<ÅN¢ÓîÎÏõ‘Wr^Tðöº‰wù©vÇ„üêvß·Ÿÿafƈ~ æDV38TSÖÜæÿ1¯ATùï¿àA›fzHáy3J§ß~õó׳¶¬Ü&cmfO¡0„) jUôK¬‚qe¤ëLjãõ Šæ$UÛ2ׯYU8·ÀVÅ© g”5j»º»ÚpÛÆ8N[ºJ‡i+ÇÛ.ÇRE´K2ªCž…nðP,RÓmô¯ ÄwOìu¢ow²#ˆ$Øž^ÿ§_–~óeã°®gt²Ëš–4è&jÑi™ÖW.èªæõC²µ†"’lýáÛlòI·EWƒ ‚œìœÖâ]l˜œXœî1ŒÕÔÌOuëVÌé›ÉI4~1ÎÖLšêQF!àX&áà·T!¦mÀ| ¡kf¢u¢“~¸lÎÒNš~Õp¤¡ƒ›U?¿~|v¨›Ôv’Æ>räêÅXVÓ Ï˾̟ööŽí©nM‰òêÞÒþh¬û£ùÇþ4Å1;Ó¥5ñÜÂP€i}i=m2•”ˆl:‹T À ”·FHb{„ßÞÄ­ö—‡ïwèµÝ¤£Æƒ¥Þ`¬XÑ4Z‹o¥%›isó˜¾Ro¨^jSÁÇØ¤7S¯ ÚrœÒ-Nw̘;HËÍÜ-ŠçCh›ˆ_Ë™雸¾‘´ÜL(µìt€íø–úñ aªdÂÝltÌ(ÇWZ‘?+ÇsÖ~JI;çn7G¤Ä 9ÄÓàYƒçyZ†ó‚7;'HX¥€MUQ¸Kuœg RúDßÖe0˨WÁ+lî+r&>2Š—³óÁSÍ(.5gyʼnØÃ¢.âMlé.Ek¸D $AܸijÒÙSòbgØ©0#„h.Ý”y;å¸0P"a<}IH”»±Ì©õT{p«Ý9•nl˜$¢‰Zéï kü…0PÂQ©§cŠ^ìÎЪüxy Û<:Èk—|Y ¹WgÝ`wLH)l£Ã†NÚ`¢œiÏK‰ƒ®öç"¥'Â…§ãd;܈Õ‚í.Ä^•í!wæ•0—ú¤À*µk‘'¯>L^"¬ös¬a5¨™uaöM1ŽÝiα|˜Öøyž (´åsbâ¢Ü$et´Ž@WýpÌâA<ÒÛj“‡Œ ÐðÑj@!ÌæÙ‚/#а™Löà<ŸÈ‘ÙS#ßæ¸Ð.ÓÇ嘿r•€ž øéCúí»FãÐÌ"ÆÐӿɉ«§E*lü?›ÿgð'5æÅIýŒŽ,3¦Ñé‡ßüõÞµKÓžRqùÙ @«Jä’~\¾ˆ|EH?)gŸV²3ÝyWü…yülÆ9%QΩã×Ç0[“YíËØ©ÌkëÄ领xn•š^D«´ÕÐ5¾ô OÛ"Œn*|˜Å^ŒBoGØâ”‡ óÕÒç”(‡£t:´Âý¾Ó¿n똞lžjŸ¬Ó[4K¡fn¼bV[ñäI3Iv}ýõy¼~ÿE69ÝKÎ’úrò6Zµäìv‘3wHË rˆÂ“ ÷f‡šµ_—™?kh6i@¥œ´4˜ÐÃ1Ö çAÊdÖÕ!ÚL7!^Qé¡iÑÜëÂèN º¶"¤1LÚ-Lpí 5… Á/8çïôœ‚¶QH$ñ(öýÊ@bS ëÒó±]—w}}»düUMyºß•`1 ³Àí*€%æÁœj$”x §Úu¾Î(%€Æ¹ÀÁØ‚¸Я€ÈÙ ø zº$·½Œ¥°ËSÑ <ˆ38Ñ€ë\r!àVÙ—æñúæpôaz’ÝÀ—%‰ŠRDe©²êt×ÊWܹA²Ü`·sÞògO‰|‚ƒO"±Æ*ì6ÄËö¬÷Ë{eý{-Ùäg?ÎÛ ÔÏ‚¨.³kˆ ¤º]_îr{­äÖZÞÍuŽoîtº¾žÝ»’Ùžlל`ÛšÀhMd[›Ìœ¶xag²¬;σ¸%FØ‘äÜ“âÒ•"/µ*Ëû0‹<˜å^\@… e tirDάR%¿ÚS¢ñ‘iàã.@´)vqÄA£ÄË6çýÝž’ v{ýúF—öÑWFóØÄ¬ bð¼E¯ff çßjfÈ9ã‚qjJ¿h±üíO¿i¬lxfsý–4ÍŠˆ²xï¢UQ„kn˜5Br‘¾(‘wØq¶ŒM„-ÂÎf«Ež"´àÎ)i¥jn]<·k…¨g¯3Ñ·Æñöáõuܫ˙½)ý‰Œ¾8F_4£+’Ùɺ‘ær5Y|M”°5ݽ.E‘Ê]!"ÖúÑ _^ö×÷Ê,£×MºÞQ]ÓV32Q=j®Ò™«M5ˆººÉÑ:ó(zM3S]$y{n¤ÝüE-©½ûûÆ—Ó¥—â…!ÌCèvBôÏÛæ‚ÍYwâ\]ïñÔ¿ÞÈ ¿½JŽÝ GºI]*Ýp%º4 ,`¡†¢2-©‰y9Ñ»hìš× ذ8ÙKν~~“+ˆ ™Ï…þQùÌãžùáNÒ|Óð»òÓ¡ŒÃRÛ]t‡tîOûóÓh|Fõ/ûÓ•Æè_íxg opƒcçjFó zãrFc³%Ñã€ýy5]Õé%áòDÕùXç]>ô$Q—³ö›¿T¦=aê0£ûd¨F“Šª’ÆêÆÛHãÕ/saô«2$6#C•hh#Îü#Ô {C‘æõšàgˆðLYÚÈÉ›Ö?·ÏŸÆGÁñ”û+]œí–˜|ÜNjïÔîA”“\c]öu€¾Yu¸¨1F XHi€C%ÒþQýrUÍ UUš¢,Ù¹0VZãŒûB“æ_¿"¬,Á/?Â;?Â7WíƒççˆÜé1w“£íZ¨^Љt‚XNP5ÑFë#´×–hJðèIñÖpzãd7—¹öD9u'Èë—yg„Ia…³Å•xi¥,û¥Èîš=oÝ8ù÷5º/{HÝûä· R=Š0Άѻ0GêP¢à@È c_€|°Ð)­ñ•ÃxBP*–ú½¨#x†FYž+€a`ø$FaXÀ+ógS²à5ø3jÕ‚ÆÀÆ`éŨ æ4$HjWÈ«VʪÒ\Ê“ä¥QòÒeUŒo}rxuJdNl蹘À­J^šÔþ™X¯s?[ÙRrøÎµ¬Ï~ß:üåMrü#räãp‚HÃe xÊü½ÍöÇtÖÀJzßrb`-íÎVöõõŽ}+í»Ré)ŒŽdVk<«9–Õ‘(ìJw$Š›¢9Qˆ?€=;áµ)NÐë¯p˜ƒWòê‚$5~"ÛrW~­¥û53àoë•RæÊ˜ð¯Çî¢ÀU¡BˆÂm²·úª>¼Ý;öèËqÓ°nÚð0à|5 ÓÁXçGÿ/›þÛ¯¥Ê<7aÖ<}¤ÿÞÌÉlܾ¦uSJÓò0TÓ•1®UñžeÑЃu9,<'àn·£o±±ÛDØà Á Ž ƒCH •s¸õ‚™-‰ü»[\ßÜívw‹øîV§››9ƒë˜+úRéýIv½ñö=1ö]Qu6í±ü¦§Â@ûüPvu²"?Zü‚—M J§Í¾ï f˜\Ò ˜ô'¡í0nªÔNTLU(s̆z̉vk7f?u‹Ô^'?xµôéí€ý¬õ;ät1L–«Fg@zÞƒwÞsRnwJiwI->ét*MÞŸ¹ÁðÛRw{îa›é‡Z¤+Ó¦¦© ,ðžê).Pc€ºu-jÛ§i¦‡jÉ©~rúµ3k/%ˆ®J¸ÿÇó]3ülðØ+Þ6ûyHÿh[ »Í Afiv@ìÚªwVI”„0›¬ûóÖ.·W7‹_ÝâtgëúzF÷‡–•5ö ©ö51Œ–@Û«QüÆ@Ço»ÂX¾íuI«n¶=7ôm½i¼M¯oD¨Ñé«0ÎÆí«<áâxĉ áT‹ãhv¡3iCå0Ûíˆ-6ÔB¾ŸC;$´?¥`fyó ùèö¤ËnnpùÕ~ß^ð}÷)ç·v omaÝÜìxcëÆZÖÍ•¬Ë97Òø×Ó„¨O»—É¥Å|¸0%»œ ãïT)®Deöúi4Ž´ˆ„n­u“© ËhÄ…ŽPC!î¦ ½sº«¤é9~üîÖZ5±¢Ê Û…6ÏÉÙOslŸfÙmƒD?$¼\x¯¸ ¹ñŽIŸq¡¥2¨ÙnåóÑ÷ßÊ%‡ÑiérO×€FåK?Ò¦&u€ü™¾«!õP¤é"g¯¢@›}ÔõB, hŸ³{Ãþþq-B i¼NNÝ}£rç^9±‹N-¨ˆãÚ‚uöç%¨¦«³`ÐcÝŸ®åÿÜŸ÷v;¿³Cøú&m6qû6pºÖq»×ðúW8az#X7cÅ=‘’ªP^iŠë‹!œe2bÿZÕo^Ëе Š)<ê#½® Ó´yCÓ“ñvä'¤n°òlÄÏûŸi6SeEР ¨:D$jümÖÌë«ÐQ_˜¡ÚSOpZ¯?ùcÛ^ÛåJâܾÈ?üîÒÌ|7X] £mäã¾Éß”½Ld†2Î{ƒHÍwÀ¸É€ŽŠ‹Ãìé8j:ÔVD¦»Íy7ÖY… ž™ÃNÄ!H`¡-, Öþ%H¡.¹· §Ae؃¾–ÀöÄ£”¬+>Ð {Vøw$Ãä‚QÁnŽáÔ†ÚV…³ó£O…KÖJˆügCþöAÞ£oku¦¶1c³y²{YÜHÿW×ÏgD Ðg.t_à: Ú€Å%fÃbZUçA„ä;Bæì”vX0 xpl è0©<"dÐ(q!ëTD Í`@ј+´Sø¼Ð¨AqçL W=É£ðj—'Uø°D¦+í¢Âî’ V5§$‡¶ûE´ÍœÙ6]õ÷ŠtßÝ{4øøqËÌÔµÇ_jHýÝ'é<Í>æewÉ—YÀnwH“ßÝ ÀyùÍA¯·~&zíiî­Žw69ÞÞÀ¾±–3¸Š{m…p ]<.k‹áa5EppêÃx ‚–8Ç’¢.Á¡=MЖìÔ#¨ wª ×J(P"Ìeª™žj6êªÌÚj°½ÈñrêöÂwmÏ.·©»«ý¦zb¬ ÑÒjjgBÿ%ÔT$02\2=Ù9ù mñ“¶thGlOqúÝG&¦A8m¸¡fîãºÃ¡´sÁög€è/cê-"ò¼é塜<ÚY9|¨‡D ’qØ*ŠÜh+žLqŠÁÄ‘ÃYŒh!‹SyLh8ÍÁüºÀ_ ¾-4Ó(3ÓXyU8¿2”ÙÃéLw§‹›S¥e)žÝÖÊˆŠ—£‡¿€ kƒq¶];Ý0Kö<™é%o•nßïB±Š(=p°´€ø• ˜L‡1GâÉ,ÜH5‘ãÒÜfG]{—¨g`ŸÉê(öÝ)1‡‹®Ä1|^hKˆ\zq(¯§dpt"c#¦FBðH®ÀàyŽ möñ‰M\" „÷mÞ_üªÄÄ¡ôU Ö`Pw†gìmžaаvЩ´jL© Ú' CÒð´ÔþðB>‚â`Ie¤¬&VV— ë]ë۳ƫ{µ7à@·¶„Þ݉Ï;¸9¤k¹wßjÿ•×Òƒn¯T8öîÊèžÄàŽe!¥qžÑ’}AvÙ{?~3S÷¨k~æÚÂBëèpÎS9Ö4ýQnn;/šQ“ nŒ—Ü\ç÷Ú†€Ûë=ïíô¾»ËãöNÅ{Ü0´ÅÈ o•¨c™Sc,Wú0”V†·}ž; £¨_t8›c…ˆÉ‘´ÒH"?˜¸ìM\ñ$ò|ì@`G£ÖÒàŠ‚ËTøYã pGj ‘Kƒ)0Â/¬[RìˆM~žï tÿ‹aý°nvYyf~)ÍÿžÕÌxz&~¦9‹e¥†#æP sàPYß>™2¹°0ay@êF¦>ÿô÷Íõ÷wl^Ó½<¾#&ðæžH÷Þ”~eCð‚?©° DRÂ- cWDóRd-éÊÖ媮5ž›ÞÚgwxÿï®Õn+U-iòºDQy” G-ÊðãÆÃ & 5íìT:qVäpÅMzZ):æí|0Ä%щ{Ö¹~üËüG_5ÌjûgÆ:ÐøÅu•7B¸¸8hðBö-ÃUp>Äåù`®ñÜ{WÍ<îšk·´P£ÿ ֨т—a€¨ *ÁWÿXûÂ0âèfÑϯ¿8ömí¢¥¢WX™ï˜w[¹ fjºÆ²™™ú¹ÉfÒØCj_=·Ás¯Z´KÍ~çÚ!‹®cÆÐij¤*GóÍšþ7Ž& ¹-vpUXg¢7ì¡‘´¤¹á<ö¦zuÇ»·D)Z£•ñîíñn‘òê`´\Zã][aó'+öçCñPÅÁ"Š‚ånÕ´, lM êZ:°1òÕ=©$9$[ c¶¿}–¯ÝEÛ£râgÛåö„¨ÌLý®qøAÊF­±úÑâä ïo¹dWĆƒp/WÜ™g¥6¨×ž‡5}Û—E(c_àÓ¸p2|œ/Ê3}ÅÄùèè&zAœ¡.Þµ>Á­ ¨åøD7·ÜÝtogð½]·¶ú÷­qkMvnJ’t¦©ú×ø¶¥y6&©¨ëÒ«êqk UÕøË:’CråyÉÞ¤DÎÞo>*3öš':ͳ€3•O™k”˜ýmNÍFñàÞàþ¯ïK¸½#bpu`ÏrŸ¦•^µËÝkâ+"D%aI柡ģPm¬f© ö#m;åLY±\öbåøpoCö•‰V¹ø ·Y¨‘€þÒ¥Ö… ±àú@D•ÓòÜ0ÝÀQ!Ý8ØœŸ±ˆ­lZËnƒû{×zÇÜ7Mè¦f&çÁ»¦À4V™s*7ùŸ»ÂÔÃ0|à9Ä™9« Õ£~QpcdDø€4ó‹Oæô3CßLõ§Þ¾ýnÑ…Žg6V$†ÁPaŠçD:H$ƒŽó)I‹Œãðr•ÌVÌ® ”‡ñJBùej§+~ލÓOH©"ý @\,Ê)Ã_à+?Op“ÕT~‹@Ÿb+°žhÜ‘y@.Ú¢p ã+"Xeù[þþ©F÷°u^×;;Ú25¬™Õ#ÔTéM%æ…݌懑¢o¾É™ÔÀ$åϧ۟N?¤\Î"–aDž Î<˜Ø©ÙÿÞ«Yîû{ûÌ£«Óº§u[†»†¾­»ÿû¢_7h}1Ìå ¼¢¯¤8Yåüýû•ónSï‘–·ÉG×Þ«}úHq:ŠÐ,¡·_æË„¿dQ”ør¢ô…0FŠ’ðeWÖ}÷EÛÜøk '’î Àx‹†9×q!q•ŽÖb•ZPÂÅtSó}áÚƒj‚’•Êñc<L\¸Tog©‡¼s‰y ]\lð¢[ÖɈP vzòAÑü£Nì 4KN׎š5ã5 vu‘=þf ~›þjÅÏ’ìþð‹Ì¯ÿXɬ%æ»5àPüw*Ú˜4ÀWë %ˆ6Æñ*´¾!i^°?ò@´ó3aœ»-ûõ›ÁsŸ…&ªá«'{§äB§z¥+ôRàö ¨p&Ê%qθäm[}*&V©7»ÂL|CrÜèÐU¦Ê%!‰×£|Jl䌜íz#ÇÅ4ˆ†IËÔ ˆó@¥,\žÈ?,¦n¢#j•§•6§\»J7‹bÄDsñ6Ýã¶ñ!Íìd³Ñ ™šn~2ÕC>ì¼u&¹>ÕùöÆà«+©|£-Í·1Ù ­›ÊX¯ªxÿ²8LÁ²BÜÏù¹œpÉ Td9_gŠsC¥Eá²Êáà'×ÄÈkãä5qM¬PÍkK•7& +ÂY9^ª¥S—`‰èCÏAÇÇ!ßÇq?»Ö[á_6´µ†ß/·‹·%²ŸþúÃRÓHÉØ>f¨Ö•‹NZz´¯Ÿ)^ƪ]%+Ie02Ýèp#BùIÁ€ @¢…çÇd;:~pâÇÒÁg í/œ/TvÈu3¶ÜèPC‚î%šhS %ü²1œÅJ…°@j‰Q"*bu&(…êIõ*òç¢ÂŠt«J¸œ¾ÃS¾ÆKE…šï¿1Æ¡Œ7=?‡a7âË’QÔÿ-ÔPI‹µ¼Z 5 äŒ5±¦'¤D)¸1åÅ2?³8gžŸ3= ß=þõíW NUï^‘Ÿ”Çv~¶ÌŒ3Ø/^€o2ðÌKU6šc®vØà0á5!˜#€óá,$*¨þFp|P C,~‹êû<çKâSReA’Ú0·ºïš°€ºÄÈÂäÐÝŒ8ÔÅ»ý~ùFÖøÃîI]Agr¤É2‚lŸÊjôúÓLµ~ªfT_5ü¸Š$Nn~Øy(sêd”ó61±ZJœØèwµú¹ß½Sˆ`½n»6g¸¹ ´ u@(×ÌD¿y¼{j¸÷ó÷¯ÜÖìÞJ$‹)Ia58½B¶øÇÞÁó›wz;Ä/âYPûá)ãNƒÙÊù`á™0á+aü]þôeîDˆŠðr'4{¾ü¶sÒòizíÉ·}{C xNxáê!àÖßâaŽáLƒÑ•ç0§³ÂË©‹ ßjŒg ýögñáÚRC¢AZ¥VÂÿ·Rí[—™Ÿ²Ee›æFäLûæÍÂãŽéñ¦QKýR¨Ñšjtæ‹©›4Þ!‡^ëØ=ðrjºq¯sß—âSÿ#ÔP¢|ÿ5(£ª,SÕ““ÐÓh$§ÈÙw[Ï­ÝæeË!ÚŠ7>üªú`ÓÈ”jt]ù{•ûˆí,â´5(é'ÊD’#Ô¼€€Ùb‘»ØóbN¡' 6yp2:'¥zhÔàƒ#Èñ4ª™L‡ŒlWÜ8ÙnŽ2[4°°9™.ôŠPFÞm©~‰ž½lXtIíû¬—hsóFËóøñéGëjÐОžn[D‘;>píHÜÕ­××€AhN€è’¯(ÓK|ÎKrÒSòŠR¸_ÄÞŶVÄ—a'Ûv>,qhûl^ؼħâÓ 1…6ø°S„N²0h0TE7ìòÐÿ}´_dÔsÿ|?‡"_ûbw» ì†L!e#²W""™I”½œøðOµæñƒ¾]«o†ƒ~ ¤¶ïÓÚ½g|ˆ®Äó!‚õ!i[ÐM‚å.Òl/æ0cÅcU~8¢„7Ïb]ž»&JÖ–âٱ̫5ɵ)^Ñë\+«qƪ“7Å«°š±oQ x‡u%y¿¾)ú»’ßÚ5˜æ Hk¤ ÕAea.—|%'<1%$²ìRUr„šÑïîôÚÙyX=™)$ìÿÂgmÏX“*«±†JðÆš Q¢~0_y2eY0ë'ǘøËÐGw_+:–±<èYwÆs.ÄQ%ÕZÏ÷ Wø2¬òÌ2?û|Úe*¤£š¾¢´I |XÊÒÔñG5r(fdŠm G1E*ùW°áš’¼+yB<™@V?ùeë‘Õ¢õJ" w‘ÿŒúÝÎãßþ©qèûîá¡>Œuc=Ÿ@Ó4µA‰1ah×i›æî¡G –‘ÎšÌØñ³@âd÷x8ëB¢,zõˆùi~Å)>9‘*hQ”Ø®w$Ð2w ‚lO[ÂWDÄGk7ˆî¾sñ»‘¾©é×IË«S_6¿’D;KËð'ЀB4zNÜxü ”‹<”dõÔýnu®‡üH&‘<$¹>¸†äU12M¢ ScÀ‰‹‚€öG!PîºÃ¥!5² )h£3ñTïfÃKÆïû†Ú¦ÇF-µ¦€5@c°4à ÔßšûSë­c++·E‹‰Üc‘Ã_6Xƺ§t@;S²ÖPCI€¢?ŒPcœ(×jKô£@\·a8r§lÏF7"ÔŽ(ËHúú³bÝPã´&Á&úo­:•b9ŽW! ?ñjµsqç¼qLN¥^hŒ€T‚Ûé.Æ4•6Ùît\Å<ÈRAjœ—=ì!WUÀE,‚ '§Ýa7ð!(Á…M4Ũn­¸±>âÕmñ××…w¢Ï³62/6p¿Ÿø¹4Å»×OQ EÚº™ÉFÐóÆz {`y­v·&ÉèýL4µ¬,ax%,õ—šÀ@ï¬'ˆuÖ…7ÛÀÄ1ñt+þw8Ý(FN¹çÅDªPÊ+ã—† ±—„:Aßqó|Y4` ŽF÷98­‹) 4úòýåG•Ž{v†µemù¼Ù¢ëÓëÚPCMc|i¾ C_^ÞpÞ—@Uˆ8yÿÓˆ]ÐÚ‚?‘ŒvÆ™ãÉ+òæW„Iª£e þ ˪P;aŸñ<8ÖFKomzm[ø½M¡7Öø_]îÕ›î,åõµþ«|ðÿ 5NÞ)­ vÄ«ú“½¯¥úõ%yt]¥,LÙhÄó`QÙRúZH™6ш4X§Ñˆ5¾ïß¼6òý}vlna~©Ó‚Xkm@ÿ׬ÔIkiõŸ²«¶þJ‰`ø4KNšI“v⇯F?ùå§Ý%·Îì*Ù’¿Ìçâ'tÊC™AšP@˜kÔŽ T„0*՜ڧj¸Z{Ú¨l =m˼J¼€¨ÄBàE ’ 6fSŽÊ¾Ô—_,‚Ç="mwºgÿjŸþÕ^«¼Wù¡xsuäíÍÉ-k"^ñcmÂäô)Ïîž0 w˜ô]&=fÜ-˜ÕÜ]_òöD2‹@Orµ›ýÖPÁu>EçÖݺzêw¿­øêÛvþÚפñæÔ'•¹«¹åi¬¢p[l]‘7HŒ|/Û?{¬¢@fY(•&æçUÄIˆ‚BüÇÝ lÅaN•QNõI⎕òžuÊþ5Ê««”ƒ«½o® ¾·)þꪘkÛ–ç%ù¯ÃL'Uùþ@¦þë®ùáöi]ý˜¥æñTÕØD¥Î\c²´Ì™ûHý½ÁK‡8ì`À…8÷œ¿ö›v47¬¡Æ*ˆAe5”® ¦lhOÍÔéõeº‘ò©Q`Œoÿð‹òsëü$K³_VýYÉÄÌ=1(Gb3ð×»Gò·Ê:žëÛvu½z`uXÿÊàÖTLóØè{…0—Va0}ª²p^U´°4„]Æ­ŠT†óËB9øm¥š§‰äkÂ8žvD¡7­Ä¬":;«C¸í Š×·F\_í3U%ƪ wjHò¼ê¾ÓÕñÈŸ߸`ïD¨ˆ"-Gî#õ°;ïIÅ« ås±ÃÁ¿"±»,±Çü%GÁ¿â s)¤`  ’ø½0¾ˆG¦’º dõÚdÊ9IRrúà"ï£Ùãé̤-R«>zûÞG÷M&ÓSô3øß¬B èå?RøPgæªÐïX ÙØ<'›!ü;Yj`:1i³ö?yüé‡_t5\8¼¶v“_Q”ªÜòHYa(?ׇ–o ùPŒ~´ª D”UAœŠN¹?¯x³-¡âÆy‚öHÙå%ÊKÑŠŽEä¯Ò 5Û€³+øÒÕ"ÔØ;/¼±Þÿõõ¾¸\—–¸4GHkƒÄH½ÉòàÀÖ~¯”L2ZïI´ç¯íþ¡âÙÄ•¡†AS½y1"¤ u Ò‰oÃ5Ðâ‰Åj~ãÀRáá( †&é»té84z7à¥á¡‹ãO»,3Wž 5ö7‚}a2“¸ƒƒÕ°Á2™«úÇ«G¦ê»ïXû['>.4nv݇òèÃ:¬$ލ1¾ÒÍNv Y†Y‹øÄZäY8_ë_rò¥.ã‰oæüò¯¦¾ûW‡úoŒ ߆h‚&ô]5¿—VºÒ¾.Šoô§@Ó]ÆÇjY,íX®¸°Ì©5Ú©y‰Se¸°8ˆ“¥§&yUfú0‹ç‰*#®o6ÜØ¬½¾Ùóê:—öeM àbáv+ЉRÝðߨ ÕôÈ[}ý°êùÀmx|MV¡ÂôŒ–õ[J ém"š–·SÖ¹®B¦• ±Â8¿Ûû—KŸ™¯ýÏR.Ø5c•££å$,Ø‹}„€5ê¢W87,sª‰‹Ï¾0„•ãOÍ2ؤk‰t QàÇ(dƒãܯe(5ìrf[¤„´ô ã_ˆ”]Z¢ìˆ’·/"W™7 U(Ë8ƒîHDRwÎ!5XL€ µ &r›½>ûKÆHJÍ ïhØ>ã|÷ ý¤MA 0Ï3~¶Š3—ŽJ²×ê|TÕÞnF½ªDëŠÖ­ªÔ[]éOÆÁ”\ËôˆcPN×ÁJ°‹ÃäEódùÁPáqÒ½©)^ÄY7"AM¤ÀkÑצ8˜U9_€jƒ*TíZ³HYæ€Zb`#q9Úi V%ø9m”Ë•Äͪ}¨7™Ú†š‡‡ê!£³Ž]¶š¯uì1´®V6D)Z–¨jÂd~¢2vŽ+pj¦«"PÒæxkƒþÖfí­š®µníKJjCùÕÁ|Ü\C߈G9zBhêñ ïA|'WmS Aݦ¼(Ý8†”z1Ð $‹I¹7DaÅ*n·#,¼_·…ûÀÇhÈðѦG„lW«Âü=+—þëý¿ô÷õô™§ÑÆ ÀŒ?CØíÿ^jȪBÖ²Ô¼hlð÷plB;CþþC–1ë Éòý×¶×7ŸÜÙ°1¬q…gE$Ðx‡òù’òù"\ÕêùÂo»FE0‡äó+üy¥Þ,ÐÑ+Þl tD™/µ:˜ZTîOÇ‚gJ^á5Ñ¥¸¼Ì½Ì‘S¬gh%ÞtüG õì\=„Rc´n¯Êf9ò×´Ÿ¿{Þô¸~x¨Ñl®7›k±P[†ÍÕˆp"Uæ:8VY'.?ƒ pæO¿|j£±ÙÛö•`ê†?|’s`@£ƒC"ioù­¯bh²Å<Þ -3ÈÆxâi>aikxÐ[0ñ´i¸»|ìçð÷î^:‘ÎÜëF¼ _6±DJ„‹‰¥j›½+kµÜ½9ÝûæÓ;£½][gGoŽƒ'ÜsÙÚÓÕsåLY$¼M£ž¨ ¤ˆ/¢ÄÏ®2„UÂ, ¢c„]µ_¾€WÁ-g—Fð+‹V*Z×¹ç0óýi¹¾¶y¾6…¾¶Å¾´BÇ̆H/ãB]R€ vï*Q·ºû«¦±'0œª²”£Î Œ”À‹´½òôçV”šÄÅŠƒ~œ¥è¶z~ú6ħoÿ§RÓÝ›ƒYKÛ³'ícß´¬t&Gx‡7)¾ý4|°s 8ÎV–óWE{Cˆ3¶ç 4€ÿðï…go®Ul_!( ãÌcâMòk—J–Éê–ÈjIr}í@(æ€PTÂ5²Š|¨P’V2_\ŸÿÚ?ä# «i¡Se° 2XTæPêPæX <¯fîØ„#RíUï¯þ‘‹RCFzÍÙÔ#ÌÂ:ucöûƸPNÕUM 3,g® ó+Ò]k¨2;Àá|Á«±%¶ÚH1xãÜF & Øl± [ª¹2ÔŒóæy =Áƒ’¬µKÓÓ±Ò½˜Ù>\ð£Ê# F‚3$¬ shŽT¶.Â|J »ToYBr‹3±ÍŸõÁÍÄþ‡Ì´ÖMÃCµOG‚sñ%«DuK$e¡üªp æé@Z$5â’0IÅ"§¶h7œƒJüY~4Ü}XE¾´"_F‘7½@Oo wl“5‡ËÛ8_XèҀ㕞‡Ùw¢ŒH…a‹;ûGÜbF?Vu !L P0™*?Ç*_y lceLX!<ʦàÐ6SmÖr¾àž9Šó³¿úì“A³©xfædí@©#£µÿ—®ñqQÎ ¸ÉR `09z‚¨ajúÙÐÐÌ£Þ¯ó|ì™y^yÁÊò I©¿}ž7+Û‹š®µMTgœˆX$ÄrרBk`„ Ê…Ž(¬e_CþVš'nÙʯ™ïÐ-¯Žâ ,| ¿4ˆ£S9ÚÐ]pËLö!å–uð-¡”CKB…Ä•üm¿|–gzR=9´¿¹»·ªíÐDëälç ìéH¬ÆIK Œ ÈÌi{5ýìâ“'%Ö§Wf`iõ yæ cñvÕ:b±ŒX¬$v/Ûâ^‘¹î_.øâƒò_¾hì¾Û.ÖÔÐíÑÞ+Ã/Xz/¢}šºôløÒèýFë@—µÿÖý–£ù¡,££Pƒ ³0¼Yì\ŸÚ…ÒÆh9jxùBqE¤Äˆd™ùâ¢ù"÷J:Ô.u5Ηå²Óõ¶©ž¿_ÌbI8Ánw²‹ä Ôk廇î]éiŸ25ŒÃ®gØh)Á@JŠ@-×~x39mÿ‰•ë „üžÙ þüíÌ¡{m@i ³˜›wÿ~€zÑÕLÍÖ[ÊPj Ó´öBÑðÞÉhÙÊ®U¢OßOï»ß8ÔÛ›etOïUï $N)¬Â@I™¿¤È[íÅ@ˆ€Ü NF3מåÏÌ `á;ç\‰cRr %Ìíh1Jýp$gã~ÉÓQq+aÿ€m‹×B?6¶JÝBys´ m6ˆÇÉî$S”ã8…-Æ—›D ’°vû}÷Yш©JX¼}’ü MúÌëS_Ô×SÒ=9…"¸Çà ;E%sÇÈø¬½|æ>!ô„‹ü€ƒh—€û*‡±KÈ9¦œóPÄ«çÜeÙ¤D+ÝÇ-Å OÖ9$jç=y‰ìdD0ø ‚ò¥ÙÞBœv³ ‚8j9ªÌ«^t58C¡|ü‡a÷´uæ)é©ò ª Y`ðgñ+t4ScãÖ±Ñç{ðÇ7r6­JÐÃ1 2UvøñÈÎ0D\Œ¤•`Q¡?9ÈF!jˆpG^Ó€+[–x5-Ó7/7´¬ðn]éwaµ_ǺàKëƒ/o˜‡/Ú×^\Ô¶Ê¿i™wÃ=þdÝb÷ÚÅ®€=KBÅ%¡R¤ÏWe…¹õl÷`ëA(Z®øÇ;©î¡2´5 à>©†ËhµÙL|àý;6Ð4ÖÛbýÃó¾®÷;O.ñ$‚áx¼Õðå;äTb°¡¯·zb¢íуâgO;§ÇÚÈ9œX¦.“c”g7 c²>»mµ\µ>½õtúòäô%s_£å1‚ê®NÜ«ûûÕƒõ™ k²"Û«_úÓ›Çüª ç·ÚÞûuƒZ0Dèíkì77ŽŒ¶ßì­é{ [­fëìuüg§zZɈLó›»Î·nõ.áZÕ…;_\ªo_âÕºDwyu@çjÿ ˽¯lÁº¸6 }ÿ…µþ­«}ë–{Õ,ÕÖ®ð*‹ò,]ìQ®@ZGa°™,Åó™Žy‘ºc^âíZîBqpµó'Hf>2pn]S#5¦žHÚ‡†ËÌCåÖçH ïøCù«™›4Ìß·]G[ÐuÛWÆvÀ@ø…',LA}„н"¬Ǧj-cå“c€Ž«a1{·co;®hÞûo& lR^e‘æàƒo€@ PMÀn:*%ÐPs±‹SÙ%kX¨3iZ.²0R<ÙéžlÔC€ºð_…â` ‘†@Òî|Ü ˆÂ`nêã·R{;ÌcM5c“€…±uoZ-7KÖ9¾¾+ôò¿ÖUzÜ2—Ö]YtiUpÃZÿª•Ü/ÍËôÕQêòÅ®…áŽ9Áâ‚Yi„²$L™ç/+q)Ô|±rz‚’¦ˆC •™”FÈÈòµ@†áBå'Üq¥¡ŽøÒµöÇ1û°#9tqH{GBã«,ÊJøT³lµ1ßÅ¡«¹ú—_¾y<Ôm™F#3t(”Œé9 NHÿá׋RƒÊ„d…¹Ev5¨;/ cã³æ¡±¾ùæÊ…³~GUЄº©Øð*OF'æBIRÚ€ÿèò„˜ô#Âc>LRN¡~ÎEÞ#Ú ª"|Ž/ƒÔJ.$ö‰ˆ=$L|¯‘l̹W|ˆö3H ¬½HÑÛÄK\b½£î=2¿F|ýYœ‘–'C•à¤áP^ë—@ÄÈ2˜ìƳd¹Ûñ;€¸Ë:Òõeç©3>ÌÊP× †­,I)€²éˆ˜{XÄÙ+dïà2¶2ìÖSm×Rˆ(‚XŠ’bg³•Kß's–Ū• Z—ƒbÆ~¡Ý=ÎC˜î#ËðuÈôæÕTÈ¾àÆ“i`§iYñsÚ.„·ùKÓ=‰À÷ÐH¸ÀùQvN(l*(%D²¡W(?~'mÄt°¡i¬¥w j ¯jz¤Íò]ù~O"Ñ‹‚²|DJ¼B%E¸˜nC7±5pŽ[e.©˜À~€ÃÊ ¢CÄk£ƒáü»? ñ Fðÿ5;æ:©1ÄYNÄ94ŠÙÒõœDO*ª·}¦4Õ Mñ’ÁÓÿÓNö‡ìiûøv¸øëYÄ&G^×vµ·û ¿üøÅãGÃ3˜ÍŽ={Ž,:´,Ó‡ÿó/ü>ÙÒ º€XŒ5w€‚¨aì¹uüùìøøè³‘ÁÉ¿ù©«=ya`¬Z’éÌC¶¬„€„ƒ ’ëÉ+ð•9¶.õº°Üpq…÷Å¥†¶EÚÖùm ½Ó÷aÉÌ/mÖ¾Ëã=šÕÜRÞ7Ñ4n½X5QÚÚh]óJ0}ª—’•™ažyážÙ!®ÙŠLE²¯ü´·ìl¸çkÞÒ%*›Ŭ²mî7?é®Ã Ñ„&d¦Óü¤x¸¯ì¦Þ²§ˆl¸ßz œ¾ËÏÎòÖ~ìWt .wªöš~nø¥²èZ¸ö‘¥fÌÂá¾rëp‡uàZ[Â’= Ø2¢µtkϯMÓæ+dj§äÀŽ¢ýªS¡¶ñ>ÜwhT€ £{'ÃŒ@=rfÄ8@5iƒ§íy7)œô–ƒ\Z£ íË|;—û`ÿ´.ÖÐhYèÑ¥«Šò*ˆPƒK€÷[¼P[±Ä§<Ú·t‘!?B“äã„!Î9­4=PU0ß«t±éòà½Z1ζùqËîÿR? ò³¥‘Œ×3Æ N¯-툩úýœ%pº^¼â ”ú¢'ܼs>¶|ãkò^žcÇal Üæ5PæÀ¥A7c=wJœ»Mœ¯tO¤éaVF- èžz*ª YcAf“‚¯ 7;dN!° Ô¥`¡æL°ãK*"koèOŸ”Žš¯˜-ž Tö™ÊÂÃyöAÝù4c´CÉYY¤sõ ÔèíËüëëK—ë+×ù7¬ò¯ŽÖ§ù;å„©ò"ܳB]Òƒ”iŠT?§D½Cv k†Ÿ <ºÓ½]r=ŠÃôU kù—„¹dû‹€/Å«i t¼Â!îdGdTK °ÐÁÙ8$¥âmâ½ïd[ðŒƒ¬Þ–XÎ$ôæ•üŽÒŒ{ßÚÓwÏ2 $wÇ&45hgòþ/¿Ðû@Íì4qÉ5û  JyfÆòüÙøä˜uløÙ½ïïßîÈ_޹“ðÒ×qªƒ]ª#5INIu¥gixGÐÌ€{†Ë ¹¥”ä¸âÁt4`< äãë±KLì‘’ëU>±_F®íx.ƒöìH’UNÄx±Êiwâ †ùÄ+xx Lo_òà{qˆmKU~+·§çj¯©Í<Þ BšÅl.ƒ ŽQ(5ð!âyØ:öºuô톔èÍ¡b¦¯VmÿáÃâéá[ã_}•9¤õùkw«µïŠõÇú¦ƒ^çRÏͧ§/Ÿ_$Ì_éœ2_œ)K'Î_¢*X®Ž äž f^K\1úY%Ø€Ï-W°îö”~ösÒϽy#Ó5Ó3ÈHª³ÎbÓhµ4[¡û¨ªÿ½Ø7³æl‹€©šÝOâ5/b›’ØÀ'bÜ™ˆÚÁG ŒÏ —鿿⃆úr¿€lêÃ3VB"Xx®°Ú…g½ŠuÒÓþ„ŠÊ(6®ÞVxZJˆ0.¡„½‰Ÿí­KÇ,æ+Ã#&Ÿ^ìí©Bÿjͳá¸]‘Æ€3·{>ÌN\'/ÛôüÛë­çÖžŽtZïN´¤®†Õÿq€jB‹ˆRƒ8ŒY&ª!™ì.}©æÀÕ¿Ôï=¾\éN4änxümÝh_;šÉg¦ëÔÍ7KV¥-d… Ä2Á¨<Ë•™¤¤¤¸Ð£—¨`ÅËi‰JL}ãåvÉ®Ìl-Í0È{Ø9Eã$jpŠàV·OHŠ¶Ð‰Í4â6±K@ì‘À¶ÊV±ºo|;éä™ÍË.ìȸI\ñðnóäØe”š S%ÞûÌP+˜œŸµ-[í™®æ".YÎ6ú¹\ó *˜>eÜr ê,_ Ï4_m’^¤wÃJñvÃ7s4EA^ðÕ/™§\™í'ÍòážEHkaˆ°4LXÂ/Â$ˆSÂ&™ª&’œç 8#uÐxR6Xç(ô)zÑ!ú&)1ÎÆÛ¼¾û¨pdðÒèD§e ŒˆòI’'ÙþèŸ)´Ä5µòa¹Žˆp§Á±ÙÏR§¹퟊~JÍ9¡bî‘×ë5>±F¸ðPÅiÍ0(C ²‰ÃB›2;xs!Æ0SÅ9« Щ2Ò£y¥ÿÕ- ÚÖÍ3.Ôfø:×I“õàH;Æ«…™ÞŽ©:I–ŸsZ€ÛIÓje‡Ÿû›Ëî}ö~÷ýo‡GúƦG'@ø}Qjþ·¦€ñP3;EÖ™T›™¥f*ô6ÓãÏFMã?|þÃ¥ºÔpïDx”ù)+½$pTF˜R8ó=¹ùàùJš#=¹7-V·.Ñt,×^Zéձܳu™æÌ5  ˈ6ë8ž€Ž{nœ¼}&´j£¬l…¸t™´0Z’åPµÅ«b³÷ÅCQ%k½287­iXáW¾È½z¥Gå*UÛVß Ûü*–ãú¸_ÜâÛ¶^ùeÿÛ;çÝz-øêVŸ«[ ·vÜÞpy‹wÍj}ýKMk}k£5U Ý룵«j7ÌË_|4Òe±qb»î‹æš;ûûj0¸Ÿœ€OrݳÁ&ëHËãûVëuëøµšSi\ÞÊ}uöûko–ìIN€HV»ÖÇd©ª$3ÊI¬æE©©®0GÆ«pÁͽ¥V`Yc·»ÿžŸ¸Ùu•ž(‰_úãG…ƒ÷,Ý PB_û×…]…ëœòæ‹áüYlüÌK ¾Y¦Ëè-ƒZß,õ‘z r=¹àé•ÀëÞ¥y‘sËb— K]¡»´Ê£c…ªe™Û…u>-/ù7¯ó«_e¨Y®«[©Ç¿¶o ­Z¡­[chÞà{aK VÓzã7\Ø•""BJÓ6Üÿµix°îú+§L5 “¡2îñN €uœ¥à‚5šæf8ìdîëñzv‚žgàŸöâœÔqŽkX'´ì/îo^‚ 9P”,Í qª]T\æ]ä^Aqˆ[Õ|φEžÕªbiž^’?8Ì…zIŽZåÆ7êKu’2½¸ÜT!„]Ù^BÈ©J–zŸ’­w#JŽG>øwÕpßÓPã SÔŠ™ ئÏ2JÖÛ7¸µ½¤n[§½º%ðö«ao½vmc@ÛË»C»vÆ©[éոίa­oÅRq±{õ2mÓ\¢Ê(Úe: |íëHÄH)¶JÍB$ÃÝ©4@^èCÆÐ@îQÝb”O¿+]'‰UŽm§ 2¹ØC%c",Ùµâ,'@aÛeôjIé½ßÿáÆƒ¯>î¹7jÄÙé“€k¦1YúÏ¿þÿR33‰?ŠaÔï] „á¨6Óãêùî_ßuÔ¤G’=%ÐãÀt=ΞÔd帳òáRî-¨ ’A¶SèÍ;KGÒ^l_&†"5ŇšÈÌä$yÓb=È*}Þ‹Š¨µx­ V¢Æ&Á“ÀŠÇ«ùšâg—ÌÈ bgù³3øé’X?É>½=ºâå^¬Îª#COnZ:GL dfÊ@ÁÔPٴو`8’NLØÜ4en'­nÍ×M_–ïZL]ªÃIßo?ʶ>ÿã㇈3è´ÎtY'aòßt+>¼~£¢*JÔ²T^àÍ€‘ä*eAçáA­æÁ1Á"0½Gd*é¢'¨ˆC "Ú)TR!‡QUFˆC\x“˜ØìHl’{ÝívâîF¼ Ê¦x¹ë9?zr(/3\|Ô8ˆ¶Ü‹vNc¯±ÅÅ9ïi“ˆrm‹Ktƒ† nn?Ç ³w0TsĉâÓóœ6j¨árœûñ‹R€´Ãµ˜ÚOMvõ´ªÍjn½ûc¶uæòô½Ú]¡ÄBÑm}òîã?W$¯p}É…HÞîñõ_“&`Éþÿ*5P,ŽNÖ`’5ÔSò‰ºæ«_×Yb¿Ò‹H;òõ_³G·>5]˜ê©·Ž\ùñVÌ Ô#ðö €+‰ô\É£TÒF>üqB$·R!LÀþ)󆿴ì 0»„}VŽÎ¶Ð‡Ž©7é«.É0p³ýÄ9þ’TÔwêY¸Üèþö…áÄäÏe°S|èÉÞ4ÀÇ=mѯõâ\©=2ÐsÅ]+ÌÐ!Ažo p<ïd“¬¤ç Á×d‡zÄx+3Ö,üsuÁ·ïÞþí‡ËàÌäbçÈyÒÿVipÂB%B3#ÏIëÔ44—äøÉj˜E ÔðôØähïèŸþÒÕX¼$¸0е&Ä:\¨äŒa…Ÿ´"°4«C¤àv`G…!ùW!œòyœêùöu‹dM+5« M+ uK´ ÝŒó]Á©]ìyqÓR]ó2¯Îu~W^ò¿°BÛ¾Rwe“OÓ:׆uŠúŽ5ѵÑΕÑnáŠc¾¢ù8‹E¹|úvÁäÀë#Ð(™ñ´ÊŸ2åϘJàÝên5ˆŠ#ý"F®YÇïܪܼo)}‰žh+Y}÷Ë©±k×ýv¯nŸÐbÿpýpÍyëZù•Õ®¥^4RXáÅ5zÁßÃÌ%X>–:‹ ”‚TnÛR¿[›v¬ðG{S»Èøȥt™kóŽ€›Öï:j>V³Y]°XдZѺ^“ {ÀEîE‹= ¢—pa”Óö»2_VØnSS×8~°‰R¶Å®9·D‘±RýaÖ¾âåÚ´p¹q¥wÎB·ÂE-B[Ö·¬ ª_ªo\êÕ±ÖÿòÚ¤xÀ“áòŸ†5ÎMë”Í+£%M‘â¦EŽU å9áNûµ´NÄrOâbé+w[ :D¼ÞžZ‹©ô­2·cÖfµ¾¼ñôrî±ÖU‡­=ïü£!k½Ç>?›¼C>÷?Ïÿ¥¦–Ìs™© ¥7‚µÿâ̽‰eDÜvÃ×Ξî-ðêS]ú(§x½¢$Ê ^•¿¢)Xݦmv¯òU"fLZÒ!«¼*|0©„¬ ÄÙ!Œ]Tj¥?æÚmËôm+üZ—û6DyUÎW•‡;WÎw­]äVµÈ«xž8Ý@OÖR2|h ¤ ׈‰Ý T»=j¾=ˆ|Ò‘¶ç–&²ÔD»u|3g‘c®FPí£l ×5/Ѐ²ŽÈR é/€J¨)Òµnke¸"ÙƒCª /N¶žãÅÎÞëa—ên—ï#…6?Lã]s´ŸKÛ2uS”kãBH>¸ jC¤MòÆ0E}°¼ŽtVw†…f¥VZmpÀÖ*÷q@GQ¦VsÜWSÖ¶Ô –{-ýÈ@PSKÎ@'@’¼ñÓëgË–96/UÁC»}¹¡c•o[”®s±îêjß¶>5k.HÜH„Ø:\TlÄÄÀÎúè›ì!f!àvø‚“êF$»âX:ç‘X†MŠÖ!Á 3—H‡nƒnx¼°/ƒ€]A3Ó}HxzØ—RÛíqA¸…d–~ÿ]?=Ð5øá5SEVK9¶g‘¾TEB—j— k÷S«6ÛRßlÛcú­þ·»ÓO¯[Ÿ¿ [•ŸÞM.Üæj\©Ä°TÛI’„gQš)9޼s ›4{&B‘PÛ·x'æy%;]Å?#µZÿ¬+íœ'㜎yÞ‡—".“…J ~É ”÷nr¤,¡ÑTbXÆ S‰É?ÙŸ–µ'¨-}Ã/ïý©öèV?Jì ç/f¤D©7ƒh†‡A¬Ç#†Nª¶ñm0Bö8¡=ÀôM1÷˜ó!R܈sÐêÊHcI(¦¹¯¹Ú,…/zëýkq3ƒ×ák:;Úb1¡!ǨýæÐýêÑGuÖg N_ýøÂÁþŵŽ?¾“míÿÃìOWs^&°BòõûéÿcØ XØ\=`‚¸LFË@%é18óÛký‰E.Äñ—<>{+mì 8‡rž’Ÿ«@€5#YHM‘°àCXäᘋhoGÖ9¤æ)XyZq‘¯uó4p:åUù;Cd‡Ë•ë¥<ë*=¥q^É¥&¯]ú§ÆªÇŸ}l~pßÒ×?>:6=÷j3Ws^|ùû+jÍ,ÔR¤4“TqcA Ž1`«Õ4…–é‡O~µüòÍçu]××ûh/‡ê/¸µø:·¨:Â4Â5(ÝÀýÊ µA.8‰Ã䮆èÐÚ!†p‚d ¾CXøÎÐ)Ÿ *‹rVb €!æ¡ÅÞ|èDRUY„½Y>üXwÆqOÎQ_Á±êãú=½ßטÕcJ‚T¦gã¼Îô7 >4M6õO7 ähØŒ€¹pWf?ëØ  `Ûrl³öö9Ž[.v?…ø}ëØ¿B$jh;ÄÄ.ô î >e?*!æ‰(ƒìjÊQ6ý8Íî@'è¶G ª»±nvèÙžsd!‚3I+Í vÉvNQæD¸¥Éãõ¢¸ §—µ‚Ujn¤" ?_£'ެ”¥îÑ]){郮C>ΙúµñÙ½ö;cˉ¤­ºîkî½S¼ˆMbň."m0õ†+}ZÄ>%¦“e ®O¹”«;ÍÑØ€ä z9iØ žd,%X±WÇ[?ŠWü¿ÿ¨ÜÒw fÂÓãM˜MƒÓë¼G+F1ÄŸx×úðfÕ΀—ðωúòù³7¬¿^Úè@l@ÁÊûü½ä›ãÐA¦Ë™jàÿ ˆ*ˆ£û»‹žN€§g´ôׂl…ÓàPW¤'±É›¹Ä•øòí¬§ƒWMýµø¿››»µ§Wi‘™©”jF­ŠêPß–y!%žF/­ÑWWâëžgpB‚[®šžn†ˆÈ@yÁÓÕºxô“p3@ α9ÂDÎñ%¾Bøxº;x,– ×m»Išx?y'1Òqœ(ÌŒþçç9¦ËðÛ9ÀÜê@’Ÿo5xÐ÷ι,r”"6›Ê¦'È/¬OÛôÕöÖO\€ÕÆÚP"RJ1ˆ7Ž˜~k4™j,uVëe´Û}ˆ´YºF’êÈÏ” 3ä\„ÐÕ8ç+¹y6Šé#@Ô,6¹Ðôbÿ0~ß?Gà0ɶ²°ÒeÉR°F²Ý•Ø?zwDT׆êó=sÕ˜"QÎË™9Zi¶F£bCŒvþåàoÿTdŸ¡Î Ñä„z¥ø¹fºàQîçDNdÜù¥.ˆÖe ¯Ê¨d”ÈmÒ Áœsf†;Í1ÉGBLg¦ '× MJSŽ+'_+,ÐJ <¤h`Š=%žÊROe±»²ÈMQ¨Ræ8;žóÏرbÞá 1ï°€uÀžý“þ Ån/“}ˆÍÙmKÙMØâÓÿWYìE ÈmqñÿñÑðýû¦înˈÙ:9þ|z Ìï¥fΙïE©!ûš¹.Ž͜FümàÉÃSOG­Óÿú廯çm\“ TdIÄñtJ,ÚW‚ôKD::ºÃ4Ò}å.ÙžŠt7Y¦JŽò’ ±Ïtq:mÏ:ʱCÆú<žþ+6&x<Á*V$©Î <*À¸†>÷ >—–ªkç»”†»å†yÄ(6*ˆ:‘´ÝëÛ¿d‚p‹L92zÉ hÕÓ¾FäQöMÖý0®ff¦“ôÄy烢Ã[•¶Q*¢½x߃ÏëWÕó°üÙèëÔÇ©kôküŒ+ür|]³µòƒ{‘Öí$—~ž}\À9bÏ9À¢£†fÒOð81RáQ‰ýAg·=s§=m‡¾SÊØ)gí3·9Q£™$q.ÔŽtˆ]*%6éq/äÆ,ë4xÿfê÷ÿ4>ù¡Î]°sÁdÒäöwÓ½¶aO„´1a}÷—µý_ÖŽ}\ÿ²‚¤2B†ƒn|¸à ëE øžŽ_$n 7 ¡soǤožs}°²®ªþŠD­–¤¡b½ÓQ²oàÞek Ãût´uéá ûŽOwZ,]VË;–¯/¼ª!^õ#:ÛvÜ{R7‹ðˆ‰?\:4ÿ¸)#:ÊövÿZ ’0›¹®†¬0ÃeÓcuýO qÍâ å œµFaBø^Uê’Í:f”„¸Y±¥ÄH<šŸO‚Ýr|ý´7 Yi$Ì j #)ç¦4‡ð±ÁDÿ6·Qž1±s²=Á„ÏT9w¾Ø?gy¬“ ;¡b` '€h´%YÈMð`7ùIºÔ8¾±Ü»3ÌVäå°9P–¥¥©ùð…¼ ÑÞø°£Á¸pš|”q:™ŽuŠJ.|Ȳ<䜄wš¢ek•Ù^.YzU–Á=ÇO—aÐøƒýpqæ®.M­:ëê¼…ÍXÃçÄ-‹z«ªìѧÿ{rr¨ïÙˆyv|tvz ÎzègæÐ²«A‘AÏó¢ÔL=‡_Í ‰íÌý6þ9öôéÈÓñîG?|£#&Ä÷‡‘Àa%²YÉÃI’©pÈp–¥ºÊ’]âTÉ•rpÑá÷X¬ggºSç‚ÎE‘¾Iáž]ì¢åD}ÒÊÇ_V>5]$OOs=tÜÈš}XaºØc©º?\:2Ñó‡™Ñ.kßmXG2p~atÕ¾Û ͲÕú:Úþ¿–†¢&‹È¨|Ž0n:b3W3mHk ñ»æ™hÁéB¹ÙC 4ói¯H¹ÛØëD”e\"’MÚ‡±ÈäDŒÆv…I’^¨9»îfù¡ïþ\r÷“šþŸ.Ž=éš¼>9|eÊÒ15zÌ…œDd’Y` øÖÎDŽÆã+†~lþ±ÎÚ{ǸEñèI8¬¥H¹qbf"²n|˜AmZ,ipl›¯( D^¢º`׿ù: [½zðç¡“\ëõá­Ì‘'׆z›[ó¼:áÞCêÜÇÛ̃ØÃx³fïjœå·º¼õÖ‰‡MàNC+ñ·¬Í§|™a<¢:mÝ£ï#ð3‡/‡‰Ì·zQj`¬MFG ס«AbÔx£ùQ-†Ú]U¯.^a¥1‹~ý¼°¯Î6(_-àø]83?Ö—ò3¦N°Å&8ÖÚâkjÙü4G16ìR]“œ±$û'KMtrÿ¤8ˆâœÓ<Æ)> ¢ÈÃð«[¾½m¶›¤n¼‹ƒë"¼Ðø¥zˆªsÄÌ$R.øî£òÑ®þÆášÑÑúÉÁ:Ø>ÿöVân7b7ôb0½Äó‘JÀ¹Ysm¨`ø…±ÚGé¶ûq”˜;8ã¿‹ß³tü12«—KE9ͱ¸”â±Â,ƒ€lìppž±Õ!ü$ß/3]ÄËu*eù.Ži®Š³*ùA7‡•RʉU†ÜÊ|Ð9ØûˆzˆîŸ‚A1õ¶õ—®=˜cJ`º¾í ±íI)ñºÙW¢BCo7ÎWÀðªÜG€ð8ÕâÂN°4ÄzL±ËƒÜêÃ=Ã5Õ ÷ë%hir4d ç9í9>å×ö,ÇaÁ‘`玂ƒbˆçðÓbêQ ®bÖf¤ìtŽâˆë€¶a7…ç9ê Öôó§8L¡ô`MÎ<·LM ›LHË}£+~~P¼3.”â ƒËdŸå°c¹ì6ó‹v€aƒ3w‡ùtuqè§„ögÄÂD'Y—gëâàúùúªPÏò ÷$ïèy^òƒ¼Ôǹ2È­<ÀµÀ á§|äŸf¸q CìÂ*i2HlwHi¿Yb1é"'âvÕþ¡Ÿáè{Æþ(5PìÂGeÔT5ö¸lv¤ñQQÿDÕøt#nÜÖž{¼©û|8É»B?y'Ïòð²åI+ÎÃ?—}Ùxä°3ëJb­ØI„'"Èçt¸6`ÚçÒ³íö1mvò¨Û•ÂM.ÂuNœ¥bêÂ!‚¹D°=±Ì²;Ê9eOxKÎŽodüð÷ÊûŸ6™~éru¢çÒøÀ¥1SÇÈàÅ!s«y´ ±’}}åM£˜xÓjþãJ DŽU§ÖüòÏüÙ¾vëØÛ_׬Xâ [lØVzcÌç\¨* tÎÑ ÀlÏRÛåºSÕ µ”ƒx)5AÆ‚.lâVrˆ&‘s ò§"Vïš©Eã‹R3eA`4ÍcH$Þ;²Î!ZMdŸ üðã¤nKKoO5„;N§F–8Ƹè{_âˆ4g0SéÂg®1'Fª§¡i5Uáòâl…ëÐätw}xýÔJ9Å' Ï{ðo¸ÙÔà~y>5ÙÍÏ›öÇûs“\Ù‰"j‹ n½M’ÚÅrè§ÙLìœ. xÔq.ÈØ ;lžÿsÿ$Ìퟋ‹Â[Â|qâ® ò,ó×Nž¯u*ÒË¡<+¶#µØØüöHÄæŸ”3×ó?6‘ujÙŸÕ˜M—û›PjÀ¿zŠ‚áK¿Þ:›)ÊŸ¯@`Y—,Ï]œ*cA¿ŒÌn`­ç6'p”ã bj,ÏS£P PÈ žþÀ±¹6'8”3bN¢\€§?‚¢ÒxÙ ~¡«0OIÚxâ5Ï•Ÿë*Èrág(¹ëœaÛŲhñ,z›~žË:˦bÐ^¥SÖ1mƒmˆsC¾ýsåx÷ ¨çÈC+Ø’` Lܱ>z=7J Þx¾VV sÌñ—øJ+ƒ¤uþöå:Z¡;Ü{(“`aÌt²=D¹ Vž§èÚŠÀ–†¶%¡yÁº¤@Ín7Q$ìéVz¼Ž÷µš{kAE©ÁÔi|¨ ‰Òˆ¬}ð$×G‰»³TODꈼäð/(0ÏtŽAå󿇥¥«”[as,ôçO²Íݵ“Ãm(5cCX¯–Ç®ÂÝE¨6@ŒÁµ&åNÖ·{¾(?*ÝåÅ.9Öóu¥¹·ÎSÁEmÌSÚc•ãÈÉÒ’6À|á nO¤;Ùiø%ÞâBƒ0ß[e:Ñé{œËÊC•(wFoi¹#`7T¿|2zŒ§ä€»è57aæÚo$ýëõ®‡ß|‰ÆCíg(RP @øw¿ˆßK Àar=ŸÅBAsczòÛÀ·Ÿ}s¹å¬¿ˆ(¹rŽ©bqÏç$JD) i’Bš ãÑN‹9'íXñRþ).§iì"T?LvÀ¤'YŒôóÎÌ4w*OÚ¼W£?À‘¯sÀó®-ºš32þm‡”³‚Kø Dcƒá«?æO÷]³ôµ˜‹0†RSÞ¥fv°zÖR×Ó—?:Y‡ëX»uòõ7Ë·Z,BRvSæ†îïšaæ0Ñß6Š'xOÓQ5©ÏÑP`ÆU¦aåÈlŠ•ÌBGf¦Ð.½ 1Ö1 ˜?#©„ ™P"bM0f[`YâÆëõ1¾‘óõG僯›^7÷wõ÷t ^€¦»·Í2܉JˆˆÐç†FêÌãCÓƒ3õXãÈn˜n$5ž·ž?¹¾Ñß.˜C4Ä¿ôäÛRëÌ5ØS$,fœÑç] 8Õg*hi &‚HÐÕÊó}ÄHèËÕ°³Ý˜@⤴8ü\ê>–Ýç;´¸þÜ®ª}ƒ;F͗怚&dû÷“ÇáÊéÉv«õÍÇŸ•n·YDT—¯úüûœ^ 9 AÆåÌ_òÒçs7¹1ëäß”ŽŽÝ×è1C©Aoƒ3ÔÄHͳ‰–¡Þ ´=ý`ãÜþà“¢mîÄjœû ¿z7©÷·ŠáþÒç|üæàŸ²÷9‰.T(ð4´%Ò0>f¹JÇT©$^À?'àcÿ`^ä$Ž— ÑIžq±yNñ R.@Ô"ì̉P—дƒ/³KpĹ^—Ì£ñ«š§3†i1ÐÉ›§KñW4Hñ8¨Ì}õá/Cƒ]fËÅ¡t²ZÚ­Ów>oØ“Á;ïEÏÔpâ¤Dº’‘¡`$JIRªl½E© ·ixx…\.ǰ֌ã©[,܆8%ÅÁ÷˜MšŽ’ÆOðeåçí)©BÖYÀ2Ð3¢ùÁo‘Μ¶ "z®Ò!S*Jæ³°pŒJw'¤B)Ý­âËÝ·ôîGõðe†ábßaqîäÍ‘¿žÒÚB²Bç"ÌS‰ÒtD÷"Œ²Ê_\äÃGžZ¼÷”ž³™kδãÎ ò´ù-L¹Qy„1”¼2%!à‘”æ0uEˆ …4Û™_ #ƒ»ÊKQ㥬Ñ:VzˆËÕöðUóÊ4< :‹½¸è¢|Ûy(¤N ®KM!ÉEˆ±pgä©X)`žÈ(è0¶;© –37"LÃ2cÖÞí·šêÿÝÌè\ÍÉb‚)ˆÁs~Àfæ¾CÖ´7ÐAa A—03òð®ù«O~¼Ø˜¨ÉôR¤Ëù \z,ƒKʰ·gÇ ˜ÇY6û(P<Ù¢±q¶çA|±°†ç¿yÌÌÀÄM-Ìëe`1ÕFx6FzÕ…iPdâ¨èÍPað×q€:ƲÉÒ©â5Îñ^;=Ã’shñOgÍ7GM-æþŒqç‚ä*&+&ûK­cu –MM7÷·éB‚aÜKÒýóèk5ÄÛ͇L¿¶õÃ(Æúè²Ï,«]íP¤·)6ØeâÌëÃ+t£Ö$yNT\ðwÌÖÊB<Óœ´¾êLÛIò®ð¼¸Õ—*ÿívæwÿ¬yòs‡¥÷ÆôÈSOçøH|üúš¡62 7õšêFÆ0bnÁà>]$™ßRÝ;ZùØb|d)yh*)Ÿ™l³N\ú¾n½/ûãÞч $M¥¯3v>¥$Zš½ 9TÕêÑ´@Wî‘¥—Ä:Úàì ì×yŸBÙèÈ©ÉP+s`†øõr^†+ü²ïß_OÄDVØjHíâ}û$¿Ñd| òÞøµ/ÛæúÒb»®®½wWMw’Ö¦ö©÷²Óæq^ó$N®pøîï(5d‹Rƒ3ÔÈ@%J©§1ëL›¹¯š f$öÕ{%~±ü€–yj©ìç÷sF{šA™C&/&ËÖo›v#ÙÍ‘Ä.^LÊÔbÔœxí “C·;æã³Îòé<íCü“¾ß÷îÓs|ÜËØ?´a5݃[hy‹K|dèðkç©ë"tUašâ ÏÜ Oø|îv`„„†A3¶Þÿ¡ÍÔwiÈ|ìqü´¤ëà½æ?d¯N ãç *ç)ÉÿšÆ>[ÍÉÑ qLH7ˆ3ü¥qz^¼Ÿä#Œó`Àƒ?£©mªÖy!ê†Pus¸&Aºn¢lgDîò²9är`cåHÙÉövÉö´,/×Y”!ç§È8˜¾åâýR‰IàâhFÛϲ¤k½€4dÈÙõà“FËCÒ‡Š‘Þ`@º†ÿš“ÌE˜Z¹^YàYåë^ê‰ýPPN­ð´£Ín±ÑžXÉ%ÖðˆÝδSZ! q‡ƒtUó¼ËµÀuqeÊ# %¡ž9>r([á$?N…¸1Ñ}e;q ]øÅÎÜB%3_NÃʑ۸P‹In‹&ôx¼¾[„˜~ï—ÄéàeØåщíBÊ6¾Í":±ÄÝîö¸ž_/ >¹!ØHà "ƒÍÖž«1¡vçX0ÌIp¦¤©ÈEª˜çŒ"§Ä@/¬eíõ ít#ö{Ø6pc\ì€{ìÅ€ŒŠH  B:ö#| ]÷z€yM,B9_ÜNtÒ–8iC¾Ù{z¢Ë#‡S@ÂûàîÆ ™ng„v n¢xåÉ Wh-+άyô9B…:†UZÇNJѨ#Kèu;ÁATE <ÁÉþ„˜–â)ÉõVžW‹N8ó»‹vyJ¶¸qÊçÛ»ôWƳÅÏyœm‡æðé”n »¨E,’=‹½}‚Càj#$w\âg;2³¥Ô™]žÌ®TÅI—)""SI„M_¡ŽÚ6¬¨Hz$²€ñ}5½ÌÀ¯„¸AT¨bŸãÿ>ØR„é؆/ ˆ`–M”Ný7n÷w?À¯§3ÿ]jÀ¶!#æJͤuîÑçæžê·Þ¿;û¯|Q”yí•Õ¯o¾ºÌ4¤V_U½^Q¥s,×8 ³ûL9š²T™-î‘x.‘&$2ÄDºHu!Žå!¥~bø#Áº6³Hu‡ý(©sY$8 )'O¬)û¬”“ì¡8ã!߯–Eãð¢e\«<`ºÛÓr˜{« çÇfÓÃæ1°2L–þ d,8ø´CMsþÞµp¨ƒ¨.ßÈýÿØz °6ïõ}ü î!Á!@pw‡B••u•µ]uu£Š/! îÚB}Ë™œÉÙ™K×m]K‚ÓüïOèÎÙù}ÿ»>WÚîj!yßç}žû¹¥‚$ Hª‘å 54\Ðgž÷ÀâïÉ`Å£§ÅʱZlŸ!oQ+;‡¾)ÚGH6ýñö™¹Ñnbe£¸‘žlR‡‰’^Èiô³)u¶„_üР—ÇÐ \ܼ?$h€­ ”æB W~hàû(°‘ÙTg妻ߕ¨&.Ž(‰çþ-ØåAþ3‡D!UÒÕ? ñC!ý Æãðr‘ƒÜÛ¡Fdžòvª%…ag‰ …‘óã¾£²þT›ùR3:T3ßÕü·Ô £ÖvÒ:âæ§¼!ÜšgûJ Ö»í{FžboÕ"ƒI…ü’z°/w]`׳Ðëj²ÿµ¤Àîhïæ@×Ö`÷FdÐCúQínNrþúA©?ÏÖ: ²–)—’ùëW~=âBžKGh5²*|,!‡9­=ÎúBoë’·ìçݶfkMiåÖø›З5úð²l {b´ùpW&)`Ï. Ñ~ÕŽÔ¥-¸éؤ "ïã”ã¨?’tè;L׺ÑrrJ­ S]ø‰¼›/%v/ jˆôݶ0Û®ÎxÿÆPžÈß©)ȵ#Ì«3Üûb¨æøµû´…úvDvLJu'†vÆ…4Gû5Çø4Exˆ`YlU³*ðbŸå1÷º˜mt6ð×£jN-ïÿ±qFÒ£Àt¯ɇŠ’ õDן·N¤…èÁ/½ÀÇ&ƒg}ÎÃî”§õQWæ³VzkL©]âs‚¿ÁZ~K*3Þ㬯}ž·ã9;f:‡~̰ØÇ5ß×%Û×ñœ‡ Š²Ï»va"×z‚¨!«`ý`![§€£}ÆŒ € ¯`! .ìš‘{‚ sÞM>Šå~ôRwÓWãJžEóR¼gO¢wW"8t\¢(·‚Þ:7Ûµ_ܾ¦xúhR)à ‹§ù ©3¤ËŒjC$—Jõ„bN51©œ|¦¾w÷AG뽯ô­O¾´<ôÊÿ¦@û–Çö`.¨Â=qÞc½Ð^‚0ÌÇŠÍA?A c5uÁ†vC^@@‘㪛é !uƒí¢ òy¾«qs4ò^èë1ôi˜–ìr/v±ëÇøË4¹ãÕ Z$Rx"­þqå,Pˆq‹RÞì¶“(5J Ò'¹¸tF…8{ö°`÷i)ýGqþçuTö.ß{ŸÏ ]F~œ¦Ô\¯ßêÚµÒ¥+É©5ѱc ïÒšÀËkBÚaýáTîÅÂf°gUtaw¯9ÖëÙ¯ÅÿiHn%0PÀA¥ŒDÀH`(Z坯´–0k•‘ÖåšS1=Ñ€¾ S¨³ò‘VP³àµ4Fò´fz“E'šùGŸfm¡N,·¼÷næÐïüñÁfù7;¼©S`;S®FÕ\³Îè ¶F§¬õ!@.òÂ:ö):Ã4  ÂæÖé&Ð(lè^‰eÿãæ©§…ªé6ŒiððArþ9ºÅ‹¦Á*‘vß»uúp„ì=¯”mzô½hÖ£CÝê» êß.fÇ1ûö.B]ï®{§ ÌØ¶ 4)oö¸?ìßK (ć§­ê±kŸ”lËOvBWÓ^´òÑ]>–n»$ï¶øjõzç¢øÎY7…9vDò:#=›ƒ]š\š[‚]ð;¸~ºc=Z!) ±€É ‹ TBUhƒë‡*âPùP^@€ã¢ ^´†¦B!¨ÜË´kÁ¡¡Ã3a§ h*Ô*]âž·9k‰v_½Y0þìªr°{rô"؆$L¥æñ¥œ»ìxëŒPVqœ£`©×…xGHÏöz¯w"8ÄŒ aQ+ýi™/û\:³Zrµ¤)5äÊš˜–xïÚ¶¨ÍÉ!µÑ>MÑ^¦ ¡ÖÑàÇEµAi p¯óáÖûº4ðšB<›Ã½›#½#}¢pú5ÆxÕ†¹ÔEpÛ}á9S‘àu:Üa¥-…]CCMØY€!ö|ÏÊõ\«ZÙþiãæUÐAàÆñçÀž(/Á÷P€-¬´âiT |ð´©hcj±³ÙÆHnÅ”·Ê\9öÒ>gc äà)¶0o¿©îs]¼3a™ö/è¾XgëPÙ¶&À«o#!+³JWz£¯]G¨óµDßKqàoŠ¡ÖÇ®d×®$^[¢[sÞA—ùÞZvkyè¥x¯¦`Ìø½ÉÈ1éˆõlŽrkOôÆÂ¢(Òmƒ›]jïÇWº‡ÿE%žŸ¡°xBA©dC4—³„Â7¥ž’©Çe3Ê1åˆòÏß'¿øìÍcGJC¼K|íêGŒÛf++p0zßÍë[òzjTw’ÏŶnK„cKˆ (Fõ¾ŒZ_†0ˆ…]0تÈèØÝ/"ìAlTÈ B °j,p6  .ØR‹J?{8žsgïãZ¬d’ “³ƒ~ÿ—@6p(„BÑR%„öó¥fLÊ—IJ'•(5BØõO:2þƽžSÅëyKÜ©ò´˜þ¯3—žÂ­OýäbVŒŒKÜ !9ãHËñ1Ï÷§gº"ëc,:¤¸ÉŒZlAù›P¾ø±#Ч4íJÙ0Y¯™R`S k…œ*Í©—áTá–Äv îĸOGž6A{%{Ô¤xÔD‚¦nyËÓ¶n}%’Jß`ÿÛ{Y¤xþÆì½ú3ñ…¡F>†Í¬ö@[¾‹9žòx¸à=Ïáš!TNìIJÉw7@DËyO«cŽŒãžÜÕL£X êÜÖ0ø[ŠGeÓ Cò²QyJͤ¸‰¸ÇÌApQ=~ûSþ6~ª÷Ù(ö×geßw<Ò§žùDýôÖ§ù/ׯ*Yì³Ë±Ô†ê(^ûøçJB‘R3JöP Óü½ÔaŠ”!W•½Ÿ”¼rÐÏ0ŽNUžŒùãÇ ¥¼{l¤c±ÂO/Öo÷<à@ÄJ˜¡Ò°à Ó€¬" _3X:ùŽäúiv½º4è5Q=IÞ]ñ¼Ž—öHÍõÃlð±¬õ³Ñë¢8ÂœÚ fS¸5îdÄ“;Ñ¿,Ø¥(Ô½$.pâF®é‘•n?}X‚VV1ØŽg„¦Ô ¸ ]Í•Š­~+ÝÓãmOG°÷¸ë­³¡Rm©3j‘µ!ÌøÀZ—¼#1¿y=gü»võã÷^åé C¢ECB‰°]—°nƒ?sDV„‚9ƒ€9м> ìZƒ/áçEaÄwŒ©…•@ÚXÂ"S”ì#J )^ŒÀ¾eÚ}¢½h›U›¬š¨™-™©'»o•.;gr*Äò¨/}Ïx)›J¶¦¢-(|Ö«¹´}qy[; _{ïbÔßù°xï™ö¦0¢®† ìïVæUã]ã]ë×”x1>øb\ÐH~ 3àŽ–Ï–pRü¡©ó²n vn uª ´­¶kviˆpDXŒbò αhÙ6X²[”óyv†Ðé€zI‚Øpá-3 ÜÔ4C*ÑT+‰kûagëÈ¿NJ§&Ç àÆŽ ¥æÅ ¥f¤š™Iõä¨Z5:#WJdw|Ú×Ûšº*ËÙ<À¶»¡iùo8èˆÜ2¡N°µ ½XÂH׿DŸž”›k£®¯ ¿”ìÓÍíŒæ"¾ ¼VèvEAœš@lÊ,°®%ZZža–5 ÷Ãçr†N¢ 2¬ˆe1´]±#"9Í—½Þކw8oOÔ½U*Ä#²æQAW 걬9õì'jÅ;ê‘·ÔzÄŸU~ßwòrþª ATÉ®àÇŸ”ÉŸ4ÁÁxü»Ê¢åÌÒ“ ®ßE—ï¤WÀ¢2Û "^–E‚h!®< Y–ïm˜ãÍDÆkF˜Ï:³Dkªðhâý{|@Câ‰êE±LUmÑä`ì¤ë0â !¾v6ÞòW»scÜØûìþD=ú±úÇ®Ñ+yçC˜E‰î[m VYê…ëSi‰¿}]<>Ô©nF)®‹éoX p›…”q¿Læ—¶c“BºV婨?~®–Ë{•Vv½]’æM,å,+ȨaÁýçúå㤕ðºÚnK¼ç•eAxfÝ\zi÷üõs1Þù#uáLQˆ¥ yFÆà#M¬ ™Ž†Â@›Ú0·R§ ¡î…Q¾û]9p¹?µÞãç‹TƒeØÁµ  %y²ž/K_1á0T—Š1¡u¨H„é¸S§V;—Œ¼^·ã›óïÿ(|r¯Qñà¢úÙuõÃ[¯À=ú_OKÈú°4Òéac*×A¿Âƒ~ÁÑä<Öß ½ m(ŽéèU]OÁö#f„òœ„ä`âà7muOøX‹tZᬷ1Ú|‰á‡]ÐZbPÃ{[ L@­ê.ÜÚv;B¢w‡ò7§’¼©íˬ/Œé)ÞðÉÅ“÷?®ºÛ#½ß§–¾£žùwç‘%ù±Ž•Á£|ÚB<]Û‚š†Ô,ð/ŒpÍóµËw·‚>⌅6ºŠzZWùØWxØ\p¦çYŸ‡Ô>–8è"y8!výè@3< T ?„±à·`-ˆ"ƒM6YgXZ@ìÓmu‘{Ó³t{£\“ÄÒ9±?¿rqâéŸsXBMÀ tƨ¥†¸–c EJÍÈó±‘iÙ¸\Üÿ¯Ï>*(¨Ž.ruØ{X®5bCÜzœ­ ‡´­úÔËÚÔD¾šRÙZðÕÄx ÷X÷–8^O²oßÒ ÈÞ;âxñî]‰îí±.uÁl„Íá”kôƒx9èŽyö:y6:9öÄÖ øoåP1XãQ¢~ù¢"JzŒ ÎÀIXFv"@ È- `^À"1¬®Ô#o®âmr¢"8Tó…TÉ SOzG¨å·>¯ÚtÔ4T.S7ÇÎ(ÃÉø´#9™@_¹Œ,¦>¶–ø `;¶É‘æg@ñsÖßÿµE)m,ƒ!H®k2/; fj&S ž,(ÿ:SR¼®U?o÷—¨Äx6]UÜP¿¡þãúƒ·ÊÛO¥4JªÙS¶=8k­Ç ªlw̽·/(ž´H™ú¡²m§WW*·5Ò²=À¢ÑM_èª'ô4áûš—û˜ùÏvÒ:ï€HªÈ[ Þ°[HÔàüš;ÙQ«­bËã‡ÂA¿¼tHU¤˜F8Þ_?'…j©‘@åtnDŽ­£Î€(Gý¯[ê/{¿m>“™äåk~Â^ç«ånÖf;6˜Y»‚úLìG Ö@r ¤ÀŽº?¿~d]…ÔŒ…`o®äåî o<ŸzC´óþÿ³´ÿ'Ñð#h[Å-3#WÀ¨T|X²C4O'ßI/‹C+u3‡¸éœ(.HSÂþ%ßÁ Ä?l½kSç­ rl ±€[﬽¯ðˆ~ F=¶F'ì /æíjŒ{Ö´xnÇÃm¾\6pmZÙ /P»G‡ŠÔs§úkwÄS;B “Ê£N¾ìSrfQCõËïÞ>ùûwƒ÷DЇ-3’ÞÉÑkÀ£Ôï A]ïF­2$4f~±²hˆ°yÁïÕØ³c Ž%o6Û(ÝR ¨†®Oú.Ô )'ð§&T¦¥.nL†=g€]3uÓ,h´ñGYL,§L Ìsm3 Û7¬áÒÙú™QTó\À²Î»Ácü€½„Ã)<‡/ûºÇü6.“IAä›/5„Œ–fâE©™zŽR£”NŠûÞ}ëNÛöí5‘m‘A!EÈé'““\‹S.Œ4ú!ÓLm ƒk´)XÍC˜¼Ÿ­{.Φ€¿Ð.VÙÁ¨Ê‹!ôç¸Ææ)òc”»9jU{š4[5‡ÚÔ°ð› œ¦0{¤-Àùù½Ön'í#"!l/Z'y„­eòSHä´qr5xjCÜ6C®Á#(Êì–,o¨‡ï4^mE­ô£î´í›ø³ø$"ÒÔª·›wf{ë°õÒA Eo×.ÃZ–9HÚ§ô©L3ÝkDɰÖÐ)ª¡hËÝï#{qLª-9`ÅÀ®®\²¡:ØŠâÀùÏ™ÄkäR —‰ ÕêËê¹Û¿ß“·öì $:…`!º_ê%7u<Ýh3ª|Ç‚_î\†5ôå{·•®bÔ-d4†µû7¸Ð*í)¡§ncˆe}$.UAf$¯'Š ›šó>T¶ÉÖ6{ód3¯ ~HUÓ?^<4Q¨œ¬&¼»'õ@}ITâdß7M{×bubJÝÞ’Ò¸:ö/s¯ŸÅBôÀL iŒ•:T’6p­pŒ<¾û${JÒ=>Üd¥Õut¸›5@dç»üý¬p «¿m>d¸Ì‰:ýªûge?jUª®ÌŽõbbz7ûT0•é­tW<çÍÁ¿uʉqy(\v—yÄÞ|7S{³µN›Zës [븭>8Æà\AÞUl“禰TAB!Eþ&(5õ¾U®Mþœ"X #8™f´Í¦´•zTŒ•·Ûÿ·o Q$—!ÿº\ЄîUÜ^º<«‹Å›>¿™ûÃ?ªüØúøÇ†1PÐï7ŒcÚRt+TØ·´\Ü£þF †dÔ‚¶k}YÕÞ,< œõ/¸áùˆKZ;A‹blMÁßÃr¤ÈŤÌ®5D[ÍÓ;é sº'ÝÍ*ÍÁ|¿µÁv¦ÎJs*ÆrÅÏ›äú͇"iÿ ŵQÈ=d¹¸|NÙ$ý£âvujWƢγKßîýâõœûw†»Ä P.;À{|Åh›zú–Zù&܇J“ p7â»™U:˜dáyêh”îbzØNg›9"H—Ë0o1 ’ºwÒ´"Æ„Œió5‚5 ÀW9jLK3ÓBñÁr jNž-ºf–µ)¦È»@1;.!×~¬BKÄ q1^F×]Ásü¬§cäÞOÊÁ~å¨}SÈåÖ( H©AW ÔR¾'Çf” •äéÝo¿éíªZ•Ò¶tÁ?6-ï‰uo°®`ƒ¼z†g“ám•h{ÎÇj¿tak!)2xaÕŽÁîî[4"#àQ§8&™ŽŒ4¶A† ?Ð ,Mâ4˦Š\õ…ô¦Nk»3šÓ›äts™ûå¥.Iö¹!fÙqœ pv* °Íô`žqcœrc½6'Àþœç>ÐËùö=x·p=Ý=ìNÚÒ²¸†iðtV!…6ÇÙî19®¸™ÇM±É‰ôÚïÁ‰6¢Š‡Þÿ©lgô ª–Ñðœkñ8CÇr·nâÉ¥g?4€º yÜ3;þÖ&kÙ%X%£¨Êe-RE †tP\ S›ý´4+@+׉Âg!r1+¶Ö-u0*s1€ôgŒÿ%\ýjwsìˆK ˹f…NFð½©ô·jŠãv-óíZî׸€[épÚ•™æÆÁ9íe“ä|Üßn­~ˆÅÓ¥v-÷øþÁÐÃùðÅ‘!ÂDâ<ö†C÷JÕÒžßÞË}_7÷´·ÿ÷F™ìÒˆªç‰´öÙvu“cõà•á ;>†ô@Ðàß8µÐ¸a ·À…â;é6óÌÊ­µ ìuòÝ Ó] Pî²] E!¶€|ëü8À^0"ÙZoÀ/™Vhft0@Ä‚ UíˆÄè^¡[ÆeÛ˜D¬!´=h æÈd¢ÁÜ ±}™öúX%xZúÐÓÜ‹Y&K=\>鹨xðû8ÃSSp§\ƒŠü7ϳ@ ˆŽÄ*ù´tàþ?ß½Zp679´.%üÖ†˜Ë ]®%Û÷$Zׄӫ¢ØÈŽ&: †–BÙ¹Ìt/s”P\º»q¹2u°Í‡ÅÓ:¤êè-£t–RZ«(ݵÚ:´ií¿f¡•xÎÝ‹Ý//Fˆ0ëJ2£o±ååE¦ MÚ—³*™ŸÕG~âžÅf²|ö;Æ¥²_–#¬ƒ$¹^V ´ã-~‰%øòߟugŲ6Ö3Êôég%0ÑRÉàqUý´£4ÂBäa^i£WmgTãdÆw¶¬vf@’ tb‹œ9•6–PB³Ò?`­‚ä”xÎÛ§dÏ®Ž\Wuˆ‡k•*¸¿6Ì>¿"•7 ÔCU„Ð:49¶&f¤~FB¾âI*—ŠÁ0÷mcÝ–ð¢ÅÞ‡CmÄ8¬öÒ‹¶¥¼Œ)W]ÊvÓ‘—ËwÜ»‚k^­þôNÓ‰ÅZ14jÖtcˆZPÕæ——9\ZÆnO2íZdqi «g ½k±QËBÃúåœüdÄÀ”š ×?ø [¹Ð{°mS”ãý!h’´ev>ŸÝ¸qί`fÄÓO˜Ÿ ¶9àNßËc®çè­dÑšRáÄÕ'ÁÚšâ”¶7àãwOüús¾LŒ‡,Qaþî4¿w0>@X‚‡o `aâ¶ f øâ¶Pj³?µ/‰ùÍ­ó#;1ꪔm3'üÁ?B½»3ºs÷Êb¯¾µÁÂxÜñ£íñ\A¢suœâh üèy¦ÅÞtŒ'èɉs”…®<¹0n@ë—ª­ƒäµR™tÖio嘕/©\{.ÚóLœÇV3¥¬©º Ë~þ6oFuåÙsª‹00W=«Ã[¡93ÃÁÿ—\TI;pQA‰‰IÞ_C£ÂðemÏ¥=êþÞïÊ·Ÿq¤*] s¨Rsíj–q‘¥n6Œ=(fK!€žïiÌw3(9• RkäÛæ»é—±š“y])HÑ$ ù„yæGùçÇä$øŽrÛÈ^çn°„KyùÙøË'0¯MaHª §aø†y\Ú|IsˆSžh(ƒ¸Þ ¦“)d²:ÔOÐDÑpª‘ñ¨V¸Õ¾}Mcˆþå`Ónw½.ðCÂX7’¸}K\[bص¡f­‘ŒŽpFS€Q'­ÆG[à¥Íw×xŠó¥œ”š‰¡ê la`8Ï'/ªŸ´&YÃ-w¹CÿgŲAâ §ž»‚½2è[}ØÕŽúÕΆÕ.&Xñð]è¨65ÎV5ÎìK‘~géÚ{Œ‰uR8ü»âYŸ\Ëž”Ü'gZÞuÒìD÷“‡ˆlk"¢ƒ†QE#Ô…$rw¯$¼À\]Ò‡:凕¢Ô€ìHÇNºè‘B9T4J5:ô²KɉÄ>áÎ_?åOŠßTO¼§žý¬èH |´Âµ¨ØÉ DT›éD "ô›âõÚ“ô.§˜]Iat-2¹¶Ò²3Ťf J±qŠ#µÌ›ºÞòÚ³? ŸI{ý_1Rj Î&ìµgM²/ò²SY™)œ“q¬½>&©èdTDë,jK0ûXªwÁ‘„æªoÞ<ñ¯ÏÏßûýÂÐr½[00Bˆ×Ü0ÁÀ]Iì@Å¢ùRƒH©çxª7§¥šo  Öò¨;5{†~h‚­ñИƒZÒœ©Õ¶ØzÅ<ý’ ³L/­æ$§ÆDQœMe«$ÌηÕစm°>¨ñeƒ|RîcwÁÏå Ïf§µÉz Õ†f‹uLPq”vøœXõjQqúT¨é~öë\-b̨…ÖÔb;j‘µ9Æôf÷®?ÿ¨œ»bù˜ÀKÞ„™á¦Yqó¬¸qŽaÒÆIi3žxˆž YµTÎ’UÊë­몾{¢(5¹çu©"S­"3Ýl#ê;ƒÞæK êÎÌ,ì&ž«•C¿½ÞS¹eiz47;˜‘ã£/7ãv$Ø·Å;4ÇØ ÃY•tläcœ›ÜEÑŸèA§¤'ü¢Ô â5ÂÝý>ºì¼~îJ;ÁØêH¤–{j¿¶Æ%ëpT§`óçof<ý¾VùøŠjð&4ÏU¯ô-ö¡|õ4¾ Ñ^Á«Uø –øSõñ´++ÍßÚbûîVç×7Ø]]ÉìZeUžly,\?ÑŠ:¼Æå«wò‡µ½(5dÑ¥Fˆv‹,_fo|ÖµõÈRƒ—|©ÕÎi\`J­uÒN[à^òJ\oÁ«_^Éúý³ê{Mðî›P^Æ7¯R5£3Ç•ŒÕø3xÈÎÒ˜-¡Ft)„_rŒ›íiÍÕu¶ÊÔïeŽ,lˆ’ éùœóQœ3aÌ´ ‹ãf(íåìt7]ï¨ Z`8,GèTj€þ‰—=ËÏ$]m~õ‡Oó”CíÈÝ€ 8š« ©ËÍ÷‰oõÊŽfвS ‚(RéqðzZV«žl™¸{¾v³ý%zǽ› Lû‚õ.h·úÓPRD!ÚåþTUÕkØoÒiÐeÒGïˆa6†š×˜×Òký™Bo߃.ô²ª÷áÔû°|9 ¾VBË Gã2[ãJ‹R' [€ÿ@,Æ5Æúû<²™´äsa•†½6‹¬z3ÎñLಓ«µÆ†ZeC¥XÊq’ù ×úd{jg¬UöÖ`ѹ”kµ»?=ãî—eƒàvØ57Ñ‹\<‚ôŽÖ‘"®A™“ƒ¯9è'ñΓǯ^^ÏÈ‘>Ö&ýúLëAû®mÖ=Ë̯/´ì6¾i|9Þ¼3ÞL©WL•B`­_¥_¬]á¯]i‰^{œCs”}C˜0‹oV…»„!è[@S/åšã@!Rhk¿Äº>ô#§èÚxƒ)t”AthÖŽZg/Ëpùfkã8Z²‡û[—û†î?—Há¯7;>N–Üðú$õ… 5xAþÃ/À­×„J1öìÏ¿~%o[ê¶ ûÍ.F;¹:'ýMºj!ì‡B6ñYžÂ2Ž8Pm©ýÖ$!h{ÙÔA;ݽÖÚˆVŒˆ´£øpÒœtιdò |ŒÊƒñ&X´/´ê[is}5çÆjÆõÕf7V÷.7¹¶Æñbª[ÛFߣáFèjÊÅ?øV0&¾D&k "ÿ§Ô(ú!'¬‘c EJMßý³‹–9p×ê8²àr§íX@ åËæ­ˆåmF…ÛPKüŒw­pÍÚ×]½ãƒ«gþ¬òé¯MŠ\!òAä´«FÛÑs"ú £¤`]Ž>«˜U ™A‘p‡fZ!ÉBUâE­¦„¬I ™ª¬K¥¸ª–¿¥–~°Þ¶ŒHáp=`k¬Ù‘;Ú_q}VcS=;¤IÇÌŽºÑ_ób$˜’‡T )‰ ìS{ꥳ̭Âô%ït¹ûqéÓŸêÅ÷ÛFû»”’K#Ãí#’V©¤I< _V€l µÒŽ"£†¾N¶H ”ÔÎh™Í‡xÿñS@$…Ï‚¨rFE³(5ãm÷?8Ü~Ä£ëUn×JNßöÕV×’mZcèuÑæ†Åáú1&µI̦d«ÆfUˆiS¼=2ÚÚ“Ý!Zå,oÖåpêV ï,Âæ2¦àÞƒe÷icƒãzzû´i{th0ú€¯>Ì·AKž7G…ß>špŒ-‰:Ú‘Ú4?2'/þñÓÏÅÉ‘À2.ƒ›Ä ñÿœ/5šš3_j R€ô{vR¥ž›œ“<ýùÝko së¬ËY\±>\¸&´~©_3 ¯%¾Èjo^ê۸ة+¢¼ú…ž¢…^uÉ^÷,vCÆk~}i¸cžäZÆ80·/ñ·¸àg‚Hȸb™ÉœÞÛk+mo¬²¹™Ê~}çÎKW×ðê9U¥¸näR’e¤<ù Ü@ùÈ­Dè"u5¸fÐHàýŸUÔ+a`.©~.ïø uÛÁ@ýå&TÏ™eý_–ÏŒuBj ¹wGZâNêPçôµÒ´hûáNÑlÖhZÛ(üR¶P´×LÌ÷YZ¤‚ðo©W´"¾bë²lplö{¤E1ÆZ¬õ :²Š¿­˜ìo™Bô_ ê ¸…¸tG”BùljÕÕî¼¥ù/»ïO°¸-|uäÏΡ§mÒ‘¥¬w£"˜jB³½ƒz°UÝ1-Ñ(w¡uš¯iQ¸]a[e_ÃF)¾¼Òº/•ƒsuõõuŽ×^âu­ölZí“̓t}Á»]iÄáSÒ‹÷ç?¥x¾ÔÌ?¾§ÇZî~ŸŽçû£Ÿ‹‡ÿ©ú;'žuF2þ¸kêY÷Ìp˜½øY0Y@ž 4Ì"°ÎÆR;¾)€jöæXmK‘O$Då™°’j3JLžÕs7Åß”mÓ{É›†®æÙW@Y»rÖ€ýÔƒžÃ-«x·Ö‡}°%ñƆÐËëü›“¹- ]Ú—ºµ¥ðÚ—»ãEëB—¶D—ÖhçöX^×ÿŽÅ¡Õq~$îäZ¬w1  SQ®Ôšƾ—¼óŽ%µUm{ÿFúÿ¬èÿ£Uü¨S>ÜM*¶¸÷,޾Ç#M0<„ÓF¿9 >ÒdÀÆ•ƒm2ÖЬiÞäÃRqÏâÿ‘cË6Ú„Ý)5Š7Õ#Ä‚¶0¼å1oÂAe¼‰®‡sÐÁ8dº«MÏ&ÇÓñB[Q”×yàÒÞI6T¬#õr"çèæ€¬}1…ß»têÙuC÷¥Z$Z‡5IûÛÆd=3S×yc¤eXR?,/A=¡Á+ˆ;ÊË|ùŸRƒR?L* &,œùdz‚å×DïW—v ¶»–/öePî­Sá®SåipÁHü²¼t³}õ©VjQfp5Ç]·ÀÛ0ÏÃ0ÃYÆï°Ë€/Ú6Pž`ñÔ ä«Éí:n¨û𯙠BN˜RbNAíE…yY—öЉñË&&ë--V±˜áúº^ZT0‹!ÈÍEK£—Ž q™SbÓôb€ú«½™_CÁP>6ÓÓ“ãÊ‘…xôþO¾|ïú¢­KΦŸŽvnXuT$@ò†Óå\‹p™P;Aˆ]mˆ=¬;ákÁ7ó ?„N¥Þôª6ü<ÛâÜpÚ㉶â£í ï¾qkµoïRüÒ®%–Qf\hXdNéOÌOd à©-¹ß-ìùO©ùÛŠ”,NbQˆ®^-ë¼^µúU¸?éRW²VÉ~©ÇÒsnªC=~¥t“cu§škYÊ6ÊµÔÆjä¥c–Ú°D>d©·ßL~nçœØ™nÖ‡íMŽ{1 ú”¤†­ ¨N +_˜»Ø}˜ù+ªñÌùÏ"µ¤g òç¿•Rm4¥ft²yrâ¢ZÙW¼×+}CÚrëö—?é@&‘TÒ€j€'¬ZÞ³&¢ŒFKšß×µ%)Nµ©þ%QÎM ëc¼z~°-é­Ma7Öz]^îܱЦ1ŽÁ'Q­æçýéǼÌ69RÁºÔ™¾?~X&yÜ¥’RŒïu`žºLðsÍCºìþ‡%JŒ ˆäÃMH®y> /˜vŽtNÈ:@© ÙbCUE@È·«§”Âiìõ†KCe˜2ÆFH®ß¤®YäŽÀŒ¶°ãÆ.Í>l=¼½3Ì<ûåÀÞ*¯{pÎÏêñK3Ÿ—7¬rDÚ4Å:7%¹ˆìQgkãìðºa¡SS2”ìëÃmšÔîØí&ŒrÏö³ÝeoœÊ¤ÁðåfúáÈËM{ÿùVίÿæ=)±w|´W.½8ÜßXfDÚHr<—M¶ŽÃ]„ø”bô¥yZÕ€ ÞÈcàGÍ«bùr%9ȃŸ¯-“—Èå@†GõãøP”}êÑÛØ©Žaâéõ¡ågXŸõ·Ê s¼έŠvÄúˆâ}“ƒ›RÂ`R•ähzc”YÆk¡Øïôû×Çùî5ŒK¯ <¨Eï ÒÎìø€Õʼnëú¸H°Ù„5d¹{I0:–Jâ40p{#†oRr Âã3Eò8lòÑ PóbR.Bb)€ÄÛ%ëNÅ™ïv¢öZjl÷@NFlRÅY˜XuŽ; K(ÍA÷Œƒ^º£PL°§›O0AFÞMâçP[àP ò^޽Ü× œ˜%7â0‚ ×ü7|=æd¾ÏÎèL í‰`D—'bN„;îógïô3ßàN‹eR­YKe¿6 E,šÿ”ôáóz8LZyõµE†›ƒ¨½ L¿{+C-¿Ñ%œ¦&‡PaPƒ¹©…¸x!Åàɵgí粂Uq¼|oka¨{…§=´Ã…pÃ4ŠDH =¢;®Š4|%k™GQ¢ôåÏ~i–<íD7ŽÝ.)5„ºL®LM©1@03Ö0¥h˜T4ƒk=T;ü þ¬¶aÕ‚ÞȶLMÑwŒñ'U¥¤Z>P¡®œA3S)¸€j3ƒ¼9”—7›¿•ȬZ¡¿ÈXëv Ž}d‰óW×3'彃šŽ‚ÕÓ¡þNTœÀ:Ì"1Üç½ §0Èó2€áÒY*Ó]'<ÞúX´!)< âàq‰9`ɉ…N¥8Q7÷|ÿy1h±Á‰·"ôX"+e­¨0Øü¢¼@É>*ãËÆHXTÿ¨0ýÀüAÙD< p¼IUF}ÿ¢ÎhJ qüÀ ™¼T®@©©D©ùÆL´ÐØŒÜÙe”ÇÞmGñ0º˜áN?ënYåZ`{!С$б,ع&Ƴ:Á;=Ôöw]Ôç÷„~òzúƒß†úÛåòË*å%ŒHcòØ€¡'×a¬Ãv’a8`#U ®a”z4xV’'…¦¶¼¨0hf€`»è‹«ÿ§Ô(p-µÀÛ¶ùPüf{r×€’„Ø p¥#BTÜ\,£öÆ`Õž°1@xe–£qO¿ÐS¿ØË¨ÌrÐ'Úƒr«jˆ/ ¸æ•^œÖH¯ËI!—÷$_IíŠó­ v.÷±.²/ ç†9æ€×âtÆ×æd€ÓwÎ.3ÉÚ»û5±>]µ%Ã~yÖ¯’H§ÇÆJ3;39¡úŸRFÆ& ¥fþ('Ƨ¦‘³ DµùóûG_½¹òükñÞ;õO¹âٚȭ=ÝÃÙ¦G™ &uCÎ~P²Aƒ‡BßQûœ•L„ʹœ ž5Œ2À‘ƒå‘±ÿuæ{¶ù¯¯ê]âàWRèjVûüëv‘âÙµ±Ñ^Pø^”(©ÿË«!Û¸Ã7SO]«?•µÊV]9«ýFCM$ËRÒYºÎ®{…k¨{¥‡c‰'7×Ó9ÝÓማÝ.æ&–ÙZ3ýUZ;í¬¶r,V™h¯·1I4¡Öṉ̃ìW?•k€]/hB!º7j·ATbáùÌÃÂaÂh±pð„š‘«¹u`±ñ–`êðRæog@n9‡¾dGúFûsдšÿݹó¨¿~–£0ЮÈÏ.ß•sŽe º&\Ž1ßíÒ(†vÂ* |ô´Ðçê‘øÝMN&ž•ìL]«Ûóì7ä^õ 6Â<ç¯RC.WRjÈJ€‹v¨*²DÖô3‡FOˆG«ñ|'zpEõÔd#œágeMX¼¢QŸƒñÅHµl¸„hç5‹-M©àŒÁ {X¼óÍê),é®®ô °yOu§ýöAÙð“fÄOã¶…å×ðû¹¢2ˆhTа=q††Ë¸0À4×IÐ:çyÚYHͳ"zL\(5§¶3©õL¢28³Æ÷ç áM15ÚM–J‚í@ &Ðw(yÃÿkâA8Er2÷áöÔð «+£9å©Qº¥:%æ&UÁÖFùàAI$|X³B̫š{ìMuÿmégÍël©íÖÚ8éúˆüÀCBK"ý>m…¥.$N¸—!0\®±ò^åAÝjÜ÷ì^«\P®od´S!o5›JRLÈ!p4Ð4†{ÄPh58“àJEà^ªBüóß/QK±+Ç^£M-ïœû6÷Ý¢¸cÑTú2ýc´ÓÞ†§aÞN'f˨-ûp»Á³Å‚†Õê9¶NC 0‰çcœ³³8Ëå e=á‚þêÕdÎgo¤ƒh:¡ºDº,©ÿ"þ·ÔÈøSâšéá$Éâb&› xéÈD#rÑïóŸŠ+”“MÓêvådà´rPR^Å@-È'ƒµŠ~>ðv 4pP™RÖhJ Š<ž°•ÒD5Ã\küQËR*p¨9õÏž“Ïî ŸÖHF5Ý]ÿ¥¢ÅÈDvRwÒ‘¬œðä ºÓÔhl¹T¡»>X7ÕÞPëç¸AIzÐÕd+×`ò¬w!f%_ö´q0Ø\«¤MÙ€ô¢­ÒŒ‡>'1ñ˜÷ñ€•G•æ¹OŒ¼pÈ^r”O|d5OÈe‚çÓ­pw—U <) òôx+Åø¡Áîl|Qïo«‡ßWu¡.ÑJšŽ•(.]@à'øÚÔ¿ ëÄFFj«m¨­á&ïw~ЊÉòìà4»¼—J"çõ¹(þ#¹‘úYxþHj§Å"|:8ÓâºI‰€¼B)œov-WJáˤ¬Å÷üäA>d¼h)Ubþè³2å`¥Å\Ù®¦x©mÇÆ€æž(Ú®ÐÕPF™'Äõ†9Ô'yu¯ïMì‚q/äó7Wú]_áqm¯w±K÷‡Ž8›–(û×{Ý\êws±?tBoÖyÑ)à~‰~vÜ5xШÁê8ù*µTsãàf‰c1DgO~zó*ZùÐÀ¤B7©3åÖY¾€…Iå™ ˆó§¦Î Ô`˜‚P„1jR:„…ÔŸ_ÿóŸí5•‹ýÑöeÌ ¾f%8ÞÆˆ -æéÁª·ÌÓ°ÜÓ_‘€\îe\ígŽ‚É÷§#Ý»ÂÃŒnœro³j::78í@Q'4ÓHJ»›Å_›òPç²pÄŸ¶ÙÀ38¾Æû×Ï*éu¥¼·Þü¿•2=aK8­ì?ácu¢žîûð2³—ƒh/‡Üîþ­VxÅÌý&:®“ïe~†œS<³=é Ðœög§XŸ´=`“Ÿà}Ô‡ïžË]jAí gîe—íKúá&ÿû×Eç¶GoJbØÄÚRü¤qnâ2ù~4knrÍ?ª4ºKµ ´öŽñŸ»bh›ü©ÂmÃ_W«Gúr]s̳h½YEªYùbÃŒ0ªx‘yI2£2Úª*Ôª<ÀRãRnXª¯¹Àß²Â×y²éNÚ'mQ$µÑRìÏ.ŽpÉLðÛd‹­Í+Þßý3Of ìfï»ûE%šP¦`ö²7¦@“\1ÿñiJ Lö æ<!6¦­`"rwÇ"ö÷oæOƒÊ2‚œµž¹Ÿø§BtPjŠ­Néž„¶¡î#½Fº; u·ëénÕÑ~E[ /¶èkAh³>*n¯ Õ~¬üò:ÿ(6˜nèçOÅ=¾+Äß)"G¨3ÿ·Ô€dã/‹O¦²×xQÅ{¤ß ½3úUùñÝÊ%tQ‚eM ‘ À ¾ôx¯ø¡Œ²`c¼?eþÚU!Ú¢]Q´W É–¢$Ëêx‹òËÒFe»&Ù¾r©û‰Dh•i|¥é•ÁgùD­X^®œ"…†³G´3`î@Y X :sÍÅŒæð‹f¶B'_û|¼]Çœ¢“˜o€´6ûŽz0ÅÕÙ'½ý_ ¾Ž=j…źjP‚Iß®)5€æK«¦¬‘RƒArr¸ó݆½ÛBŒb`òs âÏ_*†¤uÒ1’¡GÐîc±yáŒÂ ‹ÊH«šUÍnˆ$mƒ À Œ>Q }µ Ì"ø€è dsì1ÑÞaLÛKL¸<émãËú¦ÑV&éë'3é·*K‡¾ÿZü篊Qì¯Q[ÐÉ ß_5˜ðK þ„´5èqàÆ7ïcƒ‚3ƒ]Õ,–VbñxÿÓï¾ý×¥–ú-ñÉöÙ!¦ÙÆ9þàIj²G‡OeºÐNŽ´úb?KˆèKXÅÞ–¹H?ñ2ú— 1¾U [i\kVoŽSo†:Se^iYÍ,¶*‹±-ŽqÊŽ°ÛæªeF•Žÿãk¾BÜ…u V<ÿSj@›%×9r™Çåm${h¤uî·ºôu¶¯Ä›¾ºÔúË[YcH*¡½¢þY@Ëq7MgçZ[e2X'LL½fd¼ÏÔâ ã0uÎÁå€%ã%жÉÄ`§{]ÃâëÿZ­ø)gBŠ¿~¼õzû^éÆYUn"\<ÄU˜²sŸ¿õÐQÀ×°ÌÇÝ»÷&­ô¦Ï-TýÖ©–ÜB6ýù¥–…ÄA½ÒE¯Á‡…äå|{#Í»dåJË…å‹¿?BOc$ˆ3®Œ5-‹3C‘)ŽfF3 "Y8™Ñ¶›} ´í[ïòÑ›Çûû+$c•ý£ùbE ÞÌqø@’Ñh|^X’*«•C•xž&gVÖ4fz°‰¤ÝI`QxG-¾3}¯»ÿSÁ…}Ñç¶ú]®Ú$ù¥á¹¸gFÚ1FÜàüè¤Q¹’š¦7 ½¿+-õÔÍÑoO/ç®ó åï üýÇ’±ééD-°ê±Ÿ ¶e…˜[øÃ È{¨Lw`z…æÄ„ßC?ËžJ·¢ ´áÏÝe^0稿%Ô¡f”(keÿ¤XPI`‹­@(›a6ã&EO3½Î?øÈÑ”šy’!Ø›dœi@IÔ3ÖŸa¯ªU¯«‡ú¦~iý¼F¸+&=Ùm¯c§›Å^7öÉ@Ó>ûv±8ÛÌé[Œv˜™bY¦Ù²NÙ1NØšŸ÷sÊ r9ÈÍðs>ççtÌÏþ7³¥vTæö°?.Sà$G˜i+"$p)$eè6ÉSR Ï/˜P¯)8h2Qÿ ÍWEô3Hïš»¶Œ,ˆéôôëjõ;ð~þ°Mý óCáæÜµöÙˬD[|š¶ð׸–.¶¹mÚ‘êze½Om¼uI€)Æíƒ ê‡Úg‰o$ÍYûœ B® Ò9‡„wXI€¡G²2©,W-poò½ôq`ô‘ã¢Ö–†|d ä8ÒÏ; z‰‘ao}‚ÃÚO7Ûan¸ÍÌh»¥Én¦å^+Ö![ûFæ)Z†‰Ú ,YMgÎ}{çÁ_•K†T*%ŠÌüAÍù¿¥<áùRCf&¢÷&c–zjfjT¦{üø‡Þ¿”w.;Ž›åg˜íkPÉÄÙBØBÍÁ¬ <à †N£Y [`L ×Õ“oMßm~‡¿íä2û•& å‰PŒ-Þ0÷\Ÿ–táï”>C©Á}M(p8/5êç½­ûåÎùx&µ¶uk¿øÇ釨ã"©„ã—}§Ó¼´ ƒÌ2ܵЂLs¼Ò]õ2àhÁ°ª c¡¥ç'Ð9´ËƒØ5Iǃ™/»ë-ãéô w ?®—Á0g´cŘ’äÇá¯E 7ná¿0ùùýÑ4á Úš¡ý:‹!Ð8!ùÇhC¬]Ô漢z>ë>µÉéÿ£ë+À³º²µw¾¸»»» qwwO°@)î$îîî.@p)m§:íÔ¨ BHBÜ…Àùß:sçþsgžý¤ %99gµÞõŠŸ±†_¥o”8 á#œ”øŠÅ Tä¸B¹xY~l$½û6 î’œÔm†“øÃ‰ø±“0x§Hq¸ SýZÆ÷Ÿ¿À² &„®±›^›-^œ.À+rzS™‹‚C‹ÉÛõFשÍî ‚²ér*7›«†j›Zß#„tõ*³:ÄÌc Xù­î“ÖwËXtœvo;ìܾױ{c2UÃktë ª¼µ‹QLð&-±VAÞSáu,ž.)œ×—ƒ©Ô!“J"iÚ2ÙÚr©êâÈúI7•̳‘‡¸¾Ú]³ÖS»ÆS§Ö¡QògTy÷ˆøRÂû!\Ðíàeƒ°ˆEq`àäË&ŒëÃb"으~‚"v|‚û½½ëR.ÿñÕ/GžÍÎNýlšÚýo¥æ¯yê5 µ ü¶«¡@Íõ”€0sczzùéŸs¾}¯¾êR°k–£:‡ï#KŽá#4õr4µá¬ Mßaø:ÂLTÄ9!ë[~*âF# ›Ö­¼´ÍÃðˆíg‰ç%±ø-ÁNÓÏÃ@aQßÂÁQƒ£Ë%ï§¿V,Àœ s¼pŸÏÐ3W²ÙHÐR—ÕÙÆ¥—5Ìrÿú÷{] È»AJ´›ƒæmF7óSíKžlSÙtMé$>’$Èž$Æ$Á¿UŒ/R€ÝŸn§‡ò€Ïà •àt!ò¬0 ÉÇï7üy§©Ü6âäâvã/¯ŸdûðèÑþü¯Róv†¢] Öî´Ô¬](ŽHtà€·ø¬çê“nféáߊ㛑"Ô.Nž°J;xŸ‹ì`må¦{ºšAî§Ù'EÙØ¢Âs,_ â ~,â É!¶c`¿‹‘ê¬Ðç¿—>Ξ^ÈŸ\ÈN›Y, öm¹ÃWˆj¦Žëèç7–u.Øã´ÃýÌŸ-UnKPØo#tÎKå²¿Þ G•&â±ÆÂæ ñª=º“1ó[ÃäUp~³Úò¶Ôlb5-¡0Ôà¯~œÇ0·˜ç=Û¬¹£ôÉ!?éÏœXZkƒFÔšÅ>櫊d3¾Sáýb´ûMÕáG‚'ø`G¥ øÛ§eáÎç‚RCµ âä]°Ü |x‚ yvŸš®•Ñx›F5m@Ù‹+u”È„j³) ¡¸Ù_êWšçŽÇ™vAPÍ5 I@ð¸—þÑ8{—þa/Ñä ¥¬­s²Œù¶ËRu^’$×^9á!î.¶0N6¼tp|9I¸ç69¡=*¢0»Þ£,°_Uèyþ$>¨A«´Ô×ÚwÙ³Õ"lz ,ì¬ Öç‘+Z¼Iì¬9_áf%C÷ ê!ïP9ñÑtÂùÚÙ…:º0¥‘îý#¤—´Œ6$*$N—ì0 ÔåXY.B{4òh`ÅNlTé“%F1aZÃC«A0µÝ@}ˆâ éºˆkÁvn0ir (øÚ|8 Ñà>©š ÷Emñ c…lµ Õ ò§•¤vò±vqìãÞ!Å›(Á ²ÍÝþûÔÓƒ“;JRÊ…Ÿ?PS}—·û@må‹§''_.//¾-5hiþÕÕ¼bà0ü¯Cqpû ‘‚¼¶8»<üÇêÿ»ÝwýÌ»©všY¦RÉ*l¨0§äÈ%u`¸9å›7¸¨5ºk·x´ùµx5yÔ¹êÕ8ëÔºè¨Ï7—Ï·T¬p¢Å³ÚU«ÜQ­ ôQÝ&HÜujÜt vð2¨òÐN³–* 2ðV`3†ÿ^}ÂÏäÏ,4.-¶lº‹Ô£ÈÌÏ#…vò›íhåâxÑ›ùº• Ä^ÿ~ ùˆ«Œ‹IK4|ü÷܉á*¼Ñ@g]þ,«Ù.øWÄy ͹é#ìÃF\APŽƒEwßþ°ÐDÑ ~‚ùS!ꬖSQ”Ÿ Ôæ3$EGG¿.\oX™#Ì+úV}{«oÖ=ºôÁÓMw˃ǃä¢Ì8]ŒÉÕ¶ÃkÃHÁü¡bÛ½CŽ-Á:Í~zÝæ¸JunšÕîÚ•›:ŽtK¥,kåÍ /Ýr'Å{ùZ ¼t ú¨ð6­±+ô³>î¬g.EüíDž[íšzFS™æg f _•=O›.ØØ€BÎÆMK¿Å"t[ðçÚðBßø¬àîYß“F\Gµ¸.˜JžÐÝ)Ï™ Ï.Åæ y qà%ŽÂ$Tëï½És¿C³?ȵ(Ûö-\C—>'¡rN”šÉ‘2ˆÇÉxjÁ’‚ìuøúî™gOJæ^wŽàŒÔ0?TeÙ!òà‚ 9'I£ZÏC…Œ1²±•œ4—b4 Ž"Ñ&ÙÜð Þ´hÀ…ÂECµÁÕCYöàb³ãc·å‰tÚòðÆÐôÔÄòÒÂúÚ Š ø{ÐTn Àë¯@¢¡N|´ÛAcó¶ÔÙYX˜›žýù«§7º¦nqê^l.©ÉsN–Îä+¤®€í‰¡_!†Ä}’ÔO•‚Þˆ¤¡Wˆn߯KÕpvðS«Uü½âôs`k8ød¢-ñf¢–¹{È!Acn¢-IZ;¶þ2œ7³Ð°¼Dc˜–ᔋ™ež ¨6› [Ùòx>3_³øwíÞÏ×Ò/økÇ#(ä„û¯Ÿ¦Ž>+¦©ŽÀjU0ãx~NM$]OÔ&´—g åÈ6=Þ£‘Â9cåd¥cZbÇtDϘËî3Ük,˜lr-moŒ™dŒ•Ðõê¤ÙߪVFk¨aËÐÑMM7}ŸÒ~/}<€X‹0+@Y{ùÉÅXðù;Š>ºÄ¼¼Š¢4î“d§%#¡oÙ-Nv‚Û&G¶‹‘8ÆEBqç “m’d—A û6¸§â„Ÿ;.‹(I&!"û_ ²-Lë²WÇú–žc]7÷²£åètæÂ›ª…WU¸{±³`;h@¼/à#1R9ö·ó·²ýJc5 üTªõ›C¬z¢]›ƒ2íôêI'(óy 'Aø‘r&HÉ’P-öšŽMýT¿4Öƒ) «´¥œ{»YþW©yÛËÕ‚ô‚™ñX¸Ä!Wþ(òaã.fnph<â/5 wÕù(À§ŽÆÎ9ªBéʼ0gÀ-tŸ2¹¬Bà2{d×â@ð• ÆèxÝ%¿ý0{æq5L’¡ÐGK€`j®þÒègf&J0(! †ÆQ1}(Dëëmp‹ÂŠmc¥nj¾êålÕÈ „ßõ3+ïW$˜<8Òëg§rV’B´ÅSÔEaKŽ›!Å@þ’¡|²,,OÈ×—9j${n‹ÆY+õsjgÍ•“Í”±Û½l¡–i¡VïjXd©R°E묡â!…]Fr~Ü€òºkvþùK)rEaŽ7;‹a¶pc±dj4MøÊ «W'kÞ™h¥†Ïãà3×¢Ô¼˜ÈŸÃöp©ÌgØy¬þ2?ê„:Ù~ uí!‘U»Dèc‚8ø­­ðêÁ˜÷9^²-¢2Ù¯ˆÆŒÄÃî[” ø2QŒl#Ñ$3æøab¸À-'E.èñd™ä[ˆåš‰QY¢‰d¥ƒz«i¨C³§y¡…ZªŽTš®t†žüyQ3tå¶…ësÆHúˆ‘ÄaCñã¸P𤺨)—¦¯~FKé žj¢¦œ·‚p„Ñ'÷nÍÍNÏÏÍà¼miðqs'ò̫ר.©6§*àÂÖ_½^ZZZœ|9úýWŸwÖ·Ù™ëiùd–8MžÒãË‹FŸ?ÚI#|!¸Nµùë´è¶ÒÓ@O“¿Nka£Ÿn­—f•»Zµ‡z½vK AG¨IÌ–¾h+à6í!†aƽQæø¼9H¯ÚO£4Ì8XK0ÈNê“O/?Ÿ®Ä:ê×ÿVj¤ s~¼b¢Žì·•Ÿªü]ëçßæOWaWÖÜògyûtÒQOÈq‚T“¢%|^Kø´Žð)}ñS’'õ%NêÁ*Y(ÅDÄ)m¡ ;¥dÙ#6R'¼õ=ÕX'# ~¸› ®Ú‹*Ðn7ÝBþ£ÔÐå2õÌa&Ú÷ºŠ†èsDxÉ}ú ‡™¿ ¶ÈÍ õþªÁºÍþ†¸ A:±&!ú5ze¾šEÞjÅ>êåšõ¡z­áúáz}Ñ=Fm¡†á5Q¶YþfûÔlTIÚI¯gßáìÝxÙòš2[r—çJGÇ3V^Õ Ø5an.þ^öæ·ÚágzméH2©׸è$~ؔ瘥H†¯NU‚4ñº΢ĊŸX KhŒùÒmK¶º&è𨂹íå÷UK€87Wõ–šÍ¿«ge¶‘Y½Ò|ѹ Rs¿1ù ?Žy‚1 êŒ>f´“ù¼¤ÞW­ÄDzñcö܋ʹeK㥯—ë–ëW[§à †/ãio†b©—&¨XמTã“ð¼Dä¸áW‰\ZÜ!·ÑNÖ=§/–ञØqQ¼tNlò¯.IC>œ½E¹ÀZ¹ÂVµÆ Žz¹Ž鮦Gí´µy‚-®µîþhR3ºÊÕ¤f ž ôõºÅ‰dÊ3˜Œ`ƒ³rEøõ8]„ÑåÝ|í‹—yp@©™®}³Ò mÝÕÛúâõëüüT; ý´j}4ð¼ôF[u†[4‡˜´†™tD[´…›Öé×øéÕו{ê`{[éeTãk^écÞâØâÐìÔêÒîÖâ\íkSînœ¢/ /»JG¥B+™‹Z<çT¹°»¯t€ôL‘¸©:’)µ9/Éj"Yú²ŸÅOmóyDÊð)C‘3"çuD.¨‹\RË2P¾€DZ íÓ¶úqz²Ñ|)ÍòøwÔ˜ïýURè *Î:ÎÛ}÷æþéõëµõW+«0ë[|öç·×ò·Gî…«žŽÔe-Ñ· ö rÓ“2ÿeu’¡Ç™kÌwI›\Ô&É:ä¢Û%CöTž s~ðFÒÌøÒÍù¡-½`ÀqZ‹œÒ$çtÙ/r^6æ=§Ë†=jøY‚ß=¯Ç:ƒ Á–¢àúÛˆ~ó]öÄRPµ¥ùæÿRjÊ×&JÀ¢G©y=Ñ{«xÇV#.Ox8Gk=þ"sr´‚–š¥N滊d[ÁGÅ ¹ mþuîd ®‹z‚ð)Mµºh,q^G輆~7ß\sëQ5¶cº\ïj’h%b/L¬%Hî~çѯ*–žÕS:ê(VóW©Á'u5oKÍ«Nf´íAoUé-ÿÉýlfñ3Ö{Ùš-Í”\6aO›È€’«“ØÓ­ùR,y/Yñ&[ðž2á8ªOŽè!o‘$“tsö#\+rÇ\à˜…hŒ;¨&n¬öª¤¹?»ÖǺ˜é¶×Óµ³#y`¿^¯‡'&×PñL¶ŸOP<"µÏŠã¤ç~²oF²]“=P†XrSgcC~Úö›Êö~Êß±é+Kù¨úzꎭz<®Ò¤ö¼çðWùˆ Ò­ÅÚÿ«ÔPœóÙH5Âz=TʆZ:K“ï w1S÷×~¨m¸à­Gvjí\D<àI@×EÈA1¬ŒÅ%r[àh—®Ë‘©Çƒ˜Ñ“*ä¨:눱ÀvnsQrr§þÏ_üò(süy ²Î_¯5a8š|†v®”ƒð\/330óeÞõ’Ðã‘R7ã^S£Qþ'A"ZïX£¾ÊWf?.Ú¡JöÈФ•dmËú"ÉÚBçTy¡2ƒO <ÉÓÔ8/«q¥iñeŠešH¦ˆ'ë §šÊ^6‘¼lˆÏϨS‡^䆯ŽÙ‹&“—`Ö$^¢Kùð-‚ß}½ð²ò`2ØMŽä½ÊëµçÅ‹ȼìyó{ã7WNTœµË=e6=Œ´n ôÈyê RìƒOÞyÙƒ7ב'ß/Í‚ý"n=rA„&<)&|çõ8ð€àœÖc?gÄqÆ€uL‹\6Hß"’f%rÉLè’‰ð[ÀS)Ú¹H¥›+fY©fX(_69£.vR™ÿœïYUvHÀ›—Ÿlã" =ŽÊAŽËrdÉ"} E[ì¬2_²š ,Ê3´„RÔx“Õy.èò¥˜‰¦Yˆ§ _Ö dK]qÄ žPâ?®-yÀ@ÆKœØH±oüðƒ‡K‹ó(/hfÞ  ÞPè`pàaNÉ4tå´ñfiõõÜ3?»öÇ/Ÿ5UåDx5W-¶×®·UÏÓB¨:âœÊ©’B/£"3!˜vb•_j+Rb'Rd/Tì \ê"Vé!Uë#×à¯Xí-[ X¤Zã¯Pæ)Sè"Zè"^â.ѪQí'_î%]å+עޮզ٤Ѯ—뫬&ÆMá§Ÿ Çç«GÆŠ'Æà+KØ&ŠÕ,äÿÛEK 4>ë"VoXwà°“t´:ÉÝnüûçé“/ª6@XÅ(ñmi²ož½4\,`Ø p€YXKU¸*¡×*¶UÈ5“@Â/öh[d‹Ì%.êsŸÑç<¦Ï¾æ½@ØŒyW¶ø{¼#Ç*^A‡‹kº¾ÜÜzü¥øÀ‹ÖCn$)Lû³û90´gþl­kð«p—(w—­tU¬p‘+w—¬ð–®ô•ª ”« U® R,ò–Êu)t.sn ’­t*°ç+t+ÕÎÕ;`'å£AÂß¿zve´wåE3Þ鯡-„QäÔ‹‚‰áf¾an¯?nÜéÄy>P¡$Ú¤,È ÛA‰XU.wqvc.I\ôؼ¬øÞ3È9ï×Q½ë³[—Ÿ|Y6ÿG3}÷릓;±Ù—$™{Œÿ4 ‘mÔ‹Êb@ñs›þ~ÿ†Õ`xœ]©ªšYìùqàHo’õ­íŽ·“œ+53CUòb5+buð‹õ^EvJgM`½.É@,ÃX"Ç\:×RyÐùæ"EÂ%V¢®*Xhº(^r‘yg ·©I?bõë7¹TÂö¦tY°ñÁEa^ÐèdFêA`øº:¡z‡–¿'½Ä·[³ú²Ã^~UÈL7£ç~šÃyà{Ìêõ/Ê/Û‹ÙË¥é œUáLÕ*´”/± Xjˆ2C"ž#J ‰:Å:',q°CÙÁp‘o!¶jÆ #¼RSµYi:ìù–¢5^ºµfõ±ÎgœÕÕÈ.wÙŸ>Èš~R÷ì—¼ó©–éeDól®½~¯ùmðhëqÇ‚DóA »\ùò[<ý& F"جÍ.Tñ†×:uÀžï®ˆ•¿’¤×¥\æ%Zä,˜cË[ä$Ò Ô¦Uï¯\á-Wí§Ø¢Þ¡Ñ®^¢\à&Pæ%\î-Vâ!ZŠ[Å]²ÄE"ÏN„²Ðíe@aJ7IÔ%-~|;åv*ÙFâiºB—4øÓ@à7–*4W ½lC)4xø<ßT¿ŽßÜ~йÁ—+²–À}[í®\n'S`*R` \*”¹|ŠžØes…ÓŠðÐsæØæÿßJ ÖÙo wZƒRCe ¯_Ï-¼™œ\yüë Õ{b3¼,³í4óL¤ ™¡Ä»æ·úè€éeës£¥É3á=sN5Åî) rF‹œÕ%çõÉC’lH.™“,köL[ŽtKrÑŒ\BÄ­9£KŽh£jä„ý3ç6ÿäy#’i-xÌLÀ˜­9ßW_¥½\¨‘ïÚ"˜ÿ»ÔÌQ6¥(L”,—¿‚uÿÆýžp—C©Òüãï3ãµ´ÔÀ+é³ÌÃFä¼!XÉò½Ùi9r-™.ûe]γ*äXÙò¾Lˆ+Ê„C¬?̱¢#Vˆ$Ðý¸ïäÒŸÍëHa›¥nÞÀcÿYj6¬«yÛÕÌÖ¿n:ã«mÄuöûï>.ffo0Ê/o!—ôi ‡–ÑE\|I$Ùˆ¤˜“t[¶4;ŽK[ÈE rÙŒ¤˜‘óøÇ*˜øÍ¶caÇŒ÷N}Pi|ˆIÛ­óøï©È òü¶¥ù·R³I–"=W9»R ¹ /?H½l-ˆ(ùÇ1sîw-ÙdHk¶ÿóGù gÒõÙZ3³ÖJµ'OŠ>-ÌðåÏvçOµã½l+tÀ€…¨nmBrwYM}U¹6Léý“ .®¶C¡ RÊú§%U!šX›‡ ~š…g†ÌMrJŒ\:-K?¦(ƒFËJ×`¿™0ôÂäVfr ¼‘4-Vº6[†IA¦Géa)F|'´¸O‹Ç(qr.Të×{éÌÌ ³qƒa®Sœ|:¸â§÷OgEÊæ†É†©ž´:ë$¹Ë„˦¿øO]šƒça=ˆÁÓ“å´Ô¼«°#Ù™”zpã–H³$yvìi¸7,è­‚Îÿ˜:}^Ni“³†ôœÁíaLrI‘++߉:q9qçãA3#uÉ%]rA“®ù¯ óL8OY=‰àBy8^ÖBmAaÉ1’FmIÕ9!K;à¹ÚRx "ÂX“ ÏÅÙ·Ï‹IÑeÃIV¥ÿÎÇÕÓà=§)pÎXú¡8”ø.ò<Í%¹ÿm€ü–ÓG¡HV76fç_¿|ÁŒ>ù¦­²éðšp»öË6í&™fk9äƒè‰ˆçê ! ­ÔR&N•vm~ªmþJ­ Írx#·…È·‡É·‡+tF)ôÄ© $ªnÓ¼²M³?Q½?^£/A»cU{cÕ¯l×Ú¡?°U{0Q{h§ÉÐóÆ8‹(D/¥_~ÊŸYªEÍŸÇêö•Ø{þUjÀ½œqS³zó~UÒA'ñPErÀ]äÑûÆža€‚uL;óciŽh]jµ‹ì•`> ¨EÚüÔÛü5Úüµš½Ôá}-Ä´ÅIF:Ýþ\~’å¦{.{iRpÊÙß‹Oë_O5á6nc‰ò7hµù'—cs¼‰ÕÌ7½z\wÔM2T‹¤rׂ™¿±ñe^[´\gˆTS€t£¿\G€jO¸fW„r{„\W”Lo¬ü@¢*®®Ï@¼Ê•x¥ö¡aØ,7E(UEj¤ù+¾k+j=‚û“ŠàI»4Ž‘z”Y¸ß¬½¬|» °ë'0ï_©;îr>@u»!§¿q”#žºÜÛÌÇP¦šâûc,ÛƒŒªý´O;ˆžó’>fÅ{Dq{ù³¦¢»Œ/‚í¬+tQN¼è$sMDaO]e)Yo's;Ê¢?D¯Ú[ñ¼%÷1[NÀË_÷`žoºm¬¶0ãåOïﻟï4p\ÿÖa“ŽÚÜM²¼o1´Ý¾3Á6Ó[3Z‹­élØøÕKOi©Árœah°òdïóÁ3é¶"ö²56²uNJMZ0Ì)µ‘nrR¾h0¨qÅO±ß[©×[¹ÏW³ÇG§Ã]V]ƒÁf8AÆ}z]¾­žò®Ru΢9&¤ÆU¢ÑW½ÎG»1ܤʦ‚ƒçC˜_:˜¹›ÌHûêÅOnŸ¸šå]wЬ$N=ÅY ÓI¸ÄG墕x®§æ);Yy’¾Ýø‡÷/RÁ<Äæk°Äͬ¡[«lÞ!m§JG„Äíwt0½ºUóÆNý¶P¹ÁÍÞÕ¾õ«Ût®m×Å…[åêNõÞD¹¾D©îXɶ±ÎHé¾X¥þhå®…þ0ÕþPÍ-Üðþº]š-›Ÿ¡iUJד¥+Nž(N†Žp¡©t£‹fO€Q§¯~ƒ‹ N»·V°Qw€N»F«¯Z»¿F§ŸZ›§J“«R›—f³§vGˆEM€y‘¯éY{Í­:âIN†ŸÝú¯°ðÆè^Qy¿^}ójfæõØðÄç>.K- ·;g"Š·|¥•x…P±/b(ÓTxaDƒ}%Úƒt œÓtغ˜aD²LH¶ɵ"¹6$ߎ­Àž9ÑSâH?–:±ác±)p Eö$Ï–Û³—»ñ–;sãóRâÄ[æ*–á"ã&A-¾þâòôLª!|[j°oÝ þ§Ô@ßúW©yuóýúwNzÉ¢«9â-ñãG)/ŸW¡‚žúÕW9—]8ʼ¥ lJ¶ð˜r¥é³%kÈ‹à9‰®æüæ* Ô ÌìçÔ8ö`*×ãAµ³0$¨Þ1û¸aDÓ¥V´4Ã’š ^/a–—GË–‡¡Âëbæ‡@ Ñ%áš$ íSˆ^á)¿¶Ò]]Oýå˪Ñ_Û§ŸöÌ¿è™}ѹŠÌøhÝüBûÚFÿË™ú1hýÞ ­?ªI QJ°$C„ÿ~kßü(5-Ÿ~I³·67Po×Oo—Ý›<ÆÕÊåˆ{—'ª’#Úd¿‰“#¡ÄNŒ8ñ“ã[ø[b¬èóFk±WívÛ¦Üb5¸Ï«sâ%{I‘d«risçj°åpäYò]4ge{Š´"ô\ø,m(Ï¡úˆBJ{ª) `¯òå©wçkt¬³ÎÑá¤?,y.M›Pq<èù?`;Ö UæÓ'yÀ÷V A˜ü³ûØ9#V¦>×QQê&çIó5I®!o••p…%O±)[Ž.îd"7Öa”Ù°]EcsR–v5g±cE¬µÙÏ’t}RdÅ‘aÌqR ݸÀErPóÓìmÌWõ_Vï8ï+|̉=7L&;P*×G:ÛMì’Ç9r^‡ ;£Cê¼;”9ì¸ÉŽŸßOƒ™3<Á h•úéµnü–W-ÔÂ_lOªÝÙðü¬uå)wâ(³c+²%¥¬*wž W®ÉFÿ¿…œ+R'ÈŽZ‘‚-¤ÅÄŽo!¨E¦$Ï‚”Ù’FO¾?±FOs|ý›#˜ —·4 ¶äyR Ð 4' “4Ãv¾ÿ_©™ª|3[µ2ÃÕfíÚ{µ;¹ˆ„)“Ã^âß¼w~ø †šªØý*ç¼=Évä̲d/ØÂ—oÁ—aÆuÉ ¯÷N©ÁD…2ÊpvòÒŒÎ=ªìÛÔ9ÐoA½Íàó;çw;÷¼âÍb#Xý/‡‘hF_C󓈨[™ÛL×EÆÇLÁƶ«Íï7'î±är'åƒý¼xàÇe!'uÈeDÊê“C’eDòa.KdMjI%î+ÜÀ¤Äz³ÈX‘RsRe Î ©r⬔* VØkÁé 4Wá¯ß?÷ü1LŸT §q Ôùdª¹i4²|¤ó«Þnòä«L×éàﺓG¿ªƒû•ñžÕ‰Nˆt_¶@¯÷lø×M`ƒ¼Ò2¹Üôt¦êÅbÝë>à“ý¤ŽøÅ:Ûmq³#(5t³¼Éþ7Á%òáçæ _­‚ÆÓÀŒ·Äê7>bA w]²Ë_¼þ„Ëo§~¯?¾GÓA„ÔðêÛë , E\m$Ëå%ÇÀi¢´ÆËxsá‰6&)–$ÅŽ=Å•3Í‹«$R°(„UàKŠÝI…+œÿI½%©3a5˜ Hib èÛyˆ!©¾ûì»Æ™§UÌë.쎗—ZixÜLÿ÷µIXòž’'ÜüX ¦°á.Í1f/0å,ÝÂShÉžkž®K.ªÓ—=ªÉjœø wÃìŽå:€h“óZÔõXý‡e(©gIR$ \Ÿ Êó´%9fDk“Ãj”ÒâÜ>L.²äà¦cÆ6Ä›òQ#¦´DÓßÿ–³1Ó%)0z/A]ÞÊü–[êÇVãDÊð¤l¡¯› [R»ÂŠš"sRaC=¸;ü…½¸ó-é׃;*Ó”…»ºØZ(ßB Ç@¶69’¥Øa D$=;9ÈFöƒ*tK¤{RФiðçJäˆ$ñ¥ã&ÙJÉRKálöL=’cÈÊ6áȲʲɰ¸,//\€ªäŒÿV H|ʼn1Y×mËûí OŸÌLORR åå½2LõÝ”ÈG‘` 4ðÉZzÃ,¾f–Ö6Vf&_üúâýÞº]^ÁêÅ6ün­^2UÖ" Èa±Ñ€±g‰‰t¡¡X‰©hÂ5œåÝeš=e;üåzÕoÄkÜÚ®u{‡öíjwåoÆI\Š»-~#Fêz”Ä•pñ&WV£G›'o·¿p_€x‡P{¡]c6æp%Þ†äÏ_ó&d< j}5J µóÝ,5 Õ`€…Ú;AH„Y»òçƒÔòw,/ù+œ ‘ÿxèØÈÓÊ5LO‹]Ìãʦm*ƒ»tZCåz"µ»ÂµZ‚Ô«¼äŠ% $Käª\•›½uÚL‹TÓ¬•r|Ž9«†èrù°j³‚ÿxT€ê¹qÔBS<ñ<›ê£ÿ£ÔP*-5_öîN TŠÐ"uiáÃÐZ®Ýþ½ggg´ø@Žì@´òõ[‰Z·v¨ÝÚ©ø`«ì½XÉ‘×#¥nDIߌ–»!s3Tª×‹¿ÖŽ ?¢1X‘;‰¯2¹´ÃèÇ¿§Œ céªØÄ$¬þá¥@ã™–aê2 JðÞŒ  mRºÇîÛŽs‹ß4¬þÙ´2Þ´> Õ6hu( I |` ‹’™ ðTáùðŠéY{ÝE!¯…ÛE‰º<øpý°7 œ8‚âµ š"Òÿ_©¡ŠÎ…’•ù2ȬVŸ×¦$ªV8ªxyŸYkmüÝ«‡ÿü$›yÒ½p;L¨6éO‰ž½‘ßµÍ6ÇVºÆEµÌT¢ÖX¼Ý\¦ÓB²ß^âP ÚµHõkqš˜®ï1¸µ_ÿÆ;Ê×wÈ ÅKÞˆ•¸-ù^¸ÌÃ`ùûþJ=¶’$ |~ÀÃqæ§(VUzÂ/_×PbÿZë³çE(¤³ŸwèŒÑ­°jt”¨s’(³.±¬õ@ö„bOÊ@¤FO¸z[ r­»l™½T‰­T¥½"‚)k]5J¨øj…‡\c rK¸zG„J«¿T‡ŸTW€Ò@¨î•HÓ+ÑÝ&ma:ØqÔ+4ÊV{‹W»ˆâ/ªµ‘(7¬¶EžQ­r³n¦¥ò>M@iĵÛþöqî*<ÍF©nw}±Ž’ot½ú!íÎÕ‡Û¥oNJ݈½-q'JêV„Ô0©6WÎW®v^$‚õŠtøòל± ïw´å¨ ·¡ ;$Bm-÷r‘C()œÔmæ9#À&!–#'›£,}I‘ô~¤îÖØ)¶{jµ¸©×ØË•ZŠÖ:H×;KÃü§É]²ÓO®=H©5R¯%ƨ)D§Ò]¡ÔN²Â^ºÂE¡ÄU9ÛU­ Äò¸³Ž¿‹¢ð»~.}Ey¿øtft›nºÝþï¥fþ5ƒ³°¶¶4=¶üçw_·ç„™f9IåÙòA)™‡FÅ‚•¦O Œ…SÕùÐ^£M&Ÿ©šlé:'BfCR`I*ìX5.œuîœõî¬Þ@žîŽn?În®¾@ž¾ ¾¾Þž¾n_¾n?þ_Án?Á.oÔœWžžRÎ4[A/øPé‘Ç?eOÏÃÂDîÿ³Ô`œfYmžƒOÂTËô—ùÛõÎùHùª’‡ý&ÇW—º@ºÛø.§"Fª1B"Ëš1K5¦Käƒ`=‰S*„i Á Tí}J¬=šX%À~an¼lr¬ðåx>®çâjå+¦áÅXæÚr5R„ÇÊæŸÑ]É\ eæqwùN³XS¢'@ŠÎYÿþMÆø³è77n›68¨AGWCùÃë˰Œ+ƒwßÊ˺O¯ø¸ÿÐÇ7N}ûeÎÏV7®½lg&í8¹ßŠ/Óßŵ+¾ÈODü(ú„d´÷9U ÙÒ¤©Áº¤Â„TX¢Íc«tdÕ¹qãÝÝÄÛÀÙïËuÕŸç†?ÿ]Ñ{>â·½e]å3ÕÉ1Ø?ª_âoÂ;ØrrâÅÀÂÍȘš©]ô±qåÍÓú¦=†ÅXÍXñÕZ Õ;Š4ºK¶É·‡*4ùHT8ñá~H1Á€CÉS‚“èáÂBƒ 3Â9m2Ç/š²§náÊpàú؞³x «Ø’£ÂN¸ÁU¾ÞM©Ì^"ׂ7ך«Ð–#6DÆ$Ï€°Ju8е8r4HŽ PÃY%ν2,( @ÆÈßcÿü늅)8 ÎÕྂƒ 3Wûüîîþm"7"xY}þ¬~î+|~|}Þ|ý>‚}¾‚HfÄ]ÑãÍßáÉÛêÆÕèÊ[i/\í Uã(M6L-N:"sSSœÉ4yD5eÊ å©HkÊ—èÈuzèUÚHçòCÙ]ë ßâ®Rç(WlĂոÎI¼ÅS²'P±/X±ÍG¢Ì‰÷œ)9gÁuZŸ£=|‰Ñ­íQ ñòd«® —‘(LæM;Ë Fúiebjqzzeyñm'ƒ”Hó–°G;j•…®fö 3‡ÞæÕ«Å©‘…ß¿ü®-§.ѦÒvÓ‚høó,I¦Ó#ßTÚ%5‚d>K‡dè’Ò-\%Vì¥Ölö¤Ö…½Ñƒ«Å“³Å“½Ý‡tx“._V?Gï`ÿ`°N ²ûü…»}hµAðÜ@ô@¸JM€r˜" 0"Ï~ÉAf%^¦%þK©)Ÿz–õé¢/‰t0“}5,ÊwE›’¦‚Àß.^˜i–Ë<«iW}p·j{¸x©#w©_ž ç%#r\Q¦ùV—ôYຠ8ÍFî‚“Ê!GE$ Y«‘ó‡}‘3û Ýõ#ùp\YæKpó¦2„ÌBì §ÍÍR³4_Ì,C˜Ð2íúŽñ'ù±¯Fû˜±æºÝ ®ˆH3«É…»É«Á‹ÕìÃÖíCz¼IGw ÀÕ ‘¡ Qœ.wž+H} ‘©:ã$è)GâíDÞï= chg@Õƒ.\:a i9* ØtÇÆuÑVÄG‹4]òšþºb}¸ ²$@¹™çêjLtGGS‡Ÿ&3L³†òR E<³ÐK5 eþܸ§,Þð°ƒÐn{>5R›áõâ—ÒÙ±z@^•ÚÌñÜÄ„ÿ§Ô —CÁÁ– R)dÔÎ7!`n©÷ÙBëørÇ«Z¾ ÿ:`ûÛQðAõ;Ì/Ísïgœ±ãª“©Ï‘­FLë ¹M8ÌØ¬ØêmI½itaoŤàÅÙãÅÞïÍðaòç¾(p/HôA ÔÝ ùLmúãÛªLU¢æ<ƒ-'FŸus;ý²ôåTb˜7ƒÌLwç~“÷ÛÕØ4Û uxJõ…*]‰Óè‰Vª÷,´e+væ)râɵå¼dHç‚㙫“T}ö=Vº w¶_Ž _†-OšJ WŽW¯Hµ_™-O©5…­h…ƒd™h‘-‰=o‰g‰ [‘%)5%•&œ5F<Õ†¼åFœ…F°n8¯Æ}@‘(V ©>í÷âQõÆRÿ,®*‚·&бHe¦«~îìM¸ÆÞ@ú|É`×Õ@>Zm||…D®ˆ ø‰ Úl>/"ƒÁRƒa*ƒajƒaAªÝ¾ H!it¯Ã—dÈ•«Íž  *Eب “Ó°þ[ ’XȺ!:Óg5¸Èôi:î T«°E›$Ôê%5¦t3NëZ”J‹œ(X™ŽÜ9nÂÙvpG¤cãiŠpžÔã:ç¤è%GÌ$H´‡Em~Ú7Ÿ|4ñldò|±(fsb¢Æz´æü«Ôl¬¢Ú,¿a0@1ÌÊ«õ…—Ogøè¾‚[Çüo&Y\Õì ’é‹Tl ShSí Vë RÇéTí PéTî RB ¼…ìE¥¡HÙ1r·âdî$ÊÜM¹/u=Zøf,ÂEï&JÞMÞ<2÷eCD‡"$oÅÈߊQ¸IϽx‡Ûõ&wÆÄk±BÍ9†…¼¥˜…LÁ¬¬é?¨·Úf½Ð%¢™ßüQµÏ‘}«UÚfŸ²ùêót¸/RµÝd[ãNùöÉDÙ«Ñ ×ãԯū÷E©´˶Hw)÷GhD &li³ªŒ³:䦄ÜÛ gÉký'ÿü£bÔSÔ‰q3P`ó)›†ZM˜ýg©»F fµo_¦sz°Âns®; ï2SW‘jÝ~Xãþ^•;q’W#ĆbäîÆ+߉S¼‘ u{›ÔÍÛqBw$Þ^™» r÷äÀ×éÍs3^©?A­.V夛RžÄè?ú(»6DÀç ¥fržÚþÏ0™(EJ,³:ðý£§#¥ÎÄÊ=hÞ¶1ܼNõlUØnøiràý اäm·pR!Ç5¾|/ùå³´ ðë:„ûW©yë”5W49–9;U€!.+ÌR/ØÅ£õÓ ­Ï^.7½®gºëê%Ïã.ã´ݼ³,f¸á‹Ú„Lo±¹VO¹;á:ïGéÜ U¾*s3\³äíé»qÒï%È>L”½)|;Zèn¬Èý8‰q÷c¤îGËÝ‹V¾¯w-Ö :H ¡6!Zd¿âßn¤-Ï^9\>5^²¶ã‘†zóGÅ;ºÔé˜ÿ%è‚5)°½;Zvh«òí$-¬lèz4A½7R¹#H®Õ_SÕ`¸f_¸ÆPœþ­$“[»M¯ìÔïۮݻS3x_¼j´jïæƒÐ¨Ñ¢Õ©;§Û¥Ü)×%;!%TiÀ_qÀGyÐ[ùz¨úÍ(ÝÞ pãK¼t˜ˆ©“²ÓÞ¿…÷`Ç4ÍE©)b€¾ÑöÇ•¸G?Ü-~/Qè^œÐýx±ñ’÷b¥îDÉ\ CzÑÝ(¥ÛÑJ77Ÿ—»qªwhߊU¼+#ZþF”ÜõHÙ«aRƒ!ˆ@½&{{«`¹¹~?¹Á¥¡ëaZ(GÞr¨Hµ À™qd€v é¥Ì&ýC‘¤k“²-¤Ý[øZ„ܽDõ{Àbº°;NÔêSêñ•éõUì Òl3(³òScù™ÊÔ^øõÛÏF‡ŸMMÏL.ÐõҦĒ~D‘ù«ÎP¬†Y‡°i—ÌâŠØ¬¬­Î<ûõŧ7>ÈÙß½mKo„Z—ŸX³;³·H©+¥—X… ­‹`³ì›ÐI¢Œ×:óRèðh÷ákóæìðãìÂñgëð%mÞäjßµhT›ë±B×"„ûCùûƒùû‚‚E®FHÝÄ%‰TŠ”¿£z7^ µ5B:¦P3¶áŸ³`@ ‘ìâËÒÍR–n þ…Õ Ô̎䮟œ®`Ö±é-× %D6É#ý˜å£¯r¹Ã`xRyÉœ3'ÙVð"5ö¤Î‰UçÊQçÊUçÂSï*Øà*\i/Xh/’å$qÊ^,H“Ø tÀþ»o*§¦ZAì¡"âÍ dtøàÎô¯Rc7t5(5+‹¥Ìz-3UYvPóœ»Ð¾¿÷fÖî2/kwÊôÆ£»kp%í><½Â>¼hiÚÈÕhžë1|7ℯŊ Fáúô âtøò^EÁ‰S.QÜmÃòÆpzÊëç ‘UÁˆýÌÔB1müðíƒ$¼ÒÛ”b8D(ã]ÍϯîÛkX±¶Ú±´Ú ]fr°F²…‘æçôOO}^àZ%šïÍ]â+\ì)œë f'rÄ\à„‹Ü;ö¢.ªÈ¯7þó;p–§F!fMþ§Ôl:"ÎNÁ’BÈòñÑ2ˆ^±À­žkž«' ¾šÌXSY’ÖЊ¶êþãö™é§¥‹ÏŠV~ȯLROsàÄ>®Æž·Ñ‘·Ó‹¿Ã‹»Í‹ÕáÃÞåÏN‡&:W²ßˆÄÍs3^—èJ„@__O g€p“xg„z±ŸÜ'aw ´¾2ÎN ·Ó\Z˜6ÌT?ù#‹ŧ:UbÔ[[lxëm81å€ße€Ì;r^›;’bgάj7®ÚÍû¡Ñ…¿É•.)pocõSêÂYèÄÊq"8y.¤ÆO¤Þ_¢Ñ_¦Î[¶Ê]ªÜE¼ÜM¼ÊK¬Ò[°Â›§Ú—§!@ À£³``­­`½pNDeJgÌE£”‰‡©¸ðähu;¦æêPjfÆ ã«b–>­v»wHö½$¡›q˜….l9Ö$ۜ›€.ñ°PƦ)÷n5¢@"L8nµxù¬ŠÔI8K,ÖÐR3WÉŒä5‘zWìÖVžÁHVo(é bõrvúr¶zqµzñ´y ´y 5yÕ»ò׸ðV;ñÕ8ñ´ºó5»p68²áknqÇž’«Ùƒ¯ª.Aœf/üIv,7Q£6,‹ÔÇ)¸·çóéN7PÔAqcºüÍ•àÑp–…îïû÷àzÔƒïÝ>º°<ÖH 'SMý't:“¤:¢øîÄ ßç äºÀ;,l¤ÛG¸Ñ]¸ÚE8Ç^ÄòÄ[ƒ4e‡M÷NŒvNÏ4¿È‡Vn0S3³³C¨Çn’I Æ®¥¥OßÖ¬hêRâÅOú ûæöil@˜ßJwÊuG"8˜Uã̆Må•iìš½QúØ«Ê[¶3ꢔ9ú&Í_žË^ÆGøäôuä–ç…‹“•”µÜ±þGqZ´hí»'ÜÉí÷•Ÿ³˜É*f¢ìùíí]‡å¯ï—¾Ïq%€ô{“›Á¨²|/3QÁü‘6wk×Gç¶ÜßoøÙY³¿ÑüdŸÜíÖ·»¥î°îF_‰“©Š–9èÌá?:eâoNvzI^ÚjPzÔéjiÂ?n%?*ŸiAAê+ì''ªç&«Ì‹Ê¶ú²di$Ñ-Ož^š[«X|UûôEÖ³g9S{½ÚÜ Cfµ›y58û(?c§j˜!i½ì:ñCÎârùød:Ã4oüx¹.‹=©Ü¤!aûPhÁÙmБh•é¯kDJŽ:}{ïòâhëêr+lú°>ž}õY#ókVóN©k»åoĊߊ½.x#L`(BBˆqb·$0ࣃêô#˜÷»ýذ¼èòåh÷dá¶ïpçlsçnÆWbÇQcÃQoÏÝèÌ×á%Úå#Þæ!\kÏUeÃ^ëÀ]íÈY f”-)w&5Õž(8›´Õ2ÒàÏÞ$PãÌþç¬Bô’>{?eìE2@á½3³TJ_RP^L5gr×ø³ðÈ[.4§mFGµw¹W“‹ø?Ÿõ«¡îTº¢t3Œ²#îDÈâ#vâAÂh˜;½¹è«$RôvœÔƒò“ïí”Å÷{-Rðý­2÷q)"„†Bù¯‡ E_ B½#~=JÇÕp tàú`á~;AújŒÂ⻽9|Ø®x“49.lÙnÂ1ºlþ¦Rï]k{9>²°°´0¿ÞÌÒÌÂæE[˜ÿüßÚê"|ùÖÖa;̬Í.¬t½ý`ð{G<Þßet?VöN¤ø•pÉö0ÙæX­¶íz[¥û·K^I’½š¤t-Iíz’ö$ƒ[IF×·³} ZóJŒF¤JOˆlw°L_˜ôƒw·+ἩØ,ßì!Q¾®×idÒ‰P x€‰ç°ÕÛ‹¶û¨ N"ÓQñ ™P˜yt=}ùE/ÒHá8DÝÌÃ0!{u¥¦àm©y5^¾6Z<ÔÀáEEó ½†]J…aü÷Ólf>>ÆŒ33•(5‡®”}¸Wä½D<¢ÅÆ©ÜÕ¹cÔkžæ£š´EtÜW¡–ˆ§ß–.v䜫‰euµmKë ÈKBŠââl½‘>@3”Qp6KM%¬™µf¸¥ú˜Ù¶® 絟üp.eþ‘õÞI»»{·\ÛfÚc8ip-T§/XlóöHÅë» ¯îÐLž Û*މ`ø ŒíW%š‡| Ÿg<žÿ\·¾0ˆè[˜r#|ûëÙ:@13Ï‘rØÂŒÔ5ÔlÚ.÷áËor¯ìS¼yLíëtÛG'm¾;dñÙ»šŸ¼«p'œu?”|ÍýQ÷G[eãä“=¹Ð=úo!Gw´'¼ßwêû÷ÓÿõG)n_Øò AïÞVƒk±:}1š}qZ=Qê]ÁJ}Š¡Ê7v^IÒܦ4+‹‰Ä’Zk¶"#à | ¾šUA'ì$‘£ºÃèç²!AZ˜o„AÖ›•Æ•Ì\Í›oÏwï—¿wTýz"ž;¡»ÑÂ8wbès(þN¢ä‡»åßÛ……Ý\ÄKÜ—›e(\ì~¼ò{q*bÕo‡« )]õWº v3Dój€Âõ`…kr}€8|ĥѣ ¹³Uõv¢ÊõxÅ+Ñ2=8½ÑRqÒñbwv)ßÛ¦s‡Ui¸~¤kGˆâýÛgægÛ§§«ÐŽLdNNæ1«MÌLG6‡ô$©`·r%Lª?ˆb)U6àÚ±¿}^À¾ƒ«Ï?Ÿ¬neÛœ…ÛqÚ„:Ý„ñU] ”¾*3&Û9ÈW Ã›èÖõñ;>zGõýmRl•z˜ q/FäF¸àÕ0þká×#…nNjߊ»+Šs+Fׇ^™÷’tîoÕ4ý^Œè‡ñ¢Ä{ce+£Õ÷8ÈE:ëßìïxöìùÔôÂÌô"˜^mZwþg•ÙüøòÁ¾5o–§fçŸüöûí®ŠD· ?ÕJž RiE6’vÙš3Õž'Ô_H ܸJ\xóí¹s¬¸³,¸rÌøò,2` ¡C‡ ôuÀ”@8,0#é´FÐlº‚¤¯GkÞŠ3ŠÔ Õ½a<fx+ÊôáÖ-âÍz}•ËÌyðïW!‡´¸â”‰éÍK|úUÅøŸ3q…ŒâèjrWQghW‚GåÊh ĆZãE†”f¬êãêàòxÉR#öE¥?ó]ì¡æïíܧru§LW«3-"m5k9«œE œ$.9ˆ¿kÁ‡xw?cR|Áóû2^´¬Â>ýE°ŒH(bØkÀ/efºdz¢+o€+Ó¥«S¥|‰ššl/¶¥gñËÂÌ­NäÒYƒÑ_s™áª'­IUREŽþð\Öf]Fã­AÒ5Iª!ɰ¤ŠÔ +RæÊÕ,>§p+^?lÄ`TRa|ÙK5Æ€=ÞQ ­8jø·¯-6M¼„ó@õÄ 6× È˜BBñ¨_+3Ã%Á°­ —­ “)öèÞ©Ù)÷ÿ{ °¸²t tã.EaU…îî$$DˆKÇCÜÜÝÝ5žtÚÒ6ÓÓÓîöއ …;$ç­MæÎûÞ½ó&ßþø*I¥›:œýŸ¯ÉÛÛ-?Øiueæ›[´ÚÉÛkdß !‰QycVÇ:ÝðZ?±Ýôí+§ûiŸxÒ=?Þ3?Ñ¥<È{K£-HƒZ¨›ƒÇÔr/3ÛF3 ƪ¦ç릗ú'ËþèËú:ò¬üåŒþuÝË<µ •º¶ðUé›1™Û qúëÝ‘²J®Ô¬xX±°ÜŠž™k»×³-?T¾Ì[6¤,cRæ(—c-A‚–ä˜Õ¦$×YI\ŸLÐïeºÖ¨¿¹‰ûî£wÖ£ n âe8(Ÿ±öÙʇ’ê¤àGß” >FHMèåh@ªì{{oû>­Ûõº×É__+w{µŠx ßŒU¾±Ž.œ÷ÛB x8éã«©ñ ø‰`fí@Š­‘cB2Œ¨3 zL!\¼HŽ)É‘BkRî,Ô·5D•M¼%!€H ù ™®$Û…þ|Óí(m¾ÜUºÌ›}ÚI%”Gl4þúÓ\ÜÒkNÎVŽNO—2Ó̯%Yá²Ùn¤Ä•Ôø®Hµ×ãŒßÞ*º³Q„yôë­ÿ¹_ÞÛêty•a¹“|6ÈÕ|’Á%i0uÁˆ ¢Q}‚w†!)·c•ÚÉ–ÚÉ”;¡ûb5û«´‡iöFj^‰Tº½FùÍõêo¬S»¾ZùjÈ'hc40‰ HòZÕ›±j·ÖþcÝ\«Ñ¢€Ç_gió&~¤3T¢,@挫|ĸ›Ýìn{þ|`fvizjžºC,`´ýÆ+ˆ ì°ÐÕL‰ÇÄüòãÖÜX×â^…·|é DK©Ü¦Z쯖çÌq$‹Ýå œ¸,ËHGá›l$ }4İ4Q“f4'ó)ŽÝ¤ÔªØ¾J­3‚Ó¦Ýè¯Qé¦\l¯XˆUr×ÇXJ•ÙËU:)”»ª%™É'9hí3“÷Q!-—Öõ}ß0Ñß;>ò*°ÓlóB Øé¯JÍTéËéFäÂN`΋gOß:Z§yã¨Un˜lñ:•ëb™ÁvæIÃOYAÝëÔÚbä:Ö²Ú¢BXõÁìdWå=S 9gô÷·FŸ5ψ;æÇÚfWÌ—ÆZ~̓S:241išc-—b!Ù‡F¹‰+gq·Ì]þ¦õPB¤Ž·ˆœŽ7~~/»òIÎê³Ì—:Ã[¹>ª¯$çqL3¤sš•º0¥–H•ö(5\¥¦@• w…\'E0ۉ䷘H„ñÉhýw¯G`îÜ|š‡¡Á8ÑMÎÀ½¡¡Ÿ/&ºáÊòMÏ¡Ü  ^ŠG-I²+«6ÆøÚ>· WÅ|WÅ7…bo™úPVS°½Á¤ß4ªÑkpÔ]f-"‰¿[=ÝU}d°fx¸×st¢Î²Ù¡ d½×ôÿ’6ú¤xøÈ@áÐpÑØlõô˦9 K·™ù·˜‰[Ìó+Ì£žŸ”|˜¾&ÙO5/š›¦})†›²ÉpÙ$}³ó5ˆ:ç:‘”%ø·-[+£9é"ª©/°L5 ~Ðç°àçO3¾y$njԫ׮Ro‰TëŠÒìSióWªu•ÇÝ’o¯’d¥xÉC÷„§–›I?dÿð; öÝÈ®Åabp-1Ã5?v­ïا۽M(f[W#doD³®¯¦cÐÞ5 =«±:"ä^Aˆ@›‚dqfA©)ˆ Mš`Äq)).;ÚÅG¡Ø–Uh Ÿ[)0Ê€1be‚¹ê&Sà.Uâ-W¤T®^®RÀÊ÷$’cU{*{hí3•(>Îô‡/òG€\=3Wµ°P $ºšé/R2‚%[V+w¬Rl SèW‰®äC7åB;9üÿ¹_òE2¥ö¬ '…*g<¡J¡±V¾‘f¦>2A¨á‹äš(ç[(•تV8jàm•®êÕê5^Ê¥hÜI“hKòÝáJ Ђ{ œ²˜ÂÀª]ùm´:^×¢Ô0*¬zeµð«V_Š cšSä+wP$éNrlðÇwßšG î<Òp–füþŸ¥fÅÕœv5ž˜š~òà—·z 6û—G›Õ†°ÛAºµV§wµ^}¸vU0§=Ò¨7ÆôÚ:+ÄçuÁg/ؼÙß yí¢zOA¥3·ÆE¿ÖU¯ÞC¯ÝÏðJ„qBM«>H¥)T£-T©X=‘Æ—£ÍZ @Vlôåu… ¯D˜4ûjCmGÅ\æŠíµ÷ eý  ;9üSû‚øÖ´¸“ª) ¿- -5”°J 'qó/ÍÖû•2ý€]<«xy/¯û¸un8«8B©kŸùõ£vožtþà¤Ë{û¬¿:ëݱӱY¿s³aÇ&Aózaqÿ”‹ÂzšÉ›õolл±{ ^:`ÌöFv…p[|¹øNp?×¹ñÜ aÙÔ„ìa£¯Iƒ¿°1À¸!Ш„ÛPC4-å~ll¸3µÆ·Æ"Ó“[¢Ù¡Y¤ÚfØeuÒ^= Ïå¶?|–7?Þ ‡y±8j¢`f¬˜YìXü2½e«> ªÆ ù†@VSjÇ*( z£„=‘&ÿ²_L{#,}õó¬dS $³ ع†Ü|?_ÀÍæë¤èª×=‰4ªu: ÛX…ÿ6ÄÀþ˜œnIË#ð®VÜðÝ«ÔÛCÀ¯Ð¾«‡1úÝ-Æïo7}µ@­¹µQ÷m\*oD+  ‚²¦<”o§m¢ÞSšûüÏûã3âÑÉyô3Ôâr§ÿµÜ «AÂ; O‹üöÍÍΔµ§Ô „¬õ’!%Cg£ è”4õ æêrÕ&ˆS¥Ÿè JœÙ/M@|¡Lõ˜8dX|´†j5«Uù(:ËàE>/ø“g´éà î²9fÒP¡f›Id˜ÉÀ‹xŸ.Y­B%È¥­N÷?­Ö3Úß RØÿ*5+úbêØ ²“_„ÃbD15H*ü±e{r€Ô!+rÒžœ‚÷‡dU¸&€Ö"O¹’vv¸ZfÊEÅãv’› I˜ñÑ!'75—®»ÿsÙÜÌñHÃð`rWX÷~wëhg^à‹þú±(5ÌBãJ©)Z.Š‹—ÇJ1ŽA\œEWså“ò='¼ØvZ¤ø’ÿØÏUKßW¢Ç¸ $‡ÕÈ~„ËK“c,’u!›\2@|AïݬY%@Œp} ý<#”‹7RXÇ!Ä—KJ“‚ïß+o^4:Z4>š÷Z”ì)Øk Ôˆ­¹Ò§XKp2éTI¢»QÍzcæòk”Hf æW)ð]+1¬ ѪõVä0Á‘ªMB-HKé†ÈÒêÁ¹i0ÔlÉÌLéôlùä\ùÄLFo`Î,LÕÃÝŽ¦º,]e–®ƒ-3û]îðû©Ùᆅ2(k‡’4/بJýfR°à€¡¨“ñ’ä£_”öígi øÑ„…þîCŽäˆ!%œÃ)^’ÄË‘}0ö—'‡TÉ 69¯MÒÁhb|©KÓ }•K%qlþ:݈¤IÀô3£lTHQ’OÿÃê¹Ù.€óèjðС?»Ñæ›i®û H‚ ÁÑ5+RdG…6ùŽ$Û‘TÊÀp ìwRì5®$øÈnƒO ÜÀÑW–ì‘&{`-M_ì‘"¯AZ(OލÓ÷œÒ£;þÐí–çŽÿ·“&ä¤)9)$uC˜ F²™â:Mê”°Çñ·o©í 8çâá칩âq>3ÝôäæÁ’UJík´qK»(r’̶¢XÝ,†$ûÍžeÜ!åv ™f2™¦ ‡eÈairHЦ½ û%É> úMžaK$ò¤¡!B4[¶%+Ë ±Thœa³beƒ1·#¬[‚Õêý+]%þᨹ2þÆD—+ߎT{SŠû«C%fâ0Ã9Ž0z6‰ét¹ áɉ¹±ñiœŠV\É¡§dó¿ÿBÁš›G”åò„xtøþï_ÝìLZã“(LsS϶“ʃ]&k8>[hçU™c) ŸO|oéÉt#é¾T’9‡ \}Ô ¹bå[¥r{¥jWå'ÙBWÙ9ü§’¡¤æ¯Dºè=rÔ±6HnòãÛ«ÂÔ篓ØõÖ6¸¬.ÞìøÇßʧû./Lôb¢ñ¿–L½AóN ú=HP‹UÌXÇü×9ÍG­’Bä“CÓCT3U‹Cµòâ ™¡{ÚOBéí–$R›h’MŽ’ç·™Þ½|ôËÆÆ:G'ÚFÇà"²0]ËÀRïISö^ÓË…áã¿O>.Eá ä6Š‹Ð\͉"îL”»ù9Ì\®|·å„‡¦³6驊cÆé±â,Œ¿P@pCÂ…IO2ßX¡Ê\¹ÚJ©ÂA±Ò]z9`g:ÁL‡!*.òÂt¤bH¤*ݪ;}tnÔ½öø×ò¡êщ*t0s—Àe |Øk¼œìzÿêþ5îdµ¥U”!9`Øv8ò^ã¥wRv'ùqwšöÃ^Í.ú±*¢´VéUú¨§Úʰ‘Ò"›}åÿróäÄ@ûÈSØ&ÃÅ7t$gl¬prºtj®jf¡na±qy–•ð9¼‚°•åKÿZ¸&+Lõ¼Äòì"oA¦#?ÇUOþ‹ÎÜí<²‘KöÚ²B´I0—xèW. Š{1`æ)Rï>ý­”yù&#¾¾Ïöž$(#Ù4°UÙt+úžnÎ*€ç›­jµZ½“:î·BV‰³<Ò?ÁõÅY‡n<¡ÎÈ&Øi²VÛ`!¹ÖIòNǾ¡guñ,˜mªGiþ!ÏkëwìøI–zRifIÍ>rtàȪ bUÉ—ùIz’j»bGmOD;«åÚ*g[*f[¨ÂÎ%CëK¥2½v¾µ:Ìvrm•tœdB.ZàßÊå¸HgÀÆ >Œ:mÈE;jqGJüô¡žN(D°ˆ›"¹¸ßå·ïŠGûà¸^>6œÍ,V,£«oø¶fý% j½‘ Õ¶LŽ$Oî+œiZ^j]ž©Ãyú§ÊH r·a볯³çúÁ•­|…3\4=ŠÖ·u¼ EsàâÎ_+ßawÚƒlF:JÖÀxvI51†olsm 2nö¶ûš\ Ý 2ïöå6xi »ÀÜ¿ÑG¥'\ÿJ´qO„Es°e•¿u±ŸýEWÑ!Û=^ÁÇ7ÎÃZ¦µ“SHx¬aol°mïHŸ™[e®o}^pâ>³ò³>wÊwÞ»~iæ ü¯?}p=«p“}",ŽköÝë>š½N¿ó€}kœIC´á•.ù1h·¢9?ü%Ö.âGeèà’§â¥éZxò@ÇÊ,v­$Ñ_‡¾àËž}U¬J7 ê7YTDð ¼ÙEžºð~O³ç'Xë1SÙmÂÚl"Î#îl²Æ^2ΗuÉõy«ïvÇßÿ²ðåÈÕg?•-­Q=hIU}—{[ ÃËèr€E‹—°ÞÛ¸1È'£ŽUæ­~°bC<ì@«}€ò]‰2ÃA»så2ÖúšàÿÛ¸Þ;/Úa§“jŒ“äí¶=cí3sÈg¡¹'‹£uÌâf¨¥ù¨èý‹¾—w˜\ß$ÊúÖFáÛq&ol6xJ(ø³Aúѧ߫Ӻ†Cg‚k¹«¹m‘¼¶~c0¯ÆG¿Â]¯ÒU¿ÜYyâyVšVšy"Õb;Z/½æ@~}€NSˆᨄs¯7Ψk£AëZ½†(­†HíÆ(ns¤ ÇAµÚݰ)Ôñ¸ 'N¤P~11. ‚å>Ô—º8Y¸4o±®'=šÖ"õµ6@ Û ª‡îhÓ¶0ãz>‚ªË\u`W‹-äS§ å±r,UJ8ðÊF²@­¿=ÄìJ¤ÍµÛ««­{¢-VDÂÞXAgŒ^K»)R£c-çòfƒÞMÂÞM¦XW6›\Û,¼±ÙøFœàæ£[q†·7½¾I!.έ­¦7·˜\ß*¼µÃ¸s½,Ú:×´®3Í æ´ÖŒâ«\/Î[x>ðìþ³9 44šr~t¬Ç£_jÐü ÔÌÍ-Ð@ïɉ¯Þ¾Uxdç{½C6 Rg¹idØ)‚Å}ÉL*ÏN1ÇJ(::´ú&2(8a]@З©p°Âß"<ÇJ*æ3†Ôëãœä“£+1Gà‚ ªÅ#,ÇZ0ÅXÿ+‘'±—­‚ÀBUÈ'å{wsǶ/Œuƒ<†CÊ«RóŠTC(_¥Ù"C0¥ÙÀV¢vi¤~y¸q©¯áÁß’Û3‚^ó•BfÍ^„÷©q×"Nlâ©Kbí%’·‹nWï~ðEñÔ@ÏP?š™Î‰Ù®¾ÁJØ02KíÃ÷3™7ÎoæzóIÙyÏç?Ázza¦  ú(Êž(Yœ¡ÑØT‰ Væ¼37’Ât· I¸y§k,à˜gݧ­È9j²?¢³l’e$S!R.·b3Ç%¢ŸF—0ãÒ¤–ñ09ÑØ¨HÖkÊùË‘@-’³Ïÿñàü·=/¦DAXF‡b¯åÔB/ÐØ÷gáŸ&>ú6{æqÃ˾vfè #¾y4BóŸÊA_åßߺ4{¯òv^än;rÈ‚7Çy‡œsWÀ59³Qÿû.ˆïC†Ó¾sòùáÕ ‡ÿÑnæÏÚOj6'„ªœö”½è¯šª“ªŸá§{Ñ]#ÁEý‚“ækYÄfyJyâ¡B|ôÉV?õ”ƒÎ½UÛï^=oÒÇ?W<ÿ£väaýt?„= PwÎ]™ø"7Þž *ÛB&ÓP~›ô h‘£P@›JgÁtÆæFޛЦWï•¡¾P"›Æ$O­³E@m7û(}ýarÿ³jpàÙˆRûS$e0OÂåâ᳊©–æ¡RP `vœ‹®fà¸?1ð‚=Žre¹® Œ ô¶%©paµ"Ö’Ù6²è ²¬²DJyVjØÔðŸDŽÕqer)„|Úˆâçø £ ág Ø"e:J@0O¤-H¡Ïë³ÎªÆiK¹)“êô5þX63‚ KDpæ3sËâèJÞK Ns“;mD ”` c\ÓOêÒ€ž8]*Óa|Q ‡³Ûé•ý‚#$ØŒ't¨]'Þ†ß]|ÿø ™ø~`ê²;¨þ$Û$Ù’$;Úz¥bÙPZxÄáj$@îj Òu¨Ý1ø;£Ÿ19×8Ì‘½X‰2ç”ãô‰Ÿ29âõÓ;o÷ýüÛÜø䕨^¡,àÛÕ¬Œžh¨%P øgÓ“}?ÿQwSÒú “~ñ¶š‰NœŠ`¼Ø¶F‰š‚M*=uþ_2‘‚AP²±t–H¥Àž]ꢋÈzHšƒZ¥·n™7§ØU=ÏY-ÅJ!Û‰“7Øk¤Y«ÀT9Ë–úÊæÙ«çØªæÚ¨9²+Ýt«ÜõJ´à%N­•Ý S\x±:$H\+Ýõô»ÊåÉËtg¡˜^™tcƒÿk©¡µÂè{…Øõ ‚Ÿ|›·mwM¢oÚ^Qü:ÍA  çev«M½n÷wo]zúmÅȃFˆ‘çæ®‰'Û~{P°ôâòà“Rjôº|µ7?ðÂ&CcR™øü^Ùàc8½ Ôµrz¦hÉ@_Îè`u¦šhA÷Ëù«/†zÎhp=¡ùÕÝóÌH/óGca(XVÅ®š¨«ÙÊ%öZµNœrÕ,k ðÕ‘NShøñ"”'ÝZ#ÍA‰TÇEÜm6~¦Ñ&¬æKqÃ?2×:>PŽ„#\X„­,a‰ßRrãX-j j‘¸¿f¼³Ïk˜)t#·R÷½æ'j-÷û·fï×=ýû¥†Ó¶ù1ìêu¼ÊÕüô`v˜)ˆ·Ÿü£¤ª GšI3ÚaÃ7­ûJ¶˜$ú«f‡ë¡ÿÉãG‹£M³B O¹jì0—X«Gµiyñâ56²'bLrø4e¯·çøOÏ{ücÅàƒ†ÉÁ@úh™úǧFšžý™Ï,\]ºWz1X)Û]1Ùˆä˜È—Ú²³EI¦Š Š™Îì2.n¡ v)’¾-•óíá%®‹3 žã9Ö96øÿŸ2e—Dº'XÄšËì eòö¹§Êg¦[aÿ‚iàË1`Âݘ<–í4ªˆÕïˆ3­òÕhô×iÔoñÓ©õf×ø³›£øÝMë#ÙàúÖ„«•)zÉ`H__{L”X%îJÔœß]½ÔUñCå.Ôå¾ÜV§ÈŒ],b×¹páÛäÍ­rQƒCK°nS°nc^C·.@¯:@¾.~ÜbóB¥3ís&Úë´¥}´HCÞæ?ï•CË6?Q57QÌÌU2àiL_¾rÜ Æ¤ivйl,ì‘4‘rŽ­zž½& l²×ÄëÍBG27^¹;×$ÏV#Í\6ßaªÙ¶¬tKÉl[98¸)”`Øä«\ \¬\…ªTÊ*U¬ˆ`WFj×DêÔEi7Fj5¬Òh SoVíŽÑkÔªðWÌ÷”-PAzÛ&Óæ&={‹£x)~ìDoÎ 7=üÄWj´¥&Üÿôsñã¾¥Ùed  Ä,2K/þKÍ2È{´°{ò™œ¡Æþüùû×{/§?åk£AÖÀ›N•lW&È…DÞq9¡GÎð1éVHµ€¨v–n¹·iž/ÃF+˓묛f¯yÑRé¼+ÁT Yoi6œ &а9<Ù@ò¼±,`=ÄÏífÑ$¶ÒWv¯BNêI¦:óâmÕbÍ$;râPj@$û¿K õ‚CÁФ2Ô„"Ïç \çÆºÄj}Sð۹ߖùåG©ßÿ5ó׿ç?û®rüÏæ¥þž£Wp:ƒ×ÄøTÇ“þJ†¹ýü)ü(ºçÁäêj|ˆÜ?WR’àÿô^9ò£G‡ëТ/-µ2K-8ÅLbû#g¢Î™›Ì’ÜßYg@ÂÙdk Â7&#÷gêË¢3Žˆž)pãdX«§ZªæÚj•;é;kf;)¥;)'‹”S,Õ‘sZÈ:.T8f¡vD¤µÇ’­'ã¢@vzé}ÐzföIçÜ`ÓÔõ4¦·èHÂ# s_ i¹ôLG-ØdMV,¿h~¹Ô—õåѪáß275ðEßos#¹_ý¼cÛÅP¹Ã–ä¤-Ù HœCzRW1#×1ȇ vÔ|]²«c‡Ë%t,Šé>Ú(/Y¼ƒR<9GT¶™“PØk 7J…øâY`Bv¯åŸ‰w®)\÷úéŸ?-ú½i¡Ñi7^Š{^Ž÷â1?Ù59Ñ*ožjBz ¬Aqy?¬Ûá!bhàd^lË)²Ó®ô6Ëvæ!p3Å™Ïót;<¶. å.š(§‹´³¬ØÉE˜=Ê;Ãc6R=f¬yT¤É!ÎÊäø:Á“`VÖ†Ä7pŸh:ùH-3Ð8ñUÎùÖ97©47ÅlÅ\+@©À1ã@—´*Ó•Uì«Rè-Ÿç.M90HÏ¡Ïz,Øga•º)å;ÈàïÕ ˜äY5‰“ ä¸<9 o Ø*4<˜ |2÷"£÷³üÊ’£÷.´²w5‡µ[S6@Ž’ÞÚOî×à ²Ù¹‰ÒœžÆê˜gI~*g,d’Ì•ƒ…D„ÛžæI^Ð ÊWûéXjÄ6Dº ’nÎH$I$K!= Ñ.Z…®œ'5¤~$[ÉâÆCPŠ­d2:,‰$‰‹Ž’pUMu”Dë…º 1#LzÑáSŸ1!ñ†ä(0C;ÉsNr'dSõÎzqÎúèïwÒZmÄrÃ' ñ~ðÉGƒ¿ý>+¦bÔØF,ÒØÊ…Å³ÿîþ„ݨ5y/Ò¨å©éù¡þ—ý?ûðÝâ´œØÀcvüsvzù¾fµá™vúù.FEî&yÎF6¼‹–:g5OðUŽó”÷sdö²¥qåO«ž‚»™úyKv’HÑx4ÏLã´¡üI¾ôy Ï/déÖáuVê†ø<g”8ë4ø›^‰t)t6Èòñ Õ'¹‡<ÿø¤pâ„~Tô´‚ÕP­ß?ºšZ¸µ Ô ÎÀ\l:@ÇP ÍÔN5LÍw œ¶ùãO'Z'Çz°f‡{70ó¼kv ‰™0žj¿ÿ¸páEïôDè4‹ØtóoÞL I]ËÝá®;…Ƭµâû­3#ÝxÃÐ@%xt8ȬŒÂ{˜‘®ù‡Í0¡b†ßdÜʵÙç pp-÷‹S™ùwúÿ–›¢í©}B }-.*ª®ÌY¾ü9#ÙSf2§E ¼;o­sÖ†sT¤¶G(¿‘O¢t¬•‰¸%¯¹ÿöQÑBçìpæé˜ìÃÚßó¿”jW êK ¸#ãÅð¼~žGÃAf[*.Ú¯ó"yg=¿ü žZÔðyÝ× ›zÛuî¶©e¯1~¯hÛ¯¯'_ÍŽ);d[¾ÝªeÓåužï\ýîá˜êh›7Î9o Áü( åÓÉ{€€ÄØKÆÇð‹Ï´•núðÃK_ß+yø¤±ï˜Õ #=KCÌ‚%×ìóúÉþšñá:Àìâ™æáù–±¹Öþ'%8@}Þ¾;=’“篃t!xõ§™iç)îÑ–ÚóL¾ÌQ!딩B‚©B²¹jª¥n†?C¤{ÑDýœë Oîœ@å¼;Íݼ(Ê=1X„Ìß“„_ÝM~Z·8ÓÞÿ4ol ˆv5K7™ûµu¬·Ywl²¿›ôµŽ¯<ºá¸ØfZ*,æÃ8~Œ5Áºuaº «ô›#xX­«x »(”¹«Vº«U¸©V¸œT¯qÑ­s2©²5-˜êä 5rLÔ E¥ö:eNz|©p3ªò4¯ö²¬ò¶­ô´+÷¶Ëö0Í÷³.õvH´¬Ö‘ÞéÍ{ÿFòÈ`\…Ç†Š§ÇJaļ¼ÎLßIôѨµ¸h®™bÁ¹d®uA rš/wš/sÞH6É„Uécˆý‚U즗n¥’j©R袇Lêÿ¬† «¶UÑÎ-«ìëB¬Ú¢ZV;4GÛѵڦiµMóëÆµ¢†5õk„1¦A(ë1‚®µÆÝ±ÂîX“+›­¯lwèÜêX¿Áº|­UQŒ(UR¨Yö·ÔX÷XKv êŽ@×Ú´¤þ_šèXœ¦é”š¥ÿ«Á{ççgQdð¯@^XXZœ›{ôý7ƒß}õðý;o¤®;ë$óüÝŸ/gæmrCÞô[ÅÄýÎ?}”õ½¿4ÇŸóֺ䬓d§{ÚLû¨!û„笱Ö¡ú ‘ú {Îök¦ìí¦ªqÙ(.ñ×&žâŽiµ“üÑ ¦o´Æü Åt3ÿ>õåÒ¡ÿh zèOL¡º¢£ƒ:lp8oz¾<Þ™é:(ÂsöQ϶3â<á¤ÇƒŸòtýËLt\±v¯9yG¶j“H5²ÅŒì´SXÅ#Úd#ð}Ù²d úX6y)<ÈÙÄ-«Ä¡MFƒ®7½öõÝKý?V÷µ?îošéŸë·Ž5-Œ·¿DÓóКÍã8S õètÃàTÝ z-0ï½Y¶¡(΢4Ì ínŠ…N¢Pï¬9÷€½ÛPm‰f¼ç¤H;ѽ ?ÑŒ›(Ô¿h¦—&Ò˶ãá—ï.Ìó´¼`g°Ó@Ѯ’$c¯ËÐÏõK£=COË_‚á?ßD‡ò£íß_=°Ë†làÚB£y>¬LÍ~å|@‹ìFà¬*5Þ§A(ƒ±$º.šjÚ·§#O$ d2ÛB*Ý„j’ûKY| 9Bv‘ˆSh­•Ø5‘jŽH)ÓRh­Š\¹%Æ%î¦N¦Y6‚kƒ v¼Lä`:[3ÓT!{CÌþòfæ4/“íi+C³Ju÷O{wÂnÎA?ÁL÷‚@'A¨•l®a«ŸíÀËsÒÏsÒ]Ù/ƨ8]¦Z+‚éšãÈ®òž5dá+œæ±NêËÇkIÂEp«,Ù(Mâ/ŽÿµÖ˺äÉN6ÙóI$YÃ- úštÔ¢ Ø.m²C6h‘Õˆ3V'!ÄK™†ŸZËC0µ¼î´Ö?ý釅‰±éñ±…t2ˆuZ&¼wÐ…)¯æßüÓo¬¼D¾W\>šà=7G‰ÅüvûZë±}GìMcaÂ,G‰‘$kdhpòÁå$bå%# ®ÄŠ’"A„®@BcÄñ"RЬ•"1ø'ø>eW’ˆñéÐø(ÈÁ6?ÁXå0(C g¢¡L_¦ÒÉ(×J¿"Ð&3Ør£…üZ{Ù7±Ý—hÂI2c”TƒƒQ€±p’A©¡adÃô+ñ+¥¦úñxù31B“Ë\¼ ß´ÃAÎé84n#Õ cu‹+žr„ƒiz5;Û™ƒÜä¡næqorˆvš7{³ñP ÁäZÑNñ½&dõ2ÌûÌü ĺ1 ·˜™Û ßW]MŽÚm#»jhž$¢p‚äˆ Ì¢ã¬ú¦ŽYþ¼3+.΄â ÈDχ‚8õh,äª+( ¬*å*G¬1— vª$TD®æ¦um-‰ûëísý6LtŒöW#ºÖ[â¡|:‡¢f¨µò•šŠ™éš™ùÆ1BñµÆÜýã½Ä¤­Fî’Ÿzÿ—Zä# ?Ä ñ;·Ï•®ãµn¶€(•Uv„mæjg¬ìǬ › †»õYë‘XÁ!…ÃÐo{ çàzãŒs~µ¥±ïÜ:ùó7…Æž·ÒFq¤íY_å ÕÇÅÃh`[5-M4̘¥WTÈ`a €íðP5óòÎ\_g¬ñQ$±J$NŽlGôĬ1ܺÔß4ÛçeQf¨C‚¿M¼«ÙN;A_ÃKOm­“Õ‘ 1]%O~ùitðùø˜xjfrvµf™žž–T ¬f•çßø}ÂÞp ÞÑ%Òçðnðqæ§æ¦úGýýáûïÝÉNK_åÈ’Ôœ{ÐHë”ÿ¬•Ñ1îaþAC½jJ¨-Á„øÁ*»I’D*Ê­UWÞ¢ÇÞgĽ 2;kbtÖ(}ô6‰·Õ+t7Àêœ@æ¬JM–•:Â:q§z‡$Ù¦DÖª79b!Ošr6ˆ¶àÈ€3 ÐTÔŠ…Ž6NŽ7™ÁÓaÏñ¡j46xƒxº²®ršé˜YjÅÐs±¯jñQ9¬$à-ƒèR̈)7Z>9X0öÖ,k˜k˜ª¥1|#u0ñ@ $ ‚ÓðP‘x¤/ ã€ue2X½,®Y7QPkþýCa:HW?(PÚ¦,¹_S}EØ“W”)‰E¹UÖvmõCz –çŒÎôpÿ$šë$Z"ßÖ ÛÃ|¿¡Ê*%â%K»š’ãþÏ¿«B žG𲃑)ÂvU^Î\ɪ‰±MŽ®öÉ‘]Òd‹Ù$M6È“Í ´|-|MŽþíqMÉSi ª’#Êtæp&Õ@:S “e,›-”É6‘G‘¹À“…†Ïi} ¶ä³Ô²Óé¡—çÎÜ”jÇKDKo¬s„ÇÙ¯§¶Û@娵þn=•í\5Erf‹Çç+›¼ñøIžný…p/g^¼½ô{g¼­F<_=ÉÂøœ€wÎP7ÉD?Ù\÷¢9;ÕZ+ÛQ;Ñ\ᬱ8´‰fRé¶ŠvJxq‚K6J‘ ôãlG#оE’¬“ u´x¾ãA¿^–l%±2´nÇHÐ>aÙ(Cß¼MŽ<Ù±òâ ¶$ZMj1JÄOšÆ^8Iwù(KÓ->µ©—>¾}󷯿î.Ï.ÍaÍ¿X@¾ áÖ™_j€ ¿:=¡ÂÌ/¿@;Cùf¶» õå|_ÿð×_¾™›qÔÙ*BŽDKÓ°Vš~Ï«¾‰íš*[ÔU¢¥¥VˈIÐ~æŸ åkd¥ðÙQ¥7੄·M’ä (mÊåîFåî•>Æ87Õy—9éZkç³/ò”/ÕÎ;èræ€Ç[|&è‹»ÉÈ©aÅèµöÅ“V› êçðê…RƒÝgoT›§C…PøÒ¡çX3P7û´b| zRLÙÅhЧ«¦pÚ*›-]±|Y ç)fþö½›§ÓW¤øé·V>i¦§Kªwx1?^eßyôvæ%›N­ÖÚîJöyÊ®’²ÃLzµæÊƒXŠ„(kBDÊ$?y]ßýKC6B;Ê"Ñ2´ŸYE$£‰t‘À#;L铲¦xa¡s$æ“›Ù¿´‹Ÿ´ =m¢adÓ3ý5ýCå ÍD÷¥OÏR3a4rhêPTñañÁ±ÐÞ¼œæ -•‹ ȽÎ<ºÖs:x›¤;4—mûá›â¡!´:wýNnÄx·ÞMNeþœ qálIL»ÂÙÒkÍÕvE˜žØçÞP¼ûÝkÉ_ÜÍ~ômý|ßµÅËS[gûÛâ6;Ö:6R?4R½'NF(æ Us¹Wá»B÷… Ýäd ýоq¼v…‹Ãu/†™ÐwáŸóþˇ71· W£Ò‰-Š’»”é¦U%ÒxTù¬<°^u5k¥%×JЖ&mÙ¸rÿ ç?k¬žç-:m§À–É'µçBïÿ=ÎÆˆ„.žŸ,c LXºòëµ5ÛŒ;¶Zµ€ nѳÊîJ”So”K{„cÓ*;´4õ-á. ~ö宿%ÎÆ¥N‚bG~žH+ÓL%ÏJ£ØŽ ¿Ü‰Sf§IÃd-r,‘ù˜$€lV¹Õ˜eØ('šÉÕ'ñºô ²K•leѼ^Šnyìt´úѬ¢›U"¶’pmuþô¯…âÉýCõÀÓž?+d÷õ>ÿKñ)l.l¬8t5xàJQZÈ! „›+”yVxTzñk} êýŒj¼ù¥.ÚKå;Ë‚”L+ÇLnª%碩æ% L^¶½ ÀŬÔÓªÄÛºÈÓ"ßÍ4LK/Ór_“š³ÆQ[˜u{¸M[˜Ms¨mÃ*§ÚHü`÷ö&aÕP.çttD[zê»­-»qS|ÿþÌðÐÄððÒÂ"Œ:§æfQa°–h ¯Ì•`îùÒ÷h³ƒƒÓÊÂ)‹.Ô™ù—ˆ¸¤C©—óóƒ¿ÿ2üÕ§•çf†zúÛ×8–¸ØdZš$pÏpuÎðÏåqèhïÖPÇ×C:zµ´°â9ZÔTwÈHŸÔRKèZ™äY¦i%sÕ“ô”“tXyB­|Sv ;ÏX#Ç@)UGæ‚9£Dâ¥(iC¨x#éS®Z«$Êšüíúà$3PB¶Aõ3#¦tPÄ?!–JÇ+XÈ0ö EJÅ•xÈÎÕÐ`XŽ¡ ª1×:>X & ÎMسâÒ! Û³<ÞÅ öõW8æ*¿Ç”\;zÆ^锹DY ·aea ?Í›“n\e™lœbrÜS·½fœHe•‘”—qÑ$ÎúÄCB‚oß<'~|ÿå þ!+­#Fš‡uÔŽë°i±ª«îSWÙ£©ºMM>R‰8*5.ºö\û£gè:È´ÅÏ‚P75Y3Ž´©é:öö eŒLæ¼`j1dŸ™¨šŸªÁü‚ÐO¬èÙg/!ó¨›yT?øÎ…wÒCqDJñfnÚê%u¹aÛÇ•OŸãô„T讬µºHý–@£3\²“CÖJøÍA¼Ü„ÕuE[?û ý‡O³Þ«{Ü2?ص8ÜE‘®þàôì³²þ݉éþà`,4?X Ùà …ÿÔ Dš‹sSïñ·»Y‰gû9R;¯É“פÈVT]BvÈÐÑÛv²Yž¬Ã™T–ž4ѽl…TDA„¢ÝxX1e‰d]å4¾jŽ€]*âÖ:×;7:79 š ö¼jK­c•|¾\޾t<¨û€æºprƒìå·:IÝ(Ýòü‡ÊÙÁÎåé+‹`šM6Ct< C WRBð+œ•šCô¨Bó#4 V6™\Ù(5˜Pô?Îe^t0LW__áðhõÒbw?ˆ4ÌÛóvûHŸT:ê¡Â|ß{Ì^¡Ø_¯I»f•Á‚/n²³Öi{öa­BV˜.EÌl”ˆ›x›‘M!Ü#¯ÙU×m¬¨[ûÝ—¹ÌÄ[ÌÐÝFd3›Â ¸\»VÖvB3w+m,É"Nx†}ývîËÑÛ/@n™í˜1Î8¢$+&FKRoœ´ ùƒCÏSgÆJ—ay &ÆT#â혱6¦¿‰yPû俉¿æF×n5É QÏ TOñRŒ7!QšdOâ·Žô571Ì ”šŒpe Y*ìÕÓŒåN˜²RW[µçïxçVÊ_–=xÔúìQ-¼È&0‰{1ÙþbÍ^ÓâxýÈ3Huj^ÎbÊßùb¡ 4ž™©,jî—0œI§êæa³9p©»žºÇÏßdæn3cW™É[@Ú¿ýLê#@Ž (®“[9¼xQ£·â›%ÈvœpºYy±³cE²S–þÉ5ùD}ÅtCE„8WX!e•Wç*¨÷±Méí6TÓ£!1ìÙ%ó-cãå°e@ö/±¦†:«ÕaYï._b#O¤lK‰4s©T+9@²UóœÔ/™I@€mMý=J\Ô±2Eò`ª”¹iN Kü6ÇV)yö¬b6¡°ò]Ô³”ñŸºh.7RE ¨Br't¥àsá`"I^“!»Â(“`EbDÈéÝN_~^ ž¹úp¸ö§ûÙ/—{û~ÌeFo\²s;‚ôidö 5XÒø*9ÍR+½Z£z'£F'ƒfÃÃFG^•§T¨Œý’Í“)³Ò,µÖ¬qÒkõ6ÆjpãƒðÓäÉï2kõ1hp׫rÔ,°”õ d“q냴ë4j}•ky044 ‹a²777‡ãêÌ«_ÿ³ŠüG¿CKC»Ÿ•_^¾@t ~!•°ðÌÌÀwß|ÑÙØ~hG¦· ÜÅŽzù6ÊÐmåÙ)ç:ªå¹°sÝ9¹º9^z—5ÓÙ©v)Öª¶jùNœWm:¿ö2®÷4.wÔÏ2UN5½Ä“LF8©6¹¤G]5 Ld Me M$±J-dk­ëÌ•ó Ø×¥”ƒºd½ À8Xù×» 3ë0Ý Npø.ž„º+q«¯¬•‡69¯^P—< ¾âJ,4ö°œ¯šŸ«Ã‰i`0bpÀ•ÌtØSÔ–³¯;ÿ°]|{•Ę‘Oj1ß^.Œµ©Š±)äåŸwçl6ñc%j:("›ƒ9§vÙ§EÝî>ñÕG™?|™þì’…ç­ÌøëÌð[9áü†(›\[íb^™¿Ø‚—oªW`É+´3ʶ៴Ñ2’8¶ÓùÛ ÁX?À¬l¨/Ójˆ©Qs0ž€…ûº—y˜¡¨Cu… š=ü DEöª¶Ê‹ÄÝ^ì¨R梊UâªZá­Qê­¾²TK½U‹<se3ì$O!åÇä[éG[ œµs8Ù¶ÚÉ&š)¶Ü3özÛ-ájuÜëûïŠÅ3=ÓM‹Ìeœ¡¦W2Û†ßɺè­]è'¨ñþë~Àð’¾Äÿk¿˜H¿Ú/•6ŠYB LÇ  ;ª¹"]gµHˆ‘f²EÖ `Š‚aØè­uy•àÎ:«7ãœÚÂAÄ5«õå•»jÛÊ勤rE ä(xîá±Ñ {}Ð^'~ðËøàã‰ñáÙÙéùù…ä ‡ð¶Zñ þ Ì¿ (0‚éyk™.ཆÍÔÐ03>>ñÓŸ7ÕôÄïjŽ ìŒro DJ Nµ·^­?¯Ò—[⥗뮓ƒåŽh§‘h£vÁR霩|‚ë¢9+ÙŒ•l"p®¥J²ÄtÐcbbÓÎùÊ'y°¬IUØÊÔ9+vøjÝæe}Í‹ßéÎmðÐFÔ`Ž·f¢·Ò©…÷«7‰¿+˜ï«{Tˆ­·0 }e>¤ƒZó/‹öXÐ?â+˜üÔafeMÑ¢Ðcv©zl¼ g¨e1èë½Ìó­´]¾+ؤ ÒHåÙ°ûwK˜GoŸ 4<îȆ£øØˆ¦Šä’},$ÑNR;#µÏî·«-^÷î­Ó°Š;Ößµ4y  ³¼ü½iù³ªwÕúÕ¶ùŽúy"Ý\st#vW%Ñ@-Q¨yÚH}‡,„™ˆføñ ð+zpâÀaäÅ,웇çQ iùæ¹Ç• Ïp"€°cÜŽåﲿ®ßÐoU¿É c‹ùåMVk,^ßáùúN¿ÞÍ^eaÖ§8›ùÒÑ:Ó™„÷¨{ú¼}|ê*³xç壶x+’m«Xç¤_bÍ9Ä“:î¡s§rÿÐï]ã—‡úZA·ìŒÄš}9)kùèh̦p Ã‘ÕVÏp!ƒurîÄO ™N‘ñ:ÚeM£ËBDB#ó¸îç+û›ŽÚd®Ö¬ßnҽϮ}§¨m«Eç«¶5¦áÂj~™—n™»>XU%NúEºùV:Ù"bëS„ªéBµt3 õt¡F¡7Ï”“Ê“=È Îz¸y$ÏêIÅsd’œwX¨ïõÕÿò­Lñ@îëúG®Žô]_îCÿð¢~š£€ÜcŠ¤Ì‚]!âTÚRºo±•v®©z¦¡ž}ÖªÅêx€BþŒ¯x]`«–g­”­j°Æ«‚Sä \`§˜f!èjäh§YKÃ5³>T·!L¯.D¯-JØiҼʤ1TXdZd^dQ$*õµ(°Iñ3Ûç¬cÕŠŒ˜_¡a|ú«&fZû•PõÇÀÕ/ª$xpŠ„¹¶ìsÕd¾Ì«ýí"ä`’@÷ î—"‘t¹µL­“B»»7„w#Ê´'LÐê¯_ï©UëŽ(öƒËá¦WV™Ýб¾mŽ¿jðâ€#Ýä£ÂsS MµU©‹Q®m­L2t†` &XjîЗ_ËSm:ï½ÛÃ~¸?;7†¶Öyÿ¬x_¨6ÿü“ÿüÅ?¢Zèã¡ nZsÌÂÜòøø‹Á?Þ{§|Ïö½&º{´¤÷+’Ý’d—yM–ª-€]ã¬@“J, Ük$è €x€ïÐoE',Ip^Ø!AvIRÝ}‚>)±WoöáÁŶÎM½ÑK£ÉSQïM^ªZWƒ¹=îúκ]Þ†×#D—cl:7ØVǘq”ÈÝ,¸wëÓß ÍãòsçêF§+F‹Æ¦*1”¡_éªÀâëØ¥´¡ª`póÊn*Z:ºš¨œ[®ùíAÂŒà^¬ðc§ÞBAH ÖÝŒ´  ]3hüßí<;þëefî«p†y´2ñư4cWéeókªŠûø/)ß|›ûçƒÊg}µý}uƒýuÃÍÓ£0Ôº<ý¼söxþ¿ø®+B“„Ë’ÕÒ$vÞC!uŒ>Ã刋(ô !÷ïÕ.N]â:9¼·v~UY-/Ÿ¶0C½ÌÜ[t¨ôGåÀ» —;ß<àvuÓÕÝÎoò{ÿdÄAmqΧ¬”Ž˜³^Ho1”Ü` k(× 3$uëŸßïxü þü×0阾W}nT’'`A¼E…Ä€‚cøùDfâæÄ lë°²*!$‡.,öç}´“Ÿü‹©& dsÈ¢(úfâ*3~ƒ½Š=õeÉgõ»ùwÚK13D¯ ”_"ïìVPgÁiÁ±wH”ˆÄ)PLx#f +×ã]€¢¸2Ìñ'xÛÚ•iÂNBö²£²Ôe®ÂQÎëšæ{˜$¸í´ÖØâªùñí”ñ‘«Ožµ/.~Ì,~ÍŒ}Æüpk§Ô^M’ÄS,é¦jËS¡f øODŒ5‹œV¤Œ_̘RŒ¤²ÌXØwˆ—Mâá·Ø€Øé&ry"È¥’_ù¨pq”[!´kÚä‚4aHòØX¥®Ü<;NŠ©Êi]iÞ÷€*,C¶ÉÐùõŒù »S!æò$ïBÄýŸ›‡‡¯,-¿Ùß.Ðefì63õ×`:ßÇØLþ±eö­ì—Rv£âÓjM$ó6yiÑå©Ñì¥Ñ ÓƃԢ=ØçkDomr~k“ëõ›Ž`“b;µw]XUºpª\µa UïÅ-qÐɱ2J3$ ´/è²€çœP'Ç5È-²W[jƒ¦TœPû£†ò¾o>~üËèè“ef–šÌüK©yUXþ¿òŸZ´Pg^•$íb!Ú1 ˆÝ}úè‹ÞŽc~nÉÐàaœ aÜ¡ˆCºëOsà¦Lñ|̦Œð‰ß«çAæAÒ -2´€¯, Ó8x¾œGäwÍäÓ²ù>Øt´23= äÌó˃w3®&Ÿ TGz×&@Íä€19l@90û•éÔ&*û 9àŽ€;'³OOi¿®òv5YÜT˜ÑlU”ßÃÖļ{Ka´ ¾¾¦©qHO÷˜cßlBSÍRs +õ&Wž !œZBDÕÁÖà‡kµ"é¯]Xër¶l¢Yñ›ÌÌgÌøgók=j¡@oHüO›NކYÃ6 VÙÆ¬bºÜh`9$ÕHþ"OÎKÀWaÂí'‘ñP8êÐxkH‰aÙ„‚mìI¨r´é¤;Ä?Uú‘- À+piŸ"Á‚d›åU©‰•¯†Dk71“!•Ék'žÝd?œ¹ÆÌ¾Á,ÿ…y{þÓüKÂl =MÓͱ_tÐÚèÕ¸ :ƒ­{Âl;C,;LÚ¨9;ºM^ÚØ2Ø/85øèT¹±‹í•Œ‹’RïůpÖαPÎ*fš(Á¯Ÿ+AOâ•51÷˜jm“¤|,”ñd' 29ª<³/TSþ󎆑_¿Ÿí[X‡¼‰²1#ZùõŸÔ“ó´B´úGW³°ÒÒÌ¢Ô,ŒŽÌ>}8ðùÇŽg„{V­ò(÷4ƒœÞG­|'0³uÓtSíõR¸©Ž§M5ŽñÏ 5Ò­¹¹ŽF¥Î¦•Î ö­¾.íþ..Ý!.mþ6uB 7Tüb¥Zl£Rf¯^î ^b«’o¥tIÈʰTÃÏn¿*9Ćs 9m¡´MŸ„«’^Êï7Xèë¹ÊàHóøBÏh¨SµÿZj†gʰPjüˆüGXf¤‡>º Œ6½ÇD¸·ï7ÞcžÞ„­ôf-rÒ‘½ÓFÙY“¬öP½Út¬ÿA÷üÈÍé'xÏPõcÞ\ÐÛn¦ï~ö×꥾ק‡® vˆ':Ô N4L͵-̵Qädiž{ÍuæÏ¦‰»©7Ny·m·O‡ç;ÇA;Ï›e£šÄyMX.$:ò@ˆÝ&dÅX)¼Û~nê·fð63ý3u›™¸³ü[óÐGÅoänÍÝ䘿Á©n_Ív÷ìÓ‹^ܳ–z§M¹{ô”£‰—$q•%n éCâƒÆ,ΰ$%èzÃöÏß:Ó÷cé<ÔŽCmsÓã3­pÆŒü—º5 ô H‰‰L‘…b¶+ûŒ‡Ú^¹”íFŸvî]|‚øïjŒ–°Æ &«¡6XC¤Ò®Þ("È/3ãW˜ß«ß:ñ·’5¹Ñì̵$O™6$fPæP Ë乩Uú馚Iã'{gÓÛ1Î=>¦­€sô $S­”Á}-ñ0º +Lfõsl Œ5NqYªéVº9öü|'ÃRã GA“»Y‡y§¯iO Éå`³vaÿv›: W yl^ü Šæ ;O>.Çœëÿáí=Àã¬Îìñozï½÷ÑŒF½WËÝÆØ€¦Ü»$«÷Þ{ïŲåÆ!lØ6! é¡p¯²e¹{~çŽ !»löÿ߇¬žû|Œd!éÞ¹÷½o9ç¼ÿ |"+µ(M´^ƒ214ŠYå "÷¯+N‰T „þÕyø:Py:F64aèÄ0–Y8øâlYCðy‚…p:£íÑrÔªÀÆŒcPÎÁÉ‚ÄP©“¹ H£ŒÌw.ð Ïõö%»;b­ ÑÆêsYŠeg¤üY/çÉî/öú¯¼í?€ÖÓšúUßñ—+Úöµ-õtÌó tÕëJŠžHKÜ3/arAÂþE ó"Ý]Ñ&ä¯ aˆÅ÷®(9ÎKKˆG¦ÊÍ ÜB–ú?Œb'WC4†*^€;€Uy!°ŠZË¡¶àº‘”Àš7±3òGUì…rÞ¯î9ó§ßÞ˜:wÇcú64¯H…bCþ?þªÛ„Ü Ãu÷&iÔrû:éwysæÞµËSŸüÙOßêÍØ—Õ´8€ð´ °JÛãu05µ‘ÊÊ0y‰O‘ï‘e»¥ ¥lÖ0ŸPëÑ`]Åó}“˜À¢²¤üÐ2–·µÑi©¶kŠt €ÃÒZà'´ÁuÃ%/à=‘Þa¤+žc㼔Ê7[¨§õÔ\UµÚûù;ÍÀx€2|óúá3gÞ@ºLÃ@ôpfàÏÀ½™æ Ùà>`-ÀL¼ í…}„T8}Ìû'Ÿ|œ³ \Ù²(tGŒ.VHÍa=÷ú±’[3¯Ÿú¨ŒWo|ÒÕ±-¤j¹«jiðdæòÏßh¹ûù —>;ýy?T+!rçÎ>"çrUò½è–ëÿ¸ÿίš¡.×½Ò4òhPUœ°± kUlåT»$zn¶’½UL[‡[OÎ\¯b¯R iTáÒðSoúÏýÄñÇþÏ|t¬ü@ñý›S$¢Å m¦0‰}@NÍgRóhÔ (y¼T-‹_1\j¾…zfž.ãéàΪåû†Ÿ}çíüþÒòå]'NuEP´„ºoj†ôìößÛ÷›½YôÃ'}‡çZ^}0|ü¡ÐÖÝùKµÏ'л7…]~¯éö™¬* â¸ihôŸ™ð_<è?>vòµüW Ó:5t€ ü¤{ßjב5!/?và1÷àbMß\ÅèãÁ‡<ß2æûÇ[¹g®u(Z}`®ã…žžåpªÒ+È࣠#LGQb—Ù¥ Ú.µìE z8 Ø?Ù F¹QØä–7{¤ÍnQ½ƒS9ôÚS«ÙÔCLêßJñŸ}ùÚÏÊ?>¶¡y£êyt˜K5= ê¼_©–Vè_Ya;´Èpìï+«B¯ |È;´ÄÞ=×О¢mO4ŒÍ n7c4‚¡ç5û´¸ ½qÖŽH]{„Þu¢©?Ñ€1bèMÑ Ï7-²Í×÷¤ÈûÓT“Ëí¯¬áá`ŒC…î_áÛ³Ôƒ3˜æìNq¶Ï ‚bØ&'o[˜¬ê°éŸMø/ýü¿÷­[ý°›zÒEmò2yÓÄcb§ˆ¹K&(Õkë¶·­Þ…ó¢+Ô p^ï݈bÒ j3Ú‚ãÿ$à–l…¢…š*0ÀõâTaæÃrªh02kp¾ Ø‹N­…RŸÚ¥¡ZÙu!’žÍØ<Óäd{÷>Õ½427Þót°íÃï;õûß]>{0à«Àè}›ùßY0¦Ð&*PìÆ1nÎ ÷MO½}åÜÅÿðîÁÑÞO¬R=§a‚Ä”cf(YëÔ³lŠ>Ť§V0˜@-"EC§!KƒÚ.Ö„ÏŒÿÊ„™|V:ŸÚÊ¢6Ј+ Vl‘–VeãÖ¹øõnA½.ü’`nE¤´>VÕ«®TA £Ê+F’y—ƒ¾ÃCÍSìË›÷ÙÖÏ—\;Ñ䨕ó­(C:æÖõþ™«Ýh2{cšˆ*\<Õ‹šÎ¤Ý¦ŽøÏ¾­ÿÚ;×~wàPéS…sìe ö¢*\â{0Z\]úÀÏ~ÙqñÚ+Ç¿D³d8¯ÿ¹r"Ë·ocRQ¼²~™ãý‘7ÞïŸùkßÓc €“„íÕ=„ÀxvÏ©7‹å¦T/‘•&‰ŠCÅÙV6dKQë$I-\+ U6P´õcƒ³ŽÉ_Ãä=É䬢X+èÔ»uE7Þ:pü@Ký范þ°‘zPG­ÔQÏ8¥›C ›CMklrè€b¿ŠŠf™OüÜ<{ÉšäÝÕOþô…ÂÚ|æÏ§?é;w¢ïüÅÁ3Ó½'gºÏt|1 #Ó‰6y§/ÔݽÙã?Ýôñ‹Ïüû®¸wׯ¾8ÇttihU„|›—“=G‹Vh[¢9‡K—]úèG!szçÄðî}:ê¿pø?FÖîËJêzÜ6ñ\Ø«[’Ž®‰:üDøëÏÆ~À~¶e©éØ£Áo>2¥÷/± $¨àíO.qM,pô%èq믖•»äÌM85¸[qppÙ‰uxäP¾÷ß@Ëí<ûd‰¨t {±¯hT‡Û'CCÛÝkº—3çŽ<ÍÝ¿Q~,Ãòv¦û­MÖ7Ÿ6þð ÓÛÛô¸ûèJGOŠ´2’]'®JTEŠóB„Å!©€«`lòªŠJ»¼9ÔÐcõÆ;“]có<{‡ì¿/tÿ2ß¾û‚÷.q-²"ê_d}iUèkÏD¿üDØþì{–™¡Ú7Œ´É}øÔ´Âð“%5>ØLÏKÀè>)£g‡[Š’Üë‚$K…ÔJe}LL­SëpA®“¢mg²fÏ ö ÎK†€ñ÷óÂùûyÎ'’KXpÕ›ByíQÒŽ(`¥M!b¸jÕ.(¤—›C”íÚîã@¢ …­dËževôãxíéàןò¼ñ”çOùŽ­=ðhxﲈuvéê óû/üõÃS7¾8yn&Î 85ÿùñmèøÚu¨}‚’€®s~B˜ºç¿ Ç ãÆÍ«wn^™9sü㟾ùJciÞü˜G´ÇøÔó°s „   y`¤‚rÄó)²OuŸ§hh´ .+‚Ãl*K@Ï—² •(%0À¾/P¡”ÀlÂav8Ù€tñØ<ËØÛè}ÖñŶ‘yDAº?^Ý«êU^Ò'J‡vb땼ä³oäù?$2Ú—ï^è¹~ªíògõ>­ú¢é&ø/—GNÿ¥‰à:îüàî™—ü×Þößy»*Ö$­‰P¯2òð.#™GQ)dR^ðÀ{¿¸póõ_þ¹ÚK_´ÝCm壺5j'òª‚pšøWýÏúÏB-§ý&ÿôòÖîÍA[ã¨ÕêQõ$<: µ;‡ûU>Ç'›gEÃýÛû$E[MÑŸ XSìÇhìGL0ŸÑòÖZ„Ï[XÜœõös.ÖsAü•Úc&þj‹l•A´BÉX*¡–+©ûÍÔöG‚‹vÌo}ößýåÖ“ï^þ`ôÚçãÓ_ŒþwåÚ‹›î;9ÝuüjÛçSÍg/5ܘé¸qÀ!ܧ]¿h_ðÆsîï-³î‹Õ¼º,fhqdíÜ ŠE¾uÁ¢Õ(˜ÆË?£†$*¯½  ÿ¯ã¿œØšwŸ @£ÌxÎó6jšJ·RÅNÒM>òÈZª Oè}¨¶ f‡‡[=+Ç9DÐ:ƒ‚rk%` ü†¯÷.£Ù’Á +ƒmƒ1ûE|=ðO´µ Ú>{ —§pµ|Ñd.!HLåK¨áhÅdªöÕ¬o>eyõ1éÖ©~´E±ûAêà£44¼~}•ð­UÊw3üèaã«+Œ/®°ßoï\h®J5Çk³#ÕÛ‚¤[Œü*N¦Œ…p IH(~çªÙ«#“Œ ÎÃ6dx pÈB» #= ¢’(Á qÊúoù’ÀAëô}ГÔ1"äjžgSÏc3°©§Y{†ò8€í‘þþ@ n Rß[ú¦h™@-B 7 R‘'c*Yù Z–tö¼Ð‚øˆIq(SôP2›gسذw‰qr‘atžv(YÑ›(í‹—‡ŽDèø|óhª~0E‹'TÐ.uZa‡¼çþä/¯R¿ú¸öµ'ô¯?aF³­ñåÖ¾e!;B´ë£¼oöõ]øËÇwgî½0CºQ’ õ·|üfåÛþß45027!ÿy Ìk×o\AÈvíËŽÿøõ#U¹yiËé èÙ$аÓñI˜›@c1w(øÛdÜ2~¶Z\ •çk¤…Y™^Z g`WäŠ(€Ë5ôJ#«ÊÄ@¢ÎÎl­!êÇÅ”¤Brxb®nï]o»7†ÑÅì‹` „1‡ÂXãѽ‰’ñ8á¡EšÐCï´>ê].x·:íÊÒý£ßñ(z@ûoî# (È!œãqû ÔIzöÜûjÓ®G#æX¨…V"ZĦ²©XŠŠƒ/Ù| có'ØýåÙC>1xÅ] @î\ŸÿU΃qs!¦£ž€yŒ£~\½øW=npd&S9IT~"*å•Ѫ`~©•¯§e#ò5Ò² à¸vªéDXOJÛ"¥ïTò¶)x›eœM îV­h»Q¶Ã¢ØaUgYžU ž×ñ³ÂµÙÑt¥y4 @€¢…2j®?;7c³û}G›ž}÷XÎ~Vvê¯èLHè÷ݹ¸ƒÈ×\»~qlæÒ(pÑ—I’ô“½p¶¨€/?+¹u®Ù¼ápºûíç‚߸ϱ?Ú¸;Î]î1>£`? ¤ {û(΂Œz»níÏû¶ôoHzÜI=b¦¶„s²bÅ%)êõj“ŽJ×íD(HA^IUi¨"ˆåЍJ9U£¡ÊTDì…TŽ ­y¸å÷:Äù6Q¦‘» u=k‡‘“¡ìÒ wéÅé:áv•`‹œ·IÊÛ(ánU·ÈxîÔˆ3õ²]Y¶I™oVgÈD™b^ºˆ™#e–¨9ÕnžU«¥Õj©=1êÉXÉKi²cKÄ/Σ^^F½¼œ:º’vô!Æ«+Xo¬þd¥æ•¦W¨†bÍaü–]yŒÄ«gu¼gõ¢Õ*áà}}oÂÜmÂ„Š‹Ì’<=o—Š‘©¢02”T¦š(–Ãf¢ Ñ.%•m(Ýh[v"”ÄW‰¤1·!Å)(µðòu¬l#GÅÉÑâç° Œ¢›<Ï %|+åíP2wȘ;D´L!½@Î+“‹Ê¥ÂJ™¨N-.–Ñp^²ÅÀÀ¥Z '¥ÊL,¤ÎÁhò²;#ýqÔPFç(wÏWïY¨î‰G+zw ­#ŠÂ³?=ű¿ÚûôÔÏËünó:ä?3IZžý®×ÿûá×[žÚ¹P¿ÔNÅÈHò?nŒž·@Ä@¢#ôaˆ =~¸Çï œ9±ïÌ¥ýfö I% ÷A@>ÞõzVÈÇôCsÑiW ß»<„ÊtR‰Tû2IÝ|Nãši»ék"„á ³SÑmMk¸(äë#,¶Hó¢°s´\³´À¡„kãPUƆnsh6Ùåël¼‡¤í Ï Oš”Zåæ?9œóè÷{vüöHåï´_ü]×í“}·NwÞXú @ŒmN¶^:Ӊ˫§‘c9?eèhÝ<E÷ñ›g†Á³¾;34uºÉ‹¤…÷<«;r¿®B@Ȇqbá!6)³‚°„!nÛûàæ¡že¦àäì —×/pÖϱÕ&šêâ m‰æî$kG´®3 õP´~$Z7® •öxE.^“SgaÖZ9¤¾ã‘ÔËêBUPKƒRäÑJÃTåaêšPhÇ›"ˆM±C‘g–æeùfE–Nœ¡`}rM’B›¬È.Çñ,³©š\¶F»¥ÁnhuúB-ѶÑHÓH˜~ȧxu¾ó…dõÑ4Å÷K_šC[D]H½¹JôƒGÄo>(yk…â'~„f@ ÒV7ÜÉm°Á̉/Øø02ðµ2U„Ðénr·DÚÛ£í­ÑVP¢Zcô@®¶Ç:ãõ] d¤»buÈ5Õxxª…Š`¹ƒÙ[ÀrBDDÞªßv03fl½§ïI[ßöÖ%iÒ^êù`ú(Åq© Í ÝE£¼ɤ–y„[¼ýråÉÏö;µÿ³ÏБä:Ež=‘8HÇÆ7û^\ã{á×îEæ:¤Kè}sä ”i¢¶TAs7ßIU¢WˆÍ¹8z#qŽPk“QÖlR¶Z´µ:y¹B\*V¨¥u-FN]©SU4õz÷àEžN¶Ý®y@F-“ y"—Z çaŽ|ÇRCoö4÷üå‘ò“¿šþlòú‰=3'F@½óy“ÿT£ÿBÛ½©¨©;„f¬ç®ôœ›ê;©ž H7O ßûbÔ|·ÿ‹±é/û€„Ac>hòûw< :²X3`¥uiÙ[1/ª±8z@³Ð/µH[ÅQ½‰×`7[´Mfž.CŸÏ<fðé{½ª·üPŠm"Vy0U}l©îÈBá+$¯? :´}ô>ñ÷–É¿¿LóæRãk‹L’4}a²2 {ƒˆÀuÝ…ÁzÖTˆ<¸¸4UånCc˜­%Æ öe{‚³;ÑÙJ¼}sƒóƒó,ÃsuýÉÒÎX~[»#É"$‹ëC¨Ú`ÒÎMèî å5BŽØÃ®rrkƒH ¨A™ùÞÀÐ KTÂF‹¦Z#­TIë ʳªÑ oÐK¢6«ðëó"ÀIŽP „J»}‚N/9&Ý¡üŽ0Ngç¥3œÝÉé cöD0G„ûæ)&ç*‡ãyý1ŒÁ8ÖîTÁh{÷ÎEÂCK„ûÑ`nûÅe‚#÷‹Ž.¿²œÿòý¼c+G— G“±½)h?.%®þÊ`×›“Óg.M_½}é XJßYE0„û„z÷mÿ]’¼"7W ùÎ+ÓSŸ~zñwï¿34Pº0±Ô#¡¾5µ@C ì@M—èYMŒL¹Sg59e6!¶S“™W‰,RªZFÕªéMN“‰]§eV©(ŒÕ`b4ZÙ°ámAü.Ÿx0LÖj ºŒÔ˜Wôb²éðûžxÃhœn<Õ|ø0@Ç¡\”e&êÐu±ìÆ$ºm¶.Óõ­rŒ>4ø°}t¥cò1ßžG}ƒËÝÖÌÙìâ¦1¨%ù1JB9”¢ì(èxå[ o[ÿ»_t<¹ïì…}¾~s«A²÷ÆKþSûƷŬòŒ.Ú½Ô"&‘ÿ‘Œ-6î½ß6qŸ©?UZí¥°ÙzähØį³ šÒf«¢^'nÒË›ôÊ­¼J)*ñ „œ© \%Áy©7¨ëôªF“ºÕ¦ÇÙ©V‰wÉÙK9T8îY@5[·&MÖ>ôî‘ÜS¿î¹úÉä•O÷^øtâ̧ƒ§?ëðitäá…6ÿù¶{—àú4¡ü\šé½p½*=®ôƒ ÒÍSCwO Ï/G¦?ïGØ ´Ç ÿûu“«Ôo¬0¿œpœ¥LJAë !µ Wã°ÏW¹+ÄTnçù….Y~¼ TUo.K2ìôrŸÖRÏ©ÌPvy‚´:QÖ˜¬ìJÓ÷Ï5ö$k›"%>ÊåNQ†Ï°J'Ž¢Sd€õÚUÉaƒÍ[@-üèƒÝ§ÏøääÀŸ¿h9hßÝÝþ»“7€Ê8wp|SÄ&4ŒQ—øTÅiU˜¬%^9¶Ø9±Ì9ºÐ²ÿ~ç¡Îáe>ÞzPâÂöy³—2VCT¡ f¬wÈêìÒ»¤Ò",5ñ1JÌü| û»d$›Z¤eªh•= Ùÿ¹Ž²5‰ã«ÞžÜõ—ŸÔ~ôõŸ½ß|ñøÐ¹/†.ŸžÚ{}f/´õ.M·Ÿ›ª9{¡úöí¡ÓCSºÏži;s¦íÜù¶ó—ºý¡ú Ý€Þ” =’öpW‡n]º3…þM/øï#N Þ|;÷ØsÖ=ÑŒ5jg·X¸•Áj(+GÛ󽯥ùÉL¹p›¶¼Z{—†"È¡äê§“ÜæZµ+}—ƒ™ãåEðJ#õI¼Îyò¾Eê‘û •IîýŽhÚÖ@·ýÑ,T“ƒ ›PoeU¢‹œØ9^CPcãU˜9å&v™‘ƒQj`£¥jLhUª¥•ª ¡DJ5«ùÍ2^ŒS'ç6hùõzA•–[¬b"Y )< Ô¶  ú}·F3[¢©Á¢ñÅ2ä(Æ“EÈïLjG¢Ä=`„¡¯ÔÉ+ÍÙ-þ*R±‘ÿÙ¥!S´–Ç)\TTamÖðH 0P³ÁF‚@_–•eª’ v‡ ¼Ü@2*ņ}÷YÜoG—Òž !r¢]² Lji çáƒÍZdb »@ŨF^]nf•¸‚[œ2Ì¢9LUˆ\·††*I6BZ Õ 5(EuR~\Ш#ª œÎßÏ‹„^"c–)Ø•j^µV0íôY1Æ"ýÁ†¿ZÃÀ?¦lspš¬T‡›ÞDµ +º‡‰æíMLQJïOàïOäîON&I‘/mŽÒä«T²ó9yä…™Ó'f®MA’ó[í ¾ømyßÿék$fBÝév`Z@ì”%¦o î¿wíÆÔ'Ÿ]ÿà£w''³R⋜©j½°Î$iq¨Û\ÚV‡®Þª©5« ü<)§\+ÁëZƒ¢J-©QKÊEüb63›Fí¢¨,:•Ã$ÏL¼fR¹*—O堈ɦv2¨t2rØÔ¶}ƒBI”P7ˆ3œjÄ’«µôÊ…¾ýÛݰdWŠ ú§L$ã—Sëõ¤8’n¡¶i©Mjê)õ¨˜(>¥ ¨¥fî†EÍ™OOteÿú×ýŸœ˜8wõÐù«“gv߸»çö1t>BÃú÷_X»ítá†EHqíÔ°¢lÖ!°[DÀJ©é€Áz¢èã |{ž‚jñÊû¢Ì­^M…YÖT…fN…K\çSÖ‡)ëBU!²2—?'×ÈÌ7£_0¯ÜÎCy„ó¬oí:÷›±ó†h´ã u ø1ѵ›>75ŽÚÎ_mº‹»€ÊÓ¨³ƒz€BgØ”vnw°t(J5§E»L$=zCECâápñx´|o"š§«&æš.q¢¼¡sÏ|2_gfç‹ËnJÞR8@ÅÁò+GFÏ‘ÀaU[È~¼ó­³µHaZ‰F“ìJ“ Ê.ûz?ðËœ²Ú`ÕW”R,޾®°Â&«vª+š"“ì«ùÊRþ×çEŽ#óõya’ó"bó‚$và¼dó!¡¶zéî0s%Üt>}3x^4R=0 ˜ v€âT¯¾ ,OH¡Š×áàw9yHæ·û$`¼Ö%ºÖXeÉZÑÿìåÏ|òw—§Î›Éø_ $MÂÓ„a™µ3³ 6èíB*]·gü·¯ù/ž¸öÁû¿Ø7’=?¾<ÚìAÞPL––M•J˜$üD…EÅ I6dñý€Ùá$1© >‰UÑØÔ¹"»LǃTg•5ØÍ.uG°¡+̈wïB–[a‘7yÍÝQA£©1{—¦N>8¿gqlujPVœ>;Í4žµü£ï5Þýø¥/Ùóþ›•¿ÿaí/^*ú^ïæ›žÛ[±ú`í³/·oø·Éœ?þ°}ÿ¡õÄÇgNöœ¾Øÿû//û÷Ÿšê’âuþÝþþ_×ÿ¨ eüÓÈb]_¢ áB™ð†í8\èEΠvp©\9£PÍÆQ1WNǦ؃È É3eJ »Ô&ÍÑs¡˜ 7àŠB¢¢:[ž…“¡¥ãŠ|Œ³ÙHõ(Qr òO:×ú?š¸ð§Î[SàSŸ¸ÔöÙ¥ÆS7Ú¯Üë¿îž¹>pÊ™çºÁB:}®B¦×oï½pÜê1ÿ-ä—^÷Ÿ9ŒÿÝòeÿÇ/|~ wtMHÚ¨¥ ­ƒ>Ã`´4¢"TÜ€ÊÆ-)ÀâS2 á ®¹áX”=¡|8ý‚Ý)òñdÉx²l¸cñßï»öÑÞë'öO}²çîÙ#wN½ôù{ÝŸ¼ÛùåozÏüièÂÇc YC»œëógúÑ"äÚͽWnBÐkhŠ„–d£û§ž±gûŸ zù™ðמŒ=¼V!tK<òÎÓ`¬£Ý§©4ñ Æ€ƒ_kåµz$}Qjô¿ë !ÙˆÌüPŒb<˜I!Êm¡yCÊc<Å8žlÚ7ÏõúÊ„<šzhaØD²grŽo8ÖÞ`—áÀ`•fQgpó2°l‚ ‡¹†µÁÀ‹íòÖf'ˆØp|àj0D-_°4Á/‚ ø†5ne…]Ún j8Á‹m3‘:‘‚çžä°óãFC±Cjœ†R«¶È¦-u+]¦r3Nœ¡?:h8ÎÓj®1‹òp#€Rmtz¤ÃQÚ±hMWØletºy£ÑЉ”Õ|w"Ò30vò©m=ó­ó¢)XWëRÂw†Yz£ påKO!‡e"%ô›ç²rÃQö¡HN;¹Q£ !ý‘ }¸çÅkÖáhJtolHg©Ý7‡Ù+Z .à;UÙ´­^K”ÿoG°‚c™jÆZõ¸Š“À¦rWÞÿ«cG§¾ø|æòeˆãý¯22ÿÄ6ÍL$ÏüM¯& gŽbXÞ—ožþìÎýõÍ— \P·4¾iNpÏüˆñEÑ£©!{æ„\1>×3±$ddqpÿ"WïzFôÏs Î…QØ©£Éäðˆ±v‡ÃuÑ÷DGâm£ v<û£Œm^Š8V‰–‰wd;¶¨œU¬å+p±™·òè[E¬µÐ“\YÇ@#ƒ9 êI'}oÞŠ³¿íó_|éÌ3hËxýÕ©S·®º}õ…[Ó‡Ð+äÌɾ‹ç†§.^¾4|ijdæÚÄÝ[/L9‡&õ3ÀľþnßÓi‚òhZ’ Í1vG*BT®_\³'%ïÈH¢ùXŠw"-xï¼0<‡}1–f¯²+ÂÐkˆ³õF[»"La†öcW¨e Ú…í±7-t"-d÷Ð=ûz÷/ ٻȋ¶ÝC)ÖÁd È@ÓBoÏ ÚTcF$7=‘ÿ£ž'î|:tõ³®{Ócg¿l½ÂwâÞõ14 ˜9ÑC #h\''ý'Ž0§Ž¾?™]²Â²ÊJ=c£ž6Rϧj¥í²1¶kÒܸqÒ Ð”¼=TݪÍ„0ÈZC:²ÊÈ«4sªLœ €1\ÈIBxªÕ'¨uÑ!“R3À}ÑÙìfxZª3D 9¸®CO˜erNø¡1»“|àBŽ%yÆ“½{æOÎ Ãs$ÁÙmiñ(ñ^ÆÚ0ú¢ð¾›:C Ð`ÄE<åÜ“èÛ‹Í“âÝ“4™æÙ¿À{øÿ^ô4G c|Žcwšûª/Þ6”4ž<’ÔmÃÿŽbž@mkˆ²ê€89á,ãŽæÓ²dl`Gkm*ÀW üXg“#K_m‚ Í«1rÊ5´´‚ű¶ÿƒùF;±PÿÝ|ÇSìЛ˜ãžHó`;a¦³óÅ6ë ×w†êpd¾y^:<Š« JÏ*×0‘Ò„‘GŽ´D-B²övAß}s€ÙL,òüHïƒ?šU®RP¦äðo.E±[•îÖ< ç,1ÈwW–ÿÍ{ç¿øòêÔ•‹—¦EùN­Í·›šÀWïܼ1}ïúÔ­s_Þ=óÙ_ßú^þÃKª–&´-ŽXßìk´ìMó½ò@üÁ%!ÃiÖþ9Æ®9ºŽMG¢"ŠPBÒ ê•vHúEä^ˆ™%Wb‡#_a`–éè…H»ÞÂ'—ñ¤ÄÎT™Õ•e¡Bœ'(Ä€gkEà*¢ÉæfûY-õ´²œÊï¬>>6sbüê)Ò§õÔ—]ÓWÇ0™š ¾Ñéú(Ò§/ ž?Ûwé4œ^ºóɸÿöÛþã‡^Ì^Pœ(®ˆ– qCŽŠdwQpÄ*µHI8pÅaCz£ÍP˜Mvާzðˆ³ôDðf–gÖí_=1' ýÈpçîÅå›<C.˜Óýó‚Üý·±ûcÕÈ^Þ!E—y IJ,Œ‚páf•;OôÞÞPï¼{r*v÷.Œ\ÿ²ü—'ý7ÁK:ìÿtÂÿÉ‹oW?‘Ÿ$ƒöEQ¢&+L²ÍÆÎt«" yv Ó(à­*7xÀ @عJN®ŒµKŠÌy†B5Ì ¯7Âi;¸‘c)A»SûùŸüýÊç°1ø×@t3 y?Të`Z®jðïÞ$Å@²ª;F؄ƬMk¸}Õ¡Å€PQÃveÍb5H#(H6§Ê"ÍSÁ8Ó· éÈó âÞÉfÀ·É”°7¡“¡lÔÒŸ–R+„Ô†dÞÏ_ØNTbü¯ùgöÊ ÙO¿¨þüt=°m(#÷{éL`´þk‡ü·_óŸ~Éò•/–·?ì-KTuÌw–IÀ,VsëŒr¢")»Æ$Ï•27BäDLmDt€R1È?()di€Ž€Ã¿n ¨#ÿ4âÇ€ þfüÁ(¢m‡".Ù0¤…€FX¨vŸh N=” …cƒ› ©FH ¤:w™(4QÝFÌ5¤vOìöŸóŸÞí¿pÐáÅÛ¿é|³åášú’X ¤· óBXð(CCT\ëéZ–˜‰X© äNkt"¤RK´B$·‹tbfq‚¼NÿÏÚÛñÄ¡Û?oùQíUËT;#¨¬hôž–:¸€„¡Ž“«b€ŠÂ4R5Vu[°µÓgïö¹úBœ!®¯T˜üaHããr„ó :a±QØa¶oQlW´U¶r«PùÚ U­KUëVo‚õ˜i[¼@ij'`Rr/°*;Yà9òPUÌá²PEuµPÁ,Ö0ÝD¨ÂÈE韩As\\†be‚U¤âáÎm°ëpí6@%Û­·ã¢?0' 9Ÿ±D”ÉGBŽR5@VÝLE#\ãd`óT»ùnA¥“s£GÒä‘5)Pô´ +Ü2ëHôuÅ5„ZK]š"‹I‰\­W²¬yT¾AØnÃ|'ÅvG;ª °f5À­Âønç[c‘~s¾õ6íì|[óE õOæ‹Y×: ˜o½Ê]l¼¨CÓLŸwt£GôÍó–&¯Ù!n°ÈJ•Ôh`®Át(׊íšÎkW¨­-ØŒÑa눰B"ä‚Ë*2H`vOm@(áÙpë†PëÚøˆŸØ{áÃO}òéÅó—f®ƒvíÿ×PðjîÌz5³Ž 5h]wíú4tlnO_¼uéŒÿò™“ï¾s¬²(;Ò÷ " ´|®¶lœM1U©LIõ—ªIi ÆF”ò-tÐ{AUÃ(DR'·>XÜà•‚ Ó¦ž¿+TÝ¢jV´yå]ÁÊ®* F%B§·¥/ÌÓî ó †{w'ÄÉ…p2Ñ# Ö›FÓ¶ƒhìf=c¦r父%QÙ+ø?Yyò½¢{g»ïGãô£Üí?³‡™/Ž|¸?dm4â…ô‡ŽZƒ‚8Nö$¹q¸€r¸‚B©<“ÇÝŠ© ™ (qQTBá²DÏycƒ&*/Hã*Çåˆf³®©OIyr~†€Û…¢¾Þ._Üž;‘/•RÈ|‚©¾3Øye¸°l\ã(é^lUB3 Êíå±ÐZ± ?áXån¹O_*kš«¸ßÙ³ÖU ]ÜJ-»«É¡î µv[ºClcña#±!€3eóhðàSÁf€QeCƒ<år›¤Î£n 3"æÅ1£.HJN½G;+¡ÈL%k«ŒAþM—ž¨ u"¨çãÌj",z¶€["—ɤE>@ÅJN‰–ó’#%¹ßÙDq ùCËÑp§cmñÆåIù2~¾ŒKÖ'PÔ›]ŸYï2MX‡\-…XëSng—»¸ J½èДƒ©A¦¦¬ —Ån”qqp ny“È››E¨ùÇCs¸¹)ÌØjÀ|aX0ßZ¯¶1X_áTäk¹ æw5_ø0ð]‘F±žÛì|ó¤¼oÎ߃ýð_ç‹F™³óÅA»]ÐúÓZ+¦ó÷ÿt^:½YÂ`e¯OÓh•#Œ­1ˆaÀûÂý.ŒHwO¸£Ê$A:‹ª7ÞA¬6C‘†[m•!]åVÖDkÜa懌âû]ÆwMžüÓŸ Tƒ¨ =S<áùO’¼ÿÿÿ‰deB˜Œ¯ 6³ OBR¸5sïæ5ÿÌÔõ³_^ùð—~ö㣹»Šâcò<Î ‡£Ú¬Ø>€¯–5:È]Ó¦ë ×öDêbÍC ¶†04gHÿJŸ´Æ+­t‹JIJÏ'™FZ™ŽQ®gVh•:&P”€Ú6:ÑuÝz]YS®‘åIØðÒ·Ó€«dU¨ÅM=JuhÎÞaßzpiü¡‡ºÓìµ Jɼ$æ†Hj[2U¼RøÎЪ?Úøá¡ôk~b𩨒Du~¨´$BÝh®7¢9WµÏPí6Ö:ÌvG“Ý]¦ÔgrE):ö Þ—µð~aUÀ}ðGÉ.Ñð\ÅÛ×àРñA‹Û„D\ž?W,(IK4Š­¬Ì¦©vè‹á£$¥V%îÙr§ äÁÊ Uôå"ŒPäkŽ1·'Ø{“\-^]K¶)HÓàQa}ÊIÓOÆ.V>^i0 ¸2ÄY.66!!ÔØùM6Y‹MIHfi…†˜JÀ[}”`%ÅÍN]‡×ˆÑŒzè‹‹ã÷, Jƒ"œ¹!TÊWµG–g`ãT’ r¾ÊB*SÉ(4 Í€*ƒ­5¡<ˆR€R¦½T«Ìp`Áþà~ÜÁ.#›ÃDõùŸ¬O&Ÿñ߯²À€µRÖGSæPBë²>ÔÐanŽ´4E[Ú≎êÙÍ"¯Q †ªq‰à[±,P3J Ü' `x­‘Öž$Ïè‚ÈÝ‹"†Ò<ÿu¾°™¨5#;º†Ia¾*:ŒRIü9_Dvß:߯œou¨ }¦j¾>/%ö·žpÄ@kq¨0PþÆ~z$ïïû?™m>ta3tFBP"ø…¥Ñ¯­LCQ¦Þ%­ V”‡(‹#k2Pcîw›ÞÚ¿µ§W/ß½sëúÌ4𦹼ïò㛦æ«È ÿoƒ'IàܹPç»qgêÂõ/?¹ñ×?€wY–ºZ¡x^$Ú&eð¹»lªÈAA ² *F––kà囸…va‘CPêC&¨)ªlG”¹=ÒTí ÐýZ›„è.¯®/Ô4aí´W¥Èháã:È2P)Ø6\àš†ÁÉÀÝ€1D_¥´|)З d½ˆÌUéåÕD‹Š£ÙeÉüúû”uK]8FW‡­„¯oxQÔåi¯¬Z<¯¿oD:›‘Ëçg³Y™6'G¢oYŸz›þoëÓh·a}Š•ª…,]ÊÏÔHÒU¢ úˆ °–G#¢.0X"!}³œµ]ÍË4ˆ³Í²<@"m|ÄŒØ?µ!ʶ=t/qj:Ãí¡z tÖ!JbA˜J xô9:&”r1_H=ü§ùö n˜ìîNtã݇ŽèÀßÈüˆ‘yQÿ7óÍT~5_² ¾m¾éFñ.K`¾vQƒùü‰óÒcÁ‘ùÛy©³þý¼ EX1€Û‡—[¦âà,1…ó‚{oy³è$1E8BØiF(õ© ,Éd¤IAÎʵssBU|š¥PöxxɯßûÕOßíß9? $д@_`d&!Œ¼–CGæ1&µ’N!¼Z;`ÑBÿß@$³çŠ K'œ|> Cˆ ¡è#Á="Ü`‘ÛUƒ"5¿Â(mpjÑ; g`$Ìï G:‚TKÑ´¥@þ`Ì»šÁ`#ZסÖÓkl ——z8hžüü:HœF#y°ÀU¢–§áTÚ•UM‰A^¬SÖÚ­¡¡½Q1ÍO•ÍZe3W:Låv=Tå¡*€‚{‘FF Ó‡;šü9àÙ"6ê øûI^”AÇØJ§mbÐP‚8!º §‡´íìÄÛ{†IÀ{øt6—‹çì@:AÜ:XT$xÑÙ&&B3ò‹àEìÄqæP<ËGz¤×,øÖÄkt)àCöÄZ P‰'º&µ†i;" «Ä§Íè¯êö&GæÚFç:ñ¥–:¯íþ‡œ&&!C>™„í\ò™™Íì$°pI냬m!îþ¨ÐÖ {ÙZz³ÕÜh64 xÊþÛõÁßÿ·õÁâ`lfÒQÆR`3`}€ÇÁÐàk8¬'±hdY°gÐwÌkð¯ñ)¾ >²ÇÚ•X%¸Ø<ùJfƒGÓim 7 ƒ®.¥vÁì|*ø‡ùš˜³óÝŒ®(ûâ'@7Á€OÈ'ÿëç‹MøÍù>ýÀýj¾˜,ÖáoóÅ·aÌnP_q^à|"Hœ=/ˆF¿y^2°`ÉèÅ*v¥AØèPC5í‰÷ôG9Ëú¢%ÔX;öCSˆºÖ!ì –M¦:+èOµ7&šÑfî#7UÍÚÛPúÅÞ»zîô5˜Hqår÷: g¾;S;ó¦æ+c†x5ÐF—^T¢Àļ=uyêäç—>ùó©_ÿ|¬0ûÉ çsFSºÅš®ÖXÌ…6SžC—íÖç…YÊâ½Õiáuó£çE5%úÐ8¸)ÚÕålŠr‚¡_ç5U»õ Àa€ß‚žB˜Y d­@à‘_Dš$×ÜÖSE®é|…# N :PPžŒ z)5ü…äÐá0èÕ€O€É4°»¸ßQ|}'‹–Íe§3ØùØÿ(}¢ï[µ]•ƒÔ½„]jR·‡{Ë-:Ⱥb£¬Ó?OÈu"¥c’Î¥eœ¼œWª#Ý÷&GÌ‚Í Xz&%àî’ðw)D¸²1v*ÄéJI–V…ŽÃyf}©ËVâ´–¸-•>gC”·-!¼#)£=.´=ÆÛÔánp¶…Ú[<Æ&—®@Æ.ÑHŒí)z©˜Q&ba)ø$_„9’g&´L…dže6.J6ýI¶d;ÔzõÃifX›î8=Z©VŠe•€4œ;$Nag`ëàü¬Ã¤“äU¡N óh\Zš£@2\Ûêª6i{ßJ#õ”Ù0 ;âœßº>ð¿u}2âJ)Æv…d‡Bž¡Ugô9&c‘Þï°8meÁ®º¨Ð–„ÈÖäèÖ”ÈæDos¼£)ƆB@s„­ ¯ÉpÀBP/5 CÁ›2<×™ý>˜Ö9æñ9¶ž8- ›/°”¿š/þlS t’uÿêùîTH1f盩ùj¾_Ï·ÔçªmNüj¾-I!-ñžÆr^" ö:²jœ”qX²ÿñ¼`?ÇüÀÀñAñ ;vt¼% 8¶pÝì~è3 Fé&’l}±Pà16¤Ø×;ódÔ\‹ô§/£ãáÇS—ÏOO_+ò.LÍ"3þÝ™š¯t>g½—ÙAŒLÀ¿ºùíÙ>t·ï\¹:}òħ¿úÙñkwήÕÓÃ\Þ¬­Æ^’œ0ñ¡<_BÍYgäo0‰7šÄk¥Ìuæ1s³” ™>4EÝ&blа‡ µ¤‰›­’ …»˜«Z»ÙrjÀ‰†úJÀ÷ Ç j İ<(‘C…`Vy Fjµ°+Hßç³u¸í.K‹ÓŒê„•ZÝŽ¯»ÕíB°S¦"ò"µWà{qf…¤50ÈV(ÓÀçÉWc‚t¾ˆ“-`)ø(òÖ€@jUV›åˆï¼Ö*op¨šÝú– ž©p¸²Äl¸%k„rBêqD,ẂÀÕ¼ŽËØ*妫„Z26ˆ… `wH8n¦€ ˆB–¢7õ:yZ„%¤þêʧ á'4‚JÌ\Øœ@â}§‚èL™ ‚R ¬IR¬p›¡œŸ •3©ž&Ÿ-#†4«‘ n µ4ù,H+Á©v›«]–J›¾X/à d ™»"×Mp,p®P­æBù–T  dÜ2µË’+â@Ä@޲>X XN,àìú¬gÑÖ0êÓ_s—ËÇÎùê*Ǻq™ÐåÛ¡¥kÅ:Ézcƒ„¾ADC? íRÆN)3]DÇG¨")dÜàìa¾$Ÿfn1U¹t¨ºUn3&þ0ßçáë|WllÌcv¾pçfç»M#Ú¡ïÔKpXÖŠ%¬-2ÎV)B—Ûtœ—b 9/•ºož-<™g£hU>b#‹ Ü! ÀY›]´•ÉÓl N ø\¹VN†K¼TL-Ðr¶=¸àýŸ¼yþô‰ËÓW!]~ 9th"ŽÍïÐÔÜ"Œn¢Qƒê+;#ƒŽ-„áý “6tÏ„©ùòË3ïýLJǎLlߺ-ȳN,ÛÀâm¦Y<Þ& ÅíRÖf)ýy1õ>Æ!ð„TèŃæt£ 3ˆýÁv¬ÔKê,ʯµÉ¡mv° Ã1¾‘Øà¡èàáowØÿ¼>ÀÀ|µ>$pfâ­¥3Ö3YÙœu æó æZ&s‹µ‰ÇÙ,ân³‘æÂ>!qV`ef÷^ÃâÛ‚ýâPåß3?ª/ÎUlâï™…sdfQÞèF:TXïä£÷n½K XBŽ’úK†”œY\+_Ï—FR=ØÿÊùnç172x÷±6æ»–ÎĬ¿9ßõB:‰ôbË£è»+ðÍó’-&ç»â›ç. bm1ËŒ¼:§ ’ñ‹4¤x’œÑ¦j$ßÈ@².= UŠÊBµ‡tŸÑÑSÐ/%äPwÃ_~ûÞ™sg/BRüî=¨—Ïܺyç19ß¡©¹ùµ©!?sÖ¶*O05³ŸÕ¬[w 4|ùä©Óøã_^yù{%¥µi ²-ÎB•¡B¢©*š¤Š"U)á6àá5BH»¨¶„[j£<•áAðŠKƒ¬en{©Ã\j3–˜ÑCYf„…¦T#Ïò²¹¬|ú‡DÇÄ%Ê´V.² ÀTT{…Õ>A‘YêbAˆ¦Ê#¨póª  ïxPéa¡Ÿ;@;•V4L@z\*P%¡o¥˜È—â*Á%Ô:x €ºe iµa»SÛ¤…­€{Yë‘´Ej‹LxJØÀ²ÈfPùtª”É(ç³Á=‡m}àv|?Ëõ Š­MAÒF· ^­Òе"tB±U*Ìäðv2¹YºFa‘™Œb« Ì!¬€ñH`~¡l T6ì 2ɵNa\bü‘Њ'[Ë. ‚0JZ–œ†Ê;ЧƒÊ&Ç®ÎÂ&& #ñÏÁžnujšð÷+õ"H… žU¡#œñmlD› Ç™wÿëú`q0À}ÈE/H¤ñëƒïÌ“ Óy¤â± YÉ.±8K"ËS(¶rY¹*IµÃØèµbYj<šº`mC˜±>2¨*,¨"ØQæ¶’Åq˜*mÄtÕM­Mdf”§eâV¹ >^¹Š£¼:•ÍÜÝ5*€Ù0ßÍQTÆIÀÈ m`±!ö¯žo¾L˜ÁåìÌ7G,ÉýÃ|+Æúà¯æ[ëÓÖ…q^*ÂÜ8/%ÎK™Óò·óRa ç¥LMÎ pM…|n‰”W®ÀÈ‹ZmÃã­"r£pz«œb¨XT¡] ²D¨GPå¡!/D<ª|*4TZ. –øÝÙ›>úå;'¾üüÌÔÕs7ï(Œ ¬ z6ÝFžö»û€©c¯æï¦&àÕÀÇùÊÔܸqçÊÔµS'Oüþ÷Ÿþè‡?m¨oŸ?w³D¶](.(Šyâ:‘×ȃ©‘ ÚmÚŽ }½MŽ Ã,Û¡l’žç±`¢‘›UÅÇo:Þ}¸šZ ³HÊ}ô$¯ÐLW3š ¢» P(EV0•A|˜ ê¼"˜@¸ M¤á×ÿ£î=à£,³¾ÿ;ÉÌdJ¦ezͤÍ$“N*¡ƒ4vlˆŠ€H/!”H PBïXPQwuÝuÝuÝu×]Ý]ײ6DEj€„4š¼ßkÐÕ}Êÿóñù¿ÏËçÚÙAB˜ûäºÏ}®s~e¦[€`ê•;Õ mºeN[Ï·Üé©tÅ•;s–ÙKY²kaºwIл<Á¹"ÞU›ìæ´‰b†á À(ðì›m’‚Zn7VèõåѪ9 9Íp¶\Ð/™zPÒ °Ð%[žýŒö î,JN=3£Œf(›1)2rªBÁMÊ#‰¹SrÎb "E||±“Lšñz5U4ñ¶ƒ¡6"ÚJT@MƒBá¼¶|¦Y6ß©¤¼D=G˹ÜJŽ(Æ $Ér¯‚|²<™Ï£¯M5òʽ&8e6„²…F"› ( ô©¹ô^s¹Ë²Èc_à²ÏµYç9±&I®NM[™šB2\î³?[•×”åg­Lr-r@8ÒÎ2BpE£[ä2γh¦i£ø`°¾5è# xâSîŒ">„…Ò”/è»j¿``•[µÓTrz>”m“åôÒEúš¢”q±³-1À W¤¸Rm–¹” |úïLJ–;Y"œ9@qër“Ö |šVie’®Ü%#.´ËQ*QL³UÐÊENq½eNÛ«iŽÃ¶$!±*¬¦€¬Ê[ª.Õc‘µ‚­â4̳ki@ph]î6霪“‡¯wCM§X;ûíG× ZHj ÀT(ðUq1‹,Z£B%}ïz#f›uË’\ׯw¾;t½f5çG¤ºÆÉ#Ø\¯h¤³(äd,Ð@c"ËÐu7É  grÀ~p(ˆÛ£"Q»40(}3"Ï€Lð##Éðs$Vîàˆ.eËõH8ÐPuøïТ9Û}áÔ…Ëg._¡Aªfçõ“ýê¾ÒN5¡!ÔµC@A4$Bç¨ §Ž_8òÅ•c_úåÏöLž°û†þ÷~ah¿]½s×fˆÁDsq^mfJ]VjuZ"$”–yMk¡ïJž §RÜÝæ®A]ª¸¸Š8yMŠnY‚²Ò§à ¦ºàýXh6²ê‚±+ü:žÚ<¿xVRŠ0Ë£ ,TDtòéjÙÔè(4‹Ñ<:#‹I)ò«âœrŸ\F¢»/2r¼‚º=òQj†Ãü„Õ8>:’úœOø°<’³HQȨ±mÂQEü¸VAåSÎ1E“‘€†"¢A xµ*9veŠ¥&ÕZhnÎJÜÑ#°+?}wAƶÜôM9© Á"®&®Ï ®ÆÓÕ¡¹AU@ÅEך1|ûð†óKXsƒ ½àïpÄ ÅGN|jƒ¦kñ‰® ¨„—Æ)y†AÂ…lÌr°à®®LÖAd®p2¦ ÄUàY°ˆÏ4M$]—É (–*æ\Wÿé(vµ¸FÖ}2ùµø(8×f˜e·O·˜RÈø„,Þ<¤ˆä -)‚6A£ä¦¸šŠYñ±InhC1 tÇ©­:Õ(Ľ“c‰Ïö©»ò3wdmÍ Å';@B«Oõ­ë‘bj©ÏLJ§±ÉA’‡? €hqˆ<ÃtOÈs ¿&¢*ÑÆÁž#à¥^Í—ró}Nߺà%³A) î¢Žœ!æZ% ôpš¢yÂ],¾-ß3$jÊo¡Þ«ˆ|D¯™î´>a7? ’s]ãdQ÷+e÷G+xŒ‹Š| ZöhŒœÃ&m7DÛ@_Ã-‚ò@–,º*ÅÒa[å7Ô&jêý´ µõ‰(cûÂûaga×»±Gêºì@cvJ}ZâºAt0@z£Áø#¼8Ó 98Qg²"@y±8˜óùáTbo„C ´Úä˜:QA½¸_â•"Èðà|*twŸvÀpÜ`ðÕmÕàÀ‘…w¨fé¥J³bªtVõfe‰LgùÃsOýô£¶Ö3纺!=} —¢£"Z'?Ù¯®o»Â©&Ô®ƒ§kˆªšKB´ãì•“_wþãí7Ö¯^{Ë ÍéunãBS4§x:3˜k”IŠùÔö">Ú% t²ć÷ûñ™«Xžìœç²ÌrYçyì‹“<ËSVf$7ôHÝP˜Î ä; n„ÐפØAgUÇéQ³¨ZÙ õÉžºDOm‚»ÆçYæûa†Q쇙vÃÁ‰F õõä°ùÔÊuš;å‘wÈ"xjÛ2ƒfÔÎ,þnY{†}ò JõpŒj‚FñhLôTSÌ|…ëe<„±8O™Ú+ªÚ•.Å{du\ôê$-ö+]ZöCEh?PÐУŒç§O2‰ôƒÎïímêyŽXjÎ¥ ("&ÕSÖf¦oÌËâQxmf\]G åŸ|1%=Ä:ÚÅ2ø`Ñ™H†Çµ¼JÞ,ܪYJhäP¸SÐÌlŠ·×ºÍèK”gûo´ëoÎô¿q`ÿüƒTÓÑÕ‰H¿B[R ‰ç'ûÕ}ùjª ©Ó\M5álxïrwû• íWΞøø•ƒõßÿP0aŠÍ@” ˜UîPˇFkÞ  ´ª‡â<³Óó‰s½ŽÅ>'ãîwæ¿5ræ2T2Ô3ªÓ>ª×M2êÕ>aPcH$î:Ø`E%ˆèðp×3ë1D‚m¦¿näÒ2bÔNÝ Óaz¬†ùβd/¯>ç|¬²@Æ9øž“þ5>Áå驼.LºŸÇH)zíd]L¨¿­~ܨ$ŸÇbu“cµÀóÐ(Ç!,²øH¸RÒ¢› £“âó‡ñ¡6øq|âíó⃨ãdòau”Ćòá™8` èQ1çä¼Pœ,™±øY\Û O@Ñ%`ò¯íº^쇥kû!=¡*+P‘ž¸²G°®0sYVÊÒ̯`¦ÚÍ™ôb¸´IFÃ#Úv›Guš‰zõ:N±àšŒSE (Jšn”¡œÆc‘ýàŠÃÞ…˜-€µØ ¡zLu•O˜4‹þe?̳h—øl0¯'ÆÈ&?ã’C.±á Š»† )ö¸´¾éü7äY›Š¬k Ìû†elì ü’r ¹.|½ÃgI>ö» 9ÅŽû…SjˆÖ>k ¨<6&*3Íÿpd-‹œ~Åõø0Jǧ.Ý´4Ÿ¦\Ÿµ9">T Äâƒxñ WÿI|ăÛüßŇG9²p|x Çg‚*"|³"Cù'Î2"˜¡øDð@árÈ]`?øqPË…ãÃã@haKð]|PˆµˆøxQ0þ·ñ‰!>iås]¦éVÝ”ØèÉFÅ$ìQôp ‘À¢sý[K2a\֤إá Î@£Ó,H­ÈY*øÁ‡æ ð24Åûáõ2Œð9ÊÚë׋+Cñ¦•:¸ñÊ5²¸p¾’{XŸ pÂã:±±YÀ$è§Q„Ó§çz¹µÃûažîê~Xz—ǹæ{sÝb?Ôfÿp?Ìq™¦Zµ“bU r¼ÂÔ׉¤h« Ù²u9ÌÓ ¾>ß¾®7æ^l§ÀaÕ½ÌLotöœh5Ó¢Y‹RP¯·LC8Æ4LW½4V¾šÃ0¹”MöÜ÷í—_<+DËÏÑ™ħPsµªÁYû§ûu N•8œ…~1t %R \KÞ¢šÞuöĕ֟ýþµåÞ?8ÎÙG­ê¯Ö”¨uéRDZ¤âö¼‚úÉO쮨¬~à¡ EÅ£ìö±Vóƒë#ãL§¥*˜°µ4C…)«ól|Uš{9ž.@k€Ä÷ðaŵEÙlÍu8 y!£Tæ‰.s+€…€ ˜â"àáˆx,ÏÇ9Žè¹öh¦”…+à€`oa\†àU…YÃ|cŠˆ+“,«‚v¡[•ç«Î°/ö$ëg¹a‡jŽ``Çš BA»£X¢ô Ñ+¨l™ÑLކz=I§M:Çi(sÅÒ-\ìÒ/õà¸AÞDZh¹GK£ÍR6ù\úé1ÑOhT¬©1ê©:Í$нh9ÍÐqô…&ƨq"ÆF?—´Äí¥ù›zg6!—› yEÐ]êôòƒø,¤õw-><݈mpâà —ø0T"2@)x ×ç?Ϫd"«ßÏ¢dCY’Žø@-¹ŸIôÁBñ)ÿ@š)CñÑFƒZœç0-`¤èŒ]ì4"Á±"βÜcÔÁƒD<ýäP|Dˆ®ÅGóýøP5…Î&ºéfÝ\§•´¼}@Ïæ>9MEékòRj³Ù?ÕŒ8Ó<¨CÓõÍç¯íÞ²ô‘ñcÒÓG§e f JÉ7è†m+ëß{í~ûæ»öêº u÷Ý÷PZêýnÛÜ`"…hy’Òòx=SžéV"« 6ÁÀ#† ºEGtQ0ÀÞÜòssoÿÆ’Àæ’H:5þ"¬dFŸB¬4à¯Ï²*+üø¹ŒŒ¡íW†¶ô2·n©7V¤Ø^ ŠOÆ1u™’UFluv,ôÏÊ ¾¶‡ ݆•Yí¬Êdã<— ¡ò96ÆXÚtèB0d)wèÇ[%XÆ™æÙ!nË„¸ Ô ¼u#n2Ød„ÔéÑ@‹rÆoFŒjr´œ®2‡ Ï}ž‰Ô*"&éјUMÒ+ç8LóÜfXË’…1=à©ñ@ËT9W²®Ç‡èUÂÓ Ú«ÐÀL5oèó?Ÿé±ËÓ¡øññ)tŸ•YßÅg–M.¦íNm™SÏD†øÐXàŒÃ‘Û €hjD†5+€;ZÕ Œr”sX”ÈÑ gf4U´€m?>áºèZ|ÔÄg¾ÛZæB×ÂKnA9–QÔ—N¨@8#ÄA‰‘=Gü(ŸLQ ã¨m+RßßË‘=geˆ(ƒÔ¬6@?ÜYXZ° µE âj2l,ªTÂ*>vhV¨ƒ>œT¹Äg[ÂË~ÐÒºçÏñ¸Ôg_`7@cA㎣ßP N,`ìÓ9’+~¸¸ÞûÄ~´¼ËÒ\sãM3½Ú)¶¨ éAmhƒrKeõ vQP_‘a®È°. ²R6õá~ 6'"” 5£yH'i‡{ í,«Œ=Æ-Vá·a«7ÓçºßiF$;øÚîm-ŸÒÕvæÂù®‹/~/ÕÚ]+B~ŠÚ&\ňï$d$Bös!@ UM×%:ÒßÀ'·¡ŽÕòéû}õgïüâÕ·^úùï^üùkö»W_ÿüýñÕ'ïþí“·þôùoójÃê²!¥wÚô÷y`&Hö†U‡ò ×ZñÊ«ñQñ+ÎP0;@@ØHªp|¬FâCu7ËŠIŠ’øp£_J–&8L| @¦à¹ÌLÑÂ#“t@æ‚/jðŠC"(G <¾BŸŠ©«èùGN‰‰ø=ˤ]êqþ`?„¯÷1]4Õò÷g´qÌ")ªÁ#¡ÅꉘUæ—ÏIˆ¨ÈÒÖ8Wæ9pý«L·.L1c%CÙÅê€)8ÙHT2üǦÂdDZ¶¯E5ùIË3}•9©e¹÷ú½5Š›{¤ýñÅgOú¤ýÌ)¨Ž—.]‚ˆ$†ÐbøÌÿóÛŸ²-|5Õ„¿y(…ÿ)þ³í—ºácžïkÎ÷£nJ|˜Ä…iÔ–„â{ñÑbiTfPTØuUàè íz⃨7)cwHUaQÊÀœè"…O¨Ú!> ¬ù±†½opÁ3kI’&äEáá ý®ï•Ê„v¯œÍ+ö)ÆHÑKÿÑ~XéVU;ÿÃýP®Æq#§U†ªdeu’’ÇÓr¼.IW—¨_›jÙœ¿½0°¹ °)/°¥(X•`ÌÉ©ËE¶\®6  P˜#\ì|Œ8†¥ÀxŠœrNÅŒïí‡i±¸^ Ú´.¯_ïõt˜ç9TIöôᚆUù¦¦ÞÎ ý|½½Ô`Ó삽.zðõ"€GÊØŸè}U3†ë‘°®0°6/©*ÅYŸ›ÔÔ3¸:ß_•îAÃd²1ò.¹4R&Ýj53ë ´ÑwõëùÉŸÞh;þuWH8"œgþçR g&Ñ ¢;ÃB‹lÊjXêB  ¡o,œ§ “ww\éj¿@÷¦³­³³ÑØ9ñ®ã\G;õâéc§ßçë×^øÃš%‹ûkz'ÃúÇÑliP ¾u#^!Ò‡A!Í3ÛÉX#‡&¬‘Ü„÷DGÇà GÔø!¹0‘yþ_ZÐ~.Ø: £E¥Ä+ß F•Ïnž«w¿e¶¨ºø˜EVt/¥% }³ÊDiEªbMž¡>ò2² ™nT‘Q“#d~³b+ãeôô-ñÀâáų ¾à6B­ QòmÐsž ö x^È>o*ìxtêå ÍÐE2ûà9H+’/lD9ÂhrîDä»7†Âe½Êo¬÷˜„B³%ÅÍw š’xµŠCÁ÷ãCáG1 o‚ã§0NJûÉâSëC‰‚ý/ñiÈ7bL¹,«”Ƨc˜à Søññp§ƒŠ£[+ˆÐ÷à‰ Óè †ãC©s=>´Iç z->ùátÈÍ…€IÞ¼Øê“Mk’¬†TÀ*"ƒZ»¼Ü<Áá=aoD{–3¬IêCz³:Ÿ|C@¤4Ó**,z—ÚeË¢üèz«‚Š5FÜ™+r6ÒˆLqs^—aZŸfnðë Ís¶5*^\;‹ÍÀÓM 7¹ jTO#„:Ül¢-«8…¨{Ëbå<|¸^Ž{×öÜث×{}?€ß¸¾SÌhᑊmûζèLBm›m‡c½*ÇR›…O–hG,J4Ó³BeQÐUBhX}”ßœ(9`Ò?ç(¯Ar¿@@À8Iõ@¢·ZQ¤~lø ƒ›×ýáß:Ξèî:G :Õ„ò…Ià§®j©F¤²k©&LP¸žjº¿½pñò‘m.Rù¥óíç/´wÓùØÕÁºÐÙÙÝq¾³õʹ“ÝŸý½å—Þ¬™[–ç]˜f$± ¶ßXäX]`«Ë‰Eí /:«rlôÉ+“bhϱGI:™Œä(ÿ¨“aj qS™n_0MsD?†“ˆCU–hY–Çbf–QY‚mžÏÂ+Ô9zwÌ:Ùê¬0Qˆƒy¥%r©?;!èVÃo0Ç&-JwU îÈ W1’–¯ ªë3;û%î. ìé—º ñí>½ýÛ …ƒÒNĨó ó7ä$mÏOˆÜ}Äg gxI`¶á‡Ô¡CXZ™Šâz|vôKÞÕ?° kŒÿö>~¡X^”L|võ nÊOZŸéÅ+dg~ÊdNóƒ»òRŸ}³·§oÍKÚœåÅòµ!ÅJ|¢,õÅ  Dj‰¿«ñYb–Ój‹•6æ;šòlü€Âº— (ë³EÕ·c@VcAGï•8rfxq©›é@&HRÀ[XÀ‡ýÀ8r^ g±øÓï]ïÂx Àù’dYmz âÀA‚¾kJÝÝ+qoŸÀþO î±k@öæ’ô ½Ó6õÍ<0º×æ>Ú ÞDÊõɾY;ò’“mû ¹dÿÎÜä½=ƒú廯$µvþtKvü:¬4R¡ë5öãzÃûá×Ë%ï홲»8ˆ( ŽäkÓM¸ç4PI¢8§_žÌÑR´+Ål”ú ¡캆RMÀ³:Ë€µ±©&ͳf&•dZBmϬ兙s‚¾IëDåÑ$O¾$èbîíÓk{õÒOÿòCŸË:.²Ï ”kUÇÿ@ªRE-s5ÕÐ!ÿ–8«!cNUÓ!˜ä—èÛm€Ùm®\ê¸|¹ãÒå.ªÆ–s™¾sßž=zñÐ{g~÷âo–O¯,Ž_–eBƒ”Q™KZœµÀ+H‚<Œ–$F£Ï\™¤ÙÜ‹¹6ðx‘•ñ1 K5âÿ·Ù¹^ÕL§œãê£lcä4+üÖȉº¡×Ílš©t$¸VÁÈ"ÕÂ7\ó€y.óÍîùñJ3ö•¤lÍ[›iiʱlí·ïÂIÛû' ª°¥oÜîÁþ£sX»ú'¯/pmÈwí<04ggÿÖž 8†s6æy9,ðÂŽyœ ñ; ‚¤š-Y%'4§'nÊNÚB]]º¥(usÏ ¯Í=â6åÄmÉßÑÓ¿·KŽ ¸%óP„NT*7K˰®vIËyNÙ%ÀŠˆ /ô0”gç|ŸæP| ÌqD-IˆÁpveÐ ŽÓèOŸgûgî­ÀŽÏ–^Þ] ñA„áZ|²¾‹O¡{ï àÓC³I5[ŠvõÃH%¡E”1„Z—éjÚš3¼» »ˆFv".ÒÍéñáøl.$8©ÍÅ©ŒãEpr<ÛzÄï,Jñ)IÙRœ¸¾ÀSŸn%^†aw(>ÕN©Ú&-#û9¤¥ BßÊ6À*z)xÑòœ¢AÁl…7P?Ö„ 7\:û4u9ÔŒë’c7dº·åÇ?ºÞ­ÿz½;ú·õ÷o*Á &~ÿàÔƒ#s^‘»¿o`s®gcŽkO¿à ö É]ß'muQÊšÞikK³êz&Wåz—e:Wf{`zâJ³«8¸³( “Í™ž-YÞíx3v§ì,ì($ÏıHü6¼8ž¯Ïó„÷Ãõë ﮷ұıÀ Íä©Ú¹d¿š€¦žó$&Ÿ þÅ t>Ñ,G³4fz¬Šîßdhà1²û”ÂjÀÛTŸíA³v¬F>"J&“F¨"†)#K$)S’F%'>6ò†½«jýõíóm§(::Ï2ý W5" òJ8ähP?E?øê÷ ¥PÀ]®f›ï¥’ÌÕTÃ×|ËIvw·8W]éÃð+ „ƒÉ¡àÁóbË×ç?ûÛÉןûÅ‚‰ C²šð05eéð¶^ßÓјkÅ0½: æÍÆ"ÏÚóôeÎJD´ë—Æ«²Ä‰¼Y&kgCf±EΰGÍõR(ÆVìœÇ ƒk èÛ¤6¦m,ÉÙÜ7wSßìÚ çSÊoágW”Š‚Íæ üp×Üï[ò}ër\rÝ[{%n,Š«Í0£Ôº0>š>'+<”QºƒoJÌí>ŽKá‰RŸ“ð0¦œô‡±Jº4¡ãÖÉ⌀¢Òtƒ91…î¡ufÎÝe¦Š,Þ¶ôò?=,拏zíÜÙ7°½WÂöžÞ-yÎMÙæ ¬]›rLCñ!8×ã³&×FoDØs W+áéL|Ö…â³<² ñ¡?L|&‰øÔ‡âCˆˆ-⳩ßÿ·ø4÷ôÁþ >8ƒü›øp ac`š £"\È,¢0bØ$ç…YÊÐùè_ã#Ð&¡ )Τ³uR9e§÷j|žÞãà-½vîèÀikñÕø4§7fÖçÄb0±.ì’Õ”km.dÿ8¶—¤nï—±­ofs¯´µyþÕ9‰MÁ-%ÙL ÖàÅVà'ícˆ;åVs¶·)ÍA×%¼ðdÙÒ;‘ëe4I?…„JÊÝQЗ à…Û5êЀ9¬1-[u›BºM-ݤ’nÒHwš¤íòñÙý¡²€ó GªiÌF5BÕ„¿>›&?ˆSÔ¤©Ó~´ÞTüãýÀÅ⢾5Ǿ±\·ý1«ú ü¶(Ð& “àã¤ñ15øjå'ñC¯/H[ŒŸg}Ü;Åk™šè˜™ž<¿ «¢ïÊ!ýç•߈溜îgÔÜŸ“1ÿ–ÑOÕ®üõ“û¿|ï¯Ξ&Ïœ>süLë©óT "¯\DßW,Ñ¿BEÇO–lHajßùjùDMƒM·áªFd¾€.±èሠè¼È6hœ‹Tƒ(2^º®œ;}ñ‹¼úÌþ)wo]´wLþÎ~ñ[‹›p>*p°CrÌdæ•)TñYåto`µ‡POPu E£¤A}H6¨ÐGgUºº.ÇŠSöï¹½4gÛ€¼Í}²hvqD]–êÄ:ë^˜Î°ó˜r®Js×Ò+šEæhòSoLÒç™"監ÐÙ&§1îá!È(™qÏD QÆÐ<?-‰S õ­X % 苜Q€üI(f׬t[” M´:Q¼ åd(˜RÑýÆòÄgm˜%:ì )„Ø*£ß‚ÚäæÒàþáyì±çGàž)rNoßÎ’„M=Ýœ¡ˆnw×ビ‡€‹› p˜MwÝŠýˆÏªP|BF™Öu¡ø ×I|šKD|Pe!> |Æéð+ÿãø0ü½ŸrG4ñÓ÷Å¢=­$&t œQnyåØx-ñ*ÌñZ­Ï§]dU,4Ó*¿Ÿé!Ä,ÚæŒ®P’EÉ¢‰þXø°L|¦Ùd°Œ×'mêÜ3"ïù›‹Ÿ Ågg¯„]Å {úøðÇ®eCc#-5ì6Ø?É,Á¯ÇãGŒðBÍõÃs©¸ñ¿.së–„öC}º§.E0ÙèaÒJBc?|½f¡Ýq½a ö™úƒáƒ öÐz\¿öà±ØÈÇìÊé~ëŒtÏC ±7ÆF ¸Á$c‘ßåÐ<äÑOtëù¾ @ß %0ÒD‰Ÿ™™Î[øzÅá7´¸^ŒM׆öÃÞþe?ììåÛÓ;aW//W½­ÈÕ˜m^•j€< Muq¼ŠÓôt”= «/€@Ó=ŠÙ¾˜™‰ºY)®YéñósüeéÓ²ü÷'ÚouÆÞè6³‡ymƒãì¥^û”ÄGFÞ¼xÁïžÞwâ£Z:wòxw{k{gkkçÙ®KÃ’¡T# B¿~ÚTÓ)r‡èÿ8Õ„ÎPWH5@I5Ô;∊l#$nBNˆ« ™¾öÖ‹_~òÅKO7Þ}C㈞{ƺ=¥‰°îè› :!ý“±]ÃôyM†‘…-Ëj”šÒM«‚:œYМ©pE.öDqÈâ†EÎ…ÃSNZŒ;©“y²0ú„Ñ@÷žq¾Ïsši±z†ƒ°m4üècDÑ­ Ž¹FX‡2,T˜Ï"„YÒl›ªÍç8=·! DîrЪ˜bSå± ¬[Ñõøÿ¿ÄÇøýøp`ÊBR…Öb"¹x:ØCñ1IË|p?U Ì”y¨PÒBýx±“k §ãE ª¹Þ(Ž~ÄGÌp- Ö£†«ñ¹“k!Q;d‹Ò¬kAUõKÙD£¬ßÀ ¬úö•úv—¸öôõîï¿»$NØþfàÎlŸ=ÌÐÄŠ7LmrÃV²þ 7>‰šý@{V؆á=æh6#ö_º^!‹Ñ,6L8†/õêÑ›a– œEAž™å{8Ù2B'ä/ÔJ}mšâbG»tà ²áJé&¥tz>ã\„ô‹“ ‹’4ó}rF!%0쇩¶èïï‡ÀŽÚe‹ÿu?ì˜q`xö¦žŽí}Ý;ûù¶ôŽ[ßÓxE¦YÉLj~’qI†½²‡{q–mnªá‰xÅ£qŠ»mÑcmÊ1±òþJ©GèˆT}cšodzb/Ÿ­(ÞqÇÐÒÆª%oýêç‡>øÛá~ÐÕrº³åô¹¶³Ýçh€ ì Ö· šCw3Ç”‹h‰‹TCžùiS –r$“«ãóÐMtjBI…Ä}?ÕDyï[´ˆ¿å¯ÀgK$ËÏœ9ø³ì›×?nNAvrm¶uy¼²>hl̇ÅÌ¢ lÊ4Õ&«—ÇÉV§êšsí›óíë³-M™ÆÙÖM…‚ÕXà„›€q t`sír°©L©È `Ùü€(¯Ü\ò&ÜuGéå+¸! ¸tøPÂ׌Eˆ8‰ÑÙV¤×£¾r¼ÏY‰—±S æà± ¹+±¤Ô`t ñ“5ɇ§vS–»1ݱ…G‹Rýb»b]U‚¦.ÝXŸcªÉB{?j¦Wš‘ ‘`ž™h™–hžìÕ?h–£vK”[¾‡¬h•Ow«Ø“•©ÖšÝݰ V¥ vâ³.Ǿ6 Ÿ#õRŸŒ$L|è †ã³!çj|î$>‹D|€ ê|âø{‰ é忌-ÓëñÀÅl«Èý÷ã.—š-ðÉËúU R ´M°äó#å ¼uª@kWøpsˆFÆaY¢‰c€ø•3¼Q³|ú™ñÖY ¶ ÖÉ^ãùíéF™4’ÐV{Ô4è™>B[•íæ ´<1ª!M±>MmS–ac¶Š ®ãMIaZ€0îHRë  DÓàwÝÇ~ EL+˜¹9¼¬%Ûá\L¥Êõ‚=`?Àq&V4“aT!±Èì_ aižbUò£œ“å{ÄomVPÌÜœl];ë±Ý+cˆ¶«|æÂ1ƒÇ¥xoµÅÜiVNp⨞ƒg=ÈÞå9ÖòÔ˜>ÅŒxý÷ÃMQByÓÀ‰6ù4ÏÕýP›í¥˜_’DáªdØXdk(ò¬.Œ«Êv–Ì3ãtÓ}ºÇ½ª‰nùD¯lzjLE±såдY½SîI±4ÉJôÒ ñÆIƒ{®ŸóØÏ7­~akÓËOnÿí+ϽýÖoÞÿே¾þüdk CœŽsíÝ]ƒÉ)ÝBhïbÇ%8 BäST5ÿšjB™@dƒŸäW¸ª ¿Âu˵T#’ɵ’&\Õ0!]#1V˜T;"ÕÐFºøm÷é3]_~÷É]wãÇè"Ç„ôÓ@"á›éŒ.‹×ͰÉ1$Â.º¦ä¸a.OTqjØTèàœµ³ØùÔßóÃû&ì,ÅÕž:SW•¬X–¨À™Žç{¥OÉ}]é^Åt=f…I¶bÙ&î¸ïa¹CÎY©Ü,ãÅQôR¸­J€Ä<­ÐyF¢éhZÎŽU <°MWn J:¢—È)‰;]hÑõÉÆª8u¸§ÄÙŠº#ø®ežèš$üÔ¢W$«ÖdÅn-ñîž²oD`KÏšž¶ù©ÆÙ~ã¬äØy Ñr<¼>æQÝ£Ùævй  ʨ 8ÁÁt©çÅ1¿›ëp…H>0ºÊ5ôŠMÅ–m}¬ûyŒHÞ;È»£¿{cOôuËý d-—sïÓ0¤Þà8ßÁ{R›z&`ø»"h†ÍŠtÉ¢¸áfe‰BÅ…€çiühÌ®Åǯ™@ÔÏsêfZT3ML78ÇÁ[Ò¦¤ãP¿ab¦uZs~¿„ò¡é÷dÅ ôûº´ôÏßX>íÏï>ôöëGþñçã‡>8þåÇG:~ò›gOµt‚Gé:wé< zÜâT  æº  ¶g¢=BIsñÂÕT(ü$I&üMøGC©åß|Ëð]½ú¡TÃ{þ;ùç`´‡!Oœkï8üÅïwí˜?jØH³v„RËÓ\¿›l $ ¦õ-O.òùƒÈ ›Õ0ÈVd9wÉØ7Ä¿«Ô¹o€í©A–ýb ²>=Àüd©yOÃöBí¦ÊõéòÆÔÈÝ=MÆ¿0ˆö¾mkvì®|Ûž|Ûæ,]Cjäš´H^Wû#R”)š:Ÿ|©EŒJ«Q«â4«ã5ÉÚÁØ&¿®Æ­¨²DÕ»µ«}±5n]­'vu‚­!ɵÊg[áŠEÙ ,&‚UaT¬°ªVâɈ•M*÷ʱ4¥­ °Ô1®‹UÙ²¤ˆ¥‰Òª åîÞçoL}fdòήæ>¶†bûš÷Ú¾>ÖØpEÎÊÓÂ4}Yš3Í f‰õ°9ò½t—RÄgXTT©$ë'EŠˆäýEŠ˜£âTÒúmx`çˆømƒÍ;‡÷×ï¢Þ?L»o¨nÏ ÝÎþú-½µëó5 ÙÑõéÊ­½O I~z¨Ÿ7ëó ›ŠÌ›zÆ6öЬJ‘¯Fב4QÖ¤i8ŽaŒ¸Ä"-7GÔÚn͆ÝÆ@ìúC}’z™7Š*=CZ1dõºdýº u]Še-‚<ŸÌQeÀûµÒsJÎ, ¼È·<5X‹½œ» ³ —m¿îqÌõÒ ÃÓGgTMX>$¾jHüŠÒøšÒ¤•¥IÕýýËúù–$Ìíé›Qà™œå¼Ó£ëPÜ“nPIl¡Á‘Ò(MäHuäH•42Z£n¥”¥¢ùÐæÕœïÞß/qg!­TíR_}ª‚hpÕ+"W%*Öú5«âÕK­‘+½êJG´K%P‚újr“§zfÄ›&Åi§ûcË‚–Š Õ5©V†˜ÕÞ SŒÒ½TM ±½£¤R‹zß²²¼rð‹·Þ:þþ‡mŸjýäóãï½ÿùïÞü˳Ͼ¶iã£ýûÜŸŸ~wšçál÷‚¾)µÃ³ê†¤V—x›Æ­íçªïm¯)2¯,´¬,²×õòÔ÷öÍIROqFbiÊ~¯ž;PæAæTÕ«…4\™/I’ÔC=,˜|ß ’U3{yËê—7×l]8qñ½ƒ'”úGfX5÷ ¸½¾ú÷??øá[oùèï-_~S»ûìi¼:p9élï:ßÝyᢀÇ1b¾öëú}zsí¿‡ÿ뵯ù¿úÿLéj(gD$ ‘j¾½tá"gÀ+§Z>þå/WM?>'cbFÊcþ¸[ôÊ›b •ÑC•Êáªhä&H;(ÛP=\œåUÔç[7ö1ïèûÔpÓ û©ž®?0D»¿¿rOIô®Þò½}TOöÓ=SjØ!í.Pí-ÒnËVîès ·í…R÷3},[ó[óå³¢Ö¤ÕÉRƒ_¾&YY'kHÔlH5nI7oN3mN3nN34%©jR½[Þ¯]—d\íÓÕûb×ú ŠÄÕà¬1 tòù1åºÈ%ƨ }d¹^Zž¤¯ôƒ+Ö.K‰ËP•G±<5jEP¶ Tm rM¶ª1W·:K]D7[±­?öÞ ½kŠ,uyÆÚ¼ØúBÛêb÷†þº¢¸å9Î¥Ù®%Ž&šåã Š[M¶á:ûÀh}™ªTŠ(•¤A!Së³åu= ëúj· Pï¡yj”rÏPéÀHÅÓãöŒØÕ/rGIÔÎõž~†}¥æµ‘[ uÛ‹sÕ›òÕ{û™ždçus®ª9O¤£ÁÈêyMšº6E½Â§XãSnHˆÙˆÝ‘jÞœjh Äpc.KˆD‹xi@SÁ1É'`\“Ûd\¯¯qiPUZ€)þÚz j2àúÈ4Ѿ^‘¢i*t!b€¶dU¦uežoQ¦cq®g¼[v‡UºÃ-Ý䔯ú¤ñÙšÉù±³ò- ûx– ð/*M˜šk½×¯ë•qËÆ-c<ê‘nÍh¯~”S{sœùV¯ P+ý‡¼È¨"¹¬·BÖO5@&¦·£åÒJi-ž~©`'°Aß^l[Ÿ­"ÍEºM=õóbÖ¤+Ö£×¥éS¡¨hV&hW§Û׿'"B;Ýg¸ß,»Ãq»)j”VºË.Ÿ”h˜ˆåT[›î`nز.Ã=?!v¢×p‹U•/—îéÕã—;7}þöÎ~úY÷‘ã—Oµ]<ÕÚu¬¥í«cG?üø³·ßþËK/üjÛÆmeSÜT:!Ïó@PÏÕ-îë©êc]Ýßµnh<æ¤u%Žå…æ¥ù±Ké8•&Õ'¬(J¨)N^ž—4;ÉúQq§:âf•b„Fu‹ÝÎ…»¹_üþù?²˦çöìúí«/¼÷§ß|ôç×Þ}íé·®X2ñ¦›z¥N}ð®kêÿôÛ׿øèƒc_|~æÄÑ®ÖÖ®sm8%á†ÛÕ6“ Áu¼–Qþ¯æÿæ?þoS¨jΟn»rîܹ?xm}cõ·NɈgq£AÝ?J¨Ù ÝAÜG6h›#=„Lœ…ª4icoÕS#b_½ÛùëqŽ—o3<3Jýä ª=Cä;Dmëµ£¯lW©rO©jCž´¥ ª¹GäÚ4i]fÄÎ"ÝÞÞ±”=[‹u;Kb·÷2mÌÓ1[_—m\—enL3q`3º!ݾžVºmc†µ1ÅX— ¬òÊx¬ÓˆæD† 7¯`óáu8Û J¢±MÄ´ï °Ub!m!ä»,E·2ÈíxUªª©ÐVßÃô‹›we 3µèšT58«­½=[z¹›‹yæÕÙ†5Ù¦ …îÍ}’¶ö +½®( DýÚBÿÒTç,§v’Y}s¨M14d›E?gTHŽ ÉnÄQ1[Y‘&5Góú]–7î5ÿ궘GE=CÄ3ƒ#ö÷—öö•öô‘‘÷•è6fElÉ•7g˃ÒúŒˆ]=5{{ë¶©6é¶ô1mîci(4¬"%æÅ®ÉE,N a×L[Òìø­7g:@‹Ñt‚‹:\e¼–:$GF9([B`„Ìα¢Üª)3«0à^`G§Ë€—_S™j^Ð/L]f™´=á7wkÇšeC5Ò@4Ê)¿-`¼5 ¿--ö¶4ÃÈ8ù„lû£9·Òí7Ç©úë¥~FéÆ$ã}…ÉwdÅÝœî¹5#þæ´øQ~ï˜@ü—=U’Š º\­:C”$Oyžõ}W´´° ¹¢Ð÷¸7ª’ÎÀøýÝúÁyÑ4ö47÷qlèåXWä¤ï·:dz'—•=<•…É33ÝwºÔ#Œ#-²[âôã‚nz·{´7¤;˜!b5Ë…3o2Éï1)î·öÕÊú»ÌÌ=üΟÎ~y¨ëäIôo¯tt^:×~âNg÷Ŷs§Nž;òeÇ—ŸœüÛïþôÔ†æ™ã¦L›=Ò*MÍ5Ï)t”÷ò²¹9*z{WôK®\Þ+±²À·¬(ayAâ‚t×´8ýÃíÐ(©¯L"¯&HR?·»yñ¢·_yå/oüöøáCÇ¿þüÔ‘ÏZ~Ò}ò“Sÿþõ'ëLzó僟¼÷·ÖãGá1kiénk»Üy‘Ì%¼NXóË]K‡W òþ›·ùÿ†/ãCà W5P³¾;@Ѳ¹túô·ß|ÓòΟŸ©\tW0¹g¤4X9"&r”Zº=Vy·Yq—>b",é¶¥™¦úó¶ÎcæÔ:w\ÚGc#&[£f¸E„&h>3×€ñ7WØI+ÅË…¹YàÊh!"Èà^µœB‰'ªheÎдIÒËd˜ÎÒ(ñ=Áý‚¥'ßR6ã€T> • CçÇ)–ú•MyÆ]¥¶ƒ#\¯Üdí6Ëoï4½>6æg£eGD¾0"ú¥QšGhŸª{z@ÌžÞê=UûK ä–-¹ÑMiÒ†ÌÈmùÑÛ ”ësdu‘ëŠ[ú»6ös5öqÐMªÏ5“U ¥U¯¯M2¢Ê®  \œß ºÙq¦Ç\†IN=)™Th§@}D‰bu¼BR™bVÍpc=;3!ö>½Dkî–é–XY©Bê-Ž3ÝôNRÜ<ûÑŸ7-ÿņ/6Vl.{¬ìö¡ÃâŒ#|¦á¾Ø!>ãèT÷”á%ç=þêæ5¯ïÚðÆ¾­zfï›ûwÿr˦Wš7l¯¨˜sç%Éñ½ãú&Æ òÇHóIKäé5³DOÈpÄÞj‘ôEÍÊ1ÌÊŽ™“©i˜¸n`RÓ€äU½½UyÎ¥¹®eù¾e=“çäÄ=¯¬‘Š£¥!nÍcƒóš¦=ðܪEOVÎ]?u‚QïÍôÝâÔÑ–¹ÙEoäN·q¸Q1*Ζ#»© Ç¯ŸÞwò‹ÏÛN»ÐцŠÓÅ®NÁÈéè@ã…_h­|sø³³G?¿|æË+'þyüW^^¿xÁ%c3Íã²c|j’j•4H'±GÜá‘ßåQÜãVŒsÊïµGÞk‹g”nW‰â~R?Mô“1 ICRSŸY¿îËüãÈgŸœøæÈÙSÇÛÏooùê|ËáK§>k?ü×oþñûS‡?9wú$Ÿ \ê>Í–Ëç»Å‘ãÚ §šðëÿ†$òßû ?L5´kD¯FhÛ\é:Ùrñøñ+ßùà…ç+î;:Î5ܬ¾Ç¥¹Ï!Ÿâ7ÌN3?¯ž›¬ªË·­-¶±ÿw4íxþFËs7ꟽQõ̘¨ý#eûn²ì¹Åýä­^Ö¾›=»G9¶ 3odÚØO·®·vM¡²>OÞØS½¹Ÿy{릾æÕùºM}=;'o˜¼©$ k-áó˜åFRáq}̸‚ƒ7ƒ¼Ãôj²Q†©”{TÒݪˆÛ”·*"n–GðS:Ùñfˆ$QfP`Ü%|élƒÔ‚íH~€&0×#CŒŽäÔX§bú‹á/-`0¯Ÿ°Ks}‘ ’sâ™7ISÒ»ô¨Iz„9¾5b*‚0vÙC‘[èNwÉaÏMwDðmÁâÖ´"Uµ:+fc¡nO©éùöŸ¶ül”>”[äÏ “e88ÚüâöǸŸézf˜}ÿ@ûžRËöÞ±[zê7ä©›r¢› ÔTz»úš¶ö1¬ÉWo,ul¿iˆ¯‰Æu‰“ð»IC^FÁpŸ™ïTKÔx£tGŒt£Jºßk½Õ¨­–ߨVÜªŽ¾E96:ò.µ1š´¨fQ‘Ž-¥nŽQ«•ÑWéÅ X.õ³èêß{Cùì~óÜþ/Þ}ëôgœÆêýÓ÷¿þëß>øÌ__|îo/>÷î Ïþñ¹§þðìSþùÁÞüÍáwß>ü÷w|øÁ™C‡@}ÿçǧ>=ôéŸßýùþýo¼tðõžùõsO½öÌžƒ[7¬[4牱#ÇeޤÊöÞžåGTèŸk—¡}0];9Ã8?ßµ¬_ j`FyqòÄTËíåHsT?†Ôƈ! ±.¨›ûÈ+û7|ø§_|óÑÛßüýíCoüúíû^l¨Ùrñô‘+Ç®\j½p®åbW;¹…%0'WgÔߣ0¸ѨÿßK5”4a$ŽHš¢3,΀"ã\¹t®óüÉ“íþô“Oñò޲¹³÷ž˜æ˜àUÌH1TÅ-ͱ,IÕ0nÌ3n*6íêoyz¸ãÙQŽçÆXÞdyáVËÁÛÝûnJÜ62yÝ@÷ªÞæê<ͲexU¤E- F.I•*Ó"VdÊWå)×jÖhCæ,ªÊTuY< ‘ÀŒÑa|²l”jÔÂGBÖ‰)¹6ÄSrP ¨šsûpË vq»Š©´X$Ÿ± év%`¨ &ùT‡|n\ôÂd\`´¹ÔäıÝ:(qÿˆàÞá)»‡ú÷ õ?3*óà͹ώÊÜ5(°}@`Û àöÁi;‡f°¶Iß:(­y`ê–ÁæiÍ26öO_Sì¯ÎñTg{ë @*bʃ¦=Š:Äj2ôuYúµ¹¦N;ûÛžî:8ÊýÂÇ‹7Zv“õç·¸žãÛ;hˆ¼M# SH¥r©@wAž$•F+†¨ÔƒeòarÙj%içMô1 ¥t›J§‹ºß¬¾îŒàíGOeÔ¹™«g>ñÊÞÿxû÷_~þÉá/>?vôÈ7_þâã[¾úºûø©3‡¾8òþG?üèä'Ÿž9|øÜÑ£'Ouœji?yêô±';sât˱SÌL:Îvv¶v}ýÅá£_}ñÕŸ|õùÇÇ¿üôÔWŸ~õÑßÿþÆ/ß8¸ïÝWŸûÝ3[_ÛÕðóMË·,œ8ýÖ^7çÙ‡'knMÖßäU¶)n°Ê†ZdC¬Ñ#¼Æ®¡IöG†÷Ý´lî_=ðÕgï?úñWG>:tèý¶cß´óMÛW_œþäïÞù#šÞëÊgO½mÌ yé½RâåfÝwëÍ;6lüêÐí—p¸((9Lm8¢€Á\ ãÊå6q“3*>ßÑ}¦µõHÛ©ÏÛŽÔq⣮ùðwõÊ+{¶½¸mÃÏ·¬Û_»¤rüö͹7?ðPaÊCyÉãRwÆÆÅëò[ÆÝÃ]¦l|`‚ïÑ1#žmÞ„¾Hß|},oèôC·…9QוîÖ+]§¯t´D~ËØHÀkùSNJ—„ùõµ’&ô&ôŸÿŸ;@ýkªá‚©†vpŽajìø•–SWN?üÆo^®¯Z{ÛÀÙY¶Ùi¦†ÁMƒ3Öõ‚o’¸`Êïîîû«±…/Ê80$iï@ÏÖ>Öu…Zš ˳lå©–™^õDàß0åÕbÜÀÓô×T›¸GæÆ]]ó|ÒüxéXà(°±cmƒÖ DT‰@Œ 'tŸuôà€#ÙÇpaÎA° ±A4DÁÙç®oŽS7ÇŠŽn^œ=.ú3µ™Îú|gm‘©¾Wlc©eÃ ÛæaÎ#Ý{oôî¿Ùwà¶„çîHzê–¸Ã,[™v³ífß=Èjqw¯äE ;z&b޼§$™7ÍùÞæî ˆvæzÖe{V¡¾èÓãK㈥A„š†¢äõ}R6”¤6—¦nüåýƒ_º³ÏÓ7e“жòm,±5êjzh«r,‹‚¦Y^åD³ ¿‡ãC 3ƒ cn.’É,Ÿ4Ã'Í"«$^dƒ)…ò8R< ÁŸ„¦fzŒS¬÷8t£Mªávã ·sD²ÿ¾¢žSz´gÏ[â}c¬–Û]öñ>ÏÃ>÷c>ç”xÇ´$çÜTï’ì䥅å=óf·™oHðm,Ÿÿá[o~þñ‡_9|â\Û‘ÖÖ–.TÕΟji=׊2À•Ëç.‚ÓºÔy¡Å–ÖÖSgÏž>wöL;‹û¨³ëbç…Ëç/µ´¶·¡ wù ï¹:ºÚN¶;zŒì—§Ž}úøWg>vèƒoþù×–Co=ü÷ÃûÍß^?ðæ [¹gõ–ŒskVBo{ÌÀ8ë„A½7Ìâ×»šµgË_~î£wÞ<üùû_}óÙ‘–/Ovµ´ÛÙÒÒÒÖÒÚvê4B(m'Žž>òÅ¡Þ}çÍ×^{éÀ›¯ýì׿øÙGÿø[{b˜º».>Ó 4x-DØeˤö+ß¶}{éD']Ü©ñÅ8¶«ãxÛé/ÛNî8{êÌñã§¾þòôW‡OñÙWûíŸ?÷òÖõ¿ß¿ýÕæzÚÈåc‡O(ΛâbSj¤Ò8ëƒJ4®~ÿ·¯}ýÁÝg΂„éèì>ù[¤£X¢óB>¡ÑÝÌyæÂ…nr‹Ð–}®. ÔLøaýÁúï^þ|9ózU˜9”C1tIOð±:¨mNœ;|èØ?Þ;þÖožš>~V¸ûl²²LgmAâÊL7žÛ‹Lš…,d|8 uI´/8° QÐkxQ{Ð :-Qâü2Í,œ¡æbüäœ\Ô®*ü²e©P855Zž,ÑBçt¬hÛŠÓž˜¿¯.äë§xÓ' Æ4ÛÆLOSÄúlŸ°Îò•'ã%´†æ'ÒµÐ-=˜i­éa«Ê‹]ž««Ê×®*66õ·®`ièk¬/ÖÔ*zijó£ç%H“-B%£ËSå¨ôT3#+0ptmÈ3צé„]¼®.'¡±$oIQÖ‰î1.Ó î53¦m_^õêÖ-¿Ù±mû‚3nvoVƃ™Á‡ÓýS'âžHrÏLv–½K2+rƒeEù·û⊔Šñ¥}_Û¿“Ä|ð^ËùŽ£]_¶·Ÿý+®´·_‚¸{¾íB7ÚÝß9'‰°mBwf5qùJk'°Uåj=±¥£ëÜÅoÏ]¼|î<@n/ûöB÷…®sg[ÛZÚZOu´µœ;}¬»õøÙㇾúä¯Ç¾x¯åë÷¿üà­Oÿôú;¿xþ·vcBý—W¾ÿÛ_r:;ñÉg~ú›¯Z[Nðhµœû¶»õòù³ηwsg ©fgδœm=uæì‰S-GŽŸøQ¸/¿úìä©£"#q÷  Tñ©EË2L¤°!Û`˜ÔþíåÖ BëàâåîËH¯\jïîj#™¶µµ‘¬Î9Íxèì7_Ÿ;öõùGZ?ÿèôÇï}ýöïßÿÅ oìÙ²eÅâûn»»¤àÅæ¦×ŸÙ{òŸïŸþâÐÑCŸq*;ž’å*†M¼áŸç#Ÿï•”øD±¡ iX…o?ôý.Q\Ï3Wÿð»?ù_ÿŽO,Ð4¡W‘j®/>¹à ã÷ ừû"§KJÓO?ú`÷–Õ7 »Û®Äeœ›ìzܤDh®Q1O pžÀ}‘猣ãiK§”„CU_fšE†är B)Ý„2ü¦>ñ[û$¬Ë·¯íaY_hkÌ3q£‰Êjì’Ä´6'¶!‰˜Ø-Eñe¿xCÁ³ƒs÷ô n+ð#èTá«Èð•ãæ¥¸y:?쉽Ǭ¹Y/»Iu›Eu#æ^wÌ]öèÛÌ·™¤»b#2E?«|ÄÍ]ôˆ;ÐV÷SDî€=Zã1F¼Ú$ø¡óãKÒ5€cqä\ˆ^PÌõ…z50øÐ°µJ“­ÒÞ`9yäHÌ÷ O“¥©Þ…‰öv-âÕÈŒàæLÊY—WúÆÌ¤¹ú û <<„„M¦<`Zœf^–íX–m«Îµ×yJâ×öM¨ïå]U`Ggmޱ)?¶±ÐĆڣkÈ4À¯ÙY|rxï ƒz•å¦÷”¤ûí^RþçW^úÛ¯ÿóOo~úÇ7>|í/¬]5iHßÌHi€^vƒ^>66}ÅûõQãaÙë£2)ïvY‚’42Þ»{YŇ¿{ýØW‡ÚÏŸ;}¹»åÊ·§®|{š¼qåŠpgÞ8¡‘+ˆ Q óp5/Cçoñçá/a{]Gªw Ýá›ë"‰ç<9£Î`Wû™Ž¶“­ÇÚÏ=wê›Ö_Ÿ9ö%õÉ鯿äÖf:sîÄÉ ­mÝh,u yÒȤë2?ê¬çQ¨2ç_¡ßAÅÒy”öÖÎ3m]g;Ï·uŸoG»@I¸Qd]ƒ°ŠwW?óõÍ1ê k¿„¾’ ‹Âl ú½ç;Úη·^h;Ûqòxûñ£m_Õrèóo>üÇçïþùŸxóý7~CVl9ôiÛÑ#]gZÀö’U¨gDþö;Üþµ…H¤•»þ×g’ÿò†S ×*.÷û×Èß}'~bñ³ì¤1~îëCíyëµêŠùýŠïóX0«1=9ï0œ(!ìƒõùLãfK™Ï²*×ÛT¿e@ÊŽÁÍ}“òÁ»" Y £!Ï…jÊs#z•‡GÉžþ){žþìÈ”ÃýO IÜÓß»£Ä¹¥Ø¾©È~`pêsC3ž’…”DSºu±5â’(äS М¡ý °Kß¹Lqh„Ê{TÜ"“Fj¤[„Êît2Ìx­l*sÞ€«:'‘ „ÆòLïŠÌ¸ê ᅢʠeÆÝœS )¡ëe“æ&J‹‚Ѭ…©ŠÅiJÞ”§DÏKŠb-ô+16 ‹KÓ¯SA…Ó=üM‹l¶[=îšl–M4F=fŠ~ܦ™åŸ`®Ëõ6öôm.M¡#„O7&æËÒ “T('£LÜöŽÈÙ?2o÷°ìíƒÒ·õOyndöó#3Üöä0XWqÛû:·Y6å[÷õKF‰eïð^u½s&|Cº S'½ûÂsÿ|çOŸþóïG>ÿ¸éÉßûðõ—wU-˜}óìH©ˆ„% V*¨D‰…3»ÃLulVðåæuÇ?|¯õÔ7¤š3—pÚ¸Î3T5¤rKø¶äÞø®‹bÞ…n˜«ÝLîÓ0»˜f÷¶ŸÐÃy‰ï#–øJ8"å„rN¤–jëîhï<×ÖÞÚÖv–E9е ð5ºb]›TüCy†=,Þ_ºýN g;/w²:.µ“pÎ_ê ¶‚¢ãñéøË¡¿•’(z(×WZßý'>"йÐÑGœt΃téîä…ôs©û<§1ê–ö³­àyÏoi9zœ!WGË ÆI ¬¹ÆP@Ä£Eb ¿ ß~ß¿û~üþ¿¼“ÿ÷ÁwA‡%ñ+Þðö@(ѲÏjíl9vùËO¾~í¥'ç?1­8à%Ǩ•¹žª âLÜÓ¸pu—-òÇ,E4»ÀQ›§^•'¯ÏW®-Ô4—˜võ¼)ý•;rŸ“ñÔД]¥¢½è l.rCŽÞÛß¿»4°ñ޾ö-½ÌÍùú¦lµÇd‹‰ˆG¨—åFi¹S±*Þ°Úo®IŽ-ƒµñR&¼©¶Š }qб0Õ¾,dz’îêlúb×:X=bÁÞ@XTÕeÄÔ¦k€ß°êÒ4µA ººT ´ÁÆìØÆ Êoõ€œÊšåª4ÈzÊ3þVU º<Ž&’]’¤FTöS}¦ <^S®zCvs±qï@×K7¥ýú®ÂýƒŸìß?п³O­!šB7PƒÙÓ/°»_H€Í½ìëò Ùª5Aù*¿¢Œ)¼¡­È;ÕRi¤4&ÎõJÓÚ–Þ?qäLJÖÖ£mG/¶ï>q ûžÚú\MùŠq7>w·÷ÿ´÷ÝaQ][û‡ÎÐëP‡aÊP‡&¨€Ø ö5ÆØK4±+Š¢Qƒ½Å$&Q“››¨1í3×ÄDcK@)Â0ÌÀPå÷®}fF¢7yüòÇýâïržý6gNÙgÏ^﬽ʻíçË¥9];¼ÔaElØ8™¤›‡óüô~ð.iîß®W•?jiDÔ¶¶`ZÁ/ÉJ&M äE;Í&¿ˆÖ5Â~ýuÚ¦'4Koïèa@¤cúY@› îÌšI´@ÙÇÜC â­Ü£x,m‹ö™¤‚hÁ„ꄸ|X}™á·±¥Iˆ|r1îÊ 6Xc„–ê=•0u/ÇP X†ȇÒðÌr4«BDÈ#4J>Á @ހĬÄÖˆ û•&zTO6^&_ÀÔx„Áu!õûçAæÏ0‘}9vO;ý¨ÝèËbPCÿ³þEOµ"Z_¥)¹Sw÷òåØ:uÄ‚¤ÀÌdÿ­=ò»xæEÛl”›­•qRn™?·¬¿0ÀvJ.>>F†rt°÷îû¹Q†/÷NŒÍú`“e !gùþ°÷b-Z"®ö1Ìò£¨Ýl™Ñz™Av·Îù8'Ë›õÐB¹ ¹µ"A®Ã©#ö¶Yï눵)ÁźÚÏe™ÄÖàL™¨Â±}¶Ü_p !HާµÑ®YÝ×wåvåvv_Ÿ Ìîè°6Æfq·XÆ- äVÊr:ZåuqØÜÅys‚óÎdñŽÎž[c„oGØç†ÙäÊ­™¼»Í‘öàðÉvÚ›R”3’”wF;çË-vFY틳{?QŸõÎöÛbyáF›#M³ÚìQ $vÊh‡í ·ûz,ýnLÈ·£ƒÿ1Àkog*â•óä@*Ó pcÂøƒU«v Kn%r©@à–åoB½Àåøs¹~†,À‘8ÛÍtˆ%׋¾%w>óñ¡×®T«µ ê†FuCƒª>Žz%ЦêÎ%ÕµŸ Nù纥™ƒ{Mñéå8%PeÜ#NO\¡ñGS Þ+Äö$ä€-w@ sø2¹åÁŒöÈÝk»1ø <Å܉¦Ûbó;a-Ôê9¬®S˜jðT€4;â]¡GæjqyÊ™Z"4µÖ4Á·E…îMæ zm’%\¥ àôÈðGpÎÿª¼(ÃZ‰÷¢.dß ß%l^¤©˜püA ­m‚I¬´©æ~ÙõÓ_lÏX3²óÜx—…ÑÖY1–»{ºîî긭³ Œ«ow²Þ”d¿¹‡{~Ïí½ó:›fʹÅ~Ü_nu(·%Îzw²ë;ñΈöAܾd)¦Q ñÃò—ˆŸ_"â–ûdšmǪ[ùqŽÇí .;ãÝÞéà¼YîŒU ·†{¾ì.ýENæóÁToi6C6i³×f“ÌÌÆ64㳉Áxs,òe<ÙÚøu+¬0¦èÁpˆÃwÃ,ÂcÄ܃¹>Øsoør 8œ ni—fL¦ãNN¹ Âõ ®¥MÉž[S¼ò’<6Ä;gÇØg†Y® ·^i›mŸa¿&Ôze€ùrnk´Åæã·Ã8„&n‰6Ì5Þošg²+ÙjG¢Å;ñf›ã̶ÄYmëlNØ×ÍcOŠp['ëœp.ÃIÜê@QFº»ïè$D|ìöxŠLÞ—´+!hS¸7¡X",•`¦f¹>ÌÔÊÛãw'8íMpù ›ß†(×e¡®ãEvé÷]KÜ¿t¡¼¸ˆ­rXEC#¨£«yûºÊŠ{×·.|âÓ¼µ ÷íå- 7æüXðü´AΟø¢U©h­‡.èh®V©16Ø áUæJ€¬aØŽxiÔ0ŽLLt3’Mº'Ð@Âtª­ µ‘6¢=„³°a¯Ÿhð \{F›‹Ñ ƒ*‰6ˆ ›×èy@t…`šI[èãQŒ¿PkÃÑ"šþtfv"ÀÑA fè&G¤±`îý‰í‰Ї@&_8 ê³Pá0.Á}ŸÙ3IÔI!îö…ú…=–½åß~‡7Õ·™š­ïRT Ç¢W Å>Á‘`€ô\ó,6jMCyUùõë§~´vzæ ÈŒd-½%y‰Ž[`–`³!Þ*'Îvm‚}V'g$Œdu´ÎŽµÌŽµ^cµ:\°$Àpž'Âz¹™à‰r£$_êfN¹ÏnFÓí¸ àÍCÈ.hµàò5D˜Mf ­½"5[èa€Ó–‰¬2Ä”Ïëá´Dä1O(œjcûª…ÕD ›Wmì_³¾æ$œ`‡¨W›ñÖ–í¬¦8ÙÎqwœïé4ßËiMß‘žKÂ\…:. uX&·_.·Yf¹0Ðxe”Õú8Çõqök"­WÉ-°Ïîè¸8Tðfé‚óåQ¶«:Ø/54Íq ü ÉŒ–‡šgÂaM^-ûU¡V2ÓÕÁ¦«¸¾\†Œ[a´)N°¹³Õ–.–;YPI°Ú˜`o—ïÓÑq},*ûu±ö¼fµ*B°HÆÍƒì…›íÅMC  37ËÃè-›>ö³Ü¬&Ùr¯(›¶h\,õ3X)3Bà ʆ»Œ Û©b‹žV\_©Ûç»óÞ­ªª„kµ ®Í4mNÀN}C5¸F`w-/¼Qtõ—_¿?‰½EG¥ÅG¿šžztß^„¦ -¥¡RÝZal}T5RËô ^xÈ®‰=››ÆÂ£ y– G ~ѵl“:˜j#ylãÿ>³Ç¿x¿çe2Z©"¡ç2(c­"b729󿟺S gè<ŒðàCäQLÛÑg˜.àå›O‚†]¤õóóAíˬQ¡ÿ•F Ÿ)&^ã‘}Ê7€vÏZÄpñ…ÁVÏ`ŽjpÏçúD÷γ¿h§þ«ÄWAÝŒ¢}ÕfjÐ jë±¾BkcUkSy½ªJ]Ruÿêåcï¾·`ü†ô˜¼^ÁÒ¢ÞK ?Ø_¾¿Ÿ|OŸðm}¢¶öŠÞÒ;zgÿØ­ÝÃÖÇIWExdÈݰ_-Îîà½:Ì}e°Ë"°xˆ­3dB˜Yز!pÎ9qð¹lïæ·³GÀ޾TRrc°t—`Š=ñG‹Ž-¸Î_±4anŒsØ6‘õ ã0 ƒûbýñÞ6µ£á8¸œœ“„¤§q ('VPÐALDÀ OÄÍgrŲ`Áb™ù›R£ù>†(KC­Þ 0}Kf¶2ÚêMv¼Kv¬3ÊÒ@2g [Sn‹xé·cݰjN¤ëæÄ€‰²Mɲwz‡îM>8¼ã¡Ñ{ÒÃv¥Ë©¤Eì±-5jkßÈM=ä¹Ýå[úÇ­í&_çóV´û¢XOdÖ¬èì½¢‹×›QÂA¶½MÇ‹Ì_ó±æï2#Àc¦¯Ëâ•Xl7^œ—,…e 1û»yîê*ʉrÜØE2'T˜dÉu÷sÿâý½×o\)*)…®$d '0ìA3ëG…F­A„bbëÔÅî]»rþüO§ÏþpêâÙ3U¶657Tk4å^ÅäI÷ƒÍK*aÈ2M` >jrÓòó¦ÀèŒ-4· ñ$yÀèâ “5º_ÑC þÕ×QáŸI$U@[ØÝpCl¤ 1ãOSóŠÁkžˆ¡Xcˆ5›®£§`G£— N޳ŸWR×XÑ›gµÇ™Ôãí¨Kñ/Jx¨ôºÝˆ0'CÍâÈà ­ Þ8¬3ÿ¢B'Ó«óð‚¤b1Ц={.ÐO}p‚ëÄS_Ñy´ÊÙK 5ì»`c² Ô°îå9‡8Œô¼µQÕµÊZdkœ>öîÒ©3ã}G¸qC­¸n¸)7Ô˜dH.!$ò²ßÿ‚× kìSÁ´`À 0ÒŠ<‚{áû@PØà¦hnVæ’šbí?,0ºØ×t‘”2HL—؃q®¢Õ!^ËýÜx ÇÙšŽ³¼âh=ÞÉfŒƒõ`Kà ž‘uÞÕë-à9 ^ñr˜,N–8OÛqŒšOp·œ*µŸâ¶8J´8ÊcA¸ËB¹Ó$'†ØÎ²žh5Ç_0Sj:]b¼Dî8ËÇt’;÷ºˆ›æm4ÅË`²'7Ul8Å“{Õ…ïÀ!chðʆËuþеù`˜Ä—Û‘›€Ó„Ô?(é\ª×Ë„,·h$\Òpu0à:sÉÖ\?7óTóžN],‰- ©ˆð™p fT⑎„{:w3š&1Cf¹8È ˆ·ØÏë—!Þ/3V43Ô­3äÒN|z¯ðv5Á Y ¤Âž…Åh·€ö¤²F­Ô °¬º ‘"j%fÈpñ€¨áeLH¡þ}uô#ŒÃdÉo®AŽXåõŸ.ݹkîè7×ô‰\Û=x}Jp^ù¶>ÑÛúÄlîµ>1"«[ÂÒä¸9#ÆøKº9X"ܽ§Ð~tt„Ômb€çÜpŸŒŽ:Ëòàê×ýή¾9\à8ž‹(9kàÏ2\*6BRäb‘12+‘#‰õ‹±P&˜0¡ÕÀM¬ Òa.ï4¬oF\(d„åÃåÈüDäòsI†\/ ®È͸BÃAî¦#Ä‚±¾6ãì'R™ê<-ÂmF”ÇÔp× 2»‘ÞæÃ°¼H¸Ç7ã±æ¯ùÙS´yëìXÿÉr¯Q>¶c}í&ÚÏ‹v[×#x[ZÔæ>AkDyÉþPlr:zgF¹æv’ì캧oèÖ®>Ùñn»ú‡å÷“g&æì29Ò7ƘKó÷Nñrí-“¤†ú÷ ’öôõ ðê-ciožìí2!¹ãâ‘i³ôÂÂ…ý‚}ÇÅ…MõŸ.wŸâ87Èá ?›ÙžÆ8œ‰ðWã7dÀ‹é⣽u•ªæM$­ºoÙÐÅ1ˆ¯ÖtðNcæcF¦!~éš®‚'ˆ‰…~<ènÂÆöSÉ×þÆ<Ëgöÿæ†üm@Ê›×èðC ÿæÚ6WñUý9P¡W{®<½Ësmyú«ýîÚ¶hsÞïÎùý³Úœ¥“Mþ‰mo¥¯ÿîìÿ¯þ¡qúøI}S]ecUqkU¡ê꿎®_051$ΔënÍ¥;q#\ F8pC¬¸Aæ\ ®·à\rSÃîbÏñI KF_ÿú«;uè/Ær±ÐLhûÃt¬u È wØëùvŒG® r9çÇzíK x7)pO‚oh!e˽-{Òµ šÏðw‚†ƒµ2K„ó<Þ;Í’ _óq^ q,uN÷ õótèél3DêÞOdÝ×KÐßË"Ub5Pb=Pj“.µM÷³ë'ô[¤Š-±ïëiÞÃÍ8ʼnK¶Gû¹îx [ƒ~.‚N‚®Ž‚ŽÖÆAÔñ°èç<Äß©—«a¢€ë î}gn‚ˆhiå‹Päw#ˬ³X‹ë!HxškËõYŸ»y7G“Nö–KGݹbNÖÞìÕÙógOšÖ§Cx°Ð^hÀ NŒ_7oÖ±ý»¾ýðàWïïGÇÀÖEs¦u‹šÔA<^æ<-H¸8R².>pk×È}âvöOÊî7ÒÇ9ÞÎxJjÊ¿Ž\­z ¨,†…†YøÑJc‘ ©ö_Ôñ)¹kYô;àQç7ÇÖöä¿Õ@æöüþoÕÈöÆüåÀ…† ´/1ä­ª[kŠ/|v0wʈeCRõ›Ó%xz´tª\üz¨èõ A¢.м ÖoDzE§½{éÄçç?ÿ$ÑÜÌ Ãg÷è8+18£gDÞÀ˜wúE¼°<ÌqM¤ëú^—]à°JæÂÌ=‰Á›#Å›ÂEX;i¥ µ›±µ°­°8)Ó\$Ì rEPMf°ÇJ¹dQ˜÷Œ`p¯£9nÎu9 —õ ô–ˆGEˇÈ}‡û §f\28L’æ=(Ô;-T<ªƒlx´ÿÐp)ޤ‡ˆqLJ„‰G‡x—û’ºuw³ë' ‹ #G9¸jÑñüœS»7^=/{L¯9ÉASb½fD»mM܇Õ%uÜ›µ»·|_ï°÷ûË?L•qlLâ§£“v¥ÅoJOš(—¤ù‹w,œ÷?‡þròØN]úöäW¾·'gmƬiÓG=²3¿à§Ó𢻝_)½z©¾¸PyûƯ_ûpÝ’œ é“cÁÍ2ÜÝz’ÄižÜ{y\誤ØÑ=\¬„úîÏÍ|øÛMé#¢þÏ¡#G J[„a0£Å™¿-ÚüåaÜ~áß¿5¤p`Έ¤û:õãZek­¢ªàâ¯_>°rþÒ}Ê<M{yÚò*$¿5¤_þ’7NîßqöØ'·~:õðÖÕò;×ï]þ¹èÒOç¾øè`ÖÂECRFËÝÓD&ÃÅÆ¥óB„3%¶X”g,ÖÛµàÆšS?·ç:›.ð°Â.leÿ/“Ø­öwÊX.ó2]"2³}V¸hU¤d¦¿ d0ÅÁ¼L3tßôæ¬Oó7ß½ëØ®Ýßúàô¡ƒ?|´åÇ œ9ü®¾à (VN½¿åûö Žºo·o:³oû‡kVlœ9Ñ&_îÛõѯ|sâú©¯ >UüË÷wþçóÞßr0cÆ’¡É£<_ó·œh½0ÔyIˆóB?k0bY¢5–«¥æàý[.³ž`77Bœêf9$X|bGÞ•ï¾¼wéÉÐÖ_Àâ~ÃÁmÄ…Z¿Ö¿×ÙoŽ#+P©ª¬ÔK%¯œèG׿…ŽçÕƒgŽè/o¯´÷À 55eÑ4»§ìU³º¢µª´þþ­+_~öᆬËÞü(7ë»»~úðà‡üüÅáWÎjŠï–ß»YTpµôÁr¤ô–Ü{ðÛõß®ýrþ›Oåe.}%õ•ÎCB„ÃSM†8švµž"uYæ·"*0#ÂEˆÏ[ÞÂ¥~™A’¬PŸìP¿Üÿ¼˜àüøÀ-Ñž ˆ\DÈH÷Í]×ÄL’:ö°7°~ÆÄÏöä­ÙÅ‹ŠßŠ*îÝ/¿û[ÅÛŠ»jH7äR GyXp£üöÍŠ;·ôÿV\W^»Üú°XUpãö™ËnÜДÀõV\]VÚ¤ªBžâÞÍŠ[—+®Ÿ»{æË¯l>°|ÚºA Ù}ä9½"à}Ëï¾µs–&y¿kðá¡X.j['iv¼tEç A^6ÃBÅ_íÝ‚E Ë#SQ†ðu”U%Jmeù㺚êòRTxwF}•ç<®Q7#ÑænAñ¹3ç?=üáÚU+F ™â;P.{%%qÖ°AïçoQ•• £°øay-¢ka‹Ô†|kÈ¿…šçO;Ô<ß'íGþc=¨­µX* vrØ‘…âö:Õ#•BSZX^píÁÕ‹%×/+îÜB2xmÉýúò"Ä÷Ô)k«+4µUåþäPk*ëê”ÕeÊ¢kç¿:²{í[ÓÒ’„‰Ó|ÜGù{“IÇøxqu&t˜èå6;@2×O<[â9C$œáî4ÛCø¦Ä}I%Zb’š»E`û vÉŒñ9Û(o‡gó³^=ýé÷o\¾sóÚýÂ"ÆlP§x¨‚³ž–Ú*ò·Ô©ˆï@¿¯¯©nÒh('llµp ;4,UUMJeuÙCPhªÔ¢,))C¢8¥‘V£(©­(RÝ¿uç©KÇÞ›Þ5d¤%è1ÁU8Ò’Ò9Ç#Õ›ÂqXas&V.ó0™ìÚËÑ`€ŸËgù9¿]¾Pzÿ7´¸Ô@ ~knFîÚ€ô¶¸!¿FSšB˜¦ e³RÙTVVöë•K_8uøØs~øìè…ï¾þõ§AH C?ÒdUHrÆ„÷¯B U[´ù±öµ÷=¦ÏÅCÂA#@›Zª¦JQ_£B–kCªFYN•¢Õе²´¦¦¢áÈM(¨ +JÀÇ ¢3eMe…¢¤¢¬PY|»´àµ3'ÏûøÜáνwðd^Þ¶Y3f§$¥‰½ºYY¦˜™: °±êmdÐÀëcÄ 07L pDë¶ Ø] Ç ÍÒÌz ÍSDÿؾñÎÅ3j𢔗–+«ªëU×=©©£„7ø=!ƒ|pE}Á¨AÁm0;K4öDR’"ä #ÁQÈÙ{Æ[Ã}©i«üˆUoln¥@iñÝ_oŸûzßòésºÉ{ØsÝa¶âÆ!a|ë(-w5Á|càjœlÊö;÷ÏTÅ…è%·ü)|²a}ƒöaykµºªZY…NÆ9M Àmhn„;ºJ ªš‡DbP«(m®¡Aiª.kä$V×Ôª4õ5Hb¯ù×´šößÞÿ‡=@¨zf«iI„T”ßÛ#dY®çê²ëÉD@ƒ8ÀÆÇ-VJÆd,d×XWrÌš4#xfp•(KïUüvKqëFåÍU×nŸ=wîÈ‘+–ÏïŸ:,8¤§‹KW[›DY²…Yo[ë>vVÝ&] ¹ž&X-K·1€U0Œ‰n.ÖÖ,ÅOôÍGo_½ ¬*W!^¶å ‚IŒ†VRÒ[Õ‚rùÛæü¥ÀVD¤ÍÀ¢X ;ãf$T©Ñ4¨Aªô¸U…ø[(xˆ—mj¬«Ó45Ö žXe᪫ÿúxí¼iIáüÆ„®êœä“-aí³mѾX˜~u¬t^iowó´Pѱ}[‹ n*Ê+ÐFZ8 ‘hÔmôPd k3–ŸÆŽøž ™' @³â)YªÔ ã¡Ãœ`ÖÇë 44œþ9Ô´U]Pÿ?]ínï¶=Ià õûZI`!U¤­ëâ£aBYü|R öÂM!REAM((’ â QIs½ì͘ÑTU”W>(­(,zXPpçìÙ3Ÿ|r|玱]‡EE ”õñ•¤ø ôàï“*ñ|=6²¯«S/gû1!²Ro¹™Iœ»Ë¤þ}ak-.¼[­Q3Þ&RÃðéÓ5ûE+ÀK*¸½*5^߉:Ý èú,cªQ6©JKnœ9¼lÞ‘¦\<ÈÙdHÈšˆ…­­ÉÊ=Gæ²0Ižäl24&àâ7ÿÄjËÄ< Àf@åÏÅ]ïw¡Ìeô)X (s¹ñq=EÌ‚Á‰Âh.Ú µ¶ß]{½½^¢ ¸k td!èÚ/BjxœaøB™l"ÕÆi·P…ý€ê°‰)ˆ &2D&Mt!ƒ.'—º¶®FSuAòÅ7oœ=ûóñãÿ:zäëƒNìÛóÕ=ßÜ{r?ñðï^¼hFï¾=¥~É"q'7Q¢D:kèˆvìãY¥¢\Óˆ,[B?¨+üï;´/ Ü îqšjè}ik«bà3:Þ±ññ#°5ªškMŠÂŠ?ŸÜ»eÑð ~O63éof‚Hæ¹b—2Ñtç)A£ƒ=#­¹! a§þù&›uu`2ÑB âÏ¡F! jk¤ÍPc±²H,¦t$>1‡Åà‘2Ö¾µ÷ÀKØ€È.ËÛå#«Il©à¾0ƒ &TDü!Qû|ÂS‘05B +88„› Q4 zknÑPTw L¡ˆ“‡Õ¦ ui Ô(¿wGQH¥ìÎMpòž¿|þÄw?~rüü±o/ÿæÜ±¯n½XýPz¢:Pºá–fМPE(:”)´öÅ÷hãPjjqU÷¥6° wÄž4iZÕ5Eê»×¯|ùù»ËW2%*b€“–Кä=7&x|¨7Ö£r¶˜?~øµ³§ëÔJX˜µó&Šçgù¸ùôX‡#Šž¸¸ŸæY³nÇô©jtßWûß—¬tPC)|È£çe–”=Ô´©Pfyb9ÓBôgáRè7Z©E„® ‘®iy “|µ°ÁÒÐУæVÕÊJÐÉÂÚŒ½öÞ’âÊ+~+«¸[Z]¤¨-©¬.V¨Ë*Õ  raX  †UáÊA³y¨¡ Ü Q5LÒÁ fUÐ?P€3ì†è â? ÓEà¤V*ʯþzíøñ£k³æ÷î1,º W×S^gúœajSY—A=ã ÒuÉzÔç|âaw;Ô¼dòÕÞ\}à7èÁæF„3ZŒà%ƒtÝÄŠ*,‰Œ †/¼•{¸n)w“çÀE"Ü€6üa:“Nz-D Ö„³@`‚†âQKSCS½¦®–®jimÒ4Ô*Ô UXu ô1 (O¨à†À5:‰4+À#á/ÖZþuxüDoð…€—/ÍðÁ!óïI]}ŽB¬Z¢¸~íò‰Ï±øÚžoÎNï5*©Ãì1C¿8t@‰HøìëIãÂ=yôÐUô}ûL…ïágsÚö<¯ö Ëðêí[{¼”=€¡«—SÂýÏ/ý Ô`Óa Uˆû” ~‚Äç×CÞQ{3àÙ‡×r0ÙD ®…²”Ò‡¸Œ9Ø5PPFZšA «L:œÃ¦J䣩ªÕðvÑÚapÇ"Åbž§ƒÇ¯0ÍAw!{;4[‹ŸDýÄ,WÚI:‡Ìx LÝJ¢wS]W¯(oÕTVݽvãǯþ곋§¿U•ãL¼K  xÁöPOêPE ;B]…׌˜r]Gß‘/åhkoôwh…ƒ"¬Ç’1:¦áCv˜)Sf"aæJÆÛ#* ã@#4%”^Ñ!³UØBÄLÀ«@ÐZPÁ a¦ ÔàJd",EwÙ>b{ƒGñ'D:K”,µ­Í˜Ž0oùŸPcºÓ‹ï¦Pè¹¼Ü2Öîðμ˜ë'„:ÌbúÂX×PAŠ‹ {ÓúH {Ü╊2¥R¥®Ö°ÎÒ΃ô¢?®Ë‰9˜èÁÔãO ßùt€÷‘Õ3ʾÍÿÝ#¶ýí_ÊÐ nÂ~TCiÀØæ‡<ƒüà3ó/”phùLta,dhå :¦jxD"ƒÎCìn4É"´ÁçŒO"͉Ô<°YÆ4ЏŸð4(Rv`Hn€ã ªF1`sè1¤Ù0ã3ð‚Üã¾ mž^Åm$éº 5ê öÚZšÓ¡7ªë@‘ߊ•°®#´5´·±I£V?¬Õ`åÒL+ÈÚ€sQyÁ=ú”Ö4as#÷CýN¯Æ÷ ?x_ÿ  O[©kmûßöx{€É2ž™4ñ^7T>stream xíœA–Ó0 †;°èrŽ0gàáf ƒô( ÞëšKxð(cË’ÙJã¸@gP©-ÙŸåßR:Í 8÷D_ß7ñµ]µ‡/°1ˆñdu8±Ã\wšF€*ªÖá‘Ū³0ųÐa’O›5ù`„Leþ%ù§îXæåŸF€Ã¼¦·—5§i„Üú*Éo†w°za *¡;>yiÕ­&³ÇŽ@’›Õå9ÔIFAèìšÝÅ ¡cs¡y„…uJ¨lFðnMÓÁ+—íÀ:VY †é`:X]øØš¦ƒW.Ûuì> b¬ÑáÎÕ*iPÿötÀ¯L%ßP\u>ò¡V‡ñ u]m+ó(ùr|ÜP•F€t¸ü³£ªÓ4œæ èÐVÇ`8ÌÅ:øö²;×ÁÜÂäaõµ Ä5°°r?™ÏÄ`ÐîÆu€sÎ^;vø3»ÈÎUƒbF©ƒÍa \[žÕê/hÏ‹²<=7[F†Ð³Ñ e¶sÓ ‡é`:ðʰ|°|°|?3¸ öyjØýá¿?Œµ‘u˜$ã;ü¦ g¦&?‹[;6üéøŽ¬}ë ßx[³CÂ…ÂJòæŠ %–b?É<Ša)!žzP$$ãšò=Ú•„‚ lŒ!‘X®ýzB1`Þ` ‰Ä¸¦|O8`1¡Ãdæ%#Z¾«„јår&öiœüPˆª¼«„=(±Ð3¯žÐa Rb‹žyK ñÔ{i7š¥S®7î7Ü'endstream endobj 66 0 obj<]/Filter/FlateDecode/Width 528/Height 327/BitsPerComponent 4/Length 3528 >>stream x훽Ž$¹ €Û¹¿BGãÔ‹ó ær/&¹sdà€‰]jà€} 6¶ÁЉ³~¨#©’(©D•XÝUCõnEQõ‰RW×ÌÜïVŒÀÙ Ü.ºâN D;/­ýQ°i祵7G!àãÔ®¯Öþ(4´óÒÚ‡£ðqj×Wkº[‡Ë œ’ƒvRÆáNÅ8OÀ8œÃœ/šî„Ÿ·t¡;d´7÷»qðÉbŒƒ'`ŒCJà¼ù0ã{æ)?/Üx¹›´?ãçÎKS3mŒCØ;¶/<‰rÐ~ݾ ‰3rÀy) ÆÁ\ã¶Ží OÂ8œ˜ÃŒÍSæM*ìüŽ+Øàdâá`ù`ùOÛÛsžÆ…ëh³wÚ¢Ù#òÁ ñiõ‚˜”’4 |JP œqðdŒƒqðŒƒqH X>¤4Ü~Ÿ›ÊOôKfZ–a^ªö{Ý?ÀPÕÒ£—lªµ÷PH8‡*Y Œ0hr˜“\2rlqBs^²©ºÄyiJ›ƒàIDÒ³Çb&õè%›ÌQ¨L‡,ýÕ8‡4#,,,R–) ;,,R–) ;,,R–) ;,,R–) ;,,R–)öùPþ8rè‰jEÒ³­c1“zô’Mæ(T¦C –þÚæÛÞï%—²^Úên¤ñнdSu¹-‡aHiIÏ‘JséÑK6ì=‘¦CIŒAD{'ýœWr¦Õó0Tµôè%›ªC)Ī1(ƒ'cŒCºG,,,R–) ;‘é»K×oJÆõÕËOË8‡tßX>4óÎ'ç¿„¥Ð‚,B’>ô;äï‘BðÒ¼´zãÀ¼äJÅPïÑK6U—ÒRUA‰öŽ.ÔÄ $¸ŒŠ4ˆ¤gŽÅLêÑK6™£P™%Xú+Ú»ÇsHï—RB‹%•£RžœC:ÇT–æ˜êSYš~Ô?9i.=zÉ&Î=Œƒ§±HXÏþ–¦n컥ëRÈ’A^²)†ðÕ58DÇ’3­>:<Üßã´¡‡y|,˜€—\©ê=zɦêRJÙª1(ÑÞ¹äL«÷Þðݱ˜I=zÉ&s*Rˆ¡½¼èC!ÚHδúèð0‡ ’v¾’=;ÍýëôR_ö’HÓ¡$Æ ¢½Ë÷HδzöCUK^²©:”B¬ƒÒ8x2ÆÁ8¤{ÄòÁòAȼwpiÛ'þÜÌ0‡‡ô!¬Õ3]Çb&õè%›ÌQ¨H!†öòZœ“y³äL«g¯ŽÅLêÑK6™£P‘B íåÕ8x"ÆÁ8¤{#ä|f¦j†4Ö꿇²Çb&õè%›ÌQ¨à¼4%p¨ö‘œiõìܱ˜I=zÉ&s*Rˆ¡½¼¶9ÐcÅ[é}Tw#Wôè%›ªËu9T‡°}°H°%}èwˆç“l”¤yiõÑážOr°QÒÎW²Ÿ›„j"IóÒêÙ¥c1“zô’Mæ(T¤C{yE{W*C]r¦ÕòPR©>•Ù£ I! æá9m½Yr¦Õ³wÇb&õè%›ÌQ¨H!†öòŠö®T†ºäL«þ䡤R}*³GA’BÌ-0–Ä“sHï饔®êO¼/ªó•”j@ÜiIƒHzö/ ¥Õ³GAš%ïØ´—µzÒ±˜IZ}Ö¹V‘B¬Ù¢®i/5jõ<¸c1“´ú¬s­"…X³E]Ó^jÔêypÇb&iõYçZE ±f‹º¦½Ô¨ÕóàŽÅLÒê³ÎµŠbÍuM{©Q«çÁ‹™¤Õgk)Äš-êšöR£Vσ;3I«Ï:×*Rˆ5[Ô5í¥F­žOï‹zdÇ]u’¢ä¥i/5jõÒàêo=”S›V,ÚùJö­1ŽÐ¶*Ô#LØb4FÀ#`Œ€0FÀ#`Œ€0F`M·ñ“ù'xšzqŽ~ÊN@yºŽÅ(Õt±±_¸ŒM+ª±Q¦qŒn̕۲>­ 8qw þ;ù׳ЮR’(ƒ®ÝþóX ö¬”õ¬±^q8}ìÿÝ8a³N`»F¹±“ †+·LIŽ †~¡{yr’´“?ÀǦÜåûÞ6¼ð RÞ/aI`"ÅÖ-t/¯>ÒfJ°ŒŒLà ÿ ývs6XÍ/àfïßé*zÁ€ªrsæ|q— ‡B 7¨"ë9XaU #¹‚ ¼h•@S‡Ø®é…–ó D aÂ;½ÈMaPêãœÁ$0Fï9Ï(è‚\b–Bà .¾ZüH3܇.ä‹§ÐÕæ\Ób²­I¸qã§äPÙòŸ’Cåîúsr'ÄçäPIˆñ‘ñ4ã„ø ³®ÌÑUt¦2FÀ#`Œ€0[xï*ÿØ:Œ‡û _¿„òƒå‘¤˜rýÂÿÞ~z åï;`ùò%ŒõÈåÂbD?„ˆÊë þ0¼ Σbàòþþ@Õ•úÁ #D7qÚ‡Q~¼/„½(FäPF8V,\©ö‡‡9¼ýæå€!”b„¯¿¬a•8,ZÐ…a¢<ír¥ÖâÃþæáF‡Go‹‡aÌØå°“¤ó’QŸ„O»\©•8Û¢àðömà°ö¢îµŒ]}¥2oß<‡„þã9d¾~+õºÒÎ-`>†|x¢ã¡ˆp­ŒÍ8|ø|H’î 8dŽ2v¥•¤ã;³C²-‹ö÷ÂÎíJÁëaqS1 ‡¿_Cùæó!Þ±þm8/ÞªMÑl¯®TÌXÈß”áŸþLe’ºH9Ó€åú2pxôñFHçC÷J‡Ã²•êä€I—åÖßGùÐLÜ1 4tÛÉÉF!Eä0±ù6&@î®”†¯ÿpæ”}<,X©>;Ùc”{¬»40¥ú9ü'ž½×+žÅ?Çú_¯íÏ$)ôUõ“È@ÌØ>èƒošàcê?¿†ò¯ƒ8Ȫ®;ÃŒ-WêkºRþ6»Þ´ÝâÉëÇõÏâÿ ?úüx¢GQú•ꉙÃ?¯/P¯païÐEX[)\ºÆJusàmñoâÀÛâ×ë•‘nYw p­F4ÎX9”.ˆ2røð“sxôñ#|WêF_¦*oñ|¨´y" ¼- û;b‚òH8>s˜±R=ù%]xž1æ 'Ýö-€¡½R„¦±R½¶¿WH¶Eȇíg+Ž­Tx§Z©YÒ ÏuÆ°Åø×j@ËVªƒCÛçC{ȇµæ4ÇÏò•ÒrÏužŽCr<;÷ÇïÃJµ2¶ƒÂŽw¨ðŽŽœÈá×a”Ö s–XÓ'ËØy+ÕÉ!~Æ'pÈoÝŸCßJ5ïó¦9 lþ>;<‰ä?ß¿ÿŽÍA4k;ÇÇ_¸Rgà°ÆJMs@Øü çÅi>Á£‡,²ˆ¯]{´qeæ­Ôð½Â‰Û’’.Í•ñ?ÉñFˆ9×!(ä…ûüêÆü¸®³R~ù£×‚ÍÇó_V-fcOCÃÊëá,£GQÀ&|L=‡‰•º®²R%ÿ¸3áð;…ѳn[ÙìÂÁÂù0l Îâ°ð ZDhŸ• †ëûˆ5œžÃ;Pàk<HµhAvbŠ'ù&+%r`"(-œÊ’îb„ùJ=2c—L¯»¯ÈáiVª{*fØEà:!ýendstream endobj 67 0 obj<]/Filter/FlateDecode/Width 633/Height 333/BitsPerComponent 2/Length 5914 >>stream xíÍnãÈ€iO FåÞ9 x`n‘–±ñe.£Éˆï£L`àÙWp¼AÇs‰6Æ^„ô¹ÎE]‚€¥ªÿªÉ¢Hy{dw³ªûëªêf“’íÃá)§õS†;žñ†øçÙzÏÖb!ºÏ±÷l½Œ¶™úѪ‡Å o?vÞ€ŽyªÃðXÖãÐR?iÞÉÐÕ3áÙïÄYŽs×Ö±*3=!_S/¼!3ðve„W[>–s½Þ:²0ÿÎêz^¸áwã5¡k%ªåëèO^fÆØÍôôµm¶Æ“¦©m`¬ Úͤ¹m´/<ÝD![Ϲ;ƒ&Ï6üºðš2ÐsÛ‚i.ÃqnAsÓzÞw(Ì9ä°úÔ{ž•ÝÌõâÚ—5^] è~,熮EÀ™âëÀ˯^·s#×Jû)¾v¼tÉ“ªòð¶óÚec×:>EŒÈ×Tx¯ud2÷ÞÉ ÌQ˺ˆ7O‡·†E¾þUQ(¿ì2Iª#Þ–ÖåÜ4ðt_{xÒ3µ«ƒÓ6Ÿ$•­mê~o›Œ2Ѓµž¹¡h³ùŒ4ü"Ø~ŒÔº8¼ÌÍÁljó07Æp.9/T_oõZ†SÙ2/0ñ®Šà>Û^š±x-CHµtÍS6%ïoK·.³í×õ{m½¶!dÕå\g.œl€WÀ–NíéÖí!>WxǸ¶L<»°ÈÈ䀧øTZfÇ®.95Zn]-ú}WêÖ•4?ÂLᕉ«B8·qr#çKÿfð­×°`R!Á‹=íܶû·i{º‚tWšbý~ÇhZ†ãÚY…x«Eˆ÷ƒ.^ÛêZeáèÕ¹«: ?}—¨§o$ÝjUšKs´ž¼éÍÏnÎLm]CvVàñÚÕ%¹·=œËYæ•Æû“éj^¼ÙüÂCìõ¼´ïŒ|pfâI hÒ…ßkºÕ_íuÄ+±t]Gxe}cñlÆêa¦sË@“.¼1xK+ xjâ^{4;/ëkKe3A»=¬ÇZ*ƒç‚ÏâMoZO/®ÏfõðêúÝuœE=» —w&.,föË mx³²žþª¼¸.®ÁhåY=ƒÉ1—gs¬ÌYO†ç`ð¼†T޶ÎáÙ¹a­w˜ Hr3ÃIxóú¢ŽÇYæÖ·«¯ž–ïb6ƒ¼Yý3ˆÂزÜù¯cqhǃ…¦Ã[à-x¿J™ÇÓµ8´9–bÀ›/Àr¾õpKkÅþÂÂ[^iº²±wëĜœ3bo&—#œùÎUx]‹C7N³9ZO­{€Wþî"k=Ön…×µ8¼Yé´´V°Ö»–ÖC¼k9sÁÇ7hÎyù³á ‹ÂëZ蛚±+ÚŒ‡BeqâŽáÜpqñÞƒõÔƒ¤ÐÖŒ“W3ƒÌ]ÃS·ÙÐpGõ’|Tb…<`ÆËÉbî Øp&~r-ùõï‚Øˆ7†s}8/Ædµ|¿WBÖsiÀ*{%93ÆÇ :W/o¿?¯Ç- ¢ ôÖš OÎ ®õŽÄSoæ#ëñF*gÆøxAçês ù†ÁÕ»\OþW•S˜Õ&ö ùÁÁ1xÒ· .-ôйTúž\4mLzsØòҷ컆z5AƒÎaqÀ»†Ä³÷²àM5u}n¸h@ oN ð`q@<9¯ìF«ä4ƒŸŒõIGáAI<©¬­o¦I(6öÌ•áÍh×údçЉÓæ{/ÉÌÛ“âáÂ/ñÔØpŸüŽ·Yî9où{ßz؉ÄÓûÎg„Ò´hv™ØÖkÛQùÞºñ ؉Ä;X3Ç ³vb°ñ¸w5O. oWêþyx8®žÉtŽ3)yxÒ ï°ÖöcáM{¢¡ø÷ YQºZjÏ|ðÀÁó¾ÚÊw.seqxjq0xú#9žù˜sÃáMdÛïð2«»®ÊéñŸÉ“ä|"éïÙõ]Óâ郻ðäG˜¶[¾syÁ眧‹wÀÙ; 3ÓM½o  c¼]‘´–VX<³²:<ø>|¥ïÕYªdjü/[ò3zq0­Qg{Ó0äáð‹q"$öû¦|¢@rG…uoj}<¬Ûš9êA‰¢ëá\h»Û|ϸ֭{w{È™ÏØÛHö?w›Oã¹®RëeæXßý1Eß×ÊY7è%øn£M=Hò±»Ÿs¸xÁw§JÅBoΙ <:F`Ú O»`M€©ª›YGãQæ÷0¼5е̑_ÆFˆ­·Ï¨“¾å-ËØb˜~ȘOÕÛ&ñó½õt\¶gìÅÜâ0FœI<"úEÓÀQg½ÿˆŒ8o’Æh¼Äú#…žéž6ß×<¼õ$Q§C{2œ ˜>“æˆÆKl=”˜¤w¶ ^Ô)¿˜Œü<;À@špîxØ'w6zfðqIÂ|oï°¸íáˆx±ùO°ðŒû̓Ñ×Õ8Åhó ¼¦Ûzry”òñj¦¾~?“׊}±£îmÀÿ›°3ʹ~:Àþäװ͉½hñ‚™!W¼(,A¿ÐãöÁöÚŒ n°„^° «ž)<=5N‹&[÷öõ0o:„£×yŠ6l‹’€:3Ñ¨ËæÅüx|JxûCAµDÕ Æ;˜w`<9Žn¼]ñšB9U]qŽÒ ëí»m#˜,ííÌÓ¶¶6E8wØ?2ž5à‰ŽýÀ~g55xᆀ¸ç^øÑ/îšpnŒ–΄ÏJð¢57Á“K7«[V—}„/v'7éÓêh²¬7 ïl^<}òý³¬Ìu.‚íÂ'¡t; B‚½%oßt[oýãøÀv¯C:ÒznÏ»Õ\a„ed¹â]xr3Í —-g÷°Om`œ±Ó¦ªM t”ÌOá·Šñ¢EÚ$2L2sÃG‘¶^á÷`jÑ×Ö!ù߯N¿“` "4zŠil„Ó’ëŒá\ˆ½Ø(NßË^ôNáá4‹æ×Fœe¹{‚ÇkëváÎ\Àk“püiìõvW¡P¶´å„Â^}¶ {A@.RoËw®m|pFößNI¼hñÜ3«Ò$)²¬å+…nIæ`ùxݽŽ-AŽ‚´7˜G%¼8ò¨uÄØx¸Fv&Ò,©ˆ}Š–• ^Ã}Û ?Ì…ŠÔ¹¢Ü©‘ÚàøÀk÷ÒxÜNF¶^“,àOO€MD<Þax\+sä/¹×?-¼2ú)ÅSwùØÊ¾ŽË³¤˜ ‹ã%íÑx‚·tœÃs¸`D¯F¡†Æc쇅-ôÁ¡j¶”8ƒ’ä9÷‰ãá Ò"œ &‰Þ²Q6»ñ®&q«4žýÐ/Ê#:ñDÔ<95 O‘ÀH#âaS"é’¶/iîø À‹ÞÊc[4Þ£}è熃xÉ=-ƒÇ#:W ©<`Æ$Âz¼uÊ40ÖYàã5Âô”ð¶ñC8{h½T0•.ç\\*¶\ëqŸ$ÇÅ{¸Jl@;w—ìªÅ‘+Ðz{HîÒ942NÜœœ©­Òx^‚RÄúdy<çÒ÷4jÝC;?z¼¦HGK[ïÑé Öáó IÒoo’J&ªP‘—’aEÔU€÷ ¢9¦EóšÕqÐA¶ÀÅ[‹´‰ ^Ãj3mîèD#î4ÞáÕc;ñˆO<Æ wh¬Ë‚Þgð&¼ž#㋸˜=ñNáØc?‡#Þá² ó9œojAŽsï!˜Ái/'­‘Î}ºx’ŒX-2Öcšj4熓^N–É‹qšËÉ<^ŒK•snÈáa¬v§\«fÎ2¾P®¡ Þ>yê7fó¹V­f˜xÄ;òI ÛK~|Ðáø‘é’²86ùP|¢ EÀ#?DÎà1ïö£9·'ÞžÚãµ€qO짨G¡>j#A"^üý$Ùtƹé§[$ÈXÎ<êAˆž¹8Þ~o,ë\Bò”UÙaf¬×çû™#p÷Ãé‡×œ^³Íúʌع•‚´ô:¹ò{8uðèAdñXÖ ð>‘måðÖé7rý‘œ+× @LS/cì¨ñ诚“x[òBD7V­G…—¤¼ô“_Š…´žZÒ]¨»ÕÖ¡„ ß¶çðȽkÒ|Š5 bW(yž:ÉáÑÒ*Y°x%H ¢R `B\Éám B˜Qu,ùœ–¹çBôð¾| 0˜šÉN¼ºš|“‡6F“‡O¢y½»z¡žF» Íd Ië^¯Gµý~ûiò°±Ýî·ßBnÿ°ÝŠ­ÄgáíÕ„RÃuÇ^ÈUh/öÑàŸú/sBhëºîèˆeÌ‘ÃÛ¯ÉÓ¶u÷ðíj‹·~¡Aáƒ/õ³Ûz‚¬Ÿõ¨§: *;î·º…ÄÂûDOEÒz°$t·)»VßzbOx2mY”³?Èæð¡®N´Ùv»Þ¾†Ï‚×ëíäJM.m|®†5r’Åc½&б ËÕäÅz-v¯×ðÑaaY?pÄ+ȵÌ㥿õ¤sØ‘€›§.‰EÀË=öç¬×ô‹>Ù­ƒr9’'ª„8? ^ˆáJ.$³ èí^ƹм˜Dcì.ºOu\Ž‹G>„gî¹ø‚…55º‘9hëÌTÓ±‡ >”ëL A+»ŒPµ› /·ûÍâñvó¦ãp}ËÍL 05ÿ§ÿ9­2€—ÙOåñä­¦µU¸8Žs/³mÁã?*t ¢ãºôk°9:öpñ¶T®¥£s™°“íåðšçc«QœÛ¯È›0Ì1 ^fÉk± ˆµ™gŒ [ä(¼ô'OˆŽ~<ë±VQðÚ¡§F[´FVÕïÑÖ<ØéðÚZ¶&m ýÓâY„!Ñ¢LÇÞ£:÷tx£8÷¼ŒQc7Œ×ºJйýMŒ7F¹žþê7ExÇÌ~GZ¯K§cæöÁû§ú+¢ËÂü9QóWEé‘xZ'ÒX­–Z§¯õräÜJwrwöMÞF'VYet<§øYÒ!^cºø`2æümÏêQ{¦uvöºÊÜëò©B¼U£cÊ+º+tno©:Œ¼wiÛT™[]6±PrñtW®*Ø!EÝBðQxÉh  !Þ&éÂTƒ·œÑX<ÊA\<²+¹ÙÈIë„ÎMŒ] ôó£Rê”%ÞRý>Zù×vµ:‰‡ ËF ,'@wø¼¶Ý™!…έÜõE‡GY€vî_Õ¯¡­/][$ÐY¼oÿûYþëÐ"•–·8? Ïü2_û§²%'JÝÝ_þþEþ󦦈D±Xiñ•¼1-/½!NZÏþYqï¯Üºê)Fã-û/ò_cú7C¢{§8ïŽÀ3¾­kÕvHâ5Œsï5Ý—Ä:4Þ-6 É›êD¤õþ¤C¯®ßÈ&ð@è"œÃÓ¾ýò÷M«N¥¯š³ô±ª²½à0 Ï›¹Ö3ãó7À ‰´Þúh2F¼ž›ÄЂØû3®*òß¿l‡J‡Ä»5B·•ÉQ"­‡k²Jx8Òj^ºÏnêRCÒ4·Jkµªlî¤x¿Q«Yxš û㹩Ña=/önժǿAŸ*µ8·22ƒðÞØV(Gk­s¿Q«c<)ešKŸ y·ð +d3dì9ë½}•]ÕÈF]wëž®€jJ§Râú…þxìeÙsî}vÝ#g®Ã»D•ˆÑÖ;ê¦fÖ½ÿT¦»6ëÝZ!—ãâ­Êd]!»’ÞÝèŽÔ~êàM\R§Râ·Z V›cãýAã½±ªdWÁfþ[½*oÚu*uÙEœ®€j6žÞŽúÛeB7¸k¬–—råÛ¹îtdìÙ]¨½»õÀ[Ý¡ùæ—0$“H¼¤‘¸û-ÌŽæÒáLéTúº9ßê2œqrj€hqVw?Ö Ã[-^|éÖ©4Îu^š2{à­â—T„nè\hŸÖ! ± +µg–¹~xZÅžH<0žg=+k2”Ne.Êí²Þ3«:B<ç\Óˆ=ºÀicE’ŒᡲR x}viK!ćáyw ¿“WýÑÎ52Þùÿ ý¶ñÆe s¸ÉHŠ„ø#8ÇàR•b™á¤L.ÁSwwÏiѵ K»Î¨±§–Ku4hx&†¦†¸1‹O§ÂÆèDt1Ìz¸1;JeC¿g¢¿¡±§:áuev,í:‰sÛÅqÈ6¥Ö[^ÞU‹ûÕÇ—ÕÝË–—‹ZDX•(ƒÖÓ:÷——:U¯XHñîïoo«_¯Î?ÜÞ.^¾¼¿?¿—£mÅÓ:‹»ûNÀd=èjµúõêè~¾\B©Oß5;uÆÀ[H¼Û¥Ä[nƹ>è fª“ÆÆÂ¹ŒŸÅ²=~Hç®À¹`=¢«(î°¸o¡õ@‡Æ u*%þqyñS}Õ?$Æâ}”ÖS+"ìÅ•4ê ^—ŽÆ[ôP¼lŸo!ga±:??¿ûꫯ^^ž·àiçÅbAè¤ÎEc¯JcÁ r Þ*M0àLìaS¼š¤ k^\P10žz¨Q7¶¸A8åu*{àÜJ97ë †õ>ȾI¼ìvÔ×Iœ+c¡ñƒÁÐ? QB¸qð²±ZϽ،ǘÇ멳‰[ƲŒª‹ax{xaì‘x¹¯‘rÔú,XúöÐSg^~³øb¡Ž5¬#u69ióÕƒ ‹Ð¹ùi(-S@çvê„ÎÍÇå Ïýåsa€ˆs?|,PÆŽðkÿ‹9./,[ÕO§ò‡íç©>b<Ûçx™Ð¹‡úH^þŽêqžŒ=ªÙ–ºÜÖ&”Î0<ªÅQë~Òx,çF±×˺?iëõ²Ä1Âì÷ìÜclþh:ÜËÂüñf.+öXcÈ=‚õ2=³ªŸñXfÊ=uëýîâ)Mendstream endobj 68 0 obj<]/Filter/FlateDecode/Width 192/Height 194/BitsPerComponent 1/Length 890 >>stream xí—1OÛ@†‹2d«ÿAÝ”P[î'ð#:0v`€Hi®ˆ!KÕÌ]êŸÐ‘‘H V%ZF¦rTHxªœÊª>[õùëûÝÙ%f@ R)œäØytzï»Ü{ï9Ìmî¿@Ê\̹b¶ßm2—1s2`ªáè[&àóI Îà?¯p¯ówð.×П¡¥˜?ãqÙA|qEÌ_q“±]¯4ž"æ7¸Oò8—ú»¸\CáR¿ðŽƒøÏÛ—ó4üvïãG×Z(#.ƒKû*›¯ÿO1¯€µI-ë€Æ}7MÌkÈþÑáЖ½„.V€÷ÃÀæ=¢d©áÐÁZ;.Sº>Ékýn@ì¹!¬¯Écß'¦Ê:^`\©§›P^‰Nug޳יèŸLrì §Ó‹iŠc9Þh´îê„Î_×÷¶9Ïwnüžù¹M¼¶±¾{–{mï(ñÏKBpä›—m©áçN(|‹x <…Ÿ×l®SJ:‘ðŒP¹§Mûâ˜Òá ^€ðöPŽ'Äh”GTi%ý‡Ä1øx ~@y©…+B_à <W”‰,‚‚xÐà \S"² G®:ÞoxÙ¢jðÓËࣧ¡èÏÍ?·íÏ›êßK?»¼‚Ÿ‘WS~.7½Ÿ{›Ó~Fn;?#·½ŸkßJž‹oÁSïÛ˜øþÙ?‡þòY‰Ä‡à’“‡nܪç¸÷^ëì Ÿ9’þž»†>r;s¼Ì¨Â>~Š}ž$Ò¿Þy ýeËÈÿ$ EÇï߉ùŽVݸ~öÇÓ]ÿR ¼¯ÈûYkY÷cB6›y&xß8¦‹uÇ}~Â?ý0¶¹øç¹ã‰O¼¯Ì‰óUúÂñWÄû5ßwœŸ ¯ÀW /~[ÿñ¾±åxJ¼zÁ·QÏ"å$¼ÎU9/$Wq^dg¡Ô‰þÄïψ~˯Qžþp©_8êŸâUèy?¤‘rýïú:άÿžås‹ðGÌÐÖw×–O$ŸÅ'õ9ÁÏÊoáç_òÁÝ~ÿLrú å|ÿõ ùì9ò°}á-Ë༤DÇ¿Ï8Žü|ŸÀÏ2nÃŽ¶Z»’Ï5/‹&Ÿë÷zý®-rÔC‘èoþˆBý¦c‹~Îd\ˆàßÀvü~^ÕÂgúá¦çû¿Ö–ŸeÎmN¿Ào©äWÊendstream endobj 69 0 obj<]/Filter/FlateDecode/Width 519/Height 226/BitsPerComponent 1/Length 2778 >>stream xíš1ÛÈÇ×q¡T§ú³Lm¸° Ÿøä#\q™²ÙìiÃ…p€á p͆•o±±ôÁ@ÔÈ«ÒÕ‰ÚÛÀª.ÔFðRóòo†”HJE¹bDŠœß¼y3ÿy#5+ºYu¢kÀµ8†®ãàäÒâ^Ù¹˜nÔ.ópçÊ\ÁbѤgšp„ûX¸`CPÌXÃõoþ⎰\¢·¨ù§KÀ|Ï‘ó›¿p?ɶ—4\‚b-Ð-wä‹Ü[×_à@n‡-ì×Ü¢ð ‡0kMñ—€.0vŠ€©‹këÁGöŒ;D!£œ\)ßÄRÇÄ8]ñÁ§\zòž}ó }/~”Kt&Š8‘ÃŒ Û·k€ok±»x¶V¼kmÜ/×V”“!\gY/! dlË?ñ¹W,÷îÝëØÊK€'5WÞãK¾ƒ”;Å7riÁR|û™~äjäNù¶„mª Óšö W#wÊ71`9›ò ™’«‘;å[ è¦4ï{­/‰3ž\Nˆ¾ÛŠUm¢WuŸØX÷†>ä[³9wÊw³q?c>b@L§uÒa ¦äµ0Š $ŠÈóßéØñ’'½à¢#aÁå@ÿÜ={x¢q-‚’ýKòœ}¾ìdž u¡ƒîÐ={ÜÖxzD÷)è¾£3ç¾YÀÈza@ªGzfšWKF®Ž†^Æ‚Iz“5€ R-à<ДÌÛØ;YxáÒÄn[ÀmM½¬¬7(s·Ôk×;±€# 8Òº“LÐ*Žt €˜rM4æa•+ 8s €°ˆÛ4uÖ)Jbé¶Î<<7šîµJk,Ó››Æ(‰<×ÓöJt‡0}À¶¨t8ÇM$Mh`A)ÍÚ>­{j¼0>hX 0ø‹ââX¨hÁù @Õµ`ôµÂ5qP­ ÚŠ>@}.¥q€^ˆáÄ€‚y?Ó„y"Ï[ÕY]pBAXt¢1`‹¤&´×*õ©¡i vÄY°ùÕ³§_‹u»°Î«àáŒ^X³P¬ø”>X¯ç?Ê〖€Ó|M{¾ð³›4án=€J‡y@ Þº^ë¹Æ”Ñ Ì‰c¨ó ßjóèáÂÆ)ÏΉkĵ¯¡Î¢Qu¸žêðE?ð-`¡ºòt5=Þ×tºª µw"¨ó$˜XÀ,@®¥*¦³Ù‘FŠa9¨‘ëù3½ÏB ¦€i',È›…YV„Ù"Puδ#ŠLðôVÖÕX5IQH'3õS„5äù¨ó0ÓºÀ»åŒ4u®œô5]€`UWPgy$ß#½@Zĵ³\Ùú¤àNOãé2î@Mê˜[dŠÐÜ!èë`¯¢†¢3·ƒFàãt€¯Cn‘)÷nbà¤N\&K@ÏzY€æ©T|°ðæW74=…Í xêz~OOÝZZ ÓÈFLñ¶ÌÊ¢GM ž‹7¬/ØSö–Uƒø*.5 Õxz V· ½ÀÏ@)·ÀQƒ?à®@l‘ é[•9Q»êü‘ ªbR£?Àr4&&Th×üZÌ[*ÖǼ¤^¨óöÜY]Í#^†Êr+1>óYÞ Pç™ä3ÔÉX°‚Øà kyr›0g£CWž*‡Uz)ÍÓßð”«Ý&êÜGŠ©o5 D\1[PÝÔWœÖó¨³îæö|[7ºaŸ cJÔ™3Vçç;¨3õÿåêD?·êüùNê 9NÔyߪ3’ïÔ€D\qÝGîœ×2uþ7ÔùRÓ“ê·Ìáƒo!T'ÑŠÕfu¯MÞišÀNÄ~œH½ìg³:ê%rg³>ж £}¨s¶ eêüÚ¥ŽŒŽtL9Í7¨3!wÖO°ÄyíFØ–\€0Ì6©3©®vÍ* ;ÑÂ*m„Ü9î/¸E9w¶ëÄe$N?[qâÿ_Kŵ҄r‚™]‚›q”-•íDÅY€=+Ÿ&‰:Ç TXu^“|W Ùq]¨âÄþê ôï M¨€ë⹨sΉ»¨3°:Ïy¹•-U,@ïOÀ`oú8!Z°ö”Ç’ïØóÌï¬4f `öôÏǼãº&w®fr Ééš©3|ÐHyÂk¦Î¼J£»+Ž“ÃŠqu–ubQEXZHVXF²:;þrRE y¸ÊJ1w¦þ¿æ"Qdõ»uÆ^¦sG‡'½ À·€bîL]èÂc“7²›ºWtv º0t2€Í¹³ËìƒÆ&…–OäAX¢á—@™:c_yvªé' øItîgeê )šÜÕô­dez'B¨^eeêü#ÔùPÓ|À|£õý`cî,K’/lâ‰`•Ä3Ôñ¼•i¶Õ7¨3ï8œ¹ÊÄzAq (gmVgÄA@qø˜ÈØ „maJ#uAñMkA׺ØÙ^µ W¥|BqJ•Ó— Úoˆ}5ÖâÄœ°pã«4aá«ÁWˆëe®H<Ïÿ&€ººà«Ñßô_â²Q& >v)»¨ód£Lÿš-UzÕùíþLTw€!âಟSËœb¦^pîü$‚(Ú)äÎÖžòш¼óÁ!‡mM§¾&ñüõ@z¶ÅÉ[pšV±ÕºQrgÜ­P.| €Ãà{$Ë­gØÙv£Å¼±–îøVvZ÷ì¤ZTçà»=4[¢W³NÐâ³ò ©Ác÷(ߊê¼âÜÙ.f=ü -Ýá (ªsÚ”7÷™,&=Þ”=û/Ô9(ªs ˆ‘xÎnjüU(À[ò ÁJÖ†&dÔ9­+!“7 bý€W+y#EuN(Á%ÿîÊÞº°·þ’wžðÄEœXÌÍEÌÊClØ%wz/¡Î°?,.áDˆõì`,rlTç ‡8pïÀkp"â@™Ó0.› y+V&UöF®{È»ÿ`B$ºÄšO§Ÿ9K¶D7,¯t4þí’&T™ 'ã鋱Zt"¾÷¶­P;?’^èêÎ  {~h½ž~Tiäddêu-äDÿþÏÄO`M)í¨yã³ Qgþù¹ø§°½•…á×§üs¯Ä‚Ô'ÅΜ;7 x¹Éŧ߈ÃÇø' ƪJ¿¯|ÀäÎ&¹Ø$®%8ÛA6wæ¸Ù±0`ÁóÒ"ÇÚ¸= ‘q³+‚ø«“¤¥Äãܵ0€€t„v.ðÀXöìJ€oðæÎE©:ï\Ýô‚äöÜ„a]@ªÎu©:×$¹smÍMî\`sçTE$Ö(5ê¥U®v,¤©qpíÄk'rØèÿ çF`endstream endobj 70 0 obj<]/Filter/FlateDecode/Width 497/Height 226/BitsPerComponent 1/Length 1228 >>stream xí™±nÛ0†SdÈèGÐ3t,P@’!©“ tÈèµ@ øAZ€ :tôtPƒõÐA <Ȇd^J#Ã<’›¤ŽE€•¨ò ÇÏ¿HSÞÇÀD#>Ø£ëF× ð@XØäDuB´Áרg¨5ô…MˆÖ3¢{‰ä®)j .às¢Ÿ_¢¦¸Ä)'ZWÆE]¸Àk@?ð—®ð5 +ÌE(:l¯ûž? ÆW×@¼?w¸@áw“ýð{íºBø^ëž·ë®<¼Ywû=Œ¯ÓváNíuj k ‹y+ªþÿø¯k 5¾@GõÂ¥Bí5ƒæÞë?âÆ“ÇຜDHÚ l®6º‰ ye JH¬§i¢Bš0ÍPiB¬çi¢Bš(ºf(®¬%àÊøº5^5÷ÂõÜï5Žf. 5ºÔøM‹«f.Ój|‡SküR»îmk¼j¯qµîÊójÝá ÕÜuW0ž"öºcƱR1®;l|̰¤ÞÐ1¿›W 0Ç™þû»NN üHt—Uð½im•õ¼À/pA©¤ßi)`šaø¢€òøø-pÝ Â×À—Àa|üpÝ Åa±Ø'à?1÷®òë£Ói¸Ì*ú\7ƒGŸ’¨€¯¨¢/Àu3OIÜM$Œ®è¸ná+¸.k×}ëÞ5ƒpµî;¹œàÙŠLŠú´ý[ÚŒÞ5Ñ»ý]7…4„,fŠ7M`äzÁÀ[êÃþÊàïoâpÄW¼Æ«sŽÆB\GüV©:$¨ö‚cm¹‚«OLy|uÆŒM4fX6lî´ºÏçy®–£f¨¶rĸhÔ¯&Ü®SB,qiX7!Ö‰V»†uãZIñ20 OZ!¶«aKìf9ÒDf‹lsëx ["eæHNÜeüøb8®Ìó༆mæŽ 8g5lÞâ×±Q—´8k<ÖÅ‘edN­ñë£kœ_w·ñç£ÎÇìu˜û Æ=6nîôXøK‚âŒyâG×_"=øv†í;*}‚ÑY×Eν1}¸ëþîÕ°ìÜ›ÑëF´Ú5¬÷iX7¾ñiX7N¡h…’ÚѰg%}@’ʹ,37¢Õ¦aåEI¯Ï8<1¢Õ¦aåeIo€' .3#ZmV.KzüœÁñEÝ)B¯ñë‚›»_”ðl^søêÌ­a§%Žuó ‡û^™´„×sh™¬ ÒÝœ»ÌJ|p¬»{¯‹ÄëÓR¾>ºc§ÕnpÎý…ãýÄ¡5›º(Ϩ<äÙ.Üë~àøv†Õ±Ò»x¦×Óz{¼¸Ê°ùðsXdX·†µºÛ<¬‘"“ˆsX¤HÏË@3ýfÚâ §aí”yÚºŽ×°¦£ý&Òø8\}â†ëx k·¹{ªÖ8«a»~Ì52ê|–õáññ¾ïÇí:O†}ûÝ6$á™ µçŠÇ?ÊÜîàFÏÍÁësXšy4¬=TÍÓļ9‡¥ssð:äVbî. k¬´ßÔÀ#Îa7À#Îa!»ƒW›†µ›Ü{š»5l¯§õ6ï^¹sXë¨æaï?¸7ŽFãŸk£ŽÌ°&üvoô§ØÝÿ{2âa~²ô]gqJØ£gvÝ_ÿì̺endstream endobj 71 0 obj<]/Filter/FlateDecode/Width 483/Height 196/BitsPerComponent 1/Length 1615 >>stream xíšÏoÜDÇÛ®D@Bí_€,N=¢ªˆ*uþþ•8ÒJ=Јh_P{ ME‚øÏ@(m“¨¨¹„øÈ¥7M‰Oì,u¼¬íÇ÷ÙNvÏ:ãE¢¤Ý'íÎŒ3Ÿ}oÞüx~Ó2Ï.íÙQæ9\Ó{s‡u‡¥€Ìâ]||æÈa“L™ç}w‰ù J—9Da)p]÷ˆy¥S³÷P~6 œ>dN}æžb“L1[4ÇøzÃìÄGéâS– 8@oÀÑJ·LâIìa¼Kèâ3keYªálž}ŒY•I<©€Wh´ÂP5ÈØÐÓðè‚ÏAŠ1nIbòoçý'¾ía•¯l K…)o¢6)fo«9LgÅa)N°ë$eO÷˜ºr •dÊè±×ò¨yûRXÌv ³]n†µÌø~ßgª'¬^¥Üa«D½æ~¯ð:Ñš=|P‚•=\˜=ÒܤÚðnaö.Í· o·y¹QKóì' ÆüŸÀi¡tbÃÔÐŒÜM9‚¥ÇR^xò­¿$Ûèôs;Ía÷X¯”Ên·Þi`މ=|g¯óÅ7)úË¡Q³¼–Ab¼ß$YM^+ÒóiçÕ0VÅm…FiÌmÊzÄ!û ÷²`jwÒ!»é((^UOj¼"ÕÇðó™fzšÁÒÊ¥ZsJºŠæýΟÃÀ•V.Õcl_|¡«¤«ÀÏ:_­ç9îÿøì-.Íáöå‹ÃñV9Ïq#yþ‡Î]?åÐ7x[TAbÞ~žhÉD³ITüàtÓ˜ç0|PÀ¸qµ×|FÒÁl˜Í‰M£ìOÏuÇkÀ®<~w)÷l2±1Nû—² ¿»¬òáöʬf¯_Ýx°>WÞ>âJ•¹‹ñ@äZ¡GÇàÊ{Oܰü‚ v£k›¯'Óo\Ï9x_=û,m‘S4gðbÖ_®Þx8nvõ˜ƒ½wo}~}i_û~®ÞÁjçèòã½÷Än;îÍ,LŠªXaAz±ÛŽ¿úpÒé~ôøÄÛ“&K«ÒìdïèææÉ<ÿî)9¸°¼„\Òñ¶%J~;:=ƒô­îfK S” ²k¼xÙÃ+{$Jª<¦j:-È%ÙûArɺ0/ÙâC‰’öp›T½î)/FR6¬Í.n™3ÍŽ7Øß2w½ã®÷ðšý]ï$ìVöpqE}šqEíÞC”\²3)q/æ9 •çJ”¼j ÷XE„æ`…‘wN¢dË®^Û§¬°9 Q~¤¹*¯š¶ä™uØëË%K~5?0{ÛÜ·ôt—\Rý`î°jÿ”þzVö7Öyãendstream endobj 72 0 obj<]/Filter/FlateDecode/Width 187/Height 187/BitsPerComponent 2/Length 716 >>stream xíXAnÛ@ 7>ú= þzèÄgèè»}pÂ`WffErEJ[Ð!ÈnÇ#N¸Üñj5&óšñw ™¹Ð—ôúêÊü~³ñž–ø ¦¥þÉ: ï^ÁB­æB™25Ý©¤‰4ƒO¢Foèquô†8Â'r щ®G§H'BÐÓ›HoHg— ¥TË^sÈ^3‡¥ðõ²còP ‡ÚsÔ²‡¥B¿Tâ‹$éAHâ»('µÆÚ±pŒùç¢×,jÙ\è"CøT¿2Õžwݸgއ« eÏô}ÇÕHïùà­5¨ô®ï¹ÊÙA°3¸ =OöÙ“bÒR{_{€ˆhÔ”Ù·;Ooìw…ÎGîð“o¾E˜é§H_€J×-½ÅIðêA:y̦bVŒB_«(S”ùÌW••÷êp\ Joqp|³ìuô™51Çýh‰íÀnb|æR¯âN‘Nq2D#}–ýá‹ÉQËÓëã5zŽ\O'>ÿtKÍÑEúä¿§v?:ðœN¦ žÐye‹þ‹PQ „SHw$K‘ù=endstream endobj 73 0 obj<>endobj 74 0 obj<>endobj 75 0 obj<>endobj 76 0 obj<>endobj 77 0 obj<>endobj 78 0 obj<>endobj 79 0 obj<>endobj 80 0 obj<>endobj 81 0 obj<>endobj 82 0 obj<>endobj 83 0 obj<>endobj 84 0 obj<>endobj 85 0 obj<>endobj 86 0 obj<>endobj 87 0 obj<>endobj 88 0 obj<>endobj 89 0 obj<>endobj 90 0 obj<>endobj 91 0 obj<>endobj 92 0 obj<>endobj 93 0 obj<>endobj 94 0 obj<>endobj 95 0 obj<>endobj 96 0 obj<>endobj 97 0 obj<>endobj 98 0 obj<>endobj 99 0 obj<>endobj 100 0 obj<>endobj 101 0 obj<>endobj 102 0 obj[74 0 R 76 0 R 77 0 R 78 0 R 79 0 R 80 0 R 81 0 R 82 0 R 83 0 R 84 0 R 85 0 R 86 0 R 87 0 R 88 0 R 89 0 R 90 0 R 91 0 R 92 0 R 93 0 R 94 0 R 95 0 R 96 0 R 97 0 R 98 0 R 99 0 R 100 0 R 101 0 R]endobj 103 0 obj<>endobj 104 0 obj<>endobj 105 0 obj<>endobj 106 0 obj<>endobj 107 0 obj<>endobj 108 0 obj<>endobj 109 0 obj<>endobj 110 0 obj<>endobj 111 0 obj<>endobj 112 0 obj<>endobj 113 0 obj<>endobj 114 0 obj<>endobj 115 0 obj<>endobj 116 0 obj<>endobj 117 0 obj<>endobj 118 0 obj<>endobj 119 0 obj<>endobj 120 0 obj<>endobj 121 0 obj<>endobj 122 0 obj<>endobj 123 0 obj<>endobj 124 0 obj<>endobj 125 0 obj<>endobj 126 0 obj<>endobj 127 0 obj<>endobj 128 0 obj<>endobj 129 0 obj[103 0 R 104 0 R 105 0 R 106 0 R 107 0 R 108 0 R 109 0 R 110 0 R 111 0 R 112 0 R 113 0 R 114 0 R 115 0 R 116 0 R 117 0 R 118 0 R 119 0 R 120 0 R 121 0 R 122 0 R 123 0 R 124 0 R 125 0 R 126 0 R 127 0 R 128 0 R]endobj 130 0 obj<>endobj 131 0 obj<>endobj 132 0 obj<>endobj 133 0 obj<>endobj 134 0 obj<>endobj 135 0 obj<>endobj 136 0 obj[130 0 R 131 0 R 132 0 R 133 0 R 134 0 R 135 0 R]endobj 137 0 obj<>endobj 138 0 obj<>endobj 139 0 obj<>endobj 140 0 obj<>endobj 141 0 obj[138 0 R 140 0 R]endobj 142 0 obj<>endobj 143 0 obj<>endobj 144 0 obj<>endobj 145 0 obj[143 0 R 144 0 R]endobj 146 0 obj<>endobj 147 0 obj<>endobj 148 0 obj<>endobj 149 0 obj<>endobj 150 0 obj[147 0 R 149 0 R]endobj 151 0 obj<>endobj 152 0 obj<>endobj 153 0 obj[152 0 R]endobj 154 0 obj<>endobj 155 0 obj[154 0 R]endobj 156 0 obj<>endobj 157 0 obj<>endobj 158 0 obj<>endobj 159 0 obj<>endobj 160 0 obj<>endobj 161 0 obj<>endobj 162 0 obj<>endobj 163 0 obj<>endobj 164 0 obj<>endobj 165 0 obj<>endobj 166 0 obj<>endobj 167 0 obj<>endobj 168 0 obj<>endobj 169 0 obj<>endobj 170 0 obj[157 0 R 159 0 R 161 0 R 163 0 R 165 0 R 167 0 R 169 0 R]endobj 171 0 obj<>endobj 172 0 obj<>endobj 173 0 obj<>endobj 174 0 obj<>endobj 175 0 obj<>endobj 176 0 obj<>endobj 177 0 obj<>endobj 178 0 obj<>endobj 179 0 obj<>endobj 180 0 obj<>endobj 181 0 obj<>endobj 182 0 obj<>endobj 183 0 obj[172 0 R 174 0 R 176 0 R 178 0 R 180 0 R 182 0 R]endobj 184 0 obj<>endobj 185 0 obj<>endobj 186 0 obj<>endobj 187 0 obj<>endobj 188 0 obj<>endobj 189 0 obj<>endobj 190 0 obj<>endobj 191 0 obj<>endobj 192 0 obj<>endobj 193 0 obj<>endobj 194 0 obj<>endobj 195 0 obj<>endobj 196 0 obj<>endobj 197 0 obj<>endobj 198 0 obj<>endobj 199 0 obj<>endobj 200 0 obj<>endobj 201 0 obj<>endobj 202 0 obj<>endobj 203 0 obj<>endobj 204 0 obj<>endobj 205 0 obj<>endobj 206 0 obj[185 0 R 187 0 R 189 0 R 191 0 R 193 0 R 195 0 R 197 0 R 199 0 R 201 0 R 203 0 R 205 0 R]endobj 207 0 obj<>endobj 208 0 obj<>endobj 209 0 obj<>endobj 210 0 obj<>endobj 211 0 obj<>endobj 212 0 obj<>endobj 213 0 obj<>endobj 214 0 obj<>endobj 215 0 obj[208 0 R 210 0 R 212 0 R 214 0 R]endobj 216 0 obj<>endobj 217 0 obj<>endobj 218 0 obj<>endobj 219 0 obj<>endobj 220 0 obj[217 0 R 219 0 R]endobj 221 0 obj<>endobj 222 0 obj<>endobj 223 0 obj<>endobj 224 0 obj<>endobj 225 0 obj<>endobj 226 0 obj<>endobj 227 0 obj<>endobj 228 0 obj<>endobj 229 0 obj[222 0 R 224 0 R 226 0 R 228 0 R]endobj 230 0 obj<>endobj 231 0 obj<>endobj 232 0 obj<>endobj 233 0 obj<>endobj 234 0 obj<>endobj 235 0 obj<>endobj 236 0 obj<>endobj 237 0 obj<>endobj 238 0 obj<>endobj 239 0 obj<>endobj 240 0 obj<>endobj 241 0 obj<>endobj 242 0 obj<>endobj 243 0 obj<>endobj 244 0 obj<>endobj 245 0 obj<>endobj 246 0 obj<>endobj 247 0 obj<>endobj 248 0 obj<>endobj 249 0 obj<>endobj 250 0 obj[231 0 R 233 0 R 235 0 R 237 0 R 239 0 R 241 0 R 243 0 R 245 0 R 247 0 R 249 0 R]endobj 251 0 obj<>endobj 252 0 obj<>endobj 253 0 obj<>endobj 254 0 obj<>endobj 255 0 obj<>endobj 256 0 obj<>endobj 257 0 obj<>endobj 258 0 obj<>endobj 259 0 obj<>endobj 260 0 obj<>endobj 261 0 obj<>endobj 262 0 obj<>endobj 263 0 obj[252 0 R 254 0 R 256 0 R 258 0 R 260 0 R 262 0 R]endobj 264 0 obj<>endobj 265 0 obj<>endobj 266 0 obj<>endobj 267 0 obj<>endobj 268 0 obj<>endobj 269 0 obj<>endobj 270 0 obj[265 0 R 267 0 R 269 0 R]endobj 271 0 obj<>endobj 272 0 obj<>endobj 273 0 obj<>endobj 274 0 obj<>endobj 275 0 obj<>endobj 276 0 obj<>endobj 277 0 obj<>endobj 278 0 obj<>endobj 279 0 obj[272 0 R 274 0 R 276 0 R 278 0 R]endobj 280 0 obj<>endobj 281 0 obj<>endobj 282 0 obj<>endobj 283 0 obj<>endobj 284 0 obj<>endobj 285 0 obj<>endobj 286 0 obj<>endobj 287 0 obj<>endobj 288 0 obj[281 0 R 283 0 R 285 0 R 287 0 R]endobj 289 0 obj<>endobj 290 0 obj<>endobj 291 0 obj<>endobj 292 0 obj<>endobj 293 0 obj<>endobj 294 0 obj<>endobj 295 0 obj<>endobj 296 0 obj<>endobj 297 0 obj<>endobj 298 0 obj<>endobj 299 0 obj<>endobj 300 0 obj<>endobj 301 0 obj<>endobj 302 0 obj<>endobj 303 0 obj<>endobj 304 0 obj<>endobj 305 0 obj[290 0 R 292 0 R 294 0 R 296 0 R 298 0 R 300 0 R 302 0 R 304 0 R]endobj 306 0 obj<>endobj 307 0 obj<>endobj 308 0 obj<>endobj 309 0 obj<>endobj 310 0 obj<>endobj 311 0 obj<>endobj 312 0 obj<>endobj 313 0 obj<>endobj 314 0 obj[307 0 R 309 0 R 311 0 R 313 0 R]endobj 315 0 obj<>endobj 316 0 obj<>endobj 317 0 obj<>endobj 318 0 obj<>endobj 319 0 obj<>endobj 320 0 obj<>endobj 321 0 obj<>endobj 322 0 obj<>endobj 323 0 obj[316 0 R 318 0 R 320 0 R 322 0 R]endobj 324 0 obj<>endobj 325 0 obj<>endobj 326 0 obj<>endobj 327 0 obj<>endobj 328 0 obj<>endobj 329 0 obj<>endobj 330 0 obj[325 0 R 327 0 R 329 0 R]endobj 331 0 obj<>endobj 332 0 obj<>endobj 333 0 obj<>endobj 334 0 obj<>endobj 335 0 obj<>endobj 336 0 obj<>endobj 337 0 obj[332 0 R 334 0 R 336 0 R]endobj 338 0 obj<>endobj 339 0 obj<>endobj 340 0 obj<>endobj 341 0 obj<>endobj 342 0 obj<>endobj 343 0 obj<>endobj 344 0 obj[339 0 R 341 0 R 343 0 R]endobj 345 0 obj<>endobj 346 0 obj<>endobj 347 0 obj<>endobj 348 0 obj<>endobj 349 0 obj<>endobj 350 0 obj<>endobj 351 0 obj<>endobj 352 0 obj<>endobj 353 0 obj<>endobj 354 0 obj<>endobj 355 0 obj<>endobj 356 0 obj<>endobj 357 0 obj<>endobj 358 0 obj<>endobj 359 0 obj<>endobj 360 0 obj<>endobj 361 0 obj<>endobj 362 0 obj<>endobj 363 0 obj[346 0 R 348 0 R 350 0 R 352 0 R 354 0 R 356 0 R 358 0 R 360 0 R 362 0 R]endobj 364 0 obj<>endobj 365 0 obj<>endobj 366 0 obj<>endobj 367 0 obj<>endobj 368 0 obj<>endobj 369 0 obj<>endobj 370 0 obj<>endobj 371 0 obj<>endobj 372 0 obj<>endobj 373 0 obj<>endobj 374 0 obj<>endobj 375 0 obj<>endobj 376 0 obj<>endobj 377 0 obj<>endobj 378 0 obj[365 0 R 367 0 R 369 0 R 371 0 R 373 0 R 375 0 R 377 0 R]endobj 379 0 obj<>endobj 380 0 obj<>endobj 381 0 obj<>endobj 382 0 obj<>endobj 383 0 obj<>endobj 384 0 obj<>endobj 385 0 obj[380 0 R 382 0 R 384 0 R]endobj 386 0 obj<>endobj 387 0 obj<>endobj 388 0 obj<>endobj 389 0 obj<>endobj 390 0 obj<>endobj 391 0 obj<>endobj 392 0 obj<>endobj 393 0 obj<>endobj 394 0 obj<>endobj 395 0 obj<>endobj 396 0 obj<>endobj 397 0 obj<>endobj 398 0 obj<>endobj 399 0 obj<>endobj 400 0 obj<>endobj 401 0 obj<>endobj 402 0 obj<>endobj 403 0 obj<>endobj 404 0 obj[387 0 R 389 0 R 391 0 R 393 0 R 395 0 R 397 0 R 399 0 R 401 0 R 403 0 R]endobj 405 0 obj<>endobj 406 0 obj<>endobj 407 0 obj<>endobj 408 0 obj<>endobj 409 0 obj<>endobj 410 0 obj<>endobj 411 0 obj<>endobj 412 0 obj<>endobj 413 0 obj<>endobj 414 0 obj<>endobj 415 0 obj<>endobj 416 0 obj<>endobj 417 0 obj[406 0 R 408 0 R 410 0 R 412 0 R 414 0 R 416 0 R]endobj 418 0 obj<>endobj 419 0 obj<>endobj 420 0 obj[419 0 R]endobj 421 0 obj<>endobj 422 0 obj<>endobj 423 0 obj<>endobj 424 0 obj<>endobj 425 0 obj[422 0 R 424 0 R]endobj 426 0 obj<>endobj 427 0 obj<>endobj 428 0 obj<>endobj 429 0 obj<>endobj 430 0 obj<>endobj 431 0 obj<>endobj 432 0 obj<>endobj 433 0 obj<>endobj 434 0 obj<>endobj 435 0 obj<>endobj 436 0 obj[427 0 R 429 0 R 431 0 R 433 0 R 435 0 R]endobj 437 0 obj<>endobj 438 0 obj<>endobj 439 0 obj<>endobj 440 0 obj<>endobj 441 0 obj<>endobj 442 0 obj<>endobj 443 0 obj<>endobj 444 0 obj<>endobj 445 0 obj<>endobj 446 0 obj<>endobj 447 0 obj<>endobj 448 0 obj<>endobj 449 0 obj<>endobj 450 0 obj<>endobj 451 0 obj<>endobj 452 0 obj<>endobj 453 0 obj<>endobj 454 0 obj<>endobj 455 0 obj<>endobj 456 0 obj<>endobj 457 0 obj<>endobj 458 0 obj<>endobj 459 0 obj<>endobj 460 0 obj<>endobj 461 0 obj[438 0 R 440 0 R 442 0 R 444 0 R 446 0 R 448 0 R 450 0 R 452 0 R 454 0 R 456 0 R 458 0 R 460 0 R]endobj 462 0 obj<>endobj 463 0 obj<>endobj 464 0 obj<>endobj 465 0 obj<>endobj 466 0 obj<>endobj 467 0 obj<>endobj 468 0 obj<>endobj 469 0 obj<>endobj 470 0 obj[463 0 R 465 0 R 467 0 R 469 0 R]endobj 471 0 obj<>endobj 472 0 obj<>endobj 473 0 obj<>endobj 474 0 obj<>endobj 475 0 obj<>endobj 476 0 obj<>endobj 477 0 obj[472 0 R 474 0 R 476 0 R]endobj 478 0 obj<>endobj 479 0 obj<>endobj 480 0 obj<>endobj 481 0 obj<>endobj 482 0 obj<>endobj 483 0 obj<>endobj 484 0 obj<>endobj 485 0 obj<>endobj 486 0 obj<>endobj 487 0 obj<>endobj 488 0 obj<>endobj 489 0 obj<>endobj 490 0 obj<>endobj 491 0 obj<>endobj 492 0 obj<>endobj 493 0 obj<>endobj 494 0 obj<>endobj 495 0 obj<>endobj 496 0 obj<>endobj 497 0 obj<>endobj 498 0 obj<>endobj 499 0 obj<>endobj 500 0 obj<>endobj 501 0 obj<>endobj 502 0 obj[479 0 R 481 0 R 483 0 R 485 0 R 487 0 R 489 0 R 491 0 R 493 0 R 495 0 R 497 0 R 499 0 R 501 0 R]endobj 503 0 obj<>endobj 504 0 obj<>endobj 505 0 obj<>endobj 506 0 obj<>endobj 507 0 obj<>endobj 508 0 obj<>endobj 509 0 obj[504 0 R 506 0 R 508 0 R]endobj 510 0 obj<>endobj 511 0 obj<>endobj 512 0 obj<>endobj 513 0 obj<>endobj 514 0 obj<>endobj 515 0 obj<>endobj 516 0 obj[511 0 R 513 0 R 515 0 R]endobj 517 0 obj<>endobj 518 0 obj<>endobj 519 0 obj<>endobj 520 0 obj<>endobj 521 0 obj<>endobj 522 0 obj<>endobj 523 0 obj<>endobj 524 0 obj<>endobj 525 0 obj<>endobj 526 0 obj<>endobj 527 0 obj<>endobj 528 0 obj<>endobj 529 0 obj<>endobj 530 0 obj<>endobj 531 0 obj<>endobj 532 0 obj<>endobj 533 0 obj<>endobj 534 0 obj<>endobj 535 0 obj<>endobj 536 0 obj<>endobj 537 0 obj<>endobj 538 0 obj<>endobj 539 0 obj<>endobj 540 0 obj<>endobj 541 0 obj<>endobj 542 0 obj<>endobj 543 0 obj<>endobj 544 0 obj<>endobj 545 0 obj[518 0 R 520 0 R 522 0 R 524 0 R 526 0 R 528 0 R 530 0 R 532 0 R 534 0 R 536 0 R 538 0 R 540 0 R 542 0 R 544 0 R]endobj 546 0 obj<>endobj 547 0 obj<>endobj 548 0 obj<>endobj 549 0 obj<>endobj 550 0 obj<>endobj 551 0 obj<>endobj 552 0 obj<>endobj 553 0 obj<>endobj 554 0 obj[547 0 R 549 0 R 551 0 R 553 0 R]endobj 555 0 obj<>endobj 556 0 obj<>endobj 557 0 obj<>endobj 558 0 obj<>endobj 559 0 obj<>endobj 560 0 obj<>endobj 561 0 obj[556 0 R 558 0 R 560 0 R]endobj 562 0 obj<>endobj 563 0 obj<>endobj 564 0 obj<>endobj 565 0 obj<>endobj 566 0 obj<>endobj 567 0 obj<>endobj 568 0 obj<>endobj 569 0 obj<>endobj 570 0 obj<>endobj 571 0 obj<>endobj 572 0 obj<>endobj 573 0 obj<>endobj 574 0 obj<>endobj 575 0 obj<>endobj 576 0 obj<>endobj 577 0 obj<>endobj 578 0 obj<>endobj 579 0 obj<>endobj 580 0 obj<>endobj 581 0 obj<>endobj 582 0 obj[563 0 R 565 0 R 567 0 R 569 0 R 571 0 R 573 0 R 575 0 R 577 0 R 579 0 R 581 0 R]endobj 583 0 obj<>endobj 584 0 obj<>endobj 585 0 obj<>endobj 586 0 obj<>endobj 587 0 obj<>endobj 588 0 obj<>endobj 589 0 obj<>endobj 590 0 obj<>endobj 591 0 obj<>endobj 592 0 obj<>endobj 593 0 obj<>endobj 594 0 obj<>endobj 595 0 obj<>endobj 596 0 obj<>endobj 597 0 obj<>endobj 598 0 obj<>endobj 599 0 obj<>endobj 600 0 obj<>endobj 601 0 obj<>endobj 602 0 obj<>endobj 603 0 obj<>endobj 604 0 obj<>endobj 605 0 obj[584 0 R 586 0 R 588 0 R 590 0 R 592 0 R 594 0 R 596 0 R 598 0 R 600 0 R 602 0 R 604 0 R]endobj 606 0 obj<>endobj 607 0 obj<>endobj 608 0 obj<>endobj 609 0 obj<>endobj 610 0 obj<>endobj 611 0 obj<>endobj 612 0 obj<>endobj 613 0 obj<>endobj 614 0 obj<>endobj 615 0 obj<>endobj 616 0 obj<>endobj 617 0 obj<>endobj 618 0 obj<>endobj 619 0 obj<>endobj 620 0 obj<>endobj 621 0 obj<>endobj 622 0 obj<>endobj 623 0 obj<>endobj 624 0 obj<>endobj 625 0 obj<>endobj 626 0 obj<>endobj 627 0 obj<>endobj 628 0 obj<>endobj 629 0 obj<>endobj 630 0 obj<>endobj 631 0 obj<>endobj 632 0 obj[607 0 R 609 0 R 611 0 R 613 0 R 615 0 R 617 0 R 619 0 R 621 0 R 623 0 R 625 0 R 627 0 R 629 0 R 631 0 R]endobj 633 0 obj<>endobj 634 0 obj<>endobj 635 0 obj<>endobj 636 0 obj<>endobj 637 0 obj<>endobj 638 0 obj<>endobj 639 0 obj<>endobj 640 0 obj<>endobj 641 0 obj<>endobj 642 0 obj<>endobj 643 0 obj<>endobj 644 0 obj<>endobj 645 0 obj[634 0 R 636 0 R 638 0 R 640 0 R 642 0 R 644 0 R]endobj 646 0 obj<>endobj 647 0 obj<>endobj 648 0 obj<>endobj 649 0 obj<>endobj 650 0 obj<>endobj 651 0 obj<>endobj 652 0 obj<>endobj 653 0 obj<>endobj 654 0 obj<>endobj 655 0 obj<>endobj 656 0 obj<>endobj 657 0 obj<>endobj 658 0 obj[647 0 R 649 0 R 651 0 R 653 0 R 655 0 R 657 0 R]endobj 659 0 obj<>endobj 660 0 obj<>endobj 661 0 obj<>endobj 662 0 obj<>endobj 663 0 obj<>endobj 664 0 obj<>endobj 665 0 obj<>endobj 666 0 obj<>endobj 667 0 obj<>endobj 668 0 obj<>endobj 669 0 obj<>endobj 670 0 obj<>endobj 671 0 obj[660 0 R 662 0 R 664 0 R 666 0 R 668 0 R 670 0 R]endobj 672 0 obj<>endobj 673 0 obj<>endobj 674 0 obj<>endobj 675 0 obj<>endobj 676 0 obj<>endobj 677 0 obj<>endobj 678 0 obj<>endobj 679 0 obj<>endobj 680 0 obj<>endobj 681 0 obj<>endobj 682 0 obj<>endobj 683 0 obj<>endobj 684 0 obj<>endobj 685 0 obj<>endobj 686 0 obj<>endobj 687 0 obj<>endobj 688 0 obj<>endobj 689 0 obj<>endobj 690 0 obj<>endobj 691 0 obj<>endobj 692 0 obj<>endobj 693 0 obj<>endobj 694 0 obj<>endobj 695 0 obj<>endobj 696 0 obj<>endobj 697 0 obj<>endobj 698 0 obj<>endobj 699 0 obj<>endobj 700 0 obj<>endobj 701 0 obj<>endobj 702 0 obj[673 0 R 675 0 R 677 0 R 679 0 R 681 0 R 683 0 R 685 0 R 687 0 R 689 0 R 691 0 R 693 0 R 695 0 R 697 0 R 699 0 R 701 0 R]endobj 703 0 obj<>endobj 704 0 obj<>endobj 705 0 obj<>endobj 706 0 obj<>endobj 707 0 obj<>endobj 708 0 obj<>endobj 709 0 obj<>endobj 710 0 obj<>endobj 711 0 obj<>endobj 712 0 obj<>endobj 713 0 obj<>endobj 714 0 obj<>endobj 715 0 obj<>endobj 716 0 obj<>endobj 717 0 obj<>endobj 718 0 obj<>endobj 719 0 obj<>endobj 720 0 obj<>endobj 721 0 obj<>endobj 722 0 obj<>endobj 723 0 obj<>endobj 724 0 obj<>endobj 725 0 obj<>endobj 726 0 obj<>endobj 727 0 obj<>endobj 728 0 obj<>endobj 729 0 obj<>endobj 730 0 obj<>endobj 731 0 obj<>endobj 732 0 obj<>endobj 733 0 obj[704 0 R 706 0 R 708 0 R 710 0 R 712 0 R 714 0 R 716 0 R 718 0 R 720 0 R 722 0 R 724 0 R 726 0 R 728 0 R 730 0 R 732 0 R]endobj 734 0 obj<>endobj 735 0 obj<>endobj 736 0 obj<>endobj 737 0 obj<>endobj 738 0 obj<>endobj 739 0 obj<>endobj 740 0 obj<>endobj 741 0 obj<>endobj 742 0 obj<>endobj 743 0 obj<>endobj 744 0 obj<>endobj 745 0 obj<>endobj 746 0 obj<>endobj 747 0 obj<>endobj 748 0 obj<>endobj 749 0 obj<>endobj 750 0 obj<>endobj 751 0 obj<>endobj 752 0 obj<>endobj 753 0 obj<>endobj 754 0 obj<>endobj 755 0 obj<>endobj 756 0 obj<>endobj 757 0 obj<>endobj 758 0 obj[735 0 R 737 0 R 739 0 R 741 0 R 743 0 R 745 0 R 747 0 R 749 0 R 751 0 R 753 0 R 755 0 R 757 0 R]endobj 759 0 obj<>endobj 760 0 obj<>endobj 761 0 obj<>endobj 762 0 obj<>endobj 763 0 obj<>endobj 764 0 obj<>endobj 765 0 obj<>endobj 766 0 obj<>endobj 767 0 obj<>endobj 768 0 obj<>endobj 769 0 obj[760 0 R 762 0 R 764 0 R 766 0 R 768 0 R]endobj 770 0 obj<>endobj 771 0 obj<>endobj 772 0 obj<>endobj 773 0 obj<>endobj 774 0 obj<>endobj 775 0 obj<>endobj 776 0 obj<>endobj 777 0 obj<>endobj 778 0 obj<>endobj 779 0 obj<>endobj 780 0 obj<>endobj 781 0 obj<>endobj 782 0 obj<>endobj 783 0 obj<>endobj 784 0 obj<>endobj 785 0 obj<>endobj 786 0 obj<>endobj 787 0 obj<>endobj 788 0 obj<>endobj 789 0 obj<>endobj 790 0 obj<>endobj 791 0 obj<>endobj 792 0 obj<>endobj 793 0 obj<>endobj 794 0 obj[771 0 R 773 0 R 775 0 R 777 0 R 779 0 R 781 0 R 783 0 R 785 0 R 787 0 R 789 0 R 791 0 R 793 0 R]endobj 795 0 obj<>endobj 796 0 obj<>endobj 797 0 obj<>endobj 798 0 obj<>endobj 799 0 obj<>endobj 800 0 obj<>endobj 801 0 obj<>endobj 802 0 obj<>endobj 803 0 obj<>endobj 804 0 obj<>endobj 805 0 obj<>endobj 806 0 obj<>endobj 807 0 obj<>endobj 808 0 obj<>endobj 809 0 obj[796 0 R 798 0 R 800 0 R 802 0 R 804 0 R 806 0 R 808 0 R]endobj 810 0 obj<>endobj 811 0 obj<>endobj 812 0 obj<>endobj 813 0 obj<>endobj 814 0 obj<>endobj 815 0 obj<>endobj 816 0 obj<>endobj 817 0 obj<>endobj 818 0 obj<>endobj 819 0 obj<>endobj 820 0 obj<>endobj 821 0 obj<>endobj 822 0 obj<>endobj 823 0 obj<>endobj 824 0 obj<>endobj 825 0 obj<>endobj 826 0 obj<>endobj 827 0 obj<>endobj 828 0 obj<>endobj 829 0 obj<>endobj 830 0 obj<>endobj 831 0 obj<>endobj 832 0 obj[811 0 R 813 0 R 815 0 R 817 0 R 819 0 R 821 0 R 823 0 R 825 0 R 827 0 R 829 0 R 831 0 R]endobj 833 0 obj<>endobj 834 0 obj<>endobj 835 0 obj<>endobj 836 0 obj<>endobj 837 0 obj<>endobj 838 0 obj<>endobj 839 0 obj[834 0 R 836 0 R 838 0 R]endobj 840 0 obj<>endobj 841 0 obj<>endobj 842 0 obj<>endobj 843 0 obj<>endobj 844 0 obj<>endobj 845 0 obj<>endobj 846 0 obj<>endobj 847 0 obj<>endobj 848 0 obj<>endobj 849 0 obj<>endobj 850 0 obj<>endobj 851 0 obj<>endobj 852 0 obj<>endobj 853 0 obj<>endobj 854 0 obj<>endobj 855 0 obj<>endobj 856 0 obj[841 0 R 843 0 R 845 0 R 847 0 R 849 0 R 851 0 R 853 0 R 855 0 R]endobj 857 0 obj<>endobj 858 0 obj<>endobj 859 0 obj<>endobj 860 0 obj<>endobj 861 0 obj<>endobj 862 0 obj<>endobj 863 0 obj<>endobj 864 0 obj<>endobj 865 0 obj<>endobj 866 0 obj<>endobj 867 0 obj<>endobj 868 0 obj<>endobj 869 0 obj<>endobj 870 0 obj<>endobj 871 0 obj<>endobj 872 0 obj<>endobj 873 0 obj<>endobj 874 0 obj<>endobj 875 0 obj<>endobj 876 0 obj<>endobj 877 0 obj<>endobj 878 0 obj<>endobj 879 0 obj<>endobj 880 0 obj<>endobj 881 0 obj[858 0 R 860 0 R 862 0 R 864 0 R 866 0 R 868 0 R 870 0 R 872 0 R 874 0 R 876 0 R 878 0 R 880 0 R]endobj 882 0 obj<>endobj 883 0 obj<>endobj 884 0 obj<>endobj 885 0 obj<>endobj 886 0 obj<>endobj 887 0 obj<>endobj 888 0 obj<>endobj 889 0 obj<>endobj 890 0 obj<>endobj 891 0 obj<>endobj 892 0 obj<>endobj 893 0 obj<>endobj 894 0 obj<>endobj 895 0 obj<>endobj 896 0 obj<>endobj 897 0 obj<>endobj 898 0 obj<>endobj 899 0 obj<>endobj 900 0 obj<>endobj 901 0 obj<>endobj 902 0 obj[883 0 R 885 0 R 887 0 R 889 0 R 891 0 R 893 0 R 895 0 R 897 0 R 899 0 R 901 0 R]endobj 903 0 obj<>endobj 904 0 obj<>endobj 905 0 obj<>endobj 906 0 obj<>endobj 907 0 obj<>endobj 908 0 obj<>endobj 909 0 obj<>endobj 910 0 obj<>endobj 911 0 obj<>endobj 912 0 obj<>endobj 913 0 obj<>endobj 914 0 obj<>endobj 915 0 obj<>endobj 916 0 obj<>endobj 917 0 obj<>endobj 918 0 obj<>endobj 919 0 obj<>endobj 920 0 obj<>endobj 921 0 obj[904 0 R 906 0 R 908 0 R 910 0 R 912 0 R 914 0 R 916 0 R 918 0 R 920 0 R]endobj 922 0 obj<>endobj 923 0 obj<>endobj 924 0 obj<>endobj 925 0 obj<>endobj 926 0 obj<>endobj 927 0 obj<>endobj 928 0 obj<>endobj 929 0 obj<>endobj 930 0 obj<>endobj 931 0 obj<>endobj 932 0 obj<>endobj 933 0 obj<>endobj 934 0 obj[923 0 R 925 0 R 927 0 R 929 0 R 931 0 R 933 0 R]endobj 935 0 obj<>endobj 936 0 obj<>endobj 937 0 obj<>endobj 938 0 obj<>endobj 939 0 obj<>endobj 940 0 obj<>endobj 941 0 obj<>endobj 942 0 obj<>endobj 943 0 obj<>endobj 944 0 obj<>endobj 945 0 obj<>endobj 946 0 obj<>endobj 947 0 obj[936 0 R 938 0 R 940 0 R 942 0 R 944 0 R 946 0 R]endobj 948 0 obj<>endobj 949 0 obj<>endobj 950 0 obj<>endobj 951 0 obj<>endobj 952 0 obj<>endobj 953 0 obj<>endobj 954 0 obj<>endobj 955 0 obj<>endobj 956 0 obj[949 0 R 951 0 R 953 0 R 955 0 R]endobj 957 0 obj<>endobj 958 0 obj<>endobj 959 0 obj<>endobj 960 0 obj<>endobj 961 0 obj<>endobj 962 0 obj<>endobj 963 0 obj<>endobj 964 0 obj<>endobj 965 0 obj<>endobj 966 0 obj<>endobj 967 0 obj<>endobj 968 0 obj<>endobj 969 0 obj<>endobj 970 0 obj<>endobj 971 0 obj<>endobj 972 0 obj<>endobj 973 0 obj<>endobj 974 0 obj<>endobj 975 0 obj<>endobj 976 0 obj<>endobj 977 0 obj[958 0 R 960 0 R 962 0 R 964 0 R 966 0 R 968 0 R 970 0 R 972 0 R 974 0 R 976 0 R]endobj 978 0 obj<>endobj 979 0 obj<>endobj 980 0 obj<>endobj 981 0 obj<>endobj 982 0 obj<>endobj 983 0 obj<>endobj 984 0 obj<>endobj 985 0 obj<>endobj 986 0 obj<>endobj 987 0 obj<>endobj 988 0 obj<>endobj 989 0 obj<>endobj 990 0 obj<>endobj 991 0 obj<>endobj 992 0 obj<>endobj 993 0 obj<>endobj 994 0 obj<>endobj 995 0 obj<>endobj 996 0 obj[979 0 R 981 0 R 983 0 R 985 0 R 987 0 R 989 0 R 991 0 R 993 0 R 995 0 R]endobj 997 0 obj<>endobj 998 0 obj<>endobj 999 0 obj<>endobj 1000 0 obj<>endobj 1001 0 obj<>endobj 1002 0 obj<>endobj 1003 0 obj<>endobj 1004 0 obj<>endobj 1005 0 obj<>endobj 1006 0 obj<>endobj 1007 0 obj<>endobj 1008 0 obj<>endobj 1009 0 obj<>endobj 1010 0 obj<>endobj 1011 0 obj<>endobj 1012 0 obj<>endobj 1013 0 obj<>endobj 1014 0 obj<>endobj 1015 0 obj<>endobj 1016 0 obj<>endobj 1017 0 obj[998 0 R 1000 0 R 1002 0 R 1004 0 R 1006 0 R 1008 0 R 1010 0 R 1012 0 R 1014 0 R 1016 0 R]endobj 1018 0 obj<>endobj 1019 0 obj<>endobj 1020 0 obj<>endobj 1021 0 obj<>endobj 1022 0 obj<>endobj 1023 0 obj<>endobj 1024 0 obj<>endobj 1025 0 obj<>endobj 1026 0 obj<>endobj 1027 0 obj<>endobj 1028 0 obj<>endobj 1029 0 obj<>endobj 1030 0 obj<>endobj 1031 0 obj<>endobj 1032 0 obj<>endobj 1033 0 obj<>endobj 1034 0 obj<>endobj 1035 0 obj<>endobj 1036 0 obj[1019 0 R 1021 0 R 1023 0 R 1025 0 R 1027 0 R 1029 0 R 1031 0 R 1033 0 R 1035 0 R]endobj 1037 0 obj<>endobj 1038 0 obj<>endobj 1039 0 obj<>endobj 1040 0 obj<>endobj 1041 0 obj<>endobj 1042 0 obj<>endobj 1043 0 obj<>endobj 1044 0 obj<>endobj 1045 0 obj<>endobj 1046 0 obj<>endobj 1047 0 obj<>endobj 1048 0 obj<>endobj 1049 0 obj[1038 0 R 1040 0 R 1042 0 R 1044 0 R 1046 0 R 1048 0 R]endobj 1050 0 obj<>endobj 1051 0 obj<>endobj 1052 0 obj[1051 0 R]endobj 1053 0 obj<>endobj 1054 0 obj<>endobj 1055 0 obj<>endobj 1056 0 obj<>endobj 1057 0 obj<>endobj 1058 0 obj<>endobj 1059 0 obj<>endobj 1060 0 obj<>endobj 1061 0 obj[1054 0 R 1056 0 R 1058 0 R 1060 0 R]endobj 1062 0 obj<>endobj 1063 0 obj<>endobj 1064 0 obj<>endobj 1065 0 obj<>endobj 1066 0 obj<>endobj 1067 0 obj<>endobj 1068 0 obj<>endobj 1069 0 obj<>endobj 1070 0 obj<>endobj 1071 0 obj<>endobj 1072 0 obj<>endobj 1073 0 obj<>endobj 1074 0 obj<>endobj 1075 0 obj<>endobj 1076 0 obj<>endobj 1077 0 obj<>endobj 1078 0 obj[1063 0 R 1065 0 R 1067 0 R 1069 0 R 1071 0 R 1073 0 R 1075 0 R 1077 0 R]endobj 1079 0 obj<>endobj 1080 0 obj<>endobj 1081 0 obj<>endobj 1082 0 obj<>endobj 1083 0 obj<>endobj 1084 0 obj<>endobj 1085 0 obj<>endobj 1086 0 obj<>endobj 1087 0 obj<>endobj 1088 0 obj<>endobj 1089 0 obj<>endobj 1090 0 obj<>endobj 1091 0 obj[1080 0 R 1082 0 R 1084 0 R 1086 0 R 1088 0 R 1090 0 R]endobj 1092 0 obj<>endobj 1093 0 obj<>endobj 1094 0 obj<>endobj 1095 0 obj<>endobj 1096 0 obj<>endobj 1097 0 obj<>endobj 1098 0 obj<>endobj 1099 0 obj<>endobj 1100 0 obj<>endobj 1101 0 obj<>endobj 1102 0 obj<>endobj 1103 0 obj<>endobj 1104 0 obj<>endobj 1105 0 obj<>endobj 1106 0 obj<>endobj 1107 0 obj<>endobj 1108 0 obj<>endobj 1109 0 obj<>endobj 1110 0 obj<>endobj 1111 0 obj<>endobj 1112 0 obj[1093 0 R 1095 0 R 1097 0 R 1099 0 R 1101 0 R 1103 0 R 1105 0 R 1107 0 R 1109 0 R 1111 0 R]endobj 1113 0 obj<>endobj 1114 0 obj<>endobj 1115 0 obj<>endobj 1116 0 obj<>endobj 1117 0 obj<>endobj 1118 0 obj<>endobj 1119 0 obj<>endobj 1120 0 obj<>endobj 1121 0 obj<>endobj 1122 0 obj<>endobj 1123 0 obj<>endobj 1124 0 obj<>endobj 1125 0 obj<>endobj 1126 0 obj<>endobj 1127 0 obj<>endobj 1128 0 obj<>endobj 1129 0 obj<>endobj 1130 0 obj<>endobj 1131 0 obj[1114 0 R 1116 0 R 1118 0 R 1120 0 R 1122 0 R 1124 0 R 1126 0 R 1128 0 R 1130 0 R]endobj 1132 0 obj<>endobj 1133 0 obj<>endobj 1134 0 obj<>endobj 1135 0 obj<>endobj 1136 0 obj<>endobj 1137 0 obj<>endobj 1138 0 obj<>endobj 1139 0 obj<>endobj 1140 0 obj<>endobj 1141 0 obj<>endobj 1142 0 obj<>endobj 1143 0 obj<>endobj 1144 0 obj<>endobj 1145 0 obj<>endobj 1146 0 obj<>endobj 1147 0 obj<>endobj 1148 0 obj<>endobj 1149 0 obj<>endobj 1150 0 obj<>endobj 1151 0 obj<>endobj 1152 0 obj<>endobj 1153 0 obj<>endobj 1154 0 obj<>endobj 1155 0 obj<>endobj 1156 0 obj<>endobj 1157 0 obj<>endobj 1158 0 obj<>endobj 1159 0 obj<>endobj 1160 0 obj<>endobj 1161 0 obj<>endobj 1162 0 obj<>endobj 1163 0 obj<>endobj 1164 0 obj<>endobj 1165 0 obj<>endobj 1166 0 obj<>endobj 1167 0 obj<>endobj 1168 0 obj<>endobj 1169 0 obj<>endobj 1170 0 obj<>endobj 1171 0 obj<>endobj 1172 0 obj<>endobj 1173 0 obj<>endobj 1174 0 obj<>endobj 1175 0 obj<>endobj 1176 0 obj<>endobj 1177 0 obj<>endobj 1178 0 obj<>endobj 1179 0 obj<>endobj 1180 0 obj<>endobj 1181 0 obj<>endobj 1182 0 obj<>endobj 1183 0 obj<>endobj 1184 0 obj<>endobj 1185 0 obj<>endobj 1186 0 obj<>endobj 1187 0 obj<>endobj 1188 0 obj<>endobj 1189 0 obj<>endobj 1190 0 obj<>endobj 1191 0 obj<>endobj 1192 0 obj<>endobj 1193 0 obj<>endobj 1194 0 obj<>endobj 1195 0 obj<>endobj 1196 0 obj<>endobj 1197 0 obj<>endobj 1198 0 obj<>endobj 1199 0 obj<>endobj 1200 0 obj<>endobj 1201 0 obj<>endobj 1202 0 obj<>endobj 1203 0 obj<>endobj 1204 0 obj<>endobj 1205 0 obj<>endobj 1206 0 obj<>endobj 1207 0 obj<>endobj 1208 0 obj<>endobj 1209 0 obj<>endobj 1210 0 obj<>endobj 1211 0 obj<>endobj 1212 0 obj<>endobj 1213 0 obj<>endobj 1214 0 obj<>endobj 1215 0 obj<>endobj 1216 0 obj<>endobj 1217 0 obj<>endobj 1218 0 obj<>endobj 1219 0 obj<>endobj 1220 0 obj<>endobj 1221 0 obj<>endobj 1222 0 obj<>endobj 1223 0 obj<>endobj 1224 0 obj<>endobj 1225 0 obj<>endobj 1226 0 obj<>endobj 1227 0 obj<>endobj 1228 0 obj<>endobj 1229 0 obj<>endobj 1230 0 obj<>endobj 1231 0 obj<>endobj 1232 0 obj<>endobj 1233 0 obj<>endobj 1234 0 obj<>endobj 1235 0 obj<>endobj 1236 0 obj<>endobj 1237 0 obj<>endobj 1238 0 obj<>endobj 1239 0 obj<>endobj 1240 0 obj<>endobj 1241 0 obj<>endobj 1242 0 obj<>endobj 1243 0 obj<>/XObject<>>>>>endobj 1244 0 obj<
>stream x=ŽÍNÃ0„ï~Š9¶RêØŽÓâc+(¢’á¾$†&u~ºQ¼=Nh¥9ìì7³W¡¡Òhì Š-ªN\a­–æ¶.´0FºäiY.vþd Ü8‰ƒJªDÿ ÿ ?:hÿ 8•BM)íÎÂ×·C_­ãðE{fúð1Æ+õ3ŵoE~´ÐzIØwÒ©³MoøzõL.xk‡êLÙB íeîˆñÂÍtî©ÏðÙôC ߌ#1µs†ÏUÀ;Å.ðÒðàÅIü†@AÂendstream endobj 1245 0 obj<>/XObject<<>>>>/Annots 102 0 R>>endobj 1246 0 obj<
>stream xÍZÛ’E }߯è7  ÏN_æö¸ ìVªYS<;ö$<ãelC…¯çH}“í$ÅCÖREöÌéVK-µ¤îÍ_WZ•ø£Uc”­Õr¸*‹_Òÿ^ß]Ùº(UUé©AU¶ÐlÔ›+ÕVE+8¥u[T‚”¬k +YÁ¶®¨ë²ma PS‘B H!h®*­¡­™Qbš%‘<8éºa­ò€¹ a¿™pP<]pË;ìï[M2Ì*CèR’ÌÄ ™MÑN@ØWu°=O¬xÁÆð¾øäçWHœ€j-išH‰ÁÖ¤gÖ/ =JŠ 6ŠA^2p~Ä ¡†…&Rb°ðp­\bÉÃÎö”!UùgZ0#Ì"ßgJ@Ža9 1!ö».:1O@’ÙÀ—RhÆ`kMÞHÚH ¶-á‚xPÆti®GÝq踎ôc@V uÙÌÄ ˆ%ËŽ6.‘ƒu˜‚ a®\Ôû¹¬Eh1È~\Ç%qbISS0'Rb°9ìPbyIÛÙZ¤%#DZ”9ill&%ö iÉBÁˆI!ÚÜ<×+ÔÐÆ ªnð—e@ H‹ÒyȤĴh·dÖ‹­kh/‡A8]¶ð˜M¤Ä^,Í,–‰ØÛÒ –‘u%»“R¯ÿ™–̉IÓæñ0¢Dä™$‚8±š·)Mäp¶¬Ð€äI;Ç€& Â9sÎqíÌOýšotê·1paŸ)1éÊîH¬[– ²ØÀµ5©“8!”S&Y¨iÉ€ «Ihä´qÈ?™ôÇŽb« ƒ<1pÞ¨LJ }PÒJ%XI[Å+0mP‰-άW ÉŽR¾W‰AV)pA…DJœTʬW)Îõ*dV`RÉ!0Mb½JŽŽdT‰AV)pš“¡I¤Ä´tn3K‡Äß÷Pmñ?“ÐŒ0‹v"SBšœ9iµ,ŸdJ ¶«p,óT>B€znP< ™†%sÜ ÄymKÁÅBho-”Š·ÌQ-§ÔÌ E‘3]ÉfLF’ô>©»ÍöíbCŸ©ãèLø|3M‹,‘>cºý뮟¾áÏèRš¦ Ÿ_.ÆC‚¶ÀÙÅåþÓdÀŒ® Çž‘mr_p..¯ÕÿµŸµ¿¾­¢çÇar‘.ØkÑ/Æý´]–ûõv$¢.‹æëµ­0¤£Kk³‡Ð%³i6Y ^ýÝO¯ûè»s>¾þï>^`œ%½fÁ/M{#wÑêÙb·^ÒXBNÕíad·-6ëýbq“±eû„V:¶G£u1G±Ò ûiû~Z Ãz|OS(/Åòr»ê9ƒ˜6mÀ\qºDL©N|Ã&JSn6ëå"ždöœ÷îk³{NrôôðDîakDNШ´s1+N è¢~›Ö{8ç{Úgœ»œŸÖ›Up›Ã“M‰Å¸¢Á¦,\óþëÃ8æ±'PÝÝÐP´Li½qŸt]?z‡=É7 µæ<›ÜoûwÛiàMÁ1›LÇCTm£‰?­ßN‹‰¥E ¡rúš÷CÿЫ~\®}P ôÛXùNcðKc]’æ3Í­[*l0Ô›j¢ÛÞ¶Ó¾g‡¢¥íšX-âx?¡U2Á®/­êçåéP¡P ® òh† &£Ñåf+BøIwÅŽðt0Y‡¬£ñœqŠg ôè"ëè¡`÷bÈ}~+›Õ-Çž`Ž-:Ë£2WàŽsÅ'…2i‚ñm˱|oÙg’ª=Mª/F$UTëSµ Â—\÷± :ï'É4¾*CŸ|¬Í…îÈ¢Óº’YÈSø²ßíïù®à©1,Õýb· Å2d܆ ò‰eŒÏ,'á /[5l}zÇm;§ý›Íf››TüZ?~%û„-…a­gºM2î¡§¦|$žG*=Á_ (ÊþnXõYË}CJü¶8Ë%§§×ȯãNcÚüYSæºe‹êÔ!ϧ'Þ·ÆGѵH÷h\˜kcCO1#x,Ññà úà \ޏéøì¦>i8UÌ:w”ùñÛ±/o.Û‹×ß'´×†–ääîƒêtjîýWîÁ¿øígÎGþÅ‹ûSºÏ$Ë›«ó…ÁÖ¶Wc?Ûá¶Ã=#v‰ðóí0FqE¯nªØ7ªWýÄå[Jt¦MwÑúf9§È‚V‰¦Ò—³ûÃþú®ß“W©l}yåè[·3¼Šý§x³\†î4\›ñˆØ•ñð± ’Òjb¶—S?àâ·&Þ뎴ø‚À†¶¸Áä‘Ig¥ì j゚®ïûßû‰ô«éõë ³ ßç+"ÿØ€³ÊußOëíÊ?öà]H¤t¼ÊNïËð”@Éý¢çäÄλÿjàÈ!g¥ìçí8{‹–èÏîèº[Å{óöèìã¢i»ËûÊ[r}‹_J„—î¢BÏV!Õ¦¡—_ÿ¨­ü#¶¢Gk%ž¨ñÎ_ÒÕ¦ÁK¯)è~œ_ýrõ'š<+endstream endobj 1247 0 obj<>/XObject<<>>>>/Annots 129 0 R>>endobj 1248 0 obj<>stream xÕZÛnÜ6}÷Wè1…±²(êÚ7Ûˆ1âÆäY^˶šÝU*oœ¦_ß339’ÝRošéqÄá\Iå%øÏDeÙ"Zm’8Áˆÿëý9Dynâ,ÚDi\¹ßëèê  MdŒŒ(7ŽÉ"‚81/OãT‘¶ 9¶Âà&Âÿð>4QÁM„y¹âÄ Ë¸Vœ‚›¨.&óÄbÒÊ¿^˜gÓã…-†sâ… õœ‚xa=áD&ö§ò2™Ž!žS2óØF¹çÈDY•³‰*0ò›$J<(7QUaǧ v%Ë`@²‰2X‘L”ÕPD½OAìJABùAâ„ЄT¤Æä­ÕOåMËòš 9P—;B[ÅF‘“& ­ÖO±Yζ(JâXÙ†ØÏÁ¤#„PËûêIjÓ¸ÀöÈZ„µ:ÎXãI!¶âõxVÄšš,‹cÖê8c,m»'5†X8)ÖX…Á"Œ°ïUlÉîX…‘RÞ\Ïj ¶ÈA²¨“ìD¢ƒ ŽãLF;™yRcZù„b['äda®ÂXò‰f5[Õ–bÌB:…-͓ߴ܀ ö>P ræÑÓ8ŒlQ°ŸpÜ ‘ âmä&S"6sEò¶Úܲ—°ó ¡ b¢!KRc°°H©X m(äÙZ–·–@LDüA¬'5&±–ÄzVĦ%‡µˆeÄ:Îdm¹'5†Øš²©b†!‹”XÉÌ9YÌKÖFÒ¬,F^ fì8d¬Èsä<)šj¨Ía14MA¤è û8ik­—B5‹¸«ÔTÁbá™f¦ýK°Cáµì™iIYßÕ`¼\*É#ÇE7p ÂÛ+8¶â¨”¦u ®” ™\YGŽë\à¤-0´w,E¶ `°HÄV±l¬4£4ê*´š)bgR*ÑÔb3*)ŠU˜¼‹Â?°òR›Iö§¶ e^ê8÷OÊÄTú,Yƒ0Ñqd÷Ù–ž”M˜6!#ò,yžA»@Ûu­4QA$ y8vSI—Â}—æÈFŽm8 (Ž\ÀäÎLv‘cí§ ôCLRh¢Æd+Ú•0Uc°5å¼Àž,çíîpÕx0ZÞr¶J-É2Me£å 7ÆI´\½Z6×ë6êo£Ó~»k·»‡_–¿åXM]˜"‰³ªŽ)šd ݼÊcz}mކ8zƒ‰Ãç¡_µ<^%©#¯¾mW÷C¿íþjv]¿¥©TÙêJøx²J”Ä:%QCÊÊ’²Š†žÄ¨(ù¶_}¢1t6›í ?O°™{ðâË®ý“F-ŠLAÇ܇–áYMëY”‰Ö0å•+ ÏÚíª¥AœFµƒŒýþÊÙ tP+Ù¹äm¬ 錴߅뷋*¶jÞýEJG8I1¬Ó~½nW»î‘Í‚V )ÈyÙñ04ßHS‹`+Üà»ÏíÀQÆaˆÒŠøI¤4±Œ‘èæ Ã;Cv™jŒzV'n3ôÆ¿Ôï¼$‹,L‚ÈŸê(Z¦£2ïû5Û‡¢¤ÍÇ6eÓa8K]By©Å?yOÁѵ¨èšAÙëIÞxÛmÛf˜ûâñú®½ÎãrÏ~åJI!ˆ ­LÌJzü^·ÁÅ×îíƒ6Ÿ §”€~^p¶åãEÓ{™‰ƒYNoJÖB—+®É· J2ÍÀ¡Ç"§Æ®§!\¹ØÄáhyß \Épz!wÙ ;Î1¨yu9fž«þv÷µ$%ÁOGï½lVŸš;÷>ÛòòÇïMY°þ©™ï‹Öÿ¹¶#‹+_¹©Ûú5‰C‡ˆ3©ÛœŽ;”ÉæÌ²0näü^LÓOžüȺ)ºªjs<º0W+Êy­ø°ëÖ˜‹nZ|Þœ-MÜhõ'¿·’û“£3_%p£§Û18%»¬jVÐŽ5^_\%ë øf{ÛßkÒa¤ú<ñ{öªâ”¹ ë•I¡Ô¬*\†š|Òûê\%ذÞSsSIM0©.OªÂE»éθ$ ÝññcÓ­›kï«°1û*Ù•K§8ùN ó$›^àdƒÜ¶¸lºí9#n s3æÏCóÅAr-nEçqÑ3¹ö}{óeÕ }s³jv$¬Âñ£SÐ4bémìú¾ÇéäÙŠO:‹çßi@=I˜ïv÷Nt”{ò®ÒO´P©®­a±1FçLJ‡¿r.ǧƒ|L§‡‡4–R ‹ßI·½ÅRhÇg‘4èYS Uw¾î¯)¸›ÏwªKËã"{Ùsì¸>S|ÏÒ‘ˆÖC¥Êwíðص_i<ÛwS97qÅ©Ú?q¹0ׂLLcðÎ$­yºF@JË–(Ë_Ös]× -r¡»Lºdgm¨‘–NòͶÛu¨_áN¤¡­xîZaÙ›nËu=ì‹U§}TQ‰ŠI Š_‘³¡í”Eâ«ÈO‘KjiþñN»Èg–Q1¯»(zV 7¬ÉOàg±h¤r¢AÛ7IŠÝ0ôCË}=ÝŽ±M2¾)ýø>ý¹Èø¾±š€j ±zÞÁE›íÿ’äjé3fI5l®ÅÄdêlÅ£‡ÿýò£°/›E¯£3\¢»{TäSâ, #OKºÓ’xr.}xh‡è¢Ù~qe5Á×”`ÜLâžúæUבޝ—¿ü dbÔendstream endobj 1249 0 obj<>/XObject<<>>>>/Annots 136 0 R>>endobj 1250 0 obj<>stream xÍ•ËnÛ0E÷þŠY¦ OøÉekÔFEQ>@±eC…,%²œ"ßêaÚû"‰——3äá ©—…A? NÎ`{\42ÿû½á°V¢#‹~5<,yoæ‰ì錥ö¨(ÎZÔ£`/‘G‚s3£ó(KáGpš£`/‘(5†ÄL5¹š3\BSMnæÐ%®‹JIxÓ¢Q\=)ÙÙL5¥5mê&šõ¼á96.j‚ÇlZtó¢“'…§#º˜©¦´V¡LÜ!­·±bŽË`¢ˆi/’S_ÌT“ë±$n¢ÉõU&q}%ÃUæ/ùmcu¸_ò=ŒF¥éhœEé5仨‚òí]^<Õ%´{XµM_6ýéSþgq¿¶T~]N±K¥ÐÓÐîîg×nËÓ©íx&·¨´ÔãdÀ¦kÏÏ1ugÐã0~¨¿ €f4:KœtD !JžA'¬&®¯e÷Z•#°`? ÔÀ³¤:ÐvS uK±êÊ¢¯šC¤˜é‘šLI.Ky¸”R  ïÃïu)éFßÀé[¸‡²ŸØ4=[jbÛ•ûâ\÷<ÆUp)*‡öø‚ñÌUáÌ-Û·æå\uo‘.ð\·ý¹ÙöUÛÄgø5‰×ð:³¯úºŒ0!Cïúü eG©ñäWm]—´ï×`Ç_I~?Úç²£ˆ<Ú¹Û&žŠæ»:L¾êPëÇÂþGøò~í§ÇC4B‚¢Ç1SŽ!6uûTÔð¹ëŠ·<žÊ¾͹¨cù}J‚„¥ô0ïâá¯ùâ×â9¤¹Wendstream endobj 1251 0 obj<>/XObject<<>>>>>>endobj 1252 0 obj<>stream xmVËrÛ8¼ë+¦t’+-ù)'víiwS:ꈔ¬ýúí¡—Ë/ÂL÷L÷̯ٚVøZÓÃ5ÝÜSQ;ng«l…‡Ã «—GZ?Òv³w›ìqsG[™Î¬h[,Öýe£w²)¢vöbûs–>r“]·ZÞ¬²ëþ‘8»¦¿Êµ:µoi½îŽ]ßg›[>µ-½— COÞ‹s ÝâõiwAÑ9ó¦#ÕÞµT…Rx%©R•óg ñl¿>xQUÚHÙ£öÎVÊFÒ–"®.œê=’Ûs +Z®Sªr!uˆ^çMÄ…‚“!¢´Æ#ÞnQcðz~hóKÇœv½xW¥MPžjåC­@ÉQ]"Ïñy*„¥\ÎÉI "Þ žðWˆŽ±!í(3z2†¤ŠB›)fÊt€: ¨”È9€üme FžžšDV¶uhŒ`ôS’vùOÀ \í½ó•àb“È]S¡`ä‘BÞtx’ެ‹T¶¡A-Ý~ AG…JÀÎ=·×àBÕ¬ˆž²J¥°:T¸½PÍ n¾{\^¯ÂKúD£íø€W]fp™…:Y,nÍåfi§TXÖ@ÁÖÒk¼-Å$x×& ÓËmÍùJÛemàý¦R‡”:áÁz²‘¶Öû¢ø­¹ {ÒùÏ#–5¦ jBdK”Î2|ïšC™h€6z¾YŸi‡­Ã@Ø;»pUmTr|/RÞõ Ðä!êwOWà Æa'‹¤ÊIeZ£„ý"”;…t0¹yt0’ì>Ê= rzqšI ôx.uD5Ì«Œ=H[­¡E‘&éh:©wU4ÉtÇ3mÏCG Ñôœí§ÌgðÅ®b*\Ò^æ@Xáu HÅ"ãéZŠˆ™#>/XObject<<>>>>>>endobj 1254 0 obj<>stream xWÁnÛ8½ç+æè¶l'nìô–M·‡v±îÍZ¢ln(R%©¸þû}3”lÇé.‚Ø¢8óÞ¼y3ùq3§~æ´¼¥»{*››Ïë›Y1×§_aGÓçšß·´®qúãjQ,îh]É©­ËÑ?«hJzî\™ŒwÊštü°þûfú¼ ù<¿:¹½/V ¼9Zï5må…¸WAWÔèÆ‡#ùVÅïGŠ]Ûúð̸Òv•ηÝ÷·h§Ó˜Ú¿b©RÒám¸)W½{G¥ý»ƒ!#!*}ÓZÝh‡¸üîŒ&y5Úöp>žPÉ7¦¼´ª&=AÚA®“*Ë®é¬J=ˆ’mF@ß egð”ö*q.[ãt¤J%ÈúRÙ©ƒIû« ‡s€Hoiå7™ÕÍäòž<~É0Ή+QÐoþ _u#/õ9ÏH¥rä=ÒVSQ«ä¯’tÏvÖoB… Ž‘Pk”ƒá:|µ5)(ˆ ×Ât,è1‘Õ*&ÄÒtyíUDL²ÁﻪéÚ£®B nw±F¼36Ðç.2¼¯—X)t€ˆØ“h*]M;g¸–¸ŠÆÚ+ÔYGISÐ;*Ëø:ÈÚNêE;Ú™Pik¶RˆÐ é*Æ.ˆ¢/ÚÈð“õÆ vAÀ»K¬LcÑg OKẋUÓ BP;Sƒ¨rƒj¤ÐŠêÎZ4†KÁ[òФ«LLÁl;&—i¾TÔónÇ< ò¿ÈØ„áÙå=QüàdB0­é/;õŽ *ªtRÆÆk ëœø¿J}‚,r h¥N`…âsÐT­JÔú²/ªŠD-ŒIé[[DpUõïΚ4¨”,cÒMdŸÅå`f %_H±åPb5=L$Rìp3âë§Ï'Ë…ÙNáºùÛ“U@­ç†£fWIt8è‹A€ÔÎ# ]š:7BR·0âÜS|£þ üH˜¥i2!“(¸·ØôEy! W9£^> dJ• X0´ ±¬lovlK¦×[ÚÃÈP Ç?¾Á´UÛÚ7‡lÑ0WÈ Æˆ5hùÈ‚†™ŒÀŽS.ªjÔà»ïÄþ‘zc"7¢¤)þ±:4@N¶oË_…ÆFÃc¤B­ÂPöÞ^xÉOT/qGØ÷»XGö–ÙøV§ƒÆ×€|L?ÐÞ²Í.ÆL~«ÉiþO—Ñ1ô²48úËx¨­r„Ä}>1,#¼+ˆ/d pFh‡3»•ŒteîHeÿÏè“ÛU18ãeB&É{ûšÎkP„ãt°^Ò±…”`‘ÏØP Oø;éhÌ@;†ówLjÐ[õJ†oóhú)ó ®tó$w‰hÇü紺ݘjë>ô7s5rÓ ßô·ŠiªWØ"—µ ï°·à0˜÷Yà ÉEtT¼ŠÏ´ ÈNtÿ$pÁp¥WzØâ!Y&l ˜ìx†fŠ=Ô¯ýJÀT=‰cçÚ"O9ŽWÑ/t} ŒÎm[îAhtˆ¼ÕÈEÿT¼-²µrÚOÜVë»/§S\Œ&;Ó¯÷JÑ7¶dÈãv 8P’áå$¯PFÅÐGSK¼ìò%Ü$ ¼AAOnnf•6ƲÂ5­NS®×qr,ÞÐÜNS #’Ý‹®èØz¸åLök,þÀüëvÌhc3—ºây¯zW›˜7ÈAó„ d@9ZN.r G2p¶´¾Ûí%ð…pûþš¾\‹Õ}\,‹ÙbÅ&ð5o¡y ýγèwå:%kÜdxa²œ=ðiúo«Y±Âÿ4â-· óËúæÏ›hj'endstream endobj 1255 0 obj<>/XObject<>>>>>endobj 1256 0 obj<>stream xuUMoÛ8½ûW zrX±,%·»Yì¡èpo½PÒØæ–"’Jê¿o(ÙUÔ-'1?fÞ¼÷fø<Ëi‰Ÿœv+*¶T5³gZoЬHË«bŸmh™-iµÉ±†íû¿·;úÃÑ—ÙÇà ;¸}ûåOtÿô@y‘­èpD„Í~í :ÔéÔ’Õ\âüãÝÉ«¦ÑöDŸ\ÍæýáßÙýÓšò¼¿¸Xm³ý÷æ‡3Ó_Æ•ÊÐïÕ%Ñ¥WþB¡k[çc øê¨E ñb8>/XObject<<>>>>>>endobj 1258 0 obj<>stream x­VKsâF¾ó+úe #`yäF6±O9$…+.£Ñ#ÍhgFvø÷ùz„°[[ñnÊU2 iõã{´¾ŒRšá/¥õœ+’õèçýh6áÇËÅéáqIiJû‚}Ú¬¦Ûí‚öy<4£½SÝVA7•"§DN¶QNm§ÃXx ¥¢\A¹UžŒ $KaŽê0¹Ÿì_Féæüüd=ÝpMû||˜¯ÖÔÝî³Ç›Iº˜ÎùÀ{R!e‹ DP7©Ežk®„´'iëº (ì•3“0ùÿ’=×þÅj¨iÃM~´î'·¦:á¢èÍé Ö‘²$U©Z™p˜L?TM2ßLÓ-%óÕtÉÓØcÆ¢i*-ãäI Cµ0â¨Ð·ñÚe$*(Hß¡áƒkeh0ц,—Š0¯Ù‰Z¯Í±«hugL••ÝS&œÓʱëÁ¿ô= @RõogtApÀñ*t%20¥0_*9áNq(¶ÉTJáDU©Šj›ãŠ û¶i¬ *çòy4 4S’1§¢5øÀ¬ ¥è¡e +›‰ŠÐ—8*ÇhÇuªå‡sô”žM®œQ"”÷¤ ô}BÕµqV*ï©fz,PœS_ZíT~?lCưª-˜[ãŸ;!²9]%>Œkë¹@x˜pyƒ¦j$Ș‡gÄ<óiK` ÈŠI³b¶¤ ÍnÀ–§VcŠÚ(&¿éæÁ1ÐÙÛZÑñ‚ŽÂåœÝ·:ˆLW:Dª1O»Èõ\¤™„Sƒ®Í€®þç?KXžžðWï¹Ð`W-tTÖZÒg¦7é‡OÂÍ.'‡Iè !”ŸŒà8蟴CípŽkÄ€§9OãÝž 9 £ó§ÅrvåOÝÝ ½YâRT’)Ò.w ¡azö5Tv ê #/rûÎlFùiÂ£È Ž^ð} ÄòkÁw(ÿH¦RˤR¯ÐÞ@ÆÐì08µ,ij 0éÌóà¼IÙ6$¶H¤©âÙ$ƒÿ ®‡—tG¿ü±;L¾^u¿·€$«ö x³Øv¼ö=;¿Íš¸­àv"2¹n!¿L‘ú›ýU8ùů¾^Ódȳ\Ú(øÞYËÔk9Sðh%XËŽÒ™»ºÙZ"eLÉVÍ‹¨`Oȸ¤tï˜ðC]E&Úì¶ ³ÒÐ|·’X}@<: }U®Œï }¾»ûX¾d»í`í·Bt ~¯ø^Çxxìß@Ò%¼q³ OëÅt¹Ü°Y>uÛa×m‡g5ù›0­¨¸ê$]¯c@²žmãkÉ·½u¹™M7½‡,ù ¿îG¿þ²Âàendstream endobj 1259 0 obj<>/XObject<<>>>>>>endobj 1260 0 obj<>stream x’ÁnÛ0 †ï~ S V­Ø±cW¬=í0̽åÂÈ´£U‘h·"1Š[x88Cáåªèü¥¥¶ðK¡Á ÕËîæQèÿèÊוØÖ äëZTIØsÀ½6:žàÔýÂÏQ:š¯ðŠ·À’ÈOÞ)êgТ98èÓ•LÚgœ3d®ÌŽÎÐ0Jæ|‚ýÕ»Àk0hÇ9ùæir>²¯ƒVšl\°¼ÛÏ<9:f}Empo¾Šq)k±‘5ȶåan’yOÆíÙ†{ïñà9°øhg4Ia.›FTm ySlS5H¶ü~šx¨Ïóó4³ßF[ZÜ®ÚB´muÉM:á{—ýÌþÆþŒendstream endobj 1261 0 obj<>/XObject<<>>>>/Annots 141 0 R>>endobj 1262 0 obj<>stream x­WÛrÛ6}÷Wì£2#AâUT:#;¶ãÖ‰UIqÒ¿P$d3% •«úûî‚*`zIÇ3´ˆÅžÝ={øÇ…ü³`jƒãC”]LØWNåí…o±)x®AŽc3G½¥°:ß^<“.ØÍ,ð=6Ã×)¶´lÏVK½ÊuÜe­B¨Q‚€y­…^$ËsþÞxK©H+3—ù­•~0Ûõ1:SŒºNMéH;¶5cnk¨ÎÅH=£!]ç ®N¹îø,–-ú¡åÅ hLy‘îrÙäÅ€¤h1H4•.V?“ý`Šƒ]ǘÍ‘ýp*/CºÎœkûØ+¶5Á6ÈÀõf§7Õ_`KÀ.Ù” ]vdÖ\•Û¬™­B¨—i)0!5¤™$šŠ1k2Ž.Ïý` e&3ºNí;4÷Ã5Y3ÒuβÖG§\Gp®âà;›r–AÐ* P—½\6’¢Å ÑTLyiBï2Ù¦H1˜Ñuºhß!²NåÅ`H×1åEªàC£SÒìxjÊçÅ hº@øfC“È.—À€¤h1H4•.V?“ý`Šƒ]ǘôƒ&áô± p*/‰îÂÜåúb|3Ñë-^8¼iÀfëûd‚7Žu4°|.’*ÉŸ‡pY'iŒ¿ ÌcXÖyN¿oç°(Äsfå›õWÄsÁ²¼‘ Œxƒõ ‡=ßÀ.|æÍ6œ”rÛ`¿ß3ž•)Ûå){¯ãXDåø9›0—õn'ŠŠ½TYÚ…D"¯Â$/¡ÞÅaÅcHò­(²°JDN›'0²h£ýp#ê êRy,rˆ“í–<¯`—†é• )KŽCEP ¨^’Rú Û‚ÿQãîô¸2QV¸-"ýÆzI¬œY="뎱& d]ºF¼O>yI÷³…òF’|Ÿlа8À;¾ãyÌó(áŠeÔ?±Œ·W©#Àª¡ é8B%¯”#<Þ¤ÊÝåX-†pµœÿkçzüól2^=ZCXÝÞÁC‘<'ùnê¯IUÖðøeü¸ÀÝï“*Œ^’&+P®Œ¦, ›+òdûShÄÇrÂS>®Òº¬c±…½(~/+™´ò-¬DI9„»åÝ—!Ìéñ~ñ ŸïЗ Kb]Ô¾ ŸòäÏ!Ü'yÿ>®À•U…‰ S‘s¨ód”Ý:­’Ñ®/K|×]$yÉ‹W^(ZÿSüÿÀèç$žÄàþûTdØè§Ì?¤1òOa`·””ìdÕnX;¥È8„qŒ­/ò0…§A.ö 6¥HyÅŸÞ´]ƒJÑ „˜»»Åêj¿¬–CXÌ—óÛ‡Cʋֆï®ï×s¬¶‡×_àT8@ˆW¤\Tší$ÇÆÃE¾GŸ?9ÌBƒ'W7< kìÒ=‡XHÕ—ð([صg6 7—¬7jñÏÔÞ‘È2l¨v ØØ8š½Ç¶-e¯Rªn¹9v>²Ež6éã Xª½h} _Ã$ 7) Ž=Ú$•lkߥŸ?Ž—ŠÄPîx”l±½á5L“óA²ž/o¯×‡n ùóŠM"êR#íä‹0žêJËbžíÁÈ2ß‘³ð8ˆ'ÛÒ¿?Gk$ÆÀƒ*Ç(9@ëx69“©añ ‰4=ü²¿U³ð[̱g'{Kž…Øü„Øž#DZØlæ"í6ó}:Ž«:ìÉ"TÆmŽq®O/c²6Úñ»rÒüm€ËÕØ§;Ñ•¨Ôfo\ùQ®gÌ>¹‚G’f¶IÜý|qG‹šUZê˜UôçâÈx%IÃyQŒÊ]q¼5dúHÒ‡Œ¨F4õµ*’LÄ6‡c¼ã›ÓIg»³ƒ)U8Þϰԩ´þíUÉ &,À“[Æç“×ë‹_/þ£¡yendstream endobj 1263 0 obj<>/XObject<<>>>>>>endobj 1264 0 obj<>stream x•YïOÛHýÎ_1ˉ¸±8ŽîSŽãGª–r`**!U&1à*±]Û¡å¿¿ÙÙM=»Ì^¯ª„ÈŽß{3ï­×ÁývÂÿ…0‹ N`µ=c\ùù£}T`:Oñg2 æøqmêJ˜&)~TK±úHëxU$€M¤¤¦ÁtPð2…Óø×â6—Ê|$ƒŠŸ,š$8°4#ÇØlC:Q8&ƒŸn‚“NE!Žqè|vÒú42¹„Iò\„Âr¡„m/u.“±E¨0ˆÍåwÒOfLd8FÌ…¦qŒôÓ™\!Žqè|vÒz8Æ›CÝ/“a÷‹PR.Ô”í¥6Y`2¶±¹üNúÉŒ)‚ Lj¹Ð4Ž‘~:“‹ Ä1ÏNµ>™„AL¹$s<†\¤Âr¡¦l/Éd‰IÛ"UÄæò;é'Ó¦H2#æBÓ8Fúét.’Ç8t>;i=Œ|D½~¾L„ÂrÑMÙ^j“&c‹Pa›Ë蘭̘"ÈpŒ”‹žÆ1ÒOgr„8Æ¡óÙ©Öãy‚?U.ÎóE* )jÊö’L–˜´-R…Al.¿“~2mŠ$Ã1b.4c¤ŸNç" qŒC糓ÖgsóÍÍE( !Ý”í¥6Y`2¶±¹üNúÉŒ)‚ ÇH¹èi#ýt&Aˆc:Ÿ´žÄž\„Âr¡¦l/µÉ“±E¨0ˆÍåwÒOfLd8FÌ…¦qŒôÓ™\!Žqè|vÒz¼ÿSÀùû% @Ê…š²½Ô& Lơ 6—ßI?™1Eá1šÆ1ÒOgr„8Æ¡óÙIëã©ù>ææ"€” 5e{©M˜Œ-B…Al.¿“~2cŠ Ã1b.4c¤ŸÎä"qŒCç³S­G³Dþ>&€” 5e{I&KLÚ©Â 6—ßI?™6E’á1šÆ1ÒO§s‘„8Æ¡óÙIëø¦"•¾'+>·0„\tS¶—ÚdÉØ"TÄæò;é'3¦2#墧qŒôÓ™\!Žqè|vÒz83¯šœs, @Ê…š²½Ô& Lơ 6—ßI?™1Eá1šÆ1ÒOgr„8Æ¡óÙIëãмht¾'GBaH¹PS¶—ÚdÉØ"TÄæò;é'3¦2#æBÓ8FúéL.‚Ç8teoO'†=@Lb|[œâûϲ5ac|ÿœ­Þ×Û&ÿööï“ãÃìëÁh £P½1ËÖoòMó”«EåÙ8‚ï0\†ªî!ÎËM~¿)T=©£jHóñú”˜È„¢(H’©*Ÿ_B“Ú²[1Z <¿¼¹u¸ÔR2Q‹ó8H§ø‚—$’Éè¾ìá¹h»²® _¯‹5”œ- B’V¦¤Ó)Ž‚=ké÷eµû?ÒäH]Á¦Ümú6w›ú{Ñ6¼Ë½)ð~yñªÓWŽŒ†áçAÄ:pÄ— = ëçîͲϫr·½;t»Âî¿×lú¢­«»CEn²Š|!þ«°hM84<™Û–¹»@‡ðjb¶­ ÎìœÿNîO ì˜zk wh¹Á¸¡Íû§¢…þ)Ǹ/nðõá·]Ùk ¡·ù dñ 3æ×í_-> h°eØIDún>þ}ÒÏ.©ÓŸ›Ã!½þDÛ×Dø?;5 ¡Óáv£Nos…–ºçH5j§·¬à6<¸uñã}sÙâêì$ƒ²SKe…7"V1#“%Y7fWVнt}±…¼®4»>[ÂòjI§£Ú–MǬßJxõ—‹8:üE•Ù~Ä•ìÔq~0é¼ìóÕS©0Låú*ÅóQÐ9_f‹ãsWâÕí ÅqºûZöÝ>]R3LM›"uzón™]ߌ †MÅ*¿{Hª±Ç+.NŽášüæ®hË‚zÛïÛ½çx=Qí×ÙI¿hýlØ×h7X½už_íªJí ,Â-Ô-<•x—›&Ó Jgxãë|~ቖ՗kÜoÏŦn¶EÕCý»Í暺íÕC r”/W¸ãêª;Rsôlëu§xU‹'HYõÅ#'ë¼Ïû—¦PÛX_×°ìñ£êÅ´­ûò9ïõ.ïÕN‡ªÀÏØ—„¾†]W¨gÔ lòö± èúv·êwmÑA^­!BŠ}3Ë‹ìäìäêT=^ÝûBùc@†âf¬¶h­ ‰U7ÒlùJ¤›²{Òì:E‹}·uÓ–8—#»omþÂÃ& ÔEoOÓý׊Ù,˜¤1L§i†± èlSßçX´x’tpƒÛ>äÕ.ß(ä(4€Ñl>/XObject<<>>>>/Annots 145 0 R>>endobj 1266 0 obj<>stream xXÛrÚH}÷Wô£S!qËñ:.jãÄ $»®ÚÄ€f£[$áË~ýžž ‰‘wË6†™éÓݧ{ºýºriˆ—¦#ò&ÄWCgˆ•ÓË·»+w2t\ògCǧ˜Üùäô)¢Uû|¾gaò‡ü:w>N)—´ã“äÍy³xXö¨’ö‘Çi²SûA,~JpY8¡¶m+K¡"s¹\Ç÷FÔ÷]gˆÿ¸ ôÙ\v•ÜãQ€^¨k ¦žãñ­Wï6é®BÅ®ÁÁø­­Lñ@@£¯+¬?E š8ÃÉXŸ½‡9ÒúÂsYx¹aÂêËÍï·ë=^§ß, ¶r'QùøîÒ. ¸èú VW_W/«1_b›« |-@Ð…I·‘H¼Á*Œe|a××ï‹ß¾-oV\n?->{—¦|RÉáf‹( Eïe6é-ÿñ]npÄ/–øfÑRW¸FØØ2ÿÂ&¶C¯6Ãs´ÃhïÔºDV%jƒ”æ# u?ƒÚîo?Aß—?y枟}îÔZU¹ •w÷ ÍÒª¹¨HîTx§ö‚Ëjã/´þX..ÔVk6½ÚC“…ƒÇ~†â¹ÑtŽ´÷qIôýãÆæ]6¶˜«Ï^ö3Q\j»>DœÚ¢ ù‚;†¾À3MŸôz<èÔ7L¢¡HÝ?,9?×7w÷«;²kS²àv.P¥Hô]©µ³’7€]ïߺyr稴˜.ÏzŠÙ ÔTtv·kGåE+¸pí øJô±w ݰª¸'s®\ªã#EŒî® 27]w¢({hf¼ ÉèÀÝJw%†¸Ó¡®ùºUE™« Æ-tßL? hÚ%¿'êÅÐl* {¥ª¡ÍÃ2°ÅØGéj,2¢ ¶HCŠ6Ñ(êO2Qè}Q¤ÛÃð„GËRäº! s" ü•aŽžpÜAãá¦pÈ«ù†‡£ü$œUœ<Þ‘“»æ±=&Ȉ÷|Ï O™wÄàŒz¼Î¹ñ OƒÏÇwïi‹I/(a¶Vƒ#ºíꎧ[©“ílŸ‹˜Š0=D<3`°@ãpË&Ô ƒ’ú§ªáˆªij}ÌÔÇaÖuˆÅgÕéŽGŽ?óhŒg#=K6 úÎLÞ‹ä ô Üw§x8ŒóýépÎ}‰/¨KçFö€Añ4ùòãöÙ ùäŒß®¯¾^ý |Ìèendstream endobj 1267 0 obj<>/XObject<<>>>>/Annots 150 0 R>>endobj 1268 0 obj<>stream xW]OãF}çW\ñ$lâ$äc«>°,¤‘€¥KÐö!Òj°'É{Ʊô×÷ÜñG\W+UDQ<žsÏ=÷Üᯓˆ†ø‹h6¢ñ”âìd1Ò~}[žŒ§!fŒgáˆ2º‡³ú!¥§“ÎcF‹y;‘ßu3Š&Ç]øeµéh‚#Šü¿û¼î£°;º¸PÑzË8/çÓp±Ó:ñP‡´ŽË+zWÅžÖ×Ëû§epÿ¸¢Í€¿…NêA’Y™ŠBM©z±Â6gŸº T–§2“ºpgë?O†DcD½NÛRǼN¤ª8Ù6–Néá”VšŠ½r”™Džã§¤L:'v’ráü´úLÂ$¥U¡°Ùß2¡jÑ;± ƒ/n§uäzü|³\=l·›³êUCÊ€b‘¦ô¾Wñ»Òkz +·ÒJKQw+€þ±jaôv imHjñ’ÊnT‰Ü*-ynÐ!‡c•úMY£™;‘Ò›°Š×öá??ÝüÀ¹ýÃü†HîxN™íQH<„‘àrÎyèßÙýD‰²2.`'çý`MnÍΊŒÜÞ”iB±<¥ëìñ0ÚœT a>]Ø@k„0® £K ¸·Æú|³ØÞ¤uЉ«`òJ4?¡Ç±r~Ü­>{ú„<\ß=¹!a%tq†rƒ|ö°b˜YOM\…"ù™4*cÙ+§e"k¶ŒUÒóSþm ÌñK]Â&ä®Y»õfà6g!-¯zjfß‹m´>•ôã½ÐÊeØ[Eƒqë¶K!¡yÁN½IM9ªsklæiGjFÓpÂÙ_cò qµs"ÜëßPåBû}Ûê­8ÙZ“ÑÕÃÝŽBýá“犞 QÈͽJ™·”í¥H¤í¸U)³¥QÐHÂÐÀz¤‚`„ò¦@pl²ó-½[‘çÐ@7#R±bú,r:9Ẕ&G’~~xH_q`Œƒ?16ø^K3×ÕËËäGDlG|‚+,ÿô H3s_ÕŒ±Á€Î¡>šÔ«S>ª[Ý›ÁÁ”>ËõUu -àu$ðÙÙ‚sÓ2²MÅ:'T ÄÄJi‘¡BÇaTyY§Ä.à&!Ä…2A§ç§žp¢å+èçôõC¾¸æµM³pQ£Íh:óvrì þ}[¾5—$Þ¹êZÜm&e¸ Ï ùc¿¶³§ò¥T0aÕn¨K9c{1¬ìç‡Õ=Y½û ØœOë€7VÓŽþë[_0š‡Ñâè<™x•t{ýk éÚÿŠãŠck`%ʉäMÀÿБj»p†˜TjÜ7˜X”+"⹸œÉ¼“w2+—âàÎB™ÿEûIÕ3 %M(C»¨›RKìfP1º|xfM<.W¨GôD6\¤¼Ô¨E-Z9²2{VEßîÖ¤)¤'è’ãÜúŽW´™tm•ßÁ² +ôÅ5âÌ­ˆëž‹ø•ò!õNÞ •¶iG‡än:FŸËf\÷}•¨vˆÃÄqiYÚèàNéòƒžµú¨ýÕU¥Éï4ÄðvžC•x‚z§ïâF\6âþªé»Ò‰ywô°®zNEÃo’s|¾£8 g¯Mú;h<‡œ±Œz¾ ú'!öφŠ{Hâü•;‘z<·‹ZŸËÆ•.Ó™KäSÇÜyÀEeDÎl .6ÞýX•Å+M"IpEÂ’Ú2ëËTИPÝæÎ}ùÉ¿JܪzêÚäýß%ü@W©ï–Ʋ‰¶M¢Ý@ìZìøêÂdñ¬w7^ôK…°ç,¸n&ƒNu6…VÙJS¾WßbîC“àëG–•ZÕ=»iÔMÉÁ‡¼‡ªÂ‘-uP¨ >î»ñÿÃÑø_4Y„#ÜŽ/ç¸^«ëSj^Õ•µâàèÙq'º)D³Y8™)˜ Õý+Â…ü©Ìs¤¸ë¶ìï“ù0œÏ'ø¯,,xýÍúä÷“7·Çgendstream endobj 1269 0 obj<>/XObject<<>>>>/Annots 153 0 R>>endobj 1270 0 obj<>stream xV]oã6|÷¯Ø{:°y’?å^[Ôq#¸ à^\Ü‹-Q2JôQRŒô×wIж¢(iÀ‘,Š;;;;ÜŸ½|ü `6„Ñ¢¬ç9}}[õFSâÃ8ôÉ2˜†øÏÞ¸ë5n3˜HÐxÖ¸Í GdÒxx¹iGR)|ºCÀ&ÑX&á”Ìç#ØÄŽ›È»üº¸ÁwŠª'à(ö³âŠÅHåžã)Ë )™šÇÒû8eYöùbóOïÓuÖûf$Ô©obo;œÎÀ>vÑÍÃA0"C½àëb½X~ywØ­G¡¨v+5BžG¢Šá‘—{X-úpÜóh¯í*.pM^J}ê°ùpй žS‘ú¢·½èÎÉ¡îHk0 I0‡ÁdHBØj}* 1;0äKæ ‘G…LÊ#U¬~ÀóT?Ó'U•\æ˜Ó?W‹‰‡5 §(³õóœ¼feZ™ôÛKuÑ8MÛË4gŽŠ°«â‰’™Á¸¾ºY„ò¶ÞZ0Z0ˆd^Ò¨Ô;7H^1©R×4Ïá׿Sþ‡T¹ ©|üèNV%˜·SKFoPn•äX¹[.¬˜ú°¾ÔZî‘âÕÒ¡å “DÚ›ÊîàGT÷FåmŽ:–F{)Xñðdh4ÒFš…(ß½OqˆïyŽeè;·ª×U¿.pn]¬ƒma—k.u%‚Å}SÀœfØ3 M­ÖŽ\\3ýÞŽ}¤\Ð`F…/šÜ‘ßѶ:£9™jÍ":™38ÊJĈðA)íDcÀkE#†0Qw™Ádˆ-!ìBhs– gír`ÞfŸÝŒÚÖkÕq°Ñ7øsµ¸ß|[,¯°)ŽPAS(ö0QXÄ^ÛáÒhëZ*Œ˜£ò³LÝ$uHxöÇ»ÚúÆ8°¾1D_ü®0Uì~$`­dªhf8ËSAæCãK8Ôk°¸´Ô–+!wHÔB)úT¸$j·„¡Öû9WôàGj^!û}€)µ?2Å,ÞÖ¹9÷Àeï·8®©sQ»uFÎhbc$:¸‚7¶À§€ä±•B½dUa4zP 1D¬@³ª}ì@U 2i!²e2ú2ÉX ‰”²]§âR>±þ¢wjZð÷=Ëõ ªB«6§ÏíúÆâmù×ócÐ'C=Œq*ÐS€9–ƒúhšŒ‰œ]¾¦×ä`'|ËÌøVh˜½—v…Ù¿Îûâµ9îîž7Ö=(X4^n=B¬óÖcÉ´dT‡ÀÜîoº»iy`>­ɯéx™/ž¾Ãól° Wj(œ þ/ÛâáÛÈùœ½Ä䌽ªñt¾ÑÂäY}£ýë–è¦ ¡é§ù3)«~Ãü\ÀÝZp/˜mÓt9pË$]XÑRª·ñ9Ó%øX¢©ª3du”p¢mÅpÖ\ýÚþfõiÎpÅ)™ÊpN*߬M-í¢¯,oçÿJ?Au€÷Õ¦--atêÿUMCÄ×ÝË_Gvêj7ÛNHŽ`2‘ñØÎyϬöï§§[šWÔ¨ÁlFÆøÂ`æÏõ1oØûx6&³i]­À×5ºÚôþêýv4]zendstream endobj 1271 0 obj<>/XObject<<>>>>/Annots 155 0 R>>endobj 1272 0 obj<>stream x¥V]oâ8}ﯸ}YQíà’„òQièlA} Û¦š$d'xHlÖvÚaý^Û ¡ ®$ßësÎ=÷:ÿœÐÃoâ$åYôðÎþçÛÌÞp’>”†û?<žÝÄø4´ëñ'°A*‡Ë)^gÉ®F2G§.m⤣Ü^Ä?Þn†ÁøÔçØOn`«d®hiS]Nûõ®Ýw톧ïk&`6U /ܬ!þ:›?Î@ªúª;¸ó‚Q“bHFVŒ_„ƒ!x¬ŽhÿhP¯¿ê“^Ðî9•Ê(*jM‘aŒÈÈçþz÷C»Á @{2 - Ø®XÎE¶è,.Žì^aCÿps;»»_.:„,.j¯êÛJ~ÚPejQ>FÖ \ên‘ЊãðätÉ7œü_vU4º8T³ÉòîXDƒ®Ö Ê ¹¢P¥èNçv؃C:¦Òð” áˆd-¸¹÷$+Y)Õ°82¡FªÓøgü@*áEªÍTm!ß/~ÛvMmšôMm S%Ôœ,M¸/L|¸ü„cÎÁðtÕ>Wšö…ñ.f"=a––ùÃíýŸh`´ï 4~h´˜L’—:?V‡×&þÕatzÙØuƒû%^3‹ér:¨G§1§¿ÝŒ°8 )7 ¾apí4“µ×ÎOý½ÙÍn˾؞N6KùÖŒnÝ¥íÄ<ÈFE 8XWTs a?*m —Lƒ‘`Ö þz»(gÆõá!—v³ß÷{½%Æ V2a4È ,,°8  2@KY ƒl bj¦LÝ^óIÓaF0–ZD+ºÊ2žpL u,§â"mK´,l jpU© <ŒjŠeLÙTš%†K?µÛÍ#‘À%¶ä!e†òB; pc&jƒl~œÈ!}¦"Al•¦9³$6w'P}Xiâ%ƒç—aëuC;Ñ !‰à¦âEjÌ&¯Åóe¹cíI¿sÒìþ Ý´©ï·V²*Ù”®zx|¥¨WXEºÝXhG¡Ág¹< þÓ-˜óDI-3ã¡´¾m…‘ß¹Hå‹'yPC[_mT•˜ UBY¬šôµ¤«‚9Ü/°°T9ø2²?õ˜ÝÌ>¿öÈš¿ÔÉc»u¹;Ó÷¦šcÆ©ËX#†ûxãò&Rd<¿l’õµ5MæNH‰¯ ¿¡’¢JQŽ]¹’EÝDŸä`Œ[i¬¬•Fÿ–¶Mõ–%<ÛA<ù6»Z›Ï\Ia›ž©âNïEG3ƒOš¥žp3]:>ÁRÐò­›p c¡¡±~>¨yg †CÒEp5ŒH¿?²nžùC{âm@Ë*˜SQÑÂFv›€î°7>âýþ°O†ƒúm)lØm|ö÷ÙBåׂendstream endobj 1273 0 obj<>/XObject<<>>>>>>endobj 1274 0 obj<>stream x¥V]oâF}ϯ¸DÂæ³DÊ¡!mHºqº­„ öÓØ3ìŒM–þúž;Æ@ØdÛj%’í;÷ãÜsϯg-jâ·E½6u.(ÎÎn¢³fØÄËý?»¤Æ¨K­E ØþÒ¿//;%Þ¦IQ\OŸ2ñ"*•d,MùJRl²Lè„R¥%½®¤¦X¤xX‡4‚©ü&²u*¯Î£¿Îš´:ažktøq2—zCÑàóø6¢Oƒ‡ Ѭ¶ÀÙÉÍ]ãñÖ©Èñ˜ÍÎ?t2«;;§…5Ù›¬ê´äTvί½ó“öEØåL~©Jˆ_6FʃP+“yÖ"“å— žqba•)¹b½66—É>G4Í%Ì £J ÄÜl$åbž•2`Á›ŸT%RgØCª{žoß Z¹ÌÍšR¹‘)%°³j^ä mI”•qnì–{ô4üA ‘© r[8ÌœïÔIÌøÊ‘69%h´–IH÷š¾(˜WW§/“i§]§áŸàÂ#qÛ'Óèö3"Ìjk+7 â¼hóªI8º_K=Ða=†GÍßÃzÜñ¥jÆÐdkpÌÒ£ÌsÉÁ¹Yse"_yFýüº§%ÒEš3¼3ç³­ž¨ô‡—ßçµ–±Z(€Ž¾ÅF/Ô²QÑ>\1Ìù"M(^ ½DG1¨}‡ãö£À‹T  ¸Nà_‹øEÀŸTðh¹åΧ²åY«ì݉wüòsùNÂS“Ë+’‰b,Ob~gî™-ôvÏe²òk>9 qnhh¥‡RiLl˜xøÁfˆ!rÙÉFè_ '­ 9$TçÐÞ!ÄCÅtdð†û&¡Ñ2\†Ø8®ÿÁwÇTN}cÔêï†3è…}V5Ìî¬}ÑÛn5 þã^fF§GW‹ ݳa¶×«Óâ²Y'fÑëqx”—ÿTøáýC„Š™q™ú[ø …ßD΋%•t`?@"XÖ)¸ïüT£ÿžÁ‡Ð”y´þgA»¶.½‡ûjÔ™µ=fH)×£á5ZAÃá5À%\÷]b4¯Ïãþ ŽO3ߦÈOõ¼œ¸Ã©ÆÖlTn ÕÚÕ ïžÏ KRd˜ÈÇòA¯QÕB–•ê¤rZAùrs2j;½Ã1W̱î˜ý‡ˆ~J£ËýÞK;ì„-zÒêÝê²FgRç%xßÄsàˆ¿C,K“¦ râ°ðÒ‹#O0þò••.ãœÂNB ¬u…õ u~¬ç…J–ŸñÀ'x§âe Ä–õ¬†•ƒ›€—i æO³U¹€°Ç æ¥D³<–2a´vû†p ¨jé „,ú¼>]YýEµ¨ .Ÿ?MnÞ‚Rã–½g9™?=ýz{jýºRñŠÜÊkûÚ€œejâÝØ–‚ŧjn6,ï@¥ã´HN\µ…“h°þòK·eõ¼Soªõ7 ®îÇÏÃû»»GÊ —_ªõ"|ñG{ô`{Í8„hÅÒæVÿêt:»›ÖáUSýŸÚ‡>GÃ1N°Û^ßUÆé`P÷ÚÿïÍmŒ*Aoõza·ßÁų‹;iŸ…}œš¹Hi`­Ø:zb¶Ü ]”´ªA¯yÉÖ˜º9ð•»ÛíuÃÞE¿\­6¿ºÎ~;ûˆXX©endstream endobj 1275 0 obj<>/XObject<<>>>>>>endobj 1276 0 obj<>stream xVmoâFþίª–H`H€œÔ@“j¸KQ¿ E‹½~é­w¹];ÿ¾3»61NÚ»Dì—gæ™—ýÖ@€ÿ˜ a4†0ï^€oNw©7‚Áhâ !‡«À»¬¬;óM[^'àߢÈ61¾šŽ½ëël"k3€MØ}a:c;ÁÍÅæŸŽ;®„»ð´¾y^=,ÝëÚF·×C‘çûå¼%LFïI.?/îŸþ¸iKÒ,LÁ¤ªìU& ()¡BVdJ‚Ší3º‘í4ÓÇÚGýÁ󱉺™ Eqˆ2ÍÃBéŒïÆ•wýáó†âpóå{Á?¶ñ”&“ „&õ ü€m—SæÜ{İø™4‚Gø üÒhŸ° @¬Ëù JE¾½pö‘YKDÿПá—/uÒáHÀ¬L-dSþeÑpêç{Làu–sø¥Rö1‰¾6ã ü0}6iÎóéWä½Ú¨Òí¢«KQ×ùyp7ƒCV¤°YÜ­Öwa£`WfÈ28–ı)ѳ,—†kÈKS@ÄãLrûó“i%s.1ïP×m 4:y^|Y­Ö¿;-ú‘€S±|VÅ[æáï”Kh[éAξr0¥F( J‰tµ1HÜv¹—Xý¦_«Ý {{ae0o£koLu9‹ Œ?L•-CjƒPÉ8KJmë sTg‘Î0“UGàÁqÏu± ‡¯SçduAi8Wè&oÅúd¥fué:ÑјÌJrcâRˆ0(8Ò¶×*Ñ,o›¦3ï{Â!v¤æ¬pýÔ†$ÔŽ ŸÔ±1Ûúu·=xä1×tÞè5Žš K)©¯1a>óÚ5Uʨ.%rŠY”•‘:èSÝ2 l¿Y5–vÌ b4B,Ьz¿Ÿü_=ˆ‘î†‘ë€†ß H–[âQ!ÎV’PÅ Û¹ÙvßL‚·H~Å-ùíÅÏÔU}Cã[G¶’Þ>«§Á1‰ ‰:â!T8ÍþŽK M×ÛÑ&¾ë Ÿª•<;ÿ^×]—l5Ú6eÜ*]hbPF°89ª¨®—ˆs˰ÕÑƒÉ æÂ(w‚鎪ÔÍ’ð`CE”³£«é¬ÀZp ¹Rû¯CüOÏè#²NR&7þp²R ¸Lp VûW%O0WÃ]ƒÍ_’ºn—x®cZÈëÝÏf‹??R¹’ª;ҪľâXù N2@PÇ3{a™ ;BÏEAº¦ã4Ü[QЊ¦Ÿ,y0çXàÜ©¸tUnç<ÚÒü[‰+Û†üþÄ?M×Ö=¥ÑtaY/f.ÂV]cµ#¢ö…â„~……òª1Š—*ºzÁÀÉfk—œoñŸ»J8XÓê"0˜ ¼ëé®&7 ¦4úï윀™Öìhà‰öàŠÉ’ Òì&ïú“àš¤‡xœÓ:uI%™ËÉ¥7Oñ¶‡çƒ½ºÙtþêü æ£endstream endobj 1277 0 obj<>/XObject<<>>>>>>endobj 1278 0 obj<>stream xmVÛrâ8}ÏWôÛ000!ÉÖ<$ÈR›ÛNÈf¶Šª”°EÐF–<’ áï÷´lCÆC¨€-õítŸnéçAŸzøôixDÇ'”d—Óƒ^ÔÃâö˽Pw ™>MýrzÓ4 2=š&- ^Ò¼ÑãÃèù!¾¸¾¸¿ˆÿ¢Íçé=êô£#èü*ºûÔ¹îfÞ%ÝÜÙÿº>Zä"yí^O.»ÎŸôz½½vJÇüÝÑ[%<çs-<~ k5ÿâ5ñãá0QFõû§½&? ¸¹Ÿ”ñwǃ-þÎÑI4`$Ê$’Š¥t’þé ¡ )SH—Ê\šTšDIOsY¬¥4¢ aÒò©Ž§MÞf’üÆ2£L½, ròçJ9ÙˆÐç2Q •ˆBYCvQ#¡b­8K õÎ!ÑÊ8é­~“)ÉwÄc„†ƒlÎéX8›±—¤ÕÜG•“×ÄøBh­Ì ]]P|xH—ʤxõçt¹¡T.ÄJízs^m†[Ð|¥taš àù•¬ÑR ¬>ÇÏñÝ÷QaXÄ‹Â-¬ÖvíÏB\çª<[;{ –¤ìèÏ»›¦¶‰WKKaþ(Ÿg­éþ×Jë-+ã­XRL‰åê¢bœ†Êy™}næìÑ—éªÀÃÂ'_23¬À…Êr-3 ‚¤´ *cë 'Lé"¢§%h²ÃàmýF.jÅÅÊ$Ì¡Uª1ÌAw¸˜o¸ÌÊ+¦e.\á™'R«LQÓRx¯%â}+)gê!UnÎCBªUya~ ÊÛ.@ź ‹õt%Ç›åJKçiÖÒêU¶áÇÿ’킘N84OÃsºrS•xåçÑK”„n念s`­Ëµsæ—v¥‘¹å>y–[ˆLé su?+S+=SÚ¯ò…ª“²ÏŬ%£—¨,ê©æ¢—XúRåêö±±|x¸#ÓÇH:Ž¿*¤® IBñ__ïYëCP¨óǾâ–gfŸ‰Û2ŸQ•ä ¬6ãÙ¡N _¦¥Õîøì÷ixñ€B‡ qévÊî÷ŽÍ©å& =ºìÚtó@÷v-]M:0w¾búGùÚ¨"#}áuðKºv(6+‰8è4 Ér’í·!?VÎc*/“¾Œ„чÑöÎqå¬áQEoèC˜{6t BÀ€ÞJ9ãÄØÜZy5vMk>QbÈ—YæÓG¨^¥hD –ò‰T6~D`JjQåqBJ-“‚«Ãæºã“Š-íB-·ê£ºE…˜ã LQjž±5…³šî…‘ÀŒ%Ó‘³ä1j’[è\ÞÑvwö"þvèmù.x®‡ç'F^§Ê¿Ò·p€í.O¸amoì ²Ï“Ûøúñۈ뚞ÏfO˜³Ù¤L ‡×ùpwªup)úUÐpØW©9­RÓô£Áé1}ö¢ÓÞ)_Y®´£.œO™¹­<õ‡Ã ÐöÎX-M—Ì;ž²W,3¢áÉ)®Øïxi4=øûàCz3#endstream endobj 1279 0 obj<>/XObject<<>>>>>>endobj 1280 0 obj<>stream x…V]oÛ8|ϯX÷ ¶lçÃvú–~$-pÉõ®J‹üBK”Í‹Dª¤d[ÿþf)É–ÜEÁ‘–ÃÝÙá,^ÌhŠ¿-.éjNq~ñ>º˜†S¼<>ì†&÷×4›Q”"öf9oo¯(J|Ì”¢8x/’\e%•[Qâ!)©µÈUL™Ò/x¬­°J:²òg¥¬Lh]û°BØRÅU&,©¼Èd.u)Je4™”¿~!ÐØ KJÃKÞFÿ]Li<» /‘C ŠÂšÂ*QJÊLܬMõè®v¥Ìé㟺îñò GŒWÓwþ&YƸ«Õ^i]†-øå<¼fð/ÚÔ¦ YÇ¥±5%fÄa“{ðæ9qãn…ÎAEó¹£lÜÿš‡;*¥+Cy„ä7Vä MkIëJe%©fÛá.ÁfµÚdf-²ÕŠ—+½îœr|7\ÚOÀ'x܈0ȵ‹$QÜ‹y>ýþH…ˆ_ÄFRjMNϾ}D£Ri@eJ—Ò¦"–Ô•ñMk«´,)Î*4Ä’Ô;eæfŸSˆØTÒ±-üP:1{G·7“ÛeHŸÍ^î¤QRAq^ôñ¯oä¶íÌT® ¹Ñ±}dôq{R•«h¯°P›’öƾ„„.Y ÉIo[ýÖ´¨Ópj¹H x ùBxY͵ªÒ‘ÙëSApt2šsãÐa£wR+4–üÉdö”'­¨JªMe;@¿’åbÎha÷hòêX°™4ÊŸüNø¡_yò®i8Ÿ ,às'Êzxzö4²ÒTÈVÜ„}#;f œ!ZÆUÉý¡½*·¯$ù&<¼é üqº¦æÀ^iáP/NsÔèühª>ENŒþ5,Å‹o+v^ϹITZŸ: –†„ôDô;j`ÇèbHÞþäA°ÉŽH¥Ã^ Eƒö· òÚ—7¯;c ¸Ô×#ïÏ#!B~ÕKZ "c£P­ã!գˎ8kΊm¬ápÄV²Óû#A¯§r8O¥ÕcYòÝ/kZ÷îåÙÖþ ̹qµÕì6oe@¯ „€3`³›°2›Vÿ>Êrì oôØ•ßj‚GÏΓˆ¶€><³Þ)S9¶§fÃT §XÑð6ÂÌ…·~Îþ þ[P^e¥‚Rú 0œ¤ë†yH_¼.{d5 ÂÉAë`!;¿øÑŽê.¬ýpq~Pxgè¬Í¯þÐBã·ŽkýQrÍ ?¹¢Ò,Àïr«Gç}ÝLÚ˜vtŸ$¼g«÷b+ªu† Ë`£Óíƒû:îUÚ2*Š¥ƒñ§'}¸kD0[¶*/Â区Ëêr¾hóëÒðßÞ‹±‹{&‚ÔÀfÕ7xG;ÄuªñÃPÐ*xÖê°zKí|k¢;ì!­6Å-ü-=$þÜ+߀Ù9Çg› –LþXÑÝ?Ÿ¢ÕÛó%œk圃?Ü(Æ…p>/XObject<<>>>>>>endobj 1282 0 obj<>stream x­•MoâH†ïüŠº#Aƒù2ì-3JPVÊnvB´.݆žØÝžn; ÿ~ßjã „‘v“H㮯§ÞªþÞ‹i„ÿ˜’1Mæ”–½‘á—_W½É\Œh2[ⳤd!p2<ôؾ“÷wí¿û¼þèÌíhx;¥8¦uÎáf‹¹X.'´ÎBÄ­Ó(·Ea_)µe¥ Ykk(/äÎSnÕ{E«kª•¯©rvçdéIZýõDÒdô¯6™}õTÊg•ëBù«õ·Þð†ƒ â XgÑn˜Z“ëݰ;+öíá.¿HÐáœB*¥2™ÊHV*Ó=iOµEà´h2E?qÅIlãØåiÔÁÚXñ¢Ë,‹˜Æs1åÜ6ãyBçÙ Æ /i0‹y¬¥«µÙÑ«®÷Ìd"â>Y£@®)2òº¬ ¨Ðæ™ÏÙœÓ/t˜zÚŽùãeª£tIº2Õ§¨@Ã_  ðϽð…mY]ZÖ{YS†n!uY¡ÅF¥Ê{éìò$ ‚€ Jò•Ju®ST·uÒi…~°é¸aßíÐ-T¸ºmVKjûžCìŒrÎ Ç ýµ1†ñ€àÃQP­E'>?‹Ð—kï›R…? ´âíÜ0¢½„d §dv ­R†¶.jÈÊ’k éºÏ˜ŽümèþáÁ÷É7P™ôôÅÉ­'7h­£»ûÏôøÀ6èÓ’E±:Áã`²l«zòÉB­œ–µ"ðµ*y Jè¶¥whQ7åV9V,˜ºu_XN§9O:¼ ÃvÄ9?ʶ…R¿y“Pœúã#Äc'<Ƙ‡“(ü»~/±ñªµî”8³w6Ä% ƒ·¡)ù‘ù Xt$@Óï!‰ŒJUZw8¢ð¡²MdTýjÝ3jß\óõq66Q;Ú, ÌOóFtPt›«3úãKúwù/ËK$‚¶Ò#GุëDÜy Ù½B „4²çøøoo & ‰<®îN‰ôé¢5¼Ô:ՆΠ5ºmMõóÞD¬áóþÂG·Ž[¡¥è.\œëè$·Ç.ù­¿¬îW謭ߗ[Ÿn›oºöM¿•ÚŸËQ?(àxYô1›©DLû‚ M>IÎX2Æ æÀº„r:†ºêó@©7•6µ“IaŠOF¿bôC¯Ü‹âãEí÷aSÑž„c7ç ¢O•tØŽªøtÎ%ê@›¨.ž:Ý•~7ÔUú2£Í• ëâ jËXÓ »Ë8ˆoKJ9DÖFèn¦x‹ébB³d"¦Óp÷¬ »•];,'OO(Šî¥id¸ÅI Éhù?[všLE2_WÙœÍoÖ½zÿª>†ðendstream endobj 1283 0 obj<>/XObject<<>>>>>>endobj 1284 0 obj<>stream xmTÁrÚ0¼ó{ƒÌÄŠÁ†:“v¦‡´iâN/¾#°Yr%yˆÿ¾O§-S`<ãçÕÛ}»Ïüš$ˆé› Ÿ#ÍP5“˜ÅTy¿m_6×8Ö’âl×.FHÀhÕcÛIZˆabU²â^}aÞ–;ÊÉéÓg†çN–ÿ…y,o†ºãÀ‘÷à!¼&ˆ GG%ô`ì5~Pí‚)hÓâ´•´›CKÖ†=bÑŽ¾¥ìœgvÞ|z%yr²éþ7­º4>¢w(žoËRû²<ÐO™-Weé…óR>ÀyNFÞl*ŒvìÊj$Ës¶X¥XÌ—,‹W!™ÍÐwÖòÞá» “sÝquR~>åñ: ç”>Ù©‰ ›;<\t»È,ÏVôWB¸$¥ûbòmò?éEendstream endobj 1285 0 obj<>/XObject<<>>>>>>endobj 1286 0 obj<>stream x¥V]Sã6}ϯ¸0ƒ I }K· ݦ´¤Óf:²¥$[ÊJr‚÷×÷\Ɇàm§2Ìpäûqî9çêëhLøÓ|BÓ•õèÇÕè"¿ÀÃ×·¡ó›k_Ój³WóE~½¸¢•Œg.hUžLsúbtТÒßDÐÖ0’VÊÕÚÄÿOWO£ó›KSl²@ü ‚œÜXG[íƒuº9%¼5žOÂVÑ$“ºVÆ#$¾Ó&(·¥¢ƒð$Õ^Uv§$­µóáñôŒjaZÂ#“zÚŠ½¢p°ooú3.傲ñ4é­Q´F |ê8—pN´>öÁ…X|¸xP¸B'\ûîøã e,y”e²ã/Ïøq¡‰k뉲lP¬z<¥”0§²U" S*…A—k hUÕ¢#«ÔÏßUêé ª*ðÍng]6R1ÈÚBg.Vt~3ë¦rQÚ(÷~X'±£cÒ6ÊØ9UjÌð8c–ž½EîÞ)m½«ÔËàœn+[`¾Ë„ùÎÙ½–Š>EôÁÀmŽFˆ? ãà(Gƒô¢V`M­_”Ì*a6Ø0®ˆ¶q¢æð 㱈5ÙâI•Á'Ä#•«Zm62Œ µM í©8‚ÓüzWÍ wi †ÎÄÏ»o&³ü’Ù}»ì ð ö×Fcèµòµe;á=çc­Ü)ð¡¥eUÙR@ èän‰ê+]€kóBý랇`¡@Gp…z I3}ÓÃŒ¯BB´eBúŤE&cá?.Tį %ÇòÖÎH–CÒ˜Ù €ð­ª¦Œß'ÙQ³¨¸'ß!0†½[æô”­ùMåK§ p¹¤ BWžìº+c³ Í®Ý@6‘ÀÅcºýH¢ƒEɳ‡±ýÌx<Ó| èã8è>ƒ£¿·ªY¾ˆ³\¡˜h5´WŽ™Ï%½§.û’S< ”AãëëKH£ÈÙ¢ñîî¿fÀ×BçS…*™½bFÄÕ³¨7©e0Ã33!’ÐtØÒêÓíÝÃí‰ïؕԦìv(šaEÑ„­u쵿ÙB¹0ögÈDÃŒA퀓¼úmjˆ=Ÿ¼:£În¢?r ƒRÉœ(Öí£ᮅTÀ ŸÄÀÔ›¸@|`"hSV Ø+N=v’hc²B”Ïá$±³ÌBW:´´kÜÎzå´Åè6^‘Žrö53ÜkÉ@aÉ Òk,\ö!´BØcï}nßx'mäjØÎszkñÉËí*㎳¬ïŽéµäi À*~øü'¬Ì^;k˜B´‡?ÄHÏ8`Ài <ȉÚЦ­žuÈéCI¯ØØnœ×HæCoÀq”Ì®èQó\ñ?ù2ö\ é;H,ä^€îLÁD”S…0º&z`šmÏûÞ=W‰§]Åd”’‰ k :pëÆD•ãêÁG'áKÀÐñz€®Áõ‚Eó׈[OÖ§ãE;¸ÃÀOú; È”° pm`)=Üßýùp  =­š¬nª ¡ðŒ—ïÛ êéxÑ-Ülž/øþzœÌæÄå¼9Oüòõšâ[Sn1ñî~õ‘P=bŒ$<Å©Ð8C¦©aï{dñ°}3Î]ç¤åGrÎ YBl â”lâÌû5'æ+³×—¨ùîÂÁˆ×(éðÿÂ_¼mB¸”Då‚L&Â#€R=Ù"R€ÁG”sÖEfŸßüKîWf`?ᾯ&ùôrš6Õ¸b_Î/óùl‘85^p¿ŸW£_G¨SÐfendstream endobj 1287 0 obj<>/XObject<<>>>>>>endobj 1288 0 obj<>stream xuWMo7½ûWÌQ¬µd+–Ü›Z$nÓFA{0P»”ÄfInI®å×÷ ¹+íÒ ‚|IäÌ›™÷ÞÐÿ]Íi†_sZÞÒÝ=•úê×ÍÕ¬˜áÃónO7ï4ŸÓf‡³oW÷ÅÃÃmªxfF›r²9HÒÒ{±—ÓFx¯ÌžjµuÂè <K[Iʨ D­¾Ë ÿÝY')àâ‡õù¬0é´2"àØá?ùåßlþ½šÑt~WÜÅär¡è¾¹½/ü "7ξ¨JÂÑÒ®5ePÖxâƒ7ïQx,j²_Œ±8–>ï‹0žÅIUeGÃAr2´ÎDĦÕ[€·;†P¢7ÀËCÅ¥¨k´ˆ# êèΑªÐ)Ô‡s²æ{'tAŸ‚p{{Tá€dµ^Ò]1»æ”øç¥À/´$zuËЉƒT.˯´­A_!ø‚ Iáj…zº¤ž C{­¡²n=ŽÆOŽÖ}õAÄGL¤EÓ0â­ G) îd™¹'yöK'<%hb¬ N½€<L*¶Dñ>eùÁjåÞé«IõM¢ç‰±Ç ŽÝz[Ë Ÿßнj=hö‚¥Sà0V”¡u_ZNºu¶Ý^U•¡0»Û%^+ähh+œãæ>OöµÝ"´?™òà¬Qßc/¦Íõ*Dh¤‹_eð+‹ðÆ€W 1*­nPXÏ#cø¶ ˜‘©x,!èÖ¨2†»ÆØ7IœÁÚú+¢EÔ`§¼hg@`¨ˆ§ïÏ:CErH¤¨W¦Æ2¾¬Ôë(d‹¶è¼r‚DÏJKcì|áš².džD#ê"ãÜnÞ?Pò¶¶éíª¸gã`gy’Ú¾ÖumSoÆuñéûbÕû ˆ‚Ö@fwj¥Uô/Í2bÌQ,ûCæ€(I§  —Êü&¦ŸPøÜb¹(–÷+ì~lýùônsõçÕÿ{©_žendstream endobj 1289 0 obj<>/XObject<<>>>>>>endobj 1290 0 obj<>stream x¥WMoÛ8½çWÌÑb5N²±ÓËÂY4Ý t·Þ[€€–(™[‰TI*®÷×ï’’mÕí¥h‘66?fÞ¼÷føõbF×ø3£ù ÝÞSÞ\<®/®³k|8ü°½{º£ÙŒÖ%Öþ¶¸Ïni]„5×´Î'˺&S’ßJ*öZ4*u½'ü0¹ð² þ·¦F6ÆîIiú¸¤Ü4ÒQiMCÊ;þµZMµÚXa÷WÎ[…=—ë/®i:»Ínp1ßÇK/“Õòå²ß“ÑjIÃµŽ„.¨ZT¸é$„Î)]ñ¡ïžîSfr^ä_â‡}º>b¼n+E;^V(—«¶VZºŒ–zÏß¼éÊRÚ!²"DVÈ@ Å¨˜VZ῞´”…#/›Ö0&”Îq­Èå1‚ŒüjyèÑõç2V±Þ scQ:᧪ÍÕÖŠ}(LªÓéñ“30ü·À…â,pL)…ï,Žèœ,;üŽªµÖTV4# ¹éª …!×å[î=/-§7÷Ù×½–¥ÐYUm=U°…{Ï<|Èy6-ðbè éeîI•6Ïɪ‘ÚÓš7iwVy/5|1ŠF´('H†,®bæ³E¢Êtž-fÔ“ñåæ~NqEO›ðý5 œÆà®Ì E“ÚßšŽëÈÍy{å¼Ê!(ÀËl@ž ˆ$¤³³üè¦éÍ"›=`ÿð¡PžKÈ:‡È"dtäj’Ø‚ µÑS:Ê.PËyÛå‘ Ê‘6Lï\:‰íGøK›Î"»"£G™ ¾c'þWjbL°æãò ·sal$x ŠZý‡µ Td€¥TÖ8G lj£›Uf´Ž´8v‰Õò•Ï>-ø„ÊNçA·V~í€|Ó†h\+sUî{ –¸ùà©Ä+Bw¯x™¼\Æ/ûYd’"öZ‰¬Ñ D–Šl១h£˜f`áZ¡§‡NÔx¤ÇCÉÉQʰØ~i žÝÖP>ÃE†äeYbÎà-Î5RcgûPKïûy­ý5¤À1IõtU‡)ˆ‰Á¯…OÏä…û‚7ÏÖl4?ùkK|0á©S[ä–9ªO#ÎÍ a¼ 1Ä )Îö#'9~¤(M`I£ˆ^&`Z/q4`KÓÕàák+|¾±t¡~<9fWQÉ&Œë‡—à„ñ6˜£1&¬Ym'Ï“`Á5\ÛReLzEõ#ôl>Ïî·xMÞ᡹à‘÷cÊ–ñÙñþ•ИA8Ài¿a:¿~àÕü\ÅÌrxåÝü.›ß/ðRŪ›kþèÃú⯋ÿøc¬-endstream endobj 1291 0 obj<>/XObject<<>>>>/Annots 170 0 R>>endobj 1292 0 obj<>stream x½VmoÛ6þî_q&V-[ñK€}H‹µ°Ûê~Z†€–h™«Dj$×ýõ{Ž”mÅÃÖ{I€É{îÞ=w¿Rã7¥ù„¦3ÊëÁ8ãËñÏÏïéuš,(›eÉŒjšLçøW}¤é$™P†3vÓå"w«Þît3¸;fYX…Ý`yº¸w'@S\îNçlw'NÅUo÷z‰;ËÓ°:íNæKx~Ø+Þ}³:Ó–ôú]FiJ« ø¸^Ì’årJ«"P1¦U>l¤Ý[ ˇ Z·ž”§ÆšµXW{r[ÓViãi-©’Î‘ß MÙý›„V[I΋ü3¹Fä’”#£q©u² ÝVjôþ– „2úbõÛ`L£tŠÀWŧ噷^é’„.߬ô­Õ0à ÝßF3ØÉMÝTÒK—°™×ï–ípX£ÉüÃä4™2àV^‰J}¾ƒí“0šÌ’EÆÇÙ¯ÔÚ »gpu¸ |±ñÒ"‚A‹RŽá»z8Î.¯%¸“ð3¡»à}cph]Iöþd (gÁG㈯#ØÌ±•ˆ³–ºÅ »V·ÎÓFYüý'ƒtØZill˜<-eáðbÂÂ{cáÕh-kƒ8á2žÍX¦PÎ['8`kÅž áExZÄKäw†D$ 6Ÿ$mZ3­H„³o\2á:ËP|\l!Ó.SN[¦?°ÿÎXoEÌŠc]† —` díȵkk8O$•âñÄÂÃðáâ[˜£^ƽ‡Ÿ Á9…Ÿ'£ ðÿxẇ—`õ¡./_ÖAÝÜô±”ö ¿Ì¯(ÇsÒ«WX<Å`O©^þíp˜Í³d>[`„ÍIÊ—¿_ ~ü¼-Ÿ^endstream endobj 1293 0 obj<>/XObject<<>>>>/Annots 183 0 R>>endobj 1294 0 obj<>stream xVÛnã6}÷WLßÄV,ß I6m\ ŒLÙlEÑKQÙõ~}ϲ,;Ù4ÛPBq®gÎÌès+¦~cšôi0¦D·zQoêÇï÷­xGS ÇÑ4õ'“¨_2zjÅqŸÏƒž¸L¢qujÜöa·ƒ¸Ç²þäo½åx2¬tcH…ÓQ7ó[Mñ,†ÿpjÜ'ˆo§¬ëO|{»8OÄnèj1-RNu4G³Ù€kŸmI[8R¹rJdê›pÊää”–ô¼§Dd™Ê7t±ø»u5‡'o¦½«ZA®2·÷7í˯º‡‡u;޼ »1#†W"uÒžûm¸t[Ii™'ω­~°H¹`CÅUóyÆU€Ý|tû\9xšë¬ðFŽöÿy¥:+¢¢|¶¦t*—„ éVZjc÷«Liå–íðç‚Ãy×}ï˜æ/Aø„\çnÉÿ¼µ¦û›ÕÓ+·…ú&WŽ>è½éüòòƒî+ç××÷7OÒ¾¨D××àÑçÿ)¤ÿ:Ž#µºñ Œ¬1¥ ’ybJ+6rMn –qMËeO¶ÆÒŸSe GfÇuî|‘`ßÖ”›mCZä JàgPº¿¡ÜX vîIäž~´·WÅ ¸Q&À·ˆá¸š*ž@ç@’»¯Bï2îk"_ÓÃic˜”oØ%Á ˆ²AŨ駤Â|©]p31¯Ž­³l/™Ž|Q)-¹¿S±Ò‚%–œMhtpÝA6`­®,ègÒÁö²ý¸Xýzû©Ã7É?ÚJ±»ÜdæYd¯ü±™>ñ„ß·­Ýjýœu‚i¶ü¦IÖ?ä ¤Îú2—LZ‡¢ë ðÛ¿wœ³'îi²2_«ô÷ v¢Ü¸¨‚€EQD?}A‡j‘´Ö€uü|ÛJ{Vê.#{ ÃsùHÑ!ZË{G½y:‚Æ/"+%YéJ›ƒÞ©5ú)Ù¨â³:„褂)‚‰•"ôC&­¯Ý™*1vg0»)µÌmOhSâºVäL‘™„µb_`D3‡½´Ÿ,Èv—©D9ô”Ûbn¶ÁñË z¼9CÍŽ#¡ÑŸàm£C£e°B€µA;Ê%OSÅàC@(¾9=4‚´©H$àÈñ„A™#KL.R®8äí-àÀþ‹ØGhgz6n n¢¿:>y-¾*]ê#dgÐVŠäs©ì¡Ô„ÙI‹u NûÂIÍ/»dª*x’ëŠúÆ ïê1ã§ôÕ¼–á¶ú‘PË3¡j¼Í(,ÙË·ÛŸâË[pÀËPZ­r¿âO5YnMýº\ µ0\ qY´W­ù5+|º¸ô]Îp4—õ{ÛñtQüßå\ÇTÏÐ÷¾ôùñ…|@M~ÄUÓÓoßW®®æÓjÄ#|bN4Üá”KzïG:Ý„~þƒë£ÈK‘qq»1¾X‡PèNz³ï`ˆOÐÉx¾Ñú}V»[´~ký ¾bendstream endobj 1295 0 obj<>/XObject<<>>>>/Annots 206 0 R>>endobj 1296 0 obj<>stream xÕVK“Ú8¾ó+úH&à±ä'ÜÈT öTeÞBжïúÁÊ&Éüût·0x!$[Im†—[R¿¿þ¬{\ü ˆ$x!$EÏu\\9>þœõD œ?t|(@º¾¤Þö„Ž„ÀóðY€Ç=H]‰I7pÉK´+E„²/È>îÆÞQ:ízq`ýFz°[ö$Úô‚[£ø(utňu½($]–N»Òw9fOzxÊJ]×Å< Ô 1*ÉÒɯð)rŒÙ‹ð”•NºÂ£¡.ûµí¾˜Ÿ—×là~êƒ0_c‚8tF#æ)·À…yÒŸo5ìLµ1ª(´D• òºµªL ̾,³rÓžue@RÅ.× jØ)›ÿÝsa(¨…ó´ŸêF'NAƒ¶’ªL³&«JX=¢÷œ4Ôz_&´ÎêŽ$Ôø%‚§%iaÙcJ‚ZŸbÔF”ž@•Ð×¢PïW¦Ú7Y©a£–Ì¢_èºV=À°R½xö-ßÃN^öðUŸÀª,…Ùdù’ °è'[eàîè9+›½w?~£ûƒóñx6y«Í‡,Ñõx|Èþj$÷ÓØ®±™¶è1â‹î9< '£kÂQ~Ô¤˜»„ 3üo¶FkøHM ÑPê3cÎêÔ^áÀÔTÔ‰QM²0Þìs¬ašÕÉVØÓªÀ¾Æh:E ¼s<å0µ çhctá¸|¾òjCø?âJ„™MpÑow2D~1À°‹z`ó]–ªÐŠaˆ|(OCcsñ™l÷å?Ø,U‹W†à•tÛI”?5Ý/²Å<g+ÿk¶h†³FC´Ï›v?}JmÚ·*Õ›Æñ5+ã[/ú$ÛîÑ›í ½Qß½§À”áÚiÞñXœúۙ݋]µ‹d˜3}÷¾mîÍYÞÌäj6ABÈ«•Ê'4¾wv¡Ã¸³ÉY.dÊ%á1º ¾çµƒn¥¸úO pÎ'L'ø¡ÏÒoÇËÌ`ÌØàü~¾­Bí|Vy•ü¯‰ãiy:ôñÓʃ…An±•Á‘b¬(™–;cû‹™§-ÍËþáÁ»r[ê$ððËâ~·—µ/´±A„—b?¦/ª¥`Þ©á¯/¡¯T¹W9wYà½ÜG…a䎾qÕð#߉Âï¹hUz¤þrÞ{Óû ïc*Ûendstream endobj 1297 0 obj<>/XObject<>>>/Annots 215 0 R>>endobj 1298 0 obj<>stream xUM“Ú8½ó+ºrb66_s#“…â°‡$lí!¤¦„F‰-9’< ûë÷IÆ P©ÉÌTQeµ¤~ýÞëÖNLþcš )SVv"aåüóiÙ‰g1KiÙ”JN"6>}ô¹G#|§³¿%ÅÓ»š¯‹è4 íÙ4|…h¸9¥,B4IgþløòÑ”&CÜ&1Tì‹§36ñ@«QL4}ì¼_ߢ69 ØÓzó£é˜Íf ­w¡²ˆÖYWídÙ»[ëDÔ6D°Kú“ÊÎÙ/_{”¸¡·Ü~|T¼=òÑ’WÞÜëÛBgß¿|ÝÜùØ`‘žÁõ‡cƒìCFï¹;ÒŠ89QVw‚6]®Hü’ÖI•SÈ·¹£ŸÒÈík•9©UHɆ^»•{ ñ)QìÉ=çZhã ?ºüŠ:0Rè\f¼8硜?îêªÀ¢›nþÈ{”?n{ ²ÀDScô˜þé òA4ºx—óÇÏÙýJ@p+ÆŸ!\"x÷î•÷÷Ëùgažd&ìýý/À¡>iã³ô!ZËõ_§[»ýáÕP—sÀ,ô–soÑ·`îwf\Î7Ý+Ø¿Áxaòì›0ú$öÂЛóÙ¬¥¯VoÈŠàS ~vâJÁÆÒRë÷‚£•øÙ˜ uF:K¼(B¨2ºÆIaÉÖÙ¸E;Zgä¶ö=УwÜ+A\í|§ e±niotéo8%oé¼n«[hÿ¶=fD^èöë\¾û(7¼,Qv†6µ•ÈäþØ4;YùúSRZ ÈPÇgPŒVû‹7ÈÀˆÒît¡ÄPðÙ ¹5ÜÑüà£Ñ“â¶qGÜùéÞœEVæžcASŠT]nVïIü„]7É·( þ¶€½éú",˜•æß\ÅUæ%®jSi+ìæŽÑ9xæjL ^äÊJªýDC5Ûv´Dmü0ËlËw<š°tìGà™qi^Åy÷·Ã •†¥!:P‹'ÂÎN öåãÓdìú¡Ý,]LåÖ"aàÂyíÉjŠluh$€=1°}“=Oe´Þ´W°‚6 ‚Ó7NX‰s`yWgzÜhpyòTŠWå\ £yëŽ ÏÇk!<Ϲ| ßé,À†rZº‹iû LÀú4¡Ñ/Ãxêéoæ…aé ŸüÍôôØúñé@Íüî„è7­î÷¦“”Mp'^Z¼k©_úkÝùØùŒuUendstream endobj 1299 0 obj<>/XObject<>>>/Annots 220 0 R>>endobj 1300 0 obj<>stream x•VMsÛ6½ëWìQîH´(Ê’Ý›ëÖ®M'zŠ3„H$$À dÿû¾IYâd:­=#‘øzoß¾]èû$¦þcÚ,)YSVO¾ÓjuÝvÃIaA´ ÕzÝñôõóMB¿ú8Á0¶ž>þzš,ã 6Æw ,­)Ixc÷VѧIœ,ùý&ŽÌò7Ö†·0{ö^Ór³ˆÖg³¿lÇp¶ ëÇÅ1m÷àºZ-£M²¤m(-h›MŸ÷$4 kÅeBkã)•”Y)¼Ìg´7–䫨›JRÞJò†jYûF®4Ö‹B:Â!5Öf2æ3£][7^}µý:YÐ鄸~ÄÏ»xƒ{â6¡›Û$Z¬o¹—?…»|袳×þÝ‘5™æ›Å¯Fï}ï¼ÔÑœžyíj³Š68?€òéò†‡~ÛN>Nþl†endstream endobj 1301 0 obj<>/XObject<>>>/Annots 229 0 R>>endobj 1302 0 obj<>stream xÕVÉrÛ8½ë+úhO$š›$Ê7«|˜©Jœ┠! ÐhÅóõÓ jadWâ¤r‰,±6zy¯š¾Eâ_ó’ðf!>9\Þ-GQ!L³^H’Y0Û­jx?Š£yÁ4F²Îdõ+²ö¾i)Z£i„{û•·Öè…A<´ú¼i<Ljû¼ýŠ|ï!Z$Õ—G˜*ˆ’3 Œ‹Ûé^kx;ú;GL1™S´0SÂÅMQù£Pùn±H /<ôr~výêÕyþù”ï‹q¼ï>R9X^]^.¯Þ ó ¹°——ÜæÄòjµ¬´uöîŒ6¹ÇVŒîT!›1¥˜ø&Q‚ðóÂïB“ýøÉ[C8XÙ¾{C±·²p^11cØãJ±¦OíqÃòªS_>~º;ïñþ€«I¢žÓbJ‚†mC-Rå=>; ±Üz2NÒ÷>/XObject<>>>>>endobj 1304 0 obj<>stream x…VËnã6Ýû+.f6ÀVüŠÝeÚI‘E êYh‘¶XÈ¢†¤ìº_ßsIʲ5éAlÃ&ïãÜsÎÕ·Á„Æø›ÐrJ³å‡Á7šÎ§Yúz±Ì4ÎÆ4ÏñŠŸïŸ–ô‹¡¯ƒÏ›~ÁíË‹ÝÓýÓœ&ÚìôaµÈÖëmd83¦M~÷d, Ú—f+JÖŠ3´/h_ç)Weé†ä ERxAR;oõ¶ñÚT”‹Š¶ŠŽÚ5¢Ôÿ(IÂÑΔ¥9¹Ÿ>mþŒi4Yϲõê)ネ¼ Úš\9‡¤…)¥£×éxŒz[*|œ„,CʵÊÕ¦’ºÚ“7]…*%!·º fv„bŒÅyaÏ©¶„Y6åü¡¹!ÎɵkðG‰+¥÷ÅÖX®£6ºòŽtʹÌÂÓÉ4\\,B—g€Ó+¡­ÞPÛaá2ÚÚEˆªM À)² rH_*^–ŒFµoñ÷!IårL'›J"("÷’昈ʽ>*2µ²‚gçÈñWÈñt‡»ÒäÍAU>Éèwã~Dg|¢Ã+Q¡©K£FÄ2t,ì%OáypiÜí…¶‚k<EBŠÙ£îâ¸pS †áÚ ~ýüÝ4À( ´˜=}ü)xS…±‘U ¹:2ªŒ=J‘š»ƒFx<0C"g*pR½ä·°%¤ Ä…Oá23Æz CDM§ÓMàý‘F¶È&gºZÓ$›Å=š®` àù o?[…!rá¬hG#z~ŽW:7MÙjÎ7ñØÑ_$×][uÔ¦éˆòz÷á¨^?{°þgtâtàhÎð—œ?ñ;q®‡RÛ#á #éãìŸU.°D(]q‚ ‹3†%Sjæë5—\ò©^ÖÜT;½o¬’°¨ÁsäJ 8¯ìN`´ð2û`$oc°º­R0ÁЧL^.“kjV„£ ÙwDÓ'5FÿPŒb û!ŒU~À( ‰™Þ^^^"Q·£ šß¹rSó“ ®ØÜë«õ®±}O´Âœë!iÏŽ‹Êt®ë°ŸÚýÐKoE}û Òp4­7Ü…O@>Žê¤ÁfõwÍ A ì×ÍžÛ»kÀ°áqþ‡²®bg¨¨R¤‹øÅdè42¼Âx'ð0Ã\'ÂN8¨ƒÁó÷œŽÊà —Ãqã÷O«ô5Y.³ùj†G­i¶˜ñ£ÖݯñÙê1:ñŸ¼h™øæ¨½0ZŽ×ÿçÞóå<[.V0{„.ùþ—Íàëà_viYendstream endobj 1305 0 obj<>/XObject<<>>>>/Annots 250 0 R>>endobj 1306 0 obj<>stream x½VMoã6½ûWÌÑ»ˆµú²d÷–.š ‡~® äÀ %ZV#‘YRjšýõ}C*’ìèö°I€D93oæ½êó"¢¿å1%í" B¼ÿüv»ˆÖa°¡uš)µÇù¸jèÓb› ­“$ˆ±m"ü÷+ÞŒp8¡t!ïæ[ìú•Û SÞMÙ=v³$X«É6I7>lã”_M¶I²öq·y‘_M¶qÆñCö«É6^gª¶~5Ù2š ¶q˜¹Ã–ÑdË%ábDÛ>üŠw¿ß]ÖÏTôá&¥(¢Ý…^o²`»MhWº‡´+–»“$aŒx¡B(Rú™’z+KªÉ¿DÑ5/Ôá­¤gœÖX*ŒNö¶V•;ÔQÖ]­•h¨{`ÿn÷Ç"¤UÄLíÊe!šÆÄÁÝ#µ!}èD­Ø• %Ÿ©jô~<º“PeÃP¥sĬ“é³X\žÑ˜ç*fµ ŒZ•t£ P)ou¦®³ê, w²’†Ž½*8‡Y>úÃòáÝWDMò#M?ÿÑéözÿÑÕtÆ”~1úIš®––ôÑÕ|^&pÁ •ÝŒP̃/w@?éNÂZtÎ…V ÙÈÏ}m@'3Bµ¥N;Ôkƒ›RtÂs§! Ãr1°ÑOžúà+Š”l¡ðÿM‘íF÷]­$“Ã91˜‡eµW¤Êº½"ü±WÔ½<ÉoHÖŸº.™­O#¦Ï¡à„ŸÞ{8ü8ƒ4ÑéÛ¯ê[éí]ÝÑsRÉ‹Á'øz~¥²¬ÒA®M|ú|2ðƒm†sA¥êÛSWhõÚtÜä£×Q]ƒ[tŸK` ²û¥<‚?1j–„U+ŠRhóÓEl´z+ŽÃëFbüì켂 =Væ¿IÙBÚ£œò¾{C±¹bì&á ¹Ù‹o7æb›\Ž’+NÂÐûÙ–Ç2W1Õ #ÜÖ•BùˆM1ãA™íŒ½3ép¢˜ŽÍ¹ ß²¿‹S¯‡jûç·)ôw¬±kæ9€yuq‹1̡㆞bKjq£µ}K¶þrÖ^Náö¤û¦äyñ© w™¢Þ§U…ÛîÉèBZ«M@wnØ_tÒ™s¦Sö«ÈݧVƒ¾a:LÙx’êŸØj¢<Ò ¾u×y…¾9oýçÑ5¼YúÝ¢‡ª ³µz5Xåá–Oó¥û¶pMîmVtwLJÓ< òlƒ/DŒ7üê‡Ýâ×ÅßÚNL{endstream endobj 1307 0 obj<>/XObject<<>>>>/Annots 263 0 R>>endobj 1308 0 obj<>stream x½VMsã6 ½ûWàèíÆZëÖ|L»M¦ïL7î-3Z¢d¶”¨’t²þ÷H}ØJ¼=´ÉL&<|xäß³–øBA¼†¼ž-ƒ%~þ|}œ…QÄ¬Ó ‚Âl,»•„§Y¸Lhw•àWÜMãaåvoœe~w!øÕè§I‘ïš"û•Ûã`Ñjí}³8Xw+Ú]­(J"ï¼_ÑæÏ»iº‚O „!ìJ,x•­ƒÍ&†]áj]Â.ŸïŽ*¶7Üî…Ö¼ÚÂX ætÐêdEÃ!g 8œ /À*0-ÏEy‹ŽÎVÐN5 J(˜eÀjÕTÐj•sc”6ÆAªü¯»?gKX„16uWÌ™Öì ¹j,q7¨´(P‰v˜Ž3©¤:0 Þ™5˜†¯YÛ}Â1X ¥E¥ÐÆNðESL’L8Ë>G`’òw:ßêÇZ~»/Lž8ˆÆ!¸ »$„í}õ[¶xçmz÷±¦ù˜2n_'ézÜa¹L‡ê€}CÜ.›ëFè'Q É´<ßðŽ+¾[Ñ—‹Šnå^jUÃöcHLÙ~ü‚íàè1Ý‹Êè£ëø»Õu¹¹òèÀä›="C«£kÕg¤b̓A ¹w££²ˆ78xÈÑfQÀƒÒV³Æ…4©ÅÕ€Íárlº¡ªŽÊXó<¯öì^EaÏ| ïÂ÷#ò Œ?ÿŽÿ¢pfï÷O8Í=°h,8púç§Ë `­ƒ„ŠÜ‘89“òͰónæ¨ È9Z¸7‡d<†ÿ³Ém…Th»&·{¿úÛÜo}ÕæëÞë33FTÈU6 %*Aƒ3²×ˉSF½1ÅCóq¼2OÝp¶ß(žó8Õäpdïè)ñ@sfÑÀj>/XObject<<>>>>/Annots 270 0 R>>endobj 1310 0 obj<>stream x“ÍŽ›0…÷<ÅYf4ÂÁ„Ä]:m¢.ºhËìFŠ<ÁC<5'£¼}¯ I£J•È?Çß¹‡Ëïˆ#¡›C¤˜-°k¢„%4s}üØD|ÎYŽy¾` ¤\\G?#ÎS–b.RšmÀE~ݬΠ¶ðZ‘Ð{F~õSI¸Ôã2òà™¶ÆtÍÁ9Ê7ðb$Å e\%(w“ug•íCùþ¯Ý §³‚~ôÇWÛnj¹­Tïlw~™Ô[ùò0ÈÿÄ|Få”ÕäéÂuêt…ÍjûùÂÒ­Ã]¼[ÜããÀ·\nL÷*ÍÊZy^.¯…UN×ÙI2ÓË|Yn/œü¥zÈÚÑËÖÇF‘e‰½l+£`ÕÁªž¦t[ÓìI*°4ÚM8†¯š¤x“N(k; ×a'¹‰Úí!Ca“)‡ŽròÒɺ8`”\\¯èX¹súDõƇ7¯0–®*èæ`ôN;sÆÇ^µp{…c¯lðÕû3§ëk«Pw8eÝJ§†¥Kd6ŒóKW Á²|F?BFm+|ŒCöá÷xöŒo²=Jã•1±H ¿{ÆÆî‘ú ƒLdL,rúý®Ä‹¿”Ñ÷èŽlûÝendstream endobj 1311 0 obj<>/XObject<<>>>>/Annots 279 0 R>>endobj 1312 0 obj<>stream xµVMsÛ6½ëWì­J#Ñ"E}¹''­}jÒ¦ºÅDB’P@Ò÷×÷-ŠKϤ‡Ú36IûÞ.vßî÷QH3ü†´Šh¾¤$Í‚¾\þ˜-Á‚âxDâï«+2’ö#·­XÁ·p¬ÝB¸Y1Å+#=cþŒÅ‰æó`Óâ¼n.ž‡Ln¨{¦gîuve¾Yyð9âÐuhh¥sdØ!Ë­ÇÀ‘2çZéžé™{ƒ[Y‡¸˜¡š¬tŽ :ä¸õxræ¼C+Ý3=smþÙ§O£pµFþEór-§(Š‘_î-£¿šÕ(ÄÅa5\ÁßȾÙÕùYÍÖÈÑœÂuÌ«ö­] ×Q0糫öº7^}·ÝÜo(ÜÐvBY€Æf½ mŠxÎP)Ûdô±ÓR¥2¥÷:ÏëB%¢Rº 'iìSùfû†b Cgh­q>‚¡ñC¦w"£;cÄKI'£Ÿa‰tcrB….e2©Ô³¤ä A_¨:ŠŠD–é3UšD’Ȳ¤TT‚TA Â,f4 ¹–,äYUG]W°|±Fü %󱆴ieé Ž:Kí#÷ÒÈ"û Ðö(ËËIlO5|¨zàß =3gDZ’yÉLª ésA¹Ìµy!­ÙIU¼O€™ú„cxܽ Bs]uðú]©Ÿ¥™ô0K––óð*iöê$]„€†kÀZªà6òâßC¦\žX3x˘Y¥Tã|%ù,‹øQàö`Ð:ÞPiI¢H‰ Û”A¼ßç#¢Ú¸h# Nœmeà¢%ª÷‰¸·yC—Ëô÷5œg‰(]*ë<Fýˆ".°F"×âƒ6J–· µXt‰i³¹Í O.ô;ÐÿvV¸ô³Q•¼1R¤|àæ½Åf|GÁ:† 1σx:ÕÕ„ðÿ m^´¥1mvN£(X.¹ÔÆ…D¥s•ø«²¿¡%ª×! ,sy½÷sà%üCætrŸšýcúW”±‹së¬é¤Ï¨‚:ë¦Ëõe«£ÉÒ+Í™4[rN|_{öÚm"s¸›¼ltæLjŒôžÓ™«»7*DUi‹é:QüTQ!‘„]-ÚÛ:ðtDz||ƒ/Ðè§·l•‡}ïh\)sQT*)=:Óh5Ó)¼ø^+_l j¸LË“LÔþ¥’cãg+5d­…°`¸rÒÆV6¼W*c…Ò«_I¥ÊOÔÎê6Ž ¨ážTE¸‰ë òˆ;K½:4!nJy€˜P©Îà%fN±œªÙº–W¨ä¶¢‹‰÷SÙNåûïÒO‚oîæ’7hŸÓ·˜þjý"Þ£ÜytÄ»ÁÀ=ü7©áe®´Ön£c‚í ‘*$N#LJ'Hm¦'tTÚÕ{¼¤o¦TI:~—FýO,¯H*f©˜æW~ú:@¸¤6ª=Âï;6óx?ZtùyÖ*¥wOÈ‚Ç1*—lÔø!ÓŸ¿€žŽŠŸìÖŸmñ‘Q =|¡M­féç/M|_§{}æýÛ·ÿ¯¥ðpw{ë’{noO õ×°µ.^·]Ðdè]–YæP³r£ß¢žPÈ4mÁjkÓQõ7µÊÑ4p;ᔲ014c²du'Ž_®¡;–y5oI8û\êl¥¬ Ï‹ª`Iv3„ÓY;Sd~ ¹6?FôX;1ꪅçëÆ9ê²ÎwÐGí¯Û–¢s-bÌÉé˜~x$ŽWq°Zú±`2§ß¶£?GÿÏ mendstream endobj 1313 0 obj<>/XObject<<>>>>/Annots 288 0 R>>endobj 1314 0 obj<>stream xµWKoã6¾ûWÌ­J×VDYÛEévì¡EÛuOëE@[´Å@½¤'(òß;CJ²¬x·´  ˆš×7Ãá7Ì烤!LØ”£ÀðK÷øínÄÒ™B”¤ø,! #?jV|h¥qä$e©?ƒÈ®¬t:󈢿–ÀfIíª'eämÓ€t튤¦ÁÌZF!w 'JdûÐ;` ó õi€ÊlŠ-`;bs†þQLZ'‘³Jc |ÙʉV®:³Â)zb ŸJûFÊSþ+Ïaø¿ç9œS"•?ÑÜÖñ¬>Õ%ÑËa? þë[Ä`¹ÅƉg‰?ŸOa™Ùž `¹ñ2YŠÊHUàê\À®Pk^ך?¹:ŠG¡Ç k÷ˆÅ3þù|ZdP+ØkaDU[ÛjÒùCRAm­¤P^\-FLì–.3¯¥Òϰ>l·¡Îy ëgŒdD±…Rîr\ P•è»õ/a‚µF/KĬöBó³ Œµæ•ÙsmA)ý`(À1—›Jâî+dJ˜ê›rþ((£Òˆ‰¯Õ¡FMIEÑb‡Þø3±•1“äúϦ-µW(÷¡­½¼Ê†J¹*!xªS-2Þ×°áå/+ ¡©Ö JCÉ+Ú‰RÕ7Am„1ÂŒALˆ{ØnB«‹špo+¬ôf±ò°f5ínõZªÚa™Ô«6•˪ÜÂ/Ú«+»W×·ØæM NÚ]»õ…ì]©±ßLOÕÖ©¶n<؆֮`j/…Œ×¶Z•ÀϺŒØØ&Á¬(’+‡mtk.­—# KWÈ®ö ! ÙØ«Ë¡šüè B"—wŸLÑÀ&‰Ç+ón•¦vìùm ÛúíÀÖ{ ª¿ß‰zåíîùQŽ!—c:&¸ÈVW=gA:|Ý) ÿ'”g %¡”óÞ.éú¶­êðÛ^‚_ªt?JfðóÝÍ=vÔÊ“HD¶jôR¨Ÿ¾å’Þ¬ê·¶Žø‘¢L܆¶„Td?µõý2Üs›·oÞü¼ÂÝÍbqgYö†Hv±°›ýuÄÐZƒqŸè»ý KYpǘìOñÒ ùé_’˜Ù‹Ü>÷ÈÓžG“«C‘ÁF•8æél6´Ö7ãK4Ydû‰K]:é¯)ÎÔZfÄ™[¥K7úȵa¦Øƒ>,Ï ‚ 7æPº¡ç¦ªŽùT‡r-4Qg7þ•œfÝ…áêÃï8–®ÍŸJ8Ï_›?ðOœÞ,Æôöù³‘8¥q¾·Sè¼kP½¥ïwO¼Übq^¶ž²¯w,ç(ãÜÆë o;¹ñ. eµë±8qõÊclÁâ1[Ä««ŽÈ±âÂþíX O,8ã} ØOµBþç@’æîÎÆN8 {X`È÷4•ªm79z8¤^ʯÎË÷06f/c—ðiz½ü¨ã¢w| ^Zƒ/ˆxs C¼Æ L\f‹I>™âå=MqKb?@! gAc˜à]Ev±S/hvgÖ̶ þç0Äq⇠Ý=GR`YÊØn…Ÿxup¹ KS?šMa’sÒŽð¶þË¡¾nF{”F~šÌ\ôiHáß-G¿Žþ îs˜endstream endobj 1315 0 obj<>/XObject<<>>>>/Annots 305 0 R>>endobj 1316 0 obj<>stream xÍWMoã6½ûWÌ­Ê®íX²ü ‡tÛ{hºh‰¶H¢*J üïû8´¬&AÓ¢À&€!Š3ÙǙ7£?G>ÍðïÓ* ù’¢l4›Îðæòóå~ä¯ÖӀ¹ýÍ(Âix^¥ôµÙ Ó™ÝõWÓ5…¼â]h-i¾±¿ùë»nÕÙ]lœîj…æ¼j-þ:°¼Xá\·êìÎfl9XÌ ðªµì¯­Nã•[uvW¡‹h³Âù>¯ìîÛ!å‘®ï6Oڀ׬6sÚÆ ÕŒ¶‘gÝFQÕ©¨$‰<¦RŠx‚‡‰Ê£Rf2¯®¶£ë»|ßšËé:„ïsEÊ>T2§ÚÈC’ÊIPQW¤ YŠJéœ*M‘Îö*—T%’bQ Êô“ŒíŽ}S‰ò(+*JIcèYUI+Y%¢‚SFÅÒXOf4q1Å6x÷¬8&˜ÈÒêæP)R©üØ5%K9uá,ÏáxøûzŽ¡°EçÈ EìA—™{½¸Ø•ÎÔß„«Úu(¤UéÄH€Ÿ¨¢„vž ##ÆP8Œcª÷"%Q–â´»²`[üºoùEN2å»{I8ISXÃO2=1¶‰>/XObject<<>>>>/Annots 314 0 R>>endobj 1318 0 obj<>stream xµUM“â6½ó+úÏÂß6læ0™ TIU²ä„©)-ï€LdÃîT*ÿ=Ý’mlâMUUØRëu¿×ê–ÿ9`ãÏÈ/„ô8²™+íßo«‘ÅÌߣÿ#¸Íâzv€OÕ ˜MVß§½z¦­ˆ ÁwÄ ŸhÕ³«Õ‹æ“u¡3#+’7 D42ý°¾å©ö0[†à8°Þ2 ˆC6Ÿ{°Î´Ö©<±ÊóK©òSU&w0¾‡\¦wëÏ£ÙÒ¯ÁSžº!’Zg—(Q•,¡zP¨|ŸK~€ ?œ$Ö‹ØJhÛù”ñJ ßbg|6„ÚÀ)ENî˜1ÏÁñP2¶aêR¦0 Ï<ø”òªj¶âRõ}ÑÖÅš\½¯/ƒÉ¬±`ßñÔ(µ R\–;¡€Cyi¾ËE¥¨Pˆƒ8 Y¡ê‚ÂNGØŠÔΕâï%ƒõ«xÇ fEŠi™g"[`"xyIª˜Y¨ã²B~W„R™ÅI(^å…¤€´tRE*Ê2±Ï—׊/R[”@žB¦ˆm™åÆÔ%¥3kRÔ͜ΨDÓîÂIZOÓcæékÃß´AX7„®1§.gŽ%ÝV‰œþ¨3—öúØQE€.#¬½ÌZŠÒm¶õzLûEˆ®] +Tç*—äž?—¦kÿÌ'p™½ÔR& “»ŽÃA¢†c{îÿÄtˆh>ÏŽ·¹™-›dÞp|ìhj2u›$hÇ¥È3øeõð\wCb岓,2}À”Ñʇ6o›­^¡(SsŽM™¶ùü6×>àq<þd5ÕÕÃb±Ò}ô@m´X´¬Íƒt¨’¤šîõòêTþO`wáÝt,ñ–ÂVyYTºÛLÑ\ïËŠ²Š˜Äz»·?ÂÛ÷÷ ñ1c7þ©…^[º9¾¡Äj-›·íÆÞnû Îí‚»Ý2Æà.¸ÿã€×¿êµæ~úʧƒXÐò ÖúäõEü£Áû«c÷ÚÜ8jZîFþ“¹øð£ˆã‚zi@ïW™6ÉÄew€<í=¸CpoÞDwÉÇecöÑûîÔчà´spôàÚ«ÿ/Ñ=òqÙ¸Ä2¦÷+Ü­£ÇpÚ98zp=€7Ú}òqÙx=¢÷+ÜëjŸ-ãæ6Ž"æÇ‘Ç|?¦ûÖ´èº,á÷¿r?syæ >/XObject<<>>>>/Annots 323 0 R>>endobj 1320 0 obj<>stream xµVKsâF¾ó+úˆ×hÐ<ôòÆâ,TIÕfÉ (—l$–5H‰$p¥RùïÛÓ£7R*—à*,õcæë¯{¾áÏ ÿ8x¤ ¯ç‰Í\´Ô_Ù¸m3 ’;Ìá»Ìî¹øœEO8³1½þªÃ†H.C¯ËxŒï†]Ž`jÄåùc.a«Q—´éà^ÂÑE »|ÁÜa—´ýQ—T£.×Fª÷2¼* ¸!ªáŸž4«†º¡à†}zjZ 7- f*ácOúÝÑ]´™_¶×)é nvn6åâvårLO‡‚iÑ. ÓÊ¡à²)C®\—è¡ì×nfÜ X0ârå€k„19Ü€ª“Êc~Ó[ÆMæ`p³h¯RÉõ‘íÁ[Ìtähp³|ƒÙˆAweÛ¬LC„_¿­&»¬'PáHœA”ó¨ßNð¥ò:6‹^éáôF^ì)æ¢ÕCo¹½µ¼BáAÆ\Ü[JoÚûãe¬ÂAOˆ|¾D)â°ŽQï”± °ÞãÀÛ¹~¿}òËK¾È²ð¯ÜmøÁ»[›Ø`q‰0×{JÿtŠÎQR€£—ºn”Žú¹IW»­Íî@ºŽütÒiw©ÓçKUWda›•Ʊˆ‹(ƒâkùkXèçô( ‹cšÌȯDiïéå´‡—::ÚÃ1)RŠ<œÒ—ð¡®Âò¯é{‚níìÁÓÓ)}?&ˆ‡K1íçuÖ ÀR8B’X²µ]O¤’Ø„ËûÑ7P©íÕpú"·BÍjü~J  H¢ÍP±«Ñu4@B¬û\[rÇB˜9ÞEÜÓ÷)f$«2µ’©Ž‚&¸2¡dÁƒÂq_Ê%°–ÀëF*ÑÞ˜ ïlLÛt,Tzµ/-Gà† Ô\WF¬p”áx Y¬ÊÔªƒøš/Ýr-ÎñºÅ_–@53½^…8'™æµÔ)sš­,ºFY5à ilB«%§Õ÷V`ðs‡¨0ë˜á¬Ç9ÎÒ3„Й\šçNék5Ì4¥BЯTR^–ÔäúH%ÖOöL_ªü½ƒ 8Qe—iVdabÂÆ•UÏr–^ŠcArŸDÒvzxgp‘RPM3H¶w­õqˆµ‰ÿ èÎã ¾µ ö™™/+*{ŸZ%U>/XObject<>>>>>endobj 1322 0 obj<>stream x}TÑnÚ@|ç+æ‘Jp`lc'o©šDy¨Ô´®T©ªªÃ·ÀµgŸswáï»g †¹²}«¤ÓÄ~׺›C„2F8¸†£59jyvG;ŽÅúç .(%Ÿîœì:žHßq›.‚.˜í…ðŠï´1½çX¸#.â~ßúšÀXûw\Û˜IlŒ^dÓºŽl³»åÙ^ ku{x·x¯ó½ñ¾ç C"ì,ÎF÷. l~Kìtؾ!<ãrñÚIQŠ<-ã.W6ކ¼gÖN†z‹Ÿ‹ël2M®Ó_“¸Î¨yäAyrLhÚŸ´¼°áв§:NðäÀÃoínØòwûzÄ¿E´Ö›ÞÑ! òPR"+SdËDäå2z>ÿx¾{–þ,Û^šX9}-˜ó«ˆþÏçÅ›„o¾Ô8]ÆòÛjô8úrsendstream endobj 1323 0 obj<>/XObject<>>>>>endobj 1324 0 obj<>stream x¥UKÚ0¾çWÌ1ˆ$àÄ›ê¥U—Õ*uÛô 86Ĭ4˿߱“E¬Ùª]5(#y2óÍËóñè˜â@BÃî౦։¶Ò$!~,Ó”k~Ad+dâžéÆ”8YÎ& IÐY7 â8ÒÄzW‰-«à£.GÁ…Ôò…ÕØH€ÿâà'Ó¹¶¦Hêgò»¯.3¶ã†óiBƒ$žáÿÚE‰v¿MçbÎÀÛendstream endobj 1325 0 obj<>/XObject<>>>/Annots 330 0 R>>endobj 1326 0 obj<>stream x­UMsÓ0½çWì1@âZþLr …f: áD™Žb˱À¶Œ$CûïÙ•íÔÍÀÐÉ$#yW»oWï­Ìøøe&Õ³Ʊ·î' WIìÅ…‰ÇÈ~qúðJÁ‡™ïùxöô÷q7c1óV¬t­!ˆS/v|š1xé c¢•¥äëvk`2´®SÏÇ´#+¦–†^ˆ¶ˆÂô2½ÜŸ#ÑG¸¸Š€1ØXGİŠ0…}îÐú°Ïæ—Ö¢±ðSh#Uª€ÝL×¶J[¶ÔB@+´T¹Ì@áŠ[ô3ìKq\‹gûo3ìØ2H°È}>?9…]ñ⊭ K,•n7A’Bo:ã’…XÕ4NÛÙð&ÿ±x–yOŠ“Œˆƒ•ÇÖÕ½[±ö1ú9dª•Â@Î-‡B«8+uà¶Ió{0"£ÞUh©T6ð«”Y Ò¯je,¶]€áµn@‹cWqÝ'aÍ»{–ß5Û‡SÅ]&Ú>–lðöÞ.…n¹Åtoà @uÖÈ\8ÃAuMÎ5U®‚£W.kÑ5\ß|/ ªGHBâ»#ZÅÖÈã ®?š7„ñAnåaÇÊ9 ÙAÈF@sä·#n±Â›ùñ–/°W (å]›üæYõïP–“6\>¸ÏO%sx·ÛÞŽ—ÜC(ƒ•úòu´*%­Ü™çŒÒ,ûŒvò/_ÿ øñ¹Ë/žÚ¥ßm7›#Ú–x¶ÙŒ-DŽÞÌÿ†úx]®¸ê„z:@N—ûIÖ9IL~:=µŸn¾Rá!þœD~îTÊs'ÓŠL÷¨¡{Ö:Á™RuUŽDÕ8ñIr}ª@7FÎ3TÃ`§Ð„:}*Rƒ¤Õ¤ÙJ×n Žzšhù<:2㬀aþŒJÆtãä|}Çë¶›Ç&xÞ AªïÀ{eRã¼ýIĆHqŒ “ƃkKS%†;üÙ_êAÍ8{¦ñ`kL‡­³åØÕDH&ù±˜Bh7§ˆîÂ~® 3ž¥ø¶[…ã;ÓOBå=9Á±ÓÀgƒ1Þò¦ãÅ_Ž–©¿&ïß#£ằB‡ùFiä¥Éªƒ„+zôz?û0û sz¤endstream endobj 1327 0 obj<>/XObject<>>>/Annots 337 0 R>>endobj 1328 0 obj<>stream xUKoÛ0 ¾ûWð讎bùgØ!ë– ‡ íšÚ"pl9ñàX©-úïGJvÒ¸-ZPh‰¤>’郃?¡nÉÎxî†Ì×Ûî„E`3íÀñCæuoÜœ;ÌÇqXH§<$]õv<åQÀ\:B¼T¿Ñé÷åðºj㹜Ã2C¬žë±‰ïÁ2UlX&欨%Ôu»ÐlãælùÏÏÑkg2â.âY¦&tO!oí{ø#nÁ6ײc‘™{¯•9){²’”ã2}Ï"Õ.= äˆ}ä˜(2ËQ!V{Qå2Í؈ÆR;…LâÖm–¡Êi(&îžú4áI¶E kÑaéoèCíÿyD’ãѪp½)ÑãØjõiõ;¿Ã¬Ð=< ÕáJþH7TºŽZuÁ±H=ò«>#WmWÉ>&äµJQ%EU ˜½¸ÉeYƒìÓ;4áàg!†~\6È}.jHã&†¬’»É«*~†Fª­M!×Xµ7° ÝnÁÓ6O¶ˆN3ÌŸÍ<䵌¢/ïÂä¶ÂsY5U\j£“SF=‘¶u»®dÛ䥀r¯zÒ¬ömsgnV±…$*[Ä|IïÎ>‚2zA€‹O@ ¢<Ê<…ߋ٪Ϯ†— ($`wÝ[@¶ JÊæ £kF:=Ÿ”Az{ÿ1àS»‹óóO‚V×/fÓéBrFµNû"ÉîÌ÷P¿WÅ¡‚+¨_ާCqoò]^ÄØä*±iQ$oãù‘ X<½uà·5ì÷B4hê •¶ùP©Þ‹$Ïž‘–¢¢‘(4Ëë­š‰ø_QûëÐm¡ÆÛ16´[jrY7UžöcçXÎLV;Õ“Ø’JOÏ3ÝRèØÈ ¦·&}ƒ„ø)™¸àó½€f¥.¨ºÕð·Æ©ø+.Û¸ Ëï F¡‘¶‡¶ç&\–8f³8ª1½Ðc!~Óð“‘šnDæ?—ƵñFmÌDendstream endobj 1329 0 obj<>/XObject<<>>>>>>endobj 1330 0 obj<>stream xUMoÚ@½ó+æH$06˜äP)‘’´R+¥­{"9lìµÙdñR¯šß7ëÀ‰Ò‚°µö›·óÞÌ¿ùø´œÒlAñvà{>žt—·ü„æS¾n)œzÓf¡éçà*ê㋌&7 ŠRÏW ïü|FQâ8}Šâá,”ITLwU9¹•åYô4˜Ü„MÌî‡B[C*u•H*7²F´¬CºŒãj[iQ6oºØí7*ÞÐ^iM’eãÊZ™ƒ p‚NY²2.•ÉïÏ€xQõ&´e¼á­|3茒¡ÊKô‹,„&»ZƒÅ¥õèÆà3Vö_á©(I’LUÚvƒL›GЈB¼ŽH$O•-Y²‚Y™ÞW*L ô(âgBFe •©ÜñУ˔ºy:L!3ØTôýkŒŸ|àc iRÀJ'v„M{¹Åf»Ó9¹\v0ˆµÞé^°rºðB¶òúàˆ‹ЖìÈë¯Æ<lcBYƒ‰ÓwÝ/™,=º’±¨à¸* …4§?ø±µZÀZç-3¼­’ÎÚNN¢¶2·È´'µªÃahz·ËœšOKþ­º ùCæ-48úüÈ™éÚŒUªÐ©§i µ3þ7$‘¿mTÕº zOìv¸å¥~å6$“Ö=!ò Ý“Öåí45uù‡ü-[çäYÿôh<þD´vKK¬i_ˆ]Ý«ür~äTëØ{÷5S½ ™çÆé¶Z|'€Øiâ½]Vn9笺øöd·ñÝ×´hèÞQÅ©®þ‡âõ³Ìc‰s³‘¨D;RSof…h. æf}ަxå*ž¶ÃÑmâî€rI£GèÆ¯¾Þ¯:øAÈÑa?ºŽßö(ìendstream endobj 1331 0 obj<>/XObject<>>>/Annots 344 0 R>>endobj 1332 0 obj<>stream x¥UßoâF~ç¯øÞê\ÀØØŽé¸ëtmï}:ªh±×Ø­íåví$ü÷ÙBP£‹TËÌìÌ7ó}ÃAˆ€Þ!Ò ¢dÍà¢8ñgîç8ðc¤Ÿ NRßÚÇÓ¿*|~@±§Ç×ÛA˜„þ”¨ ‰§ßa8ñ)KÑ“¬aʾötf #JCÖYêˆì‰­”á,¦ˆ1_ãlz·º¬Do1^ÆC¬ ÂG‘O&Xå¶Ú«Ìû£@¦zm$ºR"—¦«ZÑUª…Ès-‘ª€Y #šº–;ÑÑ©jmH­2QcÓYDmZ)st  2e}-:™ûW«¿FnÉ*÷¾UMU Í~ZnÉG³Ãxys¨ÖÃ"ËúÆ;ˇ7¼ôü,u¥ò*{9„06›ª%,¥è ­š3Bk±ÇCÕ•ÈÙìÐЦîs×¼m­6Öù™qŸ†x(+n‡±Îþ„©Ó˜váR8£©°TºÓ¢uAÏøcƒè;50ýF«žæ"ÑnÅÝî€öNdÙÚÛÞ‰!j5DY y tȇ4‡])ÖW?«hD9b{ÿŠJ`_÷ªÊñûíâîØwWIÕv°Õð—Z}ÿkHMì¨.þfcÞp}œfäZqLmrr³%<•äÒ½ðt7¾èó|ﯯ_ Ö&¹]Ìç·vò &É|~œ1tí½„ö?pZü¶)õÿF{®ë§V$a§GGÑÊ@Ó7N¥¸w*'•úfCŠ%mçU#[CTfo+‰sžûøÓHm«ö—î¨í‹A=(­÷b)©Aj—†tÍ{$—Q7Çf/OÔ®•ûá¤lˆ6¿t*«K'Bµ+÷¦¢åRï-^Þ/£/úÖª4£BéÆŠòi«LýIò$AœãÄAÏX{޼LÜõ®ß:5á Ë‹k=¿åãhvµœ?/óLYŸd‡ÞP¡êˆÎ6ØN@º@n:µî^úŒ€zGø<Ò§{POá½s^®…,]w¸Óž‹8K}XË̃ñcäV—ÝÅãåô¸—Rú{šFH¢™Ÿ¤S^tŽî°|7–øM´½¨-þð0Jƒ{Ç´Ò»ÛŽZ/2i[§±ŸÞLéχýBÿ°|ü mp æendstream endobj 1333 0 obj<>/XObject<>>>>>endobj 1334 0 obj<>stream x…RËnƒ0¼ó{¤ áª6U•‰žªÊ Uýûîòh•TQŒ´¬ì™õxvO?Ò?„loœ€û’Ó¶±\æ‚bÆéØY‡<6°1ROýÚ8+œCZ"_ø‚E€41.¤™™è®®öô•êïÒOÃYáý3Åæ>ó`¼tóî~À=ØÜ‚ªžrÏ"š‹{ÿÁœÀÂyˆ¹:ä×ùTR°IÉŸxÛ I7ÓªÝdJÃv(Ë¢…sÍ&îžsM¨»ùº¥ÆòšåÏ) (Ä3òò)tFKP)7>< üR(¢à]èD£°-Åå•>V }õ®»””=Zƒ\ªè¬Â¹£&¼mÝäuI– ûA«~†,V›Ð‹Võus°ÆËwºÙ¢ÛªmÕ7|ÕŸ)Ñ\•KÉDäãPâxŠºô<‘"uðÖ¡˜u”&=öB°¥Z`k…­(½TY1vMHÁ$Ž8N+âFƒžRccü¼$endstream endobj 1335 0 obj<>/XObject<<>>>>>>endobj 1336 0 obj<>stream xì—¿OGlj(.¢ÈýÙ“\¸ãXñŒV*¢¤HE)(¨"Ç¢§ &eþÈû'¤LyH.®±=eªxc!±Utg¢Ùo÷å;oà0?N‰IŒåA{w»Ÿ}ïíÌ~÷»{qc q£}«ÑºÝXývâ^#nÍLÞ ‡§§&§q{ŸÓµ'å„ÅåæÒ_5—›Ÿ²Øˆç¦d|,cáëæ—x¶ÕüŸ·›‹Ÿ-!óò 1~qœßs+ó7KÒg®ê̹b.{[ˆØe¦³˜ TC€³)H˜_žHU0ÀŸgTŠù€ql¡"Ø ÀSüŒO{ óoøò0Œ¨4öÀ÷)@8žøyt° #<ðóð õ*Èk'3ßü8;Bª³G±90Ã2úAøéG]Rypf`q*VŠ5"ðåd׿v`ó¨³NûDÖÝ`#22s‹»o +íRñ çZ8/LpÀñVT§âIæ²P°|ªÃ§™ëŸ€}€PƒMæxJIªÔòc6yO"81®ZÅ# !™Å‘ªR‘ËAQ¼:¨ÒÎÀ©­:È‚ØeN"d¡†;,Ž™ãQó5ï×ÜÞŒ>¾ª‹o-Rýë€GÕøOÀÛ®öšŽj¸çÛDQ µ‹ªUË5Ô>K…‰ ö¬…íüé´C‡æÇ0À§Û³ÔQG‰®2[ቂQêÔÿ”º<ç]Ñ.wzTͧŽo$¡P Q^s|S@¥Њ N¯v\Ù18¸ùT4nÙ«àù{4¨g~ ýêâgpôóqìíú{»7ó"ü*ìzë»^zÙæTn»ä-ïí*õ.ºê ’”Ö¼ÚKÚ\ƒ·GÖƒ¦µ—ù¦÷ö͆ìD]2@_Ðs¼+û¨±âxAjðŽƒ·‡â¥xµ@H•¥Úȼ·¿RCL5:™÷vÈ:x{êÁóŽ€,p4yÄ%oô\f#Iunæ{3JÀ5»ƒ—¸Ü×Wûÿúƹºâ}ëÍ\Ôîàí]7ÐZ\ÔXÈÛœ:ʨx¨ÜÁB»Ö›9tµõÐÉ(—Íð³í·èôWx{×ñ|w-«#ð 1ý  º‹·2hÏuÚ&Nº._ osÒ¦º ð=:¤j €!Û‚åC&hDr¹}›Y6èÛ5^€QÁøyƒ!ØCl ÔOf>Ú¯nÙßÐo{'3nË<2îwÜóû¡oÿNäƒÖà%äÓ{µ£5xo‡b½ékû`ç™ÞòÀ÷íª{J÷Hûžá€•P³å@0ŽNfÅQx¢Ð-•Å}DLü¨îò--Úµ¶ä$€‚ª¤Zàó€ €9"Ш»‡HÕ¡Àøv —»†(Žy¤-*rüÿ1RÕ1smÜ €|€·Z'€7T‰Wø¨½¾·£g9?|'sáxÎ-Ë%–ä/ÿÿârõä äÿÿ…UÛŽÛ6}߯˜Gˆé›|Ù¼%E¶(Ð-ê / ´DÙÌJ¤Â˺þûž!%íFY /»5œ™sæœÑR,—ôòÇùVû½8P±Û‰5ž÷äÕwéÅz×|´Ý‰m>/v[ü\½ñæÓñnñpO« ÒkZÒæ~%vkis&Û)'ƒ¶Æ¿;~E‚‚V«|}¾Þ‰CÛ³ãE‘yû=ÎÎ*,º²,cÔã;’Q)§ŸUEµ³-ä‹j”«e‰£ɪâF$]¤©Åm,ižaT3éαU& ¤+üÔµVž¤!m|Ylò×£SߢòA0ܱð Z\ÖA£Y’“Š¥mÛht™X¡R6 jU\À`!:C¥5ÁÙ†‚Mue×5}¼ CP‚-™¸˧´“ºÚTœ„¡Q«À_ß!f3¬dœ4¾VŽ:gK噉ÔÝË(‘ÝÐI¡Í¶kTÀ0mà¸1Ý­Lsÿ5ãìñ\¥¬gAæ,ÿ1'>ÁTA%úÎ!QéNMÉZ+3–ï¦bš{L±zÕmßœ ¿Q%iÒäk#oY Ê^$÷*ô>:°˜“»J2¿ª’©€öòÔŒ WëžFú¢§§X3µ=Æ^Á㤨Sˆ¬ý”ÙÍÚ… Þh!U¿ ºtÙ cñT¬U­u0æ§=d#«ÕÀ‰—™ŒIåQ½¤LÆh †šø(û‚¾˜F?)Ö¦¤Áê¥ì»Nºàß'ᾩMîvŠÙX ¯:\ЬïÀç ˜ NÓ&9FÐð¶umræIå*†ç­Éu-:sý CO£‚6,\˜âæÊœçʦ(#”€ÁB“Ù›õ^Ÿ@E4F±'$hµÏÊ]À)A¯:ÃB˜=|‹^KTMËŽ«‘aÂO=‚0¤Rzˆì ý nXÆÞ\Ô ©‚îÙ|ìBŽ“T«´¸†ƒ¥†µwĺœ„prXþ9¹§×p’~c!ÕÚµ€ÓZ¬´ÞøìD]£#Ã¥ ‹‡}¿ßW¢Ø¬i¾-Äÿ±ä?ÿ+y[|`»>lF¿BÙ¤IJ’×”À'©ê¦‰‰‡!è/yK%ü€¿èÜØOÉ9yƒ ”Í\¤gòÁBHÚ;±ºGcfµäX ªMœ!@Ð/i↑ÆÀcÈ›»ƒñ0¡Ç˜ñÒcb&bÇf”‡ŒýN,·+ÚðñÜ2?ç¶?æ¶¿ðjøMš(¾8ç/vqØÐ|¿¼çèÿùªûBìw|’9vÃ)>ïþ¼û?\Ÿendstream endobj 1337 0 obj<>/XObject<<>>>>/Annots 363 0 R>>endobj 1338 0 obj<>stream xÝVÉnÛH½ë+ê("ÅÒѳÈÈ!F2£œ¢@h‘-©²©pqà¿ÏënR¤iˆè0c4‹µt-¯ªëÛÄ%?—büˆ’|âØ¾œåAä/c¢vLžãØK*9í'AÚáeV¯D‘íiM­Ó[ÖoÿÜOÜxß÷lŸròüØŽZ*£;®»Äñg®¦\'„Kg®¦ 7€M/^ÂKpçLõº^è·–]„i¨׋²²¬¹šp‘ ã³æjª?×ýÖ+6 ¥¹þº¾ò-'w¹À¹†RÜ?Ö“ù l—Ö{ríÀ÷(\ Í­S]‡ÖÉôTòªjP&SªxRàßéÈ*NI‘ŸššWTóüÄKVCjs3£úÈ%}ŸI*žx™±“’Í)V‹LQoÖ‡,×GEÖéÔÓle¡©è(R!”±šËäÙÖòv4 YÈT ,þ³éÅotŠ‘‘¶=ðš6S<·]€[V–ìys£T,xöÒ®Ò‘;Èo¦BŠZÀ«ŠRV3ªK&«=/©.HYdØœéÔ•ù‘‰<ç©RÏÚ³^ÕæõìØfº¹¡ùí(!d’*ª.Û'žê·29¿·¹¸×w&Ø+ÏͧŒ¿ ögiê|˜Rî_,ËBQ[àñD˜¯‚¡–¨ UT|}ä$ ií²"ùª°r÷á]E XMy)ž…}Yä t²æåž%øôL,Õct¸3@¼<49—¨á‘Õ$R¼Š½@¡™E dU3 +Å^ÛáFÉ¿5¼ª‡u¸ÅŒ›¯Ü®ç–ö¢Ç™´þ҇؞Âx€NU“ º`èÅtº*J…6#ö:SPѽŒV¨š]Y4µÈF.:pß²eÅ PšÑ®ÙƒHg$w&ü¶Lf_tØôĹg¯ë±îµÿ”Ç,I~–c–afÎTá¬@]R}9ù.nžp xU¸˜ðdxvïºáâ]É»–…³Ph~ToC\ÿ^²ukv—ÓU£éqþˆ¦ï7Fc:ä ÃhÔW­Ñåvê~²[ži‰ý ƒûÏ_Ùf7QϧB¤ôp·}Ø}PcwéA¨^²âó`oG¡Þ´ð­æêŒùjÑ].!¶Û…O!vÃ+7¼¸ÏŠËèNí9}ª°³¼g²a™n7Ží Vì,•t€Uöax»zÉÃþV)ù ì8Z`£W²úô÷zòqòdN$endstream endobj 1339 0 obj<>/XObject<<>>>>/Annots 378 0 R>>endobj 1340 0 obj<>stream xåUY›0~çWÌã^qllÀä-]5<5êAÕ‡Ý*2GZ/lIR©ÿ~ÇvËöR¢j«ª `Æ3¶çø>üÅc@ñfùÀCÈïm KïEêgX é ÈÄ1‡´°5¢ægU½]Ü|¼:O?yFŒcÀiq¿¾VjQgëB/¶€‚ª ]ÞžÛ5ˆoZ p[Ó o™N&‰n2¥§m«¾M&uöz·‡ŸþêÌúOðfqè`z<Ñ4<­Côÿ™tükÐqòÙkð3^Zp÷?Ø“ÌGÑİô;–̳ßQv<“‡S!ˆH(9ð8ƳC¢:Æ¥ÜÞoÊ^©z§´£WF· Ì›z”é&ÿ\Õ+hîËVm«¦Þ …@㘡—©÷Æ{÷õendstream endobj 1341 0 obj<>/XObject<<>>>>/Annots 385 0 R>>endobj 1342 0 obj<>stream xVÛnã6}÷WÌ£3’,É—·4[ZmÜ·#lv%Ò%¥4î×w†”â˦];€™äÌ93ç ý×$˜þ˜§0+ l'±ˆé›÷·ß&É2¤i"–ÐÂ,ž‰bxjàq’Ä9?Ç 1§Õ4™‹Åðt\M–¹Èyu9”Ì?ñê›Ëtv ·ë%mMM¸òùB,9l*)†Må~ÒÚ½5%:]î¬ÑêÙ)£?mþœÜ®3H’bš.ˆLJ!¢‡Æ¼ÈtþUUÝÎ"½öèÀÔàÎÃA)›ÆAgÀõû½±Tª®Ñ¢î¾ÙêºCƒNÅ€€j7Kaš%"¦OBñ³)¿ÂߪÛñ¾i Ód&üBÛwø¶:ÇŸÏEº$2Ãå wX÷ ÔÆ‚·“+h±5ö­©°ð«FB­¡áD|Øf€oeÓ;õŠÍdé‹'9Ý †ÒªNepXr5/ÈL<Ó¤qÁ}‰Ö¨Kü5µ«Š¶½´’úEÕív²£7„ÓF€Ù£õs œë‰NmMë÷qí•Þr«ZKÌL»o°Cìù†âÖŒåâqdó},¢ÿ!A$¼HkZ’ßië‰×þ“ºÔ®·#["ð_l/Û?«„—ô9Ò4 wä5ðöñš¨aš“õÒ‚tTˆEX.ð2c<¿°¬ÎUEeKisv”£±_Yä[¤öX¯Î OÖ³wŠëþæ[,wR+ײ0+VU£#¨T°QL¥µ!E²‘Ov޲#¹ö¬Ãæp†ô¬+ïÂÊ mýµiîéx6¸·(; øÑù2‡M£‹€^©2z´ÈJÞ4 Ä¥Ðx__Œ×軩N¦ƒ|/ÌôAôqŠLÀÚ[˜ãMüÁy†_ÐuÖû¹Ïïùü+hz“½Azímí#‰”}F™§½Ÿ¼É8´–4ÅY#þäšÆy8:»ü! àgmä¶­Ÿõ¶ò¹ôíxà=Eºo_Ð>}ú†ÓÑxEn.%€¢¹üp÷4pLÊ__›ød¨Üþ|eæwµz¸{Dûªh\­V¹÷º§èñKÓä¬Þᇠ<ÝI¥y’Îíz4 ߎ—^Ä÷ØPo¾hܳ‡8ØqÁ_ø¦9•ÍÚ©E4¼øþxñÉNªpU~ÊUAudyé •úàf罊4ÅÞƒÑ5àÄjñ +þæ*"ÓdtߨÅK߮ǫ>™Å"Ïè·CÁ?²y&æÅ‚~gЬ`D?n&¿MþDºŸmendstream endobj 1343 0 obj<>/XObject<<>>>>/Annots 404 0 R>>endobj 1344 0 obj<>stream x¥VMsÚ0½ó+öVgËØØ0ÓCÒ–LgšCzc&#lJm‰JrZþ}W’ ††6mÉ@Xöãí¾Ý•üm@ Â?Y £1õ #üeÿñùv@&$L ÍÇa5ŒHÆ­TÁý€Di8†4‹ÃµqœX['õ´émÐ7Y['9mJÐ+I™Lrü丹6‰|dB7q’Ó’ØÊqæ"“4G\/õ´ÄfSÉZ%Nêi#sNÉIí(·Ù 6Í0†—¬©Á:r—Ó(ÓV°ªžXCY7ghu7sôŒ-³ ÒméUk¸š æ+  h)žLF0/]"˜ÁL*£¨¸˜?žvÆùc,çTrÍ ZÁª…áRÀš>”L%wucئÁâÂG:ŸÉ0‚!!!ó2xûXp/. Ü^?¼û{¼>Üë×/ôpÓéíõ=SO¼`z:mk½;.õj–´ ÇÃxŒ}ÁÚZs fÃ@3r-MP(F +á;7°)]ÍöD#«^Ý‘êõJº`s#ÿ>PQ>ƒ{ÒÍ` ŠA!«ŠaûŸÈ-SÔ‚>MiŽwS¢]ºšÌ}5/GÝ,•l Ìb%‹¯‹À¶ÍqìǰíH7m~’üç±w#žõÿãBýà ?I^ÚI~ð7 w~ÿŸê‹— …<Þ‚Ìû8È™"»„KÛí‘tóê€úƒ\>â /™0|Åq›–;·k~l÷ûäæát”ES/™ºÅôÖsµ á†Ó°¢Ï9¦”T°Â7…­’xøY>_F5†Õ[øÕ'Úf÷}Ë l(†«pÙË,Φˡڰ—€ 'åiÜpÊ­âÕlÜžI=ßÿ õ¶b@¦ÇdôL¾hÜjÜ'G™ãÜž]®®š*¸®m¥È8SŽëBqã.mY”¢…ïÈïÅÞ/Ÿ6Ô4ÞØ­=>èi¯C©{7¾Z!RzÿÅ…Å'Äì­ñ ^U•Åpm\¯~Ã6ó ›ñê2:ËDÉWçP:¿‡(ÝèöÓ ÃJ ZÖÌl¸XÏ2x«WѶ1‹`ý@/ƒžO=Pï›w>lä#|DÂÇš(·÷Ôm%—8í×JÑwT4´²žCÒ: ³hb­ÓÀÇn#ïìUhÍ’, ³qŽzh’dö§÷óÁ§ÁO]¯¥endstream endobj 1345 0 obj<>/XObject<<>>>>/Annots 417 0 R>>endobj 1346 0 obj<>stream xÅUMÚ0½ó+æ¶ mL¾¸Ñª zh7½!!“ê6ØÔvvK}ÇNaÕÝîaÕ rìùxoæy~Œðñ@B”ByùÄÇ7çÇçÕ(H’CÌH £Œ¤Ýª†»Q„mýŸf9ñ»Õe7œEøm³ Oµ+·ë<‡A„þq×Ñs»Øú>¾EÏi‚>B·ºìyÖf•øè£]ÙÝ·Åc jÓ%"  Ø!â$OÉlAQ9°>åúOIëötÓˆZ–ß×c=™ßF>x˜jˆ6—£|·! ÁãÓFÉÓæÐö“éõx=YOÀ|eâ)k ØÇbJIµß8c@йcÕÍ›.érm‚Ûº`2 æ´d¢d6ÌtŸAzaJòØqÛ°µp´ÍÈ…åbG%K¦54Âð,n»[QCÁ(*ôŽ) ¥TŠé£•51ÒYÕrKkX(EO LQåРrÁ §†U°=¡×爥<kf#šÓ‘# K&¨â65Àñ§aËlØF£îüO—i‡1! X”$@@àƒ Xó_èýîz^RÑÐÚºôz/ógnª]M’8‹I–æ8ip€Ä¹µx_Œ>~Š$0endstream endobj 1347 0 obj<>/XObject<<>>>>/Annots 420 0 R>>endobj 1348 0 obj<>stream xÍVÁrÚ0½ó{$3 0ØØô–΄L=´¡·Ìd„ Æ–ˆ$—Ò¯ï®lcC&é©0Ã`kWÚ}ûö­^zŒñ@<é Ò¼7fc|súù~Oo šÐoÓ„EÕC½Öcј…­5rÇ›ÔnåÃÉ­^ónÍZ,`²:ó·)ŸÈóóò2@³…ÑMXn0“(™±ù| ˵Ob Ë´¿åO¡Rq³üÙ-Âʶya¬¤<ËÄøÆ ¥Q½!ùJ%]ç naő :üìQ¥G€[[a!Õù>Njz¬¹ãd>†a0EÔ–ë¾3\Ù0¸ZƒD™J—1bL'³  g®IµíàJàÈÒŸýÚþ¢iÑ• ö\;@Ø3'1y»§;BïÜ݈—Bn'Àò\€*ò–ŒLµAX÷ú£X´Ð&XÖLƒãè-*ùÔ¨,Ä'%¬kVb6;Qs8™aƒ`Yï~s*>ŸJ“š‘Kmò ±À¡ì wîƒ,–Û#\aèô®">ákžînÙF*Ž4Ãàðg^¹Õ»¸þ~´E@õÁŸ½7:ÖûÀ¥ƒB9™ù¦D†§®@û#äú—XWÈÔ¡µæCƒ³}ì?ÞPR­sÏ7²GûÛ'>ÆØ{<ë½G‹+%¼ø[Ô9hólËÖ4Æý-è½0¾H¶âÑ…M¹C™ü~ૼ£A–Hhª?)_M®ƒD>`ÿ­2>£ä”$ó#RåB9‹:$äØŸ°:‚;èk*ÔÑm$ÄZU£¤U“Z²±ÊD—}Z)¾¨ê9Ç©F*OCk8IpÚ¡2Dl ¯ÆFÉþÄk­µ *N­ÔÉ_¢! ƒ/Ž: ×Éä4œ;™î€ÒÎh%ÿ "Pï*TàÑLM¨r<¹ÇJ“‹†ºÏô uäÖ~lNFwÔ:G@oÚœ*äóm50UnS(ŒgŸ´~w6¡;GˆxÒÅÃô ÒÍ`Ž×“..´¡¡X:]TÎî}[¬ ŽG©µ<¥Þ40Þ/üu!ˆb6K¦ÅS† U …ÇËW®P™èÈaÇ,D‡a<ž“5´ Æ!‹g ÖWÂ99Ü-{ßzÕšendstream endobj 1349 0 obj<>/XObject<<>>>>/Annots 425 0 R>>endobj 1350 0 obj<>stream xR]Oƒ0}çWœG–I×£ÀÛ\O>è0¾,1‡a0[6ÿ·EfŒ‰‰mÒ¤=÷|Ü›¾; Ôlî#ˆPJ¨yù>3‡1ŸøXÆ¡8‚-cN·Û+Ê}ÔC[;Þ,z›Aß †ÆÅªªW,6 Œ!¯ÀòÊI /G_мp׳üíw”‘iTF¦‹i]ººD¶zÙm±sw³/âß– LƼt×óù?m&“4ÍV[©.u!ušêŽ‹M8óÆžW•TÄv±ØÄסrNÂ8‹‰(·)²¦Û‹+¥Ä ñd2à^´gÑX¦Ç&‚Çib«±$lp‹†<$<ŠÍ'1È’Ú§»Üyp>å1uendstream endobj 1351 0 obj<>/XObject<<>>>>/Annots 436 0 R>>endobj 1352 0 obj<>stream x­VÛnã6}÷WÌ£ÓuK–oyË.šEŸí(Phid³K“Z’Jêýú‡²¬¸’¢M€8ŹœsæÐßGLé7ƒe³”‡ÑTLéIÿç·Ï£¬˜‹òb&–p€l‰¢[iø:ʦs±€|6ó¸;ÏĪ[ v³¥˜Ñn^1¯x—#g³•ÈúÈiu>›å…X÷‘Ó*î~Ü\–êvps¿¦aSSOóåJ¬WsØTÜÎ6åx!à“ÕË žGøÒ “AYã¯6Žnî Ȳá:_9EÖv+u:á¡qöIUuk(„Ú:(Ï‘%G¶}ä év”ÙÁÖ†=<ï­Fàׂ2*(©ÕŒè4jð™¶K‡ÔOÕ5@Ç Œ*§¤ÄMë±n5j‰E;Y¿€‡ñÆB­ºbL’Î+U×è°‹¸Çd<"TÔu‡c¹·–úÇGžì£:‹3éeÑÉ} MË Îf4&ô^Ì^ä¦ïÏówQWgÿì´"#°®RFºc7»@cÚÍÛÉgHàLK¥· .fÀiÇ{µ#¢©2;R*#›Ð+O£>‰ŽÀáO·ø[ü?ŒGWaÆ'±ÎG{┯¿ó74þïBe'Úï9YG;%Œjø©O{sß_°©·,Ÿ‹u1KWÐ[ß”Še!–‹Uº–çYDïçÍè×ÑßÉÈ{endstream endobj 1353 0 obj<>/XObject<<>>>>/Annots 461 0 R>>endobj 1354 0 obj<>stream x½VKsã6 ¾ûWàè}X+êe;·lf“顇v½ÓKf<”DËêP¢+JÉäß%ËVw“xufì€ ¾ä?3>þ X&U3ßóqåøõçÝLø±—@¼J<*¢È zIÃ癈…·‚ØOðµ~D{Yb-ÛFëW+ñÊ‹ÀI'Ú•«hûè#b‰µQŒ‘ˆlÐv-ð×I¤]¯10¤-¨t[Y•Aà2—=d‰ý.ÊH\T±Ä¨Nbmãzà‡Î±ÛËÒ¨k,eœDøë¤Ñ³Àx”T°¤_'ØdƒÚP-K¤ý¸ÁÒTú—©þMn›ºA_Ôƒõ:„MÎòa“ÍoÞ½ú¼Ùü=í;@gì`¦ÌáîúêêN›Tê릑OWW»Rëû9ëÞ>H}ÿ†}¸z³^T¬M>ÿ­«Z Rkh÷ ”V•ªq¡¬Y–äZúë”s—ŒYH}aîÁfÿŠmPu¶…½|PìÜÊJA.[ íÓA´¸*[0»1kÇä1 ìº:kKS³ê™zû£÷Ö4m#{£3~Lj ¶KÓµe­ [›I­îçÅV¾‡cqŸk3†å‚c©1ì 5?.ÙçâRÿî®·Ÿ]¼²nÁŤ–m½ òE¨ú¨úsU'Õå­/*}‚ÌZÓ8èÑ„5u+\L×…ìÁˆZšôÕáJ=([«ôîk8’©a 6jÄž¢AL*©Ëº€Ç²Ýƒ!0ÝP‡ÔxOÜ@:eÁ4íe] €§ŒÂ…BŽ5Cø c£µy¤0 ]ð94‡'Ò09RÕ>*…ä|4.;¸(ý?°âœ”_ωb›ºs2/¤Ä G;2ÂýsyTLJ×q2c݉3Sã›pÞS§S÷„h["PØØºåa‹CŽ€u¶²6 ¤}—Àïât\Öùù(¿îªT54M‡ ?… ŽÄ3d[N©GÐ+öš)­-"“,3Y²’.•ÓåÁlq‡le¦ÎÞ5¦šæ§ÊbŸš†ö“)kMc¡³´àÜT•¬s×°o_«'…F\Õ‹üõ£þ€ú¢YÿSͤzÝî–ù>p¿Àð8ÀW˜»õ~  ݇žÚ2Ú,Ϙï/uâËyÔ~Ö œ^ /8|F«qøôù°î!ü³ðÅ"ã~—uç.µ…è KM¥I<á øko´êM¨aÑ2ò–É ñ¸%héÓföÇì_Iq'ƒendstream endobj 1355 0 obj<>/XObject<>>>/Annots 470 0 R>>endobj 1356 0 obj<>stream xT_oÚ0çSÜ#U‰ÁIHo];Ð6i[ª½TBNlO&Ní¤Œo¿³C E[§¤€ã»ûýñùGfø¥†%PîF32Ã7/oëB2ƒù"Áç¢0"ñq¥àûè˜â_& ÉÜ%C´ˆu妟æÜiøú—À0N1Å&t )%!ÐlN2‡gdq\9<Î&¦í LbDVg»±CÇÜ(! P¿r»r”:y1&9¦‚銥oQ‚™eäÜ[0ƒ¼Àíõ5>‡ÏUþóÒ#_ ú"ã'-9¬o–˵ÒS7ưÃrÙ·œub]kÛÙ‡ñÕ+3]¡#r&(+çã¼–t+ ë¤n`ȳÐÕ*— ¥P °ƒâÌÚ~'› 0Aj.Ë h{ÃÚ€Ý7|…ûaæ¥n¸tE-X¹“Н¡ßÝi„ÑV¶4²¶XÍA7Û-zaèF€Pb'šöN&ʬ¬±:¾³xŠÄW|³ƒˆÎHùÃÅîm+mPJ3侺鯻”®d‰¦oû¦ôèZ`°gãÉÚ —æa\mØäDjr!Sb̶ŠUCË¿}ý^:ÉÞïþü›¬D—¾¬o6÷þ–½âç¶>/XObject<<>>>>/Annots 477 0 R>>endobj 1358 0 obj<>stream xWMoÛF½ëW z’‰%Y²]äàµÑCƒ¤UP Q`¬È•´ µËì’RÜ_ß7³-1)’Æ~ìÎ{3ófvô±—Òÿ)ÍÇ4™Q¶ë’Þ´—ßzé|ÅÛ¾NF¶$Oüõ§EÎoèòRZ¬Áëêz–ÜÜLh‘ ¥-²þbk­k›UÆYÊ”¥•¦:èœ*Gu™«JSµÕ´ÙºPQ¦‹"*œÝ±¹Ù›¼VåÆk1’‹Åß½ Ç3_äý_+‚ý\³±°¹v»7Λj» 0¬`˜n¯}¡Ê0Ðß)s»²®Kh{åZx;mf8†7 pÀ=lMÆœÏeZŠÌ®±—â]N˾N6ɉїÈàÁÕEÞëP:+áà8<‘ú Æ’¢êàÈùrµõú„!¢žB¥wË‹²u€’8_©1Æ{UÔ:ЋËaJ ­stØj€{1LÎË9ñ¤âWa` D™vé‚©Ì^ƒ/Y½APq߯d pÙºP1­Ñ”·¸JÌ Vû@ˆ<[e*ŸáhãZåAU‚mlVÔ9$Њ+b^×®(Ü÷eŠõcOôÇÚ (ÚÝ!àüwȇ»Ç·’ÊG)äÌ*£ñ°•I›„ÛfëQžòª½¤W#ˆÍ²¿yTƒÑ`˜ÒåÅÔoÚú½;S}( Ow=]œÖO“é@AWdÄñMQ ’k;üG{·¼8æjm«õeÿl´²†…H#Ôžu·Æ Fë²p¢f˜xŠcŒÚ+SȬV*ûpP>ïæS Êke SAÙµÇÑ«CsÐ5sODªà`KŸqÅ€`af?t2ŠžÔ¼jÓXz½6Ÿ ? ð:œÎкžJvIú*â€ta?GEõ3l‡žL€Ïº=™‘¦¶¢…`2æÙx åó€,ÃjÚTAz“\?O‘f⦳Z6µ¥Cüê•w5¤Û7eQûñ<çéóÎÖ,/¾FÜóäÏbZ._g!§Þ_'Ø(ây‚ë ÷î½ÜlÍ»÷ÿÈ‹G*ßHäáîö6v2i]··‘×Üq—ýÿàry}ÌÃÕ<™]OðÓ¿9F×JRúsëPgq /›Î§É|vß$Xr5åW¿,zozÿ ì&iendstream endobj 1359 0 obj<>/XObject<<>>>>/Annots 502 0 R>>endobj 1360 0 obj<>stream xÍWMsÛ6½ëWì‘I$Фø%Ýœ¤ò©mÚ¨ÓƒíÑ€$Á“ I%Q}w~À°U;N2SkÆ$°Àâí.øi䃇?’f1äw#Ïõ°§ÿ÷çåÈ#׃(ÀN¸ƒ JܰmIøØY½ØM•ÕÃg¤ZÊê§Øç37&«ï»AÛ2¬©‡Ñ:§g¨ZƒçYšêu㟺eX“°]—PÍTkð<‹ývÝGé–a ÓvÝ„<«Öà9ˆb#^Ý2¬Á\{Ž(Þ@µ”5ˆÐgàÍ0NŠ—ÖÕ­a]?n³¡âÕ-²¾]Ùɯv0]†àû°Úb•¢4vçó¬6ª@¬rGÔP‹;!YMùju;š.ãv‚;¶þ‡WåX÷wŽà_s~h Ù³DQ7œm Ü;äI;ô¼hDÅU;A4P󦆲'œÆ¡â;Q°á[Qð d'{iÙ‚eÅÆ´÷‘y0ñ)s«ƒºK°;ˆ1­Øý{Á!g°º;|`ðÅNrøÌä‘n&¥È%¿ÃjŒGX“ïá‹höʸ=yƒøÊ»Þëq»ëUÎý6… M¿x1y¯'46DÚÐTœà,˪©Xa¸ëH¤ÜáHUB >fUyl0kP`y¶BʵBwíìÖl ²Ã^Œ) ëW†¿Gáù ~,¶Ç  Â&Ü-½ÝZ0í¤L—]'ÎwÐÿÑÉÖçRl~»¼X/,‰¢•)z‘åÕ V¸Á”]Ý`Òp<¼6Rw“ éÍ›Óó ]^,—²Ì˜¼ z,TÅ´Å®3¨È±ÞÛýpšï7Û YF³¦Ë~÷<àRÊcSÊi9Z«¯y@ÓÄ–wL›îé)‹g q½ç6 eÇ&šÝ Ç£»õ§“©Î™äÿS6=Äör:éDv'%À‹8õÑLÖ'¢únZ©œ=Í+ƒÊOpJ94™0è—Í£\ƒ^¿z>>äZ¦ÕqËò¦¬l—È#EŸ³tÏË镪òÀ+FHÂÅ噸ˆ\Û06& l+þéˆM”åcBÜq±vá'RSE6ÜÕ›%t–ÎQh3I)ë±>Ä) C%¡ÃÃ: »Cù¹ÏÝ:CF³½xZ<ï³èIýü¶˜Q?φ¬ˆÏ”2R҇Δ박ÓL9Í”ü½/ñ¢­§è?¶’8ÅïEÔõËjôÇè_¨æ©«endstream endobj 1361 0 obj<>/XObject<>>>>>endobj 1362 0 obj<>stream x…TMo£0½çWÌ1•ˆ„©´‡®ú¡öÐ]V{©0àØÔ6m£UÿûŽM’¦dÕÂfæÍó›7~šàã@Â*†¢›ùÄÇãëûݵ“:ˆBî-ü˜=Áj“dÄHü¯ñDâÀ–÷Q ×f_³)²ªayA@V!À:Éf³‚¬tÕ}ÈŠy!{Î4HÁ §¦h d¬„|ÙïÙò©»ü9må¸s@œåYTçQ\8üº•9m*Eϰë-d‰èÒ4Lý—XþOböù/.lˆ‹`…‚fåüPïs¢ù„(¬aP J1a€w}Ë:ü¢†KŠ= \¡À¦¡_ ´TqÛêW2m¸Ã] &¬ºAÈJ%¯*æÊœRÔŽÁ\ mµ´Ñ®í”ÔP0»ž<]äÓa,hõ:©«Ú3íA>ŒÄÅÐåØ Ì`ã dOíPŒÀõ ¸¨]õ^É‚i´Vh²n{nß(*t/5ï™rçžT.¨°Üðo%U7zQ÷¬à•E™ÎáM{2®ã£õaL"Ûã›Wj;t9I€+­TëÐ&…Ÿ/Ò×îFÛ^YÛ¢câq(΋==‹É½I½’k£8J‹§ÃÆšF1<ð¨j3NÌyË :) h½µoþyûùãüV*+à89¹/<¨”ì>¢àÝtT Ou 8ïðþ„„oІÛUäÁúíãa§™fG:ýWoóKÀaµ8+"DÅ©´«Øƒø3Ô3MOaÅÿ„ϲ}Fßã,¢¯ÑFî¹Þ¶èat!ö¯)käJ¶­|±F«x=(FFØtï¥ IH”®`•„$õS멟§ämmæâ°HüŽI@øÕÈ–Áh*%Iâoi YÇvë&›=Ìþu#Æendstream endobj 1363 0 obj<>/XObject<>>>/Annots 509 0 R>>endobj 1364 0 obj<>stream x•UmoÚ0þί¸´¥.!!¤}`/­*mªº1íK¥Ê$¼;µ2þýîl^Ú¨Û:N|>ûž{î9óØI ßФ9”ëÎ#¤ÃœÑ\$lù‡tTàˆë×Ù>¸íôY÷†¯W$#×]G°Æg†Ï8Sð­33ŒTd,§Åt„Ï8;.æ Ëpq&äf´ø~Öf—pq™A’ÀlP‡£œÇ)̪¨³²{£8/jX4Ö¯„í\€A#WÎÀ†kÞ@-ìÂØ5 x˵«`Ð̽4ªÆJ½ Ë¥©·øÞ;™ýì\\"ø¼ ’ m{@]pÂC´aÑ/œï iù†óúpž¤ÈݬêJ©(c@É1¡ è0Hú¬Èsò¹^"+@:Ьp+^NcYôêc*%×0°àÎÃ]Wj/¬æJm¡Ä7·ðê%¿¯ßB{wÂà†bo¤HiX†~cUQ§ÌæÕ0m^(Ž+¹G8­X=Ø„$Å/ä¼\‡|]-°tXšÚŠšª#u%Káß ¼ãlOª6ë»ââak±6vKôYñØH+*ÃÊ vRÂî0RL‹D©ŸŒz´ï5ÎwB£¾©š¤Íó)‹–£å³Ô‚[D¸sË_æK®9eä{¥Ìœ+àÖò­ÃLÍ“¬(+j"œÁãG­º ðª’$Û¬åe­$æ\WPOGUMé̵5ä7m¶P½Ì;G—´ÝõBã`£66ȶa:—3¤ Ø{ÔÜ—+áŽlìú” ù!,á iHò5.‚+= Ueºv2l,º{ÂMn‚C¸4–š.nzqW…M‡Î×Ì­i<ò (F$ñ®ËU½â=XÞã0>¼Îi^Þ¼Å^wˆâþðóoOFVWÓû)…§w8ÝÀV@¢5À‰6D_þÕÙÙÛ`ET“ITd¨Õd(z±D•x¸š¾p< I¯6b³e þ:>/XObject<<>>>>/Annots 516 0 R>>endobj 1366 0 obj<>stream x­VÙnÛF}×WÜ73ŽD‹2µyÝÚ(tU>0†äHbÃE™¡âèï{î )JCÛq‹Ú€¸ÝåÜs·ùÒ hˆÿ€¦#ºžPœ÷†þoŽ?¿ß÷‚pì(œ ýr æ3X?eôGo>çÇqˆ_|þ¬~j?¡Ñ…!Û1Oüñfå:Sºº )hµªñlâÏç×´J  !­bO$‰¦ê©$¡”8è>½[ýÝ»ºCFÉÛ< û¦1ã‘(’®TäHõ˜_%Ìo%)©÷Y…û²«;º>­ Á t¹W±¬±Q, Š$éXd2¡è@±T•H VÒ ¸«Ä[‹¸*•f+©¦r'•¨Ò²„/û8 ž®õ-°šOZä’Š}IEåšd&sY:ÇÍ¡˜ï‰¨ 1'~«ÃN‚Àh_1K¨mL'éz-,‘Þ HR©(Iu¥Rh[N¸w™aD¦K¦¡#ÜIt„Ü<ùôSE I1¸™8l"a{ÍÄ@h½/bCåÓV&È.BIJ„™"M”ç1Úª0ñƒ) ¶ ’ÓJ;~åzÆ)` ħrŸ!ç’2©¶þèëWp´”wp€›¿ö;š ©P=Ÿ2òrTkºË„ ØV' q£0s–Š9ݨ%×%¢æÚ0I‡²®+K&)ªG#¿"³uÝhüú#!RÎÂôkpì×Aƒ‰ˆîJU)aKý8LÌX1JÇš!½T‰‚*$mÄc²‘yþ౪ËרOyŸŠ>}î;œÃÏ«"Ûma3`'’•¹éáÝ÷â æeÇ&½µŽN”̘gñŒGP J¼¯Vôl˜BÔ+Óä~ùøƒ3Þ E ÉÞaŠ~C´|AÄ|±Q¦#ÒÂy9ì¤ÜGkCg&|{½ÁÚŽeÌjÿÖÞ¿g¤ÿk‹Å}VF"[òè_,êZy‡ìß!ð_ÔÒ9£1&_E÷Ë30—†dæ[rô¢ À ï¶FO7â±Ã~• =œs›yĽlzüÛÀ^Ú=¢ŒëênR÷éhDG3tK‹¶V.Ë݃GK4†½»Á½7sykʦ28µÙªä¹ Þa쯺ËZEG·…|®qj°U¦°A¼xðÓXä—¯2¥o7/Fœ]ÁBaúa'ž;FÈ}º±ïÎŽ ®ØmGvÛ±ú”V[WåÈhGÕÌÅ6%å®Htp­}>9Yä6ã5%MšëpÏq úŽigW¤FÜRT—Ù3„v™rÃ}C´/øóBo6Í/N•:Ô«BÞÉ8]§õ©‰{ëÚ æÅÄàð€5ˆŸðI m*g½Ö šœÀI£Ì²òɶÚIfxøµ•ƒC ”/~¾°o[fKåÈÑEá =Ÿlî^Ø\:Ò¼-"¾{ÕÊuˆ&ì ª.ú¶-¿®Rã¸ÉÖ Sêô8qu7«‡R0úáìšÆÓ¡?ÎxÚYOfØkúSãtûI{‘1ŽA£0˜ç,=ÁYú#اËl##eŠ<œ†þtRoäñŒ5\õ~ëýl]q­endstream endobj 1367 0 obj<>/XObject<<>>>>/Annots 545 0 R>>endobj 1368 0 obj<>stream x¥VßÚ8~篘·ÒîâüR趬*õîš{9­„Lb UˆibJé_3v!¢\VÇJ ãñ|3óyfìï#6þ1pHw#Û²q¥ý÷×óÈñ<Ë?t¬vàĶå5R_GŽoãºïÇV µ¡e7iYÈpÝ÷´A[Ç#[-u´®‹ø¨e!!i©£u0˜‹VK­è¨\Fß¾–.Z/¢Xwà2=©£õ=Ühµ¤µ~„V®id“½‘´–Ѻû&_ÛÃo#u´‘cò‰ GK™‘­Ýäë2ÒjÉØ:%C~)_3Ä0RGš|ÈE$¦%Ò~Hðà:8O“N¯ÚÀtÁ€1HÖxÂ~XqìB’éõ!Iǰ•ªxù6ùÖ?xmXÚ·æ¥Q¡MóYÊT岄 _æ™T/ãÍ’?Âf¹zy«ñ`Â(Ñ$#Gí'“‡U!`_‰4¯ÉüŒƒ0Ù«aR¹Ûâ'¢Ÿa(œ_·pîÐÃb$³ö Ñ‚» YnÚÄày¾ü¬#UHra~4|L®½\ìNš%ÄùxǾDÚÅø¨y}jø@Œîa 'ãá½ ¡£OÆlö\È/æUÅO³™)‘T–µB¦®”ï¾Gò7¤ŒZÊ®g3SI¿÷Ö!ñ¿<õ‰íy2µö:OÓvvÓ˜‡f#6 •òA‰ÔV€(ÄN”jrÌkå¡°_dvHÈ5¨£N¤Ö|V×Àð¼àÔVœÌ+! {^q%Ú¾¨!•U%ê½,³¼ÜôZTIZ˜.‚¶ã›†7ËçxÇýmý~îí^f¿1iz·oqÅAö¢¶H…'uf(AbÖ²(äã¿$|¿/N ¤fΙd9RG£…pÜJbE³²,N JEKJyMɲq|ž_kYÁž«t r/Uľ\3µùëä„<|C}XUò òRИ«O»PUþKèÙÛôË}¿ç”Ð/Í9ý1á^]û½éÿCæÍ·¯Í”{µW=PÐï`¯½†ëfmœßl§Ë¶vYú s¨Þ;56–Èà˜«-l±<±@(–颽íp$˜•¶ìgFn»cïÁòἌçó7ý8Æ7K÷ÿTǺ®‹ƒ®uìõÚÔF÷^~u >©erqzû¼ÿHi¯`,§ÁuÒg¯L:ùßÎtE÷Ï­Ó˜­=Ž×¦€úE° æoôYOøÓ#…¡åE.>‚C+°#íæ"}ÖðwϨ?xyà1;9LB;¦Ý>š¾`‹ó æÅF¬*]Œ^èYaáS·ø1Y~JFŽþhˆ endstream endobj 1369 0 obj<>/XObject<<>>>>/Annots 554 0 R>>endobj 1370 0 obj<>stream x­VÛnã6}÷WÌ£³k+–íø’·´ÝÚ¢÷i³(ж¸•HU¤âäï{†’|ŒEÒ6FJ3œ3sæðò÷ ¦ ~1-§4[,“h‚/‡ÇïƒxG º™Í¢)4½YFóö-§?Zë|=ƒ['ÑŠš·`M£ Íã9æÀ/ÙÞŽÖÙ즉¼^·ycëw›~2ÕŽ®ï×Ç´Ù"ë›Õ"Z¯g´ICÂÚÈá"š"دÂËL¹«Í×Áõýü0a<] M:Üd浑^[¼Ú‚mé(nçÅk$}˜jÆ?pØI4eß9¸c®0axo+_ ÓXÏH áàò¹:©líµQdvâI¤éSÉ?E^fbD»'þ™‚cCycb6κòFää?ï1ï`mÞÔ’³`¶Æ1…ýÔ‘ÊU¡ŒïµSä÷–¬ “’Ïøˆc¢R®Î½cXX,¾UgDŸž•“­wim½,q¸0š²ÛϧP{í3Jõv«*¤‚‘ó•Np4 ‡d«“ËD‰Ü‡œ”©‹iØmW„£¢vX MÎNêñjÄyô°m©*Ғ°»z)•qúYádPRÔL*Tt°ãL#iK ð}¦eF ÙJO…*lõ “quQr̈6È åõ@­+©ô¶‘¹J)y%A[!=ªÝâŸËë¸kv5iG®•'eåhHª<瓤¹‹¾ôÙvè, S/Ë_Ã&Š@wxÙ…µE‚D×cc´¡¦ýè)ƒBrðq€ÐǶK¿W}oϤê]Ö(ø‡^«”;U7áаPÂö:Ï Šƒ°X QøôSèåÍϽ.sD½Qœš5€9vZ ¹Øð™ðL*$Ž(Ò£ ”g†åj,‡¾Š¼WRj ¡ÍQ1С†ìðu¥C{[ý嘞y}¿8Ü}R['yPåq]yÅ]v„¼Š2W/µ»É )^ÅŠh4RVÖ[~G=請½-q_]ÍpYœã¢¶âÅßl¢vQG:¬¤Ÿ…©›2Çq;a¼œ¬Ù7Jú W5QÑ]¾SI%`¾œGËÅ ·Pv™ð§O›ÁoƒgÌendstream endobj 1371 0 obj<>/XObject<<>>>>/Annots 561 0 R>>endobj 1372 0 obj<>stream x¥VmoÚ0þί¸o¥Ò$„·HýÓ¨&­Ó^²©R©“HâÌqÚõßïΆPRÚ•®•ŒûžçrÏÝ9¿ØøïÀÀ…n¢uöl|R ß/Ž×³\è û– kp=¯Z¥ð£áØ=«½nŸâ®íU«Ý®çð)îŽl˳¢ÝI€t.ÑyèqÊ%œOp葎F]bí’ AÔ€©J²ì4¸«»«íKÛ7¡(C)J•d–l¾fj]¦óœ©h5kBÁÚ Ã6°4_á2䊵52tz© &Ê·ü-çhÏ’TиJp¼Óó;œ¿ 5b¨CþbÔ†H#F1Òˆ"ÎNM@_ÄYÃ]T>˜˜ü[…{‘Äp9ž_íÅ?Z1 ­­ ´jUZEk£áwö™5`ë8¥’Lˆ&F$3#¡ôLg5©ØQÁ&¤åU£‰QÎ̶4FÁç4ÇijhPJš9ÍlKcd}Nó.¡ÏÎHê7 íû—©Y:–’=ú¾)»¯¦ê^Qý¡·yïQ=Y¡à‰;Ðz1 jºSöeltŸä\.„\äÖùÔ…µÀèÕ¹7DãëB™æî-ø¦f[0ßPùºR}*£[hi‘ ØÁî:™ÜPÕøºÝQ| Á-ÌNáL÷æÖanãîWzXqÉÍn¿ºÆc¸‘ÏšãÙi&“Íj‚«} &°,®[“!\£Kõ³I¯±Ø·p]¨"§Yµ1r^cU IÑžÔ`µK§ ¿XŠõž¥%/Õ2}3&—åšgÊßw¡ 'ÙIN¾Ð hê4YØ;Oƒ‡X(ôK¦‘A$Öy©RáO9ñtd¥s¹qáù¨³&úS$aú¸ó.FõíË 0×eȹM…dX#É9>/XObject<<>>>>/Annots 582 0 R>>endobj 1374 0 obj<>stream xÕWÛnÚ@}ç+æ‘plïúo4©­zq_šDhm0265FJûõÙ57 Ó6EM„ñìÌÎÎåìÙå{Ãÿ pL`6‹†®é8²}|5L›k:XVW³a̲5^H1|i˜–®¹`1¦™¤åô­$©•s¹íÚ.Ú*i7—›ŽòÌlüVi Ë@k溸úLÎч’¤–[$s£A-#J’Ú®ãLwÑj]Fž¤´›kb&ä™6j•´§Õ‹Œ”VJ;ÏfMë2›¡%‘ö·) ]T1›ÂÍÐÃï Êßrm­ÛeàMduð‚fÒ\ysœkÒ4Ž.©8·9L³<‰Ò´FzFKé¹ %y8 3xZ'A¥ $S1Ž&i>^Š<˜=4§cц?"Nñ1‹Úäµ#ƒê”®7A?çýMÇ>:ÅONýYôp%c…*“tíÇ!,³0ˆVæ&^ wr2\½Òñɸ7Ô ],ãðù П—TíʣȲ˜´1UÍ×€S%š" §¨ÀS‰¹šAÏ]S¤ŸÜ€/ üù ©L´L ¡â¢±—á…‘G×?¨ó þ)q×d·ÛÝVƒ™î fú0êïö62ö )˜‰¬%©‰†žîÛÄgGø i •¥nƒ©xQ !I á !e qÝ?Ö¤.Jcpn{Ûâ’) $ûÞŒF™|«›I©æ²S¯ßŒúˆ¼¾.º_‘£~¯7ŠS_Äý,?z=:.?ªÓ2H“U%‹ÖD‚Kµ©€áv¸ ït(Ÿî¿êR¢½Å'K±‡êÿ¼ ‡ãE5ˆ‘÷«Qjxɾ%i­d³í~ÅK PÜ ÝÍMÒq4î2¼Lâå\wéÜW@‰ä|]áUð½HÖ"V˜-&t½KÖ6^÷ÞEI(2èÇÓÐÏ™q‡kŽíâO21iè­×øÔø”Ÿý—endstream endobj 1375 0 obj<>/XObject<<>>>>/Annots 605 0 R>>endobj 1376 0 obj<>stream x¥VÛrÛF }×WàQq$š÷‹ß\Oì—v¦MÕ¾ÄÍ’\™Ûð–Ý¥cÿ}]’²å$’{† 8gõ×….þzøÄP4 ×qñ/óããÍ"BÏsRhÀ c'­þ^Ä.:ƒ,ubrú©“9½0  œ¼˜(-ò¦™ƒ ^fIŠŸÖ"g”P¤ã+E„b,›7p\ð3z6à»j­9¯º64¬5çõýÄDzæ%k=Éë¶Uì QŒEÞÌGÓ ©0,)‹0¿µÈ‰0xAhyH"µ9ÛÒ*ïàü³y°Ù!ÿR“elJC½ ›b G~ŠŠÉ3Ðù D«!¯»OŸÇ¯•øôùöݻ͋óëpÆXû1Ò´)—›ŠCÏtQqªê†º„œC·Å%Ó ôc/°¶„ŠÝ󽳚œKzŸ×¼á­Vü.¾Ø7˜”ìXY -ºvE5¸°F }B#F7Èb_ƒI_ŠÝŽKÌ¥PZŠ| ê\U ËXA'Ahüë $–¥%kUß)¾2…u=—ŒàÛÔOÍ•:€ç»(b¬°á‚ ÊÔ,9SÀÄhö¶~ް o:ùˆlÕÐôâXj3T˜ú"×!¶ˆÂËÌú›@Œ¹8SÏ¡$é¤É³7±ù°Ñ–â^”«Ár;‘Øp…:芵օb)h’W`õ¢µ’ñ–ï -gò€Vßñ;Þ—èÀ5öÌXÓ× j-úZ–Ý'¢ƒÒä%á˜Â @š(Póú…%=Álí¦â‡$C1N0­"|x´ÌÌD/Ï3LÇÁ Ì{Ël01MÀR ¹ìðXµîØ–åj{Ïêß.ï¶ÌŽËÀé_Á1,/v¼ÌÇs'bãûN”ps¹½ÜÓàž¾ΓtõþýÑ"Fä÷ââ¦îrV_Ò„^\`ûÿÚîŸû5Áp¦ÇÓf2Ætõ 9Þ¦aÔ?E¯ Ã刄œªü@°­Y^F¶@Ýᣯpå˜Kx!Û?ò†èôm¿~÷Û÷Ôãt²–?—òOÚé·ËVózi ѧH;]-o•¶,·´_5kõ¤+«ûêµcùUŸ!Ï’Ñgð†~Uʲ¼šyxYÄ^Á˲4ëY#Ò ï°vÜÆ9^¾Dè8+ªi€uXOX6Óú„Øibpխ̼VbõrëtðsžÛï2ëürVV&ðì‰ôç×éxçxIâ„iQ8ah®{»SÁ,Uÿ(¼ÿ`-ÞÖTëz X'nvÒ?a:IœÚ‹#(ɇÍâ¯Åÿñ˜4.endstream endobj 1377 0 obj<>/XObject<<>>>>/Annots 632 0 R>>endobj 1378 0 obj<>stream xµVKoÛ8¾ûWÌÑImE”hI°‡4Hr*°ÛÕžê ÕJ¢*É üïw†¯$NÚØõnÈ’óüÈó}ÂÀÇqay3ñ=gÜçãÝ„ñ…—À"‰<‚…©†¿'É’Ä5p‘±ÇŒD‹‹˜4ý¿¸ès'Ñ"ã!*ñeèE´-œäìrîkÕEäqÐ-F¾·ŽÎ”fyK#=±Ë8ª ] U•äì†3N—^ˆ‰“äì†<ÑšëѦúôù¸Hþ ÈÌûi(÷ì$¾S'"IWyÝlë±êêÁò~ÍÏ}~\VBjá¼yîþ * =sd$'ÂI5ùàJ‚l8ŒûÔ çÄ 3•-8¦q§õ´ÁÏ=f/XáZ6Ýv,EĆ—5;t½,¶¹#éñA‚¢‡a/è‡M…”Þl1ɬ´D2ˆ¦„qסaÑ ÄvÛde;ö ¯ƒÈ&€~æE…ÏÃPÉ™Iû´•²§õiòÕ´šÁ×ÕÀ HÀñyfÆ™ULñ}êËÑÕ4b‡]-r ß ó&U-–Å8ÇâKw1žøDQ-ÜuZ[ž¢£ø†"GŠ æªNvFb¦Å|9ŠùÑ j먪äº=Õ÷íu ¯Þ8᳋‡Z*ÅpíÐ]G çö äw),Æ£3, ÍaYìVç7xý€Gc~ý)~J†Þ©9z•þG>p5¥‚¼‡€¸¸ML;ÈâØãIˆí$ÇãPÿ£;BP-áÿ x‡?ˆv+jÒœ[…yì/iwäظÞà9ÆO3‰ìÊ^Œxuð˜{q”`ËJ»9¹I'Mþ Ë.­endstream endobj 1379 0 obj<>/XObject<<>>>>/Annots 645 0 R>>endobj 1380 0 obj<>stream xÍVYoÚ@~çWÌ#$°±1RÒz¸•ªRY‹YÀ‘j¯ƒÒ_ß™õ±B…TÉÚÙkŽï›ùÝÒAÿ¶ýøQKcÎÔŸ/ÓÖÐa˜¦Æ†n9Ì*¥¾¶,§MÃfZì[Ì,%ZÔÍ>ÓÀÔMœÅUÇdýR¢Uu¯a8ÅQµXH´8ÐP¡Š“¶ÁœRÚß«;h&ÞkèC´¡hõ½Ût"]ÃÅÄ]w…ÞZÃsœ>¸K娮߆ÿü$Î$L/G£i˜,xx™¦üÎÖÞ¢ A,a&?u;îMKƒžÞǻ˓›¡mT¤túJg1¤Ùy§ÔoP QÿUms)2"‘ˆeod¶i²Ì} ÉJ­É][.ýÈ.ì6¿(G÷¢ÚžñH€¼ÛâÍ<^‚ã—2b/`2ï…št~OCv—ÝÃÒhD±ùP†æÜÈZ¥ãÔ;®£ÔX½ýqPýÎ Ä*˜ªŠoý‚ AP «áð«@³Õ!ð*6 ©ÐƒBÁ‘&]â¢F¨ê˜®ˆ=ö4F j`„!1S&$Ȥq³þz<½tgßÇÞìz2»ž¹?žó>U1äÏ×ÃÜó­Vhç!: |ƒŒFŽãë}ËÇÈê]ð7ÁËAy^×yUñTTSFÀCF¨í¤£HhnUkÐæjøv5tb(öŠ04w¤Ð=ÎÕ5WžŒ˜>Q…œ·á[¨À5RpõSŒ|”7Õøˆ¯ÿ®óiäÅÃép1Á¶Qõ„ºm3sØ ÛÙ6$$m¨¾-ƒo–¾<ÎyHÁëUz¶æÐî3°·¥‚Y7LÉV¤\bÓ¢$Ó6™=b÷I»-ºdì¶>·þJîâÜendstream endobj 1381 0 obj<>/XObject<<>>>>/Annots 658 0 R>>endobj 1382 0 obj<>stream xÕVYoÛ0 ~÷¯àcºÚ®o;öÐm°‡;¼§¦0dÇ]øH}¬-†ý÷}’í¤1ëºa ‹”D~¤HŠ7’Nþ:¹™E¹¤©8›áË\ò¦ªA–ãbÌIwÕ驌¾JަÚdÙ–ªñEÛæ[ÅuËß²4Õê¡k|UP|UÈ5ð¯%ÝJ9¹MÒ(¡¼­ ãa{Íò˜šû5D³bI‚,Ú<Œ+ì èõÖ*•A‘²L¤NË‚eĪŠÝ×rÆp`4”Òƒ_´˜¤2]/ˆÞãæGa?ôah×mÖÐbáLÎîÁXg,øbc@ZÀ1½rìdw  YªÁ#ƒÎÃE8ÙÞ8Y1§„)MÅŠîÄNlí\Ë„ê6¬Ê¶I¡zÅî‹ gwiÞæ‹É*`2­‚Àú õÃ¥œ<­xëÁïeº¤ùqpº£ücÑÄ+Ü—±%fK¼Ôá!=Wh6›geȲc³wÎùà›¨,qóã=ôNÀJ¶ºµôµ³ýJ½Ö!D÷çV¿C®ðÔê¢|„ûÏg½"³€úÇ]2 éõsdõ”_A ZŸf,+e"– ”t)G¡à„='’)Œ(Iÿn*np¦(Ç"ùx/.eÓ$¦¢XpÀƒ7 ¨×†??/„?TÇpœ3€€‡.._æ£gWz²4|â¯jûžúð¶bð°¸–­GVosƒ¾îûˆÍ(Ûo»@FA²'6Hù­ZùOÚ´AEYDè—²øî¥MÉ#¥óèÌë›ÝECç™èÓÐVjo"º×ÄóWÓ·¯ñ9+Z–ñ¼V†Š«MùnG5T“ø#¾éâÊu\±”hÚ,×R]ÇC/Èw;\È©/}–~øÛêendstream endobj 1383 0 obj<>/XObject<<>>>>/Annots 671 0 R>>endobj 1384 0 obj<>stream xÕVKOÛ@¾çWÌ1ÇøÇIÕE€8TêÃ=­%Yd¯S{]@ÀïÌúE,  ‚CA˜}Ì|ûÍcç×À m˜8àú§Ë´p¦ý|?LÓ‚±å›¤`O&渖ø1ð-½©kú´8vM¯–hÑö\<êΦàX­j‰Vµ^×!í¸h;¦ •Ôêuñ„6&âÓR§× œJ¯ëà®J¢ÕÏaÿù N<°m/ñ¶ãÀ7§SÂ¥¾¨a<<»µæ°dŠºÝpÄYºIøA+r/¼X0²]D.‡ðè'ž…Wó=3ç,O²›;¸g4³÷ÜGÍø®×"á ÏБ²Ç3–Y[p|¼ZN΋2Q0Æó=Ôz‹›„Å2‰d!7¥–çì¶Àì B­Z—é'ñc“;5 ã–„‘;EŸ¡“,W9«î׺]ÀmC(Ê(ÏJ%Ð4¬Ø‚'<]¤BŠ´LçÃÕ‚8½ˆôáþ‚†Ë£ŽËêÔVöPüÎÄNÇ[æÏ¤â+ž#„Ñ ˆ¦ÔÇ]PíïÃßáôÒlvšdKɳñó¥¡'Îd¡öÖø ñj#OGVGÌΣç-b|Ô&›P;Â@//t n.ÕèZjÿ6±¦®³:Òz 1¨ã5¤%^2j#³`)×ÉT“KТ,Óý”]öÔV 0ì,Â(D&1¥ªè6z°sÒæ Pþ y×dŸMî=ôŽÿO ¶Xl˜Š×už±$3€­±èè„‹HŒHD96 &âµØ- 5=/V€Žð§²°Å(¤ª‘ˆõüÂ=\‹fH¸{Ð)݈, ä£jRh‰iæó‹Ýøy»zðµòås)úŽU¡¡öIϼ±C[õÿn‹eºóÞû¢ÐvºÐx&"`ôªÒ¨s”½¼}m=®ØÈLÖ½ÊûTÊ·mް8¿®9:8 êö…ºP/p±“ó°`ÔÆTo/èÇ·€Ÿ>2_˜,YBÞ5FkJ»}“ÚMê!Ú§.Ûðœ)|o´ÿ¼‰gNü»EÚ=!%ÇáàÛàdÂâÛendstream endobj 1385 0 obj<>/XObject<<>>>>/Annots 702 0 R>>endobj 1386 0 obj<>stream xÅ–MSÛ0†ïù{ ÐK¶e;7ÚB¦‡ZÜK'3‹àŽcS[¶¿¾»’?b& LÓ0ɰ^ißÕ>ÖJ?G \ücrð,×#×qñI÷óu6Š„@à LJ509ncåp=.9=ÏáäÄAac‘“ù æ½ÜåNÜXäbœä‹ÐNÅ@acuqýÀ73™;X«ëû®‘j`ÖêâzåIù†˜·µº¸^èÛ™˜¯ Ö²q|î æ“o@^cuq9Š‘“ ãZ‹œå·Eò"\ŒµlÜ3ä‚LuðÉk¬>®Û–JÆENS_Û 2Î1‚µú¸,f5T_¬EÞ÷ÉS˜Õ ί|` ’[¤ Ù8ö I p’å8¹SP©z“k˜—óXËßøà>—Keß[Ð8&+î7dUÉß5Ž”ç7óç$ù’œÞüaôÉ “œ I‚㫲ҕ,ìŒÁ7Hr õæ¦*7:Cé•\ÔwÙ­^¤™\•…ÌçãÕB¾LseFFåÐ}v«?”Y ³‹ÅõݬÐ`´Í€Ó38;ƒÝÒ”e#?ÎòòFæTîéÔácWƒa 0á´Sq¡iZ#«¬†eYÔZbʺ4ðÚúÊÕZºn©®¥®²_ûplE^ËQ=£ˆõ|8Èp‡Ñþs þ[1*ÝC4ˆðM †Ó­¢ô`¯£MøG$nAÒ+ ™¾3£ŸÂ~PK]V¤pàTUní[ÊìßÃþ>mh¦»7ä6+-¼ÇÜh¿‰%E¬÷AöÆ^+Ót‹˜év¯Þ¢»Û]Ûi±km©iƒ¾Ôi±=»C¶hÓ{Õ³ÛoºgÍØœ¬ƒ½{È«¦6GéÁ€wƒíNÛvƒ,fæÆ‹‹ÞOE­ªCš0.Ô½=÷ü*jîF,Ä‹jäA„Žp#:Àí¡æÔ¯áJÂgYldNEŸ´&¡ÓháÐõïÒžè“Ǭƻڽª¤Îðô§>ÞzCáÅFGôè2}ýxËendstream endobj 1387 0 obj<>/XObject<<>>>>/Annots 733 0 R>>endobj 1388 0 obj<>stream xÕWÉnÛ@ ½û+xtË–Qni=¤@õfÀËŠ«BKª%iþ¾äP²´Ž—)šÒPÔ|CŠþ9`ã¿€ÀéCœlËÆ'ËËíõ@…–žò-rž‹w–2¸ø¶å8–"¥t,¿“H)\EZ/ħ¨ ½¥DZc× %+=Û’ÀÒÒ®«lË¥Ž²B`‰íJáúC­òðÎiC‡”¶`¥ôÑ>KÆ®D7R)Fxˆ›%¶käÀåhðî4i ^i+Vú>ÚgÉØ¥<8¡‰Ôõ7Kl—ð:ÊáhBJ(KÆ®O[…ÏJéZA'­ìÚ’ƒA¼øª‘VvEˆtQòÊi?EH¦CdâE£ÕÆWÑ=²N,†¡„hn·!ЇWeÕTº8Š~¼> f3Ú7›‡P·³ªl›´H`¡§u¬³dZ•Oõd‹©>¼>NŽØÎ[ F‚b‹æÃ Øîú¿Ç2ÃõùônÍoZ4ìš{z?9ÙÙçüìì:+g:;¯*ý|vfRpk2—EÝ ¼oÀñZJ`äß¶‰ †æ{”>(ïqÖë¦JQ@ÐÖi±0o<&qSVdÇÚ!³’ê]¼‹Ó¸ÌÓ)݃QxI)»ý Jß ô„ ¡D›ÿ’S[ƒPXëx ^•lQV¹è˜-òí¥:êl­,l-©,:ÿžá/ìyIî¼lgYÇ;¡è,®Pì^¸§¹Ëà V%zQæm“p‘Šmà2Mú*Åš\–¦þ€Ò$Ó´¸O‹´yî«s—ôÙ°JÜ!ÅIÔ­9>ˆÂu ;“·™»ÏË4ü7â3Oæ©.Ö>–³Slë±ùnîPŒë9쿉۫q¸l²7û%¼@¼@ ¼ØË;ùì³²¹ßêSslWÇxýþ«Ínfã&îÚî†^cã)‹¤hžÒ:æŒ;O•$ iî¨iB £ƒÔébÎg¯²TãØÐ50¼·Yƒôòœa¬à¶ÜˆhaõdˆÌc{{Æ™Ž@ }×K 쇽ÿÉPgcŒÄô½ñ•êFD–«$N™ø‹ÁVôây ÌÀT÷:©àF­Îí¨ß0 ìÞÆgõË,É1#“‚ò!©t“âxE;\Í¿ûºù!=ºŒ_¿’!Cºendstream endobj 1389 0 obj<>/XObject<<>>>>/Annots 758 0 R>>endobj 1390 0 obj<>stream x½WYsÚH~çWô#& ëFò[’ŠýäªÝ ûRÔ M$%:X„ì¯ß¯g„À”ñ‘µmW¡éééûëÖ蟑E&þ-šÛäø#Ó0±3üüu3 BÃ&Ï Ÿ ²|ßz*§Ï#ß4<òGÀt]f*Š™–ë&¹¡£EÃ\M17´Átæ,Q6{jÐëø––´=#„Li½¬:n`¸,øxjйÊ_Ûó¡õÚЯ©A¯íò&˜ŽIMi½ì¯m÷¡*½šb®ò×ræ:Ôyh8PÀԠײÝÞ¨‡mM)½Þf—Dsà~X é6'?g¾NèòÚ#Ë¢Å7TÇã$‡-bU“ÑøºªÛZ”‹ï§SÂn/<¦¦[×U×f¥¤D¬ g¢\mD¥Ëq²Sy…Ÿ4›R²ZOiÍäZ“ÑTé§™Åõ]Äcú+Ž´âbZ0U¤ÙòB‡ðPüƒùó¾­²˜nÞ¯n•-âDؼ@è_¾öË4Û/U˜T`ïR ”ˆ´Q¤do‰Ù/“UñRF‘eheóHõ—¯ÏKö»wôx–¹"}¦¯®nòj-ò÷u-~]]i¨ý¡‘UeÓ¢wÎЄ¨Å äý—~õ€€þ¡æ¤×ª&oéG²:‡Œ×ðBáâ^„ÐÌæ™ŠŽÿX›®• µ©¤DUʲý™5’tcQõ ¼ZJRC'U{©&e !aq@ÓVu¯Ï.o‘qèÉ­b…A´€Ñž½Ë *Ä/llrI‚'Ú¸„,œXŽÅåú2Z^Ê胳uæð°G ¿3Py¢6­Ü¬ ±ë§)’xóyͦ=½óê=ä0ï'ÛçÁ4Wr˜jʃ¸êÖ¹¤Éó=yrÛŸk|6yË 9ÓmÊÑ{ü;È£.{Øå¢N$ÆG˜dDÈ ÚÊâ|7¢¥H”´–$âXž‚­­4´A?Ó Š~H¹ÉÊ„¤ˆR’¹,€m)QŸ>ÁmY•¥LD›måk£ë\örŒ@w»)ÌíV=v»<ï]§!g¿æà‘;¥Wì×~]Û¯Ù»ãr?ÓË'ãñþ×PFø{Žpü¤Ê‡^ûÝÕYcªloiî\úm7Î5Ù¿R÷W“V]s‹uŒQ]ôম¾£ A®«Ã>çkf#E¥¯Ýdz÷i÷Ù7žÊ{§tô7ΉºmóÖD]¹5³¿{LøîÍи¼úû»5ŸnààË øE¥/Y¤nY ýÝÈšnEÙ‰œ%g{ÙÜ ù´oØøNù¤GÛL½·«¬1Àp#c `s?ÀgNÏMÞú´ý9úÍuendstream endobj 1391 0 obj<>/XObject<<>>>>/Annots 769 0 R>>endobj 1392 0 obj<>stream x½VMsÛ6½ëWìÑq,F¤¨/ßÒNíé!3i­Þ2£Hˆb  ÒR}ßÔ‡•Æ®=nì A,vß¾·Äî·^LüÇ4Ih8¦¬ê ¢Þ~þ¼íÅé0Ðh’DSª(Œ¢a·Rtכ΢„Òñ¿ÅÓ$Š»oŽшÒtОŒ±št+Þ ~ÓAÜMb¶ +Þ¸›D)ܦi4î¼õËü¥-èÃMJqLóÒMÇÑl6¤yÞf2 yvA¯ü+µ§Ë庼¢ÜÔK%éÒy¹ùòîÝü+P$Ì~b&¬E1: èÇCd6Ï/~}ÿž‚ù#jøSætûñúúV™¥P­»ëkøIl? Ÿ­¿\´€„2WžZf´óçé’ŠÅòªL0¯¤á»c—Œ1Dýd ™¡²Z /ùµ$%l!ºª•/7àÕ¬HP#3o,㆑ð” MKI"Ïe~–ˆ70)Ýñˆ ‡u G÷RnJ]Ùš¤’•„„ð~b}æJ­e!|ÙÈ(õ¤®ÃÊÝë­ÐáÄSÒºziMíK-© –sQ‰m²ØE‘îv{EÛ-³¹Ý² Å¢i®š†_4MxmõÖb²ÏT©}”ºfuÞ×5¿gÈÉËjþyVŽ%Õ•ýâîœ.²@N[n¡î‘q…}Pu&èÑõKŸZçøîñ§U |u{â:,-{oгf%ºAŽÃ‚1^G/Ôæmî£äôB:—æÍÈ8ðÇ7ª °u®Þχò³ å?só}-ý_¤<[¢Ï6¾kÈ•ËÐÜÚÔ*ç&P;™Cat‰5_Ñ(°\šZç¤øfuRØlÝÞßnfš/Oýdîë1—ßµ—v%2îB†æëÒæôYX¿£;³òÂJ,³{ÞÄNÜɦm+›¯%ìØ¶zGr[:\î¹½‹Mç‚réÊBè ]ÍÕ´I,-?# •‹€EîZŸn#³R(0pÞò¼A¦påÌþ(.‚J”Úá–®Ñð„;ÄaZÂî@i…}YR÷C‹ÅR€¹¼\­‹öHò[ˆQ¦> ð88„‘„Ú™Äqð¦Ì%q'|:ÙH Oè¶PO:I{&Žºt3Úa.€*˜ï2¡ÿë-é BÓG¯—Jѽ6úH»*—VØ]Kv—¼P…ÄKLHÕ¦ö!=ˆÄ·å²æ"ªdep*˜HëžÎeÈ @ìáXZ”``šÓïÂ(mÙÊah1 4aô€€JÜÇÛOJ¨ZÕ:c-Ón¨Œ'ˆ§¿1J'锇Dzü夥OB×BñÉþþ@2˜±õ+>t’F“ñÎObvûÛ¼÷GïúANendstream endobj 1393 0 obj<>/XObject<<>>>>/Annots 794 0 R>>endobj 1394 0 obj<>stream xÍWmoâ8þί˜oGoKš„@é>°Õµª´»÷RVÚHÈ$ølÖvh¹_c; $ômÕê´»ítì™gfüÌ ß;øø?€8„þ’MÇ÷|üKýñ÷u'^ƒÑÐóaaya)åpÛ ü7„Az#Ôƒ‘9k¥#í`ŒgðîÀÇS+Y­µE毨}<å¤ÃÝ(ŒíÝ`á)'iƒýÕw­dµÑQöÃÐab¼ë$« B£õGÖ¯³ì¤ƒ6›¸*ËN²Z‹9ˆ‡%æ-9Éjm6‚aßavg­t¤Å,8Ìö®•Œöã “šäGXS¹‚‹«‚fVÉ”`<îÃ,µòa–tàJH- ‡³Ù?íêYhÌèãš®¨„¬à‰f‚Ê,”ÈwtÞ]-È9¬Ëù™3ó4Ž`Œ‘÷‚>Æ=K»—Àü;ºeßÞo8ÇšGÞ`ãìr ×ÓÅ­ƒƒH Br¿¼VÏz8àúðá=qM&×¹X’|*%ÙO&eÒÁ•AÝЯ¹ËãÅUT&¿1a½pˆiÀ$X ¨½Òt"ƒœqJ$Ðï1µQ&MWúxS4ü ~ƒNQîzp£!c‘ܳ<‡„à‡^S¸\‹œª»=d$ÑB²­]¢Ðè O1½™­Æ¡žªHªTVäçΜEjí•Pï™^7íÛ#qdŸLÈ2§ E ¶¢š¶‚97–90K [Ù§¯µÁ“˜žC yOdz±$ÉùT±TšiLˆàüÁ>0Ý‚x’pë¡ MpMwy¹5زóÍɇº•&ª'1Ã’UÄ´ªg{Aý’ð•¿­ä¹~ß&И&`;aù§ƒÏgºx­— –¿ì¸AÔÉC¿uýæG˜ì`U6’+ïHçs(ã«&Ú ÆfAxëuÁ—‡/­©(~å3«öVÒ„)ÓüÕ~³¡Z²¶B1ÍvRš1Î4… AÅCÛG›­0ï®± ÅÝÉzÒ`l™ŸY~ÚËë©Xœ—È?»°›”Íçj^T¬2ÃY™áúö¹7¾,µìª]'kœ(¥ÿÿ‰h%ˆ6ÓŠŠhGˆ,éÌ;é5wŠW TìKö™™÷úq*¶óît~öØD…%Q4¤ˆ1Ù6”WqOAH˜þ‚¼ÙRž"mM4Gý©4±%’ ÕpójñÄ–ÆkQåócÜ;¡X+Ê©$9HŠ޳Ñ›~†€;ݯ·é‚q GÛ%ûmûõsì7ü6 ô‘¿’ò/û<ªáGªÚ¦7e´Õ <*WØ`€ßäF}üþ‡?ý‘YƒÝR v«VðU!C>^Ü<‰^Ç^„z±?6§‡^n0ÅÇ!Uf­œ­nq©÷p+2KE1¹#+jmG^<¹oqhÌþ>ëüÕùXpÿendstream endobj 1395 0 obj<>/XObject<<>>>>/Annots 809 0 R>>endobj 1396 0 obj<>stream xÍVMsÛ6½ëWìQI,Z¤>(ùTÕ±=94umõæ DB4’PH¶òëûÐeMÜ™{F$`ñv÷½]üÝŠ©‹ÿ˜Ò„zCʪV7êâËþçᎿP˜F U”ðƒ’[ñ ŽFÔûQ“ñxÄKýÈÏÆ ¯îÆ~k<aUf{#ÞSQ2èÂR,'£Ä[Nzi4¤0:ìM†ã`ÙŸFG³ýayÔÃ3ñ#žýuzê£)èò¶OqLÓ‚1 £ñ¸GÓÜÇ¡KÓ¬éj¹rÒ’{–¤êµ4V’^ \¯æ¥¤¥‘™²J×T gÔ+­¬ª ¿üúY—Ò~ÛÐBdNõ]8^Ç»?Lÿj]Þ·G·éëë×ði‡¦ýÆ>Ïw©÷àú4oÛMUI˜ÑR[åÔZR.ªVNn‘œž19=Á”Ìá•G[”z.Jƈ  ¿¬¬æç ;A©v1Û:°GÑï5ÙU–Ik«’ä«rïxQeÉ‹ŽÜÊtíÄÑ6ÜQ°3Þç©“ AÄ`q0î嗻ǰf‡wk¦È›_A¥ša6´=ÂïÜè•S5һІ¬.ל;ëD “TÈZQªïˆ„‘Ð!ðRaÒn¬“•hòÆ?¿å öÙTKæ‚¥LÔ4—ãù•GÄ@w•%U-KY… J ª]QÈÎRXO²Jçò4€|¶/µq‚ù)2£­%ê -Káà(£½…¿Òìó—ÀLÞ+ÖB•b®Jå6àËî\rº™Ò£¼-D]êmê2*ôº™ŽvDw]f—…F$8˜;‡öÑ—!F4HKàek')]¬êŒÕä§¢„+WüäòåU¿e {í v5êßµgø7¨³\‰â‰iAÅÌòÏú‚$P>}x@>/XObject<<>>>>/Annots 832 0 R>>endobj 1398 0 obj<>stream x•W[sÛÆ~÷¯À£ÓZŒ(Q7¿¹9‰Of’Æ'VúäÏŠ\É[“»êîʲúëûÔeŶ9m2c qû€ »È©ÿ9M4SÙ\ô³>¾9þøz{‘òlJ£é8ëSCƒA‘{©¦û‹<dMx¦¡|Zð³"%ÚÑ,³í¤ß#‘X‹0Tä쯡b’Mö«±¡é NÛY—ˆˆ8œ½HÄD†vR »“i*s¶³³ ©Œl‡Å™6•¡M³Qây_ýŒjrÚñ5Ÿ²ú92m`©AÊãÛᘿlhgt²Mdô%—ŒŽÚT†¶àI<Ù¦2´¨ hŸl¹Á÷ã3ÛT†¶8·Meh§Ó³œ‹1ó¥Ä³?>Ó Šý)ž`„Y%‚u…3E¢g"æaÊ­8Ú%"|‚#@á¨Ìû}Ô=˜„‰Ì÷Ñ^’‰ [L]žj™{3ĤµžÄ6‘ï臵© -¶F7•¡1?Nžšc} xUØ_¼¯üŠÞ~È)Ïi¾ÄNã…5› i^É:ëÓ¼¼$¢ÎG¯,½™ÿÚÝuâÎÄÁ%…Í»M4VÓJ=VF­C¬.WêŠV/W¤_Týð¦õô÷©ä¼½zËr¹|‡øßÿÿâLE·7ÿ9F66"0¢·<óÃ?Ë£Ÿ$ñãÿ.‰ëëÛÚ-T}ã½Ú]_3÷Œ•Άˆ,ÏøAàá {çÅwÓ}û¡8ö«zŒPpõ‹Ÿ4…¨l¥|E—ÖÙÞJ[íUm~×ÕÃÒ2:°Ñ´önQ놼Žo]‘ªëýºŒÎ‚#’‡ÐcçÒ€$*”ÚVlê|¥}FsÄ7v½‰Ô¨èÍ+™@VXzr/Úo½‰Q[²ÎS¥Côn§«Œ½¾ý0ûsU`8ˆüÑFíÝU,Lm⎶pH_ØihMÿzìQˆç¼ŽnjÝú9©šœ$X›…WÞèpE TP9¤¦SiªŒ*­—¥*öEÀ°É˜dô«jÖµ¾"&” N£žùC]£IN€­}nÓ`/AÓ1þõÞÙ¡¹wïç÷åÃ%ž¢;0Q¡mWôþ@ƒÏ4w®~6Qº/¶Ñ,MIï\ƒN¨hœEçщ åÖ²¢ÅNF寝œE‚?ËCªîñI-œW˜„]F’;QฉšÜ’*!E¿)1A„ÇeÏþ#:P4gŒq\+€]kП0¯Øβ›pL¹_­×µ)%·@«tݦ?Ñ m•Y.µçr!èß6í“}Œ¤¾Ûµ„ø|÷ñD nÏy£CP+Ý[«0ÀÈ¥kšÝǾ’ÂPúazÁ%Å÷ŽÅ÷¬Þ`E¯ ˜ GÚJÙZ…8¶AƒŸe­_M;ÇýÙÓNpî³Á†`\Q 决ÜwAæÿöFxƒ%‡‰·ä·Ïß>ýr ß¹˜sïÌ‹¦o }6+¸ˆŸdÖw22¿˜°áѪ9Éû¨µG¼¿›Nº_Ô3}5Õê4F”Î!«vtÜ2n™ŒK¯½uþs¨Ð.Á*0ÙfÓ€Ù€°ÈØÒù5W{µ©c/ºüµ%è ìV ¿ü© ôó@ô(ìËc#ôéÕ¬ƳvŸSÈèÐ/áÎßtèPnÚ#ÙmrÊøÆö þÓýâÕ}¿³å“wö€þ;g±kdú‚+mA_xùÉ,pnÝ0• p{ƒ»r´P|sH–f;C²y%5àKK#ý Ž)½v`)¯VcAìn`Ókœìwnm;Ð0¬µŽºÞ‘’3%]`X&g4b²1J·ÞïŒ~Ò¦I«ò‰;¿7R%À:11Ûvcb$;,& ʧ[MÍWtÆ.j§Šõ¢Äì gùð¢Ù@€BÔ|°0}V`™ "L`Í`ÙîøáµFN+J§°c{(*¿Ò©¸[a-­ä5^¹'‡ü×û˹}2XŽÇTQ°î¦Ž€Â¡MDÖ¸ ¥€oÂÓ1qÆâ{#ð¤À ƒŽ™Ç"t&oíмpM*všÌ©-ôÊØöëñ—(€×7<všÛòUeFò2Üáèq3·Oâ{ÙóN`¾–<©ª|Ó{ü›gÈ´!±P1ONÎ8œTE¿ò ,ÚáRß z…3ÔÈ%C -³¦û—|‚¿ ¦CüU>ÌŠBèzþFÑ.Ue±9Ù²w0èMú³–Üû—ö]\š?¼”Ýávíèþ°ïPΜ€wOÆS¬,‡IÁnßÏ/þwñ'Èendstream endobj 1399 0 obj<>/XObject<<>>>>/Annots 839 0 R>>endobj 1400 0 obj<>stream x¥UËnÛ0¼û+öè ±"Ù²,û–MNAÑF½(‰’ØP¤KR6ܯï.-?¢M6`„]ÎìÎl¾"ñÁb ³Šv!î¾Ü¢xLa&A -L§q÷+ £tŽÛñr$x%ó ìW§Ã4ÄŒœ‡tÕ¯èðC6|ÌÔp}CAV!ªyšËå ²Ò !+ÆB9n“`wªhŒVâsB+Xk<²PèN–sh¹©y Úßp†·zƒkQAg¹‚)¨;fæãàæð‡C-uŽÉ™1lw‘}…0‰fÈ>+Ç%s „¡üÍB++¬ãÊuÌñ2Œ—"7Ìì@W7¾#%Tˆ‚²û—.E+ù]ÃËN•dÈi g˜3K¼4Â^7HåEé­äeÍñUŸ­×R¾,AŸcš` a¬:UPÉüQ0¥nÇ(j¹o@Ô7ÀŸ"ï´qX­}Ð+‰ø Là»6°]ntç„Âr²ç–Ù—gjÖÓxm„6þo¿{µÖÖ–Oï:kÆ-àç},-J¸¿y~8@•Àˆßû0——fµº÷"»!­VTŸG_ž·¡ÚÑÜ«ð¨Å·*wîœc×2 è57{“Øæ`”b [áÔ¨ÔŽ¤S0Ã)Ò–ÜÁ¶A×hÏÔ„î*94Ì¢Áð´äyW“É(Ê7ã䖣ܷÂ6ܪÞu¨E8舖©]óÀ^„ªN¡kù Ñ=ÄA¿`¾m9°}«oWLZ¥°,—ô4’N‹R#–Þó9¯…R‹P cò”’£I6x‰ÚtVB24Rf=ßûû¯8ðˆv—ÌÈÝ´¿§›ïˆz§ö°_÷ùŒ7Öɳ0ÿcÎ\•=aªÎ?’ö­ 84Ξë»ô0K‹ Ng0›á4 SE{€·…¯4”˜ê˜¤Œ“¨˜,Â%ݦùE69ù·Z9£%MäÛSÃ>Ôn)Q¼ˆƒE’âL²˜ÓÖÇlôyôÞ,Dendstream endobj 1401 0 obj<>/XObject<<>>>>/Annots 856 0 R>>endobj 1402 0 obj<>stream xµVÛnã6}÷WÌÛ:]YÑÍ’í·´[ -ºuŸ6‹€–h™^ITI:ûõ¡®vãÂ@vD‘œ9sxf†M|ððχ$€0†´œx®‡oúŸ/“À‹Ü¢`áΡ„ ñúQt³þÜ i6Ž\";¢Yß›Ój/p}œõ¿ ³ábá.iï>/XObject<<>>>>/Annots 881 0 R>>endobj 1404 0 obj<>stream xÍWmoÚHþί˜ä Æo؆o\rD•®']~*U´ØkØÈ±¹µIš3³^lé©§œÚDBŒçeÏÌ3³ü=ðÀÅb‚Ò‡ë¸øäøññvàÅžA8 ðóü8vüV*à/«M\'d­K¶,±6HHŽÈ}Ã|YêiC×IPx,„,u‘ƒ01‘ƒ#©§ ¦&rà¡oÀR9ð}|Šçº!ù²ÔiýÙŸ¢vFØÔEöâˆQÌFêi£6ŒÊc©‹ìMÛlx¢3RO‹>œYŒè<–Hûëê<ùz “ež«œÊ3M"g6 `•q…\X¥Ã\•4; ™ª­6‡FU%T9ˆ„ÖâyO;•î`/tCÏÙV4‚¿¤¢(T¹…½®RY×P=•W«ûÁÄèÙÐs€dÇ^€¨ñ‘HÙÒ†91÷_šW{©E#Aµ.rZŠlò¤U#O܃—îZRÔ’}íÉ•$N¼Cã=ö§0ö#,)‚]abòC™RRȸmì¨mqN°×&Ø›aÁžåøÆ8ødb¡ˆè0\VºÑ¢Î2†Ã¡%×kP6ºÂ‚”Ê­¸ëWh=ÜÞ‰(JüŠj;µ¾ú7ˆnWÿmñ}ž"|j§FpOßî;¨çÉ™,m6{P¯{¯õZªàø÷X© þ¸]ÜÝôz=Te&eø¥M=+ªÏ_\ÙÐ!cSCÛª;õù‹MêëHO}®ß½û¸ öv1ŸßÕF "Ü|~ZhByøX2ëw¼ÇDÚn&®#G {p‹¼2ŒÞòù†ñLÙÉò؈˜6:¯ 9lv¢9rÞèŽÖ õÌžæBmèÕ‹{É2ű³‘8|žáQXI;YÞß8´÷6?7ÍliYièH]•ɯÈÎÌ6wïÅáÑk÷·gæ ¸—d¼ôõnïý¼\ð8c䑆ÜÃ#`bü²oôˆª~ÆKæmöc¸isÉììè¾—_ƒF v‚Ý‘©×ÑØë*“5˜C ©p<ኅŒö­2°ÞËTåJ"5Dƒ{¹%0ïjâ*6ÏLð³åì1暀H‹”wg¯œ[õˆGÓ’ÝWlCç³Hçïvwu1 çÅpD–iº Š)ë!4Q¦r}¹®(nKg;lµÌ¥–h6Äç J{LË^+º|XÀ†¶ykA7~ïõÕO=ÚKÈÉtøÁ#áÒË™`áýŸƒà£¹›õ&Áéø> o°wI¹„c²LìEoŠ?%’¦1þðpº'šõ ¼¿køTK Dy…ø›'D‡qìÎÈ:v<øˆ­šgx_æ•~ö†Æ¡G‰¹>Ç1ùÿ¶ü9ø}n‰endstream endobj 1405 0 obj<>/XObject<<>>>>/Annots 902 0 R>>endobj 1406 0 obj<>stream x­WßSã6~ç¯ØÇp—„ØÎ/x£\ÉÜL{3½æžŽ›Œb‹XÔ±RË!ÇßoWvâ()S˜ËÒj¿Ýýö“ü÷Y@ü4 )S¼>ôx³ÿóuvL‚þ˜F£Kü]Sx9è«QFÖ³Ã1Þbvõ§4’‘ÌFS¶¢~ȳ£ Ûʨ1Âf£hÌke$³£{ É÷+¶~$³Aˆ=‡ÁÐû ù¿5føöÊHf'S¼‡!ö¯#ò#™ÌaXÅ;`•5f±#GEŒ=”Ïþ2o'°XÑÅí‚€æ÷Èôh:î_^F4O$ɚǕ'çó‡Úp\¥_ ƒÊ0¸ˆ^ÈIž'¼÷É„¼vˆúq½`йµEY¨¼±]]MÙ+G‡Èm—…Ý–&×”¯Ô¢Ð™VN/¶›D•ú®³Z¨.e¶K©¹;ol÷"ºõΠ…ÿ/´×†fRÓ¥~z8 l§äâ¶ÎaåM#¢×DûŸGkú2»^|m%Éä%I¢ø!³ßžRóýG´Óà|üø œÙõÕÕ,³K•]…zººª ø­ªßkˆ\ìsª²w¤âX;G¥%E+ñ@Š]ôižjº·ÅZätIÆÑÖé„v©Î©Ä˜£h§Z%dóì‰@m™ÊTY›IÀÞ<ßÄãÀþpI»Â€ª«~eU7‚ÏI €p9*¦ÖƒŽóy:p¨"–k“T¨·ÖejŠ·E¡ó’¹ø¨L¦–™¸¢98%ü\¥ÞcežÅ̹&ùÑ©Bg«GM‡µ- ÿÑàCöeiõD^ˆ™cV2ù¸›°tÏQ<;³¾‚͹›¦‡U*ãÖzjûÕ.©uÒ%ý3Ö›Òƒ5%±Ü ºÊ!L‰þIwEÑuqw^û±…Y™\z^3« ÉJu–ÐF•qÊ3ÍTH‚êF¬k"Áˆmedò8Û&šóÖÌ¢ôÑ=‡³3HþR“”ºˆí #úÓŠü£Fz}Ó‹Ôñ3ˆMúä ‚ƒ¬uîöPí /!w¡i‘7ƒhyVIE²9ö;ðÁu9<äÕÜXç “…ÔKêÒ–©äãÑø)! ‹ç¨!µ{ÙllÝò¿užºðdreU³ªÉœvVœg0ûó6²kÿTµË{ ½KñžÕ©’<”9.à¶‘¼-MùñØëã[®¬íKÎþ§½wã]çHŸâ²úæ,ºì¤¥?r´&§ñþ¬¨{äæd½dØ/k¦G?ã¦pû; éf}”ÎÁ 9¹?˜hÞ¥Šÿ¢©h‹‰7Ç·ÌsYE™YSµŠÓ“÷»ÖÔì„.¡ÜN/*ÎyˆéýSín©¡d9ª¦òŠ$mV56_H2Ý>\>d|`S\…Þe B½0ÿ‹TˆÜ4õ¢) þêä·‡¬V—Œƒ—[QÝB'Û#¦|ˆÎ¼]Ëň¾9\Ý~W90úv¨ z“Á%¯žôúÏ lMŸjÄ‹‡“a2žâˇNùÕ¯ó³?Îþ7¤endstream endobj 1407 0 obj<>/XObject<<>>>>/Annots 921 0 R>>endobj 1408 0 obj<>stream xÅVKã6 ¾çWð˜Å&Že;Ï[ÚÝ æÐŶ뢗ÅVul)+ٓο/)ù‘I±AÑÅÎF(‘"ù‘Ô§¯#!þ3XF/ «FaâJÿùínÄÖ,H ‰–Á*ˆæa°j¥¾ŒX8Çõ„%¨S[̃°•®vCDƒ­“Ü®;9NVÎÖŸì¥Á6ŽçÎ֟쥫Ý(BU’ Vì$·;ge”Dø¥˜—èÁKn—E¨EkŸQD±{éj—-\Tq£Vä$ÚEh€!”-µmý”Þ¢g ˜í` Ò#Â<_-‚õ:†4w‡f㋬O ë“0p·…c£²Zjeƒw韣ÙnÝÛNç”Kš— |6:Ö½:jSq²ñƒ·iD Å'¡ O¢Ôg© àp6º0¼š€V@ ‘[¨54V@vâ†×ÂH[ËÌ‚>‚¬-œq±,E B=I£U%T½:ÇGña'}¡B˜²aA¿WÏŠ°À€‹6B­ áræ*‡Ë‰×€’4pÿÁéMàzµÏºÔÅ3EÓºÄóJ­¡”"€T߸æÊ^Q<SúÚLAÐuYê EÑ£ Wp@îÇ,:ôR2iËâ¼ H‚m@SâJÌÚ2±5Ö¨·ÜiSîê2 ”ûåŒð×c©jQ`¼(øƒÒ¹ù~¼÷š×éæ?{å3|ë ÜúÄŽ{øôf7×^Þ¿£ïe³¹Û~æIb·l6/3dú>#êÆ(lL,ŽÆÐkÔ´˜5Æ`v;¾i\…D¹(+ûýËõf7ÿk¹úľY­Z”¥¯•jªö2)ÓÕ¹©iÒú©ýÓe›ƒÑ‡ ¹¢Pºe?.øÄEç¾üfºü^#÷¤eŸpè>¿Œ†¤xÀ@èÅçeZ›\*¼íëý§r»€î¶8ž¥>ðrk Þl(‚´½r÷ãWbúfCäY¤BÐ}W\%Óͯ=‹L¥È»öÐp£$S7¼¼Ú( b—iœx{Ö a!!]w ¤“#qyÀrkEŽ8å¤4Ûõ7.‚íW:¶»¾œíßv÷ÿÇ¿xu.ÅæÆ¶ ‡ZëBZè}cJè}=„o‘£ðÆ"d‘µµñlè'•FWø>À„VÀ-XäR…Pܤ殶¼dÑä¾û£cñ WÈÓxÂQ"¥b»û+rh!£÷ËGz‡çï>êŽÝ+å°ÞL× dž~oÁëô•Ù£ óÝ>tÖ.²,‰ ='´$=Û­:Þ]âoã+ANvÏ$ßÑàZÚÂïo¤_¸Âž"‡SÖL—áúÕGU²L‚åb…¢çךø˜Ž~ý Üqendstream endobj 1409 0 obj<>/XObject<>>>/Annots 934 0 R>>endobj 1410 0 obj<>stream xÅ•ßOÛ0ÇßóW|‹ nœ„¦íƒñÀ#{CBnâ¶Ùì´ÿ~g'ôC¤ik¥(—óÝ÷|þäòp„ôçH#ÄcäuðÇlÚ=öw<³c$ã„q·`tAkÏ4®ƒ…¼¹|;ø”³qâjÄaÌÆ½Uá&àá±³)iLÞˆ§lÒ[;Þ($òNS"ö–÷úÌQ”²t“¹³¶±O¼n—¹³v¼!ït}æÈ[Îû){¹³Âh>çÈ–Ô‡$ž²Éx‚¬ð› ‘僔qá´jm# .ÔR›Z4¥VÙ`4O6ÁÃhLÉŠA¶–XêªÒO¥ZaÙªÜ-·È…ÂB¢µ²@£¡(ÊmFTåO ÕÖ RÒK(]H‹f-ºHܽ2¢FiaZ¥(¹+!ÄÇT! kuäcpqæÂû˜\Z{¡ hzb÷[ÙEióÖºÚ’JŸõ¹÷6ÕoÅ»XäˆHˆ(‡…o$ï{Á©‹Ø´c®Mc„o×– çƒ(ïþÔŠF®¨Âç–a%îújï”oÆíàöà-õáNGN»Å{ð¾T…ÿ‘6ÎOîúsþ€Ü®Úáá;õ:µÙìüäFšÇ’Žh6ëwúeo£[À†û]5²i Aå¹Ñ¨> ´bøªpsyaòuÙÈœÒIâ¤Y^OeU9T+B‡žô‡·š|hI“0~­"ÕQ§e¾![2÷nüOŒè•*‹‡Ñ»åþ:F{ÊïÂÈ÷çèxB¤Y£Ö†è#>(?ŸºSÇ=½Ä.Û¬|”FT=σªçƒ†[-~¡CÙƒdEMÐÕQo¡3šOžçMš²dã8XûQx^éñybŒøeñÝRA—B²®’!ï†i8uƒÓÍxŽ«ŽÚ—3>I–ÒG¾Å`ºŸ³à:ø øè±endstream endobj 1411 0 obj<>/XObject<<>>>>/Annots 947 0 R>>endobj 1412 0 obj<>stream x½UQoÚ0~çWÜ#¨Åµ“@ÒXUª=¬êÖì ©2Áo!éì¤Ýþýîì$P¦jfEckeË'Sev5ÔeµQ«‘G{»š1‡±±þt3¼>#5¸·‹Çë¿rÖXÛYyÓ^\œ™Ø§ÏoÊ<ëLÙù¼å}×Ó~UÂÕ2je;ÝÇÁG¹U7¦´Pï”Í~jV9ˆ²¶2ä³Ô…\ P_âä™1W*ô8)žïÚð/­_Wð>í¦Šõ¦m÷%h²ÿSש[É1=ÎÅ™%¼÷Ü÷B¼UÍY³@¤Ü€Þ€ÄQÈ´¬Õ^t½;åÆÍNQe²8Ì r0ø”Ç串»vvº}ësÙKÂ*à³,œ¹ÜÃ@¡•…µª_ú¹K{7n ¯–Óv¶QËn®o~ÉýS¡æ”ê@x+ÆzÑ¡` Jf;ò·+Â(ø»ÌžE0XXÛìô§Ñ×í?¡Fò„G]œ@]ˆ§FXiÕüh÷hÇì‡à²U uÄÚPuÜL•ÁWÈj˜ã¯ò }Ê]å'¹[Y‘2"ðÕ#O+×íVsŠŠœ°nËÙ!ZìmQ­±»x)ÿ ŽR'в»êÅ_ׇ;a±dÈõ¶!)QYØÊ=ó£âÀVÓ‚»‚iÆÚóT ÓL±îZ§ÜóQŸ—àáSîñ±Õ¶‘¤{íÄ1‹’Bz‘ò„îÀ[OoaŒümá›E¥?˲‘ŽE{`óEÇL0÷~?°‹Ø‹½¤·EGqÄâi‚ÂÈDУ›tðeð‡ã>¯endstream endobj 1413 0 obj<>/XObject<>>>/Annots 956 0 R>>endobj 1414 0 obj<>stream x•TÁrÚ0½û+öh&EX¶ÁÀ¶rÈ! ^˜a„-ƒ:¶d†~}W²M줓¦0ƒY¯v÷½Ý}:9<üRˆ|&çÎ ÆtL&öµïyd C’‚Y„ÿñÀhFðU£ãƒo?ßWŽQ2?Šˆ9ÞŒD•Á“ÓµÑ;‰×õvlôN_2™X:£$ŸÒ&s€kËzý1zélŠsðƒ½µÕñNCĆ™éŸÔZÆûyóšˆ:Àh9`­MŠ–7 `“Xºlb×P|à¹TWXœ™ÈØ^dB_›_Îh‰Xh:ô±Zˆ‘îý™ ²:ñÁ!¯ƒsV°Ïy¡!‘¼„Bj8²3-aÏᙫTªœ'Åc]aµñUÉÕ'XerÏ2X(Å®¥)îá¼,ðÄ}Vò,iUÄZÈ¢49Ï\‰´ÎÐ `ø¤ŸÚp*³L^DqQLÌ䜛š¸9†§¥ùM^ ¯âcËïÈJäÔoÒbg˜)f_jüÌâ'&Ù[‡N\JÀØhÞ½N.JÈxªš‚&'b™BÁ/¶Kïä÷ßæÿyäÈTuç#Š>FˆeŽcJ•ÌͱvjBcë>,¶ƒ>“àm¥5+q`Å2‘ mÛi™˜f7³©J\޶k½Y÷+„u…¡rm'±13kFo7ƒøF®!ÊÝhÖn9m•ÎP·È¥TZ±¢êiÜa»Ý.à&h~À†µuàÀv¢8U¸«»šÂÖÝþU}ØYÚ/¨ öSŠß|§aµØ­ÿ»b·àÝÝKÞ Îç«ÅWgór>oøÖ›ÐÒ}¹†ýÞ²¢¼pe§©P¥†SÅK#%k ŠëJ¥âðX.+¼p››mغ¸û«æåvPë m{òïzYÏðE@æpŒJ1*Fm#þR*»J£%^Šv¬¯ïpÀxâ“(ŠŒ{›?ðæVT,3¶ÃÈ›™Óï\a’h2­>õMøýÆytþŽ´ØVendstream endobj 1415 0 obj<>/XObject<<>>>>/Annots 977 0 R>>endobj 1416 0 obj<>stream xÅVÛnÛ8}÷WÌ£ƒV²n¶¤¼¹Á&(°F»­}1`Ðíp!‘)%u¿¾3¤ä[‘ô‚6&$çÌÌ™9äÃ(„?!¤Ä3(›QàøŸÃׇ»Q˜‡~ÓiîÏ (V Ga4%;™á7®FÉÁ:Yc?ÂÕ8FÇ0µ­’•ÌR·–úyoÐR|4ÚãvZ¯6¢$íÉhš"¦³ìj0ÅHã,Cïx6 ÖÉjšøÅ; ð7¶ÖÑs”“Œ7ˆñ×YdzQ¹\ÃÏ:ëdu–»¨òñ#kÑê›â²´z “ÛŠ r0Íf~žÇPT¶ü常ç°édÙ %¯ŠÿÐCDä$È1d=„½‡0Çh¼ˆx(ªñ­Ò­fý¡3Fí!t`aÇB¶|Ëõ¶lÕðFéýŠ=2Q/ÇË«ïa{x!qŒÈ7nó‹˜`ÿŒøÂW-ÜÍW‹ŸÄ;…{õêp××wó\?Š’›ëk—ëœReëšé©ñÎ+ˤyâÚ@‹Ü^*YÁCÇ Qä¼mAó¶ÓÒm`êd j–c!a½o¹Y^AÍ7-l”Vתdä‚¶JþÛZ­Ym3;––iÍöp—xJÈ-ì´ÂDŒÒ~¿}èWU˜;çJ/Ç‹9¢  j±ÖLï N«®’SÀ¬…R5;- š ª½d(‡Øû(1^й3›×Aö÷pO#>óÊ«™ÜvlËívµpyú¤éÙ ð¡«Û•÷'¡®yÉ”r\^`W 㖪ţ»NÀ3Xªn †ä aäÖæ4B,0RÅ%ßdI«S¦z/æ¨Bš–³ŠjI±)] IµmlÁ¨¡€€6šÛî:|Ȩ?È?ãȆ3ìÌc1'jEűX:þÓJP«-²SpH °àfÕ°a*^ _”R€ç—†ßÁœO>å†ÑëÛ³ßòº6ðtÏ‘\mîg'ùÎŽ)ÌÝ`âì o¶U¨.çÎ öèå´þ_ »dVµhDË«¡ŒhW”Ÿûm|»Lÿ>OôYÚßÊŠ´ «BXÒNÃQÏå ‰!aû¦^”èÉm{ß‹2|dàÅ™ú1¶Œ!‡Þ{f¬Ö|Ò¨Hö¶QðW]É'o´bUÉL ïpÅ^†é˜ùœù™}œ7h¯'(òò‘KÁéj:8±ª¾¼O´…C1Þ2]Õ× oØã6Æ]c•\ˆ" @3QÕt'%‰ç“hï¿×ù“ÛlxÓ¤ø´Ëb˜Î°“Œªtžʆ†“»+½°?à¥AþjšàÃ0eÈ"g1¥øW1úgô²%uendstream endobj 1417 0 obj<>/XObject<<>>>>/Annots 996 0 R>>endobj 1418 0 obj<>stream x¥VQsâ6~çWìÛ‘d Þ¸¶dîášé…ÎÜLéÜ[·¶ÅI6¹üû~’lc¸I6É"¯´»ß·ß®üµÇh„_F3ŸÆSŠòÞÈáIûñé¡Ç&Ì iN½åä3æùõ*£§c¾YÏ|ìɉ͘7­Wëdާ8;™yMìÊZ­ç á)ÎÎCDp«Žu:ƒÿÖjW'ÏÁ$°Y±InÕ±#—•³ÚUÇêÏ\VA€]u¬,°YùÎjWÆú~ Š|CQÞ OjG÷+FŒÑ:ˆš«ù|LëØ29¢uÔ_IU*^ܬÿº¤Øž‡/{¾O¤«­’U™‚vüËVÅ‘.7ýòå ´­’e¢°ßJÊrsã<¾žÑpDC6ÂuÜÿéŠðdŽ2éaùå}Þ®omØ´(ÛÌÿ×¥ÑÍâîîÊ<ê,‹‡å“PÇ4z±h¹*¥ûUP3;´¥úS”T€cG\—š%s:( ïÚ¢¡RÏ2’å^¨Æ"4qʱ…ï„©C“L »rOÐýª­¢+‘{Ø$Ð÷,jjSXï%U•©,´cäõ2žgÿÕ”îä¡ÑÒ·ÂÚJ¨¦§QŠSû<düª‹öÅtøÁFÍd±£oü9 #Œæ¹‚à+eûðÛ^ì‘У6— Ö:»–´sx\`P¢n,Ô`‹ÄiÏ‹8= c ©z™ä/h'ÊS>ŸH•£½$Ä«+ô%·ÊµàÁµƒ½à9^7r‘KõbÈŽ¡sKWÌKNîeÑ›‡)¦…ÕõYç7#(¤½È†œDf™|6ܵ÷ž¥©Õ>ÒÂæÝjæÂ1^/Ç^„F?g%§ß5ªõ‘ÏÌÉ!« g£ù÷j f7›†®ÇÂÀœøeÝû­÷Þz`endstream endobj 1419 0 obj<>/XObject<<>>>>/Annots 1017 0 R>>endobj 1420 0 obj<>stream x½VÉnÛ0½û+æè,’Eíö-Më ‡&]ÜS]Œ$;*,Ê¡¨ùû‡Úœ¥N4 `h8œ…óÞ£t;bàà?ƒÈ/„¤9¶ƒ+ÝÏ׋‹bÛ… m pÇŽk ßZoäâjç%‹¼^l‡SüE/c:Y¯çá*z§VÈ"oÀ0§ïëzmfc‘—ÅÚëFM樳^æÛ>Æzއ»|²úÌlŠÇÔ™½Æ2±.öâê²0À]ÆxCQwaF–ö¾[´ ›)Ê Læ ƒÅ‹`N=ÉéÔƒEJsv`‘Œ…õþhñ c]æcAƺ!æÇ-éx^J%¹0Ûö0¢B%ÆU}-ËZå"±á«\Üֹ̖ãÍŠŸ‚ºße§ Ò¼8ü©–GƒŒO6mQÓ(aî·ŠM2jÕm[}8£~¾Z=­ØÃaA÷wWæ)\^œ­>¶Ë…š~86“£G3>|Ô,Bµ›Ꟈ{Ý:?9ù‡f©Õ‹³Ùìb[^óí™”ü~6ë€~m»“9r™Xä€%â-3UKê&ƒ”+N‚rM \>.RàÛª4›ò"U^Šjo—MÇëÓ.0ߺ‰ÂæäÏÃjFE4|­z¬/Z1Pÿf¡‘ÁóMX8“V/'VrÃå1ô¤jªk„Y‡Y2,úbŠ´EŸæÇ%ß”îoXÜy‹¬aªGöW4¯ð®I¸0,hxH‘EV”ò®³\l ®²~çê4â“ywg Ð1Æß²q¼§y•Ô•Žç×å]¶<">MæÓÇ„ÅoªÏ²‘gÄ©n€ð¯`'Ë»<íùX*q±I€ïÅN I)T†Ø5üßüLݲ…ê¡ËšÙtD4;Î B9ì¸Jnt–g3¸3h VŠ«š”E½C¹Ë$תöê{£9¾Š‚#X–o·e•§–ð~°o‚-7èþör¥ùÓKë°J^§Íæ]U–ãÿ«É§¯ìæÄH»”ˆ?’ßå5ÍÒ†"å2ÕrÝÕÊ –y†¼‚u) ®ëF2qû1¡Rb‚¿°œX¿ÛYœ'à{•IøÄEÍ·†\M€9S½[ú\a’èãG¾…±á舋їÑjt´£endstream endobj 1421 0 obj<>/XObject<<>>>>/Annots 1036 0 R>>endobj 1422 0 obj<>stream xÅVM“â6½ó+ºöÄÔ‚±37ò•C²CN!E [€6¶E$™Yòëó$ÛØ3µSÃÖÖ&3U”äVw?u÷ëÖß½€|ü4 i<¥$ïùž/ן«^0 ¼˜&“¹7¥œÂ‰ÝeôÐ ‚Ð iM½ÈJƒàºëHÇc|…tîãÔÄíœÔYŽ‚¨Ò"œªv­nä[‹Ðõ#ø¯v­tÇžo¥ñ¨ª]k9 kÌÎoµkuàÆìnTí:R¿Â<Oá7t;+ýaý¦uêÂèÓ:é/¨4"æBû²HŒ M'%Ï"å)‰µ( ¥Ì0™#§3Éq@G‘qúný ŽC›› ³ rŽƒÚq0G†¡½Ò:í/¥2Š•Ò“„:%phûDºÜ) t§âÀ¶ÇÖùÞô[6 Lè(@ȹlî^2ôiØØÆ7 ÷w–"¥ßV‹í{ˆ€‰a™üãÏ¢c§YÝ ª‹éíÛQ9L«Åýý*“;–-”b—û{¡÷69›þ °¬ýa•&] Ýj¹fì°‚¸@òéOÄþBŒöRåÌ” Òt* múš²>FËk"+ûÕǦû¨+YðÍŠˆ+¶êkí²"%%õÈÅ¥Í^"³2/41èflÇ3žHª À-þmaÿÕ‚O¥Fy—ù‰X–9$0—óÂh’{ì-lL­Š­~m€Š©æÒÏ="H¸ ±Œªâ#ŠƒWŸjH°††oNô}ése°½éWª\™r³³o¤À⫳H¸®ðнd[ÑuÙ7)pdÑ.£G"›X#´‰•«ÜÙvÈv.Ÿ–b$O\áDéá€SNP5Ȱm™€ mH²2Å'›XL®+Š2ß?(*«×U=­”(Î ¥å) VJ¹6J^tà ®Ä†Äp…OÌòp€98K·XláõY%¶X­`´ ⺵g^ŒÞ\7ÂM8Õ”mØéäWØF–Ëe‰þðnäò Ú×ó7@4saÄ™wBôE·:iÿEŸ ¨—¼½àɆ2“Ä.Ë.ˆI. ú‚DÀlª¾GÎ>‹¼Ì)‡Iu¡ÖÐ(ª)YP]7Ì–o›Ã›£8‡È¡¢œ©¿Þ|•óa{Á¼­˜ÿ«M¤ ;Ì^Y¸Yûß4Œ§në ûºënû°ánŸˆnõüYÑ™ê_ž ?=¹}åÿ+Úˆ+êÞÅ£_ é#æjât²Ý¢>dUu¨ûׯ͊Ѳ!r0›yQ<ÆxæMýØ2«z{hú]£ eEÉ2˜a£0œùs{êôÎ6Ç‘hy³iŒ‡"DñÔjü¼î}èý cÛ.¾endstream endobj 1423 0 obj<>/XObject<<>>>>/Annots 1049 0 R>>endobj 1424 0 obj<>stream xµTMÚ0½çWÌ‘í6!Îù¸ÑU¡—UÕ’ÞVB&1IÚ$ÞÚ U÷×wl6°BB­ hì™yóžŸýÓ"àâ—@ä¿€¼µ\ÇÅ•ÓÏ×µEBâÄÆ Ç…<Ÿà¿‰ØX„xŽaäaN $!N0F“Ý0qª6ŠT®Žô®î„éºØÃD¯µA V±–¬5Ñd׋Lç$ÂŽÔî‡ ‰xŠH€ìQÂ|E€Èö8æÈ(I|È Í×…,Ÿ­¸èíî²ï—Bèzì¥ëgrØ >ôuÇ ¤[9´-õ {š˜ØqÉžîL“ëCØ.ØÄGRY1{¸ôçÀëÖËíæ±îz¸u z#욦ë冉C3™¦Îo&˜¯‚Q*[km{ ô}˜+¡îöèÚ4<§=+€ AKÔPóÕI둚Y=¶AN;Ø1`u_1Ï xa‚;&3@ÂGðÈ k™³¦¡ãƒ<ï:ÉÍ*û¡Ëûšv¸~’çÿÖCyÅòÛŠvEƒ6*·ô=Èå*ÿ·•΀ÕYjð¼¢ÞÝ:Â?úªá;Ú,•ÒT ñiÔáíW½¥ëÐ[{@K@©[SQõÒYHóüüg س`’)R8Ð/›¶¥Ê&ûäL£Íe‹Zjü–IIK=W6Õ¾g…Ê(õ«bNçÃÒºÓ¨£‰ãヅ/gûà{êiŽÕMZ~Z3 ß$^‚GÚ ´Q6 ìÈMT6–ÃguS´ãƒ(p¢EŒ/?nÅ‘ªø˜Y_¬?—+‘¥endstream endobj 1425 0 obj<>/XObject<>>>/Annots 1052 0 R>>endobj 1426 0 obj<>stream xTMsÚ0¼ó+Þ‘ AþâÃpsRBs(MŠ{c†¶ÀêÈ–#Éþ}ŸdCZ¦v˜[¬ÞîÛ}Ò[/?LCˆ&•½7§>±‹¸<áSàÇd Qã3¼çÉ>Ixí! QׯoK»‘?%”F3woÖ½‡ôvƒ:€÷4ƒ`é&1 ÆŒ§1™ÅcHsWÙ‡4ë/“Á`ƒ<ð*çÕAÃ^*X ¹£¥èYߥ?z®XDB[nØÕ†‘O&Q„ûÈ_L9;µøEû0 '$Y”eƒZÉ#Ï™êxye˜ÚÓŒ‘phy©ã… nØÜà;EgšÀg¦p ¦`Ë÷óUø›5%« 5\V ÷àz²":pšó~aL=÷¼ÓéDX©©+Aòèáfíµ¬ÞºÀÞIaJ)’,'rw1‡"?AÏLÁ®áÂ@ÎËŒ8’Yßð¢«Õ–Kâ~øP±T´DG¬õþmû}Se¶=ž>/XObject<<>>>>>>endobj 1428 0 obj<>stream x­UÛnÚ@}ç+Fô ˜k¸¤i%šš‡>TqÕ‡ºBËzÀÛ®wézMB¢þ{gÍÕÐ$mU¸Ø3;çÌ93þQhBƒÞMèµ ÝÞú…†× ‹»3‡ú¨Í&ø3Š=ëw½Á  ~˜Å4Àç%8x)c²`a<„‡²ÿ­Ð€Z³íµ(#x\²$¡„4KÁ1yõwyRO™ÃVO%þÜÞlu½ŽCs™ƒÊ‚ˆcúŬР"–€Ò¢K ר0 DƒÒR›2”A¨ˆB,Sƒr,Ò1ÌR¤6€w›ÓT(…š{G”®–T’«*ÄÚ èé7ä–’Ý º³Tqw“ :õVH S†V©*2‚SÃÌ fŒ‹£ð4Áð¨ô­°‘N-°ÙŒ*6°ÂÍL›ØÑñÀeÔGX çt¯µú^×u­ïµáZ&v¿î˜Ã⣉…ÊþŸ¯³÷ž©QÇûYËÉ{ƒ¸F»Ê3-¥¾u8ˆ|G=¡[†ú|RÌÁ83dH²F×GÝc]õMñóór JAùéÖ©û¸üéÔxË3í8RHn†ô‚YF\LÊmj²ùC<ð#Añ 0àDÞ©°Ä#•4 ’µóiBÛ¾?ÃçJ´dò˪sóBˆDˆV¯%ð{¼Gè,²iôv´÷RÿÙ.˜Œ‡“è__N`©E¸÷±£îæ9¯˜—ô{ùåkqK# ¯¡”·s¾Õr çàûŸ*¥*sE£)5y5B¶¸¡ÚUÈ]?R‘Í¿¯Ã»ZPÂÿœSþ1Îë¥WŽÃÕs‹ïñx!—iˆp!4YYìEoŽDç¬Rñ¢â&bo¢~’3¡NUÍd¥åúp¿Wo<ÌÏñÚÎÕçäçnï]\@ñ=ÒÃgmdª˜µ¯>êoºÔìõ¼N¿ g½¶×éôÝÚËíø” LÑw@kÛ„Z¯1X/É=Î*¸tO6L\P§×ñzÝ>íR·EîÒ•_øXøN€-Ãendstream endobj 1429 0 obj<>/XObject<<>>>>>>endobj 1430 0 obj<>stream x¥TÁrÚ0½ó;œ`b ›ôD3I&‡NÚâôä‹(±%*R:ÓïJÂà!æ3fäÕêíêíîûÙ‰`ˆ¿’Œ§ÀªÎ ÑrúÓkû£iBƧ‰]ã«9¬:Ÿ³Îà.†(‚l… “tJf³1d…;?„Œõ õÜϯ¯3®+!iÍó^ÞÿÔÏž;C£1á)tþÖ4¸›·YØp”’©ÝNI ÷ó×{Á¸¹öžçðáhJÒØã@ËXI 5PñjÉ5¬v’ÕBIõ†ÖP(n€–%~qX—jIKP[®©÷É{RÉjM-sÞ‡R¼ð‹|Õ S3ð WJWðüÎ…U+Øú}n‚f ¢€¼࡚ëð苃d­¤øÝÀ´P*ö\„]jE FM`aŠãGä5 ˆçir*“åÉÑ´¸ýþãáævËgÎê·„žëbùI+n¶”qÇ%m¨ê68]PoŒW­[Ýs½ºöНÁ6À¨„%‡áÔ „Ü«nÙ¿¸Ö¿x')8Vë\ɼ‡ŒÿE«mɰ\ÙsAy+DJb(a Iû˜õEì <Û»Ír'0·M„hÇ«ãJXüS#Ѣж#qÛ9'퉸ˆìé(`¥UT^7§MH# G’휵¦ÒŒ«=Z‘E^]‘ ¸ X¹³f— vøJ”|p<–÷Éÿ‡l÷~æ¶åßvÅi̼›ózwÆpœìD84Ô™SZyðç‰ó ¥ÇŽ ’Z•BÈQ†ŽÉ7sï6[ ð(yh¹* 5¯Û]]ðAÐU–8#bÏáR > ýT‹RÔmm  5¸ïðаÅ32Bñ£„LÓÔ²Õ.£'ƒÊ÷…Ê--a”$$NÇ&ÙW×1*¨-É¡²€F°Q.]ñâ$&É4õõ˜ -ÔmÖùÖù ޜԅendstream endobj 1431 0 obj<>/XObject<>>>>>endobj 1432 0 obj<>stream x]TMsÚ0½ûWì‘Ì`ùê-&39dšL8æ"$jõA$9ÔýõÝ•Á% 3€åÝ}oßÛÕGQÁ?,k˜-@ØâfËëëYÃj˜7¬j5eSz?yZ,à‡‡×âû¦À3L¾Â&k¨Ö°ÙAµX³ù| s,·^Ía#sä6bô¬CðAI¸wñnó«È‰3„ÃÔò’[63ÖLñLŽÖÈéç§ ŸZúx$UQôÊzÁV E …y. mTÀáÐmƒ–ÀÇà¹8@òÔÑhÁ“É.‚„icRœ—*w¤Ž)èm{ <étЖ!ˆ]eÒrD9 žaFˆNýYe}è`çLÑnAè *uÒŸÈ`À@>/XObject<<>>>>/Annots 1061 0 R>>endobj 1434 0 obj<>stream xWÛnÛF}÷WÌK¥•i]-ÛoN}p“Öê›cE©MÈ]ew)EùúžY^,3ESÄŒÜë9gÆ_Φ4Á¿)­f4¿¤´:›$¼éýu6Ï’ Í–ódAÍ.ýSIgÓÉ2¹¤Ùb’Ìäë|Õ?|­p§¢ùTNÍâSü-Oç ÜÁÝÅ_›'ùúv= Ætq· é”Ö9¢^^]&××sZg1à ­ÓÑz«=m§3R»³*Ý^í” :­KåÊ#Õžóº¤Ü:‰MÉ•§Ã–“rXW;ë‚2‚%oË=“¢ÊfìT`òúgoÖŸÎ&t>#äu6jÍP¥Ì‘‚®ØÉ×p­<)#‡/î.ÛÀG¤6¤Ú6ºŒFðQr ’²£Q•N=¼Uˆg Ù|FwºDäµ)J¦T•b°»¶LlØG„•IRö¶*ड “« ‚ƒ£œTiS¨ÔQ¾ùZ…rÅÊ)*ØVÜqàÙîPý-ú·umÌ6^²8 åAÙ©µãlL›:vÀ]–¨¾¥Òš¢õ÷}užƒSŸ8 ÖůFù%©Ö8uÞÃÖÖÙÇP<ÒUÞ«²Ž1û„î€ ”Êsq &° Ã,íj¿% ÕF—: VLÑÎjà©AYÛ›a#^×¥íÇŽºTqF@€²8D®=§9%ôˆâæ„Ç” Î_µ¯Á®Í¨BèÁ$ÿ´ÒUµ+å%²÷hº— ”QåуG®‡Œ½j1š›~ï¼”P‹,‰Þ€­6Ï >³IZ˳Kh hõ ³À@âÔÑS¦óÉçÎV„fgÂ!‡#ðmÌ(SA¡þ[Èá`逻hªv>Œé×6[ÿЄ!©qEüT›4Òç Ãøà4PÚ¹¡-±lÝ]k°Ô$ìë(Š˜Û³óBæJ?w ÛAEc‚} @Щۦ E©Sei( ﹯ÂÕ¯ŒŠòo1ˆL´I­C îlü¶eR#hµ#UÖ!ñJ!ö}%ñÓ¥P"!†>’nÒÓÈ3GøªÝóÀm®‹ÚñÓ›†xþèCTd©·"`¹`2uµ‰°FB³¼EªÈ7š'E2¦ù,v$Ikç|+„c±ôûÛzü8¸F¤ ûº„öô°0:œp¿O«Õ_!  ,¡÷{6‘T¯Âµ0ŸÅ šðúhÇÈ^û÷ƒ¨Ü‹gTLW ºŽcF A£H-'¿ª†n42ÿ û!‰Ö³ö*Û##…ZBk‘ ½PŠÈÏ~(JgK艠=VæÓæhçEŽy ±¬Ñ´A~]ÿ[°v’&³À="’Üs“RØ\¥˜¸b[••…fáLÚ)¡Z#-¥˜É~G¡á‘c}q¼Á8Æs+FÍÈgå£T·DBÚ~.z@ÓdÃáÀhûëê rn9$ºéÞ5ëßbÈxÇ&‹’kºQƒJ¢Ê­¬œà^ít]¦o)[}qw­IöYŠÎÓäJ´óo:­»ý¤½,Ř“™lz ¬²îÅåjÚî(Ók9‡ _EÆ<„ÜÆ=æe5Œÿ‹·`!®d#J@j¡žw…³õî¹àðÜ4ãiôôæGŽÏO–ªwÍáWûèÐÅ­?ëñÔáo¿ýO—âðþöææþö‘Ý^Cnnš|ï94Uï²}ÙXÏûªÆ¢Fýλéâ8Ô2­mÑ{t¹Ýz÷]#e޽,8àN\Ýd„ɵԱVÖ¶ý1©—eµ(íC3µÅ²5Q„…Üß>¿‹ž}‚!Î2ÍËlXàšå¬ÁßOa¦ÂjÊ-\8{ϪÁÌÅÝUÍ%þĸšÓr…?Hê÷MBäžþöÀ߃2µ*%ñóéj•,pá|5¹nˆ1¥PÓ½æƒX¬Éêò ÜÖÌäÕûõÙŸgÿVÐ7Bendstream endobj 1435 0 obj<>/XObject<<>>>>/Annots 1078 0 R>>endobj 1436 0 obj<>stream xÅVMsÛ6½ëWìQžH4¿$R¾¹iäÉÁIë²§(£HHDB A%õ¿ï[€e×{2V–5±Ÿowùm¿€’¢9åû‘ïùxrú¹»þÌ›Ó,{>í) /ívýÑŸ&!žî)ò#–µ;{…Њç‰²nâãÔíÝx;Ëq )·;; èÀr0‡‡ØîË‘ŸZ¿a{1¼ónÐ ùž‚E nwvš†ÐATi­ÐîøtHÞ®šÃAaq&iÂkü7’¶£_2H‡ŒW 4H_. ʶð à ÜbQVXX}ÊòñÛ‹ìË~.—°b5ÇÔ}TÝÒÍõúV6;¹Þ«¦Ñ,Vc~¼[‹Õ…3ô|>Mƒ°fÅøí›7¯tëœ^]ÝTz#ªë¦÷WW{áös}¹Œ»ˆ§6Ùi8¦p–•Ê9n}lU-Éjê3 ÁF mîI…ªwÔ–’r]·²n é-I‘—NŠDÞhc¨Ö…4el¤9V-ÁE[ŠÖf5äÉ–¬öÉY®÷l“O¬c8À|É‚c”ΘDÌû^TÕ…ú®Š£¨NáÊ-ð·vlB<ëƒlD«tíÑõ¶•Í 8!QUÿÈ^À–üûœ’ÁW„ª’y«¾Kì½tÍü!À_ê¦mDýŠÛÆMË­wbm3ZÊ´Íú Ú¼\ G®ñSª `ÚLhÃÛM©^æÞôœ|¯ÆQžÉ÷áDùát¬Ÿ/Ö§ÏݲTÝ’½tLìioƒfÞ+ üOŸ_NÀ¡Úú7êçWF÷7î“I<‘€ËÈ?ŸÄP†€üA n5¸¤ë¾™†(%m‘ ²Í„ØIööWìUãÔ ªK{'jsäâqåIJ›Õ…g/y¨ÄžÔFìQx†™#Y_™’åòÐNP) ÿ)X¬÷á±÷ÞQ\¥­*QÛÒúQꪫèç\ÿOõ„rRæ¬_¿ªWÿ|½Ø÷çîeþÓñ ÌOφ¼”ùWt¿ó ô$é8¦Ñ¥tëÑlMmðÎe‡Àcî‚V•á)á$ýçÉQéÝýaŸ½\¦ýüN/N#¼±Ì0åmku#‘ìL4ô§oEî ·S˜&þ‚ñ“·‡ÅéÐÇ~FÖˆ“ØKæ)^ÁX:âGï²Ñ)!©Ôendstream endobj 1437 0 obj<>/XObject<<>>>>>>endobj 1438 0 obj<>stream x}VMsÛ6½ûWìÑ™‘I–eéèvêÔ‡6¾@ D¢­¨¿¾o’¦©$ãñ„ýzûö-¾]-hޝÝ/évM²¹úmw5/æ8~øŠ>?ni±¥Ýëyqw¿!|ÛÍíÊtsN;yýìT!8O_¼káÓîß«dy[,Ùö¦7¾Yo‹Ígåõb^,èë›òoZ²ÁŠ ¾>§›åºØ¬øÚ®VôŸ½0ôà½8ŠÎ™W©¼’ÊFs¦½R–Ô÷¨l©J\¡ÐÎGª²­È¶¥:h‹ ÎÒqȹJ9œ"/RÎ9ðôiK_Ø´ò¢ tpƸEäø×ó‰#>².ˆ³æ“ RXäJm@ÚNBŽ\ô§;) 3£ ­ÌÞB¶¾¤Ò©@ÖšʃhMDÙFyÛ3êÒ|Ó%nÉèáù‰Sö“¨]Ž”` FXQ5•Nµ–5ìÊÃ…k¹ikuÔð O}|m+RöM{gÙ4ÐIÇ òåIxE)»Ð|»-ÖÜÙ'K•²HØÌ.q°Øò¹­ûRsU ôÒ5-‘ÎFüæ,¸Ò5G£¢êêþq¹E½\¿,—·lrrÞ”­^>¡9‰D{`¼¡“ `-"q#…ŒÔ8$ ø£R™‚‰“°rVM ´­V¾èIÝ3ùd¬¤W"þ˜¡ÄŸ; ¶—:D¯÷-_³U@óQÆ(IÀ‚“IðÜb(‘C˜/(œCTMA¿ƒÞJFýÆ­G£¢v6`Ê”L‘aŽJꃖº2&¸I\¯8_ÉéÕ‹,;êq^¥K©yõ­Õ¨VØ3£Œ¸t𮛺6†ž©®‚0+ÜúItõ]ð)iä‡Ú¬¬ÁSý_*ç½°‚ž ÄŸÞ`ãâE—wê;ŸÄ í>"LH6ã››k~Ð:ÌVgÎmß'__k£6=Öì{ùçE>÷²V‚1¯yž´mÓŒiÿQžk˜ñt(ïÛ#w¦Ÿë»e±ùµbwsÊmµrFï½ðg •1£ùê…œQßFKÍRŸ'¤cÜlR¹AW—¼„‡‹`f•}3ûFGZ©u!I9ðçö‚×ȼQ²V‡f9É¡Š.«K Ö 3KJǾÅ\@¥²5‰‚î9!úþÐ:‰3·ˆa$ŸFø Ñ8P€iþ( ¤F17’8B¢xA =)»ýxò:bCbûôI~ܬ}€–o!œˆõˆ Xz.(>Œr.¯ ¿oÇ9œºúGµ£Ä!Ñw¸ø‹ ¼1QX¢tzQŒ–³xÚˆ=†zãY¸AÀŠ=~Ì…ñ"‹uZX&¨û¤i¡¢ð2ŒøÚcÌç÷ùÉ;ÌiA­GLÏ+3춪 O”ÇÛºSµrÖ½=Š@òùqݽ® M?ç³þMtM•sx3”J°†6âu*s¡O…çeÍH ãe•Êb]¨ À1¹ÓM,„§»í¼®´Åc,mÌ 6id°;½:Bñù ÁƒÞ-cëìC]¯$†_ò?üG§Ž=w”y¹ ï]¡¶—O}å§Ê›þØeæ}zbwsÌXÁXLyItb½h›¼–œ”-Ä.£Ò{ðÊ Mâ¶6­ý‘8ቖ¶7óङ`GlXá£=?n×$^’©¬\™›÷'òbyWlW·,¹Ï=)GïïÕýª¸_oð˜Æ…íŠÍÿØ]ýsõ?g4tendstream endobj 1439 0 obj<>/XObject<>>>>>endobj 1440 0 obj<>stream xuUMoã6¼ûW¼£ØŠäo÷¶Å6EÅb÷f`AI´ÌV"~ÄõþúΣd9V° Ää#gÞÌðùm’QŠßŒ¶ Zn¨h&o´H· /¦´O“íÉšVéÿbûùu³¤¯†¾O~?LÒ$EÙðÇVôü²¢,£Ã §×»M²ß/éPÆš”ÅÔ´Ò ¯Œv3r¡8“p䮺8[£ÕÏÛNn( á<ª„.Éø³´ôñ¬°’¬tÞªÂË’¼!”P)O"Ôž*kB›<þ™¤4Ï–`~(§¯ú-({¥SÐňŸÕ¡ÉäDÚ”ÒEܸŽOôúu8¬¦wQl[Yƒí»C·ÖÒ9cGè%:œ÷D…¨ëÛÑJü`HUÔ袰ÝŠ’™¥t2–¤€nšvªÒ‚e€˜Lù§´f/¿(Vº»äQ/û(Ãp„›åÖ•®À RË%É· †ñF°ŽŽSá\hø"æY˜¦­¥¨Ñsó.m-Ú–7 þ *‰ˆT\æÖr¬‘r¤øL#5OÑãç—uŸ¹Í.Ù®á8"»YîØtzQU°ò7æuÏæ”¾YU)œô¬l¶¹+ê )S3¸J#[K>>ïo_l’U¼š~õ3n'zïziÖ‹$2ûâ(a-gŽ´6ž¡ï!AaW…‡mk,èoˆré/Rê.ð.‰]_?+„FÝȘÀ½¡³Â´W*…t²¦!£ewï °áwÇÄØ. Æz~’·?ZáGÎs‡ïàkD¤c„o¨j“C|a­¸º¡ 1}'3vã]•L’[¿óBn±á6öBéèSäØ1>N‘q£:Bõ¥CŒÕ1ÈÔ)^p“W4þt1¶.»5„ååöLÊî~.ùKÖʃž`maqóû6~pv!¥üO#ʯƒ÷©×@Xx0ŒÎ†6ø8 Ø… (AúO;…onÄÒ U1ÿggǗΣ©T§2‰löz§ÁñË„³xÒ6²Tñ†À…ǧÙõÑ>–òn.¹3ë[k‰Õ† ψ –Πo!ýÚ´ÈfÐ?[×ÜŒ$-/ÃÀÅXÑE8o˜|üQ’ZåVໃycÞVå(Íem.ýÚõ#(Ûn“Õn‰/¿,Ù­6<(¯úÛÁο„ÆôdRóÛù6Ýsu–&}Ãü{Wò«í*Ùnv½ØÝ¯yéÃäûäÖ ‰þendstream endobj 1441 0 obj<>/XObject<<>>>>/Annots 1091 0 R>>endobj 1442 0 obj<>stream x¥UÉnÛ0½û+æ¨1£}é- š ‡m£90h‰–UÈ¢KRIÛ¯ï )Û²tµACrÞ›åqôu€ÿ²¢ªíÌg>®ŸîfyÌ|HRŸE°… ôY2ZÜϦ6îF9Ë&»QÌRH¢Œèš%ˆà òœ˜[(¢ÃAÚK3„‰’Œ…èWÈï Ú›˜H„‡“´û,€istLs Õôh"¨Ø“=" ,4¦èè,rœÚ”ãñì2@®”bß´71 6fñdóMy^lÕÀÕm [¹Æ¾$Xû¬ˆ ¬mK|(+/ Èo”à¦íèÅ4J;}Q~™]Ý&a ÖyF,Î tön¥2Š÷îH¼?âþÚÞˆF(X}eZÙC×;‹¸¬ˆD,¼®ÕætûC,.^` Y”fÄrCxö÷ŒYàîzùá˜V_9tz2`¡‚1$dA˜A¹i5T¼ëðÑÃJÀ E F‚‹8씬„ÖR¹¢€\[T÷¨doxÛSÙÌFÏj,‚]áJñï@1 .JǹëGíYæí  Q‹o¢ ÒcÉ(¢óÍæÊà%Ì z ô¾ÜÞ×€…·ÆXÏ¢±Ž®Ôv³áæ¸3Àendstream endobj 1443 0 obj<>/XObject<<>>>>/Annots 1112 0 R>>endobj 1444 0 obj<>stream x­UKÛ8 ¾çWð˜jùÙÇîvgÐÃbuoŽ­$*l)•dÌο_RJlÏãÐNÇФ¾¤>JßWüq)d[h‡UÂü2½þ½]•9K ¯r–Áý ìKéÏs±x|¤a»ê*mê/MÆ£¡hZÒ*îÃÁØ!hš½ã¤„Ž‘øå³}™Qm~´ÚQÛƒ@ªÓã°Ç½5‡åH<w–­:¨8´$´1ï—¼¡ò+]uopΟ“jàó'b%Àiá^ÑŒ‡±®çrÂË Ï~A²Géw—ÓånýFzý Ì× •e­±ošóA¾>åp<¿iÆOËË-Ã…`y™A‘ã]SâéÖ­o{³ozøhmóàà«C‘ÿÙè1jqs]°IИ|‘ÞÓ óèò!æ"gb[â ‹À• OÔ«VÿíÉ–Äendstream endobj 1445 0 obj<>/XObject<<>>>>/Annots 1131 0 R>>endobj 1446 0 obj<>stream x½VÛnã6}÷WÌ£³°µºP–ô˜.š -Ú®ú  %ÊVK‘Z’ÚÖß!©›Ý½$½9@„áðÌœRsôaAˆd1$¨ºM„¸2ÿûùqsÈ‚ Ht…$ˆG‹ÃûÍÚFo”_y=4"A¸@µ‚NÞ8r ³×ArŠê­:{£" °x=4&Aº@µ‚N^$|€dözhcS­‰³VÐÉeHxñ’g.v‡øð†E®ÌŠÃ¼Ñú<.Ͱ„ çŒ7ú bcÎ>KRw,>3ÜèCxö!®€8´-Ã|ž­7n1‘g$+ß7åíÝP'xû@ðСlð¥9V‘@Y»ûBYmË3Ó šAT¦•BCE šÕ`$œ˜sfp¦¢æLC#hÙ1ÐW¨ªá¤äÐk ¨¸@/[a Ô+yR´  <·Z}Wþº a%xxe½í©2m5pªøÅ&lîâŸh+ZqZULkËÂ2ø]*>fƒ¶qK5kèÀ§€52gV„¬%PÐÃÑñCnõMvʵüB:_°Û`³w­RXºö´Õ ;À\Ë@ŠÑ‹£JÑ‹~º ài’Ķ,ð>v†Vf ›@ÇXX+0†¶èJv=gÏÖ%À¾ÚÎH¥òíC¾§ö÷$Â[ˆíB|½ÞIÎ-Ï dÏõ‡Œqý±yx ¾cîŠìã$ YañRE…ßB¦-[À¶OÝ G8ÑçÞÅzÖQ=mûgß¹§»OÄŽƒäÙØïl÷ûKø²­áñþùÇ—F%$È{©_Íø¨êJ›…óÎ\z¶;ÍŽ3aJJóo2%³ïÇÔ¤¸jߨ\ð¹d¿¾ú$ûUuàÊ—l,/Å—ë{õ%øÿS¶ÿ<å˯£oéõÖr8rþ­kjnu¦ Þ|­Å/OÞ¸J¯“7\âŒùïsû._ç¶Ö+3§A»éôÙªã4 i‚ƒ _è›YÀ?Q?—¨_'1‰à~ ¿OíÇ ¦(þ­f#U pC[3òDù$@‹âqyÄå³sSZ œàuÛ4L1Q1Ô»yþ_PœpûaºR@öNXÓÐa²q;ý uvR?L­‚"|Ô'~(ÚÈ[Yµ‚gT[/ÙV1n…ÂNÙUÞJ ƒ"‹ûG¥öR¤XoµSØ@GÔòq”pýXDçÔ Ú8*¶ ÈjцIòñ$Êð*O ÅgNœ<úVÞ;Ù„_4Sð=(†–ð~ì³Ð)*ïć¡U—å“Ån%ŠÀ!÷·¦ÈíÒ·åæ§ÍŸTE8§endstream endobj 1447 0 obj<>/XObject<<>>>>>>endobj 1448 0 obj<>stream x=ޱ‚0„÷>Å8P[A £ur0ù}€ …`*ÅL|{A£¹ä†/÷%÷`bŠ„Z!ÉPÞÙ–˜àb‚ÿò –‡R‚jH¥xš'P¢UŸ••ÑѺ«¶Øx¯_—`>endobj 1450 0 obj<>endobj 1451 0 obj<>endobj 1452 0 obj<>endobj 1453 0 obj<>endobj 1454 0 obj<>endobj 1455 0 obj<>endobj 1456 0 obj<>endobj 1457 0 obj<>endobj 1458 0 obj<>endobj 1459 0 obj<>endobj 1460 0 obj<>endobj 1461 0 obj<>endobj 1462 0 obj<>endobj 1463 0 obj<>endobj 1464 0 obj<>endobj 1465 0 obj<>endobj 1466 0 obj<>endobj 1467 0 obj<>endobj 1468 0 obj<>endobj 1469 0 obj<>endobj 1470 0 obj<>endobj 1471 0 obj<>endobj 1472 0 obj<>endobj 1473 0 obj<>endobj 1474 0 obj<>endobj 1475 0 obj<>endobj 1476 0 obj<>endobj 1477 0 obj<>endobj 1478 0 obj<>endobj 1479 0 obj<>endobj 1480 0 obj<>endobj 1481 0 obj<>endobj 1482 0 obj<>endobj 1483 0 obj<>endobj 1484 0 obj<>endobj 1485 0 obj<>endobj 1486 0 obj<>endobj 1487 0 obj<>endobj 1488 0 obj<>endobj 1489 0 obj<>endobj 1490 0 obj<>endobj 1491 0 obj<>endobj 1492 0 obj<>endobj 1493 0 obj<>endobj 1494 0 obj<>endobj 1495 0 obj<>endobj 1496 0 obj<>endobj 1497 0 obj<>endobj 1498 0 obj<>endobj 1499 0 obj<>endobj 1500 0 obj<>endobj 1501 0 obj<>endobj 1502 0 obj<>endobj 1503 0 obj<>endobj 1504 0 obj<>endobj 1505 0 obj<>endobj 1506 0 obj<>endobj 1507 0 obj<>endobj 1508 0 obj<>1<>4<>9<>21<>34<>49<>54<>79<>91<>94<>97<>]>>>>endobj xref 0 1509 0000000000 65535 f 0000000015 00000 n 0000000371 00000 n 0000001723 00000 n 0000046348 00000 n 0000046528 00000 n 0000047567 00000 n 0000096922 00000 n 0000097107 00000 n 0000098146 00000 n 0000141413 00000 n 0000141604 00000 n 0000142644 00000 n 0000187522 00000 n 0000187710 00000 n 0000188750 00000 n 0000232333 00000 n 0000232520 00000 n 0000233563 00000 n 0000277870 00000 n 0000278061 00000 n 0000279101 00000 n 0000322611 00000 n 0000322805 00000 n 0000323845 00000 n 0000358739 00000 n 0000358925 00000 n 0000359967 00000 n 0000394790 00000 n 0000394981 00000 n 0000396022 00000 n 0000426411 00000 n 0000426594 00000 n 0000427638 00000 n 0000427772 00000 n 0000427911 00000 n 0000428055 00000 n 0000428195 00000 n 0000428334 00000 n 0000428475 00000 n 0000428620 00000 n 0000428758 00000 n 0000428901 00000 n 0000429021 00000 n 0000456863 00000 n 0000483912 00000 n 0000484136 00000 n 0000484415 00000 n 0000489835 00000 n 0000491632 00000 n 0000493317 00000 n 0000493816 00000 n 0000494337 00000 n 0000494836 00000 n 0000495357 00000 n 0000495729 00000 n 0000496191 00000 n 0000496987 00000 n 0000497960 00000 n 0000498507 00000 n 0000499493 00000 n 0000500045 00000 n 0000501264 00000 n 0000501644 00000 n 0000502390 00000 n 0000694918 00000 n 0000696323 00000 n 0000700117 00000 n 0000706230 00000 n 0000707307 00000 n 0000710272 00000 n 0000711687 00000 n 0000713489 00000 n 0000714404 00000 n 0000714440 00000 n 0000714525 00000 n 0000714565 00000 n 0000714650 00000 n 0000714752 00000 n 0000714855 00000 n 0000714958 00000 n 0000715061 00000 n 0000715164 00000 n 0000715266 00000 n 0000715369 00000 n 0000715472 00000 n 0000715575 00000 n 0000715678 00000 n 0000715781 00000 n 0000715883 00000 n 0000715986 00000 n 0000716089 00000 n 0000716192 00000 n 0000716295 00000 n 0000716398 00000 n 0000716501 00000 n 0000716604 00000 n 0000716706 00000 n 0000716809 00000 n 0000716912 00000 n 0000717015 00000 n 0000717119 00000 n 0000717223 00000 n 0000717431 00000 n 0000717534 00000 n 0000717638 00000 n 0000717742 00000 n 0000717846 00000 n 0000717949 00000 n 0000718053 00000 n 0000718157 00000 n 0000718261 00000 n 0000718365 00000 n 0000718469 00000 n 0000718573 00000 n 0000718676 00000 n 0000718780 00000 n 0000718884 00000 n 0000718988 00000 n 0000719092 00000 n 0000719196 00000 n 0000719299 00000 n 0000719403 00000 n 0000719507 00000 n 0000719611 00000 n 0000719715 00000 n 0000719819 00000 n 0000719922 00000 n 0000720026 00000 n 0000720130 00000 n 0000720355 00000 n 0000720458 00000 n 0000720562 00000 n 0000720666 00000 n 0000720770 00000 n 0000720874 00000 n 0000720978 00000 n 0000721043 00000 n 0000721123 00000 n 0000721210 00000 n 0000721255 00000 n 0000721342 00000 n 0000721375 00000 n 0000721449 00000 n 0000721536 00000 n 0000721640 00000 n 0000721673 00000 n 0000721755 00000 n 0000721842 00000 n 0000721917 00000 n 0000722004 00000 n 0000722037 00000 n 0000722094 00000 n 0000722181 00000 n 0000722206 00000 n 0000722308 00000 n 0000722333 00000 n 0000722426 00000 n 0000722513 00000 n 0000722609 00000 n 0000722696 00000 n 0000722784 00000 n 0000722871 00000 n 0000722968 00000 n 0000723055 00000 n 0000723155 00000 n 0000723242 00000 n 0000723330 00000 n 0000723417 00000 n 0000723505 00000 n 0000723592 00000 n 0000723665 00000 n 0000723764 00000 n 0000723851 00000 n 0000723953 00000 n 0000724040 00000 n 0000724137 00000 n 0000724224 00000 n 0000724316 00000 n 0000724403 00000 n 0000724499 00000 n 0000724586 00000 n 0000724674 00000 n 0000724761 00000 n 0000724826 00000 n 0000724914 00000 n 0000725001 00000 n 0000725092 00000 n 0000725179 00000 n 0000725276 00000 n 0000725363 00000 n 0000725452 00000 n 0000725539 00000 n 0000725628 00000 n 0000725715 00000 n 0000725807 00000 n 0000725894 00000 n 0000725990 00000 n 0000726077 00000 n 0000726172 00000 n 0000726259 00000 n 0000726354 00000 n 0000726441 00000 n 0000726539 00000 n 0000726626 00000 n 0000726722 00000 n 0000726809 00000 n 0000726914 00000 n 0000727006 00000 n 0000727093 00000 n 0000727188 00000 n 0000727275 00000 n 0000727372 00000 n 0000727459 00000 n 0000727555 00000 n 0000727642 00000 n 0000727691 00000 n 0000727787 00000 n 0000727874 00000 n 0000727974 00000 n 0000728061 00000 n 0000728094 00000 n 0000728190 00000 n 0000728277 00000 n 0000728379 00000 n 0000728466 00000 n 0000728572 00000 n 0000728659 00000 n 0000728755 00000 n 0000728842 00000 n 0000728891 00000 n 0000728942 00000 n 0000729029 00000 n 0000729079 00000 n 0000729166 00000 n 0000729212 00000 n 0000729299 00000 n 0000729344 00000 n 0000729431 00000 n 0000729483 00000 n 0000729570 00000 n 0000729621 00000 n 0000729708 00000 n 0000729755 00000 n 0000729842 00000 n 0000729888 00000 n 0000729975 00000 n 0000730028 00000 n 0000730115 00000 n 0000730167 00000 n 0000730254 00000 n 0000730351 00000 n 0000730399 00000 n 0000730486 00000 n 0000730533 00000 n 0000730620 00000 n 0000730667 00000 n 0000730754 00000 n 0000730801 00000 n 0000730888 00000 n 0000730933 00000 n 0000731020 00000 n 0000731065 00000 n 0000731152 00000 n 0000731217 00000 n 0000731307 00000 n 0000731394 00000 n 0000731487 00000 n 0000731574 00000 n 0000731672 00000 n 0000731759 00000 n 0000731800 00000 n 0000731886 00000 n 0000731973 00000 n 0000732059 00000 n 0000732146 00000 n 0000732235 00000 n 0000732322 00000 n 0000732420 00000 n 0000732507 00000 n 0000732556 00000 n 0000732642 00000 n 0000732729 00000 n 0000732815 00000 n 0000732902 00000 n 0000732991 00000 n 0000733078 00000 n 0000733176 00000 n 0000733263 00000 n 0000733312 00000 n 0000733398 00000 n 0000733485 00000 n 0000733571 00000 n 0000733658 00000 n 0000733747 00000 n 0000733834 00000 n 0000733931 00000 n 0000734018 00000 n 0000734109 00000 n 0000734196 00000 n 0000734287 00000 n 0000734374 00000 n 0000734468 00000 n 0000734555 00000 n 0000734653 00000 n 0000734740 00000 n 0000734821 00000 n 0000734911 00000 n 0000734998 00000 n 0000735088 00000 n 0000735175 00000 n 0000735268 00000 n 0000735355 00000 n 0000735453 00000 n 0000735540 00000 n 0000735589 00000 n 0000735678 00000 n 0000735765 00000 n 0000735854 00000 n 0000735941 00000 n 0000736033 00000 n 0000736120 00000 n 0000736218 00000 n 0000736305 00000 n 0000736354 00000 n 0000736449 00000 n 0000736536 00000 n 0000736634 00000 n 0000736721 00000 n 0000736819 00000 n 0000736906 00000 n 0000736947 00000 n 0000737042 00000 n 0000737129 00000 n 0000737227 00000 n 0000737314 00000 n 0000737412 00000 n 0000737499 00000 n 0000737540 00000 n 0000737635 00000 n 0000737722 00000 n 0000737820 00000 n 0000737907 00000 n 0000738005 00000 n 0000738092 00000 n 0000738133 00000 n 0000738222 00000 n 0000738309 00000 n 0000738398 00000 n 0000738485 00000 n 0000738574 00000 n 0000738661 00000 n 0000738751 00000 n 0000738838 00000 n 0000738926 00000 n 0000739013 00000 n 0000739101 00000 n 0000739188 00000 n 0000739276 00000 n 0000739363 00000 n 0000739452 00000 n 0000739539 00000 n 0000739630 00000 n 0000739717 00000 n 0000739806 00000 n 0000739897 00000 n 0000739984 00000 n 0000740075 00000 n 0000740162 00000 n 0000740254 00000 n 0000740341 00000 n 0000740439 00000 n 0000740526 00000 n 0000740624 00000 n 0000740711 00000 n 0000740809 00000 n 0000740896 00000 n 0000740988 00000 n 0000741075 00000 n 0000741148 00000 n 0000741243 00000 n 0000741330 00000 n 0000741430 00000 n 0000741517 00000 n 0000741614 00000 n 0000741701 00000 n 0000741742 00000 n 0000741838 00000 n 0000741925 00000 n 0000742026 00000 n 0000742113 00000 n 0000742210 00000 n 0000742297 00000 n 0000742384 00000 n 0000742471 00000 n 0000742560 00000 n 0000742647 00000 n 0000742737 00000 n 0000742824 00000 n 0000742916 00000 n 0000743003 00000 n 0000743100 00000 n 0000743187 00000 n 0000743284 00000 n 0000743371 00000 n 0000743460 00000 n 0000743553 00000 n 0000743640 00000 n 0000743736 00000 n 0000743823 00000 n 0000743920 00000 n 0000744007 00000 n 0000744095 00000 n 0000744182 00000 n 0000744273 00000 n 0000744360 00000 n 0000744457 00000 n 0000744544 00000 n 0000744609 00000 n 0000744696 00000 n 0000744783 00000 n 0000744808 00000 n 0000744898 00000 n 0000744985 00000 n 0000745082 00000 n 0000745169 00000 n 0000745202 00000 n 0000745289 00000 n 0000745376 00000 n 0000745466 00000 n 0000745553 00000 n 0000745651 00000 n 0000745738 00000 n 0000745825 00000 n 0000745912 00000 n 0000746002 00000 n 0000746089 00000 n 0000746146 00000 n 0000746244 00000 n 0000746331 00000 n 0000746419 00000 n 0000746506 00000 n 0000746597 00000 n 0000746684 00000 n 0000746782 00000 n 0000746869 00000 n 0000746956 00000 n 0000747043 00000 n 0000747133 00000 n 0000747220 00000 n 0000747318 00000 n 0000747405 00000 n 0000747492 00000 n 0000747579 00000 n 0000747669 00000 n 0000747756 00000 n 0000747854 00000 n 0000747941 00000 n 0000748037 00000 n 0000748124 00000 n 0000748223 00000 n 0000748310 00000 n 0000748423 00000 n 0000748521 00000 n 0000748608 00000 n 0000748707 00000 n 0000748794 00000 n 0000748897 00000 n 0000748984 00000 n 0000749082 00000 n 0000749169 00000 n 0000749218 00000 n 0000749311 00000 n 0000749398 00000 n 0000749494 00000 n 0000749581 00000 n 0000749679 00000 n 0000749766 00000 n 0000749807 00000 n 0000749900 00000 n 0000749987 00000 n 0000750080 00000 n 0000750167 00000 n 0000750263 00000 n 0000750350 00000 n 0000750448 00000 n 0000750535 00000 n 0000750629 00000 n 0000750716 00000 n 0000750810 00000 n 0000750897 00000 n 0000750994 00000 n 0000751081 00000 n 0000751179 00000 n 0000751266 00000 n 0000751359 00000 n 0000751446 00000 n 0000751539 00000 n 0000751626 00000 n 0000751722 00000 n 0000751809 00000 n 0000751907 00000 n 0000751994 00000 n 0000752107 00000 n 0000752193 00000 n 0000752280 00000 n 0000752369 00000 n 0000752456 00000 n 0000752554 00000 n 0000752641 00000 n 0000752682 00000 n 0000752770 00000 n 0000752857 00000 n 0000752948 00000 n 0000753035 00000 n 0000753133 00000 n 0000753220 00000 n 0000753261 00000 n 0000753348 00000 n 0000753435 00000 n 0000753522 00000 n 0000753609 00000 n 0000753698 00000 n 0000753785 00000 n 0000753874 00000 n 0000753961 00000 n 0000754050 00000 n 0000754137 00000 n 0000754235 00000 n 0000754322 00000 n 0000754420 00000 n 0000754507 00000 n 0000754605 00000 n 0000754692 00000 n 0000754785 00000 n 0000754872 00000 n 0000754968 00000 n 0000755055 00000 n 0000755153 00000 n 0000755240 00000 n 0000755332 00000 n 0000755419 00000 n 0000755514 00000 n 0000755601 00000 n 0000755699 00000 n 0000755786 00000 n 0000755915 00000 n 0000756007 00000 n 0000756094 00000 n 0000756186 00000 n 0000756273 00000 n 0000756368 00000 n 0000756455 00000 n 0000756553 00000 n 0000756640 00000 n 0000756689 00000 n 0000756784 00000 n 0000756871 00000 n 0000756969 00000 n 0000757056 00000 n 0000757154 00000 n 0000757241 00000 n 0000757282 00000 n 0000757375 00000 n 0000757462 00000 n 0000757555 00000 n 0000757642 00000 n 0000757735 00000 n 0000757822 00000 n 0000757915 00000 n 0000758002 00000 n 0000758097 00000 n 0000758184 00000 n 0000758279 00000 n 0000758366 00000 n 0000758461 00000 n 0000758548 00000 n 0000758646 00000 n 0000758733 00000 n 0000758831 00000 n 0000758918 00000 n 0000759016 00000 n 0000759103 00000 n 0000759200 00000 n 0000759292 00000 n 0000759379 00000 n 0000759474 00000 n 0000759561 00000 n 0000759659 00000 n 0000759746 00000 n 0000759844 00000 n 0000759931 00000 n 0000760032 00000 n 0000760119 00000 n 0000760217 00000 n 0000760304 00000 n 0000760399 00000 n 0000760486 00000 n 0000760584 00000 n 0000760671 00000 n 0000760769 00000 n 0000760856 00000 n 0000760957 00000 n 0000761044 00000 n 0000761148 00000 n 0000761235 00000 n 0000761340 00000 n 0000761438 00000 n 0000761525 00000 n 0000761613 00000 n 0000761700 00000 n 0000761791 00000 n 0000761878 00000 n 0000761976 00000 n 0000762063 00000 n 0000762157 00000 n 0000762244 00000 n 0000762341 00000 n 0000762428 00000 n 0000762526 00000 n 0000762613 00000 n 0000762709 00000 n 0000762796 00000 n 0000762895 00000 n 0000762982 00000 n 0000763080 00000 n 0000763167 00000 n 0000763269 00000 n 0000763356 00000 n 0000763461 00000 n 0000763548 00000 n 0000763646 00000 n 0000763733 00000 n 0000763854 00000 n 0000763948 00000 n 0000764035 00000 n 0000764132 00000 n 0000764219 00000 n 0000764317 00000 n 0000764404 00000 n 0000764504 00000 n 0000764591 00000 n 0000764694 00000 n 0000764781 00000 n 0000764879 00000 n 0000764966 00000 n 0000765031 00000 n 0000765126 00000 n 0000765213 00000 n 0000765311 00000 n 0000765398 00000 n 0000765496 00000 n 0000765583 00000 n 0000765684 00000 n 0000765771 00000 n 0000765875 00000 n 0000765962 00000 n 0000766060 00000 n 0000766147 00000 n 0000766212 00000 n 0000766307 00000 n 0000766394 00000 n 0000766492 00000 n 0000766579 00000 n 0000766677 00000 n 0000766764 00000 n 0000766865 00000 n 0000766952 00000 n 0000767056 00000 n 0000767143 00000 n 0000767241 00000 n 0000767328 00000 n 0000767393 00000 n 0000767490 00000 n 0000767577 00000 n 0000767677 00000 n 0000767764 00000 n 0000767862 00000 n 0000767949 00000 n 0000768044 00000 n 0000768131 00000 n 0000768229 00000 n 0000768316 00000 n 0000768414 00000 n 0000768501 00000 n 0000768597 00000 n 0000768684 00000 n 0000768783 00000 n 0000768870 00000 n 0000768968 00000 n 0000769055 00000 n 0000769150 00000 n 0000769237 00000 n 0000769335 00000 n 0000769422 00000 n 0000769520 00000 n 0000769607 00000 n 0000769698 00000 n 0000769785 00000 n 0000769879 00000 n 0000769966 00000 n 0000770064 00000 n 0000770151 00000 n 0000770288 00000 n 0000770381 00000 n 0000770468 00000 n 0000770564 00000 n 0000770651 00000 n 0000770750 00000 n 0000770837 00000 n 0000770930 00000 n 0000771017 00000 n 0000771113 00000 n 0000771200 00000 n 0000771299 00000 n 0000771386 00000 n 0000771474 00000 n 0000771561 00000 n 0000771652 00000 n 0000771739 00000 n 0000771838 00000 n 0000771925 00000 n 0000772021 00000 n 0000772108 00000 n 0000772207 00000 n 0000772294 00000 n 0000772393 00000 n 0000772480 00000 n 0000772569 00000 n 0000772656 00000 n 0000772748 00000 n 0000772835 00000 n 0000772934 00000 n 0000773021 00000 n 0000773158 00000 n 0000773253 00000 n 0000773340 00000 n 0000773438 00000 n 0000773525 00000 n 0000773624 00000 n 0000773711 00000 n 0000773802 00000 n 0000773889 00000 n 0000773983 00000 n 0000774070 00000 n 0000774168 00000 n 0000774255 00000 n 0000774347 00000 n 0000774434 00000 n 0000774529 00000 n 0000774616 00000 n 0000774714 00000 n 0000774801 00000 n 0000774898 00000 n 0000774985 00000 n 0000775085 00000 n 0000775172 00000 n 0000775266 00000 n 0000775353 00000 n 0000775466 00000 n 0000775564 00000 n 0000775651 00000 n 0000775749 00000 n 0000775836 00000 n 0000775937 00000 n 0000776024 00000 n 0000776122 00000 n 0000776209 00000 n 0000776283 00000 n 0000776369 00000 n 0000776426 00000 n 0000776514 00000 n 0000776601 00000 n 0000776692 00000 n 0000776779 00000 n 0000776877 00000 n 0000776964 00000 n 0000777056 00000 n 0000777143 00000 n 0000777238 00000 n 0000777325 00000 n 0000777423 00000 n 0000777510 00000 n 0000777601 00000 n 0000777688 00000 n 0000777782 00000 n 0000777869 00000 n 0000777967 00000 n 0000778054 00000 n 0000778147 00000 n 0000778234 00000 n 0000778330 00000 n 0000778417 00000 n 0000778515 00000 n 0000778602 00000 n 0000778715 00000 n 0000778770 00000 n 0000778856 00000 n 0000778943 00000 n 0000779030 00000 n 0000779120 00000 n 0000779207 00000 n 0000779305 00000 n 0000779392 00000 n 0000779485 00000 n 0000779572 00000 n 0000779668 00000 n 0000779755 00000 n 0000779853 00000 n 0000779940 00000 n 0000780013 00000 n 0000780104 00000 n 0000780191 00000 n 0000780285 00000 n 0000780372 00000 n 0000780470 00000 n 0000780557 00000 n 0000780621 00000 n 0000780707 00000 n 0000780758 00000 n 0000780845 00000 n 0000780896 00000 n 0000780982 00000 n 0000781060 00000 n 0000781146 00000 n 0000781217 00000 n 0000781303 00000 n 0000781355 00000 n 0000781442 00000 n 0000781494 00000 n 0000781580 00000 n 0000781660 00000 n 0000781747 00000 n 0000781852 00000 n 0000781944 00000 n 0000782031 00000 n 0000782126 00000 n 0000782213 00000 n 0000782310 00000 n 0000782397 00000 n 0000782438 00000 n 0000782527 00000 n 0000782614 00000 n 0000782703 00000 n 0000782790 00000 n 0000782882 00000 n 0000782969 00000 n 0000783067 00000 n 0000783154 00000 n 0000783250 00000 n 0000783337 00000 n 0000783433 00000 n 0000783520 00000 n 0000783619 00000 n 0000783706 00000 n 0000783804 00000 n 0000783891 00000 n 0000783972 00000 n 0000784065 00000 n 0000784152 00000 n 0000784245 00000 n 0000784332 00000 n 0000784428 00000 n 0000784515 00000 n 0000784613 00000 n 0000784700 00000 n 0000784789 00000 n 0000784876 00000 n 0000784965 00000 n 0000785052 00000 n 0000785144 00000 n 0000785231 00000 n 0000785329 00000 n 0000785416 00000 n 0000785506 00000 n 0000785593 00000 n 0000785683 00000 n 0000785770 00000 n 0000785863 00000 n 0000785950 00000 n 0000786048 00000 n 0000786135 00000 n 0000786248 00000 n 0000786345 00000 n 0000786432 00000 n 0000786529 00000 n 0000786616 00000 n 0000786716 00000 n 0000786803 00000 n 0000786901 00000 n 0000786988 00000 n 0000787084 00000 n 0000787171 00000 n 0000787270 00000 n 0000787357 00000 n 0000787455 00000 n 0000787542 00000 n 0000787638 00000 n 0000787725 00000 n 0000787825 00000 n 0000787912 00000 n 0000788010 00000 n 0000788097 00000 n 0000788194 00000 n 0000788283 00000 n 0000788370 00000 n 0000788462 00000 n 0000788549 00000 n 0000788646 00000 n 0000788733 00000 n 0000788822 00000 n 0000788909 00000 n 0000789001 00000 n 0000789088 00000 n 0000789185 00000 n 0000789272 00000 n 0000789368 00000 n 0000789455 00000 n 0000789554 00000 n 0000789641 00000 n 0000789739 00000 n 0000789826 00000 n 0000789915 00000 n 0000790012 00000 n 0000790099 00000 n 0000790199 00000 n 0000790286 00000 n 0000790383 00000 n 0000790470 00000 n 0000790567 00000 n 0000790654 00000 n 0000790754 00000 n 0000790841 00000 n 0000790938 00000 n 0000791025 00000 n 0000791090 00000 n 0000791187 00000 n 0000791274 00000 n 0000791374 00000 n 0000791461 00000 n 0000791558 00000 n 0000791645 00000 n 0000791742 00000 n 0000791829 00000 n 0000791929 00000 n 0000792016 00000 n 0000792113 00000 n 0000792200 00000 n 0000792265 00000 n 0000792347 00000 n 0000792434 00000 n 0000792531 00000 n 0000792618 00000 n 0000792718 00000 n 0000792805 00000 n 0000792902 00000 n 0000792989 00000 n 0000793038 00000 n 0000793133 00000 n 0000793220 00000 n 0000793318 00000 n 0000793405 00000 n 0000793502 00000 n 0000793589 00000 n 0000793671 00000 n 0000793757 00000 n 0000793847 00000 n 0000793934 00000 n 0000794027 00000 n 0000794114 00000 n 0000794211 00000 n 0000794298 00000 n 0000794395 00000 n 0000794482 00000 n 0000794582 00000 n 0000794669 00000 n 0000794766 00000 n 0000794853 00000 n 0000794950 00000 n 0000795039 00000 n 0000795126 00000 n 0000795218 00000 n 0000795305 00000 n 0000795401 00000 n 0000795488 00000 n 0000795575 00000 n 0000795662 00000 n 0000795749 00000 n 0000795836 00000 n 0000795926 00000 n 0000796013 00000 n 0000796103 00000 n 0000796190 00000 n 0000796287 00000 n 0000796374 00000 n 0000796471 00000 n 0000796558 00000 n 0000796647 00000 n 0000796737 00000 n 0000796824 00000 n 0000796914 00000 n 0000797002 00000 n 0000797096 00000 n 0000797185 00000 n 0000797284 00000 n 0000797373 00000 n 0000797469 00000 n 0000797558 00000 n 0000797657 00000 n 0000797746 00000 n 0000797845 00000 n 0000797934 00000 n 0000798023 00000 n 0000798112 00000 n 0000798204 00000 n 0000798293 00000 n 0000798392 00000 n 0000798481 00000 n 0000798588 00000 n 0000798683 00000 n 0000798772 00000 n 0000798870 00000 n 0000798959 00000 n 0000799058 00000 n 0000799147 00000 n 0000799242 00000 n 0000799331 00000 n 0000799429 00000 n 0000799518 00000 n 0000799616 00000 n 0000799705 00000 n 0000799807 00000 n 0000799896 00000 n 0000800001 00000 n 0000800090 00000 n 0000800189 00000 n 0000800278 00000 n 0000800377 00000 n 0000800470 00000 n 0000800559 00000 n 0000800655 00000 n 0000800744 00000 n 0000800842 00000 n 0000800931 00000 n 0000801027 00000 n 0000801116 00000 n 0000801215 00000 n 0000801304 00000 n 0000801403 00000 n 0000801492 00000 n 0000801564 00000 n 0000801648 00000 n 0000801736 00000 n 0000801763 00000 n 0000801864 00000 n 0000801953 00000 n 0000802057 00000 n 0000802146 00000 n 0000802245 00000 n 0000802334 00000 n 0000802432 00000 n 0000802521 00000 n 0000802575 00000 n 0000802676 00000 n 0000802765 00000 n 0000802864 00000 n 0000802953 00000 n 0000803054 00000 n 0000803143 00000 n 0000803247 00000 n 0000803336 00000 n 0000803435 00000 n 0000803524 00000 n 0000803619 00000 n 0000803708 00000 n 0000803806 00000 n 0000803895 00000 n 0000803994 00000 n 0000804083 00000 n 0000804173 00000 n 0000804270 00000 n 0000804359 00000 n 0000804459 00000 n 0000804548 00000 n 0000804642 00000 n 0000804731 00000 n 0000804828 00000 n 0000804917 00000 n 0000805019 00000 n 0000805108 00000 n 0000805213 00000 n 0000805302 00000 n 0000805374 00000 n 0000805471 00000 n 0000805560 00000 n 0000805660 00000 n 0000805749 00000 n 0000805846 00000 n 0000805935 00000 n 0000806035 00000 n 0000806124 00000 n 0000806226 00000 n 0000806315 00000 n 0000806420 00000 n 0000806509 00000 n 0000806610 00000 n 0000806699 00000 n 0000806803 00000 n 0000806892 00000 n 0000806992 00000 n 0000807081 00000 n 0000807184 00000 n 0000807273 00000 n 0000807381 00000 n 0000807476 00000 n 0000807565 00000 n 0000807662 00000 n 0000807751 00000 n 0000807846 00000 n 0000807935 00000 n 0000808030 00000 n 0000808119 00000 n 0000808214 00000 n 0000808303 00000 n 0000808401 00000 n 0000808490 00000 n 0000808588 00000 n 0000808677 00000 n 0000808775 00000 n 0000808864 00000 n 0000808962 00000 n 0000809051 00000 n 0000809150 00000 n 0000809186 00000 n 0000809222 00000 n 0000811137 00000 n 0000811182 00000 n 0000811227 00000 n 0000811272 00000 n 0000811317 00000 n 0000811362 00000 n 0000811407 00000 n 0000811452 00000 n 0000811497 00000 n 0000811542 00000 n 0000811587 00000 n 0000811632 00000 n 0000811677 00000 n 0000811722 00000 n 0000811767 00000 n 0000811812 00000 n 0000811857 00000 n 0000811902 00000 n 0000811947 00000 n 0000811992 00000 n 0000812037 00000 n 0000812082 00000 n 0000812127 00000 n 0000812172 00000 n 0000812217 00000 n 0000812262 00000 n 0000812307 00000 n 0000812352 00000 n 0000812397 00000 n 0000812442 00000 n 0000812487 00000 n 0000812532 00000 n 0000812577 00000 n 0000812622 00000 n 0000812667 00000 n 0000812712 00000 n 0000812757 00000 n 0000812802 00000 n 0000812847 00000 n 0000812892 00000 n 0000812937 00000 n 0000812982 00000 n 0000813027 00000 n 0000813072 00000 n 0000813117 00000 n 0000813162 00000 n 0000813207 00000 n 0000813252 00000 n 0000813297 00000 n 0000813342 00000 n 0000813387 00000 n 0000813432 00000 n 0000813477 00000 n 0000813522 00000 n 0000813567 00000 n 0000813612 00000 n 0000813657 00000 n 0000813702 00000 n 0000813747 00000 n 0000813792 00000 n 0000813837 00000 n 0000813882 00000 n 0000813927 00000 n 0000813972 00000 n 0000814017 00000 n 0000814062 00000 n 0000814107 00000 n 0000814152 00000 n 0000814197 00000 n 0000814242 00000 n 0000814287 00000 n 0000814332 00000 n 0000814377 00000 n 0000814422 00000 n 0000814467 00000 n 0000814512 00000 n 0000814557 00000 n 0000814602 00000 n 0000814647 00000 n 0000814692 00000 n 0000814737 00000 n 0000814782 00000 n 0000814827 00000 n 0000814872 00000 n 0000814917 00000 n 0000814962 00000 n 0000815007 00000 n 0000815052 00000 n 0000815097 00000 n 0000815142 00000 n 0000815187 00000 n 0000815232 00000 n 0000815277 00000 n 0000815322 00000 n 0000815367 00000 n 0000815412 00000 n 0000815457 00000 n 0000815502 00000 n 0000815547 00000 n 0000815592 00000 n 0000815637 00000 n 0000815682 00000 n 0000815727 00000 n 0000815772 00000 n 0000815817 00000 n 0000815862 00000 n 0000815907 00000 n 0000815952 00000 n 0000816928 00000 n 0000817131 00000 n 0000817413 00000 n 0000817609 00000 n 0000819771 00000 n 0000819967 00000 n 0000822130 00000 n 0000822326 00000 n 0000823017 00000 n 0000823188 00000 n 0000824556 00000 n 0000824758 00000 n 0000826381 00000 n 0000826604 00000 n 0000827544 00000 n 0000827736 00000 n 0000828808 00000 n 0000828980 00000 n 0000829519 00000 n 0000829736 00000 n 0000831368 00000 n 0000831539 00000 n 0000833397 00000 n 0000833603 00000 n 0000835614 00000 n 0000835831 00000 n 0000837413 00000 n 0000837640 00000 n 0000838943 00000 n 0000839170 00000 n 0000840307 00000 n 0000840509 00000 n 0000841830 00000 n 0000842011 00000 n 0000843199 00000 n 0000843390 00000 n 0000844736 00000 n 0000844938 00000 n 0000846295 00000 n 0000846497 00000 n 0000847591 00000 n 0000847772 00000 n 0000848471 00000 n 0000848663 00000 n 0000850078 00000 n 0000850259 00000 n 0000851773 00000 n 0000851975 00000 n 0000853621 00000 n 0000853837 00000 n 0000855180 00000 n 0000855406 00000 n 0000856614 00000 n 0000856820 00000 n 0000857824 00000 n 0000858062 00000 n 0000859088 00000 n 0000859326 00000 n 0000860579 00000 n 0000860807 00000 n 0000861788 00000 n 0000861991 00000 n 0000863223 00000 n 0000863409 00000 n 0000864502 00000 n 0000864708 00000 n 0000865820 00000 n 0000866016 00000 n 0000866557 00000 n 0000866773 00000 n 0000868290 00000 n 0000868496 00000 n 0000869825 00000 n 0000870051 00000 n 0000871373 00000 n 0000871589 00000 n 0000872591 00000 n 0000872797 00000 n 0000873980 00000 n 0000874203 00000 n 0000874920 00000 n 0000875123 00000 n 0000875885 00000 n 0000876134 00000 n 0000877069 00000 n 0000877307 00000 n 0000878153 00000 n 0000878334 00000 n 0000879186 00000 n 0000879424 00000 n 0000880357 00000 n 0000880570 00000 n 0000881037 00000 n 0000881259 00000 n 0000883459 00000 n 0000883665 00000 n 0000884661 00000 n 0000884857 00000 n 0000885435 00000 n 0000885651 00000 n 0000886707 00000 n 0000886913 00000 n 0000887893 00000 n 0000888109 00000 n 0000888946 00000 n 0000889162 00000 n 0000890106 00000 n 0000890302 00000 n 0000890715 00000 n 0000890942 00000 n 0000892088 00000 n 0000892294 00000 n 0000893328 00000 n 0000893567 00000 n 0000894377 00000 n 0000894583 00000 n 0000896071 00000 n 0000896277 00000 n 0000897402 00000 n 0000897615 00000 n 0000898400 00000 n 0000898648 00000 n 0000899560 00000 n 0000899766 00000 n 0000901011 00000 n 0000901217 00000 n 0000902286 00000 n 0000902502 00000 n 0000903658 00000 n 0000903874 00000 n 0000904843 00000 n 0000905039 00000 n 0000905835 00000 n 0000906041 00000 n 0000907117 00000 n 0000907303 00000 n 0000908324 00000 n 0000908510 00000 n 0000909431 00000 n 0000909617 00000 n 0000910464 00000 n 0000910650 00000 n 0000911545 00000 n 0000911731 00000 n 0000912581 00000 n 0000912767 00000 n 0000913711 00000 n 0000913897 00000 n 0000914972 00000 n 0000915168 00000 n 0000916274 00000 n 0000916480 00000 n 0000917711 00000 n 0000917927 00000 n 0000919121 00000 n 0000919327 00000 n 0000921190 00000 n 0000921386 00000 n 0000922232 00000 n 0000922438 00000 n 0000923549 00000 n 0000923745 00000 n 0000924837 00000 n 0000925033 00000 n 0000926429 00000 n 0000926645 00000 n 0000927751 00000 n 0000927989 00000 n 0000928753 00000 n 0000928959 00000 n 0000929816 00000 n 0000930054 00000 n 0000930886 00000 n 0000931092 00000 n 0000932182 00000 n 0000932408 00000 n 0000933584 00000 n 0000933791 00000 n 0000934734 00000 n 0000934942 00000 n 0000936057 00000 n 0000936264 00000 n 0000936937 00000 n 0000937166 00000 n 0000937949 00000 n 0000938140 00000 n 0000939009 00000 n 0000939201 00000 n 0000939993 00000 n 0000940206 00000 n 0000940958 00000 n 0000941175 00000 n 0000942752 00000 n 0000942949 00000 n 0000943906 00000 n 0000944087 00000 n 0000945480 00000 n 0000945683 00000 n 0000946689 00000 n 0000946886 00000 n 0000947751 00000 n 0000947948 00000 n 0000948848 00000 n 0000949045 00000 n 0000950115 00000 n 0000950276 00000 n 0000950505 00000 n 0000950564 00000 n 0000950667 00000 n 0000950821 00000 n 0000950944 00000 n 0000951054 00000 n 0000951176 00000 n 0000951289 00000 n 0000951472 00000 n 0000951619 00000 n 0000951731 00000 n 0000951855 00000 n 0000951970 00000 n 0000952079 00000 n 0000952250 00000 n 0000952379 00000 n 0000952486 00000 n 0000952607 00000 n 0000952722 00000 n 0000952845 00000 n 0000952969 00000 n 0000953076 00000 n 0000953252 00000 n 0000953374 00000 n 0000953498 00000 n 0000953616 00000 n 0000953739 00000 n 0000953852 00000 n 0000954022 00000 n 0000954151 00000 n 0000954247 00000 n 0000954342 00000 n 0000954510 00000 n 0000954646 00000 n 0000954750 00000 n 0000954868 00000 n 0000954997 00000 n 0000955144 00000 n 0000955282 00000 n 0000955441 00000 n 0000955575 00000 n 0000955686 00000 n 0000955809 00000 n 0000955968 00000 n 0000956064 00000 n 0000956239 00000 n 0000956362 00000 n 0000956465 00000 n 0000956601 00000 n 0000956717 00000 n 0000956821 00000 n 0000956975 00000 n 0000957098 00000 n 0000957200 00000 n 0000957340 00000 n 0000957463 00000 n 0000957573 00000 n 0000957699 00000 n 0000957821 00000 n 0000957944 00000 n trailer <]>> startxref 958347 %%EOF ga-5.9.2/global/examples/000077500000000000000000000000001500715745200152025ustar00rootroot00000000000000ga-5.9.2/global/examples/CMakeLists.txt000066400000000000000000000022631500715745200177450ustar00rootroot00000000000000 set(__global_example_incs ${PROJECT_SOURCE_DIR}/global/src ${PROJECT_BINARY_DIR}/global/src ${PROJECT_SOURCE_DIR}/ma ${PROJECT_BINARY_DIR}/ma ${PROJECT_BINARY_DIR}/gaf2c ${PROJECT_SOURCE_DIR}/comex/src-armci ${PROJECT_SOURCE_DIR}/LinAlg/lapack+blas ${PROJECT_SOURCE_DIR}/global/testing ${PROJECT_BINARY_DIR}) ga_add_parallel_test (lennard "lennard-jones/lennard.c") target_include_directories(lennard.x PRIVATE ${__global_example_incs}) set(boltz_srcs boltzmann/boltzmann.F boltzmann/common boltzmann/equil.F boltzmann/get_patch.F boltzmann/initpar.F boltzmann/printdat.F boltzmann/properties.F boltzmann/setup.F boltzmann/timestep.F boltzmann/vorticity.F) #FIXME: boltz test does not compile in few cases # add_library(boltz OBJECT ${boltz_srcs}) # target_include_directories(boltz PRIVATE ${__global_example_incs}) # if (ENABLE_FORTRAN) # ga_add_parallel_test (boltz boltzmann/main.F) # target_include_directories(boltz.x PRIVATE ${__global_example_incs}) # target_link_libraries(boltz.x boltz) # endif() ga-5.9.2/global/examples/boltzmann/000077500000000000000000000000001500715745200172065ustar00rootroot00000000000000ga-5.9.2/global/examples/boltzmann/README000066400000000000000000000043511500715745200200710ustar00rootroot00000000000000This directory contains the source code for a simple lattice Boltzmann simulation of flow in a lid-driven square cavity. The algorithm used is described in BJ Palmer and DR Rector, Journal of Computational Physics, vol. 161, pp. 1-20 (2000). The temperature has been set to a constant for this simulation, eliminating the need for the second distribution and an ideal gas has been used for equation of state. The output from the simulation is sent to the file bltz.dat can be viewed using the TecPlot visualization program, or any program that can read TecPlot formatted files. The printdat.F routine also has code (currently commented out) that can be used to generate results using the format employed by the General Mesh Viewer program. The global arrays used in the code are all declared in the file main.F and then passed by reference to the subroutines. The output file is created by the subroutine in printdat.F; if you are interested in writing your own output routine, you should consult this file. The size of the grid used for the simulation is set in the include file "common" using the variable NSIZE, the remaining system parameters are set in the subroutine in setup.F. The parameters are currently set to model a relatively low velocity flow and should take a couple of minutes to run on a modest number of processors. Performance will, of course, depend on the particular system. For those wishing to try some calculations at other conditions, some of the remaining parameters are: - xmax: the dimensions of the square cavity - viscosity: viscosity - delta_t: time increment - tmprtr0: temperature of fluid - rho0: initial uniform density of fluid in cavity - uxbc: velocity of lid The executable name is boltz.x The parameters are currently set to run at a Reynolds number of 128. A calculation at a much higher Reynolds number of 2560 can be run by changing the value of NSIZE to 4097 and resetting the following parameters: - viscosity: 0.05 - delta_t: 0.1 Note that the simulation will have to be run for a considerably longer time period before any significant flow patterns begin to develop, on the order of 100000-200000 steps. This calculation also involves a much larger number of grid cells and requires a significant number of processors to run. ga-5.9.2/global/examples/boltzmann/boltzmann.F000066400000000000000000000043251500715745200213250ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine boltzmann(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) #include "common" c integer ld_fg1, ld_fg2, ld_fld1, ld_fld2, ld_bc1 double precision fg(ld_fg1,ld_fg2, *) double precision fld(ld_fld1, ld_fld2, *) integer bc(ld_bc1, *) integer g_fg, g_fld, g_bc c #include "mafdecls.fh" #include "global.fh" c integer i, me, nprocs me = ga_nodeid() nprocs = ga_nnodes() c c initialize problem c call setup(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) c if (me.eq.0) then write(6,*) 'Total mass is ',rtot write(6,*) 'Total x-momentum is ',uxtot write(6,*) 'Total y-momentum is ',uytot endif do i = 1, nsteps call timestep(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) if (mod(i,200).eq.0) then if (me.eq.0) then write(6,*) 'Completed step ',i write(6,*) 'Total density is ',rtot write(6,*) 'Total x-momentum is ',uxtot write(6,*) 'Total y-momentum is ',uytot endif call vorticity(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) call printdat(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) endif end do call vorticity(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) call printdat(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) c call ga_dgop(MT_DBL,tstats,50,'+') if (me.eq.0) write(6,100) tstats(1)/dble(nprocs) if (me.eq.0) write(6,200) tstats(2)/dble(nprocs) c 100 format('Average time in lattice Boltzmann updates :',f12.4) 200 format('Average time in ghost cell updates :',f12.4) return end ga-5.9.2/global/examples/boltzmann/common000066400000000000000000000023741500715745200204270ustar00rootroot00000000000000 implicit none integer NDIM, NSIZE parameter (NDIM=2, NSIZE=129) common /indices/ lo(NDIM+1), hi(NDIM+1), size(NDIM), width(NDIM), + dims(NDIM) integer lo, hi, size, width, dims common /ghstdat/ dims_fg(NDIM+1), dims_fld(NDIM+1), + dims_bc(NDIM) integer dims_fg, dims_fld, dims_bc common /lattice/ ei(8,NDIM), cspd double precision ei, cspd common /distributions/ ffa(0:8), ffb(0:8), ffc(0:8), ffd(0:8), + ffe(0:8) double precision ffa, ffb, ffc, ffd, ffe common /boundary/ bcpar(10,3) double precision bcpar common /props/ rtot, uxtot, uytot double precision rtot, uxtot, uytot common /constants/ a_vdw, b_vdw, rgas, tmprtr0 double precision a_vdw, b_vdw, rgas, tmprtr0 common /simpar/ delta_t, delta_x, xmax, viscosity, nsteps double precision delta_t, delta_x, xmax, viscosity integer nsteps common /patch/ fgp(-1:1,-1:1,9), hash(-1:1,-1:1), + ihash(-1:1,-1:1) double precision fgp integer hash, ihash common /bcparams/ uxbc, uybc, rhobc double precision uxbc, uybc, rhobc common /timing/ tstats(50) double precision tstats ga-5.9.2/global/examples/boltzmann/equil.F000066400000000000000000000034171500715745200204410ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine equil(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) #include "common" c integer ld_fg1, ld_fg2, ld_fld1, ld_fld2, ld_bc1 double precision fg(ld_fg1,ld_fg2, *) double precision fld(ld_fld1, ld_fld2, *) integer bc(ld_bc1, *) integer g_fg, g_fld, g_bc c double precision rdim, b, c2, cspdi, cspd2, ex, ey double precision aa0, aa integer i, j, ii, jj c c This subroutine evaluates the equilibrium distribution c rdim = 4.0d00 b = 24.0d00 c2 = cspd**2 cspdi = sqrt(2.0d00)/cspd cspd2 = 1.0d00/cspdi c c Calculate equilibrium distributions c do jj = width(2) + 1, dims(2) - width(2) do i = 10, 18 j = i-9 if (j.ne.1) then ex = cspd2*ei(j-1,1) ey = cspd2*ei(j-1,2) else ex = 0.0d00 ey = 0.0d00 endif do ii = width(1) + 1, dims(1) - width(1) aa = (rdim/(b*c2))*fld(ii,jj,4) aa0 = fld(ii,jj,1) - b*aa if (j.eq.1) then ffa(j) = aa0 + 4.0d00*aa else if (j.le.5) then ffa(j) = 4.0d00*aa else ffa(j) = aa endif c c evaluate distribution c fg(ii,jj,i) = ffa(j) + fld(ii,jj,1)*(ffb(j)*(fld(ii,jj,2)*ex + + fld(ii,jj,3)*ey) + + ffc(j)*(fld(ii,jj,2)**2*ex**2 + + 2.0d00*fld(ii,jj,2)*fld(ii,jj,3)*ex*ey + + fld(ii,jj,3)**2*ey**2) + + ffd(j)*(fld(ii,jj,2)**2+fld(ii,jj,3)**2)) end do end do end do c return end ga-5.9.2/global/examples/boltzmann/get_patch.F000066400000000000000000000116241500715745200212570ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine get_patch(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1, ii, jj) #include "common" c integer ld_fg1, ld_fg2, ld_fld1, ld_fld2, ld_bc1 double precision fg(ld_fg1,ld_fg2, *) double precision fld(ld_fld1, ld_fld2, *) integer bc(ld_bc1, *) integer g_fg, g_fld, g_bc c c Subroutine to handle cells at boundary c double precision rho, ux, uy, cspd2, dux, duy, a, b integer i, j, ii, jj, k, l, kk, ll integer bval integer mask(-1:1,-1:1) c c Locate local and absolute indices of cell c i = ii - width(1) - 1 + lo(1) j = jj - width(2) - 1 + lo(2) c c Check values of neighboring cells c do l = -1, 1 do k = -1, 1 if (i+k.ge.1.and.i+k.le.size(1).and. + j+l.ge.1.and.j+l.le.size(2)) then if (bc(ii+k,jj+l).ne.0) then mask(k,l) = 2 else mask(k,l) = 0 endif else mask(k,l) = 2 endif end do end do c c Determine if cells in mask represent interior cells or are c on the boundary c do l = -1, 1 do k = -1, 1 if (mask(k,l).ne.0.and.(k.ne.0.or.l.ne.0)) then do ll = max(l-1,-1), min(l+1,1) do kk = max(k-1,-1), min(k+1,1) if (mask(kk,ll).eq.0) mask(k,l) = 1 end do end do endif end do end do c c Evaluate distribution in boundary patch c bval = bc(ii,jj) c c Apply simple bounce back condition c if (bval.eq.1) then do l = -1, 1 do k = -1, 1 if (mask(k,l).eq.2) then fgp(k,l,ihash(k,l)) = fg(ii,jj,hash(k,l)+18) else fgp(k,l,ihash(k,l)) = fg(ii+k,jj+l,ihash(k,l)+18) endif end do end do c c Apply constant velocity boundary condition to flat upper boundary c else if (bval.eq.2) then cspd2 = cspd/sqrt(2.0d00) rho = 0.0d00 ux = 0.0d00 uy = 0.0d00 do l = -1, 1 do k = -1, 1 if (mask(k,l).eq.2) then fgp(k,l,ihash(k,l)) = fg(ii,jj,hash(k,l)+18) else fgp(k,l,ihash(k,l)) = fg(ii+k,jj+l,ihash(k,l)+18) endif rho = rho + fgp(k,l,ihash(k,l)) ux = ux - cspd2*dble(k)*fgp(k,l,ihash(k,l)) uy = uy - cspd2*dble(l)*fgp(k,l,ihash(k,l)) end do end do ux = ux/rho uy = uy/rho ux = uxbc - ux c c Add corrections needed to adjust for velocity mismatch c fgp(1,1,ihash(1,1)) = fgp(1,1,ihash(1,1)) + - 0.5d00*rho*ux/cspd2 fgp(-1,1,ihash(-1,1)) = fgp(-1,1,ihash(-1,1)) + + 0.5d00*rho*ux/cspd2 cc fgp(1,1,ihash(1,1)) = fgp(1,1,ihash(1,1)) cc + - 0.5d00*rho*ux/cspd2 cc + - rho*uy/(3.0d00*cspd2) cc fgp(0,1,ihash(0,1)) = fgp(0,1,ihash(0,1)) cc + - rho*uy/(3.0d00*cspd2) cc fgp(-1,1,ihash(-1,1)) = fgp(-1,1,ihash(-1,1)) cc + + 0.5d00*rho*ux/cspd2 cc + - rho*uy/(3.0d00*cspd2) c ux = uxbc c uy = 0.0d00 c rho = (cspd2/(uy+cspd2))*(fg(ii,jj,ihash(0,0)+18) c + + fg(ii+1,jj,ihash(1,0)+18)+fg(ii-1,jj,ihash(-1,0)+18) c + + 2.0d00*(fg(ii-1,jj-1,ihash(-1,-1)+18) c + + fg(ii,jj-1,ihash(0,-1)+18)+fg(ii+1,jj-1,ihash(1,-1)+18))) c fgp(0,1,ihash(0,1)) = fg(ii,jj-1,ihash(0,-1)+18) c a = (rho*ux-cspd2*(-fg(ii-1,jj,ihash(1,0)+18) c + + fg(ii+1,jj,ihash(1,0)+18)+fg(ii+1,jj+1,ihash(1,1)+18) c + - fg(ii-1,jj+1,ihash(-1,1)+18)))/cspd2 c b = fg(ii+1,jj+1,ihash(1,1)+18)+fg(ii+1,jj-1,ihash(1,-1)+18) c fgp(1,1,ihash(1,1)) = -0.5d00*(a+b) c fgp(1,1,ihash(1,1)) = 0.5d00*(a-b) c c Apply constant velocity boundary condition c else if (bval.eq.3) then cspd2 = sqrt(2.0d00)*cspd rho = 0.0d00 ux = 0.0d00 uy = 0.0d00 do l = -1, 1 do k = -1, 1 if (mask(k,l).eq.2.and.(k.ne.0.or.l.ne.0)) then fgp(k,l,ihash(k,l)) = fg(ii,jj,hash(k,l)) rho = rho + fg(ii,jj,hash(k,l)) ux = ux + cspd2*dble(k)*fg(ii,jj,hash(k,l)) uy = uy + cspd2*dble(l)*fg(ii,jj,hash(k,l)) else fgp(k,l,ihash(k,l)) = fg(ii+k,jj+l,ihash(i,l)) rho = rho + fg(ii+k,jj+l,ihash(k,l)) ux = ux + cspd2*dble(k)*fg(ii+k,jj+l,ihash(k,l)) uy = uy + cspd2*dble(l)*fg(ii+k,jj+l,ihash(k,l)) endif end do end do c c Determine value of correction needed to get specified final c velocity c dux = bcpar(2,1) - ux duy = bcpar(2,2) - uy endif c return end ga-5.9.2/global/examples/boltzmann/initpar.F000066400000000000000000000030731500715745200207660ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine initpar #include "common" c double precision q8_ei(18,3) data q8_ei + /1,-1, 0, 0, 1,-1, 1,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 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/ integer i,j double precision rdim,b,c2 c do i=1, 8 do j = 1, NDIM ei(i,j) = q8_ei(i,j) end do end do c rdim = 4.0d00 c2 = cspd**2 b = 24.0d00 c do i =1, 9 if (i.eq.1) then ffb(i) = 0.0d00 ffc(i) = 0.0d00 ffd(i) = -1.0d00/c2-4.0d00*rdim/(2.0d00*b*c2) elseif (i.le.5) then ffb(i) = 4.0d00*rdim/(b*c2) ffc(i) = 4.0d00*rdim*(rdim+2.0d00)/(2.0d00*b*c2**2) ffd(i) = -4.0d00*rdim/(2.0d00*b*c2) else ffb(i) = rdim/(b*c2) ffc(i) = rdim*(rdim+2.0d00)/(2.0d00*b*c2**2) ffd(i) = -rdim/(2.0d00*b*c2) endif end do c c c Initialize hash table c hash(0,0) = 1 hash(1,0) = 2 hash(-1,0) = 3 hash(0,1) = 4 hash(0,-1) = 5 hash(1,1) = 6 hash(-1,-1) = 7 hash(1,-1) = 8 hash(-1,1) = 9 c c Initialise inverse hash table c ihash(0,0) = 1 ihash(1,0) = 3 ihash(-1,0) = 2 ihash(0,1) = 5 ihash(0,-1) = 4 ihash(1,1) = 7 ihash(-1,-1) = 6 ihash(1,-1) = 9 ihash(-1,1) = 8 c do i = 1, 50 tstats(i) = 0.0d00 end do c return end ga-5.9.2/global/examples/boltzmann/main.F000066400000000000000000000111471500715745200202450ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif program main c #include "common" c #include "mafdecls.fh" #include "global.fh" c integer i, j, gdim, type integer gdims(NDIM+1), gwidth(NDIM+1) integer pdims(NDIM+1), mcnt, mapc(5000) integer g_fg, g_fld, g_bc GA_ACCESS_INDEX_TYPE ptr_fg, ptr_fld, ptr_bc integer ld_fg(NDIM+1), ld_fld(NDIM+1), ld_bc(NDIM) integer heap, stack, me, nproc c c Initialize a message passing library c #include "mp3.fh" size(1) = NSIZE size(2) = NSIZE c c Initialize global arrays c call ga_initialize() c nproc = ga_nnodes() me = ga_nodeid() c if (ga_uses_ma()) then heap = (size(1)+2)*(size(2)+2)*34/nproc else heap = 100000 endif stack = 50000 c if (.not.ma_init(MT_DBL, stack, heap)) + call ga_error("ma init failed", -1) c c initialize global arrays c do i = 1, NDIM dims(i) = size(i) gdims(i) = size(i) width(i) = 1 gwidth(i) = 1 end do c c evaluate distribution of processors c gdim = NDIM call factor(nproc,gdim,pdims) mcnt = 1 do i = 1, NDIM do j = 0, pdims(i)-1 mapc(mcnt) = ((dble(j)/dble(pdims(i)))*dble(gdims(i)))+1 mcnt = mcnt + 1 end do end do do i = 0, pdims(1)-1 mapc(mcnt) = ((dble(i)/dble(pdims(1)))*dble(NDIM))+1 mcnt = mcnt + 1 end do do i = 0, pdims(2)-1 mapc(mcnt) = ((dble(i)/dble(pdims(2)))*dble(NDIM))+1 mcnt = mcnt + 1 end do c c Create global arrays. Start by creating array for LB distribution c functions. The last dimension runs over the distribution function c indices. The first 9 elements are the actual distribution elements, c the next 9 indices are the equilibrium distribution elements, c and the last 9 elements are temporary storage space used for doing c the streaming updates. c type = MT_DBL gdim = 3 gdims(3) = 27 gwidth(3) = 0 pdims(3) = 1 mapc(mcnt) = 1 if (.not.nga_create_ghosts_irreg(type, gdim, gdims, gwidth, + "lb_dist", mapc, pdims, g_fg)) + call ga_error("g_fg init failed",me) c c Create global array to hold density, momentum, pressure, c and relaxation parameters. These are stored at each point c and indexed by the last indice as density, p_x, p_y, c pressure, t_rho. c type = MT_DBL gdim = 3 gdims(3) = 6 gwidth(3) = 0 pdims(3) = 1 mapc(mcnt) = 1 if (.not.nga_create_ghosts_irreg(type, gdim, gdims, gwidth, + "fields", mapc, pdims, g_fld)) + call ga_error("g_fld init failed",me) c c Create global array to hold boundary condition data. c type = MT_INT gdim = 2 if (.not.nga_create_ghosts_irreg(type, gdim, gdims, gwidth, + "bc_mask", mapc, pdims, g_bc)) + call ga_error("g_bc init failed",me) c c Find pointers to global array data c call nga_access_ghosts(g_fg,dims_fg,ptr_fg,ld_fg) call nga_access_ghosts(g_fld,dims_fld,ptr_fld,ld_fld) call nga_access_ghosts(g_bc,dims_bc,ptr_bc,ld_bc) do i = 1, NDIM dims(i) = dims_fg(i) end do c c Call routine to run main simulation c call boltzmann(g_fg, dbl_mb(ptr_fg), ld_fg(1), ld_fg(2), + g_fld, dbl_mb(ptr_fld), ld_fld(1), ld_fld(2), + g_bc, int_mb(ptr_bc), ld_bc(1)) c c Close out calculation c call ga_terminate() call MP_FINALIZE() stop end c subroutine factor(p,ndim,dims) implicit none integer i,j,p,ndim,dims(*),imin,mdim integer ip,ifac,pmax,prime(1000) integer fac(1000) c i = 1 ip = p do i = 1, ndim dims(i) = 1 end do c c factor p completely c first, find all prime numbers less than or equal to p c pmax = 0 do i = 2, p do j = 1, pmax if (mod(i,prime(j)).eq.0) go to 100 end do pmax = pmax + 1 prime(pmax) = i 100 continue end do c c find all prime factors of p c ifac = 0 do i = 1, pmax 200 if (mod(ip,prime(i)).eq.0) then ifac = ifac + 1 fac(ifac) = prime(i) ip = ip/prime(i) go to 200 endif end do c c determine dimensions of processor grid c do i = ifac, 1, -1 c c find dimension with minimum value c imin = dims(1) mdim = 1 do j = 2, ndim if (dims(j).lt.imin) then imin = dims(j) mdim = j endif end do dims(mdim) = dims(mdim)*fac(i) end do c return end ga-5.9.2/global/examples/boltzmann/printdat.F000066400000000000000000000161571500715745200211540ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine printdat(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) #include "common" c integer ld_fg1, ld_fg2, ld_fld1, ld_fld2, ld_bc1 double precision fg(ld_fg1, ld_fg2, *) double precision fld(ld_fld1, ld_fld2, *) integer bc(ld_bc1, *) integer g_fg, g_fld, g_bc c #include "mafdecls.fh" #include "global.fh" c double precision buffer(NSIZE,10,3) double precision dx, dy integer glo(NDIM+1), ghi(NDIM+1), bld(NDIM) integer i,j,k,imax,icnt1,icnt2,inc1,inc2,jcnt integer maxelem c c Subroutine to print current value of fields to a file c imax = size(2)/10 + 1 glo(1) = 1 ghi(1) = size(1) glo(3) = 1 ghi(3) = 3 bld(1) = NSIZE bld(2) = 10 c maxelem = 256 c c Check dimensions to see if size needs to be reduced c if (size(1).gt.maxelem) then inc1 = size(1)/maxelem icnt1 = 0 do i = 1, size(1), inc1 icnt1 = icnt1 + 1 end do else inc1 = 1 icnt1= size(1) endif if (size(2).gt.maxelem) then inc2 = size(2)/maxelem icnt2 = 0 do i = 1, size(2), inc2 icnt2 = icnt2 + 1 end do else inc2 = 1 icnt2= size(2) endif c if (ga_nodeid().eq.0) then #if 0 c c Write out results in GMV format c open(unit=7,file='bltz.gmv',status='unknown') c c Write out header type information c write(7,100) write(7,200) icnt1,icnt2,1 dx = dble(inc1)*xmax/dble(size(1)-1) dy = dble(inc1)*xmax/dble(size(1)-1) write(7,300) (dble(i-1)*dx,i=1,icnt1) write(7,300) (dble(i-1)*dy,i=1,icnt2) write(7,300) 0.0 write(7,1000) write(7,400) write(7,500) jcnt = 0 do i = 1, imax glo(2) = (i-1)*10 + 1 ghi(2) = i*10 if (ghi(2).gt.size(2)) ghi(2) = size(2) if (ghi(2).ge.glo(2)) then call nga_get(g_fld, glo, ghi, buffer, bld) do k = 1, ghi(2)-glo(2)+1 jcnt = jcnt + 1 if (mod((jcnt-1),inc2).eq.0) then write(7,300) (buffer(j,k,1),j=1,size(1),inc1) endif end do endif end do write(7,600) jcnt = 0 do i = 1, imax glo(2) = (i-1)*10 + 1 ghi(2) = i*10 if (ghi(2).gt.size(2)) ghi(2) = size(2) if (ghi(2).ge.glo(2)) then call nga_get(g_fld, glo, ghi, buffer, bld) do k = 1, ghi(2)-glo(2)+1 jcnt = jcnt + 1 if (mod((jcnt-1),inc2).eq.0) then write(7,300) (buffer(j,k,2),j=1,size(1),inc1) endif end do endif end do write(7,700) do i = 1, imax glo(2) = (i-1)*10 + 1 ghi(2) = i*10 if (ghi(2).gt.size(2)) ghi(2) = size(2) if (ghi(2).ge.glo(2)) then call nga_get(g_fld, glo, ghi, buffer, bld) do k = 1, ghi(2)-glo(2)+1 jcnt = jcnt + 1 if (mod((jcnt-1),inc2).eq.0) then write(7,300) (buffer(j,k,3),j=1,size(1),inc1) endif end do endif end do write(7,1100) glo(3) = 4 ghi(3) = 6 do i = 1, imax glo(2) = (i-1)*10 + 1 ghi(2) = i*10 if (ghi(2).gt.size(2)) ghi(2) = size(2) if (ghi(2).ge.glo(2)) then call nga_get(g_fld, glo, ghi, buffer, bld) do k = 1, ghi(2)-glo(2)+1 jcnt = jcnt + 1 if (mod((jcnt-1),inc2).eq.0) then write(7,300) (buffer(j,k,3),j=1,size(1),inc1) endif end do endif end do write(7,800) write(7,900) close(7) c c GMV write format statements c 100 format('gmvinput ascii') 200 format('nodes -1',3i10) 300 format(' ',5f12.4) 400 format('variable') 500 format('rho 1') 600 format('ux 1') 700 format('uy 1') 800 format('endvars') 900 format('endgmv') 1000 format('cells 0') 1100 format('vorticity 1') #else c c Write out results in TecPlot format c open(unit=7,file='bltz.dat',status='unknown') write(7,'(a)') 'TITLE = "Lattice Boltzmann Simulation"' write(7,'(a)') 'VARIABLES = "X" "Y" "RHO" "UX" "UY" "VORTICITY"' write(7,'(a,i8,a,i8,a)') 'ZONE I=',icnt1,' J=',icnt2, + ' DATAPACKING=BLOCK' do j=1,icnt2 write(7,100) ((dble(i-1)*dx),i=1,icnt1) end do write(7,'(a)') ' ' do j=1,icnt2 write(7,100) ((dble(j-1)*dy),i=1,icnt1) end do write(7,'(a)') ' ' jcnt = 0 glo(3) = 1 ghi(3) = 1 do i = 1, imax glo(2) = (i-1)*10 + 1 ghi(2) = i*10 if (ghi(2).gt.size(2)) ghi(2) = size(2) if (ghi(2).ge.glo(2)) then call nga_get(g_fld, glo, ghi, buffer, bld) do k = 1, ghi(2)-glo(2)+1 jcnt = jcnt + 1 if (mod((jcnt-1),inc2).eq.0) then write(7,100) (buffer(j,k,1),j=1,size(1),inc1) endif end do endif end do write(7,'(a)') ' ' jcnt = 0 glo(3) = 2 ghi(3) = 2 do i = 1, imax glo(2) = (i-1)*10 + 1 ghi(2) = i*10 if (ghi(2).gt.size(2)) ghi(2) = size(2) if (ghi(2).ge.glo(2)) then call nga_get(g_fld, glo, ghi, buffer, bld) do k = 1, ghi(2)-glo(2)+1 jcnt = jcnt + 1 if (mod((jcnt-1),inc2).eq.0) then write(7,100) (buffer(j,k,1),j=1,size(1),inc1) endif end do endif end do write(7,'(a)') ' ' jcnt = 0 glo(3) = 3 ghi(3) = 3 do i = 1, imax glo(2) = (i-1)*10 + 1 ghi(2) = i*10 if (ghi(2).gt.size(2)) ghi(2) = size(2) if (ghi(2).ge.glo(2)) then call nga_get(g_fld, glo, ghi, buffer, bld) do k = 1, ghi(2)-glo(2)+1 jcnt = jcnt + 1 if (mod((jcnt-1),inc2).eq.0) then write(7,100) (buffer(j,k,1),j=1,size(1),inc1) endif end do endif end do write(7,'(a)') ' ' jcnt = 0 glo(3) = 6 ghi(3) = 6 do i = 1, imax glo(2) = (i-1)*10 + 1 ghi(2) = i*10 if (ghi(2).gt.size(2)) ghi(2) = size(2) if (ghi(2).ge.glo(2)) then call nga_get(g_fld, glo, ghi, buffer, bld) do k = 1, ghi(2)-glo(2)+1 jcnt = jcnt + 1 if (mod((jcnt-1),inc2).eq.0) then write(7,100) (buffer(j,k,1),j=1,size(1),inc1) endif end do endif end do close(7) 100 format(' ',5f12.4) #endif endif c return end ga-5.9.2/global/examples/boltzmann/properties.F000066400000000000000000000047671500715745200215270ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine properties(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) #include "common" c integer ld_fg1, ld_fg2, ld_fld1, ld_fld2, ld_bc1 double precision fg(ld_fg1,ld_fg2, *) double precision fld(ld_fld1, ld_fld2, *) integer bc(ld_bc1, *) integer g_fg, g_fld, g_bc c #include "mafdecls.fh" #include "global.fh" c c Evaluate properties on grid c double precision cspd2, ex, ey, sum(3) double precision rho integer i, j, ii, jj c c Zero accumulators and hydrodynamic fields c rtot = 0.0d00 uxtot = 0.0d00 uytot = 0.0d00 cspd2 = cspd/sqrt(2.0d00) c do jj = width(2) + 1, dims(2) - width(2) do i = 1, 4 do ii = width(1) + 1, dims(1) - width(1) fld(ii,jj,i) = 0.0d00 end do end do end do c c Evaluate density and momentum c do jj = width(2) + 1, dims(2) - width(2) do i = 1, 9 j = i - 1 if (j.ne.0) then ex = cspd2*ei(j,1) ey = cspd2*ei(j,2) else ex = 0.0d00 ey = 0.0d00 endif do ii = width(1) + 1, dims(1) - width(1) fld(ii,jj,1) = fld(ii,jj,1) + fg(ii,jj,i) fld(ii,jj,2) = fld(ii,jj,2) + ex*fg(ii,jj,i) fld(ii,jj,3) = fld(ii,jj,3) + ey*fg(ii,jj,i) end do end do end do c c Evaluate pressure and accumulate values c do jj = width(2) + 1, dims(2) - width(2) do ii = width(1) + 1, dims(1) - width(1) fld(ii,jj,2) = fld(ii,jj,2)/fld(ii,jj,1) fld(ii,jj,3) = fld(ii,jj,3)/fld(ii,jj,1) rtot = rtot + fld(ii,jj,1) c if (bc(ii,jj).eq.1) then c fld(ii,jj,2) = 0.0d00 c fld(ii,jj,3) = 0.0d00 c elseif (bc(ii,jj).eq.2) then c fld(ii,jj,1) = rhobc c fld(ii,jj,2) = uxbc c fld(ii,jj,3) = 0.0d00 c endif uxtot = uxtot + fld(ii,jj,1)*fld(ii,jj,2) uytot = uytot + fld(ii,jj,1)*fld(ii,jj,3) rho = fld(ii,jj,1) fld(ii,jj,4) = rho*rgas*tmprtr0/(1.0d00-b_vdw*rho) + - a_vdw*rho**2 end do end do c c Sum values across processors c sum(1) = rtot sum(2) = uxtot sum(3) = uytot c call ga_dgop(MT_DBL,sum,3,'+') rtot = sum(1) uxtot = sum(2) uytot = sum(3) c return end ga-5.9.2/global/examples/boltzmann/setup.F000066400000000000000000000060611500715745200204600ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine setup(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) #include "common" c integer ld_fg1, ld_fg2, ld_fld1, ld_fld2, ld_bc1 double precision fg(ld_fg1,ld_fg2, *) double precision fld(ld_fld1, ld_fld2, *) integer bc(ld_bc1, *) integer g_fg, g_fld, g_bc c #include "mafdecls.fh" #include "global.fh" c double precision rho0, ux0, uy0, t_rho, pi integer i, j, ii, jj, me integer ld(NDIM) c c Set simulation parameters c nsteps = 5000 viscosity = 1.0d00 delta_t = 1.0d00 xmax = 256.0 c rho0 = 2.7 tmprtr0 = 1.0 ux0 = 0.0 uy0 = 0.0 t_rho = 1.0d00 uxbc = 0.5 rhobc = rho0 c rgas = 1.0 a_vdw = 0.0d00 b_vdw = 0.0d00 c delta_x = xmax/dble(size(1)-1) cspd = sqrt(2.0d00)*delta_x/delta_t if (ga_nodeid().eq.0) write(6,*) 'Value of tau_rho is ', + 6.0d00*viscosity/(cspd**2*delta_t) + 0.5d00 c c Find low and high indices of locally held data c me = ga_nodeid() call nga_distribution(g_fg, me, lo, hi) c c Initialize boundary array c call ga_zero(g_bc) do jj = width(2) + 1, dims(2) - width(2) j = jj - width(2) - 1 + lo(2) do ii = width(1) + 1, dims(1) - width(1) i = ii - width(1) - 1 + lo(1) if (i.eq.1) then bc(ii,jj) = 1 else if (i.eq.size(1)) then bc(ii,jj) = 1 else if (j.eq.1) then bc(ii,jj) = 1 else if (j.eq.size(2)) then bc(ii,jj) = 2 else bc(ii,jj) = 0 endif c bc(ii,jj) = 0 end do end do call ga_update_ghosts(g_bc) c c Create initial distribution of density and velocities c pi = 4.0d00*atan(1.0d00) rtot = 0.0d00 do jj = width(2) + 1, dims(2) - width(2) j = jj - width(2) - 1 + lo(2) do ii = width(1) + 1, dims(1) - width(1) i = ii - width(1) - 1 + lo(1) fld(ii,jj,1) = rho0 + 0.0*cos(2.0d00*pi*dble(j-1) + / dble(size(2)-1)) fld(ii,jj,2) = ux0 fld(ii,jj,3) = uy0 fld(ii,jj,4) = rho0*rgas*tmprtr0/(1.0d00-b_vdw*rho0) + - a_vdw*rho0**2 fld(ii,jj,5) = 6.0d00*viscosity/(cspd**2*delta_t) + 0.5d00 rtot = rtot + fld(ii,jj,1) end do end do c c initialize lattice parameters c call initpar c c evaluate equilibrium distribution c call equil(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) do jj = width(2)+1, dims(2) - width(2) do j = 1, 9 do ii = width(1)+1, dims(1) - width(1) fg(ii,jj,j) = fg(ii,jj,j+9) end do end do end do call properties(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) c return end ga-5.9.2/global/examples/boltzmann/timestep.F000066400000000000000000000053451500715745200211560ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine timestep(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) #include "common" c integer ld_fg1, ld_fg2, ld_fld1, ld_fld2, ld_bc1 double precision fg(ld_fg1,ld_fg2, *) double precision fld(ld_fld1, ld_fld2, *) integer bc(ld_bc1, *) integer g_fg, g_fld, g_bc c #include "mafdecls.fh" #include "global.fh" #define MP_DEFINES_ONLY #include "mp3.fh" #undef MP_DEFINES_ONLY c c Advance simulation one timestep c integer ii, jj, i, j, k, ix, iy double precision start1, start2 c c Make backup copies of distribution and update ghost cells c start1 = MP_TIMER() do jj = width(2) + 1, dims(2) - width(2) do i = 1, 9 j = i + 18 do ii = width(1) + 1, dims(1) - width(1) fg(ii,jj,j) = fg(ii,jj,i) end do end do end do start2 = MP_TIMER() call ga_mask_sync(.false.,.false.) call ga_update_ghosts(g_fg) tstats(2) = tstats(2) + MP_TIMER() - start2 c c Perform streaming operation c do jj = width(2) + 1, dims(2) - width(2) do ii = width(1) + 1, dims(1) - width(1) if (bc(ii,jj).eq.0) then do i = 2, 9 j = i + 18 k = i - 1 ix = nint(ei(k,1)) iy = nint(ei(k,2)) fg(ii,jj,i) = fg(ii-ix,jj-iy,j) end do else call get_patch(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1, ii, jj) do i = 2, 9 k = i - 1 ix = nint(ei(k,1)) iy = nint(ei(k,2)) fg(ii,jj,i) = fgp(-ix,-iy,i) end do endif end do end do c c Update properties c call properties(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) c c Perform relaxation c call equil(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1) do jj = width(2) + 1, dims(2) - width(2) do i = 1, 9 do ii = width(1) + 1, dims(1) - width(1) if (fld(ii,jj,5).gt.0.0d00) then fg(ii,jj,i) = fg(ii,jj,i+18) + - (fg(ii,jj,i+18)-fg(ii,jj,i+9)) + / fld(ii,jj,5) endif c if (bc(ii,jj).eq.2) then c fg(ii,jj,i) = fg(ii,jj,i+9) c endif end do end do end do c tstats(1) = tstats(1) + MP_TIMER() - start1 c return end ga-5.9.2/global/examples/boltzmann/vorticity.F000066400000000000000000000050171500715745200213540ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine vorticity(g_fg, fg, ld_fg1, ld_fg2, + g_fld, fld, ld_fld1, ld_fld2, + g_bc, bc, ld_bc1, ic, jc) #include "common" c integer ld_fg1, ld_fg2, ld_fld1, ld_fld2, ld_bc1 double precision fg(ld_fg1,ld_fg2, *) double precision fld(ld_fld1, ld_fld2, *) integer bc(ld_bc1, *) integer g_fg, g_fld, g_bc c #include "mafdecls.fh" #include "global.fh" c c Subroutine to evaluate the vorticity of velocity field c double precision drho(2), dux(2), duy(2) double precision dr,dx,dy,adxi,adyi,drxi,dryi,dri,drni,dxi,dyi double precision rhot,uxt,uyt integer ic, jc, i, j, ii, jj c call ga_update_ghosts(g_fld) do jj = width(2) + 1, dims(2) - width(2) do ii = width(1) + 1, dims(1) - width(1) drni = 0.0d00 drxi = 0.0d00 dryi = 0.0d00 do j = 1, 2 drho(j) = 0.0d00 dux(j) = 0.0d00 duy(j) = 0.0d00 end do do j = -1, 1 dy = dble(j)*delta_x if (dy.ne.0.0d00) then dyi = 1.0d00/dy else dyi = 0.0d00 endif do i = -1, 1 if (bc(ii+i,jj+j).eq.0.and.(i.ne.0.or.j.ne.0)) then dx = dble(i)*delta_x if (dx.ne.0.0d00) then dxi = 1.0d00/dx else dxi = 0.0d00 endif dr = sqrt(dx**2+dy**2) dri = 1.0d00/dr adxi = abs(dx*dri**2) adyi = abs(dy*dri**2) c rhot = fld(ii+i,jj+j,1) uxt = fld(ii+i,jj+j,2) uyt = fld(ii+i,jj+j,3) drho(1) = drho(1) + (rhot-fld(ii,jj,1))*dxi*adxi drho(2) = drho(2) + (rhot-fld(ii,jj,1))*dyi*adyi dux(1) = dux(1) + (uxt-fld(ii,jj,2))*dxi*adxi dux(2) = dux(2) + (uxt-fld(ii,jj,2))*dyi*adyi duy(1) = duy(1) + (uyt-fld(ii,jj,3))*dxi*adxi duy(2) = duy(2) + (uyt-fld(ii,jj,3))*dyi*adyi drxi = drxi+adxi dryi = dryi+adyi endif end do end do drho(1) = drho(1)/drxi drho(2) = drho(2)/dryi dux(1) = dux(1)/drxi dux(2) = dux(2)/dryi duy(1) = duy(1)/drxi duy(2) = duy(2)/dryi fld(ii,jj,6) = duy(1)-dux(2) end do end do return end ga-5.9.2/global/examples/conjugate_gradient/000077500000000000000000000000001500715745200210365ustar00rootroot00000000000000ga-5.9.2/global/examples/conjugate_gradient/computeloops.F000066400000000000000000000065001500715745200236770ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine f_matvecmul(a_mat, > mult_vec, > resultvec, > isresultmirrored, > firstrow, > lastrow, > myrowlist, > mycollist) implicit none integer*4 isresultmirrored double precision mult_vec(*) double precision resultvec(*) double precision a_mat(*) integer*4 myrowlist(*),mycollist(*) integer*4 firstrow,lastrow integer*4 i,j,k,l double precision tmprowsum k=0 l=1 do i=firstrow, lastrow-1 k=k+1 write(6,*) k,myrowlist(k),myrowlist(k+1) do j=myrowlist(k), myrowlist(k+1)-1 write(6,*) i,j,k,myrowlist(k+1),l tmprowsum=tmprowsum+a_mat(l)*mult_vec(mycollist(l)+1) l=l+1 enddo resultvec(k)=tmprowsum tmprowsum=0 enddo end subroutine f_computeminverse(minv, > a_mat, > myrowlist, > mycollist, > firstrow, > lastrow) double precision a_mat(*) double precision minv(*) integer*4 myrowlist(*),mycollist(*) integer*4 firstrow,lastrow integer*4 i,j,k,l k=0 l=1 do i=firstrow, lastrow-1 k=k+1 do j=myrowlist(k), myrowlist(k+1)-1 if(mycollist(l) .ge. i) then if(mycollist(l) .eq. i) then minv(k)=10.0/a_mat(l) if(minv(k) .le. 0) minv(k)=1.0/10.0 endif if(mycollist(l)>i) minv(k)=1.0/10.0 l= l+(myrowlist(k+1)-j) go to 100 endif l=l+1 enddo 100 continue enddo end subroutine f_computeminverser(minv, > r, > minvr, > firstrow, > lastrow) double precision minv(*) double precision r(*), minvr(*) integer*4 firstrow,lastrow integer*4 i,k k=1 do i=firstrow, lastrow-1 minvr(k)=minv(k)*r(k) k=k+1 enddo end subroutine f_addvec(scale1, > srcvec1, > scale2, > srcvec2, > dstvec, > firstrow, > lastrow) integer*4 firstrow,lastrow double precision scale1,scale2 double precision srcvec1(*),srcvec2(*) double precision dstvec(*) integer*4 i,k k=1 do i=firstrow, lastrow-1 dstvec(k)=srcvec1(k)*scale1+ srcvec2(k)*scale2 k=k+1 enddo end subroutine f_2addvec(scale1a, > srcvec1a, > scale2a, > srcvec2a, > dstveca, > scale1b, > srcvec1b, > scale2b, > srcvec2b, > dstvecb, > firstrow, > lastrow) integer*4 firstrow,lastrow double precision scale1a,scale2a double precision srcvec1a(*),srcvec2a(*) double precision dstveca(*) double precision scale1b,scale2b double precision srcvec1b(*),srcvec2b(*) double precision dstvecb(*) integer*4 i,k k=1 do i=firstrow, lastrow-1 dstveca(k)=srcvec1a(k)*scale1a+ srcvec2a(k)*scale2a dstvecb(k)=srcvec1b(k)*scale1b+ srcvec2b(k)*scale2b k=k+1 enddo end ga-5.9.2/global/examples/conjugate_gradient/finclude.h000066400000000000000000000013261500715745200230020ustar00rootroot00000000000000#define f_matvecmul F77_FUNC_(f_matvecmul,F_MATVECMUL) #define f_computeminverse F77_FUNC_(f_computeminverse,F_COMPUTEMINVERSE) #define f_computeminverser F77_FUNC_(f_computeminverser,F_COMPUTEMINVERSER) #define f_addvec F77_FUNC_(f_addvec,F_ADDVEC) #define f_2addvec F77_FUNC_(f_2addvec,F_2ADDVEC) extern void f_matvecmul(double*,double*,double*,int*,int*,int*,int*,int*); extern void f_computeminverse(double*,double*,int*,int*,int*,int*); extern void f_computeminverser(double*,double*,double*,int*,int*); extern void f_addvec(double*,double*,double*,double*,double*,int*,int*); extern void f_2addvec(double*,double*,double*,double*,double*,double*,double*,double*,double*,double*,int*,int*); ga-5.9.2/global/examples/conjugate_gradient/ga_cg.c000066400000000000000000000220101500715745200222350ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_FCNTL_H #include #endif #include "armci.h" #include "ga.h" #include "macdecls.h" #include "finclude.h" #include "mp3.h" #define VERIFY_RESULT 1 int na,nz; int bvec,dvec,svec,dmvec,m_dvec,amat,xvec,axvec,rvec,qvec,ridx,cidx; int me, nproc; int myfirstrow=0,mylastrow=0; double epsilon=1e-4; double time_get=0; double *entirexvecptr,*xvecptr,*entiredvecptr,*dvecptr; int isvectormirrored=0; static int niter; void read_and_create(int,char **); void computeminverser(double *,double *, double *); void computeminverse(double *,double *, int *,int *); void finalize_arrays(int); extern void matvecmul(double *,int,double *,int,int *,int *); extern double *ga_vecptr; void conjugate_gradient(int nit,int dopreconditioning) { int i,zero=0; int lo,hi; double d_one=1.0,d_negone=-1.0; double delta0=0.0,deltaold=0.0,deltanew=0.0,alpha=0.0,negalpha,beta,dtransposeq; double *axvecptr,*qvecptr,*aptr,*dmvecptr,*rvecptr,*svecptr,*bvecptr; double time0; int *mycp,*myrp; int j; double sum; NGA_Distribution(cidx,me,&lo,&hi); NGA_Access(cidx,&lo,&hi,&mycp,&zero); NGA_Access(amat,&lo,&hi,&aptr,&zero); NGA_Distribution(ridx,me,&lo,&hi); NGA_Access(ridx,&lo,&hi,&myrp,&zero); NGA_Access(axvec,&lo,&hi,&axvecptr,&zero); NGA_Access(qvec,&lo,&hi,&qvecptr,&zero); NGA_Access(rvec,&lo,&hi,&rvecptr,&zero); NGA_Access(bvec,&lo,&hi,&bvecptr,&zero); if(dopreconditioning){ NGA_Access(dmvec,&lo,&hi,&dmvecptr,&zero); NGA_Access(svec,&lo,&hi,&svecptr,&zero); /* NGA_Distribution(dvec, 0, &lo, &hi); NGA_Access(dvec, &lo, &hi, &entiredvecptr, &zero); */ } printf("\n%d:before matvecmul\n",me);fflush(stdout); /* compute Ax */ f_matvecmul(aptr,entirexvecptr,axvecptr,&zero,&myfirstrow,&mylastrow,myrp,mycp); /* r=b-Ax */ f_addvec(&d_one,bvecptr,&d_negone,axvecptr,rvecptr,&myfirstrow,&mylastrow); if(dopreconditioning){ f_computeminverse(dmvecptr,aptr,myrp,mycp,&myfirstrow,&mylastrow); f_computeminverser(dmvecptr,rvecptr,dvecptr,&myfirstrow,&mylastrow); NGA_Put(dvec,&lo,&hi,dvecptr,&hi); if (me == 0) printf("Doing preconditioning!\n"); } else{ if(me==0){ na--; NGA_Get(rvec,&zero,&na,entiredvecptr,&na); NGA_Put(dvec,&zero,&na,entiredvecptr,&na); na++; } } deltanew = GA_Ddot(dvec,rvec); /* deltanew = r.r_tranpose */ delta0 = deltanew; /* delta0 = deltanew */ if(me==0)printf("\n\tdelta0 is %f\n",delta0); /*if(me==0)printf("\n\titer\tbeta\tdelta");*/ for(i=0;i(1e-8*delta0);i++){ na--; NGA_Get(dvec, &zero, &na, entiredvecptr, &na); na++; if(isvectormirrored) matvecmul(aptr,m_dvec,qvecptr,1,myrp,mycp);/* q = Ad */ else{ f_matvecmul(aptr,entiredvecptr,qvecptr,&zero,&myfirstrow,&mylastrow,myrp,mycp); sum = 0.0; for (j = 0; j < na; j++) if (entiredvecptr[j] != 0.0) sum += entiredvecptr[j]; /* if (me == 0) printf("me: %d, sum: %g\n", me, sum); */ } NGA_Put(dvec,&lo,&hi,dvecptr,&hi); dtransposeq=GA_Ddot(dvec,qvec); /* compute d_transpose.q */ alpha = deltanew/dtransposeq; /* deltanew/(d_transpose.q) */ if(i>10000 && i%25==0){ /* compute Ax*/ f_matvecmul(aptr,entirexvecptr,axvecptr,&zero,&myfirstrow,&mylastrow,myrp,mycp); /* x = x+ alpha.d*/ /* r=b-Ax*/ f_2addvec(&d_one,xvecptr,&alpha,dvecptr,xvecptr,&d_one,bvecptr, &d_negone,axvecptr,rvecptr,&myfirstrow,&mylastrow); } else{ negalpha = 0.0-alpha; /* x = x+ alpha.d*/ /* r=r-alpha.q*/ f_2addvec(&d_one,xvecptr,&alpha,dvecptr,xvecptr,&d_one,rvecptr, &negalpha,qvecptr,rvecptr,&myfirstrow,&mylastrow); } if(dopreconditioning) computeminverser(dmvecptr,rvecptr,svecptr); deltaold = deltanew; /* deltaold = deltanew*/ if(dopreconditioning) deltanew = GA_Ddot(svec,rvec); /* deltanew = r_transpose.r*/ else deltanew = GA_Ddot(rvec,rvec); /* deltanew = r_transpose.r*/ beta = deltanew/deltaold; /* beta = deltanew/deltaold*/ if(dopreconditioning) f_addvec(&d_one,svecptr,&beta,dvecptr,dvecptr,&myfirstrow,&mylastrow); /* d = s + beta.d */ else f_addvec(&d_one,rvecptr,&beta,dvecptr,dvecptr,&myfirstrow,&mylastrow); /* d = r + beta.d */ if(isvectormirrored) GA_Copy(dvec,m_dvec); /*copy from distributed */ /* if(me==0)printf("\n\t%d\t%0.4f\t%f",(i+1),beta,deltanew); */ } if(i < nit && me == 0) printf("\n Done with CG before reaching max iter %f",sqrt(deltanew/delta0)); niter = i; #if VERIFY_RESULT GA_Zero(qvec); GA_Zero(rvec); matvecmul(aptr,xvec,qvecptr,0,myrp,mycp); GA_Add(&d_one,qvec,&d_negone,bvec,rvec); time0=GA_Ddot(rvec,rvec); if(me==0)printf("\n%d:error is %f",me,time0); #endif } void initialize_arrays(int dpc) { double d_one=1.0; int i; GA_Zero(dvec); GA_Fill(xvec,&d_one); GA_Zero(axvec); GA_Zero(rvec); GA_Zero(qvec); if(dpc){ GA_Zero(dmvec); GA_Zero(svec); } for(i=0;i ga_cg.x na nz file"); printf("\n\n where:"); printf("\n\tna is array dimention (only square arrays supported)"); printf("\n\tnz is number of non-zeros"); printf("\n\tfile is either the input file or the word random"); printf("\n\t use the word random if you to use random input"); printf("\n\t input should be in row compressed format"); printf("\n\t file should have matrix a followed by row, col & b (Ax=b)"); printf("\n\t if file also has na and nz, pass them as 0's and the"); printf("\n\t program will read them from the file"); printf("\n\nexample usages are:"); printf("\n\tmpirun -np 4 ./ga_cg.x 5000 80000 /home/me/myinput.dat"); printf("\n\tor"); printf("\n\tmpirun -np 4 ./ga_cg.x 5000 80000 random\n\n"); fflush(stdout); } GA_Terminate(); MP_FINALIZE(); return 0; } heap /= nproc; stack /= nproc; if(! MA_init(MT_F_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ read_and_create(argc,argv); create_entire_vecs(); if(me==0)printf("\nWarmup and initialization run"); initialize_arrays(dopreconditioning); conjugate_gradient(1,dopreconditioning); time_get =0.0; if(me==0)printf("\n\nStarting Conjugate Gradient ...."); initialize_arrays(dopreconditioning); time0=MP_TIMER(); conjugate_gradient(30000/*2*/,dopreconditioning); time1=MP_TIMER(); /* GA_Print(xvec); */ /* GA_Print(dvec); */ if(me==0)printf("\n%d:in %d iterations time to solution=%f-%f\n",me,niter,(time1-time0),time_get); finalize_arrays(dopreconditioning); MP_BARRIER(); if(me==0)printf("Terminating ..\n"); GA_Terminate(); MP_FINALIZE(); return 0; } void finalize_arrays(int dpc) { GA_Destroy(bvec); GA_Destroy(dvec); if(isvectormirrored) GA_Destroy(m_dvec); GA_Destroy(amat); GA_Destroy(xvec); GA_Destroy(axvec); GA_Destroy(rvec); GA_Destroy(qvec); GA_Destroy(ridx); GA_Destroy(cidx); if(dpc){ GA_Destroy(svec); GA_Destroy(dmvec); } ARMCI_Free(myptrarrx[me]); ARMCI_Free(myptrarrd[me]); } ga-5.9.2/global/examples/conjugate_gradient/matvecmul.c000066400000000000000000000040661500715745200232050ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #include "ga.h" #include "macdecls.h" #include "mp3.h" extern int na,nz; extern int me, nproc; extern int myfirstrow,mylastrow; double *ga_vecptr; extern double time_get; void computeminverse(double *minvptr,double *aptr,int *myrowptr,int *mycolptr) { int i,j,k,l=0; for(k=0,i=myfirstrow;i=i){ if(mycolptr[l]==i){ /*printf("\n%d:i=%d j=%d aptr=%d",me,i,j,aptr[l]);*/ minvptr[k]=10.0/aptr[l]; if(minvptr[k]<0)minvptr[k]=1.0; } if(mycolptr[l]>i) minvptr[k]=1.0; /*printf("\n%d:l=%d i=%d mycolptr[l]=%d",me,l,i,mycolptr[l]);*/ l+=(myrowptr[k+1]-j); break; } } } } void computeminverser(double *minvptr,double *rvecptr,double *minvrptr) { int i,k; for(k=0,i=myfirstrow;i #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_STAT_H # include #endif #if HAVE_FCNTL_H # include #endif #if HAVE_STRING_H # include #endif #include "ga.h" #include "macdecls.h" #include "mp3.h" extern int na; extern int nz; extern int dmvec,svec,bvec,dvec,m_dvec,amat,xvec,axvec,rvec,qvec,ridx,cidx; extern int me, nproc; extern int myfirstrow,mylastrow; static int *columnmap,*allfirstrow,*alllastrow; extern double *ga_vecptr; extern int isvectormirrored; static FILE *fd; void generate_random_file(int naa,int nnz){ fd = fopen("randominput.dat", "w"); } void read_and_create(int argc, char **argv) { int ri,i; int ph; double d_one=1.0; double *a,*x; int *icol, *irow; #if 0 int dims[2]; #endif int tmp1,idealelementsperproc; int lo,hi; na = atoi(argv[1]); nz = atoi(argv[2]); if(strncmp("random",argv[3],6)){ if(me==0){ fd = fopen(argv[3], "r"); if(fd==NULL)GA_Error("unable to open given file",0); } } else{ if(na==0 || nz==0){ printf("\nERROR:exiting-no input file given and na or nz is 0"); fflush(stdout); GA_Terminate(); MP_FINALIZE(); return; } if(me==0){ generate_random_file(na,nz); fd = fopen("randominput.dat", "r"); } } if(me==0){ if(na==0) fread(&na, sizeof(na), 1, fd); if(nz==0) fread(&nz, sizeof(nz), 1, fd); printf("\nReading CG input\n"); printf("Number of rows: %d\n", na); printf("Number of non-zeros: %d\n", nz); a = (double *)malloc(sizeof(double)*nz); icol = (int *)malloc(sizeof(int)*(nz+1)); x = (double *)malloc(sizeof(double)*(na+1)); irow = (int *)malloc(sizeof(int)*(na+1)); for (i = 0; i < na + 1; i++) x[i] = 1.0; fread(a, sizeof(double), nz, fd); fread(irow, sizeof(int), na + 1, fd); fread(icol, sizeof(int), nz + 1, fd); fread(x, sizeof(double), na + 1, fd); /* the c adjustment */ for (i = 0; i < na + 1; i++) irow[i] -= 1; for (i = 0; i < nz + 1; i++) icol[i] -= 1; /* MPI_Bcast(&nz,1,MPI_INT,0,MPI_COMM_WORLD); */ GA_Brdcst(&nz, sizeof(int), 0); /* MPI_Bcast(&na,1,MPI_INT,0,MPI_COMM_WORLD); */ GA_Brdcst(&na, sizeof(int), 0); } else { /* MPI_Bcast(&nz,1,MPI_INT,0,MPI_COMM_WORLD); */ GA_Brdcst(&nz, sizeof(int), 0); /* MPI_Bcast(&na,1,MPI_INT,0,MPI_COMM_WORLD); */ GA_Brdcst(&na, sizeof(int), 0); /*for now, others dont need to malloc really*/ a = (double *)malloc(sizeof(double)*nz); icol = (int *)malloc(sizeof(int)*(nz+1)); x = (double *)malloc(sizeof(double)*(na+1)); irow = (int *)malloc(sizeof(int)*(na+1)); if(!a || !icol || !x || !irow)GA_Error("malloc failed in ga_cg",0); } allfirstrow = (int *)malloc(sizeof(int)*nproc); alllastrow = (int *)malloc(sizeof(int)*nproc); columnmap = (int *)malloc(sizeof(int)*nproc); if(!allfirstrow || !alllastrow || !columnmap)GA_Error("malloc failed in ga_cg",0); /* * next decide who works on which rows, this will decide the * distribution of a,d,r,q,x,and ax */ /*create the mapping for all vectors, row matrix and column matrix*/ if(me==0){ idealelementsperproc = nz/nproc; tmp1=0; for(i=0;i=idealelementsperproc){ if((elementsperproc-idealelementsperproc) > idealelementsperproc-(elementsperproc-(irow[ri+1]-irow[ri]))){ alllastrow[i] = ri-1; if((ri-1)<0)GA_Error("run on a smaller processor count",0); tmp1--; } else{ alllastrow[i] = ri; if(ri<0)GA_Error("run on a smaller processor count",0); } elementsperproc=0; break; } } } alllastrow[nproc-1]=na-1; for(i=0;i #endif #if HAVE_STDLIB_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_MATH_H # include #endif #include "ga.h" #include "macdecls.h" #include "mp3.h" #define NATOMS 256 #define BLOCK_SIZE 64 #define DENSITY 1.05 #define TEMPERATURE 2.0 #define TIMESTEP 0.004 #define RESCALE_STEPS 10 #define EQUILIBRIUM_TIME 10.0 #define SIMULATION_TIME 20.0 #define STOP_ITERATING 0 #define DEBUG 0 #define PRINT_LEVEL_1 1 /**< dumps result: level 1 */ #define PRINT_LEVEL_2 1 /**< dumps result: level 2..more results */ #define WRITE_TO_FILE 0 /**< dumps the coordinates in molden viz format */ #define NDIM 3 /**< always 3-d */ #define MAX_PROC 256 #define MAX_BLOCKS 256 #define SAFELIMIT 3 #ifdef MSG_COMMS_MPI # define CLOCK_ MPI_Wtime #else # define CLOCK_ tcg_time #endif /* #define GA_ABS(a) ( ((a) > 0) ? (a) : -(a) ) */ typedef struct { int x; int y; } topo_t; /* box specifications */ typedef struct { double length; double width; double height; } boxSpec_t; int gMemHandle; static int gMe, gNproc; static int g_X; /* Coordinate array */ static int g_G; /* Gradient Array */ static int g_V; /* Velocity Array */ static int g_T; /* Atomic Task: 1-d array whose size = 1 */ static int gBlockSize, gNblocks; static double gDensity, gDesiredTemperature, gTimeStep, gComputeTime=0.0; static topo_t btopo[MAX_BLOCKS]; static boxSpec_t box; static int measurementStep; static double temperature, temperatureSum, temperatureSqdSum; static double pressure, pressureSum, pressureSqdSum; static double potentialEnergySum, potentialEnergySqdSum; static double kineticEnergySum, totalEnergySum, totalEnergySqdSum; /** * To get the next task id. It is an atomic operation. */ static int nxtask(void) { int subscript = 0; return NGA_Read_inc(g_T, &subscript, 1); } /** * Block Topology (of Force Matrix): * Say for example: If there are 4 block and 100 atoms, the size of * the force matrix is 100x100 and each block size is 50x50. * * ----------- * | | | * | 0,0 | 0,1 | * ----------- * | 1,0 | 1,1 | * | | | * ----------- */ void LJ_Setup(int natoms, double **x_i, double **x_j, double **grad) { int i, j, k=0, n; MA_AccessIndex maindex; n = natoms/gBlockSize; /* block topology */ for(i=0; i MAX_BLOCKS) GA_Error("Number of blocks is greater than MAX_BLOCKS(512): Solution is either increase the defined MAX_BLOCKS or increase your block size", 0L); if(gNblocks < gNproc) GA_Error("Number of blocks should be greater than number of processors",0L); } /** * The entire Force matrix is sub-divided into many blocks. * This function gets the next block to be computed by a * process. Once a process finishes a block it gets a new * block to be computed. */ void getBlock(int taskId, int size, double *x_i, double *x_j) { int lo, hi; #if DEBUG printf("%d: new task = %d: topo: %d,%d\n", gMe, taskId, btopo[taskId].x, btopo[taskId].y); #endif /** get the coordinates of the atoms in the corresponding rows in the block */ lo = btopo[taskId].x * size; hi = lo + size -1; NGA_Get(g_X, &lo, &hi, x_i, &hi); /** get the coordinates of the atoms in the corresponding columns in the block */ lo = btopo[taskId].y * size; hi = lo + size -1; NGA_Get(g_X, &lo, &hi, x_j, &hi); } /** * LJ Function Gradient Computation. */ void LJ_FG(int taskId, double *x_i, double *x_j, double *f, double *grad) { int b_x, b_y; /* block topology */ int i, j, start_x, start_y, tempA, tempB; int start_i=0, end_i=0, start_j=0, *end_j=NULL; int sign_x, sign_y, sign_z; double xx, yy, zz, rij, temp,r2,r6,r12, xtmp, ytmp, ztmp; b_x = btopo[taskId].x; b_y = btopo[taskId].y; start_x = gBlockSize * NDIM * btopo[taskId].x; start_y = gBlockSize * NDIM * btopo[taskId].y; if(b_x == b_y) { /* computer lower triagular matrix only */ start_i = 1; end_i = gBlockSize; start_j = 0; end_j = &i; } else if(b_x > b_y) { /* compute right half of the block */ start_i = 0; end_i = gBlockSize; start_j = gBlockSize/2; end_j = &gBlockSize; } else if(b_x < b_y) { /* compute upper half of the block */ start_i = 0; end_i = gBlockSize/2; start_j = 0; end_j = &gBlockSize; } /* Calculating Force exerted on 'i' by 'j' */ for(i=start_i; i< end_i; i++) { xtmp = x_i[NDIM*i]; ytmp = x_i[NDIM*i+1]; ztmp = x_i[NDIM*i+2]; for(j=start_j; j< *end_j; j++) { xx = xtmp - x_j[NDIM*j]; yy = ytmp - x_j[NDIM*j+1]; zz = ztmp - x_j[NDIM*j+2]; sign_x = xx > 0.0 ? 1 : -1; sign_y = yy > 0.0 ? 1 : -1; sign_z = zz > 0.0 ? 1 : -1; /** * Using Nearest Image Approximation in computing the distance between * any 2 atoms: If any component of (Ri-Rj) is greater than L/2, then * there is an image particle located closer which exerts a larger force * (because we use periodic boundary conditions). */ if(xx*sign_x > box.length/2) xx -= sign_x*box.length; if(yy*sign_y > box.width/2) yy -= sign_y*box.width ; if(zz*sign_z > box.height/2) zz -= sign_z*box.height; rij = xx*xx + yy*yy + zz*zz; #if DEBUG if(rij <= 0.0) GA_Error("Divide by Zero Error\n", 0L); #endif r2 = 1.0/rij; r6 = r2*r2*r2; r12 = r6*r6; *f = *f + 4.0*(r12-r6); /* function */ temp = r2 * (48.0 * r12 - 24.0 * r6); tempA = start_x + NDIM*i; tempB = start_y + NDIM*j; grad[tempA] += xx*temp; /* gradient */ grad[tempA+1] += yy*temp; grad[tempA+2] += zz*temp; grad[tempB] -= xx*temp; grad[tempB+1] -= yy*temp; grad[tempB+2] -= zz*temp; } } } /** * Compute Function and Gradient */ void computeFG(double *force, int natoms, double *x_i, double *x_j, double *grad) { int taskId, size, lo, hi, i; double tt; taskId = gMe; size = gBlockSize * NDIM; for(i=0; i 5) { printf("Argc = %d\n", argc); while((n = getopt(argc, argv, "b:")) != EOF) switch (n) { case 'b': gBlockSize = atoi(optarg); } } #endif } void rescaleVelocities (int totalAtoms) { int i, p, lo, hi, ld, natms; double vSqdSum = 0.0, scale; double *v; GA_Sync(); NGA_Distribution(g_V, gMe, &lo, &hi); NGA_Access(g_V, &lo, &hi, &v, &ld); natms = (hi-lo+1)/NDIM; for (p = 0; p < natms; p++) for (i = 0; i < NDIM; i++) vSqdSum += v[p*NDIM + i] * v[p*NDIM + i]; GA_Dgop(&vSqdSum, 1, "+"); scale = NDIM * (totalAtoms) * gDesiredTemperature / vSqdSum; scale = sqrt(scale); for (p = 0; p < natms; p++) for (i = 0; i < NDIM; i++) v[p*NDIM + i] *= scale; GA_Sync(); } double gaussianDistribution() { static int available = 0; static double savedDeviate; double r[2], rSqd, factor; int i; if (available) { available = 0; return savedDeviate; } do { rSqd = 0.0; for (i = 0; i < 2; i++) { r[i] = (2.0 * (double) rand()) / RAND_MAX - 1.0; rSqd += r[i] * r[i]; } } while (rSqd >= 1.0 || rSqd == 0.0); factor = sqrt(-2.0 * log(rSqd) / rSqd); savedDeviate = r[0] * factor; available = 1; return r[1] * factor; } void LJ_Initialize(int natoms) { double L; /* compute the length, width and height of the box */ L = pow(natoms/gDensity, 1.0/3.0); box.length = box.width = box.height = L; #if PRINT_LEVEL_1 if(gMe == 0) { printf("\n"); printf(" =========================================================\n\n"); printf(" Molecular Dynamics Simulation \n\n"); printf(" of \n\n"); printf(" Lennard Jones System \n\n"); printf(" =========================================================\n\n\n"); printf(" Number of Atoms/Particles = %d\n", natoms); printf(" Block Size = %d\n", gBlockSize); printf(" Number of Blocks = %d\n", gNblocks); printf(" Density = %f\n", gDensity); printf(" Temperature = %f\n", gDesiredTemperature); printf(" Time Step = %f\n\n", gTimeStep); printf(" Box Specifications:\n"); printf(" Size of the Cube (Box) = %f\n", box.length); printf(" System Volume = %f\n\n\n", pow(L, 3.0)); } #endif GA_Sync(); // if(gMe == 0) { int c, i, j, k, m, n, p; double b, xSum[3] = {0.0, 0.0, 0.0}; double rFCC[4][3] = {{0.0, 0.0, 0.0}, {0.0, 0.5, 0.5}, {0.5, 0.0, 0.5}, {0.5, 0.5, 0.0}}; double rCell[3]; double *x; int lo, hi, handle; MA_AccessIndex maindex; n = NDIM * natoms + 1; x = (double *) malloc(sizeof(double) * n); /* Use face centered cubic (FCC) lattice for initial positions. Find number of unit cells (c) needed to place all atoms */ for (c = 1; ; c++) if (4*c*c*c >= natoms) break; b = L / c; /* side of unit cell */ p = 0; /* atoms placed so far */ for (i = 0; i < c; i++) { rCell[0] = i * b; for (j = 0; j < c; j++) { rCell[1] = j * b; for (k = 0; k < c; k++) { rCell[2] = k * b; for (m = 0; m < 4; m++) /* 4 particles in cell */ if (p < natoms) { for (n = 0; n < NDIM; n++) /* 3-dimensions - x, y, z */ x[p*NDIM + n] = rCell[n] + b * rFCC[m][n]; ++p; } } } } lo = 0; hi = natoms*NDIM-1; if(gMe == 0) NGA_Put (g_X, &lo, &hi, x, &hi); /* Random Gaussian distribution of initial velocities */ for(i=0; i= b[i]) x[p*NDIM+i] -= b[i]; v[p*NDIM+i] += 0.5 * a[p*NDIM+i] * gTimeStep; } GA_Sync(); } /** * Using Velocity-Verlet Algorithm. */ void LJ_Update_Velocity() { int i, p, lo, hi, ld, natms; double *a, *v; GA_Sync(); NGA_Distribution(g_V, gMe, &lo, &hi); NGA_Access(g_V, &lo, &hi, &v, &ld); NGA_Access(g_G, &lo, &hi, &a, &ld); natms = (hi-lo+1)/NDIM; for (p = 0; p < natms; p++) for (i = 0; i < NDIM; i++) v[p*NDIM+i] += 0.5 * a[p*NDIM+i] * gTimeStep; GA_Sync(); } void initializeTaskArray() { int lo, hi, n; if(gMe ==0) { /* Initialize the task array */ lo = 0; hi = 0; /* only one element */ n = gNproc; /* initial value of the task counter/id */ NGA_Put (g_T, &lo, &hi, &n, &hi); } GA_Sync(); } void solveOneTimeStep(double *potentialEnergy, int natoms, double *x_i, double *x_j, double *grad) { *potentialEnergy = 0.0; LJ_Update(); /* Update the coordinates */ initializeTaskArray(); computeFG(potentialEnergy, natoms, x_i, x_j, grad); LJ_Update_Velocity(); } void writeToFile(int natoms) { static int first_time = 1; static FILE *gOutfile; int n=0, i=0, lo, hi; double *p_data; if(gMe == 0) { if(first_time) { gOutfile = fopen("output.dat", "w"); first_time = 0; } else if(natoms == -1) { fclose(gOutfile); return; } p_data = (double *) malloc(sizeof(double) * natoms * NDIM); lo = 0; hi = natoms * NDIM -1; NGA_Get(g_X, &lo, &hi, p_data, &hi); /* in molden format */ fprintf(gOutfile, "%d\n\n", natoms) ; /* 2 new lines needed */ do { fprintf(gOutfile, "%s %f %f %f\n", "XX", p_data[i], p_data[i+1], p_data[i+2]); i+=NDIM; }while(++n < natoms); free(p_data); } } /** * Lennard Jones Molecular Dynamics: starts here... */ void LJ_Solve(int natoms) { double execTime, potentialEnergy, totalEnergy = 0.0, time=0.0; double *x_i, *x_j, *grad, ip; int s=0; /* Initial Setup */ LJ_Setup(natoms, &x_i, &x_j, &grad); initializeTaskArray(); #if WRITE_TO_FILE writeToFile(natoms); #endif execTime = CLOCK_(); potentialEnergy = 0.0; computeFG(&potentialEnergy, natoms, x_i, x_j, grad); initializeProperties(); computeProperties(natoms, potentialEnergy, &totalEnergy); #if WRITE_TO_FILE writeToFile(natoms); #endif if(gMe == 0) printf("Equilibrium Steps:\n"); if(gMe == 0) printf("Time = %.3f \tEnergy = %f\n", time, totalEnergy); /* Equilibrium Steps */ while(time < EQUILIBRIUM_TIME) { solveOneTimeStep(&potentialEnergy, natoms, x_i, x_j, grad); computeProperties(natoms, potentialEnergy, &totalEnergy); #if WRITE_TO_FILE writeToFile(natoms); #endif time += gTimeStep; if(gMe == 0 && modf(time,&ip) < gTimeStep) printf("Time = %.2f \tEnergy = %f\n", ip, totalEnergy); if(++s >= RESCALE_STEPS) { rescaleVelocities(natoms); s = 0; } } /* Actual Production Steps */ if(gMe == 0) printf("Production Steps:\n"); initializeProperties(); while(time < EQUILIBRIUM_TIME + SIMULATION_TIME) { solveOneTimeStep(&potentialEnergy, natoms, x_i, x_j, grad); computeProperties(natoms, potentialEnergy, &totalEnergy); #if WRITE_TO_FILE writeToFile(natoms); #endif time += gTimeStep; if(gMe == 0 && modf(time,&ip) < gTimeStep) printf("Time = %.2f \tEnergy = %f\n", ip, totalEnergy); } #if WRITE_TO_FILE writeToFile(-1); #endif if(gMe == 0) printProperties(natoms); #if PRINT_LEVEL_2 if(gMe == 0) { execTime = CLOCK_()-execTime; printf("%d: Total Elapsed Time = %f\n", gMe, execTime); printf("%d: Computation Time = %f\n", gMe, gComputeTime); printf("%d: Percentage Overhead = %f\n\n", gMe, 100*(execTime-gComputeTime)/execTime); } #endif free(x_i); } /** * main(int argc, char **argv) */ int main(int argc, char **argv) { int heap=4000000, stack=4000000; int natoms=NATOMS; int dims[NDIM]; MP_INIT(argc, argv); /** * Initialize Global Arrays. */ GA_Initialize_args(&argc, &argv); gMe = GA_Nodeid(); gNproc = GA_Nnodes(); heap /= gNproc; stack /= gNproc; if(! MA_init(C_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ /** * Parse the command line and check the initial conditions (and assumptions) */ commandLine(argc, argv); check(natoms); /** * Create coordinate(x), gradient(g) and velocity(v) vectors */ dims[0] = natoms*NDIM; g_X = NGA_Create(C_DBL, 1, dims, "Coordinate Array - X", NULL); g_G = GA_Duplicate (g_X, "Gradient"); g_V = GA_Duplicate (g_X, "Velocity"); /** * Molecular Dynamics of the Lennard Jones(lj) clusters. * (molecular conformation problem). */ LJ_Initialize(natoms); LJ_Solve(natoms); /** * Deaalocate the arrays and free the resources */ GA_Destroy(g_X); GA_Destroy(g_G); GA_Destroy(g_V); GA_Destroy(g_T); /** * Termination signal to release the resources, etc. */ GA_Terminate(); #ifdef MSG_COMMS_MPI MPI_Finalize(); #else tcg_pend(); #endif return 0; } ga-5.9.2/global/examples/md_cluster/000077500000000000000000000000001500715745200173435ustar00rootroot00000000000000ga-5.9.2/global/examples/md_cluster/atom.inp000066400000000000000000000001101500715745200210030ustar00rootroot000000000000001.0 1.0 1.0 1.0 3 1, 1, 0, 1.0, 1.0 2, 2, 0, 1.0, 1.0 1, 2, 1, 1.0, 1.0 ga-5.9.2/global/examples/md_cluster/cell_list.F000066400000000000000000000360511500715745200214310ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine cell_list #include "common.fh" c integer MAXCL,MSKIN parameter (MAXCL=50,MSKIN=5) double precision xu,yu,zu,xl,yl,zl double precision rct2sd,rbcut,rbcut2 double precision r2(MAXAT),dx(MAXAT),dy(MAXAT),dz(MAXAT) double precision br2(MAXAT),bdx(MAXAT),bdy(MAXAT),bdz(MAXAT) double precision xcl,ycl,zcl,xmax,ymax,zmax,xmin,ymin,zmin double precision rix,riy,riz double precision tbeg,wraptime integer link(MAXAT) integer ltop(-MSKIN:MAXCL+MSKIN,-MSKIN:MAXCL+MSKIN, + -MSKIN:MAXCL+MSKIN) integer btop(-MSKIN:MAXCL+MSKIN,-MSKIN:MAXCL+MSKIN, + -MSKIN:MAXCL+MSKIN),blink(MAXAT) integer acml(MAXAT),bacml(MAXAT) integer kxmax,kymax,kzmax,kc,kx,ky,kz,jx,jy,jz,ltot,bltot integer i,j,k,ipx,ipy,ipz,nlx,nly,nlz,ix,iy,iz integer idx,idy,idz,pnum,inode,ll,kk,l,lb,jj double precision rchk c c This subroutine locates the nearest neighbors of each atom c using a link cell algorithm and constructs a neighbor list c tbeg = wraptime() c pnum = ga_nnodes() call factor(pnum,idx,idy,idz) inode = ga_nodeid() call i_proc_to_xyz(inode,ipx,ipy,ipz,idx,idy,idz) c c determine the size of each spatial decomposition zone c and then use this to divide each zone into cells c rcell = 1.1d00 rbcut = 1.5d00 rchk = 0.0d00 c rbcut2 = rbcut**2 xcl = xbox / dble(idx) ycl = ybox / dble(idy) zcl = zbox / dble(idz) if (int(xcl/rcell).ge.MAXCL) then rcell = xcl/(dble(MAXCL)) + 0.1 endif if (int(ycl/rcell).ge.MAXCL) then rcell = ycl/(dble(MAXCL)) + 0.1 endif if (int(zcl/rcell).ge.MAXCL) then rcell = zcl/(dble(MAXCL)) + 0.1 endif nlx = int(xcl/rcell) nly = int(ycl/rcell) nlz = int(zcl/rcell) xcl = xcl / dble(nlx) ycl = ycl / dble(nly) zcl = zcl / dble(nlz) c c If system is small, just use brute force neighbor list construction c and skip the link cell algorithm c ix = int(rcmax/xcl) if (rcmax-xcl*dble(ix).gt.0.0d00) ix = ix+1 iy = int(rcmax/ycl) if (rcmax-ycl*dble(iy).gt.0.0d00) iy = iy+1 iz = int(rcmax/zcl) if (rcmax-zcl*dble(iz).gt.0.0d00) iz = iz+1 if ((idx.eq.2.and.xcl*dble(ix).ge.0.5d00*xbox/dble(idx)).or. + (idy.eq.2.and.ycl*dble(iy).ge.0.5d00*ybox/dble(idy)).or. + (idz.eq.2.and.zcl*dble(iz).ge.0.5d00*zbox/dble(idz))) then call small_list return endif c c find link cells c call getcell c c find location of cell edges c xmax = xbox * dble(ipx+1)/dble(idx) ymax = ybox * dble(ipy+1)/dble(idy) zmax = zbox * dble(ipz+1)/dble(idz) xmax = xmax - xbox2 ymax = ymax - ybox2 zmax = zmax - zbox2 xmin = xbox * dble(ipx)/dble(idx) ymin = ybox * dble(ipy)/dble(idy) zmin = zbox * dble(ipz)/dble(idz) xmin = xmin - xbox2 ymin = ymin - ybox2 zmin = zmin - zbox2 c c calculate link cell indices. Start by setting all c elements of ltop() equal to 0. c if (nlx.ge.MAXCL) then write(6,105) ga_nodeid(),istep write(6,*) 'NLX = ',nlx,' ON NODE',ga_nodeid() call ga_error("Out of bound cell index",0) elseif (nly.ge.MAXCL) then write(6,105) ga_nodeid(),istep write(6,*) 'NLY = ',nly,' ON NODE',ga_nodeid() call ga_error("Out of bound cell index",0) elseif (nlz.ge.MAXCL) then write(6,105) ga_nodeid(),istep write(6,*) 'NLZ = ',nlz,' ON NODE',ga_nodeid() call ga_error("Out of bound cell index",0) endif do i = -MSKIN, nlx+MSKIN do j = -MSKIN, nly+MSKIN do k = -MSKIN, nlz+MSKIN ltop(i,j,k) = 0 btop(i,j,k) = 0 end do end do end do 105 format(i5,' Out of bound failure at step ',i8) c c find leading index for each cell and store it in c ltop(). Store link list in list(). Store atoms c in both local and buffer arrays. c do i = 1, antot ix = int((ra(i,1,1)-xmin)/xcl)+1 iy = int((ra(i,2,1)-ymin)/ycl)+1 iz = int((ra(i,3,1)-zmin)/zcl)+1 j = ltop(ix,iy,iz) ltop(ix,iy,iz) = i link(i) = j #if 0 if (ix.lt.1.or.ix.gt.nlx.or.iy.lt.1.or.iy.gt.nly.or. + iz.lt.1.or.iz.gt.nlz) then write(6,*) 'i = ',i write(6,*) 'antot = ',antot write(6,*) 'atot = ',atot write(6,*) 'ra(i,1,1) = ',ra(i,1,1) write(6,*) 'ra(i,2,1) = ',ra(i,2,1) write(6,*) 'ra(i,3,1) = ',ra(i,3,1) write(6,*) 'xmin = ',xmin write(6,*) 'ymin = ',ymin write(6,*) 'zmin = ',zmin write(6,*) 'xcl = ',xcl write(6,*) 'ycl = ',ycl write(6,*) 'zcl = ',zcl write(6,*) 'ix ',ix,nlx write(6,*) 'iy ',iy,nly write(6,*) 'iz ',iz,nlz write(6,105) ga_nodeid(),istep write(6,*) 'OUT OF BOUNDS AT 1 ON NODE ',ga_nodeid() call ga_error("Out of bounds error",0) endif #endif end do do i = 1, btot if ((xcrd(i).lt.xmin.and.xmin-xcrd(i).lt.rcmax).or. + (xcrd(i).gt.xmax.and.xcrd(i)-xmax.gt.rcmax)) then xl = xmin - xcrd(i) xl = xl - xbox * anint(xl/xbox) ix = -int(xl/xcl) elseif ((xcrd(i).ge.xmax.and.xcrd(i)-xmax.lt.rcmax).or. + (xcrd(i).lt.xmin.and.xmin-xcrd(i).gt.rcmax)) then xu = xcrd(i) - xmax xu = xu - xbox * anint(xu/xbox) ix = nlx + int(xu/xcl) + 1 else ix = int((xcrd(i)-xmin)/xcl)+1 endif if ((ycrd(i).lt.ymin.and.ymin-ycrd(i).lt.rcmax).or. + (ycrd(i).gt.ymax.and.ycrd(i)-ymax.gt.rcmax)) then yl = ymin - ycrd(i) yl = yl - ybox * anint(yl/ybox) iy = -int(yl/ycl) elseif ((ycrd(i).ge.ymax.and.ycrd(i)-ymax.lt.rcmax).or. + (ycrd(i).lt.ymin.and.ymin-ycrd(i).gt.rcmax)) then yu = ycrd(i) - ymax yu = yu - ybox * anint(yu/ybox) iy = nly + int(yu/ycl) + 1 else iy = int((ycrd(i)-ymin)/ycl)+1 endif if ((zcrd(i).lt.zmin.and.zmin-zcrd(i).lt.rcmax).or. + (zcrd(i).gt.zmax.and.zcrd(i)-zmax.gt.rcmax)) then zl = zmin - zcrd(i) zl = zl - zbox * anint(zl/zbox) iz = -int(zl/zcl) elseif ((zcrd(i).ge.zmax.and.zcrd(i)-zmax.lt.rcmax).or. + (zcrd(i).lt.zmin.and.zmin-zcrd(i).gt.rcmax)) then zu = zcrd(i) - zmax zu = zu - zbox * anint(zu/zbox) iz = nlz + int(zu/zcl) + 1 else iz = int((zcrd(i)-zmin)/zcl)+1 endif j = btop(ix,iy,iz) btop(ix,iy,iz) = i blink(i) = j #if 0 if (ix.ge.1.and.ix.le.nlx.and.iy.ge.1.and.iy.le.nly.and. + iz.ge.1.and.iz.le.nlz) then write(6,105) ga_nodeid(),istep write(6,*) 'OUT OF BOUNDS AT 2 ON NODE ',ga_nodeid() call ga_error("Out of bounds error",0) endif #endif end do c c Link cell lists are complete. Now find neighbors and construct c neighbor lists. c c Primary loop over all local atoms c ll = 1 kk = 1 do i = 1, antot c c Locate all atoms in both the local and buffer lists that c interact with atom i. Start by collecting all atoms in the c same cell c rix = ra(i,1,1) riy = ra(i,2,1) riz = ra(i,3,1) c l = 0 lb = 0 j = link(i) if (j.gt.0) then 99 l = l + 1 acml(l) = j dx(l) = ra(j,1,1) dy(l) = ra(j,2,1) dz(l) = ra(j,3,1) j = link(j) if (j.gt.0) go to 99 endif c c Collect atoms in half of the adjacent cells. c Find indices of cell containing atom i c ix = int((ra(i,1,1)-xmin)/xcl)+1 iy = int((ra(i,2,1)-ymin)/ycl)+1 iz = int((ra(i,3,1)-zmin)/zcl)+1 c c loop over neighboring cells c do kc = 1, kcmax jx = ix + nix(kc) if (jx.gt.nlx.and.idx.eq.1) jx = jx - nlx if (jx.lt.1.and.idx.eq.1) jx = nlx + jx jy = iy + niy(kc) if (jy.gt.nly.and.idy.eq.1) jy = jy - nly if (jy.lt.1.and.idy.eq.1) jy = nly + jy jz = iz + niz(kc) if (jz.gt.nlz.and.idz.eq.1) jz = jz - nlz if (jz.lt.1.and.idz.eq.1) jz = nlz + jz if ((jx.ge.1.and.jx.le.nlx).and. + (jy.ge.1.and.jy.le.nly).and. + (jz.ge.1.and.jz.le.nlz)) then c c neighboring cell is local c j = ltop(jx,jy,jz) c c gather atoms in neighboring cell c if (j.gt.0) then 199 l = l + 1 acml(l) = j dx(l) = ra(j,1,1) dy(l) = ra(j,2,1) dz(l) = ra(j,3,1) j = link(j) if (j.gt.0) go to 199 endif else c c neighboring cell is in buffer c j = btop(jx,jy,jz) c c gather atoms in neighboring cell c if (j.gt.0) then 299 lb = lb + 1 bacml(lb) = j bdx(lb) = xcrd(j) bdy(lb) = ycrd(j) bdz(lb) = zcrd(j) j = blink(j) if (j.gt.0) go to 299 endif endif end do ltot = l bltot = lb c do l = 1, ltot dx(l) = rix - dx(l) dx(l) = dx(l) - xbox * anint(dx(l)/xbox) dy(l) = riy - dy(l) dy(l) = dy(l) - ybox * anint(dy(l)/ybox) dz(l) = riz - dz(l) dz(l) = dz(l) - zbox * anint(dz(l)/zbox) r2(l) = dx(l)**2 + dy(l)**2 + dz(l)**2 end do do lb = 1, bltot bdx(lb) = rix - bdx(lb) bdx(lb) = bdx(lb) - xbox * anint(bdx(lb)/xbox) bdy(lb) = riy - bdy(lb) bdy(lb) = bdy(lb) - ybox * anint(bdy(lb)/ybox) bdz(lb) = riz - bdz(lb) bdz(lb) = bdz(lb) - zbox * anint(bdz(lb)/zbox) br2(lb) = bdx(lb)**2 + bdy(lb)**2 + bdz(lb)**2 end do if (ltot.gt.antot.or.bltot.gt.btot) then write(6,*) 'NODE ',ga_nodeid(),ltot,bltot,antot,btot stop endif c c accumulate all atoms within an interaction distance of c atom i c nafirst(i) = ll bafirst(i) = kk do 300 j = 1, ltot jj = acml(j) rct2sd = (acut(at(i),at(jj))+1.0d00)**2 if (rct2sd.gt.rchk) rchk = rct2sd c if (r2(j).gt.rct2sd) go to 300 c if (atml(i).eq.atml(jj)) then c if (r2(j).le.rbcut2) then c blist(kk) = jj c kk = kk + 1 c endif c go to 300 c endif c nblist(ll) = jj ll = ll + 1 300 continue nalast(i) = ll - 1 nbfirst(i) = ll balast(i) = kk - 1 bbfirst(i) = kk do 400 j = 1, bltot jj = bacml(j) rct2sd = (acut(at(i),bat(jj))+1.0d00)**2 if (rct2sd.gt.rchk) rchk = rct2sd if (br2(j).gt.rct2sd) go to 400 c if (atml(i).eq.batml(jj)) then c if (br2(j).le.rbcut2) then c blist(kk) = jj c kk = kk + 1 c endif c go to 400 c endif c nblist(ll) = jj ll = ll + 1 400 continue nblast(i) = ll - 1 bblast(i) = kk - 1 end do tmstat(23) = tmstat(23) + wraptime() - tbeg c return end c subroutine getcell #include "common.fh" c c This subroutine finds the total number of nearest of cells c required by the link-cell routine c double precision xcell,ycell,zcell,cx,cy,cz,rc,rcmax2 integer kxmin,kymin,kzmin,kxmax,kymax,kzmax,kc,kx,ky,kz integer pnum,idx,idy,idz,nlx,nly,nlz c pnum = ga_nnodes() call factor(pnum,idx,idy,idz) c xcell = xbox / dble(idx) ycell = ybox / dble(idy) zcell = zbox / dble(idz) nlx = int(xcell/rcell) nly = int(ycell/rcell) nlz = int(zcell/rcell) xcell = xcell / dble(nlx) ycell = ycell / dble(nly) zcell = zcell / dble(nlz) c kxmax = int(rcmax/xcell)+1 kymax = int(rcmax/ycell)+1 kzmax = int(rcmax/zcell)+1 c rcmax2 = rcmax**2 c kc = 0 do kx = 0, kxmax kymin = -kymax if (kx.eq.0) kymin = 0 do ky = kymin, kymax kzmin = -kzmax if (kx.eq.0.and.ky.eq.0) kzmin = 1 do kz = kzmin, kzmax if (kx.eq.0) then cx = 0.0d00 else cx = xcell*dble(kx-1) endif if (ky.gt.0) then cy = ycell*dble(ky-1) elseif (ky.lt.0) then cy = ycell*dble(-ky-1) else cy = 0.0d00 endif if (kz.gt.0) then cz = zcell*dble(kz-1) elseif (kz.lt.0) then cz = zcell*dble(-kz-1) else cz = 0.0d00 endif rc = cx**2+cy**2+cz**2 if (rc.lt.rcmax2) then kc = kc + 1 nix(kc) = kx niy(kc) = ky niz(kc) = kz endif end do end do end do kcmax = kc return end c subroutine small_list #include "common.fh" c integer MAXCL,MSKIN parameter (MAXCL=50,MSKIN=5) double precision rcmax2 double precision r2,dx,dy,dz double precision rix,riy,riz,rjx,rjy,rjz double precision tbeg,wraptime integer acml(MAXAT),bacml(MAXAT) integer i,j,ll c c This subroutine locates the nearest neighbors of each atom c using a direct search over all pairs. It is used to construct c the neighbor list for small systems where the link cell c algorithm may fail. c tbeg = wraptime() c c Find neighbors and construct neighbor lists. c Loop over all local atoms c rcmax2 = rcmax**2 ll = 1 do i = 1, antot c c Locate all atoms in both the local and buffer lists that c interact with atom i. c rix = ra(i,1,1) riy = ra(i,2,1) riz = ra(i,3,1) nafirst(i) = ll do j = i+1, antot rjx = ra(j,1,1) rjy = ra(j,2,1) rjz = ra(j,3,1) dx = rix - rjx dx = dx - xbox * anint(dx/xbox) dy = riy - rjy dy = dy - ybox * anint(dy/ybox) dz = riz - rjz dz = dz - zbox * anint(dz/zbox) r2 = dx**2 + dy**2 + dz**2 if (r2.le.rcmax2) then nblist(ll) = j ll = ll + 1 endif end do nalast(i) = ll - 1 nbfirst(i) = ll do j = 1, btot if (bidx(j).gt.aidx(i)) then rjx = xcrd(j) rjy = ycrd(j) rjz = zcrd(j) dx = rix - rjx dx = dx - xbox * anint(dx/xbox) dy = riy - rjy dy = dy - ybox * anint(dy/ybox) dz = riz - rjz dz = dz - zbox * anint(dz/zbox) r2 = dx**2 + dy**2 + dz**2 if (r2.le.rcmax2) then nblist(ll) = j ll = ll + 1 endif endif end do nblast(i) = ll - 1 end do c tmstat(23) = tmstat(23) + wraptime() - tbeg c return end ga-5.9.2/global/examples/md_cluster/cl_sim.F000066400000000000000000000127371500715745200207320ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine cl_sim(natom1,natom2,task) #include "common.fh" c double precision tbeg,wraptime integer i,j,nprocs,ndim,me,task integer heap, stack, natom1, natom2 integer blocks(2),map0(MD_MAXPROC) integer map1(MD_MAXPROC),map2(MD_MAXPROC+1) character*32 filename logical status c c This is the main calling program for the Molecular Dynamics c calculation. c c set io control flags c l_stdio = .true. l_rad = .false. l_rst = .false. l_step = .true. l_oldcfg = .false. c do i = 1, MAXTIM tmstat(i) = 0.0d00 end do tbeg = wraptime() c c create communication arrays c nprocs = ga_nnodes() me = ga_nodeid() c c create map and block arrays to use irregular distributions c map2(1) = 1 blocks(1) = 1 blocks(2) = nprocs do i = 1, nprocs map0(i) = i map1(i) = (i-1)*MAXAT + 1 map2(i+1) = (i-1)*MAXAT + 1 end do c ndim = 1 g_size = ga_create_handle() call ga_set_data(g_size,ndim,nprocs,MT_INT) call ga_set_irreg_distr(g_size,map0,blocks(2)) c call ga_set_chunk(g_size,ndim) status = ga_allocate(g_size) c g_coords = ga_create_handle() ndim = 2 gcoords_hi(1) = 3 gcoords_hi(2) = MAXAT*nprocs call ga_set_data(g_coords,ndim,gcoords_hi,MT_DBL) call ga_set_irreg_distr(g_coords,map2,blocks) c gcoords_hi(2) = MAXAT c call ga_set_chunk(g_coords,gcoords_hi) status = ga_allocate(g_coords) c g_frc = ga_create_handle() ndim = 2 gfrc_hi(1) = 3 gfrc_hi(2) = MAXAT*nprocs call ga_set_data(g_frc,ndim,gfrc_hi,MT_DBL) call ga_set_irreg_distr(g_frc,map2,blocks) c gfrc_hi(2) = MAXAT c call ga_set_chunk(g_frc,gfrc_hi) status = ga_allocate(g_frc) c g_acc = ga_create_handle() ndim = 2 gacc_hi(1) = 3 gacc_hi(2) = MAXAT*nprocs call ga_set_data(g_acc,ndim,gacc_hi,MT_DBL) call ga_set_irreg_distr(g_acc,map2,blocks) c gacc_hi(2) = MAXAT c call ga_set_chunk(g_acc,gacc_hi) status = ga_allocate(g_acc) c g_index = ga_create_handle() ndim = 1 gindex_hi = MAXAT*nprocs call ga_set_data(g_index,ndim,gindex_hi,MT_INT) call ga_set_irreg_distr(g_index,map1,blocks(2)) c gindex_hi = MAXAT c call ga_set_chunk(g_index,gindex_hi) status = ga_allocate(g_index) c g_rvec = ga_create_handle() ndim = 1 grvec_hi = MAXAT*nprocs call ga_set_data(g_rvec,ndim,grvec_hi,MT_DBL) call ga_set_irreg_distr(g_rvec,map1,blocks(2)) c grvec_hi = MAXAT c call ga_set_chunk(g_rvec,grvec_hi) status = ga_allocate(g_rvec) c g_iat = ga_create_handle() ndim = 2 giat_hi(1) = 2 giat_hi(2) = MAXAT*nprocs call ga_set_data(g_iat,ndim,giat_hi,MT_INT) call ga_set_irreg_distr(g_iat,map2,blocks) c giat_hi(2) = MAXAT c call ga_set_chunk(g_iat,giat_hi) status = ga_allocate(g_iat) c c set up arrays for describing data held by each processor c do i = 0, nprocs-1 gsize_lo(i) = 0 gindex_lo(i) = 0 grvec_lo(i) = 0 do j = 1, 2 gcoords_lo(j,i) = 0 gfrc_lo(j,i) = 0 gacc_lo(j,i) = 0 giat_lo(j,i) = 0 end do end do call nga_distribution(g_size,me,gsize_lo(me),gsize_hi) call ga_igop(1,gsize_lo,nprocs,'+') call nga_distribution(g_coords,me,gcoords_lo(1,me),gcoords_hi) call ga_igop(2,gcoords_lo,2*nprocs,'+') call nga_distribution(g_frc,me,gfrc_lo(1,me),gfrc_hi) call ga_igop(3,gfrc_lo,2*nprocs,'+') call nga_distribution(g_acc,me,gacc_lo(1,me),gacc_hi) call ga_igop(4,gacc_lo,2*nprocs,'+') call nga_distribution(g_index,me,gindex_lo(me),gindex_hi) call ga_igop(5,gindex_lo,nprocs,'+') call nga_distribution(g_rvec,me,grvec_lo(me),grvec_hi) call ga_igop(6,grvec_lo,nprocs,'+') call nga_distribution(g_iat,me,giat_lo(1,me),giat_hi) call ga_igop(7,giat_lo,2*nprocs,'+') c c Read in variables and initial configuration c task_id = task if (me.eq.0.and.l_stdio) then if (task.lt.10) then write(filename,100) task else if (task.ge.10.and.task.lt.100) then write(filename,101) task else if (task.ge.100.and.task.lt.1000) then write(filename,102) task else if (task.ge.1000.and.task.lt.10000) then write(filename,103) task endif 100 format('md.out',i1) 101 format('md.out',i2) 102 format('md.out',i3) 103 format('md.out',i4) open(unit=6,file=filename,status='unknown') endif if (me.eq.0.and.l_rad) + open(unit=7,file='rad.dat',status='unknown') call rdpar call atomin r_cluster = 0.0d00 if (.not.l_oldcfg) then if (me.eq.0) call newcfg(natom1,natom2,task,r_cluster,rcut) endif call ga_dgop(3,r_cluster,1,'+') call rdcfg call shuffle c c Initialize MD calculations c call mdinit c c Begin MD calculation c call mdstep tmstat(1) = tmstat(1) + wraptime() - tbeg call mdout c c Close out calculation c if (l_stdio) close(6) if (l_rad) close(7) status = ga_destroy(g_size) status = ga_destroy(g_coords) status = ga_destroy(g_frc) status = ga_destroy(g_acc) status = ga_destroy(g_index) status = ga_destroy(g_rvec) status = ga_destroy(g_iat) c return end ga-5.9.2/global/examples/md_cluster/cluster.F000066400000000000000000001235351500715745200211440ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine cluster_com #include "common.fh" c integer i double precision masstot, dx, dy, dz double precision r,rdot,com(10) c c This subroutine calculates the center of mass (COM) of the cluster of c particles of type 2, excluding the lone particle. c if (nocluster) return cl_cmx = 0.0d00 cl_cmy = 0.0d00 cl_cmz = 0.0d00 cl_acmx = 0.0d00 cl_acmy = 0.0d00 cl_acmz = 0.0d00 cl_vcmx = 0.0d00 cl_vcmy = 0.0d00 cl_vcmz = 0.0d00 masstot = 0.0d00 c do i = 1, antot c if (at(i).eq.2.and.aidx(i).ne.cl_lone_particle) then if (at(i).eq.2) then cl_cmx = cl_cmx + mass(i)*ra(i,1,6) cl_cmy = cl_cmy + mass(i)*ra(i,2,6) cl_cmz = cl_cmz + mass(i)*ra(i,3,6) cl_acmx = cl_acmx + ra(i,1,4) cl_acmy = cl_acmy + ra(i,2,4) cl_acmz = cl_acmz + ra(i,3,4) cl_vcmx = cl_vcmx + mass(i)*ra(i,1,2) cl_vcmy = cl_vcmy + mass(i)*ra(i,2,2) cl_vcmz = cl_vcmz + mass(i)*ra(i,3,2) masstot = masstot + mass(i) endif end do com(1) = cl_cmx com(2) = cl_cmy com(3) = cl_cmz com(4) = cl_vcmx com(5) = cl_vcmy com(6) = cl_vcmz com(7) = cl_acmx com(8) = cl_acmy com(9) = cl_acmz com(10) = masstot call ga_dgop(3,com,10,'+') cl_cmx = com(1) cl_cmy = com(2) cl_cmz = com(3) cl_acmx = com(7) cl_acmy = com(8) cl_acmz = com(9) cl_vcmx = com(4) cl_vcmy = com(5) cl_vcmz = com(6) masstot = com(10) if (masstot.gt.0.0d00) then cl_cmx = cl_cmx / masstot cl_cmy = cl_cmy / masstot cl_cmz = cl_cmz / masstot cl_acmx = cl_acmx / masstot cl_acmy = cl_acmy / masstot cl_acmz = cl_acmz / masstot cl_vcmx = cl_vcmx / masstot cl_vcmy = cl_vcmy / masstot cl_vcmz = cl_vcmz / masstot endif cl_mass = masstot c return end c subroutine cluster_therm #include "common.fh" c integer i double precision dx, dy, dz double precision r,rdot c c remove component of velocity that lies along the vector from COM c to the particle, if particle is outside cluster radius. This gradually c forces particles to form a drop. c if (nocluster) return if (istep.ge.equil_1) return call cluster_com do i = 1, antot c if (at(i).eq.2.and.aidx(i).ne.cl_lone_particle) then if (at(i).eq.2) then dx = ra(i,1,1) - cl_cmx dy = ra(i,2,1) - cl_cmy dz = ra(i,3,1) - cl_cmz r = sqrt(dx**2 + dy**2 + dz**2) dx = dx/r dy = dy/r dz = dz/r if (r.gt.r_cluster) then rdot = ra(i,1,2)*dx+ra(i,2,2)*dy+ra(i,3,2)*dz if (rdot.gt.0.0d00) then ra(i,1,2) = ra(i,1,2) - 2.0d00*rdot*dx ra(i,2,2) = ra(i,2,2) - 2.0d00*rdot*dy ra(i,3,2) = ra(i,3,2) - 2.0d00*rdot*dz endif endif endif end do c return end c subroutine cluster_center #include "common.fh" c c move center of mass to origin (only perform this if it is immediately c followed by a call to update subroutine) c integer i,me logical debug if (nocluster) return if (istep.eq.0.or.(mod(istep,ilist).eq.0.and. + t_rmndr.eq.0.0d00)) then me = ga_nodeid() if (istep.ge.6932366) then debug = .false. else debug = .false. endif if (debug) then write(6,*) me,' mod(istep,ilist) = ',mod(istep,ilist) write(6,*) me,' ilist = ',ilist endif call cluster_com if (debug) write(6,*) me,'cl_cmx (a) = ',cl_cmx,istep if (debug) write(6,*) me,'cl_cmy (a) = ',cl_cmy,istep if (debug) write(6,*) me,'cl_cmz (a) = ',cl_cmz,istep do i = 1, antot ra(i,1,6) = ra(i,1,6) - cl_cmx ra(i,2,6) = ra(i,2,6) - cl_cmy ra(i,3,6) = ra(i,3,6) - cl_cmz end do call fixper call cluster_com if (debug) write(6,*) me,'cl_cmx (b) = ',cl_cmx,istep if (debug) write(6,*) me,'cl_cmy (b) = ',cl_cmy,istep if (debug) write(6,*) me,'cl_cmz (b) = ',cl_cmz,istep endif return end c subroutine cluster_old_at #include "common.fh" c c store original coordinates of cluster atoms c integer i, icnt, jcnt if (nocluster) return icnt = 0 jcnt = 0 do i = 1, antot if (at(i).eq.2) then icnt = icnt + 1 cl_at(icnt) = i cl_old(icnt,1,1) = ra(i,1,1) cl_old(icnt,2,1) = ra(i,2,1) cl_old(icnt,3,1) = ra(i,3,1) cl_old(icnt,1,2) = ra(i,1,6) cl_old(icnt,2,2) = ra(i,2,6) cl_old(icnt,3,2) = ra(i,3,6) cl_old(icnt,1,3) = ra(i,1,2) cl_old(icnt,2,3) = ra(i,2,2) cl_old(icnt,3,3) = ra(i,3,2) endif if (at(i).eq.1) then jcnt = jcnt + 1 sl_at(jcnt) = i sl_old(jcnt,1,1) = ra(i,1,1) sl_old(jcnt,2,1) = ra(i,2,1) sl_old(jcnt,3,1) = ra(i,3,1) sl_old(jcnt,1,2) = ra(i,1,6) sl_old(jcnt,2,2) = ra(i,2,6) sl_old(jcnt,3,2) = ra(i,3,6) sl_old(jcnt,1,3) = ra(i,1,2) sl_old(jcnt,2,3) = ra(i,2,2) sl_old(jcnt,3,3) = ra(i,3,2) endif end do c cl_cm_old(1) = cl_cmx cl_cm_old(2) = cl_cmy cl_cm_old(3) = cl_cmz cl_vcm_old(1) = cl_vcmx cl_vcm_old(2) = cl_vcmy cl_vcm_old(3) = cl_vcmz c do i = 1, 3 cl_alen1_old(i) = alen1(i) cl_alen2_old(i) = alen2(i) end do cl_vol1_old = vol1 cl_vol2_old = vol2 cl_box_old(1) = xbox cl_box_old(2) = ybox cl_box_old(3) = zbox cl_scal1_old = scal1 cl_scal2_old = scal2 c cl_tot = icnt sl_tot = jcnt return end c subroutine cluster_check_cllsn #include "common.fh" c c This subroutine checks to see if any of the cluster particles have moved c beyond the cutoff radius from the center of mass of the cluster. If they c have, then the initial guess for the particle coordinates and velocities c is modified to reflect the collision with the restraining sphere. c integer icnt, iat, i, j, ii, jj, iloc, imin, scndat, reset double precision r, r2, rx, ry, rz, dtmax, tsav double precision rrx, rry, rrz, vvx, vvy, vvz double precision vn, vx, vy, vz, fx, fy, fz, mu, v2, f2 double precision vrdot, frdot, vfdot, a, b, c double precision t1, t2, tcut, comm(4), tmax, htausq double precision rnx, rny, rnz, vpx, vpy, vpz, vllx, vlly, vllz double precision t3, t4, ax, ay, az, tchk, tmin, t_est double precision r1x, r1y, r1z, r2x, r2y, r2z, rmax, rskin, rn double precision cluster_find_tau integer failsafe, ibuf(MD_MAXPROC), me, nproc, twohit logical debug integer iter c iter = 0 l_cllsn = .false. if (nocluster) then t_rmndr = 0.0d00 t_done = tau return endif me = ga_nodeid() nproc = ga_nnodes() if (istep.ge.68365721.and.istep.le.68365723) then debug = .false. else debug = .false. endif rskin = 0.02d00 tmax = t_rmndr if (t_rmndr.eq.tau) cllsn_isav = 0 100 dtmax = 2.0d00*t_rmndr twohit = 0 failsafe = 0 iat = 0 iloc = 0 tsav = 0.0d00 rmax = r_cluster if (debug) write(6,*) me,' atot = ',atot,istep if (debug) write(6,*) me,' antot = ',antot,istep if (debug) write(6,*) me, ' r_cluster = ',r_cluster,istep if (debug) write(6,*) me,' t_rmndr = ',tmax,istep if (debug) write(6,*) me,' cl_cmx(1) = ',cl_cmx,istep if (debug) write(6,*) me,' cl_cmy(1) = ',cl_cmy,istep if (debug) write(6,*) me,' cl_cmz(1) = ',cl_cmz,istep do i = 1, cl_tot ii = cl_at(i) rx = ra(ii,1,6) - cl_cmx ry = ra(ii,2,6) - cl_cmy rz = ra(ii,3,6) - cl_cmz r2 = rx**2 + ry**2 + rz**2 r = sqrt(r2) c if (debug) write(6,*) me, 'ra(ii,1,6) ',ra(ii,1,6),istep c if (debug) write(6,*) me, 'ra(ii,2,6) ',ra(ii,2,6),istep c if (debug) write(6,*) me, 'ra(ii,3,6) ',ra(ii,3,6),istep if (r.gt.r_cluster) then if (r.gt.rmax) rmax = r if (debug) write(6,*) me,' tmax = ',tmax,istep if (debug) write(6,*) me,' dtmax = ',dtmax,istep if (debug) write(6,*) me,' ii = ',ii,istep if (debug) write(6,*) me,' antot = ',antot,istep if (debug) write(6,*) me,' r-r_cluster ',r-r_cluster,istep if (debug) write(6,*) me,' r_cluster = ',r_cluster,istep if (debug) write(6,*) me,' sqrt(r2) = ',sqrt(r2),istep if (debug) write(6,*) me,' ra(1) = ',ra(ii,1,6),istep if (debug) write(6,*) me,' ra(2) = ',ra(ii,2,6),istep if (debug) write(6,*) me,' ra(3) = ',ra(ii,3,6),istep if (debug) write(6,*) me,' cl_cmx = ',cl_cmx,istep if (debug) write(6,*) me,' cl_cmy = ',cl_cmy,istep if (debug) write(6,*) me,' cl_cmz = ',cl_cmz,istep if (debug) write(6,*) me,' r_cluster_old = ',r_cluster_old, + istep if (debug) write(6,*) me,' cl_old(1) = ',cl_old(i,1,2),istep if (debug) write(6,*) me,' cl_old(2) = ',cl_old(i,2,2),istep if (debug) write(6,*) me,' cl_old(3) = ',cl_old(i,3,2),istep if (debug) write(6,*) me,' cl_cm_old(1) = ',cl_cm_old(1),istep if (debug) write(6,*) me,' cl_cm_old(2) = ',cl_cm_old(2),istep if (debug) write(6,*) me,' cl_cm_old(3) = ',cl_cm_old(3),istep if (debug) write(6,*) me,' r_old = ', sqrt( + + (cl_old(i,1,2)-cl_cm_old(1))**2 + + (cl_old(i,2,2)-cl_cm_old(2))**2 + + (cl_old(i,3,2)-cl_cm_old(3))**2) rn = sqrt(rx**2+ry**2+rz**2) vn = ra(ii,1,2)*rx+ra(ii,2,2)*ry+ra(ii,3,2)*rz vn = vn/rn t_est = vn*(rn-r_cluster) tcut = cluster_find_tau(ii,i,cllsn_isav,tmax,.false.) if (debug) write(6,*) me,' tcut = ',tcut,istep if (debug) write(6,*) me,' t_est = ',t_est,istep if (debug) write(6,155) me,1,tcut,istep 155 format(i3,' tcut at ',i1,': ',f12.4,' step: ',i8) c c Check to see if collision is earlier than previously found collisions c and that it is also greater than zero. c if (tcut.lt.dtmax.and.tcut.gt.0.0d00) then c c Try to protect against numerical roundoff by checking that different c particle collides with sphere or that collision time is significantly c greater than zero if it is the same particle c if (.not.(ii.eq.cllsn_isav.and.tcut.lt.1.0d-03)) then dtmax = tcut tsav = tcut iat = ii iloc = i else write(6,101) ii, tcut, istep 101 format('Rejected collision of atom ',i8,' at time ', + f16.8,' at step ',i8) failsafe = 1 endif else if (tcut.lt.0.0d00) then write(6,*) ga_nodeid(),' vn ',vn,istep write(6,*) ga_nodeid(),' t_est ',t_est,istep write(6,*) ga_nodeid(),' r-r_cluster ',r-r_cluster,istep write(6,*) ga_nodeid(),' Returned negative value at 1',istep failsafe = 1 endif #if 0 c c Check for trajectories that may have gone out and then back into c confining sphere c else if (r_cluster-r.lt.rskin.and.r_cluster-r.gt.0.0d00) then vn = rx*ra(ii,1,2)+ry*ra(ii,2,2)+rz*ra(ii,3,2) if (vn.le.0.0d00) then tcut = cluster_find_tau(ii,i,cllsn_isav,tmax,.true.) if (tcut.lt.dtmax.and.tcut.gt.0.0d00) then write(6,*) 'Found looping trajectory',istep write(6,*) 'dtmax = ',dtmax,istep write(6,*) 'tcut = ',tcut,istep write(6,*) 'ii = ',ii,istep write(6,*) 'cllsn_isav = ',cllsn_isav,istep write(6,*) 'r = ',r,istep write(6,*) 'r_cluster = ',r_cluster,istep if (.not.(ii.eq.cllsn_isav.and.tcut.lt.1.0d-10)) then write(6,*) 'Resetting trajectory',istep dtmax = tcut tsav = tcut iat = ii iloc = i else write(6,*)'Not resetting trajectory (loop is too short)' endif endif endif #endif endif end do c c Check to see if a collision occured on another processor c ibuf(1) = iat ibuf(2) = failsafe if (debug) write(6,*) me,' iat = ',iat,istep if (debug) write(6,*) me,' failsafe = ',failsafe,istep if (debug) write(6,*) me,' ibuf(1) = ',ibuf(1),istep if (debug) write(6,*) me,' ibuf(2) = ',ibuf(2),istep call ga_igop(5,ibuf,2,'+') if (debug) write(6,*) me,' ibuf(1) = ',ibuf(1),istep if (debug) write(6,*) me,' ibuf(2) = ',ibuf(2),istep c c No collisions detected. Step is complete so just return. c if (ibuf(1).eq.0.and.ibuf(2).eq.0) then t_rmndr = 0.0d00 t_done = tau if (debug) write(6,*) me,' Returning with no hits ',istep return endif c c If no solution found for atom outside confining sphere, then bail c completely and increase diameter of confining sphere so that all c atoms are enclosed. c if (ibuf(2).gt.0) then call ga_dgop(4,rmax,1,'max') if (debug) write(6,*) me,' r_cluster (a) = ',r_cluster r_cluster = r_cluster + (rmax - r_cluster)*1.1d00 if (debug) write(6,*) me,' r_cluster (b) = ',r_cluster if (ga_nodeid().eq.0) then write(6,*) ga_nodeid(),' Returned negative value at 2',istep endif failcount = failcount + 1 t_rmndr = 0.0d00 t_done = tau return endif c c At this point, a collision has been detected on at least on c processor. Check to find minimum time if collisions occur on more c than one processor. c if (iat.eq.0) then tsav = tau endif a = tsav call ga_dgop(6,a,1,'min') c c Find mass of particle that has first collision with confining sphere c if (a.eq.tsav) then b = mass(iat) else b = 0.0d00 endif call ga_dgop(7,b,1,'max') 1000 mmass = cl_mass - b tcut = a if (debug) write(6,155) me,2,tcut,istep if (tsav.ne.a) then iat = 0 iloc = 0 cllsn_isav = 0 if (debug) write(6,*) me,' cllsn_isav(1): ',cllsn_isav else cllsn_isav = iat if (debug) write(6,*) me,' cllsn_isav(2): ',cllsn_isav endif c c Handle degenerate case of only two atoms in cluster, which will c generally have two simultaneous collisions c if (ctot.eq.2) then do i = 1, nproc if (i-1.eq.me) then ibuf(i) = iat else ibuf(i) = 0 endif end do call ga_igop(3,ibuf,nproc,'+') j = 0 iat = 0 cllsn_isav = 0 do i = 1, nproc if (ibuf(i).gt.0.and.i-1.eq.me.and.j.eq.0) then iat = ibuf(i) cllsn_isav = ibuf(i) j = 1 else if (ibuf(i).gt.0) then j = 1 endif end do c c Make sure that correct mass is being used for collision c if (iat.ne.0) then b = mass(iat) else b = 0.0d00 endif call ga_dgop(6,b,1,'max') mmass = cl_mass - b endif c c Now recalculate coordinates of all particles in the cluster c call cluster_reset(tcut) call cluster_com !CHECK c c Check to see if there were any trajectories that pass through confining c sphere twice (i.e. goes out and then comes back in). Don't bother with c check if there are only 2 particles in system. c if (ctot.eq.2) then go to 2000 endif reset = 0 tchk = 0.0d00 scndat = 0 tmin = tcut tsav = tcut failsafe = 0 do i = 1, cl_tot ii = cl_at(i) rx = ra(ii,1,6) - cl_cmx ry = ra(ii,2,6) - cl_cmy rz = ra(ii,3,6) - cl_cmz r = sqrt(rx**2+ry**2+rz**2) if (r.ge.r_cluster.and.ii.ne.cllsn_isav) then tchk = cluster_find_tau(ii,i,cllsn_isav,tcut,.false.) if (debug) write(6,155) me,7,tchk,istep if (debug) write(6,*) me, 'ii: ',ii if (debug) write(6,*) me, 'cllsn_isav: ',cllsn_isav reset = 1 if (tchk.lt.tmin.and.tchk.gt.0.0d00) then if (debug) write(6,*) me,' Shorter collision found' tmin = tchk imin = ii if (debug) write(6,155) me,4,tchk,istep if (debug) write(6,155) me,5,tmin,istep else if (tchk.lt.0.0d00) then write(6,*) ga_nodeid(),' Returned negative value at 3',istep failsafe = 1 else c c This case should theoretically be impossible, but it may occur because of c roundoff c write(6,*) me,' tchk greater than or equal to tmin',istep write(6,*) me,' tchk = ',tchk,istep write(6,*) me,' tmin = ',tmin,istep failsafe = 1 endif endif end do ibuf(1) = reset ibuf(2) = failsafe call ga_igop(2,ibuf,2,'+') c c No solution for tau found, so increase confining sphere and then bail c if (ibuf(2).gt.0) then if (debug) write(6,*) me,' rmax (c) = ',rmax call ga_dgop(4,rmax,1,'max') if (debug) write(6,*) me,' rmax (d) = ',rmax if (debug) write(6,*) me,' r_cluster (c) = ',r_cluster r_cluster = r_cluster + (rmax - r_cluster)*1.1d00 if (debug) write(6,*) me,' r_cluster (d) = ',r_cluster if (ga_nodeid().eq.0) then write(6,*) 'Bogus value of collision time at 2' endif call cluster_reset(t_rmndr) t_rmndr = 0.0d00 t_done = tau return endif reset = ibuf(1) c c Shorter collision found so recalculate positions c if (reset.gt.0) then if (tmin.le.tsav) then if (debug) write(6,*) 'tmin = ',tmin,istep if (debug) write(6,*) 'tsav = ',tsav,istep a = tmin else a = tau endif call ga_dgop(2,a,1,'min') c if (a.le.tsav) then if (a.ne.tmin) then iat = 0 iloc = 0 cllsn_isav = 0 if (debug) write(6,*) me,'cllsn_isav(3): ',cllsn_isav else cllsn_isav = imin if (debug) write(6,*) me,'cllsn_isav(4): ',cllsn_isav endif tcut = a tsav = a !BJP is this correct? if (debug) write(6,155) me,3,tcut,istep if (iat.gt.0) then b = mass(iat) else b = 0.0d00 endif call ga_dgop(7,b,1,'+') call cluster_reset(0.0d00) iter = iter + 1 if (iter.gt.100) debug = .true. if (iter.gt.1000) call ga_error('Too many iterations',0) go to 1000 endif endif c 2000 l_cllsn = .true. cllsn_idx = cllsn_isav c t_rmndr = tmax - tcut t_done = tau - t_rmndr if (debug) write(6,*) me, 't_rmndr = ',t_rmndr if (debug) write(6,*) me, 't_done = ',t_done c return end c double precision function cluster_find_tau(iat,iloc,isav,tmax, + forward) #include "common.fh" c c This function calculates the time at which a particle outside the confining c sphere would have collided with the sphere. c integer iat, iloc, isav double precision r, r2, rx, ry, rz, dr double precision rrx, rry, rrz, vvx, vvy, vvz double precision vx, vy, vz, fx, fy, fz, v2, f2 double precision clcut, frdot, vrdot, vfdot, a, b, c double precision t, t1, t2, tcut, tmax, thi, tlo, tmid double precision ttmin,ttmax integer i,j,me,iter logical forward, debug c c A particle lies outside the cutoff. Find particle that crosses c cutoff first. c if (istep.ge.68365721.and.istep.le.68365723) then debug = .false. else debug = .false. endif me = ga_nodeid() mmass = cl_mass - mass(iat) c c Calculate center of mass and velocity of center of mass of remaining c particles in cluster (these are the rr and vv vectors) c rrx = (cl_mass*cl_cm_old(1) - mass(iat)*cl_old(iloc,1,2))/mmass rry = (cl_mass*cl_cm_old(2) - mass(iat)*cl_old(iloc,2,2))/mmass rrz = (cl_mass*cl_cm_old(3) - mass(iat)*cl_old(iloc,3,2))/mmass vvx = (cl_mass*cl_vcm_old(1) - mass(iat)*cl_old(iloc,1,3))/mmass vvy = (cl_mass*cl_vcm_old(2) - mass(iat)*cl_old(iloc,2,3))/mmass vvz = (cl_mass*cl_vcm_old(3) - mass(iat)*cl_old(iloc,3,3))/mmass c c The r and v vectors are the relative coordinates and velocities of c particle iat and the center of mass vectors. The vector f is the c force along this relative coordinate. c rx = cl_old(iloc,1,2) - cl_cm_old(1) ry = cl_old(iloc,2,2) - cl_cm_old(2) rz = cl_old(iloc,3,2) - cl_cm_old(3) vx = cl_old(iloc,1,3) - cl_vcm_old(1) vy = cl_old(iloc,2,3) - cl_vcm_old(2) vz = cl_old(iloc,3,3) - cl_vcm_old(3) fx = ra(iat,1,3) - cl_acmx fy = ra(iat,2,3) - cl_acmy fz = ra(iat,3,3) - cl_acmz c r = sqrt(rx**2 + ry**2 + rz**2) c c Note that we are using acceleration instead of force, c so factors of reduced mass disappear c vrdot = vx*rx + vy*ry + vz*rz frdot = fx*rx + fy*ry + fz*rz vfdot = vx*fx + vy*fy + vz*fz clcut = r_cluster v2 = vx**2 + vy**2 + vz**2 a = v2+frdot b = 2.0d00*vrdot c c c = rx**2 + ry**2 + rz**2 - clcut**2 c c Calculate c to minimize roundoff error from subtracting large numbers c dr = r - clcut c = 2.0d00*r*dr+dr**2 f2 = fx**2 + fy**2 + fz**2 c c Scan forwards from 0 to find a final value of t2. If no final c value found, then return with value of -1. c if (debug) write(6,*) me, 'iat: ',iat if (debug) write(6,*) me, 'a: ',a if (debug) write(6,*) me, 'b: ',b if (debug) write(6,*) me, 'c: ',c if (debug) write(6,*) me, 'f2: ',f2 if (debug) write(6,*) me, 'vfdot: ',vfdot if (forward) then t = tmax t1 = 0.0d00 ttmin = t1 ttmax = t c tlo = c + t1*(b + t1*(a+t1*(vfdot+t1*0.25d00*f2))) tlo = (-c-t1**2*(a+t1*(vfdot+t1*0.25d00*f2)))/b - t1 do i = 1, 1000 if (t.eq.tmax) then t2= tmax*dble(i)/1000.0d00 c thi = c + t2*(b + t2*(a+t2*(vfdot+t2*0.25d00*f2))) thi = (-c-t2**2*(a+t2*(vfdot+t2*0.25d00*f2)))/b - t2 if ((thi.ge.0.0d00.and.tlo.le.0.0d00).or. + (thi.le.0.0d00.and.tlo.ge.0.0d00)) then t = t2 write(6,*) ga_nodeid(),' tmin = ',ttmin,istep write(6,*) ga_nodeid(),' tmax = ',ttmax,istep write(6,*) ga_nodeid(),' tlo = ',tlo,istep write(6,*) ga_nodeid(),' thi = ',thi,istep write(6,*) ga_nodeid(),' t = ',t,istep go to 500 endif endif end do cluster_find_tau = -1.0d00 return c c scan backwards from tmax to find an initial value of t1 c else t = 0.0d00 t2 = tmax c thi = c + t2*(b + t2*(a+t2*(vfdot+t2*0.25d00*f2))) thi = (-c-t2**2*(a+t2*(vfdot+t2*0.25d00*f2)))/b - t2 do i = 1, 1000 if (t.eq.0.0d00) then t1= tmax - tmax*dble(i)/1000.0d00 c tlo = c + t1*(b + t1*(a+t1*(vfdot+t1*0.25d00*f2))) tlo = (-c-t1**2*(a+t1*(vfdot+t1*0.25d00*f2)))/b - t1 if ((thi.ge.0.0d00.and.tlo.le.0.0d00).or. + (thi.le.0.0d00.and.tlo.ge.0.0d00)) then t = t1 if (debug) write(6,*) me, 'tlo: ',tlo if (debug) write(6,*) me, 'thi: ',thi if (debug) write(6,*) me, 't: ',t go to 500 endif endif end do endif c c Use bisections to find accurate solution c 500 if (forward) then t1 = 0.0d00 t2 = t else t1 = t t2 = tmax if (debug) write(6,*) me, 't1: ',t1 if (debug) write(6,*) me, 't2: ',t2 endif iter = 0 c 600 tlo = c + t1*(b + t1*(a+t1*(vfdot+t1*0.25d00*f2))) c thi = c + t2*(b + t2*(a+t2*(vfdot+t2*0.25d00*f2))) 600 tlo = (-c-t1**2*(a+t1*(vfdot+t1*0.25d00*f2)))/b - t1 thi = (-c-t2**2*(a+t2*(vfdot+t2*0.25d00*f2)))/b - t2 if ((thi.ge.0.0d00.and.tlo.le.0.0d00).or. + (thi.le.0.0d00.and.tlo.ge.0.0d00)) then t = 0.5d00*(t1+t2) c tmid = c + t*(b + t*(a+t*(vfdot+t*0.25d00*f2))) tmid = (-c-t**2*(a+t*(vfdot+t*0.25d00*f2)))/b - t if ((tmid.ge.0.0d00.and.thi.le.0.0d00).or. + (tmid.le.0.0d00.and.thi.ge.0.0d00)) then t1 = t else if ((tmid.ge.0.0d00.and.tlo.le.0.0d00).or. + (tmid.le.0.0d00.and.tlo.ge.0.0d00)) then t2 = t endif tcut = 0.5d00*(t1+t2) iter = iter + 1 if (abs(t1-t2)/tau.gt.1.0d-14.and.iter.lt.1000) go to 600 c c Protect against numerical roundoff errors c if (iter.ge.1000) then write(6,*) ga_nodeid(),' Maxed out on iterations' endif if (forward.and.(tcut.lt.1.0d-10.or. + abs(tcut-tmax).lt.1.0d-10)) then cluster_find_tau = -1.0d00 return endif else open(unit=2,file='tau.dat',status='unknown') do j = 0, 1000 t = tmax*dble(j)/1000.0d00 t1 = (-c-t**2*(a+t*(vfdot+t*0.25d00*f2)))/b - t tmid = c + t*(b + t*(a+t*(vfdot+t*0.25d00*f2))) write(2,300) t,t1, tmid 300 format(3(' ',f16.8)) end do close(2) if (iat.ne.isav) then if (abs(thi).lt.0.000001d00) then tcut = tmax else if (abs(tlo).lt.0.000001d00) then if (vrdot.gt.0.0d00) then tcut = 0.0d00 else tcut = tmax endif else if (forward) write(6,*) 'Searching forward' write(6,*) 'Collision time does not converge at step ',istep tcut = -1.0d00 endif else tcut = 4.0d00*tau endif endif c if (forward) write(6,*) ga_nodeid(),' tcut = ',tcut cluster_find_tau = tcut return end c double precision function cluster_check_radius() #include "common.fh" double precision rx, ry, rz, r2, cl2 integer i c c check to make sure that all cluster particles are within cluster c radius c cl2 = 0.0d00 do i = 1, antot if (at(i).eq.2) then rx = ra(i,1,6) - cl_cmx ry = ra(i,2,6) - cl_cmy rz = ra(i,3,6) - cl_cmz r2 = rx**2 + ry**2 + rz**2 if (r2.gt.cl2) cl2 = r2 endif end do call ga_dgop(4,cl2,1,'max') c cluster_check_radius = sqrt(cl2) return end c subroutine cluster_mc #include "common.fh" c c Perform Monte Carlo adjustment of volume on confining volume c double precision r_old, delta_r, r2, cl2, rx, ry, rz, pi double precision vol_new, vol_old, x, ran1, ratio integer i, icnt, iat, me logical force_move,debug c if (nocluster) return if (istep.ge.6932366) then debug = .false. else debug = .false. endif call cluster_com me = ga_nodeid() r_cluster_old = r_cluster r_old = r_cluster force_move = .false. delta_r = 0.2 if (debug) write(6,*)me,' (1) r_cluster ',r_cluster,istep if (me.eq.0) then r_cluster = r_cluster + delta_r*(ran1(0)-0.5d00) else r_cluster = 0.0d00 endif if (debug) write(6,*)me,' (2) r_cluster ',r_cluster,istep call ga_dgop(1,r_cluster,1,'+') if (debug) write(6,*)me,' (3) r_cluster ',r_cluster,istep c c If new value of r_cluster falls outside of allowed interval then c it is rejected, unless old value was aready outside interval. c if (r_cluster.ge.cl_upper.or.r_cluster.le.cl_lower) then c c If new values move cluster radius closer to cutoffs from the outside, c then accept them, reject them otherwise. Before accepting move, make c sure that there are no illegal overlaps. c force_move = .true. if (r_cluster.gt.r_old.and.r_cluster.gt.cl_upper) then r_cluster = r_old else if (r_cluster.lt.r_old.and.r_cluster.lt.cl_lower) then r_cluster = r_old endif if (r_cluster.eq.r_old) return endif if (debug) write(6,*)me,' (4) r_cluster ',r_cluster,istep c c Check to see if any particles are outside new value of r_cluster. c If so, then new value is rejected. c if (r_cluster.lt.r_old) then icnt = 0 cl2 = r_cluster**2 do i = 1, antot if (at(i).eq.2) then rx = ra(i,1,6) - cl_cmx ry = ra(i,2,6) - cl_cmy rz = ra(i,3,6) - cl_cmz r2 = rx**2 + ry**2 + rz**2 if (r2.gt.cl2) then icnt = icnt + 1 endif endif end do call ga_igop(9,icnt,1,'+') if (icnt.gt.0) then r_cluster = r_old return endif endif if (debug) write(6,*)me,' (5) r_cluster ',r_cluster,istep if (force_move) return c c Accept with Monte Carlo probability. c pi = 4.0d00*atan(1.0d00) vol_new = 4.0d00*pi*r_cluster**3/3.0d00 vol_old = 4.0d00*pi*r_old**3/3.0d00 ratio = (r_cluster/r_old)**2 c x = ratio*exp(-cl_prssr*(vol_new-vol_old)/mc_tmprtr) if (me.eq.0) then if (x.ge.1.0d00) then icnt = 1 else if (ran1(0).lt.x) then icnt = 1 else icnt = 0 endif endif else icnt = 0 endif call ga_igop(1,icnt,1,'+') if (icnt.eq.0) then r_cluster = r_old endif if (debug) write(6,*)me,' (6) r_cluster ',r_cluster,istep return end c subroutine cluster_binr #include "common.fh" c c Bin the current value of r_cluster c double precision dr integer ir, isample if (nocluster) return c c Find bin c dr = r_cluster - cl_lower ir = int(dr/mc_step) + 1 if (ir.le.0) ir = 1 if (ir.gt.mcbins) ir = mcbins mc_cnt = mc_cnt + 1 c c Find slice for statistics c if (istep.gt.mc_start) then isample = int(10.0d00*(dble(istep-mc_start)-0.5d00) + / dble(nstep-mc_start)) + 1 if (isample.gt.10) isample = 10 else isample = 1 endif r_cnt(isample) = r_cnt(isample) + 1 r_distr(ir,isample) = r_distr(ir,isample) + 1 return end c subroutine cluster_print_binr #include "common.fh" c c Print out the distribution of r_cluster c double precision rdist(MAXBINS), norm, r, normi double precision rdist2(MAXBINS), sum double precision sigr(MAXBINS),sigr2(MAXBINS) double precision sig2(MAXBINS),sig2_2(MAXBINS) double precision rdisti(MAXBINS,10),rdisti2(MAXBINS,10) integer itot, i, j, isample character*32 filename c if (nocluster) return if (task_id.lt.10) then write(filename,100) task_id else if (task_id.ge.10.and.task_id.lt.100) then write(filename,101) task_id else if (task_id.ge.100.and.task_id.lt.1000) then write(filename,102) task_id else if (task_id.ge.1000.and.task_id.lt.10000) then write(filename,103) task_id endif 100 format('cl.distr.',i1) 101 format('cl.distr.',i2) 102 format('cl.distr.',i3) 103 format('cl.distr.',i4) c c Evaluate normalized distribution and uncertainty for P(r) c if (mc_cnt.lt.0) return norm = dble(mc_cnt) do i = 1, mcbins rdist(i) = 0.0d00 rdist2(i) = 0.0d00 sig2(i) = 0.0d00 sig2_2(i) = 0.0d00 sigr(i) = 0.0d00 sigr2(i) = 0.0d00 do j = 1, 10 rdisti(i,j) = 0.0d00 rdisti2(i,j) = 0.0d00 end do end do do j = 1, 10 normi = dble(r_cnt(j)) do i = 1, mcbins rdist(i) = rdist(i) + dble(r_distr(i,j))/norm rdisti(i,j) = dble(r_distr(i,j))/normi end do end do do j = 1, 10 do i = 1, mcbins sig2(i) = sig2(i) + rdisti(i,j)**2 end do end do do i = 1, mcbins sigr(i) = (sig2(i)-10.0d00*rdist(i)**2)/9.0d00 end do c c Uncertainty at the 95% confidence level c do i = 1, mcbins sigr(i) = 2.23d00*sqrt(sigr(i)/10.d00) end do sum = 0.0d00 do i = 1, mcbins sum = sum + rdist(i) end do do i = 1, mcbins rdist(i) = rdist(i)/(sum*mc_step) sigr(i) = sigr(i)/(sum*mc_step) end do c c Evaluate normalized distribution and uncertainty for P(V) c do j = 1, 10 normi = dble(r_cnt(j)) do i = 1, mcbins r = cl_lower + (dble(i)-0.5d00)*mc_step rdist2(i) = rdist2(i) + dble(r_distr(i,j))/(r**2*norm) rdisti2(i,j) = dble(r_distr(i,j))/(r**2*normi) end do end do do j = 1, 10 do i = 1, mcbins sig2_2(i) = sig2_2(i) + rdisti2(i,j)**2 end do end do do i = 1, mcbins sigr2(i) = (sig2_2(i)-10.0d00*rdist2(i)**2)/9.0d00 end do c c Uncertainty at the 95% confidence level c do i = 1, mcbins sigr2(i) = 2.23d00*sqrt(sigr2(i)/10.d00) end do sum = 0.0d00 do i = 1, mcbins sum = sum + rdist2(i) end do do i = 1, mcbins rdist2(i) = rdist2(i)/(sum*mc_step) sigr2(i) = sigr2(i)/(sum*mc_step) end do c if (ga_nodeid().eq.0) then open(unit=2,file=filename,status='unknown') do i = 1, mcbins r = cl_lower + (dble(i)-0.5d00)*mc_step write(2,200) r, rdist(i), rdist2(i),sigr(i),sigr2(i) 200 format(f12.4,' ',f16.8,' ',f16.8,' ', + f16.8,' 'f16.8) end do close(2) endif c return end c subroutine cluster_clear_binr #include "common.fh" c c clear the distribution of r_cluster c integer itot, i, j c if (nocluster) return do j = 1, 10 do i = 1, MAXBINS r_distr(i,j) = 0 end do r_cnt(j) = 0 end do mc_cnt = 0 mc_start = istep c return end c subroutine cluster_reset_binr(iflg) #include "common.fh" c c recalculate bounds for confining sphere c integer iflg,ilow,ihi,imax,i if (nocluster) return if (iflg.eq.1) then c c find minimum and maximum values of distribution and reset c windows to just bound these values c if (ga_nodeid().eq.0.and.l_rad) then open(unit=3,file="win1.dat",status='unknown') do i = 1, mcbins write(3,*) i, r_distr(i,1) end do endif ilow = 0 ihi = 0 do i = 1, mcbins if (r_distr(i,1).gt.0.and.ilow.eq.0) then ilow = i-1 if (ilow.lt.1) ilow = 1 endif if (ilow.gt.0.and.ihi.eq.0.and.r_distr(i,1).eq.0) then ihi = i endif end do if (ihi.eq.0) ihi = mcbins if (ihi.lt.mcbins) then cl_upper = cl_lower + mc_step*dble(ihi) endif if (ihi.gt.1) then cl_lower = cl_lower + mc_step*dble(ilow-1) endif else if (iflg.eq.2) then c c reset bounds so that maximum is set at radius value that c is the maximum value of distribution c ilow = 1 ihi = 0 imax = 0 do i = 1, mcbins c if (r_distr(i,1).gt.0.and.ilow.eq.0) then c ilow = i-1 c if (ilow.lt.1) ilow = 1 c endif if (r_distr(i,1).gt.imax) then ihi = i imax = r_distr(i,1) endif end do if (ihi.eq.0) ihi = mcbins if (ihi.lt.mcbins) then cl_upper = cl_lower + mc_step*dble(ihi) endif if (ilow.gt.1) then cl_lower = cl_lower + mc_step*dble(ilow-1) endif endif cl_upper = 3.5d00 cl_lower = 0.5d00 cl_upper = 4.5d00 cl_lower = 0.5d00 mc_step = (cl_upper-cl_lower)/dble(mcbins) call cluster_clear_binr return end c subroutine cluster_do_cllsn #include "common.fh" integer iat, i, j, ii, jj, iloc, isav, scndat double precision r, r2, rc2, rx, ry, rz double precision rrx, rry, rrz, vvx, vvy, vvz double precision vx, vy, vz, fx, fy, fz, mu, v2, f2 double precision comm(4), tmax, tcut, vrdot double precision rnx, rny, rnz, vpx, vpy, vpz, vllx, vlly, vllz c c Re-evaluate all particle velocities. First, recalculate center of c mass and center of mass velocity of cluster at new coordinates c and velocities. c if (nocluster) return cllsn_cnt = cllsn_cnt + 1 iat = cllsn_idx call cluster_com if (iat.gt.0) then rrx = (cl_mass*cl_cmx - mass(iat)*ra(iat,1,6))/mmass rry = (cl_mass*cl_cmy - mass(iat)*ra(iat,2,6))/mmass rrz = (cl_mass*cl_cmz - mass(iat)*ra(iat,3,6))/mmass vvx = (cl_mass*cl_vcmx - mass(iat)*ra(iat,1,2))/mmass vvy = (cl_mass*cl_vcmy - mass(iat)*ra(iat,2,2))/mmass vvz = (cl_mass*cl_vcmz - mass(iat)*ra(iat,3,2))/mmass c c The r and v vectors are the relative coordinates and velocities of c particle iat and the center of mass vectors. The vector f is the c force along this relative coordinate. c rx = ra(iat,1,6) - rrx ry = ra(iat,2,6) - rry rz = ra(iat,3,6) - rrz vx = ra(iat,1,2) - vvx vy = ra(iat,2,2) - vvy vz = ra(iat,3,2) - vvz c c Decompose velocity v into components that are parallel and perpendicular c to r. Save this information for computing post-collision velocities. c r = sqrt(rx**2 + ry**2 + rz**2) rnx = rx/r rny = ry/r rnz = rz/r vrdot = vx*rnx + vy*rny + vz*rnz vllx = vrdot * rnx vlly = vrdot * rny vllz = vrdot * rnz vpx = vx - vllx vpy = vy - vlly vpz = vz - vllz comm(1) = (vpx-vllx-vx)/cl_mass comm(2) = (vpy-vlly-vy)/cl_mass comm(3) = (vpz-vllz-vz)/cl_mass comm(4) = mmass else comm(1) = 0.0d00 comm(2) = 0.0d00 comm(3) = 0.0d00 comm(4) = 0.0d00 endif call ga_dgop(8,comm,4,'+') vvx = comm(1) vvy = comm(2) vvz = comm(3) mmass = comm(4) c do i = 1, cl_tot ii = cl_at(i) if (ii.eq.iat) then ra(ii,1,2) = ra(ii,1,2) + mmass*vvx ra(ii,2,2) = ra(ii,2,2) + mmass*vvy ra(ii,3,2) = ra(ii,3,2) + mmass*vvz else ra(ii,1,2) = ra(ii,1,2) - mass(ii)*vvx ra(ii,2,2) = ra(ii,2,2) - mass(ii)*vvy ra(ii,3,2) = ra(ii,3,2) - mass(ii)*vvz endif end do call cluster_old_at c return end c subroutine cluster_reset(dt) #include "common.fh" double precision dt, htausq, scale integer i, ii c c Reset coordinates and velocities using old values from beginning of c step. c htausq = 0.5d00*dt**2 do i = 1, cl_tot ii = cl_at(i) ra(ii,1,1) = cl_old(i,1,1)+dt*cl_old(i,1,3)+htausq*ra(ii,1,3) ra(ii,2,1) = cl_old(i,2,1)+dt*cl_old(i,2,3)+htausq*ra(ii,2,3) ra(ii,3,1) = cl_old(i,3,1)+dt*cl_old(i,3,3)+htausq*ra(ii,3,3) ra(ii,1,6) = cl_old(i,1,2)+dt*cl_old(i,1,3)+htausq*ra(ii,1,3) ra(ii,2,6) = cl_old(i,2,2)+dt*cl_old(i,2,3)+htausq*ra(ii,2,3) ra(ii,3,6) = cl_old(i,3,2)+dt*cl_old(i,3,3)+htausq*ra(ii,3,3) ra(ii,1,2) = cl_old(i,1,3)+dt*ra(ii,1,3) ra(ii,2,2) = cl_old(i,2,3)+dt*ra(ii,2,3) ra(ii,3,2) = cl_old(i,3,3)+dt*ra(ii,3,3) end do #if 1 do i = 1, sl_tot ii = sl_at(i) ra(ii,1,1) = sl_old(i,1,1)+dt*sl_old(i,1,3)+htausq*ra(ii,1,3) ra(ii,2,1) = sl_old(i,2,1)+dt*sl_old(i,2,3)+htausq*ra(ii,2,3) ra(ii,3,1) = sl_old(i,3,1)+dt*sl_old(i,3,3)+htausq*ra(ii,3,3) ra(ii,1,6) = sl_old(i,1,2)+dt*sl_old(i,1,3)+htausq*ra(ii,1,3) ra(ii,2,6) = sl_old(i,2,2)+dt*sl_old(i,2,3)+htausq*ra(ii,2,3) ra(ii,3,6) = sl_old(i,3,2)+dt*sl_old(i,3,3)+htausq*ra(ii,3,3) ra(ii,1,2) = sl_old(i,1,3)+dt*ra(ii,1,3) ra(ii,2,2) = sl_old(i,2,3)+dt*ra(ii,2,3) ra(ii,3,2) = sl_old(i,3,3)+dt*ra(ii,3,3) end do #endif #if 1 c c resete extended Hamiltonian parameters c if ((prsflg.or.ptflg).and.ipmode.eq.0) then vol1 = cl_vol1_old + dt * cl_vol2_old + htausq * vol3 vol2 = cl_vol2_old + dt * vol3 scale = vol1 / (cl_box_old(1) * cl_box_old(2) * cl_box_old(3)) scale = exp(log(scale)/3.0d00) xbox = scale * cl_box_old(1) ybox = scale * cl_box_old(2) zbox = scale * cl_box_old(3) xbox2 = 0.5d00 * xbox ybox2 = 0.5d00 * ybox zbox2 = 0.5d00 * zbox endif c if ((prsflg.or.ptflg).and.ipmode.eq.1) then do 500 i = 1, 3 alen1(i) = cl_alen1_old(i) + dt * cl_alen2_old(i) + + htausq * alen3(i) alen2(i) = cl_alen2_old(i) + dt * alen3(i) 500 continue xbox = alen1(1) ybox = alen1(2) zbox = alen1(3) xbox2 = 0.5d00 * xbox ybox2 = 0.5d00 * ybox zbox2 = 0.5d00 * zbox endif c if ((prsflg.or.ptflg).and.ipmode.eq.2) then do 600 i = 1, 2 alen1(i) = cl_alen1_old(i) + dt * cl_alen2_old(i) + + htausq * alen3(i) alen2(i) = cl_alen2_old(i) + dt * alen3(i) 600 continue xbox = alen1(1) ybox = alen1(2) xbox2 = 0.5d00 * xbox ybox2 = 0.5d00 * ybox endif c c predictor step for time scale c if (tmpflg.or.ptflg) then scal1 = cl_scal1_old + dt * cl_scal2_old + htausq * scal3 scal2 = cl_scal2_old + dt * scal3 endif c c center of mass parameters c cl_cmx = cl_cm_old(1) cl_cmy = cl_cm_old(2) cl_cmz = cl_cm_old(3) cl_vcmx = cl_vcm_old(1) cl_vcmy = cl_vcm_old(2) cl_vcmz = cl_vcm_old(3) #endif return end ga-5.9.2/global/examples/md_cluster/common.fh000066400000000000000000000207251500715745200211600ustar00rootroot00000000000000 implicit none #include "mafdecls.fh" #include "global.fh" #ifdef MPI # include "mpif.h" #else # include "tcgmsg.fh" #endif integer MAXNRG,MAXNB,MAXAT,MAXTIM integer NSTES,NSTDF integer MD_MAXPROC,MAXBINS parameter (MAXNRG=50, MAXNB=100) parameter (MAXAT = 40000) parameter (MAXTIM=50) parameter (NSTES=1000) parameter (MAXBINS = 2000) parameter (MD_MAXPROC=1024) c c Arrays containing locally held atoms and their properties. atot is c the total number of atoms and antot is the number of locally held c atoms. aidx is an array containing the absolute atomic index of c each locally held atom. at is an array containing an index to the c atom type, and ra is an array containing the coordinates, velocities, c and forces of each locally held atom. The array ra contains c ra(i,j,1): periodic coordinates that all are within the c simulation cell c ra(i,j,2): velocities c ra(i,j,3): accelerations c ra(i,j,4): forces (not including constraint forces) c ra(i,j,5): momentum c ra(i,j,6): absolute coordinates c ra(i,j,7): complete forces from previous timestep c ra(i,j,8): temporary storage to construct accelations with c constraints c common /syspar/ ra(MAXAT,3,8),mass(MAXAT),at(MAXAT),aidx(MAXAT), + atot,antot double precision ra,mass integer at,aidx,atot,antot c c Global array handles and utility arrays for moving data around c common /ga_data/ g_size, gsize_lo(0:MD_MAXPROC), gsize_hi, + g_coords, gcoords_lo(2,0:MD_MAXPROC), + gcoords_hi(2), + g_frc, gfrc_lo(2,0:MD_MAXPROC), gfrc_hi(2), + g_acc, gacc_lo(2,0:MD_MAXPROC), gacc_hi(2), + g_index, gindex_lo(0:MD_MAXPROC), gindex_hi, + g_iat, giat_lo(2,0:MD_MAXPROC), giat_hi(2), + g_rvec, grvec_lo(0:MD_MAXPROC), grvec_hi integer g_size, gsize_lo, gsize_hi, g_coords,gcoords_lo, + gcoords_hi, g_frc, gfrc_hi, gfrc_lo, g_acc, gacc_lo, + gacc_hi, g_index, gindex_lo, gindex_hi, g_iat, giat_hi, + giat_lo, g_rvec, grvec_lo, grvec_hi common /md_par/ tau,dftmp,dfprs,dftm,dfpm,rsc(20,4), + tmprtr,prssr,tmass,pmass,tvol,tavg,tmstrt, + nsc,nstep,istep,ilist,dflalg,isc(20,4),istart, + istop,tmpflg,prsflg,ptflg,istat,itarg,itavg, + ipmode,iseed,mbflg,cmflg double precision tau,dftmp,dfprs,dftm,dfpm,rsc, + tmprtr,prssr,tmass,pmass,tvol,tavg integer tmstrt,nsc,nstep,istep,ilist,dflalg,isc integer istart,istop,istat,itarg integer itavg,ipmode,iseed logical tmpflg,prsflg,ptflg,mbflg,cmflg common /simpar/ xbox,ybox,zbox,xbox2,ybox2,zbox2, + vol1,vol2,vol3,scal1,scal2,scal3, + alen1(3),alen2(3),alen3(3) double precision xbox,ybox,zbox,xbox2,ybox2,zbox2, + vol1,vol2,vol3,scal1,scal2,scal3,alen1,alen2,alen3 common /nrgpar/ nrg(MAXNRG),anrg(MAXNRG),anrg2(MAXNRG), + presf,presfx,presfy,presfz,esvr,esvd,esvoh, + esva,asev(MAXAT),nrgcnt,nrg2ct double precision nrg,anrg,anrg2,presf,esvr,esvd,esvoh,esva,asev, + presfx,presfy,presfz integer nrgcnt,nrg2ct common /apars/ amass(50),e12(50,50),e6(50,50), + dcorr(50,50),rcorr(50,50), + frcorr(50,50),fdcorr(50,50), + rcut,acut(50,50),acut2(50,50),rcmax,icut,atnum double precision amass,e12,e6,dcorr,rcorr,frcorr,fdcorr,acut,rcut, + acut2,rcmax integer icut,atnum common /md_timer/ tmstat(MAXTIM) double precision tmstat common /espar/ pmvac(NSTES),tmvac(NSTES),ipmvac(NSTES), + itmvac(NSTES),esinc,esstps,esflg double precision pmvac,tmvac integer ipmvac,itmvac,esinc,esstps logical esflg c c coordinates of atoms that are not held locally are stored in xcrc, c ycrd, zcrd, similarly, forces for atoms not held locally are stored c in xfrc, yfrc, zfrc c common /md_buffer/xcrd(MAXAT),ycrd(MAXAT),zcrd(MAXAT),xfrc(MAXAT), + yfrc(MAXAT),zfrc(MAXAT),xacc(MAXAT),yacc(MAXAT), + zacc(MAXAT),mbuf(MAXAT),bidx(MAXAT),bat(MAXAT), + btot,savtot double precision xcrd,ycrd,zcrd,xfrc,yfrc,zfrc,xacc,yacc,zacc,mbuf integer bidx,bat,btot,savtot c common /md_cell/rcell,kcmax,nix(2000),niy(2000),niz(2000) double precision rcell integer kcmax,nix,niy,niz c c the xgru, xgrd etc. buffers keep track of which atoms are within a c cutoff distance of the upper (u) and lower (d) boundaries of the c cell. The ygbu, ygbd etc. buffers keep track of which buffer atoms c are within a cutoff distance of the boundaries (these buffers are c not needed for the x-direction updates). The ygbu, etc. buffers are c used in the gather routine. The ysbu, etc. buffers are used in a c similar way in the scatter routine. c common /svlist/ xgru(MAXAT),xgrd(MAXAT),ygru(MAXAT),ygrd(MAXAT), + zgru(MAXAT),zgrd(MAXAT), + ygbu(MAXAT),ygbd(MAXAT),zgbu(MAXAT),zgbd(MAXAT), + xsbu(MAXAT),xsbd(MAXAT),ysbu(MAXAT),ysbd(MAXAT), + zsbu(MAXAT),zsbd(MAXAT), + clist(6,MAXAT), + ixgru,ixgrd,iygru,iygrd,izgru,izgrd, + iygbu,iygbd,izgbu,izgbd integer xgru,xgrd,ygru,ygrd,zgru,zgrd, + ygbu,ygbd,zgbu,zgbd, + ixgru,ixgrd,iygru,iygrd,izgru,izgrd, + iygbu,iygbd,izgbu,izgbd,ixsbu,ixsbd,iysbu,iysbd, + izsbu,izsbd logical xsbu,xsbd,ysbu,ysbd,zsbu,zsbd logical clist c common /neighbors/ nblist(MAXAT*MAXNB),nalast(MAXAT), + nblast(MAXAT),nafirst(MAXAT),nbfirst(MAXAT), + blist(3*MAXAT),balast(MAXAT), + bblast(MAXAT),bafirst(MAXAT),bbfirst(MAXAT) integer nblist,nalast,nblast,nafirst,nbfirst integer blist,balast,bblast,bafirst,bbfirst c common /clusterpar/ cl_cmx, cl_cmy, cl_cmz, r_cluster, cl_sep, + cl_vcmx, cl_vcmy, cl_vcmz, + cl_acmx, cl_acmy, cl_acmz, + cl_prssr, cl_mass, r_cluster_old, + cl_old(MAXAT,3,3), cl_cm_old(3), + cl_vcm_old(3), cl_vol1_old, cl_vol2_old, + cl_box_old(3), cl_alen1_old(3), + cl_alen2_old(3), cl_scal1_old, cl_scal2_old, + cl_at(MAXAT), cl_tot, ctot, nocluster double precision cl_cmx, cl_cmy, cl_cmz, r_cluster, cl_sep, + cl_vcmx, cl_vcmy, cl_vcmz, + cl_acmx, cl_acmy, cl_acmz, + cl_prssr, cl_mass, cl_old, r_cluster_old, + cl_cm_old, cl_vcm_old, cl_vol1_old, cl_vol2_old, + cl_box_old, cl_alen1_old, cl_alen2_old, + cl_scal1_old, cl_scal2_old integer cl_at, ctot, cl_tot logical nocluster common /solventpar/ sl_old(MAXAT,3,3),sl_at(MAXAT),sl_tot double precision sl_old integer sl_at, sl_tot common /equil_protocol/ r_confine, equil_1, equil_2, equil_3, + window_1, window_2 double precision r_confine integer equil_1, equil_2, equil_3, window_1, window_2 common /cllsnpar/ mmass, cllsn_idx, l_cllsn, cllsn_cnt, + cllsn_isav, failcount double precision mmass integer cllsn_idx, cllsn_cnt, cllsn_isav, failcount logical l_cllsn common /mcpar/ cl_lower, cl_upper, mc_step, mc_tmprtr, mc_dr, + r_distr(0:MAXBINS,10), r_cnt(10), mcbins, mcfreq, + mc_cnt, mc_start double precision cl_lower, cl_upper, mc_step, mc_tmprtr, mc_dr integer r_distr, r_cnt, mcbins, mcfreq, mc_cnt, mc_start common /taskpar/ task_id, g_counter integer task_id, g_counter common /iopar/ l_stdio, l_rad, l_rst, l_step, l_oldcfg logical l_stdio, l_rad, l_rst, l_step, l_oldcfg common /adapt/ t_done, t_rmndr double precision t_done, t_rmndr common /hashpar/ link_a(MAXAT), top_a(MAXAT), hash_key_a(MAXAT), + hash_value_a(MAXAT), a_cnt integer link_a, top_a, hash_key_a, hash_value_a, a_cnt ga-5.9.2/global/examples/md_cluster/estats.F000066400000000000000000000232461500715745200207640ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine estat #include "common.fh" c c This subroutine accumulates energy statistics for the simulation. c The averages are stored in the array anrg(). The contents of nrg() c and anrg() are listed below. Temporary values for the averages are c stored in tmpnrg() and the uncertainties are stored anrg2(). c Corresponding entries in anrg(), tmpnrg(), and anrg2() are all indexed c by the same variable. c c Contents of nrg() c c nrg(1): the total number of degrees of freedom in the system c c nrg(2): the total number of degrees of freedom in the system c times Rgas/2 c c nrg(3): the total energy of the system c c nrg(4): the total kinetic energy of the system c c nrg(5): temperature (in Kelvin) of the system c c nrg(6): potential energy of the system c c nrg(7): current pressure (in atmospheres) c c nrg(8): the total translational kinetic energy of the system c c nrg(9): the instantaneous value of the Hamiltonian c c nrg(10): the instantaneous value of the virial c c nrg(11): the instantaneous value of the Enthalpy c c nrg(12): the instantaneous coulomb energy of the system c c nrg(13): the instantaneous repulsion energy of the system c c nrg(14): the instantaneous dispersion energy of the system c c nrg(15): the virial contribution to the pressure (in atmospheres) c c nrg(16): the instantaneous torsional energy of the system c c nrg(17): instantaneous energy from harmonic oscillators c c nrg(18): x component of virial c c nrg(19): y component of virial c c nrg(20): z component of virial c c nrg(21): instantaneous energy from bond angle bends c c nrg(22): x component of total momentum c c nrg(23): y component of total momentum c c nrg(24): z component of total momentum c c nrg(25): total mass c c Contents of anrg(), tmpnrg(), and anrg2() c c anrg(1): the average total energy of the system c c anrg(2): the average total kinetic energy of the system c c anrg(3): the average temperature (in Kelvin) of the system c c anrg(4): the average potential energy of the system c c anrg(5): the average pressure (in atmospheres) c c anrg(6): the average total translational kinetic energy of the system c c anrg(7): the average value of the Hamiltonian c c anrg(8): the average value of the Enthalpy c c anrg(9): the average coulomb energy of the system c c anrg(10): the average repulsion energy of the system c c anrg(11): the average dispersion energy of the system c c anrg(12): the average x dimension of the system c c anrg(13): the average y dimension of the system c c anrg(14): the average z dimension of the system c c anrg(15): the average value of the time scaling parameter c c anrg(16): the average volume of the system c c anrg(17): mean square total energy c c anrg(18): mean square Hamiltonian c c anrg(19): mean square kinetic energy c c anrg(20): mean square potential energy c c anrg(21): mean square pressure (in atmospheres**2) c c anrg(22): mean square x dimension c c anrg(23): mean square y dimension c c anrg(24): mean square z dimension c c anrg(25): mean square scaling parameter c c anrg(26): mean square temperature c c anrg(27): mean square Enthalpy c c arng(28): the average torsional energy c c arng(29): the average harmonic oscillator energy c c arng(30): the average bond angle bend energy c nrgcnt = nrgcnt + 1 c anrg(1) = anrg(1) + nrg(3) anrg(2) = anrg(2) + nrg(4) anrg(3) = anrg(3) + nrg(5) anrg(4) = anrg(4) + nrg(6) anrg(5) = anrg(5) + nrg(7) anrg(6) = anrg(6) + nrg(8) anrg(7) = anrg(7) + nrg(9) anrg(8) = anrg(8) + nrg(11) anrg(9) = anrg(9) + nrg(12) anrg(10) = anrg(10) + nrg(13) anrg(11) = anrg(11) + nrg(14) anrg(12) = anrg(12) + xbox anrg(13) = anrg(13) + ybox anrg(14) = anrg(14) + zbox anrg(15) = anrg(15) + scal1 anrg(16) = anrg(16) + vol1 anrg(17) = anrg(17) + nrg(3)**2 anrg(18) = anrg(18) + nrg(9)**2 anrg(19) = anrg(19) + nrg(4)**2 anrg(20) = anrg(20) + nrg(6)**2 anrg(21) = anrg(21) + nrg(7)**2 anrg(22) = anrg(22) + xbox**2 anrg(23) = anrg(23) + ybox**2 anrg(24) = anrg(24) + zbox**2 anrg(25) = anrg(25) + scal1**2 anrg(26) = anrg(26) + nrg(5)**2 anrg(27) = anrg(27) + nrg(11)**2 anrg(28) = anrg(28) + nrg(16) anrg(29) = anrg(29) + nrg(17) anrg(30) = anrg(30) + nrg(21) c return end c subroutine prtnrg #include "common.fh" c double precision bstat,b2stat integer i,ibstat c if (nrgcnt.eq.0) return c do 25 i = 1, MAXNRG anrg(i) = anrg(i) / dble(nrgcnt) 25 continue c c call ga_dgop(3,bstat,1,'+') c call ga_dgop(3,b2stat,1,'+') c call ga_igop(3,ibstat,1,'+') c if (ga_nodeid().eq.0.and.l_stdio) then c if (ibstat.gt.0) bstat = bstat/dble(ibstat) c if (ibstat.gt.0) b2stat = b2stat/dble(ibstat) c b2stat = sqrt(abs(b2stat-bstat**2)) c write(6,50) write(6,100) anrg(1) write(6,200) anrg(2) write(6,300) anrg(3) c write(6,600) anrg(6) write(6,400) anrg(4) c write(6,900) anrg(9) write(6,1000) anrg(10) write(6,1100) anrg(11) c write(6,2800) anrg(28) write(6,2900) anrg(29) write(6,3000) anrg(30) write(6,500) anrg(5) write(6,700) anrg(7) write(6,800) anrg(8) c write(6,850) bstat write(6,1150) write(6,1200) anrg(12) write(6,1300) anrg(13) write(6,1400) anrg(14) write(6,1500) anrg(15) write(6,1600) anrg(16) write(6,1650) write(6,1700) sqrt(abs(anrg(17)-anrg(1)**2)) write(6,1800) sqrt(abs(anrg(18)-anrg(7)**2)) write(6,1900) sqrt(abs(anrg(19)-anrg(2)**2)) write(6,2000) sqrt(abs(anrg(20)-anrg(4)**2)) write(6,2600) sqrt(abs(anrg(26)-anrg(3)**2)) write(6,2700) sqrt(abs(anrg(27)-anrg(8)**2)) write(6,2100) sqrt(abs(anrg(21)-anrg(5)**2)) write(6,2200) sqrt(abs(anrg(22)-anrg(12)**2)) write(6,2300) sqrt(abs(anrg(23)-anrg(13)**2)) write(6,2400) + sqrt(abs(anrg(24)-anrg(14)**2)) write(6,2500) sqrt(abs(anrg(25)-anrg(15)**2)) write(6,2550) b2stat endif c 50 format('Average Energy statistics for simulation:') 100 format(' :',f16.4) 200 format(' :',f16.4) 300 format(' :',f16.4) 400 format(' :',f16.4) 500 format(' :',f16.4) 600 format(' :',f16.4) 700 format(' :',f16.4) 800 format(' :',f16.4) 850 format(' :',f16.4) 900 format(' :',f16.4) 1000 format(' :',f16.4) 1100 format(' :',f16.4) 1150 format('Average system parameters for simulation :') 1200 format(' :',f16.4) 1300 format(' :',f16.4) 1400 format(' :',f16.4) 1500 format(' :',f16.4) 1600 format(' :',f16.4) 1650 format('Root mean square fluctuations:') 1700 format(' Energy :',f16.4) 1800 format(' Hamiltonian :',f16.4) 1900 format(' Kinetic Energy :',f16.4) 2000 format(' Potential Energy :',f16.4) 2100 format(' Pressure :',f16.4) 2200 format(' X :',f16.4) 2300 format(' Y :',f16.4) 2400 format(' Z :',f16.4) 2500 format(' S :',f16.4) 2550 format(' Bond Length :',f16.4) 2600 format(' Temperature :',f16.4) 2700 format(' Enthalpy :',f16.4) 2800 format(' :',f16.4) 2900 format(' :',f16.4) 3000 format(' :',f16.4) return end c subroutine nrgsum #include "common.fh" c double precision rbuf(50) c c this subroutine does a global add on some of the quantities stored c in the array nrg() c rbuf(1) = nrg(3) rbuf(2) = nrg(4) rbuf(3) = nrg(6) rbuf(4) = nrg(8) rbuf(5) = nrg(9) rbuf(6) = nrg(10) rbuf(7) = nrg(11) rbuf(8) = nrg(12) rbuf(9) = nrg(13) rbuf(10) = nrg(14) rbuf(11) = nrg(17) rbuf(12) = nrg(21) rbuf(13) = nrg(22) rbuf(14) = nrg(23) rbuf(15) = nrg(24) rbuf(16) = nrg(25) rbuf(17) = nrg(15) c call ga_dgop(3,rbuf,20,'+') c nrg(3) = rbuf(1) nrg(4) = rbuf(2) nrg(6) = rbuf(3) nrg(8) = rbuf(4) nrg(9) = rbuf(5) nrg(10) = rbuf(6) nrg(11) = rbuf(7) nrg(12) = rbuf(8) nrg(13) = rbuf(9) nrg(14) = rbuf(10) nrg(17) = rbuf(11) nrg(21) = rbuf(12) nrg(22) = rbuf(13) nrg(23) = rbuf(14) nrg(24) = rbuf(15) nrg(25) = rbuf(16) nrg(15) = rbuf(17) c return end ga-5.9.2/global/examples/md_cluster/factor.F000066400000000000000000000034541500715745200207360ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine factor(p,idx,idy,idz) implicit none integer i,j,p,idx,idy,idz,it integer ip,ifac,pmax,prime(1000) integer fac(1000) c i = 1 ip = p c c factor p completely c first, find all prime numbers less than or equal to p c pmax = 0 do i = 2, p do j = 1, pmax if (mod(i,prime(j)).eq.0) go to 100 end do pmax = pmax + 1 prime(pmax) = i 100 continue end do c c find all prime factors of p c ifac = 0 do i = 1, pmax 200 if (mod(ip,prime(i)).eq.0) then ifac = ifac + 1 fac(ifac) = prime(i) ip = ip/prime(i) go to 200 endif end do c c determine three factors of p of approximately the c same size c idx = 1 idy = 1 idz = 1 do i = ifac, 1, -1 if (idx.le.idy.and.idx.le.idz) then idx = fac(i)*idx elseif (idy.le.idx.and.idy.le.idz) then idy = fac(i)*idy elseif (idz.le.idx.and.idz.le.idy) then idz = fac(i)*idz endif end do c it = idy c idy = idx c idx = it c c it = idx c idx = idz c idz = it c c it = idy c idy = idz c idz = it return end c subroutine i_proc_to_xyz(p,ix,iy,iz,idx,idy,idz) implicit none integer p,ix,iy,iz,ip,it integer idx,idy,idz c ip = p c it = mod(ip,idx) ix = it ip = (ip - it)/idx it = mod(ip,idy) iy = it ip = (ip - it)/idy it = mod(ip,idz) iz = it c return end c subroutine i_xyz_to_proc(p,ix,iy,iz,idx,idy,idz) implicit none integer p,ix,iy,iz,idx,idy,idz c p = ix + idx*iy + idx*idy*iz c return end ga-5.9.2/global/examples/md_cluster/force.F000066400000000000000000000022151500715745200205500ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine force #include "common.fh" c double precision tbeg,wraptime integer i,j integer inode c tbeg = wraptime() c c clear forces c do 100 j = 1, 3 do 200 i = 1, antot ra(i,j,4) = 0.0d00 200 continue 100 continue c do 300 i = 1, antot asev(i) = 0.0d00 300 continue c esvr = 0.0d00 esvd = 0.0d00 esvoh = 0.0d00 esva = 0.0d00 presf = 0.0d00 presfx = 0.0d00 presfy = 0.0d00 presfz = 0.0d00 c call gather c c clear buffers c do i = 1, btot xfrc(i) = 0.0d00 yfrc(i) = 0.0d00 zfrc(i) = 0.0d00 end do c c assign atoms to individual molecules c call pairs call scatter c nrg(6) =esvr + esvd + esvoh + esva nrg(13) = esvr nrg(14) = esvd nrg(17) = esvoh nrg(21) = esva nrg(10) = presf nrg(18) = presfx nrg(19) = presfy nrg(20) = presfz presf = presf / (xbox * ybox * zbox) nrg(15) = presf/ 3.0d00 c tmstat(2) = tmstat(2) + wraptime() - tbeg c return end ga-5.9.2/global/examples/md_cluster/gather.F000066400000000000000000000434611500715745200207340ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine gather #include "common.fh" c double precision xmax,ymax,zmax,xmin,ymin,zmin double precision tbeg,wraptime integer snode,rnode,pnum,idx,idy,idz,ipx,ipy,ipz integer i,is,ig,inx,iny,inz,stot,inode logical nolist c c This subroutine gathers the coordinates of all particles on c neighboring processors that lie within an interaction distance c of the domain boundaries corresponding to the local processor c tbeg = wraptime() c if (istep.eq.0.or.(mod(istep,ilist).eq.0.and. + t_rmndr.eq.0.0d00)) then nolist = .true. else nolist = .false. endif c pnum = ga_nnodes() call factor(pnum,idx,idy,idz) inode = ga_nodeid() call i_proc_to_xyz(inode,ipx,ipy,ipz,idx,idy,idz) c if (task_id.gt.3) then c write(6,*) ga_pgroup_nodeid(ga_pgroup_get_world()), c + 'Nolist is ',nolist c write(6,157) ga_pgroup_nodeid(ga_pgroup_get_world()), c + idx,idy,idz c 157 format(i3,' Proc dimensions are ',3i6) c endif c c determine local boundaries c xmax = xbox*dble(ipx+1)/dble(idx) ymax = ybox*dble(ipy+1)/dble(idy) zmax = zbox*dble(ipz+1)/dble(idz) xmin = xbox*dble(ipx)/dble(idx) ymin = ybox*dble(ipy)/dble(idy) zmin = zbox*dble(ipz)/dble(idz) xmax = xmax - xbox2 ymax = ymax - ybox2 zmax = zmax - zbox2 xmin = xmin - xbox2 ymin = ymin - ybox2 zmin = zmin - zbox2 c btot = 0 c c Send particles along x-axis. Gather all particles that are within c an interaction distance of the x-boundaries and send them to the c adjacent processors c if (idx.eq.2) then c c both boundaries are adjacent to boundaries on next processor c stot = 0 if (nolist) then ixgru = 0 do i = 1, antot if (xmax-ra(i,1,1).lt.rcmax.or. + (ra(i,1,1)-xmin).lt.rcmax) then ixgru = ixgru + 1 xgru(ixgru) = i stot = stot + 1 is = btot+stot xcrd(is) = ra(i,1,1) ycrd(is) = ra(i,2,1) zcrd(is) = ra(i,3,1) bidx(is) = aidx(i) bat(is) = at(i) endif end do else do i = 1, ixgru ig = xgru(i) stot = stot + 1 is = btot+stot xcrd(is) = ra(ig,1,1) ycrd(is) = ra(ig,2,1) zcrd(is) = ra(ig,3,1) bidx(is) = aidx(ig) bat(is) = at(ig) end do endif c c send boundary layer particles to next buffer c inx = ipx - 1 if (inx.eq.-1) inx = idx - 1 call i_xyz_to_proc(rnode,inx,ipy,ipz,idx,idy,idz) call gather_buf(rnode,stot) elseif (idx.gt.2) then c c each boundary is adjacent to a different processor c stot = 0 if (nolist) then ixgru = 0 do i = 1, antot if (xmax-ra(i,1,1).lt.rcmax) then ixgru = ixgru + 1 xgru(ixgru) = i stot = stot + 1 is = btot+stot xcrd(is) = ra(i,1,1) ycrd(is) = ra(i,2,1) zcrd(is) = ra(i,3,1) bidx(is) = aidx(i) bat(is) = at(i) endif end do else do i = 1, ixgru ig = xgru(i) stot = stot + 1 is = btot+stot xcrd(is) = ra(ig,1,1) ycrd(is) = ra(ig,2,1) zcrd(is) = ra(ig,3,1) bidx(is) = aidx(ig) bat(is) = at(ig) end do endif c c send boundary layer particles to next buffer c inx = ipx - 1 if (inx.eq.-1) inx = idx - 1 call i_xyz_to_proc(rnode,inx,ipy,ipz,idx,idy,idz) call gather_buf(rnode,stot) c stot = 0 if (nolist) then ixgrd = 0 do i = 1, antot if (ra(i,1,1)-xmin.lt.rcmax) then ixgrd = ixgrd + 1 xgrd(ixgrd) = i stot = stot + 1 is = btot+stot xcrd(is) = ra(i,1,1) ycrd(is) = ra(i,2,1) zcrd(is) = ra(i,3,1) bidx(is) = aidx(i) bat(is) = at(i) endif end do else do i = 1, ixgrd ig = xgrd(i) stot = stot + 1 is = btot+stot xcrd(is) = ra(ig,1,1) ycrd(is) = ra(ig,2,1) zcrd(is) = ra(ig,3,1) bidx(is) = aidx(ig) bat(is) = at(ig) end do endif c c send boundary layer particles to next buffer c inx = ipx + 1 if (inx.eq.idx) inx = 0 call i_xyz_to_proc(rnode,inx,ipy,ipz,idx,idy,idz) call gather_buf(rnode,stot) endif c c repeat for y axis c if (idy.eq.2) then c c both boundaries are adjacent to boundaries on next processor c stot = 0 if (nolist) then iygru = 0 do i = 1, antot if ((ymax-ra(i,2,1).lt.rcmax.or. + (ra(i,2,1)-ymin).lt.rcmax)) then iygru = iygru + 1 ygru(iygru) = i stot = stot + 1 is = btot+stot xcrd(is) = ra(i,1,1) ycrd(is) = ra(i,2,1) zcrd(is) = ra(i,3,1) bidx(is) = aidx(i) bat(is) = at(i) endif end do else do i = 1, iygru ig = ygru(i) stot = stot + 1 is = btot+stot xcrd(is) = ra(ig,1,1) ycrd(is) = ra(ig,2,1) zcrd(is) = ra(ig,3,1) bidx(is) = aidx(ig) bat(is) = at(ig) end do endif c if (nolist) then iygbu = 0 do i = 1, btot if ((ymax-ycrd(i).lt.rcmax.or. + (ycrd(i)-ymin).lt.rcmax)) then iygbu = iygbu + 1 ygbu(iygbu) = i stot = stot + 1 is = btot+stot xcrd(is) = xcrd(i) ycrd(is) = ycrd(i) zcrd(is) = zcrd(i) bidx(is) = bidx(i) bat(is) = bat(i) endif end do else do i = 1, iygbu ig = ygbu(i) stot = stot + 1 is = btot+stot xcrd(is) = xcrd(ig) ycrd(is) = ycrd(ig) zcrd(is) = zcrd(ig) bidx(is) = bidx(ig) bat(is) = bat(ig) end do endif c c send boundary layer particles to next buffer c iny = ipy - 1 if (iny.eq.-1) iny = idy - 1 call i_xyz_to_proc(rnode,ipx,iny,ipz,idx,idy,idz) call gather_buf(rnode,stot) elseif (idy.gt.2) then c c each boundary is adjacent to a different processor c stot = 0 if (nolist) then iygru = 0 do i = 1, antot if (ymax-ra(i,2,1).lt.rcmax) then iygru = iygru + 1 ygru(iygru) = i stot = stot + 1 is = btot+stot xcrd(is) = ra(i,1,1) ycrd(is) = ra(i,2,1) zcrd(is) = ra(i,3,1) bidx(is) = aidx(i) bat(is) = at(i) endif end do else do i = 1, iygru ig = ygru(i) stot = stot + 1 is = btot+stot xcrd(is) = ra(ig,1,1) ycrd(is) = ra(ig,2,1) zcrd(is) = ra(ig,3,1) bidx(is) = aidx(ig) bat(is) = at(ig) end do endif c if (nolist) then iygbu = 0 do i = 1, btot if (ymax-ycrd(i).lt.rcmax) then iygbu = iygbu + 1 ygbu(iygbu) = i stot = stot + 1 is = btot+stot xcrd(is) = xcrd(i) ycrd(is) = ycrd(i) zcrd(is) = zcrd(i) bidx(is) = bidx(i) bat(is) = bat(i) endif end do else do i = 1, iygbu ig = ygbu(i) stot = stot + 1 is = btot+stot xcrd(is) = xcrd(ig) ycrd(is) = ycrd(ig) zcrd(is) = zcrd(ig) bidx(is) = bidx(ig) bat(is) = bat(ig) end do endif c c send boundary layer particles to next buffer c iny = ipy - 1 if (iny.eq.-1) iny = idy - 1 call i_xyz_to_proc(rnode,ipx,iny,ipz,idx,idy,idz) call gather_buf(rnode,stot) c stot = 0 if (nolist) then iygrd = 0 do i = 1, antot if (ra(i,2,1)-ymin.lt.rcmax) then iygrd = iygrd + 1 ygrd(iygrd) = i stot = stot + 1 is = btot+stot xcrd(is) = ra(i,1,1) ycrd(is) = ra(i,2,1) zcrd(is) = ra(i,3,1) bidx(is) = aidx(i) bat(is) = at(i) endif end do else do i = 1, iygrd ig = ygrd(i) stot = stot + 1 is = btot+stot xcrd(is) = ra(ig,1,1) ycrd(is) = ra(ig,2,1) zcrd(is) = ra(ig,3,1) bidx(is) = aidx(ig) bat(is) = at(ig) end do endif c if (nolist) then iygbd = 0 do i = 1, btot if (ycrd(i)-ymin.lt.rcmax.and.ycrd(i).gt.ymin) then iygbd = iygbd + 1 ygbd(iygbd) = i stot = stot + 1 is = btot+stot xcrd(is) = xcrd(i) ycrd(is) = ycrd(i) zcrd(is) = zcrd(i) bidx(is) = bidx(i) bat(is) = bat(i) endif end do else do i = 1, iygbd ig = ygbd(i) stot = stot + 1 is = btot+stot xcrd(is) = xcrd(ig) ycrd(is) = ycrd(ig) zcrd(is) = zcrd(ig) bidx(is) = bidx(ig) bat(is) = bat(ig) end do endif c c send boundary layer particles to next buffer c iny = ipy + 1 if (iny.eq.idy) iny = 0 call i_xyz_to_proc(rnode,ipx,iny,ipz,idx,idy,idz) call gather_buf(rnode,stot) endif c c repeat for z axis c if (idz.eq.2) then c c both boundaries are adjacent to boundaries on next processor c stot = 0 if (nolist) then izgru = 0 do i = 1, antot if ((zmax-ra(i,3,1).lt.rcmax.or. + ra(i,3,1)-zmin.lt.rcmax)) then izgru = izgru + 1 zgru(izgru) = i stot = stot + 1 is = btot+stot xcrd(is) = ra(i,1,1) ycrd(is) = ra(i,2,1) zcrd(is) = ra(i,3,1) bidx(is) = aidx(i) bat(is) = at(i) endif end do else do i = 1, izgru ig = zgru(i) stot = stot + 1 is = btot+stot xcrd(is) = ra(ig,1,1) ycrd(is) = ra(ig,2,1) zcrd(is) = ra(ig,3,1) bidx(is) = aidx(ig) bat(is) = at(ig) end do endif c if (nolist) then izgbu = 0 do i = 1, btot if ((zmax-zcrd(i).lt.rcmax.or. + zcrd(i)-zmin.lt.rcmax)) then izgbu = izgbu + 1 zgbu(izgbu) = i stot = stot + 1 is = btot+stot xcrd(is) = xcrd(i) ycrd(is) = ycrd(i) zcrd(is) = zcrd(i) bidx(is) = bidx(i) bat(is) = bat(i) bidx(is) = bidx(i) bat(is) = bat(i) endif end do else do i = 1, izgbu ig = zgbu(i) stot = stot + 1 is = btot+stot xcrd(is) = xcrd(ig) ycrd(is) = ycrd(ig) zcrd(is) = zcrd(ig) bidx(is) = bidx(ig) bat(is) = bat(ig) end do endif c c send boundary layer particles to next buffer c inz = ipz - 1 if (inz.eq.-1) inz = idz - 1 call i_xyz_to_proc(rnode,ipx,ipy,inz,idx,idy,idz) call gather_buf(rnode,stot) elseif (idz.gt.2) then c c each boundary is adjacent to a different processor c stot = 0 if (nolist) then izgru = 0 do i = 1, antot if (zmax-ra(i,3,1).lt.rcmax) then izgru = izgru + 1 zgru(izgru) = i stot = stot + 1 is = btot+stot xcrd(is) = ra(i,1,1) ycrd(is) = ra(i,2,1) zcrd(is) = ra(i,3,1) bidx(is) = aidx(i) bat(is) = at(i) endif end do else do i = 1, izgru ig = zgru(i) stot = stot + 1 is = btot+stot xcrd(is) = ra(ig,1,1) ycrd(is) = ra(ig,2,1) zcrd(is) = ra(ig,3,1) bidx(is) = aidx(ig) bat(is) = at(ig) end do endif c if (nolist) then izgbu = 0 do i = 1, btot if (zmax-zcrd(i).lt.rcmax) then izgbu = izgbu + 1 zgbu(izgbu) = i stot = stot + 1 is = btot+stot xcrd(is) = xcrd(i) ycrd(is) = ycrd(i) zcrd(is) = zcrd(i) bidx(is) = bidx(i) bat(is) = bat(i) endif end do else do i = 1, izgbu ig = zgbu(i) stot = stot + 1 is = btot+stot xcrd(is) = xcrd(ig) ycrd(is) = ycrd(ig) zcrd(is) = zcrd(ig) bidx(is) = bidx(ig) bat(is) = bat(ig) end do endif c c send boundary layer particles to next buffer c inz = ipz - 1 if (inz.eq.-1) inz = idz - 1 call i_xyz_to_proc(rnode,ipx,ipy,inz,idx,idy,idz) call gather_buf(rnode,stot) c stot = 0 if (nolist) then izgrd = 0 do i = 1, antot if (ra(i,3,1)-zmin.lt.rcmax) then izgrd = izgrd + 1 zgrd(izgrd) = i stot = stot + 1 is = btot+stot xcrd(is) = ra(i,1,1) ycrd(is) = ra(i,2,1) zcrd(is) = ra(i,3,1) bidx(is) = aidx(i) bat(is) = at(i) endif end do else do i = 1, izgrd ig = zgrd(i) stot = stot + 1 is = btot+stot xcrd(is) = ra(ig,1,1) ycrd(is) = ra(ig,2,1) zcrd(is) = ra(ig,3,1) bidx(is) = aidx(ig) bat(is) = at(ig) end do endif c if (nolist) then izgbd = 0 do i = 1, btot if (zcrd(i)-zmin.lt.rcmax.and.zcrd(i).gt.zmin) then izgbd = izgbd + 1 zgbd(izgbd) = i stot = stot + 1 is = btot+stot xcrd(is) = xcrd(i) ycrd(is) = ycrd(i) zcrd(is) = zcrd(i) bidx(is) = bidx(i) bat(is) = bat(i) endif end do else do i = 1, izgbd ig = zgbd(i) stot = stot + 1 is = btot+stot xcrd(is) = xcrd(ig) ycrd(is) = ycrd(ig) zcrd(is) = zcrd(ig) bidx(is) = bidx(ig) bat(is) = bat(ig) end do endif c c send boundary layer particles to next buffer c inz = ipz + 1 if (inz.eq.idz) inz = 0 call i_xyz_to_proc(rnode,ipx,ipy,inz,idx,idy,idz) call gather_buf(rnode,stot) endif c call heapsort(1) cdbg do i = 1, antot cdbg write(6,178) inode,i,aidx(i) cdbg end do cdbg 178 format(i3,' Gather aidx(',i3,'): ',i8) cdbg do i = 1, btot cdbg write(6,179) inode,i,bidx(i) cdbg end do cdbg 179 format(i3,' Gather bidx(',i3,'): ',i8) c tmstat(6) = tmstat(6) + wraptime() - tbeg c return end c subroutine gather_buf(rnode,stot) #include "common.fh" c double precision buf(3,MAXAT) integer ibuf(2,MAXAT) double precision tbeg,wraptime integer rnode,i integer me,one,ld2,ld3,is,rtot,stot c c Get data from processor rnode. The amount of data on the c current processor that actually needs to be accessed c represents stot particles. c me = ga_nodeid() one = 1 ld2 = 2 ld3 = 3 c c exchange the size of lists c tbeg = wraptime() c do i = btot+1, btot+stot is = i - btot buf(1,is) = xcrd(i) buf(2,is) = ycrd(i) buf(3,is) = zcrd(i) ibuf(1,is) = bidx(i) ibuf(2,is) = bat(i) end do c gsize_hi = gsize_lo(me) call nga_put(g_size,gsize_lo(me),gsize_hi,stot,one) gcoords_hi(1) = 3 gcoords_hi(2) = gcoords_lo(2,me) + stot - 1 if (stot.gt.0) call nga_put(g_coords,gcoords_lo(1,me), + gcoords_hi,buf,ld3) giat_hi(1) = 2 giat_hi(2) = giat_lo(2,me) + stot - 1 if (stot.gt.0) call nga_put(g_iat,giat_lo(1,me),giat_hi,ibuf,ld2) call ga_sync() gsize_hi = gsize_lo(rnode) call nga_get(g_size,gsize_lo(rnode),gsize_hi,rtot,one) gcoords_hi(2) = gcoords_lo(2,rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_coords,gcoords_lo(1,rnode), + gcoords_hi,buf,ld3) giat_hi(2) = giat_lo(2,rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_iat,giat_lo(1,rnode), + giat_hi,ibuf,ld2) c do i = btot+1, btot+rtot is = i - btot xcrd(i) = buf(1,is) ycrd(i) = buf(2,is) zcrd(i) = buf(3,is) bidx(i) = ibuf(1,is) bat(i) = ibuf(2,is) end do c call ga_sync() btot = btot+rtot c tmstat(11) = tmstat(11) + wraptime() - tbeg c return end ga-5.9.2/global/examples/md_cluster/grp_sim.F000066400000000000000000000123061500715745200211140ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif #define USE_SUBGROUPS 1 program grp_sim #include "common.fh" c integer MAXTASKS parameter (MAXTASKS=2000) integer i,j,me,icnt,ndim,one integer heap, stack, group_size, nprocs, ngroups, my_grp integer max_task, itask, jtask, natom1, natom2 integer group_list(2000), proc_list(2000) double precision tbeg, wraptime, elapsed double precision task_time(MAXTASKS),task_beg double precision proc_time(MD_MAXPROC), delta_t double precision maxtime, mintime,mingtime,maxgtime logical status c c This is the main calling program for the Molecular Dynamics c calculation. c c c Initialize message passing c #ifdef MSG_COMMS_MPI integer ierr call mpi_init(ierr) #else call pbeginf #endif tbeg = wraptime() c call ga_initialize() c c Initialize global arrays c heap = 2000000 stack = 2000000 c heap = 2000 c stack = 2000 if (.not.ma_init(MT_DBL, stack, heap)) + call ga_error("ma_init failed",-1) c c Create process groups c #if USE_SUBGROUPS group_size = 1 #else group_size = ga_nnodes() #endif max_task = 8 one = 1 nprocs = ga_nnodes() me = ga_nodeid() my_grp = (me-mod(me,group_size))/group_size do i = 1, min(max_task, MAXTASKS) task_time(i) = 0.0d00 end do #if USE_SUBGROUPS c write(6,101) my_grp,me 101 format('My group is ',i2,' on proc ',i3) ngroups = nprocs/group_size do i = 1, min(nprocs, MD_MAXPROC) proc_time(i) = 0.0d00 end do c write(6,102) ngroups,me 102 format('Ngroups is ',i2,' on proc ',i3) icnt = 0 do i = 1, ngroups do j = 1, group_size proc_list(j) = icnt icnt = icnt + 1 end do group_list(i) = ga_pgroup_create(proc_list,group_size) end do #endif c c Create global array to use for master-worker algorithm c g_counter = ga_create_handle() ndim = 1 call ga_set_data(g_counter,ndim,ndim,MT_INT) status = ga_allocate(g_counter) call ga_zero(g_counter) c write(6,103) me 103 format('Created counter array on ',i3) #if USE_SUBGROUPS c write(6,104) me,group_list(my_grp+1) 104 format('Default group on ',i3,' is ',i2) call ga_pgroup_set_default(group_list(my_grp+1)) c write(6,105) me 105 format('Set default group on ',i3) #endif 100 if (ga_nodeid().eq.0) then itask = nga_read_inc(g_counter,one,one) else itask = 0 endif call ga_igop(1,itask,1,'+') if (itask.lt.max_task) then c write(6,106) itask,me 106 format('Executing task ',i3,' on proc ',i3) natom1 = 400 natom1 = 0 jtask = max_task - 1 - itask natom2 = jtask*5 + 25 natom2 = jtask + 2 task_beg = wraptime() call cl_sim(natom1,natom2,jtask) delta_t = wraptime() - task_beg if (itask.lt.MAXTASKS) task_time(itask+1) = delta_t #if USE_SUBGROUPS i = ga_pgroup_nodeid(ga_pgroup_get_world()) i = i/group_size+1 if (i.le.MD_MAXPROC) proc_time(i) = delta_t #endif go to 100 endif #if USE_SUBGROUPS call ga_pgroup_set_default(ga_pgroup_get_world()) #endif call ga_dgop(3,task_time,MAXTASKS,'+') task_time(1) = task_time(1)/dble(group_size) mintime = task_time(1) maxtime = task_time(1) do i = 2, min(max_task, MAXTASKS) task_time(i) = task_time(i)/dble(group_size) if (task_time(i).gt.maxtime) maxtime=task_time(i) if (task_time(i).lt.mintime) mintime=task_time(i) end do call ga_dgop(4,proc_time,MD_MAXPROC,'+') proc_time(1) = proc_time(1)/dble(group_size) mingtime = proc_time(1) maxgtime = proc_time(1) do i = 2, min(ngroups, MD_MAXPROC) proc_time(i) = proc_time(i)/dble(group_size) if (proc_time(i).gt.maxgtime) maxgtime=proc_time(i) if (proc_time(i).lt.mingtime) mingtime=proc_time(i) end do elapsed = wraptime()-tbeg call ga_dgop(2,elapsed,1,'+') elapsed = elapsed/dble(nprocs) if (me.eq.0) then do i = 1, min(max_task, MAXTASKS) write(6,300) i,task_time(i) end do do i = 1, min(ngroups, MD_MAXPROC) write(6,301) i,proc_time(i) end do write(6,201) max_task write(6,202) group_size write(6,203) mintime write(6,204) maxtime write(6,205) mingtime write(6,206) maxgtime write(6,200) elapsed endif 200 format('Elapsed time for simulation : ',f16.4) 201 format('Number of tasks : ',i12) 202 format('Number of processors in group: ',i12) 203 format('Minimum time for task : ',f16.4) 204 format('Maximum time for task : ',f16.4) 205 format('Minimum time for group : ',f16.4) 206 format('Maximum time for group : ',f16.4) 300 format('Time for task[',i3,'] : ',f16.4) 301 format('Time for group[',i3,'] : ',f16.4) call ga_terminate() #ifdef MSG_COMMS_MPI c write(6,*) 'Calling mpi_finalize' call mpi_finalize() #else call pend #endif c write(6,*) 'Called mpi_finalize' c c close(6) stop end ga-5.9.2/global/examples/md_cluster/hash.F000066400000000000000000000025031500715745200203750ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine clear_hash #include "common.fh" integer i c c Clear all information from hash tables prior to setting new tables c do i = 1, MAXAT link_a(i) = 0 top_a(i) = 0 hash_key_a(i) = 0 hash_value_a(i) = 0 end do a_cnt = 0 return end c subroutine add_hash_a(igx, idx) #include "common.fh" integer igx, idx, ifunc, itmp c c Add an element to hash table A, which tracks local particles. c Start by computing hash function value of idx. c ifunc = mod(igx,MAXAT) + 1 c c Store value in a linked list c a_cnt = a_cnt+1 itmp = top_a(ifunc) top_a(ifunc) = a_cnt link_a(a_cnt) = itmp hash_key_a(a_cnt) = igx hash_value_a(a_cnt) = idx return end c integer function get_hash_a(igx) #include "common.fh" integer igx, ifunc, itmp, jtmp c c Return the local index of a locally held particle from the global c index. Start by computing the hash function value of idx. c ifunc = mod(igx,MAXAT) + 1 get_hash_a = 0 itmp = top_a(ifunc) do while (hash_key_a(itmp).ne.igx.and.itmp.gt.0) itmp = link_a(itmp) end do if (itmp.gt.0) then get_hash_a = hash_value_a(itmp) endif return end ga-5.9.2/global/examples/md_cluster/heapsort.F000066400000000000000000000112531500715745200213010ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine heapsort(iflg) #include "common.fh" c c c This routine sorts either the locally owned particles (iflg=0) or c the particles stored in the buffer arrays (iflg=1) so that they are c arranged in order of increasing index c double precision rr1(3,6),rmss double precision tbeg,wraptime c integer l, ir, i,j integer iflg,jj,kk,iat,idx c if (iflg.eq.1) go to 1000 if ( antot .eq. 0 ) return if ( antot .eq. 1 ) return c tbeg = wraptime() c l = antot/2 + 1 ir = antot c 10 continue if ( l .gt. 1 ) then l = l-1 do jj = 1, 3 do kk = 1, 6 rr1(jj,kk) = ra(l,jj,kk) end do end do rmss = mass(l) iat = at(l) idx = aidx(l) else do jj = 1, 3 do kk = 1, 6 rr1(jj,kk) = ra(ir,jj,kk) end do end do rmss = mass(ir) iat = at(ir) idx = aidx(ir) c do jj = 1, 3 do kk = 1, 6 ra(ir,jj,kk) = ra(1,jj,kk) end do end do mass(ir) = mass(1) at(ir) = at(1) aidx(ir) = aidx(1) ir = ir - 1 if ( ir .eq. 1 ) then do jj = 1, 3 do kk = 1, 6 ra(1,jj,kk) = rr1(jj,kk) end do end do mass(1) = rmss at(1) = iat aidx(1) = idx tmstat(9) = tmstat(9) + wraptime() - tbeg return endif endif c if ( ir .eq. 1 ) then tmstat(9) = tmstat(9) + wraptime() - tbeg return endif c i = l j = l + l 20 continue if ( j .le. ir ) then if ( j .lt. ir ) then if ( aidx(j) .lt. aidx(j+1)) j = j+1 endif if ( idx .lt. aidx(j)) then do jj = 1, 3 do kk = 1, 6 ra(i,jj,kk) = ra(j,jj,kk) end do end do mass(i) = mass(j) at(i) = at(j) aidx(i) = aidx(j) i = j j = j + j else j = ir + 1 endif go to 20 endif do jj = 1, 3 do kk = 1, 6 ra(i,jj,kk) = rr1(jj,kk) end do end do mass(i) = rmss at(i) = iat aidx(i) = idx goto 10 c 1000 if ( btot .eq. 0 ) return if ( btot .eq. 1 ) return c tbeg = wraptime() c l = btot/2 + 1 ir = btot c 30 continue if ( l .gt. 1 ) then l = l-1 rr1(1,1) = xcrd(l) rr1(2,1) = ycrd(l) rr1(3,1) = zcrd(l) rr1(1,2) = xfrc(l) rr1(2,2) = yfrc(l) rr1(3,2) = zfrc(l) rr1(3,3) = zacc(l) rmss = mbuf(l) iat = bat(l) idx = bidx(l) else rr1(1,1) = xcrd(ir) rr1(2,1) = ycrd(ir) rr1(3,1) = zcrd(ir) rr1(1,2) = xfrc(ir) rr1(2,2) = yfrc(ir) rr1(3,2) = zfrc(ir) rr1(3,3) = zacc(ir) rmss = mbuf(ir) iat = bat(ir) idx = bidx(ir) c xcrd(ir) = xcrd(1) ycrd(ir) = ycrd(1) zcrd(ir) = zcrd(1) xfrc(ir) = xfrc(1) yfrc(ir) = yfrc(1) zfrc(ir) = zfrc(1) zacc(ir) = zacc(1) mbuf(ir) = mbuf(1) bat(ir) = bat(1) bidx(ir) = bidx(1) ir = ir - 1 if ( ir .eq. 1 ) then xcrd(1) = rr1(1,1) ycrd(1) = rr1(2,1) zcrd(1) = rr1(3,1) xfrc(1) = rr1(1,2) yfrc(1) = rr1(2,2) zfrc(1) = rr1(3,2) zacc(1) = rr1(3,3) mbuf(1) = rmss bat(1) = iat bidx(1) = idx tmstat(10) = tmstat(10) + wraptime() - tbeg return endif endif c if ( ir .eq. 1 ) then tmstat(10) = tmstat(10) + wraptime() - tbeg return endif c i = l j = l + l 40 continue if ( j .le. ir ) then if ( j .lt. ir ) then if ( bidx(j) .lt. bidx(j+1)) j = j+1 endif if ( idx .lt. bidx(j)) then xcrd(i) = xcrd(j) ycrd(i) = ycrd(j) zcrd(i) = zcrd(j) xfrc(i) = xfrc(j) yfrc(i) = yfrc(j) zfrc(i) = zfrc(j) zacc(i) = zacc(j) mbuf(i) = mbuf(j) bat(i) = bat(j) bidx(i) = bidx(j) i = j j = j + j else j = ir + 1 endif go to 40 endif xcrd(i) = rr1(1,1) ycrd(i) = rr1(2,1) zcrd(i) = rr1(3,1) xfrc(i) = rr1(1,2) yfrc(i) = rr1(2,2) zfrc(i) = rr1(3,2) zacc(i) = rr1(3,3) mbuf(i) = rmss bat(i) = iat bidx(i) = idx goto 30 c end ga-5.9.2/global/examples/md_cluster/kin.F000066400000000000000000000053611500715745200202400ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine kin #include "common.fh" c integer i,j c c This subroutine calculates the kinetic energy of the current c configuration. It assumes that the center of mass motion in c the xy plane has already been subtracted out. c nrg(4) = 0.0d00 nrg(8) = 0.0d00 do 200 j = 1, 3 do 100 i = 1, antot nrg(4) = nrg(4) + 0.5d00 * ra(i,j,5)**2 / mass(i) c nrg(4) = nrg(4) + 0.5d00 * mass(i) * ra(i,j,2)**2 100 continue 200 continue c return end c subroutine com #include "common.fh" c double precision px,py,pz,vx,vy,vz,tcmass double precision comm(4) integer i c c This subroutine calculates the contributions to the center of mass c and the center of mass momentum from each processor c c Calculate center of mass motion momentum c px = 0.0d00 py = 0.0d00 pz = 0.0d00 tcmass = 0.0d00 do 100 i = 1, antot px = px + ra(i,1,5) py = py + ra(i,2,5) pz = pz + ra(i,3,5) tcmass = tcmass + mass(i) 100 continue c comm(1) = px comm(2) = py comm(3) = pz comm(4) = tcmass call ga_dgop(7,comm,4,'+') px = comm(1) py = comm(2) pz = comm(3) tcmass = comm(4) c nrg(22) = px nrg(23) = py nrg(24) = pz c nrg(25) = tcmass c return end c subroutine com_rmv #include "common.fh" c double precision px,py,pz,vx,vy,vz,tcmass integer i c c This subroutine subtracts out the center of mass motion c Calculate center of mass motion momentum c px = nrg(22) py = nrg(23) pz = nrg(24) tcmass = nrg(25) c c Calculate center of mass velocity c vx = px / tcmass vy = py / tcmass vz = pz / tcmass c c Subtract out center of mass velocity c do 200 i = 1, antot ra(i,1,2) = ra(i,1,2) - vx ra(i,2,2) = ra(i,2,2) - vy ra(i,3,2) = ra(i,3,2) - vz ra(i,1,5) = ra(i,1,5) - mass(i) * vx ra(i,2,5) = ra(i,2,5) - mass(i) * vy ra(i,3,5) = ra(i,3,5) - mass(i) * vz 200 continue c return end c subroutine fixper #include "common.fh" c integer i c c This subroutine fixes up the periodic boundary conditions c in the xy plane after the particle positions have been updated c do 100 i = 1, antot ra(i,1,1) = ra(i,1,6) - xbox * anint(ra(i,1,6)/xbox) if (ra(i,1,1).eq.xbox2) ra(i,1,1) = -xbox2 ra(i,2,1) = ra(i,2,6) - ybox * anint(ra(i,2,6)/ybox) if (ra(i,2,1).eq.ybox2) ra(i,2,1) = -ybox2 ra(i,3,1) = ra(i,3,6) - zbox * anint(ra(i,3,6)/zbox) if (ra(i,3,1).eq.zbox2) ra(i,3,1) = -zbox2 100 continue return end ga-5.9.2/global/examples/md_cluster/md_lj.in000066400000000000000000000003411500715745200207560ustar00rootroot000000000000001000000000, 0.01, 2, -44933 2.5,20,0 1, 0.5, 0.05, 1000.0, 0.005 1, 1000, 100, 6, 0.5, 0.05, 1000.0, 0.005 1001, 1000000000, 100, 6, 0.5, 0.05, 1000.0, 0.005 2,2 10000000 50000,1000000,2000000 0.0076, 0.5, 0.0, 2.349 20, 100 ga-5.9.2/global/examples/md_cluster/mdinit.F000066400000000000000000000046011500715745200207370ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine mdinit #include "common.fh" c double precision six,twelve,ac integer i,j c c turn of all cluster related functions c nocluster = .false. c c This subroutine sets up the potentials, assigns atomic masses c and finishes initializing the calculation c six = 6.0d00 twelve = 12.0d00 c c initialize timing and energy statistics arrays c do i = 1, MAXNRG nrg(i) = 0.0d00 anrg(i) = 0.0d00 anrg2(i) = 0.0d00 end do nrgcnt = 0 nrg2ct = 0 do i = 1, MAXTIM tmstat(i) = 0.0d00 end do failcount = 0 c c assign atom masses c do i = 1, antot mass(i) = amass(at(i)) end do c c Initialize cutoffs for potential arrays. c rcmax = 0.0d00 do i = 1, atnum do j = 1, atnum ac = acut(i,j) if (ac.gt.rcmax) rcmax = ac rcorr(i,j) = e12(i,j) / ac**12 dcorr(i,j) = -e6(i,j) / ac**6 if (icut.eq.1) then frcorr(i,j) = - twelve * e12(i,j) / ac**13 fdcorr(i,j) = six * e6(i,j) / ac**7 else frcorr(i,j) = 0.0d00 fdcorr(i,j) = 0.0d00 endif end do end do rcmax = rcmax + 1.0d00 c c calculate total number of degrees of freedom c nrg(1) = dble(3*atot-3) nrg(2) = nrg(1) / 2.0 c c Convert algorithm parameters into system units c istep = 0 tavg = 0.0d00 itavg = 0 tmstrt = 0 t_done = 0.0d00 t_rmndr = tau c c Initialize parameters for mass and temperature degrees of freedom c vol1 = xbox * ybox * zbox alen1(1) = xbox alen1(2) = ybox alen1(3) = zbox vol2 = 0.0d00 vol3 = 0.0d00 do j = 1, 3 alen2(j) = 0.0d00 alen3(j) = 0.0d00 end do scal1 = 1.0d00 scal2 = 0.0d00 scal3 = 0.0d00 c c Initialize bins accumulating r_cluster values c do j = 1, 10 do i = 1, mcbins r_distr(i,j) = 0 end do end do c c Initialize absolute coordinates c call cluster_center call update call force mbflg = .true. call cluster_com c tavg = 0.0d00 do j = 1, 3 do i = 1, antot ra(i,j,7) = ra(i,j,8) ra(i,j,3) = ra(i,j,4)/mass(i) end do end do c return end ga-5.9.2/global/examples/md_cluster/mdout.F000066400000000000000000000122341500715745200206040ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine mdout #include "common.fh" c integer pnum,idx,idy,idz,me c c This routine finishes off the calculation by writing c out accumulated information, final configurations etc. c pnum = ga_nnodes() me = ga_nodeid() call factor(pnum,idx,idy,idz) if (ga_nodeid().eq.0.and.l_stdio) then write(6,100) pnum write(6,200) idx,idy,idz write(6,300) failcount endif c call prtnrg call tmout call cluster_print_binr if (l_rst) call mdrst c return 100 format('Total number of processors used in simulation :',i9) 200 format('Processors configured as ',i2,' X ',i2,' X ',i2,' grid') 300 format('Total number of collision failures :',i9) end c subroutine header #include "common.fh" c integer oldstp save oldstp data oldstp /0/ c c this prints out the current MD time if time has c changed since the last call to this subroutine c if (ga_nodeid().ne.0) return if (istep.ne.oldstp.and.l_stdio) then write(6,1000) float(istep) * tau oldstp = istep endif return 1000 format(1x,'Statistics for MD time ',f16.4) end c subroutine mdrst #include "common.fh" c double precision x,y,z,rx,ry,rz double precision rbuf(3,MAXAT) integer i,inode,pnum,me,one,ld2,ld3 integer rptr,rnode,rtot,ibuf(2,MAXAT) character*32 filename c c This routine finishes off the calculation by writing c out accumulated information, final configurations etc. c c c Write out final configuration to file 'md.rst' c pnum = ga_nnodes() me = ga_nodeid() call sort c c write all coordinates and velocities to buffers c do i = 1, antot xcrd(i) = ra(i,1,1) ycrd(i) = ra(i,2,1) zcrd(i) = ra(i,3,1) xfrc(i) = ra(i,1,2) yfrc(i) = ra(i,2,2) zfrc(i) = ra(i,3,2) bidx(i) = aidx(i) bat(i) = at(i) end do btot = antot c c open file from node 0 and write out box size c if (task_id.lt.10) then write(filename,100) task_id else if (task_id.ge.10.and.task_id.lt.100) then write(filename,101) task_id else if (task_id.ge.100.and.task_id.lt.1000) then write(filename,102) task_id else if (task_id.ge.1000.and.task_id.lt.10000) then write(filename,103) task_id endif 100 format('md.rst',i1) 101 format('md.rst',i2) 102 format('md.rst',i3) 103 format('md.rst',i4) if (me.eq.0) then open(unit=10,file=filename,status='unknown') write(10,5400) atot write(10,5300) xbox,ybox,zbox,r_cluster endif c c get data from other nodes and write to md.rst c one = 1 ld2 = 2 ld3 = 3 c c exchange the size of lists and put coordinate data in c global arrays c rptr = gsize_lo(me) call nga_put(g_size,rptr,rptr,btot,one) c gcoords_hi(1) = 3 gcoords_hi(2) = gcoords_lo(2,me) + btot - 1 giat_hi(1) = 2 giat_hi(2) = giat_lo(2,me) + btot - 1 do i = 1, btot rbuf(1,i) = xcrd(i) rbuf(2,i) = ycrd(i) rbuf(3,i) = zcrd(i) ibuf(1,i) = bidx(i) ibuf(2,i) = bat(i) end do if (btot.gt.0) call nga_put(g_coords,gcoords_lo(1,me), + gcoords_hi,rbuf,ld3) if (btot.gt.0) call nga_put(g_iat,giat_lo(1,me), + giat_hi,ibuf,ld2) c gfrc_hi(1) = 3 gfrc_hi(2) = gfrc_lo(2,me) + btot - 1 do i = 1, btot rbuf(1,i) = xfrc(i) rbuf(2,i) = yfrc(i) rbuf(3,i) = zfrc(i) end do if (btot.gt.0) call nga_put(g_frc,gfrc_lo(1,me),gfrc_hi,rbuf,ld3) call ga_sync() c do inode = 0, pnum - 1 if (me.eq.0) then rptr = gsize_lo(inode) call nga_get(g_size,rptr,rptr,rtot,one) gcoords_hi(2) = gcoords_lo(2,inode) + rtot - 1 giat_hi(2) = giat_lo(2,inode) + rtot - 1 if (rtot.gt.0) call nga_get(g_coords,gcoords_lo(1,inode), + gcoords_hi,rbuf,ld3) if (rtot.gt.0) call nga_get(g_iat,giat_lo(1,inode), + giat_hi,ibuf,ld2) do i = 1, rtot xcrd(i) = rbuf(1,i) ycrd(i) = rbuf(2,i) zcrd(i) = rbuf(3,i) bidx(i) = ibuf(1,i) bat(i) = ibuf(2,i) end do gfrc_hi(2) = gfrc_lo(2,inode) + rtot - 1 if (rtot.gt.0) call nga_get(g_frc,gfrc_lo(1,inode), + gfrc_hi,rbuf,ld3) do i = 1, rtot xfrc(i) = rbuf(1,i) yfrc(i) = rbuf(2,i) zfrc(i) = rbuf(3,i) end do c do i = 1, rtot if (istop.eq.1) then write(10,5000) bat(i),xcrd(i),ycrd(i),zcrd(i) else write(10,5100) bat(i),xcrd(i),ycrd(i),zcrd(i), + xfrc(i),yfrc(i),zfrc(i) endif end do endif end do if (me.eq.0) close(10) c 5000 format (i8,3(1pe13.5)) 5100 format (i8,6(1pe13.5)) 5300 format (4(1pe16.8)) 5400 format (i8) c return end ga-5.9.2/global/examples/md_cluster/mdstep.F000066400000000000000000000172361500715745200207570ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine mdstep #include "common.fh" c double precision vbox, rmax integer i,j,me logical newcfg double precision cluster_check_radius logical debug if (istep.gt.3930438) then debug = .false. else debug = .false. endif c c This routine guides the MD steps. c Begin the main loop through the MD steps c me = ga_nodeid() r_confine = 0.0d00 do 5000 istep = 1, nstep c newcfg = .false. mbflg = .false. cmflg = .false. ipmode = 0 t_rmndr = tau t_done = 0.0d00 cllsn_cnt = 0 c c Check to see if there are any special instructions c do 100 i = 1, nsc c c is end >= istep >= beg c if ((istep.ge.isc(i,1)).and.(istep.le.isc(i,2))) then c c is mod(istep-beg,inc) = 0 c if (mod(istep,isc(i,3)).eq.0) then c c get next configuration using the appropriate algorithm c if (isc(i,4).eq.1) then call estep newcfg = .true. elseif (isc(i,4).eq.2) then prssr = rsc(i,2) pmass = rsc(i,4) call pstep newcfg = .true. elseif (isc(i,4).eq.3) then tmprtr = rsc(i,1) prssr = rsc(i,2) pmass = rsc(i,4) call sstep newcfg = .true. elseif (isc(i,4).eq.4) then tmprtr = rsc(i,1) tmass = rsc(i,3) call tstep newcfg = .true. elseif (isc(i,4).eq.5) then tmprtr = rsc(i,1) prssr = rsc(i,2) tmass = rsc(i,3) pmass = rsc(i,4) call ptstep newcfg = .true. elseif (isc(i,4).eq.6) then tmprtr = rsc(i,1) call mbstep newcfg = .true. elseif (isc(i,4).eq.7) then itarg = isc(i,2) tmprtr = rsc(i,1) tvol = rsc(i,2) tmass = rsc(i,3) call vlstep newcfg = .true. elseif (isc(i,4).eq.8) then tmprtr = rsc(i,1) call kstep newcfg = .true. elseif (isc(i,4).eq.9) then tmprtr = rsc(i,1) prssr = rsc(i,2) tmass = rsc(i,3) pmass = rsc(i,4) ipmode = 1 call ptstep newcfg = .true. elseif (isc(i,4).eq.10) then tmprtr = rsc(i,1) prssr = rsc(i,2) tmass = rsc(i,3) pmass = rsc(i,4) ipmode = 2 call ptstep newcfg = .true. endif endif endif 100 continue c c get next configuration if no special step is taken c if (.not.newcfg) then if (dflalg.eq.1) then call estep elseif (dflalg.eq.2) then prssr = dfprs pmass = dfpm call pstep elseif (dflalg.eq.3) then tmprtr = dftmp prssr = dfprs pmass = dfpm call sstep elseif (dflalg.eq.4) then tmprtr = dftmp tmass = dftm call tstep elseif (dflalg.eq.5) then tmprtr = dftmp prssr = dfprs tmass = dftm pmass = dfpm call ptstep elseif (dflalg.eq.6) then tmprtr = dftmp call mbstep elseif (dflalg.eq.9) then tmprtr = dftmp prssr = dfprs tmass = dftm pmass = dfpm ipmode = 1 call ptstep elseif (dflalg.eq.10) then tmprtr = dftmp prssr = dfprs tmass = dftm pmass = dfpm ipmode = 2 call ptstep endif endif if (debug) then write(6,*) ga_nodeid(),' Got to 1 at step ',istep endif c c Update remaining energy quantities c nrg(3) = nrg(4) + nrg(6) vbox = xbox*ybox*zbox nrg(7) = nrg(5) * dble(atot-1) / vbox + nrg(15) if (istep.eq.equil_1) then call fixper do i = 1, antot do j = 1, 3 ra(i,j,6) = ra(i,j,1) end do end do call cluster_com call cluster_center rmax = cluster_check_radius() if (rmax.gt.r_cluster) r_cluster = rmax + 0.01 endif if (mod(istep,mcfreq).eq.0.and.istep.gt.equil_1) then call cluster_mc if (me.eq.0.and.l_rad) write(7,7100) dble(istep)*tau,r_cluster endif if (debug) then write(6,*) ga_nodeid(),' Got to 2 at step ',istep endif if (istep.gt.equil_2.and.r_cluster.le.cl_upper) + call cluster_binr if (debug) then write(6,*) ga_nodeid(),' Got to 3 at step ',istep endif if (istep.eq.window_1) call cluster_reset_binr(1) if (istep.eq.window_2) call cluster_reset_binr(2) c c Perform all statistical operations c on the new configuration. c c print pressure c if (mod(istep,istat).eq.0.and.l_stdio) then call header(istep) if (me.eq.0) write(6,6000) nrg(7) if (me.eq.0) write(6,6300) nrg(3) if (me.eq.0) write(6,6700) nrg(6),nrg(4),nrg(5) if (me.eq.0) write(6,6800) xbox,ybox,zbox,scal1 if (me.eq.0) write(6,6100) nrg(13),nrg(14), + nrg(17),nrg(21) if (me.eq.0) write(6,6900) nrg(9) endif c c accumulate energy statistics c if (istep.gt.equil_2) call estat c if (me.eq.0.and.l_step.and.mod(istep,1000).eq.0) then open(unit=2,file='step.cnt',status='unknown') write(2,*) 'proc : ',ga_pgroup_nodeid(ga_pgroup_get_world()) write(2,*) 'istep : ',istep write(2,6900) nrg(9) write(2,6100) nrg(13),nrg(14),nrg(17),nrg(21) write(2,6000) nrg(7) write(2,6300) nrg(3) write(2,6700) nrg(6),nrg(4),nrg(5) write(2,6800) xbox,ybox,zbox,scal1 write(2,7000) r_cluster write(2,7200) cl_lower write(2,7300) cl_upper close(2) endif 5000 continue return 6000 format(1x,'The instantaneous pressure is ',f12.4) 6100 format(1x,'Current energy statistics'/ + ' repulsion: ',f16.4,/ + ' dispersion: ',f16.4,/ + ' bonds: ',f16.4,/ + ' angles: ',f16.4) 6200 format(1x,'Statistics at time ',i6,' ps') 6300 format(1x,'The total energy is ',f12.4) 6700 format(' potential kinetic'/ + ' energy energy temperature'/, + 1x,3f13.3) 6800 format(1x,'The current simulation cell dimensions:'/ + ' x: ',f16.4,/ + ' y: ',f16.4,/ + ' z: ',f16.4,/ + ' s: ',f16.4) 6820 format(1x,'The current simulation cell dimensions:'/ + ' x: ',f16.4,/ + ' y: ',f16.4,/ + ' s: ',f16.4) 6900 format(1x,'The instantaneous value of the Hamiltonian is ',f12.4) 7000 format(1x,'The current value of confining sphere is ',f12.4) 7100 format(2f16.8) 7200 format(1x,'Lower bound of confining sphere ',f12.4) 7300 format(1x,'Upper bound of confining sphere ',f12.4) end ga-5.9.2/global/examples/md_cluster/newcfg.F000066400000000000000000000143521500715745200207300ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine newcfg(natom1,natom2,itask,rcluster,rcut) implicit none integer natom1, natom2, itask integer n, nc, nx, ny, nz, ntot parameter ( nc = 50, n = 2*nc ** 3) double precision lj(3,2) double precision cell,cell2,mult,xadd,yadd,zadd double precision multx,multy,multz,cmx,cmy,cmz double precision x,y,z,dx,dy,dz,rcluster,r,rx,ry,rz,rmax double precision volume, radius, pi, rcut double precision coords(n, 3) integer i, j, ix, iy, iz, iref, m, mp, mp1, mp4 integer one,two,nside, irad, ixcent, iycent, izcent integer ixmin,ixmax,iymin,iymax,izmin,izmax integer fill(nc,nc,nc,2), icnt character*32 filename c c ******************************************************* c mult = 1.5d00 lj(1,1) = 0.0d00 lj(2,1) = 0.0d00 lj(3,1) = 0.0d00 lj(1,2) = mult/2.0d00 lj(2,2) = mult/2.0d00 lj(3,2) = mult/2.0d00 c one = 1 two = 2 c if (itask.lt.10) then write(filename,100) itask else if (itask.ge.10.and.itask.lt.100) then write(filename,101) itask else if (itask.ge.100.and.itask.lt.1000) then write(filename,102) itask else if (itask.ge.1000.and.itask.lt.10000) then write(filename,103) itask endif 100 format('md.cfg',i1) 101 format('md.cfg',i2) 102 format('md.cfg',i3) 103 format('md.cfg',i4) open (unit=2,file=filename,status='unknown',form='formatted') c nside = nint((0.5d00*dble(natom1+natom2))**(1.0d00/3.0d00)) if (2*nside**3.lt.natom1+natom2) nside = nside+1 pi = 4.0d00*atan(1.0d00) volume = 0.5d00*dble(natom2)*mult**3 radius = 3.0d00*volume/(4.0d00*pi) radius = radius**(1.0d00/3.0d00) irad = int(radius/mult) + 1 if (2*irad.ge.nside) nside = 2*irad+2 do ix = 1, nside do iy = 1, nside do iz = 1, nside fill(ix,iy,iz,1) = 0 fill(ix,iy,iz,2) = 0 end do end do end do multx = mult * float(nside) multy = mult * float(nside) multz = mult * float(nside) cell = mult cell2 = 0.5d00 * cell dx = multx/float(nside) dy = multy/float(nside) dz = multz/float(nside) volume = 0.5d00*dble(natom2)*mult**3 radius = 3.0d00*volume/(4.0d00*pi) radius = radius**(1.0d00/3.0d00) irad = int(radius/mult) + 1 ixcent = nside/2 iycent = nside/2 izcent = nside/2 ixmin = ixcent - irad ixmax = ixcent + irad 300 icnt = 0 do ix = ixmin, ixmax iy = int(sqrt(dble(irad)**2-dble(ix-ixcent)**2)) iymin = iycent - iy iymax = iycent + iy do iy = iymin, iymax iz = int(sqrt(dble(irad)**2-dble(ix-ixcent)**2 + -dble(iy-iycent)**2)) izmin = izcent - iz izmax = izcent + iz do iz = izmin, izmax xadd = dx*float(ix-1) - multx/2.0d00 yadd = dy*float(iy-1) - multy/2.0d00 zadd = dz*float(iz-1) - multz/2.0d00 if (fill(ix,iy,iz,1).eq.0.and.icnt.lt.natom2) then icnt = icnt + 1 coords(natom1+icnt,1) = xadd + lj(1,1) coords(natom1+icnt,2) = yadd + lj(2,1) coords(natom1+icnt,3) = zadd + lj(3,1) fill(ix,iy,iz,1) = 1 endif if (fill(ix,iy,iz,2).eq.0.and.icnt.lt.natom2) then icnt = icnt + 1 coords(natom1+icnt,1) = xadd + lj(1,2) coords(natom1+icnt,2) = yadd + lj(2,2) coords(natom1+icnt,3) = zadd + lj(3,2) fill(ix,iy,iz,2) = 1 endif end do end do end do if (icnt.lt.natom2) then irad = irad+1 go to 300 endif c c Find center of mass of cluster c rx = 0.0d00 ry = 0.0d00 rz = 0.0d00 do icnt = 1, natom2 rx = rx + coords(natom1+icnt,1) ry = ry + coords(natom1+icnt,2) rz = rz + coords(natom1+icnt,3) end do cmx = rx/dble(natom2) cmy = ry/dble(natom2) cmz = rz/dble(natom2) rmax = rmax + sqrt(dx**2+dy**2+dz**2) c c Find radius of cluster c rmax = 0.0d00 do icnt = 1, natom2 rx = coords(natom1+icnt,1) - cmx ry = coords(natom1+icnt,2) - cmy rz = coords(natom1+icnt,3) - cmz r = sqrt(rx**2 + ry**2 + rz**2) if (r.gt.rmax) rmax = r end do rmax = rmax + 0.1 c icnt = 0 do ix = 1, nside do iy = 1, nside do iz = 1, nside xadd = dx*float(ix-1) - multx/2.0d00 yadd = dy*float(iy-1) - multy/2.0d00 zadd = dz*float(iz-1) - multz/2.0d00 if (fill(ix,iy,iz,1).eq.0.and.icnt.lt.natom1) then icnt = icnt + 1 coords(icnt,1) = xadd + lj(1,1) coords(icnt,2) = yadd + lj(2,1) coords(icnt,3) = zadd + lj(3,1) fill(ix,iy,iz,1) = 1 endif if (fill(ix,iy,iz,2).eq.0.and.icnt.lt.natom1) then icnt = icnt + 1 coords(icnt,1) = xadd + lj(1,2) coords(icnt,2) = yadd + lj(2,2) coords(icnt,3) = zadd + lj(3,2) fill(ix,iy,iz,2) = 1 endif end do end do end do c rcluster = rmax c c If there are no atoms of type 1, then assume cluster is c isolated and set size to 100 LJ units c if (natom1.eq.0) then multx = 25.0 multy = 25.0 multz = 25.0 endif if (rcluster.lt.rcut) then rcluster = rcut endif write(2,2100) natom1+natom2 write(2,2000) multx,multy,multz,rcluster do i = 1, natom1 coords(i,1) = coords(i,1) - cmx coords(i,2) = coords(i,2) - cmy coords(i,3) = coords(i,3) - cmz write(2,200) one,(coords(i,j),j=1,3) end do do i = natom1+1, natom1+natom2 coords(i,1) = coords(i,1) - cmx coords(i,2) = coords(i,2) - cmy coords(i,3) = coords(i,3) - cmz write(2,200) two,(coords(i,j),j=1,3) end do c 200 format (i5,3f12.5) 2000 format (4f16.8) 2100 format (i8) close(2) return end ga-5.9.2/global/examples/md_cluster/nextc.F000066400000000000000000000450621500715745200206020ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine estep #include "common.fh" integer iter iter = 0 c prsflg = .false. tmpflg = .false. ptflg = .false. do while (t_rmndr.gt.0.0d00) iter = iter + 1 call pred call force call corr if (iter.gt.100) then write(6,*) 'istep: ',istep write(6,*) 't_rmndr: ',t_rmndr endif if (iter.gt.1000) then call ga_error('Failed in estep',istep) endif end do c return end c subroutine pstep #include "common.fh" integer iter iter = 0 c prsflg = .true. tmpflg = .false. ptflg = .false. do while (t_rmndr.gt.0.0d00) iter = iter + 1 call pred call force call corr if (iter.gt.100) then write(6,*) 'istep: ',istep write(6,*) 't_rmndr: ',t_rmndr endif if (iter.gt.1000) then call ga_error('Failed in pstep',istep) endif end do c return end c subroutine tstep #include "common.fh" integer iter iter = 0 c prsflg = .false. tmpflg = .true. ptflg = .false. do while (t_rmndr.gt.0.0d00) iter = iter + 1 call pred call force call corr if (iter.gt.100) then write(6,*) 'istep: ',istep write(6,*) 't_rmndr: ',t_rmndr endif if (iter.gt.1000) then call ga_error('Failed in tstep',istep) endif end do c return end c subroutine sstep #include "common.fh" c double precision scale integer i,j integer iter iter = 0 c c This subroutine scales the atomic velocities and momenta c so that the system temperature corresponds to the desired c temperature. c if (nrg(5).gt.0.0d00) then scale = sqrt(tmprtr / nrg(5)) else scale = 1.0d00 endif do 200 j = 1, 3 do 100 i = 1, antot ra(i,j,2) = scale * ra(i,j,2) ra(i,j,5) = scale * ra(i,j,5) 100 continue 200 continue prsflg = .false. tmpflg = .false. ptflg = .false. mbflg = .true. do while (t_rmndr.gt.0.0d00) iter = iter + 1 call pred call force call corr if (iter.gt.100) then write(6,*) 'istep: ',istep write(6,*) 't_rmndr: ',t_rmndr endif if (iter.gt.1000) then call ga_error('Failed in sstep',istep) endif end do c return end c subroutine kstep #include "common.fh" c double precision scale,ke integer i,j integer iter iter = 0 c c This subroutine adjusts the kinetic energy so that the c average temperature corresponds to the target temperature c if (itavg.gt.0) then tavg = tavg / dble(itavg) ke = nrg(2) * (tmprtr - tavg) scale = (nrg(4) + ke)/nrg(4) else scale = 0.0d00 endif if (scale.gt.0.0d00) then scale = sqrt(scale) else scale = 1.0d00 endif do 200 j = 1, 3 do 100 i = 1, antot ra(i,j,2) = scale * ra(i,j,2) ra(i,j,5) = scale * ra(i,j,5) 100 continue 200 continue tavg = 0.0d00 itavg = 0 prsflg = .false. tmpflg = .false. ptflg = .false. mbflg = .true. do while (t_rmndr.gt.0.0d00) iter = iter + 1 call pred call force call corr if (iter.gt.100) then write(6,*) 'istep: ',istep write(6,*) 't_rmndr: ',t_rmndr endif if (iter.gt.1000) then call ga_error('Failed in kstep',istep) endif end do c return end c subroutine ptstep #include "common.fh" integer iter iter = 0 c prsflg = .false. tmpflg = .false. ptflg = .true. do while (t_rmndr.gt.0.0d00) iter = iter + 1 call pred call force call corr if (iter.gt.100) then write(6,*) 'istep: ',istep write(6,*) 't_rmndr: ',t_rmndr endif if (iter.gt.1000) then call ga_error('Failed in ptstep',istep) endif end do return end c subroutine mbstep #include "common.fh" c double precision scl,gasdev integer i,j integer iter iter = 0 c c This subroutine updates configuration if only a single c configuration is available. It starts by assigning a c Boltzmann distribution of velocities and momenta to all c the atoms and then calculating the next configuration c using a regular Gear step. c c generate atomic velocities c do 200 j = 1, 3 do 100 i = 1, antot scl = sqrt(tmprtr / mass(i)) ra(i,j,2) = scl * gasdev(0) ra(i,j,5) = mass(i) * ra(i,j,2) ra(i,j,3) = ra(i,j,4)/mass(i) 100 continue 200 continue c c Re-initialize absolute coordinates c call com call com_rmv call cluster_com tmstrt = istep scal2 = 0.0d00 scal3 = 0.0d00 vol2 = 0.0d00 vol3 = 0.0d00 prsflg = .false. tmpflg = .false. ptflg = .false. mbflg = .true. cmflg = .true. do while (t_rmndr.gt.0.0d00) iter = iter + 1 call pred call force call corr if (iter.gt.100) then write(6,*) 'istep: ',istep write(6,*) 't_rmndr: ',t_rmndr endif if (iter.gt.1000) then call ga_error('Failed in mbstep',istep) endif end do #if 0 if (mod(istep,ilist).eq.0) then if (istep.gt.equil_1) then call cluster_com call cluster_center endif call fixper do i = 1, antot ra(i,1,6) = ra(i,1,1) ra(i,2,6) = ra(i,2,1) ra(i,3,6) = ra(i,3,1) end do endif #endif return end c subroutine vlstep #include "common.fh" c double precision scale,sclinc integer i,j integer iter iter = 0 c prsflg = .false. tmpflg = .false. ptflg = .false. if (itarg.ge.istep) then scale = xbox * ybox * zbox sclinc = (tvol - scale ) / float(itarg - istep + 1) scale = (scale + sclinc) / (xbox * ybox * zbox) scale = exp(log(scale)/3.0d00) xbox = scale * xbox ybox = scale * ybox zbox = scale * zbox xbox2 = scale * xbox2 ybox2 = scale * ybox2 zbox2 = scale * zbox2 do 300 j = 1, 3 do 100 i = 1, antot ra(i,j,1) = scale * ra(i,j,1) ra(i,j,6) = scale * ra(i,j,6) 100 continue 300 continue endif mbflg = .true. do while (t_rmndr.gt.0.0d00) iter = iter + 1 call pred call force call corr if (iter.gt.100) then write(6,*) 'istep: ',istep write(6,*) 't_rmndr: ',t_rmndr endif if (iter.gt.1000) then call ga_error('Failed in vlstep',istep) endif end do if (nrg(5).gt.0.0d00) then scale = sqrt(tmprtr / nrg(5)) else scale = 1.0d00 endif do 600 j = 1, 3 do 400 i = 1, antot ra(i,j,2) = scale * ra(i,j,2) ra(i,j,5) = scale * ra(i,j,5) 400 continue 600 continue c return end c subroutine pred #include "common.fh" c double precision scale,c1,c2 double precision rx,ry,rz,dt integer i,j double precision cluster_check_radius logical debug c c This subroutine implements the predictor for a 3 point Gear c predictor-corrector algorithm c if (istep.gt.3930438) then debug = .false. else debug = .false. endif call cluster_com call cluster_old_at call cluster_therm if (debug) then write(6,*) ga_nodeid(),' (pred) Got to 1 at step ',istep endif c rx = cluster_check_radius() c if (rx.gt.r_cluster.and.istep.gt.equil_3) then c write(6,*) ga_nodeid(),' Step ',istep c write(6,*) ga_nodeid(),' t_done ',t_done c write(6,*) ga_nodeid(),' Radius out of bounds ',rx c write(6,*) ga_nodeid(),' r_cluster ',r_cluster c endif if (t_done.eq.tau) then t_done = 0.0d00 dt = tau t_rmndr = tau else dt = t_rmndr endif c c1 = dt c2 = dt * c1 / 2.0d00 c c predictor step for atoms c do 200 j = 1, 3 do 100 i = 1, antot ra(i,j,6) = ra(i,j,6) + c1 * ra(i,j,2) + c2 * ra(i,j,3) ra(i,j,2) = ra(i,j,2) + c1 * ra(i,j,3) 100 continue 200 continue c c predictor step for volume c if ((prsflg.or.ptflg).and.ipmode.eq.0) then vol1 = vol1 + c1 * vol2 + c2 * vol3 vol2 = vol2 + c1 * vol3 scale = vol1 / (xbox * ybox * zbox) scale = exp(log(scale)/3.0d00) xbox = scale * xbox ybox = scale * ybox zbox = scale * zbox xbox2 = scale * xbox2 ybox2 = scale * ybox2 zbox2 = scale * zbox2 endif c if ((prsflg.or.ptflg).and.ipmode.eq.1) then do 500 j = 1, 3 alen1(j) = alen1(j) + c1 * alen2(j) + c2 * alen3(j) alen2(j) = alen2(j) + c1 * alen3(j) 500 continue xbox = alen1(1) ybox = alen1(2) zbox = alen1(3) xbox2 = xbox/2.0d00 ybox2 = ybox/2.0d00 zbox2 = zbox/2.0d00 endif c if ((prsflg.or.ptflg).and.ipmode.eq.2) then do 600 j = 1, 2 alen1(j) = alen1(j) + c1 * alen2(j) + c2 * alen3(j) alen2(j) = alen2(j) + c1 * alen3(j) 600 continue xbox = alen1(1) ybox = alen1(2) xbox2 = xbox/2.0d00 ybox2 = ybox/2.0d00 endif c c predictor step for time scale c if (tmpflg.or.ptflg) then scal1 = scal1 + c1 * scal2 + c2 * scal3 scal2 = scal2 + c1 * scal3 endif c if (debug) then write(6,*) ga_nodeid(),' (pred) Got to 2 at step ',istep endif call fixper if (istep.gt.equil_1) then call cluster_com endif if (debug) then write(6,*) ga_nodeid(),' (pred) Got to 3 at step ',istep endif if (istep.gt.equil_1) then call cluster_check_cllsn else t_rmndr = 0.0d00 t_done = tau endif if (debug) then write(6,*) ga_nodeid(),' (pred) Got to 4 at step ',istep endif if (.not.(ptflg.or.prsflg)) then if (t_rmndr.eq.0.0d00) call cluster_center endif if (debug) then write(6,*) ga_nodeid(),' (pred) Got to 5 at step ',istep endif if (mod(istep,ilist).eq.0.and.t_rmndr.eq.0.0d00) then call update endif if (cllsn_cnt.gt.100) then call ga_error("Too many collisions ",istep) endif if (debug) then write(6,*) ga_nodeid(),' (pred) Got to 6 at step ',istep endif c return end c subroutine corr #include "common.fh" c double precision c1,c2,coef1,xcorr,vcorr,scorr,dt double precision v1,s1,pmassi,tmassi,dlnvdt(3),itmp double precision mv1,cmr,kvt,kvtl(3),l1(3) double precision rbuf(8),virial,virc(3) double precision alfx,alfy,alfpx,alfpy,alfpz integer i,j,iat,nreal,nbyte c c This subroutine implements the corrector for a 3 point Gear c predictor-corrector algorithm c dt = t_done c1 = dt c2 = dt * c1 / 2.0d00 coef1 = c2 / c1 c if (prsflg.or.ptflg) then pmassi = 1.0d00 / pmass else pmassi = 0.0d00 vol1 = xbox * ybox * zbox vol2 = 0.0 vol3 = 0.0 c alen1(1) = xbox alen1(2) = ybox alen1(3) = zbox do 20 j = 1, 3 alen2(j) = 0.0d00 alen3(j) = 0.0d00 20 continue endif if (tmpflg.or.ptflg) then tmassi = 1.0d00 / tmass else tmassi = 0.0d00 scal1 = 1.0d00 scal2 = 0.0d00 scal3 = 0.0d00 endif c if (ipmode.eq.0) then dlnvdt(1) = vol2 / (3.0d00 * vol1) dlnvdt(2) = vol2 / (3.0d00 * vol1) dlnvdt(3) = vol2 / (3.0d00 * vol1) elseif (ipmode.eq.1) then dlnvdt(1) = alen2(1) / alen1(1) dlnvdt(2) = alen2(2) / alen1(2) dlnvdt(3) = alen2(3) / alen1(3) elseif (ipmode.eq.2) then dlnvdt(1) = alen2(1) / alen1(1) dlnvdt(2) = alen2(2) / alen1(2) dlnvdt(3) = 0.0d00 endif kvt = 0.0d00 kvtl(1) = 0.0d00 kvtl(2) = 0.0d00 kvtl(3) = 0.0d00 do 200 j = 1, 3 do 150 iat = 1, antot mv1 = mass(iat) * (ra(iat,j,2) - ra(iat,j,6) * dlnvdt(j)) kvt = kvt + mv1**2/mass(iat) kvtl(j) = kvtl(j) + mv1**2/mass(iat) 150 continue 200 continue c c do global sum on quantities needed for corrector, if necessary c if (prsflg.or.ptflg.or.tmpflg) then rbuf(1) = kvt rbuf(2) = kvtl(1) rbuf(3) = kvtl(2) rbuf(4) = kvtl(3) rbuf(5) = nrg(10) rbuf(6) = nrg(18) rbuf(7) = nrg(19) rbuf(8) = nrg(20) c call ga_dgop(3,rbuf,8,'+') c kvt = rbuf(1) kvtl(1) = rbuf(2) kvtl(2) = rbuf(3) kvtl(3) = rbuf(4) virial = rbuf(5) virc(1) = rbuf(6) virc(2) = rbuf(7) virc(3) = rbuf(8) endif c itmp = (kvt + virial)/(3.0d00 * vol1) if (prsflg.or.ptflg) then if (ipmode.eq.0) then v1 = scal1**2 * pmassi * (itmp - prssr) + vol2 * scal2 / scal1 else l1(1) = scal1**2*pmassi*((kvtl(1)+virc(1))/alen1(1) + - prssr*alen1(2)*alen1(3)) + alen2(1)*scal2/scal1 l1(2) = scal1**2*pmassi*((kvtl(2)+virc(2))/alen1(2) + - prssr*alen1(1)*alen1(3)) + alen2(2)*scal2/scal1 if (ipmode.eq.1) then l1(3) = scal1**2*pmassi*((kvtl(3)+virc(3))/alen1(3) + - prssr*alen1(1)*alen1(2)) + alen2(3)*scal2/scal1 else l1(3) = 0.0d00 endif alfx = l1(1) alfy = l1(2) endif else v1 = 0.0d00 endif c do 400 j = 1, 3 if (ipmode.eq.0) then do 300 i = 1, antot cmr = ra(i,j,6) ra(i,j,8) = ra(i,j,4) / mass(i) + - (dlnvdt(j) + scal2 / scal1) + * (ra(i,j,2) - dlnvdt(j) * cmr) + - (vol2**2 * cmr / (3.0d00*vol1**2) + - v1 * cmr / (3.0d00 * vol1) + - dlnvdt(j) * ra(i,j,2)) 300 continue elseif (ipmode.ge.1) then do 340 i = 1, antot cmr = ra(i,j,6) ra(i,j,8) = ra(i,j,4) / mass(i) + - scal2 * ra(i,j,2) / scal1 + + cmr * scal2 * dlnvdt(j) / scal1 + + cmr * l1(j) / alen1(j) 340 continue endif 400 continue c do 425 j = 1, 3 do 450 i = 1, antot xcorr = ra(i,j,8) - ra(i,j,3) ra(i,j,2) = ra(i,j,2) + coef1 * xcorr ra(i,j,3) = ra(i,j,8) ra(i,j,7) = ra(i,j,8) 450 continue 425 continue c if (tmpflg.or.ptflg) then s1 = scal1 * tmassi * (kvt - nrg(1) * tmprtr) + + scal2**2 / scal1 endif if ((prsflg.or.ptflg).and.ipmode.eq.0) then vcorr = v1 - vol3 vol2 = vol2 + coef1 * vcorr vol3 = v1 endif if ((prsflg.or.ptflg).and.ipmode.ge.1) then l1(1) = l1(1) + alfpx l1(2) = l1(2) + alfpy l1(3) = l1(3) + alfpz if (ipmode.eq.1) then do 475 j = 1, 3 vcorr = l1(j) - alen3(j) alen2(j) = alen2(j) + coef1 * vcorr alen3(j) = l1(j) 475 continue elseif (ipmode.eq.2) then do 485 j = 1, 2 vcorr = l1(j) - alen3(j) alen2(j) = alen2(j) + coef1 * vcorr alen3(j) = l1(j) 485 continue endif alfpx = l1(1) alfpy = l1(2) alfpz = l1(3) endif if (tmpflg.or.ptflg) then scorr = s1 - scal3 scal2 = scal2 + coef1 * scorr scal3 = s1 endif c c calculate value of the Hamiltonian c kvt = 0.0d00 if (ipmode.eq.0) then dlnvdt(1) = vol2 / (3.0d00 * vol1) dlnvdt(2) = vol2 / (3.0d00 * vol1) dlnvdt(3) = vol2 / (3.0d00 * vol1) elseif (ipmode.eq.1) then dlnvdt(1) = alen2(1) / alen1(1) dlnvdt(2) = alen2(2) / alen1(2) dlnvdt(3) = alen2(3) / alen1(3) vol1 = alen1(1)*alen1(2)*alen1(3) elseif (ipmode.eq.2) then dlnvdt(1) = alen2(1) / alen1(1) dlnvdt(2) = alen2(2) / alen1(2) dlnvdt(3) = 0.0d00 vol1 = alen1(1)*alen1(2)*zbox endif do 600 j = 1, 3 do 550 iat = 1, antot ra(iat,j,5) = mass(iat) * (ra(iat,j,2)-ra(iat,j,6)*dlnvdt(j)) kvt = kvt + ra(iat,j,5)**2 / mass(iat) 550 continue 600 continue c call com call kin call nrgsum call com_rmv c kvt = 2.0 * nrg(4) nrg(9) = 0.5d00 * (kvt + tmass * scal2**2 / scal1**2) + nrg(6) if (ipmode.eq.0) nrg(9) = nrg(9)+0.5d00*pmass*vol2**2/scal1**2 if (ipmode.eq.1) nrg(9) = nrg(9)+0.5d00*pmass*alen2(1)**2/scal1**2 + +0.5d00*pmass*alen2(2)**2/scal1**2 + +0.5d00*pmass*alen2(3)**2/scal1**2 if (ipmode.eq.2) nrg(9) = nrg(9)+0.5d00*pmass*alen2(1)**2/scal1**2 + +0.5d00*pmass*alen2(2)**2/scal1**2 itmp = (kvt + nrg(10))/(3.0d00 * vol1) if (prsflg.or.ptflg) then nrg(11) = 0.5d00 * kvt + nrg(6) + vol1 * prssr else nrg(11) = 0.5d00 * kvt + nrg(6) + vol1 * itmp endif if (prsflg.or.ptflg) nrg(9) = nrg(9) + prssr * vol1 if (tmpflg.or.ptflg) then nrg(9) = nrg(9) + nrg(1) * tmprtr * log(scal1) endif c nrg(5) = nrg(4) / nrg(2) tavg = tavg + nrg(5) itavg = itavg + 1 c c make sure volume and temperature scale factor is the same on c all processors c if ((mod(istep,100).eq.0).and.(prsflg.or.ptflg.or.tmpflg)) then if (ga_nodeid().eq.0) then rbuf(1) = scal1 rbuf(2) = scal2 rbuf(3) = xbox rbuf(4) = ybox rbuf(5) = zbox rbuf(6) = vol2 else rbuf(1) = 0.0d00 rbuf(2) = 0.0d00 rbuf(3) = 0.0d00 rbuf(4) = 0.0d00 rbuf(5) = 0.0d00 rbuf(6) = 0.0d00 endif c nreal = 6 call ga_dgop(1,rbuf,6,'+') c scal1 = rbuf(1) scal2 = rbuf(2) xbox = rbuf(3) ybox = rbuf(4) zbox = rbuf(5) vol2 = rbuf(6) endif if (l_cllsn) then call cluster_do_cllsn endif call cluster_com c return end ga-5.9.2/global/examples/md_cluster/pairs.F000066400000000000000000000161231500715745200205730ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine pairs #include "common.fh" c integer MAXCL,MSKIN parameter (MAXCL=50,MSKIN=5) double precision savd(MAXAT),savr(MAXAT),ft(MAXAT,3) double precision rsqi,r6i,dedr,dedrd,dedrr,phid,phir double precision xu,yu,zu,xl,yl,zl,rcut2 double precision tprssr(MAXAT,3),a12(MAXAT),a6(MAXAT) double precision rct2sd,rsqcrt,rsq,b12(MAXAT),b6(MAXAT) double precision r2(MAXAT),dx(MAXAT),dy(MAXAT),dz(MAXAT) double precision br2(MAXAT),bdx(MAXAT),bdy(MAXAT),bdz(MAXAT) double precision xcl,ycl,zcl,xmax,ymax,zmax,xmin,ymin,zmin double precision rcp(MAXAT),dcp(MAXAT),brcp(MAXAT),bdcp(MAXAT) double precision one,six,twelve double precision rix,riy,riz double precision tbeg,wraptime integer acml(MAXAT),bacml(MAXAT) integer i,j,l,fat,lat,jp,jtmp,jat,ltot,bltot c if (istep.eq.0.or.(mod(istep,ilist).eq.0.and. + t_rmndr.eq.0.0d00)) call cell_list tbeg = wraptime() c c this subroutine calculates the forces on individual particles c due to the Van der Waals interactions c c calculate some parameters c one = 1.0d00 six = 6.0d00 twelve = 12.0d00 c c Primary loop over all local atoms c do i = 1, antot c c evaluate forces for both local and buffer atoms c fat = nafirst(i) lat = nalast(i) c c initialize temporary storage arrays c l = 0 do j = fat, lat l = l + 1 savr(l) = 0.0d00 savd(l) = 0.0d00 ft(l,1) = 0.0d00 ft(l,2) = 0.0d00 ft(l,3) = 0.0d00 tprssr(l,1) = 0.0d00 tprssr(l,2) = 0.0d00 tprssr(l,3) = 0.0d00 end do c jp = 0 do jat = fat, lat j = nblist(jat) jp = jp + 1 c acml(jp) = j c c calculate interatomic distances c dx(jp) = ra(i,1,6) - ra(j,1,6) dx(jp) = dx(jp) - xbox * anint(dx(jp) / xbox) dy(jp) = ra(i,2,6) - ra(j,2,6) dy(jp) = dy(jp) - ybox * anint(dy(jp) / ybox) dz(jp) = ra(i,3,6) - ra(j,3,6) dz(jp) = dz(jp) - zbox * anint(dz(jp) / zbox) c r2(jp) = dx(jp)**2 + dy(jp)**2 + dz(jp)**2 end do ltot = jp c c check displacements against cutoff c jp = 0 do 1200 j = 1, ltot rcut2 = acut2(at(i),at(acml(j))) if (r2(j).ge.rcut2) go to 1200 jp = jp + 1 jtmp = acml(j) acml(jp) = jtmp c r2(jp) = r2(j) c a12(jp) = e12(at(jtmp),at(i)) a6(jp) = e6(at(jtmp),at(i)) c dcp(jp) = -dcorr(at(jtmp),at(i)) rcp(jp) = -rcorr(at(jtmp),at(i)) c dx(jp) = dx(j) dy(jp) = dy(j) dz(jp) = dz(j) 1200 continue ltot = jp c c evaluate interaction between all local pairs c c do j = 1, ltot rsq = r2(j) rsqi = one / rsq r6i = rsqi * rsqi * rsqi c phir = a12(j) * r6i * r6i phid = -a6(j) * r6i savr(j) = savr(j) + phir + rcp(j) savd(j) = savd(j) + phid + dcp(j) dedrr = twelve * phir dedrd = six * phid dedr = (dedrr + dedrd) * rsqi ft(j,1) = dedr * dx(j) ft(j,2) = dedr * dy(j) ft(j,3) = dedr * dz(j) tprssr(j,1) = dedr*dx(j)**2 tprssr(j,2) = dedr*dy(j)**2 tprssr(j,3) = dedr*dz(j)**2 end do c c sum forces on atom i and contributions to the energy and c pressure c do j = 1, ltot ra(i,1,4) = ra(i,1,4) + ft(j,1) ra(i,2,4) = ra(i,2,4) + ft(j,2) ra(i,3,4) = ra(i,3,4) + ft(j,3) esvr = esvr + savr(j) esvd = esvd + savd(j) presf = presf + tprssr(j,1) + tprssr(j,2) + tprssr(j,3) presfx = presfx + tprssr(j,1) presfy = presfy + tprssr(j,2) presfz = presfz + tprssr(j,3) end do c c scatter forces to atoms j c do jp = 1, ltot j = acml(jp) ra(j,1,4) = ra(j,1,4) - ft(jp,1) ra(j,2,4) = ra(j,2,4) - ft(jp,2) ra(j,3,4) = ra(j,3,4) - ft(jp,3) end do c c Calculate interactions between locally held atoms and remote c atoms. Start by re-initializing temporary storage arrays. c fat = nbfirst(i) lat = nblast(i) c l = 0 do j = fat, lat l = l + 1 savr(l) = 0.0d00 savd(l) = 0.0d00 ft(l,1) = 0.0d00 ft(l,2) = 0.0d00 ft(l,3) = 0.0d00 tprssr(l,1) = 0.0d00 tprssr(l,2) = 0.0d00 tprssr(l,3) = 0.0d00 end do c jp = 0 do jat = fat, lat j = nblist(jat) jp = jp + 1 c bacml(jp) = j c c calculate interatomic distances c bdx(jp) = ra(i,1,6) - xcrd(j) bdx(jp) = bdx(jp) - xbox * anint(bdx(jp) / xbox) bdy(jp) = ra(i,2,6) - ycrd(j) bdy(jp) = bdy(jp) - ybox * anint(bdy(jp) / ybox) bdz(jp) = ra(i,3,6) - zcrd(j) bdz(jp) = bdz(jp) - zbox * anint(bdz(jp) / zbox) c br2(jp) = bdx(jp)**2 + bdy(jp)**2 + bdz(jp)**2 end do bltot = jp c c check displacements against cutoff c jp = 0 do 1250 j = 1, bltot rcut2 = acut2(at(i),bat(bacml(j))) if (br2(j).ge.rcut2) go to 1250 jp = jp + 1 jtmp = bacml(j) bacml(jp) = jtmp c br2(jp) = br2(j) c b12(jp) = e12(bat(jtmp),at(i)) b6(jp) = e6(bat(jtmp),at(i)) c bdcp(jp) = -dcorr(bat(jtmp),at(i)) brcp(jp) = -rcorr(bat(jtmp),at(i)) c bdx(jp) = bdx(j) bdy(jp) = bdy(j) bdz(jp) = bdz(j) 1250 continue bltot = jp c c evaluate interaction between all local pairs c do j = 1, bltot rsq = br2(j) rsqi = one / rsq r6i = rsqi * rsqi * rsqi c phir = b12(j) * r6i * r6i phid = -b6(j) * r6i savr(j) = savr(j) + phir + brcp(j) savd(j) = savd(j) + phid + bdcp(j) dedrr = twelve * phir dedrd = six * phid dedr = (dedrr + dedrd) * rsqi ft(j,1) = dedr * bdx(j) ft(j,2) = dedr * bdy(j) ft(j,3) = dedr * bdz(j) tprssr(j,1) = dedr*bdx(j)**2 tprssr(j,2) = dedr*bdy(j)**2 tprssr(j,3) = dedr*bdz(j)**2 end do c c sum forces on atom i and contributions to the energy and c pressure c do j = 1, bltot ra(i,1,4) = ra(i,1,4) + ft(j,1) ra(i,2,4) = ra(i,2,4) + ft(j,2) ra(i,3,4) = ra(i,3,4) + ft(j,3) esvr = esvr + savr(j) esvd = esvd + savd(j) presf = presf + tprssr(j,1) + tprssr(j,2) + tprssr(j,3) presfx = presfx + tprssr(j,1) presfy = presfy + tprssr(j,2) presfz = presfz + tprssr(j,3) end do c c scatter forces to atoms j c do jp = 1, bltot j = bacml(jp) xfrc(j) = xfrc(j) - ft(jp,1) yfrc(j) = yfrc(j) - ft(jp,2) zfrc(j) = zfrc(j) - ft(jp,3) end do end do c tmstat(3) = tmstat(3) + wraptime() - tbeg c return end ga-5.9.2/global/examples/md_cluster/ran3.F000066400000000000000000000030521500715745200203150ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c c This is really ran3 c function ran1(idum) c c Random number generator from Numerical Recipes c c implicit real*4(m) c parameter (mbig=4000000.,mseed=1618033.,mz=0.,fac=2.5e-7) implicit real*8 (a-h,o-z) parameter (mbig=1000000000,mseed=161803398,mz=0,fac=1.e-9) dimension ma(55) data iff /0/ save iff, mj, ma, mk, inext, inextp if(idum.lt.0.or.iff.eq.0)then iff=1 mj=mseed-iabs(idum) mj=mod(mj,mbig) ma(55)=mj mk=1 do 11 i=1,54 ii=mod(21*i,55) ma(ii)=mk mk=mj-mk if(mk.lt.mz)mk=mk+mbig mj=ma(ii) 11 continue do 13 k=1,4 do 12 i=1,55 ma(i)=ma(i)-ma(1+mod(i+30,55)) if(ma(i).lt.mz)ma(i)=ma(i)+mbig 12 continue 13 continue inext=0 inextp=31 idum=1 endif inext=inext+1 if(inext.eq.56)inext=1 inextp=inextp+1 if(inextp.eq.56)inextp=1 mj=ma(inext)-ma(inextp) if(mj.lt.mz)mj=mj+mbig ma(inext)=mj ran1=mj*fac return end c function gasdev(idum) implicit real*8 (a-h,o-z) data iset/0/ save iset, gset if (iset.eq.0) then 1 v1=2.*ran1(idum)-1. v2=2.*ran1(idum)-1. r=v1**2+v2**2 if(r.ge.1.)go to 1 fac=sqrt(-2.*log(r)/r) gset=v1*fac gasdev=v2*fac iset=1 else gasdev=gset iset=0 endif return end ga-5.9.2/global/examples/md_cluster/rdpar.F000066400000000000000000000365141500715745200205730ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine rdpar #include "common.fh" double precision x,ran1 integer i,j integer me c c This subroutine reads in the molecular simulation parameters c me = ga_nodeid() if (me.eq.0) open (unit=5,file='md_lj.in',status='old') c c Line 1 c c nstep: total number of steps in simulation c c tau: time step increment c c nsc: number of special control steps c c iseed: random number seed (this should be a NEGATIVE c integer) c nstep = 0 tau = 0.0d00 nsc = 0 iseed = 0 if (me.eq.0) then read(5,*) nstep,tau,nsc,iseed endif call ga_igop(2,nstep,1,'+') call ga_dgop(3,tau,1,'+') call ga_igop(4,nsc,1,'+') call ga_igop(5,iseed,1,'+') c c Line 2 c c rcut: cutoff for use in neighbor list calculation c c ilist: number of steps taken before updating c neighbor list c c icut: cutoff parameter for potential c c (0) only potential set to zero at cutoff c c (1) potential and forces set to zero at cutoff c rcut = 0.0d00 ilist = 0 icut = 0 if (me.eq.0) then read(5,*) rcut,ilist,icut endif call ga_dgop(6,rcut,1,'+') call ga_igop(7,ilist,1,'+') call ga_igop(8,icut,1,'+') c c Line 3 c c dflalg: default algorithm c c 1: constant energy 3 point Gear algorithm c 2: constant pressure algorithm c 3: constant temperature algorithm (velocity scaling) c 4: constant temperature algorithm (Nose' dynmamics) c 5: constant pressure and temperature algorithm c 6: Boltzmann velocity step c 7: adjust volume to specified target volume c (available only as a special step) c 8: adjust kinetic energy based on average temperature c since last call to this step so that temperature c matches target temperature. c (available only as a special step) c 9: constant pressure and temperature algorithm with c independent box adjustments c 10: constant pressure and temperature algorithm with c independent box adjustments in only the x and y c dimensions c c dftmp: default temperature c c dfprs: default pressure c c dftm: default mass for temperature c c dfpm: default mass for pressure c dflalg = 0 dftmp = 0.0d00 dfprs = 0.0d00 dftm = 0.0d00 dfpm = 0.0d00 if (me.eq.0) then read(5,*) dflalg,dftmp,dfprs,dftm,dfpm endif call ga_igop(9,dflalg,1,'+') call ga_dgop(1,dftmp,1,'+') call ga_dgop(2,dfprs,1,'+') call ga_dgop(3,dftm,1,'+') call ga_dgop(4,dfpm,1,'+') c c Line 4.1 - 4.nsc c c special step control instructions c c isc(i,1): begining step for special instruction c c isc(i,2): final step for special instruction c c isc(i,3): increment for special instruction c c isc(i,4): stepping algorithm for special instruction c (see documentation on dflalg) c c rsc(i,1): temperature (in K) if temperature scaling or c Boltzmann algorithm is specified c c rsc(i,2): pressure if constant pressure c algorithms are specified. If volume adjustment c algorithm is specified then this is the target c volume in A**3. c c rsc(i,3): mass (in g A**2 /mole) for Nose temperature c algorithm c c rsc(i,4): mass (in g/(mole A**4)) for constant pressure c algorithm c do 100 i=1,nsc do j=1,4 isc(i,j) = 0.0d00 rsc(i,j) = 0.0d00 end do if (me.eq.0) then read(5,*) (isc(i,j),j=1,4),(rsc(i,j),j=1,4) endif do j=1,4 call ga_igop(4,isc(i,j),1,'+') call ga_dgop(5,rsc(i,j),1,'+') end do 100 continue c c Line 5 c c istart: startup format c 1: start from single configuration c 2: start from configuration plus velocity c c istop: write-out format c 1: single configuration c 2: configuration plus velocity c istart = 0 istop = 0 if (me.eq.0) then read(5,*) istart,istop endif call ga_igop(6,istart,1,'+') call ga_igop(7,istop,1,'+') c c Line 6 c c istat: frequency to print out simulation information c istat = 0 if (me.eq.0) then read(5,*) istat endif call ga_igop(8,istat,1,'+') c c Line 7 c c equil_1: number of steps in first equilibration regime c c equil_2: number of steps in second equilibration regime c c equil_3: number of steps in third equilibration regime c equil_1 = 0 equil_2 = 0 equil_3 = 0 if (me.eq.0) then read(5,*) equil_1, equil_2, equil_3 endif call ga_igop(9,equil_1,1,'+') call ga_igop(1,equil_2,1,'+') call ga_igop(2,equil_3,1,'+') window_1 = int(0.2d00*dble(equil_3-equil_2)) window_1 = equil_2 + window_1 window_2 = int(0.4d00*dble(equil_3-equil_2)) window_2 = window_1 + window_2 c c Line 7 c c cl_prssr: pressure that is applied to cluster c c mc_tmprtr: temperature used in Monte Carlo steps c cl_prssr = 0.0d00 mc_tmprtr = 0.0d00 if (me.eq.0) then read(5,*) cl_prssr, mc_tmprtr endif call ga_dgop(3,cl_prssr,1,'+') call ga_dgop(4,mc_tmprtr,1,'+') c c Line 8 c c mcfreq: frequency to apply Monte Carlo step on radius c c mcbins: number of bins to accumulate radius in c mcfreq = 0 mcbins = 0 if (me.eq.0) then read(5,*) mcfreq, mcbins endif call ga_igop(7,mcfreq,1,'+') call ga_igop(8,mcbins,1,'+') c if (me.eq.0) close(5) c c Write out simulation information to output file c if (me.eq.0.and.l_stdio) then write(6,1300) nstep write(6,1400) 1000.0 * tau write(6,4325) rcut write(6,4350) ilist if (icut.eq.0) then write(6,4400) else write(6,4420) endif write(6,1500) dflalg write(6,1600) write(6,1700) dftmp write(6,1800) dfprs write(6,1900) dftm write(6,2000) dfpm write(6,2100) do 300 i = 1, nsc if (i.eq.1) then write(6,2200) else if (isc(i-1,4).eq.7) write(6,2200) endif if (isc(i,4).ne.7) then write(6,2300) (isc(i,j),j=1,4),(rsc(i,j),j=1,4) else write(6,2400) write(6,2500) (isc(i,j),j=1,4) write(6,2600) rsc(i,1) write(6,2700) rsc(i,2) endif 300 continue write(6,3400) istat write(6,3800) mcfreq write(6,3500) equil_1 write(6,3600) equil_2 write(6,3700) equil_3 if (istart.eq.1) then write(6,2800) else write(6,2900) endif if (istop.eq.1) then write(6,3000) else write(6,3100) endif write(6,3200) iseed write(6,4500) ga_pgroup_nodeid(ga_pgroup_get_world()) endif iseed = iseed - 10*me x = ran1(iseed) r_cluster = 0.0d00 mc_cnt = 0 c return 1300 format('Total number of steps in simulation :',i9) 1400 format('Time step interval :' + ,f16.6) 1500 format('Default algorithm :',i9) 1600 format(' Default algorithm parameters ') 1700 format(' Default temperature :' + ,f16.6) 1800 format(' Default pressure :' + ,f16.6) 1900 format(' Default temperature mass :' + ,f16.6) 2000 format(' Default pressure mass :' + ,f16.6) 2100 format('Special step instructions:') 2200 format(' begin end incrmt algrthm T P ', +'T mass P mass ') 2300 format(4i8,2f8.3,1pe9.2,1pe9.2) 2400 format(' begin end incrmt algrthm') 2500 format(4i8) 2600 format(' Temperature for volume adjustment :' + ,f16.6) 2700 format(' Target volume for volume adjustment :' + ,f16.6) 2800 format('Initial configuration is coordinates only') 2900 format('Initial configuration is coordinates and velocities') 3000 format('Final configuration is coordinates only') 3100 format('Final configuration is coordinates and velocities') 3200 format('Random number seed :',i9) 3400 format('Frequency to print out simulation information :',i9) 3500 format('Number of steps in first equilibration regime :',i9) 3600 format('Number of steps in second equilibration regime :',i9) 3700 format('Number of steps in third equilibration regime :',i9) 3800 format('Frequency to take Monte Carlo step :',i9) 4325 format('Cutoff distance :', + f16.6) 4350 format('Neighbor list update frequency :',i9) 4400 format('Potential set to zero at cutoff distance') 4420 format('Potential and forces set to zero at cutoff distance') 4500 format('Writing from node :',i9) end c subroutine rdcfg #include "common.fh" c double precision scl,gasdev integer inode,icnt,i,j,k,pnum,ilast,ifirst,me character*32 filename c c This subroutine reads in the initial coordinates of all atoms in c the system. First determine which coordinates each processor c should read. c me = ga_nodeid() pnum = ga_nnodes() c c clean up everything c atot = 0 ctot = 0 antot = 0 do k = 1, 8 do j = 1, 3 do i = 1, MAXAT ra(i,j,k) = 0.0d00 end do end do end do do i = 1, MAXAT mass(i) = 0.0d00 at(i) = 0 aidx(i) = 0 c xcrd(i) = 0.0d00 ycrd(i) = 0.0d00 zcrd(i) = 0.0d00 xfrc(i) = 0.0d00 yfrc(i) = 0.0d00 zfrc(i) = 0.0d00 xacc(i) = 0.0d00 yacc(i) = 0.0d00 zacc(i) = 0.0d00 mbuf(i) = 0.0d00 bat(i) = 0 bidx(i) = 0 end do btot = 0 c if (task_id.lt.10) then write(filename,100) task_id else if (task_id.ge.10.and.task_id.lt.100) then write(filename,101) task_id else if (task_id.ge.100.and.task_id.lt.1000) then write(filename,102) task_id else if (task_id.ge.1000.and.task_id.lt.10000) then write(filename,103) task_id endif 100 format('md.cfg',i1) 101 format('md.cfg',i2) 102 format('md.cfg',i3) 103 format('md.cfg',i4) atot = 0 if (me.eq.0) then open (unit=2,file=filename,status='old') read(2,*) atot close(2) endif call ga_igop(5,atot,1,'+') ilast = nint(dble((me+1)*atot)/dble(pnum)) ifirst = nint(dble(me*atot)/dble(pnum)) ifirst = ifirst + 1 c c read in portions of MD configuration on each node c do inode = 0, pnum - 1 call ga_sync() if (me.eq.inode) then open (unit=2,file=filename,status='old') c read(2,*) atot read(2,*) xbox,ybox,zbox xbox2 = xbox/2.0d00 ybox2 = ybox/2.0d00 zbox2 = zbox/2.0d00 c cl_lower = 0.0d00 if (xbox.lt.ybox.and.xbox.lt.zbox) then cl_upper = xbox2 else if (ybox.lt.xbox.and.ybox.lt.zbox) then cl_upper = ybox2 else cl_upper = zbox2 endif mc_step = (cl_upper-cl_lower)/dble(mcbins) c icnt = 0 if (istart.eq.1.or.(.not.l_oldcfg)) then do i = 1, ilast if (i.lt.ifirst) then read(2,*) else icnt = icnt + 1 read(2,*) at(icnt),(ra(icnt,j,1),j=1,3) if (at(icnt).eq.2) ctot = ctot + 1 c c generate atomic velocities from a Maxwell-Boltzmann distribution c do j = 1, 3 scl = sqrt(dftmp / amass(at(icnt))) ra(icnt,j,2) = scl * gasdev(0) end do aidx(icnt) = i endif end do else do i = 1, ilast if (i.lt.ifirst) then read(2,*) else icnt = icnt + 1 read(2,*) at(icnt),(ra(icnt,j,1),j=1,3), + (ra(icnt,j,2),j=1,3) if (at(icnt).eq.2) ctot = ctot + 1 aidx(icnt) = i endif end do endif close(2) endif end do antot = icnt call ga_igop(9,ctot,1,'+') c if (me.eq.0.and.l_stdio) then write(6,1100) atot write(6,1000) xbox,ybox,zbox endif c c initialize absolute coordinates c do j = 1, 3 do i = 1, antot ra(i,j,6) = ra(i,j,1) end do end do c call fixper c return 1000 format('The initial box size parameters are '/ + ' x dimension = ',f9.4,' A'/ + ' y dimension = ',f9.4,' A'/ + ' z dimension = ',f9.4,' A') 1100 format('The total number of atoms in simulation :',i9) end c subroutine atomin #include "common.fh" c double precision epsln,sigma integer i,j,ipairs,ip,ktrnc integer me logical ichk(50,50) c c This subroutine reads in atomic parameters from a parameter c file. These parameters will be used to construct the c system potential function. c me = ga_nodeid() do i = 1, 50 do j = 1, 50 ichk(i,j) = .false. end do end do c if (me.eq.0) then open(unit=2,file='atom.inp',status='old') endif c if (me.eq.0) then read (2,*) amass(1) read (2,*) amass(2) read (2,*) amass(3) read (2,*) amass(4) atnum = 4 else amass(1) = 0.0d00 amass(2) = 0.0d00 amass(3) = 0.0d00 amass(4) = 0.0d00 atnum = 0 endif call ga_dgop(1,amass(1),4,'+') call ga_igop(2,atnum,1,'+') c c read in atomic parameters c if (me.eq.0) then read(2,*) ipairs else ipairs = 0 endif call ga_igop(3,ipairs,1,'+') c do 100 ip = 1, ipairs i = 0 j = 0 ktrnc = 0 epsln = 0.0d00 sigma = 0.0d00 if (me.eq.0) then read (2,*) i,j,ktrnc,epsln,sigma endif call ga_igop(1,i,1,'+') call ga_igop(2,j,1,'+') call ga_igop(3,ktrnc,1,'+') call ga_dgop(4,epsln,1,'+') call ga_dgop(5,sigma,1,'+') ichk(i,j) = .true. if (i.ne.j) ichk(j,i) = .true. e12(i,j) = 4.0d00*epsln*sigma**12 e6(i,j) = 4.0d00*epsln*sigma**6 if (i.ne.j) e12(j,i) = e12(i,j) if (i.ne.j) e6(j,i) = e6(i,j) if (ktrnc.eq.1) then acut(i,j) = exp(log(2.0d00)/6.0d00)*sigma acut(j,i) = exp(log(2.0d00)/6.0d00)*sigma else acut(i,j) = rcut * sigma acut(j,i) = rcut * sigma endif acut2(i,j) = acut(i,j)**2 acut2(j,i) = acut(j,i)**2 100 continue c if (me.eq.0) close(2) return end ga-5.9.2/global/examples/md_cluster/scatter.F000066400000000000000000000532551500715745200211310ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine scatter #include "common.fh" c double precision xmax,xmin,ymax,ymin,zmax,zmin double precision xcell,ycell,zcell double precision tbeg,wraptime integer snode,rnode,pnum,inode,idx,idy,idz,ipx,ipy,ipz integer stot,i,inx,iny,inz logical nolist c c This subroutine scatters the forces of all particles on c neighboring processors back to their home processors after the c forces have been calculated. c tbeg = wraptime() pnum = ga_nnodes() call factor(pnum,idx,idy,idz) inode = ga_nodeid() call i_proc_to_xyz(inode,ipx,ipy,ipz,idx,idy,idz) c if (istep.eq.0.or.(mod(istep,ilist).eq.0.and. + t_rmndr.eq.0.0d00)) then nolist = .true. else nolist = .false. endif c c determine local boundaries c xmax = xbox*dble(ipx+1)/dble(idx) ymax = ybox*dble(ipy+1)/dble(idy) zmax = zbox*dble(ipz+1)/dble(idz) xmin = xbox*dble(ipx)/dble(idx) ymin = ybox*dble(ipy)/dble(idy) zmin = zbox*dble(ipz)/dble(idz) xmax = xmax - xbox2 ymax = ymax - ybox2 zmax = zmax - zbox2 xmin = xmin - xbox2 ymin = ymin - ybox2 zmin = zmin - zbox2 xcell = xbox/dble(idx) ycell = ybox/dble(idy) zcell = zbox/dble(idz) c c Send particles along x-axis. c c if (idx.gt.1) then if (idx.eq.2) then stot = 0 savtot = 0 if (nolist) then do i = 1, btot if (xcrd(i).lt.xmin.or.xcrd(i).ge.xmax) then xsbu(i) = .true. stot = stot + 1 xcrd(btot+stot) = xcrd(i) ycrd(btot+stot) = ycrd(i) zcrd(btot+stot) = zcrd(i) xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else xsbu(i) = .false. savtot = savtot + 1 xcrd(savtot) = xcrd(i) ycrd(savtot) = ycrd(i) zcrd(savtot) = zcrd(i) xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do else do i = 1, btot if (xsbu(i)) then stot = stot + 1 xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else savtot = savtot + 1 xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do endif else stot = 0 savtot = 0 if (nolist) then do i = 1, btot if ((xcrd(i).ge.xmax.and.xcrd(i)-xmax.lt.xcell).or. + (xcrd(i).lt.xmin.and.xmin-xcrd(i).gt.xcell)) then xsbu(i) = .true. stot = stot + 1 xcrd(btot+stot) = xcrd(i) ycrd(btot+stot) = ycrd(i) zcrd(btot+stot) = zcrd(i) xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else xsbu(i) = .false. savtot = savtot + 1 xcrd(savtot) = xcrd(i) ycrd(savtot) = ycrd(i) zcrd(savtot) = zcrd(i) xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do else do i = 1, btot if (xsbu(i)) then stot = stot + 1 xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else savtot = savtot + 1 xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do endif endif c inx = ipx - 1 if (inx.eq.-1) inx = idx - 1 call i_xyz_to_proc(rnode,inx,ipy,ipz,idx,idy,idz) call scatter_buf(rnode,stot) c call heapsort(1) call fcull(1) endif c if (idx.gt.2) then c stot = 0 savtot = 0 if (nolist) then do i = 1, btot if ((xcrd(i).lt.xmin.and.xmin-xcrd(i).lt.xcell).or. + (xcrd(i).gt.xmax.and.xcrd(i)-xmax.gt.xcell)) then xsbd(i) = .true. stot = stot + 1 xcrd(btot+stot) = xcrd(i) ycrd(btot+stot) = ycrd(i) zcrd(btot+stot) = zcrd(i) xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else xsbd(i) = .false. savtot = savtot + 1 xcrd(savtot) = xcrd(i) ycrd(savtot) = ycrd(i) zcrd(savtot) = zcrd(i) xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do else do i = 1, btot if (xsbd(i)) then stot = stot + 1 xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else savtot = savtot + 1 xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do endif c inx = ipx + 1 if (inx.eq.idx) inx = 0 call i_xyz_to_proc(rnode,inx,ipy,ipz,idx,idy,idz) call scatter_buf(rnode,stot) c call heapsort(1) call fcull(2) c endif c c Send particles along y-axis. c if (idy.gt.1) then c if (idy.eq.2) then stot = 0 savtot = 0 if (nolist) then do i = 1, btot if (ycrd(i).lt.ymin.or.ycrd(i).ge.ymax) then ysbu(i) = .true. stot = stot + 1 xcrd(btot+stot) = xcrd(i) ycrd(btot+stot) = ycrd(i) zcrd(btot+stot) = zcrd(i) xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else ysbu(i) = .false. savtot = savtot + 1 xcrd(savtot) = xcrd(i) ycrd(savtot) = ycrd(i) zcrd(savtot) = zcrd(i) xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do else do i = 1, btot if (ysbu(i)) then stot = stot + 1 xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else savtot = savtot + 1 xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do endif else stot = 0 savtot = 0 if (nolist) then do i = 1, btot if ((ycrd(i).ge.ymax.and.ycrd(i)-ymax.lt.ycell).or. + (ycrd(i).lt.ymin.and.ymin-ycrd(i).gt.ycell)) then ysbu(i) = .true. stot = stot + 1 xcrd(btot+stot) = xcrd(i) ycrd(btot+stot) = ycrd(i) zcrd(btot+stot) = zcrd(i) xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else ysbu(i) = .false. savtot = savtot + 1 xcrd(savtot) = xcrd(i) ycrd(savtot) = ycrd(i) zcrd(savtot) = zcrd(i) xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do else do i = 1, btot if (ysbu(i)) then stot = stot + 1 xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else savtot = savtot + 1 xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do endif endif c iny = ipy - 1 if (iny.eq.-1) iny = idy - 1 call i_xyz_to_proc(rnode,ipx,iny,ipz,idx,idy,idz) call scatter_buf(rnode,stot) c call heapsort(1) call fcull(3) c endif c if (idy.gt.2) then c stot = 0 savtot = 0 if (nolist) then do i = 1, btot if ((ycrd(i).lt.ymin.and.ymin-ycrd(i).lt.ycell).or. + (ycrd(i).gt.ymax.and.ycrd(i)-ymax.gt.ycell)) then ysbd(i) = .true. stot = stot + 1 xcrd(btot+stot) = xcrd(i) ycrd(btot+stot) = ycrd(i) zcrd(btot+stot) = zcrd(i) xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else ysbd(i) = .false. savtot = savtot + 1 xcrd(savtot) = xcrd(i) ycrd(savtot) = ycrd(i) zcrd(savtot) = zcrd(i) xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do else do i = 1, btot if (ysbd(i)) then stot = stot + 1 xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else savtot = savtot + 1 xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do endif c iny = ipy + 1 if (iny.eq.idy) iny = 0 call i_xyz_to_proc(rnode,ipx,iny,ipz,idx,idy,idz) call scatter_buf(rnode,stot) c call heapsort(1) call fcull(4) c endif c c send particles along z-axis c if (idz.gt.1) then c if (idz.eq.2) then stot = 0 savtot = 0 if (nolist) then do i = 1, btot if (zcrd(i).lt.zmin.or.zcrd(i).ge.zmax) then zsbu(i) = .true. stot = stot + 1 xcrd(btot+stot) = xcrd(i) ycrd(btot+stot) = ycrd(i) zcrd(btot+stot) = zcrd(i) xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else zsbu(i) = .false. savtot = savtot + 1 xcrd(savtot) = xcrd(i) ycrd(savtot) = ycrd(i) zcrd(savtot) = zcrd(i) xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do else do i = 1, btot if (zsbu(i)) then stot = stot + 1 xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else savtot = savtot + 1 xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do endif else stot = 0 savtot = 0 if (nolist) then do i = 1, btot if ((zcrd(i).ge.zmax.and.zcrd(i)-zmax.lt.zcell).or. + (zcrd(i).lt.zmin.and.zmin-zcrd(i).gt.zcell)) then zsbu(i) = .true. stot = stot + 1 xcrd(btot+stot) = xcrd(i) ycrd(btot+stot) = ycrd(i) zcrd(btot+stot) = zcrd(i) xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else zsbu(i) = .false. savtot = savtot + 1 xcrd(savtot) = xcrd(i) ycrd(savtot) = ycrd(i) zcrd(savtot) = zcrd(i) xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do else do i = 1, btot if (zsbu(i)) then stot = stot + 1 xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else savtot = savtot + 1 xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do endif endif c inz = ipz - 1 if (inz.eq.-1) inz = idz - 1 call i_xyz_to_proc(rnode,ipx,ipy,inz,idx,idy,idz) call scatter_buf(rnode,stot) c call heapsort(1) call fcull(5) c endif c if (idz.gt.2) then c stot = 0 savtot = 0 if (nolist) then do i = 1, btot if ((zcrd(i).lt.zmin.and.zmin-zcrd(i).lt.zcell).or. + (zcrd(i).gt.zmax.and.zcrd(i)-zmax.gt.zcell)) then zsbd(i) = .true. stot = stot + 1 xcrd(btot+stot) = xcrd(i) ycrd(btot+stot) = ycrd(i) zcrd(btot+stot) = zcrd(i) xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else zsbd(i) = .false. savtot = savtot + 1 xcrd(savtot) = xcrd(i) ycrd(savtot) = ycrd(i) zcrd(savtot) = zcrd(i) xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do else do i = 1, btot if (zsbd(i)) then stot = stot + 1 xfrc(btot+stot) = xfrc(i) yfrc(btot+stot) = yfrc(i) zfrc(btot+stot) = zfrc(i) bidx(btot+stot) = bidx(i) else savtot = savtot + 1 xfrc(savtot) = xfrc(i) yfrc(savtot) = yfrc(i) zfrc(savtot) = zfrc(i) bidx(savtot) = bidx(i) endif end do endif c inz = ipz + 1 if (inz.eq.idz) inz = 0 call i_xyz_to_proc(rnode,ipx,ipy,inz,idx,idy,idz) call scatter_buf(rnode,stot) c call heapsort(1) call fcull(6) c endif c tmstat(7) = tmstat(7) + wraptime() - tbeg c return end c subroutine scatter_buf(rnode,stot) #include "common.fh" c double precision cbuf(3,MAXAT),fbuf(3,MAXAT) integer ibuf(MAXAT) double precision tbeg, tbeg1, wraptime integer i,rnode integer me,one,ld,is,rtot,stot logical crdflag c c Get the forces from processor rnode. The amount of data c on the current processor that actually needs to be c accessed represents stot particles c me = ga_nodeid() one = 1 ld = 3 c c exchange the size of lists c tbeg = wraptime() c if (istep.eq.0.or.(mod(istep,ilist).eq.0.and. + t_rmndr.eq.0.0d00)) then crdflag = .true. else crdflag = .false. endif c if (crdflag) then do i = btot + 1, btot + stot is = i - btot cbuf(1,is) = xcrd(i) cbuf(2,is) = ycrd(i) cbuf(3,is) = zcrd(i) end do endif do i = btot + 1, btot + stot is = i - btot fbuf(1,is) = xfrc(i) fbuf(2,is) = yfrc(i) fbuf(3,is) = zfrc(i) ibuf(is) = bidx(i) end do c gsize_hi = gsize_lo(me) call nga_put(g_size,gsize_lo(me),gsize_hi,stot,one) if (crdflag) then gcoords_hi(1) = 3 gcoords_hi(2) = gcoords_lo(2,me) + stot - 1 if (stot.gt.0) call nga_put(g_coords,gcoords_lo(1,me), + gcoords_hi,cbuf,ld) endif gfrc_hi(1) = 3 gfrc_hi(2) = gfrc_lo(2,me) + stot - 1 if (stot.gt.0) call nga_put(g_frc,gfrc_lo(1,me),gfrc_hi,fbuf,ld) gindex_hi = gindex_lo(me) + stot - 1 if (stot.gt.0) call nga_put(g_index,gindex_lo(me), + gindex_hi,ibuf,ld) tbeg1 = wraptime() call ga_sync() tmstat(16) = tmstat(16) + wraptime() - tbeg1 btot = savtot gsize_hi = gsize_lo(rnode) call nga_get(g_size,gsize_lo(rnode),gsize_hi,rtot,one) if (crdflag) then gcoords_hi(2) = gcoords_lo(2,rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_coords,gcoords_lo(1,rnode), + gcoords_hi,cbuf,ld) endif gfrc_hi(2) = gfrc_lo(2,rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_frc,gfrc_lo(1,rnode), + gfrc_hi,fbuf,ld) gindex_hi = gindex_lo(rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_index,gindex_lo(rnode), + gindex_hi,ibuf,ld) c if (crdflag) then do i = btot+1, btot+rtot is = i - btot xcrd(i) = cbuf(1,is) ycrd(i) = cbuf(2,is) zcrd(i) = cbuf(3,is) end do endif do i = btot+1, btot+rtot is = i - btot xfrc(i) = fbuf(1,is) yfrc(i) = fbuf(2,is) zfrc(i) = fbuf(3,is) bidx(i) = ibuf(is) end do c call ga_sync() btot = btot+rtot if (btot.gt.MAXAT) then call ga_error("Array overflow in scatter_buf",btot) endif c tmstat(12) = tmstat(12) + wraptime() - tbeg c return end c subroutine fcull(iflg) #include "common.fh" c double precision xmax,xmin,ymax,ymin,zmax,zmin integer pnum,inode,idx,idy,idz,ipx,ipy,ipz integer i,is,icnt,iflg,get_hash_a logical nolist c c This subroutine culls the forces of particles in the buffers c back on to the particles on the home processor c pnum = ga_nnodes() call factor(pnum,idx,idy,idz) inode = ga_nodeid() call i_proc_to_xyz(inode,ipx,ipy,ipz,idx,idy,idz) c if (istep.eq.0.or.(mod(istep,ilist).eq.0.and. + t_rmndr.eq.0.0d00)) then nolist = .true. else nolist = .false. endif c c determine local boundaries c xmax = xbox*dble(ipx+1)/dble(idx) ymax = ybox*dble(ipy+1)/dble(idy) zmax = zbox*dble(ipz+1)/dble(idz) xmin = xbox*dble(ipx)/dble(idx) ymin = ybox*dble(ipy)/dble(idy) zmin = zbox*dble(ipz)/dble(idz) xmax = xmax - xbox2 ymax = ymax - ybox2 zmax = zmax - zbox2 xmin = xmin - xbox2 ymin = ymin - ybox2 zmin = zmin - zbox2 c icnt = 0 is = 1 if (nolist) then do i = 1, btot if ((xcrd(i).lt.xmax.and.xcrd(i).ge.xmin).and. + (ycrd(i).lt.ymax.and.ycrd(i).ge.ymin).and. + (zcrd(i).lt.zmax.and.zcrd(i).ge.zmin)) then clist(iflg,i) = .true. is = get_hash_a(bidx(i)) if (is.gt.0) then ra(is,1,4) = ra(is,1,4) + xfrc(i) ra(is,2,4) = ra(is,2,4) + yfrc(i) ra(is,3,4) = ra(is,3,4) + zfrc(i) else call ga_error("Unknown atom index: ",i) endif else clist(iflg,i) = .false. icnt = icnt + 1 xcrd(icnt) = xcrd(i) ycrd(icnt) = ycrd(i) zcrd(icnt) = zcrd(i) xfrc(icnt) = xfrc(i) yfrc(icnt) = yfrc(i) zfrc(icnt) = zfrc(i) bidx(icnt) = bidx(i) endif end do else do i = 1, btot if (clist(iflg,i)) then is = get_hash_a(bidx(i)) if (is.gt.0) then ra(is,1,4) = ra(is,1,4) + xfrc(i) ra(is,2,4) = ra(is,2,4) + yfrc(i) ra(is,3,4) = ra(is,3,4) + zfrc(i) else call ga_error("Unknown atom index: ",i) endif else icnt = icnt + 1 xfrc(icnt) = xfrc(i) yfrc(icnt) = yfrc(i) zfrc(icnt) = zfrc(i) bidx(icnt) = bidx(i) endif end do endif btot = icnt if (btot.gt.MAXAT) then call ga_error("Array overflow in fcull",btot) endif c return end ga-5.9.2/global/examples/md_cluster/shuffle.F000066400000000000000000000217041500715745200211120ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine shuffle #include "common.fh" c integer snode,rnode,i,inx,iny,inz,pnum,idx,idy,idz,ipx,ipy,ipz integer icnt,inode,me c c This subroutine shuffles the coordinates on each of the processors c around so that each processor has the coordinates for atoms residing c in the physical domain corresponding to that processor. This routine c assumes that atoms are completely randomized. c pnum = ga_nnodes() me = ga_nodeid() call factor(pnum,idx,idy,idz) call i_proc_to_xyz(me,ipx,ipy,ipz,idx,idy,idz) c c copy all particles into the buffer c icnt = 0 do i = 1, antot icnt = icnt + 1 ra(i,1,6) = ra(i,1,1) ra(i,2,6) = ra(i,2,1) ra(i,3,6) = ra(i,3,1) xcrd(icnt) = ra(i,1,6) ycrd(icnt) = ra(i,2,6) zcrd(icnt) = ra(i,3,6) xfrc(icnt) = ra(i,1,2) yfrc(icnt) = ra(i,2,2) zfrc(icnt) = ra(i,3,2) mbuf(icnt) = mass(i) bidx(icnt) = aidx(i) bat(icnt) = at(i) end do btot = antot antot = 0 c c send buffers to processes controlling neighboring domains, c and select out particles that are within that domain c c send along x-axis and gather particles whose x value lies c between xmin and xmax c call cull(1) do i = 1, idx-1 inx = ipx - 1 if (inx.eq.-1) inx = idx - 1 call i_xyz_to_proc(rnode,inx,ipy,ipz,idx,idy,idz) call exchange_buf(rnode) call cull(1) end do c c copy all particles into the buffer c icnt = 0 do i = 1, antot icnt = icnt + 1 xcrd(icnt) = ra(i,1,6) ycrd(icnt) = ra(i,2,6) zcrd(icnt) = ra(i,3,6) xfrc(icnt) = ra(i,1,2) yfrc(icnt) = ra(i,2,2) zfrc(icnt) = ra(i,3,2) mbuf(icnt) = mass(i) bidx(icnt) = aidx(i) bat(icnt) = at(i) end do btot = antot antot = 0 c c send along y-axis and gather particles whose y value lies c between ymin and ymax c call cull(2) do i = 1, idy-1 iny = ipy - 1 if (iny.eq.-1) iny = idy - 1 call i_xyz_to_proc(rnode,ipx,iny,ipz,idx,idy,idz) call exchange_buf(rnode) call cull(2) end do c c copy all particles into the buffer c icnt = 0 do i = 1, antot icnt = icnt + 1 xcrd(icnt) = ra(i,1,6) ycrd(icnt) = ra(i,2,6) zcrd(icnt) = ra(i,3,6) xfrc(icnt) = ra(i,1,2) yfrc(icnt) = ra(i,2,2) zfrc(icnt) = ra(i,3,2) mbuf(icnt) = mass(i) bidx(icnt) = aidx(i) bat(icnt) = at(i) end do btot = antot antot = 0 c c send along z-axis and gather particles whose z value lies c between zmin and zmax c call cull(3) do i = 1, idz-1 inz = ipz - 1 if (inz.eq.-1) inz = idz - 1 call i_xyz_to_proc(rnode,ipx,ipy,inz,idx,idy,idz) call exchange_buf(rnode) call cull(3) end do c c rearrange data stack so that they are ordered with respect to c atom index c call heapsort(0) call fixper c return end c subroutine exchange_buf(rnode) #include "common.fh" c double precision buf(3,MAXAT), rbuf(MAXAT) integer i, ibuf(MAXAT) integer rnode, rptr, rtot, me, one, ld c c exchange the size of lists c me = ga_nodeid() one = 1 ld = 3 c do i = 1, btot buf(1,i) = xcrd(i) buf(2,i) = ycrd(i) buf(3,i) = zcrd(i) end do c rptr = gsize_lo(me) call nga_put(g_size,rptr,rptr,btot,one) gcoords_hi(1) = 3 gcoords_hi(2) = gcoords_lo(2,me) + btot - 1 if (btot.gt.0) call nga_put(g_coords,gcoords_lo(1,me), + gcoords_hi,buf,ld) call ga_sync() rptr = gsize_lo(rnode) call nga_get(g_size,rptr,rptr,rtot,one) gcoords_hi(2) = gcoords_lo(2,rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_coords,gcoords_lo(1,rnode), + gcoords_hi,buf,ld) c do i = 1, rtot xcrd(i) = buf(1,i) ycrd(i) = buf(2,i) zcrd(i) = buf(3,i) end do call ga_sync() c do i = 1, btot buf(1,i) = xfrc(i) buf(2,i) = yfrc(i) buf(3,i) = zfrc(i) end do c gcoords_hi(1) = 3 gcoords_hi(2) = gcoords_lo(2,me) + btot - 1 if (btot.gt.0) call nga_put(g_coords,gcoords_lo(1,me), + gcoords_hi,buf,ld) call ga_sync() gcoords_hi(2) = gcoords_lo(2,rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_coords,gcoords_lo(1,rnode), + gcoords_hi,buf,ld) c do i = 1, rtot xfrc(i) = buf(1,i) yfrc(i) = buf(2,i) zfrc(i) = buf(3,i) end do call ga_sync() c do i = 1, btot rbuf(i) = mbuf(i) end do c grvec_hi = grvec_lo(me) + btot - 1 if (btot.gt.0) call nga_put(g_rvec,grvec_lo(me),grvec_hi,rbuf,ld) call ga_sync() grvec_hi = grvec_lo(rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_rvec,grvec_lo(rnode), + grvec_hi,rbuf,ld) c do i = 1, rtot mbuf(i) = rbuf(i) end do call ga_sync() c do i = 1, btot ibuf(i) = bidx(i) end do c gindex_hi = gindex_lo(me) + btot - 1 if (btot.gt.0) call nga_put(g_index,gindex_lo(me), + gindex_hi,ibuf,ld) call ga_sync() gindex_hi = gindex_lo(rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_index,gindex_lo(rnode), + gindex_hi,ibuf,ld) c do i = 1, rtot bidx(i) = ibuf(i) end do call ga_sync() c do i = 1, btot ibuf(i) = bat(i) end do c gindex_hi = gindex_lo(me) + btot - 1 if (btot.gt.0) call nga_put(g_index,gindex_lo(me),gindex_hi, + ibuf,ld) call ga_sync() gindex_hi = gindex_lo(rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_index,gindex_lo(rnode), + gindex_hi,ibuf,ld) c do i = 1, rtot bat(i) = ibuf(i) end do call ga_sync() c btot = rtot c return end c subroutine cull(iflg) #include "common.fh" c double precision xmax,ymax,zmax,xmin,ymin,zmin double precision xt,yt,zt integer i,pnum,me,idx,idy,idz,ipx,ipy,ipz integer icnt,iflg logical xflg,yflg,zflg c c This subroutine gathers all the particles in the buffer arrays that c have coordinates lying in the domain corresponding to the processor c and puts them in the regular particle arrays c c Determine boundaries of the physical domain assigned to the processor c pnum = ga_nnodes() me = ga_nodeid() call factor(pnum,idx,idy,idz) call i_proc_to_xyz(me,ipx,ipy,ipz,idx,idy,idz) c c set logical flags c if (iflg.eq.1) then xflg = .true. yflg = .false. zflg = .false. elseif (iflg.eq.2) then xflg = .false. yflg = .true. zflg = .false. elseif (iflg.eq.3) then xflg = .false. yflg = .false. zflg = .true. else call ga_error("Illegal direction in subroutine cull",iflg ) endif c xmax = xbox*dble(ipx+1)/dble(idx) ymax = ybox*dble(ipy+1)/dble(idy) zmax = zbox*dble(ipz+1)/dble(idz) xmin = xbox*dble(ipx)/dble(idx) ymin = ybox*dble(ipy)/dble(idy) zmin = zbox*dble(ipz)/dble(idz) xmax = xmax - xbox2 ymax = ymax - ybox2 zmax = zmax - zbox2 xmin = xmin - xbox2 ymin = ymin - ybox2 zmin = zmin - zbox2 c c Locate all particles on the processor that should reside c on the processor and move all others to the buffer c icnt = 0 do i = 1, btot xt = xcrd(i) - xbox * anint(xcrd(i)/xbox) yt = ycrd(i) - ybox * anint(ycrd(i)/ybox) zt = zcrd(i) - zbox * anint(zcrd(i)/zbox) if (xt.eq.xbox2) xt = -xbox2 if (yt.eq.ybox2) yt = -ybox2 if (zt.eq.zbox2) zt = -zbox2 if ((xflg.and.xt.lt.xmax.and.xt.ge.xmin).or. + (yflg.and.yt.lt.ymax.and.yt.ge.ymin).or. + (zflg.and.zt.lt.zmax.and.zt.ge.zmin)) then antot = antot + 1 ra(antot,1,6) = xcrd(i) ra(antot,2,6) = ycrd(i) ra(antot,3,6) = zcrd(i) ra(antot,1,2) = xfrc(i) ra(antot,2,2) = yfrc(i) ra(antot,3,2) = zfrc(i) mass(antot) = mbuf(i) aidx(antot) = bidx(i) at(antot) = bat(i) else icnt = icnt + 1 xcrd(icnt) = xcrd(i) ycrd(icnt) = ycrd(i) zcrd(icnt) = zcrd(i) xfrc(icnt) = xfrc(i) yfrc(icnt) = yfrc(i) zfrc(icnt) = zfrc(i) mbuf(icnt) = mbuf(i) bidx(icnt) = bidx(i) bat(icnt) = bat(i) endif end do btot = icnt c if (btot.gt.MAXAT) then c call ga_error("btot greater than MAXAT in cull",btot) c endif c return end ga-5.9.2/global/examples/md_cluster/sort.F000066400000000000000000000054061500715745200204460ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine sort #include "common.fh" c integer snode,rnode,pnum,idx,idy,idz,ipx,ipy,ipz,ilast,ifirst integer i,icnt,inode,mynode c c This subroutine sorts the particle data onto each of the processors c so that each processor has a list of consecutively numbered particles. c pnum = ga_nnodes() call factor(pnum,idx,idy,idz) inode = ga_nodeid() call i_proc_to_xyz(inode,ipx,ipy,ipz,idx,idy,idz) c c Find indices of first and last particle that should be on this c processor c ilast = nint(dble((ga_nodeid()+1)*atot)/dble(pnum)) ifirst = nint(dble(ga_nodeid()*atot)/dble(pnum)) ifirst = ifirst + 1 c c copy all particles into the buffer c icnt = 0 do i = 1, antot icnt = icnt + 1 xcrd(icnt) = ra(i,1,6) ycrd(icnt) = ra(i,2,6) zcrd(icnt) = ra(i,3,6) xfrc(icnt) = ra(i,1,2) yfrc(icnt) = ra(i,2,2) zfrc(icnt) = ra(i,3,2) mbuf(icnt) = mass(i) bidx(icnt) = aidx(i) bat(icnt) = at(i) end do btot = antot antot = 0 c c send buffers to all nodes in systolic loop c and select out particles that fall within the appropriate c range of indices c mynode = ga_nodeid() call icull(ifirst,ilast) do inode = 1, pnum-1 snode = ga_nodeid() + 1 if (snode.eq.pnum) snode = 0 rnode = ga_nodeid() - 1 if (rnode.eq.-1) rnode = pnum - 1 call exchange_buf(rnode) call icull(ifirst,ilast) end do c c rearrange data stack so that they are ordered with respect to c atom index c call heapsort(0) call fixper icnt = 0 c return end c subroutine icull(ifirst,ilast) #include "common.fh" c integer ifirst,ilast,i,icnt c c This subroutine gathers all the particles in the buffer arrays that c have indices lying in the domain [ifirst,ilast] c and puts them in the regular particle arrays c icnt = 0 do i = 1, btot if (bidx(i).ge.ifirst.and.bidx(i).le.ilast) then antot = antot + 1 ra(antot,1,6) = xcrd(i) ra(antot,2,6) = ycrd(i) ra(antot,3,6) = zcrd(i) ra(antot,1,2) = xfrc(i) ra(antot,2,2) = yfrc(i) ra(antot,3,2) = zfrc(i) mass(antot) = mbuf(i) aidx(antot) = bidx(i) at(antot) = bat(i) else icnt = icnt + 1 xcrd(icnt) = xcrd(i) ycrd(icnt) = ycrd(i) zcrd(icnt) = zcrd(i) xfrc(icnt) = xfrc(i) yfrc(icnt) = yfrc(i) zfrc(icnt) = zfrc(i) mbuf(icnt) = mbuf(i) bidx(icnt) = bidx(i) bat(icnt) = bat(i) endif end do btot = icnt c return end ga-5.9.2/global/examples/md_cluster/tstats.F000066400000000000000000000072211500715745200207760ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif function wraptime() double precision wraptime #if HAVE_ETIME real*4 etime,tarray(2) external etime c c This function returns the UNIX clock time in seconds. c It is designed to minimize the effort required to c convert the timing statistics in going from one c machine to another. c wraptime = dble(etime(tarray)) #else c c NO TIME STATISTICS c wraptime = 0.0d00 #endif c return end c subroutine tmout #include "common.fh" double precision tmax(MAXTIM),tmin(MAXTIM) integer i,pnum c c This routine writes out the timekeeping statistics to c the output file 'md.out' c do i = 1, MAXTIM tmin(i) = tmstat(i) tmax(i) = tmstat(i) end do call ga_dgop(5,tmstat,MAXTIM,'+') call ga_dgop(5,tmax,MAXTIM,'max') call ga_dgop(5,tmin,MAXTIM,'min') pnum = ga_nnodes() do i = 1, MAXTIM tmstat(i) = tmstat(i)/dble(pnum) end do if (ga_nodeid().ne.0) return c if (l_stdio) then write(6,100) tmstat(1),tmin(1),tmax(1) write(6,200) tmstat(2),tmin(2),tmax(2) write(6,300) tmstat(3),tmin(3),tmax(3) write(6,400) tmstat(4),tmin(4),tmax(4) write(6,500) tmstat(5),tmin(5),tmax(5) write(6,2300) tmstat(23),tmin(23),tmax(23) write(6,550) tmstat(6)+tmstat(7)+tmstat(8) write(6,600) tmstat(6),tmin(6),tmax(6) write(6,700) tmstat(7),tmin(7),tmax(7) write(6,750) write(6,800) tmstat(8),tmin(8),tmax(8) write(6,2800) tmstat(28),tmin(28),tmax(28) write(6,1400) tmstat(14),tmin(14),tmax(14) write(6,850) tmstat(9)+tmstat(10) write(6,900) tmstat(9),tmin(9),tmax(9) write(6,1000) tmstat(10),tmin(10),tmax(10) write(6,1050) tmstat(11)+tmstat(12)+tmstat(13) write(6,1100) tmstat(11),tmin(11),tmax(11) write(6,1200) tmstat(12),tmin(12),tmax(12) write(6,1300) tmstat(13),tmin(13),tmax(13) write(6,1500) tmstat(15),tmin(15),tmax(15) write(6,1600) tmstat(16),tmin(16),tmax(16) endif c 100 format('Total elapsed time for calculation ',3f8.1,' (sec)') 200 format('Total time in force calculation ',3f8.1,' (sec)') 300 format(' Pairwise interactions ',3f8.1,' (sec)') 400 format(' Bending interactions ',3f8.1,' (sec)') 500 format(' Stretching interactions ',3f8.1,' (sec)') 550 format('Total time exchanging particle lists ',f8.1,' (sec)') 600 format(' Gathering list ',3f8.1,' (sec)') 700 format(' Scattering list ',3f8.1,' (sec)') 750 format(' Updating lists ',3f8.1,' (sec)') 800 format(' Coordinates ',3f8.1,' (sec)') 850 format('Total time sorting lists ',f8.1,' (sec)') 900 format(' Sorting local list ',3f8.1,' (sec)') 1000 format(' Sorting buffer list ',3f8.1,' (sec)') 1050 format('Total time sending messages ',f8.1,' (sec)') 1100 format(' Gather ',3f8.1,' (sec)') 1200 format(' Scatter ',3f8.1,' (sec)') 1300 format(' Update ',3f8.1,' (sec)') 1400 format(' Angles ',3f8.1,' (sec)') 1500 format('Total time calculating stress tensor ',3f8.1,' (sec)') 1600 format('Total time in scatter synchronization ',3f8.1,' (sec)') 2300 format('Total time constructing neighbor list ',3f8.1,' (sec)') 2800 format(' Bonds ',3f8.1,' (sec)') c return end ga-5.9.2/global/examples/md_cluster/update.F000066400000000000000000000412451500715745200207420ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine update #include "common.fh" c double precision xmax,ymax,zmax,xmin,ymin,zmin double precision xcell,ycell,zcell double precision tbeg,wraptime integer snode,rnode,pnum,inode,idx,idy,idz,ipx,ipy,ipz integer icnt,i,j,k,inx,iny,inz c c This subroutine gathers coordinates of particles that have moved off c the processor and sends then to neighboring processors. It also recieves c the coordinates of particles that have moved onto the processor. c tbeg = wraptime() call clear_hash pnum = ga_nnodes() call factor(pnum,idx,idy,idz) inode = ga_nodeid() call i_proc_to_xyz(inode,ipx,ipy,ipz,idx,idy,idz) c c determine local boundaries c xmax = xbox*dble(ipx+1)/dble(idx) ymax = ybox*dble(ipy+1)/dble(idy) zmax = zbox*dble(ipz+1)/dble(idz) xmin = xbox*dble(ipx)/dble(idx) ymin = ybox*dble(ipy)/dble(idy) zmin = zbox*dble(ipz)/dble(idz) xmax = xmax - xbox2 ymax = ymax - ybox2 zmax = zmax - zbox2 xmin = xmin - xbox2 ymin = ymin - ybox2 zmin = zmin - zbox2 xcell = xbox/dble(idx) ycell = ybox/dble(idy) zcell = zbox/dble(idz) c btot = 0 c c Send particles along x-axis. Gather all particles that lie within c processor domain c if (idx.ge.2) then c c collect all particles that have moved off of processor c if (idx.eq.2) then c c both boundaries are adjacent to boundaries on next processor c icnt = 0 do i = 1, antot if (ra(i,1,1).ge.xmax.or.ra(i,1,1).lt.xmin) then btot = btot + 1 xcrd(btot) = ra(i,1,6) ycrd(btot) = ra(i,2,6) zcrd(btot) = ra(i,3,6) xfrc(btot) = ra(i,1,2) yfrc(btot) = ra(i,2,2) zfrc(btot) = ra(i,3,2) xacc(btot) = ra(i,1,3) yacc(btot) = ra(i,2,3) zacc(btot) = ra(i,3,3) bat(btot) = at(i) mbuf(btot) = mass(i) bidx(btot) = aidx(i) else icnt = icnt + 1 do j = 1, 3 do k = 1, 6 ra(icnt,j,k) = ra(i,j,k) end do end do at(icnt) = at(i) mass(icnt) = mass(i) aidx(icnt) = aidx(i) endif end do antot = icnt else c c processor bounded by two separate processors c icnt = 0 do i = 1, antot if ((ra(i,1,1).ge.xmax.and.ra(i,1,1)-xmax.lt.xcell).or. + (ra(i,1,1).lt.xmin.and.xmin-ra(i,1,1).gt.xcell)) then btot = btot + 1 xcrd(btot) = ra(i,1,6) ycrd(btot) = ra(i,2,6) zcrd(btot) = ra(i,3,6) xfrc(btot) = ra(i,1,2) yfrc(btot) = ra(i,2,2) zfrc(btot) = ra(i,3,2) xacc(btot) = ra(i,1,3) yacc(btot) = ra(i,2,3) zacc(btot) = ra(i,3,3) bat(btot) = at(i) mbuf(btot) = mass(i) bidx(btot) = aidx(i) else icnt = icnt + 1 do j = 1, 3 do k = 1, 6 ra(icnt,j,k) = ra(i,j,k) end do end do at(icnt) = at(i) mass(icnt) = mass(i) aidx(icnt) = aidx(i) endif end do antot = icnt endif c c send particles in positive x direction c inx = ipx - 1 if (inx.eq.-1) inx = idx - 1 call i_xyz_to_proc(rnode,inx,ipy,ipz,idx,idy,idz) call update_buf(rnode) call ucull endif c if (idx.gt.2) then c c each boundary is adjacent to a different processor c icnt = 0 do i = 1, antot if ((ra(i,1,1).lt.xmin.and.xmin-ra(i,1,1).lt.xcell).or. + (ra(i,1,1).gt.xmax.and.ra(i,1,1)-xmax.gt.xcell)) then btot = btot + 1 xcrd(btot) = ra(i,1,6) ycrd(btot) = ra(i,2,6) zcrd(btot) = ra(i,3,6) xfrc(btot) = ra(i,1,2) yfrc(btot) = ra(i,2,2) zfrc(btot) = ra(i,3,2) xacc(btot) = ra(i,1,3) yacc(btot) = ra(i,2,3) zacc(btot) = ra(i,3,3) bat(btot) = at(i) mbuf(btot) = mass(i) bidx(btot) = aidx(i) else icnt = icnt + 1 do j = 1, 3 do k = 1, 6 ra(icnt,j,k) = ra(i,j,k) end do end do at(icnt) = at(i) mass(icnt) = mass(i) aidx(icnt) = aidx(i) endif end do antot = icnt c c send particles in negative x direction c inx = ipx + 1 if (inx.eq.idx) inx = 0 call i_xyz_to_proc(rnode,inx,ipy,ipz,idx,idy,idz) call update_buf(rnode) call ucull endif c c repeat for y axis c if (idy.ge.2) then c c both boundaries are adjacent to boundaries on next processor c if (idy.eq.2) then icnt = 0 do i = 1, antot if (ra(i,2,1).ge.ymax.or.ra(i,2,1).lt.ymin) then btot = btot + 1 xcrd(btot) = ra(i,1,6) ycrd(btot) = ra(i,2,6) zcrd(btot) = ra(i,3,6) xfrc(btot) = ra(i,1,2) yfrc(btot) = ra(i,2,2) zfrc(btot) = ra(i,3,2) xacc(btot) = ra(i,1,3) yacc(btot) = ra(i,2,3) zacc(btot) = ra(i,3,3) bat(btot) = at(i) mbuf(btot) = mass(i) bidx(btot) = aidx(i) else icnt = icnt + 1 do j = 1, 3 do k = 1, 6 ra(icnt,j,k) = ra(i,j,k) end do end do at(icnt) = at(i) mass(icnt) = mass(i) aidx(icnt) = aidx(i) endif end do antot = icnt else c c processor bounded by two separate processors c icnt = 0 do i = 1, antot if ((ra(i,2,1).ge.ymax.and.ra(i,2,1)-ymax.lt.ycell).or. + (ra(i,2,1).lt.ymin.and.ymin-ra(i,2,1).gt.ycell)) then btot = btot + 1 xcrd(btot) = ra(i,1,6) ycrd(btot) = ra(i,2,6) zcrd(btot) = ra(i,3,6) xfrc(btot) = ra(i,1,2) yfrc(btot) = ra(i,2,2) zfrc(btot) = ra(i,3,2) xacc(btot) = ra(i,1,3) yacc(btot) = ra(i,2,3) zacc(btot) = ra(i,3,3) bat(btot) = at(i) mbuf(btot) = mass(i) bidx(btot) = aidx(i) else icnt = icnt + 1 do j = 1, 3 do k = 1, 6 ra(icnt,j,k) = ra(i,j,k) end do end do at(icnt) = at(i) mass(icnt) = mass(i) aidx(icnt) = aidx(i) endif end do antot = icnt endif c c send particles in positive y direction c iny = ipy - 1 if (iny.eq.-1) iny = idy - 1 call i_xyz_to_proc(rnode,ipx,iny,ipz,idx,idy,idz) call update_buf(rnode) call ucull endif icnt = 0 if (idy.gt.2) then c c each boundary is adjacent to a different processor c icnt = 0 do i = 1, antot if ((ra(i,2,1).lt.ymin.and.ymin-ra(i,2,1).lt.ycell).or. + (ra(i,2,1).gt.ymax.and.ra(i,2,1)-ymax.gt.ycell)) then btot = btot + 1 xcrd(btot) = ra(i,1,6) ycrd(btot) = ra(i,2,6) zcrd(btot) = ra(i,3,6) xfrc(btot) = ra(i,1,2) yfrc(btot) = ra(i,2,2) zfrc(btot) = ra(i,3,2) xacc(btot) = ra(i,1,3) yacc(btot) = ra(i,2,3) zacc(btot) = ra(i,3,3) bat(btot) = at(i) mbuf(btot) = mass(i) bidx(btot) = aidx(i) else icnt = icnt + 1 do j = 1, 3 do k = 1, 6 ra(icnt,j,k) = ra(i,j,k) end do end do at(icnt) = at(i) mass(icnt) = mass(i) aidx(icnt) = aidx(i) endif end do antot = icnt c c send particles in negative y direction c iny = ipy + 1 if (iny.eq.idy) iny = 0 call i_xyz_to_proc(rnode,ipx,iny,ipz,idx,idy,idz) call update_buf(rnode) call ucull endif c c repeat for z axis c if (idz.ge.2) then c c both boundaries are adjacent to boundaries on next processor c if (idz.eq.2) then icnt = 0 do i = 1, antot if (ra(i,3,1).ge.zmax.or.ra(i,3,1).lt.zmin) then btot = btot + 1 xcrd(btot) = ra(i,1,6) ycrd(btot) = ra(i,2,6) zcrd(btot) = ra(i,3,6) xfrc(btot) = ra(i,1,2) yfrc(btot) = ra(i,2,2) zfrc(btot) = ra(i,3,2) xacc(btot) = ra(i,1,3) yacc(btot) = ra(i,2,3) zacc(btot) = ra(i,3,3) bat(btot) = at(i) mbuf(btot) = mass(i) bidx(btot) = aidx(i) else icnt = icnt + 1 do j = 1, 3 do k = 1, 6 ra(icnt,j,k) = ra(i,j,k) end do end do at(icnt) = at(i) mass(icnt) = mass(i) aidx(icnt) = aidx(i) endif end do antot = icnt else c c processor bounded by two separate processors c icnt = 0 do i = 1, antot if ((ra(i,3,1).ge.zmax.and.ra(i,3,1)-zmax.lt.zcell).or. + (ra(i,3,1).lt.zmin.and.zmin-ra(i,3,1).gt.zcell)) then btot = btot + 1 xcrd(btot) = ra(i,1,6) ycrd(btot) = ra(i,2,6) zcrd(btot) = ra(i,3,6) xfrc(btot) = ra(i,1,2) yfrc(btot) = ra(i,2,2) zfrc(btot) = ra(i,3,2) xacc(btot) = ra(i,1,3) yacc(btot) = ra(i,2,3) zacc(btot) = ra(i,3,3) bat(btot) = at(i) mbuf(btot) = mass(i) bidx(btot) = aidx(i) else icnt = icnt + 1 do j = 1, 3 do k = 1, 6 ra(icnt,j,k) = ra(i,j,k) end do end do at(icnt) = at(i) mass(icnt) = mass(i) aidx(icnt) = aidx(i) endif end do antot = icnt endif c c send particles in positive z direction c inz = ipz - 1 if (inz.eq.-1) inz = idz - 1 call i_xyz_to_proc(rnode,ipx,ipy,inz,idx,idy,idz) call update_buf(rnode) call ucull endif if (idz.gt.2) then c c each boundary is adjacent to a different processor c icnt = 0 do i = 1, antot if ((ra(i,3,1).lt.zmin.and.zmin-ra(i,3,1).lt.zcell).or. + (ra(i,3,1).gt.zmax.and.ra(i,3,1)-zmax.gt.zcell)) then btot = btot + 1 xcrd(btot) = ra(i,1,6) ycrd(btot) = ra(i,2,6) zcrd(btot) = ra(i,3,6) xfrc(btot) = ra(i,1,2) yfrc(btot) = ra(i,2,2) zfrc(btot) = ra(i,3,2) xacc(btot) = ra(i,1,3) yacc(btot) = ra(i,2,3) zacc(btot) = ra(i,3,3) bat(btot) = at(i) mbuf(btot) = mass(i) bidx(btot) = aidx(i) else icnt = icnt + 1 do j = 1, 3 do k = 1, 6 ra(icnt,j,k) = ra(i,j,k) end do end do at(icnt) = at(i) mass(icnt) = mass(i) aidx(icnt) = aidx(i) endif end do antot = icnt c c send particles in negative z direction c inz = ipz + 1 if (inz.eq.idz) inz = 0 call i_xyz_to_proc(rnode,ipx,ipy,inz,idx,idy,idz) call update_buf(rnode) call ucull endif c c call heapsort(0) do i = 1, antot call add_hash_a(aidx(i),i) end do c tmstat(8) = tmstat(8) + wraptime() - tbeg c return end c subroutine update_buf(rnode) #include "common.fh" c integer ibuf(2,MAXAT) double precision cbuf(3,MAXAT),fbuf(3,MAXAT),abuf(3,MAXAT) double precision vbuf(MAXAT) double precision tbeg,wraptime integer i, stot, rtot integer me,rnode,one,is,ld2,ld3 c c Reassign all particles to the appropriate node. c me = ga_nodeid() one = 1 ld2 = 2 ld3 = 3 c tbeg = wraptime() c stot = btot do i = 1, stot cbuf(1,i) = xcrd(i) cbuf(2,i) = ycrd(i) cbuf(3,i) = zcrd(i) fbuf(1,i) = xfrc(i) fbuf(2,i) = yfrc(i) fbuf(3,i) = zfrc(i) abuf(1,i) = xacc(i) abuf(2,i) = yacc(i) abuf(3,i) = zacc(i) vbuf(i) = mbuf(i) ibuf(1,i) = bidx(i) ibuf(2,i) = bat(i) end do c gsize_hi = gsize_lo(me) call nga_put(g_size,gsize_lo(me),gsize_hi,stot,one) gcoords_hi(1) = 3 gcoords_hi(2) = gcoords_lo(2,me) + stot - 1 if (stot.gt.0) call nga_put(g_coords,gcoords_lo(1,me), + gcoords_hi,cbuf,ld3) gfrc_hi(1) = 3 gfrc_hi(2) = gfrc_lo(2,me) + stot - 1 if (stot.gt.0) call nga_put(g_frc,gfrc_lo(1,me),gfrc_hi,fbuf,ld3) gacc_hi(1) = 3 gacc_hi(2) = gacc_lo(2,me) + stot - 1 if (stot.gt.0) call nga_put(g_acc,gacc_lo(1,me),gacc_hi,abuf,ld3) grvec_hi = grvec_lo(me) + stot - 1 if (stot.gt.0) call nga_put(g_rvec,grvec_lo(me),grvec_hi,vbuf,one) giat_hi(1) = 2 giat_hi(2) = giat_lo(2,me) + stot - 1 if (stot.gt.0) call nga_put(g_iat,giat_lo(1,me),giat_hi,ibuf,ld2) c call ga_sync() c gsize_hi = gsize_lo(rnode) call nga_get(g_size,gsize_lo(rnode),gsize_hi,rtot,one) gcoords_hi(2) = gcoords_lo(2,rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_coords,gcoords_lo(1,rnode), + gcoords_hi,cbuf,ld3) gfrc_hi(2) = gfrc_lo(2,rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_frc,gfrc_lo(1,rnode), + gfrc_hi,fbuf,ld3) gacc_hi(2) = gacc_lo(2,rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_acc,gacc_lo(1,rnode), + gacc_hi,abuf,ld3) grvec_hi = grvec_lo(rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_rvec,grvec_lo(rnode), + grvec_hi,vbuf,one) giat_hi(2) = giat_lo(2,rnode) + rtot - 1 if (rtot.gt.0) call nga_get(g_iat,giat_lo(1,rnode), + giat_hi,ibuf,ld2) c do i = 1, rtot xcrd(i) = cbuf(1,i) ycrd(i) = cbuf(2,i) zcrd(i) = cbuf(3,i) xfrc(i) = fbuf(1,i) yfrc(i) = fbuf(2,i) zfrc(i) = fbuf(3,i) xacc(i) = abuf(1,i) yacc(i) = abuf(2,i) zacc(i) = abuf(3,i) mbuf(i) = vbuf(i) bidx(i) = ibuf(1,i) bat(i) = ibuf(2,i) end do c btot = rtot call ga_sync() c tmstat(13) = tmstat(13) + wraptime() - tbeg c return end c subroutine ucull #include "common.fh" c double precision xmax,ymax,zmax,xmin,ymin,zmin double precision xt,yt,zt integer pnum,inode,idx,idy,idz,ipx,ipy,ipz integer i,icnt c pnum = ga_nnodes() call factor(pnum,idx,idy,idz) inode = ga_nodeid() call i_proc_to_xyz(inode,ipx,ipy,ipz,idx,idy,idz) c c determine local boundaries c xmax = xbox*dble(ipx+1)/dble(idx) ymax = ybox*dble(ipy+1)/dble(idy) zmax = zbox*dble(ipz+1)/dble(idz) xmin = xbox*dble(ipx)/dble(idx) ymin = ybox*dble(ipy)/dble(idy) zmin = zbox*dble(ipz)/dble(idz) xmax = xmax - xbox2 ymax = ymax - ybox2 zmax = zmax - zbox2 xmin = xmin - xbox2 ymin = ymin - ybox2 zmin = zmin - zbox2 c c select out particles from buffers that belong on the home array c icnt = 0 do i = 1, btot xt = xcrd(i) - xbox*anint(xcrd(i)/xbox) yt = ycrd(i) - ybox*anint(ycrd(i)/ybox) zt = zcrd(i) - zbox*anint(zcrd(i)/zbox) antot = antot + 1 ra(antot,1,6) = xcrd(i) ra(antot,2,6) = ycrd(i) ra(antot,3,6) = zcrd(i) ra(antot,1,2) = xfrc(i) ra(antot,2,2) = yfrc(i) ra(antot,3,2) = zfrc(i) ra(antot,1,3) = xacc(i) ra(antot,2,3) = yacc(i) ra(antot,3,3) = zacc(i) ra(antot,1,1) = xt ra(antot,2,1) = yt ra(antot,3,1) = zt at(antot) = bat(i) mass(antot) = mbuf(i) aidx(antot) = bidx(i) end do btot = icnt c return end ga-5.9.2/global/examples/petsc.F000066400000000000000000000144501500715745200164330ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif program petsc implicit none #include "mafdecls.fh" #include "global.fh" integer heap, stack data heap,stack /2*40000/ c c*** Intitialize a message passing library c #ifdef MSG_COMMS_MPI call mpi_init #else call pbeginf #endif c call ga_initialize() ! initialize GAs if (.not. ma_init(MT_DBL, heap, stack)) ! initialize MA $ call ga_error("ma init failed",heap+stack) #ifdef ENABLE_TRACE call trace_init(10000) ! initialize trace #endif call iterate() ! do the work #ifdef ENABLE_TRACE call trace_end(ga_nodeid()) ! end trace #endif call ga_terminate() ! terminate GAs c #ifdef MSG_COMMS_MPI call mpi_finalize() #else call pend() #endif end subroutine iterate() implicit none #include "include/finclude/petsc.h" #include "include/finclude/vec.h" #include "include/finclude/mat.h" #include "include/finclude/pc.h" #include "include/finclude/ksp.h" #include "include/finclude/sles.h" #include "include/finclude/sys.h" #include "mafdecls.fh" #include "global.fh" double precision norm integer i, j, II, JJ, ierr, m, n parameter (m = 3) parameter (n = 3) integer its, Istart, Iend, flg integer me, nproc Scalar v, one, neg_one Vec x, b, u Mat A SLES sles KSP ksp PetscRandom rctx integer g_x integer ld double precision buf_v(1) PetscOffset idx c$$$ PC pc c$$$ PCType ptype c$$$ double precision tol ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! Beginning of program ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - c*** check parallel environment me = ga_nodeid() nproc = ga_nnodes() one = 1.0 neg_one = -1.0 ld = m*n c*** Global Array c*** create global arrays: g_x - approx. solution if (.not. ga_create(MT_DBL, m*n, 1, 'x', 1, 1, g_x)) $ call ga_error(' ga_create failed ',0) c c*** initial guess for x -- zero call ga_zero(g_x) c c$$$ do i=1,m*n c$$$ buf(i) = i c$$$ enddo c$$$ call ga_put(g_x,1,m*n,1,1,buf,ld) c c*** PETSC call PetscInitialize(PETSC_NULL_CHARACTER,ierr) ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! Compute the matrix and right-hand-side vector that define ! the linear system, Ax = b. ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - c*** Matrix A call MatCreate(PETSC_COMM_WORLD,m*n,m*n,A,ierr) call MatGetOwnershipRange(A,Istart,Iend,ierr) do II=Istart,Iend-1 v = -1.0 i = II/n j = II - i*n if ( i.gt.0 ) then JJ = II - n call MatSetValues(A,1,II,1,JJ,v,ADD_VALUES,ierr) endif if ( i.lt.m-1 ) then JJ = II + n call MatSetValues(A,1,II,1,JJ,v,ADD_VALUES,ierr) endif if ( j.gt.0 ) then JJ = II - 1 call MatSetValues(A,1,II,1,JJ,v,ADD_VALUES,ierr) endif if ( j.lt.n-1 ) then JJ = II + 1 call MatSetValues(A,1,II,1,JJ,v,ADD_VALUES,ierr) endif v = 4.0 call MatSetValues(A,1,II,1,II,v,ADD_VALUES,ierr) enddo call MatAssemblyBegin(A,MAT_FINAL_ASSEMBLY,ierr) call MatAssemblyEnd(A,MAT_FINAL_ASSEMBLY,ierr) c*** Vector b call VecCreateMPI(PETSC_COMM_WORLD,PETSC_DECIDE,m*n,u,ierr) call VecSetFromOptions(u,ierr) call VecDuplicate(u,b,ierr) call VecDuplicate(b,x,ierr) c*** u is the exact solution call VecSet(one,u,ierr) if (me .eq. 0) print *, 'Exact solution:' call VecView(u, VIEWER_STDOUT_WORLD, ierr) c*** b is the right hand side call MatMult(A,u,b,ierr) c*** Manage to make connection of ga to petsc: g_x -> x call VecGetOwnershipRange(x,Istart,Iend,ierr) call VecGetArray(x,buf_v,idx,ierr) call ga_get(g_x,Istart+1,Iend,1,1,buf_v(idx+1),ld) call VecRestoreArray(x,buf_v,idx,ierr) ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! Create the linear solver and set various options ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - call SLESCreate(PETSC_COMM_WORLD,sles,ierr) call SLESSetOperators(sles,A,A,DIFFERENT_NONZERO_PATTERN, & ierr) c$$$ call SLESGetPC(sles,pc,ierr) c$$$ ptype = PCJACOBI c$$$ call PCSetType(pc,ptype,ierr) c$$$ call SLESGetKSP(sles,ksp,ierr) c$$$ tol = 1.e-7 c$$$ call KSPSetTolerances(ksp,tol,PETSC_DEFAULT_DOUBLE_PRECISION, c$$$ & PETSC_DEFAULT_DOUBLE_PRECISION,PETSC_DEFAULT_INTEGER,ierr) call SLESSetFromOptions(sles,ierr) ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! Solve the linear system ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - call SLESSolve(sles,b,x,its,ierr) call ga_sync() c*** write the approx solution back to ga call VecGetArray(x,buf_v,idx,ierr) call ga_put(g_x,Istart+1,Iend,1,1,buf_v(idx+1),ld) call VecRestoreArray(x,buf_v,idx,ierr) if (me .eq. 0) print *, 'Approx solution:' call ga_print(g_x) ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! Check solution and clean up ! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ! Check the error call VecAXPY(neg_one,u,x,ierr) call VecNorm(x,NORM_2,norm,ierr) if (me .eq. 0) then if (norm .gt. 1.e-12) then write(6,100) norm, its else write(6,110) its endif endif 100 format('Norm of error ',e10.4,' iterations ',i5) 110 format('Norm of error < 1.e-12, iterations ',i5) c*** clean up call SLESDestroy(sles,ierr) call VecDestroy(u,ierr) call VecDestroy(x,ierr) call VecDestroy(b,ierr) call MatDestroy(A,ierr) call PetscFinalize(ierr) if(.not. ga_destroy(g_x)) call ga_error('invalid handle ?',0) c call ga_sync() end ga-5.9.2/global/examples/scf/000077500000000000000000000000001500715745200157555ustar00rootroot00000000000000ga-5.9.2/global/examples/scf/README000066400000000000000000000020641500715745200166370ustar00rootroot00000000000000This is a converted version of the scf test code in the tcgmsg directory that solves the Hartree-Fock equations for a cluster of beryllium atoms. The main modifications are that this code is uses distributed data instead of replicated data and is based on the GA function calls instead of TCGMSG. The input is a simple XYZ format file of the form:: 1 Number of beryllium atoms 2 Blank line 3 4 x y z 4 4 x y z . . . Other parameters that control convergence etc. can be found in the cscf.h file. The two-electron Fock build does not make use of the 8-fold symmetry of the two-electron integrals. The default diagonalization routine makes use of a similarity transform so that only a standard eigenvalue problem needs to be solved. A direct solution using a generalized eigenvalue solver can be had by setting the USE_TRANSFORM compiler directive to 0 in the top of the scf.F file. A larger system containing 16 atoms has also been included in this directory. To run the larger problem, just copy the file be16.inpt to be.inpt and run the code. ga-5.9.2/global/examples/scf/be.inpt000066400000000000000000000001331500715745200172340ustar00rootroot000000000000004 4 0.000 0.000 0.000 4 4.000 0.000 0.000 4 8.000 0.000 0.000 4 12.000 0.000 0.000 ga-5.9.2/global/examples/scf/be16.inpt000066400000000000000000000005441500715745200174110ustar00rootroot0000000000000016 4 0.000 0.000 0.000 4 4.000 0.000 0.000 4 8.000 0.000 0.000 4 12.000 0.000 0.000 4 16.000 0.000 0.000 4 20.000 0.000 0.000 4 24.000 0.000 0.000 4 28.000 0.000 0.000 4 32.000 0.000 0.000 4 36.000 0.000 0.000 4 40.000 0.000 0.000 4 44.000 0.000 0.000 4 48.000 0.000 0.000 4 52.000 0.000 0.000 4 56.000 0.000 0.000 4 60.000 0.000 0.000 ga-5.9.2/global/examples/scf/cscf.h000066400000000000000000000060371500715745200170520ustar00rootroot00000000000000c c include file defining common /cscf/ c c constant parameters are set in cscf, parameters are set c after reading input. c c maxatom = maximum no. of atoms (constant parameter) c maxnbfn = maximum no. of bas. fnct.(constant parameter) c maxnnbfn = maxnbfn*(maxnbfn+1)/2 (constant parameter) c natom = no. of atoms (parameter) c nbfn = no. of basis functions (parameter) c nnbfn = nbfn*(nbfn+1)/2 (parameter) c nocc = no. of occupied orbitals (parameter) c mxiter = maximim no. of iterations(constant parameter) c tol = convergence criterion (constant parameter) c pi = a familiar constant (constant parameter) c tol2e = 2-e integral screening (constant parameter) c c the remainder is initialized in block data or in the c routine ininrm (rnorm and iky) c c enrep = nuclear repulsion energy c q(1:natom) = nuclear charge of atom c ax(1:natom) = x co-ordinate of atom c ay(1:natom) = y ... c az(1:natom) = z ... c x(1:nbfn) = x co-ordinate of basis function c y(1:nbfn) = y ... c z(1:nbfn) = z ... c expnt(1:nbfn)= exponent of gaussian c rnorm(1:nbfn)= normalization constant of gaussian c iky(1:nbfn) = iky(i) = i*(i-1)/2 to speed up fock build c icut1 = no. of successful ij 2-e screening tests c icut2 = no. of successful ijkl 2-e screening tests c icut3 = no. of 2-e integrals computed c parameter (maxatom = 286) !cste original value 50 parameter (maxnbfn =15*maxatom, mxiter = 30) parameter (maxnnbfn = maxnbfn*(maxnbfn+1)/2) parameter (pi = 3.141592653589793d0) parameter (tol= 0.5d-3) parameter (tol2e=1.0d-6) c common /cscf/ $ enrep, q(maxatom), ax(maxatom), ay(maxatom), az(maxatom), $ x(maxnbfn), y(maxnbfn), z(maxnbfn), expnt(maxnbfn), $ rnorm(maxnbfn),iky(maxnbfn), icut1, icut2, icut3, icut4, $ natom, nocc, nbfn, nnbfn double precision enrep, q, ax, ay, az, x, y, z, expnt, rnorm integer*8 iky, icut1, icut2, icut3, icut4, natom, nocc, nbfn, $ nnbfn c c Global array parameters used in calculations: c c ichunk: chunk size for distributing workload c c g_counter: global array used to assign next task c g_dens: global array used to store density matrix c g_fock: global array used to store fock matrix c g_tfock: global array used to store transformed fock matrix c g_schwarz: global array used to store schwarz matrix c g_work: global array used to store work matrix c g_ident: global array used to store identity matrix c g_orbs: global array used to store orbital vectors c parameter (ichunk = 20) !cste original value 10 common /g_arrays/ eigv(maxnbfn), $ g_counter, g_dens, g_fock, g_tfock, g_schwarz, g_work, $ g_ident, g_orbs double precision eigv integer g_counter, g_dens, g_fock, g_tfock, g_schwarz, g_work, $ g_ident, g_orbs ga-5.9.2/global/examples/scf/ft-scf.F000066400000000000000000001011771500715745200172550ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif program scf C$Id: ft-scf.F,v 1.1 2005-07-07 17:41:07 vinod Exp $ implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" #define USE_TRANSFORM 1 integer heap, stack data tinit, tonel, ttwoel, tdiag, tdens, tprint /6*0.0d0/ data eone, etwo, energy, deltad /4*0.0d0/ c c initalize the parallel message passing environment c #ifdef MSG_COMMS_MPI integer ierr call mpi_init(ierr) #else call pbeginf #endif call ga_set_spare_procs(2); call ga_initialize() c c Allocate memory c heap = 2000000 stack = 2000000 if (.not.ma_init(MT_DBL, stack, heap)) + call ga_error("ma_init failed",-1) me = ga_nodeid() nproc = ga_nnodes() c c initialize a bunch of stuff and initial density matrix c rjunk = timer() c c get input from file be.inpt c call input c c create and allocate global arrays c call setarrays write(6,*) 'done with setarrays' call ininrm write(6,*) '1-done with setarrays' c c create initial guess for density matrix by using single atom c densities c call denges tinit = timer() #if USE_TRANSFORM c c make initial orthogonal orbital set for solution method using c similarity transform c call makeob #endif c c make info for sparsity test c call makesz(schwmax) c c iterate call iterate(10) c call ga_igop(6, icut1, 1, '+') call ga_igop(7, icut2, 1, '+') call ga_igop(8, icut3, 1, '+') if (me.eq.0) then c c print out timing information c call prnfin(energy) write(6,1) tinit, tonel, ttwoel, tdiag, tdens, tprint, $ nproc 1 format(/' init onel twoel diag dens print ncpu'/ $ ' ------ ------ ------ ------ ------ ------ ------'/ $ 1x, 6f7.2, i7/) c c print out information on # integrals evaulated each iteration c nints = nnbfn*(nnbfn+1)/2 nints = nbfn**4 frac = dble(icut3)/dble(nints) write(6,2) icut1, icut2, icut3, nints, frac 2 format(/' No. of integrals screened or computed ' $ /' -------------------------------------'/ $ /1x,' #ij test #kl test #compute #total', $ ' fraction', $ /1x,' --------- --------- --------- ---------', $ ' --------', $ /1x,4(2x,i9),f9.3) call stats endif c call closearrays call ga_terminate #ifdef MSG_COMMS_MPI call mpi_finalize #else call pend #endif c end c subroutine iterate(niter) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" do 10 iter = 1, niter #ifdef CHECKPOINT_SCF call ga_checkpoint_arrays(arraylist,numarrays) #endif c c make the one particle contribution to the fock matrix c and get the partial contribution to the energy c call oneel(schwmax, eone) tonel = tonel + timer() c c compute the two particle contributions to the fock matrix and c get the total energy. c call twoel(schwmax, etwo) ttwoel = ttwoel + timer() c c Diagonalize the fock matrix. The diagonalizers used in this c subroutine are actually sequential, not parallel. c call diagon(tester,iter) tdiag = tdiag + timer() c c make the new density matrix in g_work from orbitals in g_orbs, c compute the norm of the change in the density matrix and c then update the density matrix in g_dens with damping. c call makden deltad = dendif() if (iter.eq.1) then scale = 0.0d0 else if (iter .le. 5) then if (nbfn .gt. 60) then scale = 0.5d0 else scale = 0.0d0 endif else scale = 0.0d0 endif call damp(scale) tdens = tdens + timer() c c add up energy and print out convergence information c if (me.eq.0) then energy = enrep + eone + etwo call prnout(iter, energy, deltad, tester) tprint = tprint + timer() endif c c if converged then exit iteration loop c if (deltad .lt. tol) goto 20 10 continue if(me.eq.0) $ write(6,*) ' SCF failed to converge in ', niter, ' iters' c c finished ... print out eigenvalues and occupied orbitals c 20 continue end subroutine makesz(schwmax) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" dimension work(ichunk,ichunk) integer lo(2),hi(2),i,j,iloc,jloc,ld logical dotask c c schwarz(ij) = (ij|ij) for sparsity test c icut1 = 0 icut2 = 0 icut3 = 0 c call ga_zero(g_schwarz) call ga_zero(g_counter) schwmax = 0.0d0 dotask = next_chunk(lo,hi) ld = ichunk do while (dotask) do i = lo(1), hi(1) iloc = i - lo(1) + 1 do j = lo(2), hi(2) jloc = j - lo(2) + 1 call g(gg,i,j,i,j) work(iloc,jloc) = sqrt(gg) schwmax = max(schwmax, work(iloc,jloc)) end do end do call nga_put(g_schwarz,lo,hi,work,ld) dotask = next_chunk(lo,hi) end do call ga_dgop(11,schwmax,1,'max') c return end c subroutine ininrm implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" c c write a little welcome message c if (ga_nodeid().eq.0) write(6,1) natom, nocc, nbfn, tol 1 format(/' Example Direct Self Consistent Field Program '/ $ ' -------------------------------------------- '// $ ' no. of atoms ............... ',i3/ $ ' no. of occupied orbitals ... ',i3/ $ ' no. of basis functions ..... ',i3/ $ ' convergence threshold ...... ',d9.2//) c c generate normalisation coefficients for the basis functions c and the index array iky c do 10 i = 1, nbfn iky(i) = i*(i-1)/2 10 continue c do 20 i = 1, nbfn rnorm(i) = (expnt(i)*2.0d0/pi)**0.75d0 20 continue c c initialize common for computing f0 c call setfm c end double precision function h(i,j) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" cvd$r novector cvd$r noconcur c c generate the one particle hamiltonian matrix element c over the normalized primitive 1s functions i and j c f0val = 0.0d0 sum = 0.0d0 rab2 = (x(i)-x(j))**2 + (y(i)-y(j))**2 + (z(i)-z(j))**2 facij = expnt(i)*expnt(j)/(expnt(i)+expnt(j)) expij = exprjh(-facij*rab2) repij = (2.0d0*pi/(expnt(i)+expnt(j))) * expij c c first do the nuclear attraction integrals c do 10 iat = 1, natom xp = (x(i)*expnt(i) + x(j)*expnt(j))/(expnt(i)+expnt(j)) yp = (y(i)*expnt(i) + y(j)*expnt(j))/(expnt(i)+expnt(j)) zp = (z(i)*expnt(i) + z(j)*expnt(j))/(expnt(i)+expnt(j)) rpc2 = (xp-ax(iat))**2 + (yp-ay(iat))**2 + (zp-az(iat))**2 c call f0(f0val, (expnt(i)+expnt(j))*rpc2) sum = sum - repij * q(iat) * f0val 10 continue c c add on the kinetic energy term c sum = sum + facij*(3.0d0-2.0d0*facij*rab2) * $ (pi/(expnt(i)+expnt(j)))**1.5d0 * expij c c finally multiply by the normalization constants c h = sum * rnorm(i) * rnorm(j) c end double precision function s(i,j) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" c c generate the overlap matrix element between the normalized c primitve gaussian 1s functions i and j c rab2 = (x(i)-x(j))**2 + (y(i)-y(j))**2 + (z(i)-z(j))**2 facij = expnt(i)*expnt(j)/(expnt(i)+expnt(j)) s = (pi/(expnt(i)+expnt(j)))**1.5d0 * exprjh(-facij*rab2) * $ rnorm(i)*rnorm(j) c end subroutine makden implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" c dimension work(maxnbfn,maxnbfn), torbs(maxnbfn,maxnbfn) dimension work(ichunk,ichunk), orbsi(ichunk,maxnbfn) dimension orbsj(ichunk,maxnbfn) integer lo(2), hi(2), tlo(2), thi(2), i, j, iloc, jloc, ld logical dotask c c generate density matrix from orbitals in g_orbs. the first c nocc orbitals are doubly occupied. c call ga_zero(g_counter) dotask = next_chunk(lo,hi) ld = ichunk do while (dotask) tlo(1) = lo(1) thi(1) = hi(1) tlo(2) = 1 thi(2) = nocc call nga_get(g_orbs,tlo,thi,orbsi,ld) tlo(1) = lo(2) thi(1) = hi(2) call nga_get(g_orbs,tlo,thi,orbsj,ld) do i = lo(1), hi(1) iloc = i - lo(1) + 1 do j = lo(2), hi(2) jloc = j - lo(2) + 1 p = 0.0d00 do k = 1, nocc p = p + orbsi(iloc,k)*orbsj(jloc,k) end do work(iloc,jloc) = 2.0d00*p end do end do call nga_put(g_work,lo,hi,work,ld) dotask = next_chunk(lo,hi) end do return end c subroutine oneel(schwmax, eone) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" integer lo(2), hi(2), i, j, iloc, jloc, ld dimension dens(nnbfn), fock(nnbfn), schwarz(nnbfn) dimension work(ichunk,ichunk),tfock(ichunk,ichunk) logical dotask c c fill in the one-electron part of the fock matrix and c compute the one-electron energy contribution c me = ga_nodeid() nproc = ga_nnodes() c call ga_zero(g_counter) dotask = next_chunk(lo,hi) ld = ichunk do while (dotask) call nga_get(g_schwarz,lo,hi,work,ld) do j = lo(2), hi(2) jloc = j - lo(2) + 1 do i = lo(1), hi(1) iloc = i - lo(1) + 1 tfock(iloc,jloc) = 0.0d00 if (work(iloc,jloc)*schwmax.gt.tol2e) + tfock(iloc,jloc) = h(i,j) end do end do call nga_put(g_fock,lo,hi,tfock,ld) dotask = next_chunk(lo,hi) end do eone = 0.5d00*contract_matrices(g_fock,g_dens) c end integer function nxtask(nproc) parameter (ichunk = 10) save icount, nleft data nleft, icount /0, 0/ c c wrapper round nxtval() to increase granularity c and thus reduce no. of requests to shared counter c if(nproc.gt.0) then if(nleft.eq.0) then icount = nxtval(nproc) * ichunk nleft = ichunk endif nxtask = icount icount = icount + 1 nleft = nleft -1 else nleft = 0 nxtask = 0 junk = nxtval(nproc) endif c c following does dumb static load balancing c c$$$ if(nproc.gt.0) then c$$$ if (nleft .eq. 0) then c$$$ icount = ga_nodeid() c$$$ nleft = 1 c$$$ endif c$$$ nxtask = icount c$$$ icount = icount + ga_nnodes() c$$$ else c$$$ nleft = 0 c$$$ nxtask = 0 c$$$ endif end c logical function next_chunk(lo,hi) #include "cscf.h" integer one parameter (one = 1) integer imax, lo(2), hi(2), ilo, jlo itask = nga_read_inc(g_counter,one,one) imax = nbfn/ichunk if (itask.lt.imax*imax) then if (nbfn - ichunk*imax.gt.0) imax = imax + 1 ilo = mod(itask,imax) jlo = (itask-ilo)/imax lo(1) = ilo*ichunk + 1 lo(2) = jlo*ichunk + 1 hi(1) = min((ilo+1)*ichunk,nbfn) hi(2) = min((jlo+1)*ichunk,nbfn) next_chunk = .true. else next_chunk = .false. endif return end c logical function next_4chunk(lo,hi,ilo,jlo,klo,llo) #include "cscf.h" integer one parameter (one = 1) integer imax, lo(4), hi(4), ilo, jlo, klo, llo, itmp itask = nga_read_inc(g_counter,one,one) imax = nbfn/ichunk if (nbfn - ichunk*imax.gt.0) imax = imax + 1 if (itask.lt.imax**4) then ilo = mod(itask,imax) itmp = (itask - ilo)/imax jlo = mod(itmp,imax) itmp = (itmp - jlo)/imax klo = mod(itmp,imax) llo = (itmp - klo)/imax lo(1) = ilo*ichunk + 1 lo(2) = jlo*ichunk + 1 lo(3) = klo*ichunk + 1 lo(4) = llo*ichunk + 1 hi(1) = min((ilo+1)*ichunk,nbfn) hi(2) = min((jlo+1)*ichunk,nbfn) hi(3) = min((klo+1)*ichunk,nbfn) hi(4) = min((llo+1)*ichunk,nbfn) next_4chunk = .true. else next_4chunk = .false. endif return end c subroutine clean_chunk(chunk) #include "cscf.h" double precision chunk(ichunk,ichunk) integer i,j do j = 1, ichunk do i = 1, ichunk chunk(i,j) = 0.0d00 end do end do return end c subroutine twoel(schwmax, etwo) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" double precision f_ij(ichunk,ichunk),d_kl(ichunk,ichunk) double precision f_ik(ichunk,ichunk),d_jl(ichunk,ichunk) double precision s_ij(ichunk,ichunk),s_kl(ichunk,ichunk) double precision schwmax, one integer nproc,lo(4),hi(4),ld,ich integer lo_ik(2),hi_ik(2),lo_jl(2),hi_jl(2) integer i,j,k,l,iloc,jloc,kloc,lloc,it,jt,kt,lt logical dotask, next_4chunk c c add in the two-electron contribution to the fock matrix c nproc = ga_nnodes() one = 1.0d00 c call ga_zero(g_counter) ld = maxnbfn ich = ichunk dotask = next_4chunk(lo,hi,it,jt,kt,lt) itask = 0 do while (dotask) lo_ik(1) = lo(1) lo_ik(2) = lo(3) hi_ik(1) = hi(1) hi_ik(2) = hi(3) lo_jl(1) = lo(2) lo_jl(2) = lo(4) hi_jl(1) = hi(2) hi_jl(2) = hi(4) call nga_get(g_schwarz,lo,hi,s_ij,ich) call nga_get(g_schwarz,lo(3),hi(3),s_kl,ich) call nga_get(g_dens,lo(3),hi(3),d_kl,ich) call nga_get(g_dens,lo_jl,hi_jl,d_jl,ich) itask = itask + 1 call clean_chunk(f_ij) call clean_chunk(f_ik) do i = lo(1), hi(1) iloc = i-lo(1) + 1 do j = lo(2), hi(2) jloc = j-lo(2) + 1 if (s_ij(iloc,jloc)*schwmax .lt. tol2e) then icut1 = icut1 + (hi(1)-lo(1)+1)*(hi(2)-lo(2)+1) else do k = lo(3), hi(3) kloc = k-lo(3) + 1 do l = lo(4), hi(4) lloc = l-lo(4) + 1 if (s_ij(iloc,jloc)*s_kl(kloc,lloc).lt.tol2e) then icut2 = icut2 + 1 else call g(gg, i, j, k, l) f_ij(iloc,jloc) = f_ij(iloc,jloc) + + gg*d_kl(kloc,lloc) f_ik(iloc,kloc) = f_ik(iloc,kloc) + - 0.5d00*gg*d_jl(jloc,lloc) icut3 = icut3 + 1 endif end do end do endif end do end do call nga_acc(g_fock,lo,hi,f_ij,ich,one) call nga_acc(g_fock,lo_ik,hi_ik,f_ik,ich,one) dotask = next_4chunk(lo,hi,it,jt,kt,lt) end do etwo = 0.5d00*contract_matrices(g_fock,g_dens) return end c subroutine damp(fac) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" c c create damped density matrix as a linear combination of c old density matrix and density matrix formed from new orbitals c ofac = 1.0d0 - fac call ga_add(fac,g_dens,ofac,g_work,g_dens) return end c subroutine prnout(iter, energy, deltad, tester) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" c c printout results of each iteration c if (ga_nodeid().ne.0) return write(6,1) iter, energy, deltad, tester call flush(6) 1 format(' iter=',i3,', energy=',f13.8,', deltad=',d9.2, $ ', deltaf=',d9.2) return end c double precision function dendif() implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" double precision xdiff dimension dens_c(ichunk,ichunk),work_c(ichunk,ichunk) integer lo(2), hi(2), i, j, ld logical dotask c c compute largest change in density matrix elements c denmax = 0.0d0 call ga_zero(g_counter) dotask = next_chunk(lo,hi) ld = ichunk do while(dotask) call nga_get(g_dens,lo,hi,dens_c,ld) call nga_get(g_work,lo,hi,work_c,ld) do j = 1, hi(2)-lo(2)+1 do i = 1, hi(1)-lo(1)+1 xdiff = abs(dens_c(i,j)-work_c(i,j)) if (xdiff.gt.denmax) denmax = xdiff end do end do dotask = next_chunk(lo,hi) end do call ga_dgop(1,denmax,1,'max') dendif = denmax return end c double precision function testfock() implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" double precision xmax, xtmp dimension work(ichunk,ichunk) integer lo(2), hi(2), i, j, iloc, jloc, ld logical dotask c c compute largest change in density matrix elements c xmax = 0.0d0 call ga_zero(g_counter) dotask = next_chunk(lo,hi) ld = ichunk do while(dotask) call nga_get(g_fock,lo,hi,work,ld) do j = lo(2), hi(2) jloc = j - lo(2) + 1 do i = lo(1), hi(1) iloc = i - lo(1) + 1 if (i.ne.j) then xtmp = abs(work(iloc,jloc)) if (xtmp.gt.xmax) xmax = xtmp endif end do end do dotask = next_chunk(lo,hi) end do call ga_dgop(1,xmax,1,'max') testfock = xmax return end c subroutine shiftfock(shift) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" double precision shift dimension work(ichunk,ichunk) integer lo(2), hi(2), i, j, iloc, jloc, ld, icnt logical dotask c c compute largest change in density matrix elements c call ga_zero(g_counter) dotask = next_chunk(lo,hi) ld = ichunk do while(dotask) call nga_get(g_fock,lo,hi,work,ld) icnt = 0 do j = lo(2), hi(2) jloc = j - lo(2) + 1 do i = lo(1), hi(1) iloc = i - lo(1) + 1 if (i.eq.j.and.i.gt.nocc) then work(iloc,jloc) = work(iloc,jloc) + shift icnt = icnt + 1 endif end do end do if (icnt.gt.0) call nga_put(g_fock,lo,hi,work,ld) dotask = next_chunk(lo,hi) end do return end c subroutine prnfin(energy) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" dimension orbs(maxnbfn, maxnbfn) integer lo(2),hi(2),ld c c printout final results c if (ga_nodeid().ne.0) return write(6,1) energy 1 format(//' final energy = ',f16.11//' eigenvalues') call output(eigv, 1, min(nbfn,nocc+5), 1, 1, nbfn, 1, 1) write(6,2) 2 format(//' eigenvectors ') lo(1) = 1 lo(2) = 1 hi(1) = nbfn hi(2) = nbfn ld = nbfn call nga_get(g_orbs,lo,hi,orbs,ld) call output(orbs, 1, nbfn, 1, nocc, nbfn, nbfn, 1) c return end subroutine g(value,i,j,k,l) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" c c compute the two electon integral (ij|kl) over normalized c primitive 1s gaussians c f0val = 0.0d0 rab2 = (x(i)-x(j))**2 + (y(i)-y(j))**2 + (z(i)-z(j))**2 rcd2 = (x(k)-x(l))**2 + (y(k)-y(l))**2 + (z(k)-z(l))**2 facij = expnt(i)*expnt(j)/(expnt(i)+expnt(j)) fackl = expnt(k)*expnt(l)/(expnt(k)+expnt(l)) exijkl = exprjh(- facij*rab2 - fackl*rcd2) denom = (expnt(i)+expnt(j))*(expnt(k)+expnt(l)) * $ sqrt(expnt(i)+expnt(j)+expnt(k)+expnt(l)) fac = (expnt(i)+expnt(j))*(expnt(k)+expnt(l)) / $ (expnt(i)+expnt(j)+expnt(k)+expnt(l)) c xp = (x(i)*expnt(i) + x(j)*expnt(j))/(expnt(i)+expnt(j)) yp = (y(i)*expnt(i) + y(j)*expnt(j))/(expnt(i)+expnt(j)) zp = (z(i)*expnt(i) + z(j)*expnt(j))/(expnt(i)+expnt(j)) xq = (x(k)*expnt(k) + x(l)*expnt(l))/(expnt(k)+expnt(l)) yq = (y(k)*expnt(k) + y(l)*expnt(l))/(expnt(k)+expnt(l)) zq = (z(k)*expnt(k) + z(l)*expnt(l))/(expnt(k)+expnt(l)) rpq2 = (xp-xq)**2 + (yp-yq)**2 + (zp-zq)**2 c call f0(f0val, fac*rpq2) value = (2.0d0 * pi**2.5d0 / denom) * exijkl * f0val * $ rnorm(i)*rnorm(j)*rnorm(k)*rnorm(l) return end c subroutine diagon(tester, iter) c subroutine diagon(fock, orbs, evals, work, tester, iter) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" double precision r_zero, r_one, shift, tester c #if USE_TRANSFORM c c use similarity transform to solve standard eigenvalue problem c (overlap matrix has been transformed out of the problem) c r_one = 1.0d00 r_zero = 0.0d00 call ga_dgemm('n','n',nbfn,nbfn,nbfn,r_one,g_fock,g_orbs, + r_zero,g_tfock) call ga_dgemm('t','n',nbfn,nbfn,nbfn,r_one,g_orbs,g_tfock, + r_zero,g_fock) tester = testfock() shift = 0.0d00 if (tester.gt.0.3d0) then shift = 0.3d0 endif if (iter.ge.2.and.shift.ne.0.0d00) then call shiftfock(shift) endif call ga_copy(g_orbs,g_tfock) call ga_diag_std_seq(g_fock, g_work, eigv) c c Back transform eigenvectors c call ga_dgemm('n','n',nbfn,nbfn,nbfn,r_one,g_tfock,g_work, + r_zero,g_orbs) if (iter.ge.2.and.shift.ne.0.0d00) then do 50 i = nocc+1, nbfn eigv(i) = eigv(i) - shift 50 continue endif #else c c Keep remaking overlap matrix since ga_diag_seq does not c guarantee that g_ident is preserved. c call makoverlap call ga_diag_seq(g_fock, g_ident, g_orbs, eigv) tester = 0.0d00 #endif return end c subroutine makeob implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" double precision work(ichunk,ichunk),orbs(ichunk,ichunk) double precision eval(maxnbfn) integer lo(2),hi(2),ld,me,i,j,iloc,jloc logical dotask c c generate set of orthonormal vectors by creating a random c symmetric matrix and solving associated generalized eigenvalue c problem using the correct overlap matrix. c me = ga_nodeid() call ga_zero(g_counter) dotask = next_chunk(lo,hi) ld = ichunk do while (dotask) do j = lo(2), hi(2) jloc = j - lo(2) + 1 do i = lo(1), hi(1) iloc = i - lo(1) + 1 work(iloc,jloc) = s(i,j) orbs(iloc,jloc) = drand48(0) end do end do call nga_put(g_ident,lo,hi,work,ld) call nga_put(g_fock,lo,hi,orbs,ld) dotask = next_chunk(lo,hi) end do call ga_symmetrize(g_fock) call ga_diag_seq(g_fock, g_ident, g_orbs, eval) c return end c subroutine denges implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" c c Form guess density from superposition of atomic densities in the AO c basis set ... instead of doing the atomic SCF hardwire for this c small basis set for the Be atom. c integer one, itask, lo(2), hi(2), ld dimension atdens(15,15) data atdens/ $ 0.000002,0.000027,0.000129,0.000428,0.000950,0.001180, $ 0.000457,-0.000270,-0.000271,0.000004,0.000004,0.000004, $ 0.000004,0.000004,0.000004,0.000027,0.000102,0.000987, $ 0.003269,0.007254,0.009007,0.003492,-0.002099,-0.002108, $ 0.000035,0.000035,0.000035,0.000035,0.000035,0.000035, $ 0.000129,0.000987,0.002381,0.015766,0.034988,0.043433, $ 0.016835,-0.010038,-0.010082,0.000166,0.000166,0.000166, $ 0.000166,0.000166,0.000166,0.000428,0.003269,0.015766, $ 0.026100,0.115858,0.144064,0.055967,-0.035878,-0.035990, $ 0.000584,0.000584,0.000584,0.000584,0.000584,0.000584, $ 0.000950,0.007254,0.034988,0.115858,0.128586,0.320120, $ 0.124539,-0.083334,-0.083536,0.001346,0.001346,0.001346, $ 0.001346,0.001346,0.001346,0.001180,0.009007,0.043433, $ 0.144064,0.320120,0.201952,0.159935,-0.162762,-0.162267, $ 0.002471,0.002471,0.002471,0.002471,0.002471,0.002471, $ 0.000457,0.003492,0.016835,0.055967,0.124539,0.159935, $ 0.032378,-0.093780,-0.093202,0.001372,0.001372,0.001372, $ 0.001372,0.001372,0.001372,-0.000270,-0.002099,-0.010038, $ -0.035878,-0.083334,-0.162762,-0.093780,0.334488,0.660918, $ -0.009090,-0.009090,-0.009090,-0.009090,-0.009090,-0.009090, $ -0.000271,-0.002108,-0.010082,-0.035990,-0.083536,-0.162267, $ -0.093202,0.660918,0.326482,-0.008982,-0.008982,-0.008981, $ -0.008981,-0.008981,-0.008982,0.000004,0.000035,0.000166, $ 0.000584,0.001346,0.002471,0.001372,-0.009090,-0.008982, $ 0.000062,0.000124,0.000124,0.000124,0.000124,0.000124, $ 0.000004,0.000035,0.000166,0.000584,0.001346,0.002471, $ 0.001372,-0.009090,-0.008982,0.000124,0.000062,0.000124, $ 0.000124,0.000124,0.000124,0.000004,0.000035,0.000166, $ 0.000584,0.001346,0.002471,0.001372,-0.009090,-0.008981, $ 0.000124,0.000124,0.000062,0.000124,0.000124,0.000124, $ 0.000004,0.000035,0.000166,0.000584,0.001346,0.002471, $ 0.001372,-0.009090,-0.008981,0.000124,0.000124,0.000124, $ 0.000062,0.000124,0.000124,0.000004,0.000035,0.000166, $ 0.000584,0.001346,0.002471,0.001372,-0.009090,-0.008981, $ 0.000124,0.000124,0.000124,0.000124,0.000062,0.000124, $ 0.000004,0.000035,0.000166,0.000584,0.001346,0.002471, $ 0.001372,-0.009090,-0.008982,0.000124,0.000124,0.000124, $ 0.000124,0.000124,0.000062/ c c Create initial guess for density matrix in global array c call ga_zero(g_dens) call ga_zero(g_counter) one = 1 ld = 15 c c Correct for a factor of two along the diagonal c do i = 1, ld atdens(i,i) = 2.0d00*atdens(i,i) end do itask = nga_read_inc(g_counter,one,one) do while(itask.lt.natom) ioff = itask*15 lo(1) = ioff+1 lo(2) = ioff+1 hi(1) = ioff+15 hi(2) = ioff+15 call nga_put(g_dens,lo,hi,atdens,ld) itask = nga_read_inc(g_counter,one,one) end do call ga_sync return end c subroutine setarrays implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" integer one, two, dims(2) logical status one = 1 two = 2 g_counter = ga_create_handle() call ga_set_data(g_counter,one,one,MT_INT) status = ga_allocate(g_counter) call ga_zero(g_counter) dims(1) = nbfn dims(2) = nbfn g_dens = ga_create_handle() call ga_set_data(g_dens, two, dims, MT_DBL) status = ga_allocate(g_dens) call ga_zero(g_dens) g_schwarz = ga_create_handle() call ga_set_data(g_schwarz, two, dims, MT_DBL) status = ga_allocate(g_schwarz) call ga_zero(g_schwarz) g_fock = ga_create_handle() call ga_set_data(g_fock, two, dims, MT_DBL) status = ga_allocate(g_fock) call ga_zero(g_fock) g_tfock = ga_create_handle() call ga_set_data(g_tfock, two, dims, MT_DBL) status = ga_allocate(g_tfock) call ga_zero(g_tfock) g_work = ga_create_handle() call ga_set_data(g_work, two, dims, MT_DBL) status = ga_allocate(g_work) call ga_zero(g_work) #ifdef CHECKPOINT_SCF arraylist(1) = g_counter arraylist(2) = g_dens arraylist(3) = g_schwarz arraylist(4) = g_fock arraylist(5) = g_work write (6,*) arraylist(1),arraylist(2),arraylist(3) call ga_checkpoint_arrays(arraylist,numarrays) #endif g_ident = ga_create_handle() call ga_set_data(g_ident, two, dims, MT_DBL) status = ga_allocate(g_ident) call ga_zero(g_ident) g_orbs = ga_create_handle() call ga_set_data(g_orbs, two, dims, MT_DBL) status = ga_allocate(g_orbs) call ga_zero(g_orbs) return end subroutine closearrays implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" logical status c status = ga_destroy(g_counter) status = ga_destroy(g_dens) status = ga_destroy(g_schwarz) status = ga_destroy(g_fock) status = ga_destroy(g_tfock) status = ga_destroy(g_work) status = ga_destroy(g_ident) status = ga_destroy(g_orbs) c return end c subroutine makoverlap implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" integer me, lo(2), hi(2), ptr, ld(2) integer ld1, ld2 me = ga_nodeid() call nga_distribution(g_ident, me, lo, hi) call nga_access(g_ident, lo, hi, ptr, ld) ld1 = hi(1) - lo(1) + 1 ld2 = hi(2) - lo(2) + 1 call setoverlap(dbl_mb(ptr),lo,hi,ld1,ld2) call nga_release(g_ident) return end c subroutine setoverlap(a,lo,hi,ld1,ld2) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" integer lo(2), hi(2) integer ld1, ld2, ii, jj double precision a(ld1,ld2) do i = 1, ld1 ii = i + lo(1) - 1 do j = 1, ld2 jj = j + lo(2) - 1 #if USE_TRANSFORM if (ii.eq.jj) then a(i,j) = 1.0d00 else a(i,j) = 0.0d00 endif #else a(i,j) = s(ii,jj) #endif end do end do return end c subroutine print_ga_block(g_a) implicit double precision(a-h,o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" integer lo(2), hi(2), ptr, ld1, ld2 c me = ga_nodeid() call nga_distribution(g_a, me, lo, hi) ld1 = hi(1) - lo(1) + 1 ld2 = hi(2) - lo(2) + 1 call nga_access(g_a, lo, hi, ptr, ld) call dump_chunk(dbl_mb(ptr),ld1,ld2) call nga_release(g_a) c return end c subroutine print_ga_block_ij(g_a,tlo) implicit double precision(a-h,o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" integer lo(2), hi(2), ptr, ld1, ld2 c me = ga_nodeid() call nga_distribution(g_a, me, lo, hi) ld1 = hi(1) - lo(1) + 1 ld2 = hi(2) - lo(2) + 1 call nga_access(g_a, tlo, hi, ptr, ld) call dump_chunk(dbl_mb(ptr),ld1,ld2) call nga_release(g_a) c return end c subroutine dump_chunk(a,ld1,ld2) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" integer ld1, ld2 double precision a(ld1, ld2) do i = 1, min(10,ld1) write(6,100) (a(i,j), j = 1, min(10,ld2)) end do write(6,*) 100 format(10f10.4) return end c double precision function contract_matrices(g_a,g_b) implicit double precision(a-h,o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "tcgmsg.fh" integer lo(2), hi(2), ptr_a, ptr_b, ld, ld1, ld2 double precision a(ichunk,ichunk),b(ichunk,ichunk) double precision value logical dotask c c evalute sum_ij a_ij*b_ij c value = 0.0d00 call ga_zero(g_counter) dotask = next_chunk(lo,hi) ld = ichunk do while (dotask) call nga_get(g_a,lo,hi,a,ld) call nga_get(g_b,lo,hi,b,ld) do j = 1, hi(2)-lo(2)+1 do i = 1, hi(1)-lo(1)+1 value = value + a(i,j)*b(i,j) end do end do dotask = next_chunk(lo,hi) end do call ga_dgop(3,value,1,'+') contract_matrices=value c return end ga-5.9.2/global/examples/scf/input.F000066400000000000000000000051721500715745200172300ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine input #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" c....................................................................... c Input configuration from an XYZ format file call be.inpt and set up c initial data structures. Atomic numbers from XYZ file are ignored and c an atomic number of 4 (Beryllium) is used instead. c....................................................................... c c initialize variables c natom = 0 do i = 1, maxatom ax(i) = 0.0d00 ay(i) = 0.0d00 az(i) = 0.0d00 end do c if (ga_nodeid().eq.0) then open(2,file='be.inpt',status='old') read(2,*) natom read(2,*) c c Read in coordinates c do i = 1, natom read(2,*) j,ax(i),ay(i),az(i) end do close(2) endif call ga_igop(1,natom,1,'+') call ga_dgop(2,ax,natom,'+') call ga_dgop(3,ay,natom,'+') call ga_dgop(4,az,natom,'+') c c Set up s-function centers and nuclear charges c ifcnt = 1 do i = 1, natom q(i) = 4.0d00 c expnt(ifcnt) = 1741.0d00 expnt(ifcnt+1) = 262.1d00 expnt(ifcnt+2) = 60.33d00 expnt(ifcnt+3) = 17.62d00 expnt(ifcnt+4) = 5.933d00 expnt(ifcnt+5) = 2.185d00 expnt(ifcnt+6) = 0.859d00 expnt(ifcnt+7) = 0.1806d00 expnt(ifcnt+8) = 0.05835d00 expnt(ifcnt+9) = 0.3d00 expnt(ifcnt+10) = 0.3d00 expnt(ifcnt+11) = 0.3d00 expnt(ifcnt+12) = 0.3d00 expnt(ifcnt+13) = 0.3d00 expnt(ifcnt+14) = 0.3d00 c do j = 1, 15 x(ifcnt) = ax(i) y(ifcnt) = ay(i) z(ifcnt) = az(i) if (j.eq.10) then x(ifcnt) = x(ifcnt) + 1.6d00 endif if (j.eq.11) then x(ifcnt) = x(ifcnt) - 1.6d00 endif if (j.eq.12) then y(ifcnt) = y(ifcnt) + 1.6d00 endif if (j.eq.13) then y(ifcnt) = y(ifcnt) - 1.6d00 endif if (j.eq.14) then z(ifcnt) = z(ifcnt) + 1.6d00 endif if (j.eq.15) then z(ifcnt) = z(ifcnt) - 1.6d00 endif ifcnt = ifcnt + 1 end do end do c c evaluate repulsion energy c enrep = 0.0d00 do i = 1, natom do j = i+1, natom r = sqrt((ax(i)-ax(j))**2 + (ay(i)-ay(j))**2 + + (az(i)-az(j))**2) enrep = enrep + q(i)*q(j)/r end do end do nocc = 2*natom nbfn = 15*natom nnbfn = nbfn*(nbfn+1)/2 return end ga-5.9.2/global/examples/scf/integ.F000066400000000000000000000057161500715745200172030ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif double precision function exprjh(x) C$Id: integ.F,v 1.1 2005-03-08 23:58:03 d3g293 Exp $ double precision x c c dumb solution to underflow problems on sun c if (x.lt.-37.0d0) then exprjh = 0.0d0 else exprjh = exp(x) endif end subroutine setfm implicit double precision (a-h,o-z) common/values/fm(2001,5),rdelta,delta,delo2 dimension t(2001),et(2001) c c initalize common block for computation of f0 by recursion down c from f200 c delta=28.0d0/2000.0d0 delo2=delta*0.5d0 rdelta=1.0d0/delta maxm=4 do 10 i=1,2001 tt=delta*dble(i-1) et(i)=exprjh(-tt) t(i)=2.0d0*tt fm(i,maxm+1)=0.0d0 10 continue do 20 i=200,maxm,-1 rr=1.0d0/dble(2*i+1) do 30 ii=1,2001 fm(ii,maxm+1)=(et(ii)+t(ii)*fm(ii,maxm+1))*rr 30 continue 20 continue do 40 i=maxm,1,-1 rr=1.0d0/dble(2*i-1) do 50 ii=1,2001 fm(ii,i)=(et(ii)+t(ii)*fm(ii,i+1))*rr 50 continue 40 continue c end subroutine f0(value, t) implicit real*8 (a-h,o-z) common/values/fm(2001,5),rdelta,delta,delo2 parameter(fac0=0.88622692545276d0, $ rhalf=0.5d0,rthird=0.3333333333333333d0,rquart=0.25d0) data t0/28.d0/ c c computes f0 to a relative accuracy of better than 4.e-13 for all t. c uses 4th order taylor expansion on grid out to t=28.0 c asymptotic expansion accurate for t greater than 28 c if(t.ge.t0) then value = fac0 / sqrt(t) else n = idint((t+delo2)*rdelta) x = delta*dble(n)-t n = n+1 value = fm(n,1)+x*(fm(n,2)+rhalf*x*(fm(n,3)+ $ rthird*x*(fm(n,4)+rquart*x*fm(n,5)))) endif c end subroutine addin(g, i, j, k, l, fock, dens, iky) implicit double precision (a-h, o-z) dimension fock(*), dens(*), iky(*) c c add (ij|kl) into the fock matrix c gg = g g2 = gg+gg g4 = g2+g2 ik = iky(i) + k il = iky(i) + l ij = iky(i) + j jk = iky(max(j,k)) + min(j,k) jl = iky(max(j,l)) + min(j,l) kl = iky(k) + l aij = g4*dens(kl)+fock(ij) fock(kl) = g4*dens(ij)+fock(kl) fock(ij) = aij gil=gg if(i.eq.k.or.j.eq.l) gg = g2 if(j.eq.k) gil = g2 ajk = fock(jk) - gil*dens(il) ail = fock(il) - gil*dens(jk) aik = fock(ik) - gg*dens(jl) fock(jl) = fock(jl) - gg*dens(ik) fock(jk) = ajk fock(il) = ail fock(ik) = aik c end subroutine dfill(n,val,a,ia) implicit real*8 (a-h,o-z) dimension a(*) c c initialise double precision array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end ga-5.9.2/global/examples/scf/output.F000066400000000000000000000052041500715745200174250ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif subroutine output (z,rowlow,rowhi,collow,colhi,rowdim,coldim, $ nctl) c....................................................................... c output prints a real*8 matrix in formatted form with numbered rows c and columns. the input is as follows; c matrix(*,*).........matrix to be output c rowlow..............row number at which output is to begin c rowhi...............row number at which output is to end c collow..............column number at which output is to begin c colhi...............column number at which output is to end c rowdim..............row dimension of matrix(*,*) c coldim..............column dimension of matrix(*,*) c nctl................carriage control flag; 1 for single space c 2 for double space c 3 for triple space c the parameters that follow matrix are all of type integer*4. the c program is set up to handle 5 columns/page with a 1p5d24.15 format for c the columns. if a different number of columns is required, change c formats 1000 and 2000, and initialize kcol with the new number of c columns. c author; nelson h.f. beebe, quantum theory project, university of c florida, gainesville c....................................................................... C$Id: output.F,v 1.1 2005/03/08 23:58:03 d3g293 Exp $ implicit double precision (a-h,o-z) #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" integer rowlow,rowhi,collow,colhi,rowdim,coldim,begin,kcol dimension z(rowdim,coldim) dimension asa(3) data column/8hcolumn /,asa/8h ,8h00000000 , 1 8h-------- /,blank/8h / data kcol/8/ data zero/0.d00/ if (ga_nodeid().ne.0) return do 11 i=rowlow,rowhi do 10 j=collow,colhi if (z(i,j).ne.zero) go to 15 10 continue 11 continue write (6,3000) 3000 format (/' zero matrix'/) go to 3 15 continue if (rowhi.lt.rowlow) go to 3 if (colhi.lt.collow) go to 3 last = min0(colhi,collow+kcol-1) do 2 begin = collow,colhi,kcol write (6,1000) (column,i,i = begin,last) do 1 k = rowlow,rowhi do 4 i=begin,last if (z(k,i).ne.zero) go to 5 4 continue go to 1 c 5 write (6,2000) k,(z(k,i), i = begin,last) 1 continue last = min0(last+kcol,colhi) 2 continue 3 return 1000 format (/11x,7(a3,i3,3x),(a3,i3)) 2000 format ('row',i4,1x,8f9.4) end ga-5.9.2/global/examples/scf/scf.F000066400000000000000000001131311500715745200166370ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif program scf C$Id: scf.F,v 1.18 2007/03/23 19:24:36 d3g293 Exp $ implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" c integer*8 nints, maxint !cste c c CAUTION: integer precision requirements c nints, maxint, etc. are proportional to the number of basis functions c to the fourth power! 216**4 is greater than the largest number c that can be represented as a 32-bit signed interger, so 64-bit c arithmetic is needed to count integrals when calculating more than c 14 Be atoms with 15 basis functions each. Since integrals are counted c over all iterations, 18 iterations with 7 atoms can result in precision c problems. Note that the wave function could be calculated correctly c for much larger basis sets without 64-bit integers because the required c indexing is usually proportional to nbfn**2, which is good to 46,340 c basis functions, except that the task counter runs as (nbfn/ichunk)**4, c so with ichunk = 10, 32-bit integers yield correct wavefunctions out to c 2145 basis functions (maxatom=143), or 4290 (maxatom=286) with ichunk = 20, ... c c This warning applies to the Global Arrays implementation as well! c functions of special concern are ga_igop and nga_read_inc. c #define USE_TRANSFORM 1 integer heap, stack data tinit, tonel, ttwoel, tdiag, tdens, tprint /6*0.0d0/ data eone, etwo, energy, deltad /4*0.0d0/ c c initalize the parallel message passing environment c #include "mp3.fh" call ga_initialize() c c Allocate memory c heap = 32000000 stack = 32000000 if (.not.ma_init(MT_DBL, stack, heap)) + call ga_error("ma_init failed",-1) call flush(6) me = ga_nodeid() nproc = ga_nnodes() c c initialize a bunch of stuff and initial density matrix c rjunk = timer() c c get input from file be.inpt c call input c c create and allocate global arrays c call setarrays if (ga_nodeid().eq.0) write(6,*) 'bytes of memory used by node 0:' + ,ga_inquire_memory() call ininrm c c create initial guess for density matrix by using single atom c densities c call denges tinit = timer() #if USE_TRANSFORM c c make initial orthogonal orbital set for solution method using c similarity transform c call makeob #endif c c make info for sparsity test c call makesz(schwmax) c c print preliminary data before any long compute segments start if (ga_nodeid().eq.0) call flush(6) c c *** iterate *** c do 10 iter = 1, mxiter c c make the one particle contribution to the fock matrix c and get the partial contribution to the energy c call oneel(schwmax, eone) tonel = tonel + timer() c c compute the two particle contributions to the fock matrix and c get the total energy. c call twoel(schwmax, etwo) ttwoel = ttwoel + timer() c c Diagonalize the fock matrix. The diagonalizers used in this c subroutine are actually sequential, not parallel. c call diagon(tester,iter) tdiag = tdiag + timer() c c make the new density matrix in g_work from orbitals in g_orbs, c compute the norm of the change in the density matrix and c then update the density matrix in g_dens with damping. c call makden deltad = dendif() if (iter.eq.1) then scale = 0.0d0 else if (iter .le. 5) then if (nbfn .gt. 60) then scale = 0.5d0 else scale = 0.0d0 endif else scale = 0.0d0 endif call damp(scale) tdens = tdens + timer() c c add up energy and print out convergence information c if (me.eq.0) then energy = enrep + eone + etwo call prnout(iter, energy, deltad, tester) tprint = tprint + timer() endif c c if converged then exit iteration loop c if (deltad .lt. tol) goto 20 call ga_igop(9, icut4, 1, '+') !cste if(icut4 .eq. 0) then !cste c something has gone wrong--print what you know and quit. write(6,*) 'no two-electron integrals computed!' !cste goto 20 !cste endif !cste 10 continue iter = iter - 1 !cste if(me.eq.0) $ write(6,*) ' SCF failed to converge in ', iter, ' iters' c...v....1....v....2....v....3....v....4....v....5....v....6....v....7.. c c finished ... print out eigenvalues and occupied orbitals c 20 continue call ga_igop(6, icut1, 1, '+') call ga_igop(7, icut2, 1, '+') call ga_igop(8, icut3, 1, '+') if (me.eq.0) then c c print out timing information c call prnfin(energy) write(6,1) tinit, tonel, ttwoel, tdiag, tdens, tprint, $ nproc 1 format(/5x,' init ',4x,' onel ',4x,' twoel ',4x,' diag ',4x, $ ' dens print ncpu'/ $ 5x,'------',4x,'------',4x,'-------',4x,'------',4x, $ '------ ------ ------'/ $ 2f10.2,f11.2,3f10.2, i7/) totsec = tinit+tonel+ttwoel+tdiag+tdens+tprint write(6,*)'elapsed time in seconds ',totsec c c print out information on # integrals evaluated each iteration c nints = icut1+icut2+icut3 frac = dble(icut3)/dble(nints) write(6,2) icut1, icut2, icut3, nints, frac 2 format(/'No. of integrals screened or computed (all iters) ' $ /'-------------------------------------'/ $ /1x,' failed #ij test failed #kl test', $ ' #compute #total', $ ' fraction', $ /1x,' --------------- ---------------', $ ' --------------- ---------------', $ ' --------', $ /1x,4(1x,i15),f9.6) maxint = nbfn !cste maxint = maxint**4 * iter !cste if(nints .ne. maxint) then !cste write(6,*)'Inconsistent number of integrals, should be ', !cste $ maxint !cste write(6,*)'Note: largest 32-bit integer is 2,147,483,647' !cste write(6,*)'Probably due to insufficient integer precision in GA.'!cste endif !cste #ifdef MSG_COMMS_MPI call ga_print_stats() #else call stats #endif endif c call closearrays call ga_terminate #ifdef MSG_COMMS_MPI call mpi_finalize #else call pend #endif c end c subroutine makesz(schwmax) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" dimension work(ichunk,ichunk) integer lo(2),hi(2),i,j,iloc,jloc,ld logical dotask, next_chunk c c schwarz(ij) = (ij|ij) for sparsity test c icut1 = 0 icut2 = 0 icut3 = 0 c call ga_zero(g_schwarz) call ga_zero(g_counter) schwmax = 0.0d0 dotask = next_chunk(lo,hi) ld = ichunk do while (dotask) do i = lo(1), hi(1) iloc = i - lo(1) + 1 do j = lo(2), hi(2) jloc = j - lo(2) + 1 call g(gg,i,j,i,j) work(iloc,jloc) = sqrt(gg) schwmax = max(schwmax, work(iloc,jloc)) end do end do call nga_put(g_schwarz,lo,hi,work,ld) dotask = next_chunk(lo,hi) end do call ga_dgop(11,schwmax,1,'max') c return end c subroutine ininrm implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" integer*8 maxint c c write a little welcome message c maxint = nbfn maxint = maxint**4 if (ga_nodeid().eq.0) then write(6,1) natom, nocc, nbfn, maxint, tol, ichunk 1 format(/' Example Direct Self Consistent Field Program '/ $ ' -------------------------------------------- '// $ ' no. of atoms .............. ',i5/ $ ' no. of occupied orbitals .. ',i5/ $ ' no. of basis functions .... ',i5/ $ ' basis functions^4 ' ,i15/ $ ' convergence threshold ..... ',1pd9.2/ $ ' chunk size .................',i5) write(6,*) !cste call flush(6) !cste endif !cste c c generate normalisation coefficients for the basis functions c and the index array iky c do 10 i = 1, nbfn iky(i) = i*(i-1)/2 10 continue c do 20 i = 1, nbfn rnorm(i) = (expnt(i)*2.0d0/pi)**0.75d0 20 continue c c initialize common for computing f0 c call setfm c end double precision function h(i,j) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" cvd$r novector cvd$r noconcur c c generate the one particle hamiltonian matrix element c over the normalized primitive 1s functions i and j c f0val = 0.0d0 sum = 0.0d0 rab2 = (x(i)-x(j))**2 + (y(i)-y(j))**2 + (z(i)-z(j))**2 facij = expnt(i)*expnt(j)/(expnt(i)+expnt(j)) expij = exprjh(-facij*rab2) repij = (2.0d0*pi/(expnt(i)+expnt(j))) * expij c c first do the nuclear attraction integrals c do 10 iat = 1, natom xp = (x(i)*expnt(i) + x(j)*expnt(j))/(expnt(i)+expnt(j)) yp = (y(i)*expnt(i) + y(j)*expnt(j))/(expnt(i)+expnt(j)) zp = (z(i)*expnt(i) + z(j)*expnt(j))/(expnt(i)+expnt(j)) rpc2 = (xp-ax(iat))**2 + (yp-ay(iat))**2 + (zp-az(iat))**2 c call f0(f0val, (expnt(i)+expnt(j))*rpc2) sum = sum - repij * q(iat) * f0val 10 continue c c add on the kinetic energy term c sum = sum + facij*(3.0d0-2.0d0*facij*rab2) * $ (pi/(expnt(i)+expnt(j)))**1.5d0 * expij c c finally multiply by the normalization constants c h = sum * rnorm(i) * rnorm(j) c end double precision function s(i,j) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" c c generate the overlap matrix element between the normalized c primitve gaussian 1s functions i and j c rab2 = (x(i)-x(j))**2 + (y(i)-y(j))**2 + (z(i)-z(j))**2 facij = expnt(i)*expnt(j)/(expnt(i)+expnt(j)) s = (pi/(expnt(i)+expnt(j)))**1.5d0 * exprjh(-facij*rab2) * $ rnorm(i)*rnorm(j) c end subroutine makden implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" c dimension work(maxnbfn,maxnbfn), torbs(maxnbfn,maxnbfn) dimension work(ichunk,ichunk), orbsi(ichunk,maxnbfn) dimension orbsj(ichunk,maxnbfn) integer lo(2), hi(2), tlo(2), thi(2), i, j, iloc, jloc, ld logical dotask, next_chunk c c generate density matrix from orbitals in g_orbs. the first c nocc orbitals are doubly occupied. c call ga_zero(g_counter) dotask = next_chunk(lo,hi) ld = ichunk do while (dotask) tlo(1) = lo(1) thi(1) = hi(1) tlo(2) = 1 thi(2) = nocc call nga_get(g_orbs,tlo,thi,orbsi,ld) tlo(1) = lo(2) thi(1) = hi(2) call nga_get(g_orbs,tlo,thi,orbsj,ld) do i = lo(1), hi(1) iloc = i - lo(1) + 1 do j = lo(2), hi(2) jloc = j - lo(2) + 1 p = 0.0d00 do k = 1, nocc p = p + orbsi(iloc,k)*orbsj(jloc,k) end do work(iloc,jloc) = 2.0d00*p end do end do call nga_put(g_work,lo,hi,work,ld) dotask = next_chunk(lo,hi) end do return end c subroutine oneel(schwmax, eone) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" integer lo(2), hi(2), i, j, iloc, jloc, ld dimension work(ichunk,ichunk),tfock(ichunk,ichunk) logical dotask, next_chunk c c fill in the one-electron part of the fock matrix and c compute the one-electron energy contribution c me = ga_nodeid() nproc = ga_nnodes() c call ga_zero(g_counter) dotask = next_chunk(lo,hi) ld = ichunk do while (dotask) call nga_get(g_schwarz,lo,hi,work,ld) do j = lo(2), hi(2) jloc = j - lo(2) + 1 do i = lo(1), hi(1) iloc = i - lo(1) + 1 tfock(iloc,jloc) = 0.0d00 if (work(iloc,jloc)*schwmax.gt.tol2e) + tfock(iloc,jloc) = h(i,j) end do end do call nga_put(g_fock,lo,hi,tfock,ld) dotask = next_chunk(lo,hi) end do eone = 0.5d00*contract_matrices(g_fock,g_dens) c end #if 0 integer function nxtask(nproc) parameter (ichunk = 10) save icount, nleft data nleft, icount /0, 0/ c c wrapper round nxtval() to increase granularity c and thus reduce no. of requests to shared counter c if(nproc.gt.0) then if(nleft.eq.0) then #ifdef MSG_COMMS_MPI icount = nxtval_ga(nproc) * ichunk #else icount = nxtval(nproc) * ichunk #endif nleft = ichunk endif nxtask = icount icount = icount + 1 nleft = nleft -1 else nleft = 0 nxtask = 0 #ifdef MSG_COMMS_MPI junk = nxtval_ga(nproc) #else junk = nxtval(nproc) #endif endif c c following does dumb static load balancing c c$$$ if(nproc.gt.0) then c$$$ if (nleft .eq. 0) then c$$$ icount = ga_nodeid() c$$$ nleft = 1 c$$$ endif c$$$ nxtask = icount c$$$ icount = icount + ga_nnodes() c$$$ else c$$$ nleft = 0 c$$$ nxtask = 0 c$$$ endif end #endif c logical function next_chunk(lo,hi) #include "cscf.h" integer one parameter (one = 1) integer imax, lo(2), hi(2), ilo, jlo itask = nga_read_inc(g_counter,one,one) imax = nbfn/ichunk if (nbfn - ichunk*imax.gt.0) imax = imax + 1 if (itask.lt.imax*imax) then ilo = mod(itask,imax) jlo = (itask-ilo)/imax lo(1) = ilo*ichunk + 1 lo(2) = jlo*ichunk + 1 hi(1) = min((ilo+1)*ichunk,nbfn) hi(2) = min((jlo+1)*ichunk,nbfn) next_chunk = .true. else next_chunk = .false. endif return end c logical function next_4chunk(lo,hi,ilo,jlo,klo,llo) #include "cscf.h" integer one parameter (one = 1) integer*8 imax, itask, itmp !cste integer lo(4), hi(4), ilo, jlo, klo, llo !cste c itask = nga_read_inc(g_counter,one,one) imax = nbfn/ichunk if (nbfn - ichunk*imax.gt.0) imax = imax + 1 if (itask. lt. 0) then !cste write(6,*) 'next_4chunk: itask negative:',itask, !cste * ' imax:',imax,' nbfn:',nbfn,' ichunk:',ichunk!cste write(6,*) 'probable GA integer precision problem if ' !cste * ,'imax^4 > 2^31' !cste call flush(6) !cste stop 'next_4chunk' !cste end if !cste if (itask.lt.imax**4) then ilo = mod(itask,imax) itmp = (itask - ilo)/imax jlo = mod(itmp,imax) itmp = (itmp - jlo)/imax klo = mod(itmp,imax) llo = (itmp - klo)/imax lo(1) = ilo*ichunk + 1 lo(2) = jlo*ichunk + 1 lo(3) = klo*ichunk + 1 lo(4) = llo*ichunk + 1 hi(1) = min((ilo+1)*ichunk,nbfn) hi(2) = min((jlo+1)*ichunk,nbfn) hi(3) = min((klo+1)*ichunk,nbfn) hi(4) = min((llo+1)*ichunk,nbfn) next_4chunk = .true. else next_4chunk = .false. endif return end c subroutine clean_chunk(chunk) #include "cscf.h" double precision chunk(ichunk,ichunk) integer i,j do j = 1, ichunk do i = 1, ichunk chunk(i,j) = 0.0d00 end do end do return end c subroutine twoel(schwmax, etwo) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" double precision f_ij(ichunk,ichunk),d_kl(ichunk,ichunk) double precision f_ik(ichunk,ichunk),d_jl(ichunk,ichunk) double precision s_ij(ichunk,ichunk),s_kl(ichunk,ichunk) double precision schwmax, one cste integer nproc !cste integer*8 ijkls, ijcnt,klcnt,ijklcnt !cste integer lo(4),hi(4),lo_ik(2),hi_ik(2),lo_jl(2),hi_jl(2) !cste integer i,j,k,l,iloc,jloc,kloc,lloc,ld,ich,it,jt,kt,lt !cste logical dotask, next_4chunk c c add in the two-electron contribution to the fock matrix c cste nproc = ga_nnodes() one = 1.0d00 ijcnt = icut1 !cste klcnt = icut2 !cste ijklcnt = icut3 !cste c call ga_zero(g_counter) ld = maxnbfn ich = ichunk dotask = next_4chunk(lo,hi,it,jt,kt,lt) itask = 0 cste ijkls = 0 !cste do while (dotask) cste ijkl=(hi(1)-lo(1)+1)*(hi(2)-lo(2)+1)* !cste cste * (hi(3)-lo(3)+1)*(hi(4)-lo(4)+1) !cste cste ijkls = ijkls + ijkl !cste cste write(6,*)itask,lo,hi,ijkl,ijkls !cste lo_ik(1) = lo(1) lo_ik(2) = lo(3) hi_ik(1) = hi(1) hi_ik(2) = hi(3) lo_jl(1) = lo(2) lo_jl(2) = lo(4) hi_jl(1) = hi(2) hi_jl(2) = hi(4) call nga_get(g_schwarz,lo,hi,s_ij,ich) call nga_get(g_schwarz,lo(3),hi(3),s_kl,ich) call nga_get(g_dens,lo(3),hi(3),d_kl,ich) call nga_get(g_dens,lo_jl,hi_jl,d_jl,ich) itask = itask + 1 call clean_chunk(f_ij) call clean_chunk(f_ik) do i = lo(1), hi(1) iloc = i-lo(1) + 1 do j = lo(2), hi(2) jloc = j-lo(2) + 1 if (s_ij(iloc,jloc)*schwmax .lt. tol2e) then icut1 = icut1 + (hi(3)-lo(3)+1)*(hi(4)-lo(4)+1) !cste else do k = lo(3), hi(3) kloc = k-lo(3) + 1 do l = lo(4), hi(4) lloc = l-lo(4) + 1 if (s_ij(iloc,jloc)*s_kl(kloc,lloc).lt.tol2e) then icut2 = icut2 + 1 else call g(gg, i, j, k, l) f_ij(iloc,jloc) = f_ij(iloc,jloc) + + gg*d_kl(kloc,lloc) f_ik(iloc,kloc) = f_ik(iloc,kloc) + - 0.5d00*gg*d_jl(jloc,lloc) icut3 = icut3 + 1 endif end do end do endif end do end do call nga_acc(g_fock,lo,hi,f_ij,ich,one) call nga_acc(g_fock,lo_ik,hi_ik,f_ik,ich,one) dotask = next_4chunk(lo,hi,it,jt,kt,lt) end do etwo = 0.5d00*contract_matrices(g_fock,g_dens) ijcnt = icut1 - ijcnt klcnt = icut2 - klcnt ijklcnt = icut3 - ijklcnt cste write(6,*) 'node ', ga_nodeid(), ijcnt, klcnt, ijklcnt !cste cste * ,icut1,icut2,icut3 !cste cste call flush(6) !cste icut4 = icut3 !cste if (icut3 .gt. 0) return !cste c c no integrals may be calculated if there is no work for c this node (ichunk too big), or, something is wrong c write(6,*) 'no two-electron integrals computed by node', !cste * ga_nodeid() !cste call flush(6) !cste return cste stop 'twoel computed no integrals' !cste end c subroutine damp(fac) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" c c create damped density matrix as a linear combination of c old density matrix and density matrix formed from new orbitals c ofac = 1.0d0 - fac call ga_add(fac,g_dens,ofac,g_work,g_dens) return end c subroutine prnout(iter, energy, deltad, tester) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" c c printout results of each iteration c if (ga_nodeid().ne.0) return write(6,1) iter, energy, deltad, tester call flush(6) 1 format(' iter=',i3,', energy=',f15.8,', deltad=',1pd9.2, $ ', deltaf=',d9.2) return end c double precision function dendif() implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" double precision xdiff dimension dens_c(ichunk,ichunk),work_c(ichunk,ichunk) integer lo(2), hi(2), i, j, ld logical dotask, next_chunk c c compute largest change in density matrix elements c denmax = 0.0d0 call ga_zero(g_counter) dotask = next_chunk(lo,hi) ld = ichunk do while(dotask) call nga_get(g_dens,lo,hi,dens_c,ld) call nga_get(g_work,lo,hi,work_c,ld) do j = 1, hi(2)-lo(2)+1 do i = 1, hi(1)-lo(1)+1 xdiff = abs(dens_c(i,j)-work_c(i,j)) if (xdiff.gt.denmax) denmax = xdiff end do end do dotask = next_chunk(lo,hi) end do call ga_dgop(1,denmax,1,'max') dendif = denmax return end c double precision function testfock() implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" double precision xmax, xtmp dimension work(ichunk,ichunk) integer lo(2), hi(2), i, j, iloc, jloc, ld logical dotask, next_chunk c c compute largest change in density matrix elements c xmax = 0.0d0 call ga_zero(g_counter) dotask = next_chunk(lo,hi) ld = ichunk do while(dotask) call nga_get(g_fock,lo,hi,work,ld) do j = lo(2), hi(2) jloc = j - lo(2) + 1 do i = lo(1), hi(1) iloc = i - lo(1) + 1 if (i.ne.j) then xtmp = abs(work(iloc,jloc)) if (xtmp.gt.xmax) xmax = xtmp endif end do end do dotask = next_chunk(lo,hi) end do call ga_dgop(1,xmax,1,'max') testfock = xmax return end c subroutine shiftfock(shift) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" double precision shift dimension work(ichunk,ichunk) integer lo(2), hi(2), i, j, iloc, jloc, ld, icnt logical dotask, next_chunk c c compute largest change in density matrix elements c call ga_zero(g_counter) dotask = next_chunk(lo,hi) ld = ichunk do while(dotask) call nga_get(g_fock,lo,hi,work,ld) icnt = 0 do j = lo(2), hi(2) jloc = j - lo(2) + 1 do i = lo(1), hi(1) iloc = i - lo(1) + 1 if (i.eq.j.and.i.gt.nocc) then work(iloc,jloc) = work(iloc,jloc) + shift icnt = icnt + 1 endif end do end do if (icnt.gt.0) call nga_put(g_fock,lo,hi,work,ld) dotask = next_chunk(lo,hi) end do return end c subroutine prnfin(energy) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" dimension orbs(maxnbfn, maxnbfn) integer lo(2),hi(2),ld c c printout final results c if (ga_nodeid().ne.0) return write(6,1) energy 1 format(//' final energy = ',f18.11//' eigenvalues') call output(eigv, 1, min(nbfn,nocc+5), 1, 1, nbfn, 1, 1) c return end subroutine g(value,i,j,k,l) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" c c compute the two electon integral (ij|kl) over normalized c primitive 1s gaussians c f0val = 0.0d0 rab2 = (x(i)-x(j))**2 + (y(i)-y(j))**2 + (z(i)-z(j))**2 rcd2 = (x(k)-x(l))**2 + (y(k)-y(l))**2 + (z(k)-z(l))**2 facij = expnt(i)*expnt(j)/(expnt(i)+expnt(j)) fackl = expnt(k)*expnt(l)/(expnt(k)+expnt(l)) exijkl = exprjh(- facij*rab2 - fackl*rcd2) denom = (expnt(i)+expnt(j))*(expnt(k)+expnt(l)) * $ sqrt(expnt(i)+expnt(j)+expnt(k)+expnt(l)) fac = (expnt(i)+expnt(j))*(expnt(k)+expnt(l)) / $ (expnt(i)+expnt(j)+expnt(k)+expnt(l)) c xp = (x(i)*expnt(i) + x(j)*expnt(j))/(expnt(i)+expnt(j)) yp = (y(i)*expnt(i) + y(j)*expnt(j))/(expnt(i)+expnt(j)) zp = (z(i)*expnt(i) + z(j)*expnt(j))/(expnt(i)+expnt(j)) xq = (x(k)*expnt(k) + x(l)*expnt(l))/(expnt(k)+expnt(l)) yq = (y(k)*expnt(k) + y(l)*expnt(l))/(expnt(k)+expnt(l)) zq = (z(k)*expnt(k) + z(l)*expnt(l))/(expnt(k)+expnt(l)) rpq2 = (xp-xq)**2 + (yp-yq)**2 + (zp-zq)**2 c call f0(f0val, fac*rpq2) value = (2.0d0 * pi**2.5d0 / denom) * exijkl * f0val * $ rnorm(i)*rnorm(j)*rnorm(k)*rnorm(l) return end c subroutine diagon(tester, iter) c subroutine diagon(fock, orbs, evals, work, tester, iter) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" double precision r_zero, r_one, shift, tester c #if USE_TRANSFORM c c use similarity transform to solve standard eigenvalue problem c (overlap matrix has been transformed out of the problem) c r_one = 1.0d00 r_zero = 0.0d00 call ga_dgemm('n','n',nbfn,nbfn,nbfn,r_one,g_fock,g_orbs, + r_zero,g_tfock) call ga_dgemm('t','n',nbfn,nbfn,nbfn,r_one,g_orbs,g_tfock, + r_zero,g_fock) tester = testfock() shift = 0.0d00 if (tester.gt.0.3d0) then shift = 0.3d0 else if (nbfn .gt. 60) then shift = 0.1d0 else shift = 0.0d0 endif endif if (iter.ge.2.and.shift.ne.0.0d00) then call shiftfock(shift) endif call ga_copy(g_orbs,g_tfock) call ga_diag_std_seq(g_fock, g_work, eigv) c c Back transform eigenvectors c call ga_dgemm('n','n',nbfn,nbfn,nbfn,r_one,g_tfock,g_work, + r_zero,g_orbs) if (iter.ge.2.and.shift.ne.0.0d00) then do 50 i = nocc+1, nbfn eigv(i) = eigv(i) - shift 50 continue endif #else c c Keep remaking overlap matrix since ga_diag_seq does not c guarantee that g_ident is preserved. c call makoverlap call ga_diag_seq(g_fock, g_ident, g_orbs, eigv) tester = 0.0d00 #endif return end c subroutine makeob implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" double precision work(ichunk,ichunk),orbs(ichunk,ichunk) double precision eval(maxnbfn) integer lo(2),hi(2),ld,me,i,j,iloc,jloc logical dotask, next_chunk c c generate set of orthonormal vectors by creating a random c symmetric matrix and solving associated generalized eigenvalue c problem using the correct overlap matrix. c me = ga_nodeid() call ga_zero(g_counter) dotask = next_chunk(lo,hi) ld = ichunk do while (dotask) do j = lo(2), hi(2) jloc = j - lo(2) + 1 do i = lo(1), hi(1) iloc = i - lo(1) + 1 work(iloc,jloc) = s(i,j) orbs(iloc,jloc) = drand(0) end do end do call nga_put(g_ident,lo,hi,work,ld) call nga_put(g_fock,lo,hi,orbs,ld) dotask = next_chunk(lo,hi) end do call ga_symmetrize(g_fock) call ga_diag_seq(g_fock, g_ident, g_orbs, eval) c return end c subroutine denges implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" c c Form guess density from superposition of atomic densities in the AO c basis set ... instead of doing the atomic SCF hardwire for this c small basis set for the Be atom. c integer one, itask, lo(2), hi(2), ld dimension atdens(15,15) data atdens/ $ 0.000002,0.000027,0.000129,0.000428,0.000950,0.001180, $ 0.000457,-0.000270,-0.000271,0.000004,0.000004,0.000004, $ 0.000004,0.000004,0.000004,0.000027,0.000102,0.000987, $ 0.003269,0.007254,0.009007,0.003492,-0.002099,-0.002108, $ 0.000035,0.000035,0.000035,0.000035,0.000035,0.000035, $ 0.000129,0.000987,0.002381,0.015766,0.034988,0.043433, $ 0.016835,-0.010038,-0.010082,0.000166,0.000166,0.000166, $ 0.000166,0.000166,0.000166,0.000428,0.003269,0.015766, $ 0.026100,0.115858,0.144064,0.055967,-0.035878,-0.035990, $ 0.000584,0.000584,0.000584,0.000584,0.000584,0.000584, $ 0.000950,0.007254,0.034988,0.115858,0.128586,0.320120, $ 0.124539,-0.083334,-0.083536,0.001346,0.001346,0.001346, $ 0.001346,0.001346,0.001346,0.001180,0.009007,0.043433, $ 0.144064,0.320120,0.201952,0.159935,-0.162762,-0.162267, $ 0.002471,0.002471,0.002471,0.002471,0.002471,0.002471, $ 0.000457,0.003492,0.016835,0.055967,0.124539,0.159935, $ 0.032378,-0.093780,-0.093202,0.001372,0.001372,0.001372, $ 0.001372,0.001372,0.001372,-0.000270,-0.002099,-0.010038, $ -0.035878,-0.083334,-0.162762,-0.093780,0.334488,0.660918, $ -0.009090,-0.009090,-0.009090,-0.009090,-0.009090,-0.009090, $ -0.000271,-0.002108,-0.010082,-0.035990,-0.083536,-0.162267, $ -0.093202,0.660918,0.326482,-0.008982,-0.008982,-0.008981, $ -0.008981,-0.008981,-0.008982,0.000004,0.000035,0.000166, $ 0.000584,0.001346,0.002471,0.001372,-0.009090,-0.008982, $ 0.000062,0.000124,0.000124,0.000124,0.000124,0.000124, $ 0.000004,0.000035,0.000166,0.000584,0.001346,0.002471, $ 0.001372,-0.009090,-0.008982,0.000124,0.000062,0.000124, $ 0.000124,0.000124,0.000124,0.000004,0.000035,0.000166, $ 0.000584,0.001346,0.002471,0.001372,-0.009090,-0.008981, $ 0.000124,0.000124,0.000062,0.000124,0.000124,0.000124, $ 0.000004,0.000035,0.000166,0.000584,0.001346,0.002471, $ 0.001372,-0.009090,-0.008981,0.000124,0.000124,0.000124, $ 0.000062,0.000124,0.000124,0.000004,0.000035,0.000166, $ 0.000584,0.001346,0.002471,0.001372,-0.009090,-0.008981, $ 0.000124,0.000124,0.000124,0.000124,0.000062,0.000124, $ 0.000004,0.000035,0.000166,0.000584,0.001346,0.002471, $ 0.001372,-0.009090,-0.008982,0.000124,0.000124,0.000124, $ 0.000124,0.000124,0.000062/ c c Create initial guess for density matrix in global array c call ga_zero(g_dens) call ga_zero(g_counter) one = 1 ld = 15 c c Correct for a factor of two along the diagonal c do i = 1, ld atdens(i,i) = 2.0d00*atdens(i,i) end do itask = nga_read_inc(g_counter,one,one) do while(itask.lt.natom) ioff = itask*15 lo(1) = ioff+1 lo(2) = ioff+1 hi(1) = ioff+15 hi(2) = ioff+15 call nga_put(g_dens,lo,hi,atdens,ld) itask = nga_read_inc(g_counter,one,one) end do call ga_sync return end c subroutine setarrays implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" integer one, two, dims(2) logical status one = 1 two = 2 g_counter = ga_create_handle() call ga_set_data(g_counter,one,one,MT_INT) status = ga_allocate(g_counter) call ga_zero(g_counter) dims(1) = nbfn dims(2) = nbfn g_dens = ga_create_handle() call ga_set_data(g_dens, two, dims, MT_DBL) status = ga_allocate(g_dens) call ga_zero(g_dens) g_schwarz = ga_create_handle() call ga_set_data(g_schwarz, two, dims, MT_DBL) status = ga_allocate(g_schwarz) call ga_zero(g_schwarz) g_fock = ga_create_handle() call ga_set_data(g_fock, two, dims, MT_DBL) status = ga_allocate(g_fock) call ga_zero(g_fock) g_tfock = ga_create_handle() call ga_set_data(g_tfock, two, dims, MT_DBL) status = ga_allocate(g_tfock) call ga_zero(g_tfock) g_work = ga_create_handle() call ga_set_data(g_work, two, dims, MT_DBL) status = ga_allocate(g_work) call ga_zero(g_work) g_ident = ga_create_handle() call ga_set_data(g_ident, two, dims, MT_DBL) status = ga_allocate(g_ident) call ga_zero(g_ident) g_orbs = ga_create_handle() call ga_set_data(g_orbs, two, dims, MT_DBL) status = ga_allocate(g_orbs) call ga_zero(g_orbs) return end subroutine closearrays implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" logical status c status = ga_destroy(g_counter) status = ga_destroy(g_dens) status = ga_destroy(g_schwarz) status = ga_destroy(g_fock) status = ga_destroy(g_tfock) status = ga_destroy(g_work) status = ga_destroy(g_ident) status = ga_destroy(g_orbs) c return end c subroutine makoverlap implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" integer me, lo(2), hi(2), ptr, ld(2) integer ld1, ld2 me = ga_nodeid() call nga_distribution(g_ident, me, lo, hi) call nga_access(g_ident, lo, hi, ptr, ld) ld1 = hi(1) - lo(1) + 1 ld2 = hi(2) - lo(2) + 1 call setoverlap(dbl_mb(ptr),lo,hi,ld1,ld2) call nga_release(g_ident) return end c subroutine setoverlap(a,lo,hi,ld1,ld2) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" integer lo(2), hi(2) integer ld1, ld2, ii, jj double precision a(ld1,ld2) do i = 1, ld1 ii = i + lo(1) - 1 do j = 1, ld2 jj = j + lo(2) - 1 #if USE_TRANSFORM if (ii.eq.jj) then a(i,j) = 1.0d00 else a(i,j) = 0.0d00 endif #else a(i,j) = s(ii,jj) #endif end do end do return end c subroutine print_ga_block(g_a) implicit double precision(a-h,o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" integer lo(2), hi(2), ptr, ld1, ld2 c me = ga_nodeid() call nga_distribution(g_a, me, lo, hi) ld1 = hi(1) - lo(1) + 1 ld2 = hi(2) - lo(2) + 1 call nga_access(g_a, lo, hi, ptr, ld) call dump_chunk(dbl_mb(ptr),ld1,ld2) call nga_release(g_a) c return end c subroutine print_ga_block_ij(g_a,tlo) implicit double precision(a-h,o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" integer lo(2), hi(2), ptr, ld1, ld2 c me = ga_nodeid() call nga_distribution(g_a, me, lo, hi) ld1 = hi(1) - lo(1) + 1 ld2 = hi(2) - lo(2) + 1 call nga_access(g_a, tlo, hi, ptr, ld) call dump_chunk(dbl_mb(ptr),ld1,ld2) call nga_release(g_a) c return end c subroutine dump_chunk(a,ld1,ld2) implicit double precision (a-h, o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" integer ld1, ld2 double precision a(ld1, ld2) do i = 1, min(10,ld1) write(6,100) (a(i,j), j = 1, min(10,ld2)) end do write(6,*) trace = 0.0d0 do i=1,ld2 trace = trace +a(i,i) end do write(6,*) 'trace=',trace 100 format(10f10.4) return end c double precision function contract_matrices(g_a,g_b) implicit double precision(a-h,o-z) #include "cscf.h" #include "mafdecls.fh" #include "global.fh" #include "mp3def.fh" integer lo(2), hi(2), ptr_a, ptr_b, ld, ld1, ld2 double precision a(ichunk,ichunk),b(ichunk,ichunk) double precision value logical dotask, next_chunk c c evalute sum_ij a_ij*b_ij c value = 0.0d00 call ga_zero(g_counter) dotask = next_chunk(lo,hi) ld = ichunk do while (dotask) call nga_get(g_a,lo,hi,a,ld) call nga_get(g_b,lo,hi,b,ld) do j = 1, hi(2)-lo(2)+1 do i = 1, hi(1)-lo(1)+1 value = value + a(i,j)*b(i,j) end do end do dotask = next_chunk(lo,hi) end do call ga_dgop(3,value,1,'+') contract_matrices=value c return end ga-5.9.2/global/examples/scf/timer.F000066400000000000000000000011731500715745200172060ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif double precision function timer() c c return the time since the last call to timer. c c must be initialized by calling once and throwing away the c value c ... use cpu time on multi-user machines c ... use elapsed time on dedicated or single user machines. c *mdc*if unix * real*4 dtime, tt(2) * timer = dble(dtime(tt)) *mdc*elseif tcgmsg C$Id: timer.F,v 1.1 2005-03-08 23:58:03 d3g293 Exp $ #include "mp3def.fh" save mlast data mlast/0/ m = MP_TIMER() timer = dble(m - mlast) * 0.01d0 mlast = m *mdc*endif c end ga-5.9.2/global/src/000077500000000000000000000000001500715745200141535ustar00rootroot00000000000000ga-5.9.2/global/src/CMakeLists.txt000066400000000000000000000106551500715745200167220ustar00rootroot00000000000000# # module: CMakeLists.txt # author: Bruce Palmer # description: CMake build for GA. Only MPI-based runtimes are supported. # # DISCLAIMER # # This material was prepared as an account of work sponsored by an # agency of the United States Government. Neither the United States # Government nor the United States Department of Energy, nor Battelle, # nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR # ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, # COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, # SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT # INFRINGE PRIVATELY OWNED RIGHTS. # # # ACKNOWLEDGMENT # # This software and its documentation were produced with United States # Government support under Contract Number DE-AC06-76RLO-1830 awarded by # the United States Department of Energy. The United States Government # retains a paid-up non-exclusive, irrevocable worldwide license to # reproduce, prepare derivative works, perform publicly and display # publicly by or for the US Government, including the right to # distribute to other US Government contractors. # # -*- mode: cmake -*- # ------------------------------------------------------------- # file: CMakeLists.txt # ------------------------------------------------------------- if (ENABLE_FORTRAN) set(GA_FORTRAN_INTERFACE_C_FILES fapi.c ) set(GA_FORTRAN_INTERFACE_H_FILES cnames.h ${CMAKE_CURRENT_BINARY_DIR}/global.fh ) set (GA_FORTRAN_INTERFACE_F_FILES complex.F ) endif() if (ENABLE_SCALAPACK) set (GA_FORTRAN_INTERFACE_H_FILES ${GA_FORTRAN_INTERFACE_H_FILES} scalapack.fh) set (GA_FORTRAN_INTERFACE_F_FILES ${GA_FORTRAN_INTERFACE_F_FILES} scalapack.F) set (GA_C_SCALAPACK_WRAPPER ga_diag_seqc.c) endif() if (ENABLE_FORTRAN) #if (HAVE_LAPACK OR ENABLE_EISPACK) set (GA_FORTRAN_INTERFACE_F_FILES ${GA_FORTRAN_INTERFACE_F_FILES} ga_diag_seq.F) set (GA_FORTRAN_H_FILES ga-mpi.fh) endif() set(target_libraries ma elio dra eaf sf) set(WAPI_FILES ga-wapi.h ga-wapidefs.h ) set(GA_FILES base.c onesided.c collect.c ghosts.c capi.c fapi.c datatypes.c decomp.c DP.c elem_alg.c ga_diag_seqc.c ga_malloc.c ga_profile.c ga_solve_seq.c ga_symmetr.c ga_trace.c global.nalg.c global.npatch.c global.periodic.c global.util.c hsort.scat.c iterator.c matmul.c matrix.c nbutil.c peigstubs.c sclstubs.c select.c sparse.c sparse.array.c sketch.c mtwister.c ${GA_FORTRAN_INTERFACE_C_FILES} ${GA_FORTRAN_INTERFACE_F_FILES} ) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ga-wapi.h COMMAND ${CMAKE_COMMAND} -D INPUT:PATH="${CMAKE_CURRENT_SOURCE_DIR}/ga-papi.h" -D OUTPUT:PATH="${CMAKE_CURRENT_BINARY_DIR}/ga-wapi.h" -P ${PROJECT_SOURCE_DIR}/tools/ga_papi_to_wapi.cmake DEPENDS ga-papi.h ) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ga-wapidefs.h COMMAND ${CMAKE_COMMAND} -D INPUT:PATH="${CMAKE_CURRENT_SOURCE_DIR}/ga-papi.h" -D OUTPUT:PATH="${CMAKE_CURRENT_BINARY_DIR}/ga-wapidefs.h" -P ${PROJECT_SOURCE_DIR}/tools/ga_papi_to_wapidefs.cmake DEPENDS ga-papi.h ) add_library(ga_src OBJECT ${CMAKE_CURRENT_BINARY_DIR}/ga-wapi.h ${CMAKE_CURRENT_BINARY_DIR}/ga-wapidefs.h ${GA_FILES} ) target_include_directories(ga_src BEFORE PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_BINARY_DIR}/gaf2c ${PROJECT_SOURCE_DIR}/ma ${PROJECT_BINARY_DIR}/ma ${PROJECT_SOURCE_DIR}/comex/src-armci #${PROJECT_SOURCE_DIR}/tcgmsg ${PROJECT_SOURCE_DIR}/LinAlg/lapack+blas ${CMAKE_CURRENT_BINARY_DIR} ${PROJECT_BINARY_DIR} ${MPI_C_INCLUDE_DIRS} ) if(ENABLE_FORTRAN) add_dependencies(ga_src gaf2c) endif() # ------------------------------------------------------------- # Global Arrays header installation # ------------------------------------------------------------- install(FILES # abstract_ops.h # base.h # cnames.h gacommon.h # gaconfig.h ga.h ga-mpi.h ga-papi.h # ga_profile.h # ga-wapidefs.h ${CMAKE_CURRENT_BINARY_DIR}/ga-wapi.h # global.h # globalp.h # matmul.h ${GA_FORTRAN_H_FILES} ${GA_FORTRAN_INTERFACE_H_FILES} DESTINATION include/ga ) list (APPEND GA_HEADER_PATHS ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_BINARY_DIR}) set (GA_HEADER_PATHS ${GA_HEADER_PATHS} PARENT_SCOPE) ga-5.9.2/global/src/DP.c000066400000000000000000000175401500715745200146310ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDLIB_H #include #endif /* $Id: DP.c,v 1.14 2003-02-18 00:24:32 manoj Exp $ */ #include "global.h" #include "globalp.h" #include "macommon.h" #include "typesf2c.h" #include "ga-papi.h" #include "ga-wapi.h" /*\ check if I own the patch \*/ static logical own_patch(g_a, ilo, ihi, jlo, jhi) Integer g_a, ilo, ihi, jlo, jhi; { Integer ilop, ihip, jlop, jhip, me=pnga_nodeid(); Integer lo[2],hi[2]; pnga_distribution(g_a, me, lo, hi); ilop = lo[0]; jlop = lo[1]; ihip = hi[0]; jhip = hi[1]; if(ihip != ihi || ilop != ilo || jhip != jhi || jlop != jlo) return(FALSE); else return(TRUE); } static logical patch_intersect(ilo, ihi, jlo, jhi, ilop, ihip, jlop, jhip) Integer ilo, ihi, jlo, jhi; Integer *ilop, *ihip, *jlop, *jhip; { /* check consistency of patch coordinates */ if( ihi < ilo || jhi < jlo) return FALSE; /* inconsistent */ if( *ihip < *ilop || *jhip < *jlop) return FALSE; /* inconsistent */ /* find the intersection and update (ilop: ihip, jlop: jhip) */ if( ihi < *ilop || *ihip < ilo) return FALSE; /* don't intersect */ if( jhi < *jlop || *jhip < jlo) return FALSE; /* don't intersect */ *ilop = GA_MAX(ilo,*ilop); *ihip = GA_MIN(ihi,*ihip); *jlop = GA_MAX(jlo,*jlop); *jhip = GA_MIN(jhi,*jhip); return TRUE; } /*\ COPY A PATCH * * . identical shapes * . copy by column order - Fortran convention \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_copy_patch_dp = pnga_copy_patch_dp #endif void pnga_copy_patch_dp(t_a, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi) Integer g_a, ailo, aihi, ajlo, ajhi; Integer g_b, bilo, bihi, bjlo, bjhi; char *t_a; { Integer atype, btype, adim1, adim2, bdim1, bdim2; Integer ilos, ihis, jlos, jhis; Integer ilod, ihid, jlod, jhid, corr, nelem; Integer me= pnga_nodeid(), ld, i,j; Integer lo[2], hi[2]; Integer ldT; char transp; DoublePrecision *dbl_ptrA=NULL, *dbl_ptrB=NULL; Integer ndim, dims[2]; pnga_check_handle(g_a, "pnga_copy_patch_dp"); pnga_check_handle(g_b, "pnga_copy_patch_dp"); /* if(g_a == g_b) pnga_error("pnga_copy_patch_dp: arrays have to different ", 0L); */ pnga_inquire(g_a, &atype, &ndim, dims); adim1 = dims[0]; adim2 = dims[1]; pnga_inquire(g_b, &btype, &ndim, dims); bdim1 = dims[0]; bdim2 = dims[1]; if(atype != btype || (atype != C_DBL )) pnga_error("pnga_copy_patch_dp: wrong types ", 0L); /* check if patch indices and dims match */ if (ailo <= 0 || aihi > adim1 || ajlo <= 0 || ajhi > adim2) pnga_error(" pnga_copy_patch_dp: g_a indices out of range ", 0L); if (bilo <= 0 || bihi > bdim1 || bjlo <= 0 || bjhi > bdim2) pnga_error(" pnga_copy_patch_dp: g_b indices out of range ", 0L); /* check if numbers of elements in two patches match each other */ if (((bihi - bilo + 1) != (aihi - ailo + 1)) || ( (bjhi - bjlo + 1) != (ajhi - ajlo + 1)) ) pnga_error(" pnga_copy_patch_dp: shapes two of patches do not match ", 0L); /* is transpose operation required ? */ transp = (*t_a == 'n' || *t_a =='N')? 'n' : 't'; /* now find out cordinates of a patch of g_a that I own */ pnga_distribution(g_a, me, lo, hi); ilos = lo[0]; jlos = lo[1]; ihis = hi[0]; jhis = hi[1]; if(patch_intersect(ailo, aihi, ajlo, ajhi, &ilos, &ihis, &jlos, &jhis)){ pnga_access_ptr(g_a, lo, hi, &dbl_ptrA, &ld); nelem = (ihis-ilos+1)*(jhis-jlos+1); if ( transp == 'n' ) { corr = bilo - ailo; ilod = ilos + corr; ihid = ihis + corr; corr = bjlo - ajlo; jlod = jlos + corr; jhid = jhis + corr; } else { /* If this is a transpose copy, we need local scratch space */ dbl_ptrB = (DoublePrecision*) pnga_malloc(nelem,MT_F_DBL,"copypatch_dp"); /* Copy from the source into this local array, transposed */ ldT = jhis-jlos+1; for(j=0; j< jhis-jlos+1; j++) for(i=0; i< ihis-ilos+1; i++) *(dbl_ptrB + i*ldT + j) = *(dbl_ptrA + j*ld + i); /* Now we can reset index to point to the transposed stuff */ pnga_release(g_a, lo, hi); dbl_ptrA = dbl_ptrB; ld = ldT; /* And finally, figure out what the destination indices are */ corr = bilo - ajlo; ilod = jlos + corr; ihid = jhis + corr; corr = bjlo - ailo; jlod = ilos + corr; jhid = ihis + corr; } /* Put it where it belongs */ lo[0] = ilod; lo[1] = jlod; hi[0] = ihid; hi[1] = jhid; pnga_put(g_b, lo, hi, dbl_ptrA, &ld); /* Get rid of local memory if we used it */ if( transp == 't') pnga_free(dbl_ptrB); } } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_ddot_patch_dp = pnga_ddot_patch_dp #endif DoublePrecision pnga_ddot_patch_dp(g_a, t_a, ailo, aihi, ajlo, ajhi, g_b, t_b, bilo, bihi, bjlo, bjhi) Integer g_a, ailo, aihi, ajlo, ajhi; /* patch of g_a */ Integer g_b, bilo, bihi, bjlo, bjhi; /* patch of g_b */ char *t_a, *t_b; /* transpose operators */ { Integer atype, btype, adim1, adim2, bdim1, bdim2; Integer iloA, ihiA, jloA, jhiA, ldA; Integer iloB, ihiB, jloB, jhiB, ldB; Integer alo[2], ahi[2]; Integer blo[2], bhi[2]; Integer g_A = g_a; Integer me= pnga_nodeid(), i, j, temp_created=0; Integer corr, nelem; char transp, transp_a, transp_b; DoublePrecision sum = 0.; DoublePrecision *dbl_ptrA; DoublePrecision *dbl_ptrB; Integer ndim, dims[2]; pnga_check_handle(g_a, "pnga_ddot_patch_dp"); pnga_check_handle(g_b, "pnga_ddot_patch_dp"); pnga_inquire(g_a, &atype, &ndim, dims); adim1 = dims[0]; adim2 = dims[1]; pnga_inquire(g_b, &btype, &ndim, dims); bdim1 = dims[0]; bdim2 = dims[1]; if(atype != btype || (atype != C_DBL )) pnga_error("pnga_ddot_patch_dp: wrong types ", 0L); /* check if patch indices and g_a dims match */ if (ailo <= 0 || aihi > adim1 || ajlo <= 0 || ajhi > adim2) pnga_error(" pnga_ddot_patch_dp: g_a indices out of range ", 0L); /* check if patch indices and g_b dims match */ if (bilo <= 0 || bihi > bdim1 || bjlo <= 0 || bjhi > bdim2) pnga_error(" pnga_ddot_patch_dp: g_b indices out of range ", 0L); /* is transpose operation required ? */ /* -- only if for one array transpose operation requested*/ transp_a = (*t_a == 'n' || *t_a =='N')? 'n' : 't'; transp_b = (*t_b == 'n' || *t_b =='N')? 'n' : 't'; transp = (transp_a == transp_b)? 'n' : 't'; if(transp == 't') pnga_error(" pnga_ddot_patch_dp: transpose operators don't match: ", me); /* find out coordinates of patches of g_A and g_B that I own */ pnga_distribution(g_A, me, alo, ahi); iloA = alo[0]; jloA = alo[1]; ihiA = ahi[0]; jhiA = ahi[1]; if (patch_intersect(ailo, aihi, ajlo, ajhi, &iloA, &ihiA, &jloA, &jhiA)){ pnga_access_ptr(g_A, alo, ahi, &dbl_ptrA, &ldA); nelem = (ihiA-iloA+1)*(jhiA-jloA+1); corr = bilo - ailo; iloB = iloA + corr; ihiB = ihiA + corr; corr = bjlo - ajlo; jloB = jloA + corr; jhiB = jhiA + corr; blo[0] = iloB; blo[1] = jloB; bhi[0] = ihiB; bhi[1] = jhiB; if(own_patch(g_b, iloB, ihiB, jloB, jhiB)){ /* all the data is local */ pnga_access_ptr(g_b, blo, bhi, &dbl_ptrB, &ldB); }else{ /* data is remote -- get it to temp storage*/ temp_created =1; dbl_ptrB = (DoublePrecision*)pnga_malloc(nelem, MT_F_DBL, "ddot_dp_b"); ldB = ihiB-iloB+1; pnga_get(g_b, blo, bhi, dbl_ptrB, &ldB); } sum = 0.; for(j=0; j< jhiA-jloA+1; j++) for(i=0; i< ihiA-iloA+1; i++) sum += *(dbl_ptrA + j*ldA + i) * *(dbl_ptrB + j*ldB + i); pnga_release(g_A, alo, ahi); if(temp_created) pnga_free(dbl_ptrB); else pnga_release(g_b, blo, bhi); } return sum; } ga-5.9.2/global/src/abstract_ops.h000066400000000000000000000122661500715745200170170ustar00rootroot00000000000000#ifndef _ABSTRACT_OPS_H_ #define _ABSTRACT_OPS_H_ /* abstract operations, 'regular' (reg) and 'complex' (cpl) */ #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif /* assignment e.g. a = b */ #define assign_reg(a,b) (a) = (b) #define assign_cpl(a,b) (a).real = (b).real; \ (a).imag = (b).imag /* assignment of zero e.g. a = 0 */ #define assign_zero_reg(a) (a) = 0 #define assign_zero_cpl(a) (a).real = 0; \ (a).imag = 0 /* assignment of a sum of two values e.g. a = b + c */ #define assign_add_reg(a,b,c) (a) = ((b) + (c)) #define assign_add_cpl(a,b,c) (a).real = ((b).real + (c).real); \ (a).imag = ((b).imag + (c).imag) /* assignment of a product of two values e.g. a = b * c */ #define assign_mul_reg(a,b,c) (a) = ((b) * (c)) #define assign_mul_cpl(a,b,c) (a).real = ((b).real*(c).real-(b).imag*(c).imag);\ (a).imag = ((b).real*(c).imag+(b).imag*(c).real) /* assignment of a product of two valus e.g. a = b * c */ #define assign_mul_constant_reg(a,b,c) (a) = ((b) * (c)) #define assign_mul_constant_cpl(a,b,c) (a).real = ((b) * (c).real); \ (a).imag = ((b) * (c).imag) /* assignment of a product of two valus e.g. a = b * c */ #define assign_mul_reg(a,b,c) (a) = ((b) * (c)) #define assign_mul_cpl(a,b,c) (a).real = ((b).real*(c).real-(b).imag*(c).imag); \ (a).imag = ((b).real*(c).imag+(b).imag*(c).real) /* assignment of a quotient of two valus e.g. a = b / c */ #define assign_div_reg(a,b,c) (a) = ((b) / (c)) #define assign_div_cpl(a,b,c) (a).real = (((b).real*(c).real+(b).imag*(c).imag) \ /((c).real*(c).real+(c).imag*(c).imag)); \ (a).imag = (((b).imag*(c).real-(b).real*(c).imag) \ /((c).real*(c).real+(c).imag*(c).imag)) /* in-place assignment of a sum e.g. a = a + b written a += b */ #define add_assign_reg(a,b) (a) += (b) #define add_assign_cpl(a,b) (a).real += (b).real; \ (a).imag += (b).imag /* not equal to zero e.g. a != 0 */ #define neq_zero_reg(a) (0 != (a)) #define neq_zero_cpl(a) (0 != (a).real || 0 != (a).imag) /* equal to zero e.g. a == 0 */ #define eq_zero_reg(a) (0 == (a)) #define eq_zero_cpl(a) (0 == (a).real && 0 == (a).imag) /* equality e.g. a == b */ #define eq_reg(a,b) ((a) == (b)) #define eq_cpl(a,b) ((a).real == (b).real && (a).imag == (b).imag) /* absolute value */ #define abs_reg(a) ((a) < 0 ? -(a) : (a)) #define abs_cpl(a) (a) = sqrt((a).real*(a).real+(a).imag*(a).imag) /* assignment of a maximum of two values e.g. if(b > c) a = b else a = c */ #define assign_max_reg(a,b,c) (a) = (b) > (c) ? (b) : (c) #define assign_max_cpl(a,b,c) (a).real = ((b).real*(b).real+(b).imag*(b).imag)>((c).real*(c).real+(c).imag*(c).imag)\ ? (b).real : (c).real; \ (a).imag = ((b).real*(b).real+(b).imag*(b).imag)>((c).real*(c).real+(c).imag*(c).imag)\ ? (b).imag : (c).imag /* assignment of a miniimum of two values e.g. if(b > c) a = b else a = c */ #define assign_min_reg(a,b,c) (a) = (b) < (c) ? (b) : (c) #define assign_min_cpl(a,b,c) (a).real = ((b).real*(b).real+(b).imag*(b).imag)<((c).real*(c).real+(c).imag*(c).imag)\ ? (b).real : (c).real; \ (a).imag = ((b).real*(b).real+(b).imag*(b).imag)<((c).real*(c).real+(c).imag*(c).imag)\ ? (b).imag : (c).imag /* assignment of an absolute value e.g. a = |b| */ #define assign_abs_reg(a,b) (a) = abs_reg(b) /* Note: absolute value of a complex number is usually sqrt(x*x + y*y) but this * can lead to overflows and/or underflows. Instead, we use the well-known * hypot solution: * double hypot(double x,double y) * { * double t; * x = abs(x); * y = abs(y); * t = min(x,y); * x = max(x,y); * y = t; * return x*sqrt(1+(y/x)*(y/x)); * } */ #if HAVE_HYPOT #define assign_abs_cpl(a,b) (a).real = hypot((b).real, (b).imag); \ (a).imag = 0.0 #else #define assign_abs_cpl(a,b) \ if (abs_reg((b).real) >= abs_reg((b).imag)) { \ (a).real = abs_reg((b).real) * \ sqrt(1.0 + ((b).imag/(b).real)*((b).imag/(b).real)); \ } else { \ (a).real = abs_reg((b).imag) * \ sqrt(1.0 + ((b).real/(b).imag)*((b).real/(b).imag)); \ } \ (a).imag = 0.0 #endif /* assignment of a random value */ #define sign() (1.0 * rand() / RAND_MAX > 1.0 ? 1.0 : -1.0) #define assign_rand_reg(a,val) (a) = 1.0 * (val) * rand() / RAND_MAX * sign() #define assign_rand_cpl(a,val) assign_rand_reg((a).real, (val).real); \ assign_rand_reg((a).imag, (val).imag) #endif /* _ABSTRACT_OPS_H_ */ ga-5.9.2/global/src/base.c000066400000000000000000005414551500715745200152470ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: base.c,v 1.149.2.19 2007/12/18 18:42:20 d3g293 Exp $ */ /* * module: base.c * author: Jarek Nieplocha * description: implements GA primitive operations -- * create (regular& irregular) and duplicate, destroy * * DISCLAIMER * * This material was prepared as an account of work sponsored by an * agency of the United States Government. Neither the United States * Government nor the United States Department of Energy, nor Battelle, * nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR * ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, * COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, * SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT * INFRINGE PRIVATELY OWNED RIGHTS. * * * ACKNOWLEDGMENT * * This software and its documentation were produced with United States * Government support under Contract Number DE-AC06-76RLO-1830 awarded by * the United States Department of Energy. The United States Government * retains a paid-up non-exclusive, irrevocable worldwide license to * reproduce, prepare derivative works, perform publicly and display * publicly by or for the US Government, including the right to * distribute to other US Government contractors. */ #if HAVE_STDIO_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_ASSERT_H # include #endif #include #include "farg.h" #include "globalp.h" #include "message.h" #include "base.h" #include "macdecls.h" #include "armci.h" #include "ga-papi.h" #include "ga-wapi.h" #include "thread-safe.h" #include "mtwister.h" static int calc_maplen(int handle); #ifdef PROFILE_OLD #include "ga_profile.h" #endif /*#define AVOID_MA_STORAGE 1*/ #define DEBUG 0 #define USE_MALLOC 1 #define INVALID_MA_HANDLE -1 #define NEAR_INT(x) (x)< 0.0 ? ceil( (x) - 0.5) : floor((x) + 0.5) #define FLEN 80 /* length of Fortran strings */ /*uncomment line below to verify consistency of MA in every sync */ /*#define CHECK_MA yes */ /*uncomment line below to verify if MA base address is alligned wrt datatype*/ #if !(defined(LINUX) || defined(CYGWIN)) #define CHECK_MA_ALGN 1 #endif /*uncomment line below to initialize arrays in ga_create/duplicate */ /*#define GA_CREATE_INDEF yes */ /*uncomment line below to introduce padding between shared memory regions of a GA when the region spans in more than 1 process within SMP */ #define GA_ELEM_PADDING yes #define OLD_DISTRIBUTION 1 #if OLD_DISTRIBUTION extern void ddb_h2(Integer ndims, Integer dims[], Integer npes, double threshold, Integer bias, Integer blk[], Integer pedims[]); #else extern void ddb(Integer ndims, Integer dims[], Integer npes, Integer blk[], Integer pedims[]); #endif global_array_t *_ga_main_data_structure; global_array_t *GA; proc_list_t *_proc_list_main_data_structure; proc_list_t *PGRP_LIST; static int GAinitialized = 0; static int ARMCIinitialized = 0; int _ga_sync_begin = 1; int _ga_sync_end = 1; int _max_global_array = MAX_ARRAYS; int GA_World_Proc_Group = -1; int GA_Default_Proc_Group = -1; int ga_armci_world_group=0; int GA_Init_Proc_Group = -2; Integer GA_Debug_flag = 0; static Integer GA_Rand_seed = -1; static MTRand GA_Rand; /* MA addressing */ DoubleComplex *DCPL_MB; /* double precision complex base address */ SingleComplex *SCPL_MB; /* single precision complex base address */ DoublePrecision *DBL_MB; /* double precision base address */ Integer *INT_MB; /* integer base address */ float *FLT_MB; /* float base address */ int** GA_Update_Flags; int* GA_Update_Signal; typedef struct { long id; long type; long size; long dummy; } getmem_t; /*\ * Copy of GA's internal communicator \*/ #ifdef MSG_COMMS_MPI MPI_Comm GA_MPI_World_comm_dup; #endif /* set total limit (bytes) for memory usage per processor to "unlimited" */ static Integer GA_total_memory = -1; static Integer GA_memory_limited = 0; struct ga_stat_t GAstat; struct ga_bytes_t GAbytes ={0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.}; long *GAstat_arr; static Integer GA_memory_limit=0; Integer GAme, GAnproc; static Integer MPme; Integer *mapALL; static Integer _mirror_gop_grp; /* Function prototypes */ int gai_getmem(char* name, char **ptr_arr, C_Long bytes, int type, long *id, int grp_id); int gai_get_devmem(char *name, char **ptr_arr, C_Long bytes, int type, long *adj, int grp_id, int dev_flag, const char *device); extern void sai_init_sparse_arrays(); extern void sai_terminate_sparse_arrays(); #ifdef ENABLE_CHECKPOINT static int ga_group_is_for_ft=0; int ga_spare_procs; #endif /*************************************************************************/ /*\ This macro computes index (place in ordered set) for the element * identified by _subscript in ndim- dimensional array of dimensions _dim[] * assume that first subscript component changes first \*/ #define ga_ComputeIndexM(_index, _ndim, _subscript, _dims) \ { \ Integer _i, _factor=1; \ for(_i=0,*(_index)=0; _i<_ndim; _i++){ \ *(_index) += _subscript[_i]*_factor; \ if(_i<_ndim-1)_factor *= _dims[_i]; \ } \ } /*\ updates subscript corresponding to next element in a patch \*/ #define ga_UpdateSubscriptM(_ndim, _subscript, _lo, _hi, _dims)\ { \ Integer _i; \ for(_i=0; _i<_ndim; _i++){ \ if(_subscript[_i] < _hi[_i]) { _subscript[_i]++; break;} \ _subscript[_i] = _lo[_i]; \ } \ } /*\ Initialize n-dimensional loop by counting elements and setting subscript=lo \*/ #define ga_InitLoopM(_elems, _ndim, _subscript, _lo, _hi, _dims)\ { \ Integer _i; \ *_elems = 1; \ for(_i=0; _i<_ndim; _i++){ \ *_elems *= _hi[_i]-_lo[_i] +1; \ _subscript[_i] = _lo[_i]; \ } \ } Integer GAsizeof(Integer type) { switch (type) { case C_DBL : return (sizeof(double)); case C_INT : return (sizeof(int)); case C_SCPL : return (sizeof(SingleComplex)); case C_DCPL : return (sizeof(DoubleComplex)); case C_FLOAT : return (sizeof(float)); case C_LONG : return (sizeof(long)); case C_LONGLONG : return (sizeof(long long)); case F_DBL : return (sizeof(DoublePrecision)); case F_INT : return (sizeof(Integer)); default : return 0; } } void* pnga_malloc(Integer nelem, int type, char *name) { #ifdef USE_GA_MALLOC return ga_malloc(nelem, type, name); #else return malloc(nelem*GAsizeof(type)); #endif } void pnga_free(void *ptr) { #ifdef USE_GA_MALLOC ga_free(ptr); #else free(ptr); #endif } /*\ Register process list * process list can be used to: * 1. permute process ids w.r.t. message-passing ids (set PERMUTE_PIDS), or * 2. change logical mapping of array blocks to processes \*/ void ga_register_proclist_(Integer *list, Integer* np) { /* no longer used */ } void GA_Register_proclist(int *list, int np) { /* no long used */ } /*\ FINAL CLEANUP of shmem when terminating \*/ void ga_clean_resources() { ARMCI_Cleanup(); } /*\ CHECK GA HANDLE and if it's wrong TERMINATE * C version \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_check_handle = pnga_check_handle #endif void pnga_check_handle(Integer g_a, char * string) { ga_check_handleM(g_a, string); } /*\ Initialize MA-like addressing: * get addressees for the base arrays for double, complex and int types \*/ static int ma_address_init=0; void gai_ma_address_init() { #ifdef CHECK_MA_ALGN Integer off_dbl, off_int, off_dcpl, off_flt,off_scpl; #endif ma_address_init=1; INT_MB = (Integer*)MA_get_mbase(MT_F_INT); DBL_MB = (DoublePrecision*)MA_get_mbase(MT_F_DBL); DCPL_MB= (DoubleComplex*)MA_get_mbase(MT_F_DCPL); SCPL_MB= (SingleComplex*)MA_get_mbase(MT_F_SCPL); FLT_MB = (float*)MA_get_mbase(MT_F_REAL); # ifdef CHECK_MA_ALGN off_dbl = 0 != ((long)DBL_MB)%sizeof(DoublePrecision); off_int = 0 != ((long)INT_MB)%sizeof(Integer); off_dcpl= 0 != ((long)DCPL_MB)%sizeof(DoublePrecision); off_scpl= 0 != ((long)SCPL_MB)%sizeof(float); off_flt = 0 != ((long)FLT_MB)%sizeof(float); if(off_dbl) pnga_error("GA initialize: MA DBL_MB not alligned", (Integer)DBL_MB); if(off_int) pnga_error("GA initialize: INT_MB not alligned", (Integer)INT_MB); if(off_dcpl) pnga_error("GA initialize: DCPL_MB not alligned", (Integer)DCPL_MB); if(off_scpl) pnga_error("GA initialize: SCPL_MB not alligned", (Integer)SCPL_MB); if(off_flt) pnga_error("GA initialize: FLT_MB not alligned", (Integer)FLT_MB); # endif if(DEBUG) printf("%d INT_MB=%p DBL_MB=%p DCPL_MB=%p FLT_MB=%p SCPL_MB=%p\n", (int)GAme, (void*)INT_MB, (void*)DBL_MB, (void*)DCPL_MB, (void*)FLT_MB, (void*)SCPL_MB); } extern int *_ga_argc; extern char ***_ga_argv; extern int _ga_initialize_args; extern int _ga_initialize_c; extern int _ga_initialize_f; /** * Initialize library structures in Global Arrays. * either ga_initialize_ltd or ga_initialize must be the first * GA routine called (except ga_uses_ma or ga_set_memory_limit) */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_initialize = pnga_initialize #endif void pnga_initialize() { Integer i, j,nproc, nnode, zero; int bytes; GA_Internal_Threadsafe_Lock(); #ifdef MSG_COMMS_MPI MPI_Comm comm; #endif if(GAinitialized) { GA_Internal_Threadsafe_Unlock(); return; } #if HAVE_ARMCI_INITIALIZED_FUNCTION if (!ARMCI_Initialized()) #else if (!ARMCIinitialized) #endif { /* assure that GA will not alocate more shared memory than specified */ if(GA_memory_limited) ARMCI_Set_shm_limit(GA_total_memory); if (_ga_initialize_c) { if (_ga_initialize_args) { ARMCI_Init_args(_ga_argc, _ga_argv); } else { ARMCI_Init(); } } else if (_ga_initialize_f) { _ga_argc = malloc(sizeof(int)); _ga_argv = malloc(sizeof(char**)); if (!_ga_argc) pnga_error("malloc argc failed",1); ga_f2c_get_cmd_args(_ga_argc, _ga_argv); ARMCI_Init_args(_ga_argc, _ga_argv); } else { pnga_error("pnga_initialize called outside of C or F APIs",1); } ARMCIinitialized = 1; } GA_Default_Proc_Group = -1; /* zero in pointers in GA array */ _ga_main_data_structure = (global_array_t *)malloc(sizeof(global_array_t)*MAX_ARRAYS); _proc_list_main_data_structure = (proc_list_t *)malloc(sizeof(proc_list_t)*MAX_ARRAYS); if(!_ga_main_data_structure) pnga_error("ga_init:malloc ga failed",0); if(!_proc_list_main_data_structure) pnga_error("ga_init:malloc proc_list failed",0); GA = _ga_main_data_structure; PGRP_LIST = _proc_list_main_data_structure; for(i=0;iGAnproc) pnga_error("ga_init:message-passing initialization problem: my ID=",GAme); MPme= (Integer)armci_msg_me(); gai_init_onesided(); /* set activity status for all arrays to inactive */ for(i=0;i<_max_global_array;i++)GA[i].actv=0; for(i=0;i<_max_global_array;i++)GA[i].actv_handle=0; /* Create proc list for mirrored arrays */ PGRP_LIST[0].map_proc_list = (int*)malloc(GAnproc*sizeof(int)*2); PGRP_LIST[0].inv_map_proc_list = PGRP_LIST[0].map_proc_list + GAnproc; for (i=0; i=tmpcount) ga_irecover(0); printf("\n%d:here done with initialize\n",GAme); } #endif /* create duplicate of world communicator */ #ifdef MSG_COMMS_MPI comm = GA_MPI_Comm_pgroup(-1); MPI_Comm_dup(comm, &GA_MPI_World_comm_dup); #endif sai_init_sparse_arrays(); GA_Internal_Threadsafe_Unlock(); } /** * Initialize library structures in Global Arrays over a communicator that is * supplied by an external program. Some version of ga_initialize must be * the first GA routine called (except ga_uses_ma or ga_set_memory_limit) */ #ifdef MSG_COMMS_MPI #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_initialize_comm = pnga_initialize_comm #endif int pnga_initialize_comm(MPI_Comm comm) { /** * Initialize ARMCI first using communicator and then initialize GA using * conventional initialization program. The conventional initialization code * should recognize that ARMCI has already been initialized */ int ret = ARMCI_Init_mpi_comm(comm); if (ret) { pnga_initialize(); } return ret; } #endif #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_initialized = pnga_initialized #endif int pnga_initialized() { return GAinitialized; } #if ENABLE_CHECKPOINT void set_ga_group_is_for_ft(int val) { ga_group_is_for_ft = val; } #endif /** * Is MA used for allocation of GA memory? */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_uses_ma = pnga_uses_ma #endif logical pnga_uses_ma() { #ifdef AVOID_MA_STORAGE return FALSE; #else if(!GAinitialized) return FALSE; if(ARMCI_Uses_shm()) return FALSE; else return TRUE; #endif } /** * Is memory limit set */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_memory_limited = pnga_memory_limited #endif logical pnga_memory_limited() { if(GA_memory_limited) return TRUE; else return FALSE; } /** * Returns the amount of memory on each processor used in active Global Arrays */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_inquire_memory = pnga_inquire_memory #endif Integer pnga_inquire_memory() { Integer i, sum=0; for(i=0; i<_max_global_array; i++) if(GA[i].actv) sum += (Integer)GA[i].size; return(sum); } /** * Returns the amount of memory available on the calling processor */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_memory_avail = pnga_memory_avail #endif Integer pnga_memory_avail() { if(!pnga_uses_ma()) return(GA_total_memory); else{ Integer ma_limit = MA_inquire_avail(MT_F_BYTE); if ( GA_memory_limited ) return( GA_MIN(GA_total_memory, ma_limit) ); else return( ma_limit ); } } /** * (re)set limit on GA memory usage */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_set_memory_limit = pnga_set_memory_limit #endif void pnga_set_memory_limit(Integer mem_limit) { if(GA_memory_limited){ /* if we had the limit set we need to adjust the amount available */ if (mem_limit>=0) /* adjust the current value by diff between old and new limit */ GA_total_memory += (mem_limit - GA_memory_limit); else{ /* negative values reset limit to "unlimited" */ GA_memory_limited = 0; GA_total_memory= -1; } }else{ GA_total_memory = GA_memory_limit = mem_limit; if(mem_limit >= 0) GA_memory_limited = 1; } } /** * Initialize Global Array library structures and set a limit on memory * usage by GA. * the byte limit is per processor (even for shared memory) * either ga_initialize_ltd or ga_initialize must be the first * GA routine called (except ga_uses_ma) * ga_initialize is another version of ga_initialize_ltd, except * without memory control * mem_limit < 0 means "memory unlimited" */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_initialize_ltd = pnga_initialize_ltd #endif void pnga_initialize_ltd(Integer mem_limit) { GA_total_memory =GA_memory_limit = mem_limit; if(mem_limit >= 0) GA_memory_limited = 1; pnga_initialize(); } /* #define gam_checktype(_type)\ */ /* if(_type != C_DBL && _type != C_INT && \ */ /* _type != C_DCPL && _type != C_SCPL && _type != C_FLOAT && \ */ /* _type != C_LONG &&_type != C_LONGLONG)\ */ /* pnga_error("ttype not yet supported ", _type) */ #define gam_checktype(_type) if(!GAvalidtypeM(_type))pnga_error("type not yet supported", (_type)) #define gam_checkdim(ndim, dims)\ {\ int _d;\ if(ndim<1||ndim>MAXDIM) pnga_error("unsupported number of dimensions",ndim);\ for(_d=0; _d= 0) { if (PGRP_LIST[p_handle].mirrored) ret = TRUE; } return ret; } /** * map_ij: pointer to map array containing axis partitions * n: number of blocks along axis * scale: factor for coming up with an initial guess * elem: array element index that we are trying to find * block: index of block containing elem */ #define findblock(map_ij,n,scale,elem, block)\ {\ int candidate, found, b; \ C_Integer *map= (map_ij);\ \ candidate = (int)(scale*(elem));\ if (candidate == (n)) candidate = (n)-1; \ found = 0;\ if(map[candidate] <= (elem)){ /* search upward */\ b= candidate;\ while(b<(n)-1){ \ found = (map[b+1]>(elem));\ if(found)break;\ b++;\ } \ }else{ /* search downward */\ b= candidate-1;\ while(b>=0){\ found = (map[b]<=(elem));\ if(found)break;\ b--;\ }\ }\ if(!found)b=(n)-1;\ *(block) = b;\ } /*\ * Find indices of block containing the array element at the location * in subscript. \*/ #define gam_find_block_indices_from_subscript(handle,subscript,index)\ { \ int _type = GA[handle].distr_type; \ Integer *_mapc = GA[handle].mapc; \ Integer _offset; \ int _i; \ int _ndim = GA[handle].ndim; \ if (_type == REGULAR) { \ for (_i=0, _offset=0; _i<_ndim; _i++) { \ findblock(_mapc+_offset, GA[handle].nblock[_i], \ GA[handle].scale[_i],subscript[_i],&index[_i]); \ _offset += GA[handle].nblock[_i]; \ } \ } else if (_type == TILED_IRREG) { \ for (_i=0, _offset=0; _i<_ndim; _i++) { \ findblock(_mapc+_offset, GA[handle].num_blocks[_i], \ GA[handle].scale[_i],subscript[_i],&index[_i]); \ _offset += GA[handle].num_blocks[_i]; \ } \ } else { \ for (_i=0; _i<_ndim; _i++) { \ index[_i] = (subscript[_i]-1)/GA[handle].block_dims[_i]; \ } \ } \ } /** * Locate the owner of an element of a Global Array specified by the array * subscript */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_locate = pnga_locate #endif logical pnga_locate(Integer g_a, Integer* subscript, Integer* owner) { Integer d, proc, dpos, ndim, ga_handle = GA_OFFSET + g_a, proc_s[MAXDIM]; ga_check_handleM(g_a, "nga_locate"); ndim = GA[ga_handle].ndim; if (GA[ga_handle].distr_type == REGULAR) { for(d=0, *owner=-1; d< ndim; d++) if(subscript[d]< 1 || subscript[d]>GA[ga_handle].dims[d]) return FALSE; for(d = 0, dpos = 0; d< ndim; d++){ findblock(GA[ga_handle].mapc + dpos, GA[ga_handle].nblock[d], GA[ga_handle].scale[d], subscript[d], &proc_s[d]); dpos += GA[ga_handle].nblock[d]; } ga_ComputeIndexM(&proc, ndim, proc_s, GA[ga_handle].nblock); *owner = proc; if (GA[ga_handle].num_rstrctd > 0) { *owner = GA[ga_handle].rstrctd_list[*owner]; } } else { Integer i; Integer index[MAXDIM]; gam_find_block_indices_from_subscript(ga_handle,subscript,index); gam_find_block_from_indices(ga_handle,i,index); *owner = i; } return TRUE; } /*\ UTILITY FUNCTION TO LOCATE THE BOUNDING INDICES OF A CONTIGUOUS CHUNK OF * SHARED MEMORY FOR A MIRRORED ARRAY \*/ void ngai_get_first_last_indices( Integer g_a) /* array handle (input) */ { Integer lo[MAXDIM], hi[MAXDIM]; Integer nelems, nnodes, inode, nproc; Integer ifirst, ilast, nfirst, nlast, icnt, np; Integer i, j, itmp, ndim, map_offset[MAXDIM]; /* Integer icheck; */ Integer index[MAXDIM], subscript[MAXDIM]; Integer handle = GA_OFFSET + g_a; Integer type, size=0, id; /* Integer grp_id; */ int Save_default_group; char *fptr, *lptr; /* find total number of elements */ ndim = GA[handle].ndim; nelems = 1; for (i=0; i= ifirst && nfirst < 0) { nfirst = i; } if (ilast <= icnt-1 && nfirst >= 0 && nlast < 0) { nlast = i; } } /* Adjust indices corresponding to start and end of block of shared memory so that it can be decomposed into large rectangular blocks of the global array. Start by adusting the lower index */ icnt = 0; for (i = 0; i= lo[j]) { nelems *= (hi[j] - lo[j] + 1); } else { nelems = 0; } } icnt += nelems; } /* calculate offset in local block of memory */ ifirst = ifirst - icnt; /* find dimensions of data on block nfirst */ np = nfirst; pnga_distribution(g_a, np, lo, hi); nelems = 1; for (i=0; i GA[handle].dims[ndim-1]-1) ifirst=GA[handle].dims[ndim-1]-1; /* adjust value of ifirst */ pnga_proc_topology(g_a, nfirst, index); subscript[ndim-1] = ifirst; for (i=0; i nfirst) { icnt = 0; for (i = 0; i= lo[j]) { nelems *= (hi[j] - lo[j] + 1); } else { nelems = 0; } } icnt += nelems; } } ilast = ilast - icnt; /* find dimensions of data on block nlast */ np = nlast; pnga_distribution(g_a, np, lo, hi); nelems = 1; for (i=0; i GA[handle].dims[i]) { GA[handle].last[i] = GA[handle].dims[i]; } } /* find length of shared memory segment owned by this node. Adjust * length, if necessary, to account for gaps in memory between * processors */ type = GA[handle].type; switch(type) { case C_FLOAT: size = sizeof(float); break; case C_DBL: size = sizeof(double); break; case C_LONG: size = sizeof(long); break; case C_LONGLONG: size = sizeof(long long); break; case C_INT: size = sizeof(int); break; case C_SCPL: size = 2*sizeof(float); break; case C_DCPL: size = 2*sizeof(double); break; default: pnga_error("type not supported",type); } for (i=0; i 0) parent_grp = PGRP_LIST[GA_Default_Proc_Group].group; else ARMCI_Group_get_world(&parent_grp); ARMCI_Group_set_default(&parent_grp); } #endif } /** * Create a new processor group containing count processors with * process IDs (in the default group) in list. Return process group * handle. */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_pgroup_create = pnga_pgroup_create #endif Integer pnga_pgroup_create(Integer *list, Integer count) { Integer pgrp_handle, i, j, nprocs, itmp; Integer parent; int tmp_count; Integer *tmp_list; int *tmp2_list; #ifdef MSG_COMMS_MPI ARMCI_Group *tmpgrp; #endif /* Allocate temporary arrays */ tmp_list = (Integer*)malloc(GAnproc*sizeof(Integer)); tmp2_list = (int*)malloc(GAnproc*sizeof(int)); /*** Get next free process group handle ***/ pgrp_handle =-1; i=0; do{ if(!PGRP_LIST[i].actv) pgrp_handle=i; i++; }while(i<_max_global_array && pgrp_handle==-1); if( pgrp_handle == -1) pnga_error(" Too many process groups ", (Integer)_max_global_array); /* Check list for validity (no duplicates and no out of range entries) */ nprocs = GAnproc; for (i=0; i= nprocs) pnga_error(" invalid element in list ", list[i]); for (j=i+1; j=0 && tmp2_list[i] > itmp) { tmp2_list[i+1] = tmp2_list[i]; i--; } tmp2_list[i+1] = itmp; } /* Remap elements in list to absolute processor indices (if necessary)*/ if (GA_Default_Proc_Group != -1) { parent = GA_Default_Proc_Group; for (i=0; i grp_size*grp_num) grp_size++; /* Figure out what procs are in my group */ ratio = me/grp_size; start = ratio*grp_size; end = (ratio+1)*grp_size-1; end = GA_MIN(end,nprocs-1); if (end0) { grp_id = pnga_pgroup_create(nodes, grp_size); if (i == end + 1) { ret = grp_id; } icnt = 0; } nodes[icnt] = i; icnt++; } grp_id = pnga_pgroup_create(nodes, icnt); if (end == nprocs-1) { ret = grp_id; } pnga_pgroup_set_default(default_grp); if(ret==-1) pnga_error("ga_pgroup_split failed",ret); /* Free temporary array */ free(nodes); return ret; } /** * Split grp into multiple groups based on the color in mycolor. All processes * in grp with the same color are assigned to the same group. */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_pgroup_split_irreg = pnga_pgroup_split_irreg #endif Integer pnga_pgroup_split_irreg(Integer grp, Integer mycolor) { Integer nprocs, me, default_grp, grp_id; Integer i, icnt=0; Integer *nodes, *color_arr; /* Allocate temporary arrays */ nodes = (Integer*)malloc(GAnproc*sizeof(Integer)); color_arr = (Integer*)malloc(GAnproc*sizeof(Integer)); if(mycolor<0) pnga_error("Invalid argument (color < 0)",mycolor); default_grp = pnga_pgroup_get_default(); pnga_pgroup_set_default(grp); nprocs = pnga_nnodes(); me = pnga_nodeid(); /* Figure out what procs are in my group */ for(i=0; i FNAM) pnga_error("Array name exceeds maximum array name length",FNAM); strcpy(GA[ga_handle].name, array_name); } /** * Get the array name of a global array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_get_array_name = pnga_get_array_name #endif void pnga_get_array_name(Integer g_a, char *array_name) { Integer ga_handle = g_a + GA_OFFSET; if (GA[ga_handle].actv == 1) pnga_error("Cannot get array name on array that has not been allocated",0); if (strlen(array_name) > FNAM) pnga_error("Array name exceeds maximum array name length",FNAM); strcpy(array_name, GA[ga_handle].name); } /** * Set the processor group on a new global array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_set_pgroup = pnga_set_pgroup #endif void pnga_set_pgroup(Integer g_a, Integer p_handle) { Integer ga_handle = g_a + GA_OFFSET; if (GA[ga_handle].actv == 1) pnga_error("Cannot set processor configuration on array that has been allocated",0); if (p_handle == GA_World_Proc_Group || PGRP_LIST[p_handle].actv == 1) { GA[ga_handle].p_handle = (int) (p_handle); } else { pnga_error("Processor group does not exist",0); } } /** * Get the processor group handle associated with g_a */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_get_pgroup = pnga_get_pgroup #endif Integer pnga_get_pgroup(Integer g_a) { Integer ga_handle = g_a + GA_OFFSET; return (Integer)GA[ga_handle].p_handle; } /** * Return the number of processors associated with a processor group */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_get_pgroup_size = pnga_get_pgroup_size #endif Integer pnga_get_pgroup_size(Integer grp_id) { int p_handle = (int)(grp_id); if (p_handle > 0) { return (Integer)PGRP_LIST[p_handle].map_nproc; } else { return GAnproc; } } /** * Add ghost cells to a new global array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_set_ghosts = pnga_set_ghosts #endif void pnga_set_ghosts(Integer g_a, Integer *width) { Integer i; Integer ga_handle = g_a + GA_OFFSET; if (GA[ga_handle].actv == 1) pnga_error("Cannot set ghost widths on array that has been allocated",0); if (GA[ga_handle].ndim < 1) pnga_error("Dimensions must be set before array widths are specified",0); for (i=0; i GA[ga_handle].dims[i]) pnga_error("Boundary width must be <= corresponding dimension",i); if ((C_Integer)width[i] < 0) pnga_error("Boundary width must be >= 0",i); } for (i=0; i 0) GA[ga_handle].ghosts = 1; } } /** * Set irregular distribution in a new global array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_set_irreg_distr = pnga_set_irreg_distr #endif void pnga_set_irreg_distr(Integer g_a, Integer *mapc, Integer *nblock) { Integer i, j, ichk, maplen; Integer ga_handle = g_a + GA_OFFSET; if (GA[ga_handle].actv == 1) pnga_error("Cannot set irregular data distribution on array that has been allocated",0); if (GA[ga_handle].ndim < 1) pnga_error("Dimensions must be set before irregular distribution is specified",0); for (i=0; i GA[ga_handle].dims[i]) pnga_error("number of blocks must be <= corresponding dimension",i); /* Check to see that mapc array is sensible */ maplen = 0; for (i=0; i GA[ga_handle].dims[i]+1) pnga_error("Mapc entry outside array dimension limits",ichk); maplen++; for (j=1; j GA[ga_handle].dims[i]+1) pnga_error("Mapc entry outside array dimension limits",ichk); maplen++; } } maplen = 0; for (i=0; i 0)) pnga_error("Cannot set block-cyclic data distribution if array size not set",0); if (GA[ga_handle].distr_type != REGULAR) pnga_error("Cannot reset block-cyclic data distribution on array that has been set",0); GA[ga_handle].distr_type = BLOCK_CYCLIC; /* evaluate number of blocks in each dimension */ for (i=0; i 0)) pnga_error("Cannot set block-cyclic data distribution if array size not set",0); if (GA[ga_handle].distr_type != REGULAR) pnga_error("Cannot reset block-cyclic data distribution on array that has been set",0); GA[ga_handle].distr_type = SCALAPACK; for (i=0; i 0)) pnga_error("Cannot set tiled data distribution if array size not set",0); if (GA[ga_handle].distr_type != REGULAR) pnga_error("Cannot reset tiled data distribution on array that has been set",0); GA[ga_handle].distr_type = TILED; /* evaluate number of blocks in each dimension */ for (i=0; i 0)) pnga_error("Cannot set irregular tiled data distribution if array size not set",0); if (GA[ga_handle].ndim < 1) pnga_error("Dimensions must be set before irregular distribution is specified",0); for (i=0; i GA[ga_handle].dims[i]) pnga_error("number of blocks must be <= corresponding dimension",i); if (GA[ga_handle].distr_type != REGULAR) pnga_error("Cannot reset irregular tiled data distribution on array that has been set",0); GA[ga_handle].distr_type = TILED_IRREG; /* Check to see that mapc array is sensible */ maplen = 0; for (i=0; i GA[ga_handle].dims[i]+1) pnga_error("Mapc entry outside array dimension limits",ichk); maplen++; for (j=1; j GA[ga_handle].dims[i]+1) pnga_error("Mapc entry outside array dimension limits",ichk); maplen++; } } maplen = 0; for (i=0; i 0) { me = PGRP_LIST[p_handle].map_proc_list[GAme]; nproc = PGRP_LIST[p_handle].map_nproc; } else { me = GAme; nproc = GAnproc; } has_data = 0; for (i=0; i= nproc) pnga_error("Invalid processor in list",list[i]); ig = list[i]; GA[ga_handle].rank_rstrctd[ig] = i; } GA[ga_handle].has_data = has_data; GA[ga_handle].rstrctd_id = id; } /** * Restrict processors that actually contain data in the global array * by specifying a range of processors */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_set_restricted_range = pnga_set_restricted_range #endif void pnga_set_restricted_range(Integer g_a, Integer lo_proc, Integer hi_proc) { Integer i, ig, id=0, me, p_handle, has_data, icnt, nproc, size; Integer ga_handle = g_a + GA_OFFSET; size = hi_proc - lo_proc + 1; GA[ga_handle].num_rstrctd = size; GA[ga_handle].rstrctd_list = (Integer*)malloc((size)*sizeof(Integer)); GA[ga_handle].rank_rstrctd = (Integer*)malloc((GAnproc)*sizeof(Integer)); p_handle = GA[ga_handle].p_handle; if (p_handle == -2) p_handle = pnga_pgroup_get_default(); if (p_handle > 0) { me = PGRP_LIST[p_handle].map_proc_list[GAme]; nproc = PGRP_LIST[p_handle].map_nproc; } else { me = GAme; nproc = GAnproc; } has_data = 0; for (i=0; i= nproc) pnga_error("Invalid processor in list",i); ig = i; GA[ga_handle].rank_rstrctd[ig] = icnt; icnt++; } GA[ga_handle].has_data = has_data; GA[ga_handle].rstrctd_id = id; } /** * Set the property on a global array. */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_set_property = pnga_set_property #endif void pnga_set_property(Integer g_a, char* property) { Integer ga_handle = g_a + GA_OFFSET; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous sync masking*/ pnga_pgroup_sync(GA[ga_handle].p_handle); /* Check to see if property conflicts with properties already set on the * global array. This check may need more refinement as additional properties * are added. */ if (GA[ga_handle].property != NO_PROPERTY) { pnga_error("Cannot set property on an array that already has property set",0); } if (strcmp(property,"read_only")==0) { /* TODO: copy global array to new configuration */ int i, d, ndim, btot, chk; Integer nprocs, nodeid, origin_id, dflt_grp, handle, maplen; Integer nelem, mem_size, status, grp_me, g_tmp, me_local; Integer *list; Integer blk[MAXDIM], dims[MAXDIM], pe[MAXDIM], chunk[MAXDIM]; Integer lo[MAXDIM], hi[MAXDIM], ld[MAXDIM]; Integer *pmap[MAXDIM], *map; char *buf; if (GA[ga_handle].distr_type != REGULAR) { pnga_error("Block-cyclic arrays not supported for READ_ONLY",0); } if (GA[ga_handle].p_handle != pnga_pgroup_get_world()) { pnga_error("Arrays on subgroups not supported for READ_ONLY",0); } ndim = (int)GA[ga_handle].ndim; btot = 0; for (i=0; i 1) { Integer ddim = ((dims[d]-1)/GA_MIN(chunk[d],dims[d]) + 1); pcut = (ddim -(blk[d]-1)*pe[d]) ; } else { pcut = (dims[d]-(blk[d]-1)*pe[d]) ; } for (nblock=i=p=0; (p= pcut) b = b-1; map[nblock] = i+1; if (chunk[d]>1) b *= GA_MIN(chunk[d],dims[d]); i += b; } pe[d] = GA_MIN(pe[d],nblock); map += pe[d]; } maplen = 0; for( i = 0; i< ndim; i++){ GA[ga_handle].nblock[i] = pe[i]; maplen += pe[i]; } free(GA[ga_handle].mapc); GA[ga_handle].mapc = (Integer*)malloc((maplen+1)*sizeof(Integer)); for(i = 0; i< maplen; i++) { GA[ga_handle].mapc[i] = (C_Integer)mapALL[i]; } GA[ga_handle].mapc[maplen] = -1; /* Set remaining paramters and determine memory size if regular data * distribution is being used */ for( i = 0; i< ndim; i++){ GA[ga_handle].scale[i] = (double)GA[ga_handle].nblock[i] / (double)GA[ga_handle].dims[i]; } /*** determine which portion of the array I am supposed * to hold ***/ pnga_distribution(g_a, GAme, GA[ga_handle].lo, hi); chk = 1; for( i = 0, nelem=1; i< ndim; i++){ if (hi[i]-(Integer)GA[ga_handle].lo[i]+1 <= 0) chk = 0; nelem *= (hi[i]-(Integer)GA[ga_handle].lo[i]+1); } mem_size = nelem * GA[ga_handle].elemsize; if (!chk) mem_size = 0; grp_me = pnga_pgroup_nodeid(handle); /* Clean up old memory first */ #ifndef AVOID_MA_STORAGE if(gai_uses_shm((int)handle)){ #endif /* make sure that we free original (before address allignment) * pointer */ #ifdef MSG_COMMS_MPI if (GA[ga_handle].old_handle > 0){ ARMCI_Free_group( GA[ga_handle].ptr[pnga_pgroup_nodeid(GA[ga_handle].old_handle)] - GA[ga_handle].id, &PGRP_LIST[GA[ga_handle].old_handle].group); } else #endif { ARMCI_Free( GA[ga_handle].ptr[pnga_pgroup_nodeid(GA[ga_handle].old_handle)] - GA[ga_handle].id); } #ifndef AVOID_MA_STORAGE }else{ if(GA[ga_handle].id != INVALID_MA_HANDLE) MA_free_heap(GA[ga_handle].id); } #endif if(GA_memory_limited) GA_total_memory += GA[ga_handle].size; GAstat.curmem -= GA[ga_handle].size; /* if requested, enforce limits on memory consumption */ if(GA_memory_limited) GA_total_memory -= mem_size; GAstat.curmem += mem_size; /* check if everybody has enough memory left */ if(GA_memory_limited){ status = (GA_total_memory >= 0) ? 1 : 0; pnga_pgroup_gop(handle,pnga_type_f2c(MT_F_INT), &status, 1, "&&"); } else status = 1; /* allocate memory */ if (status) { /* Allocate new memory */ if (GA[ga_handle].mem_dev_set) { status = !gai_get_devmem(GA[ga_handle].name, GA[ga_handle].ptr,mem_size, GA[ga_handle].type, &GA[ga_handle].id, handle, GA[ga_handle].mem_dev_set,GA[ga_handle].mem_dev); } else { status = !gai_getmem(GA[ga_handle].name, GA[ga_handle].ptr,mem_size, GA[ga_handle].type, &GA[ga_handle].id, handle); } } else { GA[ga_handle].ptr[grp_me]=NULL; } GA[ga_handle].size = (C_Long)mem_size; if (!status) { pnga_error("Memory failure when unsetting READ_ONLY",0); } /* Copy data from copy of old GA to new GA and then get rid of copy*/ pnga_distribution(g_a,GAme,lo,hi); chk = 1; nelem = 1; for (i=0; iFNAM) { pnga_error("Illegal memory device name specified. Device name exceeds length: ", FNAM); } /* convert device name to lower case */ for (i=0; i len && ilen < FNAM) device[ilen] = '\0'; GA[ga_handle].mem_dev_set = 1; strcpy(GA[ga_handle].mem_dev,device); } /** * Clear property from global array. */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_unset_property = pnga_unset_property #endif void pnga_unset_property(Integer g_a) { Integer ga_handle = g_a + GA_OFFSET; if (GA[ga_handle].property == READ_ONLY) { /* TODO: Copy global array to original configuration */ int i, d, ndim, btot, chk; Integer g_tmp, grp_me; Integer nprocs, nodeid, origin_id, dflt_grp, handle, maplen; Integer nelem, mem_size, status; Integer *list; Integer dims[MAXDIM], chunk[MAXDIM]; Integer lo[MAXDIM], hi[MAXDIM], ld[MAXDIM]; void *ptr, *buf; ndim = (int)GA[ga_handle].ndim; /* Start by making a copy of the GA */ for (i=0; i 0){ ARMCI_Free_group( GA[ga_handle].ptr[pnga_pgroup_nodeid(GA[ga_handle].p_handle)] - GA[ga_handle].id, &PGRP_LIST[GA[ga_handle].p_handle].group); } else #endif { ARMCI_Free( GA[ga_handle].ptr[pnga_pgroup_nodeid(GA[ga_handle].p_handle)] - GA[ga_handle].id); } #ifndef AVOID_MA_STORAGE }else{ if(GA[ga_handle].id != INVALID_MA_HANDLE) MA_free_heap(GA[ga_handle].id); } #endif /* Reset distribution parameters back to original values */ btot = 0; for (i=0; i= 0) ? 1 : 0; pnga_pgroup_gop(GA[ga_handle].old_handle,pnga_type_f2c(MT_F_INT), &status, 1, "&&"); } else status = 1; handle = (Integer)GA[ga_handle].p_handle; GA[ga_handle].p_handle = GA[ga_handle].old_handle; if (status) { /* Allocate new memory */ if (GA[ga_handle].mem_dev_set) { status = !gai_get_devmem(GA[ga_handle].name, GA[ga_handle].ptr,mem_size, GA[ga_handle].type, &GA[ga_handle].id, GA[ga_handle].p_handle, GA[ga_handle].mem_dev_set,GA[ga_handle].mem_dev); } else { status = !gai_getmem(GA[ga_handle].name, GA[ga_handle].ptr,mem_size, GA[ga_handle].type, &GA[ga_handle].id, GA[ga_handle].p_handle); } } else { GA[ga_handle].ptr[grp_me]=NULL; } GA[ga_handle].size = (C_Long)mem_size; if (!status) { pnga_error("Memory failure when setting READ_ONLY",0); } /* Get rid of read-only group */ pnga_pgroup_destroy(handle); /* Generate parameters for new memory allocation. Distribution function * should work, so we can use that to find out how much data is on this * processor */ GA[ga_handle].property = NO_PROPERTY; pnga_distribution(g_a,GAme,lo,hi); chk = 1; nelem = 1; for (i=0; inext; if (GA[ga_handle].cache_head->cache_buf) free(GA[ga_handle].cache_head->cache_buf); free(GA[ga_handle].cache_head); while (next) { GA[ga_handle].cache_head = next; next = next->next; if (GA[ga_handle].cache_head->cache_buf) free(GA[ga_handle].cache_head->cache_buf); free(GA[ga_handle].cache_head); } } GA[ga_handle].cache_head = NULL; } else { GA[ga_handle].property = NO_PROPERTY; } } /** * Allocate memory and complete setup of global array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_allocate = pnga_allocate #endif logical pnga_allocate(Integer g_a) { Integer hi[MAXDIM]; Integer ga_handle = g_a + GA_OFFSET; Integer d, width[MAXDIM], ndim; Integer mem_size, nelem, pnum; Integer i, status, maplen=0, p_handle; Integer dims[MAXDIM], chunk[MAXDIM]; Integer pe[MAXDIM], *pmap[MAXDIM], *map; Integer blk[MAXDIM]; Integer grp_me=GAme, grp_nproc=GAnproc; Integer block_size = 0; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous sync masking*/ if (GA[ga_handle].ndim == -1) pnga_error("Insufficient data to create global array",0); p_handle = (Integer)GA[ga_handle].p_handle; if (p_handle == (Integer)GA_Init_Proc_Group) { GA[ga_handle].p_handle = GA_Default_Proc_Group; p_handle = GA_Default_Proc_Group; } pnga_pgroup_sync(p_handle); if (p_handle > 0) { grp_nproc = PGRP_LIST[p_handle].map_nproc; grp_me = PGRP_LIST[p_handle].map_proc_list[GAme]; } if(!GAinitialized) pnga_error("GA not initialized ", 0); if(!ma_address_init) gai_ma_address_init(); ndim = GA[ga_handle].ndim; for (i=0; i 1) { Integer ddim = ((dims[d]-1)/GA_MIN(chunk[d],dims[d]) + 1); pcut = (ddim -(blk[d]-1)*pe[d]) ; } else { pcut = (dims[d]-(blk[d]-1)*pe[d]) ; } for (nblock=i=p=0; (p= pcut) b = b-1; map[nblock] = i+1; if (chunk[d]>1) b *= GA_MIN(chunk[d],dims[d]); i += b; } pe[d] = GA_MIN(pe[d],nblock); map += pe[d]; } if(GAme==0&& DEBUG){ gai_print_subscript("pe ",(int)ndim, pe,"\n"); gai_print_subscript("blocks ",(int)ndim, blk,"\n"); printf("decomposition map\n"); for(d=0; d< ndim; d++){ printf("dim=%ld: ",(long)d); for (i=0;i GA[ga_handle].dims[i]) imax = GA[ga_handle].dims[i]; jtot += (imax-imin+1); } block_size *= jtot; } } else if (GA[ga_handle].distr_type == TILED) { /* Tiled data distribution has been specified. Figure out how much memory is needed by each processor to store blocks */ Integer j, jtot, skip, imin, imax, tot; Integer index[MAXDIM]; gam_find_tile_proc_indices(ga_handle,GAme,index); block_size = 1; tot = 1; for (i=0; i GA[ga_handle].dims[i]) imax = GA[ga_handle].dims[i]; jtot += (imax-imin+1); } block_size *= jtot; } } else if (GA[ga_handle].distr_type == TILED_IRREG) { /* Tiled data distribution has been specified. Figure out how much memory is needed by each processor to store blocks */ Integer j, jtot, skip, imin, imax, tot; Integer index[MAXDIM]; Integer offset = 0; gam_find_tile_proc_indices(ga_handle,GAme,index); block_size = 1; tot = 1; for (i=0; i= 0) ? 1 : 0; if (p_handle > 0) { /* pnga_pgroup_gop(p_handle,pnga_type_f2c(MT_F_INT), &status, 1, "*"); */ pnga_pgroup_gop(p_handle,pnga_type_f2c(MT_F_INT), &status, 1, "&&"); } else { /* pnga_gop(pnga_type_f2c(MT_F_INT), &status, 1, "*"); */ pnga_gop(pnga_type_f2c(MT_F_INT), &status, 1, "&&"); } }else status = 1; if (status) { if (GA[ga_handle].mem_dev_set) { status = !gai_get_devmem(GA[ga_handle].name, GA[ga_handle].ptr,mem_size, GA[ga_handle].type, &GA[ga_handle].id, p_handle, GA[ga_handle].mem_dev_set, GA[ga_handle].mem_dev); } else { status = !gai_getmem(GA[ga_handle].name, GA[ga_handle].ptr,mem_size, GA[ga_handle].type, &GA[ga_handle].id, p_handle); } } else { GA[ga_handle].ptr[grp_me]=NULL; } if (GA[ga_handle].distr_type == REGULAR) { /* Finish setting up information for ghost cell updates */ if (GA[ga_handle].ghosts == 1) { if (!pnga_set_ghost_info(g_a)) pnga_error("Could not allocate update information for ghost cells",0); } /* If array is mirrored, evaluate first and last indices */ /* ngai_get_first_last_indices(&g_a); */ } pnga_pgroup_sync(p_handle); if (status) { GAstat.curmem += (long)GA[ga_handle].size; GAstat.maxmem = (long)GA_MAX(GAstat.maxmem, GAstat.curmem); status = TRUE; } else { if(GA_memory_limited) GA_total_memory += mem_size; pnga_destroy(g_a); status = FALSE; } return status; } /** * Use memory from another GA and complete setup of global array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_overlay = pnga_overlay #endif logical pnga_overlay(Integer g_a, Integer g_parent) { Integer hi[MAXDIM]; Integer ga_handle = g_a + GA_OFFSET; Integer g_p = g_parent + GA_OFFSET; Integer d, width[MAXDIM], ndim; Integer mem_size, nelem; Integer i, status, maplen=0, p_handle; Integer dims[MAXDIM], chunk[MAXDIM]; Integer pe[MAXDIM], *pmap[MAXDIM], *map; Integer blk[MAXDIM]; Integer grp_me=GAme, grp_nproc=GAnproc; Integer block_size = 0; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous sync masking*/ if (GA[ga_handle].ndim == -1) pnga_error("Insufficient data to create global array",0); p_handle = (Integer)GA[ga_handle].p_handle; if (p_handle == (Integer)GA_Init_Proc_Group) { GA[ga_handle].p_handle = GA_Default_Proc_Group; p_handle = GA_Default_Proc_Group; } pnga_pgroup_sync(p_handle); if (p_handle != (Integer)GA[g_p].p_handle) { pnga_error("Parent and overlay global array must be on same procesor group",0); } if (p_handle > 0) { grp_nproc = PGRP_LIST[p_handle].map_nproc; grp_me = PGRP_LIST[p_handle].map_proc_list[GAme]; } if(!GAinitialized) pnga_error("GA not initialized ", 0); if(!ma_address_init) gai_ma_address_init(); ndim = GA[ga_handle].ndim; for (i=0; i 1) { Integer ddim = ((dims[d]-1)/GA_MIN(chunk[d],dims[d]) + 1); pcut = (ddim -(blk[d]-1)*pe[d]) ; } else { pcut = (dims[d]-(blk[d]-1)*pe[d]) ; } for (nblock=i=p=0; (p= pcut) b = b-1; map[nblock] = i+1; if (chunk[d]>1) b *= GA_MIN(chunk[d],dims[d]); i += b; } pe[d] = GA_MIN(pe[d],nblock); map += pe[d]; } if(GAme==0&& DEBUG){ gai_print_subscript("pe ",(int)ndim, pe,"\n"); gai_print_subscript("blocks ",(int)ndim, blk,"\n"); printf("decomposition map\n"); for(d=0; d< ndim; d++){ printf("dim=%ld: ",(long)d); for (i=0;i GA[ga_handle].dims[i]) imax = GA[ga_handle].dims[i]; jtot += (imax-imin+1); } block_size *= jtot; } } else if (GA[ga_handle].distr_type == TILED) { /* Tiled data distribution has been specified. Figure out how much memory is needed by each processor to store blocks */ Integer j, jtot, skip, imin, imax; Integer index[MAXDIM]; gam_find_tile_proc_indices(ga_handle,GAme,index); block_size = 1; for (i=0; i GA[ga_handle].dims[i]) imax = GA[ga_handle].dims[i]; jtot += (imax-imin+1); } block_size *= jtot; } } else if (GA[ga_handle].distr_type == TILED_IRREG) { /* Tiled data distribution has been specified. Figure out how much memory is needed by each processor to store blocks */ Integer j, jtot, skip, imin, imax; Integer index[MAXDIM]; Integer offset = 0; gam_find_tile_proc_indices(ga_handle,GAme,index); block_size = 1; for (i=0; i 0) { pnga_pgroup_gop(p_handle,pnga_type_f2c(MT_F_INT), &status, 1, "&&"); } else { pnga_gop(pnga_type_f2c(MT_F_INT), &status, 1, "&&"); } if (status) { GA[ga_handle].overlay = 1; GA[ga_handle].id = GA[g_p].id; for (i=0; i 0) { nproc = PGRP_LIST[grp_id].map_nproc; grp_me = PGRP_LIST[grp_id].map_proc_list[GAme]; } else nproc = GAnproc; /* need to enforce proper, natural allignment (on size boundary) */ switch (pnga_type_c2f(type)){ case MT_F_DBL: base = (char *) DBL_MB; break; case MT_F_INT: base = (char *) INT_MB; break; case MT_F_DCPL: base = (char *) DCPL_MB; break; case MT_F_SCPL: base = (char *) SCPL_MB; break; case MT_F_REAL: base = (char *) FLT_MB; break; default: base = (char*)0; } item_size = GAsizeofM(type); # ifdef GA_ELEM_PADDING bytes += (C_Long)item_size; # endif #endif *adj = 0; /* use ARMCI_Malloc_group for groups if proc group is not world group or mirror group */ #ifdef MSG_COMMS_MPI if (grp_id > 0) { status = ARMCI_Malloc_group((void**)ptr_arr, (armci_size_t)bytes, &PGRP_LIST[grp_id].group); } else { #endif status = ARMCI_Malloc((void**)ptr_arr, (armci_size_t)bytes); #ifdef MSG_COMMS_MPI } #endif if(bytes!=0 && ptr_arr[grp_me]==NULL) pnga_error("gai_get_shmem: ARMCI Malloc failed", GAme); if(status) return status; #ifndef _CHECK_MA_ALGN /* adjust all addresses if they are not alligned on corresponding nodes*/ /* we need storage for GAnproc*sizeof(Integer) */ /* JAD -- fixed bug where _ga_map was reused before gai_getmem was done * with it. Now malloc/free needed memory. */ adjust = (Integer*)malloc(GAnproc*sizeof(Integer)); diff = (GA_ABS( base - (char *) ptr_arr[grp_me])) % item_size; for(i=0;i 0) ? item_size - diff : 0; *adj = adjust[grp_me]; if (grp_id > 0) pnga_pgroup_gop(grp_id, pnga_type_f2c(MT_F_INT), adjust, nproc, "+"); else pnga_gop(pnga_type_f2c(MT_F_INT), adjust, nproc, "+"); for(i=0;i 0) return ARMCI_Uses_shm_grp(&PGRP_LIST[grp_id].group); else #endif return ARMCI_Uses_shm(); } int gai_getmem(char* name, char **ptr_arr, C_Long bytes, int type, long *id, int grp_id) { #ifdef AVOID_MA_STORAGE return gai_get_shmem(ptr_arr, bytes, type, id, grp_id); #else Integer handle = INVALID_MA_HANDLE, index; Integer nproc=GAnproc, grp_me=GAme, item_size = GAsizeofM(type); C_Long nelem; char *ptr = (char*)0; if (grp_id > 0) { nproc = PGRP_LIST[grp_id].map_nproc; grp_me = PGRP_LIST[grp_id].map_proc_list[GAme]; } if(gai_uses_shm(grp_id)) return gai_get_shmem(ptr_arr, bytes, type, id, grp_id); else{ nelem = bytes/((C_Long)item_size) + 1; if(bytes) if(MA_alloc_get(type, nelem, name, &handle, &index)){ MA_get_pointer(handle, &ptr);} *id = (long)handle; /* printf("bytes=%d ptr=%ld index=%d\n",bytes, ptr,index); fflush(stdout); */ bzero((char*)ptr_arr,(int)nproc*sizeof(char*)); ptr_arr[grp_me] = ptr; # ifndef _CHECK_MA_ALGN /* align */ { long diff, adjust; diff = ((unsigned long)ptr_arr[grp_me]) % item_size; adjust = (diff > 0) ? item_size - diff : 0; ptr_arr[grp_me] = adjust + (char*)ptr_arr[grp_me]; } # endif # ifdef MSG_COMMS_MPI if (grp_id > 0) { armci_exchange_address_grp((void**)ptr_arr,(int)nproc, &PGRP_LIST[grp_id].group); } else # endif armci_exchange_address((void**)ptr_arr,(int)nproc); if(bytes && !ptr) return 1; else return 0; } #endif /* AVOID_MA_STORAGE */ } /*\ get device memory alligned w.r.t. MA base * required on Linux as g77 ignores natural data alignment in common blocks \*/ int gai_get_devmem(char *name, char **ptr_arr, C_Long bytes, int type, long *adj, int grp_id, int dev_flag, const char *device) { int status=0; #ifndef _CHECK_MA_ALGN char *base; long diff, item_size; Integer *adjust; int i, nproc,grp_me=GAme; if (grp_id > 0) { nproc = PGRP_LIST[grp_id].map_nproc; grp_me = PGRP_LIST[grp_id].map_proc_list[GAme]; } else nproc = GAnproc; /* need to enforce proper, natural allignment (on size boundary) */ switch (pnga_type_c2f(type)){ case MT_F_DBL: base = (char *) DBL_MB; break; case MT_F_INT: base = (char *) INT_MB; break; case MT_F_DCPL: base = (char *) DCPL_MB; break; case MT_F_SCPL: base = (char *) SCPL_MB; break; case MT_F_REAL: base = (char *) FLT_MB; break; default: base = (char*)0; } item_size = GAsizeofM(type); # ifdef GA_ELEM_PADDING bytes += (C_Long)item_size; # endif #endif *adj = 0; /* use ARMCI_Malloc_group for groups if proc group is not world group or mirror group */ #ifdef MSG_COMMS_MPI if (grp_id > 0) { if (dev_flag) { status = ARMCI_Malloc_group_memdev((void**)ptr_arr, (armci_size_t)bytes, &PGRP_LIST[grp_id].group, device); } else { status = ARMCI_Malloc_group((void**)ptr_arr, (armci_size_t)bytes, &PGRP_LIST[grp_id].group); } } else { #endif if (dev_flag) { status = ARMCI_Malloc_memdev((void**)ptr_arr, (armci_size_t)bytes, device); } else { status = ARMCI_Malloc((void**)ptr_arr, (armci_size_t)bytes); } #ifdef MSG_COMMS_MPI } #endif if(bytes!=0 && ptr_arr[grp_me]==NULL) pnga_error("gai_get_shmem: ARMCI Malloc failed", GAme); if(status) return status; #ifndef _CHECK_MA_ALGN /* adjust all addresses if they are not alligned on corresponding nodes*/ /* we need storage for GAnproc*sizeof(Integer) */ /* JAD -- fixed bug where _ga_map was reused before gai_getmem was done * with it. Now malloc/free needed memory. */ adjust = (Integer*)malloc(GAnproc*sizeof(Integer)); diff = (GA_ABS( base - (char *) ptr_arr[grp_me])) % item_size; for(i=0;i 0) ? item_size - diff : 0; *adj = adjust[grp_me]; if (grp_id > 0) pnga_pgroup_gop(grp_id, pnga_type_f2c(MT_F_INT), adjust, nproc, "+"); else pnga_gop(pnga_type_f2c(MT_F_INT), adjust, nproc, "+"); for(i=0;i= 0) ? 1 : 0; /* pnga_gop(pnga_type_f2c(MT_F_INT), &status, 1, "*"); */ pnga_gop(pnga_type_f2c(MT_F_INT), &status, 1, "&&"); if(!status)GA_total_memory +=bytes+extra; }else status = 1; ptr_arr=malloc(GAnproc*sizeof(char**)); rc= gai_getmem("ga_getmem", ptr_arr,(Integer)bytes+extra, type, &id, grp_id); if(rc)pnga_error("ga_getmem: failed to allocate memory",bytes+extra); myptr = ptr_arr[GAme]; /* make sure that remote memory addresses point to user memory */ for(i=0; iid = id; ((getmem_t*)myptr)->type = type; ((getmem_t*)myptr)->size = bytes+extra; /* add ptr info */ memcpy(myptr+sizeof(getmem_t),ptr_arr,(size_t)GAnproc*sizeof(char**)); free(ptr_arr); return (void*)(myptr+extra); } void GA_Freemem(void *ptr) { int extra = sizeof(getmem_t)+GAnproc*sizeof(char*); getmem_t *info = (getmem_t *)((char*)ptr - extra); char **ptr_arr = (char**)(info+1); #ifndef AVOID_MA_STORAGE if(ARMCI_Uses_shm()){ #endif /* make sure that we free original (before address alignment) pointer */ ARMCI_Free(ptr_arr[GAme] - info->id); #ifndef AVOID_MA_STORAGE }else{ if(info->id != INVALID_MA_HANDLE) MA_free_heap(info->id); } #endif if(GA_memory_limited) GA_total_memory += info->size; } /** * Return coordinates of a GA patch associated with processor proc */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_distribution = pnga_distribution #endif void pnga_distribution(Integer g_a, Integer proc, Integer *lo, Integer * hi) { Integer ga_handle, lproc, old_grp; ga_check_handleM(g_a, "nga_distribution"); ga_handle = (GA_OFFSET + g_a); lproc = proc; if (GA[ga_handle].num_rstrctd > 0) { lproc = GA[ga_handle].rank_rstrctd[lproc]; } /* This currently assumes that read-only property can only be applied to * processors on the world group */ if (GA[ga_handle].property == READ_ONLY) { Integer node = pnga_cluster_proc_nodeid(proc); Integer nodesize = pnga_cluster_nprocs(node); lproc = proc%nodesize; } ga_ownsM(ga_handle, lproc, lo, hi); } /** * Check to see if array has ghost cells. */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_has_ghosts = pnga_has_ghosts #endif logical pnga_has_ghosts(Integer g_a) { int h_a = (int)g_a + GA_OFFSET; return GA[h_a].ghosts; } /** * Return the dimension of a Global Array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_ndim = pnga_ndim #endif Integer pnga_ndim(Integer g_a) { ga_check_handleM(g_a,"ga_ndim"); return GA[g_a +GA_OFFSET].ndim; } /** * Duplicate an existing global array * -- new array g_b will have properties of g_a * array_name - a character string [input] * g_a - Integer handle for reference array [input] * g_b - Integer handle for new array [output] \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_duplicate = pnga_duplicate #endif logical pnga_duplicate(Integer g_a, Integer *g_b, char* array_name) { char **save_ptr; C_Long mem_size, mem_size_proc; Integer i, ga_handle, status; int local_sync_begin,local_sync_end; Integer grp_id, grp_me=GAme; /* Integer grp_nproc=GAnproc; */ int maplen; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ grp_id = pnga_get_pgroup(g_a); if(local_sync_begin)pnga_pgroup_sync(grp_id); if (grp_id > 0) { /* grp_nproc = PGRP_LIST[grp_id].map_nproc; */ grp_me = PGRP_LIST[grp_id].map_proc_list[GAme]; } GAstat.numcre ++; ga_check_handleM(g_a,"ga_duplicate"); /* find a free global_array handle for g_b */ ga_handle =-1; i=0; do{ if(!GA[i].actv_handle) ga_handle=i; i++; }while(i<_max_global_array && ga_handle==-1); if( ga_handle == -1) pnga_error("ga_duplicate: too many arrays", (Integer)_max_global_array); *g_b = (Integer)ga_handle - GA_OFFSET; GA[ga_handle].actv_handle = 1; gai_init_struct(ga_handle); /*** copy content of the data structure ***/ save_ptr = GA[ga_handle].ptr; GA[ga_handle] = GA[GA_OFFSET + g_a]; /* <--- shallow copy */ strcpy(GA[ga_handle].name, array_name); GA[ga_handle].ptr = save_ptr; GA[ga_handle].distr_type = GA[GA_OFFSET + g_a].distr_type; maplen = calc_maplen(GA_OFFSET + g_a); if (maplen > 0) { GA[ga_handle].mapc = (C_Integer*)malloc((maplen+1)*sizeof(C_Integer*)); for(i=0;i 0) { GA[ga_handle].num_rstrctd = GA[GA_OFFSET + g_a].num_rstrctd; pnga_set_restricted(*g_b, GA[GA_OFFSET + g_a].rstrctd_list, GA[GA_OFFSET + g_a].num_rstrctd); } /*** Memory Allocation & Initialization of GA Addressing Space ***/ mem_size = mem_size_proc = GA[ga_handle].size; GA[ga_handle].id = INVALID_MA_HANDLE; /* if requested, enforce limits on memory consumption */ if(GA_memory_limited) GA_total_memory -= mem_size_proc; /* check if everybody has enough memory left */ if(GA_memory_limited){ status = (GA_total_memory >= 0) ? 1 : 0; if (grp_id > 0) { /* pnga_pgroup_gop(grp_id, pnga_type_f2c(MT_F_INT), &status, 1, "*"); */ pnga_pgroup_gop(grp_id, pnga_type_f2c(MT_F_INT), &status, 1, "&&"); status = (Integer)status; } else { /* pnga_gop(pnga_type_f2c(MT_F_INT), &status, 1, "*"); */ pnga_gop(pnga_type_f2c(MT_F_INT), &status, 1, "&&"); } }else status = 1; if(status) { if (GA[ga_handle].mem_dev_set) { status = !gai_get_devmem(array_name, GA[ga_handle].ptr,mem_size, (int)GA[ga_handle].type, &GA[ga_handle].id, (int)grp_id,GA[ga_handle].mem_dev_set,GA[ga_handle].mem_dev); } else { status = !gai_getmem(array_name, GA[ga_handle].ptr,mem_size, (int)GA[ga_handle].type, &GA[ga_handle].id, (int)grp_id); } } else{ GA[ga_handle].ptr[grp_me]=NULL; } if(local_sync_end)pnga_pgroup_sync(grp_id); # ifdef GA_CREATE_INDEF /* This code is incorrect. It needs to fixed if INDEF is ever used */ if(status){ Integer one = 1; Integer dim1 =(Integer)GA[ga_handle].dims[1], dim2=(Integer)GA[ga_handle].dims[2]; if(GAme==0)fprintf(stderr,"duplicate:initializing GA array%ld\n",g_b); if(GA[ga_handle].type == C_DBL) { double bad = (double) DBL_MAX; ga_fill_patch_(g_b, &one, &dim1, &one, &dim2, &bad); } else if (GA[ga_handle].type == C_INT) { int bad = (int) INT_MAX; ga_fill_patch_(g_b, &one, &dim1, &one, &dim2, &bad); } else if (GA[ga_handle].type == C_LONG) { long bad = LONG_MAX; ga_fill_patch_(g_b, &one, &dim1, &one, &dim2, &bad); } else if (GA[ga_handle].type == C_LONGLONG) { long long bad = LONG_MAX; ga_fill_patch_(g_b, &one, &dim1, &one, &dim2, &bad); } else if (GA[ga_handle].type == C_DCPL) { DoubleComplex bad = {DBL_MAX, DBL_MAX}; ga_fill_patch_(g_b, &one, &dim1, &one, &dim2, &bad); } else if (GA[ga_handle].type == C_SCPL) { SingleComplex bad = {FLT_MAX, FLT_MAX}; ga_fill_patch_(g_b, &one, &dim1, &one, &dim2, &bad); } else if (GA[ga_handle].type == C_FLOAT) { float bad = FLT_MAX; ga_fill_patch_(g_b, &one, &dim1, &one, &dim2, &bad); } else { pnga_error("ga_duplicate: type not supported ",GA[ga_handle].type); } } # endif if(status){ GAstat.curmem += (long)GA[ga_handle].size; GAstat.maxmem = (long)GA_MAX(GAstat.maxmem, GAstat.curmem); return(TRUE); }else{ if (GA_memory_limited) GA_total_memory += mem_size_proc; pnga_destroy(*g_b); return(FALSE); } } /*\ DUPLICATE A GLOBAL ARRAY -- memory comes from user * -- new array g_b will have properties of g_a \*/ int GA_Assemble_duplicate(int g_a, char* array_name, void* ptr) { char **save_ptr; int i, ga_handle; int extra = sizeof(getmem_t)+GAnproc*sizeof(char*); getmem_t *info = (getmem_t *)((char*)ptr - extra); char **ptr_arr = (char**)(info+1); int g_b; int maplen = calc_maplen(GA_OFFSET + g_a); pnga_sync(); GAstat.numcre ++; ga_check_handleM(g_a,"ga_assemble_duplicate"); /* find a free global_array handle for g_b */ ga_handle =-1; i=0; do{ if(!GA[i].actv_handle) ga_handle=i; i++; }while(i<_max_global_array && ga_handle==-1); if( ga_handle == -1) pnga_error("ga_assemble_duplicate: too many arrays ", (Integer)_max_global_array); g_b = ga_handle - GA_OFFSET; gai_init_struct(ga_handle); GA[ga_handle].actv_handle = 1; /*** copy content of the data structure ***/ save_ptr = GA[ga_handle].ptr; GA[ga_handle] = GA[GA_OFFSET + g_a]; strcpy(GA[ga_handle].name, array_name); GA[ga_handle].ptr = save_ptr; if (maplen > 0) { GA[ga_handle].mapc = (C_Integer*)malloc((maplen+1)*sizeof(C_Integer*)); for(i=0;itype)); GA[ga_handle].type = pnga_type_f2c(info->type); GA[ga_handle].size = (C_Long)info->size; GA[ga_handle].id = info->id; memcpy(GA[ga_handle].ptr,ptr_arr,(size_t)GAnproc*sizeof(char**)); GAstat.curmem += (long)GA[ga_handle].size; GAstat.maxmem = (long)GA_MAX(GAstat.maxmem, GAstat.curmem); pnga_sync(); return(g_b); } /** * Destroy a Global Array and clean up memory */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_destroy = pnga_destroy #endif logical pnga_destroy(Integer g_a) { Integer ga_handle = GA_OFFSET + g_a, grp_id, grp_me=GAme; int local_sync_begin,local_sync_end; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ grp_id = (Integer)GA[ga_handle].p_handle; if(local_sync_begin)pnga_pgroup_sync(grp_id); if (grp_id > 0) grp_me = PGRP_LIST[grp_id].map_proc_list[GAme]; else grp_me=GAme; GAstat.numdes ++; /*regardless of array status we count this call */ /* fails if handle is out of range or array not active */ if(ga_handle < 0 || ga_handle >= _max_global_array){ return FALSE; } if(GA[ga_handle].actv==0){ return FALSE; } if (GA[ga_handle].cache) free(GA[ga_handle].cache); GA[ga_handle].cache = NULL; GA[ga_handle].actv = 0; GA[ga_handle].actv_handle = 0; if (GA[ga_handle].num_rstrctd > 0) { GA[ga_handle].num_rstrctd = 0; if (GA[ga_handle].rstrctd_list) free(GA[ga_handle].rstrctd_list); GA[ga_handle].rstrctd_list = NULL; if (GA[ga_handle].rank_rstrctd) free(GA[ga_handle].rank_rstrctd); GA[ga_handle].rank_rstrctd = NULL; } if(GA[ga_handle].mapc != NULL){ free(GA[ga_handle].mapc); GA[ga_handle].mapc = NULL; } if (GA[ga_handle].property == READ_CACHE) { if (GA[ga_handle].cache_head != NULL) { cache_struct_t *next; next = GA[ga_handle].cache_head->next; if (GA[ga_handle].cache_head->cache_buf) free(GA[ga_handle].cache_head->cache_buf); free(GA[ga_handle].cache_head); while (next) { GA[ga_handle].cache_head = next; next = next->next; if (GA[ga_handle].cache_head->cache_buf) free(GA[ga_handle].cache_head->cache_buf); free(GA[ga_handle].cache_head); } } } GA[ga_handle].cache_head = NULL; if (GA[ga_handle].property == READ_ONLY) { free(GA[ga_handle].old_mapc); pnga_pgroup_destroy(GA[ga_handle].p_handle); } if(GA[ga_handle].ptr[grp_me]==NULL){ return TRUE; } if (!GA[ga_handle].overlay) { #ifndef AVOID_MA_STORAGE if(gai_uses_shm((int)grp_id)){ #endif /* make sure that we free original (before address allignment) pointer */ #ifdef MSG_COMMS_MPI if (grp_id > 0){ ARMCI_Free_group(GA[ga_handle].ptr[grp_me] - GA[ga_handle].id, &PGRP_LIST[grp_id].group); } else #endif if (GA[ga_handle].mem_dev_set) { ARMCI_Free_memdev(GA[ga_handle].ptr[GAme]-GA[ga_handle].id); } else { ARMCI_Free(GA[ga_handle].ptr[GAme] - GA[ga_handle].id); } #ifndef AVOID_MA_STORAGE }else{ if(GA[ga_handle].id != INVALID_MA_HANDLE) MA_free_heap(GA[ga_handle].id); } #endif if(GA_memory_limited) GA_total_memory += GA[ga_handle].size; GAstat.curmem -= GA[ga_handle].size; } else { GA[ga_handle].overlay = 0; } GA[ga_handle].mem_dev_set = 0; if(local_sync_end)pnga_pgroup_sync(grp_id); return(TRUE); } /** * Deallocate memory for a Global Array but leave other parameters. Allocate * can then be called on this handle again */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_deallocate = pnga_deallocate #endif logical pnga_deallocate(Integer g_a) { Integer ga_handle = GA_OFFSET + g_a, grp_id, grp_me=GAme; int local_sync_begin,local_sync_end; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ grp_id = (Integer)GA[ga_handle].p_handle; if(local_sync_begin)pnga_pgroup_sync(grp_id); if (grp_id > 0) grp_me = PGRP_LIST[grp_id].map_proc_list[GAme]; else grp_me=GAme; /* fails if handle is out of range or array not active */ if(ga_handle < 0 || ga_handle >= _max_global_array){ return FALSE; } if(GA[ga_handle].actv==0){ return FALSE; } if (GA[ga_handle].cache) free(GA[ga_handle].cache); GA[ga_handle].cache = NULL; GA[ga_handle].actv = 0; if (GA[ga_handle].property == READ_CACHE) { if (GA[ga_handle].cache_head != NULL) { cache_struct_t *next; next = GA[ga_handle].cache_head->next; if (GA[ga_handle].cache_head->cache_buf) free(GA[ga_handle].cache_head->cache_buf); free(GA[ga_handle].cache_head); while (next) { GA[ga_handle].cache_head = next; next = next->next; if (GA[ga_handle].cache_head->cache_buf) free(GA[ga_handle].cache_head->cache_buf); free(GA[ga_handle].cache_head); } } } GA[ga_handle].cache_head = NULL; if(GA[ga_handle].ptr[grp_me]==NULL){ return TRUE; } if (!GA[ga_handle].overlay) { #ifndef AVOID_MA_STORAGE if(gai_uses_shm((int)grp_id)){ #endif /* make sure that we free original (before address allignment) pointer */ #ifdef MSG_COMMS_MPI if (grp_id > 0){ ARMCI_Free_group(GA[ga_handle].ptr[grp_me] - GA[ga_handle].id, &PGRP_LIST[grp_id].group); } else #endif if (GA[ga_handle].mem_dev_set) { ARMCI_Free_memdev(GA[ga_handle].ptr[GAme]-GA[ga_handle].id); } else { ARMCI_Free(GA[ga_handle].ptr[GAme] - GA[ga_handle].id); } #ifndef AVOID_MA_STORAGE }else{ if(GA[ga_handle].id != INVALID_MA_HANDLE) MA_free_heap(GA[ga_handle].id); } #endif if(GA_memory_limited) GA_total_memory += GA[ga_handle].size; GAstat.curmem -= GA[ga_handle].size; } else { printf("Warning: Trying to deallocate an overlay array\n"); GA[ga_handle].overlay = 0; } if(local_sync_end)pnga_pgroup_sync(grp_id); return(TRUE); } /** * Terminate Global Array structures * * All GA arrays are destroyed & shared memory is dealocated * GA routines (except for ga_initialize) should not be called thereafter */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_terminate = pnga_terminate #endif void pnga_terminate() { //GA_Internal_Threadsafe_Lock(); Integer i, handle; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(!GAinitialized) { //GA_Internal_Threadsafe_Unlock(); return; } sai_terminate_sparse_arrays(); #ifdef PROFILE_OLD ga_profile_terminate(); #endif for (i=0;i<_max_global_array;i++){ handle = i - GA_OFFSET ; if(GA[i].actv) pnga_destroy(handle); if(GA[i].ptr) free(GA[i].ptr); if(GA[i].mapc) free(GA[i].mapc); } /* don't free groups list until all arrays destroyed */ for (i=0;i<_max_global_array;i++){ if(PGRP_LIST[i].actv) free(PGRP_LIST[i].map_proc_list); } pnga_sync(); GA_total_memory = -1; /* restore "unlimited" memory usage status */ GA_memory_limited = 0; gai_finalize_onesided(); free(mapALL); free(_ga_main_data_structure); free(_proc_list_main_data_structure); ARMCI_Free(GA_Update_Flags[GAme]); free(GA_Update_Flags); ARMCI_Free_local(GA_Update_Signal); pnga_sync(); ARMCI_Finalize(); #ifdef MSG_COMMS_MPI MPI_Comm_free(&GA_MPI_World_comm_dup); #endif ARMCIinitialized = 0; GAinitialized = 0; //GA_Internal_Threadsafe_Unlock(); } /** * Is array active or inactive */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_verify_handle = pnga_verify_handle #endif Integer pnga_verify_handle(Integer g_a) { return (Integer) ((g_a + GA_OFFSET>= 0) && (g_a + GA_OFFSET< _max_global_array) && GA[GA_OFFSET + (g_a)].actv); } /** * Fill array with random values in [0,val) */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_randomize = pnga_randomize #endif void pnga_randomize(Integer g_a, void* val) { int i,handle=GA_OFFSET + (int)g_a; char *ptr; int local_sync_begin,local_sync_end; C_Long elems; Integer grp_id; Integer num_blocks; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous sync masking*/ grp_id = pnga_get_pgroup(g_a); if(local_sync_begin)pnga_pgroup_sync(grp_id); ga_check_handleM(g_a, "ga_randomize"); gam_checktype(GA[handle].type); elems = GA[handle].size/((C_Long)GA[handle].elemsize); num_blocks = GA[handle].block_total; if (num_blocks < 0) { /* Bruce..Please CHECK if this is correct */ if (grp_id >= 0){ Integer grp_me = PGRP_LIST[GA[handle].p_handle].map_proc_list[GAme]; ptr = GA[handle].ptr[grp_me]; } else ptr = GA[handle].ptr[GAme]; switch (GA[handle].type){ /* case C_DCPL: for(i=0; i= 0){ Integer grp_me = PGRP_LIST[GA[handle].p_handle].map_proc_list[GAme]; ptr = GA[handle].ptr[grp_me]; } else ptr = GA[handle].ptr[GAme]; switch (GA[handle].type){ case C_DCPL: for(i=0; i= 0; i--) { ld *= block_grid[i]; *proc *= ld; *proc += index[i]; } *proc = *proc%pnga_nnodes(); } } #endif /*\ * RETURN HOW MANY PROCESSORS/OWNERS THERE ARE FOR THE SPECIFIED PATCH OF A * GLOBAL ARRAY \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_locate_nnodes = pnga_locate_nnodes #endif logical pnga_locate_nnodes( Integer g_a, Integer *lo, Integer *hi, Integer *np) /* g_a [input] global array handle lo [input] lower indices of patch in global array hi [input] upper indices of patch in global array np [output] total number of processors containing a portion of the patch For a block cyclic data distribution, this function returns a list of blocks that cover the region along with the lower and upper indices of each block. */ { int procT[MAXDIM], procB[MAXDIM], proc_subscript[MAXDIM]; Integer proc, i, ga_handle; Integer d, dpos, ndim, elems, use_blocks; /* Integer p_handle; */ ga_check_handleM(g_a, "nga_locate_nnodes"); ga_handle = GA_OFFSET + g_a; for(d = 0; d< GA[ga_handle].ndim; d++) if((lo[d]<1 || hi[d]>GA[ga_handle].dims[d]) ||(lo[d]>hi[d]))return FALSE; ndim = GA[ga_handle].ndim; if (GA[ga_handle].distr_type == REGULAR) { /* find "processor coordinates" for the top left corner and store them * in ProcT */ for(d = 0, dpos = 0; d< GA[ga_handle].ndim; d++){ findblock(GA[ga_handle].mapc + dpos, GA[ga_handle].nblock[d], GA[ga_handle].scale[d], lo[d], &procT[d]); dpos += GA[ga_handle].nblock[d]; } /* find "processor coordinates" for the right bottom corner and store * them in procB */ for(d = 0, dpos = 0; d< GA[ga_handle].ndim; d++){ findblock(GA[ga_handle].mapc + dpos, GA[ga_handle].nblock[d], GA[ga_handle].scale[d], hi[d], &procB[d]); dpos += GA[ga_handle].nblock[d]; } *np = 0; /* Find total number of processors containing data and return the * result in elems. Also find the lowest "processor coordinates" of the * processor block containing data and return these in proc_subscript. */ ga_InitLoopM(&elems, ndim, proc_subscript, procT,procB,GA[ga_handle].nblock); /* p_handle = (Integer)GA[ga_handle].p_handle; */ for(i= 0; i< elems; i++){ Integer _lo[MAXDIM], _hi[MAXDIM]; /* convert i to owner processor id using the current values in proc_subscript */ ga_ComputeIndexM(&proc, ndim, proc_subscript, GA[ga_handle].nblock); /* get range of global array indices that are owned by owner */ ga_ownsM(ga_handle, proc, _lo, _hi); /* Update to proc_subscript so that it corresponds to the next * processor in the block of processors containing the patch */ ga_UpdateSubscriptM(ndim,proc_subscript,procT,procB,GA[ga_handle].nblock); (*np)++; } } else { Integer nblocks = GA[ga_handle].block_total; Integer chk, j, tlo[MAXDIM], thi[MAXDIM], cnt; cnt = 0; for (i=0; i= lo[j] && tlo[j] <= hi[j]) || (thi[j] >= lo[j] && thi[j] <= hi[j]))) { chk = 0; } } /* store blocks that overlap request region in proclist */ if (chk) { cnt++; } } *np = cnt; } return(TRUE); } /** * Locate individual patches and their owner of specified patch of a * Global Array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_locate_region = pnga_locate_region #endif logical pnga_locate_region( Integer g_a, Integer *lo, Integer *hi, Integer *map, Integer *proclist, Integer *np) /* g_a [input] global array handle lo [input] lower indices of patch in global array hi [input] upper indices of patch in global array map [output] list of lower and upper indices for portion of patch that exists on each processor containing a portion of the patch. The map is constructed so that for a D dimensional global array, the first D elements are the lower indices on the first processor in proclist, the next D elements are the upper indices of the first processor in proclist, the next D elements are the lower indices for the second processor in proclist, and so on. proclist [output] list of processors containing some portion of the patch np [output] total number of processors containing a portion of the patch For a block cyclic data distribution, this function returns a list of blocks that cover the region along with the lower and upper indices of each block. */ { int procT[MAXDIM], procB[MAXDIM], proc_subscript[MAXDIM]; Integer proc, owner, i, ga_handle; Integer d, dpos, ndim, elems, use_blocks; /* Integer p_handle; */ ga_check_handleM(g_a, "nga_locate_region"); ga_handle = GA_OFFSET + g_a; for(d = 0; d< GA[ga_handle].ndim; d++) if((lo[d]<1 || hi[d]>GA[ga_handle].dims[d]) ||(lo[d]>hi[d]))return FALSE; ndim = GA[ga_handle].ndim; if (GA[ga_handle].distr_type == REGULAR) { /* find "processor coordinates" for the top left corner and store them * in ProcT */ for(d = 0, dpos = 0; d< GA[ga_handle].ndim; d++){ findblock(GA[ga_handle].mapc + dpos, GA[ga_handle].nblock[d], GA[ga_handle].scale[d], lo[d], &procT[d]); dpos += GA[ga_handle].nblock[d]; } /* find "processor coordinates" for the right bottom corner and store * them in procB */ for(d = 0, dpos = 0; d< GA[ga_handle].ndim; d++){ findblock(GA[ga_handle].mapc + dpos, GA[ga_handle].nblock[d], GA[ga_handle].scale[d], hi[d], &procB[d]); dpos += GA[ga_handle].nblock[d]; } *np = 0; /* Find total number of processors containing data and return the * result in elems. Also find the lowest "processor coordinates" of the * processor block containing data and return these in proc_subscript. */ ga_InitLoopM(&elems, ndim, proc_subscript, procT,procB,GA[ga_handle].nblock); /* p_handle = (Integer)GA[ga_handle].p_handle; */ for(i= 0; i< elems; i++){ Integer _lo[MAXDIM], _hi[MAXDIM]; Integer offset; /* convert i to owner processor id using the current values in proc_subscript */ ga_ComputeIndexM(&proc, ndim, proc_subscript, GA[ga_handle].nblock); /* get range of global array indices that are owned by owner */ ga_ownsM(ga_handle, proc, _lo, _hi); offset = *np *(ndim*2); /* location in map to put patch range */ for(d = 0; d< ndim; d++) map[d + offset ] = lo[d] < _lo[d] ? _lo[d] : lo[d]; for(d = 0; d< ndim; d++) map[ndim + d + offset ] = hi[d] > _hi[d] ? _hi[d] : hi[d]; owner = proc; if (GA[ga_handle].num_rstrctd == 0) { proclist[i] = owner; } else { proclist[i] = GA[ga_handle].rstrctd_list[owner]; } /* Update to proc_subscript so that it corresponds to the next * processor in the block of processors containing the patch */ ga_UpdateSubscriptM(ndim,proc_subscript,procT,procB,GA[ga_handle].nblock); (*np)++; } } else if (GA[ga_handle].distr_type == TILED_IRREG) { Integer nproc = pnga_pgroup_nnodes(GA[ga_handle].p_handle); /* find "processor coordinates" for the top left corner and store them * in ProcT */ for(d = 0, dpos = 0; d< GA[ga_handle].ndim; d++){ findblock(GA[ga_handle].mapc + dpos, GA[ga_handle].num_blocks[d], GA[ga_handle].scale[d], lo[d], &procT[d]); dpos += GA[ga_handle].num_blocks[d]; } /* find "processor coordinates" for the right bottom corner and store * them in procB */ for(d = 0, dpos = 0; d< GA[ga_handle].ndim; d++){ findblock(GA[ga_handle].mapc + dpos, GA[ga_handle].num_blocks[d], GA[ga_handle].scale[d], hi[d], &procB[d]); dpos += GA[ga_handle].num_blocks[d]; } *np = 0; /* Find total number of processors containing data and return the * result in elems. Also find the lowest "processor coordinates" of the * processor block containing data and return these in proc_subscript. */ ga_InitLoopM(&elems, ndim, proc_subscript, procT,procB,GA[ga_handle].num_blocks); /* p_handle = (Integer)GA[ga_handle].p_handle; */ for(i= 0; i< elems; i++){ Integer _lo[MAXDIM], _hi[MAXDIM]; Integer offset; /* convert i to owner processor id using the current values in proc_subscript */ ga_ComputeIndexM(&proc, ndim, proc_subscript, GA[ga_handle].num_blocks); proclist[i] = proc; /* get range of global array indices that are owned by owner */ ga_ownsM(ga_handle, proc, _lo, _hi); offset = *np *(ndim*2); /* location in map to put patch range */ for(d = 0; d< ndim; d++) map[d + offset ] = lo[d] < _lo[d] ? _lo[d] : lo[d]; for(d = 0; d< ndim; d++) map[ndim + d + offset ] = hi[d] > _hi[d] ? _hi[d] : hi[d]; /* Update to proc_subscript so that it corresponds to the next * processor in the block of processors containing the patch */ ga_UpdateSubscriptM(ndim,proc_subscript,procT,procB,GA[ga_handle].num_blocks); (*np)++; } } else if (GA[ga_handle].distr_type == BLOCK_CYCLIC) { Integer nblocks = GA[ga_handle].block_total; Integer chk, j, tlo[MAXDIM], thi[MAXDIM], cnt; Integer offset; cnt = 0; for (i=0; i= lo[j] && tlo[j] <= hi[j]) || (thi[j] >= lo[j] && thi[j] <= hi[j]))) { chk = 0; } } /* store blocks that overlap request region in proclist */ if (chk) { proclist[cnt] = i; cnt++; } } *np = cnt; /* fill map array with block coordinates */ for (i=0; i thi[j] ? thi[j] : hi[j]; } } } else if (GA[ga_handle].distr_type == SCALAPACK || GA[ga_handle].distr_type == TILED) { /* find min and max block coordinates of region */ Integer min[MAXDIM], max[MAXDIM]; Integer count[MAXDIM]; Integer total_blocks = 1; Integer cnt = 0; Integer offset; for (i=0; i=0; i--) { idx = idx*factor+count[i]; factor *= GA[ga_handle].num_blocks[i]; } proclist[cnt] = idx; /* store information on this block */ offset = 2*cnt*ndim; for (i=0; i GA[ga_handle].dims[i]) map[offset+ndim+i] = GA[ga_handle].dims[i]; } cnt++; /* Increment count array */ i = 0; count[0]++; while (count[i] > max[i] && i 0) { return (Integer)PGRP_LIST[GA_Default_Proc_Group].map_proc_list[GAme]; } else { return ((Integer)GAme); } } /** * Return ID of calling process in group grp */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_pgroup_nodeid = pnga_pgroup_nodeid #endif Integer pnga_pgroup_nodeid(Integer grp) { if (grp >= 0) { return (Integer)PGRP_LIST[(int)grp].map_proc_list[GAme]; } else { return GAme; } } /** * Return number of nodes in default group */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_nnodes = pnga_nnodes #endif Integer pnga_nnodes() { if (GA_Default_Proc_Group > 0) { return (Integer)PGRP_LIST[GA_Default_Proc_Group].map_nproc; } else { return ((Integer)GAnproc); } } /** * Return number of nodes in group grp */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_pgroup_nnodes = pnga_pgroup_nnodes #endif Integer pnga_pgroup_nnodes(Integer grp) { if(grp >=0 ) return (Integer)PGRP_LIST[(int)grp].map_nproc; else return ((Integer)GAnproc); } /** * Compare distributions of two global arrays */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_compare_distr = pnga_compare_distr #endif logical pnga_compare_distr(Integer g_a, Integer g_b) { int h_a =(int)g_a + GA_OFFSET; int h_b =(int)g_b + GA_OFFSET; int h_a_maplen = calc_maplen(h_a); int h_b_maplen = calc_maplen(h_b); int i; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ ga_check_handleM(g_a, "distribution a"); ga_check_handleM(g_b, "distribution b"); if(GA[h_a].ndim != GA[h_b].ndim) return FALSE; for(i=0; i 0) { for (i=0; i MAX_MUTEXES) return(FALSE); if(num_mutexes) pnga_error("mutexes already created",num_mutexes); num_mutexes= (int)num; if(GAnproc == 1){ return(TRUE); } chunk_mutex = (int)((num + GAnproc-1)/GAnproc); if(GAme * chunk_mutex >= num)myshare =0; else myshare=chunk_mutex; /* need work here to use permutation */ if(ARMCI_Create_mutexes(myshare)){ return FALSE; } return TRUE; } /** * Lock an object defined by the mutex number */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_lock = pnga_lock #endif void pnga_lock(Integer mutex) { int m,p; if(GAnproc == 1) return; if(num_mutexes< mutex)pnga_error("invalid mutex",mutex); p = num_mutexes/chunk_mutex -1; m = num_mutexes%chunk_mutex; ARMCI_Lock(m,p); } /** * Unlock a mutex */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_unlock = pnga_unlock #endif void pnga_unlock(Integer mutex) { int m,p; if(GAnproc == 1) return; if(num_mutexes< mutex)pnga_error("invalid mutex",mutex); p = num_mutexes/chunk_mutex -1; m = num_mutexes%chunk_mutex; ARMCI_Unlock(m,p); } /** * Destroy mutexes */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_destroy_mutexes = pnga_destroy_mutexes #endif logical pnga_destroy_mutexes() { _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(num_mutexes<1) pnga_error("mutexes destroyed",0); num_mutexes= 0; if(GAnproc == 1){ return TRUE; } if(ARMCI_Destroy_mutexes()){ return FALSE; } return TRUE; } /** * Return a list that maps GA process IDs to message-passing process IDs */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_list_nodeid = pnga_list_nodeid #endif void pnga_list_nodeid(Integer *list, Integer num_procs) { Integer proc; for( proc = 0; proc < num_procs; proc++) list[proc]=proc; } /** * Returns true/false depending on validity of the handle */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_valid_handle = pnga_valid_handle #endif logical pnga_valid_handle(Integer g_a) { if(GA_OFFSET+ (g_a) < 0 || GA_OFFSET+(g_a) >= _max_global_array || ! (GA[GA_OFFSET+(g_a)].actv) ) return FALSE; else return TRUE; } /** * A function that helps users avoid syncs inside a collective call * that they think are unnecessary * * Mask flags have to be reset in every collective call. Even if that * collective call doesnt do any sync at all. * If masking only the beginning sync is possible, make sure to * clear even the _sync_end mask to avoid a mask intended for this * collective_function_call to be carried to next collective_function_call * or to a collective function called by this function. * Similarly, make sure to use two copy mask values to local variables * and reset the global mask variables to avoid carring the mask to a * collective call inside the current collective call. */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_mask_sync = pnga_mask_sync #endif void pnga_mask_sync(Integer begin, Integer end) { if (begin) _ga_sync_begin = 1; else _ga_sync_begin = 0; if (end) _ga_sync_end = 1; else _ga_sync_end = 0; } /** * Merge all copies of a mirrored array by adding them together */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_merge_mirrored = pnga_merge_mirrored #endif void pnga_merge_mirrored(Integer g_a) { Integer handle = GA_OFFSET + g_a; Integer inode, nprocs, nnodes, zero, zproc, nblocks; int *blocks; C_Integer *map, *dims, *width; Integer i, j, index[MAXDIM], itmp, ndim; Integer lo[MAXDIM], hi[MAXDIM], ld[MAXDIM]; Integer nelem, count, type, atype=ARMCI_INT; char *zptr=NULL, *bptr=NULL, *nptr=NULL; Integer bytes, total; int local_sync_begin, local_sync_end; long bigint; int chk = 1; void *ptr_a; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end = 1; /*remove any previous masking */ if (local_sync_begin) pnga_sync(); /* don't perform update if node is not mirrored */ if (!pnga_is_mirrored(g_a)) return; inode = pnga_cluster_nodeid(); nnodes = pnga_cluster_nnodes(); nprocs = pnga_cluster_nprocs(inode); zero = 0; zproc = pnga_cluster_procid(inode, zero); zptr = GA[handle].ptr[zproc]; map = GA[handle].mapc; blocks = GA[handle].nblock; dims = GA[handle].dims; width = GA[handle].width; type = GA[handle].type; ndim = GA[handle].ndim; bigint = 2147483647L/GAsizeof(type); #ifdef OPENIB /* Check whether or not all nodes contain the same number of processors. */ if (nnodes*nprocs == pnga_nnodes()) { /* check to see if there is any buffer space between the data associated with each processor that needs to be zeroed out before performing the merge */ if (zproc == GAme) { /* the use of nblocks instead of nprocs is designed to support a peculiar coding style in which the dimensions of the block array are all set to 1 and all the data is restricted to the master processor on the node */ nblocks = 1; for (i=0; i total) len=((long)(total - istart)); /* printf("%ld step %d of %d len= %d total=%ld istart= %ld\n",GAme,(i+1),nsteps,len,total,istart); */ armci_msg_gop_scope(SCOPE_MASTERS, zptr+istart*GAsizeof(type), len, "+", atype); istart+=len; } } } } else { Integer _ga_tmp; Integer idims[MAXDIM], iwidth[MAXDIM], ichunk[MAXDIM]; void *one = NULL; double d_one = 1.0; int i_one = 1; float f_one = 1.0; long l_one = 1; double c_one[2]; float cf_one[2]; c_one[0] = 1.0; c_one[1] = 0.0; cf_one[0] = 1.0; cf_one[1] = 0.0; /* choose one as scaling factor in accumulate */ switch (type) { case C_FLOAT: one = &f_one; break; case C_DBL: one = &d_one; break; case C_LONG: one = &l_one; break; case C_INT: one = &i_one; break; case C_DCPL: one = &c_one; break; case C_SCPL: one = &cf_one; break; default: pnga_error("type not supported",type); } /* Nodes contain a mixed number of processors. Create a temporary GA to complete merge operation. */ count = 0; for (i=0; i 1) { pnga_error("Handle to a non-mirrored array passed",0); } else { trans[0] = 'N'; trans[1] = '\0'; pnga_copy_patch(trans, g_a, alo, ahi, g_b, blo, bhi); return; } } if (pnga_is_mirrored(g_b) && pnga_cluster_nnodes()) pnga_error("Distributed array is mirrored",0); adim = GA[a_handle].ndim; bdim = GA[b_handle].ndim; p_handle = GA[a_handle].p_handle; if (adim != bdim) pnga_error("Global arrays must have same dimension",0); type = GA[a_handle].type; if (type != GA[b_handle].type) pnga_error("Global arrays must be of same type",0); for (i=0; i= idim || ahi[i] < 0 || ahi[i] >= idim || alo[i] > ahi[i]) pnga_error("Invalid patch index on mirrored GA",0); } for (i=0; i= idim || bhi[i] < 0 || bhi[i] >= idim || blo[i] > bhi[i]) pnga_error("Invalid patch index on distributed GA",0); } for (i=0; i ahi[i]) intersect = 0; } if (intersect) { /* get portion of mirrored array patch that actually resides on this processor */ for (i=0; iGA[ga_handle].dims[d]) ||(lo[d]>hi[d])) pnga_error("Requested region out of bounds",0); if (GA[ga_handle].distr_type != REGULAR) { Integer nblocks = GA[ga_handle].block_total; Integer chk, i, j, tlo[MAXDIM], thi[MAXDIM]; cnt = 0; for (i=0; i= lo[j] && tlo[j] <= hi[j]) || (thi[j] >= lo[j] && thi[j] <= hi[j]))) { chk = 0; } } if (chk) { cnt++; } } ret = cnt; } return ret; } /** * Return the total number of blocks in a Global Array (if any). Only returns * non-zero values for block-cyclic data distributions. */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_total_blocks = pnga_total_blocks #endif Integer pnga_total_blocks(Integer g_a) { Integer ga_handle = GA_OFFSET + g_a; return GA[ga_handle].block_total; } /** * Return true if GA uses SCALPACK or TILED data distribution */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_uses_proc_grid = pnga_uses_proc_grid #endif logical pnga_uses_proc_grid(Integer g_a) { Integer ga_handle = GA_OFFSET + g_a; return (logical)(GA[ga_handle].distr_type == SCALAPACK || GA[ga_handle].distr_type == TILED || GA[ga_handle].distr_type == TILED_IRREG); } /** * Return true if GA uses IRREGULAR TILED data distribution */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_uses_irreg_proc_grid = pnga_uses_irreg_proc_grid #endif logical pnga_uses_irreg_proc_grid(Integer g_a) { Integer ga_handle = GA_OFFSET + g_a; return (GA[ga_handle].distr_type == TILED_IRREG); } /** * Return the index of a processor based on the block partition associated * with a particular Global Array, assuming GA uses some sort of block-cyclic * data distribution based on an underlying processor grid. (e.g. a * ScaLAPACK data distribution) */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_get_proc_index = pnga_get_proc_index #endif void pnga_get_proc_index(Integer g_a, Integer iproc, Integer *index) { Integer ga_handle = GA_OFFSET + g_a; if (GA[ga_handle].distr_type == SCALAPACK) { gam_find_proc_indices(ga_handle, iproc, index); } else if (GA[ga_handle].distr_type == TILED || GA[ga_handle].distr_type == TILED_IRREG) { gam_find_tile_proc_indices(ga_handle, iproc, index); } else { pnga_error("Global array does not use ScaLAPACK data distribution",0); } return; } /** * Return proc grid dimension and block dimension for a particular * Global Array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_get_block_info = pnga_get_block_info #endif void pnga_get_block_info(Integer g_a, Integer *num_blocks, Integer *block_dims) { Integer ga_handle = GA_OFFSET + g_a; Integer i, ndim; ndim = GA[ga_handle].ndim; if (GA[ga_handle].distr_type == SCALAPACK || GA[ga_handle].distr_type == TILED) { for (i=0; i 0) { if (dim%bsize == 0) { num_blocks[i] = dim/bsize; } else { num_blocks[i] = dim/bsize+1; } } else { num_blocks[i] = 0; } block_dims[i] = GA[ga_handle].block_dims[i]; } } return; } /** * Return pointers to map array and block dims for irregular * tiled distributions */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_get_map_info = pnga_get_map_info #endif void pnga_get_map_info(Integer g_a, Integer *num_blocks, Integer **map) { Integer ga_handle = GA_OFFSET + g_a; Integer i; for (i=0; i #include "ga-mpi.h" #endif extern int _max_global_array; extern Integer GAme, GAnproc; extern int GA_Default_Proc_Group; extern int** GA_Update_Flags; extern int* GA_Update_Signal; extern short int _ga_irreg_flag; extern Integer GA_Debug_flag; #define FNAM 31 /* length of array names */ #define CACHE_SIZE 512 /* size of the cache inside GA DS*/ /* #define USE_GA_MALLOC */ enum data_distribution {REGULAR, BLOCK_CYCLIC, SCALAPACK, TILED, TILED_IRREG}; typedef int ARMCI_Datatype; typedef struct { int mirrored; int map_nproc; int actv; int parent; int *map_proc_list; int *inv_map_proc_list; #ifdef MSG_COMMS_MPI ARMCI_Group group; #endif } proc_list_t; typedef Integer C_Integer; typedef armci_size_t C_Long; typedef struct cache_struct{ int lo[MAXDIM]; int hi[MAXDIM]; void* cache_buf; struct cache_struct *next; } cache_struct_t; typedef struct { short int ndim; /* number of dimensions */ short int irreg; /* 0-regular; 1-irregular distribution */ int type; /* data type in array */ int actv; /* activity status, GA is allocated */ int actv_handle; /* handle is created */ C_Long size; /* size of local data in bytes */ int elemsize; /* sizeof(datatype) */ int ghosts; /* flag indicating presence of ghosts */ long lock; /* lock */ long id; /* ID of shmem region / MA handle */ C_Integer dims[MAXDIM]; /* global array dimensions */ C_Integer chunk[MAXDIM]; /* chunking */ int nblock[MAXDIM]; /* number of blocks per dimension in */ /* processor grid */ C_Integer width[MAXDIM]; /* boundary cells per dimension */ C_Integer first[MAXDIM]; /* (Mirrored only) first local element */ C_Integer last[MAXDIM]; /* (Mirrored only) last local element */ C_Long shm_length; /* (Mirrored only) local shmem length */ C_Integer lo[MAXDIM]; /* top/left corner in local patch */ double scale[MAXDIM]; /* nblock/dim (precomputed) */ char **ptr; /* arrays of pointers to remote data */ C_Integer *mapc; /* block distribution map */ char name[FNAM+1]; /* array name */ int p_handle; /* pointer to processor list for array */ double *cache; /* store for frequently accessed ptrs */ int corner_flag; /* flag for updating corner ghost cells */ int distr_type; /* tag for data distribution type */ C_Integer block_dims[MAXDIM];/* array of block dimensions */ C_Integer num_blocks[MAXDIM];/* number of blocks in each dimension */ C_Integer block_total; /* total number of blocks in array */ /* using restricted arrays */ C_Integer *rstrctd_list; /* list of processors with data */ C_Integer num_rstrctd; /* number of processors with data */ C_Integer has_data; /* flag that processor has data */ C_Integer rstrctd_id; /* rank of processor in restricted list */ C_Integer *rank_rstrctd; /* ranks of processors with data */ /* Properties */ int property; /* property type for GA */ Integer *old_mapc; /* copy of original map */ int old_nblock[MAXDIM]; /* copy of original nblock array */ int old_handle; /* original group handle */ int old_lo[MAXDIM]; /* original lo array */ int old_chunk[MAXDIM]; /* original chunk array */ #ifdef ENABLE_CHECKPOINT int record_id; /* record id for writing ga to disk */ #endif /* new */ int read_cache; /* flag for read only pointer in cache */ cache_struct_t *cache_head; /* linked list of cached reads */ int mem_dev_set; /* flag for setting memory device */ char mem_dev[FNAM+1]; /* memory device type */ int overlay; /* GA uses memory from another GA */ } global_array_t; enum property_type { NO_PROPERTY, READ_ONLY, READ_CACHE /* new */ }; extern global_array_t *_ga_main_data_structure; extern proc_list_t *_proc_list_main_data_structure; /*\ *The following statement had to be moved here because of a problem in the c *compiler on SV1. The problem is that when a c file is compiled with a *-htaskprivate option on SV1, all global objects are given task-private status *even static variables are supposed to be initialized and given a task-private *memory/status. Somehow SV1 fails to do this for global variables that are *initialized during declaration. *So to handle that,we cannot initialize global variables to be able to run *on SV1. \*/ extern global_array_t *GA; extern proc_list_t *PGRP_LIST; /*\ * Copy of the world communicator used by GA so that applications using MPI * libraries will not use the same communicator that GA uses internally (at * least for the world communictor) \*/ #ifdef MSG_COMMS_MPI extern MPI_Comm GA_MPI_World_comm_dup; #endif #define ERR_STR_LEN 256 /* length of string for error reporting */ /**************************** MACROS ************************************/ #define ga_check_handleM(g_a, string) \ {\ if(GA_OFFSET+ (g_a) < 0 || GA_OFFSET+(g_a) >=_max_global_array){ \ char err_string[ERR_STR_LEN]; \ sprintf(err_string, "%s: INVALID ARRAY HANDLE", string); \ pnga_error(err_string, (g_a)); \ } \ if( ! (GA[GA_OFFSET+(g_a)].actv) ){ \ char err_string[ERR_STR_LEN]; \ sprintf(err_string, "%s: ARRAY NOT ACTIVE", string); \ pnga_error(err_string, (g_a)); \ } \ } /* this macro finds coordinates of the chunk of array owned by processor proc */ #define ga_ownsM_no_handle(ndim, dims, nblock, mapc, proc, lo, hi) \ { \ Integer _loc, _nb, _d, _index, _dim=ndim,_dimstart=0, _dimpos; \ for(_nb=1, _d=0; _d<_dim; _d++)_nb *= (Integer)nblock[_d]; \ if((Integer)proc > _nb - 1 || proc<0){ \ for(_d=0; _d<_dim; _d++){ \ lo[_d] = (Integer)0; \ hi[_d] = (Integer)-1;} \ } \ else{ \ _index = proc; \ for(_d=0; _d<_dim; _d++){ \ _loc = _index% (Integer)nblock[_d]; \ _index /= (Integer)nblock[_d]; \ _dimpos = _loc + _dimstart; /* correction to find place in mapc */\ _dimstart += (Integer)nblock[_d]; \ lo[_d] = (Integer)mapc[_dimpos]; \ if (_loc==nblock[_d]-1) hi[_d]=dims[_d]; \ else hi[_d] = mapc[_dimpos+1]-1; \ } \ } \ } /* this macro finds the block indices for a given block */ #define gam_find_block_indices(ga_handle,nblock,index) { \ int _itmp, _i; \ int _ndim = GA[ga_handle].ndim; \ _itmp = nblock; \ index[0] = _itmp%GA[ga_handle].num_blocks[0]; \ for (_i=1; _i<_ndim; _i++) { \ _itmp = (_itmp-index[_i-1])/GA[ga_handle].num_blocks[_i-1]; \ index[_i] = _itmp%GA[ga_handle].num_blocks[_i]; \ } \ } /* this macro finds the ScaLAPACK indices for a given processor */ /* gam_find_proc_indices(ga_handle,proc,index) */ #define gam_find_tile_proc_indices(ga_handle,proc,index) { \ Integer _itmp, _i; \ Integer _ndim = GA[ga_handle].ndim; \ _itmp = proc; \ index[0] = _itmp%GA[ga_handle].nblock[0]; \ for (_i=1; _i<_ndim; _i++) { \ _itmp = (_itmp-index[_i-1])/GA[ga_handle].nblock[_i-1]; \ index[_i] = _itmp%GA[ga_handle].nblock[_i]; \ } \ } /* #define gam_find_proc_indices(ga_handle,proc,index) { \ Integer _itmp, _i; \ Integer _ndim = GA[ga_handle].ndim; \ _itmp = proc; \ index[_ndim-1] = _itmp%GA[ga_handle].nblock[_ndim-1]; \ for (_i=_ndim-2; _i>=0; _i--) { \ _itmp = (_itmp-index[_i+1])/GA[ga_handle].nblock[_i+1]; \ index[_i] = _itmp%GA[ga_handle].nblock[_i]; \ } \ } */ #define gam_find_proc_indices(ga_handle,proc,index) { \ Integer _itmp, _i; \ Integer _ndim = GA[ga_handle].ndim; \ _itmp = proc; \ index[0] = _itmp%GA[ga_handle].nblock[0]; \ for (_i=1; _i<_ndim; _i++) { \ _itmp = (_itmp-index[_i-1])/GA[ga_handle].nblock[_i-1]; \ index[_i] = _itmp%GA[ga_handle].nblock[_i]; \ } \ } /* this macro finds cordinates of the chunk of array owned by processor proc * ga_handle: global array handle * proc: processor (or block) index * lo: lower indices of elements owned by processor (or block) * hi: upper indices of elements owned by processor (or block) */ #define ga_ownsM(ga_handle, proc, lo, hi) \ { \ if (GA[ga_handle].distr_type == REGULAR) { \ if (GA[ga_handle].num_rstrctd == 0) { \ ga_ownsM_no_handle(GA[ga_handle].ndim, GA[ga_handle].dims, \ GA[ga_handle].nblock, GA[ga_handle].mapc, \ proc,lo, hi ) \ } else { \ if (proc < GA[ga_handle].num_rstrctd) { \ ga_ownsM_no_handle(GA[ga_handle].ndim, GA[ga_handle].dims, \ GA[ga_handle].nblock, GA[ga_handle].mapc, \ proc,lo, hi ) \ } else { \ int _i; \ int _ndim = GA[ga_handle].ndim; \ for (_i=0; _i<_ndim; _i++) { \ lo[_i] = 0; \ hi[_i] = -1; \ } \ } \ } \ } else if (GA[ga_handle].distr_type == BLOCK_CYCLIC || \ GA[ga_handle].distr_type == SCALAPACK || \ GA[ga_handle].distr_type == TILED) { \ int _index[MAXDIM]; \ int _i; \ int _ndim = GA[ga_handle].ndim; \ gam_find_block_indices(ga_handle,proc,_index); \ for (_i=0; _i<_ndim; _i++) { \ lo[_i] = _index[_i]*GA[ga_handle].block_dims[_i]+1; \ hi[_i] = (_index[_i]+1)*GA[ga_handle].block_dims[_i]; \ if (hi[_i] > GA[ga_handle].dims[_i]) hi[_i]=GA[ga_handle].dims[_i]; \ } \ } else if (GA[ga_handle].distr_type == TILED_IRREG) { \ int _index[MAXDIM]; \ int _i; \ int _ndim = GA[ga_handle].ndim; \ int _offset = 0; \ gam_find_block_indices(ga_handle,proc,_index); \ for (_i=0; _i<_ndim; _i++) { \ lo[_i] = GA[ga_handle].mapc[_offset+_index[_i]]; \ if (_index[_i] < GA[ga_handle].num_blocks[_i]-1) { \ hi[_i] = GA[ga_handle].mapc[_offset+_index[_i]+1]-1; \ } else { \ hi[_i] = GA[ga_handle].dims[_i]; \ } \ _offset += GA[ga_handle].num_blocks[_i]; \ } \ } \ } /* this macro finds the block index corresponding to a given set of indices */ #define gam_find_block_from_indices(ga_handle,nblock,index) { \ int _ndim = GA[ga_handle].ndim; \ int _i; \ nblock = index[_ndim-1]; \ for (_i=_ndim-2; _i >= 0; _i--) { \ nblock = nblock*GA[ga_handle].num_blocks[_i]+index[_i]; \ } \ } /* this macro finds the proc that owns a given set block indices using the ScaLAPACK data distribution */ /* gam_find_proc_from_sl_indices(ga_handle,proc,index) */ #define gam_find_tile_proc_from_indices(ga_handle,proc,index) { \ int _ndim = GA[ga_handle].ndim; \ int _i; \ Integer _index2[MAXDIM]; \ for (_i=0; _i<_ndim; _i++) { \ _index2[_i] = index[_i]%GA[ga_handle].nblock[_i]; \ } \ proc = _index2[_ndim-1]; \ for (_i=_ndim-2; _i >= 0; _i--) { \ proc = proc*GA[ga_handle].nblock[_i]+_index2[_i]; \ } \ } #define gam_find_proc_from_sl_indices(ga_handle,proc,index) { \ int _ndim = GA[ga_handle].ndim; \ int _i; \ Integer _index2[MAXDIM]; \ for (_i=0; _i<_ndim; _i++) { \ _index2[_i] = index[_i]%GA[ga_handle].nblock[_i]; \ } \ proc = _index2[_ndim-1]; \ for (_i=_ndim-2; _i >= 0; _i--) { \ proc = proc*GA[ga_handle].nblock[_i]+_index2[_i]; \ } \ } /* this macro computes the strides on both the remote and local processors that map out the data. ld and ldrem are the physical dimensions of the memory on both the local and remote processors. */ /* NEEDS C_INT64 CONVERSION */ #define gam_setstride(ndim, size, ld, ldrem, stride_rem, stride_loc){\ int _i; \ stride_rem[0]= stride_loc[0] = (int)size; \ for(_i=0;_i 0) \ _iproc = GA[g_handle].rstrctd_list[_iproc]; \ *(ptr_loc) = GA[g_handle].ptr[_iproc]+_offset*GA[g_handle].elemsize; \ } #define ga_check_regionM(g_a, ilo, ihi, jlo, jhi, string){ \ if (*(ilo) <= 0 || *(ihi) > GA[GA_OFFSET + *(g_a)].dims[0] || \ *(jlo) <= 0 || *(jhi) > GA[GA_OFFSET + *(g_a)].dims[1] || \ *(ihi) < *(ilo) || *(jhi) < *(jlo)){ \ char err_string[ERR_STR_LEN]; \ sprintf(err_string,"%s:req(%ld:%ld,%ld:%ld) out of range (1:%ld,1:%ld)",\ string, (long)*(ilo), (long)*(ihi), (long)*(jlo), (long)*(jhi), \ (long)GA[GA_OFFSET + *(g_a)].dims[0], \ (long)GA[GA_OFFSET + *(g_a)].dims[1]); \ pnga_error(err_string, *(g_a)); \ } \ } #define gaCheckSubscriptM(subscr, lo, hi, ndim) \ { \ Integer _d; \ for(_d=0; _d< ndim; _d++) \ if( subscr[_d]< lo[_d] || subscr[_d]> hi[_d]){ \ char err_string[ERR_STR_LEN]; \ sprintf(err_string,"check subscript failed:%ld not in (%ld:%ld) dim=%d", \ (long)subscr[_d], (long)lo[_d], (long)hi[_d], (int)_d); \ pnga_error(err_string, _d); \ }\ } extern void pna_access_block_grid_ptr(Integer g_a, Integer *index, void *ptr, Integer ld); ga-5.9.2/global/src/capi.c000066400000000000000000004346501500715745200152470ustar00rootroot00000000000000/** * @file capi.c * * Implements the C interface. * These calls forward to the (possibly) weak symbols of the internal * implementations. */ #if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #include #include "armci.h" #include "ga.h" #include "globalp.h" #include "ga-papi.h" #include "matmul.h" #if ENABLE_PROFILING # include "ga-wapi.h" #else # include "ga-wapidefs.h" #endif #define USE_GATSCAT_NEW int *_ga_argc=NULL; char ***_ga_argv=NULL; int _ga_initialize_args=0; int _ga_initialize_c=0; short int _ga_irreg_flag = 0; static Integer* copy_map(int block[], int block_ndim, int map[]); static Integer* copy_map64(int64_t block[], int block_ndim, int64_t map[]); #ifdef USE_FAPI # define COPYC2F(carr, farr, n){\ int i; for(i=0; i< (n); i++)(farr)[i]=(Integer)(carr)[i];} # define COPYF2C(farr, carr, n){\ int i; for(i=0; i< (n); i++)(carr)[i]=(int)(farr)[i];} # define COPYF2C_64(farr, carr, n){\ int i; for(i=0; i< (n); i++)(carr)[i]=(int64_t)(farr)[i];} # define COPYINDEX_F2C COPYF2C # define COPYINDEX_F2C_64 COPYF2C_64 #else # define COPYC2F(carr, farr, n){\ int i; for(i=0; i< (n); i++)(farr)[n-i-1]=(Integer)(carr)[i];} # define COPYF2C(farr, carr, n){\ int i; for(i=0; i< (n); i++)(carr)[n-i-1]=(int)(farr)[i];} # define COPYF2C_64(farr, carr, n){\ int i; for(i=0; i< (n); i++)(carr)[n-i-1]=(int64_t)(farr)[i];} # define COPYINDEX_C2F(carr, farr, n){\ int i; for(i=0; i< (n); i++)(farr)[n-i-1]=(Integer)(carr)[i]+1;} # define COPYINDEX_F2C(farr, carr, n){\ int i; for(i=0; i< (n); i++)(carr)[n-i-1]=(int)(farr)[i] -1;} # define COPYINDEX_F2C_64(farr, carr, n){\ int i; for(i=0; i< (n); i++)(carr)[n-i-1]=(int64_t)(farr)[i] -1;} #define BASE_0 #endif #define COPY(CAST,src,dst,n) {\ int i; for(i=0; i< (n); i++)(dst)[i]=(CAST)(src)[i];} #define COPY_INC(CAST,src,dst,n) {\ int i; for(i=0; i< (n); i++)(dst)[i]=(CAST)(src)[i]+1;} #define COPY_DEC(CAST,src,dst,n) {\ int i; for(i=0; i< (n); i++)(dst)[i]=(CAST)(src)[i]-1;} int GA_Uses_fapi(void) { #ifdef USE_FAPI return 1; #else return 0; #endif } void GA_Initialize_ltd(size_t limit) { Integer lim = (Integer)limit; _ga_initialize_c = 1; wnga_initialize_ltd(lim); } void NGA_Initialize_ltd(size_t limit) { Integer lim = (Integer)limit; _ga_initialize_c = 1; wnga_initialize_ltd(lim); } void GA_Initialize_args(int *argc, char ***argv) { _ga_argc = argc; _ga_argv = argv; _ga_initialize_c = 1; _ga_initialize_args = 1; wnga_initialize(); } void GA_Initialize() { _ga_initialize_c = 1; wnga_initialize(); } void NGA_Initialize() { _ga_initialize_c = 1; wnga_initialize(); } int GA_Initialize_comm(MPI_Comm comm) { _ga_initialize_c = 1; return wnga_initialize_comm(comm); } int NGA_Initialize_comm(MPI_Comm comm) { _ga_initialize_c = 1; return wnga_initialize_comm(comm); } int GA_Initialized() { return wnga_initialized(); } int NGA_Initialized() { return wnga_initialized(); } void GA_Terminate() { wnga_terminate(); _ga_argc = NULL; _ga_argv = NULL; _ga_initialize_args = 0; _ga_initialize_c = 0; } void NGA_Terminate() { wnga_terminate(); _ga_argc = NULL; _ga_argv = NULL; _ga_initialize_args = 0; _ga_initialize_c = 0; } int NGA_Create(int type, int ndim, int dims[], char *name, int *chunk) { Integer *ptr, g_a; logical st; Integer _ga_work[MAXDIM]; Integer _ga_dims[MAXDIM]; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); if(!chunk)ptr=(Integer*)0; else { COPYC2F(chunk,_ga_work, ndim); ptr = _ga_work; } st = wnga_create((Integer)type, (Integer)ndim, _ga_dims, name, ptr, &g_a); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create64(int type, int ndim, int64_t dims[], char *name, int64_t *chunk) { Integer *ptr, g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); if(!chunk)ptr=(Integer*)0; else { COPYC2F(chunk,_ga_work, ndim); ptr = _ga_work; } st = wnga_create((Integer)type, (Integer)ndim, _ga_dims, name, ptr, &g_a); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create_config(int type, int ndim, int dims[], char *name, int chunk[], int p_handle) { Integer *ptr, g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); if(!chunk)ptr=(Integer*)0; else { COPYC2F(chunk,_ga_work, ndim); ptr = _ga_work; } st = wnga_create_config((Integer)type, (Integer)ndim, _ga_dims, name, ptr, (Integer)p_handle, &g_a); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create_config64(int type, int ndim, int64_t dims[], char *name, int64_t chunk[], int p_handle) { Integer *ptr, g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); if(!chunk)ptr=(Integer*)0; else { COPYC2F(chunk,_ga_work, ndim); ptr = _ga_work; } st = wnga_create_config((Integer)type, (Integer)ndim, _ga_dims, name, ptr, (Integer)p_handle, &g_a); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create_irreg(int type,int ndim,int dims[],char *name,int block[],int map[]) { Integer g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; Integer *_ga_map_capi; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); COPYC2F(block,_ga_work, ndim); _ga_map_capi = copy_map(block, ndim, map); _ga_irreg_flag = 1; /* set this flag=1, to indicate array is irregular */ st = wnga_create_irreg(type, (Integer)ndim, _ga_dims, name, _ga_map_capi, _ga_work, &g_a); _ga_irreg_flag = 0; /* unset it after creating the array */ free(_ga_map_capi); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create_irreg64(int type,int ndim,int64_t dims[],char *name,int64_t block[],int64_t map[]) { Integer g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; Integer *_ga_map_capi; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); COPYC2F(block,_ga_work, ndim); _ga_map_capi = copy_map64(block, ndim, map); _ga_irreg_flag = 1; /* set this flag=1, to indicate array is irregular */ st = wnga_create_irreg(type, (Integer)ndim, _ga_dims, name, _ga_map_capi, _ga_work, &g_a); _ga_irreg_flag = 0; /* unset it after creating the array */ free(_ga_map_capi); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create_irreg_config(int type,int ndim,int dims[],char *name,int block[], int map[], int p_handle) { Integer g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; Integer *_ga_map_capi; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); COPYC2F(block,_ga_work, ndim); _ga_map_capi = copy_map(block, ndim, map); _ga_irreg_flag = 1; /* set this flag=1, to indicate array is irregular */ st = wnga_create_irreg_config(type, (Integer)ndim, _ga_dims, name, _ga_map_capi, _ga_work, (Integer)p_handle, &g_a); _ga_irreg_flag = 0; /* unset it, after creating array */ free(_ga_map_capi); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create_irreg_config64(int type,int ndim,int64_t dims[],char *name,int64_t block[], int64_t map[], int p_handle) { Integer g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; Integer *_ga_map_capi; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); COPYC2F(block,_ga_work, ndim); _ga_map_capi = copy_map64(block, ndim, map); _ga_irreg_flag = 1; /* set this flag=1, to indicate array is irregular */ st = wnga_create_irreg_config(type, (Integer)ndim, _ga_dims, name, _ga_map_capi, _ga_work, (Integer)p_handle, &g_a); _ga_irreg_flag = 0; /* unset it, after creating array */ free(_ga_map_capi); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create_ghosts_irreg(int type,int ndim,int dims[],int width[],char *name, int block[],int map[]) { Integer g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; Integer _ga_width[MAXDIM]; Integer *_ga_map_capi; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); COPYC2F(block,_ga_work, ndim); COPYC2F(width,_ga_width, ndim); _ga_map_capi = copy_map(block, ndim, map); _ga_irreg_flag = 1; /* set this flag=1, to indicate array is irregular */ st = wnga_create_ghosts_irreg(type, (Integer)ndim, _ga_dims, _ga_width, name, _ga_map_capi, _ga_work, &g_a); _ga_irreg_flag = 0; /* unset it, after creating array */ free(_ga_map_capi); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create_ghosts_irreg64(int type,int ndim,int64_t dims[],int64_t width[],char *name, int64_t block[],int64_t map[]) { Integer g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; Integer _ga_width[MAXDIM]; Integer *_ga_map_capi; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); COPYC2F(block,_ga_work, ndim); COPYC2F(width,_ga_width, ndim); _ga_map_capi = copy_map64(block, ndim, map); _ga_irreg_flag = 1; /* set this flag=1, to indicate array is irregular */ st = wnga_create_ghosts_irreg(type, (Integer)ndim, _ga_dims, _ga_width, name, _ga_map_capi, _ga_work, &g_a); _ga_irreg_flag = 0; /* unset it, after creating array */ free(_ga_map_capi); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create_ghosts_irreg_config(int type, int ndim, int dims[], int width[], char *name, int block[], int map[], int p_handle) { Integer g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; Integer _ga_width[MAXDIM]; Integer *_ga_map_capi; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); COPYC2F(block,_ga_work, ndim); COPYC2F(width,_ga_width, ndim); _ga_map_capi = copy_map(block, ndim, map); _ga_irreg_flag = 1; /* set this flag=1, to indicate array is irregular */ st = wnga_create_ghosts_irreg_config(type, (Integer)ndim, _ga_dims, _ga_width, name, _ga_map_capi, _ga_work, (Integer)p_handle, &g_a); _ga_irreg_flag = 0; /* unset it, after creating array */ free(_ga_map_capi); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create_ghosts_irreg_config64(int type, int ndim, int64_t dims[], int64_t width[], char *name, int64_t block[], int64_t map[], int p_handle) { Integer g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; Integer _ga_width[MAXDIM]; Integer *_ga_map_capi; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); COPYC2F(block,_ga_work, ndim); COPYC2F(width,_ga_width, ndim); _ga_map_capi = copy_map64(block, ndim, map); _ga_irreg_flag = 1; /* set this flag=1, to indicate array is irregular */ st = wnga_create_ghosts_irreg_config(type, (Integer)ndim, _ga_dims, _ga_width, name, _ga_map_capi, _ga_work, (Integer)p_handle, &g_a); _ga_irreg_flag = 0; /* unset it, after creating array */ free(_ga_map_capi); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create_ghosts(int type, int ndim,int dims[], int width[], char *name, int chunk[]) { Integer *ptr, g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; Integer _ga_width[MAXDIM]; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); COPYC2F(width,_ga_width, ndim); if(!chunk)ptr=(Integer*)0; else { COPYC2F(chunk,_ga_work, ndim); ptr = _ga_work; } st = wnga_create_ghosts((Integer)type, (Integer)ndim, _ga_dims, _ga_width, name, ptr, &g_a); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create_ghosts64(int type, int ndim, int64_t dims[], int64_t width[], char *name, int64_t chunk[]) { Integer *ptr, g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; Integer _ga_width[MAXDIM]; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); COPYC2F(width,_ga_width, ndim); if(!chunk)ptr=(Integer*)0; else { COPYC2F(chunk,_ga_work, ndim); ptr = _ga_work; } st = wnga_create_ghosts((Integer)type, (Integer)ndim, _ga_dims, _ga_width, name, ptr, &g_a); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create_ghosts_config(int type, int ndim,int dims[], int width[], char *name, int chunk[], int p_handle) { Integer *ptr, g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; Integer _ga_width[MAXDIM]; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); COPYC2F(width,_ga_width, ndim); if(!chunk)ptr=(Integer*)0; else { COPYC2F(chunk,_ga_work, ndim); ptr = _ga_work; } st = wnga_create_ghosts_config((Integer)type, (Integer)ndim, _ga_dims, _ga_width, name, ptr, (Integer)p_handle, &g_a); if(st==TRUE) return (int) g_a; else return 0; } int NGA_Create_ghosts_config64(int type, int ndim,int64_t dims[], int64_t width[], char *name, int64_t chunk[], int p_handle) { Integer *ptr, g_a; logical st; Integer _ga_dims[MAXDIM]; Integer _ga_work[MAXDIM]; Integer _ga_width[MAXDIM]; if(ndim>MAXDIM)return 0; COPYC2F(dims,_ga_dims, ndim); COPYC2F(width,_ga_width, ndim); if(!chunk)ptr=(Integer*)0; else { COPYC2F(chunk,_ga_work, ndim); ptr = _ga_work; } st = wnga_create_ghosts_config((Integer)type, (Integer)ndim, _ga_dims, _ga_width, name, ptr, (Integer)p_handle, &g_a); if(st==TRUE) return (int) g_a; else return 0; } int GA_Create_handle() { Integer g_a; g_a = wnga_create_handle(); return (int)g_a; } int NGA_Create_handle() { Integer g_a; g_a = wnga_create_handle(); return (int)g_a; } void GA_Set_data(int g_a, int ndim, int dims[], int type) { Integer aa, nndim, ttype; Integer _ga_dims[MAXDIM]; COPYC2F(dims,_ga_dims, ndim); aa = (Integer)g_a; nndim = (Integer)ndim; ttype = (Integer)type; wnga_set_data(aa, nndim, _ga_dims, ttype); } void GA_Set_data64(int g_a, int ndim, int64_t dims[], int type) { Integer aa, nndim, ttype; Integer _ga_dims[MAXDIM]; COPYC2F(dims,_ga_dims, ndim); aa = (Integer)g_a; nndim = (Integer)ndim; ttype = (Integer)type; wnga_set_data(aa, nndim, _ga_dims, ttype); } void NGA_Set_data(int g_a, int ndim, int dims[], int type) { Integer aa, nndim, ttype; Integer _ga_dims[MAXDIM]; COPYC2F(dims,_ga_dims, ndim); aa = (Integer)g_a; nndim = (Integer)ndim; ttype = (Integer)type; wnga_set_data(aa, nndim, _ga_dims, ttype); } void NGA_Set_data64(int g_a, int ndim, int64_t dims[], int type) { Integer aa, nndim, ttype; Integer _ga_dims[MAXDIM]; COPYC2F(dims,_ga_dims, ndim); aa = (Integer)g_a; nndim = (Integer)ndim; ttype = (Integer)type; wnga_set_data(aa, nndim, _ga_dims, ttype); } void GA_Set_chunk(int g_a, int chunk[]) { Integer aa, *ptr, ndim; Integer _ga_work[MAXDIM]; aa = (Integer)g_a; ndim = wnga_get_dimension(aa); if(!chunk)ptr=(Integer*)0; else { COPYC2F(chunk,_ga_work, ndim); ptr = _ga_work; } wnga_set_chunk(aa, ptr); } void GA_Set_chunk64(int g_a, int64_t chunk[]) { Integer aa, *ptr, ndim; Integer _ga_work[MAXDIM]; aa = (Integer)g_a; ndim = wnga_get_dimension(aa); if(!chunk)ptr=(Integer*)0; else { COPYC2F(chunk,_ga_work, ndim); ptr = _ga_work; } wnga_set_chunk(aa, ptr); } void NGA_Set_chunk(int g_a, int chunk[]) { Integer aa, *ptr, ndim; Integer _ga_work[MAXDIM]; aa = (Integer)g_a; ndim = wnga_get_dimension(aa); if(!chunk)ptr=(Integer*)0; else { COPYC2F(chunk,_ga_work, ndim); ptr = _ga_work; } wnga_set_chunk(aa, ptr); } void NGA_Set_chunk64(int g_a, int64_t chunk[]) { Integer aa, *ptr, ndim; Integer _ga_work[MAXDIM]; aa = (Integer)g_a; ndim = wnga_get_dimension(aa); if(!chunk)ptr=(Integer*)0; else { COPYC2F(chunk,_ga_work, ndim); ptr = _ga_work; } wnga_set_chunk(aa, ptr); } void GA_Set_array_name(int g_a, char *name) { Integer aa; aa = (Integer)g_a; wnga_set_array_name(aa, name); } void NGA_Set_array_name(int g_a, char *name) { Integer aa; aa = (Integer)g_a; wnga_set_array_name(aa, name); } void GA_Get_array_name(int g_a, char *name) { Integer aa; aa = (Integer)g_a; wnga_get_array_name(aa, name); } void NGA_Get_array_name(int g_a, char *name) { Integer aa; aa = (Integer)g_a; wnga_get_array_name(aa, name); } void GA_Set_pgroup(int g_a, int p_handle) { Integer aa, pp; aa = (Integer)g_a; pp = (Integer)p_handle; wnga_set_pgroup(aa, pp); } void NGA_Set_pgroup(int g_a, int p_handle) { Integer aa, pp; aa = (Integer)g_a; pp = (Integer)p_handle; wnga_set_pgroup(aa, pp); } void GA_Set_block_cyclic(int g_a, int dims[]) { Integer aa, ndim; Integer _ga_dims[MAXDIM]; aa = (Integer)g_a; ndim = wnga_get_dimension(aa); COPYC2F(dims,_ga_dims, ndim); wnga_set_block_cyclic(aa, _ga_dims); } void NGA_Set_block_cyclic(int g_a, int dims[]) { Integer aa, ndim; Integer _ga_dims[MAXDIM]; aa = (Integer)g_a; ndim = wnga_get_dimension(aa); COPYC2F(dims,_ga_dims, ndim); wnga_set_block_cyclic(aa, _ga_dims); } void GA_Set_block_cyclic64(int g_a, int64_t dims[]) { Integer aa, ndim; Integer _ga_dims[MAXDIM]; aa = (Integer)g_a; ndim = wnga_get_dimension(aa); COPYC2F(dims,_ga_dims, ndim); wnga_set_block_cyclic(aa, _ga_dims); } void NGA_Set_block_cyclic64(int g_a, int64_t dims[]) { Integer aa, ndim; Integer _ga_dims[MAXDIM]; aa = (Integer)g_a; ndim = wnga_get_dimension(aa); COPYC2F(dims,_ga_dims, ndim); wnga_set_block_cyclic(aa, _ga_dims); } void GA_Set_restricted(int g_a, int list[], int size) { Integer aa; Integer asize = (Integer)size; int i; Integer *_ga_map_capi; aa = (Integer)g_a; _ga_map_capi = (Integer*)malloc(size * sizeof(Integer)); for (i=0; i #endif /* $Id: collect.c,v 1.23.2.5 2007-08-03 19:52:28 manoj Exp $ */ #include "typesf2c.h" #include "globalp.h" #include "message.h" #include "base.h" #include "ga-papi.h" #include "ga-wapi.h" /* can handle ga_brdcst/igop/dgop via ARMCI or native message-passing library * uncomment line below to use the ARMCI version */ #ifdef MSG_COMMS_MPI # include extern MPI_Comm ARMCI_COMM_WORLD; # include "ga-mpi.h" # if HAVE_ARMCI_GROUP_COMM extern MPI_Comm armci_group_comm(ARMCI_Group *group); # endif #else # include #endif #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_msg_brdcst = pnga_msg_brdcst #endif void pnga_msg_brdcst(Integer type, void *buffer, Integer len, Integer root) { long bigint = 2147483647L; int i, nsteps;//= (int) ceil(((double)len)/((double)bigint)); long len_small; void *buffer_ptr; long istart=0; int p_grp; /* printf("%ld len %ld bigint %ld \n",GAme,len,bigint); */ nsteps = (int) ceil(((double)len)/((double)bigint)); /* printf("%ld len %ld bigint %ld nsteps %d \n",GAme,len,bigint,nsteps); */ for (i=0; i < nsteps; i++){ len_small=bigint; buffer_ptr=(char *)buffer+istart; if (istart+len_small > len) len_small=((long)(len - istart)); /* printf("%ld step %d of %d len= %d total=%ld istart= %ld\n",GAme,(i+1),nsteps,len_small,len,istart); */ p_grp = (int)pnga_pgroup_get_default(); if (p_grp > 0) { # ifdef MSG_COMMS_MPI int aroot = PGRP_LIST[p_grp].inv_map_proc_list[root]; armci_msg_group_bcast_scope(SCOPE_ALL,buffer_ptr, (int)len_small, aroot,(&(PGRP_LIST[p_grp].group))); # endif } else { armci_msg_bcast(buffer_ptr, (int)len_small, (int)root); } istart+=len_small; } } /*\ BROADCAST \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_brdcst = pnga_brdcst #endif void pnga_brdcst(Integer type, void *buf, Integer len, Integer originator) { _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ pnga_msg_brdcst(type,buf,len,originator); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_pgroup_brdcst = pnga_pgroup_brdcst #endif void pnga_pgroup_brdcst(Integer grp_id, Integer type, void *buf, Integer len, Integer originator) { int p_grp = (int)grp_id; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if (p_grp > 0) { #ifdef MSG_COMMS_MPI int aroot = PGRP_LIST[p_grp].inv_map_proc_list[originator]; armci_msg_group_bcast_scope(SCOPE_ALL,buf,(int)len,aroot,(&(PGRP_LIST[p_grp].group))); #endif } else { int aroot = (int)originator; armci_msg_bcast(buf, (int)len, (int)aroot); } } #ifdef MSG_COMMS_MPI MPI_Comm GA_MPI_Comm() { /*return GA_MPI_Comm_pgroup(-1); */ return GA_MPI_World_comm_dup; } MPI_Comm GA_MPI_Comm_pgroup_default() { return GA_MPI_Comm_pgroup(pnga_pgroup_get_default()); } MPI_Comm GA_MPI_Comm_pgroup(int p_grp) { ARMCI_Group group; if (p_grp > 0) { group = PGRP_LIST[p_grp].group; } else { ARMCI_Group_get_world(&group); } # if HAVE_ARMCI_GROUP_COMM_MEMBER return group.comm; # else return armci_group_comm(&group); # endif } #endif #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_msg_sync = pnga_msg_sync #endif void pnga_msg_sync() { #ifdef MSG_COMMS_MPI int p_grp = (int)pnga_pgroup_get_default(); if(p_grp>0) armci_msg_group_barrier(&(PGRP_LIST[p_grp].group)); else armci_msg_barrier(); #else tcg_synch(GA_TYPE_SYN); #endif } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_msg_pgroup_sync = pnga_msg_pgroup_sync #endif void pnga_msg_pgroup_sync(Integer grp_id) { int p_grp = (int)(grp_id); if(p_grp>0) { # ifdef MSG_COMMS_MPI armci_msg_group_barrier(&(PGRP_LIST[p_grp].group)); # else pnga_error("ga_msg_pgroup_sync not implemented",0); # endif } else { # if defined(MSG_COMMS_MPI) armci_msg_barrier(); # else tcg_synch(GA_TYPE_SYN); # endif } } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_pgroup_gop = pnga_pgroup_gop #endif void pnga_pgroup_gop(Integer p_grp, Integer type, void *x, Integer n, char *op) { _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if (p_grp > 0) { #if defined(MSG_COMMS_MPI) int group = (int)p_grp; switch (type){ case C_INT: armci_msg_group_igop((int*)x, n, op, (&(PGRP_LIST[group].group))); break; case C_LONG: armci_msg_group_lgop((long*)x, n, op, (&(PGRP_LIST[group].group))); break; case C_LONGLONG: armci_msg_group_llgop((long long*)x, n, op, (&(PGRP_LIST[group].group))); break; case C_FLOAT: armci_msg_group_fgop((float*)x, n, op, (&(PGRP_LIST[group].group))); break; case C_DBL: armci_msg_group_dgop((double*)x, n, op, (&(PGRP_LIST[group].group))); break; case C_SCPL: armci_msg_group_fgop((float*)x, 2*n, op, (&(PGRP_LIST[group].group))); break; case C_DCPL: armci_msg_group_dgop((double*)x, 2*n, op, (&(PGRP_LIST[group].group))); break; default: pnga_error(" wrong data type ",type); } #else pnga_error("Groups not implemented for system",0); #endif } else { pnga_gop(type, x, n, op); } } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_gop = pnga_gop #endif void pnga_gop(Integer type, void *x, Integer n, char *op) { Integer p_grp = pnga_pgroup_get_default(); _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if (p_grp > 0) { pnga_pgroup_gop(p_grp, type, x, n, op); } else { #if defined(MSG_COMMS_MPI) switch (type){ case C_INT: armci_msg_igop((int*)x, n, op); break; case C_LONG: armci_msg_lgop((long*)x, n, op); break; case C_LONGLONG: armci_msg_llgop((long long*)x, n, op); break; case C_FLOAT: armci_msg_fgop((float*)x, n, op); break; case C_DBL: armci_msg_dgop((double*)x, n, op); break; case C_SCPL: armci_msg_fgop((float*)x, 2*n, op); break; case C_DCPL: armci_msg_dgop((double*)x, 2*n, op); break; default: pnga_error(" wrong data type ",type); } #else switch (type){ case C_INT: pnga_error("Operation not defined for system",0); break; case C_LONG: tcg_igop(GA_TYPE_GOP, x, n, op); break; case C_LONGLONG: pnga_error("Operation not defined for system",0); break; case C_FLOAT: pnga_error("Operation not defined for system",0); break; case C_DBL: tcg_dgop(GA_TYPE_GOP, x, n, op); break; case C_SCPL: pnga_error("Operation not defined for system",0); break; case C_DCPL: pnga_error("Operation not defined for system",0); break; default: pnga_error(" wrong data type ",type); } #endif } } ga-5.9.2/global/src/complex.F000066400000000000000000000074571500715745200157460ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c All Fortran functions returning a complex or double complex type must c be defined here with a corresponding C subroutine e.g. (Fortran) complex c function ga_cdot --> (C) void gai_cdot(..., SingleComplex *ret). c c from http://www.math.utah.edu/software/c-with-fortran.html c c c Function return types c ... c Unfortunately, returning composite objects that occupy more than a c single register, or an adjacent register pair, is fraught with peril. c Older C and C++ compilers did not support this at all, and newer ones c may do it differently than Fortran compilers do: thus, you should not c expect to use Fortran functions that return types such as COMPLEX or c COMPLEX*16. Write a SUBROUTINE interface to your Fortran function c instead, and then invoke it as a void function from C or C++. c c c We attempted to use various calling conventions, argument order, c hidden first arguments as the return, etc to eliminate the need c for any Fortran source such as this, but portability was an issue. c Intel compilers did not behave similarly to GCC or Pathscale. At a c later date perhaps we will revisit this. c c For the time being, any function returning a complex type must be c defined here following the established convention herein. complex function ga_cdot(g_a,g_b) implicit none integer g_a, g_b external gai_cdot ga_cdot = (0.,0.) call gai_cdot(g_a,g_b,ga_cdot) end complex function nga_cdot(g_a,g_b) implicit none integer g_a, g_b external gai_cdot nga_cdot = (0.,0.) call ngai_cdot(g_a,g_b,nga_cdot) end double complex function ga_zdot(g_a,g_b) implicit none integer g_a, g_b external gai_zdot ga_zdot = (0.,0.) call gai_zdot(g_a,g_b,ga_zdot) end double complex function nga_zdot(g_a,g_b) implicit none integer g_a, g_b external gai_zdot nga_zdot = (0.,0.) call ngai_zdot(g_a,g_b,nga_zdot) end complex function ga_cdot_patch( $ g_a, t_a, ailo, aihi, ajlo, ajhi, $ g_b, t_b, bilo, bihi, bjlo, bjhi) implicit none integer g_a, ailo, aihi, ajlo, ajhi integer g_b, bilo, bihi, bjlo, bjhi character*1 t_a, t_b external gai_cdot_patch ga_cdot_patch = (0.,0.) call gai_cdot_patch( $ g_a, t_a, ailo, aihi, ajlo, ajhi, $ g_b, t_b, bilo, bihi, bjlo, bjhi, ga_cdot_patch) end complex function nga_cdot_patch( $ g_a, t_a, alo, ahi, $ g_b, t_b, blo, bhi) implicit none integer g_a, alo, ahi integer g_b, blo, bhi character*1 t_a, t_b external ngai_cdot_patch nga_cdot_patch = (0.,0.) call ngai_cdot_patch( $ g_a, t_a, alo, ahi, $ g_b, t_b, blo, bhi, nga_cdot_patch) end double complex function ga_zdot_patch( $ g_a, t_a, ailo, aihi, ajlo, ajhi, $ g_b, t_b, bilo, bihi, bjlo, bjhi) implicit none integer g_a, ailo, aihi, ajlo, ajhi integer g_b, bilo, bihi, bjlo, bjhi character*1 t_a, t_b external gai_zdot_patch ga_zdot_patch = (0.,0.) call gai_zdot_patch( $ g_a, t_a, ailo, aihi, ajlo, ajhi, $ g_b, t_b, bilo, bihi, bjlo, bjhi, ga_zdot_patch) end double complex function nga_zdot_patch( $ g_a, t_a, alo, ahi, $ g_b, t_b, blo, bhi) implicit none integer g_a, alo, ahi integer g_b, blo, bhi character*1 t_a, t_b external ngai_zdot_patch nga_zdot_patch = (0.,0.) call ngai_zdot_patch( $ g_a, t_a, alo, ahi, $ g_b, t_b, blo, bhi, nga_zdot_patch) end ga-5.9.2/global/src/datatypes.c000066400000000000000000000062101500715745200163140ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: datatypes.c,v 1.9.10.1 2006-12-14 13:24:47 manoj Exp $ * conversion of MA identifiers between C to Fortran data types * Note that ga_type_c2f(MT_F_INT) == MT_F_INT */ #include "gacommon.h" #include "macommon.h" #include "ga-papi.h" Integer pnga_type_f2c(Integer type) { Integer ctype; switch(type){ case MT_F_INT: # if SIZEOF_F77_INTEGER == SIZEOF_INT ctype = C_INT; # elif SIZEOF_F77_INTEGER == SIZEOF_LONG ctype = C_LONG; # elif SIZEOF_F77_INTEGER == SIZEOF_LONG_LONG ctype = C_LONGLONG; # else # error SIZEOF_F77_INTEGER == SIZEOF_??? # endif break; case MT_F_REAL: # if SIZEOF_F77_REAL == SIZEOF_FLOAT ctype = C_FLOAT; # elif SIZEOF_F77_REAL == SIZEOF_DOUBLE ctype = C_DBL; # elif SIZEOF_F77_REAL == SIZEOF_LONG_DOUBLE # error SIZEOF_F77_REAL == SIZEOF_LONG_DOUBLE not supported? # else # error SIZEOF_F77_REAL == SIZEOF_??? # endif break; case MT_F_DBL: # if SIZEOF_F77_DOUBLE_PRECISION == SIZEOF_DOUBLE ctype = C_DBL; # elif SIZEOF_F77_DOUBLE_PRECISION == SIZEOF_LONG_DOUBLE # error SIZEOF_F77_DOUBLE_PRECISION == SIZEOF_LONG_DOUBLE not supported? # else # error SIZEOF_F77_DOUBLE_PRECISION == SIZEOF_??? # endif break; case MT_F_DCPL: ctype = C_DCPL; break; case MT_F_SCPL: # if SIZEOF_F77_REAL == SIZEOF_FLOAT ctype = C_SCPL; # elif SIZEOF_F77_REAL == SIZEOF_DOUBLE ctype = C_DCPL; # elif SIZEOF_F77_REAL == SIZEOF_LONG_DOUBLE # error SIZEOF_F77_REAL == SIZEOF_LONG_DOUBLE not supported? # else # error SIZEOF_F77_REAL == SIZEOF_??? # endif break; default: ctype = type; break; } return(ctype); } Integer pnga_type_c2f(Integer type) { Integer ftype; switch(type){ case C_INT: ftype = (sizeof(int) != sizeof(Integer))? -1: MT_F_INT; break; case C_LONG: ftype = (sizeof(long) != sizeof(Integer))? -1: MT_F_INT; break; case C_LONGLONG: ftype = (sizeof(long long) != sizeof(Integer))? -1: MT_F_INT; break; case C_FLOAT: # if SIZEOF_FLOAT == SIZEOF_F77_REAL ftype = MT_F_REAL; # elif SIZEOF_FLOAT == SIZEOF_F77_DOUBLE_PRECISION ftype = MT_F_DBL; # else ftype = -1; # endif break; case C_DBL: ftype = MT_F_DBL; break; case C_DCPL: ftype = MT_F_DCPL; break; case C_SCPL: # if SIZEOF_FLOAT == SIZEOF_F77_REAL ftype = MT_F_SCPL; # elif SIZEOF_FLOAT == SIZEOF_F77_DOUBLE_PRECISION ftype = MT_F_DCPL; # else ftype = -1; # endif break; default: ftype = type; break; } return(ftype); } ga-5.9.2/global/src/decomp.c000066400000000000000000000610261500715745200155730ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: decomp.c,v 1.9.6.2 2007-07-04 00:50:06 manoj Exp $ */ /*************************************************************************** *--- *--- The software in this file implements three heuristics for distributing *--- multidimensional arrays: ddb_h1 and ddb_h2 are fastest, ddb_ex does *--- an exhaustive search, see below for details. *--- *--- To compile this file: cc ddb.c -lm *--- *--- Author: Joel Malard *--- Address: Pacific Northwest National Laboratory *--- Battelle Boulevard, PO Box 999 *--- Richland, WA 99352 *--- *--- Bug et al.: jm.malard@pnl.gov *--- ***************************************************************************/ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_MATH_H # include #endif #include "ga-papi.h" #include "typesf2c.h" /*-- ***************************************************************************** *-- *-- void ddb_h1 and ddb_h2 implement load-balancing heuristics *-- void ddb_ex implements an exhaustive search */ void ddb(Integer ndims, Integer ardims[], Integer npes, Integer blk[], Integer pedims[]); void ddb_ex( long ndims, Integer ardims[], long npes, double threshold, Integer blk[], Integer pedims[]); void ddb_h1( long ndims, Integer ardims[], long npes, double threshold, Integer blk[], Integer pedims[]); void ddb_h2( Integer ndims, Integer ardims[], Integer npes, double threshold, Integer bias, Integer blk[], Integer pedims[]); /*--------------------------------------------------------------------------- *-- Arguments *-- *-- The above three procedures have similar sequences of arguments *-- the only difference is that ddb_h2 takes an additional input argument *-- that induces the heuristic to possibly favor distributing the *-- right or the left axes of the data array. *-- *-- ndims (input): number of dimensions in the data array and the *-- process grid. There is no provision for requesting process *-- with fewer dimensions as the data array. *-- *-- ardims (input): extents of each dimension of the data array. *-- This array is of size ndims. destroyed. *-- *-- npes (input): number of processes onto which the distribution *-- takes place. *-- *-- threshold (input): minimum acceptable value of the load balance *-- ratio returned by ddb_ap(), see below. *-- *-- bias (input to ddb_h2): when set to a positive value positive *-- the rightmost axes of the data array are preferentially *-- distributed, similarly when bias is negative. When bias is zero *-- the heuristic attempts to deal processes equally among the axes *-- of the data array. *-- *-- blk (input/output): granularity of data mapping: The number of *-- consecutive elements along each dimension of the array. Upon output: *-- extents of the local array assigned to the process *-- that is assigned the array element with lowest global indices, e.g. A[0]. *-- The meaning of this array is not the same for ddb than for the underlying *-- load balancing subroutines. For the subroutine ddb, any non-positive value *-- in array blk is taken at face value. For example with ardim=[50,40], *-- blk = [3,-1] and npes = 40 the process grid that will be computed is: *-- pedims=[20,2] with 3x2 processes storing no data. This is compatible *-- with the semantics of the load-balancing subroutine in the 2D GA. *-- *-- pedims(output): number of processes along each dimension of the *-- data array. *-- *-------------------------------------------------------------------------- *-- *-- Other prototypes *-- *-- dd_ev evaluates the load balance ratio for the data distribution *-- specified by its argument list. *-- *-- ddb_ap and dd_lk are specific to ddb_h1. */ void ddb_ap(long ndims, double qedims[], Integer ardims[], Integer pedims[], long npes, long npdivs, long pdivs[]); double dd_ev(long ndims,Integer ardims[], Integer pedims[]); long dd_lk(long * prt, long n, double key); void dd_su(long ndims, Integer ardims[], Integer pedims[], Integer blk[]); /*--------------------------------------------------------------------------- *-- *-- Dependencies: *-- *-- ddb: ddb_h2, ddb_ex *-- ddb_ex: dd_ev, dd_su *-- ddb_h1: ddb_ap, dd_ev, ddb_ex, dd_lk, dd_su & -lm *-- ddb_h2: dd_ev, ddb_ex, dd_su *-- ***************************************************************************/ #define THRESHOLD -0.1 /* The threshold for switching to an exhaustive search*/ /************************************************************************ *-- *-- void ddb is a wrapper ontop of some load balancing heuristics. ddb *-- is called from within GA to compute process grids. *-- The first argument, ndims, is the number of *-- array dimensions. The resulting process grid also has ndims *-- dimensions but some of these can be degenerate. ************************************************************************/ void ddb(Integer ndims, Integer ardims[], Integer npes, Integer blk[], Integer pedims[]) { double ddb_threshold = 0.1; long ddb_bias = 0; long i, j; Integer count = 0; Integer *tardim, *tblk, *tpedim; long tp, sp; tp = (long)npes; /* count how many axes have block values.*/ for(i=ndims-1;i>=0;i--){ if(blk[i]<=0){ pedims[i] = -1; count += 1; } else { sp = (long)(ardims[i]+blk[i]-1)/blk[i]; if(sp>tp) { sp = tp; tp = 1; pedims[i] = (Integer)sp; } else { for(j=sp;j0){ tardim = (Integer *) calloc((size_t)count,sizeof(Integer)); if(tardim==NULL) { fprintf(stderr,"ddb: Memory allocation failed\n"); for(i=0;iblb || (clb==blb && cevthreshold) break; tdims[pc] = 0; pc -= 1; } else { if( tdims[pc] == stack[pc] ) { /*- Backtrack when current array dimension has exhausted *- all remaining processes */ done = (pc==0); tdims[pc] = 0; pc -= 1; } else { /*- Increment the number of processes assigned to the current *- array axis. */ for(tdims[pc]+=1; stack[pc]%tdims[pc]!=0; tdims[pc]+=1); pc += 1; stack[pc] = npes; for(i=0;iq) { h = k; q = t; } } /*- Swap elements of apdims to improve load balance */ j = h; for(k=0;kub) { j = k; ub = cb; } i = apdims[h]; apdims[h] = apdims[k]; apdims[k] = i; } if(j!=h){ i = apdims[h]; apdims[h] = apdims[j]; apdims[j] = i; } } while (ub > blb); for(i=0;i1) { for(k=1,i=g+1;iprt[md]) { lw = md+1; } else { hgh = md; } } while (lw0) { v = key-prt[lw-1]; if(v<0.0) v = -v; if(v1) { k = 1; do { h = k+1; for(j=h;j0) { istep = -1; istart = ndims-1; } /*- Set pedims -*/ for(j=0;j=1;k--){ p0 = pdivs[k]; h = istart; q = (tard[istart]0){ blk[i] = (tard[i]+pedims[i]-1)/pedims[i]; } else { pnga_error("process dimension is zero: ddb_h2",0); } free(tard); return; } /**************************************************************************** *-- *-- double dd_ev evaluates the load balancing ratio as follows: *-- *-- Let n1, n2 ... nd be the extents of the array dimensions *-- Let p1, p2 ... pd be the numbers of processes across *-- the corresponding dimensions of the process grid *-- Let there be npes processes available *-- *-- Load balancing measure = (n1/p1)*...*(nd/pd)*npes *-- ------------------------ *-- n1*n2*...*nd *-- The communication volume measure is the sum of *-- all monomials (ni/pi) of degree d-1. *-- ****************************************************************************/ double dd_ev(long ndims,Integer ardims[], Integer pedims[]) { double q, t; long k; q = 1.0; t = 1.0; for(k=0;k #endif #include "abstract_ops.h" #include "ga_iterator.h" #include "ga-papi.h" #include "ga-wapi.h" #ifndef GA_HALF_MAX_INT #define GA_HALF_MAX_INT ((((int)1) << ((int)(8*sizeof(int))-2)) - 1) #endif #ifndef GA_INFINITY_I #define GA_INFINITY_I (GA_HALF_MAX_INT + GA_HALF_MAX_INT + 1) /* Original value below. Seemed too small arbitrarily. #define GA_INFINITY_I 100000 */ #endif #ifndef GA_NEGATIVE_INFINITY_I #define GA_NEGATIVE_INFINITY_I (- GA_INFINITY_I) /* Original value below. Seemed too small arbitrarily. #define GA_NEGATIVE_INFINITY_I -100000 */ #endif #ifndef GA_HALF_MAX_LONG #define GA_HALF_MAX_LONG ((((long)1) << ((int)(8*sizeof(long))-2)) - 1) #endif #ifndef GA_INFINITY_L #define GA_INFINITY_L (GA_HALF_MAX_LONG + GA_HALF_MAX_LONG + 1) /* Original value was #define GA_INFINITY_L 100000 */ #endif #ifndef GA_NEGATIVE_INFINITY_L #define GA_NEGATIVE_INFINITY_L (- GA_INFINITY_L) #endif /* Original value was: #define GA_NEGATIVE_INFINITY_L -100000 */ /* Modified by Doug Baxter 01/24/04 to distinguish between Double inifinity and float infinity. #ifndef GA_INFINITY #define GA_INFINITY 1.0e20 #endif #ifndef GA_NEGATIVE_INFINITY #define GA_NEGATIVE_INFINITY -1.0e20 #endif */ #ifndef GA_INFINITY_F #define GA_INFINITY_F 1.0e37 #endif /* Original value below. #define GA_INFINITY_F 1.0e20 */ #ifndef GA_NEGATIVE_INFINITY_F #define GA_NEGATIVE_INFINITY_F -1.0e37 #endif /* Original value below. #define GA_NEGATIVE_INFINITY_F -1.0e20 */ #ifndef GA_INFINITY_D #define GA_INFINITY_D 1.0e307 #endif /* Original value below. #define GA_INFINITY_D 1.0e20 */ #ifndef GA_NEGATIVE_INFINITY_D #define GA_NEGATIVE_INFINITY_D -1.0e307 #endif /* Original value below. #define GA_NEGATIVE_INFINITY_D -1.0e20 */ /* End of 01/24/04 Modification. Perhaps it would be more appropriate to have GA_INFINITY_D BE 1.0e307 These ranges make assumptions about the data. */ #define OP_ABS 0 #define OP_ADD_CONST 1 #define OP_RECIP 2 #define OP_ELEM_MULT 3 #define OP_ELEM_DIV 4 #define OP_ELEM_MAX 5 #define OP_ELEM_MIN 6 #define OP_STEPMAX 7 #define OP_STEPBOUNDINFO 8 #define OP_ELEM_SDIV 9 #define OP_ELEM_SDIV2 10 #define OP_STEP_MASK 11 #define OP_FILL 100 /*The OP_FILL is not currently in use */ int debug_gai_oper_elem = 1; static void do_stepboundinfo(void *ptr, int nelem, int type) /*look at elements one by one and replace the positive infinity with negative infinity */ { int i; switch (type){ int *ia; double *da; float *fa; long *la; case C_DBL: /*Only double data type will be handled for TAO/GA project*/ da = (double *) ptr; for(i=0;i=GA_INFINITY_D) da[i]=-GA_INFINITY_D; break; case C_INT: /* This block added 01/24/04 */ ia = (int *) ptr; for (i=0;i= GA_INFINITY_I) ia[i] = GA_NEGATIVE_INFINITY_I; break; case C_DCPL: case C_SCPL: /* This operation is not well defined for complex numbers . This statement added when drop through behavior changed by adding code for C_FLOAT and C_LONG cases below. 01/24/04 */ pnga_error("do_stepboundinfo:wrong data type",type); case C_FLOAT: /* This case added 01/24/04 */ fa = (float *) ptr; for (i=0;i= GA_INFINITY_F) fa[i] = GA_NEGATIVE_INFINITY_F; break; case C_LONG: /* This case added 01/24/04 */ la = (long *) ptr; for (i=0;i= GA_INFINITY_L) la[i] = GA_NEGATIVE_INFINITY_L; break; default: pnga_error("do_stepboundinfo:wrong data type",type); } } static void do_stepmax(void *ptr, int nelem, int type) /* Look at elements one by one and replace the positive with negative infinity. */ { int i; switch (type){ int *ia,i_0; double *da,d_0; float *fa,f_0; long *la,l_0; case C_DBL: /*Only double data type will be handled for TAO/GA project*/ da = (double *) ptr; d_0 = (double) 0.0; /* Modified 01/24/04 to use _D ending. */ for(i=0;id_0) da[i]=-GA_INFINITY_D; break; case C_INT: /* Thix case added 01/24/04*/ ia = (int *) ptr; i_0 = (int)0; for(i=0;ii_0)ia[i]=-GA_INFINITY_I; break; case C_DCPL: case C_SCPL: /* This operation is not well defined for complex numbers . This statement added when drop through behavior changed by adding code for C_FLOAT and C_LONG cases below. 01/24/04 */ pnga_error("do_stepmax:wrong data type",type); case C_FLOAT: /* Thix case added 01/24/04*/ fa = (float *) ptr; f_0 = (float) 0.0; for(i=0;if_0) fa[i]=-GA_INFINITY_F; break; case C_LONG: /* Thix case added 01/24/04*/ la = (long *) ptr; l_0 = (long)0; for(i=0;il_0) la[i]=-GA_INFINITY_L; break; default: pnga_error("do_stepmax:wrong data type",type); } } static void do_abs(void *ptr, int nelem, int type) { int i; double x2; float sx2; switch (type){ int *ia; double *da; float *fa; DoubleComplex *ca,val; SingleComplex *cfa,cval; long *la; case C_INT: ia = (int *)ptr; for(i=0;i= GA_ABS(val.imag)) { if (val.real == (double)0.0) { ca[i].real = (double)0.0; } else { x2 = val.imag/val.real; ca[i].real = GA_ABS(val.real)*sqrt(1.0+(x2*x2)); } } else { x2 = val.real/val.imag; ca[i].real = GA_ABS(val.imag)*sqrt(1.0+(x2*x2)); } #endif ca[i].imag=(double)0.0; } break; case C_SCPL: cfa = (SingleComplex *) ptr; for(i=0;i= GA_ABS(cval.imag)) { if (cval.real == 0.0f) { cfa[i].real = 0.0f; } else { sx2 = cval.imag/cval.real; cfa[i].real = GA_ABS(cval.real)*sqrt(1.0f+(sx2*sx2)); } } else { sx2 = cval.real/cval.imag; cfa[i].real = GA_ABS(cval.imag)*sqrt(1.0f+(sx2*sx2)); } #endif cfa[i].imag=0.0f; } break; case C_DBL: da = (double *) ptr; for(i=0;i= magi) { if (magr != ((double)0.0)) { c = x2/x1; d = ((double)1.0)/((((double)1.0) + (c*c))*x1); ca[i].real = d; ca[i].imag = -c*d; } else { pnga_error("zero value at index",i); } } else { c = x1/x2; d = ((double)1.0)/((((double)1.0) + (c*c))*x2); ca[i].real = c*d; ca[i].imag = -d; } /* printf(" do_recip ca[%d].real = %le, ca[%d].imag = %le\n", i,ca[i].real,i,ca[i].imag); */ } break; case C_SCPL: cfa = (SingleComplex *) ptr; for(i=0;i= smagi) { if (smagr != ((float)0.0)) { sc = sx2/sx1; sd = ((float)1.0)/((((float)1.0) + (sc*sc))*sx1); cfa[i].real = sd; cfa[i].imag = -sc*sd; } else { pnga_error("zero value at index",i); } } else { sc = sx1/sx2; sd = ((float)1.0)/((((float)1.0) + (sc*sc))*sx2); cfa[i].real = sc*sd; cfa[i].imag = -sd; } /* printf(" do_recip ca[%d].real = %le, ca[%d].imag = %le\n", i,ca[i].real,i,ca[i].imag); */ } break; case C_DBL: da = (double *) ptr; for(i=0;i (hiA[j]-loA[j])) bvalue[j] = 0; } switch(type){ case C_INT: temp=((int*)data_ptr)+idx; break; case C_DCPL: temp=((DoubleComplex*)data_ptr)+idx; break; case C_SCPL: temp=((SingleComplex*)data_ptr)+idx; break; case C_DBL: temp=((double*)data_ptr)+idx; break; case C_FLOAT: temp=((float*)data_ptr)+idx; break; case C_LONG: temp=((long *)data_ptr)+idx; break; default: pnga_error("wrong data type.",type); } switch(op){ case OP_ABS: do_abs(temp ,hiA[0] -loA[0] +1, type); break; break; case OP_ADD_CONST: do_add_const(temp ,hiA[0] -loA[0] +1, type, scalar); break; case OP_RECIP: do_recip(temp ,hiA[0] -loA[0] +1, type); break; break; default: pnga_error("bad operation",op); } } } static void gai_oper_elem(Integer g_a, Integer *lo, Integer *hi, void *scalar, Integer op) { Integer ndim, dims[MAXDIM], type; Integer loA[MAXDIM], hiA[MAXDIM], ld[MAXDIM]; char *data_ptr; Integer me= pnga_nodeid(); Integer num_blocks; int local_sync_begin,local_sync_end; _iterator_hdl hdl; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); pnga_check_handle(g_a, "gai_oper_elem"); pnga_inquire(g_a, &type, &ndim, dims); num_blocks = pnga_total_blocks(g_a); pnga_local_iterator_init(g_a, &hdl); while (pnga_local_iterator_next(&hdl,loA,hiA,&data_ptr,ld)) { Integer offset, i, j, jtmp, chk; Integer loS[MAXDIM]; Integer nproc = pnga_nnodes(); /* loA is changed by pnga_patch_intersect, so save a copy */ for (j=0; j dims[i]) hiA[i] = dims[i]; if (hiA[i] < loA[i]) chk = 0; } /* loA is changed by pnga_patch_intersect, so save a copy */ for (j=0; j= blocks[i] && i= GA_ABS(bImag)) { if (bReal != (double)0.0) { x1 = bImag/bReal; /* So x1 <= 1 */ x2 = ((double)1.0)/(bReal*(((double)1.0)+(x1*x1))); ((DoubleComplex*)pC)[i].real = (aReal + aImag*x1)*x2; ((DoubleComplex*)pC)[i].imag = (aImag - aReal*x1)*x2; } else{ pnga_error("zero divisor ",bReal); } } else { x1 = bReal/bImag; /* So x1 <= 1 */ x2 = ((double)1.0)/(bImag*(((double)1.0)+(x1*x1))); ((DoubleComplex*)pC)[i].real = (aReal*x1 + aImag)*x2; ((DoubleComplex*)pC)[i].imag = (aImag*x1 - aReal)*x2; } } break; case C_SCPL: for(i = 0; i= GA_ABS(bImag)) { if (bReal != (float)0.0) { x1 = bImag/bReal; /* So x1 <= 1 */ x2 = ((float)1.0)/(bReal*(((float)1.0)+(x1*x1))); ((SingleComplex*)pC)[i].real = (aReal + aImag*x1)*x2; ((SingleComplex*)pC)[i].imag = (aImag - aReal*x1)*x2; } else{ pnga_error("zero divisor ",bReal); } } else { x1 = bReal/bImag; /* So x1 <= 1 */ x2 = ((float)1.0)/(bImag*(((float)1.0)+(x1*x1))); ((SingleComplex*)pC)[i].real = (aReal*x1 + aImag)*x2; ((SingleComplex*)pC)[i].imag = (aImag*x1 - aReal)*x2; } } break; case C_INT: for(i = 0; i=d_0) { ((double*)pC)[i]= GA_INFINITY_D; } else { ((double*)pC)[i]= d_0; } } else { if(((double*)pB)[i]!=d_0) ((double*)pC)[i]= ((double*)pA)[i]/((double*)pB)[i]; else{ /* if b is zero an infinite number could be added without changing the sign of a. */ ((double*)pC)[i]= GA_INFINITY_D; } } } break; case C_DCPL: pnga_error(" do_step_divide called with type C_DCPL",C_DCPL); break; case C_SCPL: pnga_error(" do_step_divide called with type C_SCPL",C_SCPL); break; case C_INT: i_0 = (int)0; for(i = 0; i=i_0) { ((int*)pC)[i]=GA_INFINITY_I; } else { ((int*)pC)[i]=i_0; } } else { if(((int*)pB)[i]!=i_0) ((int*)pC)[i] = ((int*)pA)[i]/((int*)pB)[i]; else{ ((int*)pC)[i]=GA_INFINITY_I; } } } break; case C_FLOAT: f_0 = (float)0.0; for(i = 0; i=f_0) { ((float*)pC)[i]= GA_INFINITY_F; } else { ((float*)pC)[i]= f_0; } } else { if(((float*)pB)[i]!=f_0) { ((float*)pC)[i]= ((float*)pA)[i]/((float*)pB)[i]; } else { /* _F added 01/24/04 */ ((float*)pC)[i]= GA_INFINITY_F; } } } break; case C_LONG: l_0 = (long)0; for(i = 0; i=l_0) { ((long *)pC)[i] = GA_INFINITY_L; } else { ((long *)pC)[i] = l_0; } } else { if(((long *)pB)[i]!=l_0) ((long *)pC)[i]= ((long *)pA)[i]/((long *)pB)[i]; else{ ((long *)pC)[i] = GA_INFINITY_L; } } } break; default: pnga_error(" wrong data type ",type); } } static void do_stepb_divide(void *pA, void *pB, void *pC, Integer nelems, Integer type){ /* Elementwise divide, not aborting on a zero denominator, but returning an infinity. If an element in the numerator vector (PA) is zero, then infinity is returned if the corresponding denominator element is non-negative, else zero is returned for that element. DJB 4/02/04 */ Integer i; double d_0; long l_0; float f_0; Integer i_0; d_0 = (double)0.0; l_0 = (long)0; f_0 = (float)0.0; i_0 = (int)0; switch(type){ case C_DBL: for(i = 0; id_0) { ((double*)pC)[i]= d_0; } else { ((double*)pC)[i]= GA_INFINITY_D; } } else { if(((double*)pB)[i]!=d_0) ((double*)pC)[i]= ((double*)pA)[i]/((double*)pB)[i]; else{ /* if b is zero an infinite number could be added without changing the sign of a. */ ((double*)pC)[i]= GA_INFINITY_D; } } } break; case C_DCPL: pnga_error(" do_stepb_divide called with type C_DCPL",C_DCPL); break; case C_SCPL: pnga_error(" do_stepb_divide called with type C_SCPL",C_SCPL); break; case C_INT: i_0 = (int)0; for(i = 0; ii_0) { ((int*)pC)[i]=i_0; } else { ((int*)pC)[i]=GA_INFINITY_I; } } else { if(((int*)pB)[i]!=i_0) ((int*)pC)[i] = ((int*)pA)[i]/((int*)pB)[i]; else{ ((int*)pC)[i]=GA_INFINITY_I; } } } break; case C_FLOAT: f_0 = (float)0.0; for(i = 0; if_0) { ((float*)pC)[i]= f_0; } else { ((float*)pC)[i]= GA_INFINITY_F; } } else { if(((float*)pB)[i]!=f_0) { ((float*)pC)[i]= ((float*)pA)[i]/((float*)pB)[i]; } else { /* _F added 01/24/04 */ ((float*)pC)[i]= GA_INFINITY_F; } } } break; case C_LONG: l_0 = (long)0; for(i = 0; il_0) { ((long *)pC)[i] = l_0; } else { ((long *)pC)[i] = GA_INFINITY_L; } } else { if(((long *)pB)[i]!=l_0) ((long *)pC)[i]= ((long *)pA)[i]/((long *)pB)[i]; else{ ((long *)pC)[i] = GA_INFINITY_L; } } } break; default: pnga_error(" do_stepb_divide: wrong data type ",type); } } static void do_step_mask(void *pA, void *pB, void *pC, Integer nelems, Integer type){ /* Set vector C to vector B wherever vector A is nonzero, and to zero wherever vector A is zero. */ Integer i; switch(type){ case C_DBL: for(i = 0; itemp2){ ((DoubleComplex*)pC)[i].real=((DoubleComplex*)pA)[i].real; ((DoubleComplex*)pC)[i].imag=((DoubleComplex*)pA)[i].imag; } else{ ((DoubleComplex*)pC)[i].real=((DoubleComplex*)pB)[i].real; ((DoubleComplex*)pC)[i].imag=((DoubleComplex*)pB)[i].imag; } } } break; case C_SCPL: for(i = 0; itemp2){ ((SingleComplex*)pC)[i].real=((SingleComplex*)pA)[i].real; ((SingleComplex*)pC)[i].imag=((SingleComplex*)pA)[i].imag; } else{ ((SingleComplex*)pC)[i].real=((SingleComplex*)pB)[i].real; ((SingleComplex*)pC)[i].imag=((SingleComplex*)pB)[i].imag; } } } break; case C_INT: for(i = 0; i (hiC[j]-loC[j])) bvalue[j] = 0; } switch(atype){ case C_DBL: tempA=((double*)A_ptr)+idx; tempB=((double*)B_ptr)+idx; tempC=((double*)C_ptr)+idx; break; case C_DCPL: tempA=((DoubleComplex*)A_ptr)+idx; tempB=((DoubleComplex*)B_ptr)+idx; tempC=((DoubleComplex*)C_ptr)+idx; break; case C_SCPL: tempA=((SingleComplex*)A_ptr)+idx; tempB=((SingleComplex*)B_ptr)+idx; tempC=((SingleComplex*)C_ptr)+idx; break; case C_INT: tempA=((int*)A_ptr)+idx; tempB=((int*)B_ptr)+idx; tempC=((int*)C_ptr)+idx; break; case C_FLOAT: tempA=((float*)A_ptr)+idx; tempB=((float*)B_ptr)+idx; tempC=((float*)C_ptr)+idx; break; case C_LONG: tempA=((long *)A_ptr)+idx; tempB=((long *)B_ptr)+idx; tempC=((long *)C_ptr)+idx; break; default: pnga_error(" wrong data type ",atype); } switch((int)op) { case OP_ELEM_MULT: do_multiply(tempA,tempB,tempC,hiC[0]-loC[0]+1,atype); break; case OP_ELEM_DIV: do_divide(tempA,tempB,tempC,hiC[0]-loC[0]+1,atype); break; case OP_ELEM_SDIV: do_step_divide(tempA,tempB,tempC,hiC[0]-loC[0]+1,atype); break; case OP_ELEM_SDIV2: do_stepb_divide(tempA,tempB,tempC,hiC[0]-loC[0]+1,atype); break; case OP_STEP_MASK: do_step_mask(tempA,tempB,tempC,hiC[0]-loC[0]+1,atype); break; case OP_ELEM_MAX: do_maximum(tempA,tempB,tempC,hiC[0]-loC[0]+1,atype); break; case OP_ELEM_MIN: do_minimum(tempA,tempB,tempC,hiC[0]-loC[0]+1,atype); break; default: printf("op : OP_ELEM_MULT = %d:%d\n", op, OP_ELEM_MULT); pnga_error(" wrong operation ",op); } } } /*\ generic operation of two patches \*/ static void ngai_elem2_patch_(g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi, op) Integer g_a, *alo, *ahi; /* patch of g_a */ Integer g_b, *blo, *bhi; /* patch of g_b */ Integer g_c, *clo, *chi; /* patch of g_c */ int op; /* operation to be perform between g_a and g_b */ { Integer i, j; Integer compatible; Integer atype, btype, ctype; Integer andim, adims[MAXDIM], bndim, bdims[MAXDIM], cndim, cdims[MAXDIM]; Integer loA[MAXDIM], hiA[MAXDIM], ldA[MAXDIM]; Integer loB[MAXDIM], hiB[MAXDIM], ldB[MAXDIM]; Integer loC[MAXDIM], hiC[MAXDIM], ldC[MAXDIM]; char *A_ptr, *B_ptr, *C_ptr; Integer idx, n1dim; Integer atotal, btotal; Integer g_A = g_a, g_B = g_b; Integer num_blocks_a, num_blocks_b, num_blocks_c; Integer me= pnga_nodeid(), A_created=0, B_created=0; char *tempname = "temp", notrans='n'; int local_sync_begin,local_sync_end; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); pnga_check_handle(g_a, "gai_elem2_patch_"); pnga_inquire(g_a, &atype, &andim, adims); pnga_inquire(g_b, &btype, &bndim, bdims); pnga_inquire(g_c, &ctype, &cndim, cdims); if(atype != btype || atype != ctype ) pnga_error(" types mismatch ", 0L); /* check if patch indices and dims match */ for(i=0; i adims[i]) pnga_error("g_a indices out of range ", g_a); for(i=0; i bdims[i]) pnga_error("g_b indices out of range ", g_b); for(i=0; i cdims[i]) pnga_error("g_c indices out of range ", g_c); /* check if numbers of elements in patches match each other */ n1dim = 1; for(i=0; i bndim) cndim = bndim; if(andim < bndim) cndim = andim; if(!pnga_comp_patch(andim, loA, hiA, cndim, loC, hiC)) pnga_error(" A patch mismatch ", g_A); if(!pnga_comp_patch(bndim, loB, hiB, cndim, loC, hiC)) pnga_error(" B patch mismatch ", g_B); /* determine subsets of my patches to access */ if (pnga_patch_intersect(clo, chi, loC, hiC, cndim)){ pnga_access_ptr(g_A, loC, hiC, &A_ptr, ldA); pnga_access_ptr(g_B, loC, hiC, &B_ptr, ldB); pnga_access_ptr(g_c, loC, hiC, &C_ptr, ldC); /* compute "local" operation accoording to op */ ngai_do_elem2_oper(atype, cndim, loC, hiC, ldC, A_ptr, B_ptr, C_ptr, op); /* release access to the data */ pnga_release (g_A, loC, hiC); pnga_release (g_B, loC, hiC); pnga_release_update(g_c, loC, hiC); } } else { _iterator_hdl hdl_a, hdl_b, hdl_c; int lod[MAXDIM]; /* create copies of arrays A and B that are identically distributed as C*/ if (!pnga_duplicate(g_c, &g_A, tempname)) pnga_error("ga_dadd_patch: dup failed", 0L); pnga_copy_patch(¬rans, g_a, alo, ahi, g_A, clo, chi); andim = cndim; A_created = 1; if (!pnga_duplicate(g_c, &g_B, tempname)) pnga_error("ga_dadd_patch: dup failed", 0L); pnga_copy_patch(¬rans, g_b, blo, bhi, g_B, clo, chi); bndim = cndim; B_created = 1; /* C is normally distributed so just add copies together for regular arrays */ #if 1 pnga_local_iterator_init(g_A, &hdl_a); pnga_local_iterator_init(g_B, &hdl_b); pnga_local_iterator_init(g_c, &hdl_c); while (pnga_local_iterator_next(&hdl_c,loC,hiC,&C_ptr,ldC)) { pnga_local_iterator_next(&hdl_a,loA,hiA,&A_ptr,ldA); pnga_local_iterator_next(&hdl_b,loB,hiB,&B_ptr,ldB); /* make temporary copies of loC and hiC since pnga_patch_intersect destroys original versions */ for (j=0; j bndim) cndim = bndim; if(andim < bndim) cndim = andim; if (pnga_patch_intersect(clo, chi, loC, hiC, cndim)){ pnga_access_ptr(g_A, loC, hiC, &A_ptr, ldA); pnga_access_ptr(g_B, loC, hiC, &B_ptr, ldB); pnga_access_ptr(g_c, loC, hiC, &C_ptr, ldC); /* compute "local" operation accoording to op */ ngai_do_elem2_oper(atype, cndim, loC, hiC, ldC, A_ptr, B_ptr, C_ptr, op); /* release access to the data */ pnga_release (g_A, loC, hiC); pnga_release (g_B, loC, hiC); pnga_release_update(g_c, loC, hiC); } } else { Integer lod[MAXDIM]; /* Integer hid[MAXDIM]; */ /* Integer chk; */ Integer offset, last, jtot; if (!pnga_uses_proc_grid(g_c)) { Integer nproc = pnga_nnodes(); for (idx = me; idx < num_blocks_c; idx += nproc) { pnga_distribution(g_c, idx, loC, hiC); /* make temporary copies of loC and hiC since pnga_patch_intersect destroys original versions */ for (j=0; j cdims[i]) hiC[i] = cdims[i]; /* if (hiC[i] < loC[i]) chk = 0; */ } /* make temporary copies of loC and hiC since pnga_patch_intersect destroys original versions */ for (j=0; j= blocks[i] && i (hiA[j]-loA[j])) bvalue[j] = 0; } switch(atype){ case C_DBL: tempA=((double*)A_ptr)+idx; break; case C_DCPL: case C_SCPL: pnga_error(" ngai_elem3_patch_: wrong data type ",atype); break; case C_INT: tempA=((int*)A_ptr)+idx; break; case C_FLOAT: tempA=((float*)A_ptr)+idx; break; case C_LONG: tempA=((long *)A_ptr)+idx; break; default: pnga_error(" ngai_elem3_patch_: wrong data type ",atype); } switch(op){ case OP_STEPMAX: do_stepmax(tempA,hiA[0]-loA[0]+1, atype); break; case OP_STEPBOUNDINFO: do_stepboundinfo(tempA,hiA[0]-loA[0]+1, atype); break; default: pnga_error(" wrong operation ",op); } } } static void ngai_elem3_patch_(Integer g_a, Integer *alo, Integer *ahi, int op) /*do some preprocess jobs for stepMax and stepMax2*/ { Integer i; Integer atype; Integer andim, adims[MAXDIM]; Integer loA[MAXDIM], hiA[MAXDIM], ldA[MAXDIM]; char *A_ptr; Integer me= pnga_nodeid(); Integer num_blocks; int local_sync_begin,local_sync_end; _iterator_hdl hdl; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); pnga_check_handle(g_a, "gai_elem3_patch_"); pnga_inquire(g_a, &atype, &andim, adims); num_blocks = pnga_total_blocks(g_a); /* check if patch indices and dims match */ for(i=0; i adims[i]) pnga_error("g_a indices out of range ", g_a); #if 1 pnga_local_iterator_init(g_a, &hdl); while (pnga_local_iterator_next(&hdl, loA, hiA, &A_ptr, ldA)) { Integer offset, j, jtmp, chk; Integer loS[MAXDIM]; /* loA is changed by pnga_patch_intersect, so * save a copy */ for (j=0; j adims[i]) hiA[i] = adims[i]; } /* loA is changed by pnga_patch_intersect, so * save a copy */ for (j=0; j= blocks[i] && i (hiA[j]-loA[j])) bvalue[j] = 0; } switch(atype){ case C_DBL: /*double is the only type that is handled for Tao/GA project*/ /* DJB modification to add types int, float and long. This operation does not make sense for complex. */ tempA=((double*)A_ptr)+idx; for(j=0;j adims[i]) pnga_error("g_a indices out of range ", g_a); #if 1 pnga_local_iterator_init(g_a, &hdl); while (pnga_local_iterator_next(&hdl, loA, hiA, &A_ptr, ldA)) { Integer offset, j, jtmp, chk; Integer loS[MAXDIM]; /* loA is changed by pnga_patch_intersect, so * save a copy */ for (j=0; j adims[i]) hiA[i] = adims[i]; } /* loA is changed by pnga_patch_intersect, so * save a copy */ for (j=0; j= blocks[i] && i xxdims[i]) pnga_error("ga_elem_step_bound_info_patch: g_a indices out of range ", g_xx); for(i=0; i vvdims[i]) pnga_error("ga_elem_step_bound_info_patch: g_a indices out of range ", g_vv); for(i=0; i xldims[i]) pnga_error("ga_elem_step_bound_info_patch: g_a indices out of range ", g_xxll); for(i=0; i xudims[i]) pnga_error("ga_elem_step_bound_info_patch: g_a indices out of range ", g_xxuu); /* check if numbers of elements in patches match each other */ xxtotal = 1; for(i=0; i xx.", -1); /* Then compute t = positve elements of vv */ pnga_zero(g_T); pnga_elem_maximum(g_vv,g_T,g_T); /* Then, compute (xu-xx)/vv */ pnga_elem_stepb_divide_patch(g_S, xxlo, xxhi, g_T, vvlo, vvhi, g_T, xxlo, xxhi); /* Then, we will select the minimum of the array g_t*/ pnga_select_elem(g_T, "min", sresult, &index[0]); switch (xxtype) { case C_INT: /* This should be iresult but is lresult because of the strange implementation of nga_select_elem. */ /* result1 = (double)(iresult); */ break; case C_DCPL: case C_SCPL: pnga_error("Ga_step_bound_info_patch_: unavalable for complex datatype.", xxtype); break; case C_DBL: /* result1 = dresult; */ break; case C_FLOAT: /* result1 = (double)fresult; */ break; case C_LONG: /* result1 = (double)lresult; */ break; default: pnga_error("Ga_step_bound_info_patch_: result set: wrong data type.", xxtype); } /*Now doing the same thing to get (xx-xxll)/dv */ /*First, compute xl - xx */ pnga_add_patch(alpha, g_xx, xxlo, xxhi, beta, g_xxll, xxlllo, xxllhi, g_Q, xxlo, xxhi); /*Check for negative elements in g_s, if it has any then xxll was not a lower bound, exit with error message. */ if(has_negative_elem(g_Q, xxlo, xxhi) == 1) pnga_error("pnga_step_bound_info_patch: Lower bound is not < xx.", -1); /* Then compute r = negative elements of vv */ pnga_zero(g_R); pnga_elem_minimum(g_vv,g_R,g_R); pnga_abs_value(g_R); /* Then, compute (xx-xl)/vv */ pnga_elem_stepb_divide_patch(g_Q, xxlo, xxhi, g_R, vvlo, vvhi, g_R, xxlo, xxhi); /* Then, we will select the minimum of the array g_t*/ pnga_select_elem(g_R, "min", sresult2, &index[0]); switch (xxtype) { case C_INT: *(int*)wolfemin = GA_ABS(GA_MIN(iresult,iresult2)); break; case C_DCPL: case C_SCPL: pnga_error("Ga_step_bound_info_patch_: unavalable for complex datatype.", xxtype); break; case C_DBL: *(double*)wolfemin = GA_ABS(GA_MIN(dresult,dresult2)); break; case C_FLOAT: *(float*)wolfemin = GA_ABS(GA_MIN(fresult,fresult2)); break; case C_LONG: *(long*)wolfemin = GA_ABS(GA_MIN(lresult,lresult2)); break; default: pnga_error("Ga_step_bound_info_patch_: result2 set: wrong data type.", xxtype); } /* Now set T to be the elementwise minimum of R and T. So, T is infinity only where ever g_vv is zero. */ pnga_elem_minimum(g_R,g_T,g_T); /* Now we want to set T to be zero whenever g_vv was zero and gxx coincides with either boundary vector. Set S to be the element-wise product of S and Q. It will be zero when either of them is zero. */ pnga_elem_multiply(g_Q,g_S,g_S); /* Set Q to the |vv|. */ pnga_copy(g_vv,g_Q); pnga_abs_value(g_Q); /* Now add q and s to get a vector that is zero only where g_vv was zero and g_xx meets one of the boundary vectors. */ pnga_add_patch(alpha, g_Q, xxlo, xxhi, alpha, g_S, xxlo, xxhi, g_S, xxlo, xxhi); /* Then use that vector as a mask to set certain elements of T to be zero (so we have a collection of the a_i and c_i elements as per the TAO StepBoundInfo function). */ pnga_step_mask_patch(g_S,xxlo,xxhi,g_T,xxlo,xxhi,g_T,xxlo,xxhi); /* Then, we will select the minimum of the array g_t, that will be boundmin . */ pnga_select_elem(g_T, "min", sresult, &index[0]); switch (xxtype) { case C_INT: /* This should be iresult but is lresult because of the strange implementation of nga_select_elem. */ *(int*)boundmin = iresult; break; case C_DCPL: case C_SCPL: pnga_error("Ga_step_bound_info_patch_: unavalable for complex datatype.", xxtype); break; case C_DBL: *(double*)boundmin = dresult; break; case C_FLOAT: *(float*)boundmin = fresult; break; case C_LONG: *(long*)boundmin = lresult; break; default: pnga_error("Ga_step_bound_info_patch_: result set: wrong data type.", xxtype); } /* Then, we will select the maximum of the array g_t, that will be boundmax . */ pnga_select_elem(g_T, "max", sresult, &index[0]); switch (xxtype) { case C_INT: /* This should be iresult but is lresult because of the strange implementation of nga_select_elem. */ *(int*)boundmax = iresult; break; case C_DCPL: case C_SCPL: pnga_error("Ga_step_bound_info_patch_: unavalable for complex datatype.", xxtype); break; case C_DBL: *(double*)boundmax = dresult; break; case C_FLOAT: *(float*)boundmax = fresult; break; case C_LONG: *(long*)boundmax = lresult; break; default: pnga_error("Ga_step_bound_info_patch_: result set: wrong data type.", xxtype); } pnga_destroy(g_Q); pnga_destroy(g_R); pnga_destroy(g_S); pnga_destroy(g_T); if(local_sync_end)pnga_sync(); } /*\ generic routine for element wise operation between two array \*/ #if 0 /* I want to delete op parameter */ void ga_step_max_patch_(g_a, alo, ahi, g_b, blo, bhi, result, op) #else #endif #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_step_max_patch = pnga_step_max_patch #endif void pnga_step_max_patch(g_a, alo, ahi, g_b, blo, bhi, result) Integer g_a, *alo, *ahi; /* patch of g_a */ Integer g_b, *blo, *bhi; /* patch of g_b */ void *result; #if 0 Integer op; /* operations */ #endif { double dresult; long lresult; Integer atype; Integer andim, adims[MAXDIM]; Integer btype; Integer bndim, bdims[MAXDIM]; Integer index[MAXDIM]; /* Integer num_blocks_a, num_blocks_b; */ /* double result = -1; */ Integer g_c; int iresult; Integer atotal,btotal; float fresult; int local_sync_begin,local_sync_end; int i; Integer compatible; void *sresult = NULL; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); /* Check for valid ga handles. */ pnga_check_handle(g_a, "ga_step_max_patch_"); pnga_check_handle(g_b, "ga_step_max_patch_"); /* get chacteristics of the input ga patches */ pnga_inquire(g_a, &atype, &andim, adims); pnga_inquire(g_b, &btype, &bndim, bdims); /* num_blocks_a = pnga_total_blocks(g_a); */ /* num_blocks_b = pnga_total_blocks(g_b); */ /* Check for matching types. */ if(atype != btype) pnga_error(" ga_step_max_patch_: types mismatch ", 0L); /* check if patch indices and dims match */ for(i=0; i adims[i]) pnga_error("g_a indices out of range ", g_a); for(i=0; i bdims[i]) pnga_error("g_b indices out of range ", g_b); /* check if numbers of elements in patches match each other */ atotal = 1; for(i=0; i #endif #include "cnames.h" #include "farg.h" #include "globalp.h" #include "macommon.h" #include "matmul.h" int _ga_initialize_f=0; #include "ga-papi.h" #if ENABLE_PROFILING # include "ga-wapi.h" #else # include "ga-wapidefs.h" #endif #define FNAM 31 #define FMSG 256 /* Routines from base.c */ logical FATR ga_allocate_(Integer *g_a) { return wnga_allocate(*g_a); } logical FATR nga_allocate_(Integer *g_a) { return wnga_allocate(*g_a); } logical FATR ga_deallocate_(Integer *g_a) { return wnga_deallocate(*g_a); } logical FATR nga_deallocate_(Integer *g_a) { return wnga_deallocate(*g_a); } logical FATR ga_compare_distr_(Integer *g_a, Integer *g_b) { return wnga_compare_distr(*g_a, *g_b); } logical FATR nga_compare_distr_(Integer *g_a, Integer *g_b) { return wnga_compare_distr(*g_a, *g_b); } logical FATR ga_create_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS type, dim1, dim2, array_name, chunk1, chunk2, g_a, slen #else type, dim1, dim2, array_name, slen, chunk1, chunk2, g_a #endif ) Integer *type, *dim1, *dim2, *chunk1, *chunk2, *g_a; int slen; char* array_name; { char buf[FNAM]; Integer ndim, dims[2], chunk[2]; ga_f2cstring(array_name ,slen, buf, FNAM); dims[0] = *dim1; dims[1] = *dim2; ndim = 2; chunk[0] = (*chunk1==0)? -1 : *chunk1; chunk[1] = (*chunk2==0)? -1 : *chunk2; return(wnga_create(*type, ndim, dims, buf, chunk, g_a)); } logical FATR nga_create_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *type, Integer *ndim, Integer *dims, char* array_name, Integer *chunk, Integer *g_a, int slen #else Integer *type, Integer *ndim, Integer *dims, char* array_name, int slen, Integer *p_handle, Integer *g_a #endif ) { char buf[FNAM]; ga_f2cstring(array_name ,slen, buf, FNAM); return (wnga_create(*type, *ndim, dims, buf, chunk, g_a)); } logical FATR nga_create_config_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *type, Integer *ndim, Integer *dims, char* array_name, Integer *chunk, Integer *p_handle, Integer *g_a, int slen #else Integer *type, Integer *ndim, Integer *dims, char* array_name, int slen, Integer *chunk, Integer *p_handle, Integer *g_a #endif ) { char buf[FNAM]; ga_f2cstring(array_name ,slen, buf, FNAM); return (wnga_create_config(*type, *ndim, dims, buf, chunk, *p_handle, g_a)); } logical FATR nga_create_ghosts_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *type, Integer *ndim, Integer *dims, Integer *width, char* array_name, Integer *chunk, Integer *g_a, int slen #else Integer *type, Integer *ndim, Integer *dims, Integer *width, char* array_name, int slen, Integer *chunk, Integer *g_a #endif ) { char buf[FNAM]; ga_f2cstring(array_name ,slen, buf, FNAM); return (wnga_create_ghosts(*type, *ndim, dims, width, buf, chunk, g_a)); } logical FATR nga_create_ghosts_config_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *type, Integer *ndim, Integer *dims, Integer *width, char* array_name, Integer *chunk, Integer *p_handle, Integer *g_a, int slen #else Integer *type, Integer *ndim, Integer *dims, Integer *width, char* array_name, int slen, Integer *chunk, Integer *p_handle, Integer *g_a #endif ) { char buf[FNAM]; ga_f2cstring(array_name ,slen, buf, FNAM); return (wnga_create_ghosts_config(*type, *ndim, dims, width, buf, chunk, *p_handle, g_a)); } logical FATR nga_create_ghosts_irreg_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *type, Integer *ndim, Integer *dims, Integer width[], char* array_name, Integer map[], Integer block[], Integer *g_a, int slen #else Integer *type, Integer *ndim, Integer *dims, Integer width[], char* array_name, int slen, Integer map[], Integer block[], Integer *g_a #endif ) { char buf[FNAM]; Integer st; ga_f2cstring(array_name ,slen, buf, FNAM); st = wnga_create_ghosts_irreg(*type, *ndim, dims, width, buf, map, block, g_a); return st; } logical FATR nga_create_ghosts_irreg_config_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *type, Integer *ndim, Integer *dims, Integer width[], char* array_name, Integer map[], Integer block[], Integer *p_handle, Integer *g_a, int slen #else Integer *type, Integer *ndim, Integer *dims, Integer width[], char* array_name, int slen, Integer map[], Integer block[], Integer *p_handle, Integer *g_a #endif ) { char buf[FNAM]; Integer st; ga_f2cstring(array_name ,slen, buf, FNAM); st = wnga_create_ghosts_irreg_config(*type, *ndim, dims, width, buf, map, block, *p_handle, g_a); return st; } logical FATR ga_create_irreg_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *type, Integer *dim1, Integer *dim2, char *array_name, Integer *map1, Integer *nblock1, Integer *map2, Integer *nblock2, Integer *g_a, int slen #else Integer *type, Integer *dim1, Integer *dim2, char *array_name, int slen, Integer *map1, Integer *nblock1, Integer *map2, Integer *nblock2, Integer *g_a #endif ) { char buf[FNAM]; Integer i, ndim, dims[2], block[2], *map; Integer status; ga_f2cstring(array_name ,slen, buf, FNAM); dims[0] = *dim1; dims[1] = *dim2; block[0] = *nblock1; block[1] = *nblock2; ndim = 2; map = (Integer*)malloc((wnga_nnodes()+1)*sizeof(Integer)); for(i=0; i<*nblock1; i++) map[i] = map1[i]; for(i=0; i<*nblock2; i++) map[i+ *nblock1] = map2[i]; status = wnga_create_irreg(*type, ndim, dims, buf, map, block, g_a); free(map); return status; } logical FATR nga_create_irreg_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *type, Integer *ndim, Integer *dims, char* array_name, Integer map[], Integer block[], Integer *g_a, int slen #else Integer *type, Integer *ndim, Integer *dims, char* array_name, int slen, Integer map[], Integer block[], Integer *g_a #endif ) { char buf[FNAM]; Integer st; ga_f2cstring(array_name ,slen, buf, FNAM); st = wnga_create_irreg(*type, *ndim, dims, buf, map, block, g_a); return st; } logical FATR nga_create_irreg_config_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *type, Integer *ndim, Integer *dims, char* array_name, Integer map[], Integer block[], Integer *p_handle, Integer *g_a, int slen #else Integer *type, Integer *ndim, Integer *dims, char* array_name, int slen, Integer map[], Integer block[], Integer *p_handle, Integer *g_a #endif ) { char buf[FNAM]; Integer st; ga_f2cstring(array_name ,slen, buf, FNAM); st = wnga_create_irreg_config(*type, *ndim, dims, buf, map, block, *p_handle, g_a); return st; } Integer FATR ga_create_handle_() { return wnga_create_handle(); } Integer FATR nga_create_handle_() { return wnga_create_handle(); } logical FATR ga_create_mutexes_(Integer *num) { return wnga_create_mutexes(*num); } logical FATR nga_create_mutexes_(Integer *num) { return wnga_create_mutexes(*num); } logical FATR ga_destroy_(Integer *g_a) { return wnga_destroy(*g_a); } logical FATR nga_destroy_(Integer *g_a) { return wnga_destroy(*g_a); } logical FATR ga_destroy_mutexes_() { return wnga_destroy_mutexes(); } logical FATR nga_destroy_mutexes_() { return wnga_destroy_mutexes(); } void FATR ga_distribution_(Integer *g_a, Integer *proc, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi) { Integer lo[2], hi[2]; wnga_distribution(*g_a, *proc, lo, hi); *ilo = lo[0]; *jlo = lo[1]; *ihi = hi[0]; *jhi = hi[1]; } void FATR nga_distribution_(Integer *g_a, Integer *proc, Integer *lo, Integer *hi) { wnga_distribution(*g_a, *proc, lo, hi); } logical FATR ga_duplicate_( Integer *g_a, Integer *g_b, char *array_name, int slen) { char buf[FNAM]; ga_f2cstring(array_name ,slen, buf, FNAM); return(wnga_duplicate(*g_a, g_b, buf)); } logical FATR nga_duplicate_( Integer *g_a, Integer *g_b, char *array_name, int slen) { char buf[FNAM]; ga_f2cstring(array_name ,slen, buf, FNAM); return(wnga_duplicate(*g_a, g_b, buf)); } void FATR ga_fill_(Integer *g_a, void* val) { wnga_fill(*g_a, val); } void FATR nga_fill_(Integer *g_a, void* val) { wnga_fill(*g_a, val); } void FATR ga_get_block_info_(Integer *g_a, Integer *num_blocks, Integer *block_dims) { wnga_get_block_info(*g_a, num_blocks, block_dims); } void FATR nga_get_block_info_(Integer *g_a, Integer *num_blocks, Integer *block_dims) { wnga_get_block_info(*g_a, num_blocks, block_dims); } logical FATR ga_get_debug_() { return wnga_get_debug(); } logical FATR nga_get_debug_() { return wnga_get_debug(); } Integer FATR ga_get_dimension_(Integer *g_a) { return wnga_get_dimension(*g_a); } Integer FATR nga_get_dimension_(Integer *g_a) { return wnga_get_dimension(*g_a); } logical FATR ga_overlay_(Integer *g_a, Integer *g_p) { return wnga_overlay(*g_a,*g_p); } logical FATR nga_overlay_(Integer *g_a, Integer *g_p) { return wnga_overlay(*g_a,*g_p); } Integer FATR ga_get_pgroup_(Integer *g_a) { return wnga_get_pgroup(*g_a); } Integer FATR nga_get_pgroup_(Integer *g_a) { return wnga_get_pgroup(*g_a); } Integer FATR ga_get_pgroup_size_(Integer *grp_id) { return wnga_get_pgroup_size(*grp_id); } Integer FATR nga_get_pgroup_size_(Integer *grp_id) { return wnga_get_pgroup_size(*grp_id); } void FATR ga_get_proc_grid_(Integer *g_a, Integer *dims) { wnga_get_proc_grid(*g_a, dims); } void FATR ga_get_proc_index_(Integer *g_a, Integer *iproc, Integer *index) { wnga_get_proc_index(*g_a, *iproc, index); } void FATR nga_get_proc_index_(Integer *g_a, Integer *iproc, Integer *index) { wnga_get_proc_index(*g_a, *iproc, index); } void FATR nga_get_proc_grid_(Integer *g_a, Integer *dims) { wnga_get_proc_grid(*g_a, dims); } logical FATR ga_has_ghosts_(Integer *g_a) { return wnga_has_ghosts(*g_a); } logical FATR nga_has_ghosts_(Integer *g_a) { return wnga_has_ghosts(*g_a); } void FATR ga_initialize_() { _ga_initialize_f=1; wnga_initialize(); } void FATR nga_initialize_() { _ga_initialize_f=1; wnga_initialize(); } logical FATR ga_initialize_comm_(MPI_Comm comm) { _ga_initialize_f=1; return wnga_initialize_comm(comm); } logical FATR nga_initialize_comm_(MPI_Comm comm) { _ga_initialize_f=1; return wnga_initialize_comm(comm); } void FATR ga_initialize_ltd_(Integer *limit) { _ga_initialize_f=1; wnga_initialize_ltd(*limit); } void FATR nga_initialize_ltd_(Integer *limit) { _ga_initialize_f=1; wnga_initialize_ltd(*limit); } logical FATR ga_initialized_() { return wnga_initialized(); } logical FATR nga_initialized_() { return wnga_initialized(); } void FATR ga_inquire_(Integer *g_a, Integer *type, Integer *dim1, Integer *dim2) { Integer dims[2], ndim; wnga_inquire(*g_a, type, &ndim, dims); if (ndim != 2) wnga_error("Wrong array dimension in ga_inquire",ndim); *type = pnga_type_c2f(*type); *dim1 = dims[0]; *dim2 = dims[1]; } void FATR nga_inquire_(Integer *g_a, Integer *type, Integer *ndim, Integer *dims) { wnga_inquire(*g_a, type, ndim, dims); *type = pnga_type_c2f(*type); } Integer FATR ga_inquire_memory_() { return wnga_inquire_memory(); } Integer FATR nga_inquire_memory_() { return wnga_inquire_memory(); } void FATR ga_inquire_name_(Integer *g_a, char *array_name, int len) { char *c_name; wnga_inquire_name(*g_a, &c_name); ga_c2fstring(c_name, array_name, len); } void FATR nga_inquire_name_(Integer *g_a, char *array_name, int len) { char *c_name; wnga_inquire_name(*g_a, &c_name); ga_c2fstring(c_name, array_name, len); } logical FATR ga_is_mirrored_(Integer *g_a) { return wnga_is_mirrored(*g_a); } logical FATR nga_is_mirrored_(Integer *g_a) { return wnga_is_mirrored(*g_a); } void FATR ga_list_nodeid_(Integer *list, Integer *nprocs) { wnga_list_nodeid(list, *nprocs); } void FATR nga_list_nodeid_(Integer *list, Integer *nprocs) { wnga_list_nodeid(list, *nprocs); } Integer FATR nga_locate_num_blocks_(Integer *g_a, Integer *lo, Integer *hi) { return wnga_locate_num_blocks(*g_a,lo,hi); } logical FATR ga_locate_nnodes_( Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, Integer *np) { Integer lo[2], hi[2]; lo[0] = *ilo; lo[1] = *jlo; hi[0] = *ihi; hi[1] = *jhi; return wnga_locate_nnodes(*g_a, lo, hi, np); } logical FATR nga_locate_nnodes_(Integer *g_a, Integer *lo, Integer *hi, Integer *np) { return wnga_locate_nnodes(*g_a, lo, hi, np); } logical FATR ga_locate_region_( Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, Integer map[][5], Integer *np) { logical status = FALSE; Integer lo[2], hi[2], p; Integer *mapl, *proclist; proclist = (Integer*)malloc(wnga_nnodes()*sizeof(Integer)); mapl = (Integer*)malloc(5*wnga_nnodes()*sizeof(Integer)); lo[0] = *ilo; lo[1] = *jlo; hi[0] = *ihi; hi[1] = *jhi; if (wnga_locate_num_blocks(*g_a, lo, hi) == -1) { status = wnga_locate_region(*g_a, lo, hi, mapl, proclist, np); /* need to swap elements (ilo,jlo,ihi,jhi) -> (ilo,ihi,jlo,jhi) */ for(p = 0; p< *np; p++){ map[p][0] = mapl[4*p]; map[p][1] = mapl[4*p + 2]; map[p][2] = mapl[4*p + 1]; map[p][3] = mapl[4*p + 3]; map[p][4] = proclist[p]; } } else { wnga_error("Must call nga_locate_region on block-cyclic data distribution",0); } free(proclist); free(mapl); return status; } logical FATR nga_locate_region_( Integer *g_a, Integer *lo, Integer *hi, Integer *map, Integer *proclist, Integer *np) { return wnga_locate_region(*g_a, lo, hi, map, proclist, np); } void FATR ga_lock_(Integer *mutex) { wnga_lock(*mutex); } void FATR nga_lock_(Integer *mutex) { wnga_lock(*mutex); } logical FATR ga_locate_(Integer *g_a, Integer *i, Integer *j, Integer *owner) { Integer subscript[2]; subscript[0] = *i; subscript[1] = *j; return wnga_locate(*g_a, subscript, owner); } logical FATR nga_locate_(Integer *g_a, Integer *subscript, Integer *owner) { return wnga_locate(*g_a, subscript, owner); } void FATR ga_mask_sync_(Integer *begin, Integer *end) { wnga_mask_sync(*begin, *end); } void FATR nga_mask_sync_(Integer *begin, Integer *end) { wnga_mask_sync(*begin, *end); } Integer FATR ga_memory_avail_() { return wnga_memory_avail(); } Integer FATR nga_memory_avail_() { return wnga_memory_avail(); } logical FATR ga_memory_limited_() { return wnga_memory_limited(); } logical FATR nga_memory_limited_() { return wnga_memory_limited(); } void FATR nga_merge_distr_patch_(Integer *g_a, Integer *alo, Integer *ahi, Integer *g_b, Integer *blo, Integer *bhi) { wnga_merge_distr_patch(*g_a, alo, ahi, *g_b, blo, bhi); } void FATR ga_merge_mirrored_(Integer *g_a) { wnga_merge_mirrored(*g_a); } void FATR nga_merge_mirrored_(Integer *g_a) { wnga_merge_mirrored(*g_a); } void FATR ga_nblock_(Integer *g_a, Integer *nblock) { wnga_nblock(*g_a, nblock); } void FATR nga_nblock_(Integer *g_a, Integer *nblock) { wnga_nblock(*g_a, nblock); } Integer FATR ga_ndim_(Integer *g_a) { return wnga_ndim(*g_a); } Integer FATR nga_ndim_(Integer *g_a) { return wnga_ndim(*g_a); } Integer FATR ga_nnodes_() { return wnga_nnodes(); } Integer FATR nga_nnodes_() { return wnga_nnodes(); } Integer FATR ga_nodeid_() { return wnga_nodeid(); } Integer FATR nga_nodeid_() { return wnga_nodeid(); } Integer FATR ga_pgroup_absolute_id_(Integer *grp, Integer *pid) { return wnga_pgroup_absolute_id(*grp, *pid); } Integer FATR nga_pgroup_absolute_id_(Integer *grp, Integer *pid) { return wnga_pgroup_absolute_id(*grp, *pid); } Integer FATR ga_pgroup_create_(Integer *list, Integer *count) { return wnga_pgroup_create(list, *count); } Integer FATR nga_pgroup_create_(Integer *list, Integer *count) { return wnga_pgroup_create(list, *count); } Integer FATR ga_pgroup_duplicate_(Integer *grp) { return wnga_pgroup_duplicate(*grp); } Integer FATR nga_pgroup_duplicate_(Integer *grp) { return wnga_pgroup_duplicate(*grp); } Integer FATR ga_pgroup_self_() { return wnga_pgroup_self(); } Integer FATR nga_pgroup_self_() { return wnga_pgroup_self(); } logical FATR ga_pgroup_destroy_(Integer *grp) { return wnga_pgroup_destroy(*grp); } logical FATR nga_pgroup_destroy_(Integer *grp) { return wnga_pgroup_destroy(*grp); } Integer FATR ga_pgroup_get_default_() { return wnga_pgroup_get_default(); } Integer FATR nga_pgroup_get_default_() { return wnga_pgroup_get_default(); } Integer FATR ga_pgroup_get_mirror_() { return wnga_pgroup_get_mirror(); } Integer FATR nga_pgroup_get_mirror_() { return wnga_pgroup_get_mirror(); } Integer FATR ga_pgroup_get_world_() { return wnga_pgroup_get_world(); } Integer FATR nga_pgroup_get_world_() { return wnga_pgroup_get_world(); } void FATR ga_pgroup_set_default_(Integer *grp) { wnga_pgroup_set_default(*grp); } void FATR nga_pgroup_set_default_(Integer *grp) { wnga_pgroup_set_default(*grp); } Integer FATR ga_pgroup_split_(Integer *grp, Integer *grp_num) { return wnga_pgroup_split(*grp, *grp_num); } Integer FATR nga_pgroup_split_(Integer *grp, Integer *grp_num) { return wnga_pgroup_split(*grp, *grp_num); } Integer FATR ga_pgroup_split_irreg_(Integer *grp, Integer *mycolor) { return wnga_pgroup_split_irreg(*grp, *mycolor); } Integer FATR nga_pgroup_split_irreg_(Integer *grp, Integer *mycolor) { return wnga_pgroup_split_irreg(*grp, *mycolor); } Integer FATR ga_pgroup_nnodes_(Integer *grp) { return wnga_pgroup_nnodes(*grp); } Integer FATR nga_pgroup_nnodes_(Integer *grp) { return wnga_pgroup_nnodes(*grp); } Integer FATR ga_pgroup_nodeid_(Integer *grp) { return wnga_pgroup_nodeid(*grp); } Integer FATR nga_pgroup_nodeid_(Integer *grp) { return wnga_pgroup_nodeid(*grp); } void FATR ga_proc_topology_(Integer* g_a, Integer* proc, Integer* pr, Integer *pc) { Integer subscript[2]; wnga_proc_topology(*g_a, *proc, subscript); *pr = subscript[0]; *pc = subscript[1]; } void FATR nga_proc_topology_(Integer* g_a, Integer* proc, Integer* subscript) { wnga_proc_topology(*g_a, *proc, subscript); } DoublePrecision FATR ga_rand_(Integer *iseed) { return wnga_rand(*iseed); } DoublePrecision FATR nga_rand_(Integer *iseed) { return wnga_rand(*iseed); } void FATR ga_randomize_(Integer *g_a, void* val) { wnga_randomize(*g_a, val); } void FATR nga_randomize_(Integer *g_a, void* val) { wnga_randomize(*g_a, val); } void FATR ga_set_array_name_(Integer *g_a, char *array_name, int slen) { char buf[FNAM]; ga_f2cstring(array_name ,slen, buf, FNAM); wnga_set_array_name(*g_a, buf); } void FATR nga_set_array_name_(Integer *g_a, char *array_name, int slen) { char buf[FNAM]; ga_f2cstring(array_name ,slen, buf, FNAM); wnga_set_array_name(*g_a, buf); } void FATR ga_get_array_name_(Integer *g_a, char *array_name, int slen) { char buf[FNAM]; ga_f2cstring(array_name ,slen, buf, FNAM); wnga_get_array_name(*g_a, buf); } void FATR nga_get_array_name_(Integer *g_a, char *array_name, int slen) { char buf[FNAM]; ga_f2cstring(array_name ,slen, buf, FNAM); wnga_get_array_name(*g_a, buf); } void FATR ga_set_block_cyclic_(Integer *g_a, Integer *dims) { wnga_set_block_cyclic(*g_a, dims); } void FATR nga_set_block_cyclic_(Integer *g_a, Integer *dims) { wnga_set_block_cyclic(*g_a, dims); } void FATR ga_set_block_cyclic_proc_grid_(Integer *g_a, Integer *dims, Integer *proc_grid) { wnga_set_block_cyclic_proc_grid(*g_a, dims, proc_grid); } void FATR nga_set_block_cyclic_proc_grid_(Integer *g_a, Integer *dims, Integer *proc_grid) { wnga_set_block_cyclic_proc_grid(*g_a, dims, proc_grid); } void FATR ga_set_tiled_proc_grid_(Integer *g_a, Integer *dims, Integer *proc_grid) { wnga_set_tiled_proc_grid(*g_a, dims, proc_grid); } void FATR nga_set_tiled_proc_grid_(Integer *g_a, Integer *dims, Integer *proc_grid) { wnga_set_tiled_proc_grid(*g_a, dims, proc_grid); } void FATR ga_set_tiled_irreg_proc_grid_(Integer *g_a, Integer *mapc, Integer *nblocks, Integer *proc_grid) { wnga_set_tiled_irreg_proc_grid(*g_a, mapc, nblocks, proc_grid); } void FATR nga_set_tiled_irreg_proc_grid_(Integer *g_a, Integer *mapc, Integer *nblocks, Integer *proc_grid) { wnga_set_tiled_irreg_proc_grid(*g_a, mapc, nblocks, proc_grid); } void FATR ga_set_chunk_(Integer *g_a, Integer *chunk) { wnga_set_chunk(*g_a, chunk); } void FATR nga_set_chunk_(Integer *g_a, Integer *chunk) { wnga_set_chunk(*g_a, chunk); } void FATR ga_set_data_(Integer *g_a, Integer *ndim, Integer *dims, Integer *type) { wnga_set_data(*g_a, *ndim, dims, *type); } void FATR nga_set_data_(Integer *g_a, Integer *ndim, Integer *dims, Integer *type) { wnga_set_data(*g_a, *ndim, dims, *type); } void FATR ga_set_debug_(logical *flag) { wnga_set_debug(*flag); } void FATR nga_set_debug_(logical *flag) { wnga_set_debug(*flag); } void FATR ga_set_ghosts_(Integer *g_a, Integer *width) { wnga_set_ghosts(*g_a,width); } void FATR nga_set_ghosts_(Integer *g_a, Integer *width) { wnga_set_ghosts(*g_a,width); } void FATR ga_set_irreg_distr_(Integer *g_a, Integer *mapc, Integer *nblock) { wnga_set_irreg_distr(*g_a, mapc, nblock); } void FATR nga_set_irreg_distr_(Integer *g_a, Integer *mapc, Integer *nblock) { wnga_set_irreg_distr(*g_a, mapc, nblock); } void FATR ga_set_irreg_flag_(Integer *g_a, logical *flag) { wnga_set_irreg_flag(*g_a, *flag); } void FATR nga_set_irreg_flag_(Integer *g_a, logical *flag) { wnga_set_irreg_flag(*g_a, *flag); } void FATR ga_set_memory_limit_(Integer *mem_limit) { wnga_set_memory_limit(*mem_limit); } void FATR nga_set_memory_limit_(Integer *mem_limit) { wnga_set_memory_limit(*mem_limit); } void FATR ga_set_pgroup_(Integer *g_a, Integer *p_handle) { wnga_set_pgroup(*g_a, *p_handle); } void FATR nga_set_pgroup_(Integer *g_a, Integer *p_handle) { wnga_set_pgroup(*g_a, *p_handle); } void FATR ga_set_restricted_(Integer *g_a, Integer *list, Integer *size) { wnga_set_restricted(*g_a, list, *size); } void FATR nga_set_restricted_(Integer *g_a, Integer *list, Integer *size) { wnga_set_restricted(*g_a, list, *size); } void FATR ga_set_restricted_range_(Integer *g_a, Integer *lo_proc, Integer *hi_proc) { wnga_set_restricted_range(*g_a, *lo_proc, *hi_proc); } void FATR nga_set_restricted_range_(Integer *g_a, Integer *lo_proc, Integer *hi_proc) { wnga_set_restricted_range(*g_a, *lo_proc, *hi_proc); } void FATR ga_set_property_(Integer *g_a, char *property, int slen) { char buf[FNAM]; ga_f2cstring(property ,slen, buf, FNAM); wnga_set_property(*g_a, buf); } void FATR nga_set_property_(Integer *g_a, char *property, int slen) { char buf[FNAM]; ga_f2cstring(property ,slen, buf, FNAM); wnga_set_property(*g_a, buf); } void FATR ga_unset_property_(Integer *g_a) { wnga_unset_property(*g_a); } void FATR nga_unset_property_(Integer *g_a) { wnga_unset_property(*g_a); } void FATR ga_set_memory_dev_(Integer *g_a, char *device, int slen) { char buf[FNAM]; ga_f2cstring(device, slen, buf, FNAM); wnga_set_memory_dev(*g_a, buf); } void FATR nga_set_memory_dev_(Integer *g_a, char *device, int slen) { char buf[FNAM]; ga_f2cstring(device, slen, buf, FNAM); wnga_set_memory_dev(*g_a, buf); } void FATR ga_terminate_() { wnga_terminate(); _ga_initialize_f=0; } void FATR nga_terminate_() { wnga_terminate(); _ga_initialize_f=0; } Integer FATR ga_total_blocks_(Integer *g_a) { return wnga_total_blocks(*g_a); } Integer FATR nga_total_blocks_(Integer *g_a) { return wnga_total_blocks(*g_a); } void FATR ga_unlock_(Integer *mutex) { wnga_unlock(*mutex); } void FATR nga_unlock_(Integer *mutex) { wnga_unlock(*mutex); } logical FATR ga_uses_ma_() { return wnga_uses_ma(); } logical FATR nga_uses_ma_() { return wnga_uses_ma(); } logical FATR ga_uses_proc_grid_(Integer *g_a) { return wnga_uses_proc_grid(*g_a); } logical FATR nga_uses_proc_grid_(Integer *g_a) { return wnga_uses_proc_grid(*g_a); } logical FATR ga_valid_handle_(Integer *g_a) { return wnga_valid_handle(*g_a); } logical FATR nga_valid_handle_(Integer *g_a) { return wnga_valid_handle(*g_a); } Integer FATR ga_verify_handle_(Integer *g_a) { return wnga_verify_handle(*g_a); } Integer FATR nga_verify_handle_(Integer *g_a) { return wnga_verify_handle(*g_a); } void FATR ga_check_handle_(Integer *g_a, char *fstring, int slen) { char buf[FMSG]; ga_f2cstring(fstring , slen, buf, FMSG); wnga_check_handle(*g_a, buf); } void FATR nga_check_handle_(Integer *g_a, char *fstring, int slen) { char buf[FMSG]; ga_f2cstring(fstring , slen, buf, FMSG); wnga_check_handle(*g_a, buf); } /* Routines from onesided.c */ void FATR ga_acc_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, void *buf, Integer *ld, void *alpha) { Integer lo[2], hi[2]; lo[0]=*ilo; lo[1]=*jlo; hi[0]=*ihi; hi[1]=*jhi; wnga_acc(*g_a, lo, hi, buf, ld, alpha); } void FATR nga_acc_(Integer *g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, void *alpha) { wnga_acc(*g_a, lo, hi, buf, ld, alpha); } void FATR ga_access_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, AccessIndex* index, Integer *ld) { Integer lo[2], hi[2]; lo[0]=*ilo; lo[1]=*jlo; hi[0]=*ihi; hi[1]=*jhi; wnga_access_idx(*g_a, lo, hi, index, ld); } void FATR nga_access_(Integer* g_a, Integer lo[], Integer hi[], AccessIndex* index, Integer ld[]) { wnga_access_idx(*g_a, lo, hi, index, ld); } void FATR nga_access_block_(Integer* g_a, Integer* idx, AccessIndex* index, Integer *ld) { wnga_access_block_idx(*g_a, *idx, index, ld); } void FATR nga_access_block_grid_(Integer* g_a, Integer* subscript, AccessIndex *index, Integer *ld) { wnga_access_block_grid_idx(*g_a, subscript, index, ld); } void FATR nga_access_block_segment_(Integer* g_a, Integer *proc, AccessIndex* index, Integer *len) { wnga_access_block_segment_idx(*g_a, *proc, index, len); } void FATR nga_alloc_gatscat_buf_(Integer *nelems) { wnga_alloc_gatscat_buf(*nelems); } void FATR ga_fence_() { wnga_fence(); } void FATR nga_fence_() { wnga_fence(); } void FATR nga_free_gatscat_buf_() { wnga_free_gatscat_buf(); } void FATR ga_gather_(Integer *g_a, void *v, Integer *i, Integer *j, Integer *nv) { wnga_gather2d(*g_a, v, i, j, *nv); } void FATR nga_gather_(Integer *g_a, void* v, Integer subscript[], Integer *nv) { wnga_gather(*g_a, v, subscript, 0, *nv); } void FATR ga_get_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, void *buf, Integer *ld) { Integer lo[2], hi[2]; lo[0] = *ilo; lo[1] = *jlo; hi[0] = *ihi; hi[1] = *jhi; wnga_get(*g_a, lo, hi, buf, ld); } void FATR nga_get_(Integer *g_a, Integer *lo, Integer *hi, void *buf, Integer *ld) { wnga_get(*g_a, lo, hi, buf, ld); } void FATR ga_init_fence_() { wnga_init_fence(); } void FATR nga_init_fence_() { wnga_init_fence(); } void FATR ga_nbacc_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, void *buf, Integer *ld, void *alpha, Integer *nbhandle) { Integer lo[2], hi[2]; lo[0]=*ilo; lo[1]=*jlo; hi[0]=*ihi; hi[1]=*jhi; wnga_nbacc(*g_a, lo, hi, buf, ld, alpha, nbhandle); } void FATR nga_nbacc_(Integer *g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, void *alpha, Integer *nbhandle) { wnga_nbacc(*g_a, lo, hi, buf, ld, alpha, nbhandle); } void FATR ga_nbget_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, void *buf, Integer *ld, Integer *nbhandle) { Integer lo[2], hi[2]; lo[0]=*ilo; lo[1]=*jlo; hi[0]=*ihi; hi[1]=*jhi; wnga_nbget(*g_a, lo, hi, buf, ld, nbhandle); } void FATR nga_nbget_(Integer *g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, Integer *nbhandle) { wnga_nbget(*g_a, lo, hi, buf, ld, nbhandle); } void FATR ga_nbput_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, void *buf, Integer *ld, Integer *nbhandle) { Integer lo[2], hi[2]; lo[0]=*ilo; lo[1]=*jlo; hi[0]=*ihi; hi[1]=*jhi; wnga_nbput(*g_a, lo, hi, buf, ld, nbhandle); } void FATR nga_nbput_(Integer *g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, Integer *nbhandle) { wnga_nbput(*g_a, lo, hi, buf, ld, nbhandle); } void FATR nga_nbput_notify_(Integer *g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, Integer *g_b, Integer *ecoords, void *bufn, Integer *nbhandle) { wnga_nbput_notify(*g_a, lo, hi, buf, ld, *g_b, ecoords, bufn, nbhandle); } /* nga_nbput_notify_ */ void FATR nga_nbwait_notify_(Integer *nbhandle) { wnga_nbwait_notify(nbhandle); } /* nga_nbwait_notify_ */ Integer FATR ga_nbtest_(Integer *nbhandle) { return wnga_nbtest(nbhandle); } Integer FATR nga_nbtest_(Integer *nbhandle) { return wnga_nbtest(nbhandle); } void FATR ga_nbwait_(Integer *nbhandle) { wnga_nbwait(nbhandle); } void FATR nga_nbwait_(Integer *nbhandle) { wnga_nbwait(nbhandle); } void FATR ga_pgroup_sync_(Integer *grp_id) { wnga_pgroup_sync(*grp_id); } void FATR nga_pgroup_sync_(Integer *grp_id) { wnga_pgroup_sync(*grp_id); } void FATR ga_put_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, void *buf, Integer *ld) { Integer lo[2], hi[2]; lo[0]=*ilo; lo[1]=*jlo; hi[0]=*ihi; hi[1]=*jhi; wnga_put(*g_a, lo, hi, buf, ld); } void FATR nga_put_(Integer *g_a, Integer *lo, Integer *hi, void *buf, Integer *ld) { wnga_put(*g_a, lo, hi, buf, ld); } Integer FATR ga_read_inc_(Integer *g_a, Integer *i, Integer *j, Integer *inc) { Integer subscript[2]; subscript[0] = *i; subscript[1] = *j; return wnga_read_inc(*g_a, subscript, *inc); } Integer FATR nga_read_inc_(Integer *g_a, Integer *subscript, Integer *inc) { return wnga_read_inc(*g_a, subscript, *inc); } void FATR ga_release_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi) { Integer lo[2], hi[2]; lo[0]=*ilo; lo[1]=*jlo; hi[0]=*ihi; hi[1]=*jhi; wnga_release(*g_a, lo, hi); } void FATR nga_release_(Integer *g_a, Integer *lo, Integer *hi) { wnga_release(*g_a, lo, hi); } void FATR nga_release_block_(Integer *g_a, Integer *iblock) { wnga_release_block(*g_a, *iblock); } void FATR nga_release_block_grid_(Integer *g_a, Integer *index) { wnga_release_block_grid(*g_a, index); } void FATR nga_release_block_segment_(Integer *g_a, Integer *iproc) { wnga_release_block_segment(*g_a, *iproc); } void FATR ga_release_update_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi) { Integer lo[2], hi[2]; lo[0]=*ilo; lo[1]=*jlo; hi[0]=*ihi; hi[1]=*jhi; wnga_release_update(*g_a, lo, hi); } void FATR nga_release_update_(Integer *g_a, Integer *lo, Integer *hi) { wnga_release_update(*g_a, lo, hi); } void FATR nga_release_update_block_(Integer *g_a, Integer *iblock) { wnga_release_update_block(*g_a, *iblock); } void FATR nga_release_update_block_grid_(Integer *g_a, Integer *index) { wnga_release_update_block_grid(*g_a, index); } void FATR nga_release_update_block_segment_(Integer *g_a, Integer *iproc) { wnga_release_update_block_segment(*g_a, *iproc); } void FATR ga_scatter_(Integer *g_a, void *v, Integer *i, Integer *j, Integer *nv) { wnga_scatter2d(*g_a, v, i, j, *nv); } void FATR nga_scatter_(Integer *g_a, void* v, Integer subscript[], Integer *nv) { wnga_scatter(*g_a, v, subscript, 0, *nv); } void FATR ga_scatter_acc_(Integer *g_a, void *v, Integer *i, Integer *j, Integer *nv, void *alpha) { wnga_scatter_acc2d(*g_a, v, i, j, *nv, alpha); } void FATR nga_scatter_acc_(Integer *g_a, void* v, Integer subscript[], Integer *nv, void *alpha) { wnga_scatter_acc(*g_a, v, subscript, 0, *nv, alpha); } void FATR nga_strided_acc_(Integer *g_a, Integer *lo, Integer *hi, Integer *skip, void *buf, Integer *ld, void *alpha) { wnga_strided_acc(*g_a, lo, hi, skip, buf, ld, alpha); } void FATR nga_strided_get_(Integer *g_a, Integer *lo, Integer *hi, Integer *skip, void *buf, Integer *ld) { wnga_strided_get(*g_a, lo, hi, skip, buf, ld); } void FATR nga_strided_put_(Integer *g_a, Integer *lo, Integer *hi, Integer *skip, void *buf, Integer *ld) { wnga_strided_put(*g_a, lo, hi, skip, buf, ld); } void FATR ga_sync_() { wnga_sync(); } void FATR nga_sync_() { wnga_sync(); } void FATR ga_msg_sync_() { wnga_msg_sync(); } void FATR nga_msg_sync_() { wnga_msg_sync(); } /* Routines from global.util.c */ void FATR ga_print_stats_() { wnga_print_stats(); } void FATR nga_print_stats_() { wnga_print_stats(); } void FATR ga_error_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *string, Integer *icode, int slen #else char *string, int slen, Integer *icode #endif ) { char buf[FMSG]; ga_f2cstring(string,slen, buf, FMSG); wnga_error(buf,*icode); } void FATR nga_error_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *string, Integer *icode, int slen #else char *string, int slen, Integer *icode #endif ) { char buf[FMSG]; ga_f2cstring(string,slen, buf, FMSG); wnga_error(buf,*icode); } Integer FATR ga_cluster_nodeid_() { return wnga_cluster_nodeid(); } Integer FATR nga_cluster_nodeid_() { return wnga_cluster_nodeid(); } Integer FATR ga_cluster_nprocs_(Integer *node) { return wnga_cluster_nprocs(*node); } Integer FATR nga_cluster_nprocs_(Integer *node) { return wnga_cluster_nprocs(*node); } Integer FATR ga_cluster_procid_(Integer *node, Integer *loc_proc_id) { return wnga_cluster_procid(*node, *loc_proc_id); } Integer FATR nga_cluster_procid_(Integer *node, Integer *loc_proc_id) { return wnga_cluster_procid(*node, *loc_proc_id); } Integer FATR ga_cluster_nnodes_() { return wnga_cluster_nnodes(); } Integer FATR nga_cluster_nnodes_() { return wnga_cluster_nnodes(); } Integer FATR ga_cluster_proc_nodeid_(Integer *proc) { return wnga_cluster_proc_nodeid(*proc); } Integer FATR nga_cluster_proc_nodeid_(Integer *proc) { return wnga_cluster_proc_nodeid(*proc); } void FATR ga_print_distribution_(Integer* g_a) { wnga_print_distribution(1, *g_a); } void FATR nga_print_distribution_(Integer* g_a) { wnga_print_distribution(1, *g_a); } DoublePrecision FATR ga_wtime_() { return wnga_wtime(); } DoublePrecision FATR nga_wtime_() { return wnga_wtime(); } /* Routines from collect.c */ void FATR ga_brdcst_( Integer *type, void *buf, Integer *len, Integer *originator) { wnga_brdcst(*type, buf, *len, *originator); } void FATR nga_brdcst_( Integer *type, void *buf, Integer *len, Integer *originator) { wnga_brdcst(*type, buf, *len, *originator); } void FATR ga_pgroup_brdcst_(Integer *grp_id, Integer *type, void *buf, Integer *len, Integer *originator) { wnga_pgroup_brdcst(*grp_id, *type, buf, *len, *originator); } void FATR ga_pgroup_gop_(Integer *grp, Integer *type, void *x, Integer *n, char *op, int len) { wnga_pgroup_gop(*grp, pnga_type_f2c(*type), x, *n, op); } void FATR nga_pgroup_gop_(Integer *grp, Integer *type, void *x, Integer *n, char *op, int len) { wnga_pgroup_gop(*grp, pnga_type_f2c(*type), x, *n, op); } void FATR ga_pgroup_igop_(Integer *grp, Integer *type, Integer *x, Integer *n, char *op, int len) { wnga_pgroup_gop(*grp, pnga_type_f2c(MT_F_INT), x, *n, op); } void FATR nga_pgroup_igop_(Integer *grp, Integer *type, Integer *x, Integer *n, char *op, int len) { wnga_pgroup_gop(*grp, pnga_type_f2c(MT_F_INT), x, *n, op); } void FATR ga_pgroup_sgop_(Integer *grp, Integer *type, Real *x, Integer *n, char *op, int len) { wnga_pgroup_gop(*grp, pnga_type_f2c(MT_F_REAL), x, *n, op); } void FATR nga_pgroup_sgop_(Integer *grp, Integer *type, Real *x, Integer *n, char *op, int len) { wnga_pgroup_gop(*grp, pnga_type_f2c(MT_F_REAL), x, *n, op); } void FATR ga_pgroup_dgop_(Integer *grp, Integer *type, DoublePrecision *x, Integer *n, char *op, int len) { wnga_pgroup_gop(*grp, pnga_type_f2c(MT_F_DBL), x, *n, op); } void FATR nga_pgroup_dgop_(Integer *grp, Integer *type, DoublePrecision *x, Integer *n, char *op, int len) { wnga_pgroup_gop(*grp, pnga_type_f2c(MT_F_DBL), x, *n, op); } void FATR ga_pgroup_cgop_(Integer *grp, Integer *type, SingleComplex *x, Integer *n, char *op, int len) { wnga_pgroup_gop(*grp, pnga_type_f2c(MT_F_SCPL), x, *n, op); } void FATR nga_pgroup_cgop_(Integer *grp, Integer *type, SingleComplex *x, Integer *n, char *op, int len) { wnga_pgroup_gop(*grp, pnga_type_f2c(MT_F_SCPL), x, *n, op); } void FATR ga_pgroup_zgop_(Integer *grp, Integer *type, DoubleComplex *x, Integer *n, char *op, int len) { wnga_pgroup_gop(*grp, pnga_type_f2c(MT_F_DCPL), x, *n, op); } void FATR nga_pgroup_zgop_(Integer *grp, Integer *type, DoubleComplex *x, Integer *n, char *op, int len) { wnga_pgroup_gop(*grp, pnga_type_f2c(MT_F_DCPL), x, *n, op); } void FATR ga_gop_(Integer *type, void *x, Integer *n, char *op, int len) { wnga_gop(pnga_type_f2c(*type), x, *n, op); } void FATR nga_gop_(Integer *type, void *x, Integer *n, char *op, int len) { wnga_gop(pnga_type_f2c(*type), x, *n, op); } void FATR ga_igop_(Integer *type, Integer *x, Integer *n, char *op, int len) { wnga_gop(pnga_type_f2c(MT_F_INT), x, *n, op); } void FATR nga_igop_(Integer *type, Integer *x, Integer *n, char *op, int len) { wnga_gop(pnga_type_f2c(MT_F_INT), x, *n, op); } void FATR ga_sgop_(Integer *type, Real *x, Integer *n, char *op, int len) { wnga_gop(pnga_type_f2c(MT_F_REAL), x, *n, op); } void FATR nga_sgop_(Integer *type, Real *x, Integer *n, char *op, int len) { wnga_gop(pnga_type_f2c(MT_F_REAL), x, *n, op); } void FATR ga_dgop_(Integer *type, DoublePrecision *x, Integer *n, char *op, int len) { wnga_gop(pnga_type_f2c(MT_F_DBL), x, *n, op); } void FATR nga_dgop_(Integer *type, DoublePrecision *x, Integer *n, char *op, int len) { wnga_gop(pnga_type_f2c(MT_F_DBL), x, *n, op); } void FATR ga_cgop_(Integer *type, SingleComplex *x, Integer *n, char *op, int len) { wnga_gop(pnga_type_f2c(MT_F_SCPL), x, *n, op); } void FATR nga_cgop_(Integer *type, SingleComplex *x, Integer *n, char *op, int len) { wnga_gop(pnga_type_f2c(MT_F_SCPL), x, *n, op); } void FATR ga_zgop_(Integer *type, DoubleComplex *x, Integer *n, char *op, int len) { wnga_gop(pnga_type_f2c(MT_F_DCPL), x, *n, op); } void FATR nga_zgop_(Integer *type, DoubleComplex *x, Integer *n, char *op, int len) { wnga_gop(pnga_type_f2c(MT_F_DCPL), x, *n, op); } #ifdef MSG_COMMS_MPI # include "ga-mpi.h" # define ga_mpi_comm_ F77_FUNC_(ga_mpi_comm,GA_MPI_COMM) void FATR ga_mpi_comm_(Integer *fcomm) { MPI_Comm ccomm; ccomm = GA_MPI_Comm(); *fcomm = (Integer)(MPI_Comm_c2f(ccomm)); } #define ga_mpi_comm_pgroup_ F77_FUNC_(ga_mpi_comm_pgroup,GA_MPI_COMM_PGROUP) void FATR ga_mpi_comm_pgroup_(Integer *fcomm, Integer *pgroup) { MPI_Comm ccomm; ccomm = GA_MPI_Comm_pgroup((int)(*pgroup)); *fcomm = (Integer)(MPI_Comm_c2f(ccomm)); } #define ga_mpi_comm_pgroup_default_ F77_FUNC_(ga_mpi_comm_pgroup_default,GA_MPI_COMM_PGROUP_DEFAULT) void FATR ga_mpi_comm_pgroup_default_(Integer *fcomm) { MPI_Comm ccomm; ccomm = GA_MPI_Comm_pgroup_default(); *fcomm = (Integer)(MPI_Comm_c2f(ccomm)); } #endif /* Routines from elem_alg.c */ void FATR ga_abs_value_patch_(Integer *g_a, Integer *lo, Integer *hi) { wnga_abs_value_patch(*g_a, lo, hi); } void FATR nga_abs_value_patch_(Integer *g_a, Integer *lo, Integer *hi) { wnga_abs_value_patch(*g_a, lo, hi); } void FATR ga_recip_patch_(Integer *g_a, Integer *lo, Integer *hi) { wnga_recip_patch(*g_a, lo, hi); } void FATR nga_recip_patch_(Integer *g_a, Integer *lo, Integer *hi) { wnga_recip_patch(*g_a, lo, hi); } void FATR ga_add_constant_patch_(Integer *g_a, Integer *lo, Integer *hi, void *alpha) { wnga_add_constant_patch(*g_a, lo, hi, alpha); } void FATR nga_add_constant_patch_(Integer *g_a, Integer *lo, Integer *hi, void *alpha) { wnga_add_constant_patch(*g_a, lo, hi, alpha); } void FATR ga_abs_value_(Integer *g_a) { wnga_abs_value(*g_a); } void FATR nga_abs_value_(Integer *g_a) { wnga_abs_value(*g_a); } void FATR ga_add_constant_(Integer *g_a, void *alpha) { wnga_add_constant(*g_a, alpha); } void FATR nga_add_constant_(Integer *g_a, void *alpha) { wnga_add_constant(*g_a, alpha); } void FATR ga_recip_(Integer *g_a) { wnga_recip(*g_a); } void FATR ga_elem_multiply_(Integer *g_a, Integer *g_b, Integer *g_c) { wnga_elem_multiply(*g_a, *g_b, *g_c); } void FATR nga_elem_multiply_(Integer *g_a, Integer *g_b, Integer *g_c) { wnga_elem_multiply(*g_a, *g_b, *g_c); } void FATR ga_elem_divide_(Integer *g_a, Integer *g_b, Integer *g_c) { wnga_elem_divide(*g_a, *g_b, *g_c); } void FATR nga_elem_divide_(Integer *g_a, Integer *g_b, Integer *g_c) { wnga_elem_divide(*g_a, *g_b, *g_c); } void FATR ga_elem_maximum_(Integer *g_a, Integer *g_b, Integer *g_c) { wnga_elem_maximum(*g_a, *g_b, *g_c); } void FATR nga_elem_maximum_(Integer *g_a, Integer *g_b, Integer *g_c) { wnga_elem_maximum(*g_a, *g_b, *g_c); } void FATR ga_elem_minimum_(Integer *g_a, Integer *g_b, Integer *g_c) { wnga_elem_minimum(*g_a, *g_b, *g_c); } void FATR nga_elem_minimum_(Integer *g_a, Integer *g_b, Integer *g_c) { wnga_elem_minimum(*g_a, *g_b, *g_c); } void FATR ga_elem_multiply_patch_(Integer *g_a,Integer *alo,Integer *ahi,Integer *g_b,Integer *blo,Integer *bhi,Integer *g_c,Integer *clo,Integer *chi) { wnga_elem_multiply_patch(*g_a,alo,ahi,*g_b,blo,bhi,*g_c,clo,chi); } void FATR nga_elem_multiply_patch_(Integer *g_a,Integer *alo,Integer *ahi,Integer *g_b,Integer *blo,Integer *bhi,Integer *g_c,Integer *clo,Integer *chi) { wnga_elem_multiply_patch(*g_a,alo,ahi,*g_b,blo,bhi,*g_c,clo,chi); } void FATR ga_elem_divide_patch_(Integer *g_a,Integer *alo,Integer *ahi, Integer *g_b,Integer *blo,Integer *bhi,Integer *g_c, Integer *clo,Integer *chi) { wnga_elem_divide_patch(*g_a,alo,ahi, *g_b,blo,bhi,*g_c, clo,chi); } void FATR nga_elem_divide_patch_(Integer *g_a,Integer *alo,Integer *ahi, Integer *g_b,Integer *blo,Integer *bhi,Integer *g_c, Integer *clo,Integer *chi) { wnga_elem_divide_patch(*g_a,alo,ahi, *g_b,blo,bhi,*g_c, clo,chi); } void FATR ga_elem_step_divide_patch_(Integer *g_a,Integer *alo,Integer *ahi, Integer *g_b,Integer *blo,Integer *bhi,Integer *g_c, Integer *clo,Integer *chi) { wnga_elem_step_divide_patch(*g_a,alo,ahi, *g_b,blo,bhi,*g_c, clo,chi); } void FATR nga_elem_step_divide_patch_(Integer *g_a,Integer *alo,Integer *ahi, Integer *g_b,Integer *blo,Integer *bhi,Integer *g_c, Integer *clo,Integer *chi) { wnga_elem_step_divide_patch(*g_a,alo,ahi, *g_b,blo,bhi,*g_c, clo,chi); } void FATR ga_elem_stepb_divide_patch_(Integer *g_a,Integer *alo,Integer *ahi, Integer *g_b,Integer *blo,Integer *bhi,Integer *g_c, Integer *clo,Integer *chi) { wnga_elem_stepb_divide_patch(*g_a,alo,ahi, *g_b,blo,bhi,*g_c, clo,chi); } void FATR nga_elem_stepb_divide_patch_(Integer *g_a,Integer *alo,Integer *ahi, Integer *g_b,Integer *blo,Integer *bhi,Integer *g_c, Integer *clo,Integer *chi) { wnga_elem_stepb_divide_patch(*g_a,alo,ahi, *g_b,blo,bhi,*g_c, clo,chi); } void FATR ga_elem_maximum_patch_(Integer *g_a,Integer *alo,Integer *ahi, Integer *g_b,Integer *blo,Integer *bhi,Integer *g_c,Integer *clo,Integer *chi) { wnga_elem_maximum_patch(*g_a,alo,ahi, *g_b,blo,bhi,*g_c,clo,chi); } void FATR nga_elem_maximum_patch_(Integer *g_a,Integer *alo,Integer *ahi, Integer *g_b,Integer *blo,Integer *bhi,Integer *g_c,Integer *clo,Integer *chi) { wnga_elem_maximum_patch(*g_a,alo,ahi, *g_b,blo,bhi,*g_c,clo,chi); } void FATR ga_elem_minimum_patch_(Integer *g_a,Integer *alo,Integer *ahi, Integer *g_b,Integer *blo,Integer *bhi,Integer *g_c,Integer *clo,Integer *chi) { wnga_elem_minimum_patch(*g_a,alo,ahi, *g_b,blo,bhi,*g_c,clo,chi); } void FATR nga_elem_minimum_patch_(Integer *g_a,Integer *alo,Integer *ahi, Integer *g_b,Integer *blo,Integer *bhi,Integer *g_c,Integer *clo,Integer *chi) { wnga_elem_minimum_patch(*g_a,alo,ahi, *g_b,blo,bhi,*g_c,clo,chi); } void FATR ga_step_bound_info_patch_(Integer *g_xx, Integer *xxlo, Integer *xxhi, Integer *g_vv, Integer *vvlo, Integer *vvhi, Integer *g_xxll, Integer *xxlllo, Integer *xxllhi, Integer *g_xxuu, Integer *xxuulo, Integer *xxuuhi, void *boundmin, void* wolfemin, void *boundmax) { wnga_step_bound_info_patch(*g_xx, xxlo, xxhi, *g_vv, vvlo, vvhi, *g_xxll, xxlllo, xxllhi, *g_xxuu, xxuulo, xxuuhi, boundmin, wolfemin, boundmax); } void FATR nga_step_bound_info_patch_(Integer *g_xx, Integer *xxlo, Integer *xxhi, Integer *g_vv, Integer *vvlo, Integer *vvhi, Integer *g_xxll, Integer *xxlllo, Integer *xxllhi, Integer *g_xxuu, Integer *xxuulo, Integer *xxuuhi, void *boundmin, void* wolfemin, void *boundmax) { wnga_step_bound_info_patch(*g_xx, xxlo, xxhi, *g_vv, vvlo, vvhi, *g_xxll, xxlllo, xxllhi, *g_xxuu, xxuulo, xxuuhi, boundmin, wolfemin, boundmax); } void FATR ga_step_max_patch_(Integer *g_a, Integer *alo, Integer *ahi, Integer *g_b, Integer *blo, Integer *bhi, void *result) { wnga_step_max_patch(*g_a, alo, ahi, *g_b, blo, bhi, result); } void FATR nga_step_max_patch_(Integer *g_a, Integer *alo, Integer *ahi, Integer *g_b, Integer *blo, Integer *bhi, void *result) { wnga_step_max_patch(*g_a, alo, ahi, *g_b, blo, bhi, result); } void FATR ga_step_max_(Integer *g_a, Integer *g_b, void *retval) { wnga_step_max(*g_a, *g_b, retval); } void FATR nga_step_max_(Integer *g_a, Integer *g_b, void *retval) { wnga_step_max(*g_a, *g_b, retval); } void FATR ga_step_bound_info_(Integer *g_xx, Integer *g_vv, Integer *g_xxll, Integer *g_xxuu, void *boundmin, void *wolfemin, void *boundmax) { wnga_step_bound_info(*g_xx, *g_vv, *g_xxll, *g_xxuu, boundmin, wolfemin, boundmax); } void FATR nga_step_bound_info_(Integer *g_xx, Integer *g_vv, Integer *g_xxll, Integer *g_xxuu, void *boundmin, void *wolfemin, void *boundmax) { wnga_step_bound_info(*g_xx, *g_vv, *g_xxll, *g_xxuu, boundmin, wolfemin, boundmax); } /* Routines from ga_solve_seq.c */ void FATR ga_lu_solve_seq_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *trans, Integer *g_a, Integer *g_b, int len #else char *trans, int len, Integer *g_a, Integer *g_b #endif ) { char buf[FNAM]; ga_f2cstring(trans, len, buf, FNAM); wnga_lu_solve_seq(buf, *g_a, *g_b); } /* Routines from global.util.c */ void FATR ga_print_(Integer *g_a) { wnga_print(*g_a); } void FATR nga_print_(Integer *g_a) { wnga_print(*g_a); } void FATR ga_print_patch_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, Integer *pretty) { wnga_print_patch2d(*g_a, *ilo, *ihi, *jlo, *jhi, *pretty); } void FATR nga_print_patch_(Integer *g_a, Integer *lo, Integer *hi, Integer *pretty) { wnga_print_patch(*g_a, lo, hi, *pretty); } void FATR ga_summarize_(Integer *verbose) { wnga_summarize(*verbose); } void FATR nga_summarize_(Integer *verbose) { wnga_summarize(*verbose); } /* Routines from ghosts.c */ void FATR nga_access_ghost_element_(Integer* g_a, AccessIndex* index, Integer subscript[], Integer ld[]) { wnga_access_ghost_element(*g_a, index, subscript, ld); } void FATR nga_access_ghosts_(Integer* g_a, Integer dims[], AccessIndex* index, Integer ld[]) { wnga_access_ghosts(*g_a, dims, index, ld); } void FATR nga_release_ghost_element_(Integer* g_a, Integer subscript[]) { wnga_release_ghost_element(*g_a, subscript); } void FATR nga_release_update_ghost_element_(Integer* g_a, Integer subscript[]) { wnga_release_update_ghost_element(*g_a, subscript); } void FATR nga_release_ghosts_(Integer* g_a) { wnga_release_ghosts(*g_a); } void FATR nga_release_update_ghosts_(Integer* g_a) { wnga_release_update_ghosts(*g_a); } void FATR nga_get_ghost_block_(Integer *g_a, Integer *lo, Integer *hi, void *buf, Integer *ld) { wnga_get_ghost_block(*g_a, lo, hi, buf, ld); } void FATR ga_update1_ghosts_(Integer *g_a) { wnga_update1_ghosts(*g_a); } void FATR nga_update1_ghosts_(Integer *g_a) { wnga_update1_ghosts(*g_a); } logical FATR ga_update2_ghosts_(Integer *g_a) { return wnga_update2_ghosts(*g_a); } logical FATR nga_update2_ghosts_(Integer *g_a) { return wnga_update2_ghosts(*g_a); } logical FATR ga_update3_ghosts_(Integer *g_a) { return wnga_update3_ghosts(*g_a); } logical FATR nga_update3_ghosts_(Integer *g_a) { return wnga_update3_ghosts(*g_a); } logical FATR ga_set_update4_info_(Integer *g_a) { return wnga_set_update4_info(*g_a); } logical FATR nga_set_update4_info_(Integer *g_a) { return wnga_set_update4_info(*g_a); } logical FATR ga_update4_ghosts_(Integer *g_a) { return wnga_update4_ghosts(*g_a); } logical FATR nga_update4_ghosts_(Integer *g_a) { return wnga_update4_ghosts(*g_a); } logical FATR ga_update44_ghosts_(Integer *g_a) { return wnga_update44_ghosts(*g_a); } logical FATR nga_update44_ghosts_(Integer *g_a) { return wnga_update44_ghosts(*g_a); } logical FATR ga_update55_ghosts_(Integer *g_a) { return wnga_update55_ghosts(*g_a); } logical FATR nga_update55_ghosts_(Integer *g_a) { return wnga_update55_ghosts(*g_a); } logical FATR nga_update_ghost_dir_(Integer *g_a, Integer *pdim, Integer *pdir, logical *pflag) { return wnga_update_ghost_dir(*g_a, *pdim, *pdir, *pflag); } logical FATR ga_update5_ghosts_(Integer *g_a) { return wnga_update5_ghosts(*g_a); } logical FATR nga_update5_ghosts_(Integer *g_a) { return wnga_update5_ghosts(*g_a); } logical FATR ga_set_update5_info_(Integer *g_a) { return wnga_set_update5_info(*g_a); } logical FATR nga_set_update5_info_(Integer *g_a) { return wnga_set_update5_info(*g_a); } void FATR ga_update_ghosts_(Integer *g_a) { wnga_update_ghosts(*g_a); } void FATR nga_update_ghosts_(Integer *g_a) { wnga_update_ghosts(*g_a); } void FATR nga_update_ghosts_nb_(Integer *g_a, Integer *nb) { wnga_update_ghosts_nb(*g_a, nb); } logical FATR ga_update6_ghosts_(Integer *g_a) { return wnga_update6_ghosts(*g_a); } logical FATR nga_update6_ghosts_(Integer *g_a) { return wnga_update6_ghosts(*g_a); } logical FATR ga_update7_ghosts_(Integer *g_a) { return wnga_update7_ghosts(*g_a); } logical FATR nga_update7_ghosts_(Integer *g_a) { return wnga_update7_ghosts(*g_a); } void FATR ga_ghost_barrier_() { wnga_ghost_barrier(); } void FATR nga_ghost_barrier_() { wnga_ghost_barrier(); } void FATR nga_nbget_ghost_dir_(Integer *g_a, Integer *mask, Integer *nbhandle) { wnga_nbget_ghost_dir(*g_a, mask, nbhandle); } void FATR ga_set_ghost_corner_flag_(Integer *g_a, logical *flag) { wnga_set_ghost_corner_flag(*g_a, *flag); } void FATR nga_set_ghost_corner_flag_(Integer *g_a, logical *flag) { wnga_set_ghost_corner_flag(*g_a, *flag); } logical FATR ga_set_ghost_info_(Integer *g_a) { return wnga_set_ghost_info(*g_a); } logical FATR nga_set_ghost_info_(Integer *g_a) { return wnga_set_ghost_info(*g_a); } /* Routines from global.nalg.c */ void FATR ga_zero_(Integer *g_a) { wnga_zero(*g_a); } void FATR nga_zero_(Integer *g_a) { wnga_zero(*g_a); } void FATR ga_copy_(Integer *g_a, Integer *g_b) { wnga_copy(*g_a, *g_b); } void FATR nga_copy_(Integer *g_a, Integer *g_b) { wnga_copy(*g_a, *g_b); } Integer FATR ga_idot_(Integer *g_a, Integer *g_b) { Integer sum; wnga_dot(pnga_type_f2c(MT_F_INT), *g_a, *g_b, &sum); return sum; } Integer FATR nga_idot_(Integer *g_a, Integer *g_b) { Integer sum; wnga_dot(pnga_type_f2c(MT_F_INT), *g_a, *g_b, &sum); return sum; } DoublePrecision FATR ga_ddot_(Integer *g_a, Integer *g_b) { DoublePrecision sum; wnga_dot(pnga_type_f2c(MT_F_DBL), *g_a, *g_b, &sum); return sum; } DoublePrecision FATR nga_ddot_(Integer *g_a, Integer *g_b) { DoublePrecision sum; wnga_dot(pnga_type_f2c(MT_F_DBL), *g_a, *g_b, &sum); return sum; } Real FATR ga_sdot_(Integer *g_a, Integer *g_b) { Real sum; wnga_dot(pnga_type_f2c(MT_F_REAL), *g_a, *g_b, &sum); return sum; } Real FATR nga_sdot_(Integer *g_a, Integer *g_b) { Real sum; wnga_dot(pnga_type_f2c(MT_F_REAL), *g_a, *g_b, &sum); return sum; } void FATR gai_zdot_(Integer *g_a, Integer *g_b, DoubleComplex *sum) { wnga_dot(pnga_type_f2c(MT_F_DCPL), *g_a, *g_b, sum); } void FATR ngai_zdot_(Integer *g_a, Integer *g_b, DoubleComplex *sum) { wnga_dot(pnga_type_f2c(MT_F_DCPL), *g_a, *g_b, sum); } void gai_cdot_(Integer *g_a, Integer *g_b, SingleComplex *sum) { wnga_dot(pnga_type_f2c(MT_F_SCPL), *g_a, *g_b, sum); } void ngai_cdot_(Integer *g_a, Integer *g_b, SingleComplex *sum) { wnga_dot(pnga_type_f2c(MT_F_SCPL), *g_a, *g_b, sum); } void FATR ga_scale_(Integer *g_a, void* alpha) { wnga_scale(*g_a, alpha); } void FATR ga_cscale_(Integer *g_a, SingleComplex* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_SCPL) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR ga_cscal_(Integer *g_a, SingleComplex* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_SCPL) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR ga_dscale_(Integer *g_a, DoublePrecision* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_DBL) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR ga_dscal_(Integer *g_a, DoublePrecision* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_DBL) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR ga_iscale_(Integer *g_a, Integer* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_INT || atype != C_LONG || atype != C_LONGLONG) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR ga_iscal_(Integer *g_a, Integer* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_INT || atype != C_LONG || atype != C_LONGLONG) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR ga_sscale_(Integer *g_a, Real* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_FLOAT) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR ga_sscal_(Integer *g_a, Real* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_FLOAT) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR ga_zscale_(Integer *g_a, DoubleComplex* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_DCPL) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR ga_zscal_(Integer *g_a, DoubleComplex* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_DCPL) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR nga_scale_(Integer *g_a, void* alpha) { wnga_scale(*g_a, alpha); } void FATR nga_cscale_(Integer *g_a, SingleComplex* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_SCPL) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR nga_dscale_(Integer *g_a, DoublePrecision* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_DBL) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR nga_iscale_(Integer *g_a, Integer* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_INT || atype != C_LONG || atype != C_LONGLONG) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR nga_sscale_(Integer *g_a, Real* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_FLOAT) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR nga_zscale_(Integer *g_a, DoubleComplex* alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_DCPL) pnga_error(" wrong type ", 0L); wnga_scale(*g_a, alpha); } void FATR ga_add_(void *alpha, Integer* g_a, void* beta, Integer* g_b, Integer* g_c) { wnga_add(alpha, *g_a, beta, *g_b, *g_c); } void FATR ga_cadd_(SingleComplex *alpha, Integer *g_a, SingleComplex *beta, Integer *g_b, Integer *g_c) { wnga_add(alpha, *g_a, beta, *g_b, *g_c); } void FATR ga_dadd_(DoublePrecision *alpha, Integer *g_a, DoublePrecision *beta, Integer *g_b, Integer *g_c) { wnga_add(alpha, *g_a, beta, *g_b, *g_c); } void FATR ga_iadd_(Integer *alpha, Integer *g_a, Integer *beta, Integer *g_b, Integer *g_c) { wnga_add(alpha, *g_a, beta, *g_b, *g_c); } void FATR ga_sadd_(Real *alpha, Integer *g_a, Real *beta, Integer *g_b, Integer *g_c) { wnga_add(alpha, *g_a, beta, *g_b, *g_c); } void FATR ga_zadd_(DoubleComplex *alpha, Integer *g_a, DoubleComplex *beta, Integer *g_b, Integer *g_c) { wnga_add(alpha, *g_a, beta, *g_b, *g_c); } void FATR nga_add_(void *alpha, Integer* g_a, void* beta, Integer* g_b, Integer* g_c) { wnga_add(alpha, *g_a, beta, *g_b, *g_c); } void FATR nga_cadd_(SingleComplex *alpha, Integer *g_a, SingleComplex *beta, Integer *g_b, Integer *g_c) { wnga_add(alpha, *g_a, beta, *g_b, *g_c); } void FATR nga_dadd_(DoublePrecision *alpha, Integer *g_a, DoublePrecision *beta, Integer *g_b, Integer *g_c) { wnga_add(alpha, *g_a, beta, *g_b, *g_c); } void FATR nga_iadd_(Integer *alpha, Integer *g_a, Integer *beta, Integer *g_b, Integer *g_c) { wnga_add(alpha, *g_a, beta, *g_b, *g_c); } void FATR nga_sadd_(Real *alpha, Integer *g_a, Real *beta, Integer *g_b, Integer *g_c) { wnga_add(alpha, *g_a, beta, *g_b, *g_c); } void FATR nga_zadd_(DoubleComplex *alpha, Integer *g_a, DoubleComplex *beta, Integer *g_b, Integer *g_c) { wnga_add(alpha, *g_a, beta, *g_b, *g_c); } void FATR ga_transpose_(Integer *g_a, Integer *g_b) { wnga_transpose(*g_a, *g_b); } void FATR nga_transpose_(Integer *g_a, Integer *g_b) { wnga_transpose(*g_a, *g_b); } /* Routines from global.npatch.c */ void FATR ga_copy_patch_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *trans, Integer *g_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *g_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, int len #else char *trans, int len, Integer *g_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *g_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi #endif ) { Integer alo[2], ahi[2], blo[2], bhi[2]; alo[0] = *ailo; alo[1] = *ajlo; ahi[0] = *aihi; ahi[1] = *ajhi; blo[0] = *bilo; blo[1] = *bjlo; bhi[0] = *bihi; bhi[1] = *bjhi; wnga_copy_patch(trans, *g_a, alo, ahi, *g_b, blo, bhi); } void FATR nga_copy_patch_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *trans, Integer *g_a, Integer *alo, Integer *ahi, Integer *g_b, Integer *blo, Integer *bhi, int len #else char *trans, int len, Integer *g_a, Integer *alo, Integer *ahi, Integer *g_b, Integer *blo, Integer *bhi #endif ) { wnga_copy_patch(trans,*g_a,alo,ahi,*g_b,blo,bhi); } void FATR ga_zero_patch_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi) { Integer lo[2], hi[2]; lo[0] = *ilo; lo[1] = *jlo; hi[0] = *ihi; lo[1] = *jhi; wnga_zero_patch(*g_a, lo, hi); } void FATR nga_zero_patch_(Integer *g_a, Integer *lo, Integer *hi) { wnga_zero_patch(*g_a, lo, hi); } static void sga_dot_patch(Integer g_a, char *t_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer g_b, char *t_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, void *retval) { Integer alo[2], ahi[2], blo[2], bhi[2]; alo[0] = *ailo; alo[1] = *ajlo; ahi[0] = *aihi; ahi[1] = *ajhi; blo[0] = *bilo; blo[1] = *bjlo; bhi[0] = *bihi; bhi[1] = *bjhi; wnga_dot_patch(g_a, t_a, alo, ahi, g_b, t_b, blo, bhi, retval); } #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS DoublePrecision ga_ddot_patch_(Integer *g_a, char *t_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *g_b, char *t_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, int alen, int blen) #else DoublePrecision ga_ddot_patch_(Integer *g_a, char *t_a, int alen, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *g_b, char *t_b, int blen, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi) #endif { DoublePrecision retval; Integer atype, btype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); if (atype != btype || atype != C_DBL) pnga_error(" wrong types ", 0L); sga_dot_patch(*g_a, t_a, ailo, aihi, ajlo, ajhi, *g_b, t_b, bilo, bihi, bjlo, bjhi, &retval); return retval; } #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer ga_idot_patch_(Integer *g_a, char *t_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *g_b, char *t_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, int alen, int blen) #else Integer ga_idot_patch_(Integer *g_a, char *t_a, int alen, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *g_b, char *t_b, int blen, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi) #endif { Integer retval; Integer atype, btype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); if (atype != btype || (atype != C_INT && atype != C_LONG && atype != C_LONGLONG)) pnga_error(" wrong types ", 0L); sga_dot_patch(*g_a, t_a, ailo, aihi, ajlo, ajhi, *g_b, t_b, bilo, bihi, bjlo, bjhi, &retval); return retval; } #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Real ga_sdot_patch_(Integer *g_a, char *t_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *g_b, char *t_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, int alen, int blen) #else Real ga_sdot_patch_(Integer *g_a, char *t_a, int alen, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *g_b, char *t_b, int blen, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi) #endif { Real retval; Integer atype, btype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); if (atype != btype || atype != C_FLOAT) pnga_error(" wrong types ", 0L); sga_dot_patch(*g_a, t_a, ailo, aihi, ajlo, ajhi, *g_b, t_b, bilo, bihi, bjlo, bjhi, &retval); return retval; } #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS void gai_zdot_patch_(Integer *g_a, char *t_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *g_b, char *t_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, DoubleComplex *retval, int alen, int blen) #else void gai_zdot_patch_(Integer *g_a, char *t_a, int alen, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *g_b, char *t_b, int blen, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, DoubleComplex *retval) #endif { Integer atype, btype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); if (atype != btype || atype != C_DCPL) pnga_error(" wrong types ", 0L); sga_dot_patch(*g_a, t_a, ailo, aihi, ajlo, ajhi, *g_b, t_b, bilo, bihi, bjlo, bjhi, retval); } #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS void gai_cdot_patch_(Integer *g_a, char *t_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *g_b, char *t_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, SingleComplex *retval, int alen, int blen) #else void gai_cdot_patch_(Integer *g_a, char *t_a, int alen, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *g_b, char *t_b, int blen, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, SingleComplex *retval) #endif { Integer atype, btype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); if (atype != btype || atype != C_SCPL) pnga_error(" wrong types ", 0L); sga_dot_patch(*g_a, t_a, ailo, aihi, ajlo, ajhi, *g_b, t_b, bilo, bihi, bjlo, bjhi, retval); } #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS DoublePrecision nga_ddot_patch_(Integer *g_a, char *t_a, Integer *alo, Integer *ahi, Integer *g_b, char *t_b, Integer *blo, Integer *bhi, int alen, int blen) #else DoublePrecision nga_ddot_patch_(Integer *g_a, char *t_a, int alen, Integer *alo, Integer *ahi, Integer *g_b, char *t_b, int blen, Integer *blo, Integer *bhi) #endif { DoublePrecision retval; Integer atype, btype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); if (atype != btype || atype != C_DBL) pnga_error(" wrong types ", 0L); wnga_dot_patch(*g_a, t_a, alo, ahi, *g_b, t_b, blo, bhi, &retval); return retval; } #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer nga_idot_patch_(Integer *g_a, char *t_a, Integer *alo, Integer *ahi, Integer *g_b, char *t_b, Integer *blo, Integer *bhi, int alen, int blen) #else Integer nga_idot_patch_(Integer *g_a, char *t_a, int alen, Integer *alo, Integer *ahi, Integer *g_b, char *t_b, int blen, Integer *blo, Integer *bhi) #endif { Integer retval; Integer atype, btype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); if (atype != btype || (atype != C_INT && atype != C_LONG && atype != C_LONGLONG)) pnga_error(" wrong types ", 0L); wnga_dot_patch(*g_a, t_a, alo, ahi, *g_b, t_b, blo, bhi, &retval); return retval; } #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Real nga_sdot_patch_(Integer *g_a, char *t_a, Integer *alo, Integer *ahi, Integer *g_b, char *t_b, Integer *blo, Integer *bhi, int alen, int blen) #else Real nga_sdot_patch_(Integer *g_a, char *t_a, int alen, Integer *alo, Integer *ahi, Integer *g_b, char *t_b, int blen, Integer *blo, Integer *bhi) #endif { Real retval; Integer atype, btype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); if (atype != btype || atype != C_FLOAT) pnga_error(" wrong types ", 0L); wnga_dot_patch(*g_a, t_a, alo, ahi, *g_b, t_b, blo, bhi, &retval); return retval; } #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS void ngai_cdot_patch_(Integer *g_a, char *t_a, Integer *alo, Integer *ahi, Integer *g_b, char *t_b, Integer *blo, Integer *bhi, SingleComplex *retval, int alen, int blen) #else void ngai_cdot_patch_(Integer *g_a, char *t_a, int alen, Integer *alo, Integer *ahi, Integer *g_b, char *t_b, int blen, Integer *blo, Integer *bhi, SingleComplex *retval) #endif { Integer atype, btype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); if (atype != btype || atype != C_SCPL) pnga_error(" wrong types ", 0L); wnga_dot_patch(*g_a, t_a, alo, ahi, *g_b, t_b, blo, bhi, retval); } #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS void ngai_zdot_patch_(Integer *g_a, char *t_a, Integer *alo, Integer *ahi, Integer *g_b, char *t_b, Integer *blo, Integer *bhi, DoubleComplex *retval, int alen, int blen) #else void ngai_zdot_patch_(Integer *g_a, char *t_a, int alen, Integer *alo, Integer *ahi, Integer *g_b, char *t_b, int blen, Integer *blo, Integer *bhi, DoubleComplex *retval) #endif { Integer atype, btype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); if (atype != btype || atype != C_DCPL) pnga_error(" wrong types ", 0L); wnga_dot_patch(*g_a, t_a, alo, ahi, *g_b, t_b, blo, bhi, retval); } static void sga_fill_patch(Integer g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, void* val) { Integer lo[2], hi[2]; lo[0] = *ilo; lo[1] = *jlo; hi[0] = *ihi; hi[1] = *jhi; wnga_fill_patch(g_a, lo, hi, val); } void FATR ga_fill_patch_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, void* val) { sga_fill_patch(*g_a, ilo, ihi, jlo, jhi, val); } void FATR ga_cfill_patch_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, SingleComplex *val) { sga_fill_patch(*g_a, ilo, ihi, jlo, jhi, val); } void FATR ga_dfill_patch_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, DoublePrecision *val) { sga_fill_patch(*g_a, ilo, ihi, jlo, jhi, val); } void FATR ga_ifill_patch_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, Integer *val) { sga_fill_patch(*g_a, ilo, ihi, jlo, jhi, val); } void FATR ga_sfill_patch_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, Real *val) { sga_fill_patch(*g_a, ilo, ihi, jlo, jhi, val); } void FATR ga_zfill_patch_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, DoubleComplex *val) { sga_fill_patch(*g_a, ilo, ihi, jlo, jhi, val); } void FATR nga_fill_patch_(Integer *g_a, Integer *lo, Integer *hi, void* val) { wnga_fill_patch(*g_a, lo, hi, val); } void FATR nga_cfill_patch_(Integer *g_a, Integer *lo, Integer *hi, SingleComplex* val) { wnga_fill_patch(*g_a, lo, hi, val); } void FATR nga_dfill_patch_(Integer *g_a, Integer *lo, Integer *hi, DoublePrecision* val) { wnga_fill_patch(*g_a, lo, hi, val); } void FATR nga_ifill_patch_(Integer *g_a, Integer *lo, Integer *hi, Integer* val) { wnga_fill_patch(*g_a, lo, hi, val); } void FATR nga_sfill_patch_(Integer *g_a, Integer *lo, Integer *hi, SingleComplex* val) { wnga_fill_patch(*g_a, lo, hi, val); } void FATR nga_zfill_patch_(Integer *g_a, Integer *lo, Integer *hi, DoubleComplex* val) { wnga_fill_patch(*g_a, lo, hi, val); } static void sga_scale_patch(Integer g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, void *alpha) { Integer lo[2], hi[2]; lo[0] = *ilo; lo[1] = *jlo; hi[0] = *ihi; hi[1] = *jhi; wnga_scale_patch(g_a, lo, hi, alpha); } void FATR ga_scale_patch_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, void *alpha) { sga_scale_patch(*g_a, ilo, ihi, jlo, jhi, alpha); } void FATR ga_cscal_patch_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, SingleComplex *alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_SCPL) pnga_error(" wrong types ", 0L); sga_scale_patch(*g_a, ilo, ihi, jlo, jhi, alpha); } void FATR ga_dscal_patch_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, DoublePrecision *alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_DBL) pnga_error(" wrong types ", 0L); sga_scale_patch(*g_a, ilo, ihi, jlo, jhi, alpha); } void FATR ga_iscal_patch_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, Integer *alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_INT || atype != C_LONG || atype != C_LONGLONG) pnga_error(" wrong types ", 0L); sga_scale_patch(*g_a, ilo, ihi, jlo, jhi, alpha); } void FATR ga_sscal_patch_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, Real *alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_FLOAT) pnga_error(" wrong types ", 0L); sga_scale_patch(*g_a, ilo, ihi, jlo, jhi, alpha); } void FATR ga_zscal_patch_(Integer *g_a, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, DoubleComplex *alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_DCPL) pnga_error(" wrong types ", 0L); sga_scale_patch(*g_a, ilo, ihi, jlo, jhi, alpha); } void FATR nga_scale_patch_(Integer *g_a, Integer *lo, Integer *hi, void *alpha) { wnga_scale_patch(*g_a, lo, hi, alpha); } void FATR nga_cscale_patch_(Integer *g_a, Integer *lo, Integer *hi, SingleComplex *alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_SCPL) pnga_error(" wrong types ", 0L); wnga_scale_patch(*g_a, lo, hi, alpha); } void FATR nga_dscale_patch_(Integer *g_a, Integer *lo, Integer *hi, DoublePrecision *alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_DBL) pnga_error(" wrong types ", 0L); wnga_scale_patch(*g_a, lo, hi, alpha); } void FATR nga_iscale_patch_(Integer *g_a, Integer *lo, Integer *hi, Integer *alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_INT || atype != C_LONG || atype != C_LONGLONG) pnga_error(" wrong types ", 0L); wnga_scale_patch(*g_a, lo, hi, alpha); } void FATR nga_sscale_patch_(Integer *g_a, Integer *lo, Integer *hi, Real *alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_FLOAT) pnga_error(" wrong types ", 0L); wnga_scale_patch(*g_a, lo, hi, alpha); } void FATR nga_zscale_patch_(Integer *g_a, Integer *lo, Integer *hi, DoubleComplex *alpha) { Integer atype; pnga_inquire_type(*g_a, &atype); if (atype != C_DCPL) pnga_error(" wrong types ", 0L); wnga_scale_patch(*g_a, lo, hi, alpha); } static void sga_add_patch(void *alpha, Integer g_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, void *beta, Integer g_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, Integer g_c, Integer *cilo, Integer *cihi, Integer *cjlo, Integer *cjhi) { Integer alo[2], ahi[2], blo[2], bhi[2], clo[2], chi[2]; alo[0] = *ailo; alo[1] = *ajlo; ahi[0] = *aihi; ahi[1] = *ajhi; blo[0] = *bilo; blo[1] = *bjlo; bhi[0] = *bihi; bhi[1] = *bjhi; clo[0] = *cilo; clo[1] = *cjlo; chi[0] = *cihi; chi[1] = *cjhi; wnga_add_patch(alpha, g_a, alo, ahi, beta, g_b, blo, bhi, g_c, clo, chi); } void FATR ga_add_patch_(void *alpha, Integer *g_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, void *beta, Integer *g_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, Integer *g_c, Integer *cilo, Integer *cihi, Integer *cjlo, Integer *cjhi) { sga_add_patch(alpha, *g_a, ailo, aihi, ajlo, ajhi, beta, *g_b, bilo, bihi, bjlo, bjhi, *g_c, cilo, cihi, cjlo, cjhi); } void FATR ga_cadd_patch_(SingleComplex *alpha, Integer *g_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, SingleComplex *beta, Integer *g_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, Integer *g_c, Integer *cilo, Integer *cihi, Integer *cjlo, Integer *cjhi) { Integer atype, btype, ctype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); pnga_inquire_type(*g_c, &ctype); if (atype != btype || atype != ctype || atype != C_SCPL) pnga_error(" wrong types ", 0L); sga_add_patch(alpha, *g_a, ailo, aihi, ajlo, ajhi, beta, *g_b, bilo, bihi, bjlo, bjhi, *g_c, cilo, cihi, cjlo, cjhi); } void FATR ga_dadd_patch_(DoublePrecision *alpha, Integer *g_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, DoublePrecision *beta, Integer *g_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, Integer *g_c, Integer *cilo, Integer *cihi, Integer *cjlo, Integer *cjhi) { Integer atype, btype, ctype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); pnga_inquire_type(*g_c, &ctype); if (atype != btype || atype != ctype || atype != C_DBL) pnga_error(" wrong types ", 0L); sga_add_patch(alpha, *g_a, ailo, aihi, ajlo, ajhi, beta, *g_b, bilo, bihi, bjlo, bjhi, *g_c, cilo, cihi, cjlo, cjhi); } void FATR ga_iadd_patch_(Integer *alpha, Integer *g_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *beta, Integer *g_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, Integer *g_c, Integer *cilo, Integer *cihi, Integer *cjlo, Integer *cjhi) { Integer atype, btype, ctype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); pnga_inquire_type(*g_c, &ctype); if (atype != btype || atype != ctype || (atype != C_INT && atype != C_LONG && atype != C_LONGLONG)) pnga_error(" wrong types ", 0L); sga_add_patch(alpha, *g_a, ailo, aihi, ajlo, ajhi, beta, *g_b, bilo, bihi, bjlo, bjhi, *g_c, cilo, cihi, cjlo, cjhi); } void FATR ga_sadd_patch_(Real *alpha, Integer *g_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Real *beta, Integer *g_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, Integer *g_c, Integer *cilo, Integer *cihi, Integer *cjlo, Integer *cjhi) { Integer atype, btype, ctype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); pnga_inquire_type(*g_c, &ctype); if (atype != btype || atype != ctype || atype != C_FLOAT) pnga_error(" wrong types ", 0L); sga_add_patch(alpha, *g_a, ailo, aihi, ajlo, ajhi, beta, *g_b, bilo, bihi, bjlo, bjhi, *g_c, cilo, cihi, cjlo, cjhi); } void FATR ga_zadd_patch_(DoubleComplex *alpha, Integer *g_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, DoubleComplex *beta, Integer *g_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, Integer *g_c, Integer *cilo, Integer *cihi, Integer *cjlo, Integer *cjhi) { Integer atype, btype, ctype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); pnga_inquire_type(*g_c, &ctype); if (atype != btype || atype != ctype || atype != C_DCPL) pnga_error(" wrong types ", 0L); sga_add_patch(alpha, *g_a, ailo, aihi, ajlo, ajhi, beta, *g_b, bilo, bihi, bjlo, bjhi, *g_c, cilo, cihi, cjlo, cjhi); } void FATR nga_add_patch_(void *alpha, Integer *g_a, Integer *alo, Integer *ahi, void *beta, Integer *g_b, Integer *blo, Integer *bhi, Integer *g_c, Integer *clo, Integer *chi) { wnga_add_patch(alpha, *g_a, alo, ahi, beta, *g_b, blo, bhi, *g_c, clo, chi); } void FATR nga_cadd_patch_(SingleComplex *alpha, Integer *g_a, Integer *alo, Integer *ahi, SingleComplex *beta, Integer *g_b, Integer *blo, Integer *bhi, Integer *g_c, Integer *clo, Integer *chi) { Integer atype, btype, ctype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); pnga_inquire_type(*g_c, &ctype); if (atype != btype || atype != ctype || atype != C_SCPL) pnga_error(" wrong types ", 0L); wnga_add_patch(alpha, *g_a, alo, ahi, beta, *g_b, blo, bhi, *g_c, clo, chi); } void FATR nga_dadd_patch_(DoublePrecision *alpha, Integer *g_a, Integer *alo, Integer *ahi, DoublePrecision *beta, Integer *g_b, Integer *blo, Integer *bhi, Integer *g_c, Integer *clo, Integer *chi) { Integer atype, btype, ctype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); pnga_inquire_type(*g_c, &ctype); if (atype != btype || atype != ctype || atype != C_DBL) pnga_error(" wrong types ", 0L); wnga_add_patch(alpha, *g_a, alo, ahi, beta, *g_b, blo, bhi, *g_c, clo, chi); } void FATR nga_iadd_patch_(Integer *alpha, Integer *g_a, Integer *alo, Integer *ahi, Integer *beta, Integer *g_b, Integer *blo, Integer *bhi, Integer *g_c, Integer *clo, Integer *chi) { Integer atype, btype, ctype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); pnga_inquire_type(*g_c, &ctype); if (atype != btype || atype != ctype || (atype != C_INT && atype != C_LONG && atype != C_LONGLONG)) pnga_error(" wrong types ", 0L); wnga_add_patch(alpha, *g_a, alo, ahi, beta, *g_b, blo, bhi, *g_c, clo, chi); } void FATR nga_sadd_patch_(Real *alpha, Integer *g_a, Integer *alo, Integer *ahi, Real *beta, Integer *g_b, Integer *blo, Integer *bhi, Integer *g_c, Integer *clo, Integer *chi) { Integer atype, btype, ctype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); pnga_inquire_type(*g_c, &ctype); if (atype != btype || atype != ctype || atype != C_FLOAT) pnga_error(" wrong types ", 0L); wnga_add_patch(alpha, *g_a, alo, ahi, beta, *g_b, blo, bhi, *g_c, clo, chi); } void FATR nga_zadd_patch_(DoubleComplex *alpha, Integer *g_a, Integer *alo, Integer *ahi, DoubleComplex *beta, Integer *g_b, Integer *blo, Integer *bhi, Integer *g_c, Integer *clo, Integer *chi) { Integer atype, btype, ctype; pnga_inquire_type(*g_a, &atype); pnga_inquire_type(*g_b, &btype); pnga_inquire_type(*g_c, &ctype); if (atype != btype || atype != ctype || atype != C_DCPL) pnga_error(" wrong types ", 0L); wnga_add_patch(alpha, *g_a, alo, ahi, beta, *g_b, blo, bhi, *g_c, clo, chi); } /* Routines from select.c */ void FATR nga_select_elem_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *g_a, char* op, void* val, Integer *subscript, int oplen #else Integer *g_a, char* op, int oplen, void* val, Integer *subscript #endif ) { wnga_select_elem(*g_a, op, val, subscript); } /* Routines from sparse.c */ void FATR ga_patch_enum_(Integer* g_a, Integer* lo, Integer* hi, void* start, void* stride) { wnga_patch_enum(*g_a, *lo, *hi, start, stride); } void FATR nga_patch_enum_(Integer* g_a, Integer* lo, Integer* hi, void* start, void* stride) { wnga_patch_enum(*g_a, *lo, *hi, start, stride); } void FATR ga_scan_copy_(Integer* g_a, Integer* g_b, Integer* g_sbit, Integer* lo, Integer* hi) { wnga_scan_copy(*g_a, *g_b, *g_sbit, *lo, *hi); } void FATR nga_scan_copy_(Integer* g_a, Integer* g_b, Integer* g_sbit, Integer* lo, Integer* hi) { wnga_scan_copy(*g_a, *g_b, *g_sbit, *lo, *hi); } void FATR ga_scan_add_(Integer* g_a, Integer* g_b, Integer* g_sbit, Integer* lo, Integer* hi, Integer* excl) { wnga_scan_add(*g_a, *g_b, *g_sbit, *lo, *hi, *excl); } void FATR nga_scan_add_(Integer* g_a, Integer* g_b, Integer* g_sbit, Integer* lo, Integer* hi, Integer* excl) { wnga_scan_add(*g_a, *g_b, *g_sbit, *lo, *hi, *excl); } void FATR ga_pack_(Integer* g_a, Integer* g_b, Integer* g_sbit, Integer* lo, Integer* hi, Integer* icount) { wnga_pack(*g_a, *g_b, *g_sbit, *lo, *hi, icount); } void FATR nga_pack_(Integer* g_a, Integer* g_b, Integer* g_sbit, Integer* lo, Integer* hi, Integer* icount) { wnga_pack(*g_a, *g_b, *g_sbit, *lo, *hi, icount); } void FATR ga_unpack_(Integer* g_a, Integer* g_b, Integer* g_sbit, Integer* lo, Integer* hi, Integer* icount) { wnga_unpack(*g_a, *g_b, *g_sbit, *lo, *hi, icount); } void FATR nga_unpack_(Integer* g_a, Integer* g_b, Integer* g_sbit, Integer* lo, Integer* hi, Integer* icount) { wnga_unpack(*g_a, *g_b, *g_sbit, *lo, *hi, icount); } logical FATR ga_create_bin_range_(Integer *g_bin, Integer *g_cnt, Integer *g_off, Integer *g_range) { return wnga_create_bin_range(*g_bin, *g_cnt, *g_off, g_range); } logical FATR nga_create_bin_range_(Integer *g_bin, Integer *g_cnt, Integer *g_off, Integer *g_range) { return wnga_create_bin_range(*g_bin, *g_cnt, *g_off, g_range); } void FATR ga_bin_sorter_(Integer *g_bin, Integer *g_cnt, Integer *g_off) { wnga_bin_sorter(*g_bin, *g_cnt, *g_off); } void FATR nga_bin_sorter_(Integer *g_bin, Integer *g_cnt, Integer *g_off) { wnga_bin_sorter(*g_bin, *g_cnt, *g_off); } void FATR ga_bin_index_(Integer *g_bin, Integer *g_cnt, Integer *g_off, Integer *values, Integer *subs, Integer *n, Integer *sortit) { wnga_bin_index(*g_bin, *g_cnt, *g_off, values, subs, *n, *sortit); } void FATR nga_bin_index_(Integer *g_bin, Integer *g_cnt, Integer *g_off, Integer *values, Integer *subs, Integer *n, Integer *sortit) { wnga_bin_index(*g_bin, *g_cnt, *g_off, values, subs, *n, *sortit); } /* Routines from matrix.c */ void FATR ga_median_patch_(Integer *g_a, Integer *alo, Integer *ahi, Integer *g_b, Integer *blo, Integer *bhi, Integer *g_c, Integer *clo, Integer *chi, Integer *g_m, Integer *mlo, Integer *mhi) { wnga_median_patch(*g_a, alo, ahi, *g_b, blo, bhi, *g_c, clo, chi, *g_m, mlo, mhi); } void FATR nga_median_patch_(Integer *g_a, Integer *alo, Integer *ahi, Integer *g_b, Integer *blo, Integer *bhi, Integer *g_c, Integer *clo, Integer *chi, Integer *g_m, Integer *mlo, Integer *mhi) { wnga_median_patch(*g_a, alo, ahi, *g_b, blo, bhi, *g_c, clo, chi, *g_m, mlo, mhi); } void FATR ga_median_(Integer * g_a, Integer * g_b, Integer * g_c, Integer * g_m){ wnga_median(*g_a, *g_b, *g_c, *g_m); } void FATR nga_median_(Integer * g_a, Integer * g_b, Integer * g_c, Integer * g_m){ wnga_median(*g_a, *g_b, *g_c, *g_m); } void FATR ga_norm_infinity_(Integer * g_a, double *nm) { wnga_norm_infinity(*g_a, nm); } void FATR nga_norm_infinity_(Integer * g_a, double *nm) { wnga_norm_infinity(*g_a, nm); } void FATR ga_norm1_(Integer * g_a, double *nm) { wnga_norm1(*g_a, nm); } void FATR nga_norm1_(Integer * g_a, double *nm) { wnga_norm1(*g_a, nm); } void FATR ga_get_diag_(Integer * g_a, Integer * g_v) { wnga_get_diag(*g_a, *g_v); } void FATR nga_get_diag_(Integer * g_a, Integer * g_v) { wnga_get_diag(*g_a, *g_v); } void FATR ga_add_diagonal_(Integer * g_a, Integer * g_v) { wnga_add_diagonal(*g_a, *g_v); } void FATR nga_add_diagonal_(Integer * g_a, Integer * g_v) { wnga_add_diagonal(*g_a, *g_v); } void FATR ga_set_diagonal_(Integer * g_a, Integer * g_v) { wnga_set_diagonal(*g_a, *g_v); } void FATR nga_set_diagonal_(Integer * g_a, Integer * g_v) { wnga_set_diagonal(*g_a, *g_v); } void FATR ga_shift_diagonal_(Integer * g_a, void *c) { wnga_shift_diagonal(*g_a, c); } void FATR nga_shift_diagonal_(Integer * g_a, void *c) { wnga_shift_diagonal(*g_a, c); } void FATR ga_zero_diagonal_(Integer * g_a) { wnga_zero_diagonal(*g_a); } void FATR nga_zero_diagonal_(Integer * g_a) { wnga_zero_diagonal(*g_a); } void FATR ga_scale_rows_(Integer *g_a, Integer *g_v) { wnga_scale_rows(*g_a, *g_v); } void FATR nga_scale_rows_(Integer *g_a, Integer *g_v) { wnga_scale_rows(*g_a, *g_v); } void FATR ga_scale_cols_(Integer *g_a, Integer *g_v) { wnga_scale_cols(*g_a, *g_v); } void FATR nga_scale_cols_(Integer *g_a, Integer *g_v) { wnga_scale_cols(*g_a, *g_v); } /* Routines from ga_symmetr.c */ void FATR ga_symmetrize_(Integer *g_a) { wnga_symmetrize(*g_a); } void FATR nga_symmetrize_(Integer *g_a) { wnga_symmetrize(*g_a); } /* Routines from global.periodic.c */ void FATR nga_periodic_get_(Integer *g_a, Integer *lo, Integer *hi, void *buf, Integer *ld) { wnga_periodic(*g_a, lo, hi, buf, ld, NULL, PERIODIC_GET); } void FATR nga_periodic_put_(Integer *g_a, Integer *lo, Integer *hi, void *buf, Integer *ld) { wnga_periodic(*g_a, lo, hi, buf, ld, NULL, PERIODIC_PUT); } void FATR nga_periodic_acc_(Integer *g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, void *alpha) { wnga_periodic(*g_a, lo, hi, buf, ld, alpha, PERIODIC_ACC); } /* Routines from matmul.c */ void FATR ga_matmul_mirrored_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS transa, transb, alpha, beta, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi, alen, blen #else transa, alen, transb, blen, alpha, beta, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi #endif ) Integer *g_a, *ailo, *aihi, *ajlo, *ajhi; /* patch of g_a */ Integer *g_b, *bilo, *bihi, *bjlo, *bjhi; /* patch of g_b */ Integer *g_c, *cilo, *cihi, *cjlo, *cjhi; /* patch of g_c */ void *alpha, *beta; char *transa, *transb; int alen, blen; { wnga_matmul_mirrored(transa, transb, alpha, beta, *g_a, *ailo, *aihi, *ajlo, *ajhi, *g_b, *bilo, *bihi, *bjlo, *bjhi, *g_c, *cilo, *cihi, *cjlo, *cjhi); } void FATR nga_matmul_patch_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *transa, char *transb, void *alpha, void *beta, Integer *g_a, Integer alo[], Integer ahi[], Integer *g_b, Integer blo[], Integer bhi[], Integer *g_c, Integer clo[], Integer chi[], int alen, int blen #else char *transa, int alen, char *transb, int blen, void *alpha, void *beta, Integer *g_a, Integer alo[], Integer ahi[], Integer *g_b, Integer blo[], Integer bhi[], Integer *g_c, Integer clo[], Integer chi[] #endif ) { wnga_matmul_patch(transa, transb, alpha, beta, *g_a, alo, ahi, *g_b, blo, bhi, *g_c, clo, chi); } void FATR nga_matmul_patch_alt_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *transa, char *transb, void *alpha, void *beta, Integer *g_a, Integer alo[], Integer ahi[], Integer *g_b, Integer blo[], Integer bhi[], Integer *g_c, Integer clo[], Integer chi[], int alen, int blen #else char *transa, int alen, char *transb, int blen, void *alpha, void *beta, Integer *g_a, Integer alo[], Integer ahi[], Integer *g_b, Integer blo[], Integer bhi[], Integer *g_c, Integer clo[], Integer chi[] #endif ) { wnga_matmul_patch_alt(transa, transb, alpha, beta, *g_a, alo, ahi, *g_b, blo, bhi, *g_c, clo, chi); } void FATR ga_matmul_patch_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *transa, char *transb, DoublePrecision *alpha, DoublePrecision *beta, Integer *g_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *g_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, Integer *g_c, Integer *cilo, Integer *cihi, Integer *cjlo, Integer *cjhi, int alen, int blen #else char *transa, int alen, char *transb, int blen, DoublePrecision *alpha, DoublePrecision *beta, Integer *g_a, Integer *ailo, Integer *aihi, Integer *ajlo, Integer *ajhi, Integer *g_b, Integer *bilo, Integer *bihi, Integer *bjlo, Integer *bjhi, Integer *g_c, Integer *cilo, Integer *cihi, Integer *cjlo, Integer *cjhi #endif ) { #if 0 Integer alo[2], ahi[2]; Integer blo[2], bhi[2]; Integer clo[2], chi[2]; alo[0]=*ailo; ahi[0]=*aihi; alo[1]=*ajlo; ahi[1]=*ajhi; blo[0]=*bilo; bhi[0]=*bihi; blo[1]=*bjlo; bhi[1]=*bjhi; clo[0]=*cilo; chi[0]=*cihi; clo[1]=*cjlo; chi[1]=*cjhi; pnga_matmul_patch(transa, transb, alpha, beta, g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi); #else if(pnga_is_mirrored(*g_a)) wnga_matmul_mirrored(transa, transb, (void*)alpha, (void*)beta, *g_a, *ailo, *aihi, *ajlo, *ajhi, *g_b, *bilo, *bihi, *bjlo, *bjhi, *g_c, *cilo, *cihi, *cjlo, *cjhi); else { gai_matmul_patch_flag(SET); wnga_matmul(transa, transb, (void*)alpha, (void*)beta, *g_a, *ailo, *aihi, *ajlo, *ajhi, *g_b, *bilo, *bihi, *bjlo, *bjhi, *g_c, *cilo, *cihi, *cjlo, *cjhi); gai_matmul_patch_flag(UNSET); } #endif } void FATR ga_matmul_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS transa, transb, alpha, beta, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi, alen, blen #else transa, alen, transb, blen, alpha, beta, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi #endif ) Integer *g_a, *ailo, *aihi, *ajlo, *ajhi; /* patch of g_a */ Integer *g_b, *bilo, *bihi, *bjlo, *bjhi; /* patch of g_b */ Integer *g_c, *cilo, *cihi, *cjlo, *cjhi; /* patch of g_c */ void *alpha, *beta; char *transa, *transb; int alen, blen; { wnga_matmul(transa, transb, alpha, beta, *g_a, *ailo, *aihi, *ajlo, *ajhi, *g_b, *bilo, *bihi, *bjlo, *bjhi, *g_c, *cilo, *cihi, *cjlo, *cjhi); } # define GA_DGEMM ga_dgemm_ #define SET_GEMM_INDICES\ Integer ailo = 1;\ Integer aihi = *m;\ Integer ajlo = 1;\ Integer ajhi = *k;\ \ Integer bilo = 1;\ Integer bihi = *k;\ Integer bjlo = 1;\ Integer bjhi = *n;\ \ Integer cilo = 1;\ Integer cihi = *m;\ Integer cjlo = 1;\ Integer cjhi = *n void FATR GA_DGEMM( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *transa, char *transb, Integer *m, Integer *n, Integer *k, void *alpha, Integer *g_a, Integer *g_b, void *beta, Integer *g_c, int talen, int tblen #else char *transa, int talen, char *transb, int tblen, Integer *m, Integer *n, Integer *k, void *alpha, Integer *g_a, Integer *g_b, void *beta, Integer *g_c #endif ) { SET_GEMM_INDICES; wnga_matmul(transa, transb, alpha, beta, *g_a, ailo, aihi, ajlo, ajhi, *g_b, bilo, bihi, bjlo, bjhi, *g_c, cilo, cihi, cjlo, cjhi); } void FATR ga_cgemm_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *transa, char *transb, Integer *m, Integer *n, Integer *k, void *alpha, Integer *g_a, Integer *g_b, void *beta, Integer *g_c, int talen, int tblen #else char *transa, int talen, char *transb, int tblen, Integer *m, Integer *n, Integer *k, void *alpha, Integer *g_a, Integer *g_b, void *beta, Integer *g_c #endif ) { SET_GEMM_INDICES; wnga_matmul (transa, transb, alpha, beta, *g_a, ailo, aihi, ajlo, ajhi, *g_b, bilo, bihi, bjlo, bjhi, *g_c, cilo, cihi, cjlo, cjhi); } void FATR ga_sgemm_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *transa, char *transb, Integer *m, Integer *n, Integer *k, void *alpha, Integer *g_a, Integer *g_b, void *beta, Integer *g_c, int talen, int tblen #else char *transa, int talen, char *transb, int tblen, Integer *m, Integer *n, Integer *k, void *alpha, Integer *g_a, Integer *g_b, void *beta, Integer *g_c #endif ) { SET_GEMM_INDICES; wnga_matmul (transa, transb, alpha, beta, *g_a, ailo, aihi, ajlo, ajhi, *g_b, bilo, bihi, bjlo, bjhi, *g_c, cilo, cihi, cjlo, cjhi); } void FATR ga_zgemm_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *transa, char *transb, Integer *m, Integer *n, Integer *k, void *alpha, Integer *g_a, Integer *g_b, void *beta, Integer *g_c, int talen, int tblen #else char *transa, int talen, char *transb, int tblen, Integer *m, Integer *n, Integer *k, void *alpha, Integer *g_a, Integer *g_b, void *beta, Integer *g_c #endif ) { SET_GEMM_INDICES; wnga_matmul (transa, transb, alpha, beta, *g_a, ailo, aihi, ajlo, ajhi, *g_b, bilo, bihi, bjlo, bjhi, *g_c, cilo, cihi, cjlo, cjhi); } /* Routines from ga_diag_seqc.c */ void FATR ga_diag_seq_(Integer *g_a, Integer *g_s, Integer *g_v, DoublePrecision *eval) { wnga_diag_seq(*g_a, *g_s, *g_v, eval); } void FATR ga_diag_std_seq_(Integer * g_a, Integer * g_v, DoublePrecision *eval) { wnga_diag_std_seq(*g_a, *g_v, eval); } /* Routines from peigstubs.c */ void FATR ga_diag_(Integer * g_a, Integer * g_s, Integer * g_v, DoublePrecision *eval) { wnga_diag(*g_a, *g_s, *g_v, eval); } void FATR ga_diag_std_(Integer * g_a, Integer * g_v, DoublePrecision *eval) { wnga_diag_std(*g_a, *g_v, eval); } void FATR ga_diag_reuse_(Integer * reuse, Integer * g_a, Integer * g_s, Integer * g_v, DoublePrecision *eval) { wnga_diag_reuse(*reuse, *g_a, *g_s, *g_v, eval); } /* Routines from sclstubs.c */ void FATR ga_lu_solve_alt_(Integer *tran, Integer * g_a, Integer * g_b) { wnga_lu_solve_alt(*tran, *g_a, *g_b); } void FATR ga_lu_solve_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *tran, Integer * g_a, Integer * g_b, int len #else char *tran, int len, Integer * g_a, Integer * g_b #endif ) { wnga_lu_solve(tran, *g_a, *g_b); } Integer FATR ga_llt_solve_(Integer * g_a, Integer * g_b) { return wnga_llt_solve(*g_a, *g_b); } Integer FATR nga_llt_solve_(Integer * g_a, Integer * g_b) { return wnga_llt_solve(*g_a, *g_b); } Integer FATR ga_solve_(Integer * g_a, Integer * g_b) { return wnga_solve(*g_a, *g_b); } Integer FATR nga_solve_(Integer * g_a, Integer * g_b) { return wnga_solve(*g_a, *g_b); } Integer FATR ga_spd_invert_(Integer * g_a) { return wnga_spd_invert(*g_a); } Integer FATR nga_spd_invert_(Integer * g_a) { return wnga_spd_invert(*g_a); } /* Routines from DP.c */ void FATR ga_copy_patch_dp_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS trans, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, translen #else trans, translen, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi #endif ) Integer *g_a, *ailo, *aihi, *ajlo, *ajhi; Integer *g_b, *bilo, *bihi, *bjlo, *bjhi; char *trans; int translen; { wnga_copy_patch_dp(trans,*g_a,*ailo,*aihi,*ajlo,*ajhi,*g_b,*bilo,*bihi,*bjlo,*bjhi); } DoublePrecision FATR ga_ddot_patch_dp_( #if F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS g_a, t_a, ailo, aihi, ajlo, ajhi, g_b, t_b, bilo, bihi, bjlo, bjhi, alen, blen #else g_a, t_a, alen, ailo, aihi, ajlo, ajhi, g_b, t_b, blen, bilo, bihi, bjlo, bjhi #endif ) Integer *g_a, *ailo, *aihi, *ajlo, *ajhi; /* patch of g_a */ Integer *g_b, *bilo, *bihi, *bjlo, *bjhi; /* patch of g_b */ char *t_a, *t_b; /* transpose operators */ int alen, blen; { return wnga_ddot_patch_dp(*g_a, t_a, *ailo, *aihi, *ajlo, *ajhi, *g_b, t_b, *bilo, *bihi, *bjlo, *bjhi); } /* Routines from ga_trace.c */ double FATR ga_timer_() { return wnga_timer(); } double FATR nga_timer_() { return wnga_timer(); } Integer nga_register_type_(Integer *size) { return wnga_register_type((size_t)*size); } Integer nga_deregister_type_(Integer *type) { return wnga_deregister_type((int)*type); } void nga_get_field_(Integer *g_a, Integer *lo, Integer *hi, Integer *foff, Integer *fsize, void *buf, Integer *ld) { wnga_get_field(*g_a, lo, hi, *foff, *fsize, buf, ld); } void nga_nbget_field_(Integer *g_a, Integer *lo, Integer *hi, Integer *foff, Integer *fsize, void *buf, Integer *ld, Integer *nbhandle) { wnga_nbget_field(*g_a, lo, hi, *foff, *fsize, buf, ld, nbhandle); } void nga_nbput_field_(Integer *g_a, Integer *lo, Integer *hi, Integer *foff, Integer *fsize, void *buf, Integer *ld, Integer *nbhandle) { wnga_nbput_field(*g_a, lo, hi, *foff, *fsize, buf, ld, nbhandle); } void nga_put_field_(Integer *g_a, Integer *lo, Integer *hi, Integer *foff, Integer *fsize, void *buf, Integer *ld) { wnga_put_field(*g_a, lo, hi, *foff, *fsize, buf, ld); } void ga_version_(Integer *major, Integer *minor, Integer *patch) { wnga_version(major,minor,patch); } void nga_version_(Integer *major, Integer *minor, Integer *patch) { wnga_version(major,minor,patch); } Integer nga_sprs_array_create_(Integer *idim, Integer *jdim, Integer *type) { int ctype; if (*type == MT_F_INT) { if (sizeof(Integer) == 4) { ctype = C_INT; } else { ctype = C_LONG; } } else if (*type == MT_F_REAL) { ctype = C_FLOAT; } else if (*type == MT_F_DBL) { ctype = C_DBL; } else if (*type == MT_F_SCPL) { ctype = C_SCPL; } else if (*type == MT_F_DCPL) { ctype = C_DCPL; } return wnga_sprs_array_create(*idim,*jdim,ctype,sizeof(Integer)); } Integer nga_sprs_array_create_from_dense_(Integer *g_a) { return wnga_sprs_array_create_from_dense(*g_a,sizeof(Integer),0); } Integer nga_sprs_array_create_from_sparse_(Integer *s_a) { return wnga_sprs_array_create_from_sparse(*s_a,0); } void nga_sprs_array_add_element_(Integer *s_a, Integer *idx, Integer *jdx, void *val) { wnga_sprs_array_add_element(*s_a,*idx,*jdx,val); } logical nga_sprs_array_assemble_(Integer *s_a) { return wnga_sprs_array_assemble(*s_a); } void nga_sprs_array_row_distribution_(Integer *s_a, Integer *iproc, Integer *lo, Integer *hi) { wnga_sprs_array_row_distribution(*s_a, *iproc, lo, hi); } void nga_sprs_array_column_distribution_(Integer *s_a, Integer *iproc, Integer *lo, Integer *hi) { wnga_sprs_array_column_distribution(*s_a, *iproc, lo, hi); } void nga_sprs_array_access_col_block_(Integer *s_a, Integer *icol, AccessIndex *idx, AccessIndex *jdx, AccessIndex *vdx) { wnga_sprs_array_access_col_block_idx(*s_a, *icol, idx, jdx, vdx); } void nga_sprs_array_col_block_list_(Integer *s_a, Integer *idx, Integer *n) { /* This function assumes that idx has already been allocated by calling * program and that nn is the length of idx on input. On output, nn is * changed to the actual number of blocks found by the * wnga_sprs_array_col_block_list function */ Integer nn; Integer *blocks; wnga_sprs_array_col_block_list(*s_a, &blocks, &nn); if (*n < nn) { wnga_error("nga_sprs_array_col_block_list: allocated array is too small for" " number of blocks found: ",nn); } else { int i; for (i=0; i #ifdef __cplusplus extern "C" { #endif extern MPI_Comm GA_MPI_Comm(); extern MPI_Comm GA_MPI_Comm_pgroup(int pgroup); extern MPI_Comm GA_MPI_Comm_pgroup_default(); #ifdef __cplusplus } #endif #endif /* GA_MPI_H_ */ ga-5.9.2/global/src/ga-papi.h000066400000000000000000000641631500715745200156540ustar00rootroot00000000000000#ifndef PAPI_H_ #define PAPI_H_ #include #include "gacommon.h" #include "typesf2c.h" #include typedef intp AccessIndex; /* Routines from base.c */ extern void pnga_version(Integer *major, Integer *minor, Integer *patch); extern logical pnga_allocate(Integer g_a); extern logical pnga_deallocate(Integer g_a); extern logical pnga_compare_distr(Integer g_a, Integer g_b); extern logical pnga_create(Integer type, Integer ndim, Integer *dims, char* name, Integer *chunk, Integer *g_a); extern logical pnga_create_config(Integer type, Integer ndim, Integer *dims, char* name, Integer *chunk, Integer p_handle, Integer *g_a); extern logical pnga_create_ghosts(Integer type, Integer ndim, Integer *dims, Integer *width, char* name, Integer *chunk, Integer *g_a); extern logical pnga_create_ghosts_irreg(Integer type, Integer ndim, Integer *dims, Integer *width, char* name, Integer *map, Integer *block, Integer *g_a); extern logical pnga_create_ghosts_irreg_config(Integer type, Integer ndim, Integer *dims, Integer *width, char* name, Integer *map, Integer *block, Integer p_handle, Integer *g_a); extern logical pnga_create_ghosts_config(Integer type, Integer ndim, Integer *dims, Integer *width, char* name, Integer *chunk, Integer p_handle, Integer *g_a); extern logical pnga_create_irreg(Integer type, Integer ndim, Integer *dims, char* name, Integer *map, Integer *block, Integer *g_a); extern logical pnga_create_irreg_config(Integer type, Integer ndim, Integer *dims, char* name, Integer *map, Integer *block, Integer p_handle, Integer *g_a); extern Integer pnga_create_handle(); extern logical pnga_create_mutexes(Integer num); extern logical pnga_destroy(Integer g_a); extern logical pnga_destroy_mutexes(); extern void pnga_distribution(Integer g_a, Integer proc, Integer *lo, Integer *hi); extern logical pnga_duplicate(Integer g_a, Integer *g_b, char *array_name); extern void pnga_fill(Integer g_a, void* val); extern void pnga_free(void* ptr); extern void pnga_get_block_info(Integer g_a, Integer *num_blocks, Integer *block_dims); extern logical pnga_get_debug(); extern Integer pnga_get_dimension(Integer g_a); extern void pnga_get_distribution_type(Integer g_a, char *type); extern void pnga_get_proc_grid(Integer g_a, Integer *dims); extern void pnga_get_proc_index(Integer g_a, Integer iproc, Integer *index); extern logical pnga_has_ghosts(Integer g_a); extern void pnga_initialize(); extern int pnga_initialize_comm(MPI_Comm comm); extern int pnga_initialized(); extern void pnga_initialize_ltd(Integer limit); extern void pnga_inquire(Integer g_a, Integer *type, Integer *ndim, Integer *dims); extern void pnga_inquire_type(Integer g_a, Integer *type); extern Integer pnga_inquire_memory(); extern void pnga_inquire_name(Integer g_a, char **array_name); extern logical pnga_is_mirrored(Integer g_a); extern void pnga_list_nodeid(Integer *list, Integer nprocs); extern logical pnga_locate(Integer g_a, Integer *subscript, Integer *owner); extern Integer pnga_locate_num_blocks(Integer g_a, Integer *lo, Integer *hi); extern logical pnga_locate_nnodes(Integer g_a, Integer *lo, Integer *hi, Integer *np); extern logical pnga_locate_region(Integer g_a, Integer *lo, Integer *hi, Integer *map, Integer *proclist, Integer *np); extern void pnga_lock(Integer mutex); extern void* pnga_malloc(Integer nelem, int type, char *name); extern void pnga_mask_sync(Integer begin, Integer end); extern Integer pnga_memory_avail(); extern logical pnga_memory_limited(); extern void pnga_merge_distr_patch(Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi); extern void pnga_merge_mirrored(Integer g_a); extern void pnga_nblock(Integer g_a, Integer *nblock); extern Integer pnga_ndim(Integer g_a); extern Integer pnga_nnodes(); extern Integer pnga_nodeid(); extern logical pnga_overlay(Integer g_a, Integer g_p); extern Integer pnga_pgroup_absolute_id(Integer grp, Integer pid); extern Integer pnga_pgroup_create(Integer *list, Integer count); extern Integer pnga_pgroup_duplicate(Integer grp); extern Integer pnga_pgroup_self(); extern logical pnga_pgroup_destroy(Integer grp); extern Integer pnga_pgroup_get_default(); extern Integer pnga_pgroup_get_mirror(); extern Integer pnga_pgroup_get_world(); extern void pnga_pgroup_set_default(Integer grp); extern Integer pnga_pgroup_split(Integer grp, Integer grp_num); extern Integer pnga_pgroup_split_irreg(Integer grp, Integer mycolor); extern Integer pnga_pgroup_nnodes(Integer grp); extern Integer pnga_pgroup_nodeid(Integer grp); extern void pnga_proc_topology(Integer g_a, Integer proc, Integer* subscript); extern void pnga_randomize(Integer g_a, void* val); extern Integer pnga_get_pgroup(Integer g_a); extern Integer pnga_get_pgroup_size(Integer grp_id); extern void pnga_set_array_name(Integer g_a, char *array_name); extern void pnga_get_array_name(Integer g_a, char *array_name); extern void pnga_set_block_cyclic(Integer g_a, Integer *dims); extern void pnga_set_block_cyclic_proc_grid(Integer g_a, Integer *dims, Integer *proc_grid); extern void pnga_set_tiled_proc_grid(Integer g_a, Integer *dims, Integer *proc_grid); extern void pnga_set_tiled_irreg_proc_grid(Integer g_a, Integer *mapc, Integer *nblocks, Integer *proc_grid); extern void pnga_set_chunk(Integer g_a, Integer *chunk); extern void pnga_set_data(Integer g_a, Integer ndim, Integer *dims, Integer type); extern void pnga_set_debug(logical flag); extern void pnga_set_ghosts(Integer g_a, Integer *width); extern void pnga_set_irreg_distr(Integer g_a, Integer *map, Integer *block); extern void pnga_set_irreg_flag(Integer g_a, logical flag); extern void pnga_set_memory_limit(Integer mem_limit); extern void pnga_set_pgroup(Integer g_a, Integer p_handle); extern void pnga_set_restricted(Integer g_a, Integer *list, Integer size); extern void pnga_set_restricted_range(Integer g_a, Integer lo_proc, Integer hi_proc); extern void pnga_set_property(Integer g_a, char *property); extern void pnga_unset_property(Integer g_a); extern void pnga_set_memory_dev(Integer g_a, char *device); extern void pnga_terminate(); extern Integer pnga_total_blocks(Integer g_a); extern void pnga_unlock(Integer mutex); extern logical pnga_uses_ma(); extern logical pnga_uses_proc_grid(Integer g_a); extern logical pnga_uses_irreg_proc_grid(Integer g_a); extern void pnga_get_map_info(Integer g_a, Integer *num_blocks, Integer **map); extern logical pnga_valid_handle(Integer g_a); extern Integer pnga_verify_handle(Integer g_a); extern void pnga_check_handle(Integer g_a, char *string); /* Routines from onesided.c */ extern void pnga_acc(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, void *alpha); extern void pnga_access_idx(Integer g_a, Integer *lo, Integer *hi, AccessIndex *index, Integer *ld); extern void pnga_access_ptr(Integer g_a, Integer *lo, Integer *hi, void *ptr, Integer *ld); extern void pnga_access_block_idx(Integer g_a, Integer idx, AccessIndex* index, Integer *ld); extern void pnga_access_block_ptr(Integer g_a, Integer idx, void* ptr, Integer *ld); extern void pnga_access_block_grid_idx(Integer g_a, Integer* subscript, AccessIndex *index, Integer *ld); extern void pnga_access_block_grid_ptr(Integer g_a, Integer *index, void* ptr, Integer *ld); extern void pnga_access_block_segment_idx(Integer g_a, Integer proc, AccessIndex* index, Integer *len); extern void pnga_access_block_segment_ptr(Integer g_a, Integer proc, void* ptr, Integer *len); extern void pnga_alloc_gatscat_buf(Integer nelems); extern void pnga_fence(); extern void pnga_free_gatscat_buf(); extern void pnga_gather2d(Integer g_a, void *v, Integer *i, Integer *j, Integer nv); extern void pnga_gather(Integer g_a, void* v, void *subscript, Integer c_flag, Integer nv); extern void pnga_get(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld); extern void pnga_init_fence(); extern void pnga_nbacc(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, void *alpha, Integer *nbhndl); extern void pnga_nbget(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, Integer *nbhandle); extern void pnga_nbput(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, Integer *nbhandle); extern void pnga_nbput_notify(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, Integer g_b, Integer *ecoords, void *bufn, Integer *nbhandle); extern void pnga_nbwait_notify(Integer *nbhandle); extern Integer pnga_nbtest(Integer *nbhandle); extern void pnga_nbwait(Integer *nbhandle); extern void pnga_put(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld); extern void pnga_pgroup_sync(Integer grp_id); extern Integer pnga_read_inc(Integer g_a, Integer *subscript, Integer inc); extern void pnga_release(Integer g_a, Integer *lo, Integer *hi); extern void pnga_release_block(Integer g_a, Integer iblock); extern void pnga_release_block_grid(Integer g_a, Integer *index); extern void pnga_release_block_segment(Integer g_a, Integer iproc); extern void pnga_release_update(Integer g_a, Integer *lo, Integer *hi); extern void pnga_release_update_block(Integer g_a, Integer iblock); extern void pnga_release_update_block_grid(Integer g_a, Integer *index); extern void pnga_release_update_block_segment(Integer g_a, Integer iproc); extern void pnga_scatter2d(Integer g_a, void *v, Integer *i, Integer *j, Integer nv); extern void pnga_scatter(Integer g_a, void *v, void *subscript, Integer c_flag, Integer nv); extern void pnga_scatter_acc2d(Integer g_a, void *v, Integer *i, Integer *j, Integer nv, void *alpha); extern void pnga_scatter_acc(Integer g_a, void* v, void *subscript, Integer c_flag, Integer nv, void *alpha); extern void pnga_strided_acc(Integer g_a, Integer *lo, Integer *hi, Integer *skip, void *buf, Integer *ld, void *alpha); extern void pnga_strided_get(Integer g_a, Integer *lo, Integer *hi, Integer *skip, void *buf, Integer *ld); extern void pnga_strided_put(Integer g_a, Integer *lo, Integer *hi, Integer *skip, void *buf, Integer *ld); extern void pnga_sync(); extern DoublePrecision pnga_wtime(); extern DoublePrecision pnga_rand(Integer idum); /* Routines from datatypes.c */ extern Integer pnga_type_f2c(Integer type); extern Integer pnga_type_c2f(Integer type); /* Routines from collect.c */ extern void pnga_msg_brdcst(Integer type, void *buffer, Integer len, Integer root); extern void pnga_brdcst(Integer type, void *buf, Integer len, Integer originator); extern void pnga_pgroup_brdcst(Integer grp_id, Integer type, void *buf, Integer len, Integer originator); extern void pnga_msg_sync(); extern void pnga_msg_pgroup_sync(Integer grp_id); extern void pnga_pgroup_gop(Integer p_grp, Integer type, void *x, Integer n, char *op); extern void pnga_gop(Integer type, void *x, Integer n, char *op); /* Routines from elem_alg.c */ extern void pnga_abs_value_patch(Integer g_a, Integer *lo, Integer *hi); extern void pnga_recip_patch(Integer g_a, Integer *lo, Integer *hi); extern void pnga_add_constant_patch(Integer g_a, Integer *lo, Integer *hi, void *alpha); extern void pnga_abs_value(Integer g_a); extern void pnga_add_constant(Integer g_a, void *alpha); extern void pnga_recip(Integer g_a); extern void pnga_elem_multiply(Integer g_a, Integer g_b, Integer g_c); extern void pnga_elem_divide(Integer g_a, Integer g_b, Integer g_c); extern void pnga_elem_maximum(Integer g_a, Integer g_b, Integer g_c); extern void pnga_elem_minimum(Integer g_a, Integer g_b, Integer g_c); extern void pnga_elem_multiply_patch(Integer g_a,Integer *alo,Integer *ahi,Integer g_b,Integer *blo,Integer *bhi,Integer g_c,Integer *clo,Integer *chi); extern void pnga_elem_divide_patch(Integer g_a,Integer *alo,Integer *ahi,Integer g_b,Integer *blo,Integer *bhi,Integer g_c,Integer *clo,Integer *chi); extern void pnga_elem_maximum_patch(Integer g_a,Integer *alo,Integer *ahi,Integer g_b,Integer *blo,Integer *bhi,Integer g_c,Integer *clo,Integer *chi); extern void pnga_elem_minimum_patch(Integer g_a,Integer *alo,Integer *ahi,Integer g_b,Integer *blo,Integer *bhi,Integer g_c,Integer *clo,Integer *chi); extern void pnga_elem_step_divide_patch(Integer g_a,Integer *alo,Integer *ahi, Integer g_b,Integer *blo,Integer *bhi,Integer g_c, Integer *clo,Integer *chi); extern void pnga_elem_stepb_divide_patch(Integer g_a,Integer *alo,Integer *ahi, Integer g_b,Integer *blo,Integer *bhi,Integer g_c, Integer *clo,Integer *chi); extern void pnga_step_mask_patch(Integer g_a,Integer *alo,Integer *ahi, Integer g_b,Integer *blo,Integer *bhi,Integer g_c, Integer *clo,Integer *chi); extern void pnga_step_bound_info_patch(Integer g_xx, Integer *xxlo, Integer *xxhi, Integer g_vv, Integer *vvlo, Integer *vvhi, Integer g_xxll, Integer *xxlllo, Integer *xxllhi, Integer g_xxuu, Integer *xxuulo, Integer *xxuuhi, void *boundmin, void* wolfemin, void *boundmax); extern void pnga_step_max_patch(Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi, void *result); extern void pnga_step_max(Integer g_a, Integer g_b, void *retval); extern void pnga_step_bound_info(Integer g_xx, Integer g_vv, Integer g_xxll, Integer g_xxuu, void *boundmin, void *wolfemin, void *boundmax); /* Routines from ga_solve_seq.c */ extern void pnga_lu_solve_seq(char *trans, Integer g_a, Integer g_b); /* Routines from global.util.c */ extern void pnga_print_stats(); extern void pnga_error(char *string, Integer icode); extern Integer pnga_cluster_nodeid(); extern Integer pnga_cluster_nprocs(Integer node); extern Integer pnga_cluster_procid(Integer node, Integer loc_proc_id); extern Integer pnga_cluster_nnodes(); extern Integer pnga_cluster_proc_nodeid(Integer proc); extern void pnga_print_file(FILE *file, Integer g_a); extern void pnga_print(Integer g_a); extern void pnga_print_patch_file2d(FILE *file, Integer g_a, Integer ilo, Integer ihi, Integer jlo, Integer jhi, Integer pretty); extern void pnga_print_patch2d(Integer g_a, Integer ilo, Integer ihi, Integer jlo, Integer jhi, Integer pretty); extern void pnga_print_patch_file(FILE *file, Integer g_a, Integer *lo, Integer *hi, Integer pretty); extern void pnga_print_patch(Integer g_a, Integer *lo, Integer *hi, Integer pretty); extern void pnga_print_distribution(int fstyle, Integer g_a); extern void pnga_summarize(Integer verbose); /* Routines from ghosts.c */ extern void pnga_access_ghost_ptr(Integer g_a, Integer dims[], void* ptr, Integer ld[]); extern void pnga_access_ghost_element(Integer g_a, AccessIndex* index, Integer subscript[], Integer ld[]); extern void pnga_access_ghost_element_ptr(Integer g_a, void *ptr, Integer subscript[], Integer ld[]); extern void pnga_access_ghosts(Integer g_a, Integer dims[], AccessIndex* index, Integer ld[]); extern void pnga_release_ghost_element(Integer g_a, Integer subscript[]); extern void pnga_release_update_ghost_element(Integer g_a, Integer subscript[]); extern void pnga_release_ghosts(Integer g_a); extern void pnga_release_update_ghosts(Integer g_a); extern void pnga_get_ghost_block(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld); extern void pnga_update1_ghosts(Integer g_a); extern logical pnga_update2_ghosts(Integer g_a); extern logical pnga_update3_ghosts(Integer g_a); extern logical pnga_set_update4_info(Integer g_a); extern logical pnga_update4_ghosts(Integer g_a); extern logical pnga_update44_ghosts(Integer g_a); extern logical pnga_update55_ghosts(Integer g_a); extern logical pnga_update_ghost_dir(Integer g_a, Integer pdim, Integer pdir, logical pflag); extern logical pnga_update5_ghosts(Integer g_a); extern logical pnga_set_update5_info(Integer g_a); extern void pnga_update_ghosts(Integer g_a); extern void pnga_update_ghosts_nb(Integer g_a, Integer *nbhandle); extern logical pnga_update6_ghosts(Integer g_a); extern logical pnga_update7_ghosts(Integer g_a); extern void pnga_ghost_barrier(); extern void pnga_nbget_ghost_dir(Integer g_a, Integer *mask, Integer *nbhandle); extern logical pnga_set_ghost_info(Integer g_a); extern void pnga_set_ghost_corner_flag(Integer g_a, logical flag); /* Routines from global.nalg.c */ extern void pnga_zero(Integer g_a); extern void pnga_copy(Integer g_a, Integer g_b); extern void pnga_dot(int type, Integer g_a, Integer g_b, void *value); extern void pnga_scale(Integer g_a, void* alpha); extern void pnga_add(void *alpha, Integer g_a, void* beta, Integer g_b, Integer g_c); extern void pnga_transpose(Integer g_a, Integer g_b); /* Routines from global.npatch.c */ extern void pnga_copy_patch(char *trans, Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi); extern void pnga_zero_patch(Integer g_a, Integer *lo, Integer *hi); extern logical pnga_patch_intersect(Integer *lo, Integer *hi, Integer *lop, Integer *hip, Integer ndim); extern logical pnga_comp_patch(Integer andim, Integer *alo, Integer *ahi, Integer bndim, Integer *blo, Integer *bhi); extern void pnga_dot_patch(Integer g_a, char *t_a, Integer *alo, Integer *ahi, Integer g_b, char *t_b, Integer *blo, Integer *bhi, void *retval); extern void pnga_fill_patch(Integer g_a, Integer *lo, Integer *hi, void* val); extern void pnga_scale_patch(Integer g_a, Integer *lo, Integer *hi, void *alpha); extern void pnga_add_patch(void *alpha, Integer g_a, Integer *alo, Integer *ahi, void *beta, Integer g_b, Integer *blo, Integer *bhi, Integer g_c, Integer *clo, Integer *chi); /* Routines from select.c */ extern void pnga_select_elem(Integer g_a, char* op, void* val, Integer *subscript); /* Routines from ga_malloc.c */ extern Integer pnga_memory_avail_type(Integer datatype); /* Routines from sparse.c */ extern void pnga_patch_enum(Integer g_a, Integer lo, Integer hi, void* start, void* stride); extern void pnga_scan_copy(Integer g_a, Integer g_b, Integer g_sbit, Integer lo, Integer hi); extern void pnga_scan_add(Integer g_a, Integer g_b, Integer g_sbit, Integer lo, Integer hi, Integer excl); extern void pnga_pack(Integer g_a, Integer g_b, Integer g_sbit, Integer lo, Integer hi, Integer* icount); extern void pnga_unpack(Integer g_a, Integer g_b, Integer g_sbit, Integer lo, Integer hi, Integer* icount); extern logical pnga_create_bin_range(Integer g_bin, Integer g_cnt, Integer g_off, Integer *g_range); extern void pnga_bin_sorter(Integer g_bin, Integer g_cnt, Integer g_off); extern void pnga_bin_index(Integer g_bin, Integer g_cnt, Integer g_off, Integer *values, Integer *subs, Integer n, Integer sortit); /* Routines from matrix.c */ extern void pnga_median_patch(Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi, Integer g_c, Integer *clo, Integer *chi, Integer g_m, Integer *mlo, Integer *mhi); extern void pnga_median(Integer g_a, Integer g_b, Integer g_c, Integer g_m); extern void pnga_norm_infinity(Integer g_a, double *nm); extern void pnga_norm1(Integer g_a, double *nm); extern void pnga_get_diag(Integer g_a, Integer g_v); extern void pnga_add_diagonal(Integer g_a, Integer g_v); extern void pnga_set_diagonal(Integer g_a, Integer g_v); extern void pnga_shift_diagonal(Integer g_a, void *c); extern void pnga_zero_diagonal(Integer g_a); extern void pnga_scale_rows(Integer g_a, Integer g_v); extern void pnga_scale_cols(Integer g_a, Integer g_v); /* Routines from ga_symmetr.c */ extern void pnga_symmetrize(Integer g_a); /* Routines from global.periodic.c */ extern void pnga_periodic(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, void *alpha, Integer op_code); /* Routines from matmul.c */ extern void pnga_matmul(char *transa, char *transb, void *alpha, void *beta, Integer g_a, Integer ailo, Integer aihi, Integer ajlo, Integer ajhi, Integer g_b, Integer bilo, Integer bihi, Integer bjlo, Integer bjhi, Integer g_c, Integer cilo, Integer cihi, Integer cjlo, Integer cjhi); extern void pnga_matmul_mirrored(char *transa, char *transb, void *alpha, void *beta, Integer g_a, Integer ailo, Integer aihi, Integer ajlo, Integer ajhi, Integer g_b, Integer bilo, Integer bihi, Integer bjlo, Integer bjhi, Integer g_c, Integer cilo, Integer cihi, Integer cjlo, Integer cjhi); extern void pnga_matmul_patch(char *transa, char *transb, void *alpha, void *beta, Integer g_a, Integer alo[], Integer ahi[], Integer g_b, Integer blo[], Integer bhi[], Integer g_c, Integer clo[], Integer chi[]); extern void pnga_matmul_patch_alt(char *transa, char *transb, void *alpha, void *beta, Integer g_a, Integer alo[], Integer ahi[], Integer g_b, Integer blo[], Integer bhi[], Integer g_c, Integer clo[], Integer chi[]); extern void pnga_matmul_basic(char *transa, char *transb, void *alpha, void *beta, Integer g_a, Integer alo[], Integer ahi[], Integer g_b, Integer blo[], Integer bhi[], Integer g_c, Integer clo[], Integer chi[]); extern void pnga_matmul_basic(char *transa, char *transb, void *alpha, void *beta, Integer g_a, Integer alo[], Integer ahi[], Integer g_b, Integer blo[], Integer bhi[], Integer g_c, Integer clo[], Integer chi[]); /* Routines from ga_diag_seqc.c */ extern void pnga_diag_seq(Integer g_a, Integer g_s, Integer g_v, DoublePrecision *eval); extern void pnga_diag_std_seq(Integer g_a, Integer g_v, DoublePrecision *eval); /* Routines from peigstubs.c */ extern void pnga_diag(Integer g_a, Integer g_s, Integer g_v, DoublePrecision *eval); extern void pnga_diag_std(Integer g_a, Integer g_v, DoublePrecision *eval); extern void pnga_diag_reuse(Integer reuse, Integer g_a, Integer g_s, Integer g_v, DoublePrecision *eval); /* Routines from sclstubs.c */ extern void pnga_lu_solve_alt(Integer tran, Integer g_a, Integer g_b); extern void pnga_lu_solve(char *tran, Integer g_a, Integer g_b); extern Integer pnga_llt_solve(Integer g_a, Integer g_b); extern Integer pnga_solve(Integer g_a, Integer g_b); extern Integer pnga_spd_invert(Integer g_a); /* Routines from DP.c */ extern void pnga_copy_patch_dp(char *t_a, Integer g_a, Integer ailo, Integer aihi, Integer ajlo, Integer ajhi, Integer g_b, Integer bilo, Integer bihi, Integer bjlo, Integer bjhi); extern DoublePrecision pnga_ddot_patch_dp(Integer g_a, char *t_a, Integer ailo, Integer aihi, Integer ajlo, Integer ajhi, Integer g_b, char *t_b, Integer bilo, Integer bihi, Integer bjlo, Integer bjhi); /* Routines from ga_trace.c */ extern double pnga_timer(); /*Routines for types from base.c*/ extern int pnga_register_type(size_t size); extern int pnga_deregister_type(int type); /*Routines for field-wise GA operations*/ extern void pnga_get_field(Integer g_a, Integer *lo, Integer *hi, Integer foff, Integer fsize, void *buf, Integer *ld); extern void pnga_nbget_field(Integer g_a, Integer *lo, Integer *hi, Integer foff, Integer fsize,void *buf, Integer *ld, Integer *nbhandle); extern void pnga_nbput_field(Integer g_a, Integer *lo, Integer *hi, Integer foff, Integer fsize,void *buf, Integer *ld, Integer *nbhandle); extern void pnga_put_field(Integer g_a, Integer *lo, Integer *hi, Integer foff, Integer fsize,void *buf, Integer *ld); /* Routines for sparse matrices */ extern Integer pnga_sprs_array_create(Integer idim, Integer jdim, Integer type, Integer trans); extern Integer pnga_sprs_array_create_from_dense(Integer g_a, Integer idxsize, Integer flag); extern Integer pnga_sprs_array_create_from_sparse(Integer s_a, Integer flag); extern void pnga_sprs_array_add_element(Integer s_a, Integer idx, Integer jdx, void *val); extern logical pnga_sprs_array_assemble(Integer s_a); extern void pnga_sprs_array_row_distribution(Integer s_a, Integer iproc, Integer *lo, Integer *hi); extern void pnga_sprs_array_column_distribution(Integer s_a, Integer iproc, Integer *lo, Integer *hi); extern void pnga_sprs_array_access_col_block(Integer s_a, Integer icol, void *idx, void *jdx, void *val); extern int pnga_sprs_array_access_col_block_idx(Integer s_a, Integer icol, AccessIndex *idx, AccessIndex *jdx, AccessIndex *vdx); extern void pnga_sprs_array_col_block_list(Integer s_a, Integer **idx, Integer *n); extern Integer pnga_sprs_array_duplicate(Integer s_a); extern Integer pnga_sprs_array_matmat_multiply(Integer s_a, Integer s_b); extern Integer pnga_sprs_array_sprsdns_multiply(Integer s_a, Integer g_b, Integer trans); extern Integer pnga_sprs_array_dnssprs_multiply(Integer g_a, Integer s_b, Integer trans); extern Integer pnga_sprs_array_count_sketch(Integer s_a, Integer size_k, Integer *g_k, Integer *g_w, Integer trans); extern void pnga_sprs_array_matvec_multiply(Integer s_a, Integer g_a, Integer g_v); extern logical pnga_sprs_array_destroy(Integer s_a); extern void pnga_sprs_array_export(Integer s_a, const char* file); extern void pnga_sprs_array_get_diag(Integer s_a, Integer *g_d); extern void pnga_sprs_array_diag_right_multiply(Integer s_a, Integer g_d); extern void pnga_sprs_array_diag_left_multiply(Integer s_a, Integer g_d); extern void pnga_sprs_array_scale(Integer s_a, void *scale); extern void pnga_sprs_array_shift_diag(Integer s_a, void *shift); extern logical pnga_sprs_array_get_block(Integer s_a, Integer irow, Integer icol, void **idx, void **jdx, void **data, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi); extern Integer pnga_sprs_array_get_column(Integer g_v, Integer irow); #endif /* PAPI_H_ */ ga-5.9.2/global/src/ga.h000066400000000000000000001125341500715745200147210ustar00rootroot00000000000000/* $Id: ga.h,v 1.60.2.8 2007-10-10 21:11:04 manoj Exp $ */ #ifndef _GA_H_ #define _GA_H_ #include #include #include "gacommon.h" #include "typesf2c.h" #include #ifdef __cplusplus extern "C" { #endif typedef Integer ga_nbhdl_t; extern void GA_Abs_value(int g_a); extern void GA_Abs_value_patch(int g_a, int *lo, int *hi); extern void GA_Add_constant(int g_a, void* alpha); extern void GA_Add_constant_patch(int g,int *lo,int *hi,void *alpha); extern void GA_Add_diagonal(int g_a, int g_v); extern void GA_Add(void *alpha, int g_a, void* beta, int g_b, int g_c); extern int GA_Allocate(int g_a); extern int GA_Assemble_duplicate(int g_a, char *name, void *ptr); extern void GA_Brdcst(void *buf, int lenbuf, int root); extern SingleComplex GA_Cdot(int g_a, int g_b); extern void GA_Cgop(SingleComplex x[], int n, char *op); extern void GA_Cgemm(char ta, char tb, int m, int n, int k, SingleComplex alpha, int g_a, int g_b, SingleComplex beta, int g_c ); extern void GA_Check_handle(int g_a, char *string); extern int GA_Cluster_nnodes(void); extern int GA_Cluster_nodeid(void); extern int GA_Cluster_nprocs(int x); extern int GA_Cluster_procid(int x, int y); extern int GA_Cluster_proc_nodeid(int proc); extern int GA_Compare_distr(int g_a, int g_b); extern void GA_Copy(int g_a, int g_b); extern int GA_Create_handle(void); extern int GA_Create_mutexes(int number); extern int GA_Deallocate(int g_a); extern double GA_Ddot(int g_a, int g_b); extern void GA_Destroy(int g_a); extern int GA_Destroy_mutexes(void); extern void GA_Dgemm(char ta, char tb, int m, int n, int k, double alpha, int g_a, int g_b, double beta, int g_c ); extern void GA_Dgop(double x[], int n, char *op); extern void GA_Diag(int g_a, int g_s, int g_v, void *eval); extern void GA_Diag_reuse(int reuse, int g_a, int g_s, int g_v, void *eval); extern void GA_Diag_seq(int g_a, int g_s, int g_v, void *eval); extern void GA_Diag_std(int g_a, int g_v, void *eval); extern void GA_Diag_std_seq(int g_a, int g_v, void *eval); extern int GA_Duplicate(int g_a, char* array_name); extern void GA_Elem_divide(int g_a, int g_b, int g_c); extern void GA_Elem_divide_patch(int g_a,int *alo,int *ahi, int g_b,int *blo,int *bhi,int g_c,int *clo,int *chi); extern void GA_Elem_maximum(int g_a, int g_b, int g_c); extern void GA_Elem_maximum_patch(int g_a,int *alo,int *ahi, int g_b,int *blo,int *bhi,int g_c,int *clo,int *chi); extern void GA_Elem_minimum(int g_a, int g_b, int g_c); extern void GA_Elem_minimum_patch(int g_a,int *alo,int *ahi, int g_b,int *blo,int *bhi,int g_c,int *clo,int *chi); extern void GA_Elem_multiply(int g_a, int g_b, int g_c); extern void GA_Elem_multiply_patch(int g_a,int *alo,int *ahi, int g_b,int *blo,int *bhi,int g_c,int *clo,int *chi); extern void GA_Error(char *str, int code); extern float GA_Fdot(int g_a, int g_b); extern void GA_Fence(void); extern void GA_Fgop(float x[], int n, char *op); extern void GA_Fill(int g_a, void *value); extern void GA_Freemem(void* ptr); extern void GA_Get_block_info(int g_a, int num_blocks[], int block_dims[]); extern int GA_Get_debug(void); extern void GA_Get_diag(int g_a, int g_v); extern int GA_Get_dimension(int g_a); extern void* GA_Getmem(int type, int nelem, int grp_id); extern int GA_Get_pgroup(int g_a); extern int GA_Get_pgroup_size(int grp_id); extern void GA_Get_proc_grid(int g_a, int dims[]); extern void GA_Get_proc_index(int g_a, int iproc, int subscript[]); extern void GA_Gop(int type, void *x, int n, char *op); extern int GA_Has_ghosts(int g_a); extern int GA_Idot(int g_a, int g_b); extern void GA_Igop(int x[], int n, char *op); extern void GA_Init_fence(void); extern void GA_Initialize_args(int *argc, char ***argv); extern void GA_Initialize_ltd(size_t limit); extern void GA_Initialize(void); extern int GA_Initialize_comm(MPI_Comm comm); extern int GA_Initialized(void); extern size_t GA_Inquire_memory(void); extern char* GA_Inquire_name(int g_a); extern int GA_Is_mirrored(int g_a); extern void GA_List_nodeid(int *list, int nprocs); extern long GA_Ldot(int g_a, int g_b); extern void GA_Lgop(long x[], int n, char *op); extern long long GA_Lldot(int g_a, int g_b); extern void GA_Llgop(long long x[], int n, char *op); extern int GA_Llt_solve(int g_a, int g_b); extern void GA_Lock(int mutex); extern void GA_Lu_solve(char tran, int g_a, int g_b); extern void GA_Mask_sync(int first, int last); extern void GA_Matmul_patch(char transa, char transb, void* alpha, void *beta, int g_a, int ailo, int aihi, int ajlo, int ajhi, int g_b, int bilo, int bihi, int bjlo, int bjhi, int g_c, int cilo, int cihi, int cjlo, int cjhi); extern void GA_Median(int g_a, int g_b, int g_c, int g_m); extern void GA_Median_patch(int g_a, int *alo, int *ahi, int g_b, int *blo, int *bhi, int g_c, int *clo, int *chi, int g_m, int *mlo, int *mhi); extern size_t GA_Memory_avail(void); extern int GA_Memory_limited(void); extern void GA_Merge_mirrored(int g_a); extern void GA_Nblock(int g_a, int *nblock); extern int GA_Ndim(int g_a); extern int GA_Nnodes(void); extern int GA_Nodeid(void); extern void GA_Norm1(int g_a, double *nm); extern void GA_Norm_infinity(int g_a, double *nm); extern int GA_Overlay(int g_a, int g_p); extern int GA_Pgroup_absolute_id(int pgroup, int pid); extern void GA_Pgroup_brdcst(int grp, void *buf, int lenbuf, int root); extern void GA_Pgroup_cgop(int grp, SingleComplex x[], int n, char *op); extern int GA_Pgroup_create(int *list, int count); extern int GA_Pgroup_destroy(int grp); extern void GA_Pgroup_dgop(int grp, double x[], int n, char *op); extern int GA_Pgroup_duplicate(int grp); extern void GA_Pgroup_fgop(int grp, float x[], int n, char *op); extern int GA_Pgroup_get_default(void); extern int GA_Pgroup_get_mirror(void); extern int GA_Pgroup_get_world(void); extern void GA_Pgroup_igop(int grp, int x[], int n, char *op); extern void GA_Pgroup_lgop(int grp, long x[], int n, char *op); extern void GA_Pgroup_llgop(int grp, long long x[], int n, char *op); extern int GA_Pgroup_nnodes(int grp_id); extern int GA_Pgroup_nodeid(int grp_id); extern int GA_Pgroup_self(); extern void GA_Pgroup_set_default(int p_handle); extern int GA_Pgroup_split(int grp_id, int num_group); extern int GA_Pgroup_split_irreg(int grp_id, int color); extern void GA_Pgroup_sync(int grp_id); extern void GA_Pgroup_zgop(int grp, DoubleComplex x[], int n, char *op); extern void GA_Print_distribution(int g_a); extern void GA_Print_file(FILE *file, int g_a); extern void GA_Print(int g_a); extern void GA_Print_patch(int g_a,int ilo,int ihi,int jlo,int jhi,int pretty); extern void GA_Print_stats(void); extern double GA_Rand(int idum); extern void GA_Randomize(int g_a, void *value); extern void GA_Recip(int g_a); extern void GA_Recip_patch(int g_a,int *lo, int *hi); extern void GA_Register_stack_memory(void * (*ext_alloc)(size_t, int, char *), void (*ext_free)(void *)); extern void GA_Scale_cols(int g_a, int g_v); extern void GA_Scale(int g_a, void *value); extern void GA_Scale_rows(int g_a, int g_v); extern void GA_Scan_add(int g_a, int g_b, int g_sbit, int lo, int hi, int excl); extern void GA_Scan_copy(int g_a, int g_b, int g_sbit, int lo, int hi); extern void GA_Set_array_name(int g_a, char *name); extern void GA_Set_block_cyclic(int g_a, int dims[]); extern void GA_Set_block_cyclic_proc_grid(int g_a, int block[], int proc_grid[]); extern void GA_Set_tiled_proc_grid(int g_a, int block[], int proc_grid[]); extern void GA_Set_tiled_irreg_proc_grid(int g_a, int mapc[], int nblocks[], int proc_grid[]); extern void GA_Set_chunk(int g_a, int chunk[]); extern void GA_Set_data(int g_a, int ndim, int dims[], int type); extern void GA_Set_debug(int flag); extern void GA_Set_diagonal(int g_a, int g_v); extern void GA_Set_ghost_corner_flag(int g_a, int flag); extern void GA_Set_ghosts(int g_a, int width[]); extern void GA_Set_irreg_distr(int g_a, int map[], int block[]); extern void GA_Set_irreg_flag(int g_a, int flag); extern void GA_Set_memory_limit(size_t limit); extern void GA_Set_memory_dev(int g_a, char *device); extern void GA_Set_pgroup(int g_a, int p_handle); extern void GA_Set_restricted(int g_a, int list[], int size); extern void GA_Set_restricted_range(int g_a, int lo_proc, int hi_proc); extern void GA_Set_property(int g_a, char *property); extern void GA_Unset_property(int g_a); extern void GA_Sgemm(char ta, char tb, int m, int n, int k, float alpha, int g_a, int g_b, float beta, int g_c ); extern void GA_Shift_diagonal(int g_a, void *c); extern int GA_Solve(int g_a, int g_b); extern int GA_Spd_invert(int g_a); extern void GA_Step_bound_info(int g_xx, int g_vv, int g_xxll, int g_xxuu, void *boundmin, void *wolfemin, void *boundmax); extern void GA_Step_bound_info_patch(int g_xx, int *xxlo, int *xxhi, int g_vv, int *vvlo, int *vvhi, int g_xxll, int *xxlllo, int *xxllhi, int g_xxuu, int *xxuulo, int *xxuuhi, void *boundmin, void *wolfemin, void *boundmax); extern void GA_Step_max(int g_a, int g_b, void *step); extern void GA_Step_max_patch(int g_a, int *alo, int *ahi, int g_b, int *blo, int *bhi, void *step); extern void GA_Summarize(int verbose); extern void GA_Symmetrize(int g_a); extern void GA_Sync(void); extern void GA_Terminate(void); extern int GA_Total_blocks(int g_a); extern void GA_Transpose(int g_a, int g_b); extern void GA_Unlock(int mutex); extern void GA_Update_ghosts(int g_a); extern int GA_Uses_fapi(void); extern int GA_Uses_ma(void); extern int GA_Uses_proc_grid(int g_a); extern int GA_Valid_handle(int g_a); extern int GA_Verify_handle(int g_a); extern double GA_Wtime(void); extern DoubleComplex GA_Zdot(int g_a, int g_b); extern void GA_Zgop(DoubleComplex x[], int n, char *op); extern void GA_Zero_diagonal(int g_a); extern void GA_Zero(int g_a); extern void GA_Zgemm(char ta, char tb, int m, int n, int k, DoubleComplex alpha, int g_a, int g_b, DoubleComplex beta, int g_c ); extern void NGA_Access_block_grid(int g_a, int index[], void *ptr, int ld[]); extern void NGA_Access_block(int g_a, int idx, void *ptr, int ld[]); extern void NGA_Access_block_segment(int g_a, int proc, void *ptr, int *len); extern void NGA_Access_ghost_element(int g_a, void *ptr, int subscript[], int ld[]); extern void NGA_Access_ghosts(int g_a, int dims[], void *ptr, int ld[]); extern void NGA_Access(int g_a, int lo[], int hi[], void *ptr, int ld[]); extern void NGA_Acc(int g_a, int lo[], int hi[],void* buf,int ld[],void* alpha); extern void NGA_Add_patch(void * alpha, int g_a, int alo[], int ahi[], void * beta, int g_b, int blo[], int bhi[], int g_c, int clo[], int chi[]); extern int NGA_Allocate(int g_a); extern void NGA_Alloc_gatscat_buf(int nelems); extern SingleComplex NGA_Cdot_patch(int g_a, char t_a, int alo[], int ahi[], int g_b, char t_b, int blo[], int bhi[]); extern int NGA_Compare_distr(int g_a, int g_b); extern void NGA_Copy_patch(char trans, int g_a, int alo[], int ahi[], int g_b, int blo[], int bhi[]); extern int NGA_Create_config(int type,int ndim,int dims[], char *name, int chunk[], int p_handle); extern int NGA_Create_ghosts_config(int type,int ndim,int dims[], int width[], char *name, int chunk[], int p_handle); extern int NGA_Create_ghosts(int type,int ndim,int dims[], int width[], char *name, int chunk[]); extern int NGA_Create_ghosts_irreg_config(int type,int ndim,int dims[], int width[], char *name, int nblock[], int map[], int p_handle); extern int NGA_Create_ghosts_irreg(int type,int ndim,int dims[], int width[], char *name, int nblock[], int map[]); extern int NGA_Create(int type,int ndim,int dims[], char *name, int chunk[]); extern int NGA_Create(int type,int ndim,int dims[], char *name, int chunk[]); extern int NGA_Create_irreg_config(int type,int ndim,int dims[],char *name, int block[], int map[], int p_handle); extern int NGA_Create_irreg(int type,int ndim,int dims[],char *name, int block[], int map[]); extern int NGA_Create_handle(void); extern int NGA_Deallocate(int g_a); extern int NGA_Deregister_type(int type); extern void NGA_Destroy(int g_a); extern int NGA_Destroy_mutexes(void); extern double NGA_Ddot_patch(int g_a, char t_a, int alo[], int ahi[], int g_b, char t_b, int blo[], int bhi[]); extern void NGA_Dgop(double x[], int n, char *op); extern void NGA_Distribution(int g_a, int iproc, int lo[], int hi[]); extern int NGA_Duplicate(int g_a, char* array_name); extern void NGA_Error(char *str, int code); extern float NGA_Fdot_patch(int g_a, char t_a, int alo[], int ahi[], int g_b, char t_b, int blo[], int bhi[]); extern void NGA_Fence(void); extern void NGA_Free_gatscat_buf(); extern void NGA_Fill(int g_a, void *value); extern void NGA_Fill_patch(int g_a, int lo[], int hi[], void *val); extern void NGA_Gather(int g_a, void *v, int* subsArray[], int n); extern void NGA_Gather_flat(int g_a, void *v, int subsArray[], int n); extern void NGA_Get(int g_a, int lo[], int hi[], void* buf, int ld[]); extern void NGA_Get_block_info(int g_a, int num_blocks[], int block_dims[]); extern int NGA_Get_debug(void); extern int NGA_Get_dimension(int g_a); extern void NGA_Get_field(int g_a, int *lo, int *hi, int foff, int fsize, void *buf, int *ld); extern void NGA_Get_ghost_block(int g_a, int lo[], int hi[], void *buf, int ld[]); extern int NGA_Get_pgroup(int g_a); extern int NGA_Get_pgroup_size(int grp_id); extern void NGA_Get_proc_grid(int g_a, int dims[]); extern void NGA_Get_proc_index(int g_a, int iproc, int subscript[]); extern int NGA_Has_ghosts(int g_a); extern int NGA_Idot_patch(int g_a, char t_a, int alo[], int ahi[], int g_b, char t_b, int blo[], int bhi[]); extern void NGA_Igop(int x[], int n, char *op); extern void NGA_Init_fence(void); extern void NGA_Initialize(void); extern int NGA_Initialize_comm(MPI_Comm comm); extern int NGA_Initialized(void); extern void NGA_Initialize_ltd(size_t limit); extern void NGA_Inquire(int g_a, int *type, int *ndim, int dims[]); extern size_t NGA_Inquire_memory(void); extern char* NGA_Inquire_name(int g_a); extern int NGA_Is_mirrored(int g_a); extern void NGA_List_nodeid(int *list, int nprocs); extern long NGA_Ldot_patch(int g_a, char t_a, int alo[], int ahi[], int g_b, char t_b, int blo[], int bhi[]); extern long long NGA_Lldot_patch(int g_a, char t_a, int alo[], int ahi[], int g_b, char t_b, int blo[], int bhi[]); extern int NGA_Locate(int g_a, int subscript[]); extern int NGA_Locate_num_blocks(int g_a, int lo[], int hi[]); extern int NGA_Locate_nnodes(int g_a, int lo[], int hi[]); extern int NGA_Locate_region(int g_a,int lo[],int hi[],int map[],int procs[]); extern void NGA_Lock(int mutex); extern void NGA_Mask_sync(int first, int last); extern void NGA_Matmul_patch(char transa, char transb, void* alpha, void *beta, int g_a, int alo[], int ahi[], int g_b, int blo[], int bhi[], int g_c, int clo[], int chi[]) ; extern size_t NGA_Memory_avail(void); extern int NGA_Memory_limited(void); extern void NGA_Merge_distr_patch(int g_a, int alo[], int ahi[], int g_b, int blo[], int bhi[]); extern void NGA_Merge_mirrored(int g_a); extern void NGA_Nblock(int g_a, int *nblock); extern void NGA_NbAcc(int g_a,int lo[], int hi[],void* buf,int ld[],void* alpha, ga_nbhdl_t* nbhandle); extern void NGA_NbGet_ghost_dir(int g_a, int mask[], ga_nbhdl_t* handle); extern void NGA_NbGet(int g_a, int lo[], int hi[], void* buf, int ld[], ga_nbhdl_t* nbhandle); extern void NGA_Nbget_field(int g_a, int *lo, int *hi, int foff, int fsize,void *buf, int *ld, ga_nbhdl_t *nbhandle); extern void NGA_Nbput_field(int g_a, int *lo, int *hi, int foff, int fsize, void *buf, int *ld, ga_nbhdl_t *nbhandle); extern void NGA_NbPut(int g_a, int lo[], int hi[], void* buf, int ld[], ga_nbhdl_t* nbhandle); extern int NGA_NbTest(ga_nbhdl_t* nbhandle); extern void NGA_NbWait(ga_nbhdl_t* nbhandle); extern int NGA_Ndim(int g_a); extern int NGA_Nnodes(void); extern int NGA_Nodeid(void); extern void NGA_Periodic_acc(int g_a, int lo[], int hi[],void* buf,int ld[],void* alpha); extern void NGA_Periodic_get(int g_a, int lo[], int hi[], void* buf, int ld[]); extern void NGA_Periodic_put(int g_a, int lo[], int hi[], void* buf, int ld[]); extern int NGA_Overlay(int g_a, int g_p); extern int NGA_Pgroup_absolute_id(int pgroup, int pid); extern int NGA_Pgroup_create(int *list, int count); extern int NGA_Pgroup_destroy(int grp); extern void NGA_Pgroup_dgop(int grp, double x[], int n, char *op); extern int NGA_Pgroup_duplicate(int grp); extern void NGA_Pgroup_fgop(int grp, float x[], int n, char *op); extern int NGA_Pgroup_get_default(void); extern int NGA_Pgroup_get_mirror(void); extern int NGA_Pgroup_get_world(void); extern void NGA_Pgroup_igop(int grp, int x[], int n, char *op); extern void NGA_Pgroup_lgop(int grp, long x[], int n, char *op); extern void NGA_Pgroup_llgop(int grp, long long x[], int n, char *op); extern int NGA_Pgroup_nnodes(int grp_id); extern int NGA_Pgroup_nodeid(int grp_id); extern int NGA_Pgroup_self(); extern void NGA_Pgroup_set_default(int p_handle); extern int NGA_Pgroup_split(int grp_id, int num_group); extern int NGA_Pgroup_split_irreg(int grp_id, int color); extern void NGA_Pgroup_sync(int grp_id); extern void NGA_Pgroup_zgop(int grp, DoubleComplex x[], int n, char *op); extern void NGA_Print_patch(int g_a, int lo[], int hi[], int pretty); extern void NGA_Proc_topology(int g_a, int proc, int coord[]); extern void NGA_Put(int g_a, int lo[], int hi[], void* buf, int ld[]); extern void NGA_Put_field(int g_a, int *lo, int *hi, int foff, int fsize, void *buf, int *ld); extern double NGA_Rand(int idum); extern void NGA_Randomize(int g_a, void *value); extern long NGA_Read_inc(int g_a, int subscript[], long inc); extern int NGA_Register_type(size_t bytes); extern void NGA_Release_block_grid(int g_a, int index[]); extern void NGA_Release_block(int g_a, int idx); extern void NGA_Release_block_segment(int g_a, int idx); extern void NGA_Release_ghost_element(int g_a, int index[]); extern void NGA_Release_ghosts(int g_a); extern void NGA_Release(int g_a, int lo[], int hi[]); extern void NGA_Release_update_block_grid(int g_a, int index[]); extern void NGA_Release_update_block(int g_a, int idx); extern void NGA_Release_update_block_segment(int g_a, int idx); extern void NGA_Release_update_ghost_element(int g_a, int index[]); extern void NGA_Release_update_ghosts(int g_a); extern void NGA_Release_update(int g_a, int lo[], int hi[]); extern void NGA_Scale_patch(int g_a, int lo[], int hi[], void *alpha); extern void NGA_Scatter_acc(int g_a, void *v, int* subsArray[], int n, void *alpha); extern void NGA_Scatter_acc_flat(int g_a, void *v, int subsArray[], int n, void *alpha); extern void NGA_Scatter(int g_a, void *v, int* subsArray[], int n); extern void NGA_Scatter_flat(int g_a, void *v, int subsArray[], int n); extern void NGA_Select_elem(int g_a, char* op, void* val, int *index); extern void NGA_Set_array_name(int g_a, char *name); extern void NGA_Set_block_cyclic(int g_a, int dims[]); extern void NGA_Set_block_cyclic_proc_grid(int g_a, int block[], int proc_grid[]); extern void NGA_Set_tiled_proc_grid(int g_a, int block[], int proc_grid[]); extern void NGA_Set_tiled_irreg_proc_grid(int g_a, int mapc[], int nblocks[], int proc_grid[]); extern void NGA_Set_chunk(int g_a, int chunk[]); extern void NGA_Set_data(int g_a, int ndim, int dims[], int type); extern void NGA_Set_debug(int flag); extern void NGA_Set_ghosts(int g_a, int width[]); extern void NGA_Set_irreg_distr(int g_a, int map[], int block[]); extern void NGA_Set_irreg_flag(int g_a, int flag); extern void NGA_Set_memory_limit(size_t limit); extern void NGA_Set_memory_dev(int g_a, char *device); extern void NGA_Set_pgroup(int g_a, int p_handle); extern void NGA_Set_property(int g_a, char *property); extern void NGA_Set_restricted(int g_a, int list[], int size); extern void NGA_Set_restricted_range(int g_a, int lo_proc, int hi_proc); extern void NGA_Set_property(int g_a, char *property); extern void NGA_Set_tiled_proc_grid(int g_a, int block[], int proc_grid[]); extern void NGA_Set_tiled_irreg_proc_grid(int g_a, int mapc[], int nblocks[], int proc_grid[]); extern void NGA_Unset_property(int g_a); extern void NGA_Set_memory_dev(int g_a, char *device); extern void NGA_Sprs_array_access_col_block(int s_a, int icol, int **idx, int **jdx, void **val); extern void NGA_Sprs_array_access_col_block64(int s_a, int icol, int64_t **idx, int64_t **jdx, void **val); extern void NGA_Sprs_array_add_element(int s_a, int idx, int jdx, void *val); extern int NGA_Sprs_array_assemble(int s_a); extern int NGA_Sprs_array_create(int idim, int jdim, int type); extern int NGA_Sprs_array_create_from_dense(int g_a); extern int NGA_Sprs_array_create_from_sparse(int s_a); extern void NGA_Sprs_array_column_distribution(int s_a, int iproc, int *lo, int *hi); extern void NGA_Sprs_array_col_block_list(int s_a, int **idx, int *n); extern int NGA_Sprs_array_count_sketch(int s_a, int size_k, int *g_k, int* g_w); extern int NGA_Sprs_array_destroy(int s_a); extern void NGA_Sprs_array_diag_right_multiply(int s_a, int g_d); extern void NGA_Sprs_array_diag_left_multiply(int s_a, int g_d); extern int NGA_Sprs_array_dnssprs_multiply(int g_a, int s_b); extern int NGA_Sprs_array_duplicate(int s_a); extern void NGA_Sprs_array_export(int s_a, const char* file); extern int NGA_Sprs_array_get_block(int s_a, int irow, int icol, int **idx, int **jdx, void **data, int *ilo, int *ihi, int *jlo, int *jhi); extern int NGA_Sprs_array_get_column(int s_a, int irow); extern int NGA_Sprs_array_get_column64(int s_a, int64_t irow); extern void NGA_Sprs_array_get_diag(int s_a, int *g_d); extern int NGA_Sprs_array_matmat_multiply(int s_a, int s_b); extern void NGA_Sprs_array_matvec_multiply(int s_a, int g_a, int g_v); extern void NGA_Sprs_array_row_distribution(int s_a, int iproc, int *lo, int *hi); extern void NGA_Sprs_array_scale(int s_a, void *scale); extern void NGA_Sprs_array_shift_diag(int s_a, void *shift); extern int NGA_Sprs_array_sprsdns_multiply(int s_a, int g_b); extern void NGA_Strided_acc(int g_a, int lo[], int hi[], int skip[], void* buf, int ld[], void *alpha); extern void NGA_Strided_get(int g_a, int lo[], int hi[], int skip[], void* buf, int ld[]); extern void NGA_Strided_put(int g_a, int lo[], int hi[], int skip[], void* buf, int ld[]); extern void NGA_Sync(void); extern void NGA_Terminate(void); extern int NGA_Total_blocks(int g_a); extern void NGA_Unlock(int mutex); extern void NGA_Unset_property(int g_a); extern void NGA_Update_ghosts(int g_a); extern int NGA_Update_ghost_dir(int g_a, int dimension, int idir, int flag); extern void NGA_Update_ghosts_nb(int g_a, ga_nbhdl_t *nbhandle); extern int NGA_Uses_ma(void); extern int NGA_Uses_proc_grid(int g_a); extern int NGA_Valid_handle(int g_a); extern int NGA_Verify_handle(int g_a); extern double NGA_Wtime(void); extern DoubleComplex NGA_Zdot_patch(int g_a, char t_a, int alo[], int ahi[], int g_b, char t_b, int blo[], int bhi[]); extern void NGA_Zero(int g_a); extern void NGA_Zero_patch(int g_a, int lo[], int hi[]); /* 64 bit APIs */ extern void GA_Abs_value_patch64(int g_a, int64_t *lo, int64_t *hi); extern void GA_Add_constant_patch64(int g,int64_t *lo,int64_t *hi,void *alpha); extern void GA_Cgemm64(char ta, char tb, int64_t m, int64_t n, int64_t k, SingleComplex alpha, int g_a, int g_b, SingleComplex beta, int g_c ); extern void GA_Dgemm64(char ta, char tb, int64_t m, int64_t n, int64_t k, double alpha, int g_a, int g_b, double beta, int g_c ); extern void GA_Elem_divide_patch64(int g_a, int64_t alo[], int64_t ahi[], int g_b, int64_t blo[], int64_t bhi[], int g_c, int64_t clo[], int64_t chi[]); extern void GA_Elem_maximum_patch64(int g_a,int64_t *alo,int64_t *ahi, int g_b,int64_t *blo,int64_t *bhi, int g_c,int64_t *clo,int64_t *chi); extern void GA_Elem_minimum_patch64(int g_a,int64_t *alo,int64_t *ahi, int g_b,int64_t *blo,int64_t *bhi, int g_c,int64_t *clo,int64_t *chi); extern void GA_Elem_multiply_patch64(int g_a, int64_t alo[], int64_t ahi[], int g_b, int64_t blo[], int64_t bhi[], int g_c, int64_t clo[], int64_t chi[]); extern void GA_Matmul_patch64(char transa, char transb, void* alpha, void *beta, int g_a, int64_t ailo, int64_t aihi, int64_t ajlo, int64_t ajhi, int g_b, int64_t bilo, int64_t bihi, int64_t bjlo, int64_t bjhi, int g_c, int64_t cilo, int64_t cihi, int64_t cjlo, int64_t cjhi); extern void GA_Median_patch64(int g_a, int64_t *alo, int64_t *ahi, int g_b, int64_t *blo, int64_t *bhi, int g_c, int64_t *clo, int64_t *chi, int g_m, int64_t *mlo, int64_t *mhi); extern void GA_Pack64(int g_src, int g_dest, int g_mask, int64_t lo, int64_t hi, int64_t *icount); extern void GA_Pack(int g_src, int g_dest, int g_mask, int lo, int hi, int *icount); extern void GA_Patch_enum64(int g_a, int64_t lo, int64_t hi, void *start, void *inc); extern void GA_Patch_enum(int g_a, int lo, int hi, void *start, void *inc); extern void GA_Recip_patch64(int g_a,int64_t *lo, int64_t *hi); extern void GA_Scan_add64(int g_a, int g_b, int g_sbit, int64_t lo, int64_t hi, int excl); extern void GA_Scan_copy64(int g_a, int g_b, int g_sbit, int64_t lo, int64_t hi); extern void GA_Set_block_cyclic64(int g_a, int64_t dims[]); extern void GA_Set_block_cyclic_proc_grid64(int g_a, int64_t block[], int64_t proc_grid[]); extern void GA_Set_chunk64(int g_a, int64_t chunk[]); extern void GA_Set_data64(int g_a, int ndim, int64_t dims[], int type); extern void GA_Set_ghosts64(int g_a, int64_t width[]); extern void GA_Set_irreg_distr64(int g_a, int64_t map[], int64_t block[]); extern void GA_Set_tiled_proc_grid64(int g_a, int64_t block[], int64_t proc_grid[]); extern void GA_Set_tiled_irreg_proc_grid64(int g_a, int64_t mapc[], int64_t nblocks[], int64_t proc_grid[]); extern void GA_Sgemm64(char ta, char tb, int64_t m, int64_t n, int64_t k, float alpha, int g_a, int g_b, float beta, int g_c ); extern void GA_Step_bound_info_patch64(int g_xx, int64_t xxlo[], int64_t xxhi[], int g_vv, int64_t vvlo[], int64_t vvhi[], int g_xxll, int64_t xxlllo[], int64_t xxllhi[], int64_t g_xxuu, int64_t xxuulo[], int64_t xxuuhi[], void *boundmin, void *wolfemin, void *boundmax); extern void GA_Step_max_patch64(int g_a, int64_t alo[], int64_t ahi[], int g_b, int64_t blo[], int64_t bhi[], void *step); extern void GA_Unpack64(int g_src, int g_dest, int g_mask, int64_t lo, int64_t hi, int64_t *icount); extern void GA_Unpack(int g_src, int g_dest, int g_mask, int lo, int hi, int *icount); extern void GA_Zgemm64(char ta, char tb, int64_t m, int64_t n, int64_t k, DoubleComplex alpha, int g_a, int g_b, DoubleComplex beta, int g_c ); extern void NGA_Acc64(int g_a, int64_t lo[], int64_t hi[],void* buf,int64_t ld[],void* alpha); extern void NGA_Access64(int g_a, int64_t lo[], int64_t hi[], void *ptr, int64_t ld[]); extern void NGA_Access_block64(int g_a, int64_t idx, void *ptr, int64_t ld[]); extern void NGA_Access_block_grid64(int g_a, int64_t index[], void *ptr, int64_t ld[]); extern void NGA_Access_block_segment64(int g_a, int proc, void *ptr, int64_t *len); extern void NGA_Access_ghost_element64(int g_a, void *ptr, int64_t subscript[], int64_t ld[]); extern void NGA_Access_ghosts64(int g_a, int64_t dims[], void *ptr, int64_t ld[]); extern void NGA_Add_patch64(void * alpha, int g_a, int64_t alo[], int64_t ahi[], void * beta, int g_b, int64_t blo[], int64_t bhi[], int g_c, int64_t clo[], int64_t chi[]); extern SingleComplex NGA_Cdot_patch64(int g_a, char t_a, int64_t alo[], int64_t ahi[], int g_b, char t_b, int64_t blo[], int64_t bhi[]); extern void NGA_Copy_patch64(char trans, int g_a, int64_t alo[], int64_t ahi[], int g_b, int64_t blo[], int64_t bhi[]); extern int NGA_Create64(int type,int ndim,int64_t dims[], char *name, int64_t chunk[]); extern int NGA_Create_config64(int type,int ndim,int64_t dims[], char *name, int64_t chunk[], int p_handle); extern int NGA_Create_ghosts64(int type,int ndim,int64_t dims[], int64_t width[], char *name, int64_t chunk[]); extern int NGA_Create_ghosts_config64(int type,int ndim,int64_t dims[], int64_t width[], char *name, int64_t chunk[], int p_handle); extern int NGA_Create_ghosts_irreg64(int type,int ndim,int64_t dims[], int64_t width[], char *name, int64_t nblock[], int64_t map[]); extern int NGA_Create_ghosts_irreg_config64(int type,int ndim,int64_t dims[], int64_t width[], char *name, int64_t nblock[], int64_t map[], int p_handle); extern int NGA_Create_irreg64(int type,int ndim,int64_t dims[],char *name, int64_t block[], int64_t map[]); extern int NGA_Create_irreg_config64(int type,int ndim,int64_t dims[],char *name, int64_t block[], int64_t map[], int p_handle); extern double NGA_Ddot_patch64(int g_a, char t_a, int64_t alo[], int64_t ahi[], int g_b, char t_b, int64_t blo[], int64_t bhi[]); extern void NGA_Distribution64(int g_a, int iproc, int64_t lo[], int64_t hi[]); extern float NGA_Fdot_patch64(int g_a, char t_a, int64_t alo[], int64_t ahi[], int g_b, char t_b, int64_t blo[], int64_t bhi[]); extern void NGA_Fill_patch64(int g_a, int64_t lo[], int64_t hi[], void *val); extern void NGA_Gather64(int g_a, void *v, int64_t* subsArray[], int64_t n); extern void NGA_Gather_flat64(int g_a, void *v, int64_t subsArray[], int64_t n); extern void NGA_Get64(int g_a, int64_t lo[], int64_t hi[], void* buf, int64_t ld[]); extern void NGA_Get_ghost_block64(int g_a, int64_t lo[], int64_t hi[], void* buf, int64_t ld[]); extern int NGA_Idot_patch64(int g_a, char t_a, int64_t alo[], int64_t ahi[], int g_b, char t_b, int64_t blo[], int64_t bhi[]); extern void NGA_Inquire64(int g_a, int *type, int *ndim, int64_t dims[]); extern long NGA_Ldot_patch64(int g_a, char t_a, int64_t alo[], int64_t ahi[], int g_b, char t_b, int64_t blo[], int64_t bhi[]); extern long long NGA_Lldot_patch64(int g_a, char t_a, int64_t alo[], int64_t ahi[], int g_b, char t_b, int64_t blo[], int64_t bhi[]); extern int NGA_Locate64(int g_a, int64_t subscript[]); extern int NGA_Locate_nnodes64(int g_a, int64_t lo[], int64_t hi[]); extern int NGA_Locate_region64(int g_a,int64_t lo[],int64_t hi[],int64_t map[],int procs[]); extern void NGA_Matmul_patch64(char transa, char transb, void* alpha, void *beta, int g_a, int64_t alo[], int64_t ahi[], int g_b, int64_t blo[], int64_t bhi[], int g_c, int64_t clo[], int64_t chi[]) ; extern void NGA_Merge_distr_patch64(int g_a, int64_t alo[], int64_t ahi[], int g_b, int64_t blo[], int64_t bhi[]); extern void NGA_NbAcc64(int g_a,int64_t lo[],int64_t hi[],void* buf,int64_t ld[],void* alpha, ga_nbhdl_t* nbhandle); extern void NGA_NbGet64(int g_a, int64_t lo[], int64_t hi[], void* buf, int64_t ld[], ga_nbhdl_t* nbhandle); extern void NGA_NbGet_ghost_dir64(int g_a, int64_t mask[], ga_nbhdl_t* handle); extern void NGA_NbPut64(int g_a, int64_t lo[], int64_t hi[], void* buf, int64_t ld[], ga_nbhdl_t* nbhandle); extern void NGA_Periodic_acc64(int g_a, int64_t lo[], int64_t hi[],void* buf,int64_t ld[],void* alpha); extern void NGA_Periodic_get64(int g_a, int64_t lo[], int64_t hi[], void* buf, int64_t ld[]); extern void NGA_Periodic_put64(int g_a, int64_t lo[], int64_t hi[], void* buf, int64_t ld[]); extern void NGA_Print_patch64(int g_a, int64_t lo[], int64_t hi[], int pretty); extern void NGA_Put64(int g_a, int64_t lo[], int64_t hi[], void* buf, int64_t ld[]); extern long NGA_Read_inc64(int g_a, int64_t subscript[], long inc); extern void NGA_Release64(int g_a, int64_t lo[], int64_t hi[]); extern void NGA_Release_ghost_element64(int g_a, int64_t index[]); extern void NGA_Release_update64(int g_a, int64_t lo[], int64_t hi[]); extern void NGA_Release_update_ghost_element64(int g_a, int64_t index[]); extern void NGA_Scale_patch64(int g_a, int64_t lo[], int64_t hi[], void *alpha); extern void NGA_Scatter64(int g_a, void *v, int64_t* subsArray[], int64_t n); extern void NGA_Scatter_flat64(int g_a, void *v, int64_t subsArray[], int64_t n); extern void NGA_Scatter_acc64(int g_a, void *v, int64_t* subsArray[], int64_t n, void *alpha); extern void NGA_Scatter_acc_flat64(int g_a, void *v, int64_t subsArray[], int64_t n, void *alpha); extern void NGA_Select_elem64(int g_a, char* op, void* val, int64_t* index); extern void NGA_Set_block_cyclic64(int g_a, int64_t dims[]); extern void NGA_Set_block_cyclic_proc_grid64(int g_a, int64_t block[], int64_t proc_grid[]); extern void NGA_Set_chunk64(int g_a, int64_t chunk[]); extern void NGA_Set_data64(int g_a, int ndim, int64_t dims[], int type); extern void NGA_Set_ghosts64(int g_a, int64_t width[]); extern void NGA_Set_irreg_distr64(int g_a, int64_t map[], int64_t block[]); extern void NGA_Set_tiled_proc_grid64(int g_a, int64_t block[], int64_t proc_grid[]); extern void NGA_Set_tiled_irreg_proc_grid64(int g_a, int64_t mapc[], int64_t nblocks[], int64_t proc_grid[]); extern void NGA_Strided_acc64(int g_a, int64_t lo[], int64_t hi[], int64_t skip[], void* buf, int64_t ld[], void *alpha); extern void NGA_Strided_get64(int g_a, int64_t lo[], int64_t hi[], int64_t skip[], void* buf, int64_t ld[]); extern void NGA_Strided_put64(int g_a, int64_t lo[], int64_t hi[], int64_t skip[], void* buf, int64_t ld[]); extern DoubleComplex NGA_Zdot_patch64(int g_a, char t_a, int64_t alo[], int64_t ahi[], int g_b, char t_b, int64_t blo[], int64_t bhi[]); extern void NGA_Zero_patch64(int g_a, int64_t lo[], int64_t hi[]); extern int NGA_Sprs_array_create64(int64_t idim, int64_t jdim, int type); extern void NGA_Sprs_array_add_element64(int s_a, int64_t idx, int64_t jdx, void *val); extern void NGA_Sprs_array_row_distribution64(int s_a, int iproc, int64_t *lo, int64_t *hi); extern void NGA_Sprs_array_column_distribution64(int s_a, int iproc, int64_t *lo, int64_t *hi); extern int NGA_Sprs_array_create_from_dense64(int g_a); extern int NGA_Sprs_array_get_block64(int s_a, int64_t irow, int64_t icol, int64_t **idx, int64_t **jdx, void **data, int64_t *ilo, int64_t *ihi, int64_t *jlo, int64_t *jhi); #ifdef __cplusplus } #endif #endif ga-5.9.2/global/src/ga_ckpt.c000066400000000000000000000042431500715745200157320ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /*interfaces for saving and restoring GA's */ #include "globalp.h" #include "message.h" #include "base.h" #include "macdecls.h" #include "ga_ckpt.h" #include "ga-papi.h" #include "ga-wapi.h" #define DEBUG 0 void ga_set_spare_procs(int *spare) { extern int ga_spare_procs; ga_spare_procs=*spare; armci_set_spare_procs(*spare); } int ga_icheckpoint_init(Integer *gas, int num) { int rid,i,hdl; armci_ckpt_ds_t ckptds; extern ARMCI_Group* ga_get_armci_group_(int); /*code needs to be written to check if all the gas have same pgroup*/ (void)ARMCI_Ckpt_create_ds(&ckptds,2*num); for(i=0;i=1) rid = ARMCI_Ckpt_init(NULL,ga_get_armci_group_(GA[hdl].p_handle),0,0,&ckptds); else rid = ARMCI_Ckpt_init(NULL,ARMCI_Get_world_group(),0,0,&ckptds); for(i=0;i0 following calls ! <0 only deletes factorized g_s integer g_a ! matrix to diagonalize integer g_s ! metric integer g_v ! global matrix to return evecs double precision eval(*) ! local array to return evals c c c solve the generalized eigen-value problem returning c all eigen-vectors and values in ascending order c c the input matrices are not destroyed c c proc - # of processors c pans - panel size in columns c n - problem size c c j. nieplocha 02.08.94 c c c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c integer iproc, pan, istart, iend, k, n, npan, proc, myproc integer mypanel, myelem, mycol, elemz, ipan, ik, info logical status, first_time integer g_ps, eig_flag, thr_seq save g_ps, first_time c integer hma, adrma !mapa integer hmb, adrmb !mapb integer hmz, adrmz !mapz integer ga_diag_type c integer ha, adra !a integer hb, adrb !b integer hz, adrz !z c integer isize, rsize, ptr_size integer his, adris integer hsc, adrsc integer hip, adrip c double precision drand external pdspgv, drand intrinsic mod c logical oactive ! true iff this process participates integer dimA1, dimA2, typeA integer dimS1, dimS2, typeS integer dimV1, dimV2, typeV c integer hls, adrls ! list of TCG nodes c c integer Elem Elem(istart,iend,n)=((iend-istart+1)*(2*n-istart-iend+2))/2 data first_time /.true./ data thr_seq /64/ !min problem size to be run in parallel c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c*** check the environment c myproc = ga_nodeid() proc = ga_nnodes() c c*** check GA handles, types and dimensions c call ga_check_handle(g_a, 'gai_diag: a') call ga_check_handle(g_s, 'gai_diag: s') call ga_check_handle(g_v, 'gai_diag: v') c c... unless it is the first call, check if g_s has not changed c if(first_time)then g_ps = g_s else if(g_ps .ne. g_s) $ call ga_error('gai_diag:attempt to reuse invalid data ',g_ps) endif c call ga_inquire(g_a, typeA, dimA1, dimA2) call ga_inquire(g_s, typeS, dimS1, dimS2) call ga_inquire(g_v, typeV, dimV1, dimV2) c c... verify if the dimensions match c if (dimA1 .ne. dimA2) then call ga_error(' gai_diag: can only diag square matrix ', 1) else if (dimS1 .ne. dimS2) then call ga_error(' gai_diag: can only diag square matrix ', 2) else if (dimV1 .ne. dimV2) then call ga_error(' gai_diag: can only diag square matrix ', 3) else if(dimA1.ne.dimS1 .or. dimS1 .ne. dimV1)then call ga_error(' gai_diag: A, S, V dimension mismatch ', 4) endif c c*** for smallest problems use sequential eigensolver c n = dimA1 if (n .lt. thr_seq) then call ga_diag_seq(g_a, g_s, g_v, eval) return endif c call ga_sync() c c*** test the reuse value and take the proper action c if((reuse .ne. 0) .and. first_time) $ call ga_error(' gai_diag: cannot reuse uninitialized data',reuse) c c... detect repeated "first-time" calls -- they are allowed if c... storage allocated for B is deallocated first c if((reuse .eq. 0) .and. .not. first_time) $ call ga_error(' gai_diag_reuse: repeated call, memory problem',0) c c... case for < 0 is just a cleanup of storage for B c if(reuse .lt. 0) then if(.not. first_time) then if (.not. ma_free_heap(hb)) $ call ga_error('ga_reuse: ma_free hb', 0) if (.not. ma_free_heap(hmb)) $ call ga_error('ga_reuse: ma_free hmb', 0) first_time = .true. endif return endif c c... set the flag for George eigensolver (orthogonal to reuse) c if(reuse.eq.0) then eig_flag = 1 else eig_flag = 0 endif c c*** for smaller problems use fewer processors (performance reasons) c pan = 1 !panel size npan = n/pan c proc = min( ga_nnodes(), n/30) c oactive = myproc .lt. proc if (oactive) then c c*** allocate storage for map arrays: A & Z from stack and B from heap c status = ma_push_get(mt_int,n,'mapa',hma,adrma) if(.not.status)call ga_error('gai_diag: mapa not allocated ',0) if(first_time)status = ma_alloc_get(mt_int,n,'mapb',hmb,adrmb) if(.not.status)call ga_error('gai_diag: mapb not allocated ',0) status = ma_push_get(mt_int,n,'mapz',hmz,adrmz) if(.not.status)call ga_error('gai_diag: mapc not allocated ',0) c c allocate and setup list of TCG nodes if( .not. ma_push_get(mt_int, proc,'list',hls,adrls)) $ call ga_error('gai_diag_std: insufficient memory 1b', n) call ga_list_nodeid(int_mb(adrls), proc) c c c*** determine distribution of a,b, and z c mypanel = 0 ! number of panels at given processor mycol = 0 ! number of columns at given processor myelem = 0 ! number of elements at given processor c c... allocate first half of the panels - forwards c do ipan = 1, npan/2 iproc = mod(ipan -1, proc) istart = (ipan - 1)*pan + 1 iend = istart + pan - 1 do k = istart, iend int_mb(-1+adrma+k) = int_mb(adrls+iproc) if(first_time)int_mb(-1+adrmb+k) = int_mb(adrls+iproc) int_mb(-1+adrmz+k) = int_mb(adrls+iproc) enddo if(iproc .eq. myproc)then myelem = myelem + elem(istart,iend,n) mypanel = mypanel+1 endif enddo c c... allocate second half of the panels - backwards c ik = 1 !ik is used to forward number processors do ipan = npan-1, npan/2 +1 , -1 ik = ik+1 iproc = mod(ik -1, proc) istart = (ipan - 1)*pan + 1 iend = istart + pan - 1 do k = istart, iend int_mb(-1+adrma+k) = int_mb(adrls+iproc) if(first_time)int_mb(-1+adrmb+k) = int_mb(adrls+iproc) int_mb(-1+adrmz+k) = int_mb(adrls+iproc) enddo if(iproc .eq. myproc)then myelem = myelem + elem(istart,iend,n) mypanel = mypanel+1 endif enddo c c... actually, there is one more panel left for the mismatch c iproc = 0 !processor 0 gets the mismatch panel istart = (npan - 1)*pan + 1 iend = n do k = istart, iend int_mb(-1+adrma+k) = int_mb(adrls+iproc) if(first_time)int_mb(-1+adrmb+k) = int_mb(adrls+iproc) int_mb(-1+adrmz+k) = int_mb(adrls+iproc) enddo mycol = mypanel * pan if(iproc .eq. myproc)then myelem = myelem + elem(istart,iend,n) mycol = mycol + iend - istart + 1 endif elemz = mycol * n c c*** allocate arrays: A & Z from stack and B from heap c status = ma_push_get(mt_dbl,myelem,'a',ha,adra) if(.not. status)call ga_error('gai_diag: a not allocated ',0) if(first_time)status = ma_alloc_get(mt_dbl,myelem,'b',hb,adrb) if(.not. status)call ga_error('gai_diag: b not allocated ',0) status = ma_push_get(mt_dbl,elemz,'z',hz,adrz) if(.not. status)call ga_error('gai_diag: c not allocated ',0) c c*** load A and S matrices c istart = 0 do k = 1, n if( int_mb(-1+adrma+k) .eq. int_mb(adrls + myproc))then myelem = n-k+1 call ga_get(g_a,k,n,k,k,dbl_mb(adra+istart),n-k+1) if(first_time) % call ga_get(g_s,k,n,k,k,dbl_mb(adrb+istart),n-k+1) istart = istart + myelem endif enddo c c*** allocate some scratch memory for eigensolver c * * temporary patch following george reccomendation to use 6n * status = ma_push_get(mt_int,6*n,'iscratch',his,adris) if (.not. status) call ga_error('ga_reuse: 6*n', 6*n) call fmemreq(0,n,int_mb(adrma),int_mb(adrmb),int_mb(adrmz), & isize, rsize, ptr_size,int_mb(adris)) status = ma_pop_stack(his) if (.not. status) call ga_error('ga_reuse: ma pop his', 0) if(.not. ma_push_get(mt_int,isize,'iscratch',his,adris)) & call ga_error('eig allocator',1) if(.not. ma_push_get(mt_dbl,rsize,'scratch',hsc,adrsc)) & call ga_error('eig allocator',2) if(.not. ma_push_get(mt_dbl,ptr_size,'iptr',hip,adrip)) & call ga_error('eig allocator',3) c endif c c call ga_sync() ! To reduce message buffering problems c c*** calling PEIGS parallel eigensolver c if (oactive) then call pdspgv( eig_flag, n, dbl_mb(adra), int_mb(adrma), & dbl_mb(adrb), int_mb(adrmb), & dbl_mb(adrz), int_mb(adrmz), & eval, & int_mb(adris), isize, & dbl_mb(adrip), ptr_size, & dbl_mb(adrsc), rsize, info) if (info .ne. 0) $ call ga_error('gai_diag: info non-zero ', info) endif c call ga_sync() ! To reduce message buffering problems c c*** store the eigenvector matrix in global array g_v c if (oactive) then istart = 0 do k = 1, n if( int_mb(-1+adrmz+k) .eq. int_mb(adrls + myproc))then call ga_put(g_v,1,n,k,k,dbl_mb(adrz+istart),n) istart = istart + n endif enddo c c*** deallocate storage for temporary arrays c status = .true. status = status .and. ma_pop_stack(hip) status = status .and. ma_pop_stack(hsc) status = status .and. ma_pop_stack(his) status = status .and. ma_pop_stack(hz) status = status .and. ma_pop_stack(ha) status = status .and. ma_pop_stack(hls) status = status .and. ma_pop_stack(hmz) status = status .and. ma_pop_stack(hma) if (.not. status) call ga_error('ga_reuse: ma_pop?', 0) endif c c c*** Make sure that even processes that did not participate in the c*** diagonalization have the eigen vectors c ga_diag_type = 32760 - 30 call ga_brdcst(ga_diag_type, eval, $ ma_sizeof(MT_DBL,n,MT_BYTE), 0) c if(reuse.eq.0) first_time = .false. c call ga_sync() end ga-5.9.2/global/src/ga_diag_seq.F000066400000000000000000000166421500715745200165160ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif #if (BLAS_SIZE ==4) #define INTGR4 integer*4 #else #define INTGR4 integer*8 #endif c c This file has been converted to use LAPACK circa 2011 c instead of EISPACK circa 1983 by Jeff Hammond circa 2014. c subroutine gai_diag_seq(g_a, g_s, g_v, evals) implicit none #include "mafdecls.fh" #include "global.fh" #include "galinalg.fh" integer g_a ! Matrix to diagonalize integer g_s ! Metric integer g_v ! Global matrix to return evecs double precision evals(*) ! Local array to return evals c integer n #if ENABLE_EISPACK integer l_fv1, l_fv2, l_v MA_ACCESS_INDEX_TYPE k_fv1, k_fv2, k_v #else integer l_wrk MA_ACCESS_INDEX_TYPE k_wrk integer n2 INTGR4 n2_4 #endif integer l_a, l_s MA_ACCESS_INDEX_TYPE k_a, k_s integer dim1, dim2, type, me INTGR4 n4,ierr,one4 logical status c c c Solve the generalized eigen-value problem returning c all eigen-vectors and values in ascending order c c The input matrices may be destroyed c call ga_check_handle(g_a, 'ga_diag_seq a') call ga_check_handle(g_s, 'ga_diag_seq s') call ga_check_handle(g_v, 'ga_diag_seq v') call ga_sync() c c Only process 0 does the diag c call ga_inquire(g_a, type, dim1, dim2) if(dim1.ne.dim2) $ call ga_error('ga_diag_seq: nonsquare matrix ',0) n = dim1 n4 = n me = ga_nodeid() if (me .eq. 0) then c c allocate scratch space c status=ma_push_get(MT_DBL, n*n, $ 'diag_seq:a', l_a, k_a) status=status.and.ma_push_get(MT_DBL, n*n, $ 'diag_seq:s', l_s, k_s) #if ENABLE_EISPACK status=status.and.ma_push_get(MT_DBL, n*n, $ 'diag_seq:v', l_v, k_v) status=status.and.ma_push_get(MT_DBL, n, $ 'diag_seq:fv1', l_fv1, k_fv1) status=status.and.ma_push_get(MT_DBL, n, $ 'diag_seq:fv2', l_fv2, k_fv2) #else c LAPACK fails for n=1 without this n2 = max(n*n,3*n-1) status=status.and.ma_push_get(MT_DBL, n2, $ 'diag_seq:wrk', l_wrk, k_wrk) #endif if (.not. status) $ call ga_error('ga_diag_seq: insufficient memory', n) c c Fill local arrays from global arrays c call ga_get(g_a, 1, n, 1, n, dbl_mb(k_a), n) call ga_get(g_s, 1, n, 1, n, dbl_mb(k_s), n) c #ifdef DEBUG_MATH_LIBS write(6,*) ' fock matrix ' call output(dbl_mb(k_a), 1, n, 1, n, n, n, 1) write(6,*) ' overlap matrix ' call output(dbl_mb(k_s), 1, n, 1, n, n, n, 1) #endif #if ENABLE_EISPACK call rsg(n, n, dbl_mb(k_a), dbl_mb(k_s), evals, 1, $ dbl_mb(k_v), dbl_mb(k_fv1), dbl_mb(k_fv2), ierr) #else one4=1 n2_4=n2 call dsygv(one4,'V','U',n4,dbl_mb(k_a),n4,dbl_mb(k_s),n4, $ evals,dbl_mb(k_wrk),n2_4, ierr) if (ierr.ne.0) $ call ga_error('ga_diag_seq: dsygv failed',ierr) c We used to copy to preserve code symmetry with EISPACK c call dcopy(n*n, dbl_mb(k_a), 1, dbl_mb(k_v), 1) #endif #ifdef DEBUG_MATH_LIBS write(6,*) 'eigen vectors ' call output(dbl_mb(k_v), 1, n, 1, n, n, n, 1) write(6,*) 'eigen values ' call output(evals, 1, n, 1, 1, n, 1, 1) * call forflush(6) #endif c c Shove eigen-vectors back into global array c #if ENABLE_EISPACK call ga_put(g_v, 1, n, 1, n, dbl_mb(k_v), n) #else call ga_put(g_v, 1, n, 1, n, dbl_mb(k_a), n) #endif c c Free scratch space c #if ENABLE_EISPACK status = ma_pop_stack(l_fv2) status = ma_pop_stack(l_fv1) status = ma_pop_stack(l_v) #else status = ma_pop_stack(l_wrk) #endif status = ma_pop_stack(l_s) status = ma_pop_stack(l_a) endif c c Broadcast the eigenvalues to all processes c call ga_brdcst(32500, evals, $ ma_sizeof(MT_DBL,n,MT_BYTE), 0) call ga_sync() c end c subroutine gai_diag_std_seq(g_a, g_v, evals) implicit none #include "mafdecls.fh" #include "global.fh" integer g_a ! Matrix to diagonalize integer g_v ! Global matrix to return evecs double precision evals(*) ! Local array to return evals c integer n #if ENABLE_EISPACK integer l_fv1, l_fv2, l_v MA_ACCESS_INDEX_TYPE k_fv1, k_fv2, k_v #else integer l_wrk MA_ACCESS_INDEX_TYPE k_wrk integer n2 #endif integer l_a MA_ACCESS_INDEX_TYPE k_a integer dim1, dim2, type, me INTGR4 n4,n2_i4,ierr logical status c c c Solve the standard eigen-value problem returning c all eigen-vectors and values in ascending order c c The input matrices may be destroyed c call ga_check_handle(g_a, 'ga_diag_std a') call ga_check_handle(g_v, 'ga_diag_std v') call ga_sync() c c Only process 0 does the diag c call ga_inquire(g_a, type, dim1, dim2) if(dim1.ne.dim2) $ call ga_error('ga_diag_std_seq: nonsquare matrix ',0) n = dim1 n4 = n me = ga_nodeid() if (me .eq. 0) then c c allocate scratch space c status=ma_push_get(MT_DBL, n*n, $ 'diag_std_seq:a', l_a, k_a) #if ENABLE_EISPACK status=status.and.ma_push_get(MT_DBL, n*n, $ 'diag_std_seq:v', l_v, k_v) status=status.and.ma_push_get(MT_DBL, n, $ 'diag_std_seq:fv1', l_fv1, k_fv1) status=status.and.ma_push_get(MT_DBL, n, $ 'diag_std_seq:fv2', l_fv2, k_fv2) #else c LAPACK fails for n=1 without this n2 = max(n*n,3*n-1) n2_i4=n2 status=status.and.ma_push_get(MT_DBL, n2, $ 'diag_std_seq:wrk', l_wrk, k_wrk) #endif if (.not. status) $ call ga_error('ga_diag_std_seq: insufficient memory', n) c c Fill local arrays from global arrays c call ga_get(g_a, 1, n, 1, n, dbl_mb(k_a), n) c #ifdef DEBUG_MATH_LIBS write(6,*) ' input matrix ' call output(dbl_mb(k_a), 1, n, 1, n, n, n, 1) #endif #if ENABLE_EISPACK call rs(n, n, dbl_mb(k_a), evals, 1, $ dbl_mb(k_v), dbl_mb(k_fv1), dbl_mb(k_fv2), ierr) #else call dsyev('V', 'L', n4, dbl_mb(k_a), n4, $ evals, dbl_mb(k_wrk), n2_i4, ierr) if (ierr.ne.0) $ call ga_error('ga_diag_std_seq: dsyev failed',ierr) c We used to copy to preserve code symmetry with EISPACK c call dcopy(n*n, dbl_mb(k_a), 1, dbl_mb(k_v), 1) #endif #ifdef DEBUG_MATH_LIBS write(6,*) 'eigen vectors ' call output(dbl_mb(k_v), 1, n, 1, n, n, n, 1) write(6,*) 'eigen values ' call output(evals, 1, n, 1, 1, n, 1, 1) * call forflush(6) #endif c c Shove eigen-vectors back into global array c #if ENABLE_EISPACK call ga_put(g_v, 1, n, 1, n, dbl_mb(k_v), n) #else call ga_put(g_v, 1, n, 1, n, dbl_mb(k_a), n) #endif c c Free scratch space c #if ENABLE_EISPACK status = ma_pop_stack(l_fv2) status = ma_pop_stack(l_fv1) status = ma_pop_stack(l_v) #else status = ma_pop_stack(l_wrk) #endif status = ma_pop_stack(l_a) endif c c Broadcast the eigenvalues to all processes c call ga_brdcst(32500, evals, $ ma_sizeof(MT_DBL,n,MT_BYTE), 0) call ga_sync() c end ga-5.9.2/global/src/ga_diag_seqc.c000066400000000000000000000020371500715745200167070ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "globalp.h" #include "ga-papi.h" #include "ga-wapi.h" #if ENABLE_F77 extern void gai_diag_seq_(Integer*, Integer*, Integer*, DoublePrecision*); extern void gai_diag_std_seq_(Integer*, Integer*, DoublePrecision*); # define gai_diag_seq_ F77_FUNC_(gai_diag_seq,GAI_DIAG_SEQ) # define gai_diag_std_seq_ F77_FUNC_(gai_diag_std_seq,GAI_DIAG_STD_SEQ) #endif #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_diag_seq = pnga_diag_seq #endif void pnga_diag_seq(Integer g_a, Integer g_s, Integer g_v, DoublePrecision *eval) { #if ENABLE_F77 gai_diag_seq_(&g_a, &g_s, &g_v, eval); #else pnga_error("ga_diag_seq: you must configure --enable-f77", 0L); #endif } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_diag_std_seq = pnga_diag_std_seq #endif void pnga_diag_std_seq(Integer g_a, Integer g_v, DoublePrecision *eval) { #if ENABLE_F77 gai_diag_std_seq_(&g_a, &g_v, eval); #else pnga_error("ga_diag_std_seq: you must configure --enable-f77", 0L); #endif } ga-5.9.2/global/src/ga_iterator.h000066400000000000000000000157271500715745200166400ustar00rootroot00000000000000/*$Id: iterator.h,v 1.40.2.4 2007/12/18 18:41:27 d3g293 Exp $ */ #include "gaconfig.h" #include "typesf2c.h" /** * Struct containing all data needed to keep track of iterator state */ typedef struct { Integer g_a; Integer lo[MAXDIM]; Integer hi[MAXDIM]; Integer count; Integer *map; Integer *proclist; int *proclistperm; Integer *mapc; Integer nproc; Integer offset; Integer iproc; Integer iblock; Integer new_proc; Integer oversize; Integer lobuf[MAXDIM]; Integer hibuf[MAXDIM]; /* ScalPACK distribution parameters*/ Integer blk_num[MAXDIM]; Integer blk_dim[MAXDIM]; Integer blk_inc[MAXDIM]; Integer blk_ld[MAXDIM]; Integer hlf_blk[MAXDIM]; Integer blk_size[MAXDIM]; Integer proc_index[MAXDIM]; Integer index[MAXDIM]; } _iterator_hdl; #if 0 /* this macro finds the block indices for a given block */ #define gam_find_block_indices(ga_handle,nblock,index) { \ int _itmp, _i; \ int _ndim = GA[ga_handle].ndim; \ _itmp = nblock; \ index[0] = _itmp%GA[ga_handle].num_blocks[0]; \ for (_i=1; _i<_ndim; _i++) { \ _itmp = (_itmp-index[_i-1])/GA[ga_handle].num_blocks[_i-1]; \ index[_i] = _itmp%GA[ga_handle].num_blocks[_i]; \ } \ } /* this macro finds the ScaLAPACK indices for a given processor */ #ifdef COMPACT_SCALAPACK #define gam_find_proc_indices(ga_handle,proc,index) { \ Integer _itmp, _i; \ Integer _ndim = GA[ga_handle].ndim; \ _itmp = proc; \ index[0] = _itmp%GA[ga_handle].nblock[0]; \ for (_i=1; _i<_ndim; _i++) { \ _itmp = (_itmp-index[_i-1])/GA[ga_handle].nblock[_i-1]; \ index[_i] = _itmp%GA[ga_handle].nblock[_i]; \ } \ } #else #define gam_find_proc_indices(ga_handle,proc,index) { \ Integer _itmp, _i; \ Integer _ndim = GA[ga_handle].ndim; \ _itmp = proc; \ index[_ndim-1] = _itmp%GA[ga_handle].nblock[_ndim-1]; \ for (_i=_ndim-2; _i>=0; _i--) { \ _itmp = (_itmp-index[_i+1])/GA[ga_handle].nblock[_i+1]; \ index[_i] = _itmp%GA[ga_handle].nblock[_i]; \ } \ } #endif /* this macro finds the block index corresponding to a given set of indices */ #define gam_find_block_from_indices(ga_handle,nblock,index) { \ int _ndim = GA[ga_handle].ndim; \ int _i; \ nblock = index[_ndim-1]; \ for (_i=_ndim-2; _i >= 0; _i--) { \ nblock = nblock*GA[ga_handle].num_blocks[_i]+index[_i]; \ } \ } /* this macro finds the proc that owns a given set block indices using the ScaLAPACK data distribution */ #ifdef COMPACT_SCALAPACK #define gam_find_proc_from_sl_indices(ga_handle,proc,index) { \ int _ndim = GA[ga_handle].ndim; \ int _i; \ Integer _index2[MAXDIM]; \ for (_i=0; _i<_ndim; _i++) { \ _index2[_i] = index[_i]%GA[ga_handle].nblock[_i]; \ } \ proc = _index2[_ndim-1]; \ for (_i=_ndim-2; _i >= 0; _i--) { \ proc = proc*GA[ga_handle].nblock[_i]+_index2[_i]; \ } \ } #else #define gam_find_proc_from_sl_indices(ga_handle,proc,index) { \ int _ndim = GA[ga_handle].ndim; \ int _i; \ Integer _index2[MAXDIM]; \ for (_i=0; _i<_ndim; _i++) { \ _index2[_i] = index[_i]%GA[ga_handle].nblock[_i]; \ } \ proc = _index2[0]; \ for (_i=1; _i < _ndim; _i++) { \ proc = proc*GA[ga_handle].nblock[_i]+_index2[_i]; \ } \ } #endif /* this macro computes the strides on both the remote and local processors that map out the data. ld and ldrem are the physical dimensions of the memory on both the local and remote processors. */ /* NEEDS C_INT64 CONVERSION */ #define gam_setstride(ndim, size, ld, ldrem, stride_rem, stride_loc){\ int _i; \ stride_rem[0]= stride_loc[0] = (int)size; \ for(_i=0;_i= 2MB * ------------------------------------- * * Note#3: If Stride information needs to be printed, set GA_PRINT_STRIDE. * Stride information is printed in ga_profile_terminate() for a various * selective message ranges and event types. Modify according to your needs. */ #ifdef ENABLE_PROFILE #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_MATH_H # include #endif #include "globalp.h" #include "base.h" #include "ga_profile.h" #include "ga-papi.h" #include "ga-wapi.h" #ifndef MSG_COMMS_MPI # include "tcgmsg.h" # define MP_TIMER tcg_time #else # include "mpi.h" # define MP_TIMER MPI_Wtime #endif #define GA_PRINT_STRIDE 1 #define GA_MAX_MSG_RANGE 21 #if GA_PRINT_STRIDE #define STRIDE_COUNT 1000 typedef struct ga_stride { int ndim; int lo[GA_MAX_DIM]; int hi[GA_MAX_DIM]; char name[FNAM+1]; double time; }ga_stride_t; #endif #define GA_EVENTS 6 /* get, put, acc, Non-Contiguous get, put, acc*/ enum events {GET, /* Contiguous get */ PUT, ACC, NC_GET, /* Non contiguous Get */ NC_PUT, NC_ACC }; char *event_name[GA_EVENTS] = {"GET", "PUT", "ACC", "NON CONTIGUOUS GET", "NON CONTIGUOUS PUT", "NON CONTIGUOUS ACC"}; typedef struct ga_profile { int count; /* number of times called */ double exectime; /* total execution time for "count" calls */ #if GA_PRINT_STRIDE ga_stride_t stride[STRIDE_COUNT]; #endif }ga_profile_t; /* profile get/put/acc for various message ranges (i.e GA_MAX_MSG_RANGE) */ static ga_profile_t GA_PROF[GA_EVENTS][GA_MAX_MSG_RANGE]; /* Current event */ struct event_info { int event_type; int range; int is_set; double start_time; } gCURRENT_EVNT; void ga_profile_init() { int i,j; if(pnga_nodeid()==0) {printf("\nProfiling GA - ON\n");fflush(stdout);} for(i=0; i=GA_MAX_MSG_RANGE) range = GA_MAX_MSG_RANGE; /* check contiguous or non-contiguous */ for(i=0; i1) non_contig=1; /* i.e. non-contiguous */ switch(comm_type) { case ENABLE_PROFILE_PUT: if(non_contig) event_type = NC_PUT; else event_type = PUT; break; case ENABLE_PROFILE_GET: if(non_contig) event_type = NC_GET; else event_type = GET; break; case ENABLE_PROFILE_ACC: if(non_contig) event_type = NC_ACC; else event_type = ACC; break; default: pnga_error("ENABLE_PROFILE: Invalid communication type", 0L); } /* set the curent event for timer */ ga_profile_set_event(event_type, range); /* profile update: i.e. update event count */ GA_PROF[event_type][range].count++; #if GA_PRINT_STRIDE { int idx = GA_PROF[event_type][range].count-1; if(idx%d)\n", pnga_nodeid(), GA_PROF[GET][i].count, GA_PROF[PUT][i].count, GA_PROF[ACC][i].count, 1<%d)\n",pnga_nodeid(), GA_PROF[NC_GET][i].count, GA_PROF[NC_PUT][i].count, GA_PROF[NC_ACC][i].count, 1<%d)\n", pnga_nodeid(), GA_PROF[GET][i].exectime, GA_PROF[PUT][i].exectime, GA_PROF[ACC][i].exectime, 1<%d)\n", pnga_nodeid(), GA_PROF[NC_GET][i].exectime, GA_PROF[NC_PUT][i].exectime, GA_PROF[NC_ACC][i].exectime, 1<=STRIDE_COUNT) break; time += GA_PROF[event][range].stride[i].time; ndim = GA_PROF[event][range].stride[i].ndim; printf("%d\t%d %.2e (",i, ndim, GA_PROF[event][range].stride[i].time); for(j=0;j "); for(j=0;j #endif #if HAVE_MATH_H # include #endif #include "globalp.h" #include "macdecls.h" #include "ga-papi.h" #include "ga-wapi.h" #include "galinalg.h" #define REAL double #define ZERO 0.0e0 #define ONE 1.0e0 /*----------------------*/ void LP_daxpy(n,da,dx,incx,dy,incy) /* constant times a vector plus a vector. jack dongarra, linpack, 3/11/78. */ REAL dx[],dy[],da; int incx,incy,n; { int i,ix,iy; if(n <= 0) return; if (da == ZERO) return; if(incx != 1 || incy != 1) { /* code for unequal increments or equal increments not equal to 1 */ ix = 0; iy = 0; if(incx < 0) ix = (-n+1)*incx; if(incy < 0)iy = (-n+1)*incy; for (i = 0;i < n; i++) { dy[iy] = dy[iy] + da*dx[ix]; ix = ix + incx; iy = iy + incy; } return; } /* code for both increments equal to 1 */ #ifdef ROLL for (i = 0;i < n; i++) { dy[i] = dy[i] + da*dx[i]; } #endif #ifdef UNROLL m = n % 4; if ( m != 0) { for (i = 0; i < m; i++) dy[i] = dy[i] + da*dx[i]; if (n < 4) return; } for (i = m; i < n; i = i + 4) { dy[i] = dy[i] + da*dx[i]; dy[i+1] = dy[i+1] + da*dx[i+1]; dy[i+2] = dy[i+2] + da*dx[i+2]; dy[i+3] = dy[i+3] + da*dx[i+3]; } #endif } /*----------------------*/ REAL LP_ddot(n,dx,incx,dy,incy) /* forms the dot product of two vectors. jack dongarra, linpack, 3/11/78. */ REAL dx[],dy[]; int incx,incy,n; { REAL dtemp; int i,ix,iy; dtemp = ZERO; if(n <= 0) return(ZERO); if(incx != 1 || incy != 1) { /* code for unequal increments or equal increments not equal to 1 */ ix = 0; iy = 0; if (incx < 0) ix = (-n+1)*incx; if (incy < 0) iy = (-n+1)*incy; for (i = 0;i < n; i++) { dtemp = dtemp + dx[ix]*dy[iy]; ix = ix + incx; iy = iy + incy; } return(dtemp); } /* code for both increments equal to 1 */ #ifdef ROLL for (i=0;i < n; i++) dtemp = dtemp + dx[i]*dy[i]; #endif #ifdef UNROLL m = n % 5; if (m != 0) { for (i = 0; i < m; i++) dtemp = dtemp + dx[i]*dy[i]; if (n < 5) return(dtemp); } for (i = m; i < n; i = i + 5) { dtemp = dtemp + dx[i]*dy[i] + dx[i+1]*dy[i+1] + dx[i+2]*dy[i+2] + dx[i+3]*dy[i+3] + dx[i+4]*dy[i+4]; } #endif return(dtemp); } /*----------------------*/ void LP_dscal(n,da,dx,incx) /* scales a vector by a constant. jack dongarra, linpack, 3/11/78. */ REAL da,dx[]; int n, incx; { int i,nincx; if(n <= 0)return; if(incx != 1) { /* code for increment not equal to 1 */ nincx = n*incx; for (i = 0; i < nincx; i = i + incx) dx[i] = da*dx[i]; return; } /* code for increment equal to 1 */ #ifdef ROLL for (i = 0; i < n; i++) dx[i] = da*dx[i]; #endif #ifdef UNROLL m = n % 5; if (m != 0) { for (i = 0; i < m; i++) dx[i] = da*dx[i]; if (n < 5) return; } for (i = m; i < n; i = i + 5){ dx[i] = da*dx[i]; dx[i+1] = da*dx[i+1]; dx[i+2] = da*dx[i+2]; dx[i+3] = da*dx[i+3]; dx[i+4] = da*dx[i+4]; } #endif } /*----------------------*/ int LP_idamax(n,dx,incx) /* finds the index of element having max. absolute value. jack dongarra, linpack, 3/11/78. */ REAL dx[]; int incx,n; { REAL dmax; int i, ix, itemp = 0; if( n < 1 ) return(-1); if(n ==1 ) return(0); if(incx != 1) { /* code for increment not equal to 1 */ ix = 1; dmax = fabs((double)dx[0]); ix = ix + incx; for (i = 1; i < n; i++) { if(fabs((double)dx[ix]) > dmax) { itemp = i; dmax = fabs((double)dx[ix]); } ix = ix + incx; } } else { /* code for increment equal to 1 */ itemp = 0; dmax = fabs((double)dx[0]); for (i = 1; i < n; i++) { if(fabs((double)dx[i]) > dmax) { itemp = i; dmax = fabs((double)dx[i]); } } } return (itemp); } /*----------------------*/ void LP_dgefa(a,lda,n,ipvt,info) REAL a[]; int lda,n,ipvt[],*info; /* We would like to declare a[][lda], but c does not allow it. In this function, references to a[i][j] are written a[lda*i+j]. */ /* LP_dgefa factors a double precision matrix by gaussian elimination. LP_dgefa is usually called by dgeco, but it can be called directly with a saving in time if rcond is not needed. (time for dgeco) = (1 + 9/n)*(time for LP_dgefa) . on entry a REAL precision[n][lda] the matrix to be factored. lda integer the leading dimension of the array a . n integer the order of the matrix a . on return a an upper triangular matrix and the multipliers which were used to obtain it. the factorization can be written a = l*u where l is a product of permutation and unit lower triangular matrices and u is upper triangular. ipvt integer[n] an integer vector of pivot indices. info integer = 0 normal value. = k if u[k][k] .eq. 0.0 . this is not an error condition for this subroutine, but it does indicate that LP_dgesl or dgedi will divide by zero if called. use rcond in dgeco for a reliable indication of singularity. linpack. this version dated 08/14/78 . cleve moler, university of new mexico, argonne national lab. functions blas LP_daxpy,LP_dscal,LP_idamax */ { /* internal variables */ REAL t; int j,k,kp1,l,nm1; /* gaussian elimination with partial pivoting */ *info = 0; nm1 = n - 1; if (nm1 >= 0) { for (k = 0; k < nm1; k++) { kp1 = k + 1; /* find l = pivot index */ l = LP_idamax(n-k,&a[lda*k+k],1) + k; ipvt[k] = l; /* zero pivot implies this column already triangularized */ if (a[lda*k+l] != ZERO) { /* interchange if necessary */ if (l != k) { t = a[lda*k+l]; a[lda*k+l] = a[lda*k+k]; a[lda*k+k] = t; } /* compute multipliers */ t = -ONE/a[lda*k+k]; LP_dscal(n-(k+1),t,&a[lda*k+k+1],1); /* row elimination with column indexing */ for (j = kp1; j < n; j++) { t = a[lda*j+l]; if (l != k) { a[lda*j+l] = a[lda*j+k]; a[lda*j+k] = t; } LP_daxpy(n-(k+1),t,&a[lda*k+k+1],1, &a[lda*j+k+1],1); } } else { *info = k; } } } ipvt[n-1] = n-1; if (a[lda*(n-1)+(n-1)] == ZERO) *info = n-1; } /*----------------------*/ void LP_dgesl(a,lda,n,ipvt,b,job) int lda,n,ipvt[],job; REAL a[],b[]; /* We would like to declare a[][lda], but c does not allow it. In this function, references to a[i][j] are written a[lda*i+j]. */ /* LP_dgesl solves the double precision system a * x = b or trans(a) * x = b using the factors computed by dgeco or LP_dgefa. on entry a double precision[n][lda] the output from dgeco or LP_dgefa. lda integer the leading dimension of the array a . n integer the order of the matrix a . ipvt integer[n] the pivot vector from dgeco or LP_dgefa. b double precision[n] the right hand side vector. job integer = 0 to solve a*x = b , = nonzero to solve trans(a)*x = b where trans(a) is the transpose. on return b the solution vector x . error condition a division by zero will occur if the input factor contains a zero on the diagonal. technically this indicates singularity but it is often caused by improper arguments or improper setting of lda . it will not occur if the subroutines are called correctly and if dgeco has set rcond .gt. 0.0 or LP_dgefa has set info .eq. 0 . to compute inverse(a) * c where c is a matrix with p columns dgeco(a,lda,n,ipvt,rcond,z) if (!rcond is too small){ for (j=0,j= 1) { for (k = 0; k < nm1; k++) { l = ipvt[k]; t = b[l]; if (l != k){ b[l] = b[k]; b[k] = t; } LP_daxpy(n-(k+1),t,&a[lda*k+k+1],1,&b[k+1],1); } } /* now solve u*x = y */ for (kb = 0; kb < n; kb++) { k = n - (kb + 1); b[k] = b[k]/a[lda*k+k]; t = -b[k]; LP_daxpy(k,t,&a[lda*k+0],1,&b[0],1); } } else { /* job = nonzero, solve trans(a) * x = b first solve trans(u)*y = b */ for (k = 0; k < n; k++) { t = LP_ddot(k,&a[lda*k+0],1,&b[0],1); b[k] = (b[k] - t)/a[lda*k+k]; } /* now solve trans(l)*x = y */ if (nm1 >= 1) { for (kb = 1; kb < nm1; kb++) { k = n - (kb+1); b[k] = b[k] + LP_ddot(n-(k+1),&a[lda*k+k+1],1,&b[k+1],1); l = ipvt[k]; if (l != k) { t = b[l]; b[l] = b[k]; b[k] = t; } } } } } /** * solve the set of linear equations * * AX = B * * with possibly multiple rhs stored as columns of matrix B * the matrix A is not destroyed */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_lu_solve_seq = pnga_lu_solve_seq #endif void pnga_lu_solve_seq(char *trans, Integer g_a, Integer g_b) { logical oactive; /* true iff this process participates */ Integer dimA1, dimA2, typeA; Integer dimB1, dimB2, typeB; Integer me; #if HAVE_LAPACK || ENABLE_F77 BlasInt blas_dimA1, blas_dimA2, blas_dimB1, blas_dimB2, info=0; #else Integer info=0; #endif Integer dims[2], ndim; Integer lo[2], hi[2]; /** check environment */ me = pnga_nodeid(); /** check GA info for input arrays */ pnga_check_handle(g_a, "ga_lu_solve: a"); pnga_check_handle(g_b, "ga_lu_solve: b"); pnga_inquire(g_a, &typeA, &ndim, dims); dimA1 = dims[0]; dimA2 = dims[1]; pnga_inquire(g_b, &typeB, &ndim, dims); dimB1 = dims[0]; dimB2 = dims[1]; if (dimA1 != dimA2) pnga_error("ga_lu_solve: g_a must be square matrix ", 1); else if(dimA1 != dimB1) pnga_error("ga_lu_solve: dims of A and B do not match ", 1); else if(typeA != C_DBL || typeB != C_DBL) pnga_error("ga_lu_solve: wrong type(s) of A and/or B ", 1); pnga_sync(); oactive = (me == 0); if (oactive) { DoublePrecision *adra, *adrb; Integer *adri; Integer one=1; /** allocate a,b, and work and ipiv arrays */ adra = (DoublePrecision*) pnga_malloc(dimA1*dimA2, F_DBL, "a"); adrb = (DoublePrecision*) pnga_malloc(dimB1*dimB2, F_DBL, "b"); adri = (Integer*) pnga_malloc(GA_MIN(dimA1,dimA2), F_INT, "ipiv"); /** Fill local arrays from global arrays */ lo[0] = one; hi[0] = dimA1; lo[1] = one; hi[1] = dimA2; pnga_get(g_a, lo, hi, adra, &dimA1); lo[0] = one; hi[0] = dimB1; lo[1] = one; hi[1] = dimB2; pnga_get(g_b, lo, hi, adrb, &dimB1); /** LU factorization */ #if HAVE_LAPACK || ENABLE_F77 blas_dimA1 = dimA1; blas_dimA2 = dimA2; blas_dimB1 = dimB1; blas_dimB2 = dimB2; LAPACK_DGETRF(&blas_dimA1, &blas_dimA2, adra, &blas_dimA1, (BlasInt*)adri, &info); #else { int info_t; LP_dgefa(adra, (int)dimA1, (int)dimA2, (int*)adri, &info_t); info = info_t; } #endif /** SOLVE */ if(info == 0) { #if HAVE_LAPACK || ENABLE_F77 LAPACK_DGETRS(trans, &blas_dimA1, &blas_dimB2, adra, &blas_dimA1, (BlasInt*)adri, adrb, &blas_dimB1, &info); #else DoublePrecision *p_b; Integer i; int job=0; if(*trans == 't' || *trans == 'T') job = 1; for(i=0; i #endif /** * Symmetrizes matrix A: A := .5 * (A+A`) * diag(A) remains unchanged * */ #include "globalp.h" #include "macdecls.h" #include "ga-papi.h" #include "ga-wapi.h" static void gai_add( Integer *lo, Integer *hi, void *a, void *b, DoublePrecision alpha, Integer type, Integer nelem, Integer ndim) { Integer i, j, m=0; Integer nrow, ncol, indexA=0, indexB=0; DoublePrecision *A = (DoublePrecision *)a, *B = (DoublePrecision*)b; Integer offset1=1, offset2=1; nrow = hi[ndim-2] - lo[ndim-2] + 1; ncol = hi[ndim-1] - lo[ndim-1] + 1; for(i=0; i= nelem) indexA = j + ++m*offset2; A[indexA] = alpha *(A[indexA] + B[indexB]); } } } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_symmetrize = pnga_symmetrize #endif void pnga_symmetrize(Integer g_a) { DoublePrecision alpha = 0.5; Integer i, me = pnga_nodeid(); Integer alo[GA_MAX_DIM], ahi[GA_MAX_DIM], lda[GA_MAX_DIM], nelem=1; Integer blo[GA_MAX_DIM], bhi[GA_MAX_DIM], ldb[GA_MAX_DIM]; Integer ndim, dims[GA_MAX_DIM], type; Logical have_data; Integer g_b; /* temporary global array (b = A') */ Integer num_blocks_a; void *a_ptr=NULL, *b_ptr=NULL; int local_sync_begin,local_sync_end; char *tempB = "A_transpose"; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); num_blocks_a = pnga_total_blocks(g_a); pnga_inquire(g_a, &type, &ndim, dims); if (type != C_DBL) pnga_error("ga_symmetrize: only implemented for double precision",0); if (num_blocks_a < 0) { if (dims[ndim-1] != dims[ndim-2]) pnga_error("ga_sym: can only sym square matrix", 0L); /* Find the local distribution */ pnga_distribution(g_a, me, alo, ahi); have_data = ahi[0]>0; for(i=1; i0; if(have_data) { pnga_access_ptr(g_a, alo, ahi, &a_ptr, lda); for(i=0; i #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #include "ga.h" #ifndef MSG_COMMS_MPI # include "tcgmsg.h" #else # include "mpi.h" #endif #include "ga-papi.h" #include "ga-wapi.h" static double tt0, tt1; static Integer *tlog, thandle; static Integer *indlog, ihandle, gahandle; static int *galog; static unsigned long current, MAX_EVENTS=0; static int ganum = 0; #define MAX_GAS 100 #define min(a,b) ((a)<(b) ? (a) : (b)) #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_timer = pnga_timer #endif double pnga_timer() { #ifdef MSG_COMMS_MPI return MPI_Wtime(); #else return tcg_time(); #endif } /* n is the max number of events to be traced */ void trace_init_(long *n) { MA_AccessIndex index; long err; if(*n<=0){ printf("trace_init>> invalid max number of events: %ld\n",*n); return; } current = 0; err = 0; /* MA_initialize(MT_INT,10000,10000); */ MAX_EVENTS = *n; if(!MA_push_get(MT_LONGINT, *n*2, "timeLog", &thandle, &index)){ printf("trace_init>> failed to allocate memory 1\n"); err ++; } MA_get_pointer(thandle, &tlog); if(!tlog){ printf("trace_init>> null pointer: 1\n"); err ++; } if(!MA_push_get(MT_LONGINT, *n*6, "indexLog", &ihandle, &index)){ printf("trace_init>> failed to allocate memory 2\n"); err ++; } MA_get_pointer(ihandle, &indlog); if(!indlog) { printf("trace_init>> null pointer: 2\n"); err ++; } if(!MA_push_get(MT_INT, MAX_GAS, "gaLog", &gahandle, &index)){ printf("trace_init>> failed to allocate memory 2\n"); err ++; } MA_get_pointer(gahandle, &galog); if(!galog) { printf("trace_init>> null pointer: 2\n"); err ++; } ganum = 0; if(err) MAX_EVENTS = 0; } void trace_stime_() { tt0 = pnga_timer(); } void trace_etime_() { tt1 = pnga_timer(); } void trace_genrec_(Integer *ga, Integer *ilo, Integer *ihi, Integer *jlo, Integer *jhi, Integer *op) { int i, d, has_record, counter; FILE *fout; char fname[15]; typedef struct { int lo[2]; int hi[2]; } patch_t; patch_t *regions; int *proclist; int ndim, dims[2], tmp_dims[2], lo[2], hi[2], type, block[2]; int me=GA_Nodeid(), nproc=GA_Nnodes(), proc; if(current >= MAX_EVENTS) return; /* only node 0 does the bookkeeping */ if(me == 0) { /* test if this ga has been recorded */ has_record = 0; for(i=0; i tmp_dims[d]) { block[d]++; tmp_dims[d] = regions[i].hi[d]; } } /* print the number of processed */ fprintf(fout, "%d\n", nproc); /* print dimensions */ for(d=0; d tmp_dims[d]) { counter++; if(counter == block[d]) break; fprintf(fout, "%d\n", regions[i].hi[d]+1); tmp_dims[d] = regions[i].hi[d]; } } } fclose(fout); free(regions); free(proclist); } } tlog[current*2] = (unsigned long)(tt0 * 1000000); tlog[current*2+1] = (unsigned long)(tt1 * 1000000); indlog[current*6] = *ga; indlog[current*6+1] = *ilo; indlog[current*6+2] = *ihi; indlog[current*6+3] = *jlo; indlog[current*6+4] = *jhi; indlog[current*6+5] = *op; current++; } void trace_end_(long *proc) { FILE *fout; char fname[10]; unsigned long i,k; sprintf(fname,"%03d",(int)*proc); fout=fopen(fname,"w"); for(i=0;i #endif #if HAVE_STRING_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_ASSERT_H # include #endif #include "global.h" #include "globalp.h" #include "base.h" #include "armci.h" #include "message.h" #include "macdecls.h" #include "ga-papi.h" #include "ga-wapi.h" /* from armcip.h, but armcip.h is private so we should not include it */ extern void armci_write_strided(void *ptr, int stride_levels, int stride_arr[], int count[], char *buf); extern void armci_read_strided(void *ptr, int stride_levels, int stride_arr[], int count[], char *buf); extern armci_hdl_t* get_armci_nbhandle(Integer *); #define USE_MALLOC 1 #define INVALID_MA_HANDLE -1 #define NEAR_INT(x) (x)< 0.0 ? ceil( (x) - 0.5) : floor((x) + 0.5) /*uncomment line below to verify consistency of MA in every sync */ /*#define CHECK_MA yes */ /***************************************************************************/ /*\ Return a pointer to the location indicated by subscript and and an array * of leading dimensions (ld). Assume that subscript refers to a set of local * coordinates relative to the origin of the array and account for the * presence of ghost cells. \*/ #define gam_LocationWithGhosts(proc, handle, subscript, ptr_loc, ld) \ { \ Integer _d, _factor = 1, _last=GA[handle].ndim - 1, _offset=0; \ Integer _lo[MAXDIM], _hi[MAXDIM]; \ ga_ownsM(handle, proc, _lo, _hi); \ if (_last == 0) ld[0] = _hi[0] - _lo[0] + 1 + 2*(Integer)GA[handle].width[0];\ for (_d = 0; _d < _last; _d++) { \ _offset += subscript[_d] * _factor; \ ld[_d] = _hi[_d] - _lo[_d] + 1 + 2*(Integer)GA[handle].width[_d]; \ _factor *= ld[_d]; \ } \ _offset += subscript[_last] * _factor; \ *(ptr_loc) = GA[handle].ptr[proc] + _offset*GA[handle].elemsize; \ } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_access_ghost_ptr = pnga_access_ghost_ptr #endif void pnga_access_ghost_ptr(Integer g_a, Integer dims[], void* ptr, Integer ld[]) { char *lptr; Integer handle = GA_OFFSET + g_a; Integer i, lo[MAXDIM], hi[MAXDIM]; Integer ndim = GA[handle].ndim; Integer me = pnga_nodeid(); pnga_distribution(g_a, me, lo, hi); for (i=0; i < ndim; i++) { dims[i] = 0; } gam_LocationWithGhosts(me, handle, dims, &lptr, ld); *(char**)ptr = lptr; for (i=0; i < ndim; i++) dims[i] = hi[i] - lo[i] + 1 + 2*(Integer)GA[handle].width[i]; } /*\ PROVIDE INDEX TO LOCALLY HELD DATA, ACCOUNTING FOR * PRESENCE OF GHOST CELLS \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_access_ghost_element = pnga_access_ghost_element #endif void pnga_access_ghost_element(Integer g_a, AccessIndex* index, Integer subscript[], Integer ld[]) { char *ptr=NULL; Integer handle = GA_OFFSET + g_a; Integer i=0; Integer tmp_sub[MAXDIM]; unsigned long elemsize=0; unsigned long lref=0, lptr=0; Integer me = pnga_nodeid(); /* Indices conform to Fortran convention. Shift them down 1 so that gam_LocationWithGhosts works. */ for (i=0; i0) me = PGRP_LIST[grp_id].map_proc_list[me]; ndim = GA[handle].ndim; /* Figure out whether or not lo and hi can be accessed completely from local data */ pnga_distribution(g_a, me, glo, ghi); ichk = 1; for (i=0; i ghi[i]+(Integer)GA[handle].width[i]) ichk = 0; llo[i] = glo[i] - (Integer)GA[handle].width[i]; if (i 0) { imax = 2; } else { imax = 1; } } else { imax = 1; } } else { lo_rem[i] = lo_loc[i]; hi_rem[i] = hi_loc[i]; } } for (inx = 0; inx < imax; inx++) { /* Check to see if boundary is being updated in one patch or two, adjust lower boundary accordingly. */ for (i=0; i= slo_rem[i]) { offset = tlo_rem[i] - lo_rem[i]; slice = thi_rem[i] - tlo_rem[i]; } else { offset = 0; slice = thi_rem[i] - slo_rem[i]; } if (offset < 0) offset = offset + dims[i]; if (offset >= dims[i]) offset = offset - dims[i]; plo_rem[i] = thi_rem[i] - tlo_rem[i] + width[i] - slice; phi_rem[i] = thi_rem[i] - tlo_rem[i] + width[i]; plo_loc[i] = offset; /*phi_loc[i] = offset + slice;*/ } } else { plo_rem[i] = width[i]; phi_rem[i] = thi_rem[i] - tlo_rem[i] + width[i]; plo_loc[i] = width[i]; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + width[i];*/ } } else { plo_rem[i] = 0; phi_rem[i] = thi_rem[i] - tlo_rem[i] + increment[i]; plo_loc[i] = 0; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + increment[i];*/ } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_loc, &ptr_loc, ld_loc); gam_LocationWithGhosts(proc_rem, handle, plo_rem, &ptr_rem, ld_rem); /* Evaluate strides on local and remote processors */ gam_setstride(ndim, size, ld_loc, ld_rem, stride_rem, stride_loc); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_rem, phi_rem, count); count[0] *= size; /* get remote data */ if (p_handle >= 0) { proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem]; } ARMCI_GetS(ptr_rem, stride_rem, ptr_loc, stride_loc, count, (int)(ndim - 1), (int)proc_rem); } } /* Perform update in positive direction. Start by getting rough estimate of block of needed data*/ for (i = 0; i < ndim; i++) { if (i == idx) { lo_rem[i] = hi_loc[i] + 1; hi_rem[i] = hi_loc[i] + nwidth; /* Check to see if we will need to update ghost cells using one or two major patches of the global array. */ if (hi_rem[i] > dims[i]) { if (lo_rem[i] <= dims[i]) { imax = 2; } else { imax = 1; } } else { imax = 1; } } else { lo_rem[i] = lo_loc[i]; hi_rem[i] = hi_loc[i]; } } for (inx = 0; inx < imax; inx++) { /* Check to see if boundary is being updated in one patch or two, adjust lower boundary accordingly. */ for (i=0; i dims[i]) { slo_rem[i] = 1; shi_rem[i] = nwidth; } else { slo_rem[i] = lo_rem[i]; shi_rem[i] = hi_rem[i]; } } else { slo_rem[i] = lo_rem[i]; shi_rem[i] = hi_rem[i]; } } /* locate processor with this data */ if (!pnga_locate_region(g_a, slo_rem, shi_rem, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), slo_rem, shi_rem, g_a); for (ipx = 0; ipx < np; ipx++) { /* Get actual coordinates of desired chunk of remote data as well as the actual coordinates of the local chunk of data that will receive the remote data (these coordinates take into account the presence of ghost cells). Start by finding out what data is actually held by remote processor. */ proc_rem = _ga_proclist[ipx]; pnga_distribution(g_a, proc_rem, tlo_rem, thi_rem); for (i = 0; i < ndim; i++) { if (increment[i] == 0) { if (i == idx) { if (np == 1 && imax == 1) { plo_rem[i] = width[i]; phi_rem[i] = 2*width[i] - 1; plo_loc[i] = hi_loc[i] - lo_loc[i] + 1 + width[i]; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + 2*width[i];*/ } else { offset = tlo_rem[i] - hi_loc[i] - 1; if (thi_rem[i] <= shi_rem[i]) { slice = thi_rem[i] - tlo_rem[i]; } else { slice = shi_rem[i] - tlo_rem[i]; } if (offset < 0) offset = offset + dims[i]; if (offset >= dims[i]) offset = offset - dims[i]; plo_rem[i] = width[i]; phi_rem[i] = width[i] + slice; plo_loc[i] = hi_loc[i] - lo_loc[i] + width[i] + 1 + offset; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + width[i] + 1 + offset + slice;*/ } } else { plo_rem[i] = width[i]; phi_rem[i] = thi_rem[i] - tlo_rem[i] + width[i]; plo_loc[i] = width[i]; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + width[i];*/ } } else { plo_rem[i] = 0; phi_rem[i] = thi_rem[i] - tlo_rem[i] + increment[i]; plo_loc[i] = 0; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + increment[i];*/ } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_loc, &ptr_loc, ld_loc); gam_LocationWithGhosts(proc_rem, handle, plo_rem, &ptr_rem, ld_rem); /* Evaluate strides on local and remote processors */ gam_setstride(ndim, size, ld_loc, ld_rem, stride_rem, stride_loc); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_rem, phi_rem, count); count[0] *= size; /* get remote data */ if (p_handle >= 0) { proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem]; } ARMCI_GetS(ptr_rem, stride_rem, ptr_loc, stride_loc, count, (int)(ndim - 1), (int)proc_rem); } } } /* synchronize all processors and update increment array */ if (idx < ndim-1) pnga_pgroup_sync(p_handle); if (corner_flag) increment[idx] = 2*nwidth; } free(_ga_map); free(_ga_proclist); } /*\ UTILITY FUNCTION TO MAKE SURE GHOST CELLS WIDTHS ARE * LESS THAN VISIBLE DATA WIDTHS \*/ static logical gai_check_ghost_distr(Integer g_a) { Integer handle=GA_OFFSET + g_a; Integer idx, ndim, np, ipx; ndim = GA[handle].ndim; ipx = 0; for (idx = 0; idx < ndim; idx++) { for (np = 0; np < GA[handle].nblock[idx]; np++) { if (np < GA[handle].nblock[idx] - 1) { if (GA[handle].mapc[ipx+1]-GA[handle].mapc[ipx]+1 1) mask0 = TRUE; */ if (mask0) continue; /* Now that mask has been determined, find data that is to be moved * and identify processor to which it is going. Wrap boundaries * around, if necessary */ for (idx = 0; idx < ndim; idx++) { if (mask[idx] == 0) { /*tlo_loc[idx] = lo_loc[idx];*/ /*thi_loc[idx] = hi_loc[idx];*/ tlo_rem[idx] = lo_loc[idx]; thi_rem[idx] = hi_loc[idx]; } else if (mask[idx] == -1) { /*tlo_loc[idx] = lo_loc[idx];*/ /*thi_loc[idx] = lo_loc[idx]+width[idx]-1;*/ if (lo_loc[idx] > 1) { tlo_rem[idx] = lo_loc[idx]-width[idx]; thi_rem[idx] = lo_loc[idx]-1; } else { tlo_rem[idx] = dims[idx]-width[idx]+1; thi_rem[idx] = dims[idx]; } } else if (mask[idx] == 1) { /*tlo_loc[idx] = hi_loc[idx]-width[idx]+1;*/ /*thi_loc[idx] = hi_loc[idx];*/ if (hi_loc[idx] < dims[idx]) { tlo_rem[idx] = hi_loc[idx] + 1; thi_rem[idx] = hi_loc[idx] + width[idx]; } else { tlo_rem[idx] = 1; thi_rem[idx] = width[idx]; } } else { fprintf(stderr,"Illegal mask value found\n"); } } /* Locate remote processor to which data must be sent */ if (!pnga_locate_region(g_a, tlo_rem, thi_rem, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), tlo_rem, thi_rem, g_a); if (np > 1) { fprintf(stderr,"More than one remote processor found\n"); } /* Remote processor has been identified, now get ready to send data to it. Start by getting distribution on remote processor.*/ proc_rem = _ga_proclist[0]; pnga_distribution(g_a, proc_rem, tlo_rem, thi_rem); for (idx = 0; idx < ndim; idx++) { if (mask[idx] == 0) { plo_loc[idx] = width[idx]; phi_loc[idx] = hi_loc[idx]-lo_loc[idx]+width[idx]; plo_rem[idx] = plo_loc[idx]; } else if (mask[idx] == -1) { plo_loc[idx] = width[idx]; phi_loc[idx] = 2*width[idx]-1; plo_rem[idx] = thi_rem[idx]-tlo_rem[idx]+width[idx]+1; } else if (mask[idx] == 1) { plo_loc[idx] = hi_loc[idx]-lo_loc[idx]+1; phi_loc[idx] = hi_loc[idx]-lo_loc[idx]+width[idx]; plo_rem[idx] = 0; } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_loc, &ptr_loc, ld_loc); gam_LocationWithGhosts(proc_rem, handle, plo_rem, &ptr_rem, ld_rem); /* Evaluate strides on local and remote processors */ gam_setstride(ndim, size, ld_loc, ld_rem, stride_rem, stride_loc); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_loc, phi_loc, count); count[0] *= size; /* put data on remote processor */ /*ARMCI_PutS(ptr_loc, stride_loc, ptr_rem, stride_rem, count, (int)(ndim - 1), (int)proc_rem);*/ if (p_handle >= 0) { proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem]; } ARMCI_NbPutS(ptr_loc, stride_loc, ptr_rem, stride_rem, count, (int)(ndim - 1), (int)proc_rem, NULL); } ARMCI_WaitAll(); free(_ga_map); free(_ga_proclist); return TRUE; } /*\ GET INDICES ON REMOTE BLOCK IN NEGATIVE DIRECTION FOR UPDATE \*/ static void get_remote_block_neg(Integer idx, Integer ndim, Integer *lo_loc, Integer *hi_loc, Integer *slo_rem, Integer *shi_rem, Integer *dims, Integer *width) { Integer i, lo_rem[MAXDIM], hi_rem[MAXDIM]; /*Start by getting rough idea of where data needs to go. */ for (i = 0; i < ndim; i++) { if (i == idx) { lo_rem[i] = lo_loc[i] - width[i]; hi_rem[i] = lo_loc[i] - 1; } else { lo_rem[i] = lo_loc[i]; hi_rem[i] = hi_loc[i]; } } /* Account for boundaries, if necessary. */ for (i=0; i dims[i]) { slo_rem[i] = 1; shi_rem[i] = width[i]; } else { slo_rem[i] = lo_rem[i]; shi_rem[i] = hi_rem[i]; } } else { slo_rem[i] = lo_rem[i]; shi_rem[i] = hi_rem[i]; } } } /*\ UPDATE GHOST CELLS OF GLOBAL ARRAY USING SHIFT ALGORITHM AND PUT CALLS \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_update3_ghosts = pnga_update3_ghosts #endif logical pnga_update3_ghosts(Integer g_a) { Integer idx, i, np, handle=GA_OFFSET + g_a, proc_rem; Integer size, ndim, nwidth, increment[MAXDIM]; Integer width[MAXDIM]; Integer dims[MAXDIM]; Integer lo_loc[MAXDIM], hi_loc[MAXDIM]; Integer plo_loc[MAXDIM]/*, phi_loc[MAXDIM]*/; Integer tlo_rem[MAXDIM], thi_rem[MAXDIM]; Integer slo_rem[MAXDIM], shi_rem[MAXDIM]; Integer plo_rem[MAXDIM], phi_rem[MAXDIM]; Integer ld_loc[MAXDIM], ld_rem[MAXDIM]; int stride_loc[MAXDIM], stride_rem[MAXDIM],count[MAXDIM]; char *ptr_loc, *ptr_rem; Integer me = pnga_nodeid(); Integer p_handle; Integer *_ga_map = NULL; Integer *_ga_proclist = NULL; /* This routine makes use of the shift algorithm to update data in the * ghost cells bounding the local block of visible data. The shift * algorithm starts by updating the blocks of data along the first * dimension by grabbing a block of data that is width[0] deep but * otherwise matches the dimensions of the data residing on the * calling processor. The update of the second dimension, however, * grabs a block that is width[1] deep in the second dimension but is * ldim0 + 2*width[0] in the first dimensions where ldim0 is the * size of the visible data along the first dimension. The remaining * dimensions are left the same. For the next update, the width of the * second dimension is also increased by 2*width[1] and so on. This * algorith makes use of the fact that data for the dimensions that * have already been updated is available on each processor and can be * used in the updates of subsequent dimensions. The total number of * separate updates is 2*ndim, an update in the negative and positive * directions for each dimension. This implementation uses simple put * operations to perform the updates along each dimension with an * intervening synchronization call being used to make sure that the * necessary data is available on each processor before starting the * update along the next dimension. * * To perform the update, this routine makes use of several copies of * indices marking the upper and lower limits of data. Indices * beginning with the character "p" are relative indices marking the * location of the data set relative to the origin the local patch of * the global array, all other indices are in absolute coordinates and * mark locations in the total global array. The indices used by this * routine are described below. * * lo_loc[], hi_loc[]: The lower and upper indices of the visible * block of data held by the calling processor. * * lo_rem[], hi_rem[]: The lower and upper indices of the block * of data on a remote processor or processors that is needed to * fill in the calling processors ghost cells. These indices are * NOT corrected for wrap-around (periodic) boundary conditions * so they can be negative or greater than the array dimension * values held in dims[]. * * slo_rem[], shi_rem[]: Similar to lo_rem[] and hi_rem[], except * that these indices have been corrected for wrap-around * boundary conditions. * * thi_rem[], thi_rem[]: The lower and upper indices of the visible * data on a remote processor. * * plo_loc[], phi_loc[]: The indices of the local data patch that * is going to be updated. * * plo_rem[], phi_rem[]: The indices of the data patch on the * remote processor that will be used to update the data on the * calling processor. Note that the dimensions of the patches * represented by plo_loc[], plo_rem[] and plo_loc[], phi_loc[] * must be the same. */ /* if global array has no ghost cells, just return */ if (!pnga_has_ghosts(g_a)) return TRUE; size = GA[handle].elemsize; ndim = GA[handle].ndim; p_handle = GA[handle].p_handle; /* obtain range of data that is held by local processor */ pnga_distribution(g_a,me,lo_loc,hi_loc); /* initialize range increments and get array dimensions */ for (idx=0; idx < ndim; idx++) { increment[idx] = 0; width[idx] = (Integer)GA[handle].width[idx]; dims[idx] = (Integer)GA[handle].dims[idx]; if (lo_loc[idx] == 0 && hi_loc[idx] == -1) return FALSE; } /* Check to make sure that global array is well-behaved (all processors have data and the width of the data in each dimension is greater than the corresponding value in width[]. */ if (!gai_check_ghost_distr(g_a)) return FALSE; _ga_map = malloc((GAnproc*2*MAXDIM+1)*sizeof(Integer)); if(!_ga_map) pnga_error("pnga_update3_ghosts:malloc failed (_ga_map)",0); _ga_proclist = malloc(GAnproc*sizeof(Integer)); if(!_ga_proclist) pnga_error("pnga_update3_ghosts:malloc failed (_ga_proclist)",0); /* Get pointer to local memory */ ptr_loc = GA[handle].ptr[me]; /* loop over dimensions for sequential update using shift algorithm */ for (idx=0; idx < ndim; idx++) { nwidth = width[idx]; /* Do not bother with update if nwidth is zero */ if (nwidth != 0) { /* Perform update in negative direction. */ get_remote_block_neg(idx, ndim, lo_loc, hi_loc, slo_rem, shi_rem, dims, width); /* locate processor with this data */ if (!pnga_locate_region(g_a, slo_rem, shi_rem, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), slo_rem, shi_rem, g_a); /* Get actual coordinates of desired location of remote data as well as the actual coordinates of the local chunk of data that will be sent to remote processor (these coordinates take into account the presence of ghost cells). Start by finding out what data is actually held by remote processor. */ proc_rem = _ga_proclist[0]; pnga_distribution(g_a, proc_rem, tlo_rem, thi_rem); for (i = 0; i < ndim; i++) { if (increment[i] == 0) { if (i == idx) { plo_rem[i] = thi_rem[i] - tlo_rem[i] + width[i] + 1; phi_rem[i] = thi_rem[i] - tlo_rem[i] + 2*width[i]; plo_loc[i] = width[i]; /*phi_loc[i] = 2*width[i] - 1;*/ } else { plo_rem[i] = width[i]; phi_rem[i] = thi_rem[i] - tlo_rem[i] + width[i]; plo_loc[i] = width[i]; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + width[i];*/ } } else { plo_rem[i] = 0; phi_rem[i] = thi_rem[i] - tlo_rem[i] + increment[i]; plo_loc[i] = 0; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + increment[i];*/ } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_loc, &ptr_loc, ld_loc); gam_LocationWithGhosts(proc_rem, handle, plo_rem, &ptr_rem, ld_rem); /* Evaluate strides on local and remote processors */ gam_setstride(ndim, size, ld_loc, ld_rem, stride_rem, stride_loc); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_rem, phi_rem, count); count[0] *= size; /* Put local data on remote processor */ if (p_handle >= 0) { proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem]; } ARMCI_PutS(ptr_loc, stride_loc, ptr_rem, stride_rem, count, (int)(ndim - 1), (int)proc_rem); /* Perform update in positive direction */ get_remote_block_pos(idx, ndim, lo_loc, hi_loc, slo_rem, shi_rem, dims, width); /* locate processor with this data */ if (!pnga_locate_region(g_a, slo_rem, shi_rem, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), slo_rem, shi_rem, g_a); /* Get actual coordinates of desired chunk of remote data as well as the actual coordinates of the local chunk of data that will receive the remote data (these coordinates take into account the presence of ghost cells). Start by finding out what data is actually held by remote processor. */ proc_rem = _ga_proclist[0]; pnga_distribution(g_a, proc_rem, tlo_rem, thi_rem); for (i = 0; i < ndim; i++) { if (increment[i] == 0) { if (i == idx) { plo_rem[i] = 0; phi_rem[i] = width[i] - 1; plo_loc[i] = hi_loc[i] - lo_loc[i] + width[i] - 1; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + 2*width[i] - 1;*/ } else { plo_rem[i] = width[i]; phi_rem[i] = thi_rem[i] - tlo_rem[i] + width[i]; plo_loc[i] = width[i]; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + width[i];*/ } } else { plo_rem[i] = 0; phi_rem[i] = thi_rem[i] - tlo_rem[i] + increment[i]; plo_loc[i] = 0; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + increment[i];*/ } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_loc, &ptr_loc, ld_loc); gam_LocationWithGhosts(proc_rem, handle, plo_rem, &ptr_rem, ld_rem); /* Evaluate strides on local and remote processors */ gam_setstride(ndim, size, ld_loc, ld_rem, stride_rem, stride_loc); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_rem, phi_rem, count); count[0] *= size; /* get remote data */ if (p_handle >= 0) { proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem]; } ARMCI_PutS(ptr_loc, stride_loc, ptr_rem, stride_rem, count, (int)(ndim - 1), (int)proc_rem); } /* synchronize all processors and update increment array */ if (idx < ndim-1) pnga_pgroup_sync(p_handle); increment[idx] = 2*nwidth; } free(_ga_map); free(_ga_proclist); return TRUE; } #define GHOST_PRINT 0 /*\ UPDATE GHOST CELLS OF GLOBAL ARRAY USING SHIFT ALGORITHM AND * MESSAGE PASSING \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_set_update4_info = pnga_set_update4_info #endif logical pnga_set_update4_info(Integer g_a) { Integer idx, idir, i, np, handle=GA_OFFSET + g_a; Integer size, buflen, buftot, *bufsize, ndim, increment[MAXDIM]; Integer *proc_rem_snd, *proc_rem_rcv; Integer *length; Integer width[MAXDIM], dims[MAXDIM], index[MAXDIM]; Integer lo_loc[MAXDIM], hi_loc[MAXDIM]; Integer plo_snd[MAXDIM], phi_snd[MAXDIM]; Integer lo_rcv[MAXDIM], hi_rcv[MAXDIM]; Integer slo_rcv[MAXDIM], shi_rcv[MAXDIM]; Integer plo_rcv[MAXDIM], phi_rcv[MAXDIM]; Integer ld_loc[MAXDIM]; int *stride_snd, *stride_rcv, *count, cache_size; /*int corner_flag;*/ char **ptr_snd, **ptr_rcv, *cache; char *current; Integer me = pnga_nodeid(); Integer p_handle; Integer *_ga_map = NULL; Integer *_ga_proclist = NULL; /* This routine sets the arrays that are used to transfer data using * the update4. To perform the update, this routine makes use of several * copies of indices marking the upper and lower limits of data. Indices * beginning with the character "p" are relative indices marking the * location of the data set relative to the origin the local patch of * the global array, all other indices are in absolute coordinates and * mark locations in the total global array. The indices used by this * routine are described below. * * lo_loc[], hi_loc[]: The lower and upper indices of the visible * block of data held by the calling processor. * * lo_rcv[], hi_rcv[]: The lower and upper indices of the blocks * of data that will be either sent to or received from a remote * processor. These indices are NOT corrected for wrap-around * (periodic) boundary conditions so they can be negative or greater * than the array dimension values held in dims[]. * * slo_rcv[], shi_rcv[]: Similar to lo_rcv[] and hi_rcv[], except * that these indices have been corrected for wrap-around * boundary conditions. * * plo_rcv[], phi_rcv[]: The local indices of the local data patch * that receive that message from the remote processor. * * plo_snd[], phi_snd[]: The local indices of the data patch * that will be sent to the remote processor. Note that the * dimensions of the patches represented by plo_rec[], plo_rec[] and * plo_snd[], phi_snd[] must be the same. */ /* if global array has no ghost cells, just return */ if (!pnga_has_ghosts(g_a)) return TRUE; /* Check to make sure that global array is well-behaved (all processors have data and the width of the data in each dimension is greater than the corresponding value in width[]. */ if (!gai_check_ghost_distr(g_a)) return FALSE; size = GA[handle].elemsize; ndim = GA[handle].ndim; p_handle = GA[handle].p_handle; cache_size = 2*sizeof(char *)+3*ndim*sizeof(int)+3*sizeof(Integer); cache_size = 2* ndim *((cache_size/8) + 1) + 1; GA[handle].cache = (double *)malloc(sizeof(double)*cache_size); cache = (char *)GA[handle].cache; /*corner_flag = GA[handle].corner_flag;*/ #if GHOST_PRINT printf("p[%d]a cache_size: %d\n",GAme,cache_size); #endif /* initialize range increments and get array dimensions */ pnga_distribution(g_a,me,lo_loc,hi_loc); for (idx=0; idx < ndim; idx++) { increment[idx] = 0; width[idx] = (Integer)GA[handle].width[idx]; dims[idx] = (Integer)GA[handle].dims[idx]; if (lo_loc[idx] == 0 && hi_loc[idx] == -1) { *(char**)cache = NULL; return FALSE; } } _ga_map = malloc((GAnproc*2*MAXDIM+1)*sizeof(Integer)); if(!_ga_map) pnga_error("pnga_set_update4_info:malloc failed (_ga_map)",0); _ga_proclist = malloc(GAnproc*sizeof(Integer)); if(!_ga_proclist) pnga_error("pnga_set_update4_info:malloc failed (_ga_proclist)",0); /* Get indices of processor in virtual grid */ pnga_proc_topology(g_a, me, index); /* Try to find maximum size of message that will be sent during * update operations and use this to allocate memory for message * passing buffers. */ buftot = 1; for (i=0; i= 0) { *proc_rem_snd = PGRP_LIST[p_handle].inv_map_proc_list[*proc_rem_snd]; } /* Find processor from which data will be recieved */ for (i = 0; i < ndim; i++) { if (i == idx) { lo_rcv[i] = hi_loc[i] + 1; hi_rcv[i] = hi_loc[i] + width[i]; } else { lo_rcv[i] = lo_loc[i]; hi_rcv[i] = hi_loc[i]; } } /* Account for boundaries, if necessary. */ for (i=0; i dims[i]) { slo_rcv[i] = 1; shi_rcv[i] = width[i]; } else { slo_rcv[i] = lo_rcv[i]; shi_rcv[i] = hi_rcv[i]; } } else { slo_rcv[i] = lo_rcv[i]; shi_rcv[i] = hi_rcv[i]; } } /* locate processor with this data */ if (!pnga_locate_region(g_a, slo_rcv, shi_rcv, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), slo_rcv, shi_rcv, g_a); *proc_rem_rcv = _ga_proclist[0]; if (p_handle >= 0) { *proc_rem_rcv = PGRP_LIST[p_handle].inv_map_proc_list[*proc_rem_rcv]; } /* Get actual coordinates of chunk of data that will be sent to * remote processor as well as coordinates of the array space that * will receive data from remote processor. */ for (i = 0; i < ndim; i++) { if (increment[i] == 0) { if (i == idx) { plo_snd[i] = width[i]; phi_snd[i] = 2*width[i] - 1; plo_rcv[i] = hi_loc[i] - lo_loc[i] + width[i] + 1; phi_rcv[i] = hi_loc[i] - lo_loc[i] + 2*width[i]; } else { plo_snd[i] = width[i]; phi_snd[i] = hi_loc[i] - lo_loc[i] + width[i]; plo_rcv[i] = width[i]; phi_rcv[i] = hi_loc[i] - lo_loc[i] + width[i]; } } else { plo_rcv[i] = 0; phi_rcv[i] = hi_loc[i] - lo_loc[i] + increment[i]; plo_snd[i] = 0; phi_snd[i] = hi_loc[i] - lo_loc[i] + increment[i]; } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_snd, ptr_snd, ld_loc); #if GHOST_PRINT printf("p[%d]a 1: plo_snd[0]: %d plo_snd[1]: %d ptr_snd: %d\n", GAme, plo_snd[0], plo_snd[1], (Integer)*ptr_snd); fflush(stdout); #endif gam_LocationWithGhosts(me, handle, plo_rcv, ptr_rcv, ld_loc); #if GHOST_PRINT printf("p[%d]a 1: plo_rcv[0]: %d plo_rcv[1]: %d ptr_rcv: %d\n", GAme, plo_rcv[0], plo_rcv[1], (Integer)*ptr_rcv); fflush(stdout); #endif /* Evaluate strides for send and recieve */ gam_setstride(ndim, size, ld_loc, ld_loc, stride_rcv, stride_snd); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_rcv, phi_rcv, count); gam_CountElems(ndim, plo_snd, phi_snd, length); *length *= size; count[0] *= size; #if GHOST_PRINT printf("p[%d]a 1: length: %d bufsize: %d proc_rem_snd: %d proc_rem_rcv: %d\n", GAme, *length, *bufsize, (int)*proc_rem_snd, (int)*proc_rem_rcv); printf("p[%d]a 1: count[0]: %d stride_rcv[0]: %d stride_rcv[1]: %d\n", GAme, count[0], stride_rcv[0],stride_rcv[1]); printf("p[%d]a 1: count[1]: %d stride_rcv[2]: %d stride_rcv[3]: %d\n", GAme, count[1], stride_rcv[2],stride_rcv[3]); printf("p[%d]a 1: count[2]: %d stride_snd[0]: %d stride_snd[1]: %d\n", GAme, count[2], stride_snd[0],stride_snd[1]); printf("p[%d]a 1: count[3]: %d stride_snd[2]: %d stride_snd[3]: %d\n", GAme, count[3], stride_snd[2],stride_snd[3]); fflush(stdout); #endif ptr_snd = (char**)current; ptr_rcv = (char**)(ptr_snd+1); proc_rem_snd = (Integer*)(ptr_rcv+1); proc_rem_rcv = (Integer*)(proc_rem_snd+1); stride_snd = (int*)(proc_rem_rcv+1); stride_rcv = (int*)(stride_snd+ndim); length = (Integer*)(stride_rcv+ndim); count = (int*)(length+1); current = (char*)(count+ndim); /* Find parameters for message in positive direction. */ get_remote_block_pos(idx, ndim, lo_loc, hi_loc, slo_rcv, shi_rcv, dims, width); /* locate processor with this data */ if (!pnga_locate_region(g_a, slo_rcv, shi_rcv, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), slo_rcv, shi_rcv, g_a); *proc_rem_snd = _ga_proclist[0]; if (p_handle >= 0) { *proc_rem_snd = PGRP_LIST[p_handle].inv_map_proc_list[*proc_rem_snd]; } /* Find processor from which data will be recieved */ for (i = 0; i < ndim; i++) { if (i == idx) { lo_rcv[i] = lo_loc[i] - width[i]; hi_rcv[i] = lo_loc[i] - 1; } else { lo_rcv[i] = lo_loc[i]; hi_rcv[i] = hi_loc[i]; } } /* Account for boundaries, if necessary. */ for (i=0; i= 0) { *proc_rem_rcv = PGRP_LIST[p_handle].inv_map_proc_list[*proc_rem_rcv]; } /* Get actual coordinates of chunk of data that will be sent to * remote processor as well as coordinates of the array space that * will receive data from remote processor. */ for (i = 0; i < ndim; i++) { if (increment[i] == 0) { if (i == idx) { plo_snd[i] = hi_loc[i] - lo_loc[i] + 1; phi_snd[i] = hi_loc[i] - lo_loc[i] + width[i]; plo_rcv[i] = 0; phi_rcv[i] = width[i] - 1; } else { plo_snd[i] = width[i]; phi_snd[i] = hi_loc[i] - lo_loc[i] + width[i]; plo_rcv[i] = width[i]; phi_rcv[i] = hi_loc[i] - lo_loc[i] + width[i]; } } else { plo_rcv[i] = 0; phi_rcv[i] = hi_loc[i] - lo_loc[i] + increment[i]; plo_snd[i] = 0; phi_snd[i] = hi_loc[i] - lo_loc[i] + increment[i]; } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_snd, ptr_snd, ld_loc); #if GHOST_PRINT printf("p[%d]a 2: plo_snd[0]: %d plo_snd[1]: %d ptr_snd: %d\n", GAme, plo_snd[0], plo_snd[1], (Integer)*ptr_snd); fflush(stdout); #endif gam_LocationWithGhosts(me, handle, plo_rcv, ptr_rcv, ld_loc); #if GHOST_PRINT printf("p[%d]a 2: plo_rcv[0]: %d plo_rcv[1]: %d ptr_rcv: %d\n", GAme, plo_rcv[0], plo_rcv[1], (Integer)*ptr_rcv); fflush(stdout); #endif /* Evaluate strides for send and recieve */ gam_setstride(ndim, size, ld_loc, ld_loc, stride_rcv, stride_snd); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_rcv, phi_rcv, count); gam_CountElems(ndim, plo_snd, phi_snd, length); *length *= size; count[0] *= size; #if GHOST_PRINT printf("p[%d]a 2: length: %d bufsize: %d proc_rem_snd: %d proc_rem_rcv: %d\n", GAme, *length, *bufsize, (int)*proc_rem_snd, (int)*proc_rem_rcv); printf("p[%d]a 2: count[0]: %d stride_rcv[0]: %d stride_rcv[1]: %d\n", GAme, count[0], stride_rcv[0],stride_rcv[1]); printf("p[%d]a 2: count[1]: %d stride_rcv[2]: %d stride_rcv[3]: %d\n", GAme, count[1], stride_rcv[2],stride_rcv[3]); printf("p[%d]a 2: count[2]: %d stride_snd[0]: %d stride_snd[1]: %d\n", GAme, count[2], stride_snd[0],stride_snd[1]); printf("p[%d]a 2: count[3]: %d stride_snd[2]: %d stride_snd[3]: %d\n", GAme, count[3], stride_snd[2],stride_snd[3]); fflush(stdout); #endif } if (GA[handle].corner_flag) increment[idx] = 2*width[idx]; } #if GHOST_PRINT printf("p[%d]a final pointer: %d\n",GAme,(Integer)(Integer*)current); fflush(stdout); #endif free(_ga_map); free(_ga_proclist); return TRUE; } /*\ UPDATE GHOST CELLS OF GLOBAL ARRAY USING SHIFT ALGORITHM AND * MESSAGE PASSING \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_update4_ghosts = pnga_update4_ghosts #endif logical pnga_update4_ghosts(Integer g_a) { Integer idx, i, handle=GA_OFFSET + g_a; Integer *size, bufsize, buflen, ndim, elemsize; Integer *proc_rem_snd, *proc_rem_rcv, pmax; Integer msgcnt, *length; Integer index[MAXDIM], width[MAXDIM]; int *stride_snd, *stride_rcv, *count, msglen; char **ptr_snd, **ptr_rcv, *cache, *current; char send_name[32], rcv_name[32]; void *snd_ptr, *rcv_ptr, *snd_ptr_orig, *rcv_ptr_orig; Integer me = pnga_nodeid(); /*Integer p_handle;*/ /* This routine makes use of the shift algorithm to update data in the * ghost cells bounding the local block of visible data. The shift * algorithm starts by updating the blocks of data along the first * dimension by grabbing a block of data that is width[0] deep but * otherwise matches the dimensions of the data residing on the * calling processor. The update of the second dimension, however, * grabs a block that is width[1] deep in the second dimension but is * ldim0 + 2*width[0] in the first dimensions where ldim0 is the * size of the visible data along the first dimension. The remaining * dimensions are left the same. For the next update, the width of the * second dimension is also increased by 2*width[1] and so on. This * algorith makes use of the fact that data for the dimensions that * have already been updated is available on each processor and can be * used in the updates of subsequent dimensions. The total number of * separate updates is 2*ndim, an update in the negative and positive * directions for each dimension. * * This implementation make use of explicit message passing to perform * the update. Separate message types for the updates in each coordinate * direction are used to maintain synchronization locally and to * guarantee that the data is present before the updates in a new * coordinate direction take place. */ /* if global array has no ghost cells, just return */ if (!pnga_has_ghosts(g_a)) return TRUE; ndim = GA[handle].ndim; /*p_handle = GA[handle].p_handle;*/ cache = (char *)GA[handle].cache; elemsize = GA[handle].elemsize; for (i=0; i= 0) { proc_rem_snd = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem_snd]; } /* Find processor from which data will be recieved */ for (i = 0; i < ndim; i++) { if (i == idx) { lo_rcv[i] = hi_loc[i] + 1; hi_rcv[i] = hi_loc[i] + width[i]; } else { lo_rcv[i] = lo_loc[i]; hi_rcv[i] = hi_loc[i]; } } /* Account for boundaries, if necessary. */ for (i=0; i dims[i]) { slo_rcv[i] = 1; shi_rcv[i] = width[i]; } else { slo_rcv[i] = lo_rcv[i]; shi_rcv[i] = hi_rcv[i]; } } else { slo_rcv[i] = lo_rcv[i]; shi_rcv[i] = hi_rcv[i]; } } /* locate processor with this data */ if (!pnga_locate_region(g_a, slo_rcv, shi_rcv, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), slo_rcv, shi_rcv, g_a); proc_rem_rcv = _ga_proclist[0]; if (p_handle >= 0) { proc_rem_rcv = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem_rcv]; } /* Get actual coordinates of chunk of data that will be sent to * remote processor as well as coordinates of the array space that * will receive data from remote processor. */ for (i = 0; i < ndim; i++) { if (increment[i] == 0) { if (i == idx) { plo_snd[i] = width[i]; phi_snd[i] = 2*width[i] - 1; plo_rcv[i] = hi_loc[i] - lo_loc[i] + width[i] + 1; phi_rcv[i] = hi_loc[i] - lo_loc[i] + 2*width[i]; } else { plo_snd[i] = width[i]; phi_snd[i] = hi_loc[i] - lo_loc[i] + width[i]; plo_rcv[i] = width[i]; phi_rcv[i] = hi_loc[i] - lo_loc[i] + width[i]; } } else { plo_rcv[i] = 0; phi_rcv[i] = hi_loc[i] - lo_loc[i] + increment[i]; plo_snd[i] = 0; phi_snd[i] = hi_loc[i] - lo_loc[i] + increment[i]; } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_snd, &ptr_snd, ld_loc); #if GHOST_PRINT printf("p[%d] 1: plo_snd[0]: %d plo_snd[1]: %d ptr_snd: %d\n", GAme, plo_snd[0], plo_snd[1], (Integer)ptr_snd); fflush(stdout); #endif gam_LocationWithGhosts(me, handle, plo_rcv, &ptr_rcv, ld_loc); #if GHOST_PRINT printf("p[%d] 1: plo_rcv[0]: %d plo_rcv[1]: %d ptr_rcv: %d\n", GAme, plo_rcv[0], plo_rcv[1], (Integer)ptr_rcv); fflush(stdout); #endif /* Evaluate strides for send and recieve */ gam_setstride(ndim, size, ld_loc, ld_loc, stride_rcv, stride_snd); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_rcv, phi_rcv, count); gam_CountElems(ndim, plo_snd, phi_snd, &length); length *= size; count[0] *= size; /* Fill send buffer with data. */ #if GHOST_PRINT printf("p[%d]b 1: ptr_snd: %d ptr_rcv: %d\n", GAme, (Integer)ptr_snd, (Integer)ptr_rcv); printf("p[%d]b 1: length: %d proc_rem_snd: %d proc_rem_rcv: %d\n", GAme, (int)length, (int)proc_rem_snd, (int)proc_rem_rcv); printf("p[%d]b 1: count[0]: %d stride_rcv[0]: %d stride_rcv[1]: %d\n", GAme, count[0], stride_rcv[0],stride_rcv[1]); printf("p[%d]b 1: count[1]: %d stride_rcv[2]: %d stride_rcv[3]: %d\n", GAme, count[1], stride_rcv[2],stride_rcv[3]); printf("p[%d]b 1: count[2]: %d stride_snd[0]: %d stride_snd[1]: %d\n", GAme, count[2], stride_snd[0],stride_snd[1]); printf("p[%d]b 1: count[3]: %d stride_snd[2]: %d stride_snd[3]: %d\n", GAme, count[3], stride_snd[2],stride_snd[3]); printf("p[%d]b 1: snd_ptr: %d rcv_ptr: %d\n", GAme, (Integer)snd_ptr, (Integer)rcv_ptr); fflush(stdout); #endif armci_write_strided(ptr_snd, (int)ndim-1, stride_snd, count, snd_ptr); /* Send Messages. If processor has odd index in direction idx, it * sends message first, if processor has even index it receives * message first. Then process is reversed. Also need to account * for whether or not there are an odd number of processors along * update direction. */ #if GHOST_PRINT printf("p[%d] 1: msgcnt: %d length: %d bufsize: %d proc_rem_snd: %d proc_rem_rcv: %d\n", GAme, msgcnt, length, bufsize, (int)proc_rem_snd, (int)proc_rem_rcv); fflush(stdout); #endif snd_ptr = snd_ptr_orig; rcv_ptr = rcv_ptr_orig; if (GAme != proc_rem_snd) { if (GA[handle].nblock[idx]%2 == 0) { if (index[idx]%2 != 0) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } else { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } if (index[idx]%2 != 0) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } else { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } } else { pmax = GA[handle].nblock[idx] - 1; if (index[idx]%2 != 0) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } else if (index[idx] != pmax) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } if (index[idx]%2 != 0) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } else if (index[idx] != 0) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } /* make up for odd processor at end of string */ if (index[idx] == 0) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } if (index[idx] == pmax) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } } } else { rcv_ptr = snd_ptr; } msgcnt++; /* copy data back into global array */ armci_read_strided(ptr_rcv, (int)ndim-1, stride_rcv, count, rcv_ptr); /* Find parameters for message in positive direction. */ get_remote_block_pos(idx, ndim, lo_loc, hi_loc, slo_rcv, shi_rcv, dims, width); /* locate processor with this data */ if (!pnga_locate_region(g_a, slo_rcv, shi_rcv, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), slo_rcv, shi_rcv, g_a); proc_rem_snd = _ga_proclist[0]; if (p_handle >= 0) { proc_rem_snd = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem_snd]; } /* Find processor from which data will be recieved */ for (i = 0; i < ndim; i++) { if (i == idx) { lo_rcv[i] = lo_loc[i] - width[i]; hi_rcv[i] = lo_loc[i] - 1; } else { lo_rcv[i] = lo_loc[i]; hi_rcv[i] = hi_loc[i]; } } /* Account for boundaries, if necessary. */ for (i=0; i= 0) { proc_rem_rcv = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem_rcv]; } /* Get actual coordinates of chunk of data that will be sent to * remote processor as well as coordinates of the array space that * will receive data from remote processor. */ for (i = 0; i < ndim; i++) { if (increment[i] == 0) { if (i == idx) { plo_snd[i] = hi_loc[i] - lo_loc[i] + 1; phi_snd[i] = hi_loc[i] - lo_loc[i] + width[i]; plo_rcv[i] = 0; phi_rcv[i] = width[i] - 1; } else { plo_snd[i] = width[i]; phi_snd[i] = hi_loc[i] - lo_loc[i] + width[i]; plo_rcv[i] = width[i]; phi_rcv[i] = hi_loc[i] - lo_loc[i] + width[i]; } } else { plo_rcv[i] = 0; phi_rcv[i] = hi_loc[i] - lo_loc[i] + increment[i]; plo_snd[i] = 0; phi_snd[i] = hi_loc[i] - lo_loc[i] + increment[i]; } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_snd, &ptr_snd, ld_loc); #if GHOST_PRINT printf("p[%d] 2: plo_snd[0]: %d plo_snd[1]: %d ptr_snd: %d\n", GAme, plo_snd[0], plo_snd[1], (Integer)ptr_snd); fflush(stdout); #endif gam_LocationWithGhosts(me, handle, plo_rcv, &ptr_rcv, ld_loc); #if GHOST_PRINT printf("p[%d] 2: plo_rcv[0]: %d plo_rcv[1]: %d ptr_rcv: %d\n", GAme, plo_rcv[0], plo_rcv[1], (Integer)ptr_rcv); fflush(stdout); #endif /* Evaluate strides for send and recieve */ gam_setstride(ndim, size, ld_loc, ld_loc, stride_rcv, stride_snd); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_rcv, phi_rcv, count); gam_CountElems(ndim, plo_snd, phi_snd, &length); length *= size; count[0] *= size; /* Need to reallocate memory if length > buflen */ /* TO DO */ /* Fill send buffer with data. */ #if GHOST_PRINT printf("p[%d]b 2: ptr_snd: %d ptr_rcv: %d\n", GAme, (Integer)ptr_snd, (Integer)ptr_rcv); printf("p[%d]b 2: length: %d proc_rem_snd: %d proc_rem_rcv: %d\n", GAme, (int)length, (int)proc_rem_snd, (int)proc_rem_rcv); printf("p[%d]b 2: count[0]: %d stride_rcv[0]: %d stride_rcv[1]: %d\n", GAme, count[0], stride_rcv[0],stride_rcv[1]); printf("p[%d]b 2: count[1]: %d stride_rcv[2]: %d stride_rcv[3]: %d\n", GAme, count[1], stride_rcv[2],stride_rcv[3]); printf("p[%d]b 2: count[2]: %d stride_snd[0]: %d stride_snd[1]: %d\n", GAme, count[2], stride_snd[0],stride_snd[1]); printf("p[%d]b 2: count[3]: %d stride_snd[2]: %d stride_snd[3]: %d\n", GAme, count[3], stride_snd[2],stride_snd[3]); printf("p[%d]b 2: snd_ptr: %d rcv_ptr: %d\n", GAme, (Integer)snd_ptr, (Integer)rcv_ptr); fflush(stdout); #endif armci_write_strided(ptr_snd, (int)ndim-1, stride_snd, count, snd_ptr); /* Send Messages. If processor has odd index in direction idx, it * sends message first, if processor has even index it receives * message first. Then process is reversed. Also need to account * for whether or not there are an odd number of processors along * update direction. */ #if GHOST_PRINT printf("p[%d] 2: msgcnt: %d length: %d bufsize: %d proc_rem_snd: %d proc_rem_rcv: %d\n", GAme, msgcnt, length, bufsize, (int)proc_rem_snd, (int)proc_rem_rcv); fflush(stdout); #endif snd_ptr = snd_ptr_orig; rcv_ptr = rcv_ptr_orig; if (GAme != proc_rem_rcv) { if (GA[handle].nblock[idx]%2 == 0) { if (index[idx]%2 != 0) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } else { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } if (index[idx]%2 != 0) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } else { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } } else { pmax = GA[handle].nblock[idx] - 1; if (index[idx]%2 != 0) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } else if (index[idx] != 0) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } if (index[idx]%2 != 0) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } else if (index[idx] != pmax) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } /* make up for odd processor at end of string */ if (index[idx] == pmax) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } if (index[idx] == 0) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } } } else { rcv_ptr = snd_ptr; } /* copy data back into global array */ armci_read_strided(ptr_rcv, (int)ndim-1, stride_rcv, count, rcv_ptr); msgcnt++; } if (GA[handle].corner_flag) increment[idx] = 2*width[idx]; } pnga_free(rcv_ptr_orig); pnga_free(snd_ptr_orig); free(_ga_map); free(_ga_proclist); return TRUE; } /* Utility function for ga_update5_ghosts routine */ static inline double waitforflags (int *ptr1, int *ptr2) { int i = 1; double val = 0; while (*ptr1 == 0 || *ptr2 == 0) { val = exp(-(double)i++); } #if 0 printf("%d: flags set at %p and %p\n",GAme,ptr1,ptr2); fflush(stdout); #endif return(val); } #if 0 /* Stub in new ARMCI_PutS_flag call until actual implementation is available */ int ARMCI_PutS_flag__( void* src_ptr, /* pointer to 1st segment at source */ int src_stride_arr[], /* array of strides at source */ void* dst_ptr, /* pointer to 1st segment at destination */ int dst_stride_arr[], /* array of strides at destination */ int count[], /* number of units at each stride level, count[0] = #bytes */ int stride_levels, /* number of stride levels */ int *flag, /* pointer to remote flag */ int *val, /* pointer to value to set flag upon completion of data transfer */ int proc /* remote process(or) ID */ ) { int bytes; /* Put local data on remote processor */ ARMCI_PutS(src_ptr, src_stride_arr, dst_ptr, dst_stride_arr, count, stride_levels, proc); /* Send signal to remote processor that data transfer has * been completed. */ bytes = sizeof(int); ARMCI_Put(val, flag, bytes, proc); return 1; } #endif /*\ UPDATE GHOST CELLS OF GLOBAL ARRAY USING SHIFT ALGORITHM AND PUT CALLS * WITHOUT ANY BARRIERS \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_update55_ghosts = pnga_update55_ghosts #endif logical pnga_update55_ghosts(Integer g_a) { Integer idx, i, np, handle=GA_OFFSET + g_a, proc_rem; Integer size, ndim, nwidth, increment[MAXDIM]; Integer width[MAXDIM]; Integer dims[MAXDIM]; Integer lo_loc[MAXDIM], hi_loc[MAXDIM]; Integer plo_loc[MAXDIM]/*, phi_loc[MAXDIM]*/; Integer tlo_rem[MAXDIM], thi_rem[MAXDIM]; Integer slo_rem[MAXDIM], shi_rem[MAXDIM]; Integer plo_rem[MAXDIM], phi_rem[MAXDIM]; Integer ld_loc[MAXDIM], ld_rem[MAXDIM]; int stride_loc[MAXDIM], stride_rem[MAXDIM],count[MAXDIM]; int msgcnt; char *ptr_loc, *ptr_rem; Integer me = pnga_nodeid(); Integer p_handle; Integer *_ga_map = NULL; Integer *_ga_proclist = NULL; /* This routine makes use of the shift algorithm to update data in the * ghost cells bounding the local block of visible data. The shift * algorithm starts by updating the blocks of data along the first * dimension by grabbing a block of data that is width[0] deep but * otherwise matches the dimensions of the data residing on the * calling processor. The update of the second dimension, however, * grabs a block that is width[1] deep in the second dimension but is * ldim0 + 2*width[0] in the first dimensions where ldim0 is the * size of the visible data along the first dimension. The remaining * dimensions are left the same. For the next update, the width of the * second dimension is also increased by 2*width[1] and so on. This * algorith makes use of the fact that data for the dimensions that * have already been updated is available on each processor and can be * used in the updates of subsequent dimensions. The total number of * separate updates is 2*ndim, an update in the negative and positive * directions for each dimension. * * This operation is implemented using put calls to place the * appropriate data on remote processors. To signal the remote * processor that it has received the data, a second put call * consisting of a single integer is sent after the first put call and * used to update a signal buffer on the remote processor. Each * processor can determine how much data it has received by checking * its signal buffer. * * To perform the update, this routine makes use of several copies of * indices marking the upper and lower limits of data. Indices * beginning with the character "p" are relative indices marking the * location of the data set relative to the origin the local patch of * the global array, all other indices are in absolute coordinates and * mark locations in the total global array. The indices used by this * routine are described below. * * lo_loc[], hi_loc[]: The lower and upper indices of the visible * block of data held by the calling processor. * * lo_rem[], hi_rem[]: The lower and upper indices of the block * of data on a remote processor or processors that is needed to * fill in the calling processors ghost cells. These indices are * NOT corrected for wrap-around (periodic) boundary conditions * so they can be negative or greater than the array dimension * values held in dims[]. * * slo_rem[], shi_rem[]: Similar to lo_rem[] and hi_rem[], except * that these indices have been corrected for wrap-around * boundary conditions. * * thi_rem[], thi_rem[]: The lower and upper indices of the visible * data on a remote processor. * * plo_loc[], phi_loc[]: The indices of the local data patch that * is going to be updated. * * plo_rem[], phi_rem[]: The indices of the data patch on the * remote processor that will be used to update the data on the * calling processor. Note that the dimensions of the patches * represented by plo_loc[], plo_rem[] and plo_loc[], phi_loc[] * must be the same. */ /* if global array has no ghost cells, just return */ if (!pnga_has_ghosts(g_a)) return TRUE; size = GA[handle].elemsize; ndim = GA[handle].ndim; p_handle = GA[handle].p_handle; /* initialize range increments and get array dimensions */ for (idx=0; idx < ndim; idx++) { increment[idx] = 0; width[idx] = (Integer)GA[handle].width[idx]; dims[idx] = (Integer)GA[handle].dims[idx]; if (lo_loc[idx] == 0 && hi_loc[idx] == -1) return FALSE; } /* Check to make sure that global array is well-behaved (all processors have data and the width of the data in each dimension is greater than the corresponding value in width[]. */ if (!gai_check_ghost_distr(g_a)) return FALSE; _ga_map = malloc((GAnproc*2*MAXDIM+1)*sizeof(Integer)); if(!_ga_map) pnga_error("pnga_update55_ghosts:malloc failed (_ga_map)",0); _ga_proclist = malloc(GAnproc*sizeof(Integer)); if(!_ga_proclist) pnga_error("pnga_update55_ghosts:malloc failed (_ga_proclist)",0); /* Get pointer to local memory */ ptr_loc = GA[handle].ptr[GAme]; /* obtain range of data that is held by local processor */ pnga_distribution(g_a,me,lo_loc,hi_loc); /* loop over dimensions for sequential update using shift algorithm */ msgcnt = 0; (*GA_Update_Signal) = 1; for (idx=0; idx < ndim; idx++) { nwidth = width[idx]; /* Do not bother with update if nwidth is zero */ if (nwidth != 0) { /* Perform update in negative direction. */ get_remote_block_neg(idx, ndim, lo_loc, hi_loc, slo_rem, shi_rem, dims, width); /* locate processor with this data */ if (!pnga_locate_region(g_a, slo_rem, shi_rem, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), slo_rem, shi_rem, g_a); /* Get actual coordinates of desired location of remote data as well as the actual coordinates of the local chunk of data that will be sent to remote processor (these coordinates take into account the presence of ghost cells). Start by finding out what data is actually held by remote processor. */ proc_rem = _ga_proclist[0]; pnga_distribution(g_a, proc_rem, tlo_rem, thi_rem); for (i = 0; i < ndim; i++) { if (increment[i] == 0) { if (i == idx) { plo_rem[i] = thi_rem[i] - tlo_rem[i] + width[i] + 1; phi_rem[i] = thi_rem[i] - tlo_rem[i] + 2*width[i]; plo_loc[i] = width[i]; /*phi_loc[i] = 2*width[i] - 1;*/ } else { plo_rem[i] = width[i]; phi_rem[i] = thi_rem[i] - tlo_rem[i] + width[i]; plo_loc[i] = width[i]; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + width[i];*/ } } else { plo_rem[i] = 0; phi_rem[i] = thi_rem[i] - tlo_rem[i] + increment[i]; plo_loc[i] = 0; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + increment[i];*/ } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_loc, &ptr_loc, ld_loc); gam_LocationWithGhosts(proc_rem, handle, plo_rem, &ptr_rem, ld_rem); /* Evaluate strides on local and remote processors */ gam_setstride(ndim, size, ld_loc, ld_rem, stride_rem, stride_loc); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_rem, phi_rem, count); count[0] *= size; /* Put local data on remote processor */ if (p_handle >= 0) { proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem]; } #if 0 ARMCI_PutS(ptr_loc, stride_loc, ptr_rem, stride_rem, count, ndim- 1, proc_rem); /* Send signal to remote processor that data transfer has been completed. */ bytes = sizeof(int); ARMCI_Put(GA_Update_Signal, GA_Update_Flags[proc_rem]+msgcnt, bytes, proc_rem); #else ARMCI_PutS_flag(ptr_loc, stride_loc, ptr_rem, stride_rem, count, (int)(ndim - 1), GA_Update_Flags[proc_rem]+msgcnt, *GA_Update_Signal, (int)proc_rem); #endif msgcnt++; /* Perform update in positive direction. */ get_remote_block_pos(idx, ndim, lo_loc, hi_loc, slo_rem, shi_rem, dims, width); /* locate processor with this data */ if (!pnga_locate_region(g_a, slo_rem, shi_rem, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), slo_rem, shi_rem, g_a); /* Get actual coordinates of desired chunk of remote data as well as the actual coordinates of the local chunk of data that will receive the remote data (these coordinates take into account the presence of ghost cells). Start by finding out what data is actually held by remote processor. */ proc_rem = _ga_proclist[0]; pnga_distribution(g_a, proc_rem, tlo_rem, thi_rem); for (i = 0; i < ndim; i++) { if (increment[i] == 0) { if (i == idx) { plo_rem[i] = 0; phi_rem[i] = width[i] - 1; plo_loc[i] = hi_loc[i] - lo_loc[i] + width[i] - 1; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + 2*width[i] - 1;*/ } else { plo_rem[i] = width[i]; phi_rem[i] = thi_rem[i] - tlo_rem[i] + width[i]; plo_loc[i] = width[i]; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + width[i];*/ } } else { plo_rem[i] = 0; phi_rem[i] = thi_rem[i] - tlo_rem[i] + increment[i]; plo_loc[i] = 0; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + increment[i];*/ } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(GAme, handle, plo_loc, &ptr_loc, ld_loc); gam_LocationWithGhosts(proc_rem, handle, plo_rem, &ptr_rem, ld_rem); /* Evaluate strides on local and remote processors */ gam_setstride(ndim, size, ld_loc, ld_rem, stride_rem, stride_loc); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_rem, phi_rem, count); count[0] *= size; /* Put local data on remote processor */ if (p_handle >= 0) { proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem]; } #if 0 ARMCI_PutS(ptr_loc, stride_loc, ptr_rem, stride_rem, count, ndim- 1, proc_rem); /* Send signal to remote processor that data transfer has been completed. */ bytes = sizeof(int); ARMCI_Put(GA_Update_Signal, GA_Update_Flags[proc_rem]+msgcnt, bytes, proc_rem); #else ARMCI_PutS_flag(ptr_loc, stride_loc, ptr_rem, stride_rem, count, (int)(ndim - 1), GA_Update_Flags[proc_rem]+msgcnt, *GA_Update_Signal, (int)proc_rem); #endif msgcnt++; } /* check to make sure that all messages have been recieved before starting update along new dimension */ waitforflags((GA_Update_Flags[GAme]+msgcnt-2), (GA_Update_Flags[GAme]+msgcnt-1)); /* update increment array */ increment[idx] = 2*nwidth; } /* set GA_Update_Flags array to zero for next update operation. */ for (idx=0; idx < 2*ndim; idx++) { GA_Update_Flags[GAme][idx] = 0; } free(_ga_map); free(_ga_proclist); return TRUE; } /*\ UPDATE GHOST CELLS OF GLOBAL ARRAY USING NON-BLOCKING GET CALLS AND RETURN * A NON-BLOCKING HANDLE \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_update_ghosts_nb = pnga_update_ghosts_nb #endif void pnga_update_ghosts_nb(Integer g_a, Integer *nbhandle) { Integer idx, ipx, np, handle=GA_OFFSET + g_a, proc_rem; Integer ntot, mask[MAXDIM]; Integer size, ndim, i, itmp; Integer width[MAXDIM], dims[MAXDIM]; Integer lo_loc[MAXDIM], hi_loc[MAXDIM]; /*Integer tlo_loc[MAXDIM], thi_loc[MAXDIM];*/ Integer plo_loc[MAXDIM], phi_loc[MAXDIM]; Integer tlo_rem[MAXDIM], thi_rem[MAXDIM]; Integer plo_rem[MAXDIM]; Integer ld_loc[MAXDIM], ld_rem[MAXDIM]; logical mask0; int stride_loc[MAXDIM], stride_rem[MAXDIM],count[MAXDIM]; char *ptr_loc, *ptr_rem; Integer me = pnga_nodeid(); Integer p_handle; Integer *_ga_map = NULL; Integer *_ga_proclist = NULL; /* if global array has no ghost cells, just return */ if (!pnga_has_ghosts(g_a)) { return; } size = GA[handle].elemsize; ndim = GA[handle].ndim; p_handle = GA[handle].p_handle; /* initialize ghost cell widths and get array dimensions */ for (idx=0; idx < ndim; idx++) { width[idx] = (Integer)GA[handle].width[idx]; dims[idx] = (Integer)GA[handle].dims[idx]; } /* Check to make sure that global array is well-behaved (all processors have data and the width of the data in each dimension is greater than the corresponding value in width[]). */ if (!gai_check_ghost_distr(g_a)) return; _ga_map = malloc((GAnproc*2*MAXDIM+1)*sizeof(Integer)); if(!_ga_map) pnga_error("pnga_update_ghosts_nb:malloc failed (_ga_map)",0); _ga_proclist = malloc(GAnproc*sizeof(Integer)); if(!_ga_proclist) pnga_error("pnga_update_ghosts_nb:malloc failed (_ga_proclist)",0); /* Create non-blocking handle */ ga_init_nbhandle(nbhandle); /* Get pointer to local memory */ ptr_loc = GA[handle].ptr[me]; /* obtain range of data that is held by local processor */ pnga_distribution(g_a,me,lo_loc,hi_loc); /* evaluate total number of PUT operations that will be required */ ntot = 1; for (idx=0; idx < ndim; idx++) ntot *= 3; /* Loop over all GET operations. The operation corresponding to the mask of all zeros is left out. */ for (ipx=0; ipx < ntot; ipx++) { /* Convert ipx to corresponding mask values */ itmp = ipx; mask0 = TRUE; for (idx = 0; idx < ndim; idx++) { i = itmp%3; mask[idx] = i-1; if (mask[idx] != 0) mask0 = FALSE; itmp = (itmp-i)/3; } if (mask0) continue; /* check to see if ghost cell block has zero elements*/ mask0 = FALSE; itmp = 0; for (idx = 0; idx < ndim; idx++) { if (mask[idx] != 0 && width[idx] == 0) mask0 = TRUE; if (mask[idx] != 0) itmp++; } if (mask0) continue; /* Now that mask has been determined, find data that is to be moved * and identify processor to which it is going. Wrap boundaries * around, if necessary */ for (idx = 0; idx < ndim; idx++) { if (mask[idx] == 0) { tlo_rem[idx] = lo_loc[idx]; thi_rem[idx] = hi_loc[idx]; } else if (mask[idx] == -1) { if (lo_loc[idx] > 1) { tlo_rem[idx] = lo_loc[idx]-width[idx]; thi_rem[idx] = lo_loc[idx]-1; } else { tlo_rem[idx] = dims[idx]-width[idx]+1; thi_rem[idx] = dims[idx]; } } else if (mask[idx] == 1) { if (hi_loc[idx] < dims[idx]) { tlo_rem[idx] = hi_loc[idx] + 1; thi_rem[idx] = hi_loc[idx] + width[idx]; } else { tlo_rem[idx] = 1; thi_rem[idx] = width[idx]; } } else { fprintf(stderr,"Illegal mask value found\n"); } } /* Locate remote processor from which data must be retrieved */ if (!pnga_locate_region(g_a, tlo_rem, thi_rem, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), tlo_rem, thi_rem, g_a); if (np > 1) { fprintf(stderr,"More than one remote processor found\n"); } /* Remote processor has been identified, now get ready to get data from it. Start by getting distribution on remote processor.*/ proc_rem = _ga_proclist[0]; pnga_distribution(g_a, proc_rem, tlo_rem, thi_rem); for (idx = 0; idx < ndim; idx++) { if (mask[idx] == 0) { plo_loc[idx] = width[idx]; phi_loc[idx] = hi_loc[idx]-lo_loc[idx]+width[idx]; plo_rem[idx] = plo_loc[idx]; } else if (mask[idx] == -1) { plo_loc[idx] = 0; phi_loc[idx] = width[idx]-1; plo_rem[idx] = thi_rem[idx]-tlo_rem[idx]+1; } else if (mask[idx] == 1) { plo_loc[idx] = hi_loc[idx]-lo_loc[idx]+width[idx]+1; phi_loc[idx] = hi_loc[idx]-lo_loc[idx]+2*width[idx]; plo_rem[idx] = width[idx]; } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_loc, &ptr_loc, ld_loc); gam_LocationWithGhosts(proc_rem, handle, plo_rem, &ptr_rem, ld_rem); /* Evaluate strides on local and remote processors */ gam_setstride(ndim, size, ld_loc, ld_rem, stride_rem, stride_loc); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_loc, phi_loc, count); count[0] *= size; /* get data from remote processor */ if (p_handle >= 0) { proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem]; } ARMCI_NbGetS(ptr_rem, stride_rem, ptr_loc, stride_loc, count, (int)(ndim - 1), (int)proc_rem, (armci_hdl_t*)get_armci_nbhandle(nbhandle)); } free(_ga_map); free(_ga_proclist); return; } /*\ UPDATE GHOST CELLS OF GLOBAL ARRAY ALONG ONE SIDE OF ARRAY \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_update_ghost_dir = pnga_update_ghost_dir #endif logical pnga_update_ghost_dir(Integer g_a, /* GA handle */ Integer pdim, /* Dimension of update */ Integer pdir, /* Direction of update (+/-1) */ logical pflag) /* include corner cells */ { Integer idx, ipx, inx, np, handle=GA_OFFSET + g_a, proc_rem; Integer ntot, mask[MAXDIM],lmask[MAXDIM]; Integer size, ndim, i, itmp, idim, idir; Integer width[MAXDIM], dims[MAXDIM]; Integer lo_loc[MAXDIM], hi_loc[MAXDIM]; Integer plo_loc[MAXDIM], phi_loc[MAXDIM]; Integer tlo_rem[MAXDIM], thi_rem[MAXDIM]; Integer plo_rem[MAXDIM]/*, phi_rem[MAXDIM]*/; Integer ld_loc[MAXDIM], ld_rem[MAXDIM]; logical flag; int stride_loc[MAXDIM], stride_rem[MAXDIM],count[MAXDIM]; char *ptr_loc, *ptr_rem; Integer me = pnga_nodeid(); Integer p_handle; Integer *_ga_map = NULL; Integer *_ga_proclist = NULL; int local_sync_begin,local_sync_end; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ /* if global array has no ghost cells, just return */ if (!pnga_has_ghosts(g_a)) return TRUE; p_handle = GA[handle].p_handle; if(local_sync_begin)pnga_pgroup_sync(p_handle); idim = pdim; idir = pdir; flag = pflag; size = GA[handle].elemsize; ndim = GA[handle].ndim; /* initialize ghost cell widths and get array dimensions */ for (idx=0; idx < ndim; idx++) { width[idx] = (Integer)GA[handle].width[idx]; dims[idx] = (Integer)GA[handle].dims[idx]; } /* Check to make sure that global array is well-behaved (all processors have data and the width of the data in each dimension is greater than the corresponding value in width[]). */ ipx = 0; for (idx = 0; idx < ndim; idx++) { for (np = 0; np < GA[handle].nblock[idx]; np++) { if (np < GA[handle].nblock[idx] - 1) { if (GA[handle].mapc[ipx+1]-GA[handle].mapc[ipx]+1 1) { tlo_rem[idx] = lo_loc[idx]-width[idx]; thi_rem[idx] = lo_loc[idx]-1; } else { tlo_rem[idx] = dims[idx]-width[idx]+1; thi_rem[idx] = dims[idx]; } } else if (mask[idx] == 1) { if (hi_loc[idx] < dims[idx]) { tlo_rem[idx] = hi_loc[idx] + 1; thi_rem[idx] = hi_loc[idx] + width[idx]; } else { tlo_rem[idx] = 1; thi_rem[idx] = width[idx]; } } else { fprintf(stderr,"Illegal mask value found\n"); } } /* Locate remote processor to which data must be sent */ if (!pnga_locate_region(g_a, tlo_rem, thi_rem, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), tlo_rem, thi_rem, g_a); if (np > 1) { fprintf(stderr,"More than one remote processor found\n"); } /* Remote processor has been identified, now get ready to get data from it. Start by getting distribution on remote processor.*/ proc_rem = _ga_proclist[0]; pnga_distribution(g_a, proc_rem, tlo_rem, thi_rem); for (idx = 0; idx < ndim; idx++) { if (mask[idx] == 0) { plo_loc[idx] = width[idx]; phi_loc[idx] = hi_loc[idx]-lo_loc[idx]+width[idx]; plo_rem[idx] = plo_loc[idx]; /*phi_rem[idx] = phi_loc[idx];*/ } else if (mask[idx] == -1) { plo_loc[idx] = 0; phi_loc[idx] = width[idx]-1; plo_rem[idx] = thi_rem[idx]-tlo_rem[idx]+1; /*phi_rem[idx] = thi_rem[idx]-tlo_rem[idx]+width[idx];*/ } else if (mask[idx] == 1) { plo_loc[idx] = hi_loc[idx]-lo_loc[idx]+width[idx]+1; phi_loc[idx] = hi_loc[idx]-lo_loc[idx]+2*width[idx]; plo_rem[idx] = width[idx]; /*phi_rem[idx] = 2*width[idx]-1;*/ } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_loc, &ptr_loc, ld_loc); gam_LocationWithGhosts(proc_rem, handle, plo_rem, &ptr_rem, ld_rem); /* Evaluate strides on local and remote processors */ gam_setstride(ndim, size, ld_loc, ld_rem, stride_rem, stride_loc); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_loc, phi_loc, count); count[0] *= size; /* get data from remote processor */ if (p_handle >= 0) { proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem]; } ARMCI_GetS(ptr_rem, stride_rem, ptr_loc, stride_loc, count, (int)(ndim - 1), (int)proc_rem); } if(local_sync_end)pnga_pgroup_sync(p_handle); free(_ga_map); free(_ga_proclist); return TRUE; } /*uncomment for using message passing sendrecv in north south direction */ /*#define USE_MP_NORTHSOUTH */ /*\ UPDATE GHOST CELLS OF GLOBAL ARRAY USING PUT CALLS WITHOUT CORNERS AND * WITHOUT ANY BARRIERS \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_update5_ghosts = pnga_update5_ghosts #endif logical pnga_update5_ghosts(Integer g_a) { Integer idx, i, handle=GA_OFFSET + g_a; Integer /*size,*/ ndim, nwidth; Integer width[MAXDIM]; Integer* proc_rem_ptr; int *stride_loc, *stride_rem,*count; int msgcnt, corner_flag, proc_rem; /* int bytes; */ char *ptr_loc, *ptr_rem,*cache; int local_sync_begin,local_sync_end; Integer p_handle; #ifdef USE_MP_NORTHSOUTH char send_name[32], rcv_name[32]; void *snd_ptr, *rcv_ptr; #endif /* This routine makes use of the shift algorithm to update data in the * ghost cells bounding the local block of visible data. The shift * algorithm starts by updating the blocks of data along the first * dimension by grabbing a block of data that is width[0] deep but * otherwise matches the dimensions of the data residing on the * calling processor. The update of the second dimension, however, * grabs a block that is width[1] deep in the second dimension but is * ldim0 + 2*width[0] in the first dimensions where ldim0 is the * size of the visible data along the first dimension. The remaining * dimensions are left the same. For the next update, the width of the * second dimension is also increased by 2*width[1] and so on. This * algorith makes use of the fact that data for the dimensions that * have already been updated is available on each processor and can be * used in the updates of subsequent dimensions. The total number of * separate updates is 2*ndim, an update in the negative and positive * directions for each dimension. * * This operation is implemented using put calls to place the * appropriate data on remote processors. To signal the remote * processor that it has received the data, a second put call * consisting of a single integer is sent after the first put call and * used to update a signal buffer on the remote processor. Each * processor can determine how much data it has received by checking * its signal buffer. */ local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ p_handle = GA[handle].p_handle; if(local_sync_begin)pnga_pgroup_sync(p_handle); #ifdef USE_MP_NORTHSOUTH strcpy(send_name,"send_buffer"); strcpy(rcv_name,"receive_buffer"); snd_ptr = pnga_malloc(buflen, GA[handle].type, send_name); rcv_ptr = pnga_malloc(buflen, GA[handle].type, rcv_name); #endif cache = (char *)GA[handle].cache; /* if global array has no ghost cells, just return */ if (!pnga_has_ghosts(g_a)) return TRUE; /*size = GA[handle].elemsize;*/ ndim = GA[handle].ndim; for (i=0; i= 0) { proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem]; } if(count[0]>1000000){ /*tries to use armci direct put when possible */ ARMCI_PutS_flag(ptr_loc, stride_loc, ptr_rem, stride_rem, count, (int)(ndim - 1), GA_Update_Flags[proc_rem]+msgcnt, *GA_Update_Signal, proc_rem); } else{ #ifndef USE_MP_NORTHSOUTH ARMCI_PutS_flag(ptr_loc, stride_loc, ptr_rem, stride_rem, count, (int)(ndim - 1), GA_Update_Flags[proc_rem]+msgcnt, *GA_Update_Signal, proc_rem); #else #endif } msgcnt++; /* Perform update in positive direction. */ ptr_rem = *(char **)(cache); ptr_loc = *(char **)(cache+sizeof(char *)); stride_loc = (int *)(cache+2*sizeof(char *)); stride_rem = (int *)(stride_loc+ndim); count = (int *)(stride_rem+ndim); proc_rem_ptr = (Integer *)(count+ndim); proc_rem = (int)(*proc_rem_ptr); cache = (char *)(proc_rem_ptr+1); if (p_handle >= 0) { proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem]; } if(count[0]>1000000){ /*tries to use armci direct put when possible */ ARMCI_PutS_flag(ptr_loc, stride_loc, ptr_rem, stride_rem, count, (int)(ndim - 1), GA_Update_Flags[proc_rem]+msgcnt, *GA_Update_Signal, proc_rem); } else{ #ifndef USE_MP_NORTHSOUTH ARMCI_PutS_flag(ptr_loc, stride_loc, ptr_rem, stride_rem, count, (int)(ndim - 1), GA_Update_Flags[proc_rem]+msgcnt, *GA_Update_Signal, proc_rem); #else #endif } msgcnt++; if (corner_flag){ /* check to make sure that last two messages have been recieved before starting update along a new dimension */ waitforflags((GA_Update_Flags[GAme]+msgcnt-2), (GA_Update_Flags[GAme]+msgcnt-1)); GA_Update_Flags[GAme][msgcnt-1]=0; GA_Update_Flags[GAme][msgcnt-2]=0; } } } #if 1 if (!corner_flag) { /* check to make sure that all messages have been recieved */ while(msgcnt){ waitforflags((GA_Update_Flags[GAme]+msgcnt-1), (GA_Update_Flags[GAme]+msgcnt-2)); GA_Update_Flags[GAme][msgcnt-1]=0; GA_Update_Flags[GAme][msgcnt-2]=0; msgcnt-=2; } } #endif if(local_sync_end)pnga_pgroup_sync(p_handle); return TRUE; } /*#define UPDATE_SAMENODE_GHOSTS_FIRST*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_set_update5_info = pnga_set_update5_info #endif logical pnga_set_update5_info(Integer g_a) { int i; Integer *proc_rem; Integer size, ndim, nwidth, increment[MAXDIM],np; Integer width[MAXDIM]; Integer dims[MAXDIM]; Integer lo_loc[MAXDIM], hi_loc[MAXDIM]; Integer plo_loc[MAXDIM]/*, phi_loc[MAXDIM]*/; Integer tlo_rem[MAXDIM], thi_rem[MAXDIM]; Integer slo_rem[MAXDIM], shi_rem[MAXDIM]; Integer plo_rem[MAXDIM], phi_rem[MAXDIM]; Integer ld_loc[MAXDIM], ld_rem[MAXDIM]; int *stride_loc, *stride_rem,*count; int idx, corner_flag; char **ptr_loc, **ptr_rem,*cache; Integer handle = GA_OFFSET + g_a; int cache_size; #ifdef UPDATE_SAMENODE_GHOSTS_FIRST int scope; #endif Integer me = pnga_nodeid(); Integer p_handle; Integer *_ga_map = NULL; Integer *_ga_proclist = NULL; /* This routine sets up the arrays that are used to transfer data * using the update5 algorithm. The arrays begining with the character * "p" represent relative indices marking the location of the data set * relative to the origin the local patch of the global array, all * other indices are in absolute coordinates and mark locations in the * total global array. The indices used by this routine are described * below. * * lo_loc[], hi_loc[]: The lower and upper indices of the visible * block of data held by the calling processor. * * lo_rem[], hi_rem[]: The lower and upper indices of the block * of data on a remote processor or processors that is needed to * fill in the calling processors ghost cells. These indices are * NOT corrected for wrap-around (periodic) boundary conditions * so they can be negative or greater than the array dimension * values held in dims[]. * * slo_rem[], shi_rem[]: Similar to lo_rem[] and hi_rem[], except * that these indices have been corrected for wrap-around * boundary conditions. * * thi_rem[], thi_rem[]: The lower and upper indices of the visible * data on a remote processor. * * plo_loc[], phi_loc[]: The indices of the local data patch that * is going to be updated. * * plo_rem[], phi_rem[]: The indices of the data patch on the * remote processor that will be used to update the data on the * calling processor. Note that the dimensions of the patches * represented by plo_loc[], plo_rem[] and plo_loc[], phi_loc[] * must be the same. */ /* if global array has no ghost cells, just return */ if (!pnga_has_ghosts(g_a)) return TRUE; /* Check to make sure that global array is well-behaved (all processors have data and the width of the data in each dimension is greater than the corresponding value in width[]. */ if (!gai_check_ghost_distr(g_a)) return FALSE; ndim = GA[handle].ndim; p_handle = GA[handle].p_handle; size = GA[handle].elemsize; cache_size = 2*sizeof(char *)+3*sizeof(int)+sizeof(Integer); cache_size = 2*ndim*((cache_size/sizeof(double)) + 1); GA[handle].cache = (double *)malloc(sizeof(double)*cache_size); cache = (char *)GA[handle].cache; corner_flag = GA[handle].corner_flag; pnga_distribution(g_a,me,lo_loc,hi_loc); for (idx=0; idx < ndim; idx++) { increment[idx] = 0; width[idx] = (Integer)GA[handle].width[idx]; dims[idx] = (Integer)GA[handle].dims[idx]; if (lo_loc[idx] == 0 && hi_loc[idx] == -1){ *(char **)cache = NULL; return FALSE; } } _ga_map = malloc((GAnproc*2*MAXDIM+1)*sizeof(Integer)); if(!_ga_map) pnga_error("pnga_set_update5_info:malloc failed (_ga_map)",0); _ga_proclist = malloc(GAnproc*sizeof(Integer)); if(!_ga_proclist) pnga_error("pnga_set_update5_info:malloc failed (_ga_proclist)",0); #ifdef UPDATE_SAMENODE_GHOSTS_FIRST for(scope=0;scope < 2; scope ++) #endif for (idx=0; idx < ndim; idx++) { nwidth = width[idx]; if (nwidth != 0) { ptr_rem = (char **)cache; ptr_loc = (char **)(cache+sizeof(char *)); stride_loc = (int *)(cache+2*sizeof(char *)); stride_rem = (int *)(stride_loc+ndim); count = (int *)(stride_rem+ndim); proc_rem = (Integer *)(count+ndim); get_remote_block_neg(idx, ndim, lo_loc, hi_loc, slo_rem, shi_rem, dims, width); if (!pnga_locate_region(g_a, slo_rem, shi_rem, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), slo_rem, shi_rem, g_a); *proc_rem = (Integer)_ga_proclist[0]; if (p_handle >= 0) { *proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[*proc_rem]; } #ifdef UPDATE_SAMENODE_GHOSTS_FIRST if(scope == 0 && ARMCI_Same_node(*proc_rem)) goto do_negative; #endif cache = (char *)(proc_rem+1); pnga_distribution(g_a, *proc_rem, tlo_rem, thi_rem); for (i = 0; i < ndim; i++) { if (increment[i] == 0) { if (i == idx) { plo_rem[i] = thi_rem[i] - tlo_rem[i] + width[i] + 1; phi_rem[i] = thi_rem[i] - tlo_rem[i] + 2*width[i]; plo_loc[i] = width[i]; /*phi_loc[i] = 2*width[i] - 1;*/ } else { plo_rem[i] = width[i]; phi_rem[i] = thi_rem[i] - tlo_rem[i] + width[i]; plo_loc[i] = width[i]; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + width[i];*/ } } else { plo_rem[i] = 0; phi_rem[i] = thi_rem[i] - tlo_rem[i] + increment[i]; plo_loc[i] = 0; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + increment[i];*/ } } gam_LocationWithGhosts(me, handle, plo_loc, ptr_loc, ld_loc); gam_LocationWithGhosts(*proc_rem, handle, plo_rem, ptr_rem, ld_rem); /* Evaluate strides on local and remote processors */ gam_setstride(ndim, size, ld_loc, ld_rem, stride_rem, stride_loc); gam_ComputeCount(ndim, plo_rem, phi_rem, count); count[0] *= size; if (p_handle >= 0) { *proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[*proc_rem]; } #ifdef UPDATE_SAMENODE_GHOSTS_FIRST do_negative: #endif /*BJP proc_rem++; */ ptr_rem = (char **)cache; ptr_loc = (char **)(cache+sizeof(char *)); stride_loc = (int *)(cache+2*sizeof(char *)); stride_rem = (int *)(stride_loc+ndim); count = (int *)(stride_rem+ndim); proc_rem = (Integer *)(count+ndim); get_remote_block_pos(idx, ndim, lo_loc, hi_loc, slo_rem, shi_rem, dims, width); if (!pnga_locate_region(g_a, slo_rem, shi_rem, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), slo_rem, shi_rem, g_a); *proc_rem = (Integer)_ga_proclist[0]; if (p_handle >= 0) { *proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[*proc_rem]; } #ifdef UPDATE_SAMENODE_GHOSTS_FIRST if(scope == 0 && ARMCI_Same_node(*proc_rem)) continue; #endif cache = (char *)(proc_rem+1); pnga_distribution(g_a, *proc_rem, tlo_rem, thi_rem); for (i = 0; i < ndim; i++) { if (increment[i] == 0) { if (i == idx) { plo_rem[i] = 0; phi_rem[i] = width[i] - 1; plo_loc[i] = hi_loc[i] - lo_loc[i] + width[i] - 1; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + 2*width[i] - 1;*/ } else { plo_rem[i] = width[i]; phi_rem[i] = thi_rem[i] - tlo_rem[i] + width[i]; plo_loc[i] = width[i]; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + width[i];*/ } } else { plo_rem[i] = 0; phi_rem[i] = thi_rem[i] - tlo_rem[i] + increment[i]; plo_loc[i] = 0; /*phi_loc[i] = hi_loc[i] - lo_loc[i] + increment[i];*/ } } gam_LocationWithGhosts(GAme, handle, plo_loc, ptr_loc, ld_loc); gam_LocationWithGhosts(*proc_rem, handle, plo_rem, ptr_rem, ld_rem); gam_setstride(ndim, size, ld_loc, ld_rem, stride_rem, stride_loc); gam_ComputeCount(ndim, plo_rem, phi_rem, count); count[0] *= size; if (p_handle >= 0) { *proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[*proc_rem]; } if (corner_flag) increment[idx] = 2*nwidth; } } free(_ga_map); free(_ga_proclist); return TRUE; } /*\ UPDATE GHOST CELLS OF GLOBAL ARRAY USING SHIFT ALGORITHM \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_update_ghosts = pnga_update_ghosts #endif void pnga_update_ghosts(Integer g_a) { /* Wrapper program for ghost cell update operations. If optimized update operation fails then use slow but robust version of update operation */ int local_sync_begin,local_sync_end; Integer handle = GA_OFFSET + g_a; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_pgroup_sync(GA[handle].p_handle); if (!pnga_update4_ghosts(g_a)) { pnga_update1_ghosts(g_a); } if(local_sync_end)pnga_pgroup_sync(GA[handle].p_handle); } /* Utility function for ga_update6_ghosts routine */ static double waitformixedflags (int flag1, int flag2, int *ptr1, int *ptr2) { int i = 1; double val = 0; while ((flag1 && *ptr1 == 0) || (flag2 && *ptr2 == 0)) { val = exp(-(double)i++); } return(val); } /*\ UPDATE GHOST CELLS OF GLOBAL ARRAY USING SHIFT ALGORITHM AND * MESSAGE PASSING \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_update6_ghosts = pnga_update6_ghosts #endif logical pnga_update6_ghosts(Integer g_a) { Integer idx, idir, i, np, handle=GA_OFFSET + g_a; Integer size, buflen, buftot, bufsize, ndim, increment[MAXDIM]; Integer proc_rem_snd, proc_rem_rcv, pmax; Integer msgcnt, length; Integer width[MAXDIM], dims[MAXDIM], index[MAXDIM]; Integer lo_loc[MAXDIM], hi_loc[MAXDIM]; Integer plo_rem[MAXDIM]/*, phi_rem[MAXDIM]*/; Integer tlo_rem[MAXDIM], thi_rem[MAXDIM]; Integer plo_snd[MAXDIM], phi_snd[MAXDIM]; Integer lo_rcv[MAXDIM], hi_rcv[MAXDIM]; Integer slo_rcv[MAXDIM], shi_rcv[MAXDIM]; Integer plo_rcv[MAXDIM], phi_rcv[MAXDIM]; Integer ld_loc[MAXDIM], ld_rem[MAXDIM]; int msglen; int stride_snd[MAXDIM], stride_rcv[MAXDIM],count[MAXDIM]; int stride_rem[MAXDIM]; int flag1=0, flag2=0, sprocflag, rprocflag; char *ptr_snd, *ptr_rcv; char /* *ptr_loc,*/ *ptr_rem; char send_name[32], rcv_name[32]; void *snd_ptr, *rcv_ptr, *snd_ptr_orig, *rcv_ptr_orig; Integer me = pnga_nodeid(); Integer p_handle, wproc; Integer *_ga_map = NULL; Integer *_ga_proclist = NULL; /* This routine makes use of the shift algorithm to update data in the * ghost cells bounding the local block of visible data. The shift * algorithm starts by updating the blocks of data along the first * dimension by grabbing a block of data that is width[0] deep but * otherwise matches the dimensions of the data residing on the * calling processor. The update of the second dimension, however, * grabs a block that is width[1] deep in the second dimension but is * ldim0 + 2*width[0] in the first dimensions where ldim0 is the * size of the visible data along the first dimension. The remaining * dimensions are left the same. For the next update, the width of the * second dimension is also increased by 2*width[1] and so on. This * algorith makes use of the fact that data for the dimensions that * have already been updated is available on each processor and can be * used in the updates of subsequent dimensions. The total number of * separate updates is 2*ndim, an update in the negative and positive * directions for each dimension. * * This implementation make use of a combination of explicit message * passing between processors on different nodes and shared memory * copies with an additional flag between processors on the same node * to perform the update. Separate message types for the messages and * the use of the additional flag are for the updates in each * coordinate direction are used to maintain synchronization locally * and to guarantee that the data is present before the updates in a * new coordinate direction take place. * * To perform the update, this routine makes use of several copies of * indices marking the upper and lower limits of data. Indices * beginning with the character "p" are relative indices marking the * location of the data set relative to the origin the local patch of * the global array, all other indices are in absolute coordinates and * mark locations in the total global array. The indices used by this * routine are described below. * * lo_loc[], hi_loc[]: The lower and upper indices of the visible * block of data held by the calling processor. * * lo_rcv[], hi_rcv[]: The lower and upper indices of the blocks * of data that will be either sent to or received from a remote * processor. These indices are NOT corrected for wrap-around * (periodic) boundary conditions so they can be negative or greater * than the array dimension values held in dims[]. * * slo_rcv[], shi_rcv[]: Similar to lo_rcv[] and hi_rcv[], except * that these indices have been corrected for wrap-around * boundary conditions. * * plo_rcv[], phi_rcv[]: The local indices of the local data patch * that receive that message from the remote processor. * * plo_snd[], phi_snd[]: The local indices of the data patch * that will be sent to the remote processor. Note that the * dimensions of the patches represented by plo_rec[], plo_rec[] and * plo_snd[], phi_snd[] must be the same. * * tlo_rem[], thi_rem[]: The indices of the locally held visible * portion of the global array on the remote processor that will be * receiving the data using a shared memory copy. * * plo_rem[], phi_rem[]: The local indices of the coordinate patch * that will be put on the remote processor using a shared memory * copy. */ /* if global array has no ghost cells, just return */ if (!pnga_has_ghosts(g_a)) return TRUE; size = GA[handle].elemsize; ndim = GA[handle].ndim; p_handle = GA[handle].p_handle; /* initialize range increments and get array dimensions */ for (idx=0; idx < ndim; idx++) { increment[idx] = 0; width[idx] = (Integer)GA[handle].width[idx]; dims[idx] = (Integer)GA[handle].dims[idx]; } /* Check to make sure that global array is well-behaved (all processors have data and the width of the data in each dimension is greater than the corresponding value in width[]. */ if (!gai_check_ghost_distr(g_a)) return FALSE; msgcnt = 0; /* Get pointer to local memory */ /*ptr_loc = GA[handle].ptr[me];*/ /* obtain range of data that is held by local processor */ pnga_distribution(g_a,me,lo_loc,hi_loc); /* Get indices of processor in virtual grid */ pnga_proc_topology(g_a, me, index); /* Try to find maximum size of message that will be sent during * update operations and use this to allocate memory for message * passing buffers. */ buftot = 1; for (i=0; i= 0) { wproc = PGRP_LIST[p_handle].inv_map_proc_list[wproc]; } rprocflag = ARMCI_Same_node(wproc); proc_rem_snd = _ga_proclist[0]; /* Find processor from which data will be received */ for (i = 0; i < ndim; i++) { if (i == idx) { lo_rcv[i] = hi_loc[i] + 1; hi_rcv[i] = hi_loc[i] + width[i]; } else { lo_rcv[i] = lo_loc[i]; hi_rcv[i] = hi_loc[i]; } } /* Account for boundaries, if necessary. */ for (i=0; i dims[i]) { slo_rcv[i] = 1; shi_rcv[i] = width[i]; } else { slo_rcv[i] = lo_rcv[i]; shi_rcv[i] = hi_rcv[i]; } } else { slo_rcv[i] = lo_rcv[i]; shi_rcv[i] = hi_rcv[i]; } } /* locate processor with this data */ if (!pnga_locate_region(g_a, slo_rcv, shi_rcv, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), slo_rcv, shi_rcv, g_a); wproc = _ga_proclist[0]; if (p_handle >= 0) { wproc = PGRP_LIST[p_handle].inv_map_proc_list[wproc]; } sprocflag = ARMCI_Same_node(wproc); proc_rem_rcv = _ga_proclist[0]; pnga_distribution(g_a, proc_rem_rcv, tlo_rem, thi_rem); /* Get actual coordinates of chunk of data that will be sent to * remote processor as well as coordinates of the array space that * will receive data from remote processor. */ for (i = 0; i < ndim; i++) { if (increment[i] == 0) { if (i == idx) { plo_snd[i] = width[i]; phi_snd[i] = 2*width[i] - 1; plo_rcv[i] = hi_loc[i] - lo_loc[i] + width[i] + 1; phi_rcv[i] = hi_loc[i] - lo_loc[i] + 2*width[i]; plo_rem[i] = thi_rem[i] - tlo_rem[i] + width[i] + 1; /*phi_rem[i] = thi_rem[i] - tlo_rem[i] + 2*width[i];*/ } else { plo_snd[i] = width[i]; phi_snd[i] = hi_loc[i] - lo_loc[i] + width[i]; plo_rcv[i] = width[i]; phi_rcv[i] = hi_loc[i] - lo_loc[i] + width[i]; plo_rem[i] = width[i]; /*phi_rem[i] = thi_rem[i] - tlo_rem[i] + width[i];*/ } } else { plo_snd[i] = 0; phi_snd[i] = hi_loc[i] - lo_loc[i] + increment[i]; plo_rcv[i] = 0; phi_rcv[i] = hi_loc[i] - lo_loc[i] + increment[i]; plo_rem[i] = 0; /*phi_rem[i] = thi_rem[i] - tlo_rem[i] + increment[i];*/ } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_snd, &ptr_snd, ld_loc); gam_LocationWithGhosts(me, handle, plo_rcv, &ptr_rcv, ld_loc); gam_LocationWithGhosts(proc_rem_snd, handle, plo_rem, &ptr_rem, ld_rem); /* Evaluate strides for send and receive */ gam_setstride(ndim, size, ld_loc, ld_loc, stride_rcv, stride_snd); gam_setstride(ndim, size, ld_loc, ld_rem, stride_rem, stride_snd); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_rcv, phi_rcv, count); gam_CountElems(ndim, plo_snd, phi_snd, &length); length *= size; count[0] *= size; /* If we are sending data to another node, then use message passing */ if (!rprocflag) { /* Fill send buffer with data. */ armci_write_strided(ptr_snd, (int)ndim-1, stride_snd, count, snd_ptr); } /* Send Messages. If processor has odd index in direction idx, it * sends message first, if processor has even index it receives * message first. Then process is reversed. Also need to account * for whether or not there are an odd number of processors along * update direction. */ if (p_handle >= 0) { proc_rem_snd = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem_snd]; proc_rem_rcv = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem_rcv]; } if (GA[handle].nblock[idx]%2 == 0) { if (index[idx]%2 != 0 && !rprocflag) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } else if (index[idx]%2 == 0 && !sprocflag) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } if (rprocflag) { #if 0 ARMCI_PutS(ptr_snd, stride_snd, ptr_rem, stride_rem, count, ndim- 1, proc_rem_snd); /* Send signal to remote processor that data transfer has been completed. */ bytes = sizeof(int); ARMCI_Put(GA_Update_Signal, GA_Update_Flags[proc_rem_snd]+msgcnt, bytes, proc_rem_snd); #else ARMCI_PutS_flag(ptr_snd, stride_snd, ptr_rem, stride_rem, count, (int)(ndim-1), GA_Update_Flags[proc_rem_snd]+msgcnt, *GA_Update_Signal, (int)proc_rem_snd); #endif } if (index[idx]%2 != 0 && !sprocflag) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } else if (index[idx]%2 == 0 && !rprocflag) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } } else { /* account for wrap-around boundary condition, if necessary */ pmax = GA[handle].nblock[idx] - 1; if (index[idx]%2 != 0 && !rprocflag) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } else if (index[idx]%2 == 0 && index[idx] != pmax && !sprocflag) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } if (rprocflag) { #if 0 ARMCI_PutS(ptr_snd, stride_snd, ptr_rem, stride_rem, count, ndim- 1, proc_rem_snd); /* Send signal to remote processor that data transfer has been completed. */ bytes = sizeof(int); ARMCI_Put(GA_Update_Signal, GA_Update_Flags[proc_rem_snd]+msgcnt, bytes, proc_rem_snd); #else ARMCI_PutS_flag(ptr_snd, stride_snd, ptr_rem, stride_rem, count, (int)(ndim-1), GA_Update_Flags[proc_rem_snd]+msgcnt, *GA_Update_Signal, (int)proc_rem_snd); #endif } if (index[idx]%2 != 0 && !sprocflag) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } else if (index[idx]%2 == 0 && index[idx] != 0 && !rprocflag) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } /* make up for odd processor at end of string */ if (index[idx] == 0 && !rprocflag) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } if (index[idx] == pmax && !sprocflag) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } } if (sprocflag) { flag1 = 1; } else { flag1 = 0; } msgcnt++; /* copy data back into global array */ if (!sprocflag) { armci_read_strided(ptr_rcv, (int)ndim-1, stride_rcv, count, rcv_ptr); } /* Find parameters for message in positive direction. */ get_remote_block_pos(idx, ndim, lo_loc, hi_loc, slo_rcv, shi_rcv, dims, width); /* locate processor with this data */ if (!pnga_locate_region(g_a, slo_rcv, shi_rcv, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), slo_rcv, shi_rcv, g_a); wproc = _ga_proclist[0]; if (p_handle >= 0) { wproc = PGRP_LIST[p_handle].inv_map_proc_list[wproc]; } rprocflag = ARMCI_Same_node(wproc); proc_rem_snd = _ga_proclist[0]; /* Find processor from which data will be recieved */ for (i = 0; i < ndim; i++) { if (i == idx) { lo_rcv[i] = lo_loc[i] - width[i]; hi_rcv[i] = lo_loc[i] - 1; } else { lo_rcv[i] = lo_loc[i]; hi_rcv[i] = hi_loc[i]; } } /* Account for boundaries, if necessary. */ for (i=0; i= 0) { wproc = PGRP_LIST[p_handle].inv_map_proc_list[wproc]; } sprocflag = ARMCI_Same_node(wproc); proc_rem_rcv = _ga_proclist[0]; pnga_distribution(g_a, proc_rem_rcv, tlo_rem, thi_rem); /* Get actual coordinates of chunk of data that will be sent to * remote processor as well as coordinates of the array space that * will receive data from remote processor. */ for (i = 0; i < ndim; i++) { if (increment[i] == 0) { if (i == idx) { plo_snd[i] = hi_loc[i] - lo_loc[i] + 1; phi_snd[i] = hi_loc[i] - lo_loc[i] + width[i]; plo_rcv[i] = 0; phi_rcv[i] = width[i] - 1; plo_rem[i] = 0; /*phi_rem[i] = width[i] - 1;*/ } else { plo_snd[i] = width[i]; phi_snd[i] = hi_loc[i] - lo_loc[i] + width[i]; plo_rcv[i] = width[i]; phi_rcv[i] = hi_loc[i] - lo_loc[i] + width[i]; plo_rem[i] = width[i]; /*phi_rem[i] = thi_rem[i] - tlo_rem[i] + width[i];*/ } } else { plo_snd[i] = 0; phi_snd[i] = hi_loc[i] - lo_loc[i] + increment[i]; plo_rcv[i] = 0; phi_rcv[i] = hi_loc[i] - lo_loc[i] + increment[i]; plo_rem[i] = 0; /*phi_rem[i] = thi_rem[i] - tlo_rem[i] + increment[i];*/ } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_snd, &ptr_snd, ld_loc); gam_LocationWithGhosts(me, handle, plo_rcv, &ptr_rcv, ld_loc); gam_LocationWithGhosts(proc_rem_snd, handle, plo_rem, &ptr_rem, ld_rem); /* Evaluate strides for send and recieve */ gam_setstride(ndim, size, ld_loc, ld_loc, stride_rcv, stride_snd); gam_setstride(ndim, size, ld_loc, ld_rem, stride_rem, stride_snd); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_rcv, phi_rcv, count); gam_CountElems(ndim, plo_snd, phi_snd, &length); length *= size; count[0] *= size; /* if we are sending data to another node, use message passing */ if (!rprocflag) { /* Fill send buffer with data. */ armci_write_strided(ptr_snd, (int)ndim-1, stride_snd, count, snd_ptr); } /* Send Messages. If processor has odd index in direction idx, it * sends message first, if processor has even index it receives * message first. Then process is reversed. Also need to account * for whether or not there are an odd number of processors along * update direction. */ if (p_handle >= 0) { proc_rem_snd = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem_snd]; proc_rem_rcv = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem_rcv]; } if (GA[handle].nblock[idx]%2 == 0) { if (index[idx]%2 != 0 && !rprocflag) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } else if (index[idx]%2 == 0 && !sprocflag) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } if (rprocflag) { #if 0 ARMCI_PutS(ptr_snd, stride_snd, ptr_rem, stride_rem, count, ndim- 1, proc_rem_snd); /* Send signal to remote processor that data transfer has been completed. */ bytes = sizeof(int); ARMCI_Put(GA_Update_Signal, GA_Update_Flags[proc_rem_snd]+msgcnt, bytes, proc_rem_snd); #else ARMCI_PutS_flag(ptr_snd, stride_snd, ptr_rem, stride_rem, count, (int)(ndim-1), GA_Update_Flags[proc_rem_snd]+msgcnt, *GA_Update_Signal, (int)proc_rem_snd); #endif } if (index[idx]%2 != 0 && !sprocflag) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } else if (index[idx]%2 == 0 && !rprocflag) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } } else { /* account for wrap-around boundary condition, if necessary */ pmax = GA[handle].nblock[idx] - 1; if (index[idx]%2 != 0 && !rprocflag) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } else if (index[idx]%2 == 0 && index[idx] != 0 && !sprocflag) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } if (rprocflag) { #if 0 ARMCI_PutS(ptr_snd, stride_snd, ptr_rem, stride_rem, count, ndim- 1, proc_rem_snd); /* Send signal to remote processor that data transfer has been completed. */ bytes = sizeof(int); ARMCI_Put(GA_Update_Signal, GA_Update_Flags[proc_rem_snd]+msgcnt, bytes, proc_rem_snd); #else ARMCI_PutS_flag(ptr_snd, stride_snd, ptr_rem, stride_rem, count, (int)(ndim-1), GA_Update_Flags[proc_rem_snd]+msgcnt, *GA_Update_Signal, (int)proc_rem_snd); #endif } if (index[idx]%2 != 0 && !sprocflag) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } else if (index[idx]%2 == 0 && index[idx] != pmax && !rprocflag) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } /* make up for odd processor at end of string */ if (index[idx] == pmax && !rprocflag) { armci_msg_snd(msgcnt, snd_ptr, length, proc_rem_snd); } if (index[idx] == 0 && !sprocflag) { armci_msg_rcv(msgcnt, rcv_ptr, bufsize, &msglen, proc_rem_rcv); } } /* copy data back into global array */ if (!sprocflag) { armci_read_strided(ptr_rcv, (int)ndim-1, stride_rcv, count, rcv_ptr); } if (sprocflag) { flag2 = 1; } else { flag2 = 0; } msgcnt++; } /* check to make sure any outstanding puts have showed up */ waitformixedflags(flag1, flag2, GA_Update_Flags[GAme]+msgcnt-2, GA_Update_Flags[GAme]+msgcnt-1); /* update increment array */ increment[idx] = 2*width[idx]; } pnga_free(rcv_ptr_orig); pnga_free(snd_ptr_orig); /* set update flags to zero for next operation */ for (idx=0; idx < 2*ndim; idx++) { GA_Update_Flags[GAme][idx] = 0; } free(_ga_map); free(_ga_proclist); return TRUE; } /*\ UPDATE GHOST CELLS OF GLOBAL ARRAY USING GET CALLS \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_update7_ghosts = pnga_update7_ghosts #endif logical pnga_update7_ghosts(Integer g_a) { Integer idx, ipx, np, handle=GA_OFFSET + g_a, proc_rem; Integer ntot, mask[MAXDIM]; Integer size, ndim, i, itmp; Integer width[MAXDIM], dims[MAXDIM]; Integer lo_loc[MAXDIM], hi_loc[MAXDIM]; Integer plo_loc[MAXDIM], phi_loc[MAXDIM]; Integer tlo_rem[MAXDIM], thi_rem[MAXDIM]; Integer plo_rem[MAXDIM]; Integer ld_loc[MAXDIM], ld_rem[MAXDIM]; logical mask0; int stride_loc[MAXDIM], stride_rem[MAXDIM],count[MAXDIM]; char *ptr_loc, *ptr_rem; Integer me = pnga_nodeid(); Integer p_handle; Integer *_ga_map = NULL; Integer *_ga_proclist = NULL; /* if global array has no ghost cells, just return */ if (!pnga_has_ghosts(g_a)) { return TRUE; } size = GA[handle].elemsize; ndim = GA[handle].ndim; p_handle = GA[handle].p_handle; /* initialize ghost cell widths and get array dimensions */ for (idx=0; idx < ndim; idx++) { width[idx] = (Integer)GA[handle].width[idx]; dims[idx] = (Integer)GA[handle].dims[idx]; } /* Check to make sure that global array is well-behaved (all processors have data and the width of the data in each dimension is greater than the corresponding value in width[]). */ if (!gai_check_ghost_distr(g_a)) return FALSE; /* Get pointer to local memory */ ptr_loc = GA[handle].ptr[me]; /* obtain range of data that is held by local processor */ pnga_distribution(g_a,me,lo_loc,hi_loc); /* evaluate total number of GET operations that will be required */ ntot = 1; for (idx=0; idx < ndim; idx++) ntot *= 3; /* Loop over all GET operations. The operation corresponding to the mask of all zeros is left out. */ for (ipx=0; ipx < ntot; ipx++) { /* Convert ipx to corresponding mask values */ itmp = ipx; mask0 = TRUE; for (idx = 0; idx < ndim; idx++) { i = itmp%3; mask[idx] = i-1; if (mask[idx] != 0) mask0 = FALSE; itmp = (itmp-i)/3; } if (mask0) continue; /* check to see if ghost cell block has zero elements*/ mask0 = FALSE; itmp = 0; for (idx = 0; idx < ndim; idx++) { if (mask[idx] != 0 && width[idx] == 0) mask0 = TRUE; if (mask[idx] != 0) itmp++; } /*if (itmp>1) mask0 = TRUE; */ if (mask0) continue; /* Now that mask has been determined, find data that is to be moved * and identify processor from which it is coming. Wrap boundaries * around, if necessary */ for (idx = 0; idx < ndim; idx++) { if (mask[idx] == 0) { tlo_rem[idx] = lo_loc[idx]; thi_rem[idx] = hi_loc[idx]; } else if (mask[idx] == -1) { if (lo_loc[idx] > 1) { tlo_rem[idx] = lo_loc[idx]-width[idx]; thi_rem[idx] = lo_loc[idx]-1; } else { tlo_rem[idx] = dims[idx]-width[idx]+1; thi_rem[idx] = dims[idx]; } } else if (mask[idx] == 1) { if (hi_loc[idx] < dims[idx]) { tlo_rem[idx] = hi_loc[idx] + 1; thi_rem[idx] = hi_loc[idx] + width[idx]; } else { tlo_rem[idx] = 1; thi_rem[idx] = width[idx]; } } else { fprintf(stderr,"Illegal mask value found\n"); } } _ga_map = malloc((GAnproc*2*MAXDIM+1)*sizeof(Integer)); if(!_ga_map) pnga_error("pnga_update7_ghosts:malloc failed (_ga_map)",0); _ga_proclist = malloc(GAnproc*sizeof(Integer)); if(!_ga_proclist) pnga_error("pnga_update7_ghosts:malloc failed (_ga_proclist)",0); /* Locate remote processor to which data must be sent */ if (!pnga_locate_region(g_a, tlo_rem, thi_rem, _ga_map, _ga_proclist, &np)) ga_RegionError(pnga_ndim(g_a), tlo_rem, thi_rem, g_a); if (np > 1) { fprintf(stderr,"More than one remote processor found\n"); } /* Remote processor has been identified, now get ready to send data to it. Start by getting distribution on remote processor.*/ proc_rem = _ga_proclist[0]; pnga_distribution(g_a, proc_rem, tlo_rem, thi_rem); for (idx = 0; idx < ndim; idx++) { if (mask[idx] == 0) { plo_loc[idx] = width[idx]; phi_loc[idx] = hi_loc[idx]-lo_loc[idx]+width[idx]; plo_rem[idx] = plo_loc[idx]; } else if (mask[idx] == -1) { plo_loc[idx] = 0; phi_loc[idx] = width[idx]-1; plo_rem[idx] = thi_rem[idx]-tlo_rem[idx]+1; } else if (mask[idx] == 1) { plo_loc[idx] = hi_loc[idx]-lo_loc[idx]+width[idx]+1; phi_loc[idx] = hi_loc[idx]-lo_loc[idx]+2*width[idx]; plo_rem[idx] = width[idx]; } } /* Get pointer to local data buffer and remote data buffer as well as lists of leading dimenstions */ gam_LocationWithGhosts(me, handle, plo_loc, &ptr_loc, ld_loc); gam_LocationWithGhosts(proc_rem, handle, plo_rem, &ptr_rem, ld_rem); /* Evaluate strides on local and remote processors */ gam_setstride(ndim, size, ld_loc, ld_rem, stride_rem, stride_loc); /* Compute the number of elements in each dimension and store result in count. Scale the first element in count by the element size. */ gam_ComputeCount(ndim, plo_loc, phi_loc, count); count[0] *= size; if (p_handle >= 0) { proc_rem = PGRP_LIST[p_handle].inv_map_proc_list[proc_rem]; } /* put data on remote processor */ /* ARMCI_GetS(ptr_rem, stride_rem, ptr_loc, stride_loc, count, (int)(ndim - 1), (int)proc_rem); */ ARMCI_NbGetS(ptr_rem, stride_rem, ptr_loc, stride_loc, count, (int)(ndim - 1), (int)proc_rem, NULL); } ARMCI_WaitAll(); free(_ga_map); free(_ga_proclist); return TRUE; } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_ghost_barrier = pnga_ghost_barrier #endif void pnga_ghost_barrier() { armci_msg_barrier(); } /*\ UPDATE THE GHOST CELLS ON A PROCESSOR IN A SPECIFIC DIRECTION * USING NON-BLOCKING GET CALLS \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_nbget_ghost_dir = pnga_nbget_ghost_dir #endif void pnga_nbget_ghost_dir(Integer g_a, Integer *mask, Integer *nbhandle) { Integer handle = GA_OFFSET + g_a; Integer lo_loc[MAXDIM], hi_loc[MAXDIM], lo_rem[MAXDIM], hi_rem[MAXDIM]; Integer subscript[MAXDIM], ld[MAXDIM]; Integer i, ndim, dim, width; char *ptr_loc; Integer me = pnga_nodeid(); /*Integer p_handle;*/ ndim = GA[handle].ndim; /*p_handle = GA[handle].p_handle;*/ /* check mask to see that it corresponds to a valid direction */ for (i=0; i #endif #if HAVE_STRING_H # include #endif #include "message.h" #include "globalp.h" #include "armci.h" #include "ga_iterator.h" #include "ga-papi.h" #include "ga-wapi.h" #include "base.h" // workaround https://github.com/GlobalArrays/ga/issues/163 // by using ARMCI_Malloc_local instead of ga_malloc -> MA #define GA_TRANSPOSE_USE_ARMCI_MEM 1 #ifdef MSG_COMMS_MPI extern ARMCI_Group* ga_get_armci_group_(int); #endif /* work arrays used in all routines */ static Integer one_arr[MAXDIM]={1,1,1,1,1,1,1}; #define GET_ELEMS(ndim,lo,hi,ld,pelems){\ int _i;\ for(_i=0, *pelems = hi[ndim-1]-lo[ndim-1]+1; _i< ndim-1;_i++) {\ if(ld[_i] != (hi[_i]-lo[_i]+1)) pnga_error("layout problem",_i);\ *pelems *= hi[_i]-lo[_i]+1;\ }\ } #define GET_ELEMS_W_GHOSTS(ndim,lo,hi,ld,pelems){\ int _i;\ for(_i=0, *pelems = hi[ndim-1]-lo[ndim-1]+1; _i< ndim-1;_i++) {\ if(ld[_i] < (hi[_i]-lo[_i]+1))\ pnga_error("layout problem with ghosts",_i);\ *pelems *= hi[_i]-lo[_i]+1;\ }\ } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_zero = pnga_zero #endif void pnga_zero(Integer g_a) { Integer ndim, type, me, elems, p_handle; Integer num_blocks; void *ptr; Integer _dims[MAXDIM]; Integer _ld[MAXDIM-1]; Integer _lo[MAXDIM]; Integer _hi[MAXDIM]; /*register Integer i;*/ int local_sync_begin,local_sync_end; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ p_handle = pnga_get_pgroup(g_a); if(local_sync_begin) pnga_pgroup_sync(p_handle); me = pnga_pgroup_nodeid(p_handle); pnga_check_handle(g_a, "ga_zero"); num_blocks = pnga_total_blocks(g_a); pnga_inquire(g_a, &type, &ndim, _dims); if (num_blocks < 0) { pnga_distribution(g_a, me, _lo, _hi); if ( _lo[0]> 0 ){ /* base index is 1: we get 0 if no elements stored on p */ if (pnga_has_ghosts(g_a)) { pnga_zero_patch(g_a,_lo,_hi); return; } pnga_access_ptr(g_a, _lo, _hi, &ptr, _ld); GET_ELEMS(ndim,_lo,_hi,_ld,&elems); /* switch (type){ */ /* int *ia; */ /* double *da; */ /* float *fa; */ /* long *la; */ /* long long *lla; */ /* case C_INT: */ /* ia = (int*)ptr; */ /* for(i=0;i0){ pnga_access_ptr(g_a, _lo, _hi, &ptr_a, _ld); pnga_put(g_b, _lo, _hi, ptr_a, _ld); } } else { if (!pnga_uses_proc_grid(g_a)) { for (i=me_a; i0) { pnga_access_block_ptr(g_a, i, &ptr_a, _ld); pnga_put(g_b, _lo, _hi, ptr_a, _ld); } } } else { Integer proc_index[MAXDIM], index[MAXDIM]; Integer topology[MAXDIM], chk; pnga_get_proc_index(g_a, me_a, proc_index); pnga_get_proc_index(g_a, me_a, index); pnga_get_block_info(g_a, blocks, block_dims); pnga_get_proc_grid(g_a, topology); while (index[ndim-1] < blocks[ndim-1]) { /* find bounding coordinates of block */ chk = 1; for (i = 0; i < ndim; i++) { _lo[i] = index[i]*block_dims[i]+1; _hi[i] = (index[i] + 1)*block_dims[i]; if (_hi[i] > _dims[i]) _hi[i] = _dims[i]; if (_hi[i] < _lo[i]) chk = 0; } if (chk) { pnga_access_block_grid_ptr(g_a, index, &ptr_a, _ld); pnga_put(g_b, _lo, _hi, ptr_a, _ld); } /* increment index to get next block on processor */ index[0] += topology[0]; for (i = 0; i < ndim; i++) { if (index[i] >= blocks[i] && i0){ pnga_access_ptr(g_b, _lo, _hi, &ptr_b, _ld); pnga_get(g_a, _lo, _hi, ptr_b, _ld); } } else { if (!pnga_uses_proc_grid(g_a)) { for (i=me_b; i0) { pnga_access_block_ptr(g_b, i, &ptr_b, _ld); pnga_get(g_a, _lo, _hi, ptr_b, _ld); } } } else { Integer proc_index[MAXDIM], index[MAXDIM]; Integer topology[MAXDIM], chk; pnga_get_proc_index(g_b, me_b, proc_index); pnga_get_proc_index(g_b, me_b, index); pnga_get_block_info(g_b, blocks, block_dims); pnga_get_proc_grid(g_b, topology); while (index[ndim-1] < blocks[ndim-1]) { /* find bounding coordinates of block */ chk = 1; for (i = 0; i < ndim; i++) { _lo[i] = index[i]*block_dims[i]+1; _hi[i] = (index[i] + 1)*block_dims[i]; if (_hi[i] > _dims[i]) _hi[i] = _dims[i]; if (_hi[i] < _lo[i]) chk = 0; } if (chk) { pnga_access_block_grid_ptr(g_b, index, &ptr_b, _ld); pnga_get(g_a, _lo, _hi, ptr_b, _ld); } /* increment index to get next block on processor */ index[0] += topology[0]; for (i = 0; i < ndim; i++) { if (index[i] >= blocks[i] && i0) { pnga_access_ptr(g_b, _lo, _hi, &ptr_b, _ld); pnga_get(g_a, _lo, _hi, ptr_b, _ld); } } else { /* source array is distributed and destination array is mirrored */ pnga_zero(g_b); pnga_distribution(g_a, me_a, _lo, _hi); if (_lo[0] > 0) { pnga_access_ptr(g_a, _lo, _hi, &ptr_a, _ld); pnga_put(g_b, _lo, _hi, ptr_a, _ld); } pnga_merge_mirrored(g_b); } } if(local_sync_end) { if (anproc <= bnproc) { pnga_pgroup_sync(a_grp); } else if (a_grp == pnga_pgroup_get_world() && b_grp == pnga_pgroup_get_world()) { pnga_sync(); } else { pnga_pgroup_sync(b_grp); } } } /*\ internal version of dot product \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_dot = pnga_dot #endif void pnga_dot(int Type, Integer g_a, Integer g_b, void *value) { Integer ndim=0, type=0, atype=0, me=0, elems=0, elemsb=0; register Integer i=0; int isum=0; long lsum=0; long long llsum=0; DoubleComplex zsum ={0.,0.}; SingleComplex csum ={0.,0.}; float fsum=0.0; void *ptr_a=NULL, *ptr_b=NULL; int alen=0; Integer a_grp=0, b_grp=0; Integer num_blocks_a=0, num_blocks_b=0; Integer _dims[MAXDIM]; Integer _ld[MAXDIM-1]; Integer _lo[MAXDIM]; Integer _hi[MAXDIM]; Integer andim, adims[MAXDIM]; Integer bndim, bdims[MAXDIM]; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ a_grp = pnga_get_pgroup(g_a); b_grp = pnga_get_pgroup(g_b); if (a_grp != b_grp) pnga_error("Both arrays must be defined on same group",0L); me = pnga_pgroup_nodeid(a_grp); /* Check to see if either GA is block cyclic distributed */ num_blocks_a = pnga_total_blocks(g_a); num_blocks_b = pnga_total_blocks(g_b); if (num_blocks_a >= 0 || num_blocks_b >= 0) { pnga_inquire(g_a, &type, &andim, adims); pnga_inquire(g_b, &type, &bndim, bdims); pnga_dot_patch(g_a, "n", one_arr, adims, g_b, "n", one_arr, bdims, value); return; } if(pnga_compare_distr(g_a,g_b) == FALSE || pnga_has_ghosts(g_a) || pnga_has_ghosts(g_b)) { /* distributions not identical */ pnga_inquire(g_a, &type, &andim, adims); pnga_inquire(g_b, &type, &bndim, bdims); pnga_dot_patch(g_a, "n", one_arr, adims, g_b, "n", one_arr, bdims, value); return; } pnga_pgroup_sync(a_grp); pnga_inquire(g_a, &type, &ndim, _dims); if(type != Type) pnga_error("type not correct", g_a); pnga_distribution(g_a, me, _lo, _hi); if(_lo[0]>0){ pnga_access_ptr(g_a, _lo, _hi, &ptr_a, _ld); if (pnga_has_ghosts(g_a)) { GET_ELEMS_W_GHOSTS(ndim,_lo,_hi,_ld,&elems); } else { GET_ELEMS(ndim,_lo,_hi,_ld,&elems); } } if(g_a == g_b){ elemsb = elems; ptr_b = ptr_a; }else { pnga_inquire(g_b, &type, &ndim, _dims); if(type != Type) pnga_error("type not correct", g_b); pnga_distribution(g_b, me, _lo, _hi); if(_lo[0]>0){ pnga_access_ptr(g_b, _lo, _hi, &ptr_b, _ld); if (pnga_has_ghosts(g_b)) { GET_ELEMS_W_GHOSTS(ndim,_lo,_hi,_ld,&elemsb); } else { GET_ELEMS(ndim,_lo,_hi,_ld,&elemsb); } } } if(elems!= elemsb)pnga_error("inconsistent number of elements",elems-elemsb); /* compute "local" contribution to the dot product */ switch (type){ int *ia, *ib; double *da,*db; float *fa, *fb; long *la,*lb; long long *lla,*llb; case C_INT: ia = (int*)ptr_a; ib = (int*)ptr_b; for(i=0;i0){ pnga_release(g_a, _lo, _hi); if(g_a != g_b)pnga_release(g_b, _lo, _hi); } /*convert from C data type to ARMCI type */ switch(type) { case C_FLOAT: atype=ARMCI_FLOAT; break; case C_DBL: atype=ARMCI_DOUBLE; break; case C_INT: atype=ARMCI_INT; break; case C_LONG: atype=ARMCI_LONG; break; case C_LONGLONG: atype=ARMCI_LONG_LONG; break; case C_DCPL: atype=ARMCI_DOUBLE; break; case C_SCPL: atype=ARMCI_FLOAT; break; default: pnga_error("pnga_dot: type not supported",type); } if (pnga_is_mirrored(g_a) && pnga_is_mirrored(g_b)) { armci_msg_gop_scope(SCOPE_NODE,value,alen,"+",atype); } else { if (a_grp == -1) { armci_msg_gop_scope(SCOPE_ALL,value,alen,"+",atype); #ifdef MSG_COMMS_MPI } else { armci_msg_group_gop_scope(SCOPE_ALL,value,alen,"+",atype, ga_get_armci_group_((int)a_grp)); #endif } } } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_scale = pnga_scale #endif void pnga_scale(Integer g_a, void* alpha) { Integer ndim, type, me, elems, grp_id; register Integer i; Integer num_blocks; void *ptr; int local_sync_begin,local_sync_end; Integer _dims[MAXDIM]; Integer _ld[MAXDIM-1]; Integer _lo[MAXDIM]; Integer _hi[MAXDIM]; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ grp_id = pnga_get_pgroup(g_a); if(local_sync_begin)pnga_pgroup_sync(grp_id); me = pnga_pgroup_nodeid(grp_id); pnga_check_handle(g_a, "ga_scale"); num_blocks = pnga_total_blocks(g_a); pnga_inquire(g_a, &type, &ndim, _dims); if (num_blocks < 0) { pnga_distribution(g_a, me, _lo, _hi); if (pnga_has_ghosts(g_a)) { pnga_scale_patch(g_a, _lo, _hi, alpha); return; } if ( _lo[0]> 0 ){ /* base index is 1: we get 0 if no elements stored on p */ pnga_access_ptr(g_a, _lo, _hi, &ptr, _ld); GET_ELEMS(ndim,_lo,_hi,_ld,&elems); switch (type){ int *ia; double *da; DoubleComplex *ca, scale; SingleComplex *cfa, cfscale; long *la; long long *lla; float *fa; case C_INT: ia = (int*)ptr; for(i=0;i 0 || pnga_total_blocks(g_b) > 0 || pnga_total_blocks(g_c) > 0) { /* distributions not identical */ pnga_inquire(g_a, &type, &andim, adims); pnga_inquire(g_b, &type, &bndim, bdims); pnga_inquire(g_b, &type, &cndim, cdims); pnga_add_patch(alpha, g_a, one_arr, adims, beta, g_b, one_arr, bdims, g_c, one_arr, cdims); return; } pnga_pgroup_sync(a_grp); pnga_inquire(g_c, &typeC, &ndim, _dims); pnga_distribution(g_c, me, _lo, _hi); if ( _lo[0]>0 ){ pnga_access_ptr(g_c, _lo, _hi, &ptr_c, _ld); GET_ELEMS(ndim,_lo,_hi,_ld,&elems); } if(g_a == g_c){ ptr_a = ptr_c; elemsa = elems; }else { pnga_inquire(g_a, &type, &ndim, _dims); if(type != typeC) pnga_error("types not consistent", g_a); pnga_distribution(g_a, me, _lo, _hi); if ( _lo[0]>0 ){ pnga_access_ptr(g_a, _lo, _hi, &ptr_a, _ld); GET_ELEMS(ndim,_lo,_hi,_ld,&elemsa); } } if(g_b == g_c){ ptr_b = ptr_c; elemsb = elems; }else { pnga_inquire(g_b, &type, &ndim, _dims); if(type != typeC) pnga_error("types not consistent", g_b); pnga_distribution(g_b, me, _lo, _hi); if ( _lo[0]>0 ){ pnga_access_ptr(g_b, _lo, _hi, &ptr_b, _ld); GET_ELEMS(ndim,_lo,_hi,_ld,&elemsb); } } if(elems!= elemsb)pnga_error("inconsistent number of elements a",elems-elemsb); if(elems!= elemsa)pnga_error("inconsistent number of elements b",elems-elemsa); if ( _lo[0]>0 ){ /* operation on the "local" piece of data */ switch(type){ int *ia, *ib, *ic; double *da,*db,*dc; float *fa, *fb, *fc; long *la,*lb,*lc; long long *lla,*llb,*llc; case C_DBL: da = (double*)ptr_a; db = (double*)ptr_b; dc = (double*)ptr_c; for(i=0; i0){ Integer nelem, lob[2], hib[2], nrow, ncol; int i, size=GAsizeofM(atype); nrow = hi[0] -lo[0]+1; ncol = hi[1] -lo[1]+1; nelem = nrow*ncol; lob[0] = lo[1]; lob[1] = lo[0]; hib[0] = hi[1]; hib[1] = hi[0]; /* allocate memory for transposing elements locally */ ptr_tmp = (char *) ga_malloc(nelem, atype, "transpose_tmp"); /* get access to local data */ pnga_access_ptr(g_a, lo, hi, &ptr_a, _ld); for(i = 0; i < ncol; i++){ char *ptr = ptr_tmp + i*size; snga_local_transpose(atype, ptr_a, nrow, ncol*size, ptr); ptr_a += _ld[0]*size; } pnga_release(g_a, lo, hi); pnga_put(g_b, lob, hib, ptr_tmp ,&ncol); ga_free(ptr_tmp); } } else { Integer idx; Integer blocks[MAXDIM], block_dims[MAXDIM]; Integer nelem, lob[2], hib[2], nrow, ncol; int i, size=GAsizeofM(atype); /* allocate memory for transposing elements locally */ pnga_get_block_info(g_a, blocks, block_dims); /* Simple block-cyclic data distribution */ nelem = block_dims[0]*block_dims[1]; ptr_tmp = (char *) ga_malloc(nelem, atype, "transpose_tmp"); if (!pnga_uses_proc_grid(g_a)) { for (idx = me; idx < num_blocks_a; idx += nproc) { pnga_distribution(g_a, idx, lo, hi); pnga_access_block_ptr(g_a, idx, &ptr_a, _ld); nrow = hi[0] -lo[0]+1; ncol = hi[1] -lo[1]+1; nelem = nrow*ncol; lob[0] = lo[1]; lob[1] = lo[0]; hib[0] = hi[1]; hib[1] = hi[0]; for(i = 0; i < ncol; i++){ char *ptr = ptr_tmp + i*size; snga_local_transpose(atype, ptr_a, nrow, ncol*size, ptr); ptr_a += _ld[0]*size; } pnga_put(g_b, lob, hib, ptr_tmp ,&ncol); pnga_release_update_block(g_a, idx); } } else { /* Uses scalapack block-cyclic data distribution */ Integer chk; Integer proc_index[MAXDIM], index[MAXDIM]; Integer topology[MAXDIM], ichk; pnga_get_proc_index(g_a, me, proc_index); pnga_get_proc_index(g_a, me, index); pnga_get_block_info(g_a, blocks, block_dims); pnga_get_proc_grid(g_a, topology); /* Verify that processor has data */ ichk = 1; for (i=0; i= blocks[i]) { ichk = 0; } } if (ichk) { pnga_access_block_grid_ptr(g_a, index, &ptr_a, _ld); while (index[andim-1] < blocks[andim-1]) { /* find bounding coordinates of block */ chk = 1; for (i = 0; i < andim; i++) { lo[i] = index[i]*block_dims[i]+1; hi[i] = (index[i] + 1)*block_dims[i]; if (hi[i] > adims[i]) hi[i] = adims[i]; if (hi[i] < lo[i]) chk = 0; } if (chk) { pnga_access_block_grid_ptr(g_a, index, &ptr_a, _ld); nrow = hi[0] -lo[0]+1; ncol = hi[1] -lo[1]+1; nelem = nrow*ncol; lob[0] = lo[1]; lob[1] = lo[0]; hib[0] = hi[1]; hib[1] = hi[0]; for(i = 0; i < ncol; i++){ char *ptr = ptr_tmp + i*size; snga_local_transpose(atype, ptr_a, nrow, block_dims[0]*size, ptr); ptr_a += _ld[0]*size; } pnga_put(g_b, lob, hib, ptr_tmp ,&block_dims[0]); pnga_release_update_block_grid(g_a, index); } /* increment index to get next block on processor */ index[0] += topology[0]; for (i = 0; i < andim; i++) { if (index[i] >= blocks[i] && i #endif /* * module: global.npatch.c * author: Jialin Ju * description: Implements the n-dimensional patch operations: * - fill patch * - copy patch * - scale patch * - dot patch * - add patch * * DISCLAIMER * * This material was prepared as an account of work sponsored by an * agency of the United States Government. Neither the United States * Government nor the United States Department of Energy, nor Battelle, * nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR * ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, * COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, * SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT * INFRINGE PRIVATELY OWNED RIGHTS. * * * ACKNOWLEDGMENT * * This software and its documentation were produced with United States * Government support under Contract Number DE-AC06-76RLO-1830 awarded by * the United States Department of Energy. The United States Government * retains a paid-up non-exclusive, irrevocable worldwide license to * reproduce, prepare derivative works, perform publicly and display * publicly by or for the US Government, including the right to * distribute to other US Government contractors. */ #if HAVE_MATH_H # include #endif #include "message.h" #include "globalp.h" #include "armci.h" #include "ga_iterator.h" #include "ga-papi.h" #include "ga-wapi.h" #ifdef MSG_COMMS_MPI extern ARMCI_Group* ga_get_armci_group_(int); #endif /********************************************************** * n-dimensional utilities * **********************************************************/ /*\ compute Index from subscript and convert it back to subscript * in another array \*/ static void snga_dest_indices(Integer ndims, Integer *los, Integer *blos, Integer *dimss, Integer ndimd, Integer *lod, Integer *blod, Integer *dimsd) { Integer idx = 0, i, factor=1; for(i=0;i bndim) { ndim = bndim; for(i=ndim; i adims[i]) pnga_error("g_a indices out of range ", 0L); for(i=0; i bdims[i]) pnga_error("g_b indices out of range ", 0L); /* check if numbers of elements in two patches match each other */ atotal = 1; btotal = 1; for(i=0; i= 0 || num_blocks_b >= 0) { if (!(*trans == 'n' || *trans == 'N')) { pnga_error("Transpose option not supported for block-cyclic data", 0L); } if (!snga_test_shape(alo, ahi, blo, bhi, andim, bndim)) { pnga_error("Change in shape not supported for block-cyclic data", 0L); } } if (num_blocks_a < 0 && num_blocks_b <0) { /* now find out cordinates of a patch of g_a that I own */ if (use_put) { pnga_distribution(g_a, me_a, los, his); } else { pnga_distribution(g_b, me_b, los, his); } /* copy my share of data */ if (use_put) { has_intersection = pnga_patch_intersect(alo, ahi, los, his, andim); } else { has_intersection = pnga_patch_intersect(blo, bhi, los, his, bndim); } if(has_intersection){ if (use_put) { pnga_access_ptr(g_a, los, his, &src_data_ptr, ld); } else { pnga_access_ptr(g_b, los, his, &src_data_ptr, ld); } /* calculate the number of elements in the patch that I own */ nelem = 1; for(i=0; i destination[lo:hi] */ if (use_put) { snga_dest_indices(andim, los, alo, ald, bndim, lod, blo, bld); snga_dest_indices(andim, his, alo, ald, bndim, hid, blo, bld); pnga_put(g_b, lod, hid, src_data_ptr, ld); pnga_release(g_a, los, his); } else { snga_dest_indices(bndim, los, blo, bld, andim, lod, alo, ald); snga_dest_indices(bndim, his, blo, bld, andim, hid, alo, ald); pnga_get(g_a, lod, hid, src_data_ptr, ld); pnga_release(g_b, los, his); } /*** due to generality of this transformation scatter is required ***/ } else{ if (use_put) { tmp_ptr = pnga_malloc(nelem, atype, "v"); src_idx_ptr = (Integer*) pnga_malloc((andim*nelem), MT_F_INT, "si"); dst_idx_ptr = (Integer*) pnga_malloc((bndim*nelem), MT_F_INT, "di"); /* calculate the destination indices */ /* given los and his, find indices for each elements * bvalue: starting index in each dimension * bunit: stride in each dimension */ for (i=0; i his[j]) bvalue[j] = los[j]; } } /* index factor: reshaping without transpose */ factor_idx1[0] = 1; for (j=1; j=0; j--) factor_idx2[j] = factor_idx2[j+1] * ald[j+1]; /* data factor */ factor_data[0] = 1; for (j=1; j (..., j, i) */ for (j=(andim-1); j>=0; j--) idx += (src_idx_ptr[i*andim+j] - alo[j]) * factor_idx2[j]; /* convert the one dimensional index to n-dimensional * indices of destination */ for (j=0; j his[j]) bvalue[j] = los[j]; } } /* index factor: reshaping without transpose */ factor_idx1[0] = 1; for (j=1; j=0; j--) factor_idx2[j] = factor_idx2[j+1] * bld[j+1]; /* data factor */ factor_data[0] = 1; for (j=1; j (..., j, i) */ for (j=(andim-1); j>=0; j--) idx += (src_idx_ptr[i*bndim+j] - blo[j]) * factor_idx2[j]; /* convert the one dimensional index to n-dimensional * indices of destination */ for (j=0; j= 0) { #if 1 _iterator_hdl hdl_a; pnga_local_iterator_init(g_a, &hdl_a); while (pnga_local_iterator_next(&hdl_a, los, his, &src_data_ptr, ld)) { /* Copy limits since patch intersect modifies los array */ for (j=0; j < andim; j++) { lod[j] = los[j]; hid[j] = his[j]; } if (pnga_patch_intersect(alo,ahi,los,his,andim)) { offset = 0; last = andim - 1; jtot = 1; for (j=0; j adims[i]) his[i] = adims[i]; /*if (his[i] < los[i]) chk = 0;*/ } /* make temperary copies of los, his since ngai_patch_intersection destroys original versions */ for (j=0; j < andim; j++) { lod[j] = los[j]; hid[j] = his[j]; } if (pnga_patch_intersect(alo,ahi,los,his,andim)) { pnga_access_block_grid_ptr(g_a, index, &src_data_ptr, ld); offset = 0; last = andim - 1; jtot = 1; for (j=0; j= blocks[i] && i= 0) { #if 1 _iterator_hdl hdl_b; pnga_local_iterator_init(g_b, &hdl_b); while (pnga_local_iterator_next(&hdl_b, los, his, &src_data_ptr, ld)) { /* Copy limits since patch intersect modifies los array */ for (j=0; j < andim; j++) { lod[j] = los[j]; hid[j] = his[j]; } if (pnga_patch_intersect(blo,bhi,los,his,andim)) { offset = 0; last = andim - 1; jtot = 1; for (j=0; j bdims[i]) his[i] = bdims[i]; /*if (his[i] < los[i]) chk = 0;*/ } /* make temporory copies of los, his since ngai_patch_intersection destroys original versions */ for (j=0; j < andim; j++) { lod[j] = los[j]; hid[j] = his[j]; } if (pnga_patch_intersect(blo,bhi,los,his,andim)) { pnga_access_block_grid_ptr(g_b, index, &src_data_ptr, ld); offset = 0; last = bndim - 1; jtot = 1; for (j=0; j= blocks[i] && i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) isum += ((int *)A_ptr)[idx+j] * ((int *)B_ptr)[idx+j]; } *(int*)retval += isum; break; case C_DCPL: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) { DoubleComplex a = ((DoubleComplex *)A_ptr)[idx+j]; DoubleComplex b = ((DoubleComplex *)B_ptr)[idx+j]; zsum.real += a.real*b.real - b.imag * a.imag; zsum.imag += a.imag*b.real + b.imag * a.real; } } ((double*)retval)[0] += zsum.real; ((double*)retval)[1] += zsum.imag; break; case C_SCPL: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) { SingleComplex a = ((SingleComplex *)A_ptr)[idx+j]; SingleComplex b = ((SingleComplex *)B_ptr)[idx+j]; csum.real += a.real*b.real - b.imag * a.imag; csum.imag += a.imag*b.real + b.imag * a.real; } } ((float*)retval)[0] += csum.real; ((float*)retval)[1] += csum.imag; break; case C_DBL: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) dsum += ((double*)A_ptr)[idx+j] * ((double*)B_ptr)[idx+j]; } *(double*)retval += dsum; break; case C_FLOAT: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) fsum += ((float *)A_ptr)[idx+j] * ((float *)B_ptr)[idx+j]; } *(float*)retval += fsum; break; case C_LONG: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) lsum += ((long *)A_ptr)[idx+j] * ((long *)B_ptr)[idx+j]; } *(long*)retval += lsum; break; case C_LONGLONG: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) llsum += ((long long *)A_ptr)[idx+j] * ((long long *)B_ptr)[idx+j]; } *(long long*)retval += llsum; break; default: pnga_error("snga_dot_local_patch: type not supported",atype); } } /*\ generic dot product routine \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_dot_patch = pnga_dot_patch #endif void pnga_dot_patch(Integer g_a, char *t_a, Integer *alo, Integer *ahi, Integer g_b, char *t_b, Integer *blo, Integer *bhi, void *retval) { Integer i=0, j=0; Integer compatible=0; Integer atype=0, btype=0, andim=0, adims[MAXDIM], bndim=0, bdims[MAXDIM]; Integer loA[MAXDIM], hiA[MAXDIM], ldA[MAXDIM]; Integer loB[MAXDIM], hiB[MAXDIM], ldB[MAXDIM]; Integer g_A = g_a, g_B = g_b; char *A_ptr=NULL, *B_ptr=NULL; Integer ctype=0; Integer atotal=0, btotal=0; int isum=0, alen=0; long lsum=0; long long llsum=0; double dsum=0; DoubleComplex zsum={0,0}; DoubleComplex csum={0,0}; float fsum=0; Integer me= pnga_nodeid(), temp_created=0; Integer nproc = pnga_nnodes(); Integer num_blocks_a=0, num_blocks_b=0; char *tempname = "temp", transp, transp_a, transp_b; int local_sync_begin=0; Integer a_grp=0, b_grp=0; _iterator_hdl hdl_a, hdl_b; local_sync_begin = _ga_sync_begin; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); a_grp = pnga_get_pgroup(g_a); b_grp = pnga_get_pgroup(g_b); if (a_grp != b_grp) pnga_error("Both arrays must be defined on same group",0L); me = pnga_pgroup_nodeid(a_grp); pnga_inquire(g_a, &atype, &andim, adims); pnga_inquire(g_b, &btype, &bndim, bdims); if(atype != btype ) pnga_error(" type mismatch ", 0L); /* check if patch indices and g_a dims match */ for(i=0; i adims[i]) pnga_error("g_a indices out of range ", g_a); for(i=0; i bdims[i]) pnga_error("g_b indices out of range ", g_b); /* check if numbers of elements in two patches match each other */ atotal = 1; for(i=0; i= 0 || num_blocks_b >= 0) { if (transp_a == 't' || transp_b == 't') pnga_error("transpose not supported for block-cyclic data ", 0); } isum = 0; dsum = 0.; zsum.real = 0.; zsum.imag = 0.; fsum = 0;lsum=0;llsum=0; csum.real = 0.; csum.imag = 0.; switch (atype){ case C_INT: *(int*)retval = isum; alen = 1; break; case C_DCPL: ((double*)retval)[0] = zsum.real; ((double*)retval)[1] = zsum.imag; alen = 2; break; case C_SCPL: ((float*)retval)[0] = csum.real; ((float*)retval)[1] = csum.imag; alen = 2; break; case C_DBL: *(double*)retval = dsum; alen = 1; break; case C_FLOAT: *(float*)retval = fsum; alen = 1; break; case C_LONG: *(long*)retval = lsum; alen = 1; break; case C_LONGLONG: *(long long*)retval = llsum; alen = 1; break; default: pnga_error("snga_dot_local_patch: type not supported",atype); } if (num_blocks_a < 0 && num_blocks_b < 0) { /* find out coordinates of patches of g_A and g_B that I own */ pnga_distribution(g_A, me, loA, hiA); pnga_distribution(g_B, me, loB, hiB); if(pnga_comp_patch(andim, loA, hiA, bndim, loB, hiB) && pnga_comp_patch(andim, alo, ahi, bndim, blo, bhi)) compatible = 1; else compatible = 0; /* pnga_gop(pnga_type_f2c(MT_F_INT), &compatible, 1, "*"); */ pnga_gop(pnga_type_f2c(MT_F_INT), &compatible, 1, "&&"); if(!(compatible && (transp=='n'))) { /* either patches or distributions do not match: * - create a temp array that matches distribution of g_a * - copy & reshape patch of g_b into g_B */ if (!pnga_duplicate(g_a, &g_B, tempname)) pnga_error("duplicate failed",0L); pnga_copy_patch(&transp, g_b, blo, bhi, g_B, alo, ahi); bndim = andim; temp_created = 1; pnga_distribution(g_B, me, loB, hiB); } if(!pnga_comp_patch(andim, loA, hiA, bndim, loB, hiB)) pnga_error(" patches mismatch ",0); /* A[83:125,1:1] <==> B[83:125] */ if(andim > bndim) andim = bndim; /* need more work */ /* determine subsets of my patches to access */ if(pnga_patch_intersect(alo, ahi, loA, hiA, andim)){ pnga_access_ptr(g_A, loA, hiA, &A_ptr, ldA); pnga_access_ptr(g_B, loA, hiA, &B_ptr, ldB); snga_dot_local_patch(atype, andim, loA, hiA, ldA, A_ptr, B_ptr, &alen, retval); /* release access to the data */ pnga_release(g_A, loA, hiA); pnga_release(g_B, loA, hiA); } } else { /* Create copy of g_b identical with identical distribution as g_a */ if (!pnga_duplicate(g_a, &g_B, tempname)) pnga_error("duplicate failed",0L); pnga_copy_patch(&transp, g_b, blo, bhi, g_B, alo, ahi); temp_created = 1; #if 1 pnga_local_iterator_init(g_a, &hdl_a); pnga_local_iterator_init(g_B, &hdl_b); while(pnga_local_iterator_next(&hdl_a, loA, hiA, &A_ptr, ldA)) { Integer lo[MAXDIM]/*, hi[MAXDIM]*/; Integer offset, jtot, last; pnga_local_iterator_next(&hdl_b, loB, hiB, &B_ptr, ldB); /* make copies of loA and hiA since pnga_patch_intersect destroys original versions */ for (j=0; j B[83:125] */ if(andim > bndim) andim = bndim; /* need more work */ if(pnga_patch_intersect(alo, ahi, loA, hiA, andim)){ pnga_access_ptr(g_A, loA, hiA, &A_ptr, ldA); pnga_access_ptr(g_B, loA, hiA, &B_ptr, ldB); snga_dot_local_patch(atype, andim, loA, hiA, ldA, A_ptr, B_ptr, &alen, retval); /* release access to the data */ pnga_release(g_A, loA, hiA); pnga_release(g_B, loA, hiA); } } else { Integer lo[MAXDIM]/*, hi[MAXDIM]*/; Integer offset, jtot, last; /* simple block cyclic data distribution */ if (!pnga_uses_proc_grid(g_a)) { for (i=me; i adims[i]) hiA[i] = adims[i]; /*if (hiA[i] < loA[i]) chk = 0;*/ } /* make copies of loA and hiA since pnga_patch_intersect destroys original versions */ for (j=0; j= blocks[i] && i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((int *)data_ptr)[idx+j] = *(int*)val; } break; case C_DCPL: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) { DoubleComplex tmp = *(DoubleComplex *)val; ((DoubleComplex *)data_ptr)[idx+j].real = tmp.real; ((DoubleComplex *)data_ptr)[idx+j].imag = tmp.imag; } } break; case C_SCPL: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) { SingleComplex tmp = *(SingleComplex *)val; ((SingleComplex *)data_ptr)[idx+j].real = tmp.real; ((SingleComplex *)data_ptr)[idx+j].imag = tmp.imag; } } break; case C_DBL: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((double*)data_ptr)[idx+j] = *(double*)val; } break; case C_FLOAT: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((float *)data_ptr)[idx+j] = *(float*)val; } break; case C_LONG: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((long *)data_ptr)[idx+j] = *(long*)val; } break; case C_LONGLONG: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((long long*)data_ptr)[idx+j] = *(long long*)val; } break; default: pnga_error(" wrong data type ",type); } } /*\ FILL IN ARRAY WITH VALUE \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_fill_patch = pnga_fill_patch #endif void pnga_fill_patch(Integer g_a, Integer *lo, Integer *hi, void* val) { Integer i; Integer ndim, dims[MAXDIM], type; Integer loA[MAXDIM], hiA[MAXDIM], ld[MAXDIM]; char *data_ptr; Integer num_blocks, nproc; Integer me= pnga_nodeid(); int local_sync_begin,local_sync_end; _iterator_hdl hdl; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); pnga_inquire(g_a, &type, &ndim, dims); num_blocks = pnga_total_blocks(g_a); #if 1 pnga_local_iterator_init(g_a, &hdl); while (pnga_local_iterator_next(&hdl,loA,hiA,&data_ptr,ld)) { Integer offset, j, jtmp, chk; Integer loS[MAXDIM]; /* loA is changed by pnga_patch_intersect, so save a copy */ for (j=0; j dims[i]) hiA[i] = dims[i]; if (hiA[i] < loA[i]) chk = 0; } /* loA is changed by pnga_patch_intersect, so save a copy */ for (j=0; j= blocks[i] && i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((double*)src_data_ptr)[idx+j] *= *(double*)alpha; } break; case C_DCPL: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) { tmp1_real =((DoubleComplex *)src_data_ptr)[idx+j].real; tmp1_imag =((DoubleComplex *)src_data_ptr)[idx+j].imag; tmp2_real = (*(DoubleComplex*)alpha).real; tmp2_imag = (*(DoubleComplex*)alpha).imag; ((DoubleComplex *)src_data_ptr)[idx+j].real = tmp1_real*tmp2_real - tmp1_imag * tmp2_imag; ((DoubleComplex *)src_data_ptr)[idx+j].imag = tmp2_imag*tmp1_real + tmp1_imag * tmp2_real; } } break; case C_SCPL: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) { ftmp1_real =((SingleComplex *)src_data_ptr)[idx+j].real; ftmp1_imag =((SingleComplex *)src_data_ptr)[idx+j].imag; ftmp2_real = (*(SingleComplex*)alpha).real; ftmp2_imag = (*(SingleComplex*)alpha).imag; ((SingleComplex *)src_data_ptr)[idx+j].real = ftmp1_real*ftmp2_real - ftmp1_imag * ftmp2_imag; ((SingleComplex *)src_data_ptr)[idx+j].imag = ftmp2_imag*ftmp1_real + ftmp1_imag * ftmp2_real; } } break; case C_INT: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((int*)src_data_ptr)[idx+j] *= *(int*)alpha; } break; case C_LONG: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((long *)src_data_ptr)[idx+j] *= *(long*)alpha; } break; case C_LONGLONG: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((long long*)src_data_ptr)[idx+j] *= *(long long*)alpha; } break; case C_FLOAT: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((float *)src_data_ptr)[idx+j] *= *(float*)alpha; } break; default: pnga_error(" wrong data type ",type); } } /*\ SCALE ARRAY \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_scale_patch = pnga_scale_patch #endif void pnga_scale_patch(Integer g_a, Integer *lo, Integer *hi, void *alpha) { Integer ndim, dims[MAXDIM], type; Integer loA[MAXDIM], hiA[MAXDIM]; Integer ld[MAXDIM]; char *src_data_ptr; Integer num_blocks, nproc; Integer me= pnga_nodeid(); int local_sync_begin,local_sync_end; _iterator_hdl hdl; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); pnga_inquire(g_a, &type, &ndim, dims); num_blocks = pnga_total_blocks(g_a); #if 1 pnga_local_iterator_init(g_a, &hdl); while (pnga_local_iterator_next(&hdl,loA,hiA,&src_data_ptr,ld)) { Integer offset, j, jtmp, chk; Integer loS[MAXDIM]; /* loA is changed by pnga_patch_intersect, so save a copy */ for (j=0; j dims[i]) hiA[i] = dims[i]; if (hiA[i] < loA[i]) chk = 0; } /* loA is changed by pnga_patch_intersect, so save a copy */ for (j=0; j= blocks[i] && i (hiC[j]-loC[j])) bvalue[j] = 0; } for(j=0; j<(hiC[0]-loC[0]+1); j++) ((double*)C_ptr)[idx+j] = ((double*)C_ptr)[idx+j] + *(double*)alpha * ((double*)A_ptr)[idx+j]; } break; case C_DCPL: for(i=0; i (hiC[j]-loC[j])) bvalue[j] = 0; } for(j=0; j<(hiC[0]-loC[0]+1); j++) { DoubleComplex a = ((DoubleComplex *)A_ptr)[idx+j]; DoubleComplex x= *(DoubleComplex*)alpha; ((DoubleComplex *)C_ptr)[idx+j].real = ((DoubleComplex *)C_ptr)[idx+j].real + x.real*a.real - x.imag*a.imag; ((DoubleComplex *)C_ptr)[idx+j].imag = ((DoubleComplex *)C_ptr)[idx+j].imag + x.real*a.imag + x.imag*a.real; } } break; case C_SCPL: for(i=0; i (hiC[j]-loC[j])) bvalue[j] = 0; } for(j=0; j<(hiC[0]-loC[0]+1); j++) { SingleComplex a = ((SingleComplex *)A_ptr)[idx+j]; SingleComplex x= *(SingleComplex*)alpha; ((SingleComplex *)C_ptr)[idx+j].real = ((SingleComplex *)C_ptr)[idx+j].real + x.real*a.real - x.imag*a.imag; ((SingleComplex *)C_ptr)[idx+j].imag = ((SingleComplex *)C_ptr)[idx+j].imag + x.real*a.imag + x.imag*a.real; } } break; case C_INT: for(i=0; i (hiC[j]-loC[j])) bvalue[j] = 0; } for(j=0; j<(hiC[0]-loC[0]+1); j++) ((int*)C_ptr)[idx+j] = ((int*)C_ptr)[idx+j] + *(int *)alpha * ((int*)A_ptr)[idx+j]; } break; case C_FLOAT: for(i=0; i (hiC[j]-loC[j])) bvalue[j] = 0; } for(j=0; j<(hiC[0]-loC[0]+1); j++) ((float *)C_ptr)[idx+j] = ((float *)C_ptr)[idx+j] + *(float *)alpha * ((float *)A_ptr)[idx+j]; } break; case C_LONG: for(i=0; i (hiC[j]-loC[j])) bvalue[j] = 0; } for(j=0; j<(hiC[0]-loC[0]+1); j++) ((long *)C_ptr)[idx+j] = ((long *)C_ptr)[idx+j] + *(long *)alpha * ((long *)A_ptr)[idx+j]; } break; case C_LONGLONG: for(i=0; i (hiC[j]-loC[j])) bvalue[j] = 0; } for(j=0; j<(hiC[0]-loC[0]+1); j++) ((long long*)C_ptr)[idx+j] = ((long long*)C_ptr)[idx+j] + *(long long*)alpha * ((long long*)A_ptr)[idx+j]; } break; default: pnga_error(" wrong data type ",type); } } /*\ Utility function to add patch values together \*/ static void snga_add_patch_values(Integer type, void* alpha, void *beta, Integer ndim, Integer *loC, Integer *hiC, Integer *ldC, void *A_ptr, void *B_ptr, void *C_ptr) { Integer bvalue[MAXDIM], bunit[MAXDIM], baseldC[MAXDIM]; Integer idx, n1dim; Integer i, j; /* compute "local" add */ /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i (hiC[j]-loC[j])) bvalue[j] = 0; } for(j=0; j<(hiC[0]-loC[0]+1); j++) ((double*)C_ptr)[idx+j] = *(double*)alpha * ((double*)A_ptr)[idx+j] + *(double*)beta * ((double*)B_ptr)[idx+j]; } break; case C_DCPL: for(i=0; i (hiC[j]-loC[j])) bvalue[j] = 0; } for(j=0; j<(hiC[0]-loC[0]+1); j++) { DoubleComplex a = ((DoubleComplex *)A_ptr)[idx+j]; DoubleComplex b = ((DoubleComplex *)B_ptr)[idx+j]; DoubleComplex x= *(DoubleComplex*)alpha; DoubleComplex y= *(DoubleComplex*)beta; ((DoubleComplex *)C_ptr)[idx+j].real = x.real*a.real - x.imag*a.imag + y.real*b.real - y.imag*b.imag; ((DoubleComplex *)C_ptr)[idx+j].imag = x.real*a.imag + x.imag*a.real + y.real*b.imag + y.imag*b.real; } } break; case C_SCPL: for(i=0; i (hiC[j]-loC[j])) bvalue[j] = 0; } for(j=0; j<(hiC[0]-loC[0]+1); j++) { SingleComplex a = ((SingleComplex *)A_ptr)[idx+j]; SingleComplex b = ((SingleComplex *)B_ptr)[idx+j]; SingleComplex x= *(SingleComplex*)alpha; SingleComplex y= *(SingleComplex*)beta; ((SingleComplex *)C_ptr)[idx+j].real = x.real*a.real - x.imag*a.imag + y.real*b.real - y.imag*b.imag; ((SingleComplex *)C_ptr)[idx+j].imag = x.real*a.imag + x.imag*a.real + y.real*b.imag + y.imag*b.real; } } break; case C_INT: for(i=0; i (hiC[j]-loC[j])) bvalue[j] = 0; } for(j=0; j<(hiC[0]-loC[0]+1); j++) ((int*)C_ptr)[idx+j] = *(int *)alpha * ((int*)A_ptr)[idx+j] + *(int*)beta * ((int*)B_ptr)[idx+j]; } break; case C_FLOAT: for(i=0; i (hiC[j]-loC[j])) bvalue[j] = 0; } for(j=0; j<(hiC[0]-loC[0]+1); j++) ((float *)C_ptr)[idx+j] = *(float *)alpha * ((float *)A_ptr)[idx+j] + *(float *)beta * ((float *)B_ptr)[idx+j]; } break; case C_LONG: for(i=0; i (hiC[j]-loC[j])) bvalue[j] = 0; } for(j=0; j<(hiC[0]-loC[0]+1); j++) ((long *)C_ptr)[idx+j] = *(long *)alpha * ((long *)A_ptr)[idx+j] + *(long *)beta * ((long *)B_ptr)[idx+j]; } break; case C_LONGLONG: for(i=0; i (hiC[j]-loC[j])) bvalue[j] = 0; } for(j=0; j<(hiC[0]-loC[0]+1); j++) ((long long*)C_ptr)[idx+j] = *(long long*)alpha * ((long long*)A_ptr)[idx+j] + *(long long*)beta * ((long long*)B_ptr)[idx+j]; } break; default: pnga_error(" wrong data type ",type); } } /*\ SCALED ADDITION of two patches \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_add_patch = pnga_add_patch #endif void pnga_add_patch(alpha, g_a, alo, ahi, beta, g_b, blo, bhi, g_c, clo, chi) Integer g_a, *alo, *ahi; /* patch of g_a */ Integer g_b, *blo, *bhi; /* patch of g_b */ Integer g_c, *clo, *chi; /* patch of g_c */ void *alpha, *beta; { Integer i, j; Integer compatible_a, compatible_b; Integer atype, btype, ctype; Integer andim, adims[MAXDIM], bndim, bdims[MAXDIM], cndim, cdims[MAXDIM]; Integer loA[MAXDIM], hiA[MAXDIM], ldA[MAXDIM]; Integer loB[MAXDIM], hiB[MAXDIM], ldB[MAXDIM]; Integer loC[MAXDIM], hiC[MAXDIM], ldC[MAXDIM]; char *A_ptr, *B_ptr, *C_ptr; Integer n1dim; Integer atotal, btotal; Integer g_A = g_a, g_B = g_b; Integer me= pnga_nodeid(), A_created=0, B_created=0; Integer nproc = pnga_nnodes(); Integer num_blocks_a, num_blocks_b, num_blocks_c; char *tempname = "temp", notrans='n'; int local_sync_begin,local_sync_end; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); pnga_inquire(g_a, &atype, &andim, adims); pnga_inquire(g_b, &btype, &bndim, bdims); pnga_inquire(g_c, &ctype, &cndim, cdims); if(atype != btype || atype != ctype ) pnga_error(" types mismatch ", 0L); /* check if patch indices and dims match */ for(i=0; i adims[i]) pnga_error("g_a indices out of range ", g_a); for(i=0; i bdims[i]) pnga_error("g_b indices out of range ", g_b); for(i=0; i cdims[i]) pnga_error("g_c indices out of range ", g_c); /* check if numbers of elements in patches match each other */ n1dim = 1; for(i=0; i bndim) cndim = bndim; if(andim < bndim) cndim = andim; if(!pnga_comp_patch(andim, loA, hiA, cndim, loC, hiC)) pnga_error(" A patch mismatch ", g_A); if(!pnga_comp_patch(bndim, loB, hiB, cndim, loC, hiC)) pnga_error(" B patch mismatch ", g_B); /* determine subsets of my patches to access */ if (pnga_patch_intersect(clo, chi, loC, hiC, cndim)){ pnga_access_ptr(g_A, loC, hiC, &A_ptr, ldA); pnga_access_ptr(g_B, loC, hiC, &B_ptr, ldB); pnga_access_ptr(g_c, loC, hiC, &C_ptr, ldC); snga_add_patch_values(atype, alpha, beta, cndim, loC, hiC, ldC, A_ptr, B_ptr, C_ptr); /* release access to the data */ pnga_release (g_A, loC, hiC); pnga_release (g_B, loC, hiC); pnga_release_update(g_c, loC, hiC); } } else if (!compatible_a && compatible_b) { /* either patches or distributions do not match: * - create a temp array that matches distribution of g_c * - do C<= A */ if(g_b != g_c) { pnga_copy_patch(¬rans, g_a, alo, ahi, g_c, clo, chi); andim = cndim; g_A = g_c; pnga_distribution(g_A, me, loA, hiA); } else { if (!pnga_duplicate(g_c, &g_A, tempname)) pnga_error("ga_dadd_patch: dup failed", 0L); pnga_copy_patch(¬rans, g_a, alo, ahi, g_A, clo, chi); andim = cndim; A_created = 1; pnga_distribution(g_A, me, loA, hiA); } if(andim > bndim) cndim = bndim; if(andim < bndim) cndim = andim; if(!pnga_comp_patch(andim, loA, hiA, cndim, loC, hiC)) pnga_error(" A patch mismatch ", g_A); if(!pnga_comp_patch(bndim, loB, hiB, cndim, loC, hiC)) pnga_error(" B patch mismatch ", g_B); /* determine subsets of my patches to access */ if (pnga_patch_intersect(clo, chi, loC, hiC, cndim)){ pnga_access_ptr(g_A, loC, hiC, &A_ptr, ldA); pnga_access_ptr(g_B, loC, hiC, &B_ptr, ldB); pnga_access_ptr(g_c, loC, hiC, &C_ptr, ldC); snga_add_patch_values(atype, alpha, beta, cndim, loC, hiC, ldC, A_ptr, B_ptr, C_ptr); /* release access to the data */ pnga_release (g_A, loC, hiC); pnga_release (g_B, loC, hiC); pnga_release_update(g_c, loC, hiC); } } else if (compatible_a && !compatible_b) { /* either patches or distributions do not match: * - create a temp array that matches distribution of g_c * - copy & reshape patch of g_b into g_B */ if (!pnga_duplicate(g_c, &g_B, tempname)) pnga_error("ga_dadd_patch: dup failed", 0L); pnga_copy_patch(¬rans, g_b, blo, bhi, g_B, clo, chi); bndim = cndim; B_created = 1; pnga_distribution(g_B, me, loB, hiB); if(andim > bndim) cndim = bndim; if(andim < bndim) cndim = andim; if(!pnga_comp_patch(andim, loA, hiA, cndim, loC, hiC)) pnga_error(" A patch mismatch ", g_A); if(!pnga_comp_patch(bndim, loB, hiB, cndim, loC, hiC)) pnga_error(" B patch mismatch ", g_B); /* determine subsets of my patches to access */ if (pnga_patch_intersect(clo, chi, loC, hiC, cndim)){ pnga_access_ptr(g_A, loC, hiC, &A_ptr, ldA); pnga_access_ptr(g_B, loC, hiC, &B_ptr, ldB); pnga_access_ptr(g_c, loC, hiC, &C_ptr, ldC); snga_add_patch_values(atype, alpha, beta, cndim, loC, hiC, ldC, A_ptr, B_ptr, C_ptr); /* release access to the data */ pnga_release (g_A, loC, hiC); pnga_release (g_B, loC, hiC); pnga_release_update(g_c, loC, hiC); } } else if (!compatible_a && !compatible_b) { /* there is no match between any of the global arrays */ if (!pnga_duplicate(g_c, &g_B, tempname)) pnga_error("ga_dadd_patch: dup failed", 0L); pnga_copy_patch(¬rans, g_b, blo, bhi, g_B, clo, chi); bndim = cndim; B_created = 1; if(andim > bndim) cndim = bndim; if(andim < bndim) cndim = andim; cndim = bndim; pnga_copy_patch(¬rans, g_a, alo, ahi, g_c, clo, chi); pnga_scale_patch(g_c, clo, chi, alpha); /* determine subsets of my patches to access */ if (pnga_patch_intersect(clo, chi, loC, hiC, cndim)){ pnga_access_ptr(g_B, loC, hiC, &B_ptr, ldB); pnga_access_ptr(g_c, loC, hiC, &C_ptr, ldC); snga_acc_patch_values(atype, beta, cndim, loC, hiC, ldC, B_ptr, C_ptr); /* release access to the data */ pnga_release (g_B, loC, hiC); pnga_release_update(g_c, loC, hiC); } } } else { _iterator_hdl hdl_a, hdl_b, hdl_c; /* create copies of arrays A and B that are identically distributed as C*/ if (!pnga_duplicate(g_c, &g_A, tempname)) pnga_error("ga_dadd_patch: dup failed", 0L); pnga_copy_patch(¬rans, g_a, alo, ahi, g_A, clo, chi); andim = cndim; A_created = 1; if (!pnga_duplicate(g_c, &g_B, tempname)) pnga_error("ga_dadd_patch: dup failed", 0L); pnga_copy_patch(¬rans, g_b, blo, bhi, g_B, clo, chi); bndim = cndim; B_created = 1; #if 1 pnga_local_iterator_init(g_A, &hdl_a); pnga_local_iterator_init(g_B, &hdl_b); pnga_local_iterator_init(g_c, &hdl_c); while (pnga_local_iterator_next(&hdl_c,loC,hiC,&C_ptr,ldC)) { Integer idx, lod[MAXDIM]/*, hid[MAXDIM]*/; Integer offset, jtot, last; pnga_local_iterator_next(&hdl_a,loA,hiA,&A_ptr,ldA); pnga_local_iterator_next(&hdl_b,loB,hiB,&B_ptr,ldB); /* make temporary copies of loC and hiC since pnga_patch_intersect destroys original versions */ for (j=0; j bndim) cndim = bndim; if(andim < bndim) cndim = andim; if (pnga_patch_intersect(clo, chi, loC, hiC, cndim)){ pnga_access_ptr(g_A, loC, hiC, &A_ptr, ldA); pnga_access_ptr(g_B, loC, hiC, &B_ptr, ldB); pnga_access_ptr(g_c, loC, hiC, &C_ptr, ldC); snga_add_patch_values(atype, alpha, beta, cndim, loC, hiC, ldC, A_ptr, B_ptr, C_ptr); /* release access to the data */ pnga_release (g_A, loC, hiC); pnga_release (g_B, loC, hiC); pnga_release_update(g_c, loC, hiC); } } else { Integer idx, lod[MAXDIM]/*, hid[MAXDIM]*/; Integer offset, jtot, last; /* Simple block-cyclic data disribution */ if (!pnga_uses_proc_grid(g_c)) { for (idx = me; idx < num_blocks_c; idx += nproc) { pnga_distribution(g_c, idx, loC, hiC); /* make temporary copies of loC and hiC since pnga_patch_intersect destroys original versions */ for (j=0; j cdims[i]) hiC[i] = cdims[i]; /*if (hiC[i] < loC[i]) chk = 0;*/ } /* make temporary copies of loC and hiC since pnga_patch_intersect destroys original versions */ for (j=0; j= blocks[i] && i #endif #if HAVE_STDLIB_H # include #endif #include "globalp.h" #include "ga-papi.h" #include "ga-wapi.h" #define RANGE_NUM 3 #define RANGE_BOUND 6 #define IS_REGULAR_PATCH 100 typedef struct { Integer lo; Integer hi; int isvalid; } Range_bound; typedef struct { Range_bound low; Range_bound mid; Range_bound hig; } Range; static int ngai_peri_get_range_(Integer ndim, Integer *dims, Integer *lo_orig, Integer *hi_orig, Integer range[][RANGE_BOUND], Integer range_num[], Integer offset[][RANGE_BOUND/2], Integer op_code) /* ndim - dimension of array [input] dims[] - dimensions of each array component [input] lo_orig[] - lower bounds of request hi_orig[] - upper bounds of request range[][] - adjusted ranges for requests [output] range_num[] - number of ranges per axis offset[] - offset between original request and shifted request [output] op_code - not used */ { Integer lo[MAXDIM], hi[MAXDIM]; Integer i, rmndr, id; Range range_raw[MAXDIM]; Integer is_regular_patch, no_shift; /* check the patch is valid or not */ for (i=0; i 2*dims[i]) return 0; } /* move request so that it at least partially overlaps the array * (if necessary) */ no_shift = 1; for(i=0; i dims[i]) { rmndr = lo[i]%dims[i]; id = (lo[i]-rmndr)/dims[i]; lo[i] = lo[i] - id*dims[i]; hi[i] = hi[i] - id*dims[i]; if (id != 0) { no_shift = 0; } } if (hi[i] < 1) { rmndr = -hi[i]%dims[i]; id = (hi[i]+rmndr)/dims[i]+1; lo[i] = lo[i] + id*dims[i]; hi[i] = hi[i] + id*dims[i]; if (id != 0) { no_shift = 0; } } } /* break the lo and hi into the corresponding ranges * * lo (if < 1) 1 dims[i] hi (if > dims[i]) * |-------------------|------------------|------------------| * Example: * lo[0] = -3 hi[0] = 5 dims[0] = 10 then * range_raw[0].low.lo = -3 range_raw[0].low.hi = 0 * range_raw[0].low.isvalid = 1 * range_raw[0].mid.lo = 1 range_raw[0].mid.hi = 5 * range_raw[0].mid.isvalid = 1 * range_raw[0].hig.isvalid = 0 */ for(i=0; i= 1) { range_raw[i].mid.lo = lo[i]; range_raw[i].mid.isvalid = 1; } if (lo[i] < 1 && hi[i] > dims[i]) { range_raw[i].mid.isvalid = 1; } if(hi[i] > dims[i]) { range_raw[i].hig.hi = hi[i]; range_raw[i].hig.isvalid = 1; } else if (hi[i] <= dims[i]) { range_raw[i].mid.hi = hi[i]; range_raw[i].mid.isvalid = 1; } } /* check if this is a regular patch, not periodic operation needed */ is_regular_patch = 1; for(i=0; i #endif #if HAVE_STRING_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_UNISTD_H # include #endif #include "farg.h" #include "globalp.h" #include #include "ga-papi.h" #include "ga-wapi.h" #define ARMCI 1 /*\ PRINT g_a[ilo:ihi, jlo:jhi] \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_print_patch_file2d = pnga_print_patch_file2d #endif void pnga_print_patch_file2d(file, g_a, ilo, ihi, jlo, jhi, pretty) FILE *file; Integer g_a, ilo, ihi, jlo, jhi, pretty; /* Pretty = 0 ... spew output out with no formatting Pretty = 1 ... format output so that it is readable */ { #define BUFSIZE 6 #define FLEN 80 Integer i, j,jj, dim1, dim2, type, jmax, ld=1, bufsize ; Integer a_grp; int ibuf[BUFSIZE]; DoublePrecision dbuf[BUFSIZE]; float fbuf[BUFSIZE]; long lbuf[BUFSIZE]; long long llbuf[BUFSIZE]; char *name; Integer ndim, dims[2]; Integer lo[2], hi[2]; a_grp = pnga_get_pgroup(g_a); pnga_pgroup_sync(a_grp); pnga_check_handle(g_a, "ga_print"); if(pnga_pgroup_nodeid(a_grp) == 0){ pnga_inquire(g_a, &type, &ndim, dims); dim1 = dims[0]; dim2 = dims[1]; /* name[FLEN-1]='\0';*/ pnga_inquire_name(g_a, &name); if (ilo <= 0 || ihi > dim1 || jlo <= 0 || jhi > dim2){ fprintf(stderr,"%ld %ld %ld %ld dims: [%ld,%ld]\n", (long)ilo,(long)ihi, (long)jlo,(long)jhi, (long)dim1, (long)dim2); pnga_error(" ga_print: indices out of range ", g_a); } fprintf(file,"\n global array: %s[%ld:%ld,%ld:%ld], handle: %d \n", name, (long)ilo, (long)ihi, (long)jlo, (long)jhi, (int)g_a); bufsize = (type==C_DCPL)? BUFSIZE/2 : BUFSIZE; bufsize = (type==C_SCPL)? BUFSIZE/2 : BUFSIZE; if (!pretty) { for (i=ilo; i 1) Error(error_buffer, icode); else{ fprintf(FOUT,"%s %ld\n",error_buffer,icode); perror("system message:"); fflush(FOUT); exit(1); } #endif } void ga_debug_suspend() { #if HAVE_PAUSE fprintf(stdout,"ga_debug: process %ld ready for debugging\n", (long)getpid()); fflush(stdout); pause(); #endif } #ifdef ARMCI /********************************************************************* * N-dimensional operations * *********************************************************************/ /*\ print range of n-dimensional array with two strings before and after \*/ static void gai_print_range(char *pre,int ndim, Integer lo[], Integer hi[], char* post) { int i; printf("%s[",pre); for(i=0;i0; i--)printf("%ldx",(long)dims[i]); printf("%ld\n",(long)dims[0]); } /* print array range for every processor */ pnga_get_distribution_type(g_a, ctype); if (!strcmp(ctype,"regular")) { for(proc = 0; proc < nproc; proc++){ pnga_distribution(g_a,proc,lo,hi); sprintf(msg,"Process=%d\t owns array section: ",(int)proc); /* for C style need to swap and decremenent by 1 both arrays */ if(!fstyle){ for(i=0; i=0; j--) { idx = idx*nblocks[j]+index[j]; } sprintf(msg," idx: %ld Block=[",idx); pnga_distribution(g_a,idx,lo,hi); ptr = msg + strlen(msg); if (fstyle) { for (j=0; j0; j++) { sprintf(ptr,"%ld,",index[j]); ptr += strlen(ptr); } sprintf(ptr,"%ld]",index[0]); ptr += strlen(ptr); } sprintf(ptr,"\t corresponds to array section: %d ",(int)j); /* for C style need to swap and decremenent by 1 both arrays */ if(!fstyle){ for(i=0; i= nblocks[j] && j= nblocks[ndim-1]) ok = 0; } } } else { printf("\nData distribution type is unknown\n"); } fflush(stdout); } if(local_sync_end)pnga_sync(); } /* * Jialin added nga_print and nga_print_patch on Jun 28, 1999 */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_print_patch_file = pnga_print_patch_file #endif /*\ PRINT g_a[ilo, jlo] \*/ void pnga_print_patch_file(file, g_a, lo, hi, pretty) Integer g_a, *lo, *hi, pretty; FILE *file; /* Pretty = 0 ... spew output out with no formatting Pretty = 1 ... format output so that it is readable */ { #define BUFSIZE 6 #define FLEN 80 Integer i, j; Integer type; char *name; Integer ndim, dims[MAXDIM], ld[MAXDIM]; Integer bufsize; int ibuf[BUFSIZE], ibuf_2d[BUFSIZE*BUFSIZE]; DoublePrecision dbuf[BUFSIZE], dbuf_2d[BUFSIZE*BUFSIZE]; DoublePrecision dcbuf[2*BUFSIZE], dcbuf_2d[2*BUFSIZE*BUFSIZE]; float fbuf[BUFSIZE], fbuf_2d[BUFSIZE*BUFSIZE]; float fcbuf[2*BUFSIZE], fcbuf_2d[3*BUFSIZE*BUFSIZE]; Integer lop[MAXDIM], hip[MAXDIM]; long lbuf[BUFSIZE], lbuf_2d[BUFSIZE*BUFSIZE]; long long llbuf[BUFSIZE], llbuf_2d[BUFSIZE*BUFSIZE]; Integer done, status_2d, status_3d; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ pnga_sync(); pnga_check_handle(g_a, "nga_print"); /* only the first process print the array */ if(pnga_nodeid() == 0) { pnga_inquire(g_a, &type, &ndim, dims); pnga_inquire_name(g_a, &name); /* check the boundary */ for(i=0; i dims[i]) pnga_error("g_a indices out of range ", g_a); /* print the general information */ fprintf(file,"\n global array: %s[", name); for(i=0; i hi[i]) { if(i == (ndim-1)) { done = 0; } else { lop[i] = lo[i]; if(i == 0) hip[i] = GA_MIN(lop[i]+bufsize-1, hi[i]); else hip[i] = lo[i]; lop[i+1]++; hip[i+1]++; } } } } } else { /* pretty print */ done = 1; for(i=0; i 2)) { /* print the patch info */ fprintf(file,"\n -- patch: %s[", name); for(i=0; i 1)) { fprintf(file, "\n"); switch(type) { case C_INT: fprintf(file, " "); for (i=lop[1]; i<=hip[1]; i++) fprintf(file, "%7ld ", (long)i); fprintf(file,"\n "); for (i=lop[1]; i<=hip[1]; i++) fprintf(file," --------"); break; case C_LONG: fprintf(file, " "); for (i=lop[1]; i<=hip[1]; i++) fprintf(file, "%7ld ", (long)i); fprintf(file,"\n "); for (i=lop[1]; i<=hip[1]; i++) fprintf(file," --------"); break; case C_LONGLONG: fprintf(file, " "); for (i=lop[1]; i<=hip[1]; i++) fprintf(file, "%7ld ", (long)i); fprintf(file,"\n "); for (i=lop[1]; i<=hip[1]; i++) fprintf(file," --------"); break; case C_DBL: fprintf(file, " "); for (i=lop[1]; i<=hip[1]; i++) fprintf(file, "%10ld ", (long)i); fprintf(file,"\n "); for (i=lop[1]; i<=hip[1]; i++) fprintf(file," -----------"); break; case C_DCPL: for (i=lop[1]; i<=hip[1]; i++) fprintf(file, "%22ld ", (long)i); fprintf(file,"\n "); for (i=lop[1]; i<=hip[1]; i++) fprintf(file," -----------------------"); break; case C_SCPL: for (i=lop[1]; i<=hip[1]; i++) fprintf(file, "%22ld ", (long)i); fprintf(file,"\n "); for (i=lop[1]; i<=hip[1]; i++) fprintf(file," -----------------------"); break; case C_FLOAT: fprintf(file, " "); for (i=lop[1]; i<=hip[1]; i++) fprintf(file, "%7ld ", (long)i); fprintf(file,"\n "); for (i=lop[1]; i<=hip[1]; i++) fprintf(file," --------"); break; default: pnga_error("ga_print: wrong type", 0); } fprintf(file,"\n"); status_2d = 0; } switch(type) { case C_INT: pnga_get(g_a, lop, hip, ibuf_2d, ld); break; case C_LONG: pnga_get(g_a, lop, hip,lbuf_2d, ld); break; case C_LONGLONG: pnga_get(g_a, lop, hip,llbuf_2d,ld);break; case C_DBL: pnga_get(g_a, lop, hip, dbuf_2d, ld); break; case C_DCPL: pnga_get(g_a, lop, hip, dcbuf_2d, ld);break; case C_FLOAT: pnga_get(g_a, lop, hip, fbuf_2d, ld);break; case C_SCPL: pnga_get(g_a, lop, hip, fcbuf_2d, ld);break; default: pnga_error("ga_print: wrong type",0); } for(i=0; i<(hip[0]-lop[0]+1); i++) { fprintf(file,"%4ld ", (long)(lop[0]+i)); switch(type) { case C_INT: if(ndim > 1) for(j=0; j<(hip[1]-lop[1]+1); j++) fprintf(file," %8d", ibuf_2d[j*bufsize+i]); else fprintf(file," %8d", ibuf_2d[i]); break; case C_LONG: if(ndim > 1) for(j=0; j<(hip[1]-lop[1]+1); j++) fprintf(file," %8ld",lbuf_2d[j*bufsize+i]); else fprintf(file," %8ld",lbuf_2d[i]); break; case C_LONGLONG: if(ndim > 1) for(j=0; j<(hip[1]-lop[1]+1); j++) fprintf(file," %8lld",llbuf_2d[j*bufsize+i]); else fprintf(file," %8lld",llbuf_2d[i]); break; case C_DBL: if(ndim > 1) for(j=0; j<(hip[1]-lop[1]+1); j++) if((double)dbuf_2d[j*bufsize+i]<100000.0) fprintf(file," %11.5f", dbuf_2d[j*bufsize+i]); else fprintf(file," %.5e", dbuf_2d[j*bufsize+i]); else if((double)dbuf_2d[i]<100000.0) fprintf(file," %11.5f",dbuf_2d[i]); else fprintf(file," %.5e",dbuf_2d[i]); break; case C_FLOAT: if(ndim > 1) for(j=0; j<(hip[1]-lop[1]+1); j++) fprintf(file," %11.5f", fbuf_2d[j*bufsize+i]); else fprintf(file," %11.5f", fbuf_2d[i]); break; case C_DCPL: if(ndim > 1) for(j=0; j<(hip[1]-lop[1]+1); j++) if(((double)dcbuf_2d[(j*bufsize+i)*2]<100000.0)&&((double)dcbuf_2d[(j*bufsize+i)*2+1]<100000.0)) fprintf(file," %11.5f,%11.5f", dcbuf_2d[(j*bufsize+i)*2], dcbuf_2d[(j*bufsize+i)*2+1]); else fprintf(file," %.5e,%.5e", dcbuf_2d[(j*bufsize+i)*2], dcbuf_2d[(j*bufsize+i)*2+1]); else if(((double)dcbuf_2d[i*2]<100000.0) && ((double)dcbuf_2d[i*2+1]<100000.0)) fprintf(file," %11.5f,%11.5f", dcbuf_2d[i*2], dcbuf_2d[i*2+1]); else fprintf(file," %.5e,%.5e", dcbuf_2d[i*2], dcbuf_2d[i*2+1]); break; case C_SCPL: if(ndim > 1) for(j=0; j<(hip[1]-lop[1]+1); j++) if(((float)fcbuf_2d[(j*bufsize+i)*2]<100000.0)&&((float)fcbuf_2d[(j*bufsize+i)*2+1]<100000.0)) fprintf(file," %11.5f,%11.5f", fcbuf_2d[(j*bufsize+i)*2], fcbuf_2d[(j*bufsize+i)*2+1]); else fprintf(file," %.5e,%.5e", fcbuf_2d[(j*bufsize+i)*2], fcbuf_2d[(j*bufsize+i)*2+1]); else if(((float)fcbuf_2d[i*2]<100000.0) && ((float)fcbuf_2d[i*2+1]<100000.0)) fprintf(file," %11.5f,%11.5f", fcbuf_2d[i*2], fcbuf_2d[i*2+1]); else fprintf(file," %.5e,%.5e", fcbuf_2d[i*2], fcbuf_2d[i*2+1]); break; default: pnga_error("ga_print: wrong data type", 0); } fprintf(file,"\n"); } lop[0] = hip[0]+1; hip[0] = GA_MIN(lop[0]+bufsize-1, hi[0]); for(i=0; i hi[i]) { if(i == (ndim-1)) { done = 0; } else { lop[i] = lo[i]; if((i == 0) || (i == 1)) { hip[i] = GA_MIN(lop[i]+bufsize-1, hi[i]); } else { hip[i] = lo[i]; } if(i == 0) { lop[i+1] = hip[i+1]+1; hip[i+1] = GA_MIN(lop[i+1]+bufsize-1, hi[i+1]); } else { lop[i+1]++; hip[i+1]++; } if(i == 0) status_2d = 1; if(i == 1) status_3d = 1; } } } } } fflush(file); } pnga_sync(); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_print_patch = pnga_print_patch #endif void pnga_print_patch(Integer g_a, Integer *lo, Integer *hi, Integer pretty) { pnga_print_patch_file(stdout, g_a, lo, hi, pretty); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_summarize = pnga_summarize #endif void pnga_summarize(Integer verbose) { #define DEV stdout Integer i, j, g_a; Integer printed, arr_no; Integer type, active; char *name; Integer ndim, dims[MAXDIM]; Integer lop[MAXDIM], hip[MAXDIM]; Integer nproc = pnga_nnodes(); fprintf(DEV, " Summary of allocated global arrays\n"); fprintf(DEV, "-----------------------------------\n"); printed = 0; arr_no = 0; for(g_a=-1000; g_a<-900; g_a++) { active = pnga_verify_handle(g_a); if(active == 1) { printed = 1; pnga_inquire(g_a, &type, &ndim, dims); pnga_inquire_name(g_a, &name); switch(type) { case C_INT: fprintf(DEV, " array %d => integer ", (int)arr_no); break; case C_DBL: fprintf(DEV, " array %d => double precision ",(int)arr_no); break; case C_DCPL: fprintf(DEV, " array %d => double complex ", (int)arr_no); break; case C_SCPL: fprintf(DEV, " array %d => float (single) complex ", (int)arr_no); break; case C_FLOAT: fprintf(DEV, " array %d => float ",(int)arr_no); break; case C_LONG: fprintf(DEV, " array %d => long ",(int)arr_no); break; case C_LONGLONG: fprintf(DEV, " array %d => long long",(int)arr_no); break; default: pnga_error("ga_print: wrong type",0); } arr_no++; fprintf(DEV,"%s(", name); for(i=0; i %d \n",(int) i); } } } } if(!printed) fprintf(DEV, " No active global arrays\n"); fprintf(DEV, "\n\n"); fflush(DEV); } #endif #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_print_file = pnga_print_file #endif void pnga_print_file(FILE *file, Integer g_a) { Integer i; Integer type, ndim, dims[MAXDIM]; Integer lo[MAXDIM]; Integer pretty = 1; pnga_inquire(g_a, &type, &ndim, dims); for(i=0; i #else # include "tcgmsg.h" #endif /*\ wrapper for wallclock timer. Returns an alapsed time on calling process \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_wtime = pnga_wtime #endif DoublePrecision pnga_wtime() { double wtime=0.0; #ifdef MSG_COMMS_MPI wtime = MPI_Wtime(); #else wtime = tcg_time(); #endif return (DoublePrecision)wtime; } ga-5.9.2/global/src/globalp.h000066400000000000000000000115241500715745200157470ustar00rootroot00000000000000#ifndef _GLOBALP_H_ #define _GLOBALP_H_ #include #include "gaconfig.h" #if HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif #ifdef FALSE #undef FALSE #endif #ifdef TRUE #undef TRUE #endif #define FALSE (logical) 0 #define TRUE (logical) 1 #include "macdecls.h" #define GA_OFFSET 1000 /* offset for handle numbering */ /* types/tags of messages used internally by GA */ #define GA_TYPE_SYN GA_MSG_OFFSET + 1 #define GA_TYPE_GSM GA_MSG_OFFSET + 5 #define GA_TYPE_GOP GA_MSG_OFFSET + 15 #define GA_TYPE_BRD GA_MSG_OFFSET + 16 /* GA operation ids */ #define GA_OP_GET 1 /* Get */ #define GA_OP_END 2 /* Terminate */ #define GA_OP_CRE 3 /* Create */ #define GA_OP_PUT 4 /* Put */ #define GA_OP_ACC 5 /* Accumulate */ #define GA_OP_DES 6 /* Destroy */ #define GA_OP_DUP 7 /* Duplicate */ #define GA_OP_ZER 8 /* Zero */ #define GA_OP_DDT 9 /* dot product */ #define GA_OP_SCT 10 /* scatter */ #define GA_OP_GAT 11 /* gather */ #define GA_OP_RDI 15 /* Integer read and increment */ #define GA_OP_ACK 16 /* acknowledgment */ #define GA_OP_LCK 17 /* acquire lock */ #define GA_OP_UNL 18 /* release lock */ #ifdef ENABLE_TRACE static Integer op_code; #endif #define GA_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define GA_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define GA_ABS(a) (((a) >= 0) ? (a) : (-(a))) typedef struct ga_typeinfo_t { int active; size_t size; } ga_typeinfo_t; extern ga_typeinfo_t ga_types[]; #define GA_TYPES_MAX 256 #define GA_TYPES_RESERVED 17 /**Should match num lines initialized in ga_types struct*/ #define GAtypebuiltinM(_type) ((_type)>=MT_BASE && (_type)<(MT_BASE+GA_TYPES_RESERVED)) #define GAsizeofM(_type) ga_types[(_type)-MT_BASE].size #define GAvalidtypeM(_type) ((_type)>=MT_BASE && (_type)<(MT_BASE+GA_TYPES_MAX) && ga_types[(_type)-MT_BASE].active!=0) #define NAME_STACK_LEN 10 #define PAGE_SIZE 4096 struct ga_stat_t { long numcre; long numdes; long numget; long numput; long numacc; long numsca; long numgat; long numrdi; long numser; long curmem; long maxmem; long numget_procs; long numput_procs; long numacc_procs; long numsca_procs; long numgat_procs; }; struct ga_bytes_t{ double acctot; double accloc; double gettot; double getloc; double puttot; double putloc; double rditot; double rdiloc; double gattot; double gatloc; double scatot; double scaloc; }; #define STAT_AR_SZ sizeof(ga_stat_t)/sizeof(long) extern long *GAstat_arr; extern struct ga_stat_t GAstat; extern struct ga_bytes_t GAbytes; extern int _ga_sync_begin; extern int _ga_sync_end; extern int *_ga_argc; extern char ***_ga_argv; /* periodic operations */ #define PERIODIC_GET 1 #define PERIODIC_PUT 2 #define PERIODIC_ACC 3 #define FLUSH_CACHE #define ALLIGN_SIZE 128 #define allign__(n, SIZE) (((n)%SIZE) ? (n)+SIZE - (n)%SIZE: (n)) #define allign_size(n) allign__((long)(n), ALLIGN_SIZE) #define allign_page(n) allign__((long)(n), PAGE_SIZE) extern void ga_free(void *ptr); extern void* ga_malloc(Integer nelem, int type, char *name); extern void gai_init_onesided(); extern void gai_finalize_onesided(); extern void gai_print_subscript(char *pre,int ndim, Integer subscript[], char* post); extern Integer GAsizeof(Integer type); extern void ga_sort_gath(Integer *pn, Integer *i, Integer *j, Integer *base); extern void ga_sort_permutation(Integer *pn, Integer *index, Integer *base); extern void ga_sort_scat(Integer *pn, void *v, Integer *i, Integer *j, Integer *base, Integer type); extern void gai_hsort(Integer *list, int num); extern void ga_init_nbhandle(Integer *nbhandle); extern int nga_test_internal(Integer *nbhandle); extern int nga_wait_internal(Integer *nbhandle); extern void gai_nb_init(); extern int ga_icheckpoint_init(Integer *gas, int num); extern int ga_icheckpoint(Integer *gas, int num); extern int ga_irecover(int rid); extern int ga_icheckpoint_finalize(int g_a); extern void ga_checkpoint_arrays(Integer *gas,int num); extern int ga_recover_arrays(Integer *gas, int num); extern void set_ga_group_is_for_ft(int val); extern void ga_set_spare_procs(int *spare); #endif /* _GLOBALP_H_ */ ga-5.9.2/global/src/hsort.scat.c000066400000000000000000000133521500715745200164130ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /**************************************************************************\ Sort routines for scatter and gather. scatter requires sorting of index and value arrays. gather requires sorting of index arrays only. \**************************************************************************/ #include "typesf2c.h" #include "macommon.h" #include "ga-papi.h" #include "ga-wapi.h" #define GT(a,b) (*(a) > *(b)) #define GE(a,b) (*(a) >= *(b)) #define INDEX_SORT(base,pn,SWAP){\ unsigned long gap, g;\ Integer *p, *q, *hi, n=*pn;\ Integer *base0=base - 1;\ \ gap = n >>1;\ hi = base0 + gap + gap;\ if (n & 1) hi ++;\ \ for ( ; gap != 1; gap--) {\ for (p = base0 + (g = gap) ; (q = p + g) <= hi ; p = q) {\ g += g;\ if (q != hi && GT(q+1, q)) {\ q++;\ g++;\ }\ if (GE(p,q)) break;\ SWAP(p , q);\ }\ }\ \ for ( ; hi != base ; hi--) {\ p = base;\ for (g = 1 ; (q = p + g) <= hi ; p = q) {\ g += g;\ if (q != hi && GT(q+1,q)) {\ q++;\ g++;\ }\ if (GE(p,q)) break;\ SWAP(p, q);\ }\ SWAP(base, hi);\ }\ } static void ga_sort_scat_dcpl_(pn, v, i, j, base) Integer *pn; DoubleComplex *v; Integer *i; Integer *j; Integer *base; { if (*pn < 2) return; # undef SWAP # define SWAP(a,b) { \ Integer ltmp; \ DoubleComplex dtmp; \ long ia = a - base; \ long ib = b - base; \ ltmp=*a; *a=*b; *b=ltmp; \ dtmp=v[ia]; v[ia]=v[ib]; v[ib]=dtmp; \ ltmp=i[ia]; i[ia]=i[ib]; i[ib]=ltmp; \ ltmp=j[ia]; j[ia]=j[ib]; j[ib]=ltmp; \ } INDEX_SORT(base,pn,SWAP); } static void ga_sort_scat_scpl_(pn, v, i, j, base) Integer *pn; SingleComplex *v; Integer *i; Integer *j; Integer *base; { if (*pn < 2) return; # undef SWAP # define SWAP(a,b) { \ Integer ltmp; \ SingleComplex dtmp; \ long ia = a - base; \ long ib = b - base; \ ltmp=*a; *a=*b; *b=ltmp; \ dtmp=v[ia]; v[ia]=v[ib]; v[ib]=dtmp; \ ltmp=i[ia]; i[ia]=i[ib]; i[ib]=ltmp; \ ltmp=j[ia]; j[ia]=j[ib]; j[ib]=ltmp; \ } INDEX_SORT(base,pn,SWAP); } void ga_sort_permutation(pn, index, base) Integer *pn; Integer *index; Integer *base; { if (*pn < 2) return; # undef SWAP # define SWAP(a,b) { \ Integer ltmp; \ Integer itmp;\ long ia = a - base; \ long ib = b - base; \ ltmp=*a; *a=*b; *b=ltmp; \ itmp=index[ia]; index[ia]=index[ib]; index[ib] = itmp;\ } INDEX_SORT(base,pn,SWAP); } static void ga_sort_scat_dbl_(pn, v, i, j, base) Integer *pn; DoublePrecision *v; Integer *i; Integer *j; Integer *base; { if (*pn < 2) return; # undef SWAP # define SWAP(a,b) { \ Integer ltmp; \ DoublePrecision dtmp; \ long ia = a - base; \ long ib = b - base; \ ltmp=*a; *a=*b; *b=ltmp; \ dtmp=v[ia]; v[ia]=v[ib]; v[ib]=dtmp; \ ltmp=i[ia]; i[ia]=i[ib]; i[ib]=ltmp; \ ltmp=j[ia]; j[ia]=j[ib]; j[ib]=ltmp; \ } INDEX_SORT(base,pn,SWAP); } static void ga_sort_scat_int_(pn, v, i, j, base) Integer *pn; int *v; Integer *i; Integer *j; Integer *base; { if (*pn < 2) return; # undef SWAP # define SWAP(a,b) { \ int ltmp; \ int dtmp; \ long ia = a - base; \ long ib = b - base; \ ltmp=*a; *a=*b; *b=ltmp; \ dtmp=v[ia]; v[ia]=v[ib]; v[ib]=dtmp; \ ltmp=i[ia]; i[ia]=i[ib]; i[ib]=ltmp; \ ltmp=j[ia]; j[ia]=j[ib]; j[ib]=ltmp; \ } INDEX_SORT(base,pn,SWAP); } static void ga_sort_scat_long_(pn, v, i, j, base) Integer *pn; long *v; Integer *i; Integer *j; Integer *base; { if (*pn < 2) return; # undef SWAP # define SWAP(a,b) { \ long ltmp; \ long dtmp; \ long ia = a - base; \ long ib = b - base; \ ltmp=*a; *a=*b; *b=ltmp; \ dtmp=v[ia]; v[ia]=v[ib]; v[ib]=dtmp; \ ltmp=i[ia]; i[ia]=i[ib]; i[ib]=ltmp; \ ltmp=j[ia]; j[ia]=j[ib]; j[ib]=ltmp; \ } INDEX_SORT(base,pn,SWAP); } static void ga_sort_scat_flt_(pn, v, i, j, base) Integer *pn; float *v; Integer *i; Integer *j; Integer *base; { if (*pn < 2) return; # undef SWAP # define SWAP(a,b) { \ Integer ltmp; \ float dtmp; \ int ia = a - base; \ int ib = b - base; \ ltmp=*a; *a=*b; *b=ltmp; \ dtmp=v[ia]; v[ia]=v[ib]; v[ib]=dtmp; \ ltmp=i[ia]; i[ia]=i[ib]; i[ib]=ltmp; \ ltmp=j[ia]; j[ia]=j[ib]; j[ib]=ltmp; \ } INDEX_SORT(base,pn,SWAP); } void ga_sort_scat(pn, v, i, j, base, type) Integer *pn; void *v; Integer *i; Integer *j; Integer *base; Integer type; { switch (type){ case C_DBL: ga_sort_scat_dbl_(pn, (double*)v, i,j,base);break; case C_DCPL: ga_sort_scat_dcpl_(pn, (DoubleComplex*)v, i,j,base); break; case C_SCPL: ga_sort_scat_scpl_(pn, (SingleComplex*)v, i,j,base); break; case C_INT: ga_sort_scat_int_(pn, (int*)v, i, j, base); break; case C_FLOAT: ga_sort_scat_flt_(pn, (float*)v, i, j, base); break; case C_LONG: ga_sort_scat_long_(pn, (long*)v, i, j, base); break; default: pnga_error("ERROR:ga_sort_scat: wrong type",type); } } void ga_sort_gath(pn, i, j, base) Integer *pn; Integer *i; Integer *j; Integer *base; { if (*pn < 2) return; # undef SWAP # define SWAP(a,b) { \ Integer ltmp; \ long ia = a - base; \ long ib = b - base; \ ltmp=*a; *a=*b; *b=ltmp; \ ltmp=i[ia]; i[ia]=i[ib]; i[ib]=ltmp; \ ltmp=j[ia]; j[ia]=j[ib]; j[ib]=ltmp; \ } INDEX_SORT(base,pn,SWAP); } void gai_hsort(Integer *list, int num) { if(num<2) return; # undef SWAP # define SWAP(a,b) { Integer ltmp; ltmp=*a; *a=*b; *b=ltmp;} INDEX_SORT(list,&num,SWAP); } ga-5.9.2/global/src/iterator.c000066400000000000000000001055221500715745200161550ustar00rootroot00000000000000/* $Id: iterator.c,v 1.80.2.18 2007/12/18 22:22:27 d3g293 Exp $ */ /* * module: iterator.c * author: Jarek Nieplocha * description: implements an iterator that can be used for looping over blocks * in a GA operation. This functionality is designed to hide * the details of the data layout from the operation * * DISCLAIMER * * This material was prepared as an account of work sponsored by an * agency of the United States Government. Neither the United States * Government nor the United States Department of Energy, nor Battelle, * nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR * ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, * COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, * SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT * INFRINGE PRIVATELY OWNED RIGHTS. * * * ACKNOWLEDGMENT * * This software and its documentation were produced with United States * Government support under Contract Number DE-AC06-76RLO-1830 awarded by * the United States Department of Energy. The United States Government * retains a paid-up non-exclusive, irrevocable worldwide license to * reproduce, prepare derivative works, perform publicly and display * publicly by or for the US Government, including the right to * distribute to other US Government contractors. */ #if HAVE_CONFIG_H # include "config.h" #endif #define MAX_INT_VALUE 2147483648 #define LARGE_BLOCK_REQ #if HAVE_STDIO_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STDINT_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_STDDEF_H #include #endif #include "global.h" #include "globalp.h" #include "base.h" #include "ga_iterator.h" #include "armci.h" #include "macdecls.h" #include "ga-papi.h" #include "ga-wapi.h" #include "thread-safe.h" #define DEBUG 0 #ifdef PROFILE_OLD #include "ga_profile.h" #endif /*\ prepare permuted list of processes for remote ops \*/ #define gaPermuteProcList(nproc,ProcListPerm) \ { \ if((nproc) ==1) ProcListPerm[0]=0; \ else{ \ int _i, iswap, temp; \ if((nproc) > GAnproc) pnga_error("permute_proc: error ", (nproc));\ \ /* every process generates different random sequence */ \ (void)srand((unsigned)GAme); \ \ /* initialize list */ \ for(_i=0; _i< (nproc); _i++) ProcListPerm[_i]=_i; \ \ /* list permutation generated by random swapping */ \ for(_i=0; _i< (nproc); _i++){ \ iswap = (int)(rand() % (nproc)); \ temp = ProcListPerm[iswap]; \ ProcListPerm[iswap] = ProcListPerm[_i]; \ ProcListPerm[_i] = temp; \ } \ } \ } #define gam_GetRangeFromMap(p, ndim, plo, phi){ \ Integer _mloc = p* ndim *2; \ *plo = hdl->map + _mloc; \ *phi = *plo + ndim; \ } /*\ Return pointer (ptr_loc) to location in memory of element with subscripts * (subscript). Also return physical dimensions of array in memory in ld. \*/ #define gam_Location(proc, g_handle, subscript, ptr_loc, ld) \ { \ Integer _offset=0, _d, _w, _factor=1, _last=GA[g_handle].ndim-1; \ Integer _lo[MAXDIM], _hi[MAXDIM], _pinv, _p_handle; \ \ ga_ownsM(g_handle, proc, _lo, _hi); \ gaCheckSubscriptM(subscript, _lo, _hi, GA[g_handle].ndim); \ if(_last==0) ld[0]=_hi[0]- _lo[0]+1+2*(Integer)GA[g_handle].width[0]; \ for(_d=0; _d < _last; _d++) { \ _w = (Integer)GA[g_handle].width[_d]; \ _offset += (subscript[_d]-_lo[_d]+_w) * _factor; \ ld[_d] = _hi[_d] - _lo[_d] + 1 + 2*_w; \ _factor *= ld[_d]; \ } \ _offset += (subscript[_last]-_lo[_last] \ + (Integer)GA[g_handle].width[_last]) \ * _factor; \ _p_handle = GA[g_handle].p_handle; \ if (_p_handle != 0) { \ if (GA[g_handle].num_rstrctd == 0) { \ _pinv = proc; \ } else { \ _pinv = GA[g_handle].rstrctd_list[proc]; \ } \ } else { \ _pinv = PGRP_LIST[_p_handle].inv_map_proc_list[proc]; \ } \ *(ptr_loc) = GA[g_handle].ptr[_pinv]+_offset*GA[g_handle].elemsize; \ } void gam_LocationF(int proc, Integer g_handle, Integer subscript[], char **ptr_loc, Integer ld[]) { Integer _offset=0, _d, _w, _factor=1, _last=GA[g_handle].ndim-1; Integer _lo[MAXDIM], _hi[MAXDIM], _pinv, _p_handle; ga_ownsM(g_handle, proc, _lo, _hi); gaCheckSubscriptM(subscript, _lo, _hi, GA[g_handle].ndim); if(_last==0) ld[0]=_hi[0]- _lo[0]+1+2*(Integer)GA[g_handle].width[0]; for(_d=0; _d < _last; _d++) { _w = (Integer)GA[g_handle].width[_d]; _offset += (subscript[_d]-_lo[_d]+_w) * _factor; ld[_d] = _hi[_d] - _lo[_d] + 1 + 2*_w; _factor *= ld[_d]; } _offset += (subscript[_last]-_lo[_last] + (Integer)GA[g_handle].width[_last]) * _factor; _p_handle = GA[g_handle].p_handle; if (_p_handle != 0) { if (GA[g_handle].num_rstrctd == 0) { _pinv = proc; } else { _pinv = GA[g_handle].rstrctd_list[proc]; } } else { _pinv = PGRP_LIST[_p_handle].inv_map_proc_list[proc]; } *(ptr_loc) = GA[g_handle].ptr[_pinv]+_offset*GA[g_handle].elemsize; } #define gam_GetBlockPatch(plo,phi,lo,hi,blo,bhi,ndim) { \ Integer _d; \ for (_d=0; _d= plo[_d]) blo[_d] = lo[_d]; \ else blo[_d] = plo[_d]; \ if (hi[_d] <= phi[_d] && hi[_d] >= plo[_d]) bhi[_d] = hi[_d]; \ else bhi[_d] = phi[_d]; \ } \ } /** * Initialize an iterator handle * @param g_a global array handle * @param lo indices for lower corner of block in global array * @param hi indices for upper corner of block in global array * @param hdl handle for iterator */ void gai_iterator_init(Integer g_a, Integer lo[], Integer hi[], _iterator_hdl *hdl) { Integer handle = GA_OFFSET + g_a; Integer ndim = GA[handle].ndim; int grp = GA[handle].p_handle; int nproc = pnga_pgroup_nnodes(grp); Integer i; hdl->g_a = g_a; hdl->count = 0; hdl->oversize = 0; hdl->map = malloc((size_t)(nproc*2*MAXDIM+1)*sizeof(Integer)); hdl->proclist = malloc(nproc*sizeof(Integer));; hdl->proclistperm = malloc(nproc*sizeof(int)); for (i=0; ilo[i] = lo[i]; hdl->hi[i] = hi[i]; } /* Standard GA distribution */ if (GA[handle].distr_type == REGULAR) { /* Locate the processors containing some portion of the patch * specified by lo and hi and return the results in hdl->map, * hdl->proclist, and np. hdl->proclist contains a list of processors * containing some portion of the patch, hdl->map contains * the lower and upper indices of the portion of the patch held * by a given processor, and np contains the total number of * processors that contain some portion of the patch. */ if(!pnga_locate_region(g_a, lo, hi, hdl->map, hdl->proclist, &hdl->nproc )) ga_RegionError(pnga_ndim(g_a), lo, hi, g_a); gaPermuteProcList(hdl->nproc, hdl->proclistperm); /* Block-cyclic distribution */ } else if (GA[handle].distr_type == BLOCK_CYCLIC) { /* GA uses simple block cyclic data distribution */ hdl->iproc = 0; hdl->iblock = pnga_nodeid(); } else if (GA[handle].distr_type == SCALAPACK) { /* GA uses ScaLAPACK block cyclic data distribution */ int j; C_Integer *block_dims; /* Scalapack-type block-cyclic data distribution */ /* Calculate some properties associated with data distribution */ int *proc_grid = GA[handle].nblock; /*num_blocks = GA[handle].num_blocks;*/ block_dims = GA[handle].block_dims; /* blk_dim: length of one repeat unit * blk_num: number of repeat units * blk_inc: number of elements in last incomplete repeat unit * blk_ld: length of complete blocks in repeat unit. Does not * account for partial block at end * hlf_blk: number of full blocks in partial repeat unit */ for (j=0; jblk_size[j] = block_dims[j]; hdl->blk_dim[j] = block_dims[j]*proc_grid[j]; hdl->blk_num[j] = GA[handle].dims[j]/hdl->blk_dim[j]; hdl->blk_inc[j] = GA[handle].dims[j]-hdl->blk_num[j]*hdl->blk_dim[j]; hdl->blk_ld[j] = hdl->blk_num[j]*block_dims[j]; hdl->hlf_blk[j] = hdl->blk_inc[j]/block_dims[j]; } hdl->iproc = 0; hdl->offset = 0; /* Initialize proc_index and index arrays */ gam_find_proc_indices(handle, hdl->iproc, hdl->proc_index); gam_find_proc_indices(handle, hdl->iproc, hdl->index); } else if (GA[handle].distr_type == TILED) { int j; C_Integer *block_dims; hdl->iproc = 0; hdl->offset = 0; block_dims = GA[handle].block_dims; for (j=0; jblk_size[j] = block_dims[j]; } /* Initialize proc_index and index arrays */ gam_find_tile_proc_indices(handle, hdl->iproc, hdl->proc_index); gam_find_tile_proc_indices(handle, hdl->iproc, hdl->index); } else if (GA[handle].distr_type == TILED_IRREG) { int j; hdl->iproc = 0; hdl->offset = 0; hdl->mapc = GA[handle].mapc; /* Initialize proc_index and index arrays */ gam_find_tile_proc_indices(handle, hdl->iproc, hdl->proc_index); gam_find_tile_proc_indices(handle, hdl->iproc, hdl->index); } } /** * Reset an iterator back to the start * @param hdl handle for iterator */ void gai_iterator_reset(_iterator_hdl *hdl) { Integer handle = GA_OFFSET + hdl->g_a; if (GA[handle].distr_type == REGULAR) { /* Regular data distribution */ hdl->count = 0; } else if (GA[handle].distr_type == BLOCK_CYCLIC) { /* simple block cyclic data distribution */ hdl->iproc = 0; hdl->iblock = 0; hdl->offset = 0; } else if (GA[handle].distr_type == SCALAPACK) { hdl->iproc = 0; hdl->offset = 0; /* Initialize proc_index and index arrays */ gam_find_proc_indices(handle, hdl->iproc, hdl->proc_index); gam_find_proc_indices(handle, hdl->iproc, hdl->index); } else if (GA[handle].distr_type == TILED || GA[handle].distr_type == TILED_IRREG) { hdl->iproc = 0; hdl->offset = 0; /* Initialize proc_index and index arrays */ gam_find_tile_proc_indices(handle, hdl->iproc, hdl->proc_index); gam_find_tile_proc_indices(handle, hdl->iproc, hdl->index); } } /** * Get the next sub-block from the larger block defined when the iterator was * initialized * @param hdl handle for iterator * @param proc processor on which the next block resides * @param plo indices for lower corner of remote block * @param phi indices for upper corner of remote block * @param prem pointer to remote buffer * @return returns false if there is no new block, true otherwise */ int gai_iterator_next(_iterator_hdl *hdl, int *proc, Integer *plo[], Integer *phi[], char **prem, Integer ldrem[]) { Integer idx, i, p; Integer handle = GA_OFFSET + hdl->g_a; Integer p_handle = GA[handle].p_handle; Integer n_rstrctd = GA[handle].num_rstrctd; Integer *rstrctd_list = GA[handle].rstrctd_list; Integer *rank_rstrctd = GA[handle].rank_rstrctd; Integer elemsize = GA[handle].elemsize; int ndim; int grp = GA[handle].p_handle; int nproc = pnga_pgroup_nnodes(grp); int ok; ndim = GA[handle].ndim; if (GA[handle].distr_type == REGULAR) { Integer *blo, *bhi; Integer nelems; idx = hdl->count; /* Check to see if range is valid (it may not be valid if user has * created and irregular distribution in which some processors do not have * data). If invalid, skip this block and go to the next one */ ok = 0; while(!ok) { /* no blocks left, so return */ if (idx>=hdl->nproc) return 0; p = (Integer)hdl->proclistperm[idx]; *proc = (int)hdl->proclist[p]; if (p_handle >= 0) { *proc = (int)PGRP_LIST[p_handle].inv_map_proc_list[*proc]; } /* Find visible portion of patch held by processor p and * return the result in plo and phi. Also get actual processor * index corresponding to p and store the result in proc. */ gam_GetRangeFromMap(p, ndim, plo, phi); ok = 1; for (i=0; iproclist[p]; blo = *plo; bhi = *phi; #ifdef LARGE_BLOCK_REQ /* Check to see if block size will overflow int values and initialize * counter over sub-blocks if the block is too big*/ if (!hdl->oversize) { nelems = 1; for (i=0; i MAX_INT_VALUE) { Integer maxint = 0; int maxidx; hdl->oversize = 1; /* Figure out block dimensions that correspond to block sizes * that are beneath MAX_INT_VALUE */ for (i=0; iblk_size[i] = (bhi[i]-blo[i]+1); } while (elemsize*nelems > MAX_INT_VALUE) { for (i=0; iblk_size[i] > maxint) { maxidx = i; maxint = hdl->blk_size[i]; } } hdl->blk_size[maxidx] /= 2; nelems = 1; for (i=0; iblk_size[i]; } /* Calculate the number of blocks along each dimension */ for (i=0; iblk_dim[i] = (bhi[i]-blo[i]+1)/hdl->blk_size[i]; if (hdl->blk_dim[i]*hdl->blk_size[i] < (bhi[i]-blo[i]+1)) hdl->blk_dim[i]++; } /* initialize block counting */ for (i=0; iblk_inc[i] = 0; } } /* Get sub-block bounding dimensions */ if (hdl->oversize) { Integer tmp; for (i=0; ilobuf[i] = blo[i]; hdl->hibuf[i] = bhi[i]; } *plo = hdl->lobuf; *phi = hdl->hibuf; blo = *plo; bhi = *phi; for (i=0; ilobuf[i] += hdl->blk_inc[i]*hdl->blk_size[i]; tmp = hdl->lobuf[i] + hdl->blk_size[i]-1; if (tmp < hdl->hibuf[i]) hdl->hibuf[i] = tmp; } } #endif if (n_rstrctd == 0) { gam_Location(*proc, handle, blo, prem, ldrem); } else { gam_Location(rank_rstrctd[*proc], handle, blo, prem, ldrem); } if (p_handle >= 0) { *proc = (int)hdl->proclist[p]; /* BJP */ *proc = PGRP_LIST[p_handle].inv_map_proc_list[*proc]; } } #ifdef LARGE_BLOCK_REQ if (!hdl->oversize) { #endif hdl->count++; idx = hdl->count; #ifdef LARGE_BLOCK_REQ } else { /* update blk_inc array */ hdl->blk_inc[0]++; for (i=0; iblk_inc[i] >= hdl->blk_dim[i]) { hdl->blk_inc[i] = 0; hdl->blk_inc[i+1]++; } } if (hdl->blk_inc[ndim-1] >= hdl->blk_dim[ndim-1]) { hdl->count++; idx = hdl->count; hdl->oversize = 0; } } #endif } return 1; } else { Integer offset, l_offset, last, pinv; Integer blk_tot = GA[handle].block_total; Integer blo[MAXDIM], bhi[MAXDIM]; Integer idx, j, jtot, chk, iproc; int check1, check2; if (n_rstrctd > 0) nproc = n_rstrctd; if (GA[handle].distr_type == BLOCK_CYCLIC) { /* Simple block-cyclic distribution */ if (hdl->iproc >= nproc) return 0; /*if (hdl->iproc == nproc-1 && hdl->iblock >= blk_tot) return 0;*/ if (hdl->iblock == hdl->iproc) hdl->offset = 0; chk = 0; /* loop over blocks until a block with data is found */ while (!chk) { /* get the block corresponding to the current value of iblock */ idx = hdl->iblock; ga_ownsM(handle,idx,blo,bhi); /* check to see if this block overlaps with requested block * defined by lo and hi */ chk = 1; for (j=0; j= hdl->lo[j] && blo[j] <= hdl->hi[j]) || (bhi[j] >= hdl->lo[j] && bhi[j] <= hdl->hi[j])); /* check to see if interval represented by lo and hi * falls entirely within interval represented by blo and bhi */ check2 = ((hdl->lo[j] >= blo[j] && hdl->lo[j] <= bhi[j]) && (hdl->hi[j] >= blo[j] && hdl->hi[j] <= bhi[j])); /* If there is some data, move to the next section of code, * otherwise, check next block */ if (!check1 && !check2) { chk = 0; } } if (!chk) { /* evaluate new offset for block idx */ jtot = 1; for (j=0; joffset += jtot; /* increment to next block */ hdl->iblock += pnga_nnodes(); if (hdl->iblock >= blk_tot) { hdl->offset = 0; hdl->iproc++; hdl->iblock = hdl->iproc; if (hdl->iproc >= nproc) return 0; } } } /* The block overlaps some data in lo,hi */ if (chk) { Integer *clo, *chi; *plo = hdl->lobuf; *phi = hdl->hibuf; clo = *plo; chi = *phi; /* get the patch of block that overlaps requested region */ gam_GetBlockPatch(blo,bhi,hdl->lo,hdl->hi,clo,chi,ndim); /* evaluate offset within block */ last = ndim - 1; jtot = 1; if (last == 0) ldrem[0] = bhi[0] - blo[0] + 1; l_offset = 0; for (j=0; joffset; /* get pointer to data on remote block */ pinv = idx%nproc; if (p_handle > 0) { pinv = PGRP_LIST[p_handle].inv_map_proc_list[pinv]; } *prem = GA[handle].ptr[pinv]+l_offset*GA[handle].elemsize; *proc = pinv; /* evaluate new offset for block idx */ jtot = 1; for (j=0; joffset += jtot; hdl->iblock += pnga_nnodes(); if (hdl->iblock >= blk_tot) { hdl->iproc++; hdl->iblock = hdl->iproc; hdl->offset = 0; } } return 1; } else if (GA[handle].distr_type == SCALAPACK || GA[handle].distr_type == TILED || GA[handle].distr_type == TILED_IRREG) { /* Scalapack-type data distribution */ Integer proc_index[MAXDIM], index[MAXDIM]; Integer itmp; Integer blk_jinc; /* Return false at the end of the iteration */ if (hdl->iproc >= nproc) return 0; chk = 0; /* loop over blocks until a block with data is found */ while (!chk) { /* get bounds for current block */ if (GA[handle].distr_type == SCALAPACK || GA[handle].distr_type == TILED) { for (j = 0; j < ndim; j++) { blo[j] = hdl->blk_size[j]*(hdl->index[j])+1; bhi[j] = hdl->blk_size[j]*(hdl->index[j]+1); if (bhi[j] > GA[handle].dims[j]) bhi[j] = GA[handle].dims[j]; } } else { offset = 0; for (j = 0; j < ndim; j++) { blo[j] = GA[handle].mapc[offset+hdl->index[j]]; if (hdl->index[j] == GA[handle].num_blocks[j]-1) { bhi[j] = GA[handle].dims[j]; } else { bhi[j] = GA[handle].mapc[offset+hdl->index[j]+1]-1; } offset += GA[handle].num_blocks[j]; } } /* check to see if this block overlaps with requested block * defined by lo and hi */ chk = 1; for (j=0; j= hdl->lo[j] && blo[j] <= hdl->hi[j]) || (bhi[j] >= hdl->lo[j] && bhi[j] <= hdl->hi[j])); /* check to see if interval represented by lo and hi * falls entirely within interval represented by blo and bhi */ check2 = ((hdl->lo[j] >= blo[j] && hdl->lo[j] <= bhi[j]) && (hdl->hi[j] >= blo[j] && hdl->hi[j] <= bhi[j])); /* If there is some data, move to the next section of code, * otherwise, check next block */ if (!check1 && !check2) { chk = 0; } } if (!chk) { /* update offset for block */ itmp = 1; for (j=0; joffset += itmp; /* increment to next block */ hdl->index[0] += GA[handle].nblock[0]; for (j=0; jindex[j] >= GA[handle].num_blocks[j] && j < ndim-1) { hdl->index[j] = hdl->proc_index[j]; hdl->index[j+1] += GA[handle].nblock[j+1]; } } if (hdl->index[ndim-1] >= GA[handle].num_blocks[ndim-1]) { /* last iteration has been completed on current processor. Go * to next processor */ hdl->iproc++; if (hdl->iproc >= nproc) return 0; hdl->offset = 0; if (GA[handle].distr_type == TILED || GA[handle].distr_type == TILED_IRREG) { gam_find_tile_proc_indices(handle, hdl->iproc, hdl->proc_index); gam_find_tile_proc_indices(handle, hdl->iproc, hdl->index); } else if (GA[handle].distr_type == SCALAPACK) { gam_find_proc_indices(handle, hdl->iproc, hdl->proc_index); gam_find_proc_indices(handle, hdl->iproc, hdl->index); } } } } if (chk) { Integer *clo, *chi; *plo = hdl->lobuf; *phi = hdl->hibuf; clo = *plo; chi = *phi; /* get the patch of block that overlaps requested region */ gam_GetBlockPatch(blo,bhi,hdl->lo,hdl->hi,clo,chi,ndim); /* evaluate offset within block */ last = ndim - 1; if (GA[handle].distr_type == TILED || GA[handle].distr_type == TILED_IRREG) { jtot = 1; if (last == 0) ldrem[0] = bhi[0] - blo[0] + 1; l_offset = 0; for (j=0; joffset; } else if (GA[handle].distr_type == SCALAPACK) { l_offset = 0; jtot = 1; for (j=0; jblk_ld[j]; blk_jinc = GA[handle].dims[j]%hdl->blk_size[j]; if (hdl->blk_inc[j] > 0) { if (hdl->proc_index[j]hlf_blk[j]) { blk_jinc = hdl->blk_size[j]; } else if (hdl->proc_index[j] == hdl->hlf_blk[j]) { blk_jinc = hdl->blk_inc[j]%hdl->blk_size[j]; } else { blk_jinc = 0; } } ldrem[j] += blk_jinc; l_offset += (clo[j]-blo[j] + ((blo[j]-1)/hdl->blk_dim[j])*hdl->blk_size[j])*jtot; jtot *= ldrem[j]; } l_offset += (clo[last]-blo[last] + ((blo[last]-1)/hdl->blk_dim[j])*hdl->blk_size[last])*jtot; } /* get pointer to data on remote block */ pinv = (hdl->iproc)%nproc; if (n_rstrctd > 0) { pinv = rstrctd_list[pinv]; } if (p_handle > 0) { pinv = PGRP_LIST[p_handle].inv_map_proc_list[pinv]; } *prem = GA[handle].ptr[pinv]+l_offset*GA[handle].elemsize; *proc = pinv; /* evaluate new offset for block */ itmp = 1; for (j=0; joffset += itmp; /* increment to next block */ hdl->index[0] += GA[handle].nblock[0]; for (j=0; jindex[j] >= GA[handle].num_blocks[j] && j < ndim-1) { hdl->index[j] = hdl->proc_index[j]; hdl->index[j+1] += GA[handle].nblock[j+1]; } } if (hdl->index[ndim-1] >= GA[handle].num_blocks[ndim-1]) { hdl->iproc++; hdl->offset = 0; if (GA[handle].distr_type == TILED || GA[handle].distr_type == TILED_IRREG) { gam_find_tile_proc_indices(handle, hdl->iproc, hdl->proc_index); gam_find_tile_proc_indices(handle, hdl->iproc, hdl->index); } else if (GA[handle].distr_type == SCALAPACK) { gam_find_proc_indices(handle, hdl->iproc, hdl->proc_index); gam_find_proc_indices(handle, hdl->iproc, hdl->index); } } } } return 1; } return 0; } /** * Return true if this is the last data packet for this iterator */ int gai_iterator_last(_iterator_hdl *hdl) { Integer idx; Integer handle = GA_OFFSET + hdl->g_a; Integer p_handle = GA[handle].p_handle; Integer n_rstrctd = GA[handle].num_rstrctd; Integer *rank_rstrctd = GA[handle].rank_rstrctd; Integer elemsize = GA[handle].elemsize; int ndim; int grp = GA[handle].p_handle; int nproc = pnga_pgroup_nnodes(grp); ndim = GA[handle].ndim; if (GA[handle].distr_type == REGULAR) { idx = hdl->count; /* no blocks left after this iteration */ if (idx>=hdl->nproc) return 1; } else { if (GA[handle].distr_type == BLOCK_CYCLIC) { /* Simple block-cyclic distribution */ if (hdl->iproc >= nproc) return 1; } else if (GA[handle].distr_type == SCALAPACK || GA[handle].distr_type == TILED || GA[handle].distr_type == TILED_IRREG) { if (hdl->iproc >= nproc) return 1; } } return 0; } /** * Clean up iterator */ void gai_iterator_destroy(_iterator_hdl *hdl) { free(hdl->map); free(hdl->proclist); free(hdl->proclistperm); } /** * Functions that iterate over locally held blocks in a global array. These * are used in some routines such as copy */ /** * Initialize a local iterator * @param g_a global array handle * @param hdl handle for iterator */ void pnga_local_iterator_init(Integer g_a, _iterator_hdl *hdl) { Integer handle = GA_OFFSET + g_a; Integer ndim = GA[handle].ndim; Integer grp = (Integer)GA[handle].p_handle; hdl->g_a = g_a; hdl->count = 0; hdl->oversize = 0; /* If standard GA distribution then no additional action needs to be taken */ if (GA[handle].distr_type == BLOCK_CYCLIC) { /* GA uses simple block cyclic data distribution */ hdl->count = pnga_pgroup_nodeid(grp); } else if (GA[handle].distr_type == SCALAPACK) { /* GA uses ScaLAPACK block cyclic data distribution */ int j; Integer me = pnga_pgroup_nodeid(grp); /* Calculate some properties associated with data distribution */ for (j=0; jblk_size[j] = GA[handle].block_dims[j]; hdl->blk_num[j] = GA[handle].num_blocks[j]; hdl->blk_inc[j] = GA[handle].nblock[j]; hdl->blk_dim[j] = GA[handle].dims[j]; } /* Initialize proc_index and index arrays */ gam_find_proc_indices(handle, me, hdl->proc_index); gam_find_proc_indices(handle, me, hdl->index); } else if (GA[handle].distr_type == TILED) { /* GA uses tiled distribution */ int j; Integer me = pnga_pgroup_nodeid(grp); /* Calculate some properties associated with data distribution */ for (j=0; jblk_size[j] = GA[handle].block_dims[j]; hdl->blk_num[j] = GA[handle].num_blocks[j]; hdl->blk_inc[j] = GA[handle].nblock[j]; hdl->blk_dim[j] = GA[handle].dims[j]; } /* Initialize proc_index and index arrays */ gam_find_tile_proc_indices(handle, me, hdl->proc_index); gam_find_tile_proc_indices(handle, me, hdl->index); } else if (GA[handle].distr_type == TILED_IRREG) { /* GA uses irregular tiled distribution */ int j; Integer me = pnga_pgroup_nodeid(grp); hdl->mapc = GA[handle].mapc; /* Calculate some properties associated with data distribution */ for (j=0; jblk_num[j] = GA[handle].num_blocks[j]; hdl->blk_inc[j] = GA[handle].nblock[j]; hdl->blk_dim[j] = GA[handle].dims[j]; } /* Initialize proc_index and index arrays */ gam_find_tile_proc_indices(handle, me, hdl->proc_index); gam_find_tile_proc_indices(handle, me, hdl->index); } } /** * Get the next sub-block from local portion of global array * @param hdl handle for iterator * @param plo indices for lower corner of block * @param phi indices for upper corner of block * @param ptr pointer to local buffer * @param ld array of strides for local block * @return returns false if there is no new block, true otherwise */ int pnga_local_iterator_next(_iterator_hdl *hdl, Integer plo[], Integer phi[], char **ptr, Integer ld[]) { Integer i; Integer handle = GA_OFFSET + hdl->g_a; Integer grp = GA[handle].p_handle; Integer elemsize = GA[handle].elemsize; int ndim; int me = pnga_pgroup_nodeid(grp); ndim = GA[handle].ndim; if (GA[handle].distr_type == REGULAR) { Integer nelems; /* no blocks left, so return */ if (hdl->count>0) return 0; /* Find visible portion of patch held by this processor and * return the result in plo and phi. Return pointer to local * data as well */ pnga_distribution(hdl->g_a, me, plo, phi); /* Check to see if this process has any data. Return 0 if * it does not */ for (i=0; ig_a,plo,phi,ptr,ld); hdl->count++; } else if (GA[handle].distr_type == BLOCK_CYCLIC) { /* Simple block-cyclic distribution */ if (hdl->count >= pnga_total_blocks(hdl->g_a)) return 0; pnga_distribution(hdl->g_a,hdl->count,plo,phi); pnga_access_block_ptr(hdl->g_a,hdl->count,ptr,ld); hdl->count += pnga_pgroup_nnodes(grp); } else if (GA[handle].distr_type == SCALAPACK || GA[handle].distr_type == TILED) { /* Scalapack-type data distribution */ if (hdl->index[ndim-1] >= hdl->blk_num[ndim-1]) return 0; /* Find coordinates of bounding block */ for (i=0; iindex[i]*hdl->blk_size[i]+1; phi[i] = (hdl->index[i]+1)*hdl->blk_size[i]; if (phi[i] > hdl->blk_dim[i]) phi[i] = hdl->blk_dim[i]; } pnga_access_block_grid_ptr(hdl->g_a,hdl->index,ptr,ld); hdl->index[0] += hdl->blk_inc[0]; for (i=0; iindex[i] >= hdl->blk_num[i] && iindex[i] = hdl->proc_index[i]; hdl->index[i+1] += hdl->blk_inc[i+1]; } } } else if (GA[handle].distr_type == TILED_IRREG) { /* Irregular tiled data distribution */ Integer offset = 0; if (hdl->index[ndim-1] >= hdl->blk_num[ndim-1]) return 0; /* Find coordinates of bounding block */ for (i=0; imapc[offset+hdl->index[i]]; if (hdl->index[i] < hdl->blk_num[i]-1) { phi[i] = hdl->mapc[offset+hdl->index[i]+1]-1; } else { phi[i] = GA[handle].dims[i]; } offset += GA[handle].num_blocks[i]; } pnga_access_block_grid_ptr(hdl->g_a,hdl->index,ptr,ld); hdl->index[0] += hdl->blk_inc[0]; for (i=0; iindex[i] >= hdl->blk_num[i] && iindex[i] = hdl->proc_index[i]; hdl->index[i+1] += hdl->blk_inc[i+1]; } } } return 1; } ga-5.9.2/global/src/matmul.c000066400000000000000000002723201500715745200156240ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: matmul.c,v 1.60.4.1 2006-12-22 13:05:22 manoj Exp $ */ /*=========================================================== * * GA_Dgemm(): Parallel Matrix Multiplication * (i.e. C = alpha*A*B + beta*C) * *===========================================================*/ #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #include "matmul.h" #include "ga_iterator.h" #include "ga-papi.h" #include "ga-wapi.h" #define DEBUG_ 0 /*set 1, to verify the correctness of parallel matrix mult.*/ /* some optimization macros */ #define KCHUNK_OPTIMIZATION 0 /* This Opt performing well only for m=1000;n=1000'k=2000 kinda cases and not for the opposite*/ /* Optimization flags: Initialized everytime in pnga_matmul() */ static short int CYCLIC_DISTR_OPT_FLAG = SET; static short int CONTIG_CHUNKS_OPT_FLAG = SET; static short int DIRECT_ACCESS_OPT_FLAG = SET; Integer gNbhdlA[2], gNbhdlB[2], gNbhdlC[2];/* for A and B matrix */ static int _gai_matmul_patch_flag = 0; void gai_matmul_patch_flag(int flag) { _gai_matmul_patch_flag = flag; } static inline int max3(int ichunk, int jchunk, int kchunk) { if(ichunk>jchunk) return GA_MAX(ichunk,kchunk); else return GA_MAX(jchunk, kchunk); } static inline void init_task_list(task_list_t *thing) { thing->lo[0] = 0; thing->lo[1] = 0; thing->hi[0] = 0; thing->hi[1] = 0; thing->dim[0] = 0; thing->dim[1] = 0; thing->chunkBId = 0; thing->do_put = 0; } static void GET_BLOCK(Integer g_x, task_list_t *chunk, void *buf, char *trans, Integer xilo, Integer xjlo, Integer *dim_next, Integer *nbhdl) { Integer i0, i1, j0, j1; Integer lo[2], hi[2]; if(*trans == 'n' || *trans == 'N') { *dim_next = chunk->dim[0]; i0= xilo+chunk->lo[0]; i1= xilo+chunk->hi[0]; j0= xjlo+chunk->lo[1]; j1= xjlo+chunk->hi[1]; } else { *dim_next = chunk->dim[1]; i0= xjlo+chunk->lo[1]; i1= xjlo+chunk->hi[1]; j0= xilo+chunk->lo[0]; j1= xilo+chunk->hi[0]; } lo[0] = i0; lo[1] = j0; hi[0] = i1; hi[1] = j1; pnga_nbget(g_x, lo, hi, buf, dim_next, nbhdl); } static short int gai_get_task_list(task_list_t *taskListA, task_list_t *taskListB, task_list_t *state, Integer istart, Integer jstart, Integer kstart, Integer iend, Integer jend, Integer kend, Integer Ichunk, Integer Jchunk, Integer Kchunk, int *max_tasks, Integer g_a) { int ii, jj, nloops=0; short int do_put, more_chunks_left=0, recovery=0; Integer ilo, ihi, jlo, jhi, klo, khi, get_new_B; Integer jstart_=jstart, kstart_=kstart; if(state->lo[0] != -1) recovery = 1; nloops = (iend-istart+1)/Ichunk + ( ((iend-istart+1)%Ichunk)?1:0 ); if(nloops>MAX_CHUNKS) pnga_error("Increase MAX_CHUNKS value in matmul.h",0L); if(recovery) jstart_ = state->lo[0]; /* recovering the previous state */ for(ii=jj=0, jlo = jstart_; jlo <= jend; jlo += Jchunk) { jhi = GA_MIN(jend, jlo+Jchunk-1); if(recovery) { do_put = state->do_put; kstart_ = state->lo[1]; } else do_put = SET; /* for 1st shot we can put, instead of accumulate */ for(klo = kstart_; klo <= kend; klo += Kchunk) { khi = GA_MIN(kend, klo+Kchunk-1); get_new_B = TRUE; /* set it back after the first loop */ recovery = 0; jstart_ = jstart; kstart_ = kstart; /* save CURRENT STATE. Saving state before "i" loop helps to avoid tracking get_new_B, which is hassle in ga_matmul_regular() */ if(ii+nloops >= MAX_CHUNKS) { more_chunks_left = 1; state->lo[0] = jlo; state->lo[1] = klo; state->do_put = do_put; break; } for(ilo = istart; ilo <= iend; ilo += Ichunk){ ihi = GA_MIN(iend, ilo+Ichunk-1); taskListA[ii].dim[0] = ihi - ilo + 1; taskListA[ii].dim[1] = khi - klo + 1; taskListA[ii].lo[0] = ilo; taskListA[ii].hi[0] = ihi; taskListA[ii].lo[1] = klo; taskListA[ii].hi[1] = khi; taskListA[ii].do_put = do_put; if(get_new_B) { /* B matrix */ ihi = GA_MIN(iend, ilo+Ichunk-1); taskListB[jj].dim[0] = khi - klo + 1; taskListB[jj].dim[1] = jhi - jlo + 1; taskListB[jj].lo[0] = klo; taskListB[jj].hi[0] = khi; taskListB[jj].lo[1] = jlo; taskListB[jj].hi[1] = jhi; get_new_B = FALSE; /* Until J or K change again */ taskListA[ii].chunkBId = jj; ++jj; } else taskListA[ii].chunkBId = taskListA[ii-1].chunkBId; ++ii; } if (more_chunks_left) break; do_put = UNSET; } if (more_chunks_left) break; } *max_tasks = ii; /* Optimization disabled if chunks exceeds buffer space */ if(more_chunks_left) CYCLIC_DISTR_OPT_FLAG = UNSET; if(CYCLIC_DISTR_OPT_FLAG) { /* should not be called for irregular matmul */ int prow, pcol, offset, grp_me; Integer a_grp = pnga_get_pgroup(g_a); grp_me = (int)pnga_pgroup_nodeid(a_grp); prow = GA[GA_OFFSET + g_a].nblock[0]; pcol = GA[GA_OFFSET + g_a].nblock[1]; offset = (grp_me/prow + grp_me%prow) % pcol; for(jj=0, ilo = istart; ilo <= iend; jj++, ilo += Ichunk) taskListA[jj].do_put = UNSET; for(jj=0, ilo = istart; ilo <= iend; jj++, ilo += Ichunk) taskListA[jj+offset].do_put = SET; } return more_chunks_left; } static void gai_get_chunk_size(int irregular,Integer *Ichunk,Integer *Jchunk, Integer *Kchunk,Integer *elems,Integer atype, Integer m,Integer n,Integer k, short int nbuf, short int use_armci_memory, Integer a_grp) { double temp; Integer min_tasks = MINTASKS; /* Increase tasks if there is load imbalance. This controls the granularity of chunks */ Integer max_chunk, nproc=pnga_nnodes(), tmpa, tmpb; Integer avail = pnga_memory_avail_type(atype); tmpa = *Ichunk; tmpb = *Jchunk; if(irregular) { temp = (k*(double)(m*(double)n)) / (min_tasks * nproc); max_chunk = (Integer)pow(temp, (1.0/3.0) ); if (max_chunk < MIN_CHUNK_SIZE) max_chunk = MIN_CHUNK_SIZE; } else max_chunk = (Integer) max3(*Ichunk, *Jchunk, *Kchunk); pnga_pgroup_gop(a_grp, pnga_type_f2c(MT_F_INT), &avail, (Integer)1, "min"); if ( max_chunk > CHUNK_SIZE/nbuf) { /*if memory if very limited, performance degrades for large matrices as chunk size is very small, which leads to communication overhead)*/ if(avail max_chunk) { *Kchunk = GA_MIN(*Kchunk,(Integer)(temp/(*Ichunk))); *Jchunk = GA_MIN(*Jchunk,(Integer)(temp/(*Kchunk))); } else if(*Kchunk < max_chunk && *Ichunk > max_chunk) { temp *= 1.0/(*Kchunk); *Ichunk = GA_MIN(*Ichunk,(Integer)temp); *Jchunk = GA_MIN(*Jchunk,(Integer)temp); } else *Ichunk = *Jchunk = *Kchunk = max_chunk; } } else *Ichunk = *Jchunk = *Kchunk = CHUNK_SIZE/nbuf; /* Try to use 1-d data transfer & take advantage of zero-copy protocol */ if(CONTIG_CHUNKS_OPT_FLAG) { /* select a contiguous piece */ if(!irregular) { if(*Ichunk > tmpa && *Jchunk > tmpb) { *Ichunk = tmpa; *Jchunk = tmpb; *Kchunk = GA_MIN(*Ichunk,*Jchunk); } else { int i=1;/* i should be >=1 , to avoid divide by zero error */ temp = max_chunk*max_chunk; if(temp > tmpa) { *Ichunk = tmpa; *Jchunk = (Integer)(temp/(*Ichunk)); if(*Jchunk < tmpb) { while(tmpb/i > *Jchunk) ++i; *Jchunk = tmpb/i; } else *Jchunk = tmpb; *Kchunk = GA_MIN(*Ichunk, *Jchunk); } } } } if(*Ichunk<=0) *Ichunk = 1; /* should be atleast 1 */ if(*Jchunk<=0) *Jchunk = 1; if(*Kchunk<=0) *Kchunk = 1; /* Total elements "NUM_MAT" extra elems for safety - just in case */ *elems = ( nbuf*(*Ichunk)*(*Kchunk) + nbuf*(*Kchunk)*(*Jchunk) + (*Ichunk)*(*Jchunk) ); *elems += nbuf*NUM_MATS*sizeof(DoubleComplex)/GAsizeofM(atype); } static DoubleComplex* gai_get_armci_memory(Integer Ichunk, Integer Jchunk, Integer Kchunk, short int nbuf, Integer atype) { DoubleComplex *tmp = NULL; Integer elems; elems = (Integer) pow((double)BLOCK_SIZE,(double)2); elems = nbuf*elems + nbuf*elems + elems; /* A,B,C temporary buffers */ /* add extra elements for safety */ elems += nbuf*NUM_MATS*sizeof(DoubleComplex)/GAsizeofM(atype); /* allocate temporary storage using ARMCI_Malloc */ if( (Integer) (((double)nbuf)*(Ichunk* Kchunk) + ((double)nbuf)*(Kchunk* Jchunk) + Ichunk* Jchunk ) < elems) { tmp=(DoubleComplex*)ARMCI_Malloc_local(elems*GAsizeofM(atype)); } return tmp; } /************************************ * Sequential DGEMM * i.e. BLAS dgemm Routines ************************************/ static void GAI_DGEMM(Integer atype, char *transa, char *transb, Integer idim, Integer jdim, Integer kdim, void *alpha, DoubleComplex *a, Integer adim, DoubleComplex *b, Integer bdim, DoubleComplex *c, Integer cdim) { BlasInt idim_t, jdim_t, kdim_t, adim_t, bdim_t, cdim_t; DoubleComplex ZERO; SingleComplex ZERO_CF; idim_t=idim; jdim_t=jdim; kdim_t=kdim; adim_t=adim; bdim_t=bdim; cdim_t=cdim; ZERO.real = 0.; ZERO.imag = 0.; ZERO_CF.real = 0.; ZERO_CF.imag = 0.; switch(atype) { case C_FLOAT: BLAS_SGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (Real *)alpha, (Real *)a, &adim_t, (Real *)b, &bdim_t, (Real *)&ZERO_CF, (Real *)c, &cdim_t); break; case C_DBL: BLAS_DGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (DoublePrecision *)alpha, (DoublePrecision *)a, &adim_t, (DoublePrecision *)b, &bdim_t, (DoublePrecision *)&ZERO, (DoublePrecision *)c, &cdim_t); break; case C_DCPL: BLAS_ZGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (DoubleComplex *)alpha, (DoubleComplex *)a, &adim_t, (DoubleComplex *)b, &bdim_t, (DoubleComplex *)&ZERO, (DoubleComplex *)c, &cdim_t); break; case C_SCPL: BLAS_CGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (SingleComplex *)alpha, (SingleComplex *)a, &adim_t, (SingleComplex *)b, &bdim_t, (SingleComplex *)&ZERO_CF, (SingleComplex *)c, &cdim_t); break; default: pnga_error("ga_matmul_patch: wrong data type", atype); } } static void gai_matmul_shmem(transa, transb, alpha, beta, atype, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi, Ichunk, Kchunk, Jchunk, a,b,c, need_scaling) Integer g_a, ailo, aihi, ajlo, ajhi; /* patch of g_a */ Integer g_b, bilo, bihi, bjlo, bjhi; /* patch of g_b */ Integer g_c, cilo, cihi, cjlo, cjhi; /* patch of g_c */ Integer Ichunk, Kchunk, Jchunk, atype; void *alpha, *beta; char *transa, *transb; DoubleComplex *a, *b, *c; short int need_scaling; { Integer me = pnga_nodeid(); Integer get_new_B, loC[2]={0,0}, hiC[2]={0,0}, ld[2]; Integer i0, i1, j0, j1; Integer ilo, ihi, idim, jlo, jhi, jdim, klo, khi, kdim, adim, bdim=0, cdim; int istart, jstart, kstart, iend, jend, kend; short int do_put=UNSET, single_task_flag=UNSET; DoubleComplex ONE = {1.,0.}; SingleComplex ONE_CF = {1.,0.}; Integer clo[2], chi[2]; /* to skip accumulate and exploit data locality: get chunks according to "C" matrix distribution*/ pnga_distribution(g_c, me, loC, hiC); istart = loC[0]-1; iend = hiC[0]-1; jstart = loC[1]-1; jend = hiC[1]-1; kstart = 0 ; kend = ajhi-ajlo; if(DIRECT_ACCESS_OPT_FLAG) { /* check if there is only one task. If so, then it is contiguous */ if( (iend-istart+1 <= Ichunk) && (jend-jstart+1 <= Jchunk) && (kend-kstart+1 <= Kchunk) ) { single_task_flag = SET; pnga_access_ptr(g_c, loC, hiC, &c, ld); } } /* loop through columns of g_c patch */ for(jlo = jstart; jlo <= jend; jlo += Jchunk) { jhi = GA_MIN(jend, jlo+Jchunk-1); jdim = jhi - jlo +1; /* if beta=0,then for first shot we can do put,instead of accumulate */ if(need_scaling == UNSET) do_put = SET; /* loop cols of g_a patch : loop rows of g_b patch*/ for(klo = kstart; klo <= kend; klo += Kchunk) { khi = GA_MIN(kend, klo+Kchunk-1); kdim= khi - klo +1; get_new_B = TRUE; /* Each pass thru' outer 2 loops means we need a different patch of B.*/ /*loop through rows of g_c patch */ for(ilo = istart; ilo <= iend; ilo += Ichunk){ ihi = GA_MIN(iend, ilo+Ichunk-1); idim= cdim = ihi - ilo +1; /* STEP1(a): get matrix "A" chunk */ i0= ailo+ilo; i1= ailo+ihi; j0= ajlo+klo; j1= ajlo+khi; if (*transa == 'n' || *transa == 'N'){ clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; adim=idim; pnga_get(g_a, clo, chi, a, &idim); }else{ clo[0] = j0; clo[1] = i0; chi[0] = j1; chi[1] = i1; adim=kdim; pnga_get(g_a, clo, chi, a, &kdim); } /* STEP1(b): get matrix "B" chunk*/ if(get_new_B) {/*Avoid rereading B if same patch as last time*/ i0= bilo+klo; i1= bilo+khi; j0= bjlo+jlo; j1= bjlo+jhi; if (*transb == 'n' || *transb == 'N'){ clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; bdim=kdim; pnga_get(g_b, clo, chi, b, &kdim); }else { clo[0] = j0; clo[1] = i0; chi[0] = j1; chi[1] = i1; bdim=jdim; pnga_get(g_b, clo, chi, b, &jdim); } get_new_B = FALSE; /* Until J or K change again */ } /* STEP2: Do the sequential matrix multiply - i.e.BLAS dgemm */ GAI_DGEMM(atype, transa, transb, idim, jdim, kdim, alpha, a, adim, b, bdim, c, cdim); /* STEP3: put/accumulate into "C" matrix */ i0= cilo+ilo; i1= cilo+ihi; j0= cjlo+jlo; j1= cjlo+jhi; /* if single_task_flag is SET (i.e =1), then there is no need to update "C" matrix, as we use pointer directly in GAI_DGEMM */ if(single_task_flag != SET) { switch(atype) { case C_FLOAT: case C_SCPL: clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; if(do_put==SET) /* i.e.beta == 0.0 */ pnga_put(g_c, clo, chi, (float *)c, &cdim); else { pnga_acc(g_c, clo, chi, (float*)c, &cdim, &ONE_CF); } break; default: clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; if(do_put==SET) /* i.e.beta == 0.0 */ pnga_put(g_c, clo, chi, (DoublePrecision*)c, &cdim); else { pnga_acc(g_c, clo, chi, (DoublePrecision*)c, &cdim, (DoublePrecision*)&ONE); } break; } } } do_put = UNSET; /* In the second loop, accumulate should be done */ } } } static void init_block_info(Integer g_c, Integer *proc_index, Integer *index, Integer *blocks, Integer *block_dims, Integer *topology, Integer *iblock) { Integer me= pnga_nodeid(); /* Uses simple block-cyclic data distribution */ if(!pnga_uses_proc_grid(g_c)) { *iblock = me; } else /* Uses scalapack block-cyclic data distribution */ { *iblock = 0; pnga_get_proc_index(g_c, me, proc_index); pnga_get_proc_index(g_c, me, index); pnga_get_block_info(g_c, blocks, block_dims); pnga_get_proc_grid(g_c, topology); } } /** * get the lo/hi distribution info of the next block * return 0 indicates no more blocks available * return 1 indicates there is a block available */ static int get_next_block_info(Integer g_c, Integer *proc_index, Integer *index, Integer *blocks, Integer *block_dims, Integer*topology, Integer *iblock, Integer *blo, Integer *bhi) { Integer dims[MAXDIM], ndim, type; int i; /* works only upto 2 dims - i.e vectors/matrices*/ pnga_inquire(g_c, &type, &ndim, dims); if(ndim>2) pnga_error("get_next_block_info() supports upto 2-d only ", 0L); /* Uses simple block-cyclic data distribution */ if (!pnga_uses_proc_grid(g_c)) { if(*iblock < pnga_total_blocks(g_c)) { pnga_distribution(g_c, *iblock, blo, bhi); *iblock += pnga_nnodes(); return 1; } } else /* Uses scalapack block-cyclic data distribution */ { if (index[ndim-1] < blocks[ndim-1]) { /* find bounding coordinates of block */ for (i = 0; i < ndim; i++) { blo[i] = index[i]*block_dims[i]+1; bhi[i] = (index[i] + 1)*block_dims[i]; if (bhi[i] > dims[i]) bhi[i] = dims[i]; } /* increment index to get next block on processor */ index[0] += topology[0]; for (i = 0; i < ndim; i++) { if (index[i] >= blocks[i] && i=0) init_block_info(g_c, proc_index, index, blocks, block_dims, topology, &iblock); has_more_blocks = 1; while(has_more_blocks) { /* if block cyclic distribution and loop accordigly. In case of simple block distribution we process this loop only once */ if(numblocks<0) { /* simple block distribution */ has_more_blocks = 0; pnga_distribution(g_c, me, loC, hiC); } else { /* block cyclic */ if(!get_next_block_info(g_c, proc_index, index, blocks, block_dims, topology, &iblock, loC, hiC)) break; } /* } */ #else pnga_local_iterator_init(g_c, &hdl); while (pnga_local_iterator_next(&hdl,loC,hiC,&ptr_c,ld)) { #endif /* If loC and hiC intersects with current patch region, then they will * be updated accordingly. Else it returns FALSE */ pnga_inquire(g_c, &ctype, &cndim, cdims); if(!pnga_patch_intersect(clo,chi,loC,hiC,cndim)) continue; #if DEBUG_ printf("%d: Processing block #%d [%d,%d] - [%d,%d]\n", GAme, iblock, loC[0], loC[1], hiC[0], hiC[1]); #endif state.lo[0] = -1; /* just for first do-while loop */ do { /* Inital Settings */ a = a_ar[0]; b = b_ar[0]; c = c_ar[0]; do_put = single_task_flag = UNSET; offset = 0; /***************************************************************** * Task list: Collect information of all chunks. Matmul using * Non-blocking call needs this list *****************************************************************/ gTaskId=0; /* to skip accumulate and exploit data locality: get chunks according to "C" matrix distribution*/ /* pnga_distribution(g_c, me, loC, hiC); */ chunks_left=gai_get_task_list(taskListA, taskListB, &state,loC[0]-1, loC[1]-1, 0, hiC[0]-1, hiC[1]-1, k-1, Ichunk,Jchunk,Kchunk, &max_tasks,g_a); currA = nextA = 0; if(chunks_left) { /* then turn OFF this optimization */ if(DIRECT_ACCESS_OPT_FLAG) { /* check if there is only one task.If so,then it is contiguous */ if(max_tasks == 1) { if( !((hiC[0]-loC[0]+1 <= Ichunk) &&(hiC[1]-loC[1]+1 <=Jchunk) && (k <= Kchunk))) pnga_error("Invalid task list", 0L); single_task_flag = SET; pnga_access_ptr(g_c, loC, hiC, &c, ld); } } } if(CYCLIC_DISTR_OPT_FLAG) { int prow,pcol,grp_me; Integer a_grp=pnga_get_pgroup(g_a); grp_me = (int)pnga_pgroup_nodeid(a_grp); prow = GA[GA_OFFSET + g_a].nblock[0]; pcol = GA[GA_OFFSET + g_a].nblock[1]; offset = (grp_me/prow + grp_me%prow) % pcol; currA = nextA = nextA + offset; } /************************************************* * Do the setup & issue non-blocking calls to get * the first block/chunk I'm gonna work *************************************************/ shiftA=0; shiftB=0; if(nextA < max_tasks) { currB = nextB = taskListA[currA].chunkBId; GET_BLOCK(g_a, &taskListA[nextA], a_ar[shiftA], transa, ailo, ajlo, &adim_next, &gNbhdlA[shiftA]); GET_BLOCK(g_b, &taskListB[nextB], b_ar[shiftB], transb, bilo, bjlo, &bdim_next, &gNbhdlB[shiftB]); adim=adim_next; bdim=bdim_next; get_new_B = TRUE; } /************************************************************* * Main Parallel DGEMM Loop. *************************************************************/ while(nextA < max_tasks) { currA = nextA; currB = taskListA[currA].chunkBId; idim = cdim = taskListA[currA].dim[0]; jdim = taskListB[currB].dim[1]; kdim = taskListA[currA].dim[1]; bdim=bdim_next; /* if beta=0.0 (i.e.if need_scaling=UNSET), then for first shot, we can do put, instead of accumulate */ if(need_scaling == UNSET) do_put = taskListA[currA].do_put; nextA = ++gTaskId; /* get the next task id */ if(CYCLIC_DISTR_OPT_FLAG && nextA < max_tasks) nextA = (offset+nextA) % max_tasks; /* ---- WAIT till we get the current A & B block ---- */ a = a_ar[shiftA]; WAIT_GET_BLOCK(&gNbhdlA[shiftA]); if(get_new_B){/*Avoid rereading B if it is same patch as last time*/ get_new_B = FALSE; b = b_ar[shiftB]; WAIT_GET_BLOCK(&gNbhdlB[shiftB]); } /* ---- GET the next A & B block ---- */ if(nextA < max_tasks) { GET_BLOCK(g_a, &taskListA[nextA], a_ar[(shiftA+1)%2], transa, ailo, ajlo, &adim_next, &gNbhdlA[(shiftA+1)%2]); nextB = taskListA[nextA].chunkBId; if(currB != nextB) { shiftB=((shiftB+1)%2); GET_BLOCK(g_b, &taskListB[nextB], b_ar[shiftB], transb, bilo, bjlo, &bdim_next, &gNbhdlB[shiftB]); } } if(currB != nextB) get_new_B = TRUE; /* Do the sequential matrix multiply - i.e.BLAS dgemm */ GAI_DGEMM(atype, transa, transb, idim, jdim, kdim, alpha, a, adim, b, bdim, c, cdim); /* Non-blocking Accumulate Operation. Note: skip wait in 1st loop*/ i0 = cilo + taskListA[currA].lo[0]; i1 = cilo + taskListA[currA].hi[0]; j0 = cjlo + taskListB[currB].lo[1]; j1 = cjlo + taskListB[currB].hi[1]; if(currA < max_tasks) { if (single_task_flag != SET) { switch(atype) { case C_FLOAT: case C_SCPL: clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; if(do_put==SET) /* Note:do_put is UNSET, if beta!=0.0*/ pnga_put(g_c, clo, chi, (float *)c, &cdim); else { pnga_acc(g_c, clo, chi, (float *)c, &cdim, &ONE_CF); } break; default: clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; if(do_put==SET) /* i.e.beta ==0.0 */ pnga_put(g_c, clo, chi, (DoublePrecision*)c, &cdim); else { pnga_acc(g_c, clo, chi, (DoublePrecision*)c, &cdim,(DoublePrecision*)&ONE); } break; } } } /* shift next buffer..toggles between 0 and 1: as we use 2 buffers, one for computation and the other for communication (overlap) */ shiftA = ((shiftA+1)%2); adim = adim_next; } } while(chunks_left); } /* while(has_more_blocks) */ } static void gai_matmul_irreg(transa, transb, alpha, beta, atype, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi, Ichunk, Kchunk, Jchunk, a_ar,b_ar,c_ar, need_scaling, irregular) Integer g_a, ailo, aihi, ajlo, ajhi; /* patch of g_a */ Integer g_b, bilo, bihi, bjlo, bjhi; /* patch of g_b */ Integer g_c, cilo, cihi, cjlo, cjhi; /* patch of g_c */ Integer Ichunk, Kchunk, Jchunk, atype; void *alpha, *beta; char *transa, *transb; DoubleComplex **a_ar, **b_ar, **c_ar; short int need_scaling, irregular; { #if DEBUG_ Integer me= pnga_nodeid(); #endif Integer nproc=pnga_nnodes(); Integer get_new_B, i, i0, i1, j0, j1; Integer ilo, ihi, idim, jlo, jhi, jdim, klo, khi, kdim, ijk=0; Integer n, m, k, adim, bdim=0, cdim; Integer idim_prev=0, jdim_prev=0, kdim_prev=0; Integer adim_prev=0, bdim_prev=0, cdim_prev=0; task_list_t taskListC; short int compute_flag=0, shiftA=0, shiftB=0; DoubleComplex ONE, *a, *b, *c; SingleComplex ONE_CF; Integer grp_me, a_grp = pnga_get_pgroup(g_a); Integer clo[2], chi[2]; init_task_list(&taskListC); ONE.real =1.; ONE.imag =0.; ONE_CF.real =1.; ONE_CF.imag =0.; #if DEBUG_ if(me==0) { printf("@@ga_matmul_irreg:m,n,k=%ld %ld %ld\n", aihi-ailo+1, bjhi-bjlo+1,ajhi-ajlo+1); fflush(stdout); } #endif m = aihi - ailo +1; n = bjhi - bjlo +1; k = ajhi - ajlo +1; a = a_ar[0]; b = b_ar[0]; c = c_ar[0]; grp_me = pnga_pgroup_nodeid(a_grp); clo[0] = cilo; clo[1] = cjlo; chi[0] = cihi; chi[1] = cjhi; if(!need_scaling) pnga_fill_patch(g_c, clo, chi, beta); compute_flag=0; /* take care of the last chunk */ for(jlo = 0; jlo < n; jlo += Jchunk){ /* loop thru columns of g_c patch */ jhi = GA_MIN(n-1, jlo+Jchunk-1); jdim= jhi - jlo +1; for(klo = 0; klo < k; klo += Kchunk){ /* loop cols of g_a patch */ khi = GA_MIN(k-1, klo+Kchunk-1); /* loop rows of g_b patch */ kdim= khi - klo +1; /** Each pass through the outer two loops means we need a different patch of B.*/ get_new_B = TRUE; for(ilo = 0; ilo < m; ilo+=Ichunk){ /* loop thru rows of g_c patch */ if(ijk%nproc == grp_me){ ihi = GA_MIN(m-1, ilo+Ichunk-1); idim= cdim = ihi - ilo +1; if (*transa == 'n' || *transa == 'N'){ adim = idim; i0= ailo+ilo; i1= ailo+ihi; j0= ajlo+klo; j1= ajlo+khi; clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; pnga_nbget(g_a, clo, chi, a_ar[shiftA], &idim, &gNbhdlA[shiftA]); }else{ adim = kdim; i0= ajlo+klo; i1= ajlo+khi; j0= ailo+ilo; j1= ailo+ihi; clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; pnga_nbget(g_a, clo, chi, a_ar[shiftA], &kdim, &gNbhdlA[shiftA]); } /* Avoid rereading B if it is same patch as last time. */ if(get_new_B) { if (*transb == 'n' || *transb == 'N'){ bdim = kdim; i0= bilo+klo; i1= bilo+khi; j0= bjlo+jlo; j1= bjlo+jhi; clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; pnga_nbget(g_b, clo, chi, b_ar[shiftB], &kdim, &gNbhdlB[shiftB]); }else{ bdim = jdim; i0= bjlo+jlo; i1= bjlo+jhi; j0= bilo+klo; j1= bilo+khi; clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; pnga_nbget(g_b, clo, chi, b_ar[shiftB], &jdim, &gNbhdlB[shiftB]); } } if(compute_flag) { /* compute loop */ if(atype == C_FLOAT) for(i=0;i _GA_TOL_ || abs_value < -(_GA_TOL_)) { printf("Values are = %f : %f\n Alpha=%f Beta=%f\n", ((float*)tmpc_orig)[i], ((float*)tmpc2)[i], *((float*)alpha), *((float*)beta)); pnga_error("Matmul (type:float) check failed", 0); } } } break; case C_DBL: { double abs_value=0.0; for(i=0; i _GA_TOL_ || abs_value < -(_GA_TOL_)) { printf("Values are = %lf : %lf\n Alpha=%lf Beta=%lf\n", ((double*)tmpc_orig)[i+j], ((double*)tmpc2)[i+j], *((double*)alpha),*((double*)beta)); pnga_error("Matmul (type:double) check failed", 0); } } } break; case C_DCPL: { DoubleComplex abs_value; for(i=0; i_GA_TOL_ || abs_value.real<-(_GA_TOL_) || abs_value.imag>_GA_TOL_ || abs_value.imag<-(_GA_TOL_)) { printf("Values= %lf, %lf : %lf, %lf\n", tmpc_orig[i].real, tmpc_orig[i].imag,tmpc2[i].real,tmpc2[i].imag); pnga_error("Matmul (DoubleComplex) check failed", 0); } } } break; case C_SCPL: { SingleComplex abs_value; for(i=0; i_GA_TOL_ || abs_value.real<-(_GA_TOL_) || abs_value.imag>_GA_TOL_ || abs_value.imag<-(_GA_TOL_)) { printf("Values= %lf, %lf : %lf, %lf\n", ((SingleComplex*)tmpc_orig)[i].real, ((SingleComplex*)tmpc_orig)[i].imag,((SingleComplex*)tmpc2)[i].real,((SingleComplex*)tmpc2)[i].imag); pnga_error("Matmul (SingleComplex) check failed", 0); } } } break; default: pnga_error("ga_matmul_patch: wrong data type", atype); } printf("Matrix Multiplication check (m,n,k=%ld %ld %ld)...O.K\n",m,n,k); fflush(stdout); free(tmpc_orig); free(tmpc2); tmpc_orig = NULL; } } #endif /****************************************** * PARALLEL DGEMM * i.e. C = alpha*A*B + beta*C ******************************************/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_matmul = pnga_matmul #endif void pnga_matmul(transa, transb, alpha, beta, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi) Integer g_a, ailo, aihi, ajlo, ajhi; /* patch of g_a */ Integer g_b, bilo, bihi, bjlo, bjhi; /* patch of g_b */ Integer g_c, cilo, cihi, cjlo, cjhi; /* patch of g_c */ void *alpha, *beta; char *transa, *transb; { DoubleComplex *a=NULL, *b, *c, *a_ar[2], *b_ar[2], *c_ar[2]; Integer adim1=0, adim2=0, bdim1=0, bdim2=0, cdim1=0, cdim2=0, dims[2]; Integer atype, btype, ctype, rank, me= pnga_nodeid(); Integer n, m, k, Ichunk, Kchunk, Jchunk; Integer loA[2]={0,0}, hiA[2]={0,0}; Integer loB[2]={0,0}, hiB[2]={0,0}; Integer loC[2]={0,0}, hiC[2]={0,0}; int local_sync_begin,local_sync_end; short int need_scaling=SET,use_NB_matmul=SET; short int irregular=UNSET, use_armci_memory=UNSET; Integer a_grp=pnga_get_pgroup(g_a), b_grp=pnga_get_pgroup(g_b); Integer c_grp=pnga_get_pgroup(g_c); Integer numblocks; Integer clo[2], chi[2]; /* OPTIMIZATIONS FLAGS. To unset an optimization, replace SET by UNSET) */ CYCLIC_DISTR_OPT_FLAG = UNSET; CONTIG_CHUNKS_OPT_FLAG = SET; DIRECT_ACCESS_OPT_FLAG = SET; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_pgroup_sync(a_grp); if (a_grp != b_grp || a_grp != c_grp) pnga_error("Arrays must be defined on same group",0L); # if 0 /* disabled. should not fail if there are non-overlapping patches*/ /* check if C is different from A and B */ if (g_c == g_a || g_c == g_b) pnga_error("Global Array C should be different from A and B", 0); #endif /************************************************** * Do All Sanity Checks **************************************************/ /* Check to make sure all global arrays are of the same type */ if (!(pnga_is_mirrored(g_a) == pnga_is_mirrored(g_b) && pnga_is_mirrored(g_a) == pnga_is_mirrored(g_c))) { pnga_error("Processors do not match for all arrays",pnga_nnodes()); } /* check if ranks are O.K. */ pnga_inquire(g_a, &atype, &rank, dims); VECTORCHECK(rank, dims, adim1, adim2, ailo, aihi, ajlo, ajhi); pnga_inquire(g_b, &btype, &rank, dims); VECTORCHECK(rank, dims, bdim1, bdim2, bilo, bihi, bjlo, bjhi); pnga_inquire(g_c, &ctype, &rank, dims); VECTORCHECK(rank, dims, cdim1, cdim2, cilo, cihi, cjlo, cjhi); /* check for data-types mismatch */ if(atype != btype || atype != ctype ) pnga_error(" types mismatch ", 0L); if(atype != C_DCPL && atype != C_DBL && atype != C_FLOAT && atype!=C_SCPL) pnga_error(" type error",atype); /* check if patch indices and dims match */ if (*transa == 'n' || *transa == 'N'){ if (ailo <= 0 || aihi > adim1 || ajlo <= 0 || ajhi > adim2) pnga_error(" g_a indices out of range ", g_a); }else if (ailo <= 0 || aihi > adim2 || ajlo <= 0 || ajhi > adim1) pnga_error(" g_a indices out of range ", g_a); if (*transb == 'n' || *transb == 'N'){ if (bilo <= 0 || bihi > bdim1 || bjlo <= 0 || bjhi > bdim2) pnga_error(" g_b indices out of range ", g_b); }else if (bilo <= 0 || bihi > bdim2 || bjlo <= 0 || bjhi > bdim1) pnga_error(" g_b indices out of range ", g_b); if (cilo <= 0 || cihi > cdim1 || cjlo <= 0 || cjhi > cdim2) pnga_error(" g_c indices out of range ", g_c); /* verify if patch dimensions are consistent */ m = aihi - ailo +1; n = bjhi - bjlo +1; k = ajhi - ajlo +1; if( (cihi - cilo +1) != m) pnga_error(" a & c dims error",m); if( (cjhi - cjlo +1) != n) pnga_error(" b & c dims error",n); if( (bihi - bilo +1) != k) pnga_error(" a & b dims error",k); #if DEBUG_ if(me==0) check_result(0, transa, transb, alpha, beta, atype, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi); pnga_sync(); #endif /* switch to various matmul algorithms here. more to come */ if( GA[GA_OFFSET + g_c].irreg == 1 || GA[GA_OFFSET + g_b].irreg == 1 || GA[GA_OFFSET + g_a].irreg == 1 || _gai_matmul_patch_flag == SET) irregular = SET; /* even ga_dgemm is called, m,n & k might not match GA dimensions */ pnga_inquire(g_c, &ctype, &rank, dims); if(dims[0] != m || dims[1] != n) irregular = SET; /* C matrix dims */ if(!irregular) { if((adim1=GA_Cluster_nnodes()) > 1) use_NB_matmul = SET; else { use_NB_matmul = UNSET; CONTIG_CHUNKS_OPT_FLAG = UNSET; DIRECT_ACCESS_OPT_FLAG = UNSET; } } /* if block cyclic, then use regular algorithm. This is turned on for now * to test block cyclic */ numblocks = pnga_total_blocks(g_c); if(numblocks>=0) { #if 0 irregular = UNSET; use_NB_matmul = SET; #else loA[0] = ailo; loA[1] = ajlo; hiA[0] = aihi; hiA[1] = ajhi; loB[0] = bilo; loB[1] = bjlo; hiB[0] = bihi; hiB[1] = bjhi; loC[0] = cilo; loC[1] = cjlo; hiC[0] = cihi; hiC[1] = cjhi; pnga_matmul_basic(transa, transb, alpha, beta, g_a, loA, hiA, g_b, loB, hiB, g_c, loC, hiC); return; #endif } /**************************************************************** * Get the memory (i.e.static or dynamic) for temporary buffers ****************************************************************/ /* to skip accumulate and exploit data locality: get chunks according to "C" matrix distribution*/ pnga_distribution(g_a, me, loA, hiA); pnga_distribution(g_b, me, loB, hiB); pnga_distribution(g_c, me, loC, hiC); { Integer elems, factor=sizeof(DoubleComplex)/GAsizeofM(atype); short int nbuf=1; DoubleComplex *tmp = NULL; Ichunk = GA_MIN( (hiC[0]-loC[0]+1), (hiA[0]-loA[0]+1) ); Jchunk = GA_MIN( (hiC[1]-loC[1]+1), (hiB[1]-loB[1]+1) ); Kchunk = GA_MIN( (hiA[1]-loA[1]+1), (hiB[0]-loB[0]+1) ); #if KCHUNK_OPTIMIZATION /*works great for m=1000,n=1000,k=4000 kinda cases*/ pnga_distribution(g_a, me, loC, hiC); Kchunk = hiC[1]-loC[1]+1; pnga_distribution(g_b, me, loC, hiC); Kchunk = GA_MIN(Kchunk, (hiC[0]-loC[0]+1)); #endif /* Just to avoid divide by zero error */ if(Ichunk<=0) Ichunk = 1; if(Jchunk<=0) Jchunk = 1; if(Kchunk<=0) Kchunk = 1; { Integer irreg=0; if(Ichunk/Kchunk > GA_ASPECT_RATIO || Kchunk/Ichunk > GA_ASPECT_RATIO || Jchunk/Kchunk > GA_ASPECT_RATIO || Kchunk/Jchunk > GA_ASPECT_RATIO) { irreg = SET; } pnga_pgroup_gop(a_grp, pnga_type_f2c(MT_F_INT), &irreg, (Integer)1, "max"); if(irreg==SET) irregular = SET; } /* If non-blocking, we need 2 temporary buffers for A and B matrix */ if(use_NB_matmul) nbuf = 2; if(!irregular) { tmp = a_ar[0] =a=gai_get_armci_memory(Ichunk,Jchunk,Kchunk, nbuf, atype); if(tmp != NULL) use_armci_memory = SET; } /* get ChunkSize (i.e.BlockSize), that fits in temporary buffer */ gai_get_chunk_size(irregular, &Ichunk, &Jchunk, &Kchunk, &elems, atype, m, n, k, nbuf, use_armci_memory, a_grp); if(tmp == NULL) { /* try once again from armci for new chunk sizes */ tmp = a_ar[0] =a=gai_get_armci_memory(Ichunk,Jchunk,Kchunk, nbuf, atype); if(tmp != NULL) use_armci_memory = SET; } if(tmp == NULL) { /*if armci malloc fails again, then get from MA */ tmp = a_ar[0] = a =(DoubleComplex*) pnga_malloc(elems,atype, "GA mulmat bufs"); } if(use_NB_matmul) tmp = a_ar[1] = a_ar[0] + (Ichunk*Kchunk)/factor+1; tmp = b_ar[0] = b = tmp + (Ichunk*Kchunk)/factor + 1; if(use_NB_matmul) tmp = b_ar[1] = b_ar[0] + (Kchunk*Jchunk)/factor+1; c_ar[0] = c = tmp + (Kchunk*Jchunk)/factor + 1; } /** check if there is a need for scaling the data. Note: if beta=0, then need_scaling=0 */ if(atype==C_DCPL){ if((((DoubleComplex*)beta)->real == 0) && (((DoubleComplex*)beta)->imag ==0)) need_scaling =0;} else if(atype==C_SCPL){ if((((SingleComplex*)beta)->real == 0) && (((SingleComplex*)beta)->imag ==0)) need_scaling =0;} else if(atype==C_DBL){ if(*(DoublePrecision *)beta == 0) need_scaling =0;} else if( *(float*)beta ==0) need_scaling =0; clo[0] = cilo; clo[1] = cjlo; chi[0] = cihi; chi[1] = cjhi; if(need_scaling) pnga_scale_patch(g_c, clo, chi, beta); /******************************************************************** * Parallel Matrix Multiplication Starts Here. * 3 Steps: * 1. Get a chunk of A and B matrix, and store it in local buffer. * 2. Do sequential dgemm. * 3. Put/accumulate the result into C matrix. *********************************************************************/ /* if only one node, then enable the optimized shmem code */ if(use_NB_matmul==UNSET) { gai_matmul_shmem(transa, transb, alpha, beta, atype, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi, Ichunk, Kchunk, Jchunk, a,b,c, need_scaling); } else { if(irregular) gai_matmul_irreg(transa, transb, alpha, beta, atype, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi, Ichunk, Kchunk, Jchunk, a_ar, b_ar, c_ar, need_scaling, irregular); else { gai_matmul_regular(transa, transb, alpha, beta, atype, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi, Ichunk, Kchunk, Jchunk, a_ar, b_ar, c_ar, need_scaling, irregular); } } a = a_ar[0]; if(use_armci_memory == SET) ARMCI_Free_local(a); else { pnga_free(a); } #if DEBUG_ Integer grp_me; grp_me = pnga_pgroup_nodeid(a_grp); pnga_pgroup_sync(a_grp); if(me==0) check_result(1, transa, transb, alpha, beta, atype, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi); pnga_pgroup_sync(a_grp); #endif if(local_sync_end)pnga_pgroup_sync(a_grp); } /* This is the old matmul code. It is enabled now for mirrored matrix multiply. It also work for normal matrix/vector multiply with no changes */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_matmul_mirrored = pnga_matmul_mirrored #endif void pnga_matmul_mirrored(transa, transb, alpha, beta, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi) Integer g_a, ailo, aihi, ajlo, ajhi; /* patch of g_a */ Integer g_b, bilo, bihi, bjlo, bjhi; /* patch of g_b */ Integer g_c, cilo, cihi, cjlo, cjhi; /* patch of g_c */ void *alpha, *beta; char *transa, *transb; { #ifdef STATBUF /* approx. sqrt(2) ratio in chunk size to use the same buffer space */ DoubleComplex a[ICHUNK*KCHUNK], b[KCHUNK*JCHUNK], c[ICHUNK*JCHUNK]; #else DoubleComplex *a, *b, *c; #endif Integer atype, btype, ctype, adim1=0, adim2=0, bdim1=0, bdim2=0, cdim1=0, cdim2=0, dims[2], rank; Integer me= pnga_nodeid(), nproc; Integer i, ijk = 0, i0, i1, j0, j1; Integer ilo, ihi, idim, jlo, jhi, jdim, klo, khi, kdim; Integer n, m, k, adim, bdim=0, cdim; Integer Ichunk, Kchunk, Jchunk; DoubleComplex ONE; SingleComplex ONE_CF; DoublePrecision chunk_cube; Integer min_tasks = 10, max_chunk; int need_scaling=1; Integer ZERO_I = 0, inode, iproc; Integer get_new_B; int local_sync_begin,local_sync_end; BlasInt idim_t, jdim_t, kdim_t, adim_t, bdim_t, cdim_t; Integer clo[2], chi[2]; ONE.real =1.; ONE.imag =0.; ONE_CF.real =1.; ONE_CF.imag =0.; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); /* Check to make sure all global arrays are of the same type */ if (!(pnga_is_mirrored(g_a) == pnga_is_mirrored(g_b) && pnga_is_mirrored(g_a) == pnga_is_mirrored(g_c))) { pnga_error("Processors do not match for all arrays",pnga_nnodes()); } if (pnga_is_mirrored(g_a)) { inode = pnga_cluster_nodeid(); nproc = pnga_cluster_nprocs(inode); iproc = me - pnga_cluster_procid(inode, ZERO_I); } else { nproc = pnga_nnodes(); iproc = me; } pnga_inquire(g_a, &atype, &rank, dims); VECTORCHECK(rank, dims, adim1, adim2, ailo, aihi, ajlo, ajhi); pnga_inquire(g_b, &btype, &rank, dims); VECTORCHECK(rank, dims, bdim1, bdim2, bilo, bihi, bjlo, bjhi); pnga_inquire(g_c, &ctype, &rank, dims); VECTORCHECK(rank, dims, cdim1, cdim2, cilo, cihi, cjlo, cjhi); if(atype != btype || atype != ctype ) pnga_error(" types mismatch ", 0L); if(atype != C_DCPL && atype != C_DBL && atype != C_FLOAT && atype != C_SCPL) pnga_error(" type error",atype); /* check if patch indices and dims match */ if (*transa == 'n' || *transa == 'N'){ if (ailo <= 0 || aihi > adim1 || ajlo <= 0 || ajhi > adim2) pnga_error(" g_a indices out of range ", g_a); }else if (ailo <= 0 || aihi > adim2 || ajlo <= 0 || ajhi > adim1) pnga_error(" g_a indices out of range ", g_a); if (*transb == 'n' || *transb == 'N'){ if (bilo <= 0 || bihi > bdim1 || bjlo <= 0 || bjhi > bdim2) pnga_error(" g_b indices out of range ", g_b); }else if (bilo <= 0 || bihi > bdim2 || bjlo <= 0 || bjhi > bdim1) pnga_error(" g_b indices out of range ", g_b); if (cilo <= 0 || cihi > cdim1 || cjlo <= 0 || cjhi > cdim2) pnga_error(" g_c indices out of range ", g_c); /* verify if patch dimensions are consistent */ m = aihi - ailo +1; n = bjhi - bjlo +1; k = ajhi - ajlo +1; if( (cihi - cilo +1) != m) pnga_error(" a & c dims error",m); if( (cjhi - cjlo +1) != n) pnga_error(" b & c dims error",n); if( (bihi - bilo +1) != k) pnga_error(" a & b dims error",k); /* In 32-bit platforms, k*m*n might exceed the "long" range(2^31), eg:k=m=n=1600. So casting the temporary value to "double" helps */ chunk_cube = (k*(double)(m*n)) / (min_tasks * nproc); max_chunk = (Integer)pow(chunk_cube, (DoublePrecision)(1.0/3.0) ); if (max_chunk < 32) max_chunk = 32; #ifdef STATBUF if(atype == C_DBL || atype == C_FLOAT){ Ichunk=D_CHUNK, Kchunk=D_CHUNK, Jchunk=D_CHUNK; }else{ Ichunk=ICHUNK; Kchunk=KCHUNK; Jchunk=JCHUNK; } #else { /** * Find out how much memory we can grab. It will be used in * three chunks, and the result includes only the first one. */ Integer elems, factor = sizeof(DoubleComplex)/GAsizeofM(atype); Ichunk = Jchunk = Kchunk = CHUNK_SIZE; if ( max_chunk > Ichunk) { /*if memory if very limited, performance degrades for large matrices as chunk size is very small, which leads to communication overhead)*/ Integer avail = pnga_memory_avail_type(atype); if (pnga_is_mirrored(g_a)) { fflush(stdout); if (sizeof(Integer)/sizeof(int) > 1) armci_msg_gop_scope(SCOPE_NODE, &avail, 1, "min", ARMCI_LONG); else armci_msg_gop_scope(SCOPE_NODE, &avail, 1, "min", ARMCI_INT); fflush(stdout); } else { fflush(stdout); pnga_gop(pnga_type_f2c(MT_F_INT), &avail, (Integer)1, "min"); } if(availreal == 0) && (((DoubleComplex*)beta)->imag ==0)) need_scaling =0;} else if(atype==C_SCPL){if((((SingleComplex*)beta)->real == 0) && (((SingleComplex*)beta)->imag ==0)) need_scaling =0;} else if(atype==C_DBL){if(*(DoublePrecision *)beta == 0) need_scaling =0;} else if( *(float*)beta ==0) need_scaling =0; pnga_mask_sync(ZERO_I, ZERO_I); clo[0] = cilo; clo[1] = cjlo; chi[0] = cihi; chi[1] = cjhi; if(need_scaling) pnga_scale_patch(g_c, clo, chi, beta); else pnga_fill_patch(g_c, clo, chi, beta); for(jlo = 0; jlo < n; jlo += Jchunk){ /* loop through columns of g_c patch */ jhi = GA_MIN(n-1, jlo+Jchunk-1); jdim= jhi - jlo +1; for(klo = 0; klo < k; klo += Kchunk){ /* loop cols of g_a patch */ khi = GA_MIN(k-1, klo+Kchunk-1); /* loop rows of g_b patch */ kdim= khi - klo +1; /** Each pass through the outer two loops means we need a different patch of B.*/ get_new_B = TRUE; for(ilo = 0; ilo < m; ilo += Ichunk){ /*loop through rows of g_c patch */ if(ijk%nproc == iproc){ ihi = GA_MIN(m-1, ilo+Ichunk-1); idim= cdim = ihi - ilo +1; if(atype == C_FLOAT) for (i = 0; i < idim*jdim; i++) *(((float*)c)+i)=0; else if(atype == C_DBL) for (i = 0; i < idim*jdim; i++) *(((double*)c)+i)=0; else if(atype == C_SCPL) for (i = 0; i < idim*jdim; i++){ ((SingleComplex*)c)[i].real=0; ((SingleComplex*)c)[i].imag=0; } else for (i = 0; i < idim*jdim; i++){ c[i].real=0;c[i].imag=0;} if (*transa == 'n' || *transa == 'N'){ adim = idim; i0= ailo+ilo; i1= ailo+ihi; j0= ajlo+klo; j1= ajlo+khi; clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; pnga_get(g_a, clo, chi, a, &idim); }else{ adim = kdim; i0= ajlo+klo; i1= ajlo+khi; j0= ailo+ilo; j1= ailo+ihi; clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; pnga_get(g_a, clo, chi, a, &kdim); } /* Avoid rereading B if it is the same patch as last time. */ if(get_new_B) { if (*transb == 'n' || *transb == 'N'){ bdim = kdim; i0= bilo+klo; i1= bilo+khi; j0= bjlo+jlo; j1= bjlo+jhi; clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; pnga_get(g_b, clo, chi, b, &kdim); }else{ bdim = jdim; i0= bjlo+jlo; i1= bjlo+jhi; j0= bilo+klo; j1= bilo+khi; clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; pnga_get(g_b, clo, chi, b, &jdim); } get_new_B = FALSE; /* Until J or K change again */ } idim_t=idim; jdim_t=jdim; kdim_t=kdim; adim_t=adim; bdim_t=bdim; cdim_t=cdim; switch(atype) { case C_FLOAT: BLAS_SGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (Real *)alpha, (Real *)a, &adim_t, (Real *)b, &bdim_t, (Real *)&ONE_CF, (Real *)c, &cdim_t); break; case C_DBL: BLAS_DGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (DoublePrecision *)alpha, (DoublePrecision *)a, &adim_t, (DoublePrecision *)b, &bdim_t, (DoublePrecision *)&ONE, (DoublePrecision *)c, &cdim_t); break; case C_DCPL: BLAS_ZGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (DoubleComplex *)alpha, (DoubleComplex *)a, &adim_t, (DoubleComplex *)b, &bdim_t, (DoubleComplex *)&ONE, (DoubleComplex *)c, &cdim_t); break; case C_SCPL: BLAS_CGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (SingleComplex *)alpha, (SingleComplex *)a, &adim_t, (SingleComplex *)b, &bdim_t, (SingleComplex *)&ONE_CF, (SingleComplex *)c, &cdim_t); break; default: pnga_error("ga_matmul_patch: wrong data type", atype); } i0= cilo+ilo; i1= cilo+ihi; j0= cjlo+jlo; j1= cjlo+jhi; if(atype == C_FLOAT || atype == C_SCPL) { clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; pnga_acc(g_c, clo, chi, (float *)c, &cdim, &ONE_CF); } else { clo[0] = i0; clo[1] = j0; chi[0] = i1; chi[1] = j1; pnga_acc(g_c, clo, chi, (DoublePrecision*)c, &cdim, (DoublePrecision*)&ONE); } } ++ijk; } } } #ifndef STATBUF pnga_free(a); #endif if(local_sync_end)pnga_sync(); } #if 0 void gai_matmul_patch(char *transa, char *transb, void *alpha, void *beta, Integer g_a,Integer ailo,Integer aihi,Integer ajlo,Integer ajhi, Integer g_b,Integer bilo,Integer bihi,Integer bjlo,Integer bjhi, Integer g_c,Integer cilo,Integer cihi,Integer cjlo,Integer cjhi) { if(pnga_is_mirrored(g_a)) pnga_matmul_mirrored(transa, transb, alpha, beta, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi); else { _gai_matmul_patch_flag = SET; pnga_matmul(transa, transb, alpha, beta, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi); _gai_matmul_patch_flag = UNSET; } } #endif /*\ select the 2d plane to be used in matrix multiplication * rank: dimension of original array * trans: character signifying whether to use transpose * dims: dimensions of original array * lo, hi: bounds of patch * ipos: position of first patch dimension greater than 1 * jpos: position of second patch dimension greater than 1 * vec_idx: indicator of which dimension data is originally located for vectors * (only applies to transpose flag) \*/ static void gai_setup_2d_patch(Integer rank, char *trans, Integer dims[], Integer lo[], Integer hi[], Integer* ilo, Integer* ihi, Integer* jlo, Integer* jhi, Integer* dim1, Integer* dim2, int* ipos, int* jpos, int *vpos, int vec_idx) { int d,e=0,f=0; char t='n'; int reset_t = 0; int tmp; /* check to see if more than two dimensions of patch have * size greater than 1*/ for(d=0; d0 && ++e>2 ) pnga_error("3-D Patch Detected", 0L); /* determine two locations where patch dimensions are greater than 1*/ *ipos = *jpos = *vpos = -1; for(d=0; dlo[d]) ) { *ipos =d; continue; } if( (*ipos >=0) && (hi[d]>lo[d])) { *jpos =d; break; } } /* we have an ambiguous vector; mark its location */ if (1 == e && *ipos != 0 && *ipos != rank-1) { *vpos = *ipos; } /* if(*ipos >*jpos){Integer t=*ipos; *ipos=*jpos; *jpos=t;} */ /* single element case (trivial) */ if((*ipos <0) && (*jpos <0)){ *ipos =0; *jpos=1; } else{ if(trans == NULL) { trans = &t; reset_t = 1; } /* handle almost trivial case of only one dimension with >1 elements. * None of the conditions in this block are true for a 2D block with more * than 1 element in each of the 2 dimensions so no need to specify * condition that e == 1 */ if(*trans == 'n' || *trans == 'N') { if(*ipos == rank-1) (*ipos)--; /* i cannot be the last dimension */ if(*ipos <0) *ipos = *jpos-1; /* select i dimension based on j */ if(*jpos <0) *jpos = *ipos+1; /* select j dimenison based on i */ } else { if(*ipos <0) *ipos = *jpos-1; /* this condition is probably never reached */ if(*jpos <0) { if (vec_idx < 0) { if(*ipos==0) *jpos = *ipos + 1; else *jpos = (*ipos)--; } else { if (*ipos < vec_idx) { *jpos = vec_idx; } else if (*ipos > vec_idx) { *jpos = *ipos; *ipos = vec_idx; } else { /* this is probably an error if you get this far */ if (*ipos > 0) { *jpos = *ipos; // fix for -Wunused *ipos--; } else { *jpos = 1; *ipos = 0; } } } } } if (reset_t) trans = NULL; } *ilo = lo[*ipos]; *ihi = hi[*ipos]; *jlo = lo[*jpos]; *jhi = hi[*jpos]; *dim1 = dims[*ipos]; *dim2 = dims[*jpos]; #if 0 printf("lo/hi=[%ld:%ld", lo[0], hi[0]); for (d=1; d 0 || pnga_total_blocks(g_b) > 0 || pnga_total_blocks(g_c) > 0) { pnga_matmul_basic(transa, transb, alpha, beta, g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi); return; } /* Check to make sure all global arrays are of the same type */ if (!(pnga_is_mirrored(g_a) == pnga_is_mirrored(g_b) && pnga_is_mirrored(g_a) == pnga_is_mirrored(g_c))) { pnga_error("Processors do not match for all arrays",pnga_nnodes()); } if (pnga_is_mirrored(g_a)) { inode = pnga_cluster_nodeid(); nproc = pnga_cluster_nprocs(inode); iproc = me - pnga_cluster_procid(inode, ZERO_I); } else { nproc = pnga_nnodes(); iproc = me; } pnga_inquire(g_a, &atype, &arank, adims); pnga_inquire(g_b, &btype, &brank, bdims); pnga_inquire(g_c, &ctype, &crank, cdims); if(arank<2) pnga_error("rank of A must be at least 2",arank); if(brank<2) pnga_error("rank of B must be at least 2",brank); if(crank<2) pnga_error("rank of C must be at least 2",crank); if(atype != btype || atype != ctype ) pnga_error(" types mismatch ", 0L); if(atype != C_DCPL && atype != C_DBL && atype != C_FLOAT && atype != C_SCPL) pnga_error(" type error",atype); gai_setup_2d_patch(arank, transa, adims, alo, ahi, &ailo, &aihi, &ajlo, &ajhi, &adim1, &adim2, &aipos, &ajpos, &avpos, avec_pos); gai_setup_2d_patch(brank, transb, bdims, blo, bhi, &bilo, &bihi, &bjlo, &bjhi, &bdim1, &bdim2, &bipos, &bjpos, &bvpos, bvec_pos); gai_setup_2d_patch(crank, NULL, cdims, clo, chi, &cilo, &cihi, &cjlo, &cjhi, &cdim1, &cdim2, &cipos, &cjpos, &cvpos, -1); /* check if patch indices and dims match */ if (*transa == 'n' || *transa == 'N'){ if (ailo <= 0 || aihi > adim1 || ajlo <= 0 || ajhi > adim2) pnga_error(" g_a indices out of range ", g_a); }else if (ailo <= 0 || aihi > adim2 || ajlo <= 0 || ajhi > adim1) pnga_error(" g_a indices out of range ", g_a); if (*transb == 'n' || *transb == 'N'){ if (bilo <= 0 || bihi > bdim1 || bjlo <= 0 || bjhi > bdim2) pnga_error(" g_b indices out of range ", g_b); }else if (bilo <= 0 || bihi > bdim2 || bjlo <= 0 || bjhi > bdim1) pnga_error(" g_b indices out of range ", g_b); if (cilo <= 0 || cihi > cdim1 || cjlo <= 0 || cjhi > cdim2) pnga_error(" g_c indices out of range ", g_c); /* verify if patch dimensions are consistent */ #define RESET() do { \ m = aihi - ailo +1; \ k = ajhi - ajlo +1; \ k2= bihi - bilo +1; \ n = bjhi - bjlo +1; \ cm= cihi - cilo +1; \ cn= cjhi - cjlo +1; \ } while (0) RESET(); #define SHIFT(L,INC) do { \ L##ipos+=INC; \ L##jpos+=INC; \ L##ilo = L##lo[L##ipos]; \ L##ihi = L##hi[L##ipos]; \ L##jlo = L##lo[L##jpos]; \ L##jhi = L##hi[L##jpos]; \ L##dim1 = L##dims[L##ipos]; \ L##dim2 = L##dims[L##jpos]; \ RESET(); \ } while (0) /* gai_setup_2d_patch may produce ambiguous vectors */ if (!(m==cm && k==k2 && n==cn)) { /* patches don't agree */ if (avpos>=0 && bvpos<0 && cvpos<0) { /* only A is an ambiguous vector */ SHIFT(a,-1); } else if (avpos<0 && bvpos>=0 && cvpos<0) { /* only B is an ambiguous vector */ SHIFT(b,-1); } else if (avpos<0 && bvpos<0 && cvpos>=0) { /* only C is an ambiguous vector */ SHIFT(c,-1); } else if (avpos>=0 && bvpos>=0 && cvpos<0) { /* A and B are ambiguous vectors */ if (m != cm) { SHIFT(a,-1); } if (n != cn) { SHIFT(b,-1); } } else if (avpos>=0 && bvpos<0 && cvpos>=0) { /* A and C are ambiguous vectors */ if (k != k2) { SHIFT(a,-1); } if (n != cn) { SHIFT(c,-1); } } else if (avpos<0 && bvpos>=0 && cvpos>=0) { /* B and C are ambiguous vectors */ if (k != k2) { SHIFT(b,-1); } if (m != cm) { SHIFT(c,-1); } } else if (avpos>=0 && bvpos>=0 && cvpos>=0) { /* A and B and C are ambiguous vectors */ pnga_error("a and b and c ambiguous", 1); } } if( (cihi - cilo +1) != m) pnga_error(" a & c dims error",m); if( (cjhi - cjlo +1) != n) pnga_error(" b & c dims error",n); if( (bihi - bilo +1) != k) pnga_error(" a & b dims error",k); chunk_cube = (k*(double)(m*n)) / (min_tasks * nproc); max_chunk = (Integer)pow(chunk_cube, (DoublePrecision)(1.0/3.0) ); if (max_chunk < 32) max_chunk = 32; #ifdef STATBUF if(atype == C_DBL || atype == C_FLOAT){ Ichunk=D_CHUNK, Kchunk=D_CHUNK, Jchunk=D_CHUNK; }else{ Ichunk=ICHUNK; Kchunk=KCHUNK; Jchunk=JCHUNK; } #else { Integer elems, factor = sizeof(DoubleComplex)/GAsizeofM(atype); Ichunk = Jchunk = Kchunk = CHUNK_SIZE; if ( max_chunk > Ichunk) { /*if memory if very limited, performance degrades for large matrices as chunk size is very small, which leads to communication overhead)*/ Integer avail = pnga_memory_avail_type(atype); pnga_gop(pnga_type_f2c(MT_F_INT), &avail, (Integer)1, "min"); if(availreal == 0) && (((DoubleComplex*)beta)->imag ==0)) need_scaling =0;} else if(atype==C_SCPL){if((((SingleComplex*)beta)->real == 0) && (((SingleComplex*)beta)->imag ==0)) need_scaling =0;} else if(atype==C_DBL){if(*(DoublePrecision *)beta == 0)need_scaling =0;} else if( *(float*)beta ==0) need_scaling =0; if(need_scaling) pnga_scale_patch(g_c, clo, chi, beta); else pnga_fill_patch(g_c, clo, chi, beta); for(jlo = 0; jlo < n; jlo += Jchunk){ /* loop through columns of g_c patch */ jhi = GA_MIN(n-1, jlo+Jchunk-1); jdim= jhi - jlo +1; for(klo = 0; klo < k; klo += Kchunk){ /* loop cols of g_a patch */ khi = GA_MIN(k-1, klo+Kchunk-1); /* loop rows of g_b patch */ kdim= khi - klo +1; get_new_B = TRUE; for(ilo = 0; ilo < m; ilo += Ichunk){ /*loop through rows of g_c patch */ if(ijk%nproc == iproc){ ihi = GA_MIN(m-1, ilo+Ichunk-1); idim= cdim = ihi - ilo +1; if(atype == C_FLOAT) for (i = 0; i < idim*jdim; i++) *(((float*)c)+i)=0; else if(atype == C_DBL) for (i = 0; i < idim*jdim; i++) *(((double*)c)+i)=0; else if(atype == C_SCPL) for (i = 0; i < idim*jdim; i++){ ((SingleComplex*)c)[i].real=0; ((SingleComplex*)c)[i].imag=0; } else for (i = 0; i < idim*jdim; i++){ c[i].real=0;c[i].imag=0;} if (*transa == 'n' || *transa == 'N'){ adim = idim; i0= ailo+ilo; i1= ailo+ihi; j0= ajlo+klo; j1= ajlo+khi; }else{ adim = kdim; i0= ajlo+klo; i1= ajlo+khi; j0= ailo+ilo; j1= ailo+ihi; } /* ga_get_(g_a, &i0, &i1, &j0, &j1, a, &adim); */ memcpy(tmplo,alo,arank*sizeof(Integer)); memcpy(tmphi,ahi,arank*sizeof(Integer)); SETINT(tmpld,1,arank-1); tmplo[aipos]=i0; tmphi[aipos]=i1; tmplo[ajpos]=j0; tmphi[ajpos]=j1; tmpld[aipos]=i1-i0+1; tmpld[ajpos]=j1-j0+1; pnga_get(g_a,tmplo,tmphi,a,tmpld); if(get_new_B) { if (*transb == 'n' || *transb == 'N'){ bdim = kdim; i0= bilo+klo; i1= bilo+khi; j0= bjlo+jlo; j1= bjlo+jhi; }else{ bdim = jdim; i0= bjlo+jlo; i1= bjlo+jhi; j0= bilo+klo; j1= bilo+khi; } /* ga_get_(g_b, &i0, &i1, &j0, &j1, b, &bdim); */ memcpy(tmplo,blo,brank*sizeof(Integer)); memcpy(tmphi,bhi,brank*sizeof(Integer)); SETINT(tmpld,1,brank-1); tmplo[bipos]=i0; tmphi[bipos]=i1; tmplo[bjpos]=j0; tmphi[bjpos]=j1; tmpld[bipos]=i1-i0+1; tmpld[bjpos]=j1-j0+1; pnga_get(g_b,tmplo,tmphi,b,tmpld); get_new_B = FALSE; } idim_t=idim; jdim_t=jdim; kdim_t=kdim; adim_t=adim; bdim_t=bdim; cdim_t=cdim; switch(atype) { case C_FLOAT: BLAS_SGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (Real *)alpha, (Real *)a, &adim_t, (Real *)b, &bdim_t, (Real *)&ONE_CF, (Real *)c, &cdim_t); break; case C_DBL: BLAS_DGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (DoublePrecision *)alpha, (DoublePrecision *)a, &adim_t, (DoublePrecision *)b, &bdim_t, (DoublePrecision *)&ONE, (DoublePrecision *)c, &cdim_t); break; case C_DCPL: BLAS_ZGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (DoubleComplex *)alpha, (DoubleComplex *)a, &adim_t, (DoubleComplex *)b, &bdim_t, (DoubleComplex *)&ONE, (DoubleComplex *)c, &cdim_t); break; case C_SCPL: BLAS_CGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (SingleComplex *)alpha, (SingleComplex *)a, &adim_t, (SingleComplex *)b, &bdim_t, (SingleComplex *)&ONE_CF, (SingleComplex *)c, &cdim_t); break; default: pnga_error("ga_matmul_patch: wrong data type", atype); } i0= cilo+ilo; i1= cilo+ihi; j0= cjlo+jlo; j1= cjlo+jhi; /* ga_acc_(g_c, &i0, &i1, &j0, &j1, (DoublePrecision*)c, &cdim, (DoublePrecision*)&ONE); */ memcpy(tmplo,clo,crank*sizeof(Integer)); memcpy(tmphi,chi,crank*sizeof(Integer)); SETINT(tmpld,1,crank-1); tmplo[cipos]=i0; tmphi[cipos]=i1; tmplo[cjpos]=j0; tmphi[cjpos]=j1; tmpld[cipos]=i1-i0+1; tmpld[cjpos]=j1-j0+1; if(atype == C_FLOAT || atype == C_SCPL) pnga_acc(g_c,tmplo,tmphi,(float *)c,tmpld, &ONE_CF); else pnga_acc(g_c,tmplo,tmphi,c,tmpld,(DoublePrecision*)&ONE); } ++ijk; } } } #ifndef STATBUF pnga_free(a); #endif if(local_sync_end)pnga_sync(); } /*\ MATRIX MULTIPLICATION for 2d patches of multi-dimensional arrays * * C[lo:hi,lo:hi] = alpha*op(A)[lo:hi,lo:hi] * op(B)[lo:hi,lo:hi] * + beta *C[lo:hi,lo:hi] * * where: * op(A) = A or A' depending on the transpose flag * [lo:hi,lo:hi] - patch indices _after_ op() operator was applied * * In this interface all patch indices refer to the post-transpose block. For * example, if the transpose of the block [1:4,2:5] is request, the indices * [2:5,1:4] should be used for lo and hi. Note that this interface can not * handle a 1D slice from an array containing more than 2 dimensions. * * \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_matmul_patch = pnga_matmul_patch #endif void pnga_matmul_patch(char *transa, char *transb, void *alpha, void *beta, Integer g_a, Integer alo[], Integer ahi[], Integer g_b, Integer blo[], Integer bhi[], Integer g_c, Integer clo[], Integer chi[]) { gai_matmul_patch(transa,transb,alpha,beta,g_a,alo,ahi,-1, g_b,blo,bhi,-1,g_c,clo,chi); } /*\ MATRIX MULTIPLICATION for 2d patches of multi-dimensional arrays * * C[lo:hi,lo:hi] = alpha*op(A)[lo:hi,lo:hi] * op(B)[lo:hi,lo:hi] * + beta *C[lo:hi,lo:hi] * * where: * op(A) = A or A' depending on the transpose flag * [lo:hi,lo:hi] - patch indices _after_ op() operator was applied * * This interface fixes a bug in the original interface that cannot handle a 1D * slice from an array that is over 3D if the transpose option is used. For * this interface, all patch indices refer to the pre-transpose block, if the * transpose option is used. * \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_matmul_patch_alt = pnga_matmul_patch_alt #endif void pnga_matmul_patch_alt(char *transa, char *transb, void *alpha, void *beta, Integer g_a, Integer alo[], Integer ahi[], Integer g_b, Integer blo[], Integer bhi[], Integer g_c, Integer clo[], Integer chi[]) { int aivec, bivec; int e, d, ipos, jpos; Integer atype, btype, arank, brank, lo, hi; Integer adims[GA_MAX_DIM],bdims[GA_MAX_DIM]; /* if the patch is transposed, find the two indices that correspond to the * non-unit dimensions */ if (*transa == 't' || *transa == 'T') { pnga_inquire(g_a, &atype, &arank, adims); aivec = -1; if (arank > 2) { ipos = -1; jpos = -1; e = 0; /* find out how many patch dimensions are greater than 1*/ for (d=0; d 0 && ipos == -1) { ipos = d; e++; } else if (ahi[d]-alo[d] > 0 && jpos == -1) { jpos = d; e++; } else if (ahi[d]-alo[d] > 0) { pnga_error("Patch A has more than 2 dimensions",0); } } if (e == 0) { aivec = -1; } else if (e == 1) { /* array is a vector */ aivec = ipos; if (ipos < arank-1) { jpos = ipos + 1; } else { jpos = ipos; ipos--; } } else { aivec = -1; } } lo = alo[0]; hi = ahi[0]; alo[0] = alo[1]; ahi[0] = ahi[1]; alo[1] = lo; ahi[1] = hi; } if (*transb == 't' || *transb == 'T') { pnga_inquire(g_b, &btype, &brank, bdims); bivec = -1; if (brank > 2) { ipos = -1; jpos = -1; e = 0; /* find out how many patch dimensions are greater than 1*/ for (d=0; d 0 && ipos == -1) { ipos = d; e++; } else if (bhi[d]-blo[d] > 0 && jpos == -1) { jpos = d; e++; } else if (bhi[d]-blo[d] > 0) { pnga_error("Patch A has more than 2 dimensions",0); } } if (e == 0) { bivec = -1; } else if (e == 1) { /* array is a vector */ bivec = ipos; if (ipos < arank-1) { jpos = ipos + 1; } else { jpos = ipos; ipos--; } } else { bivec = -1; } lo = blo[ipos]; hi = bhi[ipos]; blo[ipos] = blo[jpos]; bhi[ipos] = bhi[jpos]; blo[jpos] = lo; bhi[jpos] = hi; } else { lo = blo[0]; hi = bhi[0]; blo[0] = blo[1]; bhi[0] = bhi[1]; blo[1] = lo; bhi[1] = hi; } } gai_matmul_patch(transa,transb,alpha,beta,g_a,alo,ahi,aivec, g_b,blo,bhi,bivec,g_c,clo,chi); } /** * 1. remove STATBUF * 2. */ void printBlock(char * banner, Integer type, void *ptr, Integer lo[], Integer hi[], Integer ld[]) { Integer i,j; Integer offset; printf("p[%d] %s lo[0]: %d hi[0]: %d lo[1]: %d hi[1]: %d\n", (int)pnga_nodeid(),banner,(int)lo[0],(int)hi[0],(int)lo[1],(int)hi[1]); printf(" "); for (i=lo[0]; i<=hi[0]; i++) printf(" %12d",(int)i); printf("\n"); for (j=lo[1]; j<=hi[1]; j++) { printf("J: %d",(int)j); for (i=lo[0]; i<=hi[0]; i++) { offset = (j-lo[1])*ld[0] + i-lo[0]; switch (type) { case C_FLOAT: printf(" %12.4f",*((float*)ptr+offset)); break; case C_DBL: printf(" %12.4f",*((double*)ptr+offset)); break; case C_DCPL: printf(" [%12.4f:%12.4f]",*((double*)ptr+2*offset), *((double*)ptr+2*offset+1)); break; case C_SCPL: printf(" [%12.4f:%12.4f]",*((float*)ptr+2*offset), *((float*)ptr+2*offset+1)); break; default: pnga_error("ga_matmul_basic: wrong data type", type); } } printf("\n"); } printf("\n\n"); } /** * This is a routine that is designed to work for all layouts but may not be * high performing. */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_matmul_basic = pnga_matmul_basic #endif void pnga_matmul_basic(char *transa, char *transb, void *alpha, void *beta, Integer g_a, Integer alo[], Integer ahi[], Integer g_b, Integer blo[], Integer bhi[], Integer g_c, Integer clo[], Integer chi[]) { Integer atype, btype, ctype; Integer adims[GA_MAX_DIM],bdims[GA_MAX_DIM],cdims[GA_MAX_DIM],tmpld[GA_MAX_DIM]; Integer n, m, k, nb, adim, bdim, cdim, arank, brank, crank; Integer loC[MAXDIM], hiC[MAXDIM], lot[MAXDIM], hit[MAXDIM], lC[MAXDIM]; Integer nlo, loA[MAXDIM], hiA[MAXDIM], loB[MAXDIM], hiB[MAXDIM]; DoubleComplex ONE_Z; SingleComplex ONE_F; BlasInt idim_t, jdim_t, kdim_t, adim_t, bdim_t, cdim_t; char *src_ptr; _iterator_hdl hdl_c; int local_sync_begin,local_sync_end; ONE_Z.real = 1.0; ONE_Z.imag = 0.0; ONE_F.real = 1.0; ONE_F.imag = 0.0; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); /* For the time being, punt on transposes*/ if (*transa == 't' || *transa == 'T' || *transb == 't' || *transb == 'T') { pnga_error("Cannot do basic multiply with tranpose ",0); } /* For the time being, punt on mirrored arrays */ if (pnga_is_mirrored(g_a) || pnga_is_mirrored(g_b) || pnga_is_mirrored(g_c)) { pnga_error("Cannot do basic multiply with mirrored arrays ",0); } pnga_inquire(g_a, &atype, &arank, adims); pnga_inquire(g_b, &btype, &brank, bdims); pnga_inquire(g_c, &ctype, &crank, cdims); /* Can't handle dimensions other than 2 */ if(arank != 2) pnga_error("rank of A must be 2 ",arank); if(brank != 2) pnga_error("rank of B must be 2 ",brank); if(crank != 2) pnga_error("rank of C must be 2 ",crank); if(atype != btype || atype != ctype ) pnga_error(" types mismatch ", 0L); if(atype != C_DCPL && atype != C_DBL && atype != C_FLOAT && atype != C_SCPL) pnga_error(" type error: type not supported ",atype); /* Check that patch dims are reasonable */ for (n=0; n adims[n] || ahi[n] <= 0 || ahi[n] > adims[n]) { pnga_error("Patch is out of bounds for A ",0); } } for (n=0; n bdims[n] || bhi[n] <= 0 || bhi[n] > bdims[n]) { pnga_error("Patch is out of bounds for B ",0); } } for (n=0; n cdims[n] || chi[n] <= 0 || chi[n] > cdims[n]) { pnga_error("Patch is out of bounds for C ",0); } } /* Check that multiplication is feasible */ for (n=0; n ahi[1]) hiA[1] = ahi[1]; ld = hiA[0]-loA[0]+1; size_a = (hiA[0]-loA[0]+1)*(hiA[1]-loA[1]+1); if (size_a > size_c) { printf("p[%d] size_a: %d size_c: %d\n",(int)pnga_nodeid(),(int)size_a,(int)size_c); } /* printf("p[%d] loA[0]: %d hiA[0]: %d loA[1]: %d hiA[1]: %d\n",pnga_nodeid(), loA[0],hiA[0],loA[1],hiA[1]); */ pnga_get(g_a,loA,hiA,a_buf,&ld); /* printBlock("Matrix A",atype,a_buf,loA,hiA,&ld); */ loB[1] = lot[1]; hiB[1] = hit[1]; nlo = blo[0]+in*(hit[0]-lot[0]+1); loB[0] = nlo; hiB[0] = loB[0]+(hit[0]-lot[0]); if (hiB[0] > bhi[0]) hiB[0] = bhi[0]; ld = hiB[0]-loB[0]+1; size_b = (hiB[0]-loB[0]+1)*(hiB[1]-loB[1]+1); if (size_b > size_c) { printf("p[%d] size_b: %d size_c: %d\n",(int)pnga_nodeid(), (int)size_b,(int)size_c); } /* printf("p[%d] loB[0]: %d hiB[0]: %d loB[1]: %d hiB[1]: %d\n",pnga_nodeid(), loB[0],hiB[0],loB[1],hiB[1]); */ pnga_get(g_b,loB,hiB,b_buf,&ld); /* printBlock("Matrix B",btype,b_buf,loB,hiB,&ld); */ idim_t=(BlasInt)(hiA[0]-loA[0]+1); kdim_t=(BlasInt)(hiA[1]-loA[1]+1); jdim_t=(BlasInt)(hiB[1]-loB[1]+1); adim_t=hiA[0]-loA[0]+1; bdim_t=hiB[0]-loB[0]+1; cdim_t=lC[0]; /* printf("p[%d] idim: %d jdim: %d kdim: %d adim: %d bdim: %d cdim: %d\n", pnga_nodeid(),idim_t,jdim_t,kdim_t,adim_t,bdim_t,cdim_t); */ switch(atype) { case C_FLOAT: BLAS_SGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (Real *)alpha, (Real *)a_buf, &adim_t, (Real *)b_buf, &bdim_t, (Real *)&ONE_F, (Real *)c_buf, &cdim_t); break; case C_DBL: BLAS_DGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (DoublePrecision *)alpha, (DoublePrecision *)a_buf, &adim_t, (DoublePrecision *)b_buf, &bdim_t, (DoublePrecision *)&ONE_Z, (DoublePrecision *)c_buf, &cdim_t); break; case C_DCPL: BLAS_ZGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (DoubleComplex *)alpha, (DoubleComplex *)a_buf, &adim_t, (DoubleComplex *)b_buf, &bdim_t, (DoubleComplex *)&ONE_Z, (DoubleComplex *)c_buf, &cdim_t); break; case C_SCPL: BLAS_CGEMM(transa, transb, &idim_t, &jdim_t, &kdim_t, (SingleComplex *)alpha, (SingleComplex *)a_buf, &adim_t, (SingleComplex *)b_buf, &bdim_t, (SingleComplex *)&ONE_F, (SingleComplex *)c_buf, &cdim_t); break; default: pnga_error("ga_matmul_basic: wrong data type", atype); } } /* multiplication is done, free buffers */ free(a_buf); free(b_buf); } } if(local_sync_end)pnga_sync(); } ga-5.9.2/global/src/matmul.h000066400000000000000000000023701500715745200156250ustar00rootroot00000000000000/* $Id: matmul.h,v 1.15.4.1 2006-12-22 13:05:22 manoj Exp $ */ #ifndef _MATMUL_H_ #define _MATMUL_H_ #include "ga.h" #include "globalp.h" #include "message.h" #include "base.h" #if HAVE_MATH_H # include #endif #include "armci.h" #include "galinalg.h" /* min acceptable amount of memory (in elements) and default chunk size */ # define MINMEM 64 # define CHUNK_SIZE 256 # define MAX_CHUNKS 1024 # define BLOCK_SIZE 1024 /* temp buf size for pinning */ # define GA_ASPECT_RATIO 3 # define NUM_MATS 3 # define MINTASKS 10 /* increase this if there is high load imbalance */ # define EXTRA 4 #define MIN_CHUNK_SIZE 256 #define SET 1 #define UNSET 0 extern void gai_matmul_patch_flag(int flag); typedef struct { int lo[2]; /* 2 elements: ilo and klo */ int hi[2]; int dim[2]; int chunkBId; short int do_put; }task_list_t; #define VECTORCHECK(rank,dims,dim1,dim2, ilo, ihi, jlo, jhi) \ if(rank>2) pnga_error("rank is greater than 2",rank); \ else if(rank==2) {dim1=dims[0]; dim2=dims[1];} \ else if(rank==1) {if((ihi-ilo)>0) { dim1=dims[0]; dim2=1;} \ else { dim1=1; dim2=dims[0];}} \ else pnga_error("rank must be atleast 1",rank); #define WAIT_GET_BLOCK(nbhdl) pnga_nbwait(nbhdl) #endif /* _MATMUL_H_ */ ga-5.9.2/global/src/matrix.c000066400000000000000000002475401500715745200156370ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /*$Id: matrix.c,v 1.7.8.11 2007/12/18 18:49:36 d3g293 Exp $****************************************************** File: matrix.c Author: Limin Zhang, Ph.D. Mathematics Department Columbia Basin College Pasco, WA 99301 Limin.Zhang@cbc2.org Mentor: Jarek Naplocha, Ph.D. Environmental Molecular Science Laboratory Richland, WA 99352 Date: 2/28/2002 Purpose: matrix interfaces between TAO and global arrays. **************************************************************/ #include "globalp.h" #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #include "message.h" #include "ga-papi.h" #include "ga-wapi.h" #include "ga_iterator.h" #define auxi_median(a,b,c,m) \ { \ if ((c < a) && (a < b)) m = a; \ else if ((a <= c) && (c <= b)) m = c; \ else m = b; \ } #define median(a,b,c,m) \ { \ if(a == b) m = a; \ else if(a < b) auxi_median(a, b, c, m) \ else auxi_median(b, a, c, m) \ } #define auxi_median_dcpl(na, nb, nc, za, zb, zc, zm) \ { \ if ((nc < na) && (na < nb)) zm = za; \ else if ((na <= nc) && (nc <= nb)) zm = zc; \ else zm = zb; \ } #define median_dcpl(na, nb, nc, za, zb, zc, zm) \ { \ if (na == nb) zm = za; \ else if (na < nb) auxi_median_dcpl (na, nb, nc, za, zb, zc, zm) \ else auxi_median_dcpl (nb, na, nc, zb, za, zc, zm) \ } #define auxi_median_scpl(na, nb, nc, ca, cb, cc, cm) \ { \ if ((nc < na) && (na < nb)) cm = ca; \ else if ((na <= nc) && (nc <= nb)) cm = cc; \ else cm = cb; \ } #define median_scpl(na, nb, nc, ca, cb, cc, cm) \ { \ if (na == nb) cm = ca; \ else if (na < nb) auxi_median_scpl (na, nb, nc, ca, cb, cc, cm) \ else auxi_median_scpl (nb, na, nc, cb, ca, cc, cm) \ } /*\ Utility function to find median values of patch \*/ static void sgai_median_patch_values(Integer type, Integer ndim, Integer *loA, Integer *hiA, Integer *ldA, void *A_ptr, void *B_ptr, void *C_ptr, void *M_ptr, Integer offset) { Integer bvalue[MAXDIM], bunit[MAXDIM], baseldA[MAXDIM]; Integer idx, n1dim; Integer i, j; double na, nb, nc; /*norm of a, norm of b, norm of c */ int ia, ib, ic, im; float fa, fb, fc, fm; double da, db, dc, dm; long la, lb, lc, lm; DoubleComplex za, zb, zc, zm; SingleComplex ca, cb, cc, cm; /* number of n-element of the first dimension */ n1dim = 1; for (i = 1; i < ndim; i++) n1dim *= (hiA[i] - loA[i] + 1); /* calculate the destination indices */ bvalue[0] = 0; bvalue[1] = 0; bunit[0] = 1; bunit[1] = 1; /* baseldA[0] = ldA[0] * baseldA[1] = ldA[0] * ldA[1] * baseldA[2] = ldA[0] * ldA[1] * ldA[2] ..... */ baseldA[0] = ldA[0]; baseldA[1] = baseldA[0] * ldA[1]; for (i = 2; i < ndim; i++) { bvalue[i] = 0; bunit[i] = bunit[i - 1] * (hiA[i - 1] - loA[i - 1] + 1); baseldA[i] = baseldA[i - 1] * ldA[i]; } switch(type) { case C_DBL: A_ptr = (void*)((double*)(A_ptr) + offset); B_ptr = (void*)((double*)(B_ptr) + offset); C_ptr = (void*)((double*)(C_ptr) + offset); M_ptr = (void*)((double*)(M_ptr) + offset); break; case C_INT: A_ptr = (void*)((int*)(A_ptr) + offset); B_ptr = (void*)((int*)(B_ptr) + offset); C_ptr = (void*)((int*)(C_ptr) + offset); M_ptr = (void*)((int*)(M_ptr) + offset); break; case C_DCPL: A_ptr = (void*)((DoubleComplex*)(A_ptr) + offset); B_ptr = (void*)((DoubleComplex*)(B_ptr) + offset); C_ptr = (void*)((DoubleComplex*)(C_ptr) + offset); M_ptr = (void*)((DoubleComplex*)(M_ptr) + offset); break; case C_SCPL: A_ptr = (void*)((SingleComplex*)(A_ptr) + offset); B_ptr = (void*)((SingleComplex*)(B_ptr) + offset); C_ptr = (void*)((SingleComplex*)(C_ptr) + offset); M_ptr = (void*)((SingleComplex*)(M_ptr) + offset); break; case C_FLOAT: A_ptr = (void*)((float*)(A_ptr) + offset); B_ptr = (void*)((float*)(B_ptr) + offset); C_ptr = (void*)((float*)(C_ptr) + offset); M_ptr = (void*)((float*)(M_ptr) + offset); break; case C_LONG: A_ptr = (void*)((long*)(A_ptr) + offset); B_ptr = (void*)((long*)(B_ptr) + offset); C_ptr = (void*)((long*)(C_ptr) + offset); M_ptr = (void*)((long*)(M_ptr) + offset); break; case C_LONGLONG: A_ptr = (void*)((long long*)(A_ptr) + offset); B_ptr = (void*)((long long*)(B_ptr) + offset); C_ptr = (void*)((long long*)(C_ptr) + offset); M_ptr = (void*)((long long*)(M_ptr) + offset); break; default: break; } /*compute elementwise median */ switch (type) { case C_INT: for (i = 0; i < n1dim; i++) { idx = 0; for (j = 1; j < ndim; j++) { idx += bvalue[j] * baseldA[j - 1]; if (((i + 1) % bunit[j]) == 0) bvalue[j]++; if (bvalue[j] > (hiA[j] - loA[j])) bvalue[j] = 0; } for (j = 0; j < (hiA[0] - loA[0] + 1); j++) { ia = ((int *) A_ptr)[idx + j]; ib = ((int *) B_ptr)[idx + j]; ic = ((int *) C_ptr)[idx + j]; median(ia, ib, ic, im); ((int *) M_ptr)[idx + j] = im; } } break; case C_LONG: for (i = 0; i < n1dim; i++) { idx = 0; for (j = 1; j < ndim; j++) { idx += bvalue[j] * baseldA[j - 1]; if (((i + 1) % bunit[j]) == 0) bvalue[j]++; if (bvalue[j] > (hiA[j] - loA[j])) bvalue[j] = 0; } for (j = 0; j < (hiA[0] - loA[0] + 1); j++) { la = ((long *) A_ptr)[idx + j]; lb = ((long *) B_ptr)[idx + j]; lc = ((long *) C_ptr)[idx + j]; median(la, lb, lc, lm); ((long *) M_ptr)[idx + j] = lm; } } break; case C_FLOAT: for (i = 0; i < n1dim; i++) { idx = 0; for (j = 1; j < ndim; j++) { idx += bvalue[j] * baseldA[j - 1]; if (((i + 1) % bunit[j]) == 0) bvalue[j]++; if (bvalue[j] > (hiA[j] - loA[j])) bvalue[j] = 0; } for (j = 0; j < (hiA[0] - loA[0] + 1); j++) { fa = ((float *) A_ptr)[idx + j]; fb = ((float *) B_ptr)[idx + j]; fc = ((float *) C_ptr)[idx + j]; median(fa, fb, fc, fm); ((float *) M_ptr)[idx + j] = fm; } } break; case C_DBL: for (i = 0; i < n1dim; i++) { idx = 0; for (j = 1; j < ndim; j++) { idx += bvalue[j] * baseldA[j - 1]; if (((i + 1) % bunit[j]) == 0) bvalue[j]++; if (bvalue[j] > (hiA[j] - loA[j])) bvalue[j] = 0; } for (j = 0; j < (hiA[0] - loA[0] + 1); j++) { da = ((double *) A_ptr)[idx + j]; db = ((double *) B_ptr)[idx + j]; dc = ((double *) C_ptr)[idx + j]; median(da, db, dc, dm); ((double *) M_ptr)[idx + j] = dm; } } break; case C_DCPL: for (i = 0; i < n1dim; i++) { idx = 0; for (j = 1; j < ndim; j++) { idx += bvalue[j] * baseldA[j - 1]; if (((i + 1) % bunit[j]) == 0) bvalue[j]++; if (bvalue[j] > (hiA[j] - loA[j])) bvalue[j] = 0; } for (j = 0; j < (hiA[0] - loA[0] + 1); j++) { za = ((DoubleComplex *) A_ptr)[idx + j]; zb = ((DoubleComplex *) B_ptr)[idx + j]; zc = ((DoubleComplex *) C_ptr)[idx + j]; na = sqrt ((za.real) * (za.real) + (za.imag) * (za.imag)); nb = sqrt ((zb.real) * (zb.real) + (zb.imag) * (zb.imag)); nc = sqrt ((zc.real) * (zc.real) + (zc.imag) * (zc.imag)); median_dcpl(na, nb, nc, za, zb, zc, zm); ((DoubleComplex *) M_ptr)[idx + j] = zm; } } break; case C_SCPL: for (i = 0; i < n1dim; i++) { idx = 0; for (j = 1; j < ndim; j++) { idx += bvalue[j] * baseldA[j - 1]; if (((i + 1) % bunit[j]) == 0) bvalue[j]++; if (bvalue[j] > (hiA[j] - loA[j])) bvalue[j] = 0; } for (j = 0; j < (hiA[0] - loA[0] + 1); j++) { ca = ((SingleComplex *) A_ptr)[idx + j]; cb = ((SingleComplex *) B_ptr)[idx + j]; cc = ((SingleComplex *) C_ptr)[idx + j]; na = sqrt ((ca.real) * (ca.real) + (ca.imag) * (ca.imag)); nb = sqrt ((cb.real) * (cb.real) + (cb.imag) * (cb.imag)); nc = sqrt ((cc.real) * (cc.real) + (cc.imag) * (cc.imag)); median_scpl(na, nb, nc, ca, cb, cc, cm); ((SingleComplex *) M_ptr)[idx + j] = cm; } } break; default: pnga_error("median: wrong data type", type); } } /*\ median routine. Not sure what this function is suppose to do \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_median_patch = pnga_median_patch #endif void pnga_median_patch( g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi, g_m, mlo, mhi) Integer g_a, *alo, *ahi; /* patch of g_a */ Integer g_b, *blo, *bhi; /* patch of g_b */ Integer g_c, *clo, *chi; /* patch of g_c */ Integer g_m, *mlo, *mhi; /* patch of g_m */ { Integer i, j; Integer atype, btype, andim, adims[MAXDIM], bndim, bdims[MAXDIM]; Integer ctype, mtype, cndim, cdims[MAXDIM], mndim, mdims[MAXDIM]; Integer loA[MAXDIM], hiA[MAXDIM], ldA[MAXDIM]; Integer loB[MAXDIM], hiB[MAXDIM], ldB[MAXDIM]; Integer loC[MAXDIM], hiC[MAXDIM], ldC[MAXDIM]; Integer loM[MAXDIM], hiM[MAXDIM], ldM[MAXDIM]; Integer g_A = g_a, g_B = g_b; Integer g_C = g_c, g_M = g_m; char *A_ptr, *B_ptr; char *C_ptr, *M_ptr; Integer offset; Integer atotal, btotal; Integer ctotal, mtotal; Integer me = pnga_nodeid (), a_temp_created = 0, b_temp_created = 0, c_temp_created = 0; Integer type = GA_TYPE_GSM, compatible; char *tempname = "temp", transp = 'n'; /*no transpose */ Integer num_blocks_a, num_blocks_b, num_blocks_c, num_blocks_m; int local_sync_begin,local_sync_end; _iterator_hdl hdl_a, hdl_b, hdl_c, hdl_m; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); pnga_inquire (g_a, &atype, &andim, adims); pnga_inquire (g_b, &btype, &bndim, bdims); pnga_inquire (g_c, &ctype, &cndim, cdims); pnga_inquire (g_m, &mtype, &mndim, mdims); /* I have to inquire the data type again since nga_inquire and * nga_inquire_internal_ treat data type differently */ pnga_inquire(g_m, &type, &mndim, mdims); if (mtype != atype) pnga_error(" pnga_median_patch:type mismatch ", 0L); if (mtype != btype) pnga_error(" pnga_median_patch:type mismatch ", 0L); if (mtype != ctype) pnga_error(" pnga_median_patch:type mismatch ", 0L); /* check if patch indices and g_a dims match */ for (i = 0; i < andim; i++) if (alo[i] <= 0 || ahi[i] > adims[i]) { pnga_error("pnga_median_patch: g_a indices out of range ", g_a); } for (i = 0; i < bndim; i++) if (blo[i] <= 0 || bhi[i] > bdims[i]) { pnga_error("pnga_median_patch:g_b indices out of range ", g_b); } for (i = 0; i < cndim; i++) if (clo[i] <= 0 || chi[i] > cdims[i]) { pnga_error("pnga_median_patch:g_c indices out of range ", g_c); } for (i = 0; i < mndim; i++) if (mlo[i] <= 0 || mhi[i] > mdims[i]) { pnga_error("pnga_median_patch:g_m indices out of range ", g_m); } /* check if numbers of elements in two patches match each other */ atotal = 1; for (i = 0; i < andim; i++) atotal *= (ahi[i] - alo[i] + 1); btotal = 1; for (i = 0; i < bndim; i++) btotal *= (bhi[i] - blo[i] + 1); ctotal = 1; for (i = 0; i < cndim; i++) ctotal *= (chi[i] - clo[i] + 1); mtotal = 1; for (i = 0; i < mndim; i++) mtotal *= (mhi[i] - mlo[i] + 1); if (mtotal != atotal) pnga_error("pnga_median_patch: capacities of patches do not match ", 0L); if (mtotal != btotal) pnga_error("pnga_median_patch: capacities of patches do not match ", 0L); if (mtotal != ctotal) pnga_error("pnga_median_patch: capacities of patches do not match ", 0L); num_blocks_a = pnga_total_blocks(g_a); num_blocks_b = pnga_total_blocks(g_b); num_blocks_c = pnga_total_blocks(g_c); num_blocks_m = pnga_total_blocks(g_m); if (num_blocks_a < 0 && num_blocks_b < 0 && num_blocks_c < 0 && num_blocks_m < 0) { /* find out coordinates of patches of g_A, g_B, g_C, and g_M that I own */ pnga_distribution (g_A, me, loA, hiA); pnga_distribution (g_B, me, loB, hiB); pnga_distribution (g_C, me, loC, hiC); pnga_distribution (g_M, me, loM, hiM); if (!pnga_comp_patch (andim, loA, hiA, mndim, loM, hiM)) compatible = 1; else compatible = 0; /* pnga_gop(pnga_type_f2c(MT_F_INT), &compatible, 1, "*"); */ pnga_gop(pnga_type_f2c(MT_F_INT), &compatible, 1, "&&"); if (!compatible) { /* either patches or distributions do not match: * - create a temp array that matches distribution of g_a * - copy & reshape patch of g_b into g_B */ if (!pnga_duplicate(g_m, &g_A, tempname)) pnga_error("pnga_median_patch:duplicate failed", 0L); pnga_copy_patch(&transp, g_a, alo, ahi, g_A, mlo, mhi); andim = mndim; a_temp_created = 1; pnga_distribution (g_A, me, loA, hiA); } if (!pnga_comp_patch (bndim, loB, hiB, mndim, loM, hiM)) compatible = 1; else compatible = 0; /* pnga_gop(pnga_type_f2c(MT_F_INT), &compatible, 1, "*"); */ pnga_gop(pnga_type_f2c(MT_F_INT), &compatible, 1, "&&"); if (!compatible) { /* either patches or distributions do not match: * - create a temp array that matches distribution of g_a * - copy & reshape patch of g_c into g_C */ if (!pnga_duplicate(g_m, &g_B, tempname)) pnga_error("pnga_median_patch:duplicate failed", 0L); pnga_copy_patch(&transp, g_b, blo, bhi, g_B, mlo, mhi); bndim = mndim; b_temp_created = 1; pnga_distribution (g_B, me, loB, hiB); } if (!pnga_comp_patch (cndim, loC, hiC, mndim, loM, hiM)) compatible = 1; else compatible = 0; /* pnga_gop(pnga_type_f2c(MT_F_INT), &compatible, 1, "*"); */ pnga_gop(pnga_type_f2c(MT_F_INT), &compatible, 1, "&&"); if (!compatible) { /* either patches or distributions do not match: * - create a temp array that matches distribution of g_a * - copy & reshape patch of g_m into g_M */ if (!pnga_duplicate(g_m, &g_C, tempname)) pnga_error("pnga_median_patch:duplicate failed", 0L); /*no need to copy g_m since it is the output matrix. */ cndim = mndim; c_temp_created = 1; pnga_copy_patch(&transp, g_c, clo, chi, g_C, mlo, mhi); pnga_distribution (g_C, me, loC, hiC); } if (!pnga_comp_patch (mndim, loM, hiM, andim, loA, hiA)) pnga_error(" patches mismatch ", 0); if (!pnga_comp_patch (mndim, loM, hiM, bndim, loB, hiB)) pnga_error(" patches mismatch ", 0); if (!pnga_comp_patch (mndim, loM, hiM, cndim, loC, hiC)) pnga_error(" patches mismatch ", 0); /* A[83:125,1:1] <==> B[83:125] */ if (mndim > andim) mndim = andim; /* need more work */ if (mndim > bndim) mndim = bndim; /* need more work */ if (mndim > cndim) mndim = cndim; /* need more work */ /* determine subsets of my patches to access */ if (pnga_patch_intersect (mlo, mhi, loM, hiM, mndim)) { offset = 0; pnga_access_ptr (g_A, loM, hiM, &A_ptr, ldA); pnga_access_ptr (g_B, loM, hiM, &B_ptr, ldB); pnga_access_ptr (g_C, loM, hiM, &C_ptr, ldC); pnga_access_ptr (g_M, loM, hiM, &M_ptr, ldM); sgai_median_patch_values(type, mndim, loM, hiM, ldM, A_ptr, B_ptr, C_ptr, M_ptr, offset); /* release access to the data */ pnga_release (g_A, loM, hiM); pnga_release (g_B, loM, hiM); pnga_release (g_C, loM, hiM); pnga_release_update (g_M, loM, hiM); } } else { /* create copies of A, B, and C that are identically distributed as M */ if (!pnga_duplicate(g_m, &g_A, tempname)) pnga_error("ga_add_patch: dup failed", 0L); pnga_copy_patch(&transp, g_a, alo, ahi, g_A, mlo, mhi); andim = mndim; a_temp_created = 1; if (!pnga_duplicate(g_m, &g_B, tempname)) pnga_error("ga_add_patch: dup failed", 0L); pnga_copy_patch(&transp, g_b, blo, bhi, g_B, mlo, mhi); bndim = mndim; b_temp_created = 1; if (!pnga_duplicate(g_m, &g_C, tempname)) pnga_error("ga_add_patch: dup failed", 0L); pnga_copy_patch(&transp, g_c, clo, chi, g_C, mlo, mhi); cndim = mndim; c_temp_created = 1; /* M is normally distributed so just get the mean using standard approach */ #if 1 pnga_local_iterator_init(g_A, &hdl_a); pnga_local_iterator_init(g_B, &hdl_b); pnga_local_iterator_init(g_C, &hdl_c); pnga_local_iterator_init(g_m, &hdl_m); while(pnga_local_iterator_next(&hdl_a,loA,hiA,&A_ptr,ldA)) { pnga_local_iterator_next(&hdl_b,loB,hiB,&B_ptr,ldB); pnga_local_iterator_next(&hdl_c,loC,hiC,&C_ptr,ldC); pnga_local_iterator_next(&hdl_m,loM,hiM,&M_ptr,ldM); Integer idx, lod[MAXDIM]/*, hid[MAXDIM]*/; Integer jtot, last; /* make temporary copies of loM and hiM since pnga_patch_intersect destroys original versions */ for (j=0; j mdims[i]) hiM[i] = mdims[i]; /*if (hiM[i] < loM[i]) chk = 0;*/ } /* make temporary copies of loC and hiC since pnga_patch_intersect destroys original versions */ for (j=0; j= blocks[i] && i 0 && jhiA > 0) { switch (type) { int *pi; double *pd; long *pl; float *pf; DoubleComplex *pz; SingleComplex *pc; DoubleComplex zval; SingleComplex cval; double dtemp; float ftemp; case C_INT: pi = (int *) ptr; *isum = GA_ABS(pi[0]); for (j = 0; j < jhiA - jloA + 1; j++) { for (i = 0; i < ihiA - iloA + 1; i++) if (GA_ABS(pi[j*ld + i]) > *isum) *isum = GA_ABS(pi[j*ld+i]); } break; case C_LONG: pl = (long *) ptr; *lsum = GA_ABS(pl[0]); for (j = 0; j < jhiA - jloA + 1; j++) for (i = 0; i < ihiA - iloA + 1; i++) if (GA_ABS(pl[j*ld + i]) > *lsum) *lsum = GA_ABS(pl[j*ld+i]); break; case C_DCPL: pz = (DoubleComplex *) ptr; zval = pz[0]; dtemp = sqrt (zval.real * zval.real + zval.imag * zval.imag); (*zsum).real = dtemp; for (j = 0; j < jhiA - jloA + 1; j++) for (i = 0; i < ihiA - iloA + 1; i++) { zval = pz[j * ld + i]; dtemp = sqrt (zval.real * zval.real + zval.imag * zval.imag); if (dtemp > (*zsum).real) (*zsum).real = dtemp; } break; case C_SCPL: pc = (SingleComplex *) ptr; cval = pc[0]; ftemp = sqrt (cval.real * cval.real + cval.imag * cval.imag); (*csum).real = ftemp; for (j = 0; j < jhiA - jloA + 1; j++) for (i = 0; i < ihiA - iloA + 1; i++) { cval = pc[j * ld + i]; ftemp = sqrt (cval.real * cval.real + cval.imag * cval.imag); if (ftemp > (*csum).real) (*csum).real = ftemp; } break; case C_FLOAT: pf = (float *) ptr; *fsum = pf[0]; for (j = 0; j < jhiA - jloA + 1; j++) for (i = 0; i < ihiA - iloA + 1; i++) if (GA_ABS(pf[j*ld+i]) > *fsum) *fsum = GA_ABS(pf[j*ld+i]); break; case C_DBL: pd = (double *) ptr; *dsum = pd[0]; for (i = 0; i < ihiA - iloA + 1; i++) for (j = 0; j < jhiA - jloA + 1; j++) if (GA_ABS(pd[j*ld+i]) > *dsum) *dsum = GA_ABS(pd[j*ld+i]); break; default: pnga_error("sgai_norm_infinity_block: wrong data type ", type); } } } /* Evaluate the infinity norm of an array. This is the maximum absolute * value of the the elements in the array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_norm_infinity = pnga_norm_infinity #endif void pnga_norm_infinity(Integer g_a, double *nm) { Integer type; Integer me = pnga_nodeid (), i, j, nproc = pnga_nnodes(); Integer ndim, dims[MAXDIM], lo[2], hi[2], ld; Integer num_blocks_a; int local_sync_begin,local_sync_end; int isum = 0; long lsum = 0; double dsum = 0.0; float fsum = 0.0; float fval; double dval; DoubleComplex zsum; SingleComplex csum; char *buf = NULL; char *ptr = NULL; zsum.real = 0.0; zsum.imag = 0.0; csum.real = 0.0; csum.imag = 0.0; _iterator_hdl hdl; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); pnga_check_handle (g_a, "ga_norm_infinity_"); pnga_inquire (g_a, &type, &ndim, dims); if(ndim<=0) pnga_error("ga_norm_infinity: wrong dimension", ndim); else if (ndim >= 3) pnga_error("ga_norm_infinity: wrong dimension", ndim); switch (type) { case C_INT: buf = (void*)(&isum); break; case C_LONG: buf = (void*)(&lsum); break; case C_FLOAT: buf = (void*)(&fsum); break; case C_DBL: buf = (void*)(&dsum); break; case C_DCPL: buf = (void*)(&zsum); break; case C_SCPL: buf = (void*)(&csum); break; default: pnga_error("ga_norm_infinity_: wrong data type:", type); } #if 1 pnga_local_iterator_init(g_a, &hdl); while (pnga_local_iterator_next(&hdl,lo,hi,&ptr,&ld)) { sgai_norm_infinity_block(g_a, ptr, lo, hi, ld, type, ndim, dims, buf); } #else num_blocks_a = pnga_total_blocks(g_a); if (num_blocks_a < 0) { pnga_distribution(g_a, me, lo, hi); pnga_access_ptr(g_a, lo, hi, &ptr, &ld); sgai_norm_infinity_block(g_a, ptr, lo, hi, ld, type, ndim, dims, buf); pnga_release_update(g_a, lo, hi); } else { Integer idx; /* Simple block-cyclic data distribution */ if (!pnga_uses_proc_grid(g_a)) { for (idx = me; idx < num_blocks_a; idx += nproc) { pnga_distribution(g_a, idx, lo, hi); pnga_access_block_ptr(g_a, idx, &ptr, &ld); sgai_norm_infinity_block(g_a, ptr, lo, hi, ld, type, ndim, dims, buf); pnga_release_update_block(g_a, idx); } } else { /* Uses scalapack block-cyclic data distribution */ Integer chk; Integer proc_index[MAXDIM], index[MAXDIM]; Integer topology[MAXDIM]; Integer blocks[MAXDIM], block_dims[MAXDIM]; pnga_get_proc_index(g_a, me, proc_index); pnga_get_proc_index(g_a, me, index); pnga_get_block_info(g_a, blocks, block_dims); pnga_get_proc_grid(g_a, topology); while (index[ndim-1] < blocks[ndim-1]) { /* find bounding coordinates of block */ chk = 1; for (i = 0; i < ndim; i++) { lo[i] = index[i]*block_dims[i]+1; hi[i] = (index[i] + 1)*block_dims[i]; if (hi[i] > dims[i]) hi[i] = dims[i]; if (hi[i] < lo[i]) chk = 0; } if (chk) { pnga_access_block_grid_ptr(g_a, index, &ptr, &ld); sgai_norm_infinity_block(g_a, ptr, lo, hi, ld, type, ndim, dims, buf); pnga_release_update_block_grid(g_a, index); } /* increment index to get next block on processor */ index[0] += topology[0]; for (i = 0; i < ndim; i++) { if (index[i] >= blocks[i] && i 0 && jhiA > 0) { switch (type) { int *pi; double *pd; long *pl; float *pf; DoubleComplex *pz; SingleComplex *pc; case C_INT: pi = (int *) ptr; *isum = 0; for (i = 0; i < ihiA - iloA + 1; i++) for (j = 0; j < jhiA - jloA + 1; j++) *isum += GA_ABS (pi[j * ld + i]); break; case C_LONG: pl = (long *) ptr; *lsum = 0; for (i = 0; i < ihiA - iloA + 1; i++) for (j = 0; j < jhiA - jloA + 1; j++) *lsum += GA_ABS (pl[j * ld + i]); break; case C_DCPL: pz = (DoubleComplex *) ptr; (*zsum).real = 0.0; (*zsum).imag = 0.0; for (i = 0; i < ihiA - iloA + 1; i++) for (j = 0; j < jhiA - jloA + 1; j++) { DoubleComplex zval = pz[j * ld + i]; double temp = sqrt (zval.real * zval.real + zval.imag * zval.imag); (*zsum).real += temp; } break; case C_SCPL: pc = (SingleComplex *) ptr; (*csum).real = 0.0; (*csum).imag = 0.0; for (i = 0; i < ihiA - iloA + 1; i++) for (j = 0; j < jhiA - jloA + 1; j++) { SingleComplex cval = pc[j * ld + i]; float temp = sqrt (cval.real * cval.real + cval.imag * cval.imag); (*csum).real += temp; } break; case C_FLOAT: pf = (float *) ptr; *fsum = 0.0; for (i = 0; i < ihiA - iloA + 1; i++) for (j = 0; j < jhiA - jloA + 1; j++) *fsum += GA_ABS (pf[j * ld + i]); break; case C_DBL: pd = (double *) ptr; *dsum = 0.0; for (i = 0; i < ihiA - iloA + 1; i++) for (j = 0; j < jhiA - jloA + 1; j++) *dsum += GA_ABS (pd[j * ld + i]); break; default: pnga_error("sgai_norm1_block: wrong data type ", type); } } } /* Evaluate the L_1 norm of an array. This should be the sum of the absolute * value of the individual array elements*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_norm1 = pnga_norm1 #endif void pnga_norm1(Integer g_a, double *nm) { Integer type=0; Integer me = pnga_nodeid (), i, j, nproc = pnga_nnodes(); Integer ndim, dims[MAXDIM], lo[2], hi[2], ld; Integer num_blocks_a; int local_sync_begin,local_sync_end; int isum = 0; long lsum = 0; double dsum = 0.0; float fsum = 0.0; double dval; float fval; DoubleComplex zsum; SingleComplex csum; char *buf = NULL; /*temporary buffer */ char *ptr = NULL; zsum.real = 0.0; zsum.imag = 0.0; csum.real = 0.0; csum.imag = 0.0; _iterator_hdl hdl; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); pnga_check_handle (g_a, "ga_norm1_"); pnga_inquire (g_a, &type, &ndim, dims); if(ndim<=0 || ndim > 2) pnga_error("ga_norm1: wrong dimension", ndim); switch (type) { case C_INT: buf = (void*)(&isum); break; case C_LONG: buf = (void*)(&lsum); break; case C_FLOAT: buf = (void*)(&fsum); break; case C_DBL: buf = (void*)(&dsum); break; case C_DCPL: buf = (void*)(&zsum); break; case C_SCPL: buf = (void*)(&csum); break; default: pnga_error("ga_norm1_: wrong data type:", type); } #if 1 pnga_local_iterator_init(g_a, &hdl); while (pnga_local_iterator_next(&hdl,lo,hi,&ptr,&ld)) { sgai_norm1_block(g_a, ptr, lo, hi, ld, type, ndim, dims, buf); } #else num_blocks_a = pnga_total_blocks(g_a); if (num_blocks_a < 0) { pnga_distribution(g_a, me, lo, hi); pnga_access_ptr(g_a, lo, hi, &ptr, &ld); sgai_norm1_block(g_a, ptr, lo, hi, ld, type, ndim, dims, buf); pnga_release_update(g_a, lo, hi); } else { Integer idx; /* Simple block-cyclic data distribution */ if (!pnga_uses_proc_grid(g_a)) { for (idx = me; idx < num_blocks_a; idx += nproc) { pnga_distribution(g_a, idx, lo, hi); pnga_access_block_ptr(g_a, idx, &ptr, &ld); sgai_norm1_block(g_a, ptr, lo, hi, ld, type, ndim, dims, buf); pnga_release_update_block(g_a, idx); } } else { /* Uses scalapack block-cyclic data distribution */ Integer chk; Integer proc_index[MAXDIM], index[MAXDIM]; Integer topology[MAXDIM]; Integer blocks[MAXDIM], block_dims[MAXDIM]; pnga_get_proc_index(g_a, me, proc_index); pnga_get_proc_index(g_a, me, index); pnga_get_block_info(g_a, blocks, block_dims); pnga_get_proc_grid(g_a, topology); while (index[ndim-1] < blocks[ndim-1]) { /* find bounding coordinates of block */ chk = 1; for (i = 0; i < ndim; i++) { lo[i] = index[i]*block_dims[i]+1; hi[i] = (index[i] + 1)*block_dims[i]; if (hi[i] > dims[i]) hi[i] = dims[i]; if (hi[i] < lo[i]) chk = 0; } if (chk) { pnga_access_block_grid_ptr(g_a, index, &ptr, &ld); sgai_norm1_block(g_a, ptr, lo, hi, ld, type, ndim, dims, buf); pnga_release_update_block_grid(g_a, index); } /* increment index to get next block on processor */ index[0] += topology[0]; for (i = 0; i < ndim; i++) { if (index[i] >= blocks[i] && i 0) { lo[0] = GA_MAX (iloA, jloA); lo[1] = GA_MAX (iloA, jloA); hi[0] = GA_MIN (ihiA, jhiA); hi[1] = GA_MIN (ihiA, jhiA); if (hi[0] >= lo[0]) /*make sure the equality symbol is there!!! */ { /* we got a block containing diagonal elements */ /*allocate a buffer for the given vector g_v */ size = GAsizeof (type); vlo = GA_MAX (iloA, jloA); vhi = GA_MIN (ihiA, jhiA); nelem = vhi - vlo + 1; buf = malloc (nelem * size); if (buf == NULL) pnga_error ("ga_get_diag_:failed to allocate memory for the local buffer.", 9999); /* get the vector from the global array g_a, put that in the the local memory buffer buf */ switch (type) { case C_INT: ia = (int *) ptr; ia += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { ((int *) buf)[i] = *ia; ia += ld + 1; } break; case C_LONG: la = (long *) ptr; la += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { ((long *) buf)[i] = *la; la += ld + 1; } break; case C_FLOAT: fa = (float *) ptr; fa += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { ((float *) buf)[i] = *fa; fa += ld + 1; } break; case C_DBL: da = (double *) ptr; da += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { ((double *) buf)[i] = *da; da += ld + 1; } break; case C_DCPL: dca = (DoubleComplex *) ptr; dca += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { (((DoubleComplex *) buf)[i]).real = (*dca).real; (((DoubleComplex *) buf)[i]).imag = (*dca).imag; dca += ld + 1; } break; case C_SCPL: fca = (SingleComplex *) ptr; fca += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { (((SingleComplex *) buf)[i]).real = (*fca).real; (((SingleComplex *) buf)[i]).imag = (*fca).imag; fca += ld + 1; } break; default: pnga_error("get_diagonal_zero: wrong data type:", type); } /* copy the local memory buffer buf to g_v */ pnga_put(g_v, &vlo, &vhi, buf, &vhi); /*free the memory */ free (buf); } } } /* Get the diagonal elements of a matrix and copy them into a one dimensional * array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_get_diag = pnga_get_diag #endif void pnga_get_diag(Integer g_a, Integer g_v) { Integer vndim, vdims, dim1, dim2, vtype, atype, type; Integer me = pnga_nodeid (), i, nproc = pnga_nnodes(); Integer andim, adims[2]; Integer loA[2], hiA[2], ld; Integer num_blocks_a; int local_sync_begin,local_sync_end; char *ptr; _iterator_hdl hdl; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); pnga_check_handle (g_a, "ga_get_diag_"); pnga_check_handle (g_v, "ga_get_diag_"); pnga_inquire (g_a, &atype, &andim, adims); dim1 = adims[0]; dim2 = adims[1]; type = atype; pnga_inquire (g_v, &vtype, &vndim, &vdims); /* Perform some error checking */ if (andim != 2) pnga_error("ga_get_diag: wrong dimension for g_a.", andim); if (vndim != 1) pnga_error("ga_get_diag: wrong dimension for g_v.", vndim); if (vdims != GA_MIN (dim1, dim2)) pnga_error ("ga_get_diag: The size of the first array's diagonal is greater than the size of the second array.", type); if (vtype != atype) { pnga_error ("ga_get_diag: input global arrays do not have the same data type. Global array type =", atype); } #if 1 pnga_local_iterator_init(g_a, &hdl); while (pnga_local_iterator_next(&hdl,loA,hiA,&ptr,&ld)) { sgai_get_diagonal_block(g_a, ptr, g_v, loA, hiA, ld, type); } #else num_blocks_a = pnga_total_blocks(g_a); if (num_blocks_a < 0) { pnga_distribution(g_a, me, loA, hiA); pnga_access_ptr(g_a, loA, hiA, &ptr, &ld); sgai_get_diagonal_block(g_a, ptr, g_v, loA, hiA, ld, type); pnga_release_update(g_a, loA, hiA); } else { Integer idx; /* Simple block-cyclic data distribution */ if (!pnga_uses_proc_grid(g_a)) { for (idx = me; idx < num_blocks_a; idx += nproc) { pnga_distribution(g_a, idx, loA, hiA); pnga_access_block_ptr(g_a, idx, &ptr, &ld); sgai_get_diagonal_block(g_a, ptr, g_v, loA, hiA, ld, type); pnga_release_update_block(g_a, idx); } } else { /* Uses scalapack block-cyclic data distribution */ Integer chk; Integer proc_index[MAXDIM], index[MAXDIM]; Integer topology[MAXDIM]; Integer blocks[MAXDIM], block_dims[MAXDIM]; pnga_get_proc_index(g_a, me, proc_index); pnga_get_proc_index(g_a, me, index); pnga_get_block_info(g_a, blocks, block_dims); pnga_get_proc_grid(g_a, topology); while (index[andim-1] < blocks[andim-1]) { /* find bounding coordinates of block */ chk = 1; for (i = 0; i < andim; i++) { loA[i] = index[i]*block_dims[i]+1; hiA[i] = (index[i] + 1)*block_dims[i]; if (hiA[i] > adims[i]) hiA[i] = adims[i]; if (hiA[i] < loA[i]) chk = 0; } if (chk) { pnga_access_block_grid_ptr(g_a, index, &ptr, &ld); sgai_get_diagonal_block(g_a, ptr, g_v, loA, hiA, ld, type); pnga_release_update_block_grid(g_a, index); } /* increment index to get next block on processor */ index[0] += topology[0]; for (i = 0; i < andim; i++) { if (index[i] >= blocks[i] && i 0) { lo[0] = GA_MAX (iloA, jloA); lo[1] = GA_MAX (iloA, jloA); hi[0] = GA_MIN (ihiA, jhiA); hi[1] = GA_MIN (ihiA, jhiA); if (hi[0] >= lo[0]) /*make sure the equality symbol is there!!! */ { /* we got a block containing diagonal elements */ /*allocate a buffer for the given vector g_v */ size = GAsizeof (type); vlo = GA_MAX (iloA, jloA); vhi = GA_MIN (ihiA, jhiA); nelem = vhi - vlo + 1; buf = malloc (nelem * size); if (buf == NULL) pnga_error ("ga_add_diagonal_:failed to allocate memory for the local buffer.", 0); /* get the vector from the global array to the local memory buffer */ pnga_get (g_v, &vlo, &vhi, buf, &vhi); switch (type) { case C_INT: ia = (int *) ptr; ia += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { *ia += ((int *) buf)[i]; ia += ld + 1; } break; case C_LONG: la = (long *) ptr; la += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { *la += ((long *) buf)[i]; la += ld + 1; } break; case C_FLOAT: fa = (float *) ptr; fa += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { *fa += ((float *) buf)[i]; fa += ld + 1; } break; case C_DBL: da = (double *) ptr; da += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { *da += ((double *) buf)[i]; da += ld + 1; } break; case C_DCPL: dca = (DoubleComplex *) ptr; dca += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { (*dca).real += (((DoubleComplex *) buf)[i]).real; (*dca).imag += (((DoubleComplex *) buf)[i]).imag; dca += ld + 1; } break; case C_SCPL: fca = (SingleComplex *) ptr; fca += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { (*fca).real += (((SingleComplex *) buf)[i]).real; (*fca).imag += (((SingleComplex *) buf)[i]).imag; fca += ld + 1; } break; default: pnga_error("ga_add_diagonal_: wrong data type:", type); } /*free the memory */ free (buf); } } } /* Add values in vector g_v to diagonal elements of g_a */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_add_diagonal = pnga_add_diagonal #endif void pnga_add_diagonal(Integer g_a, Integer g_v) { Integer vndim, vdims, dim1, dim2, vtype, atype, type; Integer me = pnga_nodeid (), i, nproc = pnga_nnodes(); Integer andim, adims[2]; Integer loA[2], hiA[2], ld; Integer num_blocks_a; int local_sync_begin,local_sync_end; char *ptr; _iterator_hdl hdl; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); pnga_check_handle (g_a, "ga_add_diagonal_"); pnga_check_handle (g_v, "ga_add_diagonal_"); pnga_inquire(g_a, &atype, &andim, adims); dim1 = adims[0]; dim2 = adims[1]; type = atype; pnga_inquire(g_v, &vtype, &vndim, &vdims); /* Perform some error checking */ if (andim != 2) pnga_error("ga_add_diagonal: wrong dimension for g_a.", andim); if (vndim != 1) pnga_error("ga_add_diagonal: wrong dimension for g_v.", vndim); if (vdims != GA_MIN (dim1, dim2)) pnga_error ("ga_add_diagonal: The size of the first array's diagonal is greater than the size of the second array.", type); if (vtype != atype) { pnga_error ("ga_add_diagonal: input global arrays do not have the same data type. Global array type =", atype); } #if 1 pnga_local_iterator_init(g_a, &hdl); while (pnga_local_iterator_next(&hdl,loA,hiA,&ptr,&ld)) { sgai_add_diagonal_block(g_a, ptr, g_v, loA, hiA, ld, type); } #else num_blocks_a = pnga_total_blocks(g_a); if (num_blocks_a < 0) { pnga_distribution(g_a, me, loA, hiA); pnga_access_ptr(g_a, loA, hiA, &ptr, &ld); sgai_add_diagonal_block(g_a, ptr, g_v, loA, hiA, ld, type); } else { Integer idx; /* Simple block-cyclic data distribution */ if (!pnga_uses_proc_grid(g_a)) { for (idx = me; idx < num_blocks_a; idx += nproc) { pnga_distribution(g_a, idx, loA, hiA); pnga_access_block_ptr(g_a, idx, &ptr, &ld); sgai_add_diagonal_block(g_a, ptr, g_v, loA, hiA, ld, type); pnga_release_update_block(g_a, idx); } } else { /* Uses scalapack block-cyclic data distribution */ Integer chk; Integer proc_index[MAXDIM], index[MAXDIM]; Integer topology[MAXDIM]; Integer blocks[MAXDIM], block_dims[MAXDIM]; pnga_get_proc_index(g_a, me, proc_index); pnga_get_proc_index(g_a, me, index); pnga_get_block_info(g_a, blocks, block_dims); pnga_get_proc_grid(g_a, topology); while (index[andim-1] < blocks[andim-1]) { /* find bounding coordinates of block */ chk = 1; for (i = 0; i < andim; i++) { loA[i] = index[i]*block_dims[i]+1; hiA[i] = (index[i] + 1)*block_dims[i]; if (hiA[i] > adims[i]) hiA[i] = adims[i]; if (hiA[i] < loA[i]) chk = 0; } if (chk) { pnga_access_block_grid_ptr(g_a, index, &ptr, &ld); sgai_add_diagonal_block(g_a, ptr, g_v, loA, hiA, ld, type); pnga_release_update_block_grid(g_a, index); } /* increment index to get next block on processor */ index[0] += topology[0]; for (i = 0; i < andim; i++) { if (index[i] >= blocks[i] && i 0) { lo[0] = GA_MAX (iloA, jloA); lo[1] = GA_MAX (iloA, jloA); hi[0] = GA_MIN (ihiA, jhiA); hi[1] = GA_MIN (ihiA, jhiA); if (hi[0] >= lo[0]) /*make sure the equality symbol is there!!! */ { /* we got a block containing diagonal elements*/ /*allocate a buffer for the given vector g_v */ size = GAsizeof (type); vlo = GA_MAX (iloA, jloA); vhi = GA_MIN (ihiA, jhiA); nelem = vhi - vlo + 1; buf = malloc (nelem * size); if (buf == NULL) pnga_error ("ga_set_diagonal_:failed to allocate memory for local buffer",0); /* get the vector from the global array to the local memory buffer */ pnga_get (g_v, &vlo, &vhi, buf, &vhi); switch (type) { case C_INT: ia = (int *) ptr; ia += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { *ia = ((int *) buf)[i]; ia += ld + 1; } break; case C_LONG: la = (long *) ptr; la += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { *la = ((long *) buf)[i]; la += ld + 1; } break; case C_FLOAT: fa = (float *) ptr; fa += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { *fa = ((float *) buf)[i]; fa += ld + 1; } break; case C_DBL: da = (double *) ptr; da += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { *da = ((double *) buf)[i]; da += ld + 1; } break; case C_DCPL: dca = (DoubleComplex *) ptr; dca += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { (*dca).real = (((DoubleComplex *) buf)[i]).real; (*dca).imag = (((DoubleComplex *) buf)[i]).imag; dca += ld + 1; } break; case C_SCPL: fca = (SingleComplex *) ptr; fca += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { (*fca).real = (((SingleComplex *) buf)[i]).real; (*fca).imag = (((SingleComplex *) buf)[i]).imag; fca += ld + 1; } break; default: pnga_error("ga_set_diagonal_: wrong data type:", type); } /*free the memory */ free (buf); /* release access to the data */ lo[0] = iloA; lo[1] = jloA; hi[0] = ihiA; hi[1] = jhiA; pnga_release_update (g_a, lo, hi); } } } /* Set diagonal of g_a to have values in g_v */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_set_diagonal = pnga_set_diagonal #endif void pnga_set_diagonal(Integer g_a, Integer g_v) { Integer vndim, vdims, dim1, dim2, vtype, atype, type; Integer me = pnga_nodeid (), i, nproc = pnga_nnodes(); Integer andim, adims[2]; Integer loA[2], hiA[2], ld; Integer num_blocks_a; int local_sync_begin,local_sync_end; char *ptr; _iterator_hdl hdl; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); pnga_check_handle (g_a, "ga_set_diagonal_"); pnga_check_handle (g_v, "ga_set_diagonal_"); pnga_inquire (g_a, &atype, &andim, adims); dim1 = adims[0]; dim2 = adims[1]; type = atype; pnga_inquire (g_v, &vtype, &vndim, &vdims); /* Perform some error checking */ if (andim != 2) pnga_error("ga_set_diagonal: wrong dimension for g_a.", andim); if (vndim != 1) pnga_error("ga_set_diagonal: wrong dimension for g_v.", vndim); if (vdims != GA_MIN (dim1, dim2)) pnga_error ("ga_set_diagonal: The size of the first array's diagonal is greater than the size of the second array.", type); if (vtype != atype) { pnga_error ("ga_set_diagonal: input global arrays do not have the same data type. Global array type =", atype); } #if 1 pnga_local_iterator_init(g_a, &hdl); while (pnga_local_iterator_next(&hdl,loA,hiA,&ptr,&ld)) { sgai_set_diagonal_block(g_a, ptr, g_v, loA, hiA, ld, type); } #else num_blocks_a = pnga_total_blocks(g_a); if (num_blocks_a < 0) { pnga_distribution(g_a, me, loA, hiA); pnga_access_ptr(g_a, loA, hiA, &ptr, &ld); sgai_set_diagonal_block(g_a, ptr, g_v, loA, hiA, ld, type); pnga_release_update(g_a, loA, hiA); } else { Integer idx; /* Simple block-cyclic data distribution */ if (!pnga_uses_proc_grid(g_a)) { for (idx = me; idx < num_blocks_a; idx += nproc) { pnga_distribution(g_a, idx, loA, hiA); pnga_access_block_ptr(g_a, idx, &ptr, &ld); sgai_set_diagonal_block(g_a, ptr, g_v, loA, hiA, ld, type); pnga_release_update_block(g_a, idx); } } else { /* Uses scalapack block-cyclic data distribution */ Integer chk; Integer proc_index[MAXDIM], index[MAXDIM]; Integer topology[MAXDIM]; Integer blocks[MAXDIM], block_dims[MAXDIM]; pnga_get_proc_index(g_a, me, proc_index); pnga_get_proc_index(g_a, me, index); pnga_get_block_info(g_a, blocks, block_dims); pnga_get_proc_grid(g_a, topology); while (index[andim-1] < blocks[andim-1]) { /* find bounding coordinates of block */ chk = 1; for (i = 0; i < andim; i++) { loA[i] = index[i]*block_dims[i]+1; hiA[i] = (index[i] + 1)*block_dims[i]; if (hiA[i] > adims[i]) hiA[i] = adims[i]; if (hiA[i] < loA[i]) chk = 0; } if (chk) { pnga_access_block_grid_ptr(g_a, index, &ptr, &ld); sgai_set_diagonal_block(g_a, ptr, g_v, loA, hiA, ld, type); pnga_release_update_block_grid(g_a, index); } /* increment index to get next block on processor */ index[0] += topology[0]; for (i = 0; i < andim; i++) { if (index[i] >= blocks[i] && i 0) { lo[0] = GA_MAX (iloA, jloA); lo[1] = GA_MAX (iloA, jloA); hi[0] = GA_MIN (ihiA, jhiA); hi[1] = GA_MIN (ihiA, jhiA); if (hi[0] >= lo[0]) /*make sure the equality sign is there since it is the singleton case */ { /* we got a block containing diagonal elements */ switch (type) { case C_INT: ia = (int *) ptr; ia += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { *ia += *((int *) c); ia += ld + 1; } break; case C_LONG: la = (long *) ptr; la += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { *la += *((long *) c); la += ld + 1; } break; case C_FLOAT: fa = (float *) ptr; fa += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { *fa += *((float *) c); fa += ld + 1; } break; case C_DBL: da = (double *) ptr; da += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { *da += *((double *) c); da += ld + 1; } break; case C_DCPL: dca = (DoubleComplex *) ptr; dca += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { (*dca).real += (*((DoubleComplex *) c)).real; (*dca).imag += (*((DoubleComplex *) c)).imag; dca += ld + 1; } break; case C_SCPL: fca = (SingleComplex *) ptr; fca += ld*(lo[1]-jloA) + lo[0]-iloA; for (i = 0; i < hi[0] - lo[0] + 1; i++) { (*fca).real += (*((SingleComplex *) c)).real; (*fca).imag += (*((SingleComplex *) c)).imag; fca += ld + 1; } break; default: pnga_error("ga_shift_diagonal_: wrong data type:", type); } } } } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_shift_diagonal = pnga_shift_diagonal #endif void pnga_shift_diagonal(Integer g_a, void *c) { Integer loA[2], hiA[2]/*, dim1, dim2*/, ld; Integer andim, adims[2], type, atype; Integer me = pnga_nodeid (), i, nproc = pnga_nnodes(); char *ptr; Integer num_blocks_a; int local_sync_begin,local_sync_end; _iterator_hdl hdl; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); pnga_check_handle (g_a, "ga_shift_diagonal_"); pnga_inquire (g_a, &atype, &andim, adims); /*dim1 = adims[0];*/ /*dim2 = adims[1];*/ type = atype; if (andim != 2) pnga_error("Dimension must be 2 for shift diagonal operation",andim); #if 1 pnga_local_iterator_init(g_a, &hdl); while (pnga_local_iterator_next(&hdl,loA,hiA,&ptr,&ld)) { sgai_shift_diagonal_block(g_a, ptr, loA, hiA, ld, c, type); } #else num_blocks_a = pnga_total_blocks(g_a); if (num_blocks_a < 0) { pnga_distribution(g_a, me, loA, hiA); pnga_access_ptr(g_a, loA, hiA, &ptr, &ld); sgai_shift_diagonal_block(g_a, ptr, loA, hiA, ld, c, type); pnga_release_update(g_a, loA, hiA); } else { Integer idx; /* Simple block-cyclic data distribution */ if (!pnga_uses_proc_grid(g_a)) { for (idx = me; idx < num_blocks_a; idx += nproc) { pnga_distribution(g_a, idx, loA, hiA); pnga_access_block_ptr(g_a, idx, &ptr, &ld); sgai_shift_diagonal_block(g_a, ptr, loA, hiA, ld, c, type); pnga_release_update_block(g_a, idx); } } else { /* Uses scalapack block-cyclic data distribution */ Integer chk; Integer proc_index[MAXDIM], index[MAXDIM]; Integer topology[MAXDIM]; Integer blocks[MAXDIM], block_dims[MAXDIM]; pnga_get_proc_index(g_a, me, proc_index); pnga_get_proc_index(g_a, me, index); pnga_get_block_info(g_a, blocks, block_dims); pnga_get_proc_grid(g_a, topology); while (index[andim-1] < blocks[andim-1]) { /* find bounding coordinates of block */ chk = 1; for (i = 0; i < andim; i++) { loA[i] = index[i]*block_dims[i]+1; hiA[i] = (index[i] + 1)*block_dims[i]; if (hiA[i] > adims[i]) hiA[i] = adims[i]; if (hiA[i] < loA[i]) chk = 0; } if (chk) { pnga_access_block_grid_ptr(g_a, index, &ptr, &ld); sgai_shift_diagonal_block(g_a, ptr, loA, hiA, ld, c, type); pnga_release_update_block_grid(g_a, index); } /* increment index to get next block on processor */ index[0] += topology[0]; for (i = 0; i < andim; i++) { if (index[i] >= blocks[i] && i 0) { lo[0] = GA_MAX (loA[0], loA[1]); lo[1] = GA_MAX (loA[0], loA[1]); hi[0] = GA_MIN (hiA[0], hiA[1]); hi[1] = GA_MIN (hiA[0], hiA[1]); if (hi[0] >= lo[0]) { sgai_zero_diagonal_block(g_a, ptr, lo, hi, ld, offset, type); } } } #else num_blocks_a = pnga_total_blocks(g_a); if (num_blocks_a < 0) { offset = 0; pnga_distribution (g_a, me, loA, hiA); /* determine subset of my patch to access */ if (loA[0] > 0) { lo[0] = GA_MAX (loA[0], loA[1]); lo[1] = GA_MAX (loA[0], loA[1]); hi[0] = GA_MIN (hiA[0], hiA[1]); hi[1] = GA_MIN (hiA[0], hiA[1]); if (hi[0] >= lo[0]) { /* we got a block containing diagonal elements */ pnga_access_ptr (g_a, lo, hi, &ptr, &ld); sgai_zero_diagonal_block(g_a, ptr, lo, hi, ld, offset, type); /* release access to the data */ pnga_release_update (g_a, lo, hi); } } } else { Integer idx, lld[MAXDIM], offset; Integer jtot, last, j; Integer nproc = pnga_nnodes(); /* Simple block-cyclic data distribution */ if (!pnga_uses_proc_grid(g_a)) { for (idx = me; idx < num_blocks_a; idx += nproc) { pnga_distribution(g_a, idx, loA, hiA); lo[0] = GA_MAX (loA[0], loA[1]); lo[1] = GA_MAX (loA[0], loA[1]); hi[0] = GA_MIN (hiA[0], hiA[1]); hi[1] = GA_MIN (hiA[0], hiA[1]); if (hi[0] >= lo[0]) { pnga_access_block_ptr(g_a, idx, &ptr, lld); /* evaluate offsets for system */ offset = 0; last = andim - 1; jtot = 1; for (j=0; j adims[i]) hiA[i] = adims[i]; /*if (hiA[i] < loA[i]) chk = 0;*/ } lo[0] = GA_MAX (loA[0], loA[1]); lo[1] = GA_MAX (loA[0], loA[1]); hi[0] = GA_MIN (hiA[0], hiA[1]); hi[1] = GA_MIN (hiA[0], hiA[1]); if (hi[0] >= lo[0]) { pnga_access_block_grid_ptr(g_a, index, &ptr, lld); /* evaluate offsets for system */ offset = 0; last = andim - 1; jtot = 1; for (j=0; j= blocks[i] && i 0) { if (hi[0] >= lo[0]) { /*make sure the equality symbol is there!!! */ /* we got a block containing diagonal elements */ Integer myrows = hi[0] - lo[0] + 1; Integer i, j; /*number of rows on the patch is jhiA - jloA + 1 */ vlo =lo[0] ; vhi = hi[0]; /*allocate a buffer for the given vector g_v */ size = GAsizeof (type); buf = malloc (myrows * size); if (buf == NULL) pnga_error ("ga_scale_rows_:failed to allocate memory for the local buffer.", 0); /* get the vector from the global array to the local memory buffer */ pnga_get (g_v, &vlo, &vhi, buf, &vhi); switch (type) { case C_INT: ia = (int *) ptr; for (i = 0; i < hi[0]-lo[0]+1; i++) /*for each row */ for(j=0;j #include "mtwister.h" inline static void m_seedRand(MTRand* rand, unsigned long seed) { /* set initial seeds to mt[STATE_VECTOR_LENGTH] using the generator * from Line 25 of Table 1 in: Donald Knuth, "The Art of Computer * Programming," Vol. 2 (2nd Ed.) pp.102. */ rand->mt[0] = seed & 0xffffffff; for(rand->index=1; rand->indexindex++) { rand->mt[rand->index] = (6069 * rand->mt[rand->index-1]) & 0xffffffff; } } /** * Creates a new random number generator from a given seed. */ MTRand seedRand(unsigned long seed) { MTRand rand; m_seedRand(&rand, seed); return rand; } /** * Generates a pseudo-randomly generated long. */ unsigned long genRandLong(MTRand* rand) { unsigned long y; static unsigned long mag[2] = {0x0, 0x9908b0df}; /* mag[x] = x * 0x9908b0df for x = 0,1 */ if(rand->index >= STATE_VECTOR_LENGTH || rand->index < 0) { /* generate STATE_VECTOR_LENGTH words at a time */ int kk; if(rand->index >= STATE_VECTOR_LENGTH+1 || rand->index < 0) { m_seedRand(rand, 4357); } for(kk=0; kkmt[kk] & UPPER_MASK) | (rand->mt[kk+1] & LOWER_MASK); rand->mt[kk] = rand->mt[kk+STATE_VECTOR_M] ^ (y >> 1) ^ mag[y & 0x1]; } for(; kkmt[kk] & UPPER_MASK) | (rand->mt[kk+1] & LOWER_MASK); rand->mt[kk] = rand->mt[kk+(STATE_VECTOR_M-STATE_VECTOR_LENGTH)] ^ (y >> 1) ^ mag[y & 0x1]; } y = (rand->mt[STATE_VECTOR_LENGTH-1] & UPPER_MASK) | (rand->mt[0] & LOWER_MASK); rand->mt[STATE_VECTOR_LENGTH-1] = rand->mt[STATE_VECTOR_M-1] ^ (y >> 1) ^ mag[y & 0x1]; rand->index = 0; } y = rand->mt[rand->index++]; y ^= (y >> 11); y ^= (y << 7) & TEMPERING_MASK_B; y ^= (y << 15) & TEMPERING_MASK_C; y ^= (y >> 18); return y; } /** * Generates a pseudo-randomly generated double in the range [0..1]. */ double genRand(MTRand* rand) { return((double)genRandLong(rand) / (unsigned long)0xffffffff); } ga-5.9.2/global/src/mtwister.h000066400000000000000000000006311500715745200162020ustar00rootroot00000000000000#ifndef __MTWISTER_H #define __MTWISTER_H #define STATE_VECTOR_LENGTH 624 #define STATE_VECTOR_M 397 /* changes to STATE_VECTOR_LENGTH also require changes to this */ typedef struct tagMTRand { unsigned long mt[STATE_VECTOR_LENGTH]; int index; } MTRand; MTRand seedRand(unsigned long seed); unsigned long genRandLong(MTRand* rand); double genRand(MTRand* rand); #endif /* #ifndef __MTWISTER_H */ ga-5.9.2/global/src/nbutil.c000066400000000000000000000272341500715745200156240ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "globalp.h" #include "base.h" #include "ga-papi.h" #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #define DEBUG 0 /* WARNING: The maximum value MAX_NUM_NB_HDLS can assume is 256. If it is any larger, * the 8-bit field defined in gai_hbhdl_t will exceed its upper limit of 255 in * some parts of the nbutil.c code */ #define MAX_NUM_NB_HDLS 256 static int nb_max_outstanding = MAX_NUM_NB_HDLS; /** * NOTES * The non-blocking GA handle indexes into a list of structs that point to a * linked list of non-blocking ARMCI calls. The first link in the list is * contained in the GA struct. Conversely, each link in the non-blocking list * points to the GA handle that contains the head of the list. When a new GA * non-blocking call is created, the code looks at the list of GA handles and * tries to find one that is not currently being used. If it can't find one, it * calls wait on an existing call and recycles that handle for the new call. * * Similarly, each GA call consists of multiple ARMCI non-blocking calls. The * handles for each of these calls are assembled into a list. If no handle is * available, the ARMCI_Wait function is called on a handle, freeing it for use. * The handle is also removed from the linked list pointed to by the original GA * struct. It is possible in this scheme that a GA struct has a linked list that * contains no links. When wait of test is called in this case, the struct is * marked as inactive and then returns without performing any ARMCI operations. */ /* The structure of gai_nbhdl_t (this is our internal handle). It maps directly * to a 32-bit integer*/ typedef struct { unsigned int ihdl_index:8; unsigned int ga_nbtag:24; } gai_nbhdl_t; /* We create an array of type struct_armci_hdl_t. This list represents the * number of available ARMCI non-blocking calls that are available to create * non-blocking GA calls. Each element in the armci handle linked list is of * type ga_armcihdl_t. * handle: int handle or gai_nbhdl_t struct that represents ARMCI handle for * non-blocking call * next: pointer to next element in list * previous: pointer to previous element in list * ga_hdlarr_index: index that points back to ga_nbhdl_array list. * This can be used to remove this link from GA linked list if * this armci request must be cleared to make room for a new * request. * active: indicates that this represents an outstanding ARMCI non-blocking * request */ typedef struct struct_armcihdl_t { armci_hdl_t handle; struct struct_armcihdl_t *next; struct struct_armcihdl_t *previous; int ga_hdlarr_index; int active; } ga_armcihdl_t; /* We create an array of type ga_nbhdl_array_t. Each of the elements in this * array is the head of the armci handle linked list that is associated with * each GA call. * ahandle: head node in a linked list of ARMCI handles * count: total number of ARMCI handles in linked list * ga_nbtag: unique tag that matches tag in handle (gai_nbhdl_t) * If count is 0 or ahandle is null, there are no outstanding armci calls * associated with this GA handle */ typedef struct{ ga_armcihdl_t *ahandle; int count; int ga_nbtag; int active; } ga_nbhdl_array_t; /** * Array of headers for non-blocking GA calls. The ihdl_index element of the * non-blocking handle indexes into this array. The maximum number of * outstanding non-blocking GA calls is nb_max_outstanding. */ static ga_nbhdl_array_t ga_ihdl_array[MAX_NUM_NB_HDLS]; /** * Array of armci handles. This is used to construct linked lists of ARMCI * non-blocking calls. The maximum number of outstanding ARMCI non-blocking * calls is nb_max_outstanding. */ static ga_armcihdl_t armci_ihdl_array[MAX_NUM_NB_HDLS]; static int lastGAhandle = -1; /* last assigned ga handle */ static int lastARMCIhandle = -1; /* last assigned armci handle */ /** * get a unique tag for each individual ARMCI call. These tags currently repeat * after 16777216=2^24 non-blocking calls */ static unsigned int ga_nb_tag = -1; unsigned int get_next_tag(){ ga_nb_tag++; ga_nb_tag = ga_nb_tag%16777216; return ga_nb_tag; /* return(++ga_nb_tag); */ } /** * Initialize some data structures used in the non-blocking function calls */ void gai_nb_init() { int i; char *value; /* This is a hideous kluge, but some users want to be able to set this * externally. The fact that only integer handles are exchanged between GA and * the underlying runtime make it very difficult to handle in a more elegant * manner. */ nb_max_outstanding = MAX_NUM_NB_HDLS; /* default */ value = getenv("COMEX_MAX_NB_OUTSTANDING"); if (NULL != value) { nb_max_outstanding = atoi(value); } if (nb_max_outstanding <1 || nb_max_outstanding > MAX_NUM_NB_HDLS) { pnga_error("Illegal number of outstanding Non-block requests specified", nb_max_outstanding); } for (i=0; iihdl_index; ga_armcihdl_t* next = ga_ihdl_array[index].ahandle; lastARMCIhandle++; lastARMCIhandle = lastARMCIhandle%nb_max_outstanding; top = lastARMCIhandle+nb_max_outstanding; /* default index if no handles are available */ iloc = lastARMCIhandle; for (i=lastARMCIhandle; inext = armci_ihdl_array[iloc].next; if (armci_ihdl_array[iloc].next != NULL) { armci_ihdl_array[iloc].next->previous = armci_ihdl_array[iloc].previous; } } else { /* link is first in linked list. Need to update header */ ga_ihdl_array[iga_hdl].ahandle = armci_ihdl_array[iloc].next; if (armci_ihdl_array[iloc].next != NULL) { armci_ihdl_array[iloc].next->previous = NULL; } } ga_ihdl_array[iga_hdl].count--; } /* Initialize armci handle and add this operation to the linked list * corresponding to nbhandle */ ARMCI_INIT_HANDLE(&armci_ihdl_array[iloc].handle); armci_ihdl_array[iloc].active = 1; armci_ihdl_array[iloc].previous = NULL; if (ga_ihdl_array[index].ahandle) { ga_ihdl_array[index].ahandle->previous = &armci_ihdl_array[iloc]; } armci_ihdl_array[iloc].next = ga_ihdl_array[index].ahandle; ga_ihdl_array[index].ahandle = &armci_ihdl_array[iloc]; armci_ihdl_array[iloc].ga_hdlarr_index = index; ga_ihdl_array[index].count++; /* reset lastARMCIhandle to iloc */ lastARMCIhandle = iloc; return &armci_ihdl_array[iloc].handle; } /** * the wait routine which is called inside pnga_nbwait. This always returns * zero. The return value is not checked in the code. */ int nga_wait_internal(Integer *nbhandle){ gai_nbhdl_t *inbhandle = (gai_nbhdl_t *)nbhandle; int index = inbhandle->ihdl_index; int retval = 0; int tag = inbhandle->ga_nbtag; /* check if tags match. If they don't then this request was already completed * so the handle can be used for another GA non-blocking call. Just return in * this case */ if (tag == ga_ihdl_array[index].ga_nbtag) { if (ga_ihdl_array[index].active == 0) { printf("p[%ld] nga_wait_internal: GA NB handle %d inactive slot: %d tag: %d\n", GAme,*((int*)inbhandle),index,tag); } ga_armcihdl_t* next = ga_ihdl_array[index].ahandle; /* Loop over linked list and complete all remaining armci non-blocking calls */ while(next) { ga_armcihdl_t* tmp = next->next; /* Complete the call */ ARMCI_Wait(&next->handle); /* reinitialize armci_hlt_t data structure */ next->next = NULL; next->previous = NULL; next->active = 0; ARMCI_INIT_HANDLE(&next->handle); next = tmp; } ga_ihdl_array[index].ahandle = NULL; ga_ihdl_array[index].count = 0; ga_ihdl_array[index].active = 0; } return(retval); } /** * the test routine which is called inside nga_nbtest. Return 0 if operation is * completed */ int nga_test_internal(Integer *nbhandle) { gai_nbhdl_t *inbhandle = (gai_nbhdl_t *)nbhandle; int index = inbhandle->ihdl_index; int retval = 0; int tag = inbhandle->ga_nbtag; /* check if tags match. If they don't then this request was already completed * so the handle can be used for another GA non-blocking call. Just return in * this case */ if (tag == ga_ihdl_array[index].ga_nbtag) { ga_armcihdl_t* next = ga_ihdl_array[index].ahandle; /* Loop over linked list and test all remaining armci non-blocking calls */ while(next) { int ret = ARMCI_Test(&next->handle); ga_armcihdl_t *tmp = next->next; if (ret == 0) { /* operation is completed so remove it from linked list */ if (next->previous != NULL) { /* operation is not first element in list */ next->previous->next = next->next; if (next->next != NULL) { next->next->previous = next->previous; } } else { /* operation is first element in list */ ga_ihdl_array[index].ahandle = next->next; if (next->next != NULL) { next->next->previous = NULL; } } next->previous = NULL; next->next = NULL; next->active = 0; ga_ihdl_array[index].count--; } next = tmp; } if (ga_ihdl_array[index].count == 0) { ga_ihdl_array[index].ahandle = NULL; ga_ihdl_array[index].active = 0; } if (ga_ihdl_array[index].count > 0) retval = 1; } return(retval); } /** * Find a free GA non-blocking handle. */ void ga_init_nbhandle(Integer *nbhandle) { int i, top, idx, iloc; gai_nbhdl_t *inbhandle = (gai_nbhdl_t *)nbhandle; lastGAhandle++; lastGAhandle = lastGAhandle%nb_max_outstanding; top = lastGAhandle+nb_max_outstanding; /* default index if no handles are available */ idx = lastGAhandle; for (i=lastGAhandle; iihdl_index = idx; oldhdl->ga_nbtag = ga_ihdl_array[idx].ga_nbtag; nga_wait_internal(&itmp); } inbhandle->ihdl_index = idx; inbhandle->ga_nbtag = get_next_tag(); ga_ihdl_array[idx].ahandle = NULL; ga_ihdl_array[idx].count = 0; ga_ihdl_array[idx].active = 1; ga_ihdl_array[idx].ga_nbtag = inbhandle->ga_nbtag; /* reset lastGAhandle to idx */ lastGAhandle = idx; return; } ga-5.9.2/global/src/onesided.c000066400000000000000000004303031500715745200161140ustar00rootroot00000000000000/* $Id: onesided.c,v 1.80.2.18 2007/12/18 22:22:27 d3g293 Exp $ */ /* * module: onesided.c * author: Jarek Nieplocha * description: implements GA primitive communication operations -- * accumulate, scatter, gather, read&increment & synchronization * * DISCLAIMER * * This material was prepared as an account of work sponsored by an * agency of the United States Government. Neither the United States * Government nor the United States Department of Energy, nor Battelle, * nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR * ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, * COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, * SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT * INFRINGE PRIVATELY OWNED RIGHTS. * * * ACKNOWLEDGMENT * * This software and its documentation were produced with United States * Government support under Contract Number DE-AC06-76RLO-1830 awarded by * the United States Department of Energy. The United States Government * retains a paid-up non-exclusive, irrevocable worldwide license to * reproduce, prepare derivative works, perform publicly and display * publicly by or for the US Government, including the right to * distribute to other US Government contractors. */ #if HAVE_CONFIG_H # include "config.h" #endif #define USE_GATSCAT_NEW #if HAVE_STDIO_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STDINT_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_STDDEF_H #include #endif #include "global.h" #include "globalp.h" #include "base.h" #include "ga_iterator.h" #include "armci.h" #include "macdecls.h" #include "ga-papi.h" #include "ga-wapi.h" #include "thread-safe.h" #define DEBUG 0 #define USE_MALLOC 1 #define INVALID_MA_HANDLE -1 #define NEAR_INT(x) (x)< 0.0 ? ceil( (x) - 0.5) : floor((x) + 0.5) #ifdef PROFILE_OLD #include "ga_profile.h" #endif #define DISABLE_NBOPT /* disables Non-Blocking OPTimization */ /*uncomment line below to verify consistency of MA in every sync */ /*#define CHECK_MA yes */ char *fence_array; static int GA_fence_set=0; static int GA_prealloc_gatscat = 0; static Integer *GA_header; static Integer *GA_list; static Integer *GA_elems; extern void armci_read_strided(void*, int, int*, int*, char*); extern void armci_write_strided(void*, int, int*, int*, char*); extern armci_hdl_t* get_armci_nbhandle(Integer *); /***************************************************************************/ /** * Synchronize all processes on group */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_pgroup_sync = pnga_pgroup_sync #endif void pnga_pgroup_sync(Integer grp_id) { #ifdef CHECK_MA Integer status; #endif /* printf("p[%d] calling ga_pgroup_sync on group: %d\n",GAme,*grp_id); */ #ifdef USE_ARMCI_GROUP_FENCE int grp = (int)grp_id; ARMCI_GroupFence(&grp); pnga_msg_pgroup_sync(grp_id); #else if (grp_id > 0) { # ifdef MSG_COMMS_MPI /* fence on all processes in this group */ { int p; for(p=0;p GAnproc) pnga_error("permute_proc: error ", (nproc));\ \ /* every process generates different random sequence */\ (void)srand((unsigned)GAme); \ \ /* initialize list */\ for(_i=0; _i< (nproc); _i++) ProcListPerm[_i]=_i;\ \ /* list permutation generated by random swapping */\ for(_i=0; _i< (nproc); _i++){ \ iswap = (int)(rand() % (nproc)); \ temp = ProcListPerm[iswap]; \ ProcListPerm[iswap] = ProcListPerm[_i]; \ ProcListPerm[_i] = temp; \ } \ }\ } #define gaShmemLocation(proc, g_a, _i, _j, ptr_loc, _pld) \ { \ Integer _ilo, _ihi, _jlo, _jhi, offset, proc_place, g_handle=(g_a)+GA_OFFSET;\ Integer _lo[2], _hi[2], _p_handle; \ Integer _iw = GA[g_handle].width[0]; \ Integer _jw = GA[g_handle].width[1]; \ Integer _num_rstrctd = GA[g_handle].num_rstrctd; \ \ ga_ownsM(g_handle, (proc),_lo,_hi); \ _p_handle = GA[g_handle].p_handle; \ if (_p_handle != 0) { \ proc_place = proc; \ if (_num_rstrctd != 0) { \ proc_place = GA[g_handle].rstrctd_list[proc_place]; \ } \ } else { \ proc_place = PGRP_LIST[_p_handle].inv_map_proc_list[proc]; \ } \ _ilo = _lo[0]; _ihi=_hi[0]; \ _jlo = _lo[1]; _jhi=_hi[1]; \ \ if((_i)<_ilo || (_i)>_ihi || (_j)<_jlo || (_j)>_jhi){ \ char err_string[ERR_STR_LEN]; \ sprintf(err_string,"%s:p=%ld invalid i/j (%ld,%ld)><(%ld:%ld,%ld:%ld)", \ "gaShmemLocation", (long)proc, (long)(_i),(long)(_j), \ (long)_ilo, (long)_ihi, (long)_jlo, (long)_jhi); \ pnga_error(err_string, g_a ); \ } \ offset = ((_i)-_ilo+_iw) + (_ihi-_ilo+1+2*_iw)*((_j)-_jlo+_jw); \ \ /* find location of the proc in current cluster pointer array */ \ *(ptr_loc) = GA[g_handle].ptr[proc_place] + \ offset*GAsizeofM(GA[g_handle].type); \ *(_pld) = _ihi-_ilo+1+2*_iw; \ } /*\ Return pointer (ptr_loc) to location in memory of element with subscripts * (subscript). Also return physical dimensions of array in memory in ld. \*/ #define gam_Location(proc, g_handle, subscript, ptr_loc, ld) \ { \ Integer _offset=0, _d, _w, _factor=1, _last=GA[g_handle].ndim-1; \ Integer _lo[MAXDIM], _hi[MAXDIM], _pinv, _p_handle; \ \ ga_ownsM(g_handle, proc, _lo, _hi); \ gaCheckSubscriptM(subscript, _lo, _hi, GA[g_handle].ndim); \ if(_last==0) ld[0]=_hi[0]- _lo[0]+1+2*(Integer)GA[g_handle].width[0]; \ for(_d=0; _d < _last; _d++) { \ _w = (Integer)GA[g_handle].width[_d]; \ _offset += (subscript[_d]-_lo[_d]+_w) * _factor; \ ld[_d] = _hi[_d] - _lo[_d] + 1 + 2*_w; \ _factor *= ld[_d]; \ } \ _offset += (subscript[_last]-_lo[_last] \ + (Integer)GA[g_handle].width[_last]) \ * _factor; \ _p_handle = GA[g_handle].p_handle; \ if (_p_handle != 0) { \ if (GA[g_handle].num_rstrctd == 0) { \ _pinv = proc; \ } else { \ _pinv = GA[g_handle].rstrctd_list[proc]; \ } \ } else { \ _pinv = PGRP_LIST[_p_handle].inv_map_proc_list[proc]; \ } \ *(ptr_loc) = GA[g_handle].ptr[_pinv]+_offset*GA[g_handle].elemsize; \ } #define gam_GetRangeFromMap(p, ndim, plo, phi){\ Integer _mloc = p* ndim *2;\ *plo = (Integer*)_ga_map + _mloc;\ *phi = *plo + ndim;\ } /* compute index of point subscripted by plo relative to point subscripted by lo, for a block with dimensions dims */ #define gam_ComputePatchIndex(ndim, lo, plo, dims, pidx){ \ Integer _d, _factor; \ *pidx = plo[0] -lo[0]; \ for(_d= 0,_factor=1; _d< ndim -1; _d++){ \ _factor *= (dims[_d]); \ *pidx += _factor * (plo[_d+1]-lo[_d+1]); \ } \ } #define gam_GetBlockPatch(plo,phi,lo,hi,blo,bhi,ndim) { \ Integer _d; \ for (_d=0; _d= plo[_d]) blo[_d] = lo[_d]; \ else blo[_d] = plo[_d]; \ if (hi[_d] <= phi[_d] && hi[_d] >= plo[_d]) bhi[_d] = hi[_d]; \ else bhi[_d] = phi[_d]; \ } \ } /** * A routine to test for a non-blocking call */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_nbtest = pnga_nbtest #endif Integer pnga_nbtest(Integer *nbhandle) { return !nga_test_internal((Integer *)nbhandle); } /** * A routine to wait for a non-blocking call to complete */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_nbwait = pnga_nbwait #endif void pnga_nbwait(Integer *nbhandle) { GA_Internal_Threadsafe_Lock(); nga_wait_internal((Integer *)nbhandle); GA_Internal_Threadsafe_Unlock(); } static void ngai_puts(char *loc_base_ptr, char *pbuf, int *stride_loc, char *prem, int *stride_rem, int *count, int nstrides, int proc, int field_off, int field_size, int type_size) { if(field_size<0 || field_size == type_size) { ARMCI_PutS(pbuf,stride_loc,prem,stride_rem,count,nstrides,proc); } else { int i; count -= 1; stride_loc -= 1; stride_rem -= 1; nstrides += 1; for(i=1; i= 0) { proc = (int)GA_proclist[p]; proc = PGRP_LIST[p_handle].inv_map_proc_list[proc]; } */ if(GA_fence_set)fence_array[proc]=1; #ifndef NO_GA_STATS if(proc == GAme){ gam_CountElems(ndim, plo, phi, &elems); GAbytes.putloc += (double)size*elems; } #endif /*casting what ganb_get_armci_handle function returns to armci_hdl is very crucial here as on 64 bit platforms, pointer is 64 bits where as temporary is only 32 bits*/ if(nbhandle) { /* ARMCI_NbPutS(pbuf, stride_loc, prem, stride_rem, count, ndim -1, */ /* proc,(armci_hdl_t*)get_armci_nbhandle(nbhandle)); */ ngai_nbputs(buf,pbuf, stride_loc, prem, stride_rem, count, ndim -1, proc,field_off, field_size, size, (armci_hdl_t*)get_armci_nbhandle(nbhandle)); } else { #if defined(DISABLE_NBOPT) /* ARMCI_PutS(pbuf,stride_loc,prem,stride_rem,count,ndim-1,proc); */ ngai_puts(buf, pbuf,stride_loc,prem,stride_rem,count,ndim-1,proc, field_off, field_size, size); #else /* do blocking put for local processes. If all processes are remote processes then do blocking put for the last one */ if((loop==0 && gai_iterator_last(&it_hdl)) || loop==1) { /* ARMCI_PutS(pbuf,stride_loc,prem,stride_rem,count,ndim-1,proc); */ ngai_puts(buf, pbuf,stride_loc,prem,stride_rem,count,ndim-1, proc, field_off, field_size, size); } else { ++counter; /* ARMCI_NbPutS(pbuf,stride_loc,prem,stride_rem,count, ndim-1, */ /* proc,(armci_hdl_t*)get_armci_nbhandle(&ga_nbhandle)); */ ngai_nbputs(buf,pbuf,stride_loc,prem,stride_rem,count, ndim-1, proc, field_off, field_size, size, (armci_hdl_t*)get_armci_nbhandle(&ga_nbhandle)); } #endif } #if !defined(DISABLE_NBOPT) } /* end if(cond) */ #endif } #if !defined(DISABLE_NBOPT) } if(!nbhandle) nga_wait_internal(&ga_nbhandle); #endif #ifdef PROFILE_OLD ga_profile_stop(); #endif gai_iterator_destroy(&it_hdl); } /** * (Non-blocking) Put an N-dimensional patch of data into a Global Array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_nbput = pnga_nbput #endif void pnga_nbput(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, Integer *nbhandle) { GA_Internal_Threadsafe_Lock(); ngai_put_common(g_a,lo,hi,buf,ld,0,-1,nbhandle); GA_Internal_Threadsafe_Unlock(); } /** * (Non-blocking) Put an N-dimensional patch of data into a Global Array and notify the other side with information on another Global Array */ #define HANDLES_OUTSTANDING 100 /* Maximum number of outstanding put/notify handles */ typedef struct { Integer *orighdl; Integer firsthdl; Integer elementhdl; void *elem_copy; } gai_putn_hdl_t; static int putn_handles_initted = 0; static gai_putn_hdl_t putn_handles[HANDLES_OUTSTANDING]; static int putn_check_single_elem(Integer g_a, Integer *lon, Integer *hin) { int ndims, i; ndims = pnga_ndim(g_a); for (i = 0; i < ndims; i++) if (lon[i] != hin[i]) return 0; return 1; } /* putn_check_single_elem */ static int putn_find_empty_slot(void) { int i; for (i = 0; i < HANDLES_OUTSTANDING; i++) if (!putn_handles[i].orighdl) return i; return -1; } /* putn_find_empty_slot */ static int putn_intersect_coords(Integer g_a, Integer *lo, Integer *hi, Integer *ecoords) { int ndims, i; ndims = pnga_ndim(g_a); for (i = 0; i < ndims; i++) if ((ecoords[i] < lo[i]) || (ecoords[i] > hi[i])) return 0; return 1; } /* putn_intersect_coords */ static int putn_verify_element_in_buf(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, Integer *ecoords, void *bufn, Integer elemSize) { int i, ndims; #if HAVE_STDDEF_H ptrdiff_t off = (char *)bufn - (char *)buf; #else Integer off = (char *)bufn - (char *)buf; #endif Integer eoff = 0; off /= elemSize; /* Offset in terms of elements */ ndims = pnga_ndim(g_a); eoff = ecoords[0] - lo[0]; /* Check in Fortran ordering */ for (i = 1; i < ndims; i++) eoff += (ecoords[i] - lo[i]) * ld[i - 1]; return (eoff == (Integer)off); /* Must be the same for a correct notify buffer */ } /* putn_verify_element_in_buf */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_nbput_notify = pnga_nbput_notify #endif void pnga_nbput_notify(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, Integer g_b, Integer *ecoords, void *bufn, Integer *nbhandle) { Integer ldn[MAXDIM] = { 1 }; int pos, intersect; /* Make sure everything has been initialized */ if (!putn_handles_initted) { memset(putn_handles, 0, sizeof(putn_handles)); putn_handles_initted = 1; } pos = putn_find_empty_slot(); if (pos == -1) /* no empty handles available */ pnga_error("Too many outstanding put/notify operations!", 0); putn_handles[pos].orighdl = nbhandle; /* Store original handle for nbwait_notify */ if (g_a == g_b) intersect = putn_intersect_coords(g_a, lo, hi, ecoords); else intersect = 0; if (!intersect) { /* Simpler case */ ngai_put_common(g_a, lo, hi, buf, ld, 0, -1, &putn_handles[pos].firsthdl); ngai_put_common(g_b, ecoords, ecoords, bufn, ldn, 0, -1, &putn_handles[pos].elementhdl); putn_handles[pos].elem_copy = NULL; } else { int ret, i; Integer handle = GA_OFFSET + g_a, size; void *elem_copy; char *elem; size = GA[handle].elemsize; ret = putn_verify_element_in_buf(g_a, lo, hi, buf, ld, ecoords, bufn, size); if (!ret) pnga_error("Intersecting buffers, but notify element is not in buffer!", 0); elem_copy = malloc(size); memcpy(elem_copy, bufn, size); elem = bufn; for (i = 0; i < size; i++) elem[i] += 1; /* Increment each byte by one, safe? */ putn_handles[pos].elem_copy = elem_copy; ngai_put_common(g_a, lo, hi, buf, ld, 0, -1, &putn_handles[pos].firsthdl); ngai_put_common(g_a, ecoords, ecoords, elem_copy, ldn, 0, -1, &putn_handles[pos].elementhdl); } } /* pnga_nbput_notify */ /** * Wait for a non-blocking put/notify to complete */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_nbwait_notify = pnga_nbwait_notify #endif void pnga_nbwait_notify(Integer *nbhandle) { int i; for (i = 0; i < HANDLES_OUTSTANDING; i++) if (putn_handles[i].orighdl == nbhandle) break; if (i >= HANDLES_OUTSTANDING) return; /* Incorrect handle used or maybe wait was called multiple times? */ nga_wait_internal(&putn_handles[i].firsthdl); nga_wait_internal(&putn_handles[i].elementhdl); if (putn_handles[i].elem_copy) { free(putn_handles[i].elem_copy); putn_handles[i].elem_copy = NULL; } putn_handles[i].orighdl = NULL; } /* pnga_nbwait_notify */ /** * Put an N-dimensional patch of data into a Global Array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_put = pnga_put #endif void pnga_put(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld) { GA_Internal_Threadsafe_Lock(); ngai_put_common(g_a,lo,hi,buf,ld,0,-1,NULL); GA_Internal_Threadsafe_Unlock(); } /** * (Non-blocking) Put a field in a an N-dimensional patch of data into a Global Array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_nbput_field = pnga_nbput_field #endif void pnga_nbput_field(Integer g_a, Integer *lo, Integer *hi, Integer foff, Integer fsize, void *buf, Integer *ld, Integer *nbhandle) { ngai_put_common(g_a,lo,hi,buf,ld,foff, fsize, nbhandle); } /** * Put a field in a an N-dimensional patch of data into a Global Array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_put_field = pnga_put_field #endif void pnga_put_field(Integer g_a, Integer *lo, Integer *hi, Integer foff, Integer fsize, void *buf, Integer *ld) { ngai_put_common(g_a,lo,hi,buf,ld,foff, fsize, NULL); } /*\ A common routine called by both non-blocking and blocking GA Get calls. \*/ void ngai_get_common(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, Integer field_off, Integer field_size, Integer *nbhandle) { /* g_a: Global array handle lo[]: Array of lower indices of patch of global array hi[]: Array of upper indices of patch of global array buf[]: Local buffer that array patch will be copied into ld[]: Array of physical ndim-1 dimensions of local buffer */ Integer p, np=0, handle=GA_OFFSET + g_a; Integer idx, elems, size, p_handle; int proc, ndim, loop, cond; int num_loops=2; /* 1st loop for remote procs; 2nd loop for local procs */ Integer n_rstrctd; Integer *rank_rstrctd; #if defined(DISABLE_NBOPT) #else Integer ga_nbhandle; int counter=0; #endif _iterator_hdl it_hdl; int _stride_rem[MAXDIM+1], _stride_loc[MAXDIM+1], _count[MAXDIM+1]; int *stride_rem=&_stride_rem[1], *stride_loc=&_stride_loc[1], *count=&_count[1]; ga_check_handleM(g_a, "ngai_get_common"); size = GA[handle].elemsize; ndim = GA[handle].ndim; p_handle = (Integer)GA[handle].p_handle; n_rstrctd = (Integer)GA[handle].num_rstrctd; rank_rstrctd = GA[handle].rank_rstrctd; /*initial stride portion for field-wise operations*/ _stride_rem[0] = size; _stride_loc[0] = field_size; _count[0] = field_size; gai_iterator_init(g_a, lo, hi, &it_hdl); /* get total size of patch */ #ifndef NO_GA_STATS gam_CountElems(ndim, lo, hi, &elems); GAbytes.gettot += (double)size*elems; GAstat.numget++; GAstat.numget_procs += np; #endif if(nbhandle)ga_init_nbhandle(nbhandle); #if !defined(DISABLE_NBOPT) else ga_init_nbhandle(&ga_nbhandle); #endif #ifdef PROFILE_OLD ga_profile_start((int)handle, (long)size*elems, ndim, lo, hi, ENABLE_PROFILE_GET); #endif #if !defined(DISABLE_NBOPT) for(loop=0; loop (count[j]-1)) { src_bvalue[j] = 0; } } for (j=1; j<=ndim-1; j++) { dst_idx += dst_bvalue[j]*dst_stride[j]; if ((i+1)%dst_bunit[j] == 0) { dst_bvalue[j]++; } if (dst_bvalue[j] > (count[j]-1)) { dst_bvalue[j] = 0; } } /* copy memory */ memcpy((char*)dptr+dst_idx,(char*)sptr+src_idx,segsize); } } /** * Get an N-dimensional patch of data from a Global Array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_get = pnga_get #endif void pnga_get(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld) { Integer handle = GA_OFFSET + g_a; enum property_type ga_property = GA[handle].property; if (ga_property != READ_CACHE) /* if array is not read only */ { GA_Internal_Threadsafe_Lock(); ngai_get_common(g_a,lo,hi,buf,ld,0,-1,(Integer *)NULL); GA_Internal_Threadsafe_Unlock(); } else { int i; int nelem; int ndim = GA[handle].ndim; Integer nstart[MAXDIM], ncount[MAXDIM], nld[MAXDIM]; Integer bstart[MAXDIM]; /* ngai_get_common(g_a,lo,hi,buf,ld,0,-1,(Integer *)NULL); */ /* cache is empty condition */ if (GA[handle].cache_head == NULL) { GA[handle].cache_head = malloc(sizeof(cache_struct_t)); nelem = 1; for (i=0; i lo[i] = lo[i]; GA[handle].cache_head -> hi[i] = hi[i]; nelem *= (hi[i]-lo[i]+1); } GA[handle].cache_head -> cache_buf = malloc(GA[handle].elemsize*nelem); GA[handle].cache_head -> next = NULL; void *new_buf = GA[handle].cache_head->cache_buf; /* place data to receive buffer */ ngai_get_common(g_a,lo,hi,buf,ld,0,-1,(Integer*)NULL); for (i=0; icache_buf; for (i=0; ilo[i]; temp_hi[i] = cache_temp_pointer->hi[i]; } //match chk = 1; for (i=0; i= hi[i])) { chk = 0; break; } } //match if (chk) { nelem = 1; for (i=0; inext; } //no match condition if (match == 0) { void *new_buf; /* create new node on cache list*/ cache_struct_t *cache_new = malloc(sizeof(cache_struct_t)); nelem = 1; for (i=0; i lo[i] = lo[i]; cache_new -> hi[i] = hi[i]; nelem *= (hi[i]-lo[i]+1); } cache_new -> cache_buf = malloc(GA[handle].elemsize*nelem); new_buf = cache_new->cache_buf; cache_new -> next = GA[handle].cache_head; GA[handle].cache_head = cache_new; //place data to recieve buffer ngai_get_common(g_a,lo,hi,buf,ld,0,-1,(Integer *)NULL); for (i=0; i MAXCACHE) { cache_struct_t *last = cache_temp_pointer; cache_struct_t *next = last->next; last->next = NULL; cache_temp_pointer = next; while (next) { next = cache_temp_pointer->next; free(cache_temp_pointer->cache_buf); free(cache_temp_pointer); } break; } cache_size++; } /* end */ } } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_nbget = pnga_nbget #endif void pnga_nbget(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, Integer *nbhandle) { GA_Internal_Threadsafe_Lock(); ngai_get_common(g_a,lo,hi,buf,ld,0,-1,nbhandle); GA_Internal_Threadsafe_Unlock(); } /** * Get a field in an N-dimensional patch of data from a Global Array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_get_field = pnga_get_field #endif void pnga_get_field(Integer g_a, Integer *lo, Integer *hi,Integer foff, Integer fsize, void *buf, Integer *ld) { ngai_get_common(g_a,lo,hi,buf,ld,foff,fsize,(Integer *)NULL); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_nbget_field = pnga_nbget_field #endif void pnga_nbget_field(Integer g_a, Integer *lo, Integer *hi,Integer foff, Integer fsize, void *buf, Integer *ld, Integer *nbhandle) { ngai_get_common(g_a,lo,hi,buf,ld,foff,fsize,nbhandle); } /** * A common routine called by both non-blocking and blocking GA acc calls. */ void ngai_acc_common(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, void *alpha, Integer *nbhandle) { Integer p, np=0, handle=GA_OFFSET + g_a; Integer idx, elems, size, type, p_handle, ga_nbhandle; int optype=-1, loop, ndim, cond; int proc; int num_loops=2; /* 1st loop for remote procs; 2nd loop for local procs */ Integer n_rstrctd; Integer *rank_rstrctd; _iterator_hdl it_hdl; GA_Internal_Threadsafe_Lock(); ga_check_handleM(g_a, "ngai_acc_common"); size = GA[handle].elemsize; type = GA[handle].type; ndim = GA[handle].ndim; p_handle = GA[handle].p_handle; n_rstrctd = (Integer)GA[handle].num_rstrctd; rank_rstrctd = GA[handle].rank_rstrctd; if(type==C_DBL) optype= ARMCI_ACC_DBL; else if(type==C_FLOAT) optype= ARMCI_ACC_FLT; else if(type==C_DCPL)optype= ARMCI_ACC_DCP; else if(type==C_SCPL)optype= ARMCI_ACC_CPL; else if(type==C_INT)optype= ARMCI_ACC_INT; else if(type==C_LONG)optype= ARMCI_ACC_LNG; else pnga_error("type not supported",type); gai_iterator_init(g_a, lo, hi, &it_hdl); #ifndef NO_GA_STATS gam_CountElems(ndim, lo, hi, &elems); GAbytes.acctot += (double)size*elems; GAstat.numacc++; GAstat.numacc_procs += np; #endif if(nbhandle)ga_init_nbhandle(nbhandle); else ga_init_nbhandle(&ga_nbhandle); #ifdef PROFILE_OLD ga_profile_start((int)handle, (long)size*elems, ndim, lo, hi, ENABLE_PROFILE_ACC); #endif for(loop=0; loophi[i]) { ga_RegionError(GA[handle].ndim, lo, hi, g_a); } if (p_handle >= 0) { ow = PGRP_LIST[p_handle].map_proc_list[ow]; } if (GA[handle].num_rstrctd > 0) { ow = GA[handle].rank_rstrctd[ow]; } gam_Location(ow,handle, lo, &lptr, ld); *(char**)ptr = lptr; } /*\ RETURN A POINTER TO BEGINNING OF LOCAL DATA BLOCK \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_access_block_grid_ptr = pnga_access_block_grid_ptr #endif void pnga_access_block_grid_ptr(Integer g_a, Integer *index, void* ptr, Integer *ld) /* g_a: array handle [input] * index: subscript of a particular block [input] * ptr: pointer to data in block [output] * ld: array of strides for block data [output] */ { char *lptr; Integer handle = GA_OFFSET + g_a; Integer i/*, p_handle*/, offset, factor, inode; Integer ndim; C_Integer *num_blocks, *block_dims; int *proc_grid; Integer *dims; Integer last; Integer proc_index[MAXDIM]; Integer blk_num[MAXDIM], blk_dim[MAXDIM], blk_inc[MAXDIM]; Integer blk_ld[MAXDIM],hlf_blk[MAXDIM],blk_jinc; Integer j, lo, hi; Integer lld[MAXDIM], block_idx[MAXDIM], block_count[MAXDIM]; Integer ldims[MAXDIM], ldidx[MAXDIM]; Integer *mapc; /*p_handle = GA[handle].p_handle;*/ if (GA[handle].distr_type != SCALAPACK && GA[handle].distr_type != TILED && GA[handle].distr_type != TILED_IRREG) { pnga_error("Array is not using ScaLAPACK or tiled data distribution",0); } /* dimensions of processor grid */ proc_grid = GA[handle].nblock; /* number of blocks along each dimension */ num_blocks = GA[handle].num_blocks; /* size of individual blocks */ block_dims = GA[handle].block_dims; dims = GA[handle].dims; ndim = GA[handle].ndim; for (i=0; i= num_blocks[i]) pnga_error("block index outside allowed values",index[i]); } /* Find strides of requested block */ if (GA[handle].distr_type == TILED) { /* find out what processor block is located on */ gam_find_tile_proc_from_indices(handle, inode, index) /* get proc indices of processor that owns block */ gam_find_tile_proc_indices(handle,inode,proc_index) last = ndim-1; for (i=0; i dims[i]) hi = dims[i]; ldims[i] = (hi - lo + 1); if (i 0) { if (proc_index[i] dims[i]) hi = dims[i]; lld[i] += (hi - lo + 1); if (j i && block_idx[j] == block_count[j]-1) { factor *= ldims[j]; } else { factor *= block_dims[j]; } } offset += block_idx[i]*factor; } } else if (GA[handle].distr_type == TILED_IRREG) { /* Evaluate offset for requested block. This algorithm is similar to the * algorithm for reqularly tiled data layouts. * The contribution from the fastest dimension is * ldidx[0]*ldims[2]*...*ldims[ndim-1]; * The contribution from the second fastest dimension is * lld[0]*ldidx[1]*ldims[2]*...*ldims[ndim-1]; * The contribution from the third fastest dimension is * lld[0]*lld[1]*ldidx[2]*ldims[3]*...*ldims[ndim-1]; * etc. */ offset = 0; for (i=0; i= nblocks) pnga_error("block index outside allowed values",index); if (GA[handle].distr_type == BLOCK_CYCLIC) { offset = 0; inode = index%GAnproc; for (i=inode; i= pnga_pgroup_nnodes(grp)) pnga_error("processor index outside allowed values",index); if (index != pnga_pgroup_nodeid(grp)) pnga_error("Only get accurate number of elements for processor making request",0); lptr = GA[handle].ptr[index]; *len = GA[handle].size/GA[handle].elemsize; *(char**)ptr = lptr; } /** * Provide access to a patch of a Global Array using an index */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_access_idx = pnga_access_idx #endif void pnga_access_idx(Integer g_a, Integer lo[], Integer hi[], AccessIndex* index, Integer ld[]) { char *ptr; Integer handle = GA_OFFSET + g_a; Integer ow,i,p_handle; unsigned long elemsize; unsigned long lref=0, lptr; p_handle = GA[handle].p_handle; if(!pnga_locate(g_a,lo,&ow))pnga_error("locate top failed",0); if (p_handle != -1) ow = PGRP_LIST[p_handle].inv_map_proc_list[ow]; if ((armci_domain_id(ARMCI_DOMAIN_SMP, ow) != armci_domain_my_id(ARMCI_DOMAIN_SMP)) && (ow != GAme)) pnga_error("cannot access top of the patch",ow); if(!pnga_locate(g_a,hi, &ow))pnga_error("locate bottom failed",0); if (p_handle != -1) ow = PGRP_LIST[p_handle].inv_map_proc_list[ow]; if ((armci_domain_id(ARMCI_DOMAIN_SMP, ow) != armci_domain_my_id(ARMCI_DOMAIN_SMP)) && (ow != GAme)) pnga_error("cannot access bottom of the patch",ow); for (i=0; ihi[i]) { ga_RegionError(GA[handle].ndim, lo, hi, g_a); } if (p_handle != -1) ow = PGRP_LIST[p_handle].map_proc_list[ow]; gam_Location(ow,handle, lo, &ptr, ld); /* * return patch address as the distance elements from the reference address * * .in Fortran we need only the index to the type array: dbl_mb or int_mb * that are elements of COMMON in the the mafdecls.h include file * .in C we need both the index and the pointer */ elemsize = (unsigned long)GA[handle].elemsize; /* compute index and check if it is correct */ switch (pnga_type_c2f(GA[handle].type)){ case MT_F_DBL: *index = (AccessIndex) ((DoublePrecision*)ptr - DBL_MB); lref = (unsigned long)DBL_MB; break; case MT_F_DCPL: *index = (AccessIndex) ((DoubleComplex*)ptr - DCPL_MB); lref = (unsigned long)DCPL_MB; break; case MT_F_SCPL: *index = (AccessIndex) ((SingleComplex*)ptr - SCPL_MB); lref = (unsigned long)SCPL_MB; break; case MT_F_INT: *index = (AccessIndex) ((Integer*)ptr - INT_MB); lref = (unsigned long)INT_MB; break; case MT_F_REAL: *index = (AccessIndex) ((float*)ptr - FLT_MB); lref = (unsigned long)FLT_MB; break; } /* check the allignment */ lptr = (unsigned long)ptr; if( lptr%elemsize != lref%elemsize ){ printf("%d: lptr=%lu(%lu) lref=%lu(%lu)\n",(int)GAme,lptr,lptr%elemsize, lref,lref%elemsize); pnga_error("nga_access: MA addressing problem: base address misallignment", handle); } /* adjust index for Fortran addressing */ (*index) ++ ; FLUSH_CACHE; } /*\ PROVIDE ACCESS TO AN INDIVIDUAL DATA BLOCK OF A GLOBAL ARRAY \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_access_block_idx = pnga_access_block_idx #endif void pnga_access_block_idx(Integer g_a, Integer idx, AccessIndex* index, Integer *ld) { char *ptr; Integer handle = GA_OFFSET + g_a; Integer /*p_handle,*/ iblock; unsigned long elemsize; unsigned long lref=0, lptr; /*p_handle = GA[handle].p_handle;*/ iblock = idx; if (iblock < 0 || iblock >= GA[handle].block_total) pnga_error("block index outside allowed values",iblock); pnga_access_block_ptr(g_a,iblock,&ptr,ld); /* * return patch address as the distance elements from the reference address * * .in Fortran we need only the index to the type array: dbl_mb or int_mb * that are elements of COMMON in the the mafdecls.h include file * .in C we need both the index and the pointer */ elemsize = (unsigned long)GA[handle].elemsize; /* compute index and check if it is correct */ switch (pnga_type_c2f(GA[handle].type)){ case MT_F_DBL: *index = (AccessIndex) ((DoublePrecision*)ptr - DBL_MB); lref = (unsigned long)DBL_MB; break; case MT_F_DCPL: *index = (AccessIndex) ((DoubleComplex*)ptr - DCPL_MB); lref = (unsigned long)DCPL_MB; break; case MT_F_SCPL: *index = (AccessIndex) ((SingleComplex*)ptr - SCPL_MB); lref = (unsigned long)SCPL_MB; break; case MT_F_INT: *index = (AccessIndex) ((Integer*)ptr - INT_MB); lref = (unsigned long)INT_MB; break; case MT_F_REAL: *index = (AccessIndex) ((float*)ptr - FLT_MB); lref = (unsigned long)FLT_MB; break; } /* check the allignment */ lptr = (unsigned long)ptr; if( lptr%elemsize != lref%elemsize ){ printf("%d: lptr=%lu(%lu) lref=%lu(%lu)\n",(int)GAme,lptr,lptr%elemsize, lref,lref%elemsize); pnga_error("nga_access: MA addressing problem: base address misallignment", handle); } /* adjust index for Fortran addressing */ (*index) ++ ; FLUSH_CACHE; } /** * Provide access to an individual data block of a Global Array * with a SCALAPACK type block-cyclic data distribution */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_access_block_grid_idx = pnga_access_block_grid_idx #endif void pnga_access_block_grid_idx(Integer g_a, Integer* subscript, AccessIndex *index, Integer *ld) { char *ptr; Integer handle = GA_OFFSET + g_a; Integer i,ndim/*,p_handle*/; unsigned long elemsize; unsigned long lref=0, lptr; /*p_handle = GA[handle].p_handle;*/ ndim = GA[handle].ndim; for (i=0; i= GA[handle].num_blocks[i]) pnga_error("index outside allowed values",subscript[i]); pnga_access_block_grid_ptr(g_a,subscript,&ptr,ld); /* * return patch address as the distance elements from the reference address * * .in Fortran we need only the index to the type array: dbl_mb or int_mb * that are elements of COMMON in the the mafdecls.h include file * .in C we need both the index and the pointer */ elemsize = (unsigned long)GA[handle].elemsize; /* compute index and check if it is correct */ switch (pnga_type_c2f(GA[handle].type)){ case MT_F_DBL: *index = (AccessIndex) ((DoublePrecision*)ptr - DBL_MB); lref = (unsigned long)DBL_MB; break; case MT_F_DCPL: *index = (AccessIndex) ((DoubleComplex*)ptr - DCPL_MB); lref = (unsigned long)DCPL_MB; break; case MT_F_SCPL: *index = (AccessIndex) ((SingleComplex*)ptr - SCPL_MB); lref = (unsigned long)SCPL_MB; break; case MT_F_INT: *index = (AccessIndex) ((Integer*)ptr - INT_MB); lref = (unsigned long)INT_MB; break; case MT_F_REAL: *index = (AccessIndex) ((float*)ptr - FLT_MB); lref = (unsigned long)FLT_MB; break; } /* check the allignment */ lptr = (unsigned long)ptr; if( lptr%elemsize != lref%elemsize ){ printf("%d: lptr=%lu(%lu) lref=%lu(%lu)\n",(int)GAme,lptr,lptr%elemsize, lref,lref%elemsize); pnga_error("nga_access: MA addressing problem: base address misallignment", handle); } /* adjust index for Fortran addressing */ (*index) ++ ; FLUSH_CACHE; } /*\ PROVIDE ACCESS TO A PATCH OF A GLOBAL ARRAY \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_access_block_segment_idx = pnga_access_block_segment_idx #endif void pnga_access_block_segment_idx(Integer g_a, Integer proc, AccessIndex* index, Integer *len) { char *ptr; Integer handle = GA_OFFSET + g_a; /*Integer p_handle;*/ unsigned long elemsize; unsigned long lref=0, lptr; /*p_handle = GA[handle].p_handle;*/ /* * return patch address as the distance elements from the reference address * * .in Fortran we need only the index to the type array: dbl_mb or int_mb * that are elements of COMMON in the the mafdecls.h include file * .in C we need both the index and the pointer */ pnga_access_block_segment_ptr(g_a, proc, &ptr, len); elemsize = (unsigned long)GA[handle].elemsize; /* compute index and check if it is correct */ switch (pnga_type_c2f(GA[handle].type)){ case MT_F_DBL: *index = (AccessIndex) ((DoublePrecision*)ptr - DBL_MB); lref = (unsigned long)DBL_MB; break; case MT_F_DCPL: *index = (AccessIndex) ((DoubleComplex*)ptr - DCPL_MB); lref = (unsigned long)DCPL_MB; break; case MT_F_SCPL: *index = (AccessIndex) ((SingleComplex*)ptr - SCPL_MB); lref = (unsigned long)SCPL_MB; break; case MT_F_INT: *index = (AccessIndex) ((Integer*)ptr - INT_MB); lref = (unsigned long)INT_MB; break; case MT_F_REAL: *index = (AccessIndex) ((float*)ptr - FLT_MB); lref = (unsigned long)FLT_MB; break; } /* check the allignment */ lptr = (unsigned long)ptr; if( lptr%elemsize != lref%elemsize ){ printf("%d: lptr=%lu(%lu) lref=%lu(%lu)\n",(int)GAme,lptr,lptr%elemsize, lref,lref%elemsize); pnga_error("nga_access_block_segment: MA addressing problem: base address misallignment", handle); } /* adjust index for Fortran addressing */ (*index) ++ ; FLUSH_CACHE; } /** * Release access to a patch of a Global Array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_release = pnga_release #endif void pnga_release(Integer g_a, Integer *lo, Integer *hi) {} /** * Release access and update a patch of a Global Array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_release_update = pnga_release_update #endif void pnga_release_update(Integer g_a, Integer *lo, Integer *hi) {} /** * Release access to a block in a block-cyclic Global Array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_release_block = pnga_release_block #endif void pnga_release_block(Integer g_a, Integer iblock) { /* Integer i; Integer handle = GA_OFFSET + g_a; for (i=0; i 0) iproc = GA[handle].rank_rstrctd[iproc]; gaShmemLocation(iproc, g_a, ilo, jlo, &ptr_ref, &ldp); } else { /* Integer lo[2]; lo[0] = ilo; lo[1] = jlo; */ pnga_access_block_ptr(g_a, iproc, &ptr_ref, &ldp); pnga_release_block(g_a, iproc); if (GA[handle].distr_type == BLOCK_CYCLIC) { proc = proc%pnga_nnodes(); } else if (GA[handle].distr_type == SCALAPACK) { Integer index[2]; gam_find_block_indices(handle, proc, index); index[0] = index[0]%GA[handle].nblock[0]; index[1] = index[1]%GA[handle].nblock[1]; gam_find_proc_from_sl_indices(handle,proc,index); } else if (GA[handle].distr_type == TILED || GA[handle].distr_type == TILED_IRREG) { Integer index[2]; gam_find_block_indices(handle, proc, index); index[0] = index[0]%GA[handle].nblock[0]; index[1] = index[1]%GA[handle].nblock[1]; gam_find_tile_proc_from_indices(handle,proc,index); } } type = GA[handle].type; item_size = GAsizeofM(type); ptr_src = malloc((int)nv*2*sizeof(void*)); if(ptr_src==NULL)pnga_error("malloc failed",nv); ptr_dst=ptr_src+ nv; for(k=0; k< nv; k++){ if(i[k] < ilo || i[k] > ihi || j[k] < jlo || j[k] > jhi){ char err_string[ERR_STR_LEN]; sprintf(err_string,"proc=%d invalid i/j=(%ld,%ld)>< [%ld:%ld,%ld:%ld]", (int)proc, (long)i[k], (long)j[k], (long)ilo, (long)ihi, (long)jlo, (long)jhi); pnga_error(err_string,g_a); } offset = (j[k] - jlo)* ldp + i[k] - ilo; ptr_dst[k] = ptr_ref + item_size * offset; ptr_src[k] = ((char*)v) + k*item_size; } desc.bytes = (int)item_size; desc.src_ptr_array = ptr_src; desc.dst_ptr_array = ptr_dst; desc.ptr_array_len = (int)nv; if(GA_fence_set)fence_array[proc]=1; if (p_handle >= 0) { proc = PGRP_LIST[p_handle].inv_map_proc_list[proc]; } if(alpha != NULL) { int optype=-1; if(type==C_DBL) optype= ARMCI_ACC_DBL; else if(type==C_DCPL)optype= ARMCI_ACC_DCP; else if(type==C_SCPL)optype= ARMCI_ACC_CPL; else if(type==C_INT)optype= ARMCI_ACC_INT; else if(type==C_LONG)optype= ARMCI_ACC_LNG; else if(type==C_FLOAT)optype= ARMCI_ACC_FLT; else pnga_error("type not supported",type); rc= ARMCI_AccV(optype, alpha, &desc, 1, (int)proc); } if(rc) pnga_error("scatter/_acc failed in armci",rc); free(ptr_src); } /** * Scatter nv elements of v into a Global Array at locations specified * by arrays i and j */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_scatter2d = pnga_scatter2d #endif void pnga_scatter2d(Integer g_a, void *v, Integer *i, Integer *j, Integer nv) { register Integer k; Integer kk; Integer item_size; Integer proc, type=GA[GA_OFFSET + g_a].type; Integer nproc, p_handle, iproc; Integer subscrpt[2]; Integer *aproc, naproc; /* active processes and numbers */ Integer *map; /* map the active processes to allocated space */ char *buf1, *buf2; Integer handle = g_a + GA_OFFSET; Integer *count; /* counters for each process */ Integer *nelem; /* number of elements for each process */ /* source and destination pointers for each process */ void ***ptr_src, ***ptr_dst; void **ptr_org; /* the entire pointer array */ armci_giov_t desc; Integer *ilo, *ihi, *jlo, *jhi, *ldp, *owner; Integer lo[2], hi[2]; char **ptr_ref; Integer num_blocks=0; if (nv < 1) return; ga_check_handleM(g_a, "ga_scatter"); GAstat.numsca++; /* determine how many processors are associated with array */ p_handle = GA[handle].p_handle; if (p_handle < 0) { nproc = GAnproc; if (GA[handle].num_rstrctd > 0) { nproc = GA[handle].num_rstrctd; } } else { nproc = PGRP_LIST[p_handle].map_nproc; } /* allocate temp memory */ if (GA[handle].distr_type == REGULAR) { buf1 = malloc((int) (nproc *4 +nv)* (sizeof(Integer))); if(buf1 == NULL) pnga_error("malloc failed", 3*nproc); } else { num_blocks = GA[handle].block_total; buf1 = malloc((int) (num_blocks *4 +nv)* (sizeof(Integer))); if(buf1 == NULL) pnga_error("malloc failed", 3*num_blocks); } owner = (Integer *)buf1; count = owner+ nv; if (GA[handle].distr_type == REGULAR) { nelem = count + nproc; aproc = count + 2 * nproc; map = count + 3 * nproc; } else { nelem = count + num_blocks; aproc = count + 2 * num_blocks; map = count + 3 * num_blocks; } /* initialize the counters and nelem */ if (GA[handle].distr_type == REGULAR) { for(kk=0; kk 0) { aproc[naproc] = k; map[k] = naproc; naproc ++; } } else { for(k=0; k 0) { aproc[naproc] = k; map[k] = naproc; naproc ++; } } GAstat.numsca_procs += naproc; buf2 = malloc((int)(2*naproc*sizeof(void **) + 2*nv*sizeof(void *) + 5*naproc*sizeof(Integer) + naproc*sizeof(char*))); if(buf2 == NULL) pnga_error("malloc failed", naproc); ptr_src = (void ***)buf2; ptr_dst = (void ***)(buf2 + naproc*sizeof(void **)); ptr_org = (void **)(buf2 + 2*naproc*sizeof(void **)); ptr_ref = (char **)(buf2+2*naproc*sizeof(void **)+2*nv*sizeof(void *)); ilo = (Integer *)(((char*)ptr_ref) + naproc*sizeof(char*)); ihi = ilo + naproc; jlo = ihi + naproc; jhi = jlo + naproc; ldp = jhi + naproc; if (GA[handle].distr_type == REGULAR) { for(kk=0; kk 0) iproc = GA[handle].rstrctd_list[iproc]; pnga_distribution(g_a, iproc, lo, hi); ilo[kk] = lo[0]; jlo[kk] = lo[1]; ihi[kk] = hi[0]; jhi[kk] = hi[1]; /* get address of the first element owned by proc */ gaShmemLocation(aproc[kk], g_a, ilo[kk], jlo[kk], &(ptr_ref[kk]), &(ldp[kk])); } } else { for(kk=0; kk 0) proc = GA[handle].rank_rstrctd[owner[k]]; this_count = count[proc]; count[proc]++; proc = map[proc]; ptr_src[proc][this_count] = ((char*)v) + k * item_size; if(i[k] < ilo[proc] || i[k] > ihi[proc] || j[k] < jlo[proc] || j[k] > jhi[proc]){ char err_string[ERR_STR_LEN]; sprintf(err_string,"proc=%d invalid i/j=(%ld,%ld)><[%ld:%ld,%ld:%ld]", (int)proc, (long)i[k], (long)j[k], (long)ilo[proc], (long)ihi[proc], (long)jlo[proc], (long)jhi[proc]); pnga_error(err_string, g_a); } ptr_dst[proc][this_count] = ptr_ref[proc] + item_size * ((j[k] - jlo[proc])* ldp[proc] + i[k] - ilo[proc]); } /* source and destination pointers are ready for all processes */ if (GA[handle].distr_type == REGULAR) { for(k=0; k 0) iproc = GA[handle].rstrctd_list[iproc]; } else { iproc = PGRP_LIST[p_handle].inv_map_proc_list[aproc[k]]; } rc = ARMCI_PutV(&desc, 1, (int)iproc); if(rc) pnga_error("scatter failed in armci",rc); } } else if (GA[handle].distr_type == BLOCK_CYCLIC) { for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } rc = ARMCI_PutV(&desc, 1, (int)iproc); if(rc) pnga_error("scatter failed in armci",rc); } } else if (GA[handle].distr_type == SCALAPACK) { Integer index[MAXDIM]; for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } rc = ARMCI_PutV(&desc, 1, (int)iproc); if(rc) pnga_error("scatter failed in armci",rc); } } else if (GA[handle].distr_type == TILED || GA[handle].distr_type == TILED_IRREG) { Integer index[MAXDIM]; for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } rc = ARMCI_PutV(&desc, 1, (int)iproc); if(rc) pnga_error("scatter failed in armci",rc); } } free(buf2); free(buf1); } /** * Add elements of v to contents at random locations in Global Array * specified by vectors i and j */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_scatter_acc2d = pnga_scatter_acc2d #endif void pnga_scatter_acc2d(g_a, v, i, j, nv, alpha) Integer g_a, nv, *i, *j; void *v, *alpha; { register Integer k; Integer item_size; Integer first, nelem, proc, type=GA[GA_OFFSET + g_a].type; Integer *int_ptr; Integer subscrpt[2]; if (nv < 1) return; ga_check_handleM(g_a, "ga_scatter_acc"); GAstat.numsca++; int_ptr = (Integer*) pnga_malloc(nv, MT_F_INT, "ga_scatter_acc--p"); /* find proc that owns the (i,j) element; store it in temp: int_ptr */ for(k=0; k< nv; k++) { subscrpt[0] = *(i+k); subscrpt[1] = *(j+k); if(! pnga_locate(g_a, subscrpt, int_ptr+k)){ char err_string[ERR_STR_LEN]; sprintf(err_string,"invalid i/j=(%ld,%ld)", (long)i[k], (long)j[k]); pnga_error(err_string,g_a); } } /* determine limit for message size -- v,i, & j will travel together */ item_size = GAsizeofM(type); GAbytes.scatot += (double)item_size*nv ; /* Sort the entries by processor */ ga_sort_scat(&nv, v, i, j, int_ptr, type ); /* go through the list again executing scatter for each processor */ first = 0; do { proc = int_ptr[first]; nelem = 0; /* count entries for proc from "first" to last */ for(k=first; k< nv; k++){ if(proc == int_ptr[k]) nelem++; else break; } if(proc == GAme){ GAbytes.scaloc += (double)item_size* nelem ; } gai_scatter_acc_local(g_a, ((char*)v)+item_size*first, i+first, j+first, nelem, alpha, proc); first += nelem; }while (first< nv); pnga_free(int_ptr); } #define SCATTER -99 #define GATHER -98 #define SCATTER_ACC -97 /*\ GATHER OPERATION elements from the global array into v \*/ void gai_gatscat(int op, Integer g_a, void* v, Integer subscript[], Integer nv, double *locbytes, double* totbytes, void *alpha) { Integer k, handle=g_a+GA_OFFSET; int ndim, item_size, type; Integer *proc; Integer nproc, p_handle, iproc; Integer *aproc, naproc; /* active processes and numbers */ Integer *map; /* map the active processes to allocated space */ char *buf1, *buf2; Integer *count; /* counters for each process */ Integer *nelem; /* number of elements for each process */ /* source and destination pointers for each process */ void ***ptr_src, ***ptr_dst; void **ptr_org; /* the entire pointer array */ armci_giov_t desc; Integer num_blocks=0; proc=(Integer *)pnga_malloc(nv, MT_F_INT, "ga_gat-p"); ndim = GA[handle].ndim; type = GA[handle].type; item_size = GA[handle].elemsize; *totbytes += (double)item_size*nv; /* determine how many processors are associated with array */ p_handle = GA[handle].p_handle; if (p_handle < 0) { nproc = GAnproc; if (GA[handle].num_rstrctd > 0) nproc = GA[handle].num_rstrctd; } else { nproc = PGRP_LIST[p_handle].map_nproc; } /* allocate temp memory */ if (GA[handle].distr_type == REGULAR) { buf1 = malloc((int) nproc * 4 * (sizeof(Integer))); if(buf1 == NULL) pnga_error("malloc failed", 3*nproc); } else { num_blocks = GA[handle].block_total; buf1 = malloc((int) num_blocks * 4 * (sizeof(Integer))); if(buf1 == NULL) pnga_error("malloc failed", 3*num_blocks); } count = (Integer *)buf1; if (GA[handle].distr_type == REGULAR) { nelem = (Integer *)(buf1 + nproc * sizeof(Integer)); aproc = (Integer *)(buf1 + 2 * nproc * sizeof(Integer)); map = (Integer *)(buf1 + 3 * nproc * sizeof(Integer)); } else { num_blocks = GA[handle].block_total; nelem = (Integer *)(buf1 + num_blocks * sizeof(Integer)); aproc = (Integer *)(buf1 + 2 * num_blocks * sizeof(Integer)); map = (Integer *)(buf1 + 3 * num_blocks * sizeof(Integer)); } /* initialize the counters and nelem */ if (GA[handle].distr_type == REGULAR) { for(k=0; k 0) { aproc[naproc] = k; map[k] = naproc; naproc ++; } } else { for(k=0; k 0) { aproc[naproc] = k; map[k] = naproc; naproc ++; } } buf2 = malloc((int)(2*naproc*sizeof(void **) + 2*nv*sizeof(void *))); if(buf2 == NULL) pnga_error("malloc failed", 2*naproc); ptr_src = (void ***)buf2; ptr_dst = (void ***)(buf2 + naproc * sizeof(void **)); ptr_org = (void **)(buf2 + 2 * naproc * sizeof(void **)); /* set the pointers as * P0 P1 P0 P1 * ptr_src[0] ptr_src[1] ... ptr_dst[0] ptr_dst[1] ... * \ \ \ \ * ptr_org |-------------------------------|---------------------------| * | nv | nv | * | nelem[0] | nelem[1] |... | nelem[0] | nelem[1] |... */ ptr_src[0] = ptr_org; ptr_dst[0] = ptr_org + nv; for(k=1; k 0) iproc = GA[handle].rank_rstrctd[iproc]; ptr_dst[map[iproc]][count[iproc]] = ((char*)v) + k * item_size; if (p_handle != 0) { gam_Loc_ptr(iproc, handle, (subscript+k*ndim), ptr_src[map[iproc]]+count[iproc]); } else { gam_Loc_ptr(proc[k], handle, (subscript+k*ndim), ptr_src[map[iproc]]+count[iproc]); } count[iproc]++; } } else { Integer lo[MAXDIM], hi[MAXDIM], ld[MAXDIM]; Integer j, jtot, last, offset; for(k=0; k 0) iproc = GA[handle].rstrctd_list[iproc]; } else { iproc = PGRP_LIST[p_handle].inv_map_proc_list[aproc[k]]; } rc=ARMCI_GetV(&desc, 1, (int)iproc); if(rc) pnga_error("gather failed in armci",rc); } } else if (GA[handle].distr_type == BLOCK_CYCLIC) { for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } rc=ARMCI_GetV(&desc, 1, (int)iproc); if(rc) pnga_error("gather failed in armci",rc); } } else if (GA[handle].distr_type == SCALAPACK) { Integer j, index[MAXDIM]; for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } rc=ARMCI_GetV(&desc, 1, (int)iproc); if(rc) pnga_error("gather failed in armci",rc); } } else if (GA[handle].distr_type == TILED || GA[handle].distr_type == TILED_IRREG) { Integer j, index[MAXDIM]; for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } rc=ARMCI_GetV(&desc, 1, (int)iproc); if(rc) pnga_error("gather failed in armci",rc); } } GAstat.numgat_procs += naproc; break; case SCATTER: /* go through all the elements * for process 0: ptr_src[0][0, 1, ...] = v + offset0... * ptr_dst[0][0, 1, ...] = subscript + offset0... * for process 1: ptr_src[1][...] ... * ptr_dst[1][...] ... */ if (GA[handle].distr_type == REGULAR) { for(k=0; k 0) iproc = GA[handle].rank_rstrctd[iproc]; ptr_src[map[iproc]][count[iproc]] = ((char*)v) + k * item_size; if (p_handle != 0) { gam_Loc_ptr(iproc, handle, (subscript+k*ndim), ptr_dst[map[iproc]]+count[iproc]); } else { gam_Loc_ptr(proc[k], handle, (subscript+k*ndim), ptr_dst[map[iproc]]+count[iproc]); } count[iproc]++; } } else { Integer lo[MAXDIM], hi[MAXDIM], ld[MAXDIM]; Integer j, jtot, last, offset; for(k=0; k 0) iproc = GA[handle].rstrctd_list[iproc]; } else { iproc = PGRP_LIST[p_handle].inv_map_proc_list[aproc[k]]; } if(GA_fence_set) fence_array[iproc]=1; rc=ARMCI_PutV(&desc, 1, (int)iproc); if(rc) pnga_error("scatter failed in armci",rc); } } else if (GA[handle].distr_type == BLOCK_CYCLIC) { for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } if(GA_fence_set) fence_array[iproc]=1; rc=ARMCI_PutV(&desc, 1, (int)iproc); if(rc) pnga_error("scatter failed in armci",rc); } } else if (GA[handle].distr_type == SCALAPACK) { Integer j, index[MAXDIM]; for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } if(GA_fence_set) fence_array[iproc]=1; rc=ARMCI_PutV(&desc, 1, (int)iproc); if(rc) pnga_error("scatter failed in armci",rc); } } else if (GA[handle].distr_type == TILED || GA[handle].distr_type == TILED_IRREG) { Integer j, index[MAXDIM]; for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } if(GA_fence_set) fence_array[iproc]=1; rc=ARMCI_PutV(&desc, 1, (int)iproc); if(rc) pnga_error("scatter failed in armci",rc); } } GAstat.numsca_procs += naproc; break; case SCATTER_ACC: /* go through all the elements * for process 0: ptr_src[0][0, 1, ...] = v + offset0... * ptr_dst[0][0, 1, ...] = subscript + offset0... * for process 1: ptr_src[1][...] ... * ptr_dst[1][...] ... */ if (GA[handle].distr_type == REGULAR) { for(k=0; k 0) iproc = GA[handle].rank_rstrctd[iproc]; ptr_src[map[iproc]][count[iproc]] = ((char*)v) + k * item_size; if (p_handle != 0) { gam_Loc_ptr(iproc, handle, (subscript+k*ndim), ptr_dst[map[iproc]]+count[iproc]); } else { gam_Loc_ptr(proc[k], handle, (subscript+k*ndim), ptr_dst[map[iproc]]+count[iproc]); } count[iproc]++; } } else { Integer lo[MAXDIM], hi[MAXDIM], ld[MAXDIM]; Integer j, jtot, last, offset; for(k=0; k 0) iproc = GA[handle].rstrctd_list[iproc]; } else { iproc = PGRP_LIST[p_handle].inv_map_proc_list[aproc[k]]; } if(GA_fence_set) fence_array[iproc]=1; if(alpha != NULL) { int optype=-1; if(type==C_DBL) optype= ARMCI_ACC_DBL; else if(type==C_DCPL)optype= ARMCI_ACC_DCP; else if(type==C_SCPL)optype= ARMCI_ACC_CPL; else if(type==C_INT)optype= ARMCI_ACC_INT; else if(type==C_LONG)optype= ARMCI_ACC_LNG; else if(type==C_FLOAT)optype= ARMCI_ACC_FLT; else pnga_error("type not supported",type); rc= ARMCI_AccV(optype, alpha, &desc, 1, (int)iproc); } if(rc) pnga_error("scatter_acc failed in armci",rc); } } else if (GA[handle].distr_type == BLOCK_CYCLIC) { for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } if(GA_fence_set) fence_array[iproc]=1; if(alpha != NULL) { int optype=-1; if(type==C_DBL) optype= ARMCI_ACC_DBL; else if(type==C_DCPL)optype= ARMCI_ACC_DCP; else if(type==C_SCPL)optype= ARMCI_ACC_CPL; else if(type==C_INT)optype= ARMCI_ACC_INT; else if(type==C_LONG)optype= ARMCI_ACC_LNG; else if(type==C_FLOAT)optype= ARMCI_ACC_FLT; else pnga_error("type not supported",type); rc= ARMCI_AccV(optype, alpha, &desc, 1, (int)iproc); } if(rc) pnga_error("scatter_acc failed in armci",rc); } } else if (GA[handle].distr_type == SCALAPACK) { Integer j, index[MAXDIM]; for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } if(GA_fence_set) fence_array[iproc]=1; if(alpha != NULL) { int optype=-1; if(type==C_DBL) optype= ARMCI_ACC_DBL; else if(type==C_DCPL)optype= ARMCI_ACC_DCP; else if(type==C_SCPL)optype= ARMCI_ACC_CPL; else if(type==C_INT)optype= ARMCI_ACC_INT; else if(type==C_LONG)optype= ARMCI_ACC_LNG; else if(type==C_FLOAT)optype= ARMCI_ACC_FLT; else pnga_error("type not supported",type); rc= ARMCI_AccV(optype, alpha, &desc, 1, (int)iproc); } if(rc) pnga_error("scatter_acc failed in armci",rc); } } else if (GA[handle].distr_type == TILED || GA[handle].distr_type == TILED_IRREG) { Integer j, index[MAXDIM]; for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } if(GA_fence_set) fence_array[iproc]=1; if(alpha != NULL) { int optype=-1; if(type==C_DBL) optype= ARMCI_ACC_DBL; else if(type==C_DCPL)optype= ARMCI_ACC_DCP; else if(type==C_SCPL)optype= ARMCI_ACC_CPL; else if(type==C_INT)optype= ARMCI_ACC_INT; else if(type==C_LONG)optype= ARMCI_ACC_LNG; else if(type==C_FLOAT)optype= ARMCI_ACC_FLT; else pnga_error("type not supported",type); rc= ARMCI_AccV(optype, alpha, &desc, 1, (int)iproc); } if(rc) pnga_error("scatter_acc failed in armci",rc); } } break; default: pnga_error("operation not supported",op); } free(buf2); free(buf1); pnga_free(proc); } /** * Preallocate data for internal linked-list arrays for gather/scatter calls. * @param nelems: the maximum number of elements that will be moved in * gather/scatter call using these arrays. */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_alloc_gatscat_buf = pnga_alloc_gatscat_buf #endif void pnga_alloc_gatscat_buf(Integer nelems) { Integer nprocs = pnga_nnodes(); if (GA_prealloc_gatscat) pnga_error("Gather/scatter buffers already allocated",nelems); GA_prealloc_gatscat = nelems; GA_header =(Integer *)pnga_malloc(nprocs, MT_F_INT, "ga_gat_header"); GA_list =(Integer *)pnga_malloc(nelems, MT_F_INT, "ga_gat_list"); GA_elems =(Integer *)pnga_malloc(nprocs, MT_F_INT, "ga_gat_nelems"); } /** * Free up preallocate data for internal linked-list arrays for gather/scatter calls. */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_free_gatscat_buf = pnga_free_gatscat_buf #endif void pnga_free_gatscat_buf() { if (!GA_prealloc_gatscat) pnga_error("Gather/scatter buffers not allocated",0); GA_prealloc_gatscat = 0; pnga_free(GA_elems); pnga_free(GA_list); pnga_free(GA_header); } #define gam_c2f_index(index_c, index_f, ndim) \ { \ Integer _i; \ for (_i = 0; _i < (ndim); _i++) { \ index_f[_i] = (Integer)(index_c[(ndim)-1-_i]+1); \ } \ } /*\ GATHER OPERATION elements from the global array into v \*/ void gai_gatscat_new(int op, Integer g_a, void* v, void *subscript, Integer c_flag, Integer nv, double *locbytes, double* totbytes, void *alpha) { Integer handle=g_a+GA_OFFSET; int ndim, i, j, k, type, item_size; Integer idx, p_handle, num_rstrctd; Integer *proc; Integer nprocs, me, iproc, tproc, index[MAXDIM]; Integer lo[MAXDIM], hi[MAXDIM], ld[MAXDIM-1]; Integer jtot, last, offset; Integer *subscript_ptr; Integer *header, *list, *nelems; char *buf; void **ptr_rem, **ptr_loc; int rc, maxlen; int *nblock; armci_giov_t desc; me = pnga_nodeid(); num_rstrctd = GA[handle].num_rstrctd; nblock = GA[handle].nblock; /* determine how many processors are associated with array */ p_handle = GA[handle].p_handle; if (p_handle < 0) { nprocs = GAnproc; if (num_rstrctd > 0) nprocs = num_rstrctd; } else { nprocs = PGRP_LIST[p_handle].map_nproc; } if (!GA_prealloc_gatscat) { header =(Integer *)pnga_malloc(nprocs, MT_F_INT, "ga_gat_header"); list =(Integer *)pnga_malloc(nv, MT_F_INT, "ga_gat_list"); nelems =(Integer *)pnga_malloc(nprocs, MT_F_INT, "ga_gat_nelems"); } else { if (GA_prealloc_gatscat < nv) pnga_error("Gather/scatter vector exceeds allocation length ", GA_prealloc_gatscat); header = (Integer*)GA_header; list = (Integer*)GA_list; nelems = (Integer*)GA_elems; } ndim = GA[handle].ndim; type = GA[handle].type; item_size = GA[handle].elemsize; *totbytes = item_size * nv; /* Initialize linked list data structures */ for (i=0; i 0) idx = GA[handle].rank_rstrctd[idx]; /* map from block to processor, if necessary */ if (GA[handle].distr_type == BLOCK_CYCLIC) { idx = idx%nprocs; } else if (GA[handle].distr_type == SCALAPACK) { gam_find_block_indices(handle,idx,index); for (j=0; j 0) { /* loop through linked list to find all data elements associated with * the remote processor iproc */ idx = header[iproc]; j = 0; while (idx > -1) { if (c_flag) { gam_c2f_index(((int**)subscript)[idx], index, ndim); subscript_ptr = index; } else { subscript_ptr = ((Integer*)subscript)+idx*ndim; } if (GA[handle].distr_type == REGULAR) { /* gam_Loc_ptr modifies the value of the processor variable for * restricted arrays or processor groups, so make a temporary copy */ tproc = iproc; gam_Loc_ptr(tproc, handle, (subscript_ptr), ptr_rem+j); } else { /* TODO: Figure out how to get correct pointers for block cyclic * distributions */ /* find block index from subscript */ if(!pnga_locate(g_a, subscript_ptr, &tproc)) { gai_print_subscript("invalid subscript for block-cyclic array", ndim, subscript_ptr,"\n"); pnga_error("failed -element:",idx); } pnga_distribution(g_a, tproc, lo, hi); pnga_access_block_ptr(g_a, tproc, ptr_rem+j, ld); pnga_release_block(g_a, tproc); offset = 0; last = ndim -1; jtot = 1; for (k=0; k 0) { tproc = GA[handle].rstrctd_list[iproc]; } else { if (p_handle < 0) { tproc = iproc; } else { tproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } } /* perform vector operation */ switch(op) { case GATHER: desc.bytes = (int)item_size; desc.src_ptr_array = ptr_rem; desc.dst_ptr_array = ptr_loc; desc.ptr_array_len = (int)nelems[iproc]; rc=ARMCI_GetV(&desc, 1, (int)tproc); if(rc) pnga_error("gather failed in armci",rc); break; case SCATTER: desc.bytes = (int)item_size; desc.src_ptr_array = ptr_loc; desc.dst_ptr_array = ptr_rem; desc.ptr_array_len = (int)nelems[iproc]; rc=ARMCI_PutV(&desc, 1, (int)tproc); if(rc) pnga_error("scatter failed in armci",rc); break; case SCATTER_ACC: desc.bytes = (int)item_size; desc.src_ptr_array = ptr_loc; desc.dst_ptr_array = ptr_rem; desc.ptr_array_len = (int)nelems[iproc]; if(alpha != NULL) { int optype=-1; if(type==C_DBL) optype= ARMCI_ACC_DBL; else if(type==C_DCPL)optype= ARMCI_ACC_DCP; else if(type==C_SCPL)optype= ARMCI_ACC_CPL; else if(type==C_INT)optype= ARMCI_ACC_INT; else if(type==C_LONG)optype= ARMCI_ACC_LNG; else if(type==C_FLOAT)optype= ARMCI_ACC_FLT; else pnga_error("type not supported",type); rc= ARMCI_AccV(optype, alpha, &desc, 1, (int)tproc); } if(rc) pnga_error("scatter_acc failed in armci",rc); break; default: pnga_error("operation not supported",op); } } } free(buf); if (!GA_prealloc_gatscat) { pnga_free(nelems); pnga_free(list); pnga_free(header); } } /** * Gather random elements from a global array into local buffer v \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_gather = pnga_gather #endif void pnga_gather(Integer g_a, void* v, void *subscript, Integer c_flag, Integer nv) { if (nv < 1) return; ga_check_handleM(g_a, "nga_gather"); GAstat.numgat++; #ifdef USE_GATSCAT_NEW gai_gatscat_new(GATHER,g_a,v,subscript,c_flag,nv,&GAbytes.gattot,&GAbytes.gatloc, NULL); #else gai_gatscat(GATHER,g_a,v,subscript,nv,&GAbytes.gattot,&GAbytes.gatloc, NULL); #endif } /** * Scatter elements from a local buffer v into random locations in a Global * Array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_scatter = pnga_scatter #endif void pnga_scatter(Integer g_a, void* v, void *subscript, Integer c_flag, Integer nv) { if (nv < 1) return; ga_check_handleM(g_a, "nga_scatter"); GAstat.numsca++; #ifdef USE_GATSCAT_NEW gai_gatscat_new(SCATTER,g_a,v,subscript,c_flag,nv,&GAbytes.scatot,&GAbytes.scaloc, NULL); #else gai_gatscat(SCATTER,g_a,v,subscript,nv,&GAbytes.scatot,&GAbytes.scaloc, NULL); #endif } /** * Add elements of v to contents at random locations in Global Array * specified by array subscript */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_scatter_acc = pnga_scatter_acc #endif void pnga_scatter_acc(Integer g_a, void* v, void *subscript, Integer c_flag, Integer nv, void *alpha) { if (nv < 1) return; ga_check_handleM(g_a, "nga_scatter_acc"); GAstat.numsca++; #ifdef USE_GATSCAT_NEW gai_gatscat_new(SCATTER_ACC, g_a, v, subscript, c_flag, nv, &GAbytes.scatot, &GAbytes.scaloc, alpha); #else gai_gatscat(SCATTER_ACC, g_a, v, subscript, nv, &GAbytes.scatot, &GAbytes.scaloc, alpha); #endif } /** * Gather random elements from 2D global array into local buffer v */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_gather2d = pnga_gather2d #endif void pnga_gather2d(Integer g_a, void *v, Integer *i, Integer *j, Integer nv) { Integer k, kk, proc, item_size; Integer *aproc, naproc; /* active processes and numbers */ Integer *map; /* map the active processes to allocated space */ char *buf1, *buf2; Integer handle = g_a + GA_OFFSET; Integer nproc, p_handle, iproc; Integer *count; /* counters for each process */ Integer *nelem; /* number of elements for each process */ /* source and destination pointers for each process */ void ***ptr_src, ***ptr_dst; void **ptr_org; /* the entire pointer array */ armci_giov_t desc; Integer *ilo, *ihi, *jlo, *jhi, *ldp, *owner; Integer lo[2], hi[2]; char **ptr_ref; Integer num_blocks=0; Integer subscrpt[2]; if (nv < 1) return; ga_check_handleM(g_a, "ga_gather"); GAstat.numgat++; /* determine how many processors are associated with array */ p_handle = GA[handle].p_handle; if (p_handle < 0) { nproc = GAnproc; if (GA[handle].num_rstrctd > 0) { nproc = GA[handle].num_rstrctd; } } else { nproc = PGRP_LIST[p_handle].map_nproc; } /* allocate temp memory */ if (GA[handle].distr_type == REGULAR) { buf1 = malloc((int)(nproc *4 + nv)* (sizeof(Integer))); if(buf1 == NULL) pnga_error("malloc failed", 3*nproc); } else { num_blocks = GA[handle].block_total; buf1 = malloc((int)(num_blocks *4 + nv)* (sizeof(Integer))); if(buf1 == NULL) pnga_error("malloc failed", 3*num_blocks); } owner = (Integer *)buf1; count = owner+ nv; if (GA[handle].distr_type == REGULAR) { nelem = count + nproc; aproc = count + 2 * nproc; map = count + 3 * nproc; } else { nelem = count + num_blocks; aproc = count + 2 * num_blocks; map = count + 3 * num_blocks; } if (GA[handle].distr_type == REGULAR) { /* initialize the counters and nelem */ for(kk=0; kk 0) { aproc[naproc] = k; map[k] = naproc; naproc ++; } } else { for(k=0; k 0) { aproc[naproc] = k; map[k] = naproc; naproc ++; } } GAstat.numgat_procs += naproc; buf2 = malloc((int)(2*naproc*sizeof(void **) + 2*nv*sizeof(void *) + 5*naproc*sizeof(Integer) + naproc*sizeof(char*))); if(buf2 == NULL) pnga_error("malloc failed", naproc); ptr_src = (void ***)buf2; ptr_dst = (void ***)(buf2 + naproc*sizeof(void **)); ptr_org = (void **)(buf2 + 2*naproc*sizeof(void **)); ptr_ref = (char **)(buf2+2*naproc*sizeof(void **)+2*nv*sizeof(void *)); ilo = (Integer *)(((char*)ptr_ref) + naproc*sizeof(char*)); ihi = ilo + naproc; jlo = ihi + naproc; jhi = jlo + naproc; ldp = jhi + naproc; if (GA[handle].distr_type == REGULAR) { for(kk=0; kk 0) iproc = GA[handle].rstrctd_list[iproc]; pnga_distribution(g_a, iproc, lo, hi); ilo[kk] = lo[0]; jlo[kk] = lo[1]; ihi[kk] = hi[0]; jhi[kk] = hi[1]; /* get address of the first element owned by proc */ gaShmemLocation(aproc[kk], g_a, ilo[kk], jlo[kk], &(ptr_ref[kk]), &(ldp[kk])); } } else { for(kk=0; kk 0) proc = GA[handle].rank_rstrctd[owner[k]]; this_count = count[proc]; count[proc]++; proc = map[proc]; ptr_dst[proc][this_count] = ((char*)v) + k * item_size; if(i[k] < ilo[proc] || i[k] > ihi[proc] || j[k] < jlo[proc] || j[k] > jhi[proc]){ char err_string[ERR_STR_LEN]; sprintf(err_string,"proc=%d invalid i/j=(%ld,%ld)><[%ld:%ld,%ld:%ld]", (int)proc,(long)i[k],(long)j[k], (long)ilo[proc],(long)ihi[proc], (long)jlo[proc], (long)jhi[proc]); pnga_error(err_string, g_a); } ptr_src[proc][this_count] = ptr_ref[proc] + item_size * ((j[k] - jlo[proc])* ldp[proc] + i[k] - ilo[proc]); } /* source and destination pointers are ready for all processes */ if (GA[handle].distr_type == REGULAR) { for(k=0; k 0) iproc = GA[handle].rstrctd_list[iproc]; } else { iproc = PGRP_LIST[p_handle].inv_map_proc_list[aproc[k]]; } rc=ARMCI_GetV(&desc, 1, (int)iproc); if(rc) pnga_error("gather failed in armci",rc); } } else if (GA[handle].distr_type == BLOCK_CYCLIC) { for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } rc=ARMCI_GetV(&desc, 1, (int)iproc); if(rc) pnga_error("gather failed in armci",rc); } } else if (GA[handle].distr_type == SCALAPACK) { Integer index[MAXDIM]; for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } rc=ARMCI_GetV(&desc, 1, (int)iproc); if(rc) pnga_error("gather failed in armci",rc); } } else if (GA[handle].distr_type == TILED || GA[handle].distr_type == TILED_IRREG) { Integer index[MAXDIM]; for(k=0; k= 0) { iproc = PGRP_LIST[p_handle].inv_map_proc_list[iproc]; } rc=ARMCI_GetV(&desc, 1, (int)iproc); if(rc) pnga_error("gather failed in armci",rc); } } free(buf2); free(buf1); } /** * Read and increment an element of a Global Array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_read_inc = pnga_read_inc #endif Integer pnga_read_inc(Integer g_a, Integer* subscript, Integer inc) { GA_Internal_Threadsafe_Lock(); char *ptr; Integer ldp[MAXDIM], proc, handle=GA_OFFSET+g_a, p_handle, ndim; int optype,ivalue; long lvalue; void *pval; ga_check_handleM(g_a, "nga_read_inc"); /* BJP printf("p[%d] g_a: %d subscript: %d inc: %d\n",GAme, g_a, subscript[0], inc); */ if(GA[handle].type!=C_INT && GA[handle].type!=C_LONG && GA[handle].type!=C_LONGLONG) pnga_error("type must be integer",GA[handle].type); GAstat.numrdi++; GAbytes.rditot += (double)sizeof(Integer); p_handle = GA[handle].p_handle; ndim = GA[handle].ndim; /* find out who owns it */ pnga_locate(g_a, subscript, &proc); /* get an address of the g_a(subscript) element */ if (GA[handle].distr_type == REGULAR) { if (GA[handle].num_rstrctd == 0) { gam_Location(proc, handle, subscript, (char**)&ptr, ldp); } else { gam_Location(GA[handle].rank_rstrctd[proc], handle, subscript, (char**)&ptr, ldp); } } else { Integer lo[MAXDIM], hi[MAXDIM]; Integer j, jtot, last, offset; pnga_distribution(g_a, proc, lo, hi); pnga_access_block_ptr(g_a, proc, (char**)&ptr, ldp); pnga_release_block(g_a, proc); offset = 0; last = ndim - 1; jtot = 1; for (j=0; j 1) { count[istride] = (int)(idx/skip[i]+1); istride++; } else { count[istride] = (int)idx+1; istride++; } } *nstride = istride; return 1; #else Integer idx; int i, istride = 0; /* if (GAme==0) { printf("p[%d] ComputeCount lo[0]: %d hi[0]: %d lo[1]: %d hi[1]: %d\n",GAme, lo[0],hi[0],lo[1],hi[1]); } */ for (i=0; i<(int)ndim; i++) { idx = hi[i] - lo[i]; if (idx < 0) return 0; if (skip[i] > 1) { count[istride] = 1; istride++; count[istride] = (int)(idx/skip[i]+1); istride++; } else { count[istride] = (int)idx+1; istride++; } } *nstride = istride; return 1; #endif } /** * Utility function to calculate stride lengths on local and * remote processors, taking into account the presence of skips */ void gai_SetStrideWithSkip(Integer ndim, Integer size, Integer *ld, Integer *ldrem, int *stride_rem, int *stride_loc, Integer *skip) { #if 1 int i; stride_rem[0] = stride_loc[0] = (int)size; for (i=0; i 1) { stride_rem[istride] *= (int)skip[i]; stride_rem[istride+1] = stride_rem[istride]/((int)skip[i]); stride_loc[istride+1] = stride_loc[istride]; istride++; if (i 0 : the leading minor of this order c* is not positive definite and the c* factorization could not be completed c* c*********************************************************************** integer function ga_cholesky(uplo, g_a) implicit none character*1 uplo ! (input) 'U' or 'L' integer g_A ! (input/output) integer ga_llt_f ga_cholesky = ga_llt_f(uplo, g_A, -1) end c*********************************************************************** c* function : ga_llt_f c* c* Internal function to compute the Cholesky factorization of c* an NxN double precision symmetric positive definite GA. c* c* Note: applications should use ga_cholesky c* c* On succesful exit A will contain the L/U factor c* on the lower/upper triangular part of the matrix c* c* This if (hsA.eq.-1), otherwise internal state c* is saved for future references. This is c* reserved for internal use. Users should c* not try to set hsA to something c* different from -1 if they do not c* know what they are doing! On exit hsA will c* contain the local A output array in SLS format. c* c* It calls the PDPOTRF ScaLAPACK routine. c* c* It returns c* = 0 : successful exit c* > 0 : the leading minor of this order c* is not positive definite and the c* factorization could not be completed c*********************************************************************** integer function ga_llt_f(uplo, g_A, hsA) implicit none #include "mafdecls.fh" #include "global.fh" #include "scalapack.fh" c**** character*1 uplo ! (input) 'U' or 'L' integer g_A ! (input/output) integer hsA ! (input/output) c**** logical status integer hA MA_ACCESS_INDEX_TYPE adrA integer dimA18, dimA28, typeA INTGR4 dimA1, dimA2 INTGR4 mpA, nqA INTGR4 info integer me INTGR4 n, ldA integer elemA INTGR4 numroc INTGR4 nb INTGR4 descA(DSCLEN) integer intsize,info8 INTGR4 one4,zero4 parameter(zero4=0,one4=1) logical oactive integer alen,block_dims(2),blocks(2) logical use_direct data nb /NB_/ c**** Check Environment me = ga_nodeid() c**** Check GA info for input array A call ga_check_handle(g_A, 'ga_llt_f: A') call ga_inquire(g_A, typeA, dimA18, dimA28) dima1=dima18 dima2=dima28 c**** if (dimA1.ne.dimA2) then call ga_error('ga_llt_f: g_A must be a square matrix ', 1) endif c c Determine whether or not to use GA data directly c use_direct = ga_uses_proc_grid(g_A) if (use_direct) then call ga_get_block_info(g_A,blocks,block_dims) if (block_dims(1).ne.block_dims(2)) then use_direct = .false. endif endif if (use_direct) then nb = block_dims(1) endif c**** n = dimA1 c**** Initialize SL Interface if (use_direct) then call SLinit3(g_A) else call SLinit() endif c oactive=iam.lt.maxproc oactive=.true. call ga_sync c**** Find SBS format parameters if(oactive) then mpA = numroc(n, nb, myrow, zero4, nprow) nqA = numroc(n, nb, mycol, zero4, npcol) ldA = max(one4,mpA) c**** Allocate A elemA = mpA * nqA status = .true. if (use_direct) then call nga_access_block_segment(g_A,me,adrA,alen) else c if (elemA.ne.0) c & status = ma_push_get(MT_DBL, elemA, 'A', hA, adrA) if (elemA.ne.0) then c write(6,'(a,i1,a,i12)') c & 'p[',me,'] allocating hA hsa: ',hsa status = ma_push_get(MT_DBL, elemA, 'A', hA, adrA) endif if (.not.status) & call ga_error('ga_llt_f: mem alloc failed A ', -1) endif c**** Copy ga to A using SBS ScaLAPACK format if (.not.use_direct) then call ga_to_SL(g_a, n, n, nb, nb, dbl_mb(adrA), ldA, mpA, nqA) endif endif call ga_sync() if(oactive) then c**** Fill ScaLAPACK matrix descriptor call descinit(descA, dimA1, dimA2, nb, nb, zero4, zero4, & iSLctxt, ldA, info) if(info.ne.0) call ga_error('ga_llt_f: descinit A failed', I -info) c**** CALL ScaLAPACK PDPOTRF LLT factorization routine ******* call PDPOTRF(uplo, n, dbl_mb(adrA), one4, one4, descA, info) c************************************************************* if (info.eq.0) then c**** Copy solution matrix back to A if hsA==-1 c**** and zero the L/U triangle part according to uplo if (hsA.eq.-1) then if (.not.use_direct) then call ga_from_SL(g_A, dimA1, dimA2, nb, nb, dbl_mb(adrA), & mpA, ldA, nqA) endif endif c c**** If the SL A array was allocated if (elemA.ne.0) then c**** and hsA==-1 or info>0 (i.e. fact. cannot be completed) c**** then deallocate the SL A MA array if (hsA.eq.-1) then if (.not.use_direct) then c write(6,'(a,i1,a)') 'p[',me,'] deallocating hA at 1' status = ma_pop_stack(hA) endif c**** otherwise just save the hA MA handle else hsA = hA endif endif else if (.not.use_direct) then c write(6,'(a,i1,a)') 'p[',me,'] deallocating hA at 2' status = ma_pop_stack(hA) endif endif endif c call ga_sync() #if 0 info8=info if(maxproc.lt.nnodes) then intsize=ma_sizeof(MT_INT,1,MT_BYTE) call ga_brdcst(1688,info8,intsize,0) endif info=info8 #endif if (info.eq.0) then if (hsA.eq.-1) then call ga_zeroUL(uplo, g_A) endif elseif (info.lt.0) then call ga_error('ga_llt_f: PDPOTRF failed ', -info) endif ga_llt_f = info c if (use_direct) then call slexit3 else call slexit endif end c*********************************************************************** c* subroutine: ga_llt_s c* c* Solves a system of linear equations c* c* A * X = B c* c* where A is the lower triangle L or the upper c* triangular Cholesky factor U of a NxN double precision c* symmetric positive definite global array (LL' or U'U) c* obtained from ga_llt_f routine. c* c* If (hsA.eq.-1) then you "must" give in input c* the lower or upper triangular Cholesky factor. c* For internal use, if (hsA.ne.-1) then hsA is c* the MA handle of the Cholesky L/U factor already c* in SBS SL format. c* c* On successful exit B will contain the solution X. c* c* It calls the PDPOTRS ScaLAPACK routine. c*********************************************************************** subroutine ga_llt_s(uplo, g_A, g_B, hsA) implicit none #include "mafdecls.fh" #include "global.fh" #include "scalapack.fh" c**** character*1 uplo ! (input) 'U' or 'L' integer g_A ! (input) integer g_B ! (input/ouput) integer hsA ! (input) c**** logical status integer hA, hB MA_ACCESS_INDEX_TYPE adrA, adrB integer dimA18, dimA28, typeA integer dimB18, dimB28, typeB INTGR4 dimA1, dimA2 INTGR4 dimB1, dimB2 INTGR4 mpA, nqA INTGR4 mpB, nqB INTGR4 info integer me INTGR4 n, ldA, ldB integer elemA, elemB INTGR4 numroc INTGR4 nb INTGR4 descA(DSCLEN), descB(DSCLEN) INTGR4 one4,zero4 parameter(zero4=0,one4=1) c integer block_dims_A(2),block_dims_B(2),blocks(2) integer gridA(2), gridB(2) logical uses_sl_A, uses_sl_B integer alen, blen logical use_direct c data nb /NB_/ c c**** Check the Environment me = ga_nodeid() c**** Check GA info for input array F, B call ga_check_handle(g_A, 'ga_llt_s: A') call ga_check_handle(g_B, 'ga_llt_s: B') call ga_inquire(g_A, typeA, dimA18, dimA28) call ga_inquire(g_B, typeB, dimB18, dimB28) dima1=dima18 dima2=dima28 dimb1=dimb18 dimb2=dimb28 c**** if (dimA1.ne.dimA2) then call ga_error('ga_llt_s: g_A must be a square matrix ', 1) else if (dimA1.ne.dimB1) then call ga_error('ga_llt_s: dims of A and B do not match ', 1) endif c c Determine whether or not to use GA data directly c use_direct = .true. uses_sl_A = ga_uses_proc_grid(g_a) uses_sl_B = .false. if (uses_sl_A) then uses_sl_B = ga_uses_proc_grid(g_b) endif if ((.not.uses_sl_A).or.(.not.uses_sl_B)) then use_direct = .false. endif if (uses_sl_A) then call ga_get_block_info(g_a,blocks,block_dims_A) if (block_dims_A(1).ne.block_dims_A(2)) then use_direct = .false. endif endif if (uses_sl_B) then call ga_get_block_info(g_b,blocks,block_dims_B) if (block_dims_B(1).ne.block_dims_B(2)) then use_direct = .false. endif endif if (use_direct.and.(block_dims_A(1).ne.block_dims_B(1))) then use_direct = .false. endif if (use_direct) then call ga_get_proc_grid(g_a,gridA) call ga_get_proc_grid(g_b,gridB) if (gridA(1).ne.gridB(1).or.gridA(2).ne.gridB(2)) then use_direct = .false. endif endif if (use_direct) then nb = block_dims_A(2) endif c n = dimA1 c**** Initialize SL Interface if (use_direct) then call SLinit3(g_A) else call SLinit() endif #if 1 c**** Find SBS format parameters mpA = numroc(n, nb, myrow, zero4, nprow) nqA = numroc(n, nb, mycol, zero4, npcol) mpB = numroc(n, nb, myrow, zero4, nprow) nqB = numroc(n, nb, mycol, zero4, npcol) ldA = max(one4,mpA) ldB = max(one4,mpB) call ga_sync() c**** Allocate A or retrieve it from the ga_llt_f calls elemA = mpA * nqA status = .true. if (elemA.ne.0) then if (use_direct) then call nga_access_block_segment(g_A,me,adrA,alen) else if (hsA.eq.-1) then status = ma_push_get(MT_DBL, elemA, 'A', hA, adrA) if (.not.status) & call ga_error('ga_llt_s: mem alloc failed A ', -1) c**** copy g_A to A using SBS SL format call ga_to_SL(g_A, n, n, nb, nb, dbl_mb(adrA), & ldA, mpA, nqA) else hA = hsA status = ma_get_index(hA, adrA) if (.not.status) & call ga_error('ga_llt_s: get index failed A ', -1) c**** if hsA was given the SBS SL conversion is avoided endif endif endif c**** Allocate B and copy g_B to B in SBS SL format elemB = mpB * nqB if (use_direct) then call nga_access_block_segment(g_B,me,adrB,blen) else if (elemB.ne.0) status = ma_push_get(MT_DBL,elemB,'B',hB,adrB) if (.not.status) & call ga_error('ga_llt_s: mem alloc failed B ', -1) call ga_to_SL(g_B, n, dimB2, nb, nb, dbl_mb(adrB), ldB, mpB,nqB) endif c c**** Fill ScaLAPACK matrix descriptors for A and B call descinit(descA, dimA1, dimA2, nb, nb, zero4, zero4, & iSLctxt, ldA, info) if(info.ne.0) call ga_error(' ga_llt_s: descinit A failed ', & -info) call descinit(descB, dimB1, dimB2, nb, nb, zero4,zero4, D iSLctxt, ldB, info) if(info.ne.0) call ga_error('ga_llt_s: descinit B failed', -info) c**** CALL ScaLAPACK PDPOTRS solver routine *********************** call ga_sync() call PDPOTRS(uplo, n, dimB2, & dbl_mb(adrA), one4, one4, descA, & dbl_mb(adrB), one4, one4, descB, & info) c****************************************************************** if (info.eq.0) then c**** copy solution matrix back to g_B if (.not.use_direct) then call ga_from_SL(g_B, dimB1, dimB2, nb, nb, dbl_mb(adrB), & mpB, ldB, nqB) endif else call ga_error(' ga_llt_s: PDPOTRS failed: ', -info) endif c**** deallocate work/SL arrays c**** note that should not be others MA allocations c**** between ga_llt_f and ga_llt_s beside A and B if (.not.use_direct) then if (elemB.ne.0) status = ma_pop_stack(hB) if (elemA.ne.0) status = ma_pop_stack(hA) endif c #endif if (use_direct) then call slexit3 else call slexit endif c call ga_sync() end c*********************************************************************** c* function : ga_llt_i c* c* It computes the inverse of a global array c* that is the lower triangle L or the upper c* triangular Cholesky factor U of a NxN double precision c* symmetric positive definite global array (LL' or U'U) c* obtained from the ga_llt_f routine. c* c* If (hsA.eq.-1) then you "must" give in input c* the lower or upper triangular Cholesky factor. c* For internal use, if (hsA.ne.-1) then hsA is c* the MA handle of the Cholesky L/U factor already c* in SBS SL format. c* c* On successful exit A will contain the inverse. c* c* It calls the PDPOTRI ScaLAPACK routine. c* c* It returns c* = 0 : successful exit c* > 0 : it returns the index i of the (i,i) c* element of the factor L/U that is zero and, c* so, the inverse could not be computed c*********************************************************************** integer function ga_llt_i(uplo, g_A, hsA) implicit none #include "mafdecls.fh" #include "global.fh" #include "scalapack.fh" c**** character*1 uplo ! (input) 'U' or 'L' integer g_A ! (input/output) integer hsA ! (input/output) c**** logical status integer hA MA_ACCESS_INDEX_TYPE adrA integer dimA18, dimA28, typeA INTGR4 dimA1, dimA2 INTGR4 mpA, nqA INTGR4 info integer info8 integer nproc, me INTGR4 n, ldA integer elemA INTGR4 numroc INTGR4 nb INTGR4 descA(DSCLEN) integer intsize logical oactive INTGR4 one4,zero4 parameter(zero4=0,one4=1) integer alen, block_dims(2),blocks(2) logical use_direct c**** data nb /NB_/ c c**** Check Environment nproc = ga_nnodes() me = ga_nodeid() c**** Check GA info for input array A call ga_check_handle(g_A, 'ga_llt_i: A') call ga_inquire(g_A, typeA, dimA18, dimA28) dima1=dima18 dima2=dima28 c**** Check that is actually a square matrix if (dimA1.ne.dimA2) then call ga_error('ga_llt_i: g_A must be a square matrix ', 1) endif c c Determine whether or not to use GA data directly c use_direct = ga_uses_proc_grid(g_A) if (use_direct) then call ga_get_block_info(g_A,blocks,block_dims) if (block_dims(1).ne.block_dims(2)) then use_direct = .false. endif endif if (use_direct) then nb = block_dims(1) endif c n = dimA1 c**** Initialize SL Interface if (use_direct) then call SLinit3(g_A) else call SLinit() endif c oactive=iam.lt.maxproc oactive=.true. call ga_sync() if(oactive) then c**** Find SBS format parameters mpA = numroc(n, nb, myrow, zero4, nprow) nqA = numroc(n, nb, mycol, zero4, npcol) ldA = max(one4, mpA) c**** Allocate A or retrieve it from ga_llt_f call elemA = mpA * nqA if (use_direct) then call nga_access_block_segment(g_A,me,adrA,alen) else if (elemA.ne.0) then status = .true. if (hsA.eq.-1) then status = ma_push_get(MT_DBL, elemA, 'A', hA, adrA) if (.not.status) & call ga_error(' ga_llt_i: mem alloc failed A ', -1) c**** copy g_A to A using SBS SL format call ga_to_SL(g_A, n, n, nb, nb, dbl_mb(adrA), & ldA, mpA, nqA) else hA = hsA status = ma_get_index(hA, adrA) if (.not.status) & call ga_error(' ga_llt_i: get index failed A ', -1) c**** if hsA was given the SBS SL conversion is avoided endif endif endif endif call ga_sync() if(oactive) then c**** Fill ScaLAPACK matrix descriptor for A call descinit(descA, dimA1, dimA2, nb, nb, zero4, zero4, & iSLctxt, ldA, info) if (info.ne.0) call ga_error(' ga_llt_i: descinit A failed ', & -info) c**** CALL ScaLAPACK PDPOTRI matrix inverter ********************** call PDPOTRI(uplo, n, dbl_mb(adrA), one4, one4, descA, info) c****************************************************************** if (.not.use_direct) then if (info.eq.0) then c**** Copy the inverse matrix back to A c**** and symmetrize it call ga_from_SL(g_A, dimA1, dimA2, nb, nb, dbl_mb(adrA), & mpA, ldA, nqA) endif if(elemA.ne.0) status = ma_pop_stack(hA) endif endif call ga_sync() #if 0 info8=info if(maxproc.lt.nnodes) then intsize=ma_sizeof(MT_INT,1,MT_BYTE) call ga_brdcst(1688,info8,intsize,0) endif info=info8 #endif if (info.eq.0) then call ga_symUL(uplo, g_A) elseif (info.lt.0) then call ga_error(' ga_llt_i: PDPOTRI failed ', -info) endif ga_llt_i = info c**** deallocate work/SL arrays c**** note that should not be others MA allocations c**** between ga_llt_f and ga_llt_i c end c*********************************************************************** c* function : gai_llt_solve c* c* Solves a system of linear equations c* c* A * X = B c* c* using the Cholesky factorization of an NxN c* double precision symmetric positive definite c* global array A. c* c* On successful exit B will contain the solution X. c* c* It calls the ga_llt_f and ga_llt_s and so it c* actually refer to PDPORTF/PDPOTRS ScaLAPACK c* routines. c* c* It returns c* = 0 : successful exit c* > 0 : the leading minor of this order c* is not positive definite and the c* factorization could not be completed c*********************************************************************** integer function gai_llt_solve(g_A, g_B) implicit none character*1 uplo ! (internal parameter) 'U' or 'L' integer g_A ! (input) integer g_B ! (input/output) integer hsA integer irc integer ga_llt_f c c**** call the Cholesky factorization routine hsA = 0 uplo = 'L' irc = ga_llt_f(uplo, g_A, hsA) c**** check if the factorization is OK if (irc.eq.0) then c**** if the factorization is OK then solve the system c**** call the ga_llt_s internal interface call ga_llt_s(uplo, g_A, g_B, hsA) gai_llt_solve = 0 else c**** if the factorization is not OK just return the error gai_llt_solve = irc endif end c*********************************************************************** c* function : gai_spd_invert c* c* It computes the inverse of a double precision c* using the Cholesky factorization of a NxN double c* precision symmetric positive definite global array A. c* c* On successful exit A will contain the inverse. c* c* It calls the ga_llt_f and ga_llt_i and so it c* actually refer to PDPORTF/PDPOTRI ScaLAPACK c* routines. c* c* It returns c* = 0 : successful exit c* > 0 : the leading minor of this order c* is not positive definite and the c* factorization could not be completed c* < 0 : it returns the index i of the (i,i) c* element of the factor L/U that is zero and, c* so, the inverse could not be computed c*********************************************************************** integer function gai_spd_invert(g_A) implicit none character*1 uplo ! (internal parameter) 'U' or 'L' integer g_A ! (input) integer hsA integer ircF, ircI integer ga_llt_f integer ga_llt_i c c**** call the Cholesky factorization routine hsA = 0 uplo = 'L' ircF = ga_llt_f(uplo, g_A, hsA) c c**** check if the factorization is OK if (ircF.eq.0) then c**** if the factorization is OK then invert the matrix c**** call the ga_llt_i internal interface ircI = ga_llt_i(uplo, g_A, hsA) if (ircI.eq.0) then gai_spd_invert = 0 else gai_spd_invert = -ircI endif else c**** if the factorization is not OK just return the error gai_spd_invert = ircF endif end c*********************************************************************** c* function: gai_solve c* c* Solves a system of linear equations c* c* A * X = B c* c* It first will call the Cholesky factorization c* routine and, if sucessfully, will solve the system c* with the Cholesky solver. If Cholesky will be not c* be able to factorize A, then it will call the LU c* factorization routine and will solve the system c* with forward/backward substitution. c* c* On B will contain the solution X. c* c* It calls the ga_llt_f and ga_llt_s and gai_lu_solve c* and so it actually refer to PDGETRF/PDGETRS and c* PDPORTF/PDPOTRS ScaLAPACK routines. c* c* It returns c* = 0 : Cholesky factoriztion was succesful c* > 0 : the leading minor of this order c* is not positive definite and Cholesky c* factorization could not be completed c* c* Note: High overhead due to multiple conversions to/from scalapack c* format can be avoided if gai_llt_solve and gai_lu_solve c* were not used as building blocks - possible optimization c* for the future (JN) c*********************************************************************** integer function gai_solve(g_A, g_B) implicit none integer g_A ! (input) integer g_B ! (input/output) integer irc integer gai_llt_solve c c**** Call Cholesky solver as the first try gai_solve = gai_llt_solve(g_A, g_B) if (gai_solve.eq.0) then c**** if Cholesky was succcesully just return else c**** else if Cholesky failed call gai_lu_solve('N', g_A, g_B) endif end c****************************************************************** c c ga_pdsyev c c interface into scalapack's Hermitian eigensolver c c ga interface to scalapack c c g_a .g_b(*,i) = eval(i).g_b(*,i) c c assume equal size blocks ... c c****************************************************************** c subroutine ga_pdsyev(g_a, g_b, eval, nb8) implicit none #include "mafdecls.fh" #include "global.fh" #include "scalapack.fh" c integer g_a ! matrix A integer g_b ! matrix B integer nb8 ! block size c c all eigenvalues are real c double precision eval(*) c c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c logical status c character*1 jobz, uplo c integer ha !A integer hb !B MA_ACCESS_INDEX_TYPE adrA, adrB c c logical oactive ! true iff this process participates integer dimA18, dimA28, typeA integer dimB18, dimB28, typeB INTGR4 dimA1, dimA2 INTGR4 dimB1, dimB2 c INTGR4 mpA, nqA ! rows/cols of A held by the processor INTGR4 mpB, nqB ! rows/cols of B held by the processor c integer me INTGR4 lda, ldb integer elemA,elemB INTGR4 numroc INTGR4 nb ! block size INTGR4 descA(9), descB(9) !descriptor for scalapack c integer lcwork,hcwork, adrcwork INTGR4 lcwork4 c INTGR4 nn,mq0, np0 INTGR4 n INTGR4 info integer info8 integer dblsize,ldc INTGR4 zero4,one4,two4 parameter(zero4=0,one4=1,two4=2) double precision pdlamch,dumm INTGR4 iceil logical uses_sl_A, uses_sl_B integer alen, blen integer block_dims_A(2),block_dims_B(2),blocks(2) integer gridA(2), gridB(2) logical use_direct external pdlamch integer i,j c c processor dependent; machine dependent c if(nb8.eq.0) then nb=16 else nb=nb8 endif c c*** check environment c me = ga_nodeid() c c*** check GA info for input arrays c call ga_check_handle(g_a, 'ga_pdsyev: a') call ga_check_handle(g_b, 'ga_pdsyev: b') c call ga_inquire(g_a, typeA, dimA18, dimA28) call ga_inquire(g_b, typeB, dimB18, dimB28) dima1=dima18 dima2=dima28 dimb1=dimb18 dimb2=dimb28 n=dima1 if(nb.lt.1) nb=1 if(dimA1.ne.dima2) call ga_error( % 'ga_pdsyev: matrix A not square ',0) if(dimb1.ne.dimb2) call ga_error( % 'ga_pdsyev: matrix B not square ',0) if(dimb1.ne.n) call ga_error( % 'ga_pdsyev: size matrix A and B differ ',0) use_direct = .true. uses_sl_A = ga_uses_proc_grid(g_a) uses_sl_B = .false. if (uses_sl_A) then uses_sl_B = ga_uses_proc_grid(g_b) endif if ((.not.uses_sl_A).or.(.not.uses_sl_B)) then use_direct = .false. endif if (uses_sl_A) then call ga_get_block_info(g_a,blocks,block_dims_A) if (block_dims_A(1).ne.block_dims_A(2)) then use_direct = .false. endif endif if (uses_sl_B) then call ga_get_block_info(g_b,blocks,block_dims_B) if (block_dims_B(1).ne.block_dims_B(2)) then use_direct = .false. endif endif if (use_direct.and.(block_dims_A(1).ne.block_dims_B(1))) then use_direct = .false. endif if (use_direct) then call ga_get_proc_grid(g_a,gridA) call ga_get_proc_grid(g_b,gridB) if (gridA(1).ne.gridB(1).or.gridA(2).ne.gridB(2)) then use_direct = .false. endif endif if (use_direct) then nb = block_dims_A(2) endif c c c*** initialize SL interface c if (use_direct) then call SLinit4(g_a) else call SLinit2(n) endif oactive=iam.lt.maxproc if (oactive) then c c*** find SBS format parameters c c mpA = numroc(dimA1, nb, myrow2, zero4, nprow2) nqA = numroc(dimA2, nb, mycol2, zero4, npcol2) c mpB = numroc(dimB1, nb, myrow2, zero4, nprow2) nqB = numroc(dimB2, nb, mycol2, zero4, npcol2) c c lda = max(one4,mpA) ldb = max(one4,mpB) c c c let scalapack check for errors c c should check to see if this is a compute node c check to see how this works in the new data server model c c if (use_direct) then call nga_access_block_segment(g_a,me,adra,alen) call nga_access_block_segment(g_b,me,adrb,blen) else elemA= mpA*nqA status = .true. if (elemA.ne.0)status = $ ma_push_get(MT_DBL,elemA,'a',ha,adra) if (.not.status) & call ga_error('ga_pdsyev: mem alloc failed A ', -1) c elemB= mpB*nqB c if (elemB.ne.0)status = $ ma_push_get(MT_DBL,elemB,'b',hb,adrb) if (.not.status) & call ga_error('ga_pdsyev: mem alloc failed B ', -1) c c c*** copy g_a to A using the block cyclic scalapack format c call ga_to_SL2(g_a, dimA1, dimA2, nb, nb, $ dbl_mb(adrA), lda, mpA, nqA) endif endif call ga_sync() if(oactive) then c c*** fill SCALAPACK matrix descriptors c call descinit(descA, dimA1, dimA2, nb, nb, zero4, zero4, $ islctxt2, lda, info) info8=info if (info8.ne.0) $ call ga_error(' ga_pdsyev: descinit A failed ',-info8) c call descinit(descB, dimB1, dimB2, nb, nb, zero4, zero4, . islctxt2, ldb, info) info8=info if (info8.ne.0) $ call ga_error(' ga_pdsyev: descinit B failed ',-info8) c jobz = 'V' uplo = 'L' c nn = max(n, nb, two4) np0 = numroc(nn, nb, zero4, zero4, nprow2) mq0 = numroc(nn, nb, zero4, zero4, npcol2) c c get lcwork c #if 1 lcwork4=-1 call pdsyev(jobz, uplo, 1 n, dbl_mb(adrA), one4, one4, descA, 2 eval,dbl_mb(adrB), one4, one4, D descB, dumm, lcwork4, 3 info) lcwork=dumm #else lcwork = 5*n +MAX(3*NB,NB*(NP0+1)) c lwmin ldc=numroc(n,nb,myrow2,zero4,nnodes) ldc=max(1,ldc) lcwork=max(lcwork,5*n+n*ldc+1+max(2*n-2,8192)) #endif c if (lcwork.ne.0) $ status = ma_push_get(MT_DBL, lcwork , $ 'cwork',hcwork,adrcwork) if (.not.status) & call ga_error('ga_pdsyev: mem alloc failed cwork ', -1) c c lcwork4=lcwork call pdsyev(jobz, uplo, 1 n, dbl_mb(adrA), one4, one4, descA, 2 eval,dbl_mb(adrB), one4, one4, D descB, dbl_mb(adrcwork), lcwork4, 3 info) c if ( info .ne. 0 ) then if ( info .gt. 0 ) then call ga_error(' ga_pdsyev: argument is illegal ', info) else call ga_error(' ga_pdsyev: eigenvecs not converged ', $ info) endif endif c c*** copy solution matrix back to g_c c if (.not.use_direct) then call ga_from_SL2(g_b, dimA1, dimB2, $ nb, nb, dbl_mb(adrB), & ldb, mpb, nqB) endif c c*** deallocate work/SL arrays c if ( lcwork .ne. 0 ) status = ma_pop_stack(hcwork) if (.not.use_direct) then if ( elemB .ne. 0 ) status = ma_pop_stack(hb) if ( elemA .ne. 0 ) status = ma_pop_stack(ha) endif endif c call ga_sync() if(maxproc.lt.nnodes.or.dima1.le.nb) then c broadcast evals dblsize=ma_sizeof(MT_DBL,1,MT_BYTE)*dima1 call ga_brdcst(1688,eval,dblsize,0) endif if (use_direct) then call SLexit4 else call SLexit2 endif return end c****************************************************************** c c ga_pdsyevx c c****************************************************************** c subroutine ga_pdsyevx(g_a, g_b, eval, nb8) implicit none #include "mafdecls.fh" #include "global.fh" #include "scalapack.fh" c integer g_a ! matrix A integer g_b ! matrix B integer nb8 ! block size c c all eigenvalues are real c double precision eval(*) c c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c logical status c character*1 jobz, range, uplo c integer ha, adra !A integer hb, adrb !B c c logical oactive ! true iff this process participates integer dimA18, dimA28, typeA integer dimB18, dimB28, typeB INTGR4 dimA1, dimA2 INTGR4 dimB1, dimB2 c INTGR4 mpA, nqA ! rows/cols of A held by the processor INTGR4 mpB, nqB ! rows/cols of B held by the processor c integer me INTGR4 lda, ldb integer elemA,elemB INTGR4 numroc INTGR4 nb ! block size INTGR4 descA(9), descB(9) !descriptor for scalapack c integer ngaps, hgap, adrgaps integer iclu, hclustr, adrclustr integer if, hfail, adrfail integer liwork,hiwork, adriwork integer lcwork,hcwork, adrcwork INTGR4 lcwork4 INTGR4 liwork4 c INTGR4 nn,mq0, np0 double precision vl, vu, abstol, orfac INTGR4 il, iu INTGR4 m, nz INTGR4 n INTGR4 info integer info8 integer dblsize INTGR4 zero4,one4,two4,four4 integer two4n parameter(zero4=0,one4=1,two4=2,four4=4) double precision pdlamch,dumm external pdlamch INTGR4 iceil c c processor dependent; machine dependent c if(nb8.eq.0) then nb=16 else nb=nb8 endif c c*** check environment c me = ga_nodeid() c c*** check GA info for input arrays c call ga_check_handle(g_a, 'ga_pdsyevx: a') call ga_check_handle(g_b, 'ga_pdsyevx: b') c call ga_inquire(g_a, typeA, dimA18, dimA28) call ga_inquire(g_b, typeB, dimB18, dimB28) dima1=dima18 dima2=dima28 dimb1=dimb18 dimb2=dimb28 n=dima1 if(nb.lt.1) nb=1 if(dimA1.ne.dima2) call ga_error( % 'ga_pdsyevx: matrix A not square ',0) if(dimb1.ne.dimb2) call ga_error( % 'ga_pdsyevx: matrix B not square ',0) if(dimb1.ne.n) call ga_error( % 'ga_pdsyevx: size matrix A and B differ ',0) c c c*** initialize SL interface c call SLinit2(n) oactive=iam.lt.maxproc call ga_sync if (oactive) then c c*** find SBS format parameters c c mpA = numroc(dimA1, nb, myrow2, zero4, nprow2) nqA = numroc(dimA2, nb, mycol2, zero4, npcol2) c mpB = numroc(dimB1, nb, myrow2, zero4, nprow2) nqB = numroc(dimB2, nb, mycol2, zero4, npcol2) c c lda = max(one4,mpA) ldb = max(one4,mpB) c c c let scalapack check for errors c c should check to see if this is a compute node c check to see how this works in the new data server model c c elemA= mpA*nqA status = .true. if(elemA.ne.0)status = $ ma_push_get(MT_DBL,elemA,'a',ha,adra) if(.not.status) & call ga_error('ga_pdsyevx: mem alloc failed A ', -1) c c*** copy g_a to A using the block cyclic scalapack format c call ga_to_SL2(g_a, dimA1, dimA2, nb, nb, $ dbl_mb(adrA), lda, mpA, nqA) c elemB= mpB*nqB c if(elemB.ne.0)status = $ ma_push_get(MT_DBL,elemB,'b',hb,adrb) if(.not.status) & call ga_error('ga_pdsyevx: mem alloc failed B ', -1) c ngaps = nprow2*npcol2 if(ngaps.ne.0)status = $ ma_push_get(MT_DBL, ngaps ,'gap',hgap,adrgaps) if(.not.status) & call ga_error('ga_pdsyevx: mem alloc failed gaps ', -1) c iclu = 2*nprow2*npcol2 two4n=two4*n iclu = max(two4n,iclu) if(iclu.ne.0)status = $ ma_push_get(MT_INT, iclu ,'iclustr',hclustr,adrclustr) if(.not.status) & call ga_error('ga_pdsyevx: mem alloc failed iclustr',-1) c if = n if(if.ne.0) then status = $ ma_push_get(MT_INT, if ,'ifail',hfail,adrfail) endif if(.not.status) & call ga_error('ga_pdsyevx: mem alloc failed ifail ', -1) endif call ga_sync() if (.not.ga_deallocate(g_b)) $ call ga_error('ga_pdsyevx: ga_deallocate failed',0) if(oactive) then c c*** fill SCALAPACK matrix descriptors c call descinit(descA, dimA1, dimA2, nb, nb, zero4, zero4, $ islctxt2, lda, info) info8=info if(info8.ne.0) call ga_error(' ga_pdsyevx: descinit A failed ', . -info8) c call descinit(descB, dimB1, dimB2, nb, nb, zero4, zero4, . islctxt2, ldb, info) info8=info if(info8.ne.0) call ga_error(' ga_pdsyevx: descinit B failed ', . -info8) c jobz = 'V' range = 'A' range = 'I' uplo = 'L' vl = 0.d0 vu = 0.d0 il = 0 iu = 0 il = 1 ! iu = neigen iu = n nz = 0 c c ability to deal with orthonormality ; let's just c have the regular scalapack stuff for the moment c liwork = 6*max(n, nprow2*npcol2+one4, four4) liwork=liwork if(liwork.ne.0)status = $ ma_push_get(MT_INT, liwork ,'iwork',hiwork,adriwork) if(.not.status) & call ga_error('ga_pdsyevx: mem alloc failed iwork ', -1) c nn = max(n, nb, two4) np0 = numroc(nn, nb, zero4, zero4, nprow2) mq0 = numroc(nn, nb, zero4, zero4, npcol2) c c orfac = 1.d-3 c c #if 0 lcwork4=-1 call pdsyevx(jobz, range, uplo, 1 n, dbl_mb(adrA), one4, one4, descA,vl, 2 vu, il, iu, abstol, m, nz, eval, orfac, dbl_mb(adrB), 4 one4, one4, descB, dumm, lcwork4, 3 int_mb(adriwork), liwork4, int_mb(adrfail), 4 int_mb(adrclustr), dbl_mb(adrgaps), info) lcwork=dumm #else lcwork = 5*n +MAX(5*NN,(NP0*MQ0 + 2*nb*nb))+ + ICEIL( N, NPROW2*NPCOL2)*NN+1 #endif c if(lcwork.ne.0) $ status = ma_push_get(MT_DBL, lcwork , $ 'cwork',hcwork,adrcwork) if(.not.status) & call ga_error('ga_pdsyevx: mem alloc failed cwork ', -1) c c abstol=pdlamch(islctxt2, 'U') c c liwork4=liwork lcwork4=lcwork call pdsyevx(jobz, range, uplo, 1 n, dbl_mb(adrA), one4, one4, descA,vl, 2 vu, il, iu, abstol, m, nz, eval, orfac, dbl_mb(adrB), 4 one4, one4, descB, dbl_mb(adrcwork), lcwork4, 3 int_mb(adriwork), liwork4, int_mb(adrfail), 4 int_mb(adrclustr), dbl_mb(adrgaps), info) c if (nz .ne. n ) then if ( info .ne. 0 ) then if ( info .gt. 0 ) then call ga_error(' ga_pdsyevx: argument is illegal ', info) else call ga_error(' ga_pdsyevx: eigenvectors failed to converge ', $ info) endif endif endif c c c c*** copy solution matrix back to g_c c end if ! oactive if(.not.ga_allocate(g_b)) E call ga_error(' ga_pdsyevx: ga_allocate failed',0) call ga_zero(g_b) if (oactive) then call ga_from_SL2(g_b, dimA1, dimB2, $ nb, nb, dbl_mb(adrB), & ldb, mpb, nqB) c c c c*** deallocate work/SL arrays c if ( lcwork .ne. 0 ) status = ma_pop_stack(hcwork) if ( liwork .ne. 0 ) status = ma_pop_stack(hiwork) if ( if .ne. 0 ) status = ma_pop_stack(hfail) if ( iclu .ne. 0 ) status = ma_pop_stack(hclustr) if ( ngaps.ne.0 ) status = ma_pop_stack(hgap) if ( elemB .ne. 0 ) status = ma_pop_stack(hb) if ( elemA .ne. 0 ) status = ma_pop_stack(ha) endif c call ga_sync() if(maxproc.lt.nnodes.or.dima1.le.nb) then c broadcast evals dblsize=ma_sizeof(MT_DBL,1,MT_BYTE)*dima1 call ga_brdcst(1688,eval,dblsize,0) endif return end c****************************************************************** c c ga_pdsygv c c interface into scalapack's Hermitian eigensolver c c ga interface to scalapack c c g_a .g_c(*,i) = eval(i).g_s(*,i) g_b(*,i) c c assume equal size blocks ... c c****************************************************************** subroutine ga_pdsygv(g_a, g_s, g_b, eval) implicit none #include "mafdecls.fh" #include "global.fh" #include "scalapack.fh" c integer g_a ! matrix A integer g_b ! matrix B (eigenvectors) integer g_s ! metric c c all eigenvalues are real c double precision eval(*) c c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c logical status c character*1 jobz, range, uplo c integer ha, adra !A (input) integer hb, adrb !B (output) integer hs, adrs !S (input) c c logical oactive ! true if this process participates integer dimA18, dimA28, typeA integer dimB18, dimB28, typeB integer dims18, dims28, types INTGR4 dimA1, dimA2 INTGR4 dimB1, dimB2 INTGR4 dims1, dims2 c INTGR4 mpA, nqA ! rows/cols of A held by the processor INTGR4 mpB, nqB ! rows/cols of B held by the processor INTGR4 mps, nqs ! rows/cols of B held by the processor c integer me INTGR4 lda, ldb, lds integer elemA, elemB, elems INTGR4 numroc INTGR4 nb ! block size INTGR4 descA(9), descB(9) !descriptor for scalapack INTGR4 descs(9) c integer ngaps, hgap, adrgaps integer iclu, hclustr, adrclustr integer if, hfail, adrfail integer liwork, hiwork, adriwork integer lcwork, hcwork, adrcwork INTGR4 lcwork4 INTGR4 liwork4 c INTGR4 nn, mq0, np0 double precision vl, vu, abstol, orfac INTGR4 il, iu INTGR4 m, nz INTGR4 n INTGR4 info integer info8,dblsize INTGR4 zero4,one4,two4,four4 parameter(zero4=0,one4=1,two4=2,four4=4) INTGR4 iceil double precision pdlamch integer i logical use_direct logical uses_sl_A, uses_sl_B, uses_sl_S integer alen, blen, slen integer block_dims_A(2),block_dims_B(2),block_dims_S(2),blocks(2) integer gridA(2), gridB(2), gridS(2) external pdlamch external iceil integer j ! intrinsic max c c processor dependent; machine dependent c data nb/16/ c c*** check environment c me = ga_nodeid() c c*** check GA info for input arrays c call ga_check_handle(g_a, 'ga_pdsygv: a') call ga_check_handle(g_b, 'ga_pdsygv: b') call ga_check_handle(g_s, 'ga_pdsygv: s') c call ga_inquire(g_a, typeA, dimA18, dimA28) call ga_inquire(g_b, typeB, dimB18, dimB28) call ga_inquire(g_s, types, dims18, dims28) dima1=dima18 dima2=dima28 dimb1=dimb18 dimb2=dimb28 dims1=dims18 dims2=dims28 if(dimA1.ne.dima2) call ga_error( % 'ga_pdsygv: matrix A not square ',0) n=dima1 if(nb.lt.1) nb=1 if(dimb1.ne.dimb2) call ga_error( % 'ga_pdsygv: matrix B not square ',0) if(dimb1.ne.n) call ga_error( % 'ga_pdsygv: size matrix B and A differ ',0) if(dims1.ne.dims2) call ga_error( % 'ga_pdsygv: matrix S not square ',0) if(dims1.ne.n) call ga_error( % 'ga_pdsygv: size matrix A and S differ ',0) c use_direct = .true. uses_sl_A = ga_uses_proc_grid(g_a) uses_sl_B = .false. uses_sl_S = .false. if (uses_sl_A) then uses_sl_B = ga_uses_proc_grid(g_b) uses_sl_S = ga_uses_proc_grid(g_s) endif if ((.not.uses_sl_A).or.(.not.uses_sl_B)) then use_direct = .false. endif if (uses_sl_A) then call ga_get_block_info(g_a,blocks,block_dims_A) if (block_dims_A(1).ne.block_dims_A(2)) then use_direct = .false. endif endif if (uses_sl_B) then call ga_get_block_info(g_b,blocks,block_dims_B) if (block_dims_B(1).ne.block_dims_B(2)) then use_direct = .false. endif endif if (uses_sl_S) then call ga_get_block_info(g_s,blocks,block_dims_S) if (block_dims_S(1).ne.block_dims_S(2)) then use_direct = .false. endif endif if (use_direct.and.((block_dims_A(1).ne.block_dims_B(1)).or. % (block_dims_B(1).ne.block_dims_S(1)))) then use_direct = .false. endif if (use_direct) then call ga_get_proc_grid(g_a,gridA) call ga_get_proc_grid(g_b,gridB) call ga_get_proc_grid(g_s,gridS) if ((gridA(1).ne.gridB(1).or.gridA(2).ne.gridB(2)).or. % (gridA(1).ne.gridS(1).or.gridA(2).ne.gridS(2))) then use_direct = .false. endif endif if (use_direct) then nb = block_dims_A(2) endif c c*** initialize SL interface c ! call SLinit() if (use_direct) then call SLinit4(g_a) else call SLinit2(n) endif oactive=iam.lt.maxproc call ga_sync if (oactive) then c c*** find SBS format parameters c c mpA = numroc(dimA1, nb, myrow2, zero4, nprow2) nqA = numroc(dimA2, nb, mycol2, zero4, npcol2) c mpB = numroc(dimB1, nb, myrow2, zero4, nprow2) nqB = numroc(dimB2, nb, mycol2, zero4, npcol2) c mps = numroc(dims1, nb, myrow2, zero4, nprow2) nqs = numroc(dims2, nb, mycol2, zero4, npcol2) c c lda = max(one4,mpA) ldb = max(one4,mpB) lds = max(one4,mps) c c c let scalapack check for errors c if (use_direct) then call nga_access_block_segment(g_a, me, adra, alen) call nga_access_block_segment(g_b, me, adrb, blen) call nga_access_block_segment(g_s, me, adrs, slen) else elemA= mpA*nqA status = .true. if(elemA.ne.0) status = $ ma_push_get(MT_DBL,elemA,'a',ha,adra) if(.not.status) & call ga_error('ga_pdsygv: mem alloc failed A ', elema) c elemB= mpB*nqB if(elemB.ne.0)status = $ ma_push_get(MT_DBL,elemB,'b',hb,adrb) if(.not.status) & call ga_error('ga_pdsygv: mem alloc failed B ', -1) elems= mps*nqs if(elems.ne.0)status = $ ma_push_get(MT_DBL,elems,'s',hs,adrs) if(.not.status) & call ga_error('ga_pdsygv: mem alloc failed S ', -1) endif c ngaps = nprow2*npcol2 if(ngaps.ne.0)status = $ ma_push_get(MT_DBL, ngaps ,'gap',hgap,adrgaps) if(.not.status) & call ga_error('ga_pdsygv: mem alloc failed gaps ', -1) c iclu = 2*nprow2*npcol2 iclu = 2*n if(iclu.ne.0)status = $ ma_push_get(MT_INT, iclu ,'iclustr',hclustr,adrclustr) if(.not.status) & call ga_error('ga_pdsygv: mem alloc failed iclustr ', -1) c if = n if(if.ne.0) then status = $ ma_push_get(MT_INT, if ,'ifail',hfail,adrfail) endif if(.not.status) & call ga_error('ga_pdsygv: mem alloc failed ifail ', -1) c c*** copy g_a to A and g_b to B using the block cyclic scalapack format c if (.not.use_direct) then call ga_to_SL2(g_a, dimA1, dimA2, nb, nb, $ dbl_mb(adrA), lda, mpA, nqA) c call ga_to_SL2(g_b, dimB1, dimB2, nb,nb, $ dbl_mb(adrB), ldb, mpB, nqB) call ga_to_SL2(g_s, dims1, dims2, nb,nb, $ dbl_mb(adrs), ldb, mps, nqs) endif c c c*** fill SCALAPACK matrix descriptors c call descinit(descA, dimA1, dimA2, nb, nb, zero4, zero4, $ islctxt2, lda, info) info8=info if(info8.ne.0) call ga_error(' ga_pdsygv: descinit A failed ', . -info8) c call descinit(descB, dimB1, dimB2, nb, nb, zero4, zero4, . islctxt2, ldb, info) info8=info if(info8.ne.0) call ga_error(' ga_pdsygv: descinit B failed ', . -info8) call descinit(descs, dims1, dims2, nb, nb, zero4, zero4, . islctxt2, lds, info) info8=info if(info8.ne.0) call ga_error(' ga_pdsygv: descinit S failed ', . -info8) c c call scalapack c c jobz = 'V' range = 'A' uplo = 'L' vl = 0.d0 vu = 0.d0 il = 0 iu = 0 nz = 0 c c ability to deal with orthonormality ; let's just c have the regular scalapack stuff for the moment c liwork = 6*max(n, nprow2*npcol2+one4, four4) if(liwork.ne.0)status = $ ma_push_get(MT_INT, liwork ,'iwork',hiwork,adriwork) if(.not.status) & call ga_error('ga_pdsygv: mem alloc failed iwork ', -1) c nn = max(n, nb, two4) np0 = numroc(nn, nb, zero4, zero4, nprow2) mq0 = numroc(nn, nb, zero4, zero4, npcol2) c c orfac = 1.d-3 c c lcwork = 5*n +MAX(5*NN,(NP0*MQ0 + 2*nb*nb))+ + ICEIL( N, NPROW2*NPCOL2)*NN+1 c if(lcwork.ne.0) $ status = ma_push_get(MT_DBL, lcwork , $ 'cwork',hcwork,adrcwork) if(.not.status) & call ga_error('ga_pdsygv: mem alloc failed cwork ', -1) c abstol=pdlamch(islctxt2, 'U') c c c liwork4=liwork lcwork4=lcwork call pdsygvx(one4,jobz, range, uplo, 1 n, dbl_mb(adrA), one4, one4, 2 descA, dbl_mb(adrS), one4, one4,descs, vl,vu, il, iu, 3 abstol, m, nz, eval, orfac, dbl_mb(adrB), 4 one4, one4, descB, 4 dbl_mb(adrcwork), lcwork4,int_mb(adriwork), liwork4, 4 int_mb(adrfail), int_mb(adrclustr), 5 dbl_mb(adrgaps), info) c c c c if (nz .ne. n ) then if ( info .ne. 0 ) then if ( info .gt. 0 ) then call ga_error(' ga_pdsygv: argument is illegal ', info) else call ga_error(' ga_pdsygv: eigenvectors failed to converge ', $ info) endif endif endif c c c c*** copy solution matrix back to g_c c c if (.not.use_direct) then call ga_from_SL2(g_b, dimA1, dimB2, $ nb, nb, dbl_mb(adrB), & ldb, mpb, nqB) endif c c c*** deallocate work/SL arrays c if ( lcwork .ne. 0 ) status = ma_pop_stack(hcwork) if ( liwork .ne. 0 ) status = ma_pop_stack(hiwork) if ( if .ne. 0 ) status = ma_pop_stack(hfail) if ( iclu .ne. 0 ) status = ma_pop_stack(hclustr) if ( ngaps.ne.0 ) status = ma_pop_stack(hgap) if (.not.use_direct) then if ( elemS .ne. 0 ) status = ma_pop_stack(hs) if ( elemB .ne. 0 ) status = ma_pop_stack(hb) if ( elemA .ne. 0 ) status = ma_pop_stack(ha) endif endif c call ga_sync() if(maxproc.lt.nnodes.or.dima1.le.nb) then c broadcast evals dblsize=ma_sizeof(MT_DBL,1,MT_BYTE)*dima1 call ga_brdcst(1688,eval,dblsize,0) endif if (use_direct) then call SLexit4 else call SLexit2 endif return end c c interface to pdsyevd (divide&conquer) c subroutine ga_pdsyevd(g_a, g_b, eval, nb8) implicit none #include "mafdecls.fh" #include "global.fh" #include "scalapack.fh" c integer g_a ! matrix A integer g_b ! matrix B integer nb8 c c all eigenvalues are real c double precision eval(*) c c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c logical status c character*1 jobz, uplo c integer ha, adra !A integer hb, adrb !B c c logical oactive ! true iff this process participates integer dimA18, dimA28, typeA integer dimB18, dimB28, typeB INTGR4 dimA1, dimA2 INTGR4 dimB1, dimB2 c INTGR4 mpA, nqA ! rows/cols of A held by the processor INTGR4 mpB, nqB ! rows/cols of B held by the processor c integer me INTGR4 lda, ldb integer elemA,elemB INTGR4 numroc INTGR4 nb ! block size INTGR4 descA(9), descB(9) !descriptor for scalapack c integer liwork,hiwork, adriwork integer lcwork,hcwork, adrcwork INTGR4 liwork4,lcwork4 c INTGR4 nn,nq, np,trilwmin,iarow,iacol INTGR4 n INTGR4 info,one4,zero4,two4 parameter(zero4=0,one4=1,two4=2) integer info8,dblsize double precision pdlamch,dum INTGR4 iceil,indxg2p logical uses_sl_A, uses_sl_B integer alen, blen integer block_dims_A(2),block_dims_B(2),blocks(2) integer gridA(2), gridB(2) double precision mywork logical use_direct external pdlamch,iceil,indxg2p c c processor dependent; machine dependent c if(nb8.eq.0) then nb=16 else nb=nb8 endif c c*** check environment c me = ga_nodeid() c c*** check GA info for input arrays c call ga_check_handle(g_a, 'ga_pdsyevd: a') call ga_check_handle(g_b, 'ga_pdsyevd: b') c call ga_inquire(g_a, typeA, dimA18, dimA28) call ga_inquire(g_b, typeB, dimB18, dimB28) dima1=dima18 dima2=dima28 dimb1=dimb18 dimb2=dimb28 n=dima1 if(nb.lt.1) nb=1 if(dimA1.ne.dima2) call ga_error( % 'ga_pdsyevd: matrix A not square ',0) if(dimb1.ne.dimb2) call ga_error( % 'ga_pdsyevd: matrix B not square ',0) if(dimb1.ne.n) call ga_error( % 'ga_pdsyevd: size matrix B and B differ ',0) use_direct = .true. uses_sl_A = ga_uses_proc_grid(g_a) uses_sl_B = .false. if (uses_sl_A) then uses_sl_B = ga_uses_proc_grid(g_b) endif if ((.not.uses_sl_A).or.(.not.uses_sl_B)) then use_direct = .false. endif if (uses_sl_A) then call ga_get_block_info(g_a,blocks,block_dims_A) if (block_dims_A(1).ne.block_dims_A(2)) then use_direct = .false. endif endif if (uses_sl_B) then call ga_get_block_info(g_b,blocks,block_dims_B) if (block_dims_B(1).ne.block_dims_B(2)) then use_direct = .false. endif endif if (use_direct.and.(block_dims_A(1).ne.block_dims_B(1))) then use_direct = .false. endif if (use_direct) then call ga_get_proc_grid(g_a,gridA) call ga_get_proc_grid(g_b,gridB) if (gridA(1).ne.gridB(1).or.gridA(2).ne.gridB(2)) then use_direct = .false. endif endif if (use_direct) then nb = block_dims_A(2) endif c c c*** initialize SL interface c ! call SLinit() if (use_direct) then call SLinit4(g_a) else call SLinit2(n) if(g_a.ne.g_b) then if(.not.ga_deallocate(g_b)) E call ga_error(' ga_pdsyevd: ga_deallocate failed',0) endif endif oactive=iam.lt.maxproc call ga_sync if (oactive) then c c*** find SBS format parameters c c mpA = numroc(dimA1, nb, myrow2, zero4, nprow2) nqA = numroc(dimA2, nb, mycol2, zero4, npcol2) c mpB = numroc(dimB1, nb, myrow2, zero4, nprow2) nqB = numroc(dimB2, nb, mycol2, zero4, npcol2) c c lda = max(one4,mpA) ldb = max(one4,mpB) c c c let scalapack check for errors c c should check to see if this is a compute node c check to see how this works in the new data server model c c c if (use_direct) then call nga_access_block_segment(g_a,me,adra,alen) call nga_access_block_segment(g_b,me,adrb,blen) else elemA= mpA*nqA status = .true. if(elemA.ne.0)status = $ ma_push_get(MT_DBL,elemA,'a',ha,adra) if(.not.status) & call ga_error('ga_pdsyevd: mem alloc failed A ', -1) c c*** copy g_a to A and g_b to B using the block cyclic scalapack format c call ga_to_SL2(g_a, dimA1, dimA2, nb, nb, $ dbl_mb(adrA), lda, mpA, nqA) c elemB= mpB*nqB c if(elemB.ne.0)status = $ ma_push_get(MT_DBL,elemB,'b',hb,adrb) if(.not.status) & call ga_error('ga_pdsyevd: mem alloc failed B ', -1) endif endif if(g_a.eq.g_b) then if(.not.ga_deallocate(g_b)) E call ga_error(' ga_pdsyevd: ga_deallocate failed',0) endif call ga_sync() if(oactive) then c c c c*** fill SCALAPACK matrix descriptors c call descinit(descA, dimA1, dimA2, nb, nb, zero4, zero4, $ islctxt2, lda, info) info8=info if(info8.ne.0) call ga_error(' ga_pdsyevd: descinit A failed ', . -info8) c call descinit(descB, dimB1, dimB2, nb, nb, zero4, zero4, . islctxt2, ldb, info) info8=info if(info8.ne.0) call ga_error(' ga_pdsyevd: descinit B failed ', . -info8) c jobz = 'V' uplo = 'L' c c ability to deal with orthonormality ; let's just c have the regular scalapack stuff for the moment c liwork = 7*n + 8*npcol2 +2 if(liwork.ne.0)status = $ ma_push_get(MT_INT, liwork ,'iwork',hiwork,adriwork) if(.not.status) & call ga_error('ga_pdsyevd: mem alloc failed iwork ', -1) c nn = max(n, nb, two4) IAROW = INDXG2P(one4, nb, MYROW2, DESCA( 7 ), NPROW2 ) IACOL = INDXG2P(one4, nb, MYCOL2, DESCA( 8 ), NPCOL2 ) np = numroc(n, nb, myrow2, iarow, nprow2) nq = numroc(n, nb, mycol2, iacol, npcol2) c c c liwork4=liwork lcwork4=-1 call pdsyevd(jobz, uplo, 1 n, dbl_mb(adrA), one4, one4, descA, 1 eval, dbl_mb(adrB), one4, one4, 2 descB, mywork, lcwork4, 2 int_mb(adriwork), liwork4, info) lcwork = MAX( 1+6*N+2*NP*NQ, = 3*N + MAX( NB*( NP+1 ), 3*NB ))+ 2*N lcwork=max(lcwork,16384) lcwork=max(int(mywork),lcwork) c if(lcwork.ne.0) $ status = ma_push_get(MT_DBL, lcwork , $ 'cwork',hcwork,adrcwork) if(.not.status) & call ga_error('ga_pdsyevd: mem alloc failed cwork ', -1) c c c lcwork4=lcwork call pdsyevd(jobz, uplo, 1 n, dbl_mb(adrA), one4, one4, descA, 1 eval, dbl_mb(adrB), one4, one4, 2 descB, dbl_mb(adrcwork), lcwork4, 2 int_mb(adriwork), liwork4, info) c if ( info .ne. 0 ) then if ( info .gt. 0 ) then call ga_error(' ga_pdsyevd: argument is illegal ', info) else call ga_error( $ ' ga_pdsyevd: eigenvectors failed to converge ', $ info) endif endif c c c c*** copy solution matrix back to g_b c endif !oactive if (.not.use_direct) then if(.not.ga_allocate(g_b)) E call ga_error(' ga_pdsyevd: ga_allocate failed',0) call ga_zero(g_b) if (oactive) then call ga_from_SL2(g_b, dimA1, dimB2, $ nb, nb, dbl_mb(adrB), & ldb, mpb, nqB) endif endif if (oactive) then c c c c*** deallocate work/SL arrays c if ( lcwork .ne. 0 ) status = ma_pop_stack(hcwork) if ( liwork .ne. 0 ) status = ma_pop_stack(hiwork) if (.not.use_direct) then if ( elemB .ne. 0 ) status = ma_pop_stack(hb) if ( elemA .ne. 0 ) status = ma_pop_stack(ha) endif c endif ! oactive call ga_sync() if(maxproc.lt.nnodes.or.dima1.le.nb) then c broadcast evals dblsize=ma_sizeof(MT_DBL,1,MT_BYTE)*dima1 call ga_brdcst(1688,eval,dblsize,0) endif return end #if HAVE_PDSYEVR subroutine ga_pdsyevr(g_a, g_b, eval, nb8, mout) implicit none #include "mafdecls.fh" #include "global.fh" #include "scalapack.fh" c integer g_a ! matrix A integer g_b ! matrix B integer nb8 integer mout ! no. of evals/evecs to compute c c all eigenvalues are real c double precision eval(*) c c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c logical status c character*1 jobz, range, uplo c integer ha, adra !A integer hb, adrb !B c c logical oactive ! true if this process participates integer dimA18, dimA28, typeA integer dimB18, dimB28, typeB INTGR4 dimA1, dimA2,mout4 INTGR4 dimB1, dimB2 c INTGR4 mpA, nqA ! rows/cols of A held by the processor INTGR4 mpB, nqB ! rows/cols of B held by the processor c integer me INTGR4 lda, ldb integer elemA,elemB INTGR4 numroc INTGR4 nb ! block size INTGR4 descA(9), descB(9) !descriptor for scalapack c integer n8, hfail, adrfail integer liwork,hiwork, adriwork integer lcwork,hcwork, adrcwork INTGR4 liwork4 INTGR4 lcwork4 c INTGR4 nn,mq0, np0 double precision vl, vu INTGR4 il, iu INTGR4 m, nz INTGR4 n INTGR4 info,one4,zero4,two4,four4 parameter(one4=1,zero4=0,two4=4,four4=4) integer info8,dblsize double precision pdlamch,dumm INTGR4 dumm2 external pdlamch INTGR4 iceil c c processor dependent; machine dependent c if(nb8.eq.0) then nb=16 else nb=nb8 endif c c*** check environment c me = ga_nodeid() c c*** check GA info for input arrays c call ga_check_handle(g_a, 'ga_pdsyevr: a') call ga_check_handle(g_b, 'ga_pdsyevr: b') c call ga_inquire(g_a, typeA, dimA18, dimA28) call ga_inquire(g_b, typeB, dimB18, dimB28) dima1=dima18 dima2=dima28 dimb1=dimb18 dimb2=dimb28 if(dimA1.ne.dima2) call ga_error( % 'ga_pdsyevr: matrix A not square ',0) n=dima1 if(dimb1.ne.dimb2) call ga_error( % 'ga_pdsyevr: matrix B not square ',0) if(dimb1.ne.n) call ga_error( % 'ga_pdsyevr: size matrix B and B differ ',0) if(mout.le.0.or.mout.gt.n) mout=n c c c*** initialize SL interface c call SLinit2(n) oactive=iam.lt.maxproc if(g_a.ne.g_b) then if(.not.ga_deallocate(g_b)) E call ga_error(' ga_pdsyevr: ga_deallocate failed',0) endif if (oactive) then c c*** find SBS format parameters c c mpA = numroc(dimA1, nb, myrow2, zero4, nprow2) nqA = numroc(dimA2, nb, mycol2, zero4, npcol2) mpa=max(one4,mpa) nqa=max(one4,nqa) c mpB = numroc(dimB1, nb, myrow2, zero4, nprow2) nqB = numroc(dimB2, nb, mycol2, zero4, npcol2) mpb=max(one4,mpb) nqb=max(one4,nqb) c c lda = max(one4,mpA) ldb = max(one4,mpB) c c c let scalapack check for errors c c should check to see if this is a compute node c check to see how this works in the new data server model c c elemA= mpA*nqA status = .true. if(elemA.ne.0)status = $ ma_push_get(MT_DBL,elemA,'a',ha,adra) if(.not.status) & call ga_error('ga_pdsyevr: mem alloc failed A ', -1) c c*** copy g_a to A and g_b to B using the block cyclic scalapack format c call ga_to_SL2(g_a, dimA1, dimA2, nb, nb, $ dbl_mb(adrA), lda, mpA, nqA) else mpb=1 nqb=1 endif ! oactive if(g_a.eq.g_b) then if(.not.ga_deallocate(g_b)) E call ga_error(' ga_pdsyevr: ga_deallocate failed',0) endif call ga_sync() if(oactive) then c elemB= mpB*nqB c status=.true. if(elemB.ne.0)status = $ ma_push_get(MT_DBL,elemB,'b',hb,adrb) if(.not.status) & call ga_error('ga_pdsyevr: mem alloc failed B ', -1) c n8 = n if(n8.ne.0) then status = $ ma_push_get(MT_INT, n8 ,'ifail',hfail,adrfail) endif if(.not.status) & call ga_error('ga_pdsyevr: mem alloc failed ifail ', n) c c*** fill SCALAPACK matrix descriptors c call descinit(descA, dimA1, dimA2, nb, nb, zero4, zero4, $ islctxt2, lda, info) info8=info if(info.ne.0) call ga_error(' ga_pdsyevr: descinit A failed ', . -info8) c call descinit(descB, dimB1, dimB2, nb, nb, zero4, zero4, . islctxt2, ldb, info) info8=info if(info.ne.0) call ga_error(' ga_pdsyevr: descinit B failed ', . -info8) c jobz = 'V' uplo = 'L' vl = 0.d0 vu = 0.d0 c nn = max(n, nb, two4) np0 = numroc(nn, nb, zero4, zero4, nprow2) mq0 = numroc(nn, nb, zero4, zero4, npcol2) c il=1 iu=mout range='i' lcwork4=-1 liwork4=-1 call pdsyevr(jobz, range, uplo, 1 n, dbl_mb(adrA), one4, one4, descA,vl, 2 vu, il, iu, m, nz, eval, dbl_mb(adrB), one4, 3 one4, descB, dumm, lcwork4, 3 dumm2, liwork4, info) lcwork=dumm liwork=dumm2 c lcwork = 5*n +MAX(18*NN,(NP0*MQ0 + 2*nb*nb))+ c + (2 + ICEIL( N, NPROW2*NPCOL2))*NN+1 c liwork = 12*max(n, nprow2*npcol2+one4, four4) +2 *n c if(liwork.ne.0)status = $ ma_push_get(MT_INT, liwork ,'iwork',hiwork,adriwork) if(.not.status) & call ga_error('ga_pdsyevr: mem alloc failed iwork ', -1) c if(lcwork.ne.0) $ status = ma_push_get(MT_DBL, lcwork , $ 'cwork',hcwork,adrcwork) if(.not.status) & call ga_error('ga_pdsyevr: mem alloc failed cwork ', -1) c liwork4=liwork lcwork4=lcwork call pdsyevr(jobz, range, uplo, 1 n, dbl_mb(adrA), one4, one4, descA,vl, 2 vu, il, iu, m, nz, eval, dbl_mb(adrB), one4, 3 one4, descB, dbl_mb(adrcwork), lcwork4, 3 int_mb(adriwork), liwork4, info) c if(mout.ne.m) then if(iam.eq.0) W write(6,*) ' eval search failed: got',m, W ' instead of ',mout info=-999 endif mout=m if(iam.eq.0.and.mout.ne.n) W write(6,*) ' computed eval ',mout,' out of ', A dima1 if (nz .ne. n ) then if ( info .ne. 0 ) then if ( info .gt. 0 ) then call ga_error(' ga_pdsyevr: argument is illegal ', info) else call ga_error(' ga_pdsyevr: eigenvectors failed to converge ', $ info) endif endif endif c c c c*** copy solution matrix back to g_b c mout4=mout endif ! oactive if(.not.ga_allocate(g_b)) E call ga_error(' ga_pdsyevr: ga_allocate failed',0) call ga_zero(g_b) if (oactive) then call ga_from_SL2(g_b, dimA1, mout4, $ nb, nb, dbl_mb(adrB), & ldb, mpb, nqB) c c c c*** deallocate work/SL arrays c if ( lcwork .ne. 0 ) status = ma_pop_stack(hcwork) if ( liwork .ne. 0 ) status = ma_pop_stack(hiwork) if ( n8 .ne. 0 ) status = ma_pop_stack(hfail) if ( elemB .ne. 0 ) status = ma_pop_stack(hb) if ( elemA .ne. 0 ) status = ma_pop_stack(ha) c endif ! oactive call ga_sync() if(maxproc.lt.nnodes.or.dima1.le.nb) then c broadcast evals dblsize=ma_sizeof(MT_DBL,1,MT_BYTE)*dima1 call ga_brdcst(1688,eval,dblsize,0) endif return end #endif #if HAVE_ELPA subroutine ga_evp_real(g_a, g_b, eval, nb8, mout) #if HAVE_ELPA_2017 #ifndef USE_ALLOCATE #define USE_ALLOCATE 1 #endif use elpa #else use ELPA1 #if HAVE_ELPA_2015 || HAVE_ELPA_2016 use ELPA2 #endif #endif implicit none c#include "mpif.h" #include "mafdecls.fh" #include "global.fh" #include "scalapack.fh" c integer g_a ! matrix A integer g_b ! matrix B integer nb8 integer mout ! no. of evals/evecs to compute c c all eigenvalues are real c double precision eval(*) c c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c logical status c character*1 jobz, range, uplo c integer ha, adra !A integer hb, adrb !B c c logical oactive ! true iff this process participates integer dimA18, dimA28, typeA integer dimB18, dimB28, typeB INTGR4 dimA1, dimA2,mout4 INTGR4 dimB1, dimB2 c INTGR4 mpA, nqA ! rows/cols of A held by the processor INTGR4 mpB, nqB ! rows/cols of B held by the processor c integer me INTGR4 lda, ldb integer elemA,elemB INTGR4 numroc INTGR4 nb ! block size INTGR4 descA(9), descB(9) !descriptor for scalapack INTGR4 elpa_comm_rows, elpa_comm_cols c integer n8 c INTGR4 m INTGR4 n INTGR4 info,one4,zero4,two4,four4 integer ga_comm #if HAVE_ELPA_2017 integer(kind=c_int) :: api_version class(elpa_t), pointer :: e ! ELPA instance #endif #ifdef USE_ALLOCATE c elpa does not like MA for matrices real(kind=C_DOUBLE), allocatable :: a(:,:) real(kind=C_DOUBLE), allocatable :: b(:,:) real(kind=C_DOUBLE), allocatable :: ev(:) #endif integer(kind=c_int) :: stat_alloc integer mxproc,i parameter(mxproc=100000) c mpi args should integer*4 integer*4 group_world,group_members(mxproc), C ga_comm4,mpierr,elpa_group INTGR4 elpa_comm integer*4 elpa_err parameter(one4=1,zero4=0,two4=4,four4=4) integer info8,dblsize integer*4 n4 c c processor dependent; machine dependent c if(maxproc.gt.mxproc) call ga_error( A 'ga-evp_real: increase mxproc to ',maxproc) if(nb8.eq.0) then nb=16 else nb=nb8 endif c c*** check environment c me = ga_nodeid() c c*** check GA info for input arrays c call ga_check_handle(g_a, 'ga_evp_real: a') call ga_check_handle(g_b, 'ga_evp_real: b') c call ga_inquire(g_a, typeA, dimA18, dimA28) call ga_inquire(g_b, typeB, dimB18, dimB28) dima1=dima18 dima2=dima28 dimb1=dimb18 dimb2=dimb28 if(dimA1.ne.dima2) call ga_error( % 'ga_evp_real: matrix A not square ',0) n=dima1 if(dimb1.ne.dimb2) call ga_error( % 'ga_evp_real: matrix B not square ',0) if(dimb1.ne.n) call ga_error( % 'ga_evp_real: size matrix B and B differ ',0) if(mout.le.0.or.mout.gt.n) mout=n mout4=mout c c c*** initialize SL interface c call SLinit2(n) oactive=iam.lt.maxproc mpA = numroc(dimA1, nb, myrow2, zero4, nprow2) nqA = numroc(dimA2, nb, mycol2, zero4, npcol2) if(g_a.ne.g_b) then if(.not.ga_deallocate(g_b)) E call ga_error(' ga_evp_real: ga_deallocate failed',0) endif celpa call ga_mpi_comm_pgroup_default(ga_comm) ga_comm4=ga_comm call MPI_Comm_group(ga_comm4, group_world, mpierr) do i=1,maxproc group_members(i)=i-1 enddo call MPI_Group_incl(group_world, maxproc, group_members, C elpa_group, mpierr) if(mpierr.ne.0) call ga_error( A 'ga-evp_real: mpigroupincl failed ',0) call mpi_comm_create(ga_comm4, elpa_group, elpa_comm, W mpierr) call ga_sync() if (oactive) then c c*** find SBS format parameters c c mpA = numroc(dimA1, nb, myrow2, zero4, nprow2) nqA = numroc(dimA2, nb, mycol2, zero4, npcol2) mpa=max(one4,mpa) nqa=max(one4,nqa) c mpB = numroc(dimB1, nb, myrow2, zero4, nprow2) nqB = numroc(dimB2, nb, mycol2, zero4, npcol2) mpb=max(one4,mpb) nqb=max(one4,nqb) c c lda = max(one4,mpA) ldb = max(one4,mpB) c c c let scalapack check for errors c c should check to see if this is a compute node c check to see how this works in the new data server model c c elemA= mpA*nqA status = .true. if(elemA.ne.0) then #ifdef USE_ALLOCATE allocate(ev(mout),STAT = stat_alloc) if(stat_alloc.ne.0) & call ga_error('ga_evp_real: mem alloc failed ev ', S stat_alloc) allocate(a(mpa,nqa),STAT = stat_alloc) status=stat_alloc.eq.0 #else status = $ ma_push_get(MT_DBL,elemA,'a',ha,adra) stat_alloc=0 #endif endif if(.not.status) & call ga_error('ga_evp_real: mem alloc failed A ', S stat_alloc) c elemb= mpb*nqb c if(elemB.ne.0) then #ifdef USE_ALLOCATE allocate(b(mpb,nqb),STAT = stat_alloc) status=stat_alloc.eq.0 #else $ status = $ ma_push_get(MT_DBL,elemB,'b',hb,adrb) #endif endif if(.not.status) then write(6,*) ' B alloc failed for mpb,nqb ',mpb,nqb,elemb call ga_error('ga_evp_real: mem alloc failed B ', S stat_alloc) endif c c n4=n8 n8 = n c c*** copy g_a to A and g_b to B using the block cyclic scalapack format c call ga_to_SL2(g_a, dimA1, dimA2, nb, nb, #if USE_ALLOCATE $ a, #else $ dbl_mb(adrA), #endif A lda, mpA, nqA) endif !end of oactive if(g_a.eq.g_b) then if(.not.ga_deallocate(g_b)) E call ga_error(' ga_evp_real: ga_deallocate failed',0) endif call ga_sync() if(oactive) then c c*** fill SCALAPACK matrix descriptors c call descinit(descA, dimA1, dimA2, nb, nb, zero4, zero4, $ islctxt2, lda, info) info8=info if(info.ne.0) call ga_error(' ga_evp_real: descinit A failed ', . -info8) c call descinit(descB, dimB1, dimB2, nb, nb, zero4, zero4, . islctxt2, ldb, info) info8=info if(info.ne.0) call ga_error(' ga_evp_real: descinit B failed ', . -info8) c c #if HAVE_ELPA_2017 api_version=20170403 if (elpa_init(api_version) .ne. 0) then ! put here the version number of the API call ga_error('ELPA API version not supported',0) ! which you are using endif e => elpa_allocate(elpa_err) if(elpa_err.ne.0) call ga_error( A 'ga-evp_real: elpa_allocate failed ',elpa_err) call e%set("na", int(n,kind=c_int),elpa_err) ! size of matrix if(elpa_err.ne.0) call ga_error( A 'ga-evp_real: e%set na failed ',elpa_err) call e%set("local_nrows", int(mpa,kind=c_int),elpa_err) ! MPI process local rows of the distributed matrixdo the ! desired task with the *ELPA* library, which could be if(elpa_err.ne.0) call ga_error( A 'ga-evp_real: e%set local_nrows failed ',elpa_err) call e%set("local_ncols", int(nqa,kind=c_int),elpa_err) ! MPI process local cols of the distributed matrix call e%set("nblk", int(nb,kind=c_int), elpa_err) ! size of block-cylic distribution if(elpa_err.ne.0) call ga_error( A 'ga-evp_real: e%set local_ncols failed ',elpa_err) call e%set("mpi_comm_parent", int(elpa_comm,kind=c_int),elpa_err) ! global communicator for all processes which have parts of if(elpa_err.ne.0) call ga_error( A 'ga-evp_real: e%set mpi_comm failed ',elpa_err) ! the distributed matrix call e%set("process_row", int(myrow2,kind=c_int), elpa_err) ! row coordinate of MPI task if(elpa_err.ne.0) call ga_error( A 'ga-evp_real: e%set process_row failed ',elpa_err) call e%set("process_col", int(mycol2,kind=c_int), elpa_err) ! column coordinate of MPI task if(elpa_err.ne.0) call ga_error( A 'ga-evp_real: e%set process_col failed ',elpa_err) call e%set("nev", int(mout4,kind=c_int), elpa_err) if(elpa_err.ne.0) call ga_error( A 'ga-evp_real: e%set nev failed ',elpa_err) elpa_err = e%setup() if(elpa_err.ne.ELPA_OK) call ga_error( A 'ga-evp_real: e%setup failed ',elpa_err) #ifdef ELPA_SOLVER_2STAGE call e%set("solver", ELPA_SOLVER_2STAGE, elpa_err) ! set solver to 2stage if(elpa_err.ne.ELPA_OK) call ga_error( A 'ga-evp_real: e%set solver failed ',elpa_err) #endif #else #if HAVE_ELPA_2015 || HAVE_ELPA_2016 elpa_err=get_elpa_communicators (elpa_comm, #else elpa_err=get_elpa_row_col_comms(elpa_comm, #endif V myrow2, mycol2, V elpa_comm_rows, elpa_comm_cols) if(elpa_err.ne.0) call ga_error( A 'ga-evp_real: get_elpa_row_col failed ',elpa_err) #endif #if HAVE_ELPA_2017 #if USE_ALLOCATE call e%eigenvectors(a, ev, b, elpa_err) #else call ga_elpa_eigenvectors(e,mpa, D dbl_mb(adra),eval,dbl_mb(adrb),elpa_err) #endif if(elpa_err.ne.0) #else #if HAVE_ELPA_2016 if(.not.solve_evp_real_2stage_double( #elif HAVE_ELPA_2015 if(.not.solve_evp_real_2stage( #endif #if HAVE_ELPA_2016 || HAVE_ELPA_2015 C n, mout4, A dbl_mb(adra), lda, E eval, dbl_mb(adrb), ldb, nb, C nqa, A elpa_comm_rows, elpa_comm_cols, F elpa_comm)) #else if(.not.solve_evp_real(n, mout4, A dbl_mb(adra), lda, E eval, dbl_mb(adrb), ldb, nb, A elpa_comm_rows, elpa_comm_cols)) #endif #endif E call ga_error(' solve_evp_real failed',0) if(iam.eq.0.and.mout4.ne.n) W write(6,*) ' computed eval ',mout,' out of ', A dima1 c c c c*** copy solution matrix back to g_b c endif ! oactive if(.not.ga_allocate(g_b)) E call ga_error(' solve_evp_real: ga_allocate failed',0) call ga_zero(g_b) if (oactive) then call ga_from_SL2(g_b, dimA1, mout4, $ nb, nb, #if USE_ALLOCATE $ b, #else D dbl_mb(adrB), #endif & ldb, mpb, nqB) c c c c*** deallocate work/SL arrays c if ( elemB .ne. 0 ) then #ifdef USE_ALLOCATE deallocate(b,STAT = stat_alloc) status=stat_alloc.eq.0 #else status = ma_pop_stack(hb) #endif endif if(.not.status) D call ga_error('ga_evp_real: dealloc failed B ', D stat_alloc) if ( elemA .ne. 0 ) then #ifdef USE_ALLOCATE call dcopy(mout4,ev,1,eval,1) deallocate(ev,STAT = stat_alloc) deallocate(a,STAT = stat_alloc) status=stat_alloc.eq.0 #else status = ma_pop_stack(ha) #endif endif if(.not.status) D call ga_error('ga_evp_real: dealloc failed A ', D stat_alloc) #if HAVE_ELPA_2017 call elpa_deallocate(e,elpa_err) if(elpa_err.ne.0) call ga_error( A 'ga-evp_real: elpa_deallocate failed ',elpa_err) call elpa_uninit() #endif call MPI_comm_free( elpa_comm, mpierr) if(mpierr.ne.0) call ga_error( A 'ga-evp_real: mpicommfree failed ',mpierr) endif ! oactive call MPI_Group_free( elpa_group, mpierr) if(mpierr.ne.0) call ga_error( A 'ga-evp_real: mpigroupfree failed ',mpierr) call ga_sync() if(maxproc.lt.nnodes.or.dima1.le.nb) then c broadcast evals dblsize=ma_sizeof(MT_DBL,1,MT_BYTE)*dima1 call ga_brdcst(1688,eval,dblsize,0) endif return end #if HAVE_ELPA_2017 #ifndef USE_ALLOCATE subroutine ga_elpa_eigenvectors(e,lda,a,eval,b,elpa_err) use elpa implicit none class(elpa_t), pointer :: e ! ELPA instance INTGR4 lda double precision a(lda,*) double precision b(lda,*) double precision eval(*) integer*4 elpa_err c call e%eigenvectors(a, eval, b, elpa_err) return end #endif #endif c endif for HAS_ELPA #endif subroutine ga_pzheevd(g_a, g_b, eval, nb8) implicit none #include "mafdecls.fh" #include "global.fh" #include "scalapack.fh" c integer g_a(2) ! matrix A integer g_b(2) ! matrix B integer nb8 c c real eigenvalues c double precision eval(*) c c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c logical status c character*1 jobz, uplo c integer ha, adra !A integer hb, adrb !B c c logical oactive ! true iff this process participates integer dimA18, dimA28, typeA integer dimB18, dimB28, typeB INTGR4 dimA1, dimA2,mout4 INTGR4 dimB1, dimB2 c INTGR4 mpA, nqA ! rows/cols of A held by the processor INTGR4 mpB, nqB ! rows/cols of B held by the processor c integer me INTGR4 lda, ldb integer elemA,elemB INTGR4 numroc INTGR4 nb ! block size INTGR4 descA(9), descB(9) !descriptor for scalapack c integer n8, hfail, adrfail integer liwork,hiwork, adriwork integer lwork,hwork, adrwork integer lrwork,hrwork, adrrwork INTGR4 liwork4 INTGR4 lwork4 INTGR4 lrwork4 c INTGR4 m, nz INTGR4 n INTGR4 info,one4,zero4,two4,four4 parameter(one4=1,zero4=0,two4=4,four4=4) integer info8,dblsize double precision pdlamch complex*16 dumm double precision dumm1 INTGR4 dumm2 external pdlamch INTGR4 iceil c c processor dependent; machine dependent c if(nb8.eq.0) then nb=16 else nb=nb8 endif c c*** check environment c me = ga_nodeid() c c*** check GA info for input arrays c call ga_check_handle(g_a(1), 'ga_pzheevd: a') call ga_check_handle(g_b(1), 'ga_pzheevd: b') c call ga_inquire(g_a(1), typeA, dimA18, dimA28) call ga_inquire(g_b(1), typeB, dimB18, dimB28) dima1=dima18 dima2=dima28 dimb1=dimb18 dimb2=dimb28 if(dimA1.ne.dima2) call ga_error( % 'ga_pzheevd: matrix A not square ',0) n=dima1 c nb=min(nb,n/2) if(dimb1.ne.dimb2) call ga_error( % 'ga_pzheevd: matrix B not square ',0) if(dimb1.ne.n) call ga_error( % 'ga_pzheevd: size matrix B and B differ ',0) c c*** initialize SL interface c call SLinit2(n) oactive=iam.lt.maxproc call ga_sync if (oactive) then c c*** find SBS format parameters c c mpA = numroc(dimA1, nb, myrow2, zero4, nprow2) nqA = numroc(dimA2, nb, mycol2, zero4, npcol2) c mpB = numroc(dimB1, nb, myrow2, zero4, nprow2) nqB = numroc(dimB2, nb, mycol2, zero4, npcol2) c c lda = max(one4,mpA) ldb = max(one4,mpB) c c c let scalapack check for errors c c should check to see if this is a compute node c check to see how this works in the new data server model c c elemA= mpA*nqA status = .true. if(elemA.ne.0)status = $ ma_push_get(MT_dcpL,elemA,'a',ha,adra) if(.not.status) & call ga_error('ga_pzheevd: mem alloc failed A ', -1) c c*** copy g_a to A and g_b to B using the block cyclic scalapack format c call ga_to_SL2cpl(g_a, dimA1, dimA2, nb, nb, $ dcpl_mb(adrA), lda, mpA, nqA) endif call ga_sync() if(oactive) then c elemB= mpB*nqB c if(elemB.ne.0)status = $ ma_push_get(MT_dcpL,elemB,'b',hb,adrb) if(.not.status) & call ga_error('ga_pzheevd: mem alloc failed B ', -1) c n8 = n if(n8.ne.0) then status = $ ma_push_get(MT_INT, n8 ,'ifail',hfail,adrfail) endif if(.not.status) & call ga_error('ga_pzheevd: mem alloc failed ifail ', n) c c*** fill SCALAPACK matrix descriptors c call descinit(descA, dimA1, dimA2, nb, nb, zero4, zero4, $ islctxt2, lda, info) info8=info if(info.ne.0) call ga_error(' ga_pzheevd: descinit A failed ', . -info8) c call descinit(descB, dimB1, dimB2, nb, nb, zero4, zero4, . islctxt2, ldb, info) info8=info if(info.ne.0) call ga_error(' ga_pzheevd: descinit B failed ', . -info8) c jobz = 'V' uplo = 'L' c c lwork4=-1 lrwork4=-1 liwork4=-1 call pzheevd(jobz, uplo, n, dcpl_mb(adrA), one4, one4, descA, 2 eval, dcpl_mb(adrB), one4, one4, 3 descB, dumm, lwork4, dumm1,lrwork4, dumm2, 4 liwork4, info) lwork=dumm c lwork=max(lwork,n) c lwork=2*max(lwork,mpa*(nb+nqa)) c write(0,*) ga_nodeid(), ' lw orig new ',dumm,lwork lrwork=dumm1 lrwork=max(lrwork,lwork) liwork=dumm2 c if(liwork.ne.0)status = $ ma_push_get(MT_INT, liwork ,'iwork',hiwork,adriwork) if(.not.status) & call ga_error('ga_pzheevd: mem alloc failed iwork ', -1) c if(lwork.ne.0) $ status = ma_push_get(MT_DCPL, lwork , $ 'work',hwork,adrwork) if(.not.status) & call ga_error('ga_pzheevd: mem alloc failed work ', -1) if(lrwork.ne.0) $ status = ma_push_get(MT_DBL, lrwork , $ 'rwork',hrwork,adrrwork) if(.not.status) & call ga_error('ga_pzheevd: mem alloc failed rwork ', -1) c liwork4=liwork lwork4=lwork lrwork4=lrwork call pzheevd(jobz, uplo, n, dcpl_mb(adrA),one4,one4, descA, 2 eval, dcpl_mb(adrB), one4, one4, 3 descB, dcpl_mb(adrwork), lwork4, dbl_mb(adrrwork), 4 lrwork4, int_mb(adriwork), liwork4, info) if (nz .ne. n ) then if ( info .ne. 0 ) then if ( info .gt. 0 ) then call ga_error(' ga_pzheevd: argument is illegal ', info) else call ga_error(' ga_pzheevd: eigenvectors failed to converge ', $ info) endif endif endif c c c c*** copy solution matrix back to g_b c call ga_from_SL2cpl(g_b, dimA1, dima1, $ nb, nb, dcpl_mb(adrB), & ldb, mpb, nqB) c c c c*** deallocate work/SL arrays c if ( lrwork .ne. 0 ) status = ma_pop_stack(hrwork) if ( lwork .ne. 0 ) status = ma_pop_stack(hwork) if ( liwork .ne. 0 ) status = ma_pop_stack(hiwork) if ( n8 .ne. 0 ) status = ma_pop_stack(hfail) if ( elemB .ne. 0 ) status = ma_pop_stack(hb) if ( elemA .ne. 0 ) status = ma_pop_stack(ha) c endif call ga_sync() if(maxproc.lt.nnodes.or.dima1.le.nb) then c broadcast evals dblsize=ma_sizeof(MT_DBL,1,MT_BYTE)*dima1 call ga_brdcst(1688,eval,dblsize,0) endif return end #if HAVE_PDSYEVR subroutine ga_pzheevr(g_a, g_b, eval, nb8, mout) implicit none #include "mafdecls.fh" #include "global.fh" #include "scalapack.fh" c integer g_a(2) ! matrix A integer g_b(2) ! matrix B integer nb8 integer mout ! no. of evals/evecs to compute c c real eigenvalues c double precision eval(*) c c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ c logical status c character*1 jobz, range, uplo c integer ha, adra !A integer hb, adrb !B c c logical oactive ! true iff this process participates integer dimA18, dimA28, typeA integer dimB18, dimB28, typeB INTGR4 dimA1, dimA2,mout4 INTGR4 dimB1, dimB2 c INTGR4 mpA, nqA ! rows/cols of A held by the processor INTGR4 mpB, nqB ! rows/cols of B held by the processor c integer me INTGR4 lda, ldb integer elemA,elemB INTGR4 numroc INTGR4 nb ! block size INTGR4 descA(9), descB(9) !descriptor for scalapack c integer n8, hfail, adrfail integer liwork,hiwork, adriwork integer lwork,hwork, adrwork integer lrwork,hrwork, adrrwork INTGR4 liwork4 INTGR4 lwork4 INTGR4 lrwork4 c double precision vl, vu INTGR4 il, iu INTGR4 m, nz INTGR4 n INTGR4 info,one4,zero4,two4,four4 parameter(one4=1,zero4=0,two4=4,four4=4) integer info8,dblsize double precision pdlamch complex*16 dumm double precision dumm1 INTGR4 dumm2 external pdlamch INTGR4 iceil c c processor dependent; machine dependent c if(nb8.eq.0) then nb=16 else nb=nb8 endif c c*** check environment c me = ga_nodeid() c c*** check GA info for input arrays c call ga_check_handle(g_a(1), 'ga_pzheevr: a') call ga_check_handle(g_b(1), 'ga_pzheevr: b') c call ga_inquire(g_a(1), typeA, dimA18, dimA28) call ga_inquire(g_b(1), typeB, dimB18, dimB28) dima1=dima18 dima2=dima28 dimb1=dimb18 dimb2=dimb28 if(dimA1.ne.dima2) call ga_error( % 'ga_pzheevr: matrix A not square ',0) n=dima1 c nb=min(nb,n/2) if(dimb1.ne.dimb2) call ga_error( % 'ga_pzheevr: matrix B not square ',0) if(dimb1.ne.n) call ga_error( % 'ga_pzheevr: size matrix B and B differ ',0) if(mout.le.0.or.mout.gt.n) mout=n c c c*** initialize SL interface c call SLinit2(n) oactive=iam.lt.maxproc call ga_zero(g_b(1)) call ga_zero(g_b(2)) if (oactive) then c c*** find SBS format parameters c c mpA = numroc(dimA1, nb, myrow2, zero4, nprow2) nqA = numroc(dimA2, nb, mycol2, zero4, npcol2) c mpB = numroc(dimB1, nb, myrow2, zero4, nprow2) nqB = numroc(dimB2, nb, mycol2, zero4, npcol2) c c lda = max(one4,mpA) ldb = max(one4,mpB) c c c let scalapack check for errors c c should check to see if this is a compute node c check to see how this works in the new data server model c c elemA= mpA*nqA status = .true. if(elemA.ne.0)status = $ ma_push_get(MT_dcpL,elemA,'a',ha,adra) if(.not.status) & call ga_error('ga_pzheevr: mem alloc failed A ', -1) c c*** copy g_a to A and g_b to B using the block cyclic scalapack format c call ga_to_SL2cpl(g_a, dimA1, dimA2, nb, nb, $ dcpl_mb(adrA), lda, mpA, nqA) endif call ga_sync() if(oactive) then c elemB= mpB*nqB c if(elemB.ne.0)status = $ ma_push_get(MT_dcpL,elemB,'b',hb,adrb) if(.not.status) & call ga_error('ga_pzheevr: mem alloc failed B ', -1) c n8 = n if(n8.ne.0) then status = $ ma_push_get(MT_INT, n8 ,'ifail',hfail,adrfail) endif if(.not.status) & call ga_error('ga_pzheevr: mem alloc failed ifail ', n) c c*** fill SCALAPACK matrix descriptors c call descinit(descA, dimA1, dimA2, nb, nb, zero4, zero4, $ islctxt2, lda, info) info8=info if(info.ne.0) call ga_error(' ga_pzheevr: descinit A failed ', . -info8) c call descinit(descB, dimB1, dimB2, nb, nb, zero4, zero4, . islctxt2, ldb, info) info8=info if(info.ne.0) call ga_error(' ga_pzheevr: descinit B failed ', . -info8) c jobz = 'V' uplo = 'L' vl = 0.d0 vu = 0.d0 c il=1 iu=mout range='i' lwork4=-1 lrwork4=-1 liwork4=-1 call pzheevr(jobz, range, uplo, 1 n, dcpl_mb(adrA), one4, one4, D descA,vl,vu, il, iu, m, nz, eval, dcpl_mb(adrB), one4, 3 one4, descB, 4 dumm, lwork4, dumm1,lrwork4, 3 dumm2, liwork4, info) lwork=dumm lrwork=dumm1 liwork=dumm2 c if(liwork.ne.0)status = $ ma_push_get(MT_INT, liwork ,'iwork',hiwork,adriwork) if(.not.status) & call ga_error('ga_pzheevr: mem alloc failed iwork ', -1) c if(lwork.ne.0) $ status = ma_push_get(MT_DCPL, lwork , $ 'work',hwork,adrwork) if(.not.status) & call ga_error('ga_pzheevr: mem alloc failed work ', -1) if(lrwork.ne.0) $ status = ma_push_get(MT_DBL, lrwork , $ 'rwork',hrwork,adrrwork) if(.not.status) & call ga_error('ga_pzheevr: mem alloc failed rwork ', -1) c liwork4=liwork lwork4=lwork lrwork4=lrwork call pzheevr(jobz, range, uplo, 1 n, dcpl_mb(adrA), one4, one4, D descA,vl,vu, il, iu, m, nz, eval, dcpl_mb(adrB), one4, 3 one4, descB, 4 dcpl_mb(adrwork), lwork4, dbl_mb(adrrwork),lrwork4, 3 int_mb(adriwork), liwork4, info) mout=m if(iam.eq.0.and.mout.ne.n) W write(6,*) ' computed eval ',mout,' out of ', A dima1 if (nz .ne. n ) then if ( info .ne. 0 ) then if ( info .gt. 0 ) then call ga_error(' ga_pzheevr: argument is illegal ', info) else call ga_error(' ga_pzheevr: eigenvectors failed to converge ', $ info) endif endif endif c c c c*** copy solution matrix back to g_b c mout4=mout call ga_from_SL2cpl(g_b, dimA1, mout4, $ nb, nb, dcpl_mb(adrB), & ldb, mpb, nqB) c c c c*** deallocate work/SL arrays c if ( lrwork .ne. 0 ) status = ma_pop_stack(hrwork) if ( lwork .ne. 0 ) status = ma_pop_stack(hwork) if ( liwork .ne. 0 ) status = ma_pop_stack(hiwork) if ( n8 .ne. 0 ) status = ma_pop_stack(hfail) if ( elemB .ne. 0 ) status = ma_pop_stack(hb) if ( elemA .ne. 0 ) status = ma_pop_stack(ha) c endif call ga_sync() if(maxproc.lt.nnodes.or.dima1.le.nb) then c broadcast evals dblsize=ma_sizeof(MT_DBL,1,MT_BYTE)*dima1 call ga_brdcst(1688,eval,dblsize,0) endif return end #endif #ifdef USE_PXERBLA SUBROUTINE PXERBLA( ICTXT, SRNAME, INFO ) * * -- ScaLAPACK auxiliary routine (version 2.0) -- * University of Tennessee, Knoxville, Oak Ridge National Laboratory, * and University of California, Berkeley. * April 1, 1998 * * .. Scalar Arguments .. INTEGER*4 ICTXT, INFO * .. * .. Array Arguments .. CHARACTER*(*) SRNAME * .. * * Purpose * ======= * * PXERBLA is an error handler for the ScaLAPACK routines. It is called * by a ScaLAPACK routine if an input parameter has an invalid value. A * message is printed. Installers may consider modifying this routine in * order to call system-specific exception-handling facilities. * * Arguments * ========= * * ICTXT (local input) INTEGER*4 * On entry, ICTXT specifies the BLACS context handle, indica- * ting the global context of the operation. The context itself * is global, but the value of ICTXT is local. * * SRNAME (global input) CHARACTER*(*) * On entry, SRNAME specifies the name of the routine which cal- * ling PXERBLA. * * INFO (global input) INTEGER*4 * On entry, INFO specifies the position of the invalid parame- * ter in the parameter list of the calling routine. * * -- Written on April 1, 1998 by * Antoine Petitet, University of Tennessee, Knoxville 37996, USA. * * ===================================================================== * * .. Local Scalars .. INTEGER*4 MYCOL, MYROW, NPCOL, NPROW * .. * .. External Subroutines .. EXTERNAL BLACS_GRIDINFO * .. * .. Executable Statements .. * CALL BLACS_GRIDINFO( ICTXT, NPROW, NPCOL, MYROW, MYCOL ) * WRITE( *, FMT = 9999 ) MYROW, MYCOL, SRNAME, INFO * 9999 FORMAT( '{', I5, ',', I5, '}: On entry to ', A, $ ' parameter number ', I4, ' had an illegal value' ) * call ga_error(' pxerbla error: info equal ', I info) RETURN * * End of PXERBLA * END #endif ga-5.9.2/global/src/scalapack.fh000066400000000000000000000011321500715745200164110ustar00rootroot00000000000000#ifdef SCALAPACK_I8 #define INTGR4 integer*8 #else #define INTGR4 integer*4 #endif INTGR4 iam, nnodes,nprow,npcol, myrow, mycol, iSLctxt, A maxproc, iSLctxt2,nprow2,npcol2, myrow2, mycol2 logical init,init2, init3, init4 common /SLface/ init, init2, init3, init4, * iam, nnodes, nprow,npcol, myrow, mycol, * iSLctxt,iSLctxt2, * maxproc,nprow2,npcol2, myrow2, mycol2 #ifdef LINUX_DBL_UNDERSCORE #define blacs_pinfo blacs_pinfo_ #define blacs_get blacs_get_ #define blacs_gridinit blacs_gridinit_ #define blacs_gridinfo blacs_gridinfo_ #endif ga-5.9.2/global/src/sclstubs.c000066400000000000000000000072051500715745200161650ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "globalp.h" #include "ga-papi.h" #include "ga-wapi.h" #define _MAX_PROB_SIZE_ 10000 /* 100x100 */ #if ENABLE_F77 # define gai_lu_solve_alt_ F77_FUNC_(gai_lu_solve_alt,GAI_LU_SOLVE_ALT) # define gai_llt_solve_ F77_FUNC_(gai_llt_solve,GAI_LLT_SOLVE) # define gai_solve_ F77_FUNC_(gai_solve,GAI_SOLVE) # define gai_spd_invert_ F77_FUNC_(gai_spd_invert,GAI_SPD_INVERT) #endif #ifdef SCALAPACK_I8 # if SIZEOF_SHORT == 8 # define SL_INT short # elif SIZEOF_INT == 8 # define SL_INT int # elif SIZEOF_LONG == 8 # define SL_INT long # elif SIZEOF_LONG_LONG == 8 # define SL_INT long long # else # error # endif #else # if SIZEOF_SHORT == 4 # define SL_INT short # elif SIZEOF_INT == 4 # define SL_INT int # elif SIZEOF_LONG == 4 # define SL_INT long # elif SIZEOF_LONG_LONG == 4 # define SL_INT long long # else # error # endif #endif #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_lu_solve_alt = pnga_lu_solve_alt #endif void pnga_lu_solve_alt(Integer tran, Integer g_a, Integer g_b) { #if HAVE_SCALAPACK # if ENABLE_F77 void gai_lu_solve_alt_(Integer *tran, Integer *g_a, Integer *g_b); gai_lu_solve_alt_(&tran, &g_a, &g_b); # else pnga_error("ga_lu_solve:scalapack interfaced, need configure --enable-f77",0L); # endif #else pnga_error("ga_lu_solve:scalapack not interfaced",0L); #endif } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_lu_solve = pnga_lu_solve #endif void pnga_lu_solve(char *tran, Integer g_a, Integer g_b) { Integer dimA1, dimA2, typeA; Integer dimB1, dimB2, typeB; Integer dimsA[2], dimsB[2], ndim; /** check GA info for input arrays */ pnga_check_handle(g_a, "ga_lu_solve: a"); pnga_check_handle(g_b, "ga_lu_solve: b"); pnga_inquire (g_a, &typeA, &ndim, dimsA); pnga_inquire (g_b, &typeB, &ndim, dimsB); dimA1 = dimsA[0]; dimA2 = dimsA[1]; dimB1 = dimsB[0]; dimB2 = dimsB[1]; if( (dimA1*dimA2 > _MAX_PROB_SIZE_) || (dimB1*dimB2 > _MAX_PROB_SIZE_) ) pnga_error("ga_lu_solve:Array size too large. Use scalapack for optimum performance. configure --with-scalapack or --with-scalapack-i8 for ga_lu_solve to use Scalapack interface",0L); pnga_lu_solve_seq(tran, g_a, g_b); } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_llt_solve = pnga_llt_solve #endif Integer pnga_llt_solve(Integer g_a, Integer g_b) { #if HAVE_SCALAPACK # if ENABLE_F77 Integer gai_llt_solve_(Integer *g_a, Integer *g_b); return gai_llt_solve_(&g_a, &g_b); # else pnga_error("ga_lu_solve:scalapack interfaced, need configure --enable-f77",0L); return FALSE; # endif #else pnga_error("ga_lu_solve:scalapack not interfaced",0L); return FALSE; #endif } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_solve = pnga_solve #endif Integer pnga_solve(Integer g_a, Integer g_b) { #if HAVE_SCALAPACK # if ENABLE_F77 Integer gai_solve_(Integer *g_a, Integer *g_b); return gai_solve_(&g_a, &g_b); # else pnga_error("ga_lu_solve:scalapack interfaced, need configure --enable-f77",0L); return FALSE; # endif #else pnga_error("ga_lu_solve:scalapack not interfaced",0L); return FALSE; #endif } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_spd_invert = pnga_spd_invert #endif Integer pnga_spd_invert(Integer g_a) { #if HAVE_SCALAPACK # if ENABLE_F77 Integer gai_spd_invert_(Integer *g_a); return gai_spd_invert_(&g_a); # else pnga_error("ga_lu_solve:scalapack interfaced, need configure --enable-f77",0L); return FALSE; # endif #else pnga_error("ga_lu_solve:scalapack not interfaced",0L); return FALSE; #endif } ga-5.9.2/global/src/select.c000066400000000000000000000317321500715745200156040ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STRING_H # include #endif #include "globalp.h" #include "message.h" #include "ga_iterator.h" #include "ga-papi.h" #include "ga-wapi.h" #define GET_ELEMS(ndim,lo,hi,ld,pelems){\ int _i;\ for(_i=0, *pelems = hi[ndim-1]-lo[ndim-1]+1; _i< ndim-1;_i++) {\ if(ld[_i] != (hi[_i]-lo[_i]+1)) pnga_error("layout problem",_i);\ *pelems *= hi[_i]-lo[_i]+1;\ }\ } typedef struct { union val_t {double dval; int ival; long lval; long long llval; float fval;}v; Integer subscr[MAXDIM]; DoubleComplex extra; SingleComplex extra2; } elem_info_t; /* if new info exceeds old info, copy new info to old info, otherwise leave old * info alone */ static void snga_compare_elem(Integer type, char* op, elem_info_t *new_info, elem_info_t *old_info, Integer *update) { *update = 0; switch (type) { case C_INT: if (strncmp(op,"min",3) == 0) { if (new_info->v.ival < old_info->v.ival) { old_info->v.ival = new_info->v.ival; *update = 1; } } else { if (new_info->v.ival > old_info->v.ival) { old_info->v.ival = new_info->v.ival; *update = 1; } } break; case C_LONG: if (strncmp(op,"min",3) == 0) { if (new_info->v.lval < old_info->v.lval) { old_info->v.lval = new_info->v.lval; *update = 1; } } else { if (new_info->v.lval > old_info->v.lval) { old_info->v.lval = new_info->v.lval; *update = 1; } } break; case C_LONGLONG: if (strncmp(op,"min",3) == 0) { if (new_info->v.llval < old_info->v.llval) { old_info->v.llval = new_info->v.llval; *update = 1; } } else { if (new_info->v.llval > old_info->v.llval) { old_info->v.llval = new_info->v.llval; *update = 1; } } break; case C_DBL: if (strncmp(op,"min",3) == 0) { if (new_info->v.dval < old_info->v.dval) { old_info->v.dval = new_info->v.dval; *update = 1; } } else { if (new_info->v.dval > old_info->v.dval) { old_info->v.dval = new_info->v.dval; *update = 1; } } break; case C_FLOAT: if (strncmp(op,"min",3) == 0) { if (new_info->v.fval < old_info->v.fval) { old_info->v.fval = new_info->v.fval; *update = 1; } } else { if (new_info->v.fval > old_info->v.fval) { old_info->v.fval = new_info->v.fval; *update = 1; } } break; case C_SCPL: if (strncmp(op,"min",3) == 0) { if (new_info->v.fval < old_info->v.fval) { old_info->v.fval = new_info->v.fval; old_info->extra2 = new_info->extra2; *update = 1; } } else { if (new_info->v.fval > old_info->v.fval) { old_info->v.fval = new_info->v.fval; old_info->extra2 = new_info->extra2; *update = 1; } } break; case C_DCPL: if (strncmp(op,"min",3) == 0) { if (new_info->v.dval < old_info->v.dval) { old_info->v.dval = new_info->v.dval; old_info->extra = new_info->extra; *update = 1; } } else { if (new_info->v.dval > old_info->v.dval) { old_info->v.dval = new_info->v.dval; old_info->extra = new_info->extra; *update = 1; } } break; } } static void snga_select_elem(Integer type, char* op, void *ptr, Integer elems, elem_info_t *info, Integer *ind) { Integer i; switch (type){ int *ia,ival; double *da,dval; DoubleComplex *ca; SingleComplex *cfa; float *fa,fval; long *la,lval; long long *lla,llval; case C_INT: ia = (int*)ptr; ival = *ia; if (strncmp(op,"min",3) == 0) for(i=0;i ia[i]) {ival=ia[i];*ind=i; } } else for(i=0;iv.ival = (int) ival; break; case C_DCPL: ca = (DoubleComplex*)ptr; dval=ca->real*ca->real + ca->imag*ca->imag; if (strncmp(op,"min",3) == 0) for(i=0;ireal*ca->real + ca->imag*ca->imag; if(dval > tmp){dval = tmp; *ind = i;} } else for(i=0;ireal*ca->real + ca->imag*ca->imag; if(dval < tmp){dval = tmp; *ind = i;} } info->v.dval = dval; /* use abs value for comparison*/ info->extra = ((DoubleComplex*)ptr)[*ind]; /* append the actual val */ break; case C_SCPL: cfa = (SingleComplex*)ptr; fval=cfa->real*cfa->real + cfa->imag*cfa->imag; if (strncmp(op,"min",3) == 0) for(i=0;ireal*cfa->real + cfa->imag*cfa->imag; if(fval > tmp){fval = tmp; *ind = i;} } else for(i=0;ireal*cfa->real + cfa->imag*cfa->imag; if(fval < tmp){fval = tmp; *ind = i;} } info->v.fval = fval; /* use abs value for comparison*/ info->extra2 = ((SingleComplex*)ptr)[*ind]; /* append the actual val */ break; case C_DBL: da = (double*)ptr; dval = *da; if (strncmp(op,"min",3) == 0) for(i=0;i da[i]) {dval=da[i];*ind=i; } } else for(i=0;iv.dval = dval; break; case C_FLOAT: fa = (float*)ptr; fval = *fa; if (strncmp(op,"min",3) == 0) for(i=0;i fa[i]) {fval=fa[i];*ind=i; } } else for(i=0;iv.fval = fval; break; case C_LONG: la = (long*)ptr; lval = *la; if (strncmp(op,"min",3) == 0) for(i=0;i la[i]) {lval=la[i];*ind=i; } } else for(i=0;iv.lval = lval; break; case C_LONGLONG: lla = (long long*)ptr; llval = *lla; if (strncmp(op,"min",3) == 0) for(i=0;i lla[i]) {llval=lla[i];*ind=i; } } else for(i=0;iv.llval = llval; break; default: pnga_error(" wrong data type ",type); } } #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_select_elem = pnga_select_elem #endif void pnga_select_elem(Integer g_a, char* op, void* val, Integer *subscript) { Integer ndim, type, me, elems, ind=0, i; Integer lo[MAXDIM],hi[MAXDIM],dims[MAXDIM],ld[MAXDIM-1]; elem_info_t info, new_info; Integer num_blocks; int participate=0; int local_sync_begin; void *ptr; char *cptr; _iterator_hdl hdl_a; Integer first_pass; local_sync_begin = _ga_sync_begin; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)pnga_sync(); me = pnga_nodeid(); pnga_check_handle(g_a, "ga_select_elem"); if (strncmp(op,"min",3) == 0); else if (strncmp(op,"max",3) == 0); else pnga_error("operator not recognized",0); pnga_inquire(g_a, &type, &ndim, dims); num_blocks = pnga_total_blocks(g_a); first_pass = 1; pnga_local_iterator_init(g_a, &hdl_a); while (pnga_local_iterator_next(&hdl_a, lo, hi, &cptr, ld)) { Integer update; GET_ELEMS(ndim,lo,hi,ld,&elems); participate =1; ptr = (void*)cptr; if (first_pass) { /* select local element */ snga_select_elem(type, op, ptr, elems, &info, &ind); /* determine element subscript in the ndim-array */ for(i = 0; i < ndim; i++){ int elems = (int)( hi[i]-lo[i]+1); info.subscr[i] = ind%elems + lo[i] ; ind /= elems; } } else { /* select local element */ snga_select_elem(type, op, ptr, elems, &new_info, &ind); /* compare with old block */ snga_compare_elem(type,op, &new_info, &info, &update); /* determine element subscript in the ndim-array */ if (update) { for(i = 0; i < ndim; i++){ int elems = (int)( hi[i]-lo[i]+1); info.subscr[i] = ind%elems + lo[i] ; ind /= elems; } } } first_pass = 0; } #if 0 if (num_blocks < 0) { pnga_distribution(g_a, me, lo, hi); if ( lo[0]> 0 ){ /* base index is 1: we get 0 if no elements stored on p */ /******************* calculate local result ************************/ void *ptr; pnga_access_ptr(g_a, lo, hi, &ptr, ld); GET_ELEMS(ndim,lo,hi,ld,&elems); participate =1; /* select local element */ snga_select_elem(type, op, ptr, elems, &info, &ind); /* release access to the data */ pnga_release(g_a, lo, hi); /* determine element subscript in the ndim-array */ for(i = 0; i < ndim; i++){ int elems = (int)( hi[i]-lo[i]+1); info.subscr[i] = ind%elems + lo[i] ; ind /= elems; } } } else { void *ptr; Integer j, offset, jtot, upper; Integer nproc = pnga_nnodes(); pnga_access_block_segment_ptr(g_a, me, &ptr, &elems); if (elems > 0) { participate =1; /* select local element */ snga_select_elem(type, op, ptr, elems, &info, &ind); /* release access to the data */ pnga_release_block_segment(g_a, me); /* convert local index back into a global array index */ if (!pnga_uses_proc_grid(g_a)) { offset = 0; for (i=me; i= offset && ind < upper) { break; } else { offset += jtot; } } /* determine element subscript in the ndim-array */ ind -= offset; for(i = 0; i < ndim; i++){ int elems = (int)( hi[i]-lo[i]+1); info.subscr[i] = ind%elems + lo[i] ; ind /= elems; } } else { Integer stride[MAXDIM], index[MAXDIM]; Integer blocks[MAXDIM], block_dims[MAXDIM]; Integer proc_index[MAXDIM], topology[MAXDIM]; Integer l_index[MAXDIM]; Integer min, max; pnga_get_proc_index(g_a, me, proc_index); pnga_get_proc_grid(g_a, topology); if (!pnga_uses_irreg_proc_grid(g_a)) { pnga_get_block_info(g_a, blocks, block_dims); /* figure out strides for locally held block of data */ for (i=0; i dims[i]) max = dims[i]; stride[i] += (max - min + 1); } } /* use strides to figure out local index */ l_index[0] = ind%stride[0]; for (i=1; i #endif #if HAVE_STRING_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STDINT_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_STDDEF_H #include #endif #include "global.h" #include "globalp.h" #include "base.h" #include "ga_sparse.array.h" #include "ga-papi.h" #include "ga-wapi.h" #include "thread-safe.h" #define MBIG 1000000000 #define MSEED 161803398 #define MZ 0 #define FAC (1.0/MBIG) /** * Create a new sparse array * @param s_a handle of sparse matrix * @param size_k number of rows in sparse matrix * @param g_k global array containing map to k values * @param g_w global array containing weights * @param trans flag that indicates whether or not to calculate * transpose of sketch matrix. Needed for C interface */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_sprs_array_count_sketch = pnga_sprs_array_count_sketch #endif Integer pnga_sprs_array_count_sketch(Integer s_a, Integer size_k, Integer *g_k, Integer *g_w, Integer trans) { Integer i, ii, j, jj, n, hdl, g_a; Integer nrows; hdl = s_a + GA_OFFSET; Integer *klabels; void *kweights; Integer idim, jdim; Integer dims[2]; Integer one = 1; Integer two = 2; Integer nblocks[2]; Integer *top; Integer *list; void *kbuf; Integer nprocs, me; Integer k, kk, kcnt; Integer idx_size; Integer type; Integer lo[2], hi[2], ld; Integer *map, *size; if (SPA[hdl].idim < size_k) { pnga_error("Row dimension of sketch matrix must be less than row" "dimension of original matrix",size_k); } /* accumulate operation does not support type C_LONGLONG so fail if this * * data type encountered */ if (SPA[hdl].type == C_LONGLONG) { pnga_error("Data type of sparse matrix" " cannot be of type long long",SPA[hdl].type); } /* how many rows do I own */ nrows = SPA[hdl].ihi-SPA[hdl].ilo+1; idim = SPA[hdl].idim; jdim = SPA[hdl].jdim; idx_size = SPA[hdl].idx_size; type = SPA[hdl].type; nprocs = pnga_pgroup_nnodes(SPA[hdl].grp); me = pnga_pgroup_nodeid(SPA[hdl].grp); /* create map array */ size = (Integer*)malloc(nprocs*sizeof(Integer)); map = (Integer*)malloc((nprocs+1)*sizeof(Integer)); for (i=0; i -1) { Integer irow = top[kk]; memset(kbuf,0,jdim*SPA[hdl].size); while (irow > -1) { /* loop over column blocks */ if (trans) { lo[1] = kk+1; hi[1] = kk+1; } else { lo[0] = kk+1; hi[0] = kk+1; } for (n=0; n #endif #if HAVE_STRING_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STDINT_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_STDDEF_H #include #endif #include "global.h" #include "globalp.h" #include "base.h" #include "ga_sparse.array.h" #include "armci.h" #include "macdecls.h" #include "ga-papi.h" #include "ga-wapi.h" #include "thread-safe.h" #define ENABLE_PREALLOC 1 _sparse_array *SPA; /** * Initial number of values that can be stored on a processor before needing to * increase size of local buffers */ #define INIT_BUF_SIZE 1024 /** * Internal function to initialize sparse array data structures. This needs to * be called somewhere inside ga_initialize */ void sai_init_sparse_arrays() { Integer i; SPA = (_sparse_array*)malloc(MAX_ARRAYS*sizeof(_sparse_array)); for (i=0; i= SPA[hdl].maxval) { Integer i; Integer *tidx; Integer *tjdx; char *tval; char *oval = (char*)SPA[hdl].val; Integer me = pnga_pgroup_nodeid(SPA[hdl].grp); tidx = (Integer*)malloc(2*SPA[hdl].maxval*sizeof(Integer)); tjdx = (Integer*)malloc(2*SPA[hdl].maxval*sizeof(Integer)); tval = (char*)malloc(2*SPA[hdl].maxval*SPA[hdl].size); /* copy data in old arrays to new, larger array */ for (i=0; i proc) { tlo--; } if ((tlo*nproc)/dim != proc) { tlo++; } if (proc < nproc-1) { thi = (dim*(proc+1))/nproc; while ((thi*nproc)/dim < proc+1) { thi++; } while ((thi*nproc)/dim > proc+1) { thi--; } thi--; } else { thi = dim-1; } *lo = tlo; *hi = thi; printf("p[%d] dim: %ld proc: %ld nproc: %ld lo: %ld hi: %ld\n", pnga_nodeid(),dim,proc,nproc,*lo,*hi); } */ /** * Prepare sparse array for use by distributing values into row blocks based on * processor and subdivide each row into column blocks, also based on processor * @param s_a sparse array handle */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_sprs_array_assemble = pnga_sprs_array_assemble #endif logical pnga_sprs_array_assemble(Integer s_a) { Integer hdl = GA_OFFSET + s_a; int local_sync_begin,local_sync_end; Integer lo, hi, ld; Integer i,j,ilo,ihi,jlo,jhi; int64_t *offset; Integer *count; Integer *top; Integer *list; Integer *idx = SPA[hdl].idx; Integer *jdx = SPA[hdl].jdx; Integer nvals = SPA[hdl].nval; Integer nproc = pnga_pgroup_nnodes(SPA[hdl].grp); Integer me = pnga_pgroup_nodeid(SPA[hdl].grp); Integer elemsize = SPA[hdl].size; Integer iproc; Integer g_offset; Integer g_blk; Integer ret = 1; int64_t *size; Integer *map; Integer totalvals; Integer one = 1; Integer nrows, irow, idim, jdim; char *vals; Integer ncnt, icnt, jcnt; Integer longidx; int *isdx; int *jsdx; int64_t *ildx; int64_t *jldx; int64_t *row_info; int64_t max_nnz; Integer *row_nnz; Integer nnz; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if (local_sync_begin) pnga_pgroup_sync(SPA[hdl].grp); /* set variable that distinguishes between long and ints for indices */ if (SPA[hdl].idx_size == sizeof(int64_t)) { longidx = 1; } else { longidx = 0; } idim = SPA[hdl].idim; jdim = SPA[hdl].jdim; /* Create a linked list for values on this process and bin values by which * processor owns them */ count = (Integer*)malloc(nproc*sizeof(Integer)); top = (Integer*)malloc(nproc*sizeof(Integer)); list = (Integer*)malloc(nvals*sizeof(Integer)); for (i=0; i= nproc) iproc = nproc-1; count[iproc]++; list[i] = top[iproc]; top[iproc] = i; } /* Create global array to store information on sparse blocks */ { Integer dims[3], chunk[3]; Integer three = 3; dims[0] = 7; dims[1] = nproc; dims[2] = nproc; chunk[0] = 7; chunk[1] = -1; chunk[2] = -1; /* g_blk contains information about how data for each sparse * block is laid out in g_j and g_data. The last two dimensions * describe the location of sparse block in nproc X nproc array * of sparse blocks corresponding to original sparse matrix. * * First dimension contains the following information on each * block * ilo: lowest row index of block * ihi: highest row index of block * jlo: lowest column index of block * jhi: highest column index of block * offset: offset in g_j and g_data for column indices and data * values for block * blkend: last index g_j and g_data for block * iblk: column block index * Indices in bounding block are unit based. */ g_blk = pnga_create_handle(); pnga_set_pgroup(g_blk,SPA[hdl].grp); pnga_set_data(g_blk,three,dims,C_LONG); pnga_set_chunk(g_blk,chunk); if (!pnga_allocate(g_blk)) ret = 0; SPA[hdl].g_blk = g_blk; row_info = (int64_t*)malloc(7*nproc*sizeof(int64_t)); } /* determine how many values of matrix are stored on this process and what the * offset on remote processes is for the data. Create a global array to * perform this calculation */ g_offset = pnga_create_handle(); pnga_set_data(g_offset,one,&nproc,C_LONG); pnga_set_pgroup(g_offset,SPA[hdl].grp); if (!pnga_allocate(g_offset)) ret = 0; pnga_zero(g_offset); offset = (int64_t*)malloc(nproc*sizeof(int64_t)); for (i=0; i 0) { /* offset locations are not deterministic (ordering cannot be guaranteed), * but do guarantee that a space is available to hold data going to each * processor */ offset[iproc-1] = (int64_t)pnga_read_inc(g_offset,&iproc,count[iproc-1]); } } pnga_pgroup_sync(SPA[hdl].grp); size = (int64_t*)malloc(nproc*sizeof(int64_t)); /* internal indices are unit based */ ilo = 1; ihi = nproc; pnga_get(g_offset,&ilo,&ihi,size,&nproc); /* we now know how much data is on all processors (size) and have an offset on * remote processors that we can use to store data from this processor. Start by * constructing global arrays to hold data */ map = (Integer*)malloc(nproc*sizeof(Integer)); /* internal indices are unit based so start map at 1 */ map[0] = 1; totalvals = size[0]; for (i=1; i 0) { Integer j; ncnt = 0; char *vbuf = (char*)malloc(count[iproc]*elemsize); int64_t *ilbuf; int64_t *jlbuf; int *ibuf; int *jbuf; if (longidx) { ilbuf = (int64_t*)malloc(count[iproc]*sizeof(int64_t)); jlbuf = (int64_t*)malloc(count[iproc]*sizeof(int64_t)); } else { ibuf = (int*)malloc(count[iproc]*sizeof(int)); jbuf = (int*)malloc(count[iproc]*sizeof(int)); } vals = SPA[hdl].val; /* fill up buffers with data going to process iproc */ j = top[iproc]; if (longidx) { while (j >= 0) { memcpy(((char*)vbuf+ncnt*elemsize),(vals+j*elemsize),(size_t)elemsize); ilbuf[ncnt] = (int64_t)SPA[hdl].idx[j]; jlbuf[ncnt] = (int64_t)SPA[hdl].jdx[j]; ncnt++; j = list[j]; } } else { while (j >= 0) { memcpy(((char*)vbuf+ncnt*elemsize),(vals+j*elemsize),(size_t)elemsize); ibuf[ncnt] = (int)SPA[hdl].idx[j]; jbuf[ncnt] = (int)SPA[hdl].jdx[j]; ncnt++; j = list[j]; } } /* send data to global arrays */ lo = map[iproc]; lo += (Integer)offset[iproc]; hi = lo+count[iproc]-1; if (hi>=lo) { pnga_put(SPA[hdl].g_data,&lo,&hi,vbuf,&count[iproc]); if (longidx) { pnga_put(SPA[hdl].g_i,&lo,&hi,ilbuf,&count[iproc]); pnga_put(SPA[hdl].g_j,&lo,&hi,jlbuf,&count[iproc]); } else { pnga_put(SPA[hdl].g_i,&lo,&hi,ibuf,&count[iproc]); pnga_put(SPA[hdl].g_j,&lo,&hi,jbuf,&count[iproc]); } } free(vbuf); if (longidx) { free(ilbuf); free(jlbuf); } else { free(ibuf); free(jbuf); } } } pnga_pgroup_sync(SPA[hdl].grp); /* Local buffers are no longer needed */ free(SPA[hdl].idx); free(SPA[hdl].jdx); free(SPA[hdl].val); /* All data has been moved so that each process has a row block of the sparse * matrix. Now need to organize data within each process into column blocks. * Start by binning data by column index */ pnga_distribution(SPA[hdl].g_data,me,&lo,&hi); free(list); nvals = hi - lo + 1; list = (Integer*)malloc(nvals*sizeof(Integer)); /* get pointer to list of j indices */ for (i=0; i= nproc) iproc = nproc-1; count[iproc]++; list[i] = top[iproc]; top[iproc] = i; } } else { pnga_access_ptr(SPA[hdl].g_j,&lo,&hi,&jsdx,&ld); for (i=0; i= nproc) iproc = nproc-1; count[iproc]++; list[i] = top[iproc]; top[iproc] = i; } } pnga_release(SPA[hdl].g_j,&lo,&hi); } /* find out how many column blocks have data */ ncnt = 0; for (i=0; i 0) ncnt++; SPA[hdl].nblocks = ncnt; if (ncnt > 0) SPA[hdl].blkidx = (Integer*)malloc(ncnt*sizeof(Integer)); if (ncnt > 0) SPA[hdl].offset = (Integer*)malloc(ncnt*sizeof(Integer)); if (ncnt > 0) SPA[hdl].blksize = (Integer*)malloc(ncnt*sizeof(Integer)); /* allocate local buffers to sort everything into column blocks */ SPA[hdl].val = malloc(nvals*SPA[hdl].size); SPA[hdl].idx = malloc(nvals*sizeof(Integer)); SPA[hdl].jdx = malloc(nvals*sizeof(Integer)); if (lo <= hi) { pnga_access_ptr(SPA[hdl].g_data,&lo,&hi,&vals,&ld); if (longidx) { pnga_access_ptr(SPA[hdl].g_i,&lo,&hi,&ildx,&ld); pnga_access_ptr(SPA[hdl].g_j,&lo,&hi,&jldx,&ld); } else { pnga_access_ptr(SPA[hdl].g_i,&lo,&hi,&isdx,&ld); pnga_access_ptr(SPA[hdl].g_j,&lo,&hi,&jsdx,&ld); } } else { ildx = NULL; jldx = NULL; isdx = NULL; jsdx = NULL; } ncnt = 0; icnt = 0; for (i=0; i= 0) { char* vbuf = SPA[hdl].val; Integer* ibuf = SPA[hdl].idx; Integer* jbuf = SPA[hdl].jdx; SPA[hdl].blkidx[ncnt] = i; /* copy values from global array to local buffers */ SPA[hdl].blksize[ncnt] = 0; if (longidx) { while(j >= 0) { if (icnt >= nvals) { printf("p[%ld] ICNT: %ld exceeds NVALS: %ld\n",me,icnt,nvals); } memcpy(((char*)vbuf+icnt*elemsize),(vals+j*elemsize),(size_t)elemsize); ibuf[icnt] = (Integer)ildx[j]; jbuf[icnt] = (Integer)jldx[j]; j = list[j]; SPA[hdl].blksize[ncnt]++; icnt++; } } else { while(j >= 0) { memcpy(((char*)vbuf+icnt*elemsize),(vals+j*elemsize),(size_t)elemsize); if (icnt >= nvals) { printf("p[%ld] ICNT: %ld exceeds NVALS: %ld\n",me,icnt,nvals); } ibuf[icnt] = (Integer)isdx[j]; jbuf[icnt] = (Integer)jsdx[j]; j = list[j]; SPA[hdl].blksize[ncnt]++; icnt++; } } ncnt++; } } free(count); free(top); if (lo <= hi) { pnga_release(SPA[hdl].g_i,&lo,&hi); } /* Values have all been sorted into column blocks within the row block. Now * need to sort them by row. Start by evaluating lower and upper row indices */ SPA[hdl].ilo = (SPA[hdl].idim*me)/nproc; while ((SPA[hdl].ilo*nproc)/idim < me) { SPA[hdl].ilo++; } while ((SPA[hdl].ilo*nproc)/idim > me) { SPA[hdl].ilo--; } if ((SPA[hdl].ilo*nproc)/idim != me) { SPA[hdl].ilo++; } if (me < nproc-1) { SPA[hdl].ihi = (SPA[hdl].idim*(me+1))/nproc; while ((SPA[hdl].ihi*nproc)/idim < me+1) { SPA[hdl].ihi++; } while ((SPA[hdl].ihi*nproc)/idim > me+1) { SPA[hdl].ihi--; } SPA[hdl].ihi--; } else { SPA[hdl].ihi = SPA[hdl].idim-1; } nrows = SPA[hdl].ihi - SPA[hdl].ilo + 1; /* Resize the i-index array to account for row blocks */ pnga_destroy(SPA[hdl].g_i); /* Calculate number of row values that will need to be stored. Add an extra * row to account for the total number of rows in the column block */ for (i=0; i= nrows) { printf("p[%ld] (assemble) irow: %ld out of bounds: %ld\n",me,irow,nrows); } list[j] = top[irow]; top[irow] = j; count[irow]++; icnt++; } /* copy elements back into global arrays after sorting by row index. * Offset values in g_i are relative to the start of the column block. */ icnt = 0; char *vbuf = (char*)SPA[hdl].val+ncnt*SPA[hdl].size; if (longidx) { for (j=0; j= 0) { jldx[jcnt] = (int64_t)jbuf[irow]; memcpy((vptr+icnt*elemsize),(vbuf+irow*elemsize),(size_t)elemsize); irow = list[irow]; icnt++; jcnt++; row_nnz[j]++; } /* if ((ildx+i*(nrows+1))[j] == (int64_t)icnt) { printf("p[%ld] ilo: %ld ihi: %ld icol: %ld row: %ld has no elements\n", me,SPA[hdl].ilo,SPA[hdl].ihi,SPA[hdl].blkidx[i],j+SPA[hdl].ilo); } */ } if (icnt > 0) (ildx+i*(nrows+1))[nrows] = (int64_t)icnt; /* find_lims(SPA[hdl].jdim,SPA[hdl].blkidx[i],nproc,&jlo,&jhi); printf("p[%ld] column offsets for block [%ld,%ld] offset: %ld jlo: %ld jhi: %ld\n", me,me,SPA[hdl].blkidx[i],i*(nrows+1),jlo,jhi); for (j=0; j 0) { printf(" first j: %ld val: %d\n", jldx[(ildx+i*(nrows+1))[j]], ((int*)vptr)[(ildx+i*(nrows+1))[j]]); } else { printf("\n"); } } */ } else { for (j=0; j= 0) { jsdx[jcnt] = (int)jbuf[irow]; memcpy((vptr+icnt*elemsize),(vbuf+irow*elemsize),(size_t)elemsize); irow = list[irow]; icnt++; jcnt++; row_nnz[j]++; } } if (icnt > 0) (isdx+i*(nrows+1))[nrows] = (int)icnt; } /* TODO: (maybe) sort each row so that individual elements are arranged in * order of increasing j */ SPA[hdl].offset[i] = ncnt; ncnt += SPA[hdl].blksize[i]; } /* Find maximum number of non-zeros per row on this processors */ max_nnz = 0; for (i=0; i i) { jlo--; } if (i < nproc-1) { jhi = (SPA[hdl].jdim*(i+1))/nproc; while ((jhi*nproc)/jdim < i+1) { jhi++; } while ((jhi*nproc)/jdim > i+1) { jhi--; } jhi--; } else { jhi = SPA[hdl].jdim-1; } /* set indices to unit based indexing */ jlo++; jhi++; /* find index for block in blksize and offset arrays */ iblk = -1; for (j=0; j iproc) { (*lo)--; } if (iproc < nproc-1) { (*hi) = (idim*(iproc+1))/nproc; while (((*hi)*nproc)/idim < iproc+1) { (*hi)++; } while (((*hi)*nproc)/idim > iproc+1) { (*hi)--; } (*hi)--; } else { *hi = idim-1; } } } /** * Return the range of columns in column block iproc. Note that this will return * valid index ranges for the processor even if the column block contains no * non-zero values. This function is zero-based * @param s_a sparse array handle * @param iproc process for which index ranges are requested * @param lo,hi low and high values of the row indices held by this processor */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_sprs_array_column_distribution = pnga_sprs_array_column_distribution #endif void pnga_sprs_array_column_distribution(Integer s_a, Integer iproc, Integer *lo, Integer *hi) { Integer hdl = GA_OFFSET + s_a; Integer nproc = SPA[hdl].nprocs; Integer jdim = SPA[hdl].jdim; *lo = (SPA[hdl].jdim*iproc)/nproc; while (((*lo)*nproc)/jdim < iproc) { (*lo)++; } while (((*lo)*nproc)/jdim > iproc) { (*lo)--; } if (iproc < nproc-1) { (*hi) = (jdim*(iproc+1))/nproc; while (((*hi)*nproc)/jdim < iproc+1) { (*hi)++; } while (((*hi)*nproc)/jdim > iproc+1) { (*hi)--; } (*hi)--; } else { *hi = jdim-1; } } /** * Return list of column blocks containing data on this processor * @param void *idx indices of colum blocks containing data * @param Integer n number of column blocks with data */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_sprs_array_col_block_list = pnga_sprs_array_col_block_list #endif void pnga_sprs_array_col_block_list(Integer s_a, Integer **idx, Integer *n) { Integer hdl = GA_OFFSET + s_a; Integer i, index; *n = SPA[hdl].nblocks; *idx = (Integer*)malloc(SPA[hdl].nblocks*sizeof(Integer)); for (i=0; i=ilo) { klo = ilo + 1; khi = ihi + 1; pnga_acc(g_v,&klo,&khi,vsum,&one,&one_r); } if (local_sync_end) pnga_pgroup_sync(s_grp); free(vsum); } /** * Delete a sparse array and free any resources it may be using * @param s_a sparse array handle */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_sprs_array_destroy = pnga_sprs_array_destroy #endif logical pnga_sprs_array_destroy(Integer s_a) { Integer hdl = GA_OFFSET + s_a; Integer ret = 1; int local_sync_begin,local_sync_end; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if (local_sync_begin) pnga_pgroup_sync(SPA[hdl].grp); if (SPA[hdl].ready) { if (!pnga_destroy(SPA[hdl].g_data)) ret = 0; if (!pnga_destroy(SPA[hdl].g_i)) ret = 0; if (!pnga_destroy(SPA[hdl].g_j)) ret = 0; if (!pnga_destroy(SPA[hdl].g_blk)) ret = 0; if (SPA[hdl].blkidx != NULL) { free(SPA[hdl].blkidx); SPA[hdl].blkidx = NULL; } if (SPA[hdl].blksize != NULL) { free(SPA[hdl].blksize); SPA[hdl].blksize = NULL; } if (SPA[hdl].offset != NULL) { free(SPA[hdl].offset); SPA[hdl].offset = NULL; } } else if (SPA[hdl].active) { free(SPA[hdl].val); SPA[hdl].val = NULL; free(SPA[hdl].idx); SPA[hdl].idx = NULL; free(SPA[hdl].jdx); SPA[hdl].jdx = NULL; } SPA[hdl].active = 0; SPA[hdl].ready = 0; if (local_sync_end) pnga_pgroup_sync(SPA[hdl].grp); return ret; } /** * Print out values of sparse matrix using i,j,val format. Only non-zero * value of matrix are printed. This routine gathers all data on one process so * it may cause a memory overflow for a large matrix * @param s_a sparse array handle * @param file name of file to store sparse array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_sprs_array_export = pnga_sprs_array_export #endif void pnga_sprs_array_export(Integer s_a, const char* file) { Integer hdl = GA_OFFSET + s_a; int size = SPA[hdl].size; int type = SPA[hdl].type; int local_sync_begin,local_sync_end; char frmt[32]; char *cptr; int offset; Integer nlen_data; Integer nlen_j; Integer nlen_i; Integer irow; int idx_size = SPA[hdl].idx_size; void *vptr; void *iptr; void *jptr; Integer lo, hi, ld; char op[2]; FILE *SPRS; int *ilo, *ihi, *nblock; Integer me = pnga_pgroup_nodeid(SPA[hdl].grp); Integer nprocs = pnga_pgroup_nnodes(SPA[hdl].grp); Integer iproc, iblock; Integer nblocks; int *istart; int icnt; Integer g_blks; Integer one = 1; Integer total; int *blkmap; int *blkoffset; int *blksize; int64_t nnz; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if (local_sync_begin) pnga_pgroup_sync(SPA[hdl].grp); /* find the total number of nonzero elements on each process */ nnz = 0; for (iproc = 0; iproc nnz) nnz = hi; } /* find low and hi row indices on each processor */ ilo = (int*)malloc(nprocs*sizeof(int)); ihi = (int*)malloc(nprocs*sizeof(int)); nblock = (int*)malloc(nprocs*sizeof(int)); for (iproc=0; iproc 0) { SPA[new_hdl].blkidx = (Integer*)malloc(SPA[new_hdl].nblocks*sizeof(Integer)); SPA[new_hdl].blksize = (Integer*)malloc(SPA[new_hdl].nblocks*sizeof(Integer)); SPA[new_hdl].offset = (Integer*)malloc(SPA[new_hdl].nblocks*sizeof(Integer)); } for (i=0; i 0) { *idx = (Integer*)malloc((*ihi-*ilo+2)*sizeof(Integer)); *jdx = (Integer*)malloc(len*sizeof(Integer)); *data = malloc(len*SPA[hdl].size); lo[0] = params[4]; hi[0] = params[5]; ld[0] = 1; pnga_get(SPA[hdl].g_j,lo,hi,*jdx,ld); pnga_get(SPA[hdl].g_data,lo,hi,*data,ld); if (index == -1) { printf("p[%d] irow: %d icol: %d iblk: %d ilo: %d ihi: %d jlo: %d jhi: %d len: %d\n", pnga_nodeid(),irow,icol,index,*ilo,*ihi,*jlo,*jhi,len); pnga_error("sprs_array_get_block no block found",index); } pnga_distribution(SPA[hdl].g_i,irow,&offset,&dummy); lo[0] = offset+index*(*ihi-*ilo+2); hi[0] = offset+(index+1)*(*ihi-*ilo+2)-1; pnga_get(SPA[hdl].g_i,lo,hi,*idx,ld); /* printf("p[%ld] row offsets in get_block for block: %ld icol %ld\n", pnga_nodeid(),index,SPA[hdl].blkidx[index]); for (i=lo[0]; i 0) { printf(" first j: %ld val: %d\n", ((int64_t*)(*jdx))[ptr[i-lo[0]]], ((int*)(*data))[ptr[i-lo[0]]]); } else { printf("\n"); } } */ } else { /* block has no data */ *idx == NULL; *jdx == NULL; *data == NULL; ret = 0; } return ret; } /** * Utility function to resize map data structure if number of elements * exceeds available space. If number of elements exceeds available space * double size of buffers and copy old buffers to new buffers * @param top array of starting elements for each bin * @param list linked list of elements * @param idx row index of each element * @param jdx column index of each element * @param data value of each element * @param idim total number of rows in sparse array * @param jdim total number of columns in sparse array * @param elemsize size of individual data elements * @param bufsize current size of map arrays * @param ncnt total number of elements stored in map arrays */ void update_map(Integer **top, Integer **list, Integer **idx, Integer **jdx, void **data, Integer idim, Integer jdim, Integer elemsize, Integer *bufsize, Integer *ncnt) { Integer *ttop; Integer *tlist; Integer *tidx, *tjdx; Integer lcnt; Integer newsize; void *tdata; char *nptr; char *optr; Integer ii,jj; newsize = 2*(*bufsize); ttop = (Integer*)malloc(newsize*sizeof(Integer)); tlist = (Integer*)malloc(newsize*sizeof(Integer)); tdata = malloc(newsize*elemsize); lcnt = 0; for (ii=0; ii= 0) { Integer itmp = (*idx)[jj]; Integer jtmp = (*jdx)[jj]; Integer index = (jdim*itmp + jtmp)%newsize; tlist[lcnt] = ttop[index]; ttop[index] = lcnt; tidx[lcnt] = itmp; tjdx[lcnt] = jtmp; nptr = (char*)tdata + lcnt*elemsize; optr = (char*)(*data) + jj*elemsize; memcpy(nptr,optr,elemsize); jj = (*list)[jj]; lcnt++; } } free(*top); free(*list); free(*idx); free(*jdx); free(*data); *bufsize = newsize; *top = ttop; *list = tlist; *idx = tidx; *jdx = tjdx; *data = tdata; *ncnt = lcnt; } #if ENABLE_PREALLOC /** * Macros for sparse block matrix-matrix multiply. Note that bounds * ilo_a, ihi_a, jlo_b, jhi_b are unit based, so any index that has * these values subtracted from it must also be unit based. */ #define SPRS_REAL_MATMAT_MULTIPLY_M(_type,_idxa,_jdxa,_idxb,_jdxb) \ { \ for (i=ilo_a; i<=ihi_a; i++) { \ Integer kcols = _idxa[i+1-ilo_a]-_idxa[i-ilo_a]; \ for (k=0; k= 0) { \ if (i == idx[ldx] && jj == jdx[ldx]) break; \ ldx = list[ldx]; \ } \ if (ldx >= 0) { \ /* add product to existing value*/ \ ((_type*)data)[ldx] += val_a*val_b; \ } else { \ /* add new value to list */ \ ((_type*)data)[lcnt] = val_a*val_b; \ idx[lcnt] = i; \ jdx[lcnt] = jj; \ ldx = ((i-1)*jdim+jj-1)%bufsize; \ list[lcnt] = top[ldx]; \ top[ldx] = lcnt; \ lcnt++; \ } \ } \ } \ } \ } #define SPRS_COMPLEX_MATMAT_MULTIPLY_M(_type,_idxa,_jdxa,_idxb,_jdxb) \ { \ for (i=ilo_a; i<=ihi_a; i++) { \ Integer kcols = _idxa[i+1-ilo_a]-_idxa[i-ilo_a]; \ for (k=0; k= 0) { \ if (i == idx[ldx] && jj == jdx[ldx]) break; \ ldx = list[ldx]; \ } \ if (ldx >= 0) { \ /* add product to existing value*/ \ ((_type*)data)[2*ldx] += rval_a*rval_b-ival_a*ival_b; \ ((_type*)data)[2*ldx+1] += rval_a*ival_b+ival_a*rval_b; \ } else { \ /* add new value to list */ \ ((_type*)data)[2*lcnt] = rval_a*rval_b-ival_a*ival_b; \ ((_type*)data)[2*lcnt+1] = rval_a*ival_b+ival_a*rval_b; \ idx[lcnt] = i; \ jdx[lcnt] = jj; \ ldx = ((i-1)*jdim+jj-1)%bufsize; \ list[lcnt] = top[ldx]; \ top[ldx] = lcnt; \ lcnt++; \ } \ } \ } \ } \ } #else /** * Macros for sparse block matrix-matrix multiply. Note that bounds * ilo_a, ihi_a, jlo_b, jhi_b are unit based, so any index that has * these values subtracted from it must also be unit based. */ #define SPRS_REAL_MATMAT_MULTIPLY_M(_type,_idxa,_jdxa,_idxb,_jdxb) \ { \ for (i=ilo_a; i<=ihi_a; i++) { \ Integer kcols = _idxa[i+1-ilo_a]-_idxa[i-ilo_a]; \ for (k=0; k= 0) { \ if (i == idx[ldx] && jj == jdx[ldx]) break; \ ldx = list[ldx]; \ } \ if (ldx >= 0) { \ /* add product to existing value*/ \ ((_type*)data)[ldx] += val_a*val_b; \ } else { \ /* add new value to list */ \ if (lcnt == bufsize) \ update_map(&top, &list, &idx, &jdx, &data, idim, \ jdim, elemsize, &bufsize, &lcnt); \ ((_type*)data)[lcnt] = val_a*val_b; \ idx[lcnt] = i; \ jdx[lcnt] = jj; \ ldx = ((i-1)*jdim+jj-1)%bufsize; \ list[lcnt] = top[ldx]; \ top[ldx] = lcnt; \ lcnt++; \ } \ } \ } \ } \ } #define SPRS_COMPLEX_MATMAT_MULTIPLY_M(_type,_idxa,_jdxa,_idxb,_jdxb) \ { \ for (i=ilo_a; i<=ihi_a; i++) { \ Integer kcols = _idxa[i+1-ilo_a]-_idxa[i-ilo_a]; \ for (k=0; k= 0) { \ if (i == idx[ldx] && jj == jdx[ldx]) break; \ ldx = list[ldx]; \ } \ if (ldx >= 0) { \ /* add product to existing value*/ \ ((_type*)data)[2*ldx] += rval_a*rval_b-ival_a*ival_b; \ ((_type*)data)[2*ldx+1] += rval_a*ival_b+ival_a*rval_b; \ } else { \ /* add new value to list */ \ if (lcnt == bufsize) update_map(&top, &list, &idx, &jdx, \ &data, idim, jdim, elemsize, &bufsize, &lcnt); \ ((_type*)data)[2*lcnt] = rval_a*rval_b-ival_a*ival_b; \ ((_type*)data)[2*lcnt+1] = rval_a*ival_b+ival_a*rval_b; \ idx[lcnt] = i; \ jdx[lcnt] = jj; \ ldx = ((i-1)*jdim+jj-1)%bufsize; \ list[lcnt] = top[ldx]; \ top[ldx] = lcnt; \ lcnt++; \ } \ } \ } \ } \ } #endif /** * Multiply sparse matrices A and B to get sparse matrix C * C = A.B * @param s_a sparse array A * @param s_b sparse array B * @return handle of sparse array C */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_sprs_array_matmat_multiply = pnga_sprs_array_matmat_multiply #endif Integer pnga_sprs_array_matmat_multiply(Integer s_a, Integer s_b) { Integer *top; Integer *list; Integer *idx; Integer *jdx; Integer lcnt; Integer icnt; Integer hdl_a = s_a+GA_OFFSET; Integer hdl_b = s_b+GA_OFFSET; Integer hdl_c; int local_sync_begin,local_sync_end; Integer bufsize; Integer elemsize; Integer idim, jdim; Integer i, j, k, l, m, n; void *data; Integer nprocs = pnga_pgroup_nnodes(SPA[hdl_a].grp); Integer me = pnga_pgroup_nodeid(SPA[hdl_a].grp); Integer longidx; Integer type; Integer ihi, ilo; Integer rowdim; Integer *count; Integer nblocks; Integer s_c; Integer ilen; Integer max_nnz_a, max_nnz_b; Integer *row_nnz; Integer nnz; int64_t max_nnz; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if (local_sync_begin) pnga_pgroup_sync(SPA[hdl_a].grp); /* Do some initial verification to see if matrix multiply is possible */ if (SPA[hdl_a].type != SPA[hdl_b].type) { pnga_error("(ga_sprs_array_matmat_multiply) types of sparse matrices" " A and B must match",0); } type = SPA[hdl_a].type; if (SPA[hdl_a].grp != SPA[hdl_b].grp) { pnga_error("(ga_sprs_array_matmat_multiply) matrices A and B must" " be on the same group",0); } if (SPA[hdl_a].jdim != SPA[hdl_b].idim) { pnga_error("(ga_sprs_array_matmat_multiply) column dimension of" " A must match row dimension of B",0); } if (SPA[hdl_a].idx_size != SPA[hdl_b].idx_size) { pnga_error("(ga_sprs_array_matmat_multiply) size of integer" " indices of A and B must match",0); } if (SPA[hdl_a].idx_size == sizeof(int64_t)) { longidx = 1; } else { longidx = 0; } /* Allocate initial buffers to hold elements of product matrix. This * algorithm assumes that product matrix remains relatively sparse. */ #if ENABLE_PREALLOC { Integer nrows = SPA[hdl_a].ihi-SPA[hdl_a].ilo + 1; max_nnz_a = SPA[hdl_a].max_nnz; max_nnz_b = SPA[hdl_b].max_nnz; if (max_nnz_a*max_nnz_b > SPA[hdl_b].jdim) { bufsize = nrows*SPA[hdl_b].jdim; } else { bufsize = nrows*max_nnz_a*max_nnz_b; } } #else bufsize = INIT_BUF_SIZE; #endif elemsize = SPA[hdl_a].size; idim = SPA[hdl_a].idim; jdim = SPA[hdl_b].jdim; top = (Integer*)malloc(bufsize*sizeof(Integer)); list = (Integer*)malloc(bufsize*sizeof(Integer)); idx = (Integer*)malloc(bufsize*sizeof(Integer)); jdx = (Integer*)malloc(bufsize*sizeof(Integer)); data = malloc(bufsize*elemsize); for (i=0; i= nprocs) np = nprocs-1; list[i] = top[np]; top[np] = i; count[np]++; } /* count up number of column blocks with data */ nblocks = 0; for (i=0; i 0) nblocks++; } /* create a new sparse array to hold product array */ pnga_mask_sync(local_sync_begin,local_sync_end); s_c = pnga_sprs_array_create(idim,jdim,type,SPA[hdl_a].idx_size); hdl_c = GA_OFFSET + s_c; free(SPA[hdl_c].idx); free(SPA[hdl_c].jdx); free(SPA[hdl_c].val); /* set up array of offsets */ SPA[hdl_c].blkidx = (Integer*)malloc(nblocks*sizeof(Integer)); SPA[hdl_c].blksize = (Integer*)malloc(nblocks*sizeof(Integer)); SPA[hdl_c].offset = (Integer*)malloc(nblocks*sizeof(Integer)); nblocks = 0; for (i=0; i 0) { SPA[hdl_c].blkidx[nblocks] = i; SPA[hdl_c].blksize[nblocks] = count[i]; if (nblocks>0) SPA[hdl_c].offset[nblocks] = SPA[hdl_c].offset[nblocks-1] + SPA[hdl_c].blksize[nblocks-1]; nblocks++; } } SPA[hdl_c].offset[0] = 0; for (i=1; i= 0) { SPA[hdl_c].idx[icnt] = idx[i]; SPA[hdl_c].jdx[icnt] = jdx[i]; memcpy(&((char*)SPA[hdl_c].val)[icnt*elemsize], &((char*)data)[i*elemsize],elemsize); i = list[i]; icnt++; } } free(idx); free(jdx); free(data); /* Set up global arrays to hold distributed indices and non-zero values */ { int64_t isize = (rowdim+1)*nblocks; Integer totalsize = 0; Integer ndim = 1; Integer *offset = (Integer*)malloc(nprocs*sizeof(Integer)); Integer *tmp = (Integer*)malloc(nprocs*sizeof(Integer)); Integer *map = (Integer*)malloc(nprocs*sizeof(Integer)); /* set up array to hold row indices. Evaluate offsets for rows */ for (i=0; i0) offset[i] = offset[i-1]+tmp[i-1]; map[i] = offset[i]+1; /* 1-based indexing for map array */ } SPA[hdl_c].g_i = pnga_create_handle(); if (longidx) { pnga_set_data(SPA[hdl_c].g_i,ndim,&totalsize,C_LONG); } else { pnga_set_data(SPA[hdl_c].g_i,ndim,&totalsize,C_INT); } pnga_set_pgroup(SPA[hdl_c].g_i,SPA[hdl_c].grp); pnga_set_irreg_distr(SPA[hdl_c].g_i,map,&nprocs); pnga_mask_sync(local_sync_begin,local_sync_end); pnga_allocate(SPA[hdl_c].g_i); /* set up arrays to hold column indices and data. Evaluate offsets * for row blocks*/ for (i=0; i0) offset[i] = offset[i-1]+tmp[i-1]; map[i] = offset[i]+1; /* 1-based indexing for map array */ } pnga_mask_sync(local_sync_begin,local_sync_end); SPA[hdl_c].g_j = pnga_create_handle(); pnga_mask_sync(local_sync_begin,local_sync_end); SPA[hdl_c].g_data = pnga_create_handle(); nnz = totalsize; if (longidx) { pnga_set_data(SPA[hdl_c].g_j,ndim,&totalsize,C_LONG); } else { pnga_set_data(SPA[hdl_c].g_j,ndim,&totalsize,C_INT); } pnga_set_data(SPA[hdl_c].g_data,ndim,&totalsize,SPA[hdl_c].type); pnga_set_pgroup(SPA[hdl_c].g_j,SPA[hdl_c].grp); pnga_set_irreg_distr(SPA[hdl_c].g_j,map,&nprocs); pnga_set_pgroup(SPA[hdl_c].g_data,SPA[hdl_c].grp); pnga_set_irreg_distr(SPA[hdl_c].g_data,map,&nprocs); pnga_mask_sync(local_sync_begin,local_sync_end); pnga_allocate(SPA[hdl_c].g_j); pnga_mask_sync(local_sync_begin,local_sync_end); pnga_allocate(SPA[hdl_c].g_data); free(map); free(tmp); free(offset); } /* organize row block into column blocks in CSR format */ { int *ti, *tj; int64_t *lti, *ltj; void *tdata; Integer tlo, thi, tld; char *cdata, *ctdata; /* Get pointers to global arrays */ pnga_distribution(SPA[hdl_c].g_i,me,&tlo,&thi); if (longidx) { pnga_access_ptr(SPA[hdl_c].g_i,&tlo,&thi,<i,&tld); } else { pnga_access_ptr(SPA[hdl_c].g_i,&tlo,&thi,&ti,&tld); } pnga_distribution(SPA[hdl_c].g_j,me,&tlo,&thi); if (longidx) { pnga_access_ptr(SPA[hdl_c].g_j,&tlo,&thi,<j,&tld); } else { pnga_access_ptr(SPA[hdl_c].g_j,&tlo,&thi,&tj,&tld); } pnga_distribution(SPA[hdl_c].g_data,me,&tlo,&thi); pnga_access_ptr(SPA[hdl_c].g_data,&tlo,&thi,&tdata,&tld); idx = SPA[hdl_c].idx; jdx = SPA[hdl_c].jdx; cdata = (char*)SPA[hdl_c].val; ctdata = (char*)tdata; ilen = ihi - ilo + 1; row_nnz = (Integer*)malloc(ilen*sizeof(Integer)); for (n=0; n= 0) { ltj[offset_j+icnt] = (int64_t)jdx[offset_j+jd]-1; memcpy(&ctdata[elemsize*(offset_j+icnt)], &cdata[(offset_j+jd)*elemsize],elemsize); row_nnz[irow]++; icnt++; jd = rowlist[jd]; } } lti[offset_i+ilen] = icnt; } else { for (irow=0; irow= 0) { tj[offset_j+icnt] = (int)jdx[offset_j+jd]-1; memcpy(&ctdata[elemsize*(offset_j+icnt)], &cdata[(offset_j+jd)*elemsize],elemsize); icnt++; row_nnz[irow]++; jd = rowlist[jd]; } } ti[offset_i+ilen] = icnt; } /* clean up arrays */ free(rowtop); free(rowlist); } /* Find maximum number of non-zeros per row on this processors */ max_nnz = 0; for (i=0; i i) { jlo--; } if (i < nprocs-1) { jhi = (SPA[hdl_c].jdim*(i+1))/nprocs; while ((jhi*nprocs)/jdim < i+1) { jhi++; } while ((jhi*nprocs)/jdim > i+1) { jhi--; } jhi--; } else { jhi = SPA[hdl_c].jdim-1; } /* set indices to fortran indexing */ jlo++; jhi++; /* find index for block in blksize and offset arrays */ iblk = -1; for (j=0; j= lo) { Integer ilo = lo+1; Integer ihi = hi+1; pnga_access_ptr(g_v,&ilo,&ihi,&ptr,&ld); } else { ptr = NULL; } /* Find column block that contains value icol. Scan through all non-zero * in this block and if j-index corresponds to icol, then set corresponding * value in g_v */ iblock = (icol*nprocs)/SPA[handle].jdim; for (n=0; n me) { ilo--; } if ((ilo*nprocs)/idim != me) { ilo++; } ilo++; if (me < nprocs-1) { ihi = (idim*(me+1))/nprocs; while ((ihi*nprocs)/idim < me+1) { ihi++; } while ((ihi*nprocs)/idim > me+1) { ihi--; } ihi--; } else { ihi = idim-1; } ihi++; for (i=0; i #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRINGS_H # include #endif #include "abstract_ops.h" #include "globalp.h" #include "macdecls.h" #include "message.h" #include "ga-papi.h" #include "ga-wapi.h" /*\ sets values for specified array elements by enumerating with stride \*/ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_patch_enum = pnga_patch_enum #endif void pnga_patch_enum(Integer g_a, Integer lo, Integer hi, void* start, void* stride) { Integer dims[1],lop,hip; Integer ndim, type, me, off; pnga_sync(); me = pnga_nodeid(); pnga_check_handle(g_a, "ga_patch_enum"); ndim = pnga_ndim(g_a); if (ndim > 1) pnga_error("ga_patch_enum:applicable to 1-dim arrays",ndim); pnga_inquire(g_a, &type, &ndim, dims); pnga_distribution(g_a, me, &lop, &hip); if ( lop > 0 ){ /* we get 0 if no elements stored on this process */ /* take product of patch owned and specified by the user */ if(hi hi)hip = hi; nelem = hip-lop+1; off = lop - lo; pnga_access_ptr(g_a, &lop, &hip, &ptr, &ld); switch (type) { #define TYPE_CASE(MT,T,AT) \ case MT: \ { \ T *aptr = (T*)ptr; \ T astart = *((T*)start); \ T astride = *((T*)stride); \ for (i=0; i1)pnga_error("ga_scan_copy: applicable to 1-dim arrays",ndim); if(g_src == g_dst) { pnga_error("ga_scan_copy: src and dst must be different arrays", 0); } if(type_src != type_dst) { pnga_error("ga_scan_copy: src and dst arrays must be same type", 0); } pnga_sync(); pnga_distribution(g_msk, me, &lop, &hip); /* create arrays to hold last bit set on a given process */ lim = (long *) pnga_malloc(nproc, MT_C_LONGINT, "ga scan buf"); bzero(lim,sizeof(long)*nproc); lim[me] = -1; /* find last bit set on given process (store as global index) */ if ( lop > 0 ){ /* we get 0 if no elements stored on this process */ elems = hip - lop + 1; pnga_access_ptr(g_msk, &lop, &hip, &ptr_msk, &ld); switch (type_msk) { #define TYPE_CASE(MT,T,AT) \ case MT: \ { \ T * restrict buf = (T*)ptr_msk; \ for(i=0; i= lo && ioff <= hi) { \ lim[me]= ioff; \ } \ } \ } \ break; \ } #include "types.xh" #undef TYPE_CASE } pnga_release(g_msk, &lop, &hip); } pnga_gop(C_LONG,lim,nproc,"+"); if(hi hi) { stop = hi-lop+1; } else { stop = hip-lop+1; } /* If start bit is not first local bit, find last remote bit. * We must scan the entire lim to find the next-highest index. * Otherwise, this algorithm won't work with restricted arrays? */ rmt_idx = -1; for (i=0; i rmt_idx && lim[i] < lop) { rmt_idx = lim[i]; } } pnga_access_ptr(g_src, &lop, &hip, &ptr_src, &ld); pnga_access_ptr(g_dst, &lop, &hip, &ptr_dst, &ld); pnga_access_ptr(g_msk, &lop, &hip, &ptr_msk, &ld); combined_type = MT_NUMTYPES*type_src + type_msk; switch (combined_type) { #define TYPE_CASE(MT,T,AT,MT_MSK,T_MSK,AT_MSK) \ case (MT_NUMTYPES*MT) + MT_MSK: \ { \ T * restrict src = (T*)ptr_src; \ T * restrict dst = (T*)ptr_dst; \ T_MSK * restrict msk = (T_MSK*)ptr_msk; \ T last_val; \ assign_zero_##AT(last_val); \ if (-1 != rmt_idx) { \ pnga_get(g_src, &rmt_idx, &rmt_idx, &last_val, &ld);\ } \ for (i=start; i1)pnga_error("ga_scan_add: applicable to 1-dim arrays",ndim); if(g_src == g_dst) { pnga_error("ga_scan_add: src and dst must be different arrays", 0); } if(type_src != type_dst) { pnga_error("ga_scan_add: src and dst arrays must be same type", 0); } pnga_sync(); pnga_distribution(g_msk, me, &lop, &hip); /* create arrays to hold last bit set on a given process */ lim = (long *) pnga_malloc(nproc, MT_C_LONGINT, "ga scan buf"); bzero(lim,sizeof(long)*nproc); lim[me] = -1; /* find last bit set on given process (store as global index) */ if ( lop > 0 ){ /* we get 0 if no elements stored on this process */ elems = hip - lop + 1; pnga_access_ptr(g_msk, &lop, &hip, &ptr_msk, &ld); switch (type_msk) { #define TYPE_CASE(MT,T,AT) \ case MT: \ { \ T * restrict buf = (T*)ptr_msk; \ for(i=0; i= lo && ioff <= hi) { \ lim[me]= ioff; \ } \ } \ } \ break; \ } #include "types.xh" #undef TYPE_CASE } pnga_release(g_msk, &lop, &hip); } pnga_gop(C_LONG,lim,nproc,"+"); #if VERBOSE_DEBUG if (0==me) printf("(%d) lo=%d hi=%d\n", me, lo, hi); for (i=0; i rmt_idx && lim[i] < lop) { rmt_idx = lim[i]; } } #if VERBOSE_DEBUG for (i=0; i hi) { stop = hi-lop+1; } else { stop = hip-lop+1; } #define MALLOC_CASE(_np, _type) \ map = pnga_malloc(4*_np, MT_F_INT, "ga scan add locate"); \ v = pnga_malloc(_np, _type, "ga scan add gather values"); #define FREE_CASE() \ pnga_free(map); \ pnga_free(v); /* first, perform local scan add */ pnga_access_ptr(g_src, &lop, &hip, &ptr_src, &ld); pnga_access_ptr(g_dst, &lop, &hip, &ptr_dst, &ld); pnga_access_ptr(g_msk, &lop, &hip, &ptr_msk, &ld); combined_type = MT_NUMTYPES*type_src + type_msk; switch (combined_type) { #define TYPE_CASE(MT,T,AT,MT_MSK,T_MSK,AT_MSK) \ case (MT_NUMTYPES*MT) + MT_MSK: \ { \ T * restrict src = (T*)ptr_src; \ T * restrict dst = (T*)ptr_dst; \ T_MSK * restrict msk = (T_MSK*)ptr_msk; \ int found_bit = 0; \ /* find first set bit on first segment */ \ if (lop <= lo) { \ while (eq_zero_##AT_MSK(msk[start]) && start 0) { \ pnga_get(g_src, &loc, &loc, &dst[i], NULL);\ } \ } else { \ assign_##AT(dst[i], src[i]); \ } \ } \ } \ if (excl != 0) { \ for (i=start+1; i 0 ){ /* we get 0 if no elements stored on this process */ Integer lop_1 = lop-1; /* adjust the range of elements to be within */ if(lop < lo) lop = lo; if(hip > hi) hip = hi; if(hi NWORK)pnga_error("sgai_bin_offset: >NWORK", n); len = sizeof(int)*n; armci_msg_bintree(scope, &root, &up, &left, &right); /* up-tree phase: collect number of elements */ if (left > -1) armci_msg_rcv(tag, workL, len, &lenmes, left); if (right > -1) armci_msg_rcv(tag, workR, len, &lenmes, right); /* add number of elements in each bin */ if((right > -1) && left>-1) for(i=0;i -1) for(i=0;i -1) armci_msg_snd(tag, offset, len, right); if (left > -1) { /* we saved num elems for right subtree to adjust offset for left*/ for(i=0; i0){ /* enter this block when we have data */ Integer first_proc, last_proc, p; Integer first_off, last_off; Integer *myoff, bin; /* get offset values stored on my processor to first and last bin */ pnga_access_ptr(g_off, &lobin, &hibin, &myoff, &crap); first_off = myoff[0]; last_off = myoff[hibin-lobin]; /* pnga_get(g_off,&lobin,&lobin,&first_off,&lo); pnga_get(g_off,&hibin,&hibin,&last_off,&hi); */ /* since offset starts at 0, add 1 to get index to g_bin */ first_off++; last_off++; /* find processors on which these bins are located */ if(!pnga_locate(g_bin, &first_off, &first_proc)) pnga_error("ga_bin_sorter: failed to locate region f",first_off); if(!pnga_locate(g_bin, &last_off, &last_proc)) pnga_error("ga_bin_sorter: failed to locate region l",last_off); /* inspect range of indices to bin elements stored on these processors */ for(p=first_proc, bin=lobin; p<= last_proc; p++){ Integer lo, hi, buf[2], off, cnt; buf[0] =-1; buf[1]=-1; pnga_distribution(g_bin,p,&lo,&hi); for(/* start from current bin */; bin<= hibin; bin++, myoff++){ Integer blo,bhi,stat; blo = *myoff +1; if(bin == hibin){ pnga_get(g_cnt, &hibin, &hibin, &cnt, &hibin); /* local */ bhi = blo + cnt-1; }else bhi = myoff[1]; stat= sgai_match_bin2proc(blo, bhi, lo, hi); switch (stat) { case 0: /* bin in a middle */ break; case 1: /* first bin on that processor */ buf[0] =bin; break; case 2: /* last bin on that processor */ buf[1] =bin; break; case 3: /* first and last bin on that processor */ buf[0] =bin; buf[1] =bin; break; } if(stat>1)break; /* found last bin on that processor */ } /* set range of bins on processor p */ cnt =0; off=1; if(buf[0]!=-1){cnt=1; off=0;} if(buf[1]!=-1)cnt++; if(cnt){ Integer p1 = p+1; lo = 1+off; hi = lo+cnt-1; tlo[0] = lo; tlo[1] = p1; thi[0] = hi; thi[1] = p1; pnga_put(*g_range, tlo, thi, buf+off, &cnt); } } } return TRUE; } extern void gai_hsort(Integer *list, int n); #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wnga_bin_sorter = pnga_bin_sorter #endif void pnga_bin_sorter(Integer g_bin, Integer g_cnt, Integer g_off) { Integer nbin,totbin,type,ndim,lo,hi,me=pnga_nodeid(),crap; Integer g_range; if(FALSE==pnga_create_bin_range(g_bin, g_cnt, g_off, &g_range)) pnga_error("ga_bin_sorter: failed to create temp bin range array",0); pnga_inquire(g_bin, &type, &ndim, &totbin); if(ndim !=1) pnga_error("ga_bin_sorter: 1-dim array required",ndim); pnga_distribution(g_bin, me, &lo, &hi); if (lo > 0 ){ /* we get 0 if no elements stored on this process */ Integer bin_range[2], rlo[2],rhi[2]; Integer *bin_cnt, *ptr, i; /* get and inspect range of bins stored on current processor */ rlo[0] = 1; rlo[1]= me+1; rhi[0]=2; rhi[1]=rlo[1]; pnga_get(g_range, rlo, rhi, bin_range, rhi); /* local */ nbin = bin_range[1]-bin_range[0]+1; if(nbin<1 || nbin> totbin || nbin>(hi-lo+1)) pnga_error("ga_bin_sorter:bad nbin",nbin); /* get count of elements in each bin stored on this task */ if(!(bin_cnt = (Integer*)malloc(nbin*sizeof(Integer)))) pnga_error("ga_bin_sorter:memory allocation failed",nbin); pnga_get(g_cnt,bin_range,bin_range+1,bin_cnt,&nbin); /* get access to local bin elements */ pnga_access_ptr(g_bin, &lo, &hi, &ptr, &crap); for(i=0;i nbin) pnga_error("wrong bin",selected); if(all_bin_contrib[selected-1] ==0) my_nbin++; /* new bin found */ all_bin_contrib[selected-1]++; } /* process bins in chunks to match available buffer space */ for(i=0; i nbin) { printf("Writing off end of bins array: index=%d elems=%d lo=%ld hi=%ld values=%ld nbin=%ld\n", i,elems,(long)lo,(long)hi,(long)values+i,(long)nbin); break; }else{ pnga_put(g_bin, &lo, &hi, values+i, &selected); } i+=elems; } free(offset); free(all_bin_contrib); if(sortit)pnga_bin_sorter(g_bin, g_cnt, g_off); else pnga_sync(); } ga-5.9.2/global/src/thread-safe.c000066400000000000000000000005761500715745200165120ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include "thread-safe.h" #if defined(PTHREADS) && defined(THREAD_SAFE) #include #include pthread_mutex_t ga_threadsafe_lock; void GA_Internal_Threadsafe_Lock() { pthread_mutex_lock(&ga_threadsafe_lock); } void GA_Internal_Threadsafe_Unlock() { pthread_mutex_unlock(&ga_threadsafe_lock); } #endif ga-5.9.2/global/src/thread-safe.h000066400000000000000000000006311500715745200165070ustar00rootroot00000000000000#ifndef _GA_THREAD_SAFE_H_ #define _GA_THREAD_SAFE_H_ #define THREAD_SAFE 1 #if defined(PTHREADS) && defined(THREAD_SAFE) #ifdef __cplusplus extern "C" { #endif extern void GA_Internal_Threadsafe_Lock(); extern void GA_Internal_Threadsafe_Unlock(); #ifdef __cplusplus } #endif #else #define GA_Internal_Threadsafe_Lock() #define GA_Internal_Threadsafe_Unlock() #endif #endif /* _GA_THREAD_SAFE_H_ */ ga-5.9.2/global/src/types.xh000066400000000000000000000003311500715745200156550ustar00rootroot00000000000000TYPE_CASE(C_INT,int,reg) TYPE_CASE(C_LONG,long,reg) TYPE_CASE(C_LONGLONG,long long,reg) TYPE_CASE(C_FLOAT,float,reg) TYPE_CASE(C_DBL,double,reg) TYPE_CASE(C_SCPL,SingleComplex,cpl) TYPE_CASE(C_DCPL,DoubleComplex,cpl) ga-5.9.2/global/src/types2.xh000066400000000000000000000052701500715745200157460ustar00rootroot00000000000000TYPE_CASE(C_INT,int,reg, C_INT,int,reg) TYPE_CASE(C_LONG,long,reg, C_INT,int,reg) TYPE_CASE(C_LONGLONG,long long,reg,C_INT,int,reg) TYPE_CASE(C_FLOAT,float,reg, C_INT,int,reg) TYPE_CASE(C_DBL,double,reg, C_INT,int,reg) TYPE_CASE(C_SCPL,SingleComplex,cpl,C_INT,int,reg) TYPE_CASE(C_DCPL,DoubleComplex,cpl,C_INT,int,reg) TYPE_CASE(C_INT,int,reg, C_LONG,long,reg) TYPE_CASE(C_LONG,long,reg, C_LONG,long,reg) TYPE_CASE(C_LONGLONG,long long,reg,C_LONG,long,reg) TYPE_CASE(C_FLOAT,float,reg, C_LONG,long,reg) TYPE_CASE(C_DBL,double,reg, C_LONG,long,reg) TYPE_CASE(C_SCPL,SingleComplex,cpl,C_LONG,long,reg) TYPE_CASE(C_DCPL,DoubleComplex,cpl,C_LONG,long,reg) TYPE_CASE(C_INT,int,reg, C_LONGLONG,long long,reg) TYPE_CASE(C_LONG,long,reg, C_LONGLONG,long long,reg) TYPE_CASE(C_LONGLONG,long long,reg,C_LONGLONG,long long,reg) TYPE_CASE(C_FLOAT,float,reg, C_LONGLONG,long long,reg) TYPE_CASE(C_DBL,double,reg, C_LONGLONG,long long,reg) TYPE_CASE(C_SCPL,SingleComplex,cpl,C_LONGLONG,long long,reg) TYPE_CASE(C_DCPL,DoubleComplex,cpl,C_LONGLONG,long long,reg) TYPE_CASE(C_INT,int,reg, C_FLOAT,float,reg) TYPE_CASE(C_LONG,long,reg, C_FLOAT,float,reg) TYPE_CASE(C_LONGLONG,long long,reg,C_FLOAT,float,reg) TYPE_CASE(C_FLOAT,float,reg, C_FLOAT,float,reg) TYPE_CASE(C_DBL,double,reg, C_FLOAT,float,reg) TYPE_CASE(C_SCPL,SingleComplex,cpl,C_FLOAT,float,reg) TYPE_CASE(C_DCPL,DoubleComplex,cpl,C_FLOAT,float,reg) TYPE_CASE(C_INT,int,reg, C_DBL,double,reg) TYPE_CASE(C_LONG,long,reg, C_DBL,double,reg) TYPE_CASE(C_LONGLONG,long long,reg,C_DBL,double,reg) TYPE_CASE(C_FLOAT,float,reg, C_DBL,double,reg) TYPE_CASE(C_DBL,double,reg, C_DBL,double,reg) TYPE_CASE(C_SCPL,SingleComplex,cpl,C_DBL,double,reg) TYPE_CASE(C_DCPL,DoubleComplex,cpl,C_DBL,double,reg) TYPE_CASE(C_INT,int,reg, C_SCPL,SingleComplex,cpl) TYPE_CASE(C_LONG,long,reg, C_SCPL,SingleComplex,cpl) TYPE_CASE(C_LONGLONG,long long,reg,C_SCPL,SingleComplex,cpl) TYPE_CASE(C_FLOAT,float,reg, C_SCPL,SingleComplex,cpl) TYPE_CASE(C_DBL,double,reg, C_SCPL,SingleComplex,cpl) TYPE_CASE(C_SCPL,SingleComplex,cpl,C_SCPL,SingleComplex,cpl) TYPE_CASE(C_DCPL,DoubleComplex,cpl,C_SCPL,SingleComplex,cpl) TYPE_CASE(C_INT,int,reg, C_DCPL,DoubleComplex,cpl) TYPE_CASE(C_LONG,long,reg, C_DCPL,DoubleComplex,cpl) TYPE_CASE(C_LONGLONG,long long,reg,C_DCPL,DoubleComplex,cpl) TYPE_CASE(C_FLOAT,float,reg, C_DCPL,DoubleComplex,cpl) TYPE_CASE(C_DBL,double,reg, C_DCPL,DoubleComplex,cpl) TYPE_CASE(C_SCPL,SingleComplex,cpl,C_DCPL,DoubleComplex,cpl) TYPE_CASE(C_DCPL,DoubleComplex,cpl,C_DCPL,DoubleComplex,cpl) ga-5.9.2/global/testing/000077500000000000000000000000001500715745200150415ustar00rootroot00000000000000ga-5.9.2/global/testing/CMakeLists.txt000066400000000000000000000210451500715745200176030ustar00rootroot00000000000000# # module: CMakeLists.txt # author: Bruce Palmer # description: CMake build for GA. Only MPI-based runtimes are supported. # # DISCLAIMER # # This material was prepared as an account of work sponsored by an # agency of the United States Government. Neither the United States # Government nor the United States Department of Energy, nor Battelle, # nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR # ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, # COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, # SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT # INFRINGE PRIVATELY OWNED RIGHTS. # # # ACKNOWLEDGMENT # # This software and its documentation were produced with United States # Government support under Contract Number DE-AC06-76RLO-1830 awarded by # the United States Department of Energy. The United States Government # retains a paid-up non-exclusive, irrevocable worldwide license to # reproduce, prepare derivative works, perform publicly and display # publicly by or for the US Government, including the right to # distribute to other US Government contractors. # # -*- mode: cmake -*- # ------------------------------------------------------------- # file: CMakeLists.txt # ------------------------------------------------------------- include_directories(BEFORE ${PROJECT_SOURCE_DIR}/global/src ${PROJECT_BINARY_DIR}/global/src ${PROJECT_SOURCE_DIR}/ma ${PROJECT_BINARY_DIR}/ma ${PROJECT_BINARY_DIR}/gaf2c ${PROJECT_SOURCE_DIR}/comex/src-armci # ${PROJECT_SOURCE_DIR}/tcgmsg ${PROJECT_SOURCE_DIR}/LinAlg/lapack+blas ${PROJECT_SOURCE_DIR}/global/testing ${PROJECT_BINARY_DIR}) set(TEST_NPROCS_1 1) set(TEST_NPROCS_2 2) if(MPI_PR) set(TEST_NPROCS_1 2) set(TEST_NPROCS_2 3) endif() # ------------------------------------------------------------- # Build test executables # ------------------------------------------------------------- # This test uses random() and srandom() which are not available on Windows if(NOT MSVC) ga_add_parallel_test (mulmatpatchc "mulmatpatchc.c util.c" ${TEST_NPROCS_1}) endif() ga_add_parallel_test (elempatch "elempatch.c util.c" ) ga_add_parallel_test (getmem "getmem.c util.c" ) ga_add_parallel_test (mtest "mtest.c util.c" ) ga_add_parallel_test (normc "normc.c util.c" ) ga_add_parallel_test (matrixc "matrixc.c" ) ga_add_parallel_test (ntestc "ntestc.c" ) ga_add_parallel_test (nbtestc "nbtestc.c" ) ga_add_parallel_test (ntestfc "ntestfc.c" ) ga_add_parallel_test (packc "packc.c util.c" ) ga_add_parallel_test (print "print.c util.c" ) ga_add_parallel_test (scan_addc "scan_addc.c util.c" ) ga_add_parallel_test (scan_copyc "scan_copyc.c util.c") ga_add_parallel_test (testc "testc.c util.c" ) ga_add_parallel_test (testmult "testmult.c util.c" ) ga_add_parallel_test (testmatmultc "testmatmultc.c" ) ga_add_parallel_test (testmultrect "testmultrect.c" ) ga_add_parallel_test (gemmtest "gemmtest.c" ) ga_add_parallel_test (read_only "read_only.c" ) ga_add_parallel_test (cache_test "cache_test.c" ) ga_add_parallel_test (unpackc "unpackc.c util.c" ) ga_add_parallel_test (perf2 "perf2.c" ) ga_add_parallel_test (ga-mpi "ga-mpi.c util.c" ) ga_add_parallel_test (lock "lock.c util.c" ) ga_add_parallel_test (comm_init "comm_init.c util.c" ) ga_add_parallel_test (patch_enumc "patch_enumc.c util.c" ) ga_add_parallel_test (simple_groups_commc "simple_groups_commc.c util.c") ga_add_parallel_test (thread_perf_contig "thread_perf_contig.c util.c" ) ga_add_parallel_test (thread_perf_strided "thread_perf_strided.c util.c") ga_add_parallel_test (threadsafec "threadsafec.c util.c" ) if(LAPACK_FOUND) ga_add_parallel_test (ga_lu "ga_lu.c util.c") endif() if (ENABLE_FORTRAN) ga_add_parallel_test (bin "bin.F ffflush.F" Fortran) ga_add_parallel_test (blktest "blktest.F ffflush.F" Fortran) ga_add_parallel_test (g2test "g2test.F ffflush.F util.c" Fortran) ga_add_parallel_test (g3test "g3test.F ffflush.F util.c" Fortran) ga_add_parallel_test (ga_shift "ga_shift.F ffflush.F" Fortran) ga_add_parallel_test (ghosts "ghosts.F ffflush.F util.c" Fortran) ga_add_parallel_test (jacobi "jacobi.F ffflush.F" Fortran) ga_add_parallel_test (mir_perf2 "mir_perf2.F ffflush.F util.c" Fortran) ga_add_parallel_test (mmatrix "mmatrix.F ffflush.F util.c" Fortran) ga_add_parallel_test (mulmatpatch "mulmatpatch.F ffflush.F testblas.F" Fortran) ga_add_parallel_test (nbtest "nbtest.F ffflush.F util.c" Fortran) ga_add_parallel_test (nb2test "nb2test.F ffflush.F util.c" Fortran) ga_add_parallel_test (ndim "ndim.F ffflush.F util.c" Fortran) ga_add_parallel_test (patch "patch.F ffflush.F testblas.F" Fortran) ga_add_parallel_test (patch2 "patch2.F ffflush.F" Fortran) ga_add_parallel_test (patch_enumf "patch_enumf.F ffflush.F" Fortran) ga_add_parallel_test (perfmod "perfmod.F ffflush.F util.c" Fortran) ga_add_parallel_test (perform "perform.F ffflush.F util.c" Fortran) ga_add_parallel_test (perf "perf.F ffflush.F util.c" Fortran) ga_add_parallel_test (pg2test "pg2test.F ffflush.F util.c" Fortran) ga_add_parallel_test (pgtest "pgtest.F ffflush.F util.c" Fortran) ga_add_parallel_test (scan "scan.F ffflush.F" Fortran) ga_add_parallel_test (sparse "sparse.F ffflush.F" Fortran) ga_add_parallel_test (sprsmatmult "sprsmatmult.F ffflush.F" Fortran) ga_add_parallel_test (stride "stride.F ffflush.F" Fortran) ga_add_parallel_test (test "test.F ffflush.F util.c" Fortran) ga_add_parallel_test (testmatmult "testmatmult.F ffflush.F util.c" Fortran) ga_add_parallel_test (testsolve "testsolve.F ffflush.F" Fortran) ga_add_parallel_test (overlay "overlay.F ffflush.F util.c" Fortran) ga_add_parallel_test (testeig "testeig.F ffflush.F" Fortran) ga_add_parallel_test (simple_groups "simple_groups.F ffflush.F" Fortran) ga_add_parallel_test (simple_groups_comm "simple_groups_comm.F ffflush.F" Fortran) # Generated Tests function(generate_ngatests testname) add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/${testname}.F COMMAND ${ga_m4_cmd} ${PROJECT_SOURCE_DIR}/global/testing/${testname}.m4 > ${CMAKE_BINARY_DIR}/${testname}.F WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/global/testing COMMENT "Generating ${testname} test" ) endfunction() generate_ngatests(nga-onesided) generate_ngatests(nga-patch) generate_ngatests(nga-periodic) generate_ngatests(nga-scatter) generate_ngatests(ngatest) generate_ngatests(nga-util) set (NGATEST_GEN_SRCS ${CMAKE_BINARY_DIR}/nga-onesided.F ${CMAKE_BINARY_DIR}/nga-patch.F ${CMAKE_BINARY_DIR}/nga-periodic.F ${CMAKE_BINARY_DIR}/nga-scatter.F ${CMAKE_BINARY_DIR}/ngatest.F ${CMAKE_BINARY_DIR}/nga-util.F ) add_custom_target( GenerateNGATests ALL DEPENDS ${NGATEST_GEN_SRCS} ) set_source_files_properties( ${NGATEST_GEN_SRCS} PROPERTIES GENERATED TRUE ) ga_add_parallel_test (nga-onesided "${CMAKE_BINARY_DIR}/nga-onesided.F ffflush.F util.c" ${TEST_NPROCS_2} Fortran) ga_add_parallel_test (nga-patch "${CMAKE_BINARY_DIR}/nga-patch.F ffflush.F util.c" ${TEST_NPROCS_1} Fortran) ga_add_parallel_test (nga-periodic "${CMAKE_BINARY_DIR}/nga-periodic.F ffflush.F util.c" Fortran ) ga_add_parallel_test (nga-scatter "${CMAKE_BINARY_DIR}/nga-scatter.F ffflush.F util.c" Fortran ) ga_add_parallel_test (ngatest "${CMAKE_BINARY_DIR}/ngatest.F ffflush.F util.c" ${TEST_NPROCS_1} Fortran) ga_add_parallel_test (nga-util "${CMAKE_BINARY_DIR}/nga-util.F ffflush.F util.c" Fortran ) add_dependencies(nga-onesided.x GenerateNGATests) add_dependencies(nga-patch.x GenerateNGATests) add_dependencies(nga-periodic.x GenerateNGATests) add_dependencies(nga-scatter.x GenerateNGATests) add_dependencies(ngatest.x GenerateNGATests) add_dependencies(nga-util.x GenerateNGATests) endif() ga-5.9.2/global/testing/README000066400000000000000000000062571500715745200157330ustar00rootroot00000000000000The GA Test Programs ~~~~~~~~~~~~~~~~~~~~ These programs can be built after the GA library is configure and compiled. To build C and Fortran programs type: "make checkprogs" :test.x: general test program for GA :patch.x: tests GA patch operations :testsolve.x: tests linear equation solver based on LU factorization :testeig.x: tests GA eigensolver, matrix multiply, and symmetrization :ndim.x: Fortran test program for n-dim GA :perf.x: tests performance of GA primitives: get, put, accumulate :testc.x: C test program for GA :ntestc.x: C test program for n-dim GA :ga-mpi.x: C test program for GA that demonstrates the interface to MPI :mtest.x: test program for matrix functions :testmult.x: test program for GEMM functions (Eg:dgemm). :elempatch.x: test program for the element-wise functions M4-based N-dim test program =========================== The following are special steps required to configure and build source file for testing of n-dim global arrays based on the M4 source files. The full functionality test might take several hours to complete. Therefore you might want to limit the test to a subset of functionality supported. The configuration steps are: Edit the file ngatest.m4 (this step is optional if you want to test all): 1. Specify data type: - To test Integer, set "m4_test_int" to "yes", otherwise, "no" - To test Double Precision, set "m4_test_dbl" to "yes", otherwise, "no" - To test Double Complex, set "m4_test_dcpl" to "yes", otherwise, "no" 2. Specify range of dimensions to test (upt 7 supported in GA Fortran API): Example: to test from 2 to 4 dimensions, set "m4_dim_from" to 2 and "m4_dim_to" to 4 3. Specify functions to test: Functions that can be tested include: - ga_fill Set "m4_test_GA_FILL" to "yes" to test - nga_get Set "m4_test_NGA_GET" to "yes" to test - nga_put Set "m4_test_NGA_PUT" to "yes" to test - nga_acc Set "m4_test_NGA_ACC" to "yes" to test - nga_scatter Set "m4_test_NGA_SCATTER" to "yes" to test - nga_gather Set "m4_test_NGA_GATHER" to "yes" to test - nga_fill_patch Set "m4_test_NGA_FILL_PATCH" to "yes" to test - nga_copy_patch Set "m4_test_NGA_COPY_PATCH" to "yes" to test - nga_scale_patch Set "m4_test_NGA_SCALE_PATCH" to "yes" to test - nga_add_patch Set "m4_test_NGA_ADD_PATCH" to "yes" to test - nga_idot_patch Set "m4_test_NGA_DOT_PATCH" to "yes" to test - nga_ddot_patch Set "m4_test_NGA_DOT_PATCH" to "yes" to test - nga_zdot_patch Set "m4_test_NGA_DOT_PATCH" to "yes" to test Setting any variable to "no" will bypass testing the corresponding function To compile, type "make global/testing/ngatest.x" from the top-level. Source files: - ngatest.m4 - ngatest_src/ndim.src - ngatest_src/ndim_main.src - ngatest_src/ndim_GA_FILL.src - ngatest_src/ndim_NGA_GET.src - ngatest_src/ndim_NGA_PUT.src - ngatest_src/ndim_NGA_ACC.src - ngatest_src/ndim_NGA_SCATTER.src - ngatest_src/ndim_NGA_GATHER.src - ngatest_src/ndim_NGA_FILL_PATCH.src - ngatest_src/ndim_NGA_COPY_PATCH.src - ngatest_src/ndim_NGA_SCALE_PATCH.src - ngatest_src/ndim_NGA_ADD_PATCH.src - ngatest_src/ndim_NGA_DOT_PATCH.src ga-5.9.2/global/testing/big.c000066400000000000000000000044041500715745200157500ustar00rootroot00000000000000/* * As reported by yanait@gmail.com, large (16GB) arrays caused seeg fault on * 2 nodes 2 cores per node. * * The following program (just doing ga_acc to 16GB GA (Array * Dimensions:8192x262144) did not work, resulting in segmentation fault. * * % mpirun -npernode 2 -np 4 ./a.out * * Process=0 (node#0) owns array section: [0:4095,0:131071] * Process=1 (node#0) owns array section: [0:4095,131072:262143] * Process=2 (node#1) owns array section: [4096:8191,0:131071] * Process=3 (node#1) owns array section: [4096:8191,131072:262143] * * I found that a couple of subroutines in armci fail to handle large-size * arrays. * My debugging showed that the variables of int type overflow to calculate * address or pointer (over 2GB). * I made a patch file (see below). * This is related to my previous post regarding the kr_shmem_free error in * ga_matmul (also using ga_acc). */ #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include "mp3.h" #include "ga.h" #include "macdecls.h" /* create N1 x N2 matrix (double precision) */ #define N1 1024*8 #define N2 1024*256 int main(int argc, char **argv) { int me, nproc, g_a, i, j; int ndim=2, type=MT_F_DBL, dims[2]={N1,N2}; double *buf; int lo[2], hi[2], ld[1]; double alpha = 1.0; MP_INIT(argc,argv); GA_Initialize_ltd(-1); me=GA_Nodeid(); nproc=GA_Nnodes(); if(me==0) printf("Using %ld processes\n",(long)nproc); if(me==0) printf("memory = %ld bytes\n",((long)N1)*((long)N2)*8); g_a = NGA_Create(type, ndim, dims, "A", NULL); GA_Zero(g_a); /* zero the matrix */ GA_Print_distribution(g_a); if(me == 0) { buf = (double*)(malloc(N1*1024*sizeof(double))); for(j = 0; j < N1*1024; ++j) buf[j] = 1.0; for(i = 0; i < N2/1024; ++i) { lo[0] = 0; hi[0] = lo[0] + N1 -1; lo[1] = i*1024; hi[1] = lo[1] + 1024 -1; ld[0] = 1024; printf("NGA_Acc.%d: %d:%d %d:%d\n",i,lo[0],hi[0],lo[1],hi[1]); NGA_Init_fence(); NGA_Acc(g_a, lo, hi, buf, ld, &alpha); NGA_Fence(); } } GA_Sync(); GA_Destroy(g_a); GA_Terminate(); MP_FINALIZE(); return 0; } ga-5.9.2/global/testing/bin.F000066400000000000000000000454551500715745200157350ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif C C Test the minval, minloc, maxval, maxloc, and enum functions in GA. C program main implicit none #include "mafdecls.fh" #include "global.fh" integer heap, stack, fudge, ma_heap, me, nproc logical status parameter (heap=100*100*4, fudge=100, stack=100*100) c c*** Intitialize a message passing library c #include "mp3.fh" c c*** Initialize GA c c There are 2 choices: ga_initialize or ga_initialize_ltd. c In the first case, there is no explicit limit on memory usage. c In the second, user can set limit (per processor) in bytes. c call ga_initialize() nproc = ga_nnodes() me = ga_nodeid() c we can also use GA_set_memory_limit BEFORE first ga_create call c if(ga_nodeid().eq.0)then print *,' GA initialized ' call ffflush(6) endif c c*** Initialize the MA package c MA must be initialized before any global array is allocated c status = ma_init(MT_DCPL, stack, heap) if (.not. status) call ga_error('ma_init failed',-1) c if(me.eq.0)then print *, 'using ', nproc, ' process(es)' call ffflush(6) endif c call test_nga_bin() c if(me.eq.0) call ga_print_stats() c c*** Tidy up the GA package c call ga_terminate() c c*** Tidy up after message-passing library c call MP_FINALIZE() c end logical function ga_create_bin(nelem, type, name, g_off, g_bin) implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer nelem, type, g_off, g_bin character*1 name c integer lo,hi, map(100),i,me,nproc c nproc = ga_nnodes() me = ga_nodeid() c do i = 1,nproc map(i) = 0 enddo c c call ga_print_distribution(g_off) call nga_distribution(g_off,me,lo,hi) call ga_sync() c call nga_get(g_off,lo,lo,map(me+1),1) if(me.gt.0. and. map(me+1).eq.0)then print *,'my lo/hi in offset array',lo,hi,map(me+1) call ga_error('got 0',0) endif map(me+1) = map(me+1)+1 call ga_igop(1,map,nproc,'+') c ga_create_bin=nga_create_irreg(type,1,nelem,name,map,nproc,g_bin) end subroutine test_nga_bin() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" C integer g_ielm1, g_result1 ! handles to global arrays integer g_ibin, g_ibincnt, g_ibinoff ! handles to global arrays integer g_ibinga, g_idiff ! handles to global arrays integer g_ielm2, g_sbit2, g_src2, g_sink2, g_result2 ! handles to global arrays integer g_irowmat, g_icolmat ! handles to global arrays integer g_velm4, g_velm5 ! handles to global arrays C integer ilo, ihi, num integer ilo4, ihi4, num4 integer i, it, count integer imax_bin, imin_bin integer iboff1, iboff2 integer me, nproc ! my processor & number of procs integer ndim,dims(1),chunck(1) C integer nelements_max, nen_max parameter (nelements_max=1000, nen_max=4) integer ipack(nelements_max), * jpack(nelements_max), * kpack(nelements_max), * ielm1(nelements_max), * ielm2(nelements_max), * iarray(nelements_max), * ielm4(nen_max*nelements_max) double precision velm4(nen_max*nelements_max), * velm5(nen_max*nelements_max) C integer nbins_max parameter (nbins_max=1000) integer ibincnt(nbins_max), ibinoff(nbins_max) c integer ilocmax, ilocmin integer ilocmax_ga, ilocmin_ga integer imax_ga, imin_ga C integer nelements, nen integer isum, icount, jcount, itoff double precision result integer i1, i2, ibin logical ga_create_bin C C c c*** check parallel environment me = ga_nodeid() nproc = ga_nnodes() c nelements=80 nen=4 c c*** create a global 1-D array ndim=1 dims(1)=nen*nelements chunck(1)=min(1,dims(1)/nproc) if (.not. nga_create(MT_INT, ndim, dims, 'ielm1', chunck, $ g_ielm1)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'result1', chunck, $ g_result1)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'sbit2', chunck, $ g_sbit2)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'src2', chunck, $ g_src2)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'sink2', chunck, $ g_sink2)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'result2', chunck, $ g_result2)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'irowmat', chunck, $ g_irowmat)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'icolmat', chunck, $ g_icolmat)) $ call ga_error(' ga_create failed ',0) C if (.not. nga_create(MT_DBL, ndim, dims, 'velm4', chunck, $ g_velm4)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_DBL, ndim, dims, 'velm5', chunck, $ g_velm5)) $ call ga_error(' ga_create failed ',0) C c c Enumerate the sbit arrary to get a sequential vector. call ga_fill(g_ielm1,0) call ga_patch_enum(g_ielm1,1,nen*nelements,1,1) C *** ielm1: 1 2 3 4 5 ... nelements ... 0 0 0 0 0 c c*** compute local ilo, ihi, num for each processor call nga_distribution(g_ielm1,me,ilo,ihi,chunck) num=ihi-ilo+1 if(ihi.le.0) num=0 if(ihi.gt.nen*nelements) then ihi=nelements num=max(0,ihi-ilo+1) endif C*****print *,"Random: ",ilo,ihi,ndim,dims(1) if(num.gt.nelements_max) then print *,"Too many elements " goto 9999 endif C*****print *, 'me=',me, num,ilo,ihi c itoff=nen*nelements do i=ilo,ihi c print *, 'rand=',10.0*drand(0), c * nint(10.0*drand(0)), drand(0),drand(0) ielm1(i-ilo+1)=max(1,min(nbins_max,1+nint(20.0*drand(0)))) enddo call ssortii(ielm1,ielm1,num,1) if(num.gt.0) call nga_put(g_ielm1,ilo,ihi,ielm1,1) c if(num.gt.0) call nga_get(g_ielm1,ilo,ihi,ielm1,1) C*****print *,"ielm1: ",me,(ielm1(i-ilo+1),i=ilo,ihi) call ga_sync() C*****call ga_print(g_ielm1) c call nga_select_elem(g_ielm1,'max',imax_bin,ilocmax_ga) call nga_select_elem(g_ielm1,'min',imin_bin,ilocmin_ga) C ndim=1 dims(1)=imax_bin chunck(1)=min(1,dims(1)/nproc) if (.not. nga_create(MT_INT, ndim, dims, 'ibincnt', chunck, $ g_ibincnt)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'ibinoff', chunck, $ g_ibinoff)) $ call ga_error(' ga_create failed ',0) ndim=1 dims(1)=nen*nelements chunck(1)=min(1,dims(1)/nproc) if (.not. nga_create(MT_INT, ndim, dims, 'ibin', chunck, $ g_ibin)) $ call ga_error(' ga_create failed ',0) c ndim=1 dims(1)=nen*nelements chunck(1)=min(1,dims(1)/nproc) if (.not. nga_create(MT_INT, ndim, dims, 'idiff', chunck, $ g_idiff)) $ call ga_error(' ga_create failed ',0) c iboff1=1 iboff2=0 call ga_fill(g_src2,0) call ga_patch_enum(g_src2,1,itoff,1,1) if(num.gt.0) call nga_get(g_src2,ilo,ihi,ielm2,1) do ibin=imin_bin,imax_bin icount=0 do i=ilo,ihi if(ielm1(i-ilo+1).eq.ibin) then icount=icount+1 ipack(i-ilo+1)=1 else ipack(i-ilo+1)=0 endif enddo if(num.gt.0) then call nga_put(g_sbit2,ilo,ihi,ipack,1) endif if(icount.gt.0) then jcount=0 do i=ilo,ihi if(ipack(i-ilo+1).eq.1) then jcount=jcount+1 jpack(jcount)=i endif enddo icount=0 call ga_fill(g_sink2,0) call ga_pack(g_src2, g_sink2,g_sbit2,1,itoff,icount) iboff2=iboff1+icount-1 if(me.eq.0) print *,"Pack bin: ",ibin," count ",icount, * iboff1,iboff2 call nga_copy_patch('n',g_sink2,1,icount, * g_ibin,iboff1,iboff2) iboff1=iboff1+icount endif ibincnt(ibin)=icount call ga_sync() enddo if(me.eq.0) then call nga_put(g_ibincnt,1,imax_bin,ibincnt,1) icount=0 do i=1,imax_bin ibinoff(i)=icount icount=icount+ibincnt(i) print *, 'bin off',i, ibinoff(i) enddo call nga_put(g_ibinoff,1,imax_bin,ibinoff,1) endif C C*****call ga_print(g_ibin) C #if 1 if(.not.ga_create_bin(nen*nelements,MT_INT,'ibin',g_ibinoff, $ g_ibinga)) $ call ga_error(' ga_create failed ',0) #else ndim=1 dims(1)=nen*nelements chunck(1)=min(1,dims(1)/nproc) if (.not. nga_create(MT_INT, ndim, dims, 'ibinga', chunck, $ g_ibinga)) $ call ga_error(' ga_create failed ',0) #endif c c call ga_print_distribution(g_ibinga) c call ga_print_distribution(g_ibincnt) c call ga_print(g_ibinoff) call GA_bin_index(g_ibinga,g_ibincnt,g_ibinoff,ielm2,ielm1,num,1) * call ga_fill(g_ibinga,0) * call ga_copy(g_ibin,g_ibinga) C c call ga_print(g_ibinga) c call ga_print(g_ibincnt) if(num.gt.0) call nga_get(g_ibin ,ilo,ihi,ielm1,1) if(num.gt.0) call nga_get(g_ibinga,ilo,ihi,ielm2,1) isum=0 do i=1,num if(ielm1(i).ne.ielm2(i)) then isum=isum+1 endif enddo call ga_sync() C call ga_igop(MT_INT,isum,1,'+') if(me.eq.0) then if(isum.ne.0) then print *,"Binning: failed: ",isum else print *,"Binning: successful" endif endif C call ga_sync() C goto 9999 9999 continue C C .................................................................. C return end *dk,ssortii subroutine ssortii(x,y,n,kflag) C C ##################################################################### C C PURPOSE - C C None C C INPUT ARGUMENTS - C C None C C OUTPUT ARGUMENTS - C C None C C CHANGE HISTORY - C C $Log: not supported by cvs2svn $ CPVCS CPVCS Rev 1.2 08/03/95 13:53:18 dcg CPVCS replace print * with writloga calls CPVCS CPVCS Rev 1.1 04/20/95 11:28:46 ejl CPVCS Fixed message when number of items is not greater then zero. CPVCS CPVCS CPVCS Rev 1.0 02/14/95 14:39:22 dcg CPVCS Original version C C ###################################################################### C implicit real*8 (a-h,o-z) C***BEGIN PROLOGUE SSORT C***DATE WRITTEN 761101 (YYMMDD) C***REVISION DATE 861211 (YYMMDD) C***CATEGORY NO. N6A2B1 C***KEYWORDS LIBRARY=SLATEC, C TYPE=SINGLE PRECISION(SSORT-S DSORT-D ISORT-I),QUICKSORT, C SINGLETON QUICKSORT,SORT,SORTING C***AUTHOR JONES, R. E., (SNLA) C WISNIEWSKI, J. A., (SNLA) C***PURPOSE SSORT sorts array X and optionally makes the same C interchanges in array Y. The array X may be sorted in C increasing order or decreasing order. A slightly modified C QUICKSORT algorithm is used. C***DESCRIPTION C C Written by Rondall E. Jones C Modified by John A. Wisniewski to use the Singleton quicksort C algorithm. Date 18 November 1976. C C Abstract C SSORT sorts array X and optionally makes the same C interchanges in array Y. The array X may be sorted in C increasing order or decreasing order. A slightly modified C quicksort algorithm is used. C C Reference C Singleton, R. C., Algorithm 347, An Efficient Algorithm for C Sorting with Minimal Storage, CACM,12(3),1969,185-7. C C Description of Parameters C X - array of values to be sorted (usually abscissas) C Y - array to be (optionally) carried along C N - number of values in array X to be sorted C KFLAG - control parameter C =2 means sort X in increasing order and carry Y along. C =1 means sort X in increasing order (ignoring Y) C =-1 means sort X in decreasing order (ignoring Y) C =-2 means sort X in decreasing order and carry Y along. C***REFERENCES SINGLETON,R.C., ALGORITHM 347, AN EFFICIENT ALGORITHM C FOR SORTING WITH MINIMAL STORAGE, CACM,12(3),1969, C 185-7. C***ROUTINES CALLED XERROR C***END PROLOGUE SSORT integer X(N),Y(N),IL(21),IU(21) real*4 r character*80 logmess integer TY, TTY C***FIRST EXECUTABLE STATEMENT SSORT NN = N IF (NN.LT.0) THEN write(*,'(a)') x 'SSORT- THE NUMBER OF VALUES TO BE SORTED IS NEGATIVE' RETURN ELSEIF (NN.EQ.0) THEN write(*,'(a)') x 'SSORT- THE NUMBER OF VALUES TO BE SORTED IS ZERO' RETURN ENDIF C KK = IABS(KFLAG) IF ((KK.EQ.1).OR.(KK.EQ.2)) GO TO 15 write(*,'(a)') x 'SSORT- THE SORT CONTROL PARAMETER, K, WAS NOT 2, 1, -1, OR -2.' RETURN C C ALTER ARRAY X TO GET DECREASING ORDER IF NEEDED C 15 IF (KFLAG.GE.1) GO TO 30 DO 20 I=1,NN 20 X(I) = -X(I) 30 GO TO (100,200),KK C C SORT X ONLY C 100 CONTINUE M=1 I=1 J=NN R=.375 110 IF (I .EQ. J) GO TO 155 115 IF (R .GT. .5898437) GO TO 120 R=R+3.90625E-2 GO TO 125 120 R=R-.21875 125 K=I C SELECT A CENTRAL ELEMENT OF THE C ARRAY AND SAVE IT IN LOCATION T C*****IJ = I + IFIX (FLOAT (J-I) * sngl(R)) IJ = I + IFIX (FLOAT (J-I) * R) T=X(IJ) C IF FIRST ELEMENT OF ARRAY IS GREATER C THAN T, INTERCHANGE WITH T IF (X(I) .LE. T) GO TO 130 X(IJ)=X(I) X(I)=T T=X(IJ) 130 L=J C IF LAST ELEMENT OF ARRAY IS LESS THAN C T, INTERCHANGE WITH T IF (X(J) .GE. T) GO TO 140 X(IJ)=X(J) X(J)=T T=X(IJ) C IF FIRST ELEMENT OF ARRAY IS GREATER C THAN T, INTERCHANGE WITH T IF (X(I) .LE. T) GO TO 140 X(IJ)=X(I) X(I)=T T=X(IJ) GO TO 140 135 TT=X(L) X(L)=X(K) X(K)=TT C FIND AN ELEMENT IN THE SECOND HALF OF C THE ARRAY WHICH IS SMALLER THAN T 140 L=L-1 IF (X(L) .GT. T) GO TO 140 C FIND AN ELEMENT IN THE FIRST HALF OF C THE ARRAY WHICH IS GREATER THAN T 145 K=K+1 IF (X(K) .LT. T) GO TO 145 C INTERCHANGE THESE ELEMENTS IF (K .LE. L) GO TO 135 C SAVE UPPER AND LOWER SUBSCRIPTS OF C THE ARRAY YET TO BE SORTED IF (L-I .LE. J-K) GO TO 150 IL(M)=I IU(M)=L I=K M=M+1 GO TO 160 150 IL(M)=K IU(M)=J J=L M=M+1 GO TO 160 C BEGIN AGAIN ON ANOTHER PORTION OF C THE UNSORTED ARRAY 155 M=M-1 IF (M .EQ. 0) GO TO 300 I=IL(M) J=IU(M) 160 IF (J-I .GE. 1) GO TO 125 IF (I .EQ. 1) GO TO 110 I=I-1 165 I=I+1 IF (I .EQ. J) GO TO 155 T=X(I+1) IF (X(I) .LE. T) GO TO 165 K=I 170 X(K+1)=X(K) K=K-1 IF (T .LT. X(K)) GO TO 170 X(K+1)=T GO TO 165 C C SORT X AND CARRY Y ALONG C 200 CONTINUE M=1 I=1 J=NN R=.375 210 IF (I .EQ. J) GO TO 255 215 IF (R .GT. .5898437) GO TO 220 R=R+3.90625E-2 GO TO 225 220 R=R-.21875 225 K=I C SELECT A CENTRAL ELEMENT OF THE C ARRAY AND SAVE IT IN LOCATION T C*****IJ = I + IFIX (FLOAT (J-I) *sngl(R)) IJ = I + IFIX (FLOAT (J-I) * R) T=X(IJ) TY= Y(IJ) C IF FIRST ELEMENT OF ARRAY IS GREATER C THAN T, INTERCHANGE WITH T IF (X(I) .LE. T) GO TO 230 X(IJ)=X(I) X(I)=T T=X(IJ) Y(IJ)= Y(I) Y(I)=TY TY= Y(IJ) 230 L=J C IF LAST ELEMENT OF ARRAY IS LESS THAN C T, INTERCHANGE WITH T IF (X(J) .GE. T) GO TO 240 X(IJ)=X(J) X(J)=T T=X(IJ) Y(IJ)= Y(J) Y(J)=TY TY= Y(IJ) C IF FIRST ELEMENT OF ARRAY IS GREATER C THAN T, INTERCHANGE WITH T IF (X(I) .LE. T) GO TO 240 X(IJ)=X(I) X(I)=T T=X(IJ) Y(IJ)= Y(I) Y(I)=TY TY= Y(IJ) GO TO 240 235 TT=X(L) X(L)=X(K) X(K)=TT TTY= Y(L) Y(L)= Y(K) Y(K)=TTY C FIND AN ELEMENT IN THE SECOND HALF OF C THE ARRAY WHICH IS SMALLER THAN T 240 L=L-1 IF (X(L) .GT. T) GO TO 240 C FIND AN ELEMENT IN THE FIRST HALF OF C THE ARRAY WHICH IS GREATER THAN T 245 K=K+1 IF (X(K) .LT. T) GO TO 245 C INTERCHANGE THESE ELEMENTS IF (K .LE. L) GO TO 235 C SAVE UPPER AND LOWER SUBSCRIPTS OF C THE ARRAY YET TO BE SORTED IF (L-I .LE. J-K) GO TO 250 IL(M)=I IU(M)=L I=K M=M+1 GO TO 260 250 IL(M)=K IU(M)=J J=L M=M+1 GO TO 260 C BEGIN AGAIN ON ANOTHER PORTION OF C THE UNSORTED ARRAY 255 M=M-1 IF (M .EQ. 0) GO TO 300 I=IL(M) J=IU(M) 260 IF (J-I .GE. 1) GO TO 225 IF (I .EQ. 1) GO TO 210 I=I-1 265 I=I+1 IF (I .EQ. J) GO TO 255 T=X(I+1) TY= Y(I+1) IF (X(I) .LE. T) GO TO 265 K=I 270 X(K+1)=X(K) Y(K+1)= Y(K) K=K-1 IF (T .LT. X(K)) GO TO 270 X(K+1)=T Y(K+1)=TY GO TO 265 C C CLEAN UP C 300 IF (KFLAG.GE.1) RETURN DO I=1,NN X(I) = -X(I) END DO RETURN END ga-5.9.2/global/testing/blktest.F000066400000000000000000000705111500715745200166240ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c vector boxes lack arithmetic precision # define THRESH 1d-13 # define THRESHF 1e-5 #define MISMATCH(x,y) abs(x-y)/max(1d0,abs(x)).gt.THRESH #define MISMATCHF(x,y) abs(x-y)/max(1.0,abs(x)).gt.THRESHF #define USE_SCALAPACK 0 #define USE_TILED 0 #if USE_SCALAPACK #define USE_TILED 0 #endif #define SMALL_TEST 0 program main implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer heap, stack, fudge, ma_heap, me integer nmax, DIM #if SMALL_TEST parameter (nmax = 8, DIM = 2) #else parameter (nmax = 1001, DIM = 2) #endif integer ndim, nprocs, type, length integer g_a, g_b, g_c, g_d, g_e, g_f, g_h, g_i, g_j, inode MA_ACCESS_INDEX_TYPE index integer i, j, k, nb, dims(7) integer lo(7), hi(7), tlo(7), thi(7), t2lo(7), t2hi(7) integer block_list(10000), block_map(10000), nblock integer chunk(7), ld(7), block(7), proc_grid(7) integer a(nmax, nmax),b(nmax,nmax),e(nmax,nmax),f(nmax,nmax) integer skip(7), i_one, ialpha, ibeta, check_int double precision c(nmax,nmax),d(nmax,nmax), one, ddot double precision alpha, beta logical status parameter (heap=1600*1600*4, fudge=100, stack=1600*1600*4) c c*** Intitialize a message passing library c #include "mp3.fh" c c*** Initialize GA c call ga_initialize() me = ga_nodeid() c ma_heap = heap + fudge c if(me.eq.0)then write(6,100) call ffflush(6) 100 format(' GA initialized') endif c c*** Initialize the MA package c MA must be initialized before any global array is allocated c status = ma_init(MT_DBL, stack, ma_heap) if (.not. status) call ga_error('ma_init failed',-1) c if(me.eq.0)then write(6,101) ga_nnodes() call ffflush(6) 101 format(' Using ',i3,' process(es)') endif c c Create GA c ndim = DIM do i = 1, ndim dims(i) = nmax ld(i) = nmax lo(i) = 1 hi(i) = nmax chunk(i) = -1 #if SMALL_TEST block(i) = 2 #else block(i) = 87 #endif end do if (ga_nnodes().gt.1) then proc_grid(1) = 2 proc_grid(2) = ga_nnodes()/2 else proc_grid(1) = 1 proc_grid(2) = 1 endif c g_a = ga_create_handle() call ga_set_data(g_a, ndim, dims, MT_F_INT) call ga_set_chunk(g_a, chunk) #if USE_SCALAPACK || USE_TILED #if USE_SCALAPACK call ga_set_block_cyclic_proc_grid(g_a, block, proc_grid) #else call ga_set_tiled_proc_grid(g_a, block, proc_grid) #endif #else call ga_set_block_cyclic(g_a, block) #endif status = ga_allocate(g_a) call ga_zero(g_a) c g_b = ga_create_handle() call ga_set_data(g_b, ndim, dims, MT_F_DBL) call ga_set_chunk(g_b, chunk) #if USE_SCALAPACK || USE_TILED #if USE_SCALAPACK call ga_set_block_cyclic_proc_grid(g_b, block, proc_grid) #else call ga_set_tiled_proc_grid(g_b, block, proc_grid) #endif #else call ga_set_block_cyclic(g_b, block) #endif status = ga_allocate(g_b) call ga_zero(g_b) c g_c = ga_create_handle() call ga_set_data(g_c, ndim, dims, MT_F_INT) call ga_set_chunk(g_c, chunk) status = ga_allocate(g_c) call ga_zero(g_c) c g_d = ga_create_handle() call ga_set_data(g_d, ndim, dims, MT_F_INT) call ga_set_chunk(g_d, chunk) #if USE_SCALAPACK || USE_TILED #if USE_SCALAPACK call ga_set_block_cyclic_proc_grid(g_d, block, proc_grid) #else call ga_set_tiled_proc_grid(g_d, block, proc_grid) #endif #else call ga_set_block_cyclic(g_d, block) #endif status = ga_allocate(g_d) call ga_zero(g_d) c g_e = ga_create_handle() call ga_set_data(g_e, ndim, dims, MT_F_DBL) call ga_set_chunk(g_e, chunk) #if USE_SCALAPACK || USE_TILED #if USE_SCALAPACK call ga_set_block_cyclic_proc_grid(g_e, block, proc_grid) #else call ga_set_tiled_proc_grid(g_e, block, proc_grid) #endif #else call ga_set_block_cyclic(g_e, block) #endif status = ga_allocate(g_e) call ga_zero(g_e) c g_f = ga_create_handle() call ga_set_data(g_f, ndim, dims, MT_F_DBL) call ga_set_chunk(g_f, chunk) #if USE_SCALAPACK || USE_TILED #if USE_SCALAPACK call ga_set_block_cyclic_proc_grid(g_f, block, proc_grid) #else call ga_set_tiled_proc_grid(g_f, block, proc_grid) #endif #else call ga_set_block_cyclic(g_f, block) #endif status = ga_allocate(g_f) call ga_zero(g_f) c g_h = ga_create_handle() call ga_set_data(g_h, ndim, dims, MT_F_DBL) call ga_set_chunk(g_h, chunk) status = ga_allocate(g_h) call ga_zero(g_h) c g_i = ga_create_handle() call ga_set_data(g_i, ndim, dims, MT_F_DBL) call ga_set_chunk(g_i, chunk) status = ga_allocate(g_i) call ga_zero(g_i) c g_j = ga_create_handle() call ga_set_data(g_j, ndim, dims, MT_F_DBL) call ga_set_chunk(g_j, chunk) status = ga_allocate(g_j) call ga_zero(g_j) c if(me.eq.0)then write(6,102) call ffflush(6) 102 format(' Completed allocation of GAs') endif c c Initialize local arrays c #if SMALL_TEST tlo(1) = 3 thi(1) = 4 tlo(2) = 4 thi(2) = 7 t2lo(1) = 4 t2hi(1) = 5 t2lo(2) = 5 t2hi(2) = 8 #else tlo(1) = 3 thi(1) = 943 tlo(2) = 4 thi(2) = 547 t2lo(1) = 4 t2hi(1) = 944 t2lo(2) = 5 t2hi(2) = 548 #endif c one = 1.0d00 i_one = 1 k = 1 do i = 1, nmax do j = 1, nmax a(i,j) = k b(i,j) = 0 c(i,j) = dble(k) d(i,j) = 0.0d00 e(i,j) = 0 f(i,j) = 0 k = k + 1 end do end do c c Test PUT and GET c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Put and GA_Get...' write(6,*) endif c c Copy data from local array to global array c if (me.eq.0) then call nga_put(g_a,lo,hi,a,ld) endif c c Copy data from global array back to local array c call ga_sync call nga_get(g_a,lo,hi,e,ld) call ga_sync c do j = 1, nmax do i = 1, nmax if (a(i,j).ne.e(i,j)) then write(6,103) me,a(i,j),e(i,j) call ga_error('GA PUT and GET failed',me) endif e(i,j) = 0 end do end do 103 format('proc: ',i4,' a(i,j): ',i8,' e(i,j): ',i8) if (me.eq.0) then write(6,*) write(6,*) 'GA_Put and GA_Get are OK' write(6,*) endif c c Test ga_add c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Add...' write(6,*) endif if (me.eq.0) then call nga_put(g_c,lo,hi,a,ld) endif ialpha = 1 ibeta = -1 call ga_add(ialpha, g_a, ibeta, g_c, g_c) if (ga_idot(g_c,g_c).eq.0) then if(me.eq.0)then write(6,*) write(6,*) 'GA_Add operation for regular and block-cyclic', + ' arrays is OK' write(6,*) call ffflush(6) endif else if(me.eq.0)then write(6,*) write(6,*) 'GA_Add operation for regular and block-cyclic', + ' arrays is not OK' write(6,*) call ga_error('exiting', 1) call ffflush(6) endif endif c c Check add between two block-cyclic arrays c if (me.eq.0) then call nga_put(g_d,lo,hi,a,ld) endif call ga_add(ialpha, g_a, ialpha, g_d, g_d) c c Copy data to local buffer and then back to regular array c call nga_get(g_d,lo,hi,b,ld) if (me.eq.0) then call nga_get(g_c,lo,hi,b,ld) endif call ga_sync if (ga_idot(g_c,g_c).eq.0) then if(me.eq.0)then write(6,*) write(6,*) 'GA_Add operation for two block-cyclic', + ' arrays is OK' write(6,*) call ffflush(6) endif else if(me.eq.0)then write(6,*) write(6,*) 'GA_Add operation for two block-cyclic arrays', + ' is not OK' write(6,*) call ga_error('exiting', 1) call ffflush(6) endif endif c c Check to find which blocks correspond to local patch c #if SMALL_TEST if (me.eq.0) then write(6,*) write(6,*) 'Check nga_locate_region and nga_locate_num_blocks' write(6,*) 'functions' write(6,*) write(6,*) 'Printing original matrix' do i = 1, min(nmax,8) write(6,200) (a(i,j),j=1,min(nmax,8)) end do endif status=nga_locate_region(g_a,tlo,thi,block_map,block_list,nblock) nb = nga_locate_num_blocks(g_a,tlo,thi) if (me.eq.0) then write(6,*) write(6,111) nb do i = 1, nblock write(6,110) i,block_list(i),block_map(4*(i-1)+1), + block_map(4*(i-1)+3),block_map(4*(i-1)+2), + block_map(4*(i-1)+4) end do endif 110 format(i3,' block(',i3,') tlo(1): ',i3,' thi(1): ',i3, + ' tlo(2): ',i3,' thi(2): ',i3) 111 format(' Number of blocks: ',i3) if (me.eq.0) then call nga_access_block_segment(g_a,me,index,length) call print_block(int_mb(index),length) endif #endif c c Check onesided accumulate c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Acc...' write(6,*) endif if (me.eq.0) then call nga_acc(g_b,lo,hi,c,ld,one) endif call ga_sync if (me.eq.0) then call nga_acc(g_b,lo,hi,c,ld,one) endif call ga_sync if (me.eq.0) then call nga_put(g_h,lo,hi,c,ld) endif alpha = 1.0 beta = -2.0 call ga_add(alpha,g_b,beta,g_h,g_h) if (ga_ddot(g_h,g_h).eq.0.0d00) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Acc is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Acc is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c Check copy c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Copy...' write(6,*) endif call ga_copy(g_a, g_c) call ga_copy(g_c, g_d) call ga_add(ialpha,g_a,ibeta,g_d,g_c) if (ga_idot(g_c,g_c).eq.0) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Copy is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Copy is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c Check zero c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Zero...' write(6,*) call nga_get(g_a,lo,hi,a,ld) endif call ga_sync call ga_zero(g_a) call ga_copy(g_a,g_c) if (ga_idot(g_c,g_c).eq.0) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Zero is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Zero is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c Check scale c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Scale...' write(6,*) endif if (me.eq.0) then call nga_put(g_b,lo,hi,c,ld) call nga_put(g_h,lo,hi,c,ld) endif call ga_scale(g_b,2.0d00) call ga_scale(g_h,2.0d00) alpha = 1.0d00 beta = -1.0d00 call ga_add(alpha,g_b,beta,g_h,g_h) if (ga_ddot(g_h,g_h).eq.0.0d00) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Scale is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Scale is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c Check fill c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Fill...' write(6,*) endif call ga_fill(g_a,1) call ga_fill(g_c,1) call ga_add(ialpha,g_a,ibeta,g_c,g_c) if (ga_idot(g_c,g_c).eq.0) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Fill is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Fill is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c Check zero_patch c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Zero_patch...' write(6,*) endif call ga_copy(g_a,g_c) call nga_zero_patch(g_a,tlo,thi) call nga_zero_patch(g_c,tlo,thi) call ga_add(ialpha,g_a,ibeta,g_c,g_c) if (ga_idot(g_c,g_c).eq.0) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Zero_patch is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Zero_patch is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c Check fill_patch c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Fill_patch...' write(6,*) endif call ga_copy(g_a,g_c) call nga_fill_patch(g_a,tlo,thi,2) call nga_fill_patch(g_c,tlo,thi,2) call ga_add(ialpha,g_a,ibeta,g_c,g_c) if (ga_idot(g_c,g_c).eq.0) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Fill_patch is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Fill_patch is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c Check scale_patch c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Scale_patch...' write(6,*) endif call ga_copy(g_a,g_c) call nga_scale_patch(g_a,tlo,thi,2) call nga_scale_patch(g_c,tlo,thi,2) call ga_add(ialpha,g_a,ibeta,g_c,g_c) if (ga_idot(g_c,g_c).eq.0) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Scale_patch is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Scale_patch is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c Check copy_patch c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Copy_patch...' write(6,*) endif call ga_fill(g_a,1) call ga_fill(g_c,1) call nga_fill_patch(g_a,tlo,thi,2) call nga_copy_patch('n',g_a,tlo,thi,g_c,t2lo,t2hi) call ga_fill(g_a,1) call nga_fill_patch(g_a,t2lo,t2hi,2) call ga_add(ialpha,g_a,ibeta,g_c,g_c) if (ga_idot(g_c,g_c).eq.0) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Copy_patch from block-cyclic to regular is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Copy_patch from block-cyclic to regular is', + ' not OK' write(6,*) call ga_error('exiting', 1) endif endif call ga_fill(g_a,1) call ga_fill(g_c,1) call nga_fill_patch(g_c,tlo,thi,2) call nga_copy_patch('n',g_c,tlo,thi,g_a,t2lo,t2hi) call ga_fill(g_c,1) call nga_fill_patch(g_c,t2lo,t2hi,2) call ga_add(ialpha,g_a,ibeta,g_c,g_c) if (ga_idot(g_c,g_c).eq.0) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Copy_patch from regular to block-cyclic is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Copy_patch from regular to block-cyclic is', + ' not OK' write(6,*) call ga_error('exiting', 1) endif endif c c test ga_add_patch c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Add_patch...' write(6,*) endif call ga_zero(g_b) call ga_zero(g_e) call ga_zero(g_f) call ga_zero(g_h) call ga_zero(g_i) call ga_zero(g_j) if (me.eq.0) then call nga_put(g_e,t2lo,t2hi,c,ld) call nga_put(g_f,t2lo,t2hi,c,ld) call nga_put(g_i,t2lo,t2hi,c,ld) call nga_put(g_j,t2lo,t2hi,c,ld) endif call ga_sync call nga_add_patch(one,g_e,t2lo,t2hi,one,g_f,tlo,thi,g_b,tlo,thi) call nga_add_patch(one,g_i,t2lo,t2hi,one,g_j,tlo,thi,g_h,tlo,thi) call ga_add(alpha,g_b,beta,g_h,g_i) if (ga_ddot(g_i,g_i).eq.0.0d00) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Add_patch is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Add_patch is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c test ga_ddot c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Ddot...' write(6,*) endif call ga_fill(g_b, 2.0d00) call ga_fill(g_b, 2.0d00) ddot = ga_ddot(g_b, g_b) if (me.eq.0) then write(6,112) ddot,dble(nmax*nmax*4) endif 112 format(' Value of DDOT: ',f12.2,' Expected value: ',f12.2) if (me.eq.0) write(6,*) c c test ga_ddot_patch c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Ddot_patch...' write(6,*) endif ddot = nga_ddot_patch(g_b, 'n', tlo, thi, g_b, 'n', tlo, thi) if (me.eq.0) then write(6,113) ddot,dble((thi(1)-tlo(1)+1)*(thi(2)-tlo(2)+1)*4) endif 113 format(' Value of DDOT_PATCH: ',f12.2,' Expected value: ',f12.2) c c test ga_abs_value_patch c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Abs_patch...' write(6,*) endif call ga_fill(g_a,-1) call ga_abs_value_patch(g_a, tlo, thi) call ga_fill(g_c,-1) call ga_abs_value_patch(g_c, tlo, thi) call ga_add(ialpha,g_a,ibeta,g_c,g_c) if (ga_idot(g_c,g_c).eq.0) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Abs_value_patch is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Abs_value_patch is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c test ga_elem_multiply c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Elem_multiply...' write(6,*) endif call ga_fill(g_b, 2.0d00) call ga_fill(g_e, 3.0d00) call ga_zero(g_f) call ga_elem_multiply(g_b, g_e, g_f) call ga_fill(g_h, 2.0d00) call ga_fill(g_i, 3.0d00) call ga_zero(g_j) call ga_elem_multiply(g_h, g_i, g_j) call ga_add(alpha,g_f,beta,g_j,g_h) if (ga_ddot(g_h,g_h).eq.0.0d00) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Elem_multiply is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Elem_multiply is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c test ga_elem_divide_patch c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Elem_divide_patch...' write(6,*) endif call ga_fill(g_b, 3.0d00) call ga_elem_divide_patch(g_f,t2lo,t2hi,g_e,tlo,thi,g_b,tlo,thi) call ga_fill(g_h, 3.0d00) call ga_elem_divide_patch(g_j,t2lo,t2hi,g_i,tlo,thi,g_h,tlo,thi) call ga_add(alpha,g_b,beta,g_h,g_h) if (ga_ddot(g_h,g_h).eq.0.0d00) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Elem_divide_patch is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Elem_divide_patch is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c test strided get c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Strided_get...' write(6,*) endif skip(1) = 2 skip(2) = 2 do j = 1, nmax do i = 1, nmax a(i,j) = 1 if (mod(i-1,2).eq.0.and.mod(j-1,2).eq.0) then b(i,j) = 1 else b(i,j) = 0 end if end do end do call ga_zero(g_a) call ga_zero(g_c) call nga_put(g_a,lo,hi,a,ld) call nga_put(g_c,lo,hi,b,ld) call ga_sync do j = 1, nmax do i = 1, nmax a(i,j) = 0 b(i,j) = 0 end do end do if (me.eq.0) then call nga_strided_get(g_a,lo,hi,skip,a,ld) call nga_strided_get(g_c,lo,hi,skip,b,ld) endif call ga_sync nb = 0 if (me.eq.0) then do i = 1, nmax do j = 1, nmax if (a(i,j).ne.b(i,j)) nb = nb + 1 end do end do if (nb.eq.0) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Strided_get is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Strided_get is not OK' write(6,*) call ga_error('exiting', 1) endif endif endif c c test strided put c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Strided_put...' write(6,*) endif do j = 1, nmax do i = 1, nmax a(i,j) = 1 if (mod(i-1,2).eq.0.and.mod(j-1,2).eq.0) then b(i,j) = 1 else b(i,j) = 0 end if end do end do call ga_zero(g_a) call ga_zero(g_c) if (me.eq.0) then call nga_strided_put(g_a,lo,hi,skip,a,ld) call nga_put(g_c,lo,hi,b,ld) endif call ga_sync call ga_add(ialpha,g_a,ibeta,g_c,g_c) if (ga_idot(g_c,g_c).eq.0) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Strided_put is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Strided_put is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c test strided accumulate c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Strided_acc...' write(6,*) endif do j = 1, nmax do i = 1, nmax a(i,j) = 1 if (mod(i-1,2).eq.0.and.mod(j-1,2).eq.0) then b(i,j) = 2 else b(i,j) = 0 end if end do end do call ga_zero(g_a) call ga_zero(g_c) if (me.eq.0) then call nga_strided_acc(g_a,lo,hi,skip,a,ld,i_one) call nga_strided_acc(g_a,lo,hi,skip,a,ld,i_one) call nga_put(g_c,lo,hi,b,ld) endif call ga_sync call ga_add(ialpha,g_a,ibeta,g_c,g_c) if (ga_idot(g_c,g_c).eq.0) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Strided_acc is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Strided_acc is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c test transpose c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_transpose...' write(6,*) endif k = 0 do i = 1, nmax do j = 1, nmax k = k+1 c(i,j) = dble(k) end do end do if (me.eq.0) then call nga_put(g_b,lo,hi,c,ld) call nga_put(g_h,lo,hi,c,ld) endif call ga_sync call ga_transpose(g_b,g_i) call ga_transpose(g_h,g_j) call ga_add(alpha,g_i,beta,g_j,g_h) if (ga_ddot(g_h,g_h).eq.0.0d00) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Transpose is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Transpose is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c test symmetrize c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_symmetrize...' write(6,*) endif if (me.eq.0) then call nga_put(g_b,lo,hi,c,ld) call nga_put(g_h,lo,hi,c,ld) endif call ga_sync call ga_symmetrize(g_b) call ga_symmetrize(g_h) call ga_add(alpha,g_b,beta,g_h,g_h) if (ga_ddot(g_h,g_h).eq.0.0d00) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Symmetrize is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Symmetrize is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c test periodic get c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Periodic_get...' write(6,*) endif tlo(1) = lo(1) + 2 thi(1) = hi(1) + 2 tlo(2) = lo(2) + 2 thi(2) = hi(2) + 2 k = 0 do i = 1, nmax do j = 1, nmax k = k + 1 a(i,j) = k end do end do if (me.eq.0) then call nga_put(g_a,lo,hi,a,ld) call nga_put(g_c,lo,hi,a,ld) endif call ga_sync if (me.eq.0) then call nga_periodic_get(g_a,tlo,thi,a,ld) call nga_periodic_get(g_c,tlo,thi,b,ld) endif call ga_sync nb = 0 if (me.eq.0) then do i = 1, nmax do j = 1, nmax if (a(i,j).ne.b(i,j)) nb = nb + 1 end do end do if (nb.eq.0) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Periodic_get is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Periodic_get is not OK' write(6,*) call ga_error('exiting', 1) endif endif endif c c test periodic put c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Periodic_put...' write(6,*) endif k = 0 do i = 1, nmax do j = 1, nmax k = k + 1 a(i,j) = k end do end do if (me.eq.0) then call nga_periodic_put(g_a,tlo,thi,a,ld) call nga_periodic_put(g_c,tlo,thi,a,ld) endif call ga_sync call ga_add(ialpha,g_a,ibeta,g_c,g_c) if (ga_idot(g_c,g_c).eq.0) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Periodic_put is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Periodic_put is not OK' write(6,*) call ga_error('exiting', 1) endif endif c c test periodic accumulate c if (me.eq.0) then write(6,*) write(6,*) 'Testing GA_Periodic_acc...' write(6,*) endif call ga_fill(g_b, 1.0d00) call ga_fill(g_h, 1.0d00) call ga_sync if (me.eq.0) then call nga_periodic_acc(g_b,tlo,thi,c,ld,one) call nga_periodic_acc(g_h,tlo,thi,c,ld,one) endif call ga_sync call ga_add(alpha,g_b,beta,g_h,g_h) if (ga_ddot(g_h,g_h).eq.0.0d00) then if (me.eq.0) then write(6,*) write(6,*) 'GA_Periodic_acc is OK' write(6,*) endif else if (me.eq.0) then write(6,*) write(6,*) 'GA_Periodic_acc is not OK' write(6,*) call ga_error('exiting', 1) endif endif 200 format(8i8) c 300 format(8f8.1) if (me.eq.0) then write(6,302) endif 302 format(' Finished tests: success') status = ga_destroy(g_a) status = ga_destroy(g_b) status = ga_destroy(g_c) status = ga_destroy(g_d) status = ga_destroy(g_e) status = ga_destroy(g_f) call ga_terminate() c c*** Tidy up after message-passing library c call MP_FINALIZE() c stop end c subroutine print_block(a,length) implicit none #include "mafdecls.fh" #include "global.fh" integer length, i, j integer a(length) write(6,*) write(6,200) do i=1, 4 write(6,100) (a(4*(j-1)+i), j=1,4) 100 format(8i8) end do 200 format(' Write contents of processor') return end ga-5.9.2/global/testing/cache_test.c000066400000000000000000000233231500715745200173120ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #include #include "ga.h" #include "macdecls.h" #include "mp3.h" #define DIM 2 #define DIMSIZE 1024 #define SIZE DIMSIZE/2 #define MAX_FACTOR 256 void grid_factor(int p, int *idx, int *idy) { int i, j; int ip, ifac, pmax; int prime[MAX_FACTOR]; int fac[MAX_FACTOR]; int ix, iy; int ichk; i = 1; //find all prime numbers, besides 1, less than or equal to p ip = p; pmax = 0; for (i=2; i<=ip; i++) { ichk = 1; for (j=0; j MAX_FACTOR) printf("Overflow in grid_factor\n"); prime[pmax-1] = i; } } //find all prime factors of p ip = p; ifac = 0; for (i=0; i= 0; i--) { ix = *idx; iy = *idy; if (ix <= iy) { *idx = fac[i]*(*idx); } else { *idy = fac[i]*(*idy); } } } int main(int argc, char **argv) { int g_a, g_b, g_c; int one = 1; int rank, nprocs, kdim; int i, j, k; int ipx, ipy; int pdx, pdy; int xdim, ydim; int xbl, ybl; int xcdim, ycdim; int xnbl, ynbl; int xcnt, ycnt; int nb, ind, istart; int local_test; int ldtest = 5; int *local_A = NULL; int *local_B = NULL; double delta_t; int test; int dimsize; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); nprocs = GA_Nnodes(); rank = GA_Nodeid(); for (test = 0; test <= 1; test++) { for (dimsize = 4; dimsize <=4096; dimsize *= 2){ if (rank == 0) fprintf(stderr,"dim:%d start\n",dimsize); int *abuf, *bbuf, *cbuf, *ctest; int full_size = dimsize*dimsize; int alo[DIM] = {dimsize-dimsize,dimsize-dimsize}; int ahi[DIM] = {dimsize-1,dimsize-1}; int blo[DIM] = {dimsize-dimsize,dimsize-dimsize}; int bhi[DIM] = {dimsize-1,dimsize-1}; int clo[DIM] = {dimsize-dimsize,dimsize-dimsize}; int chi[DIM] = {dimsize-1,dimsize-1}; int loC[DIM] = {dimsize-dimsize,dimsize-dimsize}; int hiC[DIM] = {dimsize-1,dimsize-1}; int loA[DIM] = {dimsize-dimsize,dimsize-dimsize}; int hiA[DIM] = {dimsize-1,dimsize-1}; int loB[DIM] = {dimsize-dimsize,dimsize-dimsize}; int hiB[DIM] = {dimsize-1,dimsize-1}; int dims[DIM]={dimsize,dimsize}; int ldb = hiB[0]-loB[0]+1; int ldc = hiC[0]-loC[0]+1; int lda = hiA[0]-loA[0]+1; int nlo, ldC; char* read_only = "read_only"; char* read_cache = "read_cache"; //subarray variables int sub_size; int *fullarray_test = (int*)malloc(full_size*sizeof(int)); int *subarray_test; int loS[DIM], hiS[DIM]; int lds; local_A=(int*)malloc(full_size*sizeof(int)); for (i=0;i= xdim) hiC[0] = xdim - 1; if (hiC[0] >= ydim) hiC[0] = ydim - 1; // stop to avoid FPE if ( hiC[0] < loC[0] ){ fprintf(stderr,"%d: bad values for HiC LoC: %d %d\n", rank, hiC[0], loC[0]); fprintf(stderr,"%dL xcnt xbl: %d %d\n", rank, xcnt, xbl); GA_Error("cache_test: div by zero", 0); } // Calculating number of blocks for inner dimension num_blocks = dimsize/(hiC[0]-loC[0]+1); if (num_blocks*(hiC[0]-loC[0]+1) < dimsize) num_blocks++; //set up buffers offset = 0; elemsize = sizeof(int); c_buf = (void*)malloc((hiC[0]-loC[0]+1)*(hiC[1]-loC[1]+1)*elemsize); a_buf = (void*)malloc((hiC[0]-loC[0]+1)*(hiC[1]-loC[1]+1)*elemsize); b_buf = (void*)malloc((hiC[0]-loC[0]+1)*(hiC[1]-loC[1]+1)*elemsize); test_buf = (hiC[0]-loC[0]+1)*(hiC[1]-loC[1]+1)*elemsize; size_c = (hiC[0]-loC[0]+1)*(hiC[1]-loC[1]+1); ldC = hiC[1]-loC[1]+1; // calculate starting block index istart = (loC[0]-clo[0])/(hiC[0]-loC[0]+1); // loop over block pairs for (nb=0; nb ahi[1]) hiA[1] = ahi[1]; ld = hiA[0]-loA[0]+1; size_a = (hiA[0]-loA[0]+1)*(hiA[1]-loA[1]+1); NGA_Get(g_a,loA,hiA,a_buf,&ld); if (dimsize > 4) { sub_size = (hiC[0]-loC[0]-1)*(hiC[1]-loC[1]-1); subarray_test = (int*)malloc(sub_size*sizeof(int)); loS[0] = loA[0]+1; loS[1] = loA[1]+1; hiS[0] = hiA[0]-1; hiS[1] = hiA[1]-1; lds = hiS[0]-loS[0]+1; NGA_Get(g_a,loS,hiS,subarray_test,&lds); } loB[1] = loC[1]; hiB[1] = hiC[1]; nlo = blo[0]+ind*(hiC[0]-loC[0]+1); loB[0] = nlo; hiB[0] = loB[0]+(hiC[0]-loC[0]); if (hiB[0] > bhi[0]) hiB[0] = bhi[0]; ld = hiB[0]-loB[0]+1; size_b = (hiB[0]-loB[0]+1)*(hiB[1]-loB[1]+1); NGA_Get(g_b,loB,hiB,b_buf,&ld); xcdim = hiC[0] - loC[0] + 1; ycdim = hiC[1] - loC[1] + 1; for (i = 0; i < xcdim; i++) { for (j = 0; j < ycdim; j++) { c_buf[i*ycdim+j] = 0.0; } } //transpose B to reduce page faults kdim = hiA[1] - loA[1] + 1; for (i = 0; i < xcdim; i++) { for (j = 0; j < ycdim; j++) { for (k = 0; k < kdim; k++) { c_buf[i*ycdim+j] += a_buf[i*kdim+k]*b_buf[k*ycdim+j]; } } } NGA_Acc(g_c, loC, hiC, c_buf, &ldC, &one); } // multiplication is done, free buffers free(a_buf); free(b_buf); free(c_buf); xcnt += pdx; if (xcnt >= xnbl) { xcnt = ipx; ycnt += pdy; } } delta_t = GA_Wtime()-delta_t; if (dimsize == 0 & rank == 0) printf("\n"); if (test == 0 && rank == 0) printf("READ - DIMSIZE: %5d Time (us): %7.4f\n",dimsize,delta_t*1.0e6); if (test == 1 && rank == 0) printf("CACHE - DIMSIZE: %5d Time (us): %7.4f\n",dimsize,delta_t*1.0e6); GA_Sync(); NGA_Unset_property(g_a); NGA_Unset_property(g_b); #if 0 /* check multipy for correctness */ abuf = (int*)malloc(sizeof(int)*dimsize*dimsize); bbuf = (int*)malloc(sizeof(int)*dimsize*dimsize); cbuf = (int*)malloc(sizeof(int)*dimsize*dimsize); ctest = (int*)malloc(sizeof(int)*dimsize*dimsize); /* get data from matrices a, b, c */ NGA_Get(g_a,alo,ahi,abuf,&dimsize); NGA_Get(g_b,blo,bhi,bbuf,&dimsize); NGA_Get(g_c,clo,chi,ctest,&dimsize); for (i=0; i #include #include #include "mpi.h" #include "ga.h" #include "mp3.h" #include "macdecls.h" #define DIM 256 void comm_test(MPI_Comm comm) { double *buf; int i, j, ii, jj, ld; int lo[2], hi[2]; int dims[2]; int two = 2; int g_a; int me, nprocs, prem; int nelems, ok; int rank; MPI_Comm_rank(comm,&rank); if (GA_Initialize_comm(comm)) { me = GA_Nodeid(); nprocs = GA_Nnodes(); prem = (me+1)%nprocs; if (me == 0) { printf("\nRunning comm_test on %d processors\n\n",nprocs); } g_a = NGA_Create_handle(); dims[0] = DIM; dims[1] = DIM; NGA_Set_data(g_a,two,dims,C_DBL); NGA_Allocate(g_a); GA_Zero(g_a); /* Find out what section of global array I'm writing to */ NGA_Distribution(g_a,prem,lo,hi); nelems = (hi[0]-lo[0]+1)*(hi[1]-lo[1]+1); ld = hi[1]-lo[1]+1; buf = (double*)malloc(nelems*sizeof(double)); for (i=lo[0]; i<=hi[0]; i++) { ii = i-lo[0]; for (j=lo[1]; j<=hi[1]; j++) { jj = j-lo[1]; buf[ii*ld+jj] = (double)(i*DIM+j); } } NGA_Put(g_a,lo,hi,buf,&ld); GA_Sync(); free(buf); /* Copy data from a different section of global array and check values */ prem = me-1; if (prem < 0) prem = nprocs-1; NGA_Distribution(g_a,prem,lo,hi); nelems = (hi[0]-lo[0]+1)*(hi[1]-lo[1]+1); ld = hi[1]-lo[1]+1; buf = (double*)malloc(nelems*sizeof(double)); for (i=0; i #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STDIO_H # include #endif #include "ga.h" #include "macdecls.h" #include "mp3.h" #include "../src/globalp.h" #include "ga-papi.h" #define BLOCK_CYCLIC 0 #define USE_SCALAPACK 0 #define USE_TILED 0 #if !BLOCK_CYCLIC #define USE_SCALAPACK 0 #define USE_TILED 0 #endif #if USE_SCALAPACK #define USED_TILED 0 #endif #ifndef GA_HALF_MAX_INT #define GA_HALF_MAX_INT ((((int)1) << ((int)(8*sizeof(int))-2)) - 1) #endif #ifndef GA_INFINITY_I #define GA_INFINITY_I (GA_HALF_MAX_INT + GA_HALF_MAX_INT + 1) /* Original value below. Seemed too small arbitrarily. #define GA_INFINITY_I 100000 */ #endif #ifndef GA_NEGATIVE_INFINITY_I #define GA_NEGATIVE_INFINITY_I (- GA_INFINITY_I) /* Original value below. Seemed too small arbitrarily. #define GA_NEGATIVE_INFINITY_I -100000 */ #endif #ifndef GA_HALF_MAX_LONG #define GA_HALF_MAX_LONG ((((long)1) << ((int)(8*sizeof(long))-2)) - 1) #endif #ifndef GA_INFINITY_L #define GA_INFINITY_L (GA_HALF_MAX_LONG + GA_HALF_MAX_LONG + 1) /* Original value was #define GA_INFINITY_L 100000 */ #endif #ifndef GA_NEGATIVE_INFINITY_L #define GA_NEGATIVE_INFINITY_L (- GA_INFINITY_L) #endif /* Original value was: #define GA_NEGATIVE_INFINITY_L -100000 */ /* Modified by Doug Baxter 01/24/04 to distinguish between Double inifinity and float infinity. #ifndef GA_INFINITY #define GA_INFINITY 1.0e20 #endif #ifndef GA_NEGATIVE_INFINITY #define GA_NEGATIVE_INFINITY -1.0e20 #endif */ #ifndef GA_INFINITY_F #define GA_INFINITY_F 1.0e37 #endif /* Original value below. #define GA_INFINITY_F 1.0e20 */ #ifndef GA_NEGATIVE_INFINITY_F #define GA_NEGATIVE_INFINITY_F -1.0e37 #endif /* Original value below. #define GA_NEGATIVE_INFINITY_F -1.0e20 */ #ifndef GA_INFINITY_D #define GA_INFINITY_D 1.0e307 #endif /* Original value below. #define GA_INFINITY_D 1.0e20 */ #ifndef GA_NEGATIVE_INFINITY_D #define GA_NEGATIVE_INFINITY_D -1.0e307 #endif #define THRESH 1e-5 #define MISMATCHED(x,y) GA_ABS((x)-(y))>=THRESH #define N 100 #define BLOCK_SIZE 20 #define OP_ELEM_MULT 0 #define OP_ELEM_DIV 1 #define OP_ELEM_MAX 2 #define OP_ELEM_MIN 3 #define OP_ABS 4 #define OP_ADD_CONST 5 #define OP_RECIP 6 #define OP_STEP_MAX 7 #define OP_STEP_BOUND_INFO 8 #define MY_TYPE 2002 Integer _ga_lo[MAXDIM], _ga_hi[MAXDIM], _ga_work[MAXDIM]; # define COPYINDEX_C2F(carr, farr, n){\ int i; for(i=0; i< (n); i++)(farr)[n-i-1]=(Integer)(carr)[i]+1;} void nga_vfill_patch(Integer *g_a, Integer *lo, Integer *hi); void nga_pnfill_patch(Integer *g_a, Integer *lo, Integer *hi); void NGA_Vfill_patch(int g_a, int lo[], int hi[]) { Integer a=(Integer)g_a; Integer ndim = pnga_ndim(a); COPYINDEX_C2F(lo,_ga_lo, ndim); COPYINDEX_C2F(hi,_ga_hi, ndim); nga_vfill_patch(&a, _ga_lo, _ga_hi); } void NGA_Pnfill_patch(int g_a, int lo[], int hi[]) { Integer a=(Integer)g_a; Integer ndim = pnga_ndim(a); COPYINDEX_C2F(lo,_ga_lo, ndim); COPYINDEX_C2F(hi,_ga_hi, ndim); nga_pnfill_patch(&a, _ga_lo, _ga_hi); } int ifun (int k) { int result; result = -k - 1; result = -2; return result; } int ifun2 (int k) { int result; result = k + 1; result = -3; return result; } void fill_func (int nelem, int type, void *buf) { int i; switch (type) { case C_FLOAT: for (i = 0; i < nelem; i++) ((float *) buf)[i] = (float) ifun (i); break; case C_LONG: for (i = 0; i < nelem; i++) ((long *) buf)[i] = (long) ifun (i); break; case C_DBL: for (i = 0; i < nelem; i++) ((double *) buf)[i] = (double) ifun (i); break; case C_DCPL: for (i = 0; i < 2 * nelem; i++) ((double *) buf)[i] = (double) ifun (i); break; case C_SCPL: for (i = 0; i < 2 * nelem; i++) ((float *) buf)[i] = (float) ifun (i); break; case C_INT: for (i = 0; i < nelem; i++) ((int *) buf)[i] = ifun (i); break; default: GA_Error (" wrong data type ", type); } } void fill_func2 (int nelem, int type, void *buf) { /* int i,size=MA_sizeof(MT_CHAR,type,1);*/ int i; switch (type) { case C_FLOAT: for (i = 0; i < nelem; i++) ((float *) buf)[i] = (float) ifun2 (i); break; case C_LONG: for (i = 0; i < nelem; i++) ((long *) buf)[i] = (long) ifun2 (i); break; case C_DBL: for (i = 0; i < nelem; i++) ((double *) buf)[i] = (double) ifun2 (i); break; case C_DCPL: for (i = 0; i < 2 * nelem; i++) ((double *) buf)[i] = (double) ifun2 (i); break; case C_SCPL: for (i = 0; i < 2 * nelem; i++) ((float *) buf)[i] = (float) ifun2 (i); break; case C_INT: for (i = 0; i < nelem; i++) ((int *) buf)[i] = ifun2 (i); break; default: GA_Error (" wrong data type ", type); } } void fill_func3 (int nelem, int type, void *buf) /*taking the absolute of the ifun() */ { /*int i,size=MA_sizeof(MT_CHAR,type,1);*/ int i; switch (type) { case C_FLOAT: for (i = 0; i < nelem; i++) ((float *) buf)[i] = (float) GA_ABS (ifun (i)); break; case C_LONG: for (i = 0; i < nelem; i++) ((long *) buf)[i] = (long) GA_ABS (ifun (i)); break; case C_DBL: for (i = 0; i < nelem; i++) ((double *) buf)[i] = (double) GA_ABS (ifun (i)); break; case C_DCPL: for (i = 0; i < 2 * nelem - 1; i = i + 2) { ((double *) buf)[i] = sqrt ((double) (ifun (i) * ifun (i) + ifun (i + 1) * ifun (i + 1))); ((double *) buf)[i + 1] = 0.0; } break; case C_SCPL: for (i = 0; i < 2 * nelem - 1; i = i + 2) { ((float *) buf)[i] = sqrt ((float) (ifun (i) * ifun (i) + ifun (i + 1) * ifun (i + 1))); ((float *) buf)[i + 1] = 0.0; } break; case C_INT: for (i = 0; i < nelem; i++) ((int *) buf)[i] = GA_ABS (ifun (i)); break; default: GA_Error (" wrong data type ", type); } } int test_fun (int type, int dim, int OP) { DoubleComplex *dcptr; int ld[7],locp[7],hicp[7]; void *boundminx=NULL,*boundmaxx=NULL,*wolfeminx=NULL; double boundmind=0,boundmaxd=0,wolfemind=0,aboundmind=0,aboundmaxd=0,awolfemind=0; long boundminl=0,boundmaxl=0,wolfeminl=0,aboundminl=0,aboundmaxl=0,awolfeminl=0; float boundminf=0,boundmaxf=0,wolfeminf=0,aboundminf=0,aboundmaxf=0,awolfeminf=0; int boundmini=0,boundmaxi=0,wolfemini=0,aboundmini=0,aboundmaxi=0,awolfemini=0; int g_a, g_b, g_c, g_d, g_e; int g_f, g_g, g_h, g_i, g_j; int g_k, g_l, g_m, g_n; int n = N; int me = GA_Nodeid (); int i; int dims[MAXDIM]; int lo[MAXDIM], hi[MAXDIM]; int index[MAXDIM]; int index2[MAXDIM]; int index3[MAXDIM]; int block_size[MAXDIM], proc_grid[MAXDIM], proc_cnt; int needs_scaled_result; void *val=NULL; void *val3=NULL; int ival = -2; double dval = -2.0; float fval = -2.0; long lval = -2; DoubleComplex dcval; SingleComplex fcval; void *val2=NULL; void *val4=NULL; int ival2 = -3; double dval2 = -3.0; float fval2 = -3.0; long lval2 = -3; void *val5=NULL; int ival5 = 5; long lval5 = 5; double dval5 = 5.0; float fval5 = 5.0; DoubleComplex dcval2; DoubleComplex dcval3; DoubleComplex dcval4; DoubleComplex dcval5; DoubleComplex dcval6; DoubleComplex dcval7; SingleComplex fcval2; SingleComplex fcval3; SingleComplex fcval4; SingleComplex fcval5; SingleComplex fcval6; SingleComplex fcval7; void *vresult=NULL; int ivresult; double dvresult; float fvresult; long lvresult; DoubleComplex dcvresult; SingleComplex fcvresult; void *bvresult=NULL; DoubleComplex dcbvresult; SingleComplex fcbvresult; int ok = 1; int result=0,result2=0,result3=0; void *max=NULL; int imax; float fmax; long lmax; double dmax; DoubleComplex dcmax; SingleComplex fcmax; void *max2=NULL; DoubleComplex dcmax2; SingleComplex fcmax2; void *max3=NULL; DoubleComplex dcmax3; SingleComplex fcmax3; void *alpha=NULL, *beta=NULL; int ai = 1, bi = -1; long al = 1, bl = -1; float af = 1.0, bf = -1.0; double ad = 1.0, bd = -1.0; DoubleComplex adc, bdc; SingleComplex afc, bfc; double x1, x2, x3, x4; float fx1, fx2, fx3, fx4; void *resultx=NULL; long resultl=0,aresultl=0; double resultd=0,aresultd=0; float resultf=0,aresultf=0; int resulti=0,aresulti=0; adc.real = 1.0; adc.imag = 0.0; bdc.real = -1.0; bdc.imag = 0.0; afc.real = 1.0; afc.imag = 0.0; bfc.real = -1.0; bfc.imag = 0.0; needs_scaled_result = 0; dcval.real = -sin (3.0); dcval.imag = -cos (3.0); dcval2.real = 2 * sin (3.0); dcval2.imag = 2 * cos (3.0); dcval3.real = dcval.real*1.0e2; dcval3.imag = dcval.imag*1.0e2; dcval4.real = dcval2.real*1.0e2; dcval4.imag = dcval2.imag*1.0e2; dcval5.real = 5.0; dcval5.imag = 0.0; dcval6.real = dcval3.imag; dcval6.imag = dcval3.real; dcval7.real = dcval4.imag; dcval7.imag = dcval4.real; fcval.real = -sin (3.0); fcval.imag = -cos (3.0); fcval2.real = 2 * sin (3.0); fcval2.imag = 2 * cos (3.0); fcval3.real = fcval.real*1.0e2; fcval3.imag = fcval.imag*1.0e2; fcval4.real = fcval2.real*1.0e2; fcval4.imag = fcval2.imag*1.0e2; fcval5.real = 5.0; fcval5.imag = 0.0; fcval6.real = fcval3.imag; fcval6.imag = fcval3.real; fcval7.real = fcval4.imag; fcval7.imag = fcval4.real; proc_cnt=1; for (i = 0; i < dim; i++) { dims[i] = N; block_size[i] = BLOCK_SIZE; if (i= GA_Nnodes()) { proc_grid[i] = 1; } else { proc_grid[i] = GA_Nnodes()/proc_cnt; } } for (i = 0; i < dim; i++) { lo[i] = 0; hi[i] = N - 1; } #if BLOCK_CYCLIC g_a = GA_Create_handle(); GA_Set_data(g_a,dim,dims,type); GA_Set_array_name(g_a,"A"); # if USE_SCALAPACK || USE_TILED # if USE_SCALAPACK GA_Set_block_cyclic_proc_grid(g_a,block_size,proc_grid); # else GA_Set_tiled_proc_grid(g_a,block_size,proc_grid); # endif # else GA_Set_block_cyclic(g_a,block_size); # endif GA_Allocate(g_a); #else g_a = NGA_Create (type, dim, dims, "A", NULL); if (!g_a) GA_Error ("create failed: A", n); #endif g_b = GA_Duplicate (g_a, "B"); if (!g_b) GA_Error ("duplicate failed: B", n); g_c = GA_Duplicate (g_a, "C"); if (!g_c) GA_Error ("duplicate failed: C", n); g_d = GA_Duplicate (g_a, "D"); if (!g_d) GA_Error ("duplicate failed: D", n); g_e = GA_Duplicate (g_a, "E"); if (!g_e) GA_Error ("duplicate failed: E", n); g_f = GA_Duplicate (g_a, "F"); if (!g_f) GA_Error ("duplicate failed: F", n); g_g = GA_Duplicate (g_a, "G"); if (!g_g) GA_Error ("duplicate failed: G", n); g_h = GA_Duplicate (g_a, "H"); if (!g_h) GA_Error ("duplicate failed: H", n); g_i = GA_Duplicate (g_a, "I"); if (!g_i) GA_Error ("duplicate failed: I", n); g_j = GA_Duplicate (g_a, "J"); if (!g_j) GA_Error ("duplicate failed: J", n); g_k = GA_Duplicate (g_a, "K"); if (!g_k) GA_Error ("duplicate failed: K", n); g_l = GA_Duplicate (g_a, "L"); if (!g_l) GA_Error ("duplicate failed: L", n); g_m = GA_Duplicate (g_a, "M"); if (!g_m) GA_Error ("duplicate failed: M", n); g_n = GA_Duplicate (g_a, "N"); if (!g_m) GA_Error ("duplicate failed: N", n); /*initialize with zero */ GA_Zero (g_a); GA_Zero (g_b); GA_Zero (g_c); GA_Zero (g_d); GA_Zero (g_e); GA_Zero (g_f); GA_Zero (g_g); GA_Zero (g_h); GA_Zero (g_i); GA_Zero (g_j); GA_Zero (g_k); GA_Zero (g_l); GA_Zero (g_m); GA_Zero (g_n); switch (type) { case C_INT: val = &ival; val2 = &ival2; val5 = &ival5; vresult = &ivresult; resultx = &resulti; boundminx = &boundmini; boundmaxx = &boundmaxi; wolfeminx = &wolfemini; break; case C_DCPL: val = &dcval; val2 = &dcval2; val3 = &dcval3; val4 = &dcval4; val5 = &dcval5; vresult = &dcvresult; bvresult = &dcbvresult; break; case C_SCPL: val = &fcval; val2 = &fcval2; val3 = &fcval3; val4 = &fcval4; val5 = &fcval5; vresult = &fcvresult; bvresult = &fcbvresult; break; case C_DBL: val = &dval; val2 = &dval2; val5 = &dval5; vresult = &dvresult; resultx = &resultd; boundminx = &boundmind; boundmaxx = &boundmaxd; wolfeminx = &wolfemind; break; case C_FLOAT: val = &fval; val2 = &fval2; val5 = &fval5; vresult = &fvresult; resultx = &resultf; boundminx = &boundminf; boundmaxx = &boundmaxf; wolfeminx = &wolfeminf; break; case C_LONG: val = &lval; val2 = &lval2; val5 = &lval5; vresult = &lvresult; resultx = &resultl; boundminx = &boundminl; boundmaxx = &boundmaxl; wolfeminx = &wolfeminl; break; default: GA_Error ("wrong data type.", type); } NGA_Fill_patch (g_a, lo, hi, val); NGA_Fill_patch (g_b, lo, hi, val2); NGA_Pnfill_patch (g_j, lo, hi); switch (OP) { double tmp, tmp2; float tmpf, tmp2f; case OP_ABS: if (me == 0) printf ("Testing GA_Abs_value..."); GA_Abs_value_patch (g_a, lo, hi); ivresult = GA_ABS (ival); dvresult = GA_ABS (dval); fvresult = GA_ABS (fval); lvresult = GA_ABS (lval); #if 0 if (GA_ABS(dcval.real) >= GA_ABS(dcval.imag)) { if (dcval.real == (double)0.0) { dcvresult.real = (double)0.0; } else { x1 = dcval.imag/dcval.real; dcvresult.real = GA_ABS(dcval.real)*sqrt(((double)1.0)+(x1*x1)); } } else { x1 = dcval.real/dcval.imag; dcvresult.real = GA_ABS(dcval.imag)*sqrt(((double)1.0)+(x1*x1)); } #endif dcvresult.real = sqrt(dcval.real*dcval.real+dcval.imag*dcval.imag); dcvresult.imag = 0.0; NGA_Fill_patch (g_d, lo, hi, vresult); if (type == C_DCPL) { needs_scaled_result = 1; NGA_Fill_patch(g_f,lo,hi,val3); GA_Abs_value_patch (g_f, lo, hi); #if 0 if (GA_ABS(dcval3.real) >= GA_ABS(dcval3.imag)) { if (dcval3.real == (double)0.0) { dcbvresult.real = (double)0.0; } else { x1 = dcval3.imag/dcval3.real; dcbvresult.real = GA_ABS(dcval3.real)*sqrt(((double)1.0)+(x1*x1)); } } else { x1 = dcval3.real/dcval3.imag; dcbvresult.real = GA_ABS(dcval3.imag)*sqrt(((double)1.0)+(x1*x1)); } #endif dcbvresult.real = sqrt(dcval3.real*dcval3.real+dcval3.imag*dcval3.imag); dcbvresult.imag = (double)0.0; NGA_Fill_patch (g_i, lo, hi, bvresult); NGA_Fill_patch(g_k,lo,hi,&dcval6); GA_Abs_value_patch (g_k, lo, hi); #if 0 if (GA_ABS(dcval6.real) >= GA_ABS(dcval6.imag)) { if (dcval6.real == (double)0.0) { dcbvresult.real = (double)0.0; } else { x1 = dcval6.imag/dcval6.real; dcbvresult.real = GA_ABS(dcval6.real)*sqrt(((double)1.0)+(x1*x1)); } } else { x1 = dcval6.real/dcval6.imag; dcbvresult.real = GA_ABS(dcval6.imag)*sqrt(((double)1.0)+(x1*x1)); } #endif dcbvresult.real = sqrt(dcval6.real*dcval6.real+dcval6.imag*dcval6.imag); dcbvresult.imag = (double)0.0; NGA_Fill_patch (g_n, lo, hi, bvresult); } if (type == C_SCPL) { needs_scaled_result = 1; NGA_Fill_patch(g_f,lo,hi,val3); GA_Abs_value_patch (g_f, lo, hi); #if 0 if (GA_ABS(fcval3.real) >= GA_ABS(fcval3.imag)) { if (fcval3.real == (float )0.0) { fcbvresult.real = (float )0.0; } else { fx1 = fcval3.imag/fcval3.real; fcbvresult.real = GA_ABS(fcval3.real)*sqrt(((float)1.0)+(fx1*fx1)); } } else { fx1 = fcval3.real/fcval3.imag; fcbvresult.real = GA_ABS(fcval3.imag)*sqrt(((float)1.0)+(fx1*fx1)); } #endif fcbvresult.real = sqrt(fcval3.real*fcval3.real+fcval3.imag*fcval3.imag); fcbvresult.imag = (double)0.0; NGA_Fill_patch (g_i, lo, hi, bvresult); NGA_Fill_patch(g_k,lo,hi,&dcval6); GA_Abs_value_patch (g_k, lo, hi); #if 0 if (GA_ABS(fcval6.real) >= GA_ABS(fcval6.imag)) { if (fcval6.real == (float)0.0) { fcbvresult.real = (float)0.0; } else { fx1 = fcval6.imag/fcval6.real; fcbvresult.real = GA_ABS(fcval6.real)*sqrt(((float)1.0)+(fx1*fx1)); } } else { fx1 = fcval6.real/fcval6.imag; fcbvresult.real = GA_ABS(fcval6.imag)*sqrt(((float)1.0)+(fx1*fx1)); } #endif fcbvresult.real = sqrt(fcval6.real*fcval6.real+fcval6.imag*fcval6.imag); fcbvresult.imag = (double)0.0; NGA_Fill_patch (g_n, lo, hi, bvresult); } break; case OP_ADD_CONST: if (me == 0) printf ("Testing GA_Add_const..."); GA_Add_constant_patch (g_a, lo, hi, val2); ivresult = ival + ival2; dvresult = dval + dval2; fvresult = fval + fval2; lvresult = lval + lval2; dcvresult.real = dcval.real + dcval2.real; dcvresult.imag = dcval.imag + dcval2.imag; fcvresult.real = fcval.real + fcval2.real; fcvresult.imag = fcval.imag + fcval2.imag; NGA_Fill_patch (g_d, lo, hi, vresult); break; case OP_RECIP: if (me == 0) printf ("Testing GA_Recip..."); GA_Recip_patch (g_a, lo, hi); ivresult = ((int)1) / ival; dvresult = ((double)1.0) / dval; fvresult = ((float)1.0) / fval; lvresult = ((long)1) / lval; if (GA_ABS(dcval.real) >= GA_ABS(dcval.imag)) { if (dcval.real != (double)0.0) { tmp = dcval.imag/dcval.real; tmp2 = ((double)1.0)/((((double)1.0)+(tmp*tmp))*dcval.real); dcvresult.real = tmp2; dcvresult.imag = -tmp * tmp2; } else { printf("Error in testing GA_Recip dcval = 0.0\n"); } } else { tmp = dcval.real/dcval.imag; tmp2 = ((double)1.0)/((((double)1.0)+(tmp*tmp))*dcval.imag); dcvresult.real = tmp * tmp2; dcvresult.imag = -tmp2; } NGA_Fill_patch (g_d, lo, hi, vresult); if (type == C_DCPL) { needs_scaled_result = 1; NGA_Fill_patch (g_f, lo, hi, val3); GA_Recip_patch (g_f, lo, hi); if (GA_ABS(dcval3.real) >= GA_ABS(dcval3.imag)) { if (dcval3.real == (double)0.0) { printf("Error testing GA_Recip, dcval3.real = 0.0\n"); } else { tmp = dcval3.imag/dcval3.real; tmp2 = ((double)1.0)/((((double)1.0)+(tmp*tmp))*dcval3.real); dcbvresult.real = tmp2; dcbvresult.imag = -tmp * tmp2; } } else { tmp = dcval3.real/dcval3.imag; tmp2 = ((double)1.0)/((((double)1.0)+(tmp*tmp))*dcval3.imag); dcbvresult.real = tmp * tmp2; dcbvresult.imag = -tmp2; } NGA_Fill_patch (g_i, lo, hi, bvresult); NGA_Fill_patch(g_k,lo,hi,&dcval6); GA_Recip_patch (g_k, lo, hi); if (GA_ABS(dcval6.real) >= GA_ABS(dcval6.imag)) { if (dcval6.real == (double)0.0) { printf("Error testing GA_Recip, dcval6.real = 0.0\n"); } else { tmp = dcval6.imag/dcval6.real; tmp2 = ((double)1.0)/((((double)1.0)+(tmp*tmp))*dcval6.real); dcbvresult.real = tmp2; dcbvresult.imag = -tmp * tmp2; } } else { tmp = dcval6.real/dcval6.imag; tmp2 = ((double)1.0)/((((double)1.0)+(tmp*tmp))*dcval6.imag); dcbvresult.real = tmp * tmp2; dcbvresult.imag = -tmp2; } NGA_Fill_patch (g_n, lo, hi, bvresult); } if (type == C_SCPL) { needs_scaled_result = 1; NGA_Fill_patch (g_f, lo, hi, val3); GA_Recip_patch (g_f, lo, hi); if (GA_ABS(fcval3.real) >= GA_ABS(fcval3.imag)) { if (fcval3.real == (float )0.0) { printf("Error testing GA_Recip, fcval3.real = 0.0\n"); } else { tmpf = fcval3.imag/fcval3.real; tmp2f = ((float)1.0)/((((float)1.0)+(tmpf*tmpf))*fcval3.real); fcbvresult.real = tmp2f; fcbvresult.imag = -tmpf * tmp2f; } } else { tmpf = fcval3.real/fcval3.imag; tmp2f = ((float)1.0)/((((float)1.0)+(tmpf*tmpf))*fcval3.imag); fcbvresult.real = tmpf * tmp2f; fcbvresult.imag = -tmp2f; } NGA_Fill_patch (g_i, lo, hi, bvresult); NGA_Fill_patch(g_k,lo,hi,&dcval6); GA_Recip_patch (g_k, lo, hi); if (GA_ABS(fcval6.real) >= GA_ABS(fcval6.imag)) { if (fcval6.real == (float)0.0) { printf("Error testing GA_Recip, fcval6.real = 0.0\n"); } else { tmpf = fcval6.imag/fcval6.real; tmp2f = ((float)1.0)/((((float)1.0)+(tmpf*tmpf))*fcval6.real); fcbvresult.real = tmp2f; fcbvresult.imag = -tmpf * tmp2f; } } else { tmpf = fcval6.real/fcval6.imag; tmp2f = ((float)1.0)/((((float)1.0)+(tmpf*tmpf))*fcval6.imag); fcbvresult.real = tmpf * tmp2f; fcbvresult.imag = -tmp2f; } NGA_Fill_patch (g_n, lo, hi, bvresult); } break; case OP_ELEM_MULT: if (me == 0) printf ("Testing GA_Elem_multiply..."); NGA_Fill_patch (g_b, lo, hi, val2); /* g_c is different from g_a or g_b*/ GA_Elem_multiply_patch (g_a, lo, hi, g_b, lo, hi, g_c, lo, hi); ivresult = ival * ival2; dvresult = dval * dval2; fvresult = fval * fval2; lvresult = lval * lval2; dcvresult.real = dcval.real * dcval2.real - dcval.imag * dcval2.imag; dcvresult.imag = dcval.real * dcval2.imag + dcval2.real * dcval.imag; NGA_Fill_patch (g_d, lo, hi, vresult); break; case OP_ELEM_DIV: if (me == 0) printf ("Testing GA_Elem_divide..."); NGA_Fill_patch (g_b, lo, hi, val2); GA_Elem_divide_patch (g_a, lo, hi, g_b, lo, hi, g_c, lo, hi); ivresult = ival / ival2; dvresult = dval / dval2; fvresult = fval / fval2; lvresult = lval / lval2; dcvresult.real = 0.0; dcvresult.imag = 0.0; if (GA_ABS(dcval2.real) >= GA_ABS(dcval2.imag)) { if (dcval2.real != (double)0.0) { tmp = dcval2.imag/dcval2.real; tmp2 = ((double)1.0)/(dcval2.real*(((double)1.0)+(tmp*tmp))); dcvresult.real = (dcval.real + dcval.imag*tmp)*tmp2; dcvresult.imag = (dcval.imag - dcval.real*tmp)*tmp2; } else { printf("Error in testing GA_Elem_divide dcval = 0.0\n"); } } else { tmp = dcval2.real/dcval2.imag; tmp2 = 1.0/(dcval2.imag*(1.0+(tmp*tmp))); dcvresult.real = (dcval.real*tmp + dcval.imag)*tmp2; dcvresult.imag = (dcval.imag*tmp - dcval.real)*tmp2; } NGA_Fill_patch (g_d, lo, hi, vresult); if (type == C_DCPL) { needs_scaled_result = 1; NGA_Fill_patch (g_f, lo, hi, val3); NGA_Fill_patch (g_g, lo, hi, val4); GA_Elem_divide_patch (g_f, lo, hi, g_g, lo, hi, g_h, lo, hi); dcbvresult.real = (double)0.0; dcbvresult.imag = (double)0.0; if (GA_ABS(dcval4.real) >= GA_ABS(dcval4.imag)) { if (dcval4.real != (double)0.0) { tmp = dcval4.imag/dcval4.real; tmp2 = ((double)1.0)/(dcval4.real*(((double)1.0)+(tmp*tmp))); dcbvresult.real = (dcval3.real + dcval3.imag*tmp)*tmp2; dcbvresult.imag = (dcval3.imag - dcval3.real*tmp)*tmp2; } else { printf("Error in testing GA_Elem_divide dcval4 = 0.0\n"); } } else { tmp = dcval4.real/dcval4.imag; tmp2 = ((double)1.0)/(dcval4.imag*(((double)1.0)+(tmp*tmp))); dcbvresult.real = (dcval3.real*tmp + dcval3.imag)*tmp2; dcbvresult.imag = (dcval3.imag*tmp - dcval3.real)*tmp2; } NGA_Fill_patch (g_i, lo, hi, bvresult); NGA_Fill_patch (g_k, lo, hi, &dcval6); NGA_Fill_patch (g_l, lo, hi, &dcval7); GA_Elem_divide_patch (g_k, lo, hi, g_l, lo, hi, g_m, lo, hi); dcbvresult.real = (double)0.0; dcbvresult.imag = (double)0.0; if (GA_ABS(dcval7.real) >= GA_ABS(dcval7.imag)) { if (dcval7.real != (double)0.0) { tmp = dcval7.imag/dcval7.real; tmp2 = ((double)1.0)/(dcval7.real*(((double)1.0)+(tmp*tmp))); dcbvresult.real = (dcval6.real + dcval6.imag*tmp)*tmp2; dcbvresult.imag = (dcval6.imag - dcval6.real*tmp)*tmp2; } else { printf("Error in testing GA_Elem_divide dcval7 = 0.0\n"); } } else { tmp = dcval7.real/dcval7.imag; tmp2 = ((double)1.0)/(dcval7.imag*(((double)1.0)+(tmp*tmp))); dcbvresult.real = (dcval6.real*tmp + dcval6.imag)*tmp2; dcbvresult.imag = (dcval6.imag*tmp - dcval6.real)*tmp2; } NGA_Fill_patch (g_n, lo, hi, bvresult); } if (type == C_SCPL) { needs_scaled_result = 1; NGA_Fill_patch (g_f, lo, hi, val3); NGA_Fill_patch (g_g, lo, hi, val4); GA_Elem_divide_patch (g_f, lo, hi, g_g, lo, hi, g_h, lo, hi); fcbvresult.real = (float)0.0; fcbvresult.imag = (float)0.0; if (GA_ABS(fcval4.real) >= GA_ABS(fcval4.imag)) { if (fcval4.real != (float)0.0) { tmpf = fcval4.imag/fcval4.real; tmp2f = ((float)1.0)/(fcval4.real*(((float)1.0)+(tmpf*tmpf))); fcbvresult.real = (fcval3.real + fcval3.imag*tmpf)*tmp2f; fcbvresult.imag = (fcval3.imag - fcval3.real*tmpf)*tmp2f; } else { printf("Error in testing GA_Elem_divide fcval4 = 0.0\n"); } } else { tmpf = fcval4.real/fcval4.imag; tmp2f = ((float)1.0)/(fcval4.imag*(((float)1.0)+(tmpf*tmpf))); fcbvresult.real = (fcval3.real*tmpf + fcval3.imag)*tmp2f; fcbvresult.imag = (fcval3.imag*tmpf - fcval3.real)*tmp2f; } NGA_Fill_patch (g_i, lo, hi, bvresult); NGA_Fill_patch (g_k, lo, hi, &dcval6); NGA_Fill_patch (g_l, lo, hi, &dcval7); GA_Elem_divide_patch (g_k, lo, hi, g_l, lo, hi, g_m, lo, hi); fcbvresult.real = (float)0.0; fcbvresult.imag = (float)0.0; if (GA_ABS(fcval7.real) >= GA_ABS(fcval7.imag)) { if (fcval7.real != (float)0.0) { tmpf = fcval7.imag/fcval7.real; tmp2f = ((float)1.0)/(fcval7.real*(((float)1.0)+(tmpf*tmpf))); fcbvresult.real = (fcval6.real + fcval6.imag*tmpf)*tmp2f; fcbvresult.imag = (fcval6.imag - fcval6.real*tmpf)*tmp2f; } else { printf("Error in testing GA_Elem_divide fcval7 = 0.0\n"); } } else { tmpf = fcval7.real/fcval7.imag; tmp2f = ((float)1.0)/(fcval7.imag*(((float)1.0)+(tmpf*tmpf))); fcbvresult.real = (fcval6.real*tmpf + fcval6.imag)*tmp2f; fcbvresult.imag = (fcval6.imag*tmpf - fcval6.real)*tmp2f; } NGA_Fill_patch (g_n, lo, hi, bvresult); } break; case OP_ELEM_MAX: if (me == 0) printf ("Testing GA_Elem_maximum..."); /*NGA_Fill_patch (g_b, lo, hi, val2);*/ GA_Elem_maximum_patch (g_a, lo, hi, g_b, lo, hi, g_c, lo, hi); ivresult = GA_MAX (ival, ival2); dvresult = GA_MAX (dval, dval2); fvresult = GA_MAX (fval, fval2); lvresult = GA_MAX (lval, lval2); tmp = GA_MAX(GA_ABS(dcval.real),GA_ABS(dcval.imag)); tmp2 = GA_MAX(GA_ABS(dcval2.real),GA_ABS(dcval2.imag)); tmp = GA_MAX(tmp,tmp2); dcvresult.real = dcval.real; dcvresult.imag = dcval.imag; if (tmp != 0.0) { tmp = ((double)1.0)/tmp; x1 = dcval.real*tmp; x2 = dcval.imag*tmp; x3 = dcval2.real*tmp; x4 = dcval2.imag*tmp; tmp = x1*x1 + x2*x2; tmp2 = x3*x3 + x4*x4; if (tmp2 > tmp) { dcvresult.real = dcval2.real; dcvresult.imag = dcval2.imag; } } NGA_Fill_patch (g_d, lo, hi, vresult); if (type == C_DCPL) { needs_scaled_result = 1; NGA_Fill_patch (g_f, lo, hi, val3); NGA_Fill_patch (g_g, lo, hi, val4); GA_Elem_maximum_patch (g_f, lo, hi, g_g, lo, hi, g_h, lo, hi); tmp = GA_MAX(GA_ABS(dcval3.real),GA_ABS(dcval3.imag)); tmp2 = GA_MAX(GA_ABS(dcval4.real),GA_ABS(dcval4.imag)); tmp = GA_MAX(tmp,tmp2); dcvresult.real = dcval3.real; dcvresult.imag = dcval3.imag; if (tmp != 0.0) { tmp = ((double)1.0)/tmp; x1 = dcval3.real*tmp; x2 = dcval3.imag*tmp; x3 = dcval4.real*tmp; x4 = dcval4.imag*tmp; tmp = x1*x1 + x2*x2; tmp2 = x3*x3 + x4*x4; if (tmp2 > tmp) { dcvresult.real = dcval4.real; dcvresult.imag = dcval4.imag; } } NGA_Fill_patch (g_i, lo, hi, vresult); NGA_Fill_patch (g_k, lo, hi, &dcval6); NGA_Fill_patch (g_l, lo, hi, &dcval7); GA_Elem_maximum_patch (g_k, lo, hi, g_l, lo, hi, g_m, lo, hi); tmp = GA_MAX(GA_ABS(dcval6.real),GA_ABS(dcval6.imag)); tmp2 = GA_MAX(GA_ABS(dcval7.real),GA_ABS(dcval7.imag)); tmp = GA_MAX(tmp,tmp2); dcvresult.real = dcval6.real; dcvresult.imag = dcval6.imag; if (tmp != 0.0) { tmp = ((double)1.0)/tmp; x1 = dcval6.real*tmp; x2 = dcval6.imag*tmp; x3 = dcval7.real*tmp; x4 = dcval7.imag*tmp; tmp = x1*x1 + x2*x2; tmp2 = x3*x3 + x4*x4; if (tmp2 > tmp) { dcvresult.real = dcval7.real; dcvresult.imag = dcval7.imag; } } NGA_Fill_patch (g_n, lo, hi, vresult); } break; case OP_ELEM_MIN: if (me == 0) printf ("Testing GA_Elem_minimum..."); NGA_Fill_patch (g_b, lo, hi, val2); GA_Elem_minimum_patch (g_a, lo, hi, g_b, lo, hi, g_c, lo, hi); ivresult = GA_MIN (ival, ival2); dvresult = GA_MIN (dval, dval2); fvresult = GA_MIN (fval, fval2); lvresult = GA_MIN (lval, lval2); tmp = GA_MAX(GA_ABS(dcval.real),GA_ABS(dcval.imag)); tmp2 = GA_MAX(GA_ABS(dcval2.real),GA_ABS(dcval2.imag)); tmp = GA_MAX(tmp,tmp2); dcvresult.real = dcval.real; dcvresult.imag = dcval.imag; if (tmp != 0.0) { tmp = 1.0/tmp; x1 = dcval.real*tmp; x2 = dcval.imag*tmp; x3 = dcval2.real*tmp; x4 = dcval2.imag*tmp; tmp = x1*x1 + x2*x2; tmp2 = x3*x3 + x4*x4; if (tmp2 < tmp) { dcvresult.real = dcval2.real; dcvresult.imag = dcval2.imag; } } NGA_Fill_patch (g_d, lo, hi, vresult); if (type == C_SCPL) { needs_scaled_result = 1; NGA_Fill_patch (g_f, lo, hi, val3); NGA_Fill_patch (g_g, lo, hi, val4); GA_Elem_minimum_patch (g_f, lo, hi, g_g, lo, hi, g_h, lo, hi); tmpf = GA_MAX(GA_ABS(fcval3.real),GA_ABS(fcval3.imag)); tmp2f = GA_MAX(GA_ABS(fcval4.real),GA_ABS(fcval4.imag)); tmpf = GA_MAX(tmpf,tmp2f); fcvresult.real = fcval3.real; fcvresult.imag = fcval3.imag; if (tmpf != 0.0) { tmpf = ((float)1.0)/tmpf; fx1 = fcval3.real*tmpf; fx2 = fcval3.imag*tmpf; fx3 = fcval4.real*tmpf; fx4 = fcval4.imag*tmpf; tmpf = fx1*fx1 + fx2*fx2; tmp2f = fx3*fx3 + fx4*fx4; if (tmp2f < tmpf) { fcvresult.real = fcval4.real; fcvresult.imag = fcval4.imag; } } NGA_Fill_patch (g_i, lo, hi, vresult); NGA_Fill_patch (g_k, lo, hi, &dcval6); NGA_Fill_patch (g_l, lo, hi, &dcval7); GA_Elem_minimum_patch (g_k, lo, hi, g_l, lo, hi, g_m, lo, hi); tmpf = GA_MAX(GA_ABS(fcval6.real),GA_ABS(fcval6.imag)); tmp2f = GA_MAX(GA_ABS(fcval7.real),GA_ABS(fcval7.imag)); tmpf = GA_MAX(tmpf,tmp2f); fcvresult.real = fcval6.real; fcvresult.imag = fcval6.imag; if (tmpf != 0.0) { tmpf = ((float)1.0)/tmpf; fx1 = fcval6.real*tmpf; fx2 = fcval6.imag*tmpf; fx3 = fcval7.real*tmpf; fx4 = fcval7.imag*tmpf; tmpf = fx1*fx1 + fx2*fx2; tmp2f = fx3*fx3 + fx4*fx4; if (tmp2f < tmpf) { fcvresult.real = fcval7.real; fcvresult.imag = fcval7.imag; } } NGA_Fill_patch (g_n, lo, hi, vresult); } break; case OP_STEP_MAX: if (me == 0) printf ("Testing GA_Step_max..."); if (type != C_DCPL || type != C_SCPL) { /*NGA_Fill_patch (g_b, lo, hi, val2);*/ GA_Abs_value_patch (g_b, lo, hi); GA_Step_max_patch (g_b, lo, hi, g_j, lo, hi, resultx); /* printf(" GA_Stepmax_patch type = %d, resultx = %le\n",type,resultx); fflush(stdout); */ /* It would be more robust to use GA_Elem_min_patch here to determine the minimum g_j value, but for now we set it to -2. */ aresulti = ((int)(GA_ABS(ival2)/GA_ABS(ival))) - resulti; aresultd = GA_ABS(dval2/dval) - resultd; aresultf = ((float)GA_ABS(fval2/fval)) - resultf; aresultl = ((long)(GA_ABS(lval2)/GA_ABS(lval))) - resultl; } break; case OP_STEP_BOUND_INFO: if (me == 0) printf ("Testing GA_Step_bound_info..."); if (type != C_DCPL || type != C_SCPL) { /*NGA_Fill_patch (g_b, lo, hi, val2);*/ GA_Abs_value_patch (g_b, lo, hi); GA_Abs_value_patch (g_a, lo, hi); /*GA_Abs_value_patch (g_j, lo, hi);*/ NGA_Fill_patch(g_c, lo, hi, val5); GA_Step_bound_info_patch (g_b,lo,hi, g_j,lo,hi, g_a,lo,hi, g_c,lo,hi, boundminx,wolfeminx,boundmaxx); /* printf(" GA_Stepmax2_patch type = %d, resultx = %le\n",type,resultx); fflush(stdout); */ /* This is currently hardwired. would need to change if val, val2 or val5 change. */ switch (type) { case C_INT: awolfemini = ((int)(((int)1)/((int)2))) - wolfemini; aboundmini = ((int)(((int)1)/((int)2))) - boundmini; aboundmaxi = (int)GA_INFINITY_I - boundmaxi; break; case C_DBL: awolfemind = (((double)1.0)/((double)2.0)) - wolfemind; aboundmind = (((double)1.0)/((double)2.0)) - boundmind; aboundmaxd = (double)GA_INFINITY_D - boundmaxd; break; case C_FLOAT: awolfeminf = (((float)1.0)/((float)2.0)) - wolfeminf; aboundminf = (((float)1.0)/((float)2.0)) - boundminf; aboundmaxf = (float)GA_INFINITY_F - boundmaxf; break; case C_LONG: awolfeminl = ((long)(((long)1)/((long)2))) - wolfeminl; aboundminl = ((long)(((long)1)/((long)2))) - boundminl; aboundmaxl = (long)GA_INFINITY_L - boundmaxl; break; default: GA_Error ("GA_step_bound_info wrong data type.", type); } } break; default: GA_Error ("test_function: wrong operation.", OP); } switch (type) { case C_INT: alpha = &ai; beta = &bi; break; case C_DCPL: alpha = &adc; beta = &bdc; break; case C_SCPL: alpha = &afc; beta = &bfc; break; case C_DBL: alpha = &ad; beta = &bd; break; case C_FLOAT: alpha = ⁡ beta = &bf; break; case C_LONG: alpha = &al; beta = &bl; break; default: GA_Error ("wrong data type.", type); } if (OP < 4) { /* Binary operation. */ NGA_Add_patch (alpha, g_c, lo, hi, beta, g_d, lo, hi, g_e, lo, hi); if (needs_scaled_result == 1) { NGA_Add_patch (alpha, g_h, lo, hi, beta, g_i, lo, hi, g_j, lo, hi); NGA_Add_patch (alpha, g_m, lo, hi, beta, g_n, lo, hi, g_n, lo, hi); } } else { /* Unary operation. */ if (OP < 7) { NGA_Add_patch (alpha, g_a, lo, hi, beta, g_d, lo, hi, g_e, lo, hi); if (needs_scaled_result == 1) { NGA_Add_patch (alpha, g_f, lo, hi, beta, g_i, lo, hi, g_j, lo, hi); NGA_Add_patch (alpha, g_k, lo, hi, beta, g_n, lo, hi, g_n, lo, hi); } } /* Else it was a reduction operation (one of the step_max functions). */ } switch (type) { case C_INT: max = &imax; break; case C_DCPL: max = &dcmax; max2 = &dcmax2; max3 = &dcmax3; break; case C_SCPL: max = &fcmax; max2 = &fcmax2; max3 = &fcmax3; break; case C_DBL: max = &dmax; break; case C_FLOAT: max = &fmax; break; case C_LONG: max = &lmax; break; default: GA_Error ("wrong data type.", type); } /* for unary and binary operators extract the maximum difference between computed and correct solutions. */ if (OP < 7) { NGA_Select_elem (g_e, "max", max, index); if (needs_scaled_result == 1) { NGA_Select_elem (g_j, "max", max2, index2); NGA_Select_elem (g_n, "max", max3, index3); } } /* NGA_Select_elem (g_e, "min", min, index);*/ if (OP < 7) { /* Binary or Unary operators. */ switch (type) { double r, im; float rf, imf; case C_INT: /* result = (int)(imax - imin);*/ result = imax; if (result != (int)0) result = 1; break; case C_DCPL: /* r = dcmax.real - dcmin.real; im = dcmax.imag - dcmin.imag; */ r = dcmax.real; im = dcmax.imag; if ((GA_ABS(r) + GA_ABS(im)) < THRESH) { result = 0; } else { result = 1; } if (needs_scaled_result == 1) { result2 = 0; r = dcmax2.real; im = dcmax2.imag; if ((GA_ABS(r) + GA_ABS(im)) < THRESH) { result2 = 0; } else { result2 = 1; } r = dcmax3.real; im = dcmax3.imag; if ((GA_ABS(r) + GA_ABS(im)) < THRESH) { result3 = 0; } else { result3 = 1; } result = result | result2 | result3; } break; case C_SCPL: /* rf = fcmax.real - fcmin.real; imf = fcmax.imag - fcmin.imag; */ rf = fcmax.real; imf = fcmax.imag; if ((GA_ABS(rf) + GA_ABS(imf)) == (float)0.0) { result = 0; } else { result = 1; } if (needs_scaled_result == 1) { result2 = 0; rf = fcmax2.real; imf = fcmax2.imag; if ((GA_ABS(rf) + GA_ABS(imf)) == (float)0.0) { result2 = 0; } else { result2 = 1; } rf = fcmax3.real; imf = fcmax3.imag; if ((GA_ABS(rf) + GA_ABS(imf)) == (float)0.0) { result3 = 0; } else { result3 = 1; } result = result | result2 | result3; } break; case C_DBL: if (dmax == (double)0.0) { result = 0; } else { result = 1; } break; case C_FLOAT: if (fmax == (float)0.0) { result = 0; } else { result = 1; } break; case C_LONG: if (lmax == (long)0) { result = 0; } else { result = 1; } break; default: GA_Error ("wrong data type.", type); } } else { /* A reduction operation, Step_max or Step_bound_info. */ if (type == C_DCPL || type == C_SCPL) { result = 0; } else { if (OP == OP_STEP_MAX) { /* Step_max */ switch (type) { case C_INT: if (aresulti == 0) { result = 0; } else { result = 1; } break; case C_DBL: if (aresultd == (double)0.0) { result = 0; } else { result = 1; } break; case C_FLOAT: if (aresultf == (float)0.0) { result = 0; } else { result = 1; } break; case C_LONG: if (aresultl == (long)0) { result = 0; } else { result = 1; } break; default: GA_Error ("Stepmax op, wrong data type.", type); } } else { /* OP = 8 so Step_bound_info */ switch (type) { case C_INT: if (awolfemini == 0) { result = 0; } else { result = 1; } if (aboundmini == 0) { result2 = 0; } else { result2 = 1; } if (aboundmaxi == 0) { result3 = 0; } else { result3 = 1; } break; case C_DBL: if (awolfemind == ((double)0.0)) { result = 0; } else { result = 1; } if (aboundmind == ((double)0.0)) { result2 = 0; } else { result2 = 1; } if (aboundmaxd == ((double)0.0)) { result3 = 0; } else { result3 = 1; } break; case C_FLOAT: if (awolfeminf == ((float)0.0)) { result = 0; } else { result = 1; } if (aboundminf == ((float)0.0)) { result2 = 0; } else { result2 = 1; } if (aboundmaxf == ((float)0.0)) { result3 = 0; } else { result3 = 1; } break; case C_LONG: if (awolfeminl == ((long)0)) { result = 0; } else { result = 1; } if (aboundminl == ((long)0)) { result2 = 0; } else { result2 = 1; } if (aboundmaxl == ((long)0)) { result3 = 0; } else { result3 = 1; } break; default: GA_Error ("Stepmax op, wrong data type.", type); } result = result | result2 | result3; } } } if (me == 0) { if (MISMATCHED (result, 0)) { printf ("is not ok\n"); GA_Error("aborting", 1); } else { printf ("is ok.\n"); } } /* NGA_Print_patch(g_a, lo, hi, 1); NGA_Print_patch(g_d, lo, hi, 1); NGA_Print_patch(g_e, lo, hi, 1); */ GA_Destroy (g_a); GA_Destroy (g_b); GA_Destroy (g_c); GA_Destroy (g_d); GA_Destroy (g_e); GA_Destroy (g_f); GA_Destroy (g_g); GA_Destroy (g_h); GA_Destroy (g_i); GA_Destroy (g_j); GA_Destroy (g_k); GA_Destroy (g_l); GA_Destroy (g_m); GA_Destroy (g_n); return ok; } int main(int argc, char **argv) { int heap = 20000, stack = 20000; int me, nproc; int d, op; int ok = 1; MP_INIT(argc,argv); GA_INIT(argc,argv); /* initialize GA */ me = GA_Nodeid (); nproc = GA_Nnodes (); if (me == 0) { if (GA_Uses_fapi ()) GA_Error ("Program runs with C array API only", 1); printf ("Using %ld processes\n", (long) nproc); fflush (stdout); } heap /= nproc; stack /= nproc; if (!MA_init (C_DBL, stack, heap)) GA_Error ("MA_init failed", stack + heap); /* initialize memory allocator */ /* op = 8;*/ for (op = 0; op < 9; op++) { /*for (d = 1; d < 2; d++)*/ for (d = 1; d < 4; d++) { if (me == 0) printf ("\n\ndim =%d\n\n", d); if (me == 0) printf ("\ndata type: INT\t\t"); ok = test_fun (C_INT, d, op); if (me == 0) printf ("\ndata type: double\t"); ok = test_fun (C_DBL, d, op); if (me == 0) printf ("\ndata type: float\t"); ok = test_fun (C_FLOAT, d, op); if (me == 0) printf ("\ndata type: long\t\t"); ok = test_fun (C_LONG, d, op); if (op < 7) { if (me == 0) printf ("\ndata type: double complex\t"); ok = test_fun (C_DCPL, d, op); } } } if (me==0) printf("\nAll tests successful\n"); GA_Terminate(); MP_FINALIZE(); return 0; } /*\ FILL IN ARRAY WITH Varying VALUEs. (from 0 to product of dims-1). For complex arrays make the real and imaginary parts equal. \*/ void nga_vfill_patch(Integer *g_a, Integer *lo, Integer *hi) { Integer i, j; Integer ndim, dims[MAXDIM], type; Integer loA[MAXDIM], hiA[MAXDIM], ld[MAXDIM]; void *data_ptr; Integer idx, n1dim; Integer bvalue[MAXDIM], bunit[MAXDIM], baseld[MAXDIM]; Integer me= pnga_nodeid(); int local_sync_begin,local_sync_end; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)GA_Sync(); pnga_inquire(*g_a, &type, &ndim, dims); /* get limits of VISIBLE patch */ pnga_distribution(*g_a, me, loA, hiA); /* determine subset of my local patch to access */ /* Output is in loA and hiA */ if(pnga_patch_intersect(lo, hi, loA, hiA, ndim)){ /* get data_ptr to corner of patch */ /* ld are leading dimensions INCLUDING ghost cells */ pnga_access_ptr(*g_a, loA, hiA, &data_ptr, ld); /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((int *)data_ptr)[idx+j] = (int)(idx+j); } break; case C_DCPL: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) { ((DoubleComplex *)data_ptr)[idx+j].real = (double)(idx+j); ((DoubleComplex *)data_ptr)[idx+j].imag = (double)(idx+j); } } break; case C_SCPL: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) { ((SingleComplex *)data_ptr)[idx+j].real = (float)(idx+j); ((SingleComplex *)data_ptr)[idx+j].imag = (float)(idx+j); } } break; case C_DBL: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((double*)data_ptr)[idx+j] = (double)(idx+j); } break; case C_FLOAT: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((float *)data_ptr)[idx+j] = (float)(idx+j); } break; case C_LONG: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((long *)data_ptr)[idx+j] = (long)(idx+j); } break; default: GA_Error(" wrong data type ",type); } /* release access to the data */ pnga_release_update(*g_a, loA, hiA); } if(local_sync_end)GA_Sync(); } /*\ Utility function to actually set positive/negative values \*/ void ngai_do_pnfill_patch(Integer type, Integer ndim, Integer *loA, Integer *hiA, Integer *ld, void *data_ptr) { Integer i, j; Integer idx, n1dim; Integer bvalue[MAXDIM], bunit[MAXDIM], baseld[MAXDIM]; /* number of n-element of the first dimension */ n1dim = 1; for(i=1; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((int *)data_ptr)[idx+j] = (int)(((idx+j)&3)-2); } break; case C_DCPL: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) { ((DoubleComplex *)data_ptr)[idx+j].real = (double)(((idx+j)&3)-2); ((DoubleComplex *)data_ptr)[idx+j].imag = (double)(((idx+j)&3)-2); } } break; case C_SCPL: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) { ((SingleComplex *)data_ptr)[idx+j].real = (float)(((idx+j)&3)-2); ((SingleComplex *)data_ptr)[idx+j].imag = (float)(((idx+j)&3)-2); } } break; case C_DBL: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((double*)data_ptr)[idx+j] = (double)(((idx+j)&3)-2); } break; case C_FLOAT: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((float *)data_ptr)[idx+j] = (float)(((idx+j)&3)-2); } break; case C_LONG: for(i=0; i (hiA[j]-loA[j])) bvalue[j] = 0; } for(j=0; j<(hiA[0]-loA[0]+1); j++) ((long *)data_ptr)[idx+j] = (long)(((idx+j)&3)-2); } break; default: GA_Error(" wrong data type ",type); } } /*\ FILL IN ARRAY WITH Varying positive and negative VALUEs. (from -2 to 1). For complex arrays make the real and imaginary parts equal. \*/ void nga_pnfill_patch(Integer *g_a, Integer *lo, Integer *hi) { Integer i; Integer ndim, dims[MAXDIM], type; Integer loA[MAXDIM], hiA[MAXDIM], ld[MAXDIM]; void *data_ptr; Integer me= pnga_nodeid(); Integer num_blocks; int local_sync_begin,local_sync_end; local_sync_begin = _ga_sync_begin; local_sync_end = _ga_sync_end; _ga_sync_begin = 1; _ga_sync_end=1; /*remove any previous masking*/ if(local_sync_begin)GA_Sync(); pnga_inquire(*g_a, &type, &ndim, dims); num_blocks = pnga_total_blocks(*g_a); if (num_blocks < 0) { /* get limits of VISIBLE patch */ pnga_distribution(*g_a, me, loA, hiA); /* determine subset of my local patch to access */ /* Output is in loA and hiA */ if(pnga_patch_intersect(lo, hi, loA, hiA, ndim)){ /* get data_ptr to corner of patch */ /* ld are leading dimensions INCLUDING ghost cells */ pnga_access_ptr(*g_a, loA, hiA, &data_ptr, ld); ngai_do_pnfill_patch(type, ndim, loA, hiA, ld, data_ptr); /* release access to the data */ pnga_release_update(*g_a, loA, hiA); } } else { Integer offset, j, jtmp, chk; Integer loS[MAXDIM]; Integer nproc = pnga_nnodes(); /* using simple block-cyclic data distribution */ if (!pnga_uses_proc_grid(*g_a)){ for (i=me; i dims[i]) hiA[i] = dims[i]; if (hiA[i] < loA[i]) chk = 0; } /* loA is changed by pnga_patch_intersect, so save a copy */ for (j=0; j= blocks[i] && i #include #include "mp3.h" #include "ga.h" #include "macdecls.h" /* This test is designed to test GAs ability to move very large chunks of * data in a single call. It requires about 18 GBytes of memory per process * to run successfully */ /* create nproc by N matrix (int32 precision) */ #define N 2147483647 /* int32 max */ #define NN 65536 int main(int argc, char **argv) { int me, nproc, g_a, ndim, type=MT_C_INT, *buf; int64_t i, j, iproc, dims[2], lo[2], hi[2], ld[2]; int ok; int *ptr; MP_INIT(argc,argv); GA_Initialize_ltd(-1); me=GA_Nodeid(); nproc=GA_Nnodes(); ndim = 1; dims[0] = N*((long)nproc); if(me==0) printf("Using %ld processes\n",(long)nproc); if(me==0) printf("memory = %ld bytes\n",((long)nproc)*((long)N)*4); if (me == 0) printf("Testing large 1D arrays\n"); g_a = NGA_Create64(type, ndim, dims, "A", NULL); GA_Zero(g_a); GA_Print_distribution(g_a); buf = (int*)(malloc(N*sizeof(int))); if(me == 0) { for (iproc=0; iproc Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c call ga_print(g_a) c c Check that it is indeed zero c lo(1) = 1 lo(2) = 1 hi(1) = n hi(2) = n call nga_get_field(g_a, lo, hi, 0, 4, breal, n) call nga_get_field(g_a, lo, hi, 4, 4, bimg, n) call ga_sync() do i = 1, n do j = 1, n if(breal(i,j).ne.0d0) then write(6,*) me,' real zero ', i, j, breal(i,j) call ffflush(6) c call ga_error('... exiting ',0) endif if(bimg(i,j).ne.0d0) then write(6,*) me,' img zero ', i, j, bimg(i,j) call ffflush(6) c call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format(/'> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/4 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) c call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) lo(1) = ilo lo(2) = jlo hi(1) = ihi hi(2) = jhi call nga_put_field(g_a, lo, hi, 0, 4, areal(ilo, jlo), n) call nga_put_field(g_a, lo, hi, 4, 4, aimg(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c c call ga_get(g_a, 1, n, 1, n, b, n) lo(1) = 1 lo(2) = 1 hi(1) = n hi(2) = n call nga_get_field(g_a, lo, hi, 0, 4, breal, n) call nga_get_field(g_a, lo, hi, 4, 4, bimg, n) c do i = 1, n do j = 1, n if (breal(i,j) .ne. areal(i,j)) then write(6,*) ' put(real) ', me, i, j, areal(i,j),breal(i,j) call ffflush(6) c call ga_error('... exiting ',0) endif if (bimg(i,j) .ne. aimg(i,j)) then write(6,*) ' put(img) ', me, i, j, aimg(i,j),bimg(i,j) call ffflush(6) c call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif call ga_sync() c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format(/'> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c c call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) lo(1) = ilo lo(2) = jlo hi(1) = ihi hi(2) = jhi call nga_get_field(g_a, lo, hi, 0, 4, breal(ilo, jlo), n) call nga_get_field(g_a, lo, hi, 4, 4, bimg(ilo, jlo), n) if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif do j = jlo, jhi do i = ilo, ihi if (breal(i,j) .ne. areal(i,j)) then write(6,*)'error:', i, j, breal(i,j), areal(i,j) call ga_error('... exiting ',0) endif if (bimg(i,j) .ne. aimg(i,j)) then write(6,*)'error:', i, j, bimg(i,j), aimg(i,j) call ga_error('... exiting ',0) endif enddo enddo c enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) call ffflush(6) endif call ga_sync() c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif call ga_sync() #ifndef MIRROR if(me.eq.0) then c call ga_put(g_a, 1, n, 1, n, a, n) lo(1) = 1 lo(2) = 1 hi(1) = n hi(2) = n call nga_put_field(g_a, lo, hi, 0, 4, areal, n) call nga_put_field(g_a, lo, hi, 4, 4, aimg, n) endif #else if(iproc.eq.0) then c call ga_put(g_a, 1, n, 1, n, a, n) lo(1) = 1 lo(2) = 1 hi(1) = n hi(2) = n call nga_put_field(g_a, lo, hi, 0, 4, areal, n) call nga_put_field(g_a, lo, hi, 4, 4, aimg, n) endif #endif call ga_copy(g_a, g_b) c call ga_get(g_b, 1, n, 1, n, b, n) lo(1) = 1 lo(2) = 1 hi(1) = n hi(2) = n call nga_get_field(g_b, lo, hi, 0, 4, breal, n) call nga_get_field(g_b, lo, hi, 4, 4, bimg, n) do j = 1, n do i = 1, n if (breal(i,j) .ne. areal(i,j)) then write(6,*) ' copy ', me, i, j, areal(i,j), breal(i,j) call ga_error('... exiting ',0) endif if (bimg(i,j) .ne. aimg(i,j)) then write(6,*) ' copy ', me, i, j, aimg(i,j), bimg(i,j) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c c Delete the global arrays c status = ga_destroy(g_b) status = ga_destroy(g_a) c end c----------------------------------------------------------------- subroutine check_complex() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,m parameter (n = 60) parameter (m = 2*n) double precision areal(n,n), breal(n,n) double precision aimg(n,n), bimg(n,n) #ifdef MIRROR integer ndim, dims(2), chunk(2), p_mirror #else # ifdef NEW_API integer ndim, dims(2), chunk(2), p_mirror # endif #endif integer iv(m), jv(m) logical status integer g_a, g_b integer iran, i,j, loop,nloop,maxloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, ii, jj, nnodes parameter (maxloop = 100) integer maxproc parameter (maxproc = 4096) double precision crap, real double precision nwords double complex x, sum1, sum2, factor integer lo(2), hi(2) integer lprocs, inode, iproc, lproc #ifdef USE_RESTRICTED integer num_rstrctd integer rstrctd_list(maxproc/2) #endif #ifdef BLOCK_CYCLIC integer block_size(2), proc_grid(2) #endif intrinsic int iran(i) = int(drand(0)*real(i)) + 1 c nproc = ga_nnodes() me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) nloop = Min(maxloop,n) #ifdef USE_RESTRICTED num_rstrctd = nproc/2 if (num_rstrctd.eq.0) num_rstrctd = 1 do i = 1, num_rstrctd rstrctd_list(i) = (num_rstrctd/2) + i-1 end do #endif #ifdef BLOCK_CYCLIC block_size(1) = 32 block_size(2) = 32 #ifdef USE_SCALAPACK_DISTR if (mod(nproc,2).ne.0) + call ga_error("Available procs must be divisible by 2",0) proc_grid(1) = 2 proc_grid(2) = nproc/2 #endif #endif c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n #ifndef MIRROR areal(i,j) = dble(i-1) aimg(i,j) = dble((j-1)*n) #else areal(i,j) = dble(inode) + dble(i-1) aimg(i,j) = 0.0d00 + dble((j-1)*n) #endif breal(i,j) = -1d0 bimg(i,j) = 1d0 enddo enddo c c Create type c c c Create a global array c c print *,ga_nodeid(), ' creating array' call ffflush(6) c call setdbg(1) #ifdef NEW_API ndim = 2 dims(1) = n dims(2) = n g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_DCPL) call ga_set_array_name(g_a,'a') #ifdef USE_RESTRICTED call ga_set_restricted(g_a, rstrctd_list, num_rstrctd) #endif #ifdef BLOCK_CYCLIC #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_a,block_size,proc_grid) #else call ga_set_block_cyclic(g_a,block_size) #endif #endif # ifdef MIRROR p_mirror = ga_pgroup_get_mirror() call ga_set_pgroup(g_a,p_mirror) # endif status = ga_allocate(g_a) #else # ifndef MIRROR status = ga_create(MT_DCPL, n, n, 'a', 0, 0, g_a) # else ndim = 2 dims(1) = n dims(2) = n chunk(1) = 0 chunk(2) = 0 p_mirror = ga_pgroup_get_mirror() status = nga_create_config(MT_DCPL, ndim, dims, 'a', chunk, + p_mirror, g_a) # endif #endif if (.not. status) then write(6,*) ' ga_create failed' call ga_error('... exiting ',0) endif #ifdef NEW_API g_b = ga_create_handle() call ga_set_data(g_b,ndim,dims,MT_DCPL) call ga_set_array_name(g_b,'b') #ifdef BLOCK_CYCLIC #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_b,block_size,proc_grid) #else call ga_set_block_cyclic(g_b,block_size) #endif #endif # ifdef MIRROR call ga_set_pgroup(g_b,p_mirror) # endif if (.not.ga_allocate(g_b)) then #else # ifndef MIRROR if (.not. ga_create(MT_DCPL, n, n, 'b', 0, 0, g_b)) then # else if (.not. nga_create_config(MT_DCPL, ndim, dims, 'b', chunk, _ p_mirror, g_b)) then # endif #endif call ga_error('ga_create failed for second array ',0) endif #ifndef MIRROR call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) #else lproc = me - ga_cluster_procid(inode,0) call ga_distribution(g_a, lproc, ilo, ihi, jlo, jhi) #endif call ga_sync() c c Zero the array c if (me .eq. 0) then write(6,21) 21 format('> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c c call ga_get(g_a, 1, n, 1, n, b, n) lo(1) = 1 lo(2) = 1 hi(1) = n hi(2) = n call nga_get_field(g_a, lo, hi, 0, 8, breal, n) call nga_get_field(g_a, lo, hi, 8, 8, bimg, n) call ga_sync() do i = 1, n do j = 1, n if(breal(i,j).ne.(0d0,0d0)) then write(6,*) me,' real zero ', i, j, breal(i,j) call ffflush(6) call ga_error('... exiting ',0) endif if(bimg(i,j).ne.(0d0,0d0)) then write(6,*) me,' img zero ', i, j, bimg(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format('> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) c call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) lo(1) = ilo lo(2) = jlo hi(1) = ihi hi(2) = jhi call nga_put_field(g_a, lo, hi, 0, 8, areal(ilo,jlo),n) call nga_put_field(g_a, lo, hi, 8, 8, aimg(ilo,jlo),n) endif ij = ij + 1 enddo enddo call ga_sync() c call ga_print(g_a) c c All nodes check all of a c call util_qfill(n*n, 0.0d0, breal, 1) call util_qfill(n*n, 0d0, bimg, 1) c call ga_get(g_a, 1, n, 1, n, b, n) lo(1) = 1 lo(2) = 1 hi(1) = n hi(2) = n call nga_get_field(g_a, lo, hi, 0, 8, breal, n) call nga_get_field(g_a, lo, hi, 8, 8, bimg, n) c do i = 1, n do j = 1, n if (breal(i,j) .ne. areal(i,j)) then write(6,*) ' real put ', me, i, j, areal(i,j),breal(i,j) call ffflush(6) call ga_error('... exiting ',0) endif if (bimg(i,j) .ne. aimg(i,j)) then write(6,*) ' img put ', me, i, j, aimg(i,j),bimg(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif call ga_sync() c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format('> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_qfill(n*n, 0.0d0, breal, 1) call util_qfill(n*n, 0d0, bimg, 1) c call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) lo(1) = ilo lo(2) = jlo hi(1) = ihi hi(2) = jhi call nga_get_field(g_a, lo, hi, 0, 8, breal(ilo,jlo),n) call nga_get_field(g_a, lo, hi, 8, 8, bimg(ilo,jlo),n) if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif do j = jlo, jhi do i = ilo, ihi if (breal(i,j) .ne. areal(i,j)) then write(6,*)'real error:', i, j, breal(i,j), areal(i,j) call ga_error('... exiting ',0) endif if (bimg(i,j) .ne. aimg(i,j)) then write(6,*)'img error:', i, j, bimg(i,j), aimg(i,j) call ga_error('... exiting ',0) endif enddo enddo c enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) call ffflush(6) endif call ga_sync() c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif call ga_sync() #ifndef MIRROR if(me.eq.0) then c call ga_put(g_a, 1, n, 1, n, a, n) lo(1) = 1 lo(2) = 1 hi(1) = n hi(2) = n call nga_put_field(g_a, lo, hi, 0, 8, areal, n) call nga_put_field(g_a, lo, hi, 8, 8, aimg, n) endif #else if(iproc.eq.0) then c call ga_put(g_a, 1, n, 1, n, a, n) lo(1) = 1 lo(2) = 1 hi(1) = n hi(2) = n call nga_put_field(g_a, lo, hi, 0, 8, areal, n) call nga_put_field(g_a, lo, hi, 8, 8, aimg, n) endif #endif call ga_copy(g_a, g_b) c call ga_get(g_b, 1, n, 1, n, b, n) lo(1) = 1 lo(2) = 1 hi(1) = n hi(2) = n call nga_get_field(g_b, lo, hi, 0, 8, breal, n) call nga_get_field(g_b, lo, hi, 8, 8, bimg, n) do j = 1, n do i = 1, n if (breal(i,j) .ne. areal(i,j)) then write(6,*) ' copy ', me, i, j, areal(i,j), breal(i,j) call ga_error('... exiting ',0) endif if (bimg(i,j) .ne. aimg(i,j)) then write(6,*) ' copy ', me, i, j, aimg(i,j), bimg(i,j) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c c Delete the global arrays c status = ga_destroy(g_b) status = ga_destroy(g_a) end subroutine util_qfill(n,val,a,ia) implicit none double precision a(*), val integer n, ia, i c c initialise double complex array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end ga-5.9.2/global/testing/g2test.F000066400000000000000000000542671500715745200163760ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c vector boxes lack arithmetic precision # define THRESH 1d-13 # define THRESHF 1e-5 #define MISMATCH(x,y) abs(x-y)/max(1d0,abs(x)).gt.THRESH #define MISMATCHF(x,y) abs(x-y)/max(1.0,abs(x)).gt.THRESHF #define USE_CORNERS #define PRINT_VAL c#define NEW_API c c Add some control over which tests are performed c #define TEST_1 #define TEST_2 #define TEST_3 #define TEST_4 c#define TEST_5 #define TEST_6 #define TEST_7 #define TEST_8 program main implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer heap, stack, fudge, ma_heap, me integer nmax, DIM, nwidth, MAXPROC, nloop parameter (nmax = 1000, DIM = 2, nwidth = 2, MAXPROC = 2000) parameter (nloop = 50) integer ndim, nproc, pdims(7), type, dcnt, g_a, maxval, nbhandle integer i, j, dims(7), width(7), map(2*nmax) integer lo(7), hi(7), ld(7) integer lo2(7), hi2(7), ld2(7) integer dims3(7), ld3(7), chunk(7) MA_ACCESS_INDEX_TYPE index3 integer a(nmax, nmax), b(nmax+2*nwidth,nmax+2*nwidth) double precision start,t1,t2,t3,t4,t5,tmp double precision t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18 double precision t19,t20,t21,t22,t23,t24,t25,t26,t27 logical status, safe_put, safe_get, has_data(0:MAXPROC-1) parameter (heap=1000*1000*4, fudge=1000, stack=1000*1000) logical corner_flag c c*** Intitialize a message passing library c #include "mp3.fh" #ifdef USE_CORNERS corner_flag = .true. #else corner_flag = .false. #endif c c*** Initialize GA c c There are 2 choices: ga_initialize or ga_initialize_ltd. c In the first case, there is no explicit limit on memory usage. c In the second, user can set limit (per processor) in bytes. c print* call ga_initialize() nproc = ga_nnodes() me = ga_nodeid() c we can also use GA_set_memory_limit BEFORE first ga_create call c ma_heap = heap + fudge call GA_set_memory_limit(util_mdtob(ma_heap)) c if(ga_nodeid().eq.0)then print *,' GA initialized ' call ffflush(6) endif c c*** Initialize the MA package c MA must be initialized before any global array is allocated c status = ma_init(MT_DCPL, stack, ma_heap) if (.not. status) call ga_error('ma_init failed',-1) c if(me.eq.0)then print *, 'using ', nproc, ' process(es)' call ffflush(6) endif c c Test ghost distributions c ndim = DIM c c Create irregular distribution on all nodes c call factor(nproc,ndim,pdims) dims(1) = pdims(1) * nmax dims(2) = pdims(2) * nmax maxval = 1 do i = 1, ndim maxval = dims(i)*maxval end do maxval = maxval - 1 c dcnt = 1 do i = 1, pdims(1) map(dcnt) = (i-1)*nmax + 1 dcnt = dcnt + 1 end do do i = 1, pdims(2) map(dcnt) = (i-1)*nmax + 1 dcnt = dcnt + 1 end do c do i = 1, ndim width(i) = nwidth chunk(i) = 1 if (pdims(i).gt.dims(i)) pdims(i) = dims(i) if (me.eq.0) then write(6,*) 'Value of pdims(',i,') is ',pdims(i) endif call ffflush(6) c do j = 1, pdims(i) c if (j.eq.1) then c map(dcnt) = 1 c else c map(dcnt) = ((j-1)*dims(i))/pdims(i) + 1 c endif c dcnt = dcnt + 1 c end do ld(i) = nmax end do if (me.eq.0) then do i = 1, dcnt - 1 write(6,'("map(",i2,") = ",i5)') i,map(i) call ffflush(6) end do endif type = MT_INT #ifdef NEW_API g_a = ga_create_handle(); call ga_set_data(g_a,ndim,dims,type) call ga_set_array_name(g_a,"test_array") call ga_set_irreg_distr(g_a,map,pdims) call ga_set_ghosts(g_a,width) status = ga_allocate(g_a) #else status = nga_create_ghosts_irreg (type, ndim, dims, width, + "test_array", map, pdims, g_a) c status = nga_create_ghosts(type, ndim, dims, width, c + "test_array", chunk, g_a) #endif if (status.and.me.eq.0) then write(6,*) '*' write(6,*) '* Global array creation was successful' write(6,*) '*' elseif (.not.status) then write(6,*) 'Global array creation failure on ',me endif c c Find processors that have data c call ga_sync do i = 0, nproc-1 call nga_distribution(g_a, i, lo, hi) has_data(i) = .true. do j = 1, ndim if (lo(j).eq.0.and.hi(j).eq.-1) has_data(i) = .false. end do call ffflush(6) call ga_sync end do c c initialize g_a c call ga_sync call nga_distribution(g_a, me, lo, hi) do i = 1, hi(1) - lo(1) + 1 do j = 1, hi(2) - lo(2) + 1 a(i,j) = (i + lo(1) - 2)*dims(1) + (j + lo(2) - 2) end do end do safe_put = .true. do i = 1, ndim if (hi(i).lt.lo(i)) safe_put = .false. end do if (has_data(me).and.safe_put) call nga_put(g_a, lo, hi, a, ld) c c get patch with ghost cells c do i = 1, ndim lo2(i) = lo(i) - width(i) hi2(i) = hi(i) + width(i) ld2(i) = ld(i) + 2*width(i) end do call ga_sync call ffflush(6) safe_get = .true. c do i = 1, ndim c if (hi2(i)-lo2(i).ge.dims(i)) safe_get = .false. c end do t19 = 0.0d00 t20 = 0.0d00 t21 = 0.0d00 do i = 1, nloop start = util_timer() call ga_ghost_barrier t19 = t19 + util_timer() - start start = util_timer() if (has_data(me).and.safe_get) + call nga_periodic_get(g_a, lo2, hi2, b, ld2) t20 = t20 + util_timer() - start start = util_timer() call ga_ghost_barrier t21 = t21 + util_timer() - start end do t19 = t19/dble(nloop) t20 = t20/dble(nloop) t21 = t21/dble(nloop) 102 format(14i5) if (me.eq.0) then write(6,*) '*' write(6,*) '* Performing nga_access_ghosts' write(6,*) '*' call ffflush(6) endif if (has_data(me)) call nga_access_ghosts(g_a, dims3, + index3, ld3) call ga_sync #ifdef TEST_1 if (me.eq.0) then write(6,*) write(6,*) 'Testing update 1' write(6,*) endif t1 = 0.0d00 t2 = 0.0d00 t3 = 0.0d00 do i = 1, nloop call zero_ghosts(int_mb(index3),ld3(1),a,hi(1)-lo(1)+1, + width,dims3) start = util_timer() call ga_ghost_barrier t1 = t1 + util_timer() - start start = util_timer() call ga_update1_ghosts(g_a) t2 = t2 + util_timer() - start start = util_timer() call ga_ghost_barrier t3 = t3 + util_timer() - start end do t1 = t1/dble(nloop) t2 = t2/dble(nloop) t3 = t3/dble(nloop) #ifdef PRINT_VAL if (maxval.lt.10000) + call aprint(int_mb(index3),dims3(1),dims3(2),ld3,has_data) #endif call atest(int_mb(index3),dims3(1),dims3(2),ld3(1),b, + nmax+2*width(1),has_data,width,corner_flag,1) call ga_sync #endif #ifdef TEST_2 if (me.eq.0) then write(6,*) write(6,*) 'Testing update 2' write(6,*) endif t4 = 0.0d00 t5 = 0.0d00 t6 = 0.0d00 do i = 1, nloop call zero_ghosts(int_mb(index3),ld3(1),a,hi(1)-lo(1)+1, + width,dims3) start = util_timer() call ga_ghost_barrier t4 = t4 + util_timer() - start start = util_timer() status = ga_update2_ghosts(g_a) t5 = t5 + util_timer() - start start = util_timer() call ga_ghost_barrier t6 = t6 + util_timer() - start end do t4 = t4/dble(nloop) t5 = t5/dble(nloop) t6 = t6/dble(nloop) #ifdef PRINT_VAL if (maxval.lt.10000) + call aprint(int_mb(index3),dims3(1),dims3(2),ld3,has_data) #endif call atest(int_mb(index3),dims3(1),dims3(2),ld3(1),b, + nmax+2*width(1),has_data,width,corner_flag,2) call ga_sync #endif #ifdef TEST_3 if (me.eq.0) then write(6,*) write(6,*) 'Testing update 3' write(6,*) endif t7 = 0.0d00 t8 = 0.0d00 t9 = 0.0d00 do i = 1, nloop call zero_ghosts(int_mb(index3),ld3(1),a,hi(1)-lo(1)+1, + width,dims3) start = util_timer() call ga_ghost_barrier t7 = t7 + util_timer() - start start = util_timer() status = ga_update3_ghosts(g_a) t8 = t8 + util_timer() - start start = util_timer() call ga_ghost_barrier t9 = t9 + util_timer() - start end do t7 = t7/dble(nloop) t8 = t8/dble(nloop) t9 = t9/dble(nloop) #ifdef PRINT_VAL if (maxval.lt.10000) + call aprint(int_mb(index3),dims3(1),dims3(2),ld3,has_data) #endif call atest(int_mb(index3),dims3(1),dims3(2),ld3(1),b, + nmax+2*width(1),has_data,width,corner_flag,3) call ga_sync #endif #ifdef TEST_4 if (me.eq.0) then write(6,*) write(6,*) 'Testing update 4' write(6,*) endif t10 = 0.0d00 t11 = 0.0d00 t12 = 0.0d00 call ga_sync call ga_set_ghost_corner_flag(g_a,corner_flag) c status = ga_set_update4_info(g_a) do i = 1, nloop call zero_ghosts(int_mb(index3),ld3(1),a,hi(1)-lo(1)+1, + width,dims3) start = util_timer() call ga_ghost_barrier t10 = t10 + util_timer() - start start = util_timer() status = ga_update4_ghosts(g_a) t11 = t11 + util_timer() - start start = util_timer() call ga_ghost_barrier t12 = t12 + util_timer() - start end do t10 = t10/dble(nloop) t11 = t11/dble(nloop) t12 = t12/dble(nloop) #ifdef PRINT_VAL if (maxval.lt.10000) + call aprint(int_mb(index3),dims3(1),dims3(2),ld3,has_data) #endif call atest(int_mb(index3),dims3(1),dims3(2),ld3(1),b, + nmax+2*width(1),has_data,width,corner_flag,4) call ga_sync #endif #ifdef TEST_5 if (me.eq.0) then write(6,*) write(6,*) 'Testing update 5' write(6,*) endif t13 = 0.0d00 t14 = 0.0d00 t15 = 0.0d00 call ga_sync call ga_set_ghost_corner_flag(g_a,corner_flag) c status = ga_set_update5_info(g_a) do i = 1, nloop call zero_ghosts(int_mb(index3),ld3(1),a,hi(1)-lo(1)+1, + width,dims3) start = util_timer() call ga_ghost_barrier t13 = t13 + util_timer() - start start = util_timer() c write(6,*) 'got to ga_update5_ghosts' status = ga_update5_ghosts(g_a) c write(6,*) 'completed ga_update5_ghosts' t14 = t14 + util_timer() - start start = util_timer() call ga_ghost_barrier t15 = t15 + util_timer() - start end do t13 = t13/dble(nloop) t14 = t14/dble(nloop) t15 = t15/dble(nloop) #ifdef PRINT_VAL if (maxval.lt.10000) + call aprint(int_mb(index3),dims3(1),dims3(2),ld3,has_data) #endif call atest(int_mb(index3),dims3(1),dims3(2),ld3(1),b, + nmax+2*width(1),has_data,width,corner_flag,5) call ga_sync #endif #ifdef TEST_6 if (me.eq.0) then write(6,*) write(6,*) 'Testing update 6' write(6,*) endif t16 = 0.0d00 t17 = 0.0d00 t18 = 0.0d00 call ga_sync do i = 1, nloop call zero_ghosts(int_mb(index3),ld3(1),a,hi(1)-lo(1)+1, + width,dims3) start = util_timer() call ga_ghost_barrier t16 = t16 + util_timer() - start start = util_timer() status = ga_update6_ghosts(g_a) t17 = t17 + util_timer() - start start = util_timer() call ga_ghost_barrier t18 = t18 + util_timer() - start end do t16 = t16/dble(nloop) t17 = t17/dble(nloop) t18 = t18/dble(nloop) #ifdef PRINT_VAL if (maxval.lt.10000) + call aprint(int_mb(index3),dims3(1),dims3(2),ld3,has_data) #endif call atest(int_mb(index3),dims3(1),dims3(2),ld3(1),b, + nmax+2*width(1),has_data,width,corner_flag,6) call ga_sync #endif #ifdef TEST_7 if (me.eq.0) then write(6,*) write(6,*) 'Testing update 7' write(6,*) endif t22 = 0.0d00 t23 = 0.0d00 t24 = 0.0d00 call ga_sync do i = 1, nloop call zero_ghosts(int_mb(index3),ld3(1),a,hi(1)-lo(1)+1, + width,dims3) start = util_timer() call ga_ghost_barrier t22 = t22 + util_timer() - start start = util_timer() status = ga_update7_ghosts(g_a) t23 = t23 + util_timer() - start start = util_timer() call ga_ghost_barrier t24 = t24 + util_timer() - start end do t22 = t22/dble(nloop) t23 = t23/dble(nloop) t24 = t24/dble(nloop) #ifdef PRINT_VAL if (maxval.lt.10000) + call aprint(int_mb(index3),dims3(1),dims3(2),ld3,has_data) #endif call atest(int_mb(index3),dims3(1),dims3(2),ld3(1),b, + nmax+2*width(1),has_data,width,corner_flag,7) call ga_sync #endif #ifdef TEST_8 if (me.eq.0) then write(6,*) write(6,*) 'Testing non-blocking update' write(6,*) endif t25 = 0.0d00 t26 = 0.0d00 t27 = 0.0d00 call ga_sync do i = 1, nloop call zero_ghosts(int_mb(index3),ld3(1),a,hi(1)-lo(1)+1, + width,dims3) start = util_timer() call ga_ghost_barrier t25 = t25 + util_timer() - start start = util_timer() call nga_update_ghosts_nb(g_a,nbhandle) call ga_nbwait(nbhandle) t26 = t26 + util_timer() - start start = util_timer() call ga_ghost_barrier t27 = t27 + util_timer() - start end do t25 = t25/dble(nloop) t26 = t26/dble(nloop) t27 = t27/dble(nloop) #ifdef PRINT_VAL if (maxval.lt.10000) + call aprint(int_mb(index3),dims3(1),dims3(2),ld3,has_data) #endif call atest(int_mb(index3),dims3(1),dims3(2),ld3(1),b, + nmax+2*width(1),has_data,width,corner_flag,8) call ga_sync #endif if (me.eq.0) then write(6,*) '*' write(6,*) '* Completed updates successfully' write(6,*) '*' call ffflush(6) endif call ga_sync c #ifdef TEST_1 tmp = t2 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,300) 1,tmp/dble(nproc) endif tmp = t1 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,400) tmp/dble(nproc) endif tmp = t3 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,500) tmp/dble(nproc) endif #endif #ifdef TEST_2 tmp = t5 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,300) 2,tmp/dble(nproc) endif tmp = t4 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,400) tmp/dble(nproc) endif tmp = t6 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,500) tmp/dble(nproc) endif #endif #ifdef TEST_3 tmp = t8 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,300) 3,tmp/dble(nproc) endif tmp = t7 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,400) tmp/dble(nproc) endif tmp = t9 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,500) tmp/dble(nproc) endif #endif #ifdef TEST_4 tmp = t11 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,300) 4,tmp/dble(nproc) endif tmp = t10 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,400) tmp/dble(nproc) endif tmp = t12 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,500) tmp/dble(nproc) endif #endif #ifdef TEST_5 tmp = t14 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,300) 5,tmp/dble(nproc) endif tmp = t13 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,400) tmp/dble(nproc) endif tmp = t15 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,500) tmp/dble(nproc) endif #endif #ifdef TEST_6 tmp = t17 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,300) 6,tmp/dble(nproc) endif tmp = t16 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,400) tmp/dble(nproc) endif tmp = t18 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,500) tmp/dble(nproc) endif #endif #ifdef TEST_7 tmp = t23 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,300) 7,tmp/dble(nproc) endif tmp = t22 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,400) tmp/dble(nproc) endif tmp = t24 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,500) tmp/dble(nproc) endif #endif #ifdef TEST_8 tmp = t26 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,300) 8,tmp/dble(nproc) endif tmp = t25 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,400) tmp/dble(nproc) endif tmp = t27 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,500) tmp/dble(nproc) endif #endif tmp = t20 call ga_dgop(6,tmp,1,'+') if (me.eq.0) then write(6,310) tmp/dble(nproc) endif tmp = t19 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,400) tmp/dble(nproc) endif tmp = t21 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,500) tmp/dble(nproc) endif 300 format('Average time for ga_update',i1,'_ghosts ',e12.3) 310 format('Average time for nga_periodic_get ',e12.3) 400 format(' Average time for prior sync ',e12.3) 500 format(' Average time for post sync ',e12.3) 127 continue c if(ga_nodeid().eq.0) print *,'All tests successful ' c c*** Tidy up the GA package c call ga_terminate() c c*** Tidy up after message-passing library c call MP_FINALIZE() c stop end c subroutine zero_ghosts(a,lda,b,ldb,w,dims) integer lda,ldb,w(*),dims(*) integer a(lda,*),b(ldb,*) integer i, j do j = 1, dims(2) do i = 1, dims(1) if ((i.gt.w(1).and.i.le.dims(1)-w(1)).and. + (j.gt.w(2).and.j.le.dims(2)-w(2))) then a(i,j) = b(i-w(1),j-w(2)) else a(i,j) = 0 endif end do end do return end c subroutine aprint(a,nrow,ncol,ld,has_data) #include "global.fh" integer ld integer a(ld,*) integer i, j, k, nproc logical has_data(0:1999) nproc = ga_nnodes() do k = 1, nproc call ga_sync if (k-1.eq.ga_nodeid().and.has_data(k-1)) then write(6,*) '*' write(6,*) '* Data on processor ',k-1 write(6,*) '*' do i = 1, min(nrow,12) write (6,102) (a(i,j), j = 1, min(ncol,12)) 102 format(14i5) end do endif call ffflush(6) enddo c return end c subroutine atest(a,nrow,ncol,ld,b,ld2,has_data,width, + check_corner, idx) #include "global.fh" integer ld, width(7) integer a(ld,*), b(ld2,*) integer i, j, nproc, me, idx logical has_data(0:1999), check_data logical check_corner nproc = ga_nnodes() me = ga_nodeid() check_data = .true. call ga_sync if (has_data(me)) then do i = 1, nrow do j = 1, ncol if (.not.check_corner.and.(.not. + ((i.le.width(1).and.j.le.width(2)).or. + (i.le.width(1).and.j.gt.ncol-width(2)).or. + (i.gt.nrow-width(1).and.j.le.width(2)).or. + (i.gt.nrow-width(1).and.j.gt.ncol-width(2))))) then if (a(i,j).ne.b(i,j)) check_data = .false. else if (check_corner) then if (a(i,j).ne.b(i,j)) check_data = .false. endif end do end do else check_data = .false. endif if (check_data) then i = 1 else i = 0 endif call ga_igop(1,i,1,'+') if (i.ne.nproc) then check_data = .false. else check_data = .true. endif if (check_data.and.me.eq.0) then write(6,*) '*' write(6,*) '* Data from nga_access_ghosts and' write(6,*) '* nga_periodic_get is the same on' write(6,100) idx 100 format( ' * all processors for update ',i1) write(6,*) '*' else if (.not.check_data) then write(6,*) '*' write(6,*) '* Data from nga_access_ghosts and' write(6,*) '* nga_periodic_get is NOT the same on' write(6,200) me,idx 200 format( ' * processor ',i2,' for update ',i1) write(6,*) '*' endif call ffflush(6) c return end c subroutine factor(p,ndim,dims) implicit none integer i,j,p,ndim,dims(7),imin,mdim integer ip,ifac,pmax,prime(1000) integer fac(1000) c i = 1 ip = p do i = 1, ndim dims(i) = 1 end do c c factor p completely c first, find all prime numbers less than or equal to p c pmax = 0 do i = 2, p do j = 1, pmax if (mod(i,prime(j)).eq.0) go to 100 end do pmax = pmax + 1 prime(pmax) = i 100 continue end do c c find all prime factors of p c ifac = 0 do i = 1, pmax 200 if (mod(ip,prime(i)).eq.0) then ifac = ifac + 1 fac(ifac) = prime(i) ip = ip/prime(i) go to 200 endif end do c c determine dimensions of processor grid c do i = ifac, 1, -1 c c find dimension with minimum value c imin = dims(1) mdim = 1 do j = 2, ndim if (dims(j).lt.imin) then imin = dims(j) mdim = j endif end do dims(mdim) = dims(mdim)*fac(i) end do c return end ga-5.9.2/global/testing/g3test.F000066400000000000000000000452421500715745200163700ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c vector boxes lack arithmetic precision # define THRESH 1d-13 # define THRESHF 1e-5 #define MISMATCH(x,y) abs(x-y)/max(1d0,abs(x)).gt.THRESH #define MISMATCHF(x,y) abs(x-y)/max(1.0,abs(x)).gt.THRESHF c Mirrored arrays do not work with ghost cells c#define MIRROR program main implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer heap, stack, fudge, ma_heap, me integer nmax, DIM, nwidth, MAXPROC, nloop parameter (nmax = 20, DIM = 3, nwidth = 2, MAXPROC = 2000) parameter (nloop = 1) integer ndim, nproc, pdims(7), type, dcnt, g_a, g_b, maxval integer i, j, k, dims(7), width(7), map(2*nmax) integer lo(7), hi(7), ld(7) integer lo2(7), hi2(7), ld2(7) integer dims3(7), ld3(7), chunk(7) MA_ACCESS_INDEX_TYPE index3 integer a(nmax, nmax, nmax) integer b(nmax+2*nwidth,nmax+2*nwidth,nmax+2*nwidth) #ifdef MIRROR integer lprocs, iproc, inode, p_mirror #endif double precision start,finish,start1,finish1 double precision t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13 double precision t14,t15,t16,t17,t18,t19,t20,t21,tmp logical status, safe_put, safe_get, has_data(0:MAXPROC-1) parameter (heap=100*nmax*nmax*nmax*4, fudge=100, stack=1000*100) c c*** Intitialize a message passing library c #include "mp3.fh" c c*** Initialize GA c c There are 2 choices: ga_initialize or ga_initialize_ltd. c In the first case, there is no explicit limit on memory usage. c In the second, user can set limit (per processor) in bytes. c print* call ga_initialize() nproc = ga_nnodes() me = ga_nodeid() #ifdef MIRROR inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) iproc = mod(me,lprocs) p_mirror = ga_pgroup_get_mirror() #endif c we can also use GA_set_memory_limit BEFORE first ga_create call c ma_heap = heap + fudge call GA_set_memory_limit(util_mdtob(ma_heap)) c if(ga_nodeid().eq.0)then print *,' GA initialized ' call ffflush(6) endif c c*** Initialize the MA package c MA must be initialized before any global array is allocated c status = ma_init(MT_DCPL, stack, ma_heap) if (.not. status) call ga_error('ma_init failed',-1) c if(me.eq.0)then #ifndef MIRROR print *, 'using ', nproc, ' process(es)' #else print *, 'using ', ga_cluster_nnodes(), ' nodes' print *, 'using ',lprocs,' process(es) per node' #endif call ffflush(6) endif c c Test ghost distributions c ndim = DIM c c Create irregular distribution on all nodes c #ifndef MIRROR call factor(nproc,ndim,pdims) #else call factor(lprocs,ndim,pdims) #endif dims(1) = pdims(1) * nmax dims(2) = pdims(2) * nmax dims(3) = pdims(3) * nmax maxval = 1 do i = 1, ndim maxval = dims(i)*maxval end do maxval = maxval - 1 c dcnt = 1 do i = 1, pdims(1) map(dcnt) = (i-1)*nmax + 1 dcnt = dcnt + 1 end do do i = 1, pdims(2) map(dcnt) = (i-1)*nmax + 1 dcnt = dcnt + 1 end do do i = 1, pdims(3) map(dcnt) = (i-1)*nmax + 1 dcnt = dcnt + 1 end do c do i = 1, ndim width(i) = nwidth chunk(i) = 1 if (pdims(i).gt.dims(i)) pdims(i) = dims(i) if (me.eq.0) then write(6,*) 'Value of pdims(',i,') is ',pdims(i) endif ld(i) = nmax call ffflush(6) end do if (me.eq.0) then do i = 1, dcnt - 1 write(6,'("map(",i2,") = ",i5)') i,map(i) call ffflush(6) end do endif type = MT_INT #ifndef MIRROR status = nga_create_ghosts_irreg (type, ndim, dims, width, + "test_array_a", map, pdims, g_a) c status = nga_create_ghosts_irreg (type, ndim, dims, width, c + "test_array_b", map, pdims, g_b) #else status = nga_create_ghosts_irreg_config (type, ndim, dims, width, + "test_array_a", map, pdims, p_mirror, g_a) c status = nga_create_ghosts_irreg_config (type, ndim, dims, width, c + "test_array_b", map, pdims, p_mirror, g_b) #endif if (status.and.me.eq.0) then write(6,*) '*' write(6,*) '* Global array creation was successful' write(6,*) '*' elseif (.not.status) then write(6,*) 'Global array creation failure on ',me endif #ifndef MIRROR status = nga_create_irreg (type, ndim, dims,"test_array_b", + map, pdims, g_b) #else status = nga_create_irreg_config (type, ndim, dims,"test_array_b", + map, pdims, p_mirror, g_b) #endif if (.not.status) then write(6,*) 'Global array creation failure on ',me endif c c Find processors that have data c call ga_sync #ifndef MIRROR do i = 0, nproc-1 #else do i = 0, lprocs-1 #endif call nga_distribution(g_a, i, lo, hi) has_data(i) = .true. do j = 1, ndim if (lo(j).eq.0.and.hi(j).eq.-1) has_data(i) = .false. end do #ifndef MIRROR if (me.eq.i) then write(6,*) '*' write(6,*) '* Distribution on processor ',i write(6,*) '*' write(6,110) lo(1), hi(1) write(6,110) lo(2), hi(2) write(6,110) lo(3), hi(3) 110 format(2i10) endif #endif call ffflush(6) call ga_sync end do c c initialize g_a c call ga_sync call nga_distribution(g_a, me, lo, hi) do i = 1, hi(1) - lo(1) + 1 do j = 1, hi(2) - lo(2) + 1 do k = 1, hi(3) - lo(3) + 1 a(i,j,k) = (i + lo(1) - 2)*dims(1)*dims(2) + + (j + lo(2) - 2)*dims(1) + + (k + lo(3) - 2) end do end do end do safe_put = .true. do i = 1, ndim if (hi(i).lt.lo(i)) safe_put = .false. end do if (has_data(me).and.safe_put) call nga_put(g_a, lo, hi, a, ld) call ga_copy(g_a,g_b) if (has_data(me)) call nga_access_ghosts(g_a, dims3, + index3, ld3) do i = 1, ndim lo2(i) = lo(i) - width(i) hi2(i) = hi(i) + width(i) ld2(i) = ld(i) + 2*width(i) end do call ga_sync safe_get = .true. c if (me.eq.0) write(6,*) '*' if (me.eq.0) write(6,*) '* Starting update 1' if (me.eq.0) write(6,*) '*' t1 = 0.0d00 t2 = 0.0d00 t3 = 0.0d00 call ga_zero(g_a) call ga_copy(g_b,g_a) do i = 1, nloop start = util_timer() call ga_ghost_barrier t1 = t1 + util_timer() - start start = util_timer() call ga_update1_ghosts(g_a) t2 = t2 + util_timer() - start start = util_timer() call ga_ghost_barrier t3 = t3 + util_timer() - start end do t1 = t1/dble(nloop) t2 = t2/dble(nloop) t3 = t3/dble(nloop) if (me.eq.0) then write(6,*) '*' write(6,*) '* Testing results of update 1' write(6,*) '*' call ffflush(6) endif if (has_data(me).and.safe_get) + call nga_periodic_get(g_a, lo2, hi2, b, ld2) call atest(int_mb(index3),dims3,ld3(1),b,nmax+2*nwidth,has_data) if (me.eq.0) write(6,*) '*' if (me.eq.0) write(6,*) '* Starting update 2' if (me.eq.0) write(6,*) '*' t4 = 0.0d00 t5 = 0.0d00 t6 = 0.0d00 call ga_zero(g_a) call ga_copy(g_b,g_a) do i = 1, nloop start = util_timer() call ga_ghost_barrier t4 = t4 + util_timer() - start start = util_timer() status = ga_update2_ghosts(g_a) t5 = t5 + util_timer() - start start = util_timer() call ga_ghost_barrier t6 = t6 + util_timer() - start end do t4 = t4/dble(nloop) t5 = t5/dble(nloop) t6 = t6/dble(nloop) if (me.eq.0) then write(6,*) '*' write(6,*) '* Testing results of update 2' write(6,*) '*' call ffflush(6) endif if (has_data(me).and.safe_get) + call nga_periodic_get(g_a, lo2, hi2, b, ld2) call atest(int_mb(index3),dims3,ld3(1),b,nmax+2*nwidth,has_data) t7 = 0.0d00 t8 = 0.0d00 t9 = 0.0d00 if (me.eq.0) write(6,*) '*' if (me.eq.0) write(6,*) '* Starting update 3' if (me.eq.0) write(6,*) '*' call ga_zero(g_a) call ga_copy(g_b,g_a) do i = 1, nloop start = util_timer() call ga_ghost_barrier t7 = t7 + util_timer() - start start = util_timer() status = ga_update3_ghosts(g_a) t8 = t8 + util_timer() - start start = util_timer() call ga_ghost_barrier t9 = t9 + util_timer() - start end do t7 = t7/dble(nloop) t8 = t8/dble(nloop) t9 = t9/dble(nloop) if (me.eq.0) then write(6,*) '*' write(6,*) '* Testing results of update 3' write(6,*) '*' call ffflush(6) endif if (has_data(me).and.safe_get) + call nga_periodic_get(g_a, lo2, hi2, b, ld2) call atest(int_mb(index3),dims3,ld3(1),b,nmax+2*nwidth,has_data) t10 = 0.0d00 t11 = 0.0d00 t12 = 0.0d00 if (me.eq.0) write(6,*) '*' if (me.eq.0) write(6,*) '* Starting update 4' if (me.eq.0) write(6,*) '*' call ga_zero(g_a) call ga_copy(g_b,g_a) do i = 1, nloop start = util_timer() call ga_ghost_barrier t10 = t10 + util_timer() - start start = util_timer() status = ga_update4_ghosts(g_a) t11 = t11 + util_timer() - start start = util_timer() call ga_ghost_barrier t12 = t12 + util_timer() - start end do t10 = t10/dble(nloop) t11 = t11/dble(nloop) t12 = t12/dble(nloop) if (me.eq.0) then write(6,*) '*' write(6,*) '* Testing results of update 4' write(6,*) '*' call ffflush(6) endif if (has_data(me).and.safe_get) + call nga_periodic_get(g_a, lo2, hi2, b, ld2) call atest(int_mb(index3),dims3,ld3(1),b,nmax+2*nwidth,has_data) c 123 continue #if 0 t13 = 0.0d00 t14 = 0.0d00 t15 = 0.0d00 if (me.eq.0) write(6,*) '*' if (me.eq.0) write(6,*) '* Starting update 5' if (me.eq.0) write(6,*) '*' call ga_zero(g_a) call ga_copy(g_b,g_a) do i = 1, nloop start = util_timer() call ga_ghost_barrier t13 = t13 + util_timer() - start start = util_timer() status = ga_update5_ghosts(g_a) t14 = t14 + util_timer() - start start = util_timer() call ga_ghost_barrier t15 = t15 + util_timer() - start end do t13 = t13/dble(nloop) t14 = t14/dble(nloop) t15 = t15/dble(nloop) if (me.eq.0) then write(6,*) '*' write(6,*) '* Testing results of update 5' write(6,*) '*' call ffflush(6) endif if (has_data(me).and.safe_get) + call nga_periodic_get(g_a, lo2, hi2, b, ld2) call atest(int_mb(index3),dims3,ld3(1),b,nmax+2*nwidth,has_data) #else t13 = 0.0d00 t14 = 0.0d00 t15 = 0.0d00 #endif t16 = 0.0d00 t17 = 0.0d00 t18 = 0.0d00 if (me.eq.0) write(6,*) '*' if (me.eq.0) write(6,*) '* Starting update 6' if (me.eq.0) write(6,*) '*' call ga_zero(g_a) call ga_copy(g_b,g_a) do i = 1, nloop start = util_timer() call ga_ghost_barrier t16 = t16 + util_timer() - start start = util_timer() status = ga_update6_ghosts(g_a) t17 = t17 + util_timer() - start start = util_timer() call ga_ghost_barrier t18 = t18 + util_timer() - start end do t16 = t16/dble(nloop) t17 = t17/dble(nloop) t18 = t18/dble(nloop) if (me.eq.0) then write(6,*) '*' write(6,*) '* Testing results of update 6' write(6,*) '*' call ffflush(6) endif if (has_data(me).and.safe_get) + call nga_periodic_get(g_a, lo2, hi2, b, ld2) call atest(int_mb(index3),dims3,ld3(1),b,nmax+2*nwidth,has_data) write(6,*) 'Update finished on processor ',me if (me.eq.0) then write(6,*) '*' write(6,*) '* Completed updates successfully' write(6,*) '*' call ffflush(6) endif c c get patch with ghost cells c t19 = 0.0d00 t20 = 0.0d00 t21 = 0.0d00 do i = 1, nloop start = util_timer() call ga_ghost_barrier t19 = t19 + util_timer() - start start = util_timer() if (has_data(me).and.safe_get) + call nga_periodic_get(g_a, lo2, hi2, b, ld2) t20 = t20 + util_timer() - start start = util_timer() call ga_ghost_barrier t21 = t21 + util_timer() - start end do t19 = t19/dble(nloop) t20 = t20/dble(nloop) t21 = t21/dble(nloop) c tmp = t2 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) 'Average time for nga_update1_ghosts ', + tmp/dble(nproc) endif tmp = t1 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) ' Average time for prior sync ',tmp/dble(nproc) endif tmp = t3 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) ' Average time for post sync ',tmp/dble(nproc) endif tmp = t5 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) 'Average time for nga_update2_ghosts ', + tmp/dble(nproc) endif tmp = t4 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) ' Average time for prior sync ',tmp/dble(nproc) endif tmp = t6 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) ' Average time for post sync ',tmp/dble(nproc) endif tmp = t8 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) 'Average time for nga_update3_ghosts ', + tmp/dble(nproc) endif tmp = t7 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) ' Average time for prior sync ',tmp/dble(nproc) endif tmp = t9 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) ' Average time for post sync ',tmp/dble(nproc) endif tmp = t11 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) 'Average time for nga_update4_ghosts ', + tmp/dble(nproc) endif tmp = t10 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) ' Average time for prior sync ',tmp/dble(nproc) endif tmp = t12 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) ' Average time for post sync ',tmp/dble(nproc) endif tmp = t14 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) 'Average time for nga_update5_ghosts ', + tmp/dble(nproc) endif tmp = t13 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) ' Average time for prior sync ',tmp/dble(nproc) endif tmp = t15 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) ' Average time for post sync ',tmp/dble(nproc) endif tmp = t17 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) 'Average time for nga_update6_ghosts ', + tmp/dble(nproc) endif tmp = t16 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) ' Average time for prior sync ',tmp/dble(nproc) endif tmp = t18 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) ' Average time for post sync ',tmp/dble(nproc) endif tmp = t20 call ga_dgop(6,tmp,1,'+') if (me.eq.0) then write(6,*) 'Average time for nga_periodic_get ',tmp/dble(nproc) endif tmp = t19 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) ' Average time for prior sync ',tmp/dble(nproc) endif tmp = t21 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) ' Average time for post sync ',tmp/dble(nproc) endif 127 continue c if(ga_nodeid().eq.0) print *,'All tests successful ' c c*** Tidy up the GA package c call ga_terminate() c c*** Tidy up after message-passing library c call MP_FINALIZE() c stop end c subroutine atest(a,dim,ld,b,ld2,has_data) #include "global.fh" integer ld,ld2 integer dim(7) integer a(ld,ld,*), b(ld2,ld2,*) integer i, j, k, l, nproc, lprocs, iproc, inode logical has_data(0:1999), check_data c isok = .true. nproc = ga_nnodes() check_data = .true. do l = 1, nproc call ga_sync if (l-1.eq.ga_nodeid().and.has_data(l-1)) then do i = 1, dim(1) do j = 1, dim(2) do k = 1, dim(3) if (a(i,j,k).ne.b(i,j,k)) check_data = .false. end do end do end do if (check_data) then if (ga_nodeid().eq.0) then write(6,*) '*' write(6,*) '* Data from nga_access_ghosts and' write(6,*) '* nga_periodic_get is the same' write(6,*) '*' endif else write(6,*) '*' write(6,*) '* Data from nga_access_ghosts and' write(6,*) '* nga_periodic_get is NOT the same on' write(6,*) '* processor ',l-1 write(6,*) '*' endif endif call ffflush(6) 101 format(10i5) enddo c return end c subroutine factor(p,ndim,dims) implicit none integer i,j,p,ndim,dims(7),imin,mdim integer ip,ifac,pmax,prime(1000) integer fac(1000) c i = 1 ip = p do i = 1, ndim dims(i) = 1 end do c c factor p completely c first, find all prime numbers less than or equal to p c pmax = 0 do i = 2, p do j = 1, pmax if (mod(i,prime(j)).eq.0) go to 100 end do pmax = pmax + 1 prime(pmax) = i 100 continue end do c c find all prime factors of p c ifac = 0 do i = 1, pmax 200 if (mod(ip,prime(i)).eq.0) then ifac = ifac + 1 fac(ifac) = prime(i) ip = ip/prime(i) go to 200 endif end do c c determine dimensions of processor grid c do i = ifac, 1, -1 c c find dimension with minimum value c imin = dims(1) mdim = 1 do j = 2, ndim if (dims(j).lt.imin) then imin = dims(j) mdim = j endif end do dims(mdim) = dims(mdim)*fac(i) end do c return end ga-5.9.2/global/testing/ga-mpi.c000066400000000000000000000122011500715745200163530ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /**************************************************************************** * program: ga-mpi.c * date: Tue Oct 3 12:31:59 PDT 1995 * author: Jarek Nieplocha * purpose: This program demonstrates interface between GA and MPI. * For a given square matrix, it creates a vector that contains maximum * elements for each matrix row. MPI group communication is used. * * notes: The program can run in two modes: * 1. Using TCGMSG calls available through the TCGMSG-MPI library * and MPI. In this mode initialization must be done with * the TCGMSG PBEGIN call. * 2. Using MPI calls only -- preprocessor symbol MPI must be defined. * ****************************************************************************/ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #include #include "ga.h" #include "ga-mpi.h" #include "globalp.h" #include "macdecls.h" #include "mp3.h" #define N 100 /* dimension of matrices */ void do_work() { int ZERO=0; /* useful constants */ int g_a, g_b; int n=N, ndim=2,type=MT_F_DBL,dims[2]={N,N},coord[2]; int me=GA_Nodeid(), nproc=GA_Nnodes(); int row, i, j; int lo[2], hi[2]; /* Note: on all current platforms DoublePrecision = double */ DoublePrecision buf[N], *max_row=NULL; MPI_Comm WORLD_COMM; MPI_Comm ROW_COMM; int ilo,ihi, jlo,jhi, ld, prow, pcol; int root=0, grp_me=-1; WORLD_COMM = GA_MPI_Comm_pgroup_default(); if(me==0)printf("Creating matrix A\n"); dims[0]=n; dims[1]=n; g_a = NGA_Create(type, ndim, dims, "A", NULL); if(!g_a) GA_Error("create failed: A",n); if(me==0)printf("OK\n"); if(me==0)printf("Creating matrix B\n"); dims[0]=n; g_b = NGA_Create(type, 1, dims, "B", NULL); if(!g_b) GA_Error("create failed: B",n); if(me==0)printf("OK\n"); GA_Zero(g_a); /* zero the matrix */ if(me==0)printf("Initializing matrix A\n"); /* fill in matrix A with values: A(i,j) = (i+j) */ for(row=me; row0){ max_row=(DoublePrecision*)malloc(sizeof(DoublePrecision)*(ihi-ilo+1)); if (!max_row) GA_Error("malloc 3 failed",(ihi-ilo+1)); for (i=0; i<(ihi-ilo+1); i++) { max_row[i] = 0.0; } } NGA_Proc_topology(g_a, me, coord); /* block coordinates */ prow = coord[0]; pcol = coord[1]; if(me==0)printf("Splitting comm according to distribution of A\n"); /* GA on SP1 requires synchronization before & after message-passing !!*/ GA_Sync(); if(me==0)printf("Computing max row elements\n"); /* create communicator for processes that 'own' A[:,jlo:jhi] */ MPI_Barrier(WORLD_COMM); if(pcol < 0 || prow <0) MPI_Comm_split(WORLD_COMM,MPI_UNDEFINED,MPI_UNDEFINED, &ROW_COMM); else MPI_Comm_split(WORLD_COMM, (int)pcol, (int)prow, &ROW_COMM); if(ROW_COMM != MPI_COMM_NULL){ double *ptr; MPI_Comm_rank(ROW_COMM, &grp_me); /* each process computes max elements in the block it 'owns' */ lo[0]=ilo; hi[0]=ihi; lo[1]=jlo; hi[1]=jhi; NGA_Access(g_a, lo, hi, &ptr, &ld); for(i=0; i #endif #if HAVE_STDLIB_H # include #endif #include "ga.h" #include "macdecls.h" #include "mp3.h" #include "galinalg.h" #define BLOCK_CYCLIC #define BLOCK_SIZE 500 #define USE_SCALAPACK_DISTR #define DEBUG 0 static int nprocs, me; static void init_array(double *a, int n) { int i, j; double max=0.0; /* for pivoting */ for(i=0; i0: if INFO = i, U(i,i) is exactly zero. The factorization\n has been completed, but the factor U is exactly singular, and\n division by zero will occur if it is used to solve a system of\n equations.\n"); exit(0); } free(a_verify); free(a); } void dtrsm_lapack(double *a, double *b, int n, char side, char uplo, char transa, char diag) { int i, j; double *aa=NULL; double *bb=NULL; BlasInt ld = (BlasInt)n; BlasInt N = (BlasInt)n; aa = (double*)malloc(n*n*sizeof(double)); bb = (double*)malloc(n*n*sizeof(double)); /* row-major to column-major (NOTE: dgetrf_ is a fortran function) */ for(i=0; i [matrix_size]\n"); exit(0); } if(matrix_size <= 0) { printf("Error: matrix size (%d) should be > 0\n", matrix_size); GA_Error("matrix size should be >0", 1); } /* ***************************************************************** * Initialize MPI/TCGMSG-MPI, GA and MA * *****************************************************************/ MP_INIT(argc,argv); GA_INIT(argc,argv); /* initialize GA */ me = GA_Nodeid(); nprocs = GA_Nnodes(); heap /= nprocs; stack /= nprocs; if(! MA_init(MT_F_DBL, stack, heap)) /* initialize MA */ { GA_Error("MA_init failed",stack+heap); } /* create/initialize the matrix */ if((A = (double*)malloc(matrix_size*matrix_size*sizeof(double))) == NULL) { GA_Error("malloc failed", matrix_size*matrix_size*sizeof(double)); } for(i=0; i<2; i++) /* 5 runs */ { init_array(A, matrix_size); #if DEBUG if(me==0) print_array(A, matrix_size); #endif /* ***************************************************************** * Perform LU Factorization * *****************************************************************/ ga_lu(A, matrix_size); } free(A); /* ***************************************************************** * Terminate MPI/TCGMSG-MPI, GA and MA * *****************************************************************/ if (me == 0) printf("All tests successful\n"); GA_Terminate(); MP_FINALIZE(); return 0; } /** * TODO: * - LU for non-square matrix * */ ga-5.9.2/global/testing/ga_shift.F000066400000000000000000000152111500715745200167340ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif # define THRESH 1d-13 # define THRESHF 1e-5 #define MISMATCH(x,y) abs(x-y)/max(1d0,abs(x)).gt.THRESH #define MISMATCHF(x,y) abs(x-y)/max(1.0,abs(x)).gt.THRESHF program main implicit none #include "mafdecls.fh" #include "global.fh" integer DIM, MAXPROC, NITER parameter (DIM = 2, MAXPROC = 1000000, NITER=10) integer MAXSIZE parameter (MAXSIZE = 2**NITER) integer heap, stack, fudge, ma_heap integer me, nproc, nshift integer iter, lsize, ndim, dims(2), lo(2), hi(2), chunk(2), ld integer g_a integer i, j, ioff double precision tbeg, t_init, t_term, t_ma_init, t_msg_init double precision t_create, t_destroy, t_put, t_get, t_sync, bw double precision a(MAXSIZE,MAXSIZE) logical status c c*** Intitialize a message passing library c #include "mp3.fh" c t_msg_init = ga_wtime() - tbeg c c*** Initialize GA c c There are 2 choices: ga_initialize or ga_initialize_ltd. c In the first case, there is no explicit limit on memory usage. c In the second, user can set limit (per processor) in bytes. c tbeg = ga_wtime() call ga_initialize() t_init = ga_wtime() - tbeg nproc = ga_nnodes() me = ga_nodeid() nshift = nproc/2 c c call ga_dgop(1,t_msg_init,1,'+') c t_msg_init = t_msg_init/dble(nproc) c if (me.eq.0) then c write(6,'(a,f16.6)') 'Time spent in runtime initialization: ', c + t_msg_init c endif call ga_dgop(2,t_init,1,'+') t_init = t_init/dble(nproc) if (me.eq.0) then write(6,'(a,f16.6)') 'Time spent in GA initialization: ', + t_init endif c we can also use GA_set_memory_limit BEFORE first ga_create call c heap = 2000*2000*4 fudge = 100 stack = 2000*2000 ma_heap = heap + fudge call GA_set_memory_limit(8*ma_heap) c if(ga_nodeid().eq.0)then write(6,'(a)') ' ' write(6,'(a)') ' GA initialized ' write(6,'(a)') ' ' call ffflush(6) endif c c*** Initialize the MA package c MA must be initialized before any global array is allocated c tbeg = ga_wtime() status = ma_init(MT_DCPL, stack, ma_heap) t_ma_init = ga_wtime() - tbeg if (.not. status) call ga_error('ma_init failed',-1) call ga_dgop(3,t_ma_init,1,'+') t_ma_init = t_ma_init/dble(nproc) if (me.eq.0) then write(6,'(a,f16.6)') 'Time spent in MA initialization: ', + t_ma_init endif c if(me.eq.0)then write(6,'(a,i8,a)') 'Using ', nproc, ' process(es)' call ffflush(6) endif c c*** begin looping over tests c lsize = 1 do iter = 1, NITER lsize = 2*lsize c c*** create GA that is lsize X lsize*nproc in dimension c if (me.eq.0) then write(6,'(a)') ' ' write(6,'(a,i8)') ' Testing block size of ',lsize*lsize write(6,'(a)') ' ' endif ndim = 2 dims(1) = lsize dims(2) = nproc*lsize chunk(1) = lsize chunk(2) = -1 call ga_sync tbeg = ga_wtime() g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_DBL) call ga_set_chunk(g_a,chunk) status = ga_allocate(g_a) if (.not.status) then call ga_error('ga_allocate failed for size: ',lsize) endif t_create = ga_wtime() - tbeg call ga_dgop(iter,t_create,1,'+') t_create = t_create/dble(nproc) if (me.eq.0) then write(6,'(a,f16.6)') 'Time spent creating GA: ', + t_create endif c c*** Fill local buffer with values c ioff = mod(me+nshift,nproc) ioff = ioff*lsize**2 do j = 1, lsize do i = 1, lsize a(i,j) = dble((j-1)*lsize+i+ioff) end do end do ld = MAXSIZE lo(1) = 1 hi(1) = lsize lo(2) = mod(me+nshift,nproc) lo(2) = lo(2)*lsize + 1 hi(2) = lo(2) - 1 + lsize call ga_zero(g_a) tbeg = ga_wtime() call nga_put(g_a,lo,hi,a,ld) t_put = ga_wtime() - tbeg call ga_dgop(iter+1,t_put,1,'+') t_put = t_put/dble(nproc) if (me.eq.0) then write(6,'(a,f16.6)') 'Time spent in put: ', + t_put endif bw = dble(8*lsize*lsize)/t_put bw = bw/1000000.0d00 if (me.eq.0) then write(6,'(a,f16.6)') 'Bandwidth for put (MB/s): ', + bw endif tbeg = ga_wtime() call ga_sync t_sync = ga_wtime() - tbeg call ga_dgop(iter+2,t_sync,1,'+') t_sync = t_sync/dble(nproc) if (me.eq.0) then write(6,'(a,f16.6)') 'Time spent in sync: ', + t_sync endif bw = dble(8*lsize*lsize)/(t_put+t_sync) bw = bw/1000000.0d00 if (me.eq.0) then write(6,'(a,f16.6)') 'Bandwidth for put with sync (MB/s): ', + bw endif do j = 1, lsize do i = 1, lsize a(i,j) = 0.0d00 end do end do call ga_sync tbeg = ga_wtime() call nga_get(g_a,lo,hi,a,ld) t_get = ga_wtime() - tbeg ioff = mod(me+nshift,nproc) ioff = ioff*lsize**2 do j = 1, lsize do i = 1, lsize if (a(i,j).ne.dble((j-1)*lsize+i+ioff)) then write(6,'(i4,a,i8,a,i8,a)') me,' mismatch for element (', + i,',',j,')' endif end do end do call ga_dgop(iter+3,t_get,1,'+') t_get = t_get/dble(nproc) if (me.eq.0) then write(6,'(a,f16.6)') 'Time spent in get: ', + t_get endif bw = dble(8*lsize*lsize)/t_get bw = bw/1000000.0d00 if (me.eq.0) then write(6,'(a,f16.6)') 'Bandwidth for get (MB/s): ', + bw endif call ga_sync tbeg = ga_wtime() status = ga_destroy(g_a) t_destroy = ga_wtime() - tbeg call ga_dgop(iter+4,t_destroy,1,'+') t_destroy = t_destroy/dble(nproc) if (me.eq.0) then write(6,'(a,f16.6)') 'Time spent destroying GA: ', + t_destroy endif end do c c*** Tidy up the GA package c call ga_terminate() c c*** Tidy up after message-passing library c call MP_FINALIZE() c stop end ga-5.9.2/global/testing/gatscat.c000066400000000000000000000104101500715745200166270ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #include #include "ga.h" #include "macdecls.h" #include "mp3.h" #define N 100 /* dimension of matrices */ int main( int argc, char **argv ) { int g_a, g_b, i, j, size, size_me; int icnt, idx, jdx, ld; int n=N, type=MT_C_INT, one; int *values, *ptr; int **indices; int dims[2]={N,N}; int lo[2], hi[2]; int heap=3000000, stack=2000000; int me, nproc; int datatype, elements; double *prealloc_mem; MP_INIT(argc,argv); #if 1 GA_INIT(argc,argv); /* initialize GA */ me=GA_Nodeid(); nproc=GA_Nnodes(); if(me==0) { if(GA_Uses_fapi())GA_Error("Program runs with C array API only",1); printf("\nUsing %ld processes\n",(long)nproc); fflush(stdout); } heap /= nproc; stack /= nproc; if(! MA_init(MT_F_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ /* Create a regular matrix. */ if(me==0)printf("\nCreating matrix A of size %d x %d\n",N,N); g_a = NGA_Create(type, 2, dims, "A", NULL); if(!g_a) GA_Error("create failed: A",n); /* Fill matrix using scatter routines */ size = N*N; if (size%nproc == 0) { size_me = size/nproc; } else { i = size - size%nproc; size_me = i/nproc; if (me < size%nproc) size_me++; } /* Check that sizes are all okay */ i = size_me; GA_Igop(&i,1,"+"); if (i != size) { GA_Error("Sizes don't add up correctly: ",i); } else if (me==0) { printf("\nSizes add up correctly\n"); } /* Allocate index and value arrays */ indices = (int**)malloc(size_me*sizeof(int*)); values = (int*)malloc(size_me*sizeof(int)); icnt = me; for (i=0; i= N || idx < 0) { printf("p[%d] Bogus index i: %d\n",me,idx); } if (jdx >= N || jdx < 0) { printf("p[%d] Bogus index j: %d\n",me,jdx); } indices[i] = (int*)malloc(2*sizeof(int)); (indices[i])[0] = idx; (indices[i])[1] = jdx; icnt += nproc; } /* Scatter values into g_a */ NGA_Scatter(g_a, values, indices, size_me); GA_Sync(); /* Check to see if contents of g_a are correct */ NGA_Distribution( g_a, me, lo, hi ); NGA_Access(g_a, lo, hi, &ptr, &ld); for (i=lo[0]; i #endif #if HAVE_STDLIB_H # include #endif #if HAVE_MATH_H # include #endif #include "macdecls.h" #include "ga.h" #include "mp3.h" /* utilities for GA test programs */ #include "testutil.h" #define N 8 /* first dimension */ #define NB 2 /* block dimension */ #define GA_DATA_TYPE MT_C_FLOAT #define GA_ABS(a) (((a) >= 0) ? (a) : (-(a))) #define TOLERANCE 0.0001 #define USE_REGULAR /* #define USE_SIMPLE_CYCLIC #define USE_SCALAPACK #define USE_TILED */ DoublePrecision gTime=0.0, gStart; #define MAX_FACTOR 512 /** * Factor p processors into 2D processor grid of dimensions px, py */ void grid_factor(int p, int *idx, int *idy) { int i, j; int ip, ifac, pmax, prime[MAX_FACTOR]; int fac[MAX_FACTOR]; int ix, iy, ichk; i = 1; /** * factor p completely * first, find all prime numbers, besides 1, less than or equal to * the square root of p */ ip = (int)(sqrt((double)p))+1; pmax = 0; for (i=2; i<=ip; i++) { ichk = 1; for (j=0; j MAX_FACTOR) printf("Overflow in grid_factor\n"); prime[pmax-1] = i; } } /** * find all prime factors of p */ ip = p; ifac = 0; for (i=0; i= 0; i--) { ix = *idx; iy = *idy; if (ix <= iy) { *idx = fac[i]*(*idx); } else { *idy = fac[i]*(*idy); } } } void test(int data_type) { int me=GA_Nodeid(); int nproc = GA_Nnodes(); int g_a, g_b, g_c; int ndim = 2; int dims[2]={N,N}; int lo[2]={0,0}; int hi[2]={N-1,N-1}; int block_size[2]={NB,NB-1}; int proc_grid[2]; int i,j,l,k,m,n, ld; double alpha_dbl = 1.0, beta_dbl = 0.0; double dzero = 0.0; double ddiff; float alpha_flt = 1.0, beta_flt = 0.0; float fzero = 0.0; float fdiff; float ftmp; double dtmp; SingleComplex ctmp; DoubleComplex ztmp; DoubleComplex alpha_dcpl = {1.0, 0.0} , beta_dcpl = {0.0, 0.0}; DoubleComplex zzero = {0.0,0.0}; DoubleComplex zdiff; SingleComplex alpha_scpl = {1.0, 0.0} , beta_scpl = {0.0, 0.0}; SingleComplex czero = {0.0,0.0}; SingleComplex cdiff; void *alpha=NULL, *beta=NULL; void *abuf=NULL, *bbuf=NULL, *cbuf=NULL, *c_ptr=NULL; switch (data_type) { case C_FLOAT: alpha = (void *)&alpha_flt; beta = (void *)&beta_flt; abuf = (void*)malloc(N*N*sizeof(float)); bbuf = (void*)malloc(N*N*sizeof(float)); cbuf = (void*)malloc(N*N*sizeof(float)); if(me==0) printf("Single Precision: Testing GA_Sgemm,NGA_Matmul_patch for %d-Dimension", ndim); break; case C_DBL: alpha = (void *)&alpha_dbl; beta = (void *)&beta_dbl; abuf = (void*)malloc(N*N*sizeof(double)); bbuf = (void*)malloc(N*N*sizeof(double)); cbuf = (void*)malloc(N*N*sizeof(double)); if(me==0) printf("Double Precision: Testing GA_Dgemm,NGA_Matmul_patch for %d-Dimension", ndim); break; case C_DCPL: alpha = (void *)&alpha_dcpl; beta = (void *)&beta_dcpl; abuf = (void*)malloc(N*N*sizeof(DoubleComplex)); bbuf = (void*)malloc(N*N*sizeof(DoubleComplex)); cbuf = (void*)malloc(N*N*sizeof(DoubleComplex)); if(me==0) printf("Double Complex: Testing GA_Zgemm,NGA_Matmul_patch for %d-Dimension", ndim); break; case C_SCPL: alpha = (void *)&alpha_scpl; beta = (void *)&beta_scpl; abuf = (void*)malloc(N*N*sizeof(SingleComplex)); bbuf = (void*)malloc(N*N*sizeof(SingleComplex)); cbuf = (void*)malloc(N*N*sizeof(SingleComplex)); if(me==0) printf("Single Complex: Testing GA_Cgemm,NGA_Matmul_patch for %d-Dimension", ndim); break; default: GA_Error("wrong data type", data_type); } if (me==0) printf("\nCreate A, B, C\n"); #ifdef USE_REGULAR g_a = NGA_Create(data_type, ndim, dims, "array A", NULL); #endif #ifdef USE_SIMPLE_CYCLIC g_a = NGA_Create_handle(); NGA_Set_data(g_a,ndim,dims,data_type); NGA_Set_array_name(g_a,"array A"); NGA_Set_block_cyclic(g_a,block_size); if (!GA_Allocate(g_a)) { GA_Error("Failed: create: g_a",40); } #endif #ifdef USE_SCALAPACK g_a = NGA_Create_handle(); NGA_Set_data(g_a,ndim,dims,data_type); NGA_Set_array_name(g_a,"array A"); grid_factor(nproc,&i,&j); proc_grid[0] = i; proc_grid[1] = j; NGA_Set_block_cyclic_proc_grid(g_a,block_size,proc_grid); if (!GA_Allocate(g_a)) { GA_Error("Failed: create: g_a",40); } #endif #ifdef USE_TILED g_a = NGA_Create_handle(); NGA_Set_data(g_a,ndim,dims,data_type); NGA_Set_array_name(g_a,"array A"); grid_factor(nproc,&i,&j); proc_grid[0] = i; proc_grid[1] = j; NGA_Set_tiled_proc_grid(g_a,block_size,proc_grid); if (!GA_Allocate(g_a)) { GA_Error("Failed: create: g_a",40); } #endif g_b = GA_Duplicate(g_a, "array B"); g_c = GA_Duplicate(g_a, "array C"); if(!g_a || !g_b || !g_c) GA_Error("Create failed: a, b or c",1); ld = N; if (me==0) printf("\nInitialize A\n"); /* Set up matrix A */ if (me == 0) { for (i=0; i TOLERANCE) { printf("p[%d] [%d,%d] Actual: %f Expected: %f\n",me,i,j, ((float*)abuf)[i*N+j],((float*)cbuf)[i*N+j]); } break; case C_DBL: ddiff = ((double*)abuf)[i*N+j]-((double*)cbuf)[i*N+j]; if (((double*)abuf)[i*N+j] != 0.0) { ddiff /= ((double*)abuf)[i*N+j]; } if (fabs(ddiff) > TOLERANCE) { printf("p[%d] [%d,%d] Actual: %f Expected: %f\n",me,i,j, ((double*)abuf)[i*N+j],((double*)cbuf)[i*N+j]); } break; case C_DCPL: zdiff.real = ((DoubleComplex*)abuf)[i*N+j].real -((DoubleComplex*)cbuf)[i*N+j].real; zdiff.imag = ((DoubleComplex*)abuf)[i*N+j].imag -((DoubleComplex*)cbuf)[i*N+j].imag; if (((DoubleComplex*)abuf)[i*N+j].real != 0.0 || ((DoubleComplex*)abuf)[i*N+j].imag != 0.0) { ztmp = ((DoubleComplex*)abuf)[i*N+j]; ddiff = sqrt((zdiff.real*zdiff.real+zdiff.imag*zdiff.imag) /(ztmp.real*ztmp.real+ztmp.imag*ztmp.imag)); } else { ddiff = sqrt(zdiff.real*zdiff.real+zdiff.imag*zdiff.imag); } if (fabs(ddiff) > TOLERANCE) { printf("p[%d] [%d,%d] Actual: (%f,%f) Expected: (%f,%f)\n",me,i,j, ((DoubleComplex*)abuf)[i*N+j].real, ((DoubleComplex*)abuf)[i*N+j].imag, ((DoubleComplex*)cbuf)[i*N+j].real, ((DoubleComplex*)cbuf)[i*N+j].imag); } break; case C_SCPL: cdiff.real = ((SingleComplex*)abuf)[i*N+j].real -((SingleComplex*)cbuf)[i*N+j].real; cdiff.imag = ((SingleComplex*)abuf)[i*N+j].imag -((SingleComplex*)cbuf)[i*N+j].imag; if (((SingleComplex*)abuf)[i*N+j].real != 0.0 || ((SingleComplex*)abuf)[i*N+j].imag != 0.0) { ctmp = ((SingleComplex*)abuf)[i*N+j]; fdiff = sqrt((cdiff.real*cdiff.real+cdiff.imag*cdiff.imag) /(ctmp.real*ctmp.real+ctmp.imag*ctmp.imag)); } else { fdiff = sqrt(cdiff.real*cdiff.real+cdiff.imag*cdiff.imag); } if (fabs(fdiff) > TOLERANCE) { printf("p[%d] [%d,%d] Actual: (%f,%f) Expected: (%f,%f)\n",me,i,j, ((SingleComplex*)abuf)[i*N+j].real, ((SingleComplex*)abuf)[i*N+j].imag, ((SingleComplex*)cbuf)[i*N+j].real, ((SingleComplex*)cbuf)[i*N+j].imag); } break; default: GA_Error("wrong data type", data_type); } } } } GA_Sync(); /* copy cbuf back to g_a */ if (me == 0) { NGA_Put(g_a,lo,hi,cbuf,&ld); } GA_Sync(); /* Get norm of g_a */ switch (data_type) { case C_FLOAT: ftmp = GA_Fdot(g_a,g_a); break; case C_DBL: dtmp = GA_Ddot(g_a,g_a); break; case C_DCPL: ztmp = GA_Zdot(g_a,g_a); break; case C_SCPL: ctmp = GA_Cdot(g_a,g_a); break; default: GA_Error("wrong data type", data_type); } /* subtract C from A and put the results in B */ beta_flt = -1.0; beta_dbl = -1.0; beta_scpl.real = -1.0; beta_dcpl.real = -1.0; GA_Zero(g_b); GA_Add(alpha,g_a,beta,g_c,g_b); /* evaluate the norm of the difference between the two matrices */ switch (data_type) { case C_FLOAT: fdiff = GA_Fdot(g_b, g_b); if (ftmp != 0.0) { fdiff /= ftmp; } if(fabs(fdiff) > TOLERANCE) { printf("\nabs(result) = %f > %f\n", fabsf(fdiff), TOLERANCE); GA_Error("GA_Sgemm Failed", 1); } else if (me == 0) { printf("\nGA_Sgemm OK\n\n"); } break; case C_DBL: ddiff = GA_Ddot(g_b, g_b); if (dtmp != 0.0) { ddiff /= dtmp; } if(fabs(ddiff) > TOLERANCE) { printf("\nabs(result) = %f > %f\n", fabsf(ddiff), TOLERANCE); GA_Error("GA_Dgemm Failed", 1); } else if (me == 0) { printf("\nGA_Dgemm OK\n\n"); } break; case C_DCPL: zdiff = GA_Zdot(g_b, g_b); if (ztmp.real != 0.0 || ztmp.imag != 0.0) { ddiff = sqrt((zdiff.real*zdiff.real+zdiff.imag*zdiff.imag) /(ztmp.real*ztmp.real+ztmp.imag*ztmp.imag)); } else { ddiff = sqrt(zdiff.real*zdiff.real+zdiff.imag*zdiff.imag); } if(fabs(ddiff) > TOLERANCE) { printf("\nabs(result) = %f > %f\n", fabsf(zdiff.real), TOLERANCE); GA_Error("GA_Zgemm Failed", 1); } else if (me == 0) { printf("\nGA_Zgemm OK\n\n"); } break; case C_SCPL: cdiff = GA_Cdot(g_b, g_b); if (ctmp.real != 0.0 || ctmp.imag != 0.0) { fdiff = sqrt((cdiff.real*cdiff.real+cdiff.imag*cdiff.imag) /(ctmp.real*ctmp.real+ctmp.imag*ctmp.imag)); } else { fdiff = sqrt(cdiff.real*cdiff.real+cdiff.imag*cdiff.imag); } if(fabs(fdiff) > TOLERANCE) { printf("\nabs(result) = %f > %f\n", fabsf(cdiff.real), TOLERANCE); GA_Error("GA_Cgemm Failed", 1); } else if (me == 0) { printf("\nGA_Cgemm OK\n\n"); } break; default: GA_Error("wrong data type", data_type); } #endif free(abuf); free(bbuf); free(cbuf); switch (data_type) { case C_FLOAT: abuf = (void*)malloc(N*N*sizeof(float)/4); bbuf = (void*)malloc(N*N*sizeof(float)/4); cbuf = (void*)malloc(N*N*sizeof(float)/4); break; case C_DBL: abuf = (void*)malloc(N*N*sizeof(double)/4); bbuf = (void*)malloc(N*N*sizeof(double)/4); cbuf = (void*)malloc(N*N*sizeof(double)/4); break; case C_DCPL: abuf = (void*)malloc(N*N*sizeof(DoubleComplex)/4); bbuf = (void*)malloc(N*N*sizeof(DoubleComplex)/4); cbuf = (void*)malloc(N*N*sizeof(DoubleComplex)/4); break; case C_SCPL: abuf = (void*)malloc(N*N*sizeof(SingleComplex)/4); bbuf = (void*)malloc(N*N*sizeof(SingleComplex)/4); cbuf = (void*)malloc(N*N*sizeof(SingleComplex)/4); break; default: GA_Error("wrong data type", data_type); } /* Test multiply on a fraction of matrix. Start by reinitializing * A and B */ GA_Zero(g_a); GA_Zero(g_b); GA_Zero(g_c); if (me==0) printf("\nTest patch multiply\n"); lo[0] = N/4; lo[1] = N/4; hi[0] = 3*N/4-1; hi[1] = 3*N/4-1; ld = N/2; /* Set up matrix A */ if (me==0) printf("\nInitialize A\n"); if (me == 0) { for (i=N/4; i<3*N/4; i++) { for (j=N/4; j<3*N/4; j++) { switch (data_type) { case C_FLOAT: ((float*)abuf)[(i-N/4)*N/2+(j-N/4)] = (float)(i*N+j); break; case C_DBL: ((double*)abuf)[(i-N/4)*N/2+(j-N/4)] = (double)(i*N+j); break; case C_DCPL: ((DoubleComplex*)abuf)[(i-N/4)*N/2+(j-N/4)].real = (double)(i*N+j); ((DoubleComplex*)abuf)[(i-N/4)*N/2+(j-N/4)].imag = 1.0; break; case C_SCPL: ((SingleComplex*)abuf)[(i-N/4)*N/2+(j-N/4)].real = (float)(i*N+j); ((SingleComplex*)abuf)[(i-N/4)*N/2+(j-N/4)].imag = 1.0; break; default: GA_Error("wrong data type", data_type); } } } NGA_Put(g_a,lo,hi,abuf,&ld); } GA_Sync(); if (me==0) printf("\nInitialize B\n"); /* Set up matrix B */ if (me == 0) { for (i=N/4; i<3*N/4; i++) { for (j=N/4; j<3*N/4; j++) { switch (data_type) { case C_FLOAT: ((float*)bbuf)[(i-N/4)*N/2+(j-N/4)] = (float)(j*N+i); break; case C_DBL: ((double*)bbuf)[(i-N/4)*N/2+(j-N/4)] = (double)(j*N+i); break; case C_DCPL: ((DoubleComplex*)bbuf)[(i-N/4)*N/2+(j-N/4)].real = (double)(j*N+i); ((DoubleComplex*)bbuf)[(i-N/4)*N/2+(j-N/4)].imag = 1.0; break; case C_SCPL: ((SingleComplex*)bbuf)[(i-N/4)*N/2+(j-N/4)].real = (float)(j*N+i); ((SingleComplex*)bbuf)[(i-N/4)*N/2+(j-N/4)].imag = 1.0; break; default: GA_Error("wrong data type", data_type); } } } NGA_Put(g_b,lo,hi,bbuf,&ld); } GA_Sync(); beta_flt = 0.0; beta_dbl = 0.0; beta_scpl.real = 0.0; beta_dcpl.real = 0.0; if (me==0) printf("\nPerform matrix multiply on sub-blocks\n"); switch (data_type) { case C_FLOAT: NGA_Matmul_patch('N','N',&alpha_flt,&beta_flt,g_a,lo,hi, g_b,lo,hi,g_c,lo,hi); break; case C_DBL: NGA_Matmul_patch('N','N',&alpha_dbl,&beta_dbl,g_a,lo,hi, g_b,lo,hi,g_c,lo,hi); break; case C_SCPL: NGA_Matmul_patch('N','N',&alpha_scpl,&beta_scpl,g_a,lo,hi, g_b,lo,hi,g_c,lo,hi); break; case C_DCPL: NGA_Matmul_patch('N','N',&alpha_dcpl,&beta_dcpl,g_a,lo,hi, g_b,lo,hi,g_c,lo,hi); break; default: GA_Error("wrong data type", data_type); } GA_Sync(); #if 0 if (0) { /* if (data_type != C_SCPL && data_type != C_DCPL) { */ if (me==0) printf("\nCheck answer\n"); /* Multiply buffers by hand */ if (me == 0) { for (i=0; i TOLERANCE) { printf("\nabs(result) = %f > %f\n", fabsf(fdiff), TOLERANCE); GA_Error("GA_Sgemm Failed", 1); } else if (me == 0) { printf("\nGA_Sgemm OK\n\n"); } break; case C_DBL: ddiff = NGA_Ddot_patch(g_b,'N',lo,hi,g_b,'N',lo,hi); if (dtmp != 0.0) { ddiff /= dtmp; } if(fabs(ddiff) > TOLERANCE) { printf("\nabs(result) = %f > %f\n", fabsf(ddiff), TOLERANCE); GA_Error("GA_Dgemm Failed", 1); } else if (me == 0) { printf("\nGA_Dgemm OK\n\n"); } break; case C_DCPL: zdiff = NGA_Zdot_patch(g_b,'N',lo,hi,g_b,'N',lo,hi); if (ztmp.real != 0.0 || ztmp.imag != 0.0) { ddiff = sqrt((zdiff.real*zdiff.real+zdiff.imag*zdiff.imag) /(ztmp.real*ztmp.real+ztmp.imag*ztmp.imag)); } else { ddiff = sqrt(zdiff.real*zdiff.real+zdiff.imag*zdiff.imag); } if(fabs(ddiff) > TOLERANCE) { printf("\nabs(result) = %f > %f\n", fabsf(zdiff.real), TOLERANCE); GA_Error("GA_Zgemm Failed", 1); } else if (me == 0) { printf("\nGA_Zgemm OK\n\n"); } break; case C_SCPL: cdiff = NGA_Cdot_patch(g_b,'N',lo,hi,g_b,'N',lo,hi); if (ctmp.real != 0.0 || ctmp.imag != 0.0) { fdiff = sqrt((cdiff.real*cdiff.real+cdiff.imag*cdiff.imag) /(ctmp.real*ctmp.real+ctmp.imag*ctmp.imag)); } else { fdiff = sqrt(cdiff.real*cdiff.real+cdiff.imag*cdiff.imag); } if(fabs(fdiff) > TOLERANCE) { printf("\nabs(result) = %f > %f\n", fabsf(cdiff.real), TOLERANCE); GA_Error("GA_Cgemm Failed", 1); } else if (me == 0) { printf("\nGA_Cgemm OK\n\n"); } break; default: GA_Error("wrong data type", data_type); } } #endif free(abuf); free(bbuf); free(cbuf); GA_Destroy(g_a); GA_Destroy(g_b); GA_Destroy(g_c); } void do_work() { int i; int me = GA_Nodeid(); test(C_FLOAT); test(C_DBL); test(C_DCPL); test(C_SCPL); /* */ if(me == 0) printf("\n\n"); } int main(int argc, char **argv) { Integer heap=9000000, stack=9000000; int me, nproc; DoublePrecision time; MP_INIT(argc,argv); GA_INIT(argc,argv); /* initialize GA */ nproc = GA_Nnodes(); me = GA_Nodeid(); if(me==0) printf("Using %d processes\n\n",nproc); if (me==0) printf ("Matrix size is %d X %d\n",N,N); #ifdef USE_REGULAR if (me == 0) printf("\nUsing regular data distribution\n\n"); #endif #ifdef USE_SIMPLE_CYCLIC if (me == 0) printf("\nUsing simple block-cyclic data distribution\n\n"); #endif #ifdef USE_SCALAPACK if (me == 0) printf("\nUsing ScaLAPACK data distribution\n\n"); #endif #ifdef USE_TILED if (me == 0) printf("\nUsing tiled data distribution\n\n"); #endif if(!MA_init((Integer)MT_F_DBL, stack/nproc, heap/nproc)) GA_Error("MA_init failed bytes= %d",stack+heap); if(GA_Uses_fapi())GA_Error("Program runs with C API only",1); time = MP_TIMER(); do_work(); /* printf("%d: Total Time = %lf\n", me, MP_TIMER()-time); printf("%d: GEMM Total Time = %lf\n", me, gTime); */ if(me==0)printf("\nSuccess\n\n"); GA_Terminate(); MP_FINALIZE(); return 0; } ga-5.9.2/global/testing/getmem.c000066400000000000000000000041111500715745200164600ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #include "ga.h" #include "macdecls.h" #include "mp3.h" #define N 4 /* dimension of matrices */ int main( int argc, char **argv ) { int g_a, g_b,i; int n=N, type=MT_F_DBL; int dims[6]={N,N,N,N,N,N}; int lo[6], hi[6]; int heap=30000, stack=20000; int me, nproc; int datatype, elements; double *prealloc_mem; MP_INIT(argc,argv); GA_INIT(argc,argv); /* initialize GA */ me=GA_Nodeid(); nproc=GA_Nnodes(); if(me==0) { if(GA_Uses_fapi())GA_Error("Program runs with C array API only",1); printf("Using %ld processes\n",(long)nproc); fflush(stdout); } heap /= nproc; stack /= nproc; if(! MA_init(MT_F_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ /* This is a regular matrix. */ if(me==0)printf("Creating matrix A\n"); g_a = NGA_Create(type, 2, dims, "A", NULL); if(!g_a) GA_Error("create failed: A",n); if(me==0)printf("OK\n"); NGA_Distribution( g_a, me, lo, hi ); #if 1 /* This is just allocating and freeing memory. */ datatype = type; elements = 1; for ( i=0; i<2; i++ ) { elements *= (hi[i] - lo[i] + 1); } prealloc_mem = GA_Getmem(datatype, elements, 0); for ( i=0; i #include #include #include #include "macdecls.h" #include "ga.h" #include "mp3.h" #define NDIM 8 #define GA_PRINT /** * Solve Laplace's equation on a cubic domain using the sparse matrix * functionality in GA. */ /* Create 2D process grid for p processors */ #define MAX_FACTOR 1024 void grid_factor(int p, int xdim, int ydim, int zdim, int *idx, int *idy, int *idz) { int i, j, k; int ip, ifac, pmax, prime[MAX_FACTOR]; int fac[MAX_FACTOR]; int ix, iy, iz, ichk; i = 1; /** * factor p completely * first, find all prime numbers, besides 1, less than or equal to p */ ip = p; pmax = 0; for (i=2; i<=ip; i++) { ichk = 1; for (j=0; j MAX_FACTOR) printf("Overflow in grid_factor\n"); prime[pmax-1] = i; } } /** * find all prime factors of p */ ip = p; ifac = 0; for (i=0; i= 0; i--) { ix = xdim/(*idx); iy = ydim/(*idy); iz = zdim/(*idz); if (ix >= iy && ix >= iz && ix > 1) { *idx = fac[i]*(*idx); } else if (iy >= ix && iy >= iz && iy > 1) { *idy = fac[i]*(*idy); } else if (iz >= ix && iz >= iy && iz > 1) { *idz = fac[i]*(*idz); } else { printf("Too many processors in grid factoring routine\n"); } } } /* Copy a column of data from a matrix (2D array) to a vector (1D array) * @param g_m matrix handle * @parma g_v vector handle * @param lo, hi bounding indices of data from matrix */ void copy_to_vector(int g_m, int g_v, int *lo, int *hi) { int vlo[2], vhi[2], ld; double *buf; int me = NGA_Nodeid(); NGA_Distribution(g_v,me,vlo,vhi); if (vlo[0] < lo[0]) vlo[0] = lo[0]; if (vhi[0] > hi[0]) vhi[0] = hi[0]; if (vlo[0] > vhi[0]) return; NGA_Access(g_v,vlo,vhi,&buf,&ld); vlo[1] = lo[1]; vhi[1] = hi[1]; ld = 1; NGA_Get(g_m,vlo,vhi,buf,&ld); NGA_Release(g_v,vlo,vhi); } /* Copy a vector (1D array) to a column in a matrix (2D array) * @param g_m matrix handle * @parma g_v vector handle * @param lo, hi bounding indices of data from matrix */ void copy_to_matrix(int g_m, int g_v, int *lo, int *hi) { int vlo[2], vhi[2], ld; double *buf; int me = NGA_Nodeid(); NGA_Distribution(g_v,me,vlo,vhi); if (vlo[0] < lo[0]) vlo[0] = lo[0]; if (vhi[0] > hi[0]) vhi[0] = hi[0]; if (vlo[0] > vhi[0]) return; NGA_Access(g_v,vlo,vhi,&buf,&ld); vlo[1] = lo[1]; vhi[1] = hi[1]; ld = 1; NGA_Put(g_m,vlo,vhi,buf,&ld); NGA_Release(g_v,vlo,vhi); } int main(int argc, char **argv) { int g_ran, s_sk, g_a, g_s, g_ss, g_st, g_pm, g_q, g_r, g_c1, g_c2; int g_tmp, g_am, g_p, g_qm, g_km; int dims[2], chunk[2], one, two; int lo[2], hi[2], ld[2]; int i, j, idx, idim, jdim, kdim, ihi, ilo, klo, khi; int it, jt; int iter; int me, nproc; int heap=10000000, stack=10000000; double x, snorm, rone,rmone; double *dptr; double dmin, dmax, omin, omax; void *ptr; char cmax[4],cmin[4]; /*Initialize MPI*/ MP_INIT(argc,argv); /* Initialize GA */ NGA_Initialize(); me = GA_Nodeid(); nproc = GA_Nnodes(); heap /= nproc; stack /= nproc; if(! MA_init(MT_F_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ /* initialize random number generator */ NGA_Rand(332182+me); //NGA_Rand(432182+me); idim = NDIM; jdim = NDIM; if (me == 0) { printf("Apply Gram-Schmidt orthoganlization to %d x %d matrix\n",idim,jdim); } /* Create random 2D matrix of size NDIM x NDIM */ two = 2; dims[0] = idim; dims[1] = jdim; chunk[0] = idim; chunk[1] = -1; g_ran = NGA_Create_handle(); NGA_Set_data(g_ran,two,dims,C_DBL); NGA_Set_chunk(g_ran,chunk); if (!NGA_Allocate(g_ran)) GA_Error("Could not allocate random GA",0); /* Fill GA with random values */ NGA_Distribution(g_ran,me,lo,hi); NGA_Access(g_ran,lo,hi,&ptr,ld); dptr = (double*)ptr; ld[0] = hi[0]-lo[0]+1; ld[1] = hi[1]-lo[1]+1; for (i=0; i 0.5) x = -1.0; /* printf("p[%d] add element %d %d %f %f\n",me,k,i,x,rx); */ NGA_Sprs_array_add_element(s_sk, k, i, &x); } #else { int *kbuf = (int*)malloc(idim*sizeof(int)); int kcnt; for (i=0; i -1) { k = kbuf[i]; } else { k = (int)(((double)kdim)*NGA_Rand(0)); } x = 1.0; double rx = NGA_Rand(0); if (rx > 0.5) x = -1.0; printf("p[%d] add element %d %d %f %f\n",me,k,i,x,rx); NGA_Sprs_array_add_element(s_sk, k, i, &x); } free(kbuf); } #endif NGA_Sprs_array_assemble(s_sk); #ifdef GA_PRINT NGA_Sprs_array_export(s_sk,"sketch.m"); #endif /* Create Q and R matrices that form the set of orthogonal vectors (Q) and * upper triangular matrix (R) from the Gram-Schmidt orthogonalization */ dims[0] = idim; dims[1] = jdim; chunk[0] = idim; chunk[1] = -1; g_q = NGA_Create_handle(); NGA_Set_data(g_q,two,dims,C_DBL); NGA_Set_chunk(g_q,chunk); if (!NGA_Allocate(g_q)) GA_Error("Could not allocate Q matrix",0); NGA_Zero(g_q); dims[0] = jdim; dims[1] = jdim; chunk[0] = jdim; chunk[1] = -1; g_r = NGA_Create_handle(); NGA_Set_data(g_r,two,dims,C_DBL); NGA_Set_chunk(g_r,chunk); if (!NGA_Allocate(g_r)) GA_Error("Could not allocate R matrix",0); NGA_Zero(g_r); dims[0] = kdim; dims[1] = jdim; chunk[0] = kdim; chunk[1] = -1; g_ss = NGA_Create_handle(); NGA_Set_data(g_ss,two,dims,C_DBL); NGA_Set_chunk(g_ss,chunk); if (!NGA_Allocate(g_ss)) GA_Error("Could not allocate S matrix",0); NGA_Zero(g_ss); dims[0] = kdim; dims[1] = 1; g_pm = NGA_Create_handle(); NGA_Set_data(g_pm,two,dims,C_DBL); if (!NGA_Allocate(g_pm)) GA_Error("Could not allocate P matrix",0); NGA_Zero(g_pm); dims[0] = idim; dims[1] = 1; g_qm = NGA_Create_handle(); NGA_Set_data(g_qm,two,dims,C_DBL); if (!NGA_Allocate(g_qm)) GA_Error("Could not allocate Q matrix",0); NGA_Zero(g_qm); /* Create distributed vectors for Gram-Schmidt orthogonalization */ one = 1; g_a = NGA_Create_handle(); NGA_Set_data(g_a, one, &idim, C_DBL); if (!NGA_Allocate(g_a)) GA_Error("Could not allocate A vector",0); NGA_Zero(g_a); g_s = NGA_Create_handle(); NGA_Set_data(g_s, one, &kdim, C_DBL); if (!NGA_Allocate(g_s)) GA_Error("Could not allocate s vector",0); NGA_Zero(g_s); g_st = NGA_Create_handle(); NGA_Set_data(g_st, one, &kdim, C_DBL); if (!NGA_Allocate(g_st)) GA_Error("Could not allocate s~ vector",0); NGA_Zero(g_st); g_p = NGA_Create_handle(); NGA_Set_data(g_p, one, &kdim, C_DBL); if (!NGA_Allocate(g_p)) GA_Error("Could not allocate p vector",0); NGA_Zero(g_p); dims[0] = jdim; dims[1] = 1; g_c1 = NGA_Create_handle(); NGA_Set_data(g_c1, two, dims, C_DBL); if (!NGA_Allocate(g_c1)) GA_Error("Could not allocate c1 vector",0); NGA_Zero(g_c1); g_c2 = NGA_Create_handle(); NGA_Set_data(g_c2, two, dims, C_DBL); if (!NGA_Allocate(g_c2)) GA_Error("Could not allocate c2 vector",0); NGA_Zero(g_c2); g_tmp = NGA_Create_handle(); NGA_Set_data(g_tmp, two, dims, C_DBL); if (!NGA_Allocate(g_tmp)) GA_Error("Could not allocate tmp vector",0); NGA_Zero(g_tmp); dims[0] = idim; dims[1] = 1; g_am = NGA_Create_handle(); NGA_Set_data(g_am, two, dims, C_DBL); if (!NGA_Allocate(g_am)) GA_Error("Could not allocate am vector",0); NGA_Zero(g_am); dims[0] = kdim; dims[1] = 1; g_km = NGA_Create_handle(); NGA_Set_data(g_km, two, dims, C_DBL); if (!NGA_Allocate(g_km)) GA_Error("Could not allocate km vector",0); NGA_Zero(g_am); /* Initialize system and calculate first vector */ lo[0] = 0; hi[0] = idim-1; lo[1] = 0; hi[1] = 0; copy_to_vector(g_ran,g_a,lo,hi); GA_Sync(); #ifdef GA_PRINT if (me == 0) printf("Initial vector a_1\n"); GA_Print(g_a); #endif NGA_Sprs_array_matvec_multiply(s_sk,g_a,g_st); GA_Copy(g_st,g_s); snorm = GA_Ddot(g_s,g_s); snorm = sqrt(snorm); lo[0] = 0; hi[0] = 0; lo[1] = 0; hi[1] = 0; ld[0] = 1; if (me == 0) { NGA_Put(g_r,lo,hi,&snorm,ld); } snorm = 1.0/snorm; GA_Scale(g_s,&snorm); lo[0] = 0; hi[0] = kdim-1; lo[1] = 0; hi[1] = 0; copy_to_matrix(g_ss,g_s,lo,hi); #ifdef GA_PRINT if (me == 0) printf("Value of S_1\n"); GA_Print(g_ss); #endif GA_Scale(g_a,&snorm); #ifdef GA_PRINT if (me == 0) printf("Value of q_1\n"); GA_Print(g_a); #endif lo[0] = 0; hi[0] = idim-1; copy_to_matrix(g_q,g_a,lo,hi); #ifdef GA_PRINT if (me == 0) printf("Initial Q\n"); GA_Print(g_q); #endif /* Iterate over remaining columns of g_ran */ rone = 1.0; rmone = -1.0; for (iter = 1; iter= lo[0] && iter <= hi[0] && iter >= lo[1] && iter <= hi[1]) { lo[0] = iter; hi[0] = iter; lo[1] = iter; hi[1] = iter; NGA_Put(g_r,lo,hi,&snorm,&one); } #ifdef GA_PRINT if (me == 0) printf("Value of R_%d\n",iter+1); GA_Print(g_r); #endif snorm = 1.0/snorm; if (me == 0) printf("sqrt(S.S)[%d]: %e\n",iter+1,snorm); GA_Scale(g_s,&snorm); #ifdef GA_PRINT if (me == 0) printf("Value of (2) S_%d\n",iter+1); GA_Print(g_s); #endif lo[0] = 0; hi[0] = kdim-1; lo[1] = iter; hi[1] = iter; copy_to_matrix(g_ss,g_s,lo,hi); GA_Scale(g_a,&snorm); lo[0] = 0; hi[0] = idim-1; lo[1] = iter; hi[1] = iter; #ifdef GA_PRINT // GA_Print(g_a); #endif copy_to_matrix(g_q,g_a,lo,hi); #ifdef GA_PRINT // GA_Print(g_q); #endif } if (me == 0) { printf("Completed Gram-Schmidt orthogonalization\n"); } /* Clean up all global arrays, except those used for verification */ NGA_Sprs_array_destroy(s_sk); GA_Destroy(g_ran); GA_Destroy(g_s); GA_Destroy(g_ss); GA_Destroy(g_st); GA_Destroy(g_pm); GA_Destroy(g_c1); GA_Destroy(g_c2); GA_Destroy(g_tmp); GA_Destroy(g_am); GA_Destroy(g_p); GA_Destroy(g_qm); g_s = NGA_Create_handle(); NGA_Set_data(g_s, one, &idim, C_DBL); if (!NGA_Allocate(g_s)) GA_Error("Could not allocate second S vector",0); NGA_Zero(g_s); g_qm = NGA_Create_handle(); dims[0] = idim; dims[1] = idim; NGA_Set_data(g_qm, two, dims, C_DBL); if (!NGA_Allocate(g_qm)) GA_Error("Could not allocate matrix of dot products",0); NGA_Zero(g_qm); #if 0 for (i=0; i dmax) dmax = dptr[idx]; if (dptr[idx] < dmin) dmin = dptr[idx]; } else { if (dptr[idx] > omax) omax = dptr[idx]; if (dptr[idx] < omin) omin = dptr[idx]; } } } NGA_Release(g_qm,lo,hi); strcpy(cmax,"max"); strcpy(cmin,"min"); GA_Dgop(&omin,1,cmin); GA_Dgop(&omax,1,cmax); GA_Dgop(&dmin,1,cmin); GA_Dgop(&dmax,1,cmax); if (me == 0) { printf("Minimum and maximum values of off-diagonal elements %16.8e %16.8e\n", omin,omax); printf("Minimum and maximum values of diagonal elements %16.8e %16.8e\n", dmin,dmax); } /* Clean up remaining global arrays */ GA_Destroy(g_a); GA_Destroy(g_q); GA_Destroy(g_r); GA_Destroy(g_s); GA_Destroy(g_km); NGA_Terminate(); /** * Tidy up after message-passing library */ MP_FINALIZE(); } ga-5.9.2/global/testing/ipc.clean.c000066400000000000000000000076411500715745200170510ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_SYS_IPC_H # include #endif #if HAVE_SYS_SEM_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_ERRNO_H # include #endif #if HAVE_STDLIB_H # include #endif #if !HAVE_UNION_SEMUN union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short int *array; /* array for GETALL, SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ }; #endif #define MAX_SEM 10 struct sembuf sops; int semaphoreID; int sem_init=0; #define P_ -1 #define V_ 1 #define P(s) \ {\ sops.sem_num = (s);\ sops.sem_op = P_;\ sops.sem_flg = 0; \ semop(semaphoreID,&sops,1);\ } #define V(s) \ {\ sops.sem_num = (s);\ sops.sem_op = V_;\ sops.sem_flg = 0; \ semop(semaphoreID,&sops,1);\ } int SemGet(num_sem) int num_sem; { if(num_sem<1)return(0); if(num_sem>MAX_SEM)return(0); semaphoreID = semget(IPC_PRIVATE,num_sem,0600); if(semaphoreID<0){ fprintf(stderr,"SemGet failed \n"); perror((char*)0); } sem_init = num_sem; return(semaphoreID); } void SemInit(id,value) int id,value; { union semun semctl_arg; fprintf(stderr,"SemInit %d %d\n",id,value); semctl_arg.val = value; if(id >= sem_init || id<0 ) fprintf(stderr,"attempt to intialize invalid semaphore %d %d\n", id,sem_init); else if( semctl(semaphoreID, id,SETVAL,semctl_arg )<0){ fprintf(stderr,"SemInit error\n"); perror((char*)0); } fprintf(stderr,"exiting SemInit \n"); } /* release semaphore(s) */ void SemDel() { semctl(semaphoreID,NULL,IPC_RMID,NULL); } /*\ * (char *) CreateSharedRegion((long *) id, (long *) size) * long DetachSharedRegion((long) id, (long) size, (char *) addr) * long DeleteSharedRegion((long) id) * long DeleteSharedAll() * (char *) AttachSharedRegion((long) id, (long) size)) \*/ void Error( str, code) char *str; int code; { fprintf(stderr,"%s %d\n",str, code); exit(0); } #if defined(SYSV) #include #include #include #include #include char *CreateSharedRegion(id, size) long *size, *id; { char *temp; /* Create the region */ if ( (*id = shmget(IPC_PRIVATE, (int) *size, (int) (IPC_CREAT | 00600))) < 0 ){ fprintf(stderr,"id=%d size=%d\n",*id, (int) *size); perror((char*)0); Error("CreateSharedRegion: failed to create shared region", (long) *id); } /* Attach to the region */ if ( (temp = shmat((int) *id, (char *) NULL, 0)) == (char *) NULL){ perror((char*)0); Error("CreateSharedRegion: failed to attach to shared region", (long) 0); } return temp; } long DetachSharedRegion( id, size, addr) long id, size; char *addr; { return shmdt(addr); } long DeleteSharedRegion(id) long id; { return shmctl((int) id, IPC_RMID, (struct shmid_ds *) NULL); } char *AttachSharedRegion(id, size) long id, size; { char *temp; if ( (temp = shmat((int) id, (char *) NULL, 0)) == (char *) NULL) Error("AttachSharedRegion: failed to attach to shared region", (long) 0); return temp; } #endif int main(int argc, char **argv) { int from=0, to, i; if(argc<2){ printf("Usage:\n ipc.clean [] \n single argument is interpreted as with = 0 assumed\n"); return 1; } if(argc=2) sscanf(argv[1],"%d",&to); else { sscanf(argv[1],"%d",&from); sscanf(argv[2],"%d",&to); } if(from>to && to <0){ printf("wrong arguments\n"); return 1; } for(i=from;i<=to;i++){ semaphoreID =i; SemDel(); DeleteSharedRegion((long)i); } return 0; } ga-5.9.2/global/testing/ipcreset000066400000000000000000000010441500715745200166010ustar00rootroot00000000000000#!/bin/csh # clean System V IPC resources (semaphores and shared memory) owned by the user # if( `uname` == "Linux") then # Linux has non-standard interface to ipcrm # we use int() to filter out text decorations foreach s ( `ipcs -s | awk '{ if( int($2) != "0") { print $2 } }'` ) ipcrm sem $s >/dev/null end foreach m ( `ipcs -m | awk '{ if( int($2) != "0") { print $2 } }'` ) ipcrm shm $m >/dev/null end else ipcrm `ipcs | awk '{if (($1 == "m") || ($1 == "s")) print sprintf("-%s %s",$1,$2) }'` endif # show what is left ipcs ga-5.9.2/global/testing/j_iter.c000066400000000000000000000774151500715745200164770ustar00rootroot00000000000000#include #include #include #include "macdecls.h" #include "ga.h" #include "mp3.h" #define OLD_XDIST #define SYNCHRONOUS 0 #define WRITE_VTK #define NDIM 64 #define MAX_ITERATIONS 500 #define NUM_BINS 40 /** * Solve Laplace's equation on a cubic domain using the sparse matrix * functionality in GA. */ #define MAX_FACTOR 1024 void grid_factor(int p, int xdim, int ydim, int zdim, int *idx, int *idy, int *idz) { int i, j, k; int ip, ifac, pmax, prime[MAX_FACTOR]; int fac[MAX_FACTOR]; int ix, iy, iz, ichk; i = 1; /** * factor p completely * first, find all prime numbers, besides 1, less than or equal to p */ ip = p; pmax = 0; for (i=2; i<=ip; i++) { ichk = 1; for (j=0; j MAX_FACTOR) printf("Overflow in grid_factor\n"); prime[pmax-1] = i; } } /** * find all prime factors of p */ ip = p; ifac = 0; for (i=0; i= 0; i--) { ix = xdim/(*idx); iy = ydim/(*idy); iz = zdim/(*idz); if (ix >= iy && ix >= iz && ix > 1) { *idx = fac[i]*(*idx); } else if (iy >= ix && iy >= iz && iy > 1) { *idy = fac[i]*(*idy); } else if (iz >= ix && iz >= iy && iz > 1) { *idz = fac[i]*(*idz); } else { printf("Too many processors in grid factoring routine\n"); } } } /** * Subroutine for doing a serial sparse matrix (CSR format) matrix-vector * multiply * @param ilo,ihi bounding indices for rows * @param jlo,jhi bounding indices for columns * @param idx array containing bounds for each set of column indices * @param jdx array containing column indices * @param vals array containing matrix values * @param x array containing vector values * @param ax array containing matrix vector product */ void lmatvec(int64_t ilo, int64_t ihi, int64_t jlo, int64_t jhi, int64_t *idx, int64_t *jdx, double *vals, double *x, double *ax) { int64_t i, j; int64_t ncols; /* don't bother to initialize inner product to zero. Assume that this has * already been done or you are accumulating results */ for (i=ilo; i<=ihi; i++) { ncols = idx[i+1-ilo]-idx[i-ilo]; for (j=0; j1) { offblocks = (int*)malloc((nblocks-1)*sizeof(int)); } icnt = 0; maxlen = 0; for (i=0; i resmax) { resmax = fabs(pbuf[i]); } #else if (fabs(tbuf[i]) > resmax) { resmax = fabs(tbuf[i]); } #endif } /* Check for convergence * Only call read-increment if local convergence has changed * since the last cycle of row updates */ *maxr = resmax; #if SYNCHRONOUS GA_Sync(); { int icnv; char op[2]; if (resmax < tolerance) { icnv = 1; } else { icnv = 0; } op[0] = '*'; op[1] = '\0'; GA_Igop(&icnv,1,op); if (icnv == 1) { converged = 1; recheck_converged = 1; } else { converged = 0; recheck_converged = 0; } } #else if (resmax < tolerance && !converged_tracker) { NGA_Read_inc(g_cvg,&zero,1); converged_tracker = 1; } else if (resmax > tolerance && converged_tracker) { NGA_Read_inc(g_cvg,&zero,-1); converged_tracker = 0; } /* Only check for global convergence if locally converged */ if (converged_tracker) { int ld; NGA_Get(g_cvg, &zero, &zero, &icnt, &ld); if (!converged) { if (icnt >= nprocs) converged = 1; } else { if (icnt >= nprocs) { recheck_converged = 1; } else { converged = 0; } } } else { converged = 0; } #endif /* estimate absolute error */ { int imax=nprocs-1; error = 0.0; for (i=0; i1) { free(offblocks); } free(xbuf); free(rbuf); free(tbuf); } /** * Conjugate gradient solver for A.x = b * s_a: handle for sparse matrix A * g_b: handle for right hand side vector b * g_x: handle for solution vector x */ void cg_solve(int s_a, int g_b, int *g_x) { double alpha, beta, tol; int g_r, g_p, g_t; int me = GA_Nodeid(); double one_r; double m_one_r; double residual; int ncnt; int iterations = 10000; *g_x = GA_Duplicate(g_b, "dup_x"); g_r = GA_Duplicate(g_b, "dup_r"); g_p = GA_Duplicate(g_b, "dup_p"); g_t = GA_Duplicate(g_b, "dup_t"); /* accumulate boundary values to right hand side vector */ if (me == 0) { printf("\nRight hand side vector completed. Starting\n"); printf("conjugate gradient iterations.\n\n"); } /* Solve Laplace's equation using conjugate gradient method */ one_r = 1.0; m_one_r = -1.0; GA_Zero(*g_x); /* Initial guess is zero, so Ax = 0 and r = b */ GA_Copy(g_b, g_r); GA_Copy(g_r, g_p); residual = GA_Ddot(g_r,g_r); GA_Norm_infinity(g_r, &tol); ncnt = 0; /* Start iteration loop */ while (tol > 1.0e-5 && ncnt < iterations) { NGA_Sprs_array_matvec_multiply(s_a, g_p, g_t); alpha = GA_Ddot(g_t,g_p); alpha = residual/alpha; GA_Add(&one_r,*g_x,&alpha,g_p,*g_x); alpha = -alpha; GA_Add(&one_r,g_r,&alpha,g_t,g_r); GA_Norm_infinity(g_r, &tol); beta = residual; residual = GA_Ddot(g_r,g_r); beta = residual/beta; GA_Add(&one_r,g_r,&beta,g_p,g_p); if (me==0) printf("Iteration: %d Tolerance: %e\n",(int)ncnt+1,tol); ncnt++; } if (ncnt == iterations) { if (me==0) printf("Conjugate gradient solution failed to converge\n"); } else { if (me==0) printf("Conjugate gradient solution converged after %d iterations\n",ncnt); } NGA_Destroy(g_r); NGA_Destroy(g_p); NGA_Destroy(g_t); } int idim, jdim, kdim; long *sizes; int64_t *offsets; long *_ilo, *_ihi, *_jlo, *_jhi, *_klo, *_khi; int64_t ilo, ihi, jlo, jhi, klo, khi; int64_t xdim, ydim, zdim; int ipx, ipy, ipz, idx, idy, idz; int getIndex(int i, int j, int k) { #ifndef OLD_DIST int ix, iy, iz, pdx, index; int ldx, ldy; int ii, jj, kk; ix = idx; if (i > ihi) ix++; if (i < ilo) ix--; iy = idy; if (j > jhi) iy++; if (j < jlo) iy--; iz = idz; if (k > khi) iz++; if (k < klo) iz--; pdx = ix + iy*ipx + iz*ipx*ipy; ldx = _ihi[pdx]-_ilo[pdx]+1; ldy = _jhi[pdx]-_jlo[pdx]+1; ii = i-_ilo[pdx]; jj = j-_jlo[pdx]; kk = k-_klo[pdx]; index = ii + jj*ldx + kk*ldx*ldy; return index+offsets[pdx]; #else /* if (i < 0) i = xdim-1; if (i >= xdim) i = 0; if (j < 0) j = ydim-1; if (j >= ydim) j = 0; if (k < 0) k = zdim-1; if (k >= zdim) k = 0; */ return i + xdim*j + k*xdim*ydim; #endif } int main(int argc, char **argv) { int s_a, g_b, g_x, g_ref, g_dist; int one; int64_t one_64; int me, nproc; int64_t rdim, cdim, rdx, cdx; int64_t ldx, ldxy; int64_t i, j, k, ncnt; int iproc, ld; double x, y, z, val, h, rxdim, rydim, rzdim; long *lmap; int64_t *ibuf, **iptr, *imap; int64_t nproc64; double *vptr; double *vbuf; int ok; double one_r = 1.0; double m_one_r = -1.0; double ir, jr, ldr; double xinc_p, yinc_p, zinc_p; double xinc_m, yinc_m, zinc_m; int64_t tlo, thi; double alpha, beta, rho, rho_m, omega, m_omega, residual; double rv,ts,tt; int nsave; int heap=10000000, stack=10000000; int iterations = 10000; double tol, twopi; FILE *PHI; double tbeg, t_cgsolve, t_acgsolve; /* Intitialize a message passing library */ one = 1; one_64 = 1; MP_INIT(argc,argv); /* Initialize GA */ NGA_Initialize(); /* initialize random number generator */ /* Interior points of the grid run from 0 to NDIM-1, boundary points are located * at -1 and NDIM for each of the axes */ idim = NDIM; jdim = NDIM; kdim = NDIM; xdim = NDIM; ydim = NDIM; zdim = NDIM; rxdim = 1.0; rydim = 1.0; rzdim = 1.0; h = rxdim/((double)NDIM); me = GA_Nodeid(); nproc = GA_Nnodes(); twopi = 8.0*atan(1.0); t_cgsolve = 0.0; t_acgsolve = 0.0; heap /= nproc; stack /= nproc; if(! MA_init(MT_F_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ /* factor array */ grid_factor(nproc, idim, jdim, kdim, &ipx, &ipy, &ipz); if (me == 0) { printf("Solving Laplace's equation on %d processors\n",nproc); printf("\n Using %d X %d X %d processor grid\n",ipx,ipy,ipz); printf("\n Grid size is %d X %d X %d\n",idim,jdim,kdim); #ifdef OLD_DIST printf("\n Using slab decomposition\n"); #else printf("\n Using block decomposition\n"); #endif } /* figure out process location in proc grid */ i = me; idx = me%ipx; i = (i-idx)/ipx; idy = i%ipy; idz = (i-idy)/ipy; /* find bounding indices for this processor */ ilo = (xdim*idx)/ipx; if (idx < ipx-1) { ihi = (xdim*(idx+1))/ipx-1; } else { ihi = xdim-1; } jlo = (ydim*idy)/ipy; if (idy < ipy-1) { jhi = (ydim*(idy+1))/ipy-1; } else { jhi = ydim-1; } klo = (zdim*idz)/ipz; if (idz < ipz-1) { khi = (zdim*(idz+1))/ipz-1; } else { khi = zdim-1; } /* redefine idim, jdim, kdim */ idim = ihi-ilo+1; jdim = jhi-jlo+1; kdim = khi-klo+1; sizes = malloc(nproc*sizeof(int64_t)); offsets = (int64_t*)malloc(nproc*sizeof(int64_t)); _ilo = malloc(nproc*sizeof(int64_t)); _ihi = malloc(nproc*sizeof(int64_t)); _jlo = malloc(nproc*sizeof(int64_t)); _jhi = malloc(nproc*sizeof(int64_t)); _klo = malloc(nproc*sizeof(int64_t)); _khi = malloc(nproc*sizeof(int64_t)); for (i=0; i= 0) { cdx = getIndex(i-1,j,k); val = xinc_m/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (j+1 < ydim) { cdx = getIndex(i,j+1,k); val = yinc_p/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (j-1 >= 0) { cdx = getIndex(i,j-1,k); val = yinc_m/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (k+1 < zdim) { cdx = getIndex(i,j,k+1); val = zinc_p/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (k-1 >= 0) { cdx = getIndex(i,j,k-1); val = zinc_m/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } } } } if (NGA_Sprs_array_assemble(s_a) && me == 0) { printf("\n Sparse array assembly completed\n"); } if (NDIM <= 16) { NGA_Sprs_array_export(s_a,"matrix.dat"); } /* Construct RHS vector. Assume points on boundary are given by * the equation f(x,y,z) = cos(twopi*x) + cos(twopi*y) + cos(twopi*z) */ ibuf = (int64_t*)malloc(ncnt*sizeof(int64_t)); iptr = (int64_t**)malloc(ncnt*sizeof(int64_t*)); vbuf = (double*)malloc(ncnt*sizeof(double)); for (i=0; i diff_max) diff_max = diff; /* Shouldn't happen, but try and make sure diff_min > 0.0 */ if (diff_min == 0.0) { diff_min_zero = 1; diff_min = diff; } else if(diff < diff_min && diff > 0.0) { diff_min = diff; } } if (diff_min_zero) { printf("p[%d] found minimum difference of zero\n",me); } /* find global minimum and maximum difference */ GA_Dgop(&diff_min,1,"min"); GA_Dgop(&diff_max,1,"max"); bins = (int*)malloc(NUM_BINS*sizeof(int)); for (i=0; i= NUM_BINS) ibin--; bins[ibin]++; } /* Bin up differences globally */ GA_Igop(bins,NUM_BINS,"+"); if (me == 0) { printf("Log_10 minimum difference: %f\n",lmin); printf("Log_10 maximum difference: %f\n",lmax); for (i=0; i norm = sqrt(product)/uxnorm if(me.eq.0 .and. Mod(k,10).eq.1) print *,k,' error= ',norm c if(norm .gt. eps) then if(.not. have_data) goto 10 c c... not done yet, get a copy of my piece of g_u and neighboring grid points c ... determine where from we should copy data -- consider topology index_first = indexl if(jlo.eq.1) index_first = index_first + ni+2 if(ilo.eq.1) index_first = index_first + 1 c call ga_get(g_u, max(1,ilo-1), min(m,ihi+1), & max(1,jlo-1), min(m,jhi+1), & dbl_mb(index_first), ni+2 ) goto 10 endif c if(me.eq.0) then print *,' converged in ',k, ' iterations, error= ',norm endif c c*** deallocate MA memory and destroy global arrays if(have_data)then if(.not. ma_pop_stack(h_l))call ga_error('invalid handle ?',0) if(.not. ma_pop_stack(h_d))call ga_error('invalid handle ?',0) endif if(.not. ga_destroy(g_u)) call ga_error('invalid handle ?',0) if(.not. ga_destroy(g_ux)) call ga_error('invalid handle ?',0) if(.not. ga_destroy(g_diff)) call ga_error('invalid handle ?',0) c call ga_sync() end subroutine nextu(ni, nj, u, ld, u0, ld0, S, W, C, N, E, D ) implicit none integer ni, nj, ld, ld0 double precision u(1:ld,1:nj), u0(0:ld0,0:nj), D(1:ni,1:nj) double precision S, W, C, N, E integer i, j c do j = 1, nj do i = 1, ni u(i,j) = S*u0(i+1,j) + W*u0(i,j-1) + & N*u0(i-1,j) + E*u0(i,j+1) + D(i,j) enddo enddo end subroutine jacinit(m, S, W, C, N, E, D, ldd, ux, & ilo, ihi, jlo, jhi, ldu ) implicit none #include "mafdecls.fh" #include "global.fh" double precision S, W, C, N, E, temp integer ilo, ihi, jlo, jhi, ldu, ldd, i, j integer m, ni, nj, jj, ii double precision h, ux(ldu,m), D(ldd,m) if(m.le.1)call ga_error('jacinit: wrong value of m',0) S = .25 W = .25 N = .25 E = .25 h = 1d0/(m+1) ni = ihi - ilo + 1 ! number of 'local' rows nj = jhi - jlo + 1 ! number of 'local' columns do j = 1, nj jj = jlo + j -1 do i = 1, ni temp = 0d0 ii = ilo + i -1 if(jj .eq. 1) temp = temp + 1 if(ii .eq. 1) temp = temp + 1 if(ii .eq. m) temp = temp + jj*h + 1 if(jj .eq. m) temp = temp + ii*h + 1 D(i,j) = temp/4d0 ux(i,j)= 1d0 + h*h*ii*jj enddo enddo end ga-5.9.2/global/testing/kaczmarz.c000066400000000000000000001034511500715745200170330ustar00rootroot00000000000000#include #include #include #include "macdecls.h" #include "ga.h" #include "mp3.h" #define OLD_XDIST #define WRITE_VTK #define NDIM 64 #define MAX_ITERATIONS 500 #define NUM_BINS 40 /** * Solve Laplace's equation on a cubic domain using the sparse matrix * functionality in GA. */ #define MAX_FACTOR 1024 void grid_factor(int p, int xdim, int ydim, int zdim, int *idx, int *idy, int *idz) { int i, j, k; int ip, ifac, pmax, prime[MAX_FACTOR]; int fac[MAX_FACTOR]; int ix, iy, iz, ichk; i = 1; /** * factor p completely * first, find all prime numbers, besides 1, less than or equal to p */ ip = p; pmax = 0; for (i=2; i<=ip; i++) { ichk = 1; for (j=0; j MAX_FACTOR) printf("Overflow in grid_factor\n"); prime[pmax-1] = i; } } /** * find all prime factors of p */ ip = p; ifac = 0; for (i=0; i= 0; i--) { ix = xdim/(*idx); iy = ydim/(*idy); iz = zdim/(*idz); if (ix >= iy && ix >= iz && ix > 1) { *idx = fac[i]*(*idx); } else if (iy >= ix && iy >= iz && iy > 1) { *idy = fac[i]*(*idy); } else if (iz >= ix && iz >= iy && iz > 1) { *idz = fac[i]*(*idz); } else { printf("Too many processors in grid factoring routine\n"); } } } /* Randomly reorder the sequence of integers [0,1,2,...,N-1] by * apply N random pair permutations */ void reorder(int *seq, int N) { int i, j, idx, jdx; double rn; int me = GA_Nodeid(); for (i=0; i= N) j--; idx = seq[i]; jdx = seq[j+i]; seq[i] = jdx; seq[j+i] = idx; } } void k_solve(int s_a, int g_b, int g_ref, int *g_x) { int nblocks; int *list; int i, j, nb, icnt, g_cvg, g_maxr, g_ax; int my_nb; int converged = 0; int converged_tracker = 0; int recheck_converged = 0; int me = GA_Nodeid(); int nprocs = GA_Nnodes(); int64_t my_lo, my_hi, my_n, ld64; double *my_vals; double *my_rhs; int64_t total; double *xblocks; double **blk_ptrs; double *diag_ptr; double *normA; double *maxr; int64_t *b_n; int64_t **m_idx; int64_t **m_jdx; int64_t *m_jlo; int64_t ilast; double **m_vals; double *resbuf; double *refptr; double *ebuf; double *maxe; double *eptr; double g_err; double error; int tlo, thi, tld; int iteration_max = MAX_ITERATIONS; int iter; int *seq; int irow; int one = 1; int zero = 0; double tolerance = 1.0e-5; double maxax; double axmb; double axtmp; double maxinc; int maxrow; /* Create solution vector */ *g_x = GA_Duplicate(g_b, "dup_x"); GA_Zero(*g_x); /* Create atomic counter for testing convergence */ g_cvg = NGA_Create_handle(); NGA_Set_data(g_cvg,one,&one,C_INT); NGA_Allocate(g_cvg); GA_Zero(g_cvg); g_maxr = NGA_Create_handle(); NGA_Set_data(g_maxr,one,&nprocs,C_DBL); NGA_Allocate(g_maxr); GA_Zero(g_maxr); g_ax = NGA_Create_handle(); NGA_Set_data(g_ax,one,&nprocs,C_DBL); NGA_Allocate(g_ax); GA_Zero(g_ax); NGA_Distribution(g_maxr,me,&tlo,&thi); NGA_Access(g_maxr,&tlo,&thi,&maxr,&tld); g_err = NGA_Create_handle(); NGA_Set_data(g_err,one,&nprocs,C_DBL); NGA_Allocate(g_err); GA_Zero(g_err); NGA_Distribution(g_err,me,&tlo,&thi); NGA_Access(g_err,&tlo,&thi,&maxe,&tld); NGA_Distribution(g_ref,me,&tlo,&thi); NGA_Access(g_ref,&tlo,&thi,&eptr,&tld); /* Find out which column blocks are non-zero */ NGA_Sprs_array_col_block_list(s_a, &list, &nblocks); if (nblocks>1) { blk_ptrs = (double**)malloc((nblocks-1)*sizeof(double*)); } b_n = (int64_t*)malloc(nblocks*sizeof(int64_t*)); m_idx = (int64_t**)malloc(nblocks*sizeof(int64_t*)); m_jdx = (int64_t**)malloc(nblocks*sizeof(int64_t*)); m_jlo = (int64_t*)malloc(nblocks*sizeof(int64_t)); m_vals = (double**)malloc(nblocks*sizeof(double*)); resbuf = (double*)malloc(nprocs*sizeof(double)); ebuf = (double*)malloc(nprocs*sizeof(double)); /* Find total data on processes corresponding to non-zero column blocks * (but not including block on diagonal) */ total = 0; for (i=0; i 1) { blk_ptrs[0] = NULL; } icnt = 0; for (i=0; i= b_n[nb]) printf("p[%d] Illegal column index: %ld\n",me,icol); double val = vals[jstart+j]; axdot += val*xptr[icol]; } } /* Finish calculating update to x[irow] */ if (normA[irow] != 0.0) { axtmp = axdot; axdot -= my_rhs[irow]; axdot /= normA[irow]; if (fabs(axdot) > axmb) { maxax = axtmp; axmb = fabs(axdot); maxrow = irow; } } else { GA_Error("Row of matrix is all zeros!",irow); } /* Update vector elements */ #if 1 { double *xptr = my_vals; int64_t *idx = m_idx[my_nb]; int64_t *jdx = m_jdx[my_nb]; double *vals = m_vals[my_nb]; int64_t jnum = idx[irow+1]-idx[irow]; int64_t jstart = idx[irow]; for (j=0; j= my_n) { printf("p[%d] icol out of bounds icol: %ld my_n: %ld\n",me,icol,my_n); } xptr[icol] -= val*axdot; if (fabs(val*axdot) > maxinc) maxinc = fabs(val*axdot); } } #else icnt = 0; for (nb = 0; nb maxinc) maxinc = fabs(val*axdot); } } #endif } /* Calculate maximum residual on this process */ residual = 0.0; for (i=0; i residual) { residual = fabs(xi-my_rhs[i]); } } *maxr = residual; /* Check for convergence * Only call read-increment if local convergence has changed * since the last cycle of row updates */ if (residual < tolerance && !converged_tracker) { NGA_Read_inc(g_cvg,&zero,1); converged_tracker = 1; } else if (residual > tolerance && converged_tracker) { NGA_Read_inc(g_cvg,&zero,-1); converged_tracker = 0; } /* Only check for global convergence if locally converged */ if (converged_tracker) { int ld; NGA_Get(g_cvg, &zero, &zero, &icnt, &ld); if (!converged) { if (icnt >= nprocs) converged = 1; } else { if (icnt >= nprocs) { recheck_converged = 1; } else { converged = 0; } } } else { converged = 0; } /* estimate absolute error */ { int imax=nprocs-1; error = 0.0; for (i=0; i 1) { free(blk_ptrs); } free(xblocks); free(seq); free(list); } void cg_solve(int s_a, int g_b, int *g_x) { double alpha, beta, tol; int g_r, g_p, g_t; int me = GA_Nodeid(); double one_r; double m_one_r; double residual; int ncnt; int iterations = 10000; *g_x = GA_Duplicate(g_b, "dup_x"); g_r = GA_Duplicate(g_b, "dup_r"); g_p = GA_Duplicate(g_b, "dup_p"); g_t = GA_Duplicate(g_b, "dup_t"); /* accumulate boundary values to right hand side vector */ if (me == 0) { printf("\nRight hand side vector completed. Starting\n"); printf("conjugate gradient iterations.\n\n"); } /* Solve Laplace's equation using conjugate gradient method */ one_r = 1.0; m_one_r = -1.0; GA_Zero(*g_x); /* Initial guess is zero, so Ax = 0 and r = b */ GA_Copy(g_b, g_r); GA_Copy(g_r, g_p); residual = GA_Ddot(g_r,g_r); GA_Norm_infinity(g_r, &tol); ncnt = 0; /* Start iteration loop */ while (tol > 1.0e-5 && ncnt < iterations) { if (me==0) printf("Iteration: %d Tolerance: %e\n",(int)ncnt+1,tol); NGA_Sprs_array_matvec_multiply(s_a, g_p, g_t); alpha = GA_Ddot(g_t,g_p); alpha = residual/alpha; GA_Add(&one_r,*g_x,&alpha,g_p,*g_x); alpha = -alpha; GA_Add(&one_r,g_r,&alpha,g_t,g_r); GA_Norm_infinity(g_r, &tol); beta = residual; residual = GA_Ddot(g_r,g_r); beta = residual/beta; GA_Add(&one_r,g_r,&beta,g_p,g_p); ncnt++; } if (ncnt == iterations) { if (me==0) printf("Conjugate gradient solution failed to converge\n"); } else { if (me==0) printf("Conjugate gradient solution converged after %d iterations\n",ncnt); } NGA_Destroy(g_r); NGA_Destroy(g_p); NGA_Destroy(g_t); } int idim, jdim, kdim; int64_t *offsets; long *sizes; long *_ilo, *_ihi, *_jlo, *_jhi, *_klo, *_khi; int64_t ilo, ihi, jlo, jhi, klo, khi; int64_t xdim, ydim, zdim; int ipx, ipy, ipz, idx, idy, idz; int getIndex(int i, int j, int k) { #ifndef OLD_DIST int ix, iy, iz, pdx, index; int ldx, ldy; int ii, jj, kk; ix = idx; if (i > ihi) ix++; if (i < ilo) ix--; iy = idy; if (j > jhi) iy++; if (j < jlo) iy--; iz = idz; if (k > khi) iz++; if (k < klo) iz--; pdx = ix + iy*ipx + iz*ipx*ipy; ldx = _ihi[pdx]-_ilo[pdx]+1; ldy = _jhi[pdx]-_jlo[pdx]+1; ii = i-_ilo[pdx]; jj = j-_jlo[pdx]; kk = k-_klo[pdx]; index = ii + jj*ldx + kk*ldx*ldy; return index+offsets[pdx]; #else /* if (i < 0) i = xdim-1; if (i >= xdim) i = 0; if (j < 0) j = ydim-1; if (j >= ydim) j = 0; if (k < 0) k = zdim-1; if (k >= zdim) k = 0; */ return i + xdim*j + k*xdim*ydim; #endif } int main(int argc, char **argv) { int s_a, g_b, g_x, g_ref, g_dist; int one; int64_t one_64; int me, nproc; int64_t rdim, cdim, rdx, cdx; int64_t ldx, ldxy; int64_t i, j, k, ncnt; int iproc, ld; double x, y, z, val, h, rxdim, rydim, rzdim; long *lmap; int64_t *ibuf, **iptr, *imap; int64_t nproc64; double *vptr; double *vbuf; int ok; double one_r = 1.0; double m_one_r = -1.0; double ir, jr, ldr; double xinc_p, yinc_p, zinc_p; double xinc_m, yinc_m, zinc_m; int64_t tlo, thi; double alpha, beta, rho, rho_m, omega, m_omega, residual; double rv,ts,tt; int nsave; int heap=10000000, stack=10000000; int iterations = 10000; double tol, twopi; FILE *PHI; double tbeg, t_cgsolve, t_ksolve; /* Intitialize a message passing library */ one = 1; one_64 = 1; MP_INIT(argc,argv); /* Initialize GA */ NGA_Initialize(); /* initialize random number generator */ /* Interior points of the grid run from 0 to NDIM-1, boundary points are located * at -1 and NDIM for each of the axes */ idim = NDIM; jdim = NDIM; kdim = NDIM; xdim = NDIM; ydim = NDIM; zdim = NDIM; rxdim = 1.0; rydim = 1.0; rzdim = 1.0; h = rxdim/((double)NDIM); me = GA_Nodeid(); nproc = GA_Nnodes(); twopi = 8.0*atan(1.0); x = NGA_Rand(32823+me); t_cgsolve = 0.0; t_ksolve = 0.0; heap /= nproc; stack /= nproc; if(! MA_init(MT_F_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ /* factor array */ grid_factor(nproc, idim, jdim, kdim, &ipx, &ipy, &ipz); if (me == 0) { printf("Solving Laplace's equation on %d processors\n",nproc); printf("\n Using %d X %d X %d processor grid\n",ipx,ipy,ipz); printf("\n Grid size is %d X %d X %d\n",idim,jdim,kdim); #ifdef OLD_DIST printf("\n Using slab decomposition\n"); #else printf("\n Using block decomposition\n"); #endif } /* figure out process location in proc grid */ i = me; idx = me%ipx; i = (i-idx)/ipx; idy = i%ipy; idz = (i-idy)/ipy; /* find bounding indices for this processor */ ilo = (xdim*idx)/ipx; if (idx < ipx-1) { ihi = (xdim*(idx+1))/ipx-1; } else { ihi = xdim-1; } jlo = (ydim*idy)/ipy; if (idy < ipy-1) { jhi = (ydim*(idy+1))/ipy-1; } else { jhi = ydim-1; } klo = (zdim*idz)/ipz; if (idz < ipz-1) { khi = (zdim*(idz+1))/ipz-1; } else { khi = zdim-1; } /* redefine idim, jdim, kdim */ idim = ihi-ilo+1; jdim = jhi-jlo+1; kdim = khi-klo+1; sizes = malloc(nproc*sizeof(int64_t)); offsets = (int64_t*)malloc(nproc*sizeof(int64_t)); _ilo = malloc(nproc*sizeof(int64_t)); _ihi = malloc(nproc*sizeof(int64_t)); _jlo = malloc(nproc*sizeof(int64_t)); _jhi = malloc(nproc*sizeof(int64_t)); _klo = malloc(nproc*sizeof(int64_t)); _khi = malloc(nproc*sizeof(int64_t)); for (i=0; i= 0) { cdx = getIndex(i-1,j,k); val = xinc_m/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (j+1 < ydim) { cdx = getIndex(i,j+1,k); val = yinc_p/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (j-1 >= 0) { cdx = getIndex(i,j-1,k); val = yinc_m/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (k+1 < zdim) { cdx = getIndex(i,j,k+1); val = zinc_p/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (k-1 >= 0) { cdx = getIndex(i,j,k-1); val = zinc_m/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } } } } if (NGA_Sprs_array_assemble(s_a) && me == 0) { printf("\n Sparse array assembly completed\n"); } if (NDIM <= 32) { NGA_Sprs_array_export(s_a,"matrix.dat"); } /* Construct RHS vector. Assume points on boundary are given by * the equation f(x,y,z) = cos(twopi*x) + cos(twopi*y) + cos(twopi*z) */ ibuf = (int64_t*)malloc(ncnt*sizeof(int64_t)); iptr = (int64_t**)malloc(ncnt*sizeof(int64_t*)); vbuf = (double*)malloc(ncnt*sizeof(double)); for (i=0; i diff_max) diff_max = diff; /* Shouldn't happen, but try and make sure diff_min > 0.0 */ if (diff_min == 0.0) { diff_min_zero = 1; diff_min = diff; } else if(diff < diff_min && diff > 0.0) { diff_min = diff; } } if (diff_min_zero) { printf("p[%d] found minimum difference of zero\n",me); } /* find global minimum and maximum difference */ GA_Dgop(&diff_min,1,"min"); GA_Dgop(&diff_max,1,"max"); bins = (int*)malloc(NUM_BINS*sizeof(int)); for (i=0; i= NUM_BINS) ibin--; bins[ibin]++; } /* Bin up differences globally */ GA_Igop(bins,NUM_BINS,"+"); if (me == 0) { printf("Log_10 minimum difference: %f\n",lmin); printf("Log_10 maximum difference: %f\n",lmax); for (i=0; i #include #include #include "macdecls.h" #include "ga.h" #include "mp3.h" #define WRITE_VTK #define CG_SOLVE 1 #define NDIM 128 /** * Solve Laplace's equation on a cubic domain using the sparse matrix * functionality in GA. */ #define MAX_FACTOR 1024 void grid_factor(int p, int xdim, int ydim, int zdim, int *idx, int *idy, int *idz) { int i, j, k; int ip, ifac, pmax, prime[MAX_FACTOR]; int fac[MAX_FACTOR]; int ix, iy, iz, ichk; i = 1; /** * factor p completely * first, find all prime numbers, besides 1, less than or equal to * the square root of p */ ip = p; pmax = 0; for (i=2; i<=ip; i++) { ichk = 1; for (j=0; j MAX_FACTOR) printf("Overflow in grid_factor\n"); prime[pmax-1] = i; } } /** * find all prime factors of p */ ip = p; ifac = 0; for (i=0; i= 0; i--) { ix = xdim/(*idx); iy = ydim/(*idy); iz = zdim/(*idz); if (ix >= iy && ix >= iz && ix > 1) { *idx = fac[i]*(*idx); } else if (iy >= ix && iy >= iz && iy > 1) { *idy = fac[i]*(*idy); } else if (iz >= ix && iz >= iy && iz > 1) { *idz = fac[i]*(*idz); } else { printf("Too many processors in grid factoring routine\n"); } } } int main(int argc, char **argv) { int s_a, g_b, g_x, g_p, g_r, g_t; int g_s, g_v, g_rm; int one; int64_t one_64; int me, nproc; int idim, jdim, kdim; int64_t xdim, ydim, zdim; int64_t rdim, cdim, rdx, cdx; int64_t ldx, ldxy; int ipx, ipy, ipz, idx, idy, idz; int64_t ilo, ihi, jlo, jhi, klo, khi; int64_t i, j, k, ncnt; int iproc, ld; double x, y, z, val, h, rxdim, rydim, rzdim; int64_t *ibuf, **iptr; double *vptr; double *vbuf; double t_beg, dot_time, dot_time_s; int ok; double one_r = 1.0; double m_one_r = -1.0; double ir, jr, ldr; double xinc_p, yinc_p, zinc_p; double xinc_m, yinc_m, zinc_m; double alpha, beta, rho, rho_m, omega, m_omega, residual; double rv,ts,tt; int nsave; int heap=20000000, stack=20000000; int iterations = 10000; double tol, twopi; FILE *PHI; char op[2]; /* Intitialize a message passing library */ one = 1; one_64 = 1; MP_INIT(argc,argv); /* Initialize GA */ NGA_Initialize(); /* Interior points of the grid run from 0 to NDIM-1, boundary points are located * at -1 and NDIM for each of the axes */ idim = NDIM; jdim = NDIM; kdim = NDIM; xdim = NDIM; ydim = NDIM; zdim = NDIM; rxdim = 1.0; rydim = 1.0; rzdim = 1.0; h = rxdim/((double)NDIM); me = GA_Nodeid(); nproc = GA_Nnodes(); twopi = 8.0*atan(1.0); heap /= nproc; stack /= nproc; if(! MA_init(MT_F_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ /* factor array */ grid_factor(nproc, idim, jdim, kdim, &ipx, &ipy, &ipz); if (me == 0) { printf("Solving Laplace's equation on %d processors\n",nproc); printf("\n Using %d X %d X %d processor grid\n",ipx,ipy,ipz); printf("\n Grid size is %d X %d X %d\n",idim,jdim,kdim); } /* figure out process location in proc grid */ i = me; idx = me%ipx; i = (i-idx)/ipx; idy = i%ipy; idz = (i-idy)/ipy; /* find bounding indices for this processor */ ilo = (xdim*idx)/ipx; if (idx < ipx-1) { ihi = (xdim*(idx+1))/ipx-1; } else { ihi = xdim-1; } jlo = (ydim*idy)/ipy; if (idy < ipy-1) { jhi = (ydim*(idy+1))/ipy-1; } else { jhi = ydim-1; } klo = (zdim*idz)/ipz; if (idz < ipz-1) { khi = (zdim*(idz+1))/ipz-1; } else { khi = zdim-1; } /* create sparse array */ rdim = xdim*ydim*zdim; cdim = xdim*ydim*zdim; ldx = xdim; ldxy = xdim*ydim; s_a = NGA_Sprs_array_create64(rdim, cdim, C_DBL); ncnt = 0; /* Set elements of Laplace operator. Use a global indexing scheme and don't * worry about setting elements locally. Count up values associated with * boundaries */ for (i=ilo; i<=ihi; i++) { if (i == 0) { xinc_m = 2.0; xinc_p = 1.0; } else if (i == NDIM - 1) { xinc_m = 1.0; xinc_p = 2.0; } else { xinc_p = 1.0; xinc_m = 1.0; } for (j=jlo; j<=jhi; j++) { if (j == 0) { yinc_m = 2.0; yinc_p = 1.0; } else if (j == NDIM - 1) { yinc_m = 1.0; yinc_p = 2.0; } else { yinc_p = 1.0; yinc_m = 1.0; } for (k=klo; k<=khi; k++) { if (k == 0) { zinc_m = 2.0; zinc_p = 1.0; } else if (k == NDIM - 1) { zinc_m = 1.0; zinc_p = 2.0; } else { zinc_p = 1.0; zinc_m = 1.0; } rdx = i + j*ldx + k*ldxy; val = -(xinc_p+xinc_m+yinc_p+yinc_m+zinc_p+zinc_m)/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,rdx,&val); if (i+1 < xdim) { cdx = i+1 + j*ldx + k*ldxy; val = xinc_p/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (i-1 >= 0) { cdx = i-1 + j*ldx + k*ldxy; val = xinc_m/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (j+1 < ydim) { cdx = i + (j+1)*ldx + k*ldxy; val = yinc_p/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (j-1 >= 0) { cdx = i + (j-1)*ldx + k*ldxy; val = yinc_m/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (k+1 < zdim) { cdx = i + j*ldx + (k+1)*ldxy; val = zinc_p/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (k-1 >= 0) { cdx = i + j*ldx + (k-1)*ldxy; val = zinc_m/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } } } } if (NGA_Sprs_array_assemble(s_a) && me == 0) { printf("\n Sparse array assembly completed\n"); } /* Construct RHS vector. Assume points on boundary are given by * the equation f(x,y,z) = cos(twopi*x) + cos(twopi*y) + cos(twopi*z) */ ibuf = (int64_t*)malloc(ncnt*sizeof(int64_t)); iptr = (int64_t**)malloc(ncnt*sizeof(int64_t*)); vbuf = (double*)malloc(ncnt*sizeof(double)); for (i=0; i 1.0e-5 && ncnt < iterations) { if (me==0) printf("Iteration: %d Tolerance: %e\n",(int)ncnt+1,tol); GA_Mask_sync(0,0); NGA_Sprs_array_matvec_multiply(s_a, g_p, g_t); t_beg = GA_Wtime(); alpha = GA_Ddot(g_t,g_p); dot_time += GA_Wtime()-t_beg; alpha = residual/alpha; GA_Mask_sync(0,0); GA_Add(&one_r,g_x,&alpha,g_p,g_x); alpha = -alpha; GA_Mask_sync(0,0); GA_Add(&one_r,g_r,&alpha,g_t,g_r); /*GA_Norm_infinity(g_r, &tol);*/ beta = residual; t_beg = GA_Wtime(); residual = GA_Ddot(g_r,g_r); dot_time += GA_Wtime()-t_beg; tol = sqrt(residual); beta = residual/beta; GA_Mask_sync(0,0); GA_Add(&one_r,g_r,&beta,g_p,g_p); ncnt++; } /* Evaluate time for dot products if no calculation takes place */ t_beg = GA_Wtime(); for (i=0; i 1.0e-5 && ncnt < iterations) { if (me==0) printf("Iteration: %d Tolerance: %e\n",(int)ncnt+1,tol); rho = GA_Ddot(g_r,g_rm); if (rho == 0.0) { GA_Error("BiCG-STAB method fails",0); } if (ncnt == 0) { GA_Copy(g_rm,g_p); } else { beta = (rho/rho_m)*(alpha/omega); m_omega = -omega; GA_Add(&one_r,g_p,&m_omega,g_v,g_p); GA_Add(&one_r,g_rm,&beta,g_p,g_p); } NGA_Sprs_array_matvec_multiply(s_a, g_p, g_v); rv = GA_Ddot(g_r,g_v); alpha = -rho/rv; GA_Add(&one_r,g_rm,&alpha,g_v,g_s); alpha = -alpha; GA_Norm_infinity(g_s, &tol); if (tol < 1.0e-05) { GA_Add(&one_r,g_x,&alpha,g_p,g_x); } NGA_Sprs_array_matvec_multiply(s_a, g_s, g_t); ts = GA_Ddot(g_t,g_s); tt = GA_Ddot(g_t,g_t); omega = ts/tt; m_omega = -omega; GA_Add(&one_r,g_x,&alpha,g_p,g_x); GA_Add(&one_r,g_x,&omega,g_s,g_x); GA_Add(&one_r,g_s,&m_omega,g_t,g_rm); GA_Norm_infinity(g_rm, &tol); if (tol < 1.0e-05) break; if (omega == 0) { GA_Error("BiCG-STAB method cannot continue",0); } ncnt++; rho_m = rho; } NGA_Destroy(g_r); NGA_Destroy(g_rm); NGA_Destroy(g_p); NGA_Destroy(g_v); NGA_Destroy(g_s); NGA_Destroy(g_t); #endif /* Write solution to file */ #ifdef WRITE_VTK if (me == 0) { vbuf = (double*)malloc(xdim*ydim*sizeof(double)); PHI = fopen("phi.vtk","w"); fprintf(PHI,"# vtk DataFile Version 3.0\n"); fprintf(PHI,"Laplace Equation Solution\n"); fprintf(PHI,"ASCII\n"); fprintf(PHI,"DATASET STRUCTURED_POINTS\n"); fprintf(PHI,"DIMENSIONS %ld %ld %ld\n",xdim,ydim,zdim); fprintf(PHI,"ORIGIN %12.6f %12.6f %12.6f\n",0.5*h,0.5*h,0.5*h); fprintf(PHI,"SPACING %12.6f %12.6f %12.6f\n",h,h,h); fprintf(PHI," \n"); fprintf(PHI,"POINT_DATA %ld\n",xdim*ydim*zdim); fprintf(PHI,"SCALARS Phi float\n"); fprintf(PHI,"LOOKUP_TABLE default\n"); for (k=0; k #include #include #include "macdecls.h" #include "ga.h" #include "mp3.h" //#define WRITE_VTK #define CG_SOLVE 0 #define NDIM 256 /** * Solve Laplace's equation on a cubic domain using the sparse matrix * functionality in GA. */ #define MAX_FACTOR 1024 void grid_factor(int p, int xdim, int ydim, int zdim, int *idx, int *idy, int *idz) { int i, j, k; int ip, ifac, pmax, prime[MAX_FACTOR]; int fac[MAX_FACTOR]; int ix, iy, iz, ichk; i = 1; /** * factor p completely * first, find all prime numbers, besides 1, less than or equal to * the square root of p */ ip = (int)(sqrt((double)p))+1; pmax = 0; for (i=2; i<=ip; i++) { ichk = 1; for (j=0; j MAX_FACTOR) printf("Overflow in grid_factor\n"); prime[pmax-1] = i; } } /** * find all prime factors of p */ ip = p; ifac = 0; for (i=0; i= 0; i--) { ix = xdim/(*idx); iy = ydim/(*idy); iz = zdim/(*idz); if (ix >= iy && ix >= iz && ix > 1) { *idx = fac[i]*(*idx); } else if (iy >= ix && iy >= iz && iy > 1) { *idy = fac[i]*(*idy); } else if (iz >= ix && iz >= iy && iz > 1) { *idz = fac[i]*(*idz); } else { printf("Too many processors in grid factoring routine\n"); } } } /** * variables for executing asynchronous dot products */ typedef struct { double *a_vec_old; /* old values of vector A on this processor */ double *b_vec_old; /* old values of vector B on this processor */ double *a_vec_new; /* new values of vector A on this processor */ double *b_vec_new; /* new values of vector B on this processor */ double *dot_buf; /* array for holding increments */ double *dot; /* current value of dot product */ double *norm_buf; /* array for holding norm estimates */ double *norm; /* current value of norm infinity */ int g_a; /* handle of array A */ int g_b; /* handle of array B */ int g_dot; /* global array containing dot product */ int g_norm; /* global array containing infinity norm */ int64_t vlen; /* length of vectors A and B on this processor */ int actv; /* flag indicating whether this handle is active */ int counter; /* counter to keep track of number of async iterations */ } async_dot_struct; /** * Array of asynchronous dot product structures */ #define MAX_DOT_HANDLES 100 async_dot_struct _dot_handles[MAX_DOT_HANDLES]; /** * Initialize asynchronous dot product functionality */ void init_async_dot() { int i; for (i=0; i *norm) *norm = fabs(a_vec_new[i]); } // printf("p[%d] handle: %d norm: %e\n",GA_Nodeid(),handle,*norm); norm_buf[me] = *norm; *_dot_handles[handle].norm = *norm; for (i=0; i *norm) *norm = fabs(norm_buf[i]); } #else GA_Norm_infinity(g_a,norm); #endif } /** * Clean up asynchronous dot product object * handle: handle of asynchronous dot product */ void destroy_async_dot(int handle) { int64_t me = GA_Nodeid(); int g_dot = _dot_handles[handle].g_dot; free(_dot_handles[handle].a_vec_new); free(_dot_handles[handle].b_vec_new); free(_dot_handles[handle].a_vec_old); free(_dot_handles[handle].b_vec_old); free(_dot_handles[handle].dot_buf); NGA_Release64(g_dot, &me, &me); NGA_Destroy(g_dot); _dot_handles[handle].a_vec_new = NULL; _dot_handles[handle].b_vec_new = NULL; _dot_handles[handle].a_vec_old = NULL; _dot_handles[handle].b_vec_old = NULL; _dot_handles[handle].dot_buf = NULL; _dot_handles[handle].dot = NULL; _dot_handles[handle].actv = 0; if (_dot_handles[handle].g_a == _dot_handles[handle].g_b) { int g_norm = _dot_handles[handle].g_norm; NGA_Release64(g_norm, &me, &me); NGA_Destroy(g_norm); free(_dot_handles[handle].norm_buf); } } void terminate_async_dot() { int i; for (i=0; i= 0) { cdx = i-1 + j*ldx + k*ldxy; val = xinc_m/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (j+1 < ydim) { cdx = i + (j+1)*ldx + k*ldxy; val = yinc_p/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (j-1 >= 0) { cdx = i + (j-1)*ldx + k*ldxy; val = yinc_m/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (k+1 < zdim) { cdx = i + j*ldx + (k+1)*ldxy; val = zinc_p/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } if (k-1 >= 0) { cdx = i + j*ldx + (k-1)*ldxy; val = zinc_m/(h*h); NGA_Sprs_array_add_element64(s_a,rdx,cdx,&val); } else { ncnt++; } } } } if (NGA_Sprs_array_assemble(s_a) && me == 0) { printf("\n Sparse array assembly completed\n"); } /* Construct RHS vector. Assume points on boundary are given by * the equation f(x,y,z) = cos(twopi*x) + cos(twopi*y) + cos(twopi*z) */ ibuf = (int64_t*)malloc(ncnt*sizeof(int64_t)); iptr = (int64_t**)malloc(ncnt*sizeof(int64_t*)); vbuf = (double*)malloc(ncnt*sizeof(double)); for (i=0; i 1.0e-5 && ncnt < iterations) { if (me==0) printf("Iteration: %d Tolerance: %e\n",(int)ncnt+1,tol); GA_Mask_sync(mask0,mask1); NGA_Sprs_array_matvec_multiply(s_a, g_p, g_t); //printf("p[%d] Got to 1\n",me); #if SYNCHED alpha = GA_Ddot(g_t,g_p); #else async_dot(d_tp,&alpha); #endif //printf("p[%d] Alpha1: %e\n",me,alpha); alpha = residual/alpha; //printf("p[%d] Alpha2: %e\n",me,alpha); GA_Mask_sync(mask0,mask1); GA_Add(&one_r,g_x,&alpha,g_p,g_x); alpha = -alpha; GA_Mask_sync(mask0,mask1); GA_Add(&one_r,g_r,&alpha,g_t,g_r); beta = residual; #if SYNCHED residual = GA_Ddot(g_r,g_r); #else async_dot(d_rr,&residual); #endif // if (me==0) printf("p[%d] Residual: %e\n",me,residual); #if SYNCHED GA_Norm_infinity(g_r, &tol); #else tol =residual/((double)cdim); #endif printf("p[%d] Tolerance: %f\n",me,tol); beta = residual/beta; GA_Add(&one_r,g_r,&beta,g_p,g_p); ncnt++; } /* if (me==0) printf("RHS Vector\n"); GA_Print(g_b); if (me==0) printf("Solution Vector\n"); GA_Print(g_x); */ if (ncnt == iterations) { if (me==0) printf("Solution failed to converge\n"); } else { if (me==0) printf("Solution converged\n"); } destroy_async_dot(d_tp); destroy_async_dot(d_rr); NGA_Destroy(g_r); NGA_Destroy(g_p); NGA_Destroy(g_t); #else /** * Based on algorithm described on page 136 in * Iterative Krylov Methods for Large Linear Systems, Henk A. van der Vorst, * Cambridge University Press, Cambridge, 2003. */ g_x = GA_Duplicate(g_b, "dup_x"); g_rt = GA_Duplicate(g_b, "dup_rt"); g_rm = GA_Duplicate(g_b, "dup_rm"); g_p = GA_Duplicate(g_b, "dup_p"); g_v = GA_Duplicate(g_b, "dup_v"); g_s = GA_Duplicate(g_b, "dup_s"); g_t = GA_Duplicate(g_b, "dup_t"); /* accumulate boundary values to right hand side vector */ if (me == 0) { printf("\nRight hand side vector completed. Starting\n"); printf("BiCG-STAB iterations.\n\n"); } /* Solve Laplace's equation using conjugate gradient method */ one_r = 1.0; m_one_r = -1.0; GA_Zero(g_x); /* Initial guess is zero, so Ax = 0 and r = b */ GA_Copy(g_b, g_rt); GA_Copy(g_b, g_rm); ncnt = 0; #if SYNCHED GA_Norm_infinity(g_rm, &tol); #else mask0 = 1; mask1 = 1; g_nrm = new_async_dot(g_rm,g_rm,&tol); async_norm_infinity(g_nrm, &tol); GA_Sync(); #endif if (me==0) printf("tol at 1: %f\n",tol); /* Start iteration loop */ while (tol > 1.0e-5 && ncnt < iterations) { if (me==0) printf("Iteration: %d Tolerance: %e\n",(int)ncnt+1,tol); #if 1 rho = GA_Ddot(g_rt,g_rm); #else #if SYNCHED rho = GA_Ddot(g_rt,g_rm); #else if (ncnt == 0) { d_rho = new_async_dot(g_rt,g_rm,&rho); } else { async_dot(d_rho,&rho); } #endif #endif if (me==0) printf("rho at 1: %f\n",rho); if (rho == 0.0) { GA_Error("BiCG-STAB method fails",0); } if (ncnt == 0) { GA_Copy(g_rm,g_p); } else { beta = (rho/rho_m)*(alpha/omega); m_omega = -omega; GA_Mask_sync(mask0,mask1); GA_Add(&one_r,g_p,&m_omega,g_v,g_p); GA_Mask_sync(mask0,mask1); GA_Add(&one_r,g_rm,&beta,g_p,g_p); } if (me==0) printf("rho at 1a: %f\n",rho); GA_Mask_sync(mask0,mask1); NGA_Sprs_array_matvec_multiply(s_a, g_p, g_v); if (me==0) printf("rho at 1b: %f\n",rho); #if 1 rv = GA_Ddot(g_rt,g_v); #else #if SYNCHED rv = GA_Ddot(g_rt,g_v); #else if (ncnt == 0) { d_rv = new_async_dot(g_rt,g_v,&rv); } else { async_dot(d_rv,&rv); } #endif #endif if (me==0) printf("rv at 1: %f\n",rv); alpha = -rho/rv; GA_Add(&one_r,g_rm,&alpha,g_v,g_s); alpha = -alpha; #if SYNCHED GA_Norm_infinity(g_s, &tol); #else if (ncnt == 0) { g_ns = new_async_dot(g_s,g_s,&tol); async_norm_infinity(g_ns, &tol); } else { async_norm_infinity(g_ns, &tol); } #endif if (me==0) printf("tol at 2: %f\n",tol); if (tol < 1.0e-05) { GA_Mask_sync(mask0,mask1); GA_Add(&one_r,g_x,&alpha,g_p,g_x); break; } GA_Mask_sync(mask0,mask1); NGA_Sprs_array_matvec_multiply(s_a, g_s, g_t); #if SYNCHED ts = GA_Ddot(g_t,g_s); tt = GA_Ddot(g_t,g_t); #else if (ncnt == 0) { d_ts = new_async_dot(g_t,g_s,&ts); d_tt = new_async_dot(g_t,g_t,&tt); } else { async_dot(d_ts,&ts); async_dot(d_tt,&tt); } #endif if (me==0) printf("ts at 1: %f\n",ts); if (me==0) printf("tt at 1: %f\n",tt); omega = ts/tt; m_omega = -omega; GA_Mask_sync(mask0,mask1); GA_Add(&one_r,g_x,&alpha,g_p,g_x); ts = GA_Ddot(g_x,g_x); if (me==0) printf("norm of x at 1: %f\n",ts); GA_Mask_sync(mask0,mask1); GA_Add(&one_r,g_x,&omega,g_s,g_x); ts = GA_Ddot(g_x,g_x); if (me==0) printf("norm of x at 2: %f\n",ts); GA_Mask_sync(mask0,mask1); GA_Add(&one_r,g_s,&m_omega,g_t,g_rm); ts = GA_Ddot(g_rm,g_rm); if (me==0) printf("norm of r at 1: %f\n",ts); #if SYNCHED GA_Norm_infinity(g_rm, &tol); #else async_norm_infinity(g_nrm, &tol); #endif if (me==0) printf("tol at 3: %f\n",tol); if (tol < 1.0e-05) break; if (omega == 0) { GA_Error("BiCG-STAB method cannot continue",0); } ncnt++; rho_m = rho; } destroy_async_dot(d_rho); destroy_async_dot(d_rv); destroy_async_dot(d_ts); destroy_async_dot(d_tt); destroy_async_dot(g_ns); destroy_async_dot(g_nrm); NGA_Destroy(g_x); NGA_Destroy(g_r); NGA_Destroy(g_rm); NGA_Destroy(g_p); NGA_Destroy(g_v); NGA_Destroy(g_s); NGA_Destroy(g_t); #endif /* Write solution to file */ #ifdef WRITE_VTK if (me == 0) { vbuf = (double*)malloc(xdim*ydim*sizeof(double)); PHI = fopen("phi.vtk","w"); fprintf(PHI,"# vtk DataFile Version 3.0\n"); fprintf(PHI,"Laplace Equation Solution\n"); fprintf(PHI,"ASCII\n"); fprintf(PHI,"DATASET STRUCTURED_POINTS\n"); fprintf(PHI,"DIMENSIONS %ld %ld %ld\n",xdim,ydim,zdim); fprintf(PHI,"ORIGIN %12.6f %12.6f %12.6f\n",0.5*h,0.5*h,0.5*h); fprintf(PHI,"SPACING %12.6f %12.6f %12.6f\n",h,h,h); fprintf(PHI," \n"); fprintf(PHI,"POINT_DATA %ld\n",xdim*ydim*zdim); fprintf(PHI,"SCALARS Phi float\n"); fprintf(PHI,"LOOKUP_TABLE default\n"); for (k=0; k #endif #include "mpi.h" #include "macdecls.h" #include "ga.h" #include "ga-mpi.h" #include "armci.h" #include "mp3.h" #define NDIM 1 int main(int argc, char **argv) { int me; int nproc; int status; int g_a; int dims[NDIM]; int chunk[NDIM]; int pg_world; size_t num = 10; double *p1 = NULL; double *p2 = NULL; size_t i; int num_mutex; int lo[1]; int hi[1]; int ld[1]={1}; MPI_Comm comm; MP_INIT(argc,argv); GA_INIT(argc,argv); me = GA_Nodeid(); nproc = GA_Nnodes(); comm = GA_MPI_Comm_pgroup_default(); printf("%d: Hello world!\n",me); if (me==0) printf("%d: GA_Initialize\n",me); /*if (me==0) printf("%d: ARMCI_Init\n",me);*/ /*ARMCI_Init();*/ /*if (me==0) printf("%d: MA_Init\n",me);*/ /*MA_init(MT_DBL, 8*1024*1024, 2*1024*1024);*/ if (me==0) printf("%d: GA_Create_handle\n",me); g_a = GA_Create_handle(); if (me==0) printf("%d: GA_Set_array_name\n",me); GA_Set_array_name(g_a,"test array A"); dims[0] = 30; if (me==0) printf("%d: GA_Set_data\n",me); GA_Set_data(g_a,NDIM,dims,MT_DBL); chunk[0] = -1; if (me==0) printf("%d: GA_Set_chunk\n",me); GA_Set_chunk(g_a,chunk); if (me==0) printf("%d: GA_Pgroup_get_world\n",me); pg_world = GA_Pgroup_get_world(); if (me==0) printf("%d: GA_Set_pgroup\n",me); GA_Set_pgroup(g_a,pg_world); if (me==0) printf("%d: GA_Allocate\n",me); status = GA_Allocate(g_a); if(0 == status) MPI_Abort(comm,100); if (me==0) printf("%d: GA_Zero\n",me); GA_Zero(g_a); if (me==0) printf("%d: GA_Sync\n",me); GA_Sync(); num = 10; p1 = malloc(num*sizeof(double)); /*double* p1 = ARMCI_Malloc_local(num*sizeof(double));*/ if (p1==NULL) MPI_Abort(comm,1000); p2 = malloc(num*sizeof(double)); /*double* p2 = ARMCI_Malloc_local(num*sizeof(double));*/ if (p2==NULL) MPI_Abort(comm,2000); for ( i=0 ; i #endif #if HAVE_MATH_H # include #endif #include "ga.h" #include "macdecls.h" #include "mp3.h" #define NDIM 8 /* dimension of matrices */ #define USEX_BLOCK_CYCLIC /* Test the functions in matrix.c on double precision arrays*/ void do_work() { int ONE=1, TWO=2; /* useful constants */ double RONE = 1.0; int g_a, g_v; int me=GA_Nodeid(), nproc=GA_Nnodes(); int i, j, m, n, ndim, ld, ok; int dims[2]; int lo[2], hi[2]; int count; double a[NDIM*NDIM]; double b[NDIM*NDIM]; double va[NDIM]; double vb[NDIM]; #ifdef USE_BLOCK_CYCLIC int block_size[2]; #endif /* Test vectors */ ndim = ONE; dims[0] = NDIM; dims[1] = NDIM; lo[0] = 0; lo[1] = 0; hi[0] = NDIM-1; hi[1] = NDIM-1; ld = NDIM; /* Create vector */ g_v = NGA_Create_handle(); NGA_Set_data(g_v, ndim, dims, C_DBL); #ifdef USE_BLOCK_CYCLIC block_size[0] = 17; block_size[1] = 17; NGA_Set_block_cyclic(g_v, block_size); #endif NGA_Allocate(g_v); /* Create matrix */ ndim = TWO; g_a = NGA_Create_handle(); NGA_Set_data(g_a, ndim, dims, C_DBL); #ifdef USE_BLOCK_CYCLIC NGA_Set_block_cyclic(g_a, block_size); #endif NGA_Allocate(g_a); /* Initialize data in local buffer */ for (i=0; i column merging [',i3,':',i3,',', $ i3,':',i3,'] ') call ffflush(6) endif else c > 2 ilo = 1 ihi = trial*eps jlo = 1 jhi = trial*eps if(cluster.eq.0)then call ga_dfill_patch(g_a, ilo,ihi,jlo,jhi,0d0) ilo = 1 ihi = n jlo = 1 jhi = n else call ga_zero(g_a) call ga_copy_patch('n', g_b,ilo,ihi,jlo,jhi, $ g_a,ilo,ihi,jlo,jhi) endif c if (me .eq. 0) then write(6,101)trial, ilo,ihi,jlo,jhi 101 format(i2,'> patch merging [',i3,':',i3,',', $ i3,':',i3,'] ') call ffflush(6) endif endif c call ga_net_merge(g_a, ilo, ihi, jlo, jhi) c call ga_print(g_a,1) c c*** check if correct do j = 1+ga_nodeid(), n, ga_nnodes() call ga_get(g_a,1,n,j,j,buf,n) do i = 1, n if(a(i,j) .ne. buf(i)) then print *, me, '(',i,j,')', a(i,j), buf(i) call ga_error("merging failed",j) endif enddo enddo if (me .eq. 0) then write(6,*)' OK ' call ffflush(6) endif c if(trial .eq.1) then c g_b keeps reference data for next trials call ga_copy(g_a,g_b) endif enddo c status = ga_destroy(g_a) c end ga-5.9.2/global/testing/mir_perf1.F000066400000000000000000000366411500715745200170460ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c $Id: mir_perf1.F,v 1.1 2003-02-26 15:33:38 d3g293 Exp $ c------------------------------------------------------------------------ c Program perf.x is used to test performance of GA put, get, accumulate | c It has to be executed on four processors. | c remote operations access data on processes 1,2,3 in the round-robin way| c------------------------------------------------------------------------ c program perf implicit none #include "mafdecls.fh" #include "global.fh" integer heap c c*** Intitialize a message passing library c #include "mp3.fh" c c*** Intitialize the GA package call ga_initialize() if(ga_nnodes().ne.4 .and. ga_nodeid().eq.0) $ call ga_error('Program requires 4 GA processes',ga_nnodes()) c c*** Initialize the MA package heap = 900000 if (.not. ma_init(MT_DBL, heap,heap)) $ call ga_error('ma init failed',2*heap) c c call test2D() call test1D() call ga_terminate() c call MP_FINALIZE() end subroutine test1D() implicit none #include "mafdecls.fh" #include "global.fh" c c integer n, nn, num_chunks parameter (n = 1024*1024, nn = n/4, num_chunks=16) double precision buf(nn) c integer g_a integer ilo, ihi, jlo, jhi integer nproc, me, loop, p_handle integer ndim, dims(2), chunks(2) integer chunk(num_chunks) data chunk /1,9,16,81,256,576,900,2304,4096,8281, $ 16384,29241,65536,124609,193600,262144/ c nproc = ga_nnodes() me = ga_nodeid() p_handle = ga_pgroup_get_mirror() ndim = 2 dims(1) = n dims(2) = 1 chunks(1) = 0 chunks(2) = 0 c c*** Create global array if (.not. nga_create_config(MT_DBL, ndim, dims, 'a', chunks, + p_handle, g_a)) $ call ga_error(' ga_create failed ',1) c do loop=1,nn buf(loop) = .01d0 enddo call ga_zero(g_a) c if (me .eq. 0) then write(*,*)' ' write(*,*)' ' write(*,*)' ' write(*,55)n 55 format(' Performance of GA get, put & acc', $ ' for 1-dimensional sections of array[',i7,']') print *,' ' endif c c do loop=1,2 c c*** local ops c call ga_distribution(g_a, me, ilo, ihi, jlo, jhi) call TestPutGetAcc1 & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo, jhi, .true.) c c*** remote ops c call TestPutGetAcc1 & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo, jhi,.false.) c enddo end subroutine TestPutGetAcc1 & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo,jhi, local) implicit none #include "global.fh" #include "testutil.fh" c integer num_chunks, chunk(num_chunks) integer n, ilo, ihi, jlo,jhi,g_a integer inode, iproc, zero double precision buf(*), tg, tp, ta double precision time_acc1, time_get1, time_put1 logical local c integer me integer loop, jump, count, bytes c zero = 0 me = ga_nodeid() inode = ga_cluster_nodeid() iproc = me - ga_cluster_procid(inode,zero) if (me .eq. 0) then write(6,*)' ' if(local) then write(6,'(26X, 25HLocal 1-D Array Section )') else write(6,'(26X, 25HRemote 1-D Array Section )') endif write(6,*)' section get put', & ' accumulate' write(6,*)' bytes dim sec MB/s sec MB/s', & ' sec MB/s' call flush(6) endif call ga_sync() c do loop = 1, num_chunks bytes = util_mdtob(1)*chunk(loop) ! how much data is accessed jump = n/(6000*loop) ! jump distance between consecutive patches if(loop.eq.num_chunks)jump=0 c c everybody touches own data c this is a kind of klugy way of filling up local data, but it works c call ga_fill_patch(g_a, 1, n, 1, 1 , 1d0*me*loop) if (iproc.eq.0) then tg=time_get1(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, 1 , 1d0*me*loop) if (iproc.eq.0) then tp=time_put1(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, 1 , 1d0*me*loop) if (iproc.eq.0) then ta=time_acc1(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c if (me .eq. 0) then write(6,77)bytes, chunk(loop), tg, & 1d-6*bytes/tg,tp, 1d-6*bytes/tp, ta, 1d-6*bytes/ta call flush(6) endif enddo c 77 format(i7, i7,1x, 3(1x,d8.3,1x,d8.3)) end double precision function & time_acc1(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je logical local integer rows, indx, shifti c integer ilo, ihi, jlo, jhi double precision seconds, buf c count = 0 rows = ie - is + 1 shifti = rows jlo = js jhi = je seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 count = count + 1 if (local) then call ga_acc(g_a, ilo, ihi, jlo, jhi, buf, chunk, 1d0) else call ga_acc(g_a, ilo+shifti, ihi+shifti, $ jlo, jhi, buf, chunk, 1d0) endif enddo seconds = util_timer() - seconds c time_acc1 = seconds/count end double precision function & time_get1(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je integer rows, indx, shifti logical local c integer ilo, ihi, jlo, jhi double precision seconds, buf c count = 0 rows = ie - is + 1 shifti = rows jlo = js jhi = je seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 count = count + 1 if (local) then call ga_get(g_a, ilo, ihi, jlo, jhi, buf, chunk) else call ga_get(g_a, ilo+shifti, ihi+shifti, $ jlo, jhi, buf, chunk) endif enddo seconds = util_timer() - seconds c time_get1 = seconds/count end double precision function & time_put1(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je integer rows, indx, shifti logical local c integer ilo, ihi, jlo, jhi double precision seconds, buf c count = 0 rows = ie - is + 1 shifti = rows jlo = js jhi = je seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 count = count + 1 if (local) then call ga_put(g_a, ilo, ihi, jlo, jhi, buf, chunk) else call ga_put(g_a, ilo+shifti, ihi+shifti, $ jlo, jhi, buf, chunk) endif enddo seconds = util_timer() - seconds c time_put1 = seconds/count end c c test for square patches c subroutine test2D() implicit none #include "mafdecls.fh" #include "global.fh" c integer n, nn, num_chunks parameter (n = 1024, nn = n*n/4, num_chunks=16) double precision buf(nn) c integer g_a integer ilo, ihi, jlo, jhi integer nproc, me, loop integer chunk(num_chunks) data chunk /1,3,4,9,16,24,30,48,64,91,128,171,256,353,440,512/ c nproc = ga_nnodes() me = ga_nodeid() c c*** Create global array if (.not. ga_create(MT_DBL, n, n, 'a', 0, 0, g_a)) $ call ga_error(' ga_create failed ',1) c do loop=1,nn buf(loop) = .01d0 enddo call ga_zero(g_a) c if (me .eq. 0) then write(*,*)' ' write(*,55)n,n 55 format(' Performance of GA get, put & acc', $ ' for square sections of array[',i4,',',i4,']') print *,' ' endif c c do loop=1,2 c c*** local ops c call ga_distribution(g_a, me, ilo, ihi, jlo, jhi) call TestPutGetAcc & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo, jhi, .true.) c c*** remote ops c call TestPutGetAcc & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo, jhi,.false.) c enddo end subroutine TestPutGetAcc & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo,jhi, local) implicit none #include "global.fh" #include "testutil.fh" c integer num_chunks, chunk(num_chunks) integer n, ilo, ihi, jlo,jhi,g_a double precision buf(*), tg, tp, ta double precision time_acc, time_get, time_put logical local c integer me integer loop, jump, count, bytes c me = ga_nodeid() if (me .eq. 0) then write(6,*)' ' if(local) then write(6,'(26X, 25HLocal 2-D Array Section )') else write(6,'(26X, 25HRemote 2-D Array Section )') endif write(6,*)' section get put', & ' accumulate' write(6,*)' bytes dim sec MB/s sec MB/s', & ' sec MB/s' call flush(6) endif call ga_sync() c do loop = 1, num_chunks bytes = util_mdtob(1)*chunk(loop)*chunk(loop) !how much data is accessed jump = n/(60*loop) ! jump distance between consecutive patches if(loop.eq.num_chunks)jump=0 c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, n , 1d0*me*loop) if (me .eq. 0) then tg=time_get(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, n , 1d0*me*loop) if (me .eq. 0) then tp=time_put(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, n , 1d0*me*loop) if (me .eq. 0) then ta=time_acc(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c if (me .eq. 0) then write(6,77)bytes, chunk(loop), tg, & 1d-6*bytes/tg,tp, 1d-6*bytes/tp, ta, 1d-6*bytes/ta call flush(6) endif enddo c 77 format(i7, i7,1x, 3(1x,d8.3,1x,d8.3)) end double precision function & time_acc(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je logical local integer rows, cols, indx, shifti(3), shiftj(3) c integer ilo, ihi, jlo, jhi double precision seconds, buf c count = 0 rows = ie - is + 1 cols = je - js + 1 shifti(1) = rows shifti(2) = 0 shifti(3) = rows shiftj(1) = 0 shiftj(2) = cols shiftj(3) = cols seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 do jlo = js, je -chunk-jump +1, chunk+jump jhi = jlo + chunk -1 count = count + 1 if (local) then call ga_acc(g_a, ilo, ihi, jlo, jhi, buf, chunk, 1d0) else indx = Mod(count,3) + 1 call ga_acc(g_a, ilo+shifti(indx), ihi+shifti(indx), $ jlo+shiftj(indx), jhi+shiftj(indx), $ buf, chunk, 1d0) endif enddo enddo seconds = util_timer() - seconds c time_acc = seconds/count end double precision function & time_get(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je integer rows, cols, indx, shifti(3), shiftj(3) logical local c integer ilo, ihi, jlo, jhi double precision seconds, buf c count = 0 rows = ie - is + 1 cols = je - js + 1 shifti(1) = rows shifti(2) = 0 shifti(3) = rows shiftj(1) = 0 shiftj(2) = cols shiftj(3) = cols seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 do jlo = js, je -chunk-jump +1, chunk+jump jhi = jlo + chunk -1 count = count + 1 if (local) then call ga_get(g_a, ilo, ihi, jlo, jhi, buf, chunk) else indx = Mod(count,3) + 1 call ga_get(g_a, ilo+shifti(indx), ihi+shifti(indx), $ jlo+shiftj(indx), jhi+shiftj(indx), $ buf, chunk) endif enddo enddo seconds = util_timer() - seconds c time_get = seconds/count end double precision function & time_put(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je integer rows, cols, indx, shifti(3), shiftj(3) logical local c integer ilo, ihi, jlo, jhi double precision seconds, buf c count = 0 rows = ie - is + 1 cols = je - js + 1 shifti(1) = rows shifti(2) = 0 shifti(3) = rows shiftj(1) = 0 shiftj(2) = cols shiftj(3) = cols seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 do jlo = js, je -chunk-jump +1, chunk+jump jhi = jlo + chunk -1 count = count + 1 if (local) then call ga_put(g_a, ilo, ihi, jlo, jhi, buf, chunk) else indx = Mod(count,3) + 1 call ga_put(g_a, ilo+shifti(indx), ihi+shifti(indx), $ jlo+shiftj(indx), jhi+shiftj(indx), $ buf, chunk) endif enddo enddo seconds = util_timer() - seconds c time_put = seconds/count end ga-5.9.2/global/testing/mir_perf2.F000066400000000000000000000360651500715745200170470ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c $Id: mir_perf2.F,v 1.1 2003-02-26 15:33:38 d3g293 Exp $ c------------------------------------------------------------------------ c Program perf.x is used to test performance of GA put, get, accumulate | c It has to be executed on four processors. | c remote operations access data on processes 1,2,3 in the round-robin way| c------------------------------------------------------------------------ c program perf implicit none #include "mafdecls.fh" #include "global.fh" integer heap c c*** Intitialize a message passing library c #include "mp3.fh" c c*** Intitialize the GA package call ga_initialize() if(ga_nnodes().ne.4 .and. ga_nodeid().eq.0) $ call ga_error('Program requires 4 GA processes',ga_nnodes()) c c*** Initialize the MA package heap = 900000 if (.not. ma_init(MT_DBL, heap,heap)) $ call ga_error('ma init failed',2*heap) c c call test2D() call test1D() c if(ga_nodeid().eq.0) print *,'All tests successful ' c call ga_terminate() c call MP_FINALIZE() end subroutine test1D() implicit none #include "mafdecls.fh" #include "global.fh" c c integer n, nn, num_chunks parameter (n = 1024*1024, nn = n/4, num_chunks=16) double precision buf(nn) c integer g_a integer ilo, ihi, jlo, jhi integer nproc, me, loop integer chunk(num_chunks) data chunk /1,9,16,81,256,576,900,2304,4096,8281, $ 16384,29241,65536,124609,193600,262144/ c nproc = ga_nnodes() me = ga_nodeid() c c*** Create global array if (.not. ga_create(MT_DBL, n, 1, 'a', 0, 0, g_a)) $ call ga_error(' ga_create failed ',1) c do loop=1,nn buf(loop) = .01d0 enddo call ga_zero(g_a) c if (me .eq. 0) then write(*,*)' ' write(*,*)' ' write(*,*)' ' write(*,55)n 55 format(' Performance of GA get, put & acc', $ ' for 1-dimensional sections of array[',i7,']') print *,' ' endif c c do loop=1,2 c c*** local ops c call ga_distribution(g_a, me, ilo, ihi, jlo, jhi) call TestPutGetAcc1 & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo, jhi, .true.) c c*** remote ops c call TestPutGetAcc1 & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo, jhi,.false.) c enddo end subroutine TestPutGetAcc1 & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo,jhi, local) implicit none #include "global.fh" #include "testutil.fh" c integer num_chunks, chunk(num_chunks) integer n, ilo, ihi, jlo,jhi,g_a double precision buf(*), tg, tp, ta double precision time_acc1, time_get1, time_put1 logical local c integer me integer loop, jump, count, bytes c me = ga_nodeid() if (me .eq. 0) then write(6,*)' ' if(local) then write(6,'(26X, 25HLocal 1-D Array Section )') else write(6,'(26X, 25HRemote 1-D Array Section )') endif write(6,*)' section get put', & ' accumulate' write(6,*)' bytes dim sec MB/s sec MB/s', & ' sec MB/s' call flush(6) endif call ga_sync() c do loop = 1, num_chunks bytes = util_mdtob(1)*chunk(loop) ! how much data is accessed jump = n/(6000*loop) ! jump distance between consecutive patches if(loop.eq.num_chunks)jump=0 c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, 1 , 1d0*me*loop) if (me .eq. 0) then tg=time_get1(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, 1 , 1d0*me*loop) if (me .eq. 0) then tp=time_put1(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, 1 , 1d0*me*loop) if (me .eq. 0) then ta=time_acc1(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c if (me .eq. 0) then write(6,77)bytes, chunk(loop), tg, & 1d-6*bytes/tg,tp, 1d-6*bytes/tp, ta, 1d-6*bytes/ta call flush(6) endif enddo c 77 format(i7, i7,1x, 3(1x,d8.3,1x,d8.3)) end double precision function & time_acc1(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je logical local integer rows, indx, shifti c integer ilo, ihi, jlo, jhi double precision seconds, buf(*) c count = 0 rows = ie - is + 1 shifti = 2*rows jlo = js jhi = je seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 count = count + 1 if (local) then call ga_acc(g_a, ilo, ihi, jlo, jhi, buf, chunk, 1d0) else call ga_acc(g_a, ilo+shifti, ihi+shifti, $ jlo, jhi, buf, chunk, 1d0) endif enddo seconds = util_timer() - seconds c time_acc1 = seconds/count end double precision function & time_get1(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je integer rows, indx, shifti logical local c integer ilo, ihi, jlo, jhi double precision seconds, buf(*) c count = 0 rows = ie - is + 1 shifti = 2*rows jlo = js jhi = je seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 count = count + 1 if (local) then call ga_get(g_a, ilo, ihi, jlo, jhi, buf, chunk) else call ga_get(g_a, ilo+shifti, ihi+shifti, $ jlo, jhi, buf, chunk) endif enddo seconds = util_timer() - seconds c time_get1 = seconds/count end double precision function & time_put1(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je integer rows, indx, shifti logical local c integer ilo, ihi, jlo, jhi double precision seconds, buf(*) c count = 0 rows = ie - is + 1 shifti = 2*rows jlo = js jhi = je seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 count = count + 1 if (local) then call ga_put(g_a, ilo, ihi, jlo, jhi, buf, chunk) else call ga_put(g_a, ilo+shifti, ihi+shifti, $ jlo, jhi, buf, chunk) endif enddo seconds = util_timer() - seconds c time_put1 = seconds/count end c c test for square patches c subroutine test2D() implicit none #include "mafdecls.fh" #include "global.fh" c integer n, nn, num_chunks parameter (n = 1024, nn = n*n/4, num_chunks=16) double precision buf(nn) c integer g_a integer ilo, ihi, jlo, jhi integer nproc, me, loop integer chunk(num_chunks) data chunk /1,3,4,9,16,24,30,48,64,91,128,171,256,353,440,512/ c nproc = ga_nnodes() me = ga_nodeid() c c*** Create global array if (.not. ga_create(MT_DBL, n, n, 'a', 0, 0, g_a)) $ call ga_error(' ga_create failed ',1) c do loop=1,nn buf(loop) = .01d0 enddo call ga_zero(g_a) c if (me .eq. 0) then write(*,*)' ' write(*,55)n,n 55 format(' Performance of GA get, put & acc', $ ' for square sections of array[',i4,',',i4,']') print *,' ' endif c c do loop=1,2 c c*** local ops c call ga_distribution(g_a, me, ilo, ihi, jlo, jhi) call TestPutGetAcc & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo, jhi, .true.) c c*** remote ops c call TestPutGetAcc & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo, jhi,.false.) c enddo end subroutine TestPutGetAcc & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo,jhi, local) implicit none #include "global.fh" #include "testutil.fh" c integer num_chunks, chunk(num_chunks) integer n, ilo, ihi, jlo,jhi,g_a double precision buf(*), tg, tp, ta double precision time_acc, time_get, time_put logical local c integer me integer loop, jump, count, bytes c me = ga_nodeid() if (me .eq. 0) then write(6,*)' ' if(local) then write(6,'(26X, 25HLocal 2-D Array Section )') else write(6,'(26X, 25HRemote 2-D Array Section )') endif write(6,*)' section get put', & ' accumulate' write(6,*)' bytes dim sec MB/s sec MB/s', & ' sec MB/s' call flush(6) endif call ga_sync() c do loop = 1, num_chunks bytes = util_mdtob(1)*chunk(loop)*chunk(loop) !how much data is accessed jump = n/(60*loop) ! jump distance between consecutive patches if(loop.eq.num_chunks)jump=0 c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, n , 1d0*me*loop) if (me .eq. 0) then tg=time_get(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, n , 1d0*me*loop) if (me .eq. 0) then tp=time_put(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, n , 1d0*me*loop) if (me .eq. 0) then ta=time_acc(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c if (me .eq. 0) then write(6,77)bytes, chunk(loop), tg, & 1d-6*bytes/tg,tp, 1d-6*bytes/tp, ta, 1d-6*bytes/ta call flush(6) endif enddo c 77 format(i7, i7,1x, 3(1x,d8.3,1x,d8.3)) end double precision function & time_acc(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je logical local integer rows, cols, indx, shifti(3), shiftj(3) c integer ilo, ihi, jlo, jhi double precision seconds, buf(*) c count = 0 rows = ie - is + 1 cols = je - js + 1 shifti(1) = rows shifti(2) = 0 shifti(3) = rows shiftj(1) = 0 shiftj(2) = cols shiftj(3) = cols seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 do jlo = js, je -chunk-jump +1, chunk+jump jhi = jlo + chunk -1 count = count + 1 if (local) then call ga_acc(g_a, ilo, ihi, jlo, jhi, buf, chunk, 1d0) else indx = Mod(count,3) + 1 call ga_acc(g_a, ilo+shifti(indx), ihi+shifti(indx), $ jlo+shiftj(indx), jhi+shiftj(indx), $ buf, chunk, 1d0) endif enddo enddo seconds = util_timer() - seconds c time_acc = seconds/count end double precision function & time_get(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je integer rows, cols, indx, shifti(3), shiftj(3) logical local c integer ilo, ihi, jlo, jhi double precision seconds, buf(*) c count = 0 rows = ie - is + 1 cols = je - js + 1 shifti(1) = rows shifti(2) = 0 shifti(3) = rows shiftj(1) = 0 shiftj(2) = cols shiftj(3) = cols seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 do jlo = js, je -chunk-jump +1, chunk+jump jhi = jlo + chunk -1 count = count + 1 if (local) then call ga_get(g_a, ilo, ihi, jlo, jhi, buf, chunk) else indx = Mod(count,3) + 1 call ga_get(g_a, ilo+shifti(indx), ihi+shifti(indx), $ jlo+shiftj(indx), jhi+shiftj(indx), $ buf, chunk) endif enddo enddo seconds = util_timer() - seconds c time_get = seconds/count end double precision function & time_put(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je integer rows, cols, indx, shifti(3), shiftj(3) logical local c integer ilo, ihi, jlo, jhi double precision seconds, buf(*) c count = 0 rows = ie - is + 1 cols = je - js + 1 shifti(1) = rows shifti(2) = 0 shifti(3) = rows shiftj(1) = 0 shiftj(2) = cols shiftj(3) = cols seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 do jlo = js, je -chunk-jump +1, chunk+jump jhi = jlo + chunk -1 count = count + 1 if (local) then call ga_put(g_a, ilo, ihi, jlo, jhi, buf, chunk) else indx = Mod(count,3) + 1 call ga_put(g_a, ilo+shifti(indx), ihi+shifti(indx), $ jlo+shiftj(indx), jhi+shiftj(indx), $ buf, chunk) endif enddo enddo seconds = util_timer() - seconds c time_put = seconds/count end ga-5.9.2/global/testing/mmatrix.F000066400000000000000000000316761500715745200166460ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c vector boxes lack arithmetic precision # define THRESH 1d-13 # define THRESHF 1e-5 #define MISMATCH(x,y) abs(x-y)/max(1d0,abs(x)).gt.THRESH #define MISMATCHF(x,y) abs(x-y)/max(1.0,abs(x)).gt.THRESHF program main implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer heap, stack, fudge, ma_heap, me integer nmax, DIM, nwidth, MAXPROC, nloop parameter (nmax = 1025, DIM = 2, MAXPROC = 2000) parameter (nloop = 50) integer ndim, nprocs, tprocs, pdims(7), type, dcnt, maxval integer g_a, g_b, g_c, g_aa, g_bb, g_cc, iproc integer inode, tnodes, p_m, ix, iy, xinc, yinc integer i, j, k, l, ii, jj, dims(7), mdim, ndims(7) integer lo(7), hi(7), ld(7) integer lo2(7), hi2(7) integer lo3(7), hi3(7) integer lo4(7), hi4(7) integer lo5(7), hi5(7) integer chunk(7), imax, jmax, iloop, afail, bfail, cfail double precision a(nmax, nmax), b(nmax,nmax), c(nmax,nmax) double precision ta(nmax, nmax), tb(nmax,nmax),tc(nmax,nmax) double precision tat(nmax, nmax) double precision start,start1,t1,t2,t3,t4,t5,t6,t7 double precision alpha, beta logical status, ltest parameter (heap=400*400*4, fudge=100, stack=400*400*4) c c*** Intitialize a message passing library c #include "mp3.fh" mdim = nmax c c*** Initialize GA c c There are 2 choices: ga_initialize or ga_initialize_ltd. c In the first case, there is no explicit limit on memory usage. c In the second, user can set limit (per processor) in bytes. c call ga_initialize() me = ga_nodeid() c we can also use GA_set_memory_limit BEFORE first ga_create call c ma_heap = heap + fudge c call GA_set_memory_limit(util_mdtob(ma_heap)) c if(ga_nodeid().eq.0)then print *,' GA initialized ' call ffflush(6) endif c c*** Initialize the MA package c MA must be initialized before any global array is allocated c status = ma_init(MT_DBL, stack, ma_heap) if (.not. status) call ga_error('ma_init failed',-1) c if(me.eq.0)then print *, 'using ', ga_nnodes(), ' process(es)' call ffflush(6) endif c c Test matrix multiply for mirrored arrays c ndim = 2 c c processor distribution on node c inode = ga_cluster_nodeid() nprocs = ga_cluster_nprocs(inode) tnodes = ga_cluster_nnodes() tprocs = ga_nnodes() iproc = mod(me,nprocs) call factor(tnodes,ndim,ndims) call factor(nprocs,ndim,pdims) c if (me.eq.0) then write(6,*) '*' write(6,*) '* Number of processors: ',tprocs write(6,*) '* Number of processors/node : ',nprocs write(6,*) '* Matrix dimension : ',mdim write(6,*) '*' endif c c create global arrays c do i = 1, ndim chunk(i) = 0 dims(i) = mdim lo(i) = 1 hi(i) = mdim ld(i) = nmax end do type = MT_DBL p_m = ga_pgroup_get_mirror() status = nga_create(type, ndim, dims,"aa", chunk, g_aa) if (status.and.me.eq.0) then write(6,*) '*' write(6,*) '* Global array AA creation was successful' write(6,*) '*' elseif (.not.status) then write(6,*) 'Global array creation failure on ',me endif status = nga_create(type, ndim, dims,"bb", chunk, g_bb) if (status.and.me.eq.0) then write(6,*) '*' write(6,*) '* Global array BB creation was successful' write(6,*) '*' elseif (.not.status) then write(6,*) 'Global array creation failure on ',me endif status = nga_create(type, ndim, dims,"cc", chunk, g_cc) if (status.and.me.eq.0) then write(6,*) '*' write(6,*) '* Global array CC creation was successful' write(6,*) '*' elseif (.not.status) then write(6,*) 'Global array creation failure on ',me endif #define MIRROR 1 #if MIRROR status = nga_create_config(type, ndim, dims,"a", chunk, p_m, g_a) if (status.and.me.eq.0) then write(6,*) '*' write(6,*) '* Global array A creation was successful' write(6,*) '*' elseif (.not.status) then write(6,*) 'Global array creation failure on ',me endif status = nga_create_config(type, ndim, dims,"b", chunk, p_m, g_b) if (status.and.me.eq.0) then write(6,*) '*' write(6,*) '* Global array B creation was successful' write(6,*) '*' elseif (.not.status) then write(6,*) 'Global array creation failure on ',me endif status = nga_create_config(type, ndim, dims,"c", chunk, p_m, g_c) if (status.and.me.eq.0) then write(6,*) '*' write(6,*) '* Global array C creation was successful' write(6,*) '*' elseif (.not.status) then write(6,*) 'Global array creation failure on ',me endif #endif c c initialize a and b matrices c k = 0 l = 7 do j = 1, mdim do i = 1, mdim k = k+1 k = mod(k,29) a(i,j) = dble(k) l = l + 1 l = mod(l,37) b(i,j) = dble(l) end do end do c c initialize global arrays c #if MIRROR if (iproc.eq.0) then call nga_put(g_a,lo,hi,a,ld) call nga_put(g_b,lo,hi,b,ld) endif call ga_zero(g_c) #endif if (me.eq.0) then call nga_put(g_aa,lo,hi,a,ld) call nga_put(g_bb,lo,hi,b,ld) endif call ga_zero(g_cc) if (me.eq.0) then write(6,*) '*' write(6,*) '* Initialized global arrays with data' write(6,*) '*' endif call ga_sync() c c work out which chunk of matrix c this node is responsible c for c t1 = 0.0d00 t2 = 0.0d00 t3 = 0.0d00 t4 = 0.0d00 t5 = 0.0d00 t6 = 0.0d00 t7 = 0.0d00 do iloop = 1, nloop call ga_zero(g_cc) #if MIRROR call ga_zero(g_c) start = util_timer() start1 = util_timer() ix = mod(inode,ndims(1)) iy = (inode - ix)/ndims(1) xinc = dims(1)/ndims(1) yinc = dims(2)/ndims(2) lo2(1) = ix*xinc+1 lo2(2) = iy*yinc+1 if (ix.ne.ndims(1)-1) then hi2(1) = (ix+1)*xinc else hi2(1) = dims(1) endif if (iy.ne.ndims(2)-1) then hi2(2) = (iy+1)*yinc else hi2(2) = dims(2) endif c c work out which chunk of matrix c this processor is responsible c for c ix = mod(iproc,pdims(1)) iy = (iproc - ix)/(hi2(1)-lo2(1)+1) xinc = (hi2(1)-lo2(1)+1)/pdims(1) yinc = (hi2(2)-lo2(2)+1)/pdims(2) lo3(1) = ix*xinc+lo2(1) lo3(2) = iy*yinc+lo2(2) if (ix.ne.pdims(1)-1) then hi3(1) = (ix+1)*xinc+lo2(1)-1 else hi3(1) = hi2(1) endif if (iy.ne.pdims(2)-1) then hi3(2) = (iy+1)*yinc+lo2(2)-1 else hi3(2) = hi2(2) endif t3 = t3 + util_timer() - start1 c start1 = util_timer() #define DGEMM2 1 #if (DGEMM2) lo4(1) = lo2(1) hi4(1) = hi2(1) lo4(2) = 1 hi4(2) = dims(2) lo5(1) = 1 hi5(1) = dims(1) lo5(2) = lo2(2) hi5(2) = hi2(2) c c perform matrix patch multiplies c alpha = 1.0 beta = 0.0 start1 = util_timer() call ga_matmul_patch('n','n',alpha,beta, + g_a,lo4(1),hi4(1),lo4(2),hi4(2), + g_b,lo5(1),hi5(1),lo5(2),hi5(2), + g_c,lo2(1),hi2(1),lo2(2),hi2(2)) t5 = t5 + util_timer() - start1 #else lo4(1) = lo3(1) hi4(1) = hi3(1) lo4(2) = 1 hi4(2) = dims(2) call nga_get(g_a,lo4,hi4,ta,ld) lo4(1) = 1 hi4(1) = dims(1) lo4(2) = lo3(2) hi4(2) = hi3(2) call nga_get(g_b,lo4,hi4,tb,ld) t4 = t4 + util_timer() - start1 start1 = util_timer() imax = hi3(1) - lo3(1) + 1 do i = 1, imax do j = 1, mdim tat(j,i) = ta(i,j) end do end do imax = hi3(1) - lo3(1) + 1 jmax = hi3(2) - lo3(2) + 1 do j = 1, jmax do i = 1, imax tc(i,j) = 0.0d00 do k = 1, mdim tc(i,j) = tc(i,j) + tat(k,i)*tb(k,j) end do end do end do t5 = t5 + util_timer() - start1 start1 = util_timer() call nga_put(g_c,lo3,hi3,tc,ld) t6 = t6 + util_timer() - start1 #endif call ga_mask_sync(.true.,.false.) start1 = util_timer() call ga_merge_mirrored(g_c) t7 = t7 + util_timer() - start1 t1 = t1 + util_timer() - start #else do i = 1, mdim do j = 1, mdim tat(j,i) = a(i,j) end do end do do i = 1, mdim do j = 1, mdim c(i,j) = 0.0d00 do k = 1, mdim c(i,j) = c(i,j) + tat(k,i)*b(k,j) end do end do end do #endif c c do a conventional ga matrix multiply c alpha = 1.0 beta = 0.0 call ga_sync() start = util_timer() #define DGEMM 1 #if DGEMM call ga_dgemm('n','n',mdim,mdim,mdim,alpha,g_aa,g_bb,beta,g_cc) #else do i = 1, mdim do j = 1, mdim tat(j,i) = a(i,j) end do end do do i = 1, mdim do j = 1, mdim tc(i,j) = 0.0d00 do k = 1, mdim tc(i,j) = tc(i,j) + tat(k,i)*b(k,j) end do end do end do #endif t2 = t2 + util_timer() - start call ga_sync() c c check to see if matrix operations agree c #if MIRROR call nga_get(g_c,lo,hi,c,ld) #endif #if DGEMM call nga_get(g_cc,lo,hi,tc,ld) #endif call ga_sync() ltest = .true. afail = 0 bfail = 0 cfail = 0 do i = 1, mdim do j = 1, mdim if (c(i,j).ne.tc(i,j)) then cfail = cfail+1 ltest = .false. if (c(i,j).eq.0.0d00) then afail = afail + 1 endif if (tc(i,j).eq.0.0d00) then bfail = bfail + 1 endif end if end do end do if (ltest) then if (me.eq.0) write(6,*) 'Matrices agree for iteration ',iloop else if (me.eq.0) then write(6,*) 'Matrices disagree for iteration',iloop write(6,*) ' Total number of disagreements',cfail write(6,*) ' Total number of zeros in mirrored array',afail write(6,*) ' Total number of zeros in dgemm array',bfail endif endif end do c call ga_dgop(MT_DBL,t1,1,'+') call ga_dgop(MT_DBL,t2,1,'+') call ga_dgop(MT_DBL,t3,1,'+') call ga_dgop(MT_DBL,t4,1,'+') call ga_dgop(MT_DBL,t5,1,'+') call ga_dgop(MT_DBL,t6,1,'+') call ga_dgop(MT_DBL,t7,1,'+') t1 = t1/dble(nloop*tprocs) t2 = t2/dble(nloop*tprocs) t3 = t3/dble(nloop*tprocs) t4 = t4/dble(nloop*tprocs) t5 = t5/dble(nloop*tprocs) t6 = t6/dble(nloop*tprocs) t7 = t7/dble(nloop*tprocs) if (me.eq.0) then write(6,*) 'Elapsed time for Mirrored matrix multiply ',t1 write(6,*) ' Elapsed time for initial setup ',t3 write(6,*) ' Elapsed time for copy to buffers ',t4 write(6,*) ' Elapsed time for partial multiply ',t5 write(6,*) ' Elapsed time for copy from buffer',t6 write(6,*) ' Elapsed time for merge',t7 write(6,*) 'Elapsed time for Conventional matrix multiply ',t2 endif c c*** Tidy up the GA package c if(ga_nodeid().eq.0)print *,' All tests successful' c call ga_terminate() c c*** Tidy up after message-passing library c call MP_FINALIZE() c stop end c subroutine factor(p,ndim,dims) implicit none integer i,j,p,ndim,dims(7),imin,mdim integer ip,ifac,pmax,prime(1000) integer fac(1000) c i = 1 ip = p do i = 1, ndim dims(i) = 1 end do c c factor p completely c first, find all prime numbers less than or equal to p c pmax = 0 do i = 2, p do j = 1, pmax if (mod(i,prime(j)).eq.0) go to 100 end do pmax = pmax + 1 prime(pmax) = i 100 continue end do c c find all prime factors of p c ifac = 0 do i = 1, pmax 200 if (mod(ip,prime(i)).eq.0) then ifac = ifac + 1 fac(ifac) = prime(i) ip = ip/prime(i) go to 200 endif end do c c determine dimensions of processor grid c do i = ifac, 1, -1 c c find dimension with minimum value c imin = dims(1) mdim = 1 do j = 2, ndim if (dims(j).lt.imin) then imin = dims(j) mdim = j endif end do dims(mdim) = dims(mdim)*fac(i) end do c return end ga-5.9.2/global/testing/mp3.fh000066400000000000000000000010231500715745200160530ustar00rootroot00000000000000#ifdef TCGMSG # include "tcgmsg.fh" # define MP_TIMER tcgtime # define MP_FINALIZE() pend() #else # include "mpif.h" # define MP_TIMER mpi_wtime # define MP_FINALIZE() mpi_finalize(ierr) #endif #ifndef MP_DEFINES_ONLY # ifdef TCGMSG call pbeginf # else integer ierr # if defined(MPI_MT) || defined(MPI_PT) integer required, provided required=MPI_THREAD_MULTIPLE call mpi_init_thread(required, provided, ierr) # else call mpi_init(ierr) # endif # endif #endif ga-5.9.2/global/testing/mp3.h000066400000000000000000000030111500715745200157040ustar00rootroot00000000000000#if defined(MSG_COMMS_TCGMSGMPI) # include # define MP_BARRIER() tcg_synch(30000) # define MP_INIT(argc,argv) tcg_pbegin((argc),(argv)) # define MP_FINALIZE() tcg_pend() # define MP_MYID(pid) *(pid) = (int)tcg_nodeid() # define MP_PROCS(pproc) *(pproc) = (int)tcg_nnodes() # define MP_TIMER tcg_time # define MP_ASSERT(code) code #else # include # define MP_BARRIER() MPI_Barrier(MPI_COMM_WORLD) # define MP_FINALIZE() MPI_Finalize() # if defined(MPI_MT) || defined(MPI_PT) static inline int MPI_INIT_THREAD(int *argc, char ***argv) { int status; int provided; status = MPI_Init_thread(argc, argv, MPI_THREAD_MULTIPLE, &provided); return status; } # define MP_INIT(argc,argv) MPI_INIT_THREAD(&(argc),&(argv)) # else # define MP_INIT(argc,argv) MPI_Init(&(argc),&(argv)) # endif # define MP_MYID(pid) MPI_Comm_rank(MPI_COMM_WORLD, (pid)) # define MP_PROCS(pproc) MPI_Comm_size(MPI_COMM_WORLD, (pproc)) # define MP_TIMER MPI_Wtime # define MP_ASSERT(code) do { \ if (MPI_SUCCESS != (code)) { \ MPI_Abort(MPI_COMM_WORLD, (code)); \ } \ } while (0) #endif #ifdef MPI_SPAWN # define GA_INIT(argc,argv) GA_Initialize_args(&(argc),&(argv)) # define ARMCI_INIT(argc,argv) ARMCI_Init_args(&(argc),&(argv)) #else # define GA_INIT(argc,argv) GA_Initialize() # define ARMCI_INIT(argc,argv) ARMCI_Init() #endif ga-5.9.2/global/testing/mp3def.fh000066400000000000000000000001011500715745200165260ustar00rootroot00000000000000#define MP_DEFINES_ONLY #include "mp3.fh" #undef MP_DEFINES_ONLY ga-5.9.2/global/testing/mtest.c000066400000000000000000000051661500715745200163510ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #include #include "ga.h" #include "macdecls.h" #include "mp3.h" #define N 100 /* dimension of matrices */ int main( int argc, char **argv ) { int g_a, g_b, i, j, size, size_me; int icnt, idx, jdx, ld; int n=N, type=MT_C_INT; int *values, *ptr; int **indices; int dims[2]={N,N}; int lo[2], hi[2]; int heap=30000, stack=20000; int me, nproc; int datatype, elements; double *prealloc_mem; MP_INIT(argc,argv); GA_INIT(argc,argv); /* initialize GA */ me=GA_Nodeid(); nproc=GA_Nnodes(); if(me==0) { if(GA_Uses_fapi())GA_Error("Program runs with C array API only",1); printf("Using %ld processes\n",(long)nproc); fflush(stdout); } heap /= nproc; stack /= nproc; if(! MA_init(MT_C_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ /* Create a regular matrix. */ if(me==0)printf("Creating matrix A\n"); g_a = NGA_Create(type, 2, dims, "A", NULL); if(!g_a) GA_Error("create failed: A",n); if(me==0)printf("OK\n"); /* Fill matrix using scatter routines */ size = N*N; if (size%nproc == 0) { size_me = size/nproc; } else { i = size - size%nproc; size_me = i/nproc; if (me < size%nproc) size_me++; } /* Check that sizes are all okay */ i = size_me; GA_Igop(&i,1,"+"); if (i != size) GA_Error("Sizes don't add up correctly: ",i); /* Allocate index and value arrays */ indices = (int**)malloc(size_me*sizeof(int*)); values = (int*)malloc(size_me*sizeof(int)); icnt = me; for (i=0; i=N) printf("p[%d] bogus idx: %d icnt: %d\n",me,idx,icnt); if (jdx<0 || jdx>=N) printf("p[%d] bogus jdx: %d jcnt: %d\n",me,jdx,icnt); icnt += nproc; icnt = icnt%size; } /* Scatter values into g_a */ NGA_Scatter(g_a, values, indices, size_me); GA_Sync(); /* Check to see if contents of g_a are correct */ NGA_Distribution( g_a, me, lo, hi ); NGA_Access(g_a, lo, hi, &ptr, &ld); for (i=lo[0]; i Checking nga_matmul_patch ... ' call ffflush(6) endif c alo(1) = 1 ahi(1) = n blo(1) = 1 bhi(1) = n bufld(1) = n do j = 2, dim-1 alo(j) = j ahi(j) = j blo(j) = j bhi(j) = j clo(j) = j chi(j) = j bufld(j) = 1 enddo do j = 1+me, n, nproc alo(dim) = j ahi(dim) = j blo(dim) = j bhi(dim) = j call nga_put(g_a1,alo,ahi,a(1,j),bufld) call nga_put(g_b1,blo,bhi,b(1,j),bufld) enddo c call ga_sync() alo(1) = ailo ahi(1) = ailo+1 c ahi(1) = ailo+n/2 alo(dim) = ajlo ahi(dim) = ajlo c ahi(dim) = ajlo+n/3 blo(1) = bilo bhi(1) = bilo c bhi(1) = bilo+n/3 blo(dim) = bjlo bhi(dim) = bjlo c bhi(dim) = bjlo+n/2 clo(1) = bilo chi(1) = bilo+1 c chi(1) = bilo+n/2 clo(dim) = bjlo chi(dim) = bjlo c chi(dim) = bjlo+n/2 c call nga_matmul_patch('n','n', 1d0, 0d0, $ g_a1, alo, ahi, $ g_b1, blo, bhi, $ g_c1, clo, chi) call xgemm('n','n',2,2,1,1d0,a(ailo,ajlo), n, $ b(bilo,bjlo),n, 0d0, c, n) c call xgemm('n','n',n/2+1,n/2+1,n/3+1,1d0,a(ailo,ajlo), n, c $ b(bilo,bjlo),n, 0d0, c, n) c bufld(1) = n/2 + 1 call nga_get(g_c1,clo,chi,buf,bufld) base = 0 do j = 1, 1 if(Mod(j,nproc).eq.me) then do i = 1, 2 base = base+1 if(ABS(buf(base)- c(i,j)).gt.1d-8) then print *,me, ' error ',i,j, buf(base), c(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo c call ga_sync() if (me .eq. 0) then write(6,*)' a*b: OK ' call ffflush(6) endif c call ga_sync() alo(1) = ailo ahi(1) = ailo+n/2 alo(dim) = ajlo ahi(dim) = ajlo+n/3 blo(1) = bilo bhi(1) = bilo+n/3 blo(dim) = bjlo bhi(dim) = bjlo+n/2 clo(1) = bilo chi(1) = bilo+n/2 clo(dim) = bjlo chi(dim) = bjlo+n/2 c call nga_matmul_patch('n','n', 1d0, 0d0, $ g_a1, alo, ahi, $ g_b1, blo, bhi, $ g_c1, clo, chi) call xgemm('n','n',n/2+1,n/2+1,n/3+1,1d0,a(ailo,ajlo), n, $ b(bilo,bjlo),n, 0d0, c, n) c bufld(1) = n/2 + 1 call nga_get(g_c1,clo,chi,buf,bufld) base = 0 do j = 1, 1+n/2 if(Mod(j,nproc).eq.me) then do i = 1, 1+n/2 base = base+1 if(ABS(buf(base)- c(i,j)).gt.1d-8) then print *,me, ' error ',i,j, buf(base), c(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo c call ga_sync() if (me .eq. 0) then write(6,*)' a*b: OK ' call ffflush(6) endif c call ga_sync() call nga_matmul_patch('t','n', 1d0, 0d0, $ g_a1, alo, ahi, $ g_b1, blo, bhi, $ g_c1, clo, chi) call xgemm('t','n',n/2+1,n/2+1,n/3+1,1d0,a(ajlo,ailo), n, $ b(bilo,bjlo),n, 0d0, c, n) call nga_get(g_c1,clo,chi,buf,bufld) base = 0 do j = 1, 1+n/2 if(Mod(j,nproc).eq.me) then do i = 1, 1+n/2 base = base+1 if(ABS(buf(base)- c(i,j)).gt.1d-8) then print *,me, ' error ',i,j, buf(base), c(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo c call ga_sync() if (me .eq. 0) then write(6,*)' trans(a)*b: OK ' call ffflush(6) endif c call ga_sync() call nga_matmul_patch('n','t', 1d0, 0d0, $ g_a1, alo, ahi, $ g_b1, blo, bhi, $ g_c1, clo, chi) call xgemm('n','t',n/2+1,n/2+1,n/3+1,1d0,a(ailo,ajlo), n, $ b(bjlo,bilo),n, 0d0, c, n) call nga_get(g_c1,clo,chi,buf,bufld) base = 0 do j = 1, 1+n/2 if(Mod(j,nproc).eq.me) then do i = 1, 1+n/2 base = base+1 if(ABS(buf(base)- c(i,j)).gt.1d-8) then print *,me, ' error ',i,j, buf(base), c(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo c call ga_sync() if (me .eq. 0) then write(6,*)' a*trans(b): OK ' call ffflush(6) endif c call nga_matmul_patch('t','t', 1d0, 0d0, $ g_a1, alo, ahi, $ g_b1, blo, bhi, $ g_c1, clo, chi) call xgemm('t','t',n/2+1,n/2+1,n/3+1,1d0,a(ajlo,ailo), n, $ b(bjlo,bilo),n, 0d0, c, n) call nga_get(g_c1,clo,chi,buf,bufld) base = 0 do j = 1, 1+n/2 if(Mod(j,nproc).eq.me) then do i = 1, 1+n/2 base = base+1 if(ABS(buf(base)- c(i,j)).gt.1d-8) then print *,me, ' error ',i,j, buf(base), c(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo c call ga_sync() if (me .eq. 0) then write(6,*)' trans(a)*trans(b): OK ' call ffflush(6) endif c status = ga_destroy(g_a1) status = status .and. ga_destroy(g_b1) status = status .and. ga_destroy(g_c1) if(.not. status) print *, 'ga_destroy failed' c end subroutine dpatch_test2() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer n,m, dim, dim1 parameter (n = 10) parameter (dim = 3) parameter (dim1 = 2) double precision alpha, beta, dot integer nproc, me integer i, j, ailo, ajlo, bilo, bjlo, base, iran integer alo(dim), ahi(dim), ald(dim1), chunk(dim) integer blo(dim), bhi(dim) integer clo(dim), chi(dim) integer g_a, g_b, g_c logical status c me = ga_nodeid() nproc = ga_nnodes() c do i = 1, dim chunk(i) = -1 chi(i) =n alo(i) =1 blo(i) =1 clo(i) =1 if (i.ne.dim) ald(i) = n enddo c ahi(1)=n ahi(2)=1 status = nga_create(MT_DBL, dim1, ahi, 'a', chunk, g_a) if(.not. status) call ga_error('create 1 failed',0) c status = nga_create(MT_DBL, dim1, ahi, 'b', chunk, g_b) if(.not. status) call ga_error('create 2 failed',0) c status = nga_create(MT_DBL, dim, chi, 'c', chunk, g_c) if(.not. status) call ga_error('create 2 failed',0) c call ga_fill(g_a,1d0) call ga_fill(g_b,1d0) call ga_zero(g_c) if(me.eq.0) print *,' [1:N,1:N,1:1]=[1,N]x[N,1]^t' chi(dim) = 1 bhi(1)=1 bhi(2)=n call nga_matmul_patch('n','t', 1d0, 0d0, $ g_a, alo, ahi, $ g_b, blo, bhi, $ g_c, clo, chi) c c call nga_print_patch(g_c,clo,chi) dot = nga_ddot_patch(g_c,'n', clo, chi, g_c,'n', clo, chi) if(abs(dot- 1d0*n*n) .gt. 0.01) then print *, 'error ', dot, n*n else if(me.eq.0)print *,' OK' endif status = ga_destroy(g_a) status = status .and. ga_destroy(g_b) status = status .and. ga_destroy(g_c) if(.not. status) print *, 'ga_destroy failed' c end ga-5.9.2/global/testing/mulmatpatchc.c000066400000000000000000000225401500715745200176720ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include "ga.h" #include "macdecls.h" #include "mp3.h" #include "xgemm.h" # define THRESH 1.0e-20 #define ABS(x) ((x) >= 0.0 ? (x) : -(x)) #define MAX(x,y) ((x) >= (y) ? (x) : (y)) #define MISMATCH(x,y) (ABS((x)-(y)) / MAX(1.0,ABS((x)))) > THRESH #define NMAX 100 #define POW2(x) ((x)*(x)) #define POW4(x) ((x)*(x)*(x)*(x)) #define SPECIFIC_CASE 0 #define VERBOSE 0 static void dpatch_test( int size, int dist_same, int ampos, int akpos, int bkpos, int bnpos, int cmpos, int cnpos); static void dpatch_test2(); #if HAVE_BLAS extern void dgemm_(char *, char *, int *, int *, int *, double *, const double *, int *, const double *, int *, double *, double *, int *); #else extern void xb_dgemm(char *, char *, int *, int *, int *, double *, const double *, int *, const double *, int *, double *, double *, int *); #endif int main(int argc, char **argv) { int bufsize, gasize; time_t t; long seed; int sizes[] = {10, 50, 100}; int s, same_dist, ampos, akpos, bkpos, bnpos, cmpos, cnpos; MP_INIT(argc,argv); GA_Initialize_args(&argc,&argv); if (0 == GA_Nodeid()) { printf(" GA initialized\n"); fflush(stdout); } /* we need srandom seed to be the same on all procs */ t = time(NULL); seed = (long)t; GA_Lgop(&seed, 1, "max"); srandom(seed); #if VERBOSE printf("seed=%ld\n", seed); fflush(stdout); #endif GA_Sync(); /* we want to force distribution of innermost loop in nga_mulmat_patch by providing less buffer memory than needed */ if(GA_Uses_ma()) { gasize = (POW4(NMAX) * 3)/GA_Nnodes(); } else { gasize = 0; } bufsize = (NMAX/2 + 1)*(NMAX/3 + 1)*2 + POW2(NMAX/2 + 1); bufsize = bufsize*6/7; if (!MA_init(MT_DBL, 10, gasize+bufsize+500000)) { GA_Error("MA_init failed", -1); } if (0 == GA_Nodeid()) { printf(" \n"); printf(" CHECKING MATRIX MULTIPLICATION FOR PATCHES \n"); #if VERBOSE printf("gasize and bufsize are %d %d\n", gasize, bufsize); #endif printf(" \n"); fflush(stdout); } #if SPECIFIC_CASE dpatch_test(10, 0, 0, 1, 1, 2, 2, 3); #else for (s=0; s<1; ++s) { for (same_dist=0; same_dist<2; ++same_dist) { for (ampos=0; ampos<4; ++ampos) { for (akpos=ampos+1; akpos<4; ++akpos) { for (bkpos=0; bkpos<4; ++bkpos) { for (bnpos=bkpos+1; bnpos<4; ++bnpos) { for (cmpos=0; cmpos<4; ++cmpos) { for (cnpos=cmpos+1; cnpos<4; ++cnpos) { dpatch_test(sizes[s], same_dist, ampos, akpos, bkpos, bnpos, cmpos, cnpos); } } } } } } } } #endif #if 0 dpatch_test2(); #endif if(0 == GA_Nodeid()) { printf(" All tests successful \n"); fflush(stdout); } GA_Terminate(); MP_FINALIZE(); return 0; } /** * We start with a 4-dimensional array and multiply various 2D patches, * comparing results against a locally computed dgemm. */ static void dpatch_test( int size, int dist_same, int ampos, int akpos, int bkpos, int bnpos, int cmpos, int cnpos) { const int ndim=4; double *a, *b, *c, *r, *v; double alpha, beta; int nproc, me; int i, j; int dims[4], chunk[4], rld[3]; int alo[4], ahi[4], ald[3]; int blo[4], bhi[4], bld[3]; int clo[4], chi[4], cld[3]; int g_a, g_b, g_c; char ta, tb; int m, n, k; me = GA_Nodeid(); nproc = GA_Nnodes(); assert(size <= NMAX); #if SPECIFIC_CASE m = 1; n = 1; k = 4; #else m = random() % (size/2); n = random() % (size/2); k = random() % (size/2); if (m<=0) m = 1; if (n<=0) n = 1; if (k<=0) k = 1; #endif if (0 == me) { printf("size=%d, dist_same=%d ampos=%d akpos=%d bkpos=%d bnpos=%d cmpos=%d cnpos=%d m=%d n=%d k=%d\n", size, dist_same, ampos, akpos, bkpos, bnpos, cmpos, cnpos, m, n, k); fflush(stdout); } a = malloc(sizeof(double)*size*size); b = malloc(sizeof(double)*size*size); c = malloc(sizeof(double)*size*size); r = malloc(sizeof(double)*size*size); memset(a, 0, sizeof(double)*size*size); memset(b, 0, sizeof(double)*size*size); memset(c, 0, sizeof(double)*size*size); memset(r, 0, sizeof(double)*size*size); /* establish the shape and default chunking of the global arrays */ for (i=0; i Checking NGA_Matmul_patch ... \n"); fflush(stdout); } #endif /* fill the g_a and g_b global arrays entirely with data */ for (i=0; i0) ald[ampos-1] = m; if (akpos>0) ald[akpos-1] = k; bhi[bkpos] += k - 1; bhi[bnpos] += n - 1; if (bkpos>0) bld[bkpos-1] = k; if (bnpos>0) bld[bnpos-1] = n; chi[cmpos] += m - 1; chi[cnpos] += n - 1; if (cmpos>0) cld[cmpos-1] = m; if (cnpos>0) cld[cnpos-1] = n; #if 0 printf("a[%d:%d,%d:%d,%d:%d,%d:%d] %dx%dx%dx%d (%dx%d)\n", alo[0], ahi[0], alo[1], ahi[1], alo[2], ahi[2], alo[3], ahi[3], m, 1, k, 1, m, k); printf("ald={%d,%d,%d}\n", ald[0], ald[1], ald[2]); printf("b[%d:%d,%d:%d,%d:%d,%d:%d] %dx%dx%dx%d (%dx%d)\n", blo[0], bhi[0], blo[1], bhi[1], blo[2], bhi[2], blo[3], bhi[3], 1, k, 1, n, k, n); printf("bld={%d,%d,%d}\n", bld[0], bld[1], bld[2]); printf("c[%d:%d,%d:%d,%d:%d,%d:%d] %dx%dx%dx%d (%dx%d)\n", clo[0], chi[0], clo[1], chi[1], clo[2], chi[2], clo[3], chi[3], m, 1, 1, n, m, n); printf("cld={%d,%d,%d}\n", cld[0], cld[1], cld[2]); #endif /* reset our buffers, just in case */ memset(a, 0, sizeof(double)*size*size); memset(b, 0, sizeof(double)*size*size); memset(c, 0, sizeof(double)*size*size); memset(r, 0, sizeof(double)*size*size); /* get patches locally and compute locally */ NGA_Get(g_a, alo, ahi, a, ald); NGA_Get(g_b, blo, bhi, b, bld); GA_Sync(); ta = 'n'; tb = 'n'; alpha = 1e0; beta = 0e0; #if HAVE_BLAS dgemm_(&tb, &ta, &n, &m, &k, &alpha, b, &n, a, &k, &beta, c, &n); #else xb_dgemm(&tb, &ta, &n, &m, &k, &alpha, b, &n, a, &k, &beta, c, &n); #endif /* perform global computation */ NGA_Matmul_patch(ta, tb, &alpha, &beta, g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi); GA_Sync(); /* get global result into local buf and compare results */ NGA_Get(g_c, clo, chi, r, cld); GA_Sync(); for (i=0; i<1; ++i) { if (MISMATCH(c[i],r[i])) { printf("at %d %f != %f\n", i, c[i], r[i]); GA_Error("mismatch", 1); } } free(a); free(b); free(c); free(r); GA_Destroy(g_a); GA_Destroy(g_b); GA_Destroy(g_c); } static void dpatch_test2() { } ga-5.9.2/global/testing/nb2test.F000066400000000000000000000276411500715745200165430ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c vector boxes lack arithmetic precision # define THRESH 1d-13 # define THRESHF 1e-5 #define MISMATCH(x,y) abs(x-y)/max(1d0,abs(x)).gt.THRESH #define MISMATCHF(x,y) abs(x-y)/max(1.0,abs(x)).gt.THRESHF program main implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c#include "tcgmsg.fh" integer heap, stack, fudge, ma_heap, me integer nmax, DIM, nwidth, MAXPROC, nloop parameter (nmax = 4, DIM = 2, nwidth = 2, MAXPROC = 2000) parameter (nloop = 1) integer ndim, nproc, pdims(7), type, dcnt, g_a, maxval integer i, j, k, dims(7), width(7), map(MAXPROC+2) integer lo(7), hi(7), ld(7) integer lo2(7), hi2(7), ld2(7) integer dims3(7), ld3(7), chunk(7) GA_ACCESS_INDEX_TYPE index3 integer a(nmax, nmax), b(nmax+2*nwidth,nmax+2*nwidth) integer handles(8),mask(7) double precision start,finish,start1,finish1,t1,t2,t3,t4,t5,tmp double precision t6,t7 logical status, safe_put, safe_get, has_data(0:MAXPROC-1) parameter (heap=60*60*4, fudge=100, stack=100*100) c c*** Intitialize a message passing library c #include "mp3.fh" c c*** Initialize GA c c There are 2 choices: ga_initialize or ga_initialize_ltd. c In the first case, there is no explicit limit on memory usage. c In the second, user can set limit (per processor) in bytes. c print* call ga_initialize() nproc = ga_nnodes() me = ga_nodeid() c we can also use GA_set_memory_limit BEFORE first ga_create call c ma_heap = heap + fudge call GA_set_memory_limit(util_mdtob(ma_heap)) c if(ga_nodeid().eq.0)then print *,' GA initialized ' call ffflush(6) endif c c*** Initialize the MA package c MA must be initialized before any global array is allocated c status = ma_init(MT_DCPL, stack, ma_heap) if (.not. status) call ga_error('ma_init failed',-1) c if(me.eq.0)then print *, 'using ', nproc, ' process(es)' call ffflush(6) endif c c Test ghost distributions c ndim = DIM c c Create irregular distribution on all nodes c call factor(nproc,ndim,pdims) dims(1) = pdims(1) * nmax dims(2) = pdims(2) * nmax maxval = 1 do i = 1, ndim maxval = dims(i)*maxval end do maxval = maxval - 1 c dcnt = 1 do i = 1, pdims(1) map(dcnt) = (i-1)*nmax + 1 dcnt = dcnt + 1 end do do i = 1, pdims(2) map(dcnt) = (i-1)*nmax + 1 dcnt = dcnt + 1 end do c do i = 1, ndim width(i) = nwidth chunk(i) = 1 if (pdims(i).gt.dims(i)) pdims(i) = dims(i) if (me.eq.0) then write(6,*) 'Value of pdims(',i,') is ',pdims(i) endif call ffflush(6) ld(i) = nmax end do if (me.eq.0) then do i = 1, dcnt - 1 write(6,'("map(",i2,") = ",i5)') i,map(i) call ffflush(6) end do endif type = MT_INT status = nga_create_ghosts_irreg (type, ndim, dims, width, + "test_array", map, pdims, g_a) if (status.and.me.eq.0) then write(6,*) '*' write(6,*) '* Global array creation was successful' write(6,*) '*' elseif (.not.status) then write(6,*) 'Global array creation failure on ',me endif c c Find processors that have data c call ga_sync do i = 0, nproc-1 call nga_distribution(g_a, i, lo, hi) has_data(i) = .true. do j = 1, ndim if (lo(j).eq.0.and.hi(j).eq.-1) has_data(i) = .false. end do if (me.eq.i) then write(6,*) '*' write(6,*) '* Distribution on processor ',i write(6,*) '*' write(6,110) lo(1), hi(1) write(6,110) lo(2), hi(2) 110 format(2i10) endif call ffflush(6) call ga_sync end do c c initialize g_a c call ga_sync call nga_distribution(g_a, me, lo, hi) do i = 1, hi(1) - lo(1) + 1 do j = 1, hi(2) - lo(2) + 1 a(i,j) = (i + lo(1) - 2)*dims(1) + (j + lo(2) - 2) + 1 end do end do safe_put = .true. do i = 1, ndim if (hi(i).lt.lo(i)) safe_put = .false. end do if (has_data(me).and.safe_put) call nga_put(g_a, lo, hi, a, ld) c c print out values of a c do k = 0, nproc-1 call ga_sync if (k.eq.me.and.has_data(me).and.maxval.lt.10000) then write(6,*) write(6,*) 'Initial data on processor ',k write(6,*) do i = 1, min(hi(1)-lo(1)+1,10) write (6,101) (a(i,j),j=1,min(hi(2)-lo(2)+1,10)) end do call ffflush(6) endif end do 101 format(10x,10i5) call ffflush(6) c go to 122 c update array using the non-blocking directional update c t1 = util_timer() k = 0 do i = -1, 1 mask(1) = i do j = -1, 1 mask(2) = j if (i.ne.0.or.j.ne.0) then k = k + 1 call nga_nbget_ghost_dir(g_a,mask,handles(k)) endif end do end do c c Wait c do i = 1, 8 call nga_nbwait(handles(i)) end do call ga_sync t1 = util_timer() - t1 if (me.eq.0) then write(6,*) '*' write(6,*) '* Completed update successfully' write(6,*) '*' call ffflush(6) endif c c get patch with ghost cells c do i = 1, ndim lo2(i) = lo(i) - width(i) hi2(i) = hi(i) + width(i) ld2(i) = ld(i) + 2*width(i) end do call ga_sync call ffflush(6) do i = 0, nproc-1 if (i.eq.me) then write(6,*) '*' write(6,*) 'ghost patch dimensions on processor ',i write(6,*) '*' do j = 1, ndim write(6,*) 'lo(',j,') = ',lo2(j) write(6,*) 'hi(',j,') = ',hi2(j) write(6,*) 'ld(',j,') = ',ld2(j) end do write(6,*) '*' endif call ga_sync call ffflush(6) end do safe_get = .true. t2 = 0.0d00 t3 = 0.0d00 do i = 1, nloop start = util_timer() call ga_sync start1 = util_timer() if (has_data(me).and.safe_get) + call nga_periodic_get(g_a, lo2, hi2, b, ld2) finish1 = util_timer() call ga_sync finish = util_timer() t2 = t2 + finish1 - start1 t3 = t3 + finish - start end do t2 = t2/dble(nloop) t3 = t3/dble(nloop) if (me.eq.0.and.maxval.lt.10000) then write(6,*) '*' write(6,*) '* Write out contents of local patch using' write(6,*) '* nga_periodic_get' write(6,*) '*' call ffflush(6) endif do k = 0, nproc-1 call ga_sync if (me.eq.k.and.has_data(me).and.maxval.lt.10000) then write(6,*) '*' write(6,*) '* Data on processor ',k write(6,*) '*' do i = 1, min(hi2(1)-lo2(1)+1,12) write (6,102) (b(i,j),j=1,min(hi2(2)-lo2(2)+1,12)) end do call ffflush(6) endif end do 102 format(14i5) if (me.eq.0) then write(6,*) '*' write(6,*) '* Performing nga_access_ghosts' write(6,*) '*' call ffflush(6) endif if (has_data(me)) call nga_access_ghosts(g_a, dims3, + index3, ld3) call ga_sync if (maxval.lt.10000) + call aprint(int_mb(index3),dims3(1),dims3(2),ld3,has_data) call atest(int_mb(index3),dims3(1),dims3(2),ld3,b, + nmax+2*nwidth,has_data) call ga_sync tmp = t1 call ga_dgop(1,tmp,1,'max') if (me.eq.0) then write(6,*) 'Maximum time for nga_update_ghosts ',tmp endif tmp = t1 call ga_dgop(2,tmp,1,'min') if (me.eq.0) then write(6,*) 'Minimum time for nga_update_ghosts ',tmp endif tmp = t1 call ga_dgop(3,tmp,1,'+') if (me.eq.0) then write(6,*) 'Average time for nga_update_ghosts ',tmp/dble(nproc) endif tmp = t2 call ga_dgop(4,tmp,1,'max') if (me.eq.0) then write(6,*) 'Maximum time for nga_periodic_get ',tmp endif tmp = t2 call ga_dgop(5,tmp,1,'min') if (me.eq.0) then write(6,*) 'Minimum time for nga_periodic_get ',tmp endif tmp = t2 call ga_dgop(6,tmp,1,'+') if (me.eq.0) then write(6,*) 'Average time for nga_periodic_get ',tmp/dble(nproc) endif tmp = t3 call ga_dgop(4,tmp,1,'max') if (me.eq.0) then write(6,*) 'Maximum time for (sync)nga_periodic_get ',tmp endif tmp = t3 call ga_dgop(5,tmp,1,'min') if (me.eq.0) then write(6,*) 'Minimum time for (sync)nga_periodic_get ',tmp endif tmp = t3 call ga_dgop(6,tmp,1,'+') if (me.eq.0) then write(6,*) 'Average time for (sync)nga_periodic_get ', + tmp/dble(nproc) endif 127 continue c if(ga_nodeid().eq.0) print *,'All tests successful ' c c*** Tidy up the GA package c call ga_terminate() c c*** Tidy up after message-passing library c call MP_FINALIZE() c stop end c subroutine aprint(a,nrow,ncol,ld,has_data) #include "global.fh" integer ld integer a(ld,*) integer i, j, k, nproc logical has_data(0:1999) nproc = ga_nnodes() do k = 1, nproc call ga_sync if (k-1.eq.ga_nodeid().and.has_data(k-1)) then write(6,*) '*' write(6,*) '* Data on processor ',k-1 write(6,*) '*' do i = 1, min(nrow,12) write (6,102) (a(i,j), j = 1, min(ncol,12)) 102 format(14i5) end do endif call ffflush(6) enddo c return end c subroutine atest(a,nrow,ncol,ld,b,ld2,has_data) #include "global.fh" integer ld integer a(ld,*), b(ld2,*) integer i, j, k, nproc logical has_data(0:1999), check_data nproc = ga_nnodes() check_data = .true. do k = 1, nproc call ga_sync if (k-1.eq.ga_nodeid().and.has_data(k-1)) then do i = 1, nrow do j = 1, ncol if (a(i,j).ne.b(i,j)) check_data = .false. end do end do if (check_data) then write(6,*) '*' write(6,*) '* Data from nga_access_ghosts and' write(6,*) '* nga_periodic_get is the same on' write(6,*) '* processor ',k-1 write(6,*) '*' else write(6,*) '*' write(6,*) '* Data from nga_access_ghosts and' write(6,*) '* nga_periodic_get is NOT the same on' write(6,*) '* processor ',k-1 write(6,*) '*' endif endif call ffflush(6) enddo c return end c subroutine factor(p,ndim,dims) implicit none integer i,j,p,ndim,dims(7),imin,mdim integer ip,ifac,pmax,prime(1000) integer fac(1000) c i = 1 ip = p do i = 1, ndim dims(i) = 1 end do c c factor p completely c first, find all prime numbers less than or equal to p c pmax = 0 do i = 2, p do j = 1, pmax if (mod(i,prime(j)).eq.0) go to 100 end do pmax = pmax + 1 prime(pmax) = i 100 continue end do c c find all prime factors of p c ifac = 0 do i = 1, pmax 200 if (mod(ip,prime(i)).eq.0) then ifac = ifac + 1 fac(ifac) = prime(i) ip = ip/prime(i) go to 200 endif end do c c determine dimensions of processor grid c do i = ifac, 1, -1 c c find dimension with minimum value c imin = dims(1) mdim = 1 do j = 2, ndim if (dims(j).lt.imin) then imin = dims(j) mdim = j endif end do dims(mdim) = dims(mdim)*fac(i) end do c return end ga-5.9.2/global/testing/nbgetput.c000066400000000000000000000163631500715745200170460ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #include #include "ga.h" #include "macdecls.h" #include "mp3.h" #include // #define N 16777216 /* dimension of matrices */ #define N 536870912 /* dimension of matrices */ #define WINDOWSIZE 2 #define min(a,b) (((a)<(b))?(a):(b)) #define TIMER GA_Wtime int main( int argc, char **argv ) { int g_a; int g_b; int g_c; //int lo[1], hi[1]; int64_t lo[1], hi[1]; //int dims[1]; int64_t dims[1]; int me, nproc; int64_t ld; int isize, jsize; int type=MT_C_INT; int nelems, ok; int *buf_a, *ptr_a; int *buf_b, *ptr_b; int *buf_c, *ptr_c; ga_nbhdl_t *nbhdl_a; ga_nbhdl_t *nbhdl_b; ga_nbhdl_t *nbhdl_c; int heap=300000000, stack=2000000; MP_INIT(argc,argv); GA_INIT(argc,argv); /* initialize GA */ me=GA_Nodeid(); nproc=GA_Nnodes(); if(me==0) { if(GA_Uses_fapi())GA_Error("Program runs with C array API only",1); printf("\nUsing %d processes\n",nproc); fflush(stdout); } heap /= nproc; stack /= nproc; if(! MA_init(MT_F_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ /* Create a regular matrix. */ if(me==0)printf("\nCreating matrix A of size %d x %d\n",N,N); if(me==0)printf("\nCreating matrix B of size %d x %d\n",N,N); if(me==0)printf("\nCreating matrix C of size %d x %d\n",N,N); dims[0] = N; //dims = N; int n; g_a = NGA_Create64(type, 1, dims, "A", NULL); if(!g_a) GA_Error("create failed: A",n); g_b = NGA_Create64(type, 1, dims, "B", NULL); if(!g_b) GA_Error("create failed: B",n); g_c = NGA_Create64(type, 1, dims, "C", NULL); if(!g_c) GA_Error("create failed: C",n); /* Fill matrix from process 0 using non-blocking puts */ nelems = N; GA_Sync(); /* Copy matrix to process 0 using non-blocking gets */ if (me == 0) { buf_a = (int*)malloc(nelems*sizeof(int)); buf_b = (int*)malloc(nelems*sizeof(int)); buf_c = (int*)malloc(nelems*sizeof(int)); nbhdl_a = (ga_nbhdl_t*)malloc(nproc*sizeof(ga_nbhdl_t)); nbhdl_b = (ga_nbhdl_t*)malloc(nproc*sizeof(ga_nbhdl_t)); nbhdl_c = (ga_nbhdl_t*)malloc(nproc*sizeof(ga_nbhdl_t)); //TODO: set this value //int alpha=1; double start_time, end_time; int m; int NN = N / nproc; for (m = 1; m < NN; m*=2) { double nbput_timings = 0; double nbget_timings = 0; double nbacc_timings = 0; double wait_timings = 0; ptr_c = buf_c; int i; for (i=0; i #endif #if HAVE_MATH_H # include #endif #include #include "ga.h" #include "macdecls.h" #include "mp3.h" #include // #define NN 8192 /* dimension of matrices */ #define NN 32768 /* dimension of matrices */ #define WINDOWSIZE 2 #define min(a,b) (((a)<(b))?(a):(b)) #define TIMER GA_Wtime int main( int argc, char **argv ) { int g_a; int g_b; int g_c; int lo[2], hi[2]; int dims[2]; int me, nproc; int ld, isize, jsize; int type=MT_C_INT; int nelems, ok; int *buf_a, *ptr_a; int *buf_b, *ptr_b; int *buf_c, *ptr_c; ga_nbhdl_t *nbhdl_a; ga_nbhdl_t *nbhdl_b; ga_nbhdl_t *nbhdl_c; int heap=3000000, stack=2000000; MP_INIT(argc,argv); GA_INIT(argc,argv); /* initialize GA */ me=GA_Nodeid(); nproc=GA_Nnodes(); if(me==0) { if(GA_Uses_fapi())GA_Error("Program runs with C array API only",1); printf("\nUsing %d processes\n",nproc); fflush(stdout); } heap /= nproc; stack /= nproc; if(! MA_init(MT_F_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ int N; for (N = 8; N < NN; N*=2) { /* Create a regular matrix. */ if(me==0)printf("\nCreating matrix A of size %d x %d\n",N,N); if(me==0)printf("\nCreating matrix B of size %d x %d\n",N,N); if(me==0)printf("\nCreating matrix C of size %d x %d\n",N,N); dims[0] = N; dims[1] = N; int n; g_a = NGA_Create(type, 2, dims, "A", NULL); if(!g_a) GA_Error("create failed: A",n); g_b = NGA_Create(type, 2, dims, "B", NULL); if(!g_b) GA_Error("create failed: B",n); g_c = NGA_Create(type, 2, dims, "C", NULL); if(!g_c) GA_Error("create failed: C",n); /* Fill matrix from process 0 using non-blocking puts */ nelems = N*N; GA_Sync(); /* Copy matrix to process 0 using non-blocking gets */ if (me == 0) { buf_a = (int*)malloc(nelems*sizeof(int)); buf_b = (int*)malloc(nelems*sizeof(int)); buf_c = (int*)malloc(nelems*sizeof(int)); nbhdl_a = (ga_nbhdl_t*)malloc(nproc*sizeof(ga_nbhdl_t)); nbhdl_b = (ga_nbhdl_t*)malloc(nproc*sizeof(ga_nbhdl_t)); nbhdl_c = (ga_nbhdl_t*)malloc(nproc*sizeof(ga_nbhdl_t)); //TODO: set this value //int alpha=1; double nbput_timings = 0; double nbget_timings = 0; double nbacc_timings = 0; double wait_timings = 0; double start_time, end_time; ptr_c = buf_c; int i; for (i=0; i #endif #if HAVE_MATH_H # include #endif #include #include "ga.h" #include "macdecls.h" #include "mp3.h" #define N 128 /* dimension of matrices */ #define BLOCK 4 /* make sure this value divides N evenly */ int main( int argc, char **argv ) { int g_a; long lone; int one; int lo[2], hi[2]; int dims[2]; int me, nproc; int i, j, n, ld, isize, jsize, icnt, nleft; int type=MT_C_INT; int nelems, ok; int *buf, *ptr; ga_nbhdl_t *nbhdl; int heap=3000000, stack=2000000; MP_INIT(argc,argv); GA_INIT(argc,argv); /* initialize GA */ me=GA_Nodeid(); nproc=GA_Nnodes(); if(me==0) { if(GA_Uses_fapi())GA_Error("Program runs with C array API only",1); printf("\nUsing %d processes\n",nproc); fflush(stdout); } heap /= nproc; stack /= nproc; if(! MA_init(MT_F_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ one = 1; lone = 1; /* Create a regular matrix. */ if(me==0)printf("\nCreating matrix A of size %d x %d\n",N,N); dims[0] = N; dims[1] = N; g_a = NGA_Create(type, 2, dims, "A", NULL); if(!g_a) GA_Error("create failed: A",n); /* Fill matrix from process 0 using non-blocking puts */ nelems = N*N; if (me == 0) { buf = (int*)malloc(nelems*sizeof(int)); ptr = buf; nbhdl = (ga_nbhdl_t*)malloc(nproc*sizeof(ga_nbhdl_t)); for (n=0; n 0) { icnt = 0; for (n=0; n 0) { icnt = 0; for (n=0; n 0) { icnt = 0; for (n=0; n', '[',(blo(i),':',bhi(i), i=1,ndim),']' endif c c keep a copy of the origian array call nga_get(g_a,alo,ahi, $ a(substr(m4_alo_all, 1, eval(m4_ndim*7-1))),dims) c call nga_copy_patch('n', g_a, alo, ahi, g_b, blo, bhi) c call nga_get(g_b,blo,bhi, $ b(substr(m4_blo_all, 1, eval(m4_ndim*7-1))),dims) c call m4_util_compare_patches(m4_test_type)(0d0,total, $ a,alo,ahi,ndim,dims,total,b,blo,bhi,ndim,dims) enddo c call ga_sync() if(me.eq.0)then print *, ' standard copy patch: OK' print *, ' ' call ffflush(6) endif c--- do loop=1, 10 call random_range(lop,hipl,alo,ahi,ndim) do i=1, ndim blo(i) = alo(ndim-i+1) + 1 bhi(i) = ahi(ndim-i+1) + 1 enddo if(me.eq.0)then call copy_range(loop,alo,ahi,ndim,blo,bhi,ndim) c$$$ print *, loop,': copy [',(alo(i),':',ahi(i), i=1,ndim),']', c$$$ $ '-->', '[',(blo(i),':',bhi(i), i=1,ndim),']' endif c call nga_copy_patch('n', g_a, alo, ahi, g_b, blo, bhi) c call nga_get(g_b,blo,bhi, $ b(substr(m4_blo_all, 1, eval(m4_ndim*7-1))),dims) c call m4_util_compare_patches(m4_test_type)(0d0,total, $ a,alo,ahi,ndim,dims,total,b,blo,bhi,ndim,dims) enddo c call ga_sync() if(me.eq.0)then print *, ' reshaping without transpose: OK' print *, ' ' call ffflush(6) endif c--- c prepare array a, make it transposed call m4_util_transpose(m4_test_type)(a,b,total,ndim,dims) c do loop=1, 10 call random_range(lop,hipl,alo,ahi,ndim) do i=1, ndim blo(i) = alo(ndim-i+1) + 1 bhi(i) = ahi(ndim-i+1) + 1 enddo if(me.eq.0)then call copy_range(loop,alo,ahi,ndim,blo,bhi,ndim) c$$$ print *, loop,': copy [',(alo(i),':',ahi(i), i=1,ndim),']', c$$$ $ '-->', '[',(blo(i),':',bhi(i), i=1,ndim),']' endif c call nga_copy_patch('t', g_a, alo, ahi, g_b, blo, bhi) c call nga_get(g_b,blo,bhi, $ b(substr(m4_blo_all, 1, eval(m4_ndim*7-1))),dims) c c adjust index of array a do i=1,ndim tlo(i) = alo(ndim-i+1) thi(i) = ahi(ndim-i+1) enddo call m4_util_compare_patches(m4_test_type)(0d0,total, $ a,tlo,thi,ndim,dims,total,b,blo,bhi,ndim,dims) enddo c call ga_sync() if(me.eq.0)then print *, ' reshaping transposed: OK' print *, ' ' call ffflush(6) endif c--------------------------- c status = ga_destroy(g_b) enddo c c----------------------------------------------------------------- changequote({,}) ifelse(m4_ndim,1,{},{ c testing copy on differet dimensions dtotal = 1 do i = 1,dndim ddims(i) = n dtotal = dtotal * ddims(i) enddo c if (.not. nga_create(m4_MT, dndim, ddims, 'd', chunk, g_b)) $ call ga_error(' ga_create failed ',1) c if(me.eq.0) $ print *, 'Testing copy patch on different dimensions' c call ga_sync() c c initialize g_b call m4_util_init_array(m4_test_type)(d,dtotal) call nga_distribution(g_b, me, dlo, dhi) elems = count_elems(dlo, dhi, dndim) if(elems.gt.0) call nga_put(g_b,dlo,dhi, $ d(substr(m4_dlo_all, 1, eval((m4_ndim-1)*7-1))),ddims) c c c calculate the maximum range of g_a that can fit into g_b do i = 1,ndim lop(i) = 1 hip(i) = n enddo hip(dndim) = 1 c call ga_sync() c do loop=1, 10 call random_range(lop,hip,alo,ahi,ndim) c do i=1, dndim dlo(i) = alo(dndim-i+1) dhi(i) = ahi(dndim-i+1) enddo dlo(1) = alo(ndim) dhi(1) = ahi(ndim) c if(me.eq.0) then call copy_range(loop,alo,ahi,ndim,dlo,dhi,dndim) c$$$ print *, loop,': copy [',(alo(i),':',ahi(i),i=1,ndim), c$$$ $ ']','-->','[',(dlo(i),':',dhi(i), i=1,dndim),']' endif c call nga_copy_patch('n', g_a, alo, ahi, g_b, dlo, dhi) c call nga_get(g_b,dlo,dhi, $ d(substr(m4_dlo_all, 1, eval((m4_ndim-1)*7-1))),ddims) call nga_get(g_a,alo,ahi, $ a(substr(m4_alo_all, 1, eval(m4_ndim*7-1))),dims) c call m4_util_compare_patches(m4_test_type)(0d0,total, $ a,alo,ahi,ndim,dims,total,d,dlo,dhi,dndim,ddims) enddo c call ga_sync() if(me.eq.0)then print *, ' copy patches on different dimensions: OK' print *, ' ' call ffflush(6) endif c status = ga_destroy(g_b) }) changequote(`,') c--- status = ga_destroy(g_a) end ga-5.9.2/global/testing/ngatest_src/ndim_NGA_DOT_PATCH.src000066400000000000000000000172731500715745200231410ustar00rootroot00000000000000 subroutine m4_func_NGA_DOT_PATCH(m4_test_type, m4_ndim) implicit none #include "mafdecls.fh" #include "global.fh" c integer n,m integer ndim parameter (n = m4_n) parameter (m = (m4_n**m4_ndim)/100) parameter (ndim = m4_ndim) m4_data_type a(substr(m4_array, 1, eval(m4_ndim*2-1))) m4_data_type b(substr(m4_array, 1, eval(m4_ndim*2-1))) m4_data_type c(substr(m4_array, 1, eval(m4_ndim*2-1))) integer dims(ndim) integer g_a, g_b integer chunk(ndim) integer i, total integer elems, count_elems integer loop integer lop(ndim), hip(ndim), hipl(ndim) integer alo(ndim), ahi(ndim) integer blo(ndim), bhi(ndim) integer tlo(ndim), thi(ndim) m4_data_type alpha, beta m4_data_type m4_util_dot_patch(m4_test_type) c for different array dimensions ifelse(m4_ndim,1,`',` m4_data_type d(substr(m4_array, 1, eval((m4_ndim-1)*2-1))) integer dndim parameter (dndim = m4_ndim-1) integer ddims(dndim),dlo(dndim),dhi(dndim),dtotal ') c integer nproc, me logical status integer repeat c nproc = ga_nnodes() me = ga_nodeid() c c---------------------- initialize the GA ----------------------- c initialize the chunk, dims, ld, and calculate the number c of elements total=1 do i = 1,ndim chunk(i) = 0 dims(i) = n total = total * dims(i) enddo c c*** Create global arrays if (.not. nga_create(m4_MT, ndim, dims, 'a', chunk, g_a)) $ call ga_error(' ga_create failed ',1) c c test the same distribution and different distribution seperately do repeat=1,2 if(repeat.eq.1) then status = ga_duplicate(g_a, g_b, 'a_duplicated') if(.not.ga_compare_distr(g_a, g_b)) $ call ga_error("g_b distribution different",0) c else do i = 1,ndim if(mod(i,2).eq.0) chunk(i) = n enddo if (.not. nga_create(m4_MT, ndim, dims, 'b', chunk, g_b)) $ call ga_error(' ga_create failed ',1) endif c call ga_sync() c c---------------------------NGA_DOT_PATCH ------------------------- c if(repeat.eq.1) then m4_print_info(m4_nga_dot_patch(m4_dot)) if(me.eq.0) print *, 'Testing with the same distributions' else if(me.eq.0) print *, 'Testing with different distributions' endif c c initialize GA call m4_util_init_array(m4_test_type)(a,total) call nga_distribution(g_a, me, lop, hip) elems = count_elems(lop, hip, ndim) if(elems.gt.0) call nga_put(g_a,lop,hip, $ a(substr(m4_lop_all, 1, eval(m4_ndim*7-1))),dims) call m4_util_init_array(m4_test_type)(b,total) call nga_distribution(g_b, me, lop, hip) elems = count_elems(lop, hip, ndim) if(elems.gt.0) call nga_put(g_b,lop,hip, $ b(substr(m4_lop_all, 1, eval(m4_ndim*7-1))),dims) c call ga_sync() do i = 1,ndim lop(i) = 1 hipl(i) = n-1 hip(i) = n enddo c c--- do loop=1, 10 call random_range(lop,hipl,alo,ahi,ndim) do i=1, ndim blo(i) = alo(i) + 1 bhi(i) = ahi(i) + 1 enddo if(me.eq.0)then call dot_range(loop,alo,ahi,ndim,blo,bhi,ndim) c$$$ print *, loop,'dot: [',(alo(i),':',ahi(i), i=1,ndim),']', c$$$ $ ',', '[',(blo(i),':',bhi(i), i=1,ndim),']' endif c alpha=m4_nga_dot_patch(m4_dot)(g_a,'n',alo,ahi,g_b,'n',blo,bhi) c c the result should be beta = m4_util_dot_patch(m4_test_type)(total, $ a,alo,ahi,ndim,dims,b,blo,bhi,ndim,dims) c if(ABS(beta-alpha).gt.1d-6*ABS(alpha)) then print *,me, ' error ', beta, alpha call ga_error('exiting ...',0) endif c enddo c call ga_sync() if(me.eq.0)then print *, ' without transpose OK' print *, ' ' call ffflush(6) endif c--- c prepare array a, make it transposed call m4_util_transpose(m4_test_type)(b,c,total,ndim,dims) c do loop=1, 10 call random_range(lop,hipl,alo,ahi,ndim) do i=1, ndim blo(i) = alo(i) + 1 bhi(i) = ahi(i) + 1 enddo if(me.eq.0)then call dot_range(loop,alo,ahi,ndim,blo,bhi,ndim) c$$$ print *, loop,'dot: [',(alo(i),':',ahi(i), i=1,ndim),']', c$$$ $ ',', '[',(blo(i),':',bhi(i), i=1,ndim),']' endif c alpha=m4_nga_dot_patch(m4_dot)(g_a,'n',alo,ahi,g_b,'t',blo,bhi) c c adjust index of array a do i=1,ndim tlo(i) = blo(ndim-i+1) thi(i) = bhi(ndim-i+1) enddo c c the result should be beta = m4_util_dot_patch(m4_test_type)(total, $ a,alo,ahi,ndim,dims,b,tlo,thi,ndim,dims) c if(ABS(beta-alpha).gt.1d-6*ABS(alpha)) then print *,me, ' error ', beta, alpha call ga_error('exiting ...',0) endif c enddo c call ga_sync() if(me.eq.0)then print *, ' with transpose OK' print *, ' ' call ffflush(6) endif c--------------------------- c status = ga_destroy(g_b) enddo c c----------------------------------------------------------------- changequote({,}) ifelse(m4_ndim,1,{},{ c testing copy on differet dimensions dtotal = 1 do i = 1,dndim ddims(i) = n dtotal = dtotal * ddims(i) enddo c if (.not. nga_create(m4_MT, dndim, ddims, 'd', chunk, g_b)) $ call ga_error(' ga_create failed ',1) c if(me.eq.0) $ print *, 'Testing dot patch on different dimensions' c c initialize GAs call m4_util_init_array(m4_test_type)(a,total) call nga_distribution(g_a, me, lop, hip) elems = count_elems(lop, hip, ndim) if(elems.gt.0) call nga_put(g_a,lop,hip, $ a(substr(m4_lop_all, 1, eval(m4_ndim*7-1))),dims) call m4_util_init_array(m4_test_type)(d,dtotal) call nga_distribution(g_b, me, dlo, dhi) elems = count_elems(dlo, dhi, dndim) if(elems.gt.0) call nga_put(g_b,dlo,dhi, $ d(substr(m4_dlo_all, 1, eval((m4_ndim-1)*7-1))),ddims) c call ga_sync() c c calculate the maximum range of g_a that can fit into g_b do i = 1,ndim lop(i) = 1 hip(i) = n enddo hip(dndim) = 1 c do loop=1, 10 call random_range(lop,hip,alo,ahi,ndim) c do i=1, dndim dlo(i) = alo(dndim-i+1) dhi(i) = ahi(dndim-i+1) enddo dlo(1) = alo(ndim) dhi(1) = ahi(ndim) c if(me.eq.0) then call dot_range(loop,alo,ahi,ndim,dlo,dhi,dndim) c$$$ print *,loop,'dot: [',(alo(i),':',ahi(i), i=1,ndim),']', c$$$ $ ',', '[',(dlo(i),':',dhi(i), i=1,dndim),']' endif c alpha=m4_nga_dot_patch(m4_dot)(g_a,'n',alo,ahi, $ g_b,'n',dlo,dhi) c c the result should be beta = m4_util_dot_patch(m4_test_type)(total, $ a,alo,ahi,ndim,dims,d,dlo,dhi,dndim,ddims) c if(ABS(beta-alpha).gt.1d-6*ABS(alpha)) then print *,me, ' error ', beta, alpha call ga_error('exiting ...',0) endif c enddo c call ga_sync() if(me.eq.0)then print *, ' dot patch on different dimensions: OK' print *, ' ' call ffflush(6) endif c status = ga_destroy(g_b) }) changequote(`,') c--- status = ga_destroy(g_a) end ga-5.9.2/global/testing/ngatest_src/ndim_NGA_FILL_PATCH.src000066400000000000000000000035031500715745200232300ustar00rootroot00000000000000 subroutine m4_func_NGA_FILL_PATCH(m4_test_type, m4_ndim) implicit none #include "mafdecls.fh" #include "global.fh" c integer n,m integer ndim parameter (n = m4_n) parameter (m = (m4_n**m4_ndim)/100) parameter (ndim = m4_ndim) m4_data_type a(substr(m4_array, 1, eval(m4_ndim*2-1))) m4_data_type b(substr(m4_array, 1, eval(m4_ndim*2-1))) integer lo(ndim),hi(ndim),dims(ndim) integer g_a integer chunk(ndim) integer i, total m4_data_type val integer nproc, me logical status c nproc = ga_nnodes() me = ga_nodeid() c c---------------------- initialize the GA ----------------------- c initialize the chunk, dims, ld, and calculate the number c of elements total=1 do i = 1,ndim chunk(i) = 0 dims(i) = n total = total * dims(i) enddo c c*** Create global arrays if (.not. nga_create(m4_MT, ndim, dims, 'a', chunk, g_a)) $ call ga_error(' ga_create failed ',1) c call ga_sync() c c--------------------------- NGA_FILL_PATCH ------------------------- m4_print_info(nga_fill_patch) c val = m4_conv(1234) c initialize the lo and hi do i=1, ndim lo(i) = 2 hi(i) = n - 1 enddo c call nga_fill_patch(g_a, lo, hi, val) c c check the result call m4_util_fill_array(m4_test_type)(a,total,val) call nga_get(g_a,lo,hi, $ b(substr(m4_lo_all, 1, eval(m4_ndim*6-1))),dims) c call m4_util_compare_patches(m4_test_type)(0d0,total, $ a,lo,hi,ndim,dims,total,b,lo,hi,ndim,dims) c call ga_sync() if(me.eq.0)then print *, 'OK' print *, ' ' call ffflush(6) endif c--------------------------- c status= ga_destroy(g_a) end ga-5.9.2/global/testing/ngatest_src/ndim_NGA_GATHER.src000066400000000000000000000052701500715745200225400ustar00rootroot00000000000000 subroutine m4_func_NGA_GATHER(m4_test_type, m4_ndim) implicit none #include "mafdecls.fh" #include "global.fh" c integer n,m integer ndim parameter (n = m4_n) parameter (m = (m4_n**m4_ndim)/100) parameter (ndim = m4_ndim) m4_data_type a(substr(m4_array, 1, eval(m4_ndim*2-1))) m4_data_type b(substr(m4_array, 1, eval(m4_ndim*2-1))) integer lo(ndim),hi(ndim),dims(ndim),ld(ndim) integer g_a integer chunk(ndim) integer i, j, total, loop integer elems, count_elems m4_data_type v(m) integer d(ndim, m) double precision drand integer unique,unique_index integer nproc, me logical status c nproc = ga_nnodes() me = ga_nodeid() c c---------------------- initialize the GA ----------------------- c initialize the chunk, dims, ld, and calculate the number c of elements total=1 do i = 1,ndim chunk(i) = 0 dims(i) = n ld(i) = n total = total * dims(i) enddo c c*** Create global arrays if (.not. nga_create(m4_MT, ndim, dims, 'a', chunk, g_a)) $ call ga_error(' ga_create failed ',1) c call ga_sync() c c------------------------------- NGA_GATHER ---------------------------- m4_print_info(nga_gather) c c initialize GA call m4_util_init_array(m4_test_type)(a,total) call nga_distribution(g_a, me, lo, hi) elems = count_elems(lo, hi, ndim) if(elems.gt.0) call nga_put(g_a,lo,hi, $ a(substr(m4_lo_all, 1, eval(m4_ndim*6-1))),dims) c do loop = 1, MAXLOOP call ga_sync() c initialize the index array do i = 1, m c generate indices unique_index = 0 do while(unique_index.eq.0) do j=1,ndim d(j,i) = int(drand(0)*real(n)) + 1 enddo c unique_index = unique(d,ndim,m,i) enddo enddo c c gather from global array call nga_gather(g_a, v, d, m) c c collect each elements and compare do i = 1, m do j=1, ndim lo(j) = d(j,i) enddo call nga_get(g_a,lo,lo, $ b(substr(m4_lo_all, 1, eval(m4_ndim*6-1))),ld) c c compare the results if(v(i).ne. $ b(substr(m4_lo_all, 1, eval(m4_ndim*6-1)))) then call ga_error('bye', 0) endif enddo enddo c call ga_sync() if(me.eq.0)then print *, 'OK' print *, ' ' call ffflush(6) endif c--------------------------- c status= ga_destroy(g_a) end ga-5.9.2/global/testing/ngatest_src/ndim_NGA_GET.src000066400000000000000000000040551500715745200222050ustar00rootroot00000000000000 subroutine m4_func_NGA_GET(m4_test_type, m4_ndim) implicit none #include "mafdecls.fh" #include "global.fh" c integer n,m integer ndim parameter (n = m4_n) parameter (m = (m4_n**m4_ndim)/100) parameter (ndim = m4_ndim) m4_data_type a(substr(m4_array, 1, eval(m4_ndim*2-1))) m4_data_type b(substr(m4_array, 1, eval(m4_ndim*2-1))) integer lo(ndim),hi(ndim),dims(ndim),ld(ndim) integer g_a integer chunk(ndim) integer i, total, loop integer lop(ndim), hip(ndim) m4_data_type val integer nproc, me logical status c nproc = ga_nnodes() me = ga_nodeid() c c---------------------- initialize the GA ----------------------- c initialize the chunk, dims, ld, and calculate the number c of elements total=1 do i = 1,ndim chunk(i) = 0 dims(i) = n ld(i) = n total = total * dims(i) enddo c c*** Create global arrays if (.not. nga_create(m4_MT, ndim, dims, 'a', chunk, g_a)) $ call ga_error(' ga_create failed ',1) c call ga_sync() c c------------------------------- NGA_GET ---------------------------- m4_print_info(nga_get) c val = m4_conv(234) call ga_fill(g_a,val) call ga_sync() c call m4_util_fill_array(m4_test_type)(a,total,val) c call ga_sync() do i = 1,ndim lop(i) = 1 hip(i) = n enddo do loop = 1, MAXLOOP call random_range(lop,hip,lo,hi,ndim) if(me.eq.0 .and. Mod(loop,10).eq.0)then call print_range(loop,lo,hi,ndim) endif c call nga_get(g_a,lo,hi, $ b(substr(m4_lo_all, 1, eval(m4_ndim*6-1))),ld) call m4_util_compare_patches(m4_test_type)(0d0,total, $ a,lo,hi,ndim,dims,total,b,lo,hi,ndim,dims) enddo c call ga_sync() if(me.eq.0)then print *, 'OK' print *, ' ' call ffflush(6) endif c--------------------------- c status= ga_destroy(g_a) end ga-5.9.2/global/testing/ngatest_src/ndim_NGA_PERIODIC_ACC.src000066400000000000000000000061641500715745200234350ustar00rootroot00000000000000 subroutine m4_func_NGA_PERIODIC_ACC(m4_test_type, m4_ndim) implicit none #include "mafdecls.fh" #include "global.fh" c integer n integer ndim parameter (n = m4_n) parameter (ndim = m4_ndim) m4_data_type a(substr(m4_array, 1, eval(m4_ndim*2-1))) m4_data_type b(substr(m4_array, 1, eval(m4_ndim*2-1))) m4_data_type c(substr(m4_array, 1, eval(m4_ndim*2-1))) integer lo(ndim),hi(ndim),lop(ndim),hip(ndim) integer blo(ndim),bhi(ndim) integer dims(ndim),ld(ndim) integer g_a integer chunk(ndim) integer i,total,loop m4_data_type alpha double precision drand integer nproc, me logical status c nproc = ga_nnodes() me = ga_nodeid() c c---------------------- initialize the GA ----------------------- c initialize the chunk, dims, ld, and calculate the number c of elements total=1 do i = 1,ndim chunk(i) = 0 dims(i) = n ld(i) = n enddo c c*** Create global arrays if (.not. nga_create(m4_MT, ndim, dims, 'a', chunk, g_a)) $ call ga_error(' ga_create failed ',1) c call ga_sync() c c------------------------ NGA_PERIODIC_ACC ---------------------- m4_print_info(nga_periodic_acc) c total = 1 do i = 1,ndim lo(i) = 1 hi(i) = n total = total*dims(i) enddo c call m4_util_init_array(m4_test_type)(a,total) call m4_util_init_array(m4_test_type)(b,total) c c initialize array g_a call ga_fill(g_a,m4_conv(123)) call ga_sync() c alpha = m4_rand(me*2+1) c do loop = 1, MAXLOOP if(mod(loop,nproc).eq.me) then call random_range_outbound(lo,hi,lop,hip,ndim) if(Mod(loop,10).eq.0) then call print_range(loop,lop,hip,ndim) endif call ga_init_fence() c keep a copy of the original patch call nga_periodic_put(g_a,lop,hip, $ b(substr(m4_lo_all,1,eval(m4_ndim*6-1))),ld) call ga_fence() call ga_init_fence() call nga_periodic_acc(g_a,lop,hip, $ a(substr(m4_lo_all,1,eval(m4_ndim*6-1))),ld,alpha) call ga_fence() call ga_init_fence() call nga_periodic_get(g_a,lop,hip, $ c(substr(m4_lo_all,1,eval(m4_ndim*6-1))),ld) call ga_fence() c c check the result do i=1,ndim blo(i) = 1 bhi(i) = hip(i)-lop(i)+1 enddo c scale the local copy of array call m4_util_scale_patch(m4_test_type)(total, $ m4_conv(1),b,blo,bhi,ndim,dims, $ alpha,a,blo,bhi,ndim,dims) call m4_util_compare_patches(m4_test_type)(1d-2, $ total,b,blo,bhi,ndim,dims,total,c,blo,bhi,ndim,dims) endif call ga_sync() enddo c call ga_sync() if(me.eq.0)then print *, 'OK' print *, ' ' call ffflush(6) endif c--------------------------- c status= ga_destroy(g_a) end ga-5.9.2/global/testing/ngatest_src/ndim_NGA_PERIODIC_GET.src000066400000000000000000000043631500715745200234650ustar00rootroot00000000000000 subroutine m4_func_NGA_PERIODIC_GET(m4_test_type, m4_ndim) implicit none #include "mafdecls.fh" #include "global.fh" c integer n integer ndim parameter (n = m4_n) parameter (ndim = m4_ndim) m4_data_type a(substr(m4_array, 1, eval(m4_ndim*2-1))) m4_data_type b(substr(m4_array, 1, eval(m4_ndim*2-1))) integer lo(ndim),hi(ndim),lop(ndim),hip(ndim) integer blo(ndim),bhi(ndim) integer dims(ndim),ld(ndim) integer g_a integer chunk(ndim) integer i,total,loop m4_data_type val integer nproc, me logical status c nproc = ga_nnodes() me = ga_nodeid() c c---------------------- initialize the GA ----------------------- c initialize the chunk, dims, ld, and calculate the number c of elements total=1 do i = 1,ndim chunk(i) = 0 dims(i) = n ld(i) = n enddo c c*** Create global arrays if (.not. nga_create(m4_MT, ndim, dims, 'a', chunk, g_a)) $ call ga_error(' ga_create failed ',1) c call ga_sync() c c------------------------ NGA_PERIODIC_GET ---------------------- m4_print_info(nga_periodic_get) c total = 1 do i = 1,ndim lo(i) = 1 hi(i) = n total = total*dims(i) enddo c c initialize array a val = m4_conv(234) call ga_fill(g_a,val) call ga_sync() c call m4_util_fill_array(m4_test_type)(a,total,val) c do loop = 1, MAXLOOP call random_range_outbound(lo,hi,lop,hip,ndim) if(me.eq.0 .and. Mod(loop,10).eq.0) then call print_range(loop,lop,hip,ndim) endif call nga_periodic_get(g_a,lop,hip, $ b(substr(m4_lo_all,1,eval(m4_ndim*6-1))),ld) call ga_sync() c c check the result do i=1,ndim blo(i) = 1 bhi(i) = hip(i)-lop(i)+1 enddo call m4_util_compare_patches(m4_test_type)(0d0, $ total,a,blo,bhi,ndim,dims,total,b,blo,bhi,ndim,dims) enddo c call ga_sync() if(me.eq.0)then print *, 'OK' print *, ' ' call ffflush(6) endif c--------------------------- c status= ga_destroy(g_a) end ga-5.9.2/global/testing/ngatest_src/ndim_NGA_PERIODIC_PUT.src000066400000000000000000000047521500715745200235200ustar00rootroot00000000000000 subroutine m4_func_NGA_PERIODIC_PUT(m4_test_type, m4_ndim) implicit none #include "mafdecls.fh" #include "global.fh" c integer n integer ndim parameter (n = m4_n) parameter (ndim = m4_ndim) m4_data_type a(substr(m4_array, 1, eval(m4_ndim*2-1))) m4_data_type b(substr(m4_array, 1, eval(m4_ndim*2-1))) integer lo(ndim),hi(ndim),lop(ndim),hip(ndim) integer blo(ndim),bhi(ndim) integer dims(ndim),ld(ndim) integer g_a integer chunk(ndim) integer i,total,loop integer nproc, me logical status c nproc = ga_nnodes() me = ga_nodeid() c c---------------------- initialize the GA ----------------------- c initialize the chunk, dims, ld, and calculate the number c of elements total=1 do i = 1,ndim chunk(i) = 0 dims(i) = n ld(i) = n enddo c c*** Create global arrays if (.not. nga_create(m4_MT, ndim, dims, 'a', chunk, g_a)) $ call ga_error(' ga_create failed ',1) c call ga_sync() c c------------------------ NGA_PERIODIC_PUT ---------------------- m4_print_info(nga_periodic_put) c total = 1 do i = 1,ndim lo(i) = 1 hi(i) = n total = total*dims(i) enddo c call m4_util_init_array(m4_test_type)(a,total) c c initialize array a call ga_zero(g_a) call ga_sync() c do loop = 1, MAXLOOP if(mod(loop,nproc).eq.me) then call random_range_outbound(lo,hi,lop,hip,ndim) if(Mod(loop,10).eq.0) then call print_range(loop,lop,hip,ndim) endif call ga_init_fence() call nga_periodic_put(g_a,lop,hip, $ a(substr(m4_lo_all,1,eval(m4_ndim*6-1))),ld) call ga_fence() call ga_init_fence() call nga_periodic_get(g_a,lop,hip, $ b(substr(m4_lo_all,1,eval(m4_ndim*6-1))),ld) call ga_fence() c c check the result do i=1,ndim blo(i) = 1 bhi(i) = hip(i)-lop(i)+1 enddo call m4_util_compare_patches(m4_test_type)(0d0, $ total,a,blo,bhi,ndim,dims,total,b,blo,bhi,ndim,dims) endif call ga_sync() enddo c call ga_sync() if(me.eq.0)then print *, 'OK' print *, ' ' call ffflush(6) endif c--------------------------- c status= ga_destroy(g_a) end ga-5.9.2/global/testing/ngatest_src/ndim_NGA_PUT.src000066400000000000000000000051211500715745200222310ustar00rootroot00000000000000 subroutine m4_func_NGA_PUT(m4_test_type, m4_ndim) implicit none #include "mafdecls.fh" #include "global.fh" c integer n,m integer ndim parameter (n = m4_n) parameter (m = (m4_n**m4_ndim)/100) parameter (ndim = m4_ndim) m4_data_type a(substr(m4_array, 1, eval(m4_ndim*2-1))) m4_data_type b(substr(m4_array, 1, eval(m4_ndim*2-1))) integer lo(ndim),hi(ndim),dims(ndim),ld(ndim) integer g_a integer chunk(ndim) integer i, total, loop integer lop(ndim),hip(ndim) integer elems, count_elems integer nproc, me integer proc logical status c nproc = ga_nnodes() me = ga_nodeid() c c---------------------- initialize the GA ----------------------- c initialize the chunk, dims, ld, and calculate the number c of elements total=1 do i = 1,ndim chunk(i) = 0 dims(i) = n ld(i) = n total = total * dims(i) enddo c c*** Create global arrays if (.not. nga_create(m4_MT, ndim, dims, 'a', chunk, g_a)) $ call ga_error(' ga_create failed ',1) c call ga_sync() c c------------------------------- NGA_PUT ---------------------------- m4_print_info(nga_put) c proc = nproc-1 -me ! access other process memory call nga_distribution(g_a, proc, lo,hi) elems = count_elems(lo,hi,ndim) call m4_util_init_array(m4_test_type)(a,total) c call ga_sync() if(elems.gt.0) then call nga_put(g_a,lo,hi, $ a(substr(m4_lo_all, 1, eval(m4_ndim*6-1))),ld) do loop = 1, MAXLOOP call random_range(lo,hi,lop,hip,ndim) if(me.eq.0 .and. Mod(loop,10).eq.0)then call print_range(loop,lop,hip,ndim) endif call nga_put(g_a,lop,hip, $ a(substr(m4_lop_all, 1, eval(m4_ndim*7-1))),ld) enddo call nga_get(g_a,lo,hi, $ b(substr(m4_lo_all, 1, eval(m4_ndim*6-1))),ld) c call m4_util_compare_patches(m4_test_type)(0d0,total, $ a,lo,hi,ndim,dims,total,b,lo,hi,ndim,dims) else c so that the random_range can be call the same number of times c in other words, drand can generate the same number for the c collective operations do loop=1, MAXLOOP call random_range(lo,hi,lop,hip,ndim) enddo endif c call ga_sync() if(me.eq.0)then print *, 'OK' print *, ' ' call ffflush(6) endif c--------------------------- c status= ga_destroy(g_a) end ga-5.9.2/global/testing/ngatest_src/ndim_NGA_SCALE_PATCH.src000066400000000000000000000053211500715745200233310ustar00rootroot00000000000000 subroutine m4_func_NGA_SCALE_PATCH(m4_test_type, m4_ndim) implicit none #include "mafdecls.fh" #include "global.fh" c integer n,m integer ndim parameter (n = m4_n) parameter (m = (m4_n**m4_ndim)/100) parameter (ndim = m4_ndim) m4_data_type a(substr(m4_array, 1, eval(m4_ndim*2-1))) m4_data_type b(substr(m4_array, 1, eval(m4_ndim*2-1))) integer dims(ndim) integer g_a integer chunk(ndim) integer i, total integer elems, count_elems integer loop integer lop(ndim), hip(ndim) integer lo(ndim), hi(ndim) double precision drand m4_data_type val integer nproc, me logical status c nproc = ga_nnodes() me = ga_nodeid() c c---------------------- initialize the GA ----------------------- c initialize the chunk, dims, ld, and calculate the number c of elements total=1 do i = 1,ndim chunk(i) = 0 dims(i) = n total = total * dims(i) enddo c c*** Create global arrays if (.not. nga_create(m4_MT, ndim, dims, 'a', chunk, g_a)) $ call ga_error(' ga_create failed ',1) c call ga_sync() c c--------------------------- NGA_SCALE_PATCH ------------------------- m4_print_info(nga_scale_patch) c c initialize GA call m4_util_init_array(m4_test_type)(a,total) call nga_distribution(g_a, me, lop, hip) elems = count_elems(lop, hip, ndim) if(elems.gt.0) call nga_put(g_a,lop,hip, $ a(substr(m4_lop_all, 1, eval(m4_ndim*7-1))),dims) c call ga_sync() do i = 1,ndim lop(i) = 1 hip(i) = n enddo c do loop=1, 10 call random_range(lop,hip,lo,hi,ndim) if(me.eq.0) $ call print_range(loop,lo,hi,ndim) c$$$ $ print *, loop,': scale [',(lo(i),':',hi(i), i=1,ndim),']' c the random number to scale val = m4_rand(1) c c keep a copy of the origian array call nga_get(g_a,lo,hi, $ a(substr(m4_lo_all, 1, eval(m4_ndim*6-1))),dims) c call nga_scale_patch(g_a,lo,hi,val) c call nga_get(g_a,lo,hi, $ b(substr(m4_lo_all, 1, eval(m4_ndim*6-1))),dims) c c check the result call m4_util_scale_patch(m4_test_type)(total, $ val,a,lo,hi,ndim,dims, $ m4_conv(0),b,lo,hi,ndim,dims) call m4_util_compare_patches(m4_test_type)(1d-10,total, $ a,lo,hi,ndim,dims,total,b,lo,hi,ndim,dims) c enddo c call ga_sync() if(me.eq.0)then print *, 'OK' print *, ' ' call ffflush(6) endif c--------------------------- c status= ga_destroy(g_a) end ga-5.9.2/global/testing/ngatest_src/ndim_NGA_SCATTER.src000066400000000000000000000051061500715745200226710ustar00rootroot00000000000000 subroutine m4_func_NGA_SCATTER(m4_test_type, m4_ndim) implicit none #include "mafdecls.fh" #include "global.fh" c integer n,m integer ndim parameter (n = m4_n) parameter (m = (m4_n**m4_ndim)/100) parameter (ndim = m4_ndim) m4_data_type a(substr(m4_array, 1, eval(m4_ndim*2-1))) m4_data_type b(substr(m4_array, 1, eval(m4_ndim*2-1))) integer lo(ndim),dims(ndim),ld(ndim) integer g_a integer chunk(ndim) integer i, j, total, loop m4_data_type v(m) integer d(ndim, m) double precision drand integer unique,unique_index integer nproc, me logical status c nproc = ga_nnodes() me = ga_nodeid() c c---------------------- initialize the GA ----------------------- c initialize the chunk, dims, ld, and calculate the number c of elements total=1 do i = 1,ndim chunk(i) = 0 dims(i) = n ld(i) = n total = total * dims(i) enddo c c*** Create global arrays if (.not. nga_create(m4_MT, ndim, dims, 'a', chunk, g_a)) $ call ga_error(' ga_create failed ',1) c call ga_sync() c c------------------------------- NGA_SCATTER ---------------------------- m4_print_info(nga_scatter) c do loop = 1, MAXLOOP call ga_sync() c initialize the index array and the array containing values do i = 1, m c generate indices unique_index = 0 do while(unique_index.eq.0) do j=1,ndim d(j,i) = int(drand(0)*real(n)) + 1 enddo c unique_index = unique(d,ndim,m,i) enddo c v(i) = m4_rand a(substr(m4_ind_all, 1, eval(m4_ndim*7-1)))=v(i) enddo c c scatter the v to the global array call nga_scatter(g_a, v, d, m) call ga_sync() c c collect each elements and compare do i = 1, m do j=1, ndim lo(j) = d(j,i) enddo call nga_get(g_a,lo,lo, $ b(substr(m4_lo_all, 1, eval(m4_ndim*6-1))),ld) c c compare the results if(a(substr(m4_lo_all, 1, eval(m4_ndim*6-1))) .ne. $ b(substr(m4_lo_all, 1, eval(m4_ndim*6-1)))) then call ga_error('bye', 0) endif enddo enddo c call ga_sync() if(me.eq.0)then print *, 'OK' print *, ' ' call ffflush(6) endif c--------------------------- c status= ga_destroy(g_a) end ga-5.9.2/global/testing/ngatest_src/ndim_NGA_SCATTER_ACC.src000066400000000000000000000071361500715745200233440ustar00rootroot00000000000000 subroutine m4_func_NGA_SCATTER_ACC(m4_test_type, m4_ndim) implicit none #include "mafdecls.fh" #include "global.fh" c integer n,m integer ndim parameter (n = m4_n) parameter (m = (m4_n**m4_ndim)/100) parameter (ndim = m4_ndim) m4_data_type a(substr(m4_array, 1, eval(m4_ndim*2-1))) m4_data_type b(substr(m4_array, 1, eval(m4_ndim*2-1))) integer lo(ndim),dims(ndim),ld(ndim) integer g_a integer chunk(ndim) integer i, j, total, loop, idx m4_data_type v(m) integer d(ndim, m) double precision drand m4_data_type alpha integer unique,unique_index integer nproc, me logical status c nproc = ga_nnodes() me = ga_nodeid() c c---------------------- initialize the GA ----------------------- c initialize the chunk, dims, ld, and calculate the number c of elements total=1 do i = 1,ndim chunk(i) = 0 dims(i) = n ld(i) = n total = total * dims(i) enddo c c*** Create global arrays if (.not. nga_create(m4_MT, ndim, dims, 'a', chunk, g_a)) $ call ga_error(' ga_create failed ',1) c call ga_sync() c c-------------------------- NGA_SCATTER_ACC ----------------------- m4_print_info(nga_scatter_acc) c alpha=m4_rand(me*2+1) c c do loop = 1, MAXLOOP call ga_sync() c initialize the index array and the array containing values do i = 1, m c generate indices unique_index = 0 do while(unique_index.eq.0) c generate random number idx = int(drand(0)*(real(total/nproc))) $ +me*total/nproc if(idx .eq. 0) idx = idx+1 c c convert to ndim call conv_1ton(ndim,dims,idx,lo) c do j=1,ndim d(j,i) = lo(j) enddo c unique_index = unique(d,ndim,m,i) enddo c v(i) = m4_rand a(substr(m4_ind_all, 1, eval(m4_ndim*7-1)))=v(i) enddo c c the result should be do i = 1, m do j=1, ndim lo(j) = d(j,i) enddo call nga_get(g_a,lo,lo, $ b(substr(m4_lo_all, 1, eval(m4_ndim*6-1))),ld) a(substr(m4_ind_all, 1, eval(m4_ndim*7-1)))= $ a(substr(m4_ind_all, 1, eval(m4_ndim*7-1))) $ *alpha $ +b(substr(m4_lo_all, 1, eval(m4_ndim*6-1))) enddo c call ga_sync() c c scatter the v to the global array call nga_scatter_acc(g_a, v, d, m, alpha) call ga_sync() c c collect each elements and compare do i = 1, m do j=1, ndim lo(j) = d(j,i) enddo call nga_get(g_a,lo,lo, $ b(substr(m4_lo_all, 1, eval(m4_ndim*6-1))),ld) c c compare the results if(abs(a(substr(m4_lo_all, 1, eval(m4_ndim*6-1))) - $ b(substr(m4_lo_all, 1, eval(m4_ndim*6-1)))) $ .gt. 1d-5) then print *,'a=', $ a(substr(m4_lo_all, 1, eval(m4_ndim*6-1))), $ 'b=', $ b(substr(m4_lo_all, 1, eval(m4_ndim*6-1))), $ 'i=',i,'alpha=',alpha,'v=',v(i) call ga_error('bye', 0) endif enddo enddo c call ga_sync() if(me.eq.0)then print *, 'OK' print *, ' ' call ffflush(6) endif c--------------------------- c status= ga_destroy(g_a) end ga-5.9.2/global/testing/ngatest_src/ndim_main.src000066400000000000000000000270601500715745200220260ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif #define MAXLOOP 100 program test implicit none #include "mafdecls.fh" #include "global.fh" integer stack, heap c c*** Intitialize a message passing library c #include "mp3.fh" c c*** Intitialize the GA package call ga_initialize() c if(ga_nodeid().eq.0)print *,ga_nnodes(),' nodes' c if(ga_uses_ma()) then stack = 200000 c stack = 100000 else stack = 60000 c stack = 20000 endif heap = stack if (.not. ma_init(MT_DBL, heap, stack)) $ call ga_error("ma init failed",heap+stack) c c c test GA_FILL ifelse(m4_test_GA_FILL, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_GA_FILL(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_GA_FILL(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_GA_FILL(m4_test_type, m4_i)') ')') c c test NGA_GET ifelse(m4_test_NGA_GET, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_NGA_GET(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_NGA_GET(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_NGA_GET(m4_test_type, m4_i)') ')') c c test NGA_PUT ifelse(m4_test_NGA_PUT, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_NGA_PUT(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_NGA_PUT(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_NGA_PUT(m4_test_type, m4_i)') ')') c c test NGA_ACC ifelse(m4_test_NGA_ACC, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_NGA_ACC(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_NGA_ACC(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_NGA_ACC(m4_test_type, m4_i)') ')') c c test NGA_PERIODIC_GET ifelse(m4_test_NGA_PERIODIC_GET, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_NGA_PERIODIC_GET(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_NGA_PERIODIC_GET(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_NGA_PERIODIC_GET(m4_test_type, m4_i)') ')') c c test NGA_PERIODIC_PUT ifelse(m4_test_NGA_PERIODIC_PUT, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_NGA_PERIODIC_PUT(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_NGA_PERIODIC_PUT(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_NGA_PERIODIC_PUT(m4_test_type, m4_i)') ')') c c test NGA_PERIODIC_ACC ifelse(m4_test_NGA_PERIODIC_ACC, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_NGA_PERIODIC_ACC(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_NGA_PERIODIC_ACC(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_NGA_PERIODIC_ACC(m4_test_type, m4_i)') ')') c c test NGA_FILL_PATCH ifelse(m4_test_NGA_FILL_PATCH, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_NGA_FILL_PATCH(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_NGA_FILL_PATCH(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_NGA_FILL_PATCH(m4_test_type, m4_i)') ')') c c test NGA_COPY_PATCH ifelse(m4_test_NGA_COPY_PATCH, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_NGA_COPY_PATCH(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_NGA_COPY_PATCH(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_NGA_COPY_PATCH(m4_test_type, m4_i)') ')') c c test NGA_SCALE_PATCH ifelse(m4_test_NGA_SCALE_PATCH, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_NGA_SCALE_PATCH(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_NGA_SCALE_PATCH(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_NGA_SCALE_PATCH(m4_test_type, m4_i)') ')') c c test NGA_ADD_PATCH ifelse(m4_test_NGA_ADD_PATCH, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_NGA_ADD_PATCH(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_NGA_ADD_PATCH(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_NGA_ADD_PATCH(m4_test_type, m4_i)') ')') c c test NGA_DOT_PATCH ifelse(m4_test_NGA_DOT_PATCH, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_NGA_DOT_PATCH(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_NGA_DOT_PATCH(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_NGA_DOT_PATCH(m4_test_type, m4_i)') ')') c c test NGA_SCATTER ifelse(m4_test_NGA_SCATTER, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_NGA_SCATTER(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_NGA_SCATTER(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_NGA_SCATTER(m4_test_type, m4_i)') ')') c c test NGA_SCATTER_ACC ifelse(m4_test_NGA_SCATTER_ACC, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_NGA_SCATTER_ACC(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_NGA_SCATTER_ACC(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_NGA_SCATTER_ACC(m4_test_type, m4_i)') ')') c c test NGA_GATHER ifelse(m4_test_NGA_GATHER, `yes', `forloop(`m4_i', m4_from, m4_to, `ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_int) divert call m4_func_NGA_GATHER(m4_test_type, m4_i)') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dbl) divert call m4_func_NGA_GATHER(m4_test_type, m4_i)') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_test_type') define(m4_test_type, m4_test_type_dcpl) divert call m4_func_NGA_GATHER(m4_test_type, m4_i)') ')') c if(ga_nodeid().eq.0) print *, 'All tests successful' c call ga_terminate() call MP_FINALIZE() end ga-5.9.2/global/testing/ngatest_src/ndim_util.src000066400000000000000000000106671500715745200220640ustar00rootroot00000000000000c fill array with random numbers subroutine m4_util_fill_array(m4_test_type)(a,n,val) implicit none integer n m4_data_type a(n),val integer i do i= 1, n a(i) = val enddo end c initialize the array with random numbers subroutine m4_util_init_array(m4_test_type)(a,n) implicit none integer n m4_data_type a(n) double precision drand integer i do i= 1, n a(i) = m4_rand(i) enddo end c if the elements do match, stop the program subroutine m4_util_compare_patches(m4_test_type)(eps, $ total1,array1,lo1,hi1,ndim1,dims1, $ total2,array2,lo2,hi2,ndim2,dims2) implicit none double precision eps integer ndim1,ndim2,total1,total2 m4_data_type array1(total1),array2(total2) integer lo1(ndim1),hi1(ndim1),lo2(ndim2),hi2(ndim2) integer dims1(ndim1),dims2(ndim2) integer next_index integer index1,index2 double precision diff,maxval c c initialize index1 and index2, searching from zeros index1 = 0 index2 = 0 c compare corresponding elements in each array index1 = next_index(index1,total1,ndim1,lo1,hi1,dims1) index2 = next_index(index2,total2,ndim2,lo2,hi2,dims2) do while((index1.ne.0).and.(index2.ne.0)) diff = abs(array1(index1) - array2(index2)) maxval = max(abs(array1(index1)), abs(array2(index2))) if((maxval.eq.0).or.(maxval.lt.eps)) maxval = 1 if(eps .lt. abs(diff)/maxval) then print *, 'Error: Comparison failed!' print *, array1(index1), array2(index2) call ga_error('bye',0) endif index1 = next_index(index1,total1,ndim1,lo1,hi1,dims1) index2 = next_index(index2,total2,ndim2,lo2,hi2,dims2) enddo c c at this point both index1 and index2 should be 0 if((index1.ne.0).or.(index2.ne.0)) then print *, 'Error: # of elems dont match' call ga_error('bye',0) endif c end c do patch = patch + buf * alpha subroutine m4_util_scale_patch(m4_test_type)(total, $ alpha,arr1,lo1,hi1,ndim1,dims1,beta,arr2,lo2,hi2,ndim2,dims2) implicit none integer ndim1,ndim2,total integer lo1(ndim1),hi1(ndim1),lo2(ndim2),hi2(ndim2) integer dims1(ndim1),dims2(ndim2) m4_data_type arr1(total),arr2(total) m4_data_type alpha, beta integer next_index integer ind1, ind2 c ind1 = 0 ind2 = 0 ind1 = next_index(ind1,total,ndim1,lo1,hi1,dims1) ind2 = next_index(ind2,total,ndim2,lo2,hi2,dims2) c do while(ind1.ne.0) arr1(ind1) = arr1(ind1)*alpha + arr2(ind2)*beta ind1 = next_index(ind1,total,ndim1,lo1,hi1,dims1) ind2 = next_index(ind2,total,ndim2,lo2,hi2,dims2) enddo c end c transpose an array subroutine m4_util_transpose(m4_test_type)(a1,a2,total,ndim,dims) implicit none integer ndim,total integer dims(ndim) m4_data_type a1(total),a2(total) integer i, j integer idx integer bv(m4_max_dim), bunit(m4_max_dim) c bv(1)=0 bunit(1)=1 do i=2, ndim bv(i) = 0 bunit(i) = bunit(i-1) * dims(i-1) enddo c do i=1, total idx = 1 do j=1, ndim idx = idx + bv(j) * bunit(ndim-j+1) if(mod(i,bunit(j)).eq.0) bv(j) = bv(j) + 1 if(bv(j).ge.dims(j)) bv(j) = 0 enddo c print *, 'i = ',i, 'idx = ',idx a2(idx) = a1(i) enddo c do i=1, total a1(i) = a2(i) enddo c end c do patch = patch + buf * alpha m4_data_type function m4_util_dot_patch(m4_test_type)( $ total,arr1,lo1,hi1,ndim1,dims1,arr2,lo2,hi2,ndim2,dims2) implicit none integer ndim1,ndim2,total integer lo1(ndim1),hi1(ndim1),lo2(ndim2),hi2(ndim2) integer dims1(ndim1),dims2(ndim2) m4_data_type arr1(total),arr2(total) integer next_index integer ind1, ind2 m4_data_type res c ind1 = 0 ind2 = 0 res = 0 ind1 = next_index(ind1,total,ndim1,lo1,hi1,dims1) ind2 = next_index(ind2,total,ndim2,lo2,hi2,dims2) c do while(ind1.ne.0) res = res + arr1(ind1)*arr2(ind2) ind1 = next_index(ind1,total,ndim1,lo1,hi1,dims1) ind2 = next_index(ind2,total,ndim2,lo2,hi2,dims2) enddo c m4_util_dot_patch(m4_test_type) = res c end ga-5.9.2/global/testing/ngatest_src/ndim_util_comm.src000066400000000000000000000105031500715745200230640ustar00rootroot00000000000000 subroutine random_range(lo,hi,lop,hip,ndim) implicit none integer lo(1),hi(1),lop(1),hip(1),ndim integer i, range, swap, val,iran double precision drand iran(range) = int(drand(0)*dble(range)) + 1 c iran(range) = range/2 do i = 1, ndim range = hi(i)-lo(i)+1 val = iran(range) lop(i) = lo(i) + val val = iran(range) hip(i) = hi(i) - val if(hip(i) .lt. lop(i))then swap =hip(i) hip(i)=lop(i) lop(i)=swap endif hip(i)=MIN(hip(i),hi(i)) lop(i)=MAX(lop(i),lo(i)) enddo end c c subroutine random_range_outbound(lo,hi,lop,hip,ndim) implicit none integer lo(1),hi(1),lop(1),hip(1),ndim integer i, range, val,iran double precision drand iran(range) = int(drand(0)*dble(range)) + 1 c iran(range) = range/2 do i = 1, ndim range = 2*(hi(i)-lo(i)+1) val = iran(range) lop(i) = lo(i) + val range = hi(i)-lo(i)+1 val = iran(range) hip(i) = lop(i) + range - val c hip(i) = hip(i)-hi(i) lop(i) = lop(i)-hi(i) enddo end c c integer function count_elems(lo,hi,ndim) implicit none integer lo(1),hi(1),ndim,elems,i elems=1 do i=1,ndim elems = elems*(hi(i)-lo(i)+1) enddo count_elems = elems end c c get the next available nindex in the range of lo and hi integer function next_index(ind,total,ndim,lo,hi,dims) implicit none integer ind,total,ndim,lo(ndim),hi(ndim),dims(ndim) integer i integer indx(m4_max_dim),nindex c nindex = ind + 1 200 call conv_1ton(ndim,dims,nindex,indx) c c test if indx(i) is in the range of lo(i) and hi(i) do i=1,ndim if((indx(i).lt.lo(i)).or.(indx(i).gt.hi(i))) then nindex = nindex + 1 if(nindex.gt.total) then next_index = 0 goto 300 else goto 200 endif endif enddo c next_index = nindex 300 end c testing if the indices are unique integer function unique(ind,ndim,m,n) implicit none integer ndim,m,n integer ind(ndim,m) integer i,j,marker c unique = 1 do i = 1, n-1 marker = 0 do j = 1, ndim if(ind(j,n).eq.ind(j,i)) marker = marker + 1 enddo c if(marker.eq.ndim) unique = 0 enddo c end c subroutine prnt_rng(me,lo,hi,ndim) implicit none integer me,ndim integer lo(ndim),hi(ndim) integer i c print *, me,': array section [',(lo(i),':',hi(i),i=1,ndim),']' c end c divide the space into equal size patches according to nproc c and calculate my lo and hi subroutine my_space(me,nproc,ndim,total,dims,lo,hi) implicit none integer me,nproc,ndim,total integer dims(ndim),lo(ndim),hi(ndim) integer div,lop,hip,i c div = total/nproc c lop = div * me + 1 c if(me.eq.(nproc-1)) then hip = total else hip = div * (me+1) endif c call conv_1ton(ndim,dims,lop,lo) call conv_1ton(ndim,dims,hip,hi) c c swap the indices if the lo if larger thant hi do i = 1,ndim if(lo(i).gt.hi(i)) then if(i.eq.ndim) call ga_error('bye',0) lo(i) = 1 lo(i+1) = lo(i+1) + 1 endif enddo end c convert the index from one dimension to n dimension subroutine conv_1ton(ndim,dims,ind1,indn) implicit none integer ndim integer dims(ndim) integer ind1,indn(ndim) integer range(m4_max_dim),remainder,i c remainder = ind1 c get the range of each dimension do i=1,ndim if(i.eq.1) then range(i) = dims(i) else range(i) = range(i-1) * dims(i) endif enddo c c get the indices in each dimension do i = ndim,1,-1 if(i.ne.1) then indn(i) = remainder/range(i-1) remainder = remainder - indn(i)*range(i-1) if(remainder.eq.0) then remainder = range(i-1) else indn(i) = indn(i) + 1 endif else indn(i) = remainder endif enddo c end ga-5.9.2/global/testing/ngatest_src/ngatest.def000066400000000000000000000256651500715745200215200ustar00rootroot00000000000000divert(-1) # ------------------------------------------------------------ # up to 8 dimensions # testing from "m4_from" to "m4_to" dimensions define(m4_from, m4_dim_from) define(m4_to, m4_dim_to) # data type can be integer, double precision, or double complex define(m4_data_type_int, `integer') define(m4_data_type_dbl, `double precision') define(m4_data_type_dcpl, `double complex') define(m4_test_type_int, `int') define(m4_test_type_dbl, `dbl') define(m4_test_type_dcpl, `dcpl') define(m4_max_dim, 8) #-------------------------------------------------------------- # n values for each dimension define(m4_n_1dim, 20000) define(m4_n_2dim, 140) define(m4_n_3dim, 27) define(m4_n_4dim, 11) define(m4_n_5dim, 7) define(m4_n_6dim, 5) define(m4_n_7dim, 4) define(m4_n_8dim, 3) # define(m4_n_1dim, 2000) # define(m4_n_2dim, 100) # define(m4_n_3dim, 20) # define(m4_n_4dim, 10) # define(m4_n_5dim, 5) # define(m4_n_6dim, 4) # define(m4_n_7dim, 3) # define(m4_n_8dim, 2) # to access n value define(m4_n_val, m4_n_$1dim) # common definitions define(m4_array, (n,n,n,n,n,n,n,n,n)) define(m4_lo_all, (lo(1),lo(2),lo(3),lo(4),lo(5),lo(6),lo(7),lo(8))) define(m4_lop_all, (lop(1),lop(2),lop(3),lop(4),lop(5),lop(6),lop(7),lop(8))) define(m4_ind_all, (d(1,i),d(2,i),d(3,i),d(4,i),d(5,i),d(6,i),d(7,i),d(8,i))) define(m4_alo_all, (alo(1),alo(2),alo(3),alo(4),alo(5),alo(6),alo(7),alo(8))) define(m4_ahi_all, (ahi(1),ahi(2),ahi(3),ahi(4),ahi(5),ahi(6),ahi(7),ahi(8))) define(m4_ald_all, (ald(1),ald(2),ald(3),ald(4),ald(5),ald(6),ald(7),ald(8))) define(m4_blo_all, (blo(1),blo(2),blo(3),blo(4),blo(5),blo(6),blo(7),blo(8))) define(m4_bhi_all, (bhi(1),bhi(2),bhi(3),bhi(4),bhi(5),bhi(6),bhi(7),bhi(8))) define(m4_bld_all, (bld(1),bld(2),bld(3),bld(4),bld(5),bld(6),bld(7),bld(8))) define(m4_clo_all, (clo(1),clo(2),clo(3),clo(4),clo(5),clo(6),clo(7),clo(8))) define(m4_chi_all, (chi(1),chi(2),chi(3),chi(4),chi(5),chi(6),chi(7),chi(8))) define(m4_cld_all, (cld(1),cld(2),cld(3),cld(4),cld(5),cld(6),cld(7),cld(8))) define(m4_dlo_all, (dlo(1),dlo(2),dlo(3),dlo(4),dlo(5),dlo(6),dlo(7),dlo(8))) define(m4_func_GA_FILL, testit_GA_FILL_$1$2()) define(m4_func_NGA_PUT, testit_NGA_PUT_$1$2()) define(m4_func_NGA_GET, testit_NGA_GET_$1$2()) define(m4_func_NGA_ACC, testit_NGA_ACC_$1$2()) define(m4_func_NGA_SCATTER, testit_NGA_SCATTER_$1$2()) define(m4_func_NGA_SCATTER_ACC, testit_NGA_SCATTER_ACC_$1$2()) define(m4_func_NGA_GATHER, testit_NGA_GATHER_$1$2()) define(m4_func_NGA_PERIODIC_PUT, testit_NGA_PERIODIC_PUT_$1$2()) define(m4_func_NGA_PERIODIC_GET, testit_NGA_PERIODIC_GET_$1$2()) define(m4_func_NGA_PERIODIC_ACC, testit_NGA_PERIODIC_ACC_$1$2()) define(m4_func_NGA_FILL_PATCH, testit_NGA_FILL_PATCH_$1$2()) define(m4_func_NGA_COPY_PATCH, testit_NGA_COPY_PATCH_$1$2()) define(m4_func_NGA_SCALE_PATCH, testit_NGA_SCALE_PATCH_$1$2()) define(m4_func_NGA_ADD_PATCH, testit_NGA_ADD_PATCH_$1$2()) define(m4_func_NGA_DOT_PATCH, testit_NGA_DOT_PATCH_$1$2()) define(m4_util_fill_array, fill_array_$1) define(m4_util_init_array, init_array_$1) define(m4_util_compare_patches, compare_patches_$1) define(m4_util_scale_patch, scale_patch_$1) define(m4_util_dot_patch, dot_patch_$1) define(m4_util_transpose, transpose_$1) define(m4_nga_dot_patch, `nga_$1_patch') changequote([,]) define(m4_print_info, [if(me.eq.0)then print *, ' ' print *, 'TESTING $1' print *, ' - Data Type: m4_data_type' print *, ' - Dimension: m4_ndim' print *, ' - Running on',nproc,'processes (processors)' call ffflush(6) endif]) changequote(`,') # utility # forloop(i, from, to, stmt) define(`forloop', `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')') define(`_forloop',`$4`'ifelse($1, `$3', , `define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')') #--------------------------------------------------------------------- # insert the main program divert include(`ngatest_src/ndim_main.src') c----------------- divert(-1) # insert the testing subroutines divert ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_data_type') undefine(`m4_test_type') undefine(`m4_MT') undefine(`m4_conv') undefine(`m4_rand') define(m4_data_type, m4_data_type_int) define(m4_test_type, m4_test_type_int) define(m4_MT, `MT_INT') define(m4_conv, `int($1)') define(m4_rand, `int(drand(0) * $1 * 2)') define(m4_dot, `idot') divert forloop(`m4_i', m4_from, m4_to, `divert(-1) undefine(`m4_n') undefine(`m4_ndim') define(m4_n, m4_n_val(m4_i)) define(m4_ndim, m4_i) divert ifelse(m4_test_GA_FILL, `yes', ` include(`ngatest_src/ndim_GA_FILL.src')') ifelse(m4_test_NGA_PUT, `yes', ` include(`ngatest_src/ndim_NGA_PUT.src')') ifelse(m4_test_NGA_GET, `yes', ` include(`ngatest_src/ndim_NGA_GET.src')') ifelse(m4_test_NGA_ACC, `yes', ` include(`ngatest_src/ndim_NGA_ACC.src')') ifelse(m4_test_NGA_SCATTER, `yes', ` include(`ngatest_src/ndim_NGA_SCATTER.src')') ifelse(m4_test_NGA_SCATTER_ACC, `yes', ` include(`ngatest_src/ndim_NGA_SCATTER_ACC.src')') ifelse(m4_test_NGA_GATHER, `yes', ` include(`ngatest_src/ndim_NGA_GATHER.src')') ifelse(m4_test_NGA_PERIODIC_PUT, `yes', ` include(`ngatest_src/ndim_NGA_PERIODIC_PUT.src')') ifelse(m4_test_NGA_PERIODIC_GET, `yes', ` include(`ngatest_src/ndim_NGA_PERIODIC_GET.src')') ifelse(m4_test_NGA_PERIODIC_ACC, `yes', ` include(`ngatest_src/ndim_NGA_PERIODIC_ACC.src')') ifelse(m4_test_NGA_FILL_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_FILL_PATCH.src')') ifelse(m4_test_NGA_COPY_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_COPY_PATCH.src')') ifelse(m4_test_NGA_SCALE_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_SCALE_PATCH.src')') ifelse(m4_test_NGA_ADD_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_ADD_PATCH.src')') ifelse(m4_test_NGA_DOT_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_DOT_PATCH.src')')')') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_data_type') undefine(`m4_test_type') undefine(`m4_MT') undefine(`m4_conv') undefine(`m4_rand') undefine(`m4_dot') define(m4_data_type, m4_data_type_dbl) define(m4_test_type, m4_test_type_dbl) define(m4_MT, `MT_DBL') define(m4_conv, `dble($1)') define(m4_rand, `drand(0) * $1 * 2') define(m4_dot, `ddot') divert forloop(`m4_i', m4_from, m4_to, `divert(-1) undefine(`m4_n') undefine(`m4_ndim') define(m4_n, m4_n_val(m4_i)) define(m4_ndim, m4_i) divert ifelse(m4_test_GA_FILL, `yes', ` include(`ngatest_src/ndim_GA_FILL.src')') ifelse(m4_test_NGA_PUT, `yes', ` include(`ngatest_src/ndim_NGA_PUT.src')') ifelse(m4_test_NGA_GET, `yes', ` include(`ngatest_src/ndim_NGA_GET.src')') ifelse(m4_test_NGA_ACC, `yes', ` include(`ngatest_src/ndim_NGA_ACC.src')') ifelse(m4_test_NGA_SCATTER, `yes', ` include(`ngatest_src/ndim_NGA_SCATTER.src')') ifelse(m4_test_NGA_SCATTER_ACC, `yes', ` include(`ngatest_src/ndim_NGA_SCATTER_ACC.src')') ifelse(m4_test_NGA_GATHER, `yes', ` include(`ngatest_src/ndim_NGA_GATHER.src')') ifelse(m4_test_NGA_PERIODIC_PUT, `yes', ` include(`ngatest_src/ndim_NGA_PERIODIC_PUT.src')') ifelse(m4_test_NGA_PERIODIC_GET, `yes', ` include(`ngatest_src/ndim_NGA_PERIODIC_GET.src')') ifelse(m4_test_NGA_PERIODIC_ACC, `yes', ` include(`ngatest_src/ndim_NGA_PERIODIC_ACC.src')') ifelse(m4_test_NGA_FILL_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_FILL_PATCH.src')') ifelse(m4_test_NGA_COPY_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_COPY_PATCH.src')') ifelse(m4_test_NGA_SCALE_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_SCALE_PATCH.src')') ifelse(m4_test_NGA_ADD_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_ADD_PATCH.src')') ifelse(m4_test_NGA_DOT_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_DOT_PATCH.src')')')') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_data_type') undefine(`m4_test_type') undefine(`m4_MT') undefine(`m4_conv') undefine(`m4_rand') undefine(`m4_dot') define(m4_data_type, m4_data_type_dcpl) define(m4_test_type, m4_test_type_dcpl) define(m4_MT, `MT_DCPL') define(m4_conv, `dcmplx(dble($1),dble(0))') define(m4_rand, `dcmplx(drand(0) * $1 * 2, $ -drand(0) * $1 * 2)') define(m4_dot, `zdot') divert forloop(`m4_i', m4_from, m4_to, `divert(-1) undefine(`m4_n') undefine(`m4_ndim') define(m4_n, m4_n_val(m4_i)) define(m4_ndim, m4_i) divert ifelse(m4_test_GA_FILL, `yes', ` include(`ngatest_src/ndim_GA_FILL.src')') ifelse(m4_test_NGA_PUT, `yes', ` include(`ngatest_src/ndim_NGA_PUT.src')') ifelse(m4_test_NGA_GET, `yes', ` include(`ngatest_src/ndim_NGA_GET.src')') ifelse(m4_test_NGA_ACC, `yes', ` include(`ngatest_src/ndim_NGA_ACC.src')') ifelse(m4_test_NGA_SCATTER, `yes', ` include(`ngatest_src/ndim_NGA_SCATTER.src')') ifelse(m4_test_NGA_SCATTER_ACC, `yes', ` include(`ngatest_src/ndim_NGA_SCATTER_ACC.src')') ifelse(m4_test_NGA_GATHER, `yes', ` include(`ngatest_src/ndim_NGA_GATHER.src')') ifelse(m4_test_NGA_PERIODIC_PUT, `yes', ` include(`ngatest_src/ndim_NGA_PERIODIC_PUT.src')') ifelse(m4_test_NGA_PERIODIC_GET, `yes', ` include(`ngatest_src/ndim_NGA_PERIODIC_GET.src')') ifelse(m4_test_NGA_PERIODIC_ACC, `yes', ` include(`ngatest_src/ndim_NGA_PERIODIC_ACC.src')') ifelse(m4_test_NGA_FILL_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_FILL_PATCH.src')') ifelse(m4_test_NGA_COPY_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_COPY_PATCH.src')') ifelse(m4_test_NGA_SCALE_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_SCALE_PATCH.src')') ifelse(m4_test_NGA_ADD_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_ADD_PATCH.src')') ifelse(m4_test_NGA_DOT_PATCH, `yes', ` include(`ngatest_src/ndim_NGA_DOT_PATCH.src')')')') c----------------------- c Utility functions divert(-1) # insert the utility program divert include(`ngatest_src/ndim_util_comm.src') ifelse(m4_test_int, `yes', `divert(-1) undefine(`m4_data_type') undefine(`m4_test_type') undefine(`m4_rand') define(m4_data_type, m4_data_type_int) define(m4_test_type, m4_test_type_int) define(m4_rand, `int(drand(0) * i * 2)') divert include(`ngatest_src/ndim_util.src')') ifelse(m4_test_dbl, `yes', `divert(-1) undefine(`m4_data_type') undefine(`m4_test_type') undefine(`m4_rand') define(m4_data_type, m4_data_type_dbl) define(m4_test_type, m4_test_type_dbl) define(m4_rand, `drand(0) * i * 2') divert include(`ngatest_src/ndim_util.src')') ifelse(m4_test_dcpl, `yes', `divert(-1) undefine(`m4_data_type') undefine(`m4_test_type') undefine(`m4_rand') define(m4_data_type, m4_data_type_dcpl) define(m4_test_type, m4_test_type_dcpl) define(m4_rand, `dcmplx(drand(0) * i * 2, $ -drand(0) * i * 2)') divert include(`ngatest_src/ndim_util.src')') ga-5.9.2/global/testing/normc.c000066400000000000000000000307211500715745200163260ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #include "ga.h" #include "macdecls.h" #include "mp3.h" #define NDIM 5 /* dimension of matrices */ #define LDIM NDIM*NDIM /* dimension of vectors */ /* Test the norm1 and norm_infinity functions on 1 and 2 dimensional global * arrays */ void do_work() { int ONE=1, TWO=2; /* useful constants */ int g_a; int me=GA_Nodeid(), nproc=GA_Nnodes(); int i, j, m, n, ndim, dim1, dim2, ld; int dims[2]; int lo[2], hi[2]; double rnorm_infinity, rnorm1, norm; int *iptr; long *lptr; float *fptr; double *dptr; DoubleComplex *zptr; SingleComplex *cptr; double delta; /* Test vectors */ ndim = ONE; dims[0] = LDIM; rnorm1 = (double)(LDIM*(LDIM-1)/2); rnorm_infinity = (double)(LDIM-1); /* Test integers */ if (me==0) printf("Testing integer vector\n"); g_a = NGA_Create_handle(); NGA_Set_data(g_a, ndim, dims, C_INT); NGA_Allocate(g_a); NGA_Distribution(g_a, me, lo, hi); dim1 = hi[0]-lo[0]+1; NGA_Access(g_a, lo, hi, &iptr, &ld); for (i=0; i #endif #if HAVE_STDLIB_H # include #endif #include "macdecls.h" #include "ga.h" #include "mp3.h" #define N 10 /* first dimension */ #define NDIM 4 /* number of dimensions */ #define BASE 0 /*#define NEW_API*/ /*\ print subscript of ndim dimensional array with two strings before and after \*/ void print_subscript(char *pre,int ndim, int subscript[], char* post) { int i; printf("%s [",pre); for(i=0;iadims[d] ){ map[offset] = regions[i].lo[d]; offset++; block[d]++; adims[d]= regions[i].hi[d]; } if(me==0){ printf("Distribution map contains %d elements\n",offset); print_subscript("number of blocks for each dimension",ndim,block,"\n"); print_subscript("distribution map",offset,map,"\n\n"); fflush(stdout); } if(me==0)printf("Creating array B applying distribution of A\n"); # ifdef USE_DUPLICATE g_b = GA_Duplicate(g_a,"array B"); # else g_b = NGA_Create_irreg(MT_F_DBL, NDIM, dims, "array B", block,map); # endif if(!g_b) GA_Error("create failed: B",1); if(me==0)printf("OK\n\n"); free(proclist); free(regions); free(map); GA_Print_distribution(g_b); GA_Sync(); if(me==0){ printf("\nCompare distributions of A and B\n"); if(GA_Compare_distr(g_a,g_b)) printf("Failure: distributions NOT identical\n"); else printf("Success: distributions identical\n"); fflush(stdout); } if(me==0){ printf("\nAccessing local elements of A: set them to the owner process id\n"); fflush(stdout); } GA_Sync(); NGA_Distribution(g_a,me,lo,hi); if(hi[0]>=0){/* -1 means no elements stored on this processor */ double *ptr; int locdim[NDIM]; NGA_Access(g_a, lo,hi, &ptr, ld); for(i=0;i=0){ char msg[100]; sprintf(msg,"%d: leading dimensions",me); print_subscript(msg,ndim-1,ld,"\n"); fflush(stdout); } GA_Sync(); } GA_Sync(); if(me==0)printf("\nRandomly checking the update using ga_get on array sections\n"); GA_Sync(); /* show ga_get working and verify array updates * every process does N random gets * for simplicity get only a single row at a time */ srand(me); /* different seed for every process */ hi[ndim-1]=adims[ndim-1] -1 + BASE; for(i=1;i #endif #if HAVE_STDLIB_H # include #endif #include "macdecls.h" #include "ga.h" #include "mp3.h" #define N 10 /* first dimension */ #define NDIM 4 /* number of dimensions */ #ifdef USE_FAPI #define BASE 1 #define GROWS_FIRST 0 #else #define BASE 0 #define GROWS_FIRST (NDIM-1) #endif /*\ print subscript of ndim dimensional array with two strings before and after \*/ void print_subscript(char *pre,int ndim, int subscript[], char* post) { int i; printf("%s [",pre); for(i=0;iadims[d] ){ map[offset] = regions[i].lo[d]; offset++; block[d]++; adims[d]= regions[i].hi[d]; } if(me==0){ printf("Distribution map contains %d elements\n",offset); print_subscript("number of blocks for each dimension",ndim,block,"\n"); print_subscript("distribution map",offset,map,"\n\n"); fflush(stdout); } if(me==0)printf("Creating array B applying distribution of A\n"); # ifdef USE_DUPLICATE g_b = GA_Duplicate(g_a,"array B"); # else g_b = NGA_Create_irreg(MT_F_DBL, NDIM, dims, "array B", block,map); # endif if(!g_b) GA_Error("create failed: B",1); if(me==0)printf("OK\n\n"); free(proclist); free(regions); free(map); GA_Print_distribution(g_b); GA_Sync(); if(me==0){ printf("\nCompare distributions of A and B\n"); if(GA_Compare_distr(g_a,g_b)) printf("Failure: distributions NOT identical\n"); else printf("Success: distributions identical\n"); fflush(stdout); } if(me==0){ printf("\nAccessing local elements of A: set them to the owner process id\n"); fflush(stdout); } GA_Sync(); NGA_Distribution(g_a,me,lo,hi); if(hi[0]>=0){ /* -1 means no array elements stored on this processor */ double *ptr; char msg[100]; int locdim[NDIM]; NGA_Access(g_a, lo,hi, &ptr, ld); sprintf(msg,"%d: leading dimensions",me); print_subscript(msg,ndim-1,ld,"\n"); for(i=0;i Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_sync() do i = 1, n do j = 1, n if (b(i,j) .ne. 0.0d0) then write(6,*) me,' zero ', i, j, b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format('> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc if (mod(ij,nproc) .eq. me) then ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) * write(6,4) me, ilo, ihi, jlo, jhi * 4 format(' node ',i2,' checking put ',4i4) * call ffflush(6) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c call util_dfill(n*n, 0.0d0, b, 1) * call ga_print(g_a,1) call ga_get(g_a, 1, n, 1, n, b, n) * write(6,*) ' after get' * call output(b, 1, n, 1, n, n, n, 1) c do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif call ga_sync() c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format('> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_dfill(n*n, 0.0d0, b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif sum1 = 0.0d0 do j = jlo, jhi do i = ilo, ihi sum1 = sum1 + b(i,j) if (b(i,j) .ne. a(i,j)) then write(6,*) i, j, b(i,j), a(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo c enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) call ffflush(6) endif call ga_sync() c c Each node accumulates into disjoint sections of the array c if (me .eq. 0) then write(6,9) 9 format('> Checking accumulate ... ') call ffflush(6) endif call ga_sync() c crap = drand(12345) ! Same seed for each process do j = 1, n do i = 1, n c b(i,j) = drand(0) b(i,j) = i+j enddo enddo c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc c x = drand(0) x = 10. ilo = i ihi = min(i+inc-1, n) if(ihi.eq.n-1)ihi=n c ihi = min(i+inc, n) jlo = j jhi = min(j+inc-1, n) if(jhi.eq.n-1)jhi=n c jhi = min(j+inc-1, n) * call ffflush(6) if (mod(ij,nproc) .eq. me) then c print *, me, 'checking accumulate ',ilo,ihi,jlo,jhi,x * 11 format(' node ',i2,' checking accumulate ',4i4) * call ffflush(6) call ga_acc(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n, x) endif ij = ij + 1 c c Each process applies all updates to its local copy c do jj = jlo, jhi do ii = ilo, ihi a(ii,jj) = a(ii,jj) + x * b(ii,jj) enddo enddo enddo enddo call ga_sync() c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if(MISMATCH(b(i,j),a(i,j)))then write(6,*) ' acc ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' disjoint ga_acc is OK' write(6,*) endif c c overlapping accumulate call ga_sync() gp_b = ga_create_handle() dims(1) = 2*n dims(2) = 2*n call ga_set_data(gp_b,ndim,dims,MT_DBL) call ga_set_array_name(gp_b,'b_parent') if (.not.ga_allocate(gp_b)) then call ga_error('ga_create failed for second parent array ',-1) endif g_b = ga_create_handle() dims(1) = n dims(2) = n call ga_set_data(g_b,ndim,dims,MT_DBL) call ga_set_array_name(g_b,'b') if (.not.ga_overlay(g_b, gp_b)) then call ga_error('ga_create failed for second array ',-1) endif c call ga_zero(g_b) call ga_acc(g_b, n/2, n/2, n/2, n/2, 1d0, 1, 1d0) call ga_sync() if (me.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, b(1,1), 1) x = abs(b(1,1) -1d0*nproc) if(x.gt. 1d-10)then write(6,*)'val=',b(1,1),' expected=',nproc, x call ga_error('overlapping accumulate failed',-1) endif if (me.eq.0) then write(6,*) write(6,*) ' overlapping ga_acc is OK' write(6,*) endif endif c c Check the ga_add function c if (me .eq. 0) then write(6,91) 91 format('> Checking add ...') call ffflush(6) endif c c crap = drand(12345) ! Everyone has same seed do j = 1, n do i = 1, n b(i,j) = drand(0) a(i,j) = 0.1d0*a(i,j) + 0.9d0*b(i,j) enddo enddo if (me.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) call ga_add(0.1d0, g_a, 0.9d0, g_b, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if(MISMATCH(b(i,j), a(i,j)))then write(6,*) ' add ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' add is OK ' write(6,*) endif call ga_sync() c c Check the ddot function c if (me .eq. 0) then write(6,19) 19 format('> Checking ddot ...') call ffflush(6) endif crap = drand(12345) ! Everyone has same seed sum1 = 0.0d0 do j = 1, n do i = 1, n b(i,j) = drand(0) sum1 = sum1 + a(i,j)*b(i,j) enddo enddo if (me.eq.0) then call ga_put(g_b, 1, n, 1, n, b, n) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() sum2 = ga_ddot(g_a,g_b) if(MISMATCH(sum1, sum2))then write(6,*) ' ddot wrong ', sum1, sum2 call ffflush(6) call ga_error('... exiting ',-1) else if (me.eq.0) then write(6,*) write(6,*) ' ddot is OK ' write(6,*) endif c c Check the ga_scale function c if (me .eq. 0) then write(6,92) 92 format('> Checking scale ...') call ffflush(6) endif call ga_scale(g_a, 0.123d0) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n a(i,j) = a(i,j)*0.123d0 if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' dscal ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' scale is OK ' write(6,*) endif c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c call ga_sync() if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_sync() itmp = iran(nproc)-1 if(me.eq.itmp) then do loop = 1,m ilo = iran(n) jlo = iran(n) ijv(1,loop) = ilo ijv(2,loop) = jlo enddo call nga_gather(g_a, v, ijv, m) do loop = 1,m ilo= ijv(1,loop) jlo= ijv(2,loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',-1) endif enddo endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_sync() if(me.eq.iran(ga_nnodes())-1) then do loop = 1,m ilo = iran(n) jlo = iran(n) ijv(1,loop) = ilo ijv(2,loop) = jlo c v(loop) = DSIN(a(ilo,jlo)+b(ilo,jlo)) v(loop) = 1d0 *(ilo+jlo) enddo call nga_scatter(g_a, v, ijv, m) do loop = 1,m ilo= ijv(1,loop) jlo= ijv(2,loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) c if(v(loop) .ne. w(loop))then if(w(loop) .ne. 1d0 *(ilo+jlo) )then write(6,927)me,ilo,jlo,w(loop),1d0 *(ilo+jlo),loop 927 format(i3,' scatter ',i5,',',i5,',',f7.1,',',f7.1,',',i5) call ffflush(6) call ga_error('... exiting ',-1) endif enddo endif call ga_sync() enddo c call ga_sync() c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c call ga_sync() c c scatter-acc available in GA ver. 3.0 if (me.eq.0) then write(6,*) write(6,*) ' checking scatter-accumulate' write(6,*) endif c crap = drand(1234) call ga_zero(g_a) c do j = 1, n do i = 1, n b(i,j) =0. enddo enddo c x = .1d0 ii =n do jj = 1,1 call ga_sync() do loop = 1, ii c generate unique i,j pairs 10 continue i = iran(n) j=iran(n) if (nfound(i,j, ijv, loop-1) ) goto 10 ijv(1,loop) = i ijv(2,loop) = j v(loop) = 1d0 *(i+j) b(i,j) = b(i,j) + nproc*x*v(loop) ! update local ref. array enddo call nga_scatter_acc(g_a,v,ijv,ii,x) c call ga_sync() c c check the result c call ga_get(g_a, 1, n, 1,n, a, n) do loop = 1,ii i = ijv(1,loop) j = ijv(2,loop) if(MISMATCH(a(i,j),b(i,j)))then print *,'Error',i,j,loop,a(i,j),'expected=',b(i,j) * if(me.eq.0)then * do ii=1,loop * print *,'element',ii, iv(ii),jv(ii) * enddo * endif call ga_error('scatter-acc error in trial ',jj) endif enddo call ga_sync() enddo call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' scatter-accumulate is OK' write(6,*) endif c c Delete the global arrays c status = ga_destroy(g_b) status = ga_destroy(gp_b) status = ga_destroy(g_a) status = ga_destroy(gp_a) c end c----------------------------------------------------------------- subroutine check_complex_float() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,m parameter (n = 60) parameter (m = 2*n) complex a(n,n), b(n,n), v(m),w(m) integer ndim, dims(2), chunk(2), p_mirror integer iv(m), jv(m) logical status integer g_a, g_b integer gp_a, gp_b integer iran, i,j, loop,nloop,maxloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, ii, jj, nnodes parameter (maxloop = 100) integer maxproc parameter (maxproc = 4096) double precision crap, real double precision nwords complex x, sum1, sum2, factor integer lprocs, inode, iproc, lproc intrinsic int iran(i) = int(drand(0)*real(i)) + 1 c nproc = ga_nnodes() me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) nloop = Min(maxloop,n) c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n a(i,j) = cmplx(real(i-1), real((j-1)*n)) b(i,j) = cmplx(-1d0,1d0) enddo enddo c c Create a global array c c print *,ga_nodeid(), ' creating array' call ffflush(6) c call setdbg(1) ndim = 2 dims(1) = 2*n dims(2) = 2*n gp_a = ga_create_handle() call ga_set_data(gp_a,ndim,dims,MT_SCPL) call ga_set_array_name(gp_a,'a_parent') status = ga_allocate(gp_a) if (.not. status) then write(6,*) ' ga_create failed for parent array' call ga_error('... exiting ',-1) endif dims(1) = n dims(2) = n g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_SCPL) call ga_set_array_name(g_a,'a') status = ga_overlay(g_a, gp_a) if (.not. status) then write(6,*) ' ga_create failed' call ga_error('... exiting ',-1) endif dims(1) = 2*n dims(2) = 2*n gp_b = ga_create_handle() call ga_set_data(gp_b,ndim,dims,MT_SCPL) call ga_set_array_name(gp_b,'b_parent') if (.not.ga_allocate(gp_b)) then call ga_error('ga_create failed for second parent array ',-1) endif dims(1) = n dims(2) = n g_b = ga_create_handle() call ga_set_data(g_b,ndim,dims,MT_SCPL) call ga_set_array_name(g_b,'b') if (.not.ga_overlay(g_b, gp_b)) then call ga_error('ga_create failed for second array ',-1) endif call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) call ga_sync() c c Zero the array c if (me .eq. 0) then write(6,21) 21 format(/'> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_sync() do i = 1, n do j = 1, n if(b(i,j).ne.(0d0,0d0)) then write(6,*) me,' zero ', i, j, b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format(/'> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/4 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc if (mod(ij,nproc) .eq. me) then ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) c do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif call ga_sync() c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format(/'> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif do j = jlo, jhi do i = ilo, ihi if (b(i,j) .ne. a(i,j)) then write(6,*)'error:', i, j, b(i,j), a(i,j) call ga_error('... exiting ',-1) endif enddo enddo c enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) call ffflush(6) endif call ga_sync() c c Each node accumulates into disjoint sections of the array c if (me .eq. 0) then write(6,9) 9 format(/'> Checking accumulate ... ') call ffflush(6) endif call ga_sync() c crap = drand(12345) ! Same seed for each process do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0),drand(1)) b(i,j) = cmplx(real(i),real(j)) enddo enddo c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc c x = cmplx(drand(0),0.333d0) c x = cmplx(0.333d0,0) * x = cmplx(0d0,0d0) x = 0 ilo = i ihi = min(i+inc-1, n) if(ihi.eq.n-1)ihi=n jlo = j jhi = min(j+inc-1, n) if(jhi.eq.n-1)jhi=n if (mod(ij,nproc) .eq. me) then call ga_acc(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n, x) endif ij = ij + 1 c c Each process applies all updates to its local copy c do jj = jlo, jhi do ii = ilo, ihi a(ii,jj) = a(ii,jj) + x * b(ii,jj) enddo enddo enddo enddo call ga_sync() c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (MISMATCHF(b(i,j), a(i,j)))then write(6,*) ' acc ', me, i, j, a(i,j), b(i,j) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' disjoint ga_acc is OK' write(6,*) endif c c overlapping accumulate c call ga_zero(g_b) call ga_acc(g_b, n/2, n/2, n/2, n/2, (1,-1), 1, (1,0)) call ga_sync() if (me.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, x, 1) if (MISMATCHF(x, ((1d0,-1d0)*nproc)))then c if(error.gt. (1d-8))then write(6,*)'val=',x,' expected=(',nproc,',',-nproc,')' call ga_error('overlapping accumulate failed',-1) endif write(6,*) write(6,*) ' overlapping ga_acc is OK' write(6,*) endif c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif call ga_sync() if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c c c Check the ga_scale function c if (me .eq. 0) then write(6,92) 92 format(/'> Checking scale ...') call ffflush(6) endif factor = (1d0,-1d0) call ga_scale(g_a, factor) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n a(i,j) = a(i,j)*factor if (MISMATCHF(b(i,j), a(i,j)))then write(6,*) ' fscal ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' scale is OK ' write(6,*) endif c c Check scatter&gather c call ga_sync() if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_sync() itmp = iran(nproc)-1 if(me.eq.itmp) then do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo call ga_gather(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',-1) endif enddo endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_sync() if(me.eq.iran(ga_nnodes())-1) then do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo v(loop) = (1d0,-1d0) *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) if(w(loop) .ne. (1d0,-1d0) *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', (1d0,-1d0) *(ilo+jlo) call ffflush(6) endif enddo endif call ga_sync() enddo c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c c Check ga_add c if (me .eq. 0) then write(6,91) 91 format(/'> Checking add ...') call ffflush(6) endif call ga_get(g_a, 1, n, 1, n, a, n) crap = drand(12345) ! Everyone has same seed do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0), drand(1)) a(i,j) = (0.1d0,-.1d0)*a(i,j) + (.9d0,-.9d0)*b(i,j) enddo enddo if (me.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) call ga_add((0.1,-.1), g_a, (0.9,-.9), g_b, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (MISMATCHF(b(i,j), a(i,j)))then write(6,*) ' add ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' add is OK ' write(6,*) endif call ga_sync() c c Check the cdot function c if (me .eq. 0) then write(6,19) 19 format(/'> Checking cdot ...') call ffflush(6) endif crap = drand(12345) ! Everyone has same seed sum1 = (0.0d0,0.d0) do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0), drand(1)) sum1 = sum1 + a(i,j)*b(i,j) enddo enddo if (me.eq.0) then call ga_put(g_b, 1, n, 1, n, b, n) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() sum2 = ga_cdot(g_a,g_b) if (MISMATCHF(sum1, sum2))then write(6,*) ' cdot wrong ', sum1, sum2 call ffflush(6) call ga_error('... exiting ',-1) else if (me.eq.0) then write(6,*) write(6,*) ' cdot is OK ' write(6,*) endif c c Delete the global arrays c status = ga_destroy(g_b) status = ga_destroy(gp_b) status = ga_destroy(g_a) status = ga_destroy(gp_a) c end c----------------------------------------------------------------- subroutine check_complex() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,m parameter (n = 60) parameter (m = 2*n) double complex a(n,n), b(n,n), v(m),w(m) integer ndim, dims(2), chunk(2), p_mirror integer iv(m), jv(m) logical status integer g_a, g_b integer gp_a, gp_b integer iran, i,j, loop,nloop,maxloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, ii, jj, nnodes parameter (maxloop = 100) integer maxproc parameter (maxproc = 4096) double precision crap, real double precision nwords double complex x, sum1, sum2, factor integer lprocs, inode, iproc, lproc intrinsic int iran(i) = int(drand(0)*real(i)) + 1 c nproc = ga_nnodes() me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) nloop = Min(maxloop,n) c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n a(i,j) = cmplx(dble(i-1), dble((j-1)*n)) b(i,j) = cmplx(-1d0,1d0) enddo enddo c c Create a global array c c print *,ga_nodeid(), ' creating array' call ffflush(6) c call setdbg(1) ndim = 2 dims(1) = 2*n dims(2) = 2*n gp_a = ga_create_handle() call ga_set_data(gp_a,ndim,dims,MT_DCPL) call ga_set_array_name(gp_a,'a_parent') status = ga_allocate(gp_a) if (.not. status) then write(6,*) ' ga_create failed for parent array' call ga_error('... exiting ',-1) endif dims(1) = n dims(2) = n g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_DCPL) call ga_set_array_name(g_a,'a') status = ga_overlay(g_a, gp_a) if (.not. status) then write(6,*) ' ga_create failed' call ga_error('... exiting ',-1) endif dims(1) = 2*n dims(2) = 2*n gp_b = ga_create_handle() call ga_set_data(gp_b,ndim,dims,MT_DCPL) call ga_set_array_name(gp_b,'b_parent') if (.not.ga_allocate(gp_b)) then call ga_error('ga_create failed for second parent array ',-1) endif dims(1) = n dims(2) = n g_b = ga_create_handle() call ga_set_data(g_b,ndim,dims,MT_DCPL) call ga_set_array_name(g_b,'b') if (.not.ga_overlay(g_b, gp_b)) then call ga_error('ga_create failed for second array ',-1) endif call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) call ga_sync() c c Zero the array c if (me .eq. 0) then write(6,21) 21 format('> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_sync() do i = 1, n do j = 1, n if(b(i,j).ne.(0d0,0d0)) then write(6,*) me,' zero ', i, j, b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format('> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc if (mod(ij,nproc) .eq. me) then ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c call util_qfill(n*n, (0d0,0d0), b, 1) call ga_get(g_a, 1, n, 1, n, b, n) c do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif call ga_sync() c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format('> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_qfill(n*n, (0.0d0,0d0), b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif do j = jlo, jhi do i = ilo, ihi if (b(i,j) .ne. a(i,j)) then write(6,*)'error:', i, j, b(i,j), a(i,j) call ga_error('... exiting ',-1) endif enddo enddo c enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) call ffflush(6) endif call ga_sync() c c Each node accumulates into disjoint sections of the array c if (me .eq. 0) then write(6,9) 9 format('> Checking accumulate ... ') call ffflush(6) endif call ga_sync() c crap = drand(12345) ! Same seed for each process do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0),drand(1)) b(i,j) = cmplx(dble(i),dble(j)) enddo enddo c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc c x = cmplx(drand(0),0.333d0) c x = cmplx(0.333d0,0) * x = cmplx(0d0,0d0) x = 0 ilo = i ihi = min(i+inc-1, n) if(ihi.eq.n-1)ihi=n jlo = j jhi = min(j+inc-1, n) if(jhi.eq.n-1)jhi=n if (mod(ij,nproc) .eq. me) then call ga_acc(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n, x) endif ij = ij + 1 c c Each process applies all updates to its local copy c do jj = jlo, jhi do ii = ilo, ihi a(ii,jj) = a(ii,jj) + x * b(ii,jj) enddo enddo enddo enddo call ga_sync() c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' acc ', me, i, j, a(i,j), b(i,j) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' disjoint ga_acc is OK' write(6,*) endif c c overlapping accumulate c call ga_zero(g_b) call ga_acc(g_b, n/2, n/2, n/2, n/2, (1d0,-1d0), 1, (1d0,0d0)) call ga_sync() if (me.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, x, 1) if (MISMATCH(x, ((1d0,-1d0)*nproc)))then c if(error.gt. (1d-8))then write(6,*)'val=',x,' expected=(',nproc,',',-nproc,')' call ga_error('overlapping accumulate failed',-1) endif write(6,*) write(6,*) ' overlapping ga_acc is OK' write(6,*) endif c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif call ga_sync() if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c c c Check the ga_scale function c if (me .eq. 0) then write(6,92) 92 format('> Checking scale ...') call ffflush(6) endif factor = (1d0,-1d0) call ga_scale(g_a, factor) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n a(i,j) = a(i,j)*factor if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' dscal ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' scale is OK ' write(6,*) endif c c Check scatter&gather c call ga_sync() if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_sync() itmp = iran(nproc)-1 if(me.eq.itmp) then do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo call ga_gather(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',-1) endif enddo endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_sync() if(me.eq.iran(ga_nnodes())-1) then do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo v(loop) = (1d0,-1d0) *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) if(w(loop) .ne. (1d0,-1d0) *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', (1d0,-1d0) *(ilo+jlo) call ffflush(6) endif enddo endif call ga_sync() enddo c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c c Check ga_add c if (me .eq. 0) then write(6,91) 91 format('> Checking add ...') call ffflush(6) endif call ga_get(g_a, 1, n, 1, n, a, n) crap = drand(12345) ! Everyone has same seed do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0), drand(1)) a(i,j) = (0.1d0,-.1d0)*a(i,j) + (.9d0,-.9d0)*b(i,j) enddo enddo if (me.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) call ga_add((0.1d0,-.1d0), g_a, (0.9d0,-.9d0), g_b, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' add ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' add is OK ' write(6,*) endif call ga_sync() c c Check the zdot function c if (me .eq. 0) then write(6,19) 19 format('> Checking zdot ...') call ffflush(6) endif crap = drand(12345) ! Everyone has same seed sum1 = (0.0d0,0.d0) do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0), drand(1)) sum1 = sum1 + a(i,j)*b(i,j) enddo enddo if (me.eq.0) then call ga_put(g_b, 1, n, 1, n, b, n) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() sum2 = ga_zdot(g_a,g_b) if (MISMATCH(sum1, sum2))then write(6,*) ' zdot wrong ', sum1, sum2 call ffflush(6) call ga_error('... exiting ',-1) else if (me.eq.0) then write(6,*) write(6,*) ' zdot is OK ' write(6,*) endif c c Delete the global arrays c status = ga_destroy(g_b) status = ga_destroy(gp_b) status = ga_destroy(g_a) status = ga_destroy(gp_a) c end c----------------------------------------------------------------- subroutine check_int() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n parameter (n = 128) integer a(n,n), b(n,n) logical status integer g_a, gp_a integer iran, i, j, loop, nloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, dimi, dimj, ii, jj integer ndim, dims(2), chunk(2), p_mirror, nnodes integer lprocs, inode, iproc,lproc double precision nwords parameter (nloop = 100) integer maxproc parameter (maxproc = 4096) double precision crap, sum1, real integer buf integer map(5,maxproc), found, np,k intrinsic int iran(i) = int(drand(0)*real(i)) + 1 c nproc = ga_nnodes() me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n a(i,j) = i-1 + (j-1)*1000 enddo enddo c c Create a global array c ndim = 2 dims(1) = 2*n dims(2) = 2*n chunk(1) = 0 chunk(2) = 0 gp_a = ga_create_handle() call ga_set_data(gp_a,ndim,dims,MT_INT) call ga_set_array_name(gp_a,'a_parent') if (.not.ga_allocate(gp_a)) then write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',-1) endif dims(1) = n dims(2) = n g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_INT) call ga_set_array_name(g_a,'a') if (.not.ga_overlay(g_a, gp_a)) then write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',-1) endif c c Zero the array c if (me .eq. 0) then write(6,21) 21 format('> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) do i = 1, n do j = 1, n if (b(i,j) .ne. 0) then write(6,*) ' zero ', me, i, j, b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format('> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc if (mod(ij,nproc) .eq. me) then ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) c write(6,4) me, ilo, ihi, jlo, jhi c4 format(' node ',i2,' checking put ',4i4) c call ffflush(6) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c if(me.eq.0)then call ga_get(g_a, 1, n, 1, n, b, n) do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo endif call ga_sync() c if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format('> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_ifill(n*n, 0.0d0, b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) c if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif c sum1 = 0.0d0 do j = jlo, jhi do i = ilo, ihi sum1 = sum1 + b(i,j) if (b(i,j) .ne. a(i,j)) then write(6,*) 'error ', i, j, b(i,j), a(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) endif c call ga_sync() if (me .eq. 0 .and. n.gt.7) then write(6,*) write(6,*) '> Checking ga_print_patch --- should see ' write(6,*)' [2002 3002 4002 5002 6002]' write(6,*)' [2003 3003 4003 5003 6003]' write(6,*)' [2004 3004 4004 5004 6004]' write(6,*) call ffflush(6) endif if(n.gt.5) call ga_print_patch(g_a,3,5,3,7,0) c call ga_sync() c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*) '> Checking read_inc ... ' write(6,*) call ffflush(6) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc inc =5 c every processor will be operating on somebody elses data c lproc = nproc - me - 1 c call ga_distribution(g_a,lproc,ilo,ihi,jlo,jhi) c dimi = ihi-ilo dimj = jhi-jlo c write(6,111) me,ilo,ihi,jlo,jhi,dimi,dimj 111 format(i2,'..',4i8,'.',2i8) call ga_sync() if(ilo .gt.0 .and. jhi .gt. 0)then do loop = 1,nloop ii= IABS(iran(dimi)) - 1 jj= IABS(iran(dimj)) - 1 i=ilo + Mod(ii,dimi) j=jlo + Mod(jj,dimj) c c write(6,*) me,'..',ilo,ihi,jlo,jhi,'.',dimi,dimj,'..',i,j c call ffflush(6) buf = ga_read_inc(g_a,i,j,inc) if(a(i,j).ne. buf)then write(6,114) me,ilo,ihi,jlo,jhi write(6,112) me,i,j,a(i,j),buf,loop c write(6,*)me,'READ_inc ', i,',',j,',', a(i,j),' ',buf,me call ffflush(6) endif 112 format(i3,' READ_inc ',i3,',',i3,',',i8,' ',i8,' loop ',i8) call ga_get(g_a, i,i,j,j, buf,1) a(i,j) = a(i,j)+inc if(a(i,j).ne. buf)then write(6,114) me,ilo,ihi,jlo,jhi write(6,113) me,i,j,a(i,j),buf,loop c write(6,*)me,'read_INC ', i,',',j,',', a(i,j),' ',buf,me call ffflush(6) endif 113 format(i3,' read_INC ',i3,',',i3,',',i8,' ',i8,' loop ',i8) 114 format(i3,' ilo ',i3,' ihi ',i3,' jlo ',i3,' jhi ',i3) enddo endif call ga_sync() c if (me.eq.0) then write(6,*) write(6,*) ' read_inc is OK' write(6,*) endif c c c if (me.eq.0) then write(6,*) write(6,*) '> checking ga_fence and ga_lock' write(6,*) call ffflush(6) endif c call ga_zero(g_a) c c*** use ga_read_inc and elements g_a(1:2,1) to implement a lock c*** compute g_a(:,n) = sum (1(:)) for P processors c status = ga_create_mutexes(1) if (.not. status) then call ga_error('ga_create_mutexes failed ',-1) endif if(n.lt.2)call ga_error('insufficient n to test ga_fence',n) call ga_lock(0) c call my_lock(g_a) c get original values g_a(:,n) call ga_get(g_a, 1,n, n,n, b,n) c add my contribution do i =1,n b(i,1)= b(i,1)+1 enddo c c need to use fence to assure that coms complete before leaving c Critical Section c call ga_init_fence() call ga_put(g_a, 1,n, n,n, b,n) call ga_fence() call ga_unlock(0) c call my_unlock(g_a) c 333 if(.not.ga_destroy_mutexes()) $ call ga_error('mutex not destroyed',-1) call ga_sync() if (me.eq.0) then call ga_get(g_a, 1,n, n,n, b,n) do i =1,n if(b(i,1).ne. nproc)then print *, 'mismatch',b(i,1),nproc call ga_error('fence failed',i) endif enddo write(6,*) write(6,*) ' ga_fence and ga_lock are OK' write(6,*) endif c c if (me.eq.0) then write(6,*) write(6,*) '> checking ga_locate_region' write(6,*) call ffflush(6) endif status = ga_locate_region(g_a, 1, n, 1, n, map, np) found = 0 do j=1,n do i=1,n b(i,j)=-1 enddo enddo if(me.eq.0)call ga_put(g_a,1,n,1,n,b,n) call ga_sync() do k = 1, np if(map(5,k).eq.me)then if(found.eq.1) then write(6,*)'double entry in map for proc ',me call ffflush(6) endif do j= map(3,k), map(4,k) do i= map(1,k), map(2,k) b(i,j)=1*me enddo enddo call ga_put(g_a, map(1,k),map(2,k),map(3,k),map(4,k), & b(map(1,k),map(3,k)),n) found = 1 endif enddo call ga_sync() c do k = 1, np if(map(5,k).eq.me)then call ga_get(g_a, map(1,k),map(2,k),map(3,k),map(4,k), & a(map(1,k),map(3,k)),n) do j= map(3,k), map(4,k) do i= map(1,k), map(2,k) if(b(i,j).ne.a(i,j)) then write(6,*) & 'proc ',me, 'overlap with ',a(i,j) call ffflush(6) endif enddo enddo endif enddo call ga_sync() c if(me.eq.0)then call ga_get(g_a,1,n,1,n,a,n) do j=1,n do i=1,n if(a(i,j).eq.-1)then write(6,*)'i=',i,' j=',j, ' not assigned ' call ga_error('... exiting ',-1) endif enddo enddo endif if (me.eq.0) then write(6,*) write(6,*) ' ga_locate_region is OK' write(6,*) call ffflush(6) endif c c Delete the global array c status = ga_destroy(g_a) status = ga_destroy(gp_a) c end c--------------------------------------------------------------------- subroutine check_flt() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer n, m parameter (n =100) parameter (m=2*n) real a(n,n), b(n,n), v(m), w(m) integer iv(m), jv(m) logical status integer g_a, g_b integer gp_a, gp_b integer iran, i, j, loop, nloop, maxloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, ii, jj, nnodes double precision nwords parameter (maxloop = 100) integer maxproc integer ndim, dims(2), chunk(2), p_mirror parameter (maxproc = 4096) double precision crap real x, sum1, sum2 logical found integer lprocs, inode, iproc, lproc intrinsic int iran(i) = int(drand(0)*real(i)) + 1 nproc = ga_nnodes() me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) nloop = Min(maxloop,n) c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n a(i,j) = i-1 + (j-1)*n b(i,j) = -1. enddo enddo c c Create a global array c ndim = 2 dims(1) = 2*n dims(2) = 2*n chunk(1) = 0 chunk(2) = 0 gp_a = ga_create_handle() call ga_set_data(gp_a,ndim,dims,MT_REAL) call ga_set_array_name(gp_a,'a_parent') if (.not.ga_allocate(gp_a)) then write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',-1) endif dims(1) = n dims(2) = n g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_REAL) call ga_set_array_name(g_a,'a') if (.not.ga_overlay(g_a,gp_a)) then write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',-1) endif c c check if handle is valid. be quiet unless error C if(.not.ga_valid_handle(g_a)) call ga_error("invalid handle",g_a) c call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) call ga_sync() c c Zero the array c if (me .eq. 0) then write(6,21) 21 format('> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_sync() do i = 1, n do j = 1, n if (b(i,j) .ne. 0.0) then write(6,*) ' zero ', me, i, j, b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format('> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc if (mod(ij,nproc) .eq. me) then ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) c write(6,4) me, ilo, ihi, jlo, jhi c 4 format(' node ',i2,' checking put ',4i4) c call ffflush(6) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo call ga_sync() c if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format('> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_rfill(n*n, 0.0, b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) c if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif c sum1 = 0.0d0 do j = jlo, jhi do i = ilo, ihi sum1 = sum1 + b(i,j) if (b(i,j) .ne. a(i,j)) then write(6,*) 'error ', i, j, b(i,j), a(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) endif call ga_sync() c c Each node accumulates into disjoint sections of the array c if (me .eq. 0) then write(6,9) 9 format('> Checking accumulate ... ') call ffflush(6) endif call ga_sync() c crap = drand(12345) ! Same seed for each process do j = 1, n do i = 1, n c b(i,j) = real(drand(0)) b(i,j) = i+j enddo enddo c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc c x = real(drand(0)) x = 10. ilo = i ihi = min(i+inc-1, n) if(ihi.eq.n-1)ihi=n c ihi = min(i+inc, n) jlo = j jhi = min(j+inc-1, n) if(jhi.eq.n-1)jhi=n c jhi = min(j+inc-1, n) * call ffflush(6) if (mod(ij,nproc) .eq. me) then c print *, me, 'checking accumulate ',ilo,ihi,jlo,jhi,x * 11 format(' node ',i2,' checking accumulate ',4i4) * call ffflush(6) call ga_acc(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n, x) endif ij = ij + 1 c c Each process applies all updates to its local copy c do jj = jlo, jhi do ii = ilo, ihi a(ii,jj) = a(ii,jj) + x * b(ii,jj) enddo enddo enddo enddo call ga_sync() c c All nodes check all of a call ga_get(g_a, 1, n, 1, n, b, n) c do j = 1, n do i = 1, n if(MISMATCHF(b(i,j),a(i,j)))then write(6,*) ' acc ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' disjoint ga_acc is OK' write(6,*) endif c c overlapping accumulate call ga_sync() if (.not. ga_create(MT_REAL, n, n, 'b', 0, 0, g_b)) then call ga_error('ga_create failed for second array ',-1) endif c call ga_zero(g_b) call ga_acc(g_b, n/2, n/2, n/2, n/2, 1.0, 1, 1.0) call ga_sync() if (me.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, b(1,1), 1) x = abs(b(1,1) -1*nproc) if(x.gt. 1e-10)then write(6,*)'val=',b(1,1),' expected=',nproc, x call ga_error('overlapping accumulate failed',-1) endif write(6,*) write(6,*) ' overlapping ga_acc is OK' write(6,*) endif c c Check the ga_add function c if (me .eq. 0) then write(6,91) 91 format('> Checking add ...') call ffflush(6) endif c crap = drand(12345) ! Everyone has same seed do j = 1, n do i = 1, n b(i,j) = real(drand(0)*real(i)) + 1 a(i,j) = 0.1*a(i,j) + 0.9*b(i,j) enddo enddo if (me.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) call ga_add(0.1, g_a, 0.9, g_b, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if(MISMATCHF(b(i,j), a(i,j)))then write(6,*) ' add ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' add is OK ' write(6,*) endif call ga_sync() c c Check the sdot function c if (me .eq. 0) then write(6,19) 19 format('> Checking sdot ...') call ffflush(6) endif crap = drand(12345) ! Everyone has same seed sum1 = 0.0 do j = 1, n do i = 1, n b(i,j) = drand(0) sum1 = sum1 + a(i,j)*b(i,j) enddo enddo if (me.eq.0) then call ga_put(g_b, 1, n, 1, n, b, n) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() sum2 = ga_sdot(g_a,g_b) if(MISMATCHF(sum1, sum2))then write(6,*) ' fdot wrong ', sum1, sum2 call ffflush(6) call ga_error('... exiting ',-1) else if (me.eq.0) then write(6,*) write(6,*) ' sdot is OK ' write(6,*) endif c c Check the ga_scale function c if (me .eq. 0) then write(6,92) 92 format('> Checking scale ...') call ffflush(6) endif call ga_scale(g_a, 0.123) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n a(i,j) = a(i,j)*0.123 if (MISMATCHF(b(i,j), a(i,j)))then write(6,*) ' dscal ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' scale is OK ' write(6,*) endif c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c call ga_sync() if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_sync() itmp = iran(nproc)-1 if(me.eq.itmp) then do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo call ga_gather(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',-1) endif enddo endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_sync() if(me.eq.iran(ga_nnodes())-1) then do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo c v(loop) = DSIN(a(ilo,jlo)+b(ilo,jlo)) v(loop) = 1.0 *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) c if(v(loop) .ne. w(loop))then if(w(loop) .ne. 1.0 *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', 1.0 *(ilo+jlo) call ffflush(6) call ga_error('... exiting ',-1) endif enddo endif call ga_sync() enddo c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c call ga_sync() c c scatter-acc available in GA ver. 3.0 if (me.eq.0) then write(6,*) write(6,*) ' checking scatter-accumulate' write(6,*) endif c crap = drand(1234) call ga_zero(g_a) c do j = 1, n do i = 1, n b(i,j) =0. enddo enddo c x = .1d0 ii =n do jj = 1,1 call ga_sync() do loop = 1, ii c generate unique i,j pairs 10 continue i = iran(n) j=iran(n) if (found(i,j, iv, jv, loop-1) ) goto 10 iv(loop) = i jv(loop) = j v(loop) = 1.0 *(i+j) b(i,j) = b(i,j) + nproc*x*v(loop) ! update local ref. array enddo call ga_scatter_acc(g_a,v,iv,jv, ii,x) c call ga_sync() c c check the result c call ga_get(g_a, 1, n, 1,n, a, n) do loop = 1,ii i = iv(loop) j = jv(loop) if(MISMATCHF(a(i,j),b(i,j)))then print *,'Error',i,j,loop,a(i,j),'expected=',b(i,j) * if(me.eq.0)then * do ii=1,loop * print *,'element',ii, iv(ii),jv(ii) * enddo * endif call ga_error('scatter-acc error in trial ',jj) endif enddo call ga_sync() enddo call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' scatter-accumulate is OK' write(6,*) endif c c Delete the global array c status = ga_destroy(g_a) status = ga_destroy(gp_a) c end c_____________________________________________________________ subroutine check_wrappers implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer n parameter (n=10000) double precision sum(n) double complex zsum(n), check(n) integer isum(n), ibuf(2) integer me, nproc, i real fsum(n) c nproc = ga_nnodes() me = ga_nodeid() c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_gop (integer)' write(6,*) call ffflush(6) endif do i = 1, n isum(i) = i + me enddo call ga_gop(MT_INT, isum, n, '+') do i = 1, n if(isum(i).ne.(i*nproc + (nproc-1)*nproc/2))then call ga_error('ga_gop error -2',isum(i)) endif enddo call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' ga_gop (integer) is OK' write(6,*) endif call ga_sync() c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_gop (double)' write(6,*) call ffflush(6) endif do i = 1, n sum(i) = i + 1d0 * me enddo call ga_gop(MT_DBL, sum, n, '+') do i = 1, n if(Int(sum(i)).ne.(i*nproc + (nproc-1)*nproc/2))then call ga_error('ga_gop error',Int(sum(i))) endif enddo call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' ga_gop (double) is OK' write(6,*) endif c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_gop (real)' write(6,*) call ffflush(6) endif do i = 1, n fsum(i) = i + 1.0 * me enddo call ga_gop(MT_REAL, fsum, n, '+') do i = 1, n if(Int(fsum(i)).ne.(i*nproc + (nproc-1)*nproc/2))then call ga_error('ga_gop error',Int(fsum(i))) endif enddo call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' ga_gop (real) is OK' write(6,*) endif c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_gop (double complex)' write(6,*) call ffflush(6) endif do i = 1, n zsum(i) = cmplx(i+1d0, i + 1d0 * me) check(i)= cmplx(nproc*i+nproc*1d0, nproc*i+(nproc-1)*nproc/2) enddo call ga_gop(MT_DCPL, zsum, n, '+') do i = 1, n if(zsum(i).ne.check(i)) then call ga_error('ga_gop error', -1) endif enddo call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' ga_gop (double complex) is OK' write(6,*) endif c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_brdcst' write(6,*) call ffflush(6) endif if(me.eq.nproc-1)then ibuf(1) = me ibuf(2) = nproc endif call ga_brdcst(1000,ibuf,util_mitob(2),nproc-1) if(ibuf(1).ne.nproc-1)call ga_error('ibuf(1) error',ibuf(1)) if(ibuf(2).ne.nproc)call ga_error('ibuf(2) error',ibuf(2)) call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> ga_brdcst is OK ' write(6,*) call ffflush(6) endif call ga_sync() end subroutine check_mem(mem_size) implicit none integer mem_size #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,nmax,left,need, me,procs,g_a, g_b logical status c if(.not. ga_memory_limited())return me = ga_nodeid() procs = ga_nnodes() nmax = int(dsqrt(dble(mem_size/util_mitob(1)))) left = mem_size/procs - ga_inquire_memory() n = nmax/2 need = util_mdtob(n*n)/procs c if(me.eq.0)then write(6,*)' ' if(ga_uses_ma())then write(6,*)' CHECKING GA MEMORY RESTRICTIONS (MA used)' else write(6,*)' CHECKING GA MEMORY RESTRICTIONS (MA not used)' endif write(6,*)' ' write(6,*)' ' call print_mem_info(n,left, need, mem_size/procs) endif c status = ga_create(MT_DBL, n, n, 'a', 0, 0, g_a) c if(me.eq.0) then if(status) then write(6,*) ' success' else write(6,*) ' failure' endif call ffflush(6) endif c n = nmax left = mem_size/procs - ga_inquire_memory() need = util_mdtob(n*n)/procs if(me.eq.0)then call print_mem_info(n,left, need, mem_size/procs) endif c status = ga_create(MT_DBL, n, n, 'b', 0, 0, g_b) c if(me.eq.0) then if(status) then write(6,*) ' success' else write(6,*) ' failure' endif write(6,*)' ' write(6,*)' ' call ffflush(6) endif status = ga_destroy(g_a) end subroutine print_mem_info(n,left, need, total) implicit none integer n,left, need, total c write(6,*)' ' if(left - need .ge. 0) then write(6,1)n,n 1 format('> Creating array ',i4,' by ',i4,' -- should succeed') else write(6,2)n,n 2 format('> Creating array ',i4,' by ',i4,' -- SHOULD FAIL') endif write(6,3) need, left, total 3 format(' (need ',i7,' and ',i7,' out of ',i7,' bytes are left)') write(6,*)' ' call ffflush(6) c end subroutine my_lock(g_b) implicit none #include "global.fh" integer g_b, val, flag, i logical first_time double precision dummy common /lock/ val common /dum/ dummy data first_time /.true./ c c this awkward initialization is to avoid a weird problem C with block data on SUN if(first_time)then first_time = .false. dummy = .0 endif c val = ga_read_inc(g_b,1,1, 1) 10 call ga_get(g_b, 2,2,1,1, flag, 1) if(flag.eq.val) return c c to reduce memory stress, wait a while before retrying do i = 1, 100 dummy = dummy + .1 enddo goto 10 end subroutine my_unlock(g_b) implicit none #include "global.fh" integer g_b, val common /lock/ val c call ga_put(g_b, 2,2,1,1, val+1, 1) end logical function found(i,j, iv, jv, n) integer n integer i,j, iv(n), jv(n) integer loop found = .false. do loop = 1, n if(i .eq. iv(loop) .and. j .eq.jv(loop))then found = .true. goto 99 endif enddo 99 continue return end logical function nfound(i,j, ijv, n) integer n integer i,j, ijv(2,n) integer loop nfound = .false. do loop = 1, n if(i .eq. ijv(1,loop) .and. j .eq.ijv(2,loop))then nfound = .true. goto 99 endif enddo 99 continue return end subroutine proc_remap() implicit none #include "global.fh" integer proc(100),nproc,i nproc = ga_nnodes() if(nproc.gt.100) $ call ga_error("remap requires<=100 processes",nproc) do i = 1, nproc proc(i) = nproc-i enddo c call ga_register_proclist(proc,nproc) end subroutine util_rfill(n,val,a,ia) implicit none real a(*), val integer n, ia, i c c initialise real array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end subroutine util_dfill(n,val,a,ia) implicit none double precision a(*), val integer n, ia, i c c initialise double precision array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end subroutine util_ifill(n,val,a,ia) implicit none integer n, ia, i, a(*),val c c initialise integer array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end subroutine util_qfill(n,val,a,ia) implicit none double complex a(*), val integer n, ia, i c c initialise double complex array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end subroutine util_create_map(mapc, base, dims, nblocks, proc_grid) implicit none #include "global.fh" integer mapc(4096) integer dims(2), nblocks(2), proc_grid(2) integer primes(256), nprime integer factors(256), nfactors integer base integer i, j, nproc, pmax, px, py, lo, hi logical ok ! ! Factor nproc into 2 x 2 processor grid. Start by finding prime ! factors ! nproc = ga_nnodes() pmax = sqrt(dble(nproc)) + 1 nprime = 1 primes(1) = 2 do i = 3, pmax ok = .true. do j = 1, nprime ! ! check to see if any of the existing primes divides i ! if (mod(i,primes(j)).eq.0) then ok = .false. endif end do ! ! none of the existing primes divides i, so add it to the list of ! primes ! if (ok) then nprime = nprime + 1 primes(nprime) = i endif end do ! ! find prime factors of nproc ! nfactors = 0 j = nproc i = 1 do while (i.le.nprime.and.j.gt.1) do while (mod(j,primes(i)).eq.0.and.j.gt.1) nfactors = nfactors + 1 factors(nfactors) = primes(i) j = j/primes(i) end do i = i + 1 end do if (nfactors.eq.0) then nfactors = 1 primes(1) = nproc endif ! ! prime factors are available. Create px and py ! px = 1 py = 1 do i = nfactors, 1, -1 if (px.lt.py) then px = px*factors(i) else py = py*factors(i) endif end do proc_grid(1) = px proc_grid(2) = py ! ! proc grid is complete, now create map. Start with x-axis ! lo = 1 hi = 0 i = 0 j = 0 do while (hi.lt.dims(1)) i = i + 1 hi = lo + base + mod(i+1,2) - 1 ! hi = lo + base - 1 if (hi.gt.dims(1)) hi = dims(1) j = j + 1 mapc(j) = lo lo = hi + 1 end do nblocks(1) = i lo = 1 hi = 0 i = 0 do while (hi.lt.dims(2)) i = i + 1 hi = lo + base + mod(i,2) - 1 ! hi = lo + base - 1 if (hi.gt.dims(1)) hi = dims(1) j = j + 1 mapc(j) = lo lo = hi + 1 end do nblocks(2) = i end ga-5.9.2/global/testing/packc.c000066400000000000000000000343671500715745200163030ustar00rootroot00000000000000/** * Tests the pack function in GA. * * Each test will locally perform the same functionality, then compare * local buffers against global buffers. */ #if HAVE_CONFIG_H # include "config.h" #endif #define NELEM 200000 /*#define NELEM 20*/ #define HEAP 200*200*4 #define FUDGE 100 #define STACK 200*200 #include #include #include "ga.h" #include "macdecls.h" #include "mp3.h" static int me; static int nproc; #define assign_reg(a,b) (a) = (b) #define assign_cpl(a,b) (a).real = (b).real; (a).imag = (b).imag #define assign_val_reg(a,b,c) (a) = (b) #define assign_val_cpl(a,b,c) (a).real = (b); (a).imag = (c) #define eq_zero_reg(a) (0 == (a)) #define eq_zero_cpl(a) (0 == (a).real && 0 == (a).imag) #define neq_zero_reg(a) (0 != (a)) #define neq_zero_cpl(a) (0 != (a).real || 0 != (a).imag) #define neq_reg(a,b) ((a) != (b)) #define neq_cpl(a,b) ((a).real != (b).real || (a).imag != (b).imag) #define assign_add_reg(a,b,c) (a) = (b) + (c) #define assign_add_cpl(a,b,c) (a).real = (b).real + (c).real; \ (a).imag = (b).imag + (c).imag #define cast_reg(a) ((float)(a)) #define cast_cpl(a) ((float)(a).real) #if 0 # define PRINT(AT,AT_MSK) do { \ int p; \ for (p=0; p Checking ga_fill_patch ... ' call ffflush(6) endif c val = 1d0 call ga_fill_patch(g_a, 2,n/2, 2,n, val) * call ga_print(g_a, 1) do j = 2+me, n, nproc call ga_get(g_a, 1,n/2, j,j, buf,n/2) do i = 2, n/2 if(buf(i) .ne.val ) then print *,me, ' error ',i,j, buf(i),val call ga_error('exiting ...',0) endif enddo enddo c call ga_sync() if (me .eq. 0) then write(6,*)' OK ' call ffflush(6) endif c do j = 1+me, n, nproc call ga_put(g_a,1,n,j,j,a(1,j),n) enddo c*** if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_copy_patch ... ' call ffflush(6) endif c do loop =1, 10 ailo = iran(n/2) ajlo = iran(n/2) aihi = min(n, -1+ailo+n/2) ajhi = min(n, -1+ajlo+n/4) rows = aihi -ailo+1 cols = ajhi -ajlo +1 c bilo = iran(n/3) bjlo = iran(n/3) bihi = bilo + rows -1 bjhi = bjlo + cols -1 if (me .eq. 0) then write(6,'(2x,1h[,4i4,1h],5h --> ,1h[,4i4,1h])') $ ailo,aihi,ajlo,ajhi, $ bilo,bihi, bjlo, bjhi call ffflush(6) endif c call ga_copy_patch('n', g_a, ailo, aihi, ajlo, ajhi, & g_b, bilo, bihi, bjlo, bjhi) c call ga_print(g_a,1) c call ga_print(g_b,1) call ga_get(g_b,bilo,bihi, bjlo, bjhi, buf, rows) base = 0 do j = ajlo, ajhi if(Mod(j,nproc).eq.me) then do i = ailo, aihi base = base+1 if(buf(base) .ne. a(i,j)) then print *,me, ' error ',i,j, buf(base), a(i,j) call ga_error('exiting ...',0) endif enddo else base = base + rows endif enddo enddo c ailo = iran(n/2) ajlo = iran(n/2) bilo = iran(n/2) bjlo = iran(n/2) c #ifndef USE_REGULAR if (me .eq. 0) then write(6,*)' without transpose: OK ' call ffflush(6) endif #else if (me .eq. 0) then write(6,'(2x,1h[,4i4,1h],5h --> ,1h[,4i4,1h])') $ ailo,aihi,ajlo,ajhi, $ bilo,bihi, bjlo, bjhi endif call ga_copy_patch('n', g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) * call ga_print_patch(g_a, ailo,ailo+n/2, ajlo, ajlo+n/3,1) * call ga_print(g_b, 1) call ga_get(g_b,bilo,bilo+n/3,bjlo, bjlo+n/2,buf,n/3+1) base = 0 do j = ajlo, ajlo+n/3 if(Mod(j,nproc).eq.me) then do i = ailo, ailo+n/2 base = base+1 if(buf(base) .ne. a(i,j)) then print *,me, ' error ',i,j, buf(base), a(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo call ga_sync() c if (me .eq. 0) then write(6,*)' without transpose: OK ' call ffflush(6) endif c if (me .eq. 0) then write(6,'(2x,1h[,4i4,2h]~,5h --> ,1h[,4i4,1h])') $ ailo,aihi,ajlo,ajhi, $ bilo,bihi, bjlo, bjhi endif call ga_copy_patch('t', g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) * call ga_print_patch(g_a, ailo,ailo+n/2, ajlo, ajlo+n/3,1) * call ga_print(g_b, 1) call ga_get(g_b,bilo,bilo+n/3,bjlo, bjlo+n/2,buf,n/3+1) base = 0 do i = ailo, ailo+n/2 if(Mod(j,nproc).eq.me) then do j = ajlo, ajlo+n/3 base = base+1 if(buf(base) .ne. a(i,j)) then print *,me, ' error ',i,j, buf(base), a(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/3+1 endif enddo call ga_sync() c if (me .eq. 0) then write(6,*)' transposed: OK ' call ffflush(6) endif #endif c c*** if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_scale_patch ... ' call ffflush(6) endif call ga_copy_patch('n', g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/2, bjlo, bjlo+n/3) val = 1.d0 call ga_scale_patch(g_b, bilo,bilo+n/2,bjlo, bjlo+n/3,val) call ga_get(g_b,bilo,bilo+n/2,bjlo, bjlo+n/3,buf,n/2+1) base = 0 do j = ajlo, ajlo+n/3 if(Mod(j,nproc).eq.me) then do i = ailo, ailo+n/2 base = base+1 if(buf(base) .ne. a(i,j)*val) then print *,me, ' error ',i,j, buf(base), a(i,j)*val call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo call ga_sync() c if (me .eq. 0) then write(6,*)' OK ' call ffflush(6) endif c c*** if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_add_patch ... ' call ffflush(6) endif alpha = .1d0 beta = .2d0 call ga_zero(g_c) call ga_add_patch(alpha, g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ beta, g_b, bilo,bilo+n/2, bjlo, bjlo+n/3, $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/3) call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/3,buf,n/2+1) base = 0 val = val*beta + alpha do j = ajlo, ajlo+n/3 if(Mod(j,nproc).eq.me) then do i = ailo, ailo+n/2 base = base+1 if(ABS(buf(base)- a(i,j)*val).gt.1d-5) then print *,me, ' error ',i,j, buf(base), a(i,j)*val call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo call ga_sync() c if (me .eq. 0) then write(6,*)' OK ' call ffflush(6) endif c c*** if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_ddot_patch ... ' call ffflush(6) endif alpha= ga_ddot_patch(g_a,'n', ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_c,'n', bilo,bilo+n/2, bjlo, bjlo+n/3) beta = 0d0 do j = ajlo, ajlo+n/3 do i = ailo, ailo+n/2 beta = beta + a(i,j)**2 enddo enddo if(ABS(beta*val- alpha).gt.1d-6*alpha) then print *,me, ' error ', beta*val, alpha call ga_error('exiting ...',0) endif call ga_sync() c if (me .eq. 0) then write(6,*)' OK ' call ffflush(6) endif c c...................................................... if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_matmul_patch ... ' call ffflush(6) endif do j = 1+me, n, nproc call ga_put(g_b,1,n,j,j,b(1,j),n) enddo call ga_sync() call ga_matmul_patch('n','n', 1d0, 0d0, $ g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2, $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/2) call xgemm('n','n',n/2+1,n/2+1,n/3+1,1d0,a(ailo,ajlo), n, $ b(bilo,bjlo),n, 0d0, c, n) * call ga_print_patch(g_a, ailo,ailo+n/2, ajlo, ajlo+n/3) * call ga_print_patch(g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) * call ga_print(g_c, 1) call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/2,buf,n/2+1) base = 0 do j = 1, 1+n/2 if(Mod(j,nproc).eq.me) then do i = 1, 1+n/2 base = base+1 if(ABS(buf(base)- c(i,j)).gt.1d-8) then print *,me, ' error ',i,j, buf(base), c(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo c call ga_sync() if (me .eq. 0) then write(6,*)' a*b: OK ' call ffflush(6) endif c #ifdef USE_REGULAR call ga_sync() call ga_matmul_patch('t','n', 1d0, 0d0, $ g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2, $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/2) call xgemm('t','n',n/2+1,n/2+1,n/3+1,1d0,a(ajlo,ailo), n, $ b(bilo,bjlo),n, 0d0, c, n) * call ga_print(g_a,1) * call ga_print_patch(g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) * call ga_print(g_c, 1) call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/2,buf,n/2+1) base = 0 do j = 1, 1+n/2 if(Mod(j,nproc).eq.me) then do i = 1, 1+n/2 base = base+1 if(ABS(buf(base)- c(i,j)).gt.1d-8) then print *,me, ' error ',i,j, buf(base), c(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo c call ga_sync() if (me .eq. 0) then write(6,*)' trans(a)*b: OK ' call ffflush(6) endif c call ga_sync() call ga_matmul_patch('n','t', 1d0, 0d0, $ g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2, $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/2) call xgemm('n','t',n/2+1,n/2+1,n/3+1,1d0,a(ailo,ajlo), n, $ b(bjlo,bilo),n, 0d0, c, n) * call ga_print(g_a,1) * call ga_print_patch(g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) * call ga_print(g_c, 1) call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/2,buf,n/2+1) base = 0 do j = 1, 1+n/2 if(Mod(j,nproc).eq.me) then do i = 1, 1+n/2 base = base+1 if(ABS(buf(base)- c(i,j)).gt.1d-8) then print *,me, ' error ',i,j, buf(base), c(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo c call ga_sync() if (me .eq. 0) then write(6,*)' a*trans(b): OK ' call ffflush(6) endif c call ga_sync() call ga_matmul_patch('t','t', 1d0, 0d0, $ g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2, $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/2) call xgemm('t','t',n/2+1,n/2+1,n/3+1,1d0,a(ajlo,ailo), n, $ b(bjlo,bilo),n, 0d0, c, n) * call ga_print(g_a,1) * call ga_print_patch(g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) * call ga_print(g_c, 1) call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/2,buf,n/2+1) base = 0 do j = 1, 1+n/2 if(Mod(j,nproc).eq.me) then do i = 1, 1+n/2 base = base+1 if(ABS(buf(base)- c(i,j)).gt.1d-8) then print *,me, ' error ',i,j, buf(base), c(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo c call ga_sync() if (me .eq. 0) then write(6,*)' trans(a)*trans(b): OK ' call ffflush(6) endif #endif status = ga_destroy(g_a) status = status .and. ga_destroy(g_b) status = status .and. ga_destroy(g_c) if(.not. status) print *, 'ga_destroy failed' c end subroutine zpatch_test() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,m parameter (n = 128) parameter (m = n*n) double complex a(n,n), b(n,n), c(n,n), buf(m), val double complex alpha, beta integer nproc, me integer i, j, ailo, ajlo, bilo, bjlo, base, iran integer aihi, ajhi, bihi, bjhi integer g_a, g_b, g_c integer rows, cols, loop logical status, dist_same #ifndef USE_REGULAR integer ndim, dims(2) integer block_size(2), proc_grid(2) #endif iran(i) = int(drand(1)*real(i)) + 1 dist_same = .false. c me = ga_nodeid() nproc = ga_nnodes() #ifndef USE_REGULAR block_size(1) = 32 block_size(2) = 32 #ifndef USE_SIMPLE_CYCLIC if (mod(nproc,2).ne.0) + call ga_error("Available procs must be divisible by 2",0) proc_grid(1) = 2 proc_grid(2) = nproc/2 #endif #endif c do j = 1, n do i = 1, n a(i,j) = cmplx(dble(i-1), dble((j-1)*n)) b(i,j) = cmplx(dble(i+j),1d0) enddo enddo c c*** Create a global array c #ifdef USE_REGULAR status = ga_create(MT_DCPL, n, n, 'a', 0, 0, g_a) #else g_a = ga_create_handle() ndim = 2 dims(1) = n dims(2) = n call ga_set_data(g_a,ndim,dims,MT_DCPL) call ga_set_array_name(g_a,'a') #ifndef USE_SIMPLE_CYCLIC #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_a,block_size,proc_grid) #else call ga_set_tiled_proc_grid(g_a,block_size,proc_grid) #endif #else call ga_set_block_cyclic(g_a,block_size) #endif status = ga_allocate(g_a) #endif if (.not. status) then write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',0) endif c if(dist_same) then status = ga_duplicate(g_a, g_b, 'a_duplicated') if(.not.ga_compare_distr(g_a, g_b)) $ call ga_error("g_b distribution different",0) status = ga_duplicate(g_a, g_c, 'a_duplicated_again') if(.not.ga_compare_distr(g_a, g_c)) $ call ga_error("g_c distribution different",0) else #ifdef USE_REGULAR status = ga_create(MT_DCPL, n, n, 'b', 0, n, g_b) #else g_b = ga_create_handle() ndim = 2 dims(1) = n dims(2) = n call ga_set_data(g_b,ndim,dims,MT_DCPL) call ga_set_array_name(g_b,'b') #ifndef USE_SIMPLE_CYCLIC #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_b,block_size,proc_grid) #else call ga_set_tiled_proc_grid(g_b,block_size,proc_grid) #endif #else call ga_set_block_cyclic(g_b,block_size) #endif status = ga_allocate(g_b) #endif if (.not. status) call ga_error('ga_create failed:b',0) #ifdef USE_REGULAR status = ga_create(MT_DCPL, n, n, 'c', n, 0, g_c) #else g_c = ga_create_handle() ndim = 2 dims(1) = n dims(2) = n call ga_set_data(g_c,ndim,dims,MT_DCPL) call ga_set_array_name(g_c,'c') #ifndef USE_SIMPLE_CYCLIC #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_c,block_size,proc_grid) #else call ga_set_tiled_proc_grid(g_c,block_size,proc_grid) #endif #else call ga_set_block_cyclic(g_c,block_size) #endif status = ga_allocate(g_c) #endif if (.not. status) call ga_error('ga_create failed:c',0) endif c c*** if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_fill_patch ... ' call ffflush(6) endif c val = (1d0,-1d0) call ga_fill_patch(g_a, 2,n/2, 2,n, val) * call ga_print(g_a, 1) do j = 2+me, n, nproc call ga_get(g_a, 1,n/2, j,j, buf,n/2) do i = 2, n/2 if(buf(i) .ne.val ) then print *,me, ' error ',i,j, buf(i),val call ga_error('exiting ...',0) endif enddo enddo c call ga_sync() if (me .eq. 0) then write(6,*)' OK ' call ffflush(6) endif c do j = 1+me, n, nproc call ga_put(g_a,1,n,j,j,a(1,j),n) enddo c*** if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_copy_patch ... ' call ffflush(6) endif c do loop =1, 10 ailo = iran(n/2) ajlo = iran(n/2) aihi = min(n, -1+ailo+n/2) ajhi = min(n, -1+ajlo+n/4) rows = aihi -ailo+1 cols = ajhi -ajlo +1 c bilo = iran(n/3) bjlo = iran(n/3) bihi = bilo + rows -1 bjhi = bjlo + cols -1 if (me .eq. 0) then write(6,'(2x,1h[,4i4,1h],5h --> ,1h[,4i4,1h])') $ ailo,aihi,ajlo,ajhi, $ bilo,bihi, bjlo, bjhi call ffflush(6) endif c call ga_copy_patch('n', g_a, ailo, aihi, ajlo, ajhi, & g_b, bilo, bihi, bjlo, bjhi) c call ga_print(g_a,1) c call ga_print(g_b,1) call ga_get(g_b,bilo,bihi, bjlo, bjhi, buf, rows) base = 0 do j = ajlo, ajhi if(Mod(j,nproc).eq.me) then do i = ailo, aihi base = base+1 if(buf(base) .ne. a(i,j)) then print *,me, ' error ',i,j, buf(base), a(i,j) call ga_error('exiting ...',0) endif enddo else base = base + rows endif enddo enddo c ailo = iran(n/2) ajlo = iran(n/2) bilo = iran(n/2) bjlo = iran(n/2) c #ifndef USE_REGULAR if (me .eq. 0) then write(6,*)' without transpose: OK ' call ffflush(6) endif #else if (me .eq. 0) then write(6,'(2x,1h[,4i4,1h],5h --> ,1h[,4i4,1h])') $ ailo,aihi,ajlo,ajhi, $ bilo,bihi, bjlo, bjhi endif call ga_copy_patch('n', g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) * call ga_print_patch(g_a, ailo,ailo+n/2, ajlo, ajlo+n/3,1) * call ga_print(g_b, 1) call ga_get(g_b,bilo,bilo+n/3,bjlo, bjlo+n/2,buf,n/3+1) base = 0 do j = ajlo, ajlo+n/3 if(Mod(j,nproc).eq.me) then do i = ailo, ailo+n/2 base = base+1 if(buf(base) .ne. a(i,j)) then print *,me, ' error ',i,j, buf(base), a(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo call ga_sync() c if (me .eq. 0) then write(6,*)' without transpose: OK ' call ffflush(6) endif c if (me .eq. 0) then write(6,'(2x,1h[,4i4,2h]~,5h --> ,1h[,4i4,1h])') $ ailo,aihi,ajlo,ajhi, $ bilo,bihi, bjlo, bjhi endif call ga_copy_patch('t', g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) * call ga_print_patch(g_a, ailo,ailo+n/2, ajlo, ajlo+n/3,1) * call ga_print(g_b, 1) call ga_get(g_b,bilo,bilo+n/3,bjlo, bjlo+n/2,buf,n/3+1) base = 0 do i = ailo, ailo+n/2 if(Mod(j,nproc).eq.me) then do j = ajlo, ajlo+n/3 base = base+1 if(buf(base) .ne. a(i,j)) then print *,me, ' error ',i,j, buf(base), a(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/3+1 endif enddo call ga_sync() c if (me .eq. 0) then write(6,*)' transposed: OK ' call ffflush(6) endif #endif c c*** if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_scale_patch ... ' call ffflush(6) endif call ga_copy_patch('n', g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/2, bjlo, bjlo+n/3) val = 1.d0 call ga_scale_patch(g_b, bilo,bilo+n/2,bjlo, bjlo+n/3,val) call ga_get(g_b,bilo,bilo+n/2,bjlo, bjlo+n/3,buf,n/2+1) base = 0 do j = ajlo, ajlo+n/3 if(Mod(j,nproc).eq.me) then do i = ailo, ailo+n/2 base = base+1 if(buf(base) .ne. a(i,j)*val) then print *,me, ' error ',i,j, buf(base), a(i,j)*val call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo call ga_sync() c if (me .eq. 0) then write(6,*)' OK ' call ffflush(6) endif c c*** if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_add_patch ... ' call ffflush(6) endif alpha = .1d0 beta = .2d0 call ga_zero(g_c) call ga_add_patch(alpha, g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ beta, g_b, bilo,bilo+n/2, bjlo, bjlo+n/3, $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/3) call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/3,buf,n/2+1) base = 0 val = val*beta + alpha do j = ajlo, ajlo+n/3 if(Mod(j,nproc).eq.me) then do i = ailo, ailo+n/2 base = base+1 if(ABS(buf(base)- a(i,j)*val).gt.1d-5) then print *,me, ' error ',i,j, buf(base), a(i,j)*val call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo call ga_sync() c if (me .eq. 0) then write(6,*)' OK ' call ffflush(6) endif c c*** if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_zdot_patch ... ' call ffflush(6) endif alpha= ga_zdot_patch(g_a,'n', ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_c,'n', bilo,bilo+n/2, bjlo, bjlo+n/3) beta = (0d0,0d0) do j = ajlo, ajlo+n/3 do i = ailo, ailo+n/2 beta = beta + a(i,j)*a(i,j) enddo enddo if(ABS(beta*val- alpha)/abs(alpha).gt.1d-6) then print *,me, ' error ', beta*val, alpha call ga_error('exiting ...',0) endif call ga_sync() c if (me .eq. 0) then write(6,*)' OK ' call ffflush(6) endif c c...................................................... if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_matmul_patch ... ' call ffflush(6) endif do j = 1+me, n, nproc call ga_put(g_b,1,n,j,j,b(1,j),n) enddo call ga_sync() call ga_matmul_patch('n','n', (1d0,0d0), (0d0,0d0), $ g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2, $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/2) call ygemm('n','n',n/2+1,n/2+1,n/3+1,(1d0,0d0), $ a(ailo,ajlo), n, $ b(bilo,bjlo),n, (0d0,0d0), c, n) * call ga_print_patch(g_a, ailo,ailo+n/2, ajlo, ajlo+n/3) * call ga_print_patch(g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) * call ga_print(g_c, 1) call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/2,buf,n/2+1) base = 0 do j = 1, 1+n/2 if(Mod(j,nproc).eq.me) then do i = 1, 1+n/2 base = base+1 if(ABS(buf(base)- c(i,j))/abs(c(i,j)).gt.1d-8) then print *,me, ' error ',i,j, buf(base), c(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo c call ga_sync() if (me .eq. 0) then write(6,*)' a*b: OK ' call ffflush(6) endif c call ga_sync() #ifdef USE_REGULAR call ga_matmul_patch('t','n', (1d0,0d0), (0d0,0d0), $ g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2, $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/2) call ygemm('t','n',n/2+1,n/2+1,n/3+1,(1d0,0d0), $ a(ajlo,ailo), n, $ b(bilo,bjlo),n, (0d0,0d0), c, n) * call ga_print(g_a,1) * call ga_print_patch(g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) * call ga_print(g_c, 1) call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/2,buf,n/2+1) base = 0 do j = 1, 1+n/2 if(Mod(j,nproc).eq.me) then do i = 1, 1+n/2 base = base+1 if(ABS(buf(base)- c(i,j))/abs(c(i,j)).gt.1d-8) then print *,me, ' error ',i,j, buf(base), c(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo c call ga_sync() if (me .eq. 0) then write(6,*)' trans(a)*b: OK ' call ffflush(6) endif c call ga_sync() call ga_matmul_patch('n','t', (1d0,0d0), (0d0,0d0), $ g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2, $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/2) call ygemm('n','t',n/2+1,n/2+1,n/3+1,(1d0,0d0), $ a(ailo,ajlo), n, $ b(bjlo,bilo),n, (0d0,0d0), c, n) * call ga_print(g_a,1) * call ga_print_patch(g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) * call ga_print(g_c, 1) call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/2,buf,n/2+1) base = 0 do j = 1, 1+n/2 if(Mod(j,nproc).eq.me) then do i = 1, 1+n/2 base = base+1 if(ABS(buf(base)- c(i,j))/abs(c(i,j)).gt.1d-8) then print *,me, ' error ',i,j, buf(base), c(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo c call ga_sync() if (me .eq. 0) then write(6,*)' a*trans(b): OK ' call ffflush(6) endif c call ga_sync() call ga_matmul_patch('t','t', (1d0,0d0), (0d0,0d0), $ g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2, $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/2) call ygemm('t','t',n/2+1,n/2+1,n/3+1,(1d0,0d0), $ a(ajlo,ailo), n, $ b(bilo,bjlo),n, (0d0,0d0), c, n) * call ga_print(g_a,1) * call ga_print_patch(g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) * call ga_print(g_c, 1) call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/2,buf,n/2+1) base = 0 do j = 1, 1+n/2 if(Mod(j,nproc).eq.me) then do i = 1, 1+n/2 base = base+1 if(ABS(buf(base)- c(i,j))/abs(c(i,j)).gt.1d-8) then print *,me, ' error ',i,j, buf(base), c(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo c call ga_sync() if (me .eq. 0) then write(6,*)' trans(a)*trans(b): OK ' call ffflush(6) endif #endif status = ga_destroy(g_a) status = status .and. ga_destroy(g_b) status = status .and. ga_destroy(g_c) if(.not. status) print *, 'ga_destroy failed' c end subroutine spatch_test() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,m parameter (n = 128) parameter (m = n*n) real a(n,n), b(n,n), c(n,n), buf(m), val real alpha, beta integer nproc, me integer i, j, ailo, ajlo, bilo, bjlo, base, iran integer aihi, ajhi, bihi, bjhi integer g_a, g_b, g_c integer rows, cols, loop logical status, dist_same #ifndef USE_REGULAR integer ndim, dims(2) integer block_size(2), proc_grid(2) #endif iran(i) = int(drand(1)*real(i)) + 1 dist_same = .false. c me = ga_nodeid() nproc = ga_nnodes() #ifndef USE_REGULAR block_size(1) = 32 block_size(2) = 32 #ifndef USE_SIMPLE_CYCLIC if (mod(nproc,2).ne.0) + call ga_error("Available procs must be divisible by 2",0) proc_grid(1) = 2 proc_grid(2) = nproc/2 #endif #endif c do j = 1, n do i = 1, n a(i,j) = i-1 + (j-1)*n b(i,j) = i+j enddo enddo c c*** Create a global array c #ifdef USE_REGULAR status = ga_create(MT_REAL, n, n, 'a', 0, 0, g_a) #else g_a = ga_create_handle() ndim = 2 dims(1) = n dims(2) = n call ga_set_data(g_a,ndim,dims,MT_REAL) call ga_set_array_name(g_a,'a') #ifndef USE_SIMPLE_CYCLIC #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_a,block_size,proc_grid) #else call ga_set_tiled_proc_grid(g_a,block_size,proc_grid) #endif #else call ga_set_block_cyclic(g_a,block_size) #endif status = ga_allocate(g_a) #endif if (.not. status) then write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',0) endif c if(dist_same) then status = ga_duplicate(g_a, g_b, 'a_duplicated') if(.not.ga_compare_distr(g_a, g_b)) $ call ga_error("g_b distribution different",0) status = ga_duplicate(g_a, g_c, 'a_duplicated_again') if(.not.ga_compare_distr(g_a, g_c)) $ call ga_error("g_c distribution different",0) else #ifdef USE_REGULAR status = ga_create(MT_REAL, n, n, 'b', 0, n, g_b) #else g_b = ga_create_handle() ndim = 2 dims(1) = n dims(2) = n call ga_set_data(g_b,ndim,dims,MT_REAL) call ga_set_array_name(g_b,'b') #ifndef USE_SIMPLE_CYCLIC #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_b,block_size,proc_grid) #else call ga_set_tiled_proc_grid(g_b,block_size,proc_grid) #endif #else call ga_set_block_cyclic(g_b,block_size) #endif status = ga_allocate(g_b) #endif if (.not. status) call ga_error('ga_create failed:b',0) #ifdef USE_REGULAR status = ga_create(MT_REAL, n, n, 'c', n, 0, g_c) #else g_c = ga_create_handle() ndim = 2 dims(1) = n dims(2) = n call ga_set_data(g_c,ndim,dims,MT_REAL) call ga_set_array_name(g_c,'c') #ifndef USE_SIMPLE_CYCLIC #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_c,block_size,proc_grid) #else call ga_set_tiled_proc_grid(g_c,block_size,proc_grid) #endif #else call ga_set_block_cyclic(g_c,block_size) #endif status = ga_allocate(g_c) #endif if (.not. status) call ga_error('ga_create failed:c',0) endif c c*** if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_fill_patch ... ' call ffflush(6) endif c val = 1.0 call ga_fill_patch(g_a, 2,n/2, 2,n, val) * call ga_print(g_a, 1) do j = 2+me, n, nproc call ga_get(g_a, 1,n/2, j,j, buf,n/2) do i = 2, n/2 if(buf(i) .ne.val ) then print *,me, ' error ',i,j, buf(i),val call ga_error('exiting ...',0) endif enddo enddo c call ga_sync() if (me .eq. 0) then write(6,*)' OK ' call ffflush(6) endif c do j = 1+me, n, nproc call ga_put(g_a,1,n,j,j,a(1,j),n) enddo c*** if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_copy_patch ... ' call ffflush(6) endif c do loop =1, 10 ailo = iran(n/2) ajlo = iran(n/2) aihi = min(n, -1+ailo+n/2) ajhi = min(n, -1+ajlo+n/4) rows = aihi -ailo+1 cols = ajhi -ajlo +1 c bilo = iran(n/3) bjlo = iran(n/3) bihi = bilo + rows -1 bjhi = bjlo + cols -1 if (me .eq. 0) then write(6,'(2x,1h[,4i4,1h],5h --> ,1h[,4i4,1h])') $ ailo,aihi,ajlo,ajhi, $ bilo,bihi, bjlo, bjhi call ffflush(6) endif c call ga_copy_patch('n', g_a, ailo, aihi, ajlo, ajhi, & g_b, bilo, bihi, bjlo, bjhi) c call ga_print(g_a,1) c call ga_print(g_b,1) call ga_get(g_b,bilo,bihi, bjlo, bjhi, buf, rows) base = 0 do j = ajlo, ajhi if(Mod(j,nproc).eq.me) then do i = ailo, aihi base = base+1 if(buf(base) .ne. a(i,j)) then print *,me, ' error ',i,j, buf(base), a(i,j) call ga_error('exiting ...',0) endif enddo else base = base + rows endif enddo enddo c ailo = iran(n/2) ajlo = iran(n/2) bilo = iran(n/2) bjlo = iran(n/2) c #ifndef USE_REGULAR if (me .eq. 0) then write(6,*)' without transpose: OK ' call ffflush(6) endif #else if (me .eq. 0) then write(6,'(2x,1h[,4i4,1h],5h --> ,1h[,4i4,1h])') $ ailo,aihi,ajlo,ajhi, $ bilo,bihi, bjlo, bjhi endif call ga_copy_patch('n', g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) * call ga_print_patch(g_a, ailo,ailo+n/2, ajlo, ajlo+n/3,1) * call ga_print(g_b, 1) call ga_get(g_b,bilo,bilo+n/3,bjlo, bjlo+n/2,buf,n/3+1) base = 0 do j = ajlo, ajlo+n/3 if(Mod(j,nproc).eq.me) then do i = ailo, ailo+n/2 base = base+1 if(buf(base) .ne. a(i,j)) then print *,me, ' error ',i,j, buf(base), a(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo call ga_sync() c if (me .eq. 0) then write(6,*)' without transpose: OK ' call ffflush(6) endif c if (me .eq. 0) then write(6,'(2x,1h[,4i4,2h]~,5h --> ,1h[,4i4,1h])') $ ailo,aihi,ajlo,ajhi, $ bilo,bihi, bjlo, bjhi endif call ga_copy_patch('t', g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) * call ga_print_patch(g_a, ailo,ailo+n/2, ajlo, ajlo+n/3,1) * call ga_print(g_b, 1) call ga_get(g_b,bilo,bilo+n/3,bjlo, bjlo+n/2,buf,n/3+1) base = 0 do i = ailo, ailo+n/2 if(Mod(j,nproc).eq.me) then do j = ajlo, ajlo+n/3 base = base+1 if(buf(base) .ne. a(i,j)) then print *,me, ' error ',i,j, buf(base), a(i,j) call ga_error('exiting ...',0) endif enddo else base = base +n/3+1 endif enddo call ga_sync() c if (me .eq. 0) then write(6,*)' transposed: OK ' call ffflush(6) endif #endif c c*** if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_scale_patch ... ' call ffflush(6) endif call ga_copy_patch('n', g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_b, bilo,bilo+n/2, bjlo, bjlo+n/3) val = 1.0 call ga_scale_patch(g_b, bilo,bilo+n/2,bjlo, bjlo+n/3,val) call ga_get(g_b,bilo,bilo+n/2,bjlo, bjlo+n/3,buf,n/2+1) base = 0 do j = ajlo, ajlo+n/3 if(Mod(j,nproc).eq.me) then do i = ailo, ailo+n/2 base = base+1 if(buf(base) .ne. a(i,j)*val) then print *,me, ' error ',i,j, buf(base), a(i,j)*val call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo call ga_sync() c if (me .eq. 0) then write(6,*)' OK ' call ffflush(6) endif c c*** if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_add_patch ... ' call ffflush(6) endif alpha = 0.1 beta = 0.2 call ga_zero(g_c) call ga_add_patch(alpha, g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, $ beta, g_b, bilo,bilo+n/2, bjlo, bjlo+n/3, $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/3) call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/3,buf,n/2+1) base = 0 val = val*beta + alpha do j = ajlo, ajlo+n/3 if(Mod(j,nproc).eq.me) then do i = ailo, ailo+n/2 base = base+1 if(ABS(buf(base)- a(i,j)*val).gt.1e-3) then print *,me, ' error ',i,j, buf(base), a(i,j)*val call ga_error('exiting ...',0) endif enddo else base = base +n/2+1 endif enddo call ga_sync() c if (me .eq. 0) then write(6,*)' OK ' call ffflush(6) endif c c*** if (me .eq. 0) then print *, ' ' write(6,*)'> Checking ga_sdot_patch ... ' call ffflush(6) endif alpha= ga_sdot_patch(g_a,'n', ailo,ailo+n/2, ajlo, ajlo+n/3, $ g_c,'n', bilo,bilo+n/2, bjlo, bjlo+n/3) beta = 0.0 do j = ajlo, ajlo+n/3 do i = ailo, ailo+n/2 beta = beta + a(i,j)**2 enddo enddo if(ABS(beta*val- alpha).gt.(1.0e-2*alpha)) then print *,me, ' error ', beta*val, alpha call ga_error('exiting ...',0) endif call ga_sync() c if (me .eq. 0) then write(6,*)' OK ' call ffflush(6) endif c...................................................... ccc if (me .eq. 0) then ccc print *, ' ' ccc write(6,*)'> Checking ga_matmul_patch ... ' ccc call ffflush(6) ccc endif ccc do j = 1+me, n, nproc ccc call ga_put(g_b,1,n,j,j,b(1,j),n) ccc enddo ccc call ga_sync() ccc call ga_matmul_patch('n','n', 1.0, 0.0, ccc $ g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, ccc $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2, ccc $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/2) ccc call xgemm('n','n',n/2+1,n/2+1,n/3+1,1d0,a(ailo,ajlo), n, ccc $ b(bilo,bjlo),n, 0d0, c, n) ccc* call ga_print_patch(g_a, ailo,ailo+n/2, ajlo, ajlo+n/3) ccc* call ga_print_patch(g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) ccc* call ga_print(g_c, 1) ccc call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/2,buf,n/2+1) ccc base = 0 ccc do j = 1, 1+n/2 ccc if(Mod(j,nproc).eq.me) then ccc do i = 1, 1+n/2 ccc base = base+1 ccc if(ABS(buf(base)- c(i,j)).gt.1e-8) then ccc print *,me, ' error ',i,j, buf(base), c(i,j) ccc call ga_error('exiting ...',0) ccc endif ccc enddo ccc else ccc base = base +n/2+1 ccc endif ccc enddo cccc ccc call ga_sync() ccc if (me .eq. 0) then ccc write(6,*)' a*b: OK ' ccc call ffflush(6) ccc endif cccc ccc call ga_sync() ccc call ga_matmul_patch('t','n', 1d0, 0d0, ccc $ g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, ccc $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2, ccc $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/2) ccc call xgemm('t','n',n/2+1,n/2+1,n/3+1,1.0,a(ajlo,ailo), n, ccc $ b(bilo,bjlo),n, 0.0, c, n) ccc* call ga_print(g_a,1) ccc* call ga_print_patch(g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) ccc* call ga_print(g_c, 1) ccc call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/2,buf,n/2+1) ccc base = 0 ccc do j = 1, 1+n/2 ccc if(Mod(j,nproc).eq.me) then ccc do i = 1, 1+n/2 ccc base = base+1 ccc if(ABS(buf(base)- c(i,j)).gt.1e-8) then ccc print *,me, ' error ',i,j, buf(base), c(i,j) ccc call ga_error('exiting ...',0) ccc endif ccc enddo ccc else ccc base = base +n/2+1 ccc endif ccc enddo cccc ccc call ga_sync() ccc if (me .eq. 0) then ccc write(6,*)' trans(a)*b: OK ' ccc call ffflush(6) ccc endif cccc ccc call ga_sync() ccc call ga_matmul_patch('n','t', 1.0, 0.0, ccc $ g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, ccc $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2, ccc $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/2) ccc call xgemm('n','t',n/2+1,n/2+1,n/3+1,1.0,a(ailo,ajlo), n, ccc $ b(bjlo,bilo),n, 0.0, c, n) ccc* call ga_print(g_a,1) ccc* call ga_print_patch(g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) ccc* call ga_print(g_c, 1) ccc call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/2,buf,n/2+1) ccc base = 0 ccc do j = 1, 1+n/2 ccc if(Mod(j,nproc).eq.me) then ccc do i = 1, 1+n/2 ccc base = base+1 ccc if(ABS(buf(base)- c(i,j)).gt.1e-8) then ccc print *,me, ' error ',i,j, buf(base), c(i,j) ccc call ga_error('exiting ...',0) ccc endif ccc enddo ccc else ccc base = base +n/2+1 ccc endif ccc enddo cccc cccc ccc call ga_sync() ccc if (me .eq. 0) then ccc write(6,*)' a*trans(b): OK ' ccc call ffflush(6) ccc endif cccc ccc call ga_sync() ccc call ga_matmul_patch('t','t', 1d0, 0d0, ccc $ g_a, ailo,ailo+n/2, ajlo, ajlo+n/3, ccc $ g_b, bilo,bilo+n/3, bjlo, bjlo+n/2, ccc $ g_c, bilo,bilo+n/2, bjlo, bjlo+n/2) ccc call xgemm('t','t',n/2+1,n/2+1,n/3+1,1.0,a(ajlo,ailo), n, ccc $ b(bjlo,bilo),n, 0.0, c, n) ccc* call ga_print(g_a,1) ccc* call ga_print_patch(g_b, bilo,bilo+n/3, bjlo, bjlo+n/2) ccc* call ga_print(g_c, 1) ccc call ga_get(g_c,bilo,bilo+n/2,bjlo, bjlo+n/2,buf,n/2+1) ccc base = 0 ccc do j = 1, 1+n/2 ccc if(Mod(j,nproc).eq.me) then ccc do i = 1, 1+n/2 ccc base = base+1 ccc if(ABS(buf(base)- c(i,j)).gt.1e-8) then ccc print *,me, ' error ',i,j, buf(base), c(i,j) ccc call ga_error('exiting ...',0) ccc endif ccc enddo ccc else ccc base = base +n/2+1 ccc endif ccc enddo cccc ccc call ga_sync() ccc if (me .eq. 0) then ccc write(6,*)' trans(a)*trans(b): OK ' ccc call ffflush(6) ccc endif ccc status = ga_destroy(g_a) ccc status = status .and. ga_destroy(g_b) ccc status = status .and. ga_destroy(g_c) ccc if(.not. status) print *, 'ga_destroy failed' cccc end ga-5.9.2/global/testing/patch2.F000066400000000000000000000076611500715745200163430ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif # define THRESH 1.0d-10 #define MISMATCH(x,y) abs(x-y)/max(1,abs(x)).gt.THRESH c #define USE_REGULAR c#define USE_SIMPLE_CYCLIC c#define USE_SCALAPACK_DISTR c#define USE_TILED c program test implicit none #include "mafdecls.fh" #include "global.fh" integer TESTDIM parameter(TESTDIM = 256) logical status integer g_a, g_b, g_c double precision alpha, beta integer ndim, adims(2), bdims(2), cdims(2), tlo(2), thi(2) integer alo(2), ahi(2), blo(2), bhi(2), clo(2), chi(2) integer ald, bld, cld, i_inc, j_inc double precision val integer me, nproc, i, j, ii, jj GA_ACCESS_INDEX_TYPE idx, inc c c*** Initialize a message passing library c #include "mp3.fh" c call nga_initialize() if(ga_nodeid().eq.0)then write(6,*) write(6,'(a)') ' GA initialized' write(6,*) call ffflush(6) endif c status = ma_init(MT_DBL, 500000, 900000/ga_nnodes()) if (.not. status)call ga_error( 'ma_init failed', -1) c me = ga_nodeid() c c create test arrays c g_a = nga_create_handle() ndim = 2 adims(1) = TESTDIM adims(2) = TESTDIM call nga_set_data(g_a,ndim,adims,MT_DBL) status = ga_allocate(g_a) c g_b = nga_create_handle() ndim = 2 bdims(1) = TESTDIM + 1 bdims(2) = TESTDIM + 1 call nga_set_data(g_b,ndim,bdims,MT_DBL) status = ga_allocate(g_b) c g_c = nga_create_handle() ndim = 2 cdims(1) = TESTDIM + 2 cdims(2) = TESTDIM + 2 call nga_set_data(g_c,ndim,cdims,MT_DBL) status = ga_allocate(g_c) c c initialize a and b c i_inc = TESTDIM/2 j_inc = TESTDIM/2 c call nga_distribution(g_a,me,alo,ahi) call nga_access(g_a,alo,ahi,idx,ald) do j = alo(2), ahi(2) do i = alo(1), ahi(1) dbl_mb(idx) = dble((j-1)*adims(1) + i-1) idx = idx + 1 end do end do call nga_release(g_a, alo, ahi) c call nga_distribution(g_b,me,blo,bhi) call nga_access(g_b,blo,bhi,idx,bld) do j = blo(2), bhi(2) do i = blo(1), bhi(1) dbl_mb(idx) = dble((j-1)*bdims(1) + i-1) idx = idx + 1 end do end do call nga_release(g_b, blo, bhi) c alo(1) = TESTDIM/4 alo(2) = TESTDIM/4 ahi(1) = alo(1) + i_inc ahi(2) = alo(2) + j_inc blo(1) = TESTDIM/4 + 1 blo(2) = TESTDIM/4 + 1 bhi(1) = blo(1) + i_inc bhi(2) = blo(2) + j_inc clo(1) = TESTDIM/4 + 2 clo(2) = TESTDIM/4 + 2 chi(1) = clo(1) + i_inc chi(2) = clo(2) + j_inc alpha = 1.0d00 beta = 1.0d00 c call nga_add_patch(alpha, g_a, alo, ahi, beta, g_b, blo, bhi, + g_c, clo, chi) c call ga_print(g_a) c call ga_print(g_b) c call ga_print(g_c) c c check C for answer c call nga_distribution(g_c,me,tlo,thi) if (tlo(1).lt.clo(1)) tlo(1) = clo(1) if (tlo(2).lt.clo(2)) tlo(2) = clo(2) if (thi(1).gt.chi(1)) thi(1) = chi(1) if (thi(2).gt.chi(2)) thi(2) = chi(2) c if (tlo(1).le.thi(1).and.tlo(2).le.thi(2)) then call nga_access(g_c,tlo,thi,idx,cld) do j = tlo(2), thi(2) jj = j - tlo(2) do i = tlo(1), thi(1) ii = i - tlo(1) val = alpha*dble((j-3)*adims(1)+i-3) + + beta*dble((j-2)*bdims(1)+i-2) if (dbl_mb(idx+jj*cld+ii).ne.val) then write(6,'(i4,a,2i8,2f8.0)') me,' Mismatch for values: ', + i,j,dbl_mb(idx+jj*cld+ii),val endif end do end do call nga_release(g_c, tlo, thi) endif c if (me.eq.0) then write(6,'(a)') 'Successfully completed test of nga_add_patch' endif c status = nga_destroy(g_a) status = nga_destroy(g_b) status = nga_destroy(g_c) call nga_terminate() c call MP_FINALIZE() end ga-5.9.2/global/testing/patch_enumc.c000066400000000000000000000061271500715745200175010ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include "ga.h" #include "macdecls.h" #include "mp3.h" #define VERBOSE 0 #define NELEM 200000 #define test(MT,T,F) \ void test_##MT() \ { \ int g_a, lo[1], hi[1], ld[1], dims[1], chunk[1], i; \ T *buf, *get, start, inc; \ start = 1; \ inc = 1; \ lo[0] = 0; \ hi[0] = NELEM-1; \ dims[0] = NELEM; \ chunk[0] = 0; \ buf = (T*)malloc(sizeof(T) * NELEM); \ get = (T*)malloc(sizeof(T) * NELEM); \ g_a = NGA_Create(MT, 1, dims, "g_a", NULL); \ for (i=0; i<100; i++ ){ \ GA_Patch_enum(g_a, lo[0], hi[0], &start, &inc); \ } \ NGA_Get(g_a, lo, hi, get, ld); \ for (i=0; i #endif #if HAVE_STDLIB_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif #if HAVE_MATH_H # include #endif #include "ga.h" #include "armci.h" #include "message.h" #include "mp3.h" #define SIZE 640 /**< must be >= biggest chunk + 128 */ #define CHUNK_NUM 28 #define TIMER armci_timer #define MALLOC_LOC 1 /**< use ARMCI_Malloc_local instead of plain malloc */ #ifndef GA_ABS # define GA_ABS(a) ((a)>0? (a): -(a)) #endif #define OP_GET 0 #define OP_PUT 1 #define OP_ACC 2 #define ENABLE_CLEANUP 0 static int CHECK_RESULT = 0; static int chunk[CHUNK_NUM] = { 1, 3, 4, 6, 9, 12, 16, 20, 24, 30, 40, 48, 52, 64, 78, 91, 104, 128, 142, 171, 210, 256, 300, 353, 400, 440, 476, 512 }; static int nproc = -1; static int me = -1; static int warn_accuracy = 0; static void fill_array(double *arr, int count, int which); static double time_op(int g_a, double *buf_, int chunk, int loop, int proc, int ndim, int op); static void test_1D(); static void test_2D(); int main(int argc, char **argv) { /* initialize GA */ MP_INIT(argc,argv); GA_Initialize_args(&argc, &argv); me = GA_Nodeid(); nproc = GA_Nnodes(); if (nproc < 2) { if (me == 0) { fprintf(stderr, "USAGE: 2 <= processes - got %d\n", nproc); } GA_Terminate(); MP_FINALIZE(); exit(0); } if (!me) { printf("\n Performance of Basic Blocking Communication Operations\n"); } GA_Sync(); /* test 1 dimension array */ if (!me) { printf("\n\t\t\tContiguous Data Transfer\n"); } test_1D(); /* test 2 dimension array */ if (!me) { printf("\n\t\t\tStrided Data Transfer\n"); } test_2D(); #if 0 if (me == 0) { if (warn_accuracy) { printf("\nWARNING: Your timer does not have sufficient accuracy for this test (%d)\n", warn_accuracy); } printf("\n\n------------ Now we test the same data transfer for correctness ----------\n"); fflush(stdout); } #endif if (me == 0) printf("All tests successful\n"); GA_Terminate(); MP_FINALIZE(); return 0; } double time_op(int g_a, double *buf_, int chunk, int loop, int proc, int ndim, int op) { double start_time = 0; double stop_time = 0; double total_time = 0; int lo[2] = {-1,-1}; int hi[2] = {-1,-1}; int ld = -1; int i = 0; int bal = 0; double *buf = buf_; double alpha = 1; /* get the location within the g_a for the given proc */ NGA_Distribution(g_a, proc, lo, hi); /* determine how much data to grab based on the chunk and dimensionality */ if (ndim == 1) { hi[0] = lo[0] + chunk*chunk - 1; } else if (ndim == 2) { hi[0] = lo[0] + chunk - 1; hi[1] = lo[1] + chunk - 1; ld = chunk; } else { GA_Error("invalid ndim for time_op", ndim); } start_time = TIMER(); for (i=0; i Performance of ga_get, ga_put & ga_acc n = ', n print *,' ' endif c c do loop=1,2 c c*** local ops c call ga_distribution(g_a, me, ilo, ihi, jlo, jhi) call TestPutGetAcc & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo, jhi, .true.) c c*** remote ops c call TestPutGetAcc & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo, jhi,.false.) c enddo end subroutine TestPutGetAcc & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo,jhi, local) implicit none #include "global.fh" #include "testutil.fh" integer num_chunks, chunk(num_chunks) integer n, ilo, ihi, jlo,jhi,g_a double precision buf(*), tg, tp, ta double precision time_acc, time_get, time_put logical local c integer me integer loop, jump, count, bytes c me = ga_nodeid() if (me .eq. 0) then write(6,*)' ' if(local) then write(6,'(21X,8hACCESS [,i3,1h:,i4,1h,,i3,1h:,i4,1h])') & ilo,ihi,jlo,jhi else write(6,'(21X,6hACCESS , 2x, 18Hremote section )') endif write(6,*)'bytes loop get put', & ' accumulate' call flush(6) endif call ga_sync() c do loop = 1, num_chunks bytes = util_mdtob(1)*chunk(loop)*chunk(loop) !how much data is accessed jump = n/(60*loop) ! jump distance between consecutive patches c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, n , 1d0*me*loop) if (me .eq. 0) then tg=time_get(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, n , 1d0*me*loop) if (me .eq. 0) then tp=time_put(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, n , 1d0*me*loop) if (me .eq. 0) then ta=time_acc(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count, $ local) endif call ga_sync() c if (me .eq. 0) then write(6,77)bytes, count, tg, 1d-6*bytes/tg, & tp, 1d-6*bytes/tp, ta, 1d-6*bytes/ta call flush(6) endif enddo c 77 format(i6, i5, 3(d10.3, d9.2,'MB/s')) end double precision function & time_acc(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je logical local integer rows, cols, indx, shifti(3), shiftj(3) c integer ilo, ihi, jlo, jhi double precision seconds, buf c count = 0 rows = ie - is + 1 cols = je - js + 1 shifti(1) = rows shifti(2) = 0 shifti(3) = rows shiftj(1) = 0 shiftj(2) = cols shiftj(3) = cols seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 do jlo = js, je -chunk-jump +1, chunk+jump jhi = jlo + chunk -1 count = count + 1 if (local) then call ga_acc(g_a, ilo, ihi, jlo, jhi, buf, chunk, 1d0) else indx = Mod(count,3) + 1 call ga_acc(g_a, ilo+shifti(indx), ihi+shifti(indx), $ jlo+shiftj(indx), jhi+shiftj(indx), $ buf, chunk, 1d0) endif enddo enddo seconds = util_timer() - seconds c time_acc = seconds/count end double precision function & time_get(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je integer rows, cols, indx, shifti(3), shiftj(3) logical local c integer ilo, ihi, jlo, jhi double precision seconds, buf c count = 0 rows = ie - is + 1 cols = je - js + 1 shifti(1) = rows shifti(2) = 0 shifti(3) = rows shiftj(1) = 0 shiftj(2) = cols shiftj(3) = cols seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 do jlo = js, je -chunk-jump +1, chunk+jump jhi = jlo + chunk -1 count = count + 1 if (local) then call ga_get(g_a, ilo, ihi, jlo, jhi, buf, chunk) else indx = Mod(count,3) + 1 call ga_get(g_a, ilo+shifti(indx), ihi+shifti(indx), $ jlo+shiftj(indx), jhi+shiftj(indx), $ buf, chunk) endif enddo enddo seconds = util_timer() - seconds c time_get = seconds/count end double precision function & time_put(g_a, is, ie, js, je, buf, chunk, jump, count, local) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je integer rows, cols, indx, shifti(3), shiftj(3) logical local c integer ilo, ihi, jlo, jhi double precision seconds, buf c count = 0 rows = ie - is + 1 cols = je - js + 1 shifti(1) = rows shifti(2) = 0 shifti(3) = rows shiftj(1) = 0 shiftj(2) = cols shiftj(3) = cols seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 do jlo = js, je -chunk-jump +1, chunk+jump jhi = jlo + chunk -1 count = count + 1 if (local) then call ga_put(g_a, ilo, ihi, jlo, jhi, buf, chunk) else indx = Mod(count,3) + 1 call ga_put(g_a, ilo+shifti(indx), ihi+shifti(indx), $ jlo+shiftj(indx), jhi+shiftj(indx), $ buf, chunk) endif enddo enddo seconds = util_timer() - seconds c time_put = seconds/count end ga-5.9.2/global/testing/perform.F000066400000000000000000000144001500715745200166210ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c $Id: perform.F,v 1.9 2000-05-25 01:09:20 d3h325 Exp $ program perform c*** c*** Last modification: Fri Jan 13 12:13:27 PST 1995 c*** implicit none #include "mafdecls.fh" #include "global.fh" integer heap c c*** Intitialize a message passing library c #include "mp3.fh" c c*** Intitialize the GA package call ga_initialize() if(ga_nnodes().ne.4 .and. ga_nodeid().eq.0) $ call ga_error('Program requires 4 GA processes',ga_nnodes()) c c*** Initialize the MA package heap = 450000 if (.not. ma_init(MT_DBL, heap,heap)) $ call ga_error('ma init failed',2*heap) c call testit() c if(ga_nodeid().eq.0) print *, 'All tests successful' c call ga_terminate() c call MP_FINALIZE() end c----------------- subroutine testit() implicit none #include "mafdecls.fh" #include "global.fh" c integer n, nn, num_chunks parameter (n = 710, nn = n*n/4, num_chunks=12) double precision buf(nn) c integer g_a integer ilo, ihi, jlo, jhi integer nproc, me, loop integer chunk(num_chunks) data chunk /1,3,4,9,16,30,64,91,128,171,256,353/ c nproc = ga_nnodes() me = ga_nodeid() c c*** Create global array if (.not. ga_create(MT_DBL, n, n, 'a', 0, 0, g_a)) $ call ga_error(' ga_create failed ',1) c do loop=1,nn buf(loop) = .01d0 enddo call ga_zero(g_a) c if (me .eq. 0) then write(*,*)' ' print *,'> Performance of ga_get, ga_put & ga_acc n = ', n print *,' ' endif c c do loop=1,2 c c*** local ops c call ga_distribution(g_a, me, ilo, ihi, jlo, jhi) call TestPutGetAcc & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo, jhi) c c*** remote ops c call ga_distribution(g_a, nproc-1, ilo, ihi, jlo, jhi) call TestPutGetAcc & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo, jhi) c enddo end subroutine TestPutGetAcc & (g_a, n, chunk, num_chunks, buf, ilo, ihi, jlo,jhi) implicit none #include "global.fh" #include "testutil.fh" c integer num_chunks, chunk(num_chunks) integer n, ilo, ihi, jlo,jhi,g_a double precision buf(*), tg, tp, ta double precision time_acc, time_get, time_put c integer me integer loop, jump, count, bytes c me = ga_nodeid() if (me .eq. 0) then write(6,*)' ' write(6,'(21X,8hACCESS [,i3,1h:,i4,1h,,i3,1h:,i4,1h])') & ilo,ihi,jlo,jhi write(6,*)'bytes loop get put', & ' accumulate' call flush(6) endif call ga_sync() c do loop = 1, num_chunks bytes = util_mdtob(1)*chunk(loop)*chunk(loop) !how much data is accessed jump = n/(60*loop) ! jump distance between consecutive patches c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, n , 1d0*me*loop) if (me .eq. 0) then tg= time_get(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count) endif call ga_sync() c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, n , 1d0*me*loop) if (me .eq. 0) then tp= time_put(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count) endif call ga_sync() c c everybody touches own data call ga_fill_patch(g_a, 1, n, 1, n , 1d0*me*loop) if (me .eq. 0) then ta= time_acc(g_a,ilo,ihi,jlo,jhi,buf,chunk(loop),jump,count) endif call ga_sync() c if (me .eq. 0) then write(6,77)bytes, count, tg, 1d-6*bytes/tg, & tp, 1d-6*bytes/tp, ta, 1d-6*bytes/ta call flush(6) endif enddo c 77 format(i6, i5, 3(d10.3, d9.2,'MB/s')) end double precision function & time_acc(g_a, is, ie, js, je, buf, chunk, jump, count) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je c integer ilo, ihi, jlo, jhi double precision seconds, buf c count = 0 seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 do jlo = js, je -chunk-jump +1, chunk+jump jhi = jlo + chunk -1 count = count + 1 call ga_acc(g_a, ilo, ihi, jlo, jhi, buf, chunk, 1d0) enddo enddo seconds = util_timer() - seconds c time_acc = seconds/count end double precision function & time_get(g_a, is, ie, js, je, buf, chunk, jump, count) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je c integer ilo, ihi, jlo, jhi double precision seconds, buf c count = 0 seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 do jlo = js, je -chunk-jump +1, chunk+jump jhi = jlo + chunk -1 count = count + 1 call ga_get(g_a, ilo, ihi, jlo, jhi, buf, chunk) enddo enddo seconds = util_timer() - seconds c time_get = seconds/count end double precision function & time_put(g_a, is, ie, js, je, buf, chunk, jump, count) c implicit none #include "global.fh" #include "testutil.fh" c integer g_a, chunk, jump, count, is, js, ie, je c integer ilo, ihi, jlo, jhi double precision seconds, buf c count = 0 seconds = util_timer() c c distance between consecutive patches increased by jump c to destroy locality of reference do ilo = is, ie -chunk-jump +1, chunk+jump ihi = ilo + chunk -1 do jlo = js, je -chunk-jump +1, chunk+jump jhi = jlo + chunk -1 count = count + 1 call ga_put(g_a, ilo, ihi, jlo, jhi, buf, chunk) enddo enddo seconds = util_timer() - seconds c time_put = seconds/count end ga-5.9.2/global/testing/perform.results000066400000000000000000000472361500715745200201520ustar00rootroot00000000000000 Performance of GA Communication Primitives ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Jarek Nieplocha Uniform block distribution of matrix 710x710. Accumulate operation is atomic. Program run on 4 processors. Process 0 holds block [1:355, 1:355], process 3 holds block [356:710, 356:710]. Process 0 first accesses local data and then remote data. To reduce data caching, each time a different section of the matrix is accessed. ................................................................................ machine: Cray T3D name: h4p.nersc.gov options: -O1 -h inline3, readahead on, -DFLUSHCACHE note: lack of cache coherency degrades latency of "local" get date: Thu Dec 28 13:49:40 PST 1995 ACCESS [ 1: 355, 1: 355] bytes loop get put accumulate 8 841 0.336E-04 0.24E+00MB/s 0.154E-04 0.52E+00MB/s 0.451E-04 0.18E+00MB/s 72 1936 0.365E-04 0.20E+01MB/s 0.174E-04 0.41E+01MB/s 0.473E-04 0.15E+01MB/s 128 2500 0.379E-04 0.34E+01MB/s 0.183E-04 0.70E+01MB/s 0.492E-04 0.26E+01MB/s 648 1024 0.471E-04 0.14E+02MB/s 0.253E-04 0.26E+02MB/s 0.638E-04 0.10E+02MB/s 2048 361 0.679E-04 0.30E+02MB/s 0.367E-04 0.56E+02MB/s 0.104E-03 0.20E+02MB/s 7200 121 0.128E-03 0.56E+02MB/s 0.771E-04 0.93E+02MB/s 0.244E-03 0.29E+02MB/s 32768 25 0.386E-03 0.85E+02MB/s 0.355E-03 0.92E+02MB/s 0.513E-03 0.64E+02MB/s 66248 9 0.717E-03 0.92E+02MB/s 0.681E-03 0.97E+02MB/s 0.874E-03 0.76E+02MB/s 131072 4 0.132E-02 0.10E+03MB/s 0.127E-02 0.10E+03MB/s 0.161E-02 0.82E+02MB/s 233928 4 0.225E-02 0.10E+03MB/s 0.220E-02 0.11E+03MB/s 0.258E-02 0.91E+02MB/s 524288 1 0.392E-02 0.13E+03MB/s 0.396E-02 0.13E+03MB/s 0.557E-02 0.94E+02MB/s 996872 1 0.695E-02 0.14E+03MB/s 0.695E-02 0.14E+03MB/s 0.101E-01 0.98E+02MB/s ACCESS [356: 710,356: 710] bytes loop get put accumulate 8 841 0.216E-04 0.37E+00MB/s 0.157E-04 0.51E+00MB/s 0.367E-04 0.22E+00MB/s 72 1936 0.280E-04 0.26E+01MB/s 0.180E-04 0.40E+01MB/s 0.466E-04 0.15E+01MB/s 128 2500 0.308E-04 0.42E+01MB/s 0.192E-04 0.67E+01MB/s 0.520E-04 0.25E+01MB/s 648 1024 0.591E-04 0.11E+02MB/s 0.278E-04 0.23E+02MB/s 0.954E-04 0.68E+01MB/s 2048 361 0.117E-03 0.17E+02MB/s 0.463E-04 0.44E+02MB/s 0.193E-03 0.11E+02MB/s 7200 121 0.315E-03 0.23E+02MB/s 0.108E-03 0.67E+02MB/s 0.533E-03 0.14E+02MB/s 32768 25 0.119E-02 0.28E+02MB/s 0.362E-03 0.90E+02MB/s 0.189E-02 0.17E+02MB/s 66248 9 0.231E-02 0.29E+02MB/s 0.671E-03 0.99E+02MB/s 0.359E-02 0.18E+02MB/s 131072 4 0.425E-02 0.31E+02MB/s 0.125E-02 0.11E+03MB/s 0.732E-02 0.18E+02MB/s 233928 4 0.733E-02 0.32E+02MB/s 0.211E-02 0.11E+03MB/s 0.122E-01 0.19E+02MB/s 524288 1 0.161E-01 0.32E+02MB/s 0.461E-02 0.11E+03MB/s 0.263E-01 0.20E+02MB/s 996872 1 0.296E-01 0.34E+02MB/s 0.843E-02 0.12E+03MB/s 0.485E-01 0.21E+02MB/s machine: Cray T3D name: h4p.nersc.gov options: -O3 -h inline3, readahead on, implicit (automatic) cache invalidation note: lack of cache coherency degrades latency of "local" get date: Mon Mar 25 10:32:42 PST 1996 ACCESS [ 1: 355, 1: 355] bytes loop get put accumulate 8 841 0.223E-04 0.35E+00MB/s 0.170E-04 0.47E+00MB/s 0.270E-04 0.30E+00MB/s 72 1936 0.255E-04 0.28E+01MB/s 0.189E-04 0.38E+01MB/s 0.289E-04 0.25E+01MB/s 128 2500 0.269E-04 0.48E+01MB/s 0.197E-04 0.65E+01MB/s 0.301E-04 0.43E+01MB/s 648 1024 0.361E-04 0.18E+02MB/s 0.261E-04 0.25E+02MB/s 0.426E-04 0.15E+02MB/s 2048 361 0.579E-04 0.35E+02MB/s 0.360E-04 0.57E+02MB/s 0.754E-04 0.27E+02MB/s 7200 121 0.121E-03 0.59E+02MB/s 0.759E-04 0.95E+02MB/s 0.226E-03 0.32E+02MB/s 32768 25 0.371E-03 0.88E+02MB/s 0.347E-03 0.95E+02MB/s 0.573E-03 0.57E+02MB/s 66248 9 0.695E-03 0.95E+02MB/s 0.668E-03 0.99E+02MB/s 0.969E-03 0.68E+02MB/s 131072 4 0.129E-02 0.10E+03MB/s 0.125E-02 0.10E+03MB/s 0.175E-02 0.75E+02MB/s 233928 4 0.220E-02 0.11E+03MB/s 0.216E-02 0.11E+03MB/s 0.277E-02 0.84E+02MB/s 524288 1 0.384E-02 0.14E+03MB/s 0.382E-02 0.14E+03MB/s 0.591E-02 0.89E+02MB/s 996872 1 0.687E-02 0.15E+03MB/s 0.680E-02 0.15E+03MB/s 0.106E-01 0.94E+02MB/s ACCESS [356: 710,356: 710] bytes loop get put accumulate 8 841 0.244E-04 0.33E+00MB/s 0.187E-04 0.43E+00MB/s 0.347E-04 0.23E+00MB/s 72 1936 0.304E-04 0.24E+01MB/s 0.210E-04 0.34E+01MB/s 0.471E-04 0.15E+01MB/s 128 2500 0.333E-04 0.38E+01MB/s 0.221E-04 0.58E+01MB/s 0.542E-04 0.24E+01MB/s 648 1024 0.611E-04 0.11E+02MB/s 0.317E-04 0.20E+02MB/s 0.106E-03 0.61E+01MB/s 2048 361 0.120E-03 0.17E+02MB/s 0.494E-04 0.41E+02MB/s 0.213E-03 0.96E+01MB/s 7200 121 0.321E-03 0.22E+02MB/s 0.111E-03 0.65E+02MB/s 0.579E-03 0.12E+02MB/s 32768 25 0.121E-02 0.27E+02MB/s 0.364E-03 0.90E+02MB/s 0.229E-02 0.14E+02MB/s 66248 9 0.235E-02 0.28E+02MB/s 0.671E-03 0.99E+02MB/s 0.403E-02 0.16E+02MB/s 131072 4 0.443E-02 0.30E+02MB/s 0.125E-02 0.11E+03MB/s 0.847E-02 0.15E+02MB/s 233928 4 0.758E-02 0.31E+02MB/s 0.210E-02 0.11E+03MB/s 0.134E-01 0.17E+02MB/s 524288 1 0.166E-01 0.32E+02MB/s 0.451E-02 0.12E+03MB/s 0.297E-01 0.18E+02MB/s 996872 1 0.303E-01 0.33E+02MB/s 0.838E-02 0.12E+03MB/s 0.509E-01 0.20E+02MB/s ................................................................................ machine: KSR-2 name: circus.pnl.gov options: -O1 -qdiv date: Mon Jan 23 09:22:48 PST 1995 ACCESS [ 1 : 355 , 1 : 355 ] bytes loop get put accumulate 8 841 0.357D-04 0.22D+00MB/s 0.350D-04 0.23D+00MB/s 0.379D-04 0.21D+00MB/s 72 1936 0.415D-04 0.17D+01MB/s 0.419D-04 0.17D+01MB/s 0.444D-04 0.16D+01MB/s 128 2500 0.454D-04 0.28D+01MB/s 0.463D-04 0.28D+01MB/s 0.493D-04 0.26D+01MB/s 648 1024 0.714D-04 0.91D+01MB/s 0.753D-04 0.86D+01MB/s 0.813D-04 0.80D+01MB/s 2048 361 0.119D-03 0.17D+02MB/s 0.129D-03 0.16D+02MB/s 0.147D-03 0.14D+02MB/s 7200 121 0.311D-03 0.23D+02MB/s 0.331D-03 0.22D+02MB/s 0.377D-03 0.19D+02MB/s 32768 25 0.109D-02 0.30D+02MB/s 0.113D-02 0.29D+02MB/s 0.128D-02 0.26D+02MB/s 66248 9 0.234D-02 0.28D+02MB/s 0.229D-02 0.29D+02MB/s 0.260D-02 0.26D+02MB/s 131072 4 0.446D-02 0.29D+02MB/s 0.444D-02 0.29D+02MB/s 0.495D-02 0.26D+02MB/s 233928 4 0.774D-02 0.30D+02MB/s 0.769D-02 0.30D+02MB/s 0.873D-02 0.27D+02MB/s 524288 1 0.171D-01 0.31D+02MB/s 0.171D-01 0.31D+02MB/s 0.191D-01 0.27D+02MB/s 996872 1 0.308D-01 0.32D+02MB/s 0.307D-01 0.32D+02MB/s 0.348D-01 0.29D+02MB/s ACCESS [ 356 : 710 , 356 : 710 ] bytes loop get put accumulate 8 841 0.373D-04 0.21D+00MB/s 0.364D-04 0.22D+00MB/s 0.448D-04 0.18D+00MB/s 72 1936 0.427D-04 0.17D+01MB/s 0.430D-04 0.17D+01MB/s 0.537D-04 0.13D+01MB/s 128 2500 0.469D-04 0.27D+01MB/s 0.479D-04 0.27D+01MB/s 0.604D-04 0.21D+01MB/s 648 1024 0.751D-04 0.86D+01MB/s 0.801D-04 0.81D+01MB/s 0.118D-03 0.55D+01MB/s 2048 361 0.135D-03 0.15D+02MB/s 0.144D-03 0.14D+02MB/s 0.160D-03 0.13D+02MB/s 7200 121 0.365D-03 0.20D+02MB/s 0.376D-03 0.19D+02MB/s 0.410D-03 0.18D+02MB/s 32768 25 0.133D-02 0.25D+02MB/s 0.137D-02 0.24D+02MB/s 0.148D-02 0.22D+02MB/s 66248 9 0.276D-02 0.24D+02MB/s 0.276D-02 0.24D+02MB/s 0.307D-02 0.22D+02MB/s 131072 4 0.543D-02 0.24D+02MB/s 0.541D-02 0.24D+02MB/s 0.588D-02 0.22D+02MB/s 233928 4 0.928D-02 0.25D+02MB/s 0.917D-02 0.26D+02MB/s 0.101D-01 0.23D+02MB/s 524288 1 0.209D-01 0.25D+02MB/s 0.210D-01 0.25D+02MB/s 0.227D-01 0.23D+02MB/s 996872 1 0.353D-01 0.28D+02MB/s 0.347D-01 0.29D+02MB/s 0.387D-01 0.26D+02MB/s ................................................................................ machine: Intel Paragon name: trex.caltech.edu options: -O2 -Msafeptr -Knoieee -Mquad -Mreentrant; run with -plk note:122K message buffer; masktrap responsible for latency of "local" accumulate date: Fri Dec 29 11:28:54 PST 1995 ACCESS [ 1: 355, 1: 355] bytes loop get put accumulate 8 841 0.707D-04 0.11D+00MB/s 0.297D-04 0.27D+00MB/s 0.167D-03 0.48D-01MB/s 72 1936 0.352D-04 0.20D+01MB/s 0.324D-04 0.22D+01MB/s 0.178D-03 0.41D+00MB/s 128 2500 0.379D-04 0.34D+01MB/s 0.341D-04 0.38D+01MB/s 0.183D-03 0.70D+00MB/s 648 1024 0.588D-04 0.11D+02MB/s 0.450D-04 0.14D+02MB/s 0.221D-03 0.29D+01MB/s 2048 361 0.108D-03 0.19D+02MB/s 0.695D-04 0.29D+02MB/s 0.306D-03 0.67D+01MB/s 7200 121 0.277D-03 0.26D+02MB/s 0.151D-03 0.48D+02MB/s 0.468D-03 0.15D+02MB/s 32768 25 0.550D-03 0.60D+02MB/s 0.550D-03 0.60D+02MB/s 0.109D-02 0.30D+02MB/s 66248 9 0.972D-03 0.68D+02MB/s 0.980D-03 0.68D+02MB/s 0.190D-02 0.35D+02MB/s 131072 4 0.170D-02 0.77D+02MB/s 0.174D-02 0.75D+02MB/s 0.321D-02 0.41D+02MB/s 233928 4 0.279D-02 0.84D+02MB/s 0.282D-02 0.83D+02MB/s 0.540D-02 0.43D+02MB/s 524288 1 0.601D-02 0.87D+02MB/s 0.618D-02 0.85D+02MB/s 0.113D-01 0.47D+02MB/s 996872 1 0.109D-01 0.92D+02MB/s 0.108D-01 0.93D+02MB/s 0.216D-01 0.46D+02MB/s ACCESS [356: 710,356: 710] bytes loop get put accumulate 8 841 0.487D-03 0.16D-01MB/s 0.629D-04 0.13D+00MB/s 0.646D-04 0.12D+00MB/s 72 1936 0.507D-03 0.14D+00MB/s 0.656D-04 0.11D+01MB/s 0.688D-04 0.10D+01MB/s 128 2500 0.524D-03 0.24D+00MB/s 0.678D-04 0.19D+01MB/s 0.702D-04 0.18D+01MB/s 648 1024 0.560D-03 0.12D+01MB/s 0.859D-04 0.75D+01MB/s 0.893D-04 0.73D+01MB/s 2048 361 0.726D-03 0.28D+01MB/s 0.162D-03 0.13D+02MB/s 0.161D-03 0.13D+02MB/s 7200 121 0.132D-02 0.55D+01MB/s 0.376D-03 0.19D+02MB/s 0.353D-03 0.20D+02MB/s 32768 25 0.270D-02 0.12D+02MB/s 0.130D-02 0.25D+02MB/s 0.117D-02 0.28D+02MB/s 66248 9 0.499D-02 0.13D+02MB/s 0.250D-02 0.27D+02MB/s 0.211D-02 0.31D+02MB/s 131072 4 0.905D-02 0.14D+02MB/s 0.477D-02 0.27D+02MB/s 0.402D-02 0.33D+02MB/s 233928 4 0.142D-01 0.17D+02MB/s 0.811D-02 0.29D+02MB/s 0.665D-02 0.35D+02MB/s 524288 1 0.323D-01 0.16D+02MB/s 0.181D-01 0.29D+02MB/s 0.147D-01 0.36D+02MB/s 996872 1 0.599D-01 0.17D+02MB/s 0.337D-01 0.30D+02MB/s 0.279D-01 0.36D+02MB/s ................................................................................ machine: IBM SP2 (MPL) name: et{0201,0301,0401,0601}nwmpp1.emsl.pnl.gov options: -O (cc) -O2 (f77) note:122K message buffer; AIX4, TARGET=SP date: Wed Oct 9 12:12:39 PDT 1996 ACCESS [ 1: 355, 1: 355] bytes loop get put accumulate 8 841 .972D-05 .82D+00MB/s .928D-05 .86D+00MB/s .134D-04 .60D+00MB/s 72 1936 .103D-04 .70D+01MB/s .103D-04 .70D+01MB/s .146D-04 .49D+01MB/s 128 2500 .104D-04 .12D+02MB/s .110D-04 .12D+02MB/s .153D-04 .84D+01MB/s 648 1024 .143D-04 .45D+02MB/s .157D-04 .41D+02MB/s .199D-04 .33D+02MB/s 2048 361 .249D-04 .82D+02MB/s .315D-04 .65D+02MB/s .346D-04 .59D+02MB/s 7200 121 .628D-04 .11D+03MB/s .764D-04 .94D+02MB/s .822D-04 .88D+02MB/s 32768 25 .318D-03 .10D+03MB/s .380D-03 .86D+02MB/s .406D-03 .81D+02MB/s 66248 9 .876D-03 .76D+02MB/s .867D-03 .76D+02MB/s .909D-03 .73D+02MB/s 131072 4 .165D-02 .80D+02MB/s .167D-02 .78D+02MB/s .171D-02 .77D+02MB/s 233928 4 .293D-02 .80D+02MB/s .302D-02 .77D+02MB/s .298D-02 .79D+02MB/s 524288 1 .665D-02 .79D+02MB/s .658D-02 .80D+02MB/s .667D-02 .79D+02MB/s 996872 1 .120D-01 .83D+02MB/s .121D-01 .82D+02MB/s .123D-01 .81D+02MB/s ACCESS [356: 710,356: 710] bytes loop get put accumulate 8 841 .256D-03 .31D-01MB/s .981D-04 .82D-01MB/s .904D-04 .88D-01MB/s 72 1936 .253D-03 .28D+00MB/s .115D-03 .63D+00MB/s .943D-04 .76D+00MB/s 128 2500 .255D-03 .50D+00MB/s .105D-03 .12D+01MB/s .947D-04 .14D+01MB/s 648 1024 .299D-03 .22D+01MB/s .139D-03 .47D+01MB/s .121D-03 .53D+01MB/s 2048 361 .394D-03 .52D+01MB/s .181D-03 .11D+02MB/s .192D-03 .11D+02MB/s 7200 121 .679D-03 .11D+02MB/s .392D-03 .18D+02MB/s .366D-03 .20D+02MB/s 32768 25 .251D-02 .13D+02MB/s .191D-02 .17D+02MB/s .192D-02 .17D+02MB/s 66248 9 .448D-02 .15D+02MB/s .333D-02 .20D+02MB/s .347D-02 .19D+02MB/s 131072 4 .791D-02 .17D+02MB/s .619D-02 .21D+02MB/s .635D-02 .21D+02MB/s 233928 4 .134D-01 .17D+02MB/s .105D-01 .22D+02MB/s .103D-01 .23D+02MB/s 524288 1 .330D-01 .16D+02MB/s .241D-01 .22D+02MB/s .241D-01 .22D+02MB/s 996872 1 .580D-01 .17D+02MB/s .466D-01 .21D+02MB/s .436D-01 .23D+02MB/s ................................................................................ machine: IBM SP1 (MPL) name: spnode{022,024,025,026}mcs.anl.gov options: -O (cc) -O2 (f77) note:122K message buffer; latency of rcvncall in MPL is much higher than in EUIH date: Fri Dec 29 15:36:19 CST 199 ACCESS [ 1: 355, 1: 355] bytes loop get put accumulate 8 841 .132D-04 .61D+00MB/s .132D-04 .61D+00MB/s .183D-04 .44D+00MB/s 72 1936 .148D-04 .49D+01MB/s .145D-04 .50D+01MB/s .196D-04 .37D+01MB/s 128 2500 .156D-04 .82D+01MB/s .157D-04 .82D+01MB/s .214D-04 .60D+01MB/s 648 1024 .252D-04 .26D+02MB/s .240D-04 .27D+02MB/s .303D-04 .21D+02MB/s 2048 361 .446D-04 .46D+02MB/s .403D-04 .51D+02MB/s .491D-04 .42D+02MB/s 7200 121 .884D-04 .81D+02MB/s .912D-04 .79D+02MB/s .108D-03 .67D+02MB/s 32768 25 .464D-03 .71D+02MB/s .472D-03 .69D+02MB/s .521D-03 .63D+02MB/s 66248 9 .869D-03 .76D+02MB/s .911D-03 .73D+02MB/s .102D-02 .65D+02MB/s 131072 4 .170D-02 .77D+02MB/s .171D-02 .77D+02MB/s .194D-02 .68D+02MB/s 233928 4 .290D-02 .81D+02MB/s .299D-02 .78D+02MB/s .330D-02 .71D+02MB/s 524288 1 .637D-02 .82D+02MB/s .643D-02 .82D+02MB/s .728D-02 .72D+02MB/s 996872 1 .116D-01 .86D+02MB/s .116D-01 .86D+02MB/s .136D-01 .73D+02MB/s ACCESS [356: 710,356: 710] bytes loop get put accumulate 8 841 .517D-03 .15D-01MB/s .752D-04 .11D+00MB/s .771D-04 .10D+00MB/s 72 1936 .549D-03 .13D+00MB/s .978D-04 .74D+00MB/s .949D-04 .76D+00MB/s 128 2500 .570D-03 .22D+00MB/s .973D-04 .13D+01MB/s .994D-04 .13D+01MB/s 648 1024 .625D-03 .10D+01MB/s .145D-03 .45D+01MB/s .147D-03 .44D+01MB/s 2048 361 .699D-03 .29D+01MB/s .277D-03 .74D+01MB/s .284D-03 .72D+01MB/s 7200 121 .113D-02 .64D+01MB/s .655D-03 .11D+02MB/s .651D-03 .11D+02MB/s 32768 25 .332D-02 .99D+01MB/s .255D-02 .13D+02MB/s .253D-02 .13D+02MB/s 66248 9 .531D-02 .12D+02MB/s .449D-02 .15D+02MB/s .444D-02 .15D+02MB/s 131072 4 .103D-01 .13D+02MB/s .810D-02 .16D+02MB/s .810D-02 .16D+02MB/s 233928 4 .166D-01 .14D+02MB/s .134D-01 .17D+02MB/s .142D-01 .16D+02MB/s 524288 1 .370D-01 .14D+02MB/s .298D-01 .18D+02MB/s .300D-01 .17D+02MB/s 996872 1 .703D-01 .14D+02MB/s .602D-01 .17D+02MB/s .576D-01 .17D+02MB/s ................................................................................ machine: 4-processor 75MHz SGI Power Challenge name: coho.pnl.gov options: -O3 (f77) -O (cc) note: SGI uslocks instead of semaphores used for locking date: Tue Oct 10 15:47:51 PDT 1995 ACCESS [ 1: 355, 1: 355] bytes loop get put accumulate 8 841 0.106D-04 0.76D+00MB/s 0.930D-05 0.86D+00MB/s 0.115D-04 0.70D+00MB/s 72 1936 0.120D-04 0.60D+01MB/s 0.107D-04 0.67D+01MB/s 0.124D-04 0.58D+01MB/s 128 2500 0.136D-04 0.94D+01MB/s 0.121D-04 0.11D+02MB/s 0.129D-04 0.99D+01MB/s 648 1024 0.177D-04 0.37D+02MB/s 0.164D-04 0.40D+02MB/s 0.164D-04 0.40D+02MB/s 2048 361 0.259D-04 0.79D+02MB/s 0.243D-04 0.84D+02MB/s 0.213D-04 0.96D+02MB/s 7200 121 0.461D-04 0.16D+03MB/s 0.440D-04 0.16D+03MB/s 0.396D-04 0.18D+03MB/s 32768 25 0.125D-03 0.26D+03MB/s 0.121D-03 0.27D+03MB/s 0.114D-03 0.29D+03MB/s 66248 9 0.208D-03 0.32D+03MB/s 0.192D-03 0.35D+03MB/s 0.222D-03 0.30D+03MB/s 131072 4 0.390D-03 0.34D+03MB/s 0.370D-03 0.35D+03MB/s 0.389D-03 0.34D+03MB/s 233928 4 0.674D-03 0.35D+03MB/s 0.542D-03 0.43D+03MB/s 0.677D-03 0.35D+03MB/s 524288 1 0.226D-02 0.23D+03MB/s 0.127D-02 0.41D+03MB/s 0.147D-02 0.36D+03MB/s 996872 1 0.331D-02 0.30D+03MB/s 0.211D-02 0.47D+03MB/s 0.276D-02 0.36D+03MB/s ACCESS [356: 710,356: 710] bytes loop get put accumulate 8 841 0.133D-04 0.60D+00MB/s 0.118D-04 0.68D+00MB/s 0.126D-04 0.64D+00MB/s 72 1936 0.146D-04 0.49D+01MB/s 0.132D-04 0.55D+01MB/s 0.151D-04 0.48D+01MB/s 128 2500 0.158D-04 0.81D+01MB/s 0.144D-04 0.89D+01MB/s 0.160D-04 0.80D+01MB/s 648 1024 0.261D-04 0.25D+02MB/s 0.245D-04 0.26D+02MB/s 0.277D-04 0.23D+02MB/s 2048 361 0.510D-04 0.40D+02MB/s 0.490D-04 0.42D+02MB/s 0.569D-04 0.36D+02MB/s 7200 121 0.128D-03 0.56D+02MB/s 0.125D-03 0.58D+02MB/s 0.156D-03 0.46D+02MB/s 32768 25 0.495D-03 0.66D+02MB/s 0.492D-03 0.67D+02MB/s 0.674D-03 0.49D+02MB/s 66248 9 0.103D-02 0.64D+02MB/s 0.954D-03 0.69D+02MB/s 0.128D-02 0.52D+02MB/s 131072 4 0.187D-02 0.70D+02MB/s 0.186D-02 0.71D+02MB/s 0.250D-02 0.52D+02MB/s 233928 4 0.324D-02 0.72D+02MB/s 0.323D-02 0.72D+02MB/s 0.439D-02 0.53D+02MB/s 524288 1 0.802D-02 0.65D+02MB/s 0.719D-02 0.73D+02MB/s 0.990D-02 0.53D+02MB/s 996872 1 0.143D-01 0.70D+02MB/s 0.131D-01 0.76D+02MB/s 0.182D-01 0.55D+02MB/s ................................................................................ machine: 4-processor Fujitsu VX-4 name: kaiousei.fecit.co.uk note: initial port, performance optimizations not completed date: Thu Dec 4 12:04:49 PST 1997 ACCESS [ 1: 710, 1: 355] bytes loop get put accumulate 8 1711 0.131d-04 0.61d+00MB/s 0.267d-04 0.30d+00MB/s 0.474d-04 0.17d+00MB/s 72 3872 0.134d-04 0.54d+01MB/s 0.273d-04 0.26d+01MB/s 0.491d-04 0.15d+01MB/s 128 5050 0.136d-04 0.94d+01MB/s 0.275d-04 0.46d+01MB/s 0.509d-04 0.25d+01MB/s 648 2048 0.148d-04 0.44d+02MB/s 0.287d-04 0.23d+02MB/s 0.655d-04 0.99d+01MB/s 2048 741 0.167d-04 0.12d+03MB/s 0.300d-04 0.68d+02MB/s 0.103d-03 0.20d+02MB/s 7200 242 0.203d-04 0.35d+03MB/s 0.359d-04 0.20d+03MB/s 0.243d-03 0.30d+02MB/s 32768 50 0.317d-04 0.10d+04MB/s 0.429d-04 0.76d+03MB/s 0.302d-03 0.11d+03MB/s 66248 21 0.430d-04 0.15d+04MB/s 0.557d-04 0.12d+04MB/s 0.510d-03 0.13d+03MB/s 131072 10 0.588d-04 0.22d+04MB/s 0.685d-04 0.19d+04MB/s 0.634d-03 0.21d+03MB/s 233928 8 0.828d-04 0.28d+04MB/s 0.948d-04 0.25d+04MB/s 0.106d-02 0.22d+03MB/s 524288 2 0.151d-03 0.35d+04MB/s 0.149d-03 0.35d+04MB/s 0.156d-02 0.34d+03MB/s 996872 2 0.226d-03 0.44d+04MB/s 0.237d-03 0.42d+04MB/s 0.293d-02 0.34d+03MB/s ACCESS [ 1: 710,356: 710] bytes loop get put accumulate 8 1711 0.260d-04 0.31d+00MB/s 0.301d-04 0.27d+00MB/s 0.673d-04 0.12d+00MB/s 72 3872 0.544d-04 0.13d+01MB/s 0.343d-04 0.21d+01MB/s 0.103d-03 0.70d+00MB/s 128 5050 0.629d-04 0.20d+01MB/s 0.358d-04 0.36d+01MB/s 0.121d-03 0.11d+01MB/s 648 2048 0.112d-03 0.58d+01MB/s 0.447d-04 0.14d+02MB/s 0.221d-03 0.29d+01MB/s 2048 741 0.179d-03 0.11d+02MB/s 0.562d-04 0.36d+02MB/s 0.374d-03 0.55d+01MB/s 7200 242 0.315d-03 0.23d+02MB/s 0.864d-04 0.83d+02MB/s 0.768d-03 0.94d+01MB/s 32768 50 0.667d-03 0.49d+02MB/s 0.177d-03 0.18d+03MB/s 0.158d-02 0.21d+02MB/s 66248 21 0.979d-03 0.68d+02MB/s 0.273d-03 0.24d+03MB/s 0.234d-02 0.28d+02MB/s 131072 10 0.149d-02 0.88d+02MB/s 0.212d-01 0.62d+01MB/s 0.334d-02 0.39d+02MB/s 233928 8 0.206d-02 0.11d+03MB/s 0.676d-03 0.35d+03MB/s 0.498d-02 0.47d+02MB/s 524288 2 0.327d-02 0.16d+03MB/s 0.131d-02 0.40d+03MB/s 0.781d-02 0.67d+02MB/s 996872 2 0.509d-02 0.20d+03MB/s 0.228d-02 0.44d+03MB/s 0.128d-01 0.78d+02MB/s ga-5.9.2/global/testing/pg2test.F000066400000000000000000002151051500715745200165440ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c $Id: pg2test.F,v 1.6 2004-12-01 22:39:46 manoj Exp $ c vector boxes lack arithmetic precision # define THRESH 1d-13 # define THRESHF 1e-5 #define MISMATCH(x,y) abs(x-y)/max(1d0,abs(x)).gt.THRESH #define MISMATCHF(x,y) abs(x-y)/max(1.0,abs(x)).gt.THRESHF #define NEW_API c#define GA3 #define NGA_GATSCAT program main implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer heap, stack, fudge, ma_heap, me, nproc integer inode,proclist(100),i,j,nprocs integer proc_group(0:100) logical status parameter (heap=200*200*4, fudge=100, stack=200*200) integer n,m,grp_id parameter (n = 128) integer ndim, dims(2), chunk(2) integer g_w, midproc c c*** Intitialize a message passing library c #include "mp3.fh" c c*** Initialize GA c c There are 2 choices: ga_initialize or ga_initialize_ltd. c In the first case, there is no explicit limit on memory usage. c In the second, user can set limit (per processor) in bytes. c call ga_initialize() nproc = ga_nnodes() me = ga_nodeid() c c*** Initialize the MA package c MA must be initialized before any global array is allocated c ma_heap = heap/nproc + fudge ma_heap = 2*ma_heap status = ma_init(MT_DCPL, stack, ma_heap) if (.not. status) call ga_error('ma_init failed',-1) c c we can also use GA_set_memory_limit BEFORE first ga_create call c call GA_set_memory_limit(util_mdtob(ma_heap)) c c*** Create a global array on the current default (world) group c ndim = 2 dims(1) = n dims(2) = n g_w = ga_create_handle() call ga_set_data(g_w,ndim,dims,MT_DBL) call ga_set_array_name(g_w,'w') status = ga_allocate(g_w) call ga_zero(g_w) c midproc = nproc/2 do i = 1, midproc proclist(i) = i-1 end do nprocs = midproc proc_group(0) = ga_pgroup_create(proclist, nprocs) do i = midproc + 1, nproc proclist(i-midproc) = i-1 end do nprocs = nproc - midproc proc_group(1) = ga_pgroup_create(proclist, nprocs) c write(6,*) 'Groups have been created' call ga_sync() if (me.ge.midproc) then call runtest(proc_group(1), g_w) else endif c c*** Check if memory limits are enforced c c call check_mem(util_mdtob(ma_heap*ga_nnodes())) c c*** Tidy up the GA package c call ga_sync() write(0,*) 'Calling ga_terminate' call ga_terminate() c c*** Tidy up after message-passing library c call MP_FINALIZE() c stop end c subroutine runtest(grp_id, g_w) implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer me, nproc, grp_id, g_w logical status me = ga_pgroup_nodeid(grp_id) nproc = ga_pgroup_nnodes(grp_id) if(me.eq.0)then print *,' GA initialized ' call ffflush(6) endif c c c Uncomment the below line to register external memory allocator c for dynamic arrays inside GA routines. c call register_ext_memory() c if(me.eq.(nproc-1))then print *, 'using ', nproc,' process(es) ', ga_cluster_nnodes(), $ ' cluster nodes' print *,'process ', me, ' is on node ',ga_cluster_nodeid(), $ ' with ', ga_cluster_nprocs(-1), ' processes' call ffflush(6) endif call ga_pgroup_sync(grp_id) c c*** Check support for double precision arrays c if (me.eq.0) then write(6,*) write(6,*) ' CHECKING DOUBLES ' write(6,*) call ffflush(6) endif call check_dbl(grp_id, g_w) c c*** Check support for double precision complex arrays c if (me.eq.0) then write(6,*) write(6,*) ' CHECKING DOUBLE COMPLEX ' write(6,*) call ffflush(6) endif call check_complex(grp_id) c c*** Check support for integer arrays c if (me.eq.0) then write(6,*) write(6,*) ' CHECKING INTEGERS ' write(6,*) call ffflush(6) endif call check_int(grp_id) c c c*** Check support for single precision c if (me.eq.0) then write(6,*) write(6,*) ' CHECKING SINGLE PRECISION ' write(6,*) call ffflush(6) endif call check_flt(grp_id) c if (me.eq.0) then write(6,*) write(6,*)'CHECKING Wrappers to Message Passing Collective ops' write(6,*) call ffflush(6) endif call check_wrappers(grp_id) c if(me.eq.0) call ga_print_stats() if(me.eq.0) print *,' ' if(me.eq.0) print *,'All tests succesful ' c return end subroutine check_dbl(grp_id, g_w) implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,m,grp_id parameter (n = 128) parameter (m = 2*n) double precision a(n,n), b(n,n), v(m),w(m) integer ndim, dims(2), chunk(2) integer iv(m), jv(m) #ifdef NGA_GATSCAT integer ijv(2,m) #endif logical status integer g_a, g_b, g_w integer iran, i,j, loop,nloop,maxloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, ii, jj parameter (maxloop = 100) integer maxproc parameter (maxproc = 128) double precision crap, sum1, sum2, x double precision nwords logical found integer lprocs,inode,iproc,nnodes intrinsic int iran(i) = int(drand(0)*real(i)) + 1 c nproc = ga_pgroup_nnodes(grp_id) me = ga_pgroup_nodeid(grp_id) lprocs = nproc inode = me nnodes = ga_cluster_nnodes() iproc = me nloop = Min(maxloop,n) c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n a(i,j) = i-1 + (j-1)*n b(i,j) =-1. enddo enddo * write(6,*) ' correct ' * call output(a, 1, n, 1, n, n, n, 1) * call ffflush(6) c c Create a global array c c print *,ga_nodeid(), ' creating array' * call ffflush(6) c call setdbg(1) g_a = ga_create_handle() ndim = 2 dims(1) = n dims(2) = n call ga_set_data(g_a,ndim,dims,MT_DBL) call ga_set_array_name(g_a,'a') write(6,*) 'grp_id ',grp_id call ga_set_pgroup(g_a,grp_id) status = ga_allocate(g_a) if (.not. status) then write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',0) endif c c check if handle is valid. be quiet unless error c if(.not.ga_valid_handle(g_a)) call ga_error("invalid handle",g_a) c call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) call ga_pgroup_sync(grp_id) c c Zero the array c if (me .eq. 0) then write(6,21) 21 format(/'> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_pgroup_sync(grp_id) do i = 1, n do j = 1, n if (b(i,j) .ne. 0.0d0) then write(6,*) me,' zero ', i, j, b(i,j) call ffflush(6) c call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_pgroup_sync(grp_id) c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format(/'> Checking disjoint put ... ') call ffflush(6) endif call ga_pgroup_sync(grp_id) c inc = (n-1)/20 + 1 inc = n ij = 0 do j = 1, n, inc do i = 1, n, inc if (mod(ij,nproc) .eq. me) then ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) * write(6,4) me, ilo, ihi, jlo, jhi * 4 format(' node ',i2,' checking put ',4i4) * call ffflush(6) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_pgroup_sync(grp_id) c c All nodes check all of a c call util_dfill(n*n, 0.0d0, b, 1) * call ga_print(g_a,1) call ga_get(g_a, 1, n, 1, n, b, n) * write(6,*) ' after get' * call output(b, 1, n, 1, n, n, n, 1) c do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif call ga_pgroup_sync(grp_id) c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format(/'> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_pgroup_sync(grp_id) c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_dfill(n*n, 0.0d0, b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif sum1 = 0.0d0 do j = jlo, jhi do i = ilo, ihi sum1 = sum1 + b(i,j) if (b(i,j) .ne. a(i,j)) then write(6,*) i, j, b(i,j), a(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo c enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) call ffflush(6) endif call ga_pgroup_sync(grp_id) c c Each node accumulates into disjoint sections of the array c if (me .eq. 0) then write(6,9) 9 format(/'> Checking accumulate ... ') call ffflush(6) endif call ga_pgroup_sync(grp_id) c crap = drand(12345) ! Same seed for each process do j = 1, n do i = 1, n c b(i,j) = drand(0) b(i,j) = i+j enddo enddo c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc c x = drand(0) x = 10. ilo = i ihi = min(i+inc-1, n) if(ihi.eq.n-1)ihi=n c ihi = min(i+inc, n) jlo = j jhi = min(j+inc-1, n) if(jhi.eq.n-1)jhi=n c jhi = min(j+inc-1, n) * call ffflush(6) if (mod(ij,nproc) .eq. me) then c print *, me, 'checking accumulate ',ilo,ihi,jlo,jhi,x * 11 format(' node ',i2,' checking accumulate ',4i4) * call ffflush(6) call ga_acc(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n, x) endif ij = ij + 1 c c Each process applies all updates to its local copy c do jj = jlo, jhi do ii = ilo, ihi a(ii,jj) = a(ii,jj) + x * b(ii,jj) enddo enddo enddo enddo call ga_pgroup_sync(grp_id) c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if(MISMATCH(b(i,j),a(i,j)))then write(6,*) ' acc ', me, i, j, a(i,j), b(i,j) call ffflush(6) c call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' disjoint ga_acc is OK' write(6,*) endif c c overlapping accumulate call ga_pgroup_sync(grp_id) g_b = ga_create_handle() call ga_set_data(g_b,ndim,dims,MT_DBL) call ga_set_array_name(g_b,'b') call ga_set_pgroup(g_b,grp_id) if (.not.ga_allocate(g_b)) then call ga_error('ga_create failed for second array ',0) endif c call ga_zero(g_b) call ga_pgroup_sync(grp_id) call ga_acc(g_b, n/2, n/2, n/2, n/2, 1d0, 1, 1d0) call ga_pgroup_sync(grp_id) if (me.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, b(1,1), 1) x = abs(b(1,1) -1d0*nproc) if(x.gt. 1d-10)then write(6,*)'val=',b(1,1),' expected=',nproc, x call ga_error('overlapping accumulate failed',0) endif if (me.eq.0) then write(6,*) write(6,*) ' overlapping ga_acc is OK' write(6,*) endif endif c c Check the ga_add function c if (me .eq. 0) then write(6,91) 91 format(/'> Checking add ...') call ffflush(6) endif c c crap = drand(12345) ! Everyone has same seed do j = 1, n do i = 1, n b(i,j) = drand(0) a(i,j) = 0.1d0*a(i,j) + 0.9d0*b(i,j) enddo enddo if (me.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) call ga_add(0.1d0, g_a, 0.9d0, g_b, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if(MISMATCH(b(i,j), a(i,j)))then write(6,*) ' add ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' add is OK ' write(6,*) endif call ga_pgroup_sync(grp_id) c c Check the ddot function c if (me .eq. 0) then write(6,19) 19 format(/'> Checking ddot ...') call ffflush(6) endif crap = drand(12345) ! Everyone has same seed sum1 = 0.0d0 do j = 1, n do i = 1, n b(i,j) = drand(0) sum1 = sum1 + a(i,j)*b(i,j) enddo enddo if (me.eq.0) then call ga_put(g_b, 1, n, 1, n, b, n) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_pgroup_sync(grp_id) sum2 = ga_ddot(g_a,g_b) if(MISMATCH(sum1, sum2))then write(6,*) ' ddot wrong ', sum1, sum2 call ffflush(6) call ga_error('... exiting ',0) else if (me.eq.0) then write(6,*) write(6,*) ' ddot is OK ' write(6,*) endif c c Check the ga_scale function c if (me .eq. 0) then write(6,92) 92 format(/'> Checking scale ...') call ffflush(6) endif call ga_scale(g_a, 0.123d0) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n a(i,j) = a(i,j)*0.123d0 if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' dscal ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' scale is OK ' write(6,*) endif c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif call ga_pgroup_sync(grp_id) c c Check the ga_copy function between two groups c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy between groups' write(6,*) call ffflush(6) endif if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) call ga_copy(g_a, g_w) call ga_get(g_w, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK from small group to big group' write(6,*) endif call ga_pgroup_sync(grp_id) call ga_zero(g_a) call ga_copy(g_w, g_a) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK from big group to small group' write(6,*) endif c c Test copy_patch c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy_patch between groups' write(6,*) call ffflush(6) endif c c klugy mechanism for zeroing g_w c call ga_zero(g_a) call ga_copy(g_a,g_w) if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) c ilo = n/4+1 jlo = n/4+1 ihi = ilo + n/2 jhi = jlo + n/2 call ga_copy_patch('n',g_a,ilo,ihi,jlo,jhi,g_w,ilo,ihi,jlo,jhi) call ga_get(g_w, 1, n, 1, n, b, n) do j = jlo, jhi do i = ilo, ihi if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy_patch is OK from small group to big group' write(6,*) endif c call ga_zero(g_a) call ga_copy_patch('n',g_w,ilo,ihi,jlo,jhi,g_a,ilo,ihi,jlo,jhi) call ga_get(g_a, 1, n, 1, n, b, n) do j = jlo, jhi do i = ilo, ihi if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy_patch is OK from big group to small group' write(6,*) endif if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) c call ga_pgroup_sync(grp_id) if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) endif call ga_pgroup_sync(grp_id) c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_pgroup_sync(grp_id) itmp = iran(nproc)-1 if(me.eq.itmp) then #ifndef NGA_GATSCAT do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo write(6,*) 'Calling ga_gather' call ga_gather(g_a, v, iv, jv, m) write(6,*) 'Completed ga_gather' do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',0) endif enddo #else do loop = 1,m ilo = iran(n) jlo = iran(n) ijv(1,loop) = ilo ijv(2,loop) = jlo enddo call nga_gather(g_a, v, ijv, m) do loop = 1,m ilo= ijv(1,loop) jlo= ijv(2,loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',0) endif enddo #endif endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_pgroup_sync(grp_id) if(me.eq.iran(ga_pgroup_nnodes(grp_id))-1) then #ifndef NGA_GATSCAT do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo c v(loop) = DSIN(a(ilo,jlo)+b(ilo,jlo)) v(loop) = 1d0 *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) c if(v(loop) .ne. w(loop))then if(w(loop) .ne. 1d0 *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', 1d0 *(ilo+jlo) call ffflush(6) call ga_error('... exiting ',0) endif enddo #else do loop = 1,m ilo = iran(n) jlo = iran(n) ijv(1,loop) = ilo ijv(2,loop) = jlo c v(loop) = DSIN(a(ilo,jlo)+b(ilo,jlo)) v(loop) = 1d0 *(ilo+jlo) enddo call nga_scatter(g_a, v, ijv, m) do loop = 1,m ilo= ijv(1,loop) jlo= ijv(2,loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) c if(v(loop) .ne. w(loop))then if(w(loop) .ne. 1d0 *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', 1d0 *(ilo+jlo) call ffflush(6) call ga_error('... exiting ',0) endif enddo #endif endif call ga_pgroup_sync(grp_id) enddo c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c call ga_pgroup_sync(grp_id) c c scatter-acc available in GA ver. 3.0 #ifdef GA3 if (me.eq.0) then write(6,*) write(6,*) ' checking scatter-accumulate' write(6,*) endif c crap = drand(1234) call ga_zero(g_a) c do j = 1, n do i = 1, n b(i,j) =0. enddo enddo c x = .1d0 ii =n do jj = 1,1 call ga_pgroup_sync(grp_id) do loop = 1, ii c generate unique i,j pairs 10 continue i = iran(n) j=iran(n) if (found(i,j, iv, jv, loop-1) ) goto 10 #ifndef NGA_GATSCAT iv(loop) = i jv(loop) = j #else ijv(1,loop) = i ijv(2,loop) = j #endif v(loop) = 1d0 *(i+j) b(i,j) = b(i,j) + nproc*x*v(loop) ! update local ref. array enddo #ifndef NGA_GATSCAT call ga_scatter_acc(g_a,v,iv,jv, ii,x) #else call nga_scatter_acc(g_a,v,ijv,ii,x) #endif c call ga_pgroup_sync(grp_id) c c check the result c call ga_get(g_a, 1, n, 1,n, a, n) do loop = 1,ii #ifndef NGA_GATSCAT i = iv(loop) j = jv(loop) #else i = ijv(1,loop) j = ijv(2,loop) #endif if(MISMATCH(a(i,j),b(i,j)))then print *,'Error',i,j,loop,a(i,j),'expected=',b(i,j) * if(me.eq.0)then * do ii=1,loop * print *,'element',ii, iv(ii),jv(ii) * enddo * endif call ga_error('scatter-acc error in trial ',jj) endif enddo call ga_pgroup_sync(grp_id) enddo call ga_pgroup_sync(grp_id) if (me.eq.0) then write(6,*) write(6,*) ' scatter-accumulate is OK' write(6,*) endif #endif c c Delete the global arrays c status = ga_destroy(g_b) status = ga_destroy(g_a) c return end c----------------------------------------------------------------- subroutine check_complex(grp_id) implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,m,grp_id parameter (n = 60) parameter (m = 2*n) double complex a(n,n), b(n,n), v(m),w(m) integer ndim, dims(2), chunk(2), p_mirror integer iv(m), jv(m) logical status integer g_a, g_b integer iran, i,j, loop,nloop,maxloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, ii, jj, nnodes parameter (maxloop = 100) integer maxproc parameter (maxproc = 128) double precision crap, real double precision nwords double complex x, sum1, sum2, factor integer lprocs, inode, iproc intrinsic int iran(i) = int(drand(0)*real(i)) + 1 c nproc = ga_pgroup_nnodes(grp_id) me = ga_pgroup_nodeid(grp_id) inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) nloop = Min(maxloop,n) c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n a(i,j) = cmplx(dble(i-1), dble((j-1)*n)) b(i,j) = cmplx(-1d0,1d0) enddo enddo c c Create a global array c c print *,ga_nodeid(), ' creating array' call ffflush(6) c call setdbg(1) ndim = 2 dims(1) = n dims(2) = n g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_DCPL) call ga_set_array_name(g_a,'a') call ga_set_pgroup(g_a,grp_id) status = ga_allocate(g_a) if (.not. status) then write(6,*) ' ga_create failed' call ga_error('... exiting ',0) endif g_b = ga_create_handle() call ga_set_data(g_b,ndim,dims,MT_DCPL) call ga_set_array_name(g_b,'b') call ga_set_pgroup(g_b,grp_id) if (.not.ga_allocate(g_b)) then call ga_error('ga_create failed for second array ',0) endif call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) call ga_pgroup_sync(grp_id) c c Zero the array c if (me .eq. 0) then write(6,21) 21 format(/'> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_pgroup_sync(grp_id) do i = 1, n do j = 1, n if(b(i,j).ne.(0d0,0d0)) then write(6,*) me,' zero ', i, j, b(i,j) call ffflush(6) c call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_pgroup_sync(grp_id) c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format(/'> Checking disjoint put ... ') call ffflush(6) endif call ga_pgroup_sync(grp_id) c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc if (mod(ij,nproc) .eq. me) then ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_pgroup_sync(grp_id) c c All nodes check all of a c call util_qfill(n*n, (0d0,0d0), b, 1) call ga_get(g_a, 1, n, 1, n, b, n) c do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif call ga_pgroup_sync(grp_id) c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format(/'> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_pgroup_sync(grp_id) c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_qfill(n*n, (0.0d0,0d0), b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif do j = jlo, jhi do i = ilo, ihi if (b(i,j) .ne. a(i,j)) then write(6,*)'error:', i, j, b(i,j), a(i,j) call ga_error('... exiting ',0) endif enddo enddo c enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) call ffflush(6) endif call ga_pgroup_sync(grp_id) c c Each node accumulates into disjoint sections of the array c if (me .eq. 0) then write(6,9) 9 format(/'> Checking accumulate ... ') call ffflush(6) endif call ga_pgroup_sync(grp_id) c crap = drand(12345) ! Same seed for each process do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0),drand(1)) b(i,j) = cmplx(dble(i),dble(j)) enddo enddo c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc c x = cmplx(drand(0),0.333d0) c x = cmplx(0.333d0,0) * x = cmplx(0d0,0d0) x = 0 ilo = i ihi = min(i+inc-1, n) if(ihi.eq.n-1)ihi=n jlo = j jhi = min(j+inc-1, n) if(jhi.eq.n-1)jhi=n if (mod(ij,nproc) .eq. me) then call ga_acc(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n, x) endif ij = ij + 1 c c Each process applies all updates to its local copy c do jj = jlo, jhi do ii = ilo, ihi a(ii,jj) = a(ii,jj) + x * b(ii,jj) enddo enddo enddo enddo call ga_pgroup_sync(grp_id) c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' acc ', me, i, j, a(i,j), b(i,j) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' disjoint ga_acc is OK' write(6,*) endif c c overlapping accumulate c call ga_zero(g_b) call ga_acc(g_b, n/2, n/2, n/2, n/2, (1d0,-1d0), 1, (1d0,0d0)) call ga_pgroup_sync(grp_id) if (me.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, x, 1) if (MISMATCH(x, ((1d0,-1d0)*nproc)))then c if(error.gt. (1d-8))then write(6,*)'val=',x,' expected=(',nproc,',',-nproc,')' call ga_error('overlapping accumulate failed',0) endif write(6,*) write(6,*) ' overlapping ga_acc is OK' write(6,*) endif c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif call ga_pgroup_sync(grp_id) if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c c c Check the ga_scale function c if (me .eq. 0) then write(6,92) 92 format(/'> Checking scale ...') call ffflush(6) endif factor = (1d0,-1d0) call ga_scale(g_a, factor) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n a(i,j) = a(i,j)*factor if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' dscal ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' scale is OK ' write(6,*) endif c c Check scatter&gather c call ga_pgroup_sync(grp_id) if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_pgroup_sync(grp_id) c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_pgroup_sync(grp_id) itmp = iran(nproc)-1 if(me.eq.itmp) then do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo call ga_gather(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',0) endif enddo endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_pgroup_sync(grp_id) if(me.eq.iran(ga_nnodes())-1) then do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo v(loop) = (1d0,-1d0) *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) if(w(loop) .ne. (1d0,-1d0) *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', (1d0,-1d0) *(ilo+jlo) call ffflush(6) endif enddo endif call ga_pgroup_sync(grp_id) enddo c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c c Check ga_add c if (me .eq. 0) then write(6,91) 91 format(/'> Checking add ...') call ffflush(6) endif call ga_get(g_a, 1, n, 1, n, a, n) crap = drand(12345) ! Everyone has same seed do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0), drand(1)) a(i,j) = (0.1d0,-.1d0)*a(i,j) + (.9d0,-.9d0)*b(i,j) enddo enddo if (me.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) call ga_add((0.1d0,-.1d0), g_a, (0.9d0,-.9d0), g_b, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' add ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' add is OK ' write(6,*) endif call ga_pgroup_sync(grp_id) c c Check the zdot function c if (me .eq. 0) then write(6,19) 19 format(/'> Checking zdot ...') call ffflush(6) endif crap = drand(12345) ! Everyone has same seed sum1 = (0.0d0,0.d0) do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0), drand(1)) sum1 = sum1 + a(i,j)*b(i,j) enddo enddo if (me.eq.0) then call ga_put(g_b, 1, n, 1, n, b, n) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_pgroup_sync(grp_id) sum2 = ga_zdot(g_a,g_b) if (MISMATCH(sum1, sum2))then write(6,*) ' zdot wrong ', sum1, sum2 call ffflush(6) call ga_error('... exiting ',0) else if (me.eq.0) then write(6,*) write(6,*) ' zdot is OK ' write(6,*) endif c c Delete the global arrays c 123 continue status = ga_destroy(g_b) status = ga_destroy(g_a) c return end c----------------------------------------------------------------- subroutine check_int(grp_id) implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,grp_id parameter (n = 128) integer a(n,n), b(n,n) logical status integer g_a integer iran, i, j, loop, nloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, dimi, dimj, ii, jj integer ndim, dims(2), chunk(2), p_mirror, nnodes integer lprocs, inode, iproc,lproc, nprocs double precision nwords parameter (nloop = 100) integer maxproc parameter (maxproc = 128) integer map(5,maxproc), found, np,k double precision crap, sum1, real integer buf, abs_me intrinsic int iran(i) = int(drand(0)*real(i)) + 1 c nproc = ga_pgroup_nnodes(grp_id) me = ga_pgroup_nodeid(grp_id) abs_me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n a(i,j) = i-1 + (j-1)*1000 enddo enddo c c Create a global array c ndim = 2 dims(1) = n dims(2) = n chunk(1) = 0 chunk(2) = 0 g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_INT) call ga_set_array_name(g_a,'a') call ga_set_pgroup(g_a,grp_id) if (.not.ga_allocate(g_a)) then write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',0) endif c c Zero the array c if (me .eq. 0) then write(6,21) 21 format(/'> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) do i = 1, n do j = 1, n if (b(i,j) .ne. 0) then write(6,*) ' zero ', me, i, j, b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_pgroup_sync(grp_id) c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format(/'> Checking disjoint put ... ') call ffflush(6) endif call ga_pgroup_sync(grp_id) c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc if (mod(ij,nproc) .eq. me) then ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) c write(6,4) me, ilo, ihi, jlo, jhi c4 format(' node ',i2,' checking put ',4i4) c call ffflush(6) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_pgroup_sync(grp_id) c c All nodes check all of a c if(me.eq.0)then call ga_get(g_a, 1, n, 1, n, b, n) do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo endif call ga_pgroup_sync(grp_id) c if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format(/'> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_pgroup_sync(grp_id) c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_ifill(n*n, 0.0d0, b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) c if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif c sum1 = 0.0d0 do j = jlo, jhi do i = ilo, ihi sum1 = sum1 + b(i,j) if (b(i,j) .ne. a(i,j)) then write(6,*) 'error ', i, j, b(i,j), a(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) endif c call ga_pgroup_sync(grp_id) if (me .eq. 0 .and. n.gt.7) then write(6,*) write(6,*) '> Checking ga_print_patch --- should see ' write(6,*)' [2002 3002 4002 5002 6002]' write(6,*)' [2003 3003 4003 5003 6003]' write(6,*)' [2004 3004 4004 5004 6004]' write(6,*) call ffflush(6) endif if(n.gt.5) call ga_print_patch(g_a,3,5,3,7,0) c call ga_pgroup_sync(grp_id) c call ga_pgroup_sync(grp_id) if (me .eq. 0) then write(6,*) write(6,*) '> Checking read_inc ... ' write(6,*) call ffflush(6) endif call ga_pgroup_sync(grp_id) c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc inc =5 c every processor will be operating on somebody elses data c lproc = nproc - me - 1 c call ga_distribution(g_a,lproc,ilo,ihi,jlo,jhi) c dimi = ihi-ilo dimj = jhi-jlo write(6,111) me,ilo,ihi,jlo,jhi,dimi,dimj 111 format(i2,'..',4i8,'.',2i8) call ga_pgroup_sync(grp_id) if(ilo .gt.0 .and. jhi .gt. 0)then do loop = 1,nloop ii= IABS(iran(dimi)) jj= IABS(iran(dimj)) i=ilo + Mod(ii,dimi) j=jlo + Mod(jj,dimj) c c write(6,*) me,'..',ilo,ihi,jlo,jhi,'.',dimi,dimj,'..',i,j c call ffflush(6) buf = ga_read_inc(g_a,i,j,inc) if(a(i,j).ne. buf)then write(6,*)me,'READ_inc ', i,',',j,',', a(i,j),' ',buf,me call ffflush(6) endif call ga_get(g_a, i,i,j,j, buf,1) a(i,j) = a(i,j)+inc if(a(i,j).ne. buf)then write(6,*)me,'read_INC ', i,',',j,',', a(i,j),' ',buf,me call ffflush(6) endif enddo endif call ga_pgroup_sync(grp_id) c if (me.eq.0) then write(6,*) write(6,*) ' read_inc is OK' write(6,*) endif c c c #if 000 if (me.eq.0) then write(6,*) write(6,*) '> checking ga_fence and ga_lock' write(6,*) call ffflush(6) endif c call ga_zero(g_a) c c*** use ga_read_inc and elements g_a(1:2,1) to implement a lock c*** compute g_a(:,n) = sum (1(:)) for P processors c status = ga_create_mutexes(1) if (.not. status) then call ga_error('ga_create_mutexes failed ',0) endif if(n.lt.2)call ga_error('insufficient n to test ga_fence',n) call ga_lock(0) c call my_lock(g_a) c get original values g_a(:,n) call ga_get(g_a, 1,n, n,n, b,n) c add my contribution do i =1,n b(i,1)= b(i,1)+1 enddo c c need to use fence to assure that coms complete before leaving c Critical Section c call ga_init_fence() call ga_put(g_a, 1,n, n,n, b,n) call ga_fence() call ga_unlock(0) c call my_unlock(g_a) c 333 if(.not.ga_destroy_mutexes()) $ call ga_error('mutex not destroyed',0) call ga_pgroup_sync(grp_id) if (me.eq.0) then call ga_get(g_a, 1,n, n,n, b,n) do i =1,n if(b(i,1).ne. nproc)then print *, 'mismatch',b(i,1),nproc call ga_error('fence failed',i) endif enddo write(6,*) write(6,*) ' ga_fence and ga_lock are OK' write(6,*) endif #endif c c if (me.eq.0) then write(6,*) write(6,*) '> checking ga_locate_region' write(6,*) call ffflush(6) endif status = ga_locate_region(g_a, 1, n, 1,n, map,np) found = 0 do j=1,n do i=1,n b(i,j)=-1 enddo enddo if(me.eq.0)call ga_put(g_a,1,n,1,n,b,n) call ga_pgroup_sync(grp_id) do k = 1, np if(map(5,k).eq.me)then if(found.eq.1) then write(6,*)'double entry in map for proc ',me call ffflush(6) endif do j= map(3,k), map(4,k) do i= map(1,k), map(2,k) b(i,j)=1*me enddo enddo call ga_put(g_a, map(1,k),map(2,k),map(3,k),map(4,k), & b(map(1,k),map(3,k)),n) found = 1 endif enddo call ga_pgroup_sync(grp_id) c do k = 1, np if(map(5,k).eq.me)then call ga_get(g_a, map(1,k),map(2,k),map(3,k),map(4,k), & a(map(1,k),map(3,k)),n) do j= map(3,k), map(4,k) do i= map(1,k), map(2,k) if(b(i,j).ne.a(i,j)) then write(6,*) & 'proc ',me, 'overlap with ',a(i,j) call ffflush(6) endif enddo enddo endif enddo call ga_pgroup_sync(grp_id) c if(me.eq.0)then call ga_get(g_a,1,n,1,n,a,n) do j=1,n do i=1,n if(a(i,j).eq.-1)then write(6,*)'i=',i,' j=',j, ' not assigned ' call ga_error('... exiting ',0) endif enddo enddo endif if (me.eq.0) then write(6,*) write(6,*) ' ga_locate_region is OK' write(6,*) call ffflush(6) endif c c Delete the global array c status = ga_destroy(g_a) c return end c--------------------------------------------------------------------- subroutine check_flt(grp_id) implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer n, m, grp_id parameter (n =10) parameter (m=2*n) real a(n,n), b(n,n), v(m), w(m) integer iv(m), jv(m) logical status integer g_a, g_b integer iran, i, j, loop, nloop, maxloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, ii, jj, nnodes double precision nwords parameter (maxloop = 100) integer maxproc integer ndim, dims(2), chunk(2), p_mirror parameter (maxproc = 128) double precision crap real x, sum1, sum2 logical found integer lprocs, inode, iproc intrinsic int iran(i) = int(drand(0)*real(i)) + 1 nproc = ga_pgroup_nnodes(grp_id) me = ga_pgroup_nodeid(grp_id) inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) nloop = Min(maxloop,n) c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n a(i,j) = i-1 + (j-1)*n b(i,j) = -1. enddo enddo c c Create a global array c ndim = 2 dims(1) = n dims(2) = n chunk(1) = 0 chunk(2) = 0 g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_REAL) call ga_set_array_name(g_a,'a') call ga_set_pgroup(g_a,grp_id) if (.not.ga_allocate(g_a)) then write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',0) endif c c check if handle is valid. be quiet unless error c if(.not.ga_valid_handle(g_a)) call ga_error("invalid handle",g_a) c call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) call ga_pgroup_sync(grp_id) c c Zero the array c if (me .eq. 0) then write(6,21) 21 format(/'> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_pgroup_sync(grp_id) do i = 1, n do j = 1, n if (b(i,j) .ne. 0.0) then write(6,*) ' zero ', me, i, j, b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_pgroup_sync(grp_id) c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format(/'> Checking disjoint put ... ') call ffflush(6) endif call ga_pgroup_sync(grp_id) c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc if (mod(ij,nproc) .eq. me) then ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) c write(6,4) me, ilo, ihi, jlo, jhi c 4 format(' node ',i2,' checking put ',4i4) c call ffflush(6) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_pgroup_sync(grp_id) c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo call ga_pgroup_sync(grp_id) c if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format(/'> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_pgroup_sync(grp_id) c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_rfill(n*n, 0.0, b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) c if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif c sum1 = 0.0d0 do j = jlo, jhi do i = ilo, ihi sum1 = sum1 + b(i,j) if (b(i,j) .ne. a(i,j)) then write(6,*) 'error ', i, j, b(i,j), a(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) endif call ga_pgroup_sync(grp_id) c c Each node accumulates into disjoint sections of the array c if (me .eq. 0) then write(6,9) 9 format(/'> Checking accumulate ... ') call ffflush(6) endif call ga_pgroup_sync(grp_id) c crap = drand(12345) ! Same seed for each process do j = 1, n do i = 1, n c b(i,j) = real(drand(0)) b(i,j) = i+j enddo enddo c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc c x = real(drand(0)) x = 10. ilo = i ihi = min(i+inc-1, n) if(ihi.eq.n-1)ihi=n c ihi = min(i+inc, n) jlo = j jhi = min(j+inc-1, n) if(jhi.eq.n-1)jhi=n c jhi = min(j+inc-1, n) * call ffflush(6) if (mod(ij,nproc) .eq. me) then c print *, me, 'checking accumulate ',ilo,ihi,jlo,jhi,x * 11 format(' node ',i2,' checking accumulate ',4i4) * call ffflush(6) call ga_acc(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n, x) endif ij = ij + 1 c c Each process applies all updates to its local copy c do jj = jlo, jhi do ii = ilo, ihi a(ii,jj) = a(ii,jj) + x * b(ii,jj) enddo enddo enddo enddo call ga_pgroup_sync(grp_id) c c All nodes check all of a call ga_get(g_a, 1, n, 1, n, b, n) c do j = 1, n do i = 1, n if(MISMATCHF(b(i,j),a(i,j)))then write(6,*) ' acc ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' disjoint ga_acc is OK' write(6,*) endif c c overlapping accumulate call ga_pgroup_sync(grp_id) g_b = ga_create_handle() call ga_set_data(g_b,ndim,dims,MT_REAL) call ga_set_array_name(g_b,'b') call ga_set_pgroup(g_b,grp_id) if (.not.ga_allocate(g_b)) then call ga_error('ga_create failed for second array ',0) endif c call ga_zero(g_b) call ga_acc(g_b, n/2, n/2, n/2, n/2, 1.0, 1, 1.0) call ga_pgroup_sync(grp_id) if (me.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, b(1,1), 1) x = abs(b(1,1) -1*nproc) if(x.gt. 1e-10)then write(6,*)'val=',b(1,1),' expected=',nproc, x call ga_error('overlapping accumulate failed',0) endif write(6,*) write(6,*) ' overlapping ga_acc is OK' write(6,*) endif c c Check the ga_add function c if (me .eq. 0) then write(6,91) 91 format(/'> Checking add ...') call ffflush(6) endif c crap = drand(12345) ! Everyone has same seed do j = 1, n do i = 1, n b(i,j) = real(drand(0)*real(i)) + 1 a(i,j) = 0.1*a(i,j) + 0.9*b(i,j) enddo enddo if (me.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) call ga_add(0.1, g_a, 0.9, g_b, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if(MISMATCHF(b(i,j), a(i,j)))then write(6,*) ' add ', me, i, j, a(i,j), b(i,j) call ffflush(6) CCC call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' add is OK ' write(6,*) endif call ga_pgroup_sync(grp_id) c c Check the sdot function c if (me .eq. 0) then write(6,19) 19 format(/'> Checking sdot ...') call ffflush(6) endif crap = drand(12345) ! Everyone has same seed sum1 = 0.0 do j = 1, n do i = 1, n b(i,j) = drand(0) sum1 = sum1 + a(i,j)*b(i,j) enddo enddo if (me.eq.0) then call ga_put(g_b, 1, n, 1, n, b, n) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_pgroup_sync(grp_id) sum2 = ga_sdot(g_a,g_b) if(MISMATCHF(sum1, sum2))then write(6,*) ' fdot wrong ', sum1, sum2 call ffflush(6) call ga_error('... exiting ',0) else if (me.eq.0) then write(6,*) write(6,*) ' sdot is OK ' write(6,*) endif c c Check the ga_scale function c if (me .eq. 0) then write(6,92) 92 format(/'> Checking scale ...') call ffflush(6) endif call ga_scale(g_a, 0.123) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n a(i,j) = a(i,j)*0.123 if (MISMATCHF(b(i,j), a(i,j)))then write(6,*) ' dscal ', me, i, j, a(i,j), b(i,j) call ffflush(6) CCC call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' scale is OK ' write(6,*) endif c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c call ga_pgroup_sync(grp_id) if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) endif call ga_pgroup_sync(grp_id) c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_pgroup_sync(grp_id) itmp = iran(nproc)-1 if(me.eq.itmp) then do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo call ga_gather(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',0) endif enddo endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_pgroup_sync(grp_id) if(me.eq.iran(ga_nnodes())-1) then do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo c v(loop) = DSIN(a(ilo,jlo)+b(ilo,jlo)) v(loop) = 1.0 *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) c if(v(loop) .ne. w(loop))then if(w(loop) .ne. 1.0 *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', 1.0 *(ilo+jlo) call ffflush(6) call ga_error('... exiting ',0) endif enddo endif call ga_pgroup_sync(grp_id) enddo c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c call ga_pgroup_sync(grp_id) c c scatter-acc available in GA ver. 3.0 #ifdef GA3 if (me.eq.0) then write(6,*) write(6,*) ' checking scatter-accumulate' write(6,*) endif c crap = drand(1234) call ga_zero(g_a) c do j = 1, n do i = 1, n b(i,j) =0. enddo enddo c x = .1d0 ii =n do jj = 1,1 call ga_pgroup_sync(grp_id) do loop = 1, ii c generate unique i,j pairs 10 continue i = iran(n) j=iran(n) if (found(i,j, iv, jv, loop-1) ) goto 10 iv(loop) = i jv(loop) = j v(loop) = 1.0 *(i+j) b(i,j) = b(i,j) + nproc*x*v(loop) ! update local ref. array enddo call ga_scatter_acc(g_a,v,iv,jv, ii,x) c call ga_pgroup_sync(grp_id) c c check the result c call ga_get(g_a, 1, n, 1,n, a, n) do loop = 1,ii i = iv(loop) j = jv(loop) if(MISMATCHF(a(i,j),b(i,j)))then print *,'Error',i,j,loop,a(i,j),'expected=',b(i,j) * if(me.eq.0)then * do ii=1,loop * print *,'element',ii, iv(ii),jv(ii) * enddo * endif call ga_error('scatter-acc error in trial ',jj) endif enddo call ga_pgroup_sync(grp_id) enddo call ga_pgroup_sync(grp_id) if (me.eq.0) then write(6,*) write(6,*) ' scatter-accumulate is OK' write(6,*) endif #endif c c Delete the global array c status = ga_destroy(g_a) c return end c_____________________________________________________________ subroutine check_wrappers(grp_id) implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" double precision sum integer isum, ibuf(2) integer me, nproc, grp_id, root real fsum c nproc = ga_pgroup_nnodes(grp_id) me = ga_pgroup_nodeid(grp_id) c call ga_pgroup_sync(grp_id) if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_igop' write(6,*) call ffflush(6) endif ibuf(1) = 1 ibuf(2) = me call ga_pgroup_igop(grp_id,10000, ibuf, 2, '+') if(ibuf(1).ne.nproc)then call ga_error('ga_igop error',isum) endif if(ibuf(2).ne.((nproc-1)*nproc/2))then call ga_error('ga_igop error -2',isum) endif call ga_pgroup_sync(grp_id) if (me.eq.0) then write(6,*) write(6,*) ' ga_igop is OK' write(6,*) endif call ga_pgroup_sync(grp_id) c call ga_pgroup_sync(grp_id) if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_dgop' write(6,*) call ffflush(6) endif sum = 1d0 * me call ga_pgroup_dgop(grp_id,10000, sum, 1, '+') if(Int(sum).ne.((nproc-1)*nproc/2))then call ga_error('ga_dgop error',Int(sum)) endif call ga_pgroup_sync(grp_id) if (me.eq.0) then write(6,*) write(6,*) ' ga_dgop is OK' write(6,*) endif c call ga_pgroup_sync(grp_id) if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_sgop' write(6,*) call ffflush(6) endif fsum = 1.0 * me call ga_pgroup_sgop(grp_id,10000, fsum, 1, '+') if(Int(sum).ne.((nproc-1)*nproc/2))then call ga_error('ga_fgop error',Int(sum)) endif call ga_pgroup_sync(grp_id) if (me.eq.0) then write(6,*) write(6,*) ' ga_sgop is OK' write(6,*) endif c call ga_pgroup_sync(grp_id) if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_brdcst' write(6,*) call ffflush(6) endif if(me.eq.nproc-1)then ibuf(1) = me ibuf(2) = nproc endif root = 7 call ga_pgroup_brdcst(grp_id,1000,ibuf,util_mitob(2),nproc-1) if(ibuf(1).ne.nproc-1)call ga_error('ibuf(1) error',ibuf(1)) if(ibuf(2).ne.nproc)call ga_error('ibuf(2) error',ibuf(2)) call ga_pgroup_sync(grp_id) if (me .eq. 0) then write(6,*) write(6,*)'> ga_brdcst is OK ' write(6,*) call ffflush(6) endif call ga_pgroup_sync(grp_id) return end subroutine check_mem(mem_size) implicit none integer mem_size #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,nmax,left,need, me,procs,g_a, g_b logical status c if(.not. ga_memory_limited())return me = ga_nodeid() procs = ga_nnodes() nmax = int(dsqrt(dble(mem_size/util_mitob(1)))) left = mem_size/procs - ga_inquire_memory() n = nmax/2 need = util_mdtob(n*n)/procs c if(me.eq.0)then write(6,*)' ' if(ga_uses_ma())then write(6,*)' CHECKING GA MEMORY RESTRICTIONS (MA used)' else write(6,*)' CHECKING GA MEMORY RESTRICTIONS (MA not used)' endif write(6,*)' ' write(6,*)' ' call print_mem_info(n,left, need, mem_size/procs) endif c status = ga_create(MT_DBL, n, n, 'a', 0, 0, g_a) c if(me.eq.0) then if(status) then write(6,*) ' success' else write(6,*) ' failure' endif call ffflush(6) endif c n = nmax left = mem_size/procs - ga_inquire_memory() need = util_mdtob(n*n)/procs if(me.eq.0)then call print_mem_info(n,left, need, mem_size/procs) endif c status = ga_create(MT_DBL, n, n, 'b', 0, 0, g_b) c if(me.eq.0) then if(status) then write(6,*) ' success' else write(6,*) ' failure' endif write(6,*)' ' write(6,*)' ' call ffflush(6) endif status = ga_destroy(g_a) end subroutine print_mem_info(n,left, need, total) implicit none integer n,left, need, total c write(6,*)' ' if(left - need .ge. 0) then write(6,1)n,n 1 format('> Creating array ',i4,' by ',i4,' -- should succeed') else write(6,2)n,n 2 format('> Creating array ',i4,' by ',i4,' -- SHOULD FAIL') endif write(6,3) need, left, total 3 format(' (need ',i7,' and ',i7,' out of ',i7,' bytes are left)') write(6,*)' ' call ffflush(6) c end subroutine my_lock(g_b) implicit none #include "global.fh" integer g_b, val, flag, i logical first_time double precision dummy common /lock/ val common /dum/ dummy data first_time /.true./ c c this awkward initialization is to avoid a weird problem C with block data on SUN if(first_time)then first_time = .false. dummy = .0 endif c val = ga_read_inc(g_b,1,1, 1) 10 call ga_get(g_b, 2,2,1,1, flag, 1) if(flag.eq.val) return c c to reduce memory stress, wait a while before retrying do i = 1, 100 dummy = dummy + .1 enddo goto 10 end subroutine my_unlock(g_b) implicit none #include "global.fh" integer g_b, val common /lock/ val c call ga_put(g_b, 2,2,1,1, val+1, 1) end logical function found(i,j, iv, jv, n) integer n integer i,j, iv(n), jv(n) integer loop found = .false. do loop = 1, n if(i .eq. iv(loop) .and. j .eq.jv(loop))then found = .true. goto 99 endif enddo 99 continue return end subroutine proc_remap() implicit none #include "global.fh" integer proc(100),nproc,i nproc = ga_nnodes() if(nproc.gt.100) $ call ga_error("remap requires<=100 processes",nproc) do i = 1, nproc proc(i) = nproc-i enddo c call ga_register_proclist(proc,nproc) end subroutine util_rfill(n,val,a,ia) implicit none real a(*), val integer n, ia, i c c initialise real array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end subroutine util_dfill(n,val,a,ia) implicit none double precision a(*), val integer n, ia, i c c initialise double precision array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end subroutine util_ifill(n,val,a,ia) implicit none integer n, ia, i, a(*),val c c initialise integer array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end subroutine util_qfill(n,val,a,ia) implicit none double complex a(*), val integer n, ia, i c c initialise double complex array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end ga-5.9.2/global/testing/pg2testmatmult.F000066400000000000000000000266161500715745200201570ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif #define xgemm TEST_DGEMM #define ygemm TEST_ZGEMM program ga_test c $Id: pg2testmatmult.F,v 1.3.6.1 2007-04-25 21:49:59 manoj Exp $ c c test ga_dgemm c compile with make FLD_REN="optimized blas" testmatmult.x c Note: - change nummax for large arrays c - turn off "verify" for large arrays due to memory c limitations, as verify=TRUE for large arrays produces c segfault, dumps core,or any crap. c c implicit none integer num_m,num_n,num_k,nummax,howmany,ntrans parameter (nummax=1024,howmany=2,ntrans=4) integer num1 parameter(num1=nummax) integer i,ii real*8 h0(num1*num1) integer g_c,g_b,g_a real*8 a real*8 t1,mf,avg_t(ntrans),avg_mf(ntrans) integer itime,ntimes,me integer nums_m(howmany),nums_n(howmany),nums_k(howmany) character*1 transa(ntrans),transb(ntrans),ta,tb real *8 tmpa(nummax,nummax), tmpb(nummax,nummax) real *8 tmpc(nummax,nummax) logical verify, status integer nproc, nprocs, proc_group(0:100), proclist(100) integer grp_id, grp_me, grp_nproc, ndim, dims(2) data transa/'n','t','n','t'/ data transb/'n','n','t','t'/ data nums_m/512,1024/ data nums_n/512,1024/ data nums_k/512,1024/ #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" #include "mp3.fh" if (.not.ma_init(MT_DBL,1,20000000)) then call ga_error('failed: ma_init(MT_DBL,1,20000000)',10) endif call ga_initialize() me = ga_nodeid() nproc = ga_nnodes() if(nproc .ne. 8) then write(*,*) 'nproc =', nproc call ga_error('failed: need exactly 8 processors',10) endif call ga_sync() proclist(1) = 0 proclist(2) = 1 proclist(3) = 2 proclist(4) = 3 nprocs = 4 proc_group(0) = ga_pgroup_create(proclist, nprocs) if (me.lt.4) then c get new proc ids for your default group grp_id = proc_group(0) grp_me = ga_pgroup_nodeid(grp_id) grp_nproc = ga_pgroup_nnodes(grp_id) write(*,*) 'GRP INFO =',me,grp_me,grp_nproc ii = 0 do i = 1, num1*num1 ii = ii + 1 if(ii.gt.num1) then ii = 0 endif h0(i) = ii enddo c c Compute times assuming 500 mflops and 5 second target time c c ntimes = max(3.0d0,5.0d0/(4.0d-9*num**3)) ntimes = 5 verify = .TRUE. ! to verify ga_dgemm c verify = .FALSE. ! to verify ga_dgemm do ii=1,howmany num_m = nums_m(ii) num_n = nums_n(ii) num_k = nums_k(ii) a = 0.5d0/(num_m*num_n) if(num_m.gt.nummax .OR. num_n.gt.nummax .OR. & num_k.gt.nummax) then call ga_error('Insufficient memory: check nummax', 0) endif c c ndim = 2 c Creating C array g_c = ga_create_handle() dims(1) = num_m dims(2) = num_n call ga_set_data(g_c,ndim,dims,MT_DBL) call ga_set_array_name(g_c,'c') call ga_set_pgroup(g_c,grp_id) status = ga_allocate(g_c) if (.not. status) then write(6,*) ' g_c ga_create failed' call ffflush(6) call ga_error('... exiting ',0) endif c Creating B array g_b = ga_create_handle() dims(1) = num_k dims(2) = num_n call ga_set_data(g_b,ndim,dims,MT_DBL) call ga_set_array_name(g_b,'b') call ga_set_pgroup(g_b,grp_id) status = ga_allocate(g_b) if (.not. status) then write(6,*) ' g_b ga_create failed' call ffflush(6) call ga_error('... exiting ',0) endif c Creating A array g_a = ga_create_handle() dims(1) = num_m dims(2) = num_k call ga_set_data(g_a,ndim,dims,MT_DBL) call ga_set_array_name(g_a,'a') call ga_set_pgroup(g_a,grp_id) status = ga_allocate(g_a) if (.not. status) then write(6,*) ' g_a ga_create failed' call ffflush(6) call ga_error('... exiting ',0) endif c c Initialize matrices A and B c call load_ga_from_square(g_a,num_m,h0,num1) c call load_ga_from_square(g_b,num_m,h0,num1) c if(grp_me.eq.0) then call load_ga(g_a, h0, num_m, num_k) call load_ga(g_b, h0, num_k, num_n) endif call ga_zero(g_c) call ga_pgroup_sync(grp_id) if (ga_pgroup_nodeid(grp_id).eq.0) then write(*,*) ! for new line write(*,*) 'Matrix Multiplication C = A[', num_m, ',', . num_k, '] x B[', num_k, ',', num_n, ']' write(*,*) ! for new line call ffflush(6) endif do i = 1, ntrans avg_t(i) = 0.0d0 avg_mf(i) = 0.0d0 enddo do itime = 1, ntimes do i = 1, ntrans call ga_pgroup_sync(grp_id) ta = transa(i) tb = transb(i) t1 = util_timer() call ga_dgemm(ta,tb,num_m,num_n,num_k, & 1.0d0, g_a, g_b, 0.0d0, g_c) t1 = util_timer() - t1 if (grp_me.eq.0) then mf = 2d0*num_m*num_n*num_k/t1*1d-6/grp_nproc avg_t(i) = avg_t(i)+t1 avg_mf(i) = avg_mf(i) + mf write(6,200) ' Run#', itime, t1, mf, ta, tb call ffflush(6) if (verify .AND. itime.eq.1) then call verify_ga_dgemm(ta, tb,num_m, num_n,num_k, & 1.0d0, g_a,g_b,0.0d0,g_c,tmpa,tmpb,tmpc) endif endif enddo enddo if (ga_pgroup_nodeid(grp_id).eq.0) then write(*,*) ! for new line do i = 1, ntrans write(6,200) 'Average:',0,avg_t(i)/ntimes, . avg_mf(i)/ntimes,transa(i),transb(i) enddo if(verify) write(*,*) 'All ga_dgemms are verified...O.K.' write(*,*) ! for new line call ffflush(6) endif c c call ga_print(g_a) c call ga_print(g_b) c call ga_print(g_c) c if (.not.ga_destroy(g_c)) then call ga_error('failed: destroy g_c',20) endif if (.not.ga_destroy(g_b)) then call ga_error('failed: destroy g_b',30) endif if (.not.ga_destroy(g_a)) then call ga_error('failed: destroy g_a',40) endif enddo endif 200 format(a15, i2, ': ', e12.4, ' seconds ',f12.1, . ' mflops/proc ', 3a2) call ga_sync() call ga_terminate call MP_FINALIZE() c end c c----------------------------------------------------------------------- c Verify for correctness. Process 0 computes BLAS dgemm c locally. For larger arrays, disbale this test as memory c might not be sufficient c subroutine verify_ga_dgemm(xt1, xt2, num_m, num_n, num_k, $ alpha, g_a, g_b, beta, g_c, tmpa, tmpb, tmpc) implicit none character *1 xt1, xt2 integer num_m, num_n, num_k, g_a, g_b, g_c double precision alpha, beta real *8 tmpa(num_m,num_k), tmpb(num_k,num_n), tmpc(num_m,num_n) integer i,j,type,dim1, dim2 real *8 abs_value #include "mafdecls.fh" #include "testutil.fh" do i = 1,num_n do j = 1, num_m tmpc(j,i) = -1.0 tmpa(j,i) = -2.0 enddo enddo call ga_inquire(g_a, type, dim1, dim2) call ga_get(g_a, 1, dim1, 1, dim2, tmpa, dim1) call ga_inquire(g_b, type, dim1, dim2) call ga_get(g_b, 1, dim1, 1, dim2, tmpb, dim1) c compute dgemm sequentially call xgemm(xt1, xt2, num_m, num_n, num_k, alpha, & tmpa, num_m, tmpb, num_k, beta, tmpc, num_m) c after computing c locally, verify it with the values in g_c call ga_inquire(g_c, type, dim1, dim2) call ga_get(g_c, 1, dim1, 1, dim2, tmpa, dim1) do i = 1,num_n do j = 1, num_m abs_value = abs(tmpc(j,i)-tmpa(j,i)) if(abs_value .gt. 1.0 .OR. abs_value .lt. -1.0) then write(*,*) 'Values are = ', tmpc(j,i), tmpa(j,i) write(*,*) 'Values are = ', abs(tmpc(j,i)-tmpa(j,i)), . abs_value call ffflush(6) call ga_error('verify ga_dgemm failed', 0) endif enddo enddo return end c c----------------------------------------------------------------------- c called by process '0' (or your master process ) c subroutine load_ga(handle,f, dim1,dim2) implicit none integer handle integer dim1,dim2,i real*8 f(dim1,dim2) #include "mafdecls.fh" if(dim1.le.0)return if(dim2.le.0)return call ga_put(handle, 1, dim1, 1, dim2, f, dim1) return end c c----------------------------------------------------------------------- c must be called by all processors, if you need to fillup the c entire array c subroutine load_ga_from_square(handle,num,f,ndim) implicit none integer handle, memhandle integer num,ndim real*8 f(ndim,ndim) integer ilo, ihi, jlo, jhi, nx, ny, ibuff integer ga_nodeid, i1, i2, i, j, ix, jx #include "mafdecls.fh" call ga_distribution(handle, ga_nodeid(), ilo, ihi, jlo, jhi) if(ihi.le.0)return if(jhi.le.0)return c nx = ihi - ilo + 1 c ny = jhi - jlo + 1 do i = ilo,ihi,ndim do j = jlo,jhi,ndim call ga_put(handle,i,min(ihi,i+ndim),j,min(jhi,j+ndim), & f,ndim) enddo enddo return end c c----------------------------------------------------------------------- c must be called by all processors, if you need to fillup the c entire array c subroutine load_ga_from_triangle(handle,f,ndim) implicit none integer handle, memhandle real*8 f(*) integer ndim integer ilo, ihi, jlo, jhi, nx, ny, ibuff integer ga_nodeid, i1, i2, i, j, ix, jx #include "mafdecls.fh" call ga_distribution(handle, ga_nodeid(), ilo, ihi, jlo, jhi) if(ihi.le.0)return if(jhi.le.0)return nx = ihi - ilo + 1 ny = jhi - jlo + 1 if (.not.ma_alloc_get(MT_DBL,nx*ny,'flap',memhandle,ibuff)) then call ga_error('failed: allocate triangle',100) endif do i = 1,nx do j = 1,ny ix = i + ilo - 1 jx = j + jlo - 1 i1 = min(ix,jx) i2 = max(ix,jx) dbl_mb(ibuff + nx*(j-1) + (i-1) ) = f(i2*(i2-1)/2 + i1) enddo enddo call ga_put(handle,ilo,ihi,jlo,jhi, & dbl_mb(ibuff),nx) if (.not.ma_free_heap(memhandle)) then call ga_error('failed: free triangle',100) endif return end ga-5.9.2/global/testing/pgtest.F000066400000000000000000002356571500715745200165000ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c $Id: pgtest.F,v 1.9 2005-11-23 10:25:18 manoj Exp $ c vector boxes lack arithmetic precision # define THRESH 1d-13 # define THRESHF 1e-5 #define MISMATCH(x,y) abs(x-y)/max(1d0,abs(x)).gt.THRESH #define MISMATCHF(x,y) abs(x-y)/max(1.0,abs(x)).gt.THRESHF #define NEW_API c#define MIRROR c#define GA3 c#define NGA_GATSCAT c#define USE_RESTRICTED #ifdef USE_RESTRICTED # define NEW_API #endif program main implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer heap, stack, fudge, ma_heap, me, nproc integer inode,proclist(100),i,j,nprocs integer proc_group(0:100), my_proc_group, grp, num_grp integer midnode, splitnode, color, key logical status parameter (heap=200*200*4, fudge=100, stack=200*200) c c*** Intitialize a message passing library c #include "mp3.fh" c c*** Initialize GA c c There are 2 choices: ga_initialize or ga_initialize_ltd. c In the first case, there is no explicit limit on memory usage. c In the second, user can set limit (per processor) in bytes. c call ga_initialize() nproc = ga_nnodes() me = ga_nodeid() c c*** Initialize the MA package c MA must be initialized before any global array is allocated c ma_heap = heap/nproc + fudge ma_heap = 2*ma_heap #ifdef USE_RESTRICTED ma_heap = 2*ma_heap #endif status = ma_init(MT_DCPL, stack, ma_heap) if (.not. status) call ga_error('ma_init failed',-1) c c we can also use GA_set_memory_limit BEFORE first ga_create call c call GA_set_memory_limit(util_mdtob(ma_heap)) c c*** Create process groups on SMP nodes c #if 1 midnode = nproc/2 do i = 1, midnode proclist(i) = i-1 end do nprocs = midnode proc_group(0) = ga_pgroup_create(proclist, nprocs) do i = midnode+1, nproc proclist(i-midnode) = i-1 end do nprocs = nproc - midnode proc_group(1) = ga_pgroup_create(proclist, nprocs) c call ga_pgroup_set_default(proc_group(inode)) if (me.lt.midnode) then call ga_pgroup_set_default(proc_group(0)) call runtest endif call ga_pgroup_set_default(ga_pgroup_get_world()) call ga_sync() if (me.ge.midnode) then call ga_pgroup_set_default(proc_group(1)) call runtest endif #else c split into 2 equal groups if(me.eq.0) then print *,' ************* Testing ga_pgroup_split ************ ' call ffflush(6) endif num_grp = 2 grp = ga_pgroup_get_default() my_proc_group = ga_pgroup_split(grp, num_grp) call ga_pgroup_set_default(my_proc_group) call runtest c reset to world group call ga_pgroup_set_default(ga_pgroup_get_world()) call ga_sync() c split into 2 irregular groups (33:67) if(me.eq.0) then print *,' ********* Testing ga_pgroup_split_irreg *********** ' call ffflush(6) endif num_grp = 2 grp = ga_pgroup_get_default() splitnode = nproc/3 if(me.lt.splitnode) then color=0 else color=1 endif c same key for all processes means automatic control of rank assignment key = 0 my_proc_group = ga_pgroup_split_irreg(grp, color, key) call ga_pgroup_set_default(my_proc_group) if(me.lt.splitnode) then call runtest endif call ffflush(6) call ga_sync() if(me.ge.splitnode) then call runtest endif call ga_sync() #endif call ga_pgroup_set_default(ga_pgroup_get_world()) c c*** Check if memory limits are enforced c c call check_mem(util_mdtob(ma_heap*ga_nnodes())) c c*** Tidy up the GA package c call ga_sync() c if(ga_nodeid().eq.0) print *,'All tests successful ' c write(6,*) 'Calling ga_terminate' call ga_terminate() c c*** Tidy up after message-passing library c call MP_FINALIZE() c stop end c subroutine runtest implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer me, nproc logical status me = ga_nodeid() nproc = ga_nnodes() if(me.eq.0)then #ifdef MIRROR print *,' Performing tests on Mirrored Arrays' #endif print *,' GA initialized ' call ffflush(6) endif c c c Uncomment the below line to register external memory allocator c for dynamic arrays inside GA routines. c call register_ext_memory() c if(me.eq.(nproc-1))then print *, 'using ', nproc,' process(es) ', ga_cluster_nnodes(), $ ' cluster nodes' print *,'process ', me, ' is on node ',ga_cluster_nodeid(), $ ' with ', ga_cluster_nprocs(-1), ' processes' call ffflush(6) endif #ifdef MIRROR if (me.eq.0) then write(6,*) write(6,*) ' TESTING MIRRORED ARRAYS ' write(6,*) call ffflush(6) endif #endif call ga_sync() c c*** Check support for double precision arrays c if (me.eq.0) then write(6,*) write(6,*) ' CHECKING DOUBLES ' write(6,*) call ffflush(6) endif call check_dbl() c c*** Check support for double precision complex arrays c if (me.eq.0) then write(6,*) write(6,*) ' CHECKING DOUBLE COMPLEX ' write(6,*) call ffflush(6) endif call check_complex() c c*** Check support for integer arrays c if (me.eq.0) then write(6,*) write(6,*) ' CHECKING INTEGERS ' write(6,*) call ffflush(6) endif call check_int() c c c*** Check support for single precision c if (me.eq.0) then write(6,*) write(6,*) ' CHECKING SINGLE PRECISION ' write(6,*) call ffflush(6) endif call check_flt() c if (me.eq.0) then write(6,*) write(6,*)'CHECKING Wrappers to Message Passing Collective ops' write(6,*) call ffflush(6) endif call check_wrappers c if(me.eq.0) call ga_print_stats() if(me.eq.0) print *,' ' if(me.eq.0) print *,'All tests succesful ' c return end subroutine check_dbl() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,m parameter (n = 128) parameter (m = 2*n) double precision a(n,n), b(n,n), v(m),w(m) #ifdef MIRROR logical lsame integer g_c, p_mirror integer ndim, dims(2), chunk(2) integer alo(2), blo(2), ahi(2), bhi(2) #else # ifdef NEW_API integer ndim, dims(2), chunk(2) # endif #endif integer iv(m), jv(m) #ifdef NGA_GATSCAT integer ijv(2,m) #endif logical status integer g_a, g_b integer iran, i,j, loop,nloop,maxloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, ii, jj parameter (maxloop = 100) integer maxproc parameter (maxproc = 128) double precision crap, sum1, sum2, x double precision nwords logical found integer lprocs,inode,iproc,nnodes intrinsic int integer tlo(2),thi(2),tld(2),index #ifdef USE_RESTRICTED integer num_rstrctd integer rstrctd_list(maxproc/2) #endif iran(i) = int(drand(0)*real(i)) + 1 c nproc = ga_nnodes() me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) nloop = Min(maxloop,n) #ifdef USE_RESTRICTED num_rstrctd = nproc/2 if (num_rstrctd.eq.0) num_rstrctd = 1 do i = 1, num_rstrctd rstrctd_list(i) = (num_rstrctd/2) + i-1 end do #endif c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n #ifndef MIRROR a(i,j) = i-1 + (j-1)*n #else a(i,j) = inode + i-1 + (j-1)*n #endif b(i,j) =-1. enddo enddo * write(6,*) ' correct ' * call output(a, 1, n, 1, n, n, n, 1) * call ffflush(6) c c Create a global array c c print *,ga_nodeid(), ' creating array' * call ffflush(6) c call setdbg(1) #ifdef NEW_API g_a = ga_create_handle() ndim = 2 dims(1) = n dims(2) = n call ga_set_data(g_a,ndim,dims,MT_DBL) call ga_set_array_name(g_a,'a') #ifdef USE_RESTRICTED call ga_set_restricted(g_a, rstrctd_list, num_rstrctd) #endif # ifdef MIRROR p_mirror = ga_pgroup_get_mirror() call ga_set_pgroup(g_a,p_mirror) # endif status = ga_allocate(g_a) #else # ifndef MIRROR status = ga_create(MT_DBL, n, n, 'a', 0, 0, g_a) # else ndim = 2 dims(1) = n dims(2) = n chunk(1) = 0 chunk(2) = 0 p_mirror = ga_pgroup_get_mirror() status = nga_create_config(MT_DBL, ndim, dims, 'a', chunk, + p_mirror, g_a) # endif #endif if (.not. status) then write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',0) endif c c check if handle is valid. be quiet unless error c if(.not.ga_valid_handle(g_a)) call ga_error("invalid handle",g_a) c call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) call ga_sync() c c Zero the array c if (me .eq. 0) then write(6,21) 21 format(/'> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_sync() do i = 1, n do j = 1, n if (b(i,j) .ne. 0.0d0) then write(6,*) me,' zero ', i, j, b(i,j) call ffflush(6) c call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format(/'> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) * write(6,4) me, ilo, ihi, jlo, jhi * 4 format(' node ',i2,' checking put ',4i4) * call ffflush(6) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c call util_dfill(n*n, 0.0d0, b, 1) * call ga_print(g_a,1) call ga_get(g_a, 1, n, 1, n, b, n) * write(6,*) ' after get' * call output(b, 1, n, 1, n, n, n, 1) c do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif call ga_sync() c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format(/'> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_dfill(n*n, 0.0d0, b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif sum1 = 0.0d0 do j = jlo, jhi do i = ilo, ihi sum1 = sum1 + b(i,j) if (b(i,j) .ne. a(i,j)) then write(6,*) i, j, b(i,j), a(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo c enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) call ffflush(6) endif call ga_sync() c c Each node accumulates into disjoint sections of the array c if (me .eq. 0) then write(6,9) 9 format(/'> Checking accumulate ... ') call ffflush(6) endif call ga_sync() c crap = drand(12345) ! Same seed for each process do j = 1, n do i = 1, n c b(i,j) = drand(0) b(i,j) = i+j enddo enddo c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc c x = drand(0) x = 10. ilo = i ihi = min(i+inc-1, n) if(ihi.eq.n-1)ihi=n c ihi = min(i+inc, n) jlo = j jhi = min(j+inc-1, n) if(jhi.eq.n-1)jhi=n c jhi = min(j+inc-1, n) * call ffflush(6) #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif c print *, me, 'checking accumulate ',ilo,ihi,jlo,jhi,x * 11 format(' node ',i2,' checking accumulate ',4i4) * call ffflush(6) call ga_acc(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n, x) endif ij = ij + 1 c c Each process applies all updates to its local copy c do jj = jlo, jhi do ii = ilo, ihi a(ii,jj) = a(ii,jj) + x * b(ii,jj) enddo enddo enddo enddo call ga_sync() c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if(MISMATCH(b(i,j),a(i,j)))then write(6,*) ' acc ', me, i, j, a(i,j), b(i,j) call ffflush(6) c call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' disjoint ga_acc is OK' write(6,*) endif c c overlapping accumulate call ga_sync() #ifdef NEW_API g_b = ga_create_handle() call ga_set_data(g_b,ndim,dims,MT_DBL) call ga_set_array_name(g_b,'b') # ifdef MIRROR call ga_set_pgroup(g_b,p_mirror) # endif #ifdef USE_RESTRICTED call ga_set_restricted(g_b, rstrctd_list, num_rstrctd) #endif if (.not.ga_allocate(g_b)) then #else # ifndef MIRROR if (.not. ga_create(MT_DBL, n, n, 'b', 0, 0, g_b)) then # else if (.not. nga_create_config(MT_DBL, ndim, dims, 'b', chunk, + p_mirror, g_b)) then # endif #endif call ga_error('ga_create failed for second array ',0) endif c call ga_zero(g_b) call ga_sync() call ga_acc(g_b, n/2, n/2, n/2, n/2, 1d0, 1, 1d0) call ga_sync() #ifndef MIRROR if (me.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, b(1,1), 1) x = abs(b(1,1) -1d0*nproc) #else if (iproc.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, b(1,1), 1) x = abs(b(1,1) -1d0*lprocs) #endif if(x.gt. 1d-10)then #ifndef MIRROR write(6,*)'val=',b(1,1),' expected=',nproc, x #else write(6,*)'val=',b(1,1),' expected=',lprocs, x #endif call ga_error('overlapping accumulate failed',0) endif if (me.eq.0) then write(6,*) write(6,*) ' overlapping ga_acc is OK' write(6,*) endif endif c c Check the ga_add function c if (me .eq. 0) then write(6,91) 91 format(/'> Checking add ...') call ffflush(6) endif c c crap = drand(12345) ! Everyone has same seed do j = 1, n do i = 1, n b(i,j) = drand(0) a(i,j) = 0.1d0*a(i,j) + 0.9d0*b(i,j) enddo enddo #ifndef MIRROR if (me.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) #else if (iproc.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) #endif call ga_add(0.1d0, g_a, 0.9d0, g_b, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if(MISMATCH(b(i,j), a(i,j)))then write(6,*) ' add ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' add is OK ' write(6,*) endif call ga_sync() c c Check the ddot function c if (me .eq. 0) then write(6,19) 19 format(/'> Checking ddot ...') call ffflush(6) endif crap = drand(12345) ! Everyone has same seed sum1 = 0.0d0 do j = 1, n do i = 1, n b(i,j) = drand(0) sum1 = sum1 + a(i,j)*b(i,j) enddo enddo #ifndef MIRROR if (me.eq.0) then #else if (iproc.eq.0) then #endif call ga_put(g_b, 1, n, 1, n, b, n) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() sum2 = ga_ddot(g_a,g_b) if(MISMATCH(sum1, sum2))then write(6,*) ' ddot wrong ', sum1, sum2 call ffflush(6) call ga_error('... exiting ',0) else if (me.eq.0) then write(6,*) write(6,*) ' ddot is OK ' write(6,*) endif c c Check the ga_scale function c if (me .eq. 0) then write(6,92) 92 format(/'> Checking scale ...') call ffflush(6) endif call ga_scale(g_a, 0.123d0) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n a(i,j) = a(i,j)*0.123d0 if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' dscal ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' scale is OK ' write(6,*) endif c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c call ga_sync() if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_sync() #ifndef MIRROR itmp = iran(nproc)-1 if(me.eq.itmp) then #else itmp = iran(lprocs)-1 if(me.eq.itmp) then #endif #ifndef NGA_GATSCAT do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo call ga_gather(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',0) endif enddo #else do loop = 1,m ilo = iran(n) jlo = iran(n) ijv(1,loop) = ilo ijv(2,loop) = jlo enddo call nga_gather(g_a, v, ijv, m) do loop = 1,m ilo= ijv(1,loop) jlo= ijv(2,loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',0) endif enddo #endif endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_sync() #ifndef MIRROR if(me.eq.iran(ga_nnodes())-1) then #else if(iproc.eq.iran(lprocs)-1) then #endif #ifndef NGA_GATSCAT do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo c v(loop) = DSIN(a(ilo,jlo)+b(ilo,jlo)) v(loop) = 1d0 *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) c if(v(loop) .ne. w(loop))then if(w(loop) .ne. 1d0 *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', 1d0 *(ilo+jlo) call ffflush(6) call ga_error('... exiting ',0) endif enddo #else do loop = 1,m ilo = iran(n) jlo = iran(n) ijv(1,loop) = ilo ijv(2,loop) = jlo c v(loop) = DSIN(a(ilo,jlo)+b(ilo,jlo)) v(loop) = 1d0 *(ilo+jlo) enddo call nga_scatter(g_a, v, ijv, m) do loop = 1,m ilo= ijv(1,loop) jlo= ijv(2,loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) c if(v(loop) .ne. w(loop))then if(w(loop) .ne. 1d0 *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', 1d0 *(ilo+jlo) call ffflush(6) call ga_error('... exiting ',0) endif enddo #endif endif call ga_sync() enddo c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c call ga_sync() c c scatter-acc available in GA ver. 3.0 #ifdef GA3 if (me.eq.0) then write(6,*) write(6,*) ' checking scatter-accumulate' write(6,*) endif c crap = drand(1234) call ga_zero(g_a) c do j = 1, n do i = 1, n b(i,j) =0. enddo enddo c x = .1d0 ii =n do jj = 1,1 call ga_sync() do loop = 1, ii c generate unique i,j pairs 10 continue i = iran(n) j=iran(n) if (found(i,j, iv, jv, loop-1) ) goto 10 #ifndef NGA_GATSCAT iv(loop) = i jv(loop) = j #else ijv(1,loop) = i ijv(2,loop) = j #endif v(loop) = 1d0 *(i+j) #ifndef MIRROR b(i,j) = b(i,j) + nproc*x*v(loop) ! update local ref. array #else b(i,j) = b(i,j) + lprocs*x*v(loop) ! update local ref. array #endif enddo #ifndef NGA_GATSCAT call ga_scatter_acc(g_a,v,iv,jv, ii,x) #else call nga_scatter_acc(g_a,v,ijv,ii,x) #endif c call ga_sync() c c check the result c call ga_get(g_a, 1, n, 1,n, a, n) do loop = 1,ii #ifndef NGA_GATSCAT i = iv(loop) j = jv(loop) #else i = ijv(1,loop) j = ijv(2,loop) #endif if(MISMATCH(a(i,j),b(i,j)))then print *,'Error',i,j,loop,a(i,j),'expected=',b(i,j) * if(me.eq.0)then * do ii=1,loop * print *,'element',ii, iv(ii),jv(ii) * enddo * endif call ga_error('scatter-acc error in trial ',jj) endif enddo call ga_sync() enddo call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' scatter-accumulate is OK' write(6,*) endif #endif #ifdef MIRROR c c Check the merge function c do j = 1, n do i = 1, n a(i,j) = inode + i-1 + (j-1)*n enddo enddo if (me .eq. 0) then write(6,23) 23 format(/'> Checking merge ...') call ffflush(6) endif if (iproc.eq.0) then call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_merge_mirrored(g_a) call ga_get(g_a, 1, n, 1, n, b, n) lsame = .true. ilo = 0 ihi = nnodes*(nnodes-1)/2 do i = 1, n do j = 1, n if ((dble(nnodes)*(a(j,i)-dble(inode)) + + dble(ihi)).ne.b(j,i)) then lsame = .false. ilo = ilo + 1 endif end do end do if(lsame.and.me.eq.0)then write(6,*) write(6,*) ' merge is OK ' write(6,*) else if (.not.lsame) then write(6,*) write(6,*) ' merge is wrong ',ilo write(6,*) call ga_error('... exiting ',0) endif c c Checking copy between different array configurations c if (me .eq. 0) then write(6,20) 20 format(/'> Checking mixed copy ...') call ffflush(6) endif #ifdef NEW_API g_c = ga_create_handle() call ga_set_data(g_c,ndim,dims,MT_DBL) call ga_set_array_name(g_c,'c') if (.not.ga_allocate(g_c)) then #else if (.not. nga_create(MT_DBL, ndim, dims, 'c', chunk, g_c)) then #endif call ga_error('ga_create failed for third array ',0) endif call ga_zero(g_c) call ga_copy(g_a,g_c) call ga_get(g_a, 1, n, 1, n, a, n) call ga_get(g_c, 1, n, 1, n, b, n) do i = 1, n do j = 1, n if (a(j,i).ne.b(j,i)) then lsame = .false. endif end do end do if(lsame.and.me.eq.0)then write(6,*) write(6,*) ' Mixed copy (mirrored to normal) is OK ' write(6,*) elseif (me.eq.0) then write(6,*) write(6,*) ' Mixed copy (mirrored to normal) is wrong ',ilo write(6,*) call ga_error('... exiting ',0) endif call ga_zero(g_a) call ga_copy(g_c,g_a) call ga_get(g_a, 1, n, 1, n, a, n) do i = 1, n do j = 1, n if (a(j,i).ne.b(j,i)) then lsame = .false. endif end do end do if(lsame.and.me.eq.0)then write(6,*) write(6,*) ' Mixed copy (normal to mirrored) is OK ' write(6,*) elseif (me.eq.0) then write(6,*) write(6,*) ' Mixed copy (normal to mirrored) is wrong ',ilo write(6,*) call ga_error('... exiting ',0) endif c c Check the merge to distributed patch function c do j = 1, n do i = 1, n a(i,j) = inode + i-1 + (j-1)*n enddo enddo if (me .eq. 0) then write(6,24) 24 format(/'> Checking merge to distributed patch ...') call ffflush(6) endif c c get low and high indices of patch c ilo = n/4 ihi = ilo + n/2 do i = 1, 2 alo(i) = ilo blo(i) = ilo + 1 ahi(i) = ihi bhi(i) = ihi + 1 end do if (iproc.eq.0) then call ga_put(g_a, 1, n, 1, n, a, n) endif call nga_merge_distr_patch(g_a, alo, ahi, g_c, blo, bhi) call ga_get(g_c, 1, n, 1, n, b, n) lsame = .true. ilo = 0 ihi = nnodes*(nnodes-1)/2 do i = alo(1), ahi(1) ii = i-alo(1) + blo(1) do j = alo(2), ahi(2) jj = j-alo(2) + blo(2) if ((dble(nnodes)*(a(j,i)-dble(inode)) + + dble(ihi)).ne.b(jj,ii)) then c if (me.eq.0) then c write(6,*) i,j,ii,jj, nnodes*a(j,i)+ihi, b(jj,ii) c endif lsame = .false. ilo = ilo + 1 endif end do end do if(lsame.and.me.eq.0)then write(6,*) write(6,*) ' merge to distributed patch is OK ' write(6,*) elseif (.not.lsame) then write(6,*) write(6,*) ' merge to distributed patch is wrong ',ilo write(6,*) call ga_error('... exiting ',0) endif #endif c c Delete the global arrays c status = ga_destroy(g_b) status = ga_destroy(g_a) c return end c----------------------------------------------------------------- subroutine check_complex() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,m parameter (n = 60) parameter (m = 2*n) double complex a(n,n), b(n,n), v(m),w(m) #ifdef MIRROR integer ndim, dims(2), chunk(2), p_mirror #else # ifdef NEW_API integer ndim, dims(2), chunk(2), p_mirror # endif #endif integer iv(m), jv(m) logical status integer g_a, g_b integer iran, i,j, loop,nloop,maxloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, ii, jj, nnodes parameter (maxloop = 100) integer maxproc parameter (maxproc = 128) double precision crap, real double precision nwords double complex x, sum1, sum2, factor integer lprocs, inode, iproc #ifdef USE_RESTRICTED integer num_rstrctd integer rstrctd_list(maxproc/2) #endif intrinsic int iran(i) = int(drand(0)*real(i)) + 1 c nproc = ga_nnodes() me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) nloop = Min(maxloop,n) #ifdef USE_RESTRICTED num_rstrctd = nproc/2 if (num_rstrctd.eq.0) num_rstrctd = 1 do i = 1, num_rstrctd rstrctd_list(i) = (num_rstrctd/2) + i-1 end do #endif c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n #ifndef MIRROR a(i,j) = cmplx(dble(i-1), dble((j-1)*n)) #else a(i,j) = cmplx(dble(inode),0.0d00) + + cmplx(dble(i-1), dble((j-1)*n)) #endif b(i,j) = cmplx(-1d0,1d0) enddo enddo c c Create a global array c c print *,ga_nodeid(), ' creating array' call ffflush(6) c call setdbg(1) #ifdef NEW_API ndim = 2 dims(1) = n dims(2) = n g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_DCPL) call ga_set_array_name(g_a,'a') #ifdef USE_RESTRICTED call ga_set_restricted(g_a, rstrctd_list, num_rstrctd) #endif # ifdef MIRROR p_mirror = ga_pgroup_get_mirror() call ga_set_pgroup(g_a,p_mirror) # endif status = ga_allocate(g_a) #else # ifndef MIRROR status = ga_create(MT_DCPL, n, n, 'a', 0, 0, g_a) # else ndim = 2 dims(1) = n dims(2) = n chunk(1) = 0 chunk(2) = 0 p_mirror = ga_pgroup_get_mirror() status = nga_create_config(MT_DCPL, ndim, dims, 'a', chunk, + p_mirror, g_a) # endif #endif if (.not. status) then write(6,*) ' ga_create failed' call ga_error('... exiting ',0) endif #ifdef NEW_API g_b = ga_create_handle() call ga_set_data(g_b,ndim,dims,MT_DCPL) call ga_set_array_name(g_b,'b') # ifdef MIRROR call ga_set_pgroup(g_b,p_mirror) # endif if (.not.ga_allocate(g_b)) then #else # ifndef MIRROR if (.not. ga_create(MT_DCPL, n, n, 'b', 0, 0, g_b)) then # else if (.not. nga_create_config(MT_DCPL, ndim, dims, 'b', chunk, _ p_mirror, g_b)) then # endif #endif call ga_error('ga_create failed for second array ',0) endif call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) call ga_sync() c c Zero the array c if (me .eq. 0) then write(6,21) 21 format(/'> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_sync() do i = 1, n do j = 1, n if(b(i,j).ne.(0d0,0d0)) then write(6,*) me,' zero ', i, j, b(i,j) call ffflush(6) c call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format(/'> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c call util_qfill(n*n, (0d0,0d0), b, 1) call ga_get(g_a, 1, n, 1, n, b, n) c do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif call ga_sync() c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format(/'> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_qfill(n*n, (0.0d0,0d0), b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif do j = jlo, jhi do i = ilo, ihi if (b(i,j) .ne. a(i,j)) then write(6,*)'error:', i, j, b(i,j), a(i,j) call ga_error('... exiting ',0) endif enddo enddo c enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) call ffflush(6) endif call ga_sync() c c Each node accumulates into disjoint sections of the array c if (me .eq. 0) then write(6,9) 9 format(/'> Checking accumulate ... ') call ffflush(6) endif call ga_sync() c crap = drand(12345) ! Same seed for each process do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0),drand(1)) b(i,j) = cmplx(dble(i),dble(j)) enddo enddo c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc c x = cmplx(drand(0),0.333d0) c x = cmplx(0.333d0,0) * x = cmplx(0d0,0d0) x = 0 ilo = i ihi = min(i+inc-1, n) if(ihi.eq.n-1)ihi=n jlo = j jhi = min(j+inc-1, n) if(jhi.eq.n-1)jhi=n #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif call ga_acc(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n, x) endif ij = ij + 1 c c Each process applies all updates to its local copy c do jj = jlo, jhi do ii = ilo, ihi a(ii,jj) = a(ii,jj) + x * b(ii,jj) enddo enddo enddo enddo call ga_sync() c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' acc ', me, i, j, a(i,j), b(i,j) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' disjoint ga_acc is OK' write(6,*) endif c c overlapping accumulate c call ga_zero(g_b) call ga_acc(g_b, n/2, n/2, n/2, n/2, (1d0,-1d0), 1, (1d0,0d0)) call ga_sync() #ifndef MIRROR if (me.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, x, 1) if (MISMATCH(x, ((1d0,-1d0)*nproc)))then #else if (iproc.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, x, 1) if (MISMATCH(x, ((1d0,-1d0)*lprocs)))then #endif c if(error.gt. (1d-8))then #ifndef MIRROR write(6,*)'val=',x,' expected=(',nproc,',',-nproc,')' #else write(6,*)'val=',x,' expected=(',lprocs,',',-lprocs,')' #endif call ga_error('overlapping accumulate failed',0) endif write(6,*) write(6,*) ' overlapping ga_acc is OK' write(6,*) endif c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif call ga_sync() #ifndef MIRROR if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) #else if(iproc.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) #endif call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c c c Check the ga_scale function c if (me .eq. 0) then write(6,92) 92 format(/'> Checking scale ...') call ffflush(6) endif factor = (1d0,-1d0) call ga_scale(g_a, factor) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n a(i,j) = a(i,j)*factor if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' dscal ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' scale is OK ' write(6,*) endif c c Check scatter&gather c call ga_sync() if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_sync() #ifndef MIRROR itmp = iran(nproc)-1 if(me.eq.itmp) then #else itmp = iran(lprocs)-1 if(iproc.eq.itmp) then #endif do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo call ga_gather(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',0) endif enddo endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_sync() #ifndef MIRROR if(me.eq.iran(ga_nnodes())-1) then #else if(me.eq.iran(lprocs)-1) then #endif do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo v(loop) = (1d0,-1d0) *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) if(w(loop) .ne. (1d0,-1d0) *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', (1d0,-1d0) *(ilo+jlo) call ffflush(6) endif enddo endif call ga_sync() enddo c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c c Check ga_add c if (me .eq. 0) then write(6,91) 91 format(/'> Checking add ...') call ffflush(6) endif call ga_get(g_a, 1, n, 1, n, a, n) crap = drand(12345) ! Everyone has same seed do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0), drand(1)) a(i,j) = (0.1d0,-.1d0)*a(i,j) + (.9d0,-.9d0)*b(i,j) enddo enddo #ifndef MIRROR if (me.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) #else if (iproc.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) #endif call ga_add((0.1d0,-.1d0), g_a, (0.9d0,-.9d0), g_b, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' add ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' add is OK ' write(6,*) endif call ga_sync() c c Check the zdot function c if (me .eq. 0) then write(6,19) 19 format(/'> Checking zdot ...') call ffflush(6) endif crap = drand(12345) ! Everyone has same seed sum1 = (0.0d0,0.d0) do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0), drand(1)) sum1 = sum1 + a(i,j)*b(i,j) enddo enddo #ifndef MIRROR if (me.eq.0) then #else if (iproc.eq.0) then #endif call ga_put(g_b, 1, n, 1, n, b, n) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() sum2 = ga_zdot(g_a,g_b) if (MISMATCH(sum1, sum2))then write(6,*) ' zdot wrong ', sum1, sum2 call ffflush(6) call ga_error('... exiting ',0) else if (me.eq.0) then write(6,*) write(6,*) ' zdot is OK ' write(6,*) endif c c Delete the global arrays c 123 continue status = ga_destroy(g_b) status = ga_destroy(g_a) c return end c----------------------------------------------------------------- subroutine check_int() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n parameter (n = 128) integer a(n,n), b(n,n) logical status integer g_a integer iran, i, j, loop, nloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, dimi, dimj, ii, jj integer ndim, dims(2), chunk(2), p_mirror, nnodes integer lprocs, inode, iproc,lproc, nprocs double precision nwords parameter (nloop = 100) integer maxproc parameter (maxproc = 128) integer map(5,maxproc), found, np,k double precision crap, sum1, real integer buf #ifdef USE_RESTRICTED integer num_rstrctd integer rstrctd_list(maxproc/2) #endif intrinsic int iran(i) = int(drand(0)*real(i)) + 1 c nproc = ga_nnodes() me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) #ifdef USE_RESTRICTED num_rstrctd = nproc/2 if (num_rstrctd.eq.0) num_rstrctd = 1 do i = 1, num_rstrctd rstrctd_list(i) = (num_rstrctd/2) + i-1 end do #endif c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n #ifndef MIRROR a(i,j) = i-1 + (j-1)*1000 #else a(i,j) = inode + i-1 + (j-1)*1000 #endif enddo enddo c c Create a global array c ndim = 2 dims(1) = n dims(2) = n chunk(1) = 0 chunk(2) = 0 #ifdef NEW_API g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_INT) call ga_set_array_name(g_a,'a') #ifdef USE_RESTRICTED call ga_set_restricted(g_a, rstrctd_list, num_rstrctd) #endif # ifdef MIRROR p_mirror = ga_pgroup_get_mirror() call ga_set_pgroup(g_a,p_mirror) # endif if (.not.ga_allocate(g_a)) then #else # ifndef MIRROR if (.not. ga_create(MT_INT, n, n, 'a', 0, 0, g_a)) then # else p_mirror = ga_pgroup_get_mirror() if (.not. nga_create_config(MT_INT, ndim, dims, 'a', chunk, + p_mirror, g_a)) then # endif #endif write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',0) endif c c Zero the array c if (me .eq. 0) then write(6,21) 21 format(/'> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) do i = 1, n do j = 1, n if (b(i,j) .ne. 0) then write(6,*) ' zero ', me, i, j, b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format(/'> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) c write(6,4) me, ilo, ihi, jlo, jhi c4 format(' node ',i2,' checking put ',4i4) c call ffflush(6) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c #ifndef MIRROR if(me.eq.0)then #else if(iproc.eq.0)then #endif call ga_get(g_a, 1, n, 1, n, b, n) do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo endif call ga_sync() c if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format(/'> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_ifill(n*n, 0.0d0, b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) c if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif c sum1 = 0.0d0 do j = jlo, jhi do i = ilo, ihi sum1 = sum1 + b(i,j) if (b(i,j) .ne. a(i,j)) then write(6,*) 'error ', i, j, b(i,j), a(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) endif c call ga_sync() if (me .eq. 0 .and. n.gt.7) then write(6,*) write(6,*) '> Checking ga_print_patch --- should see ' write(6,*)' [2002 3002 4002 5002 6002]' write(6,*)' [2003 3003 4003 5003 6003]' write(6,*)' [2004 3004 4004 5004 6004]' write(6,*) call ffflush(6) endif if(n.gt.5) call ga_print_patch(g_a,3,5,3,7,0) c call ga_sync() c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*) '> Checking read_inc ... ' write(6,*) call ffflush(6) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc inc =5 c every processor will be operating on somebody elses data c #ifndef MIRROR lproc = nproc - me - 1 #else lproc = nnodes-iproc-1 lproc = inode*lprocs + lproc #endif c call ga_distribution(g_a,lproc,ilo,ihi,jlo,jhi) c dimi = ihi-ilo dimj = jhi-jlo write(6,111) me,ilo,ihi,jlo,jhi,dimi,dimj 111 format(i2,'..',4i8,'.',2i8) call ga_sync() if(ilo .gt.0 .and. jhi .gt. 0)then do loop = 1,nloop ii= IABS(iran(dimi)) jj= IABS(iran(dimj)) i=ilo + Mod(ii,dimi) j=jlo + Mod(jj,dimj) c c write(6,*) me,'..',ilo,ihi,jlo,jhi,'.',dimi,dimj,'..',i,j c call ffflush(6) buf = ga_read_inc(g_a,i,j,inc) if(a(i,j).ne. buf)then write(6,*)me,'READ_inc ', i,',',j,',', a(i,j),' ',buf,me call ffflush(6) endif call ga_get(g_a, i,i,j,j, buf,1) a(i,j) = a(i,j)+inc if(a(i,j).ne. buf)then write(6,*)me,'read_INC ', i,',',j,',', a(i,j),' ',buf,me call ffflush(6) endif enddo endif call ga_sync() c if (me.eq.0) then write(6,*) write(6,*) ' read_inc is OK' write(6,*) endif c c c #if 000 #ifndef MIRROR if (me.eq.0) then write(6,*) write(6,*) '> checking ga_fence and ga_lock' write(6,*) call ffflush(6) endif c call ga_zero(g_a) c c*** use ga_read_inc and elements g_a(1:2,1) to implement a lock c*** compute g_a(:,n) = sum (1(:)) for P processors c status = ga_create_mutexes(1) if (.not. status) then call ga_error('ga_create_mutexes failed ',0) endif if(n.lt.2)call ga_error('insufficient n to test ga_fence',n) call ga_lock(0) c call my_lock(g_a) c get original values g_a(:,n) call ga_get(g_a, 1,n, n,n, b,n) c add my contribution do i =1,n b(i,1)= b(i,1)+1 enddo c c need to use fence to assure that coms complete before leaving c Critical Section c call ga_init_fence() call ga_put(g_a, 1,n, n,n, b,n) call ga_fence() call ga_unlock(0) c call my_unlock(g_a) c 333 if(.not.ga_destroy_mutexes()) $ call ga_error('mutex not destroyed',0) call ga_sync() if (me.eq.0) then call ga_get(g_a, 1,n, n,n, b,n) do i =1,n if(b(i,1).ne. nproc)then print *, 'mismatch',b(i,1),nproc call ga_error('fence failed',i) endif enddo write(6,*) write(6,*) ' ga_fence and ga_lock are OK' write(6,*) endif #endif #endif c c if (me.eq.0) then write(6,*) write(6,*) '> checking ga_locate_region' write(6,*) call ffflush(6) endif status = ga_locate_region(g_a, 1, n, 1,n, map,np) found = 0 do j=1,n do i=1,n b(i,j)=-1 enddo enddo #ifndef MIRROR if(me.eq.0)call ga_put(g_a,1,n,1,n,b,n) #else if(iproc.eq.0)call ga_put(g_a,1,n,1,n,b,n) #endif call ga_sync() do k = 1, np if(map(5,k).eq.me)then if(found.eq.1) then write(6,*)'double entry in map for proc ',me call ffflush(6) endif do j= map(3,k), map(4,k) do i= map(1,k), map(2,k) #ifndef MIRROR b(i,j)=1*me #else b(i,j)=1*iproc #endif enddo enddo call ga_put(g_a, map(1,k),map(2,k),map(3,k),map(4,k), & b(map(1,k),map(3,k)),n) found = 1 endif enddo call ga_sync() c do k = 1, np #ifndef MIRROR if(map(5,k).eq.me)then #else if(map(5,k).eq.iproc)then #endif call ga_get(g_a, map(1,k),map(2,k),map(3,k),map(4,k), & a(map(1,k),map(3,k)),n) do j= map(3,k), map(4,k) do i= map(1,k), map(2,k) if(b(i,j).ne.a(i,j)) then write(6,*) & 'proc ',me, 'overlap with ',a(i,j) call ffflush(6) endif enddo enddo endif enddo call ga_sync() c #ifndef MIRROR if(me.eq.0)then #else if(iproc.eq.0)then #endif call ga_get(g_a,1,n,1,n,a,n) do j=1,n do i=1,n if(a(i,j).eq.-1)then write(6,*)'i=',i,' j=',j, ' not assigned ' call ga_error('... exiting ',0) endif enddo enddo endif if (me.eq.0) then write(6,*) write(6,*) ' ga_locate_region is OK' write(6,*) call ffflush(6) endif c c Delete the global array c status = ga_destroy(g_a) c return end c--------------------------------------------------------------------- subroutine check_flt() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer n, m parameter (n =10) parameter (m=2*n) real a(n,n), b(n,n), v(m), w(m) integer iv(m), jv(m) logical status integer g_a, g_b integer iran, i, j, loop, nloop, maxloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, ii, jj, nnodes double precision nwords parameter (maxloop = 100) integer maxproc integer ndim, dims(2), chunk(2), p_mirror parameter (maxproc = 128) double precision crap real x, sum1, sum2 logical found integer lprocs, inode, iproc #ifdef USE_RESTRICTED integer num_rstrctd integer rstrctd_list(maxproc/2) #endif intrinsic int iran(i) = int(drand(0)*real(i)) + 1 nproc = ga_nnodes() me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) nloop = Min(maxloop,n) #ifdef USE_RESTRICTED num_rstrctd = nproc/2 if (num_rstrctd.eq.0) num_rstrctd = 1 do i = 1, num_rstrctd rstrctd_list(i) = (num_rstrctd/2) + i-1 end do #endif c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n #ifndef MIRROR a(i,j) = i-1 + (j-1)*n #else a(i,j) = inode + i-1 + (j-1)*n #endif b(i,j) = -1. enddo enddo c c Create a global array c ndim = 2 dims(1) = n dims(2) = n chunk(1) = 0 chunk(2) = 0 #ifdef NEW_API g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_REAL) call ga_set_array_name(g_a,'a') #ifdef USE_RESTRICTED call ga_set_restricted(g_a, rstrctd_list, num_rstrctd) #endif # ifdef MIRROR write(6,*) 'Getting p_mirror' p_mirror = ga_pgroup_get_mirror() write(6,*) 'Setting proc config' call ga_set_pgroup(g_a,p_mirror) # endif if (.not.ga_allocate(g_a)) then #else # ifndef MIRROR if (.not. ga_create(MT_REAL, n, n, 'a', 0, 0, g_a)) then # else cBJP write(6,*) 'Creating g_a' p_mirror = ga_pgroup_get_mirror() if (.not. nga_create_config(MT_REAL, ndim, dims, 'a', chunk, + p_mirror, g_a)) then cBJP write(6,*) 'Created g_a' # endif #endif write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',0) endif c c check if handle is valid. be quiet unless error C if(.not.ga_valid_handle(g_a)) call ga_error("invalid handle",g_a) c call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) call ga_sync() c c Zero the array c if (me .eq. 0) then write(6,21) 21 format(/'> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_sync() do i = 1, n do j = 1, n if (b(i,j) .ne. 0.0) then write(6,*) ' zero ', me, i, j, b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format(/'> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) c write(6,4) me, ilo, ihi, jlo, jhi c 4 format(' node ',i2,' checking put ',4i4) c call ffflush(6) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo call ga_sync() c if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format(/'> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_rfill(n*n, 0.0, b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) c if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif c sum1 = 0.0d0 do j = jlo, jhi do i = ilo, ihi sum1 = sum1 + b(i,j) if (b(i,j) .ne. a(i,j)) then write(6,*) 'error ', i, j, b(i,j), a(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) endif call ga_sync() c c Each node accumulates into disjoint sections of the array c if (me .eq. 0) then write(6,9) 9 format(/'> Checking accumulate ... ') call ffflush(6) endif call ga_sync() c crap = drand(12345) ! Same seed for each process do j = 1, n do i = 1, n c b(i,j) = real(drand(0)) b(i,j) = i+j enddo enddo c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc c x = real(drand(0)) x = 10. ilo = i ihi = min(i+inc-1, n) if(ihi.eq.n-1)ihi=n c ihi = min(i+inc, n) jlo = j jhi = min(j+inc-1, n) if(jhi.eq.n-1)jhi=n c jhi = min(j+inc-1, n) * call ffflush(6) #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif c print *, me, 'checking accumulate ',ilo,ihi,jlo,jhi,x * 11 format(' node ',i2,' checking accumulate ',4i4) * call ffflush(6) call ga_acc(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n, x) endif ij = ij + 1 c c Each process applies all updates to its local copy c do jj = jlo, jhi do ii = ilo, ihi a(ii,jj) = a(ii,jj) + x * b(ii,jj) enddo enddo enddo enddo call ga_sync() c c All nodes check all of a call ga_get(g_a, 1, n, 1, n, b, n) c do j = 1, n do i = 1, n if(MISMATCHF(b(i,j),a(i,j)))then write(6,*) ' acc ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' disjoint ga_acc is OK' write(6,*) endif c c overlapping accumulate call ga_sync() #ifndef MIRROR if (.not. ga_create(MT_REAL, n, n, 'b', 0, 0, g_b)) then #else if (.not. nga_create_config(MT_REAL, ndim, dims, 'b', chunk, + p_mirror, g_b)) then #endif call ga_error('ga_create failed for second array ',0) endif c call ga_zero(g_b) call ga_acc(g_b, n/2, n/2, n/2, n/2, 1.0, 1, 1.0) call ga_sync() if (me.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, b(1,1), 1) #ifndef MIRROR x = abs(b(1,1) -1*nproc) #else x = abs(b(1,1) -1*lprocs) #endif if(x.gt. 1e-10)then #ifndef MIRROR write(6,*)'val=',b(1,1),' expected=',nproc, x #else write(6,*)'val=',b(1,1),' expected=',lprocs, x #endif call ga_error('overlapping accumulate failed',0) endif write(6,*) write(6,*) ' overlapping ga_acc is OK' write(6,*) endif c c Check the ga_add function c if (me .eq. 0) then write(6,91) 91 format(/'> Checking add ...') call ffflush(6) endif c crap = drand(12345) ! Everyone has same seed do j = 1, n do i = 1, n b(i,j) = real(drand(0)*real(i)) + 1 a(i,j) = 0.1*a(i,j) + 0.9*b(i,j) enddo enddo #ifndef MIRROR if (me.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) #else if (iproc.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) #endif call ga_add(0.1, g_a, 0.9, g_b, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if(MISMATCHF(b(i,j), a(i,j)))then write(6,*) ' add ', me, i, j, a(i,j), b(i,j) call ffflush(6) CCC call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' add is OK ' write(6,*) endif call ga_sync() c c Check the sdot function c if (me .eq. 0) then write(6,19) 19 format(/'> Checking sdot ...') call ffflush(6) endif crap = drand(12345) ! Everyone has same seed sum1 = 0.0 do j = 1, n do i = 1, n b(i,j) = drand(0) sum1 = sum1 + a(i,j)*b(i,j) enddo enddo #ifndef MIRROR if (me.eq.0) then #else if (iproc.eq.0) then #endif call ga_put(g_b, 1, n, 1, n, b, n) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() sum2 = ga_sdot(g_a,g_b) if(MISMATCHF(sum1, sum2))then write(6,*) ' fdot wrong ', sum1, sum2 call ffflush(6) call ga_error('... exiting ',0) else if (me.eq.0) then write(6,*) write(6,*) ' sdot is OK ' write(6,*) endif c c Check the ga_scale function c if (me .eq. 0) then write(6,92) 92 format(/'> Checking scale ...') call ffflush(6) endif call ga_scale(g_a, 0.123) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n a(i,j) = a(i,j)*0.123 if (MISMATCHF(b(i,j), a(i,j)))then write(6,*) ' dscal ', me, i, j, a(i,j), b(i,j) call ffflush(6) CCC call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' scale is OK ' write(6,*) endif c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c call ga_sync() if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_sync() #ifndef MIRROR itmp = iran(nproc)-1 if(me.eq.itmp) then #else itmp = iran(lprocs)-1 if(iproc.eq.itmp) then #endif do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo call ga_gather(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',0) endif enddo endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_sync() #ifndef MIRROR if(me.eq.iran(ga_nnodes())-1) then #else if(iproc.eq.iran(lprocs)-1) then #endif do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo c v(loop) = DSIN(a(ilo,jlo)+b(ilo,jlo)) v(loop) = 1.0 *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) c if(v(loop) .ne. w(loop))then if(w(loop) .ne. 1.0 *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', 1.0 *(ilo+jlo) call ffflush(6) call ga_error('... exiting ',0) endif enddo endif call ga_sync() enddo c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c call ga_sync() c c scatter-acc available in GA ver. 3.0 #ifdef GA3 if (me.eq.0) then write(6,*) write(6,*) ' checking scatter-accumulate' write(6,*) endif c crap = drand(1234) call ga_zero(g_a) c do j = 1, n do i = 1, n b(i,j) =0. enddo enddo c x = .1d0 ii =n do jj = 1,1 call ga_sync() do loop = 1, ii c generate unique i,j pairs 10 continue i = iran(n) j=iran(n) if (found(i,j, iv, jv, loop-1) ) goto 10 iv(loop) = i jv(loop) = j v(loop) = 1.0 *(i+j) #ifndef MIRROR b(i,j) = b(i,j) + nproc*x*v(loop) ! update local ref. array #else b(i,j) = b(i,j) + lprocs*x*v(loop) ! update local ref. array #endif enddo call ga_scatter_acc(g_a,v,iv,jv, ii,x) c call ga_sync() c c check the result c call ga_get(g_a, 1, n, 1,n, a, n) do loop = 1,ii i = iv(loop) j = jv(loop) if(MISMATCHF(a(i,j),b(i,j)))then print *,'Error',i,j,loop,a(i,j),'expected=',b(i,j) * if(me.eq.0)then * do ii=1,loop * print *,'element',ii, iv(ii),jv(ii) * enddo * endif call ga_error('scatter-acc error in trial ',jj) endif enddo call ga_sync() enddo call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' scatter-accumulate is OK' write(6,*) endif #endif c c Delete the global array c status = ga_destroy(g_a) c return end c_____________________________________________________________ subroutine check_wrappers implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" double precision sum integer isum, ibuf(2) integer me, nproc real fsum c nproc = ga_nnodes() me = ga_nodeid() c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_igop' write(6,*) call ffflush(6) endif ibuf(1) = 1 ibuf(2) = me call ga_igop(10000, ibuf, 2, '+') if(ibuf(1).ne.nproc)then call ga_error('ga_igop error',isum) endif if(ibuf(2).ne.((nproc-1)*nproc/2))then call ga_error('ga_igop error -2',isum) endif call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' ga_igop is OK' write(6,*) endif call ga_sync() c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_dgop' write(6,*) call ffflush(6) endif sum = 1d0 * me call ga_dgop(10000, sum, 1, '+') if(Int(sum).ne.((nproc-1)*nproc/2))then call ga_error('ga_dgop error',Int(sum)) endif call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' ga_dgop is OK' write(6,*) endif c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_sgop' write(6,*) call ffflush(6) endif fsum = 1.0 * me call ga_sgop(10000, fsum, 1, '+') if(Int(sum).ne.((nproc-1)*nproc/2))then call ga_error('ga_fgop error',Int(sum)) endif call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' ga_sgop is OK' write(6,*) endif c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_brdcst' write(6,*) call ffflush(6) endif if(me.eq.nproc-1)then ibuf(1) = me ibuf(2) = nproc endif call ga_brdcst(1000,ibuf,util_mitob(2),nproc-1) if(ibuf(1).ne.nproc-1)call ga_error('ibuf(1) error',ibuf(1)) if(ibuf(2).ne.nproc)call ga_error('ibuf(2) error',ibuf(2)) call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> ga_brdcst is OK ' write(6,*) call ffflush(6) endif call ga_sync() return end subroutine check_mem(mem_size) implicit none integer mem_size #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,nmax,left,need, me,procs,g_a, g_b logical status c if(.not. ga_memory_limited())return me = ga_nodeid() procs = ga_nnodes() nmax = int(dsqrt(dble(mem_size/util_mitob(1)))) left = mem_size/procs - ga_inquire_memory() n = nmax/2 need = util_mdtob(n*n)/procs c if(me.eq.0)then write(6,*)' ' if(ga_uses_ma())then write(6,*)' CHECKING GA MEMORY RESTRICTIONS (MA used)' else write(6,*)' CHECKING GA MEMORY RESTRICTIONS (MA not used)' endif write(6,*)' ' write(6,*)' ' call print_mem_info(n,left, need, mem_size/procs) endif c status = ga_create(MT_DBL, n, n, 'a', 0, 0, g_a) c if(me.eq.0) then if(status) then write(6,*) ' success' else write(6,*) ' failure' endif call ffflush(6) endif c n = nmax left = mem_size/procs - ga_inquire_memory() need = util_mdtob(n*n)/procs if(me.eq.0)then call print_mem_info(n,left, need, mem_size/procs) endif c status = ga_create(MT_DBL, n, n, 'b', 0, 0, g_b) c if(me.eq.0) then if(status) then write(6,*) ' success' else write(6,*) ' failure' endif write(6,*)' ' write(6,*)' ' call ffflush(6) endif status = ga_destroy(g_a) end subroutine print_mem_info(n,left, need, total) implicit none integer n,left, need, total c write(6,*)' ' if(left - need .ge. 0) then write(6,1)n,n 1 format('> Creating array ',i4,' by ',i4,' -- should succeed') else write(6,2)n,n 2 format('> Creating array ',i4,' by ',i4,' -- SHOULD FAIL') endif write(6,3) need, left, total 3 format(' (need ',i7,' and ',i7,' out of ',i7,' bytes are left)') write(6,*)' ' call ffflush(6) c end subroutine my_lock(g_b) implicit none #include "global.fh" integer g_b, val, flag, i logical first_time double precision dummy common /lock/ val common /dum/ dummy data first_time /.true./ c c this awkward initialization is to avoid a weird problem C with block data on SUN if(first_time)then first_time = .false. dummy = .0 endif c val = ga_read_inc(g_b,1,1, 1) 10 call ga_get(g_b, 2,2,1,1, flag, 1) if(flag.eq.val) return c c to reduce memory stress, wait a while before retrying do i = 1, 100 dummy = dummy + .1 enddo goto 10 end subroutine my_unlock(g_b) implicit none #include "global.fh" integer g_b, val common /lock/ val c call ga_put(g_b, 2,2,1,1, val+1, 1) end logical function found(i,j, iv, jv, n) integer n integer i,j, iv(n), jv(n) integer loop found = .false. do loop = 1, n if(i .eq. iv(loop) .and. j .eq.jv(loop))then found = .true. goto 99 endif enddo 99 continue return end subroutine proc_remap() implicit none #include "global.fh" integer proc(100),nproc,i nproc = ga_nnodes() if(nproc.gt.100) $ call ga_error("remap requires<=100 processes",nproc) do i = 1, nproc proc(i) = nproc-i enddo c call ga_register_proclist(proc,nproc) end subroutine util_rfill(n,val,a,ia) implicit none real a(*), val integer n, ia, i c c initialise real array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end subroutine util_dfill(n,val,a,ia) implicit none double precision a(*), val integer n, ia, i c c initialise double precision array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end subroutine util_ifill(n,val,a,ia) implicit none integer n, ia, i, a(*),val c c initialise integer array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end subroutine util_qfill(n,val,a,ia) implicit none double complex a(*), val integer n, ia, i c c initialise double complex array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end ga-5.9.2/global/testing/pgtestmatmult.F000066400000000000000000000245511500715745200200710ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif program ga_test c $Id: pgtestmatmult.F,v 1.2.6.1 2007-04-25 21:49:59 manoj Exp $ c c test ga_dgemm c compile with make FLD_REN="optimized blas" testmatmult.x c Note: - change nummax for large arrays c - turn off "verify" for large arrays due to memory c limitations, as verify=TRUE for large arrays produces c segfault, dumps core,or any crap. c c implicit none integer num_m,num_n,num_k,nummax,howmany,ntrans parameter (nummax=1024,howmany=2,ntrans=4) integer num1 parameter(num1=nummax) integer i,ii real*8 h0(num1*num1) integer g_c,g_b,g_a real*8 a real*8 t1,mf,avg_t(ntrans),avg_mf(ntrans) integer itime,ntimes,me integer nums_m(howmany),nums_n(howmany),nums_k(howmany) character*1 transa(ntrans),transb(ntrans),ta,tb real *8 tmpa(nummax,nummax), tmpb(nummax,nummax) real *8 tmpc(nummax,nummax) logical verify,status integer grp_me, nproc, nprocs, proc_group(0:100), proclist(100) data transa/'n','t','n','t'/ data transb/'n','n','t','t'/ data nums_m/512,1024/ data nums_n/512,1024/ data nums_k/512,1024/ #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" #include "mp3.fh" if (.not.ma_init(MT_DBL,1,20000000)) then call ga_error('failed: ma_init(MT_DBL,1,20000000)',10) endif status = ma_set_auto_verify(.true.) ! status = ma_set_hard_fail(.true.) ! status = ma_set_error_print(.true.) call ga_initialize() me = ga_nodeid() nproc = ga_nnodes() if(nproc .ne. 8) then write(*,*) 'nproc =', nproc call ga_error('failed: need exactly 8 processors',10) endif call ga_sync() proclist(1) = 0 proclist(2) = 1 proclist(3) = 2 proclist(4) = 3 nprocs = 4 proc_group(0) = ga_pgroup_create(proclist, nprocs) proclist(1) = 4 proclist(2) = 5 proclist(3) = 6 proclist(4) = 7 nprocs = 4 proc_group(1) = ga_pgroup_create(proclist, nprocs) if (me.lt.4) then call ga_pgroup_set_default(proc_group(0)) endif if (me.ge.4) then call ga_pgroup_set_default(proc_group(1)) endif c get new proc ids for your default group me = ga_nodeid() nproc = ga_nnodes() ii = 0 do i = 1, num1*num1 ii = ii + 1 if(ii.gt.num1) then ii = 0 endif h0(i) = ii enddo c c Compute times assuming 500 mflops and 5 second target time c c ntimes = max(3.0d0,5.0d0/(4.0d-9*num**3)) ntimes = 5 verify = .TRUE. ! to verify ga_dgemm c verify = .FALSE. ! to verify ga_dgemm #if 1 do ii=1,howmany num_m = nums_m(ii) num_n = nums_n(ii) num_k = nums_k(ii) a = 0.5d0/(num_m*num_n) if(num_m.gt.nummax .OR. num_n.gt.nummax .OR. & num_k.gt.nummax) then call ga_error('Insufficient memory: check nummax', 0) endif c c if (.not.ga_create(MT_DBL,num_m,num_n,'g_c', & 0,0,g_c)) then call ga_error('failed: create g_c',20) endif if (.not.ga_create(MT_DBL,num_k,num_n,'g_b', & 0,0,g_b)) then call ga_error('failed: create g_b',30) endif if (.not.ga_create(MT_DBL,num_m,num_k,'g_a', & 0,0,g_a)) then call ga_error('failed: create g_a',40) endif c c Initialize matrices A and B c call load_ga_from_square(g_a,num_m,h0,num1) c call load_ga_from_square(g_b,num_m,h0,num1) c if(me.eq.0) then call load_ga(g_a, h0, num_m, num_k) call load_ga(g_b, h0, num_k, num_n) endif call ga_zero(g_c) call ga_sync() if (ga_nodeid().eq.0) then write(*,*) ! for new line write(*,*) 'Matrix Multiplication C = A[', num_m, ',', . num_k, '] x B[', num_k, ',', num_n, ']' write(*,*) ! for new line call ffflush(6) endif do i = 1, ntrans avg_t(i) = 0.0d0 avg_mf(i) = 0.0d0 enddo do itime = 1, ntimes do i = 1, ntrans call ga_sync() ta = transa(i) tb = transb(i) t1 = util_timer() call ga_dgemm(ta,tb,num_m,num_n,num_k, & 1.0d0, g_a, g_b, 0.0d0, g_c) t1 = util_timer() - t1 if (me.eq.0) then mf = 2d0*num_m*num_n*num_k/t1*1d-6/ga_nnodes() avg_t(i) = avg_t(i)+t1 avg_mf(i) = avg_mf(i) + mf write(6,200) ' Run#', itime, t1, mf, ta, tb call ffflush(6) if (verify .AND. itime.eq.1) then call verify_ga_dgemm(ta, tb, num_m, num_n, num_k, & 1.0d0, g_a, g_b, 0.0d0, g_c,tmpa,tmpb,tmpc) endif endif enddo enddo if (ga_nodeid().eq.0) then write(*,*) ! for new line do i = 1, ntrans write(6,200) 'Average:',0,avg_t(i)/ntimes, . avg_mf(i)/ntimes,transa(i),transb(i) enddo if(verify) write(*,*) 'All ga_dgemms are verified...O.K.' write(*,*) ! for new line call ffflush(6) endif c c call ga_print(g_a) c call ga_print(g_b) c call ga_print(g_c) c if (.not.ga_destroy(g_c)) then call ga_error('failed: destroy g_c',20) endif if (.not.ga_destroy(g_b)) then call ga_error('failed: destroy g_b',30) endif if (.not.ga_destroy(g_a)) then call ga_error('failed: destroy g_a',40) endif enddo #endif 200 format(a15, i2, ': ', e12.4, ' seconds ',f12.1, . ' mflops/proc ', 3a2) call ga_pgroup_set_default(ga_pgroup_get_world()) call ga_sync() call ga_terminate call MP_FINALIZE() c end c c----------------------------------------------------------------------- c Verify for correctness. Process 0 computes BLAS dgemm c locally. For larger arrays, disbale this test as memory c might not be sufficient c subroutine verify_ga_dgemm(xt1, xt2, num_m, num_n, num_k, $ alpha, g_a, g_b, beta, g_c, tmpa, tmpb, tmpc) implicit none character *1 xt1, xt2 integer num_m, num_n, num_k, g_a, g_b, g_c double precision alpha, beta real *8 tmpa(num_m,num_k), tmpb(num_k,num_n), tmpc(num_m,num_n) integer i,j,type,dim1, dim2 real *8 abs_value #include "mafdecls.fh" #include "testutil.fh" #include "galinalg.fh" do i = 1,num_n do j = 1, num_m tmpc(j,i) = -1.0 tmpa(j,i) = -2.0 enddo enddo call ga_inquire(g_a, type, dim1, dim2) call ga_get(g_a, 1, dim1, 1, dim2, tmpa, dim1) call ga_inquire(g_b, type, dim1, dim2) call ga_get(g_b, 1, dim1, 1, dim2, tmpb, dim1) c compute dgemm sequentially call dgemm(xt1, xt2, num_m, num_n, num_k, alpha, & tmpa, num_m, tmpb, num_k, beta, tmpc, num_m) c after computing c locally, verify it with the values in g_c call ga_inquire(g_c, type, dim1, dim2) call ga_get(g_c, 1, dim1, 1, dim2, tmpa, dim1) do i = 1,num_n do j = 1, num_m abs_value = abs(tmpc(j,i)-tmpa(j,i)) if(abs_value .gt. 1.0 .OR. abs_value .lt. -1.0) then write(*,*) 'Values are = ', tmpc(j,i), tmpa(j,i) write(*,*) 'Values are = ', abs(tmpc(j,i)-tmpa(j,i)), . abs_value call ffflush(6) call ga_error('verify ga_dgemm failed', 0) endif enddo enddo return end c c----------------------------------------------------------------------- c called by process '0' (or your master process ) c subroutine load_ga(handle,f, dim1,dim2) implicit none integer handle integer dim1,dim2,i real*8 f(dim1,dim2) #include "mafdecls.fh" if(dim1.le.0)return if(dim2.le.0)return call ga_put(handle, 1, dim1, 1, dim2, f, dim1) return end c c----------------------------------------------------------------------- c must be called by all processors, if you need to fillup the c entire array c subroutine load_ga_from_square(handle,num,f,ndim) implicit none integer handle, memhandle integer num,ndim real*8 f(ndim,ndim) integer ilo, ihi, jlo, jhi, nx, ny, ibuff integer ga_nodeid, i1, i2, i, j, ix, jx #include "mafdecls.fh" call ga_distribution(handle, ga_nodeid(), ilo, ihi, jlo, jhi) if(ihi.le.0)return if(jhi.le.0)return c nx = ihi - ilo + 1 c ny = jhi - jlo + 1 do i = ilo,ihi,ndim do j = jlo,jhi,ndim call ga_put(handle,i,min(ihi,i+ndim),j,min(jhi,j+ndim), & f,ndim) enddo enddo return end c c----------------------------------------------------------------------- c must be called by all processors, if you need to fillup the c entire array c subroutine load_ga_from_triangle(handle,f,ndim) implicit none integer handle, memhandle real*8 f(*) integer ndim integer ilo, ihi, jlo, jhi, nx, ny, ibuff integer ga_nodeid, i1, i2, i, j, ix, jx #include "mafdecls.fh" call ga_distribution(handle, ga_nodeid(), ilo, ihi, jlo, jhi) if(ihi.le.0)return if(jhi.le.0)return nx = ihi - ilo + 1 ny = jhi - jlo + 1 if (.not.ma_alloc_get(MT_DBL,nx*ny,'flap',memhandle,ibuff)) then call ga_error('failed: allocate triangle',100) endif do i = 1,nx do j = 1,ny ix = i + ilo - 1 jx = j + jlo - 1 i1 = min(ix,jx) i2 = max(ix,jx) dbl_mb(ibuff + nx*(j-1) + (i-1) ) = f(i2*(i2-1)/2 + i1) enddo enddo call ga_put(handle,ilo,ihi,jlo,jhi, & dbl_mb(ibuff),nx) if (.not.ma_free_heap(memhandle)) then call ga_error('failed: free triangle',100) endif return end ga-5.9.2/global/testing/print.c000066400000000000000000000024631500715745200163460ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDLIB_H # include #endif #include #include #include int main(int argc, char **argv) { int size_dst = 15; int g_a = 0; int I_NEG_ONE = -1; long L_NEG_ONE = -1; long long LL_NEG_ONE = -1; int FIVE = 5; int TEN = 10; int lo; int hi; int *ptr; int i; MP_INIT(argc,argv); GA_INIT(argc,argv); for (i=0; i<3; ++i) { if (0 == i) { g_a = NGA_Create(C_INT, 1, &size_dst, "dst", NULL); GA_Fill(g_a, &I_NEG_ONE); } else if (1 == i) { g_a = NGA_Create(C_LONG, 1, &size_dst, "dst", NULL); GA_Fill(g_a, &L_NEG_ONE); } else if (2 == i) { g_a = NGA_Create(C_LONGLONG, 1, &size_dst, "dst", NULL); GA_Fill(g_a, &LL_NEG_ONE); } GA_Sync(); GA_Print(g_a); NGA_Print_patch(g_a, &FIVE, &TEN, 0); NGA_Print_patch(g_a, &FIVE, &TEN, 1); NGA_Distribution(g_a, GA_Nodeid(), &lo, &hi); NGA_Access(g_a, &lo, &hi, &ptr, NULL); printf("[%d] (%d)=%d\n", GA_Nodeid(), lo, *ptr); NGA_Release(g_a, &lo, &hi); } if (GA_Nodeid() == 0) printf("All tests successful\n"); GA_Terminate(); MP_FINALIZE(); return 0; } ga-5.9.2/global/testing/rand.c000066400000000000000000000033661500715745200161410ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #include "ga.h" #include "macdecls.h" #include "mp3.h" #define N 100 /* dimension of array */ #define NCNT 1000000 /* number of random numbers generated on each process */ int main(int argc, char **argv) { int heap=20000, stack=20000; int me, nproc; int i, idx; double x, dx, norm; double min, max; int *bins; char op[2]; MP_INIT(argc,argv); GA_INIT(argc,argv); /* initialize GA */ me=GA_Nodeid(); nproc=GA_Nnodes(); if(me==0) { if(GA_Uses_fapi())GA_Error("Program runs with C array API only",1); printf("Using %ld processes\n",(long)nproc); fflush(stdout); } heap /= nproc; stack /= nproc; if(! MA_init(MT_F_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ NGA_Rand(332457+me); bins = (int*)malloc(N*sizeof(int)); for (i=0; i= N) GA_Error("Index out of bounds",idx); bins[idx]++; } strcpy(op,"+"); GA_Igop(bins,N,op); norm = ((double)(NCNT*nproc)); if (me == 0) { min = ((double)N); max = 0.0; for (i=0; imax) max = x; printf("%f %f\n",(((double)i)+0.5)*dx,x); } printf("Min: %f Max: %f\n",min,max); } free(bins); GA_Terminate(); MP_FINALIZE(); return 0; } ga-5.9.2/global/testing/random.F000066400000000000000000000101701500715745200164270ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif program main implicit none #include "mafdecls.fh" #include "global.fh" integer dim, minutes integer heap, stack logical status integer proc, me c c**** You can change dimension of the array and duration of the test here parameter (dim=500, minutes =90) c #include "mp3.fh" c c*** Initialize GA call ga_initialize() c proc = ga_nnodes() heap = dim*dim/proc stack= heap c status = ma_init(MT_DBL, stack, heap) if (.not. status) call ga_error( 'ma_init failed',stack+heap) c me = ga_nodeid() if(me.eq.0)then print *, 'Testing random gets and puts' print *, ' array: ',dim,' x ',dim print *, ' using ',proc, ' process(es)' print *, ' test should run for ',minutes,' minutes' call ffflush(6) endif c call check_dbl(dim, minutes) c if(me.eq.0)then print *, 'Test completed succesfuly' endif c if(ga_nodeid().eq.0)call ga_print_stats() call ga_terminate() call MP_FINALIZE() end subroutine check_dbl(dim, minutes) implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n parameter (n = 10) integer dim, minutes double precision a(n,n) double precision t0, elapsed integer g_a integer index, ld integer iran, i,j, loop, maxloop, ilo, ihi, jlo, jhi, range integer nproc, me logical status c c**** maxloop determines number of puts/gest done before checking the clock c parameter (maxloop = 100000) double precision crap iran(i) = int(drand(0)*real(i-1)) + 1 c nproc = ga_nnodes() me = ga_nodeid() crap = drand(real(me)) !different seed for each process if(n .gt. dim) call ga_error('insufficient dimension',dim) c status = ga_create(MT_DBL, dim, dim, 'a', 0, 0, g_a) if (.not. status) then write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',0) endif c c initialize array in place call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) call ga_access(g_a, ilo,ihi,jlo,jhi, index, ld) * print *, 'DBL_MB=', DBL_MB(1), index call fill_local(DBL_MB(index), ihi-ilo+1, jhi-jlo+1, ilo, jlo, ld) c call ga_sync() t0 = util_timer() c if (me .eq. 0) then write(6,21) 21 format(/'> Start ... ') call ffflush(6) endif c c range = dim - n -1 100 continue do loop = 1, maxloop c c always get 100x100 patches ilo = iran(range) jlo = iran(range) ihi = ilo+n-1 jhi = jlo+n-1 c call ga_get(g_a, ilo, ihi, jlo, jhi, a, n) c c check if data OK call check_data(a,n,n, ilo, jlo, n) c c copy the data back call ga_put(g_a, ilo, ihi, jlo, jhi, a, n) #ifdef DEBUG print *, me, 'OK', ilo, ihi, jlo, jhi call ffflush(6) #endif enddo elapsed = util_timer() -t0 if (me.eq.0)then print *, int(100* elapsed/(minutes*60)),'% done' call ffflush(6) endif if(elapsed .lt. real(minutes * 60)) goto 100 c call ga_sync() c if (me.eq.0) then write(6,*) write(6,*) ' everything looks OK' write(6,*) call ffflush(6) endif call ga_sync() status = ga_destroy(g_a) end subroutine fill_local(a, n,m, x, y , ld) implicit none integer ld, n,m, x,y double precision a(ld,*) integer i,j c do j=1,m do i=1,n a(i,j)= real(x+y+i+j-2) enddo enddo end subroutine check_data(a,n,m, x,y, ld) implicit none #include "global.fh" integer ld, n,m, x,y double precision a(ld,*) integer i,j c do j=1,m do i=1,n if(a(i,j) .ne. real(x+y+i+j-2))then print *, 'error:',i+x-1, j+y-1, a(i,j) call ga_error("failed",1) endif enddo enddo end ga-5.9.2/global/testing/read_only.c000066400000000000000000000150431500715745200171640ustar00rootroot00000000000000#include #include #include #include "mpi.h" #include "ga.h" #include "macdecls.h" #define DEFAULT_DIM 4096 #define MAX_LOOP 20 /* Convenience function to check that something is true on all processors */ int trueEverywhere(int flag) { int tflag, nprocs; if (flag) tflag = 1; else tflag = 0; nprocs = GA_Nnodes(); GA_Igop(&tflag,1,"+"); if (nprocs == tflag) return 1; return 0; } /* Return box dims that have been increased by a factor of 2 */ int nextBlock(int *ix, int *iy, int dims[]) { if (*ix > *iy) { *iy *= 2; } else { *ix *= 2; } if ((*ix)>=dims[0] || (*iy) > dims[1]) return 0; return 1; } /* Function to print out timing statistics */ void printTimes(double g_time, int g_ntime, int g_elems, int size[]) { int me = GA_Nodeid(); int nproc = GA_Nnodes(); double time; int ntime; long nelems; long lone = 1; int one = 1; double bandwdth; double optime; int bytes = sizeof(int)*size[0]*size[1]; NGA_Get(g_time,&me,&me,&time,&one); NGA_Get(g_ntime,&me,&me,&ntime,&one); NGA_Get(g_elems,&me,&me,&nelems,&one); GA_Dgop(&time,one,"+"); GA_Igop(&ntime,one,"+"); GA_Lgop(&nelems,one,"+"); nelems *= sizeof(int); bandwdth = ((double)nelems)/time; bandwdth /= 1.0e6; if (me==0) { printf("%10d %10d %10d %10d %16.6e\n",bytes,size[0],size[1],ntime,bandwdth); } } /* Generate random block */ void getRandomBlock( int ndim, int dims[], int blocksize[], int lo[], int hi[]) { int toss; int d; for (d=0; d= 3) { x = atoi(argv[1]); y = atoi(argv[2]); } MPI_Init(&argc, &argv); GA_Initialize(); nproc = GA_Nnodes(); me = GA_Nodeid(); i = GA_Cluster_nprocs(GA_Cluster_nodeid()); /* Find processor grid dimensions and processor grid coordinates */ if (me==0) { printf("\nTest running on %d processors with %d processes per node\n",nproc,i); printf("\nArray dimension is %d X %d\n",x,y); } dims[0] = x; dims[1] = y; /* Create GA and set all elements to zero */ g_src = NGA_Create(C_INT, 2, dims, "source", NULL); g_time = NGA_Create(C_DBL, 1, &nproc, "times", &one); g_ntime = NGA_Create(C_INT, 1, &nproc, "ntimes", &one); g_elems = NGA_Create(C_LONG, 1, &nproc, "nelems", &one); GA_Zero(g_src); /* Fill the global array with unique values */ NGA_Distribution(g_src,me,lo,hi); NGA_Access(g_src,lo,hi,&ptr,ld); for (i=lo[0]; i<=hi[0]; i++) { for (j=lo[1]; j<=hi[1]; j++) { idx = i*y+j; iloc = (i-lo[0])*ld[0]+(j-lo[1]); ptr[iloc] = idx; } } NGA_Release(g_src,lo,hi); GA_Sync(); /* Array is ready for reading. Set read-only property */ NGA_Set_property(g_src,"read_only"); if (me == 0) { printf("\nPerformance results for read-only array\n"); } ok = 1; nextOK = 1; ix = 1; iy = 1; if (me == 0) { printf("\n Bytes I-Dim J-Dim Transfers Bandwidth (MB/s)\n\n"); } while (nextOK) { /* Initialize counters and statistics */ icnt = 0; GA_Zero(g_time); GA_Zero(g_ntime); GA_Zero(g_elems); /* Read random blocks from array */ size[0] = ix; size[1] = iy; while (icnt < MAX_LOOP) { /* Get random block and copy it to local array */ getRandomBlock(ndim, dims, size, lo, hi); nelems = (long)((hi[0]-lo[0]+1)*(hi[1]-lo[1]+1)); ptr = (int*)malloc(nelems*sizeof(int)); ld[0] = (hi[1]-lo[1]+1); delta_t = GA_Wtime(); NGA_Get(g_src,lo,hi,ptr,ld); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_time,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_ntime,&me,&me,&one,&one,&one); NGA_Acc(g_elems,&me,&me,&nelems,&one,&lone); /* Check that values are okay */ for (i=lo[0]; i<=hi[0]; i++) { for (j=lo[1]; j<=hi[1]; j++) { idx = i*y+j; iloc = (i-lo[0])*ld[0]+(j-lo[1]); if (ptr[iloc] != idx) { ok = 0; printf("p[%d] expected: %d actual: %d\n",me,idx,ptr[iloc]); } } } free(ptr); icnt++; } printTimes(g_time, g_ntime, g_elems, size); nextOK = nextBlock(&ix, &iy, dims); } if (trueEverywhere(ok)) { if (me == 0) { printf("\nRead-only test succeeded\n"); } } else if (me == 0) { printf("\nRead-only test FAILED\n"); } /* Unset read-only property */ NGA_Unset_property(g_src); if (me == 0) { printf("\nPerformance results for regular array\n"); } ok = 1; nextOK = 1; ix = 1; iy = 1; if (me == 0) { printf("\n Bytes I-Dim J-Dim Transfers Bandwidth (MB/s)\n\n"); } while (nextOK) { /* Initialize counters and statistics */ icnt = 0; GA_Zero(g_time); GA_Zero(g_ntime); GA_Zero(g_elems); /* Read random blocks from array */ size[0] = ix; size[1] = iy; while (icnt < MAX_LOOP) { /* Get random block and copy it to local array */ getRandomBlock(ndim, dims, size, lo, hi); nelems = (long)((hi[0]-lo[0]+1)*(hi[1]-lo[1]+1)); ptr = (int*)malloc(nelems*sizeof(int)); ld[0] = (hi[1]-lo[1]+1); delta_t = GA_Wtime(); NGA_Get(g_src,lo,hi,ptr,ld); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_time,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_ntime,&me,&me,&one,&one,&one); NGA_Acc(g_elems,&me,&me,&nelems,&one,&lone); /* Check that values are okay */ for (i=lo[0]; i<=hi[0]; i++) { for (j=lo[1]; j<=hi[1]; j++) { idx = i*y+j; iloc = (i-lo[0])*ld[0]+(j-lo[1]); if (ptr[iloc] != idx) { ok = 0; printf("p[%d] expected: %d actual: %d\n",me,idx,ptr[iloc]); } } } free(ptr); icnt++; } printTimes(g_time, g_ntime, g_elems, size); nextOK = nextBlock(&ix, &iy, dims); } if (trueEverywhere(ok)) { if (me == 0) { printf("\nRegular array test succeeded\n\n"); } } else if (me == 0) { printf("\nRegular array test FAILED\n"); } GA_Destroy(g_src); GA_Destroy(g_time); GA_Destroy(g_ntime); GA_Destroy(g_elems); GA_Terminate(); MPI_Finalize(); return return_code; } ga-5.9.2/global/testing/scan.F000066400000000000000000000546501500715745200161060ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif C C Test the minval, minloc, maxval, maxloc, and enum functions in GA. C program main implicit none #include "mafdecls.fh" #include "global.fh" integer heap, stack, fudge, ma_heap, me, nproc logical status parameter (heap=200*200*4, fudge=100, stack=200*200) c c*** Intitialize a message passing library c #include "mp3.fh" c c*** Initialize GA c c There are 2 choices: ga_initialize or ga_initialize_ltd. c In the first case, there is no explicit limit on memory usage. c In the second, user can set limit (per processor) in bytes. c call ga_initialize() nproc = ga_nnodes() me = ga_nodeid() c we can also use GA_set_memory_limit BEFORE first ga_create call c if(ga_nodeid().eq.0)then print *,' GA initialized ' call ffflush(6) endif c c*** Initialize the MA package c MA must be initialized before any global array is allocated c ma_heap = heap/nproc + fudge status = ma_init(MT_DCPL, stack, ma_heap) if (.not. status) call ga_error('ma_init failed',-1) c if(me.eq.0)then print *, 'using ', nproc, ' process(es)' call ffflush(6) endif c call test_nga_pack() ! Test PACK/UNPACK call test_nga_scan() ! Test SCAN_COPY/SCAN_ADD c if(me.eq.0) call ga_print_stats() c c*** Tidy up the GA package c call ga_terminate() c c*** Tidy up after message-passing library c call MP_FINALIZE() c end subroutine test_nga_pack() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" C integer g_ielm1, g_result1 ! handles to global arrays integer g_ielm2, g_sbit2, g_src2, g_sink2, g_result2 ! handles to global arrays integer g_irowmat, g_icolmat ! handles to global arrays integer g_velm4, g_velm5 ! handles to global arrays C integer ilo, ihi, num integer ilo4, ihi4, num4 integer i, it, count integer me, nproc ! my processor & number of procs integer ndim,dims(1),chunk(1) C integer nelements_max, nen_max parameter (nelements_max=100000, nen_max=4) integer ipack(nelements_max), * jpack(nelements_max), * kpack(nelements_max), * ielm1(nelements_max), * ielm4(nen_max*nelements_max) double precision velm4(nen_max*nelements_max), * velm5(nen_max*nelements_max) c integer ilocmax, ilocmin integer ilocmax_ga, ilocmin_ga integer imax_ga, imin_ga C integer nelements, nen integer isum, icount, itoff double precision result integer i1, i2, one C c c*** check parallel environment me = ga_nodeid() nproc = ga_nnodes() if(me.eq.0)then print *, 'testing pack/unpack' call ffflush(6) endif c nelements=20000 nen=4 one = 1 c c*** create a global 1-D array ndim=1 dims(1)=nen*nelements chunk(1)=min(1,dims(1)/nproc) if (.not. nga_create(MT_INT, ndim, dims, 'ielm1', chunk, $ g_ielm1)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'result1', chunk, $ g_result1)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'sbit2', chunk, $ g_sbit2)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'src2', chunk, $ g_src2)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'sink2', chunk, $ g_sink2)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'result2', chunk, $ g_result2)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'irowmat', chunk, $ g_irowmat)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'icolmat', chunk, $ g_icolmat)) $ call ga_error(' ga_create failed ',0) C if (.not. nga_create(MT_DBL, ndim, dims, 'velm4', chunk, $ g_velm4)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_DBL, ndim, dims, 'velm5', chunk, $ g_velm5)) $ call ga_error(' ga_create failed ',0) c c Enumerate the sbit arrary to get a sequential vector. call ga_fill(g_ielm1,0) call ga_patch_enum(g_ielm1,1,nelements,1,1) c *** ielm1: 1 2 3 4 5 ... nelements ... 0 0 0 0 0 c c*** compute local ilo, ihi, num for each processor call nga_distribution(g_ielm1,me,ilo,ihi,chunk) do i=0,nproc-1 if(me.eq.i) then write(6,'(a,i4,a,i8,a,i8)') 'me: ',me, + ' lo: ', ilo,' hi: ',ihi call ffflush(6) endif call ga_sync() enddo num=ihi-ilo+1 if(ihi.le.0) num=0 if(ihi.gt.nelements) then ihi=nelements num=max(0,ihi-ilo+1) endif if(num.gt.nelements_max) then print *,"Too many elements " goto 9999 endif cbjp print *, 'me=',me, num,ilo,ihi do i=0,nproc-1 if(me.eq.i) then write(6,'(a,i4,a,i8,a,i8,a,i8)') 'me: ',me, + ' num: ',num, ' lo: ', ilo,' hi: ',ihi call ffflush(6) endif call ga_sync() enddo c c*** scatter some values into the global array call ga_fill(g_sbit2,0) call ga_fill(g_src2,0) if(num.gt.0) call nga_get(g_ielm1,ilo,ihi,ielm1,one) do i=ilo,ihi ipack(i-ilo+1)=1+nen*(ielm1(i-ilo+1)-1) jpack(i-ilo+1)=1 kpack(i-ilo+1)=ielm1(i-ilo+1) enddo if(num.gt.0) call NGA_scatter(g_sbit2,jpack,ipack,num) if(num.gt.0) call NGA_scatter(g_src2 ,kpack,ipack,num) c *** sbit2: 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 ..... c *** src2: 1 0 0 0 2 0 0 0 3 0 0 0 4 0 0 0 5 0 0 0 6 ..... c c .................................................................. c TEST THE "PACK" FUNCTION. c itoff=nen*nelements call ga_fill(g_sink2,0) call ga_pack(g_src2, g_sink2,g_sbit2,1,itoff,icount) c *** sbit2: 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 ..... c *** src2: 1 0 0 0 2 0 0 0 3 0 0 0 4 0 0 0 5 0 0 0 6 ..... c *** sink2: 1 2 3 4 5 ... icount ... 0 0 0 0 0 0 0 0 0 ..... 0 c [NOTE: icount should equal nelements.] if(icount.ne.nelements) then print *,"Wrong number of elements from PACK operation: ", * "count = ",icount," should be = ",nelements endif call ga_add(1,g_ielm1,-1,g_sink2,g_result1) c *** ielm1: 1 2 3 4 5 ... nelements ... 0 0 0 0 0 0 0 0 c *** sink2: 1 2 3 4 5 ... nelements ... 0 0 0 0 0 0 0 0 c *** result1: 0 0 0 0 0 0 0 ... 0 (result1=ielm1-sink2) c*** Find the maximum value and the index of the maximum value call nga_select_elem(g_result1,'max',imax_ga,ilocmax_ga) c*** Find the minimum value and the index of the minimum value call nga_select_elem(g_result1,'min',imin_ga,ilocmin_ga) if(imax_ga.eq.0.and.imin_ga.eq.0) then if (me.eq.0) then print *,"Pack successful" endif else call ga_error("Pack unsuccessful",0) endif call ga_sync() C C .................................................................. C TEST THE "UNPACK" FUNCTION. C itoff=nen*nelements call ga_fill(g_src2,0) call ga_unpack(g_sink2,g_src2,g_sbit2,1,itoff,count) C *** sink2: 1 2 3 4 5 ... icount ... 0 0 0 0 0 0 0 0 0 ..... 0 C *** sbit2: 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 ..... C *** src2: 1 0 0 0 2 0 0 0 3 0 0 0 4 0 0 0 5 0 0 0 6 ..... call ga_fill(g_sink2,0) call ga_pack(g_src2, g_sink2,g_sbit2,1,itoff,icount) C *** src2: 1 0 0 0 2 0 0 0 3 0 0 0 4 0 0 0 5 0 0 0 6 ..... C *** sbit2: 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 ..... C *** sink2: 1 2 3 4 5 ... icount ... 0 0 0 0 0 0 0 0 0 ..... 0 C [NOTE: icount should equal nelements.] if(icount.ne.nelements) then print *,"Wrong number of elements from PACK operation: ", * "count = ",icount," should be = ",nelements endif call ga_add(1,g_ielm1,-1,g_sink2,g_result1) C *** ielm1: 1 2 3 4 5 ... nelements ... 0 0 0 0 0 0 0 0 C *** sink2: 1 2 3 4 5 ... nelements ... 0 0 0 0 0 0 0 0 C *** result1: 0 0 0 0 0 0 0 ... 0 (result1=ielm1-sink2) c*** Find the maximum value and the index of the maximum value call nga_select_elem(g_result1,'max',imax_ga,ilocmax_ga) c*** Find the minimum value and the index of the minimum value call nga_select_elem(g_result1,'min',imin_ga,ilocmin_ga) if(imax_ga.eq.0.and.imin_ga.eq.0) then if (me.eq.0) then print *,"Unpack successful" endif else print *,"Unpack unsuccessful", imin_ga,ilocmin_ga print *,"Unpack unsuccessful", imax_ga,ilocmax_ga call ga_error("Unpack unsuccessful", -1) endif call ga_sync() C goto 9999 9999 continue C C .................................................................. C return end subroutine test_nga_scan() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" C integer g_ielm1, g_result1 ! handles to global arrays integer g_ielm2, g_sbit2, g_src2, g_sink2, g_result2 ! handles to global arrays integer g_irowmat, g_icolmat ! handles to global arrays integer g_velm4, g_velm5 ! handles to global arrays C integer ilo, ihi, num integer ilo4, ihi4, num4 integer i, j, it, count integer me, nproc ! my processor & number of procs integer ndim,dims(1),chunk(1) C integer nelements_max, nen_max parameter (nelements_max=100000, nen_max=4) integer ipack(nelements_max), * jpack(nelements_max), * kpack(nelements_max), * ielm1(nelements_max), * ielm2(nen_max*nelements_max), * ielm3(nen_max*nelements_max), * ielm4(nen_max*nelements_max), * ielm5(nen_max*nelements_max) integer itest(nen_max*nelements_max), one double precision velm4(nen_max*nelements_max), * velm5(nen_max*nelements_max) c integer ilocmax, ilocmin integer ilocmax_ga, ilocmin_ga integer imax_ga, imin_ga C integer nelements, nen integer isum, icount, itoff double precision result GA_ACCESS_INDEX_TYPE idx integer i1, i2, ichk C c c*** check parallel environment me = ga_nodeid() nproc = ga_nnodes() c nelements=20000 c nelements=20 nen=4 one = 1 c c*** create a global 1-D array ndim=1 dims(1)=nen*nelements chunk(1)=min(1,dims(1)/nproc) if (.not. nga_create(MT_INT, ndim, dims, 'ielm1', chunk, $ g_ielm1)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'result1', chunk, $ g_result1)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'sbit2', chunk, $ g_sbit2)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'src2', chunk, $ g_src2)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'sink2', chunk, $ g_sink2)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'result2', chunk, $ g_result2)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'irowmat', chunk, $ g_irowmat)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_INT, ndim, dims, 'icolmat', chunk, $ g_icolmat)) $ call ga_error(' ga_create failed ',0) C if (.not. nga_create(MT_DBL, ndim, dims, 'velm4', chunk, $ g_velm4)) $ call ga_error(' ga_create failed ',0) if (.not. nga_create(MT_DBL, ndim, dims, 'velm5', chunk, $ g_velm5)) $ call ga_error(' ga_create failed ',0) c c Enumerate the sbit arrary to get a sequential vector. call ga_fill(g_ielm1,0) call ga_patch_enum(g_ielm1,1,nelements,1,1) c *** ielm1: 1 2 3 4 5 ... nelements ... 0 0 0 0 0 c c*** compute local ilo, ihi, num for each processor call nga_distribution(g_ielm1,me,ilo,ihi,chunk) do i=0,nproc-1 if(me.eq.i) then write(6,'(a,i4,a,i8,a,i8)') 'me: ',me, + ' lo: ', ilo,' hi: ',ihi call ffflush(6) endif call ga_sync() enddo num=ihi-ilo+1 if(ihi.le.0) num=0 if(ihi.gt.nelements) then ihi=nelements num=max(0,ihi-ilo+1) endif if(num.gt.nelements_max) then print *,"Too many elements " goto 9999 endif cbjp print *, 'me=',me, num,ilo,ihi do i=0,nproc-1 if(me.eq.i) then write(6,'(a,i4,a,i8,a,i8,a,i8)') 'me: ',me, + ' num: ',num, ' lo: ', ilo,' hi: ',ihi call ffflush(6) endif call ga_sync() enddo c c*** scatter some values into the global array call ga_fill(g_sbit2,0) call ga_fill(g_src2,0) if(num.gt.0) call nga_get(g_ielm1,ilo,ihi,ielm1,1) do i=ilo,ihi ipack(i-ilo+1)=1+nen*(ielm1(i-ilo+1)-1) jpack(i-ilo+1)=1 kpack(i-ilo+1)=ielm1(i-ilo+1) enddo if(num.gt.0) call NGA_scatter(g_sbit2,jpack,ipack,num) if(num.gt.0) call NGA_scatter(g_src2 ,kpack,ipack,num) c *** sbit2: 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 ..... c *** src2: 1 0 0 0 2 0 0 0 3 0 0 0 4 0 0 0 5 0 0 0 6 ..... c c .................................................................. c TEST THE "PACK" FUNCTION. c itoff=nen*nelements call ga_fill(g_sink2,0) call ga_pack(g_src2, g_sink2,g_sbit2,1,itoff,icount) c *** sbit2: 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 ..... c *** src2: 1 0 0 0 2 0 0 0 3 0 0 0 4 0 0 0 5 0 0 0 6 ..... c *** sink2: 1 2 3 4 5 ... icount ... 0 0 0 0 0 0 0 0 0 ..... 0 c [NOTE: icount should equal nelements.] if(icount.ne.nelements) then print *,"Wrong number of elements from PACK operation: ", * "count = ",icount," should be = ",nelements endif call ga_add(1,g_ielm1,-1,g_sink2,g_result1) c *** ielm1: 1 2 3 4 5 ... nelements ... 0 0 0 0 0 0 0 0 c *** sink2: 1 2 3 4 5 ... nelements ... 0 0 0 0 0 0 0 0 c *** result1: 0 0 0 0 0 0 0 ... 0 (result1=ielm1-sink2) c*** Find the maximum value and the index of the maximum value call nga_select_elem(g_result1,'max',imax_ga,ilocmax_ga) c*** Find the minimum value and the index of the minimum value call nga_select_elem(g_result1,'min',imin_ga,ilocmin_ga) if(imax_ga.eq.0.and.imin_ga.eq.0) then if (me.eq.0) then print *,"Pack successful" endif else call ga_error("Pack unsuccessful",-1) endif call ga_sync() c c .................................................................. c TEST THE "UNPACK" FUNCTION. c itoff=nen*nelements call ga_fill(g_src2,0) call ga_unpack(g_sink2,g_src2,g_sbit2,1,itoff,count) * call ga_unpack(g_src2,g_sink2,g_sbit2,1,nelements,count) C *** sink2: 1 2 3 4 5 ... icount ... 0 0 0 0 0 0 0 0 0 ..... 0 C *** sbit2: 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 ..... C *** src2: 1 0 0 0 2 0 0 0 3 0 0 0 4 0 0 0 5 0 0 0 6 ..... call ga_fill(g_sink2,0) call ga_pack(g_src2, g_sink2,g_sbit2,1,itoff,icount) * call ga_pack(g_sink2,g_src2,g_sbit2,1,itoff,icount) C *** sbit2: 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 ..... C *** src2: 1 0 0 0 2 0 0 0 3 0 0 0 4 0 0 0 5 0 0 0 6 ..... C *** sink2: 1 2 3 4 5 ... icount ... 0 0 0 0 0 0 0 0 0 ..... 0 C [NOTE: icount should equal nelements.] if(icount.ne.nelements) then print *,"Wrong number of elements from PACK operation: ", * "count = ",icount," should be = ",nelements endif call ga_add(1,g_ielm1,-1,g_sink2,g_result1) C *** ielm1: 1 2 3 4 5 ... nelements ... 0 0 0 0 0 0 0 0 C *** sink2: 1 2 3 4 5 ... nelements ... 0 0 0 0 0 0 0 0 C *** result1: 0 0 0 0 0 0 0 ... 0 (result1=ielm1-sink2) c*** Find the maximum value and the index of the maximum value call nga_select_elem(g_result1,'max',imax_ga,ilocmax_ga) c*** Find the minimum value and the index of the minimum value call nga_select_elem(g_result1,'min',imin_ga,ilocmin_ga) if(imax_ga.eq.0.and.imin_ga.eq.0) then if (me.eq.0) then print *,"Unpack successful" endif else call ga_error("Unpack unsuccessful",-1) endif call ga_sync() * call ga_print(g_src2) c c .................................................................. c TEST THE SCAN COPY FUNCTION. c c*** compute local ilo, ihi, num for each processor call nga_distribution(g_irowmat,me,ilo4,ihi4,chunk) num4=ihi4-ilo4+1 if(ihi.le.0) num4=0 if(num4.gt.nen*nelements_max) then print *,"Too many elements " goto 9999 endif cbjp print *, 'me=',me, num4,ilo4,ihi4 do i=0,nproc-1 if(me.eq.i) then write(6,'(a,i4,a,i8,a,i8,a,i8)') 'me: ',me, + ' num4: ',num4, ' lo4: ', ilo4,' h4i: ',ihi4 call ffflush(6) endif call ga_sync() enddo call ga_fill(g_irowmat,-1) call GA_scan_copy(g_src2, g_irowmat, g_sbit2,1,itoff) c *** sbit2: 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 ..... c *** src2: 1 0 0 0 2 0 0 0 3 0 0 0 4 0 0 0 5 0 0 0 6 ..... c *** irowmat: 1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 5 5 5 5 6 ..... c call nga_get(g_irowmat,ilo4,ihi4,ielm4,1) do i=ilo4,ihi4 velm4(i-ilo4+1)=1.0d+00 enddo call ga_fill(g_velm4,0.0d+00) cbjp print *, num4, (ielm4(i),i=1,min(4,num4)) do j=0,nproc-1 if(me.eq.j) then write(6,'(a,i4,a,i8,a,4i8)') 'me: ',me, ' num4: ',num4, + ' ielm4: ', (ielm4(i),i=1,min(4,num4)) call ffflush(6) endif call ga_sync() enddo #if 0 call nga_scatter_acc(g_velm4,velm4,ielm4,num4,1.0d+00) C *** velm4: 4 4 4 4 4 4 4 4 4 4 ... nelements result=ga_ddot(g_velm4,g_velm4) if(result.eq.nen*nen*nelements) then #else count = 0 call nga_distribution(g_irowmat,me,ilo,ihi,chunk) call nga_access(g_irowmat,ilo,ihi,idx,one) do i = ilo, ihi count = count + int_mb(idx+i-ilo) end do call ga_igop(1,count,1,'+') call nga_distribution(g_ielm1,me,ilo,ihi,chunk) if(count.eq.nen*(nelements+1)*nelements/2) then #endif if (me.eq.0) then print *,"Scan_copy successful." endif else call ga_error("Scan_copy failed.",-1) endif call ga_sync() c c .................................................................. c TEST THE SCAN ADD FUNCTION. c call ga_fill(g_src2,1) call ga_fill(g_icolmat,0) * call ga_print(g_icolmat) * call ga_print(g_src2) * call ga_print(g_sbit2) c if (me.eq.0) then c one = 1 c call nga_get(g_src2,one,dims,itest,one) c write(6,'(25i4)') (itest(i),i=1,dims(1)) c write(6,*) c call nga_get(g_sbit2,one,dims,itest,one) c write(6,'(25i4)') (itest(i),i=1,dims(1)) c endif call ga_sync call GA_scan_add(g_src2, g_icolmat,g_sbit2,1,itoff,0) c if (me.eq.0) then c one = 1 c write(6,*) c call nga_get(g_icolmat,one,dims,itest,one) c write(6,'(25i4)') (itest(i),i=1,dims(1)) c endif c *** sbit2: 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 ..... c *** src2: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 c *** icolmat: 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 ..... call ga_fill(g_velm4,1.0d+00) call nga_get(g_icolmat,ilo4,ihi4,ielm4,1) do i=ilo4,ihi4 velm4(i-ilo4+1)=ielm4(i-ilo4+1) velm5(i-ilo4+1)=1.0d+00 enddo call nga_put(g_velm4,ilo4,ihi4,velm4,1) call nga_put(g_velm5,ilo4,ihi4,velm5,1) result=ga_ddot(g_velm4,g_velm5) c *** velm4: 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 ..... c *** velm5: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ..... c *** result = sum(velm4 * velm5) = (1+2+3+4+...+nen)*nelements isum=0 do i=1,nen isum=isum+i enddo if(result.eq.isum*nelements) then if (me.eq.0) then print *,"Scan_add successful." endif else call ga_error("Scan_add failed.",-1) endif call ga_sync() call ga_sync call GA_scan_add(g_src2, g_icolmat,g_sbit2,1,itoff,1) c *** sbit2: 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 ..... c *** src2: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 c *** icolmat: 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0 ..... c if (me.eq.0) then c one = 1 c write(6,*) c call nga_get(g_icolmat,one,dims,itest,one) c write(6,'(25i4)') (itest(i),i=1,dims(1)) c endif call nga_get(g_src2, 1, itoff, ielm2, one) call nga_get(g_sbit2, 1, itoff, ielm3, one) call nga_get(g_icolmat, 1, itoff, ielm4, one) call nga_get(g_icolmat, 1, itoff, ielm5, one) ichk = 1 do i = 1, itoff if (ielm3(i).eq.1.or.i.eq.1) then ielm4(i) = 0 else ielm4(i) = ielm4(i-1) + ielm2(i-1) endif if (ielm4(i).ne.ielm5(i)) then ichk = 0 endif end do cc ichk=ga_idot(g_src2,g_icolmat) isum = 0 do i=1,nen isum=isum+i-1 enddo cc if (ichk.eq.nelements*isum) then if (ichk.eq.1) then if (me.eq.0) then print *,"Exclusive scan_add successful." endif else call ga_error("Exclusive scan_add failed.",-1) endif c goto 9999 9999 continue c c .................................................................. c return end ga-5.9.2/global/testing/scan_addc.c000066400000000000000000000252321500715745200171100ustar00rootroot00000000000000/** * Tests the scan_add function in GA. * * Each test will locally perform the same functionality, then compare * local buffers against global buffers. */ #if HAVE_CONFIG_H # include "config.h" #endif #define NELEM 200000 /*#define NELEM 20*/ #define HEAP 200*200*4 #define FUDGE 100 #define STACK 200*200 #include #include #include "ga.h" #include "macdecls.h" #include "mp3.h" static int me; static int nproc; #if 0 # define PRINT(AT,AT_MSK) do { \ int p; \ for (p=0; p #include static int me; static int nproc; #define test_scan_copy_reg1 local_src[i] = i+1 #define test_scan_copy_cpl1 local_src[i].real = i+1; local_src[i].imag = i+2 #define test_scan_copy_reg2 local_msk[i] = rand()%q #define test_scan_copy_cpl2 local_msk[i].real = rand()%q; \ local_msk[i].imag = 0 #define test_scan_copy_reg3 local_msk[i] != 0 #define test_scan_copy_cpl3 local_msk[i].real != 0 #define test_scan_copy_reg4 local_dst[i] != buf_dst[i] #define test_scan_copy_cpl4 local_dst[i].real != buf_dst[i].real || \ local_dst[i].imag != buf_dst[i].imag #define test_scan_copy(MT,T,INNER,MT_MSK,T_MSK,INNER_MSK) \ static void test_scan_copy_##MT##_##MT_MSK(int llo, int lhi, int q) \ { \ int g_src, g_dst, g_msk; \ int ndim = 1; \ int dims[] = {NELEM}; \ int lo[1]; \ int hi[1]; \ int alo[] = {0}; \ int ahi[] = {NELEM-1}; \ int i; \ T *local_src, *local_dst, *buf_dst, last_val; \ T_MSK *local_msk; \ \ lo[0] = llo; \ hi[0] = lhi; \ \ g_src = NGA_Create(MT, ndim, dims, "g_src", NULL); \ g_dst = NGA_Create(MT, ndim, dims, "g_dst", NULL); \ g_msk = NGA_Create(MT_MSK,ndim, dims, "g_msk", NULL); \ local_src = malloc(sizeof(T)*NELEM); \ local_dst = malloc(sizeof(T)*NELEM); \ local_msk = malloc(sizeof(T_MSK)*NELEM); \ buf_dst = malloc(sizeof(T)*NELEM); \ \ (void)memset(&last_val, 0, sizeof(T)); \ (void)memset(local_src, 0, sizeof(T)*NELEM); \ (void)memset(local_dst, 0, sizeof(T)*NELEM); \ (void)memset(local_msk, 0, sizeof(T_MSK)*NELEM); \ (void)memset(buf_dst, 0, sizeof(T)*NELEM); \ \ /* process 0 initializes all buffers */ \ if (0 == me) { \ for (i=0; i #include #include "ga.h" #include "ga-mpi.h" #include "macdecls.h" #include "mp3.h" #define n 4 #define PROC_LIST_SIZE 100 int main(int argc, char **argv) { int me; int g_a; int status; int i,j; int dims[] = {n,n}; int proc_group[PROC_LIST_SIZE],proclist[PROC_LIST_SIZE],inode; int sbuf[1],rbuf[1]; MPI_Comm comm; MP_INIT(argc,argv); GA_Initialize(); me = GA_Nodeid(); status = MA_init(MT_DBL, 100000, 100000); if (!status) GA_Error("ma_init failed",-1); status = MA_set_auto_verify(1); status = MA_set_hard_fail(1); status = MA_set_error_print(1); inode = GA_Cluster_nodeid(); if (me == 0) { printf("there are %d nodes, node 0 has %d procs\n", GA_Cluster_nnodes(), GA_Cluster_nprocs(0)); fflush(stdout); } GA_Sync(); for (i=0; i #include #include #include #include "macdecls.h" #include "ga.h" #include "mp3.h" #define WRITE_VTK #define CG_SOLVE 1 #define NDIM 1024 #define ISEED 228103 /** * Test individual functions for sparse arrays */ /** * subroutine to set up a sparse matrix for testing purposes * @param s_a sparse matrix handle * @param a pointer to a regular matrix that is equivalent to s_a * @param dim dimension of sparse matrix * @param type data type used by sparse matrix */ void setup_matrix(int *s_a, void **a, int64_t dim, int type) { int64_t jlo, jhi, idx; int me = GA_Nodeid(); int nprocs = GA_Nnodes(); int64_t i, j; int64_t skip_len; void *d_val, *o_val; int size; int nskip = 5; /* if (me == 0) { printf("\n Create sparse matrix of size %ld x %ld\n",dim,dim); } */ /* Create sparse matrix */ *s_a = NGA_Sprs_array_create64(dim, dim, type); /* Determine column block set by me */ jlo = dim*me/nprocs; jhi = dim*(me+1)/nprocs-1; if (me == nprocs-1) jhi = dim-1; /* set up data values. Diagonal values are 2, off-diagonal values are -1 */ if (type == C_INT) { size = sizeof(int); } else if (type == C_LONG) { size = sizeof(long); } else if (type == C_LONGLONG) { size = sizeof(long long); } else if (type == C_FLOAT) { size = sizeof(float); } else if (type == C_DBL) { size = sizeof(double); } else if (type == C_SCPL) { size = 2*sizeof(float); } else if (type == C_DCPL) { size = 2*sizeof(double); } d_val = malloc(size); o_val = malloc(size); *a = malloc(dim*dim*size); memset(*a,0,dim*dim*size); if (type == C_INT) { *((int*)(d_val)) = 2; *((int*)(o_val)) = -1; } else if (type == C_LONG) { *((long*)(d_val)) = 2; *((long*)(o_val)) = -1; } else if (type == C_LONGLONG) { *((long long*)(d_val)) = 2; *((long long*)(o_val)) = -1; } else if (type == C_FLOAT) { *((float*)(d_val)) = 2.0; *((float*)(o_val)) = -1.0; } else if (type == C_DBL) { *((double*)(d_val)) = 2.0; *((double*)(o_val)) = -1.0; } else if (type == C_SCPL) { ((float*)d_val)[0]= 2.0; ((float*)d_val)[1]= 0.0; ((float*)o_val)[0]= -1.0; ((float*)o_val)[1]= 0.0; } else if (type == C_DCPL) { ((double*)d_val)[0]= 2.0; ((double*)d_val)[1]= 0.0; ((double*)o_val)[0]= -1.0; ((double*)o_val)[1]= 0.0; } /* loop over all columns in column block and add elements for each column. * Currently assume that each column has 5 elements, one on the diagonal * and 4 others off the diagonl. Final matrix is partitioned into row blocks * so this guarantees that sorting routines for elements are tested */ skip_len = dim/nskip; if (skip_len < 2) { nskip = dim/2; skip_len = dim/nskip; } for (j=jlo; j<=jhi; j++) { NGA_Sprs_array_add_element64(*s_a,j,j,d_val); for (i=0; i= jlo) { int64_t nrows = ihi-ilo+1; /* column block corresponding to iproc has data. Get pointers * to index and data arrays */ NGA_Sprs_array_access_col_block64(s_a, iproc, &idx, &jdx, &ptr); if (idx != NULL) { for (i=0; i= jlo) { int64_t nrows = ihi-ilo+1; /* column block corresponding to iproc has data. Get pointers * to index and data arrays */ NGA_Sprs_array_access_col_block64(s_a, iproc, &idx, &jdx, &ptr); if (idx != NULL) { for (i=0; i= jlo) { int64_t nrows = ihi-ilo+1; /* column block corresponding to iproc has data. Get pointers * to index and data arrays */ NGA_Sprs_array_access_col_block64(s_a, iproc, &idx, &jdx, &ptr); if (idx != NULL) { for (i=0; i 1.0e-8) ok = 0; } GA_Destroy(g_a); GA_Destroy(g_b); GA_Destroy(g_c); NGA_Sprs_array_destroy(s_a); free(a); GA_Dgop(&time,1,plus); time /= (double)nprocs; if (me == 0) { if (ok) { printf(" **Create from sparse array operation PASSES**\n"); printf(" Time for create from sparse array operation: %16.8f\n",time); } else { printf(" **Sparse create from sparse array operation FAILS**\n"); } } } int main(int argc, char **argv) { int me,nproc; int eight = 8; /** * Initialize GA */ MP_INIT(argc,argv); /* Initialize GA */ NGA_Initialize(); me = GA_Nodeid(); nproc = GA_Nnodes(); if (eight == SIZEOF_F77_INTEGER) { if (me == 0) { printf("\nTesting sparse matrices of size %d x %d on %d processors\n\n", NDIM,NDIM,nproc); } /** * Test different data types */ #if 1 if (me == 0) { printf("\nTesting matrices of type int\n"); } matrix_test(C_INT); if (me == 0) { printf("\nTesting matrices of type long\n"); } matrix_test(C_LONG); if (me == 0) { printf("\nTesting matrices of type long long\n"); } matrix_test(C_LONGLONG); if (me == 0) { printf("\nTesting matrices of type float\n"); } matrix_test(C_FLOAT); if (me == 0) { printf("\nTesting matrices of type double\n"); } matrix_test(C_DBL); #endif if (me == 0) { printf("\nTesting matrices of type single complex\n"); } matrix_test(C_SCPL); if (me == 0) { printf("\nTesting matrices of type double complex\n"); } matrix_test(C_DCPL); if (me == 0) { printf("\nSparse matrix tests complete\n\n"); } } else { if (me == 0) { printf("Test only runs if built with 8-byte integers\n"); } } NGA_Terminate(); /** * Tidy up after message-passing library */ MP_FINALIZE(); } ga-5.9.2/global/testing/sprsmatmult.F000066400000000000000000000500431500715745200175450ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif #define USE_PETSC 0 #define TEST_MULT 0 #if TEST_MULT c# define LMAX 972 #else c# define LMAX 94173031 c# define LMAX 9173031 # define LMAX 973031 c# define LMAX 97031 c# define LMAX 9031 #endif #define MAXVEC 5000000 #define bb_a(ib) bb_v(bb_i + (ib)) #define cc_a(ib) cc_v(cc_i + (ib)) program main implicit none #include "mafdecls.fh" #include "global.fh" #if USE_PETSC #include "finclude/petscvec.h" #include "finclude/petscmat.h" #include "finclude/petscsys.h" #endif integer heap, stack, fudge, ma_heap integer nmax, ndim, nprocs, me integer g_a_data, g_a_i, g_a_j, g_a_sbit, g_tmp, isize integer g_b, g_c, g_tmp2 integer i, j, k, one, icnt integer lo, hi, chunk, kp1, ld GA_ACCESS_INDEX_TYPE i8, idx, id_tmp integer g_chk(MAXVEC) double precision d_chk(MAXVEC) double precision t_beg, t_crt, t_mv, t_gat, t_elem, t_pack double precision t_shft, t_scnad #if TEST_MULT double precision ddata(MAXVEC),bb(LMAX),cc(LMAX),gc(LMAX) integer ii(LMAX+1),jj(MAXVEC),sbit(MAXVEC) double precision rdot #endif logical status integer idim, jdim, rmin, rmax, idum integer jmin, jmax, lsize parameter (heap=2000000, fudge=100, stack=2000000) integer i_chk(MAXVEC) double precision ran1 c c PETSc declarations c #if USE_PETSC PetscErrorCode p_ierr PetscReal bb_v(1),cc_v(1),zero,p_ij PetscOffset bb_i, cc_i Vec p_b, p_c PetscInt p_idim, p_nloc PetscReal, pointer :: val(:) PetscInt, pointer :: loc(:), iloc(:) PetscInt, pointer :: jloc(:) PetscInt p_i, p_j Mat p_m double precision t_petsc, t_assmbl, prdot double precision, pointer :: bbuf(:),cbuf(:) #endif c c*** Intitialize a message passing library c #include "mp3.fh" c c*** Initialize GA c c There are 2 choices: ga_initialize or ga_initialize_ltd. c In the first case, there is no explicit limit on memory usage. c In the second, user can set limit (per processor) in bytes. c call ga_initialize() t_crt = 0.0d00 t_mv = 0.0d00 t_gat = 0.0d00 t_elem = 0.0d00 t_pack = 0.0d00 t_shft = 0.0d00 t_scnad = 0.0d00 #if USE_PETSC t_petsc = 0.0d00 t_assmbl = 0.0d00 call PetscInitialize(PETSC_NULL_CHARACTER,p_ierr) #endif me = ga_nodeid() nprocs = ga_nnodes() one = 1 c we can also use GA_set_memory_limit BEFORE first ga_create call c ma_heap = heap + fudge c call GA_set_memory_limit(util_mdtob(ma_heap)) c if(ga_nodeid().eq.0)then print * print *,'GA initialized ' print * #if TEST_MULT print * print *,'Comparing distributed multiply with serial multiply' print * #endif call flush(6) endif c c*** Initialize the MA package c MA must be initialized before any global array is allocated c status = ma_init(MT_DBL, stack, ma_heap) if (.not. status) call ga_error('ma_init failed',-1) c c create a sparse LMAX x LMAX matrix and two vectors of length c LMAX. The matrix is stored in compressed row format. c One of the vectors is filled with random data and the other c is filled with zeros. c idim = LMAX jdim = LMAX if (me.eq.0) then print * print *, 'Nominal dimension of matrix: ',idim print * endif rmin = 5 rmax = 10 rmin = 3 rmax = 7 t_beg = ga_wtime() call create_rand_sprs_mat(idim,jdim,rmin,rmax, + g_a_data,g_a_i,g_a_j,g_a_sbit,isize) t_crt = ga_wtime() - t_beg if (me.eq.0) then print * print *, 'Number of non-zero elements in compressed matrix: ', + isize print * endif #if TEST_MULT if (me.eq.0) then call nga_get(g_a_data,one,isize,ddata,one) call nga_get(g_a_i,one,idim+1,ii,one) call nga_get(g_a_j,one,isize,jj,one) endif call ga_sync #endif g_tmp = ga_create_handle() call ga_set_data(g_tmp,one,isize,MT_DBL) status = ga_allocate(g_tmp) c g_tmp2 = ga_create_handle() call ga_set_data(g_tmp2,one,isize,MT_DBL) status = ga_allocate(g_tmp2) c g_b = ga_create_handle() call ga_set_data(g_b,one,idim,MT_DBL) status = ga_allocate(g_b) c c fill g_b with random values c call nga_distribution(g_b,me,lo,hi) call nga_access(g_b,lo,hi,idx,ld) ld = hi-lo do i8 = 0, ld dbl_mb(i8+idx) = ran1(idum) end do call nga_release(g_b,lo,hi) #if TEST_MULT call ga_sync if (me.eq.0) then call nga_get(g_b,one,idim,bb,one) endif call ga_sync #endif c g_c = ga_create_handle() call ga_set_data(g_c,one,idim,MT_DBL) status = ga_allocate(g_c) call ga_zero(g_c) c #if USE_PETSC c c Create corresponding PETSC vectors c p_idim = idim zero = 0.0d00 p_nloc = ld+1 call VecCreateMPI(PETSC_COMM_WORLD,p_nloc,PETSC_DECIDE,p_b,p_ierr) call VecDuplicate(p_b,p_c,p_ierr) c c Copy values from global array to PETSC vector c allocate(val(ld+1)) allocate(loc(ld+1)) call nga_access(g_b,lo,hi,idx,ld) ld = hi-lo do i8 = 0, ld i = i8+1 val(i) = dbl_mb(i8+idx) loc(i) = lo+i8-1 if (loc(i).lt.0.or.loc(i).ge.idim) then write(6,'(i4,a,i8,a,i12)') me,' loc(',i,'): ',loc(i) endif end do call nga_release(g_b,lo,hi) call VecSetValues(p_b,p_nloc,loc,val,INSERT_VALUES,p_ierr) t_beg = ga_wtime() call VecAssemblyBegin(p_b,p_ierr) call VecAssemblyEnd(p_b,p_ierr) t_assmbl = ga_wtime() - t_beg call VecSet(p_c,zero,p_ierr) c cdbg call VecGetArray(p_b,bb_v,bb_i,p_ierr) cdbg do i = 1, hi-lo+1 cdbg write(6,'(i4,a,i4,a,2f12.6)') me,' b(',i,'): ',val(i), cdbg + bb_a(i) cdbg end do deallocate(val) deallocate(loc) c c Create corresponding PETSC sparse matrix c call MatCreate(PETSC_COMM_WORLD,p_m,p_ierr) call MatSetSizes(p_m,PETSC_DECIDE,PETSC_DECIDE, + p_idim,p_idim,p_ierr) c call MatSetType(p_m,MATMPIAIJ,p_ierr) call MatSetType(p_m,MATAIJ,p_ierr) c call MatSetFromOptions(p_m,p_ierr) ld = hi-lo+1 allocate(iloc(ld+1)) call nga_get(g_a_i,lo,hi,iloc,ld) c write(6,'(i4,a,i12)') me,' ld: ',ld c do i = 1, ld c if (iloc(i).lt.1) then c write(6,'(i4,a,i6,a,i12)') me,' iloc(',i,'): ',iloc(i) c endif c end do jmin = iloc(1) if (hi.lt.idim) then call nga_get(g_a_i,hi+1,hi+1,jmax,one) jmax = jmax - 1 else jmax = isize endif iloc(ld+1) = jmax + 1 lsize = jmax - jmin + 1 c write(6,'(i4,a,i12)') me,' lsize: ',lsize allocate(jloc(lsize)) allocate(val(lsize)) call nga_get(g_a_j,jmin,jmax,jloc,one) c do i = 1, lsize c if (jloc(i).lt.1) then c write(6,'(i4,a,i6,a,i12)') me,' jloc(',i,'): ',jloc(i) c endif c end do call nga_get(g_a_data,jmin,jmax,val,one) #if 1 do i = 1, ld p_i = i+lo-2 if (p_i.lt.0.or.p_i.ge.idim) then write(6,'(i4,a,i8)') me,' p_i out of bounds: ',p_i endif do j = iloc(i), iloc(i+1) - 1 p_j = jloc(j-jmin+1) - 1 if (p_j.lt.0.or.p_j.ge.idim) then write(6,'(i4,a,i8)') me,' p_j out of bounds: ',p_j endif p_ij = val(j-jmin+1) cbjp write(6,'(i4,a,2i8,f12.6)') me,' m(i,j): ',p_i, p_j,p_ij call MatSetValues(p_m,one,p_i,one,p_j,p_ij,INSERT_VALUES, + p_ierr) end do end do t_beg = ga_wtime() call MatAssemblyBegin(p_m,MAT_FINAL_ASSEMBLY,p_ierr) call MatAssemblyEnd(p_m,MAT_FINAL_ASSEMBLY,p_ierr) t_assmbl = ga_wtime() - t_beg c call MatView(p_m,PETSC_VIEWER_DRAW_WORLD,p_ierr) #endif #endif c c Find out what section of j-index vector I own and use c this information to get a pointer to to the corresponding c parts of the j-index vector and g_tmp array. c t_beg = ga_wtime() call nga_distribution(g_a_j,me,lo,hi) call nga_access(g_a_j,lo,hi,idx,ld) call nga_access(g_tmp,lo,hi,id_tmp,ld) c c scatter elements of g_b into a temporary array that is the c same size as g_a_data. c ld = hi-lo+1 call nga_gather(g_b,dbl_mb(id_tmp),int_mb(idx),ld) call ga_sync call nga_release(g_a_j,lo,hi) call nga_release(g_tmp,lo,hi) t_gat = ga_wtime() - t_beg c t_beg = ga_wtime() call ga_elem_multiply(g_tmp,g_a_data,g_tmp2) t_elem = ga_wtime() - t_beg c if (me.eq.0) then c do i = 0, 1 c call nga_distribution(g_a_sbit,i,lo,hi) c call nga_get(g_a_sbit,lo,hi,i_chk,one) c write(6,'(25i5)') (i_chk(j),j=1,hi-lo+1) c end do c endif t_beg = ga_wtime() call ga_scan_add(g_tmp2,g_tmp,g_a_sbit,one,isize,0) t_scnad = ga_wtime() - t_beg c if (me.eq.0) then c do i = 0, 1 c call nga_distribution(g_tmp,i,lo,hi) c call nga_get(g_tmp,lo,hi,d_chk,one) c write(6,'(10f8.2)') (d_chk(j),j=1,hi-lo+1) c end do c endif #if TEST_MULT if (me.eq.0) then call nga_get(g_a_sbit,one,isize,sbit,one) icnt = 0 if (isize.gt.MAXVEC) call ga_error('MAXVEC exceeded in sbit', + isize) do i = 1, isize if (sbit(i).eq.1) then icnt = icnt + 1 endif end do endif #endif t_beg = ga_wtime() call shift_minus(g_a_sbit,isize) t_shft = ga_wtime() - t_beg c t_beg = ga_wtime() call ga_pack(g_tmp,g_c,g_a_sbit,one,isize,icnt) t_pack = ga_wtime() - t_beg c #if USE_PETSC t_beg = ga_wtime() call MatMult(p_m,p_b,p_c,p_ierr) t_petsc = ga_wtime() - t_beg call nga_distribution(g_c,me,lo,hi) allocate(cbuf(hi-lo+1)) call nga_get(g_c,lo,hi,cbuf,one) call VecGetArray(p_c,cc_v,cc_i,p_ierr) prdot = 0.0d00 do i = 1, hi-lo+1 cbjp write(6,'(i4,a,i4,a,2f12.6)') me,' c(',i,'): ',cbuf(i), cbjp + cc_a(i) prdot = prdot + (cc_a(i)-cbuf(i))**2 end do call ga_dgop(1,prdot,1,'+') if (me.eq.0) then write(6,'(a,e16.8)') + 'Difference between GA and PETSC results: ', + sqrt(prdot) endif #endif #if TEST_MULT if (me.eq.0) then call nga_get(g_c,one,idim,gc,one) do i = 1, idim cc(i) = 0.0d00 do j = ii(i), ii(i+1)-1 cbjp write(6,'(i4,a,2i8,f12.6)') me,' gm(i,j): ', cbjp + i-1,jj(j)-1,ddata(j) cc(i) = cc(i) + ddata(j)*bb(jj(j)) end do gc(i) = gc(i) - cc(i) end do rdot = 0.0d00 do i = 1, idim rdot = rdot + gc(i)**2 end do write(6,*) 'Difference between parallel and serial results: ', + rdot endif #endif c c Clean up arrays c status = ga_destroy(g_tmp) status = ga_destroy(g_tmp2) status = ga_destroy(g_b) status = ga_destroy(g_c) status = ga_destroy(g_a_data) status = ga_destroy(g_a_i) status = ga_destroy(g_a_j) status = ga_destroy(g_a_sbit) #if USE_PETSC call VecDestroy(p_b,p_ierr) call VecDestroy(p_c,p_ierr) call MatDestroy(p_m,p_ierr) #endif c t_mv = t_gat + t_scnad + t_elem + t_shft + t_pack call ga_dgop(1,t_crt,1,'+') call ga_dgop(2,t_gat,1,'+') call ga_dgop(3,t_scnad,1,'+') call ga_dgop(4,t_elem,1,'+') call ga_dgop(5,t_shft,1,'+') call ga_dgop(6,t_pack,1,'+') call ga_dgop(7,t_mv,1,'+') #if USE_PETSC call ga_dgop(8,t_petsc,1,'+') call ga_dgop(9,t_assmbl,1,'+') #endif c if (me.eq.0) then write(6,100) t_crt/dble(nprocs) write(6,200) t_gat/dble(nprocs) write(6,300) t_scnad/dble(nprocs) write(6,400) t_elem/dble(nprocs) write(6,500) t_shft/dble(nprocs) write(6,600) t_pack/dble(nprocs) write(6,700) t_mv/dble(nprocs) #if USE_PETSC write(6,800) t_petsc/dble(nprocs) write(6,900) t_assmbl/dble(nprocs) #endif 100 format('Time to create sparse matrix: ',f12.4) 200 format('Time to expand dense vector using gather: ',f12.4) 300 format('Time in segmented scan-add: ',f12.4) 400 format('Time for element-wise multiplication: ',f12.4) 500 format('Time to shift bit-mask vector: ',f12.4) 600 format('Time to pack result vector: ',f12.4) 700 format('Total time for sparse matrix-vector multiply: ',f12.4) #if USE_PETSC 800 format('Total time for PETSC matrix-vector multiply: ',f12.4) 900 format('Total time for PETSC assemble: ',f12.4) #endif endif c #if USE_PETSC call PetscFinalize(p_ierr) #endif call ga_terminate() c c*** Tidy up after message-passing library c call MP_FINALIZE() c stop end c c create a random sparse matrix in compressed row form c subroutine create_rand_sprs_mat(idim,jdim,rmin,rmax, + g_data,g_i,g_j,g_mask,isize) c c idim: i-dimension of array c jdim: j-dimension of array c rmin, rmax: minimum and maximum number of row elements in array c g_data: global array of values c g_j: global array containing j indices c g_i: global array containing starting location of each row in g_j c g_mask: global array of 1s and 0s containing a 1 at the start of c every new row in g_data c isize: total size of compressed matrix c implicit none #include "mafdecls.fh" #include "global.fh" integer idim, jdim, rmin, rmax, g_data, g_i, g_j, g_tmp integer g_sbit, g_mask, isize integer nprocs, me, imin, imax, icnt, jcnt, jmin, jmax, ntot double precision rval(MAXVEC), x, dr integer jval(MAXVEC), inc(MAXVEC), ones(MAXVEC) integer jdx(MAXVEC), i, j, k, itmp, itot, one, lo, hi, ld integer idum logical status integer i_chk(1000000) double precision d_chk(1000000) double precision ran1 GA_ACCESS_INDEX_TYPE idx c me = ga_nodeid() nprocs = ga_nnodes() x = ran1(12345+me) dr = dble(rmax-rmin) one = 1 c c figure out min and max indices of rows that I am responsible for c imin = nint(dble(me)*(dble(idim)/dble(nprocs))) + 1 imax = nint(dble(me+1)*(dble(idim)/dble(nprocs))) if (me.eq.0) then write(6,*) write(6,*) 'Minimum number of entries per row: ', + rmin write(6,*) write(6,*) write(6,*) 'Maximum number of entries per row: ', + rmin+nint(dr) write(6,*) endif icnt = 0 do i = imin, imax jmax = rmin + nint(dr*ran1(idum)) c do j = 1, jmax c jdx(j) = int(dble(jdim)*ran1(idum))+1 c if (jdx(j).gt.jdim) jdx(j) = jdim c end do j = 0 do while (j.lt.jmax) j = j + 1 jdx(j) = int(dble(jdim)*ran1(idum))+1 if (jdx(j).gt.jdim) jdx(j) = jdim k = 0 do while(k.lt.j-1) k = k + 1 if (jdx(j).eq.jdx(k)) then k = j j = j - 1 endif end do end do c c sort j indices c do j = 1, jmax do k = j+1, jmax if (jdx(j).gt.jdx(k)) then itmp = jdx(j) jdx(j) = jdx(k) jdx(k) = itmp endif end do end do do j = 1, jmax if (jdx(j).lt.1.or.jdx(j).gt.jdim) then write(6,'(i4,a,2i8)') me,' invalid j: ',i,jdx(j) endif end do c c create array elements c inc(i-imin+1) = jmax do j = 1, jmax icnt = icnt + 1 rval(icnt) = ran1(idum) jval(icnt) = jdx(j) end do end do itot = icnt isize = 0 do i = 1, imax-imin+1 isize = isize + inc(i) end do if (isize.gt.MAXVEC) + call ga_error('ISIZE exceeds MAXVEC in local arrays ',isize) c c create global arrays to hold sparse matrix c call ga_igop(1,itot,1,'+') isize = itot c write(6,*) me,' (create_rand) isize: ',isize c g_data = ga_create_handle(); call ga_set_data(g_data,one,itot,MT_DBL) status = ga_allocate(g_data) c g_j = ga_create_handle(); call ga_set_data(g_j,one,itot,MT_INT) status = ga_allocate(g_j) c g_i = ga_create_handle(); call ga_set_data(g_i,one,idim+1,MT_INT) status = ga_allocate(g_i) status = ga_duplicate(g_i, g_tmp, 'temp array') c g_sbit = ga_create_handle(); call ga_set_data(g_sbit,one,idim+1,MT_INT) status = ga_allocate(g_sbit) c call ga_zero(g_sbit) if (me.eq.0) then call nga_put(g_sbit,one,one,one,one) endif call ga_sync c c create offset vector c lo = imin+1 hi = imax+1 call ga_zero(g_tmp) if (me.eq.0) then call nga_put(g_tmp,one,one,one,one) endif call nga_put(g_tmp,lo,hi,inc,one) call ga_sync c if (me.eq.0) then c do i = 0, nprocs-1 c call nga_distribution(g_tmp,i,lo,hi) c call nga_get(g_tmp,lo,hi,i_chk,one) c write(6,'(25i5)') (i_chk(j),j=1,hi-lo+1) c end do c endif itot = idim+1 call ga_scan_add(g_tmp,g_i,g_sbit,one,itot,0) call ga_sync c if (me.eq.0) then c do i = 0, nprocs-1 c call nga_distribution(g_i,i,lo,hi) c call nga_get(g_i,lo,hi,i_chk,one) c write(6,'(25i5)') (i_chk(j),j=1,hi-lo+1) c end do c endif status = ga_destroy(g_tmp) status = ga_destroy(g_sbit) c c create mask vector c g_mask = ga_create_handle(); call ga_set_data(g_mask,one,isize,MT_INT) status = ga_allocate(g_mask) call ga_zero(g_mask) c call nga_distribution(g_i,me,lo,hi) call nga_access(g_i,lo,hi,idx,ld) ntot = hi - lo + 1 if (ntot.gt.MAXVEC) + call ga_error('NTOT exceeds MAXVEC in bit vector ',ntot) c c decrease count by one on last processor because this c element doesn't exist c if (me.eq.nprocs-1) then ntot = ntot - 1 endif do i = 1, ntot ones(i) = 1 end do call nga_scatter(g_mask,ones,int_mb(idx),ntot) call ga_sync call nga_release(g_i,lo,hi) c if (me.eq.0) then c do i = 0, 1 c call nga_distribution(g_mask,i,lo,hi) c call nga_get(g_mask,lo,hi,i_chk,one) c write(6,'(25i5)') (i_chk(j),j=1,hi-lo+1) c end do c endif c c store values of j in g_j and data values in g_data c call nga_get(g_i,imin,imin,jmin,one) call nga_get(g_i,imax+1,imax+1,jmax,one) jmax = jmax - 1 call nga_put(g_j,jmin,jmax,jval,one) call nga_put(g_data,jmin,jmax,rval,one) call ga_sync return end c c shift all the elements in a linear global array one element to c the left. Wrap elements around end if necessary c subroutine shift_minus(g_a, isize) implicit none #include "mafdecls.fh" #include "global.fh" integer g_a, isize integer lo, hi, ld, me, itmp integer isav GA_ACCESS_INDEX_TYPE idx, ii me = ga_nodeid() call ga_sync call nga_distribution(g_a,me,lo,hi) call nga_access(g_a,lo,hi,idx,ld) ld = hi-lo isav = int_mb(idx) do ii = 1, ld int_mb(idx + ii - 1) = int_mb(idx + ii) end do if (lo.eq.1) then itmp = isize else itmp = lo-1 endif call nga_release(g_a,lo,hi) call ga_sync call nga_put(g_a,itmp,itmp,isav,ld) call ga_sync return end c c Wrapper for random number generator c double precision function ran1(idum) implicit none #include "testutil.fh" integer idum,iff data iff /1/ save iff if (iff.eq.1) then ran1 = drand(iabs(idum)) iff = 0 endif ran1 = drand(0) return end ga-5.9.2/global/testing/sprsmatvec.c000066400000000000000000001027241500715745200174020ustar00rootroot00000000000000#include #include #include #include "macdecls.h" #include "ga.h" #include "mp3.h" #define USE_HYPRE 0 #define IMAX 200 #define JMAX 200 #define KMAX 200 #define LMAX IMAX*JMAX*KMAX #define bb_a(ib) bb_v(bb_i + (ib)) #define cc_a(ib) cc_v(cc_i + (ib)) #define Integer int #define MAX_FACTOR 10000 /** * If this test is built standalone and then linked to the Hypre library, * the Hypre sparse matrix-vector multiply routines can be used to check the * correctness of the answers */ #if USE_HYPRE #include "HYPRE.h" #include "HYPRE_struct_mv.h" #include "mpi.h" #endif void grid_factor(int p, int xdim, int ydim, int zdim, int *idx, int *idy, int *idz) { int i, j; int ip, ifac, pmax, prime[MAX_FACTOR]; int fac[MAX_FACTOR]; int ix, iy, iz, ichk; i = 1; /** * factor p completely * first, find all prime numbers, besides 1, less than or equal to * the square root of p */ ip = (int)(sqrt((double)p))+1; pmax = 0; for (i=2; i<=ip; i++) { ichk = 1; for (j=0; j MAX_FACTOR) printf("Overflow in grid_factor\n"); prime[pmax-1] = i; } } /** * find all prime factors of p */ ip = p; ifac = 0; for (i=0; i= 0; i--) { ix = xdim/(*idx); iy = ydim/(*idy); iz = zdim/(*idz); if (ix >= iy && ix >= iz && ix > 1) { *idx = fac[i]*(*idx); } else if (iy >= ix && iy >= iz && iy > 1) { *idy = fac[i]*(*idy); } else if (iz >= ix && iz >= iy && iz > 1) { *idz = fac[i]*(*idz); } else { printf("Too many processors in grid factoring routine\n"); } } } /** * Short subroutine for multiplying sparse matrix block with vector segment */ void loc_matmul(double *a_mat, int *jvec, int *ivec, double *bvec, double *cvec, int nrows) { double tempc; int i, j, jj, jmin,jmax; for (i=0; i hi[0]) { jdx = kp*pdi*pdj + jp*pdi + ip + 1; ecnt[jdx] = ecnt[jdx] + 1; } else { ecnt[me] = ecnt[me] + 1; } } if (ix-1 >= 0) { if (ix-1 < lo[0]) { jdx = kp*pdi*pdj + jp*pdi + ip - 1; ecnt[jdx] = ecnt[jdx] + 1; } else { ecnt[me] = ecnt[me] + 1; } } if (iy+1 <= jdim-1) { if (iy+1 > hi[1]) { jdx = kp*pdi*pdj + (jp+1)*pdi + ip; ecnt[jdx] = ecnt[jdx] + 1; } else { ecnt[me] = ecnt[me] + 1; } } if (iy-1 >= 0) { if (iy-1 < lo[1]) { jdx = kp*pdi*pdj + (jp-1)*pdi + ip; ecnt[jdx] = ecnt[jdx] + 1; } else { ecnt[me] = ecnt[me] + 1; } } if (iz+1 <= kdim-1) { if (iz+1 > hi[2]) { jdx = (kp+1)*pdi*pdj + jp*pdi + ip; ecnt[jdx] = ecnt[jdx] + 1; } else { ecnt[me] = ecnt[me] + 1; } } if (iz-1 >= 0) { if (iz-1 < lo[2]) { jdx = (kp-1)*pdi*pdj + jp*pdi + ip; ecnt[jdx] = ecnt[jdx] + 1; } else { ecnt[me] = ecnt[me] + 1; } } } /** * Create a list of processors that this processor is coupled to. Also count * the total number of grid points that this processor couples to. This number * is equal to the total number of matrix elements held by this processor. */ ltotal_procs = 0; ncnt = 0; for (i=0; i 0) { ltotal_procs += 1; ncnt += ecnt[i]; } } *total_procs = ltotal_procs; lproclist = (int*)malloc(ltotal_procs*sizeof(int)); *proclist = lproclist; lproc_inv = (int*)malloc(nprocs*sizeof(int)); *proc_inv = lproc_inv; licnt = (int*)malloc(ltotal_procs*sizeof(int)); *icnt = licnt; /** * Set up conventional CSR arrays to hold portion of matrix owned by this * processor */ rval = (double*)malloc(ncnt*sizeof(double)); idbg = ncnt; jval = (int*)malloc(ncnt*sizeof(int)); ival = (int*)malloc((imax-imin+2)*ltotal_procs*sizeof(int)); ivalt = (int*)malloc((imax-imin+2)*ltotal_procs*sizeof(int)); for (i=0; i 0) { lproclist[j] = i; if (j > 0) { lvoffset[j] = ecnt[lproclist[j-1]]+lvoffset[j-1]; } lproc_inv[i] = j; j++; } } free(ecnt); isize = imax-imin+2; for (i=0; i hi[0]) { jdx = kp*pdi*pdj + jp*pdi + ip + 1; il = 0; jl = iy - lo[1]; kl = iz - lo[2]; ldpi = xld[ip+1]; } else { jdx = me; il = ix - lo[0] + 1; jl = iy - lo[1]; kl = iz - lo[2]; ldpi = ldi; } idx = kl*ldpi*ldj + jl*ldpi + il; nghbrs[ncnt] = idx; procid[ncnt] = jdx; } if (ix-1 >= 0) { ncnt++; ixn[ncnt] = ix - 1; iyn[ncnt] = iy; izn[ncnt] = iz; if (ix-1 < lo[0]) { jdx = kp*pdi*pdj + jp*pdi + ip - 1; il = xld[ip-1] - 1; jl = iy - lo[1]; kl = iz - lo[2]; ldmi = xld[ip-1]; } else { jdx = me; il = ix - lo[0] - 1; jl = iy - lo[1]; kl = iz - lo[2]; ldmi = ldi; } idx = kl*ldmi*ldj + jl*ldmi + il; nghbrs[ncnt] = idx; procid[ncnt] = jdx; } if (iy+1 <= jdim-1) { ncnt++; ixn[ncnt] = ix; iyn[ncnt] = iy + 1; izn[ncnt] = iz; if (iy+1 > hi[1]) { jdx = kp*pdi*pdj + (jp+1)*pdi + ip; il = ix - lo[0]; jl = 0; kl = iz - lo[2]; ldpj = yld[jp+1]; } else { jdx = me; il = ix - lo[0]; jl = iy - lo[1] + 1; kl = iz - lo[2]; ldpj = ldj; } idx = kl*ldi*ldpj + jl*ldi + il; nghbrs[ncnt] = idx; procid[ncnt] = jdx; } if (iy-1 >= 0) { ncnt++; ixn[ncnt] = ix; iyn[ncnt] = iy - 1; izn[ncnt] = iz; if (iy-1 < lo[1]) { jdx = kp*pdi*pdj + (jp-1)*pdi + ip; il = ix - lo[0]; jl = yld[jp-1] - 1; kl = iz - lo[2]; ldmj = yld[jp-1]; } else { jdx = me; il = ix - lo[0]; jl = iy - lo[1] - 1; kl = iz - lo[2]; ldmj = ldj; } idx = kl*ldi*ldmj + jl*ldi + il; nghbrs[ncnt] = idx; procid[ncnt] = jdx; } if (iz+1 <= kdim-1) { ncnt++; ixn[ncnt] = ix; iyn[ncnt] = iy; izn[ncnt] = iz + 1; if (iz+1 > hi[2]) { jdx = (kp+1)*pdi*pdj + jp*pdi + ip; il = ix - lo[0]; jl = iy - lo[1]; kl = 0; } else { jdx = me; il = ix - lo[0]; jl = iy - lo[1]; kl = iz - lo[2] + 1; } idx = kl*ldi*ldj + jl*ldi + il; nghbrs[ncnt] = idx; procid[ncnt] = jdx; } if (iz-1 >= 0) { ncnt++; ixn[ncnt] = ix; iyn[ncnt] = iy; izn[ncnt] = iz - 1; if (iz-1 < lo[2]) { jdx = (kp-1)*pdi*pdj + jp*pdi + ip; il = ix - lo[0]; jl = iy - lo[1]; kl = zld[kp-1] - 1; } else { jdx = me; il = ix - lo[0]; jl = iy - lo[1]; kl = iz - lo[2] - 1; } idx = kl*ldi*ldj + jl*ldi + il; nghbrs[ncnt] = idx; procid[ncnt] = jdx; } /** * sort j indices. This uses a simple bubble sort, but ncnt should be small. A * more sophisticated approach could be taken if this is too time consuming. */ ncnt++; for (j=0; j nghbrs[k]) { itmp = nghbrs[j]; nghbrs[j] = nghbrs[k]; nghbrs[k] = itmp; itmp = ixn[j]; ixn[j] = ixn[k]; ixn[k] = itmp; itmp = iyn[j]; iyn[j] = iyn[k]; iyn[k] = itmp; itmp = izn[j]; izn[j] = izn[k]; izn[k] = itmp; itmp = procid[j]; procid[j] = procid[k]; procid[k] = itmp; } } } for (k=0; k= ntot) { printf("p[%d] Invalid neighbor %d\n",me,nghbrs[k]); } } /** * create array elements * for (j=0; j=idbg) { } /* TODO: Check this carefully */ jval[lvoffset[idx]+licnt[idx]] = nghbrs[j]; ivalt[idx*isize+i-imin] = ivalt[idx*isize+i-imin]+1; licnt[idx]++; } } for (i=0; i ntot-1) { bhi[1] = ntot-1; } btot = (hi[0]-lo[0]+1)*(hi[1]-lo[1]+1)*(hi[2]-lo[2]+1); for (i=0; i Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_sync() do i = 1, n do j = 1, n if (b(i,j) .ne. 0.0d0) then write(6,*) me,' zero ', i, j, b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format('> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) * write(6,4) me, ilo, ihi, jlo, jhi * 4 format(' node ',i2,' checking put ',4i4) * call ffflush(6) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c call util_dfill(n*n, 0.0d0, b, 1) * call ga_print(g_a,1) call ga_get(g_a, 1, n, 1, n, b, n) * write(6,*) ' after get' * call output(b, 1, n, 1, n, n, n, 1) c do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif call ga_sync() c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format('> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_dfill(n*n, 0.0d0, b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif sum1 = 0.0d0 do j = jlo, jhi do i = ilo, ihi sum1 = sum1 + b(i,j) if (b(i,j) .ne. a(i,j)) then write(6,*) i, j, b(i,j), a(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo c enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) call ffflush(6) endif call ga_sync() c c Each node accumulates into disjoint sections of the array c #if 1 if (me .eq. 0) then write(6,9) 9 format('> Checking accumulate ... ') call ffflush(6) endif call ga_sync() c crap = drand(12345) ! Same seed for each process do j = 1, n do i = 1, n c b(i,j) = drand(0) b(i,j) = i+j enddo enddo c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc c x = drand(0) x = 10. ilo = i ihi = min(i+inc-1, n) if(ihi.eq.n-1)ihi=n c ihi = min(i+inc, n) jlo = j jhi = min(j+inc-1, n) if(jhi.eq.n-1)jhi=n c jhi = min(j+inc-1, n) * call ffflush(6) #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif c print *, me, 'checking accumulate ',ilo,ihi,jlo,jhi,x * 11 format(' node ',i2,' checking accumulate ',4i4) * call ffflush(6) call ga_acc(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n, x) endif ij = ij + 1 c c Each process applies all updates to its local copy c do jj = jlo, jhi do ii = ilo, ihi a(ii,jj) = a(ii,jj) + x * b(ii,jj) enddo enddo enddo enddo call ga_sync() c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if(MISMATCH(b(i,j),a(i,j)))then write(6,*) ' acc ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' disjoint ga_acc is OK' write(6,*) endif c c overlapping accumulate call ga_sync() #ifdef NEW_API g_b = ga_create_handle() call ga_set_data(g_b,ndim,dims,MT_DBL) call ga_set_array_name(g_b,'b') #ifdef BLOCK_CYCLIC #if defined(USE_SCALAPACK_DISTR) || defined(USE_TILED_DISTR) || defined(USE_TILED_IRREG_DISTR) #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_b,block_size,proc_grid) #else #ifdef USE_TILED_DISTR call ga_set_tiled_proc_grid(g_b,block_size,proc_grid) #else call ga_set_tiled_irreg_proc_grid(g_b,mapc,nblocks,proc_grid) #endif #endif #else call ga_set_block_cyclic(g_b,block_size) #endif #endif # ifdef MIRROR call ga_set_pgroup(g_b,p_mirror) # endif # ifdef USE_MEMDEV call ga_set_memory_dev(g_b,MEMDEVICE) # endif if (.not.ga_allocate(g_b)) then #else # ifndef MIRROR if (.not. ga_create(MT_DBL, n, n, 'b', 0, 0, g_b)) then # else if (.not. nga_create_config(MT_DBL, ndim, dims, 'b', chunk, + p_mirror, g_b)) then # endif #endif call ga_error('ga_create failed for second array ',-1) endif c call ga_zero(g_b) call ga_acc(g_b, n/2, n/2, n/2, n/2, 1d0, 1, 1d0) call ga_sync() #ifndef MIRROR if (me.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, b(1,1), 1) x = abs(b(1,1) -1d0*nproc) #else if (iproc.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, b(1,1), 1) x = abs(b(1,1) -1d0*lprocs) #endif if(x.gt. 1d-10)then #ifndef MIRROR write(6,*)'val=',b(1,1),' expected=',nproc, x #else write(6,*)'val=',b(1,1),' expected=',lprocs, x #endif call ga_error('overlapping accumulate failed',-1) endif if (me.eq.0) then write(6,*) write(6,*) ' overlapping ga_acc is OK' write(6,*) endif endif #endif c c Check the ga_add function c if (me .eq. 0) then write(6,91) 91 format('> Checking add ...') call ffflush(6) endif c c crap = drand(12345) ! Everyone has same seed do j = 1, n do i = 1, n b(i,j) = drand(0) a(i,j) = 0.1d0*a(i,j) + 0.9d0*b(i,j) enddo enddo #ifndef MIRROR if (me.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) #else if (iproc.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) #endif call ga_add(0.1d0, g_a, 0.9d0, g_b, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if(MISMATCH(b(i,j), a(i,j)))then write(6,*) ' add ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' add is OK ' write(6,*) endif call ga_sync() c c Check the ddot function c if (me .eq. 0) then write(6,19) 19 format('> Checking ddot ...') call ffflush(6) endif crap = drand(12345) ! Everyone has same seed sum1 = 0.0d0 do j = 1, n do i = 1, n b(i,j) = drand(0) sum1 = sum1 + a(i,j)*b(i,j) enddo enddo #ifndef MIRROR if (me.eq.0) then #else if (iproc.eq.0) then #endif call ga_put(g_b, 1, n, 1, n, b, n) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() sum2 = ga_ddot(g_a,g_b) if(MISMATCH(sum1, sum2))then write(6,*) ' ddot wrong ', sum1, sum2 call ffflush(6) call ga_error('... exiting ',-1) else if (me.eq.0) then write(6,*) write(6,*) ' ddot is OK ' write(6,*) endif c c Check the ga_scale function c if (me .eq. 0) then write(6,92) 92 format('> Checking scale ...') call ffflush(6) endif call ga_scale(g_a, 0.123d0) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n a(i,j) = a(i,j)*0.123d0 if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' dscal ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' scale is OK ' write(6,*) endif c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c call ga_sync() if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_sync() #ifndef MIRROR itmp = iran(nproc)-1 if(me.eq.itmp) then #else itmp = iran(lprocs)-1 if(me.eq.itmp) then #endif #ifndef NGA_GATSCAT do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo call ga_gather(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',-1) endif enddo #else do loop = 1,m ilo = iran(n) jlo = iran(n) ijv(1,loop) = ilo ijv(2,loop) = jlo enddo call nga_gather(g_a, v, ijv, m) do loop = 1,m ilo= ijv(1,loop) jlo= ijv(2,loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',-1) endif enddo #endif endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_sync() #ifndef MIRROR if(me.eq.iran(ga_nnodes())-1) then #else if(iproc.eq.iran(lprocs)-1) then #endif #ifndef NGA_GATSCAT do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo c v(loop) = DSIN(a(ilo,jlo)+b(ilo,jlo)) v(loop) = 1d0 *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) c if(v(loop) .ne. w(loop))then if(w(loop) .ne. 1d0 *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', 1d0 *(ilo+jlo) call ffflush(6) call ga_error('... exiting ',-1) endif enddo #else do loop = 1,m ilo = iran(n) jlo = iran(n) ijv(1,loop) = ilo ijv(2,loop) = jlo c v(loop) = DSIN(a(ilo,jlo)+b(ilo,jlo)) v(loop) = 1d0 *(ilo+jlo) enddo call nga_scatter(g_a, v, ijv, m) do loop = 1,m ilo= ijv(1,loop) jlo= ijv(2,loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) c if(v(loop) .ne. w(loop))then if(w(loop) .ne. 1d0 *(ilo+jlo) )then write(6,927)me,ilo,jlo,w(loop),1d0 *(ilo+jlo),loop 927 format(i3,' scatter ',i5,',',i5,',',f7.1,',',f7.1,',',i5) call ffflush(6) call ga_error('... exiting ',-1) endif enddo #endif endif call ga_sync() enddo c call ga_sync() c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c call ga_sync() c c scatter-acc available in GA ver. 3.0 #ifdef GA3 if (me.eq.0) then write(6,*) write(6,*) ' checking scatter-accumulate' write(6,*) endif c crap = drand(1234) call ga_zero(g_a) c do j = 1, n do i = 1, n b(i,j) =0. enddo enddo c x = .1d0 ii =n do jj = 1,1 call ga_sync() do loop = 1, ii c generate unique i,j pairs 10 continue i = iran(n) j=iran(n) #ifndef NGA_GATSCAT if (found(i,j, iv, jv, loop-1) ) goto 10 iv(loop) = i jv(loop) = j #else if (nfound(i,j, ijv, loop-1) ) goto 10 ijv(1,loop) = i ijv(2,loop) = j #endif v(loop) = 1d0 *(i+j) #ifndef MIRROR b(i,j) = b(i,j) + nproc*x*v(loop) ! update local ref. array #else b(i,j) = b(i,j) + lprocs*x*v(loop) ! update local ref. array #endif enddo #ifndef NGA_GATSCAT call ga_scatter_acc(g_a,v,iv,jv, ii,x) #else call nga_scatter_acc(g_a,v,ijv,ii,x) #endif c call ga_sync() c c check the result c call ga_get(g_a, 1, n, 1,n, a, n) do loop = 1,ii #ifndef NGA_GATSCAT i = iv(loop) j = jv(loop) #else i = ijv(1,loop) j = ijv(2,loop) #endif if(MISMATCH(a(i,j),b(i,j)))then print *,'Error',i,j,loop,a(i,j),'expected=',b(i,j) * if(me.eq.0)then * do ii=1,loop * print *,'element',ii, iv(ii),jv(ii) * enddo * endif call ga_error('scatter-acc error in trial ',jj) endif enddo call ga_sync() enddo call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' scatter-accumulate is OK' write(6,*) endif #endif #ifdef MIRROR c c Check the merge function c do j = 1, n do i = 1, n a(i,j) = inode + i-1 + (j-1)*n enddo enddo if (me .eq. 0) then write(6,23) 23 format('> Checking merge ...') call ffflush(6) endif if (iproc.eq.0) then call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_merge_mirrored(g_a) call ga_get(g_a, 1, n, 1, n, b, n) lsame = .true. ilo = 0 ihi = nnodes*(nnodes-1)/2 do i = 1, n do j = 1, n if ((dble(nnodes)*(a(j,i)-dble(inode)) + + dble(ihi)).ne.b(j,i)) then lsame = .false. ilo = ilo + 1 endif end do end do if(lsame.and.me.eq.0)then write(6,*) write(6,*) ' merge is OK ' write(6,*) else if (.not.lsame) then write(6,*) write(6,*) ' merge is wrong ',ilo write(6,*) call ga_error('... exiting ',-1) endif c c Checking copy between different array configurations c if (me .eq. 0) then write(6,20) 20 format('> Checking mixed copy ...') call ffflush(6) endif #ifdef NEW_API g_c = ga_create_handle() call ga_set_data(g_c,ndim,dims,MT_DBL) call ga_set_array_name(g_c,'c') #ifdef BLOCK_CYCLIC #if defined(USE_SCALAPACK_DISTR) || defined(USE_TILED_DISTR) || defined(USE_TILED_IRREG_DISTR) #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_c,block_size,proc_grid) #else #ifdef USE_TILE_DISTR call ga_set_tiled_proc_grid(g_c,block_size,proc_grid) #else call ga_set_tiled_irreg_proc_grid(g_c,mapc,nblocks,proc_grid) #endif #endif #else call ga_set_block_cyclic(g_c,block_size) #endif #endif # ifdef USE_MEMDEV call ga_set_memory_dev(g_c,MEMDEVICE) # endif if (.not.ga_allocate(g_c)) then #else if (.not. nga_create(MT_DBL, ndim, dims, 'c', chunk, g_c)) then #endif call ga_error('ga_create failed for third array ',-1) endif call ga_zero(g_c) call ga_copy(g_a,g_c) call ga_get(g_a, 1, n, 1, n, a, n) call ga_get(g_c, 1, n, 1, n, b, n) do i = 1, n do j = 1, n if (a(j,i).ne.b(j,i)) then lsame = .false. endif end do end do if(lsame.and.me.eq.0)then write(6,*) write(6,*) ' Mixed copy (mirrored to normal) is OK ' write(6,*) elseif (me.eq.0) then write(6,*) write(6,*) ' Mixed copy (mirrored to normal) is wrong ',ilo write(6,*) call ga_error('... exiting ',-1) endif call ga_zero(g_a) call ga_copy(g_c,g_a) call ga_get(g_a, 1, n, 1, n, a, n) do i = 1, n do j = 1, n if (a(j,i).ne.b(j,i)) then lsame = .false. endif end do end do if(lsame.and.me.eq.0)then write(6,*) write(6,*) ' Mixed copy (normal to mirrored) is OK ' write(6,*) elseif (me.eq.0) then write(6,*) write(6,*) ' Mixed copy (normal to mirrored) is wrong ',ilo write(6,*) call ga_error('... exiting ',-1) endif c c Check the merge to distributed patch function c do j = 1, n do i = 1, n a(i,j) = inode + i-1 + (j-1)*n enddo enddo if (me .eq. 0) then write(6,24) 24 format('> Checking merge to distributed patch ...') call ffflush(6) endif c c get low and high indices of patch c ilo = n/4 ihi = ilo + n/2 do i = 1, 2 alo(i) = ilo blo(i) = ilo + 1 ahi(i) = ihi bhi(i) = ihi + 1 end do if (iproc.eq.0) then call ga_put(g_a, 1, n, 1, n, a, n) endif call nga_merge_distr_patch(g_a, alo, ahi, g_c, blo, bhi) call ga_get(g_c, 1, n, 1, n, b, n) lsame = .true. ilo = 0 ihi = nnodes*(nnodes-1)/2 do i = alo(1), ahi(1) ii = i-alo(1) + blo(1) do j = alo(2), ahi(2) jj = j-alo(2) + blo(2) if ((dble(nnodes)*(a(j,i)-dble(inode)) + + dble(ihi)).ne.b(jj,ii)) then c if (me.eq.0) then c write(6,*) i,j,ii,jj, nnodes*a(j,i)+ihi, b(jj,ii) c endif lsame = .false. ilo = ilo + 1 endif end do end do if(lsame.and.me.eq.0)then write(6,*) write(6,*) ' merge to distributed patch is OK ' write(6,*) elseif (.not.lsame) then write(6,*) write(6,*) ' merge to distributed patch is wrong ',ilo write(6,*) call ga_error('... exiting ',-1) endif #endif c c Delete the global arrays c status = ga_destroy(g_b) status = ga_destroy(g_a) c end c----------------------------------------------------------------- subroutine check_complex_float() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,m parameter (n = 60) parameter (m = 2*n) complex a(n,n), b(n,n), v(m),w(m) #ifdef MIRROR integer ndim, dims(2), chunk(2), p_mirror #else # ifdef NEW_API integer ndim, dims(2), chunk(2), p_mirror # endif #endif integer iv(m), jv(m) logical status integer g_a, g_b integer iran, i,j, loop,nloop,maxloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, ii, jj, nnodes parameter (maxloop = 100) integer maxproc parameter (maxproc = 4096) double precision crap, real double precision nwords complex x, sum1, sum2, factor integer lprocs, inode, iproc, lproc #ifdef USE_RESTRICTED integer num_rstrctd integer rstrctd_list(maxproc/2) #endif intrinsic int iran(i) = int(drand(0)*real(i)) + 1 c nproc = ga_nnodes() me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) nloop = Min(maxloop,n) #ifdef USE_RESTRICTED num_rstrctd = nproc/2 if (num_rstrctd.eq.0) num_rstrctd = 1 do i = 1, num_rstrctd rstrctd_list(i) = (num_rstrctd/2) + i-1 end do #endif c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n #ifndef MIRROR a(i,j) = cmplx(real(i-1), real((j-1)*n)) #else a(i,j) = cmplx(real(inode),0.0d00) + + cmplx(real(i-1), real((j-1)*n)) #endif b(i,j) = cmplx(-1d0,1d0) enddo enddo c c Create a global array c c print *,ga_nodeid(), ' creating array' call ffflush(6) c call setdbg(1) #ifdef NEW_API ndim = 2 dims(1) = n dims(2) = n g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_SCPL) call ga_set_array_name(g_a,'a') #ifdef USE_RESTRICTED call ga_set_restricted(g_a, rstrctd_list, num_rstrctd) #endif # ifdef MIRROR p_mirror = ga_pgroup_get_mirror() call ga_set_pgroup(g_a,p_mirror) # endif # ifdef USE_MEMDEV call ga_set_memory_dev(g_a,MEMDEVICE) # endif status = ga_allocate(g_a) #else # ifndef MIRROR status = ga_create(MT_SCPL, n, n, 'a', 0, 0, g_a) # else ndim = 2 dims(1) = n dims(2) = n chunk(1) = 0 chunk(2) = 0 p_mirror = ga_pgroup_get_mirror() status = nga_create_config(MT_SCPL, ndim, dims, 'a', chunk, + p_mirror, g_a) # endif #endif if (.not. status) then write(6,*) ' ga_create failed' call ga_error('... exiting ',-1) endif #ifdef NEW_API g_b = ga_create_handle() call ga_set_data(g_b,ndim,dims,MT_SCPL) call ga_set_array_name(g_b,'b') # ifdef MIRROR call ga_set_pgroup(g_b,p_mirror) # endif # ifdef USE_MEMDEV call ga_set_memory_dev(g_b,MEMDEVICE) # endif if (.not.ga_allocate(g_b)) then #else # ifndef MIRROR if (.not. ga_create(MT_SCPL, n, n, 'b', 0, 0, g_b)) then # else if (.not. nga_create_config(MT_SCPL, ndim, dims, 'b', chunk, _ p_mirror, g_b)) then # endif #endif call ga_error('ga_create failed for second array ',-1) endif #ifndef MIRROR call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) #else lproc = me - ga_cluster_procid(inode,0) call ga_distribution(g_a, lproc, ilo, ihi, jlo, jhi) #endif call ga_sync() c c Zero the array c if (me .eq. 0) then write(6,21) 21 format(/'> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_sync() do i = 1, n do j = 1, n if(b(i,j).ne.(0d0,0d0)) then write(6,*) me,' zero ', i, j, b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format(/'> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/4 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) c do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif call ga_sync() c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format(/'> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif do j = jlo, jhi do i = ilo, ihi if (b(i,j) .ne. a(i,j)) then write(6,*)'error:', i, j, b(i,j), a(i,j) call ga_error('... exiting ',-1) endif enddo enddo c enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) call ffflush(6) endif call ga_sync() c c Each node accumulates into disjoint sections of the array c if (me .eq. 0) then write(6,9) 9 format(/'> Checking accumulate ... ') call ffflush(6) endif call ga_sync() c crap = drand(12345) ! Same seed for each process do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0),drand(1)) b(i,j) = cmplx(real(i),real(j)) enddo enddo c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc c x = cmplx(drand(0),0.333d0) c x = cmplx(0.333d0,0) * x = cmplx(0d0,0d0) x = 0 ilo = i ihi = min(i+inc-1, n) if(ihi.eq.n-1)ihi=n jlo = j jhi = min(j+inc-1, n) if(jhi.eq.n-1)jhi=n #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif call ga_acc(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n, x) endif ij = ij + 1 c c Each process applies all updates to its local copy c do jj = jlo, jhi do ii = ilo, ihi a(ii,jj) = a(ii,jj) + x * b(ii,jj) enddo enddo enddo enddo call ga_sync() c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (MISMATCHF(b(i,j), a(i,j)))then write(6,*) ' acc ', me, i, j, a(i,j), b(i,j) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' disjoint ga_acc is OK' write(6,*) endif c c overlapping accumulate c call ga_zero(g_b) call ga_acc(g_b, n/2, n/2, n/2, n/2, (1,-1), 1, (1,0)) call ga_sync() #ifndef MIRROR if (me.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, x, 1) if (MISMATCHF(x, ((1d0,-1d0)*nproc)))then #else if (iproc.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, x, 1) if (MISMATCHF(x, ((1d0,-1d0)*lprocs)))then #endif c if(error.gt. (1d-8))then #ifndef MIRROR write(6,*)'val=',x,' expected=(',nproc,',',-nproc,')' #else write(6,*)'val=',x,' expected=(',lprocs,',',-lprocs,')' #endif call ga_error('overlapping accumulate failed',-1) endif write(6,*) write(6,*) ' overlapping ga_acc is OK' write(6,*) endif c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif call ga_sync() #ifndef MIRROR if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) #else if(iproc.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) #endif call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c c c Check the ga_scale function c if (me .eq. 0) then write(6,92) 92 format(/'> Checking scale ...') call ffflush(6) endif factor = (1d0,-1d0) call ga_scale(g_a, factor) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n a(i,j) = a(i,j)*factor if (MISMATCHF(b(i,j), a(i,j)))then write(6,*) ' fscal ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' scale is OK ' write(6,*) endif c c Check scatter&gather c call ga_sync() if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_sync() #ifndef MIRROR itmp = iran(nproc)-1 if(me.eq.itmp) then #else itmp = iran(lprocs)-1 if(iproc.eq.itmp) then #endif do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo call ga_gather(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',-1) endif enddo endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_sync() #ifndef MIRROR if(me.eq.iran(ga_nnodes())-1) then #else if(me.eq.iran(lprocs)-1) then #endif do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo v(loop) = (1d0,-1d0) *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) if(w(loop) .ne. (1d0,-1d0) *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', (1d0,-1d0) *(ilo+jlo) call ffflush(6) endif enddo endif call ga_sync() enddo c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c c Check ga_add c if (me .eq. 0) then write(6,91) 91 format(/'> Checking add ...') call ffflush(6) endif call ga_get(g_a, 1, n, 1, n, a, n) crap = drand(12345) ! Everyone has same seed do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0), drand(1)) a(i,j) = (0.1d0,-.1d0)*a(i,j) + (.9d0,-.9d0)*b(i,j) enddo enddo #ifndef MIRROR if (me.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) #else if (iproc.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) #endif call ga_add((0.1,-.1), g_a, (0.9,-.9), g_b, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (MISMATCHF(b(i,j), a(i,j)))then write(6,*) ' add ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' add is OK ' write(6,*) endif call ga_sync() c c Check the cdot function c if (me .eq. 0) then write(6,19) 19 format(/'> Checking cdot ...') call ffflush(6) endif crap = drand(12345) ! Everyone has same seed sum1 = (0.0d0,0.d0) do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0), drand(1)) sum1 = sum1 + a(i,j)*b(i,j) enddo enddo #ifndef MIRROR if (me.eq.0) then #else if (iproc.eq.0) then #endif call ga_put(g_b, 1, n, 1, n, b, n) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() sum2 = ga_cdot(g_a,g_b) if (MISMATCHF(sum1, sum2))then write(6,*) ' cdot wrong ', sum1, sum2 call ffflush(6) call ga_error('... exiting ',-1) else if (me.eq.0) then write(6,*) write(6,*) ' cdot is OK ' write(6,*) endif c c Delete the global arrays c status = ga_destroy(g_b) status = ga_destroy(g_a) c end c----------------------------------------------------------------- subroutine check_complex() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,m parameter (n = 60) parameter (m = 2*n) double complex a(n,n), b(n,n), v(m),w(m) #ifdef MIRROR integer ndim, dims(2), chunk(2), p_mirror #else # ifdef NEW_API integer ndim, dims(2), chunk(2), p_mirror # endif #endif integer iv(m), jv(m) logical status integer g_a, g_b integer iran, i,j, loop,nloop,maxloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, ii, jj, nnodes parameter (maxloop = 100) integer maxproc parameter (maxproc = 4096) double precision crap, real double precision nwords double complex x, sum1, sum2, factor integer lprocs, inode, iproc, lproc #ifdef USE_RESTRICTED integer num_rstrctd integer rstrctd_list(maxproc/2) #endif #ifdef BLOCK_CYCLIC integer block_size(2), proc_grid(2) #endif #ifdef USE_TILED_IRREG_DISTR integer mapc(4096), nblocks(2) #endif intrinsic int iran(i) = int(drand(0)*real(i)) + 1 c nproc = ga_nnodes() me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) nloop = Min(maxloop,n) #ifdef USE_RESTRICTED num_rstrctd = nproc/2 if (num_rstrctd.eq.0) num_rstrctd = 1 do i = 1, num_rstrctd rstrctd_list(i) = (num_rstrctd/2) + i-1 end do #endif #ifdef BLOCK_CYCLIC block_size(1) = 32 block_size(2) = 32 #if defined(USE_SCALAPACK_DISTR) || defined(USE_TILED_DISTR) if (mod(nproc,2).ne.0) + call ga_error("Available procs must be divisible by 2",-1) proc_grid(1) = 2 proc_grid(2) = nproc/2 #endif #endif c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n #ifndef MIRROR a(i,j) = cmplx(dble(i-1), dble((j-1)*n)) #else a(i,j) = cmplx(dble(inode),0.0d00) + + cmplx(dble(i-1), dble((j-1)*n)) #endif b(i,j) = cmplx(-1d0,1d0) enddo enddo c c Create a global array c c print *,ga_nodeid(), ' creating array' call ffflush(6) c call setdbg(1) #ifdef NEW_API ndim = 2 dims(1) = n dims(2) = n g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_DCPL) call ga_set_array_name(g_a,'a') #ifdef USE_RESTRICTED call ga_set_restricted(g_a, rstrctd_list, num_rstrctd) #endif #ifdef BLOCK_CYCLIC #if defined(USE_SCALAPACK_DISTR) || defined(USE_TILED_DISTR) || defined(USE_TILED_IRREG_DISTR) #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_a,block_size,proc_grid) #else #ifdef USE_TILED_DISTR call ga_set_tiled_proc_grid(g_a,block_size,proc_grid) #else call util_create_map(mapc,block_size(1),dims,nblocks,proc_grid) call ga_set_tiled_irreg_proc_grid(g_a,mapc,nblocks,proc_grid) #endif #endif #else call ga_set_block_cyclic(g_a,block_size) #endif #endif # ifdef MIRROR p_mirror = ga_pgroup_get_mirror() call ga_set_pgroup(g_a,p_mirror) # endif # ifdef USE_MEMDEV call ga_set_memory_dev(g_a,MEMDEVICE) # endif status = ga_allocate(g_a) #else # ifndef MIRROR status = ga_create(MT_DCPL, n, n, 'a', 0, 0, g_a) # else ndim = 2 dims(1) = n dims(2) = n chunk(1) = 0 chunk(2) = 0 p_mirror = ga_pgroup_get_mirror() status = nga_create_config(MT_DCPL, ndim, dims, 'a', chunk, + p_mirror, g_a) # endif #endif if (.not. status) then write(6,*) ' ga_create failed' call ga_error('... exiting ',-1) endif #ifdef NEW_API g_b = ga_create_handle() call ga_set_data(g_b,ndim,dims,MT_DCPL) call ga_set_array_name(g_b,'b') #ifdef BLOCK_CYCLIC #if defined(USE_SCALAPACK_DISTR) || defined(USE_TILED_DISTR) || defined(USE_TILED_IRREG_DISTR) #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_b,block_size,proc_grid) #else #ifdef USE_TILED_DISTR call ga_set_tiled_proc_grid(g_b,block_size,proc_grid) #else call ga_set_tiled_irreg_proc_grid(g_b,mapc,nblocks,proc_grid) #endif #endif #else call ga_set_block_cyclic(g_b,block_size) #endif #endif # ifdef MIRROR call ga_set_pgroup(g_b,p_mirror) # endif # ifdef USE_MEMDEV call ga_set_memory_dev(g_b,MEMDEVICE) # endif if (.not.ga_allocate(g_b)) then #else # ifndef MIRROR if (.not. ga_create(MT_DCPL, n, n, 'b', 0, 0, g_b)) then # else if (.not. nga_create_config(MT_DCPL, ndim, dims, 'b', chunk, _ p_mirror, g_b)) then # endif #endif call ga_error('ga_create failed for second array ',-1) endif #ifndef MIRROR call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) #else lproc = me - ga_cluster_procid(inode,0) call ga_distribution(g_a, lproc, ilo, ihi, jlo, jhi) #endif call ga_sync() c c Zero the array c if (me .eq. 0) then write(6,21) 21 format('> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_sync() do i = 1, n do j = 1, n if(b(i,j).ne.(0d0,0d0)) then write(6,*) me,' zero ', i, j, b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format('> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c call util_qfill(n*n, (0d0,0d0), b, 1) call ga_get(g_a, 1, n, 1, n, b, n) c do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif call ga_sync() c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format('> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_qfill(n*n, (0.0d0,0d0), b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif do j = jlo, jhi do i = ilo, ihi if (b(i,j) .ne. a(i,j)) then write(6,*)'error:', i, j, b(i,j), a(i,j) call ga_error('... exiting ',-1) endif enddo enddo c enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) call ffflush(6) endif call ga_sync() c c Each node accumulates into disjoint sections of the array c if (me .eq. 0) then write(6,9) 9 format('> Checking accumulate ... ') call ffflush(6) endif call ga_sync() c crap = drand(12345) ! Same seed for each process do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0),drand(1)) b(i,j) = cmplx(dble(i),dble(j)) enddo enddo c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc c x = cmplx(drand(0),0.333d0) c x = cmplx(0.333d0,0) * x = cmplx(0d0,0d0) x = 0 ilo = i ihi = min(i+inc-1, n) if(ihi.eq.n-1)ihi=n jlo = j jhi = min(j+inc-1, n) if(jhi.eq.n-1)jhi=n #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif call ga_acc(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n, x) endif ij = ij + 1 c c Each process applies all updates to its local copy c do jj = jlo, jhi do ii = ilo, ihi a(ii,jj) = a(ii,jj) + x * b(ii,jj) enddo enddo enddo enddo call ga_sync() c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' acc ', me, i, j, a(i,j), b(i,j) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' disjoint ga_acc is OK' write(6,*) endif c c overlapping accumulate c call ga_zero(g_b) call ga_acc(g_b, n/2, n/2, n/2, n/2, (1d0,-1d0), 1, (1d0,0d0)) call ga_sync() #ifndef MIRROR if (me.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, x, 1) if (MISMATCH(x, ((1d0,-1d0)*nproc)))then #else if (iproc.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, x, 1) if (MISMATCH(x, ((1d0,-1d0)*lprocs)))then #endif c if(error.gt. (1d-8))then #ifndef MIRROR write(6,*)'val=',x,' expected=(',nproc,',',-nproc,')' #else write(6,*)'val=',x,' expected=(',lprocs,',',-lprocs,')' #endif call ga_error('overlapping accumulate failed',-1) endif write(6,*) write(6,*) ' overlapping ga_acc is OK' write(6,*) endif c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif call ga_sync() #ifndef MIRROR if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) #else if(iproc.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) #endif call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c c c Check the ga_scale function c if (me .eq. 0) then write(6,92) 92 format('> Checking scale ...') call ffflush(6) endif factor = (1d0,-1d0) call ga_scale(g_a, factor) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n a(i,j) = a(i,j)*factor if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' dscal ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' scale is OK ' write(6,*) endif c c Check scatter&gather c call ga_sync() if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_sync() #ifndef MIRROR itmp = iran(nproc)-1 if(me.eq.itmp) then #else itmp = iran(lprocs)-1 if(iproc.eq.itmp) then #endif do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo call ga_gather(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',-1) endif enddo endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_sync() #ifndef MIRROR if(me.eq.iran(ga_nnodes())-1) then #else if(me.eq.iran(lprocs)-1) then #endif do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo v(loop) = (1d0,-1d0) *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) if(w(loop) .ne. (1d0,-1d0) *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', (1d0,-1d0) *(ilo+jlo) call ffflush(6) endif enddo endif call ga_sync() enddo c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c c Check ga_add c if (me .eq. 0) then write(6,91) 91 format('> Checking add ...') call ffflush(6) endif call ga_get(g_a, 1, n, 1, n, a, n) crap = drand(12345) ! Everyone has same seed do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0), drand(1)) a(i,j) = (0.1d0,-.1d0)*a(i,j) + (.9d0,-.9d0)*b(i,j) enddo enddo #ifndef MIRROR if (me.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) #else if (iproc.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) #endif call ga_add((0.1d0,-.1d0), g_a, (0.9d0,-.9d0), g_b, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (MISMATCH(b(i,j), a(i,j)))then write(6,*) ' add ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' add is OK ' write(6,*) endif call ga_sync() c c Check the zdot function c if (me .eq. 0) then write(6,19) 19 format('> Checking zdot ...') call ffflush(6) endif crap = drand(12345) ! Everyone has same seed sum1 = (0.0d0,0.d0) do j = 1, n do i = 1, n b(i,j) = cmplx(drand(0), drand(1)) sum1 = sum1 + a(i,j)*b(i,j) enddo enddo #ifndef MIRROR if (me.eq.0) then #else if (iproc.eq.0) then #endif call ga_put(g_b, 1, n, 1, n, b, n) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() sum2 = ga_zdot(g_a,g_b) if (MISMATCH(sum1, sum2))then write(6,*) ' zdot wrong ', sum1, sum2 call ffflush(6) call ga_error('... exiting ',-1) else if (me.eq.0) then write(6,*) write(6,*) ' zdot is OK ' write(6,*) endif c c Delete the global arrays c status = ga_destroy(g_b) status = ga_destroy(g_a) c end c----------------------------------------------------------------- subroutine check_int() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n parameter (n = 128) integer a(n,n), b(n,n) logical status integer g_a integer iran, i, j, loop, nloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, dimi, dimj, ii, jj integer ndim, dims(2), chunk(2), p_mirror, nnodes integer lprocs, inode, iproc,lproc double precision nwords parameter (nloop = 100) integer maxproc parameter (maxproc = 4096) double precision crap, sum1, real integer buf #ifdef USE_RESTRICTED integer num_rstrctd integer rstrctd_list(maxproc/2) #endif #ifdef BLOCK_CYCLIC integer block_size(2), proc_grid(2) integer map(4,maxproc), block_list(maxproc), found, np,k integer blo(2),bhi(2) #else integer map(5,maxproc), found, np,k #endif #ifdef USE_TILED_IRREG_DISTR integer mapc(4096), nblocks(2) #endif intrinsic int iran(i) = int(drand(0)*real(i)) + 1 c nproc = ga_nnodes() me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) #ifdef USE_RESTRICTED num_rstrctd = nproc/2 if (num_rstrctd.eq.0) num_rstrctd = 1 do i = 1, num_rstrctd rstrctd_list(i) = (num_rstrctd/2) + i-1 end do #endif #ifdef BLOCK_CYCLIC block_size(1) = 32 block_size(2) = 32 #if defined(USE_SCALAPACK_DISTR) || defined(USE_TILED_DISTR) if (mod(nproc,2).ne.0) + call ga_error("Available procs must be divisible by 2",-1) proc_grid(1) = 2 proc_grid(2) = nproc/2 #endif #endif c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n #ifndef MIRROR a(i,j) = i-1 + (j-1)*1000 #else a(i,j) = inode + i-1 + (j-1)*1000 #endif enddo enddo c c Create a global array c ndim = 2 dims(1) = n dims(2) = n chunk(1) = 0 chunk(2) = 0 #ifdef NEW_API g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_INT) call ga_set_array_name(g_a,'a') #ifdef USE_RESTRICTED call ga_set_restricted(g_a, rstrctd_list, num_rstrctd) #endif #ifdef BLOCK_CYCLIC #if defined(USE_SCALAPACK_DISTR) || defined(USE_TILED_DISTR) || defined(USE_TILED_IRREG_DISTR) #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_a,block_size,proc_grid) #else #ifdef USE_TILED_DISTR call ga_set_tiled_proc_grid(g_a,block_size,proc_grid) #else call util_create_map(mapc,block_size(1),dims,nblocks,proc_grid) call ga_set_tiled_irreg_proc_grid(g_a,mapc,nblocks,proc_grid) #endif #endif #else call ga_set_block_cyclic(g_a,block_size) #endif #endif # ifdef MIRROR p_mirror = ga_pgroup_get_mirror() call ga_set_pgroup(g_a,p_mirror) # endif # ifdef USE_MEMDEV call ga_set_memory_dev(g_a,MEMDEVICE) # endif if (.not.ga_allocate(g_a)) then #else # ifndef MIRROR if (.not. ga_create(MT_INT, n, n, 'a', 0, 0, g_a)) then # else p_mirror = ga_pgroup_get_mirror() if (.not. nga_create_config(MT_INT, ndim, dims, 'a', chunk, + p_mirror, g_a)) then # endif #endif write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',-1) endif c c Zero the array c if (me .eq. 0) then write(6,21) 21 format('> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) do i = 1, n do j = 1, n if (b(i,j) .ne. 0) then write(6,*) ' zero ', me, i, j, b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format('> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) c write(6,4) me, ilo, ihi, jlo, jhi c4 format(' node ',i2,' checking put ',4i4) c call ffflush(6) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c #ifndef MIRROR if(me.eq.0)then #else if(iproc.eq.0)then #endif call ga_get(g_a, 1, n, 1, n, b, n) do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo endif call ga_sync() c if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format('> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_ifill(n*n, 0.0d0, b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) c if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif c sum1 = 0.0d0 do j = jlo, jhi do i = ilo, ihi sum1 = sum1 + b(i,j) if (b(i,j) .ne. a(i,j)) then write(6,*) 'error ', i, j, b(i,j), a(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) endif c call ga_sync() if (me .eq. 0 .and. n.gt.7) then write(6,*) write(6,*) '> Checking ga_print_patch --- should see ' write(6,*)' [2002 3002 4002 5002 6002]' write(6,*)' [2003 3003 4003 5003 6003]' write(6,*)' [2004 3004 4004 5004 6004]' write(6,*) call ffflush(6) endif if(n.gt.5) call ga_print_patch(g_a,3,5,3,7,0) c call ga_sync() c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*) '> Checking read_inc ... ' write(6,*) call ffflush(6) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc inc =5 c every processor will be operating on somebody elses data c #ifndef MIRROR lproc = nproc - me - 1 #else lproc = nnodes-iproc-1 lproc = inode*lprocs + lproc lproc = lprocs - me-ga_cluster_procid(inode,0) #endif c #ifndef MIRROR call ga_distribution(g_a,lproc,ilo,ihi,jlo,jhi) #else lproc = me - ga_cluster_procid(inode,0) call ga_distribution(g_a, lproc, ilo, ihi, jlo, jhi) #endif c dimi = ihi-ilo dimj = jhi-jlo c write(6,111) me,ilo,ihi,jlo,jhi,dimi,dimj 111 format(i2,'..',4i8,'.',2i8) call ga_sync() if(ilo .gt.0 .and. jhi .gt. 0)then do loop = 1,nloop ii= IABS(iran(dimi)) - 1 jj= IABS(iran(dimj)) - 1 i=ilo + Mod(ii,dimi) j=jlo + Mod(jj,dimj) c c write(6,*) me,'..',ilo,ihi,jlo,jhi,'.',dimi,dimj,'..',i,j c call ffflush(6) buf = ga_read_inc(g_a,i,j,inc) if(a(i,j).ne. buf)then write(6,114) me,ilo,ihi,jlo,jhi write(6,112) me,i,j,a(i,j),buf,loop c write(6,*)me,'READ_inc ', i,',',j,',', a(i,j),' ',buf,me call ffflush(6) endif 112 format(i3,' READ_inc ',i3,',',i3,',',i8,' ',i8,' loop ',i8) call ga_get(g_a, i,i,j,j, buf,1) a(i,j) = a(i,j)+inc if(a(i,j).ne. buf)then write(6,114) me,ilo,ihi,jlo,jhi write(6,113) me,i,j,a(i,j),buf,loop c write(6,*)me,'read_INC ', i,',',j,',', a(i,j),' ',buf,me call ffflush(6) endif 113 format(i3,' read_INC ',i3,',',i3,',',i8,' ',i8,' loop ',i8) 114 format(i3,' ilo ',i3,' ihi ',i3,' jlo ',i3,' jhi ',i3) enddo endif call ga_sync() c if (me.eq.0) then write(6,*) write(6,*) ' read_inc is OK' write(6,*) endif c c c #ifndef MIRROR if (me.eq.0) then write(6,*) write(6,*) '> checking ga_fence and ga_lock' write(6,*) call ffflush(6) endif c call ga_zero(g_a) c c*** use ga_read_inc and elements g_a(1:2,1) to implement a lock c*** compute g_a(:,n) = sum (1(:)) for P processors c status = ga_create_mutexes(1) if (.not. status) then call ga_error('ga_create_mutexes failed ',-1) endif if(n.lt.2)call ga_error('insufficient n to test ga_fence',n) call ga_lock(0) c call my_lock(g_a) c get original values g_a(:,n) call ga_get(g_a, 1,n, n,n, b,n) c add my contribution do i =1,n b(i,1)= b(i,1)+1 enddo c c need to use fence to assure that coms complete before leaving c Critical Section c call ga_init_fence() call ga_put(g_a, 1,n, n,n, b,n) call ga_fence() call ga_unlock(0) c call my_unlock(g_a) c 333 if(.not.ga_destroy_mutexes()) $ call ga_error('mutex not destroyed',-1) call ga_sync() if (me.eq.0) then call ga_get(g_a, 1,n, n,n, b,n) do i =1,n if(b(i,1).ne. nproc)then print *, 'mismatch',b(i,1),nproc call ga_error('fence failed',i) endif enddo write(6,*) write(6,*) ' ga_fence and ga_lock are OK' write(6,*) endif #endif c c if (me.eq.0) then write(6,*) write(6,*) '> checking ga_locate_region' write(6,*) call ffflush(6) endif #ifndef BLOCK_CYCLIC status = ga_locate_region(g_a, 1, n, 1, n, map, np) found = 0 do j=1,n do i=1,n b(i,j)=-1 enddo enddo #ifndef MIRROR if(me.eq.0)call ga_put(g_a,1,n,1,n,b,n) #else if(iproc.eq.0)call ga_put(g_a,1,n,1,n,b,n) #endif call ga_sync() do k = 1, np #ifndef MIRROR if(map(5,k).eq.me)then #else if(map(5,k).eq.iproc)then #endif if(found.eq.1) then write(6,*)'double entry in map for proc ',me call ffflush(6) endif do j= map(3,k), map(4,k) do i= map(1,k), map(2,k) #ifndef MIRROR b(i,j)=1*me #else b(i,j)=1*iproc #endif enddo enddo call ga_put(g_a, map(1,k),map(2,k),map(3,k),map(4,k), & b(map(1,k),map(3,k)),n) found = 1 endif enddo call ga_sync() c do k = 1, np #ifndef MIRROR if(map(5,k).eq.me)then #else if(map(5,k).eq.iproc)then #endif call ga_get(g_a, map(1,k),map(2,k),map(3,k),map(4,k), & a(map(1,k),map(3,k)),n) do j= map(3,k), map(4,k) do i= map(1,k), map(2,k) if(b(i,j).ne.a(i,j)) then write(6,*) & 'proc ',me, 'overlap with ',a(i,j) call ffflush(6) endif enddo enddo endif enddo call ga_sync() c #ifndef MIRROR if(me.eq.0)then #else if(iproc.eq.0)then #endif call ga_get(g_a,1,n,1,n,a,n) do j=1,n do i=1,n if(a(i,j).eq.-1)then write(6,*)'i=',i,' j=',j, ' not assigned ' call ga_error('... exiting ',-1) endif enddo enddo endif #else c c block cyclic distributions c blo(1) = 1 bhi(1) = n blo(2) = 1 bhi(2) = n status = nga_locate_region(g_a, blo, bhi, map, block_list, np) found = 0 do j=1,n do i=1,n b(i,j)=-1 enddo enddo if(me.eq.0)call ga_put(g_a,1,n,1,n,b,n) call ga_sync() #if !defined(USE_SCALAPACK_DISTR) && !defined(USE_TILED_DISTR) && !defined(USE_TILED_IRREG_DISTR) do k = 1, np if(mod(block_list(k),nproc).eq.me)then do j= map(2,k), map(4,k) do i= map(1,k), map(3,k) b(i,j)=1*me enddo enddo call ga_put(g_a, map(1,k),map(3,k),map(2,k),map(4,k), & b(map(1,k),map(2,k)),n) found = 1 endif enddo call ga_sync() c do k = 1, np if(mod(block_list(k),nproc).eq.me)then call ga_get(g_a, map(1,k),map(3,k),map(2,k),map(4,k), & a(map(1,k),map(2,k)),n) do j= map(2,k), map(4,k) do i= map(1,k), map(3,k) if(b(i,j).ne.a(i,j)) then write(6,*) & 'proc ',me, 'overlap with ',a(i,j) call ffflush(6) endif enddo enddo endif enddo call ga_sync() #else c#if defined(USE_SCALAPACK_DISTR) || defined(USE_TILED_DISTR) do k = 1, np i = (dims(1)-1)/block_size(1); j = (dims(2)-1)/block_size(2); ii = mod(block_list(k),i) jj = (block_list(k)-ii)/i if(mod((ii+i*jj),nproc).eq.me)then do j= map(2,k), map(4,k) do i= map(1,k), map(3,k) b(i,j)=1*me enddo enddo call ga_put(g_a, map(1,k),map(3,k),map(2,k),map(4,k), & b(map(1,k),map(2,k)),n) found = 1 endif enddo call ga_sync() c do k = 1, np i = (dims(1)-1)/block_size(1); j = (dims(2)-1)/block_size(2); ii = mod(block_list(k),i) jj = (block_list(k)-ii)/i if(mod((ii+i*jj),nproc).eq.me)then call ga_get(g_a, map(1,k),map(3,k),map(2,k),map(4,k), & a(map(1,k),map(2,k)),n) do j= map(2,k), map(4,k) do i= map(1,k), map(3,k) if(b(i,j).ne.a(i,j)) then write(6,*) & 'proc ',me, 'overlap with ',a(i,j) call ffflush(6) endif enddo enddo endif enddo call ga_sync() c#endif c#ifdef USE_TILED_IRREG_DISTR c#endif #endif #endif c if(me.eq.0)then call ga_get(g_a,1,n,1,n,a,n) do j=1,n do i=1,n if(a(i,j).eq.-1)then write(6,*)'i=',i,' j=',j, ' not assigned ' call ga_error('... exiting ',-1) endif enddo enddo endif if (me.eq.0) then write(6,*) write(6,*) ' ga_locate_region is OK' write(6,*) call ffflush(6) endif c c Delete the global array c status = ga_destroy(g_a) c end c--------------------------------------------------------------------- subroutine check_flt() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer n, m parameter (n =100) parameter (m=2*n) real a(n,n), b(n,n), v(m), w(m) integer iv(m), jv(m) logical status integer g_a, g_b integer iran, i, j, loop, nloop, maxloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, ii, jj, nnodes double precision nwords parameter (maxloop = 100) integer maxproc integer ndim, dims(2), chunk(2), p_mirror parameter (maxproc = 4096) double precision crap real alpha, beta, x, sum1, sum2 logical found integer lprocs, inode, iproc, lproc #ifdef USE_RESTRICTED integer num_rstrctd integer rstrctd_list(maxproc/2) #endif #ifdef BLOCK_CYCLIC integer block_size(2), proc_grid(2) #endif intrinsic int iran(i) = int(drand(0)*real(i)) + 1 nproc = ga_nnodes() me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) nloop = Min(maxloop,n) #ifdef USE_RESTRICTED num_rstrctd = nproc/2 if (num_rstrctd.eq.0) num_rstrctd = 1 do i = 1, num_rstrctd rstrctd_list(i) = (num_rstrctd/2) + i-1 end do #endif #ifdef BLOCK_CYCLIC block_size(1) = 32 block_size(2) = 32 #if defined(USE_SCALAPACK_DISTR) || defined(USE_TILED_DISTR) if (mod(nproc,2).ne.0) + call ga_error("Available procs must be divisible by 2",-1) proc_grid(1) = 2 proc_grid(2) = nproc/2 #endif #endif c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n #ifndef MIRROR a(i,j) = i-1 + (j-1)*n #else a(i,j) = inode + i-1 + (j-1)*n #endif b(i,j) = -1. enddo enddo c c Create a global array c ndim = 2 dims(1) = n dims(2) = n chunk(1) = 0 chunk(2) = 0 #ifdef NEW_API g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_REAL) call ga_set_array_name(g_a,'a') #ifdef USE_RESTRICTED call ga_set_restricted(g_a, rstrctd_list, num_rstrctd) #endif #ifdef BLOCK_CYCLIC #if defined(USE_SCALAPACK_DISTR) || defined(USE_TILED_DISTR) #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_a,block_size,proc_grid) #else call ga_set_tiled_proc_grid(g_a,block_size,proc_grid) #endif #else call ga_set_block_cyclic(g_a,block_size) #endif #endif # ifdef MIRROR write(6,*) 'Getting p_mirror' p_mirror = ga_pgroup_get_mirror() write(6,*) 'Setting proc config' call ga_set_pgroup(g_a,p_mirror) # endif # ifdef USE_MEMDEV call ga_set_memory_dev(g_a,MEMDEVICE) # endif if (.not.ga_allocate(g_a)) then #else # ifndef MIRROR if (.not. ga_create(MT_REAL, n, n, 'a', 0, 0, g_a)) then # else cBJP p_mirror = ga_pgroup_get_mirror() if (.not. nga_create_config(MT_REAL, ndim, dims, 'a', chunk, + p_mirror, g_a)) then cBJP write(6,*) 'Created g_a' # endif #endif write(6,*) ' ga_create failed' call ffflush(6) call ga_error('... exiting ',-1) endif c c check if handle is valid. be quiet unless error C if(.not.ga_valid_handle(g_a)) call ga_error("invalid handle",g_a) c #ifndef MIRROR call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) #else lproc = me - ga_cluster_procid(inode,0) call ga_distribution(g_a, lproc, ilo, ihi, jlo, jhi) #endif call ga_sync() c c Zero the array c if (me .eq. 0) then write(6,21) 21 format('> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_sync() do i = 1, n do j = 1, n if (b(i,j) .ne. 0.0) then write(6,*) ' zero ', me, i, j, b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format('> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) c write(6,4) me, ilo, ihi, jlo, jhi c 4 format(' node ',i2,' checking put ',4i4) c call ffflush(6) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo call ga_sync() c if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format('> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_rfill(n*n, 0.0, b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) c if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif c sum1 = 0.0d0 do j = jlo, jhi do i = ilo, ihi sum1 = sum1 + b(i,j) if (b(i,j) .ne. a(i,j)) then write(6,*) 'error ', i, j, b(i,j), a(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) endif call ga_sync() c c Each node accumulates into disjoint sections of the array c if (me .eq. 0) then write(6,9) 9 format('> Checking accumulate ... ') call ffflush(6) endif call ga_sync() c crap = drand(12345) ! Same seed for each process do j = 1, n do i = 1, n c b(i,j) = real(drand(0)) b(i,j) = i+j enddo enddo c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc c x = real(drand(0)) x = 10. ilo = i ihi = min(i+inc-1, n) if(ihi.eq.n-1)ihi=n c ihi = min(i+inc, n) jlo = j jhi = min(j+inc-1, n) if(jhi.eq.n-1)jhi=n c jhi = min(j+inc-1, n) * call ffflush(6) #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif c print *, me, 'checking accumulate ',ilo,ihi,jlo,jhi,x * 11 format(' node ',i2,' checking accumulate ',4i4) * call ffflush(6) call ga_acc(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n, x) endif ij = ij + 1 c c Each process applies all updates to its local copy c do jj = jlo, jhi do ii = ilo, ihi a(ii,jj) = a(ii,jj) + x * b(ii,jj) enddo enddo enddo enddo call ga_sync() c c All nodes check all of a call ga_get(g_a, 1, n, 1, n, b, n) c do j = 1, n do i = 1, n if(MISMATCHF(b(i,j),a(i,j)))then write(6,*) ' acc ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' disjoint ga_acc is OK' write(6,*) endif c c overlapping accumulate call ga_sync() #ifndef MIRROR if (.not. ga_create(MT_REAL, n, n, 'b', 0, 0, g_b)) then #else if (.not. nga_create_config(MT_REAL, ndim, dims, 'b', chunk, + p_mirror, g_b)) then #endif call ga_error('ga_create failed for second array ',-1) endif c call ga_zero(g_b) call ga_acc(g_b, n/2, n/2, n/2, n/2, 1.0, 1, 1.0) call ga_sync() if (me.eq.0) then call ga_get(g_b, n/2, n/2, n/2, n/2, b(1,1), 1) #ifndef MIRROR x = abs(b(1,1) -1*nproc) #else x = abs(b(1,1) -1*lprocs) #endif if(x.gt. 1e-10)then #ifndef MIRROR write(6,*)'val=',b(1,1),' expected=',nproc, x #else write(6,*)'val=',b(1,1),' expected=',lprocs, x #endif call ga_error('overlapping accumulate failed',-1) endif write(6,*) write(6,*) ' overlapping ga_acc is OK' write(6,*) endif c c Check the ga_add function c if (me .eq. 0) then write(6,91) 91 format('> Checking add ...') call ffflush(6) endif c crap = drand(12345) ! Everyone has same seed do j = 1, n do i = 1, n b(i,j) = real(drand(0)*real(i)) + 1 a(i,j) = 0.1*a(i,j) + 0.9*b(i,j) enddo enddo #ifndef MIRROR if (me.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) #else if (iproc.eq.0) call ga_put(g_b, 1, n, 1, n, b, n) #endif alpha = 0.1 beta = 0.9 call ga_add(alpha, g_a, beta, g_b, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if(MISMATCHF(b(i,j), a(i,j)))then write(6,*) ' add ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' add is OK ' write(6,*) endif call ga_sync() c c Check the sdot function c if (me .eq. 0) then write(6,19) 19 format('> Checking sdot ...') call ffflush(6) endif crap = drand(12345) ! Everyone has same seed sum1 = 0.0 do j = 1, n do i = 1, n b(i,j) = drand(0) sum1 = sum1 + a(i,j)*b(i,j) enddo enddo #ifndef MIRROR if (me.eq.0) then #else if (iproc.eq.0) then #endif call ga_put(g_b, 1, n, 1, n, b, n) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() sum2 = ga_sdot(g_a,g_b) if(MISMATCHF(sum1, sum2))then write(6,*) ' fdot wrong ', sum1, sum2 call ffflush(6) call ga_error('... exiting ',-1) else if (me.eq.0) then write(6,*) write(6,*) ' sdot is OK ' write(6,*) endif c c Check the ga_scale function c if (me .eq. 0) then write(6,92) 92 format('> Checking scale ...') call ffflush(6) endif call ga_scale(g_a, 0.123) call ga_get(g_a, 1, n, 1, n, b, n) do j = 1, n do i = 1, n a(i,j) = a(i,j)*0.123 if (MISMATCHF(b(i,j), a(i,j)))then write(6,*) ' dscal ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' scale is OK ' write(6,*) endif c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ffflush(6) call ga_error('... exiting ',-1) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c call ga_sync() if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_sync() #ifndef MIRROR itmp = iran(nproc)-1 if(me.eq.itmp) then #else itmp = iran(lprocs)-1 if(iproc.eq.itmp) then #endif do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo call ga_gather(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',-1) endif enddo endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_sync() #ifndef MIRROR if(me.eq.iran(ga_nnodes())-1) then #else if(iproc.eq.iran(lprocs)-1) then #endif do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo c v(loop) = DSIN(a(ilo,jlo)+b(ilo,jlo)) v(loop) = 1.0 *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) c if(v(loop) .ne. w(loop))then if(w(loop) .ne. 1.0 *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', 1.0 *(ilo+jlo) call ffflush(6) call ga_error('... exiting ',-1) endif enddo endif call ga_sync() enddo c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c call ga_sync() c c scatter-acc available in GA ver. 3.0 #ifdef GA3 if (me.eq.0) then write(6,*) write(6,*) ' checking scatter-accumulate' write(6,*) endif c crap = drand(1234) call ga_zero(g_a) c do j = 1, n do i = 1, n b(i,j) =0. enddo enddo c x = .1d0 ii =n do jj = 1,1 call ga_sync() do loop = 1, ii c generate unique i,j pairs 10 continue i = iran(n) j=iran(n) if (found(i,j, iv, jv, loop-1) ) goto 10 iv(loop) = i jv(loop) = j v(loop) = 1.0 *(i+j) #ifndef MIRROR b(i,j) = b(i,j) + nproc*x*v(loop) ! update local ref. array #else b(i,j) = b(i,j) + lprocs*x*v(loop) ! update local ref. array #endif enddo call ga_scatter_acc(g_a,v,iv,jv, ii,x) c call ga_sync() c c check the result c call ga_get(g_a, 1, n, 1,n, a, n) do loop = 1,ii i = iv(loop) j = jv(loop) if(MISMATCHF(a(i,j),b(i,j)))then print *,'Error',i,j,loop,a(i,j),'expected=',b(i,j) * if(me.eq.0)then * do ii=1,loop * print *,'element',ii, iv(ii),jv(ii) * enddo * endif call ga_error('scatter-acc error in trial ',jj) endif enddo call ga_sync() enddo call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' scatter-accumulate is OK' write(6,*) endif #endif c c Delete the global array c status = ga_destroy(g_a) c end c_____________________________________________________________ subroutine check_wrappers implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" integer n parameter (n=10000) double precision sum(n) double complex zsum(n), check(n) integer isum(n), ibuf(2) integer me, nproc, i real fsum(n) c nproc = ga_nnodes() me = ga_nodeid() c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_gop (integer)' write(6,*) call ffflush(6) endif do i = 1, n isum(i) = i + me enddo call ga_gop(MT_INT, isum, n, '+') do i = 1, n if(isum(i).ne.(i*nproc + (nproc-1)*nproc/2))then call ga_error('ga_gop error -2',isum(i)) endif enddo call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' ga_gop (integer) is OK' write(6,*) endif call ga_sync() c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_gop (double)' write(6,*) call ffflush(6) endif do i = 1, n sum(i) = i + 1d0 * me enddo call ga_gop(MT_DBL, sum, n, '+') do i = 1, n if(Int(sum(i)).ne.(i*nproc + (nproc-1)*nproc/2))then call ga_error('ga_gop error',Int(sum(i))) endif enddo call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' ga_gop (double) is OK' write(6,*) endif c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_gop (real)' write(6,*) call ffflush(6) endif do i = 1, n fsum(i) = i + 1.0 * me enddo call ga_gop(MT_REAL, fsum, n, '+') do i = 1, n if(Int(fsum(i)).ne.(i*nproc + (nproc-1)*nproc/2))then call ga_error('ga_gop error',Int(fsum(i))) endif enddo call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' ga_gop (real) is OK' write(6,*) endif c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_gop (double complex)' write(6,*) call ffflush(6) endif do i = 1, n zsum(i) = cmplx(i+1d0, i + 1d0 * me) check(i)= cmplx(nproc*i+nproc*1d0, nproc*i+(nproc-1)*nproc/2) enddo call ga_gop(MT_DCPL, zsum, n, '+') do i = 1, n if(zsum(i).ne.check(i)) then call ga_error('ga_gop error', -1) endif enddo call ga_sync() if (me.eq.0) then write(6,*) write(6,*) ' ga_gop (double complex) is OK' write(6,*) endif c call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> Checking ga_brdcst' write(6,*) call ffflush(6) endif if(me.eq.nproc-1)then ibuf(1) = me ibuf(2) = nproc endif call ga_brdcst(1000,ibuf,util_mitob(2),nproc-1) if(ibuf(1).ne.nproc-1)call ga_error('ibuf(1) error',ibuf(1)) if(ibuf(2).ne.nproc)call ga_error('ibuf(2) error',ibuf(2)) call ga_sync() if (me .eq. 0) then write(6,*) write(6,*)'> ga_brdcst is OK ' write(6,*) call ffflush(6) endif call ga_sync() end subroutine check_mem(mem_size) implicit none integer mem_size #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,nmax,left,need, me,procs,g_a, g_b logical status c if(.not. ga_memory_limited())return me = ga_nodeid() procs = ga_nnodes() nmax = int(dsqrt(dble(mem_size/util_mitob(1)))) left = mem_size/procs - ga_inquire_memory() n = nmax/2 need = util_mdtob(n*n)/procs c if(me.eq.0)then write(6,*)' ' if(ga_uses_ma())then write(6,*)' CHECKING GA MEMORY RESTRICTIONS (MA used)' else write(6,*)' CHECKING GA MEMORY RESTRICTIONS (MA not used)' endif write(6,*)' ' write(6,*)' ' call print_mem_info(n,left, need, mem_size/procs) endif c status = ga_create(MT_DBL, n, n, 'a', 0, 0, g_a) c if(me.eq.0) then if(status) then write(6,*) ' success' else write(6,*) ' failure' endif call ffflush(6) endif c n = nmax left = mem_size/procs - ga_inquire_memory() need = util_mdtob(n*n)/procs if(me.eq.0)then call print_mem_info(n,left, need, mem_size/procs) endif c status = ga_create(MT_DBL, n, n, 'b', 0, 0, g_b) c if(me.eq.0) then if(status) then write(6,*) ' success' else write(6,*) ' failure' endif write(6,*)' ' write(6,*)' ' call ffflush(6) endif status = ga_destroy(g_a) end subroutine print_mem_info(n,left, need, total) implicit none integer n,left, need, total c write(6,*)' ' if(left - need .ge. 0) then write(6,1)n,n 1 format('> Creating array ',i4,' by ',i4,' -- should succeed') else write(6,2)n,n 2 format('> Creating array ',i4,' by ',i4,' -- SHOULD FAIL') endif write(6,3) need, left, total 3 format(' (need ',i7,' and ',i7,' out of ',i7,' bytes are left)') write(6,*)' ' call ffflush(6) c end subroutine my_lock(g_b) implicit none #include "global.fh" integer g_b, val, flag, i logical first_time double precision dummy common /lock/ val common /dum/ dummy data first_time /.true./ c c this awkward initialization is to avoid a weird problem C with block data on SUN if(first_time)then first_time = .false. dummy = .0 endif c val = ga_read_inc(g_b,1,1, 1) 10 call ga_get(g_b, 2,2,1,1, flag, 1) if(flag.eq.val) return c c to reduce memory stress, wait a while before retrying do i = 1, 100 dummy = dummy + .1 enddo goto 10 end subroutine my_unlock(g_b) implicit none #include "global.fh" integer g_b, val common /lock/ val c call ga_put(g_b, 2,2,1,1, val+1, 1) end logical function found(i,j, iv, jv, n) integer n integer i,j, iv(n), jv(n) integer loop found = .false. do loop = 1, n if(i .eq. iv(loop) .and. j .eq.jv(loop))then found = .true. goto 99 endif enddo 99 continue return end logical function nfound(i,j, ijv, n) integer n integer i,j, ijv(2,n) integer loop nfound = .false. do loop = 1, n if(i .eq. ijv(1,loop) .and. j .eq.ijv(2,loop))then nfound = .true. goto 99 endif enddo 99 continue return end subroutine proc_remap() implicit none #include "global.fh" integer proc(100),nproc,i nproc = ga_nnodes() if(nproc.gt.100) $ call ga_error("remap requires<=100 processes",nproc) do i = 1, nproc proc(i) = nproc-i enddo c call ga_register_proclist(proc,nproc) end subroutine util_rfill(n,val,a,ia) implicit none real a(*), val integer n, ia, i c c initialise real array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end subroutine util_dfill(n,val,a,ia) implicit none double precision a(*), val integer n, ia, i c c initialise double precision array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end subroutine util_ifill(n,val,a,ia) implicit none integer n, ia, i, a(*),val c c initialise integer array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end subroutine util_qfill(n,val,a,ia) implicit none double complex a(*), val integer n, ia, i c c initialise double complex array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end subroutine util_create_map(mapc, base, dims, nblocks, proc_grid) implicit none #include "global.fh" integer mapc(4096) integer dims(2), nblocks(2), proc_grid(2) integer primes(256), nprime integer factors(256), nfactors integer base integer i, j, nproc, pmax, px, py, lo, hi logical ok ! ! Factor nproc into 2D processor grid. Start by finding prime ! factors ! nproc = ga_nnodes() pmax = nproc nprime = 1 primes(1) = 2 do i = 3, pmax ok = .true. do j = 1, nprime ! ! check to see if any of the existing primes divides i ! if (mod(i,primes(j)).eq.0) then ok = .false. endif end do ! ! none of the existing primes divides i, so add it to the list of ! primes ! if (ok) then nprime = nprime + 1 primes(nprime) = i endif end do ! ! find prime factors of nproc ! nfactors = 0 j = nproc i = 1 do while (i.le.nprime.and.j.gt.1) do while (mod(j,primes(i)).eq.0.and.j.gt.1) nfactors = nfactors + 1 factors(nfactors) = primes(i) j = j/primes(i) end do i = i + 1 end do if (nfactors.eq.0) then nfactors = 1 primes(1) = nproc endif ! ! prime factors are available. Create px and py ! px = 1 py = 1 do i = nfactors, 1, -1 if (px.lt.py) then px = px*factors(i) else py = py*factors(i) endif end do proc_grid(1) = px proc_grid(2) = py ! ! proc grid is complete, now create map. Start with x-axis ! lo = 1 hi = 0 i = 0 j = 0 do while (hi.lt.dims(1)) i = i + 1 hi = lo + base + mod(i+1,2) - 1 ! hi = lo + base - 1 if (hi.gt.dims(1)) hi = dims(1) j = j + 1 mapc(j) = lo lo = hi + 1 end do nblocks(1) = i lo = 1 hi = 0 i = 0 do while (hi.lt.dims(2)) i = i + 1 hi = lo + base + mod(i,2) - 1 ! hi = lo + base - 1 if (hi.gt.dims(1)) hi = dims(1) j = j + 1 mapc(j) = lo lo = hi + 1 end do nblocks(2) = i end ga-5.9.2/global/testing/testabstract_ops.c000066400000000000000000000056571500715745200206060ustar00rootroot00000000000000#define NUM_THREADS 4 #define TRIALS 1000000 #include #include #include #include #include "ga.h" #if defined(_OPENMP) #include #endif #include "abstract_ops.h" typedef struct cpl{ double real; double imag; } cpl; double abs_val(cpl a); void divide(cpl *a, cpl *b, cpl *c); int main(int argc, char **argv) { int i; int minsafe, maxsafe,divsafe; #if defined(_OPENMP) srand(time(NULL)); cpl * arr1 = (cpl*)malloc(TRIALS*sizeof(cpl)); cpl * arr2 = (cpl*)malloc(TRIALS*sizeof(cpl)); cpl * arr3 = (cpl*)malloc(TRIALS*sizeof(cpl)); cpl * arr4 = (cpl*)malloc(TRIALS*sizeof(cpl)); cpl * arr5 = (cpl*)malloc(TRIALS*sizeof(cpl)); cpl * arr6 = (cpl*)malloc(TRIALS*sizeof(cpl)); for(i = 0; i < TRIALS; i++) { arr2[i].real = (double) (rand() % 100)+1; arr2[i].imag = (double) (rand() % 100)+1; arr3[i].real = (double) (rand() % 100)+1; arr3[i].imag = (double) (rand() % 100)+1; } omp_set_num_threads(NUM_THREADS); #pragma omp parallel { #pragma omp for for(i = 0; i < TRIALS; i++) { cpl a,b,c; b = arr2[i]; c = arr3[i]; assign_max_cpl(a,b,c); arr4[i] = a; assign_min_cpl(a,b,c); arr5[i] = a; assign_div_cpl(a,b,c); arr6[i] = a; } } maxsafe=1; minsafe = 1; divsafe = 1; for(i = 0; i < TRIALS; i++) { if(maxsafe) { arr1[i] = abs_val(arr2[i])>abs_val(arr3[i])\ ? arr2[i] : arr3[i]; if(arr1[i].real != arr4[i].real ||\ arr1[i].imag != arr4[i].imag) maxsafe = 0; } if(minsafe) { arr1[i] = abs_val(arr2[i])real = (b->real*c->real+b->imag*c->imag)/\ (c->real*c->real+c->imag*c->imag); a->imag = (b->imag*c->real-b->real*c->imag)/\ (c->real*c->real+c->imag*c->imag); } ga-5.9.2/global/testing/testblas.F000066400000000000000000001041311500715745200167710ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif #include "galinalg.fh" SUBROUTINE TEST_DGEMM(TRANSA,TRANSB,M,N,K,ALPHA,A,LDA,B, $ LDB,BETA,C,LDC) C .. Parameters .. DOUBLE PRECISION ONE, ZERO PARAMETER (ONE=1.0D+0,ZERO=0.0D+0) INTEGER MB, NB, KB PARAMETER (MB=64,NB=MB,KB=64) C .. Scalar Arguments .. DOUBLE PRECISION ALPHA, BETA INTEGER K, LDA, LDB, LDC, M, N CHARACTER TRANSA, TRANSB C .. Array Arguments .. C C Purpose C ======= C C DGEMM performs one of the matrix-matrix operations C C C := alpha*op( A )*op( B ) + beta*C, C C where op( X ) is one of C C op( X ) = X or op( X ) = X', C C alpha and beta are scalars, and A, B and C are matrices, with op( A ) C an m by k matrix, op( B ) a k by n matrix and C an m by n matrix. C C Parameters C ========== C C TRANSA - CHARACTER*1. C On entry, TRANSA specifies the form of op( A ) to be used in C the matrix multiplication as follows: C C TRANSA = 'N' or 'n', op( A ) = A. C C TRANSA = 'T' or 't', op( A ) = A'. C C TRANSA = 'C' or 'c', op( A ) = A'. C C Unchanged on exit. C C TRANSB - CHARACTER*1. C On entry, TRANSB specifies the form of op( B ) to be used in C the matrix multiplication as follows: C C TRANSB = 'N' or 'n', op( B ) = B. C C TRANSB = 'T' or 't', op( B ) = B'. C C TRANSB = 'C' or 'c', op( B ) = B'. C C Unchanged on exit. C C M - INTEGER. C On entry, M specifies the number of rows of the matrix C op( A ) and of the matrix C. M must be at least zero. C Unchanged on exit. C C N - INTEGER. C On entry, N specifies the number of columns of the matrix C op( B ) and the number of columns of the matrix C. N must be C at least zero. C Unchanged on exit. C C K - INTEGER. C On entry, K specifies the number of columns of the matrix C op( A ) and the number of rows of the matrix op( B ). K must C be at least zero. C Unchanged on exit. C C ALPHA - DOUBLE PRECISION. C On entry, ALPHA specifies the scalar alpha. C Unchanged on exit. C C A - DOUBLE PRECISION array of DIMENSION ( LDA, ka ), where ka is C k when TRANSA = 'N' or 'n', and is m otherwise. C Before entry with TRANSA = 'N' or 'n', the leading m by k C part of the array A must contain the matrix A, otherwise C the leading k by m part of the array A must contain the C matrix A. C Unchanged on exit. C C LDA - INTEGER. C On entry, LDA specifies the first dimension of A as declared C in the calling (sub) program. When TRANSA = 'N' or 'n' then C LDA must be at least max( 1, m ), otherwise LDA must be at C least max( 1, k ). C Unchanged on exit. C C B - DOUBLE PRECISION array of DIMENSION ( LDB, kb ), where kb is C n when TRANSB = 'N' or 'n', and is k otherwise. C Before entry with TRANSB = 'N' or 'n', the leading k by n C part of the array B must contain the matrix B, otherwise C the leading n by k part of the array B must contain the C matrix B. C Unchanged on exit. C C LDB - INTEGER. C On entry, LDB specifies the first dimension of B as declared C in the calling (sub) program. When TRANSB = 'N' or 'n' then C LDB must be at least max( 1, k ), otherwise LDB must be at C least max( 1, n ). C Unchanged on exit. C C BETA - DOUBLE PRECISION. C On entry, BETA specifies the scalar beta. When BETA is C supplied as zero then C need not be set on input. C Unchanged on exit. C C C - DOUBLE PRECISION array of DIMENSION ( LDC, n ). C Before entry, the leading m by n part of the array C must C contain the matrix C, except when beta is zero, in which C case C need not be set on entry. C On exit, the array C is overwritten by the m by n matrix C ( alpha*op( A )*op( B ) + beta*C ). C C LDC - INTEGER. C On entry, LDC specifies the first dimension of C as declared C in the calling (sub) program. LDC must be at least C max( 1, m ). C Unchanged on exit. C C C Level 3 Blas routine. C C -- Written on 8-February-1989. C Jack Dongarra, Argonne National Laboratory. C Iain Duff, AERE Harwell. C Jeremy Du Croz, Numerical Algorithms Group Ltd. C Sven Hammarling, Numerical Algorithms Group Ltd. * * This code comes from a report entitled: * The IBM RISC System/6000 and Linear Algebra Operations, by * Jack J. Dongarra, Peter Mayes, and Giuseppe Radicati di Brozolo, * University of Tennessee Computer Science Tech Report: CS - 90 - 122. C C DOUBLE PRECISION A(LDA,*), B(LDB,*), C(LDC,*) C .. External Functions .. LOGICAL LSAME EXTERNAL LSAME C .. External Subroutines .. EXTERNAL XERBLA C .. Intrinsic Functions .. INTRINSIC MAX, MIN C .. Local Scalars .. DOUBLE PRECISION T11, T12, T21, T22 INTEGER I, IDEPTH, II, ILEN, INFO, ISPAN, J, JDEPTH, JJ, * JLEN, JSPAN, L, LL, LSPAN, NCOLA, NROWA, NROWB LOGICAL NOTA, NOTB C .. Local Arrays .. DOUBLE PRECISION CH(KB,MB), CH1(KB), CH2(KB) C .. Executable Statements .. C C Set NOTA and NOTB as true if A and B respectively are not C transposed and set NROWA, NCOLA and NROWB as the number of rows C and columns of A and the number of rows of B respectively. C NOTA = LSAME(TRANSA,'N') NOTB = LSAME(TRANSB,'N') IF (NOTA) THEN NROWA = M NCOLA = K ELSE NROWA = K NCOLA = M END IF IF (NOTB) THEN NROWB = K ELSE NROWB = N END IF C C Test the input parameters. C INFO = 0 IF (( .NOT. NOTA) .AND. ( .NOT. LSAME(TRANSA,'C')) * .AND. ( .NOT. LSAME(TRANSA,'T'))) THEN INFO = 1 ELSE IF (( .NOT. NOTB) .AND. ( .NOT. LSAME(TRANSB,'C')) * .AND. ( .NOT. LSAME(TRANSB,'T'))) THEN INFO = 2 ELSE IF (M.LT.0) THEN INFO = 3 ELSE IF (N.LT.0) THEN INFO = 4 ELSE IF (K.LT.0) THEN INFO = 5 ELSE IF (LDA.LT.MAX(1,NROWA)) THEN INFO = 8 ELSE IF (LDB.LT.MAX(1,NROWB)) THEN INFO = 10 ELSE IF (LDC.LT.MAX(1,M)) THEN INFO = 13 END IF IF (INFO.NE.0) THEN CALL XERBLA('DGEMM ',INFO) RETURN END IF C C Quick return if possible. C IF ((M.EQ.0) .OR. (N.EQ.0) .OR. (((ALPHA.EQ.ZERO) .OR. (K.EQ.0)) * .AND. (BETA.EQ.ONE))) RETURN IF (BETA.EQ.ZERO) THEN DO 40 J = 1, N DO 20 I = 1, M C(I,J) = ZERO 20 CONTINUE 40 CONTINUE ELSE DO 80 J = 1, N DO 60 I = 1, M C(I,J) = BETA*C(I,J) 60 CONTINUE 80 CONTINUE END IF C C And if alpha.eq.zero. C IF (ALPHA.EQ.ZERO) RETURN C C Start the operations. C IF (NOTB) THEN IF (NOTA) THEN C C Form C := C + alpha*A*B. C DO 380 L = 1, K, KB LSPAN = MIN(KB,K-L+1) DO 360 I = 1, M, MB IDEPTH = 2 ISPAN = MIN(MB,M-I+1) ILEN = IDEPTH*(ISPAN/IDEPTH) DO 120 II = I, I + ISPAN - 1 DO 100 LL = L, L + LSPAN - 1 CH(LL-L+1,II-I+1) = ALPHA*A(II,LL) 100 CONTINUE 120 CONTINUE DO 340 J = 1, N, NB JDEPTH = 2 JSPAN = MIN(NB,N-J+1) JLEN = JDEPTH*(JSPAN/JDEPTH) DO 220 JJ = J, J + JLEN - 1, JDEPTH DO 160 II = I, I + ILEN - 1, IDEPTH T11 = ZERO T21 = ZERO T12 = ZERO T22 = ZERO DO 140 LL = L, L + LSPAN - 1 T11 = T11 + CH(LL-L+1,II-I+1)*B(LL,JJ) T21 = T21 + CH(LL-L+1,II-I+2)*B(LL,JJ) T12 = T12 + CH(LL-L+1,II-I+1)*B(LL,JJ+1) T22 = T22 + CH(LL-L+1,II-I+2)*B(LL,JJ+1) 140 CONTINUE C(II,JJ) = C(II,JJ) + T11 C(II+1,JJ) = C(II+1,JJ) + T21 C(II,JJ+1) = C(II,JJ+1) + T12 C(II+1,JJ+1) = C(II+1,JJ+1) + T22 160 CONTINUE IF (ILEN.LT.ISPAN) THEN DO 200 II = I + ILEN, I + ISPAN - 1 T11 = ZERO T12 = ZERO DO 180 LL = L, L + LSPAN - 1 T11 = T11 + CH(LL-L+1,II-I+1)*B(LL,JJ) T12 = T12 + CH(LL-L+1,II-I+1)*B(LL, * JJ+1) 180 CONTINUE C(II,JJ) = C(II,JJ) + T11 C(II,JJ+1) = C(II,JJ+1) + T12 200 CONTINUE END IF 220 CONTINUE IF (JLEN.LT.JSPAN) THEN DO 320 JJ = J + JLEN, J + JSPAN - 1 DO 260 II = I, I + ILEN - 1, IDEPTH T11 = ZERO T21 = ZERO DO 240 LL = L, L + LSPAN - 1 T11 = T11 + CH(LL-L+1,II-I+1)*B(LL,JJ) T21 = T21 + CH(LL-L+1,II-I+2)*B(LL,JJ) 240 CONTINUE C(II,JJ) = C(II,JJ) + T11 C(II+1,JJ) = C(II+1,JJ) + T21 260 CONTINUE IF (ILEN.LT.ISPAN) THEN DO 300 II = I + ILEN, I + ISPAN - 1 T11 = ZERO DO 280 LL = L, L + LSPAN - 1 T11 = T11 + CH(LL-L+1,II-I+1)*B(LL, * JJ) 280 CONTINUE C(II,JJ) = C(II,JJ) + T11 300 CONTINUE END IF 320 CONTINUE END IF 340 CONTINUE 360 CONTINUE 380 CONTINUE ELSE C C Form C := C + alpha*A'*B C DO 680 I = 1, M, MB IDEPTH = 2 ISPAN = MIN(MB,M-I+1) ILEN = IDEPTH*(ISPAN/IDEPTH) DO 660 L = 1, K, KB LSPAN = MIN(KB,K-L+1) DO 420 II = I, I + ISPAN - 1 DO 400 LL = L, L + LSPAN - 1 CH(LL-L+1,II-I+1) = ALPHA*A(LL,II) 400 CONTINUE 420 CONTINUE DO 640 J = 1, N, NB JDEPTH = 2 JSPAN = MIN(NB,N-J+1) JLEN = JDEPTH*(JSPAN/JDEPTH) DO 520 JJ = J, J + JLEN - 1, JDEPTH DO 460 II = I, I + ILEN - 1, IDEPTH T11 = ZERO T21 = ZERO T12 = ZERO T22 = ZERO DO 440 LL = L, L + LSPAN - 1 T11 = T11 + CH(LL-L+1,II-I+1)*B(LL,JJ) T21 = T21 + CH(LL-L+1,II-I+2)*B(LL,JJ) T12 = T12 + CH(LL-L+1,II-I+1)*B(LL,JJ+1) T22 = T22 + CH(LL-L+1,II-I+2)*B(LL,JJ+1) 440 CONTINUE C(II,JJ) = C(II,JJ) + T11 C(II+1,JJ) = C(II+1,JJ) + T21 C(II,JJ+1) = C(II,JJ+1) + T12 C(II+1,JJ+1) = C(II+1,JJ+1) + T22 460 CONTINUE IF (ILEN.LT.ISPAN) THEN DO 500 II = I + ILEN, I + ISPAN - 1 T11 = ZERO T12 = ZERO DO 480 LL = L, L + LSPAN - 1 T11 = T11 + CH(LL-L+1,II-I+1)*B(LL,JJ) T12 = T12 + CH(LL-L+1,II-I+1)*B(LL, * JJ+1) 480 CONTINUE C(II,JJ) = C(II,JJ) + T11 C(II,JJ+1) = C(II,JJ+1) + T12 500 CONTINUE END IF 520 CONTINUE IF (JLEN.LT.JSPAN) THEN DO 620 JJ = J + JLEN, J + JSPAN - 1 DO 560 II = I, I + ILEN - 1, IDEPTH T11 = ZERO T21 = ZERO DO 540 LL = L, L + LSPAN - 1 T11 = T11 + CH(LL-L+1,II-I+1)*B(LL,JJ) T21 = T21 + CH(LL-L+1,II-I+2)*B(LL,JJ) 540 CONTINUE C(II,JJ) = C(II,JJ) + T11 C(II+1,JJ) = C(II+1,JJ) + T21 560 CONTINUE IF (ILEN.LT.ISPAN) THEN DO 600 II = I + ILEN, I + ISPAN - 1 T11 = ZERO DO 580 LL = L, L + LSPAN - 1 T11 = T11 + CH(LL-L+1,II-I+1)*B(LL, * JJ) 580 CONTINUE C(II,JJ) = C(II,JJ) + T11 600 CONTINUE END IF 620 CONTINUE END IF 640 CONTINUE 660 CONTINUE 680 CONTINUE END IF ELSE IF (NOTA) THEN C C Form C := C + alpha*A*B' C DO 1000 J = 1, N, NB JDEPTH = 2 JSPAN = MIN(NB,N-J+1) JLEN = JDEPTH*(JSPAN/JDEPTH) DO 980 L = 1, K, KB LSPAN = MIN(KB,K-L+1) DO 720 JJ = J, J + JSPAN - 1 DO 700 LL = L, L + LSPAN - 1 CH(LL-L+1,JJ-J+1) = ALPHA*B(JJ,LL) 700 CONTINUE 720 CONTINUE DO 960 I = 1, M, MB IDEPTH = 2 ISPAN = MIN(MB,M-I+1) ILEN = IDEPTH*(ISPAN/IDEPTH) DO 840 II = I, I + ILEN - 1, IDEPTH DO 740 LL = L, L + LSPAN - 1 CH1(LL-L+1) = A(II,LL) CH2(LL-L+1) = A(II+1,LL) 740 CONTINUE DO 780 JJ = J, J + JLEN - 1, JDEPTH T11 = ZERO T21 = ZERO T12 = ZERO T22 = ZERO DO 760 LL = L, L + LSPAN - 1 T11 = T11 + CH1(LL-L+1)*CH(LL-L+1,JJ-J+1) T21 = T21 + CH2(LL-L+1)*CH(LL-L+1,JJ-J+1) T12 = T12 + CH1(LL-L+1)*CH(LL-L+1,JJ-J+2) T22 = T22 + CH2(LL-L+1)*CH(LL-L+1,JJ-J+2) 760 CONTINUE C(II,JJ) = C(II,JJ) + T11 C(II+1,JJ) = C(II+1,JJ) + T21 C(II,JJ+1) = C(II,JJ+1) + T12 C(II+1,JJ+1) = C(II+1,JJ+1) + T22 780 CONTINUE IF (JLEN.LT.JSPAN) THEN DO 820 JJ = J + JLEN, J + JSPAN - 1 T11 = ZERO T21 = ZERO DO 800 LL = L, L + LSPAN - 1 T11 = T11 + A(II,LL)*CH(LL-L+1,JJ-J+1) T21 = T21 + A(II+1,LL)*CH(LL-L+1, * JJ-J+1) 800 CONTINUE C(II,JJ) = C(II,JJ) + T11 C(II+1,JJ) = C(II+1,JJ) + T21 820 CONTINUE END IF 840 CONTINUE IF (ILEN.LT.ISPAN) THEN DO 940 II = I + ILEN, I + ISPAN - 1 DO 880 JJ = J, J + JLEN - 1, JDEPTH T11 = ZERO T12 = ZERO DO 860 LL = L, L + LSPAN - 1 T11 = T11 + A(II,LL)*CH(LL-L+1,JJ-J+1) T12 = T12 + A(II,LL)*CH(LL-L+1,JJ-J+2) 860 CONTINUE C(II,JJ) = C(II,JJ) + T11 C(II,JJ+1) = C(II,JJ+1) + T12 880 CONTINUE IF (JLEN.LT.JSPAN) THEN DO 920 JJ = J + JLEN, J + JSPAN - 1 T11 = ZERO DO 900 LL = L, L + LSPAN - 1 T11 = T11 + A(II,LL)*CH(LL-L+1, * JJ-J+1) 900 CONTINUE C(II,JJ) = C(II,JJ) + T11 920 CONTINUE END IF 940 CONTINUE END IF 960 CONTINUE 980 CONTINUE 1000 CONTINUE ELSE C C Form C := C + alpha*A'*B' C DO 1300 J = 1, N, NB JDEPTH = 2 JSPAN = MIN(NB,N-J+1) JLEN = JDEPTH*(JSPAN/JDEPTH) DO 1280 L = 1, K, KB LSPAN = MIN(KB,K-L+1) DO 1040 JJ = J, J + JSPAN - 1 DO 1020 LL = L, L + LSPAN - 1 CH(LL-L+1,JJ-J+1) = ALPHA*B(JJ,LL) 1020 CONTINUE 1040 CONTINUE DO 1260 I = 1, M, MB IDEPTH = 2 ISPAN = MIN(MB,M-I+1) ILEN = IDEPTH*(ISPAN/IDEPTH) DO 1140 II = I, I + ILEN - 1, IDEPTH DO 1080 JJ = J, J + JLEN - 1, JDEPTH T11 = ZERO T21 = ZERO T12 = ZERO T22 = ZERO DO 1060 LL = L, L + LSPAN - 1 T11 = T11 + A(LL,II)*CH(LL-L+1,JJ-J+1) T21 = T21 + A(LL,II+1)*CH(LL-L+1,JJ-J+1) T12 = T12 + A(LL,II)*CH(LL-L+1,JJ-J+2) T22 = T22 + A(LL,II+1)*CH(LL-L+1,JJ-J+2) 1060 CONTINUE C(II,JJ) = C(II,JJ) + T11 C(II+1,JJ) = C(II+1,JJ) + T21 C(II,JJ+1) = C(II,JJ+1) + T12 C(II+1,JJ+1) = C(II+1,JJ+1) + T22 1080 CONTINUE IF (JLEN.LT.JSPAN) THEN DO 1120 JJ = J + JLEN, J + JSPAN - 1 T11 = ZERO T21 = ZERO DO 1100 LL = L, L + LSPAN - 1 T11 = T11 + A(LL,II)*CH(LL-L+1,JJ-J+1) T21 = T21 + A(LL,II+1)*CH(LL-L+1, * JJ-J+1) 1100 CONTINUE C(II,JJ) = C(II,JJ) + T11 C(II+1,JJ) = C(II+1,JJ) + T21 1120 CONTINUE END IF 1140 CONTINUE IF (ILEN.LT.ISPAN) THEN DO 1240 II = I + ILEN, I + ISPAN - 1 DO 1180 JJ = J, J + JLEN - 1, JDEPTH T11 = ZERO T12 = ZERO DO 1160 LL = L, L + LSPAN - 1 T11 = T11 + A(LL,II)*CH(LL-L+1,JJ-J+1) T12 = T12 + A(LL,II)*CH(LL-L+1,JJ-J+2) 1160 CONTINUE C(II,JJ) = C(II,JJ) + T11 C(II,JJ+1) = C(II,JJ+1) + T12 1180 CONTINUE IF (JLEN.LT.JSPAN) THEN DO 1220 JJ = J + JLEN, J + JSPAN - 1 T11 = ZERO DO 1200 LL = L, L + LSPAN - 1 T11 = T11 + A(LL,II)*CH(LL-L+1, * JJ-J+1) 1200 CONTINUE C(II,JJ) = C(II,JJ) + T11 1220 CONTINUE END IF 1240 CONTINUE END IF 1260 CONTINUE 1280 CONTINUE 1300 CONTINUE END IF END IF C RETURN C C End of DGEMM . C END SUBROUTINE TEST_ZGEMM ( TRANSA, TRANSB, M, N, K, ALPHA, A, $ LDA, B, LDB, BETA, C, LDC ) * .. Scalar Arguments .. CHARACTER*1 TRANSA, TRANSB INTEGER M, N, K, LDA, LDB, LDC COMPLEX*16 ALPHA, BETA * .. Array Arguments .. COMPLEX*16 A( LDA, * ), B( LDB, * ), C( LDC, * ) * .. * * Purpose * ======= * * ZGEMM performs one of the matrix-matrix operations * * C := alpha*op( A )*op( B ) + beta*C, * * where op( X ) is one of * * op( X ) = X or op( X ) = X' or op( X ) = conjg( X' ), * * alpha and beta are scalars, and A, B and C are matrices, with op( A ) * an m by k matrix, op( B ) a k by n matrix and C an m by n matrix. * * Parameters * ========== * * TRANSA - CHARACTER*1. * On entry, TRANSA specifies the form of op( A ) to be used in * the matrix multiplication as follows: * * TRANSA = 'N' or 'n', op( A ) = A. * * TRANSA = 'T' or 't', op( A ) = A'. * * TRANSA = 'C' or 'c', op( A ) = conjg( A' ). * * Unchanged on exit. * * TRANSB - CHARACTER*1. * On entry, TRANSB specifies the form of op( B ) to be used in * the matrix multiplication as follows: * * TRANSB = 'N' or 'n', op( B ) = B. * * TRANSB = 'T' or 't', op( B ) = B'. * * TRANSB = 'C' or 'c', op( B ) = conjg( B' ). * * Unchanged on exit. * * M - INTEGER. * On entry, M specifies the number of rows of the matrix * op( A ) and of the matrix C. M must be at least zero. * Unchanged on exit. * * N - INTEGER. * On entry, N specifies the number of columns of the matrix * op( B ) and the number of columns of the matrix C. N must be * at least zero. * Unchanged on exit. * * K - INTEGER. * On entry, K specifies the number of columns of the matrix * op( A ) and the number of rows of the matrix op( B ). K must * be at least zero. * Unchanged on exit. * * ALPHA - COMPLEX*16 . * On entry, ALPHA specifies the scalar alpha. * Unchanged on exit. * * A - COMPLEX*16 array of DIMENSION ( LDA, ka ), where ka is * k when TRANSA = 'N' or 'n', and is m otherwise. * Before entry with TRANSA = 'N' or 'n', the leading m by k * part of the array A must contain the matrix A, otherwise * the leading k by m part of the array A must contain the * matrix A. * Unchanged on exit. * * LDA - INTEGER. * On entry, LDA specifies the first dimension of A as declared * in the calling (sub) program. When TRANSA = 'N' or 'n' then * LDA must be at least max( 1, m ), otherwise LDA must be at * least max( 1, k ). * Unchanged on exit. * * B - COMPLEX*16 array of DIMENSION ( LDB, kb ), where kb is * n when TRANSB = 'N' or 'n', and is k otherwise. * Before entry with TRANSB = 'N' or 'n', the leading k by n * part of the array B must contain the matrix B, otherwise * the leading n by k part of the array B must contain the * matrix B. * Unchanged on exit. * * LDB - INTEGER. * On entry, LDB specifies the first dimension of B as declared * in the calling (sub) program. When TRANSB = 'N' or 'n' then * LDB must be at least max( 1, k ), otherwise LDB must be at * least max( 1, n ). * Unchanged on exit. * * BETA - COMPLEX*16 . * On entry, BETA specifies the scalar beta. When BETA is * supplied as zero then C need not be set on input. * Unchanged on exit. * * C - COMPLEX*16 array of DIMENSION ( LDC, n ). * Before entry, the leading m by n part of the array C must * contain the matrix C, except when beta is zero, in which * case C need not be set on entry. * On exit, the array C is overwritten by the m by n matrix * ( alpha*op( A )*op( B ) + beta*C ). * * LDC - INTEGER. * On entry, LDC specifies the first dimension of C as declared * in the calling (sub) program. LDC must be at least * max( 1, m ). * Unchanged on exit. * * * Level 3 Blas routine. * * -- Written on 8-February-1989. * Jack Dongarra, Argonne National Laboratory. * Iain Duff, AERE Harwell. * Jeremy Du Croz, Numerical Algorithms Group Ltd. * Sven Hammarling, Numerical Algorithms Group Ltd. * * * .. External Functions .. LOGICAL LSAME EXTERNAL LSAME * .. External Subroutines .. EXTERNAL XERBLA * .. Intrinsic Functions .. INTRINSIC CONJG, MAX * .. Local Scalars .. LOGICAL CONJA, CONJB, NOTA, NOTB INTEGER I, INFO, J, L, NCOLA, NROWA, NROWB COMPLEX*16 TEMP * .. Parameters .. COMPLEX*16 ONE PARAMETER ( ONE = ( 1.0D+0, 0.0D+0 ) ) COMPLEX*16 ZERO PARAMETER ( ZERO = ( 0.0D+0, 0.0D+0 ) ) * .. * .. Executable Statements .. * * Set NOTA and NOTB as true if A and B respectively are not * conjugated or transposed, set CONJA and CONJB as true if A and * B respectively are to be transposed but not conjugated and set * NROWA, NCOLA and NROWB as the number of rows and columns of A * and the number of rows of B respectively. * NOTA = LSAME( TRANSA, 'N' ) NOTB = LSAME( TRANSB, 'N' ) CONJA = LSAME( TRANSA, 'C' ) CONJB = LSAME( TRANSB, 'C' ) IF( NOTA )THEN NROWA = M NCOLA = K ELSE NROWA = K NCOLA = M END IF IF( NOTB )THEN NROWB = K ELSE NROWB = N END IF * * Test the input parameters. * INFO = 0 IF( ( .NOT.NOTA ).AND. $ ( .NOT.CONJA ).AND. $ ( .NOT.LSAME( TRANSA, 'T' ) ) )THEN INFO = 1 ELSE IF( ( .NOT.NOTB ).AND. $ ( .NOT.CONJB ).AND. $ ( .NOT.LSAME( TRANSB, 'T' ) ) )THEN INFO = 2 ELSE IF( M .LT.0 )THEN INFO = 3 ELSE IF( N .LT.0 )THEN INFO = 4 ELSE IF( K .LT.0 )THEN INFO = 5 ELSE IF( LDA.LT.MAX( 1, NROWA ) )THEN INFO = 8 ELSE IF( LDB.LT.MAX( 1, NROWB ) )THEN INFO = 10 ELSE IF( LDC.LT.MAX( 1, M ) )THEN INFO = 13 END IF IF( INFO.NE.0 )THEN CALL XERBLA( 'ZGEMM ', INFO ) RETURN END IF * * Quick return if possible. * IF( ( M.EQ.0 ).OR.( N.EQ.0 ).OR. $ ( ( ( ALPHA.EQ.ZERO ).OR.( K.EQ.0 ) ).AND.( BETA.EQ.ONE ) ) ) $ RETURN * * And when alpha.eq.zero. * IF( ALPHA.EQ.ZERO )THEN IF( BETA.EQ.ZERO )THEN DO 20, J = 1, N DO 10, I = 1, M C( I, J ) = ZERO 10 CONTINUE 20 CONTINUE ELSE DO 40, J = 1, N DO 30, I = 1, M C( I, J ) = BETA*C( I, J ) 30 CONTINUE 40 CONTINUE END IF RETURN END IF * * Start the operations. * IF( NOTB )THEN IF( NOTA )THEN * * Form C := alpha*A*B + beta*C. * DO 90, J = 1, N IF( BETA.EQ.ZERO )THEN DO 50, I = 1, M C( I, J ) = ZERO 50 CONTINUE ELSE IF( BETA.NE.ONE )THEN DO 60, I = 1, M C( I, J ) = BETA*C( I, J ) 60 CONTINUE END IF DO 80, L = 1, K IF( B( L, J ).NE.ZERO )THEN TEMP = ALPHA*B( L, J ) DO 70, I = 1, M C( I, J ) = C( I, J ) + TEMP*A( I, L ) 70 CONTINUE END IF 80 CONTINUE 90 CONTINUE ELSE IF( CONJA )THEN * * Form C := alpha*conjg( A' )*B + beta*C. * DO 120, J = 1, N DO 110, I = 1, M TEMP = ZERO DO 100, L = 1, K TEMP = TEMP + CONJG( A( L, I ) )*B( L, J ) 100 CONTINUE IF( BETA.EQ.ZERO )THEN C( I, J ) = ALPHA*TEMP ELSE C( I, J ) = ALPHA*TEMP + BETA*C( I, J ) END IF 110 CONTINUE 120 CONTINUE ELSE * * Form C := alpha*A'*B + beta*C * DO 150, J = 1, N DO 140, I = 1, M TEMP = ZERO DO 130, L = 1, K TEMP = TEMP + A( L, I )*B( L, J ) 130 CONTINUE IF( BETA.EQ.ZERO )THEN C( I, J ) = ALPHA*TEMP ELSE C( I, J ) = ALPHA*TEMP + BETA*C( I, J ) END IF 140 CONTINUE 150 CONTINUE END IF ELSE IF( NOTA )THEN IF( CONJB )THEN * * Form C := alpha*A*conjg( B' ) + beta*C. * DO 200, J = 1, N IF( BETA.EQ.ZERO )THEN DO 160, I = 1, M C( I, J ) = ZERO 160 CONTINUE ELSE IF( BETA.NE.ONE )THEN DO 170, I = 1, M C( I, J ) = BETA*C( I, J ) 170 CONTINUE END IF DO 190, L = 1, K IF( B( J, L ).NE.ZERO )THEN TEMP = ALPHA*CONJG( B( J, L ) ) DO 180, I = 1, M C( I, J ) = C( I, J ) + TEMP*A( I, L ) 180 CONTINUE END IF 190 CONTINUE 200 CONTINUE ELSE * * Form C := alpha*A*B' + beta*C * DO 250, J = 1, N IF( BETA.EQ.ZERO )THEN DO 210, I = 1, M C( I, J ) = ZERO 210 CONTINUE ELSE IF( BETA.NE.ONE )THEN DO 220, I = 1, M C( I, J ) = BETA*C( I, J ) 220 CONTINUE END IF DO 240, L = 1, K IF( B( J, L ).NE.ZERO )THEN TEMP = ALPHA*B( J, L ) DO 230, I = 1, M C( I, J ) = C( I, J ) + TEMP*A( I, L ) 230 CONTINUE END IF 240 CONTINUE 250 CONTINUE END IF ELSE IF( CONJA )THEN IF( CONJB )THEN * * Form C := alpha*conjg( A' )*conjg( B' ) + beta*C. * DO 280, J = 1, N DO 270, I = 1, M TEMP = ZERO DO 260, L = 1, K TEMP = TEMP + $ CONJG( A( L, I ) )*CONJG( B( J, L ) ) 260 CONTINUE IF( BETA.EQ.ZERO )THEN C( I, J ) = ALPHA*TEMP ELSE C( I, J ) = ALPHA*TEMP + BETA*C( I, J ) END IF 270 CONTINUE 280 CONTINUE ELSE * * Form C := alpha*conjg( A' )*B' + beta*C * DO 310, J = 1, N DO 300, I = 1, M TEMP = ZERO DO 290, L = 1, K TEMP = TEMP + CONJG( A( L, I ) )*B( J, L ) 290 CONTINUE IF( BETA.EQ.ZERO )THEN C( I, J ) = ALPHA*TEMP ELSE C( I, J ) = ALPHA*TEMP + BETA*C( I, J ) END IF 300 CONTINUE 310 CONTINUE END IF ELSE IF( CONJB )THEN * * Form C := alpha*A'*conjg( B' ) + beta*C * DO 340, J = 1, N DO 330, I = 1, M TEMP = ZERO DO 320, L = 1, K TEMP = TEMP + A( L, I )*CONJG( B( J, L ) ) 320 CONTINUE IF( BETA.EQ.ZERO )THEN C( I, J ) = ALPHA*TEMP ELSE C( I, J ) = ALPHA*TEMP + BETA*C( I, J ) END IF 330 CONTINUE 340 CONTINUE ELSE * * Form C := alpha*A'*B' + beta*C * DO 370, J = 1, N DO 360, I = 1, M TEMP = ZERO DO 350, L = 1, K TEMP = TEMP + A( L, I )*B( J, L ) 350 CONTINUE IF( BETA.EQ.ZERO )THEN C( I, J ) = ALPHA*TEMP ELSE C( I, J ) = ALPHA*TEMP + BETA*C( I, J ) END IF 360 CONTINUE 370 CONTINUE END IF END IF * RETURN * * End of ZGEMM . * END ga-5.9.2/global/testing/testc.c000066400000000000000000000060711500715745200163330ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #include "ga.h" #include "macdecls.h" #include "mp3.h" #define N 100 /* dimension of matrices */ void do_work() { int ONE=1 ; /* useful constants */ int g_a, g_b; int n=N, type=MT_F_DBL; int me=GA_Nodeid(), nproc=GA_Nnodes(); int i, row; int dims[2]={N,N}; int lo[2], hi[2], ld; /* Note: on all current platforms DoublePrecision == double */ double buf[N], err, alpha, beta; if(me==0)printf("Creating matrix A\n"); g_a = NGA_Create(type, 2, dims, "A", NULL); if(!g_a) GA_Error("create failed: A",n); if(me==0)printf("OK\n"); if(me==0)printf("Creating matrix B\n"); /* create matrix B so that it has dims and distribution of A*/ g_b = GA_Duplicate(g_a, "B"); if(! g_b) GA_Error("duplicate failed",n); if(me==0)printf("OK\n"); GA_Zero(g_a); /* zero the matrix */ if(me==0)printf("Initializing matrix A\n"); /* fill in matrix A with random values in range 0.. 1 */ lo[1]=0; hi[1]=n-1; for(row=me; row Creating Block-Cyclic Arrays' write(6,*) ' ' endif dims(1) = n dims(2) = n block(1) = 16 block(2) = 16 call factor(nproc,g1,g2) proc_grid(1) = g1 proc_grid(2) = g2 if (me.eq.0) then write(6,'(a,i3,a,i3)') ' Proc grid dimensions: ',g1,' x ',g2 endif g_a = ga_create_handle() call ga_set_data(g_a,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_a,block,proc_grid) if (.not.ga_allocate(g_a)) & call ga_error(' ga_create a failed ', 2) g_b = ga_create_handle() call ga_set_data(g_b,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_b,block,proc_grid) if (.not.ga_allocate(g_b)) & call ga_error(' ga_create b failed ', 2) g_c = ga_create_handle() call ga_set_data(g_c,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_c,block,proc_grid) if (.not.ga_allocate(g_c)) & call ga_error(' ga_create c failed ', 2) g_d = ga_create_handle() call ga_set_data(g_d,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_d,block,proc_grid) if (.not.ga_allocate(g_d)) & call ga_error(' ga_create d failed ', 2) if (.not. ga_create(MT_DBL, n, n, 'AA', 1, 1, g_aa)) $ call ga_error(' ga_create aa failed ',2) if (.not. ga_create(MT_DBL, n, n, 'BB', 1, 1, g_bb)) $ call ga_error(' ga_create bb failed ',2) if (.not. ga_create(MT_DBL, n, n, 'CC', 1, 1, g_cc)) $ call ga_error(' ga_create cc failed ',2) if (.not. ga_create(MT_DBL, n, n, 'DD', 1, 1, g_dd)) $ call ga_error(' ga_create dd failed ',2) #else if (.not. ga_create(MT_DBL, n, n, 'A', 1, 1, g_a)) $ call ga_error(' ga_create a failed ',2) if (.not. ga_create(MT_DBL, n, n, 'B', 1, 1, g_b)) $ call ga_error(' ga_create b failed ',2) if (.not. ga_create(MT_DBL, n, n, 'C', 1, 1, g_c)) $ call ga_error(' ga_create c failed ',2) if (.not. ga_create(MT_DBL, n, n, 'D', 1, 1, g_d)) $ call ga_error(' ga_create d failed ',2) #endif c c*** Fill in arrays A & B c if (me .eq. 0) then c write(6,21) 21 format(/' filling in ... ') c call ffflush(6) do j = 1, n call ga_put(g_a, 1,n, j,j, a(1,j),n) call ga_put(g_b, 1,n, j,j, b(1,j),n) call ga_put(g_c, 1,n, j,j, c(1,j),n) enddo c print *,'A' do j = 1, n c call GA_GET(g_a, 1,n, j,j, eva,1) c write(6,'(10e8.2)')(eva(i),i=1,n) enddo endif c c*** check symmetrization c if (me .eq. 0) then print *,' ' print *,'>checking ga_symmetrize ' print *,' ' call ffflush(6) endif call ga_symmetrize(g_c) c call GA_GET(g_c, 1,n, 1,n,c,n) do j = ga_nodeid()+1, n, ga_nnodes() do i = j+1, n if(c(i,j).ne.c(j,i))then print *, me, ' symmetrize ',i,j,c(i,j),c(j,i) call ffflush(6) call ga_error('exiting',-1) endif enddo enddo call ga_sync() if (me .eq. 0) then print *,' ' print *,' ga_symmetrize is OK' print *,' ' call ffflush(6) endif c c*** check symmetrization c if (me .eq. 0) then print *,' ' print *,'>checking ga_transpose ' print *,' ' call ffflush(6) endif c call ga_transpose(g_c,g_d) * call ga_print(g_c) * call ga_print(g_d) call GA_GET(g_d, 1,n, 1,n,a,n) do j = ga_nodeid()+1, n, ga_nnodes() call GA_GET(g_d, 1,n, j,j, a,n) do i = 1, n if(a(i,1).ne.c(j,i))then print *, me, ' transpose ',i,j,a(i,1),c(j,i) call ffflush(6) call ga_error('exiting',-1) endif enddo enddo call ga_sync() if (me .eq. 0) then print *,' ' print *,' ga_transpose is OK' print *,' ' call ffflush(6) endif c c c*** solve the eigenproblem if (me .eq. 0)then print *,' ' write(6,*) '>checking the generalized eigensolver ... ' print *,' ' call ffflush(6) endif call ga_sync() #ifndef PAR_DIAG call ga_diag_seq(g_a,g_b,g_c,evals) #else #ifdef HAVE_SCALAPACK #if BLOCK_CYCLIC call ga_copy(g_a, g_aa) call ga_copy(g_b, g_bb) #endif call ga_pdsygv( g_a, g_b, g_c, evals) #if BLOCK_CYCLIC call ga_copy(g_aa, g_a) call ga_copy(g_bb, g_b) #endif #else call ga_diag(g_a,g_b,g_c,evals) #endif #endif if (me .eq. 0) then print *,' ' print *,' checking multiplication' print *,' ' call ffflush(6) endif c call ga_sync() #if BLOCK_CYCLIC call ga_copy(g_a, g_aa) call ga_copy(g_c, g_cc) call ga_dgemm('t','n',n,n,n, 1d0, g_cc, g_aa, 0d0, g_dd) call ga_dgemm('n','n',n,n,n, 1d0, g_dd, g_cc, 0d0, g_aa) call ga_copy(g_aa, g_a) #else call ga_dgemm('t','n',n,n,n, 1d0, g_c, g_a, 0d0, g_d) call ga_dgemm('n','n',n,n,n, 1d0, g_d, g_c, 0d0, g_a) #endif c call ga_sync() if (me .eq. 0) then do j = 1, n call GA_GET(g_a, j,j, j,j, eva(j),1) enddo endif call ga_sync() #if BLOCK_CYCLIC call ga_copy(g_b, g_bb) call ga_dgemm('t','n',n,n,n, 1d0, g_cc, g_bb, 0d0, g_dd) call ga_sync() call ga_dgemm('n','n',n,n,n, 1d0, g_dd, g_cc, 0d0, g_bb) call ga_copy(g_bb, g_b) #else call ga_dgemm('t','n',n,n,n, 1d0, g_c, g_b, 0d0, g_d) call ga_sync() call ga_dgemm('n','n',n,n,n, 1d0, g_d, g_c, 0d0, g_b) #endif c call ga_sync() if (me .eq. 0) then do j = 1, n call GA_GET(g_b, j,j, j,j, evb(j),1) enddo write(6,*)' j lambda eva evb eva/evb' write(6,*)' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^' call ffflush(6) do j = 1, n if(ABS(evals(j) - eva(j)/evb(j)).gt.1d-5) & write(6,'(i4,1h_,6(e10.3,1h,))') @ j,evals(j), eva(j), evb(j),eva(j)/evb(j) enddo write(6,*)' OK OK OK OK' call ffflush(6) endif if (me .eq. 0) then print *,' ' print *,' eigensolver & multiplication are OK' print *,' ' print *,' ' call ffflush(6) endif c c.................................................................. c c*** solve the std eigenproblem if (me .eq. 0)then print *,' ' write(6,*) '>checking the standard eigensolver ... ' print *,' ' call ffflush(6) endif do j =1,n index(j)=j ind(j)=j enddo call ga_sync() #ifdef PAR_DIAG #ifdef HAVE_SCALAPACK #if BLOCK_CYCLIC call ga_copy(g_a, g_aa) #endif call ga_pdsyev( g_a, g_c, evals, 16) #if BLOCK_CYCLIC call ga_copy(g_aa, g_a) #endif #else call ga_diag_std(g_a,g_c,evals) #endif #else call ga_diag_std_seq(g_a,g_c,evals) #endif c call ga_zero(g_b) #if BLOCK_CYCLIC call ga_copy(g_a, g_aa) call ga_copy(g_c, g_cc) call ga_dgemm('n','n',n,n,n, 1d0, g_aa, g_cc, 0d0, g_dd) ! d := a*c call ga_copy(g_dd, g_d) #else call ga_dgemm('n','n',n,n,n, 1d0, g_a, g_c, 0d0, g_d) ! d := a*c #endif c c copy eigenvalues to diagonal of g_b c if (me .eq. 0) call ga_scatter(g_b, evals, index, ind, n) call ga_sync() #if BLOCK_CYCLIC call ga_copy(g_b, g_bb) call ga_dgemm('n','n',n,n,n, 1d0, g_cc, g_bb, 0d0, g_aa) ! a := c*b call ga_copy(g_aa, g_a) #else call ga_dgemm('n','n',n,n,n, 1d0, g_c, g_b, 0d0, g_a) ! a := c*b #endif call ga_sync() call ga_add(1d0, g_d, -1d0, g_a, g_c) sum = ga_ddot(g_c,g_c) if (me .eq. 0) then if(dsqrt(sum)/n.lt.1d-11)then print *, ' std eigensolver is OK ' else print *, ' test failed: norm = ', dsqrt(sum)/n endif print *,' ' call ffflush(6) endif c status = MA_summarize_allocated_blocks() status = ga_destroy(g_d) status = ga_destroy(g_c) status = ga_destroy(g_b) status = ga_destroy(g_a) end #if BLOCK_CYCLIC subroutine factor(p,idx,idy) implicit none integer i,j,p,idx,idy,it integer ip,ifac,pmax,prime(1280) integer fac(1280) c i = 1 ip = p c c factor p completely c first, find all prime numbers less than or equal to p c pmax = 0 do i = 2, p do j = 1, pmax if (mod(i,prime(j)).eq.0) go to 100 end do pmax = pmax + 1 prime(pmax) = i 100 continue end do c c find all prime factors of p c ifac = 0 do i = 1, pmax 200 if (mod(ip,prime(i)).eq.0) then ifac = ifac + 1 fac(ifac) = prime(i) ip = ip/prime(i) go to 200 endif end do c c determine two factors of p of approximately the c same size c idx = 1 idy = 1 do i = ifac, 1, -1 if (idx.le.idy) then idx = fac(i)*idx else idy = fac(i)*idy endif end do return end #endif ga-5.9.2/global/testing/testmatmult.F000066400000000000000000000272021500715745200175360ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c#define BLOCK_CYCLIC c#define IRREG_DISTRIB program ga_test c $Id: testmatmult.F,v 1.6 2005-11-23 10:25:18 manoj Exp $ c c test ga_dgemm c compile with make FLD_REN="optimized blas" testmatmult.x c Note: - change nummax for large arrays c - turn off "verify" for large arrays due to memory c limitations, as verify=TRUE for large arrays produces c segfault, dumps core,or any crap. c c implicit none integer num_m,num_n,num_k,nummax,howmany,ntrans parameter (nummax=1024,howmany=2,ntrans=4) integer num1 parameter(num1=nummax) integer i,ii real*8 h0(num1*num1) integer g_c,g_b,g_a real*8 a real*8 t1,mf,avg_t(ntrans),avg_mf(ntrans) integer itime,ntimes,me integer nums_m(howmany),nums_n(howmany),nums_k(howmany) character*1 transa(ntrans),transb(ntrans),ta,tb real *8 tmpa(nummax,nummax), tmpb(nummax,nummax) real *8 tmpc(nummax,nummax) integer ndim, dims(2), block_size(2), proc_grid(2) integer mapa(10),mapb(10),nblocka,nblockb integer ilo,ihi,jlo,jhi logical verify data transa/'n','t','n','t'/ data transb/'n','n','t','t'/ data nums_m/512,1024/ data nums_n/512,1024/ data nums_k/512,1024/ #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" #include "mp3.fh" if (.not.ma_init(MT_DBL,1,20000000)) then call ga_error('failed: ma_init(MT_DBL,1,20000000)',10) endif call ga_initialize() me = ga_nodeid() ii = 0 do i = 1, num1*num1 ii = ii + 1 if(ii.gt.num1) then ii = 0 endif h0(i) = ii enddo c c Compute times assuming 500 mflops and 5 second target time c c ntimes = max(3.0d0,5.0d0/(4.0d-9*num**3)) ntimes = 5 verify = .TRUE. ! to verify ga_dgemm c verify = .FALSE. ! to verify ga_dgemm do ii=1,howmany num_m = nums_m(ii) num_n = nums_n(ii) num_k = nums_k(ii) a = 0.5d0/(num_m*num_n) if(num_m.gt.nummax .OR. num_n.gt.nummax .OR. & num_k.gt.nummax) then call ga_error('Insufficient memory: check nummax', 0) endif c c #ifdef BLOCK_CYCLIC ndim = 2 block_size(1) = 128 block_size(2) = 128 dims(1) = num_m dims(2) = num_n g_c = ga_create_handle() call ga_set_data(g_c,ndim,dims,MT_DBL) call ga_set_array_name(g_c,'g_c') call ga_set_block_cyclic(g_c,block_size) if(.not.ga_allocate(g_c)) then call ga_error('failed: create g_c',40) endif dims(1) = num_k dims(2) = num_n g_b = ga_create_handle() call ga_set_data(g_b,ndim,dims,MT_DBL) call ga_set_array_name(g_b,'g_b') call ga_set_block_cyclic(g_b,block_size) if(.not.ga_allocate(g_b)) then call ga_error('failed: create g_b',40) endif dims(1) = num_m dims(2) = num_k g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,MT_DBL) call ga_set_array_name(g_a,'g_a') call ga_set_block_cyclic(g_a,block_size) if(.not.ga_allocate(g_a)) then call ga_error('failed: create g_a',40) endif #elif defined(IRREG_DISTRIB) mapa(1)=1 mapb(1)=1 nblocka=nint(sqrt(ga_nnodes()*1d0)) nblockb=ga_nnodes()/nblocka do i=2,nblocka mapa(i)=(num_m/nblocka-2)*(i-1)+1 enddo do i=2,nblockb mapb(i)=(num_n/nblockb-2)*(i-1)+1 enddo if (.not.ga_create_irreg(MT_DBL,num_m,num_n,'g_c', & mapa,nblocka,mapb,nblockb,g_c)) then call ga_error('failed: create g_c',20) endif call ga_distribution(g_c, . ga_nodeid(), ilo, ihi, jlo, jhi) write(6,*) ga_nodeid(), ' ilo ihi jlo jhi ', . ilo,ihi,jlo,jhi if (.not.ga_create_irreg(MT_DBL,num_k,num_n,'g_b', & mapa,nblocka,mapb,nblockb,g_b)) then call ga_error('failed: create g_b',30) endif if (.not.ga_create_irreg(MT_DBL,num_m,num_k,'g_a', & mapa,nblocka,mapb,nblockb,g_a)) then call ga_error('failed: create g_a',40) endif #else if (.not.ga_create(MT_DBL,num_m,num_n,'g_c', & 0,0,g_c)) then call ga_error('failed: create g_c',20) endif if (.not.ga_create(MT_DBL,num_k,num_n,'g_b', & 0,0,g_b)) then call ga_error('failed: create g_b',30) endif if (.not.ga_create(MT_DBL,num_m,num_k,'g_a', & 0,0,g_a)) then call ga_error('failed: create g_a',40) endif #endif c c Initialize matrices A and B c call load_ga_from_square(g_a,num_m,h0,num1) c call load_ga_from_square(g_b,num_m,h0,num1) c if(me.eq.0) then call load_ga(g_a, h0, num_m, num_k) call load_ga(g_b, h0, num_k, num_n) endif call ga_zero(g_c) call ga_sync() if (ga_nodeid().eq.0) then write(*,*) ! for new line write(*,*) 'Matrix Multiplication C = A[', num_m, ',', . num_k, '] x B[', num_k, ',', num_n, ']' write(*,*) ! for new line call ffflush(6) endif do i = 1, ntrans avg_t(i) = 0.0d0 avg_mf(i) = 0.0d0 enddo do itime = 1, ntimes do i = 1, ntrans call ga_sync() ta = transa(i) tb = transb(i) t1 = util_timer() call ga_dgemm(ta,tb,num_m,num_n,num_k, & 1.0d0, g_a, g_b, 0.0d0, g_c) t1 = util_timer() - t1 if (me.eq.0) then mf = 2d0*num_m*num_n*num_k/t1*1d-6/ga_nnodes() avg_t(i) = avg_t(i)+t1 avg_mf(i) = avg_mf(i) + mf write(6,200) ' Run#', itime, t1, mf, ta, tb call ffflush(6) if (verify .AND. itime.eq.1) then call verify_ga_dgemm(ta, tb, num_m, num_n, num_k, & 1.0d0, g_a, g_b, 0.0d0, g_c,tmpa,tmpb,tmpc) endif endif enddo enddo if (ga_nodeid().eq.0) then write(*,*) ! for new line do i = 1, ntrans write(6,200) 'Average:',0,avg_t(i)/ntimes, . avg_mf(i)/ntimes,transa(i),transb(i) enddo if(verify) write(*,*) 'All ga_dgemms are verified...O.K.' write(*,*) ! for new line call ffflush(6) endif c c call ga_print(g_a) c call ga_print(g_b) c call ga_print(g_c) c if (.not.ga_destroy(g_c)) then call ga_error('failed: destroy g_c',20) endif if (.not.ga_destroy(g_b)) then call ga_error('failed: destroy g_b',30) endif if (.not.ga_destroy(g_a)) then call ga_error('failed: destroy g_a',40) endif enddo 200 format(a15, i2, ': ', e12.4, ' seconds ',f12.1, . ' mflops/proc ', 3a2) c if(ga_nodeid().eq.0) print *,'All tests successful ' c call ga_terminate call MP_FINALIZE() c end c c----------------------------------------------------------------------- c Verify for correctness. Process 0 computes BLAS dgemm c locally. For larger arrays, disbale this test as memory c might not be sufficient c subroutine verify_ga_dgemm(xt1, xt2, num_m, num_n, num_k, $ alpha, g_a, g_b, beta, g_c, tmpa, tmpb, tmpc) implicit none character *1 xt1, xt2 integer num_m, num_n, num_k, g_a, g_b, g_c double precision alpha, beta real *8 tmpa(num_m,num_k), tmpb(num_k,num_n), tmpc(num_m,num_n) integer i,j,type,dim1, dim2 real *8 abs_value #include "mafdecls.fh" #include "testutil.fh" #include "galinalg.fh" do i = 1,num_n do j = 1, num_m tmpc(j,i) = -1.0 tmpa(j,i) = -2.0 enddo enddo call ga_inquire(g_a, type, dim1, dim2) call ga_get(g_a, 1, dim1, 1, dim2, tmpa, dim1) call ga_inquire(g_b, type, dim1, dim2) call ga_get(g_b, 1, dim1, 1, dim2, tmpb, dim1) c compute dgemm sequentially call dgemm(xt1, xt2, num_m, num_n, num_k, alpha, & tmpa, num_m, tmpb, num_k, beta, tmpc, num_m) c after computing c locally, verify it with the values in g_c call ga_inquire(g_c, type, dim1, dim2) call ga_get(g_c, 1, dim1, 1, dim2, tmpa, dim1) do i = 1,num_n do j = 1, num_m abs_value = abs(tmpc(j,i)-tmpa(j,i)) if(abs_value .gt. 1.0 .OR. abs_value .lt. -1.0) then write(*,*) 'Values are = ', tmpc(j,i), tmpa(j,i) write(*,*) 'Values are = ', abs(tmpc(j,i)-tmpa(j,i)), . abs_value call ffflush(6) call ga_error('verify ga_dgemm failed', 0) endif enddo enddo return end c c----------------------------------------------------------------------- c called by process '0' (or your master process ) c subroutine load_ga(handle,f, dim1,dim2) implicit none integer handle integer dim1,dim2,i real*8 f(dim1,dim2) #include "mafdecls.fh" if(dim1.le.0)return if(dim2.le.0)return call ga_put(handle, 1, dim1, 1, dim2, f, dim1) return end c c----------------------------------------------------------------------- c must be called by all processors, if you need to fillup the c entire array c subroutine load_ga_from_square(handle,num,f,ndim) implicit none integer handle, memhandle integer num,ndim real*8 f(ndim,ndim) integer ilo, ihi, jlo, jhi, nx, ny, ibuff integer ga_nodeid, i1, i2, i, j, ix, jx #include "mafdecls.fh" call ga_distribution(handle, ga_nodeid(), ilo, ihi, jlo, jhi) if(ihi.le.0)return if(jhi.le.0)return c nx = ihi - ilo + 1 c ny = jhi - jlo + 1 do i = ilo,ihi,ndim do j = jlo,jhi,ndim call ga_put(handle,i,min(ihi,i+ndim),j,min(jhi,j+ndim), & f,ndim) enddo enddo return end c c----------------------------------------------------------------------- c must be called by all processors, if you need to fillup the c entire array c subroutine load_ga_from_triangle(handle,f,ndim) implicit none integer handle, memhandle real*8 f(*) integer ndim integer ilo, ihi, jlo, jhi, nx, ny, ibuff integer ga_nodeid, i1, i2, i, j, ix, jx #include "mafdecls.fh" call ga_distribution(handle, ga_nodeid(), ilo, ihi, jlo, jhi) if(ihi.le.0)return if(jhi.le.0)return nx = ihi - ilo + 1 ny = jhi - jlo + 1 if (.not.ma_alloc_get(MT_DBL,nx*ny,'flap',memhandle,ibuff)) then call ga_error('failed: allocate triangle',100) endif do i = 1,nx do j = 1,ny ix = i + ilo - 1 jx = j + jlo - 1 i1 = min(ix,jx) i2 = max(ix,jx) dbl_mb(ibuff + nx*(j-1) + (i-1) ) = f(i2*(i2-1)/2 + i1) enddo enddo call ga_put(handle,ilo,ihi,jlo,jhi, & dbl_mb(ibuff),nx) if (.not.ma_free_heap(memhandle)) then call ga_error('failed: free triangle',100) endif return end ga-5.9.2/global/testing/testmatmultc.c000066400000000000000000000262071500715745200177420ustar00rootroot00000000000000/** * @file testmatmultc.c * @author Jeff Daily, PNNL jeff.daily@pnl.gov * * This is the C version of testmatmul.F. The port was as direct as possible, * hopefully. */ #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include "mp3.h" #include "ga.h" #include "macdecls.h" #include "xgemm.h" void load_ga(int handle, double *f, int dim1, int dim2); void verify_ga_dgemm(char xt1, char xt2, int num_m, int num_n, int num_k, double alpha, int g_a, int g_b, double beta, int g_c, double *tmpa, double *tmpb, double *tmpc); #if HAVE_BLAS extern void dgemm_(char *, char *, int *, int *, int *, double *, const double *, int *, const double *, int *, double *, double *, int *); #else extern void xb_dgemm(char *, char *, int *, int *, int *, double *, const double *, int *, const double *, int *, double *, double *, int *); #endif #define dgemm_verify 1 #define nummax 1024 #define howmany 2 #define ntrans 4 //#define BLOCK_CYCLIC 1 /* * test ga_dgemm * Note: - change nummax for large arrays * - turn off "dgemm_verify" for large arrays due to memory * limitations, as dgemm_verify=1 for large arrays produces * segfault, dumps core,or any crap. */ int main(int argc, char **argv) { int num_m; int num_n; int num_k; int i; int ii; double *h0; int g_c; int g_b; int g_a; double a; double t1; double mf; double avg_t[ntrans]; double avg_mf[ntrans]; int itime; int ntimes; int me; int nums_m[/*howmany*/] = {512,1024}; int nums_n[/*howmany*/] = {512,1024}; int nums_k[/*howmany*/] = {512,1024}; char transa[/*ntrans*/] = "nnnn"; char transb[/*ntrans*/] = "nnnn"; char ta; char tb; double *tmpa; double *tmpb; double *tmpc; int ndim; int dims[2]; #ifdef BLOCK_CYCLIC int block_size[2]; #endif MP_INIT(argc,argv); if (!MA_init(MT_DBL,1,20000000)) { GA_Error("failed: ma_init(MT_DBL,1,20000000)",10); } GA_INIT(argc,argv); me = GA_Nodeid(); h0 = (double*)malloc(sizeof(double) * nummax*nummax); tmpa = (double*)malloc(sizeof(double) * nummax*nummax); tmpb = (double*)malloc(sizeof(double) * nummax*nummax); tmpc = (double*)malloc(sizeof(double) * nummax*nummax); ii = 0; for (i=0; i nummax) { ii = 0; } h0[i] = ii; } /* Compute times assuming 500 mflops and 5 second target time */ /* ntimes = max(3.0d0,5.0d0/(4.0d-9*num**3)); */ ntimes = 5; for (ii=0; ii nummax || num_n > nummax || num_k > nummax) { GA_Error("Insufficient memory: check nummax", 1); } #ifndef BLOCK_CYCLIC ndim = 2; dims[0] = num_m; dims[1] = num_n; if (!((g_c = NGA_Create(MT_DBL,ndim,dims,"g_c",NULL)))) { GA_Error("failed: create g_c",20); } dims[0] = num_k; dims[1] = num_n; if (!((g_b = NGA_Create(MT_DBL,ndim,dims,"g_b",NULL)))) { GA_Error("failed: create g_b",30); } dims[0] = num_m; dims[1] = num_k; if (!((g_a = NGA_Create(MT_DBL,ndim,dims,"g_a",NULL)))) { GA_Error("failed: create g_a",40); } #else ndim = 2; block_size[0] = 128; block_size[1] = 128; dims[0] = num_m; dims[1] = num_n; g_c = GA_Create_handle(); GA_Set_data(g_c,ndim,dims,MT_DBL); GA_Set_array_name(g_c,"g_c"); GA_Set_block_cyclic(g_c,block_size); if (!GA_Allocate(g_c)) { GA_Error("failed: create g_c",40); } dims[0] = num_k; dims[1] = num_n; g_b = GA_Create_handle(); GA_Set_data(g_b,ndim,dims,MT_DBL); GA_Set_array_name(g_b,"g_b"); GA_Set_block_cyclic(g_b,block_size); if (!GA_Allocate(g_b)) { GA_Error("failed: create g_b",40); } dims[0] = num_m; dims[1] = num_k; g_a = GA_Create_handle(); GA_Set_data(g_a,ndim,dims,MT_DBL); GA_Set_array_name(g_a,"g_a"); GA_Set_block_cyclic(g_a,block_size); if (!GA_Allocate(g_a)) { GA_Error("failed: create g_a",40); } #endif /* Initialize matrices A and B */ if (me == 0) { load_ga(g_a, h0, num_m, num_k); load_ga(g_b, h0, num_k, num_n); } GA_Zero(g_c); GA_Sync(); if (GA_Nodeid() == 0) { printf("\nMatrix Multiplication on C = A[%ld,%ld]xB[%ld,%ld]\n", (long)num_m, (long)num_k, (long)num_k, (long)num_n); fflush(stdout); } for (i=0; i 1.0 || abs_value < -1.0) { printf("Values are = %f %f\n", tmpc[j+i*num_m], tmpa[j+i*num_m]); printf("Values are = %f %f\n", fabs(tmpc[j+i*num_m]-tmpa[j*i*num_m]), abs_value); fflush(stdout); GA_Error("verify ga_dgemm failed", 1); } } } } /** * called by process '0' (or your master process ) */ void load_ga(int handle, double *f, int dim1, int dim2) { int lo[2], hi[2]; if (dim1 < 0 || dim2 < 0) { return; } lo[0] = 0; lo[1] = 0; hi[0] = dim1-1; hi[1] = dim2-1; NGA_Put(handle, lo, hi, f, &dim1); } /* c c----------------------------------------------------------------------- c must be called by all processors, if you need to fillup the c entire array c subroutine load_ga_from_square(handle,num,f,ndim) implicit none integer handle, memhandle integer num,ndim real*8 f(ndim,ndim) integer ilo, ihi, jlo, jhi, nx, ny, ibuff integer ga_nodeid, i1, i2, i, j, ix, jx #include "mafdecls.fh" call ga_distribution(handle, ga_nodeid(), ilo, ihi, jlo, jhi) if(ihi.le.0)return if(jhi.le.0)return c nx = ihi - ilo + 1 c ny = jhi - jlo + 1 do i = ilo,ihi,ndim do j = jlo,jhi,ndim call ga_put(handle,i,min(ihi,i+ndim),j,min(jhi,j+ndim), & f,ndim) enddo enddo return end */ /* c c----------------------------------------------------------------------- c must be called by all processors, if you need to fillup the c entire array c subroutine load_ga_from_triangle(handle,f,ndim) implicit none integer handle, memhandle real*8 f(*) integer ndim integer ilo, ihi, jlo, jhi, nx, ny, ibuff integer ga_nodeid, i1, i2, i, j, ix, jx #include "mafdecls.fh" call ga_distribution(handle, ga_nodeid(), ilo, ihi, jlo, jhi) if(ihi.le.0)return if(jhi.le.0)return nx = ihi - ilo + 1 ny = jhi - jlo + 1 if (.not.ma_alloc_get(MT_DBL,nx*ny,'flap',memhandle,ibuff)) then call ga_error('failed: allocate triangle',100) endif do i = 1,nx do j = 1,ny ix = i + ilo - 1 jx = j + jlo - 1 i1 = min(ix,jx) i2 = max(ix,jx) dbl_mb(ibuff + nx*(j-1) + (i-1) ) = f(i2*(i2-1)/2 + i1) enddo enddo call ga_put(handle,ilo,ihi,jlo,jhi, & dbl_mb(ibuff),nx) if (.not.ma_free_heap(memhandle)) then call ga_error('failed: free triangle',100) endif return end */ ga-5.9.2/global/testing/testmult.c000066400000000000000000000147051500715745200170750ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_MATH_H # include #endif #include "macdecls.h" #include "ga.h" #include "mp3.h" #define N 400 /* first dimension */ #define BASE 0 #define GA_DATA_TYPE MT_C_FLOAT #define GA_ABS(a) (((a) >= 0) ? (a) : (-(a))) #define TOLERANCE 0.000001 DoublePrecision gTime=0.0, gStart; void test(int data_type, int ndim) { int me=GA_Nodeid(); int g_a, g_b, g_c, g_A, g_B, g_C; int dims[GA_MAX_DIM]={N,N,2,2,2,1,1}; int lo[GA_MAX_DIM]={1,1,1,1,1,0,0}; int hi[GA_MAX_DIM]={N-2,N-2,1,1,1,0,0}; int clo[2], chi[2], m, n, k; double value1_dbl = 2.0, value2_dbl = 2.0; double alpha_dbl = 1.0, beta_dbl = 0.0; float value1_flt = 2.0, value2_flt = 2.0; float alpha_flt = 1.0, beta_flt = 0.0; DoubleComplex value1_dcpl = {2.0, 2.0}, value2_dcpl = {2.0, 2.0}; DoubleComplex alpha_dcpl = {1.0, 0.0} , beta_dcpl = {0.0, 0.0}; SingleComplex value1_scpl = {2.0, 2.0}, value2_scpl = {2.0, 2.0}; SingleComplex alpha_scpl = {1.0, 0.0} , beta_scpl = {0.0, 0.0}; void *value1=NULL, *value2=NULL, *alpha=NULL, *beta=NULL; switch (data_type) { case C_FLOAT: alpha = (void *)&alpha_flt; beta = (void *)&beta_flt; value1 = (void *)&value1_flt; value2 = (void *)&value2_flt; if(me==0) printf("Single Precision: Testing GA_Sgemm,NGA_Matmul_patch for %d-Dimension", ndim); break; case C_DBL: alpha = (void *)&alpha_dbl; beta = (void *)&beta_dbl; value1 = (void *)&value1_dbl; value2 = (void *)&value2_dbl; if(me==0) printf("Double Precision: Testing GA_Dgemm,NGA_Matmul_patch for %d-Dimension", ndim); break; case C_DCPL: alpha = (void *)&alpha_dcpl; beta = (void *)&beta_dcpl; value1 = (void *)&value1_dcpl; value2 = (void *)&value2_dcpl; if(me==0) printf("Double Complex: Testing GA_Zgemm,NGA_Matmul_patch for %d-Dimension", ndim); break; case C_SCPL: alpha = (void *)&alpha_scpl; beta = (void *)&beta_scpl; value1 = (void *)&value1_scpl; value2 = (void *)&value2_scpl; if(me==0) printf("Single Complex: Testing GA_Cgemm,NGA_Matmul_patch for %d-Dimension", ndim); break; default: GA_Error("wrong data type", data_type); } g_a = NGA_Create(data_type, ndim, dims, "array A", NULL); g_b = GA_Duplicate(g_a, "array B"); g_c = GA_Duplicate(g_a, "array C"); if(!g_a || !g_b || !g_c) GA_Error("Create failed: a, b or c",1); GA_Fill(g_a, value1); GA_Fill(g_b, value2); GA_Zero(g_c); NGA_Matmul_patch('N', 'N', alpha, beta, g_a, lo, hi, g_b, lo, hi, g_c, lo, hi); GA_Destroy(g_a); GA_Destroy(g_b); /** * Verifying g_c: * 1. Create g_A(=g_a) and g_B(=g_b) * 2. g_C = g_A*g_B; (Using Gemm routines) * 3. g_A = g_c; (copy the 2-d patch og g_c into g_A) * 4. g_C = g_A - g_C; (Using add() routine by making beta=-1.0) * 5. If all the elements in g_C is zero, implies SUCCESS. */ dims[0] = dims[1] = m = n = k = N-2; g_A = NGA_Create(data_type, 2, dims, "array A_", NULL); g_B = GA_Duplicate(g_A, "array B_"); g_C = GA_Duplicate(g_A, "array C_"); if(!g_A || !g_B || !g_C) GA_Error("Create failed: A, B or C",n); GA_Fill(g_A, value1); GA_Fill(g_B, value2); GA_Zero(g_C); gStart = MP_TIMER(); switch (data_type) { case C_FLOAT: GA_Sgemm('N', 'N', m, n, k, alpha_flt, g_A, g_B, beta_flt, g_C); beta_flt = -1.0; break; case C_DBL: GA_Dgemm('N', 'N', m, n, k, alpha_dbl, g_A, g_B, beta_dbl, g_C); beta_dbl = -1.0; break; case C_DCPL: GA_Zgemm('N', 'N', m, n, k, alpha_dcpl, g_A, g_B, beta_dcpl, g_C); beta_dcpl.real = -1.0; break; case C_SCPL: GA_Cgemm('N', 'N', m, n, k, alpha_scpl, g_A, g_B, beta_scpl, g_C); beta_scpl.real = -1.0; break; default: GA_Error("wrong data type", data_type); } gTime += MP_TIMER()-gStart; GA_Destroy(g_B); clo[0] = clo[1] = 0; chi[0] = chi[1] = N-3; NGA_Copy_patch('N', g_c, lo, hi, g_A, clo, chi) ; GA_Add(alpha, g_A, beta, g_C, g_C); /* NGA_Add_patch (alpha, g_c, lo, hi, beta, g_C, clo, chi, g_C, clo, chi);*/ switch (data_type) { case C_FLOAT: value1_flt = GA_Fdot(g_C, g_C); if(fabsf(value1_flt) > TOLERANCE) { printf("\nabs(result) = %f > %f\n", fabsf(value1_flt), TOLERANCE); GA_Error("GA_Sgemm, NGA_Matmul_patch Failed", 1); } break; case C_DBL: value1_dbl = GA_Ddot(g_C, g_C); if(fabs(value1_dbl) > TOLERANCE) { printf("\nabs(result) = %f > %f\n", fabs(value1_dbl), TOLERANCE); GA_Error("GA_Dgemm, NGA_Matmul_patch Failed", 1); } break; case C_DCPL: value1_dcpl = GA_Zdot(g_C, g_C); if(fabs(value1_dcpl.real) > TOLERANCE || fabs(value1_dcpl.imag) > TOLERANCE) { printf("\nabs(result) = %f+%fi > %f\n", fabs(value1_dcpl.real), fabs(value1_dcpl.imag), TOLERANCE); GA_Error("GA_Zgemm, NGA_Matmul_patch Failed", 1); } break; case C_SCPL: value1_scpl = GA_Cdot(g_C, g_C); if(fabsf(value1_scpl.real) > TOLERANCE || fabsf(value1_scpl.imag) > TOLERANCE) { printf("\nabs(result) = %f+%fi > %f\n", fabsf(value1_scpl.real), fabsf(value1_scpl.imag), TOLERANCE); GA_Error("GA_Sgemm, NGA_Matmul_patch Failed", 1); } break; default: GA_Error("wrong data type", data_type); } if(me==0) printf("....OK\n"); GA_Destroy(g_A); GA_Destroy(g_c); GA_Destroy(g_C); } void do_work() { int i; int me = GA_Nodeid(); for(i=2; i<=GA_MAX_DIM; i++) { test(C_FLOAT, i); test(C_DBL, i); test(C_DCPL, i); test(C_SCPL, i); if(me == 0) printf("\n\n"); GA_Sync(); } } int main(int argc, char **argv) { Integer heap=9000000, stack=9000000; int me, nproc; DoublePrecision time; MP_INIT(argc,argv); GA_INIT(argc,argv); /* initialize GA */ nproc = GA_Nnodes(); me = GA_Nodeid(); if(me==0) printf("Using %d processes\n\n",nproc); if(!MA_init((Integer)MT_F_DBL, stack/nproc, heap/nproc)) GA_Error("MA_init failed bytes= %d",stack+heap); if(GA_Uses_fapi())GA_Error("Program runs with C API only",1); time = MP_TIMER(); do_work(); /* printf("%d: Total Time = %lf\n", me, MP_TIMER()-time); printf("%d: GEMM Total Time = %lf\n", me, gTime); */ if (me == 0) printf("\nAll tests successful\n"); GA_Terminate(); MP_FINALIZE(); return 0; } ga-5.9.2/global/testing/testmultrect.c000066400000000000000000000043001500715745200177410ustar00rootroot00000000000000/** * testmultrect.c * Test rectangular matrix multiplication. * * Nawab Ali */ #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include "ga.h" #include "macdecls.h" #include "mp3.h" #define NDIMS 2 /* 2-dimensional matrices */ #define HEAP 4000000 #define STACK 4000000 int m = 2; int k = 3; int n = 4; int g_a, g_b, g_c; /* GA handles for matrices A, B, and C */ int mr_create_matrices(void); int mr_cleanup(void); int main(int argc, char **argv) { int ret, nprocs; int me, heap, stack; /* Initialize message passing and GA */ MP_INIT(argc,argv); GA_INIT(argc,argv); me = GA_Nodeid(); nprocs = GA_Nnodes(); heap = HEAP/nprocs; stack = STACK/nprocs; if (!MA_init(C_DBL, stack, heap)) { GA_Error("MA_init failed", stack+heap); } /* Create the matrices A, B, C */ ret = mr_create_matrices(); GA_Print(g_a); GA_Print(g_b); GA_Print(g_c); /* C = A x B */ GA_Dgemm('N', 'N', m, n, k, 1.0, g_a, g_b, 0.0, g_c); /* Clean up */ ret = mr_cleanup(); if (me == 0) printf("All tests successful\n"); GA_Terminate(); MP_FINALIZE(); return 0; } int mr_create_matrices(void) { double val_a = 10.0; double val_b = 20.0; int ret, dims[NDIMS]; /* Create a 2-dimensional matrix A[m][k] */ dims[0] = m; dims[1] = k; g_a = GA_Create_handle(); GA_Set_array_name(g_a, "Matrix A"); GA_Set_data(g_a, NDIMS, dims, C_DBL); ret = GA_Allocate(g_a); GA_Fill(g_a, &val_a); /* Create a 2-dimensional matrix B[k][n] */ dims[0] = k; dims[1] = n; g_b = GA_Create_handle(); GA_Set_array_name(g_b, "Matrix B"); GA_Set_data(g_b, NDIMS, dims, C_DBL); ret = GA_Allocate(g_b); GA_Fill(g_b, &val_b); /* Create a 2-dimensional matrix C[m][n] */ dims[0] = m; dims[1] = n; g_c = GA_Create_handle(); GA_Set_array_name(g_c, "Matrix C"); GA_Set_data(g_c, NDIMS, dims, C_DBL); ret = GA_Allocate(g_c); GA_Zero(g_c); return 0; } int mr_cleanup(void) { GA_Destroy(g_a); GA_Destroy(g_b); GA_Destroy(g_c); return 0; } ga-5.9.2/global/testing/testsolve.F000066400000000000000000000227521500715745200172100ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c $Id: testsolve.F,v 1.11 2006/03/20 20:02:29 manoj Exp $ program test implicit none #include "mafdecls.fh" #include "global.fh" integer heap, stack #define BLOCK_CYCLIC 0 c c*** Intitialize a message passing library c #include "mp3.fh" c c Intitialize the GA package c call ga_initialize() c if(ga_nodeid().eq.0)print *,ga_nnodes(),' nodes' c c Initialize the MA package c heap = 190000 stack= 190000 if (.not. ma_init(MT_DBL, heap, stack)) $ call ga_error("ma init failed",heap+stack) c c call testit() c if(ga_nodeid().eq.0) print *,'All tests successful ' c call ga_terminate() c call MP_FINALIZE() end c----------------- subroutine testit() implicit none #include "mafdecls.fh" #include "global.fh" c integer n parameter (n = 100) double precision a(n,n), b(n,n), c(n,n) integer g_a,g_b,g_c,g_d, g_e, g_f, g_g integer g_aa,g_bb,g_cc,g_dd, g_ee, g_ff, g_gg integer i, j integer ndim, dims(2), block(2), proc_grid(2), g1, g2 integer nproc, me double precision dsin, sum logical status c nproc = ga_nnodes() me = ga_nodeid() c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n a(i,j) = 1d0 * (i+j) b(i,j) = DSIN(1d0* (i+j)) if(i.eq.j) then b(i,j) = 2d0 *n a(i,j) = i endif if(i.le.j)then c(i,j) = a(i,j) else c(i,j) = 0d0 endif enddo enddo #if 0 if (me.eq.0) then open(unit=7,file='amat.dat',status='unknown') do i = 1, min(20,n) write(7,128) (a(i,j),j=1,min(20,n)) 128 format(20f6.1) end do endif #endif c c*** Create global arrays #if BLOCK_CYCLIC if (me.eq.0) then write(6,*) '*' write(6,*) '* Creating Block-Cyclic Arrays' write(6,*) '*' endif dims(1) = n dims(2) = n block(1) = 2 block(2) = 2 call factor(nproc,g1,g2) proc_grid(1) = g1 proc_grid(2) = g2 g_a = ga_create_handle() call ga_set_data(g_a,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_a,block,proc_grid) status = ga_allocate(g_a) g_b = ga_create_handle() call ga_set_data(g_b,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_b,block,proc_grid) status = ga_allocate(g_b) g_c = ga_create_handle() call ga_set_data(g_c,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_c,block,proc_grid) status = ga_allocate(g_c) g_d = ga_create_handle() call ga_set_data(g_d,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_d,block,proc_grid) status = ga_allocate(g_d) dims(2) = 1 g_e = ga_create_handle() call ga_set_data(g_e,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_e,block,proc_grid) status = ga_allocate(g_e) g_f = ga_create_handle() call ga_set_data(g_f,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_f,block,proc_grid) status = ga_allocate(g_f) g_g = ga_create_handle() call ga_set_data(g_g,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_g,block,proc_grid) status = ga_allocate(g_g) g_aa = ga_create_handle() dims(2) = n call ga_set_data(g_aa,2,dims,MT_DBL) status = ga_allocate(g_aa) g_bb = ga_create_handle() call ga_set_data(g_bb,2,dims,MT_DBL) status = ga_allocate(g_bb) g_cc = ga_create_handle() call ga_set_data(g_cc,2,dims,MT_DBL) status = ga_allocate(g_cc) g_dd = ga_create_handle() call ga_set_data(g_dd,2,dims,MT_DBL) status = ga_allocate(g_dd) dims(2) = 1 g_ee = ga_create_handle() call ga_set_data(g_ee,2,dims,MT_DBL) status = ga_allocate(g_ee) g_ff = ga_create_handle() call ga_set_data(g_ff,2,dims,MT_DBL) status = ga_allocate(g_ff) g_gg = ga_create_handle() call ga_set_data(g_gg,2,dims,MT_DBL) status = ga_allocate(g_gg) #else if (.not. ga_create(MT_DBL, n, n, 'a', 1, 1, g_a)) $ call ga_error(' ga_create failed ',2) if (.not. ga_create(MT_DBL, n, n, 'b', 1, 1, g_b)) $ call ga_error(' ga_create failed ',2) if (.not. ga_create(MT_DBL, n, n, 'c', 1, 1, g_c)) $ call ga_error(' ga_create failed ',2) if (.not. ga_create(MT_DBL, n, n, 'd', 1, 1, g_d)) $ call ga_error(' ga_create failed ',2) if (.not. ga_create(MT_DBL, n, 1, 'e', 1, 1, g_e)) $ call ga_error(' ga_create failed ',2) if (.not. ga_create(MT_DBL, n, 1, 'f', 1, 1, g_f)) $ call ga_error(' ga_create failed ',2) if (.not. ga_create(MT_DBL, n, 1, 'g', 1, 1, g_g)) $ call ga_error(' ga_create failed ',2) #endif c c c*** Fill in arrays A & B if (me .eq. 0) then print *, ' filling in A and B ' call ffflush(6) call ga_put(g_e, 1,n, 1,1, b(1,1),n) call ga_put(g_f, 1,n, 1,1, b(1,1),n) endif do j = 1+me, n, nproc call ga_put(g_a, 1,n, j,j, a(1,j),n) call ga_put(g_b, 1,n, j,j, b(1,j),n) call ga_put(g_c, 1,n, j,j, b(1,j),n) enddo call ga_sync() c c call ga_copy(g_b,g_c) c if (me .eq. 0) then print *,' ' print *, '>Test of the LU-based solver with nxn rhs ' print *,' ' call ffflush(6) endif #ifndef HAVE_SCALAPACK call ga_lu_solve_seq('n', g_a, g_b) #else #if BLOCK_CYCLIC call ga_copy(g_a,g_aa) #endif call ga_lu_solve('n', g_a, g_b) #if BLOCK_CYCLIC call ga_copy(g_aa,g_a) #endif #endif #if BLOCK_CYCLIC c call print_block(g_b) call ga_copy(g_b,g_bb) call ga_copy(g_c,g_cc) call ga_copy(g_d,g_dd) call ga_dgemm('n','n',n,n,n, 1d0, g_aa, g_bb, 0d0, g_dd) ! d := a*b call ga_add(1d0, g_dd, -1d0, g_cc, g_cc) sum = ga_ddot(g_cc,g_cc) #else c call print_rblock(g_b) call ga_dgemm('n','n',n,n,n, 1d0, g_a, g_b, 0d0, g_d) ! d := a*b call ga_add(1d0, g_d, -1d0, g_c, g_c) sum = ga_ddot(g_c,g_c) #endif if (me .eq. 0) then print *,' ' print *, ' dsqrt(sum) = ', dsqrt(sum) print *, ' n = ', n print *, ' norm = ', dsqrt(sum)/n if(dsqrt(sum)/n.lt.1d-10) then print *, ' test passed ' else call ga_error(' test failed ',3) endif print *,' ' call ffflush(6) endif c if (me .eq. 0) then print *,' ' print *,'>Test of the LU-based solver with a single vector rhs' print *,' ' call ffflush(6) endif c #ifndef HAVE_SCALAPACK call ga_lu_solve_seq('n', g_a, g_e) #else #if BLOCK_CYCLIC call ga_copy(g_a,g_aa) #endif call ga_lu_solve('n', g_a, g_e) #endif c #if BLOCK_CYCLIC call ga_copy(g_e,g_ee) call ga_copy(g_f,g_ff) call ga_copy(g_g,g_gg) call ga_dgemm('n','n',n,1,n, 1d0, g_aa, g_ee, 0d0, g_gg) ! g := a*e call ga_add(1d0, g_gg, -1d0, g_ff, g_ff) sum = ga_ddot(g_ff,g_ff) #else call ga_dgemm('n','n',n,1,n, 1d0, g_a, g_e, 0d0, g_g) ! g := a*e call ga_add(1d0, g_g, -1d0, g_f, g_f) sum = ga_ddot(g_f,g_f) #endif if (me .eq. 0) then print *,' ' print *, ' norm = ', dsqrt(sum)/n if(dsqrt(sum)/n.lt.1d-10) then print *, ' test passed ' else call ga_error(' test failed ',4) endif print *,' ' call ffflush(6) endif end c subroutine factor(p,idx,idy) implicit none integer i,j,p,idx,idy,it integer ip,ifac,pmax,prime(1280) integer fac(1280) c i = 1 ip = p c c factor p completely c first, find all prime numbers less than or equal to p c pmax = 0 do i = 2, p do j = 1, pmax if (mod(i,prime(j)).eq.0) go to 100 end do pmax = pmax + 1 prime(pmax) = i 100 continue end do c c find all prime factors of p c ifac = 0 do i = 1, pmax 200 if (mod(ip,prime(i)).eq.0) then ifac = ifac + 1 fac(ifac) = prime(i) ip = ip/prime(i) go to 200 endif end do c c determine two factors of p of approximately the c same size c idx = 1 idy = 1 do i = ifac, 1, -1 if (idx.le.idy) then idx = fac(i)*idx else idy = fac(i)*idy endif end do return end c subroutine print_block(g_a) implicit none #include "mafdecls.fh" #include "global.fh" integer g_a, i, j, istride, jstride integer idx, ld, me me = ga_nodeid() istride = 10 if (me.eq.0) then jstride = 6 else jstride = 4 endif call nga_access_block_segment(g_a,me,idx,ld) do i = 1, istride write(6,733) me,(dbl_mb(idx+(j-1)*istride+i-1),j=1,jstride) end do 733 format(i8,8f12.6) return end c subroutine print_rblock(g_a) implicit none #include "mafdecls.fh" #include "global.fh" integer g_a, i, j, istride, jstride integer idx, ld, me, lo(2), hi(2) me = ga_nodeid() istride = 10 jstride = 5 call nga_distribution(g_a,me,lo,hi) call nga_access(g_a,lo,hi,idx,ld) do i = 1, istride write(6,733) me,(dbl_mb(idx+(j-1)*istride+i-1),j=1,jstride) end do 733 format(i8,8f12.6) return end ga-5.9.2/global/testing/testsparse.c000066400000000000000000000155651500715745200174160ustar00rootroot00000000000000#include #include #include #include "macdecls.h" #include "ga.h" #include "mp3.h" #define NDIM 16384 #define MAX_FACTOR 1024 void grid_factor(int p, int xdim, int ydim, int *idx, int *idy) { int i, j; int ip, ifac, pmax, prime[MAX_FACTOR]; int fac[MAX_FACTOR]; int ix, iy, ichk; i = 1; /** * factor p completely * first, find all prime numbers, besides 1, less than or equal to * the square root of p */ ip = (int)(sqrt((double)p))+1; pmax = 0; for (i=2; i<=ip; i++) { ichk = 1; for (j=0; j MAX_FACTOR) printf("Overflow in grid_factor\n"); prime[pmax-1] = i; } } /** * find all prime factors of p */ ip = p; ifac = 0; for (i=0; i= 0; i--) { ix = xdim/(*idx); iy = ydim/(*idy); if (ix >= iy && ix > 1) { *idx = fac[i]*(*idx); } else if (iy >= ix && iy > 1) { *idy = fac[i]*(*idy); } else { printf("Too many processors in grid factoring routine\n"); } } } int main(int argc, char **argv) { int s_a, g_v, g_av; int one; int64_t one_64; int me, nproc; int idim, jdim; int64_t xdim, ydim; int ipx, ipy, idx, idy; int64_t ilo, ihi, jlo, jhi; int64_t i, j; int iproc, ld, ncols, ncnt; double val; double *vptr; double *vbuf, *vsum; int64_t *iptr = NULL, *jptr = NULL; int ok; double r_one = 1.0; double ir, jr, ldr; /* Intitialize a message passing library */ one = 1; one_64 = 1; MP_INIT(argc,argv); /* Initialize GA */ NGA_Initialize(); xdim = NDIM; ydim = NDIM; idim = NDIM; jdim = NDIM; me = GA_Nodeid(); nproc = GA_Nnodes(); /* factor array */ grid_factor(nproc, idim, jdim, &ipx, &ipy); if (me == 0) { printf("Testing sparse array on %d processors\n",nproc); printf("\n Using %d X %d processor grid\n",ipx,ipy); printf("\n Matrix size is %d X %d\n",idim,jdim); } /* figure out process location in proc grid */ idx = me%ipx; idy = (me-idx)/ipx; /* find bounding indices for this processor */ ilo = (xdim*idx)/ipx; if (idx < ipx-1) { ihi = (xdim*(idx+1))/ipx-1; } else { ihi = xdim-1; } jlo = (ydim*idy)/ipy; if (idy < ipy-1) { jhi = (ydim*(idy+1))/ipy-1; } else { jhi = ydim-1; } /* create sparse array */ s_a = NGA_Sprs_array_create64(xdim, ydim, C_DBL); if (ydim%2 == 0) { ld = ydim/2; } else { ld = (ydim-1)/2+1; } ldr = (double)ld; /* add elements to array. Every other element is zero */ for (i=ilo; i<=ihi; i++) { ir = (double)(i/2); for (j=jlo; j<=jhi; j++) { jr = (double)(j/2); if (i%2 == 0 && j%2 == 0) { val = (ir)*ldr+jr; NGA_Sprs_array_add_element64(s_a,i,j,&val); } } } if (NGA_Sprs_array_assemble(s_a) && me == 0) { printf("\n Sparse array assembly completed\n"); } /* construct vector */ g_v = NGA_Create_handle(); NGA_Set_data64(g_v,one,&ydim,C_DBL); NGA_Allocate(g_v); g_av = GA_Duplicate(g_v, "dup"); GA_Zero(g_av); /* set vector values */ NGA_Distribution64(g_v,me,&ilo,&ihi); NGA_Access64(g_v,&ilo,&ihi,&vptr,&one_64); for (i=ilo;i<=ihi;i++) { vptr[i-ilo] = (double)i; } if (me == 0) { printf("\n Vector initialized\n"); } NGA_Release64(g_v,&ilo,&ihi); /* access array blocks and check values for correctness */ NGA_Sprs_array_row_distribution64(s_a,me,&ilo,&ihi); ok = 1; ncnt = 0; for (iproc=0; iproc 1.0e-5) { ok = 0; printf("p[%d] i: %d j: %d val: %f\n",me,(int)i, (int)jptr[iptr[i-ilo]+j],vptr[iptr[i-ilo]+j]); } } } } } GA_Igop(&ncnt,one,"+"); if (ncnt != (idim/2)*(jdim/2)) ok = 0; if (ok && me==0) { printf("\n Values in sparse array are correct\n"); } /* multiply sparse matrix by sparse vector */ vsum = (double*)malloc((ihi-ilo+1)*sizeof(double)); for (i=ilo; i<=ihi; i++) { vsum[i-ilo] = 0.0; } for (iproc=0; iproc=ilo) NGA_Acc64(g_av,&ilo,&ihi,vsum,&one_64,&r_one); GA_Sync(); free(vsum); /* check product vector */ ok = 1; NGA_Distribution64(g_av,me,&ilo,&ihi); NGA_Access64(g_av,&ilo,&ihi,&vptr,&one_64); /* printf("ilo: %d ihi: %d\n",ilo,ihi); */ for (i=ilo; i<=ihi; i++) { val = 0.0; if (i%2 == 0) { for (j=0; j= 1.0e-5) { ok = 0; printf("Error for element %d expected: %f actual: %f\n", (int)i,val,vptr[i-ilo]); } } else { if (fabs(vptr[i-ilo]) >= 1.0e-5) { ok = 0; printf("Error for element %d expected: 0.00000 actual: %f\n", (int)i,vptr[i-ilo]); } } } if (ok && me==0) { printf("\n Matrix-vector product is correct\n\n"); } NGA_Sprs_array_destroy(s_a); NGA_Destroy(g_v); NGA_Destroy(g_av); NGA_Terminate(); /** * Tidy up after message-passing library */ MP_FINALIZE(); } ga-5.9.2/global/testing/testspd.F000066400000000000000000000637061500715745200166520ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c $Id: testspd.F,v 1.8 2004-11-12 22:19:10 edo Exp $ c*********************************************************************** c* Source : testllt c* Scope : test LLT SCALAPACK routines c* c* 04/12/96 GVT First Implementation c* Giuseppe Vitillaro peppe@unipg.it c*********************************************************************** #define SIZE 100 #define UPLO 'L' #define TRESH 3.d0 #define ga_dnormF(g_a) sqrt(ga_ddot(g_a, g_a)) #define BLOCK_CYCLIC 0 c*********************************************************************** c* Program: testllt c*********************************************************************** program testllt implicit none #include "mafdecls.fh" #include "global.fh" c**** integer nproc integer hsize, ssize c c*** Intitialize a message passing library c #include "mp3.fh" c c**** Initialize GA package call ga_initialize() c c**** get number of nodes and calculate memory allocation hsize = 6*SIZE*SIZE + 3*SIZE ssize = 3*SIZE*SIZE + 3*SIZE nproc=ga_nnodes() hsize = (hsize/nproc) + 1 ssize = (ssize/nproc) + 1 + 3*256*256 c**** Initialize MA package if (.not. ma_init(MT_DBL, ssize, hsize)) $ call ga_error("ma init failed",ssize+hsize) c**** Do Core Tests call ctests(UPLO) c**** Exit from the GA package call ga_terminate() c call MP_FINALIZE() end c*********************************************************************** c* subroutine: infos c* print informations about this run c*********************************************************************** subroutine infos(me, nproc, uplo, eps) implicit none #include "mafdecls.fh" #include "global.fh" c**** integer me integer nproc character*1 uplo double precision eps c**** if (me.eq.0) then print *, ' ' print *, 'Number of nodes : ', nproc print *, 'Problem size : ', SIZE print *, 'Uplo : ', uplo print *, 'Epsilon : ', eps call ffflush(6) endif return end c*********************************************************************** c* subroutine: thead c* emit test header output c*********************************************************************** subroutine thead(header) implicit none #include "mafdecls.fh" #include "global.fh" c**** character*(*) header c**** integer me me = ga_nodeid() if (me.eq.0) then print *, ' ' print *, '> ', header, ' ' call ffflush(6) endif return end c*********************************************************************** c* subroutine: dthtest c* test a double precision against the THRESH parameter c*********************************************************************** subroutine dthtest(msg, dval) implicit none #include "mafdecls.fh" #include "global.fh" c**** character*(*) msg double precision dval c**** integer me me = ga_nodeid() if (me.eq.0) then print *, ' ', msg, dval, ' ' if (dval.lt.TRESH) then print *, '> success ' else print *, '> failure ' endif call ffflush(6) endif return end c*********************************************************************** c* subrotine: stest c* test solver result c*********************************************************************** subroutine stest(irc, ierc) implicit none #include "mafdecls.fh" #include "global.fh" c**** integer irc integer ierc c**** integer me me = ga_nodeid() if (me.eq.0) then if (irc.eq.0) then print *, '> LLT ' elseif (irc.gt.0) then print *, '> LU ' endif if (irc.eq.ierc .or. (irc.gt.0 .and. ierc.gt.0)) then print *, '> success: expected factorization' else print *, '> failure: not expected factorization',irc,ierc endif call ffflush(6) endif return end c*********************************************************************** c* subroutine: emsg c* error message c*********************************************************************** subroutine emsg(msg, ival) implicit none #include "mafdecls.fh" #include "global.fh" c**** character*(*) msg integer ival c**** integer me me = ga_nodeid() if (me.eq.0) then print *, ' >>> ', msg, ival, ' ' print *, '> failure ' call ffflush(6) endif return end c*********************************************************************** c* subroutine: ctests c* do coretests for LLT Cholesky factorization, solver c*********************************************************************** subroutine ctests(uplo) implicit none #include "mafdecls.fh" #include "global.fh" integer ga_cholesky external ga_cholesky c**** character*1 uplo c**** integer n parameter (n=SIZE) external ga_llt_f, ga_llt_s, ga_llt_i integer ga_llt_f, ga_llt_i double precision A(n,n), X(n,n) integer g_A, g_B, g_X, g_Y integer g_A1, g_X1, g_Y1, g_Y2 #if BLOCK_CYCLIC integer g_AA, g_BB, g_CC, g_YY, g_ZZ integer dims(2), proc_grid(2), block(2) integer g1, g2 #endif integer hsA integer i, j integer nproc, me double precision buf(n) double precision dnA, dnX, dnX1, dnY1, dnS, dnF, dnI integer irc double precision eps, dlamch eps = dlamch('p') nproc = ga_nnodes() me = ga_nodeid() call infos(me, nproc, uplo, eps) c**************************************** c* Initialize tests variables c* Generate a Lower triangula matrix c* with large positive diagonal elements c* so the LU decomposition will be c* just this matrix c**************************************** c**** Initialize local arrays A, X c**** they are local copies of the corrispondent c**** global arrays do j = 1, n do i = 1, n X(i,j) = dsin(1.d0 * (i * j)) A(i,j) = 0.d0 if (i.gt.j) then A(i,j) = dsin(1.d0 * (i + j)) endif if (i.eq.j) then A(i,j) = SIZE*dabs(dsin(10.d0 * i)) endif end do ! i end do ! j c**** Create global arrays #if BLOCK_CYCLIC if (me.eq.0) then write(6,*) '*' write(6,*) '* Creating Block-Cyclic Arrays' write(6,*) '*' endif dims(1) = n dims(2) = n block(1) = 64 block(2) = 64 call factor(nproc,g1,g2) proc_grid(1) = g1 proc_grid(2) = g2 g_A = ga_create_handle() call ga_set_data(g_A,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_A,block,proc_grid) if (.not.ga_allocate(g_A)) & call ga_error(' ga_create A failed ', 2) g_B = ga_create_handle() call ga_set_data(g_B,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_B,block,proc_grid) if (.not.ga_allocate(g_B)) & call ga_error(' ga_create B failed ', 2) g_A1 = ga_create_handle() call ga_set_data(g_A1,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_A1,block,proc_grid) if (.not.ga_allocate(g_A1)) & call ga_error(' ga_create A1 failed ', 2) g_X = ga_create_handle() call ga_set_data(g_X,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_X,block,proc_grid) if (.not.ga_allocate(g_X)) & call ga_error(' ga_create X failed ', 2) g_X1 = ga_create_handle() call ga_set_data(g_X1,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_X1,block,proc_grid) if (.not.ga_allocate(g_X1)) & call ga_error(' ga_create X1 failed ', 2) g_AA = ga_create_handle() call ga_set_data(g_AA,2,dims,MT_DBL) if (.not.ga_allocate(g_AA)) & call ga_error(' ga_create AA failed ', 2) g_BB = ga_create_handle() call ga_set_data(g_BB,2,dims,MT_DBL) if (.not.ga_allocate(g_BB)) & call ga_error(' ga_create BB failed ', 2) g_CC = ga_create_handle() call ga_set_data(g_CC,2,dims,MT_DBL) if (.not.ga_allocate(g_CC)) & call ga_error(' ga_create CC failed ', 2) dims(2) = 1 g_Y = ga_create_handle() call ga_set_data(g_Y,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_Y,block,proc_grid) if (.not.ga_allocate(g_Y)) & call ga_error(' ga_create Y failed ', 2) g_Y1 = ga_create_handle() call ga_set_data(g_Y1,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_Y1,block,proc_grid) if (.not.ga_allocate(g_Y1)) & call ga_error(' ga_create Y1 failed ', 2) g_Y2 = ga_create_handle() call ga_set_data(g_Y2,2,dims,MT_DBL) call ga_set_block_cyclic_proc_grid(g_Y2,block,proc_grid) if (.not.ga_allocate(g_Y2)) & call ga_error(' ga_create Y1 failed ', 2) g_YY = ga_create_handle() call ga_set_data(g_YY,2,dims,MT_DBL) if (.not.ga_allocate(g_YY)) & call ga_error(' ga_create YY failed ', 2) g_ZZ = ga_create_handle() call ga_set_data(g_ZZ,2,dims,MT_DBL) if (.not.ga_allocate(g_ZZ)) & call ga_error(' ga_create YY failed ', 2) #else if (.not. ga_create(MT_DBL, n, n, 'A', 1, 1, g_A)) & call ga_error(' ga_create A failed ', 2) if (.not. ga_create(MT_DBL, n, n, 'B', 1, 1, g_B)) & call ga_error(' ga_create B failed ', 2) if (.not. ga_create(MT_DBL, n, n, 'A1', 1, 1, g_A1)) & call ga_error(' ga_create A1 failed ', 2) if (.not. ga_create(MT_DBL, n, n, 'X', 1, 1, g_X)) & call ga_error(' ga_create X failed ', 2) if (.not. ga_create(MT_DBL, n, n, 'X1', 1, 1, g_X1)) & call ga_error(' ga_create X1 failed ', 2) if (.not. ga_create(MT_DBL, n, 1, 'Y', 1, 1, g_Y)) & call ga_error(' ga_create Y failed ', 2) if (.not. ga_create(MT_DBL, n, 1, 'Y1', 1, 1, g_Y1)) & call ga_error(' ga_create Y1 failed ', 2) if (.not. ga_create(MT_DBL, n, 1, 'Y2', 1, 1, g_Y2)) & call ga_error(' ga_create Y2 failed ', 2) #endif c**** Fill in arrays A, X do j = me+1, n, nproc call ga_put(g_A, 1, n, j, j, A(1,j), n) call ga_put(g_X, 1, n, j, j, X(1,j), n) end do ! j c**** Create A, B, Y c**** It is granted that A, B, Y will not change c**** during the execution of the test LLT driver c**** B = A * A^ #if BLOCK_CYCLIC call ga_copy(g_A, g_AA) call ga_dgemm('N', 'T', n, n, n, 1.d0, g_AA, g_AA, 0.d0, g_BB) call ga_copy(g_BB, g_B) #else call ga_dgemm('N', 'T', n, n, n, 1.d0, g_A, g_A, 0.d0, g_B) #endif c**** A = B = A * A^ call ga_copy(g_B, g_A) c**** B = A * X #if BLOCK_CYCLIC call ga_copy(g_A, g_AA) call ga_copy(g_X, g_CC) call ga_dgemm('N', 'N', n, n, n, 1.d0, g_AA, g_CC, 0.d0, g_BB) call ga_copy(g_BB,g_B) #else call ga_dgemm('N', 'N', n, n, n, 1.d0, g_A, g_X, 0.d0, g_B) #endif c**** Copy B(1:n,1:1) to Y(1:n) so Y will X column 1 call ga_copy_patch('N', g_B, 1, n, 1, 1, g_Y, 1, n, 1, 1) c**** dnA = ||A|| dnA = ga_dnormF(g_A) c**************************************************** c Test Cholesky factorization external interface c**************************************************** call thead("Test Cholesky factorization") c**** copy A in X call ga_copy(g_A, g_X) c**** call Cholesky factorization routine in ScaLAPACK PDPOTRF c**** to obtain an LL'/U'U factorization (external interface) irc = ga_cholesky(uplo, g_X) c**** if return code 'zero' is OK if (irc.eq.0) then c**** A1 = X * X^ or A1 = X^ * X if (uplo.eq.'L') then #if BLOCK_CYCLIC call ga_copy(g_X, g_CC) call ga_dgemm('N', 'T', n, n, n, 1.d0, g_CC, g_CC, 0.d0, g_AA) call ga_copy(g_AA, g_A1) #else call ga_dgemm('N', 'T', n, n, n, 1.d0, g_X, g_X, 0.d0, g_A1) #endif else #if BLOCK_CYCLIC call ga_copy(g_X, g_CC) call ga_dgemm('T', 'N', n, n, n, 1.d0, g_CC, g_CC, 0.d0, g_AA) call ga_copy(g_AA, g_A1) #else call ga_dgemm('T', 'N', n, n, n, 1.d0, g_X, g_X, 0.d0, g_A1) #endif endif c**** A1 = A - A1 = A - X * X^ or A1 = A - A1 = A - X^ * X call ga_add(1.0d0, g_A1, -1.0d0, g_A, g_A1) c**** dnF = ||A - A1*A1^|| / (||A|| * N * eps) : SHAPE L c**** dnF = ||A - A1^*A1|| / (||A|| * N * eps) : SHAPE U dnF = ga_dnormF(g_A1) / (dnA * n * eps) if (uplo.eq.'L') then call dthtest("||LL' - A|| / (||A|| * N * eps) =", dnF) else call dthtest("||U'U - A|| / (||A|| * N * eps) =", dnF) endif c**** if return code is > 0 else call emsg('It is not positive definite the minor of order:', & irc) endif c**************************************************** c Test Cholesky factorization and solver c internal interfaces with a NxN RHS array c**************************************************** call thead("Test II Cholesky solver with a NxN RHS array") c**** call Cholesky factorization routine in ScaLAPACK PDPOTRF c**** to obtain an LL'/U'U factorization c**** (internal interface: it will not destroy A) #if BLOCK_CYCLIC call ga_copy(g_A, g_AA) #endif hsA = 0 irc = ga_llt_f(uplo, g_A, hsA) c**** if return code from ga_llt_f is zero if (irc.eq.0) then c**** copy B in X call ga_copy(g_B, g_X) c**** call Cholesky solver routine in ScaLAPACK PDPOTRS c**** internal interface with an NxN RHS GA call ga_llt_s(uplo, g_A, g_X, hsA) c**** A1 = A * X #if BLOCK_CYCLIC call ga_copy(g_X, g_BB) call ga_dgemm('N', 'N', n, n, n, 1.d0, & g_AA, g_BB, 0.d0, g_CC) call ga_copy(g_CC,g_A1) #else call ga_dgemm('N', 'N', n, n, n, 1.d0, & g_A, g_X, 0.d0, g_A1) #endif c**** A1 = A1 - B = A * X - B call ga_add(1.d0, g_A1, -1.d0, g_B, g_A1) c**** dnX = ||X|| dnX = ga_dnormF(g_X) c**** dnS = ||AX - B|| / (||A|| * ||X|| * N * eps) dnS = ga_dnormF(G_A1) / (dnA * dnX * n * eps) call dthtest("||A*X - B||/(||A||*||X||*n*eps) =", dnS) c**** if return code is > 0 else call emsg('It is not positive definite the minor of order:', & irc) endif c**************************************************** c Test Cholesky factorization and solver c internal interfaces with a single vector RHS c**************************************************** call thead("Test II Cholesky solver with a single vector RHS") c**** call Cholesky factorization routine in ScaLAPACK PDPOTRF c**** to obtain an LL'/U'U factorization c**** (internal interface: it will not destroy A) #if BLOCK_CYCLIC call ga_copy(g_AA, g_A) #endif hsA = 0 irc = ga_llt_f(uplo, g_A, hsA) c**** if return code from ga_llt_f is zero if (irc.eq.0) then c**** copy Y in Y1 call ga_copy(g_Y, g_Y1) c**** call Cholesky solver routine in ScaLAPACK PDPOTRS c**** internal interface with a single vector RHS call ga_llt_s(uplo, g_A, g_Y1, hsA) c**** dnY1 = ||Y1|| dnY1 = ga_dnormF(g_Y1) c**** Y1 = A * Y1 #if BLOCK_CYCLIC call ga_copy(g_Y1, g_YY) call ga_dgemm('N', 'N', n, 1, n, 1.d0, & g_AA, g_YY, 0.d0, g_ZZ) call ga_copy(g_ZZ,g_Y2) #else call ga_dgemm('N', 'N', n, 1, n, 1.d0, & g_A, g_Y1, 0.d0, g_Y2) #endif c**** Y1 = Y1 - Y = A * Y1 - Y call ga_add(1.d0, g_Y2, -1.d0, g_Y, g_Y1) c**** dnS = ||AY1 - Y|| / (||A|| * ||Y1|| * N * eps) dnS = ga_dnormF(G_A1) / (dnA * dnY1 * n * eps) call dthtest("||A*X - V||/(||A||*||X||*n*eps) =", dnS) c**** if return code is > 0 else call emsg('It is not positive definite the minor of order:', & irc) endif c**************************************************** c Test Cholesky solver with a NxN RHS array c**************************************************** call thead("Test EI Cholesky solver with a NxN RHS array") c**** copy B in X call ga_copy(g_B, g_X) c**** call Cholesky L/U solver with a NxN RHS #if BLOCK_CYCLIC call ga_copy(g_AA,g_A) #endif irc = ga_llt_solve(g_A, g_X) c**** if return code from ga_llt_solve is zero if (irc.eq.0) then c**** A1 = A * X #if BLOCK_CYCLIC call ga_copy(g_X,g_BB) call ga_dgemm('N', 'N', n, n, n, 1.d0, & g_AA, g_BB, 0.d0, g_CC) call ga_copy(g_CC,g_A1) #else call ga_dgemm('N', 'N', n, n, n, 1.d0, & g_A, g_X, 0.d0, g_A1) #endif c**** A1 = A1 - B = A * X - B call ga_add(1.d0, g_A1, -1.d0, g_B, g_A1) c**** dnX = ||X|| dnX = ga_dnormF(g_X) c**** dnS = ||AX - B|| / (||A|| * ||X|| * N * eps) dnS = ga_dnormF(G_A1) / (dnA * dnX * n * eps) call dthtest("||A*X - B||/(||A||*||X||*n*eps) =", dnS) c**** if return code is > 0 else call emsg('It is not positive definite the minor of order:', & irc) endif c**************************************************** c Test Cholesky solver with a single vector RHS c**************************************************** call thead("Test EI Cholesky solver with a single vector RHS") c**** copy Y in Y1 call ga_copy(g_Y, g_Y1) c**** call Cholesky L/U solver with a single vector RHS #if BLOCK_CYCLIC call ga_copy(g_AA,g_A) #endif irc = ga_llt_solve(g_A, g_Y1) c**** if return code from ga_llt_solve is zero if (irc.eq.0) then c**** dnY1 = ||Y1|| dnY1 = ga_dnormF(g_Y1) c**** Y1 = A * Y1 #if BLOCK_CYCLIC call ga_copy(g_Y1,g_YY) call ga_dgemm('N', 'N', n, 1, n, 1.d0, & g_AA, g_YY, 0.d0, g_ZZ) call ga_copy(g_ZZ,g_Y2) #else call ga_dgemm('N', 'N', n, 1, n, 1.d0, & g_A, g_Y1, 0.d0, g_Y2) #endif c**** Y1 = Y1 - Y = A * Y1 - Y call ga_add(1.d0, g_Y2, -1.d0, g_Y, g_Y1) c**** dnS = ||AY1 - Y|| / (||A|| * ||Y1|| * N * eps) dnS = ga_dnormF(G_A1) / (dnA * dnY1 * n * eps) call dthtest("||A*X - V||/(||A||*||X||*n*eps) =", dnS) c**** if return code is > 0 else call emsg('It is not positive definite the minor of order:', & irc) endif c**************************************************** c Test Cholesky factorization and inversion c internal interfaces c**************************************************** call thead("Test II inversion of an SPD matrix") c**** copy A in X #if BLOCK_CYCLIC call ga_copy(g_AA, g_A) #endif call ga_copy(g_A, g_X) c**** call Cholesky factorization routine in ScaLAPACK PDPOTRF c**** to obtain an LL'/U'U factorization c**** (internal interface: it will not destroy X1) hsA = 0 irc = ga_llt_f(uplo, g_X, hsA) c**** if return code from ga_llt_f is zero if (irc.eq.0) then c**** call Cholesky inversion routine in ScaLAPACK PDPOTRI c**** internal interface irc = ga_llt_i(uplo, g_X, hsA) c**** if the inversion could be done if (irc.eq.0) then c**** dnX = ||X|| = ||invA|| dnX = ga_dnormF(g_X) c**** A1 = A * X = A * invA #if BLOCK_CYCLIC call ga_copy(g_X,g_BB) call ga_dgemm('N', 'N', n, n, n, 1.d0, & g_AA, g_BB, 0.d0, g_CC) call ga_copy(g_CC,g_A1) #else call ga_dgemm('N', 'N', n, n, n, 1.d0, & g_A, g_X, 0.d0, g_A1) #endif c**** A1 = A1 - I = A * invA - I do j = me+1, n, nproc call ga_get(g_A1, j, j, j, j, buf, 1) buf(1) = buf(1) - 1.d0 call ga_put(g_A1, j, j, j, j, buf, 1) end do ! j c**** dnI = ||A*invA - I|| / (||A| * ||invA|| * N * eps) dnI = ga_dnormF(g_A1) / (dnA * dnX * n * eps) call dthtest('||A*invA - I||/(||A||*||invA||*n*eps) =', & dnI) else c**** otherwise if the ga_llt_i return code is > 0 call emsg('there is a zero diagonal element:', irc) endif c**** if return code is > 0 else call emsg('It is not positive definite the minor of order:', & irc) endif c**************************************************** c Test Cholesky inversion c**************************************************** call thead("Test inversion of an SPD matrix") c**** copy A in X #if BLOCK_CYCLIC call ga_copy(g_AA, g_A) #endif call ga_copy(g_A, g_X) c**** call Cholesky L/U inversion irc = ga_spd_invert(g_X) c**** if return code from ga_spd_invert is zero if (irc.eq.0) then c**** dnX = ||X|| = ||invA|| dnX = ga_dnormF(g_X) c**** A1 = A * X = A * invA #if BLOCK_CYCLIC call ga_copy(g_X, g_BB) call ga_dgemm('N', 'N', n, n, n, 1.d0, & g_AA, g_BB, 0.d0, g_CC) call ga_copy(g_CC, g_A1) #else call ga_dgemm('N', 'N', n, n, n, 1.d0, & g_A, g_X, 0.d0, g_A1) #endif c**** A1 = A1 - I = A * invA - I do j = me+1, n, nproc call ga_get(g_A1, j, j, j, j, buf, 1) buf(1) = buf(1) - 1.d0 call ga_put(g_A1, j, j, j, j, buf, 1) end do ! j c**** dnI = ||A*invA - I|| / (||A|| * ||invA|| * N * eps) dnI = ga_dnormF(g_A1) / (dnA * dnX * n * eps) call dthtest('||A*invA - I||/(||A||*||invA||*n*eps) =', & dnI) c**** if return code is < 0 elseif (irc.lt.0) then call emsg('there is a zero diagonal element:', irc) c**** if return code is > 0 elseif (irc.gt.0) then call emsg('It is not positive definite the minor of order:', & irc) endif c**************************************************** c* Test general solver with a NxN RSH c* simmetric positive definite array c**************************************************** call thead( & 'Test solver for a symmetric P.D. matrix and NxN RHS' & ) c**** copy B in X call ga_copy(g_B, g_X) c**** call general solver with a NxN RHS p.d. symmetric array #if BLOCK_CYCLIC call ga_copy(g_AA, g_A) #endif irc = ga_solve(g_A, g_X) c**** A1 = A * X #if BLOCK_CYCLIC call ga_copy(g_X, g_BB) call ga_dgemm('N', 'N', n, n, n, 1.d0, & g_AA, g_BB, 0.d0, g_CC) call ga_copy(g_CC, g_A1) #else call ga_dgemm('N', 'N', n, n, n, 1.d0, & g_A, g_X, 0.d0, g_A1) #endif c**** A1 = A1 - B = A * X - B call ga_add(1.d0, g_A1, -1.d0, g_B, g_A1) c**** dnX = ||X|| dnX = ga_dnormF(g_X) c**** dnS = ||AX - B|| / (||A|| * ||X|| * N * eps) dnS = ga_dnormF(G_A1) / (dnA * dnX * n * eps) call dthtest("||A*X - B||/(||A||*||X||*n*eps) =", dnS) call stest(irc, 0) c**************************************************** c* Test general solver with a NxN RSH c* simmetric non positive definite array c**************************************************** call thead( & 'Test solver for a symmetric NON P.D. matrix and NxN RHS') c**** copy B on X1 call ga_copy(g_B, g_X1) c**** and now symmetrize X1 c**** so we obtain a symmetric matrix in X1 call ga_symmetrize(g_X1) c**** copy B in X call ga_copy(g_B, g_X) c**** call general solver with a NxN RHS symmetric array #if BLOCK_CYCLIC call ga_copy(g_X1, g_BB) irc = ga_solve(g_BB, g_X) #else irc = ga_solve(g_X1, g_X) #endif c**** dnX1 = ||X1|| dnX1 = ga_dnormF(g_X1) c**** dnX = ||X|| dnX = ga_dnormF(g_X) c**** A1 = X1 * X #if BLOCK_CYCLIC call ga_copy(g_X1,g_AA) call ga_copy(g_X,g_BB) call ga_dgemm('N', 'N', n, n, n, 1.d0, & g_AA, g_BB, 0.d0, g_CC) call ga_copy(g_CC,g_A1) #else call ga_dgemm('N', 'N', n, n, n, 1.d0, & g_X1, g_X, 0.d0, g_A1) #endif c**** A1 = A1 - B = X1 * X - B call ga_add(1.d0, g_A1, -1.d0, g_B, g_A1) c**** dnS = ||X1*X - B|| / (||X1|| * ||X|| * N * eps) dnS = ga_dnormF(G_A1) / (dnX1 * dnX * n * eps) call dthtest("||A*X - B||/(||A||*||X||*n*eps) =", dnS) call stest(irc, 1) c**************************************************** c CTESTS exit code c**************************************************** c**** just print a newline and return if (me.eq.0) then print *, ' ' endif return end #if BLOCK_CYCLIC subroutine factor(p,idx,idy) implicit none integer i,j,p,idx,idy,it integer ip,ifac,pmax,prime(1280) integer fac(1280) c i = 1 ip = p c c factor p completely c first, find all prime numbers less than or equal to p c pmax = 0 do i = 2, p do j = 1, pmax if (mod(i,prime(j)).eq.0) go to 100 end do pmax = pmax + 1 prime(pmax) = i 100 continue end do c c find all prime factors of p c ifac = 0 do i = 1, pmax 200 if (mod(ip,prime(i)).eq.0) then ifac = ifac + 1 fac(ifac) = prime(i) ip = ip/prime(i) go to 200 endif end do c c determine two factors of p of approximately the c same size c idx = 1 idy = 1 do i = ifac, 1, -1 if (idx.le.idy) then idx = fac(i)*idx else idy = fac(i)*idy endif end do return end #endif ga-5.9.2/global/testing/testutil.fh000066400000000000000000000002251500715745200172340ustar00rootroot00000000000000 double precision drand, util_timer integer util_mitob, util_mdtob external drand, util_timer external util_mitob, util_mdtob ga-5.9.2/global/testing/testutil.h000066400000000000000000000004501500715745200170660ustar00rootroot00000000000000/* extern void get_range( int ndim, int dims[], int lo[], int hi[]); */ /* extern void new_range(int ndim, int dims[], int lo[], int hi[], int new_lo[], int new_hi[]); */ extern void print_subscript(char *pre,int ndim, int subscript[], char* post); /* extern void print_distribution(int g_a); */ ga-5.9.2/global/testing/thread_perf_contig.c000066400000000000000000000305131500715745200210350ustar00rootroot00000000000000#include #include #include #include #include "mpi.h" #include "ga.h" #if defined(_OPENMP) #include "omp.h" #endif #define MEDIUM_MESSAGE_SIZE 8192 #define ITER_SMALL 100 #define ITER_LARGE 10 #define WARMUP 2 #define DEFAULT_DIM 256*256 #define MAX_MESSAGE_SIZE DEFAULT_DIM*DEFAULT_DIM #define MAX_FACTOR 256 /* Convenience function to check that something is true on all processors */ int trueEverywhere(int flag) { int tflag, nprocs; if (flag) tflag = 1; else tflag = 0; nprocs = GA_Nnodes(); GA_Igop(&tflag,1,"+"); if (nprocs == tflag) return 1; return 0; } /* Function to print out timing statistics */ void printTimes(double *time, int *ntime, int *nelems, int size, int nthread) { int me = GA_Nodeid(); int nproc = GA_Nnodes(); double l_time; int l_ntime; int l_nelems; int one = 1; double bandwdth; double optime; int i; l_time = 0.0; l_ntime = 0; l_nelems = 0; for (i=0; i 8) thread_count = 8; if (me==0) { printf("\n[%d]Testing %d threads.\n", me, thread_count); } time = (double*)malloc(thread_count*sizeof(double)); ritime = (double*)malloc(thread_count*sizeof(double)); ntime = (int*)malloc(thread_count*sizeof(int)); nelems = (int*)malloc(thread_count*sizeof(int)); rinc = (int*)malloc(thread_count*sizeof(int)); arr_ok = (int*)malloc(thread_count*sizeof(int)); for (i=0; i= dim) thi = dim-1; lld = thi-tlo+1; bsize = thi-tlo+1; /* Fill a portion of local buffer with correct values */ for (m=tlo; m<=thi; m++) { offset = m-tlo; buf[offset] = m; } delta_t = GA_Wtime(); NGA_Put(g_array, &tlo, &thi, buf, &lld); delta_t = GA_Wtime()-delta_t; time[id] += delta_t; ntime[id] += one; nelems[id] += bsize; delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; ritime[id] += delta_t; rinc[id] += one; } free(buf); } printTimes(time,ntime,nelems,msg_size,ithread); /* Sync all processors at end of initialization loop */ NGA_Sync(); /* Each process determines if it is holding the correct data */ NGA_Distribution(g_array,me,&glo,&ghi); NGA_Access(g_array,&glo,&ghi,&ptr,&gld); icnt = 0; for (i=glo; i<=ghi; i++) { if (ptr[icnt] != i) { ok = 0; printf("p[%d] (Put) mismatch at point [%d] actual: %d expected: %d\n", me,i,ptr[icnt],i); } icnt++; } NGA_Release(g_array,&glo,&ghi); } } ok = trueEverywhere(ok); if (me==0 && ok) { printf("\nPut test OK\n"); } else if (me == 0 && !ok) { printf("\nPut test failed\n"); } if (me==0) { printf("\nPerformance of GA_Get\n"); printf("\nmsg size (bytes) avg time (us) avg b/w (MB/sec) N threads\n"); } ok = 1; for (ithread = 1; ithread<= thread_count; ithread++) { for (msg_size = 1; msg_size <= buf_size; msg_size *= 2) { for (i=0; i= dim) thi = dim-1; lld = thi-tlo+1; bsize = thi-tlo+1; delta_t = GA_Wtime(); NGA_Get(g_array, &tlo, &thi, buf, &lld); delta_t = GA_Wtime()-delta_t; time[id] += delta_t; ntime[id] += one; nelems[id] += bsize; /* check that values in buffer are correct */ for (m=tlo; m<=thi; m++) { offset = m-tlo; if (buf[offset] != m) { arr_ok[id] = 0; printf("p[%d] Read mismatch for [%d] expected: %d actual: %d\n", me,m,m,buf[offset]); } } delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; ritime[id] += delta_t; rinc[id] += one; } free(buf); } for (i=0; i= tn) k = k - tn; tlo = k*msg_size; thi = tlo+msg_size-1; if (thi >= dim) thi = dim-1; lld = thi-tlo+1; bsize = thi-tlo+1; /* Fill a portion of local buffer with correct values */ for (m=tlo; m<=thi; m++) { offset = m-tlo; buf[offset] = m; } delta_t = GA_Wtime(); NGA_Acc(g_array, &tlo, &thi, buf, &lld, &one); delta_t = GA_Wtime()-delta_t; time[id] += delta_t; ntime[id] += one; nelems[id] += bsize; delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; ritime[id] += delta_t; rinc[id] += one; } free(buf); } printTimes(time,ntime,nelems,msg_size,ithread); /* Sync all processors at end of initialization loop */ NGA_Sync(); /* Each process determines if it is holding the correct data */ NGA_Distribution(g_array,me,&glo,&ghi); NGA_Access(g_array,&glo,&ghi,&ptr,&gld); icnt = 0; for (i=glo; i<=ghi; i++) { if (ptr[icnt] != 2*i) { ok = 0; printf("p[%d] (Acc) mismatch at point [%d] actual: %d expected: %d\n", me,i,ptr[icnt],2*i); } icnt++; } NGA_Release(g_array,&glo,&ghi); } } ok = trueEverywhere(ok); if (me==0 && ok) { printf("\nAcc test OK\n"); } else if (me == 0 && !ok) { printf("\nAcc test failed\n"); } printRITimes(ritime, rinc, thread_count); free(time); free(ritime); free(ntime); free(nelems); free(rinc); free(arr_ok); GA_Terminate(); MPI_Finalize(); return return_code; #else printf("OPENMP Disabled\n"); return 0; #endif } ga-5.9.2/global/testing/thread_perf_strided.c000066400000000000000000000423611500715745200212140ustar00rootroot00000000000000#include #include #include #include #include "mpi.h" #include "ga.h" #if defined(_OPENMP) #include "omp.h" #endif #define MEDIUM_MESSAGE_SIZE 8192 #define ITER_SMALL 100 #define ITER_LARGE 10 #define WARMUP 2 #define DEFAULT_DIM 256 #define MAX_MESSAGE_SIZE DEFAULT_DIM*DEFAULT_DIM #define MAX_FACTOR 256 /** * Factor p processors into 2D processor grid of dimensions px, py */ void grid_factor(int p, int *idx, int *idy) { int i, j; int ip, ifac, pmax, prime[MAX_FACTOR]; int fac[MAX_FACTOR]; int ix, iy, ichk; i = 1; /** * factor p completely * first, find all prime numbers, besides 1, less than or equal to * the square root of p */ ip = (int)(sqrt((double)p))+1; pmax = 0; for (i=2; i<=ip; i++) { ichk = 1; for (j=0; j MAX_FACTOR) printf("Overflow in grid_factor\n"); prime[pmax-1] = i; } } /** * find all prime factors of p */ ip = p; ifac = 0; for (i=0; i= 0; i--) { ix = *idx; iy = *idy; if (ix <= iy) { *idx = fac[i]*(*idx); } else { *idy = fac[i]*(*idy); } } } /* Convenience function to check that something is true on all processors */ int trueEverywhere(int flag) { int tflag, nprocs; if (flag) tflag = 1; else tflag = 0; nprocs = GA_Nnodes(); GA_Igop(&tflag,1,"+"); if (nprocs == tflag) return 1; return 0; } /* Function to print out timing statistics */ void printTimes(double *time, int *ntime, int *nelems, int size, int nthread, int x, int y) { int me = GA_Nodeid(); int nproc = GA_Nnodes(); double l_time; int l_ntime; int l_nelems; int one = 1; double bandwdth; double optime; int i; l_time = 0.0; l_ntime = 0; l_nelems = 0; for (i=0; i 8) thread_count = 8; if (me==0) { printf("\n[%d]Testing %d threads.\n", me, thread_count); } time = (double*)malloc(thread_count*sizeof(double)); ritime = (double*)malloc(thread_count*sizeof(double)); ntime = (int*)malloc(thread_count*sizeof(int)); nelems = (int*)malloc(thread_count*sizeof(int)); rinc = (int*)malloc(thread_count*sizeof(int)); arr_ok = (int*)malloc(thread_count*sizeof(int)); for (i=0; i msg_size) ulim = msg_size; for (block_x=1; block_x <= ulim; block_x *= 2) { for (i=0; i DEFAULT_DIM) continue; tx = x/block_x; if (tx*block_x < x) tx++; ty = y/block_y; if (ty*block_y < y) ty++; /* Fill global array with data by having each thread write * blocks to it */ GA_Zero(g_count); #pragma omp parallel num_threads(ithread) { /* declare variables local to each thread */ int lo[2], hi[2], tlo[2], thi[2]; int ld[2]; int k, m, n; int xinc, yinc; int itx, ity; int offset; int *buf; int lld; long task, inc; int id; double delta_t; int bsize; id = omp_get_thread_num(); inc = 1; delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; ritime[id] += delta_t; rinc[id] += one; buf = (int*)malloc(block_x*block_y*sizeof(int)); while (task < tx*ty) { ity = task%ty; itx = (task-ity)/ty; tlo[0] = itx*block_x; tlo[1] = ity*block_y; /* printf("j: %d k: %d tlo[0]: %d tlo[1]: %d xinc: %d yinc: %d\n", j,k,tlo[0],tlo[1],xinc,yinc); */ thi[0] = tlo[0] + block_x - 1; if (thi[0] >= dims[0]) thi[0] = dims[0]-1; thi[1] = tlo[1] + block_y - 1; if (thi[1] >= dims[1]) thi[1] = dims[1]-1; lld = thi[1]-tlo[1]+1; bsize = (thi[0]-tlo[0]+1)*(thi[1]-tlo[1]+1); /* Fill a portion of local buffer with correct values */ for (m=tlo[0]; m<=thi[0]; m++) { for (n=tlo[1]; n<=thi[1]; n++) { offset = (m-tlo[0])*lld + (n-tlo[1]); buf[offset] = m*dims[1]+n; } } delta_t = GA_Wtime(); NGA_Put(g_array, tlo, thi, buf, &lld); delta_t = GA_Wtime()-delta_t; time[id] += delta_t; ntime[id] += one; nelems[id] += bsize; delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; ritime[id] += delta_t; rinc[id] += one; } free(buf); } printTimes(time,ntime,nelems,block_x*block_y,ithread,block_x,block_y); /* Sync all processors at end of initialization loop */ NGA_Sync(); /* Each process determines if it is holding the correct data */ NGA_Distribution(g_array,me,glo,ghi); NGA_Access(g_array,glo,ghi,&ptr,gld); icnt = 0; for (i=glo[0]; i<=ghi[0]; i++) { for (j=glo[1]; j<=ghi[1]; j++) { if (ptr[icnt] != i*dims[1]+j) { ok = 0; printf("p[%d] (Put) mismatch at point [%d,%d] actual: %d expected: %d\n", me,i,j,ptr[icnt],i*dims[1]+j); } icnt++; } } NGA_Release(g_array,glo,ghi); } } } ok = trueEverywhere(ok); if (me==0 && ok) { printf("\nPut test OK\n"); } else if (me == 0 && !ok) { printf("\nPut test failed\n"); } if (me==0) { printf("\nPerformance of GA_Get\n"); printf("\nmsg size (bytes) avg time (us) avg b/w (MB/sec) N threads Xdim Ydim\n"); } ok = 1; for (ithread = 1; ithread<= thread_count; ithread++) { for (msg_size = 1; msg_size <= buf_size; msg_size *= 2) { ulim = DEFAULT_DIM; if (ulim > msg_size) ulim = msg_size; for (block_x=1; block_x <= ulim; block_x *= 2) { for (i=0; i DEFAULT_DIM) continue; tx = x/block_x; if (tx*block_x < x) tx++; ty = y/block_y; if (ty*block_y < y) ty++; /* Fill global array with data by having each thread write * blocks to it */ GA_Zero(g_count); #pragma omp parallel num_threads(ithread) { /* declare variables local to each thread */ int lo[2], hi[2], tlo[2], thi[2]; int ld[2]; int k, m, n; int xinc, yinc; int itx, ity; int offset; int *buf; int lld; long task, inc; int id; double delta_t; int bsize; id = omp_get_thread_num(); inc = 1; arr_ok[id] = 1; delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; ritime[id] += delta_t; rinc[id] += one; buf = (int*)malloc(block_x*block_y*sizeof(int)); while (task < tx*ty) { ity = task%ty; itx = (task-ity)/ty; tlo[0] = itx*block_x; tlo[1] = ity*block_y; thi[0] = tlo[0] + block_x - 1; if (thi[0] >= dims[0]) thi[0] = dims[0]-1; thi[1] = tlo[1] + block_y - 1; if (thi[1] >= dims[1]) thi[1] = dims[1]-1; lld = thi[1]-tlo[1]+1; bsize = (thi[0]-tlo[0]+1)*(thi[1]-tlo[1]+1); delta_t = GA_Wtime(); NGA_Get(g_array, tlo, thi, buf, &lld); delta_t = GA_Wtime()-delta_t; time[id] += delta_t; ntime[id] += one; nelems[id] += bsize; /* check that values in buffer are correct */ for (m=tlo[0]; m<=thi[0]; m++) { for (n=tlo[1]; n<=thi[1]; n++) { offset = (m-tlo[0])*lld + (n-tlo[1]); if (buf[offset] != m*dims[1]+n) { arr_ok[id] = 0; /* printf("Read mismatch for [%d,%d] expected: %d actual: %d\n", m,n,m*dims[1]+n,lld); */ } } } delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; ritime[id] += delta_t; rinc[id] += one; } free(buf); } for (i=0; i msg_size) ulim = msg_size; for (block_x=1; block_x <= ulim; block_x *= 2) { for (i=0; i DEFAULT_DIM) continue; tx = x/block_x; if (tx*block_x < x) tx++; ty = y/block_y; if (ty*block_y < y) ty++; /* Fill global array with data by having each thread write * blocks to it */ GA_Zero(g_count); GA_Zero(g_array); #pragma omp parallel num_threads(ithread) { /* declare variables local to each thread */ int lo[2], hi[2], tlo[2], thi[2]; int ld[2]; int k, m, n; int xinc, yinc; int itx, ity; int offset; int *buf; int lld; long task, inc; int id; double delta_t; int bsize; id = omp_get_thread_num(); inc = 1; delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; ritime[id] += delta_t; rinc[id] += one; buf = (int*)malloc(block_x*block_y*sizeof(int)); while (task < 2*tx*ty) { k = task; if (task >= tx*ty) k = k - tx*ty; ity = k%ty; itx = (k-ity)/ty; tlo[0] = itx*block_x; tlo[1] = ity*block_y; /* printf("j: %d k: %d tlo[0]: %d tlo[1]: %d xinc: %d yinc: %d\n", j,k,tlo[0],tlo[1],xinc,yinc); */ thi[0] = tlo[0] + block_x - 1; if (thi[0] >= dims[0]) thi[0] = dims[0]-1; thi[1] = tlo[1] + block_y - 1; if (thi[1] >= dims[1]) thi[1] = dims[1]-1; lld = thi[1]-tlo[1]+1; bsize = (thi[0]-tlo[0]+1)*(thi[1]-tlo[1]+1); /* Fill a portion of local buffer with correct values */ for (m=tlo[0]; m<=thi[0]; m++) { for (n=tlo[1]; n<=thi[1]; n++) { offset = (m-tlo[0])*lld + (n-tlo[1]); buf[offset] = m*dims[1]+n; } } delta_t = GA_Wtime(); NGA_Acc(g_array, tlo, thi, buf, &lld, &one); delta_t = GA_Wtime()-delta_t; time[id] += delta_t; ntime[id] += one; nelems[id] += bsize; delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; ritime[id] += delta_t; rinc[id] += one; } free(buf); } printTimes(time,ntime,nelems,block_x*block_y,ithread,block_x,block_y); /* Sync all processors at end of initialization loop */ NGA_Sync(); /* Each process determines if it is holding the correct data */ NGA_Distribution(g_array,me,glo,ghi); NGA_Access(g_array,glo,ghi,&ptr,gld); icnt = 0; for (i=glo[0]; i<=ghi[0]; i++) { for (j=glo[1]; j<=ghi[1]; j++) { if (ptr[icnt] != 2*(i*dims[1]+j)) { ok = 0; printf("p[%d] (Acc) mismatch at point [%d,%d] actual: %d expected: %d\n", me,i,j,ptr[icnt],2*(i*dims[1]+j)); } icnt++; } } NGA_Release(g_array,glo,ghi); } } } ok = trueEverywhere(ok); if (me==0 && ok) { printf("\nAcc test OK\n"); } else if (me == 0 && !ok) { printf("\nAcc test failed\n"); } printRITimes(ritime, rinc, thread_count); free(time); free(ritime); free(ntime); free(nelems); free(rinc); free(arr_ok); GA_Terminate(); MPI_Finalize(); return return_code; #else printf("OPENMP Disabled\n"); return 0; #endif } ga-5.9.2/global/testing/threadsafec.c000066400000000000000000000746671500715745200175020ustar00rootroot00000000000000#include #include #include #include "mpi.h" #include "ga.h" #if defined(_OPENMP) #include "omp.h" #endif #define DEFAULT_DIM 500 #define BLOCK_DIM 27 #define MAX_FACTOR 256 /** * Factor p processors into 2D processor grid of dimensions px, py */ void grid_factor(int p, int *idx, int *idy) { int i, j; int ip, ifac, pmax, prime[MAX_FACTOR]; int fac[MAX_FACTOR]; int ix, iy, ichk; i = 1; /** * factor p completely * first, find all prime numbers, besides 1, less than or equal to * the square root of p */ ip = (int)(sqrt((double)p))+1; pmax = 0; for (i=2; i<=ip; i++) { ichk = 1; for (j=0; j MAX_FACTOR) printf("Overflow in grid_factor\n"); prime[pmax-1] = i; } } /** * find all prime factors of p */ ip = p; ifac = 0; for (i=0; i= 0; i--) { ix = *idx; iy = *idy; if (ix <= iy) { *idx = fac[i]*(*idx); } else { *idy = fac[i]*(*idy); } } } /* Convenience function to check that something is true on all processors */ int trueEverywhere(int flag) { int tflag, nprocs; if (flag) tflag = 1; else tflag = 0; nprocs = GA_Nnodes(); GA_Igop(&tflag,1,"+"); if (nprocs == tflag) return 1; return 0; } /* Function to print out timing statistics */ void printTimes(double g_time, int g_ntime, int g_elems, const char *header) { int me = GA_Nodeid(); int nproc = GA_Nnodes(); double time; int ntime; int nelems; int one = 1; double bandwdth; double optime; NGA_Get(g_time,&me,&me,&time,&one); NGA_Get(g_ntime,&me,&me,&ntime,&one); NGA_Get(g_elems,&me,&me,&nelems,&one); GA_Dgop(&time,one,"+"); GA_Igop(&ntime,one,"+"); GA_Igop(&nelems,one,"+"); nelems *= sizeof(int); bandwdth = ((double)nelems)/time; bandwdth /= 1.0e6; optime = ((double)ntime)/time; optime /= 1.0e6; if (me==0) { printf("\n%s\n",header); printf("\nBandwidth: %16.6e (MBytes/s) Operation time %16.6e (MOps/s)\n", bandwdth,optime); } } int main(int argc, char * argv[]) { #if defined(_OPENMP) int x = DEFAULT_DIM; int y = DEFAULT_DIM; int block_x = BLOCK_DIM; int block_y = BLOCK_DIM; int g_src, g_dest, g_count; int me, nproc; int px, py, ipx, ipy; int glo[2], ghi[2], gld[2]; int tx, ty; int i,j,icnt; int return_code = 0; int dims[2]; int ndimx = 2; int thread_count = 4; int ok; int zero = 0, one = 1; double rone = 1.0; int *ptr; int next, nextx, nexty; char *env_threads; int provided; int g_time, g_ntime, g_elems; int g_ritime, g_rinc; /* Use a different array size if specified in arguments */ if(argc >= 3) { x = atoi(argv[1]); y = atoi(argv[2]); } if(argc >= 5) { block_x = atoi(argv[3]); block_y = atoi(argv[4]); } MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); GA_Initialize(); nproc = GA_Nnodes(); me = GA_Nodeid(); if (provided < MPI_THREAD_MULTIPLE && me==0) { printf("\nMPI_THREAD_MULTIPLE not provided\n"); } /* Find processor grid dimensions and processor grid coordinates */ grid_factor(nproc, &px, &py); ipy = me%py; ipx = (me-ipy)/py; if (me==0) { printf("\nTest running of %d processors\n",nproc); printf("\n Array dimension is %d X %d\n",x,y); printf("\n Block dimension is %d X %d\n",block_x,block_y); printf("\n Processor grid is %d X %d\n\n",px,py); } dims[0] = x; dims[1] = y; /* Create GA and set all elements to zero */ g_src = NGA_Create(C_INT, 2, dims, "source", NULL); g_dest = NGA_Create(C_INT, 2, dims, "destination", NULL); g_count = NGA_Create(C_INT, 1, &one, "counter", NULL); g_time = NGA_Create(C_DBL, 1, &nproc, "times", &one); g_ntime = NGA_Create(C_INT, 1, &nproc, "ntimes", &one); g_elems = NGA_Create(C_INT, 1, &nproc, "nelems", &one); g_ritime = NGA_Create(C_DBL, 1, &nproc, "read-increment-times", &one); g_rinc = NGA_Create(C_INT, 1, &nproc, "number-read-increment", &one); GA_Zero(g_src); GA_Zero(g_dest); GA_Zero(g_count); GA_Zero(g_ritime); GA_Zero(g_rinc); tx = x/block_x; if (tx*block_x < x) tx++; ty = y/block_y; if (ty*block_y < y) ty++; if(env_threads = getenv("OMP_NUM_THREADS")) thread_count = atoi(env_threads); else omp_set_num_threads(thread_count); if (thread_count > 8) thread_count = 8; if (me==0) { printf("\n[%d]Testing %d threads.\n", me, thread_count); printf("\n[%d]Testing write1 from 0.\n", me); } /* Fill global array with data by having each thread write * blocks to it */ GA_Zero(g_time); GA_Zero(g_ntime); GA_Zero(g_elems); #pragma omp parallel num_threads(thread_count) { /* declare variables local to each thread */ int lo[2], hi[2], tlo[2], thi[2]; int ld[2]; int k, m, n; int xinc, yinc; int itx, ity; int offset; int *buf; int lld; long task, inc; int id; double delta_t; int bsize; id = omp_get_thread_num(); inc = 1; delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); buf = (int*)malloc(block_x*block_y*sizeof(int)); while (task < tx*ty) { ity = task%ty; itx = (task-ity)/ty; tlo[0] = itx*block_x; tlo[1] = ity*block_y; /* printf("j: %d k: %d tlo[0]: %d tlo[1]: %d xinc: %d yinc: %d\n", j,k,tlo[0],tlo[1],xinc,yinc); */ thi[0] = tlo[0] + block_x - 1; if (thi[0] >= dims[0]) thi[0] = dims[0]-1; thi[1] = tlo[1] + block_y - 1; if (thi[1] >= dims[1]) thi[1] = dims[1]-1; lld = thi[1]-tlo[1]+1; bsize = (thi[0]-tlo[0]+1)*(thi[1]-tlo[1]+1); /* Fill a portion of local buffer with correct values */ for (m=tlo[0]; m<=thi[0]; m++) { for (n=tlo[1]; n<=thi[1]; n++) { offset = (m-tlo[0])*lld + (n-tlo[1]); buf[offset] = m*dims[1]+n; } } delta_t = GA_Wtime(); NGA_Put(g_src, tlo, thi, buf, &lld); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_time,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_ntime,&me,&me,&one,&one,&one); NGA_Acc(g_elems,&me,&me,&bsize,&one,&one); delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); } free(buf); } /* Sync all processors at end of initialization loop */ NGA_Sync(); /* Each process determines if it is holding the correct data */ NGA_Distribution(g_src,me,glo,ghi); NGA_Access(g_src,glo,ghi,&ptr,gld); ok = 1; icnt = 0; for (i=glo[0]; i<=ghi[0]; i++) { for (j=glo[1]; j<=ghi[1]; j++) { if (ptr[icnt] != i*dims[1]+j) { ok = 0; printf("p[%d] (write1) mismatch at point [%d,%d] actual: %d expected: %d\n", me,i,j,ptr[icnt],i*dims[1]+j); } icnt++; } } NGA_Release(g_src,glo,ghi); ok = trueEverywhere(ok); if (me==0 && ok) { printf("\nwrite1 test OK\n"); } else if (me == 0 && !ok) { printf("\nwrite1 test failed\n"); } printTimes(g_time, g_ntime, g_elems, "Put performance"); /* Move data from remote processor to local buffer */ if (me==0) { printf("\n[%d]Testing read1 from 0.\n", me); } /* Threads grab data from global array and copy them into a local * buffer and verify that data is correct. */ GA_Zero(g_count); ok = 1; GA_Zero(g_time); GA_Zero(g_ntime); GA_Zero(g_elems); #pragma omp parallel num_threads(thread_count) { /* declare variables local to each thread */ int lo[2], hi[2], tlo[2], thi[2]; int ld[2]; int k, m, n; int itx, ity; int offset; int *buf; int lld; int id; long task, inc; double delta_t; int bsize; inc = 1; id = omp_get_thread_num(); buf = (int*)malloc(block_x*block_y*sizeof(int)); delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); while (task < tx*ty) { ity = task%ty; itx = (task-ity)/ty; tlo[0] = itx*block_x; tlo[1] = ity*block_y; thi[0] = tlo[0] + block_x - 1; if (thi[0] >= dims[0]) thi[0] = dims[0]-1; thi[1] = tlo[1] + block_y - 1; if (thi[1] >= dims[1]) thi[1] = dims[1]-1; lld = thi[1]-tlo[1]+1; bsize = (thi[0]-tlo[0]+1)*(thi[1]-tlo[1]+1); delta_t = GA_Wtime(); NGA_Get(g_src, tlo, thi, buf, &lld); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_time,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_ntime,&me,&me,&one,&one,&one); NGA_Acc(g_elems,&me,&me,&bsize,&one,&one); /* check that values in buffer are correct */ for (m=tlo[0]; m<=thi[0]; m++) { for (n=tlo[1]; n<=thi[1]; n++) { offset = (m-tlo[0])*lld + (n-tlo[1]); if (buf[offset] != m*dims[1]+n) { ok = 0; printf("Read mismatch for [%d,%d] expected: %d actual: %d\n", m,n,m*dims[1]+n,lld); } } } delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); } free(buf); } /* Sync all processors at end of initialization loop */ NGA_Sync(); ok = trueEverywhere(ok); if (me==0 && ok) { printf("\nread1 test OK\n"); } else if (me==0 && !ok) { printf("\nread1 test failed\n"); } printTimes(g_time, g_ntime, g_elems, "Get performance"); GA_Zero(g_count); /* Accumulate data to global array */ if (me==0) { printf("\n[%d]Testing acc1 from 0.\n", me); } GA_Zero(g_time); GA_Zero(g_ntime); GA_Zero(g_elems); #pragma omp parallel num_threads(thread_count) { /* declare variables local to each thread */ int lo[2], hi[2], tlo[2], thi[2]; int ld[2]; int k, m, n; int xinc, yinc; int itx, ity; int offset; int *buf; int lld; long task, inc; int id; double delta_t; int bsize; id = omp_get_thread_num(); inc = 1; delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); buf = (int*)malloc(block_x*block_y*sizeof(int)); while (task < tx*ty) { ity = task%ty; itx = (task-ity)/ty; tlo[0] = itx*block_x; tlo[1] = ity*block_y; thi[0] = tlo[0] + block_x - 1; if (thi[0] >= dims[0]) thi[0] = dims[0]-1; thi[1] = tlo[1] + block_y - 1; if (thi[1] >= dims[1]) thi[1] = dims[1]-1; lld = thi[1]-tlo[1]+1; bsize = (thi[0]-tlo[0]+1)*(thi[1]-tlo[1]+1); /* Accumulate values to a portion of global array */ for (m=tlo[0]; m<=thi[0]; m++) { for (n=tlo[1]; n<=thi[1]; n++) { offset = (m-tlo[0])*lld + (n-tlo[1]); buf[offset] = m*dims[1]+n; } } delta_t = GA_Wtime(); NGA_Acc(g_src, tlo, thi, buf, &lld, &one); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_time,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_ntime,&me,&me,&one,&one,&one); NGA_Acc(g_elems,&me,&me,&bsize,&one,&one); delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); } free(buf); } /* Sync all processors at end of initialization loop */ NGA_Sync(); /* Check local buffer for correct values */ NGA_Distribution(g_src,me,glo,ghi); ok = 1; NGA_Access(g_src,glo,ghi,&ptr,gld); icnt = 0; for (i=glo[0]; i<=ghi[0]; i++) { for (j=glo[1]; j<=ghi[1]; j++) { if (ptr[icnt] != 2*(i*dims[1]+j)) { ok = 0; printf("p[%d] (Acc1) mismatch at point [%d,%d] actual: %d expected: %d\n", me,i,j,ptr[icnt],i*dims[1]+j); } icnt++; } } NGA_Release(g_src,glo,ghi); ok = trueEverywhere(ok); if (me==0 && ok) { printf("\nacc1 test OK\n"); } else if (me==0 && !ok) { printf("\nacc1 test failed\n"); } printTimes(g_time, g_ntime, g_elems, "Accumulate performance"); /* Sync all processors*/ NGA_Sync(); /* Testing random work pattern */ if (me==0) { printf("\n[%d]Testing ran1 from 0.\n", me); } /* Reinitialize source array */ NGA_Distribution(g_src,me,glo,ghi); NGA_Access(g_src,glo,ghi,&ptr,gld); icnt = 0; for (i=glo[0]; i<=ghi[0]; i++) { for (j=glo[1]; j<=ghi[1]; j++) { ptr[icnt] = i*dims[1]+j; icnt++; } } NGA_Release(g_src,glo,ghi); /* Mimic random work. */ GA_Zero(g_dest); GA_Zero(g_count); #pragma omp parallel num_threads(thread_count) { /* declare variables local to each thread */ int tlo[2], thi[2]; int ld[2]; int k, m, n; int xinc, yinc; int itx, ity; int offset, icnt; int *buf, *buft; int lld; long task, inc; int id; int tmp; double delta_t; id = omp_get_thread_num(); inc = 1; delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); buf = (int*)malloc(block_x*block_y*sizeof(int)); buft = (int*)malloc(block_x*block_y*sizeof(int)); /* Read and transpose data */ while (task < 2*tx*ty) { k = task; if (k>=tx*ty) k -= tx*ty; ity = k%ty; itx = (k-ity)/ty; tlo[0] = itx*block_x; tlo[1] = ity*block_y; thi[0] = tlo[0] + block_x - 1; if (thi[0] >= dims[0]) thi[0] = dims[0]-1; thi[1] = tlo[1] + block_y - 1; if (thi[1] >= dims[1]) thi[1] = dims[1]-1; ld[0] = thi[0]-tlo[0]+1; ld[1] = thi[1]-tlo[1]+1; lld = thi[1]-tlo[1]+1; /* Get data from g_src */ NGA_Get(g_src, tlo, thi, buf, &lld); /* Evaluate transpose of local bloack */ icnt = 0; for (m=0; m= dims[0]) thi[0] = dims[0]-1; thi[1] = tlo[1] + block_x - 1; if (thi[1] >= dims[1]) thi[1] = dims[1]-1; lld = thi[1]-tlo[1]+1; NGA_Acc(g_dest, tlo, thi, buft, &lld, &one); delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); } free(buf); free(buft); } GA_Sync(); /* Check local buffer for correct values */ NGA_Distribution(g_dest,me,glo,ghi); ok = 1; NGA_Access(g_dest,glo,ghi,&ptr,gld); icnt = 0; for (i=glo[0]; i<=ghi[0]; i++) { for (j=glo[1]; j<=ghi[1]; j++) { if (ptr[icnt] != 2*(j*dims[0]+i)) { ok = 0; printf("p[%d] (Ran1) mismatch at point [%d,%d] actual: %d expected: %d\n", me,i,j,ptr[icnt],2*(j*dims[0]+i)); } icnt++; } } NGA_Release(g_dest,glo,ghi); ok = trueEverywhere(ok); if (me==0 && ok) { printf("\nran1 test OK\n"); } else if (me==0 && !ok) { printf("\nran1 test failed\n"); } GA_Zero(g_src); GA_Zero(g_dest); /* Reinitialize source array */ NGA_Distribution(g_src,me,glo,ghi); NGA_Access(g_src,glo,ghi,&ptr,gld); icnt = 0; for (i=glo[0]; i<=ghi[0]; i++) { for (j=glo[1]; j<=ghi[1]; j++) { ptr[icnt] = i*dims[1]+j; icnt++; } } NGA_Release(g_src,glo,ghi); if (me==0) { printf("\n[%d]Testing non-blocking-write1 from 0.\n", me); } /* Test nonblocking operations */ /* Create array visible to all threads that can hold non-blocking * handles */ GA_Zero(g_count); ok = 1; GA_Zero(g_src); /* Fill global array with data by having each thread write * blocks to it */ GA_Zero(g_time); GA_Zero(g_ntime); GA_Zero(g_elems); #pragma omp parallel num_threads(thread_count) { /* declare variables local to each thread */ int lo[2], hi[2], tlo[2], thi[2]; int ld[2]; int k, m, n; int xinc, yinc; int itx, ity; int offset; int *buf; int lld; long task, inc; int id; double delta_t; int bsize; int icnt = 0; ga_nbhdl_t* nb_hdl = (ga_nbhdl_t*) malloc(tx*ty*sizeof(ga_nbhdl_t)); id = omp_get_thread_num(); inc = 1; delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); buf = (int*)malloc(block_x*block_y*sizeof(int)); while (task < tx*ty) { ity = task%ty; itx = (task-ity)/ty; tlo[0] = itx*block_x; tlo[1] = ity*block_y; /* printf("j: %d k: %d tlo[0]: %d tlo[1]: %d xinc: %d yinc: %d\n", j,k,tlo[0],tlo[1],xinc,yinc); */ thi[0] = tlo[0] + block_x - 1; if (thi[0] >= dims[0]) thi[0] = dims[0]-1; thi[1] = tlo[1] + block_y - 1; if (thi[1] >= dims[1]) thi[1] = dims[1]-1; lld = thi[1]-tlo[1]+1; bsize = (thi[0]-tlo[0]+1)*(thi[1]-tlo[1]+1); /* Fill a portion of local buffer with correct values */ for (m=tlo[0]; m<=thi[0]; m++) { for (n=tlo[1]; n<=thi[1]; n++) { offset = (m-tlo[0])*lld + (n-tlo[1]); buf[offset] = m*dims[1]+n; } } delta_t = GA_Wtime(); NGA_NbPut(g_src, tlo, thi, buf, &lld, &nb_hdl[icnt]); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_time,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_ntime,&me,&me,&one,&one,&one); NGA_Acc(g_elems,&me,&me,&bsize,&one,&one); icnt++; /* NGA_NbWait(putid); */ delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); } free(buf); free(nb_hdl); /* Call wait on all outstanding tasks. Don't bother to reinitialize global counter */ for (k=0; k= dims[0]) thi[0] = dims[0]-1; thi[1] = tlo[1] + block_y - 1; if (thi[1] >= dims[1]) thi[1] = dims[1]-1; lld = thi[1]-tlo[1]+1; bsize = (thi[0]-tlo[0]+1)*(thi[1]-tlo[1]+1); delta_t = GA_Wtime(); NGA_NbGet(g_src, tlo, thi, buf, &lld,getid); NGA_NbWait(getid); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_time,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_ntime,&me,&me,&one,&one,&one); NGA_Acc(g_elems,&me,&me,&bsize,&one,&one); /* check that values in buffer are correct */ for (m=tlo[0]; m<=thi[0]; m++) { for (n=tlo[1]; n<=thi[1]; n++) { offset = (m-tlo[0])*lld + (n-tlo[1]); if (buf[offset] != m*dims[1]+n) { ok = 0; printf("Read mismatch for [%d,%d] expected: %d actual: %d\n", m,n,m*dims[1]+n,lld); } } } delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); } free(buf); free(getid); } /* Sync all processors at end of initialization loop */ NGA_Sync(); ok = trueEverywhere(ok); if (me==0 && ok) { printf("\nnon-blocking-read1 test OK\n"); } else if (me==0 && !ok) { printf("\nnon-blocking-read1 test failed\n"); } printTimes(g_time, g_ntime, g_elems, "Non-blocking get performance"); GA_Zero(g_count); if (me==0) { printf("\n[%d]Testing non-blocking-acc1 from 0.\n", me); } GA_Zero(g_time); GA_Zero(g_ntime); GA_Zero(g_elems); #pragma omp parallel num_threads(thread_count) { /* declare variables local to each thread */ int lo[2], hi[2], tlo[2], thi[2]; int ld[2]; int k, m, n; int xinc, yinc; int itx, ity; int offset; int *buf; int lld; long task, inc; int id; double delta_t; int bsize; ga_nbhdl_t* accid = (ga_nbhdl_t*) malloc(sizeof(ga_nbhdl_t*)); id = omp_get_thread_num(); inc = 1; delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); buf = (int*)malloc(block_x*block_y*sizeof(int)); while (task < tx*ty) { ity = task%ty; itx = (task-ity)/ty; tlo[0] = itx*block_x; tlo[1] = ity*block_y; thi[0] = tlo[0] + block_x - 1; if (thi[0] >= dims[0]) thi[0] = dims[0]-1; thi[1] = tlo[1] + block_y - 1; if (thi[1] >= dims[1]) thi[1] = dims[1]-1; lld = thi[1]-tlo[1]+1; bsize = (thi[0]-tlo[0]+1)*(thi[1]-tlo[1]+1); /* Accumulate values to a portion of global array */ for (m=tlo[0]; m<=thi[0]; m++) { for (n=tlo[1]; n<=thi[1]; n++) { offset = (m-tlo[0])*lld + (n-tlo[1]); buf[offset] = m*dims[1]+n; } } delta_t = GA_Wtime(); NGA_NbAcc(g_src, tlo, thi, buf, &lld, &one,accid); NGA_NbWait(accid); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_time,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_ntime,&me,&me,&one,&one,&one); NGA_Acc(g_elems,&me,&me,&bsize,&one,&one); delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); } free(buf); } /* Sync all processors at end of initialization loop */ NGA_Sync(); /* Check local buffer for correct values */ NGA_Distribution(g_src,me,glo,ghi); ok = 1; NGA_Access(g_src,glo,ghi,&ptr,gld); icnt = 0; for (i=glo[0]; i<=ghi[0]; i++) { for (j=glo[1]; j<=ghi[1]; j++) { if (ptr[icnt] != 2*(i*dims[1]+j)) { ok = 0; printf("p[%d] (Acc1) mismatch at point [%d,%d] actual: %d expected: %d\n", me,i,j,ptr[icnt],i*dims[1]+j); } icnt++; } } NGA_Release(g_src,glo,ghi); ok = trueEverywhere(ok); if (me==0 && ok) { printf("\nnon-blocking-acc1 test OK\n"); } else if (me==0 && !ok) { printf("\nnon-blocking-acc1 test failed\n"); } printTimes(g_time, g_ntime, g_elems, "Non-blocking accumulate performance"); /* Sync all processors*/ NGA_Sync(); /* Testing random work pattern */ if (me==0) { printf("\n[%d]Testing non-blocking-ran1 from 0.\n", me); } /* Reinitialize source array */ NGA_Distribution(g_src,me,glo,ghi); NGA_Access(g_src,glo,ghi,&ptr,gld); icnt = 0; for (i=glo[0]; i<=ghi[0]; i++) { for (j=glo[1]; j<=ghi[1]; j++) { ptr[icnt] = i*dims[1]+j; icnt++; } } NGA_Release(g_src,glo,ghi); /* Mimic random work. */ GA_Zero(g_dest); GA_Zero(g_count); #pragma omp parallel num_threads(thread_count) { /* declare variables local to each thread */ int tlo[2], thi[2]; int ld[2]; int k, m, n; int xinc, yinc; int itx, ity; int offset, icnt; int *buf, *buft; int lld; long task, inc; int id; int tmp; double delta_t; id = omp_get_thread_num(); inc = 1; delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); ga_nbhdl_t* gethdl=(ga_nbhdl_t*)malloc(sizeof(ga_nbhdl_t)); ga_nbhdl_t* acchdl=(ga_nbhdl_t*)malloc(sizeof(ga_nbhdl_t)); buf = (int*)malloc(block_x*block_y*sizeof(int)); buft = (int*)malloc(block_x*block_y*sizeof(int)); /* Read and transpose data */ while (task < 2*tx*ty) { k = task; if (k>=tx*ty) k -= tx*ty; ity = k%ty; itx = (k-ity)/ty; tlo[0] = itx*block_x; tlo[1] = ity*block_y; thi[0] = tlo[0] + block_x - 1; if (thi[0] >= dims[0]) thi[0] = dims[0]-1; thi[1] = tlo[1] + block_y - 1; if (thi[1] >= dims[1]) thi[1] = dims[1]-1; ld[0] = thi[0]-tlo[0]+1; ld[1] = thi[1]-tlo[1]+1; lld = thi[1]-tlo[1]+1; /* Get data from g_src */ NGA_NbGet(g_src, tlo, thi, buf, &lld, gethdl); NGA_NbWait(gethdl); /* Evaluate transpose of local bloack */ icnt = 0; for (m=0; m= dims[0]) thi[0] = dims[0]-1; thi[1] = tlo[1] + block_x - 1; if (thi[1] >= dims[1]) thi[1] = dims[1]-1; lld = thi[1]-tlo[1]+1; NGA_NbAcc(g_dest, tlo, thi, buft, &lld, &one,acchdl); NGA_NbWait(acchdl); delta_t = GA_Wtime(); task = NGA_Read_inc(g_count, &zero, inc); delta_t = GA_Wtime()-delta_t; NGA_Acc(g_ritime,&me,&me,&delta_t,&one,&rone); NGA_Acc(g_rinc,&me,&me,&one,&one,&one); } free(buf); free(buft); free(gethdl); free(acchdl); } GA_Sync(); /* Check local buffer for correct values */ NGA_Distribution(g_dest,me,glo,ghi); ok = 1; NGA_Access(g_dest,glo,ghi,&ptr,gld); icnt = 0; for (i=glo[0]; i<=ghi[0]; i++) { for (j=glo[1]; j<=ghi[1]; j++) { if (ptr[icnt] != 2*(j*dims[0]+i)) { ok = 0; printf("p[%d] (Ran1) mismatch at point [%d,%d] actual: %d expected: %d\n", me,i,j,ptr[icnt],2*(j*dims[0]+i)); } icnt++; } } NGA_Release(g_dest,glo,ghi); ok = trueEverywhere(ok); if (me==0 && ok) { printf("\nnon-blocking-ran1 test OK\n"); } else if (me==0 && !ok) { printf("\nnon-blocking-ran1 test failed\n"); } printTimes(g_ritime, g_rinc, g_rinc, "Read-increment performance"); GA_Destroy(g_src); GA_Destroy(g_dest); GA_Destroy(g_count); GA_Destroy(g_time); GA_Destroy(g_ntime); GA_Terminate(); MPI_Finalize(); return return_code; #else printf("OPENMP Disabled\n"); return 0; #endif } ga-5.9.2/global/testing/tiled_irreg_test.c000066400000000000000000000176241500715745200205470ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #include #define DIM 128 #define MINBLOCK 10 #include "ga.h" #include "macdecls.h" #include "mp3.h" #define MAX_FACTOR 1024 void grid_factor(int p, int xdim, int ydim, int zdim, int *idx, int *idy, int *idz) { int i, j, k; int ip, ifac, pmax, prime[MAX_FACTOR]; int fac[MAX_FACTOR]; int ix, iy, iz, ichk; i = 1; /** * factor p completely * first, find all prime numbers, besides 1, less than or equal to * the square root of p */ ip = (int)(sqrt((double)p))+1; pmax = 0; for (i=2; i<=ip; i++) { ichk = 1; for (j=0; j MAX_FACTOR) printf("Overflow in grid_factor\n"); prime[pmax-1] = i; } } /** * find all prime factors of p */ ip = p; ifac = 0; for (i=0; i= 0; i--) { ix = xdim/(*idx); iy = ydim/(*idy); iz = zdim/(*idz); if (ix >= iy && ix >= iz && ix > 1) { *idx = fac[i]*(*idx); } else if (iy >= ix && iy >= iz && iy > 1) { *idy = fac[i]*(*idy); } else if (iz >= ix && iz >= iy && iz > 1) { *idz = fac[i]*(*idz); } else { printf("Too many processors in grid factoring routine\n"); } } } int main( int argc, char **argv ) { int nblocks[3] = {0, 0, 0}; int inc, offset, ncnt, icnt; int *mapc; int total_blocks; int g_a; int ndim = 3; int dims[3] = {DIM,DIM,DIM}; int ipx, ipy, ipz; int proc_grid[3]; int iblock; int lo[3], hi[3]; int ld[2] = {DIM, DIM}; int i, j, k; int *chkbuf; int ok; int heap=3000000, stack=2000000; int me, nproc; MP_INIT(argc,argv); GA_INIT(argc,argv); /* initialize GA */ me=GA_Nodeid(); nproc=GA_Nnodes(); if(me==0) { if(GA_Uses_fapi())GA_Error("Program runs with C array API only",1); printf("Testing tiled irregular data distributions\n"); printf("\nUsing %ld processes\n",(long)nproc); fflush(stdout); } heap /= nproc; stack /= nproc; if(! MA_init(MT_F_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ /* Create an irregular tiled array. */ if(me==0)printf("\nCreating of size %d x %d x %d\n",DIM,DIM,DIM); /* Figure out how many blocks in each dimension */ for (i=0; i<3; i++) { inc = i; offset = 0; while (offset Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_sync() do i = 1, n do j = 1, n if(b(i,j).ne.(0d0,0d0)) then write(6,*) me,' zero ', i, j, b(i,j) call ffflush(6) c call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format(/'> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/4 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c call ga_get(g_a, 1, n, 1, n, b, n) c do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif call ga_sync() c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format(/'> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif do j = jlo, jhi do i = ilo, ihi if (b(i,j) .ne. a(i,j)) then write(6,*)'error:', i, j, b(i,j), a(i,j) call ga_error('... exiting ',0) endif enddo enddo c enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) call ffflush(6) endif call ga_sync() c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif call ga_sync() #ifndef MIRROR if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) #else if(iproc.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) #endif call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c c Check scatter&gather c call ga_sync() if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_sync() #ifndef MIRROR itmp = iran(nproc)-1 if(me.eq.itmp) then #else itmp = iran(lprocs)-1 if(iproc.eq.itmp) then #endif do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo call ga_gather(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',0) endif enddo endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_sync() #ifndef MIRROR if(me.eq.iran(ga_nnodes())-1) then #else if(me.eq.iran(lprocs)-1) then #endif do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo v(loop) = (1d0,-1d0) *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) if(w(loop) .ne. (1d0,-1d0) *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', (1d0,-1d0) *(ilo+jlo) call ffflush(6) endif enddo endif call ga_sync() enddo c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c c Delete the global arrays c status = ga_destroy(g_b) status = ga_destroy(g_a) c istatus = nga_deregister_type(scpl_type) c end c----------------------------------------------------------------- subroutine check_complex() implicit none #include "mafdecls.fh" #include "global.fh" #include "testutil.fh" c integer n,m parameter (n = 60) parameter (m = 2*n) double complex a(n,n), b(n,n), v(m),w(m) #ifdef MIRROR integer ndim, dims(2), chunk(2), p_mirror #else # ifdef NEW_API integer ndim, dims(2), chunk(2), p_mirror # endif #endif integer iv(m), jv(m) logical status integer g_a, g_b integer iran, i,j, loop,nloop,maxloop, ilo, ihi, jlo, jhi, itmp integer nproc, me, int, ij, inc, ii, jj, nnodes parameter (maxloop = 100) integer maxproc parameter (maxproc = 4096) double precision crap, real double precision nwords double complex x, sum1, sum2, factor integer lprocs, inode, iproc, lproc integer dcpl_type, istatus #ifdef USE_RESTRICTED integer num_rstrctd integer rstrctd_list(maxproc/2) #endif #ifdef BLOCK_CYCLIC integer block_size(2), proc_grid(2) #endif intrinsic int iran(i) = int(drand(0)*real(i)) + 1 c nproc = ga_nnodes() me = ga_nodeid() inode = ga_cluster_nodeid() lprocs = ga_cluster_nprocs(inode) nnodes = ga_cluster_nnodes() iproc = mod(me,lprocs) nloop = Min(maxloop,n) #ifdef USE_RESTRICTED num_rstrctd = nproc/2 if (num_rstrctd.eq.0) num_rstrctd = 1 do i = 1, num_rstrctd rstrctd_list(i) = (num_rstrctd/2) + i-1 end do #endif #ifdef BLOCK_CYCLIC block_size(1) = 32 block_size(2) = 32 #ifdef USE_SCALAPACK_DISTR if (mod(nproc,2).ne.0) + call ga_error("Available procs must be divisible by 2",0) proc_grid(1) = 2 proc_grid(2) = nproc/2 #endif #endif c c a() is a local copy of what the global array should start as c do j = 1, n do i = 1, n #ifndef MIRROR a(i,j) = cmplx(dble(i-1), dble((j-1)*n)) #else a(i,j) = cmplx(dble(inode),0.0d00) + + cmplx(dble(i-1), dble((j-1)*n)) #endif b(i,j) = cmplx(-1d0,1d0) enddo enddo c c Create type c dcpl_type = nga_register_type(16) c c Create a global array c c print *,ga_nodeid(), ' creating array' call ffflush(6) c call setdbg(1) #ifdef NEW_API ndim = 2 dims(1) = n dims(2) = n g_a = ga_create_handle() call ga_set_data(g_a,ndim,dims,dcpl_type) call ga_set_array_name(g_a,'a') #ifdef USE_RESTRICTED call ga_set_restricted(g_a, rstrctd_list, num_rstrctd) #endif #ifdef BLOCK_CYCLIC #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_a,block_size,proc_grid) #else call ga_set_block_cyclic(g_a,block_size) #endif #endif # ifdef MIRROR p_mirror = ga_pgroup_get_mirror() call ga_set_pgroup(g_a,p_mirror) # endif status = ga_allocate(g_a) #else # ifndef MIRROR status = ga_create(dcpl_type, n, n, 'a', 0, 0, g_a) # else ndim = 2 dims(1) = n dims(2) = n chunk(1) = 0 chunk(2) = 0 p_mirror = ga_pgroup_get_mirror() status = nga_create_config(dcpl_type, ndim, dims, 'a', chunk, + p_mirror, g_a) # endif #endif if (.not. status) then write(6,*) ' ga_create failed' call ga_error('... exiting ',0) endif #ifdef NEW_API g_b = ga_create_handle() call ga_set_data(g_b,ndim,dims,dcpl_type) call ga_set_array_name(g_b,'b') #ifdef BLOCK_CYCLIC #ifdef USE_SCALAPACK_DISTR call ga_set_block_cyclic_proc_grid(g_b,block_size,proc_grid) #else call ga_set_block_cyclic(g_b,block_size) #endif #endif # ifdef MIRROR call ga_set_pgroup(g_b,p_mirror) # endif if (.not.ga_allocate(g_b)) then #else # ifndef MIRROR if (.not. ga_create(dcpl_type, n, n, 'b', 0, 0, g_b)) then # else if (.not. nga_create_config(dcpl_type, ndim, dims, 'b', chunk, _ p_mirror, g_b)) then # endif #endif call ga_error('ga_create failed for second array ',0) endif #ifndef MIRROR call ga_distribution(g_a,me,ilo, ihi, jlo, jhi) #else lproc = me - ga_cluster_procid(inode,0) call ga_distribution(g_a, lproc, ilo, ihi, jlo, jhi) #endif call ga_sync() c c Zero the array c if (me .eq. 0) then write(6,21) 21 format('> Checking zero ... ') call ffflush(6) endif call ga_zero(g_a) c c Check that it is indeed zero c call ga_get(g_a, 1, n, 1, n, b, n) call ga_sync() do i = 1, n do j = 1, n if(b(i,j).ne.(0d0,0d0)) then write(6,*) me,' zero ', i, j, b(i,j) call ffflush(6) c call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_zero is OK' write(6,*) endif call ga_sync() c c Each node fills in disjoint sections of the array c if (me .eq. 0) then write(6,2) 2 format('> Checking disjoint put ... ') call ffflush(6) endif call ga_sync() c inc = (n-1)/20 + 1 ij = 0 do j = 1, n, inc do i = 1, n, inc #ifndef MIRROR if (mod(ij,nproc) .eq. me) then #else if (mod(ij,lprocs) .eq. iproc) then #endif ilo = i ihi = min(i+inc, n) jlo = j jhi = min(j+inc, n) call ga_put(g_a, ilo, ihi, jlo, jhi, a(ilo, jlo), n) endif ij = ij + 1 enddo enddo call ga_sync() c c All nodes check all of a c call util_qfill(n*n, (0d0,0d0), b, 1) call ga_get(g_a, 1, n, 1, n, b, n) c do i = 1, n do j = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' put ', me, i, j, a(i,j),b(i,j) call ffflush(6) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_put is OK' write(6,*) endif call ga_sync() c c Now check nloop random gets from each node c if (me .eq. 0) then write(6,5) nloop 5 format('> Checking random get (',i5,' calls)...') call ffflush(6) endif call ga_sync() c nwords = 0 c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do loop = 1, nloop ilo = iran(loop) ihi = iran(loop) if (ihi.lt. ilo) then itmp = ihi ihi = ilo ilo = itmp endif jlo = iran(loop) jhi = iran(loop) if (jhi.lt. jlo) then itmp = jhi jhi = jlo jlo = itmp endif c nwords = nwords + (ihi-ilo+1)*(jhi-jlo+1) c call util_qfill(n*n, (0.0d0,0d0), b, 1) call ga_get(g_a, ilo, ihi, jlo, jhi, b(ilo, jlo), n) if (me .eq. 0 .and. mod(loop-1, max(1,nloop/20)).eq.0) then write(6,1) loop, me, ilo, ihi, jlo, jhi, nwords 1 format(' call ',i5, ' node ',i2,' checking get ',4i4, $ ' total ',d9.2) call ffflush(6) endif do j = jlo, jhi do i = ilo, ihi if (b(i,j) .ne. a(i,j)) then write(6,*)'error:', i, j, b(i,j), a(i,j) call ga_error('... exiting ',0) endif enddo enddo c enddo if (me.eq.0) then write(6,*) write(6,*) ' ga_get is OK' write(6,*) call ffflush(6) endif call ga_sync() c c Check the ga_copy function c if (me .eq. 0) then write(6,*) write(6,*)'> Checking copy' write(6,*) call ffflush(6) endif call ga_sync() #ifndef MIRROR if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) #else if(iproc.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) #endif call ga_copy(g_a, g_b) call ga_get(g_b, 1, n, 1, n, b, n) do j = 1, n do i = 1, n if (b(i,j) .ne. a(i,j)) then write(6,*) ' copy ', me, i, j, a(i,j), b(i,j) call ga_error('... exiting ',0) endif enddo enddo if (me.eq.0) then write(6,*) write(6,*) ' copy is OK ' write(6,*) endif c c Check scatter&gather c call ga_sync() if (me .eq. 0) then write(6,*) '> Checking scatter/gather (might be slow)... ' call ffflush(6) if(me.eq.0) call ga_put(g_a, 1, n, 1, n, a, n) endif call ga_sync() c crap = drand(ga_nodeid()*51 +1) !Different seed for each proc do j = 1, 10 call ga_sync() #ifndef MIRROR itmp = iran(nproc)-1 if(me.eq.itmp) then #else itmp = iran(lprocs)-1 if(iproc.eq.itmp) then #endif do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo enddo call ga_gather(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,v(loop),1) if(v(loop) .ne. a(ilo,jlo))then write(6,*)me,' gather ', ilo,',',jlo,',', a(ilo,jlo) & ,' ',v(loop) call ffflush(6) call ga_error('... exiting ',0) endif enddo endif enddo c if (me.eq.0) then write(6,*) write(6,*) ' gather is OK' write(6,*) call ffflush(6) endif c do j = 1,10 call ga_sync() #ifndef MIRROR if(me.eq.iran(ga_nnodes())-1) then #else if(me.eq.iran(lprocs)-1) then #endif do loop = 1,m ilo = iran(n) jlo = iran(n) iv(loop) = ilo jv(loop) = jlo v(loop) = (1d0,-1d0) *(ilo+jlo) enddo call ga_scatter(g_a, v, iv, jv, m) do loop = 1,m ilo= iv(loop) jlo= jv(loop) call ga_get(g_a,ilo,ilo,jlo,jlo,w(loop),1) if(w(loop) .ne. (1d0,-1d0) *(ilo+jlo) )then write(6,*)me,' scatter ', ilo,',',jlo,',',w(loop) & ,' ', (1d0,-1d0) *(ilo+jlo) call ffflush(6) endif enddo endif call ga_sync() enddo c if (me.eq.0) then write(6,*) write(6,*) ' scatter is OK' write(6,*) endif c c Delete the global arrays c status = ga_destroy(g_b) status = ga_destroy(g_a) c istatus = nga_deregister_type(dcpl_type) c end subroutine util_qfill(n,val,a,ia) implicit none double complex a(*), val integer n, ia, i c c initialise double complex array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end ga-5.9.2/global/testing/unit-tests/000077500000000000000000000000001500715745200171605ustar00rootroot00000000000000ga-5.9.2/global/testing/unit-tests/ga_abs_value.c000066400000000000000000000063671500715745200217500ustar00rootroot00000000000000#include "ga.h" #include "mock.h" #include "ga_unit.h" static int test(int shape_idx, int type_idx, int dist_idx) { int type = TYPES[type_idx]; int *dims = SHAPES[shape_idx]; int ndim = SHAPES_NDIM[shape_idx]; mock_ga_t *mock_a, *result_a; int g_a; int buffer[100]; int lo[GA_MAX_DIM], hi[GA_MAX_DIM], ld[GA_MAX_DIM], shape[GA_MAX_DIM]; int result=0, error_index=-1, error_proc=-1; /* create the local array and result array */ mock_a = Mock_Create(type, ndim, dims, "mock", NULL); result_a = Mock_Create(type, ndim, dims, "mock", NULL); /* create the global array */ g_a = create_function[dist_idx](type, ndim, dims); /* create meaningful data for local array */ mock_data(mock_a, g_a); /* init global array with same data as local array */ mock_to_global(mock_a, g_a); /* call the local routine */ Mock_Abs_value(mock_a); /* call the global routine */ GA_Abs_value(g_a); /* get the results from the global array */ global_to_mock(g_a, result_a); /* compare the results */ result = neq_mock(mock_a, result_a, &error_index); if (0 != result) { error_proc = GA_Nodeid(); } /* make sure all procs get same result so they can die gracefully */ GA_Igop(&result, 1, "+"); /* if error occured, find the highest failing node ID */ GA_Igop(&error_proc, 1, "max"); /* clear the error index for all but the highest failing node ID */ if (error_proc != GA_Nodeid()) { error_index = 0; } /* make sure all procs get the error index on the highest failing node ID */ GA_Igop(&error_index, 1, "+"); if (0 != result) { if (error_proc == GA_Nodeid()) { printf("ERROR: local result failed to compare to global result\n"); printf("\terror_proc=%d\n", error_proc); printf("\terror_index=%d\n", error_index); printf("***LOCAL RESULT***\n"); Mock_Print(mock_a); printf("***GLOBAL RESULT***\n"); Mock_Print(result_a); printf("\tprinting array distribution\n"); } GA_Sync(); GA_Print(g_a); GA_Print_distribution(g_a); return 1; } /* clean up */ Mock_Destroy(mock_a); Mock_Destroy(result_a); GA_Destroy(g_a); return 0; } int main(int argc, char **argv) { TEST_SETUP; int shape_idx=0, type_idx=0, dist_idx=0; int return_code=0; for (shape_idx=0; shape_idx < NUM_SHAPES; ++shape_idx) { for (type_idx=0; type_idx < NUM_TYPES; ++type_idx) { for (dist_idx=0; dist_idx < NUM_DISTS; ++dist_idx) { if (0 == GA_Nodeid()) { printf("%s\t%s\t%s\n", SHAPE_NAMES[shape_idx], TYPE_NAMES[type_idx], DIST_NAMES[dist_idx] ); } GA_Sync(); return_code = test(shape_idx, type_idx, dist_idx); if (0 != return_code) { break; } } if (0 != return_code) { break; } } if (0 != return_code) { break; } } TEST_TEARDOWN; return return_code; } ga-5.9.2/global/testing/unit-tests/ga_acc.c000066400000000000000000000025241500715745200205240ustar00rootroot00000000000000/* * Test Program for GA * This is to test NGA_Acc (is a collective operation) * GA_Create -- used to create a global array using handles like 'g_A' */ #include #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, **local_A=NULL, **local_B=NULL; int dims[DIM]={SIZE,SIZE}, dims2[DIM], lo[DIM]={SIZE-SIZE,SIZE-SIZE}, hi[DIM]={SIZE-1,SIZE-1}, ld=5, value=5; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); local_A=(int**)malloc(SIZE*sizeof(int*)); for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, g_C, **local_C=NULL, dims[DIM]={SIZE,SIZE}, val1=5, val2=4, alpha=3, beta=2; int clo[DIM]={SIZE-SIZE,SIZE-SIZE}, chi[DIM]={SIZE-1,SIZE-1}, ld=SIZE; int **local_tm=NULL; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); local_C=(int**)malloc(SIZE*sizeof(int*)); for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 #define SIZ 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, local_A[SIZ][SIZ], dims[DIM]={SIZ,SIZ}, val1=5, alpha=3; int alo[DIM]={2,2}, ahi[DIM]={3,3}, ld=SIZ; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); GA_Fill(g_A, &val1); GA_Print(g_A); GA_Add_constant_patch(g_A, alo, ahi, &alpha); GA_Print(g_A); NGA_Get(g_A, alo, ahi, local_A, &ld); if(rank==1) { for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 #define GSIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, g_V, local_A[5][5], local_B[5][5], vsize=GSIZE; int dims[DIM]={GSIZE,GSIZE}, alo[DIM]={1,1}, ahi[DIM]={3,2}, blo[DIM]={1,1}, bhi[DIM]={2,3}, ld=5; int value=5, val2=4; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_V = NGA_Create(C_INT, 1, &vsize, "array_V", NULL); GA_Fill(g_A, &value); GA_Zero_diagonal(g_A); GA_Print(g_A); g_B = GA_Duplicate(g_A, "array_B"); GA_Fill(g_B, &val2); GA_Sync(); NGA_Zero_patch(g_B, blo, bhi); GA_Print(g_B); GA_Fill(g_V, &val2); GA_Print(g_V); GA_Add_diagonal(g_A, g_V); GA_Print(g_A); GA_Sync(); if(rank == 0) printf("Test Completed \n"); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_add_patch.c000066400000000000000000000035101500715745200217010ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Add_patch (is a collective operation) * GA_Create -- used to create a global array using handles like 'g_A' * GA_Duplicate --used to duplicate and generate one more global array.., handle 'g_A' to 'g_B' * * */ #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, g_C, local_C[DIM][DIM], dims[DIM]={5,5}, val1=5, val2=4, alpha=3, beta=2, ld=5; int alo[DIM]={2,2}, ahi[DIM]={3,3}, blo[DIM]={2,2}, bhi[DIM]={3,3}, clo[DIM]={1,1}, chi[DIM]={2,2}; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_B = GA_Duplicate(g_A, "array_B"); g_C = GA_Duplicate(g_A, "array_C"); GA_Fill(g_A, &val1); GA_Fill(g_B, &val2); GA_Zero(g_C); NGA_Add_patch(&alpha, g_A, clo, chi, &beta, g_B, blo, bhi, g_C, clo, chi); GA_Sync(); GA_Print(g_A); GA_Print(g_B); GA_Print(g_C); NGA_Get(g_C, clo, chi, local_C, &ld); //printf("check 1 \n"); for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; double g_A, g_B; int dims[DIM]={SIZE,SIZE}, dims2[DIM], ndim2, type2, dims3[DIM], ndim3, type3, lo[DIM]={0,0}, hi[DIM]={4,4}, ld=SIZE; double value=5, val2=4; double local_A[SIZE][SIZE], local_B[SIZE][SIZE]; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_DBL, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_DBL, DIM, dims, "array_A", NULL); GA_Fill(g_A, &value); g_B = GA_Duplicate(g_A, "array_B"); GA_Print(g_A); GA_Sync(); GA_Copy(g_A, g_B); GA_Print(g_B); // The process is confirmed and verified by printing the array in OP-scr /* if(rank==0) if(content(g_A) != content(g_B))printf("ERROE : \n"); */ if(rank==0) { NGA_Get(g_A, lo, hi, local_A, &ld); NGA_Get(g_B, lo, hi, local_B, &ld); for(i=1; i<3; i++) { for(j=1; j<3; j++) printf("%f ", local_B[i][j]); printf("\n"); } printf("\n"); for(i=1; i<3; i++) { for(j=1; j<3; j++) printf("%f ", local_A[i][j]); printf("\n"); } printf("\n"); for(i=1; i<3; i++) { for(j=1; j<3; j++) { if(local_B[j][i]!=local_A[i][j]) printf("ERROR : in passing values \n"); } } } if(rank == 0) GA_PRINT_MSG(); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_copy3.c000066400000000000000000000040001500715745200210220ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Duplicate (is a collective operation) * GA_Create -- used to create a global array using handles like 'g_A' * GA_Duplicate --used to duplicate and generate one more global array.., handle 'g_A' to 'g_B' * But GA_Copy -- is used to transfer the data from one array to other.., ie) from g_A to g_B * Here used GA_Inquire to verify that g_A hanle returns the right values of created_array */ #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, local_A[SIZE][SIZE], local_B[SIZE][SIZE]; int dims[DIM]={SIZE,SIZE}, dims2[DIM], ndim2, type2, dims3[DIM], ndim3, type3, lo[DIM]={0,0}, hi[DIM]={4,4}; int value=5, val2=4, ld=5; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); GA_Fill(g_A, &value); g_B = GA_Duplicate(g_A, "array_B"); GA_Print(g_A); GA_Sync(); GA_Copy(g_A, g_B); GA_Print(g_B); // The process is confirmed and verified by printing the array in OP-scr /* if(rank==0) if(content(g_A) != content(g_B))printf("ERROE : \n"); */ if(rank==0) { NGA_Get(g_A, lo, hi, local_A, &ld); NGA_Get(g_B, lo, hi, local_B, &ld); for(i=1; i<3; i++) { for(j=1; j<3; j++) printf("%d ", local_B[i][j]); printf("\n"); } printf("\n"); for(i=1; i<3; i++) { for(j=1; j<3; j++) printf("%d ", local_A[i][j]); printf("\n"); } printf("\n"); for(i=1; i<3; i++) { for(j=1; j<3; j++) { if(local_B[i][j]!=local_A[i][j]) printf("ERROR : in passing values \n"); } } } if(rank == 0) GA_PRINT_MSG(); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_copypatch.c000066400000000000000000000033321500715745200217660ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Copy_patch (is a collective operation) * GA_Create -- used to create a global array using handles like 'g_A' * GA_Duplicate --used to duplicate and generate one more global array.., handle 'g_A' to 'g_B' * GA_Copy -- is used to transfer the data from one array to other.., ie) from g_A to g_B * GA_Copy_patch -- is used to copy only certain patch of one array to another */ #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, local_A[SIZE][SIZE]; int dims[DIM]={SIZE,SIZE}, alo[DIM]={0,0}, ahi[DIM]={3,3}, blo[DIM]={0,0}, bhi[DIM]={3,3}, ld=5; int value=5, val2=4; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); GA_Fill(g_A, &value); g_B = GA_Duplicate(g_A, "array_B"); // GA_Print(g_A); GA_Fill(g_B, &val2); GA_Sync(); NGA_Copy_patch('N', g_A, alo, ahi, g_B, blo, bhi); // GA_Print(g_B); NGA_Get(g_B, blo, bhi, local_A, &ld); if(rank==0) { /* for(i=0; i<3; i++) { for(j=0; j<3; j++)// printf("%d ", local_A[i][j]); printf("\n"); } //printf("\n"); */ for(i=0; i<3; i++) { for(j=0; j<3; j++) if( local_A[i][j]!=value) printf("ERROR : \n"); } } // The process is confirmed and verified by printing the array in OP-scr /* if(rank==0) if(content(g_A) != content(g_B))printf("ERROE : \n"); */ if(rank == 0) printf("Test Completed \n"); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_copypatch2.c000066400000000000000000000032751500715745200220560ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Copy_patch (is a collective operation) * GA_Create -- used to create a global array using handles like 'g_A' * GA_Duplicate --used to duplicate and generate one more global array.., handle 'g_A' to 'g_B' * GA_Copy -- is used to transfer the data from one array to other.., ie) from g_A to g_B * GA_Copy_patch -- is used to copy only certain patch of one array to another */ #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, local_A[SIZE][SIZE]; int dims[DIM]={SIZE,SIZE}, alo[DIM]={0,0}, ahi[DIM]={3,3}, blo[DIM]={0,0}, bhi[DIM]={3,3}, ld=5; int value=5, val2=4; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); GA_Fill(g_A, &value); g_B = GA_Duplicate(g_A, "array_B"); GA_Print(g_A); GA_Fill(g_B, &val2); GA_Sync(); NGA_Copy_patch('N', g_A, alo, ahi, g_B, blo, bhi); GA_Print(g_B); NGA_Get(g_B, blo, bhi, local_A, &ld); if(rank==0) { for(i=0; i<3; i++) { for(j=0; j<3; j++) printf("%d ", local_A[i][j]); printf("\n"); } printf("\n"); for(i=0; i<3; i++) { for(j=0; j<3; j++) if( local_A[i][j]!=value) printf("ERROR : \n"); } } // The process is confirmed and verified by printing the array in OP-scr /* if(rank==0) if(content(g_A) != content(g_B))printf("ERROE : \n"); */ if(rank == 0) printf("Test Completed \n"); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_create.c000066400000000000000000000057271500715745200212510ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Create (is a collective operation) * GA_Create -- used to create a global array using handles like 'g_A' * Here used GA_Inquire to verify that g_A hanle returns the right values of created_array */ #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #define GSIZE 5 one_dimension(int rank, int nprocs) { int g_V; int ndim=1, dims=GSIZE; int dims2, ndim2, type, value=5; g_V = NGA_Create(C_INT, ndim, &dims, "array_V", NULL); GA_Fill(g_V, &value); GA_Print(g_V); GA_Sync(); // NGA_Inquire(g_V, &type, &ndim2, dims2); //printf(" %d -- %d,,\n", type, ndim2); //for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define SIZE 10 #define MAX_DIM 7 void create_ga(int ndim, int datatypes) { int g_A; int dims[MAX_DIM], i, val=4; for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define SIZE 10 #define MAX_DIM 7 void create_ga(int ndim) { int g_A; int dims[MAX_DIM], i, val=4; for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define SIZE 10 #define MAX_DIM 7 //#define NUM_TYPES 3 void create_ga(int ndim, int datatypes) { int g_A; int dims[MAX_DIM], i, val=4; for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" int main(int argc, char **argv) { int rank, nprocs; int g_A, g_B; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A=GA_Create_handle(); g_B=GA_Create_handle(); if(!g_A)\ GA_ERROR_MSG(); if(!g_B) GA_ERROR_MSG(); GA_Sync(); if(rank == 0) GA_PRINT_MSG(); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_create_irreg.c000066400000000000000000000100431500715745200224240ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Create_irreg (is a collective operation) * GA_Create -- used to create a global array of regular size * GA_Create_IRREG -- used to create G_array of irregular size -- helps user to define the distribution * Here used GA_Inquire to verify that g_A hanle returns the right values of created_array */ #include #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 #define GSIZE 10 irregular_array1(int rank) { int g_A, g_B; int dims[DIM]={5,10}, dims2[DIM], ndim, type, value=5, block[DIM]={2,3}, map[5]={0,2,0,4,6}, val=7; int n_block[DIM], block_dims[DIM], i; g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_B = NGA_Create_irreg(C_INT, DIM, dims, "array_B", block, map); GA_Fill(g_A, &value); GA_Print(g_A); GA_Fill(g_B, &val); GA_Print(g_B); GA_Sync(); NGA_Inquire(g_A, &type, &ndim, dims2); //printf(" %d -- %d,,\n", type, ndim); /* GA_Get_block_info(g_B, n_block, block_dims); for(i=0; i=GSIZE); block[0]=b1; block[1]=b2; map=(int*)malloc(nprocs*sizeof(int)); for(i=0; i=GSIZE); block[0]=b1; block[1]=b2; map=(int*)malloc(nprocs*sizeof(int)); for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 #define GSIZE 10 irregular_array1(int rank) { int g_A, g_B; int dims[DIM]={5,10}, dims2[DIM], ndim, type, val_A=5, block[DIM]={2,3}, map[5]={0,2,0,4,6}, val_B=7; int n_block[DIM], block_dims[DIM], i; g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_B = NGA_Create_irreg(C_INT, DIM, dims, "array_B", block, map); GA_Fill(g_A, &val_A); GA_Print(g_A); GA_Fill(g_B, &val_B); GA_Print(g_B); GA_Sync(); NGA_Inquire(g_A, &type, &ndim, dims2); //printf(" %d -- %d,,\n", type, ndim); /* GA_Get_block_info(g_B, n_block, block_dims); for(i=0; i=GSIZE); block[0]=b1; block[1]=b2; map=(int*)malloc(nprocs*sizeof(int)); for(i=0; i=GSIZE); block[0]=b1; block[1]=b2; map=(int*)malloc(nprocs*sizeof(int)); for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define SIZE 5 #define MAX_DIM 2 create_irreg_ga(int ndim) { int g_A; int dims[ndim], i; int block[ndim], *map=NULL; for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 int main(int argc, char **argv) { int rank, nprocs, i; int g_A, value=5; int dims[DIM]={5,5}, dims2[DIM], ndim, type; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); GA_Fill(g_A, &value); GA_Print(g_A); //GA_Sync(); printf(" -----------%d\n", rank); GA_Destroy(g_A); printf(" %d-----------\n", rank); GA_Print(g_A); if(rank == 0) printf("Test Completed \n"); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_dgop.c000066400000000000000000000076141500715745200207340ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Dgop (is a collective operation) * * */ #include #include #include #include"mpi.h" #include"ga.h" #include"macdecls.h" addition_operator(int rank, int nprocs, int n) { int i; double x[n], temp[n]; for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 #define SIZE 5 integer_dot(int rank, int nprocs) { int g_A, g_B; int dims[DIM]={SIZE,SIZE}, val_A=5, val_B=10, op; MA_init(C_INT, 1000, 1000); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_B = NGA_Create(C_INT, DIM, dims, "array_B", NULL); GA_Fill(g_A, &val_A); GA_Fill(g_B, &val_B); if(rank==0) { op=GA_Idot(g_A, g_B); printf("%d \n", op); } } /* when i use int_type is working...have find the reason behind the other datatype and how to use it.., */ /* float_dot(int rank, int nprocs) { int dims[DIM]={SIZE,SIZE}; double g_A, g_B; double val_A=5, val_B=10, op; //MA_init(C_DBL, 1000, 1000); g_A = NGA_Create(C_DBL, DIM, dims, "array_A", NULL); g_B = NGA_Create(C_DBL, DIM, dims, "array_B", NULL); GA_Fill(g_A, &val_A); GA_Fill(g_B, &val_B); if(rank==0) op=GA_Ddot(g_A, g_B); // printf("%d \n", op); } */ int main(int argc, char **argv) { int rank, nprocs; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); // MA_init(C_DBL, 1000, 1000); GA_Initialize(); integer_dot(rank, nprocs); //float_dot(rank, nprocs); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_duplicate.c000066400000000000000000000026751500715745200217570ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Duplicate (is a collective operation) * GA_Create -- used to create a global array using handles like 'g_A' * GA_Duplicate --helps to duplicate the content from handle 'g_A' * Here used GA_Inquire to verify that g_A hanle returns the right values of created_array */ #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 int main(int argc, char **argv) { int rank, nprocs, i; double g_A, g_B; int dims[DIM]={5,5}, dims2[DIM], ndim2, type2, dims3[DIM], ndim3, type3; double value=5, val2=4; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_DBL, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_DBL, DIM, dims, "array_A", NULL); GA_Fill(g_A, &value); g_B = GA_Duplicate(g_A, "array_B"); GA_Print(g_A); GA_Sync(); GA_Fill(g_B, &val2); GA_Print(g_B); if(rank==0) { NGA_Inquire(g_A, &type2, &ndim2, dims2); NGA_Inquire(g_A, &type3, &ndim3, dims3); printf(" %d -- %d,,\n", type2, ndim2); if(type2!=type3 || ndim2!=ndim3 ) printf("ERROE : \n"); if(type2==type3 || ndim2==ndim3 ) printf("ERROE : Equal \n"); for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, g_C, local_C[DIM][DIM]; int dims[DIM]={5,5}, dims2[DIM], ndim, type; int val_A=5, val_B=3, ld=DIM; int lo[DIM]={2,2}, hi[DIM]={4,4}, blo[DIM]={0,0}, bhi[DIM]={2,2}, clo[DIM]={1,1}, chi[DIM]={3,3}; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_B = NGA_Create(C_INT, DIM, dims, "array_B", NULL); g_C = NGA_Create(C_INT, DIM, dims, "array_C", NULL); GA_Fill(g_A, &val_A); GA_Fill(g_B, &val_B); GA_Zero(g_C); GA_Elem_divide_patch(g_A, lo, hi, g_B, blo, bhi, g_C, clo, chi); GA_Print(g_C); GA_Sync(); NGA_Get(g_C, clo, chi, local_C, &ld); if(rank==1) { for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, g_C, local_C[DIM][DIM], dims[DIM]={5,5}; int val_A=5, val_B=3, ld=DIM, max; int lo[DIM]={2,2}, hi[DIM]={4,4}, blo[DIM]={0,0}, bhi[DIM]={2,2}, clo[DIM]={1,1}, chi[DIM]={3,3}; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_B = NGA_Create(C_INT, DIM, dims, "array_B", NULL); g_C = NGA_Create(C_INT, DIM, dims, "array_C", NULL); GA_Fill(g_A, &val_A); GA_Fill(g_B, &val_B); GA_Zero(g_C); GA_Elem_maximum_patch(g_A, lo, hi, g_B, blo, bhi, g_C, clo, chi); GA_Print(g_C); GA_Sync(); NGA_Get(g_C, clo, chi, local_C, &ld); if(rank==1) { for(i=0; ival_B) max=val_A; else max=val_B; for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, g_C, local_C[DIM][DIM]; int dims[DIM]={5,5}, dims2[DIM], ndim, type; int val_A=5, val_B=3, ld=DIM; int lo[DIM]={2,2}, hi[DIM]={4,4}, blo[DIM]={0,0}, bhi[DIM]={2,2}, clo[DIM]={1,1}, chi[DIM]={3,3}; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_B = NGA_Create(C_INT, DIM, dims, "array_B", NULL); g_C = NGA_Create(C_INT, DIM, dims, "array_C", NULL); GA_Fill(g_A, &val_A); GA_Fill(g_B, &val_B); GA_Zero(g_C); GA_Elem_multiply_patch(g_A, lo, hi, g_B, blo, bhi, g_C, clo, chi); GA_Print(g_C); GA_Sync(); NGA_Get(g_C, clo, chi, local_C, &ld); if(rank==1) { for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 #define SIZE 5 fillandscale(int rank, int nprocs) { int g_A, val1=5, val2=5, local_A[SIZE][SIZE], i, j; int dims[DIM]={SIZE,SIZE}, alo[DIM]={1,1}, ahi[DIM]={2,2}, ld=5; g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); GA_Zero(g_A); NGA_Fill_patch(g_A, alo, ahi, &val1); GA_Print(g_A); GA_Scale(g_A, &val2); GA_Print(g_A); NGA_Get(g_A, alo, ahi, local_A, &ld); if(rank == 1) { for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, local_A[5][5], local_B[5][5]; int dims[DIM]={5,5}, alo[DIM]={1,1}, ahi[DIM]={3,2}, blo[DIM]={1,1}, bhi[DIM]={2,3}, ld=5; int value=5, val2=4; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); GA_Fill(g_A, &value); g_B = GA_Duplicate(g_A, "array_B"); GA_Print(g_A); GA_Fill(g_B, &val2); GA_Sync(); NGA_Zero_patch(g_B, blo, bhi); GA_Print(g_B); NGA_Get(g_A, alo, ahi, local_A, &ld); NGA_Get(g_B, blo, bhi, local_B, &ld); if(rank==0) { for(i=0; i<3; i++) { for(j=0; j<2; j++) printf("%d ", local_A[i][j]); printf("\n"); } printf("\n"); for(i=0; i<2; i++) { for(j=0; j<3; j++) printf("%d ", local_B[i][j]); printf("\n"); } for(i=0; i<2; i++) { for(j=0; j<3; j++) if( local_A[i][j]==local_B[i][j]) printf("ERROR : \n"); } } // The process is confirmed and verified by printing the array in OP-scr /* if(rank==0) if(content(g_A) != content(g_B))printf("ERROE : \n"); */ GA_Sync(); if(rank == 1) printf("Test Completed \n"); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_gather.c000066400000000000000000000041501500715745200212450ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Gather (is a one-sided operation) * GA_Create -- used to create a global array using handles like 'g_A' * * GA_gather -- used to get data from different location */ #include #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define N 5 #define D 2 int main(int argc, char **argv) { int rank, nprocs; int g_A, dims[D]={5,10}, local_A[N], local_G[N], sub_array[N][D], **s_array=NULL; int i, j, value=5; MPI_Init(&argc, &argv); GA_Initialize(); MA_init(C_INT, 1000, 1000); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); s_array=(int**)malloc(N*sizeof(int*)); for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define N 5 #define D 2 int main(int argc, char **argv) { int rank, nprocs; int g_A, dims[D]={5,10}, local_A[N], local_G[N], **sub_array=NULL, **s_array=NULL; int i, j, value=5; MPI_Init(&argc, &argv); GA_Initialize(); MA_init(C_INT, 1000, 1000); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); s_array=(int**)malloc(N*sizeof(int*)); for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define SIZE 10 #define N 5 #define D 2 int main(int argc, char **argv) { int rank, nprocs; int g_A, dims[D]={SIZE,SIZE}, *local_A=NULL, *local_G=NULL, **sub_array=NULL, **s_array=NULL; int i, j, value=5; MPI_Init(&argc, &argv); GA_Initialize(); MA_init(C_INT, 1000, 1000); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); s_array=(int**)malloc(N*sizeof(int*)); for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, **local_A=NULL, **local_B=NULL; int dims[DIM]={SIZE,SIZE}, dims2[DIM], lo[DIM]={SIZE-SIZE,SIZE-SIZE}, hi[DIM]={SIZE-1,SIZE-1}, ld=5, value=5; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); local_A=(int**)malloc(SIZE*sizeof(int*)); for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 int main(int argc, char **argv) { int rank, nprocs, i; int g_A, g_B; int dims[DIM]={5,10}, dims2[DIM], ndim, type, value=5, block[DIM]={2,3}, map[5]={0,2,0,4,6}, val=7; int n_block[DIM], block_dims[DIM]; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_B = NGA_Create_irreg(C_INT, DIM, dims, "array_B", block, map); GA_Fill(g_A, &value); GA_Print(g_A); GA_Fill(g_B, &val); GA_Print(g_B); GA_Sync(); GA_Get_block_info(g_B, n_block, block_dims); for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 #define GSIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, g_V, g_V2; int dims[DIM]={GSIZE,GSIZE}, alo[DIM]={1,1}, ahi[DIM]={3,2}, blo[DIM]={1,1}, bhi[DIM]={2,3}, ld=5; int value=5, val2=4, dims_V=GSIZE; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_V = NGA_Create(C_INT, 1, &dims_V, "array_V", NULL); g_V2 = NGA_Create(C_INT, 1, &dims_V, "array_V2", NULL); GA_Fill(g_A, &value); GA_Zero_diagonal(g_A); GA_Print(g_A); g_B = GA_Duplicate(g_A, "array_B"); GA_Fill(g_B, &val2); GA_Sync(); NGA_Zero_patch(g_B, blo, bhi); GA_Print(g_B); GA_Fill(g_V, &val2); GA_Print(g_V); GA_Add_diagonal(g_A, g_V); GA_Add_diagonal(g_B, g_V); GA_Print(g_A); GA_Sync(); GA_Get_diag(g_B, g_V2); GA_Print(g_V2); GA_Print(g_B); if(rank == 1) GA_PRINT_MSG(); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_igop.c000066400000000000000000000106241500715745200207340ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Igop (is a collective operation) * * */ #include #include #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define NUM 10 #if 0 addition_operator(int rank, int nprocs, int n) { int x[n], i, temp[n]; char op='+'; for(i=0; i #include #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define NUM 10 addition_operator(int *x, int nprocs) { int i, temp[NUM]; char *op="min"; for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 int main(int argc, char **argv) { int rank, nprocs, i; double g_A; int dims[DIM]={5,5}, dims2[DIM], ndim, type; double value=5; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_DBL, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_DBL, DIM, dims, "array_A", NULL); GA_Fill(g_A, &value); GA_Print(g_A); if(!g_A) printf("ERROR : \n"); NGA_Inquire(g_A, &type, &ndim, dims2); printf(" %d -- %d,,\n", type, ndim); for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 int main(int argc, char **argv) { int rank, nprocs, n=1; int g_A, dims[DIM]={5,5}; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); printf("check %d \n", n); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); if(GA_Ndim(g_A)!=DIM) printf("ERROR: GA_Ndim didnt return nDimension after GA_Initialize\n"); printf("%d : %d \n", rank, GA_Ndim(g_A)); GA_Terminate(); if(rank==0) printf(" GA: Test Completed \n"); MPI_Finalize(); return 0; } ga-5.9.2/global/testing/unit-tests/ga_lgop.c000066400000000000000000000074101500715745200207360ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Igop (is a collective operation) * * */ #include #include #include #include"mpi.h" #include"ga.h" #include"macdecls.h" addition_operator(int rank, int nprocs, int n) { int i; long x[n], temp[n]; char op='+'; for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs; int g_A, g_B, g_C, g_M, **local_M=NULL; int dims[DIM]={SIZE, SIZE}, val_A=5, val_B=4, val_C=3; int mlo[DIM]={0,0}, mhi[DIM]={4,4}, ld=SIZE, i, j; int local_T[SIZE][SIZE]; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_B = NGA_Create(C_INT, DIM, dims, "array_B", NULL); g_C = NGA_Create(C_INT, DIM, dims, "array_C", NULL); g_M = NGA_Create(C_INT, DIM, dims, "array_M", NULL); GA_Fill(g_A, &val_A); GA_Fill(g_B, &val_B); GA_Fill(g_C, &val_C); GA_Median(g_A,g_B, g_C, g_M); GA_Print(g_M); /* GA_Print(g_A); GA_Print(g_B); GA_Print(g_C); */ if(rank==0) { local_M=(int**)malloc(SIZE*sizeof(int*)); for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #define SIZE 5 #define ONE_DIM 1 #define TWO_DIM 2 #define THREE_DIM 3 one_dimension() { int g_A, dims[ONE_DIM]={SIZE}; g_A = NGA_Create(C_INT, ONE_DIM, dims, "array_A", NULL); if(GA_Ndim(g_A) != ONE_DIM) printf("ERROR: GA_Ndim didnt return nDimension after GA_Initialize \n"); GA_Destroy(g_A); } two_dimension() { int g_A, dims[TWO_DIM]={SIZE,SIZE}; g_A = NGA_Create(C_INT, TWO_DIM, dims, "array_A", NULL); if(GA_Ndim(g_A) != TWO_DIM) printf("ERROR: GA_Ndim didnt return nDimension after GA_Initialize \n"); GA_Destroy(g_A); } three_dimension() { int g_A, dims[THREE_DIM]={SIZE,SIZE,SIZE}; g_A = NGA_Create(C_INT, THREE_DIM, dims, "array_A", NULL); if(GA_Ndim(g_A) != THREE_DIM) printf("ERROR: GA_Ndim didnt return nDimension after GA_Initialize \n"); GA_Destroy(g_A); } int main(int argc, char **argv) { int rank, nprocs, n=1, temp=0; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); one_dimension(); two_dimension(); three_dimension(); GA_Sync(); if(rank == 0) printf("Test Completed \n"); GA_Terminate(); MPI_Finalize(); return 0; } ga-5.9.2/global/testing/unit-tests/ga_ndim2.c000066400000000000000000000016031500715745200210040ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Ndim (is a local operation) * verifing GA_Ndim -- which returns dimension of the g_A */ #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #define SIZE 20 #define NDIM 7 verify_ga_dim(int ndim) { int g_A, dims[ndim], i; for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" node_check(int nprocs) { if(GA_Nnodes() != nprocs) printf("ERROR: GA_Nnodes didnt return right number of processes \n"); } int main(int argc, char **argv) { int rank, nprocs; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); node_check(nprocs); GA_Sync(); if(rank == 0) printf("Test Completed \n"); GA_Terminate(); MPI_Finalize(); return 0; } ga-5.9.2/global/testing/unit-tests/ga_nodeid.c000066400000000000000000000012061500715745200212340ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Nodeid (is a local operation) * verify _Nodeid -- by comparing rank of each processes with corresponding node-ids */ #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 int main(int argc, char **argv) { int rank, nprocs; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); if(rank != GA_Nodeid()) GA_ERROR_MSG(); GA_Sync(); if(rank == 0) GA_COMPLETE_MSG(); GA_Terminate(); MPI_Finalize(); return 0; } ga-5.9.2/global/testing/unit-tests/ga_pgroup_brdcst.c000066400000000000000000000000031500715745200226410ustar00rootroot00000000000000 ga-5.9.2/global/testing/unit-tests/ga_pgroup_create.c000066400000000000000000000044731500715745200226420ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_PGroup_create (is a collective operation) * */ #include #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #define PGSIZE 4 int main(int argc, char **argv) { int rank, nprocs, i, j; int p_Geven, p_Godd, list[PGSIZE], p_size, *list_even=NULL, *list_odd=NULL, even, odd; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" int main(int argc, char **argv) { int rank, nprocs, i, j; int p_Geven, p_Godd, p_size, mod, p_size_mod, *list_even=NULL, *list_odd=NULL; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); p_size=nprocs/2; mod=nprocs%2; p_size_mod=p_size+mod; list_even = (int*)malloc(p_size*sizeof(int)); list_odd = (int*)malloc(p_size*sizeof(int)); j=0; for(i=0; i %d --:: -- %d\n", i, list_even[i], list_odd[i]); } */ GA_Sync(); printf("%d: My ID is %d :: %d --- nodeid & nnodes for GA \n", rank, GA_Nodeid(), GA_Nnodes()); p_Geven=GA_Pgroup_create(list_even, p_size_mod); p_Godd=GA_Pgroup_create(list_odd, p_size); GA_Sync(); if(rank%2==0) printf("%d: My ID is %d :: %d -- even \n", rank, GA_Pgroup_nodeid(p_Geven), GA_Pgroup_nnodes(p_Geven)); else printf("%d: My ID is %d :: %d --- odd\n", rank, GA_Pgroup_nodeid(p_Godd), GA_Pgroup_nnodes(p_Godd)); GA_Sync(); if(rank==0) GA_PRINT_MSG(); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_pgroup_create3.c000066400000000000000000000033361500715745200227220ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_PGroup_create (is a collective operation) * _pgroup_create -- helps to create different sized group from the given processes and helps to alocate different job for diffferent group * groups are identified using the group handle */ #include #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" int main(int argc, char **argv) { int rank, nprocs, i, j; int p_Geven, p_Godd, p_size, mod, p_size_mod, *list_even=NULL, *list_odd=NULL; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); p_size=nprocs/2; mod=nprocs%2; p_size_mod=p_size+mod; list_even = (int*)malloc(p_size*sizeof(int)); list_odd = (int*)malloc(p_size*sizeof(int)); j=0; for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" int main(int argc, char **argv) { int rank, nprocs, i, j; int p_Geven, p_Godd, p_size, mod, p_size_mod, *list_even=NULL, *list_odd=NULL; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); p_size=nprocs/2; mod=nprocs%2; p_size_mod=p_size+mod; list_even = (int*)malloc(p_size*sizeof(int)); list_odd = (int*)malloc(p_size*sizeof(int)); j=0; for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" int main(int argc, char **argv) { int rank, nprocs, i, j; int p_Geven, p_Godd, p_size, mod, p_size_mod, *list_even=NULL, *list_odd=NULL; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); p_size=nprocs/2; mod=nprocs%2; p_size_mod=p_size+mod; list_even = (int*)malloc(p_size*sizeof(int)); list_odd = (int*)malloc(p_size*sizeof(int)); j=0; for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" int main(int argc, char **argv) { int rank, nprocs, i, j; int p_Geven, p_Godd, p_size, mod, p_size_mod, *list_even=NULL, *list_odd=NULL; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); p_size=nprocs/2; mod=nprocs%2; p_size_mod=p_size+mod; list_even = (int*)malloc(p_size*sizeof(int)); list_odd = (int*)malloc(p_size*sizeof(int)); j=0; for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" int main(int argc, char **argv) { int rank, nprocs, i, j; int p_Geven, p_Godd, p_size, mod, p_size_mod, *list_even=NULL, *list_odd=NULL; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); p_size=nprocs/2; mod=nprocs%2; p_size_mod=p_size+mod; list_even = (int*)malloc(p_size*sizeof(int)); list_odd = (int*)malloc(p_size*sizeof(int)); j=0; for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" even_odd_pgroup(int rank, int nprocs) { int i, j; int p_Geven, p_Godd, p_size, mod, p_size_mod, *list_even=NULL, *list_odd=NULL; p_size=nprocs/2; mod=nprocs%2; p_size_mod=p_size+mod; list_even = (int*)malloc(p_size*sizeof(int)); list_odd = (int*)malloc(p_size*sizeof(int)); j=0; for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" int main(int argc, char **argv) { int rank, nprocs, i, j; int p_Geven, p_Godd, p_size, *list_even=NULL, *list_odd=NULL; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); p_size=nprocs/2; list_even = (int*)malloc(p_size*sizeof(int)); list_odd = (int*)malloc(p_size*sizeof(int)); j=0; for(i=0; i %d --:: -- %d\n", i, list_even[i], list_odd[i]); } printf("\n"); GA_Sync(); printf("%d: My ID is %d :: %d \n", rank, GA_Nodeid(), GA_Nnodes()); p_Geven=GA_Pgroup_create(list_even, p_size); p_Godd=GA_Pgroup_create(list_odd, p_size); GA_Sync(); if(rank==1) printf(" GA: Test Completed \n"); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_put.c000066400000000000000000000037421500715745200206110ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Put (is a collective operation) * GA_Create -- used to create a global array using handles like 'g_A' */ #include #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, **local_A=NULL, **local_B=NULL; int dims[DIM]={SIZE,SIZE}, dims2[DIM], lo[DIM]={SIZE-SIZE,SIZE-SIZE}, hi[DIM]={SIZE-1,SIZE-1}, ld=5, value=5; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); local_A=(int**)malloc(SIZE*sizeof(int*)); for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 #define SIZE 20 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, **local_A=NULL, **local_B=NULL; int dims[DIM]={SIZE,SIZE}, dims2[DIM], lo[DIM]={SIZE-SIZE,SIZE-SIZE}, hi[DIM]={SIZE-1,SIZE-1}, ld=5, value=5; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); local_A=(int**)malloc(SIZE*sizeof(int*)); for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define ONE_DIM 1 #define TWO_DIM 2 #define SIZE 5 one_dimension_array(int rank, int val_V, int val_scale) { int g_V, *local_V=NULL, dims_V=SIZE; int lo=SIZE-SIZE, hi=SIZE-1, i, ld=SIZE; local_V=(int*)malloc(SIZE*sizeof(int)); g_V = NGA_Create(C_INT, ONE_DIM, &dims_V, "array_A", NULL); GA_Fill(g_V, &val_V); GA_Scale(g_V, &val_scale); GA_Print(g_V); NGA_Get(g_V, &lo, &hi, local_V, &ld); if(rank==1) { for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs; int g_A, g_V, val2=3, local_A[SIZE][SIZE], dims_V=SIZE, local_V[dims_V], local_Ar[SIZE][SIZE]; int dims[DIM]={SIZE,SIZE}, dims2[DIM], lo[DIM]={0,0}, hi[DIM]={4,4}, ld=5, i, j; int loV=0, hiV=dims_V-1, arlo[DIM]={0,0}, arhi[DIM]={4,4}; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs; int g_A, g_V, val1=5, val2=5, local_A[SIZE][SIZE], dims_V=SIZE; int dims[DIM]={SIZE,SIZE}, dims2[DIM], lo[DIM]={1,1}, hi[DIM]={2,2}, ld=5, i, j; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_V = NGA_Create(C_INT, 1, &dims_V, "array_A", NULL); GA_Fill(g_A, &val1); GA_Print(g_A); printf("\n"); GA_Scale(g_A, &val2); GA_Print(g_A); GA_Get_diag(g_A, g_V); GA_Print(g_V); NGA_Get(g_A, lo, hi, local_A, &ld); if(rank==1) { for(i=0; i<2; i++) { for(j=0; j<2; j++) if(local_A[i][j]!=val1*val2)printf(" GA Error: \n"); } } if(rank == 0) printf("Test Completed \n"); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_scale_rows.c000066400000000000000000000023541500715745200221400ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Scale_rows (is a collective operation) * GA_Scale -- used to scale the global array value with constant- it to manipulate the value -- * * GA_scale_rows -- */ #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs; int g_A, g_V, val1=5, val2=5, local_A[SIZE][SIZE], dims_V=SIZE, local_V[dims_V]; int dims[DIM]={SIZE,SIZE}, dims2[DIM], lo[DIM]={1,1}, hi[DIM]={2,2}, ld=5, i, j; int loV=0, hiV=dims_V-1; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_V = NGA_Create(C_INT, 1, &dims_V, "array_A", NULL); GA_Fill(g_A, &val1); GA_Print(g_A); printf("\n"); GA_Scale(g_A, &val2); GA_Print(g_A); GA_Get_diag(g_A, g_V); GA_Print(g_V); NGA_Get(g_A, lo, hi, local_A, &ld); NGA_Get(g_V, &loV, &hiV, local_V, &ld); if(rank==1) { for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #define N 5 #define D 2 int main(int argc, char **argv) { int rank, nprocs; int g_A, dims[D]={5,10}, local_A[N], sub_array[N][D], **s_array=NULL; int i, j, value=0; MPI_Init(&argc, &argv); GA_Initialize(); MA_init(C_INT, 1000, 1000); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); s_array=(int**)malloc(N*sizeof(int*)); for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define SIZE 5 #define DIM 2 int main(int argc, char **argv) { int rank, nprocs; int g_A; int dims[DIM]={SIZE,SIZE}, ndim; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A=GA_Create_handle(); if(!g_A) GA_ERROR_MSG(); GA_Set_data(g_A, DIM, dims, C_INT); // printf("%d \n", GA_Ndim(g_A)); /* if(rank == 0) { ndim=GA_Ndim(g_A); if(ndim == 2) printf(" An Error \n"); } */ GA_Sync(); if(rank == 0) GA_PRINT_MSG(); GA_Terminate(); MPI_Finalize(); return 0; } ga-5.9.2/global/testing/unit-tests/ga_set_diagonal.c000066400000000000000000000021461500715745200224270ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Set_diagonal (is a collective operation) * GA_Create -- used to create a global array using handles like 'g_A' * handle G_V -- represents the array of single dimension * */ #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 #define GSIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_V, g_V2; int dims[DIM]={GSIZE,GSIZE}, ld=5; int val1=5, val2=4, val3=25, dims_V=GSIZE; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_V = NGA_Create(C_INT, 1, &dims_V, "array_V", NULL); g_V2 = NGA_Create(C_INT, 1, &dims_V, "array_V2", NULL); GA_Fill(g_A, &val1); GA_Fill(g_V, &val2); GA_Set_diagonal(g_A, g_V); GA_Add_diagonal(g_A, g_V); GA_Get_diag(g_A, g_V2); GA_Print(g_A); GA_Print(g_V); GA_Print(g_V2); GA_Sync(); if(rank == 0) GA_PRINT_MSG(); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_set_restricted.c000066400000000000000000000056421500715745200230250ustar00rootroot00000000000000/* * Test Program for GA * GA_Set_restricted -- */ #include #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define SIZE 5 #define DIM 2 list_by_two(int nprocs) { int i, l_size, mod, l_size_mod, *list_one=NULL, *list_two=NULL; l_size = nprocs / 2; mod = nprocs % 2; l_size_mod = l_size + mod; list_one = (int*)malloc(l_size*sizeof(int)); list_two = (int*)malloc(l_size*sizeof(int)); for(i=0; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define SIZE 10 #define MAX_DIM 7 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B; int dims[MAX_DIM], val=4, ndim, re; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); for(i=1; i<=MAX_DIM; i++) { ndim=i; dims[i]=SIZE; // for(j=0; j0) printf("Cholesky Fact couldn't be completed \n"); else printf("An Error occured\n"); if(rank == 0) GA_PRINT_MSG(); GA_Destroy(g_A); GA_Destroy(g_B); GA_Terminate(); MPI_Finalize(); } /* * TO-DO : assign SIZE to a bigger value */ ga-5.9.2/global/testing/unit-tests/ga_sync.c000066400000000000000000000013771500715745200207570ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Sync (is a collective operation) * */ #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 int main(int argc, char **argv) { int rank, nprocs, i; int g_A, value=5; int dims[DIM]={5,5}, dims2[DIM], ndim, type; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); GA_Fill(g_A, &value); GA_Print(g_A); printf(" -----------%d\n", rank); GA_Sync(); printf(" %d-----------\n", rank); GA_Print(g_A); if(rank == 0) printf("Test Completed \n"); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_transpose.c000066400000000000000000000031511500715745200220110ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Transpose (is a collective operation) * GA_Create -- used to create a global array using handles like 'g_A' * _Transpose -- used to transpose the whole array * * not completed --- */ #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, value=5, local_GA[SIZE][SIZE], local_A[SIZE][SIZE], local_B[SIZE][SIZE]; int dims[DIM]={SIZE,SIZE}, dims2[DIM], lo[DIM]={0,0}, hi[DIM]={4,4}, ld=5; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_B = NGA_Create(C_INT, DIM, dims, "array_B", NULL); for(i=0; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 #define SIZE 5 validate_transpose(int local_A[][SIZE], int local_B[][SIZE]) { int i, j; for(i=1; i #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 #define SIZE 5 validate_transpose(int g_A, int g_B, int* lo, int* hi, int ld) { int i, j; int local_A[SIZE][SIZE], local_B[SIZE][SIZE]; // int **local_A=NULL, **local_B=NULL; /* local_A=(int**)malloc(SIZE*sizeof(int*)); for(i=0; i 0) { printf("%d", array[0]); } for (i=1; i #include"mpi.h" #include"ga.h" #include"macdecls.h" #define DIM 2 #define GSIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, g_V, local_A[5][5], local_B[5][5], vsize=GSIZE; int dims[DIM]={GSIZE,GSIZE}, alo[DIM]={1,1}, ahi[DIM]={3,2}, blo[DIM]={1,1}, bhi[DIM]={2,3}, ld=5; int value=5, val2=4; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); g_V = NGA_Create(C_INT, 1, &vsize, "array_V", NULL); GA_Fill(g_A, &value); g_B = GA_Duplicate(g_A, "array_B"); GA_Zero_diagonal(g_A); GA_Print(g_A); GA_Fill(g_B, &val2); GA_Sync(); NGA_Zero_patch(g_B, blo, bhi); GA_Fill(g_V, &val2); GA_Print(g_B); GA_Print(g_V); //------------------------------------------------ /* */ //------------------------------------------------ GA_Sync(); if(rank == 1) printf("Test Completed \n"); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_zeropatch.c000066400000000000000000000034241500715745200217750ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Zero_patch (is a collective operation) * GA_Create -- used to create a global array using handles like 'g_A' * GA_Duplicate --used to duplicate and generate one more global array.., handle 'g_A' to 'g_B' * GA_Zero_patch -- is used to pass zero-value only certain patch of an array */ #include #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, local_A[SIZE][SIZE], local_B[SIZE][SIZE]; int dims[DIM]={5,5}, alo[DIM]={1,1}, ahi[DIM]={3,2}, blo[DIM]={1,1}, bhi[DIM]={2,3}, ld=5; int value=5, val2=4; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); GA_Fill(g_A, &value); g_B = GA_Duplicate(g_A, "array_B"); GA_Print(g_A); GA_Fill(g_B, &val2); GA_Sync(); NGA_Zero_patch(g_B, blo, bhi); GA_Print(g_B); NGA_Get(g_A, alo, ahi, local_A, &ld); NGA_Get(g_B, blo, bhi, local_B, &ld); if(rank==0) { for(i=0; i<3; i++) { for(j=0; j<2; j++) printf("%d ", local_A[i][j]); printf("\n"); } printf("\n"); for(i=0; i<2; i++) { for(j=0; j<3; j++) printf("%d ", local_B[i][j]); printf("\n"); } for(i=0; i<2; i++) { for(j=0; j<3; j++) if( local_A[i][j]!=local_B[i][j]) printf("ERROR : \n"); } } // The process is confirmed and verified by printing the array in OP-scr /* if(rank==0) if(content(g_A) != content(g_B))printf("ERROE : \n"); */ GA_Sync(); if(rank == 1) printf("Test Completed \n"); GA_Terminate(); MPI_Finalize(); } ga-5.9.2/global/testing/unit-tests/ga_zeropatch2.c000066400000000000000000000034661500715745200220650ustar00rootroot00000000000000/* * Test Program for GA * This is to test GA_Zero_patch (is a collective operation) * GA_Create -- used to create a global array using handles like 'g_A' * GA_Duplicate --used to duplicate and generate one more global array.., handle 'g_A' to 'g_B' * GA_Zero_patch -- is used to pass zero-value only certain patch of an array */ #include #include #include"mpi.h" #include"ga.h" #include"macdecls.h" #include"ga_unit.h" #define DIM 2 #define SIZE 5 int main(int argc, char **argv) { int rank, nprocs, i, j; int g_A, g_B, local_A[SIZE][SIZE], local_B[SIZE][SIZE]; int dims[DIM]={SIZE,SIZE}, alo[DIM]={0,0}, ahi[DIM]={SIZE-1,SIZE-1}, blo[DIM]={0,0}, bhi[DIM]={4,4}, ld=5; int value=5, val2=4; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &nprocs); MA_init(C_INT, 1000, 1000); GA_Initialize(); g_A = NGA_Create(C_INT, DIM, dims, "array_A", NULL); GA_Fill(g_A, &value); g_B = GA_Duplicate(g_A, "array_B"); GA_Print(g_A); GA_Fill(g_B, &val2); GA_Sync(); NGA_Zero_patch(g_B, blo, bhi); GA_Print(g_B); NGA_Get(g_A, alo, ahi, local_A, &ld); NGA_Get(g_B, blo, bhi, local_B, &ld); if(rank==0) { for(i=0; i #include #include #include #include "mock.h" #include "abstract_ops.h" void Mock_Abs_value(mock_ga_t *g_a) { ITER_DECLARE_VARS(g_a) switch (g_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ ITER_DECLARE_BUFFER(g_a,C_TYPE) \ ITER_INIT(g_a,C_TYPE) \ ITER_BEGIN(g_a,C_TYPE) \ assign_abs_##AT(*g_a_buf,*g_a_buf); \ ITER_NEXT(g_a) \ ITER_END \ break; \ } #include "types.xh" #undef TYPE_CASE } } void Mock_Abs_value_patch(mock_ga_t *g_a, int *lo, int *hi) { ITER_DECLARE_VARS_PATCH(g_a) switch (g_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ ITER_DECLARE_BUFFER(g_a,C_TYPE) \ ITER_INIT_PATCH(g_a,C_TYPE,lo,hi) \ ITER_BEGIN(g_a,C_TYPE) \ assign_abs_##AT(*g_a_buf,*g_a_buf); \ ITER_NEXT_PATCH(g_a) \ ITER_END \ break; \ } #include "types.xh" #undef TYPE_CASE } } void Mock_Access_block_grid(mock_ga_t *g_a, int index[], void *ptr, int ld[]) { } void Mock_Access_block(mock_ga_t *g_a, int idx, void *ptr, int ld[]) { } void Mock_Access_block_segment(mock_ga_t *g_a, int proc, void *ptr, int *len) { } void Mock_Access_ghost_element(mock_ga_t *g_a, void *ptr, int subscript[], int ld[]) { } void Mock_Access_ghosts(mock_ga_t *g_a, int dims[], void *ptr, int ld[]) { } void Mock_Access(mock_ga_t *g_a, int lo[], int hi[], void *ptr, int ld[]) { } void Mock_Acc(mock_ga_t *g_a, int lo[], int hi[],void* buf, int ld[],void* alpha) { } void Mock_Add_constant(mock_ga_t *g_a, void* alpha) { ITER_DECLARE_VARS(g_a) switch (g_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ C_TYPE value = *((C_TYPE*)alpha); \ ITER_DECLARE_BUFFER(g_a,C_TYPE) \ ITER_INIT(g_a,C_TYPE) \ ITER_BEGIN(g_a,C_TYPE) \ add_assign_##AT(*g_a_buf,value); \ ITER_NEXT(g_a) \ ITER_END \ break; \ } #include "types.xh" #undef TYPE_CASE } } void Mock_Add_constant_patch(mock_ga_t *g_a, int *lo, int *hi,void *alpha) { /* ITER_DECLARE_VARS_PATCH(g_a) switch (g_a->type){ #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ C_TYPE value = *((C_TYPE*)alpha); \ ITER_DECLARE_BUFFER(g_a,C_TYPE) \ ITER_INIT(g_a,C_TYPE) \ ITER_BEGIN(g_a,C_TYPE) \ add_assign_##AT(*g_a_buf,value); \ ITIR_NEXT_PATCH(g_a) \ ITER_END \ break; \ } #include "types.xh" #undef TYPE_CASE } */ } void Mock_Add_diagonal(mock_ga_t *g_a, int g_v) { } void Mock_Add_patch(void * alpha, mock_ga_t *g_a, int alo[], int ahi[], void * beta, mock_ga_t *g_b, int blo[], int bhi[], mock_ga_t *g_c, int clo[], int chi[]) { } void Mock_Add(void *alpha, mock_ga_t *g_a, void* beta, mock_ga_t *g_b, mock_ga_t *g_c) { } int Mock_Allocate(mock_ga_t *g_a) { } int Mock_Assemble_duplicate(mock_ga_t *g_a, char *name, void *ptr) { } void Mock_Brdcst(void *buf, int lenbuf, int root) { } SingleComplex Mock_Cdot(mock_ga_t *g_a, mock_ga_t *g_b) { } SingleComplex Mock_Cdot_patch(mock_ga_t *g_a, char t_a, int alo[], int ahi[], mock_ga_t *g_b, char t_b, int blo[], int bhi[]) { } void Mock_Cgemm(char ta, char tb, int m, int n, int k, SingleComplex alpha, mock_ga_t *g_a, mock_ga_t *g_b, SingleComplex beta, mock_ga_t *g_c ) { } void Mock_Cgop(SingleComplex x[], int n, char *op) { } void Mock_Check_handle(mock_ga_t *g_a, char *string) { } int Mock_Cluster_nnodes(void) { } int Mock_Cluster_nodeid(void) { } int Mock_Cluster_nprocs(int x) { } int Mock_Cluster_procid(int x, int y) { } int Mock_Cluster_proc_nodeid(int proc) { } int Mock_Compare_distr(mock_ga_t *g_a, mock_ga_t *g_b) { } void Mock_Copy(mock_ga_t *g_a, mock_ga_t *g_b) { } void Mock_Copy_patch(char trans, mock_ga_t *g_a, int alo[], int ahi[], mock_ga_t *g_b, int blo[], int bhi[]) { } mock_ga_t* Mock_Create_config(int type, int ndim, int dims[], char *name, int chunk[], int p_handle) { return Mock_Create(type, ndim, dims, name, chunk); } mock_ga_t* Mock_Create_ghosts_config(int type, int ndim, int dims[], int width[], char *name, int chunk[], int p_handle) { return Mock_Create(type, ndim, dims, name, chunk); } mock_ga_t* Mock_Create_ghosts(int type, int ndim, int dims[], int width[], char *name, int chunk[]) { return Mock_Create(type, ndim, dims, name, chunk); } mock_ga_t* Mock_Create_ghosts_irreg_config(int type, int ndim, int dims[], int width[], char *name, int map[], int nblock[], int p_handle) { return Mock_Create(type, ndim, dims, name, NULL); } mock_ga_t* Mock_Create_ghosts_irreg(int type, int ndim, int dims[], int width[], char *name, int map[], int nblock[]) { return Mock_Create(type, ndim, dims, name, NULL); } mock_ga_t* Mock_Create_handle(void) { return (mock_ga_t*)malloc(sizeof(mock_ga_t)); } mock_ga_t* Mock_Create(int type, int ndim, int dims[], char *name, int chunk[]) { int i; int nd_m1 = ndim-1; mock_ga_t *g_a = (mock_ga_t*)malloc(sizeof(mock_ga_t)); g_a->type = type; g_a->ndim = ndim; g_a->nd_m1 = nd_m1; g_a->size = 1; g_a->itemsize = (int)type_to_size(type); memset(g_a->dims, 0, sizeof(int)*GA_MAX_DIM); memset(g_a->dims_m1, 0, sizeof(int)*GA_MAX_DIM); memset(g_a->strides, 0, sizeof(int)*GA_MAX_DIM); memset(g_a->backstrides, 0, sizeof(int)*GA_MAX_DIM); for (i=0; idims[i] = dims[i]; g_a->size *= dims[i]; } /* setup important iterator variables */ for (i=nd_m1; i>=0; --i) { g_a->dims_m1[i] = dims[i]-1; g_a->strides[i] = (i == nd_m1) ? 1 : g_a->strides[i+1]*g_a->dims[i+1]; g_a->backstrides[i] = g_a->dims_m1[i]*g_a->strides[i]; } #if 0 /* scale the back/strides based on itemsize */ for (i=0; istrides[i] *= g_a->itemsize; g_a->backstrides[i] *= g_a->itemsize; } #endif g_a->buf = malloc(g_a->size * g_a->itemsize); #if 0 printf("Mock_Create(%d, %d, dims, %s, chunk)\n", type, ndim, name); aprint("dims", g_a->dims, g_a->ndim); aprint("dims_m1", g_a->dims_m1, g_a->ndim); aprint("strides", g_a->strides, g_a->ndim); aprint("backstrides", g_a->backstrides, g_a->ndim); #endif return g_a; } mock_ga_t* Mock_Create_irreg_config(int type, int ndim, int dims[],char *name, int block[], int map[], int p_handle) { return Mock_Create(type, ndim, dims, name, NULL); } mock_ga_t* Mock_Create_irreg(int type, int ndim, int dims[],char *name, int block[], int map[]) { return Mock_Create(type, ndim, dims, name, NULL); } int Mock_Create_mutexes(int number) { } double Mock_Ddot(mock_ga_t *g_a, mock_ga_t *g_b) { } double Mock_Ddot_patch(mock_ga_t *g_a, char t_a, int alo[], int ahi[], mock_ga_t *g_b, char t_b, int blo[], int bhi[]) { } int Mock_Deregister_type(int type) { } void Mock_Destroy(mock_ga_t *g_a) { } int Mock_Destroy_mutexes(void) { } void Mock_Dgemm(char ta, char tb, int m, int n, int k, double alpha, mock_ga_t *g_a, mock_ga_t *g_b, double beta, mock_ga_t *g_c ) { } void Mock_Dgop(double x[], int n, char *op) { } void Mock_Diag(mock_ga_t *g_a, int g_s, int g_v, void *eval) { } void Mock_Diag_reuse(int reuse, mock_ga_t *g_a, int g_s, int g_v, void *eval) { } void Mock_Diag_seq(mock_ga_t *g_a, int g_s, int g_v, void *eval) { } void Mock_Diag_std(mock_ga_t *g_a, int g_v, void *eval) { } void Mock_Diag_std_seq(mock_ga_t *g_a, int g_v, void *eval) { } void Mock_Distribution(mock_ga_t *g_a, int iproc, int lo[], int hi[]) { } mock_ga_t* Mock_Duplicate(mock_ga_t *g_a, char* array_name) { } void Mock_Elem_divide(mock_ga_t *g_a, mock_ga_t *g_b, mock_ga_t *g_c) { /* TODO FIXME: wasn't compiling */ #if 0 ITER_DECLARE_VARS(g_a) ITER_DECLARE_VARS(g_b) ITER_DECLARE_VARS(g_c) switch (g_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ ITER_DECLARE_BUFFER(g_a,C_TYPE) \ ITER_DECLARE_BUFFER(g_b,C_TYPE) \ ITER_DECLARE_BUFFER(g_c,C_TYPE) \ ITER_INIT(g_a,C_TYPE) \ ITER_INIT(g_b,C_TYPE) \ ITER_INIT(g_c,C_TYPE) \ ITER_BEGIN(g_a,C_TYPE) \ assign_div_##AT(*g_c_buf,*g_a_buf,*g_b_buf); \ ITER_NEXT(g_a) \ ITER_NEXT(g_b) \ ITER_NEXT(g_c) \ ITER_END \ break; \ } #include "types.xh" #undef TYPE_CASE } #endif } void Mock_Elem_divide_patch(mock_ga_t *g_a, int *alo, int *ahi, mock_ga_t *g_b, int *blo, int *bhi,mock_ga_t *g_c, int *clo, int *chi) { } void Mock_Elem_maximum(mock_ga_t *g_a, mock_ga_t *g_b, mock_ga_t *g_c) { ITER_DECLARE_VARS(g_a) ITER_DECLARE_VARS(g_b) ITER_DECLARE_VARS(g_c) switch (g_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ ITER_DECLARE_BUFFER(g_a,C_TYPE) \ ITER_DECLARE_BUFFER(g_b,C_TYPE) \ ITER_DECLARE_BUFFER(g_c,C_TYPE) \ ITER_INIT(g_a,C_TYPE) \ ITER_INIT(g_b,C_TYPE) \ ITER_INIT(g_c,C_TYPE) \ ITER_BEGIN(g_a,C_TYPE) \ assign_max_##AT(*g_c_buf,*g_a_buf,*g_b_buf); \ ITER_NEXT(g_a) \ ITER_NEXT(g_b) \ ITER_NEXT(g_c) \ ITER_END \ break; \ } #include "types.xh" #undef TYPE_CASE } } void Mock_Elem_maximum_patch(mock_ga_t *g_a, int *alo, int *ahi, mock_ga_t *g_b, int *blo, int *bhi,mock_ga_t *g_c, int *clo, int *chi) { } void Mock_Elem_minimum(mock_ga_t *g_a, mock_ga_t *g_b, mock_ga_t *g_c) { ITER_DECLARE_VARS(g_a) ITER_DECLARE_VARS(g_b) ITER_DECLARE_VARS(g_c) switch (g_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ ITER_DECLARE_BUFFER(g_a,C_TYPE) \ ITER_DECLARE_BUFFER(g_b,C_TYPE) \ ITER_DECLARE_BUFFER(g_c,C_TYPE) \ ITER_INIT(g_a,C_TYPE) \ ITER_INIT(g_b,C_TYPE) \ ITER_INIT(g_c,C_TYPE) \ ITER_BEGIN(g_a,C_TYPE) \ assign_max_##AT(*g_c_buf,*g_a_buf,*g_b_buf); \ ITER_NEXT(g_a) \ ITER_NEXT(g_b) \ ITER_NEXT(g_c) \ ITER_END \ break; \ } #include "types.xh" #undef TYPE_CASE } } void Mock_Elem_minimum_patch(mock_ga_t *g_a, int *alo, int *ahi, mock_ga_t *g_b, int *blo, int *bhi,mock_ga_t *g_c, int *clo, int *chi) { /* ITER_DECLARE_VARS_PATCH(g_a) ITER_DECLARE_VARS_PATCH(g_b) ITER_DECLARE_VARS_PATCH(g_c) switch (g_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ ITER_DECLARE_BUFFER(g_a,C_TYPE) \ ITER_DECLARE_BUFFER(g_b,C_TYPE) \ ITER_DECLARE_BUFFER(g_c,C_TYPE) \ ITER_BEGIN(g_a,C_TYPE,lo,hi) \ assign_abs_##AT(*g_a_buf,*g_a_buf); \ ITER_NEXT_PATCH(g_a) \ ITER_END \ break; \ } #include "types.xh" #undef TYPE_CASE } */ } void Mock_Elem_multiply(mock_ga_t *g_a, mock_ga_t *g_b, mock_ga_t *g_c) { ITER_DECLARE_VARS(g_a) ITER_DECLARE_VARS(g_b) ITER_DECLARE_VARS(g_c) switch (g_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ ITER_DECLARE_BUFFER(g_a,C_TYPE) \ ITER_DECLARE_BUFFER(g_b,C_TYPE) \ ITER_DECLARE_BUFFER(g_c,C_TYPE) \ ITER_INIT(g_a,C_TYPE) \ ITER_INIT(g_b,C_TYPE) \ ITER_INIT(g_c,C_TYPE) \ ITER_BEGIN(g_a,C_TYPE) \ assign_mul_##AT(*g_c_buf,*g_a_buf,*g_b_buf); \ ITER_NEXT(g_a) \ ITER_NEXT(g_b) \ ITER_NEXT(g_c) \ ITER_END \ break; \ } #include "types.xh" #undef TYPE_CASE } } void Mock_Elem_multiply_patch(mock_ga_t *g_a, int *alo, int *ahi, mock_ga_t *g_b, int *blo, int *bhi,mock_ga_t *g_c, int *clo, int *chi) { } void Mock_Error(char *str, int code) { } float Mock_Fdot(mock_ga_t *g_a, mock_ga_t *g_b) { } float Mock_Fdot_patch(mock_ga_t *g_a, char t_a, int alo[], int ahi[], mock_ga_t *g_b, char t_b, int blo[], int bhi[]) { } void Mock_Fence(void) { } void Mock_Fgop(float x[], int n, char *op) { } void Mock_Fill(mock_ga_t *g_a, void *value) { ITER_DECLARE_VARS(g_a) switch (g_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ ITER_DECLARE_BUFFER(g_a,C_TYPE) \ ITER_INIT(g_a,C_TYPE) \ ITER_BEGIN(g_a,C_TYPE) \ assign_##AT(*g_a_buf,*g_a_buf); \ ITER_NEXT(g_a) \ ITER_END \ break; \ } #include "types.xh" #undef TYPE_CASE } } void Mock_Fill_patch(mock_ga_t *g_a, int lo[], int hi[], void *val) { } void Mock_Freemem(void* ptr) { } void Mock_Gather_flat(mock_ga_t *g_a, void *v, int subsArray[], int n) { } void Mock_Gather(mock_ga_t *g_a, void *v, int* subsArray[], int n) { } void Mock_Get_block_info(mock_ga_t *g_a, int num_blocks[], int block_dims[]) { } int Mock_Get_debug(void) { } void Mock_Get_diag(mock_ga_t *g_a, int g_v) { } int Mock_Get_dimension(mock_ga_t *g_a) { } void Mock_Get_field(mock_ga_t *g_a, int *lo, int *hi, int foff, int fsize, void *buf, int *ld) { } void Mock_Get_ghost_block(mock_ga_t *g_a, int lo[], int hi[], void *buf, int ld[]) { } void Mock_Get(mock_ga_t *g_a, int lo[], int hi[], void* buf, int ld[]) { } void* Mock_Getmem(int type, int nelem, int grp_id) { } int Mock_Get_pgroup(mock_ga_t *g_a) { } int Mock_Get_pgroup_size(int grp_id) { } void Mock_Get_proc_grid(mock_ga_t *g_a, int dims[]) { } void Mock_Get_proc_index(mock_ga_t *g_a, int iproc, int subscript[]) { } void Mock_Gop(int type, void *x, int n, char *op) { } int Mock_Has_ghosts(mock_ga_t *g_a) { } int Mock_Idot(mock_ga_t *g_a, mock_ga_t *g_b) { } int Mock_Idot_patch(mock_ga_t *g_a, char t_a, int alo[], int ahi[], mock_ga_t *g_b, char t_b, int blo[], int bhi[]) { } void Mock_Igop(int x[], int n, char *op) { } void Mock_Init_fence(void) { } void Mock_Initialize_args(int *argc, char ***argv) { } void Mock_Initialize_ltd(size_t limit) { } void Mock_Initialize(void) { } void Mock_Inquire(mock_ga_t *g_a, int *type, int *ndim, int dims[]) { } size_t Mock_Inquire_memory(void) { } char* Mock_Inquire_name(mock_ga_t *g_a) { } int Mock_Is_mirrored(mock_ga_t *g_a) { } long Mock_Ldot(mock_ga_t *g_a, mock_ga_t *g_b) { } long Mock_Ldot_patch(mock_ga_t *g_a, char t_a, int alo[], int ahi[], mock_ga_t *g_b, char t_b, int blo[], int bhi[]) { } void Mock_Lgop(long x[], int n, char *op) { } void Mock_List_nodeid(int *list, int nprocs) { } long long Mock_Lldot(mock_ga_t *g_a, mock_ga_t *g_b) { } long long Mock_Lldot_patch(mock_ga_t *g_a, char t_a, int alo[], int ahi[], mock_ga_t *g_b, char t_b, int blo[], int bhi[]) { } void Mock_Llgop(long long x[], int n, char *op) { } int Mock_Llt_solve(mock_ga_t *g_a, mock_ga_t *g_b) { } int Mock_Locate(mock_ga_t *g_a, int subscript[]) { } int Mock_Locate_nnodes(mock_ga_t *g_a, int lo[], int hi[]) { } int Mock_Locate_num_blocks(mock_ga_t *g_a, int lo[], int hi[]) { } int Mock_Locate_region(mock_ga_t *g_a, int lo[], int hi[], int map[], int procs[]) { } void Mock_Lock(int mutex) { } void Mock_Lu_solve(char tran, mock_ga_t *g_a, mock_ga_t *g_b) { } void Mock_Mask_sync(int first, int last) { } void Mock_Matmul_patch_2d(char transa, char transb, void* alpha, void *beta, mock_ga_t *g_a, int ailo, int aihi, int ajlo, int ajhi, mock_ga_t *g_b, int bilo, int bihi, int bjlo, int bjhi, mock_ga_t *g_c, int cilo, int cihi, int cjlo, int cjhi) { } void Mock_Matmul_patch(char transa, char transb, void* alpha, void *beta, mock_ga_t *g_a, int alo[], int ahi[], mock_ga_t *g_b, int blo[], int bhi[], mock_ga_t *g_c, int clo[], int chi[]) { } void Mock_Median(mock_ga_t *g_a, mock_ga_t *g_b, mock_ga_t *g_c, int g_m) { } void Mock_Median_patch(mock_ga_t *g_a, int *alo, int *ahi, mock_ga_t *g_b, int *blo, int *bhi, mock_ga_t *g_c, int *clo, int *chi, int g_m, int *mlo, int *mhi) { } size_t Mock_Memory_avail(void) { } int Mock_Memory_limited(void) { } void Mock_Merge_distr_patch(mock_ga_t *g_a, int alo[], int ahi[], mock_ga_t *g_b, int blo[], int bhi[]) { } void Mock_Merge_mirrored(mock_ga_t *g_a) { } void Mock_NbAcc(mock_ga_t *g_a, int lo[], int hi[],void* buf, int ld[],void* alpha, ga_nbhdl_t* nbhandle) { } void Mock_Nbget_field(mock_ga_t *g_a, int *lo, int *hi, int foff, int fsize,void *buf, int *ld, ga_nbhdl_t *nbhandle) { } void Mock_NbGet_ghost_dir(mock_ga_t *g_a, int mask[], ga_nbhdl_t* handle) { } void Mock_NbGet(mock_ga_t *g_a, int lo[], int hi[], void* buf, int ld[], ga_nbhdl_t* nbhandle) { } void Mock_Nblock(mock_ga_t *g_a, int *nblock) { } void Mock_Nbput_field(mock_ga_t *g_a, int *lo, int *hi, int foff, int fsize, void *buf, int *ld, ga_nbhdl_t *nbhandle) { } void Mock_NbPut(mock_ga_t *g_a, int lo[], int hi[], void* buf, int ld[], ga_nbhdl_t* nbhandle) { } int Mock_NbTest(ga_nbhdl_t* nbhandle) { } void Mock_NbWait(ga_nbhdl_t* nbhandle) { } int Mock_Ndim(mock_ga_t *g_a) { } int Mock_Nnodes(void) { } int Mock_Nodeid(void) { } void Mock_Norm1(mock_ga_t *g_a, double *nm) { } void Mock_Norm_infinity(mock_ga_t *g_a, double *nm) { } void Mock_Periodic_acc(mock_ga_t *g_a, int lo[], int hi[],void* buf, int ld[],void* alpha) { } void Mock_Periodic_get(mock_ga_t *g_a, int lo[], int hi[], void* buf, int ld[]) { } void Mock_Periodic_put(mock_ga_t *g_a, int lo[], int hi[], void* buf, int ld[]) { } int Mock_Pgroup_absolute_id(int pgroup, int pid) { } void Mock_Pgroup_brdcst(int grp, void *buf, int lenbuf, int root) { } void Mock_Pgroup_cgop(int grp, SingleComplex x[], int n, char *op) { } int Mock_Pgroup_create(int *list, int count) { } int Mock_Pgroup_destroy(int grp) { } void Mock_Pgroup_dgop(int grp, double x[], int n, char *op) { } void Mock_Pgroup_fgop(int grp, float x[], int n, char *op) { } int Mock_Pgroup_get_default(void) { } int Mock_Pgroup_get_mirror(void) { } int Mock_Pgroup_get_world(void) { } void Mock_Pgroup_igop(int grp, int x[], int n, char *op) { } void Mock_Pgroup_lgop(int grp, long x[], int n, char *op) { } void Mock_Pgroup_llgop(int grp, long long x[], int n, char *op) { } int Mock_Pgroup_nnodes(int grp_id) { } int Mock_Pgroup_nodeid(int grp_id) { } void Mock_Pgroup_set_default(int p_handle) { } int Mock_Pgroup_split(int grp_id, int num_group) { } int Mock_Pgroup_split_irreg(int grp_id, int color) { } void Mock_Pgroup_sync(int grp_id) { } void Mock_Pgroup_zgop(int grp, DoubleComplex x[], int n, char *op) { } void Mock_Print_distribution(mock_ga_t *g_a) { } void Mock_Print_file(FILE *file, mock_ga_t *g_a) { } /* just for debugging purposes */ void Mock_Print(mock_ga_t *g_a) { switch (g_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ int i,j; \ int coords[GA_MAX_DIM] = {0}; \ C_TYPE *ptr = (C_TYPE*)(g_a->buf); \ for (i=0; isize; ++i) { \ printf("%d (%d", i, coords[0]); \ for (j=1; jndim; ++j) { \ printf(",%d", coords[j]); \ } \ printf(") " AT "\n", ptr[i]); \ for (j=g_a->nd_m1; j>=0; --j) { \ if (coords[j] < g_a->dims_m1[j]) { \ ++coords[j]; \ break; \ } \ else { \ coords[j] = 0; \ } \ } \ } \ break; \ } TYPE_CASE(C_INT,int,"%d") TYPE_CASE(C_LONG,long,"%ld") TYPE_CASE(C_LONGLONG,long long,"%lld") TYPE_CASE(C_FLOAT,float,"%f") TYPE_CASE(C_DBL,double,"%f") #undef TYPE_CASE #define TYPE_CASE(GA_TYPE,C_TYPE) \ case GA_TYPE: \ { \ int i,j; \ int coords[GA_MAX_DIM] = {0}; \ C_TYPE *ptr = (C_TYPE*)(g_a->buf); \ for (i=0; isize; ++i) { \ printf("%d (%d", i, coords[0]); \ for (j=1; jndim; ++j) { \ printf(",%d", coords[j]); \ } \ printf(") %f+%fi\n", ptr[i].real, ptr[i].imag); \ for (j=g_a->nd_m1; j>=0; --j) { \ if (coords[j] <= g_a->dims_m1[j]) { \ ++coords[j]; \ break; \ } \ else { \ coords[j] = 0; \ } \ } \ } \ break; \ } TYPE_CASE(C_SCPL,SingleComplex) TYPE_CASE(C_DCPL,DoubleComplex) #undef TYPE_CASE } } void Mock_Print_patch_2d(mock_ga_t *g_a, int ilo, int ihi, int jlo, int jhi, int pretty) { } void Mock_Print_patch(mock_ga_t *g_a, int lo[], int hi[], int pretty) { } void Mock_Print_stats(void) { } void Mock_Proc_topology(mock_ga_t *g_a, int proc, int coord[]) { } void Mock_Put_field(mock_ga_t *g_a, int *lo, int *hi, int foff, int fsize, void *buf, int *ld) { } void Mock_Put(mock_ga_t *g_a, int lo[], int hi[], void* buf, int ld[]) { } void Mock_Randomize(mock_ga_t *g_a, void *value) { switch (g_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ int i; \ C_TYPE *ptr = (C_TYPE*)(g_a->buf); \ C_TYPE val = *((C_TYPE*)value); \ for (i=0; isize; ++i) { \ assign_rand_##AT(ptr[i],val); \ } \ break; \ } #include "types.xh" #undef TYPE_CASE } } long Mock_Read_inc(mock_ga_t *g_a, int subscript[], long inc) { } void Mock_Recip(mock_ga_t *g_a) { } void Mock_Recip_patch(mock_ga_t *g_a, int *lo, int *hi) { } void Mock_Register_stack_memory(void * (*ext_alloc)(size_t, int, char *), void (*ext_free)(void *)) { } int Mock_Register_type(size_t bytes) { } void Mock_Release_block_grid(mock_ga_t *g_a, int index[]) { } void Mock_Release_block(mock_ga_t *g_a, int idx) { } void Mock_Release_block_segment(mock_ga_t *g_a, int idx) { } void Mock_Release_ghost_element(mock_ga_t *g_a, int index[]) { } void Mock_Release_ghosts(mock_ga_t *g_a) { } void Mock_Release(mock_ga_t *g_a, int lo[], int hi[]) { } void Mock_Release_update_block_grid(mock_ga_t *g_a, int index[]) { } void Mock_Release_update_block(mock_ga_t *g_a, int idx) { } void Mock_Release_update_block_segment(mock_ga_t *g_a, int idx) { } void Mock_Release_update_ghost_element(mock_ga_t *g_a, int index[]) { } void Mock_Release_update_ghosts(mock_ga_t *g_a) { } void Mock_Release_update(mock_ga_t *g_a, int lo[], int hi[]) { } void Mock_Scale_cols(mock_ga_t *g_a, int g_v) { } void Mock_Scale(mock_ga_t *g_a, void *value) { /* ITER_DECLARE_VARS(g_a) switch (g_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ C_TYPE value = *((C_TYPE*)alpha); \ ITER_DECLARE_BUFFER(g_a,C_TYPE) \ ITER_INIT(g_a,C_TYPE) \ ITER_BEGIN(g_a,C_TYPE) \ add_assign_##AT(*g_a_buf,value); \ ITER_NEXT(g_a) \ ITER_END \ break; \ } #include "types.xh" #undef TYPE_CASE } */ } void Mock_Scale_patch(mock_ga_t *g_a, int lo[], int hi[], void *alpha) { } void Mock_Scale_rows(mock_ga_t *g_a, int g_v) { } void Mock_Scan_add(mock_ga_t *g_a, mock_ga_t *g_b, int g_sbit, int lo, int hi, int excl) { } void Mock_Scan_copy(mock_ga_t *g_a, mock_ga_t *g_b, int g_sbit, int lo, int hi) { } void Mock_Scatter_acc_flat(mock_ga_t *g_a, void *v, int subsArray[], int n, void *alpha) { } void Mock_Scatter_acc(mock_ga_t *g_a, void *v, int* subsArray[], int n, void *alpha) { } void Mock_Scatter_flat(mock_ga_t *g_a, void *v, int subsArray[], int n) { } void Mock_Scatter(mock_ga_t *g_a, void *v, int* subsArray[], int n) { } void Mock_Select_elem(mock_ga_t *g_a, char* op, void* val, int *index) { } void Mock_Set_array_name(mock_ga_t *g_a, char *name) { } void Mock_Set_block_cyclic(mock_ga_t *g_a, int dims[]) { } void Mock_Set_block_cyclic_proc_grid(mock_ga_t *g_a, int block[], int proc_grid[]) { } void Mock_Set_chunk(mock_ga_t *g_a, int chunk[]) { } void Mock_Set_data(mock_ga_t *g_a, int ndim, int dims[], int type) { } void Mock_Set_debug(int flag) { } void Mock_Set_diagonal(mock_ga_t *g_a, int g_v) { } void Mock_Set_ghost_corner_flag(mock_ga_t *g_a, int flag) { } void Mock_Set_ghosts(mock_ga_t *g_a, int width[]) { } void Mock_Set_irreg_distr(mock_ga_t *g_a, int map[], int block[]) { } void Mock_Set_irreg_flag(mock_ga_t *g_a, int flag) { } void Mock_Set_memory_limit(size_t limit) { } void Mock_Set_pgroup(mock_ga_t *g_a, int p_handle) { } void Mock_Set_restricted(mock_ga_t *g_a, int list[], int size) { } void Mock_Set_restricted_range(mock_ga_t *g_a, int lo_proc, int hi_proc) { } void Mock_Sgemm(char ta, char tb, int m, int n, int k, float alpha, mock_ga_t *g_a, mock_ga_t *g_b, float beta, mock_ga_t *g_c ) { } void Mock_Shift_diagonal(mock_ga_t *g_a, void *c) { } int Mock_Solve(mock_ga_t *g_a, mock_ga_t *g_b) { } int Mock_Spd_invert(mock_ga_t *g_a) { } void Mock_Step_bound_info(int g_xx, int g_vv, int g_xxll, int g_xxuu, void *boundmin, void *wolfemin, void *boundmax) { } void Mock_Step_bound_info_patch(int g_xx, int *xxlo, int *xxhi, int g_vv, int *vvlo, int *vvhi, int g_xxll, int *xxlllo, int *xxllhi, int g_xxuu, int *xxuulo, int *xxuuhi, void *boundmin, void *wolfemin, void *boundmax) { } void Mock_Step_max(mock_ga_t *g_a, mock_ga_t *g_b, void *step) { } void Mock_Step_max_patch(mock_ga_t *g_a, int *alo, int *ahi, mock_ga_t *g_b, int *blo, int *bhi, void *step) { } void Mock_Strided_acc(mock_ga_t *g_a, int lo[], int hi[], int skip[], void* buf, int ld[], void *alpha) { } void Mock_Strided_get(mock_ga_t *g_a, int lo[], int hi[], int skip[], void* buf, int ld[]) { } void Mock_Strided_put(mock_ga_t *g_a, int lo[], int hi[], int skip[], void* buf, int ld[]) { } void Mock_Summarize(int verbose) { } void Mock_Symmetrize(mock_ga_t *g_a) { } void Mock_Sync(void) { } void Mock_Terminate(void) { } int Mock_Total_blocks(mock_ga_t *g_a) { } void Mock_Transpose(mock_ga_t *g_a, mock_ga_t *g_b) { } void Mock_Unlock(int mutex) { } int Mock_Update_ghost_dir(mock_ga_t *g_a, int dimension, int idir, int flag) { } void Mock_Update_ghosts(mock_ga_t *g_a) { } int Mock_Uses_fapi(void) { } int Mock_Uses_ma(void) { } int Mock_Uses_proc_grid(mock_ga_t *g_a) { } int Mock_Valid_handle(mock_ga_t *g_a) { return g_a != NULL; } int Mock_Verify_handle(mock_ga_t *g_a) { } double Mock_Wtime(void) { } DoubleComplex Mock_Zdot(mock_ga_t *g_a, mock_ga_t *g_b) { } DoubleComplex Mock_Zdot_patch(mock_ga_t *g_a, char t_a, int alo[], int ahi[], mock_ga_t *g_b, char t_b, int blo[], int bhi[]) { } void Mock_Zero_diagonal(mock_ga_t *g_a) { } void Mock_Zero(mock_ga_t *g_a) { ITER_DECLARE_VARS(g_a) switch (g_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ ITER_DECLARE_BUFFER(g_a,C_TYPE) \ ITER_INIT(g_a,C_TYPE) \ ITER_BEGIN(g_a,C_TYPE) \ assign_zero_##AT(*g_a_buf); \ ITER_NEXT(g_a) \ ITER_END \ break; \ } #include "types.xh" #undef TYPE_CASE } } void Mock_Zero_patch(mock_ga_t *g_a, int lo[], int hi[]) { } void Mock_Zgemm(char ta, char tb, int m, int n, int k, DoubleComplex alpha, mock_ga_t *g_a, mock_ga_t *g_b, DoubleComplex beta, mock_ga_t *g_c ) { } void Mock_Zgop(DoubleComplex x[], int n, char *op) { } ga-5.9.2/global/testing/unit-tests/mock.h000066400000000000000000000655711500715745200203000ustar00rootroot00000000000000#ifndef GLOBAL_TESTING_UNIT_TEST_MOCK_H #define GLOBAL_TESTING_UNIT_TEST_MOCK_H #include #include #include "ga.h" #include "typesf2c.h" #include "abstract_ops.h" typedef struct Mock_GA { int type; int ndim; int dims[GA_MAX_DIM]; int dims_m1[GA_MAX_DIM]; /* for convenience */ int strides[GA_MAX_DIM]; /* for convenience */ int backstrides[GA_MAX_DIM]; /* for convenience */ int size; /* for convenience, total number of elements */ int itemsize; /* for convenience, sizeof(type) */ int nd_m1; /* for convenience, ndim-1 */ void *buf; } mock_ga_t; static size_t type_to_size(int type) { size_t size; switch (type) { case C_INT: size = sizeof(int); break; case C_LONG: size = sizeof(long); break; case C_LONGLONG: size = sizeof(long long); break; case C_FLOAT: size = sizeof(float); break; case C_DBL: size = sizeof(double); break; case C_SCPL: size = sizeof(SingleComplex); break; case C_DCPL: size = sizeof(DoubleComplex); break; default: assert(0); } return size; } /* all the variables we need for the iterator, prefixed with the given name */ #define ITER_DECLARE_VARS(the_ga) \ char *the_ga##_bytebuf = (the_ga)->buf; \ int the_ga##_size = (the_ga)->size; \ int the_ga##_i; /* all the variables we need for the iterator, prefixed with the given name * Note: use this when iterating over a lo/hi patch */ #define ITER_DECLARE_VARS_PATCH(the_ga) \ char *the_ga##_bytebuf = (the_ga)->buf; \ int the_ga##_ndim = (the_ga)->ndim; \ int the_ga##_nd_m1 = (the_ga)->nd_m1; \ int the_ga##_coords[GA_MAX_DIM]; \ int the_ga##_lo[GA_MAX_DIM]; \ int the_ga##_hi[GA_MAX_DIM]; \ int the_ga##_size = 1; \ int the_ga##_i; \ int the_ga##_j; /* to define the user-visible buffer on which to perform the user-defined op */ #define ITER_DECLARE_BUFFER(the_ga,TYPE) TYPE *the_ga##_buf = NULL; /* setup the user-visible buffer and the iterator variables * Note: use this when iterating over the entire array buffer i.e. no lo/hi */ #define ITER_CAST_BUFFER(the_ga,TYPE) the_ga##_buf = (TYPE*)the_ga##_bytebuf; /* setup the user-visible buffer and the iterator variables * Note: use this when iterating over the entire array buffer i.e. no lo/hi */ #define ITER_BEGIN(the_ga,TYPE) \ for (the_ga##_i = 0; the_ga##_istrides[the_ga##_i] * the_ga##_lo[the_ga##_i] * (the_ga)->itemsize; \ } \ ITER_CAST_BUFFER(the_ga,TYPE) /* iterate over the buffer */ #define ITER_NEXT(the_ga) ++the_ga##_buf; /* iterate over the buffer */ #define ITER_NEXT_PATCH(the_ga) \ for (the_ga##_j=the_ga##_nd_m1; the_ga##_j>=0; --the_ga##_j) { \ if (the_ga##_coords[the_ga##_j] <= the_ga##_hi[the_ga##_j]) { \ ++the_ga##_coords[the_ga##_j]; \ the_ga##_buf += (the_ga)->strides[the_ga##_j]; \ break; \ } \ else { \ the_ga##_coords[the_ga##_j] = the_ga##_lo[the_ga##_j]; \ the_ga##_buf -= (the_ga)->backstrides[the_ga##_j]; \ } \ } /* end of loop */ #define ITER_END } static void mock_data(mock_ga_t *mock_a, int g_a) { ITER_DECLARE_VARS(mock_a) switch(mock_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ ITER_DECLARE_BUFFER(mock_a,C_TYPE) \ ITER_INIT(mock_a,C_TYPE) \ ITER_BEGIN(mock_a,C_TYPE) \ if (mock_a_i%2) { \ assign_##AT(*mock_a_buf, mock_a_i); \ } \ else { \ assign_##AT(*mock_a_buf,-mock_a_i); \ } \ ITER_NEXT(mock_a) \ ITER_END \ break; \ } TYPE_CASE(C_INT,int,reg) TYPE_CASE(C_LONG,long,reg) TYPE_CASE(C_LONGLONG,long long,reg) TYPE_CASE(C_FLOAT,float,reg) TYPE_CASE(C_DBL,double,reg) #undef TYPE_CASE #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ ITER_DECLARE_BUFFER(mock_a,C_TYPE) \ ITER_INIT(mock_a,C_TYPE) \ ITER_BEGIN(mock_a,C_TYPE) \ C_TYPE value; \ if (mock_a_i%2) { \ value.real = mock_a_i; \ value.imag = -mock_a_i; \ } \ else { \ value.real = -mock_a_i; \ value.imag = mock_a_i; \ } \ assign_##AT(*mock_a_buf,value); \ ITER_NEXT(mock_a) \ ITER_END \ break; \ } TYPE_CASE(C_SCPL,SingleComplex,cpl) TYPE_CASE(C_DCPL,DoubleComplex,cpl) #undef TYPE_CASE } } static void mock_to_global(mock_ga_t *mock_a, int g_a) { if (0 == GA_Nodeid()) { int i; int lo[GA_MAX_DIM]; int hi[GA_MAX_DIM]; int ld[GA_MAX_DIM]; for (i=0; indim; ++i) { lo[i] = 0; hi[i] = mock_a->dims[i]-1; } for (i=0; indim-1; ++i) { ld[i] = hi[i+1]-lo[i+1]+1; } NGA_Put(g_a, lo, hi, mock_a->buf, ld); } GA_Sync(); } static void global_to_mock(int g_a, mock_ga_t *mock_a) { int i; int lo[GA_MAX_DIM]; int hi[GA_MAX_DIM]; int ld[GA_MAX_DIM]; for (i=0; indim; ++i) { lo[i] = 0; hi[i] = mock_a->dims[i]-1; } for (i=0; indim-1; ++i) { ld[i] = hi[i+1]-lo[i+1]+1; } GA_Sync(); NGA_Get(g_a, lo, hi, mock_a->buf, ld); GA_Sync(); } static void print_val(void *val, int type) { switch(type) { case C_INT: printf("%d", *((int*)val)); break; case C_LONG: printf("%ld", *((long*)val)); break; case C_LONGLONG: printf("%lld", *((long long*)val)); break; case C_FLOAT: printf("%f", *((float*)val)); break; case C_DBL: printf("%f", *((double*)val)); break; case C_SCPL: printf("%f+%fi", ((SingleComplex*)val)->real,((SingleComplex*)val)->imag); break; case C_DCPL: printf("%f+%fi", ((DoubleComplex*)val)->real,((DoubleComplex*)val)->imag); break; default: assert(0); } } static int neq_mock(mock_ga_t *mock_a, mock_ga_t *mock_b, int *idx) { ITER_DECLARE_VARS(mock_a) ITER_DECLARE_VARS(mock_b) *idx=-1; switch(mock_a->type) { #define TYPE_CASE(GA_TYPE,C_TYPE,AT) \ case GA_TYPE: \ { \ ITER_DECLARE_BUFFER(mock_a,C_TYPE) \ ITER_DECLARE_BUFFER(mock_b,C_TYPE) \ ITER_INIT(mock_a,C_TYPE) \ ITER_INIT(mock_b,C_TYPE) \ ITER_BEGIN(mock_a,C_TYPE) \ if (!eq_##AT(*mock_a_buf, *mock_b_buf)) { \ *idx = mock_a_i; \ return 1; \ } \ ITER_NEXT(mock_a) \ ITER_NEXT(mock_b) \ ITER_END \ break; \ } #include "types.xh" #undef TYPE_CASE } return 0; } void Mock_Abs_value(mock_ga_t *g_a); void Mock_Abs_value_patch(mock_ga_t *g_a, int *lo, int *hi); void Mock_Access_block_grid(mock_ga_t *g_a, int index[], void *ptr, int ld[]); void Mock_Access_block(mock_ga_t *g_a, int idx, void *ptr, int ld[]); void Mock_Access_block_segment(mock_ga_t *g_a, int proc, void *ptr, int *len); void Mock_Access_ghost_element(mock_ga_t *g_a, void *ptr, int subscript[], int ld[]); void Mock_Access_ghosts(mock_ga_t *g_a, int dims[], void *ptr, int ld[]); void Mock_Access(mock_ga_t *g_a, int lo[], int hi[], void *ptr, int ld[]); void Mock_Acc(mock_ga_t *g_a, int lo[], int hi[],void* buf,int ld[],void* alpha); void Mock_Add_constant(mock_ga_t *g_a, void* alpha); void Mock_Add_constant_patch(mock_ga_t *g_a,int *lo,int *hi,void *alpha); void Mock_Add_diagonal(mock_ga_t *g_a, int g_v); void Mock_Add_patch(void * alpha, mock_ga_t *g_a, int alo[], int ahi[], void * beta, mock_ga_t *g_b, int blo[], int bhi[], mock_ga_t *g_c, int clo[], int chi[]); void Mock_Add(void *alpha, mock_ga_t *g_a, void* beta, mock_ga_t *g_b, mock_ga_t *g_c); int Mock_Allocate(mock_ga_t *g_a); int Mock_Assemble_duplicate(mock_ga_t *g_a, char *name, void *ptr); void Mock_Brdcst(void *buf, int lenbuf, int root); SingleComplex Mock_Cdot(mock_ga_t *g_a, mock_ga_t *g_b); SingleComplex Mock_Cdot_patch(mock_ga_t *g_a, char t_a, int alo[], int ahi[], mock_ga_t *g_b, char t_b, int blo[], int bhi[]); void Mock_Cgemm(char ta, char tb, int m, int n, int k, SingleComplex alpha, mock_ga_t *g_a, mock_ga_t *g_b, SingleComplex beta, mock_ga_t *g_c ); void Mock_Cgop(SingleComplex x[], int n, char *op); void Mock_Check_handle(mock_ga_t *g_a, char *string); int Mock_Cluster_nnodes(void); int Mock_Cluster_nodeid(void); int Mock_Cluster_nprocs(int x); int Mock_Cluster_procid(int x, int y); int Mock_Cluster_proc_nodeid(int proc); int Mock_Compare_distr(mock_ga_t *g_a, mock_ga_t *g_b); void Mock_Copy(mock_ga_t *g_a, mock_ga_t *g_b); void Mock_Copy_patch(char trans, mock_ga_t *g_a, int alo[], int ahi[], mock_ga_t *g_b, int blo[], int bhi[]); mock_ga_t * Mock_Create_config(int type,int ndim,int dims[], char *name, int chunk[], int p_handle); mock_ga_t * Mock_Create_ghosts_config(int type,int ndim,int dims[], int width[], char *name, int chunk[], int p_handle); mock_ga_t * Mock_Create_ghosts(int type,int ndim,int dims[], int width[], char *name, int chunk[]); mock_ga_t * Mock_Create_ghosts_irreg_config(int type,int ndim,int dims[], int width[], char *name, int map[], int nblock[], int p_handle); mock_ga_t * Mock_Create_ghosts_irreg(int type,int ndim,int dims[], int width[], char *name, int map[], int nblock[]); mock_ga_t * Mock_Create_handle(void); mock_ga_t * Mock_Create(int type,int ndim,int dims[], char *name, int chunk[]); mock_ga_t * Mock_Create_irreg_config(int type,int ndim,int dims[],char *name, int block[], int map[], int p_handle); mock_ga_t * Mock_Create_irreg(int type,int ndim,int dims[],char *name, int block[], int map[]); int Mock_Create_mutexes(int number); double Mock_Ddot(mock_ga_t *g_a, mock_ga_t *g_b); double Mock_Ddot_patch(mock_ga_t *g_a, char t_a, int alo[], int ahi[], mock_ga_t *g_b, char t_b, int blo[], int bhi[]); int Mock_Deregister_type(int type); void Mock_Destroy(mock_ga_t *g_a); int Mock_Destroy_mutexes(void); void Mock_Dgemm(char ta, char tb, int m, int n, int k, double alpha, mock_ga_t *g_a, mock_ga_t *g_b, double beta, mock_ga_t *g_c ); void Mock_Dgop(double x[], int n, char *op); void Mock_Diag(mock_ga_t *g_a, int g_s, int g_v, void *eval); void Mock_Diag_reuse(int reuse, mock_ga_t *g_a, int g_s, int g_v, void *eval); void Mock_Diag_seq(mock_ga_t *g_a, int g_s, int g_v, void *eval); void Mock_Diag_std(mock_ga_t *g_a, int g_v, void *eval); void Mock_Diag_std_seq(mock_ga_t *g_a, int g_v, void *eval); void Mock_Distribution(mock_ga_t *g_a, int iproc, int lo[], int hi[]); mock_ga_t * Mock_Duplicate(mock_ga_t *g_a, char* array_name); void Mock_Elem_divide(mock_ga_t *g_a, mock_ga_t *g_b, mock_ga_t *g_c); void Mock_Elem_divide_patch(mock_ga_t *g_a,int *alo,int *ahi, mock_ga_t *g_b,int *blo,int *bhi,mock_ga_t *g_c,int *clo,int *chi); void Mock_Elem_maximum(mock_ga_t *g_a, mock_ga_t *g_b, mock_ga_t *g_c); void Mock_Elem_maximum_patch(mock_ga_t *g_a,int *alo,int *ahi, mock_ga_t *g_b,int *blo,int *bhi,mock_ga_t *g_c,int *clo,int *chi); void Mock_Elem_minimum(mock_ga_t *g_a, mock_ga_t *g_b, mock_ga_t *g_c); void Mock_Elem_minimum_patch(mock_ga_t *g_a,int *alo,int *ahi, mock_ga_t *g_b,int *blo,int *bhi,mock_ga_t *g_c,int *clo,int *chi); void Mock_Elem_multiply(mock_ga_t *g_a, mock_ga_t *g_b, mock_ga_t *g_c); void Mock_Elem_multiply_patch(mock_ga_t *g_a,int *alo,int *ahi, mock_ga_t *g_b,int *blo,int *bhi,mock_ga_t *g_c,int *clo,int *chi); void Mock_Error(char *str, int code); float Mock_Fdot(mock_ga_t *g_a, mock_ga_t *g_b); float Mock_Fdot_patch(mock_ga_t *g_a, char t_a, int alo[], int ahi[], mock_ga_t *g_b, char t_b, int blo[], int bhi[]); void Mock_Fence(void); void Mock_Fgop(float x[], int n, char *op); void Mock_Fill(mock_ga_t *g_a, void *value); void Mock_Fill_patch(mock_ga_t *g_a, int lo[], int hi[], void *val); void Mock_Freemem(void* ptr); void Mock_Gather_flat(mock_ga_t *g_a, void *v, int subsArray[], int n); void Mock_Gather(mock_ga_t *g_a, void *v, int* subsArray[], int n); void Mock_Get_block_info(mock_ga_t *g_a, int num_blocks[], int block_dims[]); int Mock_Get_debug(void); void Mock_Get_diag(mock_ga_t *g_a, int g_v); int Mock_Get_dimension(mock_ga_t *g_a); void Mock_Get_field(mock_ga_t *g_a, int *lo, int *hi, int foff, int fsize, void *buf, int *ld); void Mock_Get_ghost_block(mock_ga_t *g_a, int lo[], int hi[], void *buf, int ld[]); void Mock_Get(mock_ga_t *g_a, int lo[], int hi[], void* buf, int ld[]); void* Mock_Getmem(int type, int nelem, int grp_id); int Mock_Get_pgroup(mock_ga_t *g_a); int Mock_Get_pgroup_size(int grp_id); void Mock_Get_proc_grid(mock_ga_t *g_a, int dims[]); void Mock_Get_proc_index(mock_ga_t *g_a, int iproc, int subscript[]); void Mock_Gop(int type, void *x, int n, char *op); int Mock_Has_ghosts(mock_ga_t *g_a); int Mock_Idot(mock_ga_t *g_a, mock_ga_t *g_b); int Mock_Idot_patch(mock_ga_t *g_a, char t_a, int alo[], int ahi[], mock_ga_t *g_b, char t_b, int blo[], int bhi[]); void Mock_Igop(int x[], int n, char *op); void Mock_Init_fence(void); void Mock_Initialize_args(int *argc, char ***argv); void Mock_Initialize_ltd(size_t limit); void Mock_Initialize(void); void Mock_Inquire(mock_ga_t *g_a, int *type, int *ndim, int dims[]); size_t Mock_Inquire_memory(void); char* Mock_Inquire_name(mock_ga_t *g_a); int Mock_Is_mirrored(mock_ga_t *g_a); long Mock_Ldot(mock_ga_t *g_a, mock_ga_t *g_b); long Mock_Ldot_patch(mock_ga_t *g_a, char t_a, int alo[], int ahi[], mock_ga_t *g_b, char t_b, int blo[], int bhi[]); void Mock_Lgop(long x[], int n, char *op); void Mock_List_nodeid(int *list, int nprocs); long long Mock_Lldot(mock_ga_t *g_a, mock_ga_t *g_b); long long Mock_Lldot_patch(mock_ga_t *g_a, char t_a, int alo[], int ahi[], mock_ga_t *g_b, char t_b, int blo[], int bhi[]); void Mock_Llgop(long long x[], int n, char *op); int Mock_Llt_solve(mock_ga_t *g_a, mock_ga_t *g_b); int Mock_Locate(mock_ga_t *g_a, int subscript[]); int Mock_Locate_nnodes(mock_ga_t *g_a, int lo[], int hi[]); int Mock_Locate_num_blocks(mock_ga_t *g_a, int lo[], int hi[]); int Mock_Locate_region(mock_ga_t *g_a,int lo[],int hi[],int map[],int procs[]); void Mock_Lock(int mutex); void Mock_Lu_solve(char tran, mock_ga_t *g_a, mock_ga_t *g_b); void Mock_Mask_sync(int first, int last); void Mock_Matmul_patch_2d(char transa, char transb, void* alpha, void *beta, mock_ga_t *g_a, int ailo, int aihi, int ajlo, int ajhi, mock_ga_t *g_b, int bilo, int bihi, int bjlo, int bjhi, mock_ga_t *g_c, int cilo, int cihi, int cjlo, int cjhi); void Mock_Matmul_patch(char transa, char transb, void* alpha, void *beta, mock_ga_t *g_a, int alo[], int ahi[], mock_ga_t *g_b, int blo[], int bhi[], mock_ga_t *g_c, int clo[], int chi[]) ; void Mock_Median(mock_ga_t *g_a, mock_ga_t *g_b, mock_ga_t *g_c, int g_m); void Mock_Median_patch(mock_ga_t *g_a, int *alo, int *ahi, mock_ga_t *g_b, int *blo, int *bhi, mock_ga_t *g_c, int *clo, int *chi, int g_m, int *mlo, int *mhi); size_t Mock_Memory_avail(void); int Mock_Memory_limited(void); void Mock_Merge_distr_patch(mock_ga_t *g_a, int alo[], int ahi[], mock_ga_t *g_b, int blo[], int bhi[]); void Mock_Merge_mirrored(mock_ga_t *g_a); void Mock_NbAcc(mock_ga_t *g_a,int lo[], int hi[],void* buf,int ld[],void* alpha, ga_nbhdl_t* nbhandle); void Mock_Nbget_field(mock_ga_t *g_a, int *lo, int *hi, int foff, int fsize,void *buf, int *ld, ga_nbhdl_t *nbhandle); void Mock_NbGet_ghost_dir(mock_ga_t *g_a, int mask[], ga_nbhdl_t* handle); void Mock_NbGet(mock_ga_t *g_a, int lo[], int hi[], void* buf, int ld[], ga_nbhdl_t* nbhandle); void Mock_Nblock(mock_ga_t *g_a, int *nblock); void Mock_Nbput_field(mock_ga_t *g_a, int *lo, int *hi, int foff, int fsize, void *buf, int *ld, ga_nbhdl_t *nbhandle); void Mock_NbPut(mock_ga_t *g_a, int lo[], int hi[], void* buf, int ld[], ga_nbhdl_t* nbhandle); int Mock_NbTest(ga_nbhdl_t* nbhandle); void Mock_NbWait(ga_nbhdl_t* nbhandle); int Mock_Ndim(mock_ga_t *g_a); int Mock_Nnodes(void); int Mock_Nodeid(void); void Mock_Norm1(mock_ga_t *g_a, double *nm); void Mock_Norm_infinity(mock_ga_t *g_a, double *nm); void Mock_Periodic_acc(mock_ga_t *g_a, int lo[], int hi[],void* buf,int ld[],void* alpha); void Mock_Periodic_get(mock_ga_t *g_a, int lo[], int hi[], void* buf, int ld[]); void Mock_Periodic_put(mock_ga_t *g_a, int lo[], int hi[], void* buf, int ld[]); int Mock_Pgroup_absolute_id(int pgroup, int pid); void Mock_Pgroup_brdcst(int grp, void *buf, int lenbuf, int root); void Mock_Pgroup_cgop(int grp, SingleComplex x[], int n, char *op); int Mock_Pgroup_create(int *list, int count); int Mock_Pgroup_destroy(int grp); void Mock_Pgroup_dgop(int grp, double x[], int n, char *op); void Mock_Pgroup_fgop(int grp, float x[], int n, char *op); int Mock_Pgroup_get_default(void); int Mock_Pgroup_get_mirror(void); int Mock_Pgroup_get_world(void); void Mock_Pgroup_igop(int grp, int x[], int n, char *op); void Mock_Pgroup_lgop(int grp, long x[], int n, char *op); void Mock_Pgroup_llgop(int grp, long long x[], int n, char *op); int Mock_Pgroup_nnodes(int grp_id); int Mock_Pgroup_nodeid(int grp_id); void Mock_Pgroup_set_default(int p_handle); int Mock_Pgroup_split(int grp_id, int num_group); int Mock_Pgroup_split_irreg(int grp_id, int color); void Mock_Pgroup_sync(int grp_id); void Mock_Pgroup_zgop(int grp, DoubleComplex x[], int n, char *op); void Mock_Print_distribution(mock_ga_t *g_a); void Mock_Print_file(FILE *file, mock_ga_t *g_a); void Mock_Print(mock_ga_t *g_a); void Mock_Print_patch_2d(mock_ga_t *g_a,int ilo,int ihi,int jlo,int jhi,int pretty); void Mock_Print_patch(mock_ga_t *g_a, int lo[], int hi[], int pretty); void Mock_Print_stats(void); void Mock_Proc_topology(mock_ga_t *g_a, int proc, int coord[]); void Mock_Put_field(mock_ga_t *g_a, int *lo, int *hi, int foff, int fsize, void *buf, int *ld); void Mock_Put(mock_ga_t *g_a, int lo[], int hi[], void* buf, int ld[]); void Mock_Randomize(mock_ga_t *g_a, void *value); long Mock_Read_inc(mock_ga_t *g_a, int subscript[], long inc); void Mock_Recip(mock_ga_t *g_a); void Mock_Recip_patch(mock_ga_t *g_a,int *lo, int *hi); void Mock_Register_stack_memory(void * (*ext_alloc)(size_t, int, char *), void (*ext_free)(void *)); int Mock_Register_type(size_t bytes); void Mock_Release_block_grid(mock_ga_t *g_a, int index[]); void Mock_Release_block(mock_ga_t *g_a, int idx); void Mock_Release_block_segment(mock_ga_t *g_a, int idx); void Mock_Release_ghost_element(mock_ga_t *g_a, int index[]); void Mock_Release_ghosts(mock_ga_t *g_a); void Mock_Release(mock_ga_t *g_a, int lo[], int hi[]); void Mock_Release_update_block_grid(mock_ga_t *g_a, int index[]); void Mock_Release_update_block(mock_ga_t *g_a, int idx); void Mock_Release_update_block_segment(mock_ga_t *g_a, int idx); void Mock_Release_update_ghost_element(mock_ga_t *g_a, int index[]); void Mock_Release_update_ghosts(mock_ga_t *g_a); void Mock_Release_update(mock_ga_t *g_a, int lo[], int hi[]); void Mock_Scale_cols(mock_ga_t *g_a, int g_v); void Mock_Scale(mock_ga_t *g_a, void *value); void Mock_Scale_patch(mock_ga_t *g_a, int lo[], int hi[], void *alpha); void Mock_Scale_rows(mock_ga_t *g_a, int g_v); void Mock_Scan_add(mock_ga_t *g_a, mock_ga_t *g_b, int g_sbit, int lo, int hi, int excl); void Mock_Scan_copy(mock_ga_t *g_a, mock_ga_t *g_b, int g_sbit, int lo, int hi); void Mock_Scatter_acc_flat(mock_ga_t *g_a, void *v, int subsArray[], int n, void *alpha); void Mock_Scatter_acc(mock_ga_t *g_a, void *v, int* subsArray[], int n, void *alpha); void Mock_Scatter_flat(mock_ga_t *g_a, void *v, int subsArray[], int n); void Mock_Scatter(mock_ga_t *g_a, void *v, int* subsArray[], int n); void Mock_Select_elem(mock_ga_t *g_a, char* op, void* val, int *index); void Mock_Set_array_name(mock_ga_t *g_a, char *name); void Mock_Set_block_cyclic(mock_ga_t *g_a, int dims[]); void Mock_Set_block_cyclic_proc_grid(mock_ga_t *g_a, int block[], int proc_grid[]); void Mock_Set_chunk(mock_ga_t *g_a, int chunk[]); void Mock_Set_data(mock_ga_t *g_a, int ndim, int dims[], int type); void Mock_Set_debug(int flag); void Mock_Set_diagonal(mock_ga_t *g_a, int g_v); void Mock_Set_ghost_corner_flag(mock_ga_t *g_a, int flag); void Mock_Set_ghosts(mock_ga_t *g_a, int width[]); void Mock_Set_irreg_distr(mock_ga_t *g_a, int map[], int block[]); void Mock_Set_irreg_flag(mock_ga_t *g_a, int flag); void Mock_Set_memory_limit(size_t limit); void Mock_Set_pgroup(mock_ga_t *g_a, int p_handle); void Mock_Set_restricted(mock_ga_t *g_a, int list[], int size); void Mock_Set_restricted_range(mock_ga_t *g_a, int lo_proc, int hi_proc); void Mock_Sgemm(char ta, char tb, int m, int n, int k, float alpha, mock_ga_t *g_a, mock_ga_t *g_b, float beta, mock_ga_t *g_c ); void Mock_Shift_diagonal(mock_ga_t *g_a, void *c); int Mock_Solve(mock_ga_t *g_a, mock_ga_t *g_b); int Mock_Spd_invert(mock_ga_t *g_a); void Mock_Step_bound_info(int g_xx, int g_vv, int g_xxll, int g_xxuu, void *boundmin, void *wolfemin, void *boundmax); void Mock_Step_bound_info_patch(int g_xx, int *xxlo, int *xxhi, int g_vv, int *vvlo, int *vvhi, int g_xxll, int *xxlllo, int *xxllhi, int g_xxuu, int *xxuulo, int *xxuuhi, void *boundmin, void *wolfemin, void *boundmax); void Mock_Step_max(mock_ga_t *g_a, mock_ga_t *g_b, void *step); void Mock_Step_max_patch(mock_ga_t *g_a, int *alo, int *ahi, mock_ga_t *g_b, int *blo, int *bhi, void *step); void Mock_Strided_acc(mock_ga_t *g_a, int lo[], int hi[], int skip[], void* buf, int ld[], void *alpha); void Mock_Strided_get(mock_ga_t *g_a, int lo[], int hi[], int skip[], void* buf, int ld[]); void Mock_Strided_put(mock_ga_t *g_a, int lo[], int hi[], int skip[], void* buf, int ld[]); void Mock_Summarize(int verbose); void Mock_Symmetrize(mock_ga_t *g_a); void Mock_Sync(void); void Mock_Terminate(void); int Mock_Total_blocks(mock_ga_t *g_a); void Mock_Transpose(mock_ga_t *g_a, mock_ga_t *g_b); void Mock_Unlock(int mutex); int Mock_Update_ghost_dir(mock_ga_t *g_a, int dimension, int idir, int flag); void Mock_Update_ghosts(mock_ga_t *g_a); int Mock_Uses_fapi(void); int Mock_Uses_ma(void); int Mock_Uses_proc_grid(mock_ga_t *g_a); int Mock_Valid_handle(mock_ga_t *g_a); int Mock_Verify_handle(mock_ga_t *g_a); double Mock_Wtime(void); DoubleComplex Mock_Zdot(mock_ga_t *g_a, mock_ga_t *g_b); DoubleComplex Mock_Zdot_patch(mock_ga_t *g_a, char t_a, int alo[], int ahi[], mock_ga_t *g_b, char t_b, int blo[], int bhi[]); void Mock_Zero_diagonal(mock_ga_t *g_a); void Mock_Zero(mock_ga_t *g_a); void Mock_Zero_patch(mock_ga_t *g_a, int lo[], int hi[]); void Mock_Zgemm(char ta, char tb, int m, int n, int k, DoubleComplex alpha, mock_ga_t *g_a, mock_ga_t *g_b, DoubleComplex beta, mock_ga_t *g_c ); void Mock_Zgop(DoubleComplex x[], int n, char *op); #endif /* GLOBAL_TESTING_UNIT_TEST_MOCK_H */ ga-5.9.2/global/testing/unpackc.c000066400000000000000000000345141500715745200166400ustar00rootroot00000000000000/** * Tests the unpack function in GA. * * Each test will locally perform the same functionality, then compare * local buffers against global buffers. */ #if HAVE_CONFIG_H # include "config.h" #endif #define NELEM 200000 #define HEAP 200*200*4 #define FUDGE 100 #define STACK 200*200 #include #include #include "ga.h" #include "macdecls.h" #include "mp3.h" static int me; static int nproc; #define assign_reg(a,b) (a) = (b) #define assign_cpl(a,b) (a).real = (b).real; (a).imag = (b).imag #define assign_val_reg(a,b,c) (a) = (b) #define assign_val_cpl(a,b,c) (a).real = (b); (a).imag = (c) #define eq_zero_reg(a) (0 == (a)) #define eq_zero_cpl(a) (0 == (a).real && 0 == (a).imag) #define neq_zero_reg(a) (0 != (a)) #define neq_zero_cpl(a) (0 != (a).real || 0 != (a).imag) #define neq_reg(a,b) ((a) != (b)) #define neq_cpl(a,b) ((a).real != (b).real || (a).imag != (b).imag) #define assign_add_reg(a,b,c) (a) = (b) + (c) #define assign_add_cpl(a,b,c) (a).real = (b).real + (c).real; \ (a).imag = (b).imag + (c).imag #define cast_reg(a) ((float)(a)) #define cast_cpl(a) ((float)(a).real) #if 0 # define PRINT(AT,AT_MSK) do { \ int p; \ for (p=0; p #endif #if HAVE_STDLIB_H # include #endif #if HAVE_ASSERT_H # include #endif #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif #include "ga.h" #include "global.h" #include "macdecls.h" #include "testutil.h" #include "mp3.h" #define add_range_ F77_FUNC_(add_range,ADD_RANGE) #define compare_patches_ F77_FUNC_(compare_patches,COMPARE_PATCHES) #define copy_range_ F77_FUNC_(copy_range,COPY_RANGE) #define dot_range_ F77_FUNC_(dot_range,DOT_RANGE) #define init_array_ F77_FUNC_(init_array,INIT_ARRAY) #define print_range_ F77_FUNC_(print_range,PRINT_RANGE) #define register_ext_memory_ F77_FUNC_(register_ext_memory,REGISTER_EXT_MEMORY) #define set_ma_use_armci_mem_ F77_FUNC_(set_ma_use_armci_mem,SET_MA_USE_ARMCI_MEM) #define scale_patch_ F77_FUNC_(scale_patch,SCALE_PATCH) #define util_mdtob_ F77_FUNC_(util_mdtob,UTIL_MDTOB) #define util_mitob_ F77_FUNC_(util_mitob,UTIL_MITOB) #define util_timer_ F77_FUNC_(util_timer,UTIL_TIMER) #define BASE 100 /***************************** macros ************************/ #define COPY(src, dst, bytes) memcpy((dst),(src),(bytes)) #define GA_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define GA_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define GA_ABS(a) (((a) <0) ? -(a) : (a)) void *ext_malloc(size_t bytes, int align, char *name) { return malloc(bytes); } void ext_free(void *ptr) { free(ptr); } void FATR register_ext_memory_() { GA_Register_stack_memory(ext_malloc, ext_free); } /*\ generate random range for a section of multidimensional array \*/ void get_range( int ndim, int dims[], int lo[], int hi[]) { int dim; for(dim=0; dim 0)? rand()%range : lo[dim]; new_lo[dim] = toss; new_hi[dim] = toss + diff -1; assert(new_hi[dim] < dims[dim]); assert(diff == (new_hi[dim] -new_lo[dim]+1)); } } /*\ print range of n-dimensional array with two strings before and after \*/ void print_range_internal(char *pre,int ndim, int lo[], int hi[], char* post) { int i; printf("%s[",pre); for(i=0;i #endif #if HAVE_STDLIB_H # include #endif #define MAX_EVENTS 1000000 #define MAX_ARRAYS 8 #define MAX_EVENT_TYPES 32 #define MAX_PROC 512 /* this value must be exactly like in the GA package source */ #define GA_OFFSET 1000 #define GA_ABS(x) ((x)>0 ? (x) :(-x)) #define GA_MAX(a,b) (((a) >= (b)) ? (a) : (b)) /* if enabled, program prints GA usage statistics & requires EXTRA MEMORY * NOT recommended for large number of arrays/processes and/or event types */ #define PRINT_STATS 1 #ifdef PRINT_STATS double stat[MAX_PROC][MAX_ARRAYS][MAX_EVENT_TYPES]; unsigned int acc[MAX_PROC][MAX_ARRAYS][MAX_EVENT_TYPES]; void printstat(int proc, unsigned tlast); void update(long int proc, int *record, unsigned long t0, unsigned long t1); #endif int proc=0, arrays=0, event_types=0; static int tcomp(const void *t1, const void *t2); int main(argc,argv) int argc; char **argv; { long int p,i,j,MR,events=0; unsigned long int k, *clock_base, *times, base=0, *tbase, maxtime=0; int *record; int ga=-GA_OFFSET; int flag; FILE *fin,*fout, *fdistr; char *foutname="adjust.ed", fdstrname[15], finname[8]; int distrdata; if(argc < 2){ printf("Usage: adjust [] \n"); exit(1); } sscanf(argv[1],"%d",&ga); ga += GA_OFFSET; /* find the distribution file name and open it */ sprintf(fdstrname, "distrib.%d", ga-GA_OFFSET); if((fdistr = fopen(fdstrname,"r")) == NULL) { fprintf(stderr, "This array handle %d is invalid!\n", ga-GA_OFFSET); exit(3); } fscanf(fdistr, "%ld", &p); if(argc>2) sscanf(argv[3],"%ld",&MR); else MR = MAX_EVENTS; if(p>MAX_PROC){ fprintf(stderr,"Only %d procs allowed - you must modify this program\n",MAX_PROC); exit(1); } if(p<1){ fprintf(stderr,"number of processes must be > 0: %ld\n",p); exit(2); } printf("Processing tracefiles for %ld processes\n",p); #ifdef PRINT_STATS for(i=0;i= MAX_ARRAYS){ fprintf(stderr, "array handle beyond range we can deal with %d > %d\n", record[base+1], MAX_ARRAYS-1); exit(4); } times[0] -= clock_base[i]; times[1] = base+1; fscanf(fin,"%lu",times+2); times[2] -= clock_base[i]; #ifdef PRINT_STATS update(i,record+base,times[0],times[2]); #endif times[3] = -base-1; if(maxtime *(unsigned long int*)t2 ? 1 : -1); return (flag); } #ifdef PRINT_STATS void update(proc,record, t0, t1) long int proc; int *record; unsigned long t0,t1; { int ar=record[1], et=record[6]; if(arrays < ar){ arrays = ar; if(arrays>= MAX_ARRAYS){ fprintf(stderr,"The program can handle only %d arrays\n",MAX_ARRAYS); exit(1); } } if(event_types < et){ event_types = et; if (event_types >= MAX_EVENT_TYPES){ fprintf(stderr,"The program can handle only %d event types%d\n",MAX_EVENT_TYPES,et); exit(1); } } stat[proc][ar][et] += 1e-6 * (double)(t1-t0); acc[proc][ar][et] ++; } void printstat(proc,tlast) int proc; unsigned tlast; { int p,e,a; double t,ta,te, tl= 1e-6 * tlast; double total_cm=0., total_cp=0.; long int ia, ie; printf("\t\t\tActivity Statistics\n"); for(p=0;p #endif #if HAVE_STDLIB_H # include #endif int **Patches, *Col, *lastCol; int p; #define hash(ilo,ihi,p) (((ilo)+(ihi))%(p)) #define hash2(ilo,ihi,jlo,jhi,p) ( ((ilo)+(ihi) + (jlo)+(jhi))%(p) ) int LocFound(int *patch); int **idim2(row,col) int row,col; { register int **prow, *pdata, i; pdata = (int*) calloc(row*col, sizeof(int)); if(pdata == (int*) NULL){ printf("Memory allocation failed - data: %dx%d\n",row,col); exit(1); } prow = (int **)calloc(row,sizeof(int *)); if(prow == (int**) NULL){ printf("memory allocation failed for prow"); exit(1); } for(i=0;i \n"); printf("(from,to) specifies the range of collision numbers to be printed\n"); exit(1); } sscanf(argv[1],"%d",&p); if(p>1000||p<1){ printf("%d processors ? Please be reasonable ...\n",p); exit(1); } fin = fopen(argv[2],"r"); if(!fin){ printf("%s: File Not Found, Exiting ...\n",argv[2]); exit(1); } sscanf(argv[3],"%d",&from); sscanf(argv[4],"%d",&to); if(from<0||from>p||to>p||to and \n"); exit(1); } if(!(Col = (int*)calloc(p,sizeof(int)))){ printf("couldn't allocate memory 2\n"); exit(2); } Patches = idim2(p,5); for(i=0;isscanf(ln,"%d%d%d%d%d%d%d%d%lu", &i,&k,patch+1,patch+2,patch+3,patch+4,&i,&flag,&time))continue; loc = LocFound(patch); if(!Patches[loc][0])for(i=1;i<5;i++)Patches[loc][i]=patch[i]; last_col = Patches[loc][0]; if(flag == 1){ cur_col = ++Patches[loc][0]; if(cur_col>p){ printf("%d -- Error in collision number. Record: %d\n",cur_col,j); exit(3); } Col[cur_col-1]++; if(max_col=from && last_col<=to) || (cur_col>=from && cur_col<=to)){ printf("%f ",1e-6*time); for(i=from-1;i /* Routines from gpbase.c */ extern void pgp_debug(Integer g_p, Integer intsize); extern void pgp_initialize(); extern void pgp_terminate(); extern void* pgp_malloc(size_t size); extern void pgp_free(void* ptr); extern Integer pgp_create_handle(); extern void pgp_set_dimensions(Integer g_p, Integer ndim, Integer *dims, Integer intsize); extern Integer pgp_get_dimension(Integer g_p); extern void pgp_set_chunk(Integer g_p, Integer *chunk); extern void pgp_set_irreg_distr(Integer g_p, Integer *mapc, Integer *nblock); extern logical pgp_allocate(Integer g_p); extern logical pgp_destroy(Integer g_p); extern void pgp_distribution(Integer g_p, Integer proc, Integer *lo, Integer *hi); extern void pgp_assign_local_element(Integer g_p, Integer *subscript, void *ptr, Integer size, Integer intsize); extern void* pgp_free_local_element(Integer g_p, Integer *subscript); extern void pgp_memzero(Integer g_p, Integer intsize); extern void pgp_sync(); /* Routines from gponesided.c */ extern void pgp_get(Integer g_p, Integer *lo, Integer *hi, void *buf, void **buf_ptr, Integer *ld, void *buf_size, Integer *ld_sz, Integer *size, Integer isize, Integer setbuf); extern void pgp_put(Integer g_p, Integer *lo, Integer *hi, void **buf_ptr, Integer *ld, void *buf_size, Integer *ld_sz, Integer *size, Integer checksize, Integer isize); extern void pgp_get_size(Integer g_p, Integer *lo, Integer *hi, Integer *size, Integer isize); extern void pgp_access_element(Integer g_p, Integer *subscript, void *ptr, Integer *size); extern void pgp_release_element(Integer g_p, Integer *subscript); extern void pgp_release_update_element(Integer g_p, Integer *subscript); extern void pgp_gather_size(Integer g_p, Integer nv, Integer *subscript, Integer *size, Integer intsize); extern void pgp_gather(Integer g_p, Integer nv, Integer *subscript, void *buf, void **buf_ptr, void *buf_size, Integer *size, Integer intsize, Integer setbuf); extern void pgp_scatter(Integer g_p, Integer nv, Integer *subscript, void **buf_ptr, void *buf_size, Integer *size, Integer checksize, Integer intsize); #endif /* GPPAPI_H */ ga-5.9.2/gparrays/src/gp.h000066400000000000000000000030701500715745200153220ustar00rootroot00000000000000#include extern void GP_Access_element(int g_p, int *subscript, void *ptr, int *size); extern int GP_Allocate(int g_p); extern void GP_Assign_local_element(int g_p, int *subscript, void *ptr, int size); extern int GP_Create_handle(); extern void GP_Debug(int g_p); extern int GP_Destroy(int g_p); extern void GP_Distribution(int g_p, int proc, int *lo, int *hi); extern void GP_Free(void* ptr); extern void* GP_Free_local_element(int g_p, int *subscript); extern int GP_Get_dimension(int g_p); extern void GP_Gather_size(int g_p, int nv, int *subscript, int *size); extern void GP_Gather(int g_p, int nv, int *subscript, void *buf, void **buf_ptr, int *buf_size, int *size, int setbuf); extern void GP_Get_size(int g_p, int *lo, int *hi, int *size); extern void GP_Get(int g_p, int *lo, int *hi, void *buf, void **buf_ptr, int *ld, void *buf_size, int *ld_sz, int *size, int setbuf); extern void GP_Initialize(); extern void* GP_Malloc(size_t size); extern void GP_Memzero(int g_p); extern void GP_Put(int g_p, int *lo, int *hi, void **buf_ptr, int *ld, void *buf_size, int *ld_sz, int *size, int checksize); extern void GP_Release_element(int g_p, int *subscript); extern void GP_Release_update_element(int g_p, int *subscript); extern void GP_Scatter(int g_p, int nv, int *subscript, void **buf_ptr, int *buf_size, int *size, int checksize); extern void GP_Set_chunk(int g_p, int *chunk); extern void GP_Set_dimensions(int g_p, int ndim, int *dims); extern void GP_Set_irreg_distr(int g_p, int *mapc, int *blocks); extern void GP_Sync(); extern void GP_Terminate(); ga-5.9.2/gparrays/src/gpbase.c000066400000000000000000000355521500715745200161620ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_ASSERT_H # include #endif #include "gacommon.h" #include "typesf2c.h" #include "ga-papi.h" #include "gp-papi.h" #include "gp-wapi.h" #include "gpbase.h" #include "armci.h" extern void gpi_onesided_init(); extern void gpi_onesided_clean(); gp_array_t *GP; int GP_pointer_type; /** * Initialize internal library structures for Global Pointer Arrays */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wgp_initialize = pgp_initialize #endif void pgp_initialize() { Integer i; GP = (gp_array_t*)malloc(sizeof(gp_array_t)*GP_MAX_ARRAYS); GP_pointer_type = pnga_register_type(sizeof(armci_meminfo_t)); if (!GP) { pnga_error("gp_initialize: malloc GP failed",0); } for (i=0; iarmci_addr = ((char*)meminfo_ptr->armci_addr) + meminfo_sz; meminfo_ptr->addr = ((char*)meminfo_ptr->addr) + meminfo_sz; meminfo_ptr->size -= meminfo_sz; /*bjp printf("p[%d]: armci_addr = %ld\n", pnga_nodeid(), (long)meminfo_ptr->armci_addr); */ return meminfo_ptr->addr; } /** * Special free() for Global Pointer Arrays */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wgp_free = pgp_free #endif void pgp_free(void* ptr) { armci_meminfo_t meminfo; size_t meminfo_sz = sizeof(armci_meminfo_t); if(!ptr) pnga_error("gp_free: Invalid pointer",0); memcpy( &meminfo, ((char*)ptr)-meminfo_sz, meminfo_sz); /* update the meminfo structure */ meminfo.armci_addr = ((char*)meminfo.armci_addr) - meminfo_sz; meminfo.addr = ((char*)meminfo.addr) - meminfo_sz; meminfo.size += meminfo_sz; ARMCI_Memctl(&meminfo); } /** * Create a handle for a GP array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wgp_create_handle = pgp_create_handle #endif Integer pgp_create_handle() { Integer i, handle=-GP_OFFSET-1; for (i=0; i GP_MAX_DIM) { pnga_error("gp_set_dimensions: dimension is not valid", ndim); } for (i=0; iGP[handle].hi[i]) { /*bjp printf("p[%d] subscript[%d]: %d\n",pnga_nodeid(),i,subscript[i]); printf("p[%d] lo[%d]: %d hi[%d]: %d\n",pnga_nodeid(),i,GP[handle].lo[i],i, GP[handle].hi[i]); */ /* printf("p[%d] subscript[%d]: %d lo[%d]: %d hi[%d]: %d\n",pnga_nodeid(), i, subscript[i], i, GP[handle].lo[i], i, GP[handle].hi[i]); */ pnga_error("gp_assign_local_element: subscript out of bounds", i); } } pnga_access_ptr(GP[handle].g_size_array,subscript,subscript,&gp_ptr,ld); if (intsize == 4) { *((int*)gp_ptr) = (int)size; } else { *((int64_t*)gp_ptr) = (int64_t)size; } /*bjp printf("p[%ld] (internal) size %d at location [%ld:%ld]\n", (long)pnga_nodeid(), *((int*)gp_ptr), (long)subscript[0],(long)subscript[1]); */ pnga_release_update(GP[handle].g_size_array, subscript, subscript); pnga_access_ptr(GP[handle].g_ptr_array,subscript,subscript,&gp_ptr,ld); *((armci_meminfo_t*)gp_ptr) = *((armci_meminfo_t*)(((char*)ptr)-sizeof(armci_meminfo_t))); pnga_release_update(GP[handle].g_ptr_array, subscript, subscript); } /** * Free local data element using access via the Global Pointer array. * @param[in] g_p pointer array handle * @param[in] subscript[ndim] location of element in pointer array */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wgp_free_local_element = pgp_free_local_element #endif void* pgp_free_local_element(Integer g_p, Integer *subscript) { armci_meminfo_t *gp_ptr; void *ptr; Integer handle, ld[GP_MAX_DIM-1], i; GP_Int buf; handle = g_p + GP_OFFSET; /* check to make sure that element is located in local block of GP array */ for (i=0; iGP[handle].hi[i]) { pnga_error("gp_free_local_element: subscript out of bounds", i); } } pnga_access_ptr(GP[handle].g_ptr_array,subscript,subscript,&gp_ptr,ld); ptr = (*gp_ptr).addr; memset((void*)gp_ptr,0,sizeof(armci_meminfo_t)); pnga_release_update(GP[handle].g_ptr_array, subscript, subscript); /* set corresponding element of size array to zero */ buf = 0; for (i=0; i #include #include "gp.h" #include "gpbase.h" #include "gp-papi.h" #if ENABLE_PROFILING # include "gp-wapi.h" #else # include "gp-wapidefs.h" #endif #ifdef USE_FAPI # define COPYC2F(carr, farr, n){\ int i; for(i=0; i< (n); i++)(farr)[i]=(Integer)(carr)[i];} # define COPYF2C(farr, carr, n){\ int i; for(i=0; i< (n); i++)(carr)[i]=(int)(farr)[i];} # define COPYINDEX_F2C COPYF2C #else # define COPYC2F(carr, farr, n){\ int i; for(i=0; i< (n); i++)(farr)[n-i-1]=(Integer)(carr)[i];} # define COPYINDEX_C2F(carr, farr, n){\ int i; for(i=0; i< (n); i++)(farr)[n-i-1]=(Integer)(carr)[i]+1;} # define COPYINDEX_F2C(farr, carr, n){\ int i; for(i=0; i< (n); i++)(carr)[n-i-1]=(int)(farr)[i] -1;} #endif static Integer* gp_copy_map(int block[], int block_ndim, int map[]); void GP_Access_element(int g_p, int *subscript, void *ptr, int *size) { Integer ag_p = (Integer)g_p; Integer _gp_idx[GP_MAX_DIM]; Integer asize; int ndim = wgp_get_dimension(ag_p); COPYINDEX_C2F(subscript,_gp_idx,ndim); wgp_access_element(ag_p, _gp_idx, ptr, &asize); *size = (int)asize; } int GP_Allocate(int g_p) { Integer ag_p; ag_p = (Integer)g_p; return (int)wgp_allocate(ag_p); } void GP_Assign_local_element(int g_p, int *subscript, void *ptr, int size) { Integer ag_p = (Integer)g_p; Integer _gp_idx[GP_MAX_DIM]; Integer asize = (Integer)size; int ndim = wgp_get_dimension(ag_p); COPYINDEX_C2F(subscript,_gp_idx,ndim); wgp_assign_local_element(ag_p, _gp_idx, ptr, asize, 4); } int GP_Create_handle() { return (int)wgp_create_handle(); } void GP_Debug(int g_p) { Integer ag_p; ag_p = (Integer)g_p; wgp_debug(ag_p, 4); } int GP_Destroy(int g_p) { Integer ag_p; ag_p = (Integer)g_p; return (int)wgp_destroy(ag_p); } void GP_Distribution(int g_p, int proc, int *lo, int *hi) { Integer ag_p = (Integer)g_p; Integer p = (Integer)proc; int ndim = (int)wgp_get_dimension(ag_p); Integer _gp_lo[GP_MAX_DIM], _gp_hi[GP_MAX_DIM]; wgp_distribution(ag_p, p, _gp_lo, _gp_hi); COPYINDEX_F2C(_gp_lo, lo, ndim); COPYINDEX_F2C(_gp_hi, hi, ndim); } void GP_Free(void* ptr) { wgp_free(ptr); } void* GP_Free_local_element(int g_p, int *subscript) { Integer ag_p = (Integer)g_p; int ndim = wgp_get_dimension(ag_p); Integer _gp_idx[GP_MAX_DIM]; COPYINDEX_C2F(subscript, _gp_idx, ndim); return wgp_free_local_element(ag_p, _gp_idx); } int GP_Get_dimension(int g_p) { return wgp_get_dimension(g_p); } void GP_Gather_size(int g_p, int nv, int *subscript, int *size) { int idx, i; Integer ag_p = (Integer)g_p; Integer anv = (Integer)nv; Integer asize; Integer *asubscript; int ndim = wgp_get_dimension(ag_p); asubscript = (Integer*)malloc((int)ndim*nv*sizeof(Integer)); if (asubscript == NULL) GA_Error("Memory allocation in GP_Gather_size failed",0); /* adjust the indices for fortran interface */ for (idx=0; idx #endif #if HAVE_STRING_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_ASSERT_H # include #endif #include "gacommon.h" #include "typesf2c.h" #include "gpbase.h" #include "armci.h" #include "message.h" #include "ga-papi.h" #include "gp-papi.h" #include "gp-wapi.h" #define gpm_GetRangeFromMap(p, ndim, plo, phi){ \ Integer _mloc = p* ndim *2; \ *plo = (Integer*)_gp_map + _mloc; \ *phi = *plo + ndim; \ } /** * Utility arrays used in onesided communication */ Integer *_gp_map; Integer *_gp_proclist; /** * Initialize utility arrays */ void gpi_onesided_init() { Integer nproc; nproc = pnga_pgroup_nnodes(pnga_pgroup_get_world()); _gp_proclist = (Integer*)malloc((size_t)nproc*sizeof(Integer)); _gp_map = (Integer*)malloc((size_t)(nproc*2*GP_MAX_DIM)*sizeof(Integer)); } /** * Clean utility arrays */ void gpi_onesided_clean() { free(_gp_map); free(_gp_proclist); } /** * Get sizes of element in GP array and return them to a local buffer. Also * evaluate the total size of the data to be copied and return that in the * variable size. intsize is an internal variable that can be used to * distinguish between 4 and 8 byte integers * @param[in] g_p pointer array handle * @param[in] lo[ndim] lower corner of pointer array block * @param[in] hi[ndim] upper corner of pointer array block * @param[out] buf buffer that holds element sizes * @param[in] ld[ndim-1] physical dimensions of buffer * @param[out] size total size of requested data * @param[in] intsize parameter to distinguish between 4 and 8 * byte integers */ #if HAVE_SYS_WEAK_ALIAS_PRAGMA # pragma weak wgp_get_size = pgp_get_size #endif void pgp_get_size(Integer g_p, Integer *lo, Integer *hi, Integer *size, Integer intsize) { Integer handle, ndim, i, nelems; Integer block_ld[GP_MAX_DIM-1]; int *int_ptr; int *long_ptr; handle = g_p + GP_OFFSET; if (!GP[handle].active) { pnga_error("gp_get_size: inactive array handle specified", g_p); } ndim = pnga_ndim(GP[handle].g_ptr_array); for (i=0; i GP[handle].hi[i]) pnga_error("gp_get_size: illegal block size specified", g_p); } /* Find total number of elements in block and get strides of requested block */ ndim = GP[handle].ndim; nelems = 1; for (i=0; i GP[handle].hi[i]) pnga_error("gp_get: illegal block size specified", g_p); } /*bjp printf("p[%d] (gp_get) lo[0]: %d hi[0]: %d lo[1]: %d hi[1]: %d\n", me,lo[0],hi[0],lo[1],hi[1]); */ if (!setbuf) { pnga_get(GP[handle].g_size_array, lo, hi, buf_size, ld_sz); } /* Get strides of requested block */ ndim = GP[handle].ndim; nelems = 1; for (i=0; i=0; d--) { offset_sz = offset_sz*ld_sz[d] + index[d]; offset_d = offset_d*ld[d] + index[d]; } /* evaluate offset in data buffer */ buf_ptr[offset_d] = (void*)(((char*)buf)+offset_ptr); if (intsize == 4) { offset_ptr += ((int*)buf_size)[offset_sz]; } else { offset_ptr += ((int64_t*)buf_size)[offset_sz]; } /*bjp printf("p[%d] (gp_get) buf_size[%d]: %d\n",me,offset_sz,((int*)buf_size)[offset_sz]); */ idx++; } *size = offset_ptr; /* locate the processors containing some portion of the patch represented by * lo and hi and return the results in _gp_map, gp_proclist, and np. * _gp_proclist contains a list of processors containing some portion of the * patch, _gp_map contains the lower and upper indices of the portion of the * patch held by a given processor and np contains the number of processors * that contain some portion of the patch. */ pnga_locate_region(GP[handle].g_size_array, lo, hi, _gp_map, _gp_proclist, &np); /* Loop over processors containing data */ for (idx=0; idx=0; d--) { offset_rem = offset_rem*block_ld_loc[d] + index[d]; offset_sz = offset_sz*ld_sz[d] + index[d] + plo[d] - lo[d]; offset_d = offset_d*ld[d] + index[d] + plo[d] - lo[d]; } /*bjp printf("p[%d] j: %d offset_rem: %d offset_sz: %d offset_d: %d\n", me, j, offset_rem, offset_sz, offset_d); */ if (intsize == 4) { bytes = (int)((int*)buf_size)[offset_sz]; } else { bytes = (int)((int64_t*)buf_size)[offset_sz]; } /*bjp printf("p[%d] bytes: %d\n",me,bytes); */ if (bytes > 0) { if (rem_ptr[offset_rem].cpid == me) { (src_array[j])[0] = ((void*)(rem_ptr[offset_rem].addr)); /* handle remote and SMP case */ } else if (pnga_cluster_proc_nodeid(me) == pnga_cluster_proc_nodeid(rem_ptr[offset_rem].cpid)) { (src_array[j])[0] = ARMCI_Memat(&rem_ptr[offset_rem], sizeof(armci_meminfo_t)); } else { (src_array[j])[0] = (void*)rem_ptr[offset_rem].armci_addr; } (dst_array[j])[0] = (void*)buf_ptr[offset_d]; #define DBG desc[jcnt].src_ptr_array = src_array[j]; desc[jcnt].dst_ptr_array = dst_array[j]; desc[jcnt].bytes = bytes; desc[jcnt].ptr_array_len = 1; /*bjp printf("p[%ld] jcnt: %d nelems: %ld index[%ld,%ld] bytes: %d src_ptr: %ld p: %d dst_ptr: %ld\n", (long)pnga_nodeid(), jcnt, (long)nelems, (long)(index[0]+plo[0]), (long)(index[1]+plo[1]), desc[jcnt].bytes, (long)desc[jcnt].src_ptr_array[0], (int)p, (long)desc[jcnt].dst_ptr_array[0]); */ jcnt++; } else { /*bjp printf("p[%ld] null pointer at i: %ld j: %ld\n", (long)pnga_nodeid(), (long)(index[0]+plo[0]), (long)(index[1]+plo[1])); */ } } /*bjp printf("p[%ld] (gp_get) jcnt: %d p: %d\n",(long)pnga_nodeid(),jcnt,p); */ #ifdef XDBG for (j=0; j 0) { rc = ARMCI_GetV(desc, (int)jcnt, (int)p); if (rc) pnga_error("ARMCI_GetV failure in gp_get",rc); } #endif for (j=0; j GP[handle].hi[i]) pnga_error("gp_put: illegal block size specified", g_p); } /* Get strides of target block */ ndim = GP[handle].ndim; nelems = 1; for (i=0; i=0; d--) { offset_rem = offset_rem*block_ld_loc[d] + index[d]; offset_sz = offset_sz*ld_sz[d] + index[d] + plo[d] - lo[d]; offset_d = offset_d*ld[d] + index[d] + plo[d] - lo[d]; } if (intsize == 4) { bytes = (int)((int*)buf_size)[offset_sz]; } else { bytes = (int)((int64_t*)buf_size)[offset_sz]; } if (bytes > 0) { if (rem_ptr[offset_rem].cpid == me) { (dst_array[j])[0] = ((void*)(rem_ptr[offset_rem].addr)); /* handle remote and SMP case */ } else if (pnga_cluster_proc_nodeid(me) == pnga_cluster_proc_nodeid(rem_ptr[offset_rem].cpid)) { (dst_array[j])[0] = ARMCI_Memat(&rem_ptr[offset_rem], sizeof(armci_meminfo_t)); } else { (dst_array[j])[0] = (void*)rem_ptr[offset_rem].armci_addr; } (src_array[j])[0] = (void*)buf_ptr[offset_d]; #define DBG desc[jcnt].src_ptr_array = src_array[j]; desc[jcnt].dst_ptr_array = dst_array[j]; desc[jcnt].bytes = bytes; desc[jcnt].ptr_array_len = 1; jcnt++; } else { /*bjp printf("p[%ld] null pointer at i: %ld j: %ld\n", (long)pnga_nodeid(), (long)(index[0]+plo[0]), (long)(index[1]+plo[1])); */ } } #ifdef XDBG for (j=0; j 0) { rc = ARMCI_PutV(desc, (int)jcnt, (int)p); if (rc) pnga_error("ARMCI_PutV failure in gp_put",rc); } #endif for (j=0; jhi[i]) { pnga_error("gp_access_element: subscript out of bounds", i); } if (i 0) { idx = header[iproc]; /* allocate descriptor array for this vector call */ desc = (armci_giov_t*)malloc((int)nelems[iproc]*sizeof(armci_giov_t)); src_array = (void***)malloc((int)nelems[iproc]*sizeof(void**)); dst_array = (void***)malloc((int)nelems[iproc]*sizeof(void**)); j = 0; while (idx > -1) { if (intsize == 4) { bytes = (int)((int*)buf_size)[idx]; } else { bytes = (int)((int64_t*)buf_size)[idx]; } if (bytes>0) { src_array[j] = (void**)malloc(sizeof(void*)); dst_array[j] = (void**)malloc(sizeof(void*)); if (iproc == me) { (src_array[j])[0] = ((void*)(info_buf[idx].addr)); } else if (pnga_cluster_proc_nodeid(me) == pnga_cluster_proc_nodeid(iproc)) { (src_array[j])[0] = ARMCI_Memat(&info_buf[idx],sizeof(armci_meminfo_t)); } else { (src_array[j])[0] = (void*)(info_buf[idx].armci_addr); } (dst_array[j])[0] = (void*)(buf_ptr[idx]); if (intsize == 4) { desc[j].bytes = (int)((int*)buf_size)[idx]; } else { desc[j].bytes = (int)((int64_t*)buf_size)[idx]; } desc[j].src_ptr_array = src_array[j]; desc[j].dst_ptr_array = dst_array[j]; desc[j].ptr_array_len = 1; j++; } idx = list[idx]; } /* gather data from remote locations */ #ifdef XDBG for (idx=0; idx 0) { rc = ARMCI_GetV(desc, (int)j, (int)iproc); if (rc) pnga_error("ARMCI_GetV failure in gp_gather",rc); } #endif /* free arrays */ for (j=0; j 0) { idx = header[iproc]; /* allocate descriptor array for this vector call */ desc = (armci_giov_t*)malloc((int)nelems[iproc]*sizeof(armci_giov_t)); src_array = (void***)malloc((int)nelems[iproc]*sizeof(void**)); dst_array = (void***)malloc((int)nelems[iproc]*sizeof(void**)); j = 0; while (idx > -1) { if (intsize == 4) { bytes = (int)((int*)buf_size)[idx]; } else { bytes = (int)((int64_t*)buf_size)[idx]; } if (bytes>0) { src_array[j] = (void**)malloc(sizeof(void*)); dst_array[j] = (void**)malloc(sizeof(void*)); if (iproc == me) { (dst_array[j])[0] = ((void*)(info_buf[idx].addr)); } else if (pnga_cluster_proc_nodeid(me) == pnga_cluster_proc_nodeid(iproc)) { (dst_array[j])[0] = ARMCI_Memat(&info_buf[idx],sizeof(armci_meminfo_t)); } else { (dst_array[j])[0] = (void*)(info_buf[idx].armci_addr); } (src_array[j])[0] = (void*)(buf_ptr[idx]); if (intsize == 4) { desc[j].bytes = (int)((int*)buf_size)[idx]; } else { desc[j].bytes = (int)((int64_t*)buf_size)[idx]; } desc[j].src_ptr_array = src_array[j]; desc[j].dst_ptr_array = dst_array[j]; desc[j].ptr_array_len = 1; j++; } idx = list[idx]; } /* gather data from remote locations */ #ifdef XDBG for (idx=0; idx 0) { rc = ARMCI_PutV(desc, (int)j, (int)iproc); if (rc) pnga_error("ARMCI_PutV failure in gp_gather",rc); } #endif /* free arrays */ for (j=0; j #include "ga.h" #include "gp.h" #include "macdecls.h" #include "mp3.h" #include /* #define N_I 4 #define N_J 4 */ #define N_I 32 #define N_J 32 #define N_K 32 /* #define Q_I 2 #define Q_J 2 */ #define Q_I 8 #define Q_J 8 /* get random patch for GP */ void get_range( int ndim, int dims[], int lo[], int hi[], int g_p) { int dim, nproc, i, itmp; nproc = NGA_Nodeid(); /* Mix up values on different processors */ for (i=0; i MAX_FACTOR) printf("Overflow in grid factor\n"); prime[pmax-1] = i; } } /** * find all prime factors of p */ ip = p; ifac = 0; for (i=0; i=0; ii--) { i = idim/ti; j = jdim/tj; k = kdim/tk; if (i >= j && i >= k && i > 1) { ti = fac[ii]*ti; } else if (j >= i && j >= k && j > 1) { tj = fac[ii]*tj; } else if (k >= i && k >= j && k > 1) { tk = fac[ii]*tk; } else { printf("Too many processors in factoring routine\n"); } } free(prime); free(fac); *pdi = ti; *pdj = tj; *pdk = tk; } void do_work() { int g_p, me, i, ii, j, jj, l, k, nv; int nproc, next; int m_k_ij, m_l_ij, idx; int dims[2], lo[2], hi[2], ndim; int lo_t[2], hi_t[2]; int dims3[3], lo3[3], hi3[3], blocks[3], chunk[3]; int nelems, nsize; int idim, jdim, kdim, subscript[2], size; int pdi, pdj, pdk; int ld[2], ld_sz[2]; int checksize; int *ptr, *mapc; void **buf_ptr; void *buf; int *buf_size; void *elem_buf; int *subscripts; /* Create Global Pointer array */ dims[0] = N_I; dims[1] = N_J; ndim = 2; me = NGA_Nodeid(); nproc = NGA_Nnodes(); g_p = GP_Create_handle(); GP_Set_dimensions(g_p, ndim, dims); GP_Allocate(g_p); /* Find locally owned elements in Global Pointer array. Only these elements can be assigned to data using the GP_Assign_local_element routine. */ GP_Distribution(g_p, me, lo, hi); idim = hi[0] - lo[0] + 1; jdim = hi[1] - lo[1] + 1; for (ii=0; iihi[0] || subscript[1]hi[1]) { printf("p[%d] assign i: %d j: %d lo[0]: %d hi[0]: %d lo[1]: %d hi[1]: %d\n", me,subscript[0],subscript[1],lo[0],hi[0],lo[1],hi[1]); } GP_Assign_local_element(g_p, subscript, (void*)ptr, size); } } /* Guarantee data consistency */ GP_Sync(); /*GP_Debug(g_p);*/ /* Generate bounding coordinates to an arbitrary patch in GP array */ get_range(ndim, dims, lo, hi, g_p); /* Find the total amount of data contained in the patch */ nsize = (hi[0]-lo[0]+1)*(hi[1]-lo[1]+1); GP_Get_size(g_p, lo, hi, &size); /* Allocate local buffers and retrieve data */ buf = (void*)malloc(size); buf_ptr = (void**)malloc(nsize*sizeof(void*)); buf_size = (int*) malloc(nsize*sizeof(int)); ld[1] = hi[0]-lo[0]+1; ld[0] = hi[1]-lo[1]+1; ld_sz[1] = hi[0]-lo[0]+1; ld_sz[0] = hi[1]-lo[1]+1; GA_Set_debug(1); GP_Get(g_p, lo, hi, buf, buf_ptr, ld, buf_size, ld_sz, &size, 0); if (me==0) printf("\nCompleted GP_Get\n"); GA_Set_debug(0); /* Check contents of buffers to see if data is as expected */ for (i=lo[0]; i<=hi[0]; i++) { ii = i - lo[0]; for (j=lo[1]; j<=hi[1]; j++) { jj = j - lo[1]; idx = j*N_I + i; ptr = (int*)buf_ptr[ii*ld[0]+jj]; m_k_ij = i%Q_I + 1; m_l_ij = j%Q_J + 1; if (buf_size[ii*ld_sz[0]+jj] != 4*(ptr[0]*ptr[1]+2)) { printf("p[%d] size expected: %d actual: %d\n",me,buf_size[ii*ld_sz[0]+jj], 4*(ptr[0]*ptr[1]+2)); } if (ptr[0] != m_k_ij) { printf("p[%d] [%d,%d] Dimension(1) i actual: %d expected: %d\n",me,i,j,ptr[0],m_k_ij); } if (ptr[1] != m_l_ij) { printf("p[%d] [%d,%d] Dimension(1) j actual: %d expected: %d\n",me,i,j,ptr[1],m_l_ij); } for (k=0; k #include "macdecls.h" #include "ga.h" #include "gp.h" #define USE_HYPRE 0 #define IMAX 100 #define JMAX 100 #define KMAX 100 #define LMAX IMAX*JMAX*KMAX #define MAXVEC 5000000 #define EPSLN 1.0d-10 #define CHECK_BOUND 1 #define bb_a(ib) bb_v(bb_i + (ib)) #define cc_a(ib) cc_v(cc_i + (ib)) #define Integer int #define MAX_FACTOR 10000 #if USE_HYPRE #include "HYPRE.h" #include "HYPRE_struct_mv.h" #include "mpi.h" #endif void grid_factor(int p, int xdim, int ydim, int zdim, int *idx, int *idy, int *idz) { int i, j; int ip, ifac, pmax, prime[MAX_FACTOR]; int fac[MAX_FACTOR]; int ix, iy, iz, ichk; i = 1; /* factor p completely first, find all prime numbers, besides 1, less than or equal to the square root of p */ ip = (int)(sqrt((double)p))+1; pmax = 0; for (i=2; i<=ip; i++) { ichk = 1; for (j=0; j MAX_FACTOR) printf("Overflow in grid_factor\n"); prime[pmax-1] = i; } } /* find all prime factors of p */ ip = p; ifac = 0; for (i=0; i= 0; i--) { ix = xdim/(*idx); iy = ydim/(*idy); iz = zdim/(*idz); if (ix >= iy && ix >= iz && ix > 1) { *idx = fac[i]*(*idx); } else if (iy >= ix && iy >= iz && iy > 1) { *idy = fac[i]*(*idy); } else if (iz >= ix && iz >= iy && iz > 1) { *idz = fac[i]*(*idz); } else { printf("Too many processors in grid factoring routine\n"); } } } /* Short subroutine for multiplying sparse matrix block with vector segment */ void loc_matmul(double *a_mat, int *jvec, int *ivec, double *bvec, double *cvec, int nrows) { double tempc; int i, j, jj, jmin,jmax; for (i=0; i hi[0]) { jdx = kp*pdi*pdj + jp*pdi + ip + 1; ecnt[jdx] = ecnt[jdx] + 1; } else { ecnt[me] = ecnt[me] + 1; } } if (ix-1 >= 0) { if (ix-1 < lo[0]) { jdx = kp*pdi*pdj + jp*pdi + ip - 1; ecnt[jdx] = ecnt[jdx] + 1; } else { ecnt[me] = ecnt[me] + 1; } } if (iy+1 <= jdim-1) { if (iy+1 > hi[1]) { jdx = kp*pdi*pdj + (jp+1)*pdi + ip; ecnt[jdx] = ecnt[jdx] + 1; } else { ecnt[me] = ecnt[me] + 1; } } if (iy-1 >= 0) { if (iy-1 < lo[1]) { jdx = kp*pdi*pdj + (jp-1)*pdi + ip; ecnt[jdx] = ecnt[jdx] + 1; } else { ecnt[me] = ecnt[me] + 1; } } if (iz+1 <= kdim-1) { if (iz+1 > hi[2]) { jdx = (kp+1)*pdi*pdj + jp*pdi + ip; ecnt[jdx] = ecnt[jdx] + 1; } else { ecnt[me] = ecnt[me] + 1; } } if (iz-1 >= 0) { if (iz-1 < lo[2]) { jdx = (kp-1)*pdi*pdj + jp*pdi + ip; ecnt[jdx] = ecnt[jdx] + 1; } else { ecnt[me] = ecnt[me] + 1; } } } /* Create list of processors that this processor is coupled to. If ecnt[i] is greater than zero then process i is coupled to this process. ltotal_procs: the total number of other processor that this process is coupled to. This includes this process (the diagonal term). lproclist[i]: the IDs of the processor that this processor is coupled to lproc_inv[i]: the location in lproclist of processor i. If processor i is not coupled to this process, the lproc_inv[i] = -1 ncnt: total number of non-zero elements held by this process nnz_list[i]: number of processes coupled to process i by sparse blocks nnz: total number of sparse blocks */ ltotal_procs = 0; ncnt = 0; for (i=0; i 0) { ltotal_procs++; ncnt += ecnt[i]; } } nsave = ncnt; lproclist = (int*)malloc(ltotal_procs*sizeof(int)); lproc_inv = (int*)malloc(nprocs*sizeof(int)); licnt = (int*)malloc(ltotal_procs*sizeof(int)); for (i=0; i 0) { lproclist[j] = i; if (j > 0) { lvoffset[j] = ecnt[lproclist[j-1]]+lvoffset[j-1]; } lproc_inv[i] = j; j++; } } /* Create arrays the hold the sparse block representation of the sparse matrix gp_block[nnz]: Global Pointer array holding the sparse sub-matrices g_j[nnz]: column block indices for the element in gp_block g_i[nprocs]: row block indices for the elements in g_j */ tmapc = (int*)malloc((nprocs+1)*sizeof(int)); tmapc[0] = 0; for (i=1; i<=nprocs; i++) { tmapc[i] = tmapc[i-1]+nnz_list[i-1]; } *gp_block = GP_Create_handle(); GP_Set_dimensions(*gp_block,one,&nnz); GP_Set_irreg_distr(*gp_block, tmapc, &nprocs); GP_Allocate(*gp_block); *g_j = NGA_Create_handle(); NGA_Set_data(*g_j,one,&nnz,C_INT); NGA_Set_irreg_distr(*g_j, tmapc, &nprocs); NGA_Allocate(*g_j); for (i=0; i hi[0]) { jdx = kp*pdi*pdj + jp*pdi + ip + 1; il = 0; jl = iy - lo[1]; kl = iz - lo[2]; ldpi = xld[ip+1]; } else { jdx = me; il = ix - lo[0] + 1; jl = iy - lo[1]; kl = iz - lo[2]; ldpi = ldi; } idx = kl*ldpi*ldj + jl*ldpi + il; nghbrs[ncnt] = idx; procid[ncnt] = jdx; } if (ix-1 >= 0) { ncnt++; ixn[ncnt] = ix - 1; iyn[ncnt] = iy; izn[ncnt] = iz; if (ix-1 < lo[0]) { jdx = kp*pdi*pdj + jp*pdi + ip - 1; il = xld[ip-1] - 1; jl = iy - lo[1]; kl = iz - lo[2]; ldmi = xld[ip-1]; } else { jdx = me; il = ix - lo[0] - 1; jl = iy - lo[1]; kl = iz - lo[2]; ldmi = ldi; } idx = kl*ldmi*ldj + jl*ldmi + il; nghbrs[ncnt] = idx; procid[ncnt] = jdx; } if (iy+1 <= jdim-1) { ncnt++; ixn[ncnt] = ix; iyn[ncnt] = iy + 1; izn[ncnt] = iz; if (iy+1 > hi[1]) { jdx = kp*pdi*pdj + (jp+1)*pdi + ip; il = ix - lo[0]; jl = 0; kl = iz - lo[2]; ldpj = yld[jp+1]; } else { jdx = me; il = ix - lo[0]; jl = iy - lo[1] + 1; kl = iz - lo[2]; ldpj = ldj; } idx = kl*ldi*ldpj + jl*ldi + il; nghbrs[ncnt] = idx; procid[ncnt] = jdx; } if (iy-1 >= 0) { ncnt++; ixn[ncnt] = ix; iyn[ncnt] = iy - 1; izn[ncnt] = iz; if (iy-1 < lo[1]) { jdx = kp*pdi*pdj + (jp-1)*pdi + ip; il = ix - lo[0]; jl = yld[jp-1] - 1; kl = iz - lo[2]; ldmj = yld[jp-1]; } else { jdx = me; il = ix - lo[0]; jl = iy - lo[1] - 1; kl = iz - lo[2]; ldmj = ldj; } idx = kl*ldi*ldmj + jl*ldi + il; nghbrs[ncnt] = idx; procid[ncnt] = jdx; } if (iz+1 <= kdim-1) { ncnt++; ixn[ncnt] = ix; iyn[ncnt] = iy; izn[ncnt] = iz + 1; if (iz+1 > hi[2]) { jdx = (kp+1)*pdi*pdj + jp*pdi + ip; il = ix - lo[0]; jl = iy - lo[1]; kl = 0; } else { jdx = me; il = ix - lo[0]; jl = iy - lo[1]; kl = iz - lo[2] + 1; } idx = kl*ldi*ldj + jl*ldi + il; nghbrs[ncnt] = idx; procid[ncnt] = jdx; } if (iz-1 >= 0) { ncnt++; ixn[ncnt] = ix; iyn[ncnt] = iy; izn[ncnt] = iz - 1; if (iz-1 < lo[2]) { jdx = (kp-1)*pdi*pdj + jp*pdi + ip; il = ix - lo[0]; jl = iy - lo[1]; kl = zld[kp-1] - 1; } else { jdx = me; il = ix - lo[0]; jl = iy - lo[1]; kl = iz - lo[2] - 1; } idx = kl*ldi*ldj + jl*ldi + il; nghbrs[ncnt] = idx; procid[ncnt] = jdx; } /* sort indices so that neighbors run from lowest to highest local index. This sort is not particularly efficient but ncnt is generally small */ ncnt++; for (j=0; j nghbrs[k]) { itmp = nghbrs[j]; nghbrs[j] = nghbrs[k]; nghbrs[k] = itmp; itmp = ixn[j]; ixn[j] = ixn[k]; ixn[k] = itmp; itmp = iyn[j]; iyn[j] = iyn[k]; iyn[k] = itmp; itmp = izn[j]; izn[j] = izn[k]; izn[k] = itmp; itmp = procid[j]; procid[j] = procid[k]; procid[k] = itmp; } } } for (k=0; k= ntot) { printf("p[%d] Invalid neighbor %d\n",me,nghbrs[k]); } } /* set weights corresponding to a finite difference Laplacian on a 7-point stencil */ for (j=0; j= nsave) { printf("p[%d] Out of bounds (lvoffset+licnt)[%d]: %d\n",me,idx,lvoffset[idx]+licnt[idx]); } if (lvoffset[idx]+licnt[idx]>=idbg) { } /* TODO: Check this carefully */ jval[lvoffset[idx]+licnt[idx]] = nghbrs[j]; ivalt[idx*isize+i-imin] = ivalt[idx*isize+i-imin]+1; licnt[idx]++; } } /* finish evaluating ival array */ for (i=0; i MAXVEC) NGA_Error("ISIZE exceeds MAXVEC in local arrays ",isize); /* Local portion of sparse matrix has been evaluated and decomposed into blocks that match partitioning of right hand side across processors. The following data is available at this point: 1) ltotal_procs: the number of processors that are coupled to this one via the sparse matrix 2) lproclist(ltotal_procs): a list of processor IDs that are coupled to this processor 3) lproc_inv(nprocs): The entry in proc_list that corresponds to a given processor. If the entry is -1 then that processor does not couple to this processor. 4) licnt(ltotal_procs): The number of non-zero entries in the sparse matrix that couple the process represented by proc_list(j) to this process 5) lvoffset(ltotal_procs): The offsets for the non-zero data in the arrays rval and jval for the blocks that couple this processor to other processes in proc_list 6) offset(nprocs): the offset array for the distributed right hand side vector These arrays describe how the sparse matrix is layed out both locally and across processors. In addition, the actual data for the distributed sparse matrix is found in the following arrays: 1) rval: values of matrix for all blocks on this processor 2) jval: j-indices of matrix for all blocks on this processor 3) ival(ltotal_procs*(lnsize(me)+1)): starting index in rval and jval for each row in each block */ NGA_Sync(); /* Create a sparse array of sparse blocks. Each block element is divided into for sections. The first section consists of 7 ints and contains the parameters imin: minimum i index represented by block imin: maximum i index represented by block jmin: minimum j index represented by block jmin: maximum j index represented by block iblock: row index of block jblock: column index of block nnz: number of non-zero elements in block The next section consists of nnz doubles that represent the non-zero values in the block. The third section consists of nnz ints and contains the local j indices of all values. The final section consists of (imax-imin+2) ints and contains the starting index in jval and rval for the each row between imin and imax. An extra value is included at the end and is set equal to nnz+1. This is included to simplify some coding. */ offset = 0; for (i=0; i ntot-1) { bhi[1] = ntot-1; } btot = (hi[0]-lo[0]+1)*(hi[1]-lo[1]+1)*(hi[2]-lo[2]+1); for (i=0; i 1.0e-10) { printf("p[%d] i: %d vector: %f cbuf: %f\n",me,i,vector[i],cbuf[i]); } prdot2 = prdot2 + (vector[i]-cbuf[i])*(vector[i]-cbuf[i]); } NGA_Dgop(&dotga,1,"+"); NGA_Dgop(&dothypre,1,"+"); NGA_Dgop(&prdot2,1,"+"); prtot2 += sqrt(prdot2); gatot2 += sqrt(dotga); hypretot2 += sqrt(dothypre); free(cbuf); free(blk_ptr); #endif /* Clean up transposed matrix */ GP_Distribution(gt_a_data,me,blo,bhi); for (i=blo[0]; i. # # This macro calls: # # AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS) # # And sets: # # HAVE_BOOST # # LICENSE # # Copyright (c) 2008 Thomas Porschberg # Copyright (c) 2009 Peter Adolphs # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 20 AC_DEFUN([AX_BOOST_BASE], [ AC_ARG_WITH([boost], [AS_HELP_STRING([--with-boost@<:@=ARG@:>@], [use Boost library from a standard location (ARG=yes), from the specified location (ARG=), or disable it (ARG=no) @<:@ARG=yes@:>@ ])], [ if test "$withval" = "no"; then want_boost="no" elif test "$withval" = "yes"; then want_boost="yes" ac_boost_path="" else want_boost="yes" ac_boost_path="$withval" fi ], [want_boost="yes"]) AC_ARG_WITH([boost-libdir], AS_HELP_STRING([--with-boost-libdir=LIB_DIR], [Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]), [ if test -d "$withval" then ac_boost_lib_path="$withval" else AC_MSG_ERROR(--with-boost-libdir expected directory name) fi ], [ac_boost_lib_path=""] ) if test "x$want_boost" = "xyes"; then boost_lib_version_req=ifelse([$1], ,1.20.0,$1) boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'` boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'` boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'` boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'` if test "x$boost_lib_version_req_sub_minor" = "x" ; then boost_lib_version_req_sub_minor="0" fi WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor` AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req) succeeded=no dnl On 64-bit systems check for system libraries in both lib64 and lib. dnl The former is specified by FHS, but e.g. Debian does not adhere to dnl this (as it rises problems for generic multi-arch support). dnl The last entry in the list is chosen by default when no libraries dnl are found, e.g. when only header-only libraries are installed! libsubdirs="lib" ax_arch=`uname -m` if test $ax_arch = x86_64 -o $ax_arch = ppc64 -o $ax_arch = s390x -o $ax_arch = sparc64; then libsubdirs="lib64 lib lib64" fi dnl first we check the system location for boost libraries dnl this location ist chosen if boost libraries are installed with the --layout=system option dnl or if you install boost with RPM if test "$ac_boost_path" != ""; then BOOST_CPPFLAGS="-I$ac_boost_path/include" for ac_boost_path_tmp in $libsubdirs; do if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp" break fi done elif test "$cross_compiling" != yes; then for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then for libsubdir in $libsubdirs ; do if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi done BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir" BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include" break; fi done fi dnl overwrite ld flags if we have required special directory with dnl --with-boost-libdir parameter if test "$ac_boost_lib_path" != ""; then BOOST_LDFLAGS="-L$ac_boost_lib_path" fi CPPFLAGS_SAVED="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS_SAVED="$LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" export LDFLAGS AC_REQUIRE([AC_PROG_CXX]) AC_LANG_PUSH(C++) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ #if BOOST_VERSION >= $WANT_BOOST_VERSION // Everything is okay #else # error Boost version is too old #endif ]])],[ AC_MSG_RESULT(yes) succeeded=yes found_system=yes ],[ ]) AC_LANG_POP([C++]) dnl if we found no boost with system layout we search for boost libraries dnl built and installed without the --layout=system option or for a staged(not installed) version if test "x$succeeded" != "xyes"; then _version=0 if test "$ac_boost_path" != ""; then if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` V_CHECK=`expr $_version_tmp \> $_version` if test "$V_CHECK" = "1" ; then _version=$_version_tmp fi VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE" done fi else if test "$cross_compiling" != yes; then for ac_boost_path in /usr /usr/local /opt /opt/local ; do if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'` V_CHECK=`expr $_version_tmp \> $_version` if test "$V_CHECK" = "1" ; then _version=$_version_tmp best_path=$ac_boost_path fi done fi done VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'` BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE" if test "$ac_boost_lib_path" = ""; then for libsubdir in $libsubdirs ; do if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi done BOOST_LDFLAGS="-L$best_path/$libsubdir" fi fi if test "x$BOOST_ROOT" != "x"; then for libsubdir in $libsubdirs ; do if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi done if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'` stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'` stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'` V_CHECK=`expr $stage_version_shorten \>\= $_version` if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT) BOOST_CPPFLAGS="-I$BOOST_ROOT" BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir" fi fi fi fi CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" export CPPFLAGS LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" export LDFLAGS AC_LANG_PUSH(C++) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ @%:@include ]], [[ #if BOOST_VERSION >= $WANT_BOOST_VERSION // Everything is okay #else # error Boost version is too old #endif ]])],[ AC_MSG_RESULT(yes) succeeded=yes found_system=yes ],[ ]) AC_LANG_POP([C++]) fi if test "$succeeded" != "yes" ; then if test "$_version" = "0" ; then AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in . See http://randspringer.de/boost for more documentation.]]) else AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).]) fi # execute ACTION-IF-NOT-FOUND (if present): ifelse([$3], , :, [$3]) else AC_SUBST(BOOST_CPPFLAGS) AC_SUBST(BOOST_LDFLAGS) AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available]) # execute ACTION-IF-FOUND (if present): ifelse([$2], , :, [$2]) fi CPPFLAGS="$CPPFLAGS_SAVED" LDFLAGS="$LDFLAGS_SAVED" fi ]) ga-5.9.2/m4/ax_pthread.m4000066400000000000000000000501431500715745200150300ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also to link with them as well. For example, you might link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threaded programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to # that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the # PTHREAD_PRIO_INHERIT symbol is defined when compiling with # PTHREAD_CFLAGS. # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # Updated for Autoconf 2.68 by Daniel Richard G. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # Copyright (c) 2011 Daniel Richard G. # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 24 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_SED]) AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on Tru64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then ax_pthread_save_CC="$CC" ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) AC_MSG_RESULT([$ax_pthread_ok]) if test "x$ax_pthread_ok" = "xno"; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi CC="$ax_pthread_save_CC" CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 # (Note: HP C rejects this with "bad form for `-t' option") # -pthreads: Solaris/gcc (Note: HP C also rejects) # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads and # -D_REENTRANT too), HP C (must be checked before -lpthread, which # is present but should not be used directly; and before -mthreads, # because the compiler interprets this as "-mt" + "-hreads") # -mthreads: Mingw32/gcc, Lynx/gcc # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case $host_os in freebsd*) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) ax_pthread_flags="-kthread lthread $ax_pthread_flags" ;; openedition*) # IBM z/OS requires a feature-test macro to be defined in order to # enable POSIX threads at all, so give the user a hint if this is # not set. (We don't define these ourselves, as they can affect # other portions of the system API in unpredictable ways.) AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], [ # if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) AX_PTHREAD_ZOS_MISSING # endif ], [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) ;; solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (N.B.: The stubs are missing # pthread_cleanup_push, or rather a function called by this macro, # so we could check for that, but who knows whether they'll stub # that too in a future libc.) So we'll check first for the # standard Solaris way of linking pthreads (-mt -lpthread). ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" ;; esac # GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) AS_IF([test "x$GCC" = "xyes"], [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) # The presence of a feature test macro requesting re-entrant function # definitions is, on some systems, a strong hint that pthreads support is # correctly enabled case $host_os in darwin* | linux* | osf* | solaris*) ax_pthread_check_macro="_REENTRANT" ;; aix*) ax_pthread_check_macro="_THREAD_SAFE" ;; *) ax_pthread_check_macro="--" ;; esac AS_IF([test "x$ax_pthread_check_macro" = "x--"], [ax_pthread_check_cond=0], [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) # Are we compiling with Clang? AC_CACHE_CHECK([whether $CC is Clang], [ax_cv_PTHREAD_CLANG], [ax_cv_PTHREAD_CLANG=no # Note that Autoconf sets GCC=yes for Clang as well as GCC if test "x$GCC" = "xyes"; then AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ # if defined(__clang__) && defined(__llvm__) AX_PTHREAD_CC_IS_CLANG # endif ], [ax_cv_PTHREAD_CLANG=yes]) fi ]) ax_pthread_clang="$ax_cv_PTHREAD_CLANG" ax_pthread_clang_warning=no # Clang needs special handling, because older versions handle the -pthread # option in a rather... idiosyncratic way if test "x$ax_pthread_clang" = "xyes"; then # Clang takes -pthread; it has never supported any other flag # (Note 1: This will need to be revisited if a system that Clang # supports has POSIX threads in a separate library. This tends not # to be the way of modern systems, but it's conceivable.) # (Note 2: On some systems, notably Darwin, -pthread is not needed # to get POSIX threads support; the API is always present and # active. We could reasonably leave PTHREAD_CFLAGS empty. But # -pthread does define _REENTRANT, and while the Darwin headers # ignore this macro, third-party headers might not.) PTHREAD_CFLAGS="-pthread" PTHREAD_LIBS= ax_pthread_ok=yes # However, older versions of Clang make a point of warning the user # that, in an invocation where only linking and no compilation is # taking place, the -pthread option has no effect ("argument unused # during compilation"). They expect -pthread to be passed in only # when source code is being compiled. # # Problem is, this is at odds with the way Automake and most other # C build frameworks function, which is that the same flags used in # compilation (CFLAGS) are also used in linking. Many systems # supported by AX_PTHREAD require exactly this for POSIX threads # support, and in fact it is often not straightforward to specify a # flag that is used only in the compilation phase and not in # linking. Such a scenario is extremely rare in practice. # # Even though use of the -pthread flag in linking would only print # a warning, this can be a nuisance for well-run software projects # that build with -Werror. So if the active version of Clang has # this misfeature, we search for an option to squash it. AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown # Create an alternate version of $ac_link that compiles and # links in two steps (.c -> .o, .o -> exe) instead of one # (.c -> exe), because the warning occurs only in the second # step ax_pthread_save_ac_link="$ac_link" ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" ax_pthread_save_CFLAGS="$CFLAGS" for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" ac_link="$ax_pthread_save_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [ac_link="$ax_pthread_2step_ac_link" AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], [break]) ]) done ac_link="$ax_pthread_save_ac_link" CFLAGS="$ax_pthread_save_CFLAGS" AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" ]) case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in no | unknown) ;; *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; esac fi # $ax_pthread_clang = yes if test "x$ax_pthread_ok" = "xno"; then for ax_pthread_try_flag in $ax_pthread_flags; do case $ax_pthread_try_flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -mt,pthread) AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) PTHREAD_CFLAGS="-mt" PTHREAD_LIBS="-lpthread" ;; -*) AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) PTHREAD_CFLAGS="$ax_pthread_try_flag" ;; pthread-config) AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) PTHREAD_LIBS="-l$ax_pthread_try_flag" ;; esac ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_LINK_IFELSE([AC_LANG_PROGRAM([#include # if $ax_pthread_check_cond # error "$ax_pthread_check_macro must be defined" # endif static void routine(void *a) { a = 0; } static void *start_routine(void *a) { return a; }], [pthread_t th; pthread_attr_t attr; pthread_create(&th, 0, start_routine, 0); pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_cleanup_pop(0) /* ; */])], [ax_pthread_ok=yes], []) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" AC_MSG_RESULT([$ax_pthread_ok]) AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = "xyes"; then ax_pthread_save_CFLAGS="$CFLAGS" ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_CACHE_CHECK([for joinable pthread attribute], [ax_cv_PTHREAD_JOINABLE_ATTR], [ax_cv_PTHREAD_JOINABLE_ATTR=unknown for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], [int attr = $ax_pthread_attr; return attr /* ; */])], [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], []) done ]) AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ test "x$ax_pthread_joinable_attr_defined" != "xyes"], [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$ax_cv_PTHREAD_JOINABLE_ATTR], [Define to necessary symbol if this constant uses a non-standard name on your system.]) ax_pthread_joinable_attr_defined=yes ]) AC_CACHE_CHECK([whether more special flags are required for pthreads], [ax_cv_PTHREAD_SPECIAL_FLAGS], [ax_cv_PTHREAD_SPECIAL_FLAGS=no case $host_os in solaris*) ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" ;; esac ]) AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ test "x$ax_pthread_special_flags_added" != "xyes"], [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" ax_pthread_special_flags_added=yes]) AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], [ax_cv_PTHREAD_PRIO_INHERIT], [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], [[int i = PTHREAD_PRIO_INHERIT;]])], [ax_cv_PTHREAD_PRIO_INHERIT=yes], [ax_cv_PTHREAD_PRIO_INHERIT=no]) ]) AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ test "x$ax_pthread_prio_inherit_defined" != "xyes"], [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) ax_pthread_prio_inherit_defined=yes ]) CFLAGS="$ax_pthread_save_CFLAGS" LIBS="$ax_pthread_save_LIBS" # More AIX lossage: compile with *_r variant if test "x$GCC" != "xyes"; then case $host_os in aix*) AS_CASE(["x/$CC"], [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], [#handle absolute path differently from PATH based program lookup AS_CASE(["x$CC"], [x/*], [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) ;; esac fi fi test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" AC_SUBST([PTHREAD_LIBS]) AC_SUBST([PTHREAD_CFLAGS]) AC_SUBST([PTHREAD_CC]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test "x$ax_pthread_ok" = "xyes"; then ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_POP ])dnl AX_PTHREAD ga-5.9.2/m4/ax_sys_weak_alias.m4000066400000000000000000000272471500715745200164100ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_sys_weak_alias.html # =========================================================================== # # SYNOPSIS # # AX_SYS_WEAK_ALIAS # # DESCRIPTION # # Determines whether weak aliases are supported on the system, and if so, # what scheme is used to declare them. Also checks to see if aliases can # cross object file boundaries, as some systems don't permit them to. # # Most systems permit something called a "weak alias" or "weak symbol." # These aliases permit a library to provide a stub form of a routine # defined in another library, thus allowing the first library to operate # even if the other library is not linked. This macro will check for # support of weak aliases, figure out what schemes are available, and # determine some characteristics of the weak alias support -- primarily, # whether a weak alias declared in one object file may be referenced from # another object file. # # There are four known schemes of declaring weak symbols; each scheme is # checked in turn, and the first one found is prefered. Note that only one # of the mentioned preprocessor macros will be defined! # # 1. Function attributes # # This scheme was first introduced by the GNU C compiler, and attaches # attributes to particular functions. It is among the easiest to use, and # so is the first one checked. If this scheme is detected, the # preprocessor macro HAVE_SYS_WEAK_ALIAS_ATTRIBUTE will be defined to 1. # This scheme is used as in the following code fragment: # # void __weakf(int c) # { # /* Function definition... */ # } # # void weakf(int c) __attribute__((weak, alias("__weakf"))); # # 2. #pragma weak # # This scheme is in use by many compilers other than the GNU C compiler. # It is also particularly easy to use, and fairly portable -- well, as # portable as these things get. If this scheme is detected first, the # preprocessor macro HAVE_SYS_WEAK_ALIAS_PRAGMA will be defined to 1. This # scheme is used as in the following code fragment: # # extern void weakf(int c); # #pragma weak weakf = __weakf # void __weakf(int c) # { # /* Function definition... */ # } # # 3. #pragma _HP_SECONDARY_DEF # # This scheme appears to be in use by the HP compiler. As it is rather # specialized, this is one of the last schemes checked. If it is the first # one detected, the preprocessor macro HAVE_SYS_WEAK_ALIAS_HPSECONDARY # will be defined to 1. This scheme is used as in the following code # fragment: # # extern void weakf(int c); # #pragma _HP_SECONDARY_DEF __weakf weakf # void __weakf(int c) # { # /* Function definition... */ # } # # 4. #pragma _CRI duplicate # # This scheme appears to be in use by the Cray compiler. As it is rather # specialized, it too is one of the last schemes checked. If it is the # first one detected, the preprocessor macro # HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE will be defined to 1. This scheme is # used as in the following code fragment: # # extern void weakf(int c); # #pragma _CRI duplicate weakf as __weakf # void __weakf(int c) # { # /* Function definition... */ # } # # In addition to the preprocessor macros listed above, if any scheme is # found, the preprocessor macro HAVE_SYS_WEAK_ALIAS will also be defined # to 1. # # Once a weak aliasing scheme has been found, a check will be performed to # see if weak aliases are honored across object file boundaries. If they # are, the HAVE_SYS_WEAK_ALIAS_CROSSFILE preprocessor macro is defined to # 1. # # This Autoconf macro also makes two substitutions. The first, WEAK_ALIAS, # contains the name of the scheme found (one of "attribute", "pragma", # "hpsecondary", or "criduplicate"), or "no" if no weak aliasing scheme # was found. The second, WEAK_ALIAS_CROSSFILE, is set to "yes" or "no" # depending on whether or not weak aliases may cross object file # boundaries. # # LICENSE # # Copyright (c) 2008 Kevin L. Mitchell # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 6 AU_ALIAS([KLM_SYS_WEAK_ALIAS], [AX_SYS_WEAK_ALIAS]) AC_DEFUN([AX_SYS_WEAK_ALIAS], [ # starting point: no aliasing scheme yet... ax_sys_weak_alias=no # Figure out what kind of aliasing may be supported... _AX_SYS_WEAK_ALIAS_ATTRIBUTE _AX_SYS_WEAK_ALIAS_PRAGMA _AX_SYS_WEAK_ALIAS_HPSECONDARY _AX_SYS_WEAK_ALIAS_CRIDUPLICATE # Do we actually support aliasing? AC_CACHE_CHECK([how to create weak aliases with $CC], [ax_cv_sys_weak_alias], [ax_cv_sys_weak_alias=$ax_sys_weak_alias]) # OK, set a #define AS_IF([test $ax_cv_sys_weak_alias != no], [ AC_DEFINE([HAVE_SYS_WEAK_ALIAS], 1, [Define this if your system can create weak aliases]) ]) # Can aliases cross object file boundaries? _AX_SYS_WEAK_ALIAS_CROSSFILE # OK, remember the results AC_SUBST([WEAK_ALIAS], [$ax_cv_sys_weak_alias]) AC_SUBST([WEAK_ALIAS_CROSSFILE], [$ax_cv_sys_weak_alias_crossfile]) ]) AC_DEFUN([_AX_SYS_WEAK_ALIAS_ATTRIBUTE], [ # Test whether compiler accepts __attribute__ form of weak aliasing AC_CACHE_CHECK([whether $CC accepts function __attribute__((weak,alias()))], [ax_cv_sys_weak_alias_attribute], [ # We add -Werror if it's gcc to force an error exit if the weak attribute # isn't understood AS_IF([test $GCC = yes], [ save_CFLAGS=$CFLAGS CFLAGS=-Werror]) # Try linking with a weak alias... AC_LINK_IFELSE([ AC_LANG_PROGRAM([ void __weakf(int c) {} void weakf(int c) __attribute__((weak, alias("__weakf")));], [weakf(0)])], [ax_cv_sys_weak_alias_attribute=yes], [ax_cv_sys_weak_alias_attribute=no]) # Restore original CFLAGS AS_IF([test $GCC = yes], [ CFLAGS=$save_CFLAGS]) ]) # What was the result of the test? AS_IF([test $ax_sys_weak_alias = no && test $ax_cv_sys_weak_alias_attribute = yes], [ ax_sys_weak_alias=attribute AC_DEFINE([HAVE_SYS_WEAK_ALIAS_ATTRIBUTE], 1, [Define this if weak aliases may be created with __attribute__]) ]) ]) AC_DEFUN([_AX_SYS_WEAK_ALIAS_PRAGMA], [ # Test whether compiler accepts #pragma form of weak aliasing AC_CACHE_CHECK([whether $CC supports @%:@pragma weak], [ax_cv_sys_weak_alias_pragma], [ # Try linking with a weak alias... AC_LINK_IFELSE([ AC_LANG_PROGRAM([ extern void weakf(int c); @%:@pragma weak weakf = __weakf void __weakf(int c) {}], [weakf(0)])], [ax_cv_sys_weak_alias_pragma=yes], [ax_cv_sys_weak_alias_pragma=no]) ]) # What was the result of the test? AS_IF([test $ax_sys_weak_alias = no && test $ax_cv_sys_weak_alias_pragma = yes], [ ax_sys_weak_alias=pragma AC_DEFINE([HAVE_SYS_WEAK_ALIAS_PRAGMA], 1, [Define this if weak aliases may be created with @%:@pragma weak]) ]) ]) AC_DEFUN([_AX_SYS_WEAK_ALIAS_HPSECONDARY], [ # Test whether compiler accepts _HP_SECONDARY_DEF pragma from HP... AC_CACHE_CHECK([whether $CC supports @%:@pragma _HP_SECONDARY_DEF], [ax_cv_sys_weak_alias_hpsecondary], [ # Try linking with a weak alias... AC_LINK_IFELSE([ AC_LANG_PROGRAM([ extern void weakf(int c); @%:@pragma _HP_SECONDARY_DEF __weakf weakf void __weakf(int c) {}], [weakf(0)])], [ax_cv_sys_weak_alias_hpsecondary=yes], [ax_cv_sys_weak_alias_hpsecondary=no]) ]) # What was the result of the test? AS_IF([test $ax_sys_weak_alias = no && test $ax_cv_sys_weak_alias_hpsecondary = yes], [ ax_sys_weak_alias=hpsecondary AC_DEFINE([HAVE_SYS_WEAK_ALIAS_HPSECONDARY], 1, [Define this if weak aliases may be created with @%:@pragma _HP_SECONDARY_DEF]) ]) ]) AC_DEFUN([_AX_SYS_WEAK_ALIAS_CRIDUPLICATE], [ # Test whether compiler accepts "_CRI duplicate" pragma from Cray AC_CACHE_CHECK([whether $CC supports @%:@pragma _CRI duplicate], [ax_cv_sys_weak_alias_criduplicate], [ # Try linking with a weak alias... AC_LINK_IFELSE([ AC_LANG_PROGRAM([ extern void weakf(int c); @%:@pragma _CRI duplicate weakf as __weakf void __weakf(int c) {}], [weakf(0)])], [ax_cv_sys_weak_alias_criduplicate=yes], [ax_cv_sys_weak_alias_criduplicate=no]) ]) # What was the result of the test? AS_IF([test $ax_sys_weak_alias = no && test $ax_cv_sys_weak_alias_criduplicate = yes], [ ax_sys_weak_alias=criduplicate AC_DEFINE([HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE], 1, [Define this if weak aliases may be created with @%:@pragma _CRI duplicate]) ]) ]) dnl Note: This macro is modeled closely on AC_LINK_IFELSE, and in fact dnl depends on some implementation details of that macro, particularly dnl its use of _AC_MSG_LOG_CONFTEST to log the failed test program and dnl its use of ac_link for running the linker. AC_DEFUN([_AX_SYS_WEAK_ALIAS_CROSSFILE], [ # Check to see if weak aliases can cross object file boundaries AC_CACHE_CHECK([whether $CC supports weak aliases across object file boundaries], [ax_cv_sys_weak_alias_crossfile], [ AS_IF([test $ax_cv_sys_weak_alias = no], [ax_cv_sys_weak_alias_crossfile=no], [ dnl Must build our own test files... # conftest1 contains our weak alias definition... cat >conftest1.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest1.$ac_ext cat >>conftest1.$ac_ext <<_ACEOF /* end confdefs.h. */ @%:@ifndef HAVE_SYS_WEAK_ALIAS_ATTRIBUTE extern void weakf(int c); @%:@endif @%:@if defined(HAVE_SYS_WEAK_ALIAS_PRAGMA) @%:@pragma weak weakf = __weakf @%:@elif defined(HAVE_SYS_WEAK_ALIAS_HPSECONDARY) @%:@pragma _HP_SECONDARY_DEF __weakf weakf @%:@elif defined(HAVE_SYS_WEAK_ALIAS_CRIDUPLICATE) @%:@pragma _CRI duplicate weakf as __weakf @%:@endif void __weakf(int c) {} @%:@ifdef HAVE_SYS_WEAK_ALIAS_ATTRIBUTE void weakf(int c) __attribute((weak, alias("__weakf"))); @%:@endif _ACEOF # And conftest2 contains our main routine that calls it cat >conftest2.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >> conftest2.$ac_ext cat >>conftest2.$ac_ext <<_ACEOF /* end confdefs.h. */ extern void weakf(int c); int main () { weakf(0); return 0; } _ACEOF # We must remove the object files (if any) ourselves... rm -f conftest2.$ac_objext conftest$ac_exeext # Change ac_link to compile *2* files together save_aclink=$ac_link ac_link=`echo "$ac_link" | \ sed -e 's/conftest\(\.\$ac_ext\)/conftest1\1 conftest2\1/'` dnl Substitute our own routine for logging the conftest m4_pushdef([_AC_MSG_LOG_CONFTEST], [echo "$as_me: failed program was:" >&AS_MESSAGE_LOG_FD echo ">>> conftest1.$ac_ext" >&AS_MESSAGE_LOG_FD sed "s/^/| /" conftest1.$ac_ext >&AS_MESSAGE_LOG_FD echo ">>> conftest2.$ac_ext" >&AS_MESSAGE_LOG_FD sed "s/^/| /" conftest2.$ac_ext >&AS_MESSAGE_LOG_FD ])dnl # Since we created the files ourselves, don't use SOURCE argument AC_LINK_IFELSE(, [ax_cv_sys_weak_alias_crossfile=yes], [ax_cv_sys_weak_alias_crossfile=no]) dnl Restore _AC_MSG_LOG_CONFTEST m4_popdef([_AC_MSG_LOG_CONFTEST])dnl # Restore ac_link ac_link=$save_aclink # We must remove the object files (if any) and C files ourselves... rm -f conftest1.$ac_ext conftest2.$ac_ext \ conftest1.$ac_objext conftest2.$ac_objext ]) ]) # What were the results of the test? AS_IF([test $ax_cv_sys_weak_alias_crossfile = yes], [ AC_DEFINE([HAVE_SYS_WEAK_ALIAS_CROSSFILE], 1, [Define this if weak aliases in other files are honored]) ]) ]) ga-5.9.2/m4/ga.m4000066400000000000000000000031271500715745200133000ustar00rootroot00000000000000############################################################################### # Look for Global Arrays ############################################################################### AC_DEFUN([GA_REQUIRE], [ AC_CACHE_CHECK([whether this package is part of GA distribution], [ga_cv_libga_la_found], [AS_IF([test -f "../libga.la"], [ga_cv_libga_la_found=yes], [ga_cv_libga_la_found=no])]) AS_IF([test "x$ga_cv_libga_la_found" = xyes], [GA_LIBS="../libga.la" GA_CPPFLAGS="-I$srcdir/../global/src -I../global/src -I$srcdir/../ma -I../ma -I../gaf2c -I../armci/gaf2c -I$srcdir/../armci/src/include" GA_FLIBS=`echo '@FLIBS@' | ../config.status --file=-`], [ga_save_PATH="$PATH" AS_IF([test -d $with_ga], [PATH="$with_ga:$PATH"]) AS_IF([test -d $with_ga/bin], [PATH="$with_ga/bin:$PATH"]) AC_PATH_PROG([GA_CONFIG], [ga-config]) PATH="$ga_save_PATH" AS_IF([test "x$GA_CONFIG" != x], [GA_CPPFLAGS=`$GA_CONFIG --cppflags` GA_LDFLAGS=`$GA_CONFIG --ldflags` GA_LIBS=`$GA_CONFIG --libs` GA_FLIBS=`$GA_CONFIG --flibs`], [GA_CHECK_PACKAGE([ga], [ga.h], [ga], [GA_Initialize], [$FLIBS], [GA_LIBS="-lga"], [AS_UNSET([ac_cv_search_GA_Initialize]) GA_CHECK_PACKAGE([ga], [ga.h], [global], [GA_Initialize], [-lma -larmci -llinalg $FLIBS], [GA_LIBS="-lglobal -lma -larmci -llinalg"], [AC_MSG_FAILURE([Could not locate Global Arrays 5.x nor 4.x])])])])]) AC_SUBST([GA_CPPFLAGS]) AC_SUBST([GA_LDFLAGS]) AC_SUBST([GA_LIBS]) AC_SUBST([GA_FLIBS]) ])dnl ga-5.9.2/m4/ga_64bit_flag.m4000066400000000000000000000017461500715745200153060ustar00rootroot00000000000000# GA_64BIT_FLAG # ------------- # Figure out whether the compiler needs a special flag to generate 64bit code. # We verify by testing the size of void*. We can't use AC_CHECK_SIZEOF # directly since it uses the cache. Instead we copied code from autoconf... # # Known flags: # -m64 GNU # -q64 IBM # AC_DEFUN([GA_64BIT_FLAG], [AC_CACHE_CHECK([for flag to indicate 64-bits], [ga_cv_64bit_flag], [AC_LANG_PUSH([C]) for flag in none -m64 -q64 +DD64 +DA2.0W -64 ; do ga_save_CFLAGS=$CFLAGS AS_IF([test "x$flag" != xnone], [CFLAGS="$CFLAGS $flag"]) ga_sizeof_voidp=0 AC_COMPUTE_INT([ga_sizeof_voidp], [(long int) (sizeof (void*))], [AC_INCLUDES_DEFAULT()], [ga_sizeof_voidp=0]) CFLAGS=$ga_save_CFLAGS AS_IF([test x$ga_sizeof_voidp = x8], [ga_cv_64bit_flag=$flag; break]) done AC_LANG_POP([C])]) AS_IF([test x$ga_cv_64bit_flag != xnone], [CFLAGS="$CFLAGS $ga_cv_64bit_flag" FFLAGS="$FFLAGS $ga_cv_64bit_flag"]) ]) # GA_64BIT_FLAG ga-5.9.2/m4/ga_ar.m4000066400000000000000000000007601500715745200137620ustar00rootroot00000000000000# GA_AR # ----- # Libtool doesn't advertise AR nor AR_FLAGS in case the user wishes to # override them. Further, certain systems require a different archiver. # RANLIB may also be affected. # Use this prior to LT_INIT. # # Known archivers: # ar - all known systems # AC_DEFUN([GA_AR], [ AC_ARG_VAR([AR], [archiver used by libtool (default: ar)]) AC_ARG_VAR([AR_FLAGS], [archiver flags used by libtool (default: cru)]) AC_ARG_VAR([RANLIB], [generates index to archive (default: ranlib)]) ])dnl ga-5.9.2/m4/ga_arg_parse.m4000066400000000000000000000067521500715745200153320ustar00rootroot00000000000000# GA_ARG_PARSE(ARG, VAR_LIBS, VAR_LDFLAGS, VAR_CPPFLAGS) # ------------------------------------------------------ # Parse whitespace-separated ARG into appropriate LIBS, LDFLAGS, and # CPPFLAGS variables. # some examples (not exhaustive): # $arg="/apps/mpi/hp/2.03.01.00/include/64 /apps/mpi/hp/2.03.01.00/lib/linux_amd64 -lmtmpi" # $arg="/usr/local" # $arg="-I/usr/local/include -L/usr/local/lib -lsomelib" # $arg="/usr/lib/mpich-shmem -lfmpich-shmem -lmpich-shmem -lpmpich-shmem" # $arg="/usr/lib/mpich-shmem/include /usr/lib/mpich-shmem/lib -lfmpich-shmem -lmpich-shmem -lpmpich-shmem" # $arg="/usr/local/lib64 /usr/local/include64" # # Special Cases: # -mkl[=arg] Intel compiler with special -mkl flag for headers and linking AC_DEFUN([GA_ARG_PARSE], [AC_COMPUTE_INT([ga_arg_parse_sizeof_voidp], [(long int) (sizeof (void*))]) for arg in $$1 ; do AS_CASE([$arg], [yes], [], [no], [], [-l*], [$2="$$2 $arg"], [-L*], [$3="$$3 $arg"], [-WL*], [$3="$$3 $arg"], [-Wl*], [$3="$$3 $arg"], [-I*], [$4="$$4 $arg"], [*.a], [$2="$$2 $arg"], [*.so], [$2="$$2 $arg"], [*lib], [AS_IF([test -d $arg], [$3="$$3 -L$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [*lib/], [AS_IF([test -d $arg], [$3="$$3 -L$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [*lib64], [AS_IF([test -d $arg], [$3="$$3 -L$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [*lib64/], [AS_IF([test -d $arg], [$3="$$3 -L$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [*include], [AS_IF([test -d $arg], [$4="$$4 -I$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [*include/], [AS_IF([test -d $arg], [$4="$$4 -I$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [*include64], [AS_IF([test -d $arg], [$4="$$4 -I$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [*include64/], [AS_IF([test -d $arg], [$4="$$4 -I$arg"], [AC_MSG_WARN([$arg of $1 not parsed])])], [-mkl*], [$2="$$2 $arg"], [ga_arg_parse_ok=no]) # $arg didn't fit the most common cases # check for subdirectories e.g. lib,include AS_IF([test "x$ga_arg_parse_ok" = xno], [AS_IF([test "x$ga_arg_parse_sizeof_voidp" = x8], [AS_IF([test -d $arg/lib64], [$3="$$3 -L$arg/lib64"; ga_arg_parse_ok=yes], [test -d $arg/lib], [$3="$$3 -L$arg/lib"; ga_arg_parse_ok=yes]) AS_IF([test -d $arg/include64],[$4="$$4 -I$arg/include64"; ga_arg_parse_ok=yes], [test -d $arg/include], [$4="$$4 -I$arg/include"; ga_arg_parse_ok=yes])], [AS_IF([test -d $arg/lib], [$3="$$3 -L$arg/lib"; ga_arg_parse_ok=yes]) AS_IF([test -d $arg/include], [$4="$$4 -I$arg/include"; ga_arg_parse_ok=yes])])]) # $arg still unknown, look for "lib" and "include" anywhere... AS_IF([test "x$ga_arg_parse_ok" = xno], [AS_CASE([$arg], [*lib*], [AS_IF([test -d $arg], [$3="$$3 -L$arg"; ga_arg_parse_ok=yes])], [*include*],[AS_IF([test -d $arg], [$4="$$4 -I$arg"; ga_arg_parse_ok=yes])])]) # warn user that $arg fell through AS_IF([test "x$ga_arg_parse_ok" = xno], [AC_MSG_WARN([$arg of $1 not parsed])]) done])dnl ga-5.9.2/m4/ga_armci_network.m4000066400000000000000000000505541500715745200162320ustar00rootroot00000000000000# _GA_ARMCI_NETWORK_WITH(KEY, DESCRIPTION) # -------------------------------------------------- # A helper macro for generating all of the AC_ARG_WITHs. # Also may establish value of ga_armci_network. # Counts how many armci networks were specified by user. AC_DEFUN([_GA_ARMCI_NETWORK_WITH], [ AC_ARG_WITH([$1], [AS_HELP_STRING([--with-$1[[=ARG]]], [select armci network as $2])]) AS_VAR_PUSHDEF([KEY], m4_toupper(m4_translit([$1], [-.], [__]))) AS_VAR_PUSHDEF([with_key], m4_translit([with_$1], [-.], [__])) dnl Can't have AM_CONDITIONAL here in case configure must find armci network dnl without user intervention. dnl AM_CONDITIONAL([ARMCI_NETWORK_]KEY, [test "x$with_key" != x]) AS_IF([test "x$with_key" != x], [GA_ARG_PARSE([with_key], [ARMCI_NETWORK_LIBS], [ARMCI_NETWORK_LDFLAGS], [ARMCI_NETWORK_CPPFLAGS])]) AS_IF([test "x$with_key" != xno && test "x$with_key" != x], [ga_armci_network=KEY AS_VAR_ARITH([armci_network_count], [$armci_network_count + 1])]) AS_VAR_POPDEF([KEY]) AS_VAR_POPDEF([with_key]) ])dnl # _GA_ARMCI_NETWORK_WARN(KEY) # --------------------------- # Helper macro for idicating value of armci network arguments. AC_DEFUN([_GA_ARMCI_NETWORK_WARN], [ AS_VAR_PUSHDEF([with_key], m4_translit([with_$1], [-.], [__])) AS_IF([test "x$with_key" != x && test "x$with_key" != xno], [AC_MSG_WARN([--with-$1=$with_key])]) AS_VAR_POPDEF([with_key]) ])dnl # _GA_ARMCI_NETWORK_AM_CONDITIONAL(KEY) #-------------------------------------- # Helper macro for generating all AM_CONDITIONALs. AC_DEFUN([_GA_ARMCI_NETWORK_AM_CONDITIONAL], [ AS_VAR_PUSHDEF([KEY], m4_toupper(m4_translit([$1], [-.], [__]))) AS_VAR_PUSHDEF([with_key], m4_translit([with_$1], [-.], [__])) AM_CONDITIONAL([ARMCI_NETWORK_]KEY, [test "x$with_key" != x && test "x$with_key" != xno]) AS_VAR_POPDEF([KEY]) AS_VAR_POPDEF([with_key]) ])dnl # _GA_ARMCI_NETWORK_ARMCI([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ---------------------------------------------------------------- AC_DEFUN([_GA_ARMCI_NETWORK_ARMCI], [ AC_MSG_NOTICE([searching for external ARMCI...]) happy=yes CPPFLAGS="$CPPFLAGS $GA_MP_CPPFLAGS" LDFLAGS="$LDFLAGS $GA_MP_LDFLAGS" LIBS="$LIBS $GA_MP_LIBS" AS_IF([test "x$happy" = xyes], [AC_CHECK_HEADER([armci.h], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([ARMCI_Init], [armci], [], [happy=no]) AS_CASE([$ac_cv_search_ARMCI_Init], ["none required"], [], [no], [], [# add missing lib to ARMCI_NETWORK_LIBS if not there AS_CASE([$ARMCI_NETWORK_LIBS], [*$ac_cv_search_ARMCI_Init*], [], [ARMCI_NETWORK_LIBS="$ARMCI_NETWORK_LIBS $ac_cv_search_ARMCI_Init"])])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([armci_group_comm], [armci]) AS_IF([test "x$ac_cv_search_armci_group_comm" != xno], [ac_cv_search_armci_group_comm=1], [ac_cv_search_armci_group_comm=0]) AC_DEFINE_UNQUOTED([HAVE_ARMCI_GROUP_COMM], [$ac_cv_search_armci_group_comm], [set to 1 if ARMCI has armci_group_comm function]) ]) AS_IF([test "x$happy" = xyes], [AC_CHECK_MEMBER([ARMCI_Group.comm], [], [], [[#include ]]) AS_IF([test "x$ac_cv_member_ARMCI_Group_comm" != xno], [ac_cv_member_ARMCI_Group_comm=1], [ac_cv_member_ARMCI_Group_comm=0]) AC_DEFINE_UNQUOTED([HAVE_ARMCI_GROUP_COMM_MEMBER], [$ac_cv_member_ARMCI_Group_comm], [set to 1 if ARMCI has ARMCI_Group.comm member]) ]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([ARMCI_Initialized], [armci]) AS_IF([test "x$ac_cv_search_ARMCI_Initialized" != xno], [ac_cv_search_ARMCI_Initialized=1], [ac_cv_search_ARMCI_Initialized=0]) AC_DEFINE_UNQUOTED([HAVE_ARMCI_INITIALIZED], [$ac_cv_search_ARMCI_Initialized], [set to 1 if ARMCI has ARMCI_Initialized function]) ]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([armci_stride_info_init], [armci]) AS_IF([test "x$ac_cv_search_armci_stride_info_init" != xno], [ac_cv_search_armci_stride_info_init=1], [ac_cv_search_armci_stride_info_init=0]) AC_DEFINE_UNQUOTED([HAVE_ARMCI_STRIDE_INFO_INIT], [$ac_cv_search_armci_stride_info_init], [set to 1 if ARMCI has armci_stride_info_init function]) ]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([armci_notify], [armci]) AS_IF([test "x$ac_cv_search_armci_notify" != xno], [ac_cv_search_armci_notify=1], [ac_cv_search_armci_notify=0]) AC_DEFINE_UNQUOTED([HAVE_ARMCI_NOTIFY], [$ac_cv_search_armci_notify], [set to 1 if ARMCI has armci_notify function]) ]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([armci_msg_init], [armci]) AS_IF([test "x$ac_cv_search_armci_msg_init" != xno], [ac_cv_search_armci_msg_init=1], [ac_cv_search_armci_msg_init=0]) AC_DEFINE_UNQUOTED([HAVE_ARMCI_MSG_INIT], [$ac_cv_search_armci_msg_init], [set to 1 if ARMCI has armci_msg_init function]) ]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([armci_msg_finalize], [armci]) AS_IF([test "x$ac_cv_search_armci_msg_finalize" != xno], [ac_cv_search_armci_msg_finalize=1], [ac_cv_search_armci_msg_finalize=0]) AC_DEFINE_UNQUOTED([HAVE_ARMCI_MSG_FINALIZE], [$ac_cv_search_armci_msg_finalize], [set to 1 if ARMCI has armci_msg_finalize function]) ]) AM_CONDITIONAL([HAVE_ARMCI_GROUP_COMM], [test "x$ac_cv_search_armci_group_comm" = x1]) AM_CONDITIONAL([HAVE_ARMCI_GROUP_COMM_MEMBER], [test "x$ac_cv_member_ARMCI_Group_comm" = x1]) AM_CONDITIONAL([HAVE_ARMCI_INITIALIZED], [test "x$ac_cv_search_ARMCI_Initialized" = x1]) AM_CONDITIONAL([HAVE_ARMCI_STRIDE_INFO_INIT], [test "x$ac_cv_search_armci_stride_info_init" = x1]) AM_CONDITIONAL([HAVE_ARMCI_NOTIFY], [test "x$ac_cv_search_armci_notify" = x1]) AM_CONDITIONAL([HAVE_ARMCI_MSG_INIT], [test "x$ac_cv_search_armci_msg_init" = x1]) AM_CONDITIONAL([HAVE_ARMCI_MSG_FINALIZE], [test "x$ac_cv_search_armci_msg_finalize" = x1]) AS_IF([test "x$happy" = xyes], [ga_armci_network=ARMCI; with_armci=yes; armci_network_external=1; $1], [armci_network_external=0; $2]) ])dnl # _GA_ARMCI_NETWORK_MPI_TS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------- AC_DEFUN([_GA_ARMCI_NETWORK_MPI_TS], [ AC_MSG_NOTICE([searching for MPI_TS...]) happy=yes AS_IF([test "x$happy" = xyes], [ga_armci_network=MPI_TS; with_mpi_ts=yes; $1], [$2]) ])dnl # _GA_ARMCI_NETWORK_MPI_MT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------- AC_DEFUN([_GA_ARMCI_NETWORK_MPI_MT], [ AC_MSG_NOTICE([searching for MPI_MT...]) happy=yes AS_IF([test "x$happy" = xyes], [ga_armci_network=MPI_MT; with_mpi_mt=yes; $1], [$2]) ])dnl # _GA_ARMCI_NETWORK_MPI_PT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------- AC_DEFUN([_GA_ARMCI_NETWORK_MPI_PT], [ AC_MSG_NOTICE([searching for MPI_PT...]) happy=yes AS_IF([test "x$happy" = xyes], [ga_armci_network=MPI_PT; with_mpi_pt=yes; $1], [$2]) ])dnl # _GA_ARMCI_NETWORK_MPI_PR([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------- AC_DEFUN([_GA_ARMCI_NETWORK_MPI_PR], [ AC_MSG_NOTICE([searching for MPI_PR...]) happy=yes AS_IF([test "x$happy" = xyes], [ga_armci_network=MPI_PR; with_mpi_pr=yes; $1], [$2]) ])dnl # _GA_ARMCI_NETWORK_MPI3([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------- AC_DEFUN([_GA_ARMCI_NETWORK_MPI3], [ AC_MSG_NOTICE([searching for MPI3...]) happy=yes AS_IF([test "x$happy" = xyes], [ga_armci_network=MPI3; with_mpi3=yes; $1], [$2]) ])dnl # _GA_ARMCI_NETWORK_MPI_SPAWN([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------------- AC_DEFUN([_GA_ARMCI_NETWORK_MPI_SPAWN], [ AC_MSG_NOTICE([searching for MPI_SPAWN...]) happy=yes CPPFLAGS="$CPPFLAGS $GA_MP_CPPFLAGS" LDFLAGS="$LDFLAGS $GA_MP_LDFLAGS" LIBS="$LIBS $GA_MP_LIBS" AS_IF([test "x$happy" = xyes], [AC_CHECK_HEADER([mpi.h], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([MPI_Comm_spawn_multiple], [mpi mpich.cnk mpich.rts], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [ga_armci_network=MPI_SPAWN; with_mpi_spawn=yes; $1], [$2]) ])dnl # _GA_ARMCI_NETWORK_OFA([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # --------------------------------------------------------------- AC_DEFUN([_GA_ARMCI_NETWORK_OFA], [ AC_MSG_NOTICE([searching for OFA...]) happy=yes AS_IF([test "x$happy" = xyes], [AC_CHECK_HEADER([infiniband/verbs.h], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([ibv_open_device], [ibverbs], [], [happy=no]) AS_CASE([$ac_cv_search_ibv_open_device], ["none required"], [], [no], [], [ARMCI_NETWORK_LIBS="$ARMCI_NETWORK_LIBS $ac_cv_search_ibv_open_device"])]) AS_IF([test "x$happy" = xyes], [ga_armci_network=OFA; with_ofa=yes; $1], [$2]) ])dnl # _GA_ARMCI_NETWORK_OPENIB([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ------------------------------------------------------------------ AC_DEFUN([_GA_ARMCI_NETWORK_OPENIB], [ AC_MSG_NOTICE([searching for OPENIB...]) happy=yes AS_IF([test "x$happy" = xyes], [AC_CHECK_HEADER([infiniband/verbs.h], [], [happy=no])]) AS_IF([test "x$happy" = xyes], [AC_SEARCH_LIBS([ibv_open_device], [ibverbs], [], [happy=no]) AS_CASE([$ac_cv_search_ibv_open_device], ["none required"], [], [no], [], [ARMCI_NETWORK_LIBS="$ARMCI_NETWORK_LIBS $ac_cv_search_ibv_open_device"])]) AS_IF([test "x$happy" = xyes], [ga_armci_network=OPENIB; with_openib=yes; $1], [$2]) ])dnl # _GA_ARMCI_NETWORK_PORTALS4([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ------------------------------------------------------------------- AC_DEFUN([_GA_ARMCI_NETWORK_PORTALS4], [ AC_MSG_NOTICE([searching for PORTALS4...]) happy=yes AS_IF([test "x$happy" = xyes], [ga_armci_network=PORTALS4; with_portals4=yes; $1], [$2]) ])dnl # _GA_ARMCI_NETWORK_OFI([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ------------------------------------------------------------------- AC_DEFUN([_GA_ARMCI_NETWORK_OFI], [ AC_MSG_NOTICE([searching for OFI...]) happy=yes AS_IF([test "x$happy" = xyes], [ga_armci_network=OFI; with_ofi=yes; $1], [$2]) ])dnl # GA_ARMCI_NETWORK # ---------------- # This macro allows user to choose the armci network but also allows the # network to be tested for automatically. AC_DEFUN([GA_ARMCI_NETWORK], [ # Clear the variables we will be using, just in case. ga_armci_network= ARMCI_NETWORK_LIBS= ARMCI_NETWORK_LDFLAGS= ARMCI_NETWORK_CPPFLAGS= AC_ARG_ENABLE([autodetect], [AS_HELP_STRING([--enable-autodetect], [attempt to locate ARMCI_NETWORK besides SOCKETS])]) # First, all of the "--with" stuff is taken care of. armci_network_external=0 armci_network_count=0 _GA_ARMCI_NETWORK_WITH([armci], [external; path to external ARMCI library]) _GA_ARMCI_NETWORK_WITH([mpi-mt], [(Comex) MPI-2 multi-threading]) _GA_ARMCI_NETWORK_WITH([mpi-pt], [(Comex) MPI-2 multi-threading with progress thread]) _GA_ARMCI_NETWORK_WITH([mpi-pr], [(Comex) MPI-1 two-sided with progress rank]) _GA_ARMCI_NETWORK_WITH([mpi-spawn], [MPI-2 dynamic process mgmt]) _GA_ARMCI_NETWORK_WITH([mpi-ts], [(Comex) MPI-1 two-sided]) _GA_ARMCI_NETWORK_WITH([mpi3], [(Comex) MPI-3 one-sided]) _GA_ARMCI_NETWORK_WITH([ofa], [(Comex) Infiniband OpenIB]) _GA_ARMCI_NETWORK_WITH([ofi], [(Comex) OFI]) _GA_ARMCI_NETWORK_WITH([openib], [Infiniband OpenIB]) _GA_ARMCI_NETWORK_WITH([portals4], [(Comex) Portals4]) _GA_ARMCI_NETWORK_WITH([sockets], [Ethernet TCP/IP]) # Temporarily add ARMCI_NETWORK_CPPFLAGS to CPPFLAGS. ga_save_CPPFLAGS="$CPPFLAGS"; CPPFLAGS="$CPPFLAGS $ARMCI_NETWORK_CPPFLAGS" # Temporarily add ARMCI_NETWORK_LDFLAGS to LDFLAGS. ga_save_LDFLAGS="$LDFLAGS"; LDFLAGS="$LDFLAGS $ARMCI_NETWORK_LDFLAGS" # Temporarily add ARMCI_NETWORK_LIBS to LIBS. ga_save_LIBS="$LIBS"; LIBS="$ARMCI_NETWORK_LIBS $LIBS" AS_IF([test "x$enable_autodetect" = xyes], [AC_MSG_NOTICE([searching for ARMCI_NETWORK...]) dnl AS_IF([test "x$ga_armci_network" = x && test "x$with_mpi_ts" != xno], dnl [_GA_ARMCI_NETWORK_MPI_TS()]) dnl AS_IF([test "x$ga_armci_network" = x && test "x$with_mpi_mt" != xno], dnl [_GA_ARMCI_NETWORK_MPI_MT()]) dnl AS_IF([test "x$ga_armci_network" = x && test "x$with_mpi_pt" != xno], dnl [_GA_ARMCI_NETWORK_MPI_PT()]) dnl AS_IF([test "x$ga_armci_network" = x && test "x$with_mpi_pr" != xno], dnl [_GA_ARMCI_NETWORK_MPI_PR()]) dnl AS_IF([test "x$ga_armci_network" = x && test "x$with_mpi3" != xno], dnl [_GA_ARMCI_NETWORK_MPI3()]) dnl AS_IF([test "x$ga_armci_network" = x && test "x$with_mpi_spawn" != xno], dnl [_GA_ARMCI_NETWORK_MPI_SPAWN()]) AS_IF([test "x$ga_armci_network" = x && test "x$with_ofa" != xno], [_GA_ARMCI_NETWORK_OFA()]) AS_IF([test "x$ga_armci_network" = x && test "x$with_openib" != xno], [_GA_ARMCI_NETWORK_OPENIB()]) AS_IF([test "x$ga_armci_network" = x && test "x$with_portals4" != xno], [_GA_ARMCI_NETWORK_PORTALS4()]) AS_IF([test "x$ga_armci_network" = x && test "x$with_armci" != xno], [_GA_ARMCI_NETWORK_ARMCI()]) AS_IF([test "x$ga_armci_network" = x && test "x$with_ofi" != xno], [_GA_ARMCI_NETWORK_OFI()]) AS_IF([test "x$ga_armci_network" = x], [AC_MSG_WARN([!!!]) AC_MSG_WARN([No ARMCI_NETWORK detected, defaulting to MPI_TS]) AC_MSG_WARN([!!!]) ga_armci_network=MPI_TS; with_mpi_ts=yes])], [# Not autodetecting # Check whether multiple armci networks were selected by user. AS_CASE([$armci_network_count], [0], [AC_MSG_WARN([No ARMCI_NETWORK specified, defaulting to MPI_TS]) ga_armci_network=MPI_TS; with_mpi_ts=yes], [1], [AS_IF([test "x$ga_armci_network" = xARMCI], [_GA_ARMCI_NETWORK_ARMCI([], [AC_MSG_ERROR([test for ARMCI_NETWORK=ARMCI failed])])]) AS_IF([test "x$ga_armci_network" = xMPI_TS], [_GA_ARMCI_NETWORK_MPI_TS([], [AC_MSG_ERROR([test for ARMCI_NETWORK=MPI_TS failed])])]) AS_IF([test "x$ga_armci_network" = xMPI_MT], [_GA_ARMCI_NETWORK_MPI_MT([], [AC_MSG_ERROR([test for ARMCI_NETWORK=MPI_MT failed])])]) AS_IF([test "x$ga_armci_network" = xMPI_PT], [_GA_ARMCI_NETWORK_MPI_PT([], [AC_MSG_ERROR([test for ARMCI_NETWORK=MPI_PT failed])])]) AS_IF([test "x$ga_armci_network" = xMPI_PR], [_GA_ARMCI_NETWORK_MPI_PR([], [AC_MSG_ERROR([test for ARMCI_NETWORK=MPI_PR failed])])]) AS_IF([test "x$ga_armci_network" = xMPI3], [_GA_ARMCI_NETWORK_MPI3([], [AC_MSG_ERROR([test for ARMCI_NETWORK=MPI3 failed])])]) AS_IF([test "x$ga_armci_network" = xMPI_SPAWN], [_GA_ARMCI_NETWORK_MPI_SPAWN([], [AC_MSG_ERROR([test for ARMCI_NETWORK=MPI_SPAWN failed])])]) AS_IF([test "x$ga_armci_network" = xOFA], [_GA_ARMCI_NETWORK_OFA([], [AC_MSG_ERROR([test for ARMCI_NETWORK=OFA failed])])]) AS_IF([test "x$ga_armci_network" = xOPENIB], [_GA_ARMCI_NETWORK_OPENIB([], [AC_MSG_ERROR([test for ARMCI_NETWORK=OPENIB failed])])]) AS_IF([test "x$ga_armci_network" = xPORTALS4], [_GA_ARMCI_NETWORK_PORTALS4([], [AC_MSG_ERROR([test for ARMCI_NETWORK=PORTALS4 failed])])]) AS_IF([test "x$ga_armci_network" = xOFI], [_GA_ARMCI_NETWORK_OFI([], [AC_MSG_ERROR([test for ARMCI_NETWORK=OFI failed])])]) ], [AC_MSG_WARN([too many armci networks specified: $armci_network_count]) AC_MSG_WARN([the following were specified:]) _GA_ARMCI_NETWORK_WARN([armci]) _GA_ARMCI_NETWORK_WARN([mpi-ts]) _GA_ARMCI_NETWORK_WARN([mpi-mt]) _GA_ARMCI_NETWORK_WARN([mpi-pt]) _GA_ARMCI_NETWORK_WARN([mpi-pr]) _GA_ARMCI_NETWORK_WARN([mpi-spawn]) _GA_ARMCI_NETWORK_WARN([mpi3]) _GA_ARMCI_NETWORK_WARN([ofa]) _GA_ARMCI_NETWORK_WARN([openib]) _GA_ARMCI_NETWORK_WARN([portals4]) _GA_ARMCI_NETWORK_WARN([ofi]) _GA_ARMCI_NETWORK_WARN([sockets]) AC_MSG_ERROR([please select only one armci network])])]) # Remove ARMCI_NETWORK_CPPFLAGS from CPPFLAGS. CPPFLAGS="$ga_save_CPPFLAGS" # Remove ARMCI_NETWORK_LDFLAGS from LDFLAGS. LDFLAGS="$ga_save_LDFLAGS" # Remove ARMCI_NETWORK_LIBS from LIBS. LIBS="$ga_save_LIBS" _GA_ARMCI_NETWORK_AM_CONDITIONAL([armci]) _GA_ARMCI_NETWORK_AM_CONDITIONAL([mpi-ts]) _GA_ARMCI_NETWORK_AM_CONDITIONAL([mpi-mt]) _GA_ARMCI_NETWORK_AM_CONDITIONAL([mpi-pt]) _GA_ARMCI_NETWORK_AM_CONDITIONAL([mpi-pr]) _GA_ARMCI_NETWORK_AM_CONDITIONAL([mpi-spawn]) _GA_ARMCI_NETWORK_AM_CONDITIONAL([mpi3]) _GA_ARMCI_NETWORK_AM_CONDITIONAL([ofa]) _GA_ARMCI_NETWORK_AM_CONDITIONAL([openib]) _GA_ARMCI_NETWORK_AM_CONDITIONAL([portals4]) _GA_ARMCI_NETWORK_AM_CONDITIONAL([ofi]) _GA_ARMCI_NETWORK_AM_CONDITIONAL([sockets]) AC_SUBST([ARMCI_NETWORK_LDFLAGS]) AC_SUBST([ARMCI_NETWORK_LIBS]) AC_SUBST([ARMCI_NETWORK_CPPFLAGS]) # permanent hack AS_CASE([$ga_armci_network], [MPI_MT], [ARMCI_SRC_DIR=comex], [MPI_PT], [ARMCI_SRC_DIR=comex], [MPI_PR], [ARMCI_SRC_DIR=comex], [MPI_TS], [ARMCI_SRC_DIR=comex], [MPI3], [ARMCI_SRC_DIR=comex], [OFA], [ARMCI_SRC_DIR=comex], [OFI], [ARMCI_SRC_DIR=comex], [OPENIB], [ARMCI_SRC_DIR=src], [PORTALS4], [ARMCI_SRC_DIR=comex], [ARMCI_SRC_DIR=src]) AC_SUBST([ARMCI_SRC_DIR]) AM_CONDITIONAL([ARMCI_SRC_DIR_COMEX], [test "x$ARMCI_SRC_DIR" = "xcomex"]) AM_CONDITIONAL([ARMCI_SRC_DIR_SRC], [test "x$ARMCI_SRC_DIR" = "xsrc"]) AS_IF([test "x$ARMCI_SRC_DIR" = "xcomex"], [armci_network_external=1]) AM_CONDITIONAL([ARMCI_NETWORK_EXTERNAL], [test "x$armci_network_external" = x1]) AM_CONDITIONAL([ARMCI_NETWORK_COMEX], [test "x$ARMCI_SRC_DIR" = "xcomex"]) ga_cv_sysv_hack=no # Only perform this hack for ARMCI build. AS_IF([test "x$ARMCI_TOP_BUILDDIR" != x], [ ga_cv_sysv_hack=yes AC_DEFINE([SYSV], [1], [Defined if we want this system to use SYSV shared memory]) ]) AM_CONDITIONAL([SYSV], [test x$ga_cv_sysv_hack = xyes]) # if not using external armci library, the following functions are always available AS_IF([test "x$ga_armci_network" != xARMCI], [AC_DEFINE([HAVE_ARMCI_GROUP_COMM], [1], []) AC_DEFINE([HAVE_ARMCI_INITIALIZED], [1], []) AC_DEFINE([HAVE_ARMCI_NOTIFY], [1], []) AC_DEFINE([HAVE_ARMCI_MSG_INIT], [1], []) AC_DEFINE([HAVE_ARMCI_MSG_FINALIZE], [1], [])]) AM_CONDITIONAL([HAVE_ARMCI_GROUP_COMM_MEMBER], [test "x$ac_cv_member_ARMCI_Group_comm" = x1]) AM_CONDITIONAL([HAVE_ARMCI_GROUP_COMM], [test "x$ga_armci_network" != xARMCI]) AM_CONDITIONAL([HAVE_ARMCI_INITIALIZED], [test "x$ga_armci_network" != xARMCI]) AM_CONDITIONAL([HAVE_ARMCI_NOTIFY], [test "x$ga_armci_network" != xARMCI]) AM_CONDITIONAL([HAVE_ARMCI_MSG_INIT], [test "x$ga_armci_network" != xARMCI]) AM_CONDITIONAL([HAVE_ARMCI_MSG_FINALIZE],[test "x$ga_armci_network" != xARMCI]) # the armci iterators only available in the conglomerate sources AS_CASE([$ga_armci_network], [ARMCI], [], [AC_DEFINE([HAVE_ARMCI_STRIDE_INFO_INIT], [1], [])]) AM_CONDITIONAL([HAVE_ARMCI_STRIDE_INFO_INIT], [test "x$ga_armci_network" != xARMCI]) # ugly hack for working around NWChem memory requirements # and MPI_PR startup verus the 'classic' ARMCI startup delay_tcgmsg_mpi_startup=1 AS_CASE([$ga_armci_network], [ARMCI], [delay_tcgmsg_mpi_startup=0], [MPI_TS], [delay_tcgmsg_mpi_startup=0], [MPI_MT], [delay_tcgmsg_mpi_startup=0], [MPI_PT], [delay_tcgmsg_mpi_startup=0], [MPI_PR], [delay_tcgmsg_mpi_startup=0], [MPI_SPAWN], [delay_tcgmsg_mpi_startup=1], [MPI3], [delay_tcgmsg_mpi_startup=0], [OFA], [delay_tcgmsg_mpi_startup=0], [OFI], [delay_tcgmsg_mpi_startup=0], [OPENIB], [delay_tcgmsg_mpi_startup=1], [PORTALS4], [delay_tcgmsg_mpi_startup=0], [SOCKETS], [delay_tcgmsg_mpi_startup=1]) AC_DEFINE_UNQUOTED([NEED_DELAY_TCGMSG_MPI_STARTUP], [$delay_tcgmsg_mpi_startup], [whether to wait until the last moment to call ARMCI_Init() in TCGMSG-MPI]) ])dnl ga-5.9.2/m4/ga_blas.m4000066400000000000000000000256041500715745200143050ustar00rootroot00000000000000# GA_F77_BLAS_TEST # ---------------- # Generate Fortran 77 conftest for BLAS. AC_DEFUN([GA_F77_BLAS_TEST], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([], [[ implicit none INTEGER M, N, K, LDA, LDB, LDC COMPLEX CA(20,40), CB(20,30), CC(40,30), Calpha, Cbeta DOUBLE COMPLEX ZA(20,40), ZB(20,30), ZC(40,30), Zalpha, Zbeta REAL SA(20,40), SB(20,30), SC(40,30), Salpha, Sbeta DOUBLE PRECISION DA(20,40), DB(20,30), DC(40,30), Dalpha, Dbeta external CGEMM external ZGEMM external SGEMM external DGEMM M = 10 N = 20 K = 15 LDA = 20 LDB = 20 LDC = 40 Calpha = 2.0 Cbeta = 2.0 Zalpha = 2.0 Zbeta = 2.0 Salpha = 2.0 Sbeta = 2.0 Dalpha = 2.0 Dbeta = 2.0 CALL CGEMM ('T','N',M,N,K,Calpha,CA,LDA,CB,LDB,Cbeta,CC,LDC) CALL ZGEMM ('T','N',M,N,K,Zalpha,ZA,LDA,ZB,LDB,Zbeta,ZC,LDC) CALL SGEMM ('T','N',M,N,K,Salpha,SA,LDA,SB,LDB,Sbeta,SC,LDC) CALL DGEMM ('T','N',M,N,K,Dalpha,DA,LDA,DB,LDB,Dbeta,DC,LDC)]])]) ]) # GA_C_BLAS_TEST # -------------- # Generate C conftest for BLAS. AC_DEFUN([GA_C_BLAS_TEST], [AC_LANG_CONFTEST([AC_LANG_PROGRAM( [#ifdef __cplusplus extern "C" { #endif char cgemm (); char dgemm (); char sgemm (); char zgemm (); #ifdef __cplusplus } #endif ], [[char cresult = cgemm (); char dresult = dgemm (); char sresult = sgemm (); char zresult = zgemm (); ]])]) ]) # GA_C_RUN_BLAS_TEST # ------------------ # Test the C linker. # Clears BLAS_LIBS on failure. Sets ga_blas_ok=yes on success. AC_DEFUN([GA_C_RUN_BLAS_TEST], [ AC_LANG_PUSH([C]) GA_C_BLAS_TEST() AC_LINK_IFELSE([], [ga_blas_ok=yes], [BLAS_LIBS=]) AC_LANG_POP([C]) ])dnl # GA_F77_RUN_BLAS_TEST # -------------------- # Test the Fortran 77 linker. # Clears BLAS_LIBS on failure. Sets ga_blas_ok=yes on success. AC_DEFUN([GA_F77_RUN_BLAS_TEST], [ AC_LANG_PUSH([Fortran 77]) GA_F77_BLAS_TEST() AC_LINK_IFELSE([], [ga_blas_ok=yes], [BLAS_LIBS=]) AC_LANG_POP([Fortran 77]) ])dnl # GA_RUN_BLAS_TEST # ---------------- # Test the linker. # Clears BLAS_LIBS on failure. Sets ga_blas_ok=yes on success. AC_DEFUN([GA_RUN_BLAS_TEST], [ AS_IF([test "x$enable_f77" = xno], [AC_LANG_PUSH([C]) GA_C_BLAS_TEST() AC_LINK_IFELSE([], [ga_blas_ok=yes], [BLAS_LIBS=]) AC_LANG_POP([C])], [AC_LANG_PUSH([Fortran 77]) GA_F77_BLAS_TEST() AC_LINK_IFELSE([], [ga_blas_ok=yes], [BLAS_LIBS=]) AC_LANG_POP([Fortran 77])]) ])dnl # GA_BLAS([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # ------------------------------------------------- # Parse the --with-blas argument and test for all *gemm routines. We use # different tests depending on whether Fortran sources are enabled. There are # many flavors of BLAS that we test for explicitly, although the list could # probably be reduced based on currently available systems. # # Apparently certain compilers define sgemm and dgemm, so we must # test for a different BLAS routine. cgemm seems okay. AC_DEFUN([GA_BLAS], [AC_REQUIRE([AC_F77_LIBRARY_LDFLAGS]) blas_size=4 blas_size_hack=no AC_ARG_WITH([blas], [AS_HELP_STRING([--with-blas[[=ARG]]], [use external BLAS library; attempt to detect sizeof(INTEGER)])], [blas_size_hack=yes]) AC_ARG_WITH([blas4], [AS_HELP_STRING([--with-blas4[[=ARG]]], [use external BLAS library compiled with sizeof(INTEGER)==4])], [blas_size=4; with_blas="$with_blas4"]) AC_ARG_WITH([blas8], [AS_HELP_STRING([--with-blas8[[=ARG]]], [use external BLAS library compiled with sizeof(INTEGER)==8])], [blas_size=8; with_blas="$with_blas8"]) ga_blas_ok=no AS_IF([test "x$with_blas" = xno], [ga_blas_ok=skip]) # Parse --with-blas argument. Clear previous values first. BLAS_LIBS= BLAS_LDFLAGS= BLAS_CPPFLAGS= GA_ARG_PARSE([with_blas], [BLAS_LIBS], [BLAS_LDFLAGS], [BLAS_CPPFLAGS]) # Get fortran linker names of BLAS functions to check for. AC_F77_FUNC(cgemm) AC_F77_FUNC(dgemm) AC_F77_FUNC(sgemm) AC_F77_FUNC(zgemm) ga_save_LIBS="$LIBS" ga_save_LDFLAGS="$LDFLAGS"; LDFLAGS="$BLAS_LDFLAGS $LDFLAGS" ga_save_CPPFLAGS="$CPPFLAGS"; CPPFLAGS="$BLAS_CPPFLAGS $CPPFLAGS" AC_MSG_NOTICE([Attempting to locate BLAS library]) # First, check environment/command-line variables. # If failed, erase BLAS_LIBS but maintain BLAS_LDFLAGS and BLAS_CPPFLAGS. AS_IF([test $ga_blas_ok = no], [AC_MSG_CHECKING([for BLAS with user-supplied flags]) LIBS="$BLAS_LIBS $LIBS" GA_RUN_BLAS_TEST() LIBS="$ga_save_LIBS" AS_IF([test $ga_blas_ok = yes], [AS_IF([test $blas_size_hack = yes], [AS_CASE(["$BLAS_LIBS $LIBS $LDFLAGS $CPPFLAGS"], [*ilp64*], [blas_size=8], # Intel MKL [*_int64*], [blas_size=8])])]) # AMD ACML AC_MSG_RESULT([$ga_blas_ok])]) # AMD Core Math Library (ACML) AS_IF([test $ga_blas_ok = no], [AC_MSG_CHECKING([for BLAS in AMD Core Math Library]) # add -lacml to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*acml*], [], [BLAS_LIBS="-lacml"]) LIBS="$BLAS_LIBS $LIBS" GA_RUN_BLAS_TEST() LIBS="$ga_save_LIBS" AS_IF([test "x$ga_blas_ok" = xyes], [AS_IF([test $blas_size_hack = yes], [AS_CASE(["$BLAS_LIBS $LIBS $LDFLAGS $CPPFLAGS"], [*_int64*], [blas_size=8])])]) AC_MSG_RESULT([$ga_blas_ok])]) # Intel MKL library AS_IF([test $ga_blas_ok = no], [AC_MSG_CHECKING([for BLAS in Intel Math Kernel Library]) # add -lmkl to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*mkl*], [], [BLAS_LIBS="-lmkl"]) LIBS="$BLAS_LIBS $LIBS" GA_RUN_BLAS_TEST() LIBS="$ga_save_LIBS" AS_IF([test "x$ga_blas_ok" = xyes], [AS_IF([test $blas_size_hack = yes], [AS_CASE(["$BLAS_LIBS $LIBS $LDFLAGS $CPPFLAGS"], [*ilp64*], [blas_size=8])])]) AC_MSG_RESULT([$ga_blas_ok])]) # ATLAS library (http://math-atlas.sourceforge.net/) AS_IF([test $ga_blas_ok = no], [AC_MSG_CHECKING([for BLAS in ATLAS]) AS_IF([test "x$enable_f77" = xno], [# add -lcblas if needed but missing from LIBS AS_CASE([$LIBS], [*cblas*], [], [BLAS_LIBS="-lcblas"])], [# add -lf77blas if needed but missing from LIBS AS_CASE([$LIBS], [*f77blas*], [], [BLAS_LIBS="-lf77blas"])]) # add -latlas if needed but missing from LIBS AS_CASE([$LIBS], [*atlas*], [], [BLAS_LIBS="$BLAS_LIBS -latlas"]) LIBS="$BLAS_LIBS $LIBS" GA_RUN_BLAS_TEST() LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_blas_ok])]) # PhiPACK libraries (requires generic BLAS lib, too) AS_IF([test $ga_blas_ok = no], [AC_MSG_CHECKING([for BLAS in PhiPACK libraries]) # add -lblas to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*blas*], [], [BLAS_LIBS="-lblas"]) # add -ldgemm to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*dgemm*], [], [BLAS_LIBS="-ldgemm $BLAS_LIBS"]) # add -lsgemm to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*sgemm*], [], [BLAS_LIBS="-lsgemm $BLAS_LIBS"]) LIBS="$BLAS_LIBS $LIBS" GA_RUN_BLAS_TEST() LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_blas_ok])]) # Apple Accelerate.framework AS_IF([test $ga_blas_ok = no], [AC_MSG_CHECKING([for BLAS in Apple Accelerate.framework]) # add -framework Accelerate to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*Accelerate*], [], [BLAS_LIBS="-framework Accelerate"]) LIBS="$BLAS_LIBS $LIBS" GA_RUN_BLAS_TEST() LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_blas_ok])]) # Apple vecLib.framework AS_IF([test $ga_blas_ok = no], [AC_MSG_CHECKING([for BLAS in Apple vecLib.framework]) # add -framework vecLib to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*vecLib*], [], [BLAS_LIBS="-framework vecLib"]) LIBS="$BLAS_LIBS $LIBS" GA_RUN_BLAS_TEST() LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_blas_ok])]) # Alpha CXML library (CXML stands for Compaq Extended Math Library) AS_IF([test $ga_blas_ok = no], [AC_MSG_CHECKING([for BLAS in Alpha CXML library]) # add -lcxml to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*cxml*], [], [BLAS_LIBS="-lcxml"]) LIBS="$BLAS_LIBS $LIBS" GA_RUN_BLAS_TEST() LIBS="$ga_save_LIBS" AS_IF([test $ga_blas_ok = no], [# add -lcxml to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*cxml*], [], [BLAS_LIBS="-lcxml"]) # add -lcpml to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*cpml*], [], [BLAS_LIBS="$BLAS_LIBS -lcpml"]) LIBS="$BLAS_LIBS $LIBS" GA_RUN_BLAS_TEST() LIBS="$ga_save_LIBS"]) AC_MSG_RESULT([$ga_blas_ok])]) # Alpha DXML library (now called CXML, see above) # Sun Performance library AS_IF([test $ga_blas_ok = no], [AC_MSG_CHECKING([for BLAS in Sun Performance Library]) # add -xlic_lib=sunperf to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*sunperf*], [], [BLAS_LIBS="-xlic_lib=sunperf"]) LIBS="$BLAS_LIBS $LIBS" GA_RUN_BLAS_TEST() LIBS="$ga_save_LIBS" AS_IF([test $ga_blas_ok = no], [# add -xlic_lib=sunperf to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*sunperf*], [], [BLAS_LIBS="-xlic_lib=sunperf"]) # add -lsunmath to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*sunmath*], [], [BLAS_LIBS="$BLAS_LIBS -lsunmath"]) LIBS="$BLAS_LIBS $LIBS" GA_RUN_BLAS_TEST() LIBS="$ga_save_LIBS"]) AC_MSG_RESULT([$ga_blas_ok])]) # IBM ESSL library (might require generic BLAS lib, too) AS_IF([test $ga_blas_ok = no], [AC_MSG_CHECKING([for BLAS in IBM ESSL library]) # add -lessl to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*essl*], [], [BLAS_LIBS="-lessl"]) LIBS="$BLAS_LIBS $LIBS" GA_RUN_BLAS_TEST() LIBS="$ga_save_LIBS" AS_IF([test $ga_blas_ok = no], [# add both -lessl and -lblas to BLAS_LIBS if missing from LIBS AS_CASE([$LIBS], [*essl*], [], [BLAS_LIBS="-lessl"]) AS_CASE([$LIBS], [*blas*], [], [BLAS_LIBS="$BLAS_LIBS -lblas"]) LIBS="$BLAS_LIBS $LIBS" GA_RUN_BLAS_TEST() LIBS="$ga_save_LIBS"]) AC_MSG_RESULT([$ga_blas_ok])]) # Generic BLAS library AS_IF([test $ga_blas_ok = no], [AC_MSG_CHECKING([for BLAS in generic library]) BLAS_LIBS="-lblas" LIBS="$BLAS_LIBS $LIBS" GA_RUN_BLAS_TEST() LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_blas_ok])]) CPPFLAGS="$ga_save_CPPFLAGS" LDFLAGS="$ga_save_LDFLAGS" AC_SUBST([BLAS_LIBS]) AC_SUBST([BLAS_LDFLAGS]) AC_SUBST([BLAS_CPPFLAGS]) # Tests are complete. Execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: AS_IF([test $ga_blas_ok = yes], [have_blas=1 $1], [AC_MSG_WARN([BLAS library not found, using internal BLAS]) blas_size=$ga_cv_f77_integer_size # reset blas integer size to desired have_blas=0 $2]) AC_SUBST([have_blas]) AC_DEFINE_UNQUOTED([HAVE_BLAS], [$have_blas], [Define to 1 if using external BLAS library]) AC_SUBST([blas_size]) AC_DEFINE_UNQUOTED([BLAS_SIZE], [$blas_size], [Define to sizeof(INTEGER) used to compile BLAS]) AM_CONDITIONAL([HAVE_BLAS], [test $ga_blas_ok = yes]) ])dnl GA_BLAS ga-5.9.2/m4/ga_boost.m4000066400000000000000000000032001500715745200144760ustar00rootroot00000000000000# use AX_BOOST_BASE first, then try to find boost headers using other means AC_DEFUN([GA_BOOST], [AX_BOOST_BASE([1.35.0], [boost_ok=yes], [boost_ok=no]) # could not find boost install # try $ac_boost_path or $BOOST_ROOT as CPPFLAG AS_IF([test "x$boost_ok" = xno], [AC_CACHE_CHECK( [for non-installed and non-staged boost headers], [ga_cv_boost_headers], [gfutex_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I$ac_boost_path" AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[@%:@include ]], [[#if BOOST_VERSION >= $WANT_BOOST_VERSION // Everything is okay #else # error Boost version is too old #endif]])], [ga_cv_boost_headers="$ac_boost_path"]) CPPFLAGS="$gfutex_save_CPPFLAGS" AS_IF([test "x$ga_cv_boost_headers" = x], [gfutex_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I$BOOST_ROOT" AC_COMPILE_IFELSE([AC_LANG_PROGRAM( [[@%:@include ]], [[#if BOOST_VERSION >= $WANT_BOOST_VERSION // Everything is okay #else # error Boost version is too old #endif]])], [ga_cv_boost_headers="$BOOST_ROOT"]) CPPFLAGS="$gfutex_save_CPPFLAGS"]) AS_IF([test "x$ga_cv_boost_headers" = x], [ga_cv_boost_headers="no"])]) AS_IF([test "x$ga_cv_boost_headers" = xno], [AC_MSG_ERROR([could not locate boost headers])], [BOOST_CPPFLAGS="-I$ga_cv_boost_headers"]) AC_SUBST([BOOST_CPPFLAGS])]) ]) ga-5.9.2/m4/ga_c_match_types.m4000066400000000000000000000015011500715745200161740ustar00rootroot00000000000000# GA_C_POINTER_AS_INTEGER() # ------------------------- # Size-based match between C types. AC_DEFUN([GA_C_POINTER_AS_INTEGER], [AC_CACHE_CHECK([for smallest C integer matching void*], [ga_cv_c_pointer_as_integer], [AS_IF( [test "x$ac_cv_sizeof_voidp" = "x$ac_cv_sizeof_short"], [ga_cv_c_pointer_as_integer=short], [test "x$ac_cv_sizeof_voidp" = "x$ac_cv_sizeof_int"], [ga_cv_c_pointer_as_integer=int], [test "x$ac_cv_sizeof_voidp" = "x$ac_cv_sizeof_long"], [ga_cv_c_pointer_as_integer=long], [test "x$ac_cv_sizeof_voidp" = "x$ac_cv_sizeof_long_long"], [ga_cv_c_pointer_as_integer="long long"], [AC_MSG_ERROR( [Could not determine smallest C integer matching void*])])]) AC_SUBST([C_POINTER_AS_INTEGER], [$ga_cv_c_pointer_as_integer]) ])dnl ga-5.9.2/m4/ga_c_opt.m4000066400000000000000000000031421500715745200144610ustar00rootroot00000000000000# GA_C_OPT() # ---------- # Determine TARGET-/compiler-specific CFLAGS for optimization. AC_DEFUN([GA_C_OPT], [ AC_REQUIRE([GA_TARGET64]) AC_REQUIRE([GA_ENABLE_OPT]) AC_ARG_VAR([GA_COPT], [GA C optimization flags]) AC_CACHE_CHECK([for specific C optimizations], [ga_cv_c_opt], [ AS_IF([test "x$GA_COPT" != x], [ga_cv_c_opt="$GA_COPT"], [ga_cv_c_opt=]) AS_IF([test "x$ga_cv_c_opt" = x && test "x$enable_opt" = xyes], [ AS_CASE([$ga_cv_target:$ga_cv_c_compiler_vendor:$host_cpu], [CYGWIN:*:*], [ga_cv_c_opt=], [IBM64:*:*], [ga_cv_c_opt=], [IBM:*:*], [ga_cv_c_opt=], [LINUX64:fujitsu:x86_64], [ga_cv_c_opt="-Kfast"], [LINUX64:gnu:powerpc64], [ga_cv_c_opt="-funroll-loops"], [LINUX64:gnu:ppc64], [ga_cv_c_opt="-funroll-loops"], [LINUX64:gnu:x86_64], [ga_cv_c_opt="-O2 -funroll-loops"], [LINUX64:ibm:powerpc64], [ga_cv_c_opt="-qinline=100 -qstrict -qarch=auto -qtune=auto"], [LINUX64:ibm:ppc64], [ga_cv_c_opt="-qinline=100 -qstrict -qarch=auto -qtune=auto"], [LINUX64:ibm:x86_64], [ga_cv_c_opt=], [LINUX:fujitsu:*], [ga_cv_c_opt="-Kfast"], [LINUX:gnu:786], [ga_cv_c_opt="-O2 -funroll-loops -malign-double"], [LINUX:gnu:*], [ga_cv_c_opt="-O2 -funroll-loops"], [LINUX:gnu:x86], [ga_cv_c_opt="-O2 -funroll-loops -malign-double"], [LINUX:ibm:*], [ga_cv_c_opt="-q32"], [LINUX:intel:*], [ga_cv_c_opt="-O3 -prefetch"], [MACX64:*:*], [ga_cv_c_opt=], [MACX:*:*], [ga_cv_c_opt=], [ga_cv_c_opt=]) ])]) AC_SUBST([GA_COPT], [$ga_cv_c_opt]) ])dnl ga-5.9.2/m4/ga_check_func.m4000066400000000000000000000013261500715745200154470ustar00rootroot00000000000000# GA_CHECK_FUNCS(HEADER-FILE..., [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ------------------------------------------------------------------------ # Inspired by # http://pdh11.blogspot.com/2009/04/standard-macros-available-in-gnu.html # but really a modified version of AC_CHECK_FUNCS. AC_DEFUN([GA_CHECK_FUNCS], [m4_map_args_w([$1], [_AH_CHECK_FUNC(], [)])]dnl [AS_FOR([AC_func], [ac_func], [$1], [AC_CHECK_FUNC(AC_func, [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_]AC_func), 1, [Define to 1 if you have the `]AC_func[' function, 0 if you don't]) $2], [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_]AC_func), 0, [Define to 1 if you have the `]AC_func[' function, 0 if you don't]) $3], [$4])dnl]) ])# GA_CHECK_FUNCS ga-5.9.2/m4/ga_check_header.m4000066400000000000000000000014231500715745200157420ustar00rootroot00000000000000# GA_CHECK_HEADERS(HEADER-FILE..., # [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], [INCLUDES]) # ---------------------------------------------------------------------- # Inspired by # http://pdh11.blogspot.com/2009/04/standard-macros-available-in-gnu.html # but really a modified version of AC_CHECK_HEADERS. AC_DEFUN([GA_CHECK_HEADERS], [m4_map_args_w([$1], [_AH_CHECK_HEADER(], [)])]dnl [AS_FOR([AC_header], [ac_header], [$1], [AC_CHECK_HEADER(AC_header, [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_]AC_header), 1, [Define to 1 if you have the <]AC_header[> header file, 0 if you don't]) $2], [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_]AC_header), 0, [Define to 1 if you have the <]AC_header[> header file, 0 if you don't]) $3], [$4])dnl]) ])# GA_CHECK_HEADERS ga-5.9.2/m4/ga_check_libplot.m4000066400000000000000000000005561500715745200161650ustar00rootroot00000000000000# GA_CHECK_LIBPLOT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ---------------------------------------------------------- # Check for libplot. Thus far it's only used by tcgmsg. AC_DEFUN([GA_CHECK_LIBPLOT], [AC_CHECK_LIB([plot], [openpl], [PLOTLIB=-lplot AC_SUBST([PLOTLIB]) AC_DEFINE([HAVE_LIBPLOT], [1], [Defined if plot library is available])]) ])dnl ga-5.9.2/m4/ga_check_package.m4000066400000000000000000000030701500715745200161050ustar00rootroot00000000000000# GA_CHECK_PACKAGE(pkg, header, library, function, [extra-libs], # [action-if-found], [action-if-not-found]) # -------------------------------------------------------------- # AC_DEFUN([GA_CHECK_PACKAGE], [ AS_VAR_PUSHDEF([HAVE_PKG], m4_toupper(m4_translit([HAVE_$1], [-.], [__]))) AS_VAR_PUSHDEF([PKG_LIBS], m4_toupper(m4_translit([$1_LIBS], [-.], [__]))) AS_VAR_PUSHDEF([PKG_LDFLAGS], m4_toupper(m4_translit([$1_LDFLAGS], [-.], [__]))) AS_VAR_PUSHDEF([PKG_CPPFLAGS],m4_toupper(m4_translit([$1_CPPFLAGS], [-.], [__]))) AS_VAR_SET([PKG_LIBS],[]) AS_VAR_SET([PKG_LDFLAGS],[]) AS_VAR_SET([PKG_CPPFLAGS],[]) AC_ARG_WITH([$1], [AS_HELP_STRING([--with-$1[[=ARG]]], [specify location of $1 install and/or other flags])], [], [with_$1=yes]) AS_CASE([$with_$1], [yes], [], [no], [], [GA_ARG_PARSE( [with_$1], [PKG_LIBS], [PKG_LDFLAGS], [PKG_CPPFLAGS])]) # Check for header. ga_save_CPPFLAGS="$CPPFLAGS"; CPPFLAGS="$CPPFLAGS $PKG_CPPFLAGS" AC_CHECK_HEADER([$2], [], [$7]) CPPFLAGS="$ga_save_CPPFLAGS" # Check for library. ga_save_LIBS="$LIBS"; LIBS="$PKG_LIBS $LIBS" ga_save_LDFLAGS="$LDFLAGS"; LDFLAGS="$LDFLAGS $PKG_LDFLAGS" AC_SEARCH_LIBS([$4], [$3], [], [], [$5]) LIBS="$ga_save_LIBS" LDFLAGS="$ga_save_LDFLAGS" AS_IF([test "x$ac_cv_search_$4" != xno], [$6 AC_DEFINE([HAVE_PKG], [1], [set to 1 if we have the indicated package])], [$7]) AS_VAR_POPDEF([HAVE_PKG]) AS_VAR_POPDEF([PKG_LIBS]) AS_VAR_POPDEF([PKG_LDFLAGS]) AS_VAR_POPDEF([PKG_CPPFLAGS]) ])dnl ga-5.9.2/m4/ga_compiler_vendor.m4000066400000000000000000000041631500715745200165500ustar00rootroot00000000000000# GA_COMPILER_VENDOR # ------------------ # other compilers may define __GNUC__, like intel or pathscale # that's why we put it later in the for loop, like we do with our compiler # checks since GCC is so pervasive AC_DEFUN([GA_COMPILER_VENDOR], [ AS_VAR_PUSHDEF([ga_cv_compiler_vendor], [ga_cv_[]_AC_LANG_ABBREV[]_compiler_vendor]) AC_CACHE_CHECK([for _AC_LANG compiler vendor], [ga_cv_compiler_vendor], [ ga_save_ac_ext="$ac_ext" AC_LANG_CASE([Fortran], [ac_ext=F]) AC_LANG_CASE([Fortran 77], [ac_ext=F]) ga_cv_compiler_vendor=unknown ga_cpp_vendor_symbols= for vendor in intel ibm pathscale amd gnu sun hp borland comeau kai lcc metrowerks microsoft watcom portland fujitsu do AS_CASE([$vendor], [amd], [ga_cpp_vendor_symbols="defined(__OPEN64__)"], [borland], [ga_cpp_vendor_symbols="defined(__BORLANDC__) || defined(__TURBOC__)"], [comeau], [ga_cpp_vendor_symbols="defined(__COMO__)"], [fujitsu], [ga_cpp_vendor_symbols="defined(__fcc__) || defined(__fcc_version__) || defined(_FCC_VER) || defined(__FCC_VER_)"], [gnu], [ga_cpp_vendor_symbols="defined(__GNUC__)"], [hp], [ga_cpp_vendor_symbols="defined(__HP_cc) || defined(__HP_aCC)"], [ibm], [ga_cpp_vendor_symbols="defined(__xlc__) || defined(__xlC__) || defined(__IBMC__) || defined(__IBMCPP__)"], [intel], [ga_cpp_vendor_symbols="defined(__ICC) || defined(__ECC) || defined(__INTEL_COMPILER)"], [kai], [ga_cpp_vendor_symbols="defined(__KCC)"], [lcc], [ga_cpp_vendor_symbols="defined(__LCC__)"], [metrowerks],[ga_cpp_vendor_symbols="defined(__MWERKS__)"], [microsoft], [ga_cpp_vendor_symbols="defined(_MSC_VER)"], [pathscale], [ga_cpp_vendor_symbols="defined(__PATHCC__) || defined(__PATHSCALE__)"], [portland], [ga_cpp_vendor_symbols="defined(__PGI)"], [sun], [ga_cpp_vendor_symbols="defined(__SUNPRO_C) || defined(__SUNPRO_CC)"], [watcom], [ga_cpp_vendor_symbols="defined(__WATCOMC__)"]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[ #if !($ga_cpp_vendor_symbols) chokeonthis #endif ])], [ga_cv_compiler_vendor=$vendor; break]) done ga_cpp_vendor_symbols= ac_ext="$ga_save_ac_ext" ]) AS_VAR_POPDEF([ga_cv_compiler_vendor]) ])dnl ga-5.9.2/m4/ga_cross_compiling.m4000066400000000000000000000010671500715745200165530ustar00rootroot00000000000000# GA_CROSS_COMPILING # ------------------ # The standard check for whether we are cross compiling is not sufficient. # We know certain platforms are only cross compiled. Make the fix here. # This could be expanded later to avoid using GA_TARGET and instead perform # a more rigorous cross compiling test case. AC_DEFUN([GA_CROSS_COMPILING], [ AC_REQUIRE([GA_TARGET]) AC_CACHE_CHECK([whether we are cross compiling], [ga_cv_cross_compiling], [ga_cv_cross_compiling=$cross_compiling]) AM_CONDITIONAL([CROSS_COMPILING], [test "x$cross_compiling" = xyes]) ])dnl ga-5.9.2/m4/ga_cxx.m4000066400000000000000000000004511500715745200141570ustar00rootroot00000000000000# GA_CXX_ENABLE() # --------------- # Whether to enable C++ bindings. AC_DEFUN([GA_CXX_ENABLE], [AC_ARG_ENABLE([cxx], [AS_HELP_STRING([--enable-cxx],[build C++ interface])], [enable_cxx=yes], [enable_cxx=no]) AM_CONDITIONAL([CXX_BINDINGS],[test x$enable_cxx = xyes]) ])# GA_CXX_ENABLE ga-5.9.2/m4/ga_cxx_namespaces.m4000066400000000000000000000010511500715745200163530ustar00rootroot00000000000000# GA_CXX_NAMESPACES AC_DEFUN([GA_CXX_NAMESPACES], [AC_CACHE_CHECK([whether the compiler implements namespaces], [ga_cv_cxx_namespaces], [AC_LANG_PUSH([C++]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[namespace Outer { namespace Inner { int i = 0; }}]], [[using namespace Outer::Inner; return i;]])], [ga_cv_cxx_namespaces=yes], [ga_cv_cxx_namespaces=no]) AC_LANG_POP([C++])]) AS_IF([test x$ga_cv_cxx_namespaces = xyes], [AC_DEFINE([HAVE_NAMESPACES], [1], [define if the compiler implements namespaces])]) ])#GA_CXX_NAMESPACES ga-5.9.2/m4/ga_cxx_opt.m4000066400000000000000000000012161500715745200150410ustar00rootroot00000000000000# GA_CXX_OPT() # ------------ # Determine TARGET-/compiler-specific CXXFLAGS for optimization. AC_DEFUN([GA_CXX_OPT], [ AC_REQUIRE([GA_TARGET64]) AC_REQUIRE([GA_ENABLE_OPT]) AC_ARG_VAR([GA_CXXOPT], [GA C++ optimization flags]) AC_CACHE_CHECK([for specific C++ optimizations], [ga_cv_cxx_opt], [ AS_IF([test "x$GA_CXXOPT" != x], [ga_cv_cxx_opt="$GA_CXXOPT"], [ga_cv_cxx_opt=]) AS_IF([test "x$ga_cv_cxx_opt" = x && test "x$enable_opt" = xyes], [ AS_CASE([$ga_cv_target:$ga_cv_cxx_compiler_vendor:$host_cpu], [LINUX:*:*], [ga_cv_cxx_opt="-O0"], [ga_cv_cxx_opt=]) ])]) AC_SUBST([GA_CXXOPT], [$ga_cv_cxx_opt]) ])dnl ga-5.9.2/m4/ga_elpa.m4000066400000000000000000000241471500715745200143060ustar00rootroot00000000000000# GA_F77_ELPA_TEST # ---------------- # Generate Fortran 77 conftest for ELPA. AC_DEFUN([GA_F77_ELPA_TEST], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([], [[ use ELPA1 implicit none logical status integer i4 double precision dscal8,darray8(2) status = SOLVE_EVP_REAL(i4,i4,darray8,i4, C darray8,darray8,i4,i4,i4,i4)]])]) ]) # GA_C_ELPA_TEST # -------------- # Generate C conftest for ELPA. AC_DEFUN([GA_C_ELPA_TEST], [AC_LANG_CONFTEST([AC_LANG_PROGRAM( [#ifdef __cplusplus extern "C" { #endif char solve_evp_real (); #ifdef __cplusplus } #endif ], [[char result = solve_evp_real (); ]])]) ]) ## Generate Fortran 77 conftest for ELPA 2STAGE Algo for ELPA 2015 AC_DEFUN([GA_F77_ELPA_2015_TEST], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([], [[ use ELPA1 use ELPA2 implicit none logical status integer i4,mpierr double precision darray8(2) mpierr=get_elpa_communicators (i4, c i4,i4,i4,i4) status = SOLVE_EVP_REAL_2STAGE(i4,i4,darray8,i4, C darray8,darray8,i4,i4,i4,i4,i4,i4)]])]) ]) # GA_C_ELPA_TEST # -------------- # Generate C conftest for ELPA 2015 AC_DEFUN([GA_C_ELPA_2015_TEST], [AC_LANG_CONFTEST([AC_LANG_PROGRAM( [#ifdef __cplusplus extern "C" { #endif char solve_evp_real_2stage (); #ifdef __cplusplus } #endif ], [[char result = solve_evp_real_2stage (); ]])]) ]) ## Generate Fortran 77 conftest for elpa_solve_evp_real_double for ELPA 2016 AC_DEFUN([GA_F77_ELPA_2016_TEST], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([], [[ use ELPA1 use ELPA2 implicit none logical status integer i4,mpierr double precision darray8(2) mpierr=get_elpa_communicators (i4, c i4,i4,i4,i4) status = SOLVE_EVP_REAL_2STAGE_DOUBLE(i4,i4,darray8,i4, C darray8,darray8,i4,i4,i4,i4,i4,i4)]])]) ]) # GA_C_ELPA_TEST # -------------- # Generate C conftest for ELPA 2016 AC_DEFUN([GA_C_ELPA_2016_TEST], [AC_LANG_CONFTEST([AC_LANG_PROGRAM( [#ifdef __cplusplus extern "C" { #endif char solve_evp_real_2stage_double (); #ifdef __cplusplus } #endif ], [[char result = solve_evp_real_2stage_double (); ]])]) ]) ## Generate Fortran 77 conftest for elpa_solve_evp_real_double for ELPA 2017 AC_DEFUN([GA_F77_ELPA_2017_TEST], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([], [[ use ELPA implicit none logical status integer i4,success double precision dvec8(2),dmat8(2,2) class(elpa_t), pointer :: e if (elpa_init(20170403) /= ELPA_OK) then ! put here the version number of the API error stop "ELPA API version not supported" ! which you are using endif call e%set("na", i4,success) call e%eigenvectors(dmat8, dvec8, dmat8, success)]])]) ]) # GA_C_ELPA_TEST # -------------- # Generate C conftest for ELPA 2017 AC_DEFUN([GA_C_ELPA_2017_TEST], [AC_LANG_CONFTEST([AC_LANG_PROGRAM( [#include elpa_t handle; int error; double d8[2]; handle = elpa_allocate(&error); ], [[elpa_eigenvectors(handle, d8, d8, d8, &error); ]])]) ]) # GA_RUN_ELPA_TEST # ---------------- # Test the linker. # Clears ELPA_LIBS on failure. Sets ga_elpa_ok=yes on success. AC_DEFUN([GA_RUN_ELPA_TEST], [ AS_IF([test "x$enable_f77" = xno], [AC_LANG_PUSH([C]) GA_C_ELPA_TEST() AC_LINK_IFELSE([], [ga_elpa_ok=yes], [ELPA_LIBS=]) AC_LANG_POP([C])], [AC_LANG_PUSH([Fortran 77]) GA_F77_ELPA_TEST() AC_LINK_IFELSE([], [ga_elpa_ok=yes], [ELPA_LIBS=]) AC_LANG_POP([Fortran 77])]) ])dnl # GA_RUN_ELPA_2015_TEST # ---------------- # Test the linker. # Sets ga_elpa_ok=yes on success. AC_DEFUN([GA_RUN_ELPA_2015_TEST], [ AS_IF([test "x$enable_f77" = xno], [AC_LANG_PUSH([C]) GA_C_ELPA_2015_TEST() AC_LINK_IFELSE([], [ga_elpa_2015_ok=yes]) AC_LANG_POP([C])], [AC_LANG_PUSH([Fortran 77]) GA_F77_ELPA_2015_TEST() AC_LINK_IFELSE([], [ga_elpa_2015_ok=yes]) AC_LANG_POP([Fortran 77])]) ])dnl # GA_RUN_ELPA_2016_TEST # ---------------- # Test the linker. # Sets ga_elpa_ok=yes on success. AC_DEFUN([GA_RUN_ELPA_2016_TEST], [ AS_IF([test "x$enable_f77" = xno], [AC_LANG_PUSH([C]) GA_C_ELPA_2016_TEST() AC_LINK_IFELSE([], [ga_elpa_2016_ok=yes]) AC_LANG_POP([C])], [AC_LANG_PUSH([Fortran 77]) GA_F77_ELPA_2016_TEST() AC_LINK_IFELSE([], [ga_elpa_2016_ok=yes]) AC_LANG_POP([Fortran 77])]) ])dnl # GA_RUN_ELPA_2017_TEST # ---------------- # Test the linker. # Sets ga_elpa_ok=yes on success. AC_DEFUN([GA_RUN_ELPA_2017_TEST], [ AS_IF([test "x$enable_f77" = xno], [AC_LANG_PUSH([C]) GA_C_ELPA_2017_TEST() AC_LINK_IFELSE([], [ga_elpa_2017_ok=yes]) AC_LANG_POP([C])], [AC_LANG_PUSH([Fortran 77]) GA_F77_ELPA_2017_TEST() AC_LINK_IFELSE([], [ga_elpa_2017_ok=yes]) AC_LANG_POP([Fortran 77])]) ])dnl # GA_ELPA([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # ------------------------------------------------- # Modeled after GA_SCALAPACK. # Tries to find ELPA library. # ELPA depends on SCALAPACK, LAPACK, and BLAS. AC_DEFUN([GA_ELPA], [AC_REQUIRE([GA_SCALAPACK]) elpa_size=4 AC_ARG_WITH([elpa], [AS_HELP_STRING([--with-elpa=[[ARG]]], [use ELPA library compiled with sizeof(INTEGER)==4])], [elpa_size=4]) AC_ARG_WITH([elpa8], [AS_HELP_STRING([--with-elpa8=[[ARG]]], [use ELPA library compiled with sizeof(INTEGER)==8])], [elpa_size=8; with_elpa="$with_elpa8"]) ga_elpa_ok=no ga_elpa_2015_ok=no ga_elpa_2016_ok=no ga_elpa_2017_ok=no AS_IF([test "x$with_elpa" = xno], [ga_elpa_ok=skip]) AS_IF([test "x$with_elpa" = xno], [ga_elpa_2015_ok=skip]) AS_IF([test "x$with_elpa" = xno], [ga_elpa_2016_ok=skip]) AS_IF([test "x$with_elpa" = xno], [ga_elpa_2017_ok=skip]) # Parse --with-elpa argument. Clear previous values first. ELPA_LIBS= ELPA_LDFLAGS= ELPA_CPPFLAGS= GA_ARG_PARSE([with_elpa], [ELPA_LIBS], [ELPA_LDFLAGS], [ELPA_CPPFLAGS]) ga_save_LIBS="$LIBS" ga_save_LDFLAGS="$LDFLAGS" ga_save_CPPFLAGS="$CPPFLAGS" LDFLAGS="$ELPA_LDFLAGS $SCALAPACK_LDFLAGS $LAPACK_LDFLAGS $BLAS_LDFLAGS $GA_MP_LDFLAGS $LDFLAGS" CPPFLAGS="$ELPA_CPPFLAGS $SCALAPACK_CPPFLAGS $LAPACK_CPPFLAGS $BLAS_CPPFLAGS $GA_MP_CPPFLAGS $CPPFLAGS" # ELPA fortran test uses a module and needs CPPFLAGS # but CPPFLAGS isn't used with *.f non-preprocessed extension ga_save_FFLAGS="$FFLAGS" FFLAGS="$ELPA_CPPFLAGS $FFLAGS" AC_MSG_NOTICE([Attempting to locate ELPA library]) # First, check environment/command-line variables. # If failed, erase ELPA_LIBS but maintain ELPA_LDFLAGS and # ELPA_CPPFLAGS. # check elpa2017 first AS_IF([test $ga_elpa_2017_ok = no], [AC_MSG_CHECKING([for ELPA 2017 with user-supplied flags]) LIBS="$ELPA_LIBS $SCALAPACK_LIBS $LAPACK_LIBS $BLAS_LIBS $GA_MP_LIBS $LIBS" GA_RUN_ELPA_2017_TEST() LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_elpa_2017_ok])]) # check elpa2016 second AS_IF([test $ga_elpa_2017_ok = no && test $ga_elpa_2016_ok = no], [AC_MSG_CHECKING([for ELPA 2016 with user-supplied flags]) LIBS="$ELPA_LIBS $SCALAPACK_LIBS $LAPACK_LIBS $BLAS_LIBS $GA_MP_LIBS $LIBS" GA_RUN_ELPA_2016_TEST() LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_elpa_2016_ok])]) # check elpa2015 third AS_IF([test $ga_elpa_2017_ok = no && test $ga_elpa_2016_ok = no && test $ga_elpa_2015_ok = no], [AC_MSG_CHECKING([for ELPA 2015 with user-supplied flags]) LIBS="$ELPA_LIBS $SCALAPACK_LIBS $LAPACK_LIBS $BLAS_LIBS $GA_MP_LIBS $LIBS" GA_RUN_ELPA_2015_TEST() LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_elpa_2015_ok])]) # ga_elpa_2015_ok = yes implies ga_elpa_ok = yes AS_IF([test $ga_elpa_2015_ok = yes], [ga_elpa_ok=yes]) # ga_elpa_2016_ok = yes implies ga_elpa_ok = yes AS_IF([test $ga_elpa_2016_ok = yes], [ga_elpa_ok=yes]) # ga_elpa_2017_ok = yes implies ga_elpa_ok = yes AS_IF([test $ga_elpa_2017_ok = yes], [ga_elpa_ok=yes]) AS_IF([test $ga_elpa_ok = no], [AC_MSG_CHECKING([for ELPA with user-supplied flags]) LIBS="$ELPA_LIBS $SCALAPACK_LIBS $LAPACK_LIBS $BLAS_LIBS $GA_MP_LIBS $LIBS" GA_RUN_ELPA_TEST() LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_elpa_ok])]) # Generic ELPA library? AS_IF([test $ga_elpa_ok = no], [AC_MSG_CHECKING([for ELPA in generic library]) ELPA_LIBS="-lelpa" LIBS="$ELPA_LIBS $SCALAPACK_LIBS $LAPACK_LIBS $BLAS_LIBS $GA_MP_LIBS $LIBS" GA_RUN_ELPA_TEST() LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_elpa_ok])]) CPPFLAGS="$ga_save_CPPFLAGS" LDFLAGS="$ga_save_LDFLAGS" FFLAGS="$ga_save_FFLAGS" AC_SUBST([ELPA_LIBS]) AC_SUBST([ELPA_LDFLAGS]) AC_SUBST([ELPA_CPPFLAGS]) AS_IF([test "x$elpa_size" = x8], [AC_DEFINE([ELPA_I8], [1], [ELPA is using 8-byte integers])]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: AS_IF([test $ga_elpa_ok = yes], [have_elpa=1 $1], [AC_MSG_NOTICE([ELPA library not found, interfaces won't be defined]) have_elpa=0 $2]) AC_SUBST([have_elpa]) AC_DEFINE_UNQUOTED([HAVE_ELPA], [$have_elpa], [Define to 1 if you have ELPA library.]) AM_CONDITIONAL([HAVE_ELPA], [test $ga_elpa_ok = yes]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: AS_IF([test $ga_elpa_2015_ok = yes], [have_elpa_2015=1 $1], [AC_MSG_NOTICE([ELPA 2015 library not found, interfaces won't be defined]) have_elpa_2015=0 $2]) AC_SUBST([have_elpa_2015]) AC_DEFINE_UNQUOTED([HAVE_ELPA_2015], [$have_elpa_2015], [Define to 1 if you have 2015 ELPA lib]) AM_CONDITIONAL([HAVE_ELPA_2015], [test $ga_elpa_2015_ok = yes]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: AS_IF([test $ga_elpa_2016_ok = yes], [have_elpa_2016=1 $1], [AC_MSG_NOTICE([ELPA 2016 library not found, interfaces won't be defined]) have_elpa_2016=0 $2]) AC_SUBST([have_elpa_2016]) AC_DEFINE_UNQUOTED([HAVE_ELPA_2016], [$have_elpa_2016], [Define to 1 if you have ELPA library with 2STAGE alg. in 2016 ELPA lib]) AM_CONDITIONAL([HAVE_ELPA_2016], [test $ga_elpa_2015_ok = yes]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: AS_IF([test $ga_elpa_2017_ok = yes], [have_elpa_2017=1 $1], [AC_MSG_NOTICE([ELPA 2017 library not found, interfaces won't be defined]) have_elpa_2017=0 $2]) AC_SUBST([have_elpa_2017]) AC_DEFINE_UNQUOTED([HAVE_ELPA_2017], [$have_elpa_2017], [Define to 1 if you have ELPA library with the 2017 ELPA API]) AM_CONDITIONAL([HAVE_ELPA_2017], [test $ga_elpa_2017_ok = yes]) ])dnl GA_ELPA ga-5.9.2/m4/ga_enable_checkpoint.m4000066400000000000000000000007061500715745200170150ustar00rootroot00000000000000# GA_ENABLE_CHECKPOINT # -------------------- # Whether to enable checkpoinging. AC_DEFINEs ENABLE_CHECKPOINT. AC_DEFUN([GA_ENABLE_CHECKPOINT], [AC_ARG_ENABLE([checkpoint], [AS_HELP_STRING([--enable-checkpoint], [enable checkpointing])], [enable_checkpoint=yes AC_DEFINE([ENABLE_CHECKPOINT], [1], [Define if checkpointing is enabled])], [enable_checkpoint=no]) AM_CONDITIONAL([ENABLE_CHECKPOINT], [test x$enable_checkpoint = xyes]) ])dnl ga-5.9.2/m4/ga_enable_eispack.m4000066400000000000000000000010211500715745200162740ustar00rootroot00000000000000# GA_ENABLE_EISPACK # ----------------- # Whether to enable EISPACK routines. AC_DEFUN([GA_ENABLE_EISPACK], [AC_ARG_ENABLE([eispack], [AS_HELP_STRING([--enable-eispack], [enable Matrix Eigensystem Routines (EISPACK)])], [], [enable_eispack=no]) AS_IF([test "x$enable_eispack" = xno], [AC_DEFINE([ENABLE_EISPACK], [0], [Define to 1 if EISPACK is enabled])], [AC_DEFINE([ENABLE_EISPACK], [1], [Define to 1 if EISPACK is enabled])]) AM_CONDITIONAL([ENABLE_EISPACK], [test x$enable_eispack = xyes]) ])dnl ga-5.9.2/m4/ga_enable_opt.m4000066400000000000000000000003221500715745200154620ustar00rootroot00000000000000# GA_ENABLE_OPT() # --------------- AC_DEFUN([GA_ENABLE_OPT], [ AC_ARG_ENABLE([opt], [AS_HELP_STRING([--disable-opt], [don't use hard-coded optimization flags])], [], [enable_opt=no]) ])dnl ga-5.9.2/m4/ga_enable_peigs.m4000066400000000000000000000010031500715745200157640ustar00rootroot00000000000000# GA_ENABLE_PEIGS # --------------- # Whether to enable PeIGS routines. AC_DEFUN([GA_ENABLE_PEIGS], [AC_ARG_ENABLE([peigs], [AS_HELP_STRING([--enable-peigs], [enable Parallel Eigensystem Solver interface])], [], [enable_peigs=0]) AS_IF([test "x$enable_peigs" = xyes], [enable_peigs=1], [enable_peigs=0]) AC_SUBST([enable_peigs]) AC_DEFINE_UNQUOTED([ENABLE_PEIGS], [$enable_peigs], [Define to 1 if PeIGS is enabled]) AM_CONDITIONAL([ENABLE_PEIGS], [test "x$enable_peigs" = x1]) ])dnl ga-5.9.2/m4/ga_enable_profile.m4000066400000000000000000000010301500715745200163150ustar00rootroot00000000000000# GA_ENABLE_PROFILING # ------------------- # Whether to enable profiling. AC_DEFINEs ENABLE_PROFILING. AC_DEFUN([GA_ENABLE_PROFILING], [AC_ARG_ENABLE([profiling], [AS_HELP_STRING([--enable-profiling], [enable profiling])], [], [enable_profiling=no]) AM_CONDITIONAL([ENABLE_PROFILING], [test x$enable_profiling = xyes]) AS_IF([test "x$enable_profiling" = xyes], [AC_DEFINE([ENABLE_PROFILING], [1], [set to 1 if profiling is enabled])], [AC_DEFINE([ENABLE_PROFILING], [0], [set to 1 if profiling is enabled])]) ])dnl ga-5.9.2/m4/ga_enable_trace.m4000066400000000000000000000005751500715745200157700ustar00rootroot00000000000000# GA_ENABLE_TRACE # --------------- # Whether to enable tracing. AC_DEFINEs ENABLE_TRACE. AC_DEFUN([GA_ENABLE_TRACE], [AC_ARG_ENABLE([trace], [AS_HELP_STRING([--enable-trace], [enable tracing])], [enable_trace=yes AC_DEFINE([ENABLE_TRACE], [1], [Define if tracing is enabled])], [enable_trace=no]) AM_CONDITIONAL([ENABLE_TRACE], [test x$enable_trace = xyes]) ])dnl ga-5.9.2/m4/ga_enable_unit.m4000066400000000000000000000004701500715745200156430ustar00rootroot00000000000000# GA_ENABLE_UNIT_TESTS # -------------------- AC_DEFUN([GA_ENABLE_UNIT_TESTS], [ AC_ARG_ENABLE([unit-tests], [AS_HELP_STRING([--enable-unit-tests], [build the unfinished unit tests])], [], [enable_unit_tests=no]) AM_CONDITIONAL([ENABLE_UNIT_TESTS], [test "x$enable_unit_tests" = xyes]) ])dnl ga-5.9.2/m4/ga_f2c_cmdargs.m4000066400000000000000000000106201500715745200155260ustar00rootroot00000000000000# _GA_F2C_CMDARGS_CONFTEST # ------------------------ # Link a Fortran 77 program that uses the intrinsics GETARG and IARGC. AC_DEFUN([_GA_F2C_CMDARGS_CONFTEST], [AC_LINK_IFELSE([[ program main $ga_fxx_module integer i, j, l, ier character*20 s $ga_f77_getarg_decl i = 0 call $ga_f77_getarg($ga_f77_getarg_args) i=$ga_f77_iargc() if (i .gt. 1) then j = i - $ga_f77_iargc() j = 1.0 / j endif end]], [], [ga_f77_getarg=]) ])dnl # _GA_F2C_CMDARGS # -------------- # Determine how to access the Fortran 77 command line from C. # # Output Effects: # The following variables are set: # ga_f77_getarg - Statement to get an argument i into string s # ga_f77_getarg_args - Arguments to getarg # ga_f77_iargc - Routine to return the number of arguments # ga_fxx_module - Module command when using Fortran 90 compiler # ga_f77_getarg_decl - Declaration of routine used for ga_f77_getarg # If 'ga_f77_getarg' has a value, then that value and the values for these # other symbols will be tested first. If no approach is found, all of these # variables will have empty values. # 'AC_SUBST' is called for all seven variables. AC_DEFUN([_GA_F2C_CMDARGS], [AC_LANG_PUSH([Fortran 77]) dnl AC_ARG_VAR([F77_GETARG_DECL], [Declaration of routine e.g. external GETARG]) dnl AC_ARG_VAR([F77_GETARG], [Name of routine e.g. getarg, pxfgetarg]) dnl AC_ARG_VAR([F77_GETARG_ARGS], [Arguments to getarg e.g. i,s or i,s,l,ier]) dnl AC_ARG_VAR([F77_IARGC], [Name of routine e.g. iargc, ipxfargc]) dnl AC_ARG_VAR([FXX_MODULE], dnl [Module command when using Fortran 90 compiler e.g. use f90_unix]) AC_MSG_CHECKING([for routines to access the command line from Fortran]) # User-specified values dnl AS_IF([test "x$ga_f77_getarg" = x], dnl [ga_fxx_module=$FXX_MODULE dnl ga_f77_getarg_decl=$F77_GETARG_DECL dnl ga_f77_getarg=$F77_GETARG dnl ga_f77_getarg_args=$F77_GETARG_ARGS dnl ga_f77_iargc=$F77_IARGC dnl _GA_F2C_CMDARGS_CONFTEST()]) # Standard practice, uppercase AS_IF([test "x$ga_f77_getarg" = x], [ga_fxx_module= ga_f77_getarg_decl="external GETARG" ga_f77_getarg=GETARG ga_f77_getarg_args="i,s" ga_f77_iargc=IARGC _GA_F2C_CMDARGS_CONFTEST()]) # Standard practice, lowercase AS_IF([test "x$ga_f77_getarg" = x], [ga_fxx_module= ga_f77_getarg_decl="external getarg" ga_f77_getarg=getarg ga_f77_getarg_args="i,s" ga_f77_iargc=iargc _GA_F2C_CMDARGS_CONFTEST()]) # Posix alternative AS_IF([test "x$ga_f77_getarg" = x], [ga_fxx_module= ga_f77_getarg_decl="external pxfgetarg" ga_f77_getarg=pxfgetarg ga_f77_getarg_args="i,s,l,ier" ga_f77_iargc=ipxfargc _GA_F2C_CMDARGS_CONFTEST()]) # Nag f90_unix_env module AS_IF([test "x$ga_f77_getarg" = x], [ga_fxx_module=" use f90_unix_env" ga_f77_getarg_decl= ga_f77_getarg=getarg ga_f77_getarg_args="i,s" ga_f77_iargc=iargc _GA_F2C_CMDARGS_CONFTEST()]) # Nag f90_unix module AS_IF([test "x$ga_f77_getarg" = x], [ga_fxx_module=" use f90_unix" ga_f77_getarg_decl= ga_f77_getarg=getarg ga_f77_getarg_args="i,s" ga_f77_iargc=iargc _GA_F2C_CMDARGS_CONFTEST()]) # gfortran won't find getarg if it is marked as external AS_IF([test "x$ga_f77_getarg" = x], [ga_fxx_module= ga_f77_getarg_decl="intrinsic GETARG" ga_f77_getarg=GETARG ga_f77_getarg_args="i,s" ga_f77_iargc=IARGC _GA_F2C_CMDARGS_CONFTEST()]) AS_IF([test "x$ga_f77_getarg" = x], [AC_MSG_RESULT([no]) AC_MSG_ERROR([Could not find way to access Fortran cmd line from C])]) AC_MSG_RESULT([yes]) AC_DEFINE_UNQUOTED([F77_GETARG_DECL], [$ga_f77_getarg_decl], [Declaration of routine e.g. external GETARG]) AC_DEFINE_UNQUOTED([F77_GETARG], [$ga_f77_getarg], [Name of routine e.g. getarg, pxfgetarg]) AC_DEFINE_UNQUOTED([F77_GETARG_ARGS], [$ga_f77_getarg_args], [Arguments to getarg e.g. i,s or i,s,l,ier]) AC_DEFINE_UNQUOTED([F77_IARGC], [$ga_f77_iargc], [Name of routine e.g. iargc, ipxfargc]) AC_DEFINE_UNQUOTED([FXX_MODULE], [$ga_fxx_module], [Module command when using Fortran 90 compiler e.g. use f90_unix]) AC_F77_FUNC([F2C_GETARG]) AC_F77_FUNC([F2C_IARGC]) AC_LANG_POP([Fortran 77]) ])dnl # GA_F2C_CMDARGS # -------------- # Determine how to access the Fortran 77 command line from C. AC_DEFUN([GA_F2C_CMDARGS], [ _GA_F2C_CMDARGS AC_SUBST([F2C_GETARG]) AC_SUBST([F2C_IARGC]) ])dnl ga-5.9.2/m4/ga_f2c_match_types.m4000066400000000000000000000014521500715745200164310ustar00rootroot00000000000000# GA_F2C_MATCH_TYPES([FTYPE], [CTYPES]) # ------------------------------------- # Size-based match between Fortran and C types. AC_DEFUN([GA_F2C_MATCH_TYPES], [AS_VAR_PUSHDEF([ftype_size], [ga_cv_f77_sizeof_$1]) AS_VAR_PUSHDEF([ftype_cache], [ga_cv_f2c_$1]) AC_CACHE_CHECK([for C type corresponding to $1], ftype_cache, [m4_foreach(ctype, [$2], [AS_VAR_PUSHDEF([ctype_size], [ac_cv_sizeof_[]ctype]) AS_IF([test -z "$ftype_cache"], [AS_IF([test "$ctype_size" = "$ftype_size"], [ftype_cache="ctype"])]) AS_VAR_POPDEF([ctype_size])])]) AS_IF([test "x$ftype_cache" = x], [AC_MSG_ERROR([Could not determine C type matching Fortran $1])]) AC_SUBST(AS_TR_SH([F2C_$1_C_TYPE]), [$ftype_cache]) AS_VAR_POPDEF([ftype_cache]) AS_VAR_POPDEF([ftype_size]) ])dnl ga-5.9.2/m4/ga_f2c_nomain.m4000066400000000000000000000026271500715745200153770ustar00rootroot00000000000000# GA_F2C_NOMAIN # --------------- # In mixed Fortran/C code, the Fortran linker should be used. This is the # default behavior of automake when it selects the appropriate linker. # However, if "main" is defined in a C source file but linked by Fortran, # there could be problems if the Fortran linker attempts to link its own main # routine. Look for a flag to disable this behavior. AC_DEFUN([GA_F2C_NOMAIN], [AC_CACHE_CHECK([for flag to disable $F77 main when linking with C main], [ga_cv_fld_nomain], [AC_LANG_PUSH([C]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([],[])], [mv conftest.$ac_objext cfortran_test.$ac_objext ga_save_LIBS=$LIBS LIBS="cfortran_test.$ac_objext $LIBS" AC_LANG_PUSH([Fortran 77]) for flag in none -nofor-main -nofor_main -Mnomain -mlcmain=main; do ga_save_FFLAGS=$FFLAGS AS_IF([test "x$flag" != xnone], [FFLAGS="$FFLAGS $flag"]) AC_LINK_IFELSE( [AC_LANG_SOURCE( [[ subroutine donothing() end]])], [ga_cv_fld_nomain=$flag]) FFLAGS=$ga_save_FFLAGS AS_IF([test "x$ga_cv_fld_nomain" != x], [break]) done AC_LANG_POP([Fortran 77]) LIBS=$ga_save_LIBS rm -f cfortran_test.$ac_objext]) AC_LANG_POP([C])]) AS_IF([test "x$ga_cv_fld_nomain" != xnone], [AC_SUBST([FLD_NOMAIN], [$ga_cv_fld_nomain])]) ]) # GA_F2C_NOMAIN ga-5.9.2/m4/ga_f2c_rand.m4000066400000000000000000000017151500715745200150370ustar00rootroot00000000000000# GA_F2C_RAND # ----------- # In mixed Fortran/C code, if the C code has implemented a 'rand' function for # use by Fortran, it may conflict with an existing symbol. This is the fault of # the API being implemented, but in our case for backwards compatibility it # can't be avoided... AC_DEFUN([GA_F2C_SRAND48], [AC_CACHE_CHECK([whether we can safely implement F77_FUNC(srand48)], [ga_cv_f2c_srand48], [AC_LANG_PUSH([C]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [[ #include #define _SRAND48_ F77_FUNC(srand48, SRAND48) void _SRAND48_(long *seed) { unsigned int aseed = *seed; srandom(aseed); } ]], [[ long seed=6; _SRAND48_(&seed); ]] )], [ga_cv_f2c_srand48=yes], [ga_cv_f2c_srand48=no]) AC_LANG_POP([C])]) AS_IF([test "x$ga_cv_f2c_srand48" = xyes], [val=1], [val=0]) AC_DEFINE_UNQUOTED([F2C_SRAND48_OK], [$val], [define to 1 if Fortran-callable srand48 does not conflict with system def]) ]) # GA_F2C_SRAND48 ga-5.9.2/m4/ga_f2c_string.m4000066400000000000000000000072321500715745200154210ustar00rootroot00000000000000# _GA_F2C_STRING(ARGLIST, [ACTION-IF-SUCCESS], [ACTION-IF-FAILURE]) # ----------------------------------------------------------------- # Use ARGLIST in a Fortran/C mixed-language program and attempt to run it. # The Fortran program calls a C subroutine, passing two character strings. AC_DEFUN([_GA_F2C_STRING], [ AC_F77_FUNC([CSUB]) AC_F77_FUNC([FSUB]) AC_LANG_PUSH([Fortran 77]) AC_COMPILE_IFELSE( [[ subroutine fsub character(LEN=10) :: first_name character(LEN=15) :: last_name first_name = "John" last_name = "Doe" call csub(first_name, last_name) end]], [mv conftest.$ac_objext cfortran_test.$ac_objext ga_save_LIBS=$LIBS LIBS="cfortran_test.$ac_objext $LIBS $[]_AC_LANG_PREFIX[]LIBS" AC_LANG_PUSH([C]) AC_RUN_IFELSE([AC_LANG_SOURCE( [[#include #include #define LEN 80 void fix_c_string_for_f(char *s, int len); void fix_f_string_for_c(char *s, int len); void $CSUB($1) { char result[LEN]; fix_f_string_for_c(fname, fname_len); fix_f_string_for_c(lname, lname_len); snprintf(result, LEN, "The names passed to C: %s %s\n", fname, lname); fix_c_string_for_f(fname, fname_len); fix_c_string_for_f(lname, lname_len); } void fix_c_string_for_f(char *s, int len) { int i; for (i=strlen(s); i < len; i++) { s[i] = ' '; } } void fix_f_string_for_c(char *s, int len) { int i; for (i=len-1; s[i] == ' ' && i>=0; i--) { s[i] = '\0'; } } void $FSUB(); int main(int argc, char **argv) { $FSUB(); return 0; } ]])], [m4_default([$2], :)], [m4_default([$3], :)]) AC_LANG_POP([C]) LIBS=$ga_save_LIBS], [m4_default([$3], :)]) AC_LANG_POP([Fortran 77]) rm -rf conftest* cfortran_test.$ac_objext ])dnl # GA_F2C_HIDDEN_STRING_LENGTH_CONVENTION([VALUE-IF-CROSS-COMPILING = yes]) # --------------------------------------------------------------------------- # Determine whether a Fortran program passes the hidden string length to a # C subroutine after all arguments versus immediately after the string. # AC_DEFINEs F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS=. AC_DEFUN([GA_F2C_HIDDEN_STRING_LENGTH_CONVENTION], [AC_ARG_VAR([F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS], [if cross compiling, set to either "yes" (default) or "no" (after string)]) AC_CACHE_CHECK([whether Fortran hidden string length convention is after args], [ga_cv_f2c_string_after_args], [AS_IF([test "x$F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS" != x], [ga_cv_f2c_string_after_args="$F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS"]) AS_IF([test "x$ga_cv_f2c_string_after_args" = x], [AS_IF([test x$cross_compiling = xyes], [AS_IF([test "x$1" != x], [ga_cv_f2c_string_after_args="$1"], [ga_cv_f2c_string_after_args="yes"])])]) AS_IF([test "x$ga_cv_f2c_string_after_args" = x], [_GA_F2C_STRING([char *fname, char *lname, int fname_len, int lname_len], [ga_cv_f2c_string_after_args="yes"])]) AS_IF([test "x$ga_cv_f2c_string_after_args" = x], [_GA_F2C_STRING([char *fname, int fname_len, char *lname, int lname_len], [ga_cv_f2c_string_after_args="no"])])]) AS_IF([test x$cross_compiling = xyes], [AC_MSG_WARN([cross compiling: cannot determine f2c string convention]) AC_MSG_WARN([default is after args (=yes) but can be changed using]) AC_MSG_WARN([F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS=no])]) AS_IF([test "x$ga_cv_f2c_string_after_args" = "xyes"], [AC_DEFINE([F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS], [1], [whether the hidden string length comes after all other args])]) AS_IF([test "x$ga_cv_f2c_string_after_args" != x], [m4_default([$2], :)], [m4_default([$3], [AC_MSG_ERROR( [f2c string convention is neither after args nor after string])])]) ])dnl ga-5.9.2/m4/ga_f77_check_sizeof.m4000066400000000000000000000033411500715745200164750ustar00rootroot00000000000000# GA_F77_COMPUTE_SIZEOF(TYPE, VARIABLE) # ------------------------------------- AC_DEFUN([GA_F77_COMPUTE_SIZEOF], [ AS_TR_SH([$2])=no AC_F77_FUNC([size]) AC_F77_FUNC([fsub]) AC_LANG_PUSH([Fortran 77]) AC_COMPILE_IFELSE([AC_LANG_SOURCE( [[ subroutine fsub external size $1 x(2) call size(x(1),x(2)) end]])], [mv conftest.$ac_objext cfortran_test.$ac_objext ga_save_LIBS=$LIBS LIBS="cfortran_test.$ac_objext $LIBS $[]_AC_LANG_PREFIX[]LIBS" AC_LANG_PUSH([C]) AC_RUN_IFELSE([AC_LANG_SOURCE( [[#include #include #ifdef __cplusplus extern "C" { #endif void $size(char *a, char *b) { long diff = (long) (b - a); FILE *f=fopen("conftestval", "w"); if (!f) exit(1); fprintf(f, "%ld\n", diff); fclose(f); } #ifdef __cplusplus } #endif #ifdef __cplusplus extern "C" #else extern #endif void $fsub(void); int main(int argc, char **argv) { $fsub(); return 0; } ]])], [AS_TR_SH([$2])=`cat conftestval`]) LIBS=$ga_save_LIBS rm -f conftest* cfortran_test* AC_LANG_POP([C])]) AC_LANG_POP([Fortran 77]) ]) # GA_F77_COMPUTE_SIZEOF # GA_F77_CHECK_SIZEOF(TYPE, CROSS-SIZE) # ------------------------------------- AC_DEFUN([GA_F77_CHECK_SIZEOF], [AS_VAR_PUSHDEF([type_var], [ga_cv_f77_sizeof_$1]) AC_CACHE_CHECK([size of Fortran $1], type_var, [AS_IF([test x$cross_compiling = xyes], [AS_VAR_SET(type_var, [$2])], [GA_F77_COMPUTE_SIZEOF([$1], type_var)])]) AS_IF([test x$cross_compiling = xyes], [AC_MSG_WARN([Cannot determine size of $1 when cross-compiling.]) AC_MSG_WARN([Defaulting to $2])]) AC_DEFINE_UNQUOTED(AS_TR_CPP(sizeof_f77_$1), $type_var, [The size of '$1' as computed by C's sizeof.]) AS_VAR_POPDEF([type_var]) ])# GA_F77_SIZEOF ga-5.9.2/m4/ga_f77_cpp_symbols.m4000066400000000000000000000021301500715745200163660ustar00rootroot00000000000000# GA_F77_CPP_SYMBOL([ACTION-WHEN-FOUND]) # -------------------------------------- # Detect how to pass CPP symbols to preprocessed Fortran 77. # # Known: # -D the usual # -WF,-D IBM xlf # -Wp,-D Fujitsu # AC_DEFUN([GA_F77_CPP_SYMBOL], [AC_CACHE_CHECK([how to pass symbols to preprocessed $F77], [ga_cv_f77_cpp_symbol], [AC_LANG_PUSH([Fortran 77]) ac_ext=F for symbol in -D -WF,-D -Wp,-D do ga_save_CPPFLAGS="$CPPFLAGS" ga_save_FFLAGS="$FFLAGS" CPPFLAGS="$CPPFLAGS ${symbol}GABLAHBLAH" FFLAGS="$CPPFLAGS $FFLAGS" AC_COMPILE_IFELSE( [[#ifndef GABLAHBLAH this is an error #endif end program]], [ga_cv_f77_cpp_symbol="$symbol"]) CPPFLAGS="$ga_save_CPPFLAGS" FFLAGS="$ga_save_FFLAGS" AS_IF([test "x$ga_cv_f77_cpp_symbol" != x], [break]) done AC_LANG_POP([Fortran 77]) ]) AS_IF([test "x$ga_cv_f77_cpp_symbol" = x], [AC_MSG_ERROR([don't know how to pass symbols to preprocessed Fortran])]) m4_default([$1], [AS_CASE([$ga_cv_f77_cpp_symbol], [-D], [], [FFLAGS="$FFLAGS ${ga_cv_f77_cpp_symbol}HAVE_CONFIG_H"])]) ]) # GA_F77_CPP_SYMBOL ga-5.9.2/m4/ga_f77_disable.m4000066400000000000000000000021141500715745200154410ustar00rootroot00000000000000# GA_F77_DISABLE() # ---------------- # Whether to disable all Fortran code. AC_DEFUN([GA_F77_DISABLE], [AC_ARG_ENABLE([f77], [AS_HELP_STRING([--disable-f77], [disable Fortran code])], [], [enable_f77=yes]) ])# GA_F77_DISABLE # GA_F77_DISABLE_RESULTS() # ------------------------ # This used to be part of the the GA_F77_DISABLE macro, but it turns out the # fortran compiler may be bogus and therefore we should disable it after # GA_F77_DISABLE has been run. These AC_DEFINEs and AM_CONDITIONALs should be # set at the last possible moment when the final value for enable_f77 has been # set. AC_DEFUN([GA_F77_DISABLE_RESULTS], [ AS_IF([test "x$enable_f77" = xyes], [AC_DEFINE([NOFORT], [0], [Define to 1 if not using Fortran]) AC_DEFINE([ENABLE_F77], [1], [Define to 1 if using Fortran])], [AC_DEFINE([NOFORT], [1], [Define to 1 if not using Fortran]) AC_DEFINE([ENABLE_F77], [0], [Define to 1 if using Fortran])]) AM_CONDITIONAL([NOFORT], [test "x$enable_f77" = xno]) AM_CONDITIONAL([ENABLE_F77], [test "x$enable_f77" = xyes]) ])# GA_F77_DISABLE_RESULTS ga-5.9.2/m4/ga_f77_fixed.m4000066400000000000000000000023471500715745200151450ustar00rootroot00000000000000# GA_F77_FIXED # ------------ # Similar to AC_FC_FREEFORM, ensure that the Fortran compiler can compile # fixed format source code as opposed to newer free form formats. # If necessary it will add flags to FFLAGS. # The known flags are: # -ffixed-form: GNU g77, gfortran # -fixed: Intel compiler (ifort), Sun compiler (f95) # -qfixed: IBM compiler (xlf*) # -Mfixed: Portland Group compiler # -f fixed: Absoft Fortran # +source=fixed: HP Fortran # AC_DEFUN([GA_F77_FIXED], [ AC_CACHE_CHECK([whether $F77 needs a flag to compile fixed format source], [ga_cv_f77_fixed_flag], [AC_LANG_PUSH([Fortran 77]) for testflag in none -ffixed-form -qfixed -Mfixed -fixedform "-f fixed" +source=fixed -fix ; do ga_save_FFLAGS=$FFLAGS AS_IF([test "x$testflag" != xnone], [FFLAGS="$FFLAGS $testflag"]) AC_COMPILE_IFELSE([AC_LANG_SOURCE( [[c some comment end program]])], [ga_cv_f77_fixed_flag=$testflag]) FFLAGS=$ga_save_FFLAGS AS_IF([test "x$ga_cv_f77_fixed_flag" != x], [break]) done AC_LANG_POP([Fortran 77])]) AS_IF([test "x$ga_cv_f77_fixed_flag" != xnone], [AS_IF([test "x$ga_cv_f77_fixed_flag" != x], [FFLAGS="$FFLAGS $ga_cv_f77_fixed_flag"])]) ]) # GA_F77_FIXED ga-5.9.2/m4/ga_f77_flush.m4000066400000000000000000000031101500715745200151540ustar00rootroot00000000000000# GA_F77_FLUSH # ------------ # Check whether the function "flush" is available. If not, try various flags # in order to link to it successfully. Otherwise look for the "forflush" # function. Add any appropriate flags to FFLAGS as needed and define # F77_FLUSH to the name of the appropriate function. # If flush routine is available set HAVE_F77_FLUSH to 1, otherwise 0. AC_DEFUN([GA_F77_FLUSH], [ AC_CACHE_CHECK([for $F77 flush routine], [ga_cv_f77_flush], [AC_LANG_PUSH([Fortran 77]) for testflag in none -qextname=flush ; do ga_save_FFLAGS=$FFLAGS AS_IF([test "x$testflag" != xnone], [FFLAGS="$FFLAGS $testflag"]) AC_LINK_IFELSE([AC_LANG_SOURCE( [[ call flush(13) end program]])], [ga_cv_f77_flush_flag=$testflag]) FFLAGS=$ga_save_FFLAGS AS_IF([test "x$ga_cv_f77_flush_flag" != x], [break]) done AS_IF([test "x$ga_cv_f77_flush_flag" != x], [ga_cv_f77_flush=flush], [AC_LINK_IFELSE([AC_LANG_SOURCE( [[ call forflush(13) end program]])], [ga_cv_f77_flush=forflush])]) AC_LANG_POP([Fortran 77])]) AS_IF([test "x$ga_cv_f77_flush" != x], [AC_DEFINE_UNQUOTED([F77_FLUSH], [$ga_cv_f77_flush], [Name of F77 flush routine]) AC_DEFINE([HAVE_F77_FLUSH], [1], [whether F77 flush routine is available])], [AC_MSG_WARN([Could not determine name of $F77 flush routine]) AC_DEFINE([HAVE_F77_FLUSH], [0], [whether F77 flush routine is available])]) AS_IF([test "x$ga_cv_f77_flush" = xflush], [AS_IF([test "x$ga_cv_f77_flush_flag" != xnone], [FFLAGS="$FFLAGS $ga_cv_f77_flush_flag"])]) ]) # GA_F77_FLUSH ga-5.9.2/m4/ga_f77_integer_size.m4000066400000000000000000000163221500715745200165330ustar00rootroot00000000000000# _GA_F77_INTEGER_4_KNOWN_FLAGS # ----------------------------- # These are the known flags for promoting INTEGERs to 8 bytes. AC_DEFUN([_GA_F77_INTEGER_4_KNOWN_FLAGS], [-fdefault-integer-4 -qintsize=4 "-integer-size 32" "-CcdII4 -CcdLL8" "-s integer32" -xtypemap=integer:32 -i4 +i4])dnl # _GA_F77_INTEGER_8_KNOWN_FLAGS # ----------------------------- # These are the known flags for promoting INTEGERs to 8 bytes. AC_DEFUN([_GA_F77_INTEGER_8_KNOWN_FLAGS], [-fdefault-integer-8 -qintsize=8 "-integer-size 64" "-CcdII8 -CcdLL8" "-s integer64" -xtypemap=integer:64 -i8 +i8])dnl # _GA_F77_INTEGER_4_FLAG(VARIABLE) # -------------------------------- # What FFLAG, if any, forces INTEGER size to be 4 bytes? # Assign result to VARIABLE. AC_DEFUN([_GA_F77_INTEGER_4_FLAG], [for flag in none $FFLAG_INT _GA_F77_INTEGER_4_KNOWN_FLAGS do ga_save_FFLAGS="$FFLAGS" AS_IF([test "x$flag" != xnone], [FFLAGS="$flag $FFLAGS"]) sizeof_integer=0 GA_F77_COMPUTE_SIZEOF([INTEGER], [sizeof_integer]) FFLAGS="$ga_save_FFLAGS" AS_IF([test x$sizeof_integer = x4], [AS_TR_SH([$1])=$flag; break]) done ]) # _GA_F77_INTEGER_4_FLAG # _GA_F77_INTEGER_8_FLAG(VARIABLE) # -------------------------------- # What FFLAG, if any, forces INTEGER size to be 8 bytes? # Assign result to VARIABLE. AC_DEFUN([_GA_F77_INTEGER_8_FLAG], [for flag in none $FFLAG_INT _GA_F77_INTEGER_8_KNOWN_FLAGS do ga_save_FFLAGS="$FFLAGS" AS_IF([test "x$flag" != xnone], [FFLAGS="$flag $FFLAGS"]) sizeof_integer=0 GA_F77_COMPUTE_SIZEOF([INTEGER], [sizeof_integer]) FFLAGS="$ga_save_FFLAGS" AS_IF([test x$sizeof_integer = x8], [AS_TR_SH([$1])=$flag; break]) done ]) # _GA_F77_INTEGER_8_FLAG # _GA_F77_INTEGER_4_FLAG_CROSS(VARIABLE) # -------------------------------------- # What FFLAG, if any, forces INTEGER size to be 4 bytes? # This is safe for cross-compiling, although less accurate. # Some compilers don't have the capability to change warnings to errors, so # in some cases an inccorect size flag will still succeed during # compilation. Unfortunately, there's no alternative when cross compiling. AC_DEFUN([_GA_F77_INTEGER_4_FLAG_CROSS], [AC_LANG_PUSH([Fortran 77]) ga_result= ga_save_FFLAGS="$FFLAGS" ga_save_suppress_FFLAGS="$FFLAGS" ga_save_werror_flag=$ac_f77_werror_flag ac_f77_werror_flag=yes AS_IF([test "x$ga_cv_f77_suppress" != xnone], [ga_save_suppress_FFLAGS="$FFLAGS $ga_cv_f77_suppress"]) AS_IF([test "x$FFLAG_INT" != x], [FFLAGS="$ga_save_suppress_FFLAGS $FFLAG_INT" rm -f a.out touch a.out AC_LINK_IFELSE( [[ program main integer i i=0 write(6,*) i end program]], [ga_result=$flag])]) AS_IF([test "x$ga_result" = x], [for flag in _GA_F77_INTEGER_4_KNOWN_FLAGS do FFLAGS="$ga_save_suppress_FFLAGS $flag" rm -f a.out touch a.out AC_LINK_IFELSE( [[ program main integer i i=0 write(6,*) i end program]], [ac_ext=F rm -f a.out touch a.out AC_LINK_IFELSE( [[ program main integer i i=0 write(6,*) i end program]], [ga_result=$flag; break]) ac_ext=f]) done rm -f a.out ]) ac_f77_werror_flag=$ga_save_werror_flag FFLAGS="$ga_save_FFLAGS" AS_TR_SH([$1])="$ga_result" AC_LANG_POP([Fortran 77]) ]) # _GA_F77_INTEGER_4_FLAG_CROSS # _GA_F77_INTEGER_8_FLAG_CROSS(VARIABLE) # -------------------------------------- # What FFLAG, if any, forces INTEGER size to be 8 bytes? # This is safe for cross-compiling, although less accurate. # Some compilers don't have the capability to change warnings to errors, so # in some cases an inccorect size flag will still succeed during # compilation. Unfortunately, there's no alternative when cross compiling. AC_DEFUN([_GA_F77_INTEGER_8_FLAG_CROSS], [AC_LANG_PUSH([Fortran 77]) ga_result= ga_save_FFLAGS="$FFLAGS" ga_save_suppress_FFLAGS="$FFLAGS" ga_save_werror_flag=$ac_f77_werror_flag ac_f77_werror_flag=yes AS_IF([test "x$ga_cv_f77_suppress" != xnone], [ga_save_suppress_FFLAGS="$FFLAGS $ga_cv_f77_suppress"]) AS_IF([test "x$FFLAG_INT" != x], [FFLAGS="$ga_save_suppress_FFLAGS $FFLAG_INT" rm -f a.out touch a.out AC_LINK_IFELSE( [[ program main integer i i=0 write(6,*) i end program]], [ga_result=$flag])]) AS_IF([test "x$ga_result" = x], [for flag in _GA_F77_INTEGER_8_KNOWN_FLAGS do FFLAGS="$ga_save_suppress_FFLAGS $flag" rm -f a.out touch a.out AC_LINK_IFELSE( [[ program main integer i i=0 write(6,*) i end program]], [ac_ext=F rm -f a.out touch a.out AC_LINK_IFELSE( [[ program main integer i i=0 write(6,*) i end program]], [ga_result=$flag; break]) ac_ext=f]) done rm -f a.out ]) ac_f77_werror_flag=$ga_save_werror_flag FFLAGS="$ga_save_FFLAGS" AS_TR_SH([$1])="$ga_result" AC_LANG_POP([Fortran 77]) ]) # _GA_F77_INTEGER_8_FLAG_CROSS # GA_F77_INTEGER_SIZE # ------------------- # Allow the user to pick whether to use 4- or 8-byte integers. If not # specified, the default integer size is equivalent to sizeof(void*). # Adds the appropriate flag to FFLAGS, if needed. AC_DEFUN([GA_F77_INTEGER_SIZE], [AC_ARG_VAR([FFLAG_INT], [Fortran 77 compiler flag to set desired integer size]) AC_ARG_ENABLE([i4], [AS_HELP_STRING([--enable-i4], [enable 4-byte integers [default: sizeof(void*)]])], [enable_i4=yes]) AC_ARG_ENABLE([i8], [AS_HELP_STRING([--enable-i8], [enable 8-byte integers [default: sizeof(void*)]])], [enable_i8=yes]) AC_LANG_PUSH([C]) AC_COMPUTE_INT([ga_f77_integer_size], [(long int) (sizeof (void*))], [AC_INCLUDES_DEFAULT()], [ga_f77_integer_size=0]) AC_LANG_POP([C]) AC_CACHE_CHECK([for desired Fortran INTEGER size], [ga_cv_f77_integer_size], [AS_IF([test x$enable_i4 = xyes], [AS_IF([test x$enable_i8 = xyes], [AC_MSG_ERROR([Cannot enable both i4 and i8])], [ga_cv_f77_integer_size=4])], [AS_IF([test x$enable_i8 = xyes], [ga_cv_f77_integer_size=8], [ga_cv_f77_integer_size=$ga_f77_integer_size])])]) # Now determine the correct compiler flag to adjust the integer size. AC_CACHE_CHECK([for INTEGER size compile flag], [ga_cv_f77_integer_size_flag], [AS_CASE([$cross_compiling:$ga_cv_f77_integer_size], [yes:4],[_GA_F77_INTEGER_4_FLAG_CROSS([ga_cv_f77_integer_size_flag])], [yes:8],[_GA_F77_INTEGER_8_FLAG_CROSS([ga_cv_f77_integer_size_flag])], [*:4], [_GA_F77_INTEGER_4_FLAG([ga_cv_f77_integer_size_flag])], [*:8], [_GA_F77_INTEGER_8_FLAG([ga_cv_f77_integer_size_flag])])]) AS_IF([test "x$ga_cv_f77_integer_size_flag" != x], [AS_IF([test "x$ga_cv_f77_integer_size_flag" != xnone], [AC_SUBST([FFLAG_INT], [$ga_cv_f77_integer_size_flag])])]) AS_IF([test "x$ga_cv_f77_integer_size" = x8], [AS_IF([test "x$ga_cv_f77_integer_size_flag" = x], [AC_MSG_WARN([!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!]) AC_MSG_WARN([Unable to find a flag to promote Fortran integers]) AC_MSG_WARN([INTEGER*8 promotion is not supported]) AC_MSG_WARN([!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!])])]) ])dnl ga-5.9.2/m4/ga_f77_ld_override.m4000066400000000000000000000017121500715745200163370ustar00rootroot00000000000000# GA_F77_LD_OVERRIDE # ------------------ # Replaced $(F77LINK) in our Makefile.am's with $(LINK) if needed. # # There is a ton of garbage here. We must replicate what automake will do in # the case where we enable F77 code (and therefore require the F77LINK). We # use autoconf quadrigraphs to avoid problems with $(foo) and $@. This is yet # another hack to disable F77 since automake selects the linker based on the # static list of source files. AC_DEFUN([GA_F77_LD_OVERRIDE], [ m4_pattern_allow([AM_V_lt]) AS_IF([test "x$enable_f77" = xyes], [F77LINK='@S|@@{:@LIBTOOL@:}@ @S|@@{:@AM_V_lt@:}@ --tag=F77 @S|@@{:@AM_LIBTOOLFLAGS@:}@ @S|@@{:@LIBTOOLFLAGS@:}@ --mode=link @S|@@{:@F77LD@:}@ @S|@@{:@AM_FFLAGS@:}@ @S|@@{:@FFLAGS@:}@ @S|@@{:@AM_LDFLAGS@:}@ @S|@@{:@LDFLAGS@:}@ -o @S|@@' am__v_F77LD_0='@echo " F77LD " @S|@@;'], [F77LINK='@S|@@{:@LINK@:}@' am__v_F77LD_0='@S|@@{:@am__v_CCLD_0@:}@']) AC_SUBST([F77LINK]) AC_SUBST([am__v_F77LD_0]) ])dnl ga-5.9.2/m4/ga_f77_library_ldflags.m4000066400000000000000000000140601500715745200172010ustar00rootroot00000000000000# _GA_FC_LIBRARY_LDFLAGS # ====================== # # GA HACK!!! # Until autoconf is fixed to support Sun Studio compilers using -Y, we replace # _AC_FC_LIBRARY_LDFLAGS. The rest of the marco is directly copied from # autoconf-2.67/lib/autoconf/fortran.m4 # ---------------------------------------------------------------------------- # # Determine the linker flags (e.g. "-L" and "-l") for the Fortran # intrinsic and runtime libraries that are required to successfully # link a Fortran program or shared library. The output variable # FLIBS/FCLIBS is set to these flags. # # This macro is intended to be used in those situations when it is # necessary to mix, e.g. C++ and Fortran, source code into a single # program or shared library. # # For example, if object files from a C++ and Fortran compiler must # be linked together, then the C++ compiler/linker must be used for # linking (since special C++-ish things need to happen at link time # like calling global constructors, instantiating templates, enabling # exception support, etc.). # # However, the Fortran intrinsic and runtime libraries must be # linked in as well, but the C++ compiler/linker doesn't know how to # add these Fortran libraries. Hence, the macro # "AC_F77_LIBRARY_LDFLAGS" was created to determine these Fortran # libraries. # # This macro was packaged in its current form by Matthew D. Langston. # However, nearly all of this macro came from the "OCTAVE_FLIBS" macro # in "octave-2.0.13/aclocal.m4", and full credit should go to John # W. Eaton for writing this extremely useful macro. Thank you John. AC_DEFUN([_GA_FC_LIBRARY_LDFLAGS], [_AC_FORTRAN_ASSERT()dnl _AC_PROG_FC_V AC_CACHE_CHECK([for _AC_LANG libraries of $[]_AC_FC[]], ac_cv_[]_AC_LANG_ABBREV[]_libs, [if test "x$[]_AC_LANG_PREFIX[]LIBS" != "x"; then ac_cv_[]_AC_LANG_ABBREV[]_libs="$[]_AC_LANG_PREFIX[]LIBS" # Let the user override the test. else _AC_PROG_FC_V_OUTPUT ac_cv_[]_AC_LANG_ABBREV[]_libs= # Save positional arguments (if any) ac_save_positional="$[@]" set X $ac_[]_AC_LANG_ABBREV[]_v_output while test $[@%:@] != 1; do shift ac_arg=$[1] case $ac_arg in [[\\/]]*.a | ?:[[\\/]]*.a) _AC_LIST_MEMBER_IF($ac_arg, $ac_cv_[]_AC_LANG_ABBREV[]_libs, , ac_cv_[]_AC_LANG_ABBREV[]_libs="$ac_cv_[]_AC_LANG_ABBREV[]_libs $ac_arg") ;; -bI:*) _AC_LIST_MEMBER_IF($ac_arg, $ac_cv_[]_AC_LANG_ABBREV[]_libs, , [_AC_LINKER_OPTION([$ac_arg], ac_cv_[]_AC_LANG_ABBREV[]_libs)]) ;; # Ignore these flags. -lang* | -lcrt*.o | -lc | -lgcc* | -lSystem | -libmil | -little \ |-LANG:=* | -LIST:* | -LNO:* | -link) ;; -lkernel32) test x"$CYGWIN" != xyes && ac_cv_[]_AC_LANG_ABBREV[]_libs="$ac_cv_[]_AC_LANG_ABBREV[]_libs $ac_arg" ;; -[[LRuYz]]) # These flags, when seen by themselves, take an argument. # We remove the space between option and argument and re-iterate # unless we find an empty arg or a new option (starting with -) case $[2] in "" | -*);; *) ac_arg="$ac_arg$[2]" shift; shift set X $ac_arg "$[@]" ;; esac ;; -YP,*) for ac_j in `AS_ECHO(["$ac_arg"]) | sed -e 's/-YP,/-L/;s/:/ -L/g'`; do _AC_LIST_MEMBER_IF($ac_j, $ac_cv_[]_AC_LANG_ABBREV[]_libs, , [ac_arg="$ac_arg $ac_j" ac_cv_[]_AC_LANG_ABBREV[]_libs="$ac_cv_[]_AC_LANG_ABBREV[]_libs $ac_j"]) done ;; dnl begin GA addition -Y*) for ac_j in `AS_ECHO(["$ac_arg"]) | sed -e 's/-Y/-L/;s/"//g;s/:/ -L/g'`; do _AC_LIST_MEMBER_IF($ac_j, $ac_cv_[]_AC_LANG_ABBREV[]_libs, , [ac_arg="$ac_arg $ac_j" ac_cv_[]_AC_LANG_ABBREV[]_libs="$ac_cv_[]_AC_LANG_ABBREV[]_libs $ac_j"]) done ;; dnl end GA addition -[[lLR]]*) _AC_LIST_MEMBER_IF($ac_arg, $ac_cv_[]_AC_LANG_ABBREV[]_libs, , ac_cv_[]_AC_LANG_ABBREV[]_libs="$ac_cv_[]_AC_LANG_ABBREV[]_libs $ac_arg") ;; -zallextract*| -zdefaultextract) ac_cv_[]_AC_LANG_ABBREV[]_libs="$ac_cv_[]_AC_LANG_ABBREV[]_libs $ac_arg" ;; # Ignore everything else. esac done # restore positional arguments set X $ac_save_positional; shift # We only consider "LD_RUN_PATH" on Solaris systems. If this is seen, # then we insist that the "run path" must be an absolute path (i.e. it # must begin with a "/"). case `(uname -sr) 2>/dev/null` in "SunOS 5"*) ac_ld_run_path=`AS_ECHO(["$ac_[]_AC_LANG_ABBREV[]_v_output"]) | sed -n 's,^.*LD_RUN_PATH *= *\(/[[^ ]]*\).*$,-R\1,p'` test "x$ac_ld_run_path" != x && _AC_LINKER_OPTION([$ac_ld_run_path], ac_cv_[]_AC_LANG_ABBREV[]_libs) ;; esac fi # test "x$[]_AC_LANG_PREFIX[]LIBS" = "x" ]) []_AC_LANG_PREFIX[]LIBS="$ac_cv_[]_AC_LANG_ABBREV[]_libs" AC_SUBST([]_AC_LANG_PREFIX[]LIBS) ])# _GA_FC_LIBRARY_LDFLAGS # GA_F77_LIBRARY_LDFLAGS # ---------------------- # Wrap AC_F77_LIBRARY_LDFLAGS in case user disables Fortran 77. # When mixing gcc and ifort, we sometimes need to add -lgcc_s to the FLIBS. # Lastly, Sun Studio compilers are not (yet) completely supported. AC_DEFUN([GA_F77_LIBRARY_LDFLAGS], [ GA_MPI_UNWRAP_PUSH() AC_F77_LIBRARY_LDFLAGS GA_MPI_UNWRAP_POP() AC_CACHE_CHECK([whether FLIBS needs -lgcc_s], [ga_cv_flibs_gcc_s], [happy=yes ga_save_LIBS="$LIBS"; LIBS="$LIBS $FLIBS" ga_save_FLIBS="$FLIBS" AC_LANG_PUSH([C]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([],[])], [ga_cv_flibs_gcc_s=no], [LIBS="$LIBS -lgcc_s" AC_LINK_IFELSE( [AC_LANG_PROGRAM([],[])], [FLIBS="$FLIBS -lgcc_s" ga_cv_flibs_gcc_s=yes], [happy=no FLIBS="$ga_save_FLIBS" ga_cv_flibs_gcc_s=no])]) LIBS="$ga_save_LIBS" AC_LANG_POP([C])]) dnl hack to support Sun Studio -Y linker flag AS_IF([test "x$happy" = xno], [AS_UNSET([ac_cv_f77_libs]) AS_UNSET([FLIBS]) _GA_FC_LIBRARY_LDFLAGS ga_save_LIBS="$LIBS"; LIBS="$LIBS $FLIBS" AC_LANG_PUSH([C]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([],[])], [happy=yes], [happy=no]) LIBS="$ga_save_LIBS" AC_LANG_POP([C])]) AS_IF([test "x$happy" = xno], [AC_MSG_WARN([FLIBS does not work])]) ])dnl ga-5.9.2/m4/ga_f77_mismatch_type.m4000066400000000000000000000026321500715745200167110ustar00rootroot00000000000000# GA_F77_MISMATCH_TYPE # -------------------- # Determine F77 compiler flags, if any, that are required for compiling code # which uses inconsistent data types for certain function calls. Nearly all # of our Fortran routines are written in C and some are not type safe. # The known flags are: # -mismatch_all: nagfor # -dusty: nagfor # -fallow-argument-mismatch : gfortran # AC_DEFUN([GA_F77_MISMATCH_TYPE], [ AC_CACHE_CHECK([whether $F77 needs a flag to compile inconsistent types], [ga_cv_f77_mismatch_type_flag], [AC_LANG_PUSH([Fortran 77]) for testflag in none "-mismatch_all -dusty" -fallow-argument-mismatch do ga_save_FFLAGS="$FFLAGS" AS_IF([test "x$testflag" != xnone], [FFLAGS="$FFLAGS $testflag"]) AC_COMPILE_IFELSE( [AC_LANG_SOURCE( [[ integer function the_test () implicit none logical foo external foo character*1 byte_mb integer int_mb the_test = 0 if (foo(byte_mb)) return if (foo( int_mb)) return the_test = 1 return end]])], [ga_cv_f77_mismatch_type_flag="$testflag"]) FFLAGS="$ga_save_FFLAGS" AS_IF([test "x$ga_cv_f77_mismatch_type_flag" != x], [break]) done AC_LANG_POP([Fortran 77])]) AS_IF([test "x$ga_cv_f77_mismatch_type_flag" != xnone], [AS_IF([test "x$ga_cv_f77_mismatch_type_flag" != x], [FFLAGS="$FFLAGS $ga_cv_f77_mismatch_type_flag"])]) ]) # GA_F77_MISMATCH_TYPE ga-5.9.2/m4/ga_f77_opt.m4000066400000000000000000000040151500715745200146420ustar00rootroot00000000000000# GA_F77_OPT() # ------------ # Determine TARGET-/compiler-specific FFLAGS for optimization. AC_DEFUN([GA_F77_OPT], [ AC_REQUIRE([GA_TARGET64]) AC_REQUIRE([GA_ENABLE_OPT]) AC_ARG_VAR([GA_FOPT], [GA Fortran 77 optimization flags]) AC_CACHE_CHECK([for specific Fortran optimizations], [ga_cv_f77_opt], [ AS_IF([test "x$GA_FOPT" != x], [ga_cv_f77_opt="$GA_FOPT"], [ga_cv_f77_opt=]) AS_IF([test "x$ga_cv_f77_opt" = x && test "x$enable_opt" = xyes], [ AS_CASE([$ga_cv_target:$ga_cv_f77_compiler_vendor:$host_cpu], [CYGWIN:*:*], [ga_cv_f77_opt=], [IBM64:*:*], [ga_cv_f77_opt="-qarch=auto"], [IBM:*:*], [ga_cv_f77_opt="-qarch=auto"], [LINUX64:*:alpha], [ga_cv_f77_opt="-align_dcommons -fpe3 -check nooverflow -assume accuracy_sensitive -check nopower -check nounderflow"], [LINUX64:fujitsu:x86_64], [ga_cv_f77_opt="-Kfast -X9 -Am -fw"], [LINUX64:gnu:x86_64], [ga_cv_f77_opt="-O"], [LINUX64:ibm:x86_64], [ga_cv_f77_opt=], [LINUX64:intel:powerpc64], [ga_cv_f77_opt=], [LINUX64:intel:ppc64], [ga_cv_f77_opt=], [LINUX64:intel:x86_64], [ga_cv_f77_opt="-O3 -w -cm -xW -tpp7"], [LINUX64:portland:x86_64], [ga_cv_f77_opt="-Mdalign"], [LINUX:fujitsu:*], [ga_cv_f77_opt="-Kfast -X9 -Am -fw"], [LINUX:gnu:786], [ga_cv_f77_opt="-O2 -funroll-loops -malign-double"], [LINUX:gnu:*], [ga_cv_f77_opt="-O2 -funroll-loops"], [LINUX:gnu:x86], [ga_cv_f77_opt="-O2 -funroll-loops -malign-double"], [LINUX:ibm:*], [ga_cv_f77_opt="-q32"], [LINUX:intel:*], [ga_cv_f77_opt="-O3 -prefetch -w -cm"], [LINUX:portland:*], [ga_cv_f77_opt="-Mdalign -Minform,warn -Mnolist -Minfo=loop -Munixlogical"], [MACX64:ibm:*], [ga_cv_f77_opt=], [MACX64:intel:*], [ga_cv_f77_opt="-O3 -prefetch -w -cm"], [MACX:gnu:*], [ga_cv_f77_opt="-O3 -funroll-loops"], [MACX:intel:*], [ga_cv_f77_opt="-O3 -prefetch -w -cm"], [ga_cv_f77_opt=]) ])]) AC_SUBST([GA_FOPT], [$ga_cv_f77_opt]) ])dnl ga-5.9.2/m4/ga_f77_underscore.m4000066400000000000000000000042441500715745200162150ustar00rootroot00000000000000# GA_F77_UNDERSCORE # ----------------- # If F77 requires a flag to add an underscore to external names, find it. # Only add a single underscore regardless of whether there are already # underscores in the name. # # Known flags: # -qextname IBM xl # -qEXTNAME IBM xl # -funderscoring GNU # -fno-second-underscore GNU # -f absoft compiler (OSX?) # AC_DEFUN([GA_F77_UNDERSCORE], [AC_CACHE_CHECK([for $F77 flag to add single underscore to external names], [ga_cv_f77_underscore_flag], [AC_LANG_PUSH([C]) AC_COMPILE_IFELSE( [AC_LANG_SOURCE([[void my_sub_() {}]])], [mv conftest.$ac_objext cfortran_test.$ac_objext ga_save_LIBS="$LIBS" LIBS="cfortran_test.$ac_objext $LIBS" AC_LANG_PUSH([Fortran 77]) for ga_flag in none -qextname -qEXTNAME -funderscoring -fno-second-underscore -f +ppu ; do ga_save_FFLAGS="$FFLAGS" AS_IF([test "x$ga_flag" != xnone], [FFLAGS="$FFLAGS $ga_flag"]) AC_LINK_IFELSE( [AC_LANG_CALL([], [my_sub])], [ga_cv_f77_underscore_flag="$ga_flag"]) FFLAGS="$ga_save_FFLAGS" AS_IF([test "x$ga_cv_f77_underscore_flag" != x], [break]) done AC_LANG_POP([Fortran 77]) LIBS="$ga_save_LIBS" rm -f cfortran_test.$ac_objext]) AC_LANG_POP([C])]) AS_IF([test "x$ga_cv_f77_underscore_flag" != xnone], [AS_IF([test "x$ga_cv_f77_underscore_flag" != x], [FFLAGS="$FFLAGS $ga_cv_f77_underscore_flag"])]) ]) # GA_F77_UNDERSCORE # GA_F77_MAYBE_UNDERSCORING # ------------------------- # Allow user to override the underscoring policy of their compiler. # # The default is to use whatever is 'natural' for the compiler, however # GA used to be built assuming a single underscore was appended to all # external Fortran symbols. This allows the old behavior to be turned # on (off by default). # AC_DEFUN([GA_F77_MAYBE_UNDERSCORING], [AC_ARG_ENABLE([underscoring], [AS_HELP_STRING([--enable-underscoring], [force single underscore for all external Fortran symbols [default: use compiler defaults for external Fortran symbols]])]) AS_IF([test "x$enable_underscoring" = xyes], [GA_F77_UNDERSCORE]) ]) # GA_F77_MAYBE_UNDERSCORING ga-5.9.2/m4/ga_function.m4000066400000000000000000000016611500715745200152060ustar00rootroot00000000000000# GA_FUNCTION # ----------- # Define FUNCTION_NAME to either __func__ or __FUNCTION__ appropriately. # If all else fails, #define FUNCTION_NAME . AC_DEFUN([GA_FUNCTION], [AC_CACHE_CHECK([for preprocessor symbol for function name], [ga_cv_cpp_function], [AS_IF([test x$ga_cv_cpp_function = x], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[extern int printf(const char *format, ...);]], [[printf("__func__ = %s\n", __func__);]])], [ga_cv_cpp_function=__func__])]) AS_IF([test x$ga_cv_cpp_function = x], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[extern int printf(const char *format, ...);]], [[printf("__FUNCTION__ = %s\n", __FUNCTION__);]])], [ga_cv_cpp_function=__FUNCTION__])]) AS_IF([test x$ga_cv_cpp_function = x], [ga_cv_cpp_function='"Unknown"'])]) AC_DEFINE_UNQUOTED([FUNCTION_NAME], [$ga_cv_cpp_function], [CPP symbol for function name, if available]) ])# GA_FUNCTION ga-5.9.2/m4/ga_gnu_loop_opt.m4000066400000000000000000000020361500715745200160620ustar00rootroot00000000000000# GA_GNU_LOOP_OPT # --------------- # Add -fno-aggressive-loop-optimizations to the compiler flags if using # GNU compilers. AC_DEFUN([GA_GNU_LOOP_OPT], [ AC_PREREQ([2.69]) dnl for _AC_LANG_PREFIX AC_CACHE_CHECK([for -fno-aggressive-loop-optimizations support in _AC_LANG compiler], ga_cv_[]_AC_LANG_ABBREV[]_gnu_loop_opt, [ ga_cv_[]_AC_LANG_ABBREV[]_gnu_loop_opt= save_[]_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS" []_AC_LANG_PREFIX[]FLAGS="$save_[]_AC_LANG_PREFIX[]FLAGS -fno-aggressive-loop-optimizations" save_ac_[]_AC_LANG_ABBREV[]_werror_flag="$ac_[]_AC_LANG_ABBREV[]_werror_flag" AC_LANG_WERROR rm -f a.out touch a.out AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], ga_cv_[]_AC_LANG_ABBREV[]_gnu_loop_opt=-fno-aggressive-loop-optimizations) []_AC_LANG_PREFIX[]FLAGS="$save_[]_AC_LANG_PREFIX[]FLAGS" ac_[]_AC_LANG_ABBREV[]_werror_flag="$save_ac_[]_AC_LANG_ABBREV[]_werror_flag" rm -f a.out ]) AC_SUBST([]_AC_LANG_PREFIX[]FLAG_NO_LOOP_OPT, $ga_cv_[]_AC_LANG_ABBREV[]_gnu_loop_opt) ]) ga-5.9.2/m4/ga_gnu_loop_vect.m4000066400000000000000000000017711500715745200162260ustar00rootroot00000000000000# GA_GNU_LOOP_VECT # --------------- # Add -fno-tree-slp-vectorize to the compiler flags if using # GNU compilers. AC_DEFUN([GA_GNU_LOOP_VECT], [ AC_PREREQ([2.69]) dnl for _AC_LANG_PREFIX AC_CACHE_CHECK([for -fno-tree-slp-vectorize support in _AC_LANG compiler], ga_cv_[]_AC_LANG_ABBREV[]_gnu_loop_vect, [ ga_cv_[]_AC_LANG_ABBREV[]_gnu_loop_vect= save_[]_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS" []_AC_LANG_PREFIX[]FLAGS="$save_[]_AC_LANG_PREFIX[]FLAGS -fno-tree-slp-vectorize" save_ac_[]_AC_LANG_ABBREV[]_werror_flag="$ac_[]_AC_LANG_ABBREV[]_werror_flag" AC_LANG_WERROR rm -f a.out touch a.out AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], ga_cv_[]_AC_LANG_ABBREV[]_gnu_loop_vect=-fno-tree-slp-vectorize) []_AC_LANG_PREFIX[]FLAGS="$save_[]_AC_LANG_PREFIX[]FLAGS" ac_[]_AC_LANG_ABBREV[]_werror_flag="$save_ac_[]_AC_LANG_ABBREV[]_werror_flag" rm -f a.out ]) AC_SUBST([]_AC_LANG_PREFIX[]FLAG_NO_LOOP_VECT, $ga_cv_[]_AC_LANG_ABBREV[]_gnu_loop_vect) ]) ga-5.9.2/m4/ga_gnu_std_gnu17.m4000066400000000000000000000017341500715745200160460ustar00rootroot00000000000000# GA_GNU_STD_GNU17 # --------------- # Add -std=gnu17 to the compiler flags if using # GNU compilers to avoid -std=gnu23 issues AC_DEFUN([GA_GNU_STD_GNU17], [ AC_PREREQ([2.69]) dnl for _AC_LANG_PREFIX AC_CACHE_CHECK([for -std=gnu17 support in _AC_LANG compiler], ga_cv_[]_AC_LANG_ABBREV[]_gnu_std_gnu17, [ ga_cv_[]_AC_LANG_ABBREV[]_gnu_std_gnu17= save_[]_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS" []_AC_LANG_PREFIX[]FLAGS="$save_[]_AC_LANG_PREFIX[]FLAGS -std=gnu17" save_ac_[]_AC_LANG_ABBREV[]_werror_flag="$ac_[]_AC_LANG_ABBREV[]_werror_flag" AC_LANG_WERROR rm -f a.out touch a.out AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], ga_cv_[]_AC_LANG_ABBREV[]_gnu_std_gnu17=-std=gnu17) []_AC_LANG_PREFIX[]FLAGS="$save_[]_AC_LANG_PREFIX[]FLAGS" ac_[]_AC_LANG_ABBREV[]_werror_flag="$save_ac_[]_AC_LANG_ABBREV[]_werror_flag" rm -f a.out ]) AC_SUBST([]_AC_LANG_PREFIX[]FLAG_STD_GNU17, $ga_cv_[]_AC_LANG_ABBREV[]_gnu_std_gnu17) ]) ga-5.9.2/m4/ga_lapack.m4000066400000000000000000000072001500715745200146070ustar00rootroot00000000000000# GA_F77_LAPACK_TEST # ------------------ # Generate Fortran 77 conftest for LAPACK. AC_DEFUN([GA_F77_LAPACK_TEST], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([], [[ implicit none external DGETRS external DSYGV external LSAME CALL DGETRS () CALL DSYGV () CALL LSAME ()]])]) ]) # GA_C_LAPACK_TEST # ---------------- # Generate C conftest for LAPACK. AC_DEFUN([GA_C_LAPACK_TEST], [AC_LANG_CONFTEST([AC_LANG_PROGRAM( [#ifdef __cplusplus extern "C" { #endif char dgetrs (); char dsygv (); char lsame (); #ifdef __cplusplus } #endif ], [[char result1 = dgetrs (); char result2 = dsygv (); char result3 = lsame (); ]])]) ]) # GA_LAPACK([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # --------------------------------------------------- # Test for LAPACK. Modelled after GA_BLAS macro. AC_DEFUN([GA_LAPACK], [AC_REQUIRE([GA_BLAS]) AC_ARG_WITH([lapack], [AS_HELP_STRING([--with-lapack=[[ARG]]], [use external LAPACK library])]) ga_lapack_ok=no AS_IF([test "x$with_lapack" = xno], [ga_lapack_ok=skip]) # Parse --with-lapack argument. Clear previous values first. LAPACK_LIBS= LAPACK_LDFLAGS= LAPACK_CPPFLAGS= GA_ARG_PARSE([with_lapack], [LAPACK_LIBS], [LAPACK_LDFLAGS], [LAPACK_CPPFLAGS]) # Get fortran linker name of LAPACK function to check for. AC_F77_FUNC(dgetrs) AC_F77_FUNC(dsygv) AC_F77_FUNC(lsame) ga_save_LIBS="$LIBS" ga_save_LDFLAGS="$LDFLAGS" ga_save_CPPFLAGS="$CPPFLAGS" LDFLAGS="$LAPACK_LDFLAGS $BLAS_LDFLAGS $LDFLAGS" CPPFLAGS="$LAPACK_CPPFLAGS $BLAS_CPPFLAGS $CPPFLAGS" AC_MSG_NOTICE([Attempting to locate LAPACK library]) # We cannot use LAPACK if BLAS is not found AS_IF([test $ga_blas_ok != yes], [ga_lapack_ok=noblas]) # First, check environment/command-line variables. # If failed, erase LAPACK_LIBS but maintain LAPACK_LDFLAGS and LAPACK_CPPFLAGS. AS_IF([test $ga_lapack_ok = no], [LIBS="$LAPACK_LIBS $BLAS_LIBS $LIBS" AS_IF([test "x$enable_f77" = xno], [AC_MSG_CHECKING([for C LAPACK with user-supplied flags]) AC_LANG_PUSH([C]) GA_C_LAPACK_TEST() AC_LINK_IFELSE([], [ga_lapack_ok=yes], [LAPACK_LIBS=]) AC_LANG_POP([C])], [AC_MSG_CHECKING([for Fortran 77 LAPACK with user-supplied flags]) AC_LANG_PUSH([Fortran 77]) GA_F77_LAPACK_TEST() AC_LINK_IFELSE([], [ga_lapack_ok=yes], [LAPACK_LIBS=]) AC_LANG_POP([Fortran 77])]) AC_MSG_RESULT([$ga_lapack_ok]) LIBS="$ga_save_LIBS"]) # Generic LAPACK library? for lib in lapack lapack_rs6k; do AS_IF([test $ga_lapack_ok = no], [ga_save_LIBS="$LIBS"; LIBS="-l$lib $BLAS_LIBS $LIBS"; LAPACK_LIBS="-l$lib" AS_IF([test "x$enable_f77" = xno], [AC_MSG_CHECKING([for C LAPACK using -l$lib]) AC_LANG_PUSH([C]) GA_C_LAPACK_TEST() AC_LINK_IFELSE([], [ga_lapack_ok=yes], [LAPACK_LIBS=]) AC_LANG_POP([C])], [AC_MSG_CHECKING([for Fortran 77 LAPACK using -l$lib]) AC_LANG_PUSH([Fortran 77]) GA_F77_LAPACK_TEST() AC_LINK_IFELSE([], [ga_lapack_ok=yes], [LAPACK_LIBS=]) AC_LANG_POP([Fortran 77])]) AC_MSG_RESULT([$ga_lapack_ok]) LIBS="$ga_save_LIBS"]) done CPPFLAGS="$ga_save_CPPFLAGS" LDFLAGS="$ga_save_LDFLAGS" AC_SUBST([LAPACK_LIBS]) AC_SUBST([LAPACK_LDFLAGS]) AC_SUBST([LAPACK_CPPFLAGS]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: AS_IF([test $ga_lapack_ok = yes], [have_lapack=1 $1], [AC_MSG_WARN([LAPACK library not found, using internal LAPACK]) have_lapack=0 $2]) AC_SUBST([have_lapack]) AC_DEFINE_UNQUOTED([HAVE_LAPACK], [$have_lapack], [Define to 1 if using external LAPACK library]) AM_CONDITIONAL([HAVE_LAPACK], [test $ga_lapack_ok = yes]) ])dnl GA_LAPACK ga-5.9.2/m4/ga_mpi_test_disable.m4000066400000000000000000000004371500715745200166700ustar00rootroot00000000000000# GA_DISABLE_MPI_TESTS() # --------------------- # Whether to disable all MPI linker tests. AC_DEFUN([GA_DISABLE_MPI_TESTS], [AC_ARG_ENABLE([mpi-tests], [AS_HELP_STRING([--disable-mpi-tests], [disable MPI linker tests])], [], [enable_mpi_tests=yes]) ])# GA_DISABLE_MPI_TESTS ga-5.9.2/m4/ga_mpi_unwrap.m4000066400000000000000000000223751500715745200155470ustar00rootroot00000000000000# GA_MPI_UNWRAP() # --------------- # Attempt to unwrap the MPI compiler for the current language and determine # the underlying compiler. # # The strategy is to first compare the stdout of the version flags using # a custom perl script. Next we combine stdout and sterr for the comparison. # Occasionally, the MPI compiler will always report a non-zero exit status. # That is the last case checked for. AC_DEFUN([GA_MPI_UNWRAP], [ # Find perl. AC_PATH_PROG([PERL], [perl]) # Create inside.pl. rm -f inside.pl [cat >inside.pl <<"EOF" #!/usr/bin/perl use strict; use warnings; my $numargs = @S|@#ARGV + 1; if ($numargs != 2) { print "Usage: wrapped.txt naked.txt\n"; exit 1; } # Read each input file as a string (rather than a list). local $/=undef; open WRAPPED, "$ARGV[0]" or die "Could not open wrapped text file: $!"; my $wrapped_lines = ; close WRAPPED; open NAKED, "$ARGV[1]" or die "Could not open naked text file: $!"; my $naked_lines = ; close NAKED; # Replace newlines, + from wrapped and naked lines. $wrapped_lines =~ tr/\n+/ /; $naked_lines =~ tr/\n+/ /; # Remove whitespace from beginning of wrapped and naked lines. $wrapped_lines =~ s/^\s+//; $naked_lines =~ s/^\s+//; # Remove whitespace from end of wrapped and naked lines. $wrapped_lines =~ s/\s+$//; $naked_lines =~ s/\s+$//; # If either wrapped_lines or naked_lines are empty, this is an error. # It is assumed that the particular version string which created the input # files should generate SOMETHING. unless ($wrapped_lines) { exit 1; } unless ($naked_lines) { exit 1; } # Cray compilers append a timestamp into their version string. Remove it. if ($wrapped_lines =~ /\QCray\E/) { $wrapped_lines = substr $wrapped_lines, 0, -28; $naked_lines = substr $naked_lines, 0, -28; } # Can the naked lines be found within the wrapped lines? if ($wrapped_lines =~ /\Q$naked_lines\E/) { #print "Found as substring\n"; exit 0; } # Are the naked lines exactly the same as the wrapped lines? elsif ($wrapped_lines eq $naked_lines) { #print "Found equal\n"; exit 0; } else { #print "Not found\n"; exit 1; } EOF] inside="$PERL inside.pl" wrapped="$_AC_CC" AC_LANG_CASE( [C], [AS_CASE([$wrapped], [*_r], [compilers="bgxlc_r xlc_r"], [*], [compilers="bgxlc xlc pgcc pathcc icc sxcc fcc opencc suncc craycc gcc ecc cl ccc cc"]) ], [C++], [AS_CASE([$wrapped], [*_r], [compilers="bgxlC_r xlC_r"], [*], [compilers="icpc pgCC pathCC sxc++ xlC bgxlC openCC sunCC craycxx g++ c++ gpp aCC cxx cc++ cl.exe FCC KCC RCC CC"]) ], [Fortran 77], [AS_CASE([$wrapped], [*_r], [compilers="bgxlf95_r xlf95_r bgxlf90_r xlf90_r bgxlf_r xlf_r"], [*], [compilers="gfortran g95 bgxlf95 xlf95 f95 fort ifort ifc efc pgf95 pathf95 lf95 openf95 sunf95 crayftn bgxlf90 xlf90 f90 pgf90 pathf90 pghpf epcf90 sxf90 openf90 sunf90 g77 bgxlf xlf f77 frt pgf77 pathf77 cf77 fort77 fl32 af77"]) ], [Fortran], [ ]) AS_VAR_PUSHDEF([ga_save_comp], [ga_save_[]_AC_CC[]]) AS_VAR_PUSHDEF([ga_orig_comp], [ga_orig_[]_AC_CC[]]) AS_VAR_PUSHDEF([ga_cv_mpi_naked], [ga_cv_mpi[]_AC_LANG_ABBREV[]_naked]) AC_CACHE_CHECK([for base $wrapped compiler], [ga_cv_mpi_naked], [ base="`$wrapped -show 2>/dev/null | sed 's/@<:@ @:>@.*@S|@//' | head -1`" ga_save_comp="$_AC_CC" _AC_CC="$base" # if -m32 or -m64 is present in the -show params, or separately, add it unwrapped_args="`$wrapped -show 2>/dev/null`" extra_arg="" for arg in $unwrapped_args $wrapped do AS_CASE([$arg], [*-m32*], [extra_arg="-m32"], [*-m64*], [extra_arg="-m64"]) done AS_IF([test "x$extra_arg" != x], [base="$base $extra_arg"]) AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [ga_cv_mpi_naked="$base"]) _AC_CC="$ga_save_comp" versions="--version -v -V -qversion" found_wrapped_version=0 # Try separating stdout and stderr. Only compare stdout. AS_IF([test "x$ga_cv_mpi_naked" = x], [ # prepend any CC/CXX/F77 the user may have specified compilers="$ga_orig_comp $compilers" echo "only comparing stdout" >&AS_MESSAGE_LOG_FD for version in $versions do echo "trying version=$version" >&AS_MESSAGE_LOG_FD rm -f mpi.txt mpi.err naked.txt naked.err AS_IF([$wrapped $version 1>mpi.txt 2>mpi.err], [found_wrapped_version=1 for naked_compiler in $compilers do AS_IF([test "x$naked_compiler" != "x$wrapped"], [AS_IF([$naked_compiler $version 1>naked.txt 2>naked.err], [AS_IF([$inside mpi.txt naked.txt >/dev/null], [ga_cv_mpi_naked=$naked_compiler; break], [echo "inside.pl $wrapped $naked_compiler failed, skipping" >&AS_MESSAGE_LOG_FD])], [echo "$naked_compiler $version failed, skipping" >&AS_MESSAGE_LOG_FD])]) done], [echo "$wrapped $version failed, skipping" >&AS_MESSAGE_LOG_FD]) AS_IF([test "x$ga_cv_mpi_naked" != x], [break]) done ]) # Perhaps none of the MPI compilers had a zero exit status (this is bad). # In this case we have to do a brute force match regardless of exit status. AS_IF([test "x$found_wrapped_version" = x0], [ AS_IF([test "x$ga_cv_mpi_naked" = x], [ echo "no zero exit status found for MPI compilers" >&AS_MESSAGE_LOG_FD for version in $versions do echo "trying version=$version" >&AS_MESSAGE_LOG_FD rm -f mpi.txt mpi.err $wrapped $version 1>mpi.txt 2>mpi.err for naked_compiler in $compilers do AS_IF([test "x$naked_compiler" != "x$wrapped"], [rm -f naked.txt naked.err AS_IF([$naked_compiler $version 1>naked.txt 2>naked.err], [AS_IF([$inside mpi.txt naked.txt >/dev/null], [ga_cv_mpi_naked=$naked_compiler; break], [echo "inside.pl $wrapped $naked_compiler failed, skipping" >&AS_MESSAGE_LOG_FD])], [echo "$naked_compiler $version failed, skipping" >&AS_MESSAGE_LOG_FD])]) done AS_IF([test "x$ga_cv_mpi_naked" != x], [break]) done ]) ]) # Try by combining stdout/err into one file. AS_IF([test "x$ga_cv_mpi_naked" = x], [ echo "try combining stdout and stderr into one file" >&AS_MESSAGE_LOG_FD for version in $versions do echo "trying version=$version" >&AS_MESSAGE_LOG_FD rm -f mpi.txt naked.txt AS_IF([$wrapped $version 1>mpi.txt 2>&1], [for naked_compiler in $compilers do AS_IF([test "x$naked_compiler" != "x$wrapped"], [AS_IF([$naked_compiler $version 1>naked.txt 2>&1], [AS_IF([$inside mpi.txt naked.txt >/dev/null], [ga_cv_mpi_naked=$naked_compiler; break], [echo "inside.pl $wrapped $naked_compiler failed, skipping" >&AS_MESSAGE_LOG_FD])], [echo "$naked_compiler $version failed, skipping" >&AS_MESSAGE_LOG_FD])]) done], [echo "$wrapped $version failed, skipping" >&AS_MESSAGE_LOG_FD]) AS_IF([test "x$ga_cv_mpi_naked" != x], [break]) done ]) # If we got this far, then it's likely that the MPI compiler had a zero exit # status when it shouldn't have for one version flag, but later had a non-zero # exit status for a flag it shouldn't have. One false positive hid a false # negative. In this case, brute force compare all MPI compiler output against # all compiler output. AS_IF([test "x$ga_cv_mpi_naked" = x], [ echo "we have a very badly behaving MPI compiler" >&AS_MESSAGE_LOG_FD for version in $versions do echo "trying version=$version" >&AS_MESSAGE_LOG_FD rm -f mpi.txt mpi.err $wrapped $version 1>mpi.txt 2>mpi.err for naked_compiler in $compilers do AS_IF([test "x$naked_compiler" != "x$wrapped"], [rm -f naked.txt naked.err AS_IF([$naked_compiler $version 1>naked.txt 2>naked.err], [AS_IF([$inside mpi.txt naked.txt >/dev/null], [ga_cv_mpi_naked=$naked_compiler; break], [echo "inside.pl $wrapped $naked_compiler failed, skipping" >&AS_MESSAGE_LOG_FD])], [echo "$naked_compiler $version failed, skipping" >&AS_MESSAGE_LOG_FD])]) done AS_IF([test "x$ga_cv_mpi_naked" != x], [break]) done ]) rm -f mpi.txt mpi.err naked.txt naked.err ]) AS_IF([test "x$ga_cv_mpi_naked" = x], [AC_MSG_ERROR([Could not determine the ]_AC_LANG[ compiler wrapped by MPI])], [AS_IF([test "x$ga_orig_comp" != x && test "x$ga_orig_comp" != "x$ga_cv_mpi_naked"], [AC_MSG_WARN([unwrapped $wrapped ($ga_cv_mpi_naked) does not match user-specified $ga_orig_comp])])]) AS_VAR_POPDEF([ga_save_comp]) AS_VAR_POPDEF([ga_cv_mpi_naked]) rm -f inside.pl ])dnl # GA_MPI_UNWRAP_PUSH() # -------------------- # Set CC/CXX/F77/FC to their unwrapped MPI counterparts. # Save their old values for restoring later. AC_DEFUN([GA_MPI_UNWRAP_PUSH], [ ga_mpi_unwrap_push_save_CC="$CC" ga_mpi_unwrap_push_save_CXX="$CXX" ga_mpi_unwrap_push_save_F77="$F77" ga_mpi_unwrap_push_save_FC="$FC" AS_IF([test "x$ga_cv_mpic_naked" != x], [ CC="$ga_cv_mpic_naked"]) AS_IF([test "x$ga_cv_mpicxx_naked" != x], [CXX="$ga_cv_mpicxx_naked"]) AS_IF([test "x$ga_cv_mpif77_naked" != x], [F77="$ga_cv_mpif77_naked"]) AS_IF([test "x$ga_cv_mpifc_naked" != x], [ FC="$ga_cv_mpifc_naked"]) ])dnl # GA_MPI_UNWRAP_POP() # ------------------- # Restore CC/CXX/F77/FC to their MPI counterparts. AC_DEFUN([GA_MPI_UNWRAP_POP], [ CC="$ga_mpi_unwrap_push_save_CC" CXX="$ga_mpi_unwrap_push_save_CXX" F77="$ga_mpi_unwrap_push_save_F77" FC="$ga_mpi_unwrap_push_save_FC" ])dnl ga-5.9.2/m4/ga_mpicc.m4000066400000000000000000000043551500715745200144570ustar00rootroot00000000000000# GA_PROG_MPICC # ---------------- # If desired, replace CC with MPICC while searching for a C compiler. # # Known C compilers # cc generic compiler name # cl # gcc GNU # icc Intel # xlc Intel # xlc_r Intel, thread safe # pgcc Portland Group # pathcc PathScale # fcc Fujitsu # opencc AMD's x86 open64 # suncc Sun's Studio # craycc Cray # # Known MPI C compilers: # mpicc # mpixlc_r # mpixlc # hcc # mpxlc_r # mpxlc # mpifcc Fujitsu # mpgcc # mpcc # cmpicc # cc # AC_DEFUN([GA_PROG_MPICC], [AC_ARG_VAR([MPICC], [MPI C compiler]) # In the case of using MPI wrappers, set CC=MPICC since CC will override # absolutely everything in our list of compilers. # Save CC, just in case. AS_IF([test x$with_mpi_wrappers = xyes], [AS_IF([test "x$CC" != "x$MPICC"], [ga_orig_CC="$CC"]) AS_CASE([x$CC:x$MPICC], [x:x], [], [x:x*], [CC="$MPICC"], [x*:x], [AC_MSG_WARN([MPI compilers desired but CC is set while MPICC is unset.]) AC_MSG_WARN([CC will be ignored during compiler selection, but will be]) AC_MSG_WARN([tested first during MPI compiler unwrapping. Perhaps you]) AC_MSG_WARN([meant to set MPICC instead of or in addition to CC?]) CC=], [x*:x*], [AS_IF([test "x$CC" != "x$MPICC"], [AC_MSG_WARN([MPI compilers desired, MPICC and CC are set, and MPICC!=CC.]) AC_MSG_WARN([Choosing MPICC as main compiler.]) AC_MSG_WARN([CC will be assumed as the unwrapped MPI compiler.])]) ga_cv_mpic_naked="$CC" CC="$MPICC"], [AC_MSG_ERROR([CC/MPICC case failure])])]) ga_cc="xlc_r xlc pgcc pathcc icc sxcc fcc opencc suncc craycc gcc cc ecc cl ccc" ga_mpicc="mpicc mpixlc_r mpixlc hcc mpxlc_r mpxlc sxmpicc mpifcc mpgcc mpcc cmpicc cc" AS_IF([test x$with_mpi_wrappers = xyes], [CC_TO_TEST="$ga_mpicc_pref $ga_mpicc"], [CC_TO_TEST="$ga_cc_pref $ga_cc"]) AC_PROG_CC([$CC_TO_TEST]) ])dnl # GA_PROG_CC_NOMPI # ---------------- # In at least one case, GA needs a standard C compiler i.e. not an MPI # wrapper. Find one and set it to CC_NOMPI (not CC). Defaults to the CC found # by GA_PROG_MPICC. AC_DEFUN([GA_PROG_CC_NOMPI], [AS_IF([test "x$ga_cv_mpic_naked" != x], [CC_NOMPI="$ga_cv_mpic_naked" AC_SUBST([CC_NOMPI])], [AC_CHECK_PROGS([CC_NOMPI], [$ga_cc], [$CC])]) ])dnl ga-5.9.2/m4/ga_mpicc_test.m4000066400000000000000000000105301500715745200155060ustar00rootroot00000000000000# GA_MPICC_TEST_PROGRAM # --------------------- # Create an MPI test program in C. AC_DEFUN([GA_MPICC_TEST_PROGRAM], [ AC_LANG_PUSH([C]) AC_LANG_CONFTEST([AC_LANG_PROGRAM([[#include ]], [[int myargc; char **myargv; MPI_Init(&myargc, &myargv); MPI_Finalize();]])]) AC_LANG_POP([C]) ])dnl # GA_MPICC_TEST_COMPILE # --------------------- # Attempt to compile a simple MPI program in C. AC_DEFUN([GA_MPICC_TEST_COMPILE], [ AC_LANG_PUSH([C]) GA_MPICC_TEST_PROGRAM() AC_CACHE_CHECK([whether a simple C MPI program compiles], [ga_cv_c_mpi_test_compile], [ga_save_CPPFLAGS="$CPPFLAGS"; CPPFLAGS="$CPPFLAGS $GA_MP_CPPFLAGS" AC_COMPILE_IFELSE([], [ga_cv_c_mpi_test_compile=yes], [ga_cv_c_mpi_test_compile=no]) CPPFLAGS="$ga_save_CPPFLAGS"]) rm -f conftest.$ac_ext AC_LANG_POP([C]) AS_IF([test "x$ga_cv_c_mpi_test_compile" = xno], [AC_MSG_FAILURE([could not compile simple C MPI program])]) ])dnl # GA_MPICC_TEST_LINK # ------------------ # Attempt to link a simple MPI program in C. AC_DEFUN([GA_MPICC_TEST_LINK], [ AC_LANG_PUSH([C]) GA_MPICC_TEST_PROGRAM() ga_cv_c_mpi_test_link=no AS_IF([test "x$ga_cv_c_mpi_test_link" = xno], [AC_MSG_CHECKING([whether a C MPI program links natively]) AC_LINK_IFELSE([], [ga_cv_c_mpi_test_link=yes GA_MP_LIBS= GA_MP_LDFLAGS= GA_MP_CPPFLAGS=], [ga_cv_c_mpi_test_link=no]) AC_MSG_RESULT([$ga_cv_c_mpi_test_link])]) # That didn't work, so now let's try adding our GA_MP_* flags. # The CPPFLAGS and LDFLAGS are added up top here, but LIBS will change. ga_save_LIBS="$LIBS" ga_save_CPPFLAGS="$CPPFLAGS"; CPPFLAGS="$CPPFLAGS $GA_MP_CPPFLAGS" ga_save_LDFLAGS="$LDFLAGS"; LDFLAGS="$LDFLAGS $GA_MP_LDFLAGS" AS_IF([test "x$ga_cv_c_mpi_test_link" = xno], [AC_MSG_CHECKING([whether a C MPI program links with additional env]) LIBS="$LIBS $GA_MP_LIBS" AC_LINK_IFELSE([], [ga_cv_c_mpi_test_link=yes], [ga_cv_c_mpi_test_link=no]) LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_cv_c_mpi_test_link])]) # That didn't work, so now let's try with specific libs. AS_IF([test "x$ga_cv_c_mpi_test_link" = xno], [AC_MSG_CHECKING([for mvapich libraries]) for lib in "-lmpich -lpthread" "-lmpich" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [ga_cv_c_mpi_test_link="$lib"; break], [ga_cv_c_mpi_test_link=no]) LIBS="$ga_save_LIBS" done LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_cv_c_mpi_test_link])]) AS_IF([test "x$ga_cv_c_mpi_test_link" = xno], [AC_MSG_CHECKING([for mpich libraries]) for lib in "-lmpich -lpthread" "-lmpich" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [ga_cv_c_mpi_test_link="$lib"; break], [ga_cv_c_mpi_test_link=no]) LIBS="$ga_save_LIBS" done LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_cv_c_mpi_test_link])]) AS_IF([test "x$ga_cv_c_mpi_test_link" = xno], [AC_MSG_CHECKING([for hpmpi libraries]) for lib in "-lhpmpio -lhpmpi" "-lhpmpi" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [ga_cv_c_mpi_test_link="$lib"; break], [ga_cv_c_mpi_test_link=no]) LIBS="$ga_save_LIBS" done LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_cv_c_mpi_test_link])]) AS_IF([test "x$ga_cv_c_mpi_test_link" = xno], [AC_MSG_CHECKING([for intelmpi libraries]) for lib in "-lmpi -lmpiif -lmpigi -lrt -lpthread" "-lmpi -lmpiif -lmpigi" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [ga_cv_c_mpi_test_link="$lib"; break], [ga_cv_c_mpi_test_link=no]) LIBS="$ga_save_LIBS" done LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_cv_c_mpi_test_link])]) AS_IF([test "x$ga_cv_c_mpi_test_link" = xno], [AC_MSG_CHECKING([for openmpi libraries]) for lib in "-lmpi -lpthread" "-lmpi" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [ga_cv_c_mpi_test_link="$lib"; break], [ga_cv_c_mpi_test_link=no]) LIBS="$ga_save_LIBS" done LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_cv_c_mpi_test_link])]) rm -f conftest.$ac_ext LIBS="$ga_save_LIBS" LDFLAGS="$ga_save_LDFLAGS" CPPFLAGS="$ga_save_CPPFLAGS" AC_LANG_POP([C]) AS_CASE([$ga_cv_c_mpi_test_link], [yes], [], [no], [AC_MSG_FAILURE([could not link a C MPI program])], [*], [GA_MP_LIBS="$ga_cv_c_mpi_test_link"], []) ])dnl ga-5.9.2/m4/ga_mpicxx.m4000066400000000000000000000041561500715745200146730ustar00rootroot00000000000000# GA_PROG_MPICXX # -------------- # If desired, replace CXX with MPICXX while searching for a C++ compiler. # # Known C++ compilers: # aCC HP-UX C++ compiler much better than `CC', so test before. # c++ # cc++ # CC # cl.exe # cxx # FCC Fujitsu C++ compiler # g++ # gpp # icpc Intel C++ compiler # KCC KAI C++ compiler # RCC Rational C++ # xlC AIX C Set++ # xlC_r AIX C Set++, thread safe # pgCC Portland Group # pathCC PathScale # openCC AMD's x86 open64 # sunCC Sun's Studio # craycxx Cray # # Known MPI C++ compilers # mpic++ # mpicxx # mpiCC # hcp # mpxlC_r # mpxlC # mpixlcxx_r # mpixlcxx # mpg++ # mpc++ # mpCC # cmpic++ # mpiFCC Fujitsu # CC # AC_DEFUN([GA_PROG_MPICXX], [AC_ARG_VAR([MPICXX], [MPI C++ compiler]) # In the case of using MPI wrappers, set CXX=MPICXX since CXX will override # absolutely everything in our list of compilers. AS_IF([test x$with_mpi_wrappers = xyes], [AS_IF([test "x$CXX" != "x$MPICXX"], [ga_orig_CXX="$CXX"]) AS_CASE([x$CXX:x$MPICXX], [x:x], [], [x:x*], [CXX="$MPICXX"], [x*:x], [AC_MSG_WARN([MPI compilers desired but CXX is set while MPICXX is unset.]) AC_MSG_WARN([CXX will be ignored during compiler selection, but will be]) AC_MSG_WARN([tested first during MPI compiler unwrapping. Perhaps you]) AC_MSG_WARN([meant to set MPICXX instead of or in addition to CXX?]) CXX=], [x*:x*], [AS_IF([test "x$CXX" != "x$MPICXX"], [AC_MSG_WARN([MPI compilers desired, MPICXX and CXX are set, and MPICXX!=CXX.]) AC_MSG_WARN([Choosing MPICXX as main compiler.]) AC_MSG_WARN([CXX will be assumed as the unwrapped MPI compiler.])]) ga_cv_mpicxx_naked="$CXX" CXX="$MPICXX"], [AC_MSG_ERROR([CXX/MPICXX case failure])])]) ga_cxx="icpc pgCC pathCC sxc++ xlC_r xlC openCC sunCC g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC" ga_mpicxx="mpic++ mpicxx mpiCC sxmpic++ hcp mpxlC_r mpxlC mpixlcxx_r mpixlcxx mpg++ mpc++ mpCC cmpic++ mpiFCC CC" AS_IF([test x$with_mpi_wrappers = xyes], [CXX_TO_TEST="$ga_mpicxx_pref $ga_mpicxx"], [CXX_TO_TEST="$ga_cxx_pref $ga_cxx"]) AC_PROG_CXX([$CXX_TO_TEST]) ])dnl ga-5.9.2/m4/ga_mpicxx_test.m4000066400000000000000000000043161500715745200157300ustar00rootroot00000000000000# GA_MPICXX_TEST # -------------- # Attempt to compile a simple MPI program in C++. # AC_DEFUN([GA_MPICXX_TEST], [ AS_IF([test "x$with_mpi" != xno], [ AC_LANG_PUSH([C++]) AC_CACHE_CHECK([whether a simple C++ MPI program works], [ga_cv_cxx_mpi_test], [ AC_LINK_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[int myargc; char **myargv; MPI_Init(&myargc, &myargv); MPI_Finalize();]])], [ga_cv_cxx_mpi_test=yes], [ga_cv_cxx_mpi_test=no]) # That didn't work, so now let's try with our GA_MP_* flags. AS_IF([test "x$ga_cv_cxx_mpi_test" = xno], [ ga_save_LIBS="$LIBS"; LIBS="$LIBS $GA_MP_LIBS" ga_save_CPPFLAGS="$CPPFLAGS"; CPPFLAGS="$CPPFLAGS $GA_MP_CPPFLAGS" ga_save_LDFLAGS="$LDFLAGS"; LDFLAGS="$LDFLAGS $GA_MP_LDFLAGS" AC_LINK_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[int myargc; char **myargv; MPI_Init(&myargc, &myargv); MPI_Finalize();]])], [ga_cv_cxx_mpi_test=yes], [ga_cv_cxx_mpi_test=no]) LIBS="$ga_save_LIBS" CPPFLAGS="$ga_save_CPPFLAGS" LDFLAGS="$ga_save_LDFLAGS" ]) # That didn't work, so now let's try with our GA_MP_* flags and various libs. AS_IF([test "x$ga_cv_cxx_mpi_test" = xno], [ for lib in -lmpi -lmpich; do ga_save_LIBS="$LIBS"; LIBS="$LIBS $GA_MP_LIBS $lib" ga_save_CPPFLAGS="$CPPFLAGS"; CPPFLAGS="$CPPFLAGS $GA_MP_CPPFLAGS" ga_save_LDFLAGS="$LDFLAGS"; LDFLAGS="$LDFLAGS $GA_MP_LDFLAGS" AC_LINK_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[int myargc; char **myargv; MPI_Init(&myargc, &myargv); MPI_Finalize();]])], [ga_cv_cxx_mpi_test=$lib; break], [ga_cv_cxx_mpi_test=no]) LIBS="$ga_save_LIBS" CPPFLAGS="$ga_save_CPPFLAGS" LDFLAGS="$ga_save_LDFLAGS" done LIBS="$ga_save_LIBS" CPPFLAGS="$ga_save_CPPFLAGS" LDFLAGS="$ga_save_LDFLAGS" ]) ]) AC_LANG_POP([C++]) AS_CASE([$ga_cv_cxx_mpi_test], [yes], [], [no], [AC_MSG_FAILURE([could not link simple C++ MPI program])], [*], [GA_MP_LIBS="$ga_cv_cxx_mpi_test"], []) ]) ])dnl ga-5.9.2/m4/ga_mpif77.m4000066400000000000000000000102411500715745200144640ustar00rootroot00000000000000# GA_PROG_MPIF77 # ----------------- # If desired, replace F77 with MPIF77 while searching for a Fortran 77 compiler. # We look for 95/90 compilers first so that we can control the INTEGER size. # The search order changes depending on the TARGET. # # NOTE: We prefer "FC" and "FCFLAGS" over "F77" and "FFLAGS", respectively. # But our Fortran source is only Fortran 77. If FC/MPIFC is set, it is # preferred above all. # # Known Fortran 95 compilers: # f95 generic compiler name # fort Compaq/HP Fortran 90/95 compiler for Tru64 and Linux/Alpha # ftn native Fortran 95 compiler on Cray # g95 original gcc-based f95 compiler (gfortran is a fork) # gfortran GNU Fortran 95+ compiler (released in gcc 4.0) # ifc Intel Fortran 95 compiler for Linux/x86 (now ifort) # ifort Intel Fortran 95 compiler for Linux/x86 (was ifc) # pghpf/pgf95 Portland Group F95 compiler # xlf95 IBM (AIX) F95 compiler # pathf95 PathScale # openf95 AMD's x86 open64 # sunf95 Sun's Studio # crayftn Cray # # Known MPI Fortran 95 compilers: # cmpifc ?? not sure if this is even F95 # ftn native Fortran 95 compiler on Cray # mpif95 generic compiler name # mpixlf95 IBM Blue Gene Fortran 95 # mpixlf95_r IBM Blue Gene Fortran 95, reentrant code # # Known Fortran 90 compilers: # epcf90 "Edinburgh Portable Compiler" F90 # f90 generic compiler name # fort Compaq/HP Fortran 90/95 compiler for Tru64 and Linux/Alpha # pgf90 Portland Group F90 compiler # xlf90 IBM (AIX) F90 compiler # pathf90 PathScale # openf90 AMD's x86 open64 # sunf90 Sun's Studio # # Known MPI Fortran 90 compilers: # mpifrt Fujitsu # mpif90 generic compiler name # # Known Fortran 77 compilers: # af77 Apogee F77 compiler for Intergraph hardware running CLIX # f77 generic compiler names # fl32 Microsoft Fortran 77 "PowerStation" compiler # fort77 native F77 compiler on older UNIX systems # frt Fujitsu F77 compiler # g77 GNU Fortran 77 compiler # pgf77 Portland Group F77 compiler # xlf IBM (AIX) F77 compiler # pathf77 PathScale # # Known MPI Fortran 77 compilers: # mpif77 generic compiler name # mpifrt Fujitsu # AC_DEFUN([GA_PROG_MPIF77], [AC_ARG_VAR([MPIF77], [MPI Fortran 77 compiler]) # If FC is set, override F77. Similarly for MPIFC/MPIF77 and FCFLAGS/FFLAGS. AS_IF([test "x$FC" != x], [F77="$FC"]) AS_IF([test "x$MPIFC" != x], [MPIF77="$MPIFC"]) AS_IF([test "x$FCFLAGS" != x], [FFLAGS="$FCFLAGS"]) # In the case of using MPI wrappers, set F77=MPIF77 since F77 will override # absolutely everything in our list of compilers. # Save F77, just in case. AS_IF([test x$with_mpi_wrappers = xyes], [AS_IF([test "x$F77" != "x$MPIF77"], [ga_orig_F77="$F77"]) AS_CASE([x$F77:x$MPIF77], [x:x], [], [x:x*], [F77="$MPIF77"], [x*:x], [AC_MSG_WARN([MPI compilers desired but F77 is set while MPIF77 is unset.]) AC_MSG_WARN([F77 will be ignored during compiler selection, but will be]) AC_MSG_WARN([tested first during MPI compiler unwrapping. Perhaps you]) AC_MSG_WARN([meant to set MPIF77 instead of or in addition to F77?]) F77=], [x*:x*], [AS_IF([test "x$F77" != "x$MPIF77"], [AC_MSG_WARN([MPI compilers desired, MPIF77 and F77 are set, and MPIF77!=F77.]) AC_MSG_WARN([Choosing MPIF77 as main compiler.]) AC_MSG_WARN([F77 will be assumed as the unwrapped MPI compiler.])]) ga_cv_mpif77_naked="$F77" F77="$MPIF77"], [AC_MSG_ERROR([F77/MPIF77 case failure])])]) ga_mpif95="mpif95 mpixlf95_r mpixlf95 ftn" ga_mpif90="mpif90 mpixlf90_r mpixlf90 mpf90 cmpif90c sxmpif90" ga_mpif77="mpif77 hf77 mpixlf_r mpixlf mpifrt mpf77 cmpifc" ga_f95="xlf95 pgf95 pathf95 ifort g95 f95 fort ifc efc openf95 sunf95 crayftn gfortran lf95 ftn" ga_f90="xlf90 f90 pgf90 pghpf pathf90 epcf90 sxf90 openf90 sunf90" ga_f77="xlf f77 frt pgf77 pathf77 g77 cf77 fort77 fl32 af77" AS_IF([test x$with_mpi_wrappers = xyes], [F77_TO_TEST="$ga_mpif77_pref $ga_mpif95 $ga_mpif90 $ga_mpif77"], [F77_TO_TEST="$ga_f77_pref $ga_f95 $ga_f90 $ga_f77"]) AC_PROG_F77([$F77_TO_TEST]) ])dnl ga-5.9.2/m4/ga_mpif77_test.m4000066400000000000000000000113751500715745200155340ustar00rootroot00000000000000# GA_MPIF77_TEST_PROGRAM # ---------------------- # Create an MPI test program in Fortran 77. AC_DEFUN([GA_MPIF77_TEST_PROGRAM], [ AC_LANG_PUSH([Fortran 77]) AC_LANG_CONFTEST([AC_LANG_PROGRAM([], [[ include 'mpif.h' integer ierr call MPI_Init( ierr ) call MPI_Finalize( ierr )]])]) AC_LANG_POP([Fortran 77]) ])dnl # GA_MPIF77_TEST_COMPILE # ---------------------- # Attempt to compile a simple MPI program in Fortran 77. AC_DEFUN([GA_MPIF77_TEST_COMPILE], [ AC_LANG_PUSH([Fortran 77]) GA_MPIF77_TEST_PROGRAM() AC_CACHE_CHECK([whether a simple Fortran MPI program compiles], [ga_cv_f77_mpi_test_compile], [ga_save_FFLAGS="$FFLAGS"; FFLAGS="$FFLAGS $GA_MP_CPPFLAGS" AC_COMPILE_IFELSE([], [ga_cv_f77_mpi_test_compile=yes], [ga_cv_f77_mpi_test_compile=no]) FFLAGS="$ga_save_FFLAGS"]) rm -f conftest.$ac_ext AC_LANG_POP([Fortran 77]) AS_IF([test "x$ga_cv_f77_mpi_test_compile" = xno], [AC_MSG_FAILURE([could not compile simple Fortran MPI program])]) ])dnl # GA_MPIF77_TEST_LINK # ------------------- # Attempt to compile a simple MPI program in Fortran 77. AC_DEFUN([GA_MPIF77_TEST_LINK], [ AC_LANG_PUSH([Fortran 77]) GA_MPIF77_TEST_PROGRAM() ga_cv_f77_mpi_test_link=no AS_IF([test "x$ga_cv_f77_mpi_test_link" = xno], [AC_MSG_CHECKING([whether a Fortran MPI program links natively]) AC_LINK_IFELSE([], [ga_cv_f77_mpi_test_link=yes GA_MP_LIBS= GA_MP_LDFLAGS= GA_MP_CPPFLAGS=], [ga_cv_f77_mpi_test_link=no]) AC_MSG_RESULT([$ga_cv_f77_mpi_test_link])]) # That didn't work. Let's try adding our GA_MP_* flags. # The CPPFLAGS are added to FFLAGS since *.f doesn't use CPP. LIBS changes. ga_save_LIBS="$LIBS" ga_save_FFLAGS="$FFLAGS"; FFLAGS="$FFLAGS $GA_MP_CPPFLAGS" ga_save_LDFLAGS="$LDFLAGS"; LDFLAGS="$LDFLAGS $GA_MP_LDFLAGS" AS_IF([test "x$ga_cv_f77_mpi_test_link" = xno], [AC_MSG_CHECKING([whether a Fortran MPI program links with additional env]) LIBS="$LIBS $GA_MP_LIBS" AC_LINK_IFELSE([], [ga_cv_f77_mpi_test_link=yes], [ga_cv_f77_mpi_test_link=no]) LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_cv_f77_mpi_test_link])]) # That didn't work, so now let's try with specific libs. AS_IF([test "x$ga_cv_f77_mpi_test_link" = xno], [AC_MSG_CHECKING([for mvapich libraries]) for lib in "-lmpichf90nc -lmpichfarg -lmpich -lpthread" "-lmpichf90 -lmpichfarg -lmpich -pthread" "-lmpichf90nc -lmpichfarg -lmpich" "-lmpichf90 -lmpichfarg -lmpich" "-lmpichfarg -lmpich -lpthread" "-lmpichfarg -lmpich" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [ga_cv_f77_mpi_test_link="$lib"; break], [ga_cv_f77_mpi_test_link=no]) LIBS="$ga_save_LIBS" done LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_cv_f77_mpi_test_link])]) AS_IF([test "x$ga_cv_f77_mpi_test_link" = xno], [AC_MSG_CHECKING([for mpich libraries]) for lib in "-lmpichf90 -lmpich -lpthread" "-lmpichf90 -lmpich" "-lmpich -pthread" "-lmpich" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [ga_cv_f77_mpi_test_link="$lib"; break], [ga_cv_f77_mpi_test_link=no]) LIBS="$ga_save_LIBS" done LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_cv_f77_mpi_test_link])]) AS_IF([test "x$ga_cv_f77_mpi_test_link" = xno], [AC_MSG_CHECKING([for hpmpi libraries]) for lib in "-lhpmpio -lhpmpi" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [ga_cv_f77_mpi_test_link="$lib"; break], [ga_cv_f77_mpi_test_link=no]) LIBS="$ga_save_LIBS" done LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_cv_f77_mpi_test_link])]) AS_IF([test "x$ga_cv_f77_mpi_test_link" = xno], [AC_MSG_CHECKING([for intelmpi libraries]) for lib in "-lmpi -lmpigf -lmpigi -lpthread" "-lmpi -lmpigf -lmpigi" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [ga_cv_f77_mpi_test_link="$lib"; break], [ga_cv_f77_mpi_test_link=no]) LIBS="$ga_save_LIBS" done LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_cv_f77_mpi_test_link])]) AS_IF([test "x$ga_cv_f77_mpi_test_link" = xno], [AC_MSG_CHECKING([for openmpi libraries]) for lib in "-lmpi_f90 -lmpi_f77 -lmpi" "-lmpi_f77 -lmpi" do LIBS="$LIBS $lib" AC_LINK_IFELSE([], [ga_cv_f77_mpi_test_link="$lib"; break], [ga_cv_f77_mpi_test_link=no]) LIBS="$ga_save_LIBS" done LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_cv_f77_mpi_test_link])]) rm -f conftest.$ac_ext LIBS="$ga_save_LIBS" LDFLAGS="$ga_save_LDFLAGS" FFLAGS="$ga_save_FFLAGS" AC_LANG_POP([Fortran 77]) AS_CASE([$ga_cv_f77_mpi_test_link], [yes], [], [no], [AC_MSG_FAILURE([could not link simple Fortran MPI program])], [*], [GA_MP_LIBS="$ga_cv_f77_mpi_test_link"], []) ])dnl ga-5.9.2/m4/ga_msg_comms.m4000066400000000000000000000025241500715745200153440ustar00rootroot00000000000000# GA_MSG_COMMS() # -------------- # Establishes all things related to messageing libraries. # This includes the compilers to use (either standard or MPI wrappers) # or the proper linker flags (-L), libs (-l) or preprocessor directives (-I). # Yes, it's a beefy AC macro, but because when MPI is desired it replaces the # usual compiler the order here is necessary and it is all interdependent. AC_DEFUN([GA_MSG_COMMS], [ # GA_MP_* vars might exist in environment, but they are really internal. # Reset them. GA_MP_LIBS= GA_MP_LDFLAGS= GA_MP_CPPFLAGS= AC_ARG_WITH([mpi], [AS_HELP_STRING([--with-mpi[[=ARG]]], [select MPI as the messaging library (default); leave ARG blank to use MPI compiler wrappers])], [], [with_mpi=yes]) with_mpi_need_parse=no AS_CASE([$with_mpi], [yes], [with_mpi_wrappers=yes; ga_msg_comms=MPI], [*], [with_mpi_need_parse=yes; ga_msg_comms=MPI]) dnl postpone parsing with_mpi until we know sizeof(void*) dnl AS_IF([test x$with_mpi_need_parse = xyes], dnl [GA_ARG_PARSE([with_mpi], [GA_MP_LIBS], [GA_MP_LDFLAGS], [GA_MP_CPPFLAGS])]) AM_CONDITIONAL([MSG_COMMS_MPI], [test "x$ga_msg_comms" = xMPI]) AS_CASE([$ga_msg_comms], [MPI], [AC_DEFINE([MSG_COMMS_MPI], [1], [Use MPI for messaging])]) AC_SUBST([GA_MP_LIBS]) AC_SUBST([GA_MP_LDFLAGS]) AC_SUBST([GA_MP_CPPFLAGS]) ])dnl ga-5.9.2/m4/ga_pario.m4000066400000000000000000000055571500715745200145030ustar00rootroot00000000000000# =========================================================================== # # SYNOPSIS # # GA_PARIO # # DESCRIPTION # # This macro tries to find out how to compile pario. # This was converted from pario/makefile.h and pario/*/GNUmakefile # Defines the following precious variables # PARIO_CPPFLAGS # PARIO_LDFLAGS # PARIO_CFLAGS # PARIO_FFLAGS # AC_DEFUN([GA_PARIO], [ dnl ########################################################################## dnl FROM pario/makefile.h dnl ########################################################################## OSNAME=`uname` PARIO_CPPFLAGS= if test x$OSNAME = xAIX ; then if /usr/bin/oslevel | awk -F. '{ if ($1 > 5 || ($1 == 5 && $2 > 1)) exit 0 }' then PARIO_CPPFLAGS="$PARIO_CPPFLAGS -DAIX52" fi if /usr/sbin/lsdev -C -l aio0 2>&1 | grep Legacy ; then PARIO_CPPFLAGS="$PARIO_CPPFLAGS -D_AIO_AIX_SOURCE" fi fi if test x$LARGE_FILES != x ; then if test x$OSNAME = xAIX ; then if /usr/bin/oslevel|awk -F. '{ if ($1 > 4 || ($1 == 4 && $2 > 1)) exit 0 }' then PARIO_CPPFLAGS="$PARIO_CPPFLAGS -D_LARGE_FILES -D_LARGE_FILE_API" fi if /usr/bin/oslevel|awk -F. '{ if ($1 == 4 && $2 == 2 && $3 <= 0 ) exit 0 }' then PARIO_CPPFLAGS="$PARIO_CPPFLAGS -DNOAIO" fi fi if test x$TARGET = xSOLARIS ; then PARIO_CPPFLAGS=`getconf LFS_CFLAGS` PARIO_CFLAGS=`getconf LFS_CFLAGS` fi if test x$TARGET = xLINUX ; then PARIO_CPPFLAGS="$PARIO_CPPFLAGS -D_LARGEFILE64_SOURCE" PARIO_CFLAGS=`getconf LFS_CFLAGS` fi PARIO_CPPFLAGS="$PARIO_CPPFLAGS -DLARGE_FILES" fi if test x$USE_LINUXAIO != x ; then PARIO_CPPFLAGS="$PARIO_CPPFLAGS -DLINUXAIO" PARIO_LDFLAGS="$PARIO_LDFLAGS -lrt" fi dnl ########################################################################## dnl FROM pario/elio/GNUmakefile dnl ########################################################################## dnl on platforms with Posix AIO you can choose not to use it by defining NOAIO if test x$NOAIO != x ; then PARIO_CPPFLAGS="$PARIO_CPPFLAGS -DNOAIO" fi if test x$PABLO != x ; then PARIO_CPPFLAGS="$PARIO_CPPFLAGS -DPABLO" fi dnl ########################################################################## dnl FROM pario/eaf/GNUmakefile dnl ########################################################################## PARIO_CPPFLAGS="$PARIO_CPPFLAGS -DEAF_STATS" dnl ########################################################################## dnl FROM pario/dra/GNUmakefile dnl ########################################################################## if test x$F77 = xfrt ; then PARIO_FFLAGS=-O2 fi dnl ########################################################################## dnl FROM pario/sf/GNUmakefile dnl ########################################################################## dnl nothing AC_SUBST([PARIO_CPPFLAGS]) AC_SUBST([PARIO_LDFLAGS]) AC_SUBST([PARIO_CFLAGS]) AC_SUBST([PARIO_FFLAGS]) ])dnl GA_PARIO ga-5.9.2/m4/ga_pragma.m4000066400000000000000000000021201500715745200146170ustar00rootroot00000000000000# GA_PRAGMA_OPERATOR # ------------------ # whether the C compiler understands the C99 _Pragma operator AC_DEFUN([GA_PRAGMA_OPERATOR], [AC_CACHE_CHECK([whether the C compiler understands the _Pragma operator], [ga_cv_c_pragma_operator], [AC_LANG_PUSH([C]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[_Pragma("something")]])], [ga_cv_c_pragma_operator=yes], [ga_cv_c_pragma_operator=no]) AC_LANG_POP([C])]) AS_IF([test x$ga_cv_c_pragma_operator = xyes], [AC_DEFINE([HAVE_PRAGMA_OPERATOR], [1], [define if the C compiler understands the C99 _Pragma macro])]) AH_BOTTOM([#ifndef HAVE_PRAGMA_OPERATOR # define _Pragma(_pragf) #endif]) ]) #GA_PRAGMA_OPERATOR AC_DEFUN([GA_PRAGMA_OPERATOR_OLD], [ AC_MSG_CHECKING([whether the C compiler understands the _Pragma operator]) AC_LANG_PUSH([C]) AC_COMPILE_IFELSE( [AC_LANG_SOURCE([[_Pragma("something")]])], [AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_PRAGMA_OPERATOR], [1], [define if the C compiler understands the C99 _Pragma macro])], [AC_MSG_RESULT([no])]) AC_LANG_POP([C]) ]) #GA_PRAGMA_OPERATOR_OLD ga-5.9.2/m4/ga_progname.m4000066400000000000000000000016171500715745200151720ustar00rootroot00000000000000# GA_PROGNAME # ----------- # Look for special global variables containing the name of the program. AC_DEFUN([GA_PROGNAME], [AC_CACHE_CHECK([for C global variable containing the name of the program], [ga_cv_progname], [AC_LANG_PUSH([C]) ga_cv_progname=none for name in __progname program_invocation_short_name __progname_full program_invocation_name do AC_LINK_IFELSE( [AC_LANG_PROGRAM( [[#include #include extern const char * $name;]], [[printf("%s\n", $name);]])], [ga_cv_progname=$name break]) done AC_LANG_POP([C])]) AS_IF([test x$ga_cv_progname != xnone], [val=1], [val=0]) AC_DEFINE_UNQUOTED([HAVE_PROGNAME], [$val], [define to 1 if the C compiler has a program name global varaible]) AC_DEFINE_UNQUOTED([PROGNAME], [$ga_cv_progname], [define to the name of the program name global variable]) ])dnl ga-5.9.2/m4/ga_scalapack.m4000066400000000000000000000120571500715745200153040ustar00rootroot00000000000000# GA_F77_SCALAPACK_TEST # --------------------- # Generate Fortran 77 conftest for SCALAPACK. AC_DEFUN([GA_F77_SCALAPACK_TEST], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([], [[ implicit none external PDGETRS CALL PDGETRS ()]])]) ]) # GA_C_SCALAPACK_TEST # ------------------- # Generate C conftest for SCALAPACK. AC_DEFUN([GA_C_SCALAPACK_TEST], [AC_LANG_CONFTEST([AC_LANG_PROGRAM( [#ifdef __cplusplus extern "C" { #endif char pdgetrs (); #ifdef __cplusplus } #endif ], [[char result = pdgetrs (); ]])]) ]) # GA_RUN_SCALAPACK_TEST # --------------------- # Test the linker. # Clears SCALAPACK_LIBS on failure. Sets ga_scalapack_ok=yes on success. AC_DEFUN([GA_RUN_SCALAPACK_TEST], [ AS_IF([test "x$enable_f77" = xno], [AC_LANG_PUSH([C]) GA_C_SCALAPACK_TEST() AC_LINK_IFELSE([], [ga_scalapack_ok=yes], [SCALAPACK_LIBS=]) AC_LANG_POP([C])], [AC_LANG_PUSH([Fortran 77]) GA_F77_SCALAPACK_TEST() AC_LINK_IFELSE([], [ga_scalapack_ok=yes], [SCALAPACK_LIBS=]) AC_LANG_POP([Fortran 77])]) ])dnl # GA_SCALAPACK([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # --------------------------------------------------- # Modeled after GA_LAPACK and GA_BLAS. # Tries to find ScaLAPACK library. The netlib implementation claims that # ScaLAPACK depends on PBLAS, LAPACK, BLAS, and BLACS. However, some # implementations such as Intel's combine ScaLAPACK, LAPACK, and BLAS together # such that PBLAS and BLACS are not needed. This macro attempts to find the # combination of libraries necessary to use ScaLAPACK. AC_DEFUN([GA_SCALAPACK], [AC_REQUIRE([GA_LAPACK]) scalapack_size=4 AC_ARG_WITH([scalapack], [AS_HELP_STRING([--with-scalapack=[[ARG]]], [use ScaLAPACK library compiled with sizeof(INTEGER)==4])], [scalapack_size=4]) AC_ARG_WITH([scalapack8], [AS_HELP_STRING([--with-scalapack8=[[ARG]]], [use ScaLAPACK library compiled with sizeof(INTEGER)==8])], [scalapack_size=8; with_scalapack="$with_scalapack8"]) ga_scalapack_ok=no AS_IF([test "x$with_scalapack" = xno], [ga_scalapack_ok=skip]) # Parse --with-scalapack argument. Clear previous values first. SCALAPACK_LIBS= SCALAPACK_LDFLAGS= SCALAPACK_CPPFLAGS= GA_ARG_PARSE([with_scalapack], [SCALAPACK_LIBS], [SCALAPACK_LDFLAGS], [SCALAPACK_CPPFLAGS]) ga_save_LIBS="$LIBS" ga_save_LDFLAGS="$LDFLAGS" ga_save_CPPFLAGS="$CPPFLAGS" LDFLAGS="$SCALAPACK_LDFLAGS $LAPACK_LDFLAGS $BLAS_LDFLAGS $GA_MP_LDFLAGS $LDFLAGS" CPPFLAGS="$SCALAPACK_CPPFLAGS $LAPACK_CPPFLAGS $BLAS_CPPFLAGS $GA_MP_CPPFLAGS $CPPFLAGS" AC_MSG_NOTICE([Attempting to locate SCALAPACK library]) # First, check environment/command-line variables. # If failed, erase SCALAPACK_LIBS but maintain SCALAPACK_LDFLAGS and # SCALAPACK_CPPFLAGS. AS_IF([test $ga_scalapack_ok = no], [AC_MSG_CHECKING([for SCALAPACK with user-supplied flags]) LIBS="$SCALAPACK_LIBS $LAPACK_LIBS $BLAS_LIBS $GA_MP_LIBS $LIBS" GA_RUN_SCALAPACK_TEST() LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_scalapack_ok])]) # Generic ScaLAPACK library? AS_IF([test $ga_scalapack_ok = no], [AC_MSG_CHECKING([for SCALAPACK in generic library]) SCALAPACK_LIBS="-lscalapack" LIBS="$SCALAPACK_LIBS $LAPACK_LIBS $BLAS_LIBS $GA_MP_LIBS $LIBS" GA_RUN_SCALAPACK_TEST() LIBS="$ga_save_LIBS" AC_MSG_RESULT([$ga_scalapack_ok])]) CPPFLAGS="$ga_save_CPPFLAGS" LDFLAGS="$ga_save_LDFLAGS" AC_SUBST([SCALAPACK_LIBS]) AC_SUBST([SCALAPACK_LDFLAGS]) AC_SUBST([SCALAPACK_CPPFLAGS]) AS_IF([test "x$scalapack_size" = x8], [AC_DEFINE([SCALAPACK_I8], [1], [ScaLAPACK is using 8-byte integers])]) AC_SUBST([scalapack_size]) # test for pdsyevr which some implementations may not have AS_IF([test $ga_scalapack_ok = yes], [ ga_save_LIBS="$LIBS" ga_save_LDFLAGS="$LDFLAGS" ga_save_CPPFLAGS="$CPPFLAGS" LIBS="$SCALAPACK_LIBS $LAPACK_LIBS $BLAS_LIBS $GA_MP_LIBS $LIBS" LDFLAGS="$SCALAPACK_LDFLAGS $LAPACK_LDFLAGS $BLAS_LDFLAGS $GA_MP_LDFLAGS $LDFLAGS" CPPFLAGS="$SCALAPACK_CPPFLAGS $LAPACK_CPPFLAGS $BLAS_CPPFLAGS $GA_MP_CPPFLAGS $CPPFLAGS" AC_MSG_CHECKING([whether SCALAPACK implements pdsyevr]) AS_IF([test "x$enable_f77" = xno], [AC_LANG_PUSH([C]) AC_LINK_IFELSE([AC_LANG_CALL([], [pdsyevr])], [have_pdsyevr=1; have_pdsyevr_msg=yes], [have_pdsyevr=0; have_pdsyevr_msg=no]) AC_LANG_POP([C])], [AC_LANG_PUSH([Fortran 77]) AC_LINK_IFELSE([AC_LANG_CALL([], [pdsyevr])], [have_pdsyevr=1; have_pdsyevr_msg=yes], [have_pdsyevr=0; have_pdsyevr_msg=no]) AC_LANG_POP([Fortran 77])]) AC_MSG_RESULT([$have_pdsyevr_msg]) AC_DEFINE_UNQUOTED([HAVE_PDSYEVR], [$have_pdsyevr], [whether the ScaLAPACK library implements pdsyevr]) LIBS="$ga_save_LIBS" LDFLAGS="$ga_save_LDFLAGS" CPPFLAGS="$ga_save_CPPFLAGS" ]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: AS_IF([test $ga_scalapack_ok = yes], [have_scalapack=1 $1], [AC_MSG_WARN([ScaLAPACK library not found, interfaces won't be defined]) have_scalapack=0 $2]) AC_SUBST([have_scalapack]) AC_DEFINE_UNQUOTED([HAVE_SCALAPACK], [$have_scalapack], [Define to 1 if you have ScaLAPACK library.]) AM_CONDITIONAL([HAVE_SCALAPACK], [test $ga_scalapack_ok = yes]) ])dnl GA_SCALAPACK ga-5.9.2/m4/ga_sicm.m4000066400000000000000000000026511500715745200143140ustar00rootroot00000000000000# GA_MSG_COMMS() # -------------- # Establishes all things related to SICM. AC_DEFUN([GA_SICM], [ SICM_LIBS= SICM_LDFLAGS= SICM_CPPFLAGS= AC_ARG_WITH([sicm], [AS_HELP_STRING([--with-sicm[[=ARG]]], [specify SICM library (optional)])], [], [with_sicm=no]) AC_ARG_WITH([sicm_dev], [AS_HELP_STRING([--with-sicm_dev[[=ARG]]], [specify SICM hardware (default=dram)])], [], [with_sicm_dev=no]) #TODO: AC_CHECK_HEADERS, AC_SEARCH_LIBS AS_IF([test "x$with_sicm" != "xno"], [ AM_CONDITIONAL(WITH_SICM, true) have_sicm=1 GA_ARG_PARSE([with_sicm], [SICM_LIBS], [SICM_LDFLAGS], [SICM_CPPFLAGS]) AC_SUBST([SICM_LIBS]) AC_SUBST([SICM_LDFLAGS]) AC_SUBST([SICM_CPPFLAGS]) AC_DEFINE([USE_SICM], [1], [enable sicm define]) AC_DEFINE([TEST_SICM], [1], [enable test_sicm define]) AC_DEFINE([TEST_SICM_DEV], [dram], [specify sicm test hw]) ], [ have_sicm=0 AM_CONDITIONAL(WITH_SICM, false) ] ) AC_SUBST([have_sicm]) #TODO:Check if sicm_dev provided is valid AS_IF([test "x$with_sicm_dev" != "xno"], [ have_sicm_dev=1 AC_DEFINE_UNQUOTED([TEST_SICM_DEV], [$with_sicm_dev], [specify sicm test hw]) ], [ have_sicm_dev=0 ] ) AC_SUBST([with_sicm_dev]) ])dnl ga-5.9.2/m4/ga_suppress_msg.m4000066400000000000000000000021271500715745200161110ustar00rootroot00000000000000# GA_SUPPRESS_MESSAGE # ------------------- # Determine any flags necessary to suppress informational messages from the # compiler. Useful when setting the ac_[]_AC_LANG_ABBREV[]_werror_flag=yes # because any extra output (stdout and stderr) will trigger an error. # The xlf compiler is the only case thus far. AC_DEFUN([GA_SUPPRESS_MESSAGE], [AC_CACHE_CHECK([for _AC_LANG flag to suppress info messages], [ga_cv_[]_AC_LANG_ABBREV[]_suppress], [ga_save_[]_AC_LANG_PREFIX[]FLAGS="$_AC_LANG_PREFIX[]FLAGS" ga_save_werror_flag=$ac_[]_AC_LANG_ABBREV[]_werror_flag ac_[]_AC_LANG_ABBREV[]_werror_flag=yes for flag in none -qsuppress=cmpmsg ; do _AC_LANG_PREFIX[]FLAGS=$ga_save_[]_AC_LANG_PREFIX[]FLAGS AS_IF([test "x$flag" != xnone], [_AC_LANG_PREFIX[]FLAGS="$_AC_LANG_PREFIX[]FLAGS $flag"]) AC_LINK_IFELSE([AC_LANG_PROGRAM()], [ga_cv_[]_AC_LANG_ABBREV[]_suppress="$flag"; break]) done _AC_LANG_PREFIX[]FLAGS=$ga_save_[]_AC_LANG_PREFIX[]FLAGS ac_[]_AC_LANG_ABBREV[]_werror_flag=$ga_save_werror_flag ]) ]) # GA_SUPPRESS_MESSAGE ga-5.9.2/m4/ga_sys_weak_alias.m4000066400000000000000000000017661500715745200163650ustar00rootroot00000000000000# GA_DISABLE_SYS_WEAK_ALIAS # ------------------------- # Whether to disable the test for weak aliases AC_DEFUN([GA_DISABLE_SYS_WEAK_ALIAS], [ AC_ARG_ENABLE([weak], [AS_HELP_STRING([--disable-weak], [don't use weak symbols for profiling])], [], [enable_weak=yes]) AS_IF([test "x$ga_cv_target_base" = xCYGWIN], [enable_weak=no]) ])dnl # GA_SYS_WEAK_ALIAS # ----------------- # Whether pragma weak is supported. AC_DEFUN([GA_SYS_WEAK_ALIAS], [ AS_IF([test "x$enable_weak" = xyes], [ax_sys_weak_alias=no _AX_SYS_WEAK_ALIAS_PRAGMA], [ax_cv_sys_weak_alias_pragma=no AC_DEFINE([HAVE_SYS_WEAK_ALIAS_PRAGMA], [0], [Define this if weak aliases may be created with @%:@pragma weak])]) AM_CONDITIONAL([HAVE_SYS_WEAK_ALIAS_PRAGMA], [test "x$ax_cv_sys_weak_alias_pragma" = xyes]) # enable shared libs automatically if profiling using weak symbols AS_IF([test "x$ax_cv_sys_weak_alias_pragma" = xyes], [AS_IF([test "x$enable_profiling" = xyes], [enable_shared=yes])]) ])dnl ga-5.9.2/m4/ga_target.m4000066400000000000000000000064261500715745200146530ustar00rootroot00000000000000# GA_TARGET() # ----------- # Attempt to determine the old TARGET variable automatically. AC_DEFUN([GA_TARGET], [# AH_TEMPLATE for all known TARGETs AH_TEMPLATE([CYGNUS], [Define to 1 on Cygnus systems]) AH_TEMPLATE([CYGWIN], [Define to 1 on Cygwin systems]) AH_TEMPLATE([IBM], [Define to 1 on IBM SP systems]) AH_TEMPLATE([IBM64], [Define to 1 on 64bit IBM SP systems]) AH_TEMPLATE([LINUX], [Define to 1 on generic Linux systems]) AH_TEMPLATE([LINUX64], [Define to 1 on generic 64bit Linux systems]) AH_TEMPLATE([MACX], [Define to 1 on OSX systems]) AH_TEMPLATE([MACX64], [Define to 1 on 64bit OSX systems]) AH_TEMPLATE([SOLARIS], [Define to 1 on Solaris systems]) AH_TEMPLATE([SOLARIS64], [Define to 1 on 64bit Solaris systems]) AC_REQUIRE([AC_CANONICAL_BUILD]) AC_REQUIRE([AC_CANONICAL_HOST]) AC_CACHE_CHECK([for TARGET base (64bit-ness checked later)], [ga_cv_target_base], [ga_cv_target_base=UNKNOWN AS_IF([test "x$ga_cv_target_base" = xUNKNOWN], [AS_CASE([$host], [*cygwin*], [ga_cv_target_base=CYGWIN], [*ibm*], [ga_cv_target_base=IBM], [*linux*], [ga_cv_target_base=LINUX], [*darwin*], [ga_cv_target_base=MACX], [*apple*], [ga_cv_target_base=MACX], [*mingw32*], [ga_cv_target_base=MINGW], [*solaris*], [ga_cv_target_base=SOLARIS])]) ])dnl AC_DEFINE_UNQUOTED([$ga_cv_target_base], [1], [define if this is the TARGET irregardless of whether it is 32/64 bits]) # A horrible hack that should go away somehow... dnl # Only perform this hack for ARMCI build. dnl AS_IF([test "x$ARMCI_TOP_BUILDDIR" != x], [ AC_CACHE_CHECK([whether we think this system is what we call SYSV], [ga_cv_sysv], [AS_CASE([$ga_cv_target_base], [SUN|SOLARIS|IBM|LINUX], [ga_cv_sysv=yes], [ga_cv_sysv=no]) ]) AS_IF([test x$ga_cv_sysv = xyes], [AC_DEFINE([SYSV], [1], [Define if we want this system to use SYSV shared memory])]) dnl ]) # Hopefully these will never be used and we can remove them soon. AM_CONDITIONAL([CYGNUS], [test "$ga_cv_target_base" = CYGNUS]) AM_CONDITIONAL([CYGWIN], [test "$ga_cv_target_base" = CYGWIN]) AM_CONDITIONAL([IBM], [test "$ga_cv_target_base" = IBM]) AM_CONDITIONAL([LINUX], [test "$ga_cv_target_base" = LINUX]) AM_CONDITIONAL([MACX], [test "$ga_cv_target_base" = MACX]) AM_CONDITIONAL([MINGW], [test "$ga_cv_target_base" = MINGW]) AM_CONDITIONAL([SOLARIS], [test "$ga_cv_target_base" = SOLARIS]) ])dnl # GA_TARGET64() # ------------- # Checking for 64bit platforms requires checking sizeof void*. # That's easy, but doing it too soon causes AC_PROG_F77/C/CXX to get expanded # too soon, and we want to expand those with a better list of compilers # based on our current TARGET. Therefore, we must do this 64bit test later. AC_DEFUN([GA_TARGET64], [AC_REQUIRE([GA_TARGET]) AC_COMPUTE_INT([ga_target64_sizeof_voidp], [(long int) (sizeof (void*))]) AC_CACHE_CHECK([for TARGET 64bit-ness], [ga_cv_target], [AS_IF([test x$ga_target64_sizeof_voidp = x8], [ga_cv_target=${ga_cv_target_base}64], [ga_cv_target=$ga_cv_target_base])]) AC_DEFINE_UNQUOTED([$ga_cv_target], [1], [define if this is the TARGET, 64bit-specific]) ])dnl ga-5.9.2/m4/ga_tbb.m4000066400000000000000000000042671500715745200141350ustar00rootroot00000000000000# GA_TBB() # -------- # Locate Intel Threading Building Blocks. # # They may have been installed to a typical root/include and root/lib location, # or the user may have downloaded a source or binary tarball. # # The source tarballs contain a Makefile to be used with gmake while the binary # tarball contains a number of libraries. In either case, the TBB README # recommends setting the $TBBROOT environment variable to point to the base # directory of either the source or binary untarred directories. This isn't # sufficient for locating libtbb.so since the source release builds both # 'debug' and 'release' versions of the library into a 'work_dir' location # based on the user's system information (uname, etc) and the binary release # contains multiple versions of the library based on many different systems and # CPU architectures. Ugh. # # If $TBBROOT is set (or $tbb_root), we first search for the # $TBBROOT/build/common.inc Makefile fragment since this will help us find the # appropriate libtbb.so. # # If $TBBROOT is not set, we try a standard lookup of the package which will # most likely fail unless the user helps. # AC_DEFUN([GA_TBB], [ tbb_ok=no # has the user set TBBROOT or tbb_root? AS_IF([test "x$TBBROOT" != x || test "x$tbb_root" != x], [ rm -f the_makefile rm -f result.txt rm -f out.txt cat >the_makefile <<"EOF" ifdef TBBROOT tbb_root=$(TBBROOT) endif include $(tbb_root)/build/common.inc result.txt: @echo "$(work_dir)" > result.txt EOF AS_IF([gmake -f the_makefile &> out.txt], [tbb_work_dir=`cat result.txt`; tbb_ok=yes], [cat out.txt >&AS_MESSAGE_LOG_FD]) AS_IF([test "x$tbb_work_dir" != x], [TBB_LDFLAGS="-L${tbb_work_dir}_release"]) AS_IF([test "x$TBBROOT" != x], [TBB_CPPFLAGS="-I$TBBROOT/include"], [test "x$tbb_root" != x], [TBB_CPPFLAGS="-I$tbb_root/include"]) TBB_LIBS="-ltbb" rm -f the_makefile rm -f result.txt rm -f out.txt ]) AS_IF([test "x$tbb_ok" = xno], [ GA_CHECK_PACKAGE([tbb], [tbb/tbb.h], [tbb], [TBB_runtime_interface_version], [], [tbb_ok=yes], [AC_MSG_ERROR([could not locate Intel Thread Building Blocks])]) ]) AC_SUBST([TBB_CPPFLAGS]) AC_SUBST([TBB_LDFLAGS]) AC_SUBST([TBB_LIBS]) ]) ga-5.9.2/m4/ga_thread_safe.m4000066400000000000000000000013751500715745200156300ustar00rootroot00000000000000# GA_THREAD_SAFE # -------------- # Asserts that procedures are thread safe??? # Is this specific to ARMCI??? # This was taken from older GNUmakefiles... the original doc follows # Procedures are thread safe; should also specify the max number of threads # via ARMCI_MAX_THREADS and thread library via THREAD_LIBRARY. Only supported # for SOCKETS OPENIB. AC_DEFUN([GA_THREAD_SAFE], [AC_ARG_ENABLE([thread-safety], [AS_HELP_STRING([--enable-thread-safety], [**unsupported** turn on thread safety])], [thread_safety=yes AC_DEFINE([THREAD_SAFE], [1], [turn on thread safety])], [thread_safety=no]) AM_CONDITIONAL([THREAD_SAFE], [test x$thread_safety = xyes]) AC_ARG_VAR([THREAD_LIBRARY], [See --enable-thread-safety]) AC_SUBST([THREAD_LIBRARY]) ])dnl ga-5.9.2/m4/ga_union_semun.m4000066400000000000000000000010721500715745200157140ustar00rootroot00000000000000# GA_UNION_SEMUN # -------------- # Not all packages define union semun, even those that require it. AC_DEFUN([GA_UNION_SEMUN], [AC_CACHE_CHECK([for union semun in sys/sem.h], [ga_cv_union_semun], [AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[#include #include #include ]], [[union semun arg; semctl(0, 0, 0, arg);]])], [ga_cv_union_semun=yes], [ga_cv_union_semun=no])]) AS_IF([test x$ga_cv_union_semun = xyes], [AC_DEFINE([HAVE_UNION_SEMUN], [1], [define if sys/sem.h has union semun])]) ])# GA_UNION_SEMUN ga-5.9.2/m4/ga_warn_flags.m4000066400000000000000000000030151500715745200154770ustar00rootroot00000000000000# GA_WARN_FLAGS # ------------- # Add all known warning flags to the language-specific FLAGS variable. AC_DEFUN([GA_WARN_FLAGS], [ AS_IF([test "x$enable_warnings" = xyes], [ AC_REQUIRE([GA_COMPILER_VENDOR]) AS_VAR_PUSHDEF([vendor], [ga_cv_[]_AC_LANG_ABBREV[]_compiler_vendor]) AS_VAR_PUSHDEF([result], [ga_cv_[]_AC_LANG_ABBREV[]_warning_flags]) AC_CACHE_CHECK([for _AC_LANG warning flags], [result], [ AC_LANG_CASE( [C], [ AS_CASE([$vendor], [amd], [result="-Wall -W -Wdeclaration-after-statement"], [borland], [result=], [comeau], [result=], [fujitsu], [result="-Xc -pvctl,fullmsg"], [gnu], [result="-Wall -Wextra -Wdeclaration-after-statement -Wno-unused-parameter -pedantic -Wno-long-long -Wnested-externs -ansi"], [hp], [result=], [ibm], [result=], [intel], [result="-Wall"], [kai], [result=], [lcc], [result=], [metrowerks],[result=], [microsoft], [result=], [pathscale], [result="-Wall -fullwarn -Wno-unused-parameter -pedantic -Wno-long-long -Wnested-externs"], [portland], [result="-Xc"], [sun], [result=], [watcom], [result=]) ], [Fortran 77], [ result= ], [Fortran], [ result= ], [C++], [ result= ]) ]) AC_SUBST(GA_[]_AC_LANG_PREFIX[]_WARN, [$result]) AS_VAR_POPDEF([result]) AS_VAR_POPDEF([vendor]) ]) ])dnl # GA_ENABLE_WARNINGS # ------------------ # Adds --enable-warnings. AC_DEFUN([GA_ENABLE_WARNINGS], [ AC_ARG_ENABLE([warnings], [AS_HELP_STRING([--enable-warnings], [use compiler-specific warnings])], [enable_warnings=yes], [enable_warnings=no]) ])dnl ga-5.9.2/m4/ga_with_help.m4000066400000000000000000000006531500715745200153440ustar00rootroot00000000000000# GA_WITH_HELP # ------------ # Using undocumented features, add some text to our --with-PACKAGE help to # avoid repetition. AC_DEFUN([GA_WITH_HELP], [AC_ARG_WITH([PACKAGE], [AS_HELP_STRING([--with-PACKAGE[[=ARG]]], [for most of the external software packages, ARG can be one or more whitespace-separated directories, linker or preprocessor directives; for example, --with-PACKAGE="/path/to/PACKAGE -lmylib -I/mydir"])])]) ga-5.9.2/m4/ma_enable_cuda_mem.m4000066400000000000000000000007141500715745200164450ustar00rootroot00000000000000# MA_ENABLE_CUDA_MEM # ------------------ # Whether to enable CUDA memory in MA. AC_DEFINEs ENABLE_PROFILING. AC_DEFUN([MA_ENABLE_CUDA_MEM], [AC_ARG_ENABLE([cuda-mem], [AS_HELP_STRING([--enable-cuda-mem], [enable CUDA memory])], [], [enable_cuda_mem=no]) AM_CONDITIONAL([ENABLE_CUDA_MEM], [test x$enable_cuda_mem = xyes]) AS_IF([test "x$enable_cuda_mem" = xyes], [AC_DEFINE([ENABLE_CUDA_MEM], [1], [set to 1 if CUDA memory is enabled])]) ])dnl ga-5.9.2/m4/ma_long_double.m4000066400000000000000000000005731500715745200156610ustar00rootroot00000000000000# MA_LONG_DOUBLE_TYPEDEF # ---------------------- # AC_SUBST a proper long double if one does not exist. AC_DEFUN([MA_LONG_DOUBLE_TYPEDEF], [AC_REQUIRE([AC_TYPE_LONG_DOUBLE]) AS_IF([test $ac_cv_type_long_double = yes], [ma_long_double="long double"], [ma_long_double="struct {double dummy[2];}"]) AC_SUBST([MA_LONG_DOUBLE], [$ma_long_double]) ]) # MA_LONG_DOUBLE_TYPEDEF ga-5.9.2/m4/ma_stats.m4000066400000000000000000000002061500715745200145170ustar00rootroot00000000000000# MA_STATS # --------- # Simply AC_DEFINEd. AC_DEFUN([MA_STATS], [AC_DEFINE([STATS], [1], [This is always defined. Dunno why.]) ])dnl ga-5.9.2/m4/ma_verify.m4000066400000000000000000000002111500715745200146610ustar00rootroot00000000000000# MA_VERIFY # --------- # Simply AC_DEFINEd. AC_DEFUN([MA_VERIFY], [AC_DEFINE([VERIFY], [1], [This is always defined. Dunno why.]) ])dnl ga-5.9.2/m4/tcgmsg_rsh.m4000066400000000000000000000006441500715745200150520ustar00rootroot00000000000000# TCGMSG_REMOTE_SHELL # ------------------- # tcgmsg requires a remote shell. AC_DEFUN([TCGMSG_REMOTE_SHELL], [AC_MSG_NOTICE([checking for remote shell]) AC_PATH_PROGS([ga_cv_path_rsh], [rsh remsh ssh], [not found]) AS_IF([test "x$ga_cv_path_rsh" = "xnot found"], [AC_MSG_ERROR([Could not find remote shell for use by TCGMSG])]) AC_DEFINE_UNQUOTED([TCGMSG_RSH], ["$ga_cv_path_rsh"], [remote shell for TCGMSG]) ])dnl ga-5.9.2/m4/tcgmsg_timings.m4000066400000000000000000000004041500715745200157220ustar00rootroot00000000000000# TCGMSG_ENABLE_TIMINGS # --------------------- # This was only defined for LINUX[64] previously. Does it do any harm to # define it permanently? AC_DEFUN([TCGMSG_ENABLE_TIMINGS], [AC_DEFINE([TCGMSG_TIMINGS], [1], [Gather timing information for TCGMSG]) ])dnl ga-5.9.2/ma/000077500000000000000000000000001500715745200125215ustar00rootroot00000000000000ga-5.9.2/ma/CMakeLists.txt000066400000000000000000000055741500715745200152740ustar00rootroot00000000000000# # module: CMakeLists.txt # author: Bruce Palmer # description: CMake build for GA. Only MPI-based runtimes are supported. # # DISCLAIMER # # This material was prepared as an account of work sponsored by an # agency of the United States Government. Neither the United States # Government nor the United States Department of Energy, nor Battelle, # nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR # ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, # COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, # SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT # INFRINGE PRIVATELY OWNED RIGHTS. # # # ACKNOWLEDGMENT # # This software and its documentation were produced with United States # Government support under Contract Number DE-AC06-76RLO-1830 awarded by # the United States Department of Energy. The United States Government # retains a paid-up non-exclusive, irrevocable worldwide license to # reproduce, prepare derivative works, perform publicly and display # publicly by or for the US Government, including the right to # distribute to other US Government contractors. # # -*- mode: cmake -*- # ------------------------------------------------------------- # file: CMakeLists.txt # ------------------------------------------------------------- include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_BINARY_DIR}/gaf2c ${PROJECT_BINARY_DIR}/ma ${PROJECT_BINARY_DIR} ) # ------------------------------------------------------------- # MA header installation # ------------------------------------------------------------- if (ENABLE_FORTRAN) set(FORTRAN_HEADERS maf2c.fh ${CMAKE_CURRENT_BINARY_DIR}/mafdecls.fh) endif() set(MA_HEADERS # ma.h # error.h macdecls.h macommon.h # memcpy.h # scope.h # string-util.h # table.h ${FORTRAN_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/matypes.h ) install (FILES ${MA_HEADERS} DESTINATION include/ga ) list (APPEND GA_HEADER_PATHS ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_BINARY_DIR}) set (GA_HEADER_PATHS ${GA_HEADER_PATHS} PARENT_SCOPE) # ------------------------------------------------------------- # MA library installation # ------------------------------------------------------------- if (ENABLE_FORTRAN) set(FORTRAN_FILES f2c.c maf.F) endif() add_library(ma OBJECT ma.c error.c string-util.c table.c ${FORTRAN_FILES} ) if(ENABLE_FORTRAN) add_dependencies(ma gaf2c) endif() # ------------------------------------------------------------- # Build MA tests # ------------------------------------------------------------- if(ENABLE_TESTS) if (ENABLE_FORTRAN) ga_add_parallel_test(ma/testf testf.F Fortran) set_tests_properties(ma/testf PROPERTIES WILL_FAIL TRUE) endif() ga_add_parallel_test(ma/test-coalesce test-coalesce.c) ga_add_parallel_test(ma/test-inquire test-inquire.c) endif() ga-5.9.2/ma/MA.tex000066400000000000000000000051151500715745200135420ustar00rootroot00000000000000\documentstyle[11pt,twocolumn]{article} % vertical parameters \setlength{\topmargin}{0in} \setlength{\headheight}{0in} \setlength{\headsep}{0in} \setlength{\topskip}{0in} \setlength{\textheight}{8.5in} \setlength{\footheight}{10pt} \setlength{\footskip}{0.5in} % horizontal parameters \setlength{\textwidth}{6.5in} \setlength{\oddsidemargin}{0in} \setlength{\evensidemargin}{0in} \setlength{\marginparwidth}{0in} \setlength{\parindent}{0in} \begin{document} MA is a library of routines that comprises a dynamic memory allocator for use by C, FORTRAN, or mixed-language applications. C applications can benefit from using MA instead of the ordinary malloc() and free() routines because of the extra features MA provides: both heap and stack memory management disciplines, debugging and verification support, usage statistics, and quantitative memory availability information. FORTRAN applications can take advantage of the same features, and may in fact require a library such as MA because dynamic memory allocation is not supported by all versions of the language. MA is designed to be portable across a variety of platforms. The implementation of MA uses the following memory layout: \begin{quote} segment = heap\_region stack\_region \\ region = block block block \ldots \\ block = AD gap1 guard1 client\_space guard2 gap2 \end{quote} A segment of memory is obtained from the OS upon initialization. The low end of the segment is managed as a heap; the heap region grows from low addresses to high addresses. The high end of the segment is managed as a stack; the stack region grows from high addresses to low addresses. Each region consists of a series of contiguous blocks, one per allocation request, and possibly some unused space. Blocks in the heap region are either in use by the client (allocated and not yet deallocated) or not in use by the client (allocated and already deallocated). A block on the rightmost end of the heap region becomes part of the unused space upon deallocation. Blocks in the stack region are always in use by the client, because when a stack block is deallocated, it becomes part of the unused space. A block consists of the client space, i.e., the range of memory available for use by the application; guard words adjacent to each end of the client space to help detect improper memory access by the client; bookkeeping info (in an ``allocation descriptor,'' AD); and two gaps, each zero or more bytes long, to satisfy alignment constraints (specifically, to ensure that AD and client\_space are aligned properly). A set of man pages for the MA routines is available. \end{document} ga-5.9.2/ma/error.c000066400000000000000000000036041500715745200140210ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** @file * Error handling module. */ #include #include #include "error.h" #include "scope.h" /** default # of initial table entries */ #define MA_EBUF_SIZE 1024 /** buffer for error messages */ public char ma_ebuf[MA_EBUF_SIZE]; /** print error messages for nonfatal errors? */ public Boolean ma_error_print = MA_TRUE; /** terminate execution upon any error? */ public Boolean ma_hard_fail = MA_FALSE; void (*ma_func_terminate)() = 0; void MA_set_error_callback(void (*func)()) { ma_func_terminate = func; } /** * Depending on the given arguments and certain global parameters, * possibly print a message to stderr and/or terminate the program. * * @param elevel severity of error * @param etype category of error * @param func name of routine in which error was found * @param emsg msg describing error */ public void ma_error(ErrorLevel elevel, ErrorType etype, char *func, char *emsg) { /* print a message? */ if ((elevel == EL_Fatal) || ma_hard_fail || ma_error_print) { char *s1; /* internal or not */ char *s2; /* class of error */ /* set s1 */ if (etype == ET_Internal) { s1 = "internal "; } else { s1 = ""; } /* set s2 */ if (elevel == EL_Fatal) { s2 = "fatal error"; } else if (ma_hard_fail) { s2 = "hard failure"; } else { s2 = "error"; } /* print the message */ (void)fflush(stdout); (void)fflush(stderr); (void)fprintf(stderr, "MA %s%s: %s: %s\n", s1, s2, func, emsg); (void)fflush(stderr); } /* terminate execution? */ if ((elevel == EL_Fatal) || ma_hard_fail) { if(ma_func_terminate) { ma_func_terminate("MA aborting",0); } else { exit(1); } } } ga-5.9.2/ma/error.h000066400000000000000000000016721500715745200140310ustar00rootroot00000000000000/** @file * Private header file containing symbolic constants and type declarations * for the error handling module. * * This file should only be included by internal C files. */ #ifndef _error_h #define _error_h #include "macdecls.h" /** ** types **/ /* severity levels of errors */ typedef enum { EL_Fatal, /* unrecoverable */ EL_Nonfatal /* recoverable */ } ErrorLevel; /* categories of errors */ typedef enum { ET_External, /* anticipated, client-level problem */ ET_Internal /* unanticipated problem internal to MA */ } ErrorType; /** ** function types **/ extern void ma_error(ErrorLevel elevel, ErrorType etype, char *func, char *emsg); /** ** variables **/ /* buffer for error messages */ extern char ma_ebuf[]; /* print error messages for nonfatal errors? */ extern Boolean ma_error_print; /* terminate execution upon any error? */ extern Boolean ma_hard_fail; #endif /* _error_h */ ga-5.9.2/ma/f2c.c000066400000000000000000000166741500715745200133550ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** @file * C wrapper routines. In general, f2c_foo_ is called by FORTRAN routine * MA_foo, performs any necessary argument munging, then calls C routine * MA_foo. */ #include "farg.h" #include "typesf2c.h" #include "ma.h" #include "scope.h" #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS public Boolean FATR f2c_alloc_get_(datatype, nelem, name, memhandle, index, namesize) Integer *datatype; Integer *nelem; char *name; Integer *memhandle; MA_AccessIndex *index; int namesize; /* implicitly passed by FORTRAN */ #else /* F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS */ public Boolean FATR f2c_alloc_get_(datatype, nelem, name, namesize, memhandle, index) Integer *datatype; Integer *nelem; char *name; int namesize; /* implicitly passed by FORTRAN */ Integer *memhandle; MA_AccessIndex *index; #endif /* F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS */ { Boolean value; char buf[MA_NAMESIZE]; /* ensure that name is NUL-terminated */ ga_f2cstring(name, namesize, buf, (Integer)sizeof(buf)); value = MA_alloc_get(*datatype, *nelem, buf, memhandle, index); /* FORTRAN array indexing is 1-based, so increment index */ (*index)++; return value; } #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS public Boolean FATR f2c_allocate_heap_(datatype, nelem, name, memhandle, namesize) Integer *datatype; Integer *nelem; char *name; Integer *memhandle; int namesize; /* implicitly passed by FORTRAN */ #else /* F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS */ public Boolean FATR f2c_allocate_heap_(datatype, nelem, name, namesize, memhandle) Integer *datatype; Integer *nelem; char *name; int namesize; /* implicitly passed by FORTRAN */ Integer *memhandle; #endif /* F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS */ { char buf[MA_NAMESIZE]; /* ensure that name is NUL-terminated */ ga_f2cstring(name, namesize, buf, (Integer)sizeof(buf)); return MA_allocate_heap(*datatype, *nelem, buf, memhandle); } public Boolean FATR f2c_chop_stack_(memhandle) Integer *memhandle; { return MA_chop_stack(*memhandle); } public Boolean FATR f2c_free_heap_(memhandle) Integer *memhandle; { return MA_free_heap(*memhandle); } public Boolean FATR f2c_free_heap_piece_(memhandle, nelem) Integer *memhandle; Integer *nelem; { return MA_free_heap_piece(*memhandle, *nelem); } public Boolean FATR f2c_get_index_(memhandle, index) Integer *memhandle; MA_AccessIndex *index; { Boolean value = MA_get_index(*memhandle, index); /* FORTRAN array indexing is 1-based, so increment index */ (*index)++; return value; } public Boolean FATR f2c_get_next_memhandle_(ithandle, memhandle) Integer *ithandle; Integer *memhandle; { return MA_get_next_memhandle(ithandle, memhandle); } public Boolean FATR f2c_get_numalign_(value) Integer *value; { return MA_get_numalign(value); } public Boolean FATR f2c_inform_base_(datatype, address1, address2) Integer *datatype; Pointer address1; Pointer address2; { return MAi_inform_base(*datatype, address1, address2); } public Boolean FATR f2c_init_(datatype, nominal_stack, nominal_heap) Integer *datatype; Integer *nominal_stack; Integer *nominal_heap; { return MA_init(*datatype, *nominal_stack, *nominal_heap); } public Boolean FATR f2c_initialized_() { return MA_initialized(); } public Boolean FATR f2c_init_memhandle_iterator_(ithandle) Integer *ithandle; { return MA_init_memhandle_iterator(ithandle); } public Integer FATR f2c_inquire_avail_(datatype) Integer *datatype; { return MA_inquire_avail(*datatype); } public Integer FATR f2c_inquire_heap_(datatype) Integer *datatype; { return MA_inquire_heap(*datatype); } public Integer FATR f2c_inquire_heap_check_stack_(datatype) Integer *datatype; { return MA_inquire_heap_check_stack(*datatype); } public Integer FATR f2c_inquire_heap_no_partition_(datatype) Integer *datatype; { return MA_inquire_heap_no_partition(*datatype); } public Integer FATR f2c_inquire_stack_(datatype) Integer *datatype; { return MA_inquire_stack(*datatype); } public Integer FATR f2c_inquire_stack_check_heap_(datatype) Integer *datatype; { return MA_inquire_stack_check_heap(*datatype); } public Integer FATR f2c_inquire_stack_no_partition_(datatype) Integer *datatype; { return MA_inquire_stack_no_partition(*datatype); } public Boolean FATR f2c_pop_stack_(memhandle) Integer *memhandle; { return MA_pop_stack(*memhandle); } public void FATR f2c_print_stats_(printroutines) Boolean *printroutines; { MA_print_stats(*printroutines); } #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS public Boolean FATR f2c_push_get_(datatype, nelem, name, memhandle, index, namesize) Integer *datatype; Integer *nelem; char *name; Integer *memhandle; MA_AccessIndex *index; int namesize; /* implicitly passed by FORTRAN */ #else /* F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS */ public Boolean FATR f2c_push_get_(datatype, nelem, name, namesize, memhandle, index) Integer *datatype; Integer *nelem; char *name; int namesize; /* implicitly passed by FORTRAN */ Integer *memhandle; MA_AccessIndex *index; #endif /* F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS */ { Boolean value; char buf[MA_NAMESIZE]; /* ensure that name is NUL-terminated */ ga_f2cstring(name, namesize, buf, (Integer)sizeof(buf)); value = MA_push_get(*datatype, *nelem, buf, memhandle, index); /* FORTRAN array indexing is 1-based, so increment index */ (*index)++; return value; } #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS public Boolean FATR f2c_push_stack_(datatype, nelem, name, memhandle, namesize) Integer *datatype; Integer *nelem; char *name; Integer *memhandle; int namesize; /* implicitly passed by FORTRAN */ #else /* F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS */ public Boolean FATR f2c_push_stack_(datatype, nelem, name, namesize, memhandle) Integer *datatype; Integer *nelem; char *name; int namesize; Integer *memhandle; #endif /* F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS */ { char buf[MA_NAMESIZE]; /* ensure that name is NUL-terminated */ ga_f2cstring(name, namesize, buf, (Integer)sizeof(buf)); return MA_push_stack(*datatype, *nelem, buf, memhandle); } public Boolean FATR f2c_set_auto_verify_(value) Integer *value; { return MA_set_auto_verify((Boolean)*value); } public Boolean FATR f2c_set_error_print_(value) Integer *value; { return MA_set_error_print((Boolean)*value); } public Boolean FATR f2c_set_hard_fail_(value) Integer *value; { return MA_set_hard_fail((Boolean)*value); } public Boolean FATR f2c_set_numalign_(value) Integer *value; { return MA_set_numalign(*value); } public Integer FATR f2c_sizeof_(datatype1, nelem1, datatype2) Integer *datatype1; Integer *nelem1; Integer *datatype2; { return MA_sizeof(*datatype1, *nelem1, *datatype2); } public Integer FATR f2c_sizeof_overhead_(datatype) Integer *datatype; { return MA_sizeof_overhead(*datatype); } public void FATR f2c_summarize_allocated_blocks_() { /* FORTRAN indices are 1-based */ MAi_summarize_allocated_blocks(1); } public void FATR f2c_trace_(value) Integer *value; { MA_trace((Boolean)*value); } public Boolean FATR f2c_verify_allocator_stuff_() { return MA_verify_allocator_stuff(); } ga-5.9.2/ma/ma.c000066400000000000000000003311121500715745200132630ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* * Portable dynamic memory allocator. */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_MALLOC_H # include #endif #include "error.h" #include "farg.h" #include "ma.h" #include "memcpy.h" #include "scope.h" #include "table.h" // this was only ever disabled for Blue Gene, which has been removed. #define ENABLE_ARMCI_MEM_OPTION 1 #if defined(ENABLE_CUDA_MEM) extern int cudaMallocManaged(void** devPtr, size_t size, unsigned int flags); #elif defined(ENABLE_ARMCI_MEM_OPTION) extern void* ARMCI_Malloc_local(long bytes); #endif /* * Memory layout: * * segment = heap_region stack_region * region = block block block ... * block = AD gap1 guard1 client_space guard2 gap2 * * A segment of memory is obtained from the OS upon initialization. * The low end of the segment is managed as a heap; the heap region * grows from low addresses to high addresses. The high end of the * segment is managed as a stack; the stack region grows from high * addresses to low addresses. * * Each region consists of a series of contiguous blocks, one per * allocation request, and possibly some unused space. Blocks in * the heap region are either in use by the client (allocated and * not yet deallocated) or not in use by the client (allocated and * already deallocated). A block on the rightmost end of the heap * region becomes part of the unused space upon deallocation. * Blocks in the stack region are always in use by the client, * because when a stack block is deallocated, it becomes part of * the unused space. * * A block consists of the client space, i.e., the range of memory * available for use by the application; guard words adjacent to * each end of the client space to help detect improper memory access * by the client; bookkeeping info (in an "allocation descriptor," * AD); and two gaps, each zero or more bytes long, to satisfy * alignment constraints (specifically, to ensure that AD and * client_space are aligned properly). */ /** ** constants **/ /* return value for returns that should never execute */ #define DONTCARE (Integer)0 /* default total # of bytes */ #define DEFAULT_TOTAL_HEAP 524288 /* 2^19 */ #define DEFAULT_TOTAL_STACK 524288 /* 2^19 */ /* estimate of max # of outstanding allocation requests */ #define DEFAULT_REQUESTS_HEAP 1 #define DEFAULT_REQUESTS_STACK 1 /* bytes per address */ #define BPA 1 /* per-allocation storage overhead, excluding alignment gaps */ #define BLOCK_OVERHEAD_FIXED (sizeof(AD) + (2 * sizeof(Guard))) /* block lengths are integral multiples of this */ /* * Note that for machines on which sizeof(pointer) * and sizeof(long) are different than sizeof(int), alignment issues * can be tricky. For example, the fields of a struct (e.g., * client_space of AD) can be improperly aligned if the struct is * dynamically placed (by MA) in such a way that the first field is * properly aligned but sizes of subsequent fields accumulate to cause * a later field to be misaligned. By defining the unit of alignment * to be the biggest of the integer and pointer types, part of the * problem is solved, but the sum of sizes of preceding fields can * still potentially cause difficulty. */ #define ALIGNMENT sizeof(size_t) /* min size of block split and placed on free list */ #define MINBLOCKSIZE mai_round((size_t)(ALIGNMENT + BLOCK_OVERHEAD_FIXED), \ (ulongi)ALIGNMENT) /* signatures for guard words */ #define GUARD1 (Guard)0xaaaaaaaa /* start signature */ #define GUARD2 (Guard)0x55555555 /* stop signature */ /** ** types **/ typedef unsigned int Guard; /* for detection of memory trashing */ typedef size_t ulongi; /* for brevity */ /* allocation request for a block */ typedef struct _AR { Integer datatype; /* of elements */ Integer nelem; /* # of elements */ } AR; /* allocation descriptor for a block */ typedef struct _AD { Integer datatype; /* of elements */ Integer nelem; /* # of elements */ char name[MA_NAMESIZE]; /* given by client */ Pointer client_space; /* start of client space */ ulongi nbytes; /* total # of bytes */ struct _AD *next; /* AD in linked list */ ulongi checksum; /* of AD */ } AD; /* block location for mh2ad */ typedef enum { BL_HeapOrStack, BL_Heap, BL_Stack, BL_StackTop } BlockLocation; /** ** function types **/ private Boolean ad_big_enough(AD *ad, Pointer ar); private Boolean ad_eq(AD *ad, Pointer ad_target); private Boolean ad_gt(AD *ad, Pointer ad_target); private Boolean ad_le(AD *ad, Pointer ad_target); private Boolean ad_lt(AD *ad, Pointer ad_target); private void ad_print(AD *ad, char *block_type); private void balloc_after(AR *ar, Pointer address, Pointer *client_space, ulongi *nbytes); private void balloc_before(AR *ar, Pointer address, Pointer *client_space, ulongi *nbytes); private void block_free_heap(AD *ad); private AD *block_split(AD *ad, ulongi bytes_needed, Boolean insert_free); private ulongi checksum(AD *ad); #ifdef DEBUG private void debug_ad_print(AD *ad); #endif /* DEBUG */ private Boolean guard_check(AD *ad); private void guard_set(AD *ad); private void list_coalesce(AD *list); private AD *list_delete(AD *ad, AD **list); private int list_delete_many(AD **list, Boolean (*pred)(), Pointer closure, void (*action)()); private AD *list_delete_one(AD **list, Boolean (*pred)(), Pointer closure); private void list_insert(AD *ad, AD **list); private void list_insert_ordered(AD *ad, AD **list, Boolean (*pred)()); private Boolean list_member(AD *ad, AD *list); private int list_print(AD *list, char *block_type, int index_base); private void list_verify(AD *list, char *block_type, char *preamble, int *blocks, int *bad_blocks, int *bad_checksums, int *bad_lguards, int *bad_rguards); private Integer ma_max_heap_frag_nelem(Integer datatype, Integer min_nelem); private Integer ma_nelem(Pointer address, ulongi length, Integer datatype); private void ma_preinitialize(char *caller); private Boolean mh2ad(Integer memhandle, AD **adout, BlockLocation location, char *caller); private void mh_free(AD *ad); private size_t mai_round(size_t value, ulongi unit); private void str_ncopy(char *to, char *from, int maxchars); /* foreign routines */ extern Integer ma_set_sizes_(); /* from the MA FORTRAN interface */ /** ** variables **/ /* base addresses of the datatypes */ private Pointer ma_base[] = { (Pointer)ma_cb_char, /* MT_C_CHAR */ (Pointer)ma_cb_int, /* MT_C_INT */ (Pointer)ma_cb_long, /* MT_C_LONGINT */ (Pointer)ma_cb_float, /* MT_C_FLOAT */ (Pointer)ma_cb_dbl, /* MT_C_DBL */ (Pointer)ma_cb_ldbl, /* MT_C_LDBL */ (Pointer)ma_cb_scpl, /* MT_C_SCPL */ (Pointer)ma_cb_dcpl, /* MT_C_DCPL */ (Pointer)ma_cb_ldcpl, /* MT_C_LDCPL */ 0, /* MT_F_BYTE */ 0, /* MT_F_INT */ 0, /* MT_F_LOG */ 0, /* MT_F_REAL */ 0, /* MT_F_DBL */ 0, /* MT_F_SCPL */ 0, /* MT_F_DCPL */ (Pointer)ma_cb_longlong /* MT_C_LONGLONG */ }; /* names of the datatypes */ private char *ma_datatype[] = { "char", "int", "long int", "float", "double", "long double", "single precision complex", "double precision complex", "long double precision complex", "byte", "integer", "logical", "real", "double precision", "single precision complex", "double precision complex", "long long" }; /* numbers of bytes in the datatypes */ private int ma_sizeof[] = { sizeof(char), /* MT_C_CHAR */ sizeof(int), /* MT_C_INT */ sizeof(long int), /* MT_C_LONGINT */ sizeof(float), /* MT_C_FLOAT */ sizeof(double), /* MT_C_DBL */ sizeof(MA_LongDouble), /* MT_C_LDBL */ sizeof(MA_SingleComplex), /* MT_C_SCPL */ sizeof(MA_DoubleComplex), /* MT_C_DCPL */ sizeof(MA_LongDoubleComplex), /* MT_C_LDCPL */ 0, /* MT_F_BYTE */ 0, /* MT_F_INT */ 0, /* MT_F_LOG */ 0, /* MT_F_REAL */ 0, /* MT_F_DBL */ 0, /* MT_F_SCPL */ 0, /* MT_F_DCPL */ sizeof(long long) /* MT_C_LONGLONG */ }; /* * Initially, ma_hp points to the start of the segment, and ma_sp * points to the first address past the end of the segment. The * start of the segment is always pointed to by ma_segment, and * the first address past the end of the segment is always pointed * to by ma_eos. The (unenforced) boundary between the heap region * and the stack region, defined at initialization, is always pointed * to by ma_partition. * * ................................................ * ^ ^ ^ * ma_segment, ma_hp ma_partition ma_eos, ma_sp * * Later, ma_hp points to the first address past the end of the * rightmost heap block, and ma_sp points to the leftmost stack block. * * hhhhhhhhhhhhhhhh.....................sssssssssss * ^ ^ ^ ^ ^ * ma_segment ma_hp ma_partition ma_sp ma_eos */ private Pointer ma_segment; /* memory from OS */ private Pointer ma_partition; /* boundary between heap and stack */ private Pointer ma_eos; /* end of segment */ private Pointer ma_hp; /* heap pointer */ private Pointer ma_sp; /* stack pointer */ private AD *ma_hfree; /* free list for heap */ private AD *ma_hused; /* used list for heap */ private AD *ma_sused; /* used list for stack */ /* toggled when ma_preinitialize succeeds */ private Boolean ma_preinitialized = MA_FALSE; /* toggled when MA_init succeeds */ private Boolean ma_initialized = MA_FALSE; /* invoke MA_verify_allocator_stuff in each public routine? */ private Boolean ma_auto_verify = MA_FALSE; /* print push/pop/alloc/free? */ private Boolean ma_trace = MA_FALSE; /* base arrays for the C datatypes */ public char ma_cb_char[2]; /* MT_C_CHAR */ public int ma_cb_int[2]; /* MT_C_INT */ public long ma_cb_long[2]; /* MT_C_LONGINT */ public long long ma_cb_longlong[2];/* MT_C_LONGLONG */ public float ma_cb_float[2]; /* MT_C_FLOAT */ public double ma_cb_dbl[2]; /* MT_C_DBL */ public MA_LongDouble ma_cb_ldbl[2]; /* MT_C_LDBL */ public MA_SingleComplex ma_cb_scpl[2]; /* MT_C_SCPL */ public MA_DoubleComplex ma_cb_dcpl[2]; /* MT_C_DCPL */ public MA_LongDoubleComplex ma_cb_ldcpl[2]; /* MT_C_LDCPL */ #if NOFORT public Integer ma_fb_byte[2]; public Integer ma_fb_integer[2]; public Logical ma_fb_logical[2]; public Real ma_fb_real[2]; public DoublePrecision ma_fb_dbl[2]; public SingleComplex ma_fb_scpl[2]; public DoubleComplex ma_fb_dcpl[2]; #endif /* requested power-of-two alignment */ private Integer ma_numalign = 0; /** ** macros **/ /* minimum of two values */ #ifdef min #undef min #endif #define min(a, b) (((b) < (a)) ? (b) : (a)) /* maximum of two values */ #ifdef max #undef max #endif #define max(a, b) (((b) > (a)) ? (b) : (a)) /* proper word ending corresponding to n */ #define plural(n) (((n) == 1) ? "" : "s") /* convert between internal and external datatype values */ #define mt_import(d) ((d) - MT_BASE) #define mt_export(d) ((d) + MT_BASE) /* return nonzero if d is a valid (external) datatype */ #define mt_valid(d) (((d) >= MT_FIRST) && ((d) <= MT_LAST)) /* convert between pointer (address) and equivalent byte address */ #define p2b(p) ((ulongi)(p) * BPA) #define b2p(b) ((Pointer)((b) / BPA)) /* return nonzero if a is a potentially valid address */ #define reasonable_address(a) (((a) >= ma_segment) && ((a) < ma_eos)) /* worst case bytes of overhead for any block of elements of datatype d */ #define max_block_overhead(d) \ (BLOCK_OVERHEAD_FIXED + (ma_sizeof[d] - 1) + (ALIGNMENT - 1)) /* compute 0-based index for client_space from AD */ #define client_space_index(ad) \ ((MA_AccessIndex)((size_t)((ad)->client_space - ma_base[(ad)->datatype]) / \ ma_sizeof[(ad)->datatype])) /* compute address of guard from AD */ #define guard1(ad) ((Pointer)((ad)->client_space - sizeof(Guard))) #define guard2(ad) ((Pointer)((ad)->client_space \ + ((ad)->nelem * ma_sizeof[(ad)->datatype]))) /* * When reading or writing guard values, it is necessary to do an * explicit byte copy to avoid bus errors caused by guards that * are not suitably aligned. */ /* copy from guard to value */ #define guard_read(guard, value) bytecopy((guard), (value), sizeof(Guard)) /* copy from value to guard */ #define guard_write(guard, value) bytecopy((value), (guard), sizeof(Guard)) /** ** statistics stuff **/ #ifdef STATS /* the number of routines for which calls are counted */ #define NUMROUTINES ((int)FID_MA_verify_allocator_stuff + 1) /* function identifiers */ typedef enum { FID_MA_alloc_get = 0, FID_MA_allocate_heap, FID_MA_chop_stack, FID_MA_free_heap, FID_MA_free_heap_piece, FID_MA_get_index, FID_MA_get_mbase, FID_MA_get_next_memhandle, FID_MA_get_numalign, FID_MA_get_pointer, FID_MA_init, FID_MA_initialized, FID_MA_init_memhandle_iterator, FID_MA_inquire_avail, FID_MA_inquire_heap, FID_MA_inquire_heap_check_stack, FID_MA_inquire_heap_no_partition, FID_MA_inquire_stack, FID_MA_inquire_stack_check_heap, FID_MA_inquire_stack_no_partition, FID_MA_pop_stack, FID_MA_print_stats, FID_MA_push_get, FID_MA_push_stack, FID_MA_set_auto_verify, FID_MA_set_error_print, FID_MA_set_hard_fail, FID_MA_set_numalign, FID_MA_sizeof, FID_MA_sizeof_overhead, FID_MA_summarize_allocated_blocks, FID_MA_trace, FID_MA_verify_allocator_stuff } FID; /* MA usage statistics */ typedef struct { ulongi hblocks; /* current # of heap blocks */ ulongi hblocks_max; /* max # of heap blocks */ ulongi hbytes; /* current # of heap bytes */ ulongi hbytes_max; /* max # of heap bytes */ ulongi sblocks; /* current # of stack blocks */ ulongi sblocks_max; /* max # of stack blocks */ ulongi sbytes; /* current # of stack bytes */ ulongi sbytes_max; /* max # of stack bytes */ ulongi calls[NUMROUTINES];/* # of calls to each routine */ } Stats; /* names of the routines */ private char *ma_routines[] = { "MA_alloc_get", "MA_allocate_heap", "MA_chop_stack", "MA_free_heap", "MA_free_heap_piece", "MA_get_index", "MA_get_mbase", "MA_get_next_memhandle", "MA_get_numalign", "MA_get_pointer", "MA_init", "MA_initialized", "MA_init_memhandle_iterator", "MA_inquire_avail", "MA_inquire_heap", "MA_inquire_heap_check_stack", "MA_inquire_heap_no_partition", "MA_inquire_stack", "MA_inquire_stack_check_heap", "MA_inquire_stack_no_partition", "MA_pop_stack", "MA_print_stats", "MA_push_get", "MA_push_stack", "MA_set_auto_verify", "MA_set_error_print", "MA_set_hard_fail", "MA_set_numalign", "MA_sizeof", "MA_sizeof_overhead", "MA_summarize_allocated_blocks", "MA_trace", "MA_verify_allocator_stuff" }; /* MA usage statistics */ private Stats ma_stats; #endif /* STATS */ /** ** private routines **/ /* ------------------------------------------------------------------------- */ /* * Return MA_TRUE if ad can satisfy ar, else return MA_FALSE. * If ad can satisfy ar, set its client_space and nbytes fields * after performing any splitting. */ /* ------------------------------------------------------------------------- */ private Boolean ad_big_enough(ad, ar) AD *ad; /* the AD to test */ Pointer ar; /* allocation request */ { Pointer client_space; /* location of client_space */ ulongi nbytes; /* length of block for ar */ /* perform trial allocation to determine size */ balloc_after((AR *)ar, (Pointer)ad, &client_space, &nbytes); if (nbytes <= ad->nbytes) { /* ad is big enough; split block if necessary */ (void)block_split(ad, nbytes, MA_TRUE); /* set fields appropriately */ ad->client_space = client_space; /* success */ return MA_TRUE; } else /* ad is not big enough */ return MA_FALSE; } /* ------------------------------------------------------------------------- */ /* * Return MA_TRUE if ad == ad_target, else return MA_FALSE. */ /* ------------------------------------------------------------------------- */ private Boolean ad_eq(ad, ad_target) AD *ad; /* the AD to test */ Pointer ad_target; /* the AD to match */ { return (ad == (AD *)ad_target) ? MA_TRUE : MA_FALSE; } /* ------------------------------------------------------------------------- */ /* * Return MA_TRUE if ad > ad_target, else return MA_FALSE. */ /* ------------------------------------------------------------------------- */ private Boolean ad_gt(ad, ad_target) AD *ad; /* the AD to test */ Pointer ad_target; /* the AD to match */ { return (ad > (AD *)ad_target) ? MA_TRUE : MA_FALSE; } /* ------------------------------------------------------------------------- */ /* * Return MA_TRUE if ad <= ad_target, else return MA_FALSE. */ /* ------------------------------------------------------------------------- */ private Boolean ad_le(ad, ad_target) AD *ad; /* the AD to test */ Pointer ad_target; /* the AD to match */ { return (ad <= (AD *)ad_target) ? MA_TRUE : MA_FALSE; } /* ------------------------------------------------------------------------- */ /* * Return MA_TRUE if ad < ad_target, else return MA_FALSE. */ /* ------------------------------------------------------------------------- */ private Boolean ad_lt(ad, ad_target) AD *ad; /* the AD to test */ Pointer ad_target; /* the AD to match */ { return (ad < (AD *)ad_target) ? MA_TRUE : MA_FALSE; } /* ------------------------------------------------------------------------- */ /* * Print identifying information about the given AD to stdout. */ /* ------------------------------------------------------------------------- */ private void ad_print(ad, block_type) AD *ad; /* to print */ char *block_type; /* for output */ { Integer memhandle; /* memhandle for AD */ /* convert AD to memhandle */ memhandle = ma_table_lookup_assoc((TableData)ad); /* print to stdout */ (void)printf("%s block '%s', handle ", block_type, ad->name); if (memhandle == TABLE_HANDLE_NONE) (void)printf("unknown"); else (void)printf("%ld", (size_t)memhandle); (void)printf(", address 0x%lx", (size_t)ad); } /* ------------------------------------------------------------------------- */ /* * Allocate a block suitable for ar starting at address. No fields of * the new block are modified. */ /* ------------------------------------------------------------------------- */ private void balloc_after(ar, address, client_space, nbytes) AR *ar; /* allocation request */ Pointer address; /* to allocate after */ Pointer *client_space; /* RETURN: location of client_space */ ulongi *nbytes; /* RETURN: length of block */ { Integer datatype; /* of elements in this block */ ulongi L_client_space; /* length of client_space */ Pointer A_client_space; /* address of client_space */ int L_gap1; /* length of gap1 */ int L_gap2; /* length of gap2 */ ulongi B_address; /* byte equivalent of address */ ulongi B_base; /* byte equivalent of ma_base[datatype] */ ulongi B_client_space; /* byte equivalent of A_client_space */ datatype = ar->datatype; B_address = p2b(address); B_base = p2b(ma_base[datatype]); /* * To ensure that client_space is properly aligned: * * (A(client_space) - ma_base[datatype]) % ma_sizeof[datatype] == 0 * * where * * A(client_space) == address + L(AD) + L(gap1) + L(guard1) */ L_client_space = ar->nelem * ma_sizeof[datatype]; L_gap1 = ((size_t)B_base - (size_t)B_address - (size_t)sizeof(AD) - (size_t)sizeof(Guard)) % (size_t)ma_sizeof[datatype]; if (L_gap1 < 0) L_gap1 += ma_sizeof[datatype]; B_client_space = B_address + sizeof(AD) + L_gap1 + sizeof(Guard); A_client_space = b2p(B_client_space); B_client_space = p2b(A_client_space); /* * To align client space according to overall alignment of absolute * address on user requested 2^ma_numalign boundary. * Note that if the base arrays are not aligned accordingly then * this alignement request is not satisfiable and will be quietly * ignored. */ if (ma_numalign > 0) { size_t mask = (1<datatype; B_address = p2b(address); B_base = p2b(ma_base[datatype]); /* * To ensure that client_space is properly aligned: * * (A(client_space) - ma_base[datatype]) % ma_sizeof[datatype] == 0 * * where * * A(client_space) == address - L(gap2) - L(guard2) - L(client_space) */ L_client_space = ar->nelem * ma_sizeof[datatype]; L_gap2 = (B_address - sizeof(Guard) - L_client_space - B_base) % ma_sizeof[datatype]; if (L_gap2 < 0) L_gap2 += ma_sizeof[datatype]; B_client_space = B_address - L_gap2 - sizeof(Guard) - L_client_space; A_client_space = b2p(B_client_space); B_client_space = p2b(A_client_space); /* * To align client space according to overall alignment of absolute * address on user requested 2^ma_numalign boundary. * Note that if the base arrays are not aligned accordingly then * this alignement request is not satisfiable and will be quietly * ignored. */ if (ma_numalign > 0) { size_t mask = (1<next) { if (ad2 > max_ad) max_ad = ad2; } if (max_ad) { /* at least 1 block is in use */ /* set ma_hp to first address past end of max_ad */ ma_hp = (Pointer)max_ad + max_ad->nbytes; /* delete any free list blocks that are no longer in heap region */ (void)list_delete_many( &ma_hfree, ad_gt, (Pointer)max_ad, (void (*)())NULL); /* if ad is in the heap region, add it to free list */ if (ad < max_ad) { list_insert_ordered(ad, &ma_hfree, ad_lt); list_coalesce(ma_hfree); } } else { /* no blocks are in use */ /* set ma_hp to start of segment */ ma_hp = ma_segment; /* clear the free list */ ma_hfree = (AD *)NULL; } } /* ------------------------------------------------------------------------- */ /* * If ad is sufficiently bigger than bytes_needed bytes, create a new * block from the remainder, optionally insert it in the free list, * and set the lengths of both blocks. * * Return a pointer to the new block (NULL if not created). */ /* ------------------------------------------------------------------------- */ private AD *block_split(ad, bytes_needed, insert_free) AD *ad; /* the AD to split */ ulongi bytes_needed; /* from ad */ Boolean insert_free; /* insert in free list? */ { ulongi bytes_extra; /* in ad */ AD *ad2; /* the new AD */ /* caller ensures that ad->nbytes >= bytes_needed */ bytes_extra = ad->nbytes - bytes_needed; if (bytes_extra >= ((ulongi)MINBLOCKSIZE)) { /* create a new block */ ad2 = (AD *)((Pointer)ad + bytes_needed); /* set the length of ad2 */ ad2->nbytes = bytes_extra; if (insert_free) { /* insert ad2 into free list */ list_insert_ordered(ad2, &ma_hfree, ad_lt); } /* set the length of ad */ ad->nbytes = bytes_needed; return ad2; } else { /* * If 0 <= bytes_extra < MINBLOCKSIZE then there are too few * extra bytes to form a new block. In this case, we simply * do nothing; ad will retain its original length (which is * either perfect or slightly too big), and the entire block * will be reclaimed upon deallocation, preventing any * memory leakage. */ return (AD *)NULL; } } /* ------------------------------------------------------------------------- */ /* * Compute and return a checksum for ad. Include all fields except name, * next, and checksum. */ /* ------------------------------------------------------------------------- */ private ulongi checksum(ad) AD *ad; /* the AD to compute checksum for */ { return (ulongi)( ad->datatype + ad->nelem + (ulongi)ad->client_space + ad->nbytes); } /* ------------------------------------------------------------------------- */ /* * Print to stderr the addresses of the fields of the given ad. */ /* ------------------------------------------------------------------------- */ #ifdef DEBUG private void debug_ad_print(ad) AD *ad; /* the AD to print */ { #define NUMADFIELDS 7 char *fn[NUMADFIELDS]; /* field names */ size_t fa[NUMADFIELDS]; /* field addresses */ int i; /* loop index */ size_t address; /* other addresses */ /* set field names */ fn[0] = "datatype"; fn[1] = "nelem"; fn[2] = "name"; fn[3] = "client_space"; fn[4] = "nbytes"; fn[5] = "next"; fn[6] = "checksum"; /* set field addresses */ fa[0] = (size_t)(&(ad->datatype)); fa[1] = (size_t)(&(ad->nelem)); fa[2] = (size_t)(&(ad->name)); fa[3] = (size_t)(&(ad->client_space)); fa[4] = (size_t)(&(ad->nbytes)); fa[5] = (size_t)(&(ad->next)); fa[6] = (size_t)(&(ad->checksum)); /* print AD fields to stderr */ (void)fprintf(stderr, "debug_ad_print:\n"); for (i = 0; i < NUMADFIELDS; i++) (void)fprintf(stderr, "\t0x%lx mod4,8,16=%d,%d,%-2d ad->%s\n", fa[i], fa[i] % 4, fa[i] % 8, fa[i] % 16, fn[i]); /* print other addresses to stderr */ address = (size_t)guard1(ad); (void)fprintf(stderr, "\t0x%lx mod4,8,16=%d,%d,%-2d guard1\n", address, address % 4, address % 8, address % 16); address = (size_t)ad->client_space; (void)fprintf(stderr, "\t0x%lx mod4,8,16=%d,%d,%-2d client_space\n", address, address % 4, address % 8, address % 16); address = (size_t)guard2(ad); (void)fprintf(stderr, "\t0x%lx mod4,8,16=%d,%d,%-2d guard2\n", address, address % 4, address % 8, address % 16); (void)fflush(stderr); } #endif /* DEBUG */ /* ------------------------------------------------------------------------- */ /* * Return MA_TRUE if the guards associated with ad contain valid signatures, * else return MA_FALSE. */ /* ------------------------------------------------------------------------- */ private Boolean guard_check(ad) AD *ad; /* the AD to check guards for */ { Guard signature; /* value to be read */ Pointer guard; /* address to read from */ guard = guard1(ad); guard_read(guard, &signature); if (signature != GUARD1) return MA_FALSE; guard = guard2(ad); guard_read(guard, &signature); if (signature != GUARD2) return MA_FALSE; /* success */ return MA_TRUE; } /* ------------------------------------------------------------------------- */ /* * Write signatures into the guards associated with ad. */ /* ------------------------------------------------------------------------- */ private void guard_set(ad) AD *ad; /* the AD to set guards for */ { Guard signature; /* value to be written */ Pointer guard; /* address to write to */ signature = GUARD1; guard = guard1(ad); guard_write(guard, &signature); signature = GUARD2; guard = guard2(ad); guard_write(guard, &signature); } /* ------------------------------------------------------------------------- */ /* * Coalesce list by merging any adjacent elements that are contiguous. * The list is assumed to be ordered by increasing addresses, i.e., * addressOf(element i) < addressOf(element i+1). */ /* ------------------------------------------------------------------------- */ private void list_coalesce(list) AD *list; /* the list to coalesce */ { AD *ad1; /* lead traversal pointer */ AD *ad2; /* trailing traversal pointer */ for (ad2 = list; ad2;) { /* compute first address beyond ad2 */ ad1 = (AD *)((Pointer)ad2 + ad2->nbytes); /* are ad2 and ad1 contiguous? */ if (ad1 == ad2->next) { /* yes; merge ad1 into ad2 */ ad2->nbytes += ad1->nbytes; ad2->next = ad1->next; } else { /* no; advance ad2 */ ad2 = ad2->next; } } } /* ------------------------------------------------------------------------- */ /* * Delete and return the first occurrence of ad from list. If ad is not * a member of list, return NULL. */ /* ------------------------------------------------------------------------- */ private AD *list_delete(ad, list) AD *ad; /* the AD to delete */ AD **list; /* the list to delete from */ { return list_delete_one(list, ad_eq, (Pointer)ad); } /* ------------------------------------------------------------------------- */ /* * Apply pred (with closure) to each element of list. Delete each element * that satisfies pred, after applying action to the element (if action is * not NULL). Return the number of elements deleted. */ /* ------------------------------------------------------------------------- */ private int list_delete_many(list, pred, closure, action) AD **list; /* the list to search */ Boolean (*pred)(); /* predicate */ Pointer closure; /* for pred */ void (*action)(); /* to apply before deletion */ { AD *ad1; /* lead traversal pointer */ AD *ad2; /* trailing traversal pointer */ int ndeleted = 0; /* # of elements deleted from list */ for (ad2 = (AD *)NULL, ad1 = *list; ad1; ad1 = ad1->next) { /* does ad1 match? */ if ((*pred)(ad1, closure)) { /* yes; apply action, then delete ad1 from list */ if (action != (void (*)())NULL) (*action)(ad1); if (ad2) { /* ad1 is second or later element */ ad2->next = ad1->next; } else { /* ad1 is first element */ *list = ad1->next; } ndeleted++; } else { /* no; ad1 survives, so scoot ad2 along */ ad2 = ad1; } } /* return the # of elements deleted from list */ return ndeleted; } /* ------------------------------------------------------------------------- */ /* * Apply pred (with closure) to each element of list. Delete and return * the first element that satisfies pred. If no element satisfies pred, * return NULL. */ /* ------------------------------------------------------------------------- */ private AD *list_delete_one(list, pred, closure) AD **list; /* the list to search */ Boolean (*pred)(); /* predicate */ Pointer closure; /* for pred */ { AD *ad1; /* lead traversal pointer */ AD *ad2; /* trailing traversal pointer */ for (ad2 = (AD *)NULL, ad1 = *list; ad1; ad2 = ad1, ad1 = ad1->next) { /* does ad1 match? */ if ((*pred)(ad1, closure)) { /* yes; delete ad1 from list */ if (ad2) { /* ad1 is second or later element */ ad2->next = ad1->next; } else { /* ad1 is first element */ *list = ad1->next; } /* success */ return ad1; } } /* failure */ return (AD *)NULL; } /* ------------------------------------------------------------------------- */ /* * Insert ad into list. */ /* ------------------------------------------------------------------------- */ private void list_insert(ad, list) AD *ad; /* the AD to insert */ AD **list; /* the list to insert into */ { /* push ad onto list */ ad->next = *list; *list = ad; } /* ------------------------------------------------------------------------- */ /* * Insert ad into list, immediately before the first element e * for which pred(ad, e) returns true. If there is no such element, * insert ad after the last element of list. */ /* ------------------------------------------------------------------------- */ private void list_insert_ordered(ad, list, pred) AD *ad; /* the AD to insert */ AD **list; /* the list to insert into */ Boolean (*pred)(); /* predicate */ { AD *ad1; /* lead traversal pointer */ AD *ad2; /* trailing traversal pointer */ if (*list == (AD *)NULL) { /* empty list */ ad->next = (AD *)NULL; *list = ad; return; } /* list has at least one element */ for (ad2 = (AD *)NULL, ad1 = *list; ad1; ad2 = ad1, ad1 = ad1->next) { /* does ad1 match? */ if ((*pred)(ad, ad1)) { /* yes; insert ad before ad1 */ if (ad2) { /* ad1 is second or later element */ ad2->next = ad; } else { /* ad1 is first element */ *list = ad; } ad->next = ad1; /* success */ return; } } /* append ad to list */ ad2->next = ad; ad->next = (AD *)NULL; } /* ------------------------------------------------------------------------- */ /* * Return MA_TRUE if ad is a member of list, else return MA_FALSE. */ /* ------------------------------------------------------------------------- */ private Boolean list_member(ad, list) AD *ad; /* the AD to search for */ AD *list; /* the list to search */ { AD *ad1; /* traversal pointer */ for (ad1 = list; ad1; ad1 = ad1->next) if (ad1 == ad) /* ad is a member of list */ return MA_TRUE; /* ad is not a member of list */ return MA_FALSE; } /* ------------------------------------------------------------------------- */ /* * Print information to stdout about each block on list. Return the * number of blocks on list. */ /* ------------------------------------------------------------------------- */ private int list_print(list, block_type, index_base) AD *list; /* to print */ char *block_type; /* for output */ int index_base; /* 0 (C) or 1 (FORTRAN) */ { AD *ad; /* traversal pointer */ int nblocks; /* # of blocks on list */ /* print each block on list */ for (ad = list, nblocks = 0; ad; ad = ad->next, nblocks++) { /* print to stdout */ ad_print(ad, block_type); (void)printf(":\n"); (void)printf("\ttype of elements:\t\t%s\n", ma_datatype[ad->datatype]); (void)printf("\tnumber of elements:\t\t%ld\n", (size_t)ad->nelem); (void)printf("\taddress of client space:\t0x%lx\n", (size_t)ad->client_space); (void)printf("\tindex for client space:\t\t%ld\n", (size_t)(client_space_index(ad) + index_base)); (void)printf("\ttotal number of bytes:\t\t%lu\n", ad->nbytes); } /* return the number of blocks on list */ return nblocks; } /* ------------------------------------------------------------------------- */ /* * Check each block on list for checksum and guard errors. For each error * found, print a message to stdout. Return counts of the various errors * in the bad_ parameters. */ /* ------------------------------------------------------------------------- */ private void list_verify(list, block_type, preamble, blocks, bad_blocks, bad_checksums, bad_lguards, bad_rguards) AD *list; /* to verify */ char *block_type; /* for error messages */ char *preamble; /* printed before first error message */ int *blocks; /* RETURN: # of blocks */ int *bad_blocks; /* RETURN: # of blocks having errors */ int *bad_checksums; /* RETURN: # of blocks having bad checksum */ int *bad_lguards; /* RETURN: # of blocks having bad guard1 */ int *bad_rguards; /* RETURN: # of blocks having bad guard2 */ { AD *ad; /* traversal pointer */ Boolean first_bad_block;/* first bad block found? */ Boolean bad_block; /* problem in current block? */ Guard signature; /* value to be read */ Pointer guard; /* address to read from */ /* initialize */ *blocks = 0; *bad_blocks = 0; *bad_checksums = 0; *bad_lguards = 0; *bad_rguards = 0; first_bad_block = MA_TRUE; /* check each block on list */ for (ad = list; ad; ad = ad->next) { (*blocks)++; bad_block = MA_FALSE; /* check for checksum error */ if (checksum(ad) != ad->checksum) { /* print preamble if necessary */ if (first_bad_block && (preamble != (char *)NULL)) { (void)printf("%s",preamble); first_bad_block = MA_FALSE; } /* print error message to stdout */ ad_print(ad, block_type); (void)printf(":\n\t"); (void)printf("current checksum %lu != stored checksum %lu\n", checksum(ad), ad->checksum); /* do bookkeeping */ (*bad_checksums)++; bad_block = MA_TRUE; } /* check for bad guard1 */ guard = guard1(ad); guard_read(guard, &signature); if (signature != GUARD1) { /* print preamble if necessary */ if (first_bad_block && (preamble != (char *)NULL)) { (void)printf("%s",preamble); first_bad_block = MA_FALSE; } /* print error message to stdout */ ad_print(ad, block_type); (void)printf(":\n\t"); (void)printf( "current left signature %u != proper left signature %u\n", signature, GUARD1); /* do bookkeeping */ (*bad_lguards)++; bad_block = MA_TRUE; } /* check for bad guard2 */ guard = guard2(ad); guard_read(guard, &signature); if (signature != GUARD2) { /* print preamble if necessary */ if (first_bad_block && (preamble != (char *)NULL)) { (void)printf("%s",preamble); first_bad_block = MA_FALSE; } /* print error message to stdout */ ad_print(ad, block_type); (void)printf(":\n\t"); (void)printf( "current right signature %u != proper right signature %u\n", signature, GUARD2); /* do bookkeeping */ (*bad_rguards)++; bad_block = MA_TRUE; } /* if any errors, bump bad block count */ if (bad_block) (*bad_blocks)++; } } /* ------------------------------------------------------------------------- */ /* * Return the maximum number of datatype elements that can currently be * accomodated in a heap fragment (a block on the heap free list) entirely * within the heap region, or 0 if this number is less than min_nelem. */ /* ------------------------------------------------------------------------- */ private Integer ma_max_heap_frag_nelem(datatype, min_nelem) Integer datatype; /* of elements */ Integer min_nelem; /* for fragment to be considered */ { ulongi min_bytes; /* for fragment to be considered */ AD *ad; /* traversal pointer */ ulongi nbytes; /* in current fragment */ Integer nelem; /* in current fragment */ Integer max_nelem; /* result */ /* set the threshold */ min_bytes = (min_nelem * ma_sizeof[datatype]) + BLOCK_OVERHEAD_FIXED; /* search the heap free list */ max_nelem = 0; for (ad = ma_hfree; ad; ad = ad->next) { /* * There are 3 cases to consider: * * (a) fragment is outside heap region * (b) fragment straddles partition between heap and stack regions * (c) fragment is inside heap region */ if ((Pointer)ad >= ma_partition) { /* case (a): reject */ continue; } else if (((Pointer)ad + ad->nbytes) >= ma_partition) { /* case (b): truncate fragment at partition */ nbytes = (ulongi)(ma_partition - (Pointer)ad); } else { /* case (c): accept */ nbytes = ad->nbytes; } if (nbytes >= min_bytes) { nelem = ma_nelem((Pointer)ad, nbytes, datatype); max_nelem = max(max_nelem, nelem); } } /* return the result */ return max_nelem; } /* ------------------------------------------------------------------------- */ /* * Return the maximum number of datatype elements that can currently * be accomodated in length bytes starting at address. */ /* ------------------------------------------------------------------------- */ private Integer ma_nelem(address, length, datatype) Pointer address; /* location of hypothetical block */ ulongi length; /* length of hypothetical block */ Integer datatype; /* of elements in hypothetical block */ { AR ar; /* allocation request */ Pointer client_space; /* location of client_space */ ulongi nbytes; /* length of block for ar */ if (length <= BLOCK_OVERHEAD_FIXED) /* no point in computing anything */ return (Integer)0; /* compute initial request */ ar.datatype = datatype; ar.nelem = (length - BLOCK_OVERHEAD_FIXED) / ma_sizeof[datatype]; /* make requests until one succeeds or we give up */ while (ar.nelem > 0) { /* perform trial allocation to determine size */ balloc_after(&ar, address, &client_space, &nbytes); if (nbytes > length) /* not enough space for ar.nelem elements */ ar.nelem--; else /* enough space for ar.nelem elements */ break; } /* return the result */ return ar.nelem; } /* ------------------------------------------------------------------------- */ /* * Perform operations necessary to allow certain functions to be invoked * before MA_init is called. */ /* ------------------------------------------------------------------------- */ private void ma_preinitialize(caller) char *caller; /* name of calling routine */ { if (ma_preinitialized) return; /* call a FORTRAN routine to set bases and sizes of FORTRAN datatypes */ if (ma_set_sizes_() == 0) { (void)sprintf(ma_ebuf, "unable to set sizes of FORTRAN datatypes"); ma_error(EL_Fatal, ET_Internal, caller, ma_ebuf); return; } /* success */ ma_preinitialized = MA_TRUE; } /* ------------------------------------------------------------------------- */ /* * If memhandle is valid according to location, return the corresponding AD * in adout and return MA_TRUE, else print an error message and return * MA_FALSE. */ /* ------------------------------------------------------------------------- */ private Boolean mh2ad(memhandle, adout, location, caller) Integer memhandle; /* the handle to verify and convert */ AD **adout; /* RETURN: AD corresponding to memhandle */ BlockLocation location; /* where AD must reside */ char *caller; /* name of calling routine */ { AD *ad; Boolean check_checksum = MA_TRUE; Boolean check_guards = MA_TRUE; Boolean check_heap = MA_FALSE; Boolean check_stack = MA_FALSE; Boolean check_stacktop = MA_FALSE; Boolean check_heapandstack = MA_FALSE; switch (location) { case BL_HeapOrStack: check_heapandstack = MA_TRUE; break; case BL_Heap: check_heap = MA_TRUE; break; case BL_Stack: check_stack = MA_TRUE; break; case BL_StackTop: check_stacktop = MA_TRUE; break; default: (void)sprintf(ma_ebuf, "invalid location: %d", (int)location); ma_error(EL_Nonfatal, ET_Internal, "mh2ad", ma_ebuf); return MA_FALSE; } /* convert memhandle to AD */ if (!ma_table_verify(memhandle, caller)) return MA_FALSE; else ad = (AD *)ma_table_lookup(memhandle); /* attempt to avoid crashes due to corrupt addresses */ if (!reasonable_address((Pointer)ad)) { (void)sprintf(ma_ebuf, "invalid block address (0x%lx) for memhandle %ld", (size_t)ad, (size_t)memhandle); ma_error(EL_Nonfatal, ET_External, caller, ma_ebuf); return MA_FALSE; } if (check_checksum) { if (checksum(ad) != ad->checksum) { (void)sprintf(ma_ebuf, "invalid checksum for memhandle %ld (name: '%s')", (size_t)memhandle, ad->name); ma_error(EL_Nonfatal, ET_External, caller, ma_ebuf); return MA_FALSE; } } if (check_guards) { if (!guard_check(ad)) { (void)sprintf(ma_ebuf, "invalid guard(s) for memhandle %ld (name: '%s')", (size_t)memhandle, ad->name); ma_error(EL_Nonfatal, ET_External, caller, ma_ebuf); return MA_FALSE; } } if (check_heap) { if (!list_member(ad, ma_hused)) { (void)sprintf(ma_ebuf, "memhandle %ld (name: '%s') not in heap", (size_t)memhandle, ad->name); ma_error(EL_Nonfatal, ET_External, caller, ma_ebuf); return MA_FALSE; } } else if (check_stack) { if (!list_member(ad, ma_sused)) { (void)sprintf(ma_ebuf, "memhandle %ld (name: '%s') not in stack", (size_t)memhandle, ad->name); ma_error(EL_Nonfatal, ET_External, caller, ma_ebuf); return MA_FALSE; } } else if (check_stacktop) { /* is it in the stack? */ if (!list_member(ad, ma_sused)) { (void)sprintf(ma_ebuf, "memhandle %ld (name: '%s') not in stack", (size_t)memhandle, ad->name); ma_error(EL_Nonfatal, ET_External, caller, ma_ebuf); return MA_FALSE; } /* is it on top of the stack? */ if ((Pointer)ad != ma_sp) { (void)sprintf(ma_ebuf, "memhandle %ld (name: '%s') not top of stack", (size_t)memhandle, ad->name); ma_error(EL_Nonfatal, ET_External, caller, ma_ebuf); return MA_FALSE; } } else if (check_heapandstack) { if ((!list_member(ad, ma_hused)) && (!list_member(ad, ma_sused))) { (void)sprintf(ma_ebuf, "memhandle %ld (name: '%s') not in heap or stack", (size_t)memhandle, ad->name); ma_error(EL_Nonfatal, ET_External, caller, ma_ebuf); return MA_FALSE; } } /* ad is valid */ *adout = ad; return MA_TRUE; } /* ------------------------------------------------------------------------- */ /* * Free the memhandle corresponding to the given AD. */ /* ------------------------------------------------------------------------- */ private void mh_free(ad) AD *ad; /* the AD whose memhandle to free */ { Integer memhandle; /* memhandle for AD */ /* convert AD to memhandle */ if ((memhandle = ma_table_lookup_assoc((TableData)ad)) == TABLE_HANDLE_NONE) { (void)sprintf(ma_ebuf, "cannot find memhandle for block address 0x%lx", (size_t)ad); ma_error(EL_Nonfatal, ET_Internal, "mh_free", ma_ebuf); } else /* free memhandle */ ma_table_deallocate(memhandle); } /* ------------------------------------------------------------------------- */ /* * Return the first multiple of unit which is >= value. */ /* ------------------------------------------------------------------------- */ private size_t mai_round(value, unit) size_t value; /* to round */ ulongi unit; /* to round to */ { /* voodoo ... */ unit--; value += unit; value &= ~(size_t)unit; return value; } /* ------------------------------------------------------------------------- */ /* * Copy at most maxchars-1 non-NUL chars from from to to; NUL-terminate to. */ /* ------------------------------------------------------------------------- */ private void str_ncopy(to, from, maxchars) char *to; /* space to copy to */ char *from; /* space to copy from */ int maxchars; /* max # of chars (including NUL) copied */ { if (from == (char *)NULL) { to[0] = '\0'; return; } /* copy up to maxchars chars */ (void)strncpy(to, from, maxchars); /* ensure to is NUL-terminated */ to[maxchars-1] = '\0'; } /** ** public routines for internal use only **/ /* ------------------------------------------------------------------------- */ /* * Set the base address and size of the given datatype. */ /* ------------------------------------------------------------------------- */ public Boolean MAi_inform_base(datatype, address1, address2) Integer datatype; /* to set size of */ Pointer address1; /* of datatype element base */ Pointer address2; /* of an adjacent datatype element */ { /* verify uninitialization */ if (ma_initialized) { (void)sprintf(ma_ebuf, "MA already initialized"); ma_error(EL_Nonfatal, ET_Internal, "MAi_inform_base", ma_ebuf); return MA_FALSE; } /* verify datatype */ if (!mt_valid(datatype)) { (void)sprintf(ma_ebuf, "invalid datatype: %ld", (size_t)datatype); ma_error(EL_Nonfatal, ET_Internal, "MAi_inform_base", ma_ebuf); return MA_FALSE; } /* convert datatype to internal (index-suitable) value */ datatype = mt_import(datatype); /* set the base address of datatype */ ma_base[datatype] = address1; /* set the size of datatype */ ma_sizeof[datatype] = (int)(address2 - address1); /* success */ return MA_TRUE; } #if NOFORT Integer ma_set_sizes_() { MAi_inform_base(MT_F_BYTE, (Pointer)&ma_fb_byte[0], (Pointer)&ma_fb_byte[1]); MAi_inform_base(MT_F_INT, (Pointer)&ma_fb_integer[0], (Pointer)&ma_fb_integer[1]); MAi_inform_base(MT_F_LOG, (Pointer)&ma_fb_logical[0], (Pointer)&ma_fb_logical[1]); MAi_inform_base(MT_F_REAL, (Pointer)&ma_fb_real[0], (Pointer)&ma_fb_real[1]); MAi_inform_base(MT_F_DBL, (Pointer)&ma_fb_dbl[0], (Pointer)&ma_fb_dbl[1]); MAi_inform_base(MT_F_SCPL, (Pointer)&ma_fb_scpl[0], (Pointer)&ma_fb_scpl[1]); MAi_inform_base(MT_F_DCPL, (Pointer)&ma_fb_dcpl[0], (Pointer)&ma_fb_dcpl[1]); return 1; } #endif /* ------------------------------------------------------------------------- */ /* * Print debugging information about all blocks currently in use * on the heap or the stack. */ /* ------------------------------------------------------------------------- */ public void MAi_summarize_allocated_blocks(index_base) int index_base; /* 0 (C) or 1 (FORTRAN) */ { int heap_blocks; /* # of blocks on heap used list */ int stack_blocks; /* # of blocks on stack used list */ #ifdef STATS ma_stats.calls[(int)FID_MA_summarize_allocated_blocks]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return; #endif /* VERIFY */ /* verify index_base */ if ((index_base != 0) && (index_base != 1)) { (void)sprintf(ma_ebuf, "invalid index_base: %d", index_base); ma_error(EL_Nonfatal, ET_Internal, "MAi_summarize_allocated_blocks", ma_ebuf); return; } (void)printf("MA_summarize_allocated_blocks: starting scan ...\n"); /* print blocks on the heap used list */ heap_blocks = list_print(ma_hused, "heap", index_base); /* print blocks on the stack used list */ stack_blocks = list_print(ma_sused, "stack", index_base); (void)printf("MA_summarize_allocated_blocks: scan completed: "); (void)printf("%d heap block%s, %d stack block%s\n", heap_blocks, plural(heap_blocks), stack_blocks, plural(stack_blocks)); } /** ** public routines **/ /* ------------------------------------------------------------------------- */ /* * Convenience function that combines MA_allocate_heap and MA_get_index. */ /* ------------------------------------------------------------------------- */ public Boolean MA_alloc_get( Integer datatype, /* of elements in this block */ Integer nelem, /* # of elements in this block */ const char *name, /* assigned to this block by client */ Integer *memhandle, /* RETURN: handle for this block */ MA_AccessIndex *index /* RETURN: index for this block */ ) { #ifdef STATS ma_stats.calls[(int)FID_MA_alloc_get]++; #endif /* STATS */ if (MA_allocate_heap(datatype, nelem, name, memhandle)) /* MA_allocate_heap succeeded; try MA_get_index */ return MA_get_index(*memhandle, index); else /* MA_allocate_heap failed */ return MA_FALSE; } /* ------------------------------------------------------------------------- */ /* * Allocate a heap block big enough to hold nelem elements * of the given datatype. * * Return MA_TRUE upon success, or MA_FALSE upon failure. */ /* ------------------------------------------------------------------------- */ public Boolean MA_allocate_heap( Integer datatype, /* of elements in this block */ Integer nelem, /* # of elements in this block */ const char *name, /* assigned to this block by client */ Integer *memhandle /* RETURN: handle for this block */ ) { AR ar; /* allocation request */ AD *ad; /* AD for newly allocated block */ Pointer client_space; /* location of client_space */ ulongi nbytes; /* length of block for ar */ Pointer new_hp; /* new ma_hp */ #ifdef STATS ma_stats.calls[(int)FID_MA_allocate_heap]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return MA_FALSE; #endif /* VERIFY */ if (ma_trace) (void)printf("MA: allocating '%s' (%d)\n", name, (int)nelem); /* verify initialization */ if (!ma_initialized) { (void)sprintf(ma_ebuf, "block '%s', MA not yet initialized", name); ma_error(EL_Nonfatal, ET_External, "MA_allocate_heap", ma_ebuf); return MA_FALSE; } /* verify datatype */ if (!mt_valid(datatype)) { (void)sprintf(ma_ebuf, "block '%s', invalid datatype: %ld", name, (size_t)datatype); ma_error(EL_Nonfatal, ET_External, "MA_allocate_heap", ma_ebuf); return MA_FALSE; } /* verify nelem */ if (nelem < 0) { (void)sprintf(ma_ebuf, "block '%s', invalid nelem: %ld", name, (size_t)nelem); ma_error(EL_Nonfatal, ET_External, "MA_allocate_heap", ma_ebuf); return MA_FALSE; } /* convert datatype to internal (index-suitable) value */ datatype = mt_import(datatype); /* * attempt to allocate space */ ar.datatype = datatype; ar.nelem = nelem; /* search the free list */ ad = list_delete_one(&ma_hfree, ad_big_enough, (Pointer)&ar); /* if search of free list failed, try expanding heap region */ if (ad == (AD *)NULL) { /* perform trial allocation to determine size */ balloc_after(&ar, ma_hp, &client_space, &nbytes); new_hp = ma_hp + nbytes; if (new_hp > ma_sp) { (void)sprintf(ma_ebuf, "block '%s', not enough space to allocate %lu bytes", name, nbytes); ma_error(EL_Nonfatal, ET_External, "MA_allocate_heap", ma_ebuf); return MA_FALSE; } else { /* heap region expanded successfully */ ad = (AD *)ma_hp; /* set fields appropriately */ ad->client_space = client_space; ad->nbytes = nbytes; } } /* * space has been allocated */ /* initialize the AD */ ad->datatype = datatype; ad->nelem = nelem; str_ncopy(ad->name, (char*)name, MA_NAMESIZE); /* ad->client_space is already set */ /* ad->nbytes is already set */ list_insert(ad, &ma_hused); ad->checksum = checksum(ad); /* set the guards */ guard_set(ad); #ifdef DEBUG debug_ad_print(ad); #endif /* DEBUG */ /* update ma_hp if necessary */ new_hp = (Pointer)ad + ad->nbytes; if (new_hp > ma_hp) { ma_hp = new_hp; } #ifdef STATS ma_stats.hblocks++; ma_stats.hblocks_max = max(ma_stats.hblocks, ma_stats.hblocks_max); ma_stats.hbytes += ad->nbytes; ma_stats.hbytes_max = max(ma_stats.hbytes, ma_stats.hbytes_max); #endif /* STATS */ /* convert AD to memhandle */ if ((*memhandle = ma_table_allocate((TableData)ad)) == TABLE_HANDLE_NONE) /* failure */ return MA_FALSE; else /* success */ return MA_TRUE; } /* ------------------------------------------------------------------------- */ /* * Deallocate the given stack block and all stack blocks allocated * after it. * * Return MA_TRUE upon success, or MA_FALSE upon failure. */ /* ------------------------------------------------------------------------- */ public Boolean MA_chop_stack(Integer memhandle)/*the block to deallocate up to*/ { AD *ad; /* AD for memhandle */ #ifdef STATS ma_stats.calls[(int)FID_MA_chop_stack]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return MA_FALSE; #endif /* VERIFY */ /* verify memhandle and convert to AD */ if (!mh2ad(memhandle, &ad, BL_Stack, "MA_chop_stack")) return MA_FALSE; /* delete block and all blocks above it from used list */ #ifdef STATS ma_stats.sblocks -= list_delete_many(&ma_sused, ad_le, (Pointer)ad, mh_free); #else (void)list_delete_many(&ma_sused, ad_le, (Pointer)ad, mh_free); #endif /* STATS */ /* pop block and all blocks above it from stack */ #ifdef STATS ma_stats.sbytes -= (((Pointer)ad + ad->nbytes) - ma_sp); #endif /* STATS */ ma_sp = (Pointer)ad + ad->nbytes; /* success */ return MA_TRUE; } /* ------------------------------------------------------------------------- */ /* * Deallocate the given heap block. * * Return MA_TRUE upon success, or MA_FALSE upon failure. */ /* ------------------------------------------------------------------------- */ public Boolean MA_free_heap(Integer memhandle) /* the block to deallocate */ { AD *ad; /* AD for memhandle */ #ifdef STATS ma_stats.calls[(int)FID_MA_free_heap]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return MA_FALSE; #endif /* VERIFY */ /* verify memhandle and convert to AD */ if (!mh2ad(memhandle, &ad, BL_Heap, "MA_free_heap")) return MA_FALSE; if (ma_trace) (void)printf("MA: freeing '%s'\n", ad->name); /* delete block from used list */ if (list_delete(ad, &ma_hused) != ad) { (void)sprintf(ma_ebuf, "memhandle %ld (name: '%s') not on heap used list", (size_t)memhandle, ad->name); ma_error(EL_Nonfatal, ET_Internal, "MA_free_heap", ma_ebuf); return MA_FALSE; } #ifdef STATS ma_stats.hblocks--; ma_stats.hbytes -= ad->nbytes; #endif /* STATS */ /* reclaim the deallocated block */ block_free_heap(ad); /* free memhandle */ ma_table_deallocate(memhandle); /* success */ return MA_TRUE; } /* ------------------------------------------------------------------------- */ /* * Deallocate nelem elements from the given heap block. * * The nelem elements (of the datatype specified when the heap block * was allocated) to be deallocated are assumed to be at the rightmost * (i.e., higher addresses) edge of the heap block. * * Return MA_TRUE upon success, or MA_FALSE upon failure. */ /* ------------------------------------------------------------------------- */ public Boolean MA_free_heap_piece( Integer memhandle, /* the block to deallocate a piece of */ Integer nelem /* # of elements to deallocate */) { AD *ad; /* AD for memhandle */ AD *ad_reclaim; /* AD for data returned */ AR ar; /* AR for data kept */ Pointer client_space; /* location of client_space */ ulongi nbytes; /* length of block for data kept */ #ifdef STATS ma_stats.calls[(int)FID_MA_free_heap_piece]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return MA_FALSE; #endif /* VERIFY */ /* verify memhandle and convert to AD */ if (!mh2ad(memhandle, &ad, BL_Heap, "MA_free_heap_piece")) return MA_FALSE; /* verify nelem */ if (nelem < 0) { (void)sprintf(ma_ebuf, "block '%s', invalid nelem: %ld", ad->name, (size_t)nelem); ma_error(EL_Nonfatal, ET_External, "MA_free_heap_piece", ma_ebuf); return MA_FALSE; } else if (nelem >= ad->nelem) { /* deallocate the whole block */ return MA_free_heap(memhandle); } if (ma_trace) (void)printf("MA: freeing %ld elements of '%s'\n", (size_t)nelem, ad->name); /* set AR for data to keep */ ar.datatype = ad->datatype; ar.nelem = ad->nelem - nelem; /* perform trial allocation to determine size */ balloc_after(&ar, (Pointer)ad, &client_space, &nbytes); if (nbytes < ad->nbytes) { /* ad has extra space; split block if possible */ ad_reclaim = block_split(ad, nbytes, (Boolean)MA_FALSE); if (ad_reclaim) { #ifdef STATS ma_stats.hbytes -= ad_reclaim->nbytes; #endif /* STATS */ /* reclaim the deallocated (new) block */ block_free_heap(ad_reclaim); } } /* update surviving block */ ad->nelem = ar.nelem; ad->checksum = checksum(ad); /* set the guards */ guard_set(ad); #ifdef DEBUG debug_ad_print(ad); #endif /* DEBUG */ /* success */ return MA_TRUE; } /* ------------------------------------------------------------------------- */ /* * Get the base index for the given block. * * Return MA_TRUE upon success, or MA_FALSE upon failure. */ /* ------------------------------------------------------------------------- */ public Boolean MA_get_index( Integer memhandle, /* block to get index for */ MA_AccessIndex *index /* RETURN: base index */) { AD *ad; /* AD for memhandle */ #ifdef STATS ma_stats.calls[(int)FID_MA_get_index]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return MA_FALSE; #endif /* VERIFY */ /* verify memhandle and convert to AD */ if (mh2ad(memhandle, &ad, BL_HeapOrStack, "MA_get_index")) { /* compute index */ *index = client_space_index(ad); /* success */ return MA_TRUE; } else { /* failure */ return MA_FALSE; } } /* ------------------------------------------------------------------------- */ /* * Return the base address of the given datatype. */ /* ------------------------------------------------------------------------- */ public Pointer MA_get_mbase(Integer datatype) /* to get base address of */ { #ifdef STATS ma_stats.calls[(int)FID_MA_get_mbase]++; #endif /* STATS */ /* preinitialize if necessary */ ma_preinitialize("MA_get_mbase"); /* verify datatype */ if (!mt_valid(datatype)) { (void)sprintf(ma_ebuf, "invalid datatype: %ld", (size_t)datatype); ma_error(EL_Fatal, ET_External, "MA_get_mbase", ma_ebuf); return NULL; } /* convert datatype to internal (index-suitable) value */ datatype = mt_import(datatype); return ma_base[datatype]; } /* ------------------------------------------------------------------------- */ /* * Get the handle for the next block in the scan of currently allocated * blocks. * * Return MA_TRUE upon success, or MA_FALSE upon failure. */ /* ------------------------------------------------------------------------- */ public Boolean MA_get_next_memhandle( Integer *ithandle, /* handle for this iterator */ Integer *memhandle /* RETURN: handle for the next block */) { #ifdef STATS ma_stats.calls[(int)FID_MA_get_next_memhandle]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return MA_FALSE; #endif /* VERIFY */ /* not yet implemented */ (void)sprintf(ma_ebuf, "not yet implemented"); ma_error(EL_Nonfatal, ET_External, "MA_get_next_memhandle", ma_ebuf); return MA_FALSE; } /* ------------------------------------------------------------------------- */ /* * Get the requested alignment. * * Return MA_TRUE upon success, or MA_FALSE upon failure. */ /* ------------------------------------------------------------------------- */ public Boolean MA_get_numalign(Integer *value) /* RETURN: requested alignment */ { #ifdef STATS ma_stats.calls[(int)FID_MA_get_numalign]++; #endif /* STATS */ *value = ma_numalign; return MA_TRUE; } /* ------------------------------------------------------------------------- */ /* * Get the base pointer for the given block. * * Return MA_TRUE upon success, or MA_FALSE upon failure. */ /* ------------------------------------------------------------------------- */ /* JN converted to void* to avoid calling hassles */ public Boolean MA_get_pointer( Integer memhandle, /* block to get pointer for */ void *pointer /* RETURN: base pointer */) { AD *ad; /* AD for memhandle */ #ifdef STATS ma_stats.calls[(int)FID_MA_get_pointer]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return MA_FALSE; #endif /* VERIFY */ /* verify memhandle and convert to AD */ if (mh2ad(memhandle, &ad, BL_HeapOrStack, "MA_get_pointer")) { /* compute pointer */ #if 0 *pointer = ad->client_space; #endif *(char**)pointer = ad->client_space; /* success */ return MA_TRUE; } else { /* failure */ return MA_FALSE; } } /* ------------------------------------------------------------------------- */ /* * Initialize the memory allocator. * * Return MA_TRUE upon success, or MA_FALSE upon failure. */ /* ------------------------------------------------------------------------- */ public Boolean MA_init( Integer datatype, /* for computing storage requirement */ Integer nominal_stack, /* # of datatype elements desired for stack */ Integer nominal_heap /* # of datatype elements desired for heap */) { ulongi heap_bytes; /* # of bytes for heap */ ulongi stack_bytes; /* # of bytes for stack */ ulongi total_bytes; /* total # of bytes */ #ifdef STATS ma_stats.calls[(int)FID_MA_init]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return MA_FALSE; #endif /* VERIFY */ /* preinitialize if necessary */ ma_preinitialize("MA_init"); /* verify uninitialization */ if (ma_initialized) { (void)sprintf(ma_ebuf, "MA already initialized"); ma_error(EL_Nonfatal, ET_External, "MA_init", ma_ebuf); return MA_FALSE; } /* verify datatype */ if (!mt_valid(datatype)) { (void)sprintf(ma_ebuf, "invalid datatype: %ld", (size_t)datatype); ma_error(EL_Nonfatal, ET_External, "MA_init", ma_ebuf); return MA_FALSE; } /* convert datatype to internal (index-suitable) value */ datatype = mt_import(datatype); /* compute # of bytes in heap */ if (nominal_heap < 0) { heap_bytes = DEFAULT_TOTAL_HEAP; } else { heap_bytes = (nominal_heap * ma_sizeof[datatype]) + (DEFAULT_REQUESTS_HEAP * max_block_overhead(datatype)); } heap_bytes = (size_t)mai_round((size_t)heap_bytes, (ulongi)ALIGNMENT); /* compute # of bytes in stack */ if (nominal_stack < 0) { stack_bytes = DEFAULT_TOTAL_STACK; } else { stack_bytes = (nominal_stack * ma_sizeof[datatype]) + (DEFAULT_REQUESTS_STACK * max_block_overhead(datatype)); } stack_bytes = (size_t)mai_round((size_t)stack_bytes, (ulongi)ALIGNMENT); /* segment consists of heap and stack */ total_bytes = heap_bytes + stack_bytes; #ifdef NOUSE_MMAP #if HAVE_MALLOPT /* disable memory mapped malloc */ mallopt(M_MMAP_MAX, 0); mallopt(M_TRIM_THRESHOLD, -1); #endif #endif /* allocate the segment of memory */ #ifdef ENABLE_CUDA_MEM if(getenv("MA_USE_CUDA_MEM")) { void * temp_ptr = NULL; int cuda_error = 0; // cudaSuccess 0 unsigned int cuda_mem_flags = 0x01; // cudaMemAttachGlobal 0x01 cuda_error = cudaMallocManaged(&temp_ptr, total_bytes, cuda_mem_flags); if (cuda_error == 0) { ma_segment = temp_ptr; } else { ma_segment = NULL; } } else #elif defined(ENABLE_ARMCI_MEM_OPTION) if(getenv("MA_USE_ARMCI_MEM")) { ma_segment = (Pointer)ARMCI_Malloc_local(total_bytes); } else #endif { ma_segment = (Pointer)bytealloc(total_bytes); } if (ma_segment == (Pointer)NULL) { (void)sprintf(ma_ebuf, "could not allocate %lu bytes", total_bytes); ma_error(EL_Nonfatal, ET_External, "MA_init", ma_ebuf); return MA_FALSE; } /* * initialize management stuff */ /* partition is at (first address past) end of heap */ ma_partition = ma_segment + heap_bytes; /* compute (first address past) end of segment */ ma_eos = ma_segment + total_bytes; /* ma_hp initially points at start of segment */ ma_hp = ma_segment; /* ma_sp initially points at end of segment */ ma_sp = ma_eos; /* lists are all initially empty */ ma_hfree = (AD *)NULL; ma_hused = (AD *)NULL; ma_sused = (AD *)NULL; /* we are now initialized */ ma_initialized = MA_TRUE; /* success */ return MA_TRUE; } /* ------------------------------------------------------------------------- */ /* * Return MA_TRUE if MA_init has been called successfully, * else return MA_FALSE. */ /* ------------------------------------------------------------------------- */ public Boolean MA_initialized() { #ifdef STATS ma_stats.calls[(int)FID_MA_initialized]++; #endif /* STATS */ return ma_initialized; } /* ------------------------------------------------------------------------- */ /* * Initialize a scan of currently allocated blocks. * * Return MA_TRUE upon success, or MA_FALSE upon failure. */ /* ------------------------------------------------------------------------- */ public Boolean MA_init_memhandle_iterator( Integer *ithandle) { #ifdef STATS ma_stats.calls[(int)FID_MA_init_memhandle_iterator]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return MA_FALSE; #endif /* VERIFY */ /* not yet implemented */ (void)sprintf(ma_ebuf, "not yet implemented"); ma_error(EL_Nonfatal, ET_External, "MA_init_memhandle_iterator", ma_ebuf); return MA_FALSE; } /* ------------------------------------------------------------------------- */ /* * Return the maximum number of datatype elements that can currently * be allocated in the space between the heap and the stack, in a single * allocation request, ignoring the partition defined at initialization. * * Note that this might not be the largest piece of memory available; * the heap may contain deallocated blocks that are larger. */ /* ------------------------------------------------------------------------- */ public Integer MA_inquire_avail(Integer datatype) { size_t gap_length; /* # of bytes between heap and stack */ Integer nelem_gap; /* max elements containable in gap */ #ifdef STATS ma_stats.calls[(int)FID_MA_inquire_avail]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return DONTCARE; #endif /* VERIFY */ /* verify initialization */ if (!ma_initialized) { (void)sprintf(ma_ebuf, "MA not yet initialized"); ma_error(EL_Nonfatal, ET_External, "MA_inquire_avail", ma_ebuf); return (Integer)0; } /* verify datatype */ if (!mt_valid(datatype)) { (void)sprintf(ma_ebuf, "invalid datatype: %ld", (size_t)datatype); ma_error(EL_Fatal, ET_External, "MA_inquire_avail", ma_ebuf); return DONTCARE; } /* convert datatype to internal (index-suitable) value */ datatype = mt_import(datatype); /* * compute the # of elements for which space is available */ /* try space between heap and stack */ gap_length = (size_t)(ma_sp - ma_hp); if (gap_length > 0) nelem_gap = ma_nelem(ma_hp, (ulongi)gap_length, datatype); else nelem_gap = 0; /* success */ return nelem_gap; } /* ------------------------------------------------------------------------- */ /* * Return the maximum number of datatype elements that can currently * be allocated in the heap, in a single allocation request, * honoring the partition defined at initialization. * * This routine does not check the stack. Therefore, if the stack * has overgrown the partition, the answer returned by this routine * might be incorrect, i.e., there might be less memory available * than this routine indicates. */ /* ------------------------------------------------------------------------- */ public Integer MA_inquire_heap(Integer datatype) { size_t gap_length; /* # of bytes between heap and partition */ Integer nelem_gap; /* max elements containable in gap */ Integer nelem_frag; /* max elements containable in any frag */ #ifdef STATS ma_stats.calls[(int)FID_MA_inquire_heap]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return DONTCARE; #endif /* VERIFY */ /* verify initialization */ if (!ma_initialized) { (void)sprintf(ma_ebuf, "MA not yet initialized"); ma_error(EL_Nonfatal, ET_External, "MA_inquire_heap", ma_ebuf); return (Integer)0; } /* verify datatype */ if (!mt_valid(datatype)) { (void)sprintf(ma_ebuf, "invalid datatype: %ld", (size_t)datatype); ma_error(EL_Fatal, ET_External, "MA_inquire_heap", ma_ebuf); return DONTCARE; } /* convert datatype to internal (index-suitable) value */ datatype = mt_import(datatype); /* * compute the # of elements for which space is available */ /* try space between heap and partition */ gap_length = (size_t)(ma_partition - ma_hp); /* if (gap_length > 0)*/ if (ma_partition > ma_hp) nelem_gap = ma_nelem(ma_hp, (ulongi)gap_length, datatype); else nelem_gap = 0; /* try heap fragments */ nelem_frag = ma_max_heap_frag_nelem(datatype, nelem_gap); /* success */ return max(nelem_gap, nelem_frag); } /* ------------------------------------------------------------------------- */ /* * Return the maximum number of datatype elements that can currently * be allocated in the heap, in a single allocation request, * honoring the partition defined at initialization. * * This routine does check the stack. Therefore, whether or not the stack * has overgrown the partition, the answer returned by this routine * will be correct, i.e., there will be at least the memory available * that this routine indicates. * * Note that this might not be the largest piece of memory available; * the space between the heap and the stack may be larger. */ /* ------------------------------------------------------------------------- */ public Integer MA_inquire_heap_check_stack(Integer datatype) { size_t gap_length; /* # of bytes between heap and partition */ Integer nelem_gap; /* max elements containable in gap */ Integer nelem_frag; /* max elements containable in any frag */ #ifdef STATS ma_stats.calls[(int)FID_MA_inquire_heap_check_stack]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return DONTCARE; #endif /* VERIFY */ /* verify initialization */ if (!ma_initialized) { (void)sprintf(ma_ebuf, "MA not yet initialized"); ma_error(EL_Nonfatal, ET_External, "MA_inquire_heap_check_stack", ma_ebuf); return (Integer)0; } /* verify datatype */ if (!mt_valid(datatype)) { (void)sprintf(ma_ebuf, "invalid datatype: %ld", (size_t)datatype); ma_error(EL_Fatal, ET_External, "MA_inquire_heap_check_stack", ma_ebuf); return DONTCARE; } /* convert datatype to internal (index-suitable) value */ datatype = mt_import(datatype); /* * compute the # of elements for which space is available */ /* try space between heap and partition or heap and stack */ gap_length = min((size_t)(ma_partition - ma_hp), (size_t)(ma_sp - ma_hp)); if (gap_length > 0) nelem_gap = ma_nelem(ma_hp, (ulongi)gap_length, datatype); else nelem_gap = 0; /* try heap fragments */ nelem_frag = ma_max_heap_frag_nelem(datatype, nelem_gap); /* success */ return max(nelem_gap, nelem_frag); } /* ------------------------------------------------------------------------- */ /* * Return the maximum number of datatype elements that can currently * be allocated in the heap, in a single allocation request, * ignoring the partition defined at initialization. * * This routine does check the stack. Therefore, whether or not the stack * has overgrown the partition, the answer returned by this routine * will be correct, i.e., there will be at least the memory available * that this routine indicates. * * Note that this will be the largest piece of memory available. */ /* ------------------------------------------------------------------------- */ public Integer MA_inquire_heap_no_partition(Integer datatype) { size_t gap_length; /* # of bytes between heap and partition */ Integer nelem_gap; /* max elements containable in gap */ Integer nelem_frag; /* max elements containable in any frag */ #ifdef STATS ma_stats.calls[(int)FID_MA_inquire_heap_no_partition]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return DONTCARE; #endif /* VERIFY */ /* verify initialization */ if (!ma_initialized) { (void)sprintf(ma_ebuf, "MA not yet initialized"); ma_error(EL_Nonfatal, ET_External, "MA_inquire_heap_no_partition", ma_ebuf); return (Integer)0; } /* verify datatype */ if (!mt_valid(datatype)) { (void)sprintf(ma_ebuf, "invalid datatype: %ld", (size_t)datatype); ma_error(EL_Fatal, ET_External, "MA_inquire_heap_no_partition", ma_ebuf); return DONTCARE; } /* convert datatype to internal (index-suitable) value */ datatype = mt_import(datatype); /* * compute the # of elements for which space is available */ /* try space between heap and stack */ gap_length = (size_t)(ma_sp - ma_hp); /*if (ma_sp > ma_hp)*/ if (gap_length > 0) nelem_gap = ma_nelem(ma_hp, (ulongi)gap_length, datatype); else nelem_gap = 0; /* try heap fragments */ nelem_frag = ma_max_heap_frag_nelem(datatype, nelem_gap); /* success */ return max(nelem_gap, nelem_frag); } /* ------------------------------------------------------------------------- */ /* * Return the maximum number of datatype elements that can currently * be allocated in the stack, in a single allocation request, * honoring the partition defined at initialization. * * This routine does not check the heap. Therefore, if the heap * has overgrown the partition, the answer returned by this routine * might be incorrect, i.e., there might be less memory available * than this routine indicates. */ /* ------------------------------------------------------------------------- */ public Integer MA_inquire_stack(Integer datatype) { size_t gap_length; /* # of bytes between partition and stack */ Integer nelem_gap; /* max elements containable in gap */ #ifdef STATS ma_stats.calls[(int)FID_MA_inquire_stack]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return DONTCARE; #endif /* VERIFY */ /* verify initialization */ if (!ma_initialized) { (void)sprintf(ma_ebuf, "MA not yet initialized"); ma_error(EL_Nonfatal, ET_External, "MA_inquire_stack", ma_ebuf); return (Integer)0; } /* verify datatype */ if (!mt_valid(datatype)) { (void)sprintf(ma_ebuf, "invalid datatype: %ld", (size_t)datatype); ma_error(EL_Fatal, ET_External, "MA_inquire_stack", ma_ebuf); return DONTCARE; } /* convert datatype to internal (index-suitable) value */ datatype = mt_import(datatype); /* * compute the # of elements for which space is available */ /* try space between partition and stack */ gap_length = (size_t)(ma_sp - ma_partition); /*if (ma_sp > ma_partition)*/ if (gap_length > 0) nelem_gap = ma_nelem(ma_partition, (ulongi)gap_length, datatype); else nelem_gap = 0; /* success */ return nelem_gap; } /* ------------------------------------------------------------------------- */ /* * Return the maximum number of datatype elements that can currently * be allocated in the stack, in a single allocation request, * honoring the partition defined at initialization. * * This routine does check the heap. Therefore, whether or not the heap * has overgrown the partition, the answer returned by this routine * will be correct, i.e., there will be at least the memory available * that this routine indicates. * * Note that this might not be the largest piece of memory available; * the space between the heap and the stack may be larger. */ /* ------------------------------------------------------------------------- */ public Integer MA_inquire_stack_check_heap(Integer datatype) { size_t gap_length; /* # of bytes between partition and stack */ Integer nelem_gap; /* max elements containable in gap */ #ifdef STATS ma_stats.calls[(int)FID_MA_inquire_stack_check_heap]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return DONTCARE; #endif /* VERIFY */ /* verify initialization */ if (!ma_initialized) { (void)sprintf(ma_ebuf, "MA not yet initialized"); ma_error(EL_Nonfatal, ET_External, "MA_inquire_stack_check_heap", ma_ebuf); return (Integer)0; } /* verify datatype */ if (!mt_valid(datatype)) { (void)sprintf(ma_ebuf, "invalid datatype: %ld", (size_t)datatype); ma_error(EL_Fatal, ET_External, "MA_inquire_stack_check_heap", ma_ebuf); return DONTCARE; } /* convert datatype to internal (index-suitable) value */ datatype = mt_import(datatype); /* * compute the # of elements for which space is available */ /* try space between partition and stack or heap and stack */ gap_length = min((size_t)(ma_sp - ma_partition), (size_t)(ma_sp - ma_hp)); if (gap_length > 0) nelem_gap = ma_nelem(ma_partition, (ulongi)gap_length, datatype); else nelem_gap = 0; /* success */ return nelem_gap; } /* ------------------------------------------------------------------------- */ /* * Return the maximum number of datatype elements that can currently * be allocated in the stack, in a single allocation request, * ignoring the partition defined at initialization. * * This routine does check the heap. Therefore, whether or not the heap * has overgrown the partition, the answer returned by this routine * will be correct, i.e., there will be at least the memory available * that this routine indicates. * * Note that this might not be the largest piece of memory available; * the heap may contain deallocated blocks that are larger. * * This routine is equivalent to MA_inquire_avail. */ /* ------------------------------------------------------------------------- */ public Integer MA_inquire_stack_no_partition(Integer datatype) { size_t gap_length; /* # of bytes between heap and partition */ Integer nelem_gap; /* max elements containable in gap */ #ifdef STATS ma_stats.calls[(int)FID_MA_inquire_stack_no_partition]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return DONTCARE; #endif /* VERIFY */ /* verify initialization */ if (!ma_initialized) { (void)sprintf(ma_ebuf, "MA not yet initialized"); ma_error(EL_Nonfatal, ET_External, "MA_inquire_stack_no_partition", ma_ebuf); return (Integer)0; } /* verify datatype */ if (!mt_valid(datatype)) { (void)sprintf(ma_ebuf, "invalid datatype: %ld", (size_t)datatype); ma_error(EL_Fatal, ET_External, "MA_inquire_stack_no_partition", ma_ebuf); return DONTCARE; } /* convert datatype to internal (index-suitable) value */ datatype = mt_import(datatype); /* * compute the # of elements for which space is available */ /* try space between heap and stack */ gap_length = (size_t)(ma_sp - ma_hp); /*if (ma_sp > ma_hp)*/ if (gap_length > 0) nelem_gap = ma_nelem(ma_hp, (ulongi)gap_length, datatype); else nelem_gap = 0; /* success */ return nelem_gap; } /* ------------------------------------------------------------------------- */ /* * Deallocate the given stack block, which must be the one most recently * allocated. * * Return MA_TRUE upon success, or MA_FALSE upon failure. */ /* ------------------------------------------------------------------------- */ public Boolean MA_pop_stack(Integer memhandle) /* the block to deallocate */ { AD *ad; /* AD for memhandle */ #ifdef STATS ma_stats.calls[(int)FID_MA_pop_stack]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return MA_FALSE; #endif /* VERIFY */ /* verify memhandle and convert to AD */ if (!mh2ad(memhandle, &ad, BL_StackTop, "MA_pop_stack")) return MA_FALSE; if (ma_trace) (void)printf("MA: popping '%s'\n", ad->name); /* delete block from used list */ if (list_delete(ad, &ma_sused) != ad) { (void)sprintf(ma_ebuf, "memhandle %ld (name: '%s') not on stack used list", (size_t)memhandle, ad->name); ma_error(EL_Nonfatal, ET_Internal, "MA_pop_stack", ma_ebuf); return MA_FALSE; } /* pop block from stack */ ma_sp += ad->nbytes; #ifdef STATS ma_stats.sblocks--; ma_stats.sbytes -= ad->nbytes; #endif /* STATS */ /* free memhandle */ ma_table_deallocate(memhandle); /* success */ return MA_TRUE; } /* ------------------------------------------------------------------------- */ /* * Print usage statistics. */ /* ------------------------------------------------------------------------- */ public void MA_print_stats(Boolean printroutines) { #ifdef STATS int i; /* loop index */ #ifdef STATS ma_stats.calls[(int)FID_MA_print_stats]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return; #endif /* VERIFY */ (void)printf("MA usage statistics:\n"); (void)printf("\n\tallocation statistics:\n"); (void)printf("\t\t\t\t\t heap\t stack\n"); (void)printf("\t\t\t\t\t ----\t -----\n"); (void)printf("\tcurrent number of blocks\t%10lu\t%10lu\n", ma_stats.hblocks, ma_stats.sblocks); (void)printf("\tmaximum number of blocks\t%10lu\t%10lu\n", ma_stats.hblocks_max, ma_stats.sblocks_max); (void)printf("\tcurrent total bytes\t\t%10lu\t%10lu\n", ma_stats.hbytes, ma_stats.sbytes); (void)printf("\tmaximum total bytes\t\t%10lu\t%10lu\n", ma_stats.hbytes_max, ma_stats.sbytes_max); (void)printf("\tmaximum total K-bytes\t\t%10lu\t%10lu\n", ((ma_stats.hbytes_max+999)/1000), ((ma_stats.sbytes_max+999)/1000)); (void)printf("\tmaximum total M-bytes\t\t%10lu\t%10lu\n", ((ma_stats.hbytes_max+999999)/1000000), ((ma_stats.sbytes_max+999999)/1000000)); if (printroutines) { (void)printf("\n\tcalls per routine:\n"); for (i = 0; i < NUMROUTINES; i++) (void)printf("\t\t%10lu %s\n", ma_stats.calls[i], ma_routines[i]); } #else (void)sprintf(ma_ebuf, "unavailable; recompile MA with -DSTATS"); ma_error(EL_Nonfatal, ET_External, "MA_print_stats", ma_ebuf); #endif /* STATS */ } /* ------------------------------------------------------------------------- */ /* * Convenience function that combines MA_push_stack and MA_get_index. */ /* ------------------------------------------------------------------------- */ public Boolean MA_push_get( Integer datatype, /* of elements in this block */ Integer nelem, /* # of elements in this block */ const char *name, /* assigned to this block by client */ Integer *memhandle, /* RETURN: handle for this block */ MA_AccessIndex *index /* RETURN: index for this block */) { #ifdef STATS ma_stats.calls[(int)FID_MA_push_get]++; #endif /* STATS */ if (MA_push_stack(datatype, nelem, name, memhandle)) /* MA_push_stack succeeded; try MA_get_index */ return MA_get_index(*memhandle, index); else /* MA_push_stack failed */ return MA_FALSE; } /* ------------------------------------------------------------------------- */ /* * Allocate a stack block big enough to hold nelem elements * of the given datatype. * * Return MA_TRUE upon success, or MA_FALSE upon failure. */ /* ------------------------------------------------------------------------- */ public Boolean MA_push_stack( Integer datatype, /* of elements in this block */ Integer nelem, /* # of elements in this block */ const char *name, /* assigned to this block by client */ Integer *memhandle /* RETURN: handle for this block */) { AR ar; /* allocation request */ AD *ad; /* AD for newly allocated block */ Pointer client_space; /* location of client_space */ ulongi nbytes; /* length of block for ar */ Pointer new_sp; /* new ma_sp */ #ifdef STATS ma_stats.calls[(int)FID_MA_push_stack]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return MA_FALSE; #endif /* VERIFY */ if (ma_trace) (void)printf("MA: pushing '%s' (%d)\n", name, (int)nelem); /* verify initialization */ if (!ma_initialized) { (void)sprintf(ma_ebuf, "block '%s', MA not yet initialized", name); ma_error(EL_Nonfatal, ET_External, "MA_push_stack", ma_ebuf); return MA_FALSE; } /* verify datatype */ if (!mt_valid(datatype)) { (void)sprintf(ma_ebuf, "block '%s', invalid datatype: %ld", name, (size_t)datatype); ma_error(EL_Nonfatal, ET_External, "MA_push_stack", ma_ebuf); return MA_FALSE; } /* verify nelem */ if (nelem < 0) { (void)sprintf(ma_ebuf, "block '%s', invalid nelem: %ld", name, (size_t)nelem); ma_error(EL_Nonfatal, ET_External, "MA_push_stack", ma_ebuf); return MA_FALSE; } /* convert datatype to internal (index-suitable) value */ datatype = mt_import(datatype); /* * attempt to allocate space */ ar.datatype = datatype; ar.nelem = nelem; balloc_before(&ar, ma_sp, &client_space, &nbytes); new_sp = ma_sp - nbytes; /* if (new_sp < ma_hp) */ if (((ulongi)(ma_sp - ma_hp)) < nbytes) { (void)sprintf(ma_ebuf, "block '%s', not enough space to allocate %lu bytes", name, nbytes); ma_error(EL_Nonfatal, ET_External, "MA_push_stack", ma_ebuf); return MA_FALSE; } else { ad = (AD *)new_sp; } /* * space has been allocated */ /* initialize the AD */ ad->datatype = datatype; ad->nelem = nelem; str_ncopy(ad->name, (char*)name, MA_NAMESIZE); ad->client_space = client_space; ad->nbytes = nbytes; list_insert(ad, &ma_sused); ad->checksum = checksum(ad); /* set the guards */ guard_set(ad); #ifdef DEBUG debug_ad_print(ad); #endif /* DEBUG */ /* update ma_sp */ ma_sp = new_sp; #ifdef STATS ma_stats.sblocks++; ma_stats.sblocks_max = max(ma_stats.sblocks, ma_stats.sblocks_max); ma_stats.sbytes += ad->nbytes; ma_stats.sbytes_max = max(ma_stats.sbytes, ma_stats.sbytes_max); #endif /* STATS */ /* convert AD to memhandle */ if ((*memhandle = ma_table_allocate((TableData)ad)) == TABLE_HANDLE_NONE) /* failure */ return MA_FALSE; else /* success */ return MA_TRUE; } /* ------------------------------------------------------------------------- */ /* * Set the ma_auto_verify flag to value and return its previous value. */ /* ------------------------------------------------------------------------- */ public Boolean MA_set_auto_verify(Boolean value /* to set flag to */) { Boolean old_value; /* of flag */ #ifdef STATS ma_stats.calls[(int)FID_MA_set_auto_verify]++; #endif /* STATS */ old_value = ma_auto_verify; ma_auto_verify = value; return old_value; } /* ------------------------------------------------------------------------- */ /* * Set the ma_error_print flag to value and return its previous value. */ /* ------------------------------------------------------------------------- */ public Boolean MA_set_error_print(Boolean value /* to set flag to */) { Boolean old_value; /* of flag */ #ifdef STATS ma_stats.calls[(int)FID_MA_set_error_print]++; #endif /* STATS */ old_value = ma_error_print; ma_error_print = value; return old_value; } /* ------------------------------------------------------------------------- */ /* * Set the ma_hard_fail flag to value and return its previous value. */ /* ------------------------------------------------------------------------- */ public Boolean MA_set_hard_fail( Boolean value /* to set flag to */) { Boolean old_value; /* of flag */ #ifdef STATS ma_stats.calls[(int)FID_MA_set_hard_fail]++; #endif /* STATS */ old_value = ma_hard_fail; ma_hard_fail = value; return old_value; } /* ------------------------------------------------------------------------- */ /* * Set the requested alignment. * * Return MA_TRUE upon success, or MA_FALSE upon failure. */ /* ------------------------------------------------------------------------- */ public Boolean MA_set_numalign(Integer value) { #ifdef STATS ma_stats.calls[(int)FID_MA_set_numalign]++; #endif /* STATS */ if ((value < 0) || (value > 30)) { (void)sprintf(ma_ebuf, "invalid alignment: %ld", (size_t)value); ma_error(EL_Nonfatal, ET_External, "MA_set_numalign", ma_ebuf); return MA_FALSE; } ma_numalign = value; return MA_TRUE; } /* ------------------------------------------------------------------------- */ /* * Return the number of elements of datatype2 required to contain * nelem1 elements of datatype1. */ /* ------------------------------------------------------------------------- */ public Integer MA_sizeof( Integer datatype1, /* of source elements */ Integer nelem1, /* # of source elements */ Integer datatype2 /* of target elements */) { ulongi source_bytes; /* nelem1 * ma_sizeof[datatype1] */ int ceiling; /* 1 iff ceiling alters result */ #ifdef STATS ma_stats.calls[(int)FID_MA_sizeof]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return DONTCARE; #endif /* VERIFY */ /* preinitialize if necessary */ ma_preinitialize("MA_sizeof"); /* verify datatype1 */ if (!mt_valid(datatype1)) { (void)sprintf(ma_ebuf, "invalid datatype: %ld", (size_t)datatype1); ma_error(EL_Fatal, ET_External, "MA_sizeof", ma_ebuf); return DONTCARE; } /* verify nelem1 */ if (nelem1 < 0) { (void)sprintf(ma_ebuf, "invalid nelem: %ld", (size_t)nelem1); ma_error(EL_Fatal, ET_External, "MA_sizeof", ma_ebuf); return DONTCARE; } /* verify datatype2 */ if (!mt_valid(datatype2)) { (void)sprintf(ma_ebuf, "invalid datatype: %ld", (size_t)datatype2); ma_error(EL_Fatal, ET_External, "MA_sizeof", ma_ebuf); return DONTCARE; } /* convert datatype1 to internal (index-suitable) value */ datatype1 = mt_import(datatype1); /* convert datatype2 to internal (index-suitable) value */ datatype2 = mt_import(datatype2); /* compute and return the result */ source_bytes = nelem1 * ma_sizeof[datatype1]; ceiling = (source_bytes % ma_sizeof[datatype2]) ? 1 : 0; return (Integer)((source_bytes / ma_sizeof[datatype2]) + ceiling); } /* ------------------------------------------------------------------------- */ /* * Return the number of elements of datatype required to contain * the worst case number of bytes of overhead for any block. */ /* ------------------------------------------------------------------------- */ public Integer MA_sizeof_overhead(Integer datatype) { int overhead_bytes; /* max bytes of overhead for any block */ int ceiling; /* 1 iff ceiling alters result */ int max_sizeof; /* max over i of ma_sizeof[i] */ int biggest_datatype=0; /* corresponds to max_sizeof */ int i; /* loop index */ #ifdef STATS ma_stats.calls[(int)FID_MA_sizeof_overhead]++; #endif /* STATS */ #ifdef VERIFY if (ma_auto_verify && !MA_verify_allocator_stuff()) return DONTCARE; #endif /* VERIFY */ /* preinitialize if necessary */ ma_preinitialize("MA_sizeof_overhead"); /* verify datatype */ if (!mt_valid(datatype)) { (void)sprintf(ma_ebuf, "invalid datatype: %ld", (size_t)datatype); ma_error(EL_Fatal, ET_External, "MA_sizeof_overhead", ma_ebuf); return DONTCARE; } /* convert datatype to internal (index-suitable) value */ datatype = mt_import(datatype); /* compute and return the result */ for (max_sizeof = 0, i = 0; i < MT_NUMTYPES; i++) if (ma_sizeof[i] > max_sizeof) { max_sizeof = ma_sizeof[i]; biggest_datatype = i; } overhead_bytes = max_block_overhead(biggest_datatype); ceiling = (overhead_bytes % ma_sizeof[datatype]) ? 1 : 0; return (Integer)((overhead_bytes / ma_sizeof[datatype]) + ceiling); } /* ------------------------------------------------------------------------- */ /* * Print debugging information about all blocks currently in use * on the heap or the stack. */ /* ------------------------------------------------------------------------- */ public void MA_summarize_allocated_blocks() { /* C indices are 0-based */ MAi_summarize_allocated_blocks(0); } /* ------------------------------------------------------------------------- */ /* * Control tracing of allocation and deallocation operations. */ /* ------------------------------------------------------------------------- */ public void MA_trace(Boolean value) { ma_trace = value; } /* ------------------------------------------------------------------------- */ /* * Sanity check the internal state of MA and print the results. * * Return MA_TRUE upon success, or MA_FALSE upon failure. */ /* ------------------------------------------------------------------------- */ public Boolean MA_verify_allocator_stuff() { #ifdef VERIFY char *preamble; /* printed before block error messages */ int heap_blocks; int bad_heap_blocks; int bad_heap_checksums; int bad_heap_lguards; int bad_heap_rguards; int stack_blocks; int bad_stack_blocks; int bad_stack_checksums; int bad_stack_lguards; int bad_stack_rguards; #ifdef STATS ma_stats.calls[(int)FID_MA_verify_allocator_stuff]++; #endif /* STATS */ preamble = "MA_verify_allocator_stuff: starting scan ...\n"; /* check each block on the heap used list */ list_verify(ma_hused, "heap", preamble, &heap_blocks, &bad_heap_blocks, &bad_heap_checksums, &bad_heap_lguards, &bad_heap_rguards); if (bad_heap_blocks > 0) /* only print preamble once */ preamble = (char *)NULL; /* check each block on the stack used list */ list_verify(ma_sused, "stack", preamble, &stack_blocks, &bad_stack_blocks, &bad_stack_checksums, &bad_stack_lguards, &bad_stack_rguards); if ((bad_heap_blocks > 0) || (bad_stack_blocks > 0)) { Boolean old_ma_error_print; /* print postamble */ (void)printf("MA_verify_allocator_stuff: scan completed\n"); /* construct a summary of the results */ (void)sprintf(ma_ebuf, "\n\t\t\t\theap\tstack\n\t\t\t\t----\t-----\n\tchecksum errors\t\t%4d\t%5d\n\tleft signature errors\t%4d\t%5d\n\tright signature errors\t%4d\t%5d\n\ttotal bad blocks\t%4d\t%5d\n\ttotal blocks\t\t%4d\t%5d", bad_heap_checksums, bad_stack_checksums, bad_heap_lguards, bad_stack_lguards, bad_heap_rguards, bad_stack_rguards, bad_heap_blocks, bad_stack_blocks, heap_blocks, stack_blocks); /* print the summary on stderr */ old_ma_error_print = ma_error_print; ma_error_print = MA_TRUE; ma_error(EL_Nonfatal, ET_External, "MA_verify_allocator_stuff", ma_ebuf); ma_error_print = old_ma_error_print; /* problems were found */ return MA_FALSE; } else /* no problems found */ return MA_TRUE; #else #ifdef STATS ma_stats.calls[(int)FID_MA_verify_allocator_stuff]++; #endif /* STATS */ (void)sprintf(ma_ebuf, "unavailable; recompile MA with -DVERIFY"); ma_error(EL_Nonfatal, ET_External, "MA_verify_allocator_stuff", ma_ebuf); return MA_FALSE; #endif /* VERIFY */ } ga-5.9.2/ma/ma.h000066400000000000000000000065621500715745200133000ustar00rootroot00000000000000/** @file * Private header file containing symbolic constants and type declarations * for internal C routines. * * This file should only be included by internal C files. */ #ifndef _ma_h #define _ma_h #include "macdecls.h" /** ** function types **/ extern Boolean MAi_inform_base(Integer datatype, Pointer address1, Pointer address2); extern void MAi_summarize_allocated_blocks(int index_base); #define f2c_alloc_get_ F77_FUNC_(f2c_alloc_get,F2C_ALLOC_GET) #define f2c_allocate_heap_ F77_FUNC_(f2c_allocate_heap,F2C_ALLOCATE_HEAP) #define f2c_chop_stack_ F77_FUNC_(f2c_chop_stack,F2C_CHOP_STACK) #define f2c_free_heap_ F77_FUNC_(f2c_free_heap,F2C_FREE_HEAP) #define f2c_free_heap_piece_ F77_FUNC_(f2c_free_heap_piece,F2C_FREE_HEAP_PIECE) #define f2c_get_index_ F77_FUNC_(f2c_get_index,F2C_GET_INDEX) #define f2c_get_next_memhandle_ F77_FUNC_(f2c_get_next_memhandle,F2C_GET_NEXT_MEMHANDLE) #define f2c_get_numalign_ F77_FUNC_(f2c_get_numalign,F2C_GET_NUMALIGN) #define f2c_inform_base_ F77_FUNC_(f2c_inform_base,F2C_INFORM_BASE) #define f2c_inform_base_fcd_ F77_FUNC_(f2c_inform_base_fcd,F2C_INFORM_BASE_FCD) #define f2c_init_ F77_FUNC_(f2c_init,F2C_INIT) #define f2c_init_memhandle_iterator_ F77_FUNC_(f2c_init_memhandle_iterator,F2C_INIT_MEMHANDLE_ITERATOR) #define f2c_initialized_ F77_FUNC_(f2c_initialized,F2C_INITIALIZED) #define f2c_inquire_avail_ F77_FUNC_(f2c_inquire_avail,F2C_INQUIRE_AVAIL) #define f2c_inquire_heap_ F77_FUNC_(f2c_inquire_heap,F2C_INQUIRE_HEAP) #define f2c_inquire_stack_ F77_FUNC_(f2c_inquire_stack,F2C_INQUIRE_STACK) #define f2c_inquire_heap_check_stack_ F77_FUNC_(f2c_inquire_heap_check_stack,F2C_INQUIRE_HEAP_CHECK_STACK) #define f2c_inquire_heap_no_partition_ F77_FUNC_(f2c_inquire_heap_no_partition,F2C_INQUIRE_HEAP_NO_PARTITION) #define f2c_inquire_stack_check_heap_ F77_FUNC_(f2c_inquire_stack_check_heap,F2C_INQUIRE_STACK_CHECK_HEAP) #define f2c_inquire_stack_no_partition_ F77_FUNC_(f2c_inquire_stack_no_partition,F2C_INQUIRE_STACK_NO_PARTITION) #define f2c_pop_stack_ F77_FUNC_(f2c_pop_stack,F2C_POP_STACK) #define f2c_print_stats_ F77_FUNC_(f2c_print_stats,F2C_PRINT_STATS) #define f2c_push_get_ F77_FUNC_(f2c_push_get,F2C_PUSH_GET) #define f2c_push_stack_ F77_FUNC_(f2c_push_stack,F2C_PUSH_STACK) #define f2c_set_auto_verify_ F77_FUNC_(f2c_set_auto_verify,F2C_SET_AUTO_VERIFY) #define f2c_set_error_print_ F77_FUNC_(f2c_set_error_print,F2C_SET_ERROR_PRINT) #define f2c_set_hard_fail_ F77_FUNC_(f2c_set_hard_fail,F2C_SET_HARD_FAIL) #define f2c_set_numalign_ F77_FUNC_(f2c_set_numalign,F2C_SET_NUMALIGN) #define f2c_sizeof_ F77_FUNC_(f2c_sizeof,F2C_SIZEOF) #define f2c_sizeof_overhead_ F77_FUNC_(f2c_sizeof_overhead,F2C_SIZEOF_OVERHEAD) #define f2c_summarize_allocated_blocks_ F77_FUNC_(f2c_summarize_allocated_blocks,F2C_SUMMARIZE_ALLOCATED_BLOCKS) #define f2c_trace_ F77_FUNC_(f2c_trace,F2C_TRACE) #define f2c_verify_allocator_stuff_ F77_FUNC_(f2c_verify_allocator_stuff,F2C_VERIFY_ALLOCATOR_STUFF) #define ma_set_sizes_ F77_FUNC_(ma_set_sizes,MA_SET_SIZES) #endif /* _ma_h */ ga-5.9.2/ma/macdecls.h000066400000000000000000000127741500715745200144600ustar00rootroot00000000000000/** @file * Public header file for a portable dynamic memory allocator. * * This file may be included by internal and external C files. */ #ifndef _macdecls_h #define _macdecls_h #ifdef __cplusplus extern "C" { #endif #include "macommon.h" #include "matypes.h" /** ** constants **/ /* datatypes */ #define MT_CHAR MT_C_CHAR /**< char */ #define MT_INT MT_C_INT /**< int */ #define MT_LONGINT MT_C_LONGINT /**< long int */ #define MT_LONGLONG MT_C_LONGLONG /**< long long */ #define MT_REAL MT_C_FLOAT /**< float */ #define MT_DBL MT_C_DBL /**< double */ #define MT_LDBL MT_C_LDBL /**< long double */ #define MT_SCPL MT_C_SCPL /**< single precision complex */ #define MT_DCPL MT_C_DCPL /**< double precision complex */ #define MT_LDCPL MT_C_LDCPL /**< long double precision complex */ #define MT_C_FIRST MT_CHAR /**< first type */ #define MT_C_LAST MT_LDCPL /**< last type */ /** ** function types **/ extern Boolean MA_alloc_get( Integer datatype, /**< of elements in this block */ Integer nelem, /**< # of elements in this block */ const char *name, /**< assigned to this block by client */ Integer *memhandle, /**< RETURN: handle for this block */ MA_AccessIndex *index /**< RETURN: index for this block */ ); extern Boolean MA_allocate_heap( Integer datatype, /**< of elements in this block */ Integer nelem, /**< # of elements in this block */ const char *name, /**< assigned to this block by client */ Integer *memhandle /**< RETURN: handle for this block */ ); extern Boolean MA_chop_stack(Integer memhandle); extern Boolean MA_free_heap(Integer memhandle); extern Boolean MA_free_heap_piece( Integer memhandle, /**< the block to deallocate a piece of */ Integer nelem /**< # of elements to deallocate */); extern Boolean MA_get_index( Integer memhandle, /**< block to get index for */ MA_AccessIndex *index /**< RETURN: base index */); extern Pointer MA_get_mbase(Integer datatype); /**< to get base address of */ extern Boolean MA_get_next_memhandle( Integer *ithandle, /**< handle for this iterator */ Integer *memhandle /**< RETURN: handle for the next block */); extern Boolean MA_get_numalign(Integer *value); extern Boolean MA_get_pointer( Integer memhandle, /**< block to get pointer for */ void *pointer /**< JN: void** = void* */ ); extern Boolean MA_init( Integer datatype, /**< for computing storage requirement */ Integer nominal_stack, /**< # of datatype elements desired for stack */ Integer nominal_heap /**< # of datatype elements desired for heap */); extern Boolean MA_initialized(); extern Boolean MA_init_memhandle_iterator( Integer *ithandle); extern Integer MA_inquire_avail(Integer datatype); extern Integer MA_inquire_heap(Integer datatype); extern Integer MA_inquire_heap_check_stack(Integer datatype); extern Integer MA_inquire_heap_no_partition(Integer datatype); extern Integer MA_inquire_stack(Integer datatype); extern Integer MA_inquire_stack_check_heap(Integer datatype); extern Integer MA_inquire_stack_no_partition(Integer datatype); extern Boolean MA_pop_stack(Integer memhandle); extern void MA_print_stats(Boolean printroutines); extern Boolean MA_push_get( Integer datatype, /**< of elements in this block */ Integer nelem, /**< # of elements in this block */ const char *name, /**< assigned to this block by client */ Integer *memhandle, /**< RETURN: handle for this block */ MA_AccessIndex *index /**< RETURN: index for this block */); extern Boolean MA_push_stack( Integer datatype, /**< of elements in this block */ Integer nelem, /**< # of elements in this block */ const char *name, /**< assigned to this block by client */ Integer *memhandle /**< RETURN: handle for this block */); extern Boolean MA_set_auto_verify(Boolean value /* to set flag to */); extern Boolean MA_set_error_print(Boolean value /* to set flag to */); extern Boolean MA_set_hard_fail( Boolean value /* to set flag to */); extern Boolean MA_set_numalign(Integer value); extern Integer MA_sizeof( Integer datatype1, /**< of source elements */ Integer nelem1, /**< # of source elements */ Integer datatype2 /**< of target elements */); extern Integer MA_sizeof_overhead(Integer datatype); extern void MA_summarize_allocated_blocks(); extern void MA_trace(Boolean value); extern Boolean MA_verify_allocator_stuff(); extern void MA_set_error_callback(void(*func)()); extern void ma_set_error_callback(); /** ** variables **/ /* base arrays for the C datatypes */ extern char ma_cb_char[]; /**< MT_C_CHAR */ extern int ma_cb_int[]; /**< MT_C_INT */ extern long ma_cb_long[]; /**< MT_C_LONGINT */ extern long long ma_cb_longlong[]; /**< MT_C_LONGLONG */ extern float ma_cb_float[]; /**< MT_C_FLOAT */ extern double ma_cb_dbl[]; /**< MT_C_DBL */ extern MA_LongDouble ma_cb_ldbl[]; /**< MT_C_LDBL */ extern MA_SingleComplex ma_cb_scpl[]; /**< MT_C_SCPL */ extern MA_DoubleComplex ma_cb_dcpl[]; /**< MT_C_DCPL */ extern MA_LongDoubleComplex ma_cb_ldcpl[]; /**< MT_C_LDCPL */ #ifdef __cplusplus } #endif #endif /* _macdecls_h */ ga-5.9.2/ma/macommon.h000066400000000000000000000016671500715745200145120ustar00rootroot00000000000000#ifndef _macommon_h #define _macommon_h #define MA_FALSE 0 #define MA_TRUE 1 #define MA_DEFAULT_SPACE (-1) #define MA_NAMESIZE 32 #define MT_BASE 1000 #define MT_C_CHAR (MT_BASE + 0) #define MT_C_INT (MT_BASE + 1) #define MT_C_LONGINT (MT_BASE + 2) #define MT_C_FLOAT (MT_BASE + 3) #define MT_C_DBL (MT_BASE + 4) #define MT_C_LDBL (MT_BASE + 5) #define MT_C_SCPL (MT_BASE + 6) #define MT_C_DCPL (MT_BASE + 7) #define MT_C_LDCPL (MT_BASE + 8) #define MT_F_BYTE (MT_BASE + 9) #define MT_F_INT (MT_BASE + 10) #define MT_F_LOG (MT_BASE + 11) #define MT_F_REAL (MT_BASE + 12) #define MT_F_DBL (MT_BASE + 13) #define MT_F_SCPL (MT_BASE + 14) #define MT_F_DCPL (MT_BASE + 15) #define MT_C_LONGLONG (MT_BASE + 16) #define MT_FIRST MT_C_CHAR #define MT_LAST MT_C_LONGLONG #define MT_NUMTYPES (MT_LAST - MT_FIRST + 1) #endif /* _macommon_h */ ga-5.9.2/ma/maf.F000066400000000000000000000354161500715745200134040ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c c $Id: maf.F,v 1.14 2003-07-10 19:19:28 d3h325 Exp $ c c c FORTRAN routines for a portable dynamic memory allocator. c #define MAF_INTERNAL cc cc private routines cc c --------------------------------------------------------------- c c --------------------------------------------------------------- c integer function ma_set_sizes () implicit none #include "maf2c.fh" #include "mafdecls.fh" ma_set_sizes = 0 if (f2c_inform_base(MT_BYTE, byte_mb(1), byte_mb(2)) .eq. $ MA_FALSE) return if (f2c_inform_base(MT_INT, int_mb(1), int_mb(2)) .eq. $ MA_FALSE) return if (f2c_inform_base(MT_LOG, log_mb(1), log_mb(2)) .eq. $ MA_FALSE) return if (f2c_inform_base(MT_REAL, real_mb(1), real_mb(2)) .eq. $ MA_FALSE) return if (f2c_inform_base(MT_DBL, dbl_mb(1), dbl_mb(2)) .eq. $ MA_FALSE) return if (f2c_inform_base(MT_SCPL, scpl_mb(1), scpl_mb(2)) .eq. $ MA_FALSE) return if (f2c_inform_base(MT_DCPL, dcpl_mb(1), dcpl_mb(2)) .eq. $ MA_FALSE) return ma_set_sizes = 1 return end cc cc public routines cc c In general, each routine simply calls its corresponding f2c_ C c wrapper routine, which performs any necessary argument munging c and then calls the corresponding C routine. c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_alloc_get (datatype, nelem, name, memhandle, $ index) implicit none #include "mafdecls.fh" integer datatype integer nelem character*(*) name integer memhandle MA_ACCESS_INDEX_TYPE index #include "maf2c.fh" if (f2c_alloc_get(datatype, nelem, name, memhandle, index) .eq. $ MA_TRUE) then MA_alloc_get = .true. else MA_alloc_get = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_allocate_heap (datatype, nelem, name, $ memhandle) implicit none integer datatype integer nelem character*(*) name integer memhandle #include "maf2c.fh" if (f2c_allocate_heap(datatype, nelem, name, memhandle) .eq. $ MA_TRUE) then MA_allocate_heap = .true. else MA_allocate_heap = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_chop_stack (memhandle) implicit none integer memhandle #include "maf2c.fh" if (f2c_chop_stack(memhandle) .eq. MA_TRUE) then MA_chop_stack = .true. else MA_chop_stack = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_free_heap (memhandle) implicit none integer memhandle #include "maf2c.fh" if (f2c_free_heap(memhandle) .eq. MA_TRUE) then MA_free_heap = .true. else MA_free_heap = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_free_heap_piece (memhandle, nelem) implicit none integer memhandle integer nelem #include "maf2c.fh" if (f2c_free_heap_piece(memhandle, nelem) .eq. MA_TRUE) then MA_free_heap_piece = .true. else MA_free_heap_piece = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_get_index (memhandle, index) implicit none #include "mafdecls.fh" integer memhandle MA_ACCESS_INDEX_TYPE index #include "maf2c.fh" if (f2c_get_index(memhandle, index) .eq. MA_TRUE) then MA_get_index = .true. else MA_get_index = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_get_next_memhandle (ithandle, memhandle) implicit none integer ithandle integer memhandle #include "maf2c.fh" if (f2c_get_next_memhandle(ithandle, memhandle) .eq. MA_TRUE) $ then MA_get_next_memhandle = .true. else MA_get_next_memhandle = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_get_numalign (numalign) implicit none integer numalign #include "maf2c.fh" MA_get_numalign = (f2c_get_numalign(numalign) .eq. MA_TRUE) return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_init (datatype, stack, heap) implicit none integer datatype integer stack integer heap #include "maf2c.fh" if (f2c_init(datatype, stack, heap) .eq. MA_TRUE) then MA_init = .true. else MA_init = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_initialized () implicit none #include "maf2c.fh" if (f2c_initialized() .eq. MA_TRUE) then MA_initialized = .true. else MA_initialized = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_init_memhandle_iterator (ithandle) implicit none integer ithandle #include "maf2c.fh" if (f2c_init_memhandle_iterator(ithandle) .eq. MA_TRUE) $ then MA_init_memhandle_iterator = .true. else MA_init_memhandle_iterator = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c integer function MA_inquire_avail (datatype) implicit none integer datatype #include "maf2c.fh" MA_inquire_avail = f2c_inquire_avail(datatype) return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c integer function MA_inquire_heap (datatype) implicit none integer datatype #include "maf2c.fh" MA_inquire_heap = f2c_inquire_heap(datatype) return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c integer function MA_inquire_heap_check_stack (datatype) implicit none integer datatype #include "maf2c.fh" MA_inquire_heap_check_stack = $ f2c_inquire_heap_check_stack(datatype) return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c integer function MA_inquire_heap_no_partition (datatype) implicit none integer datatype #include "maf2c.fh" MA_inquire_heap_no_partition = $ f2c_inquire_heap_no_partition(datatype) return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c integer function MA_inquire_stack (datatype) implicit none integer datatype #include "maf2c.fh" MA_inquire_stack = f2c_inquire_stack(datatype) return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c integer function MA_inquire_stack_check_heap (datatype) implicit none integer datatype #include "maf2c.fh" MA_inquire_stack_check_heap = $ f2c_inquire_stack_check_heap(datatype) return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c integer function MA_inquire_stack_no_partition (datatype) implicit none integer datatype #include "maf2c.fh" MA_inquire_stack_no_partition = $ f2c_inquire_stack_no_partition(datatype) return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_pop_stack (memhandle) implicit none integer memhandle #include "maf2c.fh" if (f2c_pop_stack(memhandle) .eq. MA_TRUE) then MA_pop_stack = .true. else MA_pop_stack = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c subroutine MA_print_stats(printroutines) implicit none logical printroutines #include "maf2c.fh" if (printroutines) then call f2c_print_stats(1) else call f2c_print_stats(0) endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_push_get (datatype, nelem, name, memhandle, $ index) implicit none #include "mafdecls.fh" integer datatype integer nelem character*(*) name integer memhandle MA_ACCESS_INDEX_TYPE index #include "maf2c.fh" if (f2c_push_get(datatype, nelem, name, memhandle, index) .eq. $ MA_TRUE) then MA_push_get = .true. else MA_push_get = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_push_stack (datatype, nelem, name, $ memhandle) implicit none integer datatype integer nelem character*(*) name integer memhandle #include "maf2c.fh" if (f2c_push_stack(datatype, nelem, name, memhandle) .eq. $ MA_TRUE) then MA_push_stack = .true. else MA_push_stack = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_set_auto_verify (value) implicit none logical value integer ivalue #include "maf2c.fh" if (value) then ivalue = MA_TRUE else ivalue = MA_FALSE endif if (f2c_set_auto_verify(ivalue) .eq. MA_TRUE) then MA_set_auto_verify = .true. else MA_set_auto_verify = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_set_error_print (value) implicit none logical value integer ivalue #include "maf2c.fh" if (value) then ivalue = MA_TRUE else ivalue = MA_FALSE endif if (f2c_set_error_print(ivalue) .eq. MA_TRUE) then MA_set_error_print = .true. else MA_set_error_print = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_set_hard_fail (value) implicit none logical value integer ivalue #include "maf2c.fh" if (value) then ivalue = MA_TRUE else ivalue = MA_FALSE endif if (f2c_set_hard_fail(ivalue) .eq. MA_TRUE) then MA_set_hard_fail = .true. else MA_set_hard_fail = .false. endif return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_set_numalign (numalign) implicit none integer numalign #include "maf2c.fh" MA_set_numalign = (f2c_set_numalign(numalign) .eq. MA_TRUE) return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c integer function MA_sizeof (datatype1, nelem1, datatype2) implicit none integer datatype1 integer nelem1 integer datatype2 #include "maf2c.fh" MA_sizeof = f2c_sizeof(datatype1, nelem1, datatype2) return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c integer function MA_sizeof_overhead (datatype) implicit none integer datatype #include "maf2c.fh" MA_sizeof_overhead = f2c_sizeof_overhead(datatype) return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c subroutine MA_summarize_allocated_blocks implicit none #include "maf2c.fh" call f2c_summarize_allocated_blocks() return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c subroutine ma_trace(value) implicit none integer value #include "maf2c.fh" call f2c_trace(value) return end c --------------------------------------------------------------- c c --------------------------------------------------------------- c logical function MA_verify_allocator_stuff () implicit none #include "maf2c.fh" if (f2c_verify_allocator_stuff() .eq. MA_TRUE) then MA_verify_allocator_stuff = .true. else MA_verify_allocator_stuff = .false. endif return end #undef MAF_INTERNAL ga-5.9.2/ma/maf2c.fh000066400000000000000000000051271500715745200140350ustar00rootroot00000000000000#ifndef _maf2c_fh #define _maf2c_fh c c $Id: maf2c.fh,v 1.6 2002-09-14 05:40:30 d3g001 Exp $ c c c Private header file containing FORTRAN type declarations for the c C wrapper routines. c c This file should only be included by internal FORTRAN files. c c c The guard ends here instead of at the end of the file because we c need the declarations (stuff below) to be defined each time this file c is included by a FORTRAN file. c #endif /* _maf2c_fh */ c c function types c integer f2c_alloc_get integer f2c_allocate_heap integer f2c_chop_stack integer f2c_free_heap integer f2c_free_heap_piece integer f2c_get_index integer f2c_get_next_memhandle integer f2c_get_numalign integer f2c_inform_base integer f2c_init integer f2c_initialized integer f2c_init_memhandle_iterator integer f2c_inquire_avail integer f2c_inquire_heap integer f2c_inquire_heap_check_stack integer f2c_inquire_heap_no_partition integer f2c_inquire_stack integer f2c_inquire_stack_check_heap integer f2c_inquire_stack_no_partition integer f2c_pop_stack c void f2c_print_stats integer f2c_push_get integer f2c_push_stack integer f2c_set_auto_verify integer f2c_set_error_print integer f2c_set_hard_fail integer f2c_set_numalign integer f2c_sizeof integer f2c_sizeof_overhead c void f2c_summarize_allocated_blocks c void f2c_trace integer f2c_verify_allocator_stuff external f2c_alloc_get external f2c_allocate_heap external f2c_chop_stack external f2c_free_heap external f2c_free_heap_piece external f2c_get_index external f2c_get_next_memhandle external f2c_get_numalign external f2c_inform_base external f2c_init external f2c_initialized external f2c_init_memhandle_iterator external f2c_inquire_avail external f2c_inquire_heap external f2c_inquire_heap_check_stack external f2c_inquire_heap_no_partition external f2c_inquire_stack external f2c_inquire_stack_check_heap external f2c_inquire_stack_no_partition external f2c_pop_stack external f2c_print_stats external f2c_push_get external f2c_push_stack external f2c_set_auto_verify external f2c_set_error_print external f2c_set_hard_fail external f2c_set_numalign external f2c_sizeof external f2c_sizeof_overhead external f2c_summarize_allocated_blocks external f2c_trace external f2c_verify_allocator_stuff ga-5.9.2/ma/mafdecls.fh.in000066400000000000000000000106731500715745200152320ustar00rootroot00000000000000#ifndef _mafdecls_fh #define _mafdecls_fh ! ! $Id: mafdecls.fh,v 1.11 2002-09-14 05:40:30 d3g001 Exp $ ! ! ! Public header file for a portable dynamic memory allocator. ! ! This file may be included by internal and external FORTRAN files. ! #include "macommon.h" ! ! The guard ends here instead of at the end of the file because we only ! need the cpp constants (stuff above) defined once per FORTRAN file, ! but need the declarations (stuff below) to be defined each time this ! file is included in a FORTRAN file. ! #endif ! ! constants ! ! type declarations for datatype constants integer MT_BYTE ! byte integer MT_INT ! integer integer MT_LOG ! logical integer MT_REAL ! real integer MT_DBL ! double precision integer MT_SCPL ! single precision complex integer MT_DCPL ! double precision complex integer MT_F_FIRST ! first type integer MT_F_LAST ! last type ! parameter declarations for datatype constants parameter (MT_BYTE = MT_F_BYTE) parameter (MT_INT = MT_F_INT) parameter (MT_LOG = MT_F_LOG) parameter (MT_REAL = MT_F_REAL) parameter (MT_DBL = MT_F_DBL) parameter (MT_SCPL = MT_F_SCPL) parameter (MT_DCPL = MT_F_DCPL) parameter (MT_F_FIRST = MT_BYTE) parameter (MT_F_LAST = MT_DCPL) ! ! function types ! #ifndef MAF_INTERNAL logical MA_alloc_get logical MA_allocate_heap logical MA_chop_stack logical MA_free_heap logical MA_free_heap_piece logical MA_get_index logical MA_get_next_memhandle logical MA_get_numalign logical MA_init logical MA_initialized logical MA_init_memhandle_iterator integer MA_inquire_avail integer MA_inquire_heap integer MA_inquire_heap_check_stack integer MA_inquire_heap_no_partition integer MA_inquire_stack integer MA_inquire_stack_check_heap integer MA_inquire_stack_no_partition logical MA_pop_stack ! subroutine MA_print_stats logical MA_push_get logical MA_push_stack logical MA_set_auto_verify logical MA_set_error_print logical MA_set_hard_fail logical MA_set_numalign integer MA_sizeof integer MA_sizeof_overhead ! subroutine MA_summarize_allocated_blocks ! subroutine MA_trace logical MA_verify_allocator_stuff external MA_alloc_get external MA_allocate_heap external MA_chop_stack external MA_free_heap external MA_free_heap_piece external MA_get_index external MA_get_next_memhandle external MA_get_numalign external MA_init external MA_initialized external MA_init_memhandle_iterator external MA_inquire_avail external MA_inquire_heap external MA_inquire_heap_check_stack external MA_inquire_heap_no_partition external MA_inquire_stack external MA_inquire_stack_check_heap external MA_inquire_stack_no_partition external MA_pop_stack external MA_print_stats external MA_push_get external MA_push_stack external MA_set_auto_verify external MA_set_error_print external MA_set_hard_fail external MA_set_numalign external MA_sizeof external MA_sizeof_overhead external MA_summarize_allocated_blocks external MA_trace external MA_verify_allocator_stuff #endif ! ! variables ! ! common blocks #ifdef INTEL_64ALIGN !DIR$ ATTRIBUTES ALIGN : 64 :: mbc_byte #endif common /mbc_byte/ byte_mb(2) character*1 byte_mb #ifdef INTEL_64ALIGN !DIR$ ATTRIBUTES ALIGN : 64 :: mbc_int #endif common /mbc_int/ int_mb(2) integer int_mb common /mbc_log/ log_mb(2) logical log_mb #ifdef INTEL_64ALIGN !DIR$ ATTRIBUTES ALIGN : 64 :: mbc_real #endif common /mbc_real/ real_mb(2) real real_mb #ifdef INTEL_64ALIGN !DIR$ ATTRIBUTES ALIGN : 64 :: mbc_dbl #endif common /mbc_dbl/ dbl_mb(2) double precision dbl_mb #ifdef INTEL_64ALIGN !DIR$ ATTRIBUTES ALIGN : 64 :: mbc_scpl #endif common /mbc_scpl/ scpl_mb(2) complex scpl_mb #ifdef INTEL_64ALIGN !DIR$ ATTRIBUTES ALIGN : 64 :: mbc_dcpl #endif common /mbc_dcpl/ dcpl_mb(2) double complex dcpl_mb #define MA_ACCESS_INDEX_TYPE @MA_ACCESS_INDEX_TYPE@ #define MAPOINTER @MA_ACCESS_INDEX_TYPE@ ga-5.9.2/ma/man/000077500000000000000000000000001500715745200132745ustar00rootroot00000000000000ga-5.9.2/ma/man/man3/000077500000000000000000000000001500715745200141325ustar00rootroot00000000000000ga-5.9.2/ma/man/man3/MA.3000066400000000000000000000123071500715745200145160ustar00rootroot00000000000000.TH MA 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA - introduction to the MA dynamic memory allocator .SH DESCRIPTION MA is a library of routines that comprises a dynamic memory allocator for use by C, FORTRAN, or mixed-language applications. C applications can benefit from using MA instead of the ordinary malloc() and free() routines because of the extra features MA provides: both heap and stack memory management disciplines, debugging and verification support, usage statistics, and quantitative memory availability information. FORTRAN applications can take advantage of the same features, and may in fact require a library such as MA because dynamic memory allocation is not supported by all versions of the language. MA is designed to be portable across a variety of platforms. .SH IMPLEMENTATION Memory layout: .in +0.5i .nf segment = heap_region stack_region region = block block block ... block = AD gap1 guard1 client_space guard2 gap2 .fi .in A segment of memory is obtained from the OS upon initialization. The low end of the segment is managed as a heap; the heap region grows from low addresses to high addresses. The high end of the segment is managed as a stack; the stack region grows from high addresses to low addresses. Each region consists of a series of contiguous blocks, one per allocation request, and possibly some unused space. Blocks in the heap region are either in use by the client (allocated and not yet deallocated) or not in use by the client (allocated and already deallocated). A block on the rightmost end of the heap region becomes part of the unused space upon deallocation. Blocks in the stack region are always in use by the client, because when a stack block is deallocated, it becomes part of the unused space. A block consists of the client space, i.e., the range of memory available for use by the application; guard words adjacent to each end of the client space to help detect improper memory access by the client; bookkeeping info (in an "allocation descriptor," AD); and two gaps, each zero or more bytes long, to satisfy alignment constraints (specifically, to ensure that AD and client_space are aligned properly). .SH "LIST OF ROUTINES" All MA routines are shown below, grouped by category and listed alphabetically within each category. Initialization .in +0.5i .nf MA_init() MA_initialized() MA_sizeof() MA_sizeof_overhead() .fi .in Allocation .in +0.5i .nf MA_alloc_get() MA_allocate_heap() MA_get_index() MA_get_pointer() MA_inquire_avail() MA_inquire_heap() MA_inquire_stack() MA_push_get() MA_push_stack() .fi .in Deallocation .in +0.5i .nf MA_chop_stack() MA_free_heap() MA_pop_stack() .fi .in Debugging .in +0.5i .nf MA_set_auto_verify() MA_set_error_print() MA_set_hard_fail() MA_summarize_allocated_blocks() MA_verify_allocator_stuff() .fi .in Iteration Over Allocated Blocks .in +0.5i .nf MA_get_next_memhandle() MA_init_memhandle_iterator() .fi .in Statistics .in +0.5i .nf MA_print_stats() .fi .in .SH TYPES There are three MA-specific types in the public C interface to MA: Integer, Boolean, and Pointer. They are accessible by including macdecls.h. Integer is defined in such a way that sizeof(Integer) is equal to the number of bytes in a FORTRAN integer, which varies according to compiler options and platform. Boolean is equivalent to Integer, and Pointer is equivalent to char *. .SH ERRORS Errors considered fatal by MA result in program termination. Errors considered nonfatal by MA cause the MA routine to return an error value to the caller. For most boolean functions, false is returned upon failure and true is returned upon success. (The boolean functions for which the return value means something other than success or failure are MA_set_auto_verify(), MA_set_error_print(), and MA_set_hard_fail().) Integer functions return zero upon failure; depending on the function, zero may or may not be distinguishable as an exceptional value. An application can force MA to treat all errors as fatal via MA_set_hard_fail(). If a fatal error occurs, an error message is printed on the standard error (stderr). By default, error messages are also printed for nonfatal errors. An application can force MA to print or not print error messages for nonfatal errors via MA_set_error_print(). .SH FILES To access required MA definitions, C applications should include macdecls.h and FORTRAN applications should include mafdecls.fh. .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA_alloc_get(3), MA_allocate_heap(3), MA_chop_stack(3), MA_free_heap(3), MA_get_index(3), MA_get_next_memhandle(3), MA_get_pointer(3), MA_init(3), MA_initialized(3), MA_init_memhandle_iterator(3), MA_inquire_avail(3), MA_inquire_heap(3), MA_inquire_stack(3), MA_pop_stack(3), MA_print_stats(3), MA_push_get(3), MA_push_stack(3), MA_set_auto_verify(3), MA_set_error_print(3), MA_set_hard_fail(3), MA_sizeof(3), MA_sizeof_overhead(3), MA_summarize_allocated_blocks(3), MA_verify_allocator_stuff(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_alloc_get.3000066400000000000000000000061141500715745200165260ustar00rootroot00000000000000.TH MA_ALLOC_GET 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_alloc_get - allocate a heap block and get the corresponding base index .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_alloc_get(datatype, nelem, name, memhandle, index) Integer datatype; /* read-only */ Integer nelem; /* read-only */ char *name; /* read-only */ Integer *memhandle; /* write-only */ Integer *index; /* write-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_alloc_get(datatype, nelem, name, memhandle, index) integer datatype integer nelem character*(*) name integer memhandle integer index .fi .SH DESCRIPTION MA_alloc_get() is a convenience function that combines MA_allocate_heap() and MA_get_index(). MA_alloc_get() allocates a heap block large enough to hold .I nelem elements of type .I datatype and associates .I name with the block for debugging purposes. A handle for the block is returned in .I memhandle and the base index for the type-specific data array that is appropriate for the block is returned in .IR index . .SH USAGE The following FORTRAN code illustrates the use of MA_alloc_get() by allocating a block of 5 integers on the heap and then storing values into the integers. .nf #include "mafdecls.fh" logical ok integer mhandle integer index integer i ok = MA_alloc_get(MT_INT, 5, 'heap int block', mhandle, index) if (ok) then do 10 i = 0, 4 int_mb(index + i) = 0 10 continue endif .fi .SH DIAGNOSTICS block '%s', MA not yet initialized .in +0.5i MA_init() must be called before this routine is called. .in block '%s', invalid datatype: %d .in +0.5i .I datatype must be one of those listed in macdecls.h or mafdecls.fh. .in block '%s', invalid nelem: %d .in +0.5i .I nelem must be greater than zero. .in block '%s', not enough space to allocate %d bytes .in +0.5i Recovery may be attempted by deallocating unneeded blocks and resubmitting the request, or the application may be rewritten to request more memory initially in the call to MA_init(). .in Other diagnostics are possible. If seen, they indicate corruption of the internal state of MA, caused by bugs in either MA or the application. .SH "RETURN VALUE" C: MA_TRUE upon success, MA_FALSE upon failure. .br FORTRAN: .true. upon success, .false. upon failure. .SH NOTES Type-specific data arrays are not yet available in C, so employ pointers instead of indices by using MA_allocate_heap() and MA_get_pointer() instead of MA_alloc_get(). .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_allocate_heap(3), MA_free_heap(3), MA_get_index(3), MA_get_pointer(3), MA_inquire_avail(3), MA_inquire_heap(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_allocate_heap.3000066400000000000000000000040751500715745200173620ustar00rootroot00000000000000.TH MA_ALLOCATE_HEAP 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_allocate_heap - allocate a heap block .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_allocate_heap(datatype, nelem, name, memhandle) Integer datatype; /* read-only */ Integer nelem; /* read-only */ char *name; /* read-only */ Integer *memhandle; /* write-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_allocate_heap(datatype, nelem, name, memhandle) integer datatype integer nelem character*(*) name integer memhandle .fi .SH DESCRIPTION MA_allocate_heap() allocates a heap block large enough to hold .I nelem elements of type .I datatype and associates .I name with the block for debugging purposes. A handle for the block is returned in .IR memhandle . .\" .SH USAGE .SH DIAGNOSTICS block '%s', MA not yet initialized .in +0.5i MA_init() must be called before this routine is called. .in block '%s', invalid datatype: %d .in +0.5i .I datatype must be one of those listed in macdecls.h or mafdecls.fh. .in block '%s', invalid nelem: %d .in +0.5i .I nelem must be greater than zero. .in block '%s', not enough space to allocate %d bytes .in +0.5i Recovery may be attempted by deallocating unneeded blocks and resubmitting the request, or the application may be rewritten to request more memory initially in the call to MA_init(). .in .SH "RETURN VALUE" C: MA_TRUE upon success, MA_FALSE upon failure. .br FORTRAN: .true. upon success, .false. upon failure. .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_alloc_get(3), MA_free_heap(3), MA_get_index(3), MA_get_pointer(3), MA_inquire_avail(3), MA_inquire_heap(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_chop_stack.3000066400000000000000000000040061500715745200167110ustar00rootroot00000000000000.TH MA_CHOP_STACK 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_chop_stack - deallocate a stack block and all stack blocks allocated after it .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_chop_stack(memhandle) Integer memhandle; /* read-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_chop_stack(memhandle) integer memhandle .fi .SH DESCRIPTION MA_chop_stack() deallocates the stack block corresponding to the handle .I memhandle (which was returned by MA_push_get() or MA_push_stack() when the block was allocated) and all stack blocks allocated after it. .\" .SH USAGE .SH DIAGNOSTICS invalid memhandle: %d .in +0.5i .I memhandle is not a valid handle. .in invalid checksum for memhandle %d (name: '%s') .in +0.5i The block's computed checksum does not match its stored checksum. This indicates that the block has been corrupted by having its internal state overwritten. .in invalid guard(s) for memhandle %d (name: '%s') .in +0.5i This indicates that the block has been corrupted by being overwritten at one or both ends. The likely cause of this is an application indexing bug. .in memhandle %d (name: '%s') not in stack .in +0.5i The block is not currently allocated in the stack. .in Other diagnostics are possible. If seen, they indicate corruption of the internal state of MA, caused by bugs in either MA or the application. .SH "RETURN VALUE" C: MA_TRUE upon success, MA_FALSE upon failure. .br FORTRAN: .true. upon success, .false. upon failure. .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_inquire_avail(3), MA_inquire_stack(3), MA_pop_stack(3), MA_push_get(3), MA_push_stack(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_free_heap.3000066400000000000000000000036431500715745200165170ustar00rootroot00000000000000.TH MA_FREE_HEAP 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_free_heap - deallocate a heap block .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_free_heap(memhandle) Integer memhandle; /* read-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_free_heap(memhandle) integer memhandle .fi .SH DESCRIPTION MA_free_heap() deallocates the heap block corresponding to the handle .I memhandle (which was returned by MA_alloc_get() or MA_allocate_heap() when the block was allocated). .\" .SH USAGE .SH DIAGNOSTICS invalid memhandle: %d .in +0.5i .I memhandle is not a valid handle. .in invalid checksum for memhandle %d (name: '%s') .in +0.5i The block's computed checksum does not match its stored checksum. This indicates that the block has been corrupted by having its internal state overwritten. .in invalid guard(s) for memhandle %d (name: '%s') .in +0.5i This indicates that the block has been corrupted by being overwritten at one or both ends. The likely cause of this is an application indexing bug. .in memhandle %d (name: '%s') not in heap .in +0.5i The block is not currently allocated in the heap. .in Other diagnostics are possible. If seen, they indicate corruption of the internal state of MA, caused by bugs in either MA or the application. .SH "RETURN VALUE" C: MA_TRUE upon success, MA_FALSE upon failure. .br FORTRAN: .true. upon success, .false. upon failure. .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_alloc_get(3), MA_allocate_heap(3), MA_inquire_avail(3), MA_inquire_heap(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_get_index.3000066400000000000000000000053651500715745200165520ustar00rootroot00000000000000.TH MA_GET_INDEX 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_get_index - get the base index for a block .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_get_index(memhandle, index) Integer memhandle; /* read-only */ Integer *index; /* write-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_get_index(memhandle, index) integer memhandle integer index .fi .SH DESCRIPTION MA_get_index() returns in .I index the base index for the type-specific data array that is appropriate for the block corresponding to the handle .I memhandle (which was returned by MA_allocate_heap() or MA_push_stack() when the block was allocated). .SH USAGE The following FORTRAN code illustrates the use of MA_get_index() by allocating a block of 5 integers on the heap and then storing values into the integers. .nf #include "mafdecls.fh" logical ok integer mhandle integer index integer i ok = MA_allocate_heap(MT_INT, 5, 'heap int block', mhandle) if (ok) then ok = MA_get_index(mhandle, index) if (ok) then do 10 i = 0, 4 int_mb(index + i) = 0 10 continue endif endif .fi .SH DIAGNOSTICS invalid memhandle: %d .in +0.5i .I memhandle is not a valid handle. .in invalid checksum for memhandle %d (name: '%s') .in +0.5i The block's computed checksum does not match its stored checksum. This indicates that the block has been corrupted by having its internal state overwritten. .in invalid guard(s) for memhandle %d (name: '%s') .in +0.5i This indicates that the block has been corrupted by being overwritten at one or both ends. The likely cause of this is an application indexing bug. .in memhandle %d (name: '%s') not in heap or stack .in +0.5i The block is not currently allocated in the heap or the stack. .in Other diagnostics are possible. If seen, they indicate corruption of the internal state of MA, caused by bugs in either MA or the application. .SH "RETURN VALUE" C: MA_TRUE upon success, MA_FALSE upon failure. .br FORTRAN: .true. upon success, .false. upon failure. .SH NOTES Type-specific data arrays are not yet available in C, so employ pointers instead of indices by using MA_get_pointer() instead of MA_get_index(). .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_alloc_get(3), MA_allocate_heap(3), MA_get_pointer(3), MA_push_get(3), MA_push_stack(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_get_next_memhandle.3000066400000000000000000000035771500715745200204360ustar00rootroot00000000000000.TH MA_GET_NEXT_MEMHANDLE 3 "20 February 1997" "MA Release 1.8" " " .SH NAME MA_get_next_memhandle - get the handle for the next block in the scan of currently allocated blocks .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_get_next_memhandle(ithandle, memhandle) Integer *ithandle; /* read-write */ Integer *memhandle; /* write-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_get_next_memhandle(ithandle, memhandle) integer ithandle integer memhandle .fi .SH DESCRIPTION MA_get_next_memhandle() returns in .I memhandle the handle for the next block in the scan of currently allocated blocks corresponding to the iterator handle .I ithandle (which was returned by MA_init_memhandle_iterator() when the scan was initialized). .SH USAGE The following FORTRAN code illustrates the use of MA_get_next_memhandle() by looping over all currently allocated blocks. .nf #include "mafdecls.fh" logical ok integer ihandle integer mhandle ok = MA_init_memhandle_iterator(ihandle) if (ok) then ok = MA_get_next_memhandle(ihandle, mhandle) 10 if (ok) then ... process mhandle ok = MA_get_next_memhandle(ihandle, mhandle) goto 10 endif endif .fi .\" .SH DIAGNOSTICS .SH "RETURN VALUE" C: MA_TRUE upon success, MA_FALSE upon failure. .br FORTRAN: .true. upon success, .false. upon failure. .SH NOTES This routine is not yet implemented. .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_init_memhandle_iterator(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_get_pointer.3000066400000000000000000000047651500715745200171260ustar00rootroot00000000000000.TH MA_GET_POINTER 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_get_pointer - get the base pointer for a block .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_get_pointer(memhandle, pointer) Integer memhandle; /* read-only */ Pointer *pointer; /* write-only */ .fi .SH "FORTRAN SYNOPSIS" Not available. .SH DESCRIPTION MA_get_pointer() returns in .I pointer the base pointer for the block corresponding to the handle .I memhandle (which was returned by MA_allocate_heap() or MA_push_stack() when the block was allocated). .SH USAGE The following C code illustrates the use of MA_get_pointer() by allocating a block of 5 integers on the heap and then storing values into the integers. .nf #include "macdecls.h" Boolean ok; Integer mhandle; Pointer pointer; int i; ok = MA_allocate_heap(MT_INT, 5, "heap int block", &mhandle); if (ok) { ok = MA_get_pointer(mhandle, &pointer); if (ok) for (i = 0; i < 5; i++) *((int *)pointer + i) = 0; } .fi .SH DIAGNOSTICS invalid memhandle: %d .in +0.5i .I memhandle is not a valid handle. .in invalid checksum for memhandle %d (name: '%s') .in +0.5i The block's computed checksum does not match its stored checksum. This indicates that the block has been corrupted by having its internal state overwritten. .in invalid guard(s) for memhandle %d (name: '%s') .in +0.5i This indicates that the block has been corrupted by being overwritten at one or both ends. The likely cause of this is an application indexing bug. .in memhandle %d (name: '%s') not in heap or stack .in +0.5i The block is not currently allocated in the heap or the stack. .in Other diagnostics are possible. If seen, they indicate corruption of the internal state of MA, caused by bugs in either MA or the application. .SH "RETURN VALUE" C: MA_TRUE upon success, MA_FALSE upon failure. .br FORTRAN: Not available. .SH NOTES MA_get_pointer() is not available in FORTRAN, because FORTRAN does not support pointers. .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_alloc_get(3), MA_allocate_heap(3), MA_get_index(3), MA_push_get(3), MA_push_stack(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_init.3000066400000000000000000000072031500715745200155400ustar00rootroot00000000000000.TH MA_INITIALIZE 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_init - initialize the memory allocator .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_init(datatype, nominal_stack, nominal_heap) Integer datatype; /* read-only */ Integer nominal_stack; /* read-only */ Integer nominal_heap; /* read-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_init(datatype, nominal_stack, nominal_heap) integer datatype integer nominal_stack integer nominal_heap .fi .SH DESCRIPTION MA_init() initializes the memory allocator by requesting from the operating system a single segment of memory that is used to satisfy allocations for both the heap and the stack. The memory segment is large enough to hold .I nominal_heap elements of type .I datatype in the heap and .I nominal_stack elements of type .I datatype in the stack, plus the overhead for two allocations (one heap and one stack); if more than two allocations will exist concurrently, overhead may be computed using MA_sizeof_overhead() and factored into .I nominal_heap and .IR nominal_stack . If .I nominal_heap is less than zero, a default total size for the heap is used. If .I nominal_stack is less than zero, a default total size for the stack is used. MA_init() must be called before any other MA routine is called, except MA_set_auto_verify(), MA_set_error_print(), MA_set_hard_fail(), MA_sizeof(), or MA_sizeof_overhead(). .SH USAGE The following FORTRAN code illustrates the use of MA_init() by computing the space required for 100 integers in 5 allocations in the heap, 200 logicals in 4 allocations in the heap, 300 reals in 3 allocations in the stack, and 400 doubles in 2 allocations in the stack. .nf #include "mafdecls.fh" logical ok integer heap_bytes_data integer heap_bytes_overhead integer heap_bytes_total integer stack_bytes_data integer stack_bytes_overhead integer stack_bytes_total heap_bytes_data = MA_sizeof(MT_INT, 100, MT_BYTE) + MA_sizeof(MT_LOG, 200, MT_BYTE) heap_bytes_overhead = (5 + 4) * MA_sizeof_overhead(MT_BYTE) heap_bytes_total = heap_bytes_data + heap_bytes_overhead stack_bytes_data = MA_sizeof(MT_REAL, 300, MT_BYTE) + MA_sizeof(MT_DBL, 400, MT_BYTE) stack_bytes_overhead = (3 + 2) * MA_sizeof_overhead(MT_BYTE) stack_bytes_total = stack_bytes_data + stack_bytes_overhead ok = MA_init(MT_BYTE, stack_bytes_total, heap_bytes_total) .fi .SH DIAGNOSTICS unable to set sizes of FORTRAN datatypes .in +0.5i This indicates either that the internal state of MA is corrupted or that there is a problem in the C-FORTRAN linkage. .in MA already initialized .in +0.5i MA_init() may not be called after it has returned successfully. .in invalid datatype: %d .in +0.5i .I datatype must be one of those listed in macdecls.h or mafdecls.fh. .in could not allocate %d bytes .in +0.5i The request for memory to the operating system failed. Recovery may be attempted by calling MA_init() again and requesting less memory. .in .SH "RETURN VALUE" C: MA_TRUE upon success, MA_FALSE upon failure. .br FORTRAN: .true. upon success, .false. upon failure. .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_initialized(3), MA_sizeof(3), MA_sizeof_overhead(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_init_memhandle_iterator.3000066400000000000000000000032211500715745200214570ustar00rootroot00000000000000.TH MA_INITIALIZE_MEMHANDLE_ITERATOR 3 "20 February 1997" "MA Release 1.8" " " .SH NAME MA_init_memhandle_iterator - initialize a scan of currently allocated blocks .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_init_memhandle_iterator(ithandle) Integer *ithandle; /* write-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_init_memhandle_iterator(ithandle) integer ithandle .fi .SH DESCRIPTION MA_init_memhandle_iterator() returns in .I ithandle the iterator handle for a scan of currently allocated blocks. .SH USAGE The following FORTRAN code illustrates the use of MA_init_memhandle_iterator() by looping over all currently allocated blocks. .nf #include "mafdecls.fh" logical ok integer ihandle integer mhandle ok = MA_init_memhandle_iterator(ihandle) if (ok) then ok = MA_get_next_memhandle(ihandle, mhandle) 10 if (ok) then ... process mhandle ok = MA_get_next_memhandle(ihandle, mhandle) goto 10 endif endif .fi .\" .SH DIAGNOSTICS .SH "RETURN VALUE" C: MA_TRUE upon success, MA_FALSE upon failure. .br FORTRAN: .true. upon success, .false. upon failure. .SH NOTES This routine is not yet implemented. .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_get_next_memhandle(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_initialized.3000066400000000000000000000020471500715745200171030ustar00rootroot00000000000000.TH MA_INITIALIZED 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_initialized - find out if MA has been successfully initialized .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_initialized() .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_initialized() .fi .SH DESCRIPTION MA_initialized() returns true if MA_init() has been called successfully, otherwise it returns false. .\" .SH USAGE .\" .SH DIAGNOSTICS .SH "RETURN VALUE" C: MA_TRUE if MA has been successfully initialized, MA_FALSE otherwise. .br FORTRAN: .true. if MA has been successfully initialized, .false. otherwise. .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_init(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_inquire_avail.3000066400000000000000000000031241500715745200174230ustar00rootroot00000000000000.TH MA_INQUIRE_AVAIL 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_inquire_avail - find out how much free space is available between the heap and stack regions .SH "C SYNOPSIS" .nf #include "macdecls.h" Integer MA_inquire_avail(datatype) Integer datatype; /* read-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" integer function MA_inquire_avail(datatype) integer datatype .fi .SH DESCRIPTION MA_inquire_avail() returns the maximum number of elements of type .I datatype that can currently be allocated in the space between the heap and stack, in a single allocation request, ignoring the partition defined at initialization. Note that this might not be the largest piece of memory available; the heap may contain deallocated blocks that are larger. .\" .SH USAGE .SH DIAGNOSTICS MA not yet initialized .in +0.5i MA_init() must be called before this routine is called. .in invalid datatype: %d .in +0.5i .I datatype must be one of those listed in macdecls.h or mafdecls.fh. .in .SH "RETURN VALUE" C: The number of elements, as described above. .br FORTRAN: The number of elements, as described above. .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_inquire_heap(3), MA_inquire_stack(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_inquire_heap.3000066400000000000000000000026561500715745200172550ustar00rootroot00000000000000.TH MA_INQUIRE_HEAP 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_inquire_heap - find out how much free space is available in the heap region .SH "C SYNOPSIS" .nf #include "macdecls.h" Integer MA_inquire_heap(datatype) Integer datatype; /* read-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" integer function MA_inquire_heap(datatype) integer datatype .fi .SH DESCRIPTION MA_inquire_heap() returns the maximum number of elements of type .I datatype that can currently be allocated in the heap, in a single allocation request, with respect to the partition defined at initialization. .\" .SH USAGE .SH DIAGNOSTICS MA not yet initialized .in +0.5i MA_init() must be called before this routine is called. .in invalid datatype: %d .in +0.5i .I datatype must be one of those listed in macdecls.h or mafdecls.fh. .in .SH "RETURN VALUE" C: The number of elements, as described above. .br FORTRAN: The number of elements, as described above. .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_inquire_avail(3), MA_inquire_stack(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_inquire_stack.3000066400000000000000000000026641500715745200174440ustar00rootroot00000000000000.TH MA_INQUIRE_STACK 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_inquire_stack - find out how much free space is available in the stack region .SH "C SYNOPSIS" .nf #include "macdecls.h" Integer MA_inquire_stack(datatype) Integer datatype; /* read-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" integer function MA_inquire_stack(datatype) integer datatype .fi .SH DESCRIPTION MA_inquire_stack() returns the maximum number of elements of type .I datatype that can currently be allocated in the stack, in a single allocation request, with respect to the partition defined at initialization. .\" .SH USAGE .SH DIAGNOSTICS MA not yet initialized .in +0.5i MA_init() must be called before this routine is called. .in invalid datatype: %d .in +0.5i .I datatype must be one of those listed in macdecls.h or mafdecls.fh. .in .SH "RETURN VALUE" C: The number of elements, as described above. .br FORTRAN: The number of elements, as described above. .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_inquire_avail(3), MA_inquire_heap(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_pop_stack.3000066400000000000000000000042031500715745200165550ustar00rootroot00000000000000.TH MA_POP_STACK 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_pop_stack - deallocate a stack block .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_pop_stack(memhandle) Integer memhandle; /* read-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_pop_stack(memhandle) integer memhandle .fi .SH DESCRIPTION MA_pop_stack() deallocates the stack block corresponding to the handle .I memhandle (which was returned by MA_push_get() or MA_push_stack() when the block was allocated), which must be at the top of the stack (i.e., the stack block most recently allocated). .\" .SH USAGE .SH DIAGNOSTICS invalid memhandle: %d .in +0.5i .I memhandle is not a valid handle. .in invalid checksum for memhandle %d (name: '%s') .in +0.5i The block's computed checksum does not match its stored checksum. This indicates that the block has been corrupted by having its internal state overwritten. .in invalid guard(s) for memhandle %d (name: '%s') .in +0.5i This indicates that the block has been corrupted by being overwritten at one or both ends. The likely cause of this is an application indexing bug. .in memhandle %d (name: '%s') not in stack .in +0.5i The block is not currently allocated in the stack. .in memhandle %d (name: '%s') not top of stack .in +0.5i The block is currently allocated in the stack, but is not at the top of the stack. .in Other diagnostics are possible. If seen, they indicate corruption of the internal state of MA, caused by bugs in either MA or the application. .SH "RETURN VALUE" C: MA_TRUE upon success, MA_FALSE upon failure. .br FORTRAN: .true. upon success, .false. upon failure. .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_push_get(3), MA_push_stack(3), MA_inquire_avail(3), MA_inquire_stack(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_print_stats.3000066400000000000000000000022021500715745200171410ustar00rootroot00000000000000.TH MA_PRINT_STATS 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_print_stats - print MA usage statistics on the standard output (stdout) .SH "C SYNOPSIS" .nf #include "macdecls.h" void MA_print_stats() .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" subroutine MA_print_stats .fi .SH DESCRIPTION MA_print_stats() prints MA allocation statistics (current and maximum number of blocks and total bytes for heap and stack) and calls per MA routine on the standard output (stdout). .\" .SH USAGE .SH DIAGNOSTICS unavailable; recompile MA with -DSTATS .in +0.5i To use this routine, MA must be compiled with cpp option -DSTATS. .in .SH "RETURN VALUE" C: None. .br FORTRAN: None. .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_summarize_allocated_blocks(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_push_get.3000066400000000000000000000061211500715745200164110ustar00rootroot00000000000000.TH MA_PUSH_GET 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_push_get - allocate a stack block and get the corresponding base index .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_push_get(datatype, nelem, name, memhandle, index) Integer datatype; /* read-only */ Integer nelem; /* read-only */ char *name; /* read-only */ Integer *memhandle; /* write-only */ Integer *index; /* write-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_push_get(datatype, nelem, name, memhandle, index) integer datatype integer nelem character*(*) name integer memhandle integer index .fi .SH DESCRIPTION MA_push_get() is a convenience function that combines MA_push_stack() and MA_get_index(). MA_push_get() allocates a stack block large enough to hold .I nelem elements of type .I datatype and associates .I name with the block for debugging purposes. A handle for the block is returned in .I memhandle and the base index for the type-specific data array that is appropriate for the block is returned in .IR index . .SH USAGE The following FORTRAN code illustrates the use of MA_push_get() by allocating a block of 5 integers on the stack and then storing values into the integers. .nf #include "mafdecls.fh" logical ok integer mhandle integer index integer i ok = MA_push_get(MT_INT, 5, 'stack int block', mhandle, index) if (ok) then do 10 i = 0, 4 int_mb(index + i) = 0 10 continue endif .fi .SH DIAGNOSTICS block '%s', MA not yet initialized .in +0.5i MA_init() must be called before this routine is called. .in block '%s', invalid datatype: %d .in +0.5i .I datatype must be one of those listed in macdecls.h or mafdecls.fh. .in block '%s', invalid nelem: %d .in +0.5i .I nelem must be greater than zero. .in block '%s', not enough space to allocate %d bytes .in +0.5i Recovery may be attempted by deallocating unneeded blocks and resubmitting the request, or the application may be rewritten to request more memory initially in the call to MA_init(). .in Other diagnostics are possible. If seen, they indicate corruption of the internal state of MA, caused by bugs in either MA or the application. .SH "RETURN VALUE" C: MA_TRUE upon success, MA_FALSE upon failure. .br FORTRAN: .true. upon success, .false. upon failure. .SH NOTES Type-specific data arrays are not yet available in C, so employ pointers instead of indices by using MA_push_stack() and MA_get_pointer() instead of MA_push_get(). .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_chop_stack(3), MA_get_index(3), MA_get_pointer(3), MA_inquire_avail(3), MA_inquire_stack(3), MA_pop_stack(3), MA_push_stack(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_push_stack.3000066400000000000000000000041021500715745200167340ustar00rootroot00000000000000.TH MA_PUSH_STACK 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_push_stack - allocate a stack block .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_push_stack(datatype, nelem, name, memhandle) Integer datatype; /* read-only */ Integer nelem; /* read-only */ char *name; /* read-only */ Integer *memhandle; /* write-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_push_stack(datatype, nelem, name, memhandle) integer datatype integer nelem character*(*) name integer memhandle .fi .SH DESCRIPTION MA_push_stack() allocates a stack block large enough to hold .I nelem elements of type .I datatype and associates .I name with the block for debugging purposes. A handle for the block is returned in .IR memhandle . .\" .SH USAGE .SH DIAGNOSTICS block '%s', MA not yet initialized .in +0.5i MA_init() must be called before this routine is called. .in block '%s', invalid datatype: %d .in +0.5i .I datatype must be one of those listed in macdecls.h or mafdecls.fh. .in block '%s', invalid nelem: %d .in +0.5i .I nelem must be greater than zero. .in block '%s', not enough space to allocate %d bytes .in +0.5i Recovery may be attempted by deallocating unneeded blocks and resubmitting the request, or the application may be rewritten to request more memory initially in the call to MA_init(). .in .SH "RETURN VALUE" C: MA_TRUE upon success, MA_FALSE upon failure. .br FORTRAN: .true. upon success, .false. upon failure. .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_chop_stack(3), MA_get_index(3), MA_get_pointer(3), MA_inquire_avail(3), MA_inquire_stack(3), MA_pop_stack(3), MA_push_get(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_set_auto_verify.3000066400000000000000000000027121500715745200200040ustar00rootroot00000000000000.TH MA_SET_AUTO_VERIFY 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_set_auto_verify - set the ma_auto_verify flag .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_set_auto_verify(value) Boolean value; /* read-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_set_auto_verify(value) logical value .fi .SH DESCRIPTION MA_set_auto_verify() sets the ma_auto_verify flag to .I value and returns the flag's previous value. If the ma_auto_verify flag is true, then MA_verify_allocator_stuff() is called upon invocation by every other public MA routine except: .in +0.5i .nf MA_initialized() MA_set_auto_verify() MA_set_error_print() MA_set_hard_fail() .fi .in The ma_auto_verify flag is initially false. .\" .SH USAGE .\" .SH DIAGNOSTICS .SH "RETURN VALUE" C: The previous value of the ma_auto_verify flag (MA_TRUE or MA_FALSE). .br FORTRAN: The previous value of the ma_auto_verify flag (.true. or .false.). .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_set_error_print(3), MA_set_hard_fail(3), MA_verify_allocator_stuff(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_set_error_print.3000066400000000000000000000025561500715745200200230ustar00rootroot00000000000000.TH MA_SET_ERROR_PRINT 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_set_error_print - set the ma_error_print flag .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_set_error_print(value) Boolean value; /* read-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_set_error_print(value) logical value .fi .SH DESCRIPTION MA_set_error_print() sets the ma_error_print flag to .I value and returns the flag's previous value. If the ma_error_print flag is true, then nonfatal errors cause messages to be printed on the standard error (stderr), else they do not. (Fatal errors always generate messages.) The ma_error_print flag is initially true. .\" .SH USAGE .\" .SH DIAGNOSTICS .SH "RETURN VALUE" C: The previous value of the ma_error_print flag (MA_TRUE or MA_FALSE). .br FORTRAN: The previous value of the ma_error_print flag (.true. or .false.). .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_set_auto_verify(3), MA_set_hard_fail(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_set_hard_fail.3000066400000000000000000000024671500715745200173700ustar00rootroot00000000000000.TH MA_SET_HARD_FAIL 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_set_hard_fail - set the ma_hard_fail flag .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_set_hard_fail(value) Boolean value; /* read-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_set_hard_fail(value) logical value .fi .SH DESCRIPTION MA_set_hard_fail() sets the ma_hard_fail flag to .I value and returns the flag's previous value. If the ma_hard_fail flag is true, then any error detected by MA (including those normally considered nonfatal) causes program termination. The ma_hard_fail flag is initially false. .\" .SH USAGE .\" .SH DIAGNOSTICS .SH "RETURN VALUE" C: The previous value of the ma_hard_fail flag (MA_TRUE or MA_FALSE). .br FORTRAN: The previous value of the ma_hard_fail flag (.true. or .false.). .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_set_auto_verify(3), MA_set_error_print(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_sizeof.3000066400000000000000000000052051500715745200160740ustar00rootroot00000000000000.TH MA_SIZEOF 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_sizeof - compute sizes of data types .SH "C SYNOPSIS" .nf #include "macdecls.h" Integer MA_sizeof(datatype1, nelem1, datatype2) Integer datatype1; /* read-only */ Integer nelem1; /* read-only */ Integer datatype2; /* read-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" integer function MA_sizeof(datatype1, nelem1, datatype2) integer datatype1 integer nelem1 integer datatype2 .fi .SH DESCRIPTION MA_sizeof() returns the number of elements of type .I datatype2 required to contain .I nelem1 elements of type .IR datatype1 . .SH USAGE The following FORTRAN code illustrates the use of MA_sizeof() by computing the space required for 100 integers in 5 allocations in the heap, 200 logicals in 4 allocations in the heap, 300 reals in 3 allocations in the stack, and 400 doubles in 2 allocations in the stack. .nf #include "mafdecls.fh" logical ok integer heap_bytes_data integer heap_bytes_overhead integer heap_bytes_total integer stack_bytes_data integer stack_bytes_overhead integer stack_bytes_total heap_bytes_data = MA_sizeof(MT_INT, 100, MT_BYTE) + MA_sizeof(MT_LOG, 200, MT_BYTE) heap_bytes_overhead = (5 + 4) * MA_sizeof_overhead(MT_BYTE) heap_bytes_total = heap_bytes_data + heap_bytes_overhead stack_bytes_data = MA_sizeof(MT_REAL, 300, MT_BYTE) + MA_sizeof(MT_DBL, 400, MT_BYTE) stack_bytes_overhead = (3 + 2) * MA_sizeof_overhead(MT_BYTE) stack_bytes_total = stack_bytes_data + stack_bytes_overhead ok = MA_init(MT_BYTE, stack_bytes_total, heap_bytes_total) .fi .SH DIAGNOSTICS unable to set sizes of FORTRAN datatypes .in +0.5i This indicates either that the internal state of MA is corrupted or that there is a problem in the C-FORTRAN linkage. .in invalid datatype: %d .in +0.5i .I datatype1 and .I datatype2 must be one of those listed in macdecls.h or mafdecls.fh. .in invalid nelem: %d .in +0.5i .I nelem1 must be greater than or equal to zero. .in .SH "RETURN VALUE" C: The number of elements, as described above. .br FORTRAN: The number of elements, as described above. .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_sizeof_overhead(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_sizeof_overhead.3000066400000000000000000000046461500715745200177610ustar00rootroot00000000000000.TH MA_SIZEOF_OVERHEAD 3 "20 February 1997" "MA Release 1.8" "MA LIBRARY ROUTINES" .SH NAME MA_sizeof_overhead - compute size of block overhead .SH "C SYNOPSIS" .nf #include "macdecls.h" Integer MA_sizeof_overhead(datatype) Integer datatype; /* read-only */ .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" integer function MA_sizeof_overhead(datatype) integer datatype .fi .SH DESCRIPTION MA_sizeof_overhead() returns the number of elements of type .I datatype required to contain the worst case number of bytes of overhead for any block. .SH USAGE The following FORTRAN code illustrates the use of MA_sizeof_overhead() by computing the space required for 100 integers in 5 allocations in the heap, 200 logicals in 4 allocations in the heap, 300 reals in 3 allocations in the stack, and 400 doubles in 2 allocations in the stack. .nf #include "mafdecls.fh" logical ok integer heap_bytes_data integer heap_bytes_overhead integer heap_bytes_total integer stack_bytes_data integer stack_bytes_overhead integer stack_bytes_total heap_bytes_data = MA_sizeof(MT_INT, 100, MT_BYTE) + MA_sizeof(MT_LOG, 200, MT_BYTE) heap_bytes_overhead = (5 + 4) * MA_sizeof_overhead(MT_BYTE) heap_bytes_total = heap_bytes_data + heap_bytes_overhead stack_bytes_data = MA_sizeof(MT_REAL, 300, MT_BYTE) + MA_sizeof(MT_DBL, 400, MT_BYTE) stack_bytes_overhead = (3 + 2) * MA_sizeof_overhead(MT_BYTE) stack_bytes_total = stack_bytes_data + stack_bytes_overhead ok = MA_init(MT_BYTE, stack_bytes_total, heap_bytes_total) .fi .SH DIAGNOSTICS unable to set sizes of FORTRAN datatypes .in +0.5i This indicates either that the internal state of MA is corrupted or that there is a problem in the C-FORTRAN linkage. .in invalid datatype: %d .in +0.5i .I datatype must be one of those listed in macdecls.h or mafdecls.fh. .in .SH "RETURN VALUE" C: The number of elements, as described above. .br FORTRAN: The number of elements, as described above. .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_sizeof(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_summarize_allocated_blocks.3000066400000000000000000000041131500715745200221530ustar00rootroot00000000000000.TH MA_SUMMARIZE_ALLOCATED_BLOCKS 3 "20 February 1997" "MA Release 1.8" " " .SH NAME MA_summarize_allocated_blocks - print information about currently allocated blocks on the standard output (stdout) .SH "C SYNOPSIS" .nf #include "macdecls.h" void MA_summarize_allocated_blocks() .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" subroutine MA_summarize_allocated_blocks .fi .SH DESCRIPTION MA_summarize_allocated_blocks() prints the following information on the standard output (stdout) for each block currently allocated on the heap or the stack. address .in +0.5i The location in memory of (i.e., pointer to) the block. .in handle .in +0.5i The memhandle by which the block is referenced (returned when the block was allocated). .in name .in +0.5i The name of the block (specified when the block was allocated). .in type of elements .in +0.5i The type of data elements stored in the block (specified when the block was allocated). .in number of elements .in +0.5i The number of data elements stored in the block (specified when the block was allocated). .in address of client space .in +0.5i The location in memory of (i.e., pointer to) the data elements. This is the value returned by MA_get_pointer(). .in index for client space .in +0.5i The base index for the appropriate type-specific data array. This is the value returned by MA_get_index(). .in total number of bytes .in +0.5i The total number of bytes, including overhead, in the block. .in .\" .SH USAGE .SH DIAGNOSTICS invalid index_base: %d .in +0.5i This indicates either that the internal state of MA is corrupted or that there is a problem in the C-FORTRAN linkage. .in .SH "RETURN VALUE" C: None. .br FORTRAN: None. .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_print_stats(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/man3/MA_verify_allocator_stuff.3000066400000000000000000000041411500715745200213460ustar00rootroot00000000000000.TH MA_VERIFY_ALLOCATOR_STUFF 3 "20 February 1997" "MA Release 1.8" " " .SH NAME MA_verify_allocator_stuff - sanity check the internal state of MA .SH "C SYNOPSIS" .nf #include "macdecls.h" Boolean MA_verify_allocator_stuff() .fi .SH "FORTRAN SYNOPSIS" .nf #include "mafdecls.fh" logical function MA_verify_allocator_stuff() .fi .SH DESCRIPTION MA_verify_allocator_stuff() checks each block currently allocated on the heap or the stack for checksum and guard errors. For each error found, a message is printed on the standard output (stdout). If any errors are found, a summary message is printed on the standard error (stderr), else nothing is printed. .\" .SH USAGE .SH DIAGNOSTICS unavailable; recompile MA with -DVERIFY .in +0.5i To use this routine, MA must be compiled with cpp option -DVERIFY. .in %s block '%s', handle %d, address 0x%lx: .in +0.5i The following diagnostics are each prefixed by this information, which identifies the corrupted block. .in current checksum %lu != stored checksum %lu .in +0.5i This indicates that the block has been corrupted by having its internal state overwritten. .in current left signature %u != proper left signature %u .in +0.5i This indicates that the block has been corrupted by being overwritten at the left end. The likely cause of this is an application indexing bug. .in current right signature %u != proper right signature %u .in +0.5i This indicates that the block has been corrupted by being overwritten at the right end. The likely cause of this is an application indexing bug. .in .SH "RETURN VALUE" C: MA_TRUE upon success (no errors), MA_FALSE upon failure (errors). .br FORTRAN: .true. upon success (no errors), .false. upon failure (errors). .\" .SH NOTES .SH FILES .nf Include files: /msrc/proj/hpctools/ma/release/current/include Library: /msrc/proj/hpctools/ma/release/current/lib/\fIplatform\fR/libma.a Man pages: /msrc/proj/hpctools/ma/release/current/man/man3 Source code: /msrc/proj/hpctools/ma/release/current/tar/ma\fIrelease\fR.tar .fi .SH "SEE ALSO" .na MA(3), MA_set_auto_verify(3) .ad .SH AUTHOR Gregory S. Thomas, Pacific Northwest National Laboratory ga-5.9.2/ma/man/whatis000066400000000000000000000032121500715745200145140ustar00rootroot00000000000000MA (3) - introduction to the MA dynamic memory allocator MA_alloc_get (3) - allocate a heap block and get the corresponding base index MA_allocate_heap (3) - allocate a heap block MA_chop_stack (3) - deallocate a stack block and all stack blocks allocated after it MA_free_heap (3) - deallocate a heap block MA_get_index (3) - get the base index for a block MA_get_next_memhandle (3) - get the handle for the next block in the scan of currently allocated blocks MA_get_pointer (3) - get the base pointer for a block MA_init (3) - initialize the memory allocator MA_initialized (3) - find out if MA has been successfully initialized MA_init_memhandle_iterator (3) - initialize a scan of currently allocated blocks MA_inquire_avail (3) - find out how much free space is available between the heap and stack regions MA_inquire_heap (3) - find out how much free space is available in the heap region MA_inquire_stack (3) - find out how much free space is available in the stack region MA_pop_stack (3) - deallocate a stack block MA_print_stats (3) - print MA usage statistics on the standard output (stdout) MA_push_get (3) - allocate a stack block and get the corresponding base index MA_push_stack (3) - allocate a stack block MA_set_auto_verify (3) - set the ma_auto_verify flag MA_set_error_print (3) - set the ma_error_print flag MA_set_hard_fail (3) - set the ma_hard_fail flag MA_sizeof (3) - compute sizes of data types MA_sizeof_overhead (3) - compute size of block overhead MA_summarize_allocated_blocks (3) - print information about currently allocated blocks on the standard output (stdout) MA_verify_allocator_stuff (3) - sanity check the internal state of MA ga-5.9.2/ma/matypes.h.in000066400000000000000000000014761500715745200147710ustar00rootroot00000000000000/** @file * Private header file containing C type definitions. * * This file should only be included directly by internal C * header files (e.g., macdecls.h). It may be included indirectly * by external C files that include the appropriate header * file (e.g., macdecls.h). */ #ifndef _MATYPES_H #define _MATYPES_H /** ** types **/ #include "typesf2c.h" typedef Integer Boolean; /* MA_TRUE or MA_FALSE */ typedef char * Pointer; /* generic pointer */ /* not all C compilers support long double */ typedef @MA_LONG_DOUBLE@ MA_LongDouble; /* no C compilers support complex types */ typedef struct {float dummy[2];} MA_SingleComplex; typedef struct {double dummy[2];} MA_DoubleComplex; typedef struct {double dummy[4];} MA_LongDoubleComplex; typedef @MA_ACCESS_INDEX_TYPE_C@ MA_AccessIndex; #endif /* _matypes_h */ ga-5.9.2/ma/memcpy.h000066400000000000000000000020051500715745200141610ustar00rootroot00000000000000/** @file * Private header file containing symbolic constants, type declarations, * and macro definitions for OS memory routines, to provide a level of * abstraction between them and routines that use them. * * This file should only be included by internal C files. */ #ifndef _memcpy_h #define _memcpy_h /** ** constants **/ /* ensure that NULL is defined */ #ifndef NULL #define NULL 0 #endif /** ** macros **/ /* allocate bytes */ #define bytealloc(nbytes) malloc((size_t)(nbytes)) /* deallocate bytes */ #define bytefree(pointer) (void)free((char *)(pointer)) #if HAVE_STRING_H # include #else # ifndef HAVE_STRCHR # define strchr index # define strrchr rindex # endif char *strchr (), *strrchr (); # ifndef HAVE_MEMCPY # define memcpy(d, s, n) bcopy ((s), (d), (n)) # define memmove(d, s, n) bcopy ((s), (d), (n)) # endif #endif #define bytecopy(from,to,nbytes) \ ((void)memcpy((char *)(to), (char *)(from), (int)(nbytes))) #endif /* _memcpy_h */ ga-5.9.2/ma/scope.h000066400000000000000000000005011500715745200137770ustar00rootroot00000000000000/** @file * Private header file containing symbolic constants for internal C routines. * * This file should only be included by internal C files. */ #ifndef _scope_h #define _scope_h /** ** constants **/ /* pseudo reserved words for scope control */ #define private static #define public #endif /* _scope_h */ ga-5.9.2/ma/string-util.c000066400000000000000000000071331500715745200151520ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /*$Id: string-util.c,v 1.2 1995-02-02 23:18:32 d3g681 Exp $*/ /* * string-util.c */ /* * : * * The standard string functions (strcat, strcmp, strcpy, strlen, etc.) * dereference their pointer arguments without first checking to see if * the arguments are NULL (0). On some machines (e.g., the VAX) this is * harmless, but on others (e.g., the DECstation 3100) it causes a * segmentation fault. * * The string utilities defined here provide functionality similar to that * of their standard counterparts, but they are careful not to dereference * arguments which are NULL. * * : * * A string is of type "pointer to char" (char *); a string is terminated * by a byte whose value is 0 (a null character). The term "Nstring" will * be used to mean "NULL string" (pointer value of 0); "Zstring" will be * used to mean "zero-length string" (nonzero pointer value, but no nonzero * bytes pointed to). is concerned with Nstrings. */ #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #include "string-util.h" /** ** public routines **/ /* ------------------------------------------------------------------------- */ /* * Return the length of (i.e., number of nonzero bytes in) the given string. */ /* ------------------------------------------------------------------------- */ unsigned int str_len(s) char *s; /* string */ { int length = 0; /* see */ if (!s) { /* length of Nstring is 0 */ return(0); } while (*s++) { length++; } return(length); } /* ------------------------------------------------------------------------- */ /* * Return the index of the string in slist for which s is a match; s "matches" * a string t in slist if (a) s and t are equal, or (b) s is a prefix of t but * is not a prefix of any other string in slist. * * If s matches no string in slist, return SM_NONE. If s matches more than * one string in slist, return SM_MANY. * * Restriction: slist can contain no Nstrings. */ /* ------------------------------------------------------------------------- */ int str_match(s, slist, n) char *s; /* string to match */ char *slist[]; /* list of strings to search */ unsigned int n; /* # of strings in slist */ { size_t i; /* loop index */ size_t length; /* of s */ int match; /* index of string in slist matched by s */ /* see */ if (!s) { return(SM_NONE); } /* we know s is not an Nstring */ length = strlen(s); match = -1; for (i = 0; i < n; i++) { /* s, slist[i] are not Nstrings */ if (!strncmp(s, slist[i], length)) { /* s is at least a prefix */ if (length == strlen(slist[i])) { /* exact match */ return(i); } /* now we know s is a proper prefix */ if (match < 0) { /* first match we've seen */ match = i; } else { /* s is a proper prefix of more than one string in slist */ return(SM_MANY); } } } if (match < 0) { /* s is not a prefix of any string in slist */ return(SM_NONE); } else { /* s is a proper prefix of exactly one string in slist */ return(match); } } ga-5.9.2/ma/string-util.h000066400000000000000000000005711500715745200151560ustar00rootroot00000000000000/** @file * Private header file for string utilities. * * This file should only be included by internal C files. */ #ifndef _string_util_h #define _string_util_h /** ** constants **/ /* str_match return values */ #define SM_NONE (-1) #define SM_MANY (-2) /** ** function types **/ extern unsigned int str_len(); extern int str_match(); #endif /* _string_util_h */ ga-5.9.2/ma/table.c000066400000000000000000000174321500715745200137630ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* * $Id: table.c,v 1.6 2000-10-13 23:18:18 d3h325 Exp $ */ /* * Dynamic table module. * * Each table entry consists of an external data item and internal state. * The size of the table is automatically increased if there isn't room * to add another entry. The client can allocate, deallocate, verify * entries, and perform both handle-->data and data-->handle lookup. */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #include "error.h" #include "memcpy.h" #include "scope.h" #include "table.h" /** ** constants **/ /* default # of initial table entries */ #define DEFAULT_TABLE_ENTRIES 32 /** ** types **/ /* state of a TableEntry */ typedef enum { TES_Unused = 0, /* never used */ TES_Allocated, /* currently in use */ TES_Deallocated /* formerly in use */ } TableEntryState; /* table entry consists of data and state */ typedef struct _TableEntry { TableData data; /* in this entry */ TableEntryState state; /* of this entry */ } TableEntry; /* table is an array of table entries */ typedef TableEntry * Table; /** ** variables **/ /* currently only one table is managed */ private Table ma_table = NULL; private Integer ma_table_capacity = 0; private Integer ma_table_entries = 0; private Integer ma_table_next_slot = 0; /** ** public routines for internal use only **/ /* ------------------------------------------------------------------------- */ /* * Allocate a table slot, store the given data into it, and return a handle * by which the slot may be referenced. If a slot cannot be allocated, * return TABLE_HANDLE_NONE. */ /* ------------------------------------------------------------------------- */ public Integer ma_table_allocate(data) TableData data; /* to store */ { Table new_ma_table; Integer new_ma_table_capacity; unsigned new_ma_table_size; Integer i; Integer slots_examined; /* expand the table if necessary */ if (ma_table_entries >= ma_table_capacity) { /* increase table capacity */ if (ma_table_capacity == 0) /* set the initial capacity */ new_ma_table_capacity = DEFAULT_TABLE_ENTRIES; else /* double the current capacity */ new_ma_table_capacity = 2 * ma_table_capacity; /* allocate space for new table */ new_ma_table_size = (unsigned)(new_ma_table_capacity * sizeof(TableEntry)); if ((new_ma_table = (Table)bytealloc(new_ma_table_size)) == (Table)NULL) { (void)sprintf(ma_ebuf, "could not allocate %u bytes for ma_table", new_ma_table_size); ma_error(EL_Nonfatal, ET_Internal, "ma_table_allocate", ma_ebuf); return TABLE_HANDLE_NONE; } /* copy and free old table */ if (ma_table_capacity > 0) { bytecopy(ma_table, new_ma_table, (ma_table_capacity * sizeof(TableEntry))); bytefree(ma_table); } /* initialize new part of new table */ for (i = new_ma_table_capacity-1; i >= ma_table_capacity; i--) new_ma_table[i].state = TES_Unused; /* remember the new table */ ma_table = new_ma_table; ma_table_next_slot = ma_table_capacity; ma_table_capacity = new_ma_table_capacity; } /* perform a linear circular search to find the next available slot */ for (slots_examined = 0, i = ma_table_next_slot; slots_examined < ma_table_capacity; slots_examined++, i = (i+1) % ma_table_capacity) { if (ma_table[i].state != TES_Allocated) { /* store the data */ ma_table[i].data = data; ma_table[i].state = TES_Allocated; /* increment ma_table_entries */ ma_table_entries++; /* advance ma_table_next_slot */ ma_table_next_slot = (i+1) % ma_table_capacity; /* return the handle */ return i; } } /* if we get here, something is wrong */ (void)sprintf(ma_ebuf, "no ma_table slot available, %ld/%ld slots used", (long)ma_table_entries, (long)ma_table_capacity); ma_error(EL_Nonfatal, ET_Internal, "ma_table_allocate", ma_ebuf); return TABLE_HANDLE_NONE; } /* ------------------------------------------------------------------------- */ /* * Deallocate the table slot corresponding to the given handle, * which should have been verified previously (e.g., via ma_table_verify()). * If handle is invalid, print an error message. */ /* ------------------------------------------------------------------------- */ public void ma_table_deallocate(handle) Integer handle; /* to deallocate */ { if (ma_table_verify(handle, "ma_table_deallocate")) { /* deallocate the slot */ ma_table[handle].state = TES_Deallocated; /* decrement ma_table_entries */ ma_table_entries--; } } /* ------------------------------------------------------------------------- */ /* * Return the data in the table slot corresponding to the given handle, * which should have been verified previously (e.g., via ma_table_verify()). * If handle is invalid, print an error message and return NULL. */ /* ------------------------------------------------------------------------- */ public TableData ma_table_lookup(handle) Integer handle; /* to lookup */ { if (ma_table_verify(handle, "ma_table_lookup")) /* success */ return ma_table[handle].data; else /* failure */ return (TableData)NULL; } /* ------------------------------------------------------------------------- */ /* * Return the handle for the table slot containing the given data (i.e., * perform an associative or inverted lookup), or TABLE_HANDLE_NONE if * no currently allocated table slot contains the given data. * * If more than one table slot contains the given data, the one whose * handle is returned is undefined (i.e., implementation dependent). */ /* ------------------------------------------------------------------------- */ public Integer ma_table_lookup_assoc(data) TableData data; /* to lookup */ { Integer i; /* perform a linear search from the first table slot */ for (i = 0; i < ma_table_capacity; i++) if ((ma_table[i].state == TES_Allocated) && (ma_table[i].data == data)) /* success */ return i; /* failure */ return TABLE_HANDLE_NONE; } /* ------------------------------------------------------------------------- */ /* * Return MA_TRUE if the given handle corresponds to a valid table slot * (one that is currently allocated), else return MA_FALSE and print an * error message. */ /* ------------------------------------------------------------------------- */ public Boolean ma_table_verify(handle, caller) Integer handle; /* to verify */ char *caller; /* name of calling routine */ { Boolean badhandle; /* is handle invalid? */ badhandle = MA_FALSE; /* if handle is invalid, construct an error message */ if ((handle < 0) || (handle >= ma_table_capacity) || (ma_table[handle].state == TES_Unused)) { (void)sprintf(ma_ebuf, "handle %ld is not valid", (long)handle); badhandle = MA_TRUE; } else if (ma_table[handle].state == TES_Deallocated) { (void)sprintf(ma_ebuf, "handle %ld already deallocated", (long)handle); badhandle = MA_TRUE; } if (badhandle) { /* invalid handle */ ma_error(EL_Nonfatal, ET_External, caller, ma_ebuf); return MA_FALSE; } else /* valid handle */ return MA_TRUE; } ga-5.9.2/ma/table.h000066400000000000000000000013211500715745200137560ustar00rootroot00000000000000/** @file * Private header file containing symbolic constants and type declarations * for the table module. * * This file should only be included by internal C files. */ #ifndef _table_h #define _table_h #include "matypes.h" /** ** constants **/ /* invalid handle */ #define TABLE_HANDLE_NONE (Integer)(-1) /** ** types **/ /* type of data in each table entry */ typedef char * TableData; /** ** function types **/ extern Integer ma_table_allocate(TableData data); extern void ma_table_deallocate(Integer handle); extern TableData ma_table_lookup(Integer handle); extern Integer ma_table_lookup_assoc(TableData data); extern Boolean ma_table_verify(Integer handle, char *caller); #endif /* _table_h */ ga-5.9.2/ma/test-coalesce.c000066400000000000000000000033341500715745200154230ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* * $Id: test-coalesce.c,v 1.4 2002-09-14 05:40:30 d3g001 Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #include "macdecls.h" #define MAXHANDLES 10 int main(int argc, char **argv) { Integer bytes_heap; Integer bytes_stack; int howmany; Boolean ok; Integer handle[MAXHANDLES]; MA_AccessIndex index[MAXHANDLES]; /* set sizes of heap and stack */ bytes_heap = 1024; bytes_stack = 0; /* initialize */ ok = MA_init(MT_CHAR, bytes_stack, bytes_heap); if (!ok) { (void)fprintf(stderr, "MA_init failed; punting\n"); exit(1); } howmany = MA_inquire_heap(MT_INT); (void)printf("MA_inquire_heap(MT_INT) = %d\n", howmany); printf("# Allocating 4 memory segments\n"); MA_alloc_get(MT_INT, howmany/5, "heap0", &handle[0], &index[0]); MA_alloc_get(MT_INT, howmany/5, "heap1", &handle[1], &index[1]); MA_alloc_get(MT_INT, howmany/5, "heap2", &handle[2], &index[2]); MA_alloc_get(MT_INT, howmany/5, "heap3", &handle[3], &index[3]); printf("# Deleting 2 non-adjacent memory segments\n"); MA_free_heap(handle[0]); MA_free_heap(handle[2]); printf("# Attempting to allocate memory segment -- should fail\n"); MA_alloc_get(MT_INT, howmany/4, "heap4", &handle[4], &index[4]); printf("# Freeing some memory and trying again -- should succeed\n"); MA_free_heap(handle[1]); MA_alloc_get(MT_INT, howmany/4, "heap4", &handle[4], &index[4]); printf("# Printing stats for allocated blocks -- should be two active\n"); MA_summarize_allocated_blocks(); return 0; } ga-5.9.2/ma/test-inquire.c000066400000000000000000000060161500715745200153210ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* * $Id: test-inquire.c,v 1.1 2002-09-14 05:40:30 d3g001 Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #include "macdecls.h" #define MAXHANDLES 10 static void print_inquire(); int main(int argc, char **argv) { Integer units_heap; Integer units_stack; Boolean ok; Integer handle[MAXHANDLES]; MA_AccessIndex index[MAXHANDLES]; /* set sizes of heap and stack */ units_heap = 50000; units_stack = 20000; /* initialize */ ok = MA_init(MT_DBL, units_stack, units_heap); if (!ok) { (void)fprintf(stderr, "MA_init failed; punting\n"); exit(1); } printf("# initialized heap = %ld, stack = %ld\n", (long)units_heap, (long)units_stack); printf("# should see roughly the following values:\n"); printf("# MA_inquire_heap(MT_DBL) = 50K\n"); printf("# MA_inquire_heap_check_stack(MT_DBL) = 50K\n"); printf("# MA_inquire_heap_no_partition(MT_DBL) = 70K\n"); printf("# MA_inquire_stack(MT_DBL) = 20K\n"); printf("# MA_inquire_stack_check_heap(MT_DBL) = 20K\n"); printf("# MA_inquire_stack_no_partition(MT_DBL) = 70K\n"); print_inquire(); printf("# allocate 2 heap (10K, 20K), 1 stack (35K)\n"); MA_alloc_get(MT_DBL, 10000, "heap0", &handle[0], &index[0]); MA_alloc_get(MT_DBL, 20000, "heap1", &handle[1], &index[1]); MA_push_get(MT_DBL, 35000, "stack0", &handle[2], &index[2]); printf("# free 1 heap (10K)\n"); MA_free_heap(handle[0]); printf("# should see roughly the following values:\n"); printf("# MA_inquire_heap(MT_DBL) = 20K\n"); printf("# MA_inquire_heap_check_stack(MT_DBL) = 10K\n"); printf("# MA_inquire_heap_no_partition(MT_DBL) = 10K\n"); printf("# MA_inquire_stack(MT_DBL) = 0\n"); printf("# MA_inquire_stack_check_heap(MT_DBL) = 0\n"); printf("# MA_inquire_stack_no_partition(MT_DBL) = 5K\n"); print_inquire(); return 0; } void print_inquire() { int howmany; (void)printf("--------------------------------------------------------\n"); howmany = MA_inquire_heap(MT_DBL); (void)printf("MA_inquire_heap(MT_DBL) = %d\n", howmany); howmany = MA_inquire_heap_check_stack(MT_DBL); (void)printf("MA_inquire_heap_check_stack(MT_DBL) = %d\n", howmany); howmany = MA_inquire_heap_no_partition(MT_DBL); (void)printf("MA_inquire_heap_no_partition(MT_DBL) = %d\n", howmany); howmany = MA_inquire_stack(MT_DBL); (void)printf("MA_inquire_stack(MT_DBL) = %d\n", howmany); howmany = MA_inquire_stack_check_heap(MT_DBL); (void)printf("MA_inquire_stack_check_heap(MT_DBL) = %d\n", howmany); howmany = MA_inquire_stack_no_partition(MT_DBL); (void)printf("MA_inquire_stack_no_partition(MT_DBL) = %d\n", howmany); (void)printf("--------------------------------------------------------\n"); } ga-5.9.2/ma/testc.c000066400000000000000000000277441500715745200140250ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* * $Id: testc.c,v 1.5 1999-05-27 16:31:16 d3h325 Exp $ */ /* * Test harness for portable dynamic memory allocator. */ #include "macdecls.h" #include "string-util.h" #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif /** ** constants **/ #define NUM_COMMANDS (int)(sizeof(commands) / sizeof(char *)) /** ** types **/ typedef enum { C_MA_alloc_get = 0, C_MA_allocate_heap, C_MA_chop_stack, C_MA_free_heap, C_MA_get_index, C_MA_get_next_memhandle, C_MA_get_pointer, C_MA_init, C_MA_init_memhandle_iterator, C_MA_inquire_avail, C_MA_inquire_heap, C_MA_inquire_stack, C_MA_pop_stack, C_MA_print_stats, C_MA_push_get, C_MA_push_stack, C_MA_set_auto_verify, C_MA_set_error_print, C_MA_set_hard_fail, C_MA_sizeof, C_MA_sizeof_overhead, C_MA_summarize_allocated_blocks, C_MA_verify_allocator_stuff, C_bye, C_exit, C_help, C_quit, C_questionmark } Command; /** ** variables **/ static char *commands[] = { "MA_alloc_get", "MA_allocate_heap", "MA_chop_stack", "MA_free_heap", "MA_get_index", "MA_get_next_memhandle", "MA_get_pointer", "MA_init", "MA_init_memhandle_iterator", "MA_inquire_avail", "MA_inquire_heap", "MA_inquire_stack", "MA_pop_stack", "MA_print_stats", "MA_push_get", "MA_push_stack", "MA_set_auto_verify", "MA_set_error_print", "MA_set_hard_fail", "MA_sizeof", "MA_sizeof_overhead", "MA_summarize_allocated_blocks", "MA_verify_allocator_stuff", "bye", "exit", "help", "quit", "?" }; static char *help[] = { "MA_alloc_get(datatype, nelem, name, memhandle, index)", "MA_allocate_heap(datatype, nelem, name, memhandle)", "MA_chop_stack(memhandle)", "MA_free_heap(memhandle)", "MA_get_index(memhandle, index)", "MA_get_next_memhandle(ithandle, memhandle)", "MA_get_pointer(memhandle, pointer)", "MA_init(datatype, nominal_stack, nominal_heap)", "MA_init_memhandle_iterator(ithandle)", "MA_inquire_avail(datatype)", "MA_inquire_heap(datatype)", "MA_inquire_stack(datatype)", "MA_pop_stack(memhandle)", "MA_print_stats()", "MA_push_get(datatype, nelem, name, memhandle, index)", "MA_push_stack(datatype, nelem, name, memhandle)", "MA_set_auto_verify(value)", "MA_set_error_print(value)", "MA_set_hard_fail(value)", "MA_sizeof(datatype1, nelem1, datatype2)", "MA_sizeof_overhead(datatype)", "MA_summarize_allocated_blocks()", "MA_verify_allocator_stuff()", "bye", "exit", "help", "quit", "?" }; /** ** public routines **/ /* ------------------------------------------------------------------------- */ /* * Interactive interface to MA routines. */ /* ------------------------------------------------------------------------- */ int main(argc, argv) int argc; char *argv[]; { char s[81]; /* string buffer */ int c; /* index of command */ int ii1; /* in int buffer */ int ii2; /* in int buffer */ int ii3; /* in int buffer */ Integer oi1; /* out int buffer */ MA_AccessIndex oi2; /* out int buffer */ Pointer op; /* out Pointer buffer */ int value; /* return value buffer */ while (1) { printf("testc> "); if (scanf("%80s", s) != 1) { printf("*** Input read failed; exiting.\n"); exit(1); } c = str_match(s, commands, NUM_COMMANDS); if (c == SM_MANY) { printf("*** '%s' is ambiguous\n", s); continue; } else if (c == SM_NONE) { printf("*** Unrecognized command '%s'\n", s); continue; } switch ((Command)c) { case C_MA_alloc_get: if (scanf("%d %d %80s", &ii1, &ii2, s) != 3) printf("*** Input read failed for %s\n", commands[c]); else { value = MA_alloc_get(ii1, ii2, s, &oi1, &oi2); printf("%s=%d memhandle=%ld index=%ld\n", commands[c], value, (long)oi1, (long)oi2); } break; case C_MA_allocate_heap: if (scanf("%d %d %80s", &ii1, &ii2, s) != 3) printf("*** Input read failed for %s\n", commands[c]); else { value = MA_allocate_heap(ii1, ii2, s, &oi1); printf("%s=%d memhandle=%ld\n", commands[c], value, (long)oi1); } break; case C_MA_chop_stack: if (scanf("%d", &ii1) != 1) printf("*** Input read failed for %s\n", commands[c]); else { value = MA_chop_stack(ii1); printf("%s=%d\n", commands[c], value); } break; case C_MA_free_heap: if (scanf("%d", &ii1) != 1) printf("*** Input read failed for %s\n", commands[c]); else { value = MA_free_heap(ii1); printf("%s=%d\n", commands[c], value); } break; case C_MA_get_index: if (scanf("%d", &ii1) != 1) printf("*** Input read failed for %s\n", commands[c]); else { value = MA_get_index(ii1, &oi2); printf("%s=%d index=%ld\n", commands[c], value, (long)oi2); } break; case C_MA_get_next_memhandle: if (scanf("%d", &ii1) != 1) printf("*** Input read failed for %s\n", commands[c]); else { Integer tmp = ii1; value = MA_get_next_memhandle(&tmp, &oi1); printf("%s=%d ithandle=%ld memhandle=%ld\n", commands[c], value, (long)tmp, (long)oi1); } break; case C_MA_get_pointer: if (scanf("%d", &ii1) != 1) printf("*** Input read failed for %s\n", commands[c]); else { value = MA_get_pointer(ii1, &op); printf("%s=%d pointer=%lu\n", commands[c], value, (unsigned long)op); } break; case C_MA_init: if (scanf("%d %d %d", &ii1, &ii2, &ii3) != 3) printf("*** Input read failed for %s\n", commands[c]); else { value = MA_init(ii1, ii2, ii3); printf("%s=%d\n", commands[c], value); } break; case C_MA_init_memhandle_iterator: value = MA_init_memhandle_iterator(&oi1); printf("%s=%d ithandle=%ld\n", commands[c], value, (long)oi1); break; case C_MA_inquire_avail: if (scanf("%d", &ii1) != 1) printf("*** Input read failed for %s\n", commands[c]); else { oi1 = MA_inquire_avail(ii1); printf("%s=%ld\n", commands[c], (long)oi1); } break; case C_MA_inquire_heap: if (scanf("%d", &ii1) != 1) printf("*** Input read failed for %s\n", commands[c]); else { oi1 = MA_inquire_heap(ii1); printf("%s=%ld\n", commands[c], (long)oi1); } break; case C_MA_inquire_stack: if (scanf("%d", &ii1) != 1) printf("*** Input read failed for %s\n", commands[c]); else { oi1 = MA_inquire_stack(ii1); printf("%s=%ld\n", commands[c], (long)oi1); } break; case C_MA_pop_stack: if (scanf("%d", &ii1) != 1) printf("*** Input read failed for %s\n", commands[c]); else { value = MA_pop_stack(ii1); printf("%s=%d\n", commands[c], value); } break; case C_MA_print_stats: MA_print_stats(1); break; case C_MA_push_get: if (scanf("%d %d %80s", &ii1, &ii2, s) != 3) printf("*** Input read failed for %s\n", commands[c]); else { value = MA_push_get(ii1, ii2, s, &oi1, &oi2); printf("%s=%d memhandle=%ld index=%ld\n", commands[c], value, (long)oi1, (long)oi2); } break; case C_MA_push_stack: if (scanf("%d %d %80s", &ii1, &ii2, s) != 3) printf("*** Input read failed for %s\n", commands[c]); else { value = MA_push_stack(ii1, ii2, s, &oi1); printf("%s=%d memhandle=%ld\n", commands[c], value, (long)oi1); } break; case C_MA_set_auto_verify: if (scanf("%d", &ii1) != 1) printf("*** Input read failed for %s\n", commands[c]); else { value = MA_set_auto_verify(ii1); printf("%s=%d\n", commands[c], value); } break; case C_MA_set_error_print: if (scanf("%d", &ii1) != 1) printf("*** Input read failed for %s\n", commands[c]); else { value = MA_set_error_print(ii1); printf("%s=%d\n", commands[c], value); } break; case C_MA_set_hard_fail: if (scanf("%d", &ii1) != 1) printf("*** Input read failed for %s\n", commands[c]); else { value = MA_set_hard_fail(ii1); printf("%s=%d\n", commands[c], value); } break; case C_MA_sizeof: if (scanf("%d %d %d", &ii1, &ii2, &ii3) != 3) printf("*** Input read failed for %s\n", commands[c]); else { oi1 = MA_sizeof(ii1, ii2, ii3); printf("%s=%ld\n", commands[c], (long)oi1); } break; case C_MA_sizeof_overhead: if (scanf("%d", &ii1) != 1) printf("*** Input read failed for %s\n", commands[c]); else { oi1 = MA_sizeof_overhead(ii1); printf("%s=%ld\n", commands[c], (long)oi1); } break; case C_MA_summarize_allocated_blocks: MA_summarize_allocated_blocks(); break; case C_MA_verify_allocator_stuff: value = MA_verify_allocator_stuff(); printf("%s=%d\n", commands[c], value); break; case C_bye: /* fall through */ case C_exit: /* fall through */ case C_quit: exit(0); case C_help: /* fall through */ case C_questionmark: for (ii1 = 0; ii1 < NUM_COMMANDS; ii1++) printf(" %s\n", help[ii1]); break; default: printf("*** Unrecognized case '%d' in switch\n", c); } } return 0; } ga-5.9.2/ma/testf.F000066400000000000000000000431461500715745200137650ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c c $Id: testf.F,v 1.6 1997-02-26 20:39:23 d3h325 Exp $ c c c Exercise the MA routines. c program testf implicit none #include "mafdecls.fh" character char1 character*2 char2 character char5(5) integer handle1 integer handle2 integer handle3 integer handle4 integer i integer ihandle1 MA_ACCESS_INDEX_TYPE index1 MA_ACCESS_INDEX_TYPE index2 logical value c ok write (*,*) 'start testing MA_set_error_print ...' value = MA_set_error_print(.true.) write (*,*) 'should see an MA error message here ...' value = MA_pop_stack(-1) value = MA_set_error_print(.false.) if (value) then write (*,*) 'should not see an MA error message here ...' value = MA_pop_stack(-1) else write (*,*) '... failure; return value is wrong' endif write (*,*) 'stop testing MA_set_error_print' write (*,*) ' ' value = MA_set_error_print(.true.) c ok write (*,*) 'should be 9 values ...' write (*,*) '1. MA_sizeof(MT_BYTE, 1, MT_BYTE) = ', $ MA_sizeof(MT_BYTE, 1, MT_BYTE) write (*,*) '2. MA_sizeof(MT_INT, 1, MT_BYTE) = ', $ MA_sizeof(MT_INT, 1, MT_BYTE) write (*,*) '3. MA_sizeof(MT_LOG, 1, MT_BYTE) = ', $ MA_sizeof(MT_LOG, 1, MT_BYTE) write (*,*) '4. MA_sizeof(MT_REAL, 1, MT_BYTE) = ', $ MA_sizeof(MT_REAL, 1, MT_BYTE) write (*,*) '5. MA_sizeof(MT_DBL, 1, MT_BYTE) = ', $ MA_sizeof(MT_DBL, 1, MT_BYTE) write (*,*) '6. MA_sizeof(MT_SCPL, 1, MT_BYTE) = ', $ MA_sizeof(MT_SCPL, 1, MT_BYTE) write (*,*) '7. MA_sizeof(MT_DCPL, 1, MT_BYTE) = ', $ MA_sizeof(MT_DCPL, 1, MT_BYTE) write (*,*) '8. MA_sizeof(MT_BYTE, 3, MT_DBL) = ', $ MA_sizeof(MT_BYTE, 3, MT_DBL) write (*,*) '9. MA_sizeof(MT_BYTE, 33, MT_DBL) = ', $ MA_sizeof(MT_BYTE, 33, MT_DBL) write (*,*) ' ' c ok write (*,*) 'should be 2 values ...' write (*,*) '1. MA_sizeof_overhead(MT_BYTE) = ', $ MA_sizeof_overhead(MT_BYTE) write (*,*) '2. MA_sizeof_overhead(MT_INT) = ', $ MA_sizeof_overhead(MT_INT) write (*,*) ' ' c fail write (*,*) 'should fail (not init) ...' value = MA_push_stack(MT_DBL, 10, 'stack1', handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c ok value = MA_initialized() if (value) then write (*,*) 'MA_initialized returns true (failure)' else write (*,*) 'MA_initialized returns false (success)' endif write (*,*) ' ' c fail write (*,*) 'should fail (bad datatype) ...' value = MA_init(-1, 10, 10) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c ok write (*,*) 'should succeed ...' value = MA_init(MT_DBL, 1000, 1000) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c ok value = MA_initialized() if (value) then write (*,*) 'MA_initialized returns true (success)' else write (*,*) 'MA_initialized returns false (failure)' endif write (*,*) ' ' c ok write (*,*) 'should be 1 value ...' write (*,*) '1. MA_sizeof_overhead(MT_BYTE) = ', $ MA_sizeof_overhead(MT_BYTE) write (*,*) ' ' c fail write (*,*) 'should fail (bad handle) ...' handle1 = 0 value = MA_pop_stack(handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c fail write (*,*) 'should fail (bad handle) ...' handle1 = 37 value = MA_pop_stack(handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c fail write (*,*) 'should fail (bad handle) ...' handle1 = 10000 value = MA_pop_stack(handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c fail write (*,*) 'should fail (bad handle) ...' value = MA_get_index(handle1, index1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c ok write (*,*) 'start testing MA_chop_stack ...' value = MA_push_stack(MT_BYTE, 1, 'stack1', handle1) value = MA_push_stack(MT_BYTE, 2, 'stack2', handle2) value = MA_push_stack(MT_BYTE, 3, 'stack3', handle3) value = MA_push_stack(MT_BYTE, 4, 'stack4', handle4) write (*,*) 'should be 4 blocks on stack ...' call MA_summarize_allocated_blocks write (*,*) 'should succeed ...' value = MA_chop_stack(handle4) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) 'should be 3 blocks on stack ...' call MA_summarize_allocated_blocks write (*,*) 'should fail (not in stack) ...' value = MA_chop_stack(handle4) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) 'should succeed ...' value = MA_chop_stack(handle2) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) 'should be 1 block on stack ...' call MA_summarize_allocated_blocks write (*,*) 'should succeed ...' value = MA_chop_stack(handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) 'should be 0 blocks ...' call MA_summarize_allocated_blocks value = MA_push_stack(MT_BYTE, 1, 'stack1', handle1) do 10 i = 1, 33 value = MA_push_stack(MT_BYTE, 1, 'stackn', handle2) 10 continue value = MA_chop_stack(handle1) write (*,*) 'stop testing MA_chop_stack' write (*,*) ' ' c ok write (*,*) 'start testing 0-length stack allocations ...' value = MA_push_get(MT_INT, 1, 'stack1', handle1, index1) value = MA_push_get(MT_INT, 0, 'stack2', handle2, index2) value = MA_push_stack(MT_BYTE, 0, 'stack3', handle3) value = MA_push_stack(MT_BYTE, 4, 'stack4', handle4) int_mb(index1) = 123 int_mb(index2) = 0 write (*,*) 'should fail (bad right sig on stack2) ...' value = MA_verify_allocator_stuff() if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) 'should succeed ...' value = MA_chop_stack(handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) 'stop testing 0-length stack allocations ...' write (*,*) ' ' c ok write (*,*) 'start testing 0-length heap allocations ...' value = MA_alloc_get(MT_INT, 1, 'heap1', handle1, index1) value = MA_alloc_get(MT_INT, 0, 'heap2', handle2, index2) value = MA_allocate_heap(MT_BYTE, 0, 'heap3', handle3) value = MA_allocate_heap(MT_BYTE, 4, 'heap4', handle4) int_mb(index1) = 123 value = MA_free_heap(handle4) value = MA_free_heap(handle3) value = MA_free_heap(handle2) value = MA_free_heap(handle1) write (*,*) 'should be 0 blocks ...' call MA_summarize_allocated_blocks write (*,*) 'stop testing 0-length heap allocations ...' write (*,*) ' ' c ok write (*,*) 'should succeed ...' value = MA_push_stack(MT_DBL, 10, 'stack1', handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) 'should succeed ...' value = MA_get_index(handle1, index1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif dbl_mb(index1) = 19.82 dbl_mb(index1 + 9) = dbl_mb(index1) - 19 write (*,*) 'should be 19.82 and 0.82 ...' write (*,*) 'dbl_mb(', index1, ') = ', dbl_mb(index1) write (*,*) 'dbl_mb(', index1 + 9, ') = ', dbl_mb(index1 + 9) write (*,*) ' ' c ok write (*,*) 'should succeed ...' value = MA_allocate_heap(MT_INT, 10, 'heap1', handle2) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c fail write (*,*) 'should fail (not in heap) ...' value = MA_free_heap(handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c fail write (*,*) 'should fail (not in stack) ...' value = MA_pop_stack(handle2) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c ok write (*,*) 'should succeed ...' value = MA_push_stack(MT_BYTE, 5, 'stack2', handle3) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c ok write (*,*) 'should see something here ...' call MA_summarize_allocated_blocks write (*,*) ' ' c fail write (*,*) 'should fail (not top of stack) ...' value = MA_pop_stack(handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c ok write (*,*) 'should succeed ...' value = MA_pop_stack(handle3) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c ok write (*,*) 'should succeed ...' value = MA_pop_stack(handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c ok write (*,*) 'should succeed ...' value = MA_get_index(handle2, index2) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif int_mb(index2) = 1963 int_mb(index2 + 9) = int_mb(index2) - 1900 write (*,*) 'should be 1963 and 63 ...' write (*,*) 'int_mb(', index2, ') = ', int_mb(index2) write (*,*) 'int_mb(', index2 + 9, ') = ', int_mb(index2 + 9) write (*,*) ' ' c ok write (*,*) 'should succeed ...' value = MA_allocate_heap(MT_REAL, 1, 'heap2', handle3) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c ok write (*,*) 'start testing MA_verify_allocator_stuff ...' write (*,*) 'should see nothing here ...' value = MA_verify_allocator_stuff() write (*,*) 'should succeed ...' if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) 'stop testing MA_verify_allocator_stuff' write (*,*) ' ' c fail write (*,*) 'should fail (bad right guard) ...' int_mb(index2 + 10) = 0 value = MA_free_heap(handle2) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c ok write (*,*) 'should succeed ...' value = MA_set_auto_verify(.true.) value = MA_set_auto_verify(.true.) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c fail write (*,*) 'start testing MA_set_auto_verify ...' write (*,*) 'should see something here ...' value = MA_set_auto_verify(.true.) value = MA_free_heap(handle2) write (*,*) 'should fail (bad right guard) ...' if (value) then write (*,*) '... success' else write (*,*) '... failure' endif value = MA_set_auto_verify(.false.) write (*,*) 'stop testing MA_set_auto_verify' write (*,*) ' ' c fail write (*,*) 'start testing MA_verify_allocator_stuff ...' write (*,*) 'should see something here ...' value = MA_verify_allocator_stuff() write (*,*) 'should fail (bad right guard) ...' if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) 'stop testing MA_verify_allocator_stuff' write (*,*) ' ' c ok write (*,*) 'should be 3 values ...' write (*,*) '1. MA_inquire_avail(MT_DBL) = ', $ MA_inquire_avail(MT_DBL) write (*,*) '2. MA_inquire_avail(MT_BYTE) = ', $ MA_inquire_avail(MT_BYTE) write (*,*) '3. MA_inquire_avail(MT_DCPL) = ', $ MA_inquire_avail(MT_DCPL) write (*,*) ' ' c fail write (*,*) 'should be 3 values ...' write (*,*) '1. MA_inquire_heap(MT_DBL) = ', $ MA_inquire_heap(MT_DBL) write (*,*) '2. MA_inquire_heap(MT_BYTE) = ', $ MA_inquire_heap(MT_BYTE) write (*,*) '3. MA_inquire_heap(MT_DCPL) = ', $ MA_inquire_heap(MT_DCPL) write (*,*) ' ' c fail write (*,*) 'should be 3 values ...' write (*,*) '1. MA_inquire_stack(MT_DBL) = ', $ MA_inquire_stack(MT_DBL) write (*,*) '2. MA_inquire_stack(MT_BYTE) = ', $ MA_inquire_stack(MT_BYTE) write (*,*) '3. MA_inquire_stack(MT_DCPL) = ', $ MA_inquire_stack(MT_DCPL) write (*,*) ' ' c ok write (*,*) 'should succeed ...' value = MA_alloc_get(MT_INT, 1, 'heap3', handle1, index1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif int_mb(index1) = 1982 write (*,*) 'should be 1982 ...' write (*,*) 'int_mb(', index1, ') = ', int_mb(index1) write (*,*) ' ' c ok write (*,*) 'should succeed ...' value = MA_free_heap(handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c ok write (*,*) 'start testing byte arrays ...' write (*,*) 'should succeed ...' value = MA_push_get(MT_BYTE, 5, 'stack3', handle1, index1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif char5(1) = 'z' char5(2) = 'y' char5(3) = 'x' char5(4) = 'w' char5(5) = 'v' do 20 i = 0, 4 byte_mb(index1 + i) = char5(i + 1) 20 continue write (*,*) 'should be zyxwv ...' do 30 i = 0, 4 char1 = byte_mb(index1+i) write (*,*) 'byte_mb(', index1+i, ') = ', char1 30 continue write (*,*) 'should succeed ...' value = MA_pop_stack(handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) 'should succeed ...' i = 1 char2 = 'ab' value = MA_alloc_get(MT_BYTE, i * 2, 'heap4', handle1, index1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif byte_mb(index1) = char2(1:1) byte_mb(index1 + 1) = char2(2:2) write (*,*) 'should be ab ...' do 40 i = 0, 1 char1 = byte_mb(index1+i) write (*,*) 'byte_mb(', index1+i, ') = ', char1 40 continue write (*,*) 'should succeed ...' value = MA_free_heap(handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) 'stop testing byte arrays ...' write (*,*) ' ' c ok write (*,*) 'should succeed ...' value = MA_push_get(MT_INT, 1, 'stack3', handle1, index1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif int_mb(index1) = 5 write (*,*) 'should be 5 ...' write (*,*) 'int_mb(', index1, ') = ', int_mb(index1) write (*,*) ' ' c ok write (*,*) 'should succeed ...' value = MA_pop_stack(handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c ok write (*,*) 'start testing MA_print_stats ...' call MA_print_stats(.true.) write (*,*) 'stop testing MA_print_stats' write (*,*) ' ' c fail write (*,*) 'should fail (not implemented) ...' value = MA_init_memhandle_iterator(ihandle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c fail write (*,*) 'should fail (not implemented) ...' value = MA_get_next_memhandle(ihandle1, handle1) if (value) then write (*,*) '... success' else write (*,*) '... failure' endif write (*,*) ' ' c ok write (*,*) 'start testing MA_set_hard_fail ...' value = MA_set_hard_fail(.false.) write (*,*) 'should see a (nonfatal) MA error message here ...' value = MA_pop_stack(-1) value = MA_set_hard_fail(.true.) value = MA_set_hard_fail(.true.) if (value) then write (*,*) 'should see a (hard failure)', $ ' MA error message here ...' value = MA_pop_stack(-1) else write (*,*) '... failure; return value is wrong' endif write (*,*) 'stop testing MA_set_hard_fail' end ga-5.9.2/pario/000077500000000000000000000000001500715745200132365ustar00rootroot00000000000000ga-5.9.2/pario/CMakeLists.txt000066400000000000000000000104571500715745200160050ustar00rootroot00000000000000# # module: CMakeLists.txt # author: Bruce Palmer # description: CMake build for GA. Only MPI-based runtimes are supported. # # DISCLAIMER # # This material was prepared as an account of work sponsored by an # agency of the United States Government. Neither the United States # Government nor the United States Department of Energy, nor Battelle, # nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR # ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, # COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, # SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT # INFRINGE PRIVATELY OWNED RIGHTS. # # # ACKNOWLEDGMENT # # This software and its documentation were produced with United States # Government support under Contract Number DE-AC06-76RLO-1830 awarded by # the United States Department of Energy. The United States Government # retains a paid-up non-exclusive, irrevocable worldwide license to # reproduce, prepare derivative works, perform publicly and display # publicly by or for the US Government, including the right to # distribute to other US Government contractors. # # -*- mode: cmake -*- # ------------------------------------------------------------- # file: CMakeLists.txt # ------------------------------------------------------------- # ------------------------------------------------------------- # PARIO header installation # ------------------------------------------------------------- if (ENABLE_FORTRAN) set(DRA_FORTRAN_HEADERS dra/dra.fh dra/draf2c.h) set(DRA_FORTRAN_FILES dra/fortran.c) set(EAF_FORTRAN_HEADERS eaf/eaf.fh) set(EAF_FORTRAN_FILES eaf/eaf_f2c.c) set(SF_FORTRAN_HEADERS sf/sf.fh sf/sff2c.h) set(SF_FORTRAN_FILES sf/sf_fortran.c) endif() set(DRA_HEADERS dra/dra.h # dra/buffers.h # dra/drap.h ${DRA_FORTRAN_HEADERS} ) set(EAF_HEADERS eaf/eaf.h # eaf/eafP.h ${EAF_FORTRAN_HEADERS} ) set(ELIO_HEADERS elio/elio.h elio/chemio.h # elio/eliop.h # elio/pablo.h ) set(SF_HEADERS sf/sf.h sf/coms.h ${SF_FORTRAN_HEADERS} ) install (FILES ${DRA_HEADERS} ${EAF_HEADERS} ${ELIO_HEADERS} ${SF_HEADERS} DESTINATION include/ga ) list (APPEND GA_HEADER_PATHS ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/dra ${CMAKE_CURRENT_LIST_DIR}/sf ${CMAKE_CURRENT_LIST_DIR}/eaf ${CMAKE_CURRENT_LIST_DIR}/elio) set (GA_HEADER_PATHS ${GA_HEADER_PATHS} PARENT_SCOPE) # ------------------------------------------------------------- # PARIO executable files # ------------------------------------------------------------- set(DRA_FILES dra/capi.c dra/disk.arrays.c dra/disk.param.c dra/env.c dra/buffers.c dra/fortran.c # dra/global.unsup.c dra/patch.util.c # dra/util.c ${DRA_FORTRAN_FILES} ) set(EAF_FILES eaf/eaf.c eaf/eaf_f2c.c ${EAF_FORTRAN_FILES} ) set(ELIO_FILES elio/elio.c elio/stat.c ) set(SF_FILES sf/shared.files.c sf/sf_capi.c sf/sf_fortran.c ${SF_FORTRAN_FILES} ) # ------------------------------------------------------------- # PARIO library installation # ------------------------------------------------------------- set(PARIO_INC_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${PROJECT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/dra ${CMAKE_CURRENT_SOURCE_DIR}/eaf ${CMAKE_CURRENT_SOURCE_DIR}/elio ${CMAKE_CURRENT_SOURCE_DIR}/sf ${PROJECT_BINARY_DIR}/gaf2c ${PROJECT_SOURCE_DIR}/ma ${PROJECT_BINARY_DIR}/ma ${PROJECT_SOURCE_DIR}/global/src) add_library(sf OBJECT ${SF_FILES}) add_library(dra OBJECT ${DRA_FILES}) add_library(eaf OBJECT ${EAF_FILES}) add_library(elio OBJECT ${ELIO_FILES}) target_include_directories(sf BEFORE PRIVATE ${PARIO_INC_DIRS}) target_include_directories(dra BEFORE PRIVATE ${PARIO_INC_DIRS}) target_include_directories(eaf BEFORE PRIVATE ${PARIO_INC_DIRS}) target_include_directories(elio BEFORE PRIVATE ${PARIO_INC_DIRS}) if (ENABLE_FORTRAN) add_dependencies(sf GenerateConfigFH) add_dependencies(dra GenerateConfigFH) add_dependencies(eaf GenerateConfigFH) add_dependencies(elio GenerateConfigFH) endif() ga-5.9.2/pario/doc/000077500000000000000000000000001500715745200140035ustar00rootroot00000000000000ga-5.9.2/pario/doc/DRAapi.html000066400000000000000000000007301500715745200157710ustar00rootroot00000000000000 Disk Resident Arrays Operations <body> Viewing this page requires browser capable of displaying frames </body> ga-5.9.2/pario/doc/EAFapi.html000066400000000000000000000007151500715745200157610ustar00rootroot00000000000000 EAF Programmers Interface <BODY> Viewing this page requires a browser capable of displaying frames. </BODY> ga-5.9.2/pario/doc/SFapi.html000066400000000000000000000006361500715745200157000ustar00rootroot00000000000000 Shared Files Library <BODY> Viewing this page requires a browser capable of displaying frames. </BODY> ga-5.9.2/pario/doc/api_eaf.html000066400000000000000000000230631500715745200162610ustar00rootroot00000000000000

Exclusive Access File Library

An exclusive access file is a file which is generated and/or read by a single process of a distributed parallel application. Files are not shared between different processes. The  library is an abstract high-performance file system which provides a common interface for a variety of architecture specific parallel storage systems.  The library also makes available features like asynchronous input and output to Fortran.  EAF's syntax is similar to the standard Unix C file operations, differences indicate new semantics or extended features available through EAF.

EAF_Open

Description:

    Open a file.
Prototype:
    integer EAF_Open(fname, type, fh)
      character *(*) fname
      integer type
      integer fd
Parameters:
    fname
      Character string of a globally unique filename (path may be fully qualified)
    type
      Read write permissions. Legal values are EAF_W, EAF_R, and EAF_RW
    fh
      File descriptor (handle).
Returns:
    0 or error code

EAF_Write

Description:

    Synchronously write to the file specified by the file handle
Prototype:
    integer EAF_Write(fh, offset, buf, bytes)
      integer fh
      double offset
      _______ buf
      double bytes
Parameters:
    fh
      File Handle offset Absolute offset, in bytes, to start writing at
    buf
      Scalar or array of data
    bytes
      Size of buffer in bytes
Returns:
    0 or error code if an error occured

EAF_AWrite

Description:

    Asynchronously write to the file specified by the file handle, and return a handle to the asynchronous operation.

    If there are more than MAX_AIO_REQ asynchronous requests (reading and/or writing) pending, the operation is handled in a synchronous fashion and returns a CHEMIO_DONE handle.

    On architectures where asynchronous I/O operations are not supported, all requests are handled synchronously, returning a CHEMIO_DONE handle.

Prototype:
    integer EAF_AWrite(fh, offset, buf, bytes, req_id)
      integer fh
      double offset
      _______
          buf
      double bytes
      integer req_id
Parameters:
    fh
      File Handle
    offset
      Absolute offset, in bytes, to start writing at
    buf
      Scalar or array of data
    bytes
      Size of buffer, in bytes
    req_id
      Handle of asynchronous operation
Returns:
    0 or error code if an error occured

EAF_Read

Description:

    Synchronously read from the file specified by the file handle
Prototype:
    integer EAF_Read(fh, offset, buf, bytes)
      integer fh
      double offset
      _______
          buf
      double bytes
      integer req_id
Parameters:
    fh
      File Handle
    offset
      Absolute offset, in bytes, to start writing at
    buf
      Scalar or array of data
    bytes
      Size of buffer, in bytes
    Returns:
      0, or error code if an error occured

EAF_ARead

Description:

    Asynchronously read from the file specified by the file handle, and return a handle to the asynchronous operation.

    If there are more than MAX_AIO_REQ asynchronous requests (reading and/or writing) pending, the operation is handled in a synchronous fashion and returns a EAF_DONE handle.

    On architectures where asynchronous I/O operations are not supported, all requests are handled synchronously, returning a EAF_DONE handle.

Prototype:
    integer EAF_ARead(fh, offset, buf,bytes, req_id)
      integer fh
      double offset
      _______
          buf
      double bytes
      integer req_id
Parameters:
    fh
      File Handle
    offset
      Absolute offset, in bytes, to start reading from
    buf
      Scalar or array of data
    bytes
      Size of buffer, in bytes
    req_id
      Handle of asynchronous operation
Returns:
    0 if succeeded, or error code if an error occured

EAF_Probe

Description:

    Determine if an asynchronous request has completed or is pending.
Prototype:
    integer EAF_Probe(id, status)
      integer id
      integer status
Parameters:
    id
      Handle of asynchronous request
    status Pending or completed status argument
Returns:
    0 if probe succeeded, else returns error code .

    status returns 0 if the asynchronous operation is complete, or 1 otherwise.

    If the asynchronous request is complete, id is invalidated.


EAF_Wait

Description:

    Wait for the completion of the asynchronous request associated with id
Prototype:
    integer EAF_Wait(id)
      integer id
Parameters:
    id
      Handle of asynchronous request
Returns:
    0 if EAF is able to wait for completion, else returns error code.

    id is invalidated.


EAF_Close

Description:

    Close a file
Prototype:
    integer EAF_Close(fh)
      integer fh
Parameters:
    fh
      File Handle
Returns:
    0 if the file was closed, else returns error code.

EAF_Delete

Description:

    Delete a file
Prototype:
    integer EAF_Delete(fname)
      character*(*) fname
Parameters:
    fname
      File name
Returns:
    0 if the file was deleted, else returns error code.

EAF_Length

Description:

    Determine the length of a file
Prototype:
    double EAF_Length(fh)
      integer fh
Parameters:
    fh
      File Handle
Returns:
    file length in bytes

EAF_Eof

Description:

    Determines if the enod of file was reached
Prototype:
    logical EAF_Eof(ierr)
      integer ierr
Parameters:
    ierr
      error code
Returns:
    true if the end of file was reached, else returns false.

    ierr=0 means success, otherwise an error.


EAF_Truncate

Description:

    Truncate a file at specified offset
Prototype:
    integer EAF_Close(fh, offset)
      integer fh
      double offset
Parameters:
    fh
      File Handle
    offset
      offset in bytes
Returns:
    0 if the file was truncated, else returns error code.

EAF_Stat

Description:

    Determine available disk space and type for a filesystem
Prototype:
    integer EAF_Stat(fname, avail, fstype)
      character*(*) fname
      integer avail
      integer fstype
Parameters:
    fname
      File/directory name
    avail
      amount of available disk space in kilobytes
    fstype
      filesystem type (Unix/PIOFS/PFS/...)
Returns:
    0 if success, else returns error code.

EAF_Errmsg

Description:

    Prints to stdout an error message associated with error code
Prototype:
    subroutine EAF_Errmsg(ierr)
      integer ierr
Parameters:
    ierr
      error code returned from other EAF routines
Returns:
    nothing
ga-5.9.2/pario/doc/commands_eaf.html000066400000000000000000000027611500715745200173130ustar00rootroot00000000000000

Commands
EAF_Open
EAF_Write
EAF_AWrite
EAF_Read
EAF_ARead
EAF_Probe
EAF_Wait
EAF_Close
EAF_Length
EAF_Delete
EAF_Eof
EAF_Errmsg
EAF_Stat
EAF_Truncate

Permissions
EAF_RW
EAF_W
EAF_R
ga-5.9.2/pario/doc/dra_index.html000066400000000000000000000034301500715745200166260ustar00rootroot00000000000000 GA Index

DRA_CLOSE
DRA_CREATE
DRA_DELETE
DRA_FLICK
DRA_INIT
DRA_INQUIRE
DRA_OPEN
DRA_PROBE
DRA_READ
DRA_READ_SECTION
DRA_TERMINATE
DRA_WAIT
DRA_WRITE
DRA_WRITE_SECTION
ga-5.9.2/pario/doc/dra_ops.html000066400000000000000000000570671500715745200163370ustar00rootroot00000000000000 Disk Resident Arrays Operations

Disk Resident Arrays

Disk Resident Arrays (DRA) extend the Global Arrays (GA) programming model to disk. The library encapsulates the details of data layout, addressing and I/O transfer in disk arrays objects. Disk resident arrays resemble global arrays except that they reside on the disk instead of the main memory. The main features of this model are:
  • Data can be transfered (copied) between disk and global memory.
  • I/O operations have nonblocking interface to allow overlapping of I/O with computations.
  • All I/O operations are collective.
  • Either whole or sections of global arrays can be transferred between GA memory and the disk.
  • Reshaping and transpose operation are allowed during the transfer.
  • Disk resident arrays might be temporary or persistent (saved after program termination and available in next runs).
  • Persistent disk arrays can be accessed by any program executing on arbitrary number of processors.
  • Distribution and internal data layout on the disk is optimized for large data transfer.
  • Hints provided by the user are utilized to guide optimization of the library performance for the specific I/O patterns.
DRA can take advantage of a shared file system of a collection of independent filesystems accessible from individual computing nodes.

dra_init

status = dra_init(max_arrays, max_array_size, total_disk_space, max_memory)
         integer max_arrays [input]
         double precision max_array_size [input]
         double precision total_disk_space [input]
         double precision max_memory [input]
Initializes Disk Resident Array I/O subsystem.

max_array_size , total_disk_space and max_memory are given in bytes.

max_memory specifies how much local memory per processor the application is willing to provide to the DRA I/O subsystem for buffering.

The value of -1 for any of input arguments means: "don't care ", "don't know" , or "use defaults ".


dra_terminate

  status=dra_terminate()
Close all open disk resident arrays and shut down DRA I/O subsystem.


dra_create

     status = dra_create(type, dim1, dim2, name, filename, mode,rdim1,rdim2,d_a)
              integer type                      [input]   ! MA type identifier
              integer dim1                      [input]
              integer dim2                      [input]
              character*(*) name                [input]
              character*(*) filename            [input]
              integer mode                      [input]
              integer rdim1                     [input] 
              integer rdim2                     [input]
              integer d_a                       [output]  ! DRA handle
Creates a new disk resident array with specified dimensions and data type (represented by MA type handle).

The string filename specifies name of an abstract meta-file that will store the data on the disk. The meta-file might be implemented as multiple files that will contain parts of the disk resident array. The component files will have names derived from the string filename according to some established scheme(s). Only one DRA object can be stored in DRA meta-file identified by filename .

DRA objects persist on the disk after calling dra_close. dra_delete should be used instead of dra_close to delete disk array and associated meta-file on the disk.

String name can be used for more informative (longer)names.

The data in disk resident array is implicitly initialized to 0 .

Access permissions (read, write, read& write) are set in mode . These are set using defined in the header files DRA.fh (Fortran) and DRA.h (C) preprocessor constants: DRA_R, DRA_W, DRA_RW.

The pair [rdim1, rdim2] specifies dimensions of a typical request. The value of -1 for either of them means "unspecified". The layout of the data on the disk(s) is determined based on the values of these arguments. Performance of the DRA operations depends on the dimensions (section shape) of the requests. If data layout is optimized for column-like sections, performance of DRA operations for row-like sections might be seriously degraded. This is analogous to the effect of wrong loop ordering iyielding frequent cache misses in the following example .

              double precision a(1000, 1000)
              do i = 1, 1000
                 do j = 1, 1000
                    a(i,j) = dfloat(i+j)
                 enddo
              enddo

instead of
              do j = 1, 1000
                 do i = 1, 1000
                    a(i,j) = dfloat(i+j) 
                 enddo
              enddo

dra_open

    status = dra_open(filename, mode, d_a)
              character*(*) filename            [input]
              integer mode                      [input]
              integer d_a                       [output]  ! DRA handle
Open and assign DRA handle to disk resident array stored in DRA meta-file filename. Disk resident arrays that are created with dra_create and saved by calling dra_close can be later opened and accessed by the same or different application.

Attributes of the disk resident array can be found by calling dra_inquire.


dra_write

     status = dra_write(g_a, d_a, request)
              integer g_a                       [input]  ! GA handle
              integer d_a                       [input]  ! DRA handle
              integer request                   [output] ! request id
Write asynchronously specified global array to specified disk resident array.

The dimensions and type of arrays represented by handles g_a and d_a must match. If dimensions don't match, dra_write_section should be used instead.

The operation is by definition asynchronous but it might be implemented as synchronous i.e., it would return only when the I/O is completed.

request can be used to dra_probe or dra_wait for completion of the associated operation.


dra_write_section

     status = dra_write_section(transp, g_a, gilo, gihi, gjlo, gjhi, 
                                        d_a, dilo, dihi, djlo, djhi, request)
              logical transp                    [input] ! transpose operator 
              integer g_a                       [input] ! GA handle 
              integer d_a                       [input] ! DRA handle 
              integer gilo                      [input] 
              integer gihi                      [input] 
              integer gjlo                      [input] 
              integer gjhi                      [input] 
              integer dilo                      [input] 
              integer dihi                      [input] 
              integer djlo                      [input] 
              integer djhi                      [input] 
              integer request                   [output] ! request id
Write asynchronously specified global array section to specified disk resident array section:
                OP(g_a[ gilo:gihi, gjlo:gjhi]) -->  d_a[ dilo:dihi, djlo:djhi]
where OP is the transpose operator (.true./.false.). Return error if the two section's types or sizes mismatch. See dra_write specs for discussion of request .


dra_read

     status = dra_read(g_a, d_a, request)
              integer g_a                       [input]  ! GA handle
              integer d_a                       [input]  ! DRA handle
              integer request                   [output] ! request id
Read asynchronously the specified global array from the specified disk resident array.

Dimensions and type of arrays referred to by handles g_a and d_a must match. If dimensions don't match, dra_read_section could be used instead.

See dra_write specs for discussion of request .


dra_read_section

     status = dra_read_section(transp, g_a, gilo, gihi, gjlo, gjhi,
                                       d_a, dilo, dihi, djlo, djhi, request)
              logical transp                    [input] ! transpose operator
              integer g_a                       [input] ! GA handle
              integer d_a                       [input] ! DRA handle
              integer gilo                      [input]
              integer gihi                      [input]
              integer gjlo                      [input]
              integer gjhi                      [input]
              integer dilo                      [input]
              integer dihi                      [input]
              integer djlo                      [input]
              integer djhi                      [input]
              integer request                   [output] ! request id
Read asynchronously specified global array section from specified disk resident array section:
                OP(d_a[ dilo:dihi, djlo:djhi]) -->  g_a[ gilo:gihi, gjlo:gjhi]
where OP is the transpose operator (.true./.false.).

See dra_write specs for discussion of request .


dra_probe

     status = dra_probe(request, compl_status)
              integer request                   [input]  ! request id
              integer compl_status              [output] ! completion status
Tests for completion of dra_write/read or dra_write/read_section operation which set the value passed in request argument.

compl_status .eq. 0 means the operation has been completed.

compl_status .ne. 0 means "not done yet ".


dra_wait

     status = dra_wait(request)
              integer request                   [input]  ! request id
Blocks until completion of dra_write/read or dra_write/read_section operation which set the value passed in request argument.


dra_inquire

     status = dra_inquire(d_a, type, dim1, dim2, name, filename)
              integer d_a                       [input]  ! DRA handle
              integer type                      [output]
              integer dim1                      [output]
              integer dim2                      [output]
              character*(*) name                [output]
              character*(*) filename            [output]
Return dimensions, type , name of disk resident array, and filename of DRA meta-file associated with d_a handle.


dra_delete

     status = dra_delete(d_a)
              integer d_a                       [input]  ! DRA handle
Delete a disk resident array associated with d_a handle. Invalidate handle. The corresponding DRA meta-file is destroyed.


dra_close

     status = dra_close(d_a)
              integer d_a                       [input]  ! DRA handle
Close DRA meta-file associated with d_a handle and deallocate data structures corresponding to this disk array. Invalidate d_a handle. The array on the disk is persistent.


dra_flick

     subroutine dra_flick()
Returns control to DRA for a VERY short time to improve progress of pending asynchronous operations. ga-5.9.2/pario/doc/index.html000066400000000000000000000011721500715745200160010ustar00rootroot00000000000000 Documentation List There are three parallel I/O models supported:

Disk Residend Arrays
Shared Files
Exclusive Access Files ga-5.9.2/pario/doc/list.html000066400000000000000000000014271500715745200156500ustar00rootroot00000000000000 Untitled Document

Shared Files Library

sf_create

sf_destroy

sf_read

sf_wait

sf_waitall

sf_write

 

 

 

  ga-5.9.2/pario/doc/sf.html000066400000000000000000000156641500715745200153150ustar00rootroot00000000000000 sf gowno

Shared Files Library

The Shared Files (SF) library implements logically-shared temporary files for parallel SPMD (single-program-multiple-data) applications. Any process can read and write at arbitrary location in a file independently of other processes. Shared files must be created and destroyed collectively. To optimize performance of the library, user can provide hints on:

  • soft limit, and
  • hard limit for the file size.

The SF read and write operations are asynchronous/nonblocking and require a wait operation to complete.

Due to the FORTRAN data type limitations, in order to facilitate access to files >2GB the file size, request size and offset arguments use double precision data types.

All SF routines return an error code. The "0" value means success, other values correspond to error codes.


sf_create

integer sf_create(fname, size_hard_limit, size_soft_limit, req_size, handle)
        fname            -- meta-file name
        size_hard_limit  -- max file size in bytes not to be exceeded (a hint)
        size_soft_limit  -- estimated file size (a hint)
        req_size         -- size of  a typical request (a hint)
        handle           -- returned handle to the created file

Creates shared file using name and path specified in fname as a template.
req_size specifies size of a typical request (-1. means "don't know").

Creates shared file using name and path specified in fname as a template.
req_size specifies size of a typical request (-1. means "don't know").

It is a collective operation.


sf_write

 

integer sf_write(handle, offset, bytes, buffer, request_id)
        handle           -- file handle returned from sf_create   [in]
        offset           -- location in file (from the beginning)
                            where data should be written to       [in]
        buffer           -- local array to put the data           [in]
        bytes            -- number of bytes to read               [in]
        request_id       -- id identifying asynchronous operation [out]

Asynchronous write operation. Writes number of bytes to the file identified by handle at location offset.Operation is guaranteed to be complete when sf_wait called with request_id argument returns.


sf_read

integer sf_read(handle, offset, bytes, buffer, request_id)
        handle           -- file handle returned from sf_create   [in]
        offset           -- location in file (from the beginning)
                            where data should be read from        [in]
        buffer           -- local array to put the data           [in]
        bytes            -- number of bytes to read               [in]
        request_id       -- id identifying asynchronous operation [out]

Asynchronous read operation. Reads number of bytes to the file identified by handle at location offset.Operation is guaranteed to be complete when sf_wait called with request_id argument returns.


sf_wait

integer sf_wait(request_id)
        request_id       -- id identifying asynchronous operation [in/out]

Blocks the calling process until I/O operation associated with request_id completes.
Invalidates request_id.
 
 



sf_waitall

integer sf_waitall(list, num)
        list(num)        -- array of ids for asynchronous operations [in/out]
        num              -- number of entries in list                [in]

Blocks the calling process until all of the num I/O operations associated with ids
specified in list complete. Invalidates (modifies) ids on the list.


sf_destroy

integer sf_destroy(handle)
        handle           -- file handle returned from sf_create      [in]

Destroys the shared file associated with handle. It is a collective operation. ga-5.9.2/pario/dra/000077500000000000000000000000001500715745200140045ustar00rootroot00000000000000ga-5.9.2/pario/dra/DRA.doc000066400000000000000000000272501500715745200151070ustar00rootroot00000000000000 DISK RESIDENT ARRAYS PRELIMINARY DOCUMENTATION Jarek Nieplocha, 05.10.95 Disk Resident Arrays extend Global Arrays NUMA programming model with disk I/O operations. Either whole or sections of global arrays can be transferred between GA memory and the disk. At present time, all operations are declared to be collective. This simplifies implementation on machines where only some processors are connected to I/O devices. Except where stated otherwise, all operations are synchronous (blocking) which means that control is returned to the calling process only after the requested operation completes. All operations return error code. Return values: 0 - OK >0 - failure A program that uses Disk Resident Arrays should look like: program foo #include "mafdecls.h" #include "global.fh" #include "dra.fh" c call pbeginf() ! initialize TCGMSG if(.not. ma_init(...)) ERROR ! initialize MA call ga_initialize() ! initialize Global Arrays if(dra_init(....).ne.0) ERROR ! initialize Disk Arrays c do work if(dra_terminate().ne.0)ERROR ! destroy DRA internal data structures call ga_terminate ! terminate Global Arrays call pend() ! terminate TCGMSG end List of DRA operations: status = dra_init(max_arrays, max_array_size, total_disk_space, max_memory) integer max_arrays [input] double precision max_array_size [input] double precision total_disk_space [input] double precision max_memory [input] discussion: Initializes disk resident array I/O subsystem. "max_array_size", "total_disk_space" and "max_memory" are given in bytes. "max_memory" specifies how much local memory per processor the application is willing to provide to the DRA I/O subsystem for buffering. The value of "-1" for any of input arguments means: "don't care", "don't know", or "use defaults" status = dra_terminate() discussion: Close all open disk resident arrays and shut down DRA I/O subsystem. status = dra_create(type, dim1, dim2, name, filename, mode,rdim1,rdim2,d_a) integer type [input] ! MA type identifier integer dim1 [input] integer dim2 [input] character*(*) name [input] character*(*) filename [input] integer mode [input] integer rdim1 [input] integer rdim2 [input] integer d_a [output] ! DRA handle discussion: Creates new disk resident array with specified dimensions and "type". String "filename" specifies name of an abstract meta-file that will store the data on the disk. The meta-file might be implemented as multiple disk files that will contain parts of the disk resident array. The component files will have names derived from the string "filename" according to some established scheme(s). Only one DRA object can be stored in DRA meta-file identified by "filename". DRA objects persist on the disk after calling dra_close(). dra_delete() should be used instead of dra_close() to delete disk array and associated meta-file on the disk. A set of UNIX commands that understand "filename" identifier will be provided to copy, rename, move, change access attributes, and delete DRA meta-files. String "name" can be used for more informative (longer)names. Disk array is implicitly initialized to "0". Access permissions (read, write, read&write) are set in "mode". These are set using defined in DRA.fh (Fortran) and DRA.h (C) preprocessor constants: DRA_R, DRA_W, DRA_RW. The pair [rdim1, rdim2] specifies dimensions of a "typical" request. The value of "-1" for either of them means "unspecified". The layout of the data on the disk(s) is determined based on the values of these arguments. Performance of the DRA operations will depend on the dimensions (section shape) of the requests. If data layout is optimized for "column-like" sections, performance of DRA operations for "row-like" sections might be seriously degraded. This is analogous to the effect of wrong loop order yielding frequent cache misses on RISC processors in the example below. double precision a(1000, 1000) do i = 1, 1000 do j = 1, 1000 a(i,j) = dfloat(i+j) enddo enddo instead of do j = 1, 1000 do i = 1, 1000 a(i,j) = dfloat(i+j) enddo enddo status = dra_open(filename, mode, d_a) character*(*) filename [input] integer mode [input] integer d_a [output] ! DRA handle discussion: Open and assign DRA handle to disk resident array stored in DRA meta-file "filename". Disk arrays that are created with 'dra_create' and saved by calling 'dra_close' can be later opened and accessed by the same or different application. Attributes of disk resident array can be found by calling 'dra_inquire'. status = dra_write(g_a, d_a, request) integer g_a [input] ! GA handle integer d_a [input] ! DRA handle integer request [output] ! request id discussion: Write asynchronously specified global array to specified disk resident array. Dimensions and type of g_a and d_a must match. If dimensions don't match, 'dra_write_section' should be used instead. The operation is by definition asynchronous but it might be implemented as synchronous i.e., it would return only when I/O is done. "request" can be used to 'dra_probe' or 'dra_wait' for completion. status = dra_write_section(transp, g_a, gilo, gihi, gjlo, gjhi, d_a, dilo, dihi, djlo, djhi, request) logical transp [input] ! transpose operator integer g_a [input] ! GA handle integer d_a [input] ! DRA handle integer gilo [input] integer gihi [input] integer gjlo [input] integer gjhi [input] integer dilo [input] integer dihi [input] integer djlo [input] integer djhi [input] integer request [output] ! request id discussion: Write asynchronously specified global array section to specified disk resident array section: OP(g_a[ gilo:gihi, gjlo:gjhi]) --> d_a[ dilo:dihi, djlo:djhi] where OP is the transpose operator (.true./.false.). Return error if the two section's types or sizes mismatch. See 'dra_write' specs for discussion of "request". Note: Section reshaping and transpose operation not implemented yet. status = dra_read(g_a, d_a, request) integer g_a [input] ! GA handle integer d_a [input] ! DRA handle integer request [output] ! request id discussion: Read asynchronously specified global array from specified disk resident array. Dimensions and type of g_a and d_a must match. If dimensions don't match, 'dra_read_section' could be used instead. See 'dra_write' specs for discussion of "request". status = dra_read_section(transp, g_a, gilo, gihi, gjlo, gjhi, d_a, dilo, dihi, djlo, djhi, request) logical transp [input] ! transpose operator integer g_a [input] ! GA handle integer d_a [input] ! DRA handle integer gilo [input] integer gihi [input] integer gjlo [input] integer gjhi [input] integer dilo [input] integer dihi [input] integer djlo [input] integer djhi [input] integer request [output] ! request id discussion: Read asynchronously specified global array section from specified disk resident array section: OP(d_a[ dilo:dihi, djlo:djhi]) --> g_a[ gilo:gihi, gjlo:gjhi] where OP is the transpose operator (.true./.false.). See 'dra_write' specs for discussion of "request". Note: Section reshaping and transpose operation not implemented yet. status = dra_probe(request, compl_status) integer request [input] ! request id integer compl_status [output] ! completion status discussion: Tests for completion of 'dra_write/read' or 'dra_write/read_section' operation which set the value passed in "request" argument. compl_status = 0 the operation has been completed compl_status <> 0 not done yet status = dra_wait(request) integer request [input] ! request id discussion: Blocks until completion of 'dra_write/read' or 'dra_write/read_section' operation which set the value passed in "request" argument. status = dra_inquire(d_a, type, dim1, dim2, name, filename) integer d_a [input] ! DRA handle integer type [output] integer dim1 [output] integer dim2 [output] character*(*) name [output] character*(*) filename [output] discussion: Return dimensions, "type", "name" of disk resident array, and "filename" of DRA meta-file associated with "d_a" handle. status = dra_delete(d_a) integer d_a [input] ! DRA handle discussion: Delete a disk resident array associated with "d_a" handle. Invalidate handle. The corresponding DRA meta-file is destroyed. status = dra_close(d_a) integer d_a [input] ! DRA handle discussion: Close DRA meta-file associated with "d_a" handle and deallocate data structures corresponding to this disk array. Invalidate "d_a" handle. The array on the disk is persistent. subroutine dra_flick() discussion: Returns control to DRA for a VERY short time to improve progress of pending asynchronous operations. ga-5.9.2/pario/dra/Notes.doc000066400000000000000000000023531500715745200155660ustar00rootroot00000000000000 Disk Resident Arrays Implementation Notes Assumptions: . ANSI C compiler is not required; . interface to C programs is minimal (most users program in Fortran) . low level I/O operations defined in ELIO library . upper layer knows if they are called collectively or not C Interface: . C programs must use Integer and DoublePrecision data types, defined in "global.h" (GA package) for both the GA and DRA, and include file "dra.h"; . all arguments passed by address; . names of DRA operations have capitalized DRA prefix i.e., C version of Fortran dra_init is DRA_init. Note that the names are converted by cpp as specified in "dra.h" so that in most cases, C and Fortran programs call the same function. Internal convention is that there is a separate Fortran wrapper to a DRA operation iff it has a character string argument(s). Implementation Status: . prototyping used for DRA functions if ANSI C compiler available . indexing arguments are currently implemented as integers . reshaping and transposing in dra_[write,read]_section not implemented yet Problems to Resolve: . do we need explicit dra_flush or we should handle it implicitly after each write when file is create in 'read+write' in mode ? ga-5.9.2/pario/dra/README000066400000000000000000000005701500715745200146660ustar00rootroot00000000000000This directory contains a prototype of the Disk Resident Arrays library. You need GNU make (version > 3.68) to build this software. To build test program, from the top-level type: make pario/dra/test.x Documentation is located in disk_arrays.doc and implementation notes in Notes.doc. Bug report, comments, questions: https://github.com/GlobalArrays/ga/issues ga-5.9.2/pario/dra/big.F000066400000000000000000000174431500715745200146650ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c c FNAME - filename for test program c #define BASE_NAME 'da.try' #define BASE_NAME1 'da1.try' #ifdef HPIODIR # define FNAME HPIODIR//BASE_NAME # define FNAME1 HPIODIR//BASE_NAME1 #else # define FNAME BASE_NAME # define FNAME1 BASE_NAME1 #endif program io #include "mafdecls.fh" #include "global.fh" #include "dra.fh" integer status, me integer max_arrays integer stack, heap double precision max_sz, max_disk, max_mem data max_arrays, max_sz, max_disk, max_mem /10,1d9,1d10, 1d6/ data stack, heap /6000000, 4000000/ c #include "mp3.fh" if(.not. ga_uses_ma())then stack = 10000 heap = 10000 endif c if(ma_init(MT_F_DBL, stack, heap) ) then call ga_initialize() me = ga_nodeid() if(dra_init(max_arrays, max_sz, max_disk, max_mem).ne.0)then call ga_error('dra_init failed: ',0) endif if (me.eq.0) print *,' ' if(me.eq.0)print *, 'TESTING PERFORMANCE OF DISK ARRAYS' if (me.eq.0) print *,' ' call test_io_dbl() status = dra_terminate() call ga_terminate() else print *,'ma_init failed' endif if(me.eq.0)print *, 'all done ...' call MP_FINALIZE() end subroutine test_io_dbl implicit none #include "mafdecls.fh" #include "global.fh" #include "dra.fh" #include "mp3def.fh" integer n,m parameter (n=10000) parameter (m = 2*n) double precision err, tt0, tt1, mbytes integer g_a, g_b, d_a, d_b double precision drand integer i,j, req, loop integer dilo,dihi,djlo,djhi integer gilo,gihi,gjlo,gjhi integer ielem, jelem integer me, nproc integer index, ld integer iran logical status integer util_mdtob external drand external util_mdtob intrinsic int, dble iran(i) = int(drand(0)*dble(i-1)) + 1 c loop = 30 req = -1 nproc = ga_nnodes() me = ga_nodeid() c if (me.eq.0) print *, 'creating global arrays ',n,' x',n if (me.eq.0)call ffflush(6) call ga_sync() if(.not. ga_create(MT_DBL, n, n, 'a', 1, 1, g_a)) & call ga_error('ga_create failed: a', 0) if(.not. ga_create(MT_DBL, n, n, 'b', 1, 1, g_b)) & call ga_error('ga_create failed: b', 0) if (me.eq.0) print *,'done ' if (me.eq.0)call ffflush(6) c c initialize g_a, g_b with random values c ... use ga_access to avoid allocating local buffers for ga_put c call ga_sync() call ga_distribution(g_a, me, gilo,gihi,gjlo,gjhi) call ga_access(g_a, gilo,gihi,gjlo,gjhi, index, ld) call fill_random(DBL_MB(index), gihi-gilo+1, gjhi-gjlo+1, ld) call ga_sync() * if (me.eq.0) print *,'done ' * if (me.eq.0)call ffflush(6) c call ga_zero(g_b) c c c....................................................................... if (me.eq.0) print *, 'creating disk array ',n,' x',n if (me.eq.0)call ffflush(6) if(dra_create(MT_DBL, n, n, 'A', FNAME, & DRA_RW, n, n, d_a).ne.0) $ CALL ga_error('dra_create failed: ',0) c if(me.eq.0) print *, 'alligned blocking write' if (me.eq.0)call ffflush(6) tt0 = MP_TIMER() if(dra_write(g_a, d_a,req).ne.0) $ CALL ga_error('dra_write failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 mbytes = 1e-6*util_mdtob(n*n) if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif c if(dra_close(d_a).ne.0)call ga_error('dra_close failed: ',d_a) tt1 = MP_TIMER() -tt0 if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif c if (me.eq.0) print *,' ' if (me.eq.0) print *,'disk array closed ' if (me.eq.0)call ffflush(6) c....................................................................... c c if (me.eq.0) print *, 'creating disk array ',m,' x',m if (me.eq.0)call ffflush(6) if(dra_create(MT_DBL, m, m, 'A', FNAME1, & DRA_RW, n, n, d_b).ne.0) $ CALL ga_error('dra_create failed: ',0) c if(me.eq.0) print *, 'non alligned blocking write' if (me.eq.0)call ffflush(6) c gilo =1 gjlo =1 gihi =n gjhi =n tt0 = MP_TIMER() if(dra_write_section(.false., g_a, gilo, gihi, gjlo, gjhi, & d_b, gilo+1, gihi+1, gjlo+1, gjhi+1, req).ne.0) & call ga_error('dra_write_section failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 mbytes = 1e-6*util_mdtob(n*n) if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif c if(dra_close(d_b).ne.0)call ga_error('dra_close failed: ',d_b) tt1 = MP_TIMER() -tt0 if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif c if (me.eq.0) print *,' ' if (me.eq.0) print *,'disk array closed ' if (me.eq.0)call ffflush(6) c....................................................................... c c if (me.eq.0) print *,' ' if (me.eq.0) print *,'opening disk array' if(dra_open(FNAME,DRA_R, d_a).ne.0) & call ga_error('dra_open failed',0) if(me.eq.0) print *, 'alligned blocking read' if (me.eq.0)call ffflush(6) tt0 = MP_TIMER() if(dra_read(g_b, d_a, req).ne.0) $ CALL ga_error('dra_read failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif call ga_dadd(1d0, g_a, -1d0, g_b, g_b) err = ga_ddot(g_b, g_b) if(err.ne.0) then if (me.eq.0) print *,'BTW, we have error =', err call ga_print(g_b) else if (me.eq.0) print *,'OK' endif if(dra_delete(d_a).ne.0) & call ga_error('dra_delete failed',0) c....................................................................... c if (me.eq.0) print *,' ' if (me.eq.0) print *,'opening disk array' if(dra_open(FNAME1,DRA_R, d_b).ne.0) & call ga_error('dra_open failed',0) if(me.eq.0) print *, 'non alligned blocking read' if (me.eq.0)call ffflush(6) gilo =1 gjlo =1 gihi =n gjhi =n tt0 = MP_TIMER() if(dra_read_section(.false., g_b, gilo, gihi, gjlo, gjhi, & d_b, gilo+1, gihi+1, gjlo+1, gjhi+1, req).ne.0) & call ga_error('dra_read_section failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif call ga_dadd(1d0, g_a, -1d0, g_b, g_b) err = ga_ddot(g_b, g_b) if(err.ne.0) then if (me.eq.0) print *,'BTW, we have error =', err else if (me.eq.0) print *,'OK' endif if(dra_delete(d_b).ne.0) & call ga_error('dra_delete failed',0) c....................................................................... status = ga_destroy(g_a) status = ga_destroy(g_b) 100 format(g11.2,' MB time=',g11.2,' rate=',g11.3,'MB/s') end subroutine swap(a,b) integer a, b, temp temp = a a = b b = temp end subroutine init_char(str, len, char) character*(*) str character*1 char integer i do i = 1, len -1 str(i:i+1) = char enddo end subroutine fill_random(a, n,m, ld) integer ld, n,m double precision a(ld,*), drand, seed integer i,j external drand c do j=1,m seed = drand(j) do i=1,n a(i,j)=seed*i enddo enddo end ga-5.9.2/pario/dra/bign.c000066400000000000000000000205701500715745200150730ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #include "dra.h" #include "ga.h" #include "macdecls.h" #include "mp3.h" #define FNAME "/scratch/da.try" #define FNAME_ALT "/tmp/da.try" #define NDIM 3 #define SIZE 300 #define NFAC 3 /* #define NDIM 3 #define SIZE 1800 #define NDIM 2 #define SIZE 4000 #define NDIM 1 #define SIZE 16000000 */ #ifndef MAXDIM # define MAXDIM GA_MAX_DIM #endif #ifndef TRUE # define TRUE (Logical)1 #endif #ifndef FALSE # define FALSE (Logical)0 #endif #define IA 16807 #define IM 2147483647 #define AM (1.0/IM) #define IQ 127773 #define IR 2836 #define MASK 123459876 float ran0(long *idum) { long k; float ans; *idum ^= MASK; k=(*idum)/IQ; *idum = IA*(*idum-k*IQ)-IR*k; if (*idum < 0) *idum += IM; ans=AM*(*idum); *idum ^= MASK; return ans; } void fill_random(double *a, int isize) { long *idum; long i, j; j = 38282; idum = &j; a[0] = (double)ran0(idum); for (i=0; i<(long)isize; i++) { a[i] = (double)(10000.0*ran0(idum)); } } void array_int_to_dra_size_t(int *in, dra_size_t *out, size_t size) { size_t i; for (i=0; i= nfac || icoord[j] < 0) printf("Invalid icoord[%d]: %d\n",j,icoord[j]); for (j=1; j= nfac || icoord[j] < 0) printf("Invalid icoord[%d]: %d\n",j,icoord[j]); } for (j=0; j= nfac || icoord[j] < 0) printf("Invalid icoord[%d]: %d\n",j,icoord[j]); for (j=1; j= nfac || icoord[j] < 0) printf("Invalid icoord[%d]: %d\n",j,icoord[j]); } for (j=0; j #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRINGS_H # include #endif #include "buffers.h" /*#define STATBUF 1 */ /* set if static buffers will be used */ /*#define DEBUG 1 */ #define MAX_CTXT 5 int ctxt_count = 0; /** buffer management initialization routine */ void buffer_init( buf_context_t *ctxt, int nbuf, int buf_size, void (*fptr)(char*)) { int i; long diff; if (nbuf < 1 || nbuf > MAXBUF) { printf("Too many (or too few) buffers requested, using default number (%d) of buffers", DEFBUF); nbuf = DEFBUF; } /* create a context */ ctxt->ctxt_id = ctxt_count++; if (ctxt->ctxt_id > MAX_CTXT) { printf("Max number of contexts reached!\n"); return; } ctxt->nbuf = nbuf; ctxt->size = buf_size; ctxt->buf = (_buffer_t *)malloc(sizeof(_buffer_t) * nbuf); ctxt->fptr = fptr; #ifdef STATBUF double buffers[MAXBUF][DBL_BUF_SIZE]; for (i = 0; i < nbuf; i++) { ctxt->buf[i].buffer = (char *) buffers[i]; bzero(buffers[i], sizeof(buffers[i])); } #else /* STATBUF */ /* get buffer memory */ for (i = 0; i < nbuf; i++) { ctxt->buf[i].buffer = (char*) malloc((buf_size + ALIGN-1) *sizeof(double)); if (ctxt->buf[i].buffer == NULL) { printf("Could not allocate memory for buffers!\n"); return; } bzero(ctxt->buf[i].buffer, sizeof(double)*(buf_size + ALIGN-1)); /* align buffer address */ diff = ((long)(ctxt->buf[i].buffer)) % (sizeof(double)*ALIGN); if(diff) { ctxt->buf[i].align_off = (int) (sizeof(double)*ALIGN - diff); } else { ctxt->buf[i].align_off = (int) 0; } ctxt->buf[i].buffer += ctxt->buf[i].align_off; } #endif /* STATBUF */ for (i = 0; i < nbuf; i++) { ctxt->buf[i].active = 0; ctxt->buf[i].group_id = 0; } #ifdef DEBUG printf("Created a context\n\n"); #endif } /** internal function to return empty buffer handle */ int get_buf_hdl(buf_context_t *ctxt) { int i; for (i = 0; i < ctxt->nbuf; i++) { if (ctxt->buf[i].active == 0) { ctxt->buf[i].active = 1; return i; } } return -1; } char* get_buf(buf_context_t *ctxt, int call_id) { int hdl; char *buf; hdl = get_buf_hdl(ctxt); if (hdl == -1) { int cur_buf; /* no buffer is available, wait for the oldest buffer to become free */ cur_buf = (ctxt->last_buf + 1) % ctxt->nbuf; /* free this buffer by calling the callback function provided by the application */ ctxt->fptr(ctxt->buf[cur_buf].buffer); hdl = cur_buf; } buf = ctxt->buf[hdl].buffer; ctxt->buf[hdl].buf_hdl = hdl; ctxt->buf[hdl].call_id = call_id; ctxt->buf[hdl].active = 1; ctxt->last_buf = hdl; #ifdef DEBUG printf("Giving a buffer with internal handle: %d\n", hdl); #endif return (buf); } /** function to free a buffer */ void ga_dra_free_buf(buf_context_t *ctxt, char *buf) { int i; for (i = 0; i < ctxt->nbuf; i++) { if (ctxt->buf[i].buffer == buf) { ctxt->buf[i].active = 0; break; } } } /** function to complete an entire call */ void buf_complete_call(buf_context_t *ctxt, int call_id) { int i; #ifdef DEBUG printf("Completing call with call id: %d\n", call_id); #endif for (i = 0; i < ctxt->nbuf; i++) { if (ctxt->buf[i].call_id == call_id && ctxt->buf[i].active == 1) { /* force completion by calling user function */ ctxt->fptr(ctxt->buf[i].buffer); ctxt->buf[i].active = 0; } } } /** function to return the call_id associated with a particular buffer */ int buf_get_call_id(buf_context_t *ctxt, char *buf) { int i; for (i = 0; i < ctxt->nbuf; i++) if (ctxt->buf[i].buffer == buf) return(ctxt->buf[i].call_id); printf("Buf_man error: Cannot find call_id for this buffer\n"); return -1; } /** * Function to return an array of buffers associated with a call_id * the last two parameters are output */ int get_bufs_of_call_id( buf_context_t *ctxt, int call_id, int *n_buf, char *bufs[]) { int i, count = 0; for (i = 0; i < ctxt->nbuf; i++) if (ctxt->buf[i].call_id == call_id) { bufs[count++] = ctxt->buf[i].buffer; } *n_buf = count; if (*n_buf == 0) { #ifdef DEBUG printf("Buf_man: No active buffer found for call_id %d\n", call_id); #endif return -1; /* no buffer found */ } return 0; /* success */ } /** terminates an application context */ void buf_terminate(buf_context_t *ctxt) { #ifndef STATBUF int i; for (i = 0; i < ctxt->nbuf; i++) { ctxt->buf[i].buffer -= ctxt->buf[i].align_off; free(ctxt->buf[i].buffer); } #endif free(ctxt->buf); ctxt_count--; /* this context can be reallocated */ } ga-5.9.2/pario/dra/buffers.h000066400000000000000000000034121500715745200156110ustar00rootroot00000000000000#ifndef BUFFERS_H_ /* so that this file is not included twice */ #define BUFFERS_H_ /** buffer size --- adjust to be a multiplicity of the striping factor in a parallel filesystem */ #define DBL_BUF_SIZE 131072 #define BUF_SIZE (DBL_BUF_SIZE*sizeof(double)) #define INT_BUF_SIZE (BUF_SIZE/sizeof(int)) /* alignment factor for the internal buffer */ #define ALIGN 16 #define MAXBUF 16 /* max # of buffers that can be used */ #define DEFBUF 4 /* default # of buffers */ /** internal buffer structure */ typedef struct { char *buffer; int align_off; /**< caching alignment offset */ int buf_hdl; /**< buffer handle; index of the buffer in the ctxt */ int group_id; /**< identify callback function to use to release buffer */ int call_id; /**< id to be used to complete an entire call */ int active; /**< if the buffer active or not */ } _buffer_t; /** structure to create application context */ typedef struct { int ctxt_id; _buffer_t *buf; /**< will be allocated nbuf buffers*/ int nbuf; int size; /**< in bytes */ void (*fptr)(char*); /**< array of pointers to functions provided by the application */ int last_buf; /* utility variable; contains the last buf assigned in this ctxt */ } buf_context_t; void buffer_init(buf_context_t *ctxt, int nbuf, int buf_size, void (*fptr)(char*)); char *get_buf(buf_context_t *ctxt, int call_id); void buf_terminate(buf_context_t *ctxt); void buf_complete_call(buf_context_t *ctxt, int call_id); int buf_get_call_id(buf_context_t *ctxt, char *buf); int get_bufs_of_call_id(buf_context_t *ctxt, int call_id, int *n_buf, char *bufs[]); void ga_dra_free_buf(buf_context_t *ctxt, char *buf); #endif /* BUFFERS_H_ */ ga-5.9.2/pario/dra/capi.c000066400000000000000000000161541500715745200150730ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #include "dra.h" #include "draf2c.h" #include "drap.h" #include "global.h" #include "ga-papi.h" static Integer _da_lo[MAXDIM], _da_hi[MAXDIM]; static Integer _da_dims[MAXDIM]; static Integer _da_reqdims[MAXDIM]; static Integer _ga_lo[MAXDIM], _ga_hi[MAXDIM]; #ifdef USE_FAPI # define COPYC2F(carr, farr, n){\ int i; for(i=0; i< (n); i++)(farr)[i]=(Integer)(carr)[i];} # define COPYF2C(farr, carr, n){\ int i; for(i=0; i< (n); i++)(carr)[i]=(int)(farr)[i];} # define COPYF2C_DRA(farr, carr, n){\ int i; for(i=0; i< (n); i++)(carr)[i]=(dra_size_t)(farr)[i];} #else # define COPYC2F(carr, farr, n){\ int i; for(i=0; i< (n); i++)(farr)[n-i-1]=(Integer)(carr)[i];} # define COPYF2C(farr, carr, n){\ int i; for(i=0; i< (n); i++)(carr)[n-i-1]=(int)(farr)[i];} # define COPYF2C_DRA(farr, carr, n){\ int i; for(i=0; i< (n); i++)(carr)[n-i-1]=(dra_size_t)(farr)[i];} #define BASE_0 #endif #define COPY(CAST,src,dst,n) {\ int i; for(i=0; i< (n); i++)(dst)[i]=(CAST)(src)[i];} #ifdef BASE_0 # define COPYINDEX_C2F(carr, farr, n){\ int i; for(i=0; i< (n); i++)(farr)[n-i-1]=(Integer)(carr)[i]+1;} # define COPYINDEX_F2C(farr, carr, n){\ int i; for(i=0; i< (n); i++)(carr)[n-i-1]=(int)(farr)[i] -1;} # define COPYINDEX_F2C_DRA(farr, carr, n){\ int i; for(i=0; i< (n); i++)(carr)[n-i-1]=(dra_size_t)(farr)[i] -1;} #else # define COPYINDEX_F2C COPYF2C # define COPYINDEX_F2C_DRA COPYF2C_DRA # define COPYINDEX_C2F COPYC2F #endif int DRA_uses_fapi(void) { #ifdef USE_FAPI return 1; #else return 0; #endif } int NDRA_Create(int type, int ndim, dra_size_t dims[], char *name, char* filename, int mode, dra_size_t reqdims[], int *d_a) { Integer ttype, nndim, dd_a, mmode; logical st; if (ndim>MAXDIM) return 0; COPYC2F(dims, _da_dims, ndim); COPYC2F(reqdims, _da_reqdims, ndim); ttype = (Integer)type; nndim = (Integer)ndim; mmode = (Integer)mode; st = ndrai_create(&ttype, &nndim, _da_dims, name, filename, &mmode, _da_reqdims, &dd_a); *d_a = (int)dd_a; if(st==TRUE) return 1; else return 0; } int NDRA_Create_config(int type, int ndim, dra_size_t dims[], char *name, char* filename, int mode, dra_size_t reqdims[], int numfiles, int ioprocs, int *d_a) { Integer ttype, nndim, dd_a, mmode; Integer nnumfiles, iioprocs; logical st; if (ndim>MAXDIM) return 0; COPYC2F(dims, _da_dims, ndim); COPYC2F(reqdims, _da_reqdims, ndim); ttype = (Integer)type; nndim = (Integer)ndim; mmode = (Integer)mode; nnumfiles = (Integer)numfiles; iioprocs = (Integer)ioprocs; st = ndrai_create_config(&ttype, &nndim, _da_dims, name, filename, &mmode, _da_reqdims, &nnumfiles, &iioprocs, &dd_a); *d_a = (int)dd_a; if(st==TRUE) return 1; else return 0; } int NDRA_Inquire(int d_a, int *type, int *ndim, dra_size_t dims[], char *name, char* filename) { Integer dd_a, ttype, nndim, status; dd_a = (Integer)d_a; status = ndrai_inquire(&dd_a, &ttype, &nndim, _da_dims, name, filename); COPYF2C_DRA(_da_dims, dims, nndim); *type = (int)ttype; *ndim = (int)nndim; return (int)status; } int NDRA_Write(int g_a, int d_a, int *request) { Integer status, gg_a, dd_a, rrequest; gg_a = (Integer)g_a; dd_a = (Integer)d_a; rrequest = (Integer)*request; status = ndra_write_(&gg_a, &dd_a, &rrequest); *request = (int)rrequest; return (int)status; } int NDRA_Read(int g_a, int d_a, int *request) { Integer status, gg_a, dd_a, rrequest; gg_a = (Integer)g_a; dd_a = (Integer)d_a; rrequest = (Integer)*request; status = ndra_read_(&gg_a, &dd_a, &rrequest); *request = (int)rrequest; return (int)status; } int NDRA_Write_section(logical transp, int g_a, int glo[], int ghi[], int d_a, dra_size_t dlo[], dra_size_t dhi[], int *request) { Integer status; Integer ttransp, gg_a, dd_a, rrequest; Integer ndim; ttransp = (Integer)transp; gg_a = (Integer)g_a; ndim = pnga_ndim(gg_a); dd_a = (Integer)d_a; rrequest = (Integer)*request; COPYINDEX_C2F(glo, _ga_lo, ndim); COPYINDEX_C2F(ghi, _ga_hi, ndim); COPYINDEX_C2F(dlo, _da_lo, ndim); COPYINDEX_C2F(dhi, _da_hi, ndim); status = ndra_write_section_(&ttransp, &gg_a, _ga_lo, _ga_hi, &dd_a, _da_lo, _da_hi, &rrequest); *request = (int)rrequest; return (int)status; } int NDRA_Read_section(logical transp, int g_a, int glo[], int ghi[], int d_a, dra_size_t dlo[], dra_size_t dhi[], int *request) { Integer status; Integer ttransp, gg_a, dd_a, rrequest; Integer ndim; ttransp = (Integer)transp; gg_a = (Integer)g_a; ndim = pnga_ndim(gg_a); dd_a = (Integer)d_a; rrequest = (Integer)*request; COPYINDEX_C2F(glo, _ga_lo, ndim); COPYINDEX_C2F(ghi, _ga_hi, ndim); COPYINDEX_C2F(dlo, _da_lo, ndim); COPYINDEX_C2F(dhi, _da_hi, ndim); status = ndra_read_section_(&ttransp, &gg_a, _ga_lo, _ga_hi, &dd_a, _da_lo, _da_hi, &rrequest); *request = (int)rrequest; return (int)status; } int DRA_Init(int max_arrays, double max_array_size, double total_disk_space, double max_memory) { Integer mmax_arrays, status; DoublePrecision mmax_array_size, ttotal_disk_space, mmax_memory; mmax_arrays = (Integer)max_arrays; mmax_array_size = (DoublePrecision)max_array_size; ttotal_disk_space = (DoublePrecision)total_disk_space; mmax_memory = (DoublePrecision)max_memory; status = dra_init_(&mmax_arrays, &mmax_array_size, &ttotal_disk_space, &mmax_memory); return (int)status; } int DRA_Terminate() { Integer status; status = dra_terminate_(); return (int)status; } int DRA_Open(char* filename, int mode, int *d_a) { Integer mmode, dd_a, status; mmode = (Integer)mode; status = drai_open(filename, &mmode, &dd_a); *d_a = (int)dd_a; return (int)status; } int DRA_Probe(int request, int *compl_status) { Integer rrequest, ccompl_status, status; rrequest = (Integer)request; status = dra_probe_(&rrequest, &ccompl_status); *compl_status = (int)ccompl_status; return (int)status; } void DRA_Set_debug(logical flag) { dra_set_debug_(&flag); return; } void DRA_Print_internals(int d_a) { Integer dd_a; dd_a = (Integer)d_a; dra_print_internals_(&dd_a); return; } void DRA_Set_default_config(int numfiles, int numioprocs) { Integer nfiles, nprocs; nfiles = (Integer)numfiles; nprocs = (Integer)numioprocs; dra_set_default_config_(&nfiles, &nprocs); return; } int DRA_Wait(int request) { Integer rrequest, status; rrequest = (Integer)request; status = dra_wait_(&rrequest); return (int)status; } int DRA_Delete(int d_a) { Integer dd_a, status; dd_a = (Integer)d_a; status = dra_delete_(&dd_a); return (int)status; } int DRA_Close(int d_a) { Integer dd_a, status; dd_a = (Integer)d_a; status = dra_close_(&dd_a); return (int)status; } void DRA_Flick() { dra_flick_(); } ga-5.9.2/pario/dra/dbg_read.c000066400000000000000000000037441500715745200157070ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include "macommon.h" #include "global.h" #include "dra.h" #include "drap.h" #define LEN 10 int main(int argc, char **argv) { int from, to, type; Integer idata[LEN]; #if 0 int fd; #endif Integer i, ii, imax, offset, status; DoublePrecision ddata[LEN]; if(argc < 2){ printf("program prints data from a binary file to screen\n"); printf("usage: dbg_read.x \n"); printf("type: 1 - integer 2 - double \n -range of elements (0, ...)\n"); return(1); } type = atoi(argv[2]); from = atoi(argv[3]); to = atoi(argv[4]); if(from < 0 || to < from) {printf("range error\n"); return 1;} #if 0 if(!(fd = dra_el_open(argv[1],DRA_R))){printf("not found\n"); return 1;} #else /* TODO This must be an old test program using an old API... * consider removing this program. */ return 1; #endif switch (type){ case 1: for(i=from; i<= to; i+= LEN){ imax = PARIO_MIN(i+LEN-1,to); offset = sizeof(Integer)*i; #if 0 status=dra_el_read(idata, sizeof(Integer), imax -i+1, fd, offset); #else status = 1; #endif if(!status)printf("error read failed\n"); for(ii=0;ii #include "macommon.h" #include "global.h" #include "dra.h" #include "drap.h" #define LEN 10 int main(int argc, char **argv) { int from, to, type; Integer idata[LEN]; #if 0 int fd; #endif Integer i, ii, imax, offset, status; DoublePrecision ddata[LEN]; if(argc < 2){ printf("program writes test data to a binary file\n"); printf("usage: dbg_write.x \n"); printf("type: 1 - integer 2 - double \n -range of elements (0, ...)\n"); return(1); } type = atoi(argv[2]); from = atoi(argv[3]); to = atoi(argv[4]); if(from < 0 || to < from) {printf("range error\n"); return 1;} #if 0 if(!(fd = dra_el_open(argv[1],DRA_W))){printf("not found\n"); return 1;} #else /* TODO This must be an old test program using an old API... * consider removing this program. */ return 1; #endif switch (type){ case 1: for(i=from; i<= to; i+= LEN){ imax = PARIO_MIN(i+LEN-1,to); offset = sizeof(Integer)*i; for(ii=0;ii #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STDIO_H # include #endif #include "buffers.h" #include "dra.h" #include "draf2c.h" #include "drap.h" #include "global.h" #include "ga-papi.h" #include "macdecls.h" #define WALLTIME 0 #if WALLTIME # include "walltime.c" #endif /************************** constants ****************************************/ #define DRA_FAIL (Integer)1 #define COLUMN 1 #define ROW 0 #define ON 1 #define OFF 0 #define ILO 0 #define IHI 1 #define JLO 2 #define JHI 3 #define DRA_OP_WRITE 777 #define DRA_OP_READ 888 #define PROBE 111 #define WAIT 222 #define MAX_REQ 5 /** message type/tag used by DRA */ #define DRA_TYPE_GSM 32760 - 6 #define DRA_TYPE_GOP 32760 - 7 #define INFINITE_NUM_PROCS 8094 #define CLIENT_TO_SERVER 2 /* #define DRA_DBLE_BUFFER */ # define DRA_NUM_IOPROCS 1 #ifndef DRA_NUM_FILE_MGR # define DRA_NUM_FILE_MGR DRA_NUM_IOPROCS #endif #define DRA_BUF_SIZE BUF_SIZE #define DEF_MAX_ARRAYS 16 #define DRA_MAX_ARRAYS 1024 #define EPS_SEARCH 100 #define LOAD 1 #define STORE 2 #define TRANS 1 #define NOTRANS 0 /*#define DEBUG 1*/ /*#define CLEAR_BUF 1*/ /***************************** Global Data ***********************************/ /** Default number of IO procs */ Integer _dra_io_procs; /** Default number of files to use on open file systems */ Integer _dra_number_of_files; /** array of struct for basic info about DRA arrays*/ disk_array_t *DRA; buf_context_t buf_ctxt; /**< buffer context handle */ int nbuf = 4; /**< number of buffers to be used */ Integer _max_disk_array; /**< max number of disk arrays open at a time */ logical dra_debug_flag; /**< globally defined debug parameter */ request_t Requests[MAX_REQ]; int num_pending_requests=0; Integer _dra_turn=0; int Dra_num_serv=DRA_NUM_IOPROCS; /****************************** Macros ***************************************/ #define dai_sizeofM(_type) MA_sizeof(_type, 1, MT_C_CHAR) #define dai_check_typeM(_type) if (_type != C_DBL && _type != C_INT \ && _type != C_LONG && _type != C_DCPL && _type != C_FLOAT && _type != C_SCPL) \ dai_error("invalid type ",_type) #define dai_check_handleM(_handle, msg) \ {\ if((_handle+DRA_OFFSET)>=_max_disk_array || (_handle+DRA_OFFSET)<0) \ {fprintf(stderr,"%s, %ld --",msg, (long)_max_disk_array);\ dai_error("invalid DRA handle",_handle);} \ if( DRA[(_handle+DRA_OFFSET)].actv == 0) \ {fprintf(stderr,"%s:",msg);\ dai_error("disk array not active",_handle);} \ } #define dai_check_rangeM(_lo, _hi, _dim, _err_msg) \ if(_lo < (Integer)1 || _lo > _dim ||_hi < _lo || _hi > _dim) \ dai_error(_err_msg, _dim) #define ga_get_sectM(sect, _buf, _ld)\ pnga_get(sect.handle, sect.lo, sect.hi, _buf, &_ld) #define ga_put_sectM(sect, _buf, _ld)\ pnga_put(sect.handle, sect.lo, sect.hi, _buf, &_ld) #define fill_sectionM(sect, _hndl, _ilo, _ihi, _jlo, _jhi) \ { \ sect.handle = _hndl;\ sect.ndim = 2; \ sect.lo[0] = _ilo;\ sect.hi[0] = _ihi;\ sect.lo[1] = _jlo;\ sect.hi[1] = _jhi;\ } #define sect_to_blockM(ds_a, CR)\ {\ Integer hndl = (ds_a).handle+DRA_OFFSET;\ Integer br = ((ds_a).lo[0]-1)/DRA[hndl].chunk[0];\ Integer bc = ((ds_a).lo[1]-1)/DRA[hndl].chunk[1];\ Integer R = (DRA[hndl].dims[0] + DRA[hndl].chunk[0] -1)/DRA[hndl].chunk[0];\ *(CR) = bc * R + br;\ } #define block_to_sectM(ds_a, CR)\ {\ Integer hndl = (ds_a)->handle+DRA_OFFSET;\ Integer R = (DRA[hndl].dims[0] + DRA[hndl].chunk[0]-1)/DRA[hndl].chunk[0];\ Integer br = (CR)%R;\ Integer bc = ((CR) - br)/R;\ (ds_a)-> lo[0]= br * DRA[hndl].chunk[0] +1;\ (ds_a)-> lo[1]= bc * DRA[hndl].chunk[1] +1;\ (ds_a)-> hi[0]= (ds_a)->lo[0] + DRA[hndl].chunk[0];\ (ds_a)-> hi[1]= (ds_a)->lo[1] + DRA[hndl].chunk[1];\ if( (ds_a)->hi[0] > DRA[hndl].dims[0]) (ds_a)->hi[0] = DRA[hndl].dims[0];\ if( (ds_a)->hi[1] > DRA[hndl].dims[1]) (ds_a)->hi[1] = DRA[hndl].dims[1];\ } #define INDEPFILES(x) (DRA[(x)+DRA_OFFSET].indep) #define ITERATOR_2D(i,j, base, ds_chunk)\ for(j = ds_chunk.lo[1], base=0, jj=0; j<= ds_chunk.hi[1]; j++,jj++)\ for(i = ds_chunk.lo[0], ii=0; i<= ds_chunk.hi[0]; i++,ii++,base++) #define COPY_SCATTER(ADDR_BASE, TYPE, ds_chunk)\ ITERATOR_2D(i,j, base, ds_chunk) \ ADDR_BASE[base+vindex] = ((TYPE*)buffer)[ldb*jj + ii] #define COPY_GATHER(ADDR_BASE, TYPE, ds_chunk)\ for(i=0; i< nelem; i++){\ Integer ldc = ds_chunk.hi[0] - ds_chunk.lo[0]+1;\ base = INT_MB[pindex+i]; jj = base/ldc; ii = base%ldc;\ ((TYPE*)buffer)[ldb*jj + ii] = ADDR_BASE[i+vindex];\ } #define COPY_TYPE(OPERATION, MATYPE, ds_chunk)\ switch(MATYPE){\ case C_DBL: COPY_ ## OPERATION(DBL_MB,double,ds_chunk);break;\ case C_INT: COPY_ ## OPERATION(INT_MB,int,ds_chunk);break;\ case C_DCPL: COPY_ ## OPERATION(DCPL_MB,DoubleComplex,ds_chunk);break;\ case C_SCPL: COPY_ ## OPERATION(SCPL_MB,SingleComplex,ds_chunk);break;\ case C_FLOAT: COPY_ ## OPERATION(FLT_MB,float,ds_chunk);\ } #define nsect_to_blockM(ds_a, CR) \ { \ Integer hndl = (ds_a).handle+DRA_OFFSET;\ Integer _i, _ndim = DRA[hndl].ndim; \ Integer _R, _b; \ *(CR) = 0; \ _R = 0; \ for (_i=_ndim-1; _i >= 0; _i--) { \ _b = ((ds_a).lo[_i]-1)/DRA[hndl].chunk[_i]; \ _R = (DRA[hndl].dims[_i]+DRA[hndl].chunk[_i]-1)/DRA[hndl].chunk[_i];\ *(CR) = *(CR) * _R + _b; \ } \ } #define dai_dest_indices_1d_M(index, id, jd, ilod, jlod, ldd) \ { \ Integer _index_;\ _index_ = (is)-(ilos);\ *(id) = (_index_)%(ldd) + (ilod);\ *(jd) = (_index_)/(ldd) + (jlod);\ } #define dai_dest_indicesM(is, js, ilos, jlos, lds, id, jd, ilod, jlod, ldd)\ { \ Integer _index_;\ _index_ = (lds)*((js)-(jlos)) + (is)-(ilos);\ *(id) = (_index_)%(ldd) + (ilod);\ *(jd) = (_index_)/(ldd) + (jlod);\ } #define nga_get_sectM(sect, _buf, _ld, hdl)\ if (hdl != NULL)\ pnga_nbget(sect.handle, sect.lo, sect.hi, _buf, _ld, hdl);\ else\ pnga_get(sect.handle, sect.lo, sect.hi, _buf, _ld); #define nga_put_sectM(sect, _buf, _ld, hdl)\ if (hdl != NULL)\ pnga_nbput(sect.handle, sect.lo, sect.hi, _buf, _ld, hdl);\ else\ pnga_put(sect.handle, sect.lo, sect.hi, _buf, _ld); #define ndai_dest_indicesM(ds_chunk, ds_a, gs_chunk, gs_a) \ {\ Integer _i; \ Integer _ndim = ds_a.ndim; \ for (_i=0; _i<_ndim; _i++) { \ gs_chunk.lo[_i] = gs_a.lo[_i] + ds_chunk.lo[_i]- ds_a.lo[_i]; \ gs_chunk.hi[_i] = gs_a.lo[_i] + ds_chunk.hi[_i]- ds_a.lo[_i]; \ } \ } #define ndai_trnsp_dest_indicesM(ds_chunk, ds_a, gs_chunk, gs_a) \ {\ Integer _i; \ Integer _ndim = ds_a.ndim; \ for (_i=0; _i<_ndim; _i++) { \ gs_chunk.lo[_ndim-1-_i] = gs_a.lo[_ndim-1-_i] \ + ds_chunk.lo[_i]- ds_a.lo[_i]; \ gs_chunk.hi[_ndim-1-_i] = gs_a.lo[_ndim-1-_i] \ + ds_chunk.hi[_i]- ds_a.lo[_i]; \ } \ } /** Simple sort using straight insertion */ #define block_sortM(_ndim, _block_orig, _block_map) \ {\ Integer _i,_j,_it,_bt; \ Integer _block_tmp[MAXDIM]; \ for (_i=0; _i < (_ndim); _i++) { \ _block_map[_i] = _i; \ _block_tmp[_i] = _block_orig[_i]; \ } \ for (_j=(_ndim)-2; _j >= 0; _j--) { \ _i = _j + 1; \ _bt = _block_tmp[_j]; \ _it = _block_map[_j]; \ while (_i < (_ndim) && _bt < _block_tmp[_i]) { \ _block_tmp[_i-1] = _block_tmp[_i]; \ _block_map[_i-1] = _block_map[_i]; \ _i++; \ }\ _block_tmp[_i-1] = _bt; \ _block_map[_i-1] = _it; \ }\ } #define nfill_sectionM(sect, _hndl, _ndim, _lo, _hi) \ { \ Integer _i; \ sect.handle = _hndl; \ sect.ndim = _ndim; \ for (_i=0; _i<_ndim; _i++) { \ sect.lo[_i] = _lo[_i]; \ sect.hi[_i] = _hi[_i]; \ } \ } #define nblock_to_sectM(ds_a, _CR) \ {\ Integer _i, _b[MAXDIM], _C = (_CR); \ Integer _hndl = (ds_a)->handle+DRA_OFFSET; \ Integer _R = (DRA[_hndl].dims[0]+DRA[_hndl].chunk[0]-1)/DRA[_hndl].chunk[0]; \ (ds_a)->ndim = DRA[_hndl].ndim; \ _b[0] = _C%_R; \ for (_i=1; _ilo[_i] = _b[_i]*DRA[_hndl].chunk[_i] + 1; \ (ds_a)->hi[_i] = (ds_a)->lo[_i] + DRA[_hndl].chunk[_i] - 1; \ if ((ds_a)->hi[_i] > DRA[_hndl].dims[_i]) \ (ds_a)->hi[_i] = DRA[_hndl].dims[_i]; \ } \ } #define nblock_to_indicesM(_index,_ndim,_block_dims,_CC) \ { \ Integer _i, _C=(_CC); \ _index[0] = _C%_block_dims[0]; \ for (_i=1; _i<(_ndim); _i++) { \ _C = (_C - _index[_i-1])/_block_dims[_i-1]; \ _index[_i] = _C%_block_dims[_i]; \ } \ } #define ndai_check_rangeM(_lo, _hi, _ndim, _dims, _err_msg) \ { \ int _range_ok = 1, _i; \ for (_i=0; _i < (_ndim); _i++) { \ if (_lo[_i] < 1 || _lo[_i] > _dims[_i] || _hi[_i] < _lo[_i] \ || _hi[_i] > _dims[_i] ) _range_ok = 0; \ } \ if(!_range_ok) dai_error(_err_msg, _dim); \ } char dummy_fname[DRA_MAX_FNAME]; /*****************************************************************************/ /** * determines if write operation to a disk array is allowed */ int dai_write_allowed(Integer d_a) { Integer handle = d_a+DRA_OFFSET; if(DRA[handle].mode == DRA_W || DRA[handle].mode == DRA_RW) return 1; else return 0; } /** * determines if read operation from a disk array is allowed */ int dai_read_allowed(Integer d_a) { Integer handle = d_a+DRA_OFFSET; if(DRA[handle].mode == DRA_R || DRA[handle].mode == DRA_RW) return 1; else return 0; } /** * number of processes that could perform I/O */ Integer dai_io_procs(Integer d_a) { Integer handle = d_a+DRA_OFFSET; Integer num; /* this one of many possibilities -- depends on the system */ /* num = (INDEPFILES(d_a)) ? INFINITE_NUM_PROCS: DRA_NUM_IOPROCS; */ if (INDEPFILES(d_a)) { num = pnga_cluster_nnodes(); } else { num = DRA[handle].ioprocs; } return( PARIO_MIN( pnga_nnodes(), num)); } /** * Translation of DRA create/opening modes to ELIO create/open * mode. DRA modes map directly to ELIO modes, except that write-only * DRAs are backed by read-write ELIO files. */ int dai_elio_mode(int dra_mode) { int emode = dra_mode; /* dra modes map to elio mode*/ if(dra_mode == DRA_W) { /*except W, which translate to read-write files*/ emode = ELIO_RW; } return emode; } /** * rank of calling process in group of processes that could perform I/O * a negative value means that this process doesn't do I/O */ Integer dai_io_nodeid(Integer d_a) { Integer handle = d_a+DRA_OFFSET; Integer me = pnga_nodeid(); Integer pid, id, nid, nnodes,nprocs; Integer nodeid = pnga_cluster_nodeid(); Integer zero = 0; /* again, one of many possibilities: * if proc id beyond I/O procs number, negate it */ if (INDEPFILES(d_a)) { if(me == pnga_cluster_procid(nodeid, zero)) me = nodeid; else me = -1; } else { if (DRA[handle].ioprocs == 1) { if (me == 0) return me; else return -1; } else { nnodes = pnga_cluster_nnodes(); nprocs = pnga_cluster_nprocs(nodeid); pid = me % nprocs; nid = (me - pid)/nprocs; id = pid * nnodes + nid; if (id < DRA[handle].ioprocs) return id; else return -1; } } /* if (me >= dai_io_procs(d_a)) me = -me;*/ return (me); } /** * determines if I/O process participates in file management (create/delete) */ Integer dai_io_manage(Integer d_a) { Integer me = dai_io_nodeid(d_a); if(me >= 0 ) return (1); else return (0); } /** * select one master process for each file associated with d_a */ Integer dai_file_master(Integer d_a) { Integer handle = d_a+DRA_OFFSET; if(dai_io_nodeid(d_a)<0)return 0; /* for indep files each I/O process has its own file * for shared file 0 is the master */ if(INDEPFILES(d_a) || DRA[handle].numfiles > 1 || dai_io_nodeid(d_a) == 0 ) return 1; else return 0; } void dai_callback(int op, int transp, section_t gs_a, section_t ds_a, section_t ds_chunk, Integer ld[], char *buf, Integer req) { int i; buf_info *bi; bi = (buf_info*) buf; if (bi->callback==ON) dai_error("DRA: callback not cleared for a buffer",0); bi->callback = ON; bi->args.op = op; bi->args.transp = transp; bi->args.gs_a = gs_a; bi->args.ds_a = ds_a; bi->args.ds_chunk = ds_chunk; for (i=0; iargs.ld[i] = ld[i]; } /** * function to release buffers by completing the transfers * this function will be passed on as a parameter to a buffer management layer */ void wait_buf(char *buf); /** * INITIALIZE DISK ARRAY DATA STRUCTURES * * @param max_arrays[in] * @param max_array_size[in] * @param tot_disk_space[in] * @param max_memory[in] */ Integer FATR dra_init_( Integer *max_arrays, DoublePrecision *max_array_size, DoublePrecision *tot_disk_space, DoublePrecision *max_memory) { int i, buf_size; pnga_sync(); if(*max_arrays<-1 || *max_arrays> DRA_MAX_ARRAYS) dai_error("dra_init: incorrect max number of arrays",*max_arrays); _max_disk_array = (*max_arrays==-1) ? DEF_MAX_ARRAYS: *max_arrays; Dra_num_serv = drai_get_num_serv(); DRA = (disk_array_t*)malloc(sizeof(disk_array_t)* (int)*max_arrays); if(!DRA) dai_error("dra_init: memory alocation failed\n",0); for(i=0; i<_max_disk_array ; i++)DRA[i].actv=0; for(i=0; i PARIO_MIN(1,trial-EPS_SEARCH); bt--){ eps1 = prod%bt; if(eps1 < eps0){ /* better solution found */ b0 = bt; eps0 = eps1; } } *a = prod/b0; *b = b0; } /** * compute chunk parameters for layout of arrays on the disk * ---- a very simple algorithm to be refined later ---- * @param elem_size[in] Size of stored data * @param block1[in] Estimated size of request in dimension 1 * @param block2[in] Estimated size of request in dimension 2 * @param dim1[in] Size of DRA in dimension 1 * @param dim2[in] Size of DRA in dimension 2 * @param chunk1[out] Data block size in dimension 1? * @param chunk2[out] Data block size in dimension 2? */ void dai_chunking(Integer elem_size, Integer block1, Integer block2, Integer dim1, Integer dim2, Integer *chunk1, Integer *chunk2) { Integer patch_size; *chunk1 = *chunk2 =0; if(block1 <= 0 && block2 <= 0){ *chunk1 = dim1; *chunk2 = dim2; }else if(block1 <= 0){ *chunk2 = block2; *chunk1 = PARIO_MAX(1, DRA_BUF_SIZE/(elem_size* (*chunk2))); }else if(block2 <= 0){ *chunk1 = block1; *chunk2 = PARIO_MAX(1, DRA_BUF_SIZE/(elem_size* (*chunk1))); }else{ *chunk1 = block1; *chunk2 = block2; } /* need to correct chunk size to fit chunk1 x chunk2 request in buffer*/ patch_size = (*chunk1)* (*chunk2)*elem_size; if (patch_size > ((Integer)DRA_BUF_SIZE)){ if( *chunk1 == 1) *chunk2 = DRA_BUF_SIZE/elem_size; else if( *chunk2 == 1) *chunk1 = DRA_BUF_SIZE/elem_size; else { double ratio = ((double)patch_size)/((double)DRA_BUF_SIZE); /* smaller chunk to be scaled first */ if(*chunk1 < *chunk2){ dai_correct_chunking(chunk2,chunk1,DRA_BUF_SIZE/elem_size,ratio); }else{ dai_correct_chunking(chunk1,chunk2,DRA_BUF_SIZE/elem_size,ratio); } } } #ifdef DEBUG printf("\n%d:CREATE chunk=(%d,%d) elem_size=%d req=(%d,%d) buf=%d\n", pnga_nodeid(),*chunk1, *chunk2, elem_size, block1, block2, DRA_DBL_BUF_SIZE); fflush(stdout); #endif } /** * get a new handle for disk array */ Integer dai_get_handle(void) { Integer dra_handle =-1, candidate = 0; do{ if(!DRA[candidate].actv){ dra_handle=candidate; DRA[candidate].actv =1; } candidate++; }while(candidate < _max_disk_array && dra_handle == -1); return(dra_handle); } /** * release handle -- makes array inactive */ void dai_release_handle(Integer *handle) { DRA[*handle+DRA_OFFSET].actv =0; *handle = 0; } /** * find offset in file for (ilo,ihi) element */ void dai_file_location(section_t ds_a, Off_t* offset) { Integer row_blocks, handle=ds_a.handle+DRA_OFFSET, offelem, cur_ld, part_chunk1; if((ds_a.lo[0]-1)%DRA[handle].chunk[0]) dai_error("dai_file_location: not alligned ??",ds_a.lo[0]); row_blocks = (ds_a.lo[0]-1)/DRA[handle].chunk[0];/* # row blocks from top*/ part_chunk1 = DRA[handle].dims[0]%DRA[handle].chunk[0];/*dim1 in part block*/ cur_ld = (row_blocks == DRA[handle].dims[0] / DRA[handle].chunk[0]) ? part_chunk1: DRA[handle].chunk[0]; /* compute offset (in elements) */ if(INDEPFILES(ds_a.handle) || DRA[handle].numfiles > 1) { Integer CR, R; Integer i, num_part_block = 0; Integer ioprocs=dai_io_procs(ds_a.handle); Integer iome = dai_io_nodeid(ds_a.handle); sect_to_blockM(ds_a, &CR); R = (DRA[handle].dims[0] + DRA[handle].chunk[0]-1)/DRA[handle].chunk[0]; for(i = R -1; i< CR; i+=R) if(i%ioprocs == iome)num_part_block++; if(!part_chunk1) part_chunk1=DRA[handle].chunk[0]; offelem = ((CR/ioprocs - num_part_block)*DRA[handle].chunk[0] + num_part_block * part_chunk1 ) * DRA[handle].chunk[1]; /* add offset within block */ offelem += ((ds_a.lo[1]-1) %DRA[handle].chunk[1])*cur_ld; } else { offelem = row_blocks * DRA[handle].dims[1] * DRA[handle].chunk[0]; offelem += (ds_a.lo[1]-1)*cur_ld; } *offset = offelem* dai_sizeofM(DRA[handle].type); } /** * write aligned block of data from memory buffer to d_a */ void dai_put( section_t ds_a, void *buf, Integer ld, io_request_t *id) { Integer handle = ds_a.handle + DRA_OFFSET, elem; Off_t offset; Size_t bytes; /* find location in a file where data should be written */ dai_file_location(ds_a, &offset); if((ds_a.hi[0] - ds_a.lo[0] + 1) != ld) dai_error("dai_put: bad ld",ld); /* since everything is aligned, write data to disk */ elem = (ds_a.hi[0] - ds_a.lo[0] + 1) * (ds_a.hi[1] - ds_a.lo[1] + 1); bytes= (Size_t) elem * dai_sizeofM(DRA[handle].type); if( ELIO_OK != elio_awrite(DRA[handle].fd, offset, buf, bytes, id )) dai_error("dai_put failed", ds_a.handle); } /** * write zero at EOF */ void dai_zero_eof(Integer d_a) { Integer handle = d_a+DRA_OFFSET, nelem; char byte; Off_t offset; byte = (char)0; if(INDEPFILES(d_a) || DRA[handle].numfiles > 1) { Integer CR=0, i=0, nblocks=0; section_t ds_a; /* number of processors that do io */ Integer ioprocs=dai_io_procs(d_a); /* node id of current process (if it does io) */ Integer iome = dai_io_nodeid(d_a); /* total number of blocks in the disk resident array */ nblocks = ((DRA[handle].dims[0] + DRA[handle].chunk[0]-1)/DRA[handle].chunk[0]) * ((DRA[handle].dims[1] + DRA[handle].chunk[1]-1)/DRA[handle].chunk[1]); fill_sectionM(ds_a, d_a, 0, 0, 0, 0); /* search for the last block for each I/O processor */ for(i = 0; i 1) { sprintf(dummy_fname,"%s.%ld",DRA[handle].fname,(long)dai_io_nodeid(*d_a)); DRA[handle].fd = elio_open(dummy_fname,emode, ELIO_PRIVATE); }else{ DRA[handle].fd = elio_open(DRA[handle].fname,emode, ELIO_SHARED); } if(DRA[handle].fd ==NULL)dai_error("dra_open failed (null)", pnga_nodeid()); if(DRA[handle].fd->fd ==-1)dai_error("dra_open failed (-1)", pnga_nodeid()); } #ifdef DEBUG printf("\n%d:OPEN chunking=(%d,%d) type=%d buf=%d\n", pnga_nodeid(),DRA[handle].chunk[0], DRA[handle].chunk[1], DRA[handle].type, DRA_DBL_BUF_SIZE); fflush(stdout); #endif pnga_sync(); /* printf("FILE OPENED!!\n"); */ return(ELIO_OK); } /** * CLOSE AN ARRAY AND SAVE IT ON THE DISK */ Integer FATR dra_close_(Integer* d_a) /* input:DRA handle*/ { Integer handle = *d_a+DRA_OFFSET; int rc; pnga_sync(); dai_check_handleM(*d_a, "dra_close"); if(dai_io_manage(*d_a)) if(ELIO_OK != (rc=elio_close(DRA[handle].fd))) dai_error("dra_close: close failed",rc); dai_release_handle(d_a); pnga_sync(); return(ELIO_OK); } /** * decompose [ilo:ihi, jlo:jhi] into aligned and unaligned DRA subsections * * * section [ilo:ihi, jlo:jhi] is decomposed into a number of * 'aligned' and 'unaligned' (on chunk1/chunk2 bounday) subsections * depending on the layout of the 2D array on the disk; * * 'cover' subsections correspond to 'unaligned' subsections and * extend them to aligned on chunk1/chunk2 boundaries; * * disk I/O will be actually performed on 'aligned' and * 'cover' instead of 'unaligned' subsections */ void dai_decomp_section( section_t ds_a, Integer aligned[][2*MAXDIM], int *na, Integer cover[][2*MAXDIM], Integer unaligned[][2*MAXDIM], int *nu) { Integer a=0, u=0, handle = ds_a.handle+DRA_OFFSET, off, chunk_units, algn_flag; aligned[a][ ILO ] = ds_a.lo[0]; aligned[a][ IHI ] = ds_a.hi[0]; aligned[a][ JLO ] = ds_a.lo[1]; aligned[a][ JHI ] = ds_a.hi[1]; switch (DRA[handle].layout){ case COLUMN : /* need to check row alignment only */ algn_flag = ON; /* has at least one aligned subsection */ /* top of section */ off = (ds_a.lo[0] -1) % DRA[handle].chunk[0]; if(off){ if(MAX_UNLG<= u) dai_error("dai_decomp_sect:insufficient nu",u); chunk_units = (ds_a.lo[0] -1) / DRA[handle].chunk[0]; cover[u][ ILO ] = chunk_units*DRA[handle].chunk[0] + 1; cover[u][ IHI ] = PARIO_MIN(cover[u][ ILO ] + DRA[handle].chunk[0]-1, DRA[handle].dims[0]); unaligned[u][ ILO ] = ds_a.lo[0]; unaligned[u][ IHI ] = PARIO_MIN(ds_a.hi[0],cover[u][ IHI ]); unaligned[u][ JLO ] = cover[u][ JLO ] = ds_a.lo[1]; unaligned[u][ JHI ] = cover[u][ JHI ] = ds_a.hi[1]; if(cover[u][ IHI ] < ds_a.hi[0]){ /* cover subsection ends above ihi */ if(MAX_ALGN<=a) dai_error("dai_decomp_sect: na too small",a); aligned[a][ ILO ] = cover[u][ IHI ]+1; }else{ /* cover subsection includes ihi */ algn_flag = OFF; } u++; } /* bottom of section */ off = ds_a.hi[0] % DRA[handle].chunk[0]; if(off && (ds_a.hi[0] != DRA[handle].dims[0]) && (algn_flag == ON)){ if(MAX_UNLG<=u) dai_error("dai_decomp_sect:insufficient nu",u); chunk_units = ds_a.hi[0] / DRA[handle].chunk[0]; cover[u][ ILO ] = chunk_units*DRA[handle].chunk[0] + 1; cover[u][ IHI ] = PARIO_MIN(cover[u][ ILO ] + DRA[handle].chunk[0]-1, DRA[handle].dims[0]); unaligned[u][ ILO ] = cover[u][ ILO ]; unaligned[u][ IHI ] = ds_a.hi[0]; unaligned[u][ JLO ] = cover[u][ JLO ] = ds_a.lo[1]; unaligned[u][ JHI ] = cover[u][ JHI ] = ds_a.hi[1]; aligned[a][ IHI ] = PARIO_MAX(1,unaligned[u][ ILO ]-1); algn_flag=(DRA[handle].chunk[0] == DRA[handle].dims[0])?OFF:ON; u++; } *nu = (int)u; if(aligned[0][ IHI ]-aligned[0][ ILO ] < 0) algn_flag= OFF; *na = (algn_flag== OFF)? 0: 1; break; case ROW : /* we need to check column alignment only */ default: dai_error("dai_decomp_sect: ROW layout not yet implemented", DRA[handle].layout); } } /** * given current (i,j) compute (ni, nj) - next loop index * o - outermost loop, i- innermost loop * iinc increment for i * oinc increment for o */ int dai_next2d(Integer* i, Integer imin, Integer imax, Integer iinc, Integer* o, Integer omin, Integer omax, Integer oinc) { int retval; if (*o == 0 || *i == 0) { /* to handle initial out-of range indices */ *o = omin; *i = imin; } else { *i = *i + iinc; } if (*i > imax) { *i = imin; *o += oinc; } retval = (*o <= omax); return retval; } /** * compute next chunk of array to process */ int dai_next_chunk(Integer req, Integer* list, section_t* ds_chunk) { Integer handle = ds_chunk->handle+DRA_OFFSET; int retval; if(INDEPFILES(ds_chunk->handle) || DRA[handle].numfiles > 1) if(ds_chunk->lo[1] && DRA[handle].chunk[1]>1) ds_chunk->lo[1] -= (ds_chunk->lo[1] -1) % DRA[handle].chunk[1]; retval = dai_next2d(&ds_chunk->lo[0], list[ ILO ], list[ IHI ], DRA[handle].chunk[0], &ds_chunk->lo[1], list[ JLO ], list[ JHI ], DRA[handle].chunk[1]); if(!retval) return(retval); ds_chunk->hi[0] = PARIO_MIN(list[ IHI ], ds_chunk->lo[0] + DRA[handle].chunk[0] -1); ds_chunk->hi[1] = PARIO_MIN(list[ JHI ], ds_chunk->lo[1] + DRA[handle].chunk[1] -1); if(INDEPFILES(ds_chunk->handle) || DRA[handle].numfiles > 1) { Integer jhi_temp = ds_chunk->lo[1] + DRA[handle].chunk[1] -1; jhi_temp -= jhi_temp % DRA[handle].chunk[1]; ds_chunk->hi[1] = PARIO_MIN(ds_chunk->hi[1], jhi_temp); /*this line was absent from older version on bonnie that worked */ if(ds_chunk->lo[1] < list[ JLO ]) ds_chunk->lo[1] = list[ JLO ]; } return 1; } int dai_myturn(section_t ds_chunk) { /* Integer handle = ds_chunk.handle+DRA_OFFSET; */ Integer ioprocs = dai_io_procs(ds_chunk.handle); Integer iome = dai_io_nodeid(ds_chunk.handle); /* if(INDEPFILES(ds_chunk.handle) || DRA[handle].numfiles > 1){ */ /* compute cardinal number for the current chunk */ nsect_to_blockM(ds_chunk, &_dra_turn); /* }else{ _dra_turn++; } */ return ((_dra_turn%ioprocs) == iome); } /** * print routine for debugging purposes only (double) */ void dai_print_buf(double *buf, Integer ld, Integer rows, Integer cols) { int i,j; printf("\n ld=%ld rows=%ld cols=%ld\n",(long)ld,(long)rows,(long)cols); for (i=0; i 1) { itmp = i; index[1] = itmp%ldg[1]; for (j=2; j 1) { itmp = i; index[1] = itmp%ldg[1]; for (j=2; jcallback==OFF) return; bi->callback = OFF; arg = &(bi->args); /* bail if there is no valid global array handle */ if (arg->gs_a.handle == 0) return; buffer = (char*) (buf + sizeof(buf_info)); if (caller == WAIT) {/* call blocking nga_move() */ nga_move(arg->op, arg->transp, arg->gs_a, arg->ds_a, arg->ds_chunk, buffer, arg->ld, NULL); ga_dra_free_buf(&buf_ctxt, buf); } else if (caller == PROBE) /* call non-blocking nga_move() */ nga_move(arg->op, arg->transp, arg->gs_a, arg->ds_a, arg->ds_chunk, buffer, arg->ld, &(bi->ga_movhdl)); } /** * wait until buffer space associated with request is avilable */ void dai_wait(Integer req0) { /* Integer req; int ibuf; */ /* wait for all requests to complete on buffer Requests[req].ibuf */ /* ibuf = Requests[req0].ibuf; for(req=0; reqop; io_req = &(bi->io_req); ga_movhdl = &(bi->ga_movhdl); if (op_code == DRA_OP_WRITE) { /* last op is a disk write */ if(elio_probe(io_req, &stat) != ELIO_OK) { ret = DRA_FAIL; k = 0; break; } if (stat != ELIO_DONE) { k = 0; } else { ga_dra_free_buf(&buf_ctxt, bufs[i]); } } else if (op_code == DRA_OP_READ) { /* last op depends on aligned or unaligned transfer */ if (bi->align == 0) { /* unaligned read */ /* last op is a ga move */ if (pnga_nbtest(ga_movhdl) == 0) { /* ga op not complete */ k = 0; } else { /* ga op complete, free this buf */ ga_dra_free_buf(&buf_ctxt, bufs[i]); } } else { /* if aligned read, last op is a disk read */ if(elio_probe(io_req, &stat) != ELIO_OK) { ret = DRA_FAIL; k = 0; break; } if (stat != ELIO_DONE) k = 0; else { /* disk read done, initiate/test ga move */ /* callback=OFF means ga move done/being done */ if (bi->callback == OFF && pnga_nbtest(ga_movhdl) == 0) k = 0; else if (bi->callback == OFF && pnga_nbtest(ga_movhdl) ==1) { ga_dra_free_buf(&buf_ctxt, bufs[i]); } else if (bi->callback == ON) {/* need to call callback */ k = 0; cb[i] = 1; /* mark for a ga move */ } } } } } done = (Integer) k; /* determine global status */ pnga_gop(pnga_type_f2c(MT_F_INT), &done, (Integer)1, op); if(done){ *status = ELIO_DONE; Requests[*request].num_pending = 0; } else { *status = 0; for (i = 0; i < n_buf; i++) if (cb[i]) dai_exec_callback(bufs[i], PROBE); } if (ret == DRA_FAIL) *status = 0; /* basically value of status is irrelevant/undetermined in this case */ return ((Integer) ret); } /** * Returns control to DRA for a VERY short time to improve progress */ void dra_flick_() { Integer req, stat; for (req = 0; req < MAX_REQ; req++) { if (Requests[req].num_pending) { dra_probe_(&req, &stat); } } } /** * INQUIRE PARAMETERS OF EXISTING DISK ARRAY * @param d_a[in] DRA handle * @param type[out] * @param dim1[out] * @param dim2[out] * @param name[out] * @param filename[out] */ Integer drai_inquire(Integer *d_a, Integer *type, Integer *dim1, Integer *dim2, char *name, char *filename) { Integer handle=*d_a+DRA_OFFSET; dai_check_handleM(*d_a,"dra_inquire"); *type = (Integer)DRA[handle].type; *dim1 = DRA[handle].dims[0]; *dim2 = DRA[handle].dims[1]; strcpy(name, DRA[handle].name); strcpy(filename, DRA[handle].fname); return(ELIO_OK); } /** * DELETE DISK ARRAY -- relevant file(s) gone * * @param d_a[in] DRA handle */ Integer FATR dra_delete_(Integer* d_a) { Integer handle = *d_a+DRA_OFFSET; int rc; pnga_sync(); dai_check_handleM(*d_a,"dra_delete"); dai_delete_param(DRA[handle].fname,*d_a); if(dai_io_manage(*d_a)) if(ELIO_OK != (rc=elio_close(DRA[handle].fd))) dai_error("dra_close: close failed",rc); if(dai_file_master(*d_a)) { if(INDEPFILES(*d_a) || DRA[handle].numfiles > 1){ sprintf(dummy_fname,"%s.%ld",DRA[handle].fname,(long)dai_io_nodeid(*d_a)); elio_delete(dummy_fname); } else { elio_delete(DRA[handle].fname); } } dai_release_handle(d_a); pnga_sync(); return(ELIO_OK); } /** * TERMINATE DRA DRATA STRUCTURES */ Integer FATR dra_terminate_() { free(DRA); buf_terminate(&buf_ctxt); pnga_sync(); return(ELIO_OK); } /** * compute chunk parameters for layout of arrays on the disk * ---- a very simple algorithm to be refined later ---- * * @param elem_size[in] Size of individual data element in bytes * @param ndim[in] Dimension of DRA * @param block_orig[in] Estimated size of request in each coordinate * direction. If size is unknown then use -1. * @param dims[in] Size of DRA in each coordinate direction * @param chunk[out] Size of data block size (in elements) in each * coordinate direction */ void ndai_chunking(Integer elem_size, Integer ndim, Integer block_orig[], Integer dims[], Integer chunk[]) { long patch_size, tmp_patch; Integer i, j, block[MAXDIM], block_map[MAXDIM]; double ratio; logical full_buf, some_neg, overfull_buf; /* copy block_orig so that original guesses are not destroyed */ for (i=0; i dims[i]) block[i] = dims[i]; } /* do additional adjustments to see if initial guesses are near some perfect factors of DRA dimensions */ for (i=0; i 0 && block[i] 0) patch_size *= (long)block[i]; else some_neg = TRUE; } if (patch_size*((long)elem_size) > ((long)DRA_BUF_SIZE)) overfull_buf = TRUE; /* map dimension sizes from highest to lowest */ block_sortM(ndim, dims, block_map); /* IO buffer is not full and there are some unspecied chunk dimensions. Set unspecified dimensions equal to block dimensions until buffer is filled. */ if (!full_buf && !overfull_buf && some_neg) { for (i=ndim-1; i>=0; i--) { if (block[block_map[i]] < 0) { tmp_patch = patch_size * ((long)dims[block_map[i]]); if (tmp_patch*elem_size < ((long)DRA_BUF_SIZE)) { patch_size *= (long)dims[block_map[i]]; block[block_map[i]] = dims[block_map[i]]; } else { block[block_map[i]] = (Integer)(DRA_BUF_SIZE/(patch_size*((long)elem_size))); patch_size *= ((long)block[block_map[i]]); full_buf = TRUE; } } } } /* copy block array to chunk array */ for (i=0; i 0) chunk[i] = block[i]; else chunk[i] = 1; } /* If patch overfills buffer, scale patch down until it fits */ if (overfull_buf) { ratio = ((double)DRA_BUF_SIZE) / ((double)(patch_size*((long)elem_size))); ratio = pow(ratio,1.0/((double)ndim)); patch_size = 1; for (i=0; i ((long)DRA_BUF_SIZE)) { /* map chunks from highest to lowest */ block_sortM(ndim, chunk, block_map); for (i=0; i < ndim; i++) { while (chunk[block_map[i]] > 1 && ((long)elem_size)*patch_size > ((long)DRA_BUF_SIZE)) { patch_size /= ((long)chunk[block_map[i]]); chunk[block_map[i]]--; patch_size *= ((long)chunk[block_map[i]]); } } } } /** * find offset in file for (lo,hi) element */ void ndai_file_location(section_t ds_a, Off_t* offset) { Integer handle=ds_a.handle+DRA_OFFSET, ndim, i, j; Integer blocks[MAXDIM], part_chunk[MAXDIM], cur_ld[MAXDIM]; long par_block[MAXDIM]; long offelem=0; ndim = DRA[handle].ndim; for (i=0; i 1) { Integer CR, block_dims[MAXDIM]; Integer index[MAXDIM]; long nelem; Integer i, j; Integer ioprocs = dai_io_procs(ds_a.handle); Integer iome = dai_io_nodeid(ds_a.handle); /* Find index of current block and find number of chunks in each dimension of DRA */ nsect_to_blockM(ds_a, &CR); for (i=0; i= 0) { offelem = 0; for (i=iome; i 1) { Integer CR=0, i=0, nblocks=0; section_t ds_a; /* number of processors that do io */ Integer ioprocs=dai_io_procs(d_a); /* node id of current process (if it does io) */ Integer iome = dai_io_nodeid(d_a); /* total number of blocks in the disk resident array */ nblocks = 1; for (i=0; i 0) { numfiles = numioprocs; } else { numfiles = pnga_cluster_nnodes(); } } if (numioprocs < 1) { numioprocs = numfiles; } *number_of_files = numfiles; *io_procs = numioprocs; if (*number_of_files > pnga_nnodes()) { if (pnga_nodeid() == 0) { printf("WARNING: Number of files requested exceeds number of\n"); printf("processors. Value is reset to number of processors: %ld\n", (long)pnga_nnodes()); } *number_of_files = pnga_nnodes(); } if (*io_procs > 1 && *number_of_files > 1) { if (*io_procs != *number_of_files) { if (pnga_nodeid() == 0) { printf("WARNING: Number of IO processors is not equal to the\n"); printf("number of files requested. Number of IO processors\n"); printf("is reset to number of files: %ld\n",(long)*number_of_files); } *io_procs = *number_of_files; } } if (*number_of_files == 1) { if (*io_procs > pnga_nnodes()) { if (pnga_nodeid() == 0) { printf("WARNING: Number of requested IO processors\n"); printf("exceeds number of available processors. Number of IO\n"); printf("processors reset to the number of available processors %ld\n", (long)pnga_nnodes()); } *io_procs = pnga_nnodes(); } } if (*number_of_files > *io_procs) { if (pnga_nodeid() == 0) { printf("WARNING: Number of files is greater than\n"); printf("number of IO processors. Number of files reset to number of\n"); printf("IO processors: %ld",(long)*io_procs); } *number_of_files = *io_procs; } } /** * CREATE AN N-DIMENSIONAL DISK ARRAY WITH USER SPECIFIED IO CONFIGURATION * * @param type[in] * @param ndim[in] dimension of DRA * @param dims[in] dimensions of DRA * @param name[in] * @param filename[in] * @param mode[in] * @param reqdims[in] dimension of typical request * @param numfiles[in] number of files for DRA * @param numioprocs[in] number of IO procs to use * @param d_a[out] DRA handle */ Integer ndrai_create_config(Integer *type, Integer *ndim, Integer dims[], char *name, char *filename, Integer *mode, Integer reqdims[], Integer *numfiles, Integer *numioprocs, Integer *d_a) { Integer handle, elem_size, ctype, i; int emode; /* convert Fortran to C data type */ ctype = pnga_type_f2c(*type); pnga_sync(); /* if we have an error here, it is fatal */ dai_check_typeM(ctype); for (i=0; i<*ndim; i++) if (dims[i] <=0) dai_error("ndra_create: disk array dimension invalid ", dims[i]); if(strlen(filename)>DRA_MAX_FNAME) dai_error("ndra_create: filename too long", DRA_MAX_FNAME); /*** Get next free DRA handle ***/ if( (handle = dai_get_handle()) == -1) dai_error("ndra_create: too many disk arrays ", _max_disk_array); *d_a = handle - DRA_OFFSET; /*translate DRA mode into ELIO mode*/ emode = dai_elio_mode((int)*mode); /* Determine array configuration */ dai_set_config(*numfiles, *numioprocs, &DRA[handle].numfiles, &DRA[handle].ioprocs); /* determine disk array decomposition */ elem_size = dai_sizeofM(ctype); ndai_chunking( elem_size, *ndim, reqdims, dims, DRA[handle].chunk); /* determine layout -- by row or column */ DRA[handle].layout = COLUMN; /* complete initialization */ for (i=0; i<*ndim; i++) DRA[handle].dims[i] = dims[i]; DRA[handle].ndim = *ndim; DRA[handle].type = ctype; DRA[handle].mode = (int)*mode; strncpy (DRA[handle].fname, filename, DRA_MAX_FNAME); strncpy(DRA[handle].name, name, DRA_MAX_NAME ); dai_write_param(DRA[handle].fname, *d_a); /* create param file */ DRA[handle].indep = dai_file_config(filename); /*check file configuration*/ /* create file */ if(dai_io_manage(*d_a)){ if (INDEPFILES(*d_a) || DRA[handle].numfiles > 1) { sprintf(dummy_fname,"%s.%ld",DRA[handle].fname,(long)dai_io_nodeid(*d_a)); DRA[handle].fd = elio_open(dummy_fname,emode, ELIO_PRIVATE); } else{ DRA[handle].fd = elio_open(DRA[handle].fname,emode, ELIO_SHARED); } if(DRA[handle].fd==NULL)dai_error("ndra_create:failed to open file",0); if(DRA[handle].fd->fd==-1)dai_error("ndra_create:failed to open file",-1); } /* * Need to zero the last element in the array on the disk so that * we never read beyond EOF. * * For multiple component files will stamp every one of them. * */ pnga_sync(); if(dai_file_master(*d_a) && dai_write_allowed(*d_a)) ndai_zero_eof(*d_a); pnga_sync(); return(ELIO_OK); } /** * CREATE AN N-DIMENSIONAL DISK ARRAY * * @param type[in] * @param ndim[in] dimension of DRA * @param dims[][in] dimensions of DRA * @param name[in] * @param filename[in] * @param mode[in] * @param reqdims[in] dimension of typical request * @param d_a[out] DRA handle */ Integer ndrai_create(Integer *type, Integer *ndim, Integer dims[], char *name, char *filename, Integer *mode, Integer reqdims[], Integer *d_a) { Integer ret; Integer files = _dra_number_of_files; Integer procs = _dra_io_procs; ret = ndrai_create_config(type, ndim, dims, name, filename, mode, reqdims, &files, &procs, d_a); return ret; } /** * CREATE A 2-D DISK ARRAY * * @param type[in] * @param dim1[in] * @param dim2[in] * @param name[in] * @param filename[in] * @param mode[in] * @param reqdim1[in] dim1 of typical request * @param reqdim2[in] dim2 of typical request * @param d_a[out] DRA handle */ Integer drai_create(Integer *type, Integer *dim1, Integer *dim2, char *name, char *filename, Integer *mode, Integer *reqdim1, Integer *reqdim2, Integer *d_a) { Integer ndim = 2; Integer dims[2], reqdims[2]; dims[0] = *dim1; dims[1] = *dim2; reqdims[0] = *reqdim1; reqdims[1] = *reqdim2; return ndrai_create(type, &ndim, dims, name, filename, mode, reqdims, d_a); #if 0 Integer handle, elem_size, ctype; int emode; /* convert Fortran to C data type */ ctype = pnga_type_f2c(*type); pnga_sync(); /* if we have an error here, it is fatal */ dai_check_typeM(ctype); if( *dim1 <= 0 ) dai_error("dra_create: disk array dimension1 invalid ", *dim1); else if( *dim2 <= 0) dai_error("dra_create: disk array dimension2 invalid ", *dim2); if(strlen(filename)>DRA_MAX_FNAME) dai_error("dra_create: filename too long", DRA_MAX_FNAME); /* Get next free DRA handle */ if( (handle = dai_get_handle()) == -1) dai_error("dai_create: too many disk arrays ", _max_disk_array); *d_a = handle - DRA_OFFSET; /*translate DRA mode into ELIO mode*/ emode = dai_elio_mode((int)*mode); /* determine disk array decomposition */ elem_size = dai_sizeofM(ctype); dai_chunking( elem_size, *reqdim1, *reqdim2, *dim1, *dim2, &DRA[handle].chunk[0], &DRA[handle].chunk[1]); /* determine layout -- by row or column */ DRA[handle].layout = COLUMN; /* complete initialization */ DRA[handle].dims[0] = *dim1; DRA[handle].dims[1] = *dim2; DRA[handle].ndim = 2; DRA[handle].type = ctype; DRA[handle].mode = (int)*mode; strncpy (DRA[handle].fname, filename, DRA_MAX_FNAME); strncpy(DRA[handle].name, name, DRA_MAX_NAME ); DRA[handle].ioprocs = _dra_io_procs; DRA[handle].numfiles = _dra_number_of_files; dai_write_param(DRA[handle].fname, *d_a); DRA[handle].indep = dai_file_config(filename); /* create file */ if(dai_io_manage(*d_a)){ if (INDEPFILES(*d_a) || DRA[handle].numfiles > 1) { sprintf(dummy_fname,"%s.%ld",DRA[handle].fname,(long)dai_io_nodeid(*d_a)); DRA[handle].fd = elio_open(dummy_fname,emode, ELIO_PRIVATE); } else { DRA[handle].fd = elio_open(DRA[handle].fname,emode, ELIO_SHARED); } if(DRA[handle].fd==NULL)dai_error("dra_create:failed to open file",0); if(DRA[handle].fd->fd==-1)dai_error("dra_create:failed to open file",0); } pnga_sync(); if(dai_file_master(*d_a) && dai_write_allowed(*d_a)) dai_zero_eof(*d_a); pnga_sync(); return(ELIO_OK); #endif } /** * write N-dimensional aligned block of data from memory buffer to d_a * * @param ds_a[in] section of DRA written to disk * @param buf[in] pointer to io buffer * @param ld[in] array of strides * @param id */ void ndai_put(section_t ds_a, void *buf, Integer ld[], io_request_t *id) { Integer handle = ds_a.handle + DRA_OFFSET, elem, i; Integer ndim = ds_a.ndim; Off_t offset; Size_t bytes; #if WALLTIME double ss0,tt0,tt1; #endif /* find location in a file where data should be written */ ndai_file_location(ds_a, &offset); for (i=0; i DRA[handle].dims[i]) cover_hi[i] = DRA[handle].dims[i]; } } /* Find coordinates of aligned chunk (if there is one) */ j = 0; check = 1; for (i=0; i= ds_a.hi[idir]) check = 0; } else { check = 1; } /* handle cover over upper end of patch */ if (off_hi[idir] != 0 && check == 1) { for (i=0, j=0; i cover[2*i+1]) { if (ihandle+DRA_OFFSET; int retval, ndim = DRA[handle].ndim, i; /* If we are writing out to multiple files then we need to consider chunk boundaries along last dimension */ /* if(INDEPFILES(ds_chunk->handle) || DRA[handle].numfiles > 1) */ if(ds_chunk->lo[ndim-1] && DRA[handle].chunk[ndim-1]>1) ds_chunk->lo[ndim-1] -= (ds_chunk->lo[ndim-1] -1) % DRA[handle].chunk[ndim-1]; /* ds_chunk->lo is getting set in this call. list contains the the lower and upper indices of the cover section. */ retval = ndai_next(ds_chunk->lo, list, DRA[handle].chunk, ndim); /* printf("Request %d\n",req); for (i=0; ilo[i], i, list[2*i], i, list[2*i+1]); } */ if(!retval) { return(retval); } for (i=0; ihi[i] = PARIO_MIN(list[2*i+1], ds_chunk->lo[i]+DRA[handle].chunk[i]-1); } /* Again, if we are writing out to multiple files then we need to consider chunk boundaries along last dimension */ /* if(INDEPFILES(ds_chunk->handle) || DRA[handle].numfiles > 1) { */ if (1) { Integer nlo; Integer hi_temp = ds_chunk->lo[ndim-1] + DRA[handle].chunk[ndim-1] -1; hi_temp -= hi_temp % DRA[handle].chunk[ndim-1]; ds_chunk->hi[ndim-1] = PARIO_MIN(ds_chunk->hi[ndim-1], hi_temp); /*this line was absent from older version on bonnie that worked */ nlo = 2*(ndim-1); if(ds_chunk->lo[ndim-1] < list[nlo]) ds_chunk->lo[ndim-1] = list[nlo]; } /* for (i=0; ihi[i]); } */ return 1; } /** * function to complete an operation and release the buffer associated * with a buffer id */ void wait_buf(char *buf) { Integer *ga_movhdl; io_request_t *io_req; int op_code; /* int buf_id = nbuf; */ buf_info *bi; if (buf == NULL) return; bi = (buf_info*) buf; op_code = bi->op; io_req = &(bi->io_req); ga_movhdl = &(bi->ga_movhdl); /*if (buf_id >= nbuf) { printf("Wait_buf Error: No operation is associated with this buffer\n"); return; }*/ switch(op_code) { case DRA_OP_WRITE: elio_wait(io_req); break; case DRA_OP_READ: if (bi->align == 0) pnga_nbwait(ga_movhdl); else { elio_wait(io_req); dai_exec_callback(buf, WAIT); } break; default: return; } #ifdef BUF_DEBUG printf("Released a buffer\n"); #endif } /** * Write or Read Unaligned Subsections to/from disk: * always read an aligned extension of a section from disk to local buffer then * for read : copy requested data from buffer to global array; * for write: overwrite part of buffer with data from g_a and write * complete buffer to disk * * @param opcode[in] signal for read or write * @param transp[in] should data be transposed * @param ds_a[in] section of DRA that is to be read from or written to * @param gs_a[in] section of GA that is to be read from or written to * @param req[in] request number */ void ndai_transfer_unlgn( int opcode, int transp, section_t ds_a, section_t gs_a, Integer req) { Integer chunk_ld[MAXDIM], next, offset, i, j; int type = DRA[ds_a.handle+DRA_OFFSET].type; Integer ndim = DRA[ds_a.handle+DRA_OFFSET].ndim; section_t ds_chunk, ds_unlg; char *buf, *buffer; Integer *ga_movhdl; io_request_t *io_req; buf_info *bi; ds_chunk = ds_unlg = ds_a; if (dra_debug_flag && 0) { for (i=0; iio_req); ga_movhdl = &(bi->ga_movhdl); bi->align = 0; buf = (char*) (buf + sizeof(buf_info)); ndai_get(ds_chunk, buf, chunk_ld, io_req); elio_wait(io_req); /* determine location in the buffer where GA data should be */ offset = ds_unlg.lo[ndim-1]-ds_chunk.lo[ndim-1]; for (i=ndim-2; i>=0; i--) { offset = offset*chunk_ld[i]; offset += ds_unlg.lo[i] - ds_chunk.lo[i]; } buffer = (char*)buf; buffer += offset * dai_sizeofM(type); switch (opcode){ case DRA_OP_WRITE: bi->op = DRA_OP_WRITE; /* overwrite a part of buffer with data from g_a */ nga_move(LOAD, transp, gs_a, ds_a, ds_unlg, buffer, chunk_ld, ga_movhdl); pnga_nbwait(ga_movhdl); /* write ENTIRE updated buffer back to disk */ ndai_put(ds_chunk, buf, chunk_ld, io_req); break; case DRA_OP_READ: bi->op = DRA_OP_READ; /* copy requested data from buffer to g_a */ nga_move(STORE, transp, gs_a, ds_a, ds_unlg, buffer, chunk_ld, ga_movhdl); break; default: dai_error("dai_transfer_unlg: invalid opcode",(Integer)opcode); } # ifdef DEBUG fprintf(stderr,"%d transf unlg g[%d:%d,%d:%d]-d[%d:%d,%d:%d]\n", dai_io_nodeid(), gs_chunk.lo[0], gs_chunk.hi[0], gs_chunk.lo[1], gs_chunk.hi[1], ds_unlg.lo[0], ds_unlg.hi[0], ds_unlg.lo[1], ds_unlg.hi[1]); # endif } } } /* returning from this function leaving some outstanding operations, so that dra_read()/write() can be non-blocking to some extent. we will have to call dra_wait() to make sure these operations are complete. */ } /** * write or read aligned subsections to disk */ void ndai_transfer_algn( int opcode, int transp, section_t ds_a, section_t gs_a, Integer req) { Integer next, chunk_ld[MAXDIM], ndim = ds_a.ndim; Integer i; section_t ds_chunk = ds_a; char *buf, *buffer; Integer *ga_movhdl; io_request_t *io_req; buf_info *bi; for(next = 0; next < Requests[req].na; next++){ for (i=0; iio_req); ga_movhdl = &(bi->ga_movhdl); bi->align = 1; bi->callback = OFF; buffer = buf; buf = buf + sizeof(buf_info); switch (opcode){ case DRA_OP_WRITE: bi->op = DRA_OP_WRITE; /* copy data from g_a to DRA buffer */ nga_move(LOAD, transp, gs_a, ds_a, ds_chunk, buf, chunk_ld, ga_movhdl); pnga_nbwait(ga_movhdl); /* copy data from DRA buffer to disk */ ndai_put(ds_chunk, buf, chunk_ld, io_req); break; case DRA_OP_READ: bi->op = DRA_OP_READ; /* copy data from disk to DRA buffer */ ndai_get(ds_chunk, buf, chunk_ld, io_req); /* copy data from DRA buffer to g_a */ /* nga_move(STORE, transp, gs_a, ds_a, ds_chunk, buf, chunk_ld, ga_movhdl); */ dai_callback(STORE, transp, gs_a, ds_a, ds_chunk, chunk_ld, buffer, req); break; default: dai_error("dai_transfer_algn: invalid opcode",(Integer)opcode); } # ifdef DEBUG fprintf(stderr,"%d transf algn g[%d:%d,%d:%d]-d[%d:%d,%d:%d]\n", dai_io_nodeid(), gs_chunk.lo[0], gs_chunk.hi[0], gs_chunk.lo[1], gs_chunk.hi[1], ds_chunk.lo[0], ds_chunk.hi[0], ds_chunk.lo[1], ds_chunk.hi[1]); # endif } } } /* returning from this function leaving some outstanding operations, so that dra_read()/write() can be non-blocking to some extent. we will have to call dra_wait() to make sure these operations are complete. */ } /** * WRITE SECTION g_a[glo:ghi] TO d_a[dlo:dhi] * * @param transp[in] transpose operator * @param g_a[in] GA handle * @param glo[in] * @param ghi[in] * @param d_a[in] DRA handle * @param dlo[in] * @param dhi[in] * @param request[out] async. request id */ Integer FATR ndra_write_section_(logical *transp, Integer *g_a, Integer glo[], Integer ghi[], Integer *d_a, Integer dlo[], Integer dhi[], Integer *request) { Integer gdims[MAXDIM], gtype, handle=*d_a+DRA_OFFSET; Integer i, gelem, delem, ndim; section_t d_sect, g_sect; pnga_sync(); /* usual argument/type/range checking stuff */ dai_check_handleM(*d_a,"ndra_write_sect"); pnga_inquire(*g_a, >ype, &ndim, gdims); if(!dai_write_allowed(*d_a))dai_error("ndra_write_sect: write not allowed",*d_a); if(DRA[handle].type != (int)gtype)dai_error("ndra_write_sect: type mismatch",gtype); if(DRA[handle].ndim != ndim)dai_error("ndra_write_sect: dimension mismatch", ndim); for (i=0; i #endif #if HAVE_UNISTD_H # include #endif #include "drap.h" #include "ga-papi.h" #define MAX_HD_NAME_LEN 512 #define HD_NAME_EXT_LEN 10 #define HDLEN 80 #define HD_EXT ".info" /** * check file configuration: shared or independent files are used * we'll verify if every process can access DRA metafile * if yes, then we have shared file, otherwise independent files */ int dai_file_config(char* filename) { char param_filename[MAX_HD_NAME_LEN]; Integer len; char sum='+'; Integer me=pnga_nodeid(); Integer nproc = pnga_nnodes(); Integer status; stat_t info; if(nproc==1) return 0; /* build param file name */ len = strlen(filename); if(len+HD_NAME_EXT_LEN >= MAX_HD_NAME_LEN) dai_error("dai_file_config: filename too long:",len); strcpy(param_filename,filename); strcat(param_filename,HD_EXT); /* printf("checking file: %s\n",param_filename);fflush(stdout);*/ status = (Integer) elio_stat(param_filename, &info); /* processor 0 created the file => it must be able to stat it */ if(me==0 && status!= ELIO_OK) dai_error("dai_file_config: no access from 0",status); status = (status==ELIO_OK) ? 1 : 0; /* normalize status */ /* combine status accross all processors */ pnga_gop(pnga_type_f2c(MT_F_INT), &status, 1, &sum); /* 1 - only 0 can access the file => independent files * nproc - all can access it => shared file * otherwise - same processors can access it => something is wrong!!! */ if(status == 1) return(1); else if(status == nproc) return 0; #ifdef NO_SMP_NODES else dai_error("dai_file_config: confusing file configuration",status); #endif return 1; } /** * Retrive metadata for a disk array from the disk */ int dai_read_param(char* filename,Integer d_a) { FILE *fd; char param_filename[MAX_HD_NAME_LEN]; Integer len, i, ndim; Integer me=pnga_nodeid(); Integer brd_type=DRA_BRD_TYPE, orig, dra_hndl=d_a+DRA_OFFSET; long input; int rc=0; char dummy[HDLEN]; pnga_sync(); if(!me){ /* only process 0 reads metafile */ /* build param file name */ len = strlen(filename); if(len+HD_NAME_EXT_LEN >= MAX_HD_NAME_LEN) dai_error("dai_read_param: filename too long:",len); strcpy(param_filename,filename); strcat(param_filename,HD_EXT); if((fd=fopen(param_filename,"r"))){ if(!fscanf(fd,"%ld", &input)) dai_error("dai_read_param:ndim",0); DRA[dra_hndl].ndim = (Integer) input; ndim = (Integer) input; for (i=0; i= MAX_HD_NAME_LEN) dai_error("dai_write_param: filename too long:",len); strcpy(param_filename,filename); strcat(param_filename,HD_EXT); if(! (fd = fopen(param_filename,"w")) ) { char message[MAX_HD_NAME_LEN*2]; strcpy(message,"dai_write_param:open failed :: "); strcpy(message,param_filename); dai_error(message,0); } if(!fprintf(fd,"%ld ",(long)DRA[dra_hndl].ndim)) dai_error("dai_write_param:ndim",0); for (i=0; i= MAX_HD_NAME_LEN) dai_error("dai_read_param: filename too long:",len); strcpy(param_filename,filename); strcat(param_filename,HD_EXT); if(unlink(param_filename)) dai_error("dai_delete_param failed",d_a); } } ga-5.9.2/pario/dra/dra.fh000066400000000000000000000037351500715745200151010ustar00rootroot00000000000000!****************** Fortran header file for Disk Arrays ***************** #include "chemio.h" #define DRA_RW ELIO_RW #define DRA_R ELIO_R #define DRA_W ELIO_W Integer DRA_REQ_INVALID parameter (DRA_REQ_INVALID = -333) Integer DRA_CREATE Integer NDRA_CREATE Integer NDRA_CREATE_CONFIG Integer DRA_OPEN Integer DRA_INQUIRE Integer NDRA_INQUIRE Integer DRA_INIT Integer DRA_CLOSE Integer DRA_DELETE Integer DRA_WRITE Integer NDRA_WRITE Integer DRA_READ Integer NDRA_READ Integer DRA_WRITE_SECTION Integer NDRA_WRITE_SECTION Integer DRA_READ_SECTION Integer NDRA_READ_SECTION Integer DRA_PROBE Integer DRA_SET_DEBUG Integer DRA_WAIT Integer DRA_TERMINATE External DRA_CREATE External NDRA_CREATE External NDRA_CREATE_CONFIG External DRA_OPEN External DRA_INQUIRE External NDRA_INQUIRE External DRA_INIT External DRA_CLOSE External DRA_DELETE External DRA_WRITE External NDRA_WRITE External DRA_READ External NDRA_READ External DRA_WRITE_SECTION External NDRA_WRITE_SECTION External DRA_READ_SECTION External NDRA_READ_SECTION External DRA_PROBE External DRA_SET_DEBUG External DRA_WAIT External DRA_TERMINATE External DRA_FLICK ga-5.9.2/pario/dra/dra.h000066400000000000000000000052411500715745200147250ustar00rootroot00000000000000/******************* header file for Disk Arrays *****************/ #ifndef _DRA_H_ #define _DRA_H_ #include "chemio.h" #include "typesf2c.h" typedef long dra_size_t; #define DRA_RW ELIO_RW #define DRA_R ELIO_R #define DRA_W ELIO_W #define DRA_REQ_INVALID -333 #ifdef __cplusplus extern "C" { #endif /* C-interface prototypes */ extern int NDRA_Create( int type, int ndim, dra_size_t dims[], char *name, char* filename, int mode, dra_size_t reqdims[], int *d_a); extern int NDRA_Inquire( int d_a, int *type, int *ndim, dra_size_t dims[], char *name, char* filename); extern int NDRA_Write( int g_a, int d_a, int *request); extern int NDRA_Read( int g_a, int d_a, int *request); extern int NDRA_Write_section(logical transp, int g_a, int glo[], int ghi[], int d_a, dra_size_t dlo[], dra_size_t dhi[], int *request); extern int NDRA_Read_section( logical transp, int g_a, int glo[], int ghi[], int d_a, dra_size_t dlo[], dra_size_t dhi[], int *request); extern int DRA_Init( int max_arrays, double max_array_size, double total_disk_space, double max_memory); extern int DRA_Terminate(); extern int DRA_Open( char* filename, int mode, int *d_a); extern int DRA_Probe( int request, int *compl_status); extern void DRA_Set_debug( logical flag); extern void DRA_Print_internals( int d_a); extern void DRA_Set_default_config( int numfiles, int numioprocs); extern int DRA_Wait( int request); extern int DRA_Delete( int d_a); extern int DRA_Close( int d_a); extern void DRA_Flick(); #ifdef __cplusplus } #endif #endif /* _DRA_H_ */ ga-5.9.2/pario/dra/dra2arviz.c000066400000000000000000000057411500715745200160630ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #include "dra.h" #include "ga.h" #include "macommon.h" #include "macdecls.h" #include "mp3.h" #define ERROR(msg,code){printf("ERROR:%s\n",(msg)); fflush(stdout); exit(1);} #define BUFSIZE 8000000 int main(int argc, char **argv) { int heap=400000, stack=400000; int me, nproc; int max_arrays=2; double max_sz=1e8, max_disk=2e8, max_mem=1e6; int d_a, mode=DRA_R; int g_a,rows,cols; char name[1024], fname[1024]; logical transp=0; int reqid; int i,ilo,ihi,jlo,jhi,type,ndim,glo[2],ghi[2],gld[1],gdims[2]; dra_size_t dlo[2],dhi[2],ddims[2]; size_t size, nitems; void *ptr; if(argc!=6){ printf("Usage: dra2arviz \n"); printf(" dra_filename is the meta-file name for disk resident array\n"); printf(" [ilo:ihi, jlo:jhi] array section to read\n\n\n"); return(1); } MP_INIT(argc,argv); MP_MYID(&me); MP_PROCS(&nproc); heap /= nproc; stack /= nproc; if(! MA_init((Integer)MT_F_DBL, stack, heap)) GA_Error("MA_init failed",stack+heap); /* initialize memory allocator*/ GA_Initialize(); /* initialize GA */ if(nproc != 1)ERROR("Error: does not run in parallel",nproc); if(DRA_Init(max_arrays, max_sz, max_disk, max_mem)) ERROR("initialization failed",0); if(DRA_Open(argv[1], mode, &d_a)) ERROR("dra_open failed",0); ilo = atoi(argv[2]); ihi = atoi(argv[3]); jlo = atoi(argv[4]); jhi = atoi(argv[5]); dlo[0] = ilo; dlo[1] = jlo; dhi[0] = ihi; dhi[1] = jhi; rows = ihi - ilo +1; cols = jhi - jlo +1; glo[0] = 0; glo[1] = 0; ghi[0] = rows-1; ghi[1] = cols-1; gdims[0] = rows; gdims[1] = cols; if(NDRA_Inquire(d_a, &type, &ndim, ddims, name, fname)) ERROR("dra_inquire failed",0); switch (type) { case MT_F_INT: size = sizeof(Integer); break; case MT_F_DBL: size = sizeof(DoublePrecision); break; case MT_F_DCPL: size = sizeof(DoubleComplex); break; default: ERROR("type not supported",type); } g_a = NGA_Create(type, 2, gdims, "temp", NULL); if(NDRA_Read_section(transp, g_a, glo, ghi, d_a, dlo, dhi, &reqid)) ERROR("dra_read_section failed",0); if(DRA_Wait(reqid)) ERROR("dra_wait failed",0); NGA_Access(g_a, glo, ghi, &ptr, gld); if(gld[0] != rows) ERROR("ld != rows",gld[0]); fwrite("OK\n",1,3,stdout); nitems = (size_t)rows; /* write data by columns */ for(i=0; i #endif #include "gacommon.h" #include "draf2c.h" #include "elio.h" #include "macdecls.h" #define MAXDIM GA_MAX_DIM #ifdef FALSE #undef FALSE #endif #ifdef TRUE #undef TRUE #endif #define FALSE (logical) 0 #define TRUE (logical) 1 /************************* common constants **********************************/ #define DRA_OFFSET 5000 /**< DRA handle offset */ #define DRA_BRD_TYPE 30000 /**< msg type for DRA broadcast */ #define DRA_GOP_TYPE 30001 /**< msg type for DRA sum */ #define DRA_MAX_NAME 72 /**< max length of array name */ #define DRA_MAX_FNAME 248 /**< max length of metafile name */ /************************* common data structures **************************/ typedef struct { /**< stores basic DRA info */ Integer ndim; /**< dimension of array */ Integer dims[MAXDIM]; /**< array dimensions */ Integer chunk[MAXDIM]; /**< data layout chunking */ Integer layout; /**< date layout type */ int type; /**< data type */ int mode; /**< file/array access permissions */ char name[DRA_MAX_NAME+8]; /**< array name */ char fname[DRA_MAX_FNAME+8]; /**< metafile name */ Integer actv; /**< is array active ? */ Integer indep; /**< shared/independent files ? */ Fd_t fd; /**< ELIO meta-file descriptor */ Integer numfiles; /**< # files on open file system */ Integer ioprocs; /**< number of IO procs per node */ } disk_array_t; #define MAX_ALGN 1 /**< max # aligned subsections */ #define MAX_UNLG (2*(MAXDIM-1)) /**< max # unaligned subsections */ typedef struct { /**< object describing DRA/GA section */ Integer handle; Integer ndim; Integer lo[MAXDIM]; Integer hi[MAXDIM]; } section_t; typedef struct { /**< structure stores arguments for callback f */ int op; int transp; Integer ld[MAXDIM]; section_t gs_a; section_t ds_a; section_t ds_chunk; } args_t; typedef struct { /**< stores info associated with DRA request */ Integer d_a; /**< disk array handle */ int num_pending; /**< number of pending asynch. I/O ops */ Integer list_algn[MAX_ALGN][2*MAXDIM]; /**< coordinates of aligned subsection */ Integer list_unlgn[MAX_UNLG][2*MAXDIM]; /**< coordinates of unaligned subsections*/ Integer list_cover[MAX_UNLG][2*MAXDIM]; /**< coordinates of "cover" subsections */ int nu; int na; int call_id; /**< id of this request */ } request_t; typedef struct { char *buf; int op; io_request_t io_req; Integer ga_movhdl; args_t args; int align; int callback; } buf_info; extern disk_array_t *DRA; extern logical dra_debug_flag; /**************************** common macros ********************************/ #define PARIO_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define PARIO_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define dai_error pnga_error extern int dai_read_param(char* filename, Integer d_a); extern void dai_write_param(char* filename, Integer d_a); extern void dai_delete_param(char* filename, Integer d_a); extern int dai_file_config(char* filename); extern logical dai_section_intersect(section_t sref, section_t* sadj); extern int drai_get_num_serv(void); /* internal fortran calls */ extern Integer drai_create(Integer *type, Integer *dim1, Integer *dim2, char *name, char *filename, Integer *mode, Integer *reqdim1, Integer *reqdim2, Integer *d_a); extern Integer ndrai_create(Integer *type, Integer *ndim, Integer dims[], char *name, char *filename, Integer *mode, Integer reqdims[], Integer *d_a); extern Integer drai_open(char *filename, Integer *mode, Integer *d_a); extern Integer drai_inquire(Integer *d_a, Integer *type, Integer *dim1, Integer *dim2, char *name, char *filename); extern Integer ndrai_inquire(Integer *d_a, Integer *type, Integer *ndim, Integer dims[],char *name,char *filename); extern Integer ndrai_create_config(Integer *type, Integer *ndim, Integer dims[], char *name, char *filename, Integer *mode, Integer reqdims[], Integer *numfiles, Integer *numioprocs, Integer *d_a); /* external fortran calls */ #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS extern Integer FATR dra_create_(Integer *type, Integer *dim1, Integer *dim2, char *name, char *filename, Integer *mode, Integer *reqdim1, Integer *reqdim2, Integer *d_a, size_t nlen, size_t flen); extern Integer FATR ndra_create_(Integer *type, Integer *ndim, Integer dims[], char *name, char *filename, Integer *mode, Integer reqdims[], Integer *d_a, size_t nlen, size_t flen); extern Integer FATR dra_open_(char *filename, Integer *mode, Integer *d_a, size_t flen); extern Integer FATR dra_inquire_(Integer *d_a, Integer *type, Integer *dim1, Integer *dim2, char *name, char *filename, size_t nlen, size_t flen); extern Integer FATR ndra_inquire_(Integer *d_a, Integer *type, Integer *ndim, Integer dims[], char *name, char *filename, size_t nlen, size_t flen); extern Integer ndra_create_config_(Integer *type, Integer *ndim, Integer dims[], char *name, char *filename, Integer *mode, Integer reqdims[], Integer *numfiles, Integer *numioprocs, Integer *d_a, size_t nlen, size_t flen); #else /* F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS */ extern Integer FATR dra_create_(Integer *type, Integer *dim1, Integer *dim2, char *name, size_t nlen, char *filename, size_t flen, Integer *mode, Integer *reqdim1, Integer *reqdim2, Integer *d_a); extern Integer FATR ndra_create_(Integer *type, Integer *ndim, Integer dims[], char *name, size_t nlen, char *filename, size_t flen, Integer *mode, Integer reqdims[], Integer *d_a); extern Integer FATR dra_open_(char *filename, size_t flen, Integer *mode, Integer *d_a); extern Integer FATR dra_inquire_(Integer *d_a, Integer *type, Integer *dim1, Integer *dim2, char *name, size_t nlen, char *filename, size_t flen); extern Integer FATR ndra_inquire_(Integer *d_a, Integer *type, Integer *ndim, Integer dims[], char *name, size_t nlen, char *filename, size_t flen); extern Integer ndra_create_config_(Integer *type, Integer *ndim, Integer dims[], char *name, size_t nlen, char *filename, size_t flen, Integer *mode, Integer reqdims[], Integer *numfiles, Integer *numioprocs, Integer *d_a); #endif /* F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS */ extern Integer FATR dra_terminate_(); extern Integer FATR dra_init_( Integer *max_arrays, /* input */ DoublePrecision *max_array_size, /* input */ DoublePrecision *tot_disk_space, /* input */ DoublePrecision *max_memory); /* input */ extern Integer FATR dra_probe_( Integer *request, /*input*/ Integer *status); /*output*/ extern Integer FATR ndra_write_( Integer *g_a, /*input:GA handle*/ Integer *d_a, /*input:DRA handle*/ Integer *request); /*output: handle to async oper.*/ extern Integer FATR ndra_write_section_( logical *transp, /*input:transpose operator*/ Integer *g_a, /*input:GA handle*/ Integer glo[], /*input*/ Integer ghi[], /*input*/ Integer *d_a, /*input:DRA handle*/ Integer dlo[], /*input*/ Integer dhi[], /*input*/ Integer *request); /*output: async. request id*/ extern Integer FATR ndra_read_(Integer* g_a, Integer* d_a, Integer* request); extern Integer FATR ndra_read_section_( logical *transp, /*input:transpose operator*/ Integer *g_a, /*input:GA handle*/ Integer glo[], /*input*/ Integer ghi[], /*input*/ Integer *d_a, /*input:DRA handle*/ Integer dlo[], /*input*/ Integer dhi[], /*input*/ Integer *request); /*output: request id*/ extern void FATR dra_print_internals_(Integer *d_a); extern void FATR dra_set_debug_(logical *flag); extern void FATR dra_set_default_config_(Integer *numfiles, Integer *numioprocs); extern Integer FATR dra_delete_(Integer* d_a); extern Integer FATR dra_close_(Integer* d_a); extern void dra_flick_(); extern Integer FATR dra_wait_(Integer* request); ga-5.9.2/pario/dra/env.c000066400000000000000000000011261500715745200147400ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** @file * $Id: env.c,v 1.1 1997-12-07 11:14:18 d3e129 Exp $ */ #if HAVE_STDLIB_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_STRING_H # include #endif #define MAX_NUM_SERV 64 /** * get number of I/O servers from optional environmental variable DRA_NUM_SERV */ int drai_get_num_serv() { int val=-1; char *str; str = getenv("DRA_NUM_SERV"); if(str==NULL)val = 0; else{ val = atoi(str); if(val<1 || val >MAX_NUM_SERV)val =0; } return val; } ga-5.9.2/pario/dra/ffflush.F000066400000000000000000000003361500715745200155520ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c $Id: ffflush.F,v 1.2 2002-10-07 16:51:51 d3h325 Exp $ subroutine ffflush(unit) integer unit c #if HAVE_F77_FLUSH call F77_FLUSH(unit) #endif c end ga-5.9.2/pario/dra/fortran.c000066400000000000000000000110411500715745200156200ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /*************************** fortran DRA interface **************************/ #include "dra.h" #include "drap.h" #include "draf2c.h" #include "farg.h" #include "typesf2c.h" #include "ga-papi.h" #include "global.h" static char cname[DRA_MAX_NAME+1], cfilename[DRA_MAX_FNAME+1]; Integer FATR dra_create_( #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *type, Integer *dim1, Integer *dim2, char *name, char *filename, Integer *mode, Integer *reqdim1, Integer *reqdim2, Integer *d_a, size_t nlen, size_t flen #else Integer *type, Integer *dim1, Integer *dim2, char *name, size_t nlen, char *filename, size_t flen, Integer *mode, Integer *reqdim1, Integer *reqdim2, Integer *d_a #endif ) { ga_f2cstring(name, nlen, cname, DRA_MAX_NAME); ga_f2cstring(filename, flen, cfilename, DRA_MAX_FNAME); return drai_create(type, dim1, dim2, cname, cfilename, mode, reqdim1, reqdim2,d_a); } Integer FATR ndra_create_( #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *type, Integer *ndim, Integer dims[], char *name, char *filename, Integer *mode, Integer reqdims[], Integer *d_a, size_t nlen, size_t flen #else Integer *type, Integer *ndim, Integer dims[], char *name, size_t nlen, char *filename, size_t flen, Integer *mode, Integer reqdims[], Integer *d_a #endif ) { ga_f2cstring(name, nlen, cname, DRA_MAX_NAME); ga_f2cstring(filename, flen, cfilename, DRA_MAX_FNAME); return ndrai_create(type, ndim, dims, cname, cfilename, mode, reqdims, d_a); } Integer FATR dra_open_( #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *filename, Integer *mode, Integer *d_a, size_t flen #else char *filename, size_t flen, Integer *mode, Integer *d_a #endif ) { ga_f2cstring(filename, flen, cfilename, DRA_MAX_FNAME); return drai_open(cfilename, mode, d_a); } Integer FATR dra_inquire_( #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *d_a, Integer *type, Integer *dim1, Integer *dim2, char *name, char *filename, size_t nlen, size_t flen #else Integer *d_a, Integer *type, Integer *dim1, Integer *dim2, char *name, size_t nlen, char *filename, size_t flen #endif ) { Integer stat = drai_inquire(d_a, type, dim1, dim2, cname, cfilename); *type = pnga_type_c2f(*type); ga_c2fstring(cname, name, nlen); ga_c2fstring(cfilename, filename, flen); return stat; } Integer FATR ndra_inquire_( #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *d_a, Integer *type, Integer *ndim, Integer dims[], char *name, char *filename, size_t nlen, size_t flen #else Integer *d_a, Integer *type, Integer *ndim, Integer dims[], char *name, size_t nlen, char *filename, size_t flen #endif ) { Integer stat = ndrai_inquire(d_a, type, ndim, dims, cname, cfilename); *type = pnga_type_c2f(*type); ga_c2fstring(cname, name, nlen); ga_c2fstring(cfilename, filename, flen); return stat; } Integer ndra_create_config_( #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS Integer *type, Integer *ndim, Integer dims[], char *name, char *filename, Integer *mode, Integer reqdims[], Integer *numfiles, Integer *numioprocs, Integer *d_a, size_t nlen, size_t flen #else Integer *type, Integer *ndim, Integer dims[], char *name, size_t nlen, char *filename, size_t flen, Integer *mode, Integer reqdims[], Integer *numfiles, Integer *numioprocs, Integer *d_a #endif ) { ga_f2cstring(name, nlen, cname, DRA_MAX_NAME); ga_f2cstring(filename, flen, cfilename, DRA_MAX_FNAME); return ndrai_create_config(type, ndim, dims, cname, cfilename, mode, reqdims, numfiles, numioprocs, d_a); } ga-5.9.2/pario/dra/global.unsup.c000066400000000000000000000047741500715745200165750ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** @file * This file contains some data parallel GA operations that are not part of * the official GA distribution. * * Date: 05.10.95 * Author: Jarek Nieplocha */ #if HAVE_STDIO_H # include #endif #include "global.h" #include "macommon.h" #define ga_idot_ F77_FUNC_(ga_idot,GA_IDOT) #define DRA_TYPE_GSM 32760 - 6 /** * Integer version of ga_ddot */ Integer ga_idot_(Integer *g_a, Integer *g_b) { Integer atype, adim1, adim2, btype, bdim1, bdim2, ald, bld; Integer ailo,aihi, ajlo, ajhi, bilo, bihi, bjlo, bjhi; register Integer i,j; Integer me, sum; Integer index_a, index_b; pnga_sync(); me = ga_nodeid_(); ga_check_handle(g_a, "ga_idot"); ga_check_handle(g_b, "ga_idot"); ga_inquire_(g_a, &atype, &adim1, &adim2); ga_inquire_(g_b, &btype, &bdim1, &bdim2); if(atype != btype || atype != MT_F_INT) ga_error("ga_idot: types not correct", 0L); if (adim1!=bdim1 || adim2 != bdim2) ga_error("ga_idot: arrays not conformant", 0L); if (DBL_MB == (DoublePrecision*)0 || INT_MB == (Integer*)0) ga_error("ga_idot: null pointer for base array",0L); ga_distribution_(g_a, &me, &ailo, &aihi, &ajlo, &ajhi); ga_distribution_(g_b, &me, &bilo, &bihi, &bjlo, &bjhi); if (ailo!=bilo || aihi != bihi || ajlo!=bjlo || ajhi != bjhi){ /* fprintf(stderr,"\nme =%d: %d-%d %d-%d vs %d-%d %d-%d dim:%dx%d\n",me, ailo,aihi, ajlo, ajhi, bilo, bihi, bjlo, bjhi,adim1,adim2); */ ga_error("ga_idot: distributions not identical",0L); } sum = 0.; if ( aihi>0 && ajhi>0 ){ ga_access_(g_a, &ailo, &aihi, &ajlo, &ajhi, &index_a, &ald); if(g_a == g_b){ index_b = index_a; bld =ald; }else ga_access_(g_b, &bilo, &bihi, &bjlo, &bjhi, &index_b, &bld); index_a --; /* Fortran to C correction of starting address */ index_b --; /* Fortran to C correction of starting address */ /* compute "local" contribution to the dot product */ for(j=0; j #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #define BASE_NAME "dra.file" #ifdef HPIODIR # define FNAME HPIODIR/*BASE_NAME*/ #else # define FNAME BASE_NAME #endif #define NDIM 3 #define SIZE 20 #define NSIZE 8000 #define LSIZE 216000 #define TRUE (logical)1 #define FALSE (logical)0 #define IA 16807 #define IM 2147483647 #define AM (1.0/IM) #define IQ 127773 #define IR 2836 #define MASK 123459876 #include "dra.h" #include "ga.h" #include "macdecls.h" #include "mp3.h" #define MAXDIM GA_MAX_DIM #define COPY_INT_DRAT(src,dst) {\ int i; \ for (i=0; i dhi[i]) swap(&dlo[i],&dhi[i]); elem = dhi[i] - dlo[i] + 1; glo[i] = iran(n-elem,&idum); ghi[i] = glo[i] + elem - 1; } if (me == 0) { printf(" read global["); printf("%4d:%4d",glo[0],ghi[0]); for (i=1; i ghi[i]) swap(&glo[i],&ghi[i]); elem = ghi[i] - glo[i] +1; dlo[i] = iran(m-elem,&idum); dhi[i] = dlo[i]+elem-1; } if (me == 0) { printf(" writing global["); printf("%4d:%4d",glo[0],ghi[0]); for (i=1; i= (b)) ? (a) : (b)) #define PARIO_MIN(a,b) (((a) <= (b)) ? (a) : (b)) /** * check if two patches are conforming (dimensions are divisible) */ logical dai_patches_conforming( Integer* ailo, Integer* aihi, Integer* ajlo, Integer* ajhi, Integer* bilo, Integer* bihi, Integer* bjlo, Integer* bjhi) { Integer mismatch; Integer adim1, bdim1, adim2, bdim2; adim1 = *aihi - *ailo +1; adim2 = *ajhi - *ajlo +1; bdim1 = *bihi - *bilo +1; bdim2 = *bjhi - *bjlo +1; mismatch = (adim1ndim) isconsistent = FALSE; /* check consistency of patch coordinates */ if (isconsistent) { for (i=0; ihi[i] < sadj->lo[i]) isconsistent = FALSE; } } /* check to see if there is an intersection */ if (isconsistent) { for (i=0; ilo[i]) isconsistent = FALSE; if (sadj->hi[i] < sref.lo[i]) isconsistent = FALSE; } } /* if there is an intersection then return it in sadj */ if (isconsistent) { for (i=0; ilo[i] = PARIO_MAX(sref.lo[i],sadj->lo[i]); sadj->hi[i] = PARIO_MIN(sref.hi[i],sadj->hi[i]); } } return (isconsistent); } ga-5.9.2/pario/dra/perf.F000066400000000000000000000206741500715745200150600ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c c FNAME - filename for test program c c Only use the INTEGER4_TEST if you are compiling c with USE_INTEGER4 and you are using an F90 c fortran compiler c c#define INTEGER4_TEST c #define BASE_NAME 'da.try' #define BASE_NAME1 'da1.try' #ifdef HPIODIR # define FNAME HPIODIR//BASE_NAME # define FNAME1 HPIODIR//BASE_NAME1 #else # define FNAME BASE_NAME # define FNAME1 BASE_NAME1 #endif program io #include "mafdecls.fh" #include "global.fh" #include "dra.fh" integer status, me integer max_arrays integer stack, heap double precision max_sz, max_disk, max_mem data max_arrays, max_sz, max_disk, max_mem /10,1d8,1d10, 1d6/ data stack, heap /1200000, 800000/ c #include "mp3.fh" call ga_initialize() if(.not. ga_uses_ma())then stack = 10000 heap = 10000 endif c if(ma_init(MT_F_DBL, stack, heap) ) then me = ga_nodeid() if(dra_init(max_arrays, max_sz, max_disk, max_mem).ne.0)then call ga_error('dra_init failed: ',0) endif if (me.eq.0) print *,' ' if(me.eq.0)print *, 'TESTING PERFORMANCE OF DISK ARRAYS' if (me.eq.0) print *,' ' call test_io_dbl() status = dra_terminate() call ga_terminate() else print *,'ma_init failed' endif if(me.eq.0)print *, 'all done ...' call MP_FINALIZE() end subroutine test_io_dbl implicit none #include "mafdecls.fh" #include "global.fh" #include "dra.fh" #include "mp3def.fh" integer n,m parameter (n=5000) parameter (m = 2*n) double precision err, tt0, tt1, mbytes integer g_a, g_b, d_a, d_b double precision drand integer i,j, req, loop integer dilo,dihi,djlo,djhi integer gilo,gihi,gjlo,gjhi integer ielem, jelem integer me, nproc integer index, ld integer iran logical status integer util_mdtob external drand external util_mdtob intrinsic int, dble #ifdef INTEGER4_TEST double precision, allocatable :: buffer(:,:) #endif iran(i) = int(drand(0)*dble(i-1)) + 1 c loop = 30 req = -1 nproc = ga_nnodes() me = ga_nodeid() c if (me.eq.0) print *, 'creating global arrays ',n,' x',n if (me.eq.0)call ffflush(6) call ga_sync() if(.not. ga_create(MT_DBL, n, n, 'a', 1, 1, g_a)) & call ga_error('ga_create failed: a', 0) if(.not. ga_create(MT_DBL, n, n, 'b', 1, 1, g_b)) & call ga_error('ga_create failed: b', 0) if (me.eq.0) print *,'done ' if (me.eq.0)call ffflush(6) c c initialize g_a, g_b with random values c ... use ga_access to avoid allocating local buffers for ga_put c call ga_sync() call ga_distribution(g_a, me, gilo,gihi,gjlo,gjhi) #ifdef INTEGER4_TEST allocate(buffer(gihi-gilo+1,gjhi-gjlo+1)) ld = gihi-gilo+1 call ga_get(g_a, gilo, gihi, gjlo, gjhi, buffer, ld) call fill_random(buffer, gihi-gilo+1, gjhi-gjlo+1, ld) call ga_put(g_a, gilo, gihi, gjlo, gjhi, buffer, ld) deallocate(buffer) #else call ga_access(g_a, gilo,gihi,gjlo,gjhi, index, ld) call fill_random(DBL_MB(index), gihi-gilo+1, gjhi-gjlo+1, ld) #endif call ga_sync() * if (me.eq.0) print *,'done ' * if (me.eq.0)call ffflush(6) c call ga_zero(g_b) c c c....................................................................... if (me.eq.0) print *, 'creating disk array ',n,' x',n if (me.eq.0)call ffflush(6) if(dra_create(MT_DBL, n, n, 'A', FNAME, & DRA_RW, n, n, d_a).ne.0) $ CALL ga_error('dra_create failed: ',0) c if(me.eq.0) print *, 'alligned blocking write' if (me.eq.0)call ffflush(6) tt0 = MP_TIMER() if(dra_write(g_a, d_a, req).ne.0) $ CALL ga_error('dra_write failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 mbytes = 1e-6*util_mdtob(n*n) if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif c if(dra_close(d_a).ne.0) & call ga_error('dra_close(d_a) failed: ',d_a) tt1 = MP_TIMER() -tt0 if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif c if (me.eq.0) print *,' ' if (me.eq.0) print *,'disk array closed ' if (me.eq.0)call ffflush(6) c....................................................................... c c if (me.eq.0) print *, 'creating disk array ',m,' x',m if (me.eq.0)call ffflush(6) if(dra_create(MT_DBL, m, m, 'B', FNAME1, & DRA_RW, n, n, d_b).ne.0) $ CALL ga_error('dra_create failed: ',0) c if(me.eq.0) print *, 'non alligned blocking write' if (me.eq.0)call ffflush(6) c gilo =1 gjlo =1 gihi =n gjhi =n tt0 = MP_TIMER() if(dra_write_section(.false., g_a, gilo, gihi, gjlo, gjhi, & d_b, gilo+1, gihi+1, gjlo+1, gjhi+1, req).ne.0) & call ga_error('dra_write_section failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 mbytes = 1e-6*util_mdtob(n*n) if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif c if(dra_close(d_b).ne.0) & call ga_error('dra_close(d_b) failed: ',d_b) tt1 = MP_TIMER() -tt0 if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif c if (me.eq.0) print *,' ' if (me.eq.0) print *,'disk array closed ' if (me.eq.0)call ffflush(6) c....................................................................... c c if (me.eq.0) print *,' ' if (me.eq.0) print *,'opening disk array' if(dra_open(FNAME, DRA_R, d_a).ne.0) & call ga_error('dra_open failed',0) if(me.eq.0) print *, 'alligned blocking read' if (me.eq.0)call ffflush(6) tt0 = MP_TIMER() if(dra_read(g_b, d_a, req).ne.0) $ CALL ga_error('dra_read failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif call ga_dadd(1d0, g_a, -1d0, g_b, g_b) err = ga_ddot(g_b, g_b) if(err.ne.0) then if (me.eq.0) print *,'BTW, we have error =', err, & ' for dra_read' c call ga_print(g_b) else if (me.eq.0) print *,'OK' endif if(dra_delete(d_a).ne.0) & call ga_error('dra_delete failed',0) c....................................................................... c if (me.eq.0) print *,' ' if (me.eq.0) print *,'opening disk array' if(dra_open(FNAME1,DRA_R, d_b).ne.0) & call ga_error('dra_open failed',0) if(me.eq.0) print *, 'non alligned blocking read' if (me.eq.0)call ffflush(6) gilo =1 gjlo =1 gihi =n gjhi =n tt0 = MP_TIMER() if(dra_read_section(.false., g_b, gilo, gihi, gjlo, gjhi, & d_b, gilo+1, gihi+1, gjlo+1, gjhi+1, req).ne.0) & call ga_error('dra_read_section failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif call ga_dadd(1d0, g_a, -1d0, g_b, g_b) err = ga_ddot(g_b, g_b) if(err.ne.0) then if (me.eq.0) print *,'BTW, we have error =', err, + ' for dra_read_section' else if (me.eq.0) print *,'OK' endif if(dra_delete(d_b).ne.0) & call ga_error('dra_delete failed',0) c....................................................................... status = ga_destroy(g_a) status = ga_destroy(g_b) 100 format(g11.2,' MB time=',g11.2,' rate=',g11.3,'MB/s') end subroutine swap(a,b) integer a, b, temp temp = a a = b b = temp end subroutine init_char(str, len, char) character*(*) str character*1 char integer i do i = 1, len -1 str(i:i+1) = char enddo end subroutine fill_random(a, n,m, ld) integer ld, n,m double precision a(ld,*), drand, seed integer i,j external drand c do j=1,m seed = drand(j) do i=1,n a(i,j)=seed*i enddo enddo end ga-5.9.2/pario/dra/perf2.F000066400000000000000000000176311500715745200151410ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c c FNAME - filename for test program c #define BASE_NAME 'da.try' #define BASE_NAME1 'da1.try' #ifdef HPIODIR # define FNAME HPIODIR//BASE_NAME # define FNAME1 HPIODIR//BASE_NAME1 #else # define FNAME BASE_NAME # define FNAME1 BASE_NAME1 #endif program io #include "mafdecls.fh" #include "global.fh" #include "dra.fh" integer status, me integer max_arrays integer stack, heap double precision max_sz, max_disk, max_mem data max_arrays, max_sz, max_disk, max_mem /10,1d8,1d10, 1d6/ data stack, heap /1200000, 800000/ c #include "mp3.fh" call ga_initialize() if(.not. ga_uses_ma())then stack = 10000 heap = 10000 endif c if(ma_init(MT_F_DBL, stack, heap) ) then me = ga_nodeid() if(dra_init(max_arrays, max_sz, max_disk, max_mem).ne.0)then call ga_error('dra_init failed: ',0) endif if (me.eq.0) print *,' ' if(me.eq.0)print *, 'TESTING PERFORMANCE OF DISK ARRAYS' if (me.eq.0) print *,' ' call test_io_dbl() status = dra_terminate() call ga_terminate() else print *,'ma_init failed' endif if(me.eq.0)print *, 'all done ...' call MP_FINALIZE() end subroutine test_io_dbl implicit none #include "mafdecls.fh" #include "global.fh" #include "dra.fh" #include "mp3def.fh" integer n,m parameter (n=5000) parameter (m = 2*n) double precision err, tt0, tt1, mbytes integer g_a, g_b, d_a, d_b double precision drand integer i,j, req, loop integer dlo(2),dhi(2) integer glo(2),ghi(2) integer me, nproc integer index, dims(2), reqdims(2), ld(2) integer iran integer util_mdtob logical status external drand external util_mdtob intrinsic int, dble iran(i) = int(drand(0)*dble(i-1)) + 1 c loop = 30 req = -1 nproc = ga_nnodes() me = ga_nodeid() c if (me.eq.0) print *, 'creating global arrays ',n,' x',n if (me.eq.0)call ffflush(6) call ga_sync() if(.not. ga_create(MT_DBL, n, n, 'a', 1, 1, g_a)) & call ga_error('ga_create failed: a', 0) if(.not. ga_create(MT_DBL, n, n, 'b', 1, 1, g_b)) & call ga_error('ga_create failed: b', 0) if (me.eq.0) print *,'done ' if (me.eq.0)call ffflush(6) c c initialize g_a, g_b with random values c ... use ga_access to avoid allocating local buffers for ga_put c call ga_sync() call nga_distribution(g_a, me, glo, ghi) call nga_access(g_a, glo, ghi, index, ld) call fill_random(DBL_MB(index), ghi(1)-glo(1)+1, & ghi(2)-glo(2)+1, ld(1)) call ga_sync() * if (me.eq.0) print *,'done ' * if (me.eq.0)call ffflush(6) c call ga_zero(g_b) c c c....................................................................... if (me.eq.0) print *, 'creating disk array ',n,' x',n if (me.eq.0)call ffflush(6) dims(1) = n dims(2) = n reqdims(1) = n reqdims(2) = n if(ndra_create(MT_DBL, 2, dims, 'A', & FNAME, & DRA_RW, reqdims, d_a).ne.0) $ CALL ga_error('ndra_create failed: ',0) c if(me.eq.0) print *, 'alligned blocking write' if (me.eq.0)call ffflush(6) tt0 = MP_TIMER() if(ndra_write(g_a, d_a, req).ne.0) $ CALL ga_error('ndra_write failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 mbytes = 1e-6*util_mdtob(n*n) if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif c if(dra_close(d_a).ne.0)call ga_error('dra_close failed: ',d_a) tt1 = MP_TIMER() -tt0 if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif c if (me.eq.0) print *,' ' if (me.eq.0) print *,'disk array closed ' if (me.eq.0)call ffflush(6) c....................................................................... c c if (me.eq.0) print *, 'creating disk array ',m,' x',m if (me.eq.0)call ffflush(6) dims(1) = m dims(2) = m reqdims(1) = n reqdims(2) = n if(ndra_create(MT_DBL, 2, dims, 'B', & FNAME1, & DRA_RW, reqdims, d_b).ne.0) $ CALL ga_error('ndra_create failed: ',0) c if(me.eq.0) print *, 'non alligned blocking write' if (me.eq.0)call ffflush(6) c glo(1) = 1 glo(2) = 1 ghi(1) = n ghi(2) = n dlo(1) = 2 dlo(2) = 2 dhi(1) = n+1 dhi(2) = n+1 tt0 = MP_TIMER() if(ndra_write_section(.false., g_a, glo, ghi, & d_b, dlo, dhi, req).ne.0) & call ga_error('ndra_write_section failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 mbytes = 1e-6*util_mdtob(n*n) if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif c if(dra_close(d_b).ne.0)call ga_error('dra_close failed: ',d_b) tt1 = MP_TIMER() -tt0 if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif c if (me.eq.0) print *,' ' if (me.eq.0) print *,'disk array closed ' if (me.eq.0)call ffflush(6) c....................................................................... c c if (me.eq.0) print *,' ' if (me.eq.0) print *,'opening disk array' if(dra_open(FNAME,DRA_R, d_a).ne.0) & call ga_error('dra_open failed',0) if(me.eq.0) print *, 'alligned blocking read' if (me.eq.0)call ffflush(6) tt0 = MP_TIMER() if(ndra_read(g_b, d_a, req).ne.0) $ CALL ga_error('ndra_read failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif call ga_dadd(1d0, g_a, -1d0, g_b, g_b) err = ga_ddot(g_b, g_b) if(err.ne.0) then if (me.eq.0) print *,'BTW, we have error =', err else if (me.eq.0) print *,'OK' endif if(dra_delete(d_a).ne.0) & call ga_error('dra_delete failed',0) c....................................................................... c if (me.eq.0) print *,' ' if (me.eq.0) print *,'opening disk array' if(dra_open(FNAME1,DRA_R, d_b).ne.0) & call ga_error('dra_open failed',0) if(me.eq.0) print *, 'non alligned blocking read' if (me.eq.0)call ffflush(6) tt0 = MP_TIMER() if(ndra_read_section(.false., g_b, glo, ghi, & d_b, dlo, dhi, req).ne.0) & call ga_error('ndra_read_section failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 if (me.eq.0)then write(6,100)mbytes,tt1,mbytes/tt1 endif call ga_dadd(1d0, g_a, -1d0, g_b, g_b) err = ga_ddot(g_b, g_b) if(err.ne.0) then if (me.eq.0) print *,'BTW, we have error =', err else if (me.eq.0) print *,'OK' endif if(dra_delete(d_b).ne.0) & call ga_error('dra_delete failed',0) c....................................................................... status = ga_destroy(g_a) status = ga_destroy(g_b) 100 format(g11.2,' MB time=',g11.2,' rate=',g11.3,'MB/s') end subroutine swap(a,b) integer a, b, temp temp = a a = b b = temp end subroutine init_char(str, len, char) character*(*) str character*1 char integer i do i = 1, len -1 str(i:i+1) = char enddo end subroutine fill_random(a, n,m, ld) integer ld, n,m double precision a(ld,*), drand, seed integer i,j external drand c do j=1,m seed = drand(j) do i=1,n a(i,j)=seed*i enddo enddo end ga-5.9.2/pario/dra/perf3.F000066400000000000000000000224741500715745200151430ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c c FNAME - filename for test program c #define BASE_NAME '/scratch/da.try' #define BASE_NAME1 '/scratch/da1.try' #ifdef HPIODIR # define FNAME HPIODIR//BASE_NAME # define FNAME1 HPIODIR//BASE_NAME1 #else # define FNAME BASE_NAME # define FNAME1 BASE_NAME1 #endif c#define MULTFILES 1 # define USEMULTFILES 1 program io #include "mafdecls.fh" #include "global.fh" #include "dra.fh" integer status, me integer max_arrays integer stack, heap double precision max_sz, max_disk, max_mem data max_arrays, max_sz, max_disk, max_mem /10,1d8,1d10, 1d6/ data stack, heap /1200000, 800000/ c #include "mp3.fh" call ga_initialize() if(.not. ga_uses_ma())then stack = 100000 heap = 100000 endif c if(ma_init(MT_F_DBL, stack, heap) ) then me = ga_nodeid() if(dra_init(max_arrays, max_sz, max_disk, max_mem).ne.0)then call ga_error('dra_init failed: ',0) endif if (me.eq.0) print *,' ' if(me.eq.0)print *, 'TESTING PERFORMANCE OF DISK ARRAYS' if (me.eq.0) print *,' ' call test_io_dbl() status = dra_terminate() call ga_terminate() else print *,'ma_init failed' endif if(me.eq.0)print *, 'all done ...' call MP_FINALIZE() end subroutine test_io_dbl implicit none #include "mafdecls.fh" #include "global.fh" #include "dra.fh" #include "mp3def.fh" integer n,m,ndim parameter (n=250, ndim=3) parameter (m = 2*n) double precision err, tt0, tt1, mbytes, rmax, ravg integer g_a, g_b, d_a, d_b double precision drand integer i, req, loop integer dlo(ndim),dhi(ndim),glo(ndim),ghi(ndim) integer dims(ndim),reqdims(ndim) integer me, nproc integer index, ld(ndim), chunk(ndim) integer iran #if USEMULTFILES integer ilen character*80 filename, filename1 #endif integer util_mdtob logical status external drand external util_mdtob intrinsic int, dble iran(i) = int(drand(0)*dble(i-1)) + 1 c loop = 30 req = -1 nproc = ga_nnodes() me = ga_nodeid() c if (me.eq.0) print *, 'Creating global arrays ',n,' x',n,' x',n if (me.eq.0)call ffflush(6) call ga_sync() do i = 1, ndim dims(i) = n chunk(i) = 1 end do if(.not. nga_create(MT_DBL, ndim, dims, 'a', chunk, g_a)) & call ga_error('nga_create failed: a', 0) if(.not. nga_create(MT_DBL, ndim, dims, 'b', chunk, g_b)) & call ga_error('nga_create failed: b', 0) if (me.eq.0) print *,'done ' if (me.eq.0)call ffflush(6) c c initialize g_a, g_b with random values c ... use ga_access to avoid allocating local buffers for ga_put c call ga_sync() call nga_distribution(g_a, me, glo, ghi) call nga_access(g_a, glo, ghi, index, ld) call fill_random(DBL_MB(index), glo, ghi, ld(1), ld(2)) call ga_sync() * if (me.eq.0) print *,'done ' * if (me.eq.0)call ffflush(6) c call ga_zero(g_b) c c c....................................................................... if (me.eq.0) print *, 'creating disk array ',n,' x',n,' x',n if (me.eq.0)call ffflush(6) do i = 1, ndim reqdims(i) = n end do #if USEMULTFILES ilen = len(FNAME) filename(1:ilen) = FNAME write(filename(ilen+1:ilen+1),200) me 200 format(i1) if(ndra_create(MT_DBL, ndim, dims, 'A', & filename, & DRA_RW, reqdims, d_a).ne.0) $ CALL ga_error('ndra_create failed: ',0) #else if(ndra_create(MT_DBL, ndim, dims, 'A', & FNAME, & DRA_RW, reqdims, d_a).ne.0) $ CALL ga_error('ndra_create failed: ',0) #endif c if(me.eq.0) print *, 'alligned blocking write' if (me.eq.0)call ffflush(6) tt0 = MP_TIMER() if(ndra_write(g_a, d_a,req).ne.0) $ CALL ga_error('ndra_write failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 rmax = tt1 call ga_dgop(1,rmax,1,'max') mbytes = 1e-6*util_mdtob(n*n*n) if (me.eq.0)then write(6,100)mbytes,rmax,mbytes/rmax endif c if(dra_close(d_a).ne.0)call ga_error('dra_close failed: ',d_a) tt1 = MP_TIMER() -tt0 rmax = tt1 call ga_dgop(1,rmax,1,'max') if (me.eq.0)then write(6,100)mbytes,rmax,mbytes/rmax endif c if (me.eq.0) print *,' ' if (me.eq.0) print *,'disk array closed ' if (me.eq.0)call ffflush(6) c....................................................................... c c if (me.eq.0) print *, 'creating disk array ',m,' x',m,' x',m if (me.eq.0)call ffflush(6) do i = 1, ndim dims(i) = m reqdims(i) = n end do #ifdef USEMULTFILES ilen = len(FNAME1) filename1(1:ilen) = FNAME1 write(filename1(ilen+1:ilen+1),200) me if(ndra_create(MT_DBL, ndim, dims, 'B', & filename1, & DRA_RW, reqdims, d_b).ne.0) $ CALL ga_error('ndra_create failed: ',0) #else if(ndra_create(MT_DBL, ndim, dims, 'B', & FNAME1, & DRA_RW, reqdims, d_b).ne.0) $ CALL ga_error('ndra_create failed: ',0) #endif c if(me.eq.0) print *, 'non alligned blocking write' if (me.eq.0)call ffflush(6) c do i = 1, ndim glo(i) = 1 ghi(i) = n dlo(i) = 2 dhi(i) = n+1 end do tt0 = MP_TIMER() if(ndra_write_section(.false., g_a, glo, ghi, & d_b, dlo, dhi, req).ne.0) & call ga_error('ndra_write_section failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 rmax = tt1 call ga_dgop(1,rmax,1,'max') mbytes = 1e-6*util_mdtob(n*n*n) if (me.eq.0)then write(6,100)mbytes,rmax,mbytes/rmax endif c if(dra_close(d_b).ne.0)call ga_error('dra_close failed: ',d_b) tt1 = MP_TIMER() -tt0 rmax = tt1 call ga_dgop(1,rmax,1,'max') mbytes = 1e-6*util_mdtob(n*n*n) if (me.eq.0)then write(6,100)mbytes,rmax,mbytes/rmax endif c if (me.eq.0) print *,' ' if (me.eq.0) print *,'disk array closed ' if (me.eq.0)call ffflush(6) c....................................................................... c c if (me.eq.0) print *,' ' if (me.eq.0) print *,'opening disk array' #ifdef USEMULTFILES if(dra_open(filename, & DRA_R, d_a).ne.0) & call ga_error('dra_open failed',0) #else if(dra_open(FNAME, & DRA_R, d_a).ne.0) & call ga_error('dra_open failed',0) #endif if(me.eq.0) print *, 'alligned blocking read' if (me.eq.0)call ffflush(6) tt0 = MP_TIMER() if(ndra_read(g_b, d_a, req).ne.0) $ CALL ga_error('ndra_read failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 rmax = tt1 call ga_dgop(1,rmax,1,'max') if (me.eq.0)then write(6,100)mbytes,rmax,mbytes/rmax endif call ga_dadd(1d0, g_a, -1d0, g_b, g_b) err = ga_ddot(g_b, g_b) if(err.ne.0) then if (me.eq.0) print *,'BTW, we have error =', err cbjp call ga_print(g_b) else if (me.eq.0) print *,'OK' endif if(dra_delete(d_a).ne.0) & call ga_error('dra_delete failed',0) c....................................................................... c if (me.eq.0) print *,' ' if (me.eq.0) print *,'opening disk array' #ifdef USEMULTFILES if(dra_open(filename1, & DRA_R, d_b).ne.0) & call ga_error('dra_open failed',0) #else if(dra_open(FNAME1, & DRA_R, d_b).ne.0) & call ga_error('dra_open failed',0) #endif if(me.eq.0) print *, 'non alligned blocking read' if (me.eq.0)call ffflush(6) tt0 = MP_TIMER() if(ndra_read_section(.false., g_b, glo, ghi, & d_b, dlo, dhi, req).ne.0) & call ga_error('ndra_read_section failed:',0) if(dra_wait(req).ne.0) call ga_error('dra_wait failed: ' ,req) tt1 = MP_TIMER() -tt0 rmax = tt1 call ga_dgop(1,rmax,1,'max') if (me.eq.0)then write(6,100)mbytes,rmax,mbytes/rmax endif call ga_dadd(1d0, g_a, -1d0, g_b, g_b) err = ga_ddot(g_b, g_b) if(err.ne.0) then if (me.eq.0) print *,'BTW, we have error =', err else if (me.eq.0) print *,'OK' endif if(dra_delete(d_b).ne.0) & call ga_error('dra_delete failed',0) c....................................................................... status = ga_destroy(g_a) status = ga_destroy(g_b) 100 format(g11.2,' MB time=',g11.2,' rate=',g11.3,'MB/s') end subroutine fill_random(a, lo, hi, ld1, ld2) parameter (ndim = 3) integer lo(ndim), hi(ndim), ld1, ld2 double precision a(ld1,ld2,*), drand, seed1, seed2 integer i,j,k external drand c do k=1, hi(3)-lo(3) + 1 seed1 = drand(k) do j = 1, hi(2) - lo(2) + 1 seed2 = seed1*j do i = 1, hi(1) - lo(1) + 1 a(i,j,k)=seed2*i end do enddo enddo end ga-5.9.2/pario/dra/perfn.c000066400000000000000000000470751500715745200152770ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #define FNAME "/scratch/da.try" #define FNAME_ALT "/tmp/da.try" #define FNAME1 "/scratch/da1.try" #define FNAME1_ALT "/tmp/da1.try" #define FNAME2 "/scratch/da2.try" #define FNAME2_ALT "/tmp/da2.try" #include "dra.h" #include "ga.h" #include "macdecls.h" #include "mp3.h" #ifndef MAXDIM # define MAXDIM GA_MAX_DIM #endif #ifndef TRUE # define TRUE (Logical)1 #endif #ifndef FALSE # define FALSE (Logical)0 #endif /* If USER_CONFIG set to: * 0: Use default configuration * 1: Number of files is 1, number of I/O procs equals * the number of nodes * 2: Number of files and number of I/O procs equals * the number of nodes * 3: Number of files and number of I/O procs equals 1 * These configurations only apply if files are not located * on local scratch disk. For local scratch, system defaults * to 1 I/O proc per node and 1 file per node. Note that * USER_CONFIG=1 will only work if system has parallel I/O. * * Memory and Disk Usage: * The aggregate memory required to run this test is approximately * 2*SIZE**NDIM*sizeof(double) bytes. The amount of disk space * required is approximately 1+2**NDIM times this amount. */ #define USER_CONFIG 0 #define TEST_TRANSPOSE 0 #define NDIM 3 #define SIZE 250 /* #define NDIM 2 #define SIZE 4000 #define NDIM 1 #define SIZE 16000000 */ #define SWITCH 0 /*#define MAXDIM 7*/ /*#define TRUE (logical)1*/ /*#define FALSE (logical)0*/ #define MULTFILES 0 # define USEMULTFILES 0 #define IA 16807 #define IM 2147483647 #define AM (1.0/IM) #define IQ 127773 #define IR 2836 #define MASK 123459876 void filename_check(char *result, const char *fname, const char *fname_alt) { FILE *fd; strcpy(result, fname); if (! (fd = fopen(result, "w"))) { strcpy(result, fname_alt); if (! (fd = fopen(result, "w"))) { GA_Error("Could not open file", 0); } } fclose(fd); } float ran0(long *idum) { long k; float ans; *idum ^= MASK; k=(*idum)/IQ; *idum = IA*(*idum-k*IQ)-IR*k; if (*idum < 0) *idum += IM; ans=AM*(*idum); *idum ^= MASK; return ans; } void fill_random(double *a, int isize) { long *idum; long i, j; j = 38282; idum = &j; a[0] = (double)ran0(idum); for (i=0; i<(long)isize; i++) { a[i] = (double)(10000.0*ran0(idum)); } } void test_io_dbl() { int ndim = NDIM; double err, tt0, tt1, mbytes; int g_a, g_b, d_a, d_b; #if TEST_TRANSPOSE int g_c, g_d, d_c; #endif int i, req, loop; dra_size_t dlo[MAXDIM],dhi[MAXDIM]; dra_size_t n, m; dra_size_t ddims[MAXDIM], reqdims[MAXDIM]; int glo[MAXDIM],ghi[MAXDIM]; int dims[MAXDIM]; int me, nproc, isize, numfiles, nioprocs; double plus, minus; double *index; int ld[MAXDIM], chunk[MAXDIM]; #if USEMULTFILES int ilen; #endif char filename[80], filename1[80]; n = SIZE; m = 2*SIZE; loop = 30; req = -1; nproc = GA_Nnodes(); me = GA_Nodeid(); nioprocs = GA_Cluster_nnodes(); numfiles = nioprocs; if (me == 0) { printf("Creating temporary global arrays %ld",(long)n); for (i=1; i #endif #if HAVE_MATH_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #define FNAME "/scratch/da.try" #define FNAME_ALT "/tmp/da.try" #include "dra.h" #include "eaf.h" #include "ga.h" #include "macdecls.h" #include "mp3.h" #ifndef MAXDIM # define MAXDIM GA_MAX_DIM #endif #ifndef TRUE # define TRUE (Logical)1 #endif #ifndef FALSE # define FALSE (Logical)0 #endif /* If USER_CONFIG set to: * 0: Use default configuration * 1: Number of files is 1, number of I/O procs equals * the number of nodes * 2: Number of files and number of I/O procs equals * the number of nodes * 3: Number of files and number of I/O procs equals 1 * These configurations only apply if files are not located * on local scratch disk. For local scratch, system defaults * to 1 I/O proc per node and 1 file per node. Note that * USER_CONFIG=1 will only work if system has parallel I/O. * * Memory and Disk Usage: * The aggregate memory required to run this test is approximately * 2*SIZE**NDIM*sizeof(double) bytes. The amount of disk space * required is approximately NFACTOR**NDIM times this amount. */ #define USER_CONFIG 0 #define NDIM 3 #define SIZE 250 #define NFACTOR 7 /* #define NDIM 2 #define SIZE 4000 #define NFACTOR 3 #define NDIM 1 #define SIZE 12500000 #define NFACTOR 640 */ /*#define MAXDIM 7*/ /*#define TRUE (logical)1*/ /*#define FALSE (logical)0*/ #define MULTFILES 0 # define USEMULTFILES 0 #define IA 16807 #define IM 2147483647 #define AM (1.0/IM) #define IQ 127773 #define IR 2836 #define MASK 123459876 float ran0(long *idum) { long k; float ans; *idum ^= MASK; k=(*idum)/IQ; *idum = IA*(*idum-k*IQ)-IR*k; if (*idum < 0) *idum += IM; ans=AM*(*idum); *idum ^= MASK; return ans; } void fill_random(double *a, int isize) { long *idum; long i, j; j = 38282; idum = &j; a[0] = (double)ran0(idum); for (i=0; i<(long)isize; i++) { a[i] = (double)(10000.0*ran0(idum)); } } void test_io_dbl() { int n, ndim = NDIM; double err, tt0, tt1, mbytes; int g_a, g_b, d_a; int i, itmp, j, req, loop; int glo[MAXDIM],ghi[MAXDIM]; dra_size_t dlo[MAXDIM],dhi[MAXDIM]; dra_size_t ddims[MAXDIM],reqdims[MAXDIM]; dra_size_t m; int index[MAXDIM], dims[MAXDIM]; int me, nproc, isize; double *ptr; double plus, minus; int ld[MAXDIM], chunk[MAXDIM]; char filename[80]; FILE *fd; n = SIZE; m = ((dra_size_t)NFACTOR)*((dra_size_t)SIZE); loop = 1; for (i=0; i #endif #if HAVE_STDLIB_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_LINUX_LIMITS_H # include #endif #if HAVE_LIMITS_H # include #endif #ifdef WIN32 # define PATH_MAX _MAX_PATH # define F_OK 2 #else # include #endif #ifndef PATH_MAX # define PATH_MAX 4096 #endif #ifdef EAF_STATS # include # include #endif #include "elio.h" #include "eaf.h" #include "eafP.h" #include "macdecls.h" #ifdef OPEN_MAX # define EAF_MAX_FILES OPEN_MAX #else # define EAF_MAX_FILES 1024 #endif static int eafhack_openfiles=0; static struct { char *fname; /**< Filename --- if non-null is active*/ Fd_t elio_fd; /**< ELIO file descriptor */ int type; /**< file type */ int nwait; /**< #waits */ int nwrite; /**< #synchronous writes */ int nread; /**< #synchronous reads */ int nawrite; /**< #asynchronous writes */ int naread; /**< #asynchronous reads */ double nb_write; /**< #synchronous bytes written */ double nb_read; /**< #synchronous bytes read */ double nb_awrite; /**< #asynchronous bytes written */ double nb_aread; /**< #asynchronous bytes read */ double t_write; /**< Wall seconds synchronous writing */ double t_read; /**< Wall seconds synchronous reading */ double t_awrite; /**< Wall seconds asynchronous writing */ double t_aread; /**< Wall seconds asynchronous reading */ double t_wait; /**< Wall seconds waiting */ Integer size; /**< size for MA hack */ Integer handle; /**< handle for MA hack */ char *pointer; /**< pointer for MA */ Integer openma; /**< open yes or no for MA to simulate file behavoir */ } file[EAF_MAX_FILES]; int eaf_flushbuf(int , eaf_off_t , const void *, size_t ); static int valid_fd(int fd) { return ( (fd >= 0) && (fd < EAF_MAX_FILES) && (file[fd].fname) ); } /** * Return wall_time in seconds as cheaply and as accurately as possible */ static double wall_time(void) { #ifdef EAF_STATS static int firstcall = 1; static unsigned firstsec, firstusec; double low, high; struct timeval tp; struct timezone tzp; if (firstcall) { (void) gettimeofday(&tp,&tzp); firstusec = tp.tv_usec; firstsec = tp.tv_sec; firstcall = 0; } (void) gettimeofday(&tp,&tzp); low = (double) (tp.tv_usec>>1) - (double) (firstusec>>1); high = (double) (tp.tv_sec - firstsec); return high + 1.0e-6*(low+low); #else /* EAF_STATS */ return 0.0; #endif /* EAF_STATS */ } /** * Open the named file returning the EAF file descriptor in fd. * Return 0 on success, non-zero on failure */ int EAF_Open(const char *fname, int type, int *fd) { int i=0, j=0, found=0; char *ptr; Integer handle; MA_AccessIndex index; while ((i0) { found=1; break; } } #ifdef DEBUG printf(" JJJ %d III %d fname %s filejfname %s found %d type %d\n", j, i, fname, file[j].fname, found, type); #endif if(type > 0) { /* check if this file aka MA region labeled by fname is already open with size >=0*/ #ifdef DEBUG printf(" fname %s type %d found %d \n", fname, type, found); #endif if(found == 0 ) { file[i].size=(Integer)type*1024*1024; #ifdef DEBUG printf(" fname %s type %lf alloc_get size %lf \n", fname, type, file[i].size); #endif if (!MA_alloc_get(MT_CHAR, file[i].size, fname, &handle, &index)) return EAF_ERR_OPEN; if ((i +1) > eafhack_openfiles) eafhack_openfiles++; /* MA hack: we pass type = sizeof MA alloc in megabytes */ MA_get_pointer(handle, &ptr); if (!(file[i].fname = strdup(fname))) return EAF_ERR_MEMORY; file[i].pointer=ptr; file[i].handle=handle; file[i].openma=1; }else{ #ifdef DEBUG printf(" found old fileMA %d size %ld \n", j, file[j].size); #endif /* need check if new size is <= old size*/ i=j; file[i].openma=1; } type=0; #ifdef DEBUG printf(" size %ld ptr %p \n", file[i].size, file[i].pointer); #endif }else{ if (!(file[i].fname = strdup(fname))) return EAF_ERR_MEMORY; if(type > 0) type = EAF_RW; #ifdef DEBUG printf(" opening regular %d eaf %s \n", i, fname); #endif if (!(file[i].elio_fd = elio_open(fname, type, ELIO_PRIVATE))) { free(file[i].fname); file[i].fname = 0; return ELIO_PENDING_ERR; } } file[i].nwait = file[i].nread = file[i].nwrite = file[i].naread = file[i].nawrite = 0; file[i].nb_read = file[i].nb_write = file[i].nb_aread = file[i].nb_awrite = file[i].t_read = file[i].t_write = file[i].t_wait = 0.0; file[i].type = type; *fd = i; return EAF_OK; } /** * Close the EAF file and return 0 on success, non-zero on failure */ int EAF_Close(int fd) { if (!valid_fd(fd)) return EAF_ERR_INVALID_FD; if (file[fd].size > 0) { #ifdef DEBUG printf(" maclosing %d %s \n", fd, file[fd].fname); #endif file[fd].openma=0; return 0; }else{ #ifdef DEBUG printf(" closing regular file %s fd %d \n", file[fd].fname, fd); #endif free(file[fd].fname); file[fd].fname = 0; return elio_close(file[fd].elio_fd); } } /** * Write the buffer to the file at the specified offset. * Return 0 on success, non-zero on failure */ int EAF_Write(int fd, eaf_off_t offset, const void *buf, size_t bytes) { double start = wall_time(); Size_t rc; if (!valid_fd(fd)) return EAF_ERR_INVALID_FD; if (file[fd].size > 0) { if((Integer)(offset+bytes)>file[fd].size){ #if 1 printf("eaf_write failure: increase MA stack memory \n "); return EAF_ERR_WRITE; #else rc=0; printf("eaf_write: offset %ld larger than MA size %ld ptr %p \n", (long)(offset+bytes), file[fd].size, file[fd].pointer); rc=eaf_flushbuf(fd, offset, buf, bytes); printf("eaf_write: from flushbug rc %d bytes %d\n ", rc, bytes); #endif }else{ memcpy(((char*)file[fd].pointer)+(long)offset, buf, bytes); rc=bytes; } }else{ rc = elio_write(file[fd].elio_fd, (Off_t) offset, buf, (Size_t) bytes); } if (rc != ((Size_t)bytes)){ printf("eaf_write: rc ne bytes %ld bytes %ld\n ", rc, (long)bytes); if(rc < 0) return((int)rc); /* rc<0 means ELIO detected error */ else return EAF_ERR_WRITE; }else { file[fd].nwrite++; file[fd].nb_write += bytes; file[fd].t_write += wall_time() - start; return EAF_OK; } } /** * Initiate an asynchronous write of the buffer to the file at the * specified offset. Return in *req_id the ID of the request for * subsequent use in EAF_Wait/probe. The buffer may not be reused until * the operation has completed. * Return 0 on success, non-zero on failure */ int EAF_Awrite( int fd, eaf_off_t offset, const void *buf, size_t bytes, int *req_id) { double start = wall_time(); io_request_t req; int rc; if (!valid_fd(fd)) return EAF_ERR_INVALID_FD; if (file[fd].size > 0) { if(offset>file[fd].size){ rc=0; printf("eaf_awrite: offset %f larger than MA size %ld \n", offset, file[fd].size); return EAF_ERR_WRITE; }else{ memcpy(((char*)file[fd].pointer)+(long)offset, buf, bytes); rc=bytes; } }else{ rc = elio_awrite(file[fd].elio_fd, (Off_t)offset, buf, (Size_t)bytes, &req); } if(!rc){ *req_id = req; file[fd].nawrite++; file[fd].nb_awrite += bytes; } file[fd].t_awrite += wall_time() - start; return rc; } /** * Read the buffer from the specified offset in the file. * Return 0 on success, non-zero on failure */ int EAF_Read(int fd, eaf_off_t offset, void *buf, size_t bytes) { double start = wall_time(); Size_t rc; if (!valid_fd(fd)) return EAF_ERR_INVALID_FD; if (file[fd].size > 0) { if(offset>file[fd].size){ rc=0; printf("eaf_read: offset %f larger than MA size %ld \n", offset, file[fd].size); }else{ memcpy(buf, ((char*)file[fd].pointer)+(long)offset, bytes); rc=bytes; } }else{ rc = elio_read(file[fd].elio_fd, (Off_t) offset, buf, (Size_t) bytes); } if (rc != ((Size_t)bytes)){ if(rc < 0) return((int)rc); /* rc<0 means ELIO detected error */ else return EAF_ERR_READ; } else { file[fd].nread++; file[fd].nb_read += bytes; file[fd].t_read += wall_time() - start; return EAF_OK; } } /** * Initiate an asynchronous read of the buffer from the file at the * specified offset. Return in *req_id the ID of the request for * subsequent use in EAF_Wait/probe. The buffer may not be reused until * the operation has completed. * Return 0 on success, non-zero on failure */ int EAF_Aread(int fd, eaf_off_t offset, void *buf, size_t bytes, int *req_id) { double start = wall_time(); io_request_t req; int rc; if (!valid_fd(fd)) return EAF_ERR_INVALID_FD; if (file[fd].size > 0) { if(offset>file[fd].size){ rc=0; printf("eaf_aread: offset %f larger than MA size %ld \n", offset, file[fd].size); return EAF_ERR_READ; }else{ memcpy(file[fd].pointer, buf, bytes); rc=0; } }else{ rc = elio_aread(file[fd].elio_fd, (Off_t) offset, buf, (Size_t)bytes, &req); } if(!rc){ *req_id = req; file[fd].naread++; file[fd].nb_aread += bytes; } file[fd].t_aread += wall_time() - start; return rc; } /** * Wait for the I/O operation referred to by req_id to complete. * Return 0 on success, non-zero on failure */ int EAF_Wait(int fd, int req_id) { double start = wall_time(); int code; io_request_t req = req_id; if (file[fd].size > 0) { /* got nothin' to do */ }else{ code = elio_wait(&req); } file[fd].t_wait += wall_time() - start; file[fd].nwait++; return code; } /** * status returns 0 if the I/O operation reffered to by req_id * is complete, 1 otherwise. * Return 0 on success, non-zero on failure. */ int EAF_Probe(int req_id, int *status) { io_request_t req = req_id; int rc; #if 0 if (file[fd].size > 0) { /* got nothin' to do */ rc=0; }else{ rc = elio_probe(&req, status); } #else rc=0; #endif if(!rc) *status = !(*status == ELIO_DONE); return rc; } /** * Delete the named file. If the delete succeeds, or the file * does not exist, return 0. Otherwise return non-zero. */ int EAF_Delete(const char *fname) { /* if (access(fname, F_OK) == 0) if (unlink(fname)) return EAF_ERR_UNLINK; return EAF_OK; */ int j, found=0; /* get fd from fname */ for (j=0; (j< eafhack_openfiles); j++){ if(file[j].fname){ if(strcmp(file[j].fname,fname) == 0 && file[j].size >0) { found=1; break; } } } #ifdef DEBUG printf("eaf_delete: fname %s found %d \n", fname, found); if (found ==1) printf("eaf_delete: j %d filej.fname %s handle %d \n", j, file[j].fname,file[j].handle); #endif if (found > 0) { if(!MA_free_heap(file[j].handle)) { MA_summarize_allocated_blocks(); return EAF_ERR_UNLINK; } else if (file[j].fname != NULL) { free(file[j].fname); }; file[j].fname= NULL; return EAF_OK; }else{ /* Now that ELIO files can have extents must call its routine to delete files */ if (elio_delete(fname) == ELIO_OK) return EAF_OK; else return EAF_ERR_UNLINK; } } /** * Return in *avail_mb and *fstype the amount of free space (in Mb) * and filesystem type (currenly UFS, PFS, or PIOFS) of the filesystem * associated with path. Path should be either a filename, or a directory * name ending in a slash (/). fslen should specify the size of the * buffer pointed to by fstype. * * Return 0 on success, non-zero on failure. */ int EAF_Stat(const char *path, long *avail_mb, char *fstype, int fslen) { char dirname[PATH_MAX]; stat_t statinfo; int rc; if ((rc = elio_dirname(path, dirname, sizeof(dirname)))) return rc; if ((rc = elio_stat(dirname, &statinfo))) return rc; if (fslen < 8) return EAF_ERR_TOO_SHORT; *avail_mb = (long)(statinfo.avail>>10); if (statinfo.fs == ELIO_UFS) strcpy(fstype, "UFS"); else if (statinfo.fs == ELIO_PFS) strcpy(fstype, "PFS"); else if (statinfo.fs == ELIO_PIOFS) strcpy(fstype, "PIOFS"); else strcpy(fstype, "UNKNOWN"); return EAF_OK; } /** * Return 0 if code corresponds to EOF, or non-zero. */ int EAF_Eof(int code) { return !(code == EAF_ERR_EOF); } /** * Return in msg (assumed to hold up to 80 characters) * a description of the error code obtained from an EAF call, * or an empty string if there is no such code */ void EAF_Errmsg(int code, char *msg) { if (code == EAF_OK) (void) strcpy(msg, "OK"); else if (code == EAF_ERR_EOF) (void) strcpy(msg, "end of file"); else if (code == EAF_ERR_MAX_OPEN) (void) strcpy(msg, "too many open files"); else if (code == EAF_ERR_MEMORY) (void) strcpy(msg, "memory allocation failed"); else if (code == EAF_ERR_OPEN) (void) strcpy(msg, "failed opening file"); else if (code == EAF_ERR_CLOSE) (void) strcpy(msg, "failed closing file"); else if (code == EAF_ERR_INVALID_FD) (void) strcpy(msg, "invalid file descriptor"); else if (code == EAF_ERR_WRITE) (void) strcpy(msg, "write failed"); else if (code == EAF_ERR_AWRITE) (void) strcpy(msg, "asynchronous write failed"); else if (code == EAF_ERR_READ) (void) strcpy(msg, "read failed"); else if (code == EAF_ERR_AREAD) (void) strcpy(msg, "asynchronous read failed"); else if (code == EAF_ERR_WAIT) (void) strcpy(msg, "wait failed"); else if (code == EAF_ERR_PROBE) (void) strcpy(msg, "probe failed"); else if (code == EAF_ERR_UNLINK) (void) strcpy(msg, "unlink failed"); else if (code == EAF_ERR_UNIMPLEMENTED) (void) strcpy(msg, "unimplemented operation"); else if (code == EAF_ERR_STAT) (void) strcpy(msg, "stat failed"); else if (code == EAF_ERR_TOO_SHORT) (void) strcpy(msg, "an argument string/buffer is too short"); else if (code == EAF_ERR_TOO_LONG) (void) strcpy(msg, "an argument string/buffer is too long"); else if (code == EAF_ERR_NONINTEGER_OFFSET) (void) strcpy(msg, "offset is not an integer"); else if (code == EAF_ERR_TRUNCATE) (void) strcpy(msg, "truncate failed"); else elio_errmsg(code, msg); } /** * Truncate the file to the specified length. * Return 0 on success, non-zero otherwise. */ int EAF_Truncate(int fd, eaf_off_t length) { if (!valid_fd(fd)) return EAF_ERR_INVALID_FD; if(elio_truncate(file[fd].elio_fd, (Off_t)length)) return EAF_ERR_TRUNCATE; return EAF_OK; /* return elio_truncate(file[fd].elio_fd, (Off_t) length);*/ } /** * Return in length the length of the file. * Return 0 on success, nonzero on failure. */ int EAF_Length(int fd, eaf_off_t *length) { Off_t len; int rc; if (!valid_fd(fd)) return EAF_ERR_INVALID_FD; if (file[fd].size > 0) { // should be in MB??? if(file[fd].openma == 0) return EAF_ERR_INVALID_FD; len=(Off_t)file[fd].size; rc=0; }else{ rc = elio_length(file[fd].elio_fd, &len); } if(!rc) *length = (eaf_off_t) len; return rc; } /** * Print performance statistics for this file to standard output */ void EAF_Print_stats(int fd) { eaf_off_t len; double mbr, mbw, mbra, mbwa; if (!valid_fd(fd)) return; if (EAF_Length(fd, &len)) len = -1; printf("\n"); printf("------------------------------------------------------------\n"); #if HAVE_UNSIGNED_LONG_LONG_INT printf("EAF file %d: \"%s\" size=%llu bytes\n", fd, file[fd].fname, (unsigned long long) len); #else printf("EAF file %d: \"%s\" size=%lu bytes\n", fd, file[fd].fname, (unsigned long) len); #endif printf("------------------------------------------------------------\n"); printf(" write read awrite aread wait\n"); printf(" ----- ---- ------ ----- ----\n"); printf(" calls: %8d %8d %8d %8d %8d\n", file[fd].nwrite, file[fd].nread, file[fd].nawrite, file[fd].naread, file[fd].nwait); printf(" data(b): %.2e %.2e %.2e %.2e\n", file[fd].nb_write, file[fd].nb_read, file[fd].nb_awrite, file[fd].nb_aread); printf(" time(s): %.2e %.2e %.2e %.2e %.2e\n", file[fd].t_write, file[fd].t_read, file[fd].t_awrite, file[fd].t_aread, file[fd].t_wait); mbr = 0.0; mbw = 0.0; mbwa= 0.0; mbra= 0.0; if (file[fd].t_write > 0.0) mbw = file[fd].nb_write/(1e6*file[fd].t_write); if (file[fd].t_read > 0.0) mbr = file[fd].nb_read/(1e6*file[fd].t_read); if ((file[fd].t_wait + file[fd].t_aread) > 0.0) mbra = 1e-6*file[fd].nb_aread / (file[fd].t_wait + file[fd].t_aread); if ((file[fd].t_wait + file[fd].t_awrite) > 0.0) mbwa = 1e-6*file[fd].nb_awrite / (file[fd].t_wait + file[fd].t_awrite); /* Note that wait time does not distinguish between read/write completion so that entire wait time is counted in computing effective speed for async read & write */ if (mbwa+mbra) { printf("rate(mb/s): %.2e %.2e %.2e* %.2e*\n", mbw, mbr, mbwa, mbra); printf("------------------------------------------------------------\n"); printf("* = Effective rate. Full wait time used for read and write.\n\n"); } else { printf("rate(mb/s): %.2e %.2e\n", mbw, mbr); printf("------------------------------------------------------------\n\n"); } fflush(stdout); } int eaf_flushbuf(int fd, eaf_off_t offset, const void *buf, size_t bytes) /* once we run out of MA memory, let's open a real eaf file, flush the whole MA allocation to the file, plus the last bytes */ { int rc, fd_new; Integer masize, mahandle; char *mapointer, *oldfname; double start = wall_time(); /* invalidate old FD but do not deallocate MA */ masize=file[fd].size; mahandle=file[fd].handle; mapointer=file[fd].pointer; oldfname = malloc((unsigned) (strlen(file[fd].fname))); strcpy(oldfname, file[fd].fname); file[fd].fname= NULL; rc=EAF_Open(oldfname, EAF_RW, &fd_new); (void) free(oldfname); if (rc !=0 ) { printf(" flushbuf: open failure \n"); return rc; } /* flush MA */ rc = elio_write(file[fd_new].elio_fd, 0., (char*)mapointer, (Size_t) masize); /* write last bytes */ rc = elio_write(file[fd_new].elio_fd, (Off_t) file[fd].size , buf, (Size_t) bytes); if (rc != bytes){ printf(" flushbuf: write failure \n"); if(rc < 0) return((int)rc); /* rc<0 means ELIO detected error */ else return EAF_ERR_WRITE; }else { file[fd_new].nwrite++; file[fd_new].nb_write += file[fd].size; file[fd_new].nwrite++; file[fd_new].nb_write += bytes; file[fd_new].t_write += wall_time() - start; } if(!MA_free_heap(mahandle)) { MA_summarize_allocated_blocks(); return EAF_ERR_UNLINK; } /* swap fd with fd_new, is this too little?? */ fd = fd_new; return rc; } ga-5.9.2/pario/eaf/eaf.doc000066400000000000000000000102021500715745200152060ustar00rootroot00000000000000Here is the Fortran interface. Below this is the C interface with a full description of each parameter. FORTRAN interface ----------------- All functions return an integer error code with the value zero implying success, non-zero implying some error condition. Offsets are doubles and an offset with a fractional component generates an error. The include file "eaf.fh" defines all of the functions along with EAF_R, EAF_W and EAF_rw. integer function eaf_write(fd, offset, buf, bytes) integer function eaf_read (fd, offset, buf, bytes) integer function eaf_awrite(fd, offset, buf, bytes, req_id) integer function eaf_aread (fd, offset, buf, bytes, req_id) integer function eaf_wait(fd, req_id) integer function eaf_probe(req_id, status) integer function eaf_open(fname, type, fd) [type can be one of EAF_R, EAF_RW, and EAF_W] integer function eaf_close(fd) integer function eaf_delete(fname) integer function eaf_length(fd) integer function eaf_truncate(fd, offset) logical function eaf_eof(ierr) Returns true if ierr corresponds to EOF subroutine eaf_errmsg(ierr,message) Returns a string interpretation of the error code, or an empty string (Fortran all blanks, C null first character) if the error code is not recognized. integer function eaf_stat(fname, avail, fstype) subroutine eaf_print_stats(fd) C interface ----------- int eaf_open(const char *fname, int type, int *fd) /* Open the named file returning the EAF file descriptor in fd. Return 0 on success, non-zero on failure */ void eaf_print_stats(int fd) /* Print performance statistics for this file to standard output */ int eaf_close(int fd) /* Close the EAF file and return 0 on success, non-zero on failure */ int eaf_write(int fd, eaf_off_t offset, const void *buf, size_t bytes) /* Write the buffer to the file at the specified offset. Return 0 on success, non-zero on failure */ int eaf_awrite(int fd, eaf_off_t offset, const void *buf, size_t bytes, int *req_id) /* Initiate an asynchronous write of the buffer to the file at the specified offset. Return in *req_id the ID of the request for subsequent use in eaf_wait/probe. The buffer may not be reused until the operation has completed. Return 0 on success, non-zero on failure */ int eaf_read(int fd, eaf_off_t offset, void *buf, size_t bytes) /* Read the buffer from the specified offset in the file. Return 0 on success, non-zero on failure */ int eaf_aread(int fd, eaf_off_t offset, void *buf, size_t bytes, int *req_id) /* Initiate an asynchronous read of the buffer from the file at the specified offset. Return in *req_id the ID of the request for subsequent use in eaf_wait/probe. The buffer may not be reused until the operation has completed. Return 0 on success, non-zero on failure */ int eaf_wait(int fd, int req_id) /* Wait for the I/O operation referred to by req_id to complete. Return 0 on success, non-zero on failure */ int eaf_probe(int req_id, int *status) /* *status returns 0 if the I/O operation reffered to by req_id is complete, 1 otherwise. Return 0 on success, non-zero on failure. */ int eaf_delete(const char *fname) /* Delete the named file. If the delete succeeds, or the file does not exist, return 0. Otherwise return non-zero. */ int eaf_length(int fd, eaf_off_t *length) /* Return in *length the length of the given file. Reutrn 0 on success, non-zero on failure */ int eaf_stat(const char *path, int *avail_kb, char *fstype, int fslen) /* Return in *avail_kb and *fstype the amount of free space (in Kb) and filesystem type (currenly UFS, PFS, or PIOFS) of the filesystem associated with path. Path should be either a filename, or a directory name ending in a slash (/). fslen should specify the size of the buffer pointed to by fstype. Return 0 on success, non-zero on failure. */ int eaf_eof(int code) /* Return 0 if code corresponds to EOF, or non-zero. */ void eaf_errmsg(int code, char *msg) /* Return in msg (assumed to hold up to 80 characters) a description of the error code obtained from an EAF call, or an empty string if there is no such code */ ga-5.9.2/pario/eaf/eaf.fh000066400000000000000000000012311500715745200150400ustar00rootroot00000000000000#ifndef EAF_FORTRAN #define EAF_FORTRAN #endif #include "eaf.h" integer eaf_write, eaf_awrite, eaf_read, eaf_aread integer eaf_wait, eaf_probe, eaf_close, eaf_open integer eaf_delete, eaf_stat, eaf_length, eaf_truncate logical eaf_eof integer eaf_util_szint double precision eaf_util_random external eaf_write, eaf_awrite, eaf_read, eaf_aread external eaf_wait, eaf_probe, eaf_close, eaf_open external eaf_delete, eaf_stat, eaf_length, eaf_truncate external eaf_eof external eaf_util_random,eaf_util_szint integer eaf_rw, eaf_r, eaf_w parameter (eaf_rw=EAF_RW, eaf_r=EAF_R, eaf_w=EAF_W) ga-5.9.2/pario/eaf/eaf.h000066400000000000000000000021351500715745200146760ustar00rootroot00000000000000#ifndef EAF_H #define EAF_H #if 0 /* This section used by both C and Fortran */ #endif #define EAF_RW -1 #define EAF_W -2 #define EAF_R -3 #ifndef EAF_FORTRAN /* This section used by only C */ /* This to ensure size_t is defined */ #include #include typedef double eaf_off_t; int EAF_Aread(int fd, eaf_off_t offset, void *buf, size_t bytes, int *req_id); int EAF_Awrite(int fd, eaf_off_t offset, const void *buf, size_t bytes, int *req_id); int EAF_Close(int fd); int EAF_Delete(const char *fname); int EAF_Eof(int code); void EAF_Errmsg(int code, char *msg); int EAF_Length(int fd, eaf_off_t *length); int EAF_Length(int fd, eaf_off_t *length); int EAF_Open(const char *fname, int type, int *fd); void EAF_Print_stats(int fd); int EAF_Probe(int id, int *status); int EAF_Read(int fd, eaf_off_t offset, void *buf, size_t bytes); int EAF_Stat(const char *path, long *avail_kb, char *fstype, int fslen); int EAF_Truncate(int fd, eaf_off_t length); int EAF_Wait(int fd, int id); int EAF_Write(int fd, eaf_off_t offset, const void *buf, size_t bytes); #endif #endif ga-5.9.2/pario/eaf/eafP.h000066400000000000000000000016161500715745200150210ustar00rootroot00000000000000#ifndef EAFP_H #define EAFP_H /* These macros are for EAF internal use only. EOF_OK must be 0. */ #define EAF_OK 0 #define EAF_ERR_EOF -10001 #define EAF_ERR_MAX_OPEN -10002 #define EAF_ERR_MEMORY -10003 #define EAF_ERR_OPEN -10004 #define EAF_ERR_CLOSE -10005 #define EAF_ERR_INVALID_FD -10006 #define EAF_ERR_WRITE -10007 #define EAF_ERR_AWRITE -10008 #define EAF_ERR_READ -10009 #define EAF_ERR_AREAD -10010 #define EAF_ERR_WAIT -10011 #define EAF_ERR_PROBE -10012 #define EAF_ERR_UNLINK -10013 #define EAF_ERR_UNIMPLEMENTED -10014 #define EAF_ERR_STAT -10015 #define EAF_ERR_TOO_SHORT -10016 #define EAF_ERR_TOO_LONG -10017 #define EAF_ERR_NONINTEGER_OFFSET -10018 #define EAF_ERR_TRUNCATE -10019 #define EAF_ERR_LENGTH -10020 #endif ga-5.9.2/pario/eaf/eaf_f2c.c000066400000000000000000000123661500715745200154320ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_MATH_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_STDLIB_H # include #endif #include "eaf.h" #include "eafP.h" #include "typesf2c.h" #include "farg.h" #define eaf_aread_ F77_FUNC_(eaf_aread,EAF_AREAD) #define eaf_awrite_ F77_FUNC_(eaf_awrite,EAF_AWRITE) #define eaf_close_ F77_FUNC_(eaf_close,EAF_CLOSE) #define eaf_delete_ F77_FUNC_(eaf_delete,EAF_DELETE) #define eaf_eof_ F77_FUNC_(eaf_eof,EAF_EOF) #define eaf_errmsg_ F77_FUNC_(eaf_errmsg,EAF_ERRMSG) #define eaf_error_ F77_FUNC_(eaf_error,EAF_ERROR) #define eaf_length_ F77_FUNC_(eaf_length,EAF_LENGTH) #define eaf_open_ F77_FUNC_(eaf_open,EAF_OPEN) #define eaf_print_stats_ F77_FUNC_(eaf_print_stats,EAF_PRINT_STATS) #define eaf_probe_ F77_FUNC_(eaf_probe,EAF_PROBE) #define eaf_read_ F77_FUNC_(eaf_read,EAF_READ) #define eaf_stat_ F77_FUNC_(eaf_stat,EAF_STAT) #define eaf_truncate_ F77_FUNC_(eaf_truncate,EAF_TRUNCATE) #define eaf_util_random_ F77_FUNC_(eaf_util_random,EAF_UTIL_RANDOM) #define eaf_util_szint_ F77_FUNC_(eaf_util_szint,EAF_UTIL_SZINT) #define eaf_wait_ F77_FUNC_(eaf_wait,EAF_WAIT) #define eaf_write_ F77_FUNC_(eaf_write,EAF_WRITE) static int valid_offset(double offset) { return ((offset - ((double) ((eaf_off_t) offset))) == 0.0); } Integer FATR eaf_write_(Integer *fd, double *offset, const void *buf, Integer *bytes) { if (!valid_offset(*offset)) return EAF_ERR_NONINTEGER_OFFSET; return (Integer) EAF_Write((int) *fd, (eaf_off_t) *offset, buf, (size_t) *bytes); } Integer FATR eaf_awrite_(Integer *fd, double *offset, const void *buf, Integer *bytes, Integer *req_id) { int req, status; if (!valid_offset(*offset)) return EAF_ERR_NONINTEGER_OFFSET; status = EAF_Awrite((int) *fd, (eaf_off_t) *offset, buf, (size_t) *bytes, &req); *req_id = (Integer) req; return (Integer) status; } Integer FATR eaf_read_(Integer *fd, double *offset, void *buf, Integer *bytes) { if (!valid_offset(*offset)) return EAF_ERR_NONINTEGER_OFFSET; return (Integer) EAF_Read((int) *fd, (eaf_off_t) *offset, buf, (size_t) *bytes); } Integer FATR eaf_aread_(Integer *fd, double *offset, void *buf, Integer *bytes, Integer *req_id) { int req, status; if (!valid_offset(*offset)) return EAF_ERR_NONINTEGER_OFFSET; status = EAF_Aread((int) *fd, (eaf_off_t) *offset, buf, (size_t) *bytes, &req); *req_id = (Integer) req; return (Integer) status; } Integer FATR eaf_wait_(Integer *fd, Integer *id) { return (Integer) EAF_Wait((int) *fd, (int) *id); } void FATR eaf_print_stats_(Integer *fd) { EAF_Print_stats((int) *fd); } Integer FATR eaf_truncate_(Integer *fd, double *length) { return (Integer) EAF_Truncate((int) *fd, (eaf_off_t) *length); } Integer FATR eaf_probe_(Integer *id, Integer *status) { int s, code; code = EAF_Probe((int) *id, &s); *status = (Integer) s; return (Integer) code; } Integer FATR eaf_close_(Integer *fd) { return (Integer) EAF_Close((int) *fd); } Integer FATR eaf_length_(Integer *fd, double *length) { eaf_off_t len; int code; code = EAF_Length((int) *fd, &len); if (!code) *length = (double) len; return code; } logical eaf_eof_(Integer *code) { return (logical) EAF_Eof((int) *code); } Integer FATR eaf_open_( #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *fname, Integer *type, Integer *fd, int flen #else char *fname, int flen, Integer *type, Integer *fd #endif ) { char buf[1024]; int code, tmp; ga_f2cstring(fname, flen, buf, sizeof(buf)); /* return (Integer) EAF_ERR_TOO_LONG; TODO errcheck? */ code = EAF_Open(buf, (int) *type, &tmp); *fd = (Integer) tmp; return (Integer)code; } Integer FATR eaf_delete_(char *fname, int flen) { char buf[1024]; ga_f2cstring(fname, flen, buf, sizeof(buf)); /* return (Integer) EAF_ERR_TOO_LONG; TODO errcheck? */ return (Integer) EAF_Delete(buf); } Integer FATR eaf_stat_( #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *path, Integer *avail_kb, char *fstype, int pathlen, int fslen #else char *path, int pathlen, Integer *avail_kb, char *fstype, int fslen #endif ) { char pbuf[1024]; char fbuf[32]; int code; long kb; ga_f2cstring(path, pathlen, pbuf, sizeof(pbuf)); /* return (Integer) EAF_ERR_TOO_LONG; TODO errcheck? */ code = EAF_Stat(pbuf, &kb, fbuf, sizeof(fbuf)); if (!code) { ga_c2fstring(fbuf, fstype, fslen); /* return (Integer) EAF_ERR_TOO_SHORT; TODO errcheck? */ *avail_kb = (double) kb; } return code; } void FATR eaf_errmsg_(Integer *code, char *msg, int msglen) { char buf[80]; EAF_Errmsg((int) *code, buf); (void) ga_c2fstring(buf, msg, msglen); } double FATR eaf_util_random_(Integer* seed) { if(*seed) srandom((unsigned) *seed); return ((double) random())*4.6566128752458e-10; } Integer FATR eaf_util_szint_() { return (Integer)sizeof(Integer); } ga-5.9.2/pario/eaf/test.F000066400000000000000000000252001500715745200150560ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif #define IO_TEST_SZ (1024*1024/8) #define IO_NUM_FILES 4 #define MAX_ITER 4 #define DMAP(x) dble(IO_TEST_SZ*((x-1)*eaf_util_szint())) #define MAX_FILE_SZ 48.0d9 #define BASE_NAME 'EAFfile' #ifdef HPIODIR # define FNAME HPIODIR//BASE_NAME #else # define FNAME BASE_NAME #endif program eaftest c c call test1 c call test2 c call test3 call test4 c print *,' ' print *,'All tests completed' end subroutine test2 implicit none #include "eaf.fh" c integer buf(IO_TEST_SZ, MAX_ITER), buf2(IO_TEST_SZ) character fname*200 integer fnum, iter integer i, nap real dummy integer fd(IO_NUM_FILES) integer buf_sz integer aio(IO_NUM_FILES, MAX_ITER) integer stat2 c buf_sz = (IO_TEST_SZ*eaf_util_szint()) c write(6,*) write(6,*) ' TEST 2 ... asynchronous read/write of multiple files' write(6,*) ' ----------------------------------------------------' write(6,*) c 10 format (A,'.', I1) c do fnum = 1, IO_NUM_FILES write (fname, 10) FNAME, fnum c call testerr(eaf_open(fname, eaf_rw, fd(fnum))) c print *,'testing EAF_AWrite', ' file= ',fname do iter = 1, MAX_ITER do i = 1, IO_TEST_SZ buf(i,iter) = i + fnum + (iter*10000) enddo call testerr(eaf_awrite(fd(fnum), $ DMAP(iter), $ buf(1,iter), buf_sz, aio(fnum, iter))) write(0,*) 'Submitted EAF_AWriteF fnum=', fnum, $ ' iter=',iter, ' aio=',aio(fnum, iter) enddo enddo do fnum = 1, IO_NUM_FILES do iter = 1, MAX_ITER write(0,*) 'Waiting to close fnum=', fnum, $ ' iter=',iter call testerr(eaf_wait(fd(fnum), aio(fnum, iter))) enddo call eaf_print_stats(fd(fnum)) call testerr(eaf_close(fd(fnum))) enddo c................................. print *,'testing EAF_ARead' do fnum = 1, IO_NUM_FILES write (fname, 10) FNAME, fnum call testerr(eaf_open(fname, eaf_r, fd(fnum))) do iter = 1, MAX_ITER do i = 1, IO_TEST_SZ buf(i,iter) = i + fnum + (iter*10000) enddo call testerr(eaf_aread(fd(fnum), $ DMAP(iter), $ buf2, buf_sz, $ aio(fnum,iter))) 1000 call testerr(eaf_probe(aio(fnum, iter), stat2)) write(0,999) stat2 999 format(i5,i8) if(stat2 .ne. 0) then do nap=1, 2000000 dummy = 2.0**2.0+nap enddo goto 1000 endif call sleep(10) write(0,*) 'Finished reading file=',fnum, $ ' fd=',fd(fnum), $ ' iter=',iter do i = 1, IO_TEST_SZ if(buf(i,iter) .ne. buf2(i)) then write(6,*) 'Bad read of file=',fnum, $ ' fd=',fd(fnum), $ ' iter=',iter,' offset=',i,' buf1=', $ buf(i,iter),' buf2=',buf2(i) stop 1 endif enddo enddo enddo c do fnum = 1, IO_NUM_FILES call eaf_print_stats(fd(fnum)) call testerr(eaf_close(fd(fnum))) write (fname, 10) FNAME, fnum call testerr(eaf_delete(fname)) enddo c end subroutine test1 implicit none #include "eaf.fh" integer buf(IO_TEST_SZ), buf2(IO_TEST_SZ) character fname*200 integer fnum, iter integer i integer fd(IO_NUM_FILES) integer buf_sz c buf_sz = (IO_TEST_SZ*eaf_util_szint()) c write(6,*) write(6,*) ' TEST 1 ... synchronous read/write of multiple files' write(6,*) ' ---------------------------------------------------' write(6,*) c do fnum = 1, IO_NUM_FILES write (fname, 10) FNAME, fnum 10 format (A,'.', I1) call testerr(eaf_open(fname, eaf_rw, fd(fnum))) print *, 'testing EAF_Write' do iter = 1, MAX_ITER do i = 1, IO_TEST_SZ buf(i) = i + fnum + (iter*10000) enddo call testerr(eaf_write(fd(fnum), $ DMAP(iter), $ buf(1), buf_sz)) enddo enddo c do fnum = 1, IO_NUM_FILES call eaf_print_stats(fd(fnum)) call testerr(eaf_close(fd(fnum))) enddo c c................................. c do fnum = 1, IO_NUM_FILES write (fname, 10) FNAME, fnum c call testerr(eaf_open(fname, eaf_r, fd(fnum))) c print *, 'testing EAF_Read' do iter = 1, MAX_ITER do i = 1, IO_TEST_SZ buf(i) = i + fnum + (iter*10000) enddo c call testerr(eaf_read(fd(fnum), $ DMAP(iter), $ buf2(1), buf_sz)) do i = 1, IO_TEST_SZ if(buf(i) .ne. buf2(i)) then write(6,*) 'Bad read of file=',fnum, $ ' iter=',iter,' offset=',i,' buf1=', $ buf(i),' buf2=',buf2(i) stop 1 endif enddo enddo enddo c do fnum = 1, IO_NUM_FILES call eaf_print_stats(fd(fnum)) call testerr(eaf_close(fd(fnum))) write (fname, 10) FNAME, fnum call testerr(eaf_delete(fname)) enddo c end subroutine testerr(code) implicit none #include "eaf.fh" integer code character*80 msg integer fstrlen external fstrlen c call eaf_errmsg(code, msg) write(6,*) ' code ', code, ' = ', msg(1:fstrlen(msg)) *** stop 1 c end integer function fstrlen(a) implicit none character*(*) a integer i intrinsic len c do i = len(a),1,-1 if (a(i:i) .ne. ' ') goto 10 enddo c 10 fstrlen = i c end subroutine test3 implicit none #include "eaf.fh" character fname*200 character*8 fstype integer avail, fd double precision length c write(6,*) write(6,*) ' TEST 3 ... misc. functions' write(6,*) ' --------------------------' write(6,*) c write (fname, 10) FNAME, 1 10 format (A,'.', I1) c call testerr(eaf_stat(fname, avail, fstype)) write(6,*) ' avail = ', avail, ' fstype = ', fstype c call testerr(eaf_open(fname, eaf_rw, fd)) call testerr(eaf_length(fd,length)) write(6,*) ' file length = ', length call testerr(eaf_truncate(fd,129.0d0)) call testerr(eaf_length(fd,length)) write(6,*) ' file length (should be 129) = ', length call testerr(eaf_close(fd)) c write(6,*) ' This delete should succeed' call testerr(eaf_delete(fname)) c call testerr(eaf_open(fname, eaf_rw, fd)) call testerr(eaf_length(fd,length)) write(6,*) ' should be zero file length = ', length call testerr(eaf_close(fd)) call testerr(eaf_delete(fname)) c end subroutine test4 implicit none #include "eaf.fh" character*200 fname character*255 fstype integer avail, fd, i, nwrite, ierr, iolen4, iolen, loop double precision length, offset, ofw double precision buf(IO_TEST_SZ) c write(6,*) write(6,*) ' TEST 4 ... fill the file system ' write(6,*) ' ------------------------------- ' write(6,*) c fname = ' ' write (fname, 10) FNAME, 1 10 format (A,'.', I1) c call testerr(eaf_stat(fname, avail, fstype)) write(6,*) ' avail = ', avail, ' fstype = ', fstype avail = 0.95*avail length = 1024*1024.0d0*avail call testerr(eaf_open(fname, eaf_rw, fd)) if(length.gt. MAX_FILE_SZ) then length = MAX_FILE_SZ write(6,*) ' trying max file length ', length,' bytes' else write(6,*) ' 95% available length ', length,' bytes' endif c ofw = 0 do offset = 0, length, (IO_TEST_SZ)*8 nwrite = nint(min(length-offset+1.0d0,(IO_TEST_SZ)*8.0d0)) call util_dfill(nwrite/8, offset, buf, 1) if (ofw .le. offset) then write(6,*) ' writing at ',offset, nwrite ofw = ofw + 100000000.0d0 endif ierr = eaf_write(fd, offset, buf, nwrite) if (ierr .ne. 0) then call testerr(ierr) call ga_error('bad write',ierr) endif enddo c call eaf_print_stats(fd) c ofw = 0 do offset = 0, length, (IO_TEST_SZ)*8 nwrite = nint(min(length-offset+1.0d0,(IO_TEST_SZ)*8.0d0)) if (ofw .le. offset) then write(6,*) ' reading at ', offset, nwrite ofw = ofw + 100000000.0d0 endif ierr = eaf_read(fd, offset, buf, nwrite) if (ierr .ne. 0) then call testerr(ierr) call ga_error('bad read',ierr) endif do i = 1, nwrite/8 if (buf(i).ne.offset) call ga_error('bad ', i) enddo enddo c call eaf_print_stats(fd) c call testerr(eaf_close(fd)) c c random read and write c buf(1) = eaf_util_random(35321) do iolen4 = 0,2 iolen = 8*1024 * (4**iolen4) ! 1024,4096,16384 doubles write(6,*) ' Random write: len=',iolen call testerr(eaf_open(fname, eaf_rw, fd)) do loop = 1, int(length/iolen)/4 offset = aint((length-iolen)*eaf_util_random(0)) ierr = eaf_write(fd, offset, buf, iolen) if (ierr .ne. 0) then call testerr(ierr) call ga_error('bad write',ierr) endif enddo call eaf_print_stats(fd) write(6,*) ' Random read : len=',iolen call testerr(eaf_open(fname, eaf_rw, fd)) do loop = 1, int(length/iolen)/4 offset = aint((length-iolen)*eaf_util_random(0)) ierr = eaf_read(fd, offset, buf, iolen) if (ierr .ne. 0) then call testerr(ierr) call ga_error('bad write',ierr) endif enddo call eaf_print_stats(fd) call testerr(eaf_close(fd)) enddo c call testerr(eaf_delete(fname)) c end subroutine util_dfill(n,val,a,ia) implicit none double precision a(*), val integer n, ia, i c c initialise double precision array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif end ga-5.9.2/pario/elio/000077500000000000000000000000001500715745200141665ustar00rootroot00000000000000ga-5.9.2/pario/elio/chemio.h000066400000000000000000000006251500715745200156060ustar00rootroot00000000000000#if !defined(CHEMIO_H) #define CHEMIO_H #define ELIO_RW -1 #define ELIO_W -2 #define ELIO_R -3 #define ELIO_DONE -1 #define ELIO_PENDING 1 #define ELIO_OK 0 #define CHEMIO_RW ELIO_RW #define CHEMIO_W ELIO_W #define CHEMIO_R ELIO_R #define CHEMIO_DONE ELIO_DONE #define CHEMIO_PENDING ELIO_PENDING #define CHEMIO_OK ELIO_OK #endif ga-5.9.2/pario/elio/elio.c000066400000000000000000000551741500715745200152760ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** @file **********************************************************************\ ELementary I/O (ELIO) disk operations for parallel I/O libraries Authors: Jarek Nieplocha (PNNL) and Jace Mogill (ANL) * * DISCLAIMER * * This material was prepared as an account of work sponsored by an * agency of the United States Government. Neither the United States * Government nor the United States Department of Energy, nor Battelle, * nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR * ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, * COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, * SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT * INFRINGE PRIVATELY OWNED RIGHTS. * * ACKNOWLEDGMENT * * This software and its documentation were produced with United States * Government support under Contract Number DE-AC06-76RLO-1830 awarded by * the United States Department of Energy. The United States Government * retains a paid-up non-exclusive, irrevocable worldwide license to * reproduce, prepare derivative works, perform publicly and display * publicly by or for the US Government, including the right to * distribute to other US Government contractors. */ #ifdef USE_LUSTRE #include /* for O_LOV_DELAY_CREATE, LL_IOC_LOV_SETSTRIPE */ #include /* for struct lov_mds_md, LOV_MAGIC */ #include /* for ioctl */ #endif #include "eliop.h" #if defined(WIN32) #undef ERROR #endif #include "../sf/coms.h" #if defined(AIX) || defined(LINUXAIO) /* systems with Asynchronous I/O */ #else # ifndef NOAIO # define NOAIO # endif #endif /****************** Internal Constants and Parameters **********************/ #define MAX_AIO_REQ 4 #define NULL_AIO -123456 #define FOPEN_MODE 0644 #define MAX_ATTEMPTS 10 #ifndef NOAIO # define AIO 1 #endif #ifdef FFIO # define WRITE ffwrite # define WRITEA ffwritea # define READ ffread # define READA ffreada # define CLOSE ffclose # define SEEK ffseek # define OPEN ffopens # define DEFARG FULL #else # define WRITE write # define WRITEA writea # define READ read # define READA reada # define CLOSE close # define SEEK lseek # define OPEN open # define DEFARG 0 #endif #ifdef WIN32 #define ELIO_FSYNC _commit #else #include #define ELIO_FSYNC fsync #endif /* structure to emulate control block in Posix AIO */ #if defined(AIO) # include # if defined(AIX) # define INPROGRESS EINPROG # else # define INPROGRESS EINPROGRESS # endif struct aiocb cb_fout[MAX_AIO_REQ]; #ifndef AIX const #endif struct aiocb *cb_fout_arr[MAX_AIO_REQ]; #endif #ifndef INPROGRESS # define INPROGRESS 1 #endif static long aio_req[MAX_AIO_REQ]; /* array for AIO requests */ static int first_elio_init = 1; /* intialization status */ int _elio_Errors_Fatal=0; /* sets mode of handling errors */ /****************************** Internal Macros *****************************/ #if defined(AIO) # define AIO_LOOKUP(aio_i) {\ aio_i = 0;\ while(aio_req[aio_i] != NULL_AIO && aio_i < MAX_AIO_REQ) aio_i++;\ } #else # define AIO_LOOKUP(aio_i) aio_i = MAX_AIO_REQ #endif #define SYNC_EMULATE(op) *req_id = ELIO_DONE; \ if((stat= elio_ ## op (fd, offset, buf, bytes)) != bytes ){ \ ELIO_ERROR(stat,0); \ }else \ stat = 0; #ifndef MIN #define PARIO_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #endif /* * Offsets bigger than ABSURDLY_LARGE generate a SEEKFAIL. * The maximum no. of extents permitted for a file is MAX_EXTENT. */ #if defined(_LARGE_FILES) || defined(_LARGEFILE_SOURCE) || defined(_LARGEFILE64_SOURCE) || _FILE_OFFSET_BITS+0 == 64 || SIZEOF_VOIDP == 8 # define LARGE_FILES #endif #define MAX_EXTENT 127 #ifdef LARGE_FILES #define ABSURDLY_LARGE 1e14 #else #define ABSURDLY_LARGE (MAX_EXTENT*2147483648.0) #endif /*****************************************************************************/ static Off_t elio_max_file_size(Fd_t fd) /* * Return the maximum size permitted for this PHYSICAL file. * Presently not file dependent. */ { #ifdef LARGE_FILES return ABSURDLY_LARGE; #else return (2047.0*1024.0*1024.0); /* 2 GB - 1 MB */ #endif } static Fd_t elio_get_next_extent(Fd_t fd) /* * Return a pointer to the file descriptor that forms * the next extent of this file. If the extension file * does not exist then it is opened. If the open fails * then the usual error condition of elio_open is returned. */ { Fd_t next_fd = (Fd_t) fd->next; if (!next_fd) { /* Eventually need to replace this with user controllable naming * and combine with similar logic in delete routine. */ char fname[ELIO_FILENAME_MAX]; int len; if (fd->extent >= MAX_EXTENT) return 0; strcpy(fname, fd->name); len = strlen(fname); if (fd->extent) len -= 4; sprintf(fname+len,"x%3.3d",fd->extent+1); /*printf("Opening extent %d with name '%s'\n",fd->extent+1,fname);*/ if ((next_fd = elio_open(fname, fd->type, fd->mode))) { next_fd->extent = fd->extent + 1; fd->next = (struct fd_struct *) next_fd; } } return next_fd; } void elio_errors_fatal(int onoff) { _elio_Errors_Fatal = onoff; } /*\ Blocking Write * - returns number of bytes written or error code (<0) if failed \*/ Size_t elio_write(Fd_t fd, Off_t doffset, const void* buf, Size_t bytes) { off_t offset; Size_t stat, bytes_to_write = bytes; Size_t nextbytes; if (doffset >= ABSURDLY_LARGE) ELIO_ERROR(SEEKFAIL,0); /* Follow the linked list of extents down until we hit the file that contains the offset */ if (doffset >= elio_max_file_size(fd)) { Fd_t next_fd = elio_get_next_extent(fd); if (!next_fd) ELIO_ERROR(OPENFAIL,0); doffset -= elio_max_file_size(fd); return elio_write(next_fd, doffset, buf, bytes); } /* Figure out if the write continues onto the next extent */ offset = (off_t) doffset; nextbytes = 0; if ((doffset+bytes_to_write) >= elio_max_file_size(fd)) { nextbytes = bytes_to_write; bytes_to_write = (Size_t) (elio_max_file_size(fd)-doffset); nextbytes -= bytes_to_write; } /*printf("TRYING TO WRITE AT doffset=%f offset=%lu bw=%lu nb=%lu\n", doffset, offset, bytes_to_write, nextbytes);*/ /* Write to this extent */ #ifdef PABLO int pablo_code = PABLO_elio_write; PABLO_start( pablo_code ); #endif if(offset != SEEK(fd->fd,offset,SEEK_SET)) ELIO_ERROR(SEEKFAIL,0); while (bytes_to_write) { stat = WRITE(fd->fd, buf, bytes_to_write); if ((stat == -1) && ((errno == EINTR) || (errno == EAGAIN))) { ; /* interrupted write should be restarted */ } else if (stat > 0) { bytes_to_write -= stat; buf = stat + (char*)buf; /*advance pointer by # bytes written*/ } else { ELIO_ERROR(WRITFAIL, stat); } } /* Only get here if all has gone OK */ #ifdef PABLO PABLO_end(pablo_code); #endif /* Write to next extent(s) ... relies on incrementing of buf */ if (nextbytes) { Fd_t next_fd = elio_get_next_extent(fd); if (!next_fd) ELIO_ERROR(OPENFAIL,0); stat = elio_write(next_fd, (Off_t) 0, buf, nextbytes); if (stat != nextbytes) ELIO_ERROR(WRITFAIL, stat); } return bytes; } int elio_set_cb(Fd_t fd, Off_t doffset, int reqn, void *buf, Size_t bytes) { #if defined(AIO) off_t offset = (off_t) doffset; cb_fout[reqn].aio_offset = offset; cb_fout_arr[reqn] = cb_fout+reqn; cb_fout[reqn].aio_buf = buf; cb_fout[reqn].aio_nbytes = bytes; # if defined(AIX) cb_fout[reqn].aio_whence = SEEK_SET; # else cb_fout[reqn].aio_sigevent.sigev_notify = SIGEV_NONE; cb_fout[reqn].aio_fildes = fd->fd; # endif #endif return ELIO_OK; } /*\ Asynchronous Write: returns 0 if succeded or err code if failed \*/ int elio_awrite(Fd_t fd, Off_t doffset, const void* buf, Size_t bytes, io_request_t * req_id) { off_t offset; Size_t stat; #ifdef AIO int aio_i; #endif if (doffset >= ABSURDLY_LARGE) ELIO_ERROR(SEEKFAIL,0); /* Follow the linked list of extents down until we hit the file that contains the offset */ if (doffset >= elio_max_file_size(fd)) { Fd_t next_fd = elio_get_next_extent(fd); if (!next_fd) ELIO_ERROR(OPENFAIL,0); doffset -= elio_max_file_size(fd); return elio_awrite(next_fd, doffset, buf, bytes, req_id); } /* Figure out if the write continues onto the next extent * ... if so then force the entire request to be done synchronously * so that we don't have to manage multiple async requests */ if ((doffset+((Off_t) bytes)) >= elio_max_file_size(fd)) { *req_id = ELIO_DONE; if (elio_write(fd, doffset, buf, bytes) != bytes) return -1; else return 0; } offset = (off_t) doffset; #ifdef PABLO int pablo_code = PABLO_elio_awrite; PABLO_start( pablo_code ); #endif *req_id = ELIO_DONE; #ifdef AIO AIO_LOOKUP(aio_i); /* blocking io when request table is full */ if(aio_i >= MAX_AIO_REQ){ # if defined(DEBUG) && defined(ASYNC) fprintf(stderr, "elio_awrite: Warning- asynch overflow\n"); # endif SYNC_EMULATE(write); } else { int rc; *req_id = (io_request_t) aio_i; if((rc=elio_set_cb(fd, offset, aio_i, (void*) buf, bytes))) ELIO_ERROR(rc,0); # if defined(AIX) # if !defined(AIX52) && !defined(_AIO_AIX_SOURCE) stat = aio_write(fd->fd, cb_fout + aio_i); # endif # else stat = aio_write(cb_fout+aio_i); # endif aio_req[aio_i] = *req_id; } #else /* call blocking write when AIO not available */ SYNC_EMULATE(write); #endif if(stat ==-1) ELIO_ERROR(AWRITFAIL, 0); #ifdef PABLO PABLO_end(pablo_code); #endif return((int)stat); } /*\ Truncate the file at the specified length. \*/ int elio_truncate(Fd_t fd, Off_t dlength) { off_t length = (off_t) dlength; #ifdef WIN32 # define ftruncate _chsize #endif #ifdef PABLO int pablo_code = PABLO_elio_truncate; PABLO_start( pablo_code ); #endif if(dlength >= elio_max_file_size(fd)){ Fd_t next_fd = elio_get_next_extent(fd); dlength -= elio_max_file_size(fd); # if defined(DEBUG) printf(stderr," calling ftruncate with length = %f \n", dlength); #endif return elio_truncate(next_fd, dlength); } (void) SEEK(fd->fd, 0L, SEEK_SET); if (ftruncate(fd->fd, length)) return TRUNFAIL; else { return ELIO_OK; } #ifdef PABLO PABLO_end(pablo_code); #endif } /*\ Return in length the length of the file \*/ int elio_length(Fd_t fd, Off_t *dlength) { off_t length; int status; /* Add up the lengths of any extents */ if (fd->next) { status = elio_length((Fd_t) fd->next, dlength); *dlength += elio_max_file_size(fd); return status; } else { #ifdef PABLO int pablo_code = PABLO_elio_length; PABLO_start( pablo_code ); #endif if ((length = SEEK(fd->fd, (off_t) 0, SEEK_END)) != -1) status = ELIO_OK; else status = SEEKFAIL; #ifdef PABLO PABLO_end(pablo_code); #endif *dlength = (Off_t) length; return status; } } /*\ Blocking Read * - returns number of bytes read or error code (<0) if failed \*/ Size_t elio_read(Fd_t fd, Off_t doffset, void* buf, Size_t bytes) { off_t offset; Size_t stat, bytes_to_read = bytes; Size_t nextbytes; int attempt=0; if (doffset >= ABSURDLY_LARGE) ELIO_ERROR(SEEKFAIL,0); /* Follow the linked list of extents down until we hit the file that contains the offset */ if (doffset >= elio_max_file_size(fd)) { Fd_t next_fd = elio_get_next_extent(fd); if (!next_fd) ELIO_ERROR(OPENFAIL,0); doffset -= elio_max_file_size(fd); return elio_read(next_fd, doffset, buf, bytes); } /* Figure out if the read continues onto the next extent */ offset = (off_t) doffset; nextbytes = 0; if ((doffset+bytes_to_read) >= elio_max_file_size(fd)) { nextbytes = bytes_to_read; bytes_to_read = (Size_t) (elio_max_file_size(fd)-doffset); nextbytes -= bytes_to_read; } /* Read from this physical file */ #ifdef PABLO int pablo_code = PABLO_elio_read; PABLO_start( pablo_code ); #endif if(offset != SEEK(fd->fd,offset,SEEK_SET)) ELIO_ERROR(SEEKFAIL,0); while (bytes_to_read) { stat = READ(fd->fd, buf, bytes_to_read); if(stat==0){ ELIO_ERROR(EOFFAIL, stat); } else if ((stat == -1) && ((errno == EINTR) || (errno == EAGAIN))) { ; /* interrupted read should be restarted */ } else if (stat > 0) { bytes_to_read -= stat; buf = stat + (char*)buf; /*advance pointer by # bytes read*/ } else { ELIO_ERROR(READFAIL, stat); } attempt++; } /* Only get here if all went OK */ #ifdef PABLO PABLO_end(pablo_code); #endif /* Read from next extent(s) ... relies on incrementing of buf */ if (nextbytes) { Fd_t next_fd = elio_get_next_extent(fd); if (!next_fd) ELIO_ERROR(OPENFAIL,0); stat = elio_read(next_fd, (Off_t) 0, buf, nextbytes); if (stat != nextbytes) ELIO_ERROR(READFAIL, stat); } return bytes; } /*\ Asynchronous Read: returns 0 if succeded or -1 if failed \*/ int elio_aread(Fd_t fd, Off_t doffset, void* buf, Size_t bytes, io_request_t * req_id) { off_t offset = (off_t) doffset; Size_t stat; #ifdef AIO int aio_i; #endif if (doffset >= ABSURDLY_LARGE) ELIO_ERROR(SEEKFAIL,0); /* Follow the linked list of extents down until we hit the file that contains the offset */ if (doffset >= elio_max_file_size(fd)) { Fd_t next_fd = elio_get_next_extent(fd); if (!next_fd) ELIO_ERROR(OPENFAIL,0); doffset -= elio_max_file_size(fd); return elio_aread(next_fd, doffset, buf, bytes, req_id); } /* Figure out if the read continues onto the next extent * ... if so then force the entire request to be done synchronously * so that we don't have to manage multiple async requests */ if ((doffset+((Off_t) bytes)) >= elio_max_file_size(fd)) { *req_id = ELIO_DONE; if (elio_read(fd, doffset, buf, bytes) != bytes) return -1; else return 0; } offset = (off_t) doffset; #ifdef PABLO int pablo_code = PABLO_elio_aread; PABLO_start( pablo_code ); #endif *req_id = ELIO_DONE; #ifdef AIO AIO_LOOKUP(aio_i); /* blocking io when request table is full */ if(aio_i >= MAX_AIO_REQ){ # if defined(DEBUG) fprintf(stderr, "elio_read: Warning- asynch overflow\n"); # endif SYNC_EMULATE(read); } else { *req_id = (io_request_t) aio_i; if((stat=elio_set_cb(fd, offset, aio_i, (void*) buf, bytes))) ELIO_ERROR((int)stat,0); # if defined(AIX) #if !defined(AIX52) && !defined(_AIO_AIX_SOURCE) stat = aio_read(fd->fd, cb_fout+aio_i); #endif # else stat = aio_read(cb_fout+aio_i); # endif aio_req[aio_i] = *req_id; } #else /* call blocking write when AIO not available */ SYNC_EMULATE(read); #endif if(stat ==-1) ELIO_ERROR(AWRITFAIL, 0); #ifdef PABLO PABLO_end(pablo_code); #endif return((int)stat); } /*\ Wait for asynchronous I/O operation to complete. Invalidate id. \*/ int elio_wait(io_request_t *req_id) { int aio_i=0; int rc; rc=0; /* just to remove the compiler warning */ #ifdef PABLO int pablo_code = PABLO_elio_wait; PABLO_start( pablo_code ); #endif if(*req_id != ELIO_DONE ) { # ifdef AIO # if defined(AIX) # if !defined(AIX52) && !defined(_AIO_AIX_SOURCE) do { /* I/O can be interrupted on SP through rcvncall ! */ rc =(int)aio_suspend(1, cb_fout_arr+(int)*req_id); } while(rc == -1 && errno == EINTR); # endif # else if((int)aio_suspend((const struct aiocb *const*)(cb_fout_arr+(int)*req_id), 1, NULL) != 0) rc =-1; # endif if(rc ==-1) ELIO_ERROR(SUSPFAIL,0); #endif while(aio_req[aio_i] != *req_id && aio_i < MAX_AIO_REQ) aio_i++; if(aio_i >= MAX_AIO_REQ) ELIO_ERROR(HANDFAIL, aio_i); aio_req[aio_i] = NULL_AIO; *req_id = ELIO_DONE; } #ifdef PABLO PABLO_end(pablo_code); #endif return ELIO_OK; } /*\ Check if asynchronous I/O operation completed. If yes, invalidate id. \*/ int elio_probe(io_request_t *req_id, int* status) { int errval=-1; int aio_i = 0; #ifdef PABLO int pablo_code = PABLO_elio_probe; PABLO_start( pablo_code ); #endif if(*req_id == ELIO_DONE){ *status = ELIO_DONE; } else { #ifdef AIO # if defined(AIX) errval = aio_error(cb_fout[(int)*req_id].aio_handle); # else errval = aio_error(cb_fout+(int)*req_id); # endif #endif switch (errval) { case 0: while(aio_req[aio_i] != *req_id && aio_i < MAX_AIO_REQ) aio_i++; if(aio_i >= MAX_AIO_REQ) ELIO_ERROR(HANDFAIL, aio_i); *req_id = ELIO_DONE; *status = ELIO_DONE; aio_req[aio_i] = NULL_AIO; break; case INPROGRESS: *status = ELIO_PENDING; break; default: return PROBFAIL; } } #ifdef PABLO PABLO_end(pablo_code); #endif return ELIO_OK; } /*\ Noncollective File Open \*/ Fd_t elio_open(const char* fname, int type, int mode) { Fd_t fd=NULL; stat_t statinfo; int ptype=0, rc; char dirname[ELIO_FILENAME_MAX]; /* Create a file for writing to in lustre with a specified pagesize and stripe. pagesize = 1048576; lustre_stripe_count = 32; are good choices. */ #ifdef USE_LUSTRE struct lov_mds_md stripecfg; int lustre_file; int lustre_stripe_count; int pagesize; pagesize = 1048576; lustre_stripe_count = 32; #endif #ifdef PABLO int pablo_code = PABLO_elio_open; PABLO_start( pablo_code ); #endif if(first_elio_init) elio_init(); switch(type){ case ELIO_W: ptype = O_CREAT | O_TRUNC | O_WRONLY; break; case ELIO_R: ptype = O_RDONLY; break; case ELIO_RW: ptype = O_CREAT | O_RDWR; break; default: ELIO_ERROR_NULL(MODEFAIL, type); } #if defined(WIN32) ptype |= O_BINARY; #endif if((fd = (Fd_t ) malloc(sizeof(fd_struct)) ) == NULL) ELIO_ERROR_NULL(ALOCFAIL, 0); if( (rc = elio_dirname(fname, dirname, ELIO_FILENAME_MAX)) != ELIO_OK) { free(fd); ELIO_ERROR_NULL(rc, 0); } if( (rc = elio_stat(dirname, &statinfo)) != ELIO_OK) { free(fd); ELIO_ERROR_NULL(rc, 0); } fd->fs = statinfo.fs; fd->mode = mode; fd->type = type; fd->extent = 0; fd->next = NULL; #ifdef USE_LUSTRE lustre_file = (strncmp(fname,"/dtemp",6) == 0) && (access(fname, F_OK) != 0) && (ME() == 0); if (lustre_file) { ptype = ptype | O_LOV_DELAY_CREATE ; } #endif fd->fd = OPEN(fname, ptype, FOPEN_MODE ); if( (int)fd->fd == -1) { free(fd); ELIO_ERROR_NULL(OPENFAIL, 0); } fd->name = strdup(fname); #ifdef USE_LUSTRE if (lustre_file) { stripecfg.lmm_magic = LOV_MAGIC; stripecfg.lmm_pattern = 0; /* Only available option for now. */ stripecfg.lmm_stripe_size = pagesize; /* Stripe size in bytes. */ stripecfg.lmm_stripe_count = lustre_stripe_count; if (ioctl((int)fd->fd, LL_IOC_LOV_SETSTRIPE, &stripecfg) < 0) { fprintf(stderr, "fp_create_out_filefp: Error: unable to stripe %s file.\n" "error was %s\n", fname,strerror(errno)); fflush(stderr); free(fd); ELIO_ERROR_NULL(OPENFAIL, 0); } } /* end if (luster_file) (is in /dtemp) */ #endif #ifdef PABLO PABLO_end(pablo_code); #endif return(fd); } /*\ Close File \*/ int elio_close(Fd_t fd) { int status = ELIO_OK; #ifdef PABLO pablo_code = PABLO_elio_close; PABLO_start( pablo_code ); #endif if (fd->next) status = elio_close((Fd_t) fd->next); /*printf("Closing extent %d name %s\n", fd->extent, fd->name);*/ if(CLOSE(fd->fd)==-1 || (status != ELIO_OK)) ELIO_ERROR(CLOSFAIL, 0); free(fd->name); free(fd); #ifdef PABLO PABLO_end(pablo_code); #endif return ELIO_OK; } /*\ Close File \*/ int elio_fsync(Fd_t fd) { int status = ELIO_OK; #ifdef ELIO_FSYNC if (fd->next) status = elio_fsync((Fd_t) fd->next); /* printf("syncing extent %d name %s\n", fd->extent, fd->name); */ /* if(ELIO_FSYNC(fd->fd)==-1 || (status != ELIO_OK)) */ #ifndef WIN32 sync(); #endif if(ELIO_FSYNC(fd->fd)==-1 ) ELIO_ERROR(FSYNCFAIL, 0); #endif return ELIO_OK; } /*\ Delete File \*/ int elio_delete(const char* filename) { int rc; if (access(filename, F_OK) != 0) /* Succeed if the file does not exist */ return ELIO_OK; #ifdef PABLO int pablo_code = PABLO_elio_delete; PABLO_start( pablo_code ); #endif rc = unlink(filename); /* Remeber the first rc ... now delete possible extents until one fails */ { int extent; for (extent=1; extentOK"); return; } else if(code == ELIO_PENDING_ERR) code = elio_pending_error; if(codeOFFSET+ERRLEN) *msg=(char)0; else (void) strcpy(msg, errtable[-OFFSET + code]); } int elio_pending_error=UNKNFAIL; char *errtable[ERRLEN] ={ ">Unable to Seek", ">Write Failed", ">Asynchronous Write Failed", ">Read Failed", ">Asynchronous Read Failed", ">Suspend Failed", ">I/O Request Handle not in Table", ">Incorrect File Mode", ">Unable to Determine Directory", ">Stat For Specified File or Directory Failed", ">Open Failed", ">Unable To Allocate Internal Data Structure", ">Unsupported Feature", ">Unlink Failed", ">Close Failed", ">Operation Interrupted Too Many Times", ">AIO Return Failed", ">Name String too Long", ">Unable to Determine Filesystem Type", ">Numeric Conversion Error", ">Incorrect Filesystem/Device Type", ">Error in Probe", ">Unable to Truncate", ">End of File", ">Fsync Failed", ""}; ga-5.9.2/pario/elio/elio.h000066400000000000000000000045661500715745200153020ustar00rootroot00000000000000/* file name: elio.h to be included by all apps that use ELIO */ /*include file that contains some common constants, also included by fortran */ #include "chemio.h" #include #include #define ELIO_UFS 0 /* Unix filesystem type */ #define ELIO_PFS 1 /* PFS Intel parallel filesystem type */ #define ELIO_PIOFS 2 /* IBM SP parallel filesystem type */ #define ELIO_PENDING_ERR -44 /* error code for failing elio_(g)open */ #define ELIO_SHARED 77 #define ELIO_PRIVATE 88 /*********************** type definitions for ELIO interface *****************/ typedef long Size_t; /* size of I/O request type */ typedef double Off_t; /* size of offset type - double = 56 bit integer*/ typedef struct { int fd; /* OS handle */ int fs; /* ??? */ int mode; /* ??? */ int type; /* ??? */ char *name; /* Name of physical file */ int extent; /* Counts extents of logical files */ struct fd_struct *next; /* Next extent */ } fd_struct; /* file descriptor type definition */ typedef fd_struct* Fd_t; typedef unsigned long avail_t; typedef struct{ int fs; avail_t avail; } stat_t; typedef long io_request_t; /* asynchronous I/O request type */ /********************** prototypes for elio functions ***********************/ extern Size_t elio_read(Fd_t fd, Off_t offset, void *buf, Size_t bytes); extern int elio_aread(Fd_t fd, Off_t offset, void *buf, Size_t bytes, io_request_t *req_id); extern Size_t elio_write(Fd_t fd, Off_t offset, const void *buf, Size_t bytes); extern int elio_awrite(Fd_t fd, Off_t offset, const void *buf, Size_t bytes, io_request_t *req_id); extern int elio_wait(io_request_t *id); extern int elio_probe(io_request_t *id, int* status); extern int elio_delete(const char *filename); extern Fd_t elio_open(const char *fname, int type, int mode); extern int elio_close(Fd_t fd); extern int elio_stat(char *fname, stat_t *statinfo); extern int elio_dirname(const char *fname, char *statinfo, int len); extern int elio_truncate(Fd_t fd, Off_t length); extern int elio_length(Fd_t fd, Off_t *length); extern void elio_errmsg(int code, char *msg); extern int elio_fsync(Fd_t fd); ga-5.9.2/pario/elio/eliop.h000066400000000000000000000065151500715745200154560ustar00rootroot00000000000000#ifndef ELIOP_H #define ELIOP_H #ifdef WIN32 #include #ifndef __MINGW32__ /* #include "winutil.h" */ #endif #define F_OK 00 #endif #include #if HAVE_STDLIB_H #include #endif #if HAVE_STRING_H #include #endif #include #include #include "typesf2c.h" #define PRINT_AND_ABORT(msg, val) GA_Error(msg, (int)val) #ifndef GLOBAL_H extern void GA_Error(char*, int); #endif #if defined(__FreeBSD__) && !defined(GLIBC) # include # include # define STATVFS statfs # define NO_F_FRSIZE #elif defined(WIN32) # define STATVFS _stat # define S_ISDIR(mode) ((mode&S_IFMT) == S_IFDIR) # define S_ISREG(mode) ((mode&S_IFMT) == S_IFREG) #elif defined(LINUX) || defined(CYGWIN) || defined(__GLIBC__) # include # define STATVFS statfs # define NO_F_FRSIZE #else # include # define STATVFS statvfs #endif #ifdef WIN32 #include #else #include #endif #include #if defined(FFIO) # include # include # include #endif #include "elio.h" #include "pablo.h" extern int _elio_Errors_Fatal; extern void elio_init(void); extern int elio_pending_error; #if !defined(PRINT_AND_ABORT) # define PRINT_AND_ABORT(msg, val){\ fprintf(stderr, "ELIO fatal error: %s %ld\n", msg, val);\ fprintf(stdout, "ELIO fatal error: %s %ld\n", msg, val);\ fflush(stdout);\ exit(val);\ } #endif /**************************** Error Macro ******************************/ /* ELIO defines error macro called in case of error * the macro can also use user-provided error routine PRINT_AND_ABORT * defined as macro to do some cleanup in the application before * aborting * The requirement is that PRINT_AND_ABORT is defined before * including ELIO header file - this file */ #define ELIO_ERROR_NULL(code, val){\ PABLO_end(pablo_code);\ if(! _elio_Errors_Fatal){\ elio_pending_error= code;\ return NULL;\ }\ if( _elio_Errors_Fatal)\ PRINT_AND_ABORT(errtable[code-OFFSET], val);\ } #define ELIO_ERROR(code, val) { \ PABLO_end(pablo_code);\ if(! _elio_Errors_Fatal) return(code);\ else PRINT_AND_ABORT(errtable[code-OFFSET], val);\ } /* error codes and messages */ #define ERRLEN 26 #define OFFSET (-2000) #define SEEKFAIL (OFFSET + 0) #define WRITFAIL (OFFSET + 1) #define AWRITFAIL (OFFSET + 2) #define READFAIL (OFFSET + 3) #define AREADFAIL (OFFSET + 4) #define SUSPFAIL (OFFSET + 5) #define HANDFAIL (OFFSET + 6) #define MODEFAIL (OFFSET + 7) #define DIRFAIL (OFFSET + 8) #define STATFAIL (OFFSET + 9) #define OPENFAIL (OFFSET + 10) #define ALOCFAIL (OFFSET + 11) #define UNSUPFAIL (OFFSET + 12) #define DELFAIL (OFFSET + 13) #define CLOSFAIL (OFFSET + 14) #define INTRFAIL (OFFSET + 15) #define RETUFAIL (OFFSET + 16) #define LONGFAIL (OFFSET + 17) #define FTYPFAIL (OFFSET + 18) #define CONVFAIL (OFFSET + 19) #define TYPEFAIL (OFFSET + 20) #define PROBFAIL (OFFSET + 21) #define TRUNFAIL (OFFSET + 22) #define EOFFAIL (OFFSET + 23) #define FSYNCFAIL (OFFSET + 24) #define UNKNFAIL (OFFSET + 25) extern char *errtable[ERRLEN]; #define ELIO_FILENAME_MAX 1024 #define SDIRS_INIT_SIZE 1024 #endif ga-5.9.2/pario/elio/pablo.h000066400000000000000000000017141500715745200154370ustar00rootroot00000000000000/* Header file for PABLO instrumentation */ #if defined(PABLO) # define IOTRACE # include "IOTrace.h" #endif /* Pablo profiler definitions */ # define PABLO_elio_write 710000 # define PABLO_elio_awrite 710001 # define PABLO_elio_read 710002 # define PABLO_elio_aread 710003 # define PABLO_elio_wait 710004 # define PABLO_elio_probe 710005 # define PABLO_elio_stat 710006 # define PABLO_elio_open 710007 # define PABLO_elio_close 710009 # define PABLO_elio_set_cb 710010 # define PABLO_elio_delete 710011 # define PABLO_elio_truncate 710012 # define PABLO_elio_length 710014 #if defined(PABLO) # define PABLO_init initIOTrace # define PABLO_start(_id) startTimeEvent( _id ) # define PABLO_end(_id) endTimeEvent( _id ) # define PABLO_terminate {endIOTrace(); endTracing();} #else # define PABLO_init # define PABLO_start(_id) # define PABLO_end( _id ) # define PABLO_terminate #endif ga-5.9.2/pario/elio/stat.c000066400000000000000000000070021500715745200153040ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Id: stat.c,v 1.20.10.3 2007-08-30 18:19:44 manoj Exp $ */ #include "eliop.h" #include "chemio.h" #define DEBUG_ 0 /** * determines directory path for a given file */ int elio_dirname(const char *fname, char *dirname, int len) { size_t flen = strlen(fname); if(len<=((int)flen)) ELIO_ERROR(LONGFAIL,flen); #ifdef WIN32 while(fname[flen] != '/' && fname[flen] != '\\' && flen >0 ) flen--; #else while(fname[flen] != '/' && flen >0 ) flen--; #endif if(flen==0)strcpy(dirname,"."); else {strncpy(dirname, fname, flen); dirname[flen]=(char)0;} return(ELIO_OK); } #ifdef WIN32 #include #include /** * determine drive name given the file path name */ char* elio_drivename(const char* fname) { static char path[_MAX_PATH]; static char drive[_MAX_DRIVE]; if( _fullpath(path,fname,_MAX_PATH) == NULL) return NULL; _splitpath(path, drive, NULL, NULL, NULL); return(drive); } void get_avail_space(int dev, avail_t *avail, int* bsize) { static char drive[4]="A:\\"; int sectors, cfree, ctotal; drive[0]= dev + 'A'; GetDiskFreeSpace(drive, §ors, bsize, &cfree, &ctotal); *avail = sectors*(avail_t)cfree; } #endif /* WIN32 */ /** * Stat a file (or path) to determine it's filesystem info */ int elio_stat(char *fname, stat_t *statinfo) { struct stat ufs_stat; int bsize; struct STATVFS ufs_statfs; PABLO_start(PABLO_elio_stat); if(stat(fname, &ufs_stat) != 0) ELIO_ERROR(STATFAIL, 1); # if defined(PIOFS) /* fprintf(stderr,"filesystem %d\n",ufs_stat.st_vfstype);*/ /* according to /etc/vfs, "9" means piofs */ if(ufs_stat.st_vfstype == 9) statinfo->fs = ELIO_PIOFS; else # endif statinfo->fs = ELIO_UFS; /* only regular or directory files are OK */ if(!S_ISREG(ufs_stat.st_mode) && !S_ISDIR(ufs_stat.st_mode)) ELIO_ERROR(TYPEFAIL, 1); if(STATVFS(fname, &ufs_statfs) != 0) ELIO_ERROR(STATFAIL,1); # if defined(WIN32) get_avail_space(ufs_statfs.st_dev, &(statinfo->avail), &bsize); # else /* get number of available blocks */ statinfo->avail = (avail_t) ufs_statfs.f_bavail; # ifdef NO_F_FRSIZE /* on some older systems it was f_bsize */ bsize = (int) ufs_statfs.f_bsize; # else /* get block size, fail if bszie is still 0 */ bsize = (int) ufs_statfs.f_frsize; if(bsize==0)bsize =(int) ufs_statfs.f_bsize; if(bsize==0) ELIO_ERROR(STATFAIL, 1); if(DEBUG_) printf("stat: f_frsize=%d f_bsize=%d bsize=%d free blocks=%ld\n", (int) ufs_statfs.f_frsize,(int) ufs_statfs.f_bsize, bsize, statinfo->avail ); # endif # endif /* translate number of available blocks into kilobytes */ switch (bsize) { case 512: statinfo->avail /=2; break; case 1024: break; case 2048: statinfo->avail *=2; break; case 4096: statinfo->avail *=4; break; case 8192: statinfo->avail *=8; break; case 16384: statinfo->avail *=16; break; case 32768: statinfo->avail *=32; break; default: { double avail; double factor = ((double)bsize)/1024.0; avail = factor * (double)statinfo->avail; statinfo->avail = (avail_t) avail; } } PABLO_end(PABLO_elio_stat); return(ELIO_OK); } ga-5.9.2/pario/makefile.h000066400000000000000000000031151500715745200151640ustar00rootroot00000000000000 OSNAME = $(shell uname | awk '{ print $1}') #under AIX, AIX52 is defined to use POSIX API in AIX 5.2.0.0 or greater ifeq ($(OSNAME),AIX) LIB_DEFINES += $(shell /usr/bin/oslevel | awk -F. \ '{ if ($$1 > 5 || ($$1 == 5 && $$2 > 1))\ print "-DAIX52" }') #lsdev -C -l aio0 #aio0 Defined Asynchronous I/O (Legacy) ifdef USE_OLDAIO USE_OLDAIO=Y else USE_OLDAIO= $(shell /usr/sbin/lsdev -C -l aio0 2>&1|grep Lega|awk ' /Legacy/ {print "Y"}') endif ifeq ($(USE_OLDAIO),Y) LIB_DEFINES += -D_AIO_AIX_SOURCE endif endif #under AIX, there can be problems with AIO and large files ifdef LARGE_FILES ifeq ($(OSNAME),AIX) LIB_DEFINES += $(shell /usr/bin/oslevel | awk -F. \ '{ if ($$1 > 4 || ($$1 == 4 && $$2 > 1))\ print "-D_LARGE_FILES -D_LARGE_FILE_API" }') # asynchronous I/O with large files supported starting with 4.2.1 # However, there is a bug in IBM libs on PNNL system that prevents us # from using AIO under 4.2.1 :-) # AIO_LARGE_FILES = $(shell /usr/bin/oslevel | awk -F. \ '{ if ($$1 == 4 && $$2 == 2 && $$3 <= 0 ) \ print "NO"}') endif # # LINUX: kernel 2.4 is needed # ifeq ($(TARGET), LINUX) LIB_DEFINES += -D_LARGEFILE64_SOURCE LIB_DEFINES += $(shell getconf LFS_CFLAGS) endif LIB_DEFINES += -DLARGE_FILES endif ifdef LIB_TARGETS # HPIODIR is used as a filename prefix in test programs ifdef HPIODIR LIB_DEFINES += -DHPIODIR=\'$(HPIODIR)/\' endif endif ifdef USE_LINUXAIO LIB_DEFINES += -DLINUXAIO COMM_LIBS += -lrt endif ga-5.9.2/pario/pablo2upshot000077500000000000000000000030241500715745200156050ustar00rootroot00000000000000#!/bin/csh -f /home/aydt/pablo/bin/SDDFconverter << CONV_END Pablo.bin a pab.ascii n CONV_END ./pabody /bin/rm -f pab.sort pab.upshot sort -n +5 pab.out > pab.sort set nevents=`wc pab.sort | awk '{print $1}'` set tfirst=`head -1 pab.sort | awk '{print $6}'` set tlast=`tail -1 pab.sort | awk '{print $6}'` cat > pab.upshot << END_HEADER -1 0 0 0 0 0 sddf -2 0 0 $nevents 0 0 -3 0 0 1 0 0 -4 0 0 1 0 0 -5 0 0 10 0 0 -6 0 0 0 0 $tfirst -7 0 0 0 0 $tlast -8 0 0 1 0 0 -11 0 0 0 0 4294967295 -13 0 700001 701001 0 0 blue: Native Open -13 0 700003 701003 0 0 blue: Native fopen -13 0 700005 701005 0 0 red: Native close -13 0 700007 701007 0 0 red: Native fclose -13 0 700009 701009 0 0 green: Native read -13 0 700011 701011 0 0 green: Native fread -13 0 700013 701013 0 0 yellow: Native lseek -13 0 700015 701015 0 0 yellow: Native fseek -13 0 700017 701017 0 0 magenta: Native write -13 0 700019 701019 0 0 magenta: Native fwrite -13 0 710000 711000 0 0 chocolate1: elio_write -13 0 710001 711001 0 0 chocolate4: elio_awrite -13 0 710002 711002 0 0 brown1: elio_read -13 0 710003 711003 0 0 brown4: elio_aread -13 0 710004 711004 0 0 LightSalmon1: elio_probe -13 0 710005 711005 0 0 LightSalmon4: elio_wait -13 0 710006 711006 0 0 coral1: elio_stat -13 0 710007 711007 0 0 coral4: elio_open -13 0 710009 711009 0 0 DeepPink1: elio_close -13 0 710010 711010 0 0 DeepPink4: elio_set_cb -13 0 710011 711011 0 0 HotPink1: elio_delete -13 0 710012 711012 0 0 HotPink4: elio_init END_HEADER cat pab.sort >> pab.upshot #/bin/rm -f pab.sort pab.out pab.ascii ga-5.9.2/pario/pabody000077500000000000000000000045461500715745200144530ustar00rootroot00000000000000#!/usr/bin/env perl print "Enter ascii SDDF file: "; #$file = ; $file = "pab.ascii"; open(infile,"$file") || (chop($file) && (die "can't open \"$file\": $!\n")); print "Enter output Upshot file: "; #$file = ; $file = "pab.out"; open(outfile,">$file"); print "\n"; $generic_trace_first = 0; $open_flag = 0; $close_flag = 0; $write_flag = 0; $first{'"Open"'} = 0; $first{'"Close"'} = 0; $first{'"Write"'} = 0; $first{'"Read"'} = 0; $first{'"Seek"'} = 0; while () { if (/"Generic Interval"/) { if ($generic_trace_first == 0) { $generic_trace_first = 1; print "Skipping first: ", $0, "\n"; next; }; $junk = ; $junk = ; $junk = ; $useful = ; $useful =~ /\}, (.+), (.+), (.+), (.+),/; $useful =~ /\}, ([0-9].[0-9]+), ([0-9]+), ([0-9].[0-9]+),/; # print $1," | ", $2," | ", $3," | ", $4," |\n"; $endtime = $1; $eventid = $2; $procid = $3; $duration = $4; $starttime = $endtime - $duration; $starttime = $starttime * 1000000.0; $starttime = int($starttime); # print $starttime, "\n"; $endtime = $endtime * 1000000.0; $endtime = int($endtime); # $endevent = $eventid + 1000; $endevent = $eventid + 1; print outfile $eventid, " ", $procid, " ", 0, " ", 0, " ", 0, " ", $starttime, "\n"; print outfile $endevent, " ", $procid, " ", 0, " ", 0, " ", 0, " ", $endtime, "\n"; } if (/"Open"/ || /"Close"/ || /"Write"/ || /"Read"/ || /"Seek"/) { if( $first_time{ $_ } == 0 ) { $first_time{ $_ } = 1; next; }; $junk = ; $junk = ; $junk = ; $useful = ; $useful =~ /\}, (.+), (\d+), (\d+), (.+), (\d+)/; # print $1, " ", $2, " ", $3, " ", $4, "\n"; $starttime = $1; $eventid = $2; $procid = $3; $duration = $4; $endtime = $starttime + $duration; $starttime = $starttime * 1000000.0; $starttime = int($starttime); # print $starttime, "\n"; $endtime = $endtime * 1000000.0; $endtime = int($endtime); $endevent = $eventid + 1000; print outfile $eventid, " ", $procid, " ", 0, " ", 0, " ", 0, " ", $starttime, "\n"; print outfile $endevent, " ", $procid, " ", 0, " ", 0, " ", 0, " ", $endtime, "\n"; } } close outfile; close infile; ga-5.9.2/pario/sf/000077500000000000000000000000001500715745200136465ustar00rootroot00000000000000ga-5.9.2/pario/sf/SF.doc000066400000000000000000000064051500715745200146520ustar00rootroot00000000000000 Shared Files Model . Shared files are non-persistent (temporary) . Shared files resemble one-dimensional arrays in main memory . Each process can independently read/write to any location in the file . The file size has a hard limit specified when it is created . User can also specify (or use "don't know" flag) the estimated approximate file size -- might be exceeded at run-time (a hint) . sf_flush flushes the buffers so that previously written data goes to the disk before the routine returns. . All routines return error code: "0" means success. . sf_create and sf_destroy are collective . file, request sizes, and offset (all in bytes) are DOUBLE PRECISION arguments, all the other arguments are INTEGERS . read/writes are asynchronous integer sf_create(fname, size_hard_limit, size_soft_limit, req_size, handle) fname -- meta-file name size_hard_limit -- max file size in bytes not to be exceeded (a hint) size_soft_limit -- estimated file size (a hint) req_size -- size of a typical request (a hint) handle -- returned handle to the created file Creates shared file using name and path specified in fname as a template. req_size specifies size of a typical request (-1 = "don't know") integer sf_write(handle, offset, bytes, buffer, request_id) handle -- file handle returned from sf_create [in] offset -- location in file (from the beginning) where data should be written to [in] buffer -- local array to put the data [in] bytes -- number of bytes to read [in] request_id -- id identifying asynchronous operation [out] asynchronous write operation integer sf_read(handle, offset, bytes, buffer, request_it) handle -- file handle returned from sf_create [in] offset -- location in file (from the beginning) where data should be read from [in] buffer -- local array to put the data [in] bytes -- number of bytes to read [in] request_id -- id identifying asynchronous operation [out] asynchronous read operation integer sf_wait(request_id) request_id -- id identifying asynchronous operation [in/out] blocks calling process until I/O operation associated with id completed, invalidates request_id integer sf_waitall(list, num) list(num) -- array of ids for asynchronous operations [in/o] num -- number of entries in list [in] blocks calling process until all "num" I/O operations associated with ids specified in list completed, invalidates ids on the list integer sf_destroy(handle) handle -- file handle returned from sf_create [in] subroutine sf_errmsg(ierr,message) ierr -- error code returned by a previous call to SF [in] message -- character string where the corresponding message is written returns a string interpretation of the error code, or an empty string (Fortran all blanks, C null first character) if the error code is not recognized. ga-5.9.2/pario/sf/coms.h000066400000000000000000000003251500715745200147600ustar00rootroot00000000000000/*************** interface to a parallel communication system ********/ # include "ga.h" # define SYNC GA_Sync # define NPROC GA_Nnodes # define ME GA_Nodeid # define ERROR GA_Error ga-5.9.2/pario/sf/sf.fh000066400000000000000000000004471500715745200146020ustar00rootroot00000000000000 integer sf_create,sf_write,sf_read,sf_wait, sf_destroy integer sf_waitall, sf_open, sf_close, sf_rwtor integer sf_create_suffix external sf_create,sf_write,sf_read,sf_wait, sf_destroy external sf_waitall, sf_open, sf_close, sf_rwtor external sf_create_suffix ga-5.9.2/pario/sf/sf.h000066400000000000000000000015461500715745200144350ustar00rootroot00000000000000#ifndef SF_H_ #define SF_H_ typedef double SFsize_t; #include "typesf2c.h" extern int SF_Create(char* fname, SFsize_t size_hard_limit, SFsize_t size_soft_limit, SFsize_t req_size, int *handle); extern int SF_Create_suffix(char* fname, SFsize_t size_hard_limit, SFsize_t size_soft_limit, SFsize_t req_size, int *handle, int suffix); extern int SF_Destroy(int handle); extern int SF_Rwtor(int handle); extern int SF_Open(int handle); extern int SF_Close(int handle); extern int SF_Fsync(int handle); extern int SF_Write(int handle, SFsize_t offset, SFsize_t bytes, char *buffer, int *req_id); extern int SF_Read(int handle, SFsize_t offset, SFsize_t bytes, char *buffer, int *req_id); extern int SF_Wait(int *req_id); extern int SF_Waitall(int *list, int num); extern void SF_Errmsg(int code, char *msg); #endif /* SF_H_ */ ga-5.9.2/pario/sf/sf_capi.c000066400000000000000000000044011500715745200154150ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_MALLOC_H # include #endif #if HAVE_STDLIB_H # include #endif #include "typesf2c.h" #include "sf.h" #include "sff2c.h" int SF_Create(char* fname, SFsize_t size_hard_limit, SFsize_t size_soft_limit, SFsize_t req_size, int *handle) { Integer s_a; int retcode = sfi_create(fname, &size_hard_limit, &size_soft_limit, &req_size, &s_a); *handle = s_a; return retcode; } int SF_Create_suffix(char* fname, SFsize_t size_hard_limit, SFsize_t size_soft_limit, SFsize_t req_size, int *handle, int suffix) { Integer s_a; Integer isuffix = suffix; int retcode = sfi_create_suffix(fname, &size_hard_limit, &size_soft_limit, &req_size, &s_a, &isuffix); *handle = s_a; return retcode; } int SF_Destroy(int handle) { Integer s_a = handle; return sf_destroy_(&s_a); } int SF_Rwtor(int handle) { Integer s_a = handle; return sf_rwtor_(&s_a); } int SF_Open(int handle) { Integer s_a = handle; return sf_open_(&s_a); } int SF_Close(int handle) { Integer s_a = handle; return sf_close_(&s_a); } int SF_Fsync(int handle) { Integer s_a = handle; return sf_fsync_(&s_a); } int SF_Write(int handle, SFsize_t offset, SFsize_t bytes, char *buffer, int *req_id) { Integer s_a = handle; Integer id; int retcode = sf_write_(&s_a, &offset, &bytes, buffer, &id); *req_id = id; return retcode; } int SF_Read(int handle, SFsize_t offset, SFsize_t bytes, char *buffer, int *req_id) { Integer s_a = handle; Integer id; int retcode = sf_read_(&s_a, &offset, &bytes, buffer, &id); *req_id = id; return retcode; } int SF_Wait(int *req_id) { Integer id = (Integer)*req_id; int retcode = sf_wait_(&id); *req_id = id; return retcode; } int SF_Waitall(int *list, int num) { Integer *copy = (Integer*)malloc(num * sizeof(Integer)); Integer inum = num; int retcode; int i; for (i=0; i #endif #include "sf.h" #include "sff2c.h" #include "farg.h" #define MAX_NAME 256 static char cname[MAX_NAME+1]; Integer FATR sf_create_( #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *fname, SFsize_t *size_hard_limit, SFsize_t *size_soft_limit, SFsize_t *req_size, Integer *handle, int len #else char *fname, int len, SFsize_t *size_hard_limit, SFsize_t *size_soft_limit, SFsize_t *req_size, Integer *handle #endif ) { ga_f2cstring(fname, len, cname, MAX_NAME); return sfi_create(cname, size_hard_limit, size_soft_limit, req_size, handle); } Integer FATR sf_create_suffix_( #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS char *fname, SFsize_t *size_hard_limit, SFsize_t *size_soft_limit, SFsize_t *req_size, Integer *handle, Integer *suffix, int len #else char *fname, int len, SFsize_t *size_hard_limit, SFsize_t *size_soft_limit, SFsize_t *req_size, Integer *handle, Integer *suffix #endif ) { ga_f2cstring(fname, len, cname, MAX_NAME); return sfi_create_suffix(cname, size_hard_limit, size_soft_limit, req_size, handle, suffix); } /*****************************************************************************/ void FATR sf_errmsg_(Integer *code, char *msg, int msglen) { char buf[80]; sfi_errmsg((int) *code, buf); (void) ga_c2fstring(buf, msg, msglen); } ga-5.9.2/pario/sf/sff2c.h000066400000000000000000000047171500715745200150330ustar00rootroot00000000000000#ifndef SFF2C_H_ #define SFF2C_H_ #define sf_close_ F77_FUNC_(sf_close,SF_CLOSE) #define sf_create_ F77_FUNC_(sf_create,SF_CREATE) #define sf_create_suffix_ F77_FUNC_(sf_create_suffix,SF_CREATE_SUFFIX) #define sf_destroy_ F77_FUNC_(sf_destroy,SF_DESTROY) #define sf_errmsg_ F77_FUNC_(sf_errmsg,SF_ERRMSG) #define sf_open_ F77_FUNC_(sf_open,SF_OPEN) #define sf_read_ F77_FUNC_(sf_read,SF_READ) #define sf_rwtor_ F77_FUNC_(sf_rwtor,SF_RWTOR) #define sf_waitall_ F77_FUNC_(sf_waitall,SF_WAITALL) #define sf_wait_ F77_FUNC_(sf_wait,SF_WAIT) #define sf_write_ F77_FUNC_(sf_write,SF_WRITE) #define sf_fsync_ F77_FUNC_(sf_fsync,SF_FSYNC) extern Integer sfi_create(char *fname, SFsize_t *size_hard_limit, SFsize_t *size_soft_limit, SFsize_t *req_size, Integer *handle); extern Integer sfi_create_suffix(char *fname, SFsize_t *size_hard_limit, SFsize_t *size_soft_limit, SFsize_t *req_size, Integer *handle, Integer *suffix); extern void sfi_errmsg(int code, char *msg); extern Integer FATR sf_close_(Integer *s_a); extern Integer FATR sf_destroy_(Integer *s_a); extern Integer FATR sf_open_(Integer *s_a); extern Integer FATR sf_read_(Integer *s_a, SFsize_t *offset, SFsize_t *bytes, char *buffer, Integer *req_id); extern Integer FATR sf_rwtor_(Integer *s_a); extern Integer FATR sf_waitall_(Integer *list, Integer *num); extern Integer FATR sf_wait_(Integer *req_id); extern Integer FATR sf_write_(Integer *s_a, SFsize_t *offset, SFsize_t *bytes, char *buffer, Integer *req_id); extern Integer FATR sf_fsync_(Integer *s_a); #ifdef F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS extern Integer sf_create(char *fname, SFsize_t *size_hard_limit, SFsize_t *size_soft_limit, SFsize_t *req_size, Integer *handle, int len); extern Integer sf_create_suffix(char *fname, SFsize_t *size_hard_limit, SFsize_t *size_soft_limit, SFsize_t *req_size, Integer *handle, Integer *suffix, int len); extern void sf_errmsg(Integer *code, char *msg, int len); #else /* F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS */ extern Integer sf_create(char *fname, int len, SFsize_t *size_hard_limit, SFsize_t *size_soft_limit, SFsize_t *req_size, Integer *handle); extern Integer sf_create_suffix(char *fname, int len, SFsize_t *size_hard_limit, SFsize_t *size_soft_limit, SFsize_t *req_size, Integer *handle, Integer *suffix); extern void sf_errmsg(Integer *code, char *msg, int len); #endif /* F2C_HIDDEN_STRING_LENGTH_AFTER_ARGS */ #endif /* SFF2C_H_ */ ga-5.9.2/pario/sf/shared.files.c000066400000000000000000000176241500715745200163730ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** @file * $Id: shared.files.c,v 1.14 2002-11-09 06:05:40 sohirata Exp $ * * DISCLAIMER * * This material was prepared as an account of work sponsored by an * agency of the United States Government. Neither the United States * Government nor the United States Department of Energy, nor Battelle, * nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR * ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, * COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, * SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT * INFRINGE PRIVATELY OWNED RIGHTS. * * * ACKNOWLEDGMENT * * This software and its documentation were produced with United States * Government support under Contract Number DE-AC06-76RLO-1830 awarded by * the United States Department of Energy. The United States Government * retains a paid-up non-exclusive, irrevocable worldwide license to * reproduce, prepare derivative works, perform publicly and display * publicly by or for the US Government, including the right to * distribute to other US Government contractors. */ #if HAVE_STRING_H # include #endif #include "ga.h" #include "elio.h" #include "sf.h" #include "sff2c.h" #define _max_shared_files 100 #define SF_OFFSET 3000 #define SF_FAIL (Integer)1 typedef struct{ Integer handle; Integer actv; SFsize_t soft_size; SFsize_t hard_size; Fd_t fd; char fname[200]; } SF_t; SF_t SF[_max_shared_files]; #include "coms.h" /******************** internal macros ********************************/ #define sfi_check_handleM(_handle, msg)\ {\ if(_handle+SF_OFFSET >=_max_shared_files||_handle+SF_OFFSET<0) { \ fprintf(stderr,"%s, %ld --",msg,(long) _max_shared_files); \ ERROR("invalid SF handle",(int)_handle); \ } \ if( SF[(_handle+SF_OFFSET)].actv == 0) { \ fprintf(stderr,"%s:",msg); \ ERROR("disk array not active",(int)_handle); \ } \ } /*********************************************************************/ Integer sfi_get_handle() { Integer sf_handle =-1, candidate = 0; do{ if(!SF[candidate].actv){ sf_handle=candidate; SF[candidate].actv =1; } candidate++; }while(candidate < _max_shared_files && sf_handle == -1); return(sf_handle); } void sfi_release_handle(Integer *handle) { SF[*handle+SF_OFFSET].actv =0; *handle = 0; } Integer sfi_create(char *fname, SFsize_t *size_hard_limit, SFsize_t *size_soft_limit, SFsize_t *req_size, Integer *handle) { Integer hndl; SYNC(); /*** Get next free SF handle ***/ if( (hndl = sfi_get_handle()) == -1) ERROR("sf_create: too many shared files ",(int)_max_shared_files); *handle = hndl - SF_OFFSET; /*generate file name(s) */ sprintf(SF[hndl].fname,"%s.%d",fname, (int)hndl); if (ME() == 0) SF[hndl].fd = elio_open(SF[hndl].fname,ELIO_RW, ELIO_SHARED); SYNC(); if (ME() != 0) SF[hndl].fd = elio_open(SF[hndl].fname,ELIO_RW, ELIO_SHARED); if(SF[hndl].fd==NULL) ERROR("sf_create: could not open file",0); if(SF[hndl].fd->fd==-1) ERROR("sf_create: descriptor -1",0); SF[hndl].soft_size = *size_soft_limit; SF[hndl].hard_size = *size_hard_limit; SYNC(); return(ELIO_OK); } /** * takes an additional input suffix which is appended to the filename instead * of the handle. This will give users complete control over the filename as it * is written to and read from disk. */ Integer sfi_create_suffix(char *fname, SFsize_t *size_hard_limit, SFsize_t *size_soft_limit, SFsize_t *req_size, Integer *handle, Integer *suffix) { Integer hndl; SYNC(); /*** Get next free SF handle ***/ if( (hndl = sfi_get_handle()) == -1) { ERROR("sf_create_suffix: too many shared files ", (int)_max_shared_files); } *handle = hndl - SF_OFFSET; /*generate file name(s) */ sprintf(SF[hndl].fname,"%s.%d",fname, (int) *suffix); if (ME() == 0) SF[hndl].fd = elio_open(SF[hndl].fname,ELIO_RW, ELIO_SHARED); SYNC(); if (ME() != 0) SF[hndl].fd = elio_open(SF[hndl].fname,ELIO_RW, ELIO_SHARED); if(SF[hndl].fd==NULL) ERROR("sf_create_suffix: could not open file",0); if(SF[hndl].fd->fd==-1) ERROR("sf_create_suffix: descriptor -1",0); SF[hndl].soft_size = *size_soft_limit; SF[hndl].hard_size = *size_hard_limit; SYNC(); return(ELIO_OK); } /*****************************************************************************/ Integer FATR sf_destroy_(Integer *s_a /**< [in] SF handle */) { Integer handle = *s_a+SF_OFFSET; SYNC(); sfi_check_handleM(*s_a,"sf_delete"); elio_close(SF[handle].fd); /* fix from Peter Knowles */ SYNC(); /* this sync is unnecessary under Unix */ if(ME() == 0)elio_delete(SF[handle].fname); sfi_release_handle(s_a); SYNC(); return(ELIO_OK); } /** * close rw file and open as read only */ Integer FATR sf_rwtor_(Integer *s_a /**< [in] SF handle */) { Integer handle = *s_a+SF_OFFSET; elio_close(SF[handle].fd); SF[handle].fd = elio_open(SF[handle].fname,ELIO_R, ELIO_SHARED); if(SF[handle].fd==NULL) ERROR("sf_open: could not open file",0); if(SF[handle].fd->fd==-1) ERROR("sf_open: descriptor -1",0); return(ELIO_OK); } /** * open */ Integer FATR sf_open_(Integer *s_a /**< [in] SF handle */) { Integer handle = *s_a+SF_OFFSET; SF[handle].fd = elio_open(SF[handle].fname,ELIO_RW, ELIO_SHARED); if(SF[handle].fd==NULL) ERROR("sf_open: could not open file",0); if(SF[handle].fd->fd==-1) ERROR("sf_open: descriptor -1",0); return(ELIO_OK); } /** * close */ Integer FATR sf_close_(Integer *s_a) { Integer handle = *s_a+SF_OFFSET; elio_close(SF[handle].fd); return(ELIO_OK); } /** * fsync */ Integer FATR sf_fsync_(Integer *s_a) { Integer handle = *s_a+SF_OFFSET; elio_fsync(SF[handle].fd); return(ELIO_OK); } /** * asynchronous write to shared file */ Integer FATR sf_write_(Integer *s_a, SFsize_t *offset, SFsize_t *bytes, char *buffer, Integer *req_id) { Integer handle = *s_a+SF_OFFSET; int status; io_request_t id; sfi_check_handleM(*s_a,"sf_write"); status = elio_awrite(SF[handle].fd, (Off_t)*offset, buffer, (Size_t)*bytes, &id); *req_id = (Integer)id; return((Integer)status); /* status = elio_write(SF[handle].fd, (Off_t)*offset, buffer,(Size_t)*bytes); *req_id = (Integer)ELIO_DONE; if(status != (int)*bytes) return((Integer)SF_FAIL); else return((Integer)ELIO_OK); */ } /** * asynchronous read from shared file */ Integer FATR sf_read_(Integer *s_a, SFsize_t *offset, SFsize_t *bytes, char *buffer, Integer *req_id) { Integer handle = *s_a+SF_OFFSET; int status; io_request_t id; sfi_check_handleM(*s_a,"sf_read"); status = elio_aread(SF[handle].fd, (Off_t)*offset, buffer, (Size_t)*bytes, &id); *req_id = (Integer)id; return((Integer)status); } /** * block calling process until I/O operation completes */ Integer FATR sf_wait_(Integer *req_id) { int status; io_request_t id = (io_request_t) *req_id; status = elio_wait(&id); *req_id = (Integer)id; return((Integer)status); } /** * block calling process until all I/O operations associated * with id in the list complete */ Integer FATR sf_waitall_(Integer *list, Integer *num) { int i; int status, fail=0; for(i=0;i<*num;i++){ io_request_t id = (io_request_t) list[i]; status = elio_wait(&id); if(status != ELIO_OK) fail++; list[i] = (Integer)id; } if (fail)return((Integer)SF_FAIL); else return((Integer)ELIO_OK); } /** * retrieve message associated with error code */ void sfi_errmsg(int code, char *msg) { if(code==SF_FAIL) (void) strcpy(msg, "SF operation failed"); else elio_errmsg(code, msg); } ga-5.9.2/pario/sf/test.F000066400000000000000000000064141500715745200147410ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif c c FNAME - filename for test program c #define BASE_NAME 'shared.file' #ifdef HPIODIR # define FNAME HPIODIR//BASE_NAME #else # define FNAME BASE_NAME #endif #define SYNCH ga_sync program sftest c implicit none #include "global.fh" #include "sf.fh" c integer size integer dimnsn integer maxid parameter(maxid=5) ! max number of outstanding I/O requests parameter(dimnsn=8192) real*8 buffer(dimnsn,maxid) ! need buffering for all maxid requests double precision tt0,ttw, ttr c integer stack, heap integer idlist(maxid), curid character*80 errmsg c integer me,nproc,handle,i,start,end,j, rc, chunk c #include "mp3.fh" call ga_initialize() c curid = 0 me = ga_nodeid() nproc = ga_nnodes() size = maxid*dimnsn*nproc c rc = sf_create(FNAME, $ dble(16*size),dble(8*size),dble(8*dimnsn),handle) c call SYNCH chunk = (size+nproc-1)/nproc start = me*chunk+1 end = min((start+chunk-1),size) tt0 = MP_TIMER() c print *,'me=',me,'writing:', start, end c everybody writes chunk of data if(start.le.end) then do i = start, end,dimnsn do j = 1, min(dimnsn,(end-i+1)) buffer(j,curid+1) = dble(i+j-1) enddo if(curid .eq. maxid)then rc=sf_waitall(idlist,maxid) curid = 0 endif curid = curid+1 rc = sf_write(handle, 8*dble(i-1), & 8*dble(min(dimnsn,(end-i+1))), buffer(1,curid), & idlist(curid)) if (rc.ne.0)call ga_error('write failed',rc) enddo endif c call sf_fsync(handle) rc=sf_waitall(idlist,curid) if(rc.ne.0)call ga_error('waitall failed',rc) curid = 0 ttw = MP_TIMER() -tt0 c call ga_dgop(88,ttw,1,'max') call SYNCH c c c everybody reads different chunk of data start = (nproc-me-1)*chunk+1 end = min((start+chunk-1),size) print *,'me=',me,'reading:', start, end tt0 = MP_TIMER() do i = start,end,dimnsn c read and test data chunk by chunk rc = sf_read(handle, 8*dble(i-1), . 8*dble(min(dimnsn,(end-i+1))), buffer, idlist(1)) if (rc.ne.0)then call sf_errmsg(rc,errmsg) print *,'read at offset ',8*dble(i-1),' failed:',errmsg call ga_error('read failed',rc) endif rc=sf_wait(idlist(1)) if (rc.ne.0)call ga_error('wait failed',rc) c do j = 1,min(dimnsn,(end-i+1)) if(buffer(j,1).ne.dble(i+j-1)) then print *, me,buffer(j,1), i+j-1,i stop 'test failed' endif enddo enddo ttr = MP_TIMER() -tt0 c call ga_dgop(88,ttr,1,'max') call SYNCH c rc = sf_destroy(handle) if(me.eq.0)then write(*,*)' ' write(*,*)'test passed ', 8*maxid*dimnsn,' bytes' write(*,*) 8.0e-6*maxid*dimnsn/ttw,' MB/s write rate' write(*,*) 8.0e-6*maxid*dimnsn/ttr,' MB/s read rate' write(*,*)' ' endif c call SYNCH call ga_terminate call MP_FINALIZE() end ga-5.9.2/support/000077500000000000000000000000001500715745200136405ustar00rootroot00000000000000ga-5.9.2/support/INSTALL000066400000000000000000000362761500715745200147070ustar00rootroot00000000000000Installation Instructions ************************* Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. Basic Installation ================== Briefly, the shell commands `./configure; make; make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. Some packages provide this `INSTALL' file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in *note Makefile Conventions: (standards)Makefile Conventions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 4. Type `make install' to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the `make install' phase executed with root privileges. 5. Optionally, type `make installcheck' to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior `make install' required root privileges, verifies that the installation completed correctly. 6. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 7. Often, you can also type `make uninstall' to remove the installed files again. In practice, not all packages have tested that uninstallation works correctly, even though it is required by the GNU Coding Standards. 8. Some packages, particularly those that use Automake, provide `make distcheck', which can by used by developers to test that all other targets like `make install' and `make uninstall' work correctly. This target is generally not run by end users. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. This is known as a "VPATH" build. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. On MacOS X 10.5 and later systems, you can create libraries and executables that work on multiple system types--known as "fat" or "universal" binaries--by specifying multiple `-arch' options to the compiler but only a single `-arch' option to the preprocessor. Like this: ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ CPP="gcc -E" CXXCPP="g++ -E" This is not guaranteed to produce working output in all cases, you may have to build one architecture at a time and combine the results using the `lipo' tool if you have problems. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX', where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of `${prefix}', so that specifying just `--prefix' will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to `configure'; however, many packages provide one or both of the following shortcuts of passing variable assignments to the `make install' command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, `make install prefix=/alternate/directory' will choose an alternate location for all directory configuration variables that were expressed in terms of `${prefix}'. Any directories that were specified during `configure', but not in terms of `${prefix}', must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the `DESTDIR' variable. For example, `make install DESTDIR=/alternate/directory' will prepend `/alternate/directory' before all installation names. The approach of `DESTDIR' overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of `${prefix}' at `configure' time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Some packages offer the ability to configure how verbose the execution of `make' will be. For these packages, running `./configure --enable-silent-rules' sets the default to minimal output, which can be overridden with `make V=1'; while running `./configure --disable-silent-rules' sets the default to verbose, which can be overridden with `make V=0'. Particular systems ================== On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC is not installed, it is recommended to use the following options in order to use an ANSI C compiler: ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" and if that doesn't work, install pre-built binaries of GCC for HP-UX. On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended to try ./configure CC="cc" and if that doesn't work, try ./configure CC="cc -nodtk" On Solaris, don't put `/usr/ucb' early in your `PATH'. This directory contains several dysfunctional programs; working variants of these programs are available in `/usr/bin'. So, if you need `/usr/ucb' in your `PATH', put it _after_ `/usr/bin'. On Haiku, software installed for all users goes in `/boot/common', not `/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf bug. Until the bug is fixed you can use this workaround: CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of all of the options to `configure', and exit. `--help=short' `--help=recursive' Print a summary of the options unique to this package's `configure', and exit. The `short' variant lists options used only in the top level, while the `recursive' variant lists options also present in any nested packages. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `--prefix=DIR' Use DIR as the installation prefix. *note Installation Names:: for more details, including other options available for fine-tuning the installation locations. `--no-create' `-n' Run the configure checks, but stop before creating any output files. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. ga-5.9.2/support/README_AUTOTOOLS.md000066400000000000000000000064001500715745200165700ustar00rootroot00000000000000# Autotools Notes These notes are intended to describe the autotools build system and to assist developers in modifying and maintaining it. ## Tutorial There are many great tutorials on how to use the autotools to configure and make software. Please refer to them. ## configure.ac We make all attempts to not commit generated files to our git repo. This includes files generated by the autotools. The important, non-generated files are the [configure.ac](configure.ac) and [Makefile.am](Makefile.am) templates for autoconf and automake. In addition, the [m4](m4) directory contains all of the custom autoconf commands that appear in the configure.ac template. configure.ac is relatively well-organized and broken into sections via comments. Perhaps the trickiest hack is the unwrapping of the MPI compiler wrappers. Unfortunately, libtool at times relies on comparing the name of the compiler against a known list instead of testing for behavior. That means the MPI compiler wrappers, e.g., mpicc, are unknown to libtool. The custom m4 function `GA_MPI_UNWRAP` in [m4/ga_mpi_unwrap.m4](m4/ga_mpi_unwrap.m4) attempts to use the `-show` argument to display the underlying compiler. If it fails, it then compares the output of all known compilers' `--version` commands against the MPI compiler. We hack certain libtool variables to test against the unwrapped compiler and then replace the results again with the compiler wrappers. ## Makefile.am [Makefile.am](Makefile.am) is relatively well-organized and broken into sections. We preferred to declare all variables at the top of the file and later append to them using `+=`. We do not use recursive make; we favor using a single top-level Makefile. ## autogen.sh [autogen.sh](autogen.sh) runs the auxiliary script [travis/install-autotools.sh](travis/install-autotools.sh). Next, the autogen.sh script will run `autoreconf -vif` to generate the various [configure](configure) scripts as well as the Makefile.in templates. Finally, autogen.sh applies some patches to the generated build files. See autogen.sh file for details. ## install-autotools.sh [travis/install-autotools.sh](travis/install-autotools.sh) downloads and installs specific versions of autoconf (2.69), automake (1.11.6), and libtool (2.4.6). These versions are known to produce suitable build files, e.g., configure, Makefile.in. This script is in the [travis](travis) subdirectory because this script is intented to be run by the travis continous integration service, however the script is generally useful for developers to run as part of the autogen.sh script. The primary reason for specific versions is that we hacked our Makefile.am template to create a custom test suite. We copied code that was generated by automake 1.11.6 and pasted it directly into our Makefile.am template to override the built-in test suite features of automake. This was to let our applications launch using the environment variable MPIEXEC, detected during the configure step. Additionally, we split our test suite into 'serial' (no MPI) tests as well as 'parallel' (MPI) tests, so these different classes of tests required different launch mechanisms. Our reliance on this automake 1.11.6 code caused us to be dependent on this version, all so we could type `make check` from the command line. ga-5.9.2/support/README_RELEASES.md000066400000000000000000000070251500715745200164060ustar00rootroot00000000000000# Release Process for GA ## Gitflow GA tries to follow the gitflow workflow. A good tutorial on gitflow is [here](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow). Releases are simply tagged commits on the 'master' branch. All development occurs in the 'develop' branch. New features are developed in feature branches that started from a branch of 'develop'. Complete features are merged back into develop. ## The Release Branch Once enough new features exist, the develop branch is branched into a release branch. No new features should be added at this point. Once the release branch is ready, it gets merged into both 'master' and 'develop'. The 'master' branch is tagged with the new release number. A new tarball is created for the tag and added to the github release. These steps are detailed next. ### Create the Branch Starting with the develop branch, create a new release branch. ``` git checkout develop git checkout -b release/0.1.0 # <-- use the intended version number ``` ### Update the Version and CHANGELOG.md The version number is in [global/src/gacommon.h](global/src/gacommon.h). The [CHANGELOG.md](CHANGELOG.md) keeps track of user-visible changes. Follow the formatting guidelines in the CHANGELOG.md file. ### Merge Back You must merge the release branch back into develop and master. If new features were added to develop, you might need to work through merge conflicts. The merge into master should be a fast forward merge and without conflicts. ``` git checkout develop git merge release/0.1.0 ``` When merging master, you can also tag the release. You must push the tag in addition to pushing the merged branch back to the origin. ``` git checkout master git merge release/0.1.0 git push # pushes the merged master branch to origin git tag v0.1.0 git push --tags # pushes the tag to origin, creates a github draft release ``` ### Finishing the GitHub Release Pushing a tag creates a draft release. You must edit the draft release to finish the process. Select the tag you just created as the tag to build the release around. Copy-and-paste the latest changes from the CHANGELOG.md file from the earlier release step to create the release notes. GitHub automatically creates zip and tar.gz archives. However, these do not contain the configure script. 1. Download the tar.gz archive. 2. Untar the archive. 3. Run autogen.sh inside the new directory. This creates the missing generated files, e.g., configure, Makefile.in. 4. Remove the created autotools directory. We don't want to bundle the autotools with our releases. 5. Tar the archive back up. 6. Got to the releases pages and select the Tags button on the left hand side of the page. Then go select the most recent tag (this should correspond to the release number). 7. Select the 'Create release from tag' button. This will bring up a page that allows you to edit the release. 8. Title the release. Use the format vX.X.X where X.X.X represents the major version, minor version and patch number. 9. Copy the current CHANGELOG.md notes into the window 'Describe this release'. 10. Click on the bar 'Attach binaries by dropping them here or selecting them' and upload the tar file created in step 5. 11. Click on 'Publish release'. ### Miscellaneous You need to update the Global Arrays web page at [http://hpc.pnl.gov/globalarrays/](http://hpc.pnl.gov/globalarrays/). The files for this page can be found at /msrc/webroot/hpc/globalarrays/ on the AFS filesystem at PNNL. Relevant files for the current release are index.shtml and shared/rightnav.inc ga-5.9.2/support/README_SICM.md000066400000000000000000000013541500715745200157350ustar00rootroot00000000000000# Building GA with SICM SICM was downloaded from https://github.com/lanl/SICM.git In order to build following modules were used. Some other combination of compilers and libraries may also work. - numactl-2.0.11 - libpfm4-4.10.1 - hwloc-1.11.9 - openmpi-3.1.0 - llvm With above modules loaded, SICM was configured as follows: The command:: ``` $ ./configure --with-jemalloc="PATH_TO/jemalloc/install" --prefix="/PATH_TO/SICM/install" ``` Then ga was configured using following command: The command:: ``` $ ./configure --with-mpi-pr=1 --prefix="/PATH_TO/GA/install" --x-includes="/PATH_TO/SICM/install/include" --x-libraries="/PATH_TO/SICM/install/lib:/PATH_TO/jemalloc/install/lib" CFLAGS="-DUSE_SICM=1 -I/PATH_TO/SICM/install/include" ``` ga-5.9.2/tcgmsg/000077500000000000000000000000001500715745200134105ustar00rootroot00000000000000ga-5.9.2/tcgmsg/capi.c000066400000000000000000000067471500715745200145060ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** @file * This header file declares the public C API to tcgmsg. */ #include #include #include "sndrcv.h" void tcg_alt_pbegin(int *argc, char **argv[]) { tcgi_pbegin(*argc, *argv); } void tcg_brdcst(long type, void *buf, long lenbuf, long originator) { long atype = type; long alenbuf = lenbuf; long aoriginator = originator; BRDCST_(&atype, buf, &alenbuf, &aoriginator); } void tcg_dgop(long type, double *x, long n, char *op) { long atype = type; long an = n; double *ax; int i; ax = (double*)malloc(n * sizeof(double)); for (i=0; i template.p echo "# PROGRAM with the name of the program." >> template.p echo "# Add other hosts and processes as desired." >> template.p IF NEXTNOEXT echo "$$LOGNAME `hostname` 1 `pwd`/PROGRAM /tmp" >> template.p echo "$$LOGNAME `hostname` 1 `pwd`/PROGRAM /tmp" >> template.p echo "$$LOGNAME `hostname` 1 `pwd`/PROGRAM /tmp" >> template.p echo "$$LOGNAME `hostname` 1 `pwd`/PROGRAM /tmp" >> template.p ELSE echo "`whoami` `hostname` 4 `pwd`/PROGRAM /tmp" >> template.p ENDIF ga-5.9.2/tcgmsg/examples/README000066400000000000000000000125661500715745200161200ustar00rootroot00000000000000TCGMSG Examples =============== These example programs are realisitic (?) models of actual applications or algorithms from chemical-physics. They should make cleanly once the Makefile has been appropriately modified (which is done automatically for all supported machines). Serial and shared-memory parallel (and possibly CM and Linda) versions are also available but not included here. The programs may be run using the csh script demo in this directory The script takes a single argument which is the name of the desired demo (scf, md, mc, jacobi, grid). The script uses a template PROCGRP file (template.p) to generate the actual PROCGRP file used ... its makes a default file if one does not exist ... look in that for details. Self Consistent Field (scf) --------------------------- This SCF code is a cleaned up and much enhanced version of the one in Szabo and Ostlund. It uses distributed primitive 1s gaussian functions as a basis (thus emulating use of s,p,... functions) and computes integrals to essentially full accuracy. It is a direct SCF (integrals are computed each iteration using the Schwarz inequality for screeing). An atomic denisty is used for a starting guess. Damping and level shifting are used to aid convergence. Rather than complicate the program with code for parsing input the include file 'cscf.h' and block data file 'blkdata.f' contain all the data and thus there are three versions, one for each of the available problem sizes. The three sizes correpsond to 15 basis functions (Be), 30 basis functions (Be2) and 60 basis functions (tetrahedral Be4). [In addition to these three cases there are files for 60, 120 and 240 functions, which are not built by default (type 'make extra' for these). These are 4, 8 and 16 Be atoms, respectively, arranged in a line.] The O(N**4) step has been parallelized with the assumption that each process can hold all of the density and fock matrices which is reasonable for up to O(1000) basis functions on most workstations networks and many MIMD machines (e.g. iPSC-i860). The work is dynamically load-balanced, with tasks comprising 10 sets of integrals (ij|**) (see TWOEL() and NXTASK() in scf.f). The work of O(N**3) has not been parallelized, but has been optimized to use BLAS and a tweaked Jacobi diagonalizer with dynamic threshold selection. Molecular Dynamics (md) ----------------------- This program bounces a few thousand argon atoms around in a box with periodic boundary conditions. Pairwise interactions (Leonard-Jones) are used with a simple integration of the Newtonian equations of motion. This program is derived from the serial code of Deiter Heerman, but many modifications have been made. Prof. Frank Harris has a related FORTRAN 9X Connection Machine version. The O(N) work constructing the forces has been parallelized, as has the computation of the pair distribution function. The neighbour list is computed in parallel every 20 steps with a simple static decomposition. This then drives the parallelization of the forces computation. To make the simulation bigger increase the value of mm in the parameter statement at the top of md.f (mm=8 gives 2048 particles, mm=13 gives 8878). Each particle interacts with about 80 others, and the neighbor list is computed for about 130 neighbors to allow for movement before it is updated. Monte Carlo (mc) ---------------- This code evaluates the energy of the simplest explicitly correlated electronic wavefunction for the He atom ground state using a variational monte-carlo method without importance sampling. It is completely boringly parallel and for realistic problem sizes gives completely linear speed-ups for several hunderd processes. You have to give it the no. of moves to equilibrate the system for (neq) and the no. of moves to compute averages over (nstep). Apropriate values for a very short run are 200 and 500 respectively. Jacobi iterative linear equation solver (jacobi) ------------------------------------------------ Uses a naive jacobi iterative algorithm to solve a linear equation. This algorithm is not applicable to real linear equations (sic) and neither is it the most parallel algorithm available. The code as implemented here gets 780+ MFLOP on a 128 node iPSC-i860 ... a paltry 30% efficiency, but it is not hard to improve upon either. All the time is spent in a large matrix vector product which is statically distributed across the processes. You need to give it the matrix dimension (pick as big as will fit in memory). Solution of Laplace's equation on a 2-D grid (grid) --------------------------------------------------- Solve Laplace's eqn. on a 2-D square grid subject to b.c.s on the boundary. Use 5 point discretization of the operator and a heirarchy of grids with red/black gauss seidel w-relaxation. This is not the most efficient means of solving this equation (probably should use a fast-poisson solver) but it provides a 'real-world' example of spatial decomposition determining the parallel decomposition. It is also the only example of a full application in C that is included here. If the code is compiled with -DPLOT and run with the option '-plot XXX', where XXX is one of 'value', 'residual' or 'error', then grids are dumped at intervals to the file 'plot' (in the directory of process zero). This file may be displayed with the X11-R4/5 program xpix. Xpix is not built automatically and must be extracted and built from the shar file in this directory. ga-5.9.2/tcgmsg/examples/blkdat120lin.f000066400000000000000000000061261500715745200175710ustar00rootroot00000000000000 block data C$Id: blkdat120lin.f,v 1.2 1995-02-02 23:23:46 d3g681 Exp $ implicit double precision (a-h, o-z) include 'cscf.h' c c initalize data in common ... clumsy but avoids code to read in data c c line of eight be atoms 4.0 a.u. apart with 120 orbitals c c have 9s functions on each center and simulate p's by having c s function at +- 1 in each of x, y, z c data ax / 0.0d0, 4.0d0, 8.0d0, 12.0d0, 16.0d0, $ 20.0d0, 24.0d0, 28.0d0/ data ay / 8*0.0d0/ data az / 8*0.0d0/ data q /8*4.0d0/, enrep/54.9714285664/ c data x /9*0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, $ 9*4.0d0, 5.6d0, 2.4d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, $ 9*8.0d0, 9.6d0, 6.4d0, 8.0d0, 8.0d0, 8.0d0, 8.0d0, $ 9*12.0d0,13.6d0, 10.4d0,12.0d0,12.0d0,12.0d0,12.0d0, $ 9*16.0d0,17.6d0, 14.4d0,16.0d0,16.0d0,16.0d0,16.0d0, $ 9*20.0d0,21.6d0, 18.4d0,20.0d0,20.0d0,20.0d0,20.0d0, $ 9*24.0d0,25.6d0, 22.4d0,24.0d0,24.0d0,24.0d0,24.0d0, $ 9*28.0d0,29.6d0, 26.4d0,28.0d0,28.0d0,28.0d0,28.0d0/ data y /9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0/ data z /9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0/ data expnt /1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0/ end ga-5.9.2/tcgmsg/examples/blkdat15.f000066400000000000000000000014001500715745200167770ustar00rootroot00000000000000 block data C$Id: blkdat15.f,v 1.2 1995-02-02 23:23:47 d3g681 Exp $ implicit double precision (a-h, o-z) include 'cscf.h' c c initalize data in common ... clumsy but avoids code to read in data c c one be atom with 15 orbitals c c have 9s functions on each center and simulate p's by having c s function at +- 1 in each of x, y, z c data ax /0.0d0/, ay /0.0d0/, az /0.0d0/, q /4.0d0/, enrep/0.0d0/ data x /9*0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0/ data y /9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0/ data z /9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0/ data expnt /1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0/ c end ga-5.9.2/tcgmsg/examples/blkdat240lin.f000066400000000000000000000132731500715745200175750ustar00rootroot00000000000000 block data C$Id: blkdat240lin.f,v 1.2 1995-02-02 23:23:48 d3g681 Exp $ implicit double precision (a-h, o-z) include 'cscf.h' c c initalize data in common ... clumsy but avoids code to read in data c c line of sixteen be atoms 4.0 a.u. apart with 240 orbitals c c have 9s functions on each center and simulate p's by having c s function at +- 1 in each of x, y, z c data ax / 0.0d0, 4.0d0, 8.0d0, 12.0d0, $ 16.0d0, 20.0d0, 24.0d0, 28.0d0, $ 32.0d0, 36.0d0, 40.0d0, 44.0d0, $ 48.0d0, 52.0d0, 56.0d0, 60.0d0/ data ay / 16*0.0d0/ data az / 16*0.0d0/ data q /16*4.0d0/, enrep/152.3666555456/ c data x /9*0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, $ 9*4.0d0, 5.6d0, 2.4d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, $ 9*8.0d0, 9.6d0, 6.4d0, 8.0d0, 8.0d0, 8.0d0, 8.0d0, $ 9*12.0d0,13.6d0, 10.4d0,12.0d0,12.0d0,12.0d0,12.0d0, $ 9*16.0d0,17.6d0, 14.4d0,16.0d0,16.0d0,16.0d0,16.0d0, $ 9*20.0d0,21.6d0, 18.4d0,20.0d0,20.0d0,20.0d0,20.0d0, $ 9*24.0d0,25.6d0, 22.4d0,24.0d0,24.0d0,24.0d0,24.0d0, $ 9*28.0d0,29.6d0, 26.4d0,28.0d0,28.0d0,28.0d0,28.0d0, $ 9*32.0d0,33.6d0, 30.4d0,32.0d0,32.0d0,32.0d0,32.0d0, $ 9*36.0d0,37.6d0, 34.4d0,36.0d0,36.0d0,36.0d0,36.0d0, $ 9*40.0d0,41.6d0, 38.4d0,40.0d0,40.0d0,40.0d0,40.0d0, $ 9*44.0d0,45.6d0, 42.4d0,44.0d0,44.0d0,44.0d0,44.0d0, $ 9*48.0d0,49.6d0, 46.4d0,48.0d0,48.0d0,48.0d0,48.0d0, $ 9*52.0d0,53.6d0, 50.4d0,52.0d0,52.0d0,52.0d0,52.0d0, $ 9*56.0d0,57.6d0, 54.4d0,56.0d0,56.0d0,56.0d0,56.0d0, $ 9*60.0d0,61.6d0, 58.4d0,60.0d0,60.0d0,60.0d0,60.0d0/ data y /9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0/ data z /9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0/ data expnt /1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0/ end ga-5.9.2/tcgmsg/examples/blkdat30.f000066400000000000000000000022111500715745200167750ustar00rootroot00000000000000 block data C$Id: blkdat30.f,v 1.2 1995-02-02 23:23:49 d3g681 Exp $ implicit double precision (a-h, o-z) include 'cscf.h' c c initalize data in common ... clumsy but avoids code to read in data c c two be atoms 4.0 a.u. apart with 30 orbitals c c have 9s functions on each center and simulate p's by having c s function at +- 1 in each of x, y, z c data ax /0.0d0, 0.0d0/, ay /0.0d0, 0.0d0/, az /0.0d0, 4.0d0/, $ q /4.0d0, 4.0d0/, enrep/4.0d0/ c nbfn=30 two atoms data x /9*0.0d0, 1.0d0, -1.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, $ 9*0.0d0, 1.0d0, -1.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0/ data y /9*0.0d0, 0.0d0, 0.0d0, 1.0d0, -1.0d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.0d0, -1.0d0, 0.0d0, 0.0d0/ data z /9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.0d0, -1.0d0, $ 9*4.0d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, 5.0d0, 3.0d0/ data expnt /1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0/ end ga-5.9.2/tcgmsg/examples/blkdat60.f000066400000000000000000000035701500715745200170110ustar00rootroot00000000000000 block data C$Id: blkdat60.f,v 1.2 1995-02-02 23:23:50 d3g681 Exp $ implicit double precision (a-h, o-z) include 'cscf.h' c c initalize data in common ... clumsy but avoids code to read in data c c four be atoms 4.0 a.u. apart (tetrahedron) with 60 orbitals c c have 9s functions on each center and simulate p's by having c s function at +- 1 in each of x, y, z c data ax /1.414214d0, 1.414214d0, -1.414214d0, -1.414214d0/ data ay / 0.0d0, 0.0d0, 2.0d0, -2.0d0/ data az / -2.0d0, 2.0d0, 0.0d0, 0.0d0/ data q /4*4.0d0/, enrep/24.0d0/ c data x /9* 1.414214d0, 2.414214d0, 0.414214d0, 4* 1.414214d0, $ 9* 1.414214d0, 2.414214d0, 0.414214d0, 4* 1.414214d0, $ 9*-1.414214d0,-0.414214d0,-2.414214d0, 4*-1.414214d0, $ 9*-1.414214d0,-0.414214d0,-2.414214d0, 4*-1.414214d0/ data y /9* 0.0d0, 0.0d0, 0.0d0, 1.0d0,-1.0d0, 0.0d0, 0.0d0, $ 9* 0.0d0, 0.0d0, 0.0d0, 1.0d0,-1.0d0, 0.0d0, 0.0d0, $ 9* 2.0d0, 2.0d0, 2.0d0, 3.0d0, 1.0d0, 2.0d0, 2.0d0, $ 9*-2.0d0,-2.0d0,-2.0d0,-1.0d0,-3.0d0,-2.0d0,-2.0d0/ data z /9* 2.0d0, 2.0d0, 2.0d0, 2.0d0, 2.0d0, 3.0d0, 1.0d0, $ 9*-2.0d0,-2.0d0,-2.0d0,-2.0d0,-2.0d0,-1.0d0,-3.0d0, $ 9* 0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.0d0,-1.0d0, $ 9* 0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.0d0,-1.0d0/ data expnt /1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0/ end ga-5.9.2/tcgmsg/examples/blkdat60lin.f000066400000000000000000000035221500715745200175110ustar00rootroot00000000000000 block data C$Id: blkdat60lin.f,v 1.2 1995-02-02 23:23:52 d3g681 Exp $ implicit double precision (a-h, o-z) include 'cscf.h' c c initalize data in common ... clumsy but avoids code to read in data c c line of four be atoms 4.0 a.u. apart with 60 orbitals c c have 9s functions on each center and simulate p's by having c s function at +- 1 in each of x, y, z c data ax / 0.0d0, 4.0d0, 8.0d0, 12.0d0/ data ay / 0.0d0, 0.0d0, 0.0d0, 0.0d0/ data az / 0.0d0, 0.0d0, 0.0d0, 0.0d0/ data q /4*4.0d0/, enrep/17.3333333333333d0/ c data x /9*0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, $ 9*4.0d0, 5.6d0, 2.4d0, 4.0d0, 4.0d0, 4.0d0, 4.0d0, $ 9*8.0d0, 9.6d0, 6.4d0, 8.0d0, 8.0d0, 8.0d0, 8.0d0, $ 9*12.0d0,13.6d0, 10.4d0,12.0d0,12.0d0,12.0d0,12.0d0/ data y /9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0, $ 9*0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, 0.0d0, 0.0d0/ data z /9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0, $ 9*0.0d0, 0.0d0, 0.0d0, 0.0d0, 0.0d0, 1.6d0, -1.6d0/ data expnt /1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0, $ 1741.0d0, 262.1d0, 60.33d0, 17.62d0, 5.933d0, 2.185d0, $ 0.859, 0.1806d0, 0.05835d0, 6*0.3d0/ end ga-5.9.2/tcgmsg/examples/cscf.h000066400000000000000000000032741500715745200163230ustar00rootroot00000000000000/*$Id: cscf.h,v 1.3 1995-02-02 23:23:54 d3g681 Exp $*/ c c include file defining common /cscf/ c c natom = no. of atoms (constant parameter) c nbfn = no. of basis functions (constant parameter) c nnbfn = nbfn*(nbfn+1)/2 (constant parameter) c nocc = no. of occupied orbitals (constant parameter) c mxiter = maximim no. of iterations(constant parameter) c tol = convergence criterion (constant parameter) c pi = a familiar constant (constant parameter) c tol2e = 2-e integral screening (constant parameter) c c the remainder is initialized in block data or in the c routine ininrm (rnorm and iky) c c enrep = nuclear repulsion energy c q(1:natom) = nuclear charge of atom c ax(1:natom) = x co-ordinate of atom c ay(1:natom) = y ... c az(1:natom) = z ... c x(1:nbfn) = x co-ordinate of basis function c y(1:nbfn) = y ... c z(1:nbfn) = z ... c expnt(1:nbfn)= exponent of gaussian c rnorm(1:nbfn)= normalization constant of gaussian c iky(1:nbfn) = iky(i) = i*(i-1)/2 to speed up fock build c icut1 = no. of successful ij 2-e screening tests c icut2 = no. of successful ijkl 2-e screening tests c icut3 = no. of 2-e integrals computed c parameter (natom = 1, nbfn = 15, nocc = 2, mxiter = 20) parameter (nnbfn = nbfn*(nbfn+1)/2, pi = 3.141592653589793d0) parameter (tol= 1.0d-4) parameter (tol2e=1.0d-7) c common /cscf/ $ enrep, q(natom), ax(natom), ay(natom), az(natom), $ x(nbfn), y(nbfn), z(nbfn), expnt(nbfn), rnorm(nbfn), $ iky(nbfn), icut1, icut2, icut3 c ga-5.9.2/tcgmsg/examples/cscf120lin.h000066400000000000000000000033021500715745200172410ustar00rootroot00000000000000/*$Id: cscf120lin.h,v 1.2 1995-02-02 23:23:55 d3g681 Exp $*/ c c include file defining common /cscf/ c c natom = no. of atoms (constant parameter) c nbfn = no. of basis functions (constant parameter) c nnbfn = nbfn*(nbfn+1)/2 (constant parameter) c nocc = no. of occupied orbitals (constant parameter) c mxiter = maximim no. of iterations(constant parameter) c tol = convergence criterion (constant parameter) c pi = a familiar constant (constant parameter) c tol2e = 2-e integral screening (constant parameter) c c the remainder is initialized in block data or in the c routine ininrm (rnorm and iky) c c enrep = nuclear repulsion energy c q(1:natom) = nuclear charge of atom c ax(1:natom) = x co-ordinate of atom c ay(1:natom) = y ... c az(1:natom) = z ... c x(1:nbfn) = x co-ordinate of basis function c y(1:nbfn) = y ... c z(1:nbfn) = z ... c expnt(1:nbfn)= exponent of gaussian c rnorm(1:nbfn)= normalization constant of gaussian c iky(1:nbfn) = iky(i) = i*(i-1)/2 to speed up fock build c icut1 = no. of successful ij 2-e screening tests c icut2 = no. of successful ijkl 2-e screening tests c icut3 = no. of 2-e integrals computed c parameter (natom = 8, nbfn =120, nocc =16, mxiter = 30) parameter (nnbfn = nbfn*(nbfn+1)/2, pi = 3.141592653589793d0) parameter (tol= 1.0d-4) parameter (tol2e=1.0d-6) c common /cscf/ $ enrep, q(natom), ax(natom), ay(natom), az(natom), $ x(nbfn), y(nbfn), z(nbfn), expnt(nbfn), rnorm(nbfn), $ iky(nbfn), icut1, icut2, icut3 c ga-5.9.2/tcgmsg/examples/cscf15.h000066400000000000000000000032761500715745200164730ustar00rootroot00000000000000/*$Id: cscf15.h,v 1.2 1995-02-02 23:23:56 d3g681 Exp $*/ c c include file defining common /cscf/ c c natom = no. of atoms (constant parameter) c nbfn = no. of basis functions (constant parameter) c nnbfn = nbfn*(nbfn+1)/2 (constant parameter) c nocc = no. of occupied orbitals (constant parameter) c mxiter = maximim no. of iterations(constant parameter) c tol = convergence criterion (constant parameter) c pi = a familiar constant (constant parameter) c tol2e = 2-e integral screening (constant parameter) c c the remainder is initialized in block data or in the c routine ininrm (rnorm and iky) c c enrep = nuclear repulsion energy c q(1:natom) = nuclear charge of atom c ax(1:natom) = x co-ordinate of atom c ay(1:natom) = y ... c az(1:natom) = z ... c x(1:nbfn) = x co-ordinate of basis function c y(1:nbfn) = y ... c z(1:nbfn) = z ... c expnt(1:nbfn)= exponent of gaussian c rnorm(1:nbfn)= normalization constant of gaussian c iky(1:nbfn) = iky(i) = i*(i-1)/2 to speed up fock build c icut1 = no. of successful ij 2-e screening tests c icut2 = no. of successful ijkl 2-e screening tests c icut3 = no. of 2-e integrals computed c parameter (natom = 1, nbfn = 15, nocc = 2, mxiter = 20) parameter (nnbfn = nbfn*(nbfn+1)/2, pi = 3.141592653589793d0) parameter (tol= 1.0d-4) parameter (tol2e=1.0d-7) c common /cscf/ $ enrep, q(natom), ax(natom), ay(natom), az(natom), $ x(nbfn), y(nbfn), z(nbfn), expnt(nbfn), rnorm(nbfn), $ iky(nbfn), icut1, icut2, icut3 c ga-5.9.2/tcgmsg/examples/cscf240lin.h000066400000000000000000000033021500715745200172440ustar00rootroot00000000000000/*$Id: cscf240lin.h,v 1.2 1995-02-02 23:23:57 d3g681 Exp $*/ c c include file defining common /cscf/ c c natom = no. of atoms (constant parameter) c nbfn = no. of basis functions (constant parameter) c nnbfn = nbfn*(nbfn+1)/2 (constant parameter) c nocc = no. of occupied orbitals (constant parameter) c mxiter = maximim no. of iterations(constant parameter) c tol = convergence criterion (constant parameter) c pi = a familiar constant (constant parameter) c tol2e = 2-e integral screening (constant parameter) c c the remainder is initialized in block data or in the c routine ininrm (rnorm and iky) c c enrep = nuclear repulsion energy c q(1:natom) = nuclear charge of atom c ax(1:natom) = x co-ordinate of atom c ay(1:natom) = y ... c az(1:natom) = z ... c x(1:nbfn) = x co-ordinate of basis function c y(1:nbfn) = y ... c z(1:nbfn) = z ... c expnt(1:nbfn)= exponent of gaussian c rnorm(1:nbfn)= normalization constant of gaussian c iky(1:nbfn) = iky(i) = i*(i-1)/2 to speed up fock build c icut1 = no. of successful ij 2-e screening tests c icut2 = no. of successful ijkl 2-e screening tests c icut3 = no. of 2-e integrals computed c parameter (natom =16, nbfn =240, nocc =32, mxiter = 30) parameter (nnbfn = nbfn*(nbfn+1)/2, pi = 3.141592653589793d0) parameter (tol= 0.5d-3) parameter (tol2e=1.0d-6) c common /cscf/ $ enrep, q(natom), ax(natom), ay(natom), az(natom), $ x(nbfn), y(nbfn), z(nbfn), expnt(nbfn), rnorm(nbfn), $ iky(nbfn), icut1, icut2, icut3 c ga-5.9.2/tcgmsg/examples/cscf30.h000066400000000000000000000032761500715745200164700ustar00rootroot00000000000000/*$Id: cscf30.h,v 1.2 1995-02-02 23:23:59 d3g681 Exp $*/ c c include file defining common /cscf/ c c natom = no. of atoms (constant parameter) c nbfn = no. of basis functions (constant parameter) c nnbfn = nbfn*(nbfn+1)/2 (constant parameter) c nocc = no. of occupied orbitals (constant parameter) c mxiter = maximim no. of iterations(constant parameter) c tol = convergence criterion (constant parameter) c pi = a familiar constant (constant parameter) c tol2e = 2-e integral screening (constant parameter) c c the remainder is initialized in block data or in the c routine ininrm (rnorm and iky) c c enrep = nuclear repulsion energy c q(1:natom) = nuclear charge of atom c ax(1:natom) = x co-ordinate of atom c ay(1:natom) = y ... c az(1:natom) = z ... c x(1:nbfn) = x co-ordinate of basis function c y(1:nbfn) = y ... c z(1:nbfn) = z ... c expnt(1:nbfn)= exponent of gaussian c rnorm(1:nbfn)= normalization constant of gaussian c iky(1:nbfn) = iky(i) = i*(i-1)/2 to speed up fock build c icut1 = no. of successful ij 2-e screening tests c icut2 = no. of successful ijkl 2-e screening tests c icut3 = no. of 2-e integrals computed c parameter (natom = 2, nbfn = 30, nocc = 4, mxiter = 20) parameter (nnbfn = nbfn*(nbfn+1)/2, pi = 3.141592653589793d0) parameter (tol= 1.0d-4) parameter (tol2e=1.0d-6) c common /cscf/ $ enrep, q(natom), ax(natom), ay(natom), az(natom), $ x(nbfn), y(nbfn), z(nbfn), expnt(nbfn), rnorm(nbfn), $ iky(nbfn), icut1, icut2, icut3 c ga-5.9.2/tcgmsg/examples/cscf60.h000066400000000000000000000032761500715745200164730ustar00rootroot00000000000000/*$Id: cscf60.h,v 1.2 1995-02-02 23:24:02 d3g681 Exp $*/ c c include file defining common /cscf/ c c natom = no. of atoms (constant parameter) c nbfn = no. of basis functions (constant parameter) c nnbfn = nbfn*(nbfn+1)/2 (constant parameter) c nocc = no. of occupied orbitals (constant parameter) c mxiter = maximim no. of iterations(constant parameter) c tol = convergence criterion (constant parameter) c pi = a familiar constant (constant parameter) c tol2e = 2-e integral screening (constant parameter) c c the remainder is initialized in block data or in the c routine ininrm (rnorm and iky) c c enrep = nuclear repulsion energy c q(1:natom) = nuclear charge of atom c ax(1:natom) = x co-ordinate of atom c ay(1:natom) = y ... c az(1:natom) = z ... c x(1:nbfn) = x co-ordinate of basis function c y(1:nbfn) = y ... c z(1:nbfn) = z ... c expnt(1:nbfn)= exponent of gaussian c rnorm(1:nbfn)= normalization constant of gaussian c iky(1:nbfn) = iky(i) = i*(i-1)/2 to speed up fock build c icut1 = no. of successful ij 2-e screening tests c icut2 = no. of successful ijkl 2-e screening tests c icut3 = no. of 2-e integrals computed c parameter (natom = 4, nbfn = 60, nocc = 8, mxiter = 20) parameter (nnbfn = nbfn*(nbfn+1)/2, pi = 3.141592653589793d0) parameter (tol= 1.0d-4) parameter (tol2e=1.0d-6) c common /cscf/ $ enrep, q(natom), ax(natom), ay(natom), az(natom), $ x(nbfn), y(nbfn), z(nbfn), expnt(nbfn), rnorm(nbfn), $ iky(nbfn), icut1, icut2, icut3 c ga-5.9.2/tcgmsg/examples/cscf60lin.h000066400000000000000000000033011500715745200171630ustar00rootroot00000000000000/*$Id: cscf60lin.h,v 1.2 1995-02-02 23:24:04 d3g681 Exp $*/ c c include file defining common /cscf/ c c natom = no. of atoms (constant parameter) c nbfn = no. of basis functions (constant parameter) c nnbfn = nbfn*(nbfn+1)/2 (constant parameter) c nocc = no. of occupied orbitals (constant parameter) c mxiter = maximim no. of iterations(constant parameter) c tol = convergence criterion (constant parameter) c pi = a familiar constant (constant parameter) c tol2e = 2-e integral screening (constant parameter) c c the remainder is initialized in block data or in the c routine ininrm (rnorm and iky) c c enrep = nuclear repulsion energy c q(1:natom) = nuclear charge of atom c ax(1:natom) = x co-ordinate of atom c ay(1:natom) = y ... c az(1:natom) = z ... c x(1:nbfn) = x co-ordinate of basis function c y(1:nbfn) = y ... c z(1:nbfn) = z ... c expnt(1:nbfn)= exponent of gaussian c rnorm(1:nbfn)= normalization constant of gaussian c iky(1:nbfn) = iky(i) = i*(i-1)/2 to speed up fock build c icut1 = no. of successful ij 2-e screening tests c icut2 = no. of successful ijkl 2-e screening tests c icut3 = no. of 2-e integrals computed c parameter (natom = 4, nbfn = 60, nocc = 8, mxiter = 20) parameter (nnbfn = nbfn*(nbfn+1)/2, pi = 3.141592653589793d0) parameter (tol= 1.0d-4) parameter (tol2e=1.0d-6) c common /cscf/ $ enrep, q(natom), ax(natom), ay(natom), az(natom), $ x(nbfn), y(nbfn), z(nbfn), expnt(nbfn), rnorm(nbfn), $ iky(nbfn), icut1, icut2, icut3 c ga-5.9.2/tcgmsg/examples/daxpy.f000066400000000000000000000022721500715745200165250ustar00rootroot00000000000000 subroutine daxpy(n,da,dx,incx,dy,incy) c c constant times a vector plus a vector. c uses unrolled loops for increments equal to one. c jack dongarra, linpack, 3/11/78. c C$Id: daxpy.f,v 1.2 1995-02-02 23:24:05 d3g681 Exp $ double precision dx(1),dy(1),da integer i,incx,incy,ix,iy,m,mp1,n c if(n.le.0)return if (da .eq. 0.0d0) return if(incx.eq.1.and.incy.eq.1)go to 20 c c code for unequal increments or equal increments c not equal to 1 c ix = 1 iy = 1 if(incx.lt.0)ix = (-n+1)*incx + 1 if(incy.lt.0)iy = (-n+1)*incy + 1 do 10 i = 1,n dy(iy) = dy(iy) + da*dx(ix) ix = ix + incx iy = iy + incy 10 continue return c c code for both increments equal to 1 c c c clean-up loop c 20 m = mod(n,4) if( m .eq. 0 ) go to 40 do 30 i = 1,m dy(i) = dy(i) + da*dx(i) 30 continue if( n .lt. 4 ) return 40 mp1 = m + 1 do 50 i = mp1,n,4 dy(i) = dy(i) + da*dx(i) dy(i + 1) = dy(i + 1) + da*dx(i + 1) dy(i + 2) = dy(i + 2) + da*dx(i + 2) dy(i + 3) = dy(i + 3) + da*dx(i + 3) 50 continue return end ga-5.9.2/tcgmsg/examples/daxpy1.s000066400000000000000000000076401500715745200166270ustar00rootroot00000000000000// daxpy1(n, a, x, y) // do i=1,n // y(i) = y(i) + a * x(i) // end do // // modification of daxpy, using pipelined loads of x values // reads two elements beyond end of array, assumes strides of 1 // based on code by Dave Scott, but adapted for use with // matrix diagonaliser. // bill purvis dl. // // // r16 N // r18 x pointer // r19 y pointer // r20 loop counter // r30 -1 constant for bla // // f2 value of A // f4 x1, x5 // f6 x2, x6 // f8 x3, x7 // f10 x4, x8 // f12 products // f14 // f16 y1 // f18 y2 // f20 y3 // f22 y4 // f24 y5 // f26 y6 // f28 y7 // f30 y8 // .globl _daxpy1_ .align 8 _daxpy1_: ld.l r0(r16),r16 // get n adds -1,r0,r30 // -1 for bla adds -8,r19,r19 // correct y pointer shr 3,r16,r20 // loop count = n/8 bte r20,r0,finale // bypass main loop for N < 8 nop // align for dual mode fst.q f0,-64(sp)++ // save regs fst.q f4,16(sp) fst.q f8,32(sp) fst.q f12,48(sp) d.pfiadd.dd f0,f0,f0 // initiate dual mode pfld.d r0(r17),f0 // discard, load A d.fnop adds -1,r20,r20 // adjust loop count for bla d.fnop pfld.d r0(r18),f0 // discard, read x1 d.fnop pfld.d 8(r18)++,f0 // discard, read x2 d.fnop bla r30,r20,loop // prime LCC d.fnop pfld.d 8(r18)++,f2 // load A, read x3 loop: d.fnop fld.d 8(r19),f16 // read y1 d.fnop pfld.d 8(r18)++,f4 // load x1, read x4 d.fnop fld.d 16(r19),f18 // read y2 d.fnop pfld.d 8(r18)++,f6 // load x2, read x5 d.pfmul.dd f2,f4,f0 // - start a*x1 fld.d 24(r19),f20 // read y3 d.fnop pfld.d 8(r18)++,f8 // load x3, read x6 d.pfmul.dd f2,f6,f0 // - start a*x2 fld.d 32(r19),f22 // read y4 d.fnop pfld.d 8(r18)++,f10 // load x4, read x7 d.pfmul.dd f2,f8,f12 // store a*x1, start a*x3 fld.d 40(r19),f24 // read y5 d.pfadd.dd f16,f12,f0 // - start (a*x1)+y1 pfld.d 8(r18)++,f4 // load x5, read x8 d.pfmul.dd f2,f10,f12 // store a*x2, start a*x4 fld.d 48(r19),f26 // read y6 d.pfadd.dd f18,f12,f0 // - start (a*x2)+y2 pfld.d 8(r18)++,f6 // load x6, read x1' d.pfmul.dd f2,f4,f12 // store a*x3, start a*x5 fld.d 56(r19),f28 // read y7 d.pfadd.dd f20,f12,f0 // - start (a*x3)+y3 pfld.d 8(r18)++,f8 // load x7, read x2' d.pfmul.dd f2,f6,f12 // store a*x4, start a*x6 fld.d 64(r19),f30 // read y8 d.pfadd.dd f22,f12,f16 // store y1 start (a*x4)+y4 pfld.d 8(r18)++,f10 // load x8, read x3' d.pfmul.dd f2,f8,f12 // store a*x5, start a*x7 fst.d f16,8(r19)++ // save y1 d.pfadd.dd f24,f12,f18 // store y2 start (a*x5)+y5 nop d.pfmul.dd f2,f10,f12 // store a*x6 start a*x8 nop d.pfadd.dd f26,f12,f20 // store y3 start (a*x6)+y6 nop d.pfmul.dd f0,f0,f12 // store a*x7 flush fst.d f18,8(r19)++ // save y2 d.pfadd.dd f28,f12,f22 // store y4, start (a*x7)+y7 fst.d f20,8(r19)++ // save y3 d.pfmul.dd f0,f0,f12 // store a*x8 flush fst.d f22,8(r19)++ // save y4 d.pfadd.dd f30,f12,f24 // store y5, start (a*x8)+y8 fst.d f24,8(r19)++ // save y5 d.pfadd.dd f0,f0,f26 // store y6, flush fst.d f26,8(r19)++ // save y6 d.pfadd.dd f0,f0,f28 // store y7, flush fst.d f28,8(r19)++ // save y7 d.pfadd.dd f0,f0,f30 // store y8, flush bla r30,r20,loop // loop control d.fnop fst.d f30,8(r19)++ // save y8 // // exit dual mode, restore fp regs // fnop fld.q 0(sp),f0 fnop fld.q 16(sp),f4 fld.q 32(sp),f8 fld.q 48(sp),f12 adds 64,sp,sp adds -16,r18,r18 // correct overrun for remainder finale: and 7,r16,r16 // mask off to get remainder bc exit // skip if nothing left adds r30,r16,r16 // pre-adjust counter adds -8,r18,r18 // adjust x pointer bla r30,r16,loop2 fld.d r0(r17),f16 // re-load a loop2: fld.d 8(r18)++,f18 // load x value fld.d 8(r19),f20 // load y value fmul.dd f16,f18,f18 // a * x(i) fadd.dd f18,f20,f20 // + y(i) bla r30,r16,loop2 fst.d f20,8(r19)++ // ...storing updated value // exit: bri r1 nop ga-5.9.2/tcgmsg/examples/ddot.f000066400000000000000000000023271500715745200163330ustar00rootroot00000000000000 double precision function ddot(n,dx,incx,dy,incy) c c forms the dot product of two vectors. c uses unrolled loops for increments equal to one. c jack dongarra, linpack, 3/11/78. c C$Id: ddot.f,v 1.2 1995-02-02 23:24:06 d3g681 Exp $ double precision dx(1),dy(1),dtemp integer i,incx,incy,ix,iy,m,mp1,n c ddot = 0.0d0 dtemp = 0.0d0 if(n.le.0)return if(incx.eq.1.and.incy.eq.1)go to 20 c c code for unequal increments or equal increments c not equal to 1 c ix = 1 iy = 1 if(incx.lt.0)ix = (-n+1)*incx + 1 if(incy.lt.0)iy = (-n+1)*incy + 1 do 10 i = 1,n dtemp = dtemp + dx(ix)*dy(iy) ix = ix + incx iy = iy + incy 10 continue ddot = dtemp return c c code for both increments equal to 1 c c c clean-up loop c 20 m = mod(n,5) if( m .eq. 0 ) go to 40 do 30 i = 1,m dtemp = dtemp + dx(i)*dy(i) 30 continue if( n .lt. 5 ) go to 60 40 mp1 = m + 1 do 50 i = mp1,n,5 dtemp = dtemp + dx(i)*dy(i) + dx(i + 1)*dy(i + 1) + * dx(i + 2)*dy(i + 2) + dx(i + 3)*dy(i + 3) + dx(i + 4)*dy(i + 4) 50 continue 60 ddot = dtemp return end ga-5.9.2/tcgmsg/examples/demo.proto000066400000000000000000000032551500715745200172440ustar00rootroot00000000000000#!/bin/csh set path = (../ipcv4.0 $path) echo " Example Message Passing Programs" echo " --------------------------------" echo "" # Find/generate template PROCGRP file if (! -e template.p) then echo "$0 : template.p not found. A default using this host only will be made." echo "$0 : ... add extra hosts and processes as desired. " make template.p endif # Check no. of arguments if ($#argv != 1) then echo "$0 : a single argument is required" echo "usage: $0 scf|md|mc|jacobi|grid" exit 1 endif # Jump to desired example set ARGS = " " switch ("$1") case "scf": goto SCF breaksw case "grid" goto GRID breaksw case "md": case "mc": case "jacobi": set PROGRAM = "$1" goto RUNIT breaksw default: echo "usage: $0 scf|md|mc|jacobi|grid" exit 1 endsw # For GRID find out how big a grid GRID: echo -n 'Input arguments for grid program ' set ARGS="$<" set PROGRAM = grid goto RUNIT # For SCF find out how many basis functions SCF: getnbfn: echo -n 'Input no. of basis functions to use (15, 30, 60) ' set nbfn="$<" if ( ("$nbfn" != 15) && ("$nbfn" != 30) && ("$nbfn" != 60) ) goto getnbfn echo "" if ($nbfn == 60) then echo 'Sixty basis functions can take a long time ... be patient' echo "" endif set PROGRAM = scf$nbfn goto RUNIT RUNIT: # Generate the actual PROCGRP file from the template and print out # summary of it sed "s/PROGRAM/$PROGRAM/" < template.p > {$PROGRAM}.p echo "" echo 'The following hosts/processes will be used:' echo ' ' awk 'NF==5 {printf("%25s ... %d processes\n",$2,$3)}' < {$PROGRAM}.p echo "" echo time parallel $PROGRAM $ARGS echo "" time parallel $PROGRAM $ARGS ga-5.9.2/tcgmsg/examples/diagon.f000066400000000000000000000146041500715745200166430ustar00rootroot00000000000000 subroutine orthv2(n, v, s, work1, work2) C$Id: diagon.f,v 1.2 1995-02-02 23:24:07 d3g681 Exp $ implicit none integer n double precision v(n,n), s(n,n), work1(n), work2(n) c integer i double precision a, ddot c c orthonormalize vectors (v) in place over metric (s) c c v = matrix of column vectors c s = full square metric matrix c c note ... is not suitable for high precision or if the c input vectors are close to linear dependence c c ... assumes that the metric is symmetric c c normalize vector one c call dgemv('n',n,n,1.0d0,s,n,v(1,1),1,0.0d0,work1,1) a = ddot(n, work1, 1, v(1,1), 1) call dscal(n, 1.0d0/dsqrt(a), v(1,1), 1) c do 10 i = 2, n c c orthog vector i to vectors j=1,...,i-1 c call dgemv('n',n,n,1.0d0,s,n,v(1,i),1,0.0d0,work1,1) call dgemv('t',n,i-1,1.0d0,v,n,work1,1,0.0d0,work2,1) call dgemv('n',n,i-1,-1.0d0,v,n,work2,1,1.0d0,v(1,i),1) c c normalize vector i c call dgemv('n',n,n,1.0d0,s,n,v(1,i),1,0.0d0,work1,1) a = ddot(n, work1, 1, v(1,i), 1) call dscal(n, 1.0d0/dsqrt(a), v(1,i), 1) 10 continue c end subroutine jacobi(a,iky,newbas,q,ilifq,nrow,e,iop1, *iop2,thresh) implicit real*8 (a-h,p-w),integer (i-n),logical (o) implicit character *8 (z),character *1 (x) implicit character *4 (y) dimension a(*),q(*),e(*),iky(*),ilifq(*) common/blkin/ppp(2048),omask(1024),iipt(1024),ipt(1024) if(iop1.eq.1) go to 88 do 44 i=1,newbas ilifi=ilifq(i) call vclr(q(ilifi+1),1,nrow) 44 q(ilifi+i)=1.0d0 88 if(newbas.eq.1)goto 66 86 te=0.0d0 do 1 i=2,newbas i1=i-1 ikyi=iky(i) do 1 j=1,i1 temp= dabs(a(j+ikyi)) if(te.lt.temp)te=temp 1 continue if(te.lt.thresh)goto 99 te=te*0.1d0 do 22 i=2,newbas i1=i-1 ip1=i+1 ikyi=iky(i) itest=newbas-ip1 ii=i+ikyi ilifi=ilifq(i) do 22 j=1,i1 ij=j+ikyi vij=a(ij) if( dabs(vij) .lt. te) go to 22 vii=a(ii)*0.5d0 j1=j-1 jp1=j+1 ikyj=iky(j) jj=j+ikyj vjj=a(jj)*0.5d0 temp=vii-vjj tem=dsqrt(temp*temp+vij*vij) if(temp)78,77,77 78 tem=-tem 77 cost=(temp+tem)/vij sint=dsqrt(1.0d0/(1.0d0+cost*cost)) cost=cost*sint temp=vii+vjj a(ii)=temp+tem a(jj)=temp-tem a(ij)=0.0d0 if(j1.le.0)go to 3 call drot(j1,a(ikyi+1),1,a(ikyj+1),1,cost,sint) 3 if(i1 .lt. jp1) go to 5 do 6 k=jp1,i1 jj=iky(k)+j vij=a(k+ikyi) a(k+ikyi)=vij*cost+a(jj)*sint 6 a(jj)=a(jj)*cost-vij*sint 5 if(itest)7,79,79 79 do 8 k=ip1,newbas ij=iky(k)+i jj=j+iky(k) vij=a(ij) a(ij)=vij*cost+a(jj)*sint 8 a(jj)=a(jj)*cost-vij*sint 7 continue call drot(nrow,q(ilifi+1),1,q(ilifq(j)+1),1,cost,sint) 22 continue goto 86 99 do 11 i=1,newbas omask(i)=.false. e(i)=a(iky(i)+i) 11 iipt(i)=i/2 goto (67,55,55,43),iop2 c... binary sort of e.values to increasing value sequence 55 ipt(1)=1 do 19 j=2,newbas ia=1 ib=j-1 test=e(j) 53 irm1=ib-ia if(irm1)58,50,51 51 ibp=ia+iipt(irm1) if(test.lt.e(ipt(ibp)))goto 52 c... insert into high half ia=ibp+1 goto 53 c... insert into low half 52 jj=ib do 54 i=ibp,ib ipt(jj+1)=ipt(jj) 54 jj=jj-1 ib=ibp-1 goto 53 c... end point of search 50 jj=ipt(ia) if(test.ge.e(jj))goto 57 ipt(ia+1)=jj 58 ipt(ia)=j goto 19 57 ipt(ia+1)=j 19 continue goto (67,68,69,43),iop2 c... sort by decreasing e.value(invert order) 69 itest=newbas+1 ip1=iipt(newbas) do 41 i=1,ip1 j=itest-i k=ipt(i) ipt(i)=ipt(j) 41 ipt(j)=k 68 do 20 i=1,newbas k=ipt(i) iipt(k)=i 20 ppp(i)=e(k) 59 continue call dcopy(newbas,ppp(1),1,e(1),1) c... iipt(i)=k means column i is to move to posn k c... ipt(i)=k means column k is to move to posn i call sortq(q,ilifq,iipt,newbas,nrow) go to 67 c ... locking requested 43 do 31 j=1,newbas m=ilifq(j) temp=0.0d0 do 32 i=1,newbas vij= dabs(q(i+m)) if(vij.lt.temp.or.omask(i))goto 32 temp=vij k=i 32 continue iipt(j)=k omask(k)=.true. 31 ppp(k)=e(j) goto 59 66 e(1)=a(1) 67 return end subroutine sortq(q,ilifq,iipt,newbas,nrow) implicit real*8 (a-h,p-w),integer (i-n),logical (o) implicit character *8 (z),character *1 (x) implicit character *4 (y) dimension q(*),ilifq(*),iipt(*) common/blkin/ppp(2048),omask(1024) juse=1 jnext=1025 do 10 i = 1, newbas omask(i) = .false. 10 continue do 21 i=1,newbas if(omask(i))goto 21 j=i call dcopy(nrow,q(ilifq(j)+1),1,ppp(juse),1) c... start a permutation cycle 23 m=iipt(j) ilifi=ilifq(m) call dcopy(nrow,q(ilifi+1),1,ppp(jnext),1) call dcopy(nrow,ppp(juse),1,q(ilifi+1),1) if(m.eq.i)goto 21 juse=jnext jnext=1026-jnext omask(m)=.true. j=m goto 23 21 continue return end subroutine vclr(a,incr,n) implicit real*8 (a-h,o-z) dimension a(*) if(incr.eq.1) then do 10 loop=1,n 10 a(loop) = 0.0d0 else loopi=1 do 20 loop=1,n a(loopi)=0.0d0 loopi=loopi+incr 20 continue endif return end subroutine zsqua(n, h, s) implicit double precision (a-h, o-z) c dimension h(*), s(n,n) c c put lower triangular array h into square matrix s c and halve the diagonal c do 10 i = 1,n-1 do 20 j = i+1,n s(j,i) = 0.0d0 20 continue 10 continue c ij = 0 do 30 i = 1,n do 40 j = 1,i-1 s(j,i) = h(ij+j) 40 continue ij = ij + i s(i,i) = h(ij) * 0.5d0 30 continue c end subroutine zfold(n, h, s) implicit double precision (a-h, o-z) c dimension h(*), s(n,n) c c fold square matrix s into lower triangular array h c ... note that the diagonal gets doubled. c ij = 0 do 10 i = 1,n do 20 j = 1,i h(ij+j) = s(i,j) + s(j,i) 20 continue ij = ij + i 10 continue c end ga-5.9.2/tcgmsg/examples/dscal.f000066400000000000000000000016531500715745200164700ustar00rootroot00000000000000 subroutine dscal(n,da,dx,incx) c c scales a vector by a constant. c uses unrolled loops for increment equal to one. c jack dongarra, linpack, 3/11/78. c C$Id: dscal.f,v 1.2 1995-02-02 23:24:08 d3g681 Exp $ double precision da,dx(1) integer i,incx,m,mp1,n,nincx c if(n.le.0)return if(incx.eq.1)go to 20 c c code for increment not equal to 1 c nincx = n*incx do 10 i = 1,nincx,incx dx(i) = da*dx(i) 10 continue return c c code for increment equal to 1 c c c clean-up loop c 20 m = mod(n,5) if( m .eq. 0 ) go to 40 do 30 i = 1,m dx(i) = da*dx(i) 30 continue if( n .lt. 5 ) return 40 mp1 = m + 1 do 50 i = mp1,n,5 dx(i) = da*dx(i) dx(i + 1) = da*dx(i + 1) dx(i + 2) = da*dx(i + 2) dx(i + 3) = da*dx(i + 3) dx(i + 4) = da*dx(i + 4) 50 continue return end ga-5.9.2/tcgmsg/examples/fexit.f.proto000066400000000000000000000001371500715745200176570ustar00rootroot00000000000000 subroutine fexit end IF IBM IBMNOEXT subroutine flush(iunit) end ENDIF ga-5.9.2/tcgmsg/examples/getmem.c000066400000000000000000000014011500715745200166440ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /*$Id: getmem.c,v 1.2 1995-02-02 23:24:10 d3g681 Exp $*/ extern char * memalign(); #if defined(AIX) && !defined(EXTNAME) #define getmem_ getmem #endif /* getmem gets n real*8 storage locations and returns its address (iaddr) and offset (ioff) within the real*8 array work so that the usable memory is (work(i+ioff),i=1,n). e.g. call getmem(n,work,iaddr,ioff) if (iaddr.eq.0) call error Mods are needed to release this later. */ void getmem_(pn,pwork,paddr,pioff) unsigned long *pn,*paddr,*pioff; double *pwork; { double *ptemp; unsigned int size = 8; ptemp = (double *) memalign(size, (unsigned) size* *pn); *paddr = (unsigned long) ptemp; *pioff = ptemp - pwork; } ga-5.9.2/tcgmsg/examples/grid.c000066400000000000000000000541061500715745200163250ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /*$Id: grid.c,v 1.2 1995-02-02 23:24:11 d3g681 Exp $*/ #include #include #ifdef PLOT #include #include FILE *plot_file; #define PLOT_VALUE 1 #define PLOT_ERROR 2 #define PLOT_RESIDUAL 3 static long plot_type = 0; /* 0 means no plot */ #endif #include "sndrcv.h" #if !defined(AIX) extern char *malloc(); #endif extern void exit(); #define TCG_MAX(a,b) (((a)>(b)) ? (a) : (b)) #define TCG_MIN(a,b) (((a)<(b)) ? (a) : (b)) #define TCG_ABS(a) (((a)>=0 ) ? (a) : -(a)) #define LO -3.1415926535 /* Phsyical dimensions */ #define HI 3.1415926535 double scale; /* Physical mesh grain */ long P, ncol_P, nrow_P; /* P = No. of processes = ncol_P * nrow_P */ long my_col_P, my_row_P; /* Co-ords of this proc. in grid of processes*/ double *buffer; /* Buffer used for exchanging rows */ long north, south, east, west; /* No. of process in that direction on process grid or -1 if no-one there */ long col_low = 0; /* Grid co-ords of top left corner */ long row_low = 0; /* Mapping from GLOBAL grid index to physical coordinates */ #define MAPCOL(I) (scale*((double) (I+col_low)) + LO) #define MAPROW(J) (scale*((double) (J+row_low)) + LO) long cs_exchange = 0; /* Timing information */ long cs_global = 0; long cs_interpolate = 0; long cs_plot = 0; long cs_total = 0; /* void ZeroGrid(grid, ncols, nrows) double **grid; long ncols, nrows; { long i,j; for (i=0; i<=ncols; i++) for (j=0; j<=nrows; j++) grid[i][j] = 0.0; } */ double Solution(x,y) double x,y; /* Model potential that defines solution and boundary conditions. This particular choice makes the discretization error quite apparent. */ { /* return cos(x)*exp(-y) + 0.1*sin(2.*x)*exp(-2.*y) + 0.1*cos(3.*x)*exp(-3.*y); */ return 0.5 * (cos(x)*exp(-y) + cos(y)*exp(-x) + 0.1*(sin(2.*x)*exp(-2.*y) + sin(2.*y)*exp(-2.*x)) + 0.1*(cos(3.*x)*exp(-3.*y) + cos(3.*y)*exp(-3.*x))); } double GridError(grid, ncols, nrows, ngrid) long ncols, nrows, ngrid; double **grid; /* Compute mean absolute error at grid points relative to the analytic solution ... error is due to either lack of convergence or discretization error. */ { long i,j, type=9, length=1; long start; double error = 0.0; for (i=1; i= 0) { SND_(&type1, (char *) grid[1], &bnrows, &west, &synch); RCV_(&type2, (char *) grid[0], &bnrows, &lenmes, &west, &nodefrom, &synch); } if (east >= 0) { SND_(&type3, (char *) grid[ncols-1], &bnrows, &east, &synch); RCV_(&type4, (char *) grid[ncols], &bnrows, &lenmes, &east, &nodefrom, &synch); } } else { if (east >= 0) { RCV_(&type1, (char *) grid[ncols], &bnrows, &lenmes, &east, &nodefrom, &synch); SND_(&type2, (char *) grid[ncols-1], &bnrows, &east, &synch); } if (west >= 0) { RCV_(&type3, (char *) grid[0], &bnrows, &lenmes, &west, &nodefrom, &synch); SND_(&type4, (char *) grid[1], &bnrows, &west, &synch); } } if (my_row_P%2) { if (north >= 0) { GATHER(1); SND_(&type5, (char *) buffer, &bncols, &north, &synch); RCV_(&type6, (char *) buffer, &bncols, &lenmes, &north, &nodefrom, &synch); SCATTER(0); } if (south >= 0) { GATHER(nrows-1); SND_(&type7, (char *) buffer, &bncols, &south, &synch); RCV_(&type8, (char *) buffer, &bncols, &lenmes, &south, &nodefrom, &synch); SCATTER(nrows); } } else { if (south >= 0) { RCV_(&type5, (char *) buffer, &bncols, &lenmes, &south, &nodefrom, &synch); SCATTER(nrows); GATHER(nrows-1); SND_(&type6, (char *) buffer, &bncols, &south, &synch); } if (north >= 0) { RCV_(&type7, (char *) buffer, &bncols, &lenmes, &north, &nodefrom, &synch); SCATTER(0); GATHER(1); SND_(&type8, (char *) buffer, &bncols, &north, &synch); } } cs_exchange += MTIME_() - start; } #ifdef PLOT void ClosePlotFile() { if (!plot_type) return; if (NODEID_() == 0) (void) fclose(plot_file); } void OpenPlotFile(maxgrid) long maxgrid; { if (!plot_type) return; if (NODEID_()) return; /* Only node 0 needs to do this */ if (!(plot_file = fopen("plot","w+"))) Error("OpenPlotFile: failed to open plot file", (long) -1); maxgrid = htonl((long) maxgrid); /* For portability */ if (fwrite((char *) &maxgrid, sizeof(int), 1, plot_file) != 1) Error("OpenPlotFile: failed to write maxgrid", (long) -1); (void) fflush(plot_file); } void InsertPlot(plot_full, ngrid, plot_grid, nrows, ncols, col_low, row_low) unsigned char *plot_full, *plot_grid; long ngrid, nrows, ncols, col_low, row_low; { long i, j; long i_lo = (col_low == 0) ? 0 : 1; long j_lo = (row_low == 0) ? 0 : 1; unsigned char *temp; /* Dink around with i_lo, j_lo so that the interior edges in the parallel case are not overwritten with incorrect values */ if (i_lo) plot_grid += nrows; for (i=i_lo; i update red then update black using new reds (gauss-seidel red-black w-relaxation). Return the mean abs. error (value of the laplacian) on the old grid. */ { double residual = 0.0; double omega = 0.94; /* Over relaxation parameter */ long i,j, type=10, length=1, jlo; double *gg, *ggm, *ggp; Exchange(grid, ncols, nrows); /* Update red */ for (i=1; i=0) && (i1<=new_ncols) && (j1>=0) && (j1<=new_nrows) ) new_grid[i1][j1] = old_grid[i][j]; } for (i=1; i<=old_ncols; i++) for (j=1; j<=old_nrows; j++) { i2 = 2*i-1+col_shift; j2 = 2*j-1+row_shift; if ( (i2>=0) && (i2<=new_ncols) && (j2>=0) && (j2<=new_nrows) ) new_grid[i2][j2] = 0.25 * (old_grid[i ][j ] + old_grid[i-1][j-1] + old_grid[i-1][j ] + old_grid[i ][j-1]); } BoundaryConditions(new_grid, new_ncols, new_nrows); Exchange(new_grid, new_ncols, new_nrows); for (i=1; i 0) ? NODEID_()-1 : -1; south = (my_row_P < (nrow_P-1)) ? NODEID_()+1 : -1; east = (my_col_P < (ncol_P-1)) ? NODEID_()+nrow_P : -1; west = (my_col_P > 0) ? NODEID_()-nrow_P : -1; ParseArguments(argc, argv, &maxgrid, &niter, &nprint, &nlevel, &thresh); ngrid1 = TCG_MAX(ncol_P,maxgrid>>(nlevel-1)); ngrid1 = TCG_MAX(nrow_P,ngrid1); /* Size of first grid */ maxgrid = ngrid1<<(nlevel-1); /* Actual size of final grid */ if (!(buffer = (double *) malloc((unsigned) (sizeof(double)*(maxgrid+1))))) Error("failed to allocate comms buffer", (long) (maxgrid+1)); #ifdef PLOT OpenPlotFile(maxgrid); #endif /* Loop from coarse to fine grids */ for (level=0; level #include #include static void catchit() { printf("!! Floating point interrupt caught !!\n"); fflush(stdout); (void) signal(SIGIOT, SIG_DFL); abort(); } void ieeetrap_() { (void) ieee_handler("set","inexact", SIGFPE_IGNORE); (void) ieee_handler("set","underflow", SIGFPE_IGNORE); (void) ieee_handler("set","invalid", SIGFPE_IGNORE); } ga-5.9.2/tcgmsg/examples/integ.f000066400000000000000000000056351500715745200165140ustar00rootroot00000000000000 double precision function exprjh(x) C$Id: integ.f,v 1.2 1995-02-02 23:24:14 d3g681 Exp $ double precision x c c dumb solution to underflow problems on sun c if (x.lt.-37.0d0) then exprjh = 0.0d0 else exprjh = exp(x) endif end subroutine setfm implicit double precision (a-h,o-z) common/values/fm(2001,5),rdelta,delta,delo2 dimension t(2001),et(2001) c c initalize common block for computation of f0 by recursion down c from f200 c delta=28.0d0/2000.0d0 delo2=delta*0.5d0 rdelta=1.0d0/delta maxm=4 do 10 i=1,2001 tt=delta*dble(i-1) et(i)=exprjh(-tt) t(i)=2.0d0*tt fm(i,maxm+1)=0.0d0 10 continue do 20 i=200,maxm,-1 rr=1.0d0/dble(2*i+1) do 30 ii=1,2001 fm(ii,maxm+1)=(et(ii)+t(ii)*fm(ii,maxm+1))*rr 30 continue 20 continue do 40 i=maxm,1,-1 rr=1.0d0/dble(2*i-1) do 50 ii=1,2001 fm(ii,i)=(et(ii)+t(ii)*fm(ii,i+1))*rr 50 continue 40 continue c end subroutine f0(value, t) implicit real*8 (a-h,o-z) common/values/fm(2001,5),rdelta,delta,delo2 parameter(fac0=0.88622692545276d0, $ rhalf=0.5d0,rthird=0.3333333333333333d0,rquart=0.25d0) data t0/28.d0/ c c computes f0 to a relative accuracy of better than 4.e-13 for all t. c uses 4th order taylor expansion on grid out to t=28.0 c asymptotic expansion accurate for t greater than 28 c if(t.ge.t0) then value = fac0 / sqrt(t) else n = idint((t+delo2)*rdelta) x = delta*dble(n)-t n = n+1 value = fm(n,1)+x*(fm(n,2)+rhalf*x*(fm(n,3)+ $ rthird*x*(fm(n,4)+rquart*x*fm(n,5)))) endif c end subroutine addin(g, i, j, k, l, fock, dens, iky) implicit double precision (a-h, o-z) dimension fock(*), dens(*), iky(*) c c add (ij|kl) into the fock matrix c gg = g g2 = gg+gg g4 = g2+g2 ik = iky(i) + k il = iky(i) + l ij = iky(i) + j jk = iky(max(j,k)) + min(j,k) jl = iky(max(j,l)) + min(j,l) kl = iky(k) + l aij = g4*dens(kl)+fock(ij) fock(kl) = g4*dens(ij)+fock(kl) fock(ij) = aij gil=gg if(i.eq.k.or.j.eq.l) gg = g2 if(j.eq.k) gil = g2 ajk = fock(jk) - gil*dens(il) ail = fock(il) - gil*dens(jk) aik = fock(ik) - gg*dens(jl) fock(jl) = fock(jl) - gg*dens(ik) fock(jk) = ajk fock(il) = ail fock(ik) = aik c end subroutine dfill(n,val,a,ia) implicit real*8 (a-h,o-z) dimension a(*) c c initialise double precision array to scalar value c if (ia.eq.1) then do 10 i = 1, n a(i) = val 10 continue else do 20 i = 1,(n-1)*ia+1,ia a(i) = val 20 continue endif c end ga-5.9.2/tcgmsg/examples/jacobi.f000066400000000000000000000146751500715745200166410ustar00rootroot00000000000000 program main C$Id: jacobi.f,v 1.2 1995-02-02 23:24:15 d3g681 Exp $ implicit double precision (a-h, o-z) dimension q(1) include 'msgtypesf.h' parameter (niter = 30, maxp = 2047) dimension jlo(0:maxp), nj(0:maxp) data iqoff, iqaddr/0, 0/ c c Simple iterative (Jacobi) linear equation solver (Ax = b) c c Initialize the message passing environment c call pbeginf c call evon c c Read in the dimension of the matrix on process 0 and c broadcast its value to everyone else c if (nodeid() .eq. 0) then write(6,1) 1 format(' Input dimension of matrix: '$) read(5,*) n endif call brdcst(1+MSGINT, n, mitob(1), 0) c c makjlo assigns a range of columns to each process. c ncolp is the number that this process is to do. c call makjlo(n, jlo, nj) ncolp = nj(nodeid()) c c allocate memory c need = n*ncolp + 2*n + 2*ncolp call getmem(need, q, iqaddr, iqoff) if (iqaddr.eq.0) call parerr(999) ia = iqoff + 1 ib = ia + n*ncolp ix = ib + ncolp is = ix + ncolp iw = is + n c c Make matrix (a), rhs vector (b) and initial guess (x) c call makabx(n, jlo(nodeid()), ncolp, q(ia), q(ib), q(ix)) c c Do niter iterations of the Jacobi algorithm. c Synchronize first for accurate timings. c call synch(13) rjunk = timer() call jacobi(n, jlo, nj, q(ia), q(ib), q(ix), q(is), q(iw), niter) used = timer() c c Print out results c rmflop = dble(niter*2*n)*dble(n) / (used*1.0d6) if (nodeid() .eq. 0) write(6,2) n, used, nnodes(), rmflop 2 format(' N=',i4,' used ',f6.2,' secs with ',i3,' processes', $ ', mflop=', f8.2) c call pend call fexit end subroutine jacobi(n, jlo, nj, a, b, x, s, work, niter) implicit double precision (a-h, o-z) dimension a(n, *), b(*), x(*), s(n), work(n), $ jlo(0:*), nj(0:*) c c Apply niter iterations of the Jacobi algorithm. c me = nodeid() do 10 iter = 1, niter c c Compute matrix vector product Ax ... this is the real work c c Do the part that we have c call mxv(a, n, x, nj(me), s) c c Now we have to add up the result over all the processors c Call dgop for simple but inefficient version. Call mxvadd c for a much more efficient version c c call dgop(2, s, n, '+') c call mxvadd(s, work, jlo, nj) c c Compute our part of the update vector and compute c the residual error (the error requires a global sum) c err = 0.0d0 do 20 j = jlo(me), jlo(me) + nj(me) - 1 jj = j - jlo(me) + 1 x(jj) = x(jj) + (b(jj)-s(j))/a(j,jj) err = err + abs((b(jj)-s(j))) 20 continue c c Write out results every now and again c if (mod(iter,10).eq.0) then call dgop(3, err, 1, '+') if (nodeid().eq.0) write(6,1) iter, err 1 format(' Iteration',i3,', Error',d9.2) endif 10 continue c end subroutine makabx(n, jlo, nj, a, b, x) implicit double precision (a-h, o-z) dimension a(n, nj), b(nj), x(nj) c jhi = jlo + nj - 1 do 10 j = jlo, jhi jj = j - jlo + 1 cvd$ novector do 20 i = 1, n a(i,jj) = dble(i+j) / dble(abs(i-j)*n+n/50+1) 20 continue b(jj) = dble(mod(j,3)) x(jj) = b(jj) / a(j,jj) 10 continue c end subroutine makjlo(n, jlo, nj) dimension jlo(0:*), nj(0:*) c ncolp = n / nnodes() next = n - (ncolp*nnodes()) jjlo = 1 do 10 iproc = 0, nnodes()-1 jlo(iproc) = jjlo jjlo = jjlo + ncolp if (iproc.lt.next) jjlo = jjlo + 1 nj(iproc) = jjlo - jlo(iproc) 10 continue c end subroutine mxvadd(s, work, jlo, nj) implicit real*8 (a-h, o-z) include 'msgtypesf.h' dimension s(*), work(*), jlo(0:*), nj(0:*) logical synch parameter (synch=.true.) c c We have in s(1:n) this process's contribution to the c matrix vector product A*x where we had nj(me) elements c of x starting at element jlo(me). Each process needs c to end up with the same elements of the full result c vector s. c c Thus we need to send to each process (ip) the elements c s(jlo(ip)+k-1), k=1,nj(ip). And we need to receive from c each process our piece of s which we add onto our result c vector. c c If communication is synchronous then we must explictly pair up c send/receive requests on this process with the matching c receive/send operations on other processes. c c There is potential for much more asynch stuff but the damned c iPSC-i860 hangs (irreproducibly) if we send off too many c unresolved asynchronous messages (how many is too much?). c me = nodeid() nproc = nnodes() nn = nproc + mod(nproc,2) c if (synch) then do 10 iter = 1, nn-1 call pairup(nn, me, iter, ip) if (ip.lt.nproc) then if (me. lt. ip) then call snd(3+MSGDBL, s(jlo(ip)), mdtob(nj(ip)), ip, 1) call rcv(3+MSGDBL, work, mdtob(nj(me)), lenmes, ip, & node, 1) else if (me.gt.ip) then call rcv(3+MSGDBL, work, mdtob(nj(me)), lenmes, ip, & node, 1) call snd(3+MSGDBL, s(jlo(ip)), mdtob(nj(ip)), ip, 1) endif call vvadd(nj(me), s(jlo(me)), work) endif 10 continue else do 20 iter = 1, nn-1 call pairup(nn, me, iter, ip) if (ip.lt.nproc) then call snd(3+MSGDBL, s(jlo(ip)), mdtob(nj(ip)), ip, 0) call rcv(3+MSGDBL, work, mdtob(nj(me)), lenmes, ip, node, 1) call vvadd(nj(me), s(jlo(me)), work) endif 20 continue call waitcom(-1) endif c end subroutine pairup(n, me, iter, ipair) c c one of many ways of generating maximally overlapped pairs c (not all that good on a hypercube though!) c if (iter.eq.1) then ipair = mod(n+1-me,n) else if (me.eq.0) then ipair = iter else if (me.eq.iter) then ipair = 0 else if (ipair.eq.0) ipair = me ipair = ipair + 2 if (ipair.ge.n) ipair = ipair + 1 - n endif end subroutine vvadd(n, a, b) implicit real*8 (a-h, o-z) dimension a(*), b(*) c do 10 i = 1,n a(i) = a(i) + b(i) 10 continue c end ga-5.9.2/tcgmsg/examples/mc.f000066400000000000000000000124131500715745200157750ustar00rootroot00000000000000 program he C$Id: mc.f,v 1.2 1995-02-02 23:24:16 d3g681 Exp $ implicit double precision (a-h, o-z) include 'msgtypesf.h' parameter (maxpt=512) dimension x(6,maxpt), psix(maxpt), ex(maxpt) data esq/0.0d0/ c c Monte Carlo Test Program ... evaluates the expectation c value of the energy for a simple wavefunction for helium. c c Initalize the message passing environment c call pbeginf call evon c c process zero reads in input and then broadcasts to others c if (nodeid().eq.0) then write(6,1) 1 format(' He atom variational monte carlo '// & ' Input neq and nstep '$) c call flush(6) read(5,*) neq, nstep nstep = ( (nstep+99)/100 ) * 100 neq = ( (neq+99)/100 ) * 100 endif call brdcst(1+MSGINT, neq, mitob(1), 0) call brdcst(1+MSGINT, nstep, mitob(1), 0) c c Divide up the work ... each process does a subset of the points c or configurations ... make sure that total is indeed maxpt c nproc = nnodes() npoint = maxpt / nproc nremain = maxpt - npoint*nproc if (nodeid().lt.nremain) npoint = npoint + 1 c c Actually do the work. Routine init generates the intial points c in a 2x2x2 cube. Equilibriate for neq moves then compute averages c for nstep moves. c rjunk = timer() call init(npoint, x, psix) call mover(x, psix, ex, e, esq, npoint, neq) call mover(x, psix, ex, e, esq, npoint, nstep) c c Sum the results from all the processors c call dgop(2+MSGDBL, e, 1, '+') call dgop(3+MSGDBL, esq, 1, '+') c c Write out the results before terminating c if (nodeid().eq.0) then e = e / dble(nproc) esq = esq / dble(nproc) err = sqrt((esq-e*e) / dble(nproc*nstep/100)) used = timer() write(6,2) e, err, used, nproc 2 format(' energy =',f10.6,' +/-',f9.6,': used =',f7.2,' secs', $ ': nproc =',i3) endif c call pend call fexit c end subroutine mover(x, psix, ex, e, esq, npoint, nstep) implicit double precision (a-h, o-z) dimension x(6,npoint), psix(npoint), ex(npoint) c c move the set of points nstep times accumulating averages c for the energy and square of the energy c e = 0.0d0 esq = 0.0d0 eb = 0.0d0 do 10 istep = 1, nstep c c sample a new set of points c do 20 ipoint = 1, npoint call sample(x(1, ipoint), psix(ipoint), ex(ipoint)) 20 continue c c accumulate average(s) c do 30 ipoint = 1, npoint eb = eb + ex(ipoint) 30 continue c c block averages every 100 moves to reduce statistical correlation c if (mod(istep,100).eq.0) then eb = eb / dble(npoint*100) e = e + eb esq = esq + eb*eb eb = 0.0d0 endif 10 continue c c compute final averages c e = e / dble(nstep/100) esq = esq / dble(nstep/100) c end subroutine sample(x, psix, ex) implicit double precision (a-h, o-z) dimension x(6), xnew(6) c c sample a new point ... i.e. move current point by a c random amount and accept the move according to the c ratio of the square of the wavefunction at the new c point and the old point. c c generate trial point and info at the new point c do 10 i = 1,6 xnew(i) = x(i) + (drand48(0)-0.5d0)*0.3d0 10 continue call rvals(xnew, r1, r2, r12, r1dr2) psinew = psi(r1, r2, r12) c c accept or reject the move c prob = min((psinew / psix)**2, 1.0d0) if (prob .gt. drand48(0)) then do 20 i = 1,6 x(i) = xnew(i) 20 continue psix = psinew else call rvals(x, r1, r2, r12, r1dr2) endif ex = elocal(r1, r2, r12, r1dr2) c end subroutine rvals(x, r1, r2, r12, r1dr2) implicit double precision (a-h, o-z) dimension x(6) c c compute required distances etc. c r1 = dsqrt(x(1)**2 + x(2)**2 + x(3)**2) r2 = dsqrt(x(4)**2 + x(5)**2 + x(6)**2) r12 = dsqrt((x(1)-x(4))**2 + (x(2)-x(5))**2 + (x(3)-x(6))**2) r1dr2 = x(1)*x(4) + x(2)*x(5) + x(3)*x(6) c end double precision function psi(r1, r2, r12) implicit double precision (a-h, o-z) c c compute value of the trial wavefunction c psi = dexp(-2.0d0*(r1+r2)) * (1.0d0 + 0.5d0*r12) c end double precision function elocal(r1, r2, r12, r1dr2) implicit double precision (a-h, o-z) c c compute local energy = (H psi) / psi c f = r12*(1.0d0 + 0.5d0*r12) g = 0.5d0*r12 + r1 +r2 - r1dr2*(1.0d0/r1 + 1.0d0/r2) elocal = -4.0d0 + g / f c end subroutine init(npoint, x, psix) implicit double precision (a-h, o-z) dimension x(6,npoint), psix(npoint) c c distribute points in a 2x2x2 cube. c c in parallel version make random no. seed depend on the c process number ... for a production code should be more c sophisticated than this. c call srand48(2*nodeid()+1) c do 10 ipoint = 1,npoint do 20 i = 1,6 x(i,ipoint) = (drand48(0) - 0.5d0) * 2.0d0 20 continue call rvals(x(1,ipoint), r1, r2, r12, r1dr2) psix(ipoint) = psi(r1, r2, r12) 10 continue c end ga-5.9.2/tcgmsg/examples/mc.input000066400000000000000000000000121500715745200166770ustar00rootroot000000000000004000 4000 ga-5.9.2/tcgmsg/examples/md.f000066400000000000000000000365701500715745200160100ustar00rootroot00000000000000c c md example program due to dieter heermann c restructured and pdf added by rjh december 1990 c c the message passing version distributes the computation c of the forces (order npart**2) over the processes, assuming c that each process has all of the co-ordinates. c A global add then gives each process all the information c needed to compute the next update. c None of the order npart work has been parallelized so that c will begin to dominate on many processors. c implicit double precision (a-h, o-z) c parameter (mm = 13, lenpdf = 256) c parameter (mm = 8, lenpdf = 256) c parameter (mm = 6, lenpdf = 256) C$Id: md.f,v 1.2 1995-02-02 23:24:18 d3g681 Exp $ parameter (mm = 4, lenpdf = 256) c parameter (mm = 3, lenpdf = 256) parameter (npart = 4*mm*mm*mm) parameter (maxint = npart*150) include 'msgtypesf.h' c dimension x(1:npart,1:3), vh(1:npart,1:3),f(1:npart,1:3), & pdf(1:lenpdf+1), times(8), inter(2,maxint) data times/8*0.0d0/ c c initalize message passing environment c call pbeginf me = nodeid() c c parameter definition (density, volume, temperature ...) c den = 0.83134d0 side = (dble(npart) / den)**0.3333333d0 tref = 0.722d0 rcoff = min(3.5d0, side/2.0d0) c islow = 1 to match the rather large original timestep islow = 4 h = 0.064d0 / islow irep = 50 istop = 400 iprint = 10 ineigh = 10 * (1 + islow/4) movemx = 800 delpdf = 0.5d0*side/lenpdf rdelp = 1.0d0 / delpdf c if (me.eq.0)write(6,1) npart,side,rcoff,tref,h,delpdf,irep,istop, & iprint,ineigh,movemx 1 format(' molecular dynamics simulation example program'/ & ' ---------------------------------------------'// & ' number of particles is ............ ',i6/ & ' side length of the box is ......... ',f13.6/ & ' cut off is ........................ ',f13.6/ & ' reduced temperature is ............ ',f13.6/ & ' basic time step is ................ ',f13.6/ & ' pdf sampling interval ............. ',f13.6/ & ' temperature scale interval ........ ',i6/ & ' stop scaling at move .............. ',i6/ & ' print interval .................... ',i6/ & ' update neighbor list every ........ ',i6, ' steps'/ & ' total no. of steps ................ ',i6) c call flush(6) c a = side / dble(mm) sideh = side * 0.5d0 hsq = h * h hsq2 = hsq * 0.5d0 npartm = npart - 1 rcoffs = rcoff * rcoff tscale = 16.0d0 / (1.0d0 * npart - 1.0d0) vaver = 1.13d0 * sqrt(tref / 24.0d0) ekinavg = 0.0d0 c c generate fcc lattice for atoms inside box c rjunk = timer() call fcc(x, npart, mm, a) times(1) = times(1) + timer() c c initialise velocites and forces (which are zero in fcc positions) c call mxwell(vh,3*npart,h,tref) call dfill(3*npart, 0.0d0, f, 1) times(2) = times(2) + timer() c c start of md. c if (me.eq.0) write(6,3) 3 format(//1x,' i ',' ke ',' pe ',' e ', & ' temp ',' pres ',' vel ',' rp'/ & 1x,' -----',' ----------',' ----------',' ----------', & ' --------',' --------',' --------',' ----') c call flush(6) c do 200 move = 1,movemx if (move.eq.1 .or. move.eq.(istop+1)) $ call dfill(lenpdf, 0.0d0, pdf, 1) c c move the particles and partially update velocities c call domove(3*npart,x,vh,f,side) times(3) = times(3) + timer() c c compute forces in the new positions and accumulate the pdf c virial and potential energy. Have to get the full forces c on each node, hence the dgop (global add). c if (mod(move-1,ineigh).eq.0) then call neigh(npart, x, side, rcoff, ninter, inter, maxint, $ pdf, rdelp, lenpdf) times(8) = times(8) + timer() endif call forces(npart, x, f, vir, epot, side, rcoff, ninter, inter) call dgop(1+MSGDBL, f, 3*npart, '+') times(4) = times(4) + timer() c c scale forces, complete update of velocites and compute k.e. c call mkekin(npart,f,vh,hsq2,hsq,ekin) ekinavg = ekinavg + ekin times(5) = times(5) + timer() c c average the velocity and temperature scale if desired c if ((move.le.istop) .and. (mod(move, irep).eq.0)) then call velavg(npart, vh, vaver, count, vel, h) sc = sqrt( tref / (tscale * ekinavg / irep) ) call dscal(3*npart, sc, vh, 1) ekinavg = 0.0d0 endif times(6) = times(6) + timer() c c printout information if desired ... have to do global c sum to get full potential energy and virial c if (mod(move, iprint) .eq. 0) then call velavg(npart, vh, vaver, count, vel, h) call dgop(2+MSGDBL, epot, 1, '+') call dgop(2+MSGDBL, vir, 1, '+') if (me.eq.0) $ call prnout(move, ekin, epot, tscale, vir, vel, count, $ npart, den) times(7) = times(7) + timer() endif c 200 continue c c print out the pdf at the end of the calculation c have first to get contribution from everyone with global add c call dgop(2+MSGDBL, pdf, lenpdf, '+') if (me.eq.0) $ call prnpdf(lenpdf, pdf, side, delpdf, npart, movemx-istop, $ ineigh) times(7) = times(7) + timer() if (me.eq.0) write(6,431) nnodes(),(times(i),i=1,8) 431 format(' nproc geom mxwell domove forces ekin velscl', & ' print neigh' & /1x,i6,8f8.2) c if (me.eq.0) call stats call pend call fexit end subroutine mxwell(vh,n3,h,tref) implicit double precision (a-h, o-z) dimension vh(1:n3) c c sample maxwell distribution at temperature tref c iseed = 4711 ujunk = drand48(iseed) iseed = 0 npart = n3/3 iof1 = npart iof2 = npart*2 tscale = 16.0d0 / (1.0d0 * npart - 1.0d0) do 10 i = 1,n3,2 c 1 u1 = drand48(iseed) u2 = drand48(iseed) v1 = 2.0d0 * u1 - 1.0d0 v2 = 2.0d0 * u2 - 1.0d0 s = v1*v1 + v2*v2 if (s.ge.1.0) goto 1 c r = sqrt(-2.0d0*dlog(s)/s) vh(i) = v1 * r vh(i+1) = v2 * r 10 continue c ekin = 0.0d0 sp = 0.0d0 do 20 i = 1,npart sp = sp + vh(i) 20 continue sp = sp / npart do 21 i = 1,npart vh(i) = vh(i) - sp ekin = ekin + vh(i)*vh(i) 21 continue sp = 0.0d0 do 22 i = iof1 + 1,iof2 sp = sp + vh(i) 22 continue sp = sp / npart do 23 i = iof1 + 1,iof2 vh(i) = vh(i) - sp ekin = ekin + vh(i)*vh(i) 23 continue sp = 0.0d0 do 24 i = iof2 + 1,n3 sp = sp + vh(i) 24 continue sp = sp / npart do 25 i = iof2 + 1,n3 vh(i) = vh(i) - sp ekin = ekin + vh(i)*vh(i) 25 continue ts = tscale * ekin sc = h * sqrt(tref/ts) do 30 i = 1,n3 vh(i) = vh(i) * sc 30 continue c end subroutine domove(n3,x,vh,f,side) implicit double precision (a-h, o-z) dimension x(n3),vh(n3),f(n3) c c move particles c do 10 i = 1,n3 x(i) = x(i) + vh(i) + f(i) c periodic boundary conditions if (x(i).lt.0.0d0) x(i) = x(i) + side if (x(i).gt.side) x(i) = x(i) - side c partial velocity updates vh(i) = vh(i) + f(i) c initialise forces for next iteration f(i) = 0.0d0 10 continue c end subroutine mkekin(npart,f,vh,hsq2,hsq,ekin) implicit double precision (a-h, o-z) dimension f(1:npart,3),vh(1:npart,3) c c scale forces, update velocites and compute k.e. c sum = 0.0d0 do 10 ix = 1,3 do 20 i = 1,npart f(i,ix) = f(i,ix) * hsq2 vold = vh(i,ix) vh(i,ix) = vh(i,ix) + f(i,ix) sum = sum + vh(i,ix) * vh(i,ix) 20 continue 10 continue ekin = sum / hsq c end subroutine fcc(x, npart, mm, a) implicit double precision (a-h, o-z) dimension x(1:npart, 3) c c generate fcc lattice for atoms inside box c ijk = 0 do 10 lg = 0,1 do 11 i = 0,mm-1 do 12 j = 0,mm-1 do 13 k = 0,mm-1 ijk = ijk + 1 x(ijk,1) = i * a + lg * a * 0.5d0 x(ijk,2) = j * a + lg * a * 0.5d0 x(ijk,3) = k * a 13 continue 12 continue 11 continue 10 continue do 20 lg = 1,2 do 21 i = 0,mm-1 do 22 j = 0,mm-1 do 23 k = 0,mm-1 ijk = ijk + 1 x(ijk,1) = i * a + (2-lg) * a * 0.5d0 x(ijk,2) = j * a + (lg-1) * a * 0.5d0 x(ijk,3) = k * a + a * 0.5d0 23 continue 22 continue 21 continue 20 continue c end subroutine dfill(n,val,a,ia) implicit double precision (a-h, o-z) dimension a(*) c c initialise double precision array to scalar value c do 10 i = 1,(n-1)*ia+1,ia a(i) = val 10 continue c end subroutine prnout(move, ekin, epot, tscale, vir, vel, count, $ npart, den) implicit double precision (a-h, o-z) c c printout interesting (?) information at current timestep c ek = 24.0d0 * ekin epot = 4.0d0 * epot etot = ek + epot temp = tscale * ekin pres = den * 16.0d0 * (ekin - vir) / npart vel = vel / npart rp = (count / dble(npart)) * 100.0d0 write(6,2) move,ek,epot,etot,temp,pres,vel,rp 2 format(1x,i6,3f12.4,f10.4,f10.4,f10.4,f6.1) c call flush(6) c end subroutine velavg(npart, vh, vaver, count, vel, h) implicit double precision (a-h, o-z) dimension vh(npart, 3) c c compute average velocity c vaverh = vaver*h vel = 0.0d0 count = 0.0d0 do 10 i = 1,npart sq = sqrt(vh(i,1)**2 + vh(i,2)**2 + vh(i,3)**2) if (sq.gt.vaverh) count = count + 1.0d0 vel = vel + sq 10 continue vel = vel / h c end subroutine prnpdf(lenpdf, pdf, side, delpdf, npart, nmove, ineigh) implicit double precision (a-h, o-z) dimension pdf(lenpdf) c c final scaling and printout of the pdf c write(6,1) 1 format(/' pair distribution function written to file pdf.dat'/) open(1, file='pdf.dat', form='formatted', status='unknown', $ err=999) c coord = 0.0d0 volfac = side*side*side / (4.0d0*delpdf*delpdf*delpdf*3.141593d0) facnn = 2.0d0 / dble(npart*nmove/ineigh) facn = 1.0d0 / dble(npart) do 10 i = 1,lenpdf ri = dble(i) grfac = volfac / (ri*ri) func = pdf(i) * facnn coord = coord + func pdf(i) = func * grfac * facn write(1,2) dble(i)*delpdf,pdf(i),coord 10 continue 2 format(1x,f7.3,f13.6,4x,f9.2) close(1) return c 999 write(6,*) ' error opening pdf.dat' call parerr(999) c end subroutine neigh(npart, x, side, rcoff, ninter, inter, maxint, $ pdf, rdelp, lenpdf) implicit double precision (a-h, o-z) dimension x(npart, 3), inter(2,maxint), pdf(lenpdf) c c Form my part of the neighbour list and also compute the pair c distribution function. c c npart = no. of particles c x(,) = coords c side = side of box c rcoff = cutoff for force c ninter= returns no. of interactions c inter(,) = returns interactions c maxint = size of inter c me = nodeid() nproc = nnodes() c sideh = 0.5d0*side rcoffs = (rcoff*1.2d0)**2 ninter = 0 c c Get better work distribution by having the same c processor handle particles (i) and (npart-i) c Note that assume that npart is even. c do 270 ii = me+1, npart/2, nproc do 275 icase = 1, 2 if (icase .eq. 1) then i = ii else i = npart - ii endif xi = x(i,1) yi = x(i,2) zi = x(i,3) do 280 j = i+1,npart ij = ij + 1 xx = xi - x(j,1) yy = yi - x(j,2) zz = zi - x(j,3) if (xx .lt. -sideh) xx = xx + side if (xx .gt. sideh) xx = xx - side if (yy .lt. -sideh) yy = yy + side if (yy .gt. sideh) yy = yy - side if (zz .lt. -sideh) zz = zz + side if (zz .gt. sideh) zz = zz - side rd = xx*xx + yy*yy + zz*zz ipdf = min(sqrt(rd),sideh) * rdelp + 1 pdf(ipdf) = pdf(ipdf) + 1.0d0 if (rd .le. rcoffs) then ninter = ninter + 1 if (ninter.gt.maxint) then write(6,*) ' too many interactions ', ninter call parerr(1) endif inter(1,ninter) = i inter(2,ninter) = j endif 280 continue 275 continue 270 continue c c$$$ ij = ninter c$$$ call igop(99, ij, 1, '+') c$$$ if (me .eq. 0) then c$$$ write(6,*) ' No. of interactions per particle = ', c$$$ $ ij/npart c$$$ endif c end subroutine forces(npart, x, f, vir, epot, side, rcoff, $ ninter, inter) implicit double precision (a-h, o-z) logical oshift parameter (oshift = .true.) dimension x(npart, 3), f(npart, 3), inter(2, ninter) c c compute forces driven by the neighbour list c vir = 0.0d0 epot = 0.0d0 sideh = 0.5d0*side rcoffs = rcoff*rcoff c c for shifted potential ... set oshift true to enable c if (oshift) then rc6 = 1.0d0 / rcoff**6 rc12 = 1.0d0 / rcoff**12 ecut = rc12 - rc6 fcut = (rc12 - 0.5d0*rc6)/rcoff efcut = 12.0d0 * fcut endif c do 10 ij = 1, ninter i = inter(1,ij) j = inter(2,ij) xx = x(i,1) - x(j,1) yy = x(i,2) - x(j,2) zz = x(i,3) - x(j,3) if (xx .lt. -sideh) xx = xx + side if (xx .gt. sideh) xx = xx - side if (yy .lt. -sideh) yy = yy + side if (yy .gt. sideh) yy = yy - side if (zz .lt. -sideh) zz = zz + side if (zz .gt. sideh) zz = zz - side rd = xx*xx + yy*yy + zz*zz if (rd .le. rcoffs) then rrd = 1.0d0/rd rrd2 = rrd*rrd rrd3 = rrd2*rrd rrd4 = rrd2*rrd2 rrd6 = rrd2*rrd4 rrd7 = rrd6*rrd epot = epot + (rrd6 - rrd3) r148 = rrd7 - 0.5d0*rrd4 if (oshift) then r = sqrt(rd) epot = epot - ecut + efcut*(r-rcoff) r148 = r148 - fcut / r endif vir = vir - rd*r148 forcex = xx * r148 f(i,1) = f(i,1) + forcex f(j,1) = f(j,1) - forcex forcey = yy * r148 f(i,2) = f(i,2) + forcey f(j,2) = f(j,2) - forcey forcez = zz * r148 f(i,3) = f(i,3) + forcez f(j,3) = f(j,3) - forcez endif 10 continue c end ga-5.9.2/tcgmsg/examples/mxv_daxpy1.f000066400000000000000000000014411500715745200174750ustar00rootroot00000000000000 subroutine mxv(a,ncol,b,nrow,c) C$Id: mxv_daxpy1.f,v 1.2 1995-02-02 23:24:19 d3g681 Exp $ implicit double precision (a-h, o-z) dimension a(ncol, nrow), b(nrow), c(ncol) parameter (nchunk = 800) c c matrix vector product stripmined to optimize cache usage c when inner loop is replaced with a daxpy that uses pipelined c loads for a to avoid writing over c in the cache. c do 10 ilo = 1, ncol, nchunk ihi = min(ncol, ilo+nchunk-1) ndo = ihi - ilo + 1 do 20 i = ilo, ihi c(i) = 0.0d0 20 continue do 30 j = 1, nrow c do 40 i = ilo, ihi c c(i) = c(i) + a(i,j)*b(j) c40 continue call daxpy1(ndo, b(j), a(ilo, j), c(ilo)) 30 continue 10 continue c end ga-5.9.2/tcgmsg/examples/mxv_dgemv.f000066400000000000000000000013611500715745200173720ustar00rootroot00000000000000 subroutine mxv(a,ncol,b,nrow,c) C$Id: mxv_dgemv.f,v 1.2 1995-02-02 23:24:20 d3g681 Exp $ implicit double precision (a-h, o-z) double precision a(ncol, nrow), b(nrow), c(ncol) parameter (ilen=500, jlen=60) c call dgemv('n', ncol, nrow, 1.0d0, a, ncol, b, 1, 0.0d0, c, 1) c$$$ do 10 i = 1, ncol c$$$ c(i) = 0.0d0 c$$$ 10 continue c$$$c c$$$ do 40 jlo = 1, nrow, jlen c$$$ jhi = min(jlo+jlen-1, nrow) c$$$ do 30 ilo = 1, ncol, ilen c$$$ ihi = min(ilo+ilen-1, ncol) c$$$ ndo = ihi - ilo + 1 c$$$ do 20 j = jlo, jhi c$$$ call daxpy2(ndo, b(j), a(ilo,j), c(ilo)) c$$$ 20 continue c$$$ 30 continue c$$$ 40 continue c end ga-5.9.2/tcgmsg/examples/mxv_fortran.f000066400000000000000000000013231500715745200177410ustar00rootroot00000000000000 subroutine mxv(a,ncol,b,nrow,c) C$Id: mxv_fortran.f,v 1.2 1995-02-02 23:24:21 d3g681 Exp $ implicit double precision (a-h, o-z) dimension a(ncol, nrow), b(nrow), c(ncol) parameter (nchunk = 127) c c matrix vector product stripmined to optimize cache usage c when inner loop is replaced with a daxpy that uses pipelined c loads for a to avoid writing over c in the cache. c do 10 ilo = 1, ncol, nchunk ihi = min(ncol, ilo+nchunk-1) do 20 i = ilo, ihi c(i) = 0.0d0 20 continue do 30 j = 1, nrow do 40 i = ilo, ihi c(i) = c(i) + a(i,j)*b(j) 40 continue 30 continue 10 continue c end ga-5.9.2/tcgmsg/examples/output.f000066400000000000000000000052341500715745200167410ustar00rootroot00000000000000 subroutine output (z,rowlow,rowhi,collow,colhi,rowdim,coldim, $ nctl) c....................................................................... c output prints a real*8 matrix in formatted form with numbered rows c and columns. the input is as follows; c matrix(*,*).........matrix to be output c rowlow..............row number at which output is to begin c rowhi...............row number at which output is to end c collow..............column number at which output is to begin c colhi...............column number at which output is to end c rowdim..............row dimension of matrix(*,*) c coldim..............column dimension of matrix(*,*) c nctl................carriage control flag; 1 for single space c 2 for double space c 3 for triple space c the parameters that follow matrix are all of type integer*4. the c program is set up to handle 5 columns/page with a 1p5d24.15 format for c the columns. if a different number of columns is required, change c formats 1000 and 2000, and initialize kcol with the new number of c columns. c author; nelson h.f. beebe, quantum theory project, university of c florida, gainesville c....................................................................... C$Id: output.f,v 1.2 1995-02-02 23:24:22 d3g681 Exp $ implicit double precision (a-h,o-z) integer rowlow,rowhi,collow,colhi,rowdim,coldim,begin,kcol dimension z(rowdim,coldim) dimension asa(3) data column/8hcolumn /,asa/8h ,8h00000000 , 1 8h-------- /,blank/8h / data kcol/8/ data zero/0.d00/ do 11 i=rowlow,rowhi do 10 j=collow,colhi if (z(i,j).ne.zero) go to 15 10 continue 11 continue write (6,3000) 3000 format (/' zero matrix'/) go to 3 15 continue ctl = blank if ((nctl.le.3).and.(nctl.gt.0)) ctl = asa(nctl) if (rowhi.lt.rowlow) go to 3 if (colhi.lt.collow) go to 3 last = min0(colhi,collow+kcol-1) do 2 begin = collow,colhi,kcol write (6,1000) (column,i,i = begin,last) do 1 k = rowlow,rowhi do 4 i=begin,last if (z(k,i).ne.zero) go to 5 4 continue go to 1 5 write (6,2000) ctl,k,(z(k,i), i = begin,last) 1 continue last = min0(last+kcol,colhi) 2 continue 3 return * kcol = 4 * 1000 format (/1h ,16x,3(a6,i3,2x),(a6,i3)) * 2000 format (a1,3hrow,i4,2x,4f17.11) * kcol = 8 1000 format (/1h ,11x,7(a3,i3,3x),(a3,i3)) 2000 format (a1,'row',i4,1x,8f9.4) end ga-5.9.2/tcgmsg/examples/prtri.f000066400000000000000000000013651500715745200165420ustar00rootroot00000000000000 subroutine prtri(d,n) c c ----- print out a real triangular matrix ----- c C$Id: prtri.f,v 1.2 1995-02-02 23:24:24 d3g681 Exp $ implicit double precision (a-h, o-z) dimension d(*),dd(6) iw = 6 c max = 6 imax = 0 100 imin = imax+1 imax = imax+max if (imax .gt. n) imax = n write (iw,9008) write (iw,8028) (i,i = imin,imax) do 160 j = 1,n k = 0 do 140 i = imin,imax k = k+1 m = max0(i,j)*(max0(i,j)-1)/2 + min0(i,j) dd(k) = d(m) 140 continue write (iw,8048) j,(dd(i),i = 1,k) 160 continue if (imax .lt. n) go to 100 return 9008 format(/) 8028 format(6x,6(4x,i4,4x)) 8048 format(i5,1x,6f12.6) end ga-5.9.2/tcgmsg/examples/random.c000066400000000000000000000004021500715745200166460ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /*$Id: random.c,v 1.2 1995-02-02 23:24:25 d3g681 Exp $*/ double random_(seed) unsigned long *seed; { *seed = *seed *1812433253 + 12345; return (double) ((*seed & 0x7fffffff) * 4.6566128752458e-10); } ga-5.9.2/tcgmsg/examples/runit000077500000000000000000000006231500715745200163160ustar00rootroot00000000000000#!/bin/csh -x foreach nproc (16 12 8 4 2 1) foreach demo (grid) echo `whoami` `hostname` $nproc $demo /tmp > ${demo}.p (time ../ipcv4.0/parallel $demo -ngrid 1024) >& ${demo}.log.${nproc} end end foreach nproc (16 12 8 4 2 1) foreach demo (mc) echo `whoami` `hostname` $nproc $demo /tmp > ${demo}.p (time ../ipcv4.0/parallel $demo) < mc.input >& ${demo}.log.${nproc} end end ga-5.9.2/tcgmsg/examples/runit.grid000077500000000000000000000003211500715745200172350ustar00rootroot00000000000000#!/bin/csh -x foreach nproc (16 12 8 4 2 1) foreach demo (grid) echo `whoami` `hostname` $nproc $demo /tmp > ${demo}.p (time ../ipcv4.0/parallel $demo -ngrid 1024) >& ${demo}.log.${nproc} end end ga-5.9.2/tcgmsg/examples/scf.f000066400000000000000000000461141500715745200161560ustar00rootroot00000000000000 program scf C$Id: scf.f,v 1.3 1997-03-04 06:17:21 d3e129 Exp $ implicit double precision (a-h, o-z) include 'cscf.h' include 'msgtypesf.h' dimension orbs(nbfn*nbfn), dens(nnbfn), fock(nnbfn), $ work(nbfn*nbfn), evals(nbfn) data tinit, tonel, ttwoel, tdiag, tdens, tprint /6*0.0d0/ data eone, etwo, energy, deltad /4*0.0d0/ c c initalize the parallel message passing environment c call pbeginf * call ieeetrap me = nodeid() nproc = nnodes() c c initialize a bunch of stuff and initial density matrix c rjunk = timer() call ininrm call denges(dens, work) tinit = timer() c c make initial orthogonal orbital set for jacobi diagonalizer c call makeob(orbs, work) c c iterate c do 10 iter = 1, mxiter c c make info for sparsity test ... redone every iter to save space c call makesz(work, schwmax) c c make the one particle contribution to the fock matrix (in fock) c and the partial contribution to the energy c call oneel(dens, work, schwmax, fock, eone) call dgop(1, eone, 1, '+') tonel = tonel + timer() c c compute the two particle contribution and then add up the c contributions from each process with dgop c call twoel(dens, work, schwmax, fock, etwo) call dgop(2, etwo, 1, '+') call dgop(3, fock, nnbfn, '+') ttwoel = ttwoel + timer() c c only process 0 diagonalizes and updates the density c if (me.eq.0) then c c diagonalize the fock matrix ... c call diagon(fock, orbs, evals, work, tester, iter) call flush(6) tdiag = tdiag + timer() c c make the new density matrix in work from orbitals in orbs, c compute the norm of the change in the density matrix and c then update the density matrix in dens with damping. c call makden(orbs, work) deltad = dendif(dens, work) if (iter.eq.1) then scale = 0.0d0 else if (iter .le. 5) then if (nbfn .gt. 60) then scale = 0.5d0 else scale = 0.0d0 endif else scale = 0.0d0 endif call damp(scale, dens, work) tdens = tdens + timer() c c add up energy and print out convergence information c energy = enrep + eone + etwo call prnout(iter, energy, deltad, tester) tprint = tprint + timer() endif c c brodcast new density matrix and deltad to everyone c call brdcst(4+MSGDBL, dens, mdtob(nnbfn), 0) call brdcst(5+MSGDBL, deltad, mdtob(1), 0) c c if converged then exit iteration loop c if (deltad .lt. tol) goto 20 10 continue if(me.eq.0) $ write(6,*) ' SCF failed to converge in ', mxiter, ' iters' c c finished ... print out eigenvalues and occupied orbitals c 20 continue call igop(6, icut1, 1, '+') call igop(7, icut2, 1, '+') call igop(8, icut3, 1, '+') if (me.eq.0) then c c print out timing information c call prnfin(energy, evals, orbs) write(6,1) tinit, tonel, ttwoel, tdiag, tdens, tprint, $ nproc 1 format(/' init onel twoel diag dens print ncpu'/ $ ' ------ ------ ------ ------ ------ ------ ------'/ $ 1x, 6f7.2, i7/) c c print out information on # integrals evaulated each iteration c nints = nnbfn*(nnbfn+1)/2 frac = dble(icut3)/dble(nints) write(6,2) icut1, icut2, icut3, nints, frac 2 format(/' No. of integrals screened or computed ' $ /' -------------------------------------'/ $ /1x,' #ij test #kl test #compute #total', $ ' fraction', $ /1x,' --------- --------- --------- ---------', $ ' --------', $ /1x,4(2x,i9),f9.3) call stats endif c call pend call fexit c end subroutine makesz(schwarz, schwmax) implicit double precision (a-h, o-z) include 'cscf.h' include 'msgtypesf.h' dimension schwarz(nnbfn) c c schwarz(ij) = (ij|ij) for sparsity test c icut1 = 0 icut2 = 0 icut3 = 0 ij = 0 schwmax = 0.0d0 call dfill(nnbfn, 0.0d0, schwarz, 1) c me = nodeid() nproc = nnodes() do 10 i = 1, nbfn do 20 j = 1, i ij = ij + 1 if (mod(ij,nproc).eq.me) then call g(gg, i, j, i, j) schwarz(ij) = sqrt(gg) schwmax = max(schwmax, schwarz(ij)) endif 20 continue 10 continue c call dgop(101+MSGDBL, schwarz, nnbfn, '+') call dgop(102+MSGDBL, schwmax, 1, 'max') c end subroutine ininrm implicit double precision (a-h, o-z) include 'cscf.h' c c write a little welcome message c if (nodeid().eq.0) write(6,1) natom, nocc, nbfn, tol 1 format(/' Example Direct Self Consistent Field Program '/ $ ' -------------------------------------------- '// $ ' no. of atoms ............... ',i3/ $ ' no. of occupied orbitals ... ',i3/ $ ' no. of basis functions ..... ',i3/ $ ' convergence threshold ...... ',d9.2//) c c generate normalisation coefficients for the basis functions c and the index array iky c do 10 i = 1, nbfn iky(i) = i*(i-1)/2 10 continue c do 20 i = 1, nbfn rnorm(i) = (expnt(i)*2.0d0/pi)**0.75d0 20 continue c c initialize common for computing f0 c call setfm c end double precision function h(i,j) implicit double precision (a-h, o-z) include 'cscf.h' cvd$r novector cvd$r noconcur c c generate the one particle hamiltonian matrix element c over the normalized primitive 1s functions i and j c f0val = 0.0d0 sum = 0.0d0 rab2 = (x(i)-x(j))**2 + (y(i)-y(j))**2 + (z(i)-z(j))**2 facij = expnt(i)*expnt(j)/(expnt(i)+expnt(j)) expij = exprjh(-facij*rab2) repij = (2.0d0*pi/(expnt(i)+expnt(j))) * expij c c first do the nuclear attraction integrals c do 10 iat = 1, natom xp = (x(i)*expnt(i) + x(j)*expnt(j))/(expnt(i)+expnt(j)) yp = (y(i)*expnt(i) + y(j)*expnt(j))/(expnt(i)+expnt(j)) zp = (z(i)*expnt(i) + z(j)*expnt(j))/(expnt(i)+expnt(j)) rpc2 = (xp-ax(iat))**2 + (yp-ay(iat))**2 + (zp-az(iat))**2 c call f0(f0val, (expnt(i)+expnt(j))*rpc2) sum = sum - repij * q(iat) * f0val 10 continue c c add on the kinetic energy term c sum = sum + facij*(3.0d0-2.0d0*facij*rab2) * $ (pi/(expnt(i)+expnt(j)))**1.5d0 * expij c c finally multiply by the normalization constants c h = sum * rnorm(i) * rnorm(j) c end double precision function s(i,j) implicit double precision (a-h, o-z) include 'cscf.h' c c generate the overlap matrix element between the normalized c primitve gaussian 1s functions i and j c rab2 = (x(i)-x(j))**2 + (y(i)-y(j))**2 + (z(i)-z(j))**2 facij = expnt(i)*expnt(j)/(expnt(i)+expnt(j)) s = (pi/(expnt(i)+expnt(j)))**1.5d0 * exprjh(-facij*rab2) * $ rnorm(i)*rnorm(j) c end subroutine makden(orbs, dens) implicit double precision (a-h, o-z) include 'cscf.h' dimension orbs(nbfn, nbfn), dens(nnbfn) c c generate density matrix from orbitals in orbs. the first c nocc orbitals are doubly occupied. Note that the diagonal c elements are scaled by 0.5 c ij = 0 do 10 i = 1, nbfn do 20 j = 1, i p = 0.0d0 do 30 k = 1, nocc p = p + orbs(i,k)*orbs(j,k) 30 continue ij = ij + 1 dens(ij) = 2.0d0 * p 20 continue dens(ij) = dens(ij)*0.5d0 10 continue c end subroutine oneel(dens, schwarz, schwmax, fock, eone) implicit double precision (a-h, o-z) include 'cscf.h' dimension dens(nnbfn), fock(nnbfn), schwarz(nnbfn) c c fill in the one-electron part of the fock matrix and c compute the one-electron energy contribution c c simple structure to share out the work between processes c me = nodeid() nproc = nnodes() c call dfill(nnbfn, 0.0d0, fock, 1) do 10 i = me+1, nbfn, nproc do 20 j = 1,i ij = iky(i) + j if (schwarz(ij)*schwmax.gt.tol2e) fock(ij) = h(i,j) 20 continue 10 continue eone = ddot(nnbfn, fock, 1, dens, 1) c end integer function nxtask(nproc) parameter (ichunk = 10) save icount, nleft data nleft, icount /0, 0/ c c wrapper round nxtval() to increase granularity c and thus reduce no. of requests to shared counter c if(nproc.gt.0) then if(nleft.eq.0) then icount = nxtval(nproc) * ichunk nleft = ichunk endif nxtask = icount icount = icount + 1 nleft = nleft -1 else nleft = 0 nxtask = 0 junk = nxtval(nproc) endif c c following does dumb static load balancing c c$$$ if(nproc.gt.0) then c$$$ if (nleft .eq. 0) then c$$$ icount = nodeid() c$$$ nleft = 1 c$$$ endif c$$$ nxtask = icount c$$$ icount = icount + nnodes() c$$$ else c$$$ nleft = 0 c$$$ nxtask = 0 c$$$ endif end subroutine twoel(dens, schwarz, schwmax, fock, etwo) implicit double precision (a-h, o-z) include 'cscf.h' dimension dens(nnbfn), fock(nnbfn), schwarz(nnbfn) c c add in the two-electron contribution to the fock matrix c nproc = nnodes() c gg = 0.0d0 c next = nnbfn - nxtask(nproc) do 10 i = nbfn, 1, -1 do 20 j = i, 1, -1 ij = iky(i) + j if (ij .eq. next) then if (schwarz(ij)*schwmax .lt. tol2e) then icut1 = icut1 + ij else do 30 k = 1, i lhi = k if (k.eq.i) lhi = j do 40 l = 1, lhi kl = iky(k) + l if (schwarz(ij)*schwarz(kl).lt.tol2e) then icut2 = icut2 + 1 else icut3 = icut3 + 1 c c compute value of integral (ij|kl) and add into fock matrix c call g(gg, i, j, k, l) call addin(gg*0.5d0, i, j, k, l, fock, $ dens, iky) endif 40 continue 30 continue endif next = nnbfn - nxtask(nproc) endif 20 continue 10 continue c etwo = ddot(nnbfn, fock, 1, dens, 1) c c wait for all processes to finish work c junk = nxtask(-nproc) c end subroutine damp(fac, dens, work) implicit double precision (a-h, o-z) include 'cscf.h' dimension dens(nnbfn), work(nnbfn) c ofac = 1.0d0 - fac do 10 i = 1, nnbfn dens(i) = fac*dens(i) + ofac*work(i) 10 continue c end subroutine prnout(iter, energy, deltad, tester) implicit double precision (a-h, o-z) include 'cscf.h' c c printout results of each iteration c write(6,1) iter, energy, deltad, tester call flush(6) 1 format(' iter=',i3,', energy=',f13.8,', deltad=',d9.2, $ ', deltaf=',d9.2) c end double precision function dendif(dens, work) implicit double precision (a-h, o-z) include 'cscf.h' dimension dens(nnbfn), work(nnbfn) c c compute largest change in density matrix elements c denmax = 0.0d0 do 10 i = 1, nnbfn denmax = max(denmax, abs(work(i)-dens(i))) 10 continue dendif = denmax c end subroutine prnfin(energy, evals, orbs) implicit double precision (a-h, o-z) include 'cscf.h' dimension evals(nbfn), orbs(nbfn, nbfn) c c printout final results c write(6,1) energy 1 format(//' final energy = ',f16.11//' eigenvalues') call output(evals, 1, min(nbfn,nocc+5), 1, 1, nbfn, 1, 1) write(6,2) 2 format(//' eigenvectors ') call output(orbs, 1, nbfn, 1, nocc, nbfn, nbfn, 1) c end subroutine g(value,i,j,k,l) implicit double precision (a-h, o-z) include 'cscf.h' c c compute the two electon integral (ij|kl) over normalized c primitive 1s gaussians c f0val = 0.0d0 rab2 = (x(i)-x(j))**2 + (y(i)-y(j))**2 + (z(i)-z(j))**2 rcd2 = (x(k)-x(l))**2 + (y(k)-y(l))**2 + (z(k)-z(l))**2 facij = expnt(i)*expnt(j)/(expnt(i)+expnt(j)) fackl = expnt(k)*expnt(l)/(expnt(k)+expnt(l)) exijkl = exprjh(- facij*rab2 - fackl*rcd2) denom = (expnt(i)+expnt(j))*(expnt(k)+expnt(l)) * $ sqrt(expnt(i)+expnt(j)+expnt(k)+expnt(l)) fac = (expnt(i)+expnt(j))*(expnt(k)+expnt(l)) / $ (expnt(i)+expnt(j)+expnt(k)+expnt(l)) c xp = (x(i)*expnt(i) + x(j)*expnt(j))/(expnt(i)+expnt(j)) yp = (y(i)*expnt(i) + y(j)*expnt(j))/(expnt(i)+expnt(j)) zp = (z(i)*expnt(i) + z(j)*expnt(j))/(expnt(i)+expnt(j)) xq = (x(k)*expnt(k) + x(l)*expnt(l))/(expnt(k)+expnt(l)) yq = (y(k)*expnt(k) + y(l)*expnt(l))/(expnt(k)+expnt(l)) zq = (z(k)*expnt(k) + z(l)*expnt(l))/(expnt(k)+expnt(l)) rpq2 = (xp-xq)**2 + (yp-yq)**2 + (zp-zq)**2 c call f0(f0val, fac*rpq2) value = (2.0d0 * pi**2.5d0 / denom) * exijkl * f0val * $ rnorm(i)*rnorm(j)*rnorm(k)*rnorm(l) c end subroutine diagon(fock, orbs, evals, work, tester, iter) implicit double precision (a-h, o-z) include 'cscf.h' dimension fock(nnbfn), orbs(nbfn, nbfn), evals(nbfn), $ work(nbfn*nbfn) dimension ilifq(nbfn), work2(nbfn*nbfn) c do 10 i = 1, nbfn ilifq(i) = (i-1)*nbfn 10 continue c c transform fock matrix from AO to MO basis c call zsqua(nbfn, fock, work) call dgemm('n', 'n', nbfn, nbfn, nbfn, 1.0d0, $ work, nbfn, orbs, nbfn, 0.0d0, work2, nbfn) call dgemm('t', 'n', nbfn, nbfn, nbfn, 1.0d0, $ orbs, nbfn, work2, nbfn, 0.0d0, work, nbfn) call zfold(nbfn, fock, work) c iop1 = 1 iop2 = 2 tester = 0.0d0 do 20 i = 2, nbfn do 30 j = 1, i-1 tester = max(tester, abs(fock(iky(i)+j))) 30 continue 20 continue c if (tester.gt.0.3d0) then shift = 0.3d0 else if (nbfn .gt. 60) then shift = 0.1d0 else shift = 0.0d0 endif endif if (iter.ge.2) then do 40 i = nocc+1, nbfn fock(iky(i)+i) = fock(iky(i)+i) + shift 40 continue endif thresh = min(0.0001d0,max(1.0d-12,tester*0.01d0)) call jacobi(fock, iky, nbfn, orbs, ilifq, nbfn, evals, iop1, * iop2,thresh) if (iter.ge.2) then do 50 i = nocc+1, nbfn evals(i) = evals(i) - shift 50 continue endif c end subroutine makeob(orbs, work) implicit double precision (a-h, o-z) include 'cscf.h' include 'msgtypesf.h' dimension orbs(nbfn,nbfn), work(nbfn,nbfn) dimension tmp1(nbfn), tmp2(nbfn) c c generate set of orthonormal vectors by orthoging twice c a set of random vectors ... don't do this at home! c ... should really diagonalize the overlap to get sym adaption c if (nodeid().eq.0) then call srand48(12345) do 10 i = 1, nbfn do 20 j = 1, nbfn work(j,i) = s(i,j) orbs(j,i) = drand48(0) 20 continue 10 continue call orthv2(nbfn, orbs, work, tmp1, tmp2) call orthv2(nbfn, orbs, work, tmp1, tmp2) endif call brdcst(99+MSGDBL, orbs, mdtob(nbfn*nbfn), 0) c end subroutine denges(dens, work) implicit double precision (a-h, o-z) include 'cscf.h' dimension dens(nnbfn), work(nbfn, nbfn) c c Form guess density from superposition of atomic densities in the AO c basis set ... instead of doing the atomic SCF hardwire for this c small basis set for the Be atom. c dimension atdens(15,15) data atdens/ $ 0.000002,0.000027,0.000129,0.000428,0.000950,0.001180, $ 0.000457,-0.000270,-0.000271,0.000004,0.000004,0.000004, $ 0.000004,0.000004,0.000004,0.000027,0.000102,0.000987, $ 0.003269,0.007254,0.009007,0.003492,-0.002099,-0.002108, $ 0.000035,0.000035,0.000035,0.000035,0.000035,0.000035, $ 0.000129,0.000987,0.002381,0.015766,0.034988,0.043433, $ 0.016835,-0.010038,-0.010082,0.000166,0.000166,0.000166, $ 0.000166,0.000166,0.000166,0.000428,0.003269,0.015766, $ 0.026100,0.115858,0.144064,0.055967,-0.035878,-0.035990, $ 0.000584,0.000584,0.000584,0.000584,0.000584,0.000584, $ 0.000950,0.007254,0.034988,0.115858,0.128586,0.320120, $ 0.124539,-0.083334,-0.083536,0.001346,0.001346,0.001346, $ 0.001346,0.001346,0.001346,0.001180,0.009007,0.043433, $ 0.144064,0.320120,0.201952,0.159935,-0.162762,-0.162267, $ 0.002471,0.002471,0.002471,0.002471,0.002471,0.002471, $ 0.000457,0.003492,0.016835,0.055967,0.124539,0.159935, $ 0.032378,-0.093780,-0.093202,0.001372,0.001372,0.001372, $ 0.001372,0.001372,0.001372,-0.000270,-0.002099,-0.010038, $ -0.035878,-0.083334,-0.162762,-0.093780,0.334488,0.660918, $ -0.009090,-0.009090,-0.009090,-0.009090,-0.009090,-0.009090, $ -0.000271,-0.002108,-0.010082,-0.035990,-0.083536,-0.162267, $ -0.093202,0.660918,0.326482,-0.008982,-0.008982,-0.008981, $ -0.008981,-0.008981,-0.008982,0.000004,0.000035,0.000166, $ 0.000584,0.001346,0.002471,0.001372,-0.009090,-0.008982, $ 0.000062,0.000124,0.000124,0.000124,0.000124,0.000124, $ 0.000004,0.000035,0.000166,0.000584,0.001346,0.002471, $ 0.001372,-0.009090,-0.008982,0.000124,0.000062,0.000124, $ 0.000124,0.000124,0.000124,0.000004,0.000035,0.000166, $ 0.000584,0.001346,0.002471,0.001372,-0.009090,-0.008981, $ 0.000124,0.000124,0.000062,0.000124,0.000124,0.000124, $ 0.000004,0.000035,0.000166,0.000584,0.001346,0.002471, $ 0.001372,-0.009090,-0.008981,0.000124,0.000124,0.000124, $ 0.000062,0.000124,0.000124,0.000004,0.000035,0.000166, $ 0.000584,0.001346,0.002471,0.001372,-0.009090,-0.008981, $ 0.000124,0.000124,0.000124,0.000124,0.000062,0.000124, $ 0.000004,0.000035,0.000166,0.000584,0.001346,0.002471, $ 0.001372,-0.009090,-0.008982,0.000124,0.000124,0.000124, $ 0.000124,0.000124,0.000062/ c call dfill(nbfn*nbfn,0.0d0,work,1) do 10 iat = 1,natom ioff = (iat-1)*15 do 20 i = 1,15 do 30 j = 1,15 work(ioff+j,ioff+i) = atdens(j,i)*0.5d0 30 continue 20 continue 10 continue c call zfold(nbfn,dens,work) c end ga-5.9.2/tcgmsg/examples/scfblas.f000066400000000000000000000607221500715745200170210ustar00rootroot00000000000000 SUBROUTINE DGEMV ( TRANS, M, N, ALPHA, A, LDA, X, INCX, $ BETA, Y, INCY ) * .. Scalar Arguments .. C$Id: scfblas.f,v 1.2 1995-02-02 23:24:29 d3g681 Exp $ DOUBLE PRECISION ALPHA, BETA INTEGER INCX, INCY, LDA, M, N CHARACTER*1 TRANS * .. Array Arguments .. DOUBLE PRECISION A( LDA, * ), X( * ), Y( * ) * .. * * Purpose * ======= * * DGEMV performs one of the matrix-vector operations * * y := alpha*A*x + beta*y, or y := alpha*A'*x + beta*y, * * where alpha and beta are scalars, x and y are vectors and A is an * m by n matrix. * * Parameters * ========== * * TRANS - CHARACTER*1. * On entry, TRANS specifies the operation to be performed as * follows: * * TRANS = 'N' or 'n' y := alpha*A*x + beta*y. * * TRANS = 'T' or 't' y := alpha*A'*x + beta*y. * * TRANS = 'C' or 'c' y := alpha*A'*x + beta*y. * * Unchanged on exit. * * M - INTEGER. * On entry, M specifies the number of rows of the matrix A. * M must be at least zero. * Unchanged on exit. * * N - INTEGER. * On entry, N specifies the number of columns of the matrix A. * N must be at least zero. * Unchanged on exit. * * ALPHA - DOUBLE PRECISION. * On entry, ALPHA specifies the scalar alpha. * Unchanged on exit. * * A - DOUBLE PRECISION array of DIMENSION ( LDA, n ). * Before entry, the leading m by n part of the array A must * contain the matrix of coefficients. * Unchanged on exit. * * LDA - INTEGER. * On entry, LDA specifies the first dimension of A as declared * in the calling (sub) program. LDA must be at least * max( 1, m ). * Unchanged on exit. * * X - DOUBLE PRECISION array of DIMENSION at least * ( 1 + ( n - 1 )*abs( INCX ) ) when TRANS = 'N' or 'n' * and at least * ( 1 + ( m - 1 )*abs( INCX ) ) otherwise. * Before entry, the incremented array X must contain the * vector x. * Unchanged on exit. * * INCX - INTEGER. * On entry, INCX specifies the increment for the elements of * X. INCX must not be zero. * Unchanged on exit. * * BETA - DOUBLE PRECISION. * On entry, BETA specifies the scalar beta. When BETA is * supplied as zero then Y need not be set on input. * Unchanged on exit. * * Y - DOUBLE PRECISION array of DIMENSION at least * ( 1 + ( m - 1 )*abs( INCY ) ) when TRANS = 'N' or 'n' * and at least * ( 1 + ( n - 1 )*abs( INCY ) ) otherwise. * Before entry with BETA non-zero, the incremented array Y * must contain the vector y. On exit, Y is overwritten by the * updated vector y. * * INCY - INTEGER. * On entry, INCY specifies the increment for the elements of * Y. INCY must not be zero. * Unchanged on exit. * * * Level 2 Blas routine. * * -- Written on 22-October-1986. * Jack Dongarra, Argonne National Lab. * Jeremy Du Croz, Nag Central Office. * Sven Hammarling, Nag Central Office. * Richard Hanson, Sandia National Labs. * * * .. Parameters .. DOUBLE PRECISION ONE , ZERO PARAMETER ( ONE = 1.0D+0, ZERO = 0.0D+0 ) * .. Local Scalars .. DOUBLE PRECISION TEMP INTEGER I, INFO, IX, IY, J, JX, JY, KX, KY, LENX, LENY * .. External Functions .. LOGICAL LSAME EXTERNAL LSAME * .. External Subroutines .. EXTERNAL XERBLA * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Executable Statements .. * * Test the input parameters. * INFO = 0 IF ( .NOT.LSAME( TRANS, 'N' ).AND. $ .NOT.LSAME( TRANS, 'T' ).AND. $ .NOT.LSAME( TRANS, 'C' ) )THEN INFO = 1 ELSE IF( M.LT.0 )THEN INFO = 2 ELSE IF( N.LT.0 )THEN INFO = 3 ELSE IF( LDA.LT.MAX( 1, M ) )THEN INFO = 6 ELSE IF( INCX.EQ.0 )THEN INFO = 8 ELSE IF( INCY.EQ.0 )THEN INFO = 11 END IF IF( INFO.NE.0 )THEN CALL XERBLA( 'DGEMV ', INFO ) RETURN END IF * * Quick return if possible. * IF( ( M.EQ.0 ).OR.( N.EQ.0 ).OR. $ ( ( ALPHA.EQ.ZERO ).AND.( BETA.EQ.ONE ) ) ) $ RETURN * * Set LENX and LENY, the lengths of the vectors x and y, and set * up the start points in X and Y. * IF( LSAME( TRANS, 'N' ) )THEN LENX = N LENY = M ELSE LENX = M LENY = N END IF IF( INCX.GT.0 )THEN KX = 1 ELSE KX = 1 - ( LENX - 1 )*INCX END IF IF( INCY.GT.0 )THEN KY = 1 ELSE KY = 1 - ( LENY - 1 )*INCY END IF * * Start the operations. In this version the elements of A are * accessed sequentially with one pass through A. * * First form y := beta*y. * IF( BETA.NE.ONE )THEN IF( INCY.EQ.1 )THEN IF( BETA.EQ.ZERO )THEN DO 10, I = 1, LENY Y( I ) = ZERO 10 CONTINUE ELSE DO 20, I = 1, LENY Y( I ) = BETA*Y( I ) 20 CONTINUE END IF ELSE IY = KY IF( BETA.EQ.ZERO )THEN DO 30, I = 1, LENY Y( IY ) = ZERO IY = IY + INCY 30 CONTINUE ELSE DO 40, I = 1, LENY Y( IY ) = BETA*Y( IY ) IY = IY + INCY 40 CONTINUE END IF END IF END IF IF( ALPHA.EQ.ZERO ) $ RETURN IF( LSAME( TRANS, 'N' ) )THEN * * Form y := alpha*A*x + y. * JX = KX IF( INCY.EQ.1 )THEN DO 60, J = 1, N IF( X( JX ).NE.ZERO )THEN TEMP = ALPHA*X( JX ) DO 50, I = 1, M Y( I ) = Y( I ) + TEMP*A( I, J ) 50 CONTINUE END IF JX = JX + INCX 60 CONTINUE ELSE DO 80, J = 1, N IF( X( JX ).NE.ZERO )THEN TEMP = ALPHA*X( JX ) IY = KY DO 70, I = 1, M Y( IY ) = Y( IY ) + TEMP*A( I, J ) IY = IY + INCY 70 CONTINUE END IF JX = JX + INCX 80 CONTINUE END IF ELSE * * Form y := alpha*A'*x + y. * JY = KY IF( INCX.EQ.1 )THEN DO 100, J = 1, N TEMP = ZERO DO 90, I = 1, M TEMP = TEMP + A( I, J )*X( I ) 90 CONTINUE Y( JY ) = Y( JY ) + ALPHA*TEMP JY = JY + INCY 100 CONTINUE ELSE DO 120, J = 1, N TEMP = ZERO IX = KX DO 110, I = 1, M TEMP = TEMP + A( I, J )*X( IX ) IX = IX + INCX 110 CONTINUE Y( JY ) = Y( JY ) + ALPHA*TEMP JY = JY + INCY 120 CONTINUE END IF END IF * RETURN * * End of DGEMV . * END *deck dblas3 *mdc*on fortran_dblas3 c * ************************************************************************ * * File of the DOUBLE PRECISION Level-3 BLAS. * ========================================== * * SUBROUTINE DGEMM ( TRANSA, TRANSB, M, N, K, * $ ALPHA, A, LDA, B, LDB, BETA, C, LDC ) * * SUBROUTINE DSYMM ( SIDE, UPLO, TRANSB, M, N, * $ ALPHA, A, LDA, B, LDB, BETA, C, LDC ) * * SUBROUTINE DSYRK ( UPLO, TRANSA, N, K, * $ ALPHA, A, LDA, BETA, C, LDC ) * * SUBROUTINE DTRMM ( SIDE, UPLO, TRANSA, DIAG, M, N, * $ A, LDA, B, LDB ) * * SUBROUTINE DTRSM ( SIDE, UPLO, TRANSA, DIAG, M, N, * $ A, LDA, B, LDB ) * * See: * * Dongarra J. J., Du Croz J. J., Duff I. and Hammarling S. * A Proposal for a set of Level 3 Basic Linear Algebra * Subprograms. Technical Memorandum No.88, Mathematics and * Computer Science Division, Argonne National Laboratory, * 9700 South Cass Avenue, Argonne, Illinois 60439. * * ************************************************************************ * SUBROUTINE DGEMM ( TRANSA, TRANSB, M, N, K, ALPHA, A, LDA, B, LDB, $ BETA, C, LDC ) * .. Scalar Arguments .. DOUBLE PRECISION ALPHA, BETA INTEGER M, N, K, LDA, LDB, LDC CHARACTER*1 TRANSA, TRANSB * .. Array Arguments .. DOUBLE PRECISION A( LDA, * ), B( LDB, * ), C( LDC, * ) * .. * * Purpose * ======= * * DGEMM performs one of the matrix-matrix operations * * C := alpha*op( A )*op( B ) + beta*C, * * where op( X ) is one of * * op( X ) = X or op( X ) = X', * * alpha and beta are scalars, and A, B and C are matrices, with op( A ) * an m by k matrix, op( B ) a k by n matrix and C an m by n matrix. * * Parameters * ========== * * TRANSA - CHARACTER*1. * On entry, TRANSA specifies the form of op( A ) to be used in * the matrix multiplication as follows: * * TRANSA = 'N' or 'n', op( A ) = A. * * TRANSA = 'T' or 't', op( A ) = A'. * * TRANSA = 'C' or 'c', op( A ) = A'. * * Unchanged on exit. * * TRANSB - CHARACTER*1. * On entry, TRANSB specifies the form of op( B ) to be used in * the matrix multiplication as follows: * * TRANSA = 'N' or 'n', op( B ) = B. * * TRANSA = 'T' or 't', op( B ) = B'. * * TRANSA = 'C' or 'c', op( B ) = B'. * * Unchanged on exit. * * M - INTEGER. * On entry, M specifies the number of rows of the matrix * op( A ) and of the matrix C. M must be at least zero. * Unchanged on exit. * * N - INTEGER. * On entry, N specifies the number of columns of the matrix * op( B ) and the number of columns of the matrix C. N must be * at least zero. * Unchanged on exit. * * K - INTEGER. * On entry, K specifies the number of columns of the matrix * op( A ) and the number of rows of the matrix op( B ). K must * be at least zero. * Unchanged on exit. * * ALPHA - DOUBLE PRECISION. * On entry, ALPHA specifies the scalar alpha. * Unchanged on exit. * * A - DOUBLE PRECISION array of DIMENSION ( LDA, ka ), where ka is * k when TRANSA = 'N' or 'n', and is m otherwise. * Before entry with TRANSA = 'N' or 'n', the leading m by k * part of the array A must contain the matrix A, otherwise * the leading k by m part of the array A must contain the * matrix A. * Unchanged on exit. * * LDA - INTEGER. * On entry, LDA specifies the first dimension of A as declared * in the calling (sub) program. When TRANSA = 'N' or 'n' then * LDA must be at least max( 1, m ), otherwise LDA must be at * least max( 1, k ). * Unchanged on exit. * * B - DOUBLE PRECISION array of DIMENSION ( LDB, kb ), where kb is * n when TRANSB = 'N' or 'n', and is k otherwise. * Before entry with TRANSB = 'N' or 'n', the leading k by n * part of the array B must contain the matrix B, otherwise * the leading n by k part of the array B must contain the * matrix B. * Unchanged on exit. * * LDB - INTEGER. * On entry, LDB specifies the first dimension of B as declared * in the calling (sub) program. When TRANSB = 'N' or 'n' then * LDB must be at least max( 1, k ), otherwise LDB must be at * least max( 1, n ). * Unchanged on exit. * * BETA - DOUBLE PRECISION. * On entry, BETA specifies the scalar beta. When BETA is * supplied as zero then C need not be set on input. * Unchanged on exit. * * C - DOUBLE PRECISION array of DIMENSION ( LDC, n ). * Before entry, the leading m by n part of the array C must * contain the matrix C, except when beta is zero, in which * case C need not be set on entry. * On exit, the array C is overwritten by the m by n matrix * ( alpha*op( A )*op( B ) + beta*C ). * * LDC - INTEGER. * On entry, LDC specifies the first dimension of C as declared * in the calling (sub) program. LDC must be at least * max( 1, m ). * Unchanged on exit. * * * Level 3 Blas routine. * * -- Written on 30-April-1987. * Sven Hammarling, Nag Central Office. * * * .. Parameters .. DOUBLE PRECISION ONE , ZERO PARAMETER ( ONE = 1.0D+0, ZERO = 0.0D+0 ) * .. Local Scalars .. INTEGER I, INFO, J, NCOLA, NROWA, NROWB LOGICAL NOTA, NOTB * .. External Functions .. LOGICAL LSAME EXTERNAL LSAME * .. External Subroutines .. EXTERNAL XERBLA, DGEMV * .. Intrinsic Functions .. INTRINSIC MAX * .. * .. Executable Statements .. * * Set NOTA and NOTB as true if A and B respectively are not * transposed, and set NROWA, NCOLA and NROWB as the number of rows * and columns of A and the number of rows of B respectively. * NOTA = LSAME( TRANSA, 'N' ) NOTB = LSAME( TRANSB, 'N' ) IF( NOTA )THEN NROWA = M NCOLA = K ELSE NROWA = K NCOLA = M END IF IF( NOTB )THEN NROWB = K ELSE NROWB = N END IF * * Test the input parameters. * INFO = 0 IF( ( .NOT.NOTA ).AND. $ ( .NOT.LSAME( TRANSA, 'T' ) ).AND. $ ( .NOT.LSAME( TRANSA, 'C' ) ) )THEN INFO = 1 ELSE IF( ( .NOT.NOTB ).AND. $ ( .NOT.LSAME( TRANSB, 'T' ) ).AND. $ ( .NOT.LSAME( TRANSB, 'C' ) ) )THEN INFO = 2 ELSE IF( M.LT.0 )THEN INFO = 3 ELSE IF( N.LT.0 )THEN INFO = 4 ELSE IF( K.LT.0 )THEN INFO = 5 ELSE IF( LDA.LT.MAX( 1, NROWA ) )THEN INFO = 8 ELSE IF( LDB.LT.MAX( 1, NROWB ) )THEN INFO = 10 ELSE IF( LDC.LT.MAX( 1, M ) )THEN INFO = 13 END IF IF( INFO.NE.0 )THEN CALL XERBLA( 'DGEMM ', INFO ) RETURN END IF * * Quick return if possible. * IF( ( M.EQ.0 ).OR.( N.EQ.0 ).OR. $ ( ( ( ALPHA.EQ.ZERO ).or.( K.EQ.0 ) ).AND.( BETA.EQ.ONE ) ) ) $ RETURN * * Start the operations. * IF( K.EQ.0 )THEN * * Form C := beta*C. * IF( BETA.EQ.ZERO )THEN DO 20, J = 1, N DO 10, I = 1, M C( I, J ) = ZERO 10 CONTINUE 20 CONTINUE ELSE DO 40, J = 1, N DO 30, I = 1, M C( I, J ) = BETA*C( I, J ) 30 CONTINUE 40 CONTINUE END IF ELSE IF( NOTB )THEN * * Form C := alpha*op( A )*B + beta*C. * DO 50, J = 1, N CALL DGEMV ( TRANSA, NROWA, NCOLA, $ ALPHA, A, LDA, B( 1, J ), 1, $ BETA, C( 1, J ), 1 ) 50 CONTINUE ELSE * * Form C := alpha*op( A )*B' + beta*C. * DO 60, J = 1, N CALL DGEMV ( TRANSA, NROWA, NCOLA, $ ALPHA, A, LDA, B( J, 1 ), LDB, $ BETA, C( 1, J ), 1 ) 60 CONTINUE END IF * RETURN * * End of DGEMM . * END LOGICAL FUNCTION LSAME ( CA, CB ) * .. Scalar Arguments .. CHARACTER*1 CA, CB * .. * * Purpose * ======= * * LSAME tests if CA is the same letter as CB regardless of case. * CB is assumed to be an upper case letter. LSAME returns .TRUE. if * CA is either the same as CB or the equivalent lower case letter. * * N.B. This version of the routine is only correct for ASCII code. * Installers must modify the routine for other character-codes. * * For EBCDIC systems the constant IOFF must be changed to -64. * For CDC systems using 6-12 bit representations, the system- * specific code in comments must be activated. * * Parameters * ========== * * CA - CHARACTER*1 * CB - CHARACTER*1 * On entry, CA and CB specify characters to be compared. * Unchanged on exit. * * * Auxiliary routine for Level 2 Blas. * * -- Written on 20-July-1986 * Richard Hanson, Sandia National Labs. * Jeremy Du Croz, Nag Central Office. * * .. Parameters .. INTEGER IOFF PARAMETER ( IOFF=32 ) * .. Intrinsic Functions .. INTRINSIC ICHAR * .. Executable Statements .. * * Test if the characters are equal * LSAME = CA .EQ. CB * * Now test for equivalence * IF ( .NOT.LSAME ) THEN LSAME = ICHAR(CA) - IOFF .EQ. ICHAR(CB) END IF * RETURN * * The following comments contain code for CDC systems using 6-12 bit * representations. * * .. Parameters .. * INTEGER ICIRFX * PARAMETER ( ICIRFX=62 ) * .. Scalar Arguments .. * CHARACTER*1 CB * .. Array Arguments .. * CHARACTER*1 CA(*) * .. Local Scalars .. * INTEGER IVAL * .. Intrinsic Functions .. * INTRINSIC ICHAR, CHAR * .. Executable Statements .. * * See if the first character in string CA equals string CB. * * LSAME = CA(1) .EQ. CB .AND. CA(1) .NE. CHAR(ICIRFX) * * IF (LSAME) RETURN * * The characters are not identical. Now check them for equivalence. * Look for the 'escape' character, circumflex, followed by the * letter. * * IVAL = ICHAR(CA(2)) * IF (IVAL.GE.ICHAR('A') .AND. IVAL.LE.ICHAR('Z')) THEN * LSAME = CA(1) .EQ. CHAR(ICIRFX) .AND. CA(2) .EQ. CB * END IF * * RETURN * * End of LSAME. * END SUBROUTINE XERBLA ( SRNAME, INFO ) * .. Scalar Arguments .. INTEGER INFO CHARACTER*6 SRNAME * .. * * Purpose * ======= * * XERBLA is an error handler for the Level 2 BLAS routines. * * It is called by the Level 2 BLAS routines if an input parameter is * invalid. * * Installers should consider modifying the STOP statement in order to * call system-specific exception-handling facilities. * * Parameters * ========== * * SRNAME - CHARACTER*6. * On entry, SRNAME specifies the name of the routine which * called XERBLA. * * INFO - INTEGER. * On entry, INFO specifies the position of the invalid * parameter in the parameter-list of the calling routine. * * * Auxiliary routine for Level 2 Blas. * * Written on 20-July-1986. * * .. Executable Statements .. * WRITE (*,99999) SRNAME, INFO * STOP * 99999 FORMAT ( ' ** On entry to ', A6, ' parameter number ', I2, $ ' had an illegal value' ) * * End of XERBLA. * END double precision function ddot(n,dx,incx,dy,incy) c c forms the dot product of two vectors. c uses unrolled loops for increments equal to one. c jack dongarra, linpack, 3/11/78. c double precision dx(1),dy(1),dtemp integer i,incx,incy,ix,iy,m,mp1,n c ddot = 0.0d0 dtemp = 0.0d0 if(n.le.0)return if(incx.eq.1.and.incy.eq.1)go to 20 c c code for unequal increments or equal increments c not equal to 1 c ix = 1 iy = 1 if(incx.lt.0)ix = (-n+1)*incx + 1 if(incy.lt.0)iy = (-n+1)*incy + 1 do 10 i = 1,n dtemp = dtemp + dx(ix)*dy(iy) ix = ix + incx iy = iy + incy 10 continue ddot = dtemp return c c code for both increments equal to 1 c c c clean-up loop c 20 m = mod(n,5) if( m .eq. 0 ) go to 40 do 30 i = 1,m dtemp = dtemp + dx(i)*dy(i) 30 continue if( n .lt. 5 ) go to 60 40 mp1 = m + 1 do 50 i = mp1,n,5 dtemp = dtemp + dx(i)*dy(i) + dx(i + 1)*dy(i + 1) + * dx(i + 2)*dy(i + 2) + dx(i + 3)*dy(i + 3) + dx(i + 4)*dy(i + 4) 50 continue 60 ddot = dtemp return end subroutine daxpy(n,da,dx,incx,dy,incy) c c constant times a vector plus a vector. c uses unrolled loops for increments equal to one. c jack dongarra, linpack, 3/11/78. c double precision dx(1),dy(1),da integer i,incx,incy,ix,iy,m,mp1,n c if(n.le.0)return if (da .eq. 0.0d0) return if(incx.eq.1.and.incy.eq.1)go to 20 c c code for unequal increments or equal increments c not equal to 1 c ix = 1 iy = 1 if(incx.lt.0)ix = (-n+1)*incx + 1 if(incy.lt.0)iy = (-n+1)*incy + 1 do 10 i = 1,n dy(iy) = dy(iy) + da*dx(ix) ix = ix + incx iy = iy + incy 10 continue return c c code for both increments equal to 1 c c c clean-up loop c 20 m = mod(n,4) if( m .eq. 0 ) go to 40 do 30 i = 1,m dy(i) = dy(i) + da*dx(i) 30 continue if( n .lt. 4 ) return 40 mp1 = m + 1 do 50 i = mp1,n,4 dy(i) = dy(i) + da*dx(i) dy(i + 1) = dy(i + 1) + da*dx(i + 1) dy(i + 2) = dy(i + 2) + da*dx(i + 2) dy(i + 3) = dy(i + 3) + da*dx(i + 3) 50 continue return end subroutine dscal(n,da,dx,incx) c c scales a vector by a constant. c uses unrolled loops for increment equal to one. c jack dongarra, linpack, 3/11/78. c double precision da,dx(1) integer i,incx,m,mp1,n,nincx c if(n.le.0)return if(incx.eq.1)go to 20 c c code for increment not equal to 1 c nincx = n*incx do 10 i = 1,nincx,incx dx(i) = da*dx(i) 10 continue return c c code for increment equal to 1 c c c clean-up loop c 20 m = mod(n,5) if( m .eq. 0 ) go to 40 do 30 i = 1,m dx(i) = da*dx(i) 30 continue if( n .lt. 5 ) return 40 mp1 = m + 1 do 50 i = mp1,n,5 dx(i) = da*dx(i) dx(i + 1) = da*dx(i + 1) dx(i + 2) = da*dx(i + 2) dx(i + 3) = da*dx(i + 3) dx(i + 4) = da*dx(i + 4) 50 continue return end subroutine dcopy(n,a,ia,b,ib) implicit real*8 (a-h,o-z) dimension a(ia,*), b(ib,*) c c copy a into b c do 10 i = 1,n b(1,i) = a(1,i) 10 continue c end subroutine drot(n,sx,incx,sy,incy,sc,ss) implicit real*8 (a-h,o-z) c c b l a s subprogram c description of parameters c c --input-- c n number of elements in input vector(s) c sx double precision vector with n elements c incx storage spacing between elements of sx c sy single precision vector with n elements c incy storage spacing between elements of sy c sc element of rotation matrix c ss element of rotation matrix c c --output-- c sx rotated vector sx (unchanged if n .le. 0) c sy rotated vector sy (unchanged if n .le. 0) c c multiply the 2 x 2 matrix ( sc ss) times the 2 x n matrix (sx**t) c (-ss sc) (sy**t) c where **t indicates transpose. the elements of sx are in c sx(lx+i*incx), i = 0 to n-1, where lx = 1 if incx .ge. 0, else c lx = (-incx)*n, and similarly for sy using ly and incy. dimension sx(*),sy(*) data zero,one/0.d0,1.d0/ if(n .le. 0 .or. (ss .eq. zero .and. sc .eq. one)) go to 40 if(.not. (incx .eq. incy .and. incx .gt. 0)) go to 20 c nsteps=incx*n do 10 i=1,nsteps,incx w=sx(i) z=sy(i) sx(i)=sc*w+ss*z sy(i)=-ss*w+sc*z 10 continue go to 40 c 20 continue kx=1 ky=1 c if(incx .lt. 0) kx=1-(n-1)*incx if(incy .lt. 0) ky=1-(n-1)*incy c do 30 i=1,n w=sx(kx) z=sy(ky) sx(kx)=sc*w+ss*z sy(ky)=-ss*w+sc*z kx=kx+incx ky=ky+incy 30 continue 40 continue c return end ga-5.9.2/tcgmsg/examples/timer.f000066400000000000000000000010621500715745200165140ustar00rootroot00000000000000 double precision function timer() c c return the time since the last call to timer. c c must be initialized by calling once and throwing away the c value c ... use cpu time on multi-user machines c ... use elapsed time on dedicated or single user machines. c *mdc*if unix * real*4 dtime, tt(2) * timer = dble(dtime(tt)) *mdc*elseif tcgmsg C$Id: timer.f,v 1.2 1995-02-02 23:24:33 d3g681 Exp $ save mlast data mlast/0/ m = mtime() timer = dble(m - mlast) * 0.01d0 mlast = m *mdc*endif c end ga-5.9.2/tcgmsg/examples/trace.out000066400000000000000000000004651500715745200170620ustar00rootroot00000000000000#*********** PROGRAM STACKTRACE ************** # text address function cp func start # ------------ ----------- ---------- 0x1176840 0x81b8858 0x1175fd0 0x1174ea8 0x81b8498 0x1174900 0x119a208 0x81bdd10 0x119a000 #*************** END STACKTRACE ************** ga-5.9.2/tcgmsg/examples/xpix.shar000066400000000000000000000407041500715745200171020ustar00rootroot00000000000000 # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # check.bitmap makefile xpix.c echo x - check.bitmap sed -e 's/^X//' > "check.bitmap" << '//E*O*F check.bitmap//' X#define check_width 8 X#define check_height 8 Xstatic char check_bits[] = { X 0x40, 0x20, 0x20, 0x10, 0x10, 0x09, 0x0a, 0x04}; //E*O*F check.bitmap// echo x - makefile sed -e 's/^X//' > "makefile" << '//E*O*F makefile//' X# X CFLAGS = -O4 -DSUN -dalign -I/msrc/apps/X11R5/include X XLIBS = -L/msrc/apps/X11R5/lib -lXaw -lXmu -lXt -lX11 -lXau -lXdmcp -lXext X X#SUN R4 X# XLIBS = -lXaw -lXmu -lXt -lX11 -lXau -lXdmcp -lXext -lXinput X Xxpix: xpix.o X cc $(CFLAGS) -o xpix xpix.o $(XLIBS) -lm //E*O*F makefile// echo x - xpix.c sed -e 's/^X//' > "xpix.c" << '//E*O*F xpix.c//' X#include X#include X#include X#include X X#include "check.bitmap" X X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X Xextern char *malloc(); X X/* Globals needed for display etc. */ X Xstatic Widget top_level_widget, box_widget, file_menu_button, start_button, X canvas_widget, title_widget, grid_data_widget, X scroll_widget, interval_widget, file_menu; X#define n_file_menu_entries 3 Xstatic String file_menu_entries[] = {"Tail ","Cycle ","Quit"}; Xstatic Widget file_menu_widgets[n_file_menu_entries]; Xstatic Pixmap check_mark; Xstatic Arg arg[25]; Xstatic Display *display; Xstatic Window window; Xstatic int screen, depth; Xstatic Visual *visual; Xstatic XtIntervalId timer; Xstatic XImage *image; Xstatic u_char *pict; Xstatic GC gc; Xstatic char title[80]; Xstatic char grid_data[80]; Xstatic char interval_string[10]; X Xstatic long first_time = True; /* Used to set scroll bar on first expose */ Xstatic long interval_max = 2000; Xstatic long interval = 500; /* 0.5s between exposures by default */ X Xstatic int tail_mode = False; /* By default cycle thru frames X instead of tailing a file */ X X#define TCG_MAX(a,b) (((a)>(b)) ? (a) : (b)) X#define TCG_MIN(a,b) (((a)<(b)) ? (a) : (b)) X X#define MAX_COL 64 Xstatic u_char cmap[MAX_COL]; X X Xstatic int n_grid = 0; /* The number of the current grid */ Xstatic int grid_size; /* The size of the current grid */ Xstatic int max_grid_size=-1; /* Size of the largest grid */ X X#define PICT_SIZE max_grid_size X Xstatic u_char *grid; Xstatic int working = False; X Xvoid Error(string, integer) X char *string; X int integer; X{ X (void) fflush(stdout); X (void) fprintf(stderr,"\n\nError was called.\n"); X (void) fprintf(stderr,string); X (void) fprintf(stderr," %d (%#x).\n",integer,integer); X exit(1); X} X X/*ARGSUSED*/ Xvoid FileMenuCallback(w, pane_num, data) X Widget w; X int pane_num; X caddr_t data; X{ X switch (pane_num) { X case 0: /* Tail mode */ X tail_mode = True; X XtSetArg(arg[0], XtNrightBitmap, check_mark); /* Check tail */ X XtSetValues(file_menu_widgets[0], arg, 1); X XtSetArg(arg[0], XtNrightBitmap, NULL); /* Un-check cycle */ X XtSetValues(file_menu_widgets[1], arg, 1); X break; X X case 1: /* Cycle mode */ X tail_mode = False; X XtSetArg(arg[0], XtNrightBitmap, check_mark); /* Check cycle */ X XtSetValues(file_menu_widgets[1], arg, 1); X XtSetArg(arg[0], XtNrightBitmap, NULL); /* Un-check tail */ X XtSetValues(file_menu_widgets[0], arg, 1); X break; X X case 2: /* Quit */ X exit(0); X break; X X default: X Error("Unknown menu item", pane_num); X } X} X Xint GetNextGrid(grid) X u_char *grid; X{ X int n, junk, nread; X u_char *temp; X X if (fread(&grid_size, sizeof(int), 1, stdin) != 1) { X clearerr(stdin); X X if (tail_mode) { X /* In this mode just wait for next time out and try again then */ X return False; X } X else { X /* Cycling ... retry at the front of the file */ X n_grid = 0; X fseek(stdin, 0, 0); X (void) fread(&junk, sizeof(int), 1, stdin); /* Ignore first word */ X X if (fread(&grid_size, sizeof(int), 1, stdin) != 1) { X (void) fprintf(stderr, X "GetNextGrid: Failed to read first grid size\n"); X return False; X } X } X } X X grid_size = ntohl(grid_size); /* Convert to machine format */ X X /* Have read the grid size so there must be a grid on the way */ X X n = grid_size*grid_size; X temp = grid; X X while(n) { X nread = fread(temp, 1, n, stdin); X if (!nread) { X clearerr(stdin); X sleep(1); /* Don't be too agressive */ X } X temp += nread; X n -= nread; X } X n_grid++; X return True; X} X Xvoid DrawGrid(grid) X u_char *grid; X{ X register int scale = max_grid_size/grid_size; X register int i,j,k,l,npix,index; X register u_char *from, *to, *tempk, *tempkl, value; X X from = grid; to = pict; /* For clarity and convenience */ X X if (scale > 1) { X for (i=0; iwidth, image->height); X (void) sprintf(grid_data, "Grid %d (%d x %d)", X n_grid, grid_size, grid_size); X X XtSetArg(arg[0], XtNlabel, grid_data); /* Reset the grid data */ X XtSetValues(grid_data_widget,arg,1); X XFlush(display); X} X Xvoid DisplayIntervalValue() X{ X (void) sprintf(interval_string, "%4d ms", interval); X XtSetArg(arg[0], XtNlabel, interval_string); X XtSetValues(interval_widget,arg,1); X} X X/*ARGSUSED*/ Xvoid ScrollProc(scrollbar, data, position) X Widget scrollbar; X caddr_t data; X caddr_t position; X/* X Called when the left or right buttons are used to step the X scrollbar left or right. We have the responsibility of X moving the scrollbar. X*/ X{ X Dimension length; X float fraction, shown; X X /* Get the scrollbar length and move the scroll bar */ X X XtSetArg(arg[0], XtNlength, &length); X XtGetValues(scrollbar, arg, 1); X fraction = ((int) position)/ (double) length; X X interval -= fraction*0.05*interval_max; X interval = TCG_MIN(interval, interval_max); X interval = TCG_MAX(interval, 1); X X fraction = (float) interval/ (float) interval_max; X shown = -1.0; X X DisplayIntervalValue(); X XawScrollbarSetThumb(scrollbar, fraction, shown); X} X X/*ARGSUSED*/ Xvoid JumpProc(scrollbar, data, fraction_ptr) X Widget scrollbar; X caddr_t data; X caddr_t fraction_ptr; X/* X Called when the middle button is used to drag to X the scrollbar. The scrollbar is moved for us. X*/ X{ X float fraction = *(float *) fraction_ptr; X X interval = fraction*interval_max; X interval = TCG_MIN(interval, interval_max); X interval = TCG_MAX(interval, 1); X X DisplayIntervalValue(); X} X X/*ARGSUSED*/ Xvoid Exposed(widget, data, event) X Widget widget; X caddr_t data; X XEvent *event; X{ X /* Now we are exposed we can draw ... only if we have a grid! */ X X if (first_time) { X /* Cannot seem to set this before now ? */ X ScrollProc(scroll_widget, NULL, 0); X first_time = False; X } X X if (n_grid) X DrawGrid(grid); X} X X/*ARGSUSED*/ Xvoid TimeOutCallback(data) X caddr_t data; X{ X /* If there's another grid available draw it */ X X if (GetNextGrid(grid)) X DrawGrid(grid); X X /* Always restore the call back */ X X timer = XtAddTimeOut(interval, TimeOutCallback, NULL); X X} X X/*ARGSUSED*/ Xvoid StartStop(widget, data, event) X Widget widget; X caddr_t data; X XEvent *event; X{ X /* Toggle propagation of display */ X X if (working) { X XtRemoveTimeOut(timer); X working = False; X XtSetArg(arg[0], XtNlabel, "Start"); /* Reset button label */ X XtSetValues(start_button,arg,1); X XFlush(display); X } X else { X XtSetArg(arg[0], XtNlabel, "Stop"); /* Reset button label */ X XtSetValues(start_button,arg,1); X timer = XtAddTimeOut(interval, TimeOutCallback, NULL); X working = True; X XFlush(display); X } X} X Xvoid HSVtoRGB(h, s, v, r, g, b) X double h, s, v, *r, *g, *b; X/* X hue (0-360), s&v (0-1), r&g&b (0-1) X*/ X{ X int ih; X double rh, x, y, z; X X /* Zero saturation means gray */ X X if (s < 0.0001) { X *r = v; *g = v; *b = v; X return; X } X X /* Put hue in [0,6) */ X X if (h > 359.999) X h = 0.0; X else X h = h/60.0; X X ih = h; rh = h - ih; /* Integer and fractional parts */ X X x = v*(1.0 - s); X y = v*(1.0-s*rh); X z = v*(1.0-s*(1.0-rh)); X X switch (ih) { X case 0: X *r = v; *g = z; *b = x; break; X case 1: X *r = y; *g = v; *b = x; break; X case 2: X *r = x; *g = v; *b = z; break; X case 3: X *r = x; *g = y; *b = v; break; X case 4: X *r = z; *g = x; *b = v; break; X case 5: X *r = v; *g = x; *b = y; break; X default: X Error("HLStoRGB: invalid hue", ih); X } X} X Xvoid Setcmap() X/* X Make the color map ... scheme base on linear interpolation of hsv. X*/ X{ X int i; X XColor color; X Colormap colormap; X double scale = 1.0 / ((double) (MAX_COL-1)); X double hue, saturation, value, red, green, blue; X X colormap = DefaultColormap(display, screen); X X for (i=0; i #include #include "farg.h" #include "typesf2c.h" #include "srftoc.h" #include "sndrcv.h" #define _BRDCST_ F77_FUNC(brdcst, BRDCST) #define _DGOP_ F77_FUNC(dgop, DGOP) #define _DRAND48_ F77_FUNC(drand48, DRAND48) #define _IGOP_ F77_FUNC(igop, IGOP) #define _LLOG_ F77_FUNC(llog, LLOG) #define _MDTOB_ F77_FUNC(mdtob, MDTOB) #define _MDTOI_ F77_FUNC(mdtoi, MDTOI) #define _MITOB_ F77_FUNC(mitob, MITOB) #define _MITOD_ F77_FUNC(mitod, MITOD) #define _MTIME_ F77_FUNC(mtime, MTIME) #define _NICEFTN_ F77_FUNC(niceftn, NICEFTN) #define _NNODES_ F77_FUNC(nnodes, NNODES) #define _NODEID_ F77_FUNC(nodeid, NODEID) #define _NXTVAL_ F77_FUNC(nxtval, NXTVAL) #define _PARERR_ F77_FUNC(parerr, PARERR) #define _PBEGINF_ F77_FUNC(pbeginf, PBEGINF) #define _PBGINF_ F77_FUNC(pbginf, PBGINF) #define _PEND_ F77_FUNC(pend, PEND) #define _PFCOPY_ F77_FUNC(pfcopy, PFCOPY) #define _PFILECOPY_ F77_FUNC(pfilecopy, PFILECOPY) #define _PROBE_ F77_FUNC(probe, PROBE) #define _RCV_ F77_FUNC(rcv, RCV) #define _SETDBG_ F77_FUNC(setdbg, SETDBG) #define _SND_ F77_FUNC(snd, SND) #define _SRAND48_ F77_FUNC(srand48, SRAND48) #define _STATS_ F77_FUNC(stats, STATS) #define _SYNCH_ F77_FUNC(synch, SYNCH) #define _TCGREADY_ F77_FUNC(tcgready, TCGREADY) #define _TCGTIME_ F77_FUNC(tcgtime, TCGTIME) #define _WAITCOM_ F77_FUNC(waitcom, WAITCOM) void FATR _BRDCST_(Integer *type, void *buf, Integer *lenbuf, Integer *originator) { long atype = *type; long alenbuf = *lenbuf; long aoriginator = *originator; BRDCST_(&atype, buf, &alenbuf, &aoriginator); } void FATR _DGOP_(Integer *type, DoublePrecision *x, Integer *n, char *op, int oplen) { long atype = *type; long an = *n; double *ax; int i; ax = (double*)malloc(an * sizeof(double)); for (i=0; i 0) { sum = sum ^ *c++; } return sum; */ unsigned int sum = 0; unsigned int mask = 0xff; while (n-- > 0) { sum += (int) *c++; } sum = (sum + (sum>>8) + (sum>>16) + (sum>>24)) & mask; return (unsigned char) sum; } ga-5.9.2/tcgmsg/tcgmsg-mpi/clustercheck.c000066400000000000000000000025761500715745200203140ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #if HAVE_STDLIB_H # include #endif #if HAVE_STDIO_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_WINSOCK_H # include #endif #if HAVE_UNISTD_H # include #endif #define HOSTNAME_LEN 128 static char myname[HOSTNAME_LEN], rootname[HOSTNAME_LEN]; /** * return 1 if all processes are running on the same machine, 0 otherwise */ int single_cluster() { int rc,me,root=0,stat,global_stat,len; gethostname(myname, HOSTNAME_LEN-1); rc = MPI_Comm_rank(MPI_COMM_WORLD, &me); if(me==root) { rc = MPI_Bcast(myname,HOSTNAME_LEN,MPI_CHAR,root,MPI_COMM_WORLD); } else { rc = MPI_Bcast(rootname, HOSTNAME_LEN,MPI_CHAR,root,MPI_COMM_WORLD); } if(rc != MPI_SUCCESS){ fprintf(stderr,"single_cluster:MPI_Bcast failed rc=%d\n",rc); MPI_Abort(MPI_COMM_WORLD,rc); } len = strlen(myname); stat = (me==root) ? 0 : strncmp(rootname, myname, len); if(stat != 0) { stat = 1; } rc = MPI_Allreduce(&stat, &global_stat, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); if(rc != MPI_SUCCESS){ fprintf(stderr,"single_cluster:MPI_MPI_Allreduce failed rc=%d\n",rc); MPI_Abort(MPI_COMM_WORLD,rc); } if(global_stat) { return 0; } else { return 1; } } ga-5.9.2/tcgmsg/tcgmsg-mpi/collect.c000066400000000000000000000112561500715745200172550ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include "tcgmsgP.h" /* size of internal buffer for global ops */ #define DGOP_BUF_SIZE 65536 #define IGOP_BUF_SIZE 65536 /*#define IGOP_BUF_SIZE (sizeof(double)/sizeof(long))*DGOP_BUF_SIZE */ static double dgop_work[DGOP_BUF_SIZE]; /**< global ops buffer */ static long igop_work[IGOP_BUF_SIZE]; /**< global ops buffer */ /** * global operations -- integer version */ void IGOP_(long *ptype, long *x, long *pn, char *op, int oplen) { long *work = (long *) igop_work; long nleft = *pn; long buflen = TCG_MIN(nleft,IGOP_BUF_SIZE); /**< Try to get even sized buffers */ long nbuf = (nleft-1) / buflen + 1; long n; /* #ifdef ARMCI */ if(!_tcg_initialized){ PBEGINF_(); } /* #endif */ buflen = (nleft-1) / nbuf + 1; if (strncmp(op,"abs",3) == 0) { n = *pn; while(n--) { x[n] = TCG_ABS(x[n]); } } while (nleft) { int ierr = MPI_SUCCESS; int ndo = TCG_MIN(nleft, buflen); MPI_Op mop; if (strncmp(op,"+",1) == 0) { mop = MPI_SUM; } else if (strncmp(op,"*",1) == 0) { mop = MPI_PROD; } else if (strncmp(op,"max",3) == 0 || strncmp(op,"absmax",6) == 0) { mop = MPI_MAX; } else if (strncmp(op,"min",3) == 0 || strncmp(op,"absmin",6) == 0) { mop = MPI_MIN; } else if (strncmp(op,"or",2) == 0) { mop = MPI_BOR; } /* these are new */ else if ((strncmp(op, "&&", 2) == 0) || (strncmp(op, "land", 4) == 0)) { mop = MPI_LAND; } else if ((strncmp(op, "||", 2) == 0) || (strncmp(op, "lor", 3) == 0)) { mop = MPI_LOR; } else if ((strncmp(op, "&", 1) == 0) || (strncmp(op, "band", 4) == 0)) { mop = MPI_BAND; } else if ((strncmp(op, "|", 1) == 0) || (strncmp(op, "bor", 3) == 0)) { mop = MPI_BOR; } else { Error("IGOP: unknown operation requested", (long) *pn); } ierr = MPI_Allreduce(x, work, ndo, TCG_INT, mop, TCGMSG_Comm); tcgmsg_test_statusM("IGOP: MPI_Allreduce:", ierr ); n = ndo; while(n--) { x[n] = work[n]; } nleft -= ndo; x+= ndo; } } /** * global operations -- double version */ void DGOP_(long *ptype, double *x, long *pn, char *op, int oplen) { double *work= dgop_work; long nleft = *pn; long buflen = TCG_MIN(nleft,DGOP_BUF_SIZE); /**< Try to get even sized buffers */ long nbuf = (nleft-1) / buflen + 1; long n; buflen = (nleft-1) / nbuf + 1; if (strncmp(op,"abs",3) == 0) { n = *pn; while(n--) { x[n] = TCG_ABS(x[n]); } } while (nleft) { int ierr = MPI_SUCCESS; int ndo = TCG_MIN(nleft, buflen); MPI_Op mop; if (strncmp(op,"+",1) == 0) { mop = MPI_SUM; } else if (strncmp(op,"*",1) == 0) { mop = MPI_PROD; } else if (strncmp(op,"max",3) == 0 || strncmp(op,"absmax",6) == 0) { mop = MPI_MAX; } else if (strncmp(op,"min",3) == 0 || strncmp(op,"absmin",6) == 0) { mop = MPI_MIN; } else if (strncmp(op,"or",2) == 0) { mop = MPI_BOR; } /* these are new */ else if ((strncmp(op, "&&", 2) == 0) || (strncmp(op, "land", 4) == 0)) { mop = MPI_LAND; } else if ((strncmp(op, "||", 2) == 0) || (strncmp(op, "lor", 3) == 0)) { mop = MPI_LOR; } else if ((strncmp(op, "&", 1) == 0) || (strncmp(op, "band", 4) == 0)) { mop = MPI_BAND; } else if ((strncmp(op, "|", 1) == 0) || (strncmp(op, "bor", 3) == 0)) { mop = MPI_BOR; } else { Error("DGOP: unknown operation requested", (long) *pn); } ierr = MPI_Allreduce(x, work, ndo, TCG_DBL, mop, TCGMSG_Comm); tcgmsg_test_statusM("DGOP: MPI_Allreduce:", ierr ); n = ndo; while(n--) { x[n] = work[n]; } nleft -= ndo; x+= ndo; } } /** * Synchronize processes */ void SYNCH_(long *type) { /* #ifdef ARMCI */ if(!_tcg_initialized){ PBEGINF_(); } /* #endif */ MPI_Barrier(TCGMSG_Comm); } /** * broadcast buffer to all other processes from process originator */ void BRDCST_(long *type, void *buf, long *lenbuf, long *originator) { /* hope that MPI int is large enough to store value in lenbuf */ int count = (int)*lenbuf, root = (int)*originator; MPI_Bcast(buf, count, MPI_CHAR, root, TCGMSG_Comm); } ga-5.9.2/tcgmsg/tcgmsg-mpi/drand48.c000066400000000000000000000004131500715745200170650ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include #include "sndrcv.h" double DRAND48_() { double val=((double) random() ) * 4.6566128752458e-10; return val; } void SRAND48_(long *seed) { (void) srandom(*seed); } ga-5.9.2/tcgmsg/tcgmsg-mpi/evlog.c000066400000000000000000000266071500715745200167520ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** * Event logging routine with key driven varargs interface */ #include #include #include #include #include #include "evlog.h" #include "sndrcv.h" #define BUFLEN 262144 /**< Size allocated for buffer ... biggish */ #define MAX_EV_LEN 1000 /**< Assumed maximum size of single event record */ #define ERROR_RETURN() \ do { \ error = 1; \ return; \ } while (0) #define DUMPBUF() \ do { \ (void) fputs(buffer, file); \ (void) fflush(file); \ if (ferror(file)) { \ ERROR_RETURN(); \ } \ bufpt = buffer; \ left = BUFLEN; \ } while (0) #define RECORD(A) \ do { \ A; \ nchars = strlen(bufpt); \ bufpt += nchars; \ left -= nchars; \ } while (0) static double walltime(); /** * The format of the argument list is as follows: * * evlog([(int) key, [values, ...]], ..., EVKEY_LAST_ARG) * * Arguments are read as keys with corresponding values. Recognised keys * are defined in evlog.h and are described in detail below. * * Logging is enabled/disabled by calling evlog with one of EVKEY_ENABLE * or EVKEY_DISABLE specified. Note that EVKEY_ENABLE must be the * first key specified for it to be recognized and that all keys * in the argument list after EVKEY_DISABLE are ignored. By default * events are logged in the file events. This can be overridden with * the key EVKEY_FILENAME, which takes the filename as its value. * * The model for logging events assumed by the post-analysis routines * assumes that upon logging an event: * * a) no state chage occurs (EVKEY_EVENT). The event is just recorded. * * b) the process changes state by pushing the event onto the state stack * (EVKEY_BEGIN). * * c) the process changes state by popping an event off the state stack * (EVKEY_END). If the event or state popped off the stack does not * match the specified event then the post-analysis may get confused * but this does not interfere with the actual logging. * * EVKEY_EVENT, EVKEY_BEGIN or EVKEY_END must be the first key specified other * than a possible EVKEY_ENABLE. * * Internally an event is stored as a large character string to simplify * post-analysis. Users specify data for storage in addition to * that which is automatically stored (only the time and process) with * key, value combinations (EVKEY_STR_INT, EVKEY_STR_DBL, EVKEY_STR). * Many such key-value combinations as required may be specified. * Since the internal data format uses colons ':', double quotation * marks '"' and carriage returns users should avoid these in their * string data. * * ---------------------------- * Sample calling sequence: * * evlog(EVKEY_ENABLE, EVKEY_FILENAME, "events.log", EVKEY_LAST_ARG); * * * evlog(EVKEY_EVENT, "Finished startup code", * EVKEY_STR, "Now do some real work", * EVKEY_LAST_ARG); * * evlog(EVKEY_BEGIN, "Get Matrix", EVKEY_LAST_ARG); * * * evlog(EVKEY_END, "Get matrix", * EVKEY_STR_INT, "Size of matrix", (int) N, * EVKEY_STR_DBL, "Norm of matrix", (double) matrix_norm, * EVKEY_LAST_ARG); * * evlog(EVKEY_BEGIN, "Transform matrix", * EVKEY_STR_DBL, "Recomputed norm", (double) matrix_norm, * EVKEY_LAST_ARG); * * * evlog(EVKEY_END, "Transform matrix", * EVKEY_STR_INT, "No. of iterations", (int) niters, * EVKEY_LAST_ARG); * * evlog(EVKEY_DUMP, EVKEY_DISABLE, EVKEY_LAST_ARG); * * evlog(EVKEY_EVENT, "Logging is disabled ... this should not print", * EVKEY_DUMP, EVKEY_LAST_ARG); * * ---------------------------- * * EVKEY_LAST_ARG * Terminates list ... takes no value and must be present * * EVKEY_EVENT, (char *) event * Simply log occurence of the event * * EVKEY_BEGIN, (char *) event * Push event onto process state stack * * EVKEY_END, (char *) event * Pop event off process state stack * * EVKEY_MSG_LEN, (int) length * Value is (int) mesage length SND/RCV only * * EVKEY_MSG_TO, (int) to * Value is (int) to process id SND/RCV only * * EVKEY_MSG_FROM, (int) from * Value is (int) from process SND/RCV only * * EVKEY_MSG_TYPE, (int) type * Value is (int) message type SND/RCV only * * EVKEY_STR_INT, (char *) string, (int) data * User data value pair * * EVKEY_STR_DBL, (char *) string, (double) data * User data value pair (char *), (double) * * EVKEY_STR, (char *) string * User data value (char *) * * EVKEY_ENABLE * Enable logging * * EVKEY_DISABLE * Disable logging * * EVKEY_DUMP * Dump out the current buffer to disk * * EVKEY_FILE, (char *) filename * Use specified file to capture events. Default is "events". */ void evlog(int farg_key, ...) { static int logging=0;/**< Boolean flag for login enabled/disabled */ static int error=0; /**< Boolean flag for error detected */ static int ncall=0; /**< Need to do stuff on first entry */ static char *buffer; /**< Logging buffer ... null terminated */ static char *bufpt; /**< Pointer to next free char in buffer */ static int left; /**< Amount of free space in buffer */ static FILE *file; /**< File where events will be dumped */ static char *filename = "events"; /**< Default name of events file */ va_list ap; /**< For variable argument list */ int key; /**< Hold key being processed */ int nchars; /**< No. of chars printed by sprintf call */ char *temp; /**< Temporary copy of bufpt */ char *string; /**< Temporary */ int integer; /**< Temporary */ double dbl; /**< Temporary */ int valid; /**< Temporary */ /* If an error was detected on a previous call don't even try to do anything */ if (error) { ERROR_RETURN(); } /* First time in need to allocate the buffer, open the file etc */ if (ncall == 0) { ncall = 1; if (!(bufpt = buffer = malloc((unsigned) BUFLEN))) { ERROR_RETURN(); } left = BUFLEN; if (!(file = fopen(filename, "w"))) { ERROR_RETURN(); } } /* Parse the arguments */ temp = bufpt; /**< Save to check if anything has been logged */ valid = 0; /**< One of BEGIN, END or EVENT must preceed most keys */ va_start(ap, farg_key); key = farg_key; while (key != EVKEY_LAST_ARG) { if ( (!logging) && (key != EVKEY_ENABLE) ) { return; } switch (key) { case EVKEY_ENABLE: logging = 1; break; case EVKEY_DISABLE: logging = 0; goto done; /* break; */ case EVKEY_FILENAME: if (!(filename = strdup(va_arg(ap, char *)))) { ERROR_RETURN(); } if (!(file = freopen(filename, "w", file))) { ERROR_RETURN(); } break; case EVKEY_BEGIN: valid = 1; RECORD(sprintf(bufpt, ":BEGIN:%s", va_arg(ap, char *))); RECORD(sprintf(bufpt, ":TIME:%.2f", walltime())); break; case EVKEY_END: valid = 1; RECORD(sprintf(bufpt, ":END:%s", va_arg(ap, char *))); RECORD(sprintf(bufpt, ":TIME:%.2f", walltime())); break; case EVKEY_EVENT: valid = 1; RECORD(sprintf(bufpt, ":EVENT:%s", va_arg(ap, char *))); RECORD(sprintf(bufpt, ":TIME:%.2f", walltime())); break; case EVKEY_MSG_LEN: if (!valid) { ERROR_RETURN(); } RECORD(sprintf(bufpt, ":MSG_LEN:%d", va_arg(ap, int))); break; case EVKEY_MSG_TO: if (!valid) { ERROR_RETURN(); } RECORD(sprintf(bufpt, ":MSG_TO:%d", va_arg(ap, int))); break; case EVKEY_MSG_FROM: if (!valid) { ERROR_RETURN(); } RECORD(sprintf(bufpt, ":MSG_FROM:%d", va_arg(ap, int))); break; case EVKEY_MSG_TYPE: if (!valid) { ERROR_RETURN(); } RECORD(sprintf(bufpt, ":MSG_TYPE:%d", va_arg(ap, int))); break; case EVKEY_MSG_SYNC: if (!valid) { ERROR_RETURN(); } RECORD(sprintf(bufpt, ":MSG_SYNC:%d", va_arg(ap, int))); break; case EVKEY_STR_INT: if (!valid) { ERROR_RETURN(); } string = va_arg(ap, char *); integer = va_arg(ap, int); RECORD(sprintf(bufpt, ":STR_INT:%s:%d", string, integer)); break; case EVKEY_STR_DBL: if (!valid) { ERROR_RETURN(); } string = va_arg(ap, char *); dbl = va_arg(ap, double); RECORD(sprintf(bufpt, ":STR_DBL:%s:%g", string, dbl)); break; case EVKEY_STR: if (!valid) { ERROR_RETURN(); } RECORD(sprintf(bufpt, ":STR:%s", va_arg(ap, char *))); break; case EVKEY_DUMP: DUMPBUF(); if (temp != bufpt) { RECORD(sprintf(bufpt, "\n")); temp = bufpt; } break; default: DUMPBUF(); ERROR_RETURN(); } key = va_arg(ap, int); } done: va_end(ap); /* Put a linefeed on the end of the record if something is written */ if (temp != bufpt) { RECORD(sprintf(bufpt, "\n")); temp = bufpt; } /* Should really check on every access to the buffer that there is enough space ... however just assume a very large maximum size for a single event log entry and check here */ if (left <= 0) { ERROR_RETURN(); } if (left < MAX_EV_LEN) { DUMPBUF(); } } /** * return the wall time in seconds as a double */ static double walltime() { return ((double) MTIME_()) * 0.01; } /* int main(int argc, char **argv) { int N = 19; double matrix_norm = 99.1; int niters = 5; evlog(EVKEY_ENABLE, EVKEY_FILENAME, "events.log", EVKEY_LAST_ARG); evlog(EVKEY_EVENT, "Finished startup code", EVKEY_STR, "Now do some real work", EVKEY_LAST_ARG); evlog(EVKEY_BEGIN, "Get Matrix", EVKEY_LAST_ARG); evlog(EVKEY_END, "Get matrix", EVKEY_STR_INT, "Size of matrix", (int) N, EVKEY_STR_DBL, "Norm of matrix", (double) matrix_norm, EVKEY_LAST_ARG); evlog(EVKEY_BEGIN, "Transform matrix", EVKEY_STR_DBL, "Recomputed norm", (double) matrix_norm, EVKEY_LAST_ARG); evlog(EVKEY_END, "Transform matrix", EVKEY_STR_INT, "No. of iterations", (int) niters, EVKEY_LAST_ARG); evlog(EVKEY_DUMP, EVKEY_LAST_ARG); evlog(EVKEY_EVENT, "Logging is disabled ... this should not print", EVKEY_DUMP, EVKEY_LAST_ARG); return 0; } */ ga-5.9.2/tcgmsg/tcgmsg-mpi/evlog.h000066400000000000000000000027351500715745200167530ustar00rootroot00000000000000/** @file * Define EVENT and KEY values used when calling evlog. */ #ifndef EVLOG_H_ #define EVLOG_H_ extern void evlog(int farg_key, ...); /* Values of keys in key value pairs */ #define EVKEY_LAST_ARG 0 /**< Terminates list ... takes no value */ #define EVKEY_BEGIN 1 /**< Push (char *) value onto state stack */ #define EVKEY_END 2 /**< Pop (char *) value off state stack */ #define EVKEY_EVENT 3 /**< Record (char *) value, no stack change */ #define EVKEY_MSG_LEN 4 /**< Value is (int) mesage length SND/RCV only */ #define EVKEY_MSG_TO 5 /**< Value is (int) to process id SND/RCV only */ #define EVKEY_MSG_FROM 6 /**< Value is (int) from process SND/RCV only */ #define EVKEY_MSG_TYPE 7 /**< Value is (int) message type SND/RCV only */ #define EVKEY_MSG_SYNC 8 /**< Value is (int) message sync SND/RCV only */ #define EVKEY_STR_INT 9 /**< User data value pair (char *), (int) */ #define EVKEY_STR_DBL 10 /**< User data value pair (char *), (double) */ #define EVKEY_STR 11 /**< User data value (char *) */ #define EVKEY_ENABLE 12 /**< Enable logging ... takes no value */ #define EVKEY_DISABLE 13 /**< Disable logging ... takes no value */ #define EVKEY_DUMP 14 /**< Dump out the current buffer to disk */ #define EVKEY_FILENAME 15 /**< Set the name of the events file */ #define EVENT_SND "Snd" /**< Predefined strings for internal events */ #define EVENT_RCV "Rcv" #define EVENT_PROCESS "Process" #endif /* EVLOG_H_ */ ga-5.9.2/tcgmsg/tcgmsg-mpi/evon.c000066400000000000000000000040711500715745200165740ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDLIB_H # include #endif /** @file * Crude FORTRAN interface to C event logging routines. * See evlog.c for more details. * * FORTRAN character variables are so unportable that guaranteeing * that U can parse a variable length argument list is next to impossible. * * This provides very basic event logging functionality. * * CALL EVON() * enable logging. * * CALL EVOFF() * disable logging. * * CALL EVBGIN("event description") * push event onto state stack * * CALL EVEND("event description") * pop event off state stack * * CALL EVENT("event description") * log occurence of event that doesn't change state stack */ #include "evlog.h" /* These to get portable FORTRAN interface ... these routines * will not be called from C which has the superior evlog interface */ #define evon_ F77_FUNC(evon,EVON) #define evoff_ F77_FUNC(evoff,EVOFF) #define evbgin_ F77_FUNC(evbgin,EVBGIN) #define evend_ F77_FUNC(evend,EVEND) #define event_ F77_FUNC(event,EVENT) void evon_() { #ifdef EVENTLOG evlog(EVKEY_ENABLE, EVKEY_LAST_ARG); #endif } void evoff_() { #ifdef EVENTLOG evlog(EVKEY_DISABLE, EVKEY_LAST_ARG); #endif } void evbgin_(char *string, long len) { #ifdef EVENTLOG char *value = malloc( (unsigned) (len+1) ); if (value) { (void) bcopy(string, value, len); value[len] = '\0'; evlog(EVKEY_BEGIN, value, EVKEY_LAST_ARG); (void) free(value); } #endif } void evend_(char *string, long len) { #ifdef EVENTLOG char *value = malloc( (unsigned) (len+1) ); if (value) { (void) bcopy(string, value, len); value[len] = '\0'; evlog(EVKEY_END, value, EVKEY_LAST_ARG); (void) free(value); } #endif } void event_(char *string, long len) { #ifdef EVENTLOG char *value = malloc( (unsigned) (len+1) ); if (value) { (void) bcopy(string, value, len); value[len] = '\0'; evlog(EVKEY_EVENT, value, EVKEY_LAST_ARG); (void) free(value); } #endif } ga-5.9.2/tcgmsg/tcgmsg-mpi/llog.c000066400000000000000000000016361500715745200165660ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_TIME_H # include #endif #if HAVE_SYS_TIME_H # include #endif #include "sndrcv.h" extern void Error(); /** * close and open stdin and stdout to append to a local logfile * with the name log. in the current directory */ void LLOG_() { char name[12]; time_t t; (void) sprintf(name, "log.%03ld",NODEID_()); (void) fflush(stdout); (void) fflush(stderr); if (freopen(name, "a", stdout) == (FILE *) NULL) { Error("LLOG_: error re-opening stdout", (long) -1); } if (freopen(name, "a", stderr) == (FILE *) NULL) { Error("LLOG_: error re-opening stderr", (long) -1); } (void) time(&t); (void) printf("\n\nLog file opened : %s\n\n",ctime(&t)); (void) fflush(stdout); } ga-5.9.2/tcgmsg/tcgmsg-mpi/misc.c000066400000000000000000000117131500715745200165610ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include extern void exit(int status); #include "tcgmsgP.h" #include "srftoc.h" #include "armci.h" char tcgmsg_err_string[ERR_STR_LEN]; MPI_Comm TCGMSG_Comm=MPI_COMM_WORLD; int _tcg_initialized=0; long DEBUG_; int SR_parallel; int SR_single_cluster =1; int tcgi_argc=0; char **tcgi_argv=NULL; static int SR_initialized=0; extern int nxtval_installed; extern MPI_Comm armci_group_comm(ARMCI_Group *group); long TCGREADY_() { return (long)SR_initialized; } /** * number of processes */ long NNODES_() { int numprocs; MPI_Comm_size(TCGMSG_Comm, &numprocs); #ifdef NXTVAL_SERVER if(SR_parallel) { return((long)numprocs-1); } #endif return((long)numprocs); } /** * Get calling process id */ long NODEID_() { int myid; MPI_Comm_rank(TCGMSG_Comm,&myid); return((long)myid); } void Error(char *string, long code) { fprintf(stdout, FMT_INT ": %s " FMT_INT " (%#lx).\n", NODEID_(), string, code, (long unsigned int)code); fflush(stdout); fprintf(stderr, FMT_INT ": %s " FMT_INT " (%#lx).\n", NODEID_(), string, code, (long unsigned int)code); finalize_nxtval(); /* clean nxtval resources */ MPI_Abort(TCGMSG_Comm,(int)code); } /** * this is based on the MPI Forum decision that MPI_COMM_WORLD is a C constant */ void make_tcgmsg_comm() { extern int single_cluster(); #ifdef NXTVAL_SERVER if( SR_parallel ){ /* data server for a single process */ int server; MPI_Group MPI_GROUP_WORLD, tcgmsg_grp; MPI_Comm_size(MPI_COMM_WORLD, &server); server --; /* the highest numbered process will be excluded */ MPI_Comm_group(MPI_COMM_WORLD, &MPI_GROUP_WORLD); MPI_Group_excl(MPI_GROUP_WORLD, 1, &server, &tcgmsg_grp); MPI_Comm_create(MPI_COMM_WORLD, tcgmsg_grp, &TCGMSG_Comm); }else #endif { #if HAVE_ARMCI_INITIALIZED if (ARMCI_Initialized()) #else if (nxtval_installed) #endif { ARMCI_Group group; ARMCI_Group_get_world(&group); # if HAVE_ARMCI_GROUP_COMM_MEMBER TCGMSG_Comm = group.comm; # else TCGMSG_Comm = armci_group_comm(&group); # endif } else { TCGMSG_Comm = MPI_COMM_WORLD; } } } /** * Alternative initialization for C programs * used to address argv/argc manipulation in MPI */ void tcgi_alt_pbegin(int *argc, char **argv[]) { int numprocs, myid; int init=0; if(SR_initialized) { Error("TCGMSG initialized already???",-1); } else { SR_initialized=1; _tcg_initialized=1; } /* check if another library initialized MPI already */ MPI_Initialized(&init); if(!init){ /* nope */ #if defined(MPI_MT) || defined(MPI_PT) int provided; MPI_Init_thread(argc, argv, MPI_THREAD_MULTIPLE, &provided); #else MPI_Init(argc, argv); #endif #if defined(MPI_VERSION) && (MPI_VERSION >= 2) MPI_Comm_set_errhandler(TCGMSG_Comm, MPI_ERRORS_RETURN); #else MPI_Errhandler_set(TCGMSG_Comm, MPI_ERRORS_RETURN); #endif } MPI_Comm_size(TCGMSG_Comm, &numprocs); MPI_Comm_rank(TCGMSG_Comm, &myid); SR_parallel = numprocs > 1 ? 1 : 0; #if NEED_DELAY_TCGMSG_MPI_STARTUP /* printf("%d:ready to go\n",NODEID_()); */ /* wait until the last possible moment to call install_nxtval * it could be called by ARMCI_Init * or is called the first time nxtval is invoked (yuck) */ /*install_nxtval(argc, argv);*/ #else install_nxtval(argc, argv); /* which calls ARMCI_Init(), if needed */ #endif make_tcgmsg_comm(); MPI_Barrier(TCGMSG_Comm); } /** * Initialization for C programs */ void tcgi_pbegin(int argc, char* argv[]) { tcgi_argc = argc; tcgi_argv = argv; tcgi_alt_pbegin(&argc, &argv); } /** * shut down message-passing library */ void PEND_() { #ifdef NXTVAL_SERVER long zero=0; if( SR_parallel ) { (void) NXTVAL_(&zero); } MPI_Barrier(TCGMSG_Comm); #endif finalize_nxtval(); MPI_Finalize(); exit(0); } double TCGTIME_() { static int first_call = 1; static double first_time, last_time, cur_time; double diff; if (first_call) { first_time = MPI_Wtime(); first_call = 0; last_time = -1e-9; } cur_time = MPI_Wtime(); diff = cur_time - first_time; /* address crappy MPI_Wtime: consectutive calls must be at least 1ns apart */ if(diff - last_time < 1e-9) { diff +=1e-9; } last_time = diff; return diff; /* Add logic here for clock wrap */ } long MTIME_() { return (long) (TCGTIME_()*100.0); /* time in centiseconds */ } /** * longerface from Fortran to C error routine */ void PARERR_(long *code) { Error("User detected error in FORTRAN", *code); } void SETDBG_(long *onoff) { DEBUG_ = *onoff; } void STATS_() { printf("STATS not implemented\n"); } ga-5.9.2/tcgmsg/tcgmsg-mpi/msgtypesc.h000066400000000000000000000004361500715745200176510ustar00rootroot00000000000000/** @file * This defines bit masks that can be OR'ed with user types (1-32767) * to indicate the nature of the data to the message passing system */ #ifndef MSGTYPES_H_ #define MSGTYPES_H_ #define MSGDBL 65536 #define MSGINT 131072 #define MSGCHR 262144 #endif /* MSGTYPES_H_ */ ga-5.9.2/tcgmsg/tcgmsg-mpi/msgtypesf.h000066400000000000000000000003571500715745200176560ustar00rootroot00000000000000C C This defines bit masks that can be ORed with user types (1-32767) C to indicate the nature of the data to the message passing system C integer msgdbl, msgint, msgchr parameter (msgdbl=65536, msgint=131072, msgchr=262144) ga-5.9.2/tcgmsg/tcgmsg-mpi/niceftn.c000066400000000000000000000004641500715745200172550ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_UNISTD_H # include #endif #include "srftoc.h" /** * Wrapper around nice for FORTRAN users courtesy of Rick Kendall * ... C has the system interface. */ long NICEFTN_(long *ival) { int val = (int)(*ival); return nice(val); } ga-5.9.2/tcgmsg/tcgmsg-mpi/nxtval-armci.c000066400000000000000000000076031500715745200202360ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #if HAVE_STDLIB_H # include #endif #if HAVE_MALLOC_H # include #endif #include "armci.h" #include "message.h" #include "tcgmsgP.h" #define LEN 2 static long pnxtval_counter_val; static long *pnxtval_counter=&pnxtval_counter_val; int nxtval_installed=0; extern int tcgi_argc; extern char **tcgi_argv; #define INCR 1 /**< increment for NXTVAL */ #define BUSY -1L /**< indicates somebody else updating counter*/ #define NXTV_SERVER ((int)NNODES_() -1) static int ARMCIinitialized = 0; extern void make_tcgmsg_comm(void); /** * Get next value of shared counter. * mproc > 0 ... returns requested value * mproc < 0 ... server blocks until abs(mproc) processes are queued * and returns junk * mproc = 0 ... indicates to server that I am about to terminate */ long NXTVAL_(long *mproc) { long local; int rc; int server = NXTV_SERVER; /* id of server process */ install_nxtval(&tcgi_argc, &tcgi_argv); if (SR_parallel) { if (DEBUG_) { (void) printf(FMT_INT ": nxtval: mproc=" FMT_INT "\n", NODEID_(), *mproc); (void) fflush(stdout); } if (*mproc < 0) { rc=MPI_Barrier(TCGMSG_Comm); if(rc!=MPI_SUCCESS) { Error("nxtval: barrier failed",0); } /* reset the counter value to zero */ if( NODEID_() == server) { *pnxtval_counter = 0; } rc=MPI_Barrier(TCGMSG_Comm); if(rc!=MPI_SUCCESS) { Error("nxtval: barrier failed",0); } } if (*mproc > 0) { #if SIZEOF_F77_INTEGER == SIZEOF_INT int op = ARMCI_FETCH_AND_ADD; rc = ARMCI_Rmw(op,(void*)&local,(void*)pnxtval_counter,1,server); #elif SIZEOF_F77_INTEGER == SIZEOF_LONG int op = ARMCI_FETCH_AND_ADD_LONG; rc = ARMCI_Rmw(op,(void*)&local,(void*)pnxtval_counter,1,server); #else #ifdef WIN64 Error("nxtval: not implemented",0); #else # error #endif #endif } } else { /* Not running in parallel ... just do a simulation */ static int count = 0; if (*mproc == 1) { local = count++; } else if (*mproc == -1) { count = 0; local = 0; } else { Error("nxtval: sequential version with silly mproc ", (long) *mproc); } } return local; } /** * initialization for nxtval */ void install_nxtval(int *argc, char **argv[]) { int rc; int me = (int)NODEID_(), bytes, server; void **ptr_ar; if (nxtval_installed) { return; } nxtval_installed = 1; #if HAVE_ARMCI_INITIALIZED_FUNCTION if (!ARMCI_Initialized()) #else if (!ARMCIinitialized) #endif { ARMCI_Init_args(argc, argv); ARMCIinitialized = 1; make_tcgmsg_comm(); } me = (int)NODEID_(); ptr_ar = (void **)malloc(sizeof(void *)*(int)NNODES_()); if(!ptr_ar) { Error("malloc failed in install_nxtval", (long)NNODES_()); } server = NXTV_SERVER; if(me== server) { bytes = sizeof(long); } else { bytes =0; } rc = ARMCI_Malloc(ptr_ar,bytes); if(rc) { Error("nxtv: armci_malloc failed",rc); } pnxtval_counter = (long*) ptr_ar[server]; if(me==server) { *pnxtval_counter = (long)0; } free(ptr_ar); rc=MPI_Barrier(TCGMSG_Comm); if(rc!=MPI_SUCCESS) { Error("init_nxtval: barrier failed",0); } } void finalize_nxtval() { /* * Cannot call ARMCI functions here as ARMCI might have been terminated * by now. NOTE: finalize_nxtval is called in pend(), which is called after * GA_Terminate/ARMCI_Finalize. */ #if 0 if(NODEID_() == NXTV_SERVER)ARMCI_Free(pnxtval_counter); #endif ARMCI_Finalize(); } ga-5.9.2/tcgmsg/tcgmsg-mpi/nxtval.c000066400000000000000000000113121500715745200171350ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include "tcgmsgP.h" #define LEN 2 long nxtval_counter=0; #define INCR 1 /**< increment for NXTVAL */ #define BUSY -1L /**< indicates somebody else updating counter*/ void NextValueServer() { long cnt = 0; /**< actual counter */ long ndone = 0; /**< no. finished for this loop */ int type = TYPE_NXTVAL; /**< message type */ long buf[LEN]; /**< buffer to get values */ long mproc; /**< no. of processes running loop */ long nval; /**< no. of values requested */ int done_list[MAX_PROCESS];/**< list of processes finished with this loop */ int lenmes, nodefrom; int node; long ntermin=0; MPI_Status status; while (1) { /* Wait for input from any node */ MPI_Recv(buf, LEN, MPI_LONG, MPI_ANY_SOURCE, type, MPI_COMM_WORLD, &status); MPI_Get_count(&status, MPI_LONG, &lenmes); nodefrom = status.MPI_SOURCE; if (lenmes != LEN) { Error("NextValueServer: lenmes != LEN", (long) lenmes); return; /* Never actually gets here as does long jump */ } mproc = buf[0]; nval = buf[1]; if (DEBUG_) { (void) printf("NVS: from=%ld, mproc=%ld, ndone=%ld\n", nodefrom, mproc, ndone); } if (mproc == 0) { /* Sending process is about to terminate. Send reply and disable * sending to him. If all processes have finished return. * * All processes block on waiting for message * from nxtval server before terminating. nxtval only lets * everyone go when all have registered termination. */ if (++ntermin == NNODES_()) { for (node=0; node 0) { /* This is what we are here for */ MPI_Send(&cnt, 1, MPI_LONG, nodefrom, type, MPI_COMM_WORLD); cnt += nval; } else if (mproc < 0) { /* This process has finished the loop. Wait until all mproc processes have finished before releasing it */ done_list[ndone++] = nodefrom; if (ndone == -mproc) { while (ndone--) { nodefrom = done_list[ndone]; MPI_Send(&cnt, 1, MPI_LONG, nodefrom, type, MPI_COMM_WORLD); } cnt = 0; ndone = 0; } } } } /** * Get next value of shared counter. * mproc > 0 ... returns requested value * mproc < 0 ... server blocks until abs(mproc) processes are queued * and returns junk * mproc = 0 ... indicates to server that I am about to terminate */ long NXTVAL_(long *mproc) { long buf[2]; MPI_Status status; int type = TYPE_NXTVAL; long local; #ifdef NXTVAL_SERVER int server = (int)NNODES_(); /**< id of server process */ #else int server = (int)NNODES_() -1; /**< id of server process */ #endif if (SR_parallel) { buf[0] = *mproc; buf[1] = INCR; if (DEBUG_) { (void) printf("%2ld: nxtval: mproc=%ld\n",NODEID_(), *mproc); (void) fflush(stdout); } #ifdef NXTVAL_SERVER MPI_Send(buf, LEN, MPI_LONG, server, type, MPI_COMM_WORLD); MPI_Recv(buf, 1, MPI_LONG, server, type, MPI_COMM_WORLD, &status); return buf[0]; #endif } else { /* Not running in parallel ... just do a simulation */ static int count = 0; if (*mproc == 1) { local = count++; } else if (*mproc == -1) { count = 0; local = 0; } else { Error("nxtval: sequential version with silly mproc ", (long) *mproc); } } return local; } /** * initialization for nxtval -- called in PBEGIN */ void install_nxtval(int *argc, char **argv[]) { int numprocs, myid; MPI_Comm_size(MPI_COMM_WORLD, &numprocs); MPI_Comm_rank(MPI_COMM_WORLD, &myid); #ifdef NXTVAL_SERVER /* in this mode one process is hidden from the application */ if(SR_parallel && myid == numprocs -1) { # ifndef QUIET printf("TCGMSG-MPI info: excluding one process for nxtval server\n"); fflush(stdout); # endif /* QUIET */ NextValueServer(); } #else # error Do not know how to implement nxtval ! #endif } void finalize_nxtval() { } ga-5.9.2/tcgmsg/tcgmsg-mpi/p2p.c000066400000000000000000000073311500715745200163300ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #include "tcgmsgP.h" /************************ nonblocking message list ********************/ #define MAX_Q_LEN 1024 /* Maximum no. of outstanding messages */ static struct msg_q_struct{ MPI_Request request; long node; long type; long lenbuf; long snd; long from; } msg_q[MAX_Q_LEN]; static int n_in_msg_q=0; /**********************************************************************/ void SND_(long *type, void *buf, long *lenbuf, long *node, long *sync) { int ierr; int ttype = (int)*type; if (DEBUG_) { printf("SND_: node " FMT_INT " sending to " FMT_INT ", len=" FMT_INT ", type=" FMT_INT ", sync=" FMT_INT "\n", NODEID_(), *node, *lenbuf, *type, *sync); fflush(stdout); } if (*sync){ ierr = MPI_Send(buf, (int)*lenbuf, MPI_CHAR, (int)*node, ttype, TCGMSG_Comm); tcgmsg_test_statusM("SND_:", ierr); }else{ if (n_in_msg_q >= MAX_Q_LEN) { Error("SND:overflowing async Q limit", n_in_msg_q); } ierr = MPI_Isend(buf, (int)*lenbuf, MPI_CHAR,(int)*node, ttype, TCGMSG_Comm, &msg_q[n_in_msg_q].request); tcgmsg_test_statusM("nonblocking SND_:", ierr); msg_q[n_in_msg_q].node = *node; msg_q[n_in_msg_q].type = *type; msg_q[n_in_msg_q].lenbuf = *lenbuf; msg_q[n_in_msg_q].snd = 1; } } void RCV_(long *type, void *buf, long *lenbuf, long *lenmes, long *nodeselect, long *nodefrom, long *sync) { int ierr; int node, count = (int)*lenbuf; MPI_Status status; MPI_Request request; if (*nodeselect == -1) { node = MPI_ANY_SOURCE; } else { node = (int)*nodeselect; } if (DEBUG_) { printf("RCV_: node " FMT_INT " receiving from " FMT_INT ", len=" FMT_INT ", type=" FMT_INT ", sync=" FMT_INT "\n", NODEID_(), *nodeselect, *lenbuf, *type, *sync); fflush(stdout); } if(*sync==0){ if (n_in_msg_q >= MAX_Q_LEN) { Error("nonblocking RCV_: overflowing async Q limit", n_in_msg_q); } ierr = MPI_Irecv(buf, count, MPI_CHAR, node, (int)*type, TCGMSG_Comm, &request); tcgmsg_test_statusM("nonblocking RCV_:", ierr); *nodefrom = node; /* Get source node */ *lenmes = -1L; msg_q[n_in_msg_q].request = request; msg_q[n_in_msg_q].node = *nodeselect; msg_q[n_in_msg_q].type = *type; msg_q[n_in_msg_q].lenbuf = *lenbuf; msg_q[n_in_msg_q].snd = 0; n_in_msg_q++; } else { ierr = MPI_Recv(buf, count, MPI_CHAR, node, (int)*type, TCGMSG_Comm, &status); tcgmsg_test_statusM("RCV_:", ierr); ierr = MPI_Get_count(&status, MPI_CHAR, &count); tcgmsg_test_statusM("RCV:Get_count:", ierr); *nodefrom = (long)status.MPI_SOURCE; *lenmes = (long)count; } } void WAITCOM_(long *nodesel) { int ierr, i; MPI_Status status; for (i=0; i #endif #if HAVE_STRING_H # include #endif #include "farg.h" #include "sndrcv.h" #include "srftoc.h" #define LEN 255 extern int tcgi_argc; extern char **tcgi_argv; /** * Hewlett Packard Risc box, SparcWorks F77 2.* compilers. * Have to construct the argument list by calling FORTRAN. */ void PBEGINF_() { ga_f2c_get_cmd_args(&tcgi_argc, &tcgi_argv); tcgi_pbegin(tcgi_argc, tcgi_argv); } /** * Alternative entry for those senstive to FORTRAN making reference * to 7 character external names */ void PBGINF_() { PBEGINF_(); } ga-5.9.2/tcgmsg/tcgmsg-mpi/pfilecopy.c000066400000000000000000000104061500715745200176160ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STRING_H # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_MEMORY_H # include #endif #if HAVE_STDLIB_H # include #endif extern void free(void *ptr); #include "msgtypesc.h" #include "sndrcv.h" #include "tcgmsgP.h" /** * Process node0 has a file (assumed unopened) named fname. * This file will be copied to all other processes which must * simultaneously invoke pfilecopy. Since the processes may be * using the same directory one probably ought to make sure * that each process uses a different name in the call. * * e.g. * * on node 0 pfilecopy(99, 0, 'argosin') * on node 1 pfilecopy(99, 0, 'argosin_001') * on node 2 pfilecopy(99, 0, 'argosin_002') */ void tcgi_pfilecopy(long *type, long *node0, char *filename) { char *buffer; FILE *file; long length, nread=32768, len_nread=sizeof(long); long typenr = (*type & 32767) | MSGINT; /* Force user type integer */ long typebuf =(*type & 32767) | MSGCHR; if (!(buffer = malloc((unsigned) nread))) { Error("pfilecopy: failed to allocate the I/O buffer",nread); } if (*node0 == NODEID_()) { /* I have the original file ... open and check its size */ if ((file = fopen(filename,"r")) == (FILE *) NULL) { (void) fprintf(stderr, "me=" FMT_INT ", filename = %s.\n", NODEID_(), filename); Error("pfilecopy: node0 failed to open original file", *node0); } /* Quick sanity check on the length */ (void) fseek(file, 0L, (int) 2); /* Seek to end of file */ length = ftell(file); /* Find the length of file */ (void) fseek(file, 0L, (int) 0); /* Seek to beginning of file */ if ( (length<0) || (length>1e12) ) { Error("pfilecopy: the file length is -ve or very big", length); } /* Send the file in chunks of nread bytes */ while (nread) { nread = fread(buffer, 1, (int) nread, file); BRDCST_(&typenr, (char *) &nread, &len_nread, node0); typenr++; if (nread) { BRDCST_(&typebuf, buffer, &nread, node0); typebuf++; } } } else { /* Open the file for the duplicate */ if ((file = fopen(filename,"w+")) == (FILE *) NULL) { (void) fprintf(stderr,"me=" FMT_INT ", filename = %s.\n", NODEID_(), filename); Error("pfilecopy: failed to open duplicate file", *node0); } /* Receive data and write to file */ while (nread) { BRDCST_(&typenr, (char *) &nread, &len_nread, node0); typenr++; if (nread) { BRDCST_(&typebuf, buffer, &nread, node0); typebuf++; if (nread != (long)fwrite(buffer, 1, (int) nread, file)) { Error("pfilecopy: error data to duplicate file", nread); } } } } /* Tidy up the stuff we have been using */ (void) fflush(file); (void) fclose(file); (void) free(buffer); } /** The original C interface to PFCOPY_. */ void PFILECOPY_(long *type, long *node0, char *filename) { tcgi_pfilecopy(type, node0, filename); } /** Fortran wrapper around pfilecopy */ void PFCOPY_(long *type, long *node0, char *fname, int len) { char *filename; #ifdef DEBUG (void) printf("me=%d, type=%d, node0=%d, fname=%x, fname=%.8s, len=%d\n", NODEID_(), *type, *node0, fname, fname, len); #endif /* Strip trailing blanks off the file name */ while ((len > 0) && (fname[len-1] == ' ')) { len--; } if (len <= 0) { Error("pfcopy_: file name length is toast", (long) len); } /* Generate a NULL terminated string */ filename = malloc( (unsigned) (len+1) ); if (filename) { (void) bcopy(fname, filename, len); filename[len] = '\0'; } else { Error("PFCOPY_: failed to malloc space for filename", (long) len); } /* Now call the C routine to do the work */ tcgi_pfilecopy(type, node0, filename); (void) free(filename); } ga-5.9.2/tcgmsg/tcgmsg-mpi/sizeof.c000066400000000000000000000021531500715745200171230ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /** @file * These routines use C's knowledge of the sizes of data types * to generate a portable mechanism for FORTRAN to translate * between bytes, integers and doubles. */ #include "sndrcv.h" /** * Return the no. of bytes that n doubles occupy */ long MDTOB_(long *n) { if (*n < 0) { Error("MDTOB_: negative argument",*n); } return (long) (*n * sizeof(double)); } /** * Return the minimum no. of integers which will hold n doubles. */ long MDTOI_(long *n) { if (*n < 0) { Error("MDTOI_: negative argument",*n); } return (long) ( (MDTOB_(n) + sizeof(long) - 1) / sizeof(long) ); } /** * Return the no. of bytes that n ints=longs occupy */ long MITOB_(long *n) { if (*n < 0) { Error("MITOB_: negative argument",*n); } return (long) (*n * sizeof(long)); } /** * Return the minimum no. of doubles in which we can store n longs */ long MITOD_(long *n) { if (*n < 0) { Error("MITOD_: negative argument",*n); } return (long) ( (MITOB_(n) + sizeof(double) - 1) / sizeof(double) ); } ga-5.9.2/tcgmsg/tcgmsg-mpi/sndrcv.h000066400000000000000000000036061500715745200171340ustar00rootroot00000000000000/** @file * This header file declares stubs and show prototypes of the * public sndrcv calls * * srftoc.h contains macros which define the names of c routines * accessible from FORTRAN and vice versa */ #ifndef SNDRCV_H_ #define SNDRCV_H_ #include "msgtypesc.h" #include "srftoc.h" #ifdef __cplusplus extern "C" { #endif extern void BRDCST_(long *type, void *buf, long *lenbuf, long *originator); extern void DGOP_(long *type, double *x, long *n, char *op, int oplen); extern double DRAND48_(); extern void IGOP_(long *type, long *x, long *n, char *op, int oplen); extern void LLOG_(); extern long MDTOB_(long *n); extern long MDTOI_(long *n); extern long MITOB_(long *n); extern long MITOD_(long *n); extern long MTIME_(); extern long NICEFTN_(long *ival); extern long NNODES_(); extern long NODEID_(); extern long NXTVAL_(long *mproc); extern void PARERR_(long *code); extern void PBEGINF_(); extern void PBGINF_(); extern void PEND_(); extern void PFCOPY_(long *type, long *node0, char *filename, int flen); extern void PFILECOPY_(long *type, long *node0, char *filename); extern long PROBE_(long *type, long *node); extern void RCV_(long *type, void *buf, long *lenbuf, long *lenmes, long *nodeselect, long *nodefrom, long *sync); extern void SETDBG_(long *value); extern void SND_(long *type, void *buf, long *lenbuf, long *node, long *sync); extern void SRAND48_(long *seed); extern void STATS_(); extern void SYNCH_(long *type); extern long TCGREADY_(); extern double TCGTIME_(); extern void WAITCOM_(long *node); /* Miscellaneous routines for internal use only? */ extern void Error(char *string, long integer); extern void MtimeReset(); extern void PrintProcInfo(); extern void RemoteConnect(long a, long b, long c); extern void tcgi_pbegin(int argc, char **argv); extern void USleep(long us); #ifdef __cplusplus } #endif #endif /* SNDRCV_H_ */ ga-5.9.2/tcgmsg/tcgmsg-mpi/srftoc.h000066400000000000000000000032021500715745200171250ustar00rootroot00000000000000/** @file This header file provides definitions for c for the names of the c message passing routines accessible from FORTRAN. It need not be included directly in user c code, assuming that sndrcv.h has already. It is needed as the FORTRAN naming convention varies between machines and it is the FORTRAN interface that is portable, not the c interface. However by coding with the macro defnition names c portability is ensured. */ #ifndef SRFTOC_H_ #define SRFTOC_H_ #define BRDCST_ armci_tcgmsg_brdcst #define DGOP_ armci_tcgmsg_dgop #define DRAND48_ armci_tcgmsg_drand48 #define IGOP_ armci_tcgmsg_igop #define LLOG_ armci_tcgmsg_llog #define MDTOB_ armci_tcgmsg_mdtob #define MDTOI_ armci_tcgmsg_mdtoi #define MITOB_ armci_tcgmsg_mitob #define MITOD_ armci_tcgmsg_mitod #define MTIME_ armci_tcgmsg_mtime #define NICEFTN_ armci_tcgmsg_niceftn #define NNODES_ armci_tcgmsg_nnodes #define NODEID_ armci_tcgmsg_nodeid #define NXTVAL_ armci_tcgmsg_nxtval #define PARERR_ armci_tcgmsg_parerr #define PBEGINF_ armci_tcgmsg_pbeginf #define PBGINF_ armci_tcgmsg_pbginf #define PEND_ armci_tcgmsg_pend #define PFCOPY_ armci_tcgmsg_pfcopy #define PFILECOPY_ armci_tcgmsg_pfilecopy #define PROBE_ armci_tcgmsg_probe #define RCV_ armci_tcgmsg_rcv #define SETDBG_ armci_tcgmsg_setdbg #define SND_ armci_tcgmsg_snd #define SRAND48_ armci_tcgmsg_srand48 #define STATS_ armci_tcgmsg_stats #define SYNCH_ armci_tcgmsg_synch #define TCGREADY_ armci_tcgmsg_tcgready #define TCGTIME_ armci_tcgmsg_tcgtime #define WAITCOM_ armci_tcgmsg_waitcom #endif /* SRFTOC_H_ */ ga-5.9.2/tcgmsg/tcgmsg-mpi/tcgmsgP.h000066400000000000000000000035311500715745200172360ustar00rootroot00000000000000#ifndef TCGMSGP_H_ #define TCGMSGP_H_ #include #if HAVE_STDIO_H # include #endif #if HAVE_STRING_H # include #endif #include "sndrcv.h" #if SIZEOF_F77_INTEGER == SIZEOF_SHORT # define TCG_INT MPI_SHORT # define FMT_INT "%hd" #elif SIZEOF_F77_INTEGER == SIZEOF_INT # define TCG_INT MPI_INT # define FMT_INT "%d" #elif SIZEOF_F77_INTEGER == SIZEOF_LONG # define TCG_INT MPI_LONG # define FMT_INT "%ld" #elif SIZEOF_F77_INTEGER == SIZEOF_LONG_LONG # define TCG_INT MPI_LONG_LONG_INT # define FMT_INT "%lld" #else # error Cannot determine TCG_INT #endif #if SIZEOF_F77_DOUBLE_PRECISION == SIZEOF_FLOAT # define TCG_DBL MPI_FLOAT # define FMT_DBL "%f" #elif SIZEOF_F77_DOUBLE_PRECISION == SIZEOF_DOUBLE # define TCG_DBL MPI_DOUBLE # define FMT_DBL "%f" #elif SIZEOF_F77_DOUBLE_PRECISION == SIZEOF_LONG_DOUBLE # define TCG_DBL MPI_LONG_DOUBLE # define FMT_DBL "%Lf" #else # error Cannot determine TCG_DBL #endif #define MAX_PROCESS 100000 #define TYPE_NXTVAL 33333 extern MPI_Comm TCGMSG_Comm; extern int SR_parallel; extern int SR_single_cluster; extern long DEBUG_; extern int _tcg_initialized; #define TCG_MAX(a,b) (((a) >= (b)) ? (a) : (b)) #define TCG_MIN(a,b) (((a) <= (b)) ? (a) : (b)) #define TCG_ABS(a) (((a) >= 0) ? (a) : (-(a))) #define TCG_ERR_LEN 80 #define ERR_STR_LEN TCG_ERR_LEN + MPI_MAX_ERROR_STRING extern char tcgmsg_err_string[ERR_STR_LEN]; #define tcgmsg_test_statusM(_str, _status)\ {\ if( _status != MPI_SUCCESS){\ int _tot_len, _len = TCG_MIN(ERR_STR_LEN, strlen(_str));\ strncpy(tcgmsg_err_string, _str, _len);\ MPI_Error_string( _status, tcgmsg_err_string + _len, &_tot_len);\ Error(tcgmsg_err_string, (int)_status);\ }\ } extern void finalize_nxtval(); extern void install_nxtval(int *argc, char **argv[]); #endif /* TCGMSGP_H_ */ ga-5.9.2/tcgmsg/tcgmsg.fh000066400000000000000000000002351500715745200152130ustar00rootroot00000000000000 double precision tcgtime integer nodeid, nnodes, mitob, mdtob, nxtval external nodeid, nnodes, mitob, mdtob, nxtval external tcgtime ga-5.9.2/tcgmsg/tcgmsg.h000066400000000000000000000027761500715745200150610ustar00rootroot00000000000000/** @file * This header file declares the public C API to tcgmsg. */ #ifndef TCGMSG_H_ #define TCGMSG_H_ #ifdef __cplusplus extern "C" { #endif extern void tcg_alt_pbegin(); extern void tcg_brdcst(long type, void *buf, long lenbuf, long originator); extern void tcg_dgop(long type, double *x, long n, char *op); extern double tcg_drand48(); extern void tcg_error(char *msg, long code); extern void tcg_igop(long type, long *x, long n, char *op); extern void tcg_llog(); extern long tcg_mdtob(long n); extern long tcg_mdtoi(long n); extern long tcg_mitob(long n); extern long tcg_mitod(long n); extern long tcg_mtime(); extern long tcg_niceftn(long ival); extern long tcg_nnodes(); extern long tcg_nodeid(); extern long tcg_nxtval(long mproc); extern void tcg_parerr(long code); extern void tcg_pbeginf(); extern void tcg_pbegin(int argc, char **argv); extern void tcg_pbginf(); extern void tcg_pend(); extern void tcg_pfcopy(long type, long node0, char *fname); extern long tcg_probe(long type, long node); extern void tcg_rcv(long type, void *buf, long lenbuf, long *lenmes, long nodeselect, long *nodefrom, long sync); extern long tcg_ready(); extern void tcg_setdbg(long value); extern void tcg_snd(long type, void *buf, long lenbuf, long node, long sync); extern void tcg_srand48(long seed); extern void tcg_stats(); extern void tcg_synch(long type); extern double tcg_time(); extern void tcg_waitcom(long node); #ifdef __cplusplus } #endif #endif /* TCGMSG_H_ */ ga-5.9.2/tcgmsg/tests/000077500000000000000000000000001500715745200145525ustar00rootroot00000000000000ga-5.9.2/tcgmsg/tests/brdcst_delta.c000066400000000000000000000106441500715745200173550ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #include "sndrcv.h" /* Given the col and row no. return the actual process no. */ #define MAP(Row,Col) (ncols*(Row) + (Col)) /* Given the node return the row no. */ #define ROW(Node) ((Node) / ncols) /* Given the node return the column no. */ #define COL(Node) ((Node) - ncols*((Node)/ncols)) static Integer ncols; static Integer nrows; /** * broadcast buffer to all other processes from process originator * ... all processes call this routine specifying the same * orginating process * * Modified for grid nrows * ncols. * * Always send message to process 0. Then proceed from there. * * Algorithm ... recursively bisect the grid horizontally * and then vertically * * Need to modify to include pipelining. * * 4 x 6 grid is numbered (nrows=4, ncols=6) * * 0 1 2 3 4 5 * 6 7 8 9 10 11 * 12 13 14 15 16 17 * 18 19 20 21 22 23 */ void brdcst_delta_(Integer *type, char *buf, Integer *lenbuf, Integer *originator) { Integer me = NODEID_(); Integer mycol = COL(me); Integer myrow = ROW(me); Integer sync = 1; Integer from, id, left, middle, right, lenmes; /* First try implementation ... always send data to process 0 */ if (*originator != 0) { if (me == 0) { (void) printf("a %d receiving from %d %d\n",me,*originator); (void) fflush(stdout); RCV_(type, buf, lenbuf, &lenmes, originator, &from, &sync); } else if (me == *originator) { id = 0; (void) printf("a %d sending to %d type %d\n",me,*originator,*type); (void) fflush(stdout); SND_(type, buf, lenbuf, &id, &sync); } } /* Now broadcast from process 0 */ /* Bisect aInteger top horizonal edge of mesh */ if (myrow == 0) { (void) printf("%d myrow == 0\n",NODEID_()); left = 0; right = ncols-1; while (left != right) { middle = (left + right + 1) / 2; if (mycol == left) { id = MAP((Integer) 0,middle); (void) printf("b %d sending to %d type %d\n",me,id,*type); (void) fflush(stdout); SND_(type, buf, lenbuf, &id, &sync); } else if (mycol == middle) { id = MAP((Integer) 0,left); (void) printf("b %d receiving from %d\n",me,id); (void) fflush(stdout); RCV_(type, buf, lenbuf, &lenmes, &id, &from, &sync); } if (mycol < middle) { right = middle-1; } else { left = middle; } } } /* Bisect down vertical columns of mesh */ left = 0; right = nrows-1; while (left != right) { middle = (left + right + 1) / 2; if (myrow == left) { id = MAP(middle,mycol); (void) printf("c %d sending to %d type %d\n",me,id,*type); (void) fflush(stdout); SND_(type, buf, lenbuf, &id, &sync); } else if (myrow == middle) { id = MAP(left,mycol); (void) printf("c %d receiving from %d %d\n",me,id); (void) fflush(stdout); RCV_(type, buf, lenbuf, &lenmes, &id, &from, &sync); } if (myrow < middle) { right = middle-1; } else { left = middle; } } (void) fflush(stdout); } int main(int argc, char **argv) { Integer row, col, node, data, type, len, me; pbegin(argc, argv); LLOG_(); if (NODEID_() == 0) { (void) printf("Input nrows, ncols "); (void) scanf("%d %d",&nrows, &ncols); } node = 0; type = 1; len = 4; BRDCST_(&type, &nrows, &len, &node); BRDCST_(&type, &ncols, &len, &node); me = NODEID_(); (void) printf(" me=%d row=%d col=%d map=%d\n",me, ROW(me),COL(me),MAP(ROW(me),COL(me))); /* SETDBG_(&type); */ for (node=0; node #endif #include "tcgmsg.h" /** * Traditional first parallel program. */ int main(int argc, char **argv) { tcg_pbegin(argc, argv); (void) printf("Hello from node %ld\n",tcg_nodeid()); tcg_pend(); return 0; } ga-5.9.2/tcgmsg/tests/hello2.c000066400000000000000000000004751500715745200161110ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #include "tcgmsg.h" /** * Traditional first parallel program */ int main(int argc, char **argv) { tcg_pbegin(argc, argv); (void) printf("Hello from node %ld\n",tcg_nodeid()); tcg_pend(); return 0; } ga-5.9.2/tcgmsg/tests/jacobi.F000066400000000000000000000146561500715745200161240ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif program main implicit double precision (a-h, o-z) dimension q(1) #include "msgtypesf.h" parameter (niter = 30, maxp = 2047) dimension jlo(0:maxp), nj(0:maxp) data iqoff, iqaddr/0, 0/ c c Simple iterative (Jacobi) linear equation solver (Ax = b) c c Initialize the message passing environment c call pbeginf c call evon c c Read in the dimension of the matrix on process 0 and c broadcast its value to everyone else c if (nodeid() .eq. 0) then write(6,1) 1 format(' Input dimension of matrix: '$) read(5,*) n endif call brdcst(1+MSGINT, n, mitob(1), 0) c c makjlo assigns a range of columns to each process. c ncolp is the number that this process is to do. c call makjlo(n, jlo, nj) ncolp = nj(nodeid()) c c allocate memory c need = n*ncolp + 2*n + 2*ncolp call getmem(need, q, iqaddr, iqoff) if (iqaddr.eq.0) call parerr(999) ia = iqoff + 1 ib = ia + n*ncolp ix = ib + ncolp is = ix + ncolp iw = is + n c c Make matrix (a), rhs vector (b) and initial guess (x) c call makabx(n, jlo(nodeid()), ncolp, q(ia), q(ib), q(ix)) c c Do niter iterations of the Jacobi algorithm. c Synchronize first for accurate timings. c call synch(13) rjunk = timer() call jacobi(n, jlo, nj, q(ia), q(ib), q(ix), q(is), q(iw), niter) used = timer() c c Print out results c rmflop = dble(niter*2*n)*dble(n) / (used*1.0d6) if (nodeid() .eq. 0) write(6,2) n, used, nnodes(), rmflop 2 format(' N=',i4,' used ',f6.2,' secs with ',i3,' processes', $ ', mflop=', f8.2) c call pend call fexit end subroutine jacobi(n, jlo, nj, a, b, x, s, work, niter) implicit double precision (a-h, o-z) dimension a(n, *), b(*), x(*), s(n), work(n), $ jlo(0:*), nj(0:*) c c Apply niter iterations of the Jacobi algorithm. c me = nodeid() do 10 iter = 1, niter c c Compute matrix vector product Ax ... this is the real work c c Do the part that we have c call mxv(a, n, x, nj(me), s) c c Now we have to add up the result over all the processors c Call dgop for simple but inefficient version. Call mxvadd c for a much more efficient version c c call dgop(2, s, n, '+') c call mxvadd(s, work, jlo, nj) c c Compute our part of the update vector and compute c the residual error (the error requires a global sum) c err = 0.0d0 do 20 j = jlo(me), jlo(me) + nj(me) - 1 jj = j - jlo(me) + 1 x(jj) = x(jj) + (b(jj)-s(j))/a(j,jj) err = err + abs((b(jj)-s(j))) 20 continue c c Write out results every now and again c if (mod(iter,10).eq.0) then call dgop(3, err, 1, '+') if (nodeid().eq.0) write(6,1) iter, err 1 format(' Iteration',i3,', Error',d9.2) endif 10 continue c end subroutine makabx(n, jlo, nj, a, b, x) implicit double precision (a-h, o-z) dimension a(n, nj), b(nj), x(nj) c jhi = jlo + nj - 1 do 10 j = jlo, jhi jj = j - jlo + 1 cvd$ novector do 20 i = 1, n a(i,jj) = dble(i+j) / dble(abs(i-j)*n+n/50+1) 20 continue b(jj) = dble(mod(j,3)) x(jj) = b(jj) / a(j,jj) 10 continue c end subroutine makjlo(n, jlo, nj) dimension jlo(0:*), nj(0:*) c ncolp = n / nnodes() next = n - (ncolp*nnodes()) jjlo = 1 do 10 iproc = 0, nnodes()-1 jlo(iproc) = jjlo jjlo = jjlo + ncolp if (iproc.lt.next) jjlo = jjlo + 1 nj(iproc) = jjlo - jlo(iproc) 10 continue c end subroutine mxvadd(s, work, jlo, nj) implicit real*8 (a-h, o-z) #include "msgtypesf.h" dimension s(*), work(*), jlo(0:*), nj(0:*) logical synch parameter (synch=.true.) c c We have in s(1:n) this process's contribution to the c matrix vector product A*x where we had nj(me) elements c of x starting at element jlo(me). Each process needs c to end up with the same elements of the full result c vector s. c c Thus we need to send to each process (ip) the elements c s(jlo(ip)+k-1), k=1,nj(ip). And we need to receive from c each process our piece of s which we add onto our result c vector. c c If communication is synchronous then we must explictly pair up c send/receive requests on this process with the matching c receive/send operations on other processes. c c There is potential for much more asynch stuff but the damned c iPSC-i860 hangs (irreproducibly) if we send off too many c unresolved asynchronous messages (how many is too much?). c me = nodeid() nproc = nnodes() nn = nproc + mod(nproc,2) c if (synch) then do 10 iter = 1, nn-1 call pairup(nn, me, iter, ip) if (ip.lt.nproc) then if (me. lt. ip) then call snd(3+MSGDBL, s(jlo(ip)), mdtob(nj(ip)), ip, 1) call rcv(3+MSGDBL, work, mdtob(nj(me)), lenmes, ip, & node, 1) else if (me.gt.ip) then call rcv(3+MSGDBL, work, mdtob(nj(me)), lenmes, ip, & node, 1) call snd(3+MSGDBL, s(jlo(ip)), mdtob(nj(ip)), ip, 1) endif call vvadd(nj(me), s(jlo(me)), work) endif 10 continue else do 20 iter = 1, nn-1 call pairup(nn, me, iter, ip) if (ip.lt.nproc) then call snd(3+MSGDBL, s(jlo(ip)), mdtob(nj(ip)), ip, 0) call rcv(3+MSGDBL, work, mdtob(nj(me)), lenmes, ip, node, 1) call vvadd(nj(me), s(jlo(me)), work) endif 20 continue call waitcom(-1) endif c end subroutine pairup(n, me, iter, ipair) c c one of many ways of generating maximally overlapped pairs c (not all that good on a hypercube though!) c if (iter.eq.1) then ipair = mod(n+1-me,n) else if (me.eq.0) then ipair = iter else if (me.eq.iter) then ipair = 0 else if (ipair.eq.0) ipair = me ipair = ipair + 2 if (ipair.ge.n) ipair = ipair + 1 - n endif end subroutine vvadd(n, a, b) implicit real*8 (a-h, o-z) dimension a(*), b(*) c do 10 i = 1,n a(i) = a(i) + b(i) 10 continue c end ga-5.9.2/tcgmsg/tests/mxv_fortran.f000066400000000000000000000012301500715745200172620ustar00rootroot00000000000000 subroutine mxv(a,ncol,b,nrow,c) implicit double precision (a-h, o-z) dimension a(ncol, nrow), b(nrow), c(ncol) parameter (nchunk = 127) c c matrix vector product stripmined to optimize cache usage c when inner loop is replaced with a daxpy that uses pipelined c loads for a to avoid writing over c in the cache. c do 10 ilo = 1, ncol, nchunk ihi = min(ncol, ilo+nchunk-1) do 20 i = ilo, ihi c(i) = 0.0d0 20 continue do 30 j = 1, nrow do 40 i = ilo, ihi c(i) = c(i) + a(i,j)*b(j) 40 continue 30 continue 10 continue c end ga-5.9.2/tcgmsg/tests/nxtsrv.c000066400000000000000000000043411500715745200162640ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Header: /tmp/hpctools/ga/tcgmsg/ipcv4.0/nxtsrv.c,v 1.4 1995-02-24 02:17:33 d3h325 Exp $ */ #include #include "sndrcv.h" #define DEBUG_ 0 #define TYPE_NXTVAL 32767 int main() /* This runs as highest no. process(es) */ { long cnt = 0; /* actual counter */ long lencnt = sizeof cnt; /* length of cnt */ long ndone = 0; /* no. finished for this loop */ long ntermin = 0; /* no. terminated so far (pend) */ long node = -1; /* select any node */ long type = TYPE_NXTVAL; /* message type */ long buf[2]; /* buffer to get values */ long lenbuf = sizeof buf; /* length of buffer */ long mproc; /* no. of processes running loop */ long nval; /* no. of values requested */ long done_list[16384]; /* list of processes finished with this loop */ long sync = 1; /* all info goes synchronously */ long on=0; long lenmes, nodefrom; PBEGIN_(); SETDBG_(&on); while (1) { /* Wait for input from any node */ RCV_(&type, (char *) buf, &lenbuf, &lenmes, &node, &nodefrom, &sync); if (lenmes != lenbuf) Error("NextValueServer: lenmes != lenbuf", lenmes); mproc = buf[0]; nval = buf[1]; if (DEBUG_) (void) printf("NVS: from=%d, mproc=%d, ndone=%d, ntermin=%d\n", nodefrom, mproc, ndone, ntermin); if (mproc == 0) { /* Sending process is about to terminate. Send reply and disable sending to him. If all processes have finished return. */ SND_(&type, (char *) &cnt, &lencnt, &nodefrom, &sync); if (++ntermin == NNODES_()) return 0; } else if (mproc > 0) { /* This is what we are here for */ SND_(&type, (char *) &cnt, &lencnt, &nodefrom, &sync); cnt += nval; } else if (mproc < 0) { /* This process has finished the loop. Wait until all mproc processes have finished before releasing it */ done_list[ndone++] = nodefrom; if (ndone == -mproc) { while (ndone--) { nodefrom = done_list[ndone]; SND_(&type, (char *) &cnt, &lencnt, &nodefrom, &sync); } cnt = 0; ndone = 0; } } } return 0; } ga-5.9.2/tcgmsg/tests/parse.c000066400000000000000000000141371500715745200160360ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #define MAX_TOKEN 2048 /** * Read next token from file. Tokens are separated by the character * delimeter. * Tokens are returned as NUL terminated character strings which * almost certainly should always be freed with free() after use. * * There is an internally imposed maximum token size of MAX_TOKEN bytes. * * All errors are handled by returning a NULL pointer. */ char *ReadToken(FILE *file, char delimiter) { char *buf = malloc((unsigned) MAX_TOKEN); char *temp; int input, used=0; if (!buf) { return (char *) NULL; } temp = buf; while ( (input = getc(file)) != EOF ) { used++; if (input == delimiter) { *temp = '\0'; break; } else { *temp++ = (char) input; if (used == MAX_TOKEN) { used = 0; break; } } } /* duplicate string to minimize problems if string is not freed in calling program */ if (used) { temp = strdup(buf); } else { temp = (char *) NULL; } (void) free(buf); return temp; } int FindToken(char *token, FILE *file, char delimiter) { char *input; while (input = ReadToken(file, delimiter)) { if (strcmp(input, token) == 0) { return 1; } else { (void) free(input); } } return 0; } /** * Return the starting time and duration of the events file. */ void GetTimeSpan(FILE *file, DoublePrecision *start, DoublePrecision *duration) { char *input; DoublePrecision end; end = *start = 0.0; if (FindToken("TIME", file, ':')) { end = *start = atof(input = ReadToken(file, ':')); (void) free(input); } while (FindToken("TIME", file, ':')) { end = atof(input = ReadToken(file, ':')); (void) free(input); } *duration = end - *start; (void) fseek(file, 0L, 0); } int main(int argc, char **argv) { char filename[11]; FILE *file, *plot; char *token; char *event; DoublePrecision time, start, duration=0.0, otim, span, margin, comms, useful; int newstate, state, i, nproc, lo, hi; if (argc == 1) { lo = 0; hi = 127; } else if (argc == 3) { lo = atoi(argv[1]); hi = atoi(argv[2]); } else { (void) fprintf(stderr, "usage: %s [lo hi]\n", argv[0]); (void) fprintf(stderr, "... with no arguments parse all event files\n"); (void) fprintf(stderr, "... or with lo & hi only files in this range\n"); (void) fprintf(stderr, "... e.g. parse 16 31\n"); return 1; } /* open the file that will have the plot stuff in it */ /* change of heart here ... just write to stdout */ /* if (!(plot = fopen("plot", "w"))) { perror("failed to open plot file"); return 1; } */ plot = stdout; /* Determine how many processes there are and maximum time span */ nproc = 0; for (i=lo; i<=hi; i++) { (void) sprintf(filename, "events.%03d", nproc); if ( !(file = fopen(filename, "r")) ) { break; } GetTimeSpan(file, &start, &span); (void) fclose(file); if (span > duration) { duration = span; } nproc++; } margin = duration * 0.1; (void) fprintf(plot, "s %d %d %d %d\n",0,0, (int) ((margin*2.0+duration)*100.0), 5*nproc); /* (void) fprintf(stderr, "nproc=%d, duration=%4.2f\n", nproc, duration); */ /* Now go thru the files and actually parse the contents */ for (i=lo; i<=hi; i++) { (void) sprintf(filename, "events.%03d", i); if ( !(file = fopen(filename, "r")) ) { break; } GetTimeSpan(file, &start, &span); comms = 0.0; state = 5*(i-lo); otim = 0.0 + margin; (void) fprintf(plot, "t %d %d %d\n", 0, state, i); while ( token = ReadToken(file, ':') ) { if (strcmp(token, "BEGIN") == 0) { newstate = 5*(i-lo) + 1; } else if (strcmp(token, "END") == 0) { newstate = 5*(i-lo); } else { continue; } /* Have a BEGIN or END ... only process Snd/Rcv at moment */ event = ReadToken(file, ':'); if ((strcmp(event, "Snd") == 0) || (strcmp(event,"Rcv") == 0) || (strcmp(event,"Waitcom") == 0)) { if (FindToken("TIME", file, ':')) { time = atof(ReadToken(file, ':')) - start + margin; (void) fprintf(plot, "l %d %d %d %d\n", (int) (otim*100.0), state, (int) (time*100.0), state); (void) fprintf(plot, "l %d %d %d %d\n", (int) (time*100.0), state, (int) (time*100.0), newstate); /* Accumulate the time spent in communication */ if (newstate == (5*(i-lo))) { comms = comms + time - otim; } otim = time; state = newstate; } } else if (strcmp(event, "Process") == 0) { if (FindToken("TIME", file, ':')) { time = atof(ReadToken(file, ':')) - start + margin; (void) fprintf(plot, "l %d %d %d %d\n", (int) (otim*100.0), state, (int) (time*100.0), state); otim = time; } } } /* Assume that non-communication time is useful */ useful = 100.0 * (span - comms) / duration; (void) fprintf(plot, "t %d %d %4.1f%%\n", (int) (100.0*duration+150.0*margin), 5*(i-lo), useful); (void) fflush(plot); (void) fclose(file); } return 0; } ga-5.9.2/tcgmsg/tests/test.c000066400000000000000000000457141500715745200157100ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_MEMORY_H # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_MALLOC_H # include #endif #include "evlog.h" #include "typesf2c.h" #include "msgtypesc.h" #include "tcgmsg.h" extern unsigned char CheckByte(); extern void Error(const char *, Integer); /** * Process 0 sleeps for 20 seconds and then sends each * process a message. The other processes sleep for periods * of 2 seconds and probes until it gets a message. All processes * respond to process 0 which recieves using a wildcard probe. */ static void TestProbe() { long type_syn = 32; long type_msg = 33; long type_ack = 34; long me = tcg_nodeid(); char buf; long lenbuf = sizeof buf; long sync = 1; if (me == 0) { (void) printf("Probe test ... processes should sleep for 20s only\n"); (void) printf("----------\n\n"); (void) fflush(stdout); } tcg_synch(type_syn); if (me == 0) { long nproc = tcg_nnodes(); long anyone = -1; long ngot = 0; long node; (void) sleep((unsigned) 20); for (node=1; node hi) { ran = hi; } list[i] = ran; } } /** * Stress the system by passing messages between a ranomly selected * list of nodes */ void Stress() { long me = tcg_nodeid(); long nproc = tcg_nnodes(); long type, lenbuf, node, lenmes, nodefrom, i, j, from, to; long *list_i, *list_j, *list_n; #define N_LEN 11 static long len[N_LEN] = {0,1,2,4,8,4095,4096,4097,16367,16368,16369}; char *buf1, *buf2; long n_stress, mod; long sync = 1; from = 0; lenbuf = sizeof(long); if (me == 0) { (void) printf("Stress test ... randomly exchange messages\n-----------"); (void) printf("\n\nInput no. of messages: "); (void) fflush(stdout); if (scanf("%ld",&n_stress) != 1) { Error("Stress: error reading n_stress",(long) -1); } if ( (n_stress <= 0) || (n_stress > 100000) ) { n_stress = 100; } } type = 13 | MSGINT; tcg_brdcst(type, (void *) &n_stress, lenbuf, from); type++; lenbuf = n_stress * sizeof(long); #if HAVE_MEMALIGN list_i = (long *) memalign(sizeof(long), (unsigned) lenbuf); #else list_i = (long *) malloc((unsigned) lenbuf); #endif if ( list_i == (long *) NULL ) { Error("Stress: failed to allocate list_i",n_stress); } #if HAVE_MEMALIGN list_j = (long *) memalign(sizeof(long), (unsigned) lenbuf); #else list_j = (long *) malloc((unsigned) lenbuf); #endif if ( list_j == (long *) NULL ) { Error("Stress: failed to allocate list_j",n_stress); } #if HAVE_MEMALIGN list_n = (long *) memalign(sizeof(long), (unsigned) lenbuf); #else list_n = (long *) malloc((unsigned) lenbuf); #endif if ( list_n == (long *) NULL ) { Error("Stress: failed to allocate list_n",n_stress); } if ( (buf1 = malloc((unsigned) 16376)) == (char *) NULL ) { Error("Stress: failed to allocate buf1", (long) 16376); } if ( (buf2 = malloc((unsigned) 16376)) == (char *) NULL ) { Error("Stress: failed to allocate buf2", (long) 16376); } if (me == 0) { /* Make random list of node pairs and message lengths */ RandList((long) 0, (long) (tcg_nnodes()-1), list_i, n_stress); RandList((long) 0, (long) (tcg_nnodes()-1), list_j, n_stress); RandList((long) 0, (long) (N_LEN-1), list_n, n_stress); for (i=0; i= nproc) ) { Error("Stress: from is out of range", from); } if ( (to < 0) || (to >= nproc) ) { Error("Stress: to is out of range", to); } if (from == to) { continue; } if ( (me == 0) && (j%mod == 0) ) { (void) printf("Stress: test=%ld: from=%ld, to=%ld, len=%ld\n", i, from, to, lenbuf); (void) fflush(stdout); } j++; if (from == me) { tcg_snd(type, buf1, lenbuf, to, sync); } else if (to == me) { (void) bzero(buf2, (int) lenbuf); /* Initialize the receive buffer */ buf2[lenbuf] = '+'; tcg_rcv(type, buf2, lenbuf, &lenmes, from, &nodefrom, sync); if (buf2[lenbuf] != '+') { Error("Stress: overran buffer on receive",lenbuf); } if (CheckByte((unsigned char *) buf1, lenbuf) != CheckByte((unsigned char *) buf2, lenbuf)) { Error("Stress: invalid checksum on receive",lenbuf); } if (lenmes != lenbuf) { Error("Stress: invalid message length on receive",lenbuf); } } } (void) free(buf2); (void) free(buf1); (void) free((char *) list_n); (void) free((char *) list_j); (void) free((char *) list_i); #undef N_LEN } /** Time passing a message round a ring. */ void RingTest() { long me = tcg_nodeid(); long type = 4; long left = (me + tcg_nnodes() - 1) % tcg_nnodes(); long right = (me + 1) % tcg_nnodes(); char *buf, *buf2=NULL; unsigned char sum, sum2; long lenbuf, lenmes, nodefrom; DoublePrecision start, used, rate; long max_len; long i; long sync = 1; i = 0; lenbuf = sizeof(long); if (me == 0) { (void) printf("Ring test ... time network performance\n---------\n\n"); (void) printf("Input maximum message size: "); (void) fflush(stdout); if (scanf("%ld", &max_len) != 1) { Error("RingTest: error reading max_len",(long) -1); } if ( (max_len <= 0) || (max_len >= 4*1024*1024) ) { max_len = 256*1024; } } type = 4 | MSGINT; tcg_brdcst(type, (void *) &max_len, lenbuf, i); if ( (buf = malloc((unsigned) max_len)) == (char *) NULL) { Error("failed to allocate buf",max_len); } if (me == 0) { if ( (buf2 = malloc((unsigned) max_len)) == (char *) NULL) { Error("failed to allocate buf2",max_len); } for (i=0; i0)?lenbuf:1); int loop = nloops; if (me == 0) { sum = CheckByte((unsigned char *) buf, lenbuf); if (lenbuf) (void) bzero(buf2, (int) lenbuf); start = tcg_time(); while (loop--) { tcg_snd(type, buf, lenbuf, left, sync); tcg_rcv(type, buf2, lenbuf, &lenmes, right, &nodefrom, sync); } used = tcg_time() - start; sum2 = CheckByte((unsigned char *) buf2, lenbuf); sum2 = 0; if (used > 0) { rate = 1.0e-6 * (DoublePrecision) (tcg_nnodes() * lenbuf) / (DoublePrecision) used; } else { rate = 0.0; } rate = rate * nloops; printf("len=%6ld bytes, nloop=%4d, used=%8.4f s, rate=%8.4f Mb/s (0x%x, 0x%x)\n", lenbuf, nloops, used, rate, sum, sum2); (void) fflush(stdout); } else { while (loop--) { tcg_rcv(type, buf, lenbuf, &lenmes, right, &nodefrom, sync); tcg_snd(type, buf, lenbuf, left, sync); } } if (lenbuf) { lenbuf *= 2; } else { lenbuf = 1; } (void) fflush(stdout); } if (me == 0) { (void) free(buf2); } (void) free(buf); } /** Test receiveing a message from any node. */ void RcvAnyTest() { long me = tcg_nodeid(); long type = 337 | MSGINT; char buf[8]; long i, j, node, lenbuf, lenmes, nodefrom, receiver, n_msg; long sync = 1; lenbuf = sizeof(long); if (me == 0) { (void) printf("RCV any test ... check is working!\n-----------\n\n"); (void) printf("Input node to receive : "); (void) fflush(stdout); if (scanf("%ld", &receiver) != 1) { Error("RcvAnyTest: error reading receiver",(long) -1); } if ( (receiver < 0) || (receiver >= tcg_nnodes()) ) { receiver = tcg_nnodes()-1; } (void) printf("Input number of messages : "); (void) fflush(stdout); if (scanf("%ld", &n_msg) != 1) { Error("RcvAnyTest: error reading n_msg",(long) -1); } if ( (n_msg <= 0) || (n_msg > 10) ) { n_msg = 5; } } node = 0; tcg_brdcst(type, (void *) &receiver, lenbuf, node); type++; tcg_brdcst(type, (void *) &n_msg, lenbuf, node); type++; lenbuf = 0; type = 321; for (i=0; i= 10000) ) { n_val = 100; } } node = 0; tcg_brdcst(type, (void *) &n_val, lenbuf, node); /* Loop thru a few values to visually show it is working */ next = -1; for (i=0; i<10; i++) { if (i > next) { next = tcg_nxtval(nproc); } if (i == next) { (void) printf("node %ld got value %ld\n",me, i); (void) fflush(stdout); } } nproc = -nproc; next = tcg_nxtval(nproc); nproc = -nproc; /* Now time it for real .. twice*/ for (ntimes=0; ntimes<2; ntimes++) { if (me == 0) { start = tcg_time(); } next = -1; ngot = 0; for (i=0; i next) { next = tcg_nxtval(nproc); } if (i == next) { ngot++; } } nproc = -nproc; next = tcg_nxtval(nproc); nproc = -nproc; if (me == 0) { used = tcg_time() - start; rate = used / ngot; (void) printf("node 0: From %ld busy iters did %ld, used=%fs per call\n", n_val, ngot, rate); (void) fflush(stdout); } type++; tcg_synch(type); } } void ToggleDebug() { static long on = 0; long me = tcg_nodeid(); long type = 666 | MSGINT; long lenbuf = sizeof(long); long from=0; long node; if (me == 0) { (void) printf("\nInput node to debug (-1 = all) : "); (void) fflush(stdout); if (scanf("%ld", &node) != 1) { Error("ToggleDebug: error reading node",(long) -1); } } tcg_brdcst(type, (void*) &node, lenbuf, from); if ((node < 0) || (node == me)) { on = (on + 1)%2; tcg_setdbg(on); } } int main(int argc, char **argv) { long type; long lenbuf; long node, opt; #if TEST_OLD_PBEGIN tcg_pbegin(argc, argv); #else tcg_alt_pbegin(&argc, &argv); #endif (void) printf("In process %ld\n", tcg_nodeid()); (void) fflush(stdout); /* Read user input for action */ lenbuf = sizeof(long); node = 0; while (1) { (void) fflush(stdout); if (tcg_nodeid() == 0) { (void) sleep(1); } type = 999; tcg_synch(type); (void) sleep(1); if (tcg_nodeid() == 0) { again: (void) printf("\n\ 0=quit\n\ 1=Ring 5=NxtVal\n\ 2=Stress 6=Global\n\ 3=Hello 7=Debug\n\ 4=RcvAny 8=Probe\n\n\ Enter test number : "); (void) fflush(stdout); if (scanf("%ld", &opt) != 1) { Error("test: input of option failed",(long) -1); } (void) printf("\n"); (void) fflush(stdout); if ( (opt < 0) || (opt > 8) ) { goto again; } } type = 2 | MSGINT; tcg_brdcst(type, (void*) &opt, lenbuf, node); switch (opt) { case 0: if (tcg_nodeid() == 0) { tcg_stats(); } tcg_pend(); return 0; case 1: RingTest(); break; case 2: Stress(); break; case 3: Hello(); break; case 4: RcvAnyTest(); break; case 5: NextValueTest(); break; case 6: TestGlobals(); break; case 7: ToggleDebug(); break; case 8: TestProbe(); break; default: Error("test: invalid option", opt); break; } } } ga-5.9.2/tcgmsg/tests/test.isend.c000066400000000000000000000475321500715745200170110ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Header: /tmp/hpctools/ga/tcgmsg/ipcv5.0/test.isend.c,v 1.2 1994-12-30 20:56:23 d3h325 Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_MEMORY_H # include #endif #include "evlog.h" #include "tcgmsg.h" extern unsigned char CheckByte(); /** * Process 0 sleeps for 20 seconds and then sends each * process a message. The other processes sleep for periods * of 2 seconds and probes until it gets a message. All processes * respond to process 0 which recieves using a wildcard probe. */ static void TestProbe() { long type_syn = 32; long type_msg = 33; long type_ack = 34; long me = NODEID_(); char buf; long lenbuf = sizeof buf; long sync = 1; if (me == 0) { (void) printf("Probe test ... processes should sleep for 20s only\n"); (void) printf("----------\n\n"); (void) fflush(stdout); } SYNCH_(&type_syn); if (me == 0) { long nproc = NNODES_(); long anyone = -1; long ngot = 0; long node; (void) sleep((unsigned) 20); for (node=1; node hi) ran = hi; list[i] = ran; } } /** * Stress the system by passing messages between a ranomly selected * list of nodes */ void Stress() { long me = NODEID_(); long nproc = NNODES_(); long type, lenbuf, node, lenmes, nodefrom, i, j, from, to; long *list_i, *list_j, *list_n; #define N_LEN 11 static long len[N_LEN] = {0,1,2,4,8,4095,4096,4097,16367,16368,16369}; char *buf1, *buf2; long n_stress, mod; long sync = 1; from = 0; lenbuf = sizeof(long); if (me == 0) { (void) printf("Stress test ... randomly exchange messages\n-----------"); (void) printf("\n\nInput no. of messages: "); (void) fflush(stdout); if (scanf("%ld",&n_stress) != 1) Error("Stress: error reading n_stress",(long) -1); if ( (n_stress <= 0) || (n_stress > 100000) ) n_stress = 100; } type = 13 | MSGINT; BRDCST_(&type, (char *) &n_stress, &lenbuf, &from); type++; lenbuf = n_stress * sizeof(long); #if HAVE_MEMALIGN list_i = (long *) memalign(sizeof(long), (unsigned) lenbuf); #else list_i = (long *) malloc((unsigned) lenbuf); #endif if ( list_i == (long *) NULL ) { Error("Stress: failed to allocate list_i",n_stress); } #if HAVE_MEMALIGN list_j = (long *) memalign(sizeof(long), (unsigned) lenbuf); #else list_j = (long *) malloc((unsigned) lenbuf); #endif if ( list_j == (long *) NULL ) { Error("Stress: failed to allocate list_j",n_stress); } #if HAVE_MEMALIGN list_n = (long *) memalign(sizeof(long), (unsigned) lenbuf); #else list_n = (long *) malloc((unsigned) lenbuf); #endif if ( list_n == (long *) NULL ) { Error("Stress: failed to allocate list_n",n_stress); } if ( (buf1 = malloc((unsigned) 16376)) == (char *) NULL ) Error("Stress: failed to allocate buf1", (long) 16376); if ( (buf2 = malloc((unsigned) 16376)) == (char *) NULL ) Error("Stress: failed to allocate buf2", (long) 16376); if (me == 0) { /* Make random list of node pairs and message lengths */ RandList((long) 0, (long) (NNODES_()-1), list_i, n_stress); RandList((long) 0, (long) (NNODES_()-1), list_j, n_stress); RandList((long) 0, (long) (N_LEN-1), list_n, n_stress); for (i=0; i= nproc) ) Error("Stress: from is out of range", from); if ( (to < 0) || (to >= nproc) ) Error("Stress: to is out of range", to); if (from == to) continue; if ( (me == 0) && (j%mod == 0) ) { (void) printf("Stress: test=%ld: from=%ld, to=%ld, len=%ld\n", i, from, to, lenbuf); (void) fflush(stdout); } j++; if (from == me) SND_(&type, buf1, &lenbuf, &to, &sync); else if (to == me) { (void) bzero(buf2, (int) lenbuf); /* Initialize the receive buffer */ buf2[lenbuf] = '+'; RCV_(&type, buf2, &lenbuf, &lenmes, &from, &nodefrom, &sync); if (buf2[lenbuf] != '+') Error("Stress: overran buffer on receive",lenbuf); if (CheckByte((unsigned char *) buf1, lenbuf) != CheckByte((unsigned char *) buf2, lenbuf)) Error("Stress: invalid checksum on receive",lenbuf); if (lenmes != lenbuf) Error("Stress: invalid message length on receive",lenbuf); } } (void) free(buf2); (void) free(buf1); (void) free((char *) list_n); (void) free((char *) list_j); (void) free((char *) list_i); } void IsendTest() { #define N_ISEND_MSG 5 long me = NODEID_(); long type = 4; long left = (me + NNODES_() - 1) % NNODES_(); long right = (me + 1) % NNODES_(); char *source[N_ISEND_MSG], *dest[N_ISEND_MSG]; long lenbuf, lenmes, from; long sync = 1; long max_len; long ids[N_ISEND_MSG]; long i; if (me == 0) { (void) printf("Isend test ... test async. comms\n---------\n\n"); (void) printf("Input maximum message size: "); (void) fflush(stdout); if (scanf("%ld", &max_len) != 1) Error("IsendTest: error reading max_len", (long) -1); if ( (max_len <= 0) || (max_len >= 4*1024*1024) ) max_len = 256*1024; } type = 4 | MSGINT; lenbuf = sizeof(long); i = 0; BRDCST_(&type, (char *) &max_len, &lenbuf, &i); for (i=0; i= 4*1024*1024) ) max_len = 256*1024; } type = 4 | MSGINT; BRDCST_(&type, (char *) &max_len, &lenbuf, &i); if ( (buf = malloc((unsigned) max_len)) == (char *) NULL) Error("failed to allocate buf",max_len); if (me == 0) { if ( (buf2 = malloc((unsigned) max_len)) == (char *) NULL) Error("failed to allocate buf2",max_len); for (i=0; i 0) rate = 1.0e-6 * (double) (NNODES_() * lenbuf) / (double) used; else rate = 0.0; rate = rate * nloops; printf("len=%6ld bytes, nloop=%4ld, used=%8.4f s, rate=%8.4f Mb/s (0x%x, 0x%x)\n", lenbuf, nloops, used, rate, sum, sum2); (void) fflush(stdout); } else { while (loop--) { RCV_(&type, buf, &lenbuf, &lenmes, &right, &nodefrom, &sync); SND_(&type, buf, &lenbuf, &left, &sync); } } lenbuf *= 2; } if (me == 0) (void) free(buf2); (void) free(buf); } /** * Test receiveing a message from any node */ void RcvAnyTest() { long me = NODEID_(); long type = 337 | MSGINT; char buf[8]; long i, j, node, lenbuf, lenmes, nodefrom, receiver, n_msg; long sync = 1; lenbuf = sizeof(long); if (me == 0) { (void) printf("RCV any test ... check is working!\n-----------\n\n"); (void) printf("Input node to receive : "); (void) fflush(stdout); if (scanf("%ld", &receiver) != 1) Error("RcvAnyTest: error reading receiver",(long) -1); if ( (receiver < 0) || (receiver >= NNODES_()) ) receiver = NNODES_()-1; (void) printf("Input number of messages : "); (void) fflush(stdout); if (scanf("%ld", &n_msg) != 1) Error("RcvAnyTest: error reading n_msg",(long) -1); if ( (n_msg <= 0) || (n_msg > 10) ) n_msg = 5; } node = 0; BRDCST_(&type, (char *) &receiver, &lenbuf, &node); type++; BRDCST_(&type, (char *) &n_msg, &lenbuf, &node); type++; lenbuf = 0; type = 321; for (i=0; i= 10000) ) n_val = 100; } node = 0; BRDCST_(&type, (char *) &n_val, &lenbuf, &node); /* Loop thru a few values to visually show it is working */ next = -1; for (i=0; i<10; i++) { if (i > next) next = NXTVAL_(&nproc); if (i == next) { (void) printf("node %ld got value %ld\n",me, i); (void) fflush(stdout); } } nproc = -nproc; next = NXTVAL_(&nproc); nproc = -nproc; /* Now time it for real .. twice*/ for (ntimes=0; ntimes<2; ntimes++) { if (me == 0) start = TCGTIME_(); next = -1; ngot = 0; for (i=0; i next) next = NXTVAL_(&nproc); if (i == next) ngot++; } nproc = -nproc; next = NXTVAL_(&nproc); nproc = -nproc; if (me == 0) { used = TCGTIME_() - start; if(ngot){ rate = used / ngot; (void) printf("node 0: From %ld busy iters did %ld, used=%fs per call\n", n_val, ngot, rate); }else printf("node 0: From %ld busy iters did 0\n",n_val); (void) fflush(stdout); } type++; SYNCH_(&type); } } void ToggleDebug() { static long on = 0; long me = NODEID_(); long type = 666 | MSGINT; long lenbuf = sizeof(long); long from=0; long node; if (me == 0) { (void) printf("\nInput node to debug (-1 = all) : "); (void) fflush(stdout); if (scanf("%ld", &node) != 1) Error("ToggleDebug: error reading node",(long) -1); } BRDCST_(&type, (char *) &node, &lenbuf, &from); if ((node < 0) || (node == me)) { on = (on + 1)%2; SETDBG_(&on); } } int main(int argc, char **argv) { long type; long lenbuf; long node, opt; tcg_pbegin(argc, argv); (void) printf("In process %ld\n", NODEID_()); (void) fflush(stdout); /* Read user input for action */ lenbuf = sizeof(long); node = 0; while (1) { (void) fflush(stdout); if (NODEID_() == 0) (void) sleep(1); type = 999; SYNCH_(&type); (void) sleep(1); if (NODEID_() == 0) { again: (void) printf("\n\ 0=quit\n\ 1=Ring 5=NxtVal\n\ 2=Stress 6=Global\n\ 3=Hello 7=Debug\n\ 4=RcvAny 8=Probe\n\ 9=Isend\n\n\ Enter test number : "); (void) fflush(stdout); if (scanf("%ld", &opt) != 1) Error("test: input of option failed",(long) -1); (void) printf("\n"); (void) fflush(stdout); if ( (opt < 0) || (opt > 9) ) goto again; } type = 2 | MSGINT; BRDCST_(&type, (char *) &opt, &lenbuf, &node); switch (opt) { case 0: if (NODEID_() == 0) STATS_(); PEND_(); return 0; case 1: RingTest(); break; case 2: Stress(); break; case 3: Hello(); break; case 4: RcvAnyTest(); break; case 5: NextValueTest(); break; case 6: TestGlobals(); break; case 7: ToggleDebug(); break; case 8: TestProbe(); break; case 9: IsendTest(); break; default: Error("test: invalid option", opt); break; } } } ga-5.9.2/tcgmsg/tests/test2.c000066400000000000000000000443211500715745200157630ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif /* $Header: /tmp/hpctools/ga/tcgmsg/ipcv5.0/test.c,v 1.4 2002-01-28 19:42:04 d3h325 Exp $ */ #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_MEMORY_H # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_MALLOC_H # include #endif #include "evlog.h" #include "msgtypesc.h" #include "typesf2c.h" #include "tcgmsg.h" extern unsigned char CheckByte(); extern void Error(const char *, Integer); /** * Process 0 sleeps for 20 seconds and then sends each * process a message. The other processes sleep for periods * of 2 seconds and probes until it gets a message. All processes * respond to process 0 which recieves using a wildcard probe. */ static void TestProbe() { long type_syn = 32; long type_msg = 33; long type_ack = 34; long me = tcg_nodeid(); char buf; long lenbuf = sizeof buf; long sync = 1; if (me == 0) { (void) printf("Probe test ... processes should sleep for 20s only\n"); (void) printf("----------\n\n"); (void) fflush(stdout); } tcg_synch(type_syn); if (me == 0) { long nproc = tcg_nnodes(); long anyone = -1; long ngot = 0; long node; (void) sleep((unsigned) 20); for (node=1; node hi) ran = hi; list[i] = ran; } } /** * Stress the system by passing messages between a ranomly selected * list of nodes */ void Stress() { long me = tcg_nodeid(); long nproc = tcg_nnodes(); long type, lenbuf, node, lenmes, nodefrom, i, j, from, to; long *list_i, *list_j, *list_n; #define N_LEN 11 static long len[N_LEN] = {0,1,2,4,8,4063,4064,4065,4072,8127,8129}; char *buf1, *buf2; long n_stress, mod; long sync = 1; from = 0; lenbuf = sizeof(long); if (me == 0) { (void) printf("Stress test ... randomly exchange messages\n-----------"); (void) printf("\n\nInput no. of messages: "); (void) fflush(stdout); if (scanf("%ld",&n_stress) != 1) Error("Stress: error reading n_stress",(long) -1); if ( (n_stress <= 0) || (n_stress > 100000) ) n_stress = 100; } type = 13 | MSGINT; tcg_brdcst(type, (void *) &n_stress, lenbuf, from); type++; lenbuf = n_stress * sizeof(long); #if HAVE_MEMALIGN list_i = (long *) memalign(sizeof(long), (unsigned) lenbuf); #else list_i = (long *) malloc((unsigned) lenbuf); #endif if ( list_i == (long *) NULL ) { Error("Stress: failed to allocate list_i",n_stress); } #if HAVE_MEMALIGN list_j = (long *) memalign(sizeof(long), (unsigned) lenbuf); #else list_j = (long *) malloc((unsigned) lenbuf); #endif if ( list_j == (long *) NULL ) { Error("Stress: failed to allocate list_j",n_stress); } #if HAVE_MEMALIGN list_n = (long *) memalign(sizeof(long), (unsigned) lenbuf); #else list_n = (long *) malloc((unsigned) lenbuf); #endif if ( list_n == (long *) NULL ) { Error("Stress: failed to allocate list_n",n_stress); } if ( (buf1 = malloc((unsigned) 16376)) == (char *) NULL ) Error("Stress: failed to allocate buf1", (long) 16376); if ( (buf2 = malloc((unsigned) 16376)) == (char *) NULL ) Error("Stress: failed to allocate buf2", (long) 16376); if (me == 0) { /* Make random list of node pairs and message lengths */ RandList((long) 0, (long) (tcg_nnodes()-1), list_i, n_stress); RandList((long) 0, (long) (tcg_nnodes()-1), list_j, n_stress); RandList((long) 0, (long) (N_LEN-1), list_n, n_stress); for (i=0; i= nproc) ) Error("Stress: from is out of range", from); if ( (to < 0) || (to >= nproc) ) Error("Stress: to is out of range", to); if (from == to) continue; if ( (me == 0) && (j%mod == 0) ) { (void) printf("Stress: test=%ld: from=%ld, to=%ld, len=%ld\n", i, from, to, lenbuf); (void) fflush(stdout); } j++; if (from == me) tcg_snd(type, buf1, lenbuf, to, sync); else if (to == me) { (void) bzero(buf2, (int) lenbuf); /* Initialize the receive buffer */ buf2[lenbuf] = '+'; tcg_rcv(type, buf2, lenbuf, &lenmes, from, &nodefrom, sync); if (buf2[lenbuf] != '+') Error("Stress: overran buffer on receive",lenbuf); if (CheckByte((unsigned char *) buf1, lenbuf) != CheckByte((unsigned char *) buf2, lenbuf)) Error("Stress: invalid checksum on receive",lenbuf); if (lenmes != lenbuf) Error("Stress: invalid message length on receive",lenbuf); } } (void) free(buf2); (void) free(buf1); (void) free((char *) list_n); (void) free((char *) list_j); (void) free((char *) list_i); } /** * Time passing a message round a ring * */ void RingTest() { long me = tcg_nodeid(); long type = 4; long left = (me + tcg_nnodes() - 1) % tcg_nnodes(); long right = (me + 1) % tcg_nnodes(); char *buf, *buf2; unsigned char sum, sum2; long lenbuf, lenmes, nodefrom; double start, used, rate; long max_len; long i; long sync = 1; i = 0; lenbuf = sizeof(long); if (me == 0) { (void) printf("Ring test ... time network performance\n---------\n\n"); (void) printf("Input maximum message size: "); (void) fflush(stdout); if (scanf("%ld", &max_len) != 1) Error("RingTest: error reading max_len",(long) -1); if ( (max_len <= 0) || (max_len >= 4*1024*1024) ) max_len = 256*1024; } type = 4 | MSGINT; tcg_brdcst(type, (void *) &max_len, lenbuf, i); if ( (buf = malloc((unsigned) max_len)) == (char *) NULL) Error("failed to allocate buf",max_len); if (me == 0) { if ( (buf2 = malloc((unsigned) max_len)) == (char *) NULL) Error("failed to allocate buf2",max_len); for (i=0; i 0) rate = 1.0e-6 * (double) (tcg_nnodes() * lenbuf) / (double) used; else rate = 0.0; rate = rate * nloops; printf("len=%6ld bytes, nloop=%4d, used=%8.4f s, rate=%8.4f Mb/s (0x%x, 0x%x)\n", lenbuf, nloops, used, rate, sum, sum2); (void) fflush(stdout); } else { while (loop--) { tcg_rcv(type, buf, lenbuf, &lenmes, right, &nodefrom, sync); tcg_snd(type, buf, lenbuf, left, sync); } } lenbuf *= 2; } if (me == 0) (void) free(buf2); (void) free(buf); } /** * Test receiveing a message from any node */ void RcvAnyTest() { long me = tcg_nodeid(); long type = 337 | MSGINT; char buf[8]; long i, j, node, lenbuf, lenmes, nodefrom, receiver, n_msg; long sync = 1; lenbuf = sizeof(long); if (me == 0) { (void) printf("RCV any test ... check is working!\n-----------\n\n"); (void) printf("Input node to receive : "); (void) fflush(stdout); if (scanf("%ld", &receiver) != 1) Error("RcvAnyTest: error reading receiver",(long) -1); if ( (receiver < 0) || (receiver >= tcg_nnodes()) ) receiver = tcg_nnodes()-1; (void) printf("Input number of messages : "); (void) fflush(stdout); if (scanf("%ld", &n_msg) != 1) Error("RcvAnyTest: error reading n_msg",(long) -1); if ( (n_msg <= 0) || (n_msg > 10) ) n_msg = 5; } node = 0; tcg_brdcst(type, (void *) &receiver, lenbuf, node); type++; tcg_brdcst(type, (void *) &n_msg, lenbuf, node); type++; lenbuf = 0; type = 321; for (i=0; i= 10000) ) n_val = 100; } node = 0; tcg_brdcst(type, (void *) &n_val, lenbuf, node); /* Loop thru a few values to visually show it is working */ next = -1; for (i=0; i<10; i++) { if (i > next) next = tcg_nxtval(nproc); if (i == next) { (void) printf("node %ld got value %ld\n",me, i); (void) fflush(stdout); } } nproc = -nproc; next = tcg_nxtval(nproc); nproc = -nproc; /* Now time it for real .. twice*/ for (ntimes=0; ntimes<2; ntimes++) { if (me == 0) start = tcg_time(); next = -1; ngot = 0; for (i=0; i next) next = tcg_nxtval(nproc); if (i == next) ngot++; } nproc = -nproc; next = tcg_nxtval(nproc); nproc = -nproc; if (me == 0) { used = tcg_time() - start; if(ngot){ rate = used / ngot; (void) printf("node 0: From %ld busy iters did %ld, used=%fs per call\n", n_val, ngot, rate); }else printf("node 0: From %ld busy iters did 0\n",n_val); (void) fflush(stdout); } type++; tcg_synch(type); } } void ToggleDebug() { static long on = 0; long me = tcg_nodeid(); long type = 666 | MSGINT; long lenbuf = sizeof(long); long from=0; long node; if (me == 0) { (void) printf("\nInput node to debug (-1 = all) : "); (void) fflush(stdout); if (scanf("%ld", &node) != 1) Error("ToggleDebug: error reading node",(long) -1); } tcg_brdcst(type, (void *) &node, lenbuf, from); if ((node < 0) || (node == me)) { on = (on + 1)%2; tcg_setdbg(on); } } int main(int argc, char **argv) { long type; long lenbuf; long node, opt; tcg_alt_pbegin(&argc, &argv); (void) printf("In process %ld\n", tcg_nodeid()); (void) fflush(stdout); /* Read user input for action */ lenbuf = sizeof(long); node = 0; while (1) { (void) fflush(stdout); if (tcg_nodeid() == 0) (void) sleep(1); type = 999; tcg_synch(type); (void) sleep(1); if (tcg_nodeid() == 0) { again: (void) printf("\n\ 0=quit\n\ 1=Ring 5=NxtVal\n\ 2=Stress 6=Global\n\ 3=Hello 7=Debug\n\ 4=RcvAny 8=Probe\n\n\ Enter test number : "); (void) fflush(stdout); if (scanf("%ld", &opt) != 1) Error("test: input of option failed",(long) -1); (void) printf("\n"); (void) fflush(stdout); if ( (opt < 0) || (opt > 8) ) goto again; } type = 2 | MSGINT; tcg_brdcst(type, (void *) &opt, lenbuf, node); switch (opt) { case 0: if (tcg_nodeid() == 0) tcg_stats(); tcg_pend(); return 0; case 1: RingTest(); break; case 2: Stress(); break; case 3: Hello(); break; case 4: RcvAnyTest(); break; case 5: NextValueTest(); break; case 6: TestGlobals(); break; case 7: ToggleDebug(); break; case 8: TestProbe(); break; default: Error("test: invalid option", opt); break; } } return 0; } ga-5.9.2/tcgmsg/tests/test3.c000066400000000000000000000453221500715745200157660ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #if HAVE_STDLIB_H # include #endif #if HAVE_MALLOC_H # include #endif #if HAVE_MEMORY_H # include #endif #if HAVE_UNISTD_H # include #elif HAVE_WINDOWS_H # include # define sleep(x) Sleep(1000*(x)) #endif #define MAXLENG 256*1024 #define N_LEN 11 #include "typesf2c.h" #include #include extern unsigned char CheckByte(); extern void Error(const char *, Integer); /** * Process 0 sleeps for 20 seconds and then sends each * process a message. The other processes sleep for periods * of 2 seconds and probes until it gets a message. All processes * respond to process 0 which recieves using a wildcard probe. */ static void TestProbe() { long type_syn = 32; long type_msg = 33; long type_ack = 34; long me = tcg_nodeid(); char buf; long lenbuf = sizeof buf; long sync = 1; if (me == 0) { (void) printf("Probe test ... processes should sleep for 20s only\n"); (void) printf("----------\n\n"); (void) fflush(stdout); } tcg_synch(type_syn); if (me == 0) { long nproc = tcg_nnodes(); long anyone = -1; long ngot = 0; long node; (void) sleep((unsigned) 20); for (node=1; node hi) { ran = hi; } list[i] = ran; } } /** * Stress the system by passing messages between a ranomly selected * list of nodes */ void Stress() { long me = tcg_nodeid(); long nproc = tcg_nnodes(); long type, lenbuf, node, lenmes, nodefrom, i, j, from, to; long *list_i, *list_j, *list_n; static long len[N_LEN] = {0,1,2,4,8,4095,4096,4097,16367,16368,16369}; char *buf1, *buf2; long n_stress, mod; long sync = 1; from = 0; lenbuf = sizeof(long); if (me == 0) { (void) printf("Stress test ... randomly exchange messages\n---------"); (void) printf("\n\nInput no. of messages: "); (void) fflush(stdout); if (scanf("%ld",&n_stress) != 1) { Error("Stress: error reading n_stress",(long) -1); } if ( (n_stress <= 0) || (n_stress > 100000) ) { n_stress = 100; } } type = 13 | MSGINT; tcg_brdcst(type, (void *) &n_stress, lenbuf, from); type++; lenbuf = n_stress * sizeof(long); #if HAVE_MEMALIGN list_i = (long *) memalign(sizeof(long), (unsigned) lenbuf); #else list_i = (long *) malloc((unsigned) lenbuf); #endif if ( list_i == (long *) NULL ) { Error("Stress: failed to allocate list_i",n_stress); } #if HAVE_MEMALIGN list_j = (long *) memalign(sizeof(long), (unsigned) lenbuf); #else list_j = (long *) malloc((unsigned) lenbuf); #endif if ( list_j == (long *) NULL ) { Error("Stress: failed to allocate list_j",n_stress); } #if HAVE_MEMALIGN list_n = (long *) memalign(sizeof(long), (unsigned) lenbuf); #else list_n = (long *) malloc((unsigned) lenbuf); #endif if ( list_n == (long *) NULL ) { Error("Stress: failed to allocate list_n",n_stress); } if ( (buf1 = malloc((unsigned) 16376)) == (char *) NULL ) { Error("Stress: failed to allocate buf1", (long) 16376); } if ( (buf2 = malloc((unsigned) 16376)) == (char *) NULL ) { Error("Stress: failed to allocate buf2", (long) 16376); } if (me == 0) { /* Make random list of node pairs and message lengths */ RandList((long) 0, (long) (tcg_nnodes()-1), list_i, n_stress); RandList((long) 0, (long) (tcg_nnodes()-1), list_j, n_stress); RandList((long) 0, (long) (N_LEN-1), list_n, n_stress); for (i=0; i= nproc) ) { Error("Stress: from is out of range", from); } if ( (to < 0) || (to >= nproc) ) { Error("Stress: to is out of range", to); } if (from == to) { continue; } if ( (me == 0) && (j%mod == 0) ) { (void) printf("Stress: test=%ld: from=%ld, to=%ld, len=%ld\n", i, from, to, lenbuf); (void) fflush(stdout); } j++; if (from == me) { tcg_snd(type, buf1, lenbuf, to, sync); } else if (to == me) { (void) bzero(buf2, (int) lenbuf); /* Initialize the rcv buffer */ buf2[lenbuf] = '+'; tcg_rcv(type, buf2, lenbuf, &lenmes, from, &nodefrom, sync); if (buf2[lenbuf] != '+') { Error("Stress: overran buffer on receive",lenbuf); } if (CheckByte((unsigned char *) buf1, lenbuf) != CheckByte((unsigned char *) buf2, lenbuf)) { Error("Stress: invalid checksum on receive",lenbuf); } if (lenmes != lenbuf) { Error("Stress: invalid message length on receive",lenbuf); } } } (void) free(buf2); (void) free(buf1); (void) free((char *) list_n); (void) free((char *) list_j); (void) free((char *) list_i); } /** Time passing a message round a ring */ void RingTest() { long me = tcg_nodeid(); long type = 4; long left = (me + tcg_nnodes() - 1) % tcg_nnodes(); long right = (me + 1) % tcg_nnodes(); char *buf=NULL, *buf2=NULL; unsigned char sum, sum2; long lenbuf, lenmes, nodefrom; double start, used, rate; long max_len; long i; long sync = 1; i = 0; lenbuf = sizeof(long); if (me == 0) { (void) printf("Ring test ... time network performance\n---------\n\n"); (void) printf("Input maximum message size: "); (void) fflush(stdout); if (scanf("%ld", &max_len) != 1) { Error("RingTest: error reading max_len",(long) -1); } if ( (max_len <= 0) || (max_len >= 4*1024*1024) ) { max_len = 256*1024; } } type = 4 | MSGINT; tcg_brdcst(type, (void *) &max_len, lenbuf, i); if ( (buf = malloc((unsigned) max_len)) == (char *) NULL) { Error("failed to allocate buf",max_len); } if (me == 0) { if ( (buf2 = malloc((unsigned) max_len)) == (char *) NULL) { Error("failed to allocate buf2",max_len); } for (i=0; i 0) { rate = 1.0e-6 * (double) (tcg_nnodes() * lenbuf) / (double) used; } else { rate = 0.0; } rate = rate * nloops; printf("len=%ld bytes, nloop=%d, used=%8.4f s, rate=%8.4f Mb/s (0x%x, 0x%x)\n", lenbuf, nloops, used, rate, sum, sum2); (void) fflush(stdout); } else { while (loop--) { tcg_rcv(type, buf, lenbuf, &lenmes, right, &nodefrom, sync); tcg_snd(type, buf, lenbuf, left, sync); } } lenbuf *= 2; } if (me == 0) { (void) free(buf2); } (void) free(buf); } /** Test receiveing a message from any node */ void RcvAnyTest() { long me = tcg_nodeid(); long type = 337 | MSGINT; char buf[8]; long i, j, node, lenbuf, lenmes, nodefrom, receiver, n_msg; long sync = 1; lenbuf = sizeof(long); if (me == 0) { (void) printf("RCV any test ... check is working!\n-----------\n\n"); (void) printf("Input node to receive : "); (void) fflush(stdout); if (scanf("%ld", &receiver) != 1) { Error("RcvAnyTest: error reading receiver",(long) -1); } if ( (receiver < 0) || (receiver >= tcg_nnodes()) ) { receiver = tcg_nnodes()-1; } (void) printf("Input number of messages : "); (void) fflush(stdout); if (scanf("%ld", &n_msg) != 1) { Error("RcvAnyTest: error reading n_msg",(long) -1); } if ( (n_msg <= 0) || (n_msg > 10) ) { n_msg = 5; } } node = 0; tcg_brdcst(type, (void *) &receiver, lenbuf, node); type++; tcg_brdcst(type, (void *) &n_msg, lenbuf, node); type++; lenbuf = 0; type = 321; for (i=0; i= 10000) ) { n_val = 100; } } node = 0; tcg_brdcst(type, (void *) &n_val, lenbuf, node); /* Loop thru a few values to visually show it is working */ next = -1; for (i=0; i<10; i++) { if (i > next) { next = tcg_nxtval(nproc); } sleep(1); if (i == next) { (void) printf("node %ld got value %ld\n", me, i); (void) fflush(stdout); } } nproc = -nproc; next = tcg_nxtval(nproc); nproc = -nproc; /* Now time it for real .. twice*/ for (ntimes=0; ntimes<2; ntimes++) { if (me == 0) { start = tcg_time(); } next = -1; ngot = 0; for (i=0; i next) { next = tcg_nxtval(nproc); } if (i == next) { ngot++; } } nproc = -nproc; next = tcg_nxtval(nproc); nproc = -nproc; if (me == 0) { used = tcg_time() - start; rate = ngot ? used / ngot: 0.; printf("node 0: From %ld busy iters did %ld, used=%lfs per call\n", n_val, ngot, rate); fflush(stdout); } type++; tcg_synch(type); } } void ToggleDebug() { static long on = 0; long me = tcg_nodeid(); long type = 666 | MSGINT; long lenbuf = sizeof(long); long from=0; long node; if (me == 0) { (void) printf("\nInput node to debug (-1 = all) : "); (void) fflush(stdout); if (scanf("%ld", &node) != 1) { Error("ToggleDebug: error reading node",(long) -1); } } tcg_brdcst(type, (void *) &node, lenbuf, from); if ((node < 0) || (node == me)) { on = (on + 1)%2; tcg_setdbg(on); } } int main(int argc, char **argv) { long type; long lenbuf; long node, opt; tcg_alt_pbegin(&argc, &argv); (void) printf("In process %ld\n", tcg_nodeid()); (void) fflush(stdout); /* Read user input for action */ lenbuf = sizeof(long); node = 0; while (1) { (void) fflush(stdout); if (tcg_nodeid() == 0) { (void) sleep(1); } type = 999; tcg_synch(type); (void) sleep(1); if (tcg_nodeid() == 0) { again: (void) printf("\n\ 0=quit\n\ 1=Ring 5=NxtVal\n\ 2=Stress 6=Global\n\ 3=Hello 7=Debug\n\ 4=RcvAny 8=Probe\n\n\ Enter test number : "); (void) fflush(stdout); if (scanf("%ld", &opt) != 1) { Error("test: input of option failed",(long) -1); } (void) printf("\n"); (void) fflush(stdout); if ( (opt < 0) || (opt > 8) ) { goto again; } } type = 2 | MSGINT; tcg_brdcst(type, (void *) &opt, lenbuf, node); switch (opt) { case 0: if (tcg_nodeid() == 0) { tcg_stats(); } tcg_pend(); return 0; case 1: RingTest(); break; case 2: Stress(); break; case 3: Hello(); break; case 4: RcvAnyTest(); break; case 5: NextValueTest(); break; case 6: TestGlobals(); break; case 7: ToggleDebug(); break; case 8: TestProbe(); break; default: Error("test: invalid option", opt); break; } } } ga-5.9.2/tcgmsg/tests/test_arcv.f000066400000000000000000000015371500715745200167210ustar00rootroot00000000000000 program async integer nodeid, nnodes integer me, proc integer req, ack, len,node, to c call pbeginf proc = nnodes() me = nodeid() print *,'proc=',proc,' node=',me if(proc.lt.2) then call pend() return endif c call setdbg(1) if(me.eq.0)then print *, 'Checking non-blocking receive' call rcv(33, req,1, len, 1, node, 1) !blocking print *, 'received request ',me call snd(34, ack,1, 1, 1) endif c if(me.eq.1)then call rcv(34, ack, 1, len, 0, node, 0)!nonblocking print *,'after nonblocking receive =',me call snd(33, req,1, 0, 0) print *,'after nonblocking send =',me call waitcom(0) print *, 'OK' endif call pend() end ga-5.9.2/tcgmsg/tests/test_asyn.c000066400000000000000000000023421500715745200167300ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #include #if HAVE_STDIO_H # include #endif int main(int argc, char **argv) { int numprocs, myid; int ierr; char req=0, ack=0; int atag=999, rtag=555, to; MPI_Status status; MPI_Request request; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &numprocs); MPI_Comm_rank(MPI_COMM_WORLD, &myid); if(myid==0){ to = numprocs-1; printf("Testing nonblocking receive\n\n"); fflush(stdout); ierr = MPI_Irecv(&ack, 1, MPI_CHAR, to, atag ,MPI_COMM_WORLD, &request); printf(":after nonblocking receive\n"); fflush(stdout); ierr = MPI_Send(&req, 1, MPI_CHAR, to, rtag, MPI_COMM_WORLD); printf(":sent request\n"); fflush(stdout); ierr = MPI_Wait(&request, &status); printf(":received response\n"); fflush(stdout); printf("\nnonblocking receive is working\n"); fflush(stdout); } if(myid==numprocs-1){ to = 0; ierr = MPI_Recv(&req, 1, MPI_CHAR, to, rtag, MPI_COMM_WORLD, &status); printf("::request received\n"); fflush(stdout); ierr = MPI_Send(&ack, 1, MPI_CHAR, to, atag, MPI_COMM_WORLD); } MPI_Finalize(); return 0; } ga-5.9.2/tcgmsg/tests/testargf.f000066400000000000000000000001731500715745200165410ustar00rootroot00000000000000c $Header: /tmp/hpctools/ga/tcgmsg/ipcv4.0/testargf.f,v 1.4 1995-02-24 02:17:59 d3h325 Exp $ call parg end ga-5.9.2/tcgmsg/tests/testf.F000066400000000000000000000112001500715745200160000ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif program main implicit double precision (a-h,o-z) c c FORTRAN program to test message passing routines c c LOG is the FORTRAN unit number for standard output. c parameter (LOG = 6) parameter (MAXLEN = 262144 ) #include "msgtypesf.h" dimension buf(MAXLEN) integer ibuf(MAXLEN) character*80 fname integer dtype c c Always the first thing to do is call pbeginf c call pbeginf call setdbg(0) call evon c c who am i and how many processes c nproc = nnodes() me = nodeid() c c now try broadcasting messages from all nodes to every other node c send each process my id as a message c call evbgin('Hello test') itype = 1 + MSGINT do 10 iproc = 0,nproc-1 itest = me call brdcst(itype, itest, mitob(1),iproc) if (iproc.ne.me) then write(LOG,1) me, itest 1 format(' me=',i3,', itest=',i3) endif 10 continue call evend('Hello test') call evbgin('Counter test') c c now try using the shared counter c mproc = nproc do 20 i = 1,10 write(LOG,*) ' process ',me,' got ',nxtval(mproc) 20 continue junk = nxtval(-mproc) call evend('Counter test') c c now time sending a message round a ring c if (nproc.gt.1) then call evbgin('Ring test') itype = 3 left = mod(me + nproc - 1, nproc) iright = mod(me + 1, nproc) c lenbuf = 1 30 if (me .eq. 0) then start = tcgtime() call snd(itype, buf, lenbuf, left, 1) call rcv(itype, buf, lenbuf, lenmes, iright, node, 1) used = tcgtime() - start if (used.gt.0d0) then rate = 1.0d-6 * dble(nproc * lenbuf) / used else rate = 0.0d0 endif write(LOG,31) lenbuf, used, rate else call rcv(itype, buf, lenbuf, lenmes, iright, node, 1) call snd(itype, buf, lenbuf, left, 1) endif lenbuf = lenbuf * 2 if (lenbuf .le. mdtob(MAXLEN)) goto 30 31 format(' len=',i7,' bytes, used=',f8.2,' cs, rate=',f10.6,' Mb/s') call evend('Ring test') endif c c global sums c do i=1,MAXLEN ibuf(i) = i*me buf(i) = dble(ibuf(i)) enddo dtype=1+MSGDBL call igop(itype, ibuf, MAXLEN, "+") call dgop(dtype, buf, MAXLEN, "+") do i=1,MAXLEN iresult = i*nproc*(nproc-1)/2 if (ibuf(i).ne.iresult.or.buf(i).ne.dble(iresult)) . call error('TestGlobals: global sum failed', i) enddo if (me.eq.0) write(LOG,*) 'global sums OK' c c c Check that everyone can open, write, read and close c a binary FORTRAN file c call pfname('junk',fname) open(9,file=fname,form='unformatted',status='unknown', & err=1000) write(9,err=1001) buf rewind 9 read(9,err=1002) buf close(9,status='delete') call event('Read file OK') c if (me.eq.0) call stats c c Always the last thing to do is call pend c call pend c c check that everyone makes it thru after pend .. NODEID c is not actually guaranteed to work outside of pbegin/pend c section ... it may return junk. All you should do is exit c is some FORTRAN supported fashion c write(LOG,32) nodeid() 32 format(' Process ',i4,' after pend') stop c c error returns for FORTRAN I/O c 1000 call error('failed to open fortran binary file',-1) 1001 call error('failed to write fortran binary file',-1) 1002 call error('failed to read fortran binary file',-1) c end subroutine pfname(name, fname) character*(*) name, fname c c construct a unique filename by appending the process c number after the stub name c i.e. = . c c find last non-blank character in name c do 10 i = len(name),1,-1 if (name(i:i).ne.' ') goto 20 10 continue call error('pfname: name is all blanks!',i) c c check that have room to store result and then write result c 20 if (i+4.gt.len(fname)) & call error('pfname: fname too short for name.id',len(fname)) fname = name write(fname(i+1:i+4),1) nodeid() 1 format('.',i3.3) c end subroutine error(s,i) parameter (LOG = 6) character*(*) s integer i c write(LOG,1) s,i 1 format(// $ ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'/ $ 1x,a,1x,i8/ $ ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'/) c $ 1x,a,1x,i8/ c call parerr(i) c end ga-5.9.2/tcgmsg/tests/testf2.F000066400000000000000000000111401500715745200160650ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif program main implicit double precision (a-h,o-z) c c $Header: /tmp/hpctools/ga/tcgmsg/ipcv5.0/testf.f,v 1.2 2001-05-08 17:30:39 edo Exp $ c c FORTRAN program to test message passing routines c c LOG is the FORTRAN unit number for standard output. c parameter (LOG = 6) parameter (MAXLEN = 262144 / 8) #include "msgtypesf.h" dimension buf(MAXLEN) integer ibuf(MAXLEN) character*80 fortnn character*80 fname c c Always the first thing to do is call pbeginf c call pbeginf call setdbg(0) c c who am i and how many processes c nproc = nnodes() me = nodeid() c c now try broadcasting messages from all nodes to every other node c send each process my id as a message c itype = 1 + MSGINT do 10 iproc = 0,nproc-1 itest = me call brdcst(itype, itest, mitob(1),iproc) if (iproc.ne.me) then write(LOG,1) me, itest 1 format(' me=',i3,', itest=',i3) endif 10 continue c c now try using the shared counter c mproc = nproc do 20 i = 1,40 write(LOG,*) ' process ',me,' got ',nxtval(mproc) 20 continue junk = nxtval(-mproc) c c now time sending a message round a ring c if (nproc.gt.1) then itype = 3 left = mod(me + nproc - 1, nproc) iright = mod(me + 1, nproc) c lenbuf = 1 30 if (me .eq. 0) then istart = mtime() call snd(itype, buf, lenbuf, left, 1) call rcv(itype, buf, lenbuf, lenmes, iright, node, 1) iused = mtime() - istart if (iused.gt.0) then rate = 1.0d-4 * dble(nproc * lenbuf) / dble(iused) else rate = 0.0d0 endif write(LOG,31) lenbuf, iused, rate else call rcv(itype, buf, lenbuf, lenmes, iright, node, 1) call snd(itype, buf, lenbuf, left, 1) endif lenbuf = lenbuf * 2 if (lenbuf .le. mdtob(MAXLEN)) goto 30 31 format(' len=',i7,' bytes, used=',i4,' cs, rate=',f10.6,' Mb/s') endif c c global sums c do i=1,MAXLEN ibuf(i) = i*me buf(i) = dble(ibuf(i)) enddo dtype=1+MSGDBL call igop(itype, ibuf, MAXLEN, "+") call dgop(dtype, buf, MAXLEN, "+") do i=1,MAXLEN iresult = i*nproc*(nproc-1)/2 if (ibuf(i).ne.iresult.or.buf(i).ne.dble(iresult)) . call error('TestGlobals: global sum failed', i) enddo if (me.eq.0) write(LOG,*) 'global sums OK' c c c Check that everyone can open, write, read and close c a binary FORTRAN file c do ii=29,45 write(fortnn,1234) ii 1234 format('fort',i2) call pfname(fortnn,fname) open(ii,file=fname,form='unformatted',status='unknown', & err=1000) enddo do ii=11,19 write(ii,err=1001) buf rewind ii read(ii,err=1002) buf close(ii,status='delete') enddo c if (me.eq.0) call stats c c Always the last thing to do is call pend c call pend c c check that everyone makes it thru after pend .. NODEID c is not actually guaranteed to work outside of pbegin/pend c section ... it may return junk. All you should do is exit c is some FORTRAN supported fashion c write(LOG,32) nodeid() 32 format(' Process ',i4,' after pend') stop c c error returns for FORTRAN I/O c 1000 call error('failed to open fortran binary file',-1) 1001 call error('failed to write fortran binary file',-1) 1002 call error('failed to read fortran binary file',-1) c end subroutine pfname(name, fname) character*(*) name, fname c c construct a unique filename by appending the process c number after the stub name c i.e. = . c c find last non-blank character in name c do 10 i = len(name),1,-1 if (name(i:i).ne.' ') goto 20 10 continue call error('pfname: name is all blanks!',i) c c check that have room to store result and then write result c 20 if (i+4.gt.len(fname)) & call error('pfname: fname too short for name.id',len(fname)) fname = name write(fname(i+1:i+4),1) nodeid() 1 format('.',i3.3) c end subroutine error(s,i) parameter (LOG = 6) character*(*) s integer i c write(LOG,1) s,i 1 format(// $ ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'/ $ 1x,a,1x,i8/ $ ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'/) c $ 1x,a,1x,i8/ c call parerr(i) c end ga-5.9.2/tcgmsg/tests/testf3.F000066400000000000000000000120711500715745200160720ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.fh" #endif program main implicit double precision (a-h,o-z) c c $Header: /tmp/hpctools/ga/tcgmsg-mpi/testf.F,v 1.1.12.1 2006-12-14 13:24:56 manoj Exp $ c c FORTRAN program to test message passing routines c c LOG is the FORTRAN unit number for standard output. c parameter (LOG = 6) parameter (MAXLEN = 262144 / 8) #include "msgtypesf.h" dimension buf(MAXLEN) integer ibuf(MAXLEN) character*80 fname integer IUNIT double precision tcgtime, start,used c c Always the first thing to do is call pbeginf c call pbeginf call setdbg(0) call evon c c who am i and how many processes c nproc = nnodes() me = nodeid() c c now try broadcasting messages from all nodes to every other node c send each process my id as a message c call evbgin('Hello test') itype = 1 + MSGINT do 10 iproc = 0,nproc-1 itest = me call brdcst(itype, itest, mitob(1),iproc) if (iproc.ne.me) then write(LOG,1) me, itest 1 format(' me=',i3,', itest=',i3) endif 10 continue call evend('Hello test') call evbgin('Counter test') c c now try using the shared counter c mproc = nproc do 20 i = 1,10 write(LOG,*) ' process ',me,' got ',nxtval(mproc) 20 continue junk = nxtval(-mproc) call evend('Counter test') c c now time sending a message round a ring c if (nproc.gt.1) then call evbgin('Ring test') itype = 3 left = mod(me + nproc - 1, nproc) iright = mod(me + 1, nproc) c lenbuf = 1 30 if (me .eq. 0) then start = tcgtime() call snd(itype, buf, lenbuf, left, 1) call rcv(itype, buf, lenbuf, lenmes, iright, node, 1) used = tcgtime() - start if (used.gt.0) then c rate = 1.0d-4 * dble(nproc * lenbuf) / dble(iused) rate = 1.0d-6 * dble(nproc * lenbuf) / used else rate = 0.0d0 endif write(LOG,31) lenbuf, used, rate else call rcv(itype, buf, lenbuf, lenmes, iright, node, 1) call snd(itype, buf, lenbuf, left, 1) endif lenbuf = lenbuf * 2 if (lenbuf .le. mdtob(MAXLEN)) goto 30 31 format(' len=',i7,'bytes, used=',f10.6,'s, rate=',f10.6,'Mb/s') call evend('Ring test') endif c c global sums c do i=1,MAXLEN ibuf(i) = i*me buf(i) = dble(ibuf(i)) enddo dtype=1+MSGDBL call igop(itype, ibuf, MAXLEN, "+") call dgop(dtype, buf, MAXLEN, "+") do i=1,MAXLEN iresult = i*nproc*(nproc-1)/2 if (ibuf(i).ne.iresult) then print *,'error',i,ibuf(i),iresult call error('TestGlobals: global sum (igop) failed', i) endif if (buf(i).ne.dble(iresult)) then print *,'error',i,buf(i),iresult call error('TestGlobals: global sum (dgop) failed', i) endif enddo if (me.eq.0) write(LOG,*) 'global sums OK' c c c Check that everyone can open, write, read and close c a binary FORTRAN file c IUNIT = 9+me call pfname('junk',fname) open(IUNIT,file=fname,form='unformatted',status='unknown', & err=1000) write(IUNIT,err=1001) buf rewind IUNIT read(IUNIT,err=1002) buf close(IUNIT,status='delete') call event('Read file OK') c if (me.eq.0) call stats c c Always the last thing to do is call pend c call pend c c check that everyone makes it thru after pend .. NODEID c is not actually guaranteed to work outside of pbegin/pend c section ... it may return junk. All you should do is exit c is some FORTRAN supported fashion c write(LOG,32) me 32 format(' Process ',i4,' after pend') stop c c error returns for FORTRAN I/O c 1000 call error('failed to open fortran binary file',-1) 1001 call error('failed to write fortran binary file',-1) 1002 call error('failed to read fortran binary file',-1) c end subroutine pfname(name, fname) character*(*) name, fname c c construct a unique filename by appending the process c number after the stub name c i.e. = . c c find last non-blank character in name c do 10 i = len(name),1,-1 if (name(i:i).ne.' ') goto 20 10 continue call error('pfname: name is all blanks!',i) c c check that have room to store result and then write result c 20 if (i+4.gt.len(fname)) & call error('pfname: fname too short for name.id',len(fname)) fname = name write(fname(i+1:i+4),1) nodeid() 1 format('.',i3.3) c end subroutine error(s,i) parameter (LOG = 6) character*(*) s integer i c write(LOG,1) s,i 1 format(// $ ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'/ $ 1x,a,1x,i8/ $ ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'/) c $ 1x,a,1x,i8/ c call parerr(i) c end ga-5.9.2/tcgmsg/tests/testmap.c000066400000000000000000000014271500715745200163770ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H # include #endif #include "typesf2c.h" int ncols; int nrows; /* Given the col and row no. return the actual process no. */ #define MAP(Row,Col) (ncols*(Row) + (Col)) /* Given the node return the row no. */ #define ROW(Node) ((Node) / ncols) /* Given the node return the column no. */ #define COL(Node) ((Node) - ncols*((Node)/ncols)) int main(int argc, char **argv) { int node, type, len, me; (void) printf("Input nrows, ncols "); (void) scanf("%d %d",&nrows, &ncols); node = 0; type = 1; len = 4; for (me=0; me<(nrows*ncols); me++) { (void) printf(" me=%d row=%d col=%d map=%d\n",me, ROW(me),COL(me),MAP(ROW(me),COL(me))); } return 0; } ga-5.9.2/tcgmsg/tests/testpf.f000066400000000000000000000004201500715745200162220ustar00rootroot00000000000000c $Header: /tmp/hpctools/ga/tcgmsg/ipcv4.0/testpf.f,v 1.3 1995-02-24 02:18:01 d3h325 Exp $ character*60 fname call pbeginf fname = ' ' write(fname,'(a,i3.3)') '/tmp/pfcopy.test',nodeid() call pfcopy(5, 0, fname) call pend end ga-5.9.2/tcgmsg/tests/timer.f000066400000000000000000000007751500715745200160520ustar00rootroot00000000000000 double precision function timer() c c return the time since the last call to timer. c c must be initialized by calling once and throwing away the c value c ... use cpu time on multi-user machines c ... use elapsed time on dedicated or single user machines. c *mdc*if unix * real*4 dtime, tt(2) * timer = dble(dtime(tt)) *mdc*elseif tcgmsg save mlast data mlast/0/ m = mtime() timer = dble(m - mlast) * 0.01d0 mlast = m *mdc*endif c end ga-5.9.2/tcgmsg/tests/toplot.c000066400000000000000000000100141500715745200162330ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_LIBPLOT #if HAVE_STDIO_H # include #endif #if HAVE_CTYPE_H # include #endif extern void openpl(); extern void erase(); extern void label(); extern void line(); extern void circle(); extern void arc(); extern void move(); extern void cont(); extern void point(); extern void linemod(); extern void space(); extern void closepl(); void Error(char *string, int integer) { (void) fflush(stdout); (void) fprintf(stderr,"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); (void) fprintf(stderr,"%s %d (0x%x)\n",string, integer, integer); (void) fprintf(stderr,"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); (void) fflush(stderr); (void) exit(1); } static int xmin = 0; static int ymin = 0; #define SCALE(X, XMIN, XSCALE) \ 2048 + (int) (XSCALE * (DoublePrecision) ( (X) - (XMIN) ) ) /** * Crude ascii interface into UNIX binary plot file format. * * All co-ordinates are integer values. * * s xmin ymin xmax ymax ... defines user co-ordinates * * f 1 ... solid lines * f 2 ... dotted * f 3 ... shortdashed * f 4 ... longdashed * * t x y text_string_with_no_spaces_in_it ... draw text at coords x,y * * l x1 y1 x2 y2 ... draw line from (x1,y1)->(x2,y2) */ int main(int argc, char **argv) { int test, i1,i2,i3,i4,nline=0,x1,x2,y1,y2; char string[1024]; DoublePrecision scalex, scaley; openpl(); space(0, 0, 32767, 32767); /* open as large as possible */ while ( (test = getchar()) != EOF ) { if ( !isspace(test) ) { nline++; switch (test) { case 's': if (scanf("%d %d %d %d",&i1,&i2,&i3,&i4) != 4) Error("plot: scanning space arguments, line=",nline); else { xmin = i1; ymin = i2; scalex = (32767.0-4096.0) / (DoublePrecision) (i3 - i1); scaley = (32767.0-4096.0) / (DoublePrecision) (i4 - i2); } break; case 'l': if (scanf("%d %d %d %d",&i1,&i2,&i3,&i4) != 4) Error("plot: scanning line arguments, line=",nline); x1 = SCALE(i1, xmin, scalex); x2 = SCALE(i3, xmin, scalex); y1 = SCALE(i2, ymin, scaley); y2 = SCALE(i4, ymin, scaley); line(x1, y1, x2, y2); break; case 'f': if (scanf("%d",&i1) != 1) Error("plot: scanning linemode arguments",-1); switch (i1) { case 1: linemod("solid"); break; case 2: linemod("dotted"); break; case 3: linemod("shortdashed"); break; case 4: linemod("longdashed"); break; default: Error("plot: unknown linemode",i1); } break; case 't': if (scanf("%d %d %s",&i1, &i2, string) != 3) Error("plot: scanning text arguments", -1); x1 = SCALE(i1, xmin, scalex); y1 = SCALE(i2, ymin, scaley); move(x1, y1); label(string); break; default: Error("plot: unknown directive, line=",nline); break; } } } closepl(); (void) fflush(stdout); return 0; } #else /* HAVE_LIBPLOT */ #if HAVE_STDIO_H # include #endif int main(int argc, char **argv) { printf("no plot utility on this system\n"); return 0; } #endif /* HAVE_LIBPLOT */ ga-5.9.2/tcgmsg/tests/waitcom.f000066400000000000000000000020451500715745200163650ustar00rootroot00000000000000 program main implicit none integer maxloop parameter (maxloop = 67 ) integer buf,me,nproc,loop,lenmes,node, received integer nnodes, nodeid c call pbeginf c nproc = nnodes() me = nodeid() if(nproc.lt.3)then print *,'min 3 processes required ',nproc call parerr(0) endif received =0 do loop = 1, maxloop node = Mod(loop,2)+1 if(me.eq.0) then call snd(loop, buf, 1, node, 0) endif if(me.eq.node) then received = received +1 call rcv(loop, buf, 1, lenmes, 0, node, 1) endif enddo if(me.eq.0)print *,'0: waiting for coms to node 1 to complete' call waitcom(1) if(me.eq.0)print *,'0: waiting for remaining coms to complete' call waitcom(-1) if(me.eq.0) then print *,'node=',me, maxloop,' messages sent asynchronously' else print *, 'node=',me, received,' messages received' endif c call pend end ga-5.9.2/tcgmsg/tests/xdrtest.c000066400000000000000000000040511500715745200164130ustar00rootroot00000000000000#if HAVE_CONFIG_H # include "config.h" #endif #if HAVE_STDIO_H #include #endif #if HAVE_RPC_TYPES_H #include #endif #if HAVE_RPC_XDR_H #include #endif #if HAVE_STRING_H #include #endif static char *xdrbuf; static XDR xdrs; int main(int argc, char **argv) { long data[4]; u_int len; long *temp=data; if (argc != 2) { return 1; } xdrbuf = malloc(4096); if (strcmp(argv[1], "encode") == 0) { xdrmem_create(&xdrs, xdrbuf, 4096, XDR_ENCODE); (void) fprintf(stderr," encode xdr_setpos=%d\n", xdr_setpos(&xdrs, (u_int) 0)); (void) scanf("%ld %ld %ld %ld", data, data+1, data+2, data+3); (void) fprintf(stderr,"encode Input longs %ld, %ld, %ld, %ld\n", data[0], data[1], data[2], data[3]); len = 4; (void) fprintf(stderr,"encode xdr_array=%d\n", xdr_array(&xdrs, (char **) &temp, &len, (u_int) 4096, (u_int) sizeof(long), (xdrproc_t)xdr_long)); len = 4*4 + 4; (void) fprintf(stderr,"encode len=%lu\n", (long unsigned)len); (void) fwrite(&len, 4, 1, stdout); (void) fwrite(xdrbuf, 1, len, stdout); (void) fprintf(stderr,"encode data written\n"); return 0; } else { xdrmem_create(&xdrs, xdrbuf, 4096, XDR_DECODE); (void) fprintf(stderr," decode xdr_setpos=%d\n", xdr_setpos(&xdrs, (u_int) 0)); (void) fread(&len, 4, 1, stdin); (void) fprintf(stderr,"decode len=%lu\n", (long unsigned)len); (void) fread(xdrbuf, 1, len, stdin); (void) fprintf(stderr,"decode data read\n"); (void) fprintf(stderr,"decode xdr_array=%d\n", xdr_array(&xdrs, (char **) &temp, &len, (u_int) 4096, (u_int) sizeof(long), (xdrproc_t)xdr_long)); (void) fprintf(stderr,"decode Input longs %ld, %ld, %ld, %ld\n", data[0], data[1], data[2], data[3]); return 0; } } ga-5.9.2/tools/000077500000000000000000000000001500715745200132645ustar00rootroot00000000000000ga-5.9.2/tools/CMakeLists.txt000066400000000000000000000042041500715745200160240ustar00rootroot00000000000000# # module: CMakeLists.txt # author: Bruce Palmer # description: CMake build for GA. Only MPI-based runtimes are supported. # # DISCLAIMER # # This material was prepared as an account of work sponsored by an # agency of the United States Government. Neither the United States # Government nor the United States Department of Energy, nor Battelle, # nor any of their employees, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR # ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, # COMPLETENESS, OR USEFULNESS OF ANY INFORMATION, APPARATUS, PRODUCT, # SOFTWARE, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT # INFRINGE PRIVATELY OWNED RIGHTS. # # # ACKNOWLEDGMENT # # This software and its documentation were produced with United States # Government support under Contract Number DE-AC06-76RLO-1830 awarded by # the United States Department of Energy. The United States Government # retains a paid-up non-exclusive, irrevocable worldwide license to # reproduce, prepare derivative works, perform publicly and display # publicly by or for the US Government, including the right to # distribute to other US Government contractors. # # -*- mode: cmake -*- # ------------------------------------------------------------- # file: CMakeLists.txt # ------------------------------------------------------------- # ------------------------------------------------------------- # GA tools headers installation # ------------------------------------------------------------- set(GA_TOOLS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/ga-wprof.h ) install(FILES ${GA_TOOLS_HEADERS} DESTINATION include/ga ) add_library(ga_tools OBJECT ga-wapi.c ga-wprof.c ) list (APPEND GA_HEADER_PATHS ${CMAKE_CURRENT_LIST_DIR}) set (GA_HEADER_PATHS ${GA_HEADER_PATHS} PARENT_SCOPE) target_include_directories(ga_tools BEFORE PRIVATE ${PROJECT_SOURCE_DIR}/ma ${PROJECT_BINARY_DIR}/ma ${PROJECT_BINARY_DIR}/gaf2c ${PROJECT_BINARY_DIR}/global/src ${PROJECT_SOURCE_DIR}/global/src ${PROJECT_SOURCE_DIR}/comex/src-armci ${PROJECT_BINARY_DIR}) ga-5.9.2/tools/config_fh_from_h.cmake000066400000000000000000000011011500715745200175330ustar00rootroot00000000000000if (INPUT) if (OUTPUT) file(READ "${INPUT}" in0) # replace carriage returns with a semi-colon string (REGEX REPLACE "\n" ";" in0 "${in0}") set(out "") foreach (l in ${in0}) # Only retain lines that start with "#" set(found "") string(REGEX MATCH "^#" found "${l}") if (found) set(out "${out}${l}\n") endif () endforeach () file(WRITE "${OUTPUT}" "${out}") else (OUTPUT) message(ERROR "OUTPUT variable must be set") endif (OUTPUT) else (INPUT) message(ERROR "INPUT variable must be set") endif (INPUT) ga-5.9.2/tools/config_fh_from_h.pl000077500000000000000000000001301500715745200170720ustar00rootroot00000000000000#!/usr/bin/env perl while () { $line = $_; if (/^#/) { print "$_"; } } ga-5.9.2/tools/f77funcgen.pl000077500000000000000000000105301500715745200155740ustar00rootroot00000000000000# # f77funcgen.pl # # find all pnga_ functions and output as many F77_FUNC_ macros as possible # output pnga_ functions that don't quite fit # if ($#ARGV+1 != 1) { die "usage: f77funcgen.pl filename"; } open FILE, "<$ARGV[0]" or die $!; while () { if (/pnga_/) { chomp; s/^.*pnga_(.*)\(.*$/\1/; $big = uc $_; print "#define ga_${_}_ F77_FUNC_(ga_$_, GA_$big)\n"; print "#define ga_c${_}_ F77_FUNC_(ga_c$_,GA_C$big)\n"; print "#define ga_d${_}_ F77_FUNC_(ga_d$_,GA_D$big)\n"; print "#define ga_i${_}_ F77_FUNC_(ga_i$_,GA_I$big)\n"; print "#define ga_s${_}_ F77_FUNC_(ga_s$_,GA_S$big)\n"; print "#define ga_z${_}_ F77_FUNC_(ga_z$_,GA_Z$big)\n"; print "#define nga_${_}_ F77_FUNC_(nga_$_, NGA_$big)\n"; print "#define nga_c${_}_ F77_FUNC_(nga_c$_,NGA_C$big)\n"; print "#define nga_d${_}_ F77_FUNC_(nga_d$_,NGA_D$big)\n"; print "#define nga_i${_}_ F77_FUNC_(nga_i$_,NGA_I$big)\n"; print "#define nga_s${_}_ F77_FUNC_(nga_s$_,NGA_S$big)\n"; print "#define nga_z${_}_ F77_FUNC_(nga_z$_,NGA_Z$big)\n"; } } print "/* the missing functions are either complex type or strangely named */\n"; print "\n"; print "#define gai_cdot_patch_ F77_FUNC_(gai_cdot_patch,GAI_CDOT_PATCH)\n"; print "#define gai_zdot_patch_ F77_FUNC_(gai_zdot_patch,GAI_ZDOT_PATCH)\n"; print "#define ngai_cdot_patch_ F77_FUNC_(ngai_cdot_patch,NGAI_CDOT_PATCH)\n"; print "#define ngai_zdot_patch_ F77_FUNC_(ngai_zdot_patch,NGAI_ZDOT_PATCH)\n"; print "\n"; print "#define ga_cscal_patch_ F77_FUNC_(ga_cscal_patch,GA_CSCAL_PATCH)\n"; print "#define ga_dscal_patch_ F77_FUNC_(ga_dscal_patch,GA_DSCAL_PATCH)\n"; print "#define ga_iscal_patch_ F77_FUNC_(ga_iscal_patch,GA_ISCAL_PATCH)\n"; print "#define ga_sscal_patch_ F77_FUNC_(ga_sscal_patch,GA_SSCAL_PATCH)\n"; print "#define ga_zscal_patch_ F77_FUNC_(ga_zscal_patch,GA_ZSCAL_PATCH)\n"; print "\n"; print "#define ga_cscal_ F77_FUNC_(ga_cscal,GA_CSCAL)\n"; print "#define ga_dscal_ F77_FUNC_(ga_dscal,GA_DSCAL)\n"; print "#define ga_iscal_ F77_FUNC_(ga_iscal,GA_ISCAL)\n"; print "#define ga_sscal_ F77_FUNC_(ga_sscal,GA_SSCAL)\n"; print "#define ga_zscal_ F77_FUNC_(ga_zscal,GA_ZSCAL)\n"; print "\n"; print "#define gai_cdot_ F77_FUNC_(gai_cdot,GAI_CDOT)\n"; print "#define gai_zdot_ F77_FUNC_(gai_zdot,GAI_ZDOT)\n"; print "#define ngai_cdot_ F77_FUNC_(ngai_cdot,NGAI_CDOT)\n"; print "#define ngai_zdot_ F77_FUNC_(ngai_zdot,NGAI_ZDOT)\n"; print "\n"; print "#define ga_cgemm_ F77_FUNC_(ga_cgemm,GA_CGEMM)\n"; print "#define ga_dgemm_ F77_FUNC_(ga_dgemm,GA_DGEMM)\n"; print "#define ga_sgemm_ F77_FUNC_(ga_sgemm,GA_SGEMM)\n"; print "#define ga_zgemm_ F77_FUNC_(ga_zgemm,GA_ZGEMM)\n"; print "\n"; print "#define nga_periodic_get_ F77_FUNC_(nga_periodic_get,NGA_PERIODIC_GET)\n"; print "#define nga_periodic_put_ F77_FUNC_(nga_periodic_put,NGA_PERIODIC_PUT)\n"; print "#define nga_periodic_acc_ F77_FUNC_(nga_periodic_acc,NGA_PERIODIC_ACC)\n"; print "\n"; print "#define ga_access_ F77_FUNC_(ga_access,GA_ACCESS)\n"; print "#define nga_access_ F77_FUNC_(nga_access,NGA_ACCESS)\n"; print "#define nga_access_block_ F77_FUNC_(nga_access_block,NGA_ACCESS_BLOCK)\n"; print "#define nga_access_block_grid_ F77_FUNC_(nga_access_block_grid,NGA_ACCESS_BLOCK_GRID)\n"; print "#define nga_access_block_segment_ F77_FUNC_(nga_access_block_segment,NGA_ACCESS_BLOCK_SEGMENT)\n"; print "\n"; print "#define ga_pgroup_cgop_ F77_FUNC_(ga_pgroup_cgop,GA_PGROUP_CGOP)\n"; print "#define ga_pgroup_dgop_ F77_FUNC_(ga_pgroup_dgop,GA_PGROUP_DGOP)\n"; print "#define ga_pgroup_igop_ F77_FUNC_(ga_pgroup_igop,GA_PGROUP_IGOP)\n"; print "#define ga_pgroup_sgop_ F77_FUNC_(ga_pgroup_sgop,GA_PGROUP_SGOP)\n"; print "#define ga_pgroup_zgop_ F77_FUNC_(ga_pgroup_zgop,GA_PGROUP_ZGOP)\n"; print "\n"; print "#define nga_pgroup_cgop_ F77_FUNC_(nga_pgroup_cgop,NGA_PGROUP_CGOP)\n"; print "#define nga_pgroup_dgop_ F77_FUNC_(nga_pgroup_dgop,NGA_PGROUP_DGOP)\n"; print "#define nga_pgroup_igop_ F77_FUNC_(nga_pgroup_igop,NGA_PGROUP_IGOP)\n"; print "#define nga_pgroup_sgop_ F77_FUNC_(nga_pgroup_sgop,NGA_PGROUP_SGOP)\n"; print "#define nga_pgroup_zgop_ F77_FUNC_(nga_pgroup_zgop,NGA_PGROUP_ZGOP)\n"; ga-5.9.2/tools/fapi_header_gen.py000077500000000000000000000073721500715745200167320ustar00rootroot00000000000000#!/usr/bin/env python '''Generate the fapi.c source from the ga-papi.h header.''' import sys def get_signatures(header): # first, gather all function signatures from ga-papi.h aka argv[1] accumulating = False signatures = [] current_signature = '' EXTERN = 'extern' SEMICOLON = ';' for line in open(header): line = line.strip() # remove whitespace before and after line if not line: continue # skip blank lines if EXTERN in line and SEMICOLON in line: signatures.append(line) elif EXTERN in line: current_signature = line accumulating = True elif SEMICOLON in line and accumulating: current_signature += line signatures.append(current_signature) accumulating = False elif accumulating: current_signature += line return signatures class FunctionArgument(object): def __init__(self, signature): self.pointer = signature.count('*') self.array = '[' in signature signature = signature.replace('*','').strip() signature = signature.replace('[','').strip() signature = signature.replace(']','').strip() self.type,self.name = signature.split() def __str__(self): ret = self.type[:] ret += ' ' for p in range(self.pointer): ret += '*' ret += self.name if self.array: ret += '[]' return ret class Function(object): def __init__(self, signature): signature = signature.replace('extern','').strip() self.return_type,signature = signature.split(None,1) self.return_type = self.return_type.strip() signature = signature.strip() self.name,signature = signature.split('(',1) self.name = self.name.strip() signature = signature.replace(')','').strip() signature = signature.replace(';','').strip() self.args = [] if signature: for arg in signature.split(','): self.args.append(FunctionArgument(arg.strip())) def get_call(self, name=None): sig = '' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += arg.name sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def get_signature(self, name=None): sig = self.return_type[:] sig += ' ' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += str(arg) sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def __str__(self): return self.get_signature() if __name__ == '__main__': if len(sys.argv) != 2: print 'incorrect number of arguments' print 'usage: wapigen.py > ' sys.exit(len(sys.argv)) # print headers print ''' #if HAVE_CONFIG_H # include "config.h" #endif #include "ga-papi.h" #include "typesf2c.h" ''' functions = {} # parse signatures into the Function class for sig in get_signatures(sys.argv[1]): function = Function(sig) functions[function.name] = function # now process the functions for name in sorted(functions): func = functions[name] maybe_return = '' if 'void' not in func.return_type: maybe_return = 'return ' func = functions[name] wnga_name = name.replace('pnga_','wnga_') print ''' %s { %s%s; } ''' % (func.get_signature(wnga_name), maybe_return, func.get_call()) ga-5.9.2/tools/ga-config.in000066400000000000000000000265321500715745200154560ustar00rootroot00000000000000#! /bin/sh # Generated from ga-config.in.m4sh by GNU Autoconf 2.69. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested="" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes break 2 fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset ## -------------------- ## ## Main body of script. ## ## -------------------- ## prefix="@prefix@" exec_prefix="@exec_prefix@" dep_libs="-larmci @GA_MP_LIBS@ @ARMCI_NETWORK_LIBS@" if test -f "$prefix/bin/armci-config"; then : dep_libs=`$prefix/bin/armci-config --libs` fi f77="@F77@" cc="@CC@" cppflags="@SCALAPACK_CPPFLAGS@ @LAPACK_CPPFLAGS@ @BLAS_CPPFLAGS@ @GA_MP_CPPFLAGS@ @ARMCI_NETWORK_CPPFLAGS@ @SICM_CPPFLAGS@ -I@includedir@" network_cppflags="@ARMCI_NETWORK_CPPFLAGS@" cflags="@GA_COPT@" fflags="@GA_FOPT@ @FFLAG_INT@" fint="@FFLAG_INT@" blas_size="@blas_size@" scalapack_size="@scalapack_size@" use_blas="@have_blas@" use_lapack="@have_lapack@" use_scalapack="@have_scalapack@" use_peigs="@enable_peigs@" use_elpa="@have_elpa@" use_elpa_2015="@have_elpa_2015@" use_elpa_2016="@have_elpa_2016@" use_sicm="@have_sicm@" sicm_dev="@with_sicm_dev@" ldflags="@SCALAPACK_LDFLAGS@ @LAPACK_LDFLAGS@ @BLAS_LDFLAGS@ @GA_MP_LDFLAGS@ @ARMCI_NETWORK_LDFLAGS@ @SICM_LDFLAGS@ -L@libdir@" network_ldflags="@ARMCI_NETWORK_LDFLAGS@" libs="-lga @SCALAPACK_LIBS@ @LAPACK_LIBS@ @BLAS_LIBS@ $dep_libs @SICM_LIBS@" network_libs="@ARMCI_NETWORK_LIBS@" flibs="@FLIBS@" enable_f77_true="@ENABLE_F77_TRUE@" version="@PACKAGE_VERSION@" if test "x$enable_f77_true" = x; then : enable_f77=yes else enable_f77=no fi usage="Usage: ga-config [OPTIONS]... With the exception of --version and --help, all other options can be combined or run exclusively. Output is echoed to stdout. Options: --f77 --cc --cppflags --cflags --fflags --fint --blas_size --scalapack_size --use_blas --use_lapack --use_scalapack --use_peigs --use_elpa --use_elpa_2015 --use_elpa_2016 --use_sicm --sicm_dev --ldflags --libs --flibs --network_ldflags --network_libs --network_cppflags --enable-f77 --version --help " result= while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\(^=*\)='` ac_optarg=`expr "X$1" : 'X^=*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\(^=*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -help | --help | --hel | --he | -h ) $as_echo "$usage"; exit ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$version"; exit ;; -f77 | --f77) result="$result $f77" ;; -cc | --cc ) result="$result $cc" ;; -cppflags | --cppflags ) result="$result $cppflags" ;; -network_cppflags | --network_cppflags ) result="$result $network_cppflags" ;; -cflags | --cflags ) result="$result $cflags" ;; -fflags | --fflags ) result="$result $fflags" ;; -ldflags | --ldflags ) result="$result $ldflags" ;; -network_ldflags | --network_ldflags ) result="$result $network_ldflags" ;; -libs | --libs ) result="$result $libs" ;; -network_libs | --network_libs ) result="$result $network_libs" ;; -flibs | --flibs ) result="$result $flibs" ;; -fint | --fint ) result="$result $fint" ;; -blas_size | --blas_size ) result="$result $blas_size" ;; -scalapack_size | --scalapack_size ) result="$result $scalapack_size" ;; -use_blas | --use_blas ) result="$result $use_blas" ;; -use_lapack | --use_lapack ) result="$result $use_lapack" ;; -use_scalapack | --use_scalapack ) result="$result $use_scalapack" ;; -use_peigs | --use_peigs ) result="$result $use_peigs" ;; -use_elpa | --use_elpa ) result="$result $use_elpa" ;; -use_elpa_2015 | --use_elpa_2015 ) result="$result $use_elpa_2015" ;; -use_elpa_2016 | --use_elpa_2016 ) result="$result $use_elpa_2016" ;; -use_sicm | --use_sicm ) result="$result $use_sicm" ;; -sicm_dev | --sicm_dev ) result="$result $sicm_dev" ;; -enable-f77 | --enable-f77 ) result="$result $enable_f77" ;; # This is an error. *) $as_echo "unrecognized option: \`$1' Try \`$0 --help' for more information."; exit ;; esac shift done $as_echo "$result" | sed 's/ */ /g;s/" /"/g;s/ "/"/g;s/^ *//;s/ *$//' ga-5.9.2/tools/ga-config.in.m4sh000066400000000000000000000100341500715745200163160ustar00rootroot00000000000000dnl run "autom4te -l m4sh ga-config.in.m4sh > ga-config.in" AS_INIT prefix="@prefix@" exec_prefix="@exec_prefix@" dep_libs="-larmci @GA_MP_LIBS@ @ARMCI_NETWORK_LIBS@" AS_IF([test -f "$prefix/bin/armci-config"], [ dep_libs=`$prefix/bin/armci-config --libs` ]) f77="@F77@" cc="@CC@" cppflags="@SCALAPACK_CPPFLAGS@ @LAPACK_CPPFLAGS@ @BLAS_CPPFLAGS@ @GA_MP_CPPFLAGS@ @ARMCI_NETWORK_CPPFLAGS@ @SICM_CPPFLAGS@ -I@includedir@" network_cppflags="@ARMCI_NETWORK_CPPFLAGS@" cflags="@GA_COPT@" fflags="@GA_FOPT@ @FFLAG_INT@" fint="@FFLAG_INT@" blas_size="@blas_size@" scalapack_size="@scalapack_size@" use_blas="@have_blas@" use_lapack="@have_lapack@" use_scalapack="@have_scalapack@" use_peigs="@enable_peigs@" use_elpa="@have_elpa@" use_elpa_2015="@have_elpa_2015@" use_elpa_2016="@have_elpa_2016@" use_sicm="@have_sicm@" sicm_dev="@with_sicm_dev@" ldflags="@SCALAPACK_LDFLAGS@ @LAPACK_LDFLAGS@ @BLAS_LDFLAGS@ @GA_MP_LDFLAGS@ @ARMCI_NETWORK_LDFLAGS@ @SICM_LDFLAGS@ -L@libdir@" network_ldflags="@ARMCI_NETWORK_LDFLAGS@" libs="-lga @SCALAPACK_LIBS@ @LAPACK_LIBS@ @BLAS_LIBS@ $dep_libs @SICM_LIBS@" network_libs="@ARMCI_NETWORK_LIBS@" flibs="@FLIBS@" enable_f77_true="@ENABLE_F77_TRUE@" version="@PACKAGE_VERSION@" AS_IF([test "x$enable_f77_true" = x], [enable_f77=yes], [enable_f77=no]) [usage="Usage: ga-config [OPTIONS]... With the exception of --version and --help, all other options can be combined or run exclusively. Output is echoed to stdout. Options: --f77 --cc --cppflags --cflags --fflags --fint --blas_size --scalapack_size --use_blas --use_lapack --use_scalapack --use_peigs --use_elpa --use_elpa_2015 --use_elpa_2016 --use_sicm --sicm_dev --ldflags --libs --flibs --network_ldflags --network_libs --network_cppflags --enable-f77 --version --help "] result= while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -help | --help | --hel | --he | -h ) AS_ECHO(["$usage"]); exit ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) AS_ECHO(["$version"]); exit ;; -f77 | --f77) result="$result $f77" ;; -cc | --cc ) result="$result $cc" ;; -cppflags | --cppflags ) result="$result $cppflags" ;; -network_cppflags | --network_cppflags ) result="$result $network_cppflags" ;; -cflags | --cflags ) result="$result $cflags" ;; -fflags | --fflags ) result="$result $fflags" ;; -ldflags | --ldflags ) result="$result $ldflags" ;; -network_ldflags | --network_ldflags ) result="$result $network_ldflags" ;; -libs | --libs ) result="$result $libs" ;; -network_libs | --network_libs ) result="$result $network_libs" ;; -flibs | --flibs ) result="$result $flibs" ;; -fint | --fint ) result="$result $fint" ;; -blas_size | --blas_size ) result="$result $blas_size" ;; -scalapack_size | --scalapack_size ) result="$result $scalapack_size" ;; -use_blas | --use_blas ) result="$result $use_blas" ;; -use_lapack | --use_lapack ) result="$result $use_lapack" ;; -use_scalapack | --use_scalapack ) result="$result $use_scalapack" ;; -use_peigs | --use_peigs ) result="$result $use_peigs" ;; -use_elpa | --use_elpa ) result="$result $use_elpa" ;; -use_elpa_2015 | --use_elpa_2015 ) result="$result $use_elpa_2015" ;; -use_elpa_2016 | --use_elpa_2016 ) result="$result $use_elpa_2016" ;; -use_sicm | --use_sicm ) result="$result $use_sicm" ;; -sicm_dev | --sicm_dev ) result="$result $sicm_dev" ;; -enable-f77 | --enable-f77 ) result="$result $enable_f77" ;; # This is an error. *) AS_ECHO(["unrecognized option: \`$1' Try \`$0 --help' for more information."]); exit ;; esac shift done AS_ECHO(["$result"]) | sed 's/ [ ]*/ /g;s/" /"/g;s/ "/"/g;s/^ [ ]*//;s/ [ ]*$//' ga-5.9.2/tools/ga-wapi.c000066400000000000000000002070651500715745200147670ustar00rootroot00000000000000 #if HAVE_CONFIG_H # include "config.h" #endif #include #include "ga-papi.h" #include "typesf2c.h" #include "ga-wprof.h" // New profile headers static int me; static int nproc; void wnga_abs_value(Integer g_a) { unsigned long long ltme; ltme=- I_Wtime(); pnga_abs_value(g_a); ltme += I_Wtime(); update_local_entry(PNGA_ABS_VALUE, ltme, 0); } void wnga_abs_value_patch(Integer g_a, Integer *lo, Integer *hi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_abs_value_patch(g_a, lo, hi); ltme += I_Wtime(); update_local_entry(PNGA_ABS_VALUE_PATCH, ltme, 0); } void wnga_acc(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, void *alpha) { unsigned long long ltme, sz; GET_SIZE(g_a, lo, hi, sz); ltme=- I_Wtime(); pnga_acc(g_a, lo, hi, buf, ld, alpha); ltme += I_Wtime(); update_local_entry(PNGA_ACC, ltme, sz); } void wnga_access_block_grid_idx(Integer g_a, Integer *subscript, AccessIndex *index, Integer *ld) { unsigned long long ltme; ltme=- I_Wtime(); pnga_access_block_grid_idx(g_a, subscript, index, ld); ltme += I_Wtime(); update_local_entry(PNGA_ACCESS_BLOCK_GRID_IDX, ltme, 0); } void wnga_access_block_grid_ptr(Integer g_a, Integer *index, void *ptr, Integer *ld) { unsigned long long ltme; ltme=- I_Wtime(); pnga_access_block_grid_ptr(g_a, index, ptr, ld); ltme += I_Wtime(); update_local_entry(PNGA_ACCESS_BLOCK_GRID_PTR, ltme, 0); } void wnga_access_block_idx(Integer g_a, Integer idx, AccessIndex *index, Integer *ld) { unsigned long long ltme; ltme=- I_Wtime(); pnga_access_block_idx(g_a, idx, index, ld); ltme += I_Wtime(); update_local_entry(PNGA_ACCESS_BLOCK_IDX, ltme, 0); } void wnga_access_block_ptr(Integer g_a, Integer idx, void *ptr, Integer *ld) { unsigned long long ltme; ltme=- I_Wtime(); pnga_access_block_ptr(g_a, idx, ptr, ld); ltme += I_Wtime(); update_local_entry(PNGA_ACCESS_BLOCK_PTR, ltme, 0); } void wnga_access_block_segment_idx(Integer g_a, Integer proc, AccessIndex *index, Integer *len) { unsigned long long ltme; ltme=- I_Wtime(); pnga_access_block_segment_idx(g_a, proc, index, len); ltme += I_Wtime(); update_local_entry(PNGA_ACCESS_BLOCK_SEGMENT_IDX, ltme, 0); } void wnga_access_block_segment_ptr(Integer g_a, Integer proc, void *ptr, Integer *len) { unsigned long long ltme; ltme=- I_Wtime(); pnga_access_block_segment_ptr(g_a, proc, ptr, len); ltme += I_Wtime(); update_local_entry(PNGA_ACCESS_BLOCK_SEGMENT_PTR, ltme, 0); } void wnga_access_ghost_element(Integer g_a, AccessIndex *index, Integer subscript[], Integer ld[]) { unsigned long long ltme; ltme=- I_Wtime(); pnga_access_ghost_element(g_a, index, subscript, ld); ltme += I_Wtime(); update_local_entry(PNGA_ACCESS_GHOST_ELEMENT, ltme, 0); } void wnga_access_ghost_element_ptr(Integer g_a, void *ptr, Integer subscript[], Integer ld[]) { unsigned long long ltme; ltme=- I_Wtime(); pnga_access_ghost_element_ptr(g_a, ptr, subscript, ld); ltme += I_Wtime(); update_local_entry(PNGA_ACCESS_GHOST_ELEMENT_PTR, ltme, 0); } void wnga_access_ghost_ptr(Integer g_a, Integer dims[], void *ptr, Integer ld[]) { unsigned long long ltme; ltme=- I_Wtime(); pnga_access_ghost_ptr(g_a, dims, ptr, ld); ltme += I_Wtime(); update_local_entry(PNGA_ACCESS_GHOST_PTR, ltme, 0); } void wnga_access_ghosts(Integer g_a, Integer dims[], AccessIndex *index, Integer ld[]) { unsigned long long ltme; ltme=- I_Wtime(); pnga_access_ghosts(g_a, dims, index, ld); ltme += I_Wtime(); update_local_entry(PNGA_ACCESS_GHOSTS, ltme, 0); } void wnga_access_idx(Integer g_a, Integer *lo, Integer *hi, AccessIndex *index, Integer *ld) { unsigned long long ltme; ltme=- I_Wtime(); pnga_access_idx(g_a, lo, hi, index, ld); ltme += I_Wtime(); update_local_entry(PNGA_ACCESS_IDX, ltme, 0); } void wnga_access_ptr(Integer g_a, Integer *lo, Integer *hi, void *ptr, Integer *ld) { unsigned long long ltme; ltme=- I_Wtime(); pnga_access_ptr(g_a, lo, hi, ptr, ld); ltme += I_Wtime(); update_local_entry(PNGA_ACCESS_PTR, ltme, 0); } void wnga_add(void *alpha, Integer g_a, void *beta, Integer g_b, Integer g_c) { unsigned long long ltme; ltme=- I_Wtime(); pnga_add(alpha, g_a, beta, g_b, g_c); ltme += I_Wtime(); update_local_entry(PNGA_ADD, ltme, 0); } void wnga_add_constant(Integer g_a, void *alpha) { unsigned long long ltme; ltme=- I_Wtime(); pnga_add_constant(g_a, alpha); ltme += I_Wtime(); update_local_entry(PNGA_ADD_CONSTANT, ltme, 0); } void wnga_add_constant_patch(Integer g_a, Integer *lo, Integer *hi, void *alpha) { unsigned long long ltme; ltme=- I_Wtime(); pnga_add_constant_patch(g_a, lo, hi, alpha); ltme += I_Wtime(); update_local_entry(PNGA_ADD_CONSTANT_PATCH, ltme, 0); } void wnga_add_diagonal(Integer g_a, Integer g_v) { unsigned long long ltme; ltme=- I_Wtime(); pnga_add_diagonal(g_a, g_v); ltme += I_Wtime(); update_local_entry(PNGA_ADD_DIAGONAL, ltme, 0); } void wnga_add_patch(void *alpha, Integer g_a, Integer *alo, Integer *ahi, void *beta, Integer g_b, Integer *blo, Integer *bhi, Integer g_c, Integer *clo, Integer *chi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_add_patch(alpha, g_a, alo, ahi, beta, g_b, blo, bhi, g_c, clo, chi); ltme += I_Wtime(); update_local_entry(PNGA_ADD_PATCH, ltme, 0); } logical wnga_allocate(Integer g_a) { logical return_value; unsigned long long ltme, sz; ltme=- I_Wtime(); return_value = pnga_allocate(g_a); ltme += I_Wtime(); GET_LOCAL_MSIZE(g_a, sz); update_local_entry(PNGA_ALLOCATE, ltme, sz); return return_value; } void wnga_bin_index(Integer g_bin, Integer g_cnt, Integer g_off, Integer *values, Integer *subs, Integer n, Integer sortit) { unsigned long long ltme; ltme=- I_Wtime(); pnga_bin_index(g_bin, g_cnt, g_off, values, subs, n, sortit); ltme += I_Wtime(); update_local_entry(PNGA_BIN_INDEX, ltme, 0); } void wnga_bin_sorter(Integer g_bin, Integer g_cnt, Integer g_off) { unsigned long long ltme; ltme=- I_Wtime(); pnga_bin_sorter(g_bin, g_cnt, g_off); ltme += I_Wtime(); update_local_entry(PNGA_BIN_SORTER, ltme, 0); } void wnga_brdcst(Integer type, void *buf, Integer len, Integer originator) { unsigned long long ltme; ltme=- I_Wtime(); pnga_brdcst(type, buf, len, originator); ltme += I_Wtime(); update_local_entry(PNGA_BRDCST, ltme, len); } void wnga_check_handle(Integer g_a, char *string) { unsigned long long ltme; ltme=- I_Wtime(); pnga_check_handle(g_a, string); ltme += I_Wtime(); update_local_entry(PNGA_CHECK_HANDLE, ltme, 0); } Integer wnga_cluster_nnodes() { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_cluster_nnodes(); ltme += I_Wtime(); update_local_entry(PNGA_CLUSTER_NNODES, ltme, 0); return return_value; } Integer wnga_cluster_nodeid() { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_cluster_nodeid(); ltme += I_Wtime(); update_local_entry(PNGA_CLUSTER_NODEID, ltme, 0); return return_value; } Integer wnga_cluster_nprocs(Integer node) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_cluster_nprocs(node); ltme += I_Wtime(); update_local_entry(PNGA_CLUSTER_NPROCS, ltme, 0); return return_value; } Integer wnga_cluster_proc_nodeid(Integer proc) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_cluster_proc_nodeid(proc); ltme += I_Wtime(); update_local_entry(PNGA_CLUSTER_PROC_NODEID, ltme, 0); return return_value; } Integer wnga_cluster_procid(Integer node, Integer loc_proc_id) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_cluster_procid(node, loc_proc_id); ltme += I_Wtime(); update_local_entry(PNGA_CLUSTER_PROCID, ltme, 0); return return_value; } logical wnga_comp_patch(Integer andim, Integer *alo, Integer *ahi, Integer bndim, Integer *blo, Integer *bhi) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_comp_patch(andim, alo, ahi, bndim, blo, bhi); ltme += I_Wtime(); update_local_entry(PNGA_COMP_PATCH, ltme, 0); return return_value; } logical wnga_compare_distr(Integer g_a, Integer g_b) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_compare_distr(g_a, g_b); ltme += I_Wtime(); update_local_entry(PNGA_COMPARE_DISTR, ltme, 0); return return_value; } void wnga_copy(Integer g_a, Integer g_b) { unsigned long long ltme; ltme=- I_Wtime(); pnga_copy(g_a, g_b); ltme += I_Wtime(); update_local_entry(PNGA_COPY, ltme, 0); } void wnga_copy_patch(char *trans, Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_copy_patch(trans, g_a, alo, ahi, g_b, blo, bhi); ltme += I_Wtime(); update_local_entry(PNGA_COPY_PATCH, ltme, 0); } void wnga_copy_patch_dp(char *t_a, Integer g_a, Integer ailo, Integer aihi, Integer ajlo, Integer ajhi, Integer g_b, Integer bilo, Integer bihi, Integer bjlo, Integer bjhi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_copy_patch_dp(t_a, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi); ltme += I_Wtime(); update_local_entry(PNGA_COPY_PATCH_DP, ltme, 0); } logical wnga_create(Integer type, Integer ndim, Integer *dims, char *name, Integer *chunk, Integer *g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_create(type, ndim, dims, name, chunk, g_a); ltme += I_Wtime(); uint64_t sz; GET_LOCAL_MSIZE(*g_a, sz); update_local_entry(PNGA_CREATE, ltme, sz); return return_value; } logical wnga_create_bin_range(Integer g_bin, Integer g_cnt, Integer g_off, Integer *g_range) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_create_bin_range(g_bin, g_cnt, g_off, g_range); ltme += I_Wtime(); update_local_entry(PNGA_CREATE_BIN_RANGE, ltme, 0); return return_value; } logical wnga_create_config(Integer type, Integer ndim, Integer *dims, char *name, Integer *chunk, Integer p_handle, Integer *g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_create_config(type, ndim, dims, name, chunk, p_handle, g_a); ltme += I_Wtime(); uint64_t sz ; GET_LOCAL_MSIZE(*g_a, sz); update_local_entry(PNGA_CREATE_CONFIG, ltme, sz); return return_value; } logical wnga_create_ghosts(Integer type, Integer ndim, Integer *dims, Integer *width, char *name, Integer *chunk, Integer *g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_create_ghosts(type, ndim, dims, width, name, chunk, g_a); ltme += I_Wtime(); uint64_t sz ; GET_LOCAL_MSIZE(*g_a, sz); update_local_entry(PNGA_CREATE_GHOSTS, ltme, sz); return return_value; } logical wnga_create_ghosts_config(Integer type, Integer ndim, Integer *dims, Integer *width, char *name, Integer *chunk, Integer p_handle, Integer *g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_create_ghosts_config(type, ndim, dims, width, name, chunk, p_handle, g_a); ltme += I_Wtime(); uint64_t sz; GET_LOCAL_MSIZE(*g_a, sz); update_local_entry(PNGA_CREATE_GHOSTS_CONFIG, ltme, sz); return return_value; } logical wnga_create_ghosts_irreg(Integer type, Integer ndim, Integer *dims, Integer *width, char *name, Integer *map, Integer *block, Integer *g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_create_ghosts_irreg(type, ndim, dims, width, name, map, block, g_a); ltme += I_Wtime(); uint64_t sz; GET_LOCAL_MSIZE(*g_a, sz); update_local_entry(PNGA_CREATE_GHOSTS_IRREG, ltme, sz); return return_value; } logical wnga_create_ghosts_irreg_config(Integer type, Integer ndim, Integer *dims, Integer *width, char *name, Integer *map, Integer *block, Integer p_handle, Integer *g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_create_ghosts_irreg_config(type, ndim, dims, width, name, map, block, p_handle, g_a); ltme += I_Wtime(); uint64_t sz; GET_LOCAL_MSIZE(*g_a, sz); update_local_entry(PNGA_CREATE_GHOSTS_IRREG_CONFIG, ltme, sz); return return_value; } Integer wnga_create_handle() { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_create_handle(); ltme += I_Wtime(); update_local_entry(PNGA_CREATE_HANDLE, ltme, 0); return return_value; } logical wnga_create_irreg(Integer type, Integer ndim, Integer *dims, char *name, Integer *map, Integer *block, Integer *g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_create_irreg(type, ndim, dims, name, map, block, g_a); ltme += I_Wtime(); uint64_t sz; GET_LOCAL_MSIZE(*g_a, sz); update_local_entry(PNGA_CREATE_IRREG, ltme, sz); return return_value; } logical wnga_create_irreg_config(Integer type, Integer ndim, Integer *dims, char *name, Integer *map, Integer *block, Integer p_handle, Integer *g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_create_irreg_config(type, ndim, dims, name, map, block, p_handle, g_a); ltme += I_Wtime(); uint64_t sz; GET_LOCAL_MSIZE(*g_a, sz); update_local_entry(PNGA_CREATE_IRREG_CONFIG, ltme, sz); return return_value; } logical wnga_create_mutexes(Integer num) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_create_mutexes(num); ltme += I_Wtime(); update_local_entry(PNGA_CREATE_CREATE_MUTEXES, ltme, 0); return return_value; } DoublePrecision wnga_ddot_patch_dp(Integer g_a, char *t_a, Integer ailo, Integer aihi, Integer ajlo, Integer ajhi, Integer g_b, char *t_b, Integer bilo, Integer bihi, Integer bjlo, Integer bjhi) { DoublePrecision return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_ddot_patch_dp(g_a, t_a, ailo, aihi, ajlo, ajhi, g_b, t_b, bilo, bihi, bjlo, bjhi); ltme += I_Wtime(); update_local_entry(PNGA_DDOT_PATCH_DP, ltme, 0); return return_value; } int wnga_deregister_type(int type) { int return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_deregister_type(type); ltme += I_Wtime(); update_local_entry(PNGA_DEREGISTER_TYPE, ltme, 0); return return_value; } logical wnga_destroy(Integer g_a) { logical return_value; unsigned long long ltme, sz; ltme=- I_Wtime(); return_value = pnga_destroy(g_a); ltme += I_Wtime(); GET_LOCAL_MSIZE(g_a, sz); update_local_entry(PNGA_DESTROY, ltme, sz); return return_value; } logical wnga_destroy_mutexes() { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_destroy_mutexes(); ltme += I_Wtime(); update_local_entry(PNGA_DESTROY_MUTEXES, ltme, 0); return return_value; } void wnga_diag(Integer g_a, Integer g_s, Integer g_v, DoublePrecision *eval) { unsigned long long ltme; ltme=- I_Wtime(); pnga_diag(g_a, g_s, g_v, eval); ltme += I_Wtime(); update_local_entry(PNGA_DIAG, ltme, 0); } void wnga_diag_reuse(Integer reuse, Integer g_a, Integer g_s, Integer g_v, DoublePrecision *eval) { unsigned long long ltme; ltme=- I_Wtime(); pnga_diag_reuse(reuse, g_a, g_s, g_v, eval); ltme += I_Wtime(); update_local_entry(PNGA_DIAG_REUSE, ltme, 0); } void wnga_diag_seq(Integer g_a, Integer g_s, Integer g_v, DoublePrecision *eval) { unsigned long long ltme; ltme=- I_Wtime(); pnga_diag_seq(g_a, g_s, g_v, eval); ltme += I_Wtime(); update_local_entry(PNGA_DIAG_SEQ, ltme, 0); } void wnga_diag_std(Integer g_a, Integer g_v, DoublePrecision *eval) { unsigned long long ltme; ltme=- I_Wtime(); pnga_diag_std(g_a, g_v, eval); ltme += I_Wtime(); update_local_entry(PNGA_DIAG_STD, ltme, 0); } void wnga_diag_std_seq(Integer g_a, Integer g_v, DoublePrecision *eval) { unsigned long long ltme; ltme=- I_Wtime(); pnga_diag_std_seq(g_a, g_v, eval); ltme += I_Wtime(); update_local_entry(PNGA_DIAG_STD_SEQ, ltme, 0); } void wnga_distribution(Integer g_a, Integer proc, Integer *lo, Integer *hi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_distribution(g_a, proc, lo, hi); ltme += I_Wtime(); update_local_entry(PNGA_DISTRIBUTION, ltme, 0); } void wnga_dot(int type, Integer g_a, Integer g_b, void *value) { unsigned long long ltme; ltme=- I_Wtime(); pnga_dot(type, g_a, g_b, value); ltme += I_Wtime(); update_local_entry(PNGA_DOT, ltme, 0); } void wnga_dot_patch(Integer g_a, char *t_a, Integer *alo, Integer *ahi, Integer g_b, char *t_b, Integer *blo, Integer *bhi, void *retval) { unsigned long long ltme; ltme=- I_Wtime(); pnga_dot_patch(g_a, t_a, alo, ahi, g_b, t_b, blo, bhi, retval); ltme += I_Wtime(); update_local_entry(PNGA_DOT_PATCH, ltme, 0); } logical wnga_duplicate(Integer g_a, Integer *g_b, char *array_name) { logical return_value; unsigned long long ltme, sz; ltme=- I_Wtime(); return_value = pnga_duplicate(g_a, g_b, array_name); ltme += I_Wtime(); GET_LOCAL_MSIZE(*g_b, sz) update_local_entry(PNGA_DUPLICATE, ltme, sz); return return_value; } void wnga_elem_divide(Integer g_a, Integer g_b, Integer g_c) { unsigned long long ltme; ltme=- I_Wtime(); pnga_elem_divide(g_a, g_b, g_c); ltme += I_Wtime(); update_local_entry(PNGA_ELEM_DIVIDE, ltme, 0); } void wnga_elem_divide_patch(Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi, Integer g_c, Integer *clo, Integer *chi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_elem_divide_patch(g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi); ltme += I_Wtime(); update_local_entry(PNGA_ELEM_DIVIDE_PATCH, ltme, 0); } void wnga_elem_maximum(Integer g_a, Integer g_b, Integer g_c) { unsigned long long ltme; ltme=- I_Wtime(); pnga_elem_maximum(g_a, g_b, g_c); ltme += I_Wtime(); update_local_entry(PNGA_ELEM_MAXIMUM, ltme, 0); } void wnga_elem_maximum_patch(Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi, Integer g_c, Integer *clo, Integer *chi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_elem_maximum_patch(g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi); ltme += I_Wtime(); update_local_entry(PNGA_ELEM_MAXIMUM_PATCH, ltme, 0); } void wnga_elem_minimum(Integer g_a, Integer g_b, Integer g_c) { unsigned long long ltme; ltme=- I_Wtime(); pnga_elem_minimum(g_a, g_b, g_c); ltme += I_Wtime(); update_local_entry(PNGA_ELEM_MINIMUM, ltme, 0); } void wnga_elem_minimum_patch(Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi, Integer g_c, Integer *clo, Integer *chi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_elem_minimum_patch(g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi); ltme += I_Wtime(); update_local_entry(PNGA_ELEM_MINIMUM_PATCH, ltme, 0); } void wnga_elem_multiply(Integer g_a, Integer g_b, Integer g_c) { unsigned long long ltme; ltme=- I_Wtime(); pnga_elem_multiply(g_a, g_b, g_c); ltme += I_Wtime(); update_local_entry(PNGA_ELEM_MULTIPLY, ltme, 0); } void wnga_elem_multiply_patch(Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi, Integer g_c, Integer *clo, Integer *chi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_elem_multiply_patch(g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi); ltme += I_Wtime(); update_local_entry(PNGA_ELEM_MULTIPLY_PATCH, ltme, 0); } void wnga_elem_step_divide_patch(Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi, Integer g_c, Integer *clo, Integer *chi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_elem_step_divide_patch(g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi); ltme += I_Wtime(); update_local_entry(PNGA_ELEM_STEP_DIVIDE_PATCH, ltme, 0); } void wnga_elem_stepb_divide_patch(Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi, Integer g_c, Integer *clo, Integer *chi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_elem_stepb_divide_patch(g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi); ltme += I_Wtime(); update_local_entry(PNGA_ELEM_STEPB_DIVIDE_PATCH, ltme, 0); } void wnga_error(char *string, Integer icode) { unsigned long long ltme; ltme=- I_Wtime(); pnga_error(string, icode); ltme += I_Wtime(); update_local_entry(PNGA_ERROR, ltme, 0); } void wnga_fence() { unsigned long long ltme; ltme=- I_Wtime(); pnga_fence(); ltme += I_Wtime(); update_local_entry(PNGA_FENCE, ltme, 0); } void wnga_fill(Integer g_a, void *val) { unsigned long long ltme; ltme=- I_Wtime(); pnga_fill(g_a, val); ltme += I_Wtime(); update_local_entry(PNGA_FILL, ltme, 0); } void wnga_fill_patch(Integer g_a, Integer *lo, Integer *hi, void *val) { unsigned long long ltme; ltme=- I_Wtime(); pnga_fill_patch(g_a, lo, hi, val); ltme += I_Wtime(); update_local_entry(PNGA_FILL_PATCH, ltme, 0); } void wnga_gather(Integer g_a, void *v, Integer subscript[], Integer c_flag, Integer nv) { unsigned long long ltme, sz, tp; ltme=- I_Wtime(); pnga_gather(g_a, v, subscript, c_flag, nv); ltme += I_Wtime(); OBTAIN_SIZE(tp, c_flag); sz = tp * nv; update_local_entry(PNGA_GATHER, ltme, sz); } void wnga_gather2d(Integer g_a, void *v, Integer *i, Integer *j, Integer nv) { unsigned long long ltme, sz, tp; ltme=- I_Wtime(); pnga_gather2d(g_a, v, i, j, nv); ltme += I_Wtime(); OBTAIN_ESIZE(tp, g_a); sz = tp * nv; update_local_entry(PNGA_GATHER2D, ltme, sz); } void wnga_get(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld) { unsigned long long ltme, sz; /* TODO: Calculate the bytes */ GET_SIZE(g_a, lo, hi, sz); ltme=- I_Wtime(); pnga_get(g_a, lo, hi, buf, ld); ltme += I_Wtime(); update_local_entry(PNGA_GET, ltme, sz); } void wnga_get_block_info(Integer g_a, Integer *num_blocks, Integer *block_dims) { unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); pnga_get_block_info(g_a, num_blocks, block_dims); ltme += I_Wtime(); update_local_entry(PNGA_GET_BLOCK_INFO, ltme, 0); } logical wnga_get_debug() { logical return_value; unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); return_value = pnga_get_debug(); ltme += I_Wtime(); update_local_entry(PNGA_GET_DEBUG, ltme, 0); return return_value; } void wnga_get_diag(Integer g_a, Integer g_v) { unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); pnga_get_diag(g_a, g_v); ltme += I_Wtime(); update_local_entry(PNGA_GET_DIAG, ltme, 0); } Integer wnga_get_dimension(Integer g_a) { Integer return_value; unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); return_value = pnga_get_dimension(g_a); ltme += I_Wtime(); update_local_entry(PNGA_GET_DIMENSION, ltme, 0); return return_value; } void wnga_get_field(Integer g_a, Integer *lo, Integer *hi, Integer foff, Integer fsize, void *buf, Integer *ld) { unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); pnga_get_field(g_a, lo, hi, foff, fsize, buf, ld); ltme += I_Wtime(); update_local_entry(PNGA_GET_FIELD, ltme, 0); } void wnga_get_ghost_block(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld) { unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); pnga_get_ghost_block(g_a, lo, hi, buf, ld); ltme += I_Wtime(); update_local_entry(PNGA_GET_GHOST_BLOCK, ltme, 0); } Integer wnga_get_pgroup(Integer g_a) { Integer return_value; unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); return_value = pnga_get_pgroup(g_a); ltme += I_Wtime(); update_local_entry(PNGA_GET_PGROUP, ltme, 0); return return_value; } Integer wnga_get_pgroup_size(Integer grp_id) { Integer return_value; unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); return_value = pnga_get_pgroup_size(grp_id); ltme += I_Wtime(); update_local_entry(PNGA_GET_PGROUP_SIZE, ltme, 0); return return_value; } void wnga_get_proc_grid(Integer g_a, Integer *dims) { unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); pnga_get_proc_grid(g_a, dims); ltme += I_Wtime(); update_local_entry(PNGA_GET_PROC_GRID, ltme, 0); } void wnga_get_proc_index(Integer g_a, Integer iproc, Integer *index) { unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); pnga_get_proc_index(g_a, iproc, index); ltme += I_Wtime(); update_local_entry(PNGA_GET_PROC_INDEX, ltme, 0); } void wnga_ghost_barrier() { unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); pnga_ghost_barrier(); ltme += I_Wtime(); update_local_entry(PNGA_GHOST_BARRIER, ltme, 0); } void wnga_gop(Integer type, void *x, Integer n, char *op) { unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); pnga_gop(type, x, n, op); ltme += I_Wtime(); update_local_entry(PNGA_GOP, ltme, 0); } logical wnga_has_ghosts(Integer g_a) { logical return_value; unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); return_value = pnga_has_ghosts(g_a); ltme += I_Wtime(); update_local_entry(PNGA_HAS_GHOSTS, ltme, 0); return return_value; } void wnga_init_fence() { unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); pnga_init_fence(); ltme += I_Wtime(); update_local_entry(PNGA_INIT_FENCE, ltme, 0); } void wnga_inquire(Integer g_a, Integer *type, Integer *ndim, Integer *dims) { unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); pnga_inquire(g_a, type, ndim, dims); ltme += I_Wtime(); update_local_entry(PNGA_INQUIRE, ltme, 0); } Integer wnga_inquire_memory() { Integer return_value; unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); return_value = pnga_inquire_memory(); ltme += I_Wtime(); update_local_entry(PNGA_INQUIRE_MEMORY, ltme, 0); return return_value; } void wnga_inquire_name(Integer g_a, char **array_name) { unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); pnga_inquire_name(g_a, array_name); ltme += I_Wtime(); update_local_entry(PNGA_INQUIRE_NAME, ltme, 0); } void wnga_inquire_type(Integer g_a, Integer *type) { unsigned long long ltme; /* TODO: Calculate the bytes */ ltme=- I_Wtime(); pnga_inquire_type(g_a, type); ltme += I_Wtime(); update_local_entry(PNGA_INQUIRE_TYPE, ltme, 0); } logical wnga_is_mirrored(Integer g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_is_mirrored(g_a); ltme += I_Wtime(); update_local_entry(PNGA_IS_MIRRORED, ltme, 0); return return_value; } void wnga_list_nodeid(Integer *list, Integer nprocs) { unsigned long long ltme; ltme=- I_Wtime(); pnga_list_nodeid(list, nprocs); ltme += I_Wtime(); update_local_entry(PNGA_LIST_NODEID, ltme, 0); } Integer wnga_llt_solve(Integer g_a, Integer g_b) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_llt_solve(g_a, g_b); ltme += I_Wtime(); update_local_entry(PNGA_LLT_SOLVE, ltme, 0); return return_value; } logical wnga_locate(Integer g_a, Integer *subscript, Integer *owner) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_locate(g_a, subscript, owner); ltme += I_Wtime(); update_local_entry(PNGA_LOCATE, ltme, 0); return return_value; } logical wnga_locate_nnodes(Integer g_a, Integer *lo, Integer *hi, Integer *np) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_locate_nnodes(g_a, lo, hi, np); ltme += I_Wtime(); update_local_entry(PNGA_LOCATE_NNODES, ltme, 0); return return_value; } Integer wnga_locate_num_blocks(Integer g_a, Integer *lo, Integer *hi) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_locate_num_blocks(g_a, lo, hi); ltme += I_Wtime(); update_local_entry(PNGA_LOCATE_NUM_BLOCKS, ltme, 0); return return_value; } logical wnga_locate_region(Integer g_a, Integer *lo, Integer *hi, Integer *map, Integer *proclist, Integer *np) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_locate_region(g_a, lo, hi, map, proclist, np); ltme += I_Wtime(); update_local_entry(PNGA_LOCATE_REGION, ltme, 0); return return_value; } void wnga_lock(Integer mutex) { unsigned long long ltme; ltme=- I_Wtime(); pnga_lock(mutex); ltme += I_Wtime(); update_local_entry(PNGA_LOCK, ltme, 0); } void wnga_lu_solve(char *tran, Integer g_a, Integer g_b) { unsigned long long ltme; ltme=- I_Wtime(); pnga_lu_solve(tran, g_a, g_b); ltme += I_Wtime(); update_local_entry(PNGA_LU_SOLVE, ltme, 0); } void wnga_lu_solve_alt(Integer tran, Integer g_a, Integer g_b) { unsigned long long ltme; ltme=- I_Wtime(); pnga_lu_solve_alt(tran, g_a, g_b); ltme += I_Wtime(); update_local_entry(PNGA_LU_SOLVE_ALT, ltme, 0); } void wnga_lu_solve_seq(char *trans, Integer g_a, Integer g_b) { unsigned long long ltme; ltme=- I_Wtime(); pnga_lu_solve_seq(trans, g_a, g_b); ltme += I_Wtime(); update_local_entry(PNGA_LU_SOLVE_SEQ, ltme, 0); } void wnga_mask_sync(Integer begin, Integer end) { unsigned long long ltme; ltme=- I_Wtime(); pnga_mask_sync(begin, end); ltme += I_Wtime(); update_local_entry(PNGA_MASK_SYNC, ltme, 0); } void wnga_matmul(char *transa, char *transb, void *alpha, void *beta, Integer g_a, Integer ailo, Integer aihi, Integer ajlo, Integer ajhi, Integer g_b, Integer bilo, Integer bihi, Integer bjlo, Integer bjhi, Integer g_c, Integer cilo, Integer cihi, Integer cjlo, Integer cjhi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_matmul(transa, transb, alpha, beta, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi); ltme += I_Wtime(); update_local_entry(PNGA_MATMUL, ltme, 0); } void wnga_matmul_mirrored(char *transa, char *transb, void *alpha, void *beta, Integer g_a, Integer ailo, Integer aihi, Integer ajlo, Integer ajhi, Integer g_b, Integer bilo, Integer bihi, Integer bjlo, Integer bjhi, Integer g_c, Integer cilo, Integer cihi, Integer cjlo, Integer cjhi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_matmul_mirrored(transa, transb, alpha, beta, g_a, ailo, aihi, ajlo, ajhi, g_b, bilo, bihi, bjlo, bjhi, g_c, cilo, cihi, cjlo, cjhi); ltme += I_Wtime(); update_local_entry(PNGA_MATMUL_MIRRORED, ltme, 0); } void wnga_matmul_patch(char *transa, char *transb, void *alpha, void *beta, Integer g_a, Integer alo[], Integer ahi[], Integer g_b, Integer blo[], Integer bhi[], Integer g_c, Integer clo[], Integer chi[]) { unsigned long long ltme; ltme=- I_Wtime(); pnga_matmul_patch(transa, transb, alpha, beta, g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi); ltme += I_Wtime(); update_local_entry(PNGA_MATMUL_PATCH, ltme, 0); } void wnga_median(Integer g_a, Integer g_b, Integer g_c, Integer g_m) { unsigned long long ltme; ltme=- I_Wtime(); pnga_median(g_a, g_b, g_c, g_m); ltme += I_Wtime(); update_local_entry(PNGA_MEDIAN, ltme, 0); } void wnga_median_patch(Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi, Integer g_c, Integer *clo, Integer *chi, Integer g_m, Integer *mlo, Integer *mhi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_median_patch(g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi, g_m, mlo, mhi); ltme += I_Wtime(); update_local_entry(PNGA_MEDIAN_PATCH, ltme, 0); } Integer wnga_memory_avail() { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_memory_avail(); ltme += I_Wtime(); update_local_entry(PNGA_MEMORY_AVAIL, ltme, 0); return return_value; } Integer wnga_memory_avail_type(Integer datatype) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_memory_avail_type(datatype); ltme += I_Wtime(); update_local_entry(PNGA_MEMORY_AVAIL_TYPE, ltme, 0); return return_value; } logical wnga_memory_limited() { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_memory_limited(); ltme += I_Wtime(); update_local_entry(PNGA_MEMORY_LIMITED, ltme, 0); return return_value; } void wnga_merge_distr_patch(Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_merge_distr_patch(g_a, alo, ahi, g_b, blo, bhi); ltme += I_Wtime(); update_local_entry(PNGA_MERGE_DISTR_PATCH, ltme, 0); } void wnga_merge_mirrored(Integer g_a) { unsigned long long ltme; ltme=- I_Wtime(); pnga_merge_mirrored(g_a); ltme += I_Wtime(); update_local_entry(PNGA_MERGE_MIRRORED, ltme, 0); } void wnga_msg_brdcst(Integer type, void *buffer, Integer len, Integer root) { unsigned long long ltme; ltme=- I_Wtime(); pnga_msg_brdcst(type, buffer, len, root); ltme += I_Wtime(); update_local_entry(PNGA_MSG_BRDCST, ltme, len); } void wnga_msg_pgroup_sync(Integer grp_id) { unsigned long long ltme; ltme=- I_Wtime(); pnga_msg_pgroup_sync(grp_id); ltme += I_Wtime(); update_local_entry(PNGA_MSG_PGROUP_SYNC, ltme, 0); } void wnga_msg_sync() { unsigned long long ltme; ltme=- I_Wtime(); pnga_msg_sync(); ltme += I_Wtime(); update_local_entry(PNGA_MSG_SYNC, ltme, 0); } void wnga_nbacc(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, void *alpha, Integer *nbhndl) { unsigned long long ltme, sz; GET_SIZE(g_a, lo, hi, sz); ltme=- I_Wtime(); pnga_nbacc(g_a, lo, hi, buf, ld, alpha, nbhndl); ltme += I_Wtime(); update_local_entry(PNGA_NBACC, ltme, sz); } void wnga_nbget(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, Integer *nbhandle) { unsigned long long ltme, sz; /* TODO: Calculate the size */ GET_SIZE(g_a, lo, hi, sz); ltme=- I_Wtime(); pnga_nbget(g_a, lo, hi, buf, ld, nbhandle); ltme += I_Wtime(); update_local_entry(PNGA_NBGET, ltme, sz); } void wnga_nbget_field(Integer g_a, Integer *lo, Integer *hi, Integer foff, Integer fsize, void *buf, Integer *ld, Integer *nbhandle) { unsigned long long ltme, sz; /* TODO: Calculate the size */ GET_SIZE(g_a, lo, hi, sz); ltme=- I_Wtime(); pnga_nbget_field(g_a, lo, hi, foff, fsize, buf, ld, nbhandle); ltme += I_Wtime(); update_local_entry(PNGA_NBGET_FIELD, ltme, sz); } void wnga_nbget_ghost_dir(Integer g_a, Integer *mask, Integer *nbhandle) { unsigned long long ltme; /* TODO: Calculate the size */ ltme=- I_Wtime(); pnga_nbget_ghost_dir(g_a, mask, nbhandle); ltme += I_Wtime(); update_local_entry(PNGA_NBGET_GHOST_DIR, ltme, 0); } void wnga_nblock(Integer g_a, Integer *nblock) { unsigned long long ltme; /* TODO: Calculate the size */ ltme=- I_Wtime(); pnga_nblock(g_a, nblock); ltme += I_Wtime(); update_local_entry(PNGA_NBLOCK, ltme, 0); } void wnga_nbput(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, Integer *nbhandle) { unsigned long long ltme, sz; /* TODO: Calculate the size */ GET_SIZE(g_a, lo, hi, sz); ltme=- I_Wtime(); pnga_nbput(g_a, lo, hi, buf, ld, nbhandle); ltme += I_Wtime(); update_local_entry(PNGA_NBPUT, ltme, sz); } void wnga_nbput_field(Integer g_a, Integer *lo, Integer *hi, Integer foff, Integer fsize, void *buf, Integer *ld, Integer *nbhandle) { unsigned long long ltme, sz; /* TODO: Calculate the size */ GET_SIZE(g_a, lo, hi, sz); ltme=- I_Wtime(); pnga_nbput_field(g_a, lo, hi, foff, fsize, buf, ld, nbhandle); ltme += I_Wtime(); update_local_entry(PNGA_NBPUT_FIELD, ltme, sz); } Integer wnga_nbtest(Integer *nbhandle) { Integer return_value; unsigned long long ltme; /* TODO: Calculate the size */ ltme=- I_Wtime(); return_value = pnga_nbtest(nbhandle); ltme += I_Wtime(); update_local_entry(PNGA_NBTEST, ltme, 0); return return_value; } void wnga_nbwait(Integer *nbhandle) { unsigned long long ltme; /* TODO: Calculate the size */ ltme=- I_Wtime(); pnga_nbwait(nbhandle); ltme += I_Wtime(); update_local_entry(PNGA_NBWAIT, ltme, 0); } Integer wnga_ndim(Integer g_a) { Integer return_value; unsigned long long ltme; /* TODO: Calculate the size */ ltme=- I_Wtime(); return_value = pnga_ndim(g_a); ltme += I_Wtime(); update_local_entry(PNGA_NDIM, ltme, 0); return return_value; } Integer wnga_nnodes() { Integer return_value; unsigned long long ltme; /* TODO: Calculate the size */ ltme=- I_Wtime(); return_value = pnga_nnodes(); ltme += I_Wtime(); update_local_entry(PNGA_NNODES, ltme, 0); return return_value; } Integer wnga_nodeid() { Integer return_value; unsigned long long ltme; /* TODO: Calculate the size */ ltme=- I_Wtime(); return_value = pnga_nodeid(); ltme += I_Wtime(); update_local_entry(PNGA_NODEID, ltme, 0); return return_value; } void wnga_norm1(Integer g_a, double *nm) { unsigned long long ltme; ltme=- I_Wtime(); pnga_norm1(g_a, nm); ltme += I_Wtime(); update_local_entry(PNGA_NORM1, ltme, 0); } void wnga_norm_infinity(Integer g_a, double *nm) { unsigned long long ltme; ltme=- I_Wtime(); pnga_norm_infinity(g_a, nm); ltme += I_Wtime(); update_local_entry(PNGA_NORM_INFINITY, ltme, 0); } logical wnga_overlay(Integer g_a, Integer g_parent) { logical result; unsigned long long ltme, sz; ltme=- I_Wtime(); result = pnga_overlay(g_a, g_parent); ltme += I_Wtime(); GET_LOCAL_MSIZE(g_a, sz); update_local_entry(PNGA_OVERLAY, ltme, sz); return result; } void wnga_pack(Integer g_a, Integer g_b, Integer g_sbit, Integer lo, Integer hi, Integer *icount) { unsigned long long ltme; ltme=- I_Wtime(); pnga_pack(g_a, g_b, g_sbit, lo, hi, icount); ltme += I_Wtime(); update_local_entry(PNGA_PACK, ltme, 0); } void wnga_patch_enum(Integer g_a, Integer lo, Integer hi, void *start, void *stride) { unsigned long long ltme; ltme=- I_Wtime(); pnga_patch_enum(g_a, lo, hi, start, stride); ltme += I_Wtime(); update_local_entry(PNGA_PATCH_ENUM, ltme, 0); } logical wnga_patch_intersect(Integer *lo, Integer *hi, Integer *lop, Integer *hip, Integer ndim) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_patch_intersect(lo, hi, lop, hip, ndim); ltme += I_Wtime(); update_local_entry(PNGA_PATCH_INTERSECT, ltme, 0); return return_value; } void wnga_periodic(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld, void *alpha, Integer op_code) { unsigned long long ltme; ltme=- I_Wtime(); pnga_periodic(g_a, lo, hi, buf, ld, alpha, op_code); ltme += I_Wtime(); update_local_entry(PNGA_PERIODIC, ltme, 0); } Integer wnga_pgroup_absolute_id(Integer grp, Integer pid) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_pgroup_absolute_id(grp, pid); ltme += I_Wtime(); update_local_entry(PNGA_PGROUP_ABSOLUTE_ID, ltme, 0); return return_value; } void wnga_pgroup_brdcst(Integer grp_id, Integer type, void *buf, Integer len, Integer originator) { unsigned long long ltme; ltme=- I_Wtime(); pnga_pgroup_brdcst(grp_id, type, buf, len, originator); ltme += I_Wtime(); update_local_entry(PNGA_PGROUP_BRDCST, ltme, 0); } Integer wnga_pgroup_create(Integer *list, Integer count) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_pgroup_create(list, count); ltme += I_Wtime(); update_local_entry(PNGA_PGROUP_CREATE, ltme, 0); return return_value; } logical wnga_pgroup_destroy(Integer grp) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_pgroup_destroy(grp); ltme += I_Wtime(); update_local_entry(PNGA_PGROUP_DESTROY, ltme, 0); return return_value; } Integer wnga_pgroup_get_default() { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_pgroup_get_default(); ltme += I_Wtime(); update_local_entry(PNGA_PGROUP_GET_DEFAULT, ltme, 0); return return_value; } Integer wnga_pgroup_get_mirror() { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_pgroup_get_mirror(); ltme += I_Wtime(); update_local_entry(PNGA_PGROUP_GET_MIRROR, ltme, 0); return return_value; } Integer wnga_pgroup_get_world() { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_pgroup_get_world(); ltme += I_Wtime(); update_local_entry(PNGA_PGROUP_GET_WORLD, ltme, 0); return return_value; } void wnga_pgroup_gop(Integer p_grp, Integer type, void *x, Integer n, char *op) { unsigned long long ltme; ltme=- I_Wtime(); pnga_pgroup_gop(p_grp, type, x, n, op); ltme += I_Wtime(); update_local_entry(PNGA_PGROUP_GOP, ltme, 0); } Integer wnga_pgroup_nnodes(Integer grp) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_pgroup_nnodes(grp); ltme += I_Wtime(); update_local_entry(PNGA_PGROUP_NNODES, ltme, 0); return return_value; } Integer wnga_pgroup_nodeid(Integer grp) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_pgroup_nodeid(grp); ltme += I_Wtime(); update_local_entry(PNGA_PGROUP_NODEID, ltme, 0); return return_value; } void wnga_pgroup_set_default(Integer grp) { unsigned long long ltme; ltme=- I_Wtime(); pnga_pgroup_set_default(grp); ltme += I_Wtime(); update_local_entry(PNGA_PGROUP_SET_DEFAULT, ltme, 0); } Integer wnga_pgroup_split(Integer grp, Integer grp_num) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_pgroup_split(grp, grp_num); ltme += I_Wtime(); update_local_entry(PNGA_PGROUP_SPLIT, ltme, 0); return return_value; } Integer wnga_pgroup_split_irreg(Integer grp, Integer mycolor) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_pgroup_split_irreg(grp, mycolor); ltme += I_Wtime(); update_local_entry(PNGA_PGROUP_SPLIT_IRREG, ltme, 0); return return_value; } void wnga_pgroup_sync(Integer grp_id) { unsigned long long ltme; ltme=- I_Wtime(); pnga_pgroup_sync(grp_id); ltme += I_Wtime(); update_local_entry(PNGA_PGROUP_SYNC, ltme, 0); } void wnga_print(Integer g_a) { unsigned long long ltme; ltme=- I_Wtime(); pnga_print(g_a); ltme += I_Wtime(); update_local_entry(PNGA_PRINT, ltme, 0); } void wnga_print_distribution(int fstyle, Integer g_a) { unsigned long long ltme; ltme=- I_Wtime(); pnga_print_distribution(fstyle, g_a); ltme += I_Wtime(); update_local_entry(PNGA_PRINT_DISTRIBUTION, ltme, 0); } void wnga_print_file(FILE *file, Integer g_a) { unsigned long long ltme; ltme=- I_Wtime(); pnga_print_file(file, g_a); ltme += I_Wtime(); update_local_entry(PNGA_PRINT_FILE, ltme, 0); } void wnga_print_patch(Integer g_a, Integer *lo, Integer *hi, Integer pretty) { unsigned long long ltme; ltme=- I_Wtime(); pnga_print_patch(g_a, lo, hi, pretty); ltme += I_Wtime(); update_local_entry(PNGA_PRINT_PATCH, ltme, 0); } void wnga_print_patch2d(Integer g_a, Integer ilo, Integer ihi, Integer jlo, Integer jhi, Integer pretty) { unsigned long long ltme; ltme=- I_Wtime(); pnga_print_patch2d(g_a, ilo, ihi, jlo, jhi, pretty); ltme += I_Wtime(); update_local_entry(PNGA_PRINT_PATCH2D, ltme, 0); } void wnga_print_patch_file(FILE *file, Integer g_a, Integer *lo, Integer *hi, Integer pretty) { unsigned long long ltme; ltme=- I_Wtime(); pnga_print_patch_file(file, g_a, lo, hi, pretty); ltme += I_Wtime(); update_local_entry(PNGA_PRINT_PATCH_FILE, ltme, 0); } void wnga_print_patch_file2d(FILE *file, Integer g_a, Integer ilo, Integer ihi, Integer jlo, Integer jhi, Integer pretty) { unsigned long long ltme; ltme=- I_Wtime(); pnga_print_patch_file2d(file, g_a, ilo, ihi, jlo, jhi, pretty); ltme += I_Wtime(); update_local_entry(PNGA_PRINT_PATCH_FILE2D, ltme, 0); } void wnga_print_stats() { unsigned long long ltme; ltme=- I_Wtime(); pnga_print_stats(); ltme += I_Wtime(); update_local_entry(PNGA_PRINT_STATS, ltme, 0); } void wnga_proc_topology(Integer g_a, Integer proc, Integer *subscript) { unsigned long long ltme; ltme=- I_Wtime(); pnga_proc_topology(g_a, proc, subscript); ltme += I_Wtime(); update_local_entry(PNGA_PROC_TOPOLOGY, ltme, 0); } void wnga_put(Integer g_a, Integer *lo, Integer *hi, void *buf, Integer *ld) { unsigned long long ltme, sz; GET_SIZE(g_a, lo, hi, sz); ltme=- I_Wtime(); pnga_put(g_a, lo, hi, buf, ld); ltme += I_Wtime(); update_local_entry(PNGA_PUT, ltme, sz); } void wnga_put_field(Integer g_a, Integer *lo, Integer *hi, Integer foff, Integer fsize, void *buf, Integer *ld) { unsigned long long ltme, sz; GET_SIZE(g_a, lo, hi, sz); ltme=- I_Wtime(); pnga_put_field(g_a, lo, hi, foff, fsize, buf, ld); ltme += I_Wtime(); update_local_entry(PNGA_PUT_FIELD, ltme, sz); } void wnga_randomize(Integer g_a, void *val) { unsigned long long ltme; ltme=- I_Wtime(); pnga_randomize(g_a, val); ltme += I_Wtime(); update_local_entry(PNGA_RANDOMIZE, ltme, 0); } Integer wnga_read_inc(Integer g_a, Integer *subscript, Integer inc) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_read_inc(g_a, subscript, inc); ltme += I_Wtime(); update_local_entry(PNGA_READ_INC, ltme, 0); return return_value; } void wnga_recip(Integer g_a) { unsigned long long ltme; ltme=- I_Wtime(); pnga_recip(g_a); ltme += I_Wtime(); update_local_entry(PNGA_RECIP, ltme, 0); } void wnga_recip_patch(Integer g_a, Integer *lo, Integer *hi) { unsigned long long ltme, sz; GET_SIZE(g_a, lo, hi, sz); ltme=- I_Wtime(); pnga_recip_patch(g_a, lo, hi); ltme += I_Wtime(); update_local_entry(PNGA_RECIP_PATCH, ltme, sz); } int wnga_register_type(size_t size) { int return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_register_type(size); ltme += I_Wtime(); update_local_entry(PNGA_REGISTER_TYPE, ltme, 0); return return_value; } void wnga_release(Integer g_a, Integer *lo, Integer *hi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_release(g_a, lo, hi); ltme += I_Wtime(); update_local_entry(PNGA_RELEASE, ltme, 0); } void wnga_release_block(Integer g_a, Integer iblock) { unsigned long long ltme; ltme=- I_Wtime(); pnga_release_block(g_a, iblock); ltme += I_Wtime(); update_local_entry(PNGA_RELEASE_BLOCK, ltme, 0); } void wnga_release_block_grid(Integer g_a, Integer *index) { unsigned long long ltme; ltme=- I_Wtime(); pnga_release_block_grid(g_a, index); ltme += I_Wtime(); update_local_entry(PNGA_RELEASE_BLOCK_GRID, ltme, 0); } void wnga_release_block_segment(Integer g_a, Integer iproc) { unsigned long long ltme; ltme=- I_Wtime(); pnga_release_block_segment(g_a, iproc); ltme += I_Wtime(); update_local_entry(PNGA_RELEASE_BLOCK_SEGMENT, ltme, 0); } void wnga_release_ghost_element(Integer g_a, Integer subscript[]) { unsigned long long ltme; ltme=- I_Wtime(); pnga_release_ghost_element(g_a, subscript); ltme += I_Wtime(); update_local_entry(PNGA_RELEASE_GHOST_ELEMENT, ltme, 0); } void wnga_release_ghosts(Integer g_a) { unsigned long long ltme; ltme=- I_Wtime(); pnga_release_ghosts(g_a); ltme += I_Wtime(); update_local_entry(PNGA_RELEASE_GHOSTS, ltme, 0); } void wnga_release_update(Integer g_a, Integer *lo, Integer *hi) { unsigned long long ltme, sz; GET_SIZE(g_a, lo, hi, sz); ltme=- I_Wtime(); pnga_release_update(g_a, lo, hi); ltme += I_Wtime(); update_local_entry(PNGA_RELEASE_UDPATE, ltme, sz); } void wnga_release_update_block(Integer g_a, Integer iblock) { unsigned long long ltme; ltme=- I_Wtime(); pnga_release_update_block(g_a, iblock); ltme += I_Wtime(); update_local_entry(PNGA_RELEASE_UDPATE_BLOCK, ltme, 0); } void wnga_release_update_block_grid(Integer g_a, Integer *index) { unsigned long long ltme; ltme=- I_Wtime(); pnga_release_update_block_grid(g_a, index); ltme += I_Wtime(); update_local_entry(PNGA_RELEASE_UDPATE_BLOCK_GRID, ltme, 0); } void wnga_release_update_block_segment(Integer g_a, Integer iproc) { unsigned long long ltme; ltme=- I_Wtime(); pnga_release_update_block_segment(g_a, iproc); ltme += I_Wtime(); update_local_entry(PNGA_RELEASE_UDPATE_BLOCK_SEGMENT, ltme, 0); } void wnga_release_update_ghost_element(Integer g_a, Integer subscript[]) { unsigned long long ltme; ltme=- I_Wtime(); pnga_release_update_ghost_element(g_a, subscript); ltme += I_Wtime(); update_local_entry(PNGA_RELEASE_UDPATE_GHOST_ELEMENT, ltme, 0); } void wnga_release_update_ghosts(Integer g_a) { unsigned long long ltme; ltme=- I_Wtime(); pnga_release_update_ghosts(g_a); ltme += I_Wtime(); update_local_entry(PNGA_RELEASE_UDPATE_GHOSTS, ltme, 0); } void wnga_scale(Integer g_a, void *alpha) { unsigned long long ltme; ltme=- I_Wtime(); pnga_scale(g_a, alpha); ltme += I_Wtime(); update_local_entry(PNGA_SCALE, ltme, 0); } void wnga_scale_cols(Integer g_a, Integer g_v) { unsigned long long ltme; ltme=- I_Wtime(); pnga_scale_cols(g_a, g_v); ltme += I_Wtime(); update_local_entry(PNGA_SCALE_COLS, ltme, 0); } void wnga_scale_patch(Integer g_a, Integer *lo, Integer *hi, void *alpha) { unsigned long long ltme, sz; GET_SIZE(g_a, lo, hi, sz); ltme=- I_Wtime(); pnga_scale_patch(g_a, lo, hi, alpha); ltme += I_Wtime(); update_local_entry(PNGA_SCALE_PATCH, ltme, sz); } void wnga_scale_rows(Integer g_a, Integer g_v) { unsigned long long ltme; ltme=- I_Wtime(); pnga_scale_rows(g_a, g_v); ltme += I_Wtime(); update_local_entry(PNGA_SCALE_ROWS, ltme, 0); } void wnga_scan_add(Integer g_a, Integer g_b, Integer g_sbit, Integer lo, Integer hi, Integer excl) { unsigned long long ltme; ltme=- I_Wtime(); pnga_scan_add(g_a, g_b, g_sbit, lo, hi, excl); ltme += I_Wtime(); update_local_entry(PNGA_SCAN_ADD, ltme, 0); } void wnga_scan_copy(Integer g_a, Integer g_b, Integer g_sbit, Integer lo, Integer hi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_scan_copy(g_a, g_b, g_sbit, lo, hi); ltme += I_Wtime(); update_local_entry(PNGA_SCAN_COPY, ltme, 0); } void wnga_scatter(Integer g_a, void *v, Integer *subscript, Integer c_flag, Integer nv) { unsigned long long ltme, sz; OBTAIN_SIZE(sz, c_flag); sz *= nv; ltme=- I_Wtime(); pnga_scatter(g_a, v, subscript, c_flag, nv); ltme += I_Wtime(); update_local_entry(PNGA_SCATTER, ltme, sz); } void wnga_scatter2d(Integer g_a, void *v, Integer *i, Integer *j, Integer nv) { unsigned long long ltme, sz; OBTAIN_ESIZE(sz, g_a); ltme=- I_Wtime(); pnga_scatter2d(g_a, v, i, j, nv); ltme += I_Wtime(); sz *= nv; update_local_entry(PNGA_SCATTER2D, ltme, sz); } void wnga_scatter_acc(Integer g_a, void *v, Integer subscript[], Integer c_flag, Integer nv, void *alpha) { unsigned long long ltme, sz; OBTAIN_SIZE(sz, c_flag); sz *= nv; ltme=- I_Wtime(); pnga_scatter_acc(g_a, v, subscript, c_flag, nv, alpha); ltme += I_Wtime(); update_local_entry(PNGA_SCATTER_ACC, ltme, sz); } void wnga_scatter_acc2d(Integer g_a, void *v, Integer *i, Integer *j, Integer nv, void *alpha) { unsigned long long ltme, sz; OBTAIN_ESIZE(sz, g_a); ltme=- I_Wtime(); pnga_scatter_acc2d(g_a, v, i, j, nv, alpha); ltme += I_Wtime(); sz *= nv; update_local_entry(PNGA_SCATTER_ACC2D, ltme, sz); } void wnga_select_elem(Integer g_a, char *op, void *val, Integer *subscript) { unsigned long long ltme; ltme=- I_Wtime(); pnga_select_elem(g_a, op, val, subscript); ltme += I_Wtime(); update_local_entry(PNGA_SELECT_ELEM, ltme, 0); } void wnga_set_array_name(Integer g_a, char *array_name) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_array_name(g_a, array_name); ltme += I_Wtime(); update_local_entry(PNGA_SET_ARRAY_NAME, ltme, 0); } void wnga_set_block_cyclic(Integer g_a, Integer *dims) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_block_cyclic(g_a, dims); ltme += I_Wtime(); update_local_entry(PNGA_SET_BLOCK_CYCLIC, ltme, 0); } void wnga_set_block_cyclic_proc_grid(Integer g_a, Integer *dims, Integer *proc_grid) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_block_cyclic_proc_grid(g_a, dims, proc_grid); ltme += I_Wtime(); update_local_entry(PNGA_SET_BLOCK_CYCLIC_PROC_GRID, ltme, 0); } void wnga_set_chunk(Integer g_a, Integer *chunk) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_chunk(g_a, chunk); ltme += I_Wtime(); update_local_entry(PNGA_SET_CHUNK, ltme, 0); } void wnga_set_data(Integer g_a, Integer ndim, Integer *dims, Integer type) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_data(g_a, ndim, dims, type); ltme += I_Wtime(); update_local_entry(PNGA_SET_DATA, ltme, 0); } void wnga_set_debug(logical flag) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_debug(flag); ltme += I_Wtime(); update_local_entry(PNGA_SET_DEBUG, ltme, 0); } void wnga_set_diagonal(Integer g_a, Integer g_v) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_diagonal(g_a, g_v); ltme += I_Wtime(); update_local_entry(PNGA_SET_DIAGONAL, ltme, 0); } void wnga_set_ghost_corner_flag(Integer g_a, logical flag) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_ghost_corner_flag(g_a, flag); ltme += I_Wtime(); update_local_entry(PNGA_SET_GHOST_CORNER_FLAG, ltme, 0); } logical wnga_set_ghost_info(Integer g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_set_ghost_info(g_a); ltme += I_Wtime(); update_local_entry(PNGA_SET_GHOST_INFO, ltme, 0); return return_value; } void wnga_set_ghosts(Integer g_a, Integer *width) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_ghosts(g_a, width); ltme += I_Wtime(); update_local_entry(PNGA_SET_GHOSTS, ltme, 0); } void wnga_set_irreg_distr(Integer g_a, Integer *map, Integer *block) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_irreg_distr(g_a, map, block); ltme += I_Wtime(); update_local_entry(PNGA_SET_IRREG_DISTR, ltme, 0); } void wnga_set_irreg_flag(Integer g_a, logical flag) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_irreg_flag(g_a, flag); ltme += I_Wtime(); update_local_entry(PNGA_SET_IRREG_FLAG, ltme, 0); } void wnga_set_memory_limit(Integer mem_limit) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_memory_limit(mem_limit); ltme += I_Wtime(); update_local_entry(PNGA_SET_MEMORY_LIMIT, ltme, 0); } void wnga_set_pgroup(Integer g_a, Integer p_handle) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_pgroup(g_a, p_handle); ltme += I_Wtime(); update_local_entry(PNGA_SET_PGROUP, ltme, 0); } void wnga_set_restricted(Integer g_a, Integer *list, Integer size) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_restricted(g_a, list, size); ltme += I_Wtime(); update_local_entry(PNGA_SET_RESTRICTED, ltme, 0); } void wnga_set_restricted_range(Integer g_a, Integer lo_proc, Integer hi_proc) { unsigned long long ltme; ltme=- I_Wtime(); pnga_set_restricted_range(g_a, lo_proc, hi_proc); ltme += I_Wtime(); update_local_entry(PNGA_SET_RESTRICTED_RANGE, ltme, 0); } logical wnga_set_update4_info(Integer g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_set_update4_info(g_a); ltme += I_Wtime(); update_local_entry(PNGA_SET_UPDATE4_INFO, ltme, 0); return return_value; } logical wnga_set_update5_info(Integer g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_set_update5_info(g_a); ltme += I_Wtime(); update_local_entry(PNGA_SET_UPDATE5_INFO, ltme, 0); return return_value; } void wnga_shift_diagonal(Integer g_a, void *c) { unsigned long long ltme; ltme=- I_Wtime(); pnga_shift_diagonal(g_a, c); ltme += I_Wtime(); update_local_entry(PNGA_SHIFT_DIAGONAL, ltme, 0); } Integer wnga_solve(Integer g_a, Integer g_b) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_solve(g_a, g_b); ltme += I_Wtime(); update_local_entry(PNGA_SOLVE, ltme, 0); return return_value; } Integer wnga_spd_invert(Integer g_a) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_spd_invert(g_a); ltme += I_Wtime(); update_local_entry(PNGA_SPD_INVERT, ltme, 0); return return_value; } void wnga_step_bound_info(Integer g_xx, Integer g_vv, Integer g_xxll, Integer g_xxuu, void *boundmin, void *wolfemin, void *boundmax) { unsigned long long ltme; ltme=- I_Wtime(); pnga_step_bound_info(g_xx, g_vv, g_xxll, g_xxuu, boundmin, wolfemin, boundmax); ltme += I_Wtime(); update_local_entry(PNGA_STEP_BOUND_INFO, ltme, 0); } void wnga_step_bound_info_patch(Integer g_xx, Integer *xxlo, Integer *xxhi, Integer g_vv, Integer *vvlo, Integer *vvhi, Integer g_xxll, Integer *xxlllo, Integer *xxllhi, Integer g_xxuu, Integer *xxuulo, Integer *xxuuhi, void *boundmin, void *wolfemin, void *boundmax) { unsigned long long ltme; ltme=- I_Wtime(); pnga_step_bound_info_patch(g_xx, xxlo, xxhi, g_vv, vvlo, vvhi, g_xxll, xxlllo, xxllhi, g_xxuu, xxuulo, xxuuhi, boundmin, wolfemin, boundmax); ltme += I_Wtime(); update_local_entry(PNGA_STEP_BOUND_INFO_PATCH, ltme, 0); } void wnga_step_mask_patch(Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi, Integer g_c, Integer *clo, Integer *chi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_step_mask_patch(g_a, alo, ahi, g_b, blo, bhi, g_c, clo, chi); ltme += I_Wtime(); update_local_entry(PNGA_STEP_MASK_PATCH, ltme, 0); } void wnga_step_max(Integer g_a, Integer g_b, void *retval) { unsigned long long ltme; ltme=- I_Wtime(); pnga_step_max(g_a, g_b, retval); ltme += I_Wtime(); update_local_entry(PNGA_STEP_MAX, ltme, 0); } void wnga_step_max_patch(Integer g_a, Integer *alo, Integer *ahi, Integer g_b, Integer *blo, Integer *bhi, void *result) { unsigned long long ltme; ltme=- I_Wtime(); pnga_step_max_patch(g_a, alo, ahi, g_b, blo, bhi, result); ltme += I_Wtime(); update_local_entry(PNGA_STEP_MAX_PATCH, ltme, 0); } void wnga_strided_acc(Integer g_a, Integer *lo, Integer *hi, Integer *skip, void *buf, Integer *ld, void *alpha) { unsigned long long ltme; ltme=- I_Wtime(); pnga_strided_acc(g_a, lo, hi, skip, buf, ld, alpha); ltme += I_Wtime(); update_local_entry(PNGA_STRIDED_ACC, ltme, 0); } void wnga_strided_get(Integer g_a, Integer *lo, Integer *hi, Integer *skip, void *buf, Integer *ld) { unsigned long long ltme; ltme=- I_Wtime(); pnga_strided_get(g_a, lo, hi, skip, buf, ld); ltme += I_Wtime(); update_local_entry(PNGA_STRIDED_GET, ltme, 0); } void wnga_strided_put(Integer g_a, Integer *lo, Integer *hi, Integer *skip, void *buf, Integer *ld) { unsigned long long ltme; ltme=- I_Wtime(); pnga_strided_put(g_a, lo, hi, skip, buf, ld); ltme += I_Wtime(); update_local_entry(PNGA_STRIDED_PUT, ltme, 0); } void wnga_summarize(Integer verbose) { unsigned long long ltme; ltme=- I_Wtime(); pnga_summarize(verbose); ltme += I_Wtime(); update_local_entry(PNGA_SUMMARIZE, ltme, 0); } void wnga_symmetrize(Integer g_a) { unsigned long long ltme; ltme=- I_Wtime(); pnga_symmetrize(g_a); ltme += I_Wtime(); update_local_entry(PNGA_SYMMETRIZE, ltme, 0); } void wnga_sync() { unsigned long long ltme; ltme=- I_Wtime(); pnga_sync(); ltme += I_Wtime(); update_local_entry(PNGA_SYNC, ltme, 0); } double wnga_timer() { double return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_timer(); ltme += I_Wtime(); update_local_entry(PNGA_TIMER, ltme, 0); return return_value; } Integer wnga_total_blocks(Integer g_a) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_total_blocks(g_a); ltme += I_Wtime(); update_local_entry(PNGA_TOTAL_BLOCKS, ltme, 0); return return_value; } void wnga_transpose(Integer g_a, Integer g_b) { unsigned long long ltme; ltme=- I_Wtime(); pnga_transpose(g_a, g_b); ltme += I_Wtime(); update_local_entry(PNGA_TRANSPOSE, ltme, 0); } Integer wnga_type_c2f(Integer type) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_type_c2f(type); ltme += I_Wtime(); update_local_entry(PNGA_TYPE_C2F, ltme, 0); return return_value; } Integer wnga_type_f2c(Integer type) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_type_f2c(type); ltme += I_Wtime(); update_local_entry(PNGA_TYPE_F2C, ltme, 0); return return_value; } void wnga_unlock(Integer mutex) { unsigned long long ltme; ltme=- I_Wtime(); pnga_unlock(mutex); ltme += I_Wtime(); update_local_entry(PNGA_UNLOCK, ltme, 0); } void wnga_unpack(Integer g_a, Integer g_b, Integer g_sbit, Integer lo, Integer hi, Integer *icount) { unsigned long long ltme; ltme=- I_Wtime(); pnga_unpack(g_a, g_b, g_sbit, lo, hi, icount); ltme += I_Wtime(); update_local_entry(PNGA_UNPACK, ltme, 0); } void wnga_update1_ghosts(Integer g_a) { unsigned long long ltme; ltme=- I_Wtime(); pnga_update1_ghosts(g_a); ltme += I_Wtime(); update_local_entry(PNGA_UPDATE1_GHOSTS, ltme, 0); } logical wnga_update2_ghosts(Integer g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_update2_ghosts(g_a); ltme += I_Wtime(); update_local_entry(PNGA_UPDATE2_GHOSTS, ltme, 0); return return_value; } logical wnga_update3_ghosts(Integer g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_update3_ghosts(g_a); ltme += I_Wtime(); update_local_entry(PNGA_UPDATE3_GHOSTS, ltme, 0); return return_value; } logical wnga_update44_ghosts(Integer g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_update44_ghosts(g_a); ltme += I_Wtime(); update_local_entry(PNGA_UPDATED44_GHOSTS, ltme, 0); return return_value; } logical wnga_update4_ghosts(Integer g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_update4_ghosts(g_a); ltme += I_Wtime(); update_local_entry(PNGA_UPDATE4_GHOSTS, ltme, 0); return return_value; } logical wnga_update55_ghosts(Integer g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_update55_ghosts(g_a); ltme += I_Wtime(); update_local_entry(PNGA_UPDATE55_GHOSTS, ltme, 0); return return_value; } logical wnga_update5_ghosts(Integer g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_update5_ghosts(g_a); ltme += I_Wtime(); update_local_entry(PNGA_UPDATE55_GHOSTS, ltme, 0); return return_value; } logical wnga_update6_ghosts(Integer g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_update6_ghosts(g_a); ltme += I_Wtime(); update_local_entry(PNGA_UPDATE6_GHOSTS, ltme, 0); return return_value; } logical wnga_update7_ghosts(Integer g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_update7_ghosts(g_a); ltme += I_Wtime(); update_local_entry(PNGA_UPDATE7_GHOSTS, ltme, 0); return return_value; } logical wnga_update_ghost_dir(Integer g_a, Integer pdim, Integer pdir, logical pflag) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_update_ghost_dir(g_a, pdim, pdir, pflag); ltme += I_Wtime(); update_local_entry(PNGA_UPDATE_GHOST_DIR, ltme, 0); return return_value; } void wnga_update_ghosts(Integer g_a) { unsigned long long ltme; ltme=- I_Wtime(); pnga_update_ghosts(g_a); ltme += I_Wtime(); update_local_entry(PNGA_UPDATE_GHOSTS, ltme, 0); } logical wnga_uses_ma() { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_uses_ma(); ltme += I_Wtime(); update_local_entry(PNGA_USES_MA, ltme, 0); return return_value; } logical wnga_uses_proc_grid(Integer g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_uses_proc_grid(g_a); ltme += I_Wtime(); update_local_entry(PNGA_USES_PROC_GRID, ltme, 0); return return_value; } logical wnga_valid_handle(Integer g_a) { logical return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_valid_handle(g_a); ltme += I_Wtime(); update_local_entry(PNGA_VALID_HANDLE, ltme, 0); return return_value; } Integer wnga_verify_handle(Integer g_a) { Integer return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_verify_handle(g_a); ltme += I_Wtime(); update_local_entry(PNGA_VERIFY_HANDLE, ltme, 0); return return_value; } DoublePrecision wnga_wtime() { DoublePrecision return_value; unsigned long long ltme; ltme=- I_Wtime(); return_value = pnga_wtime(); ltme += I_Wtime(); update_local_entry(PNGA_WTIME, ltme, 0); return return_value; } void wnga_zero(Integer g_a) { unsigned long long ltme; ltme=- I_Wtime(); pnga_zero(g_a); ltme += I_Wtime(); update_local_entry(PNGA_ZERO, ltme, 0); } void wnga_zero_diagonal(Integer g_a) { unsigned long long ltme; ltme=- I_Wtime(); pnga_zero_diagonal(g_a); ltme += I_Wtime(); update_local_entry(PNGA_ZERO_DIAGONAL, ltme, 0); } void wnga_zero_patch(Integer g_a, Integer *lo, Integer *hi) { unsigned long long ltme; ltme=- I_Wtime(); pnga_zero_patch(g_a, lo, hi); ltme += I_Wtime(); update_local_entry(PNGA_ZERO_PATCH, ltme, 0); } void wnga_initialize() { pnga_initialize(); update_local_entry(PNGA_INITIALIZE, 0, 0); MPI_Comm_rank(MPI_COMM_WORLD, &me); MPI_Comm_size(MPI_COMM_WORLD, &nproc); init_ga_prof_struct(me, nproc); } void wnga_initialize_ltd(Integer limit) { pnga_initialize_ltd(limit); update_local_entry(PNGA_INITIALIZE_LTD, 0, 0); MPI_Comm_rank(MPI_COMM_WORLD, &me); MPI_Comm_size(MPI_COMM_WORLD, &nproc); init_ga_prof_struct(me, nproc); } void wnga_terminate() { int lme, lnproc, i; static int terminate_flag = 0; ++terminate_flag; MPI_Comm comm = GA_MPI_Comm(); update_local_entry(PNGA_TERMINATE, 0, 0); pnga_terminate(); if(terminate_flag == 1){ for(i = 0; i < WPROF_TOTAL; ++i) update_global_entry(i, comm); print_ga_prof_stats(HUMAN_FMT, stdout, comm); } } ga-5.9.2/tools/ga-wprof.c000066400000000000000000000334031500715745200151550ustar00rootroot00000000000000#include "./ga-wprof.h" ga_weak_profile_t gaw_global_stats[WPROF_TOTAL]; ga_weak_profile_t gaw_global_min_stats[WPROF_TOTAL]; ga_weak_profile_t gaw_global_max_stats[WPROF_TOTAL]; ga_weak_profile_t gaw_local_stats[WPROF_TOTAL]; char ga_weak_symbols[WPROF_TOTAL+1][80] = { "PNGA_ABS_VALUE", "PNGA_ABS_VALUE_PATCH", "PNGA_ACC", "PNGA_ACCESS_BLOCK_GRID_IDX", "PNGA_ACCESS_BLOCK_GRID_PTR", "PNGA_ACCESS_BLOCK_IDX", "PNGA_ACCESS_BLOCK_PTR", "PNGA_ACCESS_BLOCK_SEGMENT_IDX", "PNGA_ACCESS_BLOCK_SEGMENT_PTR", "PNGA_ACCESS_GHOST_ELEMENT", "PNGA_ACCESS_GHOST_ELEMENT_PTR", "PNGA_ACCESS_GHOST_PTR", "PNGA_ACCESS_GHOSTS", "PNGA_ACCESS_IDX", "PNGA_ACCESS_PTR", "PNGA_ADD", "PNGA_ADD_CONSTANT", "PNGA_ADD_CONSTANT_PATCH", "PNGA_ADD_DIAGONAL", "PNGA_ADD_PATCH", "PNGA_ALLOCATE", "PNGA_BIN_INDEX", "PNGA_BIN_SORTER", "PNGA_BRDCST", "PNGA_CHECK_HANDLE", "PNGA_CLUSTER_NNODES", "PNGA_CLUSTER_NODEID", "PNGA_CLUSTER_NPROCS", "PNGA_CLUSTER_PROC_NODEID", "PNGA_CLUSTER_PROCID", "PNGA_COMP_PATCH", "PNGA_COMPARE_DISTR", "PNGA_COPY", "PNGA_COPY_PATCH", "PNGA_COPY_PATCH_DP", "PNGA_CREATE", "PNGA_CREATE_BIN_RANGE", "PNGA_CREATE_CONFIG", "PNGA_CREATE_GHOSTS", "PNGA_CREATE_GHOSTS_CONFIG", "PNGA_CREATE_GHOSTS_IRREG", "PNGA_CREATE_GHOSTS_IRREG_CONFIG", "PNGA_CREATE_HANDLE", "PNGA_CREATE_IRREG", "PNGA_CREATE_IRREG_CONFIG", "PNGA_CREATE_CREATE_MUTEXES", "PNGA_DDOT_PATCH_DP", "PNGA_DEREGISTER_TYPE", "PNGA_DESTROY", "PNGA_DESTROY_MUTEXES", "PNGA_DIAG", "PNGA_DIAG_REUSE", "PNGA_DIAG_SEQ", "PNGA_DIAG_STD", "PNGA_DIAG_STD_SEQ", "PNGA_DISTRIBUTION", "PNGA_DOT", "PNGA_DOT_PATCH", "PNGA_DUPLICATE", "PNGA_ELEM_DIVIDE", "PNGA_ELEM_DIVIDE_PATCH", "PNGA_ELEM_MAXIMUM", "PNGA_ELEM_MAXIMUM_PATCH", "PNGA_ELEM_MINIMUM", "PNGA_ELEM_MINIMUM_PATCH", "PNGA_ELEM_MULTIPLY", "PNGA_ELEM_MULTIPLY_PATCH", "PNGA_ELEM_STEP_DIVIDE_PATCH", "PNGA_ELEM_STEPB_DIVIDE_PATCH", "PNGA_ERROR", "PNGA_FENCE", "PNGA_FILL", "PNGA_FILL_PATCH", "PNGA_GATHER", "PNGA_GATHER2D", "PNGA_GET", "PNGA_GET_BLOCK_INFO", "PNGA_GET_DEBUG", "PNGA_GET_DIAG", "PNGA_GET_DIMENSION", "PNGA_GET_FIELD", "PNGA_GET_GHOST_BLOCK", "PNGA_GET_PGROUP", "PNGA_GET_PGROUP_SIZE", "PNGA_GET_PROC_GRID", "PNGA_GET_PROC_INDEX", "PNGA_GHOST_BARRIER", "PNGA_GOP", "PNGA_HAS_GHOSTS", "PNGA_INIT_FENCE", "PNGA_INITIALIZE", "PNGA_INITIALIZE_LTD", "PNGA_INQUIRE", "PNGA_INQUIRE_MEMORY", "PNGA_INQUIRE_NAME", "PNGA_INQUIRE_TYPE", "PNGA_IS_MIRRORED", "PNGA_LIST_NODEID", "PNGA_LLT_SOLVE", "PNGA_LOCATE", "PNGA_LOCATE_NNODES", "PNGA_LOCATE_NUM_BLOCKS", "PNGA_LOCATE_REGION", "PNGA_LOCK", "PNGA_LU_SOLVE", "PNGA_LU_SOLVE_ALT", "PNGA_LU_SOLVE_SEQ", "PNGA_MASK_SYNC", "PNGA_MATMUL", "PNGA_MATMUL_MIRRORED", "PNGA_MATMUL_PATCH", "PNGA_MEDIAN", "PNGA_MEDIAN_PATCH", "PNGA_MEMORY_AVAIL", "PNGA_MEMORY_AVAIL_TYPE", "PNGA_MEMORY_LIMITED", "PNGA_MERGE_DISTR_PATCH", "PNGA_MERGE_MIRRORED", "PNGA_MSG_BRDCST", "PNGA_MSG_PGROUP_SYNC", "PNGA_MSG_SYNC", "PNGA_NBACC", "PNGA_NBGET", "PNGA_NBGET_FIELD", "PNGA_NBGET_GHOST_DIR", "PNGA_NBLOCK", "PNGA_NBPUT", "PNGA_NBPUT_FIELD", "PNGA_NBTEST", "PNGA_NBWAIT", "PNGA_NDIM", "PNGA_NNODES", "PNGA_NODEID", "PNGA_NORM1", "PNGA_NORM_INFINITY", "PNGA_OVERLAY", "PNGA_PACK", "PNGA_PATCH_ENUM", "PNGA_PATCH_INTERSECT", "PNGA_PERIODIC", "PNGA_PGROUP_ABSOLUTE_ID", "PNGA_PGROUP_BRDCST", "PNGA_PGROUP_CREATE", "PNGA_PGROUP_DESTROY", "PNGA_PGROUP_GET_DEFAULT", "PNGA_PGROUP_GET_MIRROR", "PNGA_PGROUP_GET_WORLD", "PNGA_PGROUP_GOP", "PNGA_PGROUP_NNODES", "PNGA_PGROUP_NODEID", "PNGA_PGROUP_SET_DEFAULT", "PNGA_PGROUP_SPLIT", "PNGA_PGROUP_SPLIT_IRREF", "PNGA_PGROUP_SYNC", "PNGA_PRINT", "PNGA_PRINT_DISTRIBUTION", "PNGA_PRINT_FILE", "PNGA_PRINT_PATCH", "PNGA_PRINT_PATCH2D", "PNGA_PRINT_PATCH_FILE", "PNGA_PRINT_PATCH_FILE2D", "PNGA_PRINT_STATS", "PNGA_PROC_TOPOLOGY", "PNGA_PUT", "PNGA_PUT_FIELD", "PNGA_RANDOMIZE", "PNGA_READ_INC", "PNGA_RECIP", "PNGA_RECIP_PATCH", "PNGA_REGISTER_TYPE", "PNGA_RELEASE", "PNGA_RELEASE_BLOCK", "PNGA_RELEASE_BLOCK_GRID", "PNGA_RELEASE_BLOCK_SEGMENT", "PNGA_RELEASE_GHOST_ELEMENT", "PNGA_RELEASE_GHOSTS", "PNGA_RELEASE_UPDATE", "PNGA_RELEASE_UDPATE_BLOCK", "PNGA_RELEASE_UDPATE_BLOCK_GRID", "PNGA_RELEASE_UDPATE_BLOCK_SEGMENT", "PNGA_RELEASE_UDPATE_GHOST_ELEMENT", "PNGA_RELEASE_UDPATE_GHOSTS", "PNGA_SCALE", "PNGA_SCALE_COLS", "PNGA_SCALE_PATCH", "PNGA_SCALE_ROWS", "PNGA_SCAN_ADD", "PNGA_SCAN_COPY", "PNGA_SCATTER", "PNGA_SCATTER2D", "PNGA_SCATTER_ACC", "PNGA_SCATTER_ACC2D", "PNGA_SELECT_ELEM", "PNGA_SET_ARRAY_NAME", "PNGA_SET_BLOCK_CYCLIC", "PNGA_SET_BLOCK_CYCLIC_PROC_GRID", "PNGA_SET_CHUNK", "PNGA_SET_DATA", "PNGA_SET_DEBUG", "PNGA_SET_DIAGONAL", "PNGA_SET_GHOST_CORNER_FLAG", "PNGA_SET_GHOST_INFO", "PNGA_SET_GHOSTS", "PNGA_SET_IRREG_DISTR", "PNGA_SET_IRREG_FLAG", "PNGA_SET_MEMORY_LIMIT", "PNGA_SET_PGROUP", "PNGA_SET_RESTRICTED", "PNGA_SET_RESTRCITED_RANGE", "PNGA_SET_UPDATE4_INFO", "PNGA_SET_UPDATE5_INFO", "PNGA_SHIFT_DIAGONAL", "PNGA_SOLVE", "PNGA_SPD_INVERT", "PNGA_STEP_BOUND_INFO", "PNGA_STEP_BOUND_INFO_PATCH", "PNGA_STEP_MASK_PATCH", "PNGA_STEP_MAX", "PNGA_STEP_MAX_PATCH", "PNGA_STRIDED_ACC", "PNGA_STRIDED_GET", "PNGA_STRIDED_PUT", "PNGA_SUMMARIZE", "PNGA_SYMMETRIZE", "PNGA_SYNC", "PNGA_TERMINATE", "PNGA_TIMER", "PNGA_TOTAL_BLOCKS", "PNGA_TRANSPOSE", "PNGA_TYPE_C2F", "PNGA_TYPE_F2C", "PNGA_UNLOCK", "PNGA_UNPACK", "PNGA_UPDATE1_GHOSTS", "PNGA_UPDATE2_GHOSTS", "PNGA_UPDATE3_GHOSTS", "PNGA_UPDATED44_GHOSTS", "PNGA_UPDATE4_GHOSTS", "PNGA_UPDATE55_GHOSTS", "PNGA_UPDATE5_GHOSTS", "PNGA_UPDATE6_GHOSTS", "PNGA_UPDATE7_GHOSTS", "PNGA_UDPATE_GHOST_DIR", "PNGA_UPDATE_GHOSTS", "PNGA_USES_MA", "PNGA_USES_PROC_GRID", "PNGA_VALID_HANDLE", "PNGA_VERIFY_HANDLE", "PNGA_WTIME", "PNGA_ZERO", "PNGA_ZERO_DIAGONAL", "PNGA_ZERO_PATCH", "WPROF_TOTAL" }; #include #include #include #include #define NANOSECS 1000000000ULL uint64_t I_Wtime(void) { struct timespec res; clock_gettime(CLOCK_REALTIME, &res); uint64_t timeRes = res.tv_sec*NANOSECS+res.tv_nsec; return timeRes; } volatile enum FMT logs = HUMAN_FMT; FILE *filePre = NULL; FILE *globalFilePre = NULL; volatile int gaw_depth = 1; volatile int enable_local = 0; pthread_mutex_t gprof_lock = PTHREAD_MUTEX_INITIALIZER; int init_ga_prof_struct(int me, int nproc){ int i; pthread_mutex_lock(&gprof_lock); for(i = 0; i < WPROF_TOTAL; ++i){ strncpy(gaw_global_stats[i].name, ga_weak_symbols[i], 80); strncpy(gaw_local_stats[i].name, ga_weak_symbols[i], 80); gaw_global_stats[i].count = 0; gaw_global_stats[i].time = 0; gaw_global_stats[i].total_bytes = 0; gaw_local_stats[i].count = 0; gaw_local_stats[i].time = 0; gaw_local_stats[i].total_bytes = 0; } char *filePrefix = getenv("GAW_FILE_PREFIX"); char *strLogs = getenv("GAW_FMT"); char *Gdepth = getenv("GAW_DEPTH"); char *Glocal = getenv("GAW_ENABLE_LOCAL"); if(!strLogs){ /* Assume that the default is HUMAN readable */ } else{ int fm = atoi(strLogs); /* zero for CSV and one for HUMAN READABLE */ switch(fm){ case HUMAN_FMT: logs = HUMAN_FMT; break; case CSV_FMT: logs = CSV_FMT; break; default: logs = HUMAN_FMT; break; } } if(Glocal){ enable_local = 1; } if(!filePrefix){ filePrefix = alloca(1024); strncpy(filePrefix, "logs_pre", 1024); } char fname[1024]; if(enable_local){ if (logs == CSV_FMT) sprintf(fname, "%s-%d-%d.csv", filePrefix, me, nproc); else sprintf(fname, "%s-%d-%d.txt", filePrefix, me, nproc); filePre = fopen(fname, "wb+"); if(!filePre){ pthread_mutex_unlock(&gprof_lock); printf("Error in openning logs files: %s\n", fname); exit(11); } else{ printf("Log File openned: %s\n", fname); } } if(me == 0){ if (logs == CSV_FMT) sprintf(fname, "%s-GLOBAL.csv", filePrefix); else sprintf(fname, "%s-GLOBAL.txt", filePrefix); globalFilePre = fopen(fname, "wb+"); if(!globalFilePre){ pthread_mutex_unlock(&gprof_lock); printf("Error in openning logs files: %s\n", fname); exit(11); } else{ printf("Log File openned: %s\n", fname); } } pthread_mutex_unlock(&gprof_lock); } int update_local_entry(enum WPROF_GA e, uint64_t tme, uint64_t bytes){ if(e >= WPROF_TOTAL){ return -1; } pthread_mutex_lock(&gprof_lock); gaw_local_stats[e].count++; gaw_local_stats[e].time += tme; gaw_local_stats[e].total_bytes += bytes; pthread_mutex_unlock(&gprof_lock); return 0; } int update_global_entry(enum WPROF_GA e, MPI_Comm comm){ if(e >= WPROF_TOTAL){ return -1; } pthread_mutex_lock(&gprof_lock); MPI_Reduce(&gaw_local_stats[e].count, &gaw_global_stats[e].count, 1, MPI_LONG_LONG, MPI_SUM, 0, comm); MPI_Reduce(&gaw_local_stats[e].time, &gaw_global_stats[e].time, 1, MPI_LONG_LONG, MPI_SUM, 0, comm); MPI_Reduce(&gaw_local_stats[e].total_bytes, &gaw_global_stats[e].total_bytes, 1, MPI_LONG_LONG, MPI_SUM, 0, comm); MPI_Reduce(&gaw_local_stats[e].count, &gaw_global_min_stats[e].count, 1, MPI_LONG_LONG, MPI_MIN, 0, comm); MPI_Reduce(&gaw_local_stats[e].time, &gaw_global_min_stats[e].time, 1, MPI_LONG_LONG, MPI_MIN, 0, comm); MPI_Reduce(&gaw_local_stats[e].total_bytes, &gaw_global_min_stats[e].total_bytes, 1, MPI_LONG_LONG, MPI_MIN, 0, comm); MPI_Reduce(&gaw_local_stats[e].count, &gaw_global_max_stats[e].count, 1, MPI_LONG_LONG, MPI_MAX, 0, comm); MPI_Reduce(&gaw_local_stats[e].time, &gaw_global_max_stats[e].time, 1, MPI_LONG_LONG, MPI_MAX, 0, comm); MPI_Reduce(&gaw_local_stats[e].total_bytes, &gaw_global_max_stats[e].total_bytes, 1, MPI_LONG_LONG, MPI_MAX, 0, comm); pthread_mutex_unlock(&gprof_lock); return 0; } int print_ga_prof_stats(enum FMT f, FILE *fp, MPI_Comm comm){ int i; int me, nproc; MPI_Comm_rank(MPI_COMM_WORLD, &me); MPI_Comm_size(MPI_COMM_WORLD, &nproc); /* TODO: change the arguments, they are not longer needed */ f = logs; pthread_mutex_lock(&gprof_lock); if(enable_local){ fp = filePre; fprintf(fp, "Local stats\n"); switch(f) { case CSV_FMT: fprintf(fp, "Func Name, Node, Count, Time, Bytes\n"); break; case HUMAN_FMT: break; } for (i = 0; i < WPROF_TOTAL; ++i){ if(gaw_local_stats[i].count){ switch(f){ case CSV_FMT: fprintf(fp, "%s, %d, %"PRIu64", %"PRIu64", %"PRIu64"\n", gaw_local_stats[i].name, me, gaw_local_stats[i].count, gaw_local_stats[i].time, gaw_local_stats[i].total_bytes); break; case HUMAN_FMT: fprintf(fp, "[%d] Function Name: %s(%d)\n", me, gaw_local_stats[i].name, i); fprintf(fp, "[%d]\tCount: %"PRIu64"\n", me, gaw_local_stats[i].count); fprintf(fp, "[%d]\tCumm Time: %"PRIu64"\n", me, gaw_local_stats[i].time); fprintf(fp, "[%d]\tTotal Bytes: %"PRIu64"\n", me, gaw_local_stats[i].total_bytes); break; } } } } MPI_Barrier(comm); fp = globalFilePre; if(me == 0){ fprintf(fp, "Global Stats\n"); switch(f) { case CSV_FMT: fprintf(fp, "Func Name, Count, Avg Time (ms), Bytes, Avg Bytes, min Count, min Time, min Bytes, max Count, max Time, max Bytes\n"); break; case HUMAN_FMT: break; } for (i = 0; i < WPROF_TOTAL; ++i){ if(gaw_local_stats[i].count){ switch(f){ case CSV_FMT: fprintf(fp, "%s, %"PRIu64", %.3lf, %"PRIu64", %.3lf ,%"PRIu64", %"PRIu64", %"PRIu64", %"PRIu64", %"PRIu64", %"PRIu64"\n", gaw_global_stats[i].name, gaw_global_stats[i].count, ((double)(gaw_global_stats[i].time/(double)(nproc)))/1e6, gaw_global_stats[i].total_bytes, (double)(gaw_global_stats[i].total_bytes) / (double)(nproc), gaw_global_min_stats[i].count, gaw_global_min_stats[i].time, gaw_global_min_stats[i].total_bytes, gaw_global_max_stats[i].count, gaw_global_max_stats[i].time, gaw_global_max_stats[i].total_bytes); break; case HUMAN_FMT: fprintf(fp, "Function Name: %s\n", gaw_global_stats[i].name); fprintf(fp, "\tCount: %"PRIu64"\n", gaw_global_stats[i].count); fprintf(fp, "\tAverage Time: %.3lf\n", (double)(gaw_global_stats[i].time/(double)(nproc))); fprintf(fp, "\tTotal Bytes: %"PRIu64"\n", gaw_global_stats[i].total_bytes); fprintf(fp, "\tAverage Bytes: %.3lf\n", (gaw_global_stats[i].total_bytes/(double)nproc)); fprintf(fp, "\tMin Count: %"PRIu64"\n", gaw_global_min_stats[i].count); fprintf(fp, "\tMin Time: %"PRIu64"\n", gaw_global_min_stats[i].time); fprintf(fp, "\tMin Bytes: %"PRIu64"\n", gaw_global_min_stats[i].total_bytes); fprintf(fp, "\tMax Count: %"PRIu64"\n", gaw_global_max_stats[i].count); fprintf(fp, "\tMax Time: %"PRIu64"\n", gaw_global_max_stats[i].time); fprintf(fp, "\tMax Bytes: %"PRIu64"\n", gaw_global_max_stats[i].total_bytes); break; } } } } if(enable_local) fclose(filePre); if(me == 0) fclose(globalFilePre); pthread_mutex_unlock(&gprof_lock); } ga-5.9.2/tools/ga-wprof.h000066400000000000000000000167141500715745200151700ustar00rootroot00000000000000#ifndef _GA_WPRF #define _GA_WPRF 1 #include #include #include #include #include #include enum WPROF_GA { PNGA_ABS_VALUE = 0, PNGA_ABS_VALUE_PATCH, PNGA_ACC, PNGA_ACCESS_BLOCK_GRID_IDX, PNGA_ACCESS_BLOCK_GRID_PTR, PNGA_ACCESS_BLOCK_IDX, PNGA_ACCESS_BLOCK_PTR, PNGA_ACCESS_BLOCK_SEGMENT_IDX, PNGA_ACCESS_BLOCK_SEGMENT_PTR, PNGA_ACCESS_GHOST_ELEMENT, PNGA_ACCESS_GHOST_ELEMENT_PTR, PNGA_ACCESS_GHOST_PTR, PNGA_ACCESS_GHOSTS, PNGA_ACCESS_IDX, PNGA_ACCESS_PTR, PNGA_ADD, PNGA_ADD_CONSTANT, PNGA_ADD_CONSTANT_PATCH, PNGA_ADD_DIAGONAL, PNGA_ADD_PATCH, PNGA_ALLOCATE, PNGA_BIN_INDEX, PNGA_BIN_SORTER, PNGA_BRDCST, PNGA_CHECK_HANDLE, PNGA_CLUSTER_NNODES, PNGA_CLUSTER_NODEID, PNGA_CLUSTER_NPROCS, PNGA_CLUSTER_PROC_NODEID, PNGA_CLUSTER_PROCID, PNGA_COMP_PATCH, PNGA_COMPARE_DISTR, PNGA_COPY, PNGA_COPY_PATCH, PNGA_COPY_PATCH_DP, PNGA_CREATE, PNGA_CREATE_BIN_RANGE, PNGA_CREATE_CONFIG, PNGA_CREATE_GHOSTS, PNGA_CREATE_GHOSTS_CONFIG, PNGA_CREATE_GHOSTS_IRREG, PNGA_CREATE_GHOSTS_IRREG_CONFIG, PNGA_CREATE_HANDLE, PNGA_CREATE_IRREG, PNGA_CREATE_IRREG_CONFIG, PNGA_CREATE_CREATE_MUTEXES, PNGA_DDOT_PATCH_DP, PNGA_DEREGISTER_TYPE, PNGA_DESTROY, PNGA_DESTROY_MUTEXES, PNGA_DIAG, PNGA_DIAG_REUSE, PNGA_DIAG_SEQ, PNGA_DIAG_STD, PNGA_DIAG_STD_SEQ, PNGA_DISTRIBUTION, PNGA_DOT, PNGA_DOT_PATCH, PNGA_DUPLICATE, PNGA_ELEM_DIVIDE, PNGA_ELEM_DIVIDE_PATCH, PNGA_ELEM_MAXIMUM, PNGA_ELEM_MAXIMUM_PATCH, PNGA_ELEM_MINIMUM, PNGA_ELEM_MINIMUM_PATCH, PNGA_ELEM_MULTIPLY, PNGA_ELEM_MULTIPLY_PATCH, PNGA_ELEM_STEP_DIVIDE_PATCH, PNGA_ELEM_STEPB_DIVIDE_PATCH, PNGA_ERROR, PNGA_FENCE, PNGA_FILL, PNGA_FILL_PATCH, PNGA_GATHER, PNGA_GATHER2D, PNGA_GET, PNGA_GET_BLOCK_INFO, PNGA_GET_DEBUG, PNGA_GET_DIAG, PNGA_GET_DIMENSION, PNGA_GET_FIELD, PNGA_GET_GHOST_BLOCK, PNGA_GET_PGROUP, PNGA_GET_PGROUP_SIZE, PNGA_GET_PROC_GRID, PNGA_GET_PROC_INDEX, PNGA_GHOST_BARRIER, PNGA_GOP, PNGA_HAS_GHOSTS, PNGA_INIT_FENCE, PNGA_INITIALIZE, PNGA_INITIALIZE_LTD, PNGA_INQUIRE, PNGA_INQUIRE_MEMORY, PNGA_INQUIRE_NAME, PNGA_INQUIRE_TYPE, PNGA_IS_MIRRORED, PNGA_LIST_NODEID, PNGA_LLT_SOLVE, PNGA_LOCATE, PNGA_LOCATE_NNODES, PNGA_LOCATE_NUM_BLOCKS, PNGA_LOCATE_REGION, PNGA_LOCK, PNGA_LU_SOLVE, PNGA_LU_SOLVE_ALT, PNGA_LU_SOLVE_SEQ, PNGA_MASK_SYNC, PNGA_MATMUL, PNGA_MATMUL_MIRRORED, PNGA_MATMUL_PATCH, PNGA_MEDIAN, PNGA_MEDIAN_PATCH, PNGA_MEMORY_AVAIL, PNGA_MEMORY_AVAIL_TYPE, PNGA_MEMORY_LIMITED, PNGA_MERGE_DISTR_PATCH, PNGA_MERGE_MIRRORED, PNGA_MSG_BRDCST, PNGA_MSG_PGROUP_SYNC, PNGA_MSG_SYNC, PNGA_NBACC, PNGA_NBGET, PNGA_NBGET_FIELD, PNGA_NBGET_GHOST_DIR, PNGA_NBLOCK, PNGA_NBPUT, PNGA_NBPUT_FIELD, PNGA_NBTEST, PNGA_NBWAIT, PNGA_NDIM, PNGA_NNODES, PNGA_NODEID, PNGA_NORM1, PNGA_NORM_INFINITY, PNGA_OVERLAY, PNGA_PACK, PNGA_PATCH_ENUM, PNGA_PATCH_INTERSECT, PNGA_PERIODIC, PNGA_PGROUP_ABSOLUTE_ID, PNGA_PGROUP_BRDCST, PNGA_PGROUP_CREATE, PNGA_PGROUP_DESTROY, PNGA_PGROUP_GET_DEFAULT, PNGA_PGROUP_GET_MIRROR, PNGA_PGROUP_GET_WORLD, PNGA_PGROUP_GOP, PNGA_PGROUP_NNODES, PNGA_PGROUP_NODEID, PNGA_PGROUP_SET_DEFAULT, PNGA_PGROUP_SPLIT, PNGA_PGROUP_SPLIT_IRREG, PNGA_PGROUP_SYNC, PNGA_PRINT, PNGA_PRINT_DISTRIBUTION, PNGA_PRINT_FILE, PNGA_PRINT_PATCH, PNGA_PRINT_PATCH2D, PNGA_PRINT_PATCH_FILE, PNGA_PRINT_PATCH_FILE2D, PNGA_PRINT_STATS, PNGA_PROC_TOPOLOGY, PNGA_PUT, PNGA_PUT_FIELD, PNGA_RANDOMIZE, PNGA_READ_INC, PNGA_RECIP, PNGA_RECIP_PATCH, PNGA_REGISTER_TYPE, PNGA_RELEASE, PNGA_RELEASE_BLOCK, PNGA_RELEASE_BLOCK_GRID, PNGA_RELEASE_BLOCK_SEGMENT, PNGA_RELEASE_GHOST_ELEMENT, PNGA_RELEASE_GHOSTS, PNGA_RELEASE_UDPATE, PNGA_RELEASE_UDPATE_BLOCK, PNGA_RELEASE_UDPATE_BLOCK_GRID, PNGA_RELEASE_UDPATE_BLOCK_SEGMENT, PNGA_RELEASE_UDPATE_GHOST_ELEMENT, PNGA_RELEASE_UDPATE_GHOSTS, PNGA_SCALE, PNGA_SCALE_COLS, PNGA_SCALE_PATCH, PNGA_SCALE_ROWS, PNGA_SCAN_ADD, PNGA_SCAN_COPY, PNGA_SCATTER, PNGA_SCATTER2D, PNGA_SCATTER_ACC, PNGA_SCATTER_ACC2D, PNGA_SELECT_ELEM, PNGA_SET_ARRAY_NAME, PNGA_SET_BLOCK_CYCLIC, PNGA_SET_BLOCK_CYCLIC_PROC_GRID, PNGA_SET_CHUNK, PNGA_SET_DATA, PNGA_SET_DEBUG, PNGA_SET_DIAGONAL, PNGA_SET_GHOST_CORNER_FLAG, PNGA_SET_GHOST_INFO, PNGA_SET_GHOSTS, PNGA_SET_IRREG_DISTR, PNGA_SET_IRREG_FLAG, PNGA_SET_MEMORY_LIMIT, PNGA_SET_PGROUP, PNGA_SET_RESTRICTED, PNGA_SET_RESTRICTED_RANGE, PNGA_SET_UPDATE4_INFO, PNGA_SET_UPDATE5_INFO, PNGA_SHIFT_DIAGONAL, PNGA_SOLVE, PNGA_SPD_INVERT, PNGA_STEP_BOUND_INFO, PNGA_STEP_BOUND_INFO_PATCH, PNGA_STEP_MASK_PATCH, PNGA_STEP_MAX, PNGA_STEP_MAX_PATCH, PNGA_STRIDED_ACC, PNGA_STRIDED_GET, PNGA_STRIDED_PUT, PNGA_SUMMARIZE, PNGA_SYMMETRIZE, PNGA_SYNC, PNGA_TERMINATE, PNGA_TIMER, PNGA_TOTAL_BLOCKS, PNGA_TRANSPOSE, PNGA_TYPE_C2F, PNGA_TYPE_F2C, PNGA_UNLOCK, PNGA_UNPACK, PNGA_UPDATE1_GHOSTS, PNGA_UPDATE2_GHOSTS, PNGA_UPDATE3_GHOSTS, PNGA_UPDATED44_GHOSTS, PNGA_UPDATE4_GHOSTS, PNGA_UPDATE55_GHOSTS, PNGA_UPDATE5_GHOSTS, PNGA_UPDATE6_GHOSTS, PNGA_UPDATE7_GHOSTS, PNGA_UPDATE_GHOST_DIR, PNGA_UPDATE_GHOSTS, PNGA_USES_MA, PNGA_USES_PROC_GRID, PNGA_VALID_HANDLE, PNGA_VERIFY_HANDLE, PNGA_WTIME, PNGA_ZERO, PNGA_ZERO_DIAGONAL, PNGA_ZERO_PATCH, WPROF_TOTAL }; #include "ga.h" #include "globalp.h" #include "base.h" #define GET_SIZE(ga, lo, hi, elems) { \ Integer handle, size, nelems, ndim; \ handle = GA_OFFSET + ga; \ size = GA[handle].elemsize; \ ndim = GA[handle].ndim; \ gam_CountElems(ndim, lo, hi, &nelems);\ elems = size * nelems; \ } #define GET_LOCAL_MSIZE(ga, sz) {\ Integer handle; \ handle = GA_OFFSET + ga; \ sz = GA[handle].size; \ } #define OBTAIN_ESIZE(r, ga) { \ Integer handle = GA_OFFSET+ga;\ r = GA[handle].elemsize;\ } #define OBTAIN_SIZE(r, t) { \ switch(t){ \ case C_INT: \ r *= sizeof(int); \ break; \ case C_LONG: \ r *= sizeof(long); \ break; \ case C_LONGLONG: \ r *= sizeof(long long); \ break; \ case C_FLOAT: \ r *= sizeof(float);\ break; \ case C_DBL: \ r *= sizeof(double);\ break; \ case C_SCPL: \ r *= sizeof(float) * 2;\ break; \ case C_DCPL: \ r *= sizeof(double) * 2;\ break; \ default: \ r *= 1; \ break; \ } \ } enum FMT { CSV_FMT, HUMAN_FMT }; typedef struct __ga_weak_profile{ char name[80]; /* Name of the function */ uint64_t count; /* Counting of event */ uint64_t time; /* Nanoseconds timer */ uint64_t total_bytes; /* Moved / allocated /touched bytes */ } ga_weak_profile_t; extern ga_weak_profile_t gaw_global_stats[]; extern ga_weak_profile_t gaw_global_min_stats[]; extern ga_weak_profile_t gaw_global_max_stats[]; extern ga_weak_profile_t gaw_local_stats[]; extern char ga_weak_symbols[][80]; int init_ga_prof_struct(int, int); int print_ga_prof_stats(enum FMT f, FILE *fp, MPI_Comm comm); int update_local_entry(enum WPROF_GA e, uint64_t tme, uint64_t bytes); int update_global_entry(enum WPROF_GA e, MPI_Comm comm); uint64_t I_Wtime(); #endif ga-5.9.2/tools/ga_papi_to_wapi.cmake000066400000000000000000000007511500715745200174130ustar00rootroot00000000000000if (INPUT) if (OUTPUT) message(STATUS "Reading ${INPUT} ...") FILE(READ "${INPUT}" in0) string(REPLACE "PAPI" "WAPI" in1 "${in0}") string(REPLACE "pnga_" "wnga_" in2 "${in1}") string(REGEX REPLACE "typedef[^;]*;" "" in3 "${in2}") message(STATUS "Writing ${OUTPUT} ...") FILE(WRITE "${OUTPUT}" "${in3}") else (OUTPUT) message(ERROR "OUTPUT variable must be set") endif (OUTPUT) else (INPUT) message(ERROR "INPUT variable must be set") endif (INPUT) ga-5.9.2/tools/ga_papi_to_wapi.pl000077500000000000000000000002421500715745200167440ustar00rootroot00000000000000#!/usr/bin/env perl while () { $line = $_; $line =~ s/PAPI/WAPI/g; $line =~ s/pnga_/wnga_/g; if (!($line =~ /typedef/)) { print "$line"; } } ga-5.9.2/tools/ga_papi_to_wapidefs.cmake000066400000000000000000000021711500715745200202530ustar00rootroot00000000000000if (INPUT) if (OUTPUT) message(STATUS "Reading ${INPUT} ...") file(READ "${INPUT}" in0) string(REGEX REPLACE "\r?\n" ";" lines "${in0}") set(out "") foreach (l ${lines}) string(REGEX MATCH "p(nga_.*) *\\(" found "${l}") if (found) set(l "#define w${CMAKE_MATCH_1} p${CMAKE_MATCH_1}") endif (found) string(REGEX MATCH "PAPI" found "${l}") if (found) string(REPLACE "PAPI" "WAPIDEFS" l "${l}") endif (found) # remove comments string(REGEX REPLACE "/\\*..*\\*/" "" l "${l}") # ignore lines with leading whitespace string(REGEX MATCH "^ *" found "${l}") if (found) set(l "") endif () # ignore typedef lines string(REGEX MATCH "typedef" found "${l}") if (found) set(l "") endif () if (l) set(out "${out}${l}\n") endif (l) endforeach () message(STATUS "Writing ${OUTPUT} ...") file(WRITE "${OUTPUT}" "${out}") else (OUTPUT) message(ERROR "OUTPUT variable must be set") endif (OUTPUT) else (INPUT) message(ERROR "INPUT variable must be set") endif (INPUT) ga-5.9.2/tools/ga_papi_to_wapidefs.pl000077500000000000000000000006401500715745200176100ustar00rootroot00000000000000#!/usr/bin/env perl while () { $line = $_; if ($line =~ /.*p(nga_.*) *\(/) { $line = "#define w$1 p$1\n"; } if ($line =~ /PAPI/) { $line =~ s/PAPI/WAPIDEFS/g; } if ($line =~ /^ /) { $line = ""; } if ($line =~ /^\/\*/) { $line = ""; } if ($line =~ /^ \*\\/) { $line = ""; } if ($line =~ /typedef/) { $line = ""; } if ($line ne "") { print "$line"; } } ga-5.9.2/tools/gp-wapi.c000066400000000000000000000030701500715745200147740ustar00rootroot00000000000000 #if HAVE_CONFIG_H # include "config.h" #endif #include "gp-papi.h" #include "typesf2c.h" logical wgp_allocate(Integer g_p) { return pgp_allocate(g_p); } void wgp_assign_local_element(Integer g_p, Integer *subscript, void *ptr, Integer size) { pgp_assign_local_element(g_p, subscript, ptr, size); } Integer wgp_create_handle() { return pgp_create_handle(); } void wgp_debug(Integer g_p) { pgp_debug(g_p); } logical wgp_destroy(Integer g_p) { return pgp_destroy(g_p); } void wgp_distribution(Integer g_p, Integer proc, Integer *lo, Integer *hi) { pgp_distribution(g_p, proc, lo, hi); } void wgp_free(void *ptr) { pgp_free(ptr); } void* wgp_free_local_element(Integer g_p, Integer *subscript) { pgp_free_local_element(g_p, subscript); } void wgp_get(Integer g_p, Integer *lo, Integer *hi, void *buf, void **buf_ptr, Integer *ld, void *buf_size, Integer *ld_sz, Integer *size, Integer isize) { pgp_get(g_p, lo, hi, buf, buf_ptr, ld, buf_size, ld_sz, size, isize); } Integer wgp_get_dimension(Integer g_p) { return pgp_get_dimension(g_p); } void wgp_get_size(Integer g_p, Integer *lo, Integer *hi, Integer *size, Integer isize) { pgp_get_size(g_p, lo, hi, size, isize); } void wgp_initialize() { pgp_initialize(); } void* wgp_malloc(size_t size) { pgp_malloc(size); } void wgp_set_chunk(Integer g_p, Integer *chunk) { pgp_set_chunk(g_p, chunk); } void wgp_set_dimensions(Integer g_p, Integer ndim, Integer *dims) { pgp_set_dimensions(g_p, ndim, dims); } void wgp_terminate() { pgp_terminate(); } ga-5.9.2/tools/gpwapigen.py000077500000000000000000000074001500715745200156230ustar00rootroot00000000000000#!/usr/bin/env python '''Generate the gp-wapi.c source from the gp-papi.h header.''' import sys def get_signatures(header): # first, gather all function signatures from gp-papi.h aka argv[1] accumulating = False signatures = [] current_signature = '' EXTERN = 'extern' SEMICOLON = ';' for line in open(header): line = line.strip() # remove whitespace before and after line if not line: continue # skip blank lines if EXTERN in line and SEMICOLON in line: signatures.append(line) elif EXTERN in line: current_signature = line accumulating = True elif SEMICOLON in line and accumulating: current_signature += line signatures.append(current_signature) accumulating = False elif accumulating: current_signature += line return signatures class FunctionArgument(object): def __init__(self, signature): self.pointer = signature.count('*') self.array = '[' in signature signature = signature.replace('*','').strip() signature = signature.replace('[','').strip() signature = signature.replace(']','').strip() self.type,self.name = signature.split() def __str__(self): ret = self.type[:] ret += ' ' for p in range(self.pointer): ret += '*' ret += self.name if self.array: ret += '[]' return ret class Function(object): def __init__(self, signature): signature = signature.replace('extern','').strip() self.return_type,signature = signature.split(None,1) self.return_type = self.return_type.strip() signature = signature.strip() self.name,signature = signature.split('(',1) self.name = self.name.strip() signature = signature.replace(')','').strip() signature = signature.replace(';','').strip() self.args = [] if signature: for arg in signature.split(','): self.args.append(FunctionArgument(arg.strip())) def get_call(self, name=None): sig = '' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += arg.name sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def get_signature(self, name=None): sig = self.return_type[:] sig += ' ' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += str(arg) sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def __str__(self): return self.get_signature() if __name__ == '__main__': if len(sys.argv) != 2: print 'incorrect number of arguments' print 'usage: gpwapigen.py > ' sys.exit(len(sys.argv)) # print headers print ''' #if HAVE_CONFIG_H # include "config.h" #endif #include "gp-papi.h" #include "typesf2c.h" ''' functions = {} # parse signatures into the Function class for sig in get_signatures(sys.argv[1]): function = Function(sig) functions[function.name] = function # now process the functions for name in sorted(functions): func = functions[name] maybe_return = '' if 'void' not in func.return_type: maybe_return = 'return ' func = functions[name] wnga_name = name.replace('pgp_','wgp_') print ''' %s { %s%s; } ''' % (func.get_signature(wnga_name), maybe_return, func.get_call()) ga-5.9.2/tools/wapigen.py000077500000000000000000000074001500715745200152740ustar00rootroot00000000000000#!/usr/bin/env python '''Generate the ga-wapi.c source from the ga-papi.h header.''' import sys def get_signatures(header): # first, gather all function signatures from ga-papi.h aka argv[1] accumulating = False signatures = [] current_signature = '' EXTERN = 'extern' SEMICOLON = ';' for line in open(header): line = line.strip() # remove whitespace before and after line if not line: continue # skip blank lines if EXTERN in line and SEMICOLON in line: signatures.append(line) elif EXTERN in line: current_signature = line accumulating = True elif SEMICOLON in line and accumulating: current_signature += line signatures.append(current_signature) accumulating = False elif accumulating: current_signature += line return signatures class FunctionArgument(object): def __init__(self, signature): self.pointer = signature.count('*') self.array = '[' in signature signature = signature.replace('*','').strip() signature = signature.replace('[','').strip() signature = signature.replace(']','').strip() self.type,self.name = signature.split() def __str__(self): ret = self.type[:] ret += ' ' for p in range(self.pointer): ret += '*' ret += self.name if self.array: ret += '[]' return ret class Function(object): def __init__(self, signature): signature = signature.replace('extern','').strip() self.return_type,signature = signature.split(None,1) self.return_type = self.return_type.strip() signature = signature.strip() self.name,signature = signature.split('(',1) self.name = self.name.strip() signature = signature.replace(')','').strip() signature = signature.replace(';','').strip() self.args = [] if signature: for arg in signature.split(','): self.args.append(FunctionArgument(arg.strip())) def get_call(self, name=None): sig = '' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += arg.name sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def get_signature(self, name=None): sig = self.return_type[:] sig += ' ' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += str(arg) sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def __str__(self): return self.get_signature() if __name__ == '__main__': if len(sys.argv) != 2: print 'incorrect number of arguments' print 'usage: wapigen.py > ' sys.exit(len(sys.argv)) # print headers print ''' #if HAVE_CONFIG_H # include "config.h" #endif #include "ga-papi.h" #include "typesf2c.h" ''' functions = {} # parse signatures into the Function class for sig in get_signatures(sys.argv[1]): function = Function(sig) functions[function.name] = function # now process the functions for name in sorted(functions): func = functions[name] maybe_return = '' if 'void' not in func.return_type: maybe_return = 'return ' func = functions[name] wnga_name = name.replace('pnga_','wnga_') print ''' %s { %s%s; } ''' % (func.get_signature(wnga_name), maybe_return, func.get_call()) ga-5.9.2/tools/wapigen_counts.py000077500000000000000000000113011500715745200166620ustar00rootroot00000000000000#!/usr/bin/env python '''Generate the wapi_counts.c source from the ga-papi.h header.''' import sys def get_signatures(header): # first, gather all function signatures from ga-papi.h aka argv[1] accumulating = False signatures = [] current_signature = '' EXTERN = 'extern' SEMICOLON = ';' for line in open(header): line = line.strip() # remove whitespace before and after line if not line: continue # skip blank lines if EXTERN in line and SEMICOLON in line: signatures.append(line) elif EXTERN in line: current_signature = line accumulating = True elif SEMICOLON in line and accumulating: current_signature += line signatures.append(current_signature) accumulating = False elif accumulating: current_signature += line return signatures class FunctionArgument(object): def __init__(self, signature): self.pointer = signature.count('*') self.array = '[' in signature signature = signature.replace('*','').strip() signature = signature.replace('[','').strip() signature = signature.replace(']','').strip() self.type,self.name = signature.split() def __str__(self): ret = self.type[:] ret += ' ' for p in range(self.pointer): ret += '*' ret += self.name if self.array: ret += '[]' return ret class Function(object): def __init__(self, signature): signature = signature.replace('extern','').strip() self.return_type,signature = signature.split(None,1) self.return_type = self.return_type.strip() signature = signature.strip() self.name,signature = signature.split('(',1) self.name = self.name.strip() signature = signature.replace(')','').strip() signature = signature.replace(';','').strip() self.args = [] if signature: for arg in signature.split(','): self.args.append(FunctionArgument(arg.strip())) def get_call(self, name=None): sig = '' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += arg.name sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def get_signature(self, name=None): sig = self.return_type[:] sig += ' ' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += str(arg) sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def __str__(self): return self.get_signature() if __name__ == '__main__': if len(sys.argv) != 2: print 'incorrect number of arguments' print 'usage: wapigen_counts.py > ' sys.exit(len(sys.argv)) # print headers print ''' #if HAVE_CONFIG_H # include "config.h" #endif #include "ga-papi.h" #include "typesf2c.h" ''' functions = {} # parse signatures into the Function class for sig in get_signatures(sys.argv[1]): function = Function(sig) functions[function.name] = function # for each function, generate a static count for name in sorted(functions): print 'static long count_%s = 0;' % name print '' # now process the functions for name in sorted(functions): func = functions[name] if 'terminate' in name: continue maybe_return = '' if 'void' not in func.return_type: maybe_return = 'return ' func = functions[name] wnga_name = name.replace('pnga_','wnga_') print ''' %s { ++count_%s; %s%s; } ''' % (func.get_signature(wnga_name), name, maybe_return, func.get_call()) # prepare to output the terminate function name = 'pnga_terminate' wnga_name = name.replace('pnga_','wnga_') func = functions[name] the_code = '' # establish 'the_code' to use in the body of terminate # it's printing the count of each function if it was called at least once for name in sorted(functions): the_code += ''' if (count_%s) { printf("%s %%ld\\n", count_%s); } ''' % (name, name, name) # output the terminate function print '''%s { ++count_pnga_terminate; %s; /* don't dump info if terminate more than once */ if (1 == count_pnga_terminate) { %s } } ''' % (func.get_signature(wnga_name), func.get_call(), the_code) ga-5.9.2/tools/wapigen_timers.py000077500000000000000000000152371500715745200166660ustar00rootroot00000000000000#!/usr/bin/env python '''Generate the wapi_times.c source from the ga-papi.h header.''' import sys def get_signatures(header): # first, gather all function signatures from ga-papi.h aka argv[1] accumulating = False signatures = [] current_signature = '' EXTERN = 'extern' SEMICOLON = ';' for line in open(header): line = line.strip() # remove whitespace before and after line if not line: continue # skip blank lines if EXTERN in line and SEMICOLON in line: signatures.append(line) elif EXTERN in line: current_signature = line accumulating = True elif SEMICOLON in line and accumulating: current_signature += line signatures.append(current_signature) accumulating = False elif accumulating: current_signature += line return signatures class FunctionArgument(object): def __init__(self, signature): self.pointer = signature.count('*') self.array = '[' in signature signature = signature.replace('*','').strip() signature = signature.replace('[','').strip() signature = signature.replace(']','').strip() self.type,self.name = signature.split() def __str__(self): ret = self.type[:] ret += ' ' for p in range(self.pointer): ret += '*' ret += self.name if self.array: ret += '[]' return ret class Function(object): def __init__(self, signature): signature = signature.replace('extern','').strip() self.return_type,signature = signature.split(None,1) self.return_type = self.return_type.strip() signature = signature.strip() self.name,signature = signature.split('(',1) self.name = self.name.strip() signature = signature.replace(')','').strip() signature = signature.replace(';','').strip() self.args = [] if signature: for arg in signature.split(','): self.args.append(FunctionArgument(arg.strip())) def get_call(self, name=None): sig = '' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += arg.name sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def get_signature(self, name=None): sig = self.return_type[:] sig += ' ' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += str(arg) sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def __str__(self): return self.get_signature() if __name__ == '__main__': if len(sys.argv) != 2: print 'incorrect number of arguments' print 'usage: wapigen_counts.py > ' sys.exit(len(sys.argv)) # print headers print ''' #if HAVE_CONFIG_H # include "config.h" #endif #include #include "ga-papi.h" #include "typesf2c.h" static MPI_Comm comm; static int me; static int nproc; static double global_start = 0; static double global_stop = 0; ''' functions = {} # parse signatures into the Function class for sig in get_signatures(sys.argv[1]): function = Function(sig) functions[function.name] = function # for each function, generate a static count for name in sorted(functions): print 'static long count_%s = 0;' % name print '' # for each function, generate a static time for name in sorted(functions): print 'static double time_%s = 0;' % name print '' # now process the functions for name in sorted(functions): func = functions[name] if 'terminate' in name: continue if 'initialize' in name and 'initialized' not in name: continue func = functions[name] wnga_name = name.replace('pnga_','wnga_') if 'void' not in func.return_type: print ''' %s { %s return_value; double local_start, local_stop; ++count_%s; local_start = MPI_Wtime(); return_value = %s; local_stop = MPI_Wtime(); time_%s += local_stop - local_start; return return_value; } ''' % (func.get_signature(wnga_name), func.return_type, name, func.get_call(), name) else: print ''' %s { double local_start, local_stop; ++count_%s; local_start = MPI_Wtime(); %s; local_stop = MPI_Wtime(); time_%s += local_stop - local_start; } ''' % (func.get_signature(wnga_name), name, func.get_call(), name) # prepare to output the initialize function name = 'pnga_initialize' wnga_name = name.replace('pnga_','wnga_') func = functions[name] # output the initialize function print '''%s { ++count_pnga_initialize; global_start = MPI_Wtime(); %s; MPI_Comm_dup(GA_MPI_Comm(), &comm); MPI_Comm_rank(comm, &me); MPI_Comm_size(comm, &nproc); } ''' % (func.get_signature(wnga_name), func.get_call()) # prepare to output the initialize_ltd function name = 'pnga_initialize_ltd' wnga_name = name.replace('pnga_','wnga_') func = functions[name] # output the initialize_ltd function print '''%s { ++count_pnga_initialize_ltd; global_start = MPI_Wtime(); %s; MPI_Comm_dup(GA_MPI_Comm(), &comm); MPI_Comm_rank(comm, &me); MPI_Comm_size(comm, &nproc); } ''' % (func.get_signature(wnga_name), func.get_call()) # prepare to output the terminate function name = 'pnga_terminate' wnga_name = name.replace('pnga_','wnga_') func = functions[name] the_code = '' # establish 'the_code' to use in the body of terminate # it's printing the count of each function if it was called at least once the_code += ''' double recvbuf = 0.0; ''' for name in sorted(functions): the_code += ''' MPI_Reduce(&time_%s, &recvbuf, 1, MPI_DOUBLE, MPI_SUM, 0, comm); if (me == 0) { printf("%s,%%ld,%%lf\\n", count_%s, recvbuf); } ''' % (name, name, name) # output the terminate function print '''%s { ++count_pnga_terminate; %s; global_stop = MPI_Wtime(); /* don't dump info if terminate more than once */ if (1 == count_pnga_terminate) { %s if (me == 0) { printf("global_stop-global_start,0,%%lf\\n", global_stop-global_start); } } MPI_Comm_free(&comm); } ''' % (func.get_signature(wnga_name), func.get_call(), the_code) ga-5.9.2/tools/wapigen_trace.py000077500000000000000000000414371500715745200164620ustar00rootroot00000000000000#!/usr/bin/env python '''Generate the wapi_trace.c source from the ga-papi.h header.''' import sys def get_signatures(header): # first, gather all function signatures from ga-papi.h aka argv[1] accumulating = False signatures = [] current_signature = '' EXTERN = 'extern' SEMICOLON = ';' for line in open(header): line = line.strip() # remove whitespace before and after line if not line: continue # skip blank lines if EXTERN in line and SEMICOLON in line: signatures.append(line) elif EXTERN in line: current_signature = line accumulating = True elif SEMICOLON in line and accumulating: current_signature += line signatures.append(current_signature) accumulating = False elif accumulating: current_signature += line return signatures format_from_type = { "char": "%c", "int": "%d", "long": "%ld", "long long": "%lld", "float": "%f", "double": "%lf", "Integer": "%ld", "logical": "%ld", } intp_names = [ 'lo', 'alo', 'blo', 'clo', 'hi', 'ahi', 'bhi', 'chi', 'subscript', 'dims', 'ld', 'width', 'chunk', 'map', 'block', ] class FunctionArgument(object): def __init__(self, signature): self.pointer = signature.count('*') self.array = '[' in signature signature = signature.replace('*','').strip() signature = signature.replace('[','').strip() signature = signature.replace(']','').strip() self.type,self.name = signature.split() self.intp = ((self.pointer == 1 or self.array) and self.type == 'Integer' and self.name in intp_names) def __str__(self): ret = self.type[:] ret += ' ' for p in range(self.pointer): ret += '*' ret += self.name if self.array: ret += '[]' return ret class Function(object): def __init__(self, signature): signature = signature.replace('extern','').strip() self.return_type,signature = signature.split(None,1) self.return_type = self.return_type.strip() signature = signature.strip() self.name,signature = signature.split('(',1) self.name = self.name.strip() signature = signature.replace(')','').strip() signature = signature.replace(';','').strip() self.args = [] if signature: for arg in signature.split(','): self.args.append(FunctionArgument(arg.strip())) def get_call(self, name=None): sig = '' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += arg.name sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def get_signature(self, name=None): sig = self.return_type[:] sig += ' ' if not name: sig += self.name else: sig += name sig += '(' if self.args: for arg in self.args: sig += str(arg) sig += ', ' sig = sig[:-2] # remove last ', ' sig += ')' return sig def args_have_nv(self): for arg in self.args: if arg.name == 'nv' and not arg.pointer: return True return False def args_have_ga(self): for arg in self.args: if arg.name == 'g_a' and not arg.pointer: return True return False def args_have_alpha(self): for arg in self.args: if arg.name == 'alpha': return True return False def args_have_beta(self): for arg in self.args: if arg.name == 'beta': return True return False def args_have_ndim(self): for arg in self.args: if 'ndim' in arg.name and not arg.pointer: return True return False def get_ndim_name(self): if 'set_irreg' in self.name: return 'pnga_set_data_last_ndim' else: for arg in self.args: if 'ndim' in arg.name and not arg.pointer: return arg.name return '_ndim' def args_have_intp(self): for arg in self.args: if arg.intp: return True return False def get_tracer_body(self): slot = 0 tracer = '' if self.args_have_intp() and 'set_irreg' not in self.name: if not self.args_have_ndim() and self.args_have_ga(): tracer += ' int _ndim = pnga_ndim(g_a);\n' if self.args_have_alpha(): tracer += ' Integer _atype;\n' if self.args_have_beta(): tracer += ' Integer _btype;\n' if self.args_have_alpha(): tracer += ' pnga_inquire_type(g_a, &_atype);\n' if self.args_have_beta(): tracer += ' pnga_inquire_type(g_b, &_btype);\n' if 'void' not in func.return_type: tracer += ' %s retval;\n' % self.return_type pf,slot = self.get_tracer_printf(False,slot) tracer += ' %s;\n' % pf tracer += ' ' if 'void' not in func.return_type: tracer += 'retval = ' tracer += '%s;\n' % self.get_call() pf,slot = self.get_tracer_printf(True,slot) tracer += ' %s;\n' % pf if 'void' not in func.return_type: tracer += ' return retval;\n' return tracer def get_tracer_printf(self, end, slot): tracer = 'fprintf(fptrace, "%lf,' if end: tracer += '/' tracer += self.name + ',' if self.args: tracer += '(' for arg in self.args: if arg.pointer == 1 and 'char' in arg.type: tracer += '%s;' elif arg.name == 'alpha' or arg.name == 'beta': tracer += '%s;' elif arg.intp: tracer += '%s;' elif arg.pointer or arg.array: tracer += '%p;' else: tracer += '%s;' % format_from_type[arg.type] tracer = tracer[:-1] tracer += ')' tracer += '\\n",MPI_Wtime()-first_wtime' if self.args: for arg in self.args: if arg.intp: #if self.args_have_nv(): # tracer += ',expand_intp(%s*nv,%s,%d)' % ( # self.get_ndim_name(), arg.name, slot) if 'map' in arg.name and 'locate_region' not in self.name: tracer += ',expand_intp(sum_intp(%s,block),%s,%d)' % ( self.get_ndim_name(), arg.name, slot) elif 'ld' in arg.name: tracer += ',expand_intp(%s-1,%s,%d)' % ( self.get_ndim_name(), arg.name, slot) else: tracer += ',expand_intp(%s,%s,%d)' % ( self.get_ndim_name(), arg.name, slot) slot += 1 elif 'alpha' in arg.name: tracer += ',expand_voidp(_atype,alpha,%d)' % slot slot += 1 elif 'beta' in arg.name: tracer += ',expand_voidp(_btype,beta,%d)' % slot slot += 1 else: tracer += ',%s' % arg.name tracer += ')' return tracer,slot def __str__(self): return self.get_signature() if __name__ == '__main__': if len(sys.argv) != 2: print 'incorrect number of arguments' print 'usage: wapigen_trace.py > ' sys.exit(len(sys.argv)) # print headers and other static stuff print ''' #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include "ga-papi.h" #include "typesf2c.h" FILE *fptrace=NULL; double first_wtime; int me, nproc; MPI_Comm comm; #if HAVE_PROGNAME extern const char * PROGNAME; #endif #define LOHI_BUFSIZE 200 #define LOHI_SLOTS 20 static char **lohi_bufs; static int pnga_set_data_last_ndim=-1; static Integer sum_intp(int ndim, Integer *lohi) { Integer sum=0; assert(ndim>=0); if (NULL != lohi) { int i; for (i=0; i= 0 && slot < LOHI_SLOTS); str = lohi_bufs[slot]; current_str = str; if (NULL == value) { size_written = sprintf(current_str, "NULL"); total_written += size_written; assert(size_written > 0); assert(total_written < LOHI_BUFSIZE); current_str += size_written; } else { switch (type) { case C_INT: size_written = sprintf(current_str, "%d", *((int*)value)); break; case C_LONG: size_written = sprintf(current_str, "%ld", *((long*)value)); break; case C_LONGLONG:size_written = sprintf(current_str, "%lld", *((long long*)value)); break; case C_FLOAT: size_written = sprintf(current_str, "%f", *((float*)value)); break; case C_DBL: size_written = sprintf(current_str, "%lf", *((double*)value)); break; case C_SCPL: size_written = sprintf(current_str, "%f#%f", ((SingleComplex*)value)->real, ((SingleComplex*)value)->imag); break; case C_DCPL: size_written = sprintf(current_str, "%lf#%lf", ((DoubleComplex*)value)->real, ((DoubleComplex*)value)->imag); break; } total_written += size_written; assert(size_written > 0); assert(total_written < LOHI_BUFSIZE); current_str += size_written; } return str; } static char* expand_intp(int ndim, Integer *lohi, int slot) { char *str=NULL, *current_str=NULL; int i; int size_written; int total_written = 0; assert(ndim>=0); assert(slot >= 0 && slot < LOHI_SLOTS); str = lohi_bufs[slot]; current_str = str; if (NULL == lohi) { size_written = sprintf(current_str, "{NULL}"); total_written += size_written; assert(size_written > 0); assert(total_written < LOHI_BUFSIZE); current_str += size_written; } else if (0 == ndim) { size_written = sprintf(current_str, "{}"); total_written += size_written; assert(size_written > 0); assert(total_written < LOHI_BUFSIZE); current_str += size_written; } else { size_written = sprintf(current_str, "{%ld", lohi[0]); total_written += size_written; assert(size_written > 0); assert(total_written < LOHI_BUFSIZE); current_str += size_written; for (i=1; i 0); assert(total_written < LOHI_BUFSIZE); current_str += size_written; } size_written = sprintf(current_str, "}\\0"); total_written += size_written; assert(size_written > 0); assert(total_written < LOHI_BUFSIZE); current_str += size_written; } return str; } static void init_lohi_bufs() { int i; lohi_bufs = (char**)malloc(sizeof(char*)*LOHI_SLOTS); for (i=0; i&1 |head -1 |cut -d ' ' -f 4 |cut -d . -f 1) if [[ $( [ $xcode_v -ge 15 ] && echo 1) ]] ; then export LDFLAGS=" -ld_classic " fi echo "Mac CFLAGS" $CFLAGS ;; Linux) export CFLAGS="${CFLAGS} -fPIC " echo "Linux CFLAGS" $CFLAGS ;; esac if [ "$USE_CMAKE" = "Y" ] ; then case "x$PORT" in xmpi-ts) ga_rt="MPI_2SIDED" ;; xmpi-pr) ga_rt="MPI_PROGRESS_RANK" ;; xmpi-pt) ga_rt="MPI_PROGRESS_THREAD" ;; xmpi-mt) ga_rt="MPI_MULTITHREADED" ;; x) ga_rt="MPI_2SIDED" ;; x*) echo PORT = "$PORT" not recognized exit 1 ;; esac mkdir -p build cd build echo FORTRAN_COMPILER is $FORTRAN_COMPILER mpif90 -show || true echo CC is $CC FC="$FORTRAN_COMPILER" cmake -DMPIEXEC_MAX_NUMPROCS=5 -DGA_RUNTIME="$ga_rt" ../ else case "x$PORT" in xofi) ./configure --with-ofi=$TRAVIS_ROOT/libfabric if [[ "$os" = "Darwin" ]] ; then export COMEX_OFI_LIBRARY=$TRAVIS_ROOT/libfabric/lib/libfabric.dylib fi ;; xarmci) ./configure --with-armci=$TRAVIS_ROOT/external-armci CFLAGS="${CFLAGS} -pthread " LIBS=-lpthread ;; x) ./configure ${CONFIG_OPTS} ;; xmpi-pr) if [[ "$USE_SICM" = "Y" ]] ; then export CFLAGS="-DUSE_SICM=1 -I${HOME}/no_cache/SICM/include -I${HOME}/no_cache/SICM/include/public ${CFLAGS}" export LDFLAGS="-L${HOME}/no_cache/jemalloc/lib -ljemalloc -L${HOME}/no_cache/SICM/lib -lsicm ${LDFLAGS}" export LD_LIBRARY_PATH="${HOME}/no_cache/SICM/lib:${HOME}/no_cache/jemalloc/lib:${LD_LIBRARY_PATH}" fi ./configure --with-${PORT} ${CONFIG_OPTS} ;; x*) if [[ "$MPI_IMPL" = "intel" ]] ; then export I_MPI_F90="$F77" export I_MPI_F77="$F77" export I_MPI_CC="$CC" #hack to get scalapack going export CONFIG_OPTS2=--with-blas="-L${MKLROOT}/lib/intel64 -lmkl_intel_lp64 -lmkl_sequential -lmkl_core -lpthread -lm -ldl" export CONFIG_OPTS3=--with-scalapack="-L${MKLROOT}/lib/intel64 -lmkl_scalapack_lp64 -lmkl_intel_lp64 -lmkl_sequential -lmkl_core -lmkl_blacs_intelmpi_lp64 -lpthread -lm -ldl" ./configure --with-${PORT} ${CONFIG_OPTS} "$CONFIG_OPTS2" "$CONFIG_OPTS3" FFLAGS=-fPIC else ./configure --with-${PORT} ${CONFIG_OPTS} fi ;; esac fi # build libga make V=0 -j ${MAKE_JNUM} # build test programs if [ "$USE_CMAKE" = "Y" ] ; then cd global/testing make cd ../.. else make V=0 checkprogs -j ${MAKE_JNUM} fi # run one test MAYBE_OVERSUBSCRIBE=" --map-by :OVERSUBSCRIBE " if test "x$os" = "xDarwin" && test "x$MPI_IMPL" = "xopenmpi" then MAYBE_OVERSUBSCRIBE=" --map-by :OVERSUBSCRIBE " fi # Determine test name based on whether fortran was supported. TEST_NAME=./global/testing/test.x if test -x $TEST_NAME then echo "Running fortran-based test" else TEST_NAME=./global/testing/testc.x if test -x $TEST_NAME then echo "Running C-based test" else echo "No suitable test was found" exit 1 fi fi if test "x$PORT" = "xmpi-pr" then mpirun -n 5 ${MAYBE_OVERSUBSCRIBE} ${TEST_NAME} else mpirun -n 4 ${MAYBE_OVERSUBSCRIBE} ${TEST_NAME} fi if [ "$USE_CMAKE" = "Y" ] ; then echo "skipping dra test when using cmake" else TEST_NAME=./pario/dra/ntest.x if test -x $TEST_NAME then echo "Running fortran-based test" else TEST_NAME=./pario/dra/ntestc.x if test -x $TEST_NAME then echo "Running C-based test" else echo "No suitable test was found" exit 1 fi fi if test "x$PORT" = "xmpi-pr" then mpirun -n 5 ${MAYBE_OVERSUBSCRIBE} ${TEST_NAME} else mpirun -n 4 ${MAYBE_OVERSUBSCRIBE} ${TEST_NAME} fi fi ga-5.9.2/travis/fix_xcodebuild.sh000077500000000000000000000010231500715745200167570ustar00rootroot00000000000000#!/bin/bash rm -f xcodebuild INTEL_OSXSDK_VER=`xcodebuild -sdk macosx -version | grep SDKVersion` INTEL_OSXSDK_PATH=`xcodebuild -sdk macosx -version Path` echo $INTEL_OSXSDK_PATH cat > xcodebuild <&1 2>&1 | sed '/^Driving:/d; /^Configured with:/d; + s/\/.*\/lib.*\.a//p ; '"/^[[_$as_cr_Letters]][[_$as_cr_alnum]]*=/d"` AS_ECHO(["$ac_[]_AC_LANG_ABBREV[]_v_output"]) >&AS_MESSAGE_LOG_FD _AC_LANG_PREFIX[]FLAGS=$ac_save_[]_AC_LANG_PREFIX[]FLAGS ga-5.9.2/travis/install-armci-mpi.sh000077500000000000000000000024611500715745200173200ustar00rootroot00000000000000#!/usr/bin/env bash set -e set -x TRAVIS_ROOT="$1" os=`uname` if [ "x$os" = "xDarwin" ] ; then brew install libtool which libtool fi case "$MPI_IMPL" in mpich) export PATH=$TRAVIS_ROOT/mpich/bin:$PATH mpichversion mpicc -show export MPICC=mpicc ;; openmpi) if [ "$os" = "Linux" ] ; then $TRAVIS_ROOT/open-mpi/bin/mpicc --showme:command export MPICC=$TRAVIS_ROOT/open-mpi/bin/mpicc fi ;; esac if [ ! -z "${MPICC}" ] ; then echo "Found MPICC=${MPICC} in your environment. Using that." ARMCIMPICC=${MPICC} else ARMCIMPICC=mpicc fi ARMCI_MPI_DIR=${TRAVIS_ROOT}/armci-mpi /bin/rm -rf ${ARMCI_MPI_DIR} git clone -b master --depth 10 https://github.com/jeffhammond/armci-mpi.git ${ARMCI_MPI_DIR} if ! [ -f ${ARMCI_MPI_DIR}/configure ] ; then cd ${ARMCI_MPI_DIR} ./autogen.sh fi if ! [ -d ${ARMCI_MPI_DIR}/build ] ; then mkdir ${ARMCI_MPI_DIR}/build fi cd ${ARMCI_MPI_DIR}/build case "$os" in Darwin) echo "Mac CFLAGS" $CFLAGS ;; Linux) if [ $(${CC} -dM -E - /dev/null |grep __clang__|head -1|cut -c19) ] ; then export CFLAGS="${CFLAGS} -fPIC " fi echo "Linux CFLAGS" $CFLAGS ;; esac ${ARMCI_MPI_DIR}/configure CC=$ARMCIMPICC --prefix=${TRAVIS_ROOT}/external-armci --enable-g make install ga-5.9.2/travis/install-autotools.sh000077500000000000000000000164741500715745200175040ustar00rootroot00000000000000#!/bin/sh set -e set -x os=`uname` TOP="$1" if [ ! -d ${TOP} ] ; then mkdir ${TOP} fi if [ ! -d ${TOP}/bin ] ; then mkdir ${TOP}/bin fi download="" if wget --version > /dev/null ; then download="wget -O" else if curl --version > /dev/null ; then download="curl -o" fi fi if [ "x${download}" = x ] ; then echo "failed to determine download agent" exit 1 fi MAKE_JNUM=4 # we need m4 at least version 1.4.19 M4_VERSION=1.4.19 LIBTOOL_VERSION=2.4.6 AUTOCONF_VERSION=2.71 AUTOMAKE_VERSION=1.11.6 # check whether we can reach ftp.gnu.org TIMEOUT=timeout if [ "x$os" = "xDarwin" ] ; then TIMEOUT=gtimeout fi # do we have a working timeout command? HAVE_TIMEOUT=yes if ! $TIMEOUT --version > /dev/null ; then HAVE_TIMEOUT=no fi FTP_OK=yes if [ "x$HAVE_TIMEOUT" = xyes ] ; then if ! $TIMEOUT 2 bash -c " ${TOP}/bin/automake.patched fi if diff -q ${TOP}/bin/automake.patched ${TOP}/bin/automake >/dev/null ; then echo ${TOP}/bin/automake is already patched! else cp ${TOP}/bin/automake.patched ${TOP}/bin/automake fi ########################################## ### libtool ########################################## cd ${TOP} TOOL=libtool TOOL_VERSION=$LIBTOOL_VERSION TDIR=${TOOL}-${TOOL_VERSION} FILE=${TDIR}.tar.gz BIN=${TOP}/bin/${TOOL} URL=http://ftp.gnu.org/gnu/${TOOL}/${FILE} if [ "x$FTP_OK" = "xno" ] ; then URL=https://github.com/GlobalArrays/autotools/blob/master/${FILE}?raw=true fi if [ ! -f ${FILE} ] ; then ${download} ${FILE} ${URL} else echo ${FILE} already exists! Using existing copy. fi if [ ! -d ${TDIR} ] ; then echo Unpacking ${FILE} tar -xzf ${FILE} else echo ${TDIR} already exists! Using existing copy. fi if [ -f ${BIN} ] ; then echo ${BIN} already exists! Skipping build. else cd ${TOP}/${TDIR} cp ${TOP}/bin/config.guess ./build-aux/config.guess cp ${TOP}/bin/config.sub ./build-aux/config.sub ./configure --prefix=${TOP} && make -j ${MAKE_JNUM} && make install if [ "x$?" != "x0" ] ; then echo FAILURE 2 exit 2 fi fi # refresh the path export PATH=${TOP}/bin:$PATH ga-5.9.2/travis/install-intel.sh000077500000000000000000000077451500715745200165670ustar00rootroot00000000000000#!/bin/bash # This configuration file was taken originally from the mpi4py project # , and then modified for Julia set -e set -x os=`uname` MAKE_JNUM=4 case "$os" in Darwin) IONEAPI_ROOT=~/apps/oneapi ;; Linux) IONEAPI_ROOT=/opt/intel/oneapi ;; esac #echo "os oneapi root" $os $IONEAPI_ROOT #exit 0 echo stev "$IONEAPI_ROOT/setvars.sh" if [ -f "$IONEAPI_ROOT/setvars.sh" ]; then echo "Intel oneapi already installed" source "$IONEAPI_ROOT"/setvars.sh --force || true exit 0 fi case "$os" in Darwin) mkdir -p ~/mntdmg ~/apps/oneapi || true cd ~/Downloads dir_base="cd013e6c-49c4-488b-8b86-25df6693a9b7" dir_hpc="edb4dc2f-266f-47f2-8d56-21bc7764e119" base="m_BaseKit_p_2023.2.0.49398" hpc="m_HPCKit_p_2023.2.0.49443" curl -sS -LJO https://registrationcenter-download.intel.com/akdlm/IRC_NAS/"$dir_base"/"$base".dmg curl -sS -LJO https://registrationcenter-download.intel.com/akdlm/IRC_NAS/"$dir_hpc"/"$hpc".dmg echo "installing BaseKit" hdiutil attach "$base".dmg -mountpoint ~/mntdmg -nobrowse sudo ~/mntdmg/bootstrapper.app/Contents/MacOS/install.sh --cli --eula accept \ --action install --components default --install-dir ~/apps/oneapi hdiutil detach ~/mntdmg # echo "installing HPCKit" hdiutil attach "$hpc".dmg -mountpoint ~/mntdmg -nobrowse sudo ~/mntdmg/bootstrapper.app/Contents/MacOS/install.sh --cli --eula accept \ --action install --components default --install-dir ~/apps/oneapi hdiutil detach ~/mntdmg ls -lrta ~/apps ||true sudo rm -rf "$IONEAPI_ROOT"/intelpython "$IONEAPI_ROOT"/dal "$IONEAPI_ROOT"/advisor \ "$IONEAPI_ROOT"/ipp "$IONEAPI_ROOT"/conda_channel "$IONEAPI_ROOT"/dnnl \ "$IONEAPI_ROOT"/installer "$IONEAPI_ROOT"/vtune_profiler "$IONEAPI_ROOT"/tbb || true $GITHUB_WORKSPACE/travis/fix_xcodebuild.sh sudo cp xcodebuild "$IONEAPI_ROOT"/compiler/latest/mac/bin/intel64/. source "$IONEAPI_ROOT"/setvars.sh || true ifort -V icc -V # get user ownership of /opt/intel to keep caching happy my_gr=`id -g` my_id=`id -u` sudo chown -R $my_id /opt/intel sudo chgrp -R $my_gr /opt/intel ;; Linux) export APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=1 export TERM=dumb rm -f l_Base*sh l_HP*sh tries=0 ; until [ "$tries" -ge 10 ] ; do \ dir_base="20f4e6a1-6b0b-4752-b8c1-e5eacba10e01" dir_hpc="1b2baedd-a757-4a79-8abb-a5bf15adae9a" base="l_BaseKit_p_2024.0.0.49564" hpc="l_HPCKit_p_2024.0.0.49589" wget -nv https://registrationcenter-download.intel.com/akdlm/IRC_NAS/"$dir_hpc"/"$hpc".sh \ && wget -nv https://registrationcenter-download.intel.com/akdlm/IRC_NAS/"$dir_base"/"$base".sh \ && break ;\ tries=$((tries+1)) ; echo attempt no. $tries ; sleep 30 ; done sh ./"$base".sh -a -c -s --action install --components intel.oneapi.lin.mkl.devel --install-dir $IONEAPI_ROOT --eula accept if [[ "$?" != 0 ]]; then df -h echo "base kit install failed: exit code " "${?}" exit 1 fi rm -rf $IONEAPI_ROOT/mkl/latest/lib/ia32 rm -rf $IONEAPI_ROOT/mkl/latest/lib/intel64/*sycl* rm -rf $IONEAPI_ROOT/mkl/latest/lib/intel64/*_pgi_* rm -rf $IONEAPI_ROOT/mkl/latest/lib/intel64/*_gf_* intel_components="intel.oneapi.lin.ifort-compiler:intel.oneapi.lin.dpcpp-cpp-compiler" if [[ "$MPI_IMPL" == "intel" ]]; then intel_components+=":intel.oneapi.lin.mpi.devel" fi sh ./"$hpc".sh -a -c -s --action install \ --components "$intel_components" \ --install-dir $IONEAPI_ROOT --eula accept if [[ "$?" != 0 ]]; then df -h echo "hpc kit install failed: exit code " "${?}" exit 1 fi rm -rf $IONEAPI_ROOT/compiler/latest/linux/lib/oclfpga rm -f ./"$hpc".sh ./"$base".sh rm -rf $IONEAPI_ROOT/compiler/latest/linux/lib/oclfpga || true source "$IONEAPI_ROOT"/setvars.sh --force || true export I_MPI_F90=ifort export I_MPI_F77=ifort which mpif90 mpif90 -show esac which ifort ifort -V echo ""##### end of install-intel.sh ####" ga-5.9.2/travis/install-libfabric.sh000077500000000000000000000007331500715745200173570ustar00rootroot00000000000000#!/bin/sh set -e set -x TRAVIS_ROOT="$1" if [ ! -d "$TRAVIS_ROOT/libfabric" ]; then cd $TRAVIS_ROOT #git clone --depth 10 https://github.com/ofiwg/libfabric.git libfabric-source git clone -b 'v1.5.1' --depth 10 https://github.com/ofiwg/libfabric.git libfabric-source cd libfabric-source ./autogen.sh ./configure --prefix=$TRAVIS_ROOT/libfabric make make install export FI_LOG_LEVEL=error else echo "OFI/libfabric installed..." find $TRAVIS_ROOT -name "fi.h" fi ga-5.9.2/travis/install-mpi.sh000077500000000000000000000110331500715745200162220ustar00rootroot00000000000000#!/usr/bin/env bash # This configuration file was taken originally from the mpi4py project # , and then modified for Julia set -e set -x os=`uname` TRAVIS_ROOT="$1" MPI_IMPL="$2" MAKE_JNUM=2 #check if FC,F77 and CC are defined if [[ -z "${CC}" ]]; then CC=cc fi if [[ -z "${FC}" ]]; then FC=gfortran fi if [[ -z "${F77}" ]]; then F77="${FC}" fi if [ "$F77" == "gfortran" ] && [ "$os" == "Darwin" ]; then if [[ ! -x "$(command -v gfortran)" ]]; then echo gfortran undefined echo symbolic link gfortran-12 ln -sf /usr/local/bin/gfortran-12 /usr/local/bin/gfortran fi fi # this is where updated Autotools will be for Linux export PATH=$TRAVIS_ROOT/bin:$PATH case "$MPI_IMPL" in mpich) if [ "$TRAVIS" == "true" ] && [ "$os" == "Darwin" ]; then brew install mpich || brew upgrade mpich || true else if [ ! -d "$TRAVIS_ROOT/mpich" ] || [ ! -x "$TRAVIS_ROOT/mpich/bin/mpicc" ]; then MPI_VER=3.4.2 wget --no-check-certificate http://www.mpich.org/static/downloads/"$MPI_VER"/mpich-"$MPI_VER".tar.gz tar -xzf mpich-"$MPI_VER".tar.gz cd mpich-"$MPI_VER" mkdir -p build && cd build GNUMAJOR=`$F77 -dM -E - < /dev/null 2> /dev/null | grep __GNUC__ |cut -c18-` GFORTRAN_EXTRA=$(echo $F77 | cut -c 1-8) echo MPICH F77 is `which "$F77"` echo F77 version is `"$F77" -v` if [ "$GFORTRAN_EXTRA" = "gfortran" ]; then if [ $GNUMAJOR -ge 10 ]; then FFLAGS_IN="-w -fallow-argument-mismatch -O1" else FFLAGS_IN="-w -O1" fi elif [ "$F77" = "ifort" ]; then case "$os" in Darwin) IONEAPI_ROOT=~/apps/oneapi FFLAGS_IN="-O0" ;; Linux) IONEAPI_ROOT=/opt/intel/oneapi ;; esac source "$IONEAPI_ROOT"/setvars.sh --force || true ifort -V icc -V fi CFLAGS_in="-O1 -w -fPIC" # --disable-opencl since opencl detection generates -framework opencl on macos that confuses opencl ../configure CC="$CC" FC="$F77" F77="$F77" CFLAGS="$CFLAGS_in" FFLAGS="$FFLAGS_IN" --prefix=$TRAVIS_ROOT/mpich --with-device=ch3 --disable-shared --enable-static --disable-opencl pac_cv_have_float16=no make -j ${MAKE_JNUM} make -j ${MAKE_JNUM} install ls -Rlta $TRAVIS_ROOT/mpich/lib # file $TRAVIS_ROOT/mpich/lib/libmpi.*.dylib || true # file $TRAVIS_ROOT/mpich/lib/libpmpi.*.dylib || true # ls -lrt $TRAVIS_ROOT/mpich/lib/libpmpi.*.dylib || true # nm $TRAVIS_ROOT/mpich/lib/libpmpi.*.dylib || true else echo "MPICH already installed" fi fi ;; openmpi) case "$os" in Darwin) echo "Mac" # Homebrew is at 1.10.2, which is broken for STRIDED/IOV=DIRECT. brew info open-mpi brew update brew install open-mpi || brew upgrade open-mpi || true ;; Linux) if [ ! -d "$TRAVIS_ROOT/open-mpi" ] || [ ! -x "$TRAVIS_ROOT/open-mpi/bin/mpicc" ] ; then wget --no-check-certificate https://download.open-mpi.org/release/open-mpi/v5.0/openmpi-5.0.6.tar.bz2 tar -xjf openmpi-5.0.6.tar.bz2 cd openmpi-5.0.6 mkdir -p build && cd build if [ $(${CC} -dM -E - /dev/null |grep __clang__|head -1|cut -c19) ] ; then CFLAGS_in="-w -fPIC" else CFLAGS_in="-w" fi ../configure CC="$CC" FC="$F77" F77="$F77" CFLAGS="$CFLAGS_in" --prefix=$TRAVIS_ROOT/open-mpi \ --without-verbs --without-fca --without-mxm --without-ucx \ --without-portals4 --without-psm --without-psm2 \ --without-libfabric --without-usnic \ --without-udreg --without-ugni --without-xpmem \ --without-alps --without-munge \ --without-sge --without-loadleveler --without-tm \ --without-lsf --without-slurm \ --without-pvfs2 --without-plfs \ --without-cuda --disable-oshmem \ --disable-libompitrace \ --disable-mpi-io --disable-io-romio \ --enable-mpi-thread-multiple make -j ${MAKE_JNUM} make install else echo "Open-MPI already installed" fi ;; esac ;; intel) ./travis/install-intel.sh ;; *) echo "Unknown MPI implementation: $MPI_IMPL" exit 10 ;; esac ga-5.9.2/travis/install-sicm.sh000077500000000000000000000025031500715745200163720ustar00rootroot00000000000000#!/usr/bin/env bash # This should only be run on Linux machines, as OSX does not have NUMA # SICM has only been added to mpi-pr os=`uname` echo " in sicm install. os is " $os if [ "$os" != "Linux" ] || [ "$PORT" != "mpi-pr" ]; then exit 1; fi set -e set -x # install dependencies sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test sudo apt-get update -qq sudo apt-get install -qq libhwloc-dev libomp5 libomp-dev libnuma-dev libpfm4-dev llvm-dev numactl xsltproc # set install directory to current location to not cache jemalloc/SICM TRAVIS_ROOT="$1" export PATH=$TRAVIS_ROOT/bin:$PATH #install jemalloc git clone -b 5.3.0 https://github.com/jemalloc/jemalloc cd jemalloc export JEPATH="${TRAVIS_ROOT}/jemalloc" sh autogen.sh ./configure --with-jemalloc-prefix=je_ --prefix="${JEPATH}" make -j $(nproc --all) make -j $(nproc --all) install export LD_LIBRARY_PATH="${JEPATH}/lib:${LD_LIBRARY_PATH}" export PKG_CONFIG_PATH="${JEPATH}/lib/pkgconfig:${PKG_CONFIG_PATH}" # get SICM git clone https://github.com/lanl/SICM.git cd SICM git checkout 5944a56e0ccf159b72ce6fe980745b021216b580 # install SICM mkdir -p build cd build cmake .. -DCMAKE_INSTALL_PREFIX="${TRAVIS_ROOT}/SICM" #../configure --with-jemalloc="${JEPATH}" --prefix="${TRAVIS_ROOT}/SICM" CFLAGS="-std=gnu99 ${CFLAGS}" make -j $(nproc --all) make -j $(nproc --all) install ga-5.9.2/version.sh000077500000000000000000000014041500715745200141470ustar00rootroot00000000000000#!/bin/sh # # This script extracts the version from global/src/gacommon.h, which is the master # location for this information. # filename="global/src/gacommon.h" if [ ! -f $filename ]; then echo "version.sh: error: global/src/gacommon.h does not exist" 1>&2 exit 1 fi MAJOR=`egrep '^#define +GA_VERSION_MAJOR +[0-9]+$' $filename` MINOR=`egrep '^#define +GA_VERSION_MINOR +[0-9]+$' $filename` PATCH=`egrep '^#define +GA_VERSION_PATCH +[0-9]+$' $filename` if [ -z "$MAJOR" -o -z "$MINOR" -o -z "$PATCH" ]; then echo "version.sh: error: could not extract version from $filename" 1>&2 exit 1 fi MAJOR=`echo $MAJOR | awk '{ print $3 }'` MINOR=`echo $MINOR | awk '{ print $3 }'` PATCH=`echo $PATCH | awk '{ print $3 }'` echo $MAJOR.$MINOR.$PATCH | tr -d '\n'